]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/mutators/gamemode_arena.qc
Calling Arena_AddChallengers() before the game starts g_start_delay works as expected...
[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_GetTimeLeft() <= 0)
64         {
65                 FOR_EACH_REALCLIENT(e)
66                         centerprint(e, "Round over, there's no winner");
67                 bprint("Round over, there's no winner\n");
68                 round_handler_Init(5, autocvar_g_arena_warmup, autocvar_g_arena_round_timelimit);
69                 return 1;
70         }
71
72         if(numspawned > 1)
73                 return 0;
74
75         entity champion;
76         champion = world;
77         FOR_EACH_CLIENT(e)
78         {
79                 if(e.spawned && e.classname == "player")
80                         champion = e;
81         }
82
83         if(champion)
84         {
85                 FOR_EACH_REALCLIENT(e)
86                         centerprint(e, strcat("The Champion is ", champion.netname));
87                 bprint("The Champion is ", champion.netname, "\n");
88                 UpdateFrags(champion, +1);
89         }
90         else
91         {
92                 FOR_EACH_REALCLIENT(e)
93                         centerprint(e, "Round tied");
94                 bprint("Round tied\n");
95         }
96         round_handler_Init(5, autocvar_g_arena_warmup, autocvar_g_arena_round_timelimit);
97         return 1;
98 }
99
100 void Arena_AddChallengers()
101 {
102         entity e;
103         e = self;
104         while(numspawned < maxspawned && spawnqueue_first)
105         {
106                 self = spawnqueue_first;
107
108                 bprint ("^4", self.netname, "^4 is the next challenger\n");
109
110                 Spawnqueue_Remove(self);
111                 Spawnqueue_Mark(self);
112
113                 self.classname = "player";
114                 PutClientInServer();
115         }
116         self = e;
117 }
118
119 float prev_numspawned;
120 float Arena_CheckPlayers()
121 {
122         entity e;
123
124         Arena_AddChallengers();
125
126         if(numspawned >= 2)
127         {
128                 if(prev_numspawned != -1)
129                 {
130                         FOR_EACH_REALCLIENT(e)
131                                 Send_CSQC_Centerprint_Generic_Expire(e, CPID_WAITING_PLAYERS);
132                 }
133                 prev_numspawned = -1;
134                 return 1;
135         }
136
137         if(prev_numspawned != numspawned && numspawned == 1)
138         {
139                 FOR_EACH_REALCLIENT(e)
140                         Send_CSQC_Centerprint_Generic(e, CPID_WAITING_PLAYERS, "Waiting for players to join...", -1, 0);
141                 prev_numspawned = numspawned;
142         }
143
144         return 0;
145 }
146
147 void Arena_RoundStart()
148 {
149         entity e;
150         FOR_EACH_PLAYER(e)
151                 e.player_blocked = 0;
152 }
153
154 MUTATOR_HOOKFUNCTION(arena_ClientDisconnect)
155 {
156         Spawnqueue_Unmark(self);
157         Spawnqueue_Remove(self);
158         return 1;
159 }
160
161 MUTATOR_HOOKFUNCTION(arena_reset_map_players)
162 {
163         FOR_EACH_CLIENT(self)
164         {
165                 if(self.spawned)
166                 {
167                         PutClientInServer();
168                         self.player_blocked = 1;
169                 }
170                 else
171                         PutObserverInServer();
172         }
173         return 1;
174 }
175
176 MUTATOR_HOOKFUNCTION(arena_MakePlayerObserver)
177 {
178         if(self.version_mismatch)
179         {
180                 self.frags = FRAGS_SPECTATOR;
181                 Spawnqueue_Unmark(self);
182                 Spawnqueue_Remove(self);
183         }
184         else
185         {
186                 self.frags = FRAGS_LMS_LOSER;
187                 Spawnqueue_Insert(self);
188         }
189         return 1;
190 }
191
192 MUTATOR_HOOKFUNCTION(arena_PutClientInServer)
193 {
194         if(!self.spawned)
195                 self.classname = "observer";
196         return 1;
197 }
198
199 MUTATOR_HOOKFUNCTION(arena_ClientConnect)
200 {
201         self.classname = "observer";
202         Spawnqueue_Insert(self);
203         return 1;
204 }
205
206 MUTATOR_HOOKFUNCTION(arena_PlayerSpawn)
207 {
208         Spawnqueue_Remove(self);
209         Spawnqueue_Mark(self);
210         if(arena_roundbased)
211                 self.player_blocked = 1;
212         return 1;
213 }
214
215 MUTATOR_HOOKFUNCTION(arena_PlayerPreThink)
216 {
217         self.stat_respawn_time = 0;
218
219         // put dead players in the spawn queue
220         if(arena_roundbased)
221         if(self.deadflag && time - self.death_time >= 1.5)
222                 PutClientInServer();
223
224         return 1;
225 }
226
227 MUTATOR_HOOKFUNCTION(arena_ForbidPlayerScore_Clear)
228 {
229         return 1;
230 }
231
232 MUTATOR_HOOKFUNCTION(arena_GiveFragsForKill)
233 {
234         if(arena_roundbased)
235                 frag_score = 0; // score will be given to the champion when the round ends
236         return 1;
237 }
238
239 MUTATOR_HOOKFUNCTION(arena_PlayerDies)
240 {
241         Spawnqueue_Unmark(self);
242         return 1;
243 }
244
245 MUTATOR_HOOKFUNCTION(arena_SV_StartFrame)
246 {
247         if(gameover) return 1;
248         if(time <= game_starttime || !arena_roundbased)
249                 Arena_AddChallengers();
250         return 1;
251 }
252
253 void arena_Initialize()
254 {
255         maxspawned = max(2, autocvar_g_arena_maxspawned);
256         arena_roundbased = autocvar_g_arena_roundbased;
257         if(arena_roundbased)
258         {
259                 round_handler_Spawn(Arena_CheckPlayers, Arena_CheckWinner, Arena_RoundStart);
260                 round_handler_Init(5, autocvar_g_arena_warmup, autocvar_g_arena_round_timelimit);
261         }
262 }
263
264 MUTATOR_DEFINITION(gamemode_arena)
265 {
266         MUTATOR_HOOK(ClientDisconnect, arena_ClientDisconnect, CBC_ORDER_ANY);
267         MUTATOR_HOOK(reset_map_players, arena_reset_map_players, CBC_ORDER_ANY);
268         MUTATOR_HOOK(MakePlayerObserver, arena_MakePlayerObserver, CBC_ORDER_ANY);
269         MUTATOR_HOOK(PutClientInServer, arena_PutClientInServer, CBC_ORDER_ANY);
270         MUTATOR_HOOK(ClientConnect, arena_ClientConnect, CBC_ORDER_ANY);
271         MUTATOR_HOOK(PlayerSpawn, arena_PlayerSpawn, CBC_ORDER_ANY);
272         MUTATOR_HOOK(PlayerPreThink, arena_PlayerPreThink, CBC_ORDER_ANY);
273         MUTATOR_HOOK(ForbidPlayerScore_Clear, arena_ForbidPlayerScore_Clear, CBC_ORDER_ANY);
274         MUTATOR_HOOK(GiveFragsForKill, arena_GiveFragsForKill, CBC_ORDER_ANY);
275         MUTATOR_HOOK(PlayerDies, arena_PlayerDies, CBC_ORDER_ANY);
276         MUTATOR_HOOK(SV_StartFrame, arena_SV_StartFrame, CBC_ORDER_ANY);
277
278         MUTATOR_ONADD
279         {
280                 if(time > 1) // game loads at time 1
281                         error("This is a game type and it cannot be added at runtime.");
282                 arena_Initialize();
283         }
284
285         MUTATOR_ONREMOVE
286         {
287                 print("This is a game type and it cannot be removed at runtime.");
288                 return -1;
289         }
290
291         return 0;
292 }