1 const float NMM_TURN_PLACE = 0x0100; // player has to place a piece on the board
2 const float NMM_TURN_MOVE = 0x0200; // player has to move a piece by one tile
3 const float NMM_TURN_FLY = 0x0400; // player has to move a piece anywhere
4 const float NMM_TURN_TAKE = 0x0800; // player has to take a non-mill piece
5 const float NMM_TURN_TAKEANY=0x1000; // combine with NMM_TURN_TAKE, can take mill pieces
6 const float NMM_TURN_WIN = 0x2000; // player has won
7 const float NMM_TURN_TYPE = 0xff00;
8 const float NMM_TURN_TEAM1 = 0x0001;
9 const float NMM_TURN_TEAM2 = 0x0002;
10 const float NMM_TURN_TEAM = 0x00ff;
12 const float NMM_PIECE_DEAD = 0x0; // captured by the enemy
13 const float NMM_PIECE_HOME = 0x1; // not yet placed
14 const float NMM_PIECE_BOARD = 0x2; // placed on the board
16 .float nmm_tile_distance;
17 .entity nmm_tile_piece;
18 .string nmm_tile_hmill;
19 .string nmm_tile_vmill;
21 // build a string containing the indices of the tile to check for a horizontal mill
22 string nmm_tile_build_hmill(entity tile)
24 float number = minigame_tile_number(tile.netname);
25 float letter = minigame_tile_letter(tile.netname);
26 if ( number == letter || number+letter == 6 )
28 float add = letter < 3 ? 1 : -1;
29 return strcat(tile.netname," ",
30 minigame_tile_buildname(letter+add*tile.nmm_tile_distance,number)," ",
31 minigame_tile_buildname(letter+add*2*tile.nmm_tile_distance,number) );
33 else if ( letter == 3 )
34 return strcat(minigame_tile_buildname(letter-tile.nmm_tile_distance,number)," ",
36 minigame_tile_buildname(letter+tile.nmm_tile_distance,number) );
37 else if ( letter < 3 )
38 return strcat(minigame_tile_buildname(0,number)," ",
39 minigame_tile_buildname(1,number)," ",
40 minigame_tile_buildname(2,number) );
42 return strcat(minigame_tile_buildname(4,number)," ",
43 minigame_tile_buildname(5,number)," ",
44 minigame_tile_buildname(6,number) );
47 // build a string containing the indices of the tile to check for a vertical mill
48 string nmm_tile_build_vmill(entity tile)
50 float letter = minigame_tile_letter(tile.netname);
51 float number = minigame_tile_number(tile.netname);
52 if ( letter == number || letter+number == 6 )
54 float add = number < 3 ? 1 : -1;
55 return strcat(tile.netname," ",
56 minigame_tile_buildname(letter,number+add*tile.nmm_tile_distance)," ",
57 minigame_tile_buildname(letter,number+add*2*tile.nmm_tile_distance) );
59 else if ( number == 3 )
60 return strcat(minigame_tile_buildname(letter,number-tile.nmm_tile_distance)," ",
62 minigame_tile_buildname(letter,number+tile.nmm_tile_distance) );
63 else if ( number < 3 )
64 return strcat(minigame_tile_buildname(letter,0)," ",
65 minigame_tile_buildname(letter,1)," ",
66 minigame_tile_buildname(letter,2) );
68 return strcat(minigame_tile_buildname(letter,4)," ",
69 minigame_tile_buildname(letter,5)," ",
70 minigame_tile_buildname(letter,6) );
74 // \param id Tile index (eg: a1)
75 // \param minig Owner minigame instance
76 // \param distance Distance from adjacent tiles
77 void nmm_spawn_tile(string id, entity minig, float distance)
79 // TODO global variable + list_next for simpler tile loops
81 e.origin = minigame_tile_pos(id,7,7);
82 e.classname = "minigame_nmm_tile";
86 e.nmm_tile_distance = distance;
87 e.nmm_tile_hmill = strzone(nmm_tile_build_hmill(e));
88 e.nmm_tile_vmill = strzone(nmm_tile_build_vmill(e));
91 // Create a tile square and recursively create inner squares
92 // \param minig Owner minigame instance
93 // \param offset Index offset (eg: 1 to start the square at b2, 0 at a1 etc.)
94 // \param skip Number of indices to skip between tiles (eg 1: a1, a3)
95 void nmm_spawn_tile_square( entity minig, float offset, float skip )
97 float letter = offset;
98 float number = offset;
100 for ( i = 0; i < 3; i++ )
103 for ( j = 0; j < 3; j++ )
105 if ( i != 1 || j != 1 )
106 nmm_spawn_tile(strzone(minigame_tile_buildname(letter,number)),minig, skip+1);
113 nmm_spawn_tile_square(minig,offset+1,skip-1);
116 // Remove tiles of a NMM minigame
117 void nmm_kill_tiles(entity minig)
120 while ( ( e = findentity(e,owner,minig) ) )
121 if ( e.classname == "minigame_nmm_tile" )
123 strunzone(e.netname);
124 strunzone(e.nmm_tile_hmill);
125 strunzone(e.nmm_tile_vmill);
130 // Create the tiles of a NMM minigame
131 void nmm_init_tiles(entity minig)
133 nmm_spawn_tile_square(minig,0,2);
136 // Find a tile by its id
137 entity nmm_find_tile(entity minig, string id)
140 while ( ( e = findentity(e,owner,minig) ) )
141 if ( e.classname == "minigame_nmm_tile" && e.netname == id )
146 // Check whether two tiles are adjacent
147 float nmm_tile_adjacent(entity tile1, entity tile2)
150 float dnumber = fabs ( minigame_tile_number(tile1.netname) - minigame_tile_number(tile2.netname) );
151 float dletter = fabs ( minigame_tile_letter(tile1.netname) - minigame_tile_letter(tile2.netname) );
153 return ( dnumber == 0 && ( dletter == 1 || dletter == tile1.nmm_tile_distance ) ) ||
154 ( dletter == 0 && ( dnumber == 1 || dnumber == tile1.nmm_tile_distance ) );
157 // Returns 1 if there is at least 1 free adjacent tile
158 float nmm_tile_canmove(entity tile)
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) )
170 // Check if the given tile id appears in the string
171 float nmm_in_mill_string(entity tile, string s)
173 float argc = tokenize(s);
175 for ( i = 0; i < argc; i++ )
177 entity e = nmm_find_tile(tile.owner,argv(i));
178 if ( !e || !e.nmm_tile_piece || e.nmm_tile_piece.team != tile.nmm_tile_piece.team )
184 // Check if a tile is in a mill
185 float nmm_in_mill(entity tile)
187 return tile.nmm_tile_piece && (
188 nmm_in_mill_string(tile,tile.nmm_tile_hmill) ||
189 nmm_in_mill_string(tile,tile.nmm_tile_vmill) );
194 // Find a NMM piece matching some of the given flags and team number
195 entity nmm_find_piece(entity start, entity minigame, float teamn, float pieceflags)
198 while ( ( e = findentity(e,owner,minigame) ) )
199 if ( e.classname == "minigame_board_piece" &&
200 (e.minigame_flags & pieceflags) && e.team == teamn )
205 // Count NMM pieces matching flags and team number
206 float nmm_count_pieces(entity minigame, float teamn, float pieceflags)
210 while (( e = nmm_find_piece(e,minigame, teamn, pieceflags) ))
215 // required function, handle server side events
216 float minigame_event_nmm(entity minigame, string event, ...)
218 if ( event == "start" )
220 minigame.minigame_flags = NMM_TURN_PLACE|NMM_TURN_TEAM1;
221 nmm_init_tiles(minigame);
224 for ( i = 0; i < 7; i++ )
226 e = msle_spawn(minigame,"minigame_board_piece");
228 e.minigame_flags = NMM_PIECE_HOME;
229 e = msle_spawn(minigame,"minigame_board_piece");
231 e.minigame_flags = NMM_PIECE_HOME;
236 else if ( event == "end" )
238 nmm_kill_tiles(minigame);
240 else if ( event == "join" )
244 for ( e = minigame.minigame_players; e; e = e.list_next )
248 if ( minigame.minigame_players && minigame.minigame_players.team == 1 )
252 else if ( event == "cmd" )
254 entity e = ...(0,entity);
255 float argc = ...(1,float);
257 entity piece = world;
260 if ( e && argc >= 2 && argv(0) == "move" &&
261 ( minigame.minigame_flags & NMM_TURN_TEAM ) == e.team )
263 tile = nmm_find_tile(minigame,argv(1));
268 else if ( minigame.minigame_flags & NMM_TURN_PLACE )
270 piece = nmm_find_piece(world,minigame,e.team,NMM_PIECE_HOME);
271 if ( !tile.nmm_tile_piece && piece )
273 tile.nmm_tile_piece = piece;
274 piece.minigame_flags = NMM_PIECE_BOARD;
275 piece.origin = tile.origin;
276 piece.SendFlags |= MINIG_SF_UPDATE;
280 else if ( minigame.minigame_flags & NMM_TURN_MOVE )
282 if ( tile.nmm_tile_piece && tile.nmm_tile_piece.team == e.team )
284 piece = tile.nmm_tile_piece;
285 entity tile2 = nmm_find_tile(minigame,argv(2));
286 if ( tile2 && nmm_tile_adjacent(tile,tile2) && !tile2.nmm_tile_piece )
288 tile.nmm_tile_piece = world;
289 tile2.nmm_tile_piece = piece;
290 piece.origin = tile2.origin;
291 piece.SendFlags |= MINIG_SF_UPDATE;
298 else if ( minigame.minigame_flags & NMM_TURN_FLY )
300 if ( tile.nmm_tile_piece && tile.nmm_tile_piece.team == e.team )
302 piece = tile.nmm_tile_piece;
303 entity tile2 = nmm_find_tile(minigame,argv(2));
304 if ( tile2 && !tile2.nmm_tile_piece )
306 tile.nmm_tile_piece = world;
307 tile2.nmm_tile_piece = piece;
308 piece.origin = tile2.origin;
309 piece.SendFlags |= MINIG_SF_UPDATE;
316 else if ( minigame.minigame_flags & NMM_TURN_TAKE )
318 piece = tile.nmm_tile_piece;
319 if ( piece && piece.nmm_tile_piece.team != e.team )
321 tile.nmm_tile_piece = world;
322 piece.minigame_flags = NMM_PIECE_DEAD;
323 piece.SendFlags |= MINIG_SF_UPDATE;
328 float nextteam = e.team % 2 + 1;
329 float npieces = nmm_count_pieces(minigame,nextteam,NMM_PIECE_HOME|NMM_PIECE_BOARD);
333 minigame.minigame_flags = NMM_TURN_WIN | e.team;
334 minigame.SendFlags |= MINIG_SF_UPDATE;
338 if ( !(minigame.minigame_flags & NMM_TURN_TAKE) && nmm_in_mill(tile) )
340 minigame.minigame_flags = NMM_TURN_TAKE|e.team;
341 float takemill = NMM_TURN_TAKEANY;
343 while ( ( f = findentity(f,owner,minigame) ) )
344 if ( f.classname == "minigame_nmm_tile" && f.nmm_tile_piece &&
345 f.nmm_tile_piece.team == nextteam && !nmm_in_mill(f) )
350 minigame.minigame_flags |= takemill;
354 if ( nmm_find_piece(world,minigame,nextteam,NMM_PIECE_HOME) )
355 minigame.minigame_flags = NMM_TURN_PLACE|nextteam;
356 else if ( npieces == 3 )
357 minigame.minigame_flags = NMM_TURN_FLY|nextteam;
360 minigame.minigame_flags = NMM_TURN_WIN|e.team;
362 while ( ( f = findentity(f,owner,minigame) ) )
363 if ( f.classname == "minigame_nmm_tile" && f.nmm_tile_piece &&
364 f.nmm_tile_piece.team == nextteam && nmm_tile_canmove(f) )
366 minigame.minigame_flags = NMM_TURN_MOVE|nextteam;
371 minigame.SendFlags |= MINIG_SF_UPDATE;
374 dprint("Invalid move: ",...(2,string),"\n");
387 vector nmm_boardsize;
389 // whether the given tile is a valid selection
390 float nmm_valid_selection(entity tile)
392 if ( ( tile.owner.minigame_flags & NMM_TURN_TEAM ) != minigame_self.team )
393 return 0; // not our turn
394 if ( tile.owner.minigame_flags & NMM_TURN_PLACE )
395 return !tile.nmm_tile_piece; // need to put a piece on an empty spot
396 if ( tile.owner.minigame_flags & NMM_TURN_MOVE )
398 if ( tile.nmm_tile_piece && tile.nmm_tile_piece.team == minigame_self.team &&
399 nmm_tile_canmove(tile) )
400 return 1; // movable tile
401 if ( nmm_fromtile ) // valid destination
402 return !tile.nmm_tile_piece && nmm_tile_adjacent(nmm_fromtile,tile);
405 if ( tile.owner.minigame_flags & NMM_TURN_FLY )
408 return !tile.nmm_tile_piece;
410 return tile.nmm_tile_piece && tile.nmm_tile_piece.team == minigame_self.team;
412 if ( tile.owner.minigame_flags & NMM_TURN_TAKE )
413 return tile.nmm_tile_piece && tile.nmm_tile_piece.team != minigame_self.team &&
414 ( (tile.owner.minigame_flags & NMM_TURN_TAKEANY) || !nmm_in_mill(tile) );
418 // whether it should highlight valid tile selections
419 float nmm_draw_avaliable(entity tile)
421 if ( ( tile.owner.minigame_flags & NMM_TURN_TEAM ) != minigame_self.team )
423 if ( (tile.owner.minigame_flags & NMM_TURN_TAKE) )
425 if ( (tile.owner.minigame_flags & (NMM_TURN_FLY|NMM_TURN_MOVE)) && nmm_fromtile )
426 return !tile.nmm_tile_piece;
430 // Required function, draw the game board
431 void minigame_hud_board_nmm(vector pos, vector mySize)
433 minigame_hud_fitsqare(pos, mySize);
435 nmm_boardsize = mySize;
436 minigame_hud_simpleboard(pos,mySize,minigame_texture("nmm/board"));
438 vector tile_size = minigame_hud_denormalize_size('1 1 0'/7,pos,mySize);
441 FOREACH_MINIGAME_ENTITY(e)
443 if ( e.classname == "minigame_nmm_tile" )
445 tile_pos = minigame_hud_denormalize(e.origin,pos,mySize);
447 if ( e == nmm_fromtile )
449 minigame_drawpic_centered( tile_pos, minigame_texture("nmm/tile_active"),
450 tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
452 else if ( nmm_draw_avaliable(e) && nmm_valid_selection(e) )
454 minigame_drawpic_centered( tile_pos, minigame_texture("nmm/tile_available"),
455 tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
458 if ( e == nmm_currtile )
460 minigame_drawpic_centered( tile_pos, minigame_texture("nmm/tile_selected"),
461 tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_ADDITIVE );
464 if ( e.nmm_tile_piece )
466 minigame_drawpic_centered( tile_pos,
467 minigame_texture(strcat("nmm/piece",ftos(e.nmm_tile_piece.team))),
468 tile_size*0.8, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
471 //drawstring(tile_pos, e.netname, hud_fontsize, '1 0 0', 1, DRAWFLAG_NORMAL);
475 if ( active_minigame.minigame_flags & NMM_TURN_WIN )
477 vector winfs = hud_fontsize*2;
478 string playername = "";
479 FOREACH_MINIGAME_ENTITY(e)
480 if ( e.classname == "minigame_player" &&
481 e.team == (active_minigame.minigame_flags & NMM_TURN_TEAM) )
482 playername = GetPlayerName(e.minigame_playerslot-1);
484 vector win_pos = pos+eY*(mySize_y-winfs_y)/2;
486 win_sz = minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos,
487 sprintf("%s^7 won the game!",playername),
488 winfs, 0, DRAWFLAG_NORMAL, 0.5);
490 drawfill(win_pos-eY*hud_fontsize_y,win_sz+2*eY*hud_fontsize_y,'1 1 1',0.5,DRAWFLAG_ADDITIVE);
492 minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos,
493 sprintf("%s^7 won the game!",playername),
494 winfs, panel_fg_alpha, DRAWFLAG_NORMAL, 0.5);
498 // Required function, draw the game status panel
499 void minigame_hud_status_nmm(vector pos, vector mySize)
504 ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message,
505 hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5);
509 vector player_fontsize = hud_fontsize * 1.75;
510 ts_y = ( mySize_y - 2*player_fontsize_y ) / 2;
515 vector piece_sz = '48 48 0';
516 float piece_space = piece_sz_x + ( ts_x - 7 * piece_sz_x ) / 6;
518 float piece_light = 1;
522 if ( (active_minigame.minigame_flags&NMM_TURN_TEAM) == 2 )
523 mypos_y += player_fontsize_y + ts_y;
524 drawfill(mypos,eX*mySize_x+eY*player_fontsize_y,'1 1 1',0.5,DRAWFLAG_ADDITIVE);
525 mypos_y += player_fontsize_y;
526 drawfill(mypos,eX*mySize_x+eY*piece_sz_y,'1 1 1',0.25,DRAWFLAG_ADDITIVE);
528 FOREACH_MINIGAME_ENTITY(e)
530 if ( e.classname == "minigame_player" )
534 mypos_y += player_fontsize_y + ts_y;
535 minigame_drawcolorcodedstring_trunc(mySize_x,mypos,
536 GetPlayerName(e.minigame_playerslot-1),
537 player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
539 else if ( e.classname == "minigame_board_piece" )
542 mypos_y += player_fontsize_y;
546 player2x += piece_space;
547 mypos_y += player_fontsize_y + ts_y;
552 player1x += piece_space;
554 if ( e.minigame_flags == NMM_PIECE_HOME )
556 else if ( e.minigame_flags == NMM_PIECE_BOARD )
561 drawpic(mypos, minigame_texture(strcat("nmm/piece",ftos(e.team))), piece_sz,
562 '1 1 1'*piece_light, panel_fg_alpha, DRAWFLAG_NORMAL );
567 // Make the correct move
568 void nmm_make_move(entity minigame)
572 if ( minigame.minigame_flags & (NMM_TURN_PLACE|NMM_TURN_TAKE) )
574 minigame_cmd("move ",nmm_currtile.netname);
575 nmm_fromtile = world;
577 else if ( (minigame.minigame_flags & (NMM_TURN_MOVE|NMM_TURN_FLY)) )
579 if ( nmm_fromtile == nmm_currtile )
581 nmm_fromtile = world;
583 else if ( nmm_currtile.nmm_tile_piece && nmm_currtile.nmm_tile_piece.team == minigame_self.team )
585 nmm_fromtile = nmm_currtile;
587 else if ( nmm_fromtile )
589 minigame_cmd("move ",nmm_fromtile.netname," ",nmm_currtile.netname);
590 nmm_fromtile = world;
595 nmm_fromtile = world;
598 string nmm_turn_to_string(float turnflags)
600 if ( turnflags & NMM_TURN_WIN )
602 if ( (turnflags&NMM_TURN_TEAM) != minigame_self.team )
603 return _("You lost the game!");
604 return _("You win!");
607 if ( (turnflags&NMM_TURN_TEAM) != minigame_self.team )
608 return _("Wait for your opponent to make their move");
609 if ( turnflags & NMM_TURN_PLACE )
610 return _("Click on the game board to place your piece");
611 if ( turnflags & NMM_TURN_MOVE )
612 return _("You can select one of your pieces to move it in one of the surrounding places");
613 if ( turnflags & NMM_TURN_FLY )
614 return _("You can select one of your pieces to move it anywhere on the board");
615 if ( turnflags & NMM_TURN_TAKE )
616 return _("You can take one of the opponent's pieces");
621 // Required function, handle client events
622 float minigame_event_nmm(entity minigame, string event, ...)
624 if ( event == "activate" )
626 nmm_fromtile = world;
627 nmm_init_tiles(minigame);
628 minigame.message = nmm_turn_to_string(minigame.minigame_flags);
630 else if ( event == "deactivate" )
632 nmm_fromtile = world;
633 nmm_kill_tiles(minigame);
635 else if ( event == "key_pressed" && (minigame.minigame_flags&NMM_TURN_TEAM) == minigame_self.team )
637 switch ( ...(0,float) )
640 case K_KP_RIGHTARROW:
641 if ( ! nmm_currtile )
642 nmm_currtile = nmm_find_tile(active_minigame,"a7");
645 string tileid = nmm_currtile.netname;
646 nmm_currtile = world;
647 while ( !nmm_currtile )
649 tileid = minigame_relative_tile(tileid,1,0,7,7);
650 nmm_currtile = nmm_find_tile(active_minigame,tileid);
656 if ( ! nmm_currtile )
657 nmm_currtile = nmm_find_tile(active_minigame,"g7");
660 string tileid = nmm_currtile.netname;
661 nmm_currtile = world;
662 while ( !nmm_currtile )
664 tileid = minigame_relative_tile(tileid,-1,0,7,7);
665 nmm_currtile = nmm_find_tile(active_minigame,tileid);
671 if ( ! nmm_currtile )
672 nmm_currtile = nmm_find_tile(active_minigame,"a1");
675 string tileid = nmm_currtile.netname;
676 nmm_currtile = world;
677 while ( !nmm_currtile )
679 tileid = minigame_relative_tile(tileid,0,1,7,7);
680 nmm_currtile = nmm_find_tile(active_minigame,tileid);
686 if ( ! nmm_currtile )
687 nmm_currtile = nmm_find_tile(active_minigame,"a7");
690 string tileid = nmm_currtile.netname;
691 nmm_currtile = world;
692 while ( !nmm_currtile )
694 tileid = minigame_relative_tile(tileid,0,-1,7,7);
695 nmm_currtile = nmm_find_tile(active_minigame,tileid);
702 nmm_make_move(minigame);
707 else if ( event == "mouse_pressed" && ...(0,float) == K_MOUSE1 )
709 nmm_make_move(minigame);
712 else if ( event == "mouse_moved" )
714 nmm_currtile = world;
716 vector tile_size = minigame_hud_denormalize_size('1 1 0'/7,nmm_boardpos,nmm_boardsize);
718 FOREACH_MINIGAME_ENTITY(e)
720 if ( e.classname == "minigame_nmm_tile" )
722 tile_pos = minigame_hud_denormalize(e.origin,nmm_boardpos,nmm_boardsize)-tile_size/2;
723 if ( minigame_hud_mouse_in(tile_pos, tile_size) && nmm_valid_selection(e) )
732 else if ( event == "network_receive" )
734 if ( self.classname == "minigame_board_piece" && ( ...(1,float) & MINIG_SF_UPDATE ) )
738 if ( self.minigame_flags & NMM_PIECE_BOARD )
739 tileid = minigame_tile_name(self.origin,7,7);
740 FOREACH_MINIGAME_ENTITY(e)
742 if ( e.classname == "minigame_nmm_tile" )
744 if ( e.nmm_tile_piece == self )
745 e.nmm_tile_piece = world;
746 if ( e.netname == tileid )
747 e.nmm_tile_piece = self;
751 else if ( self.classname == "minigame" && ( ...(1,float) & MINIG_SF_UPDATE ) )
753 self.message = nmm_turn_to_string(self.minigame_flags);
754 if ( self.minigame_flags & minigame_self.team )