2 /// \brief Source file that contains implementation of the GunGame gamemode.
4 /// \copyright GNU GPLv2 or any later version.
6 #include "sv_gungame.qh"
8 //============================ Constants ======================================
10 const string GUNGAME_WEAPONS_CVAR = "g_gg_weapons";
12 //======================= Global variables ====================================
14 /// \brief Number of kills needed to advance to the next weapon.
15 int autocvar_g_gg_kills_per_weapon;
17 int gungame_max_level; ///< Player who reaches this level wins.
18 string gungame_weapons; ///< Holds weapons corresponding to levels.
20 entity gungame_leading_player; ///< Holds the leading player.
21 int gungame_leading_level; ///< Holds the leading level.
22 entity gungame_leading_weapon; ///< Holds the leading weapon.
24 //====================== Forward declarations =================================
26 /// \brief Resets the state to initial one.
29 /// \brief Returns the weapon that corresponds to the given level.
30 /// \param[in] level Level of the weapon.
31 /// \return Weapon corresponding to the given level.
32 entity GunGame_GetWeapon(int level);
34 /// \brief Updates stats of all players.
35 void GunGame_UpdateStats();
37 //========================= Free functions ====================================
39 void GunGame_Initialize()
46 strcpy(gungame_weapons, cvar_string(GUNGAME_WEAPONS_CVAR));
47 gungame_max_level = tokenize_console(gungame_weapons) *
48 autocvar_g_gg_kills_per_weapon;
49 if (gungame_max_level == 0)
51 LOG_FATAL("GunGame: Invalid weapon configuration.");
53 GameRules_limit_score(gungame_max_level);
54 gungame_leading_player = NULL;
55 gungame_leading_level = 0;
56 gungame_leading_weapon = GunGame_GetWeapon(0);
57 GunGame_UpdateStats();
60 entity GunGame_GetWeapon(int level)
62 if (level >= gungame_max_level)
66 tokenize_console(gungame_weapons);
67 string weapon = argv(floor(level / autocvar_g_gg_kills_per_weapon));
68 FOREACH(Weapons, it != WEP_Null,
70 if (it.netname == weapon)
75 LOG_FATAL("GunGame_GetWeapon: Invalid level or weapon name");
79 /// \brief Returns the player level.
80 /// \param[in] player Player to check.
81 /// \return Level of the player.
82 int GunGame_GetPlayerLevel(entity player)
84 return PlayerScore_Get(player, SP_SCORE);
87 /// \brief Updates the information about the leading player.
88 void GunGame_UpdateLeadingPlayer()
90 entity previous_leader = gungame_leading_player;
93 if (gungame_leading_player == NULL)
95 gungame_leading_player = it;
98 if (GunGame_GetPlayerLevel(it) > GunGame_GetPlayerLevel(
99 gungame_leading_player))
101 gungame_leading_player = it;
104 if (gungame_leading_player == NULL)
108 if ((gungame_leading_player == previous_leader) &&
109 (GunGame_GetPlayerLevel(gungame_leading_player) ==
110 gungame_leading_level))
114 gungame_leading_level = GunGame_GetPlayerLevel(gungame_leading_player);
115 gungame_leading_weapon = GunGame_GetWeapon(gungame_leading_level);
116 GunGame_UpdateStats();
117 //PrintToChatAll(strcat(gungame_leading_player.netname,
118 // " is leading with level ", ftos(gungame_leading_level)));
121 void GunGame_UpdateStats()
123 FOREACH_CLIENT(IS_REAL_CLIENT(it),
125 STAT(GUNGAME_LEADING_WEAPON, it) = gungame_leading_weapon.m_id;
129 /// \brief Gives the player a weapon that corresponds to their level.
130 /// \param[in,out] player Player to give weapon to.
131 void GunGame_GivePlayerWeapon(entity player)
133 int level = GunGame_GetPlayerLevel(player);
134 if (level >= gungame_max_level)
138 entity weapon = GunGame_GetWeapon(level);
139 STAT(WEAPONS, player) |= weapon.m_wepset;
140 centerprint(player, strcat("^3Level ", ftos(level + 1), ": ^2",
144 //============================= Hooks ========================================
146 /// \brief Hook that is called to determine if there is a weapon arena.
147 MUTATOR_HOOKFUNCTION(gg, SetWeaponArena)
149 //PrintToChatAll("SetWeaponArena");
150 M_ARGV(0, string) = "off";
153 /// \brief Hook that is called to determine start items of all players.
154 MUTATOR_HOOKFUNCTION(gg, SetStartItems)
156 //PrintToChatAll("SetStartItems");
157 start_weapons = WEPSET(Null);
158 warmup_start_weapons = WEPSET(Null);
161 /// \brief Hook that is called when an item is about to spawn.
162 MUTATOR_HOOKFUNCTION(gg, FilterItemDefinition)
164 //PrintToChatAll("FilterItemDefinition");
165 entity item = M_ARGV(0, entity);
166 if (item.instanceOfAmmo)
168 // Block ammo from spawning.
171 if (item.instanceOfWeaponPickup)
173 // Block weapons from spawning.
178 /// \brief Hook that is called when player connects to the server.
179 MUTATOR_HOOKFUNCTION(gg, ClientConnect)
181 entity player = M_ARGV(0, entity);
182 if (!IS_REAL_CLIENT(player))
186 STAT(GUNGAME_LEADING_WEAPON, player) = gungame_leading_weapon.m_id;
190 MUTATOR_HOOKFUNCTION(gg, reset_map_global)
195 /// \brief Hook that is called when player spawns.
196 MUTATOR_HOOKFUNCTION(gg, PlayerSpawn, CBC_ORDER_LAST)
198 entity player = M_ARGV(0, entity);
199 STAT(WEAPONS, player) = WEPSET(Null);
200 GunGame_GivePlayerWeapon(player);
201 player.items |= IT_UNLIMITED_AMMO;
204 /// \brief Hook which is called when the player tries to throw their weapon.
205 MUTATOR_HOOKFUNCTION(gg, ForbidThrowCurrentWeapon)
210 /// \brief Hook that is called when player dies.
211 MUTATOR_HOOKFUNCTION(gg, PlayerDies)
213 GunGame_UpdateLeadingPlayer();
214 entity attacker = M_ARGV(1, entity);
215 if (!IS_PLAYER(attacker) || IS_DEAD(attacker) || (GunGame_GetPlayerLevel(
216 attacker) >= gungame_max_level))
220 STAT(WEAPONS, attacker) = WEPSET(Null);
221 GunGame_GivePlayerWeapon(attacker);
224 /// \brief Hook that determines whether remaining frags are announced.
225 MUTATOR_HOOKFUNCTION(gg, Scores_CountFragsRemaining)
227 // announce remaining frags