2 /// \brief Source file that contains implementation of the GunGame gamemode.
3 /// \copyright GNU GPLv2 or any later version.
5 #include "sv_gungame.qh"
7 //============================ Constants ======================================
9 const string GUNGAME_WEAPONS_CVAR = "g_gg_weapons";
11 //======================= Global variables ====================================
13 /// \brief Number of kills needed to advance to the next weapon.
14 int autocvar_g_gg_kills_per_weapon;
16 int gungame_win_level; ///< Player who reaches this level wins.
17 string gungame_weapons; ///< Holds weapons corresponding to levels.
19 int gungame_status; ///< Holds global status of the game.
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_win_level = tokenize_console(gungame_weapons) *
48 autocvar_g_gg_kills_per_weapon;
49 if (gungame_win_level == 0)
51 LOG_FATAL("GunGame: Invalid weapon configuration.");
53 gungame_status = WINNING_NO;
54 GameRules_limit_score(gungame_win_level);
55 gungame_leading_player = NULL;
56 gungame_leading_level = 0;
57 gungame_leading_weapon = GunGame_GetWeapon(0);
58 GunGame_UpdateStats();
61 entity GunGame_GetWeapon(int level)
63 if (level >= gungame_win_level)
67 tokenize_console(gungame_weapons);
68 string weapon_name = argv(floor(level / autocvar_g_gg_kills_per_weapon));
69 Weapon weapon = Weapons_fromstr(weapon_name);
70 if (weapon == WEP_Null)
72 LOG_FATAL("GunGame_GetWeapon: Invalid level or weapon name");
77 /// \brief Returns the player level.
78 /// \param[in] player Player to check.
79 /// \return Level of the player.
80 int GunGame_GetPlayerLevel(entity player)
82 return PlayerScore_Get(player, SP_SCORE);
85 /// \brief Updates the information about the leading player.
86 void GunGame_UpdateLeadingPlayer()
88 entity previous_leader = gungame_leading_player;
91 if (gungame_leading_player == NULL)
93 gungame_leading_player = it;
96 if (GunGame_GetPlayerLevel(it) > GunGame_GetPlayerLevel(
97 gungame_leading_player))
99 gungame_leading_player = it;
102 if (gungame_leading_player == NULL)
106 if ((gungame_leading_player == previous_leader) &&
107 (GunGame_GetPlayerLevel(gungame_leading_player) ==
108 gungame_leading_level))
112 gungame_leading_level = GunGame_GetPlayerLevel(gungame_leading_player);
113 gungame_leading_weapon = GunGame_GetWeapon(gungame_leading_level);
114 GunGame_UpdateStats();
117 void GunGame_UpdateStats()
119 FOREACH_CLIENT(IS_REAL_CLIENT(it),
121 STAT(GUNGAME_LEADING_WEAPON, it) = gungame_leading_weapon.m_id;
125 /// \brief Gives the player a weapon that corresponds to their level.
126 /// \param[in,out] player Player to give weapon to.
127 void GunGame_GivePlayerWeapon(entity player)
129 int level = GunGame_GetPlayerLevel(player);
130 if (level >= gungame_win_level)
134 entity weapon = GunGame_GetWeapon(level);
135 STAT(WEAPONS, player) |= weapon.m_wepset;
136 int levels_left = gungame_win_level - level;
139 Send_Notification(NOTIF_ONE, player, MSG_CENTER,
140 CENTER_GUNGAME_NEW_LEVEL, level + 1, weapon.m_id);
141 Send_Notification(NOTIF_ONE, player, MSG_INFO, INFO_GUNGAME_NEW_LEVEL,
142 level + 1, weapon.m_id);
147 Send_Notification(NOTIF_ONE, player, MSG_CENTER,
148 CENTER_GUNGAME_LEVELS_LEFT, levels_left, weapon.m_id);
149 Send_Notification(NOTIF_ONE, player, MSG_INFO, INFO_GUNGAME_LEVELS_LEFT,
150 levels_left, weapon.m_id);
153 Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_GUNGAME_LAST_LEVEL,
155 Send_Notification(NOTIF_ONE, player, MSG_INFO, INFO_GUNGAME_LAST_LEVEL,
159 //============================= Hooks ========================================
161 /// \brief Hook that is called to determine if there is a weapon arena.
162 MUTATOR_HOOKFUNCTION(gg, SetWeaponArena)
164 M_ARGV(0, string) = "off";
167 /// \brief Hook that is called to determine start items of all players.
168 MUTATOR_HOOKFUNCTION(gg, SetStartItems)
170 start_weapons = WEPSET(Null);
171 warmup_start_weapons = WEPSET(Null);
174 /// \brief Hook that is called when an item is about to spawn.
175 MUTATOR_HOOKFUNCTION(gg, FilterItemDefinition)
177 entity item = M_ARGV(0, entity);
178 if (item.instanceOfAmmo)
180 // Block ammo from spawning.
183 if (item.instanceOfWeaponPickup)
185 // Block weapons from spawning.
190 /// \brief Hook that is called every frame to check if the game is won and/or
192 MUTATOR_HOOKFUNCTION(gg, CheckRules_World)
194 M_ARGV(0, float) = gungame_status;
195 //M_ARGV(2, float) = gungame_win_level;
199 /// \brief Hook that is called when player connects to the server.
200 MUTATOR_HOOKFUNCTION(gg, ClientConnect)
202 entity player = M_ARGV(0, entity);
203 if (!IS_REAL_CLIENT(player))
207 STAT(GUNGAME_LEADING_WEAPON, player) = gungame_leading_weapon.m_id;
211 MUTATOR_HOOKFUNCTION(gg, reset_map_global)
216 /// \brief Hook that is called when player spawns.
217 MUTATOR_HOOKFUNCTION(gg, PlayerSpawn, CBC_ORDER_LAST)
219 entity player = M_ARGV(0, entity);
220 STAT(WEAPONS, player) = WEPSET(Null);
221 GunGame_GivePlayerWeapon(player);
222 player.items |= IT_UNLIMITED_AMMO;
225 /// \brief Hook which is called when the player tries to throw their weapon.
226 MUTATOR_HOOKFUNCTION(gg, ForbidThrowCurrentWeapon)
231 /// \brief Hook that is called when player dies.
232 MUTATOR_HOOKFUNCTION(gg, PlayerDies)
234 GunGame_UpdateLeadingPlayer();
235 entity attacker = M_ARGV(1, entity);
236 if (!IS_PLAYER(attacker) || IS_DEAD(attacker))
240 if (GunGame_GetPlayerLevel(attacker) >= gungame_win_level)
242 gungame_status = WINNING_YES;
245 STAT(WEAPONS, attacker) = WEPSET(Null);
246 GunGame_GivePlayerWeapon(attacker);
249 /// \brief Hook that determines whether remaining frags are announced.
250 MUTATOR_HOOKFUNCTION(gg, Scores_CountFragsRemaining)
252 // Announce remaining frags.