1 #include "gamemode_survival.qh"
3 #include <common/mutators/mutator/overkill/hmg.qh>
4 #include <common/mutators/mutator/overkill/rpc.qh>
6 //============================ Constants ======================================
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.
11 /// \brief Used when bitfield team count is requested.
12 const int SURVIVAL_TEAM_BITS = 3;
16 SURVIVAL_TYPE_COOP, ///< All humans are on the defender team.
17 SURVIVAL_TYPE_VERSUS ///< Humans take turns between attackers and defenders.
20 const string SURVIVAL_TYPE_COOP_VALUE = "coop"; ///< Cvar value for coop.
21 const string SURVIVAL_TYPE_VERSUS_VALUE = "versus"; ///< Cvar value for versus.
25 /// \brief First round where there is timelimit set by the server.
27 /// \brief Second round where defender team tries to survive for the first
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
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.
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");
51 SOUND(SURV_RED_SCORES, "ctf/red_capture");
52 SOUND(SURV_BLUE_SCORES, "ctf/blue_capture");
54 //======================= Global variables ====================================
56 float autocvar_g_surv_warmup; ///< Warmup time in seconds.
57 float autocvar_g_surv_round_timelimit; ///< First round time limit in seconds.
59 int autocvar_g_surv_point_limit; ///< Maximum number of points.
60 int autocvar_g_surv_point_leadlimit; ///< Maximum lead of a single team.
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;
67 /// \brief Whether to force overkill player models for attackers.
68 int autocvar_g_surv_attacker_force_overkill_models;
69 /// \brief Whether to force overkill player models for defenders.
70 int autocvar_g_surv_defender_force_overkill_models;
71 /// \brief Whether to force overkill player models for cannon fodder.
72 int autocvar_g_surv_cannon_fodder_force_overkill_models;
74 /// \brief Number of weapons that can be randomly given to attackers during
76 int autocvar_g_surv_attacker_num_random_start_weapons;
78 /// \brief How much health do defenders get during spawn.
79 int autocvar_g_surv_defender_start_health;
80 /// \brief How much armor do defenders get during spawn.
81 int autocvar_g_surv_defender_start_armor;
82 /// \brief How many shells do defenders get during spawn.
83 int autocvar_g_surv_defender_start_ammo_shells;
84 /// \brief How many bullets do defenders get during spawn.
85 int autocvar_g_surv_defender_start_ammo_bullets;
86 /// \brief How many rockets do defenders get during spawn.
87 int autocvar_g_surv_defender_start_ammo_rockets;
88 /// \brief How many cells do defenders get during spawn.
89 int autocvar_g_surv_defender_start_ammo_cells;
90 /// \brief Number of weapons that can be randomly given to defenders during
92 int autocvar_g_surv_defender_num_random_start_weapons;
94 /// \brief How much health does cannon fodder get during spawn.
95 int autocvar_g_surv_cannon_fodder_start_health;
96 /// \brief How much armor does cannon fodder get during spawn.
97 int autocvar_g_surv_cannon_fodder_start_armor;
98 /// \brief Number of weapons that can be randomly given to cannon fodder during
100 int autocvar_g_surv_cannon_fodder_num_random_start_weapons;
102 /// \brief How much health do attackers get when they pickup ammo.
103 int autocvar_g_surv_attacker_pickup_ammo_health;
104 /// \brief How much armor do attackers get when they pickup ammo.
105 int autocvar_g_surv_attacker_pickup_ammo_armor;
106 /// \brief How many health do defenders get when they pickup small health.
107 int autocvar_g_surv_defender_pickup_health_small;
108 /// \brief How many health do defenders get when they pickup medium health.
109 int autocvar_g_surv_defender_pickup_health_medium;
110 /// \brief How many health do defenders get when they pickup big health.
111 int autocvar_g_surv_defender_pickup_health_big;
112 /// \brief How many health do defenders get when they pickup mega health.
113 int autocvar_g_surv_defender_pickup_health_mega;
114 /// \brief How many armor do defenders get when they pickup small armor.
115 int autocvar_g_surv_defender_pickup_armor_small;
116 /// \brief How many armor do defenders get when they pickup medium armor.
117 int autocvar_g_surv_defender_pickup_armor_medium;
118 /// \brief How many armor do defenders get when they pickup big armor.
119 int autocvar_g_surv_defender_pickup_armor_big;
120 /// \brief How many armor do defenders get when they pickup mega armor.
121 int autocvar_g_surv_defender_pickup_armor_mega;
122 /// \brief How many shells do defenders get when they pickup small health/armor.
123 int autocvar_g_surv_defender_pickup_shells_small;
124 /// \brief How many shells do defenders get when they pickup medium
126 int autocvar_g_surv_defender_pickup_shells_medium;
127 /// \brief How many shells do defenders get when they pickup big health/armor.
128 int autocvar_g_surv_defender_pickup_shells_big;
129 /// \brief How many shells do defenders get when they pickup mega health/armor.
130 int autocvar_g_surv_defender_pickup_shells_mega;
131 /// \brief How many bullets do defenders get when they pickup small
133 int autocvar_g_surv_defender_pickup_bullets_small;
134 /// \brief How many bullets do defenders get when they pickup medium
136 int autocvar_g_surv_defender_pickup_bullets_medium;
137 /// \brief How many bullets do defenders get when they pickup big health/armor.
138 int autocvar_g_surv_defender_pickup_bullets_big;
139 /// \brief How many bullets do defenders get when they pickup mega health/armor.
140 int autocvar_g_surv_defender_pickup_bullets_mega;
141 /// \brief How many rockets do defenders get when they pickup small
143 int autocvar_g_surv_defender_pickup_rockets_small;
144 /// \brief How many rockets do defenders get when they pickup medium
146 int autocvar_g_surv_defender_pickup_rockets_medium;
147 /// \brief How many rockets do defenders get when they pickup big health/armor.
148 int autocvar_g_surv_defender_pickup_rockets_big;
149 /// \brief How many rockets do defenders get when they pickup mega health/armor.
150 int autocvar_g_surv_defender_pickup_rockets_mega;
151 /// \brief How many cells do defenders get when they pickup small health/armor.
152 int autocvar_g_surv_defender_pickup_cells_small;
153 /// \brief How many cells do defenders get when they pickup medium health/armor.
154 int autocvar_g_surv_defender_pickup_cells_medium;
155 /// \brief How many cells do defenders get when they pickup big health/armor.
156 int autocvar_g_surv_defender_pickup_cells_big;
157 /// \brief How many cells do defenders get when they pickup mega health/armor.
158 int autocvar_g_surv_defender_pickup_cells_mega;
160 /// \brief How much score attackers gain per 1 point of damage.
161 float autocvar_g_surv_attacker_damage_score;
162 /// \brief How much defenders damage others. Higher values mean more damage.
163 float autocvar_g_surv_defender_attack_scale;
164 /// \brief How much defenders get damaged. High values mean less damage.
165 float autocvar_g_surv_defender_defense_scale;
166 /// \brief How much cannon fodder damages others. Higher values mean more
168 float autocvar_g_surv_cannon_fodder_attack_scale;
169 /// \brief How much cannon fodder gets damaged. Higher values mean less damage.
170 float autocvar_g_surv_cannon_fodder_defense_scale;
172 /// \brief How much score attackers get for fragging defenders.
173 float autocvar_g_surv_attacker_frag_score;
174 /// \brief How much health do defenders get when they frag an attacker.
175 int autocvar_g_surv_defender_attacker_frag_health;
176 /// \brief How much armor do defenders get when they frag an attacker.
177 int autocvar_g_surv_defender_attacker_frag_armor;
178 /// \brief How many shells do defenders get when they frag an attacker.
179 int autocvar_g_surv_defender_attacker_frag_shells;
180 /// \brief How many bullets do defenders get when they frag an attacker.
181 int autocvar_g_surv_defender_attacker_frag_bullets;
182 /// \brief How many rockets do defenders get when they frag an attacker.
183 int autocvar_g_surv_defender_attacker_frag_rockets;
184 /// \brief How many cells do defenders get when they frag an attacker.
185 int autocvar_g_surv_defender_attacker_frag_cells;
186 /// \brief How much health do defenders get when they frag cannon fodder.
187 int autocvar_g_surv_defender_cannon_fodder_frag_health;
188 /// \brief How much armor do defenders get when they frag cannon fodder.
189 int autocvar_g_surv_defender_cannon_fodder_frag_armor;
190 /// \brief How many shells do defenders get when they frag cannon fodder.
191 int autocvar_g_surv_defender_cannon_fodder_frag_shells;
192 /// \brief How many bullets do defenders get when they frag cannon fodder.
193 int autocvar_g_surv_defender_cannon_fodder_frag_bullets;
194 /// \brief How many rockets do defenders get when they frag cannon fodder.
195 int autocvar_g_surv_defender_cannon_fodder_frag_rockets;
196 /// \brief How many cells do defenders get when they frag cannon fodder.
197 int autocvar_g_surv_defender_cannon_fodder_frag_cells;
199 /// \brief Whether defenders drop weapons after death.
200 int autocvar_g_surv_defender_drop_weapons;
202 /// \brief A stat that is used to track the time left in the round.
203 .float surv_round_time_stat = _STAT(SURV_ROUND_TIME);
204 /// \brief A stat that is used to track defender team.
205 .int surv_defender_team_stat = _STAT(SURV_DEFENDER_TEAM);
206 /// \brief A stat that is used to track number of defenders alive.
207 .int surv_defenders_alive_stat = _STAT(SURV_DEFENDERS_ALIVE);
208 /// \brief A stat that is used to track the total health of defenders.
209 .float surv_defender_health_stat = _STAT(SURV_DEFENDER_HEALTH);
211 /// \brief Holds the state of the player. See SURVIVAL_STATE constants.
213 /// \brief Holds the role of the player. See SURVIVAL_ROLE constants.
215 .string surv_savedplayermodel; ///< Initial player model.
216 .string surv_playermodel; ///< Player model forced by the game.
218 .entity surv_attack_sprite; ///< Holds the sprite telling attackers to attack.
220 int surv_type; ///< Holds the type of survival. See SURVIVAL_TYPE constants.
221 /// \brief Holds the type of the current round. See SURVIVAL_ROUND constants.
223 bool surv_isroundactive; ///< Holds whether the round is active.
224 float surv_roundstarttime; ///< Holds the time of the round start.
225 /// \brief Holds the time needed to survive in the second round.
226 float surv_timetobeat;
228 int surv_attackerteam; ///< Holds the attacker team.
229 int surv_defenderteam; ///< Holds the defender team.
231 int surv_attackerteambit; ///< Hols the attacker team bitmask.
232 int surv_defenderteambit; ///< Holds the defender team bitmask.
234 int surv_numattackers; ///< Holds the number of players in attacker team.
235 int surv_numdefenders; ///< Holds the number of players in defender team.
237 /// \brief Holds the number of humans in attacker team.
238 int surv_numattackerhumans;
239 /// \brief Holds the number of humans in defender team.
240 int surv_numdefenderhumans;
242 /// \brief Holds the number of attacker players that are alive.
243 int surv_numattackersalive;
244 /// \brief Holds the number of defender players that are alive.
245 int surv_numdefendersalive;
247 bool surv_autobalance; ///< Holds whether autobalance is active.
248 bool surv_allowed_to_spawn; ///< Holds whether players are allowed to spawn.
250 //====================== Forward declarations =================================
252 /// \brief Determines whether the round can start.
253 /// \return True if the round can start, false otherwise.
254 bool Surv_CanRoundStart();
256 /// \brief Determines whether the round can end.
257 /// \return True if the round can end, false otherwise.
258 bool Surv_CanRoundEnd();
260 /// \brief Called when the round starts.
261 /// \return No return.
262 void Surv_RoundStart();
264 /// \brief Returns whether player has been eliminated.
265 /// \param[in] player Player to check.
266 /// \return True if player is eliminated, false otherwise.
267 bool Surv_IsEliminated(entity player);
269 /// \brief Updates stats of team count on HUD.
270 /// \return No return.
271 void Surv_UpdateTeamStats();
273 /// \brief Updates stats of alive players on HUD.
274 /// \return No return.
275 void Surv_UpdateAliveStats();
277 /// \brief Updates defender health on the HUD.
278 /// \return No return.
279 void Surv_UpdateDefenderHealthStat();
281 //========================= Free functions ====================================
283 void Surv_Initialize()
285 switch (cvar_string("g_surv_type"))
287 case SURVIVAL_TYPE_COOP_VALUE:
289 surv_type = SURVIVAL_TYPE_COOP;
292 case SURVIVAL_TYPE_VERSUS_VALUE:
294 surv_type = SURVIVAL_TYPE_VERSUS;
299 error("Invalid survival type.");
302 surv_roundtype = SURVIVAL_ROUND_FIRST;
303 surv_isroundactive = false;
304 surv_roundstarttime = time;
305 surv_timetobeat = autocvar_g_surv_round_timelimit;
308 surv_attackerteam = NUM_TEAM_1;
309 surv_defenderteam = NUM_TEAM_2;
310 surv_attackerteambit = SURVIVAL_TEAM_1_BIT;
311 surv_defenderteambit = SURVIVAL_TEAM_2_BIT;
315 surv_attackerteam = NUM_TEAM_2;
316 surv_defenderteam = NUM_TEAM_1;
317 surv_attackerteambit = SURVIVAL_TEAM_2_BIT;
318 surv_defenderteambit = SURVIVAL_TEAM_1_BIT;
320 surv_numattackers = 0;
321 surv_numdefenders = 0;
322 surv_numattackerhumans = 0;
323 surv_numdefenderhumans = 0;
324 surv_numattackersalive = 0;
325 surv_numdefendersalive = 0;
326 surv_autobalance = true;
327 surv_allowed_to_spawn = true;
328 precache_all_playermodels("models/ok_player/*.dpm");
329 ScoreRules_basics(SURVIVAL_TEAM_BITS, SFL_SORT_PRIO_PRIMARY, 0, true);
330 ScoreInfo_SetLabel_TeamScore(1, "rounds", SFL_SORT_PRIO_PRIMARY);
331 ScoreRules_basics_end();
332 round_handler_Spawn(Surv_CanRoundStart, Surv_CanRoundEnd, Surv_RoundStart);
333 round_handler_Init(5, autocvar_g_surv_warmup, surv_timetobeat);
334 EliminatedPlayers_Init(Surv_IsEliminated);
336 SetLimits(autocvar_g_surv_point_limit, autocvar_g_surv_point_leadlimit,
337 autocvar_timelimit_override, -1);
340 /// \brief Changes the number of players in a team.
341 /// \param[in] teamnum Team to adjust.
342 /// \param[in] delta Amount to adjust by.
343 /// \return No return.
344 void Surv_ChangeNumberOfPlayers(int teamnum, int delta)
348 case surv_attackerteam:
350 surv_numattackers += delta;
351 LOG_TRACE("Number of attackers = ", ftos(surv_numattackers),
352 " was = ", ftos(surv_numattackers - delta));
353 Surv_UpdateTeamStats();
356 case surv_defenderteam:
358 surv_numdefenders += delta;
359 LOG_TRACE("Number of defenders = ", ftos(surv_numdefenders),
360 " was = ", ftos(surv_numdefenders - delta));
361 Surv_UpdateTeamStats();
367 /// \brief Changes the number of alive players in a team.
368 /// \param[in] teamnum Team to adjust.
369 /// \param[in] delta Amount to adjust by.
370 /// \return No return.
371 void Surv_ChangeNumberOfAlivePlayers(int teamnum, int delta)
375 case surv_attackerteam:
377 surv_numattackersalive += delta;
378 LOG_TRACE("Number of alive attackers = ", ftos(
379 surv_numattackersalive), " was = ", ftos(surv_numattackersalive
383 case surv_defenderteam:
385 surv_numdefendersalive += delta;
386 LOG_TRACE("Number of alive defenders = ", ftos(
387 surv_numdefendersalive), " was = ", ftos(surv_numdefendersalive
392 Surv_UpdateAliveStats();
393 eliminatedPlayers.SendFlags |= 1;
396 /// \brief Sets the player role.
397 /// \param[in,out] player Player to adjust.
398 /// \param[in] role Role to set.
399 /// \return No return.
400 void Surv_SetPlayerRole(entity player, int role)
402 if (player.surv_role == role)
406 player.surv_role = role;
409 case SURVIVAL_ROLE_NONE:
411 LOG_TRACE(player.netname, " now has no role.");
414 case SURVIVAL_ROLE_PLAYER:
416 LOG_TRACE(player.netname, " is now a player.");
419 case SURVIVAL_ROLE_CANNON_FODDER:
421 LOG_TRACE(player.netname, " is now a cannon fodder.");
427 /// \brief Adds player to team. Handles bookkeeping information.
428 /// \param[in] player Player to add.
429 /// \param[in] teamnum Team to add to.
430 /// \return True on success, false otherwise.
431 bool Surv_AddPlayerToTeam(entity player, int teamnum)
433 LOG_TRACE("Survival: AddPlayerToTeam, player: ", player.netname);
436 case surv_attackerteam:
438 LOG_TRACE("Attacker team");
439 if (player.surv_role == SURVIVAL_ROLE_CANNON_FODDER)
441 LOG_TRACE("Cannon fodder is switching team");
444 if (IS_BOT_CLIENT(player))
446 LOG_TRACE("Client is bot");
447 LOG_TRACE("Attackers = ", ftos(surv_numattackers));
448 if (surv_numattackers < autocvar_g_surv_team_size)
450 Surv_SetPlayerRole(player, SURVIVAL_ROLE_PLAYER);
451 Surv_ChangeNumberOfPlayers(teamnum, +1);
454 Surv_SetPlayerRole(player, SURVIVAL_ROLE_CANNON_FODDER);
457 LOG_TRACE("Client is not a bot");
458 LOG_TRACE("Attackers = ", ftos(surv_numattackers));
459 if (surv_numattackers >= autocvar_g_surv_team_size)
461 LOG_TRACE("Removing bot");
462 // Remove bot to make space for human.
463 bool removedbot = false;
464 surv_autobalance = false;
467 if ((it.team == surv_attackerteam) && (it.surv_role ==
468 SURVIVAL_ROLE_PLAYER) && IS_BOT_CLIENT(it))
470 LOG_TRACE("Changing ", it.netname,
471 " from attacker to cannon fodder.");
472 Surv_SetPlayerRole(it, SURVIVAL_ROLE_CANNON_FODDER);
475 Surv_ChangeNumberOfAlivePlayers(teamnum, -1);
477 Surv_ChangeNumberOfPlayers(teamnum, -1);
482 surv_autobalance = true;
485 LOG_TRACE("No valid bot to remove");
486 // No space in team, denying team change.
487 TRANSMUTE(Spectator, player);
490 LOG_TRACE("Removed bot");
492 Surv_SetPlayerRole(player, SURVIVAL_ROLE_PLAYER);
493 Surv_ChangeNumberOfPlayers(teamnum, +1);
494 ++surv_numattackerhumans;
495 LOG_TRACE("Human attackers = ", ftos(surv_numattackerhumans));
498 case surv_defenderteam:
500 LOG_TRACE("Defender team");
501 if (IS_BOT_CLIENT(player))
503 LOG_TRACE("Client is bot");
504 LOG_TRACE("Defenders = ", ftos(surv_numdefenders));
505 if (surv_numdefenders < autocvar_g_surv_team_size)
507 Surv_SetPlayerRole(player, SURVIVAL_ROLE_PLAYER);
508 Surv_ChangeNumberOfPlayers(teamnum, +1);
511 LOG_TRACE("No space for defender, switching to attacker");
512 SetPlayerTeamSimple(player, surv_attackerteam);
515 LOG_TRACE("Client is not a bot");
516 LOG_TRACE("Defenders = ", ftos(surv_numdefenders));
517 if (surv_numdefenders >= autocvar_g_surv_team_size)
519 LOG_TRACE("Removing bot");
520 // Remove bot to make space for human.
521 bool removedbot = false;
522 surv_autobalance = false;
525 if ((it.team == surv_defenderteam) && IS_BOT_CLIENT(it))
527 LOG_TRACE("Changing ", it.netname,
528 " from defender to cannon fodder.");
529 SetPlayerTeamSimple(it, surv_attackerteam);
534 surv_autobalance = true;
537 LOG_TRACE("No valid bot to remove");
538 // No space in team, denying team change.
539 TRANSMUTE(Spectator, player);
542 LOG_TRACE("Removed bot");
544 Surv_SetPlayerRole(player, SURVIVAL_ROLE_PLAYER);
545 Surv_ChangeNumberOfPlayers(teamnum, +1);
546 ++surv_numdefenderhumans;
547 LOG_TRACE("Human defenders = ", ftos(surv_numdefenderhumans));
551 player.surv_role = SURVIVAL_ROLE_NONE;
555 /// \brief Removes player from team. Handles bookkeeping information.
556 /// \param[in] player Player to remove.
557 /// \param[in] Team to remove from.
558 /// \return No return.
559 void Surv_RemovePlayerFromTeam(entity player, int teamnum)
561 LOG_TRACE("Survival: RemovePlayerFromTeam, player: ", player.netname);
564 case surv_attackerteam:
566 LOG_TRACE("Attacker team");
567 if (player.surv_role == SURVIVAL_ROLE_NONE)
569 string message = strcat("RemovePlayerFromTeam: ",
570 player.netname, " has invalid role.");
571 DebugPrintToChatAll(message);
574 if (player.surv_role == SURVIVAL_ROLE_CANNON_FODDER)
576 Surv_SetPlayerRole(player, SURVIVAL_ROLE_NONE);
579 Surv_SetPlayerRole(player, SURVIVAL_ROLE_NONE);
580 Surv_ChangeNumberOfPlayers(teamnum, -1);
581 if (!IS_BOT_CLIENT(player))
583 --surv_numattackerhumans;
585 if ((surv_autobalance == false) || (surv_numattackers >=
590 // Add bot to keep teams balanced.
593 if (it.surv_role == SURVIVAL_ROLE_CANNON_FODDER)
595 LOG_TRACE("Changing ", it.netname,
596 " from cannon fodder to attacker.");
597 Surv_SetPlayerRole(it, SURVIVAL_ROLE_PLAYER);
598 Surv_ChangeNumberOfPlayers(teamnum, +1);
601 Surv_ChangeNumberOfAlivePlayers(teamnum, +1);
608 case surv_defenderteam:
610 LOG_TRACE("Defender team");
611 if (player.surv_role == SURVIVAL_ROLE_CANNON_FODDER)
613 // This happens during team switch. We don't need to change
615 LOG_TRACE("Cannon fodder. Assuming team switch");
618 if (player.surv_role != SURVIVAL_ROLE_PLAYER)
620 string message = strcat("RemovePlayerFromTeam: ",
621 player.netname, " has invalid role.");
622 DebugPrintToChatAll(message);
625 Surv_SetPlayerRole(player, SURVIVAL_ROLE_NONE);
626 Surv_ChangeNumberOfPlayers(teamnum, -1);
627 if (!IS_BOT_CLIENT(player))
629 --surv_numdefenderhumans;
631 if ((surv_autobalance == false) || (surv_numdefenders >=
636 // Add bot to keep teams balanced.
639 if (it.surv_role == SURVIVAL_ROLE_CANNON_FODDER)
641 LOG_TRACE("Changing ", it.netname,
642 " from cannon fodder to defender.");
643 SetPlayerTeamSimple(it, surv_defenderteam);
651 LOG_TRACE("Invalid team");
657 /// \brief Updates stats of team count on HUD.
658 /// \return No return.
659 void Surv_UpdateTeamStats()
662 if (surv_attackerteam == NUM_TEAM_1)
664 yellowalive = surv_numattackers;
665 pinkalive = surv_numdefenders;
669 pinkalive = surv_numattackers;
670 yellowalive = surv_numdefenders;
672 FOREACH_CLIENT(IS_REAL_CLIENT(it),
674 it.yellowalive_stat = yellowalive;
675 it.pinkalive_stat = pinkalive;
679 /// \brief Adds player to alive list. Handles bookkeeping information.
680 /// \param[in] player Player to add.
681 /// \param[in] teamnum Team of the player.
682 /// \return No return.
683 void Surv_AddPlayerToAliveList(entity player, int teamnum)
687 case surv_attackerteam:
689 if (player.surv_role == SURVIVAL_ROLE_PLAYER)
691 Surv_ChangeNumberOfAlivePlayers(teamnum, +1);
695 case surv_defenderteam:
697 Surv_ChangeNumberOfAlivePlayers(teamnum, +1);
703 /// \brief Removes player from alive list. Handles bookkeeping information.
704 /// \param[in] player Player to remove.
705 /// \param[in] teamnum Team of the player.
706 /// \return No return.
707 void Surv_RemovePlayerFromAliveList(entity player, int teamnum)
709 if (player.surv_attack_sprite)
711 WaypointSprite_Kill(player.surv_attack_sprite);
715 case surv_attackerteam:
717 if (player.surv_role == SURVIVAL_ROLE_PLAYER)
719 Surv_ChangeNumberOfAlivePlayers(teamnum, -1);
723 case surv_defenderteam:
725 if (player.surv_role == SURVIVAL_ROLE_CANNON_FODDER)
727 // This happens during team switch. We don't need to change
731 Surv_ChangeNumberOfAlivePlayers(teamnum, -1);
732 if (warmup_stage || surv_allowed_to_spawn)
736 switch (surv_numdefendersalive)
740 sound(NULL, CH_TRIGGER, SND_SURV_1_FRAG_LEFT,
741 VOL_BASE, ATTEN_NONE);
742 FOREACH_CLIENT(IS_PLAYER(it),
744 if (it.team == surv_defenderteam)
746 Send_Notification(NOTIF_ONE, it, MSG_CENTER,
755 sound(NULL, CH_TRIGGER, SND_SURV_2_FRAGS_LEFT,
756 VOL_BASE, ATTEN_NONE);
761 sound(NULL, CH_TRIGGER, SND_SURV_3_FRAGS_LEFT,
762 VOL_BASE, ATTEN_NONE);
771 /// \brief Counts alive players.
772 /// \return No return.
773 /// \note In a perfect world this function shouldn't exist. However, since QC
774 /// code is so bad and spurious mutators can really mess with your code, this
775 /// function is called as a last resort.
776 void Surv_CountAlivePlayers()
778 int savednumdefenders = surv_numdefendersalive;
779 surv_numattackersalive = 0;
780 surv_numdefendersalive = 0;
781 FOREACH_CLIENT(IS_PLAYER(it),
785 case surv_attackerteam:
787 if ((it.surv_role == SURVIVAL_ROLE_PLAYER) && !IS_DEAD(it))
789 ++surv_numattackersalive;
793 case surv_defenderteam:
795 if ((it.surv_role == SURVIVAL_ROLE_PLAYER) && !IS_DEAD(it))
797 ++surv_numdefendersalive;
803 Surv_UpdateAliveStats();
804 eliminatedPlayers.SendFlags |= 1;
805 if (warmup_stage || surv_allowed_to_spawn || (savednumdefenders <=
806 surv_numdefendersalive))
810 switch (surv_numdefendersalive)
814 sound(NULL, CH_TRIGGER, SND_SURV_1_FRAG_LEFT, VOL_BASE, ATTEN_NONE);
815 FOREACH_CLIENT(IS_PLAYER(it),
817 if (it.team == surv_defenderteam)
819 Send_Notification(NOTIF_ONE, it, MSG_CENTER, CENTER_ALONE);
827 sound(NULL, CH_TRIGGER, SND_SURV_2_FRAGS_LEFT, VOL_BASE,
833 sound(NULL, CH_TRIGGER, SND_SURV_3_FRAGS_LEFT, VOL_BASE,
840 /// \brief Updates stats of alive players on HUD.
841 /// \return No return.
842 void Surv_UpdateAliveStats()
845 if (surv_attackerteam == NUM_TEAM_1)
847 redalive = surv_numattackersalive;
848 bluealive = surv_numdefendersalive;
852 bluealive = surv_numattackersalive;
853 redalive = surv_numdefendersalive;
855 FOREACH_CLIENT(IS_REAL_CLIENT(it),
857 it.surv_defenders_alive_stat = surv_numdefendersalive;
858 it.redalive_stat = redalive;
859 it.bluealive_stat = bluealive;
861 Surv_UpdateDefenderHealthStat();
864 /// \brief Updates defender health on the HUD.
865 /// \return No return.
866 void Surv_UpdateDefenderHealthStat()
868 float maxhealth = surv_numdefenders * (
869 autocvar_g_surv_defender_start_health +
870 autocvar_g_surv_defender_start_armor);
871 float totalhealth = 0;
872 FOREACH_CLIENT(IS_PLAYER(it),
874 if (it.team == surv_defenderteam)
876 totalhealth += it.health;
877 totalhealth += it.armorvalue;
887 healthratio = totalhealth / maxhealth;
889 FOREACH_CLIENT(IS_REAL_CLIENT(it),
891 it.surv_defender_health_stat = healthratio;
895 /// \brief Returns whether the player can spawn.
896 /// \param[in] player Player to check.
897 /// \return True if the player can spawn, false otherwise.
898 bool Surv_CanPlayerSpawn(entity player)
900 if (player.team == surv_attackerteam)
904 return surv_allowed_to_spawn;
907 /// \brief Switches the round type.
908 /// \return No return.
909 void Surv_SwitchRoundType()
911 switch (surv_roundtype)
913 case SURVIVAL_ROUND_FIRST:
915 surv_roundtype = SURVIVAL_ROUND_SECOND;
918 case SURVIVAL_ROUND_SECOND:
920 surv_roundtype = SURVIVAL_ROUND_FIRST;
926 /// \brief Cleans up the mess after the round has finished.
927 /// \return No return.
928 void Surv_RoundCleanup()
930 surv_allowed_to_spawn = false;
931 surv_isroundactive = false;
935 if (it.surv_attack_sprite)
937 WaypointSprite_Kill(it.surv_attack_sprite);
940 if (surv_type == SURVIVAL_TYPE_VERSUS)
942 Surv_SwitchRoundType();
943 round_handler_Init(5, autocvar_g_surv_warmup, surv_timetobeat);
946 round_handler_Init(5, autocvar_g_surv_warmup,
947 autocvar_g_surv_round_timelimit);
950 /// \brief Swaps attacker and defender teams.
951 /// \return No return.
952 void Surv_SwapTeams()
954 int temp = surv_attackerteam;
955 surv_attackerteam = surv_defenderteam;
956 surv_defenderteam = temp;
957 temp = surv_attackerteambit;
958 surv_attackerteambit = surv_defenderteambit;
959 surv_defenderteambit = temp;
960 temp = surv_numattackers;
961 surv_numattackers = surv_numdefenders;
962 surv_numdefenders = temp;
963 temp = surv_numattackerhumans;
964 surv_numattackerhumans = surv_numdefenderhumans;
965 surv_numdefenderhumans = temp;
968 if ((it.team == surv_defenderteam) && (it.surv_role ==
969 SURVIVAL_ROLE_CANNON_FODDER))
971 SetPlayerTeamSimple(it, surv_attackerteam);
974 FOREACH_CLIENT(IS_REAL_CLIENT(it),
976 it.surv_defender_team_stat = Team_TeamToNumber(surv_defenderteam);
980 /// \brief Forces the overkill model for specific player.
981 /// \param[in,out] player Player to force the model of.
982 /// \return No return.
983 void Surv_ForceOverkillPlayerModel(entity player)
989 switch (floor(random() * 4))
993 player.surv_playermodel = "models/ok_player/okrobot1.dpm";
998 player.surv_playermodel = "models/ok_player/okrobot2.dpm";
1003 player.surv_playermodel = "models/ok_player/okrobot3.dpm";
1008 player.surv_playermodel = "models/ok_player/okrobot4.dpm";
1016 switch (floor(random() * 4))
1020 player.surv_playermodel = "models/ok_player/okmale1.dpm";
1025 player.surv_playermodel = "models/ok_player/okmale2.dpm";
1030 player.surv_playermodel = "models/ok_player/okmale3.dpm";
1035 player.surv_playermodel = "models/ok_player/okmale4.dpm";
1044 /// \brief Determines the player model to the one configured for the gamemode.
1045 /// \param[in,out] player Player to determine the model of.
1046 /// \return No return.
1047 void Surv_DeterminePlayerModel(entity player)
1049 switch (player.team)
1051 case surv_attackerteam:
1053 switch (player.surv_role)
1055 case SURVIVAL_ROLE_PLAYER:
1057 if (!autocvar_g_surv_attacker_force_overkill_models)
1059 player.surv_playermodel = player.surv_savedplayermodel;
1062 Surv_ForceOverkillPlayerModel(player);
1065 case SURVIVAL_ROLE_CANNON_FODDER:
1067 if (!autocvar_g_surv_cannon_fodder_force_overkill_models)
1069 player.surv_playermodel = player.surv_savedplayermodel;
1072 Surv_ForceOverkillPlayerModel(player);
1077 case surv_defenderteam:
1079 if (!autocvar_g_surv_defender_force_overkill_models)
1081 player.surv_playermodel = player.surv_savedplayermodel;
1084 Surv_ForceOverkillPlayerModel(player);
1090 /// \brief Gives start weapons to the player.
1091 /// \param[in,out] player Player to give weapons to.
1092 /// \return No return.
1093 void Surv_GiveStartWeapons(entity player)
1095 int numrandomweapons = 0;
1096 string randomweaponlist = "";
1097 switch (player.team)
1099 case surv_attackerteam:
1101 FOREACH(Weapons, it != WEP_Null,
1105 player.weapons |= it.m_wepset;
1108 switch (player.surv_role)
1110 case SURVIVAL_ROLE_PLAYER:
1113 autocvar_g_surv_attacker_num_random_start_weapons;
1114 randomweaponlist = "g_surv_attacker_random_start_weapons";
1117 case SURVIVAL_ROLE_CANNON_FODDER:
1120 autocvar_g_surv_cannon_fodder_num_random_start_weapons;
1122 "g_surv_cannon_fodder_random_start_weapons";
1128 case surv_defenderteam:
1130 int numweapons = tokenize_console(cvar_string(
1131 "g_surv_defender_start_weapons"));
1132 for (int i = 0; i < numweapons; ++i)
1134 string weapon = argv(i);
1135 FOREACH(Weapons, it != WEP_Null,
1137 if (it.netname == weapon)
1139 player.weapons |= it.m_wepset;
1145 autocvar_g_surv_defender_num_random_start_weapons;
1146 randomweaponlist = "g_surv_defender_random_start_weapons";
1150 if (numrandomweapons == 0)
1154 int numweapons = tokenize_console(cvar_string(randomweaponlist));
1157 // Give all weapons during warmup stage.
1158 for (int i = 0; i < numweapons; ++i)
1160 string weapon = argv(i);
1161 FOREACH(Weapons, it != WEP_Null,
1163 if (it.netname == weapon)
1165 player.weapons |= it.m_wepset;
1172 for (int i = 0; i < numrandomweapons; ++i)
1174 // Finding weapon which player doesn't have.
1175 WepSet weaponbit = WEPSET(Null);
1176 int numattempts = 0;
1179 string weapon = argv(floor(random() * numweapons));
1180 FOREACH(Weapons, it != WEP_Null,
1182 if (it.netname == weapon)
1184 weaponbit = it.m_wepset;
1190 while ((player.weapons & weaponbit) && (numattempts < 10));
1191 player.weapons |= weaponbit;
1195 //=============================== Callbacks ===================================
1197 bool Surv_CanRoundStart()
1199 return (surv_numattackersalive > 0) && (surv_numdefendersalive > 0);
1202 bool Surv_CanRoundEnd()
1208 if((round_handler_GetEndTime() > 0) && (round_handler_GetEndTime() -
1211 if (surv_roundtype == SURVIVAL_ROUND_FIRST)
1213 surv_timetobeat = time - surv_roundstarttime;
1214 Send_Notification(NOTIF_ALL, NULL, MSG_CENTER,
1215 CENTER_SURVIVAL_DEFENDERS_SURVIVED);
1216 Send_Notification(NOTIF_ALL, NULL, MSG_INFO,
1217 INFO_SURVIVAL_DEFENDERS_SURVIVED);
1218 Surv_RoundCleanup();
1221 surv_timetobeat = autocvar_g_surv_round_timelimit;
1222 Send_Notification(NOTIF_ALL, NULL, MSG_CENTER,
1223 CENTER_SURVIVAL_DEFENDERS_SURVIVED);
1224 Send_Notification(NOTIF_ALL, NULL, MSG_INFO,
1225 INFO_SURVIVAL_DEFENDERS_SURVIVED);
1226 switch (surv_defenderteam)
1230 sound(NULL, CH_TRIGGER, SND_SURV_RED_SCORES, VOL_BASE,
1236 sound(NULL, CH_TRIGGER, SND_SURV_BLUE_SCORES, VOL_BASE,
1241 TeamScore_AddToTeam(surv_defenderteam, 1, 1);
1242 Surv_RoundCleanup();
1245 if (surv_numdefendersalive > 0)
1249 if (surv_roundtype == SURVIVAL_ROUND_FIRST)
1251 surv_timetobeat = time - surv_roundstarttime;
1252 Send_Notification(NOTIF_ALL, NULL, MSG_CENTER,
1253 CENTER_SURVIVAL_DEFENDERS_ELIMINATED_IN, surv_timetobeat);
1254 Send_Notification(NOTIF_ALL, NULL, MSG_INFO,
1255 INFO_SURVIVAL_DEFENDERS_ELIMINATED_IN, surv_timetobeat);
1256 Surv_RoundCleanup();
1259 surv_timetobeat = autocvar_g_surv_round_timelimit;
1260 Send_Notification(NOTIF_ALL, NULL, MSG_CENTER,
1261 CENTER_SURVIVAL_DEFENDERS_ELIMINATED);
1262 Send_Notification(NOTIF_ALL, NULL, MSG_INFO,
1263 INFO_SURVIVAL_DEFENDERS_ELIMINATED);
1264 switch (surv_attackerteam)
1268 sound(NULL, CH_TRIGGER, SND_SURV_RED_SCORES, VOL_BASE,
1274 sound(NULL, CH_TRIGGER, SND_SURV_BLUE_SCORES, VOL_BASE,
1279 TeamScore_AddToTeam(surv_attackerteam, 1, 1);
1280 Surv_RoundCleanup();
1284 void Surv_RoundStart()
1288 surv_allowed_to_spawn = true;
1291 surv_isroundactive = true;
1292 surv_roundstarttime = time;
1293 surv_allowed_to_spawn = false;
1294 switch (surv_roundtype)
1296 case SURVIVAL_ROUND_FIRST:
1298 FOREACH_CLIENT(IS_PLAYER(it),
1300 if (it.team == surv_attackerteam)
1302 Send_Notification(NOTIF_TEAM, it, MSG_CENTER,
1303 CENTER_SURVIVAL_1ST_ROUND_ATTACKER);
1304 Send_Notification(NOTIF_TEAM, it, MSG_INFO,
1305 INFO_SURVIVAL_1ST_ROUND_ATTACKER);
1309 FOREACH_CLIENT(IS_PLAYER(it),
1311 if (it.team == surv_defenderteam)
1313 if (surv_type == SURVIVAL_TYPE_COOP)
1315 Send_Notification(NOTIF_TEAM, it, MSG_CENTER,
1316 CENTER_SURVIVAL_COOP_DEFENDER);
1317 Send_Notification(NOTIF_TEAM, it, MSG_INFO,
1318 INFO_SURVIVAL_COOP_DEFENDER);
1321 Send_Notification(NOTIF_TEAM, it, MSG_CENTER,
1322 CENTER_SURVIVAL_1ST_ROUND_DEFENDER);
1323 Send_Notification(NOTIF_TEAM, it, MSG_INFO,
1324 INFO_SURVIVAL_1ST_ROUND_DEFENDER);
1330 case SURVIVAL_ROUND_SECOND:
1332 FOREACH_CLIENT(IS_PLAYER(it),
1334 if (it.team == surv_attackerteam)
1336 Send_Notification(NOTIF_TEAM, it, MSG_CENTER,
1337 CENTER_SURVIVAL_2ND_ROUND_ATTACKER, surv_timetobeat);
1338 Send_Notification(NOTIF_TEAM, it, MSG_INFO,
1339 INFO_SURVIVAL_2ND_ROUND_ATTACKER, surv_timetobeat);
1343 FOREACH_CLIENT(IS_PLAYER(it),
1345 if (it.team == surv_defenderteam)
1347 Send_Notification(NOTIF_TEAM, it, MSG_CENTER,
1348 CENTER_SURVIVAL_2ND_ROUND_DEFENDER, surv_timetobeat);
1349 Send_Notification(NOTIF_TEAM, it, MSG_INFO,
1350 INFO_SURVIVAL_2ND_ROUND_DEFENDER, surv_timetobeat);
1357 if (autocvar_g_surv_stealth)
1361 FOREACH_CLIENT(IS_PLAYER(it),
1365 case surv_defenderteam:
1367 if (it.surv_role == SURVIVAL_ROLE_PLAYER)
1369 WaypointSprite_Spawn(WP_AssaultDestroy, 0, 0, it, '0 0 64',
1370 NULL, surv_attackerteam, it, surv_attack_sprite, false,
1371 RADARICON_OBJECTIVE);
1372 WaypointSprite_UpdateMaxHealth(it.surv_attack_sprite,
1373 autocvar_g_surv_defender_start_health +
1374 autocvar_g_surv_defender_start_armor);
1375 WaypointSprite_UpdateHealth(it.surv_attack_sprite,
1376 it.health + it.armorvalue);
1384 bool Surv_IsEliminated(entity player)
1386 switch (player.surv_state)
1388 case SURVIVAL_STATE_NOT_PLAYING:
1392 case SURVIVAL_STATE_PLAYING:
1394 if (player.surv_role == SURVIVAL_ROLE_CANNON_FODDER)
1396 // A hack until proper scoreboard is done.
1399 if ((player.team == surv_defenderteam) && (IS_DEAD(player) ||
1400 IS_OBSERVER(player)))
1407 // Should never reach here
1411 //============================= Hooks ========================================
1413 /// \brief Hook that is called to determine if there is a weapon arena.
1414 MUTATOR_HOOKFUNCTION(surv, SetWeaponArena)
1416 // Removing any weapon arena.
1417 M_ARGV(0, string) = "off";
1420 /// \brief Hook that is called to determine start items of all players.
1421 MUTATOR_HOOKFUNCTION(surv, SetStartItems)
1423 start_weapons = WEPSET(Null);
1424 warmup_start_weapons = WEPSET(Null);
1427 MUTATOR_HOOKFUNCTION(surv, SV_StartFrame)
1429 if (game_stopped || !surv_isroundactive)
1433 float roundtime = 0;
1434 switch (surv_roundtype)
1436 case SURVIVAL_ROUND_FIRST:
1438 roundtime = time - surv_roundstarttime;
1441 case SURVIVAL_ROUND_SECOND:
1443 roundtime = round_handler_GetEndTime() - time;
1447 FOREACH_CLIENT(IS_REAL_CLIENT(it),
1449 it.surv_round_time_stat = roundtime;
1453 /// \brief Hook that determines which team player can join. This is called
1454 /// before ClientConnect.
1455 MUTATOR_HOOKFUNCTION(surv, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE)
1457 entity player = M_ARGV(2, entity);
1458 LOG_TRACE("Survival: CheckAllowedTeams, player = ", player.netname);
1459 if (!IS_BOT_CLIENT(player))
1461 if (surv_type == SURVIVAL_TYPE_COOP)
1463 if (surv_numdefenderhumans < autocvar_g_surv_team_size)
1465 M_ARGV(0, float) = surv_defenderteambit;
1468 M_ARGV(0, float) = 0;
1472 if (surv_numattackerhumans < autocvar_g_surv_team_size)
1474 teambits |= surv_attackerteambit;
1476 if (surv_allowed_to_spawn && (surv_numdefenderhumans <
1477 autocvar_g_surv_team_size))
1479 teambits |= surv_defenderteambit;
1481 M_ARGV(0, float) = teambits;
1484 int teambits = surv_attackerteambit;
1485 if ((player.team == surv_defenderteam) || (surv_numdefenders <
1486 autocvar_g_surv_team_size))
1488 teambits |= surv_defenderteambit;
1490 M_ARGV(0, float) = teambits;
1493 /// \brief Hook that determines the best team for the player to join.
1494 MUTATOR_HOOKFUNCTION(surv, JoinBestTeam, CBC_ORDER_EXCLUSIVE)
1496 if (surv_type == SURVIVAL_TYPE_COOP)
1500 entity player = M_ARGV(0, entity);
1501 if (IS_BOT_CLIENT(player))
1505 int numattackerhumans = surv_numattackerhumans;
1506 int numdefenderhumans = surv_numdefenderhumans;
1507 if (player.team == surv_attackerteam)
1509 --numattackerhumans;
1511 else if (player.team == surv_defenderteam)
1513 --numdefenderhumans;
1515 if (numattackerhumans < numdefenderhumans)
1517 M_ARGV(1, float) = Team_TeamToNumber(surv_attackerteam);
1520 if (numattackerhumans > numdefenderhumans)
1522 M_ARGV(1, float) = Team_TeamToNumber(surv_defenderteam);
1525 M_ARGV(1, float) = floor(random() * 2) + 1;
1528 /// \brief Hook that is called when player has changed the team.
1529 MUTATOR_HOOKFUNCTION(surv, Player_ChangedTeam)
1531 entity player = M_ARGV(0, entity);
1532 int oldteam = M_ARGV(1, float);
1533 int newteam = M_ARGV(2, float);
1534 string message = strcat("Survival: Player_ChangedTeam, ", player.netname,
1535 ", old team = ", ftos(oldteam), " new team = ", ftos(newteam));
1537 DebugPrintToChatAll(message);
1538 if ((oldteam != -1) && IS_PLAYER(player) && !IS_DEAD(player))
1540 Surv_RemovePlayerFromAliveList(player, oldteam);
1542 Surv_RemovePlayerFromTeam(player, oldteam);
1543 if (Surv_AddPlayerToTeam(player, newteam) == false)
1547 //Surv_CountAlivePlayers();
1548 if ((oldteam != -1) && IS_PLAYER(player) && !IS_DEAD(player))
1550 Surv_AddPlayerToAliveList(player, newteam);
1554 /// \brief Hook that is called when player connects to the server.
1555 MUTATOR_HOOKFUNCTION(surv, ClientConnect)
1557 entity player = M_ARGV(0, entity);
1558 LOG_TRACE("Survival: ClientConnect, player = ", player.netname);
1559 player.surv_savedplayermodel = player.playermodel;
1560 if (IS_REAL_CLIENT(player))
1562 player.surv_defender_team_stat = Team_TeamToNumber(surv_defenderteam);
1563 player.surv_defenders_alive_stat = surv_numdefendersalive;
1564 player.redalive_stat = redalive;
1565 player.bluealive_stat = bluealive;
1566 player.yellowalive_stat = yellowalive;
1567 player.pinkalive_stat = pinkalive;
1569 if (player.surv_role == SURVIVAL_ROLE_NONE)
1571 Surv_AddPlayerToTeam(player, player.team);
1576 /// \brief Hook that is called when player disconnects from the server.
1577 MUTATOR_HOOKFUNCTION(surv, ClientDisconnect)
1579 entity player = M_ARGV(0, entity);
1580 if (!IS_DEAD(player))
1582 Surv_RemovePlayerFromAliveList(player, player.team);
1584 Surv_RemovePlayerFromTeam(player, player.team);
1585 //Surv_CountAlivePlayers();
1588 MUTATOR_HOOKFUNCTION(surv, PutClientInServer)
1590 entity player = M_ARGV(0, entity);
1591 LOG_TRACE("Survival: PutClientInServer, player = ", player.netname);
1592 if (!Surv_CanPlayerSpawn(player) && IS_PLAYER(player))
1594 TRANSMUTE(Observer, player);
1598 MUTATOR_HOOKFUNCTION(surv, MakePlayerObserver)
1600 entity player = M_ARGV(0, entity);
1601 LOG_TRACE("Survival: MakePlayerObserver, player = ", player.netname);
1602 if (player.killindicator_teamchange == -2) // player wants to spectate
1604 LOG_TRACE("killindicator_teamchange == -2");
1605 player.surv_state = SURVIVAL_STATE_NOT_PLAYING;
1607 if (player.surv_state == SURVIVAL_STATE_NOT_PLAYING)
1609 return false; // allow team reset
1611 return true; // prevent team reset
1614 /// \brief Hook that determines whether player can spawn. It is not called for
1615 /// players who have joined the team and are dead.
1616 MUTATOR_HOOKFUNCTION(surv, ForbidSpawn)
1618 entity player = M_ARGV(0, entity);
1619 LOG_TRACE("Survival: ForbidSpawn, player = ", player.netname);
1620 if (player.surv_state == SURVIVAL_STATE_NOT_PLAYING)
1624 return !Surv_CanPlayerSpawn(player);
1627 MUTATOR_HOOKFUNCTION(surv, reset_map_global)
1629 LOG_TRACE("Survival: reset_map_global");
1630 surv_allowed_to_spawn = true;
1631 surv_numattackersalive = 0;
1632 surv_numdefendersalive = 0;
1633 if (surv_roundtype == SURVIVAL_ROUND_FIRST)
1635 FOREACH_CLIENT(IS_REAL_CLIENT(it),
1637 it.surv_round_time_stat = 0;
1643 MUTATOR_HOOKFUNCTION(surv, reset_map_players)
1645 LOG_TRACE("Survival: reset_map_players");
1646 if (surv_type == SURVIVAL_TYPE_VERSUS)
1650 FOREACH_CLIENT(true,
1653 if ((it.surv_state == SURVIVAL_STATE_NOT_PLAYING) && IS_BOT_CLIENT(it))
1656 it.surv_state = SURVIVAL_STATE_PLAYING;
1658 if (it.surv_state != SURVIVAL_STATE_NOT_PLAYING)
1660 TRANSMUTE(Player, it);
1661 it.surv_state = SURVIVAL_STATE_PLAYING;
1662 PutClientInServer(it);
1665 bot_relinkplayerlist();
1669 /// \brief Hook that is called when player spawns.
1670 MUTATOR_HOOKFUNCTION(surv, PlayerSpawn)
1672 entity player = M_ARGV(0, entity);
1673 LOG_TRACE("Survival: PlayerSpawn, player = ", player.netname);
1674 player.surv_state = SURVIVAL_STATE_PLAYING;
1675 //Surv_CountAlivePlayers();
1676 Surv_AddPlayerToAliveList(player, player.team);
1677 Surv_DeterminePlayerModel(player);
1678 Surv_GiveStartWeapons(player);
1679 switch (player.team)
1681 case surv_attackerteam:
1683 switch (player.surv_role)
1685 case SURVIVAL_ROLE_PLAYER:
1687 player.items |= IT_UNLIMITED_AMMO;
1688 Send_Notification(NOTIF_ONE, player, MSG_CENTER,
1689 CENTER_ASSAULT_ATTACKING);
1692 case SURVIVAL_ROLE_CANNON_FODDER:
1694 player.health = autocvar_g_surv_cannon_fodder_start_health;
1696 autocvar_g_surv_cannon_fodder_start_armor;
1697 player.items |= IT_UNLIMITED_AMMO;
1702 LOG_TRACE("Survival: PlayerSpawn: Invalid attacker role.");
1708 case surv_defenderteam:
1710 if (player.surv_role != SURVIVAL_ROLE_PLAYER)
1712 LOG_TRACE("Survival: PlayerSpawn: ", player.netname,
1713 " has invalid defender role.");
1715 player.health = autocvar_g_surv_defender_start_health;
1716 player.armorvalue = autocvar_g_surv_defender_start_armor;
1717 player.ammo_shells = autocvar_g_surv_defender_start_ammo_shells;
1718 player.ammo_nails = autocvar_g_surv_defender_start_ammo_bullets;
1719 player.ammo_rockets = autocvar_g_surv_defender_start_ammo_rockets;
1720 player.ammo_cells = autocvar_g_surv_defender_start_ammo_cells;
1721 Send_Notification(NOTIF_ONE, player, MSG_CENTER,
1722 CENTER_ASSAULT_DEFENDING);
1728 /// \brief UGLY HACK. This is called every frame to keep player model correct.
1729 MUTATOR_HOOKFUNCTION(surv, FixPlayermodel)
1731 entity player = M_ARGV(2, entity);
1732 M_ARGV(0, string) = player.surv_playermodel;
1735 /// \brief Hook that is called every frame to determine how player health should
1737 MUTATOR_HOOKFUNCTION(surv, PlayerRegen)
1739 entity player = M_ARGV(0, entity);
1740 if (player.team == surv_defenderteam)
1747 /// \brief Hook that is called to determine if balance messages will appear.
1748 MUTATOR_HOOKFUNCTION(surv, HideTeamNagger)
1753 /// \brief Hook that is called when player touches an item.
1754 MUTATOR_HOOKFUNCTION(surv, ItemTouch)
1756 entity item = M_ARGV(0, entity);
1757 entity player = M_ARGV(1, entity);
1758 switch (player.team)
1760 case surv_attackerteam:
1762 switch (item.classname)
1765 case "item_bullets":
1766 case "item_rockets":
1768 case "droppedweapon":
1770 GivePlayerHealth(player,
1771 autocvar_g_surv_attacker_pickup_ammo_health);
1772 GivePlayerArmor(player,
1773 autocvar_g_surv_attacker_pickup_ammo_armor);
1774 return MUT_ITEMTOUCH_CONTINUE;
1777 return MUT_ITEMTOUCH_CONTINUE;
1779 case surv_defenderteam:
1781 switch (item.classname)
1783 case "item_health_small":
1785 GivePlayerHealth(player,
1786 autocvar_g_surv_defender_pickup_health_small);
1787 GivePlayerAmmo(player, ammo_shells,
1788 autocvar_g_surv_defender_pickup_shells_small);
1789 GivePlayerAmmo(player, ammo_nails,
1790 autocvar_g_surv_defender_pickup_bullets_small);
1791 GivePlayerAmmo(player, ammo_rockets,
1792 autocvar_g_surv_defender_pickup_rockets_small);
1793 GivePlayerAmmo(player, ammo_cells,
1794 autocvar_g_surv_defender_pickup_cells_small);
1795 Item_ScheduleRespawn(item);
1796 sound(player, CH_TRIGGER, SND_HealthSmall, VOL_BASE,
1798 return MUT_ITEMTOUCH_RETURN;
1800 case "item_health_medium":
1802 GivePlayerHealth(player,
1803 autocvar_g_surv_defender_pickup_health_medium);
1804 GivePlayerAmmo(player, ammo_shells,
1805 autocvar_g_surv_defender_pickup_shells_medium);
1806 GivePlayerAmmo(player, ammo_nails,
1807 autocvar_g_surv_defender_pickup_bullets_medium);
1808 GivePlayerAmmo(player, ammo_rockets,
1809 autocvar_g_surv_defender_pickup_rockets_medium);
1810 GivePlayerAmmo(player, ammo_cells,
1811 autocvar_g_surv_defender_pickup_cells_medium);
1812 Item_ScheduleRespawn(item);
1813 sound(player, CH_TRIGGER, SND_HealthMedium, VOL_BASE,
1815 return MUT_ITEMTOUCH_RETURN;
1817 case "item_health_big":
1819 GivePlayerHealth(player,
1820 autocvar_g_surv_defender_pickup_health_big);
1821 GivePlayerAmmo(player, ammo_shells,
1822 autocvar_g_surv_defender_pickup_shells_big);
1823 GivePlayerAmmo(player, ammo_nails,
1824 autocvar_g_surv_defender_pickup_bullets_big);
1825 GivePlayerAmmo(player, ammo_rockets,
1826 autocvar_g_surv_defender_pickup_rockets_big);
1827 GivePlayerAmmo(player, ammo_cells,
1828 autocvar_g_surv_defender_pickup_cells_big);
1829 Item_ScheduleRespawn(item);
1830 sound(player, CH_TRIGGER, SND_HealthBig, VOL_BASE,
1832 return MUT_ITEMTOUCH_RETURN;
1834 case "item_health_mega":
1836 GivePlayerHealth(player,
1837 autocvar_g_surv_defender_pickup_health_mega);
1838 GivePlayerAmmo(player, ammo_shells,
1839 autocvar_g_surv_defender_pickup_shells_mega);
1840 GivePlayerAmmo(player, ammo_nails,
1841 autocvar_g_surv_defender_pickup_bullets_mega);
1842 GivePlayerAmmo(player, ammo_rockets,
1843 autocvar_g_surv_defender_pickup_rockets_mega);
1844 GivePlayerAmmo(player, ammo_cells,
1845 autocvar_g_surv_defender_pickup_cells_mega);
1846 Item_ScheduleRespawn(item);
1847 sound(player, CH_TRIGGER, SND_HealthMega, VOL_BASE, ATTEN_NORM);
1848 return MUT_ITEMTOUCH_RETURN;
1850 case "item_armor_small":
1852 GivePlayerArmor(player,
1853 autocvar_g_surv_defender_pickup_armor_small);
1854 GivePlayerAmmo(player, ammo_shells,
1855 autocvar_g_surv_defender_pickup_shells_small);
1856 GivePlayerAmmo(player, ammo_nails,
1857 autocvar_g_surv_defender_pickup_bullets_small);
1858 GivePlayerAmmo(player, ammo_rockets,
1859 autocvar_g_surv_defender_pickup_rockets_small);
1860 GivePlayerAmmo(player, ammo_cells,
1861 autocvar_g_surv_defender_pickup_cells_small);
1862 Item_ScheduleRespawn(item);
1863 sound(player, CH_TRIGGER, SND_HealthSmall, VOL_BASE,
1865 return MUT_ITEMTOUCH_RETURN;
1867 case "item_armor_medium":
1869 GivePlayerArmor(player,
1870 autocvar_g_surv_defender_pickup_armor_medium);
1871 GivePlayerAmmo(player, ammo_shells,
1872 autocvar_g_surv_defender_pickup_shells_medium);
1873 GivePlayerAmmo(player, ammo_nails,
1874 autocvar_g_surv_defender_pickup_bullets_medium);
1875 GivePlayerAmmo(player, ammo_rockets,
1876 autocvar_g_surv_defender_pickup_rockets_medium);
1877 GivePlayerAmmo(player, ammo_cells,
1878 autocvar_g_surv_defender_pickup_cells_medium);
1879 Item_ScheduleRespawn(item);
1880 sound(player, CH_TRIGGER, SND_HealthMedium, VOL_BASE,
1882 return MUT_ITEMTOUCH_RETURN;
1884 case "item_armor_big":
1886 GivePlayerArmor(player,
1887 autocvar_g_surv_defender_pickup_armor_big);
1888 GivePlayerAmmo(player, ammo_shells,
1889 autocvar_g_surv_defender_pickup_shells_big);
1890 GivePlayerAmmo(player, ammo_nails,
1891 autocvar_g_surv_defender_pickup_bullets_big);
1892 GivePlayerAmmo(player, ammo_rockets,
1893 autocvar_g_surv_defender_pickup_rockets_big);
1894 GivePlayerAmmo(player, ammo_cells,
1895 autocvar_g_surv_defender_pickup_cells_big);
1896 Item_ScheduleRespawn(item);
1897 sound(player, CH_TRIGGER, SND_HealthBig, VOL_BASE, ATTEN_NORM);
1898 return MUT_ITEMTOUCH_RETURN;
1900 case "item_armor_mega":
1902 GivePlayerArmor(player,
1903 autocvar_g_surv_defender_pickup_armor_mega);
1904 GivePlayerAmmo(player, ammo_shells,
1905 autocvar_g_surv_defender_pickup_shells_mega);
1906 GivePlayerAmmo(player, ammo_nails,
1907 autocvar_g_surv_defender_pickup_bullets_mega);
1908 GivePlayerAmmo(player, ammo_rockets,
1909 autocvar_g_surv_defender_pickup_rockets_mega);
1910 GivePlayerAmmo(player, ammo_cells,
1911 autocvar_g_surv_defender_pickup_cells_mega);
1912 Item_ScheduleRespawn(item);
1913 sound(player, CH_TRIGGER, SND_HealthMega, VOL_BASE, ATTEN_NORM);
1914 return MUT_ITEMTOUCH_RETURN;
1917 case "item_bullets":
1918 case "item_rockets":
1921 return MUT_ITEMTOUCH_CONTINUE;
1923 case "droppedweapon":
1925 switch (item.weapon)
1927 case WEP_SHOTGUN.m_id:
1929 GivePlayerAmmo(player, ammo_shells, cvar(
1930 "g_pickup_shells_weapon"));
1933 case WEP_MACHINEGUN.m_id:
1935 GivePlayerAmmo(player, ammo_nails, cvar(
1936 "g_pickup_nails_weapon"));
1939 case WEP_MORTAR.m_id:
1940 case WEP_HAGAR.m_id:
1941 case WEP_DEVASTATOR.m_id:
1943 GivePlayerAmmo(player, ammo_rockets, cvar(
1944 "g_pickup_rockets_weapon"));
1947 case WEP_ELECTRO.m_id:
1948 case WEP_CRYLINK.m_id:
1949 case WEP_VORTEX.m_id:
1951 GivePlayerAmmo(player, ammo_cells, cvar(
1952 "g_pickup_cells_weapon"));
1957 sound(player, CH_TRIGGER, SND_WEAPONPICKUP, VOL_BASE,
1959 return MUT_ITEMTOUCH_RETURN;
1961 case "replacedweapon":
1963 switch (item.weapon)
1965 case WEP_SHOTGUN.m_id:
1967 GivePlayerAmmo(player, ammo_shells, cvar(
1968 "g_pickup_shells_weapon"));
1971 case WEP_RIFLE.m_id:
1973 GivePlayerAmmo(player, ammo_nails, cvar(
1974 "g_pickup_nails_weapon"));
1977 case WEP_MINE_LAYER.m_id:
1978 case WEP_SEEKER.m_id:
1980 GivePlayerAmmo(player, ammo_rockets, cvar(
1981 "g_pickup_rockets_weapon"));
1986 GivePlayerAmmo(player, ammo_cells, cvar(
1987 "g_pickup_cells_weapon"));
1991 Item_ScheduleRespawn(item);
1992 sound(player, CH_TRIGGER, SND_WEAPONPICKUP, VOL_BASE,
1994 return MUT_ITEMTOUCH_RETURN;
1996 case "weapon_machinegun":
1999 GivePlayerAmmo(player, ammo_nails,
2000 cvar("g_pickup_nails_weapon"));
2001 Item_ScheduleRespawn(item);
2002 sound(player, CH_TRIGGER, SND_WEAPONPICKUP, VOL_BASE,
2004 return MUT_ITEMTOUCH_RETURN;
2006 case "weapon_grenadelauncher":
2007 case "weapon_hagar":
2008 case "weapon_rocketlauncher":
2010 GivePlayerAmmo(player, ammo_rockets,
2011 cvar("g_pickup_rockets_weapon"));
2012 Item_ScheduleRespawn(item);
2013 sound(player, CH_TRIGGER, SND_WEAPONPICKUP, VOL_BASE,
2015 return MUT_ITEMTOUCH_RETURN;
2017 case "weapon_electro":
2018 case "weapon_crylink":
2021 GivePlayerAmmo(player, ammo_cells,
2022 cvar("g_pickup_cells_weapon"));
2023 Item_ScheduleRespawn(item);
2024 sound(player, CH_TRIGGER, SND_WEAPONPICKUP, VOL_BASE,
2026 return MUT_ITEMTOUCH_RETURN;
2028 case "item_strength":
2030 W_GiveWeapon(player, WEP_HMG.m_id);
2031 player.superweapons_finished = max(
2032 player.superweapons_finished, time) +
2033 autocvar_g_balance_superweapons_time;
2034 Item_ScheduleRespawn(item);
2035 sound(player, CH_TRIGGER, SND_Strength, VOL_BASE,
2037 return MUT_ITEMTOUCH_RETURN;
2039 case "item_invincible":
2041 W_GiveWeapon(player, WEP_RPC.m_id);
2042 player.superweapons_finished = max(
2043 player.superweapons_finished, time) +
2044 autocvar_g_balance_superweapons_time;
2045 Item_ScheduleRespawn(item);
2046 sound(player, CH_TRIGGER, SND_Shield, VOL_BASE, ATTEN_NORM);
2047 return MUT_ITEMTOUCH_RETURN;
2050 DebugPrintToChat(player, item.classname);
2051 return MUT_ITEMTOUCH_RETURN;
2054 return MUT_ITEMTOUCH_CONTINUE;
2057 /// \brief Hook which is called when the player tries to throw their weapon.
2058 MUTATOR_HOOKFUNCTION(surv, ForbidThrowCurrentWeapon)
2060 entity player = M_ARGV(0, entity);
2061 if (player.team == surv_defenderteam)
2067 /// \brief Hook which is called when the damage amount must be determined.
2068 MUTATOR_HOOKFUNCTION(surv, Damage_Calculate)
2070 entity frag_attacker = M_ARGV(1, entity);
2071 entity frag_target = M_ARGV(2, entity);
2072 float damage = M_ARGV(4, float);
2073 switch (frag_attacker.team)
2075 case surv_attackerteam:
2077 switch (frag_attacker.surv_role)
2079 case SURVIVAL_ROLE_CANNON_FODDER:
2081 damage *= autocvar_g_surv_cannon_fodder_attack_scale;
2087 case surv_defenderteam:
2089 damage *= autocvar_g_surv_defender_attack_scale;
2093 switch (frag_target.team)
2095 case surv_attackerteam:
2097 switch (frag_target.surv_role)
2099 case SURVIVAL_ROLE_CANNON_FODDER:
2101 damage /= autocvar_g_surv_cannon_fodder_defense_scale;
2107 case surv_defenderteam:
2109 damage /= autocvar_g_surv_defender_defense_scale;
2113 M_ARGV(4, float) = damage;
2116 /// \brief Hook which is called when the player was damaged.
2117 MUTATOR_HOOKFUNCTION(surv, PlayerDamaged)
2119 entity target = M_ARGV(1, entity);
2120 if (target.team != surv_defenderteam)
2124 Surv_UpdateDefenderHealthStat();
2125 entity attacker = M_ARGV(0, entity);
2126 if ((attacker.team == surv_attackerteam) && (attacker.surv_role ==
2127 SURVIVAL_ROLE_PLAYER))
2129 float health = M_ARGV(2, float);
2130 float armor = M_ARGV(3, float);
2131 float score = (health + armor) * autocvar_g_surv_attacker_damage_score;
2132 PlayerScore_Add(attacker, SP_SCORE, score);
2134 if (autocvar_g_surv_stealth)
2138 if (target.health < 1)
2140 WaypointSprite_Kill(target.surv_attack_sprite);
2144 WaypointSprite_UpdateHealth(target.surv_attack_sprite, target.health +
2149 /// \brief Hook which is called when the player dies.
2150 MUTATOR_HOOKFUNCTION(surv, PlayerDies)
2152 //DebugPrintToChatAll("PlayerDies");
2153 entity attacker = M_ARGV(1, entity);
2154 entity victim = M_ARGV(2, entity);
2155 if ((attacker.team == surv_defenderteam) &&
2156 (victim.team == surv_attackerteam))
2158 switch (victim.surv_role)
2160 case SURVIVAL_ROLE_PLAYER:
2162 GivePlayerHealth(attacker,
2163 autocvar_g_surv_defender_attacker_frag_health);
2164 GivePlayerArmor(attacker,
2165 autocvar_g_surv_defender_attacker_frag_armor);
2166 GivePlayerAmmo(attacker, ammo_shells,
2167 autocvar_g_surv_defender_attacker_frag_shells);
2168 GivePlayerAmmo(attacker, ammo_nails,
2169 autocvar_g_surv_defender_attacker_frag_bullets);
2170 GivePlayerAmmo(attacker, ammo_rockets,
2171 autocvar_g_surv_defender_attacker_frag_rockets);
2172 GivePlayerAmmo(attacker, ammo_cells,
2173 autocvar_g_surv_defender_attacker_frag_cells);
2176 case SURVIVAL_ROLE_CANNON_FODDER:
2178 GivePlayerHealth(attacker,
2179 autocvar_g_surv_defender_cannon_fodder_frag_health);
2180 GivePlayerArmor(attacker,
2181 autocvar_g_surv_defender_cannon_fodder_frag_armor);
2182 GivePlayerAmmo(attacker, ammo_shells,
2183 autocvar_g_surv_defender_cannon_fodder_frag_shells);
2184 GivePlayerAmmo(attacker, ammo_nails,
2185 autocvar_g_surv_defender_cannon_fodder_frag_bullets);
2186 GivePlayerAmmo(attacker, ammo_rockets,
2187 autocvar_g_surv_defender_cannon_fodder_frag_rockets);
2188 GivePlayerAmmo(attacker, ammo_cells,
2189 autocvar_g_surv_defender_cannon_fodder_frag_cells);
2194 if ((victim.team == surv_defenderteam) &&
2195 (autocvar_g_surv_defender_drop_weapons == false))
2197 for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
2199 .entity went = weaponentities[slot];
2200 victim.(went).m_weapon = WEP_Null;
2203 if (!Surv_CanPlayerSpawn(victim))
2205 victim.respawn_flags = RESPAWN_SILENT;
2206 if (IS_BOT_CLIENT(victim))
2214 /// \brief Hook which is called after the player died.
2215 MUTATOR_HOOKFUNCTION(surv, PlayerDied)
2217 //DebugPrintToChatAll("PlayerDied");
2218 entity player = M_ARGV(0, entity);
2219 Surv_RemovePlayerFromAliveList(player, player.team);
2220 //Surv_CountAlivePlayers();
2223 /// \brief Hook which is called when player has scored a frag.
2224 MUTATOR_HOOKFUNCTION(surv, GiveFragsForKill, CBC_ORDER_FIRST)
2226 if (surv_type == SURVIVAL_TYPE_COOP)
2230 entity attacker = M_ARGV(0, entity);
2231 if ((attacker.team == surv_defenderteam) || (attacker.surv_role ==
2232 SURVIVAL_ROLE_CANNON_FODDER))
2234 M_ARGV(2, float) = 0;
2237 entity target = M_ARGV(1, entity);
2238 if ((attacker.surv_role == SURVIVAL_ROLE_PLAYER) && (target.team ==
2241 M_ARGV(2, float) = autocvar_g_surv_attacker_frag_score;
2246 /// \brief I'm not sure exactly what this function does but it is very
2247 /// important. Without it bots are completely broken. Is it a hack? Of course.
2248 MUTATOR_HOOKFUNCTION(surv, Bot_FixCount, CBC_ORDER_EXCLUSIVE)
2250 FOREACH_CLIENT(IS_REAL_CLIENT(it), {
2251 if (IS_PLAYER(it) || (it.surv_state == SURVIVAL_STATE_PLAYING))
2260 MUTATOR_HOOKFUNCTION(surv, Scores_CountFragsRemaining)
2262 // Don't announce remaining frags