]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/mutators/mutator/gamemode_survival.qc
Merged Lyberta/TeamplayFixes_
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / mutator / gamemode_survival.qc
1 #include "gamemode_survival.qh"
2
3 #include <common/mutators/mutator/playertemplates/sv_playertemplates.qh>
4 #include <common/mutators/mutator/overkill/hmg.qh>
5 #include <common/mutators/mutator/overkill/rpc.qh>
6
7 //============================ Constants ======================================
8
9 const int SURVIVAL_TEAM_1_BIT = BIT(0); ///< Bitmask of the first team.
10 const int SURVIVAL_TEAM_2_BIT = BIT(1); ///< Bitmask of the second team.
11
12 /// \brief Used when bitfield team count is requested.
13 const int SURVIVAL_TEAM_BITS = 3;
14
15 enum
16 {
17         SURVIVAL_TYPE_COOP, ///< All humans are on the defender team.
18         SURVIVAL_TYPE_VERSUS ///< Humans take turns between attackers and defenders.
19 };
20
21 const string SURVIVAL_TYPE_COOP_VALUE = "coop"; ///< Cvar value for coop.
22 const string SURVIVAL_TYPE_VERSUS_VALUE = "versus"; ///< Cvar value for versus.
23
24 enum
25 {
26         /// \brief First round where there is timelimit set by the server.
27         SURVIVAL_ROUND_FIRST,
28         /// \brief Second round where defender team tries to survive for the first
29         /// round's time.
30         SURVIVAL_ROUND_SECOND
31 };
32
33 enum
34 {
35         /// \brief Player is spectating and has no intention of playing.
36         SURVIVAL_STATE_NOT_PLAYING,
37         /// \brief Player is playing the game.
38         SURVIVAL_STATE_PLAYING = 1
39 };
40
41 enum
42 {
43         SURVIVAL_ROLE_NONE, ///< Player is not playing.
44         SURVIVAL_ROLE_PLAYER, ///< Player is an attacker or defender.
45         SURVIVAL_ROLE_CANNON_FODDER ///< Player is a cannon fodder.
46 };
47
48 SOUND(SURV_3_FRAGS_LEFT, "announcer/default/3fragsleft");
49 SOUND(SURV_2_FRAGS_LEFT, "announcer/default/2fragsleft");
50 SOUND(SURV_1_FRAG_LEFT, "announcer/default/1fragleft");
51
52 SOUND(SURV_RED_SCORES, "ctf/red_capture");
53 SOUND(SURV_BLUE_SCORES, "ctf/blue_capture");
54
55 //======================= Global variables ====================================
56
57 float autocvar_g_surv_warmup; ///< Warmup time in seconds.
58 float autocvar_g_surv_round_timelimit; ///< First round time limit in seconds.
59
60 int autocvar_g_surv_point_limit; ///< Maximum number of points.
61 int autocvar_g_surv_point_leadlimit; ///< Maximum lead of a single team.
62
63 /// \brief How much players are allowed in teams (excluding cannon fodder).
64 int autocvar_g_surv_team_size;
65 /// \brief If set, defenders will not be shown on the radar.
66 int autocvar_g_surv_stealth;
67 /// \brief Whether to allow spectating enemy players while dead.
68 bool autocvar_g_surv_spectate_enemies;
69
70 /// \brief Whether to force overkill player models for attackers.
71 int autocvar_g_surv_attacker_force_overkill_models;
72 /// \brief Whether to force overkill player models for defenders.
73 int autocvar_g_surv_defender_force_overkill_models;
74 /// \brief Whether to force overkill player models for cannon fodder.
75 int autocvar_g_surv_cannon_fodder_force_overkill_models;
76
77 /// \brief How much score attackers gain per 1 point of damage.
78 float autocvar_g_surv_attacker_damage_score;
79
80 /// \brief How much score attackers get for fragging defenders.
81 float autocvar_g_surv_attacker_frag_score;
82
83 /// \brief How much health do defenders get when they frag an attacker.
84 int autocvar_g_surv_defender_attacker_frag_health;
85 /// \brief How much armor do defenders get when they frag an attacker.
86 int autocvar_g_surv_defender_attacker_frag_armor;
87 /// \brief How many shells do defenders get when they frag an attacker.
88 int autocvar_g_surv_defender_attacker_frag_shells;
89 /// \brief How many bullets do defenders get when they frag an attacker.
90 int autocvar_g_surv_defender_attacker_frag_bullets;
91 /// \brief How many rockets do defenders get when they frag an attacker.
92 int autocvar_g_surv_defender_attacker_frag_rockets;
93 /// \brief How many cells do defenders get when they frag an attacker.
94 int autocvar_g_surv_defender_attacker_frag_cells;
95 /// \brief How much health do defenders get when they frag cannon fodder.
96 int autocvar_g_surv_defender_cannon_fodder_frag_health;
97 /// \brief How much armor do defenders get when they frag cannon fodder.
98 int autocvar_g_surv_defender_cannon_fodder_frag_armor;
99 /// \brief How many shells do defenders get when they frag cannon fodder.
100 int autocvar_g_surv_defender_cannon_fodder_frag_shells;
101 /// \brief How many bullets do defenders get when they frag cannon fodder.
102 int autocvar_g_surv_defender_cannon_fodder_frag_bullets;
103 /// \brief How many rockets do defenders get when they frag cannon fodder.
104 int autocvar_g_surv_defender_cannon_fodder_frag_rockets;
105 /// \brief How many cells do defenders get when they frag cannon fodder.
106 int autocvar_g_surv_defender_cannon_fodder_frag_cells;
107
108 /// \brief Whether defenders drop weapons after death.
109 int autocvar_g_surv_defender_drop_weapons;
110
111 /// \brief A stat that is used to track the time left in the round.
112 .float surv_round_time_stat = _STAT(SURV_ROUND_TIME);
113 /// \brief A stat that is used to track defender team.
114 .int surv_defender_team_stat = _STAT(SURV_DEFENDER_TEAM);
115 /// \brief A stat that is used to track number of defenders alive.
116 .int surv_defenders_alive_stat = _STAT(SURV_DEFENDERS_ALIVE);
117 /// \brief A stat that is used to track the total health of defenders.
118 .float surv_defender_health_stat = _STAT(SURV_DEFENDER_HEALTH);
119
120 /// \brief Holds the state of the player. See SURVIVAL_STATE constants.
121 .int surv_state;
122 /// \brief Holds the role of the player. See SURVIVAL_ROLE constants.
123 .int surv_role;
124 .string surv_savedplayermodel; ///< Initial player model.
125 /// \brief Player state used during replacement of bot player with real player.
126 .entity surv_savedplayerstate;
127 .string surv_playermodel; ///< Player model forced by the game.
128
129 .entity surv_attack_sprite; ///< Holds the sprite telling attackers to attack.
130
131 int surv_type; ///< Holds the type of survival. See SURVIVAL_TYPE constants.
132 bool surv_warmup; ///< Holds whether warmup is active.
133 /// \brief Holds the type of the current round. See SURVIVAL_ROUND constants.
134 int surv_roundtype;
135 bool surv_isroundactive; ///< Holds whether the round is active.
136 float surv_roundstarttime; ///< Holds the time of the round start.
137 /// \brief Holds the time needed to survive in the second round.
138 float surv_timetobeat;
139
140 int surv_attackerteam; ///< Holds the attacker team.
141 int surv_defenderteam; ///< Holds the defender team.
142
143 int surv_attackerteambit; ///< Hols the attacker team bitmask.
144 int surv_defenderteambit; ///< Holds the defender team bitmask.
145
146 int surv_numattackers; ///< Holds the number of players in attacker team.
147 int surv_numdefenders; ///< Holds the number of players in defender team.
148
149 /// \brief Holds the number of humans in attacker team.
150 int surv_numattackerhumans;
151 /// \brief Holds the number of humans in defender team.
152 int surv_numdefenderhumans;
153
154 /// \brief Holds the number of attacker players that are alive.
155 int surv_numattackersalive;
156 /// \brief Holds the number of defender players that are alive.
157 int surv_numdefendersalive;
158
159 bool surv_autobalance; ///< Holds whether autobalance is active.
160 bool surv_announcefrags; ///< Holds whether remaining frags must be announced.
161 bool surv_allowed_to_spawn; ///< Holds whether players are allowed to spawn.
162
163 //====================== Forward declarations =================================
164
165 /// \brief Determines whether the round can start.
166 /// \return True if the round can start, false otherwise.
167 bool Surv_CanRoundStart();
168
169 /// \brief Determines whether the round can end.
170 /// \return True if the round can end, false otherwise.
171 bool Surv_CanRoundEnd();
172
173 /// \brief Called when the round starts.
174 /// \return No return.
175 void Surv_RoundStart();
176
177 /// \brief Returns whether player has been eliminated.
178 /// \param[in] player Player to check.
179 /// \return True if player is eliminated, false otherwise.
180 bool Surv_IsEliminated(entity player);
181
182 /// \brief Updates stats of team count on HUD.
183 /// \return No return.
184 void Surv_UpdateTeamStats();
185
186 /// \brief Updates stats of alive players on HUD.
187 /// \return No return.
188 void Surv_UpdateAliveStats();
189
190 /// \brief Updates defender health on the HUD.
191 /// \return No return.
192 void Surv_UpdateDefenderHealthStat();
193
194 //========================= Free functions ====================================
195
196 void Surv_Initialize()
197 {
198         switch (cvar_string("g_surv_type"))
199         {
200                 case SURVIVAL_TYPE_COOP_VALUE:
201                 {
202                         surv_type = SURVIVAL_TYPE_COOP;
203                         break;
204                 }
205                 case SURVIVAL_TYPE_VERSUS_VALUE:
206                 {
207                         surv_type = SURVIVAL_TYPE_VERSUS;
208                         break;
209                 }
210                 default:
211                 {
212                         error("Invalid survival type.");
213                 }
214         }
215         surv_roundtype = SURVIVAL_ROUND_FIRST;
216         surv_isroundactive = false;
217         surv_roundstarttime = time;
218         surv_timetobeat = autocvar_g_surv_round_timelimit;
219         if (random() < 0.5)
220         {
221                 surv_attackerteam = NUM_TEAM_1;
222                 surv_defenderteam = NUM_TEAM_2;
223                 surv_attackerteambit = SURVIVAL_TEAM_1_BIT;
224                 surv_defenderteambit = SURVIVAL_TEAM_2_BIT;
225         }
226         else
227         {
228                 surv_attackerteam = NUM_TEAM_2;
229                 surv_defenderteam = NUM_TEAM_1;
230                 surv_attackerteambit = SURVIVAL_TEAM_2_BIT;
231                 surv_defenderteambit = SURVIVAL_TEAM_1_BIT;
232         }
233         surv_numattackers = 0;
234         surv_numdefenders = 0;
235         surv_numattackerhumans = 0;
236         surv_numdefenderhumans = 0;
237         surv_numattackersalive = 0;
238         surv_numdefendersalive = 0;
239         surv_autobalance = true;
240         surv_announcefrags = true;
241         surv_allowed_to_spawn = true;
242         precache_all_playermodels("models/ok_player/*.dpm");
243         ScoreRules_basics(SURVIVAL_TEAM_BITS, SFL_SORT_PRIO_PRIMARY, 0, true);
244         ScoreInfo_SetLabel_TeamScore(1, "rounds", SFL_SORT_PRIO_PRIMARY);
245         ScoreRules_basics_end();
246         round_handler_Spawn(Surv_CanRoundStart, Surv_CanRoundEnd, Surv_RoundStart);
247         round_handler_Init(5, autocvar_g_surv_warmup, surv_timetobeat);
248         EliminatedPlayers_Init(Surv_IsEliminated);
249         ActivateTeamplay();
250         SetLimits(autocvar_g_surv_point_limit, autocvar_g_surv_point_leadlimit,
251                 autocvar_timelimit_override, -1);
252 }
253
254 /// \brief Returns the name of the template of the given player.
255 /// \param[in] player Player to inspect.
256 /// \return Name of the template of the given player.
257 string Surv_GetPlayerTemplate(entity player)
258 {
259         switch (player.team)
260         {
261                 case surv_attackerteam:
262                 {
263                         switch (player.surv_role)
264                         {
265                                 case SURVIVAL_ROLE_NONE:
266                                 case SURVIVAL_ROLE_PLAYER:
267                                 {
268                                         return cvar_string("g_surv_attacker_template");
269                                 }
270                                 case SURVIVAL_ROLE_CANNON_FODDER:
271                                 {
272                                         return cvar_string("g_surv_cannon_fodder_template");
273                                 }
274                         }
275                 }
276                 case surv_defenderteam:
277                 {
278                         return cvar_string("g_surv_defender_template");
279                 }
280         }
281         return "default";
282 }
283
284 /// \brief Saves the player state. Used to seamlessly swap bots with humans.
285 /// \param[in] player Player to save the state of.
286 /// \return Entity containing the player state.
287 entity Surv_SavePlayerState(entity player)
288 {
289         entity state = spawn();
290         state.origin = player.origin;
291         state.velocity = player.velocity;
292         state.angles = player.angles;
293         state.health = player.health;
294         state.armorvalue = player.armorvalue;
295         state.ammo_shells = player.ammo_shells;
296         state.ammo_nails = player.ammo_nails;
297         state.ammo_rockets = player.ammo_rockets;
298         state.ammo_cells = player.ammo_cells;
299         state.weapons = player.weapons;
300         state.items = player.items;
301         state.superweapons_finished = player.superweapons_finished;
302         return state;
303 }
304
305 /// \brief Restores a saved player state.
306 /// \param[in] player Player to restore the state of.
307 /// \param[in] st State to restore.
308 /// \return No return.
309 void Surv_RestorePlayerState(entity player, entity st)
310 {
311         player.origin = st.origin;
312         player.velocity = st.velocity;
313         player.angles = st.angles;
314         player.health = st.health;
315         player.armorvalue = st.armorvalue;
316         player.ammo_shells = st.ammo_shells;
317         player.ammo_nails = st.ammo_nails;
318         player.ammo_rockets = st.ammo_rockets;
319         player.ammo_cells = st.ammo_cells;
320         player.weapons = st.weapons;
321         player.items = st.items;
322         player.superweapons_finished = st.superweapons_finished;
323 }
324
325 /// \brief Returns the attacker with the lowest score.
326 /// \param[in] bot Whether to search only for bots.
327 /// \return Attacker with the lowest score or NULL if not found.
328 entity Surv_FindLowestAttacker(bool bot)
329 {
330         entity player = NULL;
331         float score = FLOAT_MAX;
332         FOREACH_CLIENT(bot ? IS_BOT_CLIENT(it) : true,
333         {
334                 if ((it.team == surv_attackerteam) && (it.surv_role ==
335                         SURVIVAL_ROLE_PLAYER))
336                 {
337                         float tempscore = PlayerScore_Get(it, SP_SCORE);
338                         if (tempscore < score)
339                         {
340                                 player = it;
341                                 score = tempscore;
342                         }
343                 }
344         });
345         return player;
346 }
347
348 /// \brief Returns the defender with the lowest score.
349 /// \param[in] bot Whether to search only for bots.
350 /// \param[in] alive Whether to search only for alive players.
351 /// \return Defender with the lowest score or NULL if not found.
352 entity Surv_FindLowestDefender(bool bot, bool alive)
353 {
354         entity player = NULL;
355         float score = FLOAT_MAX;
356         FOREACH_CLIENT(bot ? IS_BOT_CLIENT(it) : true,
357         {
358                 if ((it.team == surv_defenderteam) && (alive ? !IS_DEAD(it) : true))
359                 {
360                         float tempscore = PlayerScore_Get(it, SP_SCORE);
361                         if (tempscore < score)
362                         {
363                                 player = it;
364                                 score = tempscore;
365                         }
366                 }
367         });
368         return player;
369 }
370
371 /// \brief Returns the cannon fodder.
372 /// \return Cannon fodder or NULL if not found.
373 entity Surv_FindCannonFodder()
374 {
375         FOREACH_CLIENT(IS_BOT_CLIENT(it),
376         {
377                 if (it.surv_role == SURVIVAL_ROLE_CANNON_FODDER)
378                 {
379                         return it;
380                 }
381         });
382         return NULL;
383 }
384
385 /// \brief Changes the number of players in a team.
386 /// \param[in] teamnum Team to adjust.
387 /// \param[in] delta Amount to adjust by.
388 /// \return No return.
389 void Surv_ChangeNumberOfPlayers(int teamnum, int delta)
390 {
391         switch (teamnum)
392         {
393                 case surv_attackerteam:
394                 {
395                         surv_numattackers += delta;
396                         LOG_TRACE("Number of attackers = ", ftos(surv_numattackers),
397                                 " was = ", ftos(surv_numattackers - delta));
398                         Surv_UpdateTeamStats();
399                         return;
400                 }
401                 case surv_defenderteam:
402                 {
403                         surv_numdefenders += delta;
404                         LOG_TRACE("Number of defenders = ", ftos(surv_numdefenders),
405                                 " was = ", ftos(surv_numdefenders - delta));
406                         Surv_UpdateTeamStats();
407                         return;
408                 }
409         }
410 }
411
412 /// \brief Changes the number of alive players in a team.
413 /// \param[in] teamnum Team to adjust.
414 /// \param[in] delta Amount to adjust by.
415 /// \return No return.
416 void Surv_ChangeNumberOfAlivePlayers(int teamnum, int delta)
417 {
418         switch (teamnum)
419         {
420                 case surv_attackerteam:
421                 {
422                         surv_numattackersalive += delta;
423                         LOG_TRACE("Number of alive attackers = ", ftos(
424                                 surv_numattackersalive), " was = ", ftos(surv_numattackersalive
425                                 - delta));
426                         break;
427                 }
428                 case surv_defenderteam:
429                 {
430                         surv_numdefendersalive += delta;
431                         LOG_TRACE("Number of alive defenders = ", ftos(
432                                 surv_numdefendersalive), " was = ", ftos(surv_numdefendersalive
433                                 - delta));
434                         break;
435                 }
436         }
437         Surv_UpdateAliveStats();
438         eliminatedPlayers.SendFlags |= 1;
439 }
440
441 /// \brief Sets the player role.
442 /// \param[in,out] player Player to adjust.
443 /// \param[in] role Role to set.
444 /// \return No return.
445 void Surv_SetPlayerRole(entity player, int role)
446 {
447         if (player.surv_role == role)
448         {
449                 return;
450         }
451         player.surv_role = role;
452         switch (role)
453         {
454                 case SURVIVAL_ROLE_NONE:
455                 {
456                         LOG_TRACE(player.netname, " now has no role.");
457                         break;
458                 }
459                 case SURVIVAL_ROLE_PLAYER:
460                 {
461                         LOG_TRACE(player.netname, " is now a player.");
462                         break;
463                 }
464                 case SURVIVAL_ROLE_CANNON_FODDER:
465                 {
466                         LOG_TRACE(player.netname, " is now a cannon fodder.");
467                         break;
468                 }
469         }
470 }
471
472 /// \brief Adds player to team. Handles bookkeeping information.
473 /// \param[in] player Player to add.
474 /// \param[in] teamnum Team to add to.
475 /// \return True on success, false otherwise.
476 bool Surv_AddPlayerToTeam(entity player, int teamnum)
477 {
478         LOG_TRACE("Survival: AddPlayerToTeam, player: ", player.netname);
479         switch (teamnum)
480         {
481                 case surv_attackerteam:
482                 {
483                         LOG_TRACE("Attacker team");
484                         if (player.surv_role == SURVIVAL_ROLE_CANNON_FODDER)
485                         {
486                                 LOG_TRACE("Cannon fodder is switching team");
487                                 return true;
488                         }
489                         if (IS_BOT_CLIENT(player))
490                         {
491                                 LOG_TRACE("Client is bot");
492                                 LOG_TRACE("Attackers = ", ftos(surv_numattackers));
493                                 if (surv_numattackers < autocvar_g_surv_team_size)
494                                 {
495                                         Surv_SetPlayerRole(player, SURVIVAL_ROLE_PLAYER);
496                                         Surv_ChangeNumberOfPlayers(teamnum, +1);
497                                         return true;
498                                 }
499                                 Surv_SetPlayerRole(player, SURVIVAL_ROLE_CANNON_FODDER);
500                                 return true;
501                         }
502                         LOG_TRACE("Client is not a bot");
503                         LOG_TRACE("Attackers = ", ftos(surv_numattackers));
504                         if (surv_numattackers >= autocvar_g_surv_team_size)
505                         {
506                                 LOG_TRACE("Removing bot");
507                                 // Remove bot to make space for human.
508                                 entity bot = Surv_FindLowestAttacker(true);
509                                 if (bot == NULL)
510                                 {
511                                         LOG_TRACE("No valid bot to remove");
512                                         // No space in team, denying team change.
513                                         TRANSMUTE(Spectator, player);
514                                         return false;
515                                 }
516                                 LOG_TRACE("Changing ", bot.netname,
517                                         " from attacker to cannon fodder.");
518                                 Surv_SetPlayerRole(bot, SURVIVAL_ROLE_CANNON_FODDER);
519                                 if (!IS_DEAD(bot))
520                                 {
521                                         Surv_ChangeNumberOfAlivePlayers(teamnum, -1);
522                                 }
523                                 Surv_ChangeNumberOfPlayers(teamnum, -1);
524                                 LOG_TRACE("Removed bot");
525                         }
526                         Surv_SetPlayerRole(player, SURVIVAL_ROLE_PLAYER);
527                         Surv_ChangeNumberOfPlayers(teamnum, +1);
528                         ++surv_numattackerhumans;                       
529                         LOG_TRACE("Human attackers = ", ftos(surv_numattackerhumans));
530                         if ((surv_autobalance == false) || (surv_numattackers -
531                                 surv_numdefenders) < 2)
532                         {
533                                 return true;
534                         }
535                         entity lowestplayer = Surv_FindLowestAttacker(true);
536                         if (lowestplayer != NULL)
537                         {
538                                 bool savedautobalance = surv_autobalance;
539                                 surv_autobalance = false;
540                                 SetPlayerTeamSimple(lowestplayer, surv_defenderteam);
541                                 surv_autobalance = savedautobalance;
542                                 return true;
543                         }
544                         lowestplayer = Surv_FindLowestAttacker(false);
545                         if (lowestplayer != NULL)
546                         {
547                                 bool savedautobalance = surv_autobalance;
548                                 surv_autobalance = false;
549                                 SetPlayerTeamSimple(lowestplayer, surv_defenderteam);
550                                 surv_autobalance = savedautobalance;
551                         }
552                         return true;
553                 }
554                 case surv_defenderteam:
555                 {
556                         LOG_TRACE("Defender team");
557                         if (IS_BOT_CLIENT(player))
558                         {
559                                 LOG_TRACE("Client is bot");
560                                 LOG_TRACE("Defenders = ", ftos(surv_numdefenders));
561                                 if (surv_numdefenders < autocvar_g_surv_team_size)
562                                 {
563                                         Surv_SetPlayerRole(player, SURVIVAL_ROLE_PLAYER);
564                                         Surv_ChangeNumberOfPlayers(teamnum, +1);
565                                         return true;
566                                 }
567                                 LOG_TRACE("No space for defender, switching to attacker");
568                                 SetPlayerTeamSimple(player, surv_attackerteam);
569                                 return false;
570                         }
571                         LOG_TRACE("Client is not a bot");
572                         LOG_TRACE("Defenders = ", ftos(surv_numdefenders));
573                         if (surv_numdefenders >= autocvar_g_surv_team_size)
574                         {
575                                 LOG_TRACE("Removing bot");
576                                 // Remove bot to make space for human.
577                                 entity bot = Surv_FindLowestDefender(true, true);
578                                 if (bot == NULL)
579                                 {
580                                         bot = Surv_FindLowestDefender(true, false);
581                                 }
582                                 if (bot == NULL)
583                                 {
584                                         LOG_TRACE("No valid bot to remove");
585                                         // No space in team, denying team change.
586                                         TRANSMUTE(Spectator, player);
587                                         return false;
588                                 }
589                                 LOG_TRACE("Changing ", bot.netname,
590                                         " from defender to cannon fodder.");
591                                 if (!IS_DEAD(bot))
592                                 {
593                                         player.surv_savedplayerstate = Surv_SavePlayerState(bot);
594                                 }
595                                 bool savedautobalance = surv_autobalance;
596                                 surv_autobalance = false;
597                                 surv_announcefrags = false;
598                                 SetPlayerTeamSimple(bot, surv_attackerteam);
599                                 surv_autobalance = savedautobalance;
600                                 surv_announcefrags = true;
601                                 LOG_TRACE("Removed bot");
602                         }
603                         Surv_SetPlayerRole(player, SURVIVAL_ROLE_PLAYER);
604                         Surv_ChangeNumberOfPlayers(teamnum, +1);
605                         ++surv_numdefenderhumans;
606                         LOG_TRACE("Human defenders = ", ftos(surv_numdefenderhumans));
607                         if ((surv_autobalance == false) || (surv_numdefenders -
608                                 surv_numattackers) < 2)
609                         {
610                                 return true;
611                         }
612                         entity lowestplayer = Surv_FindLowestDefender(true, false);
613                         if (lowestplayer != NULL)
614                         {
615                                 bool savedautobalance = surv_autobalance;
616                                 surv_autobalance = false;
617                                 SetPlayerTeamSimple(lowestplayer, surv_attackerteam);
618                                 surv_autobalance = savedautobalance;
619                                 return true;
620                         }
621                         lowestplayer = Surv_FindLowestDefender(false, false);
622                         if (lowestplayer != NULL)
623                         {
624                                 bool savedautobalance = surv_autobalance;
625                                 surv_autobalance = false;
626                                 SetPlayerTeamSimple(lowestplayer, surv_attackerteam);
627                                 surv_autobalance = savedautobalance;
628                         }
629                         return true;
630                 }
631                 case -1:
632                 {
633                         LOG_TRACE("Spectator team");
634                         player.surv_role = SURVIVAL_ROLE_NONE;                  
635                         return false;
636                 }
637         }
638         LOG_TRACE("Invalid team");
639         player.surv_role = SURVIVAL_ROLE_NONE;
640         return false;
641 }
642
643 /// \brief Removes player from team. Handles bookkeeping information.
644 /// \param[in] player Player to remove.
645 /// \param[in] Team to remove from.
646 /// \return No return.
647 void Surv_RemovePlayerFromTeam(entity player, int teamnum)
648 {
649         LOG_TRACE("Survival: RemovePlayerFromTeam, player: ", player.netname);
650         switch (teamnum)
651         {
652                 case surv_attackerteam:
653                 {
654                         LOG_TRACE("Attacker team");
655                         if (player.surv_role == SURVIVAL_ROLE_NONE)
656                         {
657                                 string message = strcat("RemovePlayerFromTeam: ",
658                                         player.netname, " has invalid role.");
659                                 DebugPrintToChatAll(message);
660                                 return;
661                         }
662                         if (player.surv_role == SURVIVAL_ROLE_CANNON_FODDER)
663                         {
664                                 Surv_SetPlayerRole(player, SURVIVAL_ROLE_NONE);
665                                 return;
666                         }
667                         Surv_SetPlayerRole(player, SURVIVAL_ROLE_NONE);
668                         Surv_ChangeNumberOfPlayers(teamnum, -1);
669                         if (!IS_BOT_CLIENT(player))
670                         {
671                                 --surv_numattackerhumans;
672                         }
673                         if ((surv_autobalance == false) || (surv_numattackers >=
674                                 surv_numdefenders))
675                         {
676                                 return;
677                         }
678                         // Add bot to keep teams balanced.
679                         entity lowestplayer = Surv_FindCannonFodder();
680                         if (lowestplayer != NULL)
681                         {
682                                 LOG_TRACE("Changing ", lowestplayer.netname,
683                                         " from cannon fodder to attacker.");
684                                 Surv_SetPlayerRole(lowestplayer, SURVIVAL_ROLE_PLAYER);
685                                 Surv_ChangeNumberOfPlayers(teamnum, +1);
686                                 if (!IS_DEAD(lowestplayer))
687                                 {
688                                         Surv_ChangeNumberOfAlivePlayers(teamnum, +1);
689                                 }
690                                 return;
691                         }
692                         lowestplayer = Surv_FindLowestDefender(true, false);
693                         if (lowestplayer != NULL)
694                         {
695                                 bool savedautobalance = surv_autobalance;
696                                 surv_autobalance = false;
697                                 SetPlayerTeamSimple(lowestplayer, surv_attackerteam);
698                                 surv_autobalance = savedautobalance;
699                                 return;
700                         }
701                         lowestplayer = Surv_FindLowestDefender(false, false);
702                         if (lowestplayer == NULL)
703                         {
704                                 return;
705                         }
706                         bool savedautobalance = surv_autobalance;
707                         surv_autobalance = false;
708                         SetPlayerTeamSimple(lowestplayer, surv_attackerteam);
709                         surv_autobalance = savedautobalance;
710                         return;
711                 }
712                 case surv_defenderteam:
713                 {
714                         LOG_TRACE("Defender team");
715                         if (player.surv_role == SURVIVAL_ROLE_CANNON_FODDER)
716                         {
717                                 // This happens during team switch. We don't need to change
718                                 // anything.
719                                 LOG_TRACE("Cannon fodder. Assuming team switch");
720                                 return;
721                         }
722                         if (player.surv_role != SURVIVAL_ROLE_PLAYER)
723                         {
724                                 string message = strcat("RemovePlayerFromTeam: ",
725                                         player.netname, " has invalid role.");
726                                 DebugPrintToChatAll(message);
727                                 return;
728                         }
729                         Surv_SetPlayerRole(player, SURVIVAL_ROLE_NONE);
730                         Surv_ChangeNumberOfPlayers(teamnum, -1);
731                         if (!IS_BOT_CLIENT(player))
732                         {
733                                 --surv_numdefenderhumans;
734                         }
735                         if ((surv_autobalance == false) || (surv_numdefenders >=
736                                 surv_numattackers))
737                         {
738                                 return;
739                         }
740                         // Add bot to keep teams balanced.
741                         entity lowestplayer = Surv_FindCannonFodder();
742                         if (lowestplayer != NULL)
743                         {
744                                 LOG_TRACE("Changing ", lowestplayer.netname,
745                                         " from cannon fodder to defender.");
746                                 if (!IS_DEAD(player))
747                                 {
748                                         lowestplayer.surv_savedplayerstate =
749                                                 Surv_SavePlayerState(player);
750                                 }
751                                 bool savedautobalance = surv_autobalance;
752                                 surv_autobalance = false;
753                                 surv_announcefrags = false;
754                                 SetPlayerTeamSimple(lowestplayer, surv_defenderteam);
755                                 surv_autobalance = savedautobalance;
756                                 surv_announcefrags = true;                                      
757                                 return;
758                         }
759                         lowestplayer = Surv_FindLowestAttacker(true);
760                         if (lowestplayer != NULL)
761                         {
762                                 bool savedautobalance = surv_autobalance;
763                                 surv_autobalance = false;
764                                 surv_announcefrags = false;
765                                 SetPlayerTeamSimple(lowestplayer, surv_defenderteam);
766                                 surv_autobalance = savedautobalance;
767                                 surv_announcefrags = true;
768                                 return;
769                         }
770                         lowestplayer = Surv_FindLowestAttacker(false);
771                         if (lowestplayer == NULL)
772                         {
773                                 return;
774                         }
775                         bool savedautobalance = surv_autobalance;
776                         surv_autobalance = false;
777                         surv_announcefrags = false;
778                         SetPlayerTeamSimple(lowestplayer, surv_defenderteam);
779                         surv_autobalance = savedautobalance;
780                         surv_announcefrags = true;
781                         return;
782                 }
783                 case -1:
784                 {
785                         LOG_TRACE("Spectator team");
786                         return;
787                 }
788                 default:
789                 {
790                         LOG_TRACE("Invalid team");
791                         return;
792                 }
793         }
794 }
795
796 /// \brief Updates stats of team count on HUD.
797 /// \return No return.
798 void Surv_UpdateTeamStats()
799 {
800         // Debug stuff
801         if (surv_attackerteam == NUM_TEAM_1)
802         {
803                 yellowalive = surv_numattackers;
804                 pinkalive = surv_numdefenders;
805         }
806         else
807         {
808                 pinkalive = surv_numattackers;
809                 yellowalive = surv_numdefenders;
810         }
811         FOREACH_CLIENT(IS_REAL_CLIENT(it),
812         {
813                 it.yellowalive_stat = yellowalive;
814                 it.pinkalive_stat = pinkalive;
815         });
816 }
817
818 /// \brief Adds player to alive list. Handles bookkeeping information.
819 /// \param[in] player Player to add.
820 /// \param[in] teamnum Team of the player.
821 /// \return No return.
822 void Surv_AddPlayerToAliveList(entity player, int teamnum)
823 {
824         switch (teamnum)
825         {
826                 case surv_attackerteam:
827                 {
828                         if (player.surv_role == SURVIVAL_ROLE_PLAYER)
829                         {
830                                 Surv_ChangeNumberOfAlivePlayers(teamnum, +1);
831                         }
832                         return;
833                 }
834                 case surv_defenderteam:
835                 {
836                         Surv_ChangeNumberOfAlivePlayers(teamnum, +1);
837                         return;
838                 }
839         }
840 }
841
842 /// \brief Removes player from alive list. Handles bookkeeping information.
843 /// \param[in] player Player to remove.
844 /// \param[in] teamnum Team of the player.
845 /// \return No return.
846 void Surv_RemovePlayerFromAliveList(entity player, int teamnum)
847 {
848         if (player.surv_attack_sprite)
849         {
850                 WaypointSprite_Kill(player.surv_attack_sprite);
851         }
852         switch (teamnum)
853         {
854                 case surv_attackerteam:
855                 {
856                         if (player.surv_role == SURVIVAL_ROLE_PLAYER)
857                         {
858                                 Surv_ChangeNumberOfAlivePlayers(teamnum, -1);
859                         }
860                         return;
861                 }
862                 case surv_defenderteam:
863                 {
864                         if (player.surv_role == SURVIVAL_ROLE_CANNON_FODDER)
865                         {
866                                 // This happens during team switch. We don't need to change
867                                 // anything.
868                                 return;
869                         }
870                         Surv_ChangeNumberOfAlivePlayers(teamnum, -1);
871                         if (warmup_stage || surv_allowed_to_spawn || !surv_announcefrags)
872                         {
873                                 return;
874                         }
875                         switch (surv_numdefendersalive)
876                         {
877                                 case 1:
878                                 {
879                                         sound(NULL, CH_TRIGGER, SND_SURV_1_FRAG_LEFT,
880                                                 VOL_BASE, ATTEN_NONE);
881                                         FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it),
882                                         {
883                                                 if (it.team == surv_defenderteam)
884                                                 {
885                                                         Send_Notification(NOTIF_ONE, it, MSG_CENTER,
886                                                                 CENTER_ALONE);
887                                                         return;
888                                                 }
889                                         });
890                                         return;
891                                 }
892                                 case 2:
893                                 {
894                                         sound(NULL, CH_TRIGGER, SND_SURV_2_FRAGS_LEFT,
895                                                 VOL_BASE, ATTEN_NONE);
896                                         return;
897                                 }
898                                 case 3:
899                                 {
900                                         sound(NULL, CH_TRIGGER, SND_SURV_3_FRAGS_LEFT,
901                                                 VOL_BASE, ATTEN_NONE);
902                                         return;
903                                 }
904                         }
905                         return;
906                 }
907         }
908 }
909
910 /// \brief Counts alive players.
911 /// \return No return.
912 /// \note In a perfect world this function shouldn't exist. However, since QC
913 /// code is so bad and spurious mutators can really mess with your code, this
914 /// function is called as a last resort.
915 void Surv_CountAlivePlayers()
916 {
917         int savednumdefenders = surv_numdefendersalive;
918         surv_numattackersalive = 0;
919         surv_numdefendersalive = 0;
920         FOREACH_CLIENT(IS_PLAYER(it),
921         {
922                 switch (it.team)
923                 {
924                         case surv_attackerteam:
925                         {
926                                 if ((it.surv_role == SURVIVAL_ROLE_PLAYER) && !IS_DEAD(it))
927                                 {
928                                         ++surv_numattackersalive;
929                                 }
930                                 break;
931                         }
932                         case surv_defenderteam:
933                         {
934                                 if ((it.surv_role == SURVIVAL_ROLE_PLAYER) && !IS_DEAD(it))
935                                 {
936                                         ++surv_numdefendersalive;
937                                 }
938                                 break;
939                         }
940                 }
941         });
942         Surv_UpdateAliveStats();
943         eliminatedPlayers.SendFlags |= 1;
944         if (warmup_stage || surv_allowed_to_spawn || (savednumdefenders <=
945                 surv_numdefendersalive))
946         {
947                 return;
948         }
949         switch (surv_numdefendersalive)
950         {
951                 case 1:
952                 {
953                         sound(NULL, CH_TRIGGER, SND_SURV_1_FRAG_LEFT, VOL_BASE, ATTEN_NONE);
954                         FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it),
955                         {
956                                 if (it.team == surv_defenderteam)
957                                 {
958                                         Send_Notification(NOTIF_ONE, it, MSG_CENTER, CENTER_ALONE);
959                                         return;
960                                 }
961                         });
962                         return;
963                 }
964                 case 2:
965                 {
966                         sound(NULL, CH_TRIGGER, SND_SURV_2_FRAGS_LEFT, VOL_BASE,
967                                 ATTEN_NONE);
968                         return;
969                 }
970                 case 3:
971                 {
972                         sound(NULL, CH_TRIGGER, SND_SURV_3_FRAGS_LEFT, VOL_BASE,
973                                 ATTEN_NONE);
974                         return;
975                 }
976         }
977 }
978
979 /// \brief Updates stats of alive players on HUD.
980 /// \return No return.
981 void Surv_UpdateAliveStats()
982 {
983         // Debug stuff
984         if (surv_attackerteam == NUM_TEAM_1)
985         {
986                 redalive = surv_numattackersalive;
987                 bluealive = surv_numdefendersalive;
988         }
989         else
990         {
991                 bluealive = surv_numattackersalive;
992                 redalive = surv_numdefendersalive;
993         }
994         FOREACH_CLIENT(IS_REAL_CLIENT(it),
995         {
996                 it.surv_defenders_alive_stat = surv_numdefendersalive;
997                 it.redalive_stat = redalive;
998                 it.bluealive_stat = bluealive;
999         });
1000         Surv_UpdateDefenderHealthStat();
1001 }
1002
1003 /// \brief Updates defender health on the HUD.
1004 /// \return No return.
1005 void Surv_UpdateDefenderHealthStat()
1006 {
1007         float maxhealth;
1008         float totalhealth = 0;
1009         if (autocvar_g_instagib == 1)
1010         {
1011                 maxhealth = surv_numdefenders * (PlayerTemplate_GetFloatValue(
1012                         "surv_defender", "start_armor") + 1);
1013                 FOREACH_CLIENT(IS_PLAYER(it),
1014                 {
1015                         if (it.team == surv_defenderteam)
1016                         {
1017                                 totalhealth += it.armorvalue + 1;
1018                         }
1019                 });
1020         }
1021         else
1022         {
1023                 maxhealth = surv_numdefenders * (PlayerTemplate_GetFloatValue(
1024                         "surv_defender", "start_health") + PlayerTemplate_GetFloatValue(
1025                         "surv_defender", "start_armor"));
1026                 FOREACH_CLIENT(IS_PLAYER(it),
1027                 {
1028                         if (it.team == surv_defenderteam)
1029                         {
1030                                 totalhealth += it.health;
1031                                 totalhealth += it.armorvalue;
1032                         }
1033                 });
1034         }
1035         float healthratio;
1036         if (maxhealth == 0)
1037         {
1038                 healthratio = 0;
1039         }
1040         else
1041         {
1042                 healthratio = totalhealth / maxhealth;
1043         }
1044         FOREACH_CLIENT(IS_REAL_CLIENT(it),
1045         {
1046                 it.surv_defender_health_stat = healthratio;
1047         });
1048 }
1049
1050 /// \brief Returns whether the player can spawn.
1051 /// \param[in] player Player to check.
1052 /// \return True if the player can spawn, false otherwise.
1053 bool Surv_CanPlayerSpawn(entity player)
1054 {
1055         if ((player.team == surv_attackerteam) ||
1056                 (player.surv_savedplayerstate != NULL))
1057         {
1058                 return true;
1059         }
1060         return surv_allowed_to_spawn;
1061 }
1062
1063 /// \brief Switches the round type.
1064 /// \return No return.
1065 void Surv_SwitchRoundType()
1066 {
1067         switch (surv_roundtype)
1068         {
1069                 case SURVIVAL_ROUND_FIRST:
1070                 {
1071                         surv_roundtype = SURVIVAL_ROUND_SECOND;
1072                         return;
1073                 }
1074                 case SURVIVAL_ROUND_SECOND:
1075                 {
1076                         surv_roundtype = SURVIVAL_ROUND_FIRST;
1077                         return;
1078                 }
1079         }
1080 }
1081
1082 /// \brief Cleans up the mess after the round has finished.
1083 /// \return No return.
1084 void Surv_RoundCleanup()
1085 {
1086         surv_allowed_to_spawn = false;
1087         surv_isroundactive = false;
1088         game_stopped = true;
1089         FOREACH_CLIENT(true,
1090         {
1091                 if (it.surv_attack_sprite)
1092                 {
1093                         WaypointSprite_Kill(it.surv_attack_sprite);
1094                 }
1095                 if (it.surv_savedplayerstate)
1096                 {
1097                         delete(it.surv_savedplayerstate);
1098                         it.surv_savedplayerstate = NULL;
1099                 }
1100         });
1101         if (surv_type == SURVIVAL_TYPE_VERSUS)
1102         {
1103                 Surv_SwitchRoundType();
1104                 round_handler_Init(5, autocvar_g_surv_warmup, surv_timetobeat);
1105                 return;
1106         }
1107         round_handler_Init(5, autocvar_g_surv_warmup,
1108                 autocvar_g_surv_round_timelimit);
1109 }
1110
1111 /// \brief Swaps attacker and defender teams.
1112 /// \return No return.
1113 void Surv_SwapTeams()
1114 {
1115         int temp = surv_attackerteam;
1116         surv_attackerteam = surv_defenderteam;
1117         surv_defenderteam = temp;
1118         temp = surv_attackerteambit;
1119         surv_attackerteambit = surv_defenderteambit;
1120         surv_defenderteambit = temp;
1121         temp = surv_numattackers;
1122         surv_numattackers = surv_numdefenders;
1123         surv_numdefenders = temp;
1124         temp = surv_numattackerhumans;
1125         surv_numattackerhumans = surv_numdefenderhumans;
1126         surv_numdefenderhumans = temp;
1127         FOREACH_CLIENT(true,
1128         {
1129                 if ((it.team == surv_defenderteam) && (it.surv_role ==
1130                         SURVIVAL_ROLE_CANNON_FODDER))
1131                 {
1132                         SetPlayerTeamSimple(it, surv_attackerteam);
1133                 }
1134         });
1135         FOREACH_CLIENT(IS_REAL_CLIENT(it),
1136         {
1137                 it.surv_defender_team_stat = Team_TeamToNumber(surv_defenderteam);
1138         });
1139 }
1140
1141 /// \brief Forces the overkill model for specific player.
1142 /// \param[in,out] player Player to force the model of.
1143 /// \return No return.
1144 void Surv_ForceOverkillPlayerModel(entity player)
1145 {
1146         switch (player.team)
1147         {
1148                 case NUM_TEAM_1:
1149                 {
1150                         switch (floor(random() * 4))
1151                         {
1152                                 case 0:
1153                                 {
1154                                         player.surv_playermodel = "models/ok_player/okrobot1.dpm";
1155                                         return;
1156                                 }
1157                                 case 1:
1158                                 {
1159                                         player.surv_playermodel = "models/ok_player/okrobot2.dpm";
1160                                         return;
1161                                 }
1162                                 case 2:
1163                                 {
1164                                         player.surv_playermodel = "models/ok_player/okrobot3.dpm";
1165                                         return;
1166                                 }
1167                                 case 3:
1168                                 {
1169                                         player.surv_playermodel = "models/ok_player/okrobot4.dpm";
1170                                         return;
1171                                 }
1172                         }
1173                         return;
1174                 }
1175                 case NUM_TEAM_2:
1176                 {
1177                         switch (floor(random() * 4))
1178                         {
1179                                 case 0:
1180                                 {
1181                                         player.surv_playermodel = "models/ok_player/okmale1.dpm";
1182                                         return;
1183                                 }
1184                                 case 1:
1185                                 {
1186                                         player.surv_playermodel = "models/ok_player/okmale2.dpm";
1187                                         return;
1188                                 }
1189                                 case 2:
1190                                 {
1191                                         player.surv_playermodel = "models/ok_player/okmale3.dpm";
1192                                         return;
1193                                 }
1194                                 case 3:
1195                                 {
1196                                         player.surv_playermodel = "models/ok_player/okmale4.dpm";
1197                                         return;
1198                                 }
1199                         }
1200                         return;
1201                 }
1202         }
1203 }
1204
1205 /// \brief Determines the player model to the one configured for the gamemode.
1206 /// \param[in,out] player Player to determine the model of.
1207 /// \return No return.
1208 void Surv_DeterminePlayerModel(entity player)
1209 {
1210         switch (player.team)
1211         {
1212                 case surv_attackerteam:
1213                 {
1214                         switch (player.surv_role)
1215                         {
1216                                 case SURVIVAL_ROLE_PLAYER:
1217                                 {
1218                                         if (!autocvar_g_surv_attacker_force_overkill_models)
1219                                         {
1220                                                 player.surv_playermodel = player.surv_savedplayermodel;
1221                                                 return;
1222                                         }
1223                                         Surv_ForceOverkillPlayerModel(player);
1224                                         return;
1225                                 }
1226                                 case SURVIVAL_ROLE_CANNON_FODDER:
1227                                 {
1228                                         if (!autocvar_g_surv_cannon_fodder_force_overkill_models)
1229                                         {
1230                                                 player.surv_playermodel = player.surv_savedplayermodel;
1231                                                 return;
1232                                         }
1233                                         Surv_ForceOverkillPlayerModel(player);
1234                                         return;
1235                                 }
1236                         }
1237                 }
1238                 case surv_defenderteam:
1239                 {
1240                         if (!autocvar_g_surv_defender_force_overkill_models)
1241                         {
1242                                 player.surv_playermodel = player.surv_savedplayermodel;
1243                                 return;
1244                         }
1245                         Surv_ForceOverkillPlayerModel(player);
1246                         return;
1247                 }
1248         }
1249 }
1250
1251 /// \brief Setups a waypoint sprite used to track defenders.
1252 /// \param[in] player Player to attach sprite too.
1253 /// \return No return.
1254 void Surv_SetupWaypointSprite(entity player)
1255 {
1256         WaypointSprite_Spawn(WP_AssaultDestroy, 0, 0, player, '0 0 64', NULL,
1257                 surv_attackerteam, player, surv_attack_sprite, false,
1258                 RADARICON_OBJECTIVE);
1259         if (autocvar_g_instagib == 1)
1260         {
1261                 WaypointSprite_UpdateMaxHealth(player.surv_attack_sprite,
1262                         PlayerTemplate_GetFloatValue("surv_defender", "start_armor") + 1);
1263                 WaypointSprite_UpdateHealth(player.surv_attack_sprite,
1264                         player.armorvalue + 1);
1265                 return;
1266         }
1267         WaypointSprite_UpdateMaxHealth(player.surv_attack_sprite,
1268                 PlayerTemplate_GetFloatValue("surv_defender", "start_health") +
1269                 PlayerTemplate_GetFloatValue("surv_defender", "start_armor"));
1270         WaypointSprite_UpdateHealth(player.surv_attack_sprite, player.health +
1271                 player.armorvalue);
1272 }
1273
1274 //=============================== Callbacks ===================================
1275
1276 bool Surv_CanRoundStart()
1277 {
1278         return (surv_numattackersalive > 0) && (surv_numdefendersalive > 0);
1279 }
1280
1281 bool Surv_CanRoundEnd()
1282 {
1283         if (warmup_stage)
1284         {
1285                 return false;
1286         }
1287         if((round_handler_GetEndTime() > 0) && (round_handler_GetEndTime() -
1288                 time <= 0))
1289         {
1290                 if (surv_roundtype == SURVIVAL_ROUND_FIRST)
1291                 {
1292                         surv_timetobeat = time - surv_roundstarttime;
1293                         Send_Notification(NOTIF_ALL, NULL, MSG_CENTER,
1294                                 CENTER_SURVIVAL_DEFENDERS_SURVIVED);
1295                         Send_Notification(NOTIF_ALL, NULL, MSG_INFO,
1296                                 INFO_SURVIVAL_DEFENDERS_SURVIVED);
1297                         Surv_RoundCleanup();
1298                         return true;
1299                 }
1300                 surv_timetobeat = autocvar_g_surv_round_timelimit;
1301                 Send_Notification(NOTIF_ALL, NULL, MSG_CENTER,
1302                         CENTER_SURVIVAL_DEFENDERS_SURVIVED);
1303                 Send_Notification(NOTIF_ALL, NULL, MSG_INFO,
1304                         INFO_SURVIVAL_DEFENDERS_SURVIVED);
1305                 switch (surv_defenderteam)
1306                 {
1307                         case NUM_TEAM_1:
1308                         {
1309                                 sound(NULL, CH_TRIGGER, SND_SURV_RED_SCORES, VOL_BASE,
1310                                         ATTEN_NONE);
1311                                 break;
1312                         }
1313                         case NUM_TEAM_2:
1314                         {
1315                                 sound(NULL, CH_TRIGGER, SND_SURV_BLUE_SCORES, VOL_BASE,
1316                                         ATTEN_NONE);
1317                                 break;
1318                         }
1319                 }
1320                 TeamScore_AddToTeam(surv_defenderteam, 1, 1);
1321                 Surv_RoundCleanup();
1322                 return true;
1323         }
1324         if (surv_numdefendersalive > 0)
1325         {
1326                 return false;
1327         }
1328         if (surv_roundtype == SURVIVAL_ROUND_FIRST)
1329         {
1330                 surv_timetobeat = time - surv_roundstarttime;
1331                 Send_Notification(NOTIF_ALL, NULL, MSG_CENTER,
1332                         CENTER_SURVIVAL_DEFENDERS_ELIMINATED_IN, surv_timetobeat);
1333                 Send_Notification(NOTIF_ALL, NULL, MSG_INFO,
1334                         INFO_SURVIVAL_DEFENDERS_ELIMINATED_IN, surv_timetobeat);
1335                 Surv_RoundCleanup();
1336                 return true;
1337         }
1338         surv_timetobeat = autocvar_g_surv_round_timelimit;
1339         Send_Notification(NOTIF_ALL, NULL, MSG_CENTER,
1340                 CENTER_SURVIVAL_DEFENDERS_ELIMINATED);
1341         Send_Notification(NOTIF_ALL, NULL, MSG_INFO,
1342                 INFO_SURVIVAL_DEFENDERS_ELIMINATED);
1343         switch (surv_attackerteam)
1344         {
1345                 case NUM_TEAM_1:
1346                 {
1347                         sound(NULL, CH_TRIGGER, SND_SURV_RED_SCORES, VOL_BASE,
1348                                 ATTEN_NONE);
1349                         break;
1350                 }
1351                 case NUM_TEAM_2:
1352                 {
1353                         sound(NULL, CH_TRIGGER, SND_SURV_BLUE_SCORES, VOL_BASE,
1354                                 ATTEN_NONE);
1355                         break;
1356                 }
1357         }
1358         TeamScore_AddToTeam(surv_attackerteam, 1, 1);
1359         Surv_RoundCleanup();
1360         return true;
1361 }
1362
1363 void Surv_RoundStart()
1364 {
1365         if (warmup_stage)
1366         {
1367                 surv_allowed_to_spawn = true;
1368                 return;
1369         }
1370         surv_isroundactive = true;
1371         surv_roundstarttime = time;
1372         surv_allowed_to_spawn = false;
1373         switch (surv_roundtype)
1374         {
1375                 case SURVIVAL_ROUND_FIRST:
1376                 {
1377                         FOREACH_CLIENT(IS_PLAYER(it),
1378                         {
1379                                 if (it.team == surv_attackerteam)
1380                                 {
1381                                         Send_Notification(NOTIF_TEAM, it, MSG_CENTER,
1382                                                 CENTER_SURVIVAL_1ST_ROUND_ATTACKER);
1383                                         Send_Notification(NOTIF_TEAM, it, MSG_INFO,
1384                                                 INFO_SURVIVAL_1ST_ROUND_ATTACKER);
1385                                         break;
1386                                 }
1387                         });
1388                         FOREACH_CLIENT(IS_PLAYER(it),
1389                         {
1390                                 if (it.team == surv_defenderteam)
1391                                 {
1392                                         if (surv_type == SURVIVAL_TYPE_COOP)
1393                                         {
1394                                                 Send_Notification(NOTIF_TEAM, it, MSG_CENTER,
1395                                                         CENTER_SURVIVAL_COOP_DEFENDER);
1396                                                 Send_Notification(NOTIF_TEAM, it, MSG_INFO,
1397                                                         INFO_SURVIVAL_COOP_DEFENDER);
1398                                                 break;
1399                                         }
1400                                         Send_Notification(NOTIF_TEAM, it, MSG_CENTER,
1401                                                 CENTER_SURVIVAL_1ST_ROUND_DEFENDER);
1402                                         Send_Notification(NOTIF_TEAM, it, MSG_INFO,
1403                                                 INFO_SURVIVAL_1ST_ROUND_DEFENDER);
1404                                         break;
1405                                 }
1406                         });
1407                         break;
1408                 }
1409                 case SURVIVAL_ROUND_SECOND:
1410                 {
1411                         FOREACH_CLIENT(IS_PLAYER(it),
1412                         {
1413                                 if (it.team == surv_attackerteam)
1414                                 {
1415                                         Send_Notification(NOTIF_TEAM, it, MSG_CENTER,
1416                                                 CENTER_SURVIVAL_2ND_ROUND_ATTACKER, surv_timetobeat);
1417                                         Send_Notification(NOTIF_TEAM, it, MSG_INFO,
1418                                                 INFO_SURVIVAL_2ND_ROUND_ATTACKER, surv_timetobeat);
1419                                         break;
1420                                 }
1421                         });
1422                         FOREACH_CLIENT(IS_PLAYER(it),
1423                         {
1424                                 if (it.team == surv_defenderteam)
1425                                 {
1426                                         Send_Notification(NOTIF_TEAM, it, MSG_CENTER,
1427                                                 CENTER_SURVIVAL_2ND_ROUND_DEFENDER, surv_timetobeat);
1428                                         Send_Notification(NOTIF_TEAM, it, MSG_INFO,
1429                                                 INFO_SURVIVAL_2ND_ROUND_DEFENDER, surv_timetobeat);
1430                                         break;
1431                                 }
1432                         });
1433                         break;
1434                 }
1435         }
1436         if (autocvar_g_surv_stealth)
1437         {
1438                 return;
1439         }
1440         FOREACH_CLIENT(IS_PLAYER(it),
1441         {
1442                 switch (it.team)
1443                 {
1444                         case surv_defenderteam:
1445                         {
1446                                 if (it.surv_role == SURVIVAL_ROLE_PLAYER)
1447                                 {
1448                                         Surv_SetupWaypointSprite(it);
1449                                 }
1450                                 break;
1451                         }
1452                 }
1453         });
1454 }
1455
1456 bool Surv_IsEliminated(entity player)
1457 {
1458         switch (player.surv_state)
1459         {
1460                 case SURVIVAL_STATE_NOT_PLAYING:
1461                 {
1462                         return true;
1463                 }
1464                 case SURVIVAL_STATE_PLAYING:
1465                 {
1466                         if (player.surv_role == SURVIVAL_ROLE_CANNON_FODDER)
1467                         {
1468                                 // A hack until proper scoreboard is done.
1469                                 return true;
1470                         }
1471                         if ((player.team == surv_defenderteam) && (IS_DEAD(player) ||
1472                                 IS_OBSERVER(player)))
1473                         {
1474                                 return true;
1475                         }
1476                         return false;
1477                 }
1478         }
1479         // Should never reach here
1480         return true;
1481 }
1482
1483 //============================= Hooks ========================================
1484
1485 /// \brief Hook that is called to determine general rules of the game. 
1486 MUTATOR_HOOKFUNCTION(surv, ReadLevelCvars)
1487 {
1488         surv_warmup = warmup_stage;
1489 }
1490
1491 /// \brief Hook that is called to determine if there is a weapon arena.
1492 MUTATOR_HOOKFUNCTION(surv, SetWeaponArena)
1493 {
1494         // Removing any weapon arena.
1495         M_ARGV(0, string) = "off";
1496 }
1497
1498 /// \brief Hook that is called to determine start items of all players.
1499 MUTATOR_HOOKFUNCTION(surv, SetStartItems)
1500 {
1501         if (autocvar_g_instagib == 1)
1502         {
1503                 return;
1504         }
1505         start_weapons = WEPSET(Null);
1506         warmup_start_weapons = WEPSET(Null);
1507 }
1508
1509 /// \brief Hook that is called on every frame.
1510 MUTATOR_HOOKFUNCTION(surv, SV_StartFrame)
1511 {
1512         if (game_stopped || !surv_isroundactive)
1513         {
1514                 return;
1515         }
1516         float roundtime = 0;
1517         switch (surv_roundtype)
1518         {
1519                 case SURVIVAL_ROUND_FIRST:
1520                 {
1521                         roundtime = time - surv_roundstarttime;
1522                         break;
1523                 }
1524                 case SURVIVAL_ROUND_SECOND:
1525                 {
1526                         roundtime = round_handler_GetEndTime() - time;
1527                         break;
1528                 }
1529         }
1530         FOREACH_CLIENT(IS_REAL_CLIENT(it),
1531         {
1532                 it.surv_round_time_stat = roundtime;
1533         });
1534 }
1535
1536 /// \brief Hook that determines which team player can join. This is called
1537 /// before ClientConnect.
1538 MUTATOR_HOOKFUNCTION(surv, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE)
1539 {
1540         entity player = M_ARGV(2, entity);
1541         LOG_TRACE("Survival: CheckAllowedTeams, player = ", player.netname);
1542         if (player == NULL)
1543         {
1544                 return SURVIVAL_TEAM_BITS;
1545         }
1546         if (IS_BOT_CLIENT(player))
1547         {
1548                 int teambits = surv_attackerteambit;
1549                 if ((player.team == surv_defenderteam) || (surv_numdefenders <
1550                         autocvar_g_surv_team_size))
1551                 {
1552                         teambits |= surv_defenderteambit;
1553                 }
1554                 M_ARGV(0, float) = teambits;
1555                 return;
1556         }
1557         if (surv_type == SURVIVAL_TYPE_COOP)
1558         {
1559                 if (surv_numdefenderhumans < autocvar_g_surv_team_size)
1560                 {
1561                         M_ARGV(0, float) = surv_defenderteambit;
1562                         return;
1563                 }
1564                 M_ARGV(0, float) = 0;
1565                 return;
1566         }
1567         int teambits = 0;
1568         if (surv_numattackerhumans < autocvar_g_surv_team_size)
1569         {
1570                 LOG_TRACE("Player can join attackers");
1571                 teambits |= surv_attackerteambit;
1572         }
1573         if (surv_numdefenderhumans < autocvar_g_surv_team_size)
1574         {
1575                 LOG_TRACE("Player can join defenders");
1576                 teambits |= surv_defenderteambit;
1577         }
1578         M_ARGV(0, float) = teambits;
1579         return;
1580 }
1581
1582 /// \brief Hook that override team counts.
1583 MUTATOR_HOOKFUNCTION(surv, GetTeamCounts, CBC_ORDER_EXCLUSIVE)
1584 {
1585         return true;
1586 }
1587
1588 /// \brief Hook that sets the team count.
1589 MUTATOR_HOOKFUNCTION(surv, GetTeamCount, CBC_ORDER_EXCLUSIVE)
1590 {
1591         float teamnum = M_ARGV(0, float);
1592         entity ignore = M_ARGV(1, entity);
1593         switch (teamnum)
1594         {
1595                 case surv_attackerteam:
1596                 {
1597                         M_ARGV(2, float) = surv_numattackers;
1598                         M_ARGV(3, float) = surv_numattackers - surv_numattackerhumans;
1599                         if (ignore.team == surv_attackerteam)
1600                         {
1601                                 --M_ARGV(2, float);
1602                                 if (IS_BOT_CLIENT(ignore))
1603                                 {
1604                                         --M_ARGV(3, float);
1605                                 }
1606                         }
1607                         entity lowestplayer = NULL;
1608                         float lowestplayerscore = FLOAT_MAX;
1609                         entity lowestbot = NULL;
1610                         float lowestbotscore = FLOAT_MAX;
1611                         FOREACH_CLIENT((it.team == surv_attackerteam) && (it.surv_role ==
1612                                 SURVIVAL_ROLE_PLAYER),
1613                         {
1614                                 if (it == ignore)
1615                                 {
1616                                         continue;
1617                                 }
1618                                 if (IS_BOT_CLIENT(it))
1619                                 {
1620                                         float tempscore = PlayerScore_Get(it, SP_SCORE);
1621                                         if (tempscore < lowestbotscore)
1622                                         {
1623                                                 lowestbot = it;
1624                                                 lowestbotscore = tempscore;
1625                                                 continue;
1626                                         }
1627                                 }
1628                                 float tempscore = PlayerScore_Get(it, SP_SCORE);
1629                                 if (tempscore < lowestplayerscore)
1630                                 {
1631                                         lowestplayer = it;
1632                                         lowestplayerscore = tempscore;
1633                                 }
1634                         });
1635                         M_ARGV(4, entity) = lowestplayer;
1636                         M_ARGV(5, entity) = lowestbot;
1637                         break;
1638                 }
1639                 case surv_defenderteam:
1640                 {
1641                         M_ARGV(2, float) = surv_numdefenders;
1642                         M_ARGV(3, float) = surv_numdefenders - surv_numdefenderhumans;
1643                         if (ignore.team == surv_defenderteam)
1644                         {
1645                                 --M_ARGV(2, float);
1646                                 if (IS_BOT_CLIENT(ignore))
1647                                 {
1648                                         --M_ARGV(3, float);
1649                                 }
1650                         }
1651                         entity lowestplayer = NULL;
1652                         float lowestplayerscore = FLOAT_MAX;
1653                         entity lowestbot = NULL;
1654                         float lowestbotscore = FLOAT_MAX;
1655                         FOREACH_CLIENT((it.team == surv_defenderteam),
1656                         {
1657                                 if (it == ignore)
1658                                 {
1659                                         continue;
1660                                 }
1661                                 if (IS_BOT_CLIENT(it))
1662                                 {
1663                                         float tempscore = PlayerScore_Get(it, SP_SCORE);
1664                                         if (tempscore < lowestbotscore)
1665                                         {
1666                                                 lowestbot = it;
1667                                                 lowestbotscore = tempscore;
1668                                                 continue;
1669                                         }
1670                                 }
1671                                 float tempscore = PlayerScore_Get(it, SP_SCORE);
1672                                 if (tempscore < lowestplayerscore)
1673                                 {
1674                                         lowestplayer = it;
1675                                         lowestplayerscore = tempscore;
1676                                 }
1677                         });
1678                         M_ARGV(4, entity) = lowestplayer;
1679                         M_ARGV(5, entity) = lowestbot;
1680                         break;
1681                 }
1682         }
1683         return true;
1684 }
1685
1686 /// \brief Hook that determines the best teams for the player to join.
1687 MUTATOR_HOOKFUNCTION(surv, FindBestTeams, CBC_ORDER_EXCLUSIVE)
1688 {
1689         if (surv_type == SURVIVAL_TYPE_COOP)
1690         {
1691                 return false;
1692         }
1693         entity player = M_ARGV(0, entity);
1694         if (IS_BOT_CLIENT(player))
1695         {
1696                 return false;
1697         }
1698         int numattackerhumans = surv_numattackerhumans;
1699         int numdefenderhumans = surv_numdefenderhumans;
1700         if (player.team == surv_attackerteam)
1701         {
1702                 --numattackerhumans;
1703         }
1704         else if (player.team == surv_defenderteam)
1705         {
1706                 --numdefenderhumans;
1707         }
1708         if (numattackerhumans < numdefenderhumans)
1709         {
1710                 M_ARGV(1, float) = BIT(Team_TeamToNumber(surv_attackerteam) - 1);
1711                 return true;
1712         }
1713         if (numattackerhumans > numdefenderhumans)
1714         {
1715                 M_ARGV(1, float) = BIT(Team_TeamToNumber(surv_defenderteam) - 1);
1716                 return true;
1717         }
1718         M_ARGV(1, float) = SURVIVAL_TEAM_BITS;
1719         return true;
1720 }
1721
1722 /// \brief Hook that is called when player has changed the team.
1723 MUTATOR_HOOKFUNCTION(surv, Player_ChangedTeam)
1724 {
1725         entity player = M_ARGV(0, entity);
1726         int oldteam = M_ARGV(1, float);
1727         int newteam = M_ARGV(2, float);
1728         string message = strcat("Survival: Player_ChangedTeam, ", player.netname,
1729                 ", old team = ", ftos(oldteam), " new team = ", ftos(newteam));
1730         LOG_TRACE(message);
1731         DebugPrintToChatAll(message);
1732         if ((oldteam != -1) && IS_PLAYER(player) && !IS_DEAD(player))
1733         {
1734                 Surv_RemovePlayerFromAliveList(player, oldteam);
1735         }
1736         Surv_RemovePlayerFromTeam(player, oldteam);
1737         if (Surv_AddPlayerToTeam(player, newteam) == false)
1738         {
1739                 return;
1740         }
1741         //Surv_CountAlivePlayers();
1742         if ((oldteam != -1) && IS_PLAYER(player) && !IS_DEAD(player))
1743         {
1744                 Surv_AddPlayerToAliveList(player, newteam);
1745         }
1746 }
1747
1748 /// \brief Hook that is called when player is about to be killed when changing
1749 /// teams.
1750 MUTATOR_HOOKFUNCTION(surv, Player_ChangeTeamKill)
1751 {
1752         entity player = M_ARGV(0, entity);
1753         if (player.team != surv_defenderteam)
1754         {
1755                 return false;
1756         }
1757         if (player.surv_savedplayerstate == NULL)
1758         {
1759                 return false;
1760         }
1761         Surv_RestorePlayerState(player, player.surv_savedplayerstate);
1762         delete(player.surv_savedplayerstate);
1763         player.surv_savedplayerstate = NULL;
1764         return true;
1765 }
1766
1767 /// \brief Hook that is called when player is about to be killed as a result of
1768 /// the kill command or changing teams.
1769 MUTATOR_HOOKFUNCTION(surv, ClientKill_Now)
1770 {
1771         entity player = M_ARGV(0, entity);
1772         if (player.team == surv_defenderteam)
1773         {
1774                 // Deny suicide.
1775                 return true;
1776         }
1777 }
1778
1779 /// \brief Hook that is called when player connects to the server.
1780 MUTATOR_HOOKFUNCTION(surv, ClientConnect)
1781 {
1782         entity player = M_ARGV(0, entity);
1783         LOG_TRACE("Survival: ClientConnect, player = ", player.netname);
1784         player.surv_savedplayermodel = player.playermodel;
1785         if (IS_REAL_CLIENT(player))
1786         {
1787                 player.surv_defender_team_stat = Team_TeamToNumber(surv_defenderteam);
1788                 player.surv_defenders_alive_stat = surv_numdefendersalive;
1789                 player.redalive_stat = redalive;
1790                 player.bluealive_stat = bluealive;
1791                 player.yellowalive_stat = yellowalive;
1792                 player.pinkalive_stat = pinkalive;
1793         }
1794         if (player.surv_role == SURVIVAL_ROLE_NONE)
1795         {
1796                 Surv_AddPlayerToTeam(player, player.team);
1797         }
1798         return true;
1799 }
1800
1801 /// \brief Hook that is called when player disconnects from the server.
1802 MUTATOR_HOOKFUNCTION(surv, ClientDisconnect)
1803 {
1804     entity player = M_ARGV(0, entity);
1805         if (!IS_DEAD(player))
1806         {
1807                 Surv_RemovePlayerFromAliveList(player, player.team);
1808         }
1809         Surv_RemovePlayerFromTeam(player, player.team);
1810         //Surv_CountAlivePlayers();
1811 }
1812
1813 /// \brief Hook that determines whether player can spawn. It is not called for
1814 /// players who have joined the team and are dead.
1815 MUTATOR_HOOKFUNCTION(surv, ForbidSpawn)
1816 {
1817         entity player = M_ARGV(0, entity);
1818         LOG_TRACE("Survival: ForbidSpawn, player = ", player.netname);
1819         if (player.surv_state == SURVIVAL_STATE_NOT_PLAYING)
1820         {
1821                 return false;
1822         }
1823         return !Surv_CanPlayerSpawn(player);
1824 }
1825
1826 MUTATOR_HOOKFUNCTION(surv, PutClientInServer)
1827 {
1828         entity player = M_ARGV(0, entity);
1829         LOG_TRACE("Survival: PutClientInServer, player = ", player.netname);
1830         if (!Surv_CanPlayerSpawn(player) && IS_PLAYER(player))
1831         {
1832                 LOG_TRACE("Transmuting to observer");
1833                 TRANSMUTE(Observer, player);
1834         }
1835 }
1836
1837 MUTATOR_HOOKFUNCTION(surv, MakePlayerObserver)
1838 {
1839         entity player = M_ARGV(0, entity);
1840         LOG_TRACE("Survival: MakePlayerObserver, player = ", player.netname);
1841         if (player.killindicator_teamchange == -2) // player wants to spectate
1842         {
1843                 LOG_TRACE("killindicator_teamchange == -2");
1844                 player.surv_state = SURVIVAL_STATE_NOT_PLAYING;
1845         }
1846         if (player.surv_state == SURVIVAL_STATE_NOT_PLAYING)
1847         {
1848                 return false;  // allow team reset
1849         }
1850         return true;  // prevent team reset
1851 }
1852
1853 MUTATOR_HOOKFUNCTION(surv, reset_map_global)
1854 {
1855         LOG_TRACE("Survival: reset_map_global");
1856         surv_allowed_to_spawn = true;
1857         if (surv_roundtype == SURVIVAL_ROUND_FIRST)
1858         {
1859                 FOREACH_CLIENT(IS_REAL_CLIENT(it),
1860                 {
1861                         it.surv_round_time_stat = 0;
1862                 });
1863         }
1864         return true;
1865 }
1866
1867 MUTATOR_HOOKFUNCTION(surv, reset_map_players)
1868 {
1869         LOG_TRACE("Survival: reset_map_players");
1870         surv_numattackersalive = 0;
1871         surv_numdefendersalive = 0;
1872         if (surv_warmup)
1873         {
1874                 surv_warmup = false;
1875         }
1876         else if (surv_type == SURVIVAL_TYPE_VERSUS)
1877         {
1878                 Surv_SwapTeams();
1879         }
1880         FOREACH_CLIENT(true,
1881         {
1882                 it.killcount = 0;
1883                 if ((it.surv_state == SURVIVAL_STATE_NOT_PLAYING) && IS_BOT_CLIENT(it))
1884                 {
1885                         it.team = -1;
1886                         it.surv_state = SURVIVAL_STATE_PLAYING;
1887                 }
1888                 if (it.surv_state == SURVIVAL_STATE_PLAYING)
1889                 {
1890                         TRANSMUTE(Player, it);
1891                         it.surv_state = SURVIVAL_STATE_PLAYING;
1892                         PutClientInServer(it);
1893                 }
1894         });
1895         bot_relinkplayerlist();
1896         return true;
1897 }
1898
1899 /// \brief Hook that is called when player spawns.
1900 MUTATOR_HOOKFUNCTION(surv, PlayerSpawn)
1901 {
1902         entity player = M_ARGV(0, entity);
1903         LOG_TRACE("Survival: PlayerSpawn, player = ", player.netname);
1904         player.surv_state = SURVIVAL_STATE_PLAYING;
1905         Surv_DeterminePlayerModel(player);
1906         if (player.surv_savedplayerstate != NULL)
1907         {
1908                 Surv_RestorePlayerState(player, player.surv_savedplayerstate);
1909                 delete(player.surv_savedplayerstate);
1910                 player.surv_savedplayerstate = NULL;
1911         }
1912         else
1913         {
1914                 PlayerTemplate_PlayerSpawn(player, Surv_GetPlayerTemplate(player));
1915         }
1916         switch (player.team)
1917         {
1918                 case surv_attackerteam:
1919                 {
1920                         if (player.surv_role == SURVIVAL_ROLE_PLAYER)
1921                         {
1922                                 Send_Notification(NOTIF_ONE, player, MSG_CENTER,
1923                                         CENTER_ASSAULT_ATTACKING);
1924                         }
1925                         break;
1926                 }
1927                 case surv_defenderteam:
1928                 {
1929                         Send_Notification(NOTIF_ONE, player, MSG_CENTER,
1930                                 CENTER_ASSAULT_DEFENDING);
1931                         break;
1932                 }
1933         }
1934         //Surv_CountAlivePlayers();
1935         Surv_AddPlayerToAliveList(player, player.team);
1936 }
1937
1938 /// \brief UGLY HACK. This is called every frame to keep player model correct.
1939 MUTATOR_HOOKFUNCTION(surv, FixPlayermodel)
1940 {
1941         entity player = M_ARGV(2, entity);
1942         M_ARGV(0, string) = player.surv_playermodel;
1943 }
1944
1945 /// \brief Hook that is called every frame to determine how player health should
1946 /// regenerate.
1947 MUTATOR_HOOKFUNCTION(surv, PlayerRegen)
1948 {
1949         entity player = M_ARGV(0, entity);
1950         if (player.team == surv_defenderteam)
1951         {
1952                 return true;
1953         }
1954         return PlayerTemplate_PlayerRegen(player, Surv_GetPlayerTemplate(player));
1955 }
1956
1957 /// \brief Hook that is called to determine if balance messages will appear.
1958 MUTATOR_HOOKFUNCTION(surv, HideTeamNagger)
1959 {
1960         return true;
1961 }
1962
1963 /// \brief Hook that is called when player touches an item.
1964 MUTATOR_HOOKFUNCTION(surv, ItemTouch)
1965 {
1966         entity item = M_ARGV(0, entity);
1967         entity player = M_ARGV(1, entity);
1968         switch (player.team)
1969         {
1970                 case surv_attackerteam:
1971                 {
1972                         return PlayerTemplate_ItemTouch(player, item,
1973                                 Surv_GetPlayerTemplate(player));
1974                 }
1975                 case surv_defenderteam:
1976                 {
1977                         switch (item.classname)
1978                         {
1979                                 case "item_strength":
1980                                 {
1981                                         W_GiveWeapon(player, WEP_HMG.m_id);
1982                                         player.superweapons_finished = max(
1983                                                 player.superweapons_finished, time) +
1984                                                 autocvar_g_balance_superweapons_time;
1985                                         Item_ScheduleRespawn(item);
1986                                         sound(player, CH_TRIGGER, SND_Strength, VOL_BASE,
1987                                                 ATTEN_NORM);
1988                                         return MUT_ITEMTOUCH_RETURN;
1989                                 }
1990                                 case "item_invincible":
1991                                 {
1992                                         W_GiveWeapon(player, WEP_RPC.m_id);
1993                                         player.superweapons_finished = max(
1994                                                 player.superweapons_finished, time) +
1995                                                 autocvar_g_balance_superweapons_time;
1996                                         Item_ScheduleRespawn(item);
1997                                         sound(player, CH_TRIGGER, SND_Shield, VOL_BASE, ATTEN_NORM);
1998                                         return MUT_ITEMTOUCH_RETURN;
1999                                 }
2000                                 default:
2001                                 {
2002                                         return PlayerTemplate_ItemTouch(player, item,
2003                                                 Surv_GetPlayerTemplate(player));
2004                                 }
2005                         }
2006                         DebugPrintToChat(player, item.classname);
2007                         return MUT_ITEMTOUCH_RETURN;
2008                 }
2009         }
2010         return MUT_ITEMTOUCH_CONTINUE;
2011 }
2012
2013 /// \brief Hook which is called when the player tries to throw their weapon.
2014 MUTATOR_HOOKFUNCTION(surv, ForbidThrowCurrentWeapon)
2015 {
2016         entity player = M_ARGV(0, entity);
2017         if (player.team == surv_defenderteam)
2018         {
2019                 return true;
2020         }
2021 }
2022
2023 /// \brief Hook which is called when the damage amount must be determined.
2024 MUTATOR_HOOKFUNCTION(surv, Damage_Calculate)
2025 {
2026         entity frag_attacker = M_ARGV(1, entity);
2027         entity frag_target = M_ARGV(2, entity);
2028         float deathtype = M_ARGV(3, float);
2029         float damage = M_ARGV(4, float);
2030         M_ARGV(4, float) = PlayerTemplate_Damage_Calculate(frag_attacker,
2031                 Surv_GetPlayerTemplate(frag_attacker), frag_target,
2032                 Surv_GetPlayerTemplate(frag_target), deathtype, damage);
2033 }
2034
2035 /// \brief Hook which is called when the player was damaged.
2036 MUTATOR_HOOKFUNCTION(surv, PlayerDamaged)
2037 {
2038         entity target = M_ARGV(1, entity);
2039         if (target.team != surv_defenderteam)
2040         {
2041                 return;
2042         }
2043         Surv_UpdateDefenderHealthStat();
2044         entity attacker = M_ARGV(0, entity);
2045         if ((attacker.team == surv_attackerteam) && (attacker.surv_role ==
2046                 SURVIVAL_ROLE_PLAYER))
2047         {
2048                 float health = M_ARGV(2, float);
2049                 float armor = M_ARGV(3, float);
2050                 float score = (health + armor) * autocvar_g_surv_attacker_damage_score;
2051                 PlayerScore_Add(attacker, SP_SCORE, score);
2052         }
2053         if (autocvar_g_surv_stealth)
2054         {
2055                 return;
2056         }
2057         if (target.health < 1)
2058         {
2059                 WaypointSprite_Kill(target.surv_attack_sprite);
2060         }
2061         else
2062         {
2063                 if (autocvar_g_instagib == 1)
2064                 {
2065                         WaypointSprite_UpdateHealth(target.surv_attack_sprite,
2066                                 target.armorvalue + 1);
2067                 }
2068                 else
2069                 {
2070                         WaypointSprite_UpdateHealth(target.surv_attack_sprite,
2071                                 target.health + target.armorvalue);
2072                 }
2073         }
2074 }
2075
2076 /// \brief Hook which is called when the player dies.
2077 MUTATOR_HOOKFUNCTION(surv, PlayerDies)
2078 {
2079         //DebugPrintToChatAll("PlayerDies");
2080         entity attacker = M_ARGV(1, entity);
2081         entity victim = M_ARGV(2, entity);
2082         if ((attacker.team == surv_defenderteam) &&
2083                 (victim.team == surv_attackerteam))
2084         {
2085                 switch (victim.surv_role)
2086                 {
2087                         case SURVIVAL_ROLE_PLAYER:
2088                         {
2089                                 GivePlayerHealth(attacker,
2090                                         autocvar_g_surv_defender_attacker_frag_health);
2091                                 GivePlayerArmor(attacker,
2092                                         autocvar_g_surv_defender_attacker_frag_armor);
2093                                 GivePlayerAmmo(attacker, ammo_shells,
2094                                         autocvar_g_surv_defender_attacker_frag_shells);
2095                                 GivePlayerAmmo(attacker, ammo_nails,
2096                                         autocvar_g_surv_defender_attacker_frag_bullets);
2097                                 GivePlayerAmmo(attacker, ammo_rockets,
2098                                         autocvar_g_surv_defender_attacker_frag_rockets);
2099                                 GivePlayerAmmo(attacker, ammo_cells,
2100                                         autocvar_g_surv_defender_attacker_frag_cells);
2101                                 break;
2102                         }
2103                         case SURVIVAL_ROLE_CANNON_FODDER:
2104                         {
2105                                 GivePlayerHealth(attacker,
2106                                         autocvar_g_surv_defender_cannon_fodder_frag_health);
2107                                 GivePlayerArmor(attacker,
2108                                         autocvar_g_surv_defender_cannon_fodder_frag_armor);
2109                                 GivePlayerAmmo(attacker, ammo_shells,
2110                                         autocvar_g_surv_defender_cannon_fodder_frag_shells);
2111                                 GivePlayerAmmo(attacker, ammo_nails,
2112                                         autocvar_g_surv_defender_cannon_fodder_frag_bullets);
2113                                 GivePlayerAmmo(attacker, ammo_rockets,
2114                                         autocvar_g_surv_defender_cannon_fodder_frag_rockets);
2115                                 GivePlayerAmmo(attacker, ammo_cells,
2116                                         autocvar_g_surv_defender_cannon_fodder_frag_cells);
2117                                 break;
2118                         }
2119                 }
2120         }
2121         if ((victim.team == surv_defenderteam) &&
2122                 (autocvar_g_surv_defender_drop_weapons == false))
2123         {
2124                 for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
2125                 {
2126                         .entity went = weaponentities[slot];
2127                         victim.(went).m_weapon = WEP_Null;
2128                 }
2129         }
2130         if (!Surv_CanPlayerSpawn(victim))
2131         {
2132                 victim.respawn_flags = RESPAWN_SILENT;
2133                 if (IS_BOT_CLIENT(victim))
2134                 {
2135                         bot_clear(victim);
2136                 }
2137         }
2138         return true;
2139 }
2140
2141 /// \brief Hook which is called after the player died.
2142 MUTATOR_HOOKFUNCTION(surv, PlayerDied)
2143 {
2144         //DebugPrintToChatAll("PlayerDied");
2145         entity player = M_ARGV(0, entity);
2146         Surv_RemovePlayerFromAliveList(player, player.team);
2147         //Surv_CountAlivePlayers();
2148 }
2149
2150 /// \brief Hook which is called when player has scored a frag.
2151 MUTATOR_HOOKFUNCTION(surv, GiveFragsForKill, CBC_ORDER_FIRST)
2152 {
2153         if (surv_type == SURVIVAL_TYPE_COOP)
2154         {
2155                 return true;
2156         }
2157         entity attacker = M_ARGV(0, entity);
2158         if ((attacker.team == surv_defenderteam) || (attacker.surv_role ==
2159                 SURVIVAL_ROLE_CANNON_FODDER))
2160         {
2161                 M_ARGV(2, float) = 0;
2162                 return true;
2163         }
2164         entity target = M_ARGV(1, entity);
2165         if ((attacker.surv_role == SURVIVAL_ROLE_PLAYER) && (target.team ==
2166                 surv_defenderteam))
2167         {
2168                 M_ARGV(2, float) = autocvar_g_surv_attacker_frag_score;
2169         }
2170         return true;
2171 }
2172
2173 MUTATOR_HOOKFUNCTION(surv, SpectateSet)
2174 {
2175         entity client = M_ARGV(0, entity);
2176         entity targ = M_ARGV(1, entity);
2177
2178         if (!autocvar_g_surv_spectate_enemies &&
2179                 (client.surv_state == SURVIVAL_STATE_PLAYING) &&
2180                 DIFF_TEAM(targ, client))
2181         {
2182                 return true;
2183         }
2184 }
2185
2186 MUTATOR_HOOKFUNCTION(surv, SpectateNext)
2187 {
2188         entity client = M_ARGV(0, entity);
2189
2190         if (!autocvar_g_surv_spectate_enemies &&
2191                 (client.surv_state == SURVIVAL_STATE_PLAYING))
2192         {
2193                 entity targ = M_ARGV(1, entity);
2194                 M_ARGV(1, entity) = CA_SpectateNext(client, targ);
2195                 return true;
2196         }
2197 }
2198
2199 MUTATOR_HOOKFUNCTION(surv, SpectatePrev)
2200 {
2201         entity client = M_ARGV(0, entity);
2202         entity targ = M_ARGV(1, entity);
2203         entity first = M_ARGV(2, entity);
2204
2205         if (!autocvar_g_surv_spectate_enemies &&
2206                 (client.surv_state == SURVIVAL_STATE_PLAYING))
2207         {
2208                 do
2209                 {
2210                         targ = targ.chain;
2211                 }
2212                 while (targ && DIFF_TEAM(targ, client));
2213                 if (!targ)
2214                 {
2215                         for (targ = first; targ && DIFF_TEAM(targ, client);
2216                                 targ = targ.chain);
2217
2218                         if (targ == client.enemy)
2219                         {
2220                                 return MUT_SPECPREV_RETURN;
2221                         }
2222                 }
2223         }
2224         M_ARGV(1, entity) = targ;
2225         return MUT_SPECPREV_FOUND;
2226 }
2227
2228 /// \brief I'm not sure exactly what this function does but it is very
2229 /// important. Without it bots are completely broken. Is it a hack? Of course.
2230 MUTATOR_HOOKFUNCTION(surv, Bot_FixCount, CBC_ORDER_EXCLUSIVE)
2231 {
2232         FOREACH_CLIENT(IS_REAL_CLIENT(it),
2233         {
2234                 if (IS_PLAYER(it) || (it.surv_state == SURVIVAL_STATE_PLAYING))
2235                 {
2236                         ++M_ARGV(0, int);
2237                 }
2238                 ++M_ARGV(1, int);
2239         });
2240         return true;
2241 }
2242
2243 MUTATOR_HOOKFUNCTION(surv, Scores_CountFragsRemaining)
2244 {
2245         // Don't announce remaining frags
2246         return false;
2247 }