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