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