4 float RUNE_STRENGTH = 1;
5 float RUNE_DEFENSE = 2;
8 float RUNE_VAMPIRE = 16;
11 float CURSE_FIRST = 8192;
12 float CURSE_WEAK = 8192;
13 float CURSE_VULNER = 16384;
14 float CURSE_VENOM = 32768;
15 float CURSE_SLOW = 65536;
16 float CURSE_EMPATHY = 131072;
17 float CURSE_LAST = 131072;
24 Rune: When you damage enemies, you have a slight chance of instant-killing them (porportional to damage dealt / their health)
25 Curse: When you are damaged, you have a chance of being instant-killed
28 Rune: The lower your health below 100, the more damage you deal (does not decrease your damage if you're above 100)
29 Curse: The higher your health (up to 100), the less damage you deal (at 100 hp deal 1/5th damage)
33 /*QUAKED spawnfunc_runematch_spawn_point (1 0 0) (-16 -16 -24) (16 16 24)
34 spawn point for runes in runematch
37 void spawnfunc_runematch_spawn_point()
39 if(!g_runematch || !autocvar_g_runematch_fixedspawns)
45 setsize(self, '0 0 -35', '0 0 0');
50 // only used if using rune spawns at all
51 entity rune_find_spawnpoint()
55 if(rune_numspawns < RUNE_COUNT)
58 RandomSelection_Init();
60 for(e = world; (e = find(e, classname, "runematch_spawn_point")); )
62 RandomSelection_Add(e, 0, string_null, e.cnt, 0);
64 return RandomSelection_chosen_ent;
67 float rune_spawn_somewhere(entity e)
70 spot = rune_find_spawnpoint();
74 setorigin(e, spot.origin);
83 if(MoveToRandomMapLocation(e, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, 10, 1024, 256))
86 makevectors(self.angles),
87 self.velocity = v_forward * 250;
88 self.angles = '0 0 0';
93 // sorry, can't spawn, better luck next frame
99 void rune_unmark_spot(entity e)
101 if(e.owner.classname == "runematch_spawn_point")
103 e.owner.owner = world;
108 string RuneName(float r)
110 if(r == RUNE_STRENGTH)
111 return "^1Strength^7";
112 if(r == RUNE_DEFENSE)
113 return "^4Defense^7";
115 return "^2Vitality^7";
118 if(r == RUNE_VAMPIRE)
119 return "^6Vampire^7";
122 return "^1Weakness^7";
123 if(r == CURSE_VULNER)
124 return "^4Vulnerability^7";
129 if(r == CURSE_EMPATHY)
130 return "^6Empathy^7";
131 return strcat("^8[unnamed", ftos(r), "]^7");
134 vector RuneColormod(float r)
136 vector _color = '255 0 255';
138 if(r == RUNE_STRENGTH)
140 if(r == RUNE_DEFENSE)
141 _color = '0 0 255';//'0 102 255';//
143 _color = '0 204 0';//'0 255 0';
145 _color = 0.35*'185 185 0';//255 230 0';//'255 255 0';
146 if(r == RUNE_VAMPIRE)
147 _color = '64 0 128';//'108 0 217';//'128 0 255';//'179 0 204';//
151 if(r == CURSE_VULNER)
152 _color = '0 0 255';//'0 102 255';//
154 _color = '0 204 0';//'0 255 0';
156 _color = 0.5*'185 185 0';//'255 255 0';
157 if(r == CURSE_EMPATHY)
158 _color = '179 0 204';//'128 0 255';
160 return _color * (1 / 255) * autocvar_g_runematch_rune_color_strength;
165 void RuneCarriedThink()
171 if(self.owner.classname != "player" || time < game_starttime)
177 self.nextthink = time + 0.1;
179 // count runes my owner holds
181 rune = find(world, classname, "rune");
185 if(rune.owner == self.owner)
189 rune = find(rune, classname, "rune");
192 ang_y = rnum*(360 / rcount) + mod(time, 360)*45;//180;
196 setorigin(self, v_forward*32);
201 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
203 self.think = rune_respawn;
204 self.nextthink = time;
208 if(other.classname != "player" || other.health < 1)
211 return; // "notouch" time isn't finished
213 // detach from the spawn point you're on
214 rune_unmark_spot(self);
217 self.enemy.owner = other;
218 setattachment(self, other, "");
220 other.runes = other.runes | self.runes | self.enemy.runes;
222 //self.think = SUB_Null;
223 //self.nextthink = 0;
224 self.think = RuneCarriedThink;
225 self.nextthink = time;
226 self.touch = SUB_Null;
228 self.solid = SOLID_NOT;
229 setorigin(self, self.origin);
231 //sprint(other, strcat("^3You have picked up ",
232 // RuneName(self.runes & (RUNE_LAST*2-1)), " and "));
233 //sprint(other, strcat(RuneName(self.enemy.runes & (CURSE_WEAK | CURSE_VULNER | CURSE_VENOM | CURSE_SLOW | CURSE_EMPATHY)), "\n"));
235 bprint("^3", other.netname, "^7 has picked up ",
236 RuneName(self.runes & (RUNE_LAST*2-1)), "^7 and ");
237 bprint(RuneName(self.enemy.runes & (CURSE_WEAK | CURSE_VULNER | CURSE_VENOM | CURSE_SLOW | CURSE_EMPATHY)), "\n");
242 rune_unmark_spot(self);
243 if(rune_spawn_somewhere(self))
245 self.solid = SOLID_TRIGGER;
246 self.touch = rune_touch;
247 self.think = rune_respawn;
248 self.nextthink = time + autocvar_g_runematch_shuffletime;//30 + random()*5; // fixme: cvar
253 self.think = rune_respawn;
254 self.nextthink = time;
258 entity FindRune(entity own, string clname, float r)
268 rune = find(rune, classname, clname);
270 rune = find(rune, classname, clname);
273 if(rune.owner == own)
287 void DropRune(entity pl, entity e)
292 // detach from player
293 setattachment(e, world, "");
295 e.enemy.owner = world;
296 // don't instantly touch player again
297 e.wait = time + 1; // "notouch" time
298 e.movetype = MOVETYPE_TOSS;
299 e.solid = SOLID_TRIGGER;
300 // reposition itself if not picked up soon
301 e.think = rune_respawn;
302 e.nextthink = time + autocvar_g_runematch_respawntime;//15 + random()*5; // fixme: cvar
303 e.touch = rune_touch;
305 pl.runes = pl.runes - (pl.runes & (e.runes | e.enemy.runes));
308 setorigin(e, pl.origin + '0 0 10');
309 e.velocity = '0 0 200' + '0 100 0'*crandom() + '100 0 0'*crandom();
312 bprint("^3", pl.netname, "^7 has lost ",
313 RuneName(e.runes & (RUNE_LAST*2-1)), "^7 and ");
314 bprint(RuneName(e.enemy.runes & (CURSE_WEAK | CURSE_VULNER | CURSE_VENOM | CURSE_SLOW | CURSE_EMPATHY)), "\n");
317 float RuneMatchesCurse(float r, float c)
320 if(r & RUNE_STRENGTH)
322 else if(r & RUNE_DEFENSE)
324 else if(r & RUNE_REGEN)
326 else if(r & RUNE_SPEED)
328 else if(r & RUNE_VAMPIRE)
330 else return FALSE; // fixme: error?
337 // player died, drop runes
338 // each rune should pair up with a random curse and then be tossed from the player
339 void DropAllRunes(entity pl)
342 float rcount, ccount, r, c, rand, prevent_same, numtodrop, tries;
344 entity curse1, rune1, curse2, rune2;
346 rcount = ccount = r = c = 0;
347 rune = find(rune, classname, "rune");
352 rune = find(rune, classname, "rune");
354 curse = find(curse, classname, "curse");
357 if(curse.owner == pl)
359 curse = find(curse, classname, "curse");
362 numtodrop = autocvar_g_runematch_drop_runes_max;
363 prevent_same = !autocvar_g_runematch_allow_same;
367 rune = find(rune, classname, "rune");
374 // find a random curse
376 if(ccount > 1 && prevent_same)
378 // avoid pairing runes and curses that match each other
380 rand = floor(random()*ccount) + 1;
381 curse = FindRune(pl, "curse", rand);
383 }while(RuneMatchesCurse(rune.runes, curse.runes) && tries > 0);
386 bprint("warning: couldn't prevent same rune\n");
391 rand = floor(random()*ccount) + 1;
392 curse = FindRune(pl, "curse", rand);
396 error("Couldn't fine curse to bind rune to\n");
398 // pair rune and curse
402 rune2 = curse1.enemy;
403 curse2 = rune1.enemy;
405 if(rune1 != rune2) // not already attached to each other
407 rune1.enemy = curse1;
408 curse1.enemy = rune1;
409 setattachment(curse1, rune1, "");
410 rune2.enemy = curse2;
411 curse2.enemy = rune2;
412 setattachment(curse2, rune2, "");
413 //DropRune(pl, rune2);
414 //ccount = ccount - 1;
415 //rcount = rcount - 1;
421 rune1.think = rune_respawn;
422 rune1.nextthink = time;
425 numtodrop = numtodrop - 1;
436 if(self.owner.classname != "runematch_spawn_point")
437 DropAllRunes(self.owner);
443 float rn, cs, runes_used, curses_used, prevent_same, numrunes;
449 // fixme: instead of placing them all now, why not
450 // simply create them all and let them call rune_respawn() as their think?
455 prevent_same = !autocvar_g_runematch_allow_same;
456 numrunes = RUNE_COUNT;
460 RandomSelection_Init();
461 for(rn = RUNE_FIRST; rn <= RUNE_LAST; rn *= 2)
462 if not(runes_used & rn)
463 RandomSelection_Add(world, rn, string_null, 1, 1);
464 rn = RandomSelection_chosen_float;
466 RandomSelection_Init();
467 for(cs = CURSE_FIRST; cs <= CURSE_LAST; cs *= 2)
468 if not(curses_used & cs)
469 if not(prevent_same && cs == RuneMatchesCurse(rn, cs))
470 RandomSelection_Add(world, cs, string_null, 1, 1);
471 cs = RandomSelection_chosen_float;
474 error("No rune/curse left");
481 e.classname = "rune";
482 e.touch = rune_touch;
483 e.think = rune_respawn;
485 e.movetype = MOVETYPE_TOSS;
486 e.solid = SOLID_TRIGGER;
488 e.reset = rune_reset;
489 setmodel(e, "models/runematch/rune.mdl"); // precision set below
490 setsize(e, '0 0 -35', '0 0 0');
494 e.enemy.classname = "curse";
496 //e.enemy.avelocity = '300 500 200';
497 setmodel(e.enemy, "models/runematch/curse.mdl"); // precision set below
498 setorigin(e, '0 0 0');
499 setattachment(e.enemy, e, "");
501 e.colormod = RuneColormod(rn);
502 e.enemy.colormod = RuneColormod(cs);
504 e.alpha = e.enemy.alpha = autocvar_g_runematch_rune_alpha;//0.78;
505 e.effects = e.enemy.effects = autocvar_g_runematch_rune_effects | EF_LOWPRECISION;//EF_ADDITIVE;// | EF_FULLBRIGHT;
507 //e.glow_size = e.enemy.glow_size = cvar("g_runematch_rune_glow_size");
508 //e.glow_color = e.enemy.glow_color = cvar("g_runematch_rune_glow_color");
513 numrunes = numrunes - 1;
517 void runematch_init()
524 e.think = spawn_runes;
525 e.nextthink = time + 0.1;
529 float runematch_point_time;
531 // give points to players who are holding runes
532 void RuneMatchGivePoints()
536 if(!g_runematch || !autocvar_g_runematch_pointamt)
542 if(runematch_point_time > time)
545 runematch_point_time = time + autocvar_g_runematch_pointrate;
550 rune = find(rune, classname, "rune");
554 if(rune.owner.classname == "player")
556 UpdateFrags(rune.owner, autocvar_g_runematch_pointamt);
561 float RunematchHandleFrags(entity attacker, entity targ, float f)
564 float arunes, trunes, newfrags;
573 head = find(world, classname, "rune");
576 if(head.owner == attacker)
580 else if(head.owner == targ)
585 head = find(head, classname, "rune");
588 if(!arunes && !trunes)
589 return f - 1 + autocvar_g_runematch_frags_norune; // don't give points to players when no runes are involved.
593 { // got a kill while holding runes
594 newfrags = newfrags + autocvar_g_runematch_frags_killedby_runeholder;//5;
597 { // killed an enemy holding runes
598 newfrags = newfrags + autocvar_g_runematch_frags_killed_runeholder;//5;
601 f = f - 1 + newfrags;