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