rename teamplay_default to teamplay_mode, stop using the "teamplay" cvar (more cleanu...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / monsters / mode_management.qc
1
2 string(float c) colorname =
3 {
4         // yikes, the quake color set is HARD to describe
5         // many are easy, but, uh, 2 browns???
6         // 2 purples???
7         // that 'pink' is hard to classify
8         // I think 'biege' is a fairly good name for color 10
9         // oh well, gotta do all the color names...
10         if (c ==  0) return "white";
11         else if (c ==  1) return "brown";
12         else if (c ==  2) return "lightblue";
13         else if (c ==  3) return "green";
14         else if (c ==  4) return "red";
15         else if (c ==  5) return "lighterbrown";
16         else if (c ==  6) return "orange";
17         else if (c ==  7) return "pink";
18         else if (c ==  8) return "purple";
19         else if (c ==  9) return "redishpurple";
20         else if (c == 10) return "biege";
21         else if (c == 11) return "aqua";
22         else if (c == 12) return "yellow";
23         else if (c == 13) return "blue";
24         else if (c == 14) return "flamingorange";
25         else if (c == 15) return "psychadelic";
26         else              return "INVALID COLOR";
27 };
28
29 float mode_shirtmustmatchpants;
30 float mode_numteams;
31 float mode_allowedteams[17];
32 float mode_teamcount[17];
33 float mode_teamscore[17];
34
35 void() mode_initallowedteams =
36 {
37         local float c;
38         c = 0;
39         while(c < 17)
40         {
41                 mode_allowedteams[c] = FALSE;
42                 c = c + 1;
43         }
44         mode_allowedteams[5] = TRUE; // red
45         mode_allowedteams[14] = TRUE; // blue
46         if (deathmatch == DM_ELIM
47          || deathmatch == DM_ONEVSALL
48          || deathmatch == DM_CTF_2TEAM
49          || deathmatch == DM_DOMINATION
50          || deathmatch == DM_SUPERDOMINATION)
51                 mode_numteams = 2;
52         else if (deathmatch == DM_CTF_3TEAM)
53         {
54                 mode_numteams = 3;
55                 mode_allowedteams[13] = TRUE; // yellow
56         }
57         else
58         {
59                 mode_numteams = 16;
60                 c = 1;
61                 while(c < 17)
62                 {
63                         mode_allowedteams[c] = TRUE;
64                         c = c + 1;
65                 }
66         }
67 };
68
69 float(float t) validteam =
70 {
71         return mode_allowedteams[t];
72 };
73
74 float() weakestteam =
75 {
76         local float bestteam;
77         local float bestteamcount;
78         local float headcount;
79         local float c;
80         bestteam = -1;
81         bestteamcount = 0;
82         c = 1;
83         while (c < 17)
84         {
85                 if (mode_allowedteams[c])
86                 {
87                         headcount = mode_teamcount[c];
88                         if (bestteamcount > headcount || bestteam == -1)
89                         {
90                                 bestteamcount = headcount;
91                                 bestteam = c;
92                         }
93                 }
94                 c = c + 1;
95         }
96         return bestteam;
97 };
98
99 void() updateteams =
100 {
101         local entity head;
102         local float c;
103         c = 1;
104         while (c < 17)
105         {
106                 mode_teamcount[c] = 0;
107                 mode_teamscore[c] = 0;
108                 c = c + 1;
109         }
110         c = 0;
111         head = nextent(world);
112         while (c < maxclients)
113         {
114                 mode_teamcount[head.team] = mode_teamcount[head.team] + 1;
115                 mode_teamscore[head.team] = mode_teamscore[head.team] + head.frags;
116                 c = c + 1;
117                 head = nextent(head);
118         }
119 };
120
121 float(float p) checkteamcolor =
122 {
123         if (!validteam(p + 1))
124                 p = weakestteam() - 1;
125         return p;
126 };
127
128 void(float c) SV_ChangeTeam =
129 {
130         local float pants, shirt, old;
131         old = self.clientcolors & 15;
132         if (c >= 0)
133         {
134                 pants = c & 15;
135                 shirt = (c / 16) & 15;
136         }
137         else
138         {
139                 pants = -1;
140                 shirt = -1;
141         }
142         pants = checkteamcolor(pants);
143         if (mode_shirtmustmatchpants || shirt < 0)
144                 shirt = pants;
145         setcolor(self, pants + shirt * 16);
146         if (pants != old && old >= 0 && teams_matter && deathmatch)
147         {
148                 T_Damage(self, self, self, 0, 0, " changed teams", DT_TELEFRAG, self.origin, '0 0 0', Obituary_Generic);
149                 self.frags = 0;
150                 PutClientInServer ();
151         }
152 };
153
154 void() checkinvalidteam =
155 {
156         // call SV_ChangeTeam to trigger the weakestteam change
157         if (!validteam(self.team))
158                 SV_ChangeTeam(self.team - 1);
159 };
160
161
162 string dmmessage;
163
164 void(string m) setdm =
165 {
166         error("THIS MUST NOT BE CALLED IN XONOTIC");
167 }
168
169 void(string m) setteamplay =
170 {
171         error("THIS MUST NOT BE CALLED IN XONOTIC");
172 }
173
174 void() mode_updatecvars =
175 {
176         error("THIS MUST NOT BE CALLED IN XONOTIC");
177 };
178
179 float nextcvarupdate;
180 void() deathmatch7update;
181 void() modeupdate =
182 {
183         if (time > nextcvarupdate)
184         {
185                 nextcvarupdate = time + 1;
186                 mode_updatecvars();
187         }
188         deathmatch7update();
189 };
190
191 // true if items should respawn
192 float itemrespawn;
193 // when the next monster spawning check will occur in deathmatch 7 mode
194 float spawnchecktime;
195
196 void() precachemonsters;
197 void() superdomination_precache;
198 void() modesetup =
199 {
200         mode_shirtmustmatchpants = deathmatch >= DM_TEAM_MODS_START && deathmatch < DM_TEAM_MODS_END;
201         mode_initallowedteams();
202
203         itemrespawn = cvar("deathmatch") + cvar("coop");
204
205         // don't spawn any monsters until 15 seconds
206         spawnchecktime = 15;
207         if (deathmatch == 7 || cvar("spawnmonsters") >= 1)
208                 precachemonsters();
209
210         superdomination_precache();
211 };
212
213 float monsterspawn;
214 void() spawnmonster_think =
215 {
216         //local float c;
217         local void() sfunc;
218         self.nextthink = time;
219         if (time > self.cnt)
220         {
221                 remove(self);
222                 return;
223         }
224         if (vlen(self.velocity) > 5)
225                 return; // try again later
226
227         //if (!(self.flags & FL_FLY))
228         //      droptofloor();
229         // don't spawn if something is in the way
230         /*
231         // walk around a lot
232         if (walkmove(0,0))
233         {
234                 if (self.lefty > 0)
235                 {
236                         c = 100;
237                         self.lefty = self.lefty - 1;
238                         self.angles = '0 0 0';
239                         while(c > 0)
240                         {
241                                 c = c - 1;
242                                 if (!walkmove(self.angles_y, 16))
243                                         self.angles_y = random() * 360;
244                         }
245                         self.angles = '0 0 0';
246                         return;
247                 }
248         }
249         */
250         // don't spawn if something is in the way
251         if (!walkmove(0,0))
252         {
253                 self.lefty = 10;
254                 setorigin(self, self.dest);
255                 self.flags = self.flags - (self.flags & FL_ONGROUND);
256                 self.velocity = randomvec() * 700 + '0 0 1000';
257                 return;
258         }
259         newmis = findchain(classname, "player");
260         while (newmis)
261         {
262                 if (vlen(newmis.origin - self.origin) < 300)
263                         return;
264                 newmis = newmis.chain;
265         }
266
267         if (self.netname == "monster_fish")
268         {
269                 if (pointcontents(self.origin) != CONTENT_WATER)
270                 {
271                         remove(self);
272                         return;
273                 }
274         }
275
276         // spawn in
277         self.movetype = MOVETYPE_NONE;
278         self.solid = SOLID_NOT;
279         self.velocity = '0 0 0';
280         self.flags = 0;
281         self.model = "";
282         self.modelindex = 0;
283         setorigin(self, self.origin);
284         self.angles = '0 360 0' * random();
285         self.classname = self.netname;
286         self.netname = "";
287         self.cnt = 0;
288         self.think = SUB_Remove;
289         sfunc = self.th_run;
290         self.th_run = SUB_Null;
291         te_teleport(self.origin);
292         monsterspawn = TRUE;
293         sfunc();
294         monsterspawn = FALSE;
295 };
296
297 void(vector org, float c1, float c2, string cname, void() spawnfunc, vector m1, vector m2) spawnmonster =
298 {
299         local float c;
300         c = (c2 - c1) * random() + c1;
301         c = rint(c);
302         while (c > 0)
303         {
304                 c = c - 1;
305
306                 newmis = spawn();
307                 newmis.cnt = time + 10;
308                 if (cname == "monster_wizard")
309                         newmis.cnt = time + 2;
310                 newmis.lefty = 10;
311                 newmis.dest = org;
312                 newmis.classname = "spawningmonster";
313                 newmis.netname = cname;
314                 newmis.solid = SOLID_TRIGGER;
315                 newmis.movetype = MOVETYPE_TOSS;
316                 newmis.flags = FL_MONSTER; // make this count as a monster even though it hasn't spawned in yet
317                 newmis.velocity = randomvec() * 700 + '0 0 1000';
318                 newmis.th_run = spawnfunc;
319                 newmis.think = spawnmonster_think;
320                 newmis.nextthink = time + random() * 0.5 + 0.3;
321                 setorigin(newmis, org);
322                 setmodel(newmis, "progs/s_explod.spr");
323                 setsize(newmis, m1, m2);
324         }
325 };
326
327 void() monster_army;
328 void() monster_demon1;
329 void() monster_dog;
330 void() monster_enforcer;
331 void() monster_hell_knight;
332 void() monster_knight;
333 void() monster_ogre;
334 void() monster_shalrath;
335 void() monster_shambler;
336 void() monster_tarbaby;
337 void() monster_wizard;
338 void() monster_zombie;
339 void() monster_fish;
340 void() monster_hellfish;
341
342 void() spawnmonsters =
343 {
344         local float r;
345         local vector org;
346         local entity head, e;
347         head = findchain(classname, "info_player_deathmatch");
348         if (head == world)
349         {
350                 head = findchain(classname, "info_player_coop");
351                 if (head == world)
352                 {
353                         head = findchain(classname, "info_player_start");
354                         if (head == world)
355                                 return;
356                 }
357         }
358
359         // count the spawn points
360         r = 0;
361         e = head;
362         while (e)
363         {
364                 r = r + 1;
365                 e = e.chain;
366         }
367
368         // pick a random one
369         r = random() * r;
370         e = head;
371         while (r > 0)
372         {
373                 r = r - 1;
374                 org = e.origin;
375                 e = e.chain;
376         }
377
378         // pick a type of monster
379         if (cvar("registered"))
380         {
381                 r = floor(random() * 13);
382                 if (r > 12)
383                         r = 12;
384         }
385         else
386         {
387                 r = floor(random() * 8);
388                 if (r > 7)
389                         r = 7;
390         }
391              if (r ==  0) spawnmonster(org,  5, 10, "monster_army"       , monster_army       , '-16 -16 -24', '16 16 32');
392         else if (r ==  1) spawnmonster(org,  3,  6, "monster_demon1"     , monster_demon1     , '-32 -32 -24', '32 32 64');
393         else if (r ==  2) spawnmonster(org,  6, 12, "monster_dog"        , monster_dog        , '-16 -16 -24', '16 16 32');
394         else if (r ==  3) spawnmonster(org,  6, 12, "monster_knight"     , monster_knight     , '-16 -16 -24', '16 16 32');
395         else if (r ==  4) spawnmonster(org,  3,  6, "monster_ogre"       , monster_ogre       , '-32 -32 -24', '32 32 64');
396         else if (r ==  5) spawnmonster(org,  1,  1, "monster_shambler"   , monster_shambler   , '-32 -32 -24', '32 32 64');
397         else if (r ==  6) spawnmonster(org,  6, 10, "monster_wizard"     , monster_wizard     , '-16 -16 -24', '16 16 32');
398         else if (r ==  7) spawnmonster(org,  8, 16, "monster_zombie"     , monster_zombie     , '-16 -16 -24', '16 16 32');
399         else if (r ==  8) spawnmonster(org,  4,  8, "monster_enforcer"   , monster_enforcer   , '-16 -16 -24', '16 16 32');
400         else if (r ==  9) spawnmonster(org,  4,  8, "monster_hell_knight", monster_hell_knight, '-16 -16 -24', '16 16 32');
401         else if (r == 10) spawnmonster(org,  1,  3, "monster_shalrath"   , monster_shalrath   , '-32 -32 -24', '32 32 64');
402         else if (r == 11) spawnmonster(org, 10, 15, "monster_tarbaby"    , monster_tarbaby    , '-16 -16 -24', '16 16 32');
403         else if (r == 12) spawnmonster(org,  4,  8, "monster_fish"       , monster_fish       , '-16 -16 -24', '16 16 32');
404 };
405
406 float monstersprecached;
407 void() precachemonster_army;
408 void() precachemonster_demon1;
409 void() precachemonster_dog;
410 void() precachemonster_enforcer;
411 void() precachemonster_hell_knight;
412 void() precachemonster_knight;
413 void() precachemonster_ogre;
414 void() precachemonster_shalrath;
415 void() precachemonster_shambler;
416 void() precachemonster_tarbaby;
417 void() precachemonster_wizard;
418 void() precachemonster_zombie;
419 void() precachemonster_fish;
420
421 void() precachemonsters =
422 {
423         precachemonster_army();
424         precachemonster_demon1();
425         precachemonster_dog();
426         precachemonster_knight();
427         precachemonster_ogre();
428         precachemonster_shambler();
429         precachemonster_wizard();
430         precachemonster_zombie();
431         if (cvar("registered"))
432         {
433                 precachemonster_enforcer();
434                 precachemonster_hell_knight();
435                 precachemonster_shalrath();
436                 precachemonster_tarbaby();
437                 precachemonster_fish();
438         }
439         monstersprecached = TRUE;
440 };
441
442 float spawnedexitmonsters;
443 void() deathmatch7update =
444 {
445         local entity e;
446         local float f, monster_count, monsters;
447         if (skill >= 5)
448         if (!deathmatch)
449         {
450                 if (!spawnedexitmonsters)
451                 if (time >= 2)
452                 {
453                         spawnedexitmonsters = TRUE;
454                         e = find(world, classname, "target_changelevel");
455                         while (e)
456                         {
457                                 spawnmonster(e.origin + (e.mins + e.maxs) * 0.5, 8, 8, "monster_hellfish", monster_hellfish, '-16 -16 -24', '16 16 32');
458                                 e = find(e, classname, "target_changelevel");
459                         }
460                 }
461                 return;
462         }
463         if (time < spawnchecktime)
464                 return;
465         if (!monstersprecached)
466                 return;
467         spawnchecktime = time + 0.2;
468         monsters = 0;
469         if (deathmatch == 7)
470                 monsters = 50;
471         f = cvar("spawnmonsters");
472         if (f >= 1)
473                 monsters = f;
474         if (monsters < 1)
475                 return;
476         monster_count = 0;
477         e = findchainflags(flags, FL_MONSTER);
478         while (e)
479         {
480                 monster_count = monster_count + 1;
481                 e = e.chain;
482         }
483         if (monster_count >= monsters)
484                 return;
485         spawnmonsters();
486 }