]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/gamemodes/gamemode/gungame/sv_gungame.qc
Merge branch 'Lyberta/GenmodFix' into Lyberta/master
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / gamemodes / gamemode / gungame / sv_gungame.qc
1 /// \file
2 /// \brief Source file that contains implementation of the GunGame gamemode.
3 /// \author Lyberta
4 /// \copyright GNU GPLv2 or any later version.
5
6 #include "sv_gungame.qh"
7
8 //============================ Constants ======================================
9
10 const string GUNGAME_WEAPONS = "g_gg_weapons";
11
12 //======================= Global variables ====================================
13
14 /// \brief Number of kills needed to advance to the next weapon.
15 int autocvar_g_gg_kills_per_weapon;
16
17 int gungame_maxlevel; ///< Player who reaches this level wins.
18 string gungame_weapons; ///< Holds weapons corresponding to levels.
19
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.
23
24 //====================== Forward declarations =================================
25
26 /// \brief Resets the state to initial one.
27 /// \return No return.
28 void GunGame_Reset();
29
30 /// \brief Returns the weapon that corresponds to the given level.
31 /// \param[in] level Level of the weapon.
32 /// \return Weapon corresponding to the given level.
33 entity GunGame_GetWeapon(int level);
34
35 /// \brief Updates stats of all players.
36 /// \return No return.
37 void GunGame_UpdateStats();
38
39 //========================= Free functions ====================================
40
41 void GunGame_Initialize()
42 {
43         GunGame_Reset();
44 }
45
46 void GunGame_Reset()
47 {
48         if (gungame_weapons)
49         {
50                 strunzone(gungame_weapons);
51         }
52         gungame_weapons = strzone(cvar_string(GUNGAME_WEAPONS));
53         gungame_maxlevel = tokenize_console(gungame_weapons) *
54                 autocvar_g_gg_kills_per_weapon;
55         if (gungame_maxlevel == 0)
56         {
57                 error("GunGame: Invalid weapon configuration.");
58         }
59         GameRules_limit_score(gungame_maxlevel);
60         gungame_leading_player = NULL;
61         gungame_leading_level = 0;
62         gungame_leading_weapon = GunGame_GetWeapon(0);
63         GunGame_UpdateStats();
64 }
65
66 entity GunGame_GetWeapon(int level)
67 {
68         if (level >= gungame_maxlevel)
69         {
70                 return NULL;
71         }
72         tokenize_console(gungame_weapons);
73         string weapon = argv(floor(level / autocvar_g_gg_kills_per_weapon));
74         FOREACH(Weapons, it != WEP_Null,
75         {
76                 if (it.netname == weapon)
77                 {
78                         return it;
79                 }
80         });
81         error("GunGame_GetWeapon: Invalid level or weapon name");
82         return NULL;
83 }
84
85 /// \brief Returns the player level.
86 /// \param[in] player Player to check.
87 /// \return Level of the player.
88 int GunGame_GetPlayerLevel(entity player)
89 {
90         return PlayerScore_Get(player, SP_SCORE);
91 }
92
93 /// \brief Updates the information about the leading player.
94 /// \return No return.
95 void GunGame_UpdateLeadingPlayer()
96 {
97         entity previous_leader = gungame_leading_player;
98         FOREACH_CLIENT(true,
99         {
100                 if (gungame_leading_player == NULL)
101                 {
102                         gungame_leading_player = it;
103                         continue;
104                 }
105                 if (GunGame_GetPlayerLevel(it) > GunGame_GetPlayerLevel(
106                         gungame_leading_player))
107                 {
108                         gungame_leading_player = it;
109                 }
110         });
111         if (gungame_leading_player == NULL)
112         {
113                 return;
114         }
115         if ((gungame_leading_player == previous_leader) &&
116                 (GunGame_GetPlayerLevel(gungame_leading_player) ==
117                 gungame_leading_level))
118         {
119                 return;
120         }
121         gungame_leading_level = GunGame_GetPlayerLevel(gungame_leading_player);
122         gungame_leading_weapon = GunGame_GetWeapon(gungame_leading_level);
123         GunGame_UpdateStats();
124         //PrintToChatAll(strcat(gungame_leading_player.netname,
125         //      " is leading with level ", ftos(gungame_leading_level)));
126 }
127
128 void GunGame_UpdateStats()
129 {
130         FOREACH_CLIENT(IS_REAL_CLIENT(it),
131         {
132                 STAT(GUNGAME_LEADING_WEAPON, it) = gungame_leading_weapon.m_id;
133         });
134 }
135
136 /// \brief Gives the player a weapon that corresponds to their level.
137 /// \param[in,out] player Player to give weapon to.
138 /// \return No return.
139 void GunGame_GivePlayerWeapon(entity player)
140 {
141         int level = GunGame_GetPlayerLevel(player);
142         if (level >= gungame_maxlevel)
143         {
144                 return;
145         }
146         entity weapon = GunGame_GetWeapon(level);
147         player.weapons |= weapon.m_wepset;
148         centerprint(player, strcat("^3Level ", ftos(level + 1), ": ^2",
149                 weapon.m_name));
150 }
151
152 //============================= Hooks ========================================
153
154 /// \brief Hook that is called to determine if there is a weapon arena.
155 MUTATOR_HOOKFUNCTION(gg, SetWeaponArena)
156 {
157         //PrintToChatAll("SetWeaponArena");
158         M_ARGV(0, string) = "off";
159 }
160
161 /// \brief Hook that is called to determine start items of all players.
162 MUTATOR_HOOKFUNCTION(gg, SetStartItems)
163 {
164         //PrintToChatAll("SetStartItems");
165         start_weapons = WEPSET(Null);
166         warmup_start_weapons = WEPSET(Null);
167 }
168
169 /// \brief Hook that is called when an item is about to spawn.
170 MUTATOR_HOOKFUNCTION(gg, FilterItem)
171 {
172         //PrintToChatAll("FilterItem");
173         entity item = M_ARGV(0, entity);
174         if (item.itemdef.instanceOfWeaponPickup)
175         {
176                 // Block weapons from spawning.
177                 return true;
178         }
179 }
180
181 /// \brief Hook that is called when player connects to the server.
182 MUTATOR_HOOKFUNCTION(gg, ClientConnect)
183 {
184         entity player = M_ARGV(0, entity);
185         if (!IS_REAL_CLIENT(player))
186         {
187                 return true;
188         }
189         STAT(GUNGAME_LEADING_WEAPON, player) = gungame_leading_weapon.m_id;
190         return true;
191 }
192
193 MUTATOR_HOOKFUNCTION(gg, reset_map_global)
194 {
195         GunGame_Reset();
196 }
197
198 /// \brief Hook that is called when player spawns.
199 MUTATOR_HOOKFUNCTION(gg, PlayerSpawn, CBC_ORDER_LAST)
200 {
201         entity player = M_ARGV(0, entity);
202         player.weapons = WEPSET(Null);
203         GunGame_GivePlayerWeapon(player);
204         player.items |= IT_UNLIMITED_AMMO;
205 }
206
207 /// \brief Hook which is called when the player tries to throw their weapon.
208 MUTATOR_HOOKFUNCTION(gg, ForbidThrowCurrentWeapon)
209 {
210         return true;
211 }
212
213 /// \brief Hook that is called when player dies.
214 MUTATOR_HOOKFUNCTION(gg, PlayerDies)
215 {
216         GunGame_UpdateLeadingPlayer();
217         entity attacker = M_ARGV(1, entity);
218         if (!IS_PLAYER(attacker) || IS_DEAD(attacker) || (GunGame_GetPlayerLevel(
219                 attacker) >= gungame_maxlevel))
220         {
221                 return;
222         }
223         attacker.weapons = WEPSET(Null);
224         GunGame_GivePlayerWeapon(attacker);
225 }
226
227 /// \brief Hook that determines whether remaining frags are announced.
228 MUTATOR_HOOKFUNCTION(gg, Scores_CountFragsRemaining)
229 {
230         // announce remaining frags
231         return true;
232 }