- now using a fresh dpextensions.qc
[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 runematch_spawn_point (1 0 0) (-16 -16 -24) (16 16 24)
31 spawn point for runes in runematch
32 */
33
34 void runematch_spawn_point()
35 {
36         if(!cvar("g_runematch"))
37         {
38                 remove(self);
39                 return;
40         }
41
42         droptofloor();
43         setorigin(self, self.origin + '0 0 35');
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")
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) + math_mod(time, 360)*45;//180;
175
176         makevectors(ang);
177
178         //setorigin(self, v_forward*32);
179         self.view_ofs = v_forward*32;
180 }
181
182 void rune_touch()
183 {
184         if(other.classname != "player" || other.health < 2)
185                 return;
186         if(self.wait > time)
187                 return; // "notouch" time isn't finished
188
189         // detach from the spawn point you're on
190         if(self.owner.classname == "runematch_spawn_point")
191         {
192                 self.owner.owner = world;
193                 self.owner = world;
194         }
195
196         self.owner = other;
197         self.enemy.owner = other;
198         //setattachment(self, other, "");
199         setattachment(self, world, "");
200
201         self.movetype = MOVETYPE_FOLLOW;
202         self.aiment = other;
203         self.view_ofs = other.origin;
204
205         setorigin(self, '0 0 0');
206         other.runes = other.runes | self.runes | self.enemy.runes;
207
208         //self.think = SUB_Null;
209         //self.nextthink = 0;
210         self.think = RuneCarriedThink;
211         self.nextthink = time;
212         self.touch = SUB_Null;
213
214         self.solid = SOLID_NOT;
215         setorigin(self, self.origin);
216
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"));
220
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");
224 }
225
226 void rune_respawn()
227 {
228         float num, r;
229         entity spot;
230         num = count_rune_spawnpoints();
231         r = ceil(random()*num);
232
233         if(self.owner.classname == "runematch_spawn_point")
234         {
235                 self.owner.owner = world;
236                 self.owner = world;
237         }
238
239
240         spot = rune_find_spawnpoint(num, r);
241         if(!spot)
242         {
243                 bprint("Warning: couldn't find spawn spot for rune respawn\n");
244                 return;
245         }
246
247         self.solid = SOLID_TRIGGER;
248
249         setorigin(self, spot.origin);
250         self.owner = spot;
251         spot.owner = self;
252
253         self.touch = rune_touch;
254
255         self.think = rune_respawn;
256         self.nextthink = time + cvar("g_runematch_shuffletime");//30 + random()*5; // fixme: cvar
257 }
258
259 entity FindRune(entity own, string clname, float r)
260 {
261         entity rune;
262         float _count, c;
263
264         c = _count = 0;
265         rune = world;
266
267         do
268         {
269                 rune = find(rune, classname, clname);
270                 if(!rune)
271                         rune = find(rune, classname, clname);
272                 if(!rune)
273                         break;
274                 if(rune.owner == own)
275                 {
276                         _count = _count + 1;
277                         if(_count >= r)
278                                 return rune;
279                         if(r <= 1)
280                                 return rune;
281                 }
282                 c = c + 1;
283         }while(c < 30);
284         return world;
285 }
286
287
288 void DropRune(entity pl, entity e)
289 {
290         //entity pl;
291
292         //pl = e.owner;
293         // detach from player
294         setattachment(e, world, "");
295         e.owner = 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;
305
306         pl.runes = pl.runes - (pl.runes & (e.runes | e.enemy.runes));
307
308         // toss from player
309         setorigin(e, pl.origin + '0 0 10');
310         e.velocity = '0 0 200' + '0 100 0'*crandom() + '100 0 0'*crandom();
311
312
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");
316 }
317
318 float RuneMatchesCurse(float r, float c)
319 {
320         float cr;
321         if(r & RUNE_STRENGTH)
322                 cr = CURSE_WEAK;
323         else if(r & RUNE_DEFENSE)
324                 cr = CURSE_VULNER;
325         else if(r & RUNE_REGEN)
326                 cr = CURSE_VENOM;
327         else if(r & RUNE_SPEED)
328                 cr = CURSE_SLOW;
329         else if(r & RUNE_VAMPIRE)
330                 cr = CURSE_EMPATHY;
331         else return FALSE; // fixme: error?
332
333         if(c & cr)
334                 return TRUE;
335         return FALSE;
336 }
337
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)
341 {
342         entity rune, curse;
343         float rcount, ccount, r, c, rand, prevent_same, numtodrop, tries;
344
345         entity c1, r1, c2, r2;
346
347         rune = curse = world;
348         rcount = ccount = r = c = 0;
349         rune = find(rune, classname, "rune");
350         while(rune)
351         {
352                 if(rune.owner == pl)
353                         rcount = rcount + 1;
354                 rune = find(rune, classname, "rune");
355         }
356         curse = find(curse, classname, "curse");
357         while(curse)
358         {
359                 if(curse.owner == pl)
360                         ccount = ccount + 1;
361                 curse = find(curse, classname, "curse");
362         }
363
364         numtodrop = cvar("g_runematch_drop_runes_max");
365         prevent_same = !cvar("g_runematch_allow_same");
366
367         rune = curse = world;
368         do
369         {
370                 rune = find(rune, classname, "rune");
371                 if(!rune)
372                         break;
373                 if(rune.owner != pl)
374                         continue;
375
376
377                 // find a random curse
378                 tries = 15;
379                 if(ccount > 1 && prevent_same)
380                 {
381                         // avoid pairing runes and curses that match each other
382                         do{
383                                 rand = ceil(random()*ccount);
384                                 curse = FindRune(pl, "curse", rand);
385                                 tries = tries - 1;
386                         }while(RuneMatchesCurse(rune.runes, curse.runes) && tries > 0);
387                         if(tries <= 0)
388                         {
389                                 bprint("warning: couldn't prevent same rune\n");
390                         }
391                 }
392                 else
393                 {
394                                 rand = ceil(random()*ccount);
395                                 curse = FindRune(pl, "curse", rand);
396                 }
397
398                 if(!curse)
399                         error("Couldn't fine curse to bind rune to\n");
400
401                 // pair rune and curse
402
403                 r1 = rune;
404                 c1 = curse;
405                 r2 = c1.enemy;
406                 c2 = r1.enemy;
407
408                 if(r1 != r2) // not already attached to each other
409                 {
410                         r1.enemy = c1;
411                         c1.enemy = r1;
412                         setattachment(c1, r1, "");
413                         r2.enemy = c2;
414                         c2.enemy = r2;
415                         setattachment(c2, r2, "");
416                         //DropRune(pl, r2);
417                         //ccount = ccount - 1;
418                         //rcount = rcount - 1;
419                 }
420                 DropRune(pl, r1);
421
422                 if(numtodrop <=0)
423                 {
424                         r1.think = rune_respawn;
425                         r1.nextthink = time;
426                 }
427
428                 numtodrop = numtodrop - 1;
429
430                 ccount = ccount - 1;
431                 rcount = rcount - 1;
432
433         }while(rune);
434 }
435
436 void spawn_default_runespawnpoints()
437 {
438         entity spot, e;
439         spot = find(world, classname, "info_player_deathmatch");
440         while(spot)
441         {
442                 e = spawn();
443                 e.classname = "runematch_spawn_point";
444                 e.origin = spot.origin;
445                 spot = find(spot, classname, "info_player_deathmatch");
446         }
447 }
448
449 void spawn_runes()
450 {
451         float r, num, max_num, rn, cs, numrunes, runes_left, curses_left, tries, prevent_same;
452         entity e, spot;
453
454         if(self)
455                 remove(self);
456
457         // fixme: instead of placing them all now, why not
458         // simply create them all and let them call rune_respawn() as their think?
459
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;
462         numrunes = 5;
463         max_num = num = count_rune_spawnpoints();
464
465         if(num < numrunes)
466         {
467                 spawn_default_runespawnpoints();
468         }
469
470         prevent_same = !cvar("g_runematch_allow_same");
471
472         max_num = num = count_rune_spawnpoints();
473
474         if(num < numrunes)
475                 error(strcat("not enough spawn points for runematch, need ", ftos(numrunes), " but found ", ftos(num), "\n"));
476
477         while(numrunes > 0)
478         {
479                 r = ceil(random()*numrunes);
480                 if(r == 1)
481                         rn = RUNE_STRENGTH;
482                 else if(r == 2)
483                         rn = RUNE_DEFENSE;
484                 else if(r == 3)
485                         rn = RUNE_REGEN;
486                 else if(r == 4)
487                         rn = RUNE_SPEED;
488                 else
489                         rn = RUNE_VAMPIRE;
490
491                 if(curses_left > 1 && prevent_same)
492                 {
493                         tries = 15;
494                         // avoid pairing runes and curses that match each other
495                         do{
496                                 r = ceil(random()*numrunes);
497                                 if(r == 1)
498                                         cs = CURSE_WEAK;
499                                 else if(r == 2)
500                                         cs = CURSE_VULNER;
501                                 else if(r == 3)
502                                         cs = CURSE_VENOM;
503                                 else if(r == 4)
504                                         cs = CURSE_SLOW;
505                                 else
506                                         cs = CURSE_EMPATHY;
507                                 tries = tries - 1;
508                         }while(RuneMatchesCurse(rn, cs) && tries > 0);
509                         if(tries <= 0)
510                         {
511                                 bprint("warning: couldn't prevent same rune\n");
512                         }
513                 }
514                 else
515                 {
516                         r = ceil(random()*numrunes);
517                         if(r == 1)
518                                 cs = CURSE_WEAK;
519                         else if(r == 2)
520                                 cs = CURSE_VULNER;
521                         else if(r == 3)
522                                 cs = CURSE_VENOM;
523                         else if(r == 4)
524                                 cs = CURSE_SLOW;
525                         else
526                                 cs = CURSE_EMPATHY;
527                 }
528
529
530
531                 if(num <= 1)
532                         r = 1;
533                 else
534                         r = ceil(random()*num*1.25);
535
536                 spot = rune_find_spawnpoint(num, r);
537
538                 if(spot == world)
539                 {
540                         error("failed to find runematch spawnpoint!\n");
541                 }
542
543                 // choose and spawn rune
544
545 /*              //rn = RUNE_FIRST;
546                 while(!runes_left & rn && rn <= RUNE_LAST)
547                         rn = rn * 2;
548                 if(rn > RUNE_LAST)
549                         error("couldn't select rune\n");
550                 runes_left = runes_left - rn;
551
552                 //cs = CURSE_FIRST;
553                 while(!curses_left & cs && cs <= CURSE_LAST)
554                         cs = cs * 2;
555                 if(cs > CURSE_LAST)
556                         error("couldn't select rune\n");
557                 curses_left = curses_left - cs;
558 */
559                 while(!runes_left & rn)
560                 {
561                         rn = rn * 2;
562                         if(rn > RUNE_LAST)
563                                 rn = RUNE_FIRST;
564                 }
565                 runes_left = runes_left - rn;
566
567                 while(!curses_left & cs)
568                 {
569                         cs = cs * 2;
570                         if(cs > CURSE_LAST)
571                                 cs = CURSE_FIRST;
572                 }
573                 curses_left = curses_left - cs;
574
575                 e = spawn();
576                 e.runes = rn;
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;
583                 e.flags = FL_ITEM;
584                 setmodel(e, "models/runematch/rune.mdl");
585                 setorigin(e, spot.origin);
586                 setsize(e, '0 0 -35', '0 0 0');
587
588                 e.enemy = spawn();
589                 e.enemy.enemy = e;
590                 e.enemy.classname = "curse";
591                 e.enemy.runes = cs;
592                 //e.enemy.avelocity = '300 500 200';
593                 setmodel(e.enemy, "models/runematch/curse.mdl");
594                 setorigin(e, '0 0 0');
595                 setattachment(e.enemy, e, "");
596
597
598                 e.colormod = RuneColormod(rn);
599                 e.enemy.colormod = RuneColormod(cs);
600
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_ADDITIVE;// | EF_FULLBRIGHT;
603
604                 // Savage: Save some bandwidth
605                 self.effects |= EF_LOWPRECISION;
606
607                 //e.glow_size = e.enemy.glow_size = cvar("g_runematch_rune_glow_size");
608                 //e.glow_color = e.enemy.glow_color = cvar("g_runematch_rune_glow_color");
609
610                 // this spot is taken
611                 spot.owner = e;
612                 e.owner = spot;
613
614
615                 //rn = RUNE_FIRST;
616                 //cs = CURSE_FIRST;
617
618                 numrunes = numrunes - 1;
619                 num = num - 1;
620         }
621 }
622
623 void runematch_init()
624 {
625         if(!cvar("g_runematch"))
626                 return;
627
628         entity e;
629         e = spawn();
630         e.think = spawn_runes;
631         e.nextthink = time + 0.1;
632 }
633
634
635 float runematch_point_time;
636
637 // give points to players who are holding runes
638 void RuneMatchGivePoints()
639 {
640         entity rune;
641
642         if(!cvar("g_runematch") || !cvar("g_runematch_pointamt"))
643                 return;
644
645         if(gameover)
646                 return;
647
648         if(runematch_point_time > time)
649                 return;
650
651         runematch_point_time = time + cvar("g_runematch_pointrate");
652
653         rune = world;
654         do
655         {
656                 rune = find(rune, classname, "rune");
657                 if(!rune)
658                         return;
659
660                 if(rune.owner.classname == "player")
661                 {
662                         UpdateFrags(rune.owner, cvar("g_runematch_pointamt"));
663                 }
664         }while(rune);
665 }
666
667 float RunematchHandleFrags(entity attacker, entity targ, float f)
668 {
669         entity head;
670         float arunes, trunes, newfrags;
671
672         if(f <= 0)
673                 return f;
674         if(attacker == targ)
675                 return f;
676
677         arunes = trunes = 0;
678
679         head = find(world, classname, "rune");
680         while(head)
681         {
682                 if(head.owner == attacker)
683                 {
684                         arunes = arunes + 1;
685                 }
686                 else if(head.owner == targ)
687                 {
688                         trunes = trunes + 1;
689                 }
690
691                 head = find(head, classname, "rune");
692         }
693
694         if(!arunes && !trunes)
695                 return f - 1 + cvar("g_runematch_frags_norune"); // don't give points to players when no runes are involved.
696
697         if(arunes)
698         {       // got a kill while holding runes
699                 newfrags = newfrags + cvar("g_runematch_frags_killedby_runeholder");//5;
700         }
701         if(trunes)
702         {       // killed an enemy holding runes
703                 newfrags = newfrags + cvar("g_runematch_frags_killed_runeholder");//5;
704         }
705         if(newfrags)
706                 f = f - 1 + newfrags;
707
708         return f;
709 }