]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/mutators/mutator/gamemode_lms.qc
Mutators: combine headers
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / mutator / gamemode_lms.qc
1 #ifndef GAMEMODE_LMS_H
2 #define GAMEMODE_LMS_H
3
4 // scoreboard stuff
5 const float SP_LMS_LIVES = 4;
6 const float SP_LMS_RANK = 5;
7
8 // lives related defs
9 float lms_lowest_lives;
10 float lms_next_place;
11 float LMS_NewPlayerLives();
12
13 #endif
14
15 #ifdef IMPLEMENTATION
16
17 #include "../../campaign.qh"
18 #include "../../command/cmd.qh"
19
20 int autocvar_g_lms_extra_lives;
21 bool autocvar_g_lms_join_anytime;
22 int autocvar_g_lms_last_join;
23 #define autocvar_g_lms_lives_override cvar("g_lms_lives_override")
24 bool autocvar_g_lms_regenerate;
25
26 // main functions
27 float LMS_NewPlayerLives()
28 {
29         float fl;
30         fl = autocvar_fraglimit;
31         if(fl == 0)
32                 fl = 999;
33
34         // first player has left the game for dying too much? Nobody else can get in.
35         if(lms_lowest_lives < 1)
36                 return 0;
37
38         if(!autocvar_g_lms_join_anytime)
39                 if(lms_lowest_lives < fl - autocvar_g_lms_last_join)
40                         return 0;
41
42         return bound(1, lms_lowest_lives, fl);
43 }
44
45 // mutator hooks
46 MUTATOR_HOOKFUNCTION(lms, reset_map_global)
47 {
48         lms_lowest_lives = 999;
49         lms_next_place = player_count;
50
51         return false;
52 }
53
54 MUTATOR_HOOKFUNCTION(lms, reset_map_players)
55 {SELFPARAM();
56         entity e;
57         if(restart_mapalreadyrestarted || (time < game_starttime))
58         FOR_EACH_CLIENT(e)
59         if(IS_PLAYER(e))
60         {
61                 WITH(entity, self, e, PlayerScore_Add(e, SP_LMS_LIVES, LMS_NewPlayerLives()));
62         }
63
64         return false;
65 }
66
67 MUTATOR_HOOKFUNCTION(lms, PutClientInServer)
68 {SELFPARAM();
69         // player is dead and becomes observer
70         // FIXME fix LMS scoring for new system
71         if(PlayerScore_Add(self, SP_LMS_RANK, 0) > 0)
72         {
73                 self.classname = "observer";
74                 Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_LMS_NOLIVES);
75         }
76
77         return false;
78 }
79
80 MUTATOR_HOOKFUNCTION(lms, PlayerDies)
81 {SELFPARAM();
82         self.respawn_flags |= RESPAWN_FORCE;
83
84         return false;
85 }
86
87 void lms_RemovePlayer(entity player)
88 {
89         // Only if the player cannot play at all
90         if(PlayerScore_Add(player, SP_LMS_RANK, 0) == 666)
91                 player.frags = FRAGS_SPECTATOR;
92         else
93                 player.frags = FRAGS_LMS_LOSER;
94
95         if(player.killcount != -666)
96                 if(PlayerScore_Add(player, SP_LMS_RANK, 0) > 0 && player.lms_spectate_warning != 2)
97                         Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_LMS_NOLIVES, player.netname);
98                 else
99                         Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_LMS_FORFEIT, player.netname);
100 }
101
102 MUTATOR_HOOKFUNCTION(lms, ClientDisconnect)
103 {SELFPARAM();
104         lms_RemovePlayer(self);
105         return false;
106 }
107
108 MUTATOR_HOOKFUNCTION(lms, MakePlayerObserver)
109 {SELFPARAM();
110         lms_RemovePlayer(self);
111         return false;
112 }
113
114 MUTATOR_HOOKFUNCTION(lms, ClientConnect)
115 {SELFPARAM();
116         self.classname = "player";
117         campaign_bots_may_start = 1;
118
119         if(PlayerScore_Add(self, SP_LMS_LIVES, LMS_NewPlayerLives()) <= 0)
120         {
121                 PlayerScore_Add(self, SP_LMS_RANK, 666);
122                 self.frags = FRAGS_SPECTATOR;
123         }
124
125         return false;
126 }
127
128 MUTATOR_HOOKFUNCTION(lms, PlayerPreThink)
129 {SELFPARAM();
130         if(self.deadflag == DEAD_DYING)
131                 self.deadflag = DEAD_RESPAWNING;
132
133         return false;
134 }
135
136 MUTATOR_HOOKFUNCTION(lms, PlayerRegen)
137 {
138         if(autocvar_g_lms_regenerate)
139                 return false;
140         return true;
141 }
142
143 MUTATOR_HOOKFUNCTION(lms, ForbidThrowCurrentWeapon)
144 {
145         // forbode!
146         return true;
147 }
148
149 MUTATOR_HOOKFUNCTION(lms, GiveFragsForKill)
150 {
151         // remove a life
152         float tl;
153         tl = PlayerScore_Add(frag_target, SP_LMS_LIVES, -1);
154         if(tl < lms_lowest_lives)
155                 lms_lowest_lives = tl;
156         if(tl <= 0)
157         {
158                 if(!lms_next_place)
159                         lms_next_place = player_count;
160                 else
161                         lms_next_place = min(lms_next_place, player_count);
162                 PlayerScore_Add(frag_target, SP_LMS_RANK, lms_next_place); // won't ever spawn again
163                 --lms_next_place;
164         }
165         frag_score = 0;
166
167         return true;
168 }
169
170 MUTATOR_HOOKFUNCTION(lms, SetStartItems)
171 {
172         start_items &= ~IT_UNLIMITED_AMMO;
173         start_health       = warmup_start_health       = cvar("g_lms_start_health");
174         start_armorvalue   = warmup_start_armorvalue   = cvar("g_lms_start_armor");
175         start_ammo_shells  = warmup_start_ammo_shells  = cvar("g_lms_start_ammo_shells");
176         start_ammo_nails   = warmup_start_ammo_nails   = cvar("g_lms_start_ammo_nails");
177         start_ammo_rockets = warmup_start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
178         start_ammo_cells   = warmup_start_ammo_cells   = cvar("g_lms_start_ammo_cells");
179         start_ammo_plasma  = warmup_start_ammo_plasma  = cvar("g_lms_start_ammo_plasma");
180         start_ammo_fuel    = warmup_start_ammo_fuel    = cvar("g_lms_start_ammo_fuel");
181
182         return false;
183 }
184
185 MUTATOR_HOOKFUNCTION(lms, ForbidPlayerScore_Clear)
186 {
187         // don't clear player score
188         return true;
189 }
190
191 MUTATOR_HOOKFUNCTION(lms, FilterItem)
192 {SELFPARAM();
193         if(autocvar_g_lms_extra_lives)
194         if(self.itemdef == ITEM_HealthMega)
195         {
196                 self.max_health = 1;
197                 return false;
198         }
199
200         return true;
201 }
202
203 MUTATOR_HOOKFUNCTION(lms, ItemTouch)
204 {SELFPARAM();
205         // give extra lives for mega health
206         if (self.items & ITEM_HealthMega.m_itemid)
207         {
208                 Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_EXTRALIVES);
209                 PlayerScore_Add(other, SP_LMS_LIVES, autocvar_g_lms_extra_lives);
210         }
211
212         return MUT_ITEMTOUCH_CONTINUE;
213 }
214
215 MUTATOR_HOOKFUNCTION(lms, Bot_FixCount, CBC_ORDER_EXCLUSIVE)
216 {
217         entity head;
218         FOR_EACH_REALCLIENT(head)
219         {
220                 ++bot_activerealplayers;
221                 ++bot_realplayers;
222         }
223
224         return true;
225 }
226
227 MUTATOR_HOOKFUNCTION(lms, ClientCommand_Spectate)
228 {
229         if(self.lms_spectate_warning)
230         {
231                 // for the forfeit message...
232                 self.lms_spectate_warning = 2;
233                 // mark player as spectator
234                 PlayerScore_Add(self, SP_LMS_RANK, 666 - PlayerScore_Add(self, SP_LMS_RANK, 0));
235         }
236         else
237         {
238                 self.lms_spectate_warning = 1;
239                 sprint(self, "WARNING: you won't be able to enter the game again after spectating in LMS. Use the same command again to spectate anyway.\n");
240                 return MUT_SPECCMD_RETURN;
241         }
242         return MUT_SPECCMD_CONTINUE;
243 }
244
245 MUTATOR_HOOKFUNCTION(lms, CheckRules_World)
246 {
247         ret_float = WinningCondition_LMS();
248         return true;
249 }
250
251 MUTATOR_HOOKFUNCTION(lms, WantWeapon)
252 {
253         want_allguns = true;
254         return false;
255 }
256
257 MUTATOR_HOOKFUNCTION(lms, GetPlayerStatus)
258 {
259         return true;
260 }
261
262 MUTATOR_HOOKFUNCTION(lms, AddPlayerScore)
263 {
264         if(gameover)
265         if(score_field == SP_LMS_RANK)
266                 return true; // allow writing to this field in intermission as it is needed for newly joining players
267         return false;
268 }
269
270 // scoreboard stuff
271 void lms_ScoreRules()
272 {
273         ScoreRules_basics(0, 0, 0, false);
274         ScoreInfo_SetLabel_PlayerScore(SP_LMS_LIVES,    "lives",     SFL_SORT_PRIO_SECONDARY);
275         ScoreInfo_SetLabel_PlayerScore(SP_LMS_RANK,     "rank",      SFL_LOWER_IS_BETTER | SFL_RANK | SFL_SORT_PRIO_PRIMARY | SFL_ALLOW_HIDE);
276         ScoreRules_basics_end();
277 }
278
279 void lms_Initialize()
280 {
281         lms_lowest_lives = 9999;
282         lms_next_place = 0;
283
284         lms_ScoreRules();
285 }
286
287 REGISTER_MUTATOR(lms, IS_GAMETYPE(LMS))
288 {
289         SetLimits(((!autocvar_g_lms_lives_override) ? -1 : autocvar_g_lms_lives_override), 0, -1, -1);
290
291         MUTATOR_ONADD
292         {
293                 if(time > 1) // game loads at time 1
294                         error("This is a game type and it cannot be added at runtime.");
295                 lms_Initialize();
296         }
297
298         MUTATOR_ONROLLBACK_OR_REMOVE
299         {
300                 // we actually cannot roll back lms_Initialize here
301                 // BUT: we don't need to! If this gets called, adding always
302                 // succeeds.
303         }
304
305         MUTATOR_ONREMOVE
306         {
307                 LOG_INFO("This is a game type and it cannot be removed at runtime.");
308                 return -1;
309         }
310
311         return 0;
312 }
313 #endif