]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/mutators/gamemode_arena.qc
Merge remote-tracking branch 'origin/master' into samual/serverlist
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / gamemode_arena.qc
1 .float spawned;
2 float maxspawned;
3 float numspawned;
4 .entity spawnqueue_next;
5 .entity spawnqueue_prev;
6 .float spawnqueue_in;
7 entity spawnqueue_first;
8 entity spawnqueue_last;
9
10 void Spawnqueue_Insert(entity e)
11 {
12         if(e.spawnqueue_in)
13                 return;
14         dprint(strcat("Into queue: ", e.netname, "\n"));
15         e.spawnqueue_in = TRUE;
16         e.spawnqueue_prev = spawnqueue_last;
17         e.spawnqueue_next = world;
18         if(spawnqueue_last)
19                 spawnqueue_last.spawnqueue_next = e;
20         spawnqueue_last = e;
21         if(!spawnqueue_first)
22                 spawnqueue_first = e;
23 }
24
25 void Spawnqueue_Remove(entity e)
26 {
27         if(!e.spawnqueue_in)
28                 return;
29         dprint(strcat("Out of queue: ", e.netname, "\n"));
30         e.spawnqueue_in = FALSE;
31         if(e == spawnqueue_first)
32                 spawnqueue_first = e.spawnqueue_next;
33         if(e == spawnqueue_last)
34                 spawnqueue_last = e.spawnqueue_prev;
35         if(e.spawnqueue_prev)
36                 e.spawnqueue_prev.spawnqueue_next = e.spawnqueue_next;
37         if(e.spawnqueue_next)
38                 e.spawnqueue_next.spawnqueue_prev = e.spawnqueue_prev;
39         e.spawnqueue_next = world;
40         e.spawnqueue_prev = world;
41 }
42
43 void Spawnqueue_Unmark(entity e)
44 {
45         if(!e.spawned)
46                 return;
47         e.spawned = FALSE;
48         numspawned = numspawned - 1;
49 }
50
51 void Spawnqueue_Mark(entity e)
52 {
53         if(e.spawned)
54                 return;
55         e.spawned = TRUE;
56         numspawned = numspawned + 1;
57 }
58
59 float Arena_CheckWinner()
60 {
61         entity e;
62
63         if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
64         {
65                 Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER);
66                 Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER);
67                 round_handler_Init(5, autocvar_g_arena_warmup, autocvar_g_arena_round_timelimit);
68                 return 1;
69         }
70
71         if(numspawned > 1)
72                 return 0;
73
74         entity champion;
75         champion = world;
76         FOR_EACH_CLIENT(e)
77         {
78                 if(e.spawned && IS_PLAYER(e))
79                         champion = e;
80         }
81
82         if(champion)
83         {
84                 Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_PLAYER_WIN, champion.netname);
85                 Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_PLAYER_WIN, champion.netname);
86                 UpdateFrags(champion, +1);
87         }
88         else
89         {
90                 Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_TIED);
91                 Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED);
92         }
93         round_handler_Init(5, autocvar_g_arena_warmup, autocvar_g_arena_round_timelimit);
94         return 1;
95 }
96
97 void Arena_AddChallengers()
98 {
99         entity e;
100         if(time < 2) // don't force players to spawn so early
101                 return;
102         e = self;
103         while(numspawned < maxspawned && spawnqueue_first)
104         {
105                 self = spawnqueue_first;
106
107                 bprint ("^4", self.netname, "^4 is the next challenger\n");
108
109                 Spawnqueue_Remove(self);
110                 Spawnqueue_Mark(self);
111
112                 self.classname = "player";
113                 PutClientInServer();
114         }
115         self = e;
116 }
117
118 float prev_numspawned;
119 float Arena_CheckPlayers()
120 {
121         Arena_AddChallengers();
122
123         if(numspawned >= 2)
124         {
125                 if(prev_numspawned > 0)
126                         Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_PLAYERS);
127                 prev_numspawned = -1;
128                 return 1;
129         }
130
131         if(prev_numspawned != numspawned && numspawned == 1)
132         {
133                 if(maxspawned - numspawned > 0)
134                         Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_MISSING_PLAYERS, maxspawned - numspawned);
135                 prev_numspawned = numspawned;
136         }
137
138         return 0;
139 }
140
141 void Arena_RoundStart()
142 {
143         entity e;
144         FOR_EACH_PLAYER(e)
145                 e.player_blocked = 0;
146 }
147
148 MUTATOR_HOOKFUNCTION(arena_ClientDisconnect)
149 {
150         Spawnqueue_Unmark(self);
151         Spawnqueue_Remove(self);
152         return 1;
153 }
154
155 MUTATOR_HOOKFUNCTION(arena_reset_map_players)
156 {
157         FOR_EACH_CLIENT(self)
158         {
159                 if(self.spawned)
160                 {
161                         PutClientInServer();
162                         self.player_blocked = 1;
163                 }
164                 else
165                         PutObserverInServer();
166         }
167         return 1;
168 }
169
170 MUTATOR_HOOKFUNCTION(arena_MakePlayerObserver)
171 {
172         if(self.version_mismatch)
173         {
174                 self.frags = FRAGS_SPECTATOR;
175                 Spawnqueue_Unmark(self);
176                 Spawnqueue_Remove(self);
177         }
178         else
179         {
180                 self.frags = FRAGS_LMS_LOSER;
181                 Spawnqueue_Insert(self);
182         }
183         return 1;
184 }
185
186 MUTATOR_HOOKFUNCTION(arena_PutClientInServer)
187 {
188         if(!self.spawned)
189                 self.classname = "observer";
190         return 1;
191 }
192
193 MUTATOR_HOOKFUNCTION(arena_ClientConnect)
194 {
195         self.classname = "observer";
196         Spawnqueue_Insert(self);
197         return 1;
198 }
199
200 MUTATOR_HOOKFUNCTION(arena_PlayerSpawn)
201 {
202         Spawnqueue_Remove(self);
203         Spawnqueue_Mark(self);
204         if(arena_roundbased)
205                 self.player_blocked = 1;
206         return 1;
207 }
208
209 MUTATOR_HOOKFUNCTION(arena_ForbidPlayerScore_Clear)
210 {
211         return 1;
212 }
213
214 MUTATOR_HOOKFUNCTION(arena_GiveFragsForKill)
215 {
216         if(arena_roundbased)
217                 frag_score = 0; // score will be given to the champion when the round ends
218         return 1;
219 }
220
221 MUTATOR_HOOKFUNCTION(arena_PlayerDies)
222 {
223         // put dead players in the spawn queue
224         if(arena_roundbased)
225                 self.respawn_flags = (RESPAWN_FORCE | RESPAWN_SILENT);
226         else
227                 self.respawn_flags = RESPAWN_SILENT;
228         Spawnqueue_Unmark(self);
229         return 1;
230 }
231
232 MUTATOR_HOOKFUNCTION(arena_SV_StartFrame)
233 {
234         if(gameover) return 1;
235         if(time <= game_starttime || !arena_roundbased)
236                 Arena_AddChallengers();
237         return 1;
238 }
239
240 MUTATOR_HOOKFUNCTION(arena_FilterItem)
241 {
242         if(autocvar_g_powerups <= 0)
243         if(self.flags & FL_POWERUP)
244                 return TRUE;
245                 
246         return FALSE;
247 }
248
249 void arena_Initialize()
250 {
251         maxspawned = max(2, autocvar_g_arena_maxspawned);
252         arena_roundbased = autocvar_g_arena_roundbased;
253         if(arena_roundbased)
254         {
255                 round_handler_Spawn(Arena_CheckPlayers, Arena_CheckWinner, Arena_RoundStart);
256                 round_handler_Init(5, autocvar_g_arena_warmup, autocvar_g_arena_round_timelimit);
257         }
258 }
259
260 MUTATOR_DEFINITION(gamemode_arena)
261 {
262         MUTATOR_HOOK(ClientDisconnect, arena_ClientDisconnect, CBC_ORDER_ANY);
263         MUTATOR_HOOK(reset_map_players, arena_reset_map_players, CBC_ORDER_ANY);
264         MUTATOR_HOOK(MakePlayerObserver, arena_MakePlayerObserver, CBC_ORDER_ANY);
265         MUTATOR_HOOK(PutClientInServer, arena_PutClientInServer, CBC_ORDER_ANY);
266         MUTATOR_HOOK(ClientConnect, arena_ClientConnect, CBC_ORDER_ANY);
267         MUTATOR_HOOK(PlayerSpawn, arena_PlayerSpawn, CBC_ORDER_ANY);
268         MUTATOR_HOOK(ForbidPlayerScore_Clear, arena_ForbidPlayerScore_Clear, CBC_ORDER_ANY);
269         MUTATOR_HOOK(GiveFragsForKill, arena_GiveFragsForKill, CBC_ORDER_ANY);
270         MUTATOR_HOOK(PlayerDies, arena_PlayerDies, CBC_ORDER_ANY);
271         MUTATOR_HOOK(SV_StartFrame, arena_SV_StartFrame, CBC_ORDER_ANY);
272         MUTATOR_HOOK(FilterItem, arena_FilterItem, CBC_ORDER_ANY);
273
274         MUTATOR_ONADD
275         {
276                 if(time > 1) // game loads at time 1
277                         error("This is a game type and it cannot be added at runtime.");
278                 arena_Initialize();
279         }
280
281         MUTATOR_ONREMOVE
282         {
283                 print("This is a game type and it cannot be removed at runtime.");
284                 return -1;
285         }
286
287         return 0;
288 }