]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/arena.qc
Merge branch 'master' into terencehill/arena_and_ca_fixes
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / arena.qc
1 float maxspawned;
2 float numspawned;
3 float arena_roundbased;
4 .float spawned;
5 .entity spawnqueue_next;
6 .entity spawnqueue_prev;
7 .float spawnqueue_in;
8 entity spawnqueue_first;
9 entity spawnqueue_last;
10 entity champion;
11 float warmup;
12 float ca_teams_ok;
13 .float caplayer;
14
15 void PutObserverInServer();
16 void PutClientInServer();
17
18 float next_round;
19 float redalive, bluealive, yellowalive, pinkalive;
20 float totalalive;
21 .float redalive_stat, bluealive_stat, yellowalive_stat, pinkalive_stat;
22 float red_players, blue_players, yellow_players, pink_players;
23 float total_players;
24
25 /**
26  * Resets the state of all clients, items, flags, runes, keys, weapons, waypoints, ... of the map.
27  * Sets the 'warmup' global variable.
28  */
29 void reset_map(float dorespawn)
30 {
31         entity oldself;
32         oldself = self;
33
34         if(g_arena && autocvar_g_arena_warmup)
35                 warmup = time + autocvar_g_arena_warmup;
36         else if(g_ca) {
37                 warmup = time + autocvar_g_ca_warmup;
38                 allowed_to_spawn = 1;
39         }
40         else if(g_freezetag)
41         {
42                 warmup = time + autocvar_g_freezetag_warmup;
43         }
44
45         lms_lowest_lives = 999;
46         lms_next_place = player_count;
47
48         race_ReadyRestart();
49
50         for(self = world; (self = nextent(self)); )
51         if(clienttype(self) == CLIENTTYPE_NOTACLIENT && self.items != IT_STRENGTH && self.items != IT_INVINCIBLE) // don't respawn strength or shield, that will only lead to them spawning very early each match
52         {
53                 if(self.reset)
54                 {
55                         self.reset();
56                         continue;
57                 }
58
59                 if(self.team_saved)
60                         self.team = self.team_saved;
61
62                 if(self.flags & FL_PROJECTILE) // remove any projectiles left
63                         remove(self);
64         }
65
66         // Waypoints and assault start come LAST
67         for(self = world; (self = nextent(self)); )
68         if(clienttype(self) == CLIENTTYPE_NOTACLIENT)
69         {
70                 if(self.reset2)
71                 {
72                         self.reset2();
73                         continue;
74                 }
75         }
76
77         // Moving the player reset code here since the player-reset depends
78         // on spawnpoint entities which have to be reset first --blub
79         if(dorespawn)
80         FOR_EACH_CLIENT(self) {
81                 if(self.flags & FL_CLIENT)                              // reset all players
82                 {
83                         if(g_arena)
84                         {
85                                 if(self.spawned)
86                                         PutClientInServer();
87                                 else
88                                         PutObserverInServer();
89                         }
90                         else if(g_ca && self.caplayer) {
91                                 self.classname = "player";
92                                 PutClientInServer();
93                         }
94                         else if(g_freezetag)
95                         {
96                                 if(self.classname == "player")
97                                         PutClientInServer();
98                         }
99                         else
100                         {
101                                 /*
102                                 only reset players if a restart countdown is active
103                                 this can either be due to cvar sv_ready_restart_after_countdown having set
104                                 restart_mapalreadyrestarted to 1 after the countdown ended or when
105                                 sv_ready_restart_after_countdown is not used and countdown is still running
106                                 */
107                                 if (restart_mapalreadyrestarted || (time < game_starttime))
108                                 {
109                                         //NEW: changed behaviour so that it prevents that previous spectators/observers suddenly spawn as players
110                                         if (self.classname == "player") {
111                                                 //PlayerScore_Clear(self);
112                                                 if(g_lms)
113                                                         PlayerScore_Add(self, SP_LMS_LIVES, LMS_NewPlayerLives());
114                                                 self.killcount = 0;
115                                                 //stop the player from moving so that he stands still once he gets respawned
116                                                 self.velocity = '0 0 0';
117                                                 self.avelocity = '0 0 0';
118                                                 self.movement = '0 0 0';
119                                                 PutClientInServer();
120                                         }
121                                 }
122                         }
123                 }
124         }
125
126         if(g_keyhunt)
127                 kh_Controller_SetThink_NoMsg(autocvar_g_balance_keyhunt_delay_round+(game_starttime - time), kh_StartRound);
128
129         if(g_arena)
130         if(champion && champion.classname == "player" && player_count > 1)
131                 UpdateFrags(champion, +1);
132
133         self = oldself;
134 }
135
136 void Spawnqueue_Insert(entity e)
137 {
138         if(e.spawnqueue_in)
139                 return;
140         dprint(strcat("Into queue: ", e.netname, "\n"));
141         e.spawnqueue_in = TRUE;
142         e.spawnqueue_prev = spawnqueue_last;
143         e.spawnqueue_next = world;
144         if(spawnqueue_last)
145                 spawnqueue_last.spawnqueue_next = e;
146         spawnqueue_last = e;
147         if(!spawnqueue_first)
148                 spawnqueue_first = e;
149 }
150
151 void Spawnqueue_Remove(entity e)
152 {
153         if(!e.spawnqueue_in)
154                 return;
155         dprint(strcat("Out of queue: ", e.netname, "\n"));
156         e.spawnqueue_in = FALSE;
157         if(e == spawnqueue_first)
158                 spawnqueue_first = e.spawnqueue_next;
159         if(e == spawnqueue_last)
160                 spawnqueue_last = e.spawnqueue_prev;
161         if(e.spawnqueue_prev)
162                 e.spawnqueue_prev.spawnqueue_next = e.spawnqueue_next;
163         if(e.spawnqueue_next)
164                 e.spawnqueue_next.spawnqueue_prev = e.spawnqueue_prev;
165         e.spawnqueue_next = world;
166         e.spawnqueue_prev = world;
167 }
168
169 void Spawnqueue_Unmark(entity e)
170 {
171         if(!e.spawned)
172                 return;
173         e.spawned = FALSE;
174         numspawned = numspawned - 1;
175 }
176
177 void Spawnqueue_Mark(entity e)
178 {
179         if(e.spawned)
180                 return;
181         e.spawned = TRUE;
182         numspawned = numspawned + 1;
183 }
184
185 /**
186  * If roundbased arena game mode is active, it centerprints the texts for the
187  * player when player is waiting for the countdown to finish.
188  * Blocks the players movement while countdown is active.
189  * Unblocks the player once the countdown is over.
190  *
191  * Called in StartFrame()
192  */
193 float roundStartTime_prev; // prevent networkspam
194 void Arena_Warmup()
195 {
196         float f;
197         entity e;
198
199         if(gameover)
200         {
201                 if(champion && g_arena)
202                 {
203                         FOR_EACH_CLIENT(e)
204                                 centerprint(e, strcat("The Champion is ", champion.netname));
205                         champion = world;
206                 }
207                 return;
208         }
209         if((!g_arena && !g_ca && !g_freezetag) || (g_arena && !arena_roundbased) || (time < game_starttime))
210                 return;
211
212         f = ceil(warmup - time);
213
214         allowed_to_spawn = 0;
215
216         if(inWarmupStage)
217                 allowed_to_spawn = 1;
218         if(g_ca && !ca_teams_ok)
219                 allowed_to_spawn = 1;
220
221         if(time < warmup && !inWarmupStage)
222         {
223                 if (g_ca)
224                         allowed_to_spawn = 1;
225                 if(champion && g_arena)
226                 {
227                         FOR_EACH_PLAYER(e)
228                                 centerprint(e, strcat("The Champion is ", champion.netname));
229                 }
230
231                 if(f != roundStartTime_prev) {
232                         roundStartTime_prev = f;
233                         if(f == 5)
234                                 Announce("prepareforbattle");
235                         else if(f == 3)
236                                 Announce("3");
237                         else if(f == 2)
238                                 Announce("2");
239                         else if(f == 1)
240                                 Announce("1");
241
242                         FOR_EACH_PLAYER(e)
243                                 Send_CSQC_Centerprint_Generic(e, CPID_ROUND_STARTING, "Round will start in %d", 1, f);
244                 }
245
246                 if (g_arena) {
247                         FOR_EACH_CLIENT(e)
248                         {
249                                 if(e.spawned && e.classname == "player")
250                                         e.movetype = MOVETYPE_NONE;
251                                 e.velocity = '0 0 0';
252                                 e.avelocity = '0 0 0';
253                                 e.movement = '0 0 0';
254                         }
255                 }
256         }
257         else if(f > -1 && f != roundStartTime_prev)
258         {
259                 roundStartTime_prev = f;
260                 Announce("begin");
261                 FOR_EACH_PLAYER(e)
262                         Send_CSQC_Centerprint_Generic(e, CPID_ROUND_STARTING, "^1Begin!", 1, 0);
263
264                 if(g_ca)
265                         ca_teams_ok = (red_players && blue_players); // teams are ok if there's at least 1 player in each team
266
267                 if(g_arena) {
268                         FOR_EACH_PLAYER(e)
269                         {
270                                 if(e.health > 0 && e.movetype == MOVETYPE_NONE)
271                                         e.movetype = MOVETYPE_WALK;
272                         }
273                 }
274         }
275
276         // clear champion to avoid centerprinting again the champion msg
277         if (champion)
278                 champion = world;
279 }
280
281 void count_players()
282 {
283         // count amount of players in each team
284         total_players = red_players = blue_players = yellow_players = pink_players = 0;
285         FOR_EACH_PLAYER(self) {
286                 if (self.team == COLOR_TEAM1)
287                 {
288                         red_players += 1;
289                         total_players += 1;
290                 }
291                 else if (self.team == COLOR_TEAM2)
292                 {
293                         blue_players += 1;
294                         total_players += 1;
295                 }
296                 else if (self.team == COLOR_TEAM3)
297                 {
298                         yellow_players += 1;
299                         total_players += 1;
300                 }
301                 else if (self.team == COLOR_TEAM4)
302                 {
303                         pink_players += 1;
304                         total_players += 1;
305                 }
306         }
307 }
308
309 void count_alive_players()
310 {
311         totalalive = redalive = bluealive = yellowalive = pinkalive = 0;
312         if(g_ca)
313         {
314                 FOR_EACH_PLAYER(self) {
315                         if (self.team == COLOR_TEAM1 && self.health >= 1)
316                         {
317                                 redalive += 1;
318                                 totalalive += 1;
319                         }
320                         else if (self.team == COLOR_TEAM2 && self.health >= 1)
321                         {
322                                 bluealive += 1;
323                                 totalalive += 1;
324                         }
325                 }
326                 FOR_EACH_REALCLIENT(self) {
327                         self.redalive_stat = redalive;
328                         self.bluealive_stat = bluealive;
329                 }
330         }
331         else if(g_freezetag)
332         {
333                 // count amount of alive players in each team
334                 FOR_EACH_PLAYER(self) {
335                         if (self.team == COLOR_TEAM1 && self.freezetag_frozen == 0 && self.health >= 1)
336                         {
337                                 redalive += 1;
338                                 totalalive += 1;
339                         }
340                         else if (self.team == COLOR_TEAM2 && self.freezetag_frozen == 0 && self.health >= 1)
341                         {
342                                 bluealive += 1;
343                                 totalalive += 1;
344                         }
345                         else if (self.team == COLOR_TEAM3 && self.freezetag_frozen == 0 && self.health >= 1)
346                         {
347                                 yellowalive += 1;
348                                 totalalive += 1;
349                         }
350                         else if (self.team == COLOR_TEAM4 && self.freezetag_frozen == 0 && self.health >= 1)
351                         {
352                                 pinkalive += 1;
353                                 totalalive += 1;
354                         }
355                 }
356                 FOR_EACH_REALCLIENT(self) {
357                         self.redalive_stat = redalive;
358                         self.bluealive_stat = bluealive;
359                         self.yellowalive_stat = yellowalive;
360                         self.pinkalive_stat = pinkalive;
361                 }
362         }
363
364 }
365
366 /**
367  * This function finds out whether an arena round is over 1 player is left.
368  * It determines the last player who's still alive and saves it's entity reference
369  * in the global variable 'champion'. Then the new enemy/enemies are put into the server.
370  *
371  * Gets called in StartFrame()
372  */
373 float warntime;
374 void Spawnqueue_Check()
375 {
376         if(time < warmup + 1 || inWarmupStage || intermission_running)
377                 return;
378
379         if(g_ca) {
380                 if(!ca_teams_ok && (red_players && blue_players)) {
381                         reset_map(TRUE);
382                 }
383                 else if(!ca_teams_ok) {
384                         if (time > warntime)
385                         {
386                                 FOR_EACH_PLAYER(self)
387                                         Send_CSQC_Centerprint_Generic(self, CPID_ROUND_STARTING, "^1Need at least 1 player in each team to play CA", 2, 0);
388                                 warntime = time + 1;
389                         }
390                         return;
391                 }
392                 else if(!next_round) {
393                         if(!(redalive && bluealive)) {
394                                 // every player of (at least) one team is dead, round ends here
395                                 if(redalive) {
396                                         play2all("ctf/red_capture.wav");
397                                         FOR_EACH_CLIENT(self) centerprint(self, "^1RED ^7team wins the round");
398                                         TeamScore_AddToTeam(COLOR_TEAM1, ST_SCORE, +1);
399                                 }
400                                 else if(bluealive) {
401                                         play2all("ctf/blue_capture.wav");
402                                         FOR_EACH_CLIENT(self) centerprint(self, "^4BLUE ^7team wins the round");
403                                         TeamScore_AddToTeam(COLOR_TEAM2, ST_SCORE, +1);
404                                 }
405                                 else
406                                         FOR_EACH_CLIENT(self) centerprint(self, "^7Round tied");
407                                 next_round = -1;
408                         }
409                         else if(time - warmup > autocvar_g_ca_round_timelimit) {
410                                 FOR_EACH_CLIENT(self) centerprint(self, "^7Round tied");
411                                 next_round = time + 5;
412                         }
413                 }
414                 else if(next_round == -1) {
415                         // wait for killed players to be put as spectators
416                         if(!(red_players && blue_players))
417                                 next_round = time + 5;
418                 }
419                 else if((next_round > 0 && next_round < time))
420                 {
421                         next_round = 0;
422                         reset_map(TRUE);
423                 }
424         } else if(g_freezetag) {
425                 if((next_round && next_round < time))
426                 {
427                         next_round = 0;
428                         reset_map(TRUE);
429                 }
430         } else { // arena
431                 //extend next_round if it isn't set yet and only 1 player is spawned
432                 if(!next_round)
433                 if(numspawned < 2)
434                         next_round = time + 3;
435
436                 if(!arena_roundbased || (next_round && next_round < time && player_count > 1))
437                 {
438                         next_round = 0;
439
440                         if(arena_roundbased)
441                         {
442                                 champion = find(world, classname, "player");
443                                 while(champion && champion.deadflag)
444                                         champion = find(champion, classname, "player");
445                                 reset_map(TRUE);
446                         }
447
448                         while(numspawned < maxspawned && spawnqueue_first)
449                         {
450                                 self = spawnqueue_first;
451
452                                 bprint ("^4", self.netname, "^4 is the next challenger\n");
453
454                                 Spawnqueue_Remove(self);
455                                 Spawnqueue_Mark(self);
456
457                                 self.classname = "player";
458                                 PutClientInServer();
459                         }
460                 }
461         }
462 }