]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/minigames/minigame/nmm.qc
Show spectator list on all minigames
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / minigames / minigame / nmm.qc
1 #include "nmm.qh"
2
3 REGISTER_MINIGAME(nmm, _("Nine Men's Morris"));
4
5 const int NMM_TURN_PLACE = 0x0100; // player has to place a piece on the board
6 const int NMM_TURN_MOVE  = 0x0200; // player has to move a piece by one tile
7 const int NMM_TURN_FLY   = 0x0400; // player has to move a piece anywhere
8 const int NMM_TURN_TAKE  = 0x0800; // player has to take a non-mill piece
9 const int NMM_TURN_TAKEANY=0x1000; // combine with NMM_TURN_TAKE, can take mill pieces
10 const int NMM_TURN_WIN   = 0x2000; // player has won
11 const int NMM_TURN_TYPE  = 0xff00;
12 const int NMM_TURN_TEAM1 = 0x0001;
13 const int NMM_TURN_TEAM2 = 0x0002;
14 const int NMM_TURN_TEAM  = 0x00ff;
15
16 const int NMM_PIECE_DEAD  = 0x0; // captured by the enemy
17 const int NMM_PIECE_HOME  = 0x1; // not yet placed
18 const int NMM_PIECE_BOARD = 0x2; // placed on the board
19
20 const int NMM_SPECTATOR_TEAM = 255; // must be above max teams and equal to or below 255
21
22 .int  nmm_tile_distance;
23 .entity nmm_tile_piece;
24 .string nmm_tile_hmill;
25 .string nmm_tile_vmill;
26
27 // build a string containing the indices of the tile to check for a horizontal mill
28 string nmm_tile_build_hmill(entity tile)
29 {
30         int number = minigame_tile_number(tile.netname);
31         int letter = minigame_tile_letter(tile.netname);
32         if ( number == letter || number+letter == 6 )
33         {
34                 int add = letter < 3 ? 1 : -1;
35                 return strcat(tile.netname," ",
36                         minigame_tile_buildname(letter+add*tile.nmm_tile_distance,number)," ",
37                         minigame_tile_buildname(letter+add*2*tile.nmm_tile_distance,number) );
38         }
39         else if ( letter == 3 )
40                 return strcat(minigame_tile_buildname(letter-tile.nmm_tile_distance,number)," ",
41                         tile.netname," ",
42                         minigame_tile_buildname(letter+tile.nmm_tile_distance,number) );
43         else if ( letter < 3 )
44                 return strcat(minigame_tile_buildname(0,number)," ",
45                         minigame_tile_buildname(1,number)," ",
46                         minigame_tile_buildname(2,number) );
47         else
48                 return strcat(minigame_tile_buildname(4,number)," ",
49                         minigame_tile_buildname(5,number)," ",
50                         minigame_tile_buildname(6,number) );
51 }
52
53 // build a string containing the indices of the tile to check for a vertical mill
54 string nmm_tile_build_vmill(entity tile)
55 {
56         int letter = minigame_tile_letter(tile.netname);
57         int number = minigame_tile_number(tile.netname);
58         if ( letter == number || letter+number == 6 )
59         {
60                 int add = number < 3 ? 1 : -1;
61                 return strcat(tile.netname," ",
62                         minigame_tile_buildname(letter,number+add*tile.nmm_tile_distance)," ",
63                         minigame_tile_buildname(letter,number+add*2*tile.nmm_tile_distance) );
64         }
65         else if ( number == 3 )
66                 return strcat(minigame_tile_buildname(letter,number-tile.nmm_tile_distance)," ",
67                         tile.netname," ",
68                         minigame_tile_buildname(letter,number+tile.nmm_tile_distance) );
69         else if ( number < 3 )
70                 return strcat(minigame_tile_buildname(letter,0)," ",
71                         minigame_tile_buildname(letter,1)," ",
72                         minigame_tile_buildname(letter,2) );
73         else
74                 return strcat(minigame_tile_buildname(letter,4)," ",
75                         minigame_tile_buildname(letter,5)," ",
76                         minigame_tile_buildname(letter,6) );
77 }
78
79 // Create an new tile
80 // \param id       Tile index (eg: a1)
81 // \param minig    Owner minigame instance
82 // \param distance Distance from adjacent tiles
83 void nmm_spawn_tile(string id, entity minig, int distance)
84 {
85         // TODO global variable + list_next for simpler tile loops
86         entity e = new(minigame_nmm_tile);
87         e.origin = minigame_tile_pos(id,7,7);
88         e.netname = id;
89         e.owner = minig;
90         e.team = 0;
91         e.nmm_tile_distance = distance;
92         e.nmm_tile_hmill = strzone(nmm_tile_build_hmill(e));
93         e.nmm_tile_vmill = strzone(nmm_tile_build_vmill(e));
94 }
95
96 // Create a tile square and recursively create inner squares
97 // \param minig    Owner minigame instance
98 // \param offset   Index offset (eg: 1 to start the square at b2, 0 at a1 etc.)
99 // \param skip     Number of indices to skip between tiles (eg 1: a1, a3)
100 void nmm_spawn_tile_square( entity minig, int offset, int skip )
101 {
102         int letter = offset;
103         int number = offset;
104         for ( int i = 0; i < 3; i++ )
105         {
106                 number = offset;
107                 for ( int j = 0; j < 3; j++ )
108                 {
109                         if ( i != 1 || j != 1 )
110                                 nmm_spawn_tile(strzone(minigame_tile_buildname(letter,number)),minig, skip+1);
111                         number += skip+1;
112                 }
113                 letter += skip+1;
114         }
115
116         if ( skip > 0 )
117                 nmm_spawn_tile_square(minig,offset+1,skip-1);
118 }
119
120 // Remove tiles of a NMM minigame
121 void nmm_kill_tiles(entity minig)
122 {
123         entity e = NULL;
124         while ( ( e = findentity(e,owner,minig) ) )
125                 if ( e.classname == "minigame_nmm_tile" )
126                 {
127                         strfree(e.netname);
128                         strfree(e.nmm_tile_hmill);
129                         strfree(e.nmm_tile_vmill);
130                         delete(e);
131                 }
132 }
133
134 // Create the tiles of a NMM minigame
135 void nmm_init_tiles(entity minig)
136 {
137         nmm_spawn_tile_square(minig,0,2);
138 }
139
140 // Find a tile by its id
141 entity nmm_find_tile(entity minig, string id)
142 {
143         entity e = NULL;
144         while ( ( e = findentity(e,owner,minig) ) )
145                 if ( e.classname == "minigame_nmm_tile" && e.netname == id )
146                         return e;
147         return NULL;
148 }
149
150 // Check whether two tiles are adjacent
151 bool nmm_tile_adjacent(entity tile1, entity tile2)
152 {
153
154         int dnumber = fabs ( minigame_tile_number(tile1.netname) - minigame_tile_number(tile2.netname) );
155         int dletter = fabs ( minigame_tile_letter(tile1.netname) - minigame_tile_letter(tile2.netname) );
156
157         return ( dnumber == 0 && ( dletter == 1 || dletter == tile1.nmm_tile_distance ) ) ||
158                 ( dletter == 0 && ( dnumber == 1 || dnumber == tile1.nmm_tile_distance ) );
159 }
160
161 // Returns 1 if there is at least 1 free adjacent tile
162 bool nmm_tile_canmove(entity tile)
163 {
164         entity e = NULL;
165         while ( ( e = findentity(e,owner,tile.owner) ) )
166                 if ( e.classname == "minigame_nmm_tile" && !e.nmm_tile_piece
167                                 && nmm_tile_adjacent(e,tile) )
168                 {
169                         return true;
170                 }
171         return false;
172 }
173
174 // Check if the given tile id appears in the string
175 bool nmm_in_mill_string(entity tile, string s)
176 {
177         int argc = tokenize(s);
178         for ( int i = 0; i < argc; i++ )
179         {
180                 entity e = nmm_find_tile(tile.owner,argv(i));
181                 if ( !e || !e.nmm_tile_piece || e.nmm_tile_piece.team != tile.nmm_tile_piece.team )
182                         return false;
183         }
184         return true;
185 }
186
187 // Check if a tile is in a mill
188 bool nmm_in_mill(entity tile)
189 {
190         return tile.nmm_tile_piece &&  (
191                 nmm_in_mill_string(tile,tile.nmm_tile_hmill) ||
192                 nmm_in_mill_string(tile,tile.nmm_tile_vmill) );
193 }
194
195
196 #ifdef SVQC
197 // Find a NMM piece matching some of the given flags and team number
198 entity nmm_find_piece(entity start, entity minigame, int teamn, int pieceflags)
199 {
200         entity e = start;
201         while ( ( e = findentity(e,owner,minigame) ) )
202                 if ( e.classname == "minigame_board_piece" &&
203                                 (e.minigame_flags & pieceflags) && e.team == teamn )
204                         return e;
205         return NULL;
206 }
207
208 // Count NMM pieces matching flags and team number
209 int nmm_count_pieces(entity minigame, int teamn, int pieceflags)
210 {
211         int n = 0;
212         entity e = NULL;
213         while (( e = nmm_find_piece(e,minigame, teamn, pieceflags) ))
214                 n++;
215         return n;
216 }
217
218 // required function, handle server side events
219 int nmm_server_event(entity minigame, string event, ...)
220 {
221         if ( event == "start" )
222         {
223                 minigame.minigame_flags = NMM_TURN_PLACE|NMM_TURN_TEAM1;
224                 nmm_init_tiles(minigame);
225                 entity e;
226                 for ( int i = 0; i < 7; i++ )
227                 {
228                         e = msle_spawn(minigame,new(minigame_board_piece));
229                         e.team = 1;
230                         e.minigame_flags = NMM_PIECE_HOME;
231                         e = msle_spawn(minigame,new(minigame_board_piece));
232                         e.team = 2;
233                         e.minigame_flags = NMM_PIECE_HOME;
234                 }
235
236                 return 1;
237         }
238         else if ( event == "end" )
239         {
240                 nmm_kill_tiles(minigame);
241         }
242         else if ( event == "join" )
243         {
244                 int n = 0;
245                 entity e;
246                 for ( e = minigame.minigame_players; e; e = e.list_next )
247                         n++;
248                 if ( n >= 2 )
249                         return NMM_SPECTATOR_TEAM;
250                 if ( minigame.minigame_players && minigame.minigame_players.team == 1 )
251                         return 2;
252                 return 1;
253         }
254         else if ( event == "cmd" )
255         {
256                 entity e = ...(0,entity);
257                 int argc = ...(1,int);
258                 entity tile = NULL;
259                 entity piece = NULL;
260                 bool move_ok = false;
261
262                 if ( e && argc >= 2 && argv(0) == "move" &&
263                         ( minigame.minigame_flags & NMM_TURN_TEAM ) == e.team )
264                 {
265                         tile = nmm_find_tile(minigame,argv(1));
266                         if ( !tile )
267                         {
268                                 move_ok = false;
269                         }
270                         else if ( minigame.minigame_flags & NMM_TURN_PLACE )
271                         {
272                                 piece = nmm_find_piece(NULL,minigame,e.team,NMM_PIECE_HOME);
273                                 if ( !tile.nmm_tile_piece && piece )
274                                 {
275                                         tile.nmm_tile_piece = piece;
276                                         piece.minigame_flags = NMM_PIECE_BOARD;
277                                         piece.origin = tile.origin;
278                                         piece.SendFlags |= MINIG_SF_UPDATE;
279                                         move_ok = true;
280                                 }
281                         }
282                         else if ( minigame.minigame_flags & NMM_TURN_MOVE )
283                         {
284                                 if ( tile.nmm_tile_piece && tile.nmm_tile_piece.team == e.team )
285                                 {
286                                         piece = tile.nmm_tile_piece;
287                                         entity tile2 = nmm_find_tile(minigame,argv(2));
288                                         if ( tile2 && nmm_tile_adjacent(tile,tile2) && !tile2.nmm_tile_piece )
289                                         {
290                                                 tile.nmm_tile_piece = NULL;
291                                                 tile2.nmm_tile_piece = piece;
292                                                 piece.origin = tile2.origin;
293                                                 piece.SendFlags |= MINIG_SF_UPDATE;
294                                                 tile = tile2;
295                                                 move_ok = true;
296                                         }
297                                 }
298
299                         }
300                         else if ( minigame.minigame_flags & NMM_TURN_FLY )
301                         {
302                                 if ( tile.nmm_tile_piece && tile.nmm_tile_piece.team == e.team )
303                                 {
304                                         piece = tile.nmm_tile_piece;
305                                         entity tile2 = nmm_find_tile(minigame,argv(2));
306                                         if ( tile2 && !tile2.nmm_tile_piece )
307                                         {
308                                                 tile.nmm_tile_piece = NULL;
309                                                 tile2.nmm_tile_piece = piece;
310                                                 piece.origin = tile2.origin;
311                                                 piece.SendFlags |= MINIG_SF_UPDATE;
312                                                 tile = tile2;
313                                                 move_ok = true;
314                                         }
315                                 }
316
317                         }
318                         else if ( minigame.minigame_flags & NMM_TURN_TAKE )
319                         {
320                                 piece = tile.nmm_tile_piece;
321                                 if ( piece && piece.nmm_tile_piece.team != e.team )
322                                 {
323                                         tile.nmm_tile_piece = NULL;
324                                         piece.minigame_flags = NMM_PIECE_DEAD;
325                                         piece.SendFlags |= MINIG_SF_UPDATE;
326                                         move_ok = true;
327                                 }
328                         }
329
330                         int nextteam = e.team % 2 + 1;
331                         int npieces = nmm_count_pieces(minigame,nextteam,NMM_PIECE_HOME|NMM_PIECE_BOARD);
332
333                         if ( npieces < 3 )
334                         {
335                                 minigame.minigame_flags = NMM_TURN_WIN | e.team;
336                                 minigame.SendFlags |= MINIG_SF_UPDATE;
337                         }
338                         else if ( move_ok)
339                         {
340                                 if ( !(minigame.minigame_flags & NMM_TURN_TAKE) && nmm_in_mill(tile) )
341                                 {
342                                         minigame.minigame_flags = NMM_TURN_TAKE|e.team;
343                                         int takemill = NMM_TURN_TAKEANY;
344                                         entity f = NULL;
345                                         while ( ( f = findentity(f,owner,minigame) ) )
346                                                 if ( f.classname == "minigame_nmm_tile" && f.nmm_tile_piece  &&
347                                                                 f.nmm_tile_piece.team == nextteam && !nmm_in_mill(f) )
348                                                 {
349                                                         takemill = 0;
350                                                         break;
351                                                 }
352                                         minigame.minigame_flags |= takemill;
353                                 }
354                                 else
355                                 {
356                                         if ( nmm_find_piece(NULL,minigame,nextteam,NMM_PIECE_HOME) )
357                                                 minigame.minigame_flags = NMM_TURN_PLACE|nextteam;
358                                         else if ( npieces == 3 )
359                                                 minigame.minigame_flags = NMM_TURN_FLY|nextteam;
360                                         else
361                                         {
362                                                 minigame.minigame_flags = NMM_TURN_WIN|e.team;
363                                                 entity f = NULL;
364                                                 while ( ( f = findentity(f,owner,minigame) ) )
365                                                         if ( f.classname == "minigame_nmm_tile" && f.nmm_tile_piece  &&
366                                                                 f.nmm_tile_piece.team == nextteam && nmm_tile_canmove(f) )
367                                                         {
368                                                                 minigame.minigame_flags = NMM_TURN_MOVE|nextteam;
369                                                                 break;
370                                                         }
371                                         }
372                                 }
373                                 minigame.SendFlags |= MINIG_SF_UPDATE;
374                         }
375                         else
376                                 LOG_TRACE("Invalid move: ", ...(2, string));
377                         return 1;
378                 }
379         }
380         return 0;
381 }
382
383 #elif defined(CSQC)
384
385 entity nmm_currtile;
386 entity nmm_fromtile;
387
388 vector nmm_boardpos;
389 vector nmm_boardsize;
390
391 // whether the given tile is a valid selection
392 bool nmm_valid_selection(entity tile)
393 {
394         if ( ( tile.owner.minigame_flags & NMM_TURN_TEAM ) != minigame_self.team )
395                 return false; // not our turn
396         if ( tile.owner.minigame_flags & NMM_TURN_PLACE )
397                 return !tile.nmm_tile_piece; // need to put a piece on an empty spot
398         if ( tile.owner.minigame_flags & NMM_TURN_MOVE )
399         {
400                 if ( tile.nmm_tile_piece && tile.nmm_tile_piece.team == minigame_self.team &&
401                                 nmm_tile_canmove(tile) )
402                         return true; //  movable tile
403                 if ( nmm_fromtile ) // valid destination
404                         return !tile.nmm_tile_piece && nmm_tile_adjacent(nmm_fromtile,tile);
405                 return false;
406         }
407         if ( tile.owner.minigame_flags & NMM_TURN_FLY )
408         {
409                 if ( nmm_fromtile )
410                         return !tile.nmm_tile_piece;
411                 else
412                         return tile.nmm_tile_piece && tile.nmm_tile_piece.team == minigame_self.team;
413         }
414         if ( tile.owner.minigame_flags & NMM_TURN_TAKE )
415                 return tile.nmm_tile_piece && tile.nmm_tile_piece.team != minigame_self.team &&
416                         ( (tile.owner.minigame_flags & NMM_TURN_TAKEANY) || !nmm_in_mill(tile) );
417         return false;
418 }
419
420 // whether it should highlight valid tile selections
421 bool nmm_draw_avaliable(entity tile)
422 {
423         if ( ( tile.owner.minigame_flags & NMM_TURN_TEAM ) != minigame_self.team )
424                 return false;
425         if ( (tile.owner.minigame_flags & NMM_TURN_TAKE) )
426                 return true;
427         if ( (tile.owner.minigame_flags & (NMM_TURN_FLY|NMM_TURN_MOVE)) && nmm_fromtile )
428                 return !tile.nmm_tile_piece;
429         return false;
430 }
431
432 // Required function, draw the game board
433 void nmm_hud_board(vector pos, vector mySize)
434 {
435         minigame_hud_fitsqare(pos, mySize);
436         nmm_boardpos = pos;
437         nmm_boardsize = mySize;
438         minigame_hud_simpleboard(pos,mySize,minigame_texture("nmm/board"));
439
440         vector tile_size = minigame_hud_denormalize_size('1 1 0'/7,pos,mySize);
441         vector tile_pos;
442         entity e;
443         FOREACH_MINIGAME_ENTITY(e)
444         {
445                 if ( e.classname == "minigame_nmm_tile" )
446                 {
447                         tile_pos = minigame_hud_denormalize(e.origin,pos,mySize);
448
449                         if ( e == nmm_fromtile )
450                         {
451                                 minigame_drawpic_centered( tile_pos, minigame_texture("nmm/tile_active"),
452                                         tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
453                         }
454                         else if ( nmm_draw_avaliable(e) && nmm_valid_selection(e) )
455                         {
456                                 minigame_drawpic_centered( tile_pos, minigame_texture("nmm/tile_available"),
457                                         tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
458                         }
459
460                         if ( e == nmm_currtile )
461                         {
462                                 minigame_drawpic_centered( tile_pos, minigame_texture("nmm/tile_selected"),
463                                         tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_ADDITIVE );
464                         }
465
466                         if ( e.nmm_tile_piece )
467                         {
468                                 minigame_drawpic_centered( tile_pos,
469                                         minigame_texture(strcat("nmm/piece",ftos(e.nmm_tile_piece.team))),
470                                         tile_size*0.8, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
471                         }
472
473                         //drawstring(tile_pos, e.netname, hud_fontsize, '1 0 0', 1, DRAWFLAG_NORMAL);
474                 }
475         }
476
477         if ( active_minigame.minigame_flags & NMM_TURN_WIN )
478         {
479                 vector winfs = hud_fontsize*2;
480                 string pname = "";
481                 FOREACH_MINIGAME_ENTITY(e)
482                         if ( e.classname == "minigame_player" &&
483                                         e.team == (active_minigame.minigame_flags & NMM_TURN_TEAM) )
484                                 pname = entcs_GetName(e.minigame_playerslot-1);
485
486                 vector win_pos = pos+eY*(mySize_y-winfs_y)/2;
487                 vector win_sz;
488                 win_sz = minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos,
489                         sprintf(_("%s^7 won the game!"), pname),
490                         winfs, 0, DRAWFLAG_NORMAL, 0.5);
491
492                 drawfill(win_pos-eY*hud_fontsize_y,win_sz+2*eY*hud_fontsize_y,'1 1 1',0.5,DRAWFLAG_ADDITIVE);
493
494                 minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos,
495                         sprintf(_("%s^7 won the game!"), pname),
496                         winfs, panel_fg_alpha, DRAWFLAG_NORMAL, 0.5);
497         }
498
499         minigame_show_allspecs(nmm_boardpos, nmm_boardsize);
500 }
501
502 // Required function, draw the game status panel
503 void nmm_hud_status(vector pos, vector mySize)
504 {
505         HUD_Panel_DrawBg();
506         vector ts;
507         ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message,
508                 hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5);
509         pos_y += ts_y;
510         mySize_y -= ts_y;
511
512         vector player_fontsize = hud_fontsize * 1.75;
513         ts_y = ( mySize_y - 2*player_fontsize_y ) / 2;
514         ts_x = mySize_x;
515
516         float player1x = 0;
517         float player2x = 0;
518         vector piece_sz = '48 48 0';
519         float piece_space = piece_sz_x + ( ts_x - 7 * piece_sz_x ) / 6;
520         vector mypos;
521         float piece_light = 1;
522         entity e = NULL;
523
524         if(minigame_self.team != NMM_SPECTATOR_TEAM)
525         {
526                 mypos = pos;
527                 if ( (active_minigame.minigame_flags&NMM_TURN_TEAM) == 2 )
528                         mypos_y  += player_fontsize_y + ts_y;
529                 drawfill(mypos,eX*mySize_x+eY*player_fontsize_y,'1 1 1',0.5,DRAWFLAG_ADDITIVE);
530                 mypos_y += player_fontsize_y;
531                 drawfill(mypos,eX*mySize_x+eY*piece_sz_y,'1 1 1',0.25,DRAWFLAG_ADDITIVE);
532         }
533
534         FOREACH_MINIGAME_ENTITY(e)
535         {
536                 if ( e.classname == "minigame_player" && e.team != NMM_SPECTATOR_TEAM )
537                 {
538                         mypos = pos;
539                         if ( e.team == 2 )
540                                 mypos_y  += player_fontsize_y + ts_y;
541                         minigame_drawcolorcodedstring_trunc(mySize_x,mypos,
542                                 entcs_GetName(e.minigame_playerslot-1),
543                                 player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
544                 }
545                 else if ( e.classname == "minigame_board_piece" )
546                 {
547                         mypos = pos;
548                         mypos_y += player_fontsize_y;
549                         if ( e.team == 2 )
550                         {
551                                 mypos_x += player2x;
552                                 player2x += piece_space;
553                                 mypos_y  += player_fontsize_y + ts_y;
554                         }
555                         else
556                         {
557                                 mypos_x += player1x;
558                                 player1x += piece_space;
559                         }
560                         if ( e.minigame_flags == NMM_PIECE_HOME )
561                                 piece_light = 0.5;
562                         else if ( e.minigame_flags == NMM_PIECE_BOARD )
563                                 piece_light = 1;
564                         else
565                                 piece_light = 0.15;
566
567                         drawpic(mypos, minigame_texture(strcat("nmm/piece",ftos(e.team))), piece_sz,
568                                 '1 1 1'*piece_light, panel_fg_alpha, DRAWFLAG_NORMAL );
569                 }
570         }
571 }
572
573 // Make the correct move
574 void nmm_make_move(entity minigame)
575 {
576         if ( nmm_currtile )
577         {
578                 if ( minigame.minigame_flags & (NMM_TURN_PLACE|NMM_TURN_TAKE) )
579                 {
580                         minigame_cmd("move ",nmm_currtile.netname);
581                         nmm_fromtile = NULL;
582                 }
583                 else if ( (minigame.minigame_flags & (NMM_TURN_MOVE|NMM_TURN_FLY)) )
584                 {
585                         if ( nmm_fromtile == nmm_currtile )
586                         {
587                                 nmm_fromtile = NULL;
588                         }
589                         else if ( nmm_currtile.nmm_tile_piece && nmm_currtile.nmm_tile_piece.team == minigame_self.team )
590                         {
591                                 nmm_fromtile = nmm_currtile;
592                         }
593                         else if ( nmm_fromtile )
594                         {
595                                 minigame_cmd("move ",nmm_fromtile.netname," ",nmm_currtile.netname);
596                                 nmm_fromtile = NULL;
597                         }
598                 }
599         }
600         else
601                 nmm_fromtile = NULL;
602 }
603
604 string nmm_turn_to_string(int turnflags)
605 {
606         if( minigame_self.team == NMM_SPECTATOR_TEAM )
607                 return _("You are spectating");
608
609         if ( turnflags & NMM_TURN_WIN )
610         {
611                 if ( (turnflags&NMM_TURN_TEAM) != minigame_self.team )
612                         return _("You lost the game!");
613                 return _("You win!");
614         }
615
616         if ( (turnflags&NMM_TURN_TEAM) != minigame_self.team )
617                 return _("Wait for your opponent to make their move");
618         if ( turnflags & NMM_TURN_PLACE )
619                 return _("Click on the game board to place your piece");
620         if ( turnflags & NMM_TURN_MOVE )
621                 return _("You can select one of your pieces to move it in one of the surrounding places");
622         if ( turnflags & NMM_TURN_FLY )
623                 return _("You can select one of your pieces to move it anywhere on the board");
624         if ( turnflags & NMM_TURN_TAKE )
625                 return _("You can take one of the opponent's pieces");
626
627         return "";
628 }
629
630 // Required function, handle client events
631 int nmm_client_event(entity minigame, string event, ...)
632 {
633         if ( event == "activate" )
634         {
635                 nmm_fromtile = NULL;
636                 nmm_init_tiles(minigame);
637                 strcpy(minigame.message, nmm_turn_to_string(minigame.minigame_flags));
638         }
639         else if ( event == "deactivate" )
640         {
641                 nmm_fromtile = NULL;
642                 nmm_kill_tiles(minigame);
643                 strfree(minigame.message);
644         }
645         else if ( (event == "key_pressed" || event == "key_released") )
646         {
647                 bool event_blocked = ((event == "key_released")
648                         || ((minigame.minigame_flags & NMM_TURN_TEAM) != minigame_self.team));
649                 if (!(minigame.minigame_flags & NMM_TURN_WIN))
650                 {
651                         switch ( ...(0,int) )
652                         {
653                                 case K_RIGHTARROW:
654                                 case K_KP_RIGHTARROW:
655                                         if (event_blocked)
656                                                 return true;
657                                         if ( ! nmm_currtile )
658                                                 nmm_currtile = nmm_find_tile(active_minigame,"a7");
659                                         else
660                                         {
661                                                 string tileid = nmm_currtile.netname;
662                                                 nmm_currtile = NULL;
663                                                 while ( !nmm_currtile )
664                                                 {
665                                                         tileid = minigame_relative_tile(tileid,1,0,7,7);
666                                                         nmm_currtile = nmm_find_tile(active_minigame,tileid);
667                                                 }
668                                         }
669                                         return 1;
670                                 case K_LEFTARROW:
671                                 case K_KP_LEFTARROW:
672                                         if (event_blocked)
673                                                 return true;
674                                         if ( ! nmm_currtile )
675                                                 nmm_currtile = nmm_find_tile(active_minigame,"g7");
676                                         else
677                                         {
678                                                 string tileid = nmm_currtile.netname;
679                                                 nmm_currtile = NULL;
680                                                 while ( !nmm_currtile )
681                                                 {
682                                                         tileid = minigame_relative_tile(tileid,-1,0,7,7);
683                                                         nmm_currtile = nmm_find_tile(active_minigame,tileid);
684                                                 }
685                                         }
686                                         return 1;
687                                 case K_UPARROW:
688                                 case K_KP_UPARROW:
689                                         if (event_blocked)
690                                                 return true;
691                                         if ( ! nmm_currtile )
692                                                 nmm_currtile = nmm_find_tile(active_minigame,"a1");
693                                         else
694                                         {
695                                                 string tileid = nmm_currtile.netname;
696                                                 nmm_currtile = NULL;
697                                                 while ( !nmm_currtile )
698                                                 {
699                                                         tileid = minigame_relative_tile(tileid,0,1,7,7);
700                                                         nmm_currtile = nmm_find_tile(active_minigame,tileid);
701                                                 }
702                                         }
703                                         return 1;
704                                 case K_DOWNARROW:
705                                 case K_KP_DOWNARROW:
706                                         if (event_blocked)
707                                                 return true;
708                                         if ( ! nmm_currtile )
709                                                 nmm_currtile = nmm_find_tile(active_minigame,"a7");
710                                         else
711                                         {
712                                                 string tileid = nmm_currtile.netname;
713                                                 nmm_currtile = NULL;
714                                                 while ( !nmm_currtile )
715                                                 {
716                                                         tileid = minigame_relative_tile(tileid,0,-1,7,7);
717                                                         nmm_currtile = nmm_find_tile(active_minigame,tileid);
718                                                 }
719                                         }
720                                         return 1;
721                                 case K_ENTER:
722                                 case K_KP_ENTER:
723                                 case K_SPACE:
724                                         if (event_blocked)
725                                                 return true;
726                                         nmm_make_move(minigame);
727                                         return 1;
728                         }
729                 }
730                 return 0;
731         }
732         else if ( event == "mouse_pressed" && ...(0,int) == K_MOUSE1 )
733         {
734                 nmm_client_event(minigame, "mouse_moved");
735                 nmm_make_move(minigame);
736                 return 1;
737         }
738         else if ( event == "mouse_moved" )
739         {
740                 nmm_currtile = NULL;
741                 vector tile_pos;
742                 vector tile_size = minigame_hud_denormalize_size('1 1 0'/7,nmm_boardpos,nmm_boardsize);
743                 entity e;
744                 FOREACH_MINIGAME_ENTITY(e)
745                 {
746                         if ( e.classname == "minigame_nmm_tile" )
747                         {
748                                 tile_pos = minigame_hud_denormalize(e.origin,nmm_boardpos,nmm_boardsize)-tile_size/2;
749                                 if ( minigame_hud_mouse_in(tile_pos, tile_size) && nmm_valid_selection(e) )
750                                 {
751                                         nmm_currtile = e;
752                                         break;
753                                 }
754                         }
755                 }
756                 return 1;
757         }
758         else if ( event == "network_receive" )
759         {
760                 entity sent = ...(0,entity);
761
762                 if ( sent.classname == "minigame_board_piece" && ( ...(1,int) & MINIG_SF_UPDATE ) )
763                 {
764                         entity e;
765                         string tileid = "";
766                         if ( sent.minigame_flags & NMM_PIECE_BOARD )
767                                 tileid = minigame_tile_name(sent.origin,7,7);
768                         FOREACH_MINIGAME_ENTITY(e)
769                         {
770                                 if ( e.classname == "minigame_nmm_tile" )
771                                 {
772                                         if ( e.nmm_tile_piece == sent )
773                                                 e.nmm_tile_piece = NULL;
774                                         if ( e.netname == tileid )
775                                                 e.nmm_tile_piece = sent;
776                                 }
777                         }
778                 }
779                 else if ( sent.classname == "minigame" && ( ...(1,int) & MINIG_SF_UPDATE ) )
780                 {
781                         strcpy(sent.message, nmm_turn_to_string(sent.minigame_flags));
782                         if ( sent.minigame_flags & minigame_self.team )
783                                 minigame_prompt();
784                 }
785         }
786
787         return 0;
788 }
789
790 #endif