]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/minigames/sv_minigames.qc
Snake: the game (ladders not included)
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / minigames / sv_minigames.qc
1 #include "minigames.qh"
2
3 void player_clear_minigame(entity player)
4 {
5         player.active_minigame = world;
6         player.minigame_players = world;
7         if ( IS_PLAYER(player) )
8                 player.movetype = MOVETYPE_WALK;
9         else
10                 player.movetype = MOVETYPE_FLY_WORLDONLY;
11         player.team_forced = 0;
12 }
13
14 void minigame_rmplayer(entity minigame_session, entity player)
15 {
16         entity e;
17         entity p = minigame_session.minigame_players;
18
19         if ( p.minigame_players == player )
20         {
21                 if ( p.list_next == world )
22                 {
23                         end_minigame(minigame_session);
24                         return;
25                 }
26                 minigame_session.minigame_event(minigame_session,"part",player);
27                 GameLogEcho(strcat(":minigame:part:",minigame_session.netname,":",
28                         ftos(num_for_edict(player)),":",player.netname));
29                 minigame_session.minigame_players = p.list_next;
30                 remove ( p );
31                 player_clear_minigame(player);
32         }
33         else
34         {
35                 for ( e = p.list_next; e != world; e = e.list_next )
36                 {
37                         if ( e.minigame_players == player )
38                         {
39                                 minigame_session.minigame_event(minigame_session,"part",player);
40                                 GameLogEcho(strcat(":minigame:part:",minigame_session.netname,":",
41                                         ftos(num_for_edict(player)),":",player.netname));
42                                 p.list_next = e.list_next;
43                                 remove(e);
44                                 player_clear_minigame(player);
45                                 return;
46                         }
47                         p = e;
48                 }
49         }
50 }
51
52
53 #define FIELD(Flags, Type,Name) if ( sf & (Flags) ) Write##Type(MSG_ENTITY, self.Name);
54 #define WriteVector(to,Name) WriteCoord(to,Name##_x); WriteCoord(to,Name##_y); WriteCoord(to,Name##_z)
55 #define WriteVector2D(to,Name) WriteCoord(to,Name##_x); WriteCoord(to,Name##_y)
56 #define MSLE(Name,Fields) \
57         else if ( self.classname == #Name ) { \
58                 if ( sf & MINIG_SF_CREATE ) WriteString(MSG_ENTITY,self.owner.netname); \
59                 Fields }
60
61 // Send an entity to a client
62 // only use on minigame entities or entities with a minigame owner
63 bool minigame_SendEntity(entity to, int sf)
64 {SELFPARAM();
65         WriteByte(MSG_ENTITY, ENT_CLIENT_MINIGAME);
66         WriteByte(MSG_ENTITY, sf);
67
68         if ( sf & MINIG_SF_CREATE )
69         {
70                 WriteShort(MSG_ENTITY,msle_id(self.classname));
71                 WriteString(MSG_ENTITY,self.netname);
72         }
73
74         entity minigame_ent = self.owner;
75
76         if ( self.classname == "minigame" )
77         {
78                 minigame_ent = self;
79
80                 if ( sf & MINIG_SF_CREATE )
81                         WriteString(MSG_ENTITY,self.descriptor.netname);
82
83                 if ( sf & MINIG_SF_UPDATE )
84                         WriteLong(MSG_ENTITY,self.minigame_flags);
85         }
86         else if ( self.classname == "minigame_player" )
87         {
88                 if ( sf & MINIG_SF_CREATE )
89                 {
90                         WriteString(MSG_ENTITY,self.owner.netname);
91                         WriteLong(MSG_ENTITY,num_for_edict(self.minigame_players));
92                 }
93                 if ( sf & MINIG_SF_UPDATE )
94                         WriteByte(MSG_ENTITY,self.team);
95         }
96         MINIGAME_SIMPLELINKED_ENTITIES
97
98         minigame_ent.minigame_event(minigame_ent,"network_send",self,sf);
99
100         return true;
101
102 }
103 #undef FIELD
104 #undef MSLE
105
106 // Force resend all minigame entities
107 void minigame_resend(entity minigame)
108 {
109         minigame.SendFlags = MINIG_SF_ALL;
110         entity e = world;
111         while (( e = findentity(e,owner,minigame) ))
112         {
113                 e.SendFlags = MINIG_SF_ALL;
114         }
115 }
116
117 bool minigame_CheckSend()
118 {SELFPARAM();
119         entity e;
120         for ( e = self.owner.minigame_players; e != world; e = e.list_next )
121                 if ( e.minigame_players == other )
122                         return true;
123         return false;
124 }
125
126 int minigame_addplayer(entity minigame_session, entity player)
127 {SELFPARAM();
128         if ( player.active_minigame )
129         {
130                 if ( player.active_minigame == minigame_session )
131                         return 0;
132                 minigame_rmplayer(player.active_minigame,player);
133         }
134
135         int mgteam = minigame_session.minigame_event(minigame_session,"join",player);
136
137         if ( mgteam )
138         {
139                 entity player_pointer = spawn();
140                 player_pointer.classname = "minigame_player";
141                 player_pointer.owner = minigame_session;
142                 player_pointer.minigame_players = player;
143                 player_pointer.team = mgteam;
144                 player_pointer.list_next = minigame_session.minigame_players;
145                 minigame_session.minigame_players = player_pointer;
146                 player.active_minigame = minigame_session;
147                 player.minigame_players = player_pointer;
148                 player_pointer.customizeentityforclient = minigame_CheckSend;
149                 Net_LinkEntity(player_pointer, false, 0, minigame_SendEntity);
150
151                 if ( !IS_OBSERVER(player) && autocvar_sv_minigames_observer )
152                 {
153                         WITH(entity, self, player, PutObserverInServer());
154                 }
155                 if ( autocvar_sv_minigames_observer == 2 )
156                         player.team_forced = -1;
157
158                 minigame_resend(minigame_session);
159         }
160         GameLogEcho(strcat(":minigame:join",(mgteam?"":"fail"),":",minigame_session.netname,":",
161                 ftos(num_for_edict(player)),":",player.netname));
162
163         return mgteam;
164 }
165
166 void minigame_frame()
167 {
168         entity minig = self;
169
170         minig.minigame_event(minig,"frame");
171         minig.nextthink = time;
172 }
173
174 entity start_minigame(entity player, string minigame )
175 {
176         if ( !autocvar_sv_minigames || !IS_REAL_CLIENT(player) )
177                 return world;
178
179         entity e = minigame_get_descriptor(minigame);
180         if ( e )
181         {
182                 entity minig = spawn();
183                 minig.classname = "minigame";
184                 minig.netname = strzone(strcat(e.netname,"_",ftos(num_for_edict(minig))));
185                 minig.descriptor = e;
186                 minig.think = minigame_frame;
187                 minig.nextthink = time;
188                 minig.minigame_event = e.minigame_event;
189                 minig.minigame_event(minig,"start");
190                 GameLogEcho(strcat(":minigame:start:",minig.netname));
191                 if ( ! minigame_addplayer(minig,player) )
192                 {
193                         LOG_TRACE("Minigame ",minig.netname," rejected the first player join!\n");
194                         end_minigame(minig);
195                         return world;
196                 }
197                 Net_LinkEntity(minig, false, 0, minigame_SendEntity);
198
199                 if ( !minigame_sessions )
200                         minigame_sessions = minig;
201                 else
202                 {
203                         minigame_sessions.owner = minig;
204                         minig.list_next = minigame_sessions;
205                         minigame_sessions = minig;
206                 }
207                 return minig;
208         }
209
210         return world;
211 }
212
213 entity join_minigame(entity player, string game_id )
214 {
215         if ( !autocvar_sv_minigames || !IS_REAL_CLIENT(player) )
216                 return world;
217
218         entity minig;
219         for ( minig = minigame_sessions; minig != world; minig = minig.list_next )
220         {
221                 if ( minig.netname == game_id )
222                 if ( minigame_addplayer(minig,player) )
223                         return minig;
224         }
225
226         return world;
227 }
228
229 void part_minigame(entity player )
230 {
231         entity minig = player.active_minigame;
232
233         if ( minig && minig.classname == "minigame" )
234                 minigame_rmplayer(minig,player);
235 }
236
237 void end_minigame(entity minigame_session)
238 {
239         if ( minigame_session.owner )
240                 minigame_session.owner.list_next = minigame_session.list_next;
241         else
242                 minigame_sessions = minigame_session.list_next;
243
244         minigame_session.minigame_event(minigame_session,"end");
245         GameLogEcho(strcat(":minigame:end:",minigame_session.netname));
246
247
248         entity e = world;
249         while( (e = findentity(e, owner, minigame_session)) )
250                 if ( e.minigame_autoclean )
251                 {
252                         LOG_TRACE("SV Auto-cleaned: ",ftos(num_for_edict(e)), " (",e.classname,")\n");
253                         remove(e);
254                 }
255
256         entity p;
257         for ( e = minigame_session.minigame_players; e != world; e = p )
258         {
259                 p = e.list_next;
260                 player_clear_minigame(e.minigame_players);
261                 remove(e);
262         }
263
264         strunzone(minigame_session.netname);
265         remove(minigame_session);
266 }
267
268 void end_minigames()
269 {
270         while ( minigame_sessions )
271         {
272                 end_minigame(minigame_sessions);
273         }
274 }
275
276 void initialize_minigames()
277 {
278         entity last_minig = world;
279         entity minig;
280         #define MINIGAME(name,nicename) \
281                 minig = spawn(); \
282                 minig.classname = "minigame_descriptor"; \
283                 minig.netname = #name; \
284                 minig.message = nicename; \
285                 minig.minigame_event = name##_server_event; \
286                 if ( !last_minig ) minigame_descriptors = minig; \
287                 else last_minig.list_next = minig; \
288                 last_minig = minig;
289
290         REGISTERED_MINIGAMES
291
292         #undef MINIGAME
293 }
294
295 string invite_minigame(entity inviter, entity player)
296 {
297         if ( !inviter || !inviter.active_minigame )
298                 return "Invalid minigame";
299         if ( !VerifyClientEntity(player, true, false) )
300                 return "Invalid player";
301         if ( inviter == player )
302                 return "You can't invite yourself";
303         if ( player.active_minigame == inviter.active_minigame )
304                 return strcat(player.netname," is already playing");
305
306         Send_Notification(NOTIF_ONE, player, MSG_INFO, INFO_MINIGAME_INVITE,
307                 inviter.active_minigame.netname, inviter.netname );
308
309         GameLogEcho(strcat(":minigame:invite:",inviter.active_minigame.netname,":",
310                 ftos(num_for_edict(player)),":",player.netname));
311
312         return "";
313 }
314
315 entity minigame_find_player(entity client)
316 {
317         if ( ! client.active_minigame )
318                 return world;
319         entity e;
320         for ( e = client.active_minigame.minigame_players; e; e = e.list_next )
321                 if ( e.minigame_players == client )
322                         return e;
323         return world;
324 }
325
326 bool MinigameImpulse(int imp)
327 {SELFPARAM();
328         entity e = minigame_find_player(self);
329         if ( imp && self.active_minigame && e )
330         {
331                 return self.active_minigame.minigame_event(self.active_minigame,"impulse",e,imp);
332         }
333         return false;
334 }
335
336
337
338 void ClientCommand_minigame(int request, int argc, string command)
339 {SELFPARAM();
340         if ( !autocvar_sv_minigames )
341         {
342                 sprint(self,"Minigames are not enabled!\n");
343                 return;
344         }
345
346         if (request == CMD_REQUEST_COMMAND )
347         {
348                 string minig_cmd = argv(1);
349                 if ( minig_cmd == "create" && argc > 2 )
350                 {
351                         entity minig = start_minigame(self, argv(2));
352                         if ( minig )
353                                 sprint(self,"Created minigame session: ",minig.netname,"\n");
354                         else
355                                 sprint(self,"Cannot start minigame session!\n");
356                         return;
357                 }
358                 else if ( minig_cmd == "join" && argc > 2 )
359                 {
360                         entity minig = join_minigame(self, argv(2));
361                         if ( minig )
362                                 sprint(self,"Joined: ",minig.netname,"\n");
363                         else
364                         {
365                                 Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_JOIN_PREVENT_MINIGAME);
366                                 sprint(self,"Cannot join given minigame session!\n");
367                         }
368                         return;
369                 }
370                 else if ( minig_cmd == "list" )
371                 {
372                         entity e;
373                         for ( e = minigame_descriptors; e != world; e = e.list_next )
374                                 sprint(self,e.netname," (",e.message,") ","\n");
375                         return;
376                 }
377                 else if ( minig_cmd == "list-sessions" )
378                 {
379                         entity e;
380                         for ( e = minigame_sessions; e != world; e = e.list_next )
381                                 sprint(self,e.netname,"\n");
382                         return;
383                 }
384                 else if ( minig_cmd == "end" || minig_cmd == "part" )
385                 {
386                         if ( self.active_minigame )
387                         {
388                                 part_minigame(self);
389                                 sprint(self,"Left minigame session\n");
390                         }
391                         else
392                                 sprint(self,"You aren't playing any minigame...\n");
393                         return;
394                 }
395                 else if ( minig_cmd == "invite" && argc > 2 )
396                 {
397                         if ( self.active_minigame )
398                         {
399                                 entity client = GetIndexedEntity(argc, 2);
400                                 string error = invite_minigame(self,client);
401                                 if ( error == "" )
402                                 {
403                                         sprint(self,"You have invited ",client.netname,
404                                                 " to join your game of ", self.active_minigame.descriptor.message, "\n");
405                                 }
406                                 else
407                                         sprint(self,"Could not invite: ", error, ".\n");
408                         }
409                         else
410                                 sprint(self,"You aren't playing any minigame...\n");
411                         return;
412                 }
413                 else if ( self.active_minigame )
414                 {
415                         entity e = minigame_find_player(self);
416                         string subcommand = substring(command,argv_end_index(0),-1);
417                         int arg_c = tokenize_console(subcommand);
418                         if ( self.active_minigame.minigame_event(self.active_minigame,"cmd",e,arg_c,subcommand) )
419                                 return;
420
421                 }
422                 else sprint(self,strcat("Wrong command:^1 ",command,"\n"));
423         }
424
425         sprint(self, "\nUsage:^3 cmd minigame create <minigame>\n");
426         sprint(self, "  Start a new minigame session\n");
427         sprint(self, "Usage:^3 cmd minigame join <session>\n");
428         sprint(self, "  Join an exising minigame session\n");
429         sprint(self, "Usage:^3 cmd minigame list\n");
430         sprint(self, "  List available minigames\n");
431         sprint(self, "Usage:^3 cmd minigame list-sessions\n");
432         sprint(self, "  List available minigames sessions\n");
433         sprint(self, "Usage:^3 cmd minigame part|end\n");
434         sprint(self, "  Leave the current minigame\n");
435         sprint(self, "Usage:^3 cmd minigame invite <player>\n");
436         sprint(self, "  Invite the given player to join you in a minigame\n");
437 }