3 float RUNE_STRENGTH = 1;
4 float RUNE_DEFENSE = 2;
7 float RUNE_VAMPIRE = 16;
10 float CURSE_FIRST = 8192;
11 float CURSE_WEAK = 8192;
12 float CURSE_VULNER = 16384;
13 float CURSE_VENOM = 32768;
14 float CURSE_SLOW = 65536;
15 float CURSE_EMPATHY = 131072;
16 float CURSE_LAST = 131072;
21 Rune: When you damage enemies, you have a slight chance of instant-killing them (porportional to damage dealt / their health)
22 Curse: When you are damaged, you have a chance of being instant-killed
25 Rune: The lower your health below 100, the more damage you deal (does not decrease your damage if you're above 100)
26 Curse: The higher your health (up to 100), the less damage you deal (at 100 hp deal 1/5th damage)
30 /*QUAKED runematch_spawn_point (1 0 0) (-16 -16 -24) (16 16 24)
31 spawn point for runes in runematch
34 void runematch_spawn_point()
43 setorigin(self, self.origin + '0 0 35');
46 string RuneName(float r)
48 if(r == RUNE_STRENGTH)
49 return "^1Strength^7";
53 return "^2Vitality^7";
60 return "^1Weakness^7";
62 return "^4Vulnerability^7";
67 if(r == CURSE_EMPATHY)
69 return strcat("^8[unnamed", ftos(r), "]^7");
72 vector RuneColormod(float r)
75 if(r == RUNE_STRENGTH)
78 _color = '0 0 255';//'0 102 255';//
80 _color = '0 204 0';//'0 255 0';
82 _color = 0.35*'185 185 0';//255 230 0';//'255 255 0';
84 _color = '64 0 128';//'108 0 217';//'128 0 255';//'179 0 204';//
89 _color = '0 0 255';//'0 102 255';//
91 _color = '0 204 0';//'0 255 0';
93 _color = 0.5*'185 185 0';//'255 255 0';
94 if(r == CURSE_EMPATHY)
95 _color = '179 0 204';//'128 0 255';
97 return _color * (1 / 255) * cvar("g_runematch_rune_color_strength");
100 float count_rune_spawnpoints()
105 e = findchain(classname, "runematch_spawn_point");
115 entity rune_find_spawnpoint(float num, float r)
121 e = find(e, classname, "runematch_spawn_point");
123 e = find(e, classname, "runematch_spawn_point");
129 return e; // emergency: prevent crash
132 if(e.owner != world)//e.wait > time) // already taken
148 void RuneCarriedThink()
154 if(self.owner.classname != "player" || time < restart_countdown)
160 self.nextthink = time + 0.1;
162 // count runes my owner holds
164 rune = find(world, classname, "rune");
167 if(rune.owner == self.owner)
171 rune = find(rune, classname, "rune");
174 ang_y = rnum*(360 / rcount) + mod(time, 360)*45;//180;
178 //setorigin(self, v_forward*32);
179 self.view_ofs = v_forward*32;
184 if(other.classname != "player" || other.health < 1)
187 return; // "notouch" time isn't finished
189 // detach from the spawn point you're on
190 if(self.owner.classname == "runematch_spawn_point")
192 self.owner.owner = world;
197 self.enemy.owner = other;
198 //setattachment(self, other, "");
199 setattachment(self, world, "");
201 self.movetype = MOVETYPE_FOLLOW;
203 self.view_ofs = other.origin;
205 setorigin(self, '0 0 0');
206 other.runes = other.runes | self.runes | self.enemy.runes;
208 //self.think = SUB_Null;
209 //self.nextthink = 0;
210 self.think = RuneCarriedThink;
211 self.nextthink = time;
212 self.touch = SUB_Null;
214 self.solid = SOLID_NOT;
215 setorigin(self, self.origin);
217 //sprint(other, strcat("^3You have picked up ",
218 // RuneName(self.runes & (RUNE_LAST*2-1)), " and "));
219 //sprint(other, strcat(RuneName(self.enemy.runes & (CURSE_WEAK | CURSE_VULNER | CURSE_VENOM | CURSE_SLOW | CURSE_EMPATHY)), "\n"));
221 bprint("^3", other.netname, "^7 has picked up ",
222 RuneName(self.runes & (RUNE_LAST*2-1)), "^7 and ");
223 bprint(RuneName(self.enemy.runes & (CURSE_WEAK | CURSE_VULNER | CURSE_VENOM | CURSE_SLOW | CURSE_EMPATHY)), "\n");
230 num = count_rune_spawnpoints();
231 r = ceil(random()*num);
233 if(self.owner.classname == "runematch_spawn_point")
235 self.owner.owner = world;
240 spot = rune_find_spawnpoint(num, r);
243 bprint("Warning: couldn't find spawn spot for rune respawn\n");
247 self.solid = SOLID_TRIGGER;
249 setorigin(self, spot.origin);
253 self.touch = rune_touch;
255 self.think = rune_respawn;
256 self.nextthink = time + cvar("g_runematch_shuffletime");//30 + random()*5; // fixme: cvar
259 entity FindRune(entity own, string clname, float r)
269 rune = find(rune, classname, clname);
271 rune = find(rune, classname, clname);
274 if(rune.owner == own)
288 void DropRune(entity pl, entity e)
293 // detach from player
294 setattachment(e, world, "");
296 e.enemy.owner = world;
297 // don't instantly touch player again
298 e.wait = time + 1; // "notouch" time
299 e.movetype = MOVETYPE_TOSS;
300 e.solid = SOLID_TRIGGER;
301 // reposition itself if not picked up soon
302 e.think = rune_respawn;
303 e.nextthink = time + cvar("g_runematch_respawntime");//15 + random()*5; // fixme: cvar
304 e.touch = rune_touch;
306 pl.runes = pl.runes - (pl.runes & (e.runes | e.enemy.runes));
309 setorigin(e, pl.origin + '0 0 10');
310 e.velocity = '0 0 200' + '0 100 0'*crandom() + '100 0 0'*crandom();
313 bprint("^3", pl.netname, "^7 has lost ",
314 RuneName(e.runes & (RUNE_LAST*2-1)), "^7 and ");
315 bprint(RuneName(e.enemy.runes & (CURSE_WEAK | CURSE_VULNER | CURSE_VENOM | CURSE_SLOW | CURSE_EMPATHY)), "\n");
318 float RuneMatchesCurse(float r, float c)
321 if(r & RUNE_STRENGTH)
323 else if(r & RUNE_DEFENSE)
325 else if(r & RUNE_REGEN)
327 else if(r & RUNE_SPEED)
329 else if(r & RUNE_VAMPIRE)
331 else return FALSE; // fixme: error?
338 // player died, drop runes
339 // each rune should pair up with a random curse and then be tossed from the player
340 void DropAllRunes(entity pl)
343 float rcount, ccount, r, c, rand, prevent_same, numtodrop, tries;
345 entity curse1, rune1, curse2, rune2;
347 rune = curse = world;
348 rcount = ccount = r = c = 0;
349 rune = find(rune, classname, "rune");
354 rune = find(rune, classname, "rune");
356 curse = find(curse, classname, "curse");
359 if(curse.owner == pl)
361 curse = find(curse, classname, "curse");
364 numtodrop = cvar("g_runematch_drop_runes_max");
365 prevent_same = !cvar("g_runematch_allow_same");
367 rune = curse = world;
370 rune = find(rune, classname, "rune");
377 // find a random curse
379 if(ccount > 1 && prevent_same)
381 // avoid pairing runes and curses that match each other
383 rand = ceil(random()*ccount);
384 curse = FindRune(pl, "curse", rand);
386 }while(RuneMatchesCurse(rune.runes, curse.runes) && tries > 0);
389 bprint("warning: couldn't prevent same rune\n");
394 rand = ceil(random()*ccount);
395 curse = FindRune(pl, "curse", rand);
399 error("Couldn't fine curse to bind rune to\n");
401 // pair rune and curse
405 rune2 = curse1.enemy;
406 curse2 = rune1.enemy;
408 if(rune1 != rune2) // not already attached to each other
410 rune1.enemy = curse1;
411 curse1.enemy = rune1;
412 setattachment(curse1, rune1, "");
413 rune2.enemy = curse2;
414 curse2.enemy = rune2;
415 setattachment(curse2, rune2, "");
416 //DropRune(pl, rune2);
417 //ccount = ccount - 1;
418 //rcount = rcount - 1;
424 rune1.think = rune_respawn;
425 rune1.nextthink = time;
428 numtodrop = numtodrop - 1;
436 void spawn_default_runespawnpoints()
439 spot = find(world, classname, "info_player_deathmatch");
443 e.classname = "runematch_spawn_point";
444 e.origin = spot.origin;
445 spot = find(spot, classname, "info_player_deathmatch");
451 float r, num, max_num, rn, cs, numrunes, runes_left, curses_left, tries, prevent_same;
457 // fixme: instead of placing them all now, why not
458 // simply create them all and let them call rune_respawn() as their think?
460 runes_left = RUNE_STRENGTH | RUNE_DEFENSE | RUNE_REGEN | RUNE_SPEED | RUNE_VAMPIRE;
461 curses_left = CURSE_WEAK | CURSE_VULNER | CURSE_VENOM | CURSE_SLOW | CURSE_EMPATHY;
463 max_num = num = count_rune_spawnpoints();
467 spawn_default_runespawnpoints();
470 prevent_same = !cvar("g_runematch_allow_same");
472 max_num = num = count_rune_spawnpoints();
475 error(strcat("not enough spawn points for runematch, need ", ftos(numrunes), " but found ", ftos(num), "\n"));
479 r = ceil(random()*numrunes);
491 if(curses_left > 1 && prevent_same)
494 // avoid pairing runes and curses that match each other
496 r = ceil(random()*numrunes);
508 }while(RuneMatchesCurse(rn, cs) && tries > 0);
511 bprint("warning: couldn't prevent same rune\n");
516 r = ceil(random()*numrunes);
534 r = ceil(random()*num*1.25);
536 spot = rune_find_spawnpoint(num, r);
540 error("failed to find runematch spawnpoint!\n");
543 // choose and spawn rune
545 /* //rn = RUNE_FIRST;
546 while(!runes_left & rn && rn <= RUNE_LAST)
549 error("couldn't select rune\n");
550 runes_left = runes_left - rn;
553 while(!curses_left & cs && cs <= CURSE_LAST)
556 error("couldn't select rune\n");
557 curses_left = curses_left - cs;
559 while(!runes_left & rn)
565 runes_left = runes_left - rn;
567 while(!curses_left & cs)
573 curses_left = curses_left - cs;
577 e.classname = "rune";
578 e.touch = rune_touch;
579 e.think = rune_respawn;
580 e.nextthink = time + random();
581 e.movetype = MOVETYPE_TOSS;
582 e.solid = SOLID_TRIGGER;
584 setmodel(e, "models/runematch/rune.mdl"); // precision set below
585 setorigin(e, spot.origin);
586 setsize(e, '0 0 -35', '0 0 0');
590 e.enemy.classname = "curse";
592 //e.enemy.avelocity = '300 500 200';
593 setmodel(e.enemy, "models/runematch/curse.mdl"); // precision set below
594 setorigin(e, '0 0 0');
595 setattachment(e.enemy, e, "");
598 e.colormod = RuneColormod(rn);
599 e.enemy.colormod = RuneColormod(cs);
601 e.alpha = e.enemy.alpha = cvar("g_runematch_rune_alpha");//0.78;
602 e.effects = e.enemy.effects = cvar("g_runematch_rune_effects") | EF_LOWPRECISION;//EF_ADDITIVE;// | EF_FULLBRIGHT;
604 //e.glow_size = e.enemy.glow_size = cvar("g_runematch_rune_glow_size");
605 //e.glow_color = e.enemy.glow_color = cvar("g_runematch_rune_glow_color");
607 // this spot is taken
615 numrunes = numrunes - 1;
620 void runematch_init()
627 e.think = spawn_runes;
628 e.nextthink = time + 0.1;
632 float runematch_point_time;
634 // give points to players who are holding runes
635 void RuneMatchGivePoints()
639 if(!g_runematch || !cvar("g_runematch_pointamt"))
645 if(runematch_point_time > time)
648 runematch_point_time = time + cvar("g_runematch_pointrate");
653 rune = find(rune, classname, "rune");
657 if(rune.owner.classname == "player")
659 UpdateFrags(rune.owner, cvar("g_runematch_pointamt"));
664 float RunematchHandleFrags(entity attacker, entity targ, float f)
667 float arunes, trunes, newfrags;
676 head = find(world, classname, "rune");
679 if(head.owner == attacker)
683 else if(head.owner == targ)
688 head = find(head, classname, "rune");
691 if(!arunes && !trunes)
692 return f - 1 + cvar("g_runematch_frags_norune"); // don't give points to players when no runes are involved.
695 { // got a kill while holding runes
696 newfrags = newfrags + cvar("g_runematch_frags_killedby_runeholder");//5;
699 { // killed an enemy holding runes
700 newfrags = newfrags + cvar("g_runematch_frags_killed_runeholder");//5;
703 f = f - 1 + newfrags;