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