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