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()
36 if(!cvar("g_runematch"))
40 string RuneName(float r)
42 if(r == RUNE_STRENGTH)
43 return "^1Strength^7";
47 return "^2Vitality^7";
54 return "^1Weakness^7";
56 return "^4Vulnerability^7";
61 if(r == CURSE_EMPATHY)
63 return strcat("^8[unnamed", ftos(r), "]^7");
66 vector RuneColormod(float r)
69 if(r == RUNE_STRENGTH)
72 _color = '0 0 255';//'0 102 255';//
74 _color = '0 204 0';//'0 255 0';
76 _color = 0.35*'185 185 0';//255 230 0';//'255 255 0';
78 _color = '64 0 128';//'108 0 217';//'128 0 255';//'179 0 204';//
83 _color = '0 0 255';//'0 102 255';//
85 _color = '0 204 0';//'0 255 0';
87 _color = 0.5*'185 185 0';//'255 255 0';
88 if(r == CURSE_EMPATHY)
89 _color = '179 0 204';//'128 0 255';
91 return _color * (1 / 255) * cvar("g_runematch_rune_color_strength");
94 float count_rune_spawnpoints()
101 e = find(e, classname, "runematch_spawn_point");
111 entity rune_find_spawnpoint(float num, float r)
117 e = find(e, classname, "runematch_spawn_point");
119 e = find(e, classname, "runematch_spawn_point");
125 return e; // emergency: prevent crash
128 if(e.owner != world)//e.wait > time) // already taken
144 void RuneCarriedThink()
150 if(self.owner.classname != "player")
156 self.nextthink = time + 0.1;
158 // count runes my owner holds
160 rune = find(world, classname, "rune");
163 if(rune.owner == self.owner)
167 rune = find(rune, classname, "rune");
170 ang_y = rnum*(360 / rcount) + math_mod(time, 360)*45;//180;
174 //setorigin(self, v_forward*32);
175 self.view_ofs = v_forward*32;
180 if(other.classname != "player" || other.health < 2)
183 return; // "notouch" time isn't finished
185 // detach from the spawn point you're on
186 if(self.owner.classname == "runematch_spawn_point")
188 self.owner.owner = world;
193 self.enemy.owner = other;
194 //setattachment(self, other, "");
195 setattachment(self, world, "");
197 self.movetype = MOVETYPE_FOLLOW;
199 self.view_ofs = other.origin;
201 setorigin(self, '0 0 0');
202 other.runes = other.runes | self.runes | self.enemy.runes;
204 //self.think = SUB_Null;
205 //self.nextthink = 0;
206 self.think = RuneCarriedThink;
207 self.nextthink = time;
208 self.touch = SUB_Null;
210 self.solid = SOLID_NOT;
211 setorigin(self, self.origin);
213 //sprint(other, strcat("^3You have picked up ",
214 // RuneName(self.runes & (RUNE_LAST*2-1)), " and "));
215 //sprint(other, strcat(RuneName(self.enemy.runes & (CURSE_WEAK | CURSE_VULNER | CURSE_VENOM | CURSE_SLOW | CURSE_EMPATHY)), "\n"));
217 bprint(strcat("^3", other.netname, "^7 has picked up ",
218 RuneName(self.runes & (RUNE_LAST*2-1)), "^7 and "));
219 bprint(strcat(RuneName(self.enemy.runes & (CURSE_WEAK | CURSE_VULNER | CURSE_VENOM | CURSE_SLOW | CURSE_EMPATHY)), "\n"));
226 num = count_rune_spawnpoints();
227 r = ceil(random()*num);
229 if(self.owner.classname == "runematch_spawn_point")
231 self.owner.owner = world;
236 spot = rune_find_spawnpoint(num, r);
239 bprint("Warning: couldn't find spawn spot for rune respawn\n");
243 self.solid = SOLID_TRIGGER;
245 setorigin(self, spot.origin);
249 self.touch = rune_touch;
251 self.think = rune_respawn;
252 self.nextthink = time + cvar("g_runematch_shuffletime");//30 + random()*5; // fixme: cvar
255 entity FindRune(entity own, string clname, float r)
265 rune = find(rune, classname, clname);
267 rune = find(rune, classname, clname);
270 if(rune.owner == own)
284 void DropRune(entity pl, entity e)
289 // detach from player
290 setattachment(e, world, "");
292 e.enemy.owner = world;
293 // don't instantly touch player again
294 e.wait = time + 1; // "notouch" time
295 e.movetype = MOVETYPE_TOSS;
296 e.solid = SOLID_TRIGGER;
297 // reposition itself if not picked up soon
298 e.think = rune_respawn;
299 e.nextthink = time + cvar("g_runematch_respawntime");//15 + random()*5; // fixme: cvar
300 e.touch = rune_touch;
302 pl.runes = pl.runes - (pl.runes & (e.runes | e.enemy.runes));
305 setorigin(e, pl.origin + '0 0 10');
306 e.velocity = '0 0 200' + '0 100 0'*crandom() + '100 0 0'*crandom();
309 bprint(strcat("^3", pl.netname, "^7 has lost ",
310 RuneName(e.runes & (RUNE_LAST*2-1)), "^7 and "));
311 bprint(strcat(RuneName(e.enemy.runes & (CURSE_WEAK | CURSE_VULNER | CURSE_VENOM | CURSE_SLOW | CURSE_EMPATHY)), "\n"));
314 float RuneMatchesCurse(float r, float c)
317 if(r & RUNE_STRENGTH)
319 else if(r & RUNE_DEFENSE)
321 else if(r & RUNE_REGEN)
323 else if(r & RUNE_SPEED)
325 else if(r & RUNE_VAMPIRE)
327 else return FALSE; // fixme: error?
334 // player died, drop runes
335 // each rune should pair up with a random curse and then be tossed from the player
336 void DropAllRunes(entity pl)
339 float rcount, ccount, r, c, rand, prevent_same, numtodrop, tries;
341 entity c1, r1, c2, r2;
343 rune = curse = world;
344 rcount = ccount = r = c = 0;
345 rune = find(rune, classname, "rune");
350 rune = find(rune, classname, "rune");
352 curse = find(curse, classname, "curse");
355 if(curse.owner == pl)
357 curse = find(curse, classname, "curse");
360 numtodrop = cvar("g_runematch_drop_runes_max");
361 prevent_same = !cvar("g_runematch_allow_same");
363 rune = curse = world;
366 rune = find(rune, classname, "rune");
373 // find a random curse
375 if(ccount > 1 && prevent_same)
377 // avoid pairing runes and curses that match each other
379 rand = ceil(random()*ccount);
380 curse = FindRune(pl, "curse", rand);
382 }while(RuneMatchesCurse(rune.runes, curse.runes) && tries > 0);
385 bprint("warning: couldn't prevent same rune\n");
390 rand = ceil(random()*ccount);
391 curse = FindRune(pl, "curse", rand);
395 error("Couldn't fine curse to bind rune to\n");
397 // pair rune and curse
404 if(r1 != r2) // not already attached to each other
408 setattachment(c1, r1, "");
411 setattachment(c2, r2, "");
413 //ccount = ccount - 1;
414 //rcount = rcount - 1;
420 r1.think = rune_respawn;
424 numtodrop = numtodrop - 1;
432 void spawn_default_runespawnpoints()
435 spot = find(world, classname, "info_player_deathmatch");
439 e.classname = "runematch_spawn_point";
440 e.origin = spot.origin;
441 spot = find(spot, classname, "info_player_deathmatch");
447 float r, num, max_num, rn, cs, numrunes, runes_left, curses_left, tries, prevent_same;
453 // fixme: instead of placing them all now, why not
454 // simply create them all and let them call rune_respawn() as their think?
456 runes_left = RUNE_STRENGTH | RUNE_DEFENSE | RUNE_REGEN | RUNE_SPEED | RUNE_VAMPIRE;
457 curses_left = CURSE_WEAK | CURSE_VULNER | CURSE_VENOM | CURSE_SLOW | CURSE_EMPATHY;
459 max_num = num = count_rune_spawnpoints();
463 spawn_default_runespawnpoints();
466 prevent_same = !cvar("g_runematch_allow_same");
468 max_num = num = count_rune_spawnpoints();
471 error(strcat("not enough spawn points for runematch, need ", ftos(numrunes), " but found ", ftos(num), "\n"));
475 r = ceil(random()*numrunes);
487 if(curses_left > 1 && prevent_same)
490 // avoid pairing runes and curses that match each other
492 r = ceil(random()*numrunes);
504 }while(RuneMatchesCurse(rn, cs) && tries > 0);
507 bprint("warning: couldn't prevent same rune\n");
512 r = ceil(random()*numrunes);
530 r = ceil(random()*num*1.25);
532 spot = rune_find_spawnpoint(num, r);
536 error("failed to find runematch spawnpoint!\n");
539 // choose and spawn rune
541 /* //rn = RUNE_FIRST;
542 while(!runes_left & rn && rn <= RUNE_LAST)
545 error("couldn't select rune\n");
546 runes_left = runes_left - rn;
549 while(!curses_left & cs && cs <= CURSE_LAST)
552 error("couldn't select rune\n");
553 curses_left = curses_left - cs;
555 while(!runes_left & rn)
561 runes_left = runes_left - rn;
563 while(!curses_left & cs)
569 curses_left = curses_left - cs;
573 e.classname = "rune";
574 e.touch = rune_touch;
575 e.think = rune_respawn;
576 e.nextthink = time + random();
577 e.movetype = MOVETYPE_TOSS;
578 e.solid = SOLID_TRIGGER;
580 setmodel(e, "models/runematch/rune.mdl");
581 setorigin(e, spot.origin);
585 e.enemy.classname = "curse";
587 //e.enemy.avelocity = '300 500 200';
588 setmodel(e.enemy, "models/runematch/curse.mdl");
589 setorigin(e, '0 0 0');
590 setattachment(e.enemy, e, "");
593 e.colormod = RuneColormod(rn);
594 e.enemy.colormod = RuneColormod(cs);
596 e.alpha = e.enemy.alpha = cvar("g_runematch_rune_alpha");//0.78;
597 e.effects = e.enemy.effects = cvar("g_runematch_rune_effects");//EF_ADDITIVE;// | EF_FULLBRIGHT;
599 // Savage: Save some bandwidth
600 self.effects |= EF_LOWPRECISION;
602 //e.glow_size = e.enemy.glow_size = cvar("g_runematch_rune_glow_size");
603 //e.glow_color = e.enemy.glow_color = cvar("g_runematch_rune_glow_color");
605 // this spot is taken
613 numrunes = numrunes - 1;
618 void runematch_init()
620 if(!cvar("g_runematch"))
622 precache_model("models/runematch/rune.mdl");
623 precache_model("models/runematch/curse.mdl");
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(!cvar("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 rune.owner.frags = rune.owner.frags + 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;