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