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