]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/gamec/runematch.c
removing this makefile also
[divverent/nexuiz.git] / data / qcsrc / server / 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         {
38                 remove(self);
39                 return;
40         }
41
42         setorigin(self, find_floor(self.origin) + '0 0 35');
43 }
44
45 string RuneName(float r)
46 {
47         if(r == RUNE_STRENGTH)
48                 return "^1Strength^7";
49         if(r == RUNE_DEFENSE)
50                 return "^4Defense^7";
51         if(r == RUNE_REGEN)
52                 return "^2Vitality^7";
53         if(r == RUNE_SPEED)
54                 return "^3Speed^7";
55         if(r == RUNE_VAMPIRE)
56                 return "^6Vampire^7";
57
58         if(r == CURSE_WEAK)
59                 return "^1Weakness^7";
60         if(r == CURSE_VULNER)
61                 return "^4Vulnerability^7";
62         if(r == CURSE_VENOM)
63                 return "^2Venom^7";
64         if(r == CURSE_SLOW)
65                 return "^3Slow^7";
66         if(r == CURSE_EMPATHY)
67                 return "^6Empathy^7";
68         return strcat("^8[unnamed", ftos(r), "]^7");
69 }
70
71 vector RuneColormod(float r)
72 {
73         vector _color;
74         if(r == RUNE_STRENGTH)
75                 _color = '255 0 0';
76         if(r == RUNE_DEFENSE)
77                 _color = '0 0 255';//'0 102 255';//
78         if(r == RUNE_REGEN)
79                 _color = '0 204 0';//'0 255 0';
80         if(r == RUNE_SPEED)
81                 _color = 0.35*'185 185 0';//255 230 0';//'255 255 0';
82         if(r == RUNE_VAMPIRE)
83                 _color = '64 0 128';//'108 0 217';//'128 0 255';//'179 0 204';//
84
85         if(r == CURSE_WEAK)
86                 _color = '255 0 0';
87         if(r == CURSE_VULNER)
88                 _color = '0 0 255';//'0 102 255';//
89         if(r == CURSE_VENOM)
90                 _color = '0 204 0';//'0 255 0';
91         if(r == CURSE_SLOW)
92                 _color = 0.5*'185 185 0';//'255 255 0';
93         if(r == CURSE_EMPATHY)
94                 _color = '179 0 204';//'128 0 255';
95
96         return _color * (1 / 255) * cvar("g_runematch_rune_color_strength");
97 }
98
99 float count_rune_spawnpoints()
100 {
101         float num;
102         entity e;
103         num = 0;
104         e = findchain(classname, "runematch_spawn_point");
105         while(e)
106         {
107                 num = num + 1;
108                 e = e.chain;
109
110         }
111         return num;
112 }
113
114 entity rune_find_spawnpoint(float num, float r)
115 {
116         entity e;
117         e = world;
118         do
119         {
120                 e = find(e, classname, "runematch_spawn_point");
121                 if(!e)
122                         e = find(e, classname, "runematch_spawn_point");
123                 if(!e)
124                         break;
125
126                 if(r < 0)
127                 {
128                         return e; // emergency: prevent crash
129                 }
130
131                 if(e.owner != world)//e.wait > time) // already taken
132                         continue;
133
134                 if(r <= 1)
135                 {
136                         return e;
137                 }
138
139                 r = r - 1;
140
141         }while(e);
142         return world;
143 }
144
145 void rune_respawn();
146
147 void RuneCarriedThink()
148 {
149         float rcount, rnum;
150         vector ang;
151         entity rune;
152
153         if(self.owner.classname != "player")
154         {
155                 rune_respawn();
156                 return;
157         }
158
159         self.nextthink = time + 0.1;
160
161         // count runes my owner holds
162         rcount = 0;
163         rune = find(world, classname, "rune");
164         while(rune)
165         {
166                 if(rune.owner == self.owner)
167                         rcount = rcount + 1;
168                 if(rune == self)
169                         rnum = rcount;
170                 rune = find(rune, classname, "rune");
171         }
172
173         ang_y = rnum*(360 / rcount) + math_mod(time, 360)*45;//180;
174
175         makevectors(ang);
176
177         //setorigin(self, v_forward*32);
178         self.view_ofs = v_forward*32;
179 }
180
181 void rune_touch()
182 {
183         if(other.classname != "player" || other.health < 2)
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         setattachment(self, world, "");
199
200         self.movetype = MOVETYPE_FOLLOW;
201         self.aiment = other;
202         self.view_ofs = other.origin;
203
204         setorigin(self, '0 0 0');
205         other.runes = other.runes | self.runes | self.enemy.runes;
206
207         //self.think = SUB_Null;
208         //self.nextthink = 0;
209         self.think = RuneCarriedThink;
210         self.nextthink = time;
211         self.touch = SUB_Null;
212
213         self.solid = SOLID_NOT;
214         setorigin(self, self.origin);
215
216         //sprint(other, strcat("^3You have picked up ",
217         //      RuneName(self.runes & (RUNE_LAST*2-1)), " and "));
218         //sprint(other, strcat(RuneName(self.enemy.runes & (CURSE_WEAK | CURSE_VULNER | CURSE_VENOM | CURSE_SLOW | CURSE_EMPATHY)), "\n"));
219
220         bprint(strcat("^3", other.netname, "^7 has picked up ",
221                 RuneName(self.runes & (RUNE_LAST*2-1)), "^7 and "));
222         bprint(strcat(RuneName(self.enemy.runes & (CURSE_WEAK | CURSE_VULNER | CURSE_VENOM | CURSE_SLOW | CURSE_EMPATHY)), "\n"));
223 }
224
225 void rune_respawn()
226 {
227         float num, r;
228         entity spot;
229         num = count_rune_spawnpoints();
230         r = ceil(random()*num);
231
232         if(self.owner.classname == "runematch_spawn_point")
233         {
234                 self.owner.owner = world;
235                 self.owner = world;
236         }
237
238
239         spot = rune_find_spawnpoint(num, r);
240         if(!spot)
241         {
242                 bprint("Warning: couldn't find spawn spot for rune respawn\n");
243                 return;
244         }
245
246         self.solid = SOLID_TRIGGER;
247
248         setorigin(self, spot.origin);
249         self.owner = spot;
250         spot.owner = self;
251
252         self.touch = rune_touch;
253
254         self.think = rune_respawn;
255         self.nextthink = time + cvar("g_runematch_shuffletime");//30 + random()*5; // fixme: cvar
256 }
257
258 entity FindRune(entity own, string clname, float r)
259 {
260         entity rune;
261         float _count, c;
262
263         c = _count = 0;
264         rune = world;
265
266         do
267         {
268                 rune = find(rune, classname, clname);
269                 if(!rune)
270                         rune = find(rune, classname, clname);
271                 if(!rune)
272                         break;
273                 if(rune.owner == own)
274                 {
275                         _count = _count + 1;
276                         if(_count >= r)
277                                 return rune;
278                         if(r <= 1)
279                                 return rune;
280                 }
281                 c = c + 1;
282         }while(c < 30);
283         return world;
284 }
285
286
287 void DropRune(entity pl, entity e)
288 {
289         //entity pl;
290
291         //pl = e.owner;
292         // detach from player
293         setattachment(e, world, "");
294         e.owner = 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 + cvar("g_runematch_respawntime");//15 + random()*5; // fixme: cvar
303         e.touch = rune_touch;
304
305         pl.runes = pl.runes - (pl.runes & (e.runes | e.enemy.runes));
306
307         // toss from player
308         setorigin(e, pl.origin + '0 0 10');
309         e.velocity = '0 0 200' + '0 100 0'*crandom() + '100 0 0'*crandom();
310
311
312         bprint(strcat("^3", pl.netname, "^7 has lost ",
313                 RuneName(e.runes & (RUNE_LAST*2-1)), "^7 and "));
314         bprint(strcat(RuneName(e.enemy.runes & (CURSE_WEAK | CURSE_VULNER | CURSE_VENOM | CURSE_SLOW | CURSE_EMPATHY)), "\n"));
315 }
316
317 float RuneMatchesCurse(float r, float c)
318 {
319         float cr;
320         if(r & RUNE_STRENGTH)
321                 cr = CURSE_WEAK;
322         else if(r & RUNE_DEFENSE)
323                 cr = CURSE_VULNER;
324         else if(r & RUNE_REGEN)
325                 cr = CURSE_VENOM;
326         else if(r & RUNE_SPEED)
327                 cr = CURSE_SLOW;
328         else if(r & RUNE_VAMPIRE)
329                 cr = CURSE_EMPATHY;
330         else return FALSE; // fixme: error?
331
332         if(c & cr)
333                 return TRUE;
334         return FALSE;
335 }
336
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)
340 {
341         entity rune, curse;
342         float rcount, ccount, r, c, rand, prevent_same, numtodrop, tries;
343
344         entity c1, r1, c2, r2;
345
346         rune = curse = world;
347         rcount = ccount = r = c = 0;
348         rune = find(rune, classname, "rune");
349         while(rune)
350         {
351                 if(rune.owner == pl)
352                         rcount = rcount + 1;
353                 rune = find(rune, classname, "rune");
354         }
355         curse = find(curse, classname, "curse");
356         while(curse)
357         {
358                 if(curse.owner == pl)
359                         ccount = ccount + 1;
360                 curse = find(curse, classname, "curse");
361         }
362
363         numtodrop = cvar("g_runematch_drop_runes_max");
364         prevent_same = !cvar("g_runematch_allow_same");
365
366         rune = curse = world;
367         do
368         {
369                 rune = find(rune, classname, "rune");
370                 if(!rune)
371                         break;
372                 if(rune.owner != pl)
373                         continue;
374
375
376                 // find a random curse
377                 tries = 15;
378                 if(ccount > 1 && prevent_same)
379                 {
380                         // avoid pairing runes and curses that match each other
381                         do{
382                                 rand = ceil(random()*ccount);
383                                 curse = FindRune(pl, "curse", rand);
384                                 tries = tries - 1;
385                         }while(RuneMatchesCurse(rune.runes, curse.runes) && tries > 0);
386                         if(tries <= 0)
387                         {
388                                 bprint("warning: couldn't prevent same rune\n");
389                         }
390                 }
391                 else
392                 {
393                                 rand = ceil(random()*ccount);
394                                 curse = FindRune(pl, "curse", rand);
395                 }
396
397                 if(!curse)
398                         error("Couldn't fine curse to bind rune to\n");
399
400                 // pair rune and curse
401
402                 r1 = rune;
403                 c1 = curse;
404                 r2 = c1.enemy;
405                 c2 = r1.enemy;
406
407                 if(r1 != r2) // not already attached to each other
408                 {
409                         r1.enemy = c1;
410                         c1.enemy = r1;
411                         setattachment(c1, r1, "");
412                         r2.enemy = c2;
413                         c2.enemy = r2;
414                         setattachment(c2, r2, "");
415                         //DropRune(pl, r2);
416                         //ccount = ccount - 1;
417                         //rcount = rcount - 1;
418                 }
419                 DropRune(pl, r1);
420
421                 if(numtodrop <=0)
422                 {
423                         r1.think = rune_respawn;
424                         r1.nextthink = time;
425                 }
426
427                 numtodrop = numtodrop - 1;
428
429                 ccount = ccount - 1;
430                 rcount = rcount - 1;
431
432         }while(rune);
433 }
434
435 void spawn_default_runespawnpoints()
436 {
437         entity spot, e;
438         spot = find(world, classname, "info_player_deathmatch");
439         while(spot)
440         {
441                 e = spawn();
442                 e.classname = "runematch_spawn_point";
443                 e.origin = spot.origin;
444                 spot = find(spot, classname, "info_player_deathmatch");
445         }
446 }
447
448 void spawn_runes()
449 {
450         float r, num, max_num, rn, cs, numrunes, runes_left, curses_left, tries, prevent_same;
451         entity e, spot;
452
453         if(self)
454                 remove(self);
455
456         // fixme: instead of placing them all now, why not
457         // simply create them all and let them call rune_respawn() as their think?
458
459         runes_left  = RUNE_STRENGTH | RUNE_DEFENSE | RUNE_REGEN | RUNE_SPEED | RUNE_VAMPIRE;
460         curses_left = CURSE_WEAK | CURSE_VULNER | CURSE_VENOM | CURSE_SLOW | CURSE_EMPATHY;
461         numrunes = 5;
462         max_num = num = count_rune_spawnpoints();
463
464         if(num < numrunes)
465         {
466                 spawn_default_runespawnpoints();
467         }
468
469         prevent_same = !cvar("g_runematch_allow_same");
470
471         max_num = num = count_rune_spawnpoints();
472
473         if(num < numrunes)
474                 error(strcat("not enough spawn points for runematch, need ", ftos(numrunes), " but found ", ftos(num), "\n"));
475
476         while(numrunes > 0)
477         {
478                 r = ceil(random()*numrunes);
479                 if(r == 1)
480                         rn = RUNE_STRENGTH;
481                 else if(r == 2)
482                         rn = RUNE_DEFENSE;
483                 else if(r == 3)
484                         rn = RUNE_REGEN;
485                 else if(r == 4)
486                         rn = RUNE_SPEED;
487                 else
488                         rn = RUNE_VAMPIRE;
489
490                 if(curses_left > 1 && prevent_same)
491                 {
492                         tries = 15;
493                         // avoid pairing runes and curses that match each other
494                         do{
495                                 r = ceil(random()*numrunes);
496                                 if(r == 1)
497                                         cs = CURSE_WEAK;
498                                 else if(r == 2)
499                                         cs = CURSE_VULNER;
500                                 else if(r == 3)
501                                         cs = CURSE_VENOM;
502                                 else if(r == 4)
503                                         cs = CURSE_SLOW;
504                                 else
505                                         cs = CURSE_EMPATHY;
506                                 tries = tries - 1;
507                         }while(RuneMatchesCurse(rn, cs) && tries > 0);
508                         if(tries <= 0)
509                         {
510                                 bprint("warning: couldn't prevent same rune\n");
511                         }
512                 }
513                 else
514                 {
515                         r = ceil(random()*numrunes);
516                         if(r == 1)
517                                 cs = CURSE_WEAK;
518                         else if(r == 2)
519                                 cs = CURSE_VULNER;
520                         else if(r == 3)
521                                 cs = CURSE_VENOM;
522                         else if(r == 4)
523                                 cs = CURSE_SLOW;
524                         else
525                                 cs = CURSE_EMPATHY;
526                 }
527
528
529
530                 if(num <= 1)
531                         r = 1;
532                 else
533                         r = ceil(random()*num*1.25);
534
535                 spot = rune_find_spawnpoint(num, r);
536
537                 if(spot == world)
538                 {
539                         error("failed to find runematch spawnpoint!\n");
540                 }
541
542                 // choose and spawn rune
543
544 /*              //rn = RUNE_FIRST;
545                 while(!runes_left & rn && rn <= RUNE_LAST)
546                         rn = rn * 2;
547                 if(rn > RUNE_LAST)
548                         error("couldn't select rune\n");
549                 runes_left = runes_left - rn;
550
551                 //cs = CURSE_FIRST;
552                 while(!curses_left & cs && cs <= CURSE_LAST)
553                         cs = cs * 2;
554                 if(cs > CURSE_LAST)
555                         error("couldn't select rune\n");
556                 curses_left = curses_left - cs;
557 */
558                 while(!runes_left & rn)
559                 {
560                         rn = rn * 2;
561                         if(rn > RUNE_LAST)
562                                 rn = RUNE_FIRST;
563                 }
564                 runes_left = runes_left - rn;
565
566                 while(!curses_left & cs)
567                 {
568                         cs = cs * 2;
569                         if(cs > CURSE_LAST)
570                                 cs = CURSE_FIRST;
571                 }
572                 curses_left = curses_left - cs;
573
574                 e = spawn();
575                 e.runes = rn;
576                 e.classname = "rune";
577                 e.touch = rune_touch;
578                 e.think = rune_respawn;
579                 e.nextthink = time + random();
580                 e.movetype = MOVETYPE_TOSS;
581                 e.solid = SOLID_TRIGGER;
582                 e.flags = FL_ITEM;
583                 setmodel(e, "models/runematch/rune.mdl");
584                 setorigin(e, spot.origin);
585                 setsize(e, '0 0 -35', '0 0 0');
586
587                 e.enemy = spawn();
588                 e.enemy.enemy = e;
589                 e.enemy.classname = "curse";
590                 e.enemy.runes = cs;
591                 //e.enemy.avelocity = '300 500 200';
592                 setmodel(e.enemy, "models/runematch/curse.mdl");
593                 setorigin(e, '0 0 0');
594                 setattachment(e.enemy, e, "");
595
596
597                 e.colormod = RuneColormod(rn);
598                 e.enemy.colormod = RuneColormod(cs);
599
600                 e.alpha = e.enemy.alpha = cvar("g_runematch_rune_alpha");//0.78;
601                 e.effects = e.enemy.effects = cvar("g_runematch_rune_effects");//EF_ADDITIVE;// | EF_FULLBRIGHT;
602
603                 // Savage: Save some bandwidth
604                 self.effects |= EF_LOWPRECISION;
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(!cvar("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(!cvar("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                         rune.owner.frags = rune.owner.frags + 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 }