]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/gamemodes/gamemode/gungame/sv_gungame.qc
Merge branch 'Lyberta/Survival' 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_CVAR = "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_max_level; ///< 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 void GunGame_Reset();
28
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);
33
34 /// \brief Updates stats of all players.
35 void GunGame_UpdateStats();
36
37 //========================= Free functions ====================================
38
39 void GunGame_Initialize()
40 {
41         GunGame_Reset();
42 }
43
44 void GunGame_Reset()
45 {
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)
50         {
51                 LOG_FATAL("GunGame: Invalid weapon configuration.");
52         }
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();
58 }
59
60 entity GunGame_GetWeapon(int level)
61 {
62         if (level >= gungame_max_level)
63         {
64                 return NULL;
65         }
66         tokenize_console(gungame_weapons);
67         string weapon = argv(floor(level / autocvar_g_gg_kills_per_weapon));
68         FOREACH(Weapons, it != WEP_Null,
69         {
70                 if (it.netname == weapon)
71                 {
72                         return it;
73                 }
74         });
75         LOG_FATAL("GunGame_GetWeapon: Invalid level or weapon name");
76         return NULL;
77 }
78
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)
83 {
84         return PlayerScore_Get(player, SP_SCORE);
85 }
86
87 /// \brief Updates the information about the leading player.
88 void GunGame_UpdateLeadingPlayer()
89 {
90         entity previous_leader = gungame_leading_player;
91         FOREACH_CLIENT(true,
92         {
93                 if (gungame_leading_player == NULL)
94                 {
95                         gungame_leading_player = it;
96                         continue;
97                 }
98                 if (GunGame_GetPlayerLevel(it) > GunGame_GetPlayerLevel(
99                         gungame_leading_player))
100                 {
101                         gungame_leading_player = it;
102                 }
103         });
104         if (gungame_leading_player == NULL)
105         {
106                 return;
107         }
108         if ((gungame_leading_player == previous_leader) &&
109                 (GunGame_GetPlayerLevel(gungame_leading_player) ==
110                 gungame_leading_level))
111         {
112                 return;
113         }
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)));
119 }
120
121 void GunGame_UpdateStats()
122 {
123         FOREACH_CLIENT(IS_REAL_CLIENT(it),
124         {
125                 STAT(GUNGAME_LEADING_WEAPON, it) = gungame_leading_weapon.m_id;
126         });
127 }
128
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)
132 {
133         int level = GunGame_GetPlayerLevel(player);
134         if (level >= gungame_max_level)
135         {
136                 return;
137         }
138         entity weapon = GunGame_GetWeapon(level);
139         STAT(WEAPONS, player) |= weapon.m_wepset;
140         centerprint(player, strcat("^3Level ", ftos(level + 1), ": ^2",
141                 weapon.m_name));
142 }
143
144 //============================= Hooks ========================================
145
146 /// \brief Hook that is called to determine if there is a weapon arena.
147 MUTATOR_HOOKFUNCTION(gg, SetWeaponArena)
148 {
149         //PrintToChatAll("SetWeaponArena");
150         M_ARGV(0, string) = "off";
151 }
152
153 /// \brief Hook that is called to determine start items of all players.
154 MUTATOR_HOOKFUNCTION(gg, SetStartItems)
155 {
156         //PrintToChatAll("SetStartItems");
157         start_weapons = WEPSET(Null);
158         warmup_start_weapons = WEPSET(Null);
159 }
160
161 /// \brief Hook that is called when an item is about to spawn.
162 MUTATOR_HOOKFUNCTION(gg, FilterItemDefinition)
163 {
164         //PrintToChatAll("FilterItemDefinition");
165         entity item = M_ARGV(0, entity);
166         if (item.instanceOfAmmo)
167         {
168                 // Block ammo from spawning.
169                 return true;
170         }
171         if (item.instanceOfWeaponPickup)
172         {
173                 // Block weapons from spawning.
174                 return true;
175         }
176 }
177
178 /// \brief Hook that is called when player connects to the server.
179 MUTATOR_HOOKFUNCTION(gg, ClientConnect)
180 {
181         entity player = M_ARGV(0, entity);
182         if (!IS_REAL_CLIENT(player))
183         {
184                 return true;
185         }
186         STAT(GUNGAME_LEADING_WEAPON, player) = gungame_leading_weapon.m_id;
187         return true;
188 }
189
190 MUTATOR_HOOKFUNCTION(gg, reset_map_global)
191 {
192         GunGame_Reset();
193 }
194
195 /// \brief Hook that is called when player spawns.
196 MUTATOR_HOOKFUNCTION(gg, PlayerSpawn, CBC_ORDER_LAST)
197 {
198         entity player = M_ARGV(0, entity);
199         STAT(WEAPONS, player) = WEPSET(Null);
200         GunGame_GivePlayerWeapon(player);
201         player.items |= IT_UNLIMITED_AMMO;
202 }
203
204 /// \brief Hook which is called when the player tries to throw their weapon.
205 MUTATOR_HOOKFUNCTION(gg, ForbidThrowCurrentWeapon)
206 {
207         return true;
208 }
209
210 /// \brief Hook that is called when player dies.
211 MUTATOR_HOOKFUNCTION(gg, PlayerDies)
212 {
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))
217         {
218                 return;
219         }
220         STAT(WEAPONS, attacker) = WEPSET(Null);
221         GunGame_GivePlayerWeapon(attacker);
222 }
223
224 /// \brief Hook that determines whether remaining frags are announced.
225 MUTATOR_HOOKFUNCTION(gg, Scores_CountFragsRemaining)
226 {
227         // announce remaining frags
228         return true;
229 }