Subversion Repositories Games.Chess Giants

Rev

Rev 3 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. // window_main.cpp
  2.  
  3. #include "../common.h"
  4.  
  5.  
  6. // prototypes of local functions
  7. static bool Button_IsHovered (guibutton_t *button, int gui_x, int gui_y);
  8. static bool Button_UpdateHoverState (guibutton_t *button, int gui_x, int gui_y);
  9.  
  10.  
  11. LRESULT CALLBACK WindowProc_Main (HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam)
  12. {
  13.    // this is the main message handler for the program
  14.  
  15.    static wchar_t fen_string[128];
  16.    static char selectable_parts[14] = "PRNBQK kqbnrp";
  17.    static int prevgui_x = 0;
  18.    static int prevgui_y = 0;
  19.    static bool rbutton_pushed = false;
  20.  
  21.    unsigned short wParam_hiword;
  22.    unsigned short wParam_loword;
  23.    int prev_hovered_position[2];
  24.    player_t *current_player;
  25.    player_t *opposite_player;
  26.    player_t *local_player;
  27.    player_t *remote_player;
  28.    ccreply_t *entered_ccreply;
  29.    boardmove_t *current_move;
  30.    float board_x;
  31.    float board_y;
  32.    int gui_x;
  33.    int gui_y;
  34.    int line;
  35.    int column;
  36.    int index_line;
  37.    int index_column;
  38.    int part_index;
  39.    int part_color;
  40.  
  41.    // filter out the commonly used message values
  42.    wParam_hiword = HIWORD (wParam);
  43.    wParam_loword = LOWORD (wParam);
  44.  
  45.    ////////////////////////////////////////////////////////////////////////////////////////////////
  46.    // has the window just been fired up ?
  47.    if (message == WM_CREATE)
  48.    {
  49.       the_scene.gui.is_entering_text = false; // we are NOT entering text yet
  50.  
  51.       // call the default window message processing function to keep things going
  52.       return (DefWindowProc (hWnd, message, wParam, lParam));
  53.    }
  54.  
  55.    ////////////////////////////////////////////////////////////////////////////////////////////////
  56.    // else have we clicked on the close button ?
  57.    else if (message == WM_CLOSE)
  58.    {
  59.       if (the_board.game_state == STATE_PLAYING)
  60.          DialogBox_Quit (); // if a game has started, ask for confirmation
  61.       else
  62.          is_dialogbox_quit_validated = true; // if game hasn't started yet, quit without question
  63.  
  64.       return (0); // don't let Windows do the default processing on this message
  65.    }
  66.  
  67.    ////////////////////////////////////////////////////////////////////////////////////////////////
  68.    // else is the user closing its session OR the window is being destroyed ?
  69.    else if ((message == WM_QUERYENDSESSION) || (message == WM_ENDSESSION) || (message == WM_DESTROY))
  70.       terminate_everything = true; // suicide (for some reason, PostQuitMessage() is unreliable !?)
  71.  
  72.    ////////////////////////////////////////////////////////////////////////////////////////////////
  73.    // menu event
  74.    else if (message == WM_COMMAND)
  75.    {
  76.       // game menu, new game
  77.       if (wParam_loword == MENUID_GAME_NEWGAME)
  78.       {
  79.          if ((the_board.game_state == STATE_PLAYING) && (the_board.move_count > 1))
  80.             DialogBox_Resign (); // if a game is playing, ask to resign first
  81.          else
  82.             DialogBox_NewGame (); // game not started yet, show the new game dialog box
  83.       }
  84.  
  85.       // game menu, setup start position
  86.       else if (wParam_loword == MENUID_GAME_SETUPPOSITION)
  87.       {
  88.          if ((the_board.game_state == STATE_PLAYING) && (the_board.move_count > 1))
  89.             DialogBox_Resign (); // if a game is playing, ask to resign first
  90.          else
  91.          {
  92.             current_move = &the_board.moves[0]; // quick access to start move
  93.             memset (&current_move->slots, 0, sizeof (current_move->slots)); // erase all slots
  94.  
  95.             the_board.game_state = STATE_SETUPPOSITION; // game not started yet, enter board setup mode
  96.             the_board.reevaluate = true; // evaluate board again
  97.  
  98.             // display the "please choose start position" phrase in the middle of the screen
  99.             Scene_SetText (&the_scene.gui.central_text, 50.0f, 50.0f, -1, ALIGN_CENTER, ALIGN_CENTER, ALIGN_CENTER, centermsg_fontindex, RGBA_TO_RGBACOLOR (255, 255, 255, 191),
  100.                            999999.0f, true, LOCALIZE (L"SetupMode"));
  101.             the_scene.gui.partspick_selectedpart = ' '; // no selected part yet
  102.             the_scene.update = true; // and update the 3D scene
  103.          }
  104.       }
  105.  
  106.       // game menu, load game
  107.       else if (wParam_loword == MENUID_GAME_LOAD)
  108.          DialogBox_Load (); // fire up the load dialog box
  109.  
  110.       // game menu, save game
  111.       else if (wParam_loword == MENUID_GAME_SAVE)
  112.       {
  113.          if (save_pathname[0] != 0)
  114.             is_dialogbox_save_validated = true; // if the filename is known, save it directly
  115.          else
  116.             DialogBox_Save (); // else fire up the save dialog box
  117.       }
  118.  
  119.       // game menu, save as
  120.       else if (wParam_loword == MENUID_GAME_SAVEAS)
  121.          DialogBox_Save (); // fire up the save dialog box
  122.  
  123.       // game menu, save position as
  124.       else if (wParam_loword == MENUID_GAME_SAVEPOSITIONAS)
  125.          DialogBox_SavePosition (); // fire up the save position dialog box
  126.  
  127.       // game menu, resign
  128.       else if (wParam_loword == MENUID_GAME_RESIGN)
  129.          DialogBox_Resign (); // if a game has started, ask for confirmation
  130.  
  131.       // game menu, statistics (stats are only available in online mode)
  132.       else if (wParam_loword == MENUID_GAME_STATISTICS)
  133.       {
  134.          local_player = Player_FindByType (PLAYER_HUMAN);
  135.          if (local_player != NULL)
  136.             PlayerCard_FindOrCreate (local_player->name); // display player's own player card
  137.       }
  138.  
  139.       // game menu, options
  140.       else if (wParam_loword == MENUID_GAME_OPTIONS)
  141.          DialogBox_Options (); // fire up the options dialog box
  142.  
  143.       // game menu, quit
  144.       else if (wParam_loword == MENUID_GAME_QUIT)
  145.       {
  146.          if (the_board.game_state == STATE_PLAYING)
  147.             DialogBox_Quit (); // if a game has started, ask for confirmation
  148.          else
  149.             is_dialogbox_quit_validated = true; // if game hasn't started yet, quit without question
  150.       }
  151.  
  152.       // chessboard menu, suggest move
  153.       else if (wParam_loword == MENUID_CHESSBOARD_SUGGESTMOVE)
  154.          the_board.players[current_viewer].wants_hint = true; // remember this player wants a hint
  155.  
  156.       // chessboard menu, cancel last move
  157.       else if (wParam_loword == MENUID_CHESSBOARD_CANCELLASTMOVE)
  158.          the_board.players[current_viewer].wants_cancel = true; // remember this player wants to cancel his last move
  159.  
  160.       // chessboard menu, comment move
  161.       else if (wParam_loword == MENUID_CHESSBOARD_COMMENTMOVE)
  162.          DialogBox_Comment (); // fire up the comment dialog box
  163.  
  164.       // chessboard menu, go to move
  165.       else if (wParam_loword == MENUID_CHESSBOARD_GOTOMOVE)
  166.          DialogBox_GoToMove (); // fire up the go to move dialog box
  167.  
  168.       // chessboard menu, swap sides
  169.       else if (wParam_loword == MENUID_CHESSBOARD_SWAPSIDES)
  170.          the_board.want_playerswap = true; // remember board sides are to be swapped
  171.  
  172.       // chessboard menu, beginning of game
  173.       else if (wParam_loword == MENUID_CHESSBOARD_BEGINNINGOFGAME)
  174.       {
  175.          the_board.viewed_move = 0; // enter view mode and go to the beginning of the game
  176.          Audio_PlaySound (SOUNDTYPE_CLICK); // make a click sound
  177.          the_board.reevaluate = true; // evaluate board again
  178.       }
  179.  
  180.       // chessboard menu, previous move (only if arrow is enabled)
  181.       else if ((wParam_loword == MENUID_CHESSBOARD_PREVIOUSMOVE) && (the_scene.gui.larrow.state != 0))
  182.       {
  183.          the_board.viewed_move--; // enter view mode and go back one move
  184.          Audio_PlaySound (SOUNDTYPE_CLICK); // make a click sound
  185.          the_board.reevaluate = true; // evaluate board again
  186.       }
  187.  
  188.       // chessboard menu, next move (only if arrow is enabled)
  189.       else if ((wParam_loword == MENUID_CHESSBOARD_NEXTMOVE) && (the_scene.gui.rarrow.state != 0))
  190.       {
  191.          the_board.viewed_move++; // enter view mode and go forward one move
  192.          Audio_PlaySound (SOUNDTYPE_CLICK); // make a click sound
  193.          the_board.reevaluate = true; // evaluate board again
  194.       }
  195.  
  196.       // chessboard menu, current state of game
  197.       else if (wParam_loword == MENUID_CHESSBOARD_CURRENTSTATEOFGAME)
  198.       {
  199.          the_board.viewed_move = the_board.move_count - 1; // enter view mode and go to the current state of the game
  200.          Audio_PlaySound (SOUNDTYPE_CLICK); // make a click sound
  201.          the_board.reevaluate = true; // evaluate board again
  202.       }
  203.  
  204.       // chessboard menu, top view
  205.       else if (wParam_loword == MENUID_CHESSBOARD_TOPVIEW)
  206.       {
  207.          the_board.players[current_viewer].view_pitch = 89.0f;
  208.          the_board.players[current_viewer].view_yaw = (current_viewer == COLOR_BLACK ? 90.0f : -90.0f);
  209.          the_board.players[current_viewer].view_distance = 58.0f;
  210.       }
  211.  
  212.       // chessboard menu, default view
  213.       else if (wParam_loword == MENUID_CHESSBOARD_DEFAULTVIEW)
  214.       {
  215.          the_board.players[current_viewer].view_pitch = 55.0f;
  216.          the_board.players[current_viewer].view_yaw = (current_viewer == COLOR_BLACK ? 90.0f : -90.0f);
  217.          the_board.players[current_viewer].view_distance = 70.0f;
  218.       }
  219.  
  220.       // chessboard menu, reset view
  221.       else if (wParam_loword == MENUID_CHESSBOARD_RESETVIEW)
  222.       {
  223.          the_board.players[current_viewer].view_pitch = the_board.players[current_viewer].custom_pitch;
  224.          the_board.players[current_viewer].view_yaw = the_board.players[current_viewer].custom_yaw;
  225.          the_board.players[current_viewer].view_distance = the_board.players[current_viewer].custom_distance;
  226.       }
  227.  
  228.       // chessboard menu, zoom in
  229.       else if (wParam_loword == MENUID_CHESSBOARD_ZOOMIN)
  230.       {
  231.          // scroll up & save this as the new custom distance
  232.          the_board.players[current_viewer].view_distance = max (48.0f, the_board.players[current_viewer].view_distance - 2.0f);
  233.          the_board.players[current_viewer].custom_distance = the_board.players[current_viewer].view_distance;
  234.          the_scene.update = true; // update the 3D scene
  235.       }
  236.  
  237.       // chessboard menu, zoom out
  238.       else if (wParam_loword == MENUID_CHESSBOARD_ZOOMOUT)
  239.       {
  240.          // scroll down & save this as the new custom distance
  241.          the_board.players[current_viewer].view_distance = min (100.0f, the_board.players[current_viewer].view_distance + 2.0f);
  242.          the_board.players[current_viewer].custom_distance = the_board.players[current_viewer].view_distance;
  243.          the_scene.update = true; // update the 3D scene
  244.       }
  245.  
  246.       // chessboard menu, change appearance
  247.       else if (wParam_loword == MENUID_CHESSBOARD_CHANGEAPPEARANCE)
  248.          DialogBox_ChangeAppearance ();
  249.  
  250.       // chessboard menu, display windows desktop
  251.       else if (wParam_loword == MENUID_CHESSBOARD_DISPLAYWINDOWSDESKTOP)
  252.          ShowWindow (hWnd, SW_MINIMIZE);
  253.  
  254.       // internet menu, show online players
  255.       else if (wParam_loword == MENUID_INTERNET_SHOWONLINEPLAYERS)
  256.          Window_Opponents ();
  257.  
  258.       // internet menu, show sought games
  259.       else if (wParam_loword == MENUID_INTERNET_SHOWSOUGHTGAMES)
  260.          Window_Sought ();
  261.  
  262.       // internet menu, seek game
  263.       else if (wParam_loword == MENUID_INTERNET_SEEKGAME)
  264.          DialogBox_SendSeek ();
  265.  
  266.       // internet menu, chatter channels
  267.       else if (wParam_loword == MENUID_INTERNET_CHATTERCHANNELS)
  268.          Window_ChatterChannels ();
  269.  
  270.       // internet menu, enter chat text
  271.       else if (wParam_loword == MENUID_INTERNET_ENTERCHATTEXT)
  272.          PostMessage (hWnd, WM_CHAR, L' ', 0); // emulate a space bar hit
  273.  
  274.       // internet menu, display player card
  275.       else if (wParam_loword == MENUID_INTERNET_DISPLAYPLAYERCARD)
  276.          DialogBox_PlayerInfoName ();
  277.  
  278.       // internet menu, display your card
  279.       else if (wParam_loword == MENUID_INTERNET_DISPLAYYOURCARD)
  280.       {
  281.          local_player = Player_FindByType (PLAYER_HUMAN);
  282.          if (local_player != NULL)
  283.             PlayerCard_FindOrCreate (local_player->name); // display player's own player card
  284.       }
  285.  
  286.       // internet menu, MOTD
  287.       else if (wParam_loword == MENUID_INTERNET_MOTD)
  288.          Window_MOTD ();
  289.  
  290.       // help menu, help
  291.       else if (wParam_loword == MENUID_HELP_HELP)
  292.          ShellExecute (NULL, L"open", L"Chess Giants.html", NULL, NULL, SW_MAXIMIZE); // fire up the help file
  293.  
  294.       // help menu, get chess games
  295.       else if (wParam_loword == MENUID_HELP_GETCHESSGAMES)
  296.          ShellExecute (NULL, L"open", L"http://www.chessgames.com", NULL, NULL, SW_MAXIMIZE); // fire up the browser
  297.  
  298.       // help menu, about
  299.       else if (wParam_loword == MENUID_HELP_ABOUT)
  300.          DialogBox_About (); // show the About dialog box
  301.  
  302.       // call the default window message processing function to keep things going
  303.       return (DefWindowProc (hWnd, message, wParam, lParam));
  304.    }
  305.  
  306.    ////////////////////////////////////////////////////////////////////////////////////////////////
  307.    // left mouse button release (while not in animation)
  308.    else if ((message == WM_LBUTTONUP) && (animation_endtime + 1.0f < current_time))
  309.    {
  310.       prevgui_x = GET_X_LPARAM (lParam); // remember mouse coordinates
  311.       prevgui_y = GET_Y_LPARAM (lParam);
  312.  
  313.       // get mouse coordinates
  314.       gui_x = GET_X_LPARAM (lParam);
  315.       gui_y = GET_Y_LPARAM (lParam);
  316.  
  317.       // is the left arrow displayed AND is the mouse hovering it ?
  318.       if ((the_scene.gui.larrow.state != 0) && Button_IsHovered (&the_scene.gui.larrow, gui_x, gui_y))
  319.          SendMessage (hWnd, WM_COMMAND, MENUID_CHESSBOARD_PREVIOUSMOVE, NULL); // send a "previous move" event
  320.  
  321.       // is the right arrow displayed AND is the mouse hovering it ?
  322.       if ((the_scene.gui.rarrow.state != 0) && Button_IsHovered (&the_scene.gui.rarrow, gui_x, gui_y))
  323.          SendMessage (hWnd, WM_COMMAND, MENUID_CHESSBOARD_NEXTMOVE, NULL); // send a "next move" event
  324.  
  325.       // is the chat button displayed AND is the mouse hovering it ?
  326.       if ((the_scene.gui.chatbutton.state != 0) && Button_IsHovered (&the_scene.gui.chatbutton, gui_x, gui_y))
  327.       {
  328.          opposite_player = Player_FindByType (PLAYER_INTERNET); // get a hand on the remote player
  329.  
  330.          // find or create the corresponding interlocutor structure and fire it up
  331.          if (opposite_player != NULL)
  332.             Interlocutor_FindOrCreate (opposite_player->name);
  333.       }
  334.  
  335.       // is the games button displayed AND is the mouse hovering it ?
  336.       if ((the_scene.gui.gamesbutton.state != 0) && Button_IsHovered (&the_scene.gui.gamesbutton, gui_x, gui_y))
  337.          Window_Sought (); // if so, display the sought games window
  338.  
  339.       // is the people button displayed AND is the mouse hovering it ?
  340.       if ((the_scene.gui.peoplebutton.state != 0) && Button_IsHovered (&the_scene.gui.peoplebutton, gui_x, gui_y))
  341.          Window_Opponents (); // if so, display the opponents window
  342.  
  343.       // is the parts selection line displayed AND is the mouse anywhere near it ?
  344.       if (the_scene.gui.is_partspick_displayed && Render_IsMouseInBox (gui_x, gui_y, 0.0f, 0.0f, 100.0f, 11.0f))
  345.       {
  346.          // for each selectable part, if the mouse is on it, mark it as selected
  347.          for (part_index = 0; part_index < 13; part_index++)
  348.             if (Render_IsMouseInBox (gui_x, gui_y, part_index * (100.0f / 13.0f), 0, 100.0f / 13.0f, 11.0f))
  349.             {
  350.                the_scene.gui.partspick_selectedpart = selectable_parts[part_index]; // mark it as selected
  351.                Audio_PlaySound (SOUNDTYPE_CLICK); // make a click sound
  352.                break; // no need to search further if one selection was found
  353.             }
  354.       }
  355.  
  356.       // get current player and see if we're online
  357.       current_player = Player_GetCurrent ();
  358.       remote_player = Player_FindByType (PLAYER_INTERNET);
  359.  
  360.       // if we are not allowed to select anything, don't even try
  361.       if ((current_player->type != PLAYER_HUMAN) || (the_board.viewed_move != the_board.move_count - 1)
  362.           || ((remote_player != NULL) && !remote_player->is_in_game))
  363.       {
  364.          the_scene.update = true; // update the 3D scene
  365.  
  366.          // call the default window message processing function to keep things going
  367.          return (DefWindowProc (hWnd, message, wParam, lParam));
  368.       }
  369.  
  370.       // it's a single click. The user clicked on a square.
  371.  
  372.       // figure out the coordinates on table
  373.       Render_MouseToFloor (gui_x, gui_y, &board_x, &board_y);
  374.  
  375.       // translate them to board coordinates
  376.       the_board.hovered_position[0] = (int) floor ((20.0f - board_y) / 5.0f);
  377.       the_board.hovered_position[1] = 7 - (int) floor ((20.0f - board_x) / 5.0f);
  378.       highlight_endtime = 0; // stop highlighting anything
  379.  
  380.       // if it's outside the table, end the job
  381.       if (!IS_VALID_POSITION (the_board.hovered_position))
  382.       {
  383.          the_scene.update = true; // update the 3D scene
  384.  
  385.          // call the default window message processing function to keep things going
  386.          return (DefWindowProc (hWnd, message, wParam, lParam));
  387.       }
  388.  
  389.       // we clicked a valid slot on the table
  390.  
  391.       current_move = &the_board.moves[the_board.viewed_move]; // quick access to current move
  392.  
  393.       ///////////////////////////////////
  394.       // are we in parts placement mode ?
  395.       if (the_board.game_state == STATE_SETUPPOSITION)
  396.       {
  397.          // quick access to line and column
  398.          line = the_board.hovered_position[0];
  399.          column = the_board.hovered_position[1];
  400.  
  401.          // figure out the color of the part we are placing
  402.          if (the_scene.gui.partspick_selectedpart == tolower (the_scene.gui.partspick_selectedpart))
  403.             part_color = COLOR_BLACK; // black color
  404.          else
  405.             part_color = COLOR_WHITE; // white color
  406.  
  407.          // are we erasing a king ? if so, replace it at its default location
  408.          if ((current_move->slots[line][column].part == PART_KING) && (current_move->slots[line][column].color == COLOR_BLACK)
  409.              && (the_scene.gui.partspick_selectedpart != 'k'))
  410.          {
  411.             // is it ALREADY the king's default location ? if so, give up
  412.             if ((line == 7) && (column == 4))
  413.             {
  414.                Audio_PlaySound (SOUNDTYPE_ILLEGALMOVE); // play the "illegal move" sound
  415.                return (DefWindowProc (hWnd, message, wParam, lParam)); // call the default window message proc
  416.             }
  417.  
  418.             Move_SetSlot (current_move, 7, 4, COLOR_BLACK, PART_KING); // replace black king
  419.          }
  420.          else if ((current_move->slots[line][column].part == PART_KING) && (current_move->slots[line][column].color == COLOR_WHITE)
  421.                   && (the_scene.gui.partspick_selectedpart != 'K'))
  422.          {
  423.             // is it ALREADY the king's default location ? if so, give up
  424.             if ((line == 0) && (column == 4))
  425.             {
  426.                Audio_PlaySound (SOUNDTYPE_ILLEGALMOVE); // play the "illegal move" sound
  427.                return (DefWindowProc (hWnd, message, wParam, lParam)); // call the default window message proc
  428.             }
  429.  
  430.             Move_SetSlot (current_move, 0, 4, COLOR_WHITE, PART_KING); // replace white king
  431.          }
  432.  
  433.          // place the selected part on the board
  434.          if (tolower (the_scene.gui.partspick_selectedpart) == 'p')
  435.             Move_SetSlot (current_move, line, column, part_color, PART_PAWN); // pawn
  436.          else if (tolower (the_scene.gui.partspick_selectedpart) == 'r')
  437.             Move_SetSlot (current_move, line, column, part_color, PART_ROOK); // rook
  438.          else if (tolower (the_scene.gui.partspick_selectedpart) == 'n')
  439.             Move_SetSlot (current_move, line, column, part_color, PART_KNIGHT); // knight
  440.          else if (tolower (the_scene.gui.partspick_selectedpart) == 'b')
  441.             Move_SetSlot (current_move, line, column, part_color, PART_BISHOP); // bishop
  442.          else if (tolower (the_scene.gui.partspick_selectedpart) == 'q')
  443.             Move_SetSlot (current_move, line, column, part_color, PART_QUEEN); // queen
  444.          else if (tolower (the_scene.gui.partspick_selectedpart) == 'k')
  445.          {
  446.             // parse the board for other kings of the same color and erase them
  447.             for (index_line = 0; index_line < 8; index_line++)
  448.                for (index_column = 0; index_column < 8; index_column++)
  449.                   if ((current_move->slots[index_line][index_column].color == part_color)
  450.                       && (current_move->slots[index_line][index_column].part == PART_KING))
  451.                      memset (&current_move->slots[index_line][index_column], 0, sizeof (boardslot_t)); // erase this slot
  452.  
  453.             Move_SetSlot (current_move, line, column, part_color, PART_KING); // king
  454.          }
  455.          else
  456.             Move_SetSlot (current_move, line, column, 0, PART_NONE); // no part
  457.  
  458.          // figure out which sound to play
  459.          if (the_scene.gui.partspick_selectedpart != ' ')
  460.             Audio_PlaySound (SOUNDTYPE_MOVE); // play a move sound when placing a part
  461.  
  462.          the_scene.update = true; // update the 3D scene
  463.  
  464.          // call the default window message processing function to keep things going
  465.          return (DefWindowProc (hWnd, message, wParam, lParam));
  466.       }
  467.       // end of parts placement mode
  468.       //////////////////////////////
  469.  
  470.       // does a selection NOT exist yet ?
  471.       if (!IS_VALID_POSITION (the_board.selected_position))
  472.       {
  473.          // is there a selectable part at this location ?
  474.          if ((current_move->slots[the_board.hovered_position[0]][the_board.hovered_position[1]].part != PART_NONE)
  475.             && (current_move->slots[the_board.hovered_position[0]][the_board.hovered_position[1]].color == Board_ColorToMove (&the_board)))
  476.          {
  477.             // mark the selected position as selected (and remember it)
  478.             the_board.selected_position[0] = the_board.hovered_position[0];
  479.             the_board.selected_position[1] = the_board.hovered_position[1];
  480.          }
  481.  
  482.          the_scene.update = true; // update the 3D scene
  483.  
  484.          // call the default window message processing function to keep things going
  485.          return (DefWindowProc (hWnd, message, wParam, lParam));
  486.       }
  487.  
  488.       // a selection exists already
  489.  
  490.       // is it the slot that was previously selected ? (i.e, user wants to "unselect" it)
  491.       if ((the_board.hovered_position[0] == the_board.selected_position[0]) && (the_board.hovered_position[1] == the_board.selected_position[1]))
  492.       {
  493.          // forget the selected position
  494.          the_board.selected_position[0] = -1;
  495.          the_board.selected_position[1] = -1;
  496.  
  497.          the_scene.update = true; // update the 3D scene
  498.  
  499.          // call the default window message processing function to keep things going
  500.          return (DefWindowProc (hWnd, message, wParam, lParam));
  501.       }
  502.  
  503.       // else is it another part of the same color ? (i.e, user wants to change the part he selected)
  504.       else if ((current_move->slots[the_board.hovered_position[0]][the_board.hovered_position[1]].part != PART_NONE)
  505.                && (current_move->slots[the_board.hovered_position[0]][the_board.hovered_position[1]].color == Board_ColorToMove (&the_board)))
  506.       {
  507.          // mark the selected position as selected (and remember it)
  508.          the_board.selected_position[0] = the_board.hovered_position[0];
  509.          the_board.selected_position[1] = the_board.hovered_position[1];
  510.  
  511.          the_scene.update = true; // update the 3D scene
  512.  
  513.          // call the default window message processing function to keep things going
  514.          return (DefWindowProc (hWnd, message, wParam, lParam));
  515.       }
  516.  
  517.       // else is it a possible move ?
  518.       else if ((current_move->slots[the_board.hovered_position[0]][the_board.hovered_position[1]].flags & FLAG_POSSIBLEMOVE)
  519.                || (current_move->slots[the_board.hovered_position[0]][the_board.hovered_position[1]].flags & FLAG_TAKEABLE))
  520.       {
  521.          // are we in check after the move ? (FIXME: call EP version of this func for en passant moves)
  522.          if (Move_IsColorInCheckAfterTestMove (current_move, the_board.selected_position[0], the_board.selected_position[1], the_board.hovered_position[0], the_board.hovered_position[1], Board_ColorToMove (&the_board)))
  523.          {
  524.             Audio_PlaySound (SOUNDTYPE_ILLEGALMOVE); // play the "illegal move" sound
  525.  
  526.             the_scene.update = true; // update the 3D scene
  527.  
  528.             // call the default window message processing function to keep things going
  529.             return (DefWindowProc (hWnd, message, wParam, lParam));
  530.          }
  531.  
  532.          ////////////////////
  533.          // movement is valid
  534.  
  535.          // do it
  536.          Board_AppendMove (&the_board, the_board.selected_position[0], the_board.selected_position[1], the_board.hovered_position[0], the_board.hovered_position[1], PART_NONE, NULL);
  537.          current_move = &the_board.moves[the_board.move_count - 1]; // update current move pointer
  538.  
  539.          // are we in internet mode ?
  540.          if (remote_player != NULL)
  541.             the_board.reevaluate = false; // if so, don't reevaluate the board yet, let the server do it
  542.  
  543.          // was it a pawn being promoted ? if so, display the dialog box and wait for the reply
  544.          if ((current_move->slots[the_board.hovered_position[0]][the_board.hovered_position[1]].part == PART_PAWN)
  545.              && ((the_board.hovered_position[0] == 0) || (the_board.hovered_position[0] == 7)))
  546.             DialogBox_PawnPromotion (); // display the pawn promotion dialog box
  547.  
  548.          // else it was a normal move
  549.          else
  550.          {
  551.             Board_SetSelectedAndHovered (&the_board, -1, -1, -1, -1); // forget the hovered and selected positions
  552.             the_board.has_playerchanged = true; // and switch players
  553.          }
  554.  
  555.          the_scene.update = true; // update the 3D scene
  556.          animation_endtime = current_time + ANIMATION_DURATION; // play animation now
  557.          sound_playtime = current_time + ANIMATION_DURATION - 0.1f; // play sound near the end of animation
  558.  
  559.          // call the default window message processing function to keep things going
  560.          return (DefWindowProc (hWnd, message, wParam, lParam));
  561.       }
  562.  
  563.       // else it's another location
  564.  
  565.       Audio_PlaySound (SOUNDTYPE_ILLEGALMOVE); // play the "illegal move" sound
  566.  
  567.       the_scene.update = true; // update the 3D scene
  568.  
  569.       // call the default window message processing function to keep things going
  570.       return (DefWindowProc (hWnd, message, wParam, lParam));
  571.    }
  572.  
  573.    ////////////////////////////////////////////////////////////////////////////////////////////////
  574.    // right mouse button push (while not in animation)
  575.    else if ((message == WM_RBUTTONDOWN) && (animation_endtime + 1.0f < current_time))
  576.    {
  577.       prevgui_x = GET_X_LPARAM (lParam); // remember mouse coordinates
  578.       prevgui_y = GET_Y_LPARAM (lParam);
  579.  
  580.       // if we click something, stop moving the table immediately
  581.       the_board.players[current_viewer].view_pitch = current_pitch;
  582.       the_board.players[current_viewer].view_yaw = current_yaw;
  583.  
  584.       rbutton_pushed = true; // remember button is clicked
  585.  
  586.       // call the default window message processing function to keep things going
  587.       return (DefWindowProc (hWnd, message, wParam, lParam));
  588.    }
  589.  
  590.    ////////////////////////////////////////////////////////////////////////////////////////////////
  591.    // right mouse button release (while not in animation)
  592.    else if ((message == WM_RBUTTONUP) && (animation_endtime + 1.0f < current_time))
  593.    {
  594.       rbutton_pushed = false; // remember button is released
  595.       the_scene.update = true; // update the 3D scene
  596.  
  597.       // call the default window message processing function to keep things going
  598.       return (DefWindowProc (hWnd, message, wParam, lParam));
  599.    }
  600.  
  601.    /////////////////////////////////
  602.    // left mouse button DOUBLE-click
  603.    else if ((message == WM_LBUTTONDBLCLK) && (animation_endtime + 1.0f < current_time))
  604.    {
  605.       // get mouse coordinates
  606.       gui_x = GET_X_LPARAM (lParam);
  607.       gui_y = GET_Y_LPARAM (lParam);
  608.  
  609.       // are we in game and did we click the move comments area ?
  610.       if ((the_board.game_state >= STATE_PLAYING) && (the_board.viewed_move > 0) && Render_IsMouseInBox (gui_x, gui_y, 10.0f, 0.0f, 80.0f, 10.0f))
  611.       {
  612.          DialogBox_Comment (); // fire up the comment dialog box
  613.          return (DefWindowProc (hWnd, message, wParam, lParam)); // call the default window message processing function to keep things going
  614.       }
  615.    }
  616.  
  617.    ////////////////////////////////////////////////////////////////////////////////////////////////
  618.    // mouse move (while not in animation)
  619.    else if ((message == WM_MOUSEMOVE) && (animation_endtime + 1.0f < current_time))
  620.    {
  621.       // get mouse coordinates
  622.       gui_x = GET_X_LPARAM (lParam);
  623.       gui_y = GET_Y_LPARAM (lParam);
  624.  
  625.       // handle button update status
  626.       the_scene.update |= Button_UpdateHoverState (&the_scene.gui.larrow, gui_x, gui_y);
  627.       the_scene.update |= Button_UpdateHoverState (&the_scene.gui.rarrow, gui_x, gui_y);
  628.       the_scene.update |= Button_UpdateHoverState (&the_scene.gui.chatbutton, gui_x, gui_y);
  629.       the_scene.update |= Button_UpdateHoverState (&the_scene.gui.gamesbutton, gui_x, gui_y);
  630.       the_scene.update |= Button_UpdateHoverState (&the_scene.gui.peoplebutton, gui_x, gui_y);
  631.  
  632.       // see if we're online
  633.       remote_player = Player_FindByType (PLAYER_INTERNET);
  634.  
  635.       // are we online AND do we NOT have the right to select anything ?
  636.       if ((remote_player != NULL) && !remote_player->is_in_game)
  637.          return (DefWindowProc (hWnd, message, wParam, lParam)); // if so, call the default window message processing function to keep things going
  638.  
  639.       // is the parts selection line displayed AND is the mouse anywhere in it ?
  640.       if (the_scene.gui.is_partspick_displayed && Render_IsMouseInBox (gui_x, gui_y, 0.0f, 0.0f, 100.0f, 11.0f))
  641.       {
  642.          // for each selectable part...
  643.          for (part_index = 0; part_index < 13; part_index++)
  644.          {
  645.             // is the mouse hovering it ?
  646.             if (Render_IsMouseInBox (gui_x, gui_y, part_index * (100.0f / 13.0f), 0, 100.0f / 13.0f, 11.0f))
  647.             {
  648.                // was it NOT hovered before ?
  649.                if (the_scene.gui.partspick_hoveredpart != selectable_parts[part_index])
  650.                {
  651.                   the_scene.gui.partspick_hoveredpart = selectable_parts[part_index]; // mark it as hovered
  652.                   the_scene.update = true; // update the scene
  653.                }
  654.             }
  655.  
  656.             // else was it hovered before ?
  657.             else if (the_scene.gui.partspick_hoveredpart == selectable_parts[part_index])
  658.             {
  659.                the_scene.gui.partspick_hoveredpart = 0; // clear the hovered part
  660.                the_scene.update = true; // update the scene
  661.             }
  662.          }
  663.       }
  664.       else
  665.          the_scene.gui.partspick_hoveredpart = 0; // clear the hovered part
  666.  
  667.       // get current and opposite players
  668.       current_player = Player_GetCurrent ();
  669.       opposite_player = Player_GetOpposite ();
  670.  
  671.       // if right button was clicked, compute new pitch and yaw
  672.       if (rbutton_pushed)
  673.       {
  674.          the_board.players[current_viewer].view_pitch += (gui_y - prevgui_y) * 0.3f;
  675.          if (the_board.players[current_viewer].view_pitch < 10.0f)
  676.             the_board.players[current_viewer].view_pitch = 10.0f; // wrap angles around so that they
  677.          if (the_board.players[current_viewer].view_pitch > 89.0f)
  678.             the_board.players[current_viewer].view_pitch = 89.0f; // stay in the [10, 89] bounds
  679.  
  680.          the_board.players[current_viewer].view_yaw += (gui_x - prevgui_x) * 0.3f;
  681.          the_board.players[current_viewer].view_yaw = WrapAngle (the_board.players[current_viewer].view_yaw);
  682.  
  683.          // save these as the new custom angles
  684.          the_board.players[current_viewer].custom_pitch = the_board.players[current_viewer].view_pitch;
  685.          the_board.players[current_viewer].custom_yaw = the_board.players[current_viewer].view_yaw;
  686.  
  687.          // when moving the table around, jump to ideal angles immediately
  688.          current_pitch = the_board.players[current_viewer].view_pitch;
  689.          current_yaw = the_board.players[current_viewer].view_yaw;
  690.  
  691.          the_scene.update = true; // button was clicked, update the 3D scene
  692.       }
  693.  
  694.       // else it's just the mouse wandering around ; have we the right to select something ?
  695.       else if ((the_board.viewed_move == the_board.move_count - 1) && (current_player->type == PLAYER_HUMAN) && (highlight_endtime < current_time))
  696.       {
  697.          // save the old positions
  698.          prev_hovered_position[0] = the_board.hovered_position[0];
  699.          prev_hovered_position[1] = the_board.hovered_position[1];
  700.  
  701.          // figure out the coordinates on table
  702.          Render_MouseToFloor (gui_x, gui_y, &board_x, &board_y);
  703.  
  704.          // translate them to board coordinates
  705.          the_board.hovered_position[0] = (int) floor ((20.0f - board_y) / 5.0f);
  706.          the_board.hovered_position[1] = 7 - (int) floor ((20.0f - board_x) / 5.0f);
  707.  
  708.          // do they differ from last time ?
  709.          if ((the_board.hovered_position[0] != prev_hovered_position[0]) || (the_board.hovered_position[1] != prev_hovered_position[1]))
  710.             the_scene.update = true; // if so, update scene
  711.       }
  712.  
  713.       // has the user the right to leave a command AND is there no comment yet ?
  714.       if ((the_board.game_state >= STATE_PLAYING) && (the_board.viewed_move > 0)
  715.           && ((the_board.moves[the_board.viewed_move].comment == NULL) || (the_board.moves[the_board.viewed_move].comment[0] == 0)))
  716.       {
  717.          // is the mouse above the comments zone ? if so, display a dimmed hint text
  718.          if (Render_IsMouseInBox (gui_x, gui_y, 30.0f, 0.0f, 40.0f, 10.0f))
  719.          {
  720.             if (!the_scene.gui.comment_text.is_displayed)
  721.             {
  722.                Scene_SetText (&the_scene.gui.comment_text, 50.0f, 5.0f, 40.0f, ALIGN_CENTER, ALIGN_CENTER, ALIGN_LEFT, chat_fontindex, RGBA_TO_RGBACOLOR (255, 255, 255, 127), 999999.0f, false, LOCALIZE (L"DoubleClickToEnterComment"));
  723.                the_scene.update = true; // and update the scene
  724.             }
  725.          }
  726.          else
  727.          {
  728.             if (the_scene.gui.comment_text.is_displayed)
  729.             {
  730.                the_scene.gui.comment_text.is_displayed = false; // if not, erase the hint text
  731.                the_scene.update = true; // and update the scene
  732.             }
  733.          }
  734.       }
  735.  
  736.       // remember these coordinates for next time
  737.       prevgui_x = gui_x;
  738.       prevgui_y = gui_y;
  739.  
  740.       // call the default window message processing function to keep things going
  741.       return (DefWindowProc (hWnd, message, wParam, lParam));
  742.    }
  743.  
  744.    ////////////////////////////////////////////////////////////////////////////////////////////////
  745.    // mouse scroll (while not in animation)
  746.    else if ((message == WM_MOUSEWHEEL) && (animation_endtime + 1.0f < current_time))
  747.    {
  748.       // see if we're online
  749.       remote_player = Player_FindByType (PLAYER_INTERNET);
  750.  
  751.       // are we online AND do we NOT have the right to select anything ?
  752.       if ((remote_player != NULL) && !remote_player->is_in_game)
  753.          return (DefWindowProc (hWnd, message, wParam, lParam)); // if so, call the default window message processing function to keep things going
  754.  
  755.       // scroll up / scroll down ?
  756.       if (GET_WHEEL_DELTA_WPARAM (wParam) > 0)
  757.          the_board.players[current_viewer].view_distance = max (48.0f, the_board.players[current_viewer].view_distance - 2.0f);
  758.       else if (GET_WHEEL_DELTA_WPARAM (wParam) < 0)
  759.          the_board.players[current_viewer].view_distance = min (100.0f, the_board.players[current_viewer].view_distance + 2.0f);
  760.  
  761.       // save this as the new custom distance
  762.       the_board.players[current_viewer].custom_distance = the_board.players[current_viewer].view_distance;
  763.  
  764.       // when moving the table around, jump to ideal angles immediately
  765.       current_distance = the_board.players[current_viewer].view_distance;
  766.  
  767.       the_scene.update = true; // update the 3D scene
  768.  
  769.       // call the default window message processing function to keep things going
  770.       return (DefWindowProc (hWnd, message, wParam, lParam));
  771.    }
  772.  
  773.    ////////////////////////////////////////////////////////////////////////////////////////////////
  774.    // keyboard release
  775.    else if (message == WM_CHAR)
  776.    {
  777.       // are we in position setup mode ?
  778.       if (the_board.game_state == STATE_SETUPPOSITION)
  779.       {
  780.          // is it the enter key ? if so, exit setup mode and start playing
  781.          if (wParam == L'\r')
  782.          {
  783.             current_move = &the_board.moves[the_board.viewed_move]; // quick access to current move
  784.  
  785.             // (in)validate the castling positions
  786.             if ((current_move->slots[0][0].color == COLOR_WHITE) && (current_move->slots[0][0].part == PART_ROOK)
  787.                 && (current_move->slots[0][4].color == COLOR_WHITE) && (current_move->slots[0][4].part == PART_KING))
  788.                current_move->sides[COLOR_WHITE].longcastle_allowed = true; // white castling queenside allowed
  789.             else
  790.                current_move->sides[COLOR_WHITE].longcastle_allowed = false; // white castling queenside no longer possible
  791.             if ((current_move->slots[0][7].color == COLOR_WHITE) && (current_move->slots[0][7].part == PART_ROOK)
  792.                 && (current_move->slots[0][4].color == COLOR_WHITE) && (current_move->slots[0][4].part == PART_KING))
  793.                current_move->sides[COLOR_WHITE].shortcastle_allowed = true; // white castling kingside allowed
  794.             else
  795.                current_move->sides[COLOR_WHITE].shortcastle_allowed = false; // white castling kingside no longer possible
  796.             if ((current_move->slots[7][0].color == COLOR_BLACK) && (current_move->slots[7][0].part == PART_ROOK)
  797.                 && (current_move->slots[7][4].color == COLOR_BLACK) && (current_move->slots[7][4].part == PART_KING))
  798.                current_move->sides[COLOR_BLACK].longcastle_allowed = true; // white castling queenside allowed
  799.             else
  800.                current_move->sides[COLOR_BLACK].longcastle_allowed = false; // white castling queenside no longer possible
  801.             if ((current_move->slots[7][7].color == COLOR_BLACK) && (current_move->slots[7][7].part == PART_ROOK)
  802.                 && (current_move->slots[7][4].color == COLOR_BLACK) && (current_move->slots[7][4].part == PART_KING))
  803.                current_move->sides[COLOR_BLACK].shortcastle_allowed = true; // white castling kingside allowed
  804.             else
  805.                current_move->sides[COLOR_BLACK].shortcastle_allowed = false; // white castling kingside no longer possible
  806.  
  807.             current_move->color = COLOR_BLACK; // so that game starts with white
  808.  
  809.             // validate this board in Forsyth-Edwards Notation and reset it
  810.             Move_DescribeInFEN (current_move);
  811.             wcscpy_s (fen_string, WCHAR_SIZEOF (fen_string), current_move->fen_string); // have a copy of fen string
  812.             Board_Reset (&the_board, fen_string);
  813.  
  814.             the_board.game_state = STATE_PLAYING; // start the game now
  815.             the_board.reevaluate = true; // evaluate board again
  816.  
  817.             the_scene.gui.central_text.disappear_time = current_time + 1.0f; // fade out help text now (FIXME: ugly)
  818.             the_scene.update = true; // update the 3D scene
  819.  
  820.             // call the default window message processing function to keep things going
  821.             return (DefWindowProc (hWnd, message, wParam, lParam));
  822.          }
  823.       }
  824.  
  825.       // else are we in internet mode ?
  826.       else if (Player_FindByType (PLAYER_INTERNET) != NULL)
  827.       {
  828.          entered_ccreply = &the_scene.gui.entered_ccreply; // quick access to entered ccreply
  829.          local_player = Player_FindByType (PLAYER_HUMAN); // quick access to local player
  830.          if (local_player == NULL)
  831.             return (DefWindowProc (hWnd, message, wParam, lParam)); // theoretically impossible condition, but better be sure
  832.          if (selected_chatterchannel == NULL)
  833.             return (DefWindowProc (hWnd, message, wParam, lParam)); // theoretically impossible condition, but better be sure
  834.  
  835.          // are we NOT entering text yet ?
  836.          if (!the_scene.gui.is_entering_text)
  837.          {
  838.             // is it the space bar ?
  839.             if (wParam == L' ')
  840.             {
  841.                the_scene.gui.is_entering_text = true; // remember we are entering text
  842.  
  843.                // reset the entered text buffer
  844.                entered_ccreply->text = (wchar_t *) SAFE_malloc (1, sizeof (wchar_t), false);
  845.                entered_ccreply->text[0] = 0; // only the null terminator will fit
  846.                entered_ccreply->text_length = 0; // and set its length to zero
  847.             }
  848.          }
  849.  
  850.          // else we are currently in text entering mode
  851.          else
  852.          {
  853.             // is the typed character a printable character ?
  854.             if (iswprint (wParam))
  855.             {
  856.                // if so, reallocate space in the typed buffer (include null terminator)
  857.                entered_ccreply->text = (wchar_t *) SAFE_realloc (entered_ccreply->text, entered_ccreply->text_length + 1, entered_ccreply->text_length + 1 + 1, sizeof (wchar_t), false);
  858.                swprintf_s (&entered_ccreply->text[entered_ccreply->text_length], 1 + 1, L"%c", wParam); // append character
  859.                entered_ccreply->text_length++; // buffer holds now one character more
  860.             }
  861.  
  862.             // else is the typed character a backspace AND is there text to erase ?
  863.             else if ((wParam == 0x08) && (entered_ccreply->text_length > 0))
  864.             {
  865.                // if so, reallocate space in the typed buffer (include null terminator)
  866.                entered_ccreply->text = (wchar_t *) SAFE_realloc (entered_ccreply->text, entered_ccreply->text_length + 1, entered_ccreply->text_length + 1 - 1, sizeof (wchar_t), false);
  867.                entered_ccreply->text[entered_ccreply->text_length - 1] = 0; // backspace, delete one character
  868.                entered_ccreply->text_length--; // buffer holds now one character less
  869.             }
  870.  
  871.             // else is the typed character the escape key ?
  872.             else if (wParam == 0x1b)
  873.             {
  874.                SAFE_free ((void **) &entered_ccreply->text); // reset the entered text buffer
  875.                entered_ccreply->text_length = 0; // and set its length to zero
  876.                the_scene.gui.is_entering_text = false; // and exit from the text entering mode
  877.             }
  878.  
  879.             // else is the typed character the enter key ?
  880.             else if (wParam == 0x0d)
  881.                the_scene.gui.is_entering_text = false; // enter, exit from the text entering mode (this will validate our reply)
  882.  
  883.             // else it's an illegal character
  884.             else
  885.                Audio_PlaySound (SOUNDTYPE_ILLEGALMOVE); // illegal character, beep the illegal move sound
  886.          }
  887.  
  888.          the_scene.update = true; // update the 3D scene
  889.  
  890.          // call the default window message processing function to keep things going
  891.          return (DefWindowProc (hWnd, message, wParam, lParam));
  892.       }
  893.    }
  894.  
  895.    ////////////////////////////////////////////////////////////////////////////////////////////////
  896.    // mouse move outside the window
  897.    else if (message == WM_NCMOUSEMOVE)
  898.    {
  899.       rbutton_pushed = false; // remember right button is released
  900.  
  901.       // call the default window message processing function to keep things going
  902.       return (DefWindowProc (hWnd, message, wParam, lParam));
  903.    }
  904.  
  905.    ////////////////////////////////////////////////////////////////////////////////////////////////
  906.    // window repaint
  907.    else if (message == WM_PAINT)
  908.    {
  909.       the_scene.update = true; // update the 3D scene
  910.  
  911.       // call the default window message processing function to keep things going
  912.       return (DefWindowProc (hWnd, message, wParam, lParam));
  913.    }
  914.  
  915.    // call the default window message processing function to keep things going
  916.    return (DefWindowProc (hWnd, message, wParam, lParam));
  917. }
  918.  
  919.  
  920. static bool Button_IsHovered (guibutton_t *button, int gui_x, int gui_y)
  921. {
  922.    // handy wrapper that returns whether a particular GUI button is hovered when the mouse is at the given coordinates
  923.  
  924.    return (Render_IsMouseInBox (gui_x, gui_y, button->left, button->top, button->width, button->height));
  925. }
  926.  
  927.  
  928. static bool Button_UpdateHoverState (guibutton_t *button, int gui_x, int gui_y)
  929. {
  930.    // this function updates the hover state of a GUI button, and returns TRUE if the
  931.    // scene needs to be updated, FALSE otherwise.
  932.  
  933.    // is the button displayed ?
  934.    if (button->state != 0)
  935.    {
  936.       // is the mouse hovering it ?
  937.       if (Render_IsMouseInBox (gui_x, gui_y, button->left, button->top, button->width, button->height))
  938.       {
  939.          // was it NOT hovered before ?
  940.          if (button->state != 2)
  941.          {
  942.             button->state = 2; // mark it as hovered
  943.             return (true); // return TRUE so as to update the scene
  944.          }
  945.       }
  946.  
  947.       // else was it hovered before ?
  948.       else if (button->state == 2)
  949.       {
  950.          button->state = 1; // else mark it as not hovered
  951.          return (true); // return TRUE so as to update the scene
  952.       }
  953.    }
  954.  
  955.    return (false); // no need to update the scene
  956. }
  957.