]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/runematch.qc
more sound changes
[divverent/nexuiz.git] / data / qcsrc / server / runematch.qc
1
2 float   RUNE_FIRST      = 1;
3 float   RUNE_STRENGTH   = 1;
4 float   RUNE_DEFENSE    = 2;
5 float   RUNE_REGEN      = 4;
6 float   RUNE_SPEED      = 8;
7 float   RUNE_VAMPIRE    = 16;
8 float   RUNE_LAST       = 16;
9
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;
17
18 /* rune ideas:
19
20         Doom/Death
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
23
24         Vengence/Slothful
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)
27
28 */
29
30 /*QUAKED spawnfunc_runematch_spawn_point (1 0 0) (-16 -16 -24) (16 16 24)
31 spawn point for runes in runematch
32 */
33
34 void spawnfunc_runematch_spawn_point()
35 {
36         if(!g_runematch)
37         {
38                 remove(self);
39                 return;
40         }
41
42         setsize(self, '0 0 -35', '0 0 0');
43         droptofloor();
44 }
45
46 string RuneName(float r)
47 {
48         if(r == RUNE_STRENGTH)
49                 return "^1Strength^7";
50         if(r == RUNE_DEFENSE)
51                 return "^4Defense^7";
52         if(r == RUNE_REGEN)
53                 return "^2Vitality^7";
54         if(r == RUNE_SPEED)
55                 return "^3Speed^7";
56         if(r == RUNE_VAMPIRE)
57                 return "^6Vampire^7";
58
59         if(r == CURSE_WEAK)
60                 return "^1Weakness^7";
61         if(r == CURSE_VULNER)
62                 return "^4Vulnerability^7";
63         if(r == CURSE_VENOM)
64                 return "^2Venom^7";
65         if(r == CURSE_SLOW)
66                 return "^3Slow^7";
67         if(r == CURSE_EMPATHY)
68                 return "^6Empathy^7";
69         return strcat("^8[unnamed", ftos(r), "]^7");
70 }
71
72 vector RuneColormod(float r)
73 {
74         vector _color;
75         if(r == RUNE_STRENGTH)
76                 _color = '255 0 0';
77         if(r == RUNE_DEFENSE)
78                 _color = '0 0 255';//'0 102 255';//
79         if(r == RUNE_REGEN)
80                 _color = '0 204 0';//'0 255 0';
81         if(r == RUNE_SPEED)
82                 _color = 0.35*'185 185 0';//255 230 0';//'255 255 0';
83         if(r == RUNE_VAMPIRE)
84                 _color = '64 0 128';//'108 0 217';//'128 0 255';//'179 0 204';//
85
86         if(r == CURSE_WEAK)
87                 _color = '255 0 0';
88         if(r == CURSE_VULNER)
89                 _color = '0 0 255';//'0 102 255';//
90         if(r == CURSE_VENOM)
91                 _color = '0 204 0';//'0 255 0';
92         if(r == CURSE_SLOW)
93                 _color = 0.5*'185 185 0';//'255 255 0';
94         if(r == CURSE_EMPATHY)
95                 _color = '179 0 204';//'128 0 255';
96
97         return _color * (1 / 255) * cvar("g_runematch_rune_color_strength");
98 }
99
100 float count_rune_spawnpoints()
101 {
102         float num;
103         entity e;
104         num = 0;
105         e = findchain(classname, "runematch_spawn_point");
106         while(e)
107         {
108                 num = num + 1;
109                 e = e.chain;
110
111         }
112         return num;
113 }
114
115 entity rune_find_spawnpoint(float num, float r)
116 {
117         entity e;
118         e = world;
119         do
120         {
121                 e = find(e, classname, "runematch_spawn_point");
122                 if(!e)
123                         e = find(e, classname, "runematch_spawn_point");
124                 if(!e)
125                         break;
126
127                 if(r < 0)
128                 {
129                         return e; // emergency: prevent crash
130                 }
131
132                 if(e.owner != world)//e.wait > time) // already taken
133                         continue;
134
135                 if(r <= 1)
136                 {
137                         return e;
138                 }
139
140                 r = r - 1;
141
142         }while(e);
143         return world;
144 }
145
146 void rune_respawn();
147
148 void RuneCarriedThink()
149 {
150         float rcount, rnum;
151         vector ang;
152         entity rune;
153
154         if(self.owner.classname != "player" || time < game_starttime)
155         {
156                 rune_respawn();
157                 return;
158         }
159
160         self.nextthink = time + 0.1;
161
162         // count runes my owner holds
163         rcount = 0;
164         rune = find(world, classname, "rune");
165         while(rune)
166         {
167                 if(rune.owner == self.owner)
168                         rcount = rcount + 1;
169                 if(rune == self)
170                         rnum = rcount;
171                 rune = find(rune, classname, "rune");
172         }
173
174         ang_y = rnum*(360 / rcount) + mod(time, 360)*45;//180;
175
176         makevectors(ang);
177
178         setorigin(self, v_forward*32);
179 }
180
181 void rune_touch()
182 {
183         if(other.classname != "player" || other.health < 1)
184                 return;
185         if(self.wait > time)
186                 return; // "notouch" time isn't finished
187
188         // detach from the spawn point you're on
189         if(self.owner.classname == "runematch_spawn_point")
190         {
191                 self.owner.owner = world;
192                 self.owner = world;
193         }
194
195         self.owner = other;
196         self.enemy.owner = other;
197         setattachment(self, other, "");
198
199         other.runes = other.runes | self.runes | self.enemy.runes;
200
201         //self.think = SUB_Null;
202         //self.nextthink = 0;
203         self.think = RuneCarriedThink;
204         self.nextthink = time;
205         self.touch = SUB_Null;
206
207         self.solid = SOLID_NOT;
208         setorigin(self, self.origin);
209
210         //sprint(other, strcat("^3You have picked up ",
211         //      RuneName(self.runes & (RUNE_LAST*2-1)), " and "));
212         //sprint(other, strcat(RuneName(self.enemy.runes & (CURSE_WEAK | CURSE_VULNER | CURSE_VENOM | CURSE_SLOW | CURSE_EMPATHY)), "\n"));
213
214         bprint("^3", other.netname, "^7 has picked up ",
215                 RuneName(self.runes & (RUNE_LAST*2-1)), "^7 and ");
216         bprint(RuneName(self.enemy.runes & (CURSE_WEAK | CURSE_VULNER | CURSE_VENOM | CURSE_SLOW | CURSE_EMPATHY)), "\n");
217 }
218
219 void rune_respawn()
220 {
221         float num, r;
222         entity spot;
223         num = count_rune_spawnpoints();
224         r = floor(random()*num) + 1;
225
226         if(self.owner.classname == "runematch_spawn_point")
227         {
228                 self.owner.owner = world;
229                 self.owner = world;
230         }
231
232
233         spot = rune_find_spawnpoint(num, r);
234         if(!spot)
235         {
236                 bprint("Warning: couldn't find spawn spot for rune respawn\n");
237                 return;
238         }
239
240         self.solid = SOLID_TRIGGER;
241
242         setorigin(self, spot.origin);
243         self.owner = spot;
244         spot.owner = self;
245
246         self.touch = rune_touch;
247
248         self.think = rune_respawn;
249         self.nextthink = time + cvar("g_runematch_shuffletime");//30 + random()*5; // fixme: cvar
250 }
251
252 entity FindRune(entity own, string clname, float r)
253 {
254         entity rune;
255         float _count, c;
256
257         c = _count = 0;
258         rune = world;
259
260         do
261         {
262                 rune = find(rune, classname, clname);
263                 if(!rune)
264                         rune = find(rune, classname, clname);
265                 if(!rune)
266                         break;
267                 if(rune.owner == own)
268                 {
269                         _count = _count + 1;
270                         if(_count >= r)
271                                 return rune;
272                         if(r <= 1)
273                                 return rune;
274                 }
275                 c = c + 1;
276         }while(c < 30);
277         return world;
278 }
279
280
281 void DropRune(entity pl, entity e)
282 {
283         //entity pl;
284
285         //pl = e.owner;
286         // detach from player
287         setattachment(e, world, "");
288         e.owner = world;
289         e.enemy.owner = world;
290         // don't instantly touch player again
291         e.wait = time + 1; // "notouch" time
292         e.movetype = MOVETYPE_TOSS;
293         e.solid = SOLID_TRIGGER;
294         // reposition itself if not picked up soon
295         e.think = rune_respawn;
296         e.nextthink = time + cvar("g_runematch_respawntime");//15 + random()*5; // fixme: cvar
297         e.touch = rune_touch;
298
299         pl.runes = pl.runes - (pl.runes & (e.runes | e.enemy.runes));
300
301         // toss from player
302         setorigin(e, pl.origin + '0 0 10');
303         e.velocity = '0 0 200' + '0 100 0'*crandom() + '100 0 0'*crandom();
304
305
306         bprint("^3", pl.netname, "^7 has lost ",
307                 RuneName(e.runes & (RUNE_LAST*2-1)), "^7 and ");
308         bprint(RuneName(e.enemy.runes & (CURSE_WEAK | CURSE_VULNER | CURSE_VENOM | CURSE_SLOW | CURSE_EMPATHY)), "\n");
309 }
310
311 float RuneMatchesCurse(float r, float c)
312 {
313         float cr;
314         if(r & RUNE_STRENGTH)
315                 cr = CURSE_WEAK;
316         else if(r & RUNE_DEFENSE)
317                 cr = CURSE_VULNER;
318         else if(r & RUNE_REGEN)
319                 cr = CURSE_VENOM;
320         else if(r & RUNE_SPEED)
321                 cr = CURSE_SLOW;
322         else if(r & RUNE_VAMPIRE)
323                 cr = CURSE_EMPATHY;
324         else return FALSE; // fixme: error?
325
326         if(c & cr)
327                 return TRUE;
328         return FALSE;
329 }
330
331 // player died, drop runes
332 // each rune should pair up with a random curse and then be tossed from the player
333 void DropAllRunes(entity pl)
334 {
335         entity rune, curse;
336         float rcount, ccount, r, c, rand, prevent_same, numtodrop, tries;
337
338         entity curse1, rune1, curse2, rune2;
339
340         rune = curse = world;
341         rcount = ccount = r = c = 0;
342         rune = find(rune, classname, "rune");
343         while(rune)
344         {
345                 if(rune.owner == pl)
346                         rcount = rcount + 1;
347                 rune = find(rune, classname, "rune");
348         }
349         curse = find(curse, classname, "curse");
350         while(curse)
351         {
352                 if(curse.owner == pl)
353                         ccount = ccount + 1;
354                 curse = find(curse, classname, "curse");
355         }
356
357         numtodrop = cvar("g_runematch_drop_runes_max");
358         prevent_same = !cvar("g_runematch_allow_same");
359
360         rune = curse = world;
361         do
362         {
363                 rune = find(rune, classname, "rune");
364                 if(!rune)
365                         break;
366                 if(rune.owner != pl)
367                         continue;
368
369
370                 // find a random curse
371                 tries = 15;
372                 if(ccount > 1 && prevent_same)
373                 {
374                         // avoid pairing runes and curses that match each other
375                         do{
376                                 rand = floor(random()*ccount) + 1;
377                                 curse = FindRune(pl, "curse", rand);
378                                 tries = tries - 1;
379                         }while(RuneMatchesCurse(rune.runes, curse.runes) && tries > 0);
380                         if(tries <= 0)
381                         {
382                                 bprint("warning: couldn't prevent same rune\n");
383                         }
384                 }
385                 else
386                 {
387                                 rand = floor(random()*ccount) + 1;
388                                 curse = FindRune(pl, "curse", rand);
389                 }
390
391                 if(!curse)
392                         error("Couldn't fine curse to bind rune to\n");
393
394                 // pair rune and curse
395
396                 rune1 = rune;
397                 curse1 = curse;
398                 rune2 = curse1.enemy;
399                 curse2 = rune1.enemy;
400
401                 if(rune1 != rune2) // not already attached to each other
402                 {
403                         rune1.enemy = curse1;
404                         curse1.enemy = rune1;
405                         setattachment(curse1, rune1, "");
406                         rune2.enemy = curse2;
407                         curse2.enemy = rune2;
408                         setattachment(curse2, rune2, "");
409                         //DropRune(pl, rune2);
410                         //ccount = ccount - 1;
411                         //rcount = rcount - 1;
412                 }
413                 DropRune(pl, rune1);
414
415                 if(numtodrop <=0)
416                 {
417                         rune1.think = rune_respawn;
418                         rune1.nextthink = time;
419                 }
420
421                 numtodrop = numtodrop - 1;
422
423                 ccount = ccount - 1;
424                 rcount = rcount - 1;
425
426         }while(rune);
427 }
428
429 void spawn_default_runespawnpoints()
430 {
431         entity spot, e;
432         spot = find(world, classname, "info_player_deathmatch");
433         while(spot)
434         {
435                 e = spawn();
436                 e.classname = "runematch_spawn_point";
437                 e.origin = spot.origin;
438                 spot = find(spot, classname, "info_player_deathmatch");
439         }
440 }
441
442 void rune_reset()
443 {
444         if(self.owner)
445                 if(self.owner.classname != "runematch_spawn_point")
446                         DropAllRunes(self.owner);
447         rune_respawn();
448 }
449
450 void spawn_runes()
451 {
452         float r, num, max_num, rn, cs, numrunes, runes_left, curses_left, tries, prevent_same;
453         entity e, spot;
454
455         if(self)
456                 remove(self);
457
458         // fixme: instead of placing them all now, why not
459         // simply create them all and let them call rune_respawn() as their think?
460
461         runes_left  = RUNE_STRENGTH | RUNE_DEFENSE | RUNE_REGEN | RUNE_SPEED | RUNE_VAMPIRE;
462         curses_left = CURSE_WEAK | CURSE_VULNER | CURSE_VENOM | CURSE_SLOW | CURSE_EMPATHY;
463         numrunes = 5;
464         max_num = num = count_rune_spawnpoints();
465
466         if(num < numrunes)
467         {
468                 spawn_default_runespawnpoints();
469         }
470
471         prevent_same = !cvar("g_runematch_allow_same");
472
473         max_num = num = count_rune_spawnpoints();
474
475         if(num < numrunes)
476                 error(strcat("not enough spawn points for runematch, need ", ftos(numrunes), " but found ", ftos(num), "\n"));
477
478         while(numrunes > 0)
479         {
480                 r = floor(random()*numrunes) + 1;
481                 if(r == 1)
482                         rn = RUNE_STRENGTH;
483                 else if(r == 2)
484                         rn = RUNE_DEFENSE;
485                 else if(r == 3)
486                         rn = RUNE_REGEN;
487                 else if(r == 4)
488                         rn = RUNE_SPEED;
489                 else
490                         rn = RUNE_VAMPIRE;
491
492                 if(curses_left > 1 && prevent_same)
493                 {
494                         tries = 15;
495                         // avoid pairing runes and curses that match each other
496                         do{
497                                 r = floor(random()*numrunes) + 1;
498                                 if(r == 1)
499                                         cs = CURSE_WEAK;
500                                 else if(r == 2)
501                                         cs = CURSE_VULNER;
502                                 else if(r == 3)
503                                         cs = CURSE_VENOM;
504                                 else if(r == 4)
505                                         cs = CURSE_SLOW;
506                                 else
507                                         cs = CURSE_EMPATHY;
508                                 tries = tries - 1;
509                         }while(RuneMatchesCurse(rn, cs) && tries > 0);
510                         if(tries <= 0)
511                         {
512                                 bprint("warning: couldn't prevent same rune\n");
513                         }
514                 }
515                 else
516                 {
517                         r = floor(random()*numrunes) + 1;
518                         if(r == 1)
519                                 cs = CURSE_WEAK;
520                         else if(r == 2)
521                                 cs = CURSE_VULNER;
522                         else if(r == 3)
523                                 cs = CURSE_VENOM;
524                         else if(r == 4)
525                                 cs = CURSE_SLOW;
526                         else
527                                 cs = CURSE_EMPATHY;
528                 }
529
530
531
532                 if(num <= 1)
533                         r = 1;
534                 else
535                         r = floor(random()*num*1.25) + 1;
536
537                 spot = rune_find_spawnpoint(num, r);
538
539                 if(spot == world)
540                 {
541                         error("failed to find runematch spawnpoint!\n");
542                 }
543
544                 // choose and spawn rune
545
546 /*              //rn = RUNE_FIRST;
547                 while(!runes_left & rn && rn <= RUNE_LAST)
548                         rn = rn * 2;
549                 if(rn > RUNE_LAST)
550                         error("couldn't select rune\n");
551                 runes_left = runes_left - rn;
552
553                 //cs = CURSE_FIRST;
554                 while(!curses_left & cs && cs <= CURSE_LAST)
555                         cs = cs * 2;
556                 if(cs > CURSE_LAST)
557                         error("couldn't select rune\n");
558                 curses_left = curses_left - cs;
559 */
560                 while(!runes_left & rn)
561                 {
562                         rn = rn * 2;
563                         if(rn > RUNE_LAST)
564                                 rn = RUNE_FIRST;
565                 }
566                 runes_left = runes_left - rn;
567
568                 while(!curses_left & cs)
569                 {
570                         cs = cs * 2;
571                         if(cs > CURSE_LAST)
572                                 cs = CURSE_FIRST;
573                 }
574                 curses_left = curses_left - cs;
575
576                 e = spawn();
577                 e.runes = rn;
578                 e.classname = "rune";
579                 e.touch = rune_touch;
580                 e.think = rune_respawn;
581                 e.nextthink = time + random();
582                 e.movetype = MOVETYPE_TOSS;
583                 e.solid = SOLID_TRIGGER;
584                 e.flags = FL_ITEM;
585                 e.reset = rune_reset;
586                 setmodel(e, "models/runematch/rune.mdl"); // precision set below
587                 setorigin(e, spot.origin);
588                 setsize(e, '0 0 -35', '0 0 0');
589
590                 e.enemy = spawn();
591                 e.enemy.enemy = e;
592                 e.enemy.classname = "curse";
593                 e.enemy.runes = cs;
594                 //e.enemy.avelocity = '300 500 200';
595                 setmodel(e.enemy, "models/runematch/curse.mdl"); // precision set below
596                 setorigin(e, '0 0 0');
597                 setattachment(e.enemy, e, "");
598
599
600                 e.colormod = RuneColormod(rn);
601                 e.enemy.colormod = RuneColormod(cs);
602
603                 e.alpha = e.enemy.alpha = cvar("g_runematch_rune_alpha");//0.78;
604                 e.effects = e.enemy.effects = cvar("g_runematch_rune_effects") | EF_LOWPRECISION;//EF_ADDITIVE;// | EF_FULLBRIGHT;
605
606                 //e.glow_size = e.enemy.glow_size = cvar("g_runematch_rune_glow_size");
607                 //e.glow_color = e.enemy.glow_color = cvar("g_runematch_rune_glow_color");
608
609                 // this spot is taken
610                 spot.owner = e;
611                 e.owner = spot;
612
613
614                 //rn = RUNE_FIRST;
615                 //cs = CURSE_FIRST;
616
617                 numrunes = numrunes - 1;
618                 num = num - 1;
619         }
620 }
621
622 void runematch_init()
623 {
624         if(!g_runematch)
625                 return;
626
627         entity e;
628         e = spawn();
629         e.think = spawn_runes;
630         e.nextthink = time + 0.1;
631 }
632
633
634 float runematch_point_time;
635
636 // give points to players who are holding runes
637 void RuneMatchGivePoints()
638 {
639         entity rune;
640
641         if(!g_runematch || !cvar("g_runematch_pointamt"))
642                 return;
643
644         if(gameover)
645                 return;
646
647         if(runematch_point_time > time)
648                 return;
649
650         runematch_point_time = time + cvar("g_runematch_pointrate");
651
652         rune = world;
653         do
654         {
655                 rune = find(rune, classname, "rune");
656                 if(!rune)
657                         return;
658
659                 if(rune.owner.classname == "player")
660                 {
661                         UpdateFrags(rune.owner, cvar("g_runematch_pointamt"));
662                 }
663         }while(rune);
664 }
665
666 float RunematchHandleFrags(entity attacker, entity targ, float f)
667 {
668         entity head;
669         float arunes, trunes, newfrags;
670
671         if(f <= 0)
672                 return f;
673         if(attacker == targ)
674                 return f;
675
676         arunes = trunes = 0;
677
678         head = find(world, classname, "rune");
679         while(head)
680         {
681                 if(head.owner == attacker)
682                 {
683                         arunes = arunes + 1;
684                 }
685                 else if(head.owner == targ)
686                 {
687                         trunes = trunes + 1;
688                 }
689
690                 head = find(head, classname, "rune");
691         }
692
693         if(!arunes && !trunes)
694                 return f - 1 + cvar("g_runematch_frags_norune"); // don't give points to players when no runes are involved.
695
696         if(arunes)
697         {       // got a kill while holding runes
698                 newfrags = newfrags + cvar("g_runematch_frags_killedby_runeholder");//5;
699         }
700         if(trunes)
701         {       // killed an enemy holding runes
702                 newfrags = newfrags + cvar("g_runematch_frags_killed_runeholder");//5;
703         }
704         if(newfrags)
705                 f = f - 1 + newfrags;
706
707         return f;
708 }