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