Subversion Repositories Games.Chess Giants

Rev

Rev 136 | Rev 154 | 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. // global variables used in this module only
  7. static wchar_t folder_path[MAX_PATH];
  8.  
  9.  
  10. // prototypes of local functions
  11. static bool Button_IsHovered (guibutton_t *button, int gui_x, int gui_y);
  12. static bool Button_UpdateHoverState (guibutton_t *button, int gui_x, int gui_y);
  13.  
  14.  
  15. LRESULT CALLBACK WindowProc_Main (HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam)
  16. {
  17.    // this is the main message handler for the program
  18.  
  19.    static wchar_t fen_string[128];
  20.    static char selectable_parts[14] = "PRNBQK kqbnrp";
  21.    static int prevgui_x = 0;
  22.    static int prevgui_y = 0;
  23.    static bool ctrl_pressed = false;
  24.    static bool rbutton_pushed = false;
  25.  
  26.    unsigned short wParam_hiword;
  27.    unsigned short wParam_loword;
  28.    int prev_hovered_position[2];
  29.    player_t *current_player;
  30.    player_t *opposite_player;
  31.    player_t *local_player;
  32.    player_t *remote_player;
  33.    ccreply_t *entered_ccreply;
  34.    boardmove_t *current_move;
  35.    float board_x;
  36.    float board_y;
  37.    int gui_x;
  38.    int gui_y;
  39.    int line;
  40.    int column;
  41.    int index_line;
  42.    int index_column;
  43.    int part_index;
  44.    int part_color;
  45.    int viewer_index;
  46.    RECT rect;
  47.    POINT point;
  48.  
  49.    // filter out the commonly used message values
  50.    wParam_hiword = HIWORD (wParam);
  51.    wParam_loword = LOWORD (wParam);
  52.  
  53.    // get current and opposite players and see if we're online
  54.    current_player = Player_GetCurrent ();
  55.    opposite_player = Player_GetOpposite ();
  56.    remote_player = Player_FindByType (PLAYER_INTERNET);
  57.  
  58.    ////////////////////////////////////////////////////////////////////////////////////////////////
  59.    // has the window just been fired up ?
  60.    if (message == WM_CREATE)
  61.    {
  62.       the_scene.gui.is_entering_text = false; // we are NOT entering text yet
  63.  
  64.       // call the default window message processing function to keep things going
  65.       return (DefWindowProc (hWnd, message, wParam, lParam));
  66.    }
  67.  
  68.    ////////////////////////////////////////////////////////////////////////////////////////////////
  69.    // else is the window being resized ?
  70.    else if ((message == WM_SIZE) || (message == WM_WINDOWPOSCHANGED))
  71.    {
  72.       GetWindowRect (hMainWnd, &rect); // grab the current window size and update the options accordingly
  73.       options.want_maximized = (rect.right - rect.left > GetSystemMetrics (SM_CXMAXIMIZED) * 90 / 100) && (rect.bottom - rect.top > GetSystemMetrics (SM_CYMAXIMIZED) * 80 / 100);
  74.       if (!options.want_maximized)
  75.       {
  76.          options.window_width = max (rect.right - rect.left, 640); // min window width
  77.          options.window_height = max (rect.bottom - rect.top, 480); // min window height (only save them when the window is not maximized)
  78.       }
  79.  
  80.       return (0); // don't let Windows do the default processing on this message
  81.    }
  82.  
  83.    ////////////////////////////////////////////////////////////////////////////////////////////////
  84.    // else have we clicked on the close button ?
  85.    else if (message == WM_CLOSE)
  86.    {
  87.       if ((the_board.game_state == STATE_PLAYING) && ((the_board.move_count > 1) || (remote_player != NULL)))
  88.          DialogBox_Quit (); // if a game has started OR if we are online against somebody, ask for confirmation
  89.       else
  90.          is_dialogbox_quit_validated = true; // if game hasn't started yet, quit without question
  91.  
  92.       return (0); // don't let Windows do the default processing on this message
  93.    }
  94.  
  95.    ////////////////////////////////////////////////////////////////////////////////////////////////
  96.    // else is the user closing its session OR the window is being destroyed ?
  97.    else if ((message == WM_QUERYENDSESSION) || (message == WM_ENDSESSION) || (message == WM_DESTROY))
  98.       terminate_everything = true; // suicide (for some reason, PostQuitMessage() is unreliable !?)
  99.  
  100.    /////////////////
  101.    // MENU EVENTS //
  102.    /////////////////
  103.  
  104.    ///////////////
  105.    // menu command
  106.    else if (message == WM_COMMAND)
  107.    {
  108.       // ANY menu choice makes the "new game" / "open game" front GUI button disappear
  109.       GUIBUTTON_DISABLE (the_scene.gui.newgamebutton);
  110.       GUIBUTTON_DISABLE (the_scene.gui.opengamebutton);
  111.  
  112.       // game menu, new game
  113.       if (wParam_loword == MENUID_GAME_NEWGAME)
  114.       {
  115.          if ((the_board.game_state == STATE_PLAYING) && (the_board.move_count > 1))
  116.             DialogBox_Resign (RESIGNTYPE_NEWGAME); // if a game is playing, ask to resign first
  117.          else
  118.             DialogBox_NewGame (); // game not started yet, show the new game dialog box
  119.       }
  120.  
  121.       // game menu, setup start position
  122.       else if (wParam_loword == MENUID_GAME_SETUPPOSITION)
  123.          Board_EnterSetupPosition (&the_board); // enter setup position mode
  124.  
  125.       // game menu, load game
  126.       else if (wParam_loword == MENUID_GAME_LOAD)
  127.       {
  128.          if ((the_board.game_state == STATE_PLAYING) && (the_board.move_count > 1))
  129.             DialogBox_Resign (RESIGNTYPE_LOADGAME); // if a game is playing, ask to resign first
  130.          else
  131.             DialogBox_Load (); // fire up the load dialog box
  132.       }
  133.  
  134.       // game menu, save game
  135.       else if (wParam_loword == MENUID_GAME_SAVE)
  136.       {
  137.          if (save_pathname[0] != 0)
  138.             is_dialogbox_save_validated = true; // if the filename is known, save it directly
  139.          else
  140.             DialogBox_Save (false); // else fire up the save dialog box
  141.       }
  142.  
  143.       // game menu, save as
  144.       else if (wParam_loword == MENUID_GAME_SAVEAS)
  145.          DialogBox_Save (false); // fire up the save dialog box
  146.  
  147.       // game menu, save position as
  148.       else if (wParam_loword == MENUID_GAME_SAVEPOSITIONAS)
  149.          DialogBox_SavePosition (); // fire up the save position dialog box
  150.  
  151.       // game menu, pause
  152.       else if (wParam_loword == MENUID_GAME_PAUSE)
  153.       {
  154.          is_paused ^= true; // toggle game pause state on/off
  155.          CheckMenuItem (GetMenu (hWnd), MENUID_GAME_PAUSE, (is_paused ? MF_CHECKED : MF_UNCHECKED)); // highlight the menu entry as necessary
  156.          the_board.reevaluate = true; // evaluate board again
  157.       }
  158.  
  159.       // game menu, resign
  160.       else if (wParam_loword == MENUID_GAME_RESIGN)
  161.          DialogBox_Resign (RESIGNTYPE_UNDEFINED); // if a game has started, ask for confirmation
  162.  
  163.       // game menu, statistics (stats are only available in online mode)
  164.       else if (wParam_loword == MENUID_GAME_STATISTICS)
  165.       {
  166.          local_player = Player_FindByType (PLAYER_HUMAN);
  167.          if (local_player != NULL)
  168.             PlayerCard_FindOrCreate (local_player->name); // display player's own player card
  169.       }
  170.  
  171.       // game menu, options
  172.       else if (wParam_loword == MENUID_GAME_OPTIONS)
  173.          DialogBox_Options (); // fire up the options dialog box
  174.  
  175.       // game menu, quit
  176.       else if (wParam_loword == MENUID_GAME_QUIT)
  177.       {
  178.          if ((the_board.game_state == STATE_PLAYING) && ((the_board.move_count > 1) || (remote_player != NULL)))
  179.             DialogBox_Quit (); // if a game has started OR if we are online against somebody, ask for confirmation
  180.          else
  181.             is_dialogbox_quit_validated = true; // if game hasn't started yet, quit without question
  182.       }
  183.  
  184.       // chessboard menu, cancel last move
  185.       else if (wParam_loword == MENUID_MOVE_CANCELLASTMOVE)
  186.          the_board.players[current_viewer].wants_cancel = true; // remember this player wants to cancel his last move
  187.  
  188.       // chessboard menu, suggest move
  189.       else if (wParam_loword == MENUID_MOVE_SUGGESTMOVE)
  190.          the_board.players[current_viewer].wants_hint = true; // remember this player wants a hint
  191.  
  192.       // chessboard menu, comment move
  193.       else if (wParam_loword == MENUID_MOVE_COMMENTMOVE)
  194.          DialogBox_Comment (); // fire up the comment dialog box
  195.  
  196.       // chessboard menu, go to move
  197.       else if (wParam_loword == MENUID_MOVE_GOTOMOVE)
  198.          DialogBox_GoToMove (); // fire up the go to move dialog box
  199.  
  200.       // chessboard menu, swap sides
  201.       else if (wParam_loword == MENUID_CHESSBOARD_SWAPSIDES)
  202.          the_board.want_playerswap = true; // remember board sides are to be swapped
  203.  
  204.       // chessboard menu, rename players
  205.       else if (wParam_loword == MENUID_CHESSBOARD_RENAMESIDES)
  206.          DialogBox_RenameSides(); // fire up the rename sides dialog box
  207.  
  208.       // chessboard menu, beginning of game
  209.       else if (wParam_loword == MENUID_CHESSBOARD_BEGINNINGOFGAME)
  210.       {
  211.          the_board.viewed_move = 0; // enter view mode and go to the beginning of the game
  212.          Audio_PlaySound (SOUNDTYPE_CLICK, 0.0f, 0.0f, 0.04f); // make a click sound at the center of the board
  213.          the_board.reevaluate = true; // evaluate board again
  214.       }
  215.  
  216.       // chessboard menu, previous move (only if arrow is enabled)
  217.       else if ((wParam_loword == MENUID_CHESSBOARD_PREVIOUSMOVE) && (the_scene.gui.larrow.state != 0))
  218.       {
  219.          the_board.viewed_move--; // enter view mode and go back one move
  220.          Audio_PlaySound (SOUNDTYPE_CLICK, 0.0f, 0.0f, 0.04f); // make a click sound at the center of the board
  221.          the_board.reevaluate = true; // evaluate board again
  222.       }
  223.  
  224.       // chessboard menu, next move (only if arrow is enabled)
  225.       else if ((wParam_loword == MENUID_CHESSBOARD_NEXTMOVE) && (the_scene.gui.rarrow.state != 0))
  226.       {
  227.          the_board.viewed_move++; // enter view mode and go forward one move
  228.          Audio_PlaySound (SOUNDTYPE_CLICK, 0.0f, 0.0f, 0.04f); // make a click sound at the center of the board
  229.          the_board.reevaluate = true; // evaluate board again
  230.       }
  231.  
  232.       // chessboard menu, current state of game
  233.       else if (wParam_loword == MENUID_CHESSBOARD_CURRENTSTATEOFGAME)
  234.       {
  235.          the_board.viewed_move = the_board.move_count - 1; // enter view mode and go to the current state of the game
  236.          Audio_PlaySound (SOUNDTYPE_CLICK, 0.0f, 0.0f, 0.04f); // make a click sound at the center of the board
  237.          the_board.reevaluate = true; // evaluate board again
  238.       }
  239.  
  240.       // chessboard menu, top view
  241.       else if (wParam_loword == MENUID_CHESSBOARD_TOPVIEW)
  242.       {
  243.          // cycle through both players and change their view angles EXCEPT the opponent if he's human
  244.          for (viewer_index = 0; viewer_index < 2; viewer_index++)
  245.             if ((the_board.players[viewer_index].type == PLAYER_COMPUTER)
  246.                 || (the_board.players[viewer_index].type == PLAYER_INTERNET)
  247.                 || (viewer_index == current_viewer))
  248.             {
  249.                the_board.players[viewer_index].view_pitch = 89.99f;
  250.                the_board.players[viewer_index].view_yaw = (current_viewer == COLOR_BLACK ? 90.0f : -90.0f);
  251.                the_board.players[viewer_index].view_distance = 58.0f;
  252.             }
  253.       }
  254.  
  255.       // chessboard menu, default view
  256.       else if (wParam_loword == MENUID_CHESSBOARD_DEFAULTVIEW)
  257.       {
  258.          // cycle through both players and change their view angles EXCEPT the opponent if he's human
  259.          for (viewer_index = 0; viewer_index < 2; viewer_index++)
  260.             if ((the_board.players[viewer_index].type == PLAYER_COMPUTER)
  261.                 || (the_board.players[viewer_index].type == PLAYER_INTERNET)
  262.                 || (viewer_index == current_viewer))
  263.             {
  264.                the_board.players[viewer_index].view_pitch = 55.0f;
  265.                the_board.players[viewer_index].view_yaw = (current_viewer == COLOR_BLACK ? 90.0f : -90.0f);
  266.                the_board.players[viewer_index].view_distance = 70.0f;
  267.             }
  268.       }
  269.  
  270.       // chessboard menu, reset view
  271.       else if (wParam_loword == MENUID_CHESSBOARD_RESETVIEW)
  272.       {
  273.          // cycle through both players and change their view angles EXCEPT the opponent if he's human
  274.          for (viewer_index = 0; viewer_index < 2; viewer_index++)
  275.             if ((the_board.players[viewer_index].type == PLAYER_COMPUTER)
  276.                 || (the_board.players[viewer_index].type == PLAYER_INTERNET)
  277.                 || (viewer_index == current_viewer))
  278.             {
  279.                the_board.players[viewer_index].view_pitch = the_board.players[current_viewer].custom_pitch;
  280.                the_board.players[viewer_index].view_yaw = the_board.players[current_viewer].custom_yaw;
  281.                the_board.players[viewer_index].view_distance = the_board.players[current_viewer].custom_distance;
  282.             }
  283.       }
  284.  
  285.       // chessboard menu, zoom in
  286.       else if (wParam_loword == MENUID_CHESSBOARD_ZOOMIN)
  287.       {
  288.          // scroll up & save this as the new custom distance
  289.          the_board.players[current_viewer].view_distance = max (48.0f, the_board.players[current_viewer].view_distance - 2.0f);
  290.          the_board.players[current_viewer].custom_distance = the_board.players[current_viewer].view_distance;
  291.          the_scene.update = true; // update the 3D scene
  292.       }
  293.  
  294.       // chessboard menu, zoom out
  295.       else if (wParam_loword == MENUID_CHESSBOARD_ZOOMOUT)
  296.       {
  297.          // scroll down & save this as the new custom distance
  298.          the_board.players[current_viewer].view_distance = min (100.0f, the_board.players[current_viewer].view_distance + 2.0f);
  299.          the_board.players[current_viewer].custom_distance = the_board.players[current_viewer].view_distance;
  300.          the_scene.update = true; // update the 3D scene
  301.       }
  302.  
  303.       // chessboard menu, change appearance
  304.       else if (wParam_loword == MENUID_CHESSBOARD_CHANGEAPPEARANCE)
  305.          DialogBox_ChangeAppearance ();
  306.  
  307.       // chessboard menu, display windows desktop
  308.       else if (wParam_loword == MENUID_CHESSBOARD_DISPLAYWINDOWSDESKTOP)
  309.          ShowWindow (hWnd, SW_MINIMIZE);
  310.  
  311.       // internet menu, show online players
  312.       else if (wParam_loword == MENUID_INTERNET_SHOWONLINEPLAYERS)
  313.          Window_Opponents ();
  314.  
  315.       // internet menu, show sought games
  316.       else if (wParam_loword == MENUID_INTERNET_SHOWSOUGHTGAMES)
  317.          Window_Sought ();
  318.  
  319.       // internet menu, seek game
  320.       else if (wParam_loword == MENUID_INTERNET_SEEKGAME)
  321.          DialogBox_SendSeek ();
  322.  
  323.       // internet menu, chatter channels
  324.       else if (wParam_loword == MENUID_INTERNET_CHATTERCHANNELS)
  325.          Window_ChatterChannels ();
  326.  
  327.       // internet menu, enter chat text
  328.       else if (wParam_loword == MENUID_INTERNET_ENTERCHATTEXT)
  329.          PostMessage (hWnd, WM_CHAR, L' ', 0); // emulate a space bar hit
  330.  
  331.       // internet menu, display player card
  332.       else if (wParam_loword == MENUID_INTERNET_DISPLAYPLAYERCARD)
  333.          DialogBox_PlayerInfoName ();
  334.  
  335.       // internet menu, display your card
  336.       else if (wParam_loword == MENUID_INTERNET_DISPLAYYOURCARD)
  337.       {
  338.          local_player = Player_FindByType (PLAYER_HUMAN);
  339.          if (local_player != NULL)
  340.             PlayerCard_FindOrCreate (local_player->name); // display player's own player card
  341.       }
  342.  
  343.       // internet menu, MOTD
  344.       else if (wParam_loword == MENUID_INTERNET_MOTD)
  345.          Window_MOTD ();
  346.  
  347.       // help menu, help
  348.       else if (wParam_loword == MENUID_HELP_HELP)
  349.       {
  350.          swprintf_s (folder_path, WCHAR_SIZEOF (folder_path), L"%s\\Chess Giants (%s).html", app_path, languages[language_id].name);
  351.          if (_waccess (folder_path, 0) != 0)
  352.             swprintf_s (folder_path, WCHAR_SIZEOF (folder_path), L"%s\\Chess Giants (English).html", app_path);
  353.          ShellExecute (NULL, L"open", folder_path, NULL, NULL, SW_MAXIMIZE); // fire up the help file
  354.       }
  355.  
  356.       // help menu, get chess games
  357.       else if (wParam_loword == MENUID_HELP_GETCHESSGAMES)
  358.          ShellExecute (NULL, L"open", L"http://www.chessgames.com", NULL, NULL, SW_MAXIMIZE); // fire up the browser
  359.  
  360.       // help menu, add/modify visual themes
  361.       else if (wParam_loword == MENUID_HELP_ADDMODIFYVISUALTHEMES)
  362.       {
  363.          swprintf_s (folder_path, WCHAR_SIZEOF (folder_path), L"%s\\themes", app_path);
  364.          ShellExecute (NULL, L"open", folder_path, NULL, NULL, SW_SHOWNORMAL); // fire up Windows Explorer
  365.       }
  366.  
  367.       // help menu, add/modify engines
  368.       else if (wParam_loword == MENUID_HELP_ADDMODIFYENGINES)
  369.       {
  370.          swprintf_s (folder_path, WCHAR_SIZEOF (folder_path), L"%s\\engines", app_path);
  371.          ShellExecute (NULL, L"open", folder_path, NULL, NULL, SW_SHOWNORMAL); // fire up Windows Explorer
  372.       }
  373.  
  374.       // help menu, add/modify translations
  375.       else if (wParam_loword == MENUID_HELP_ADDMODIFYTRANSLATIONS)
  376.       {
  377.          swprintf_s (folder_path, WCHAR_SIZEOF (folder_path), L"%s\\data\\languages", app_path);
  378.          ShellExecute (NULL, L"open", folder_path, NULL, NULL, SW_SHOWNORMAL); // fire up Windows Explorer
  379.       }
  380.  
  381.       // help menu, about
  382.       else if (wParam_loword == MENUID_HELP_ABOUT)
  383.          DialogBox_About (); // show the About dialog box
  384.  
  385.       // call the default window message processing function to keep things going
  386.       return (DefWindowProc (hWnd, message, wParam, lParam));
  387.    }
  388.  
  389.    /////////////////////////////////////////////////////
  390.    // ctrl key press or release (while not in animation)
  391.    else if ((message == WM_KEYDOWN) && (wParam == VK_CONTROL) && (animation_endtime + 1.0f < current_time))
  392.    {
  393.       ctrl_pressed = true; // remember the ctrl key is pressed
  394.  
  395.       // call the default window message processing function to keep things going
  396.       return (DefWindowProc (hWnd, message, wParam, lParam));
  397.    }
  398.    else if ((message == WM_KEYUP) && (wParam == VK_CONTROL) && (animation_endtime + 1.0f < current_time))
  399.    {
  400.       ctrl_pressed = false; // remember the ctrl key is released
  401.       rbutton_pushed = false; // remember button is released
  402.       the_scene.update = true; // update the 3D scene
  403.  
  404.       // call the default window message processing function to keep things going
  405.       return (DefWindowProc (hWnd, message, wParam, lParam));
  406.    }
  407.  
  408.    //////////////////
  409.    // MOUSE EVENTS //
  410.    //////////////////
  411.  
  412.    ////////////////////////////////////////////////////////////////////////////////////////////////
  413.    // left mouse button push
  414.    else if (message == WM_LBUTTONDOWN)
  415.    {
  416.       // are we in animation OR are mouse commands NOT allowed ?
  417.       if ((animation_endtime + 1.0f >= current_time) || (command_ignoretime >= current_time))
  418.          return (DefWindowProc (hWnd, message, wParam, lParam)); // if so, call the default message proc to keep things going
  419.  
  420.       // are we in board closeup mode OR are we online AND do we NOT have the right to select anything ?
  421.       if (((current_distance == CLOSEUP_VIEW_DISTANCE) && (current_pitch == CLOSEUP_VIEW_PITCH))
  422.           || ((remote_player != NULL) && !remote_player->is_in_game))
  423.          return (DefWindowProc (hWnd, message, wParam, lParam)); // if so, call the default window message processing function to keep things going
  424.  
  425.       // is the ctrl key pressed (emulates a right click) ?
  426.       if (ctrl_pressed)
  427.       {
  428.          prevgui_x = GET_X_LPARAM (lParam); // remember mouse coordinates
  429.          prevgui_y = GET_Y_LPARAM (lParam);
  430.  
  431.          // if we click something, stop moving the table immediately
  432.  
  433.          // cycle through both players and change their view angles EXCEPT the opponent if he's human
  434.          for (viewer_index = 0; viewer_index < 2; viewer_index++)
  435.             if ((the_board.players[viewer_index].type == PLAYER_COMPUTER)
  436.                 || (the_board.players[viewer_index].type == PLAYER_INTERNET)
  437.                 || (viewer_index == current_viewer))
  438.             {
  439.                the_board.players[viewer_index].view_pitch = current_pitch;
  440.                the_board.players[viewer_index].view_yaw = current_yaw;
  441.             }
  442.  
  443.          rbutton_pushed = true; // remember button is clicked
  444.       }
  445.  
  446.       // call the default window message processing function to keep things going
  447.       return (DefWindowProc (hWnd, message, wParam, lParam));
  448.    }
  449.  
  450.    ////////////////////////////////////////////////////////////////////////////////////////////////
  451.    // left mouse button release
  452.    else if (message == WM_LBUTTONUP)
  453.    {
  454.       // are we in animation OR are mouse commands NOT allowed ?
  455.       if ((animation_endtime + 1.0f >= current_time) || (command_ignoretime >= current_time))
  456.          return (DefWindowProc (hWnd, message, wParam, lParam)); // if so, call the default message proc to keep things going
  457.  
  458.       // is the ctrl key pressed (emulates a right click) ?
  459.       if (ctrl_pressed)
  460.       {
  461.          rbutton_pushed = false; // remember button is released
  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.  
  468.       prevgui_x = GET_X_LPARAM (lParam); // remember mouse coordinates
  469.       prevgui_y = GET_Y_LPARAM (lParam);
  470.  
  471.       // get mouse coordinates
  472.       gui_x = GET_X_LPARAM (lParam);
  473.       gui_y = GET_Y_LPARAM (lParam);
  474.  
  475.       // is the "new game" button displayed AND is the mouse hovering it ?
  476.       if ((the_scene.gui.newgamebutton.state != 0) && Button_IsHovered (&the_scene.gui.newgamebutton, gui_x, gui_y))
  477.       {
  478.          GUIBUTTON_DISABLE (the_scene.gui.newgamebutton); // hide the "new game" button
  479.          GUIBUTTON_DISABLE (the_scene.gui.opengamebutton); // hide the "open game" button
  480.          DialogBox_NewGame (); // fire up the "new game" dialog box
  481.       }
  482.  
  483.       // is the "open game" button displayed AND is the mouse hovering it ?
  484.       if ((the_scene.gui.opengamebutton.state != 0) && Button_IsHovered (&the_scene.gui.opengamebutton, gui_x, gui_y))
  485.       {
  486.          GUIBUTTON_DISABLE (the_scene.gui.newgamebutton); // hide the "new game" button
  487.          GUIBUTTON_DISABLE (the_scene.gui.opengamebutton); // hide the "open game" button
  488.          DialogBox_Load (); // fire up the "open game" dialog box
  489.       }
  490.  
  491.       // is the chat button displayed AND is the mouse hovering it ?
  492.       if ((the_scene.gui.chatbutton.state != 0) && Button_IsHovered (&the_scene.gui.chatbutton, gui_x, gui_y))
  493.       {
  494.          // find or create the corresponding interlocutor structure and fire it up
  495.          if (remote_player != NULL)
  496.             Interlocutor_FindOrCreate (remote_player->name);
  497.       }
  498.  
  499.       // is the games button displayed AND is the mouse hovering it ?
  500.       if ((the_scene.gui.gamesbutton.state != 0) && Button_IsHovered (&the_scene.gui.gamesbutton, gui_x, gui_y))
  501.          Window_Sought (); // if so, display the sought games window
  502.  
  503.       // is the people button displayed AND is the mouse hovering it ?
  504.       if ((the_scene.gui.peoplebutton.state != 0) && Button_IsHovered (&the_scene.gui.peoplebutton, gui_x, gui_y))
  505.          Window_Opponents (); // if so, display the opponents window
  506.  
  507.       // are we in board closeup mode OR are we online AND do we NOT have the right to select anything ?
  508.       if (((current_distance == CLOSEUP_VIEW_DISTANCE) && (current_pitch == CLOSEUP_VIEW_PITCH))
  509.           || ((remote_player != NULL) && !remote_player->is_in_game))
  510.          return (DefWindowProc (hWnd, message, wParam, lParam)); // if so, call the default window message processing function to keep things going
  511.  
  512.       // handle game history navigation
  513.       if ((the_scene.gui.llarrow.state != 0) && Button_IsHovered (&the_scene.gui.llarrow, gui_x, gui_y))
  514.          SendMessage (hWnd, WM_COMMAND, MENUID_CHESSBOARD_BEGINNINGOFGAME, NULL); // send a "jump to beginning" event
  515.       else if ((the_scene.gui.larrow.state != 0) && Button_IsHovered (&the_scene.gui.larrow, gui_x, gui_y))
  516.          SendMessage (hWnd, WM_COMMAND, MENUID_CHESSBOARD_PREVIOUSMOVE, NULL); // send a "previous move" event
  517.       else if ((the_scene.gui.rarrow.state != 0) && Button_IsHovered (&the_scene.gui.rarrow, gui_x, gui_y))
  518.          SendMessage (hWnd, WM_COMMAND, MENUID_CHESSBOARD_NEXTMOVE, NULL); // send a "next move" event
  519.       else if ((the_scene.gui.rrarrow.state != 0) && Button_IsHovered (&the_scene.gui.rrarrow, gui_x, gui_y))
  520.          SendMessage (hWnd, WM_COMMAND, MENUID_CHESSBOARD_CURRENTSTATEOFGAME, NULL); // send a "jump to end" event
  521.  
  522.       // is the parts selection line displayed AND is the mouse anywhere near it ?
  523.       if (the_scene.gui.is_partspick_displayed && Render_IsMouseInBox (gui_x, gui_y, 0.0f, 0.0f, 100.0f, 11.0f))
  524.       {
  525.          // for each selectable part, if the mouse is on it, mark it as selected
  526.          for (part_index = 0; part_index < 13; part_index++)
  527.             if (Render_IsMouseInBox (gui_x, gui_y, part_index * (100.0f / 13.0f), 0, 100.0f / 13.0f, 11.0f))
  528.             {
  529.                the_scene.gui.partspick_selectedpart = selectable_parts[part_index]; // mark it as selected
  530.                Audio_PlaySound (SOUNDTYPE_CLICK, 0.0f, 0.0f, 0.04f); // make a click sound at the center of the board
  531.                break; // no need to search further if one selection was found
  532.             }
  533.       }
  534.  
  535.       // if we are not allowed to select anything, don't even try
  536.       if (is_paused || (current_player->type != PLAYER_HUMAN) || (the_board.viewed_move != the_board.move_count - 1)
  537.           || ((the_board.game_state != STATE_SETUPPOSITION) && (the_board.game_state != STATE_PLAYING))
  538.           || ((remote_player != NULL) && !remote_player->is_in_game))
  539.       {
  540.          the_scene.update = true; // update the 3D scene
  541.  
  542.          // call the default window message processing function to keep things going
  543.          return (DefWindowProc (hWnd, message, wParam, lParam));
  544.       }
  545.  
  546.       // it's a single click. The user clicked on a square.
  547.  
  548.       // figure out the coordinates on table
  549.       Render_MouseToFloor (gui_x, gui_y, &board_x, &board_y);
  550.  
  551.       // translate them to board coordinates
  552.       the_board.hovered_position[0] = (int) floor ((20.0f - board_y) / 5.0f);
  553.       the_board.hovered_position[1] = 7 - (int) floor ((20.0f - board_x) / 5.0f);
  554.       highlight_endtime = 0; // stop highlighting anything
  555.  
  556.       // if it's outside the table, end the job
  557.       if (!IS_VALID_POSITION (the_board.hovered_position))
  558.       {
  559.          the_scene.update = true; // update the 3D scene
  560.  
  561.          // call the default window message processing function to keep things going
  562.          return (DefWindowProc (hWnd, message, wParam, lParam));
  563.       }
  564.  
  565.       // we clicked a valid slot on the table
  566.  
  567.       current_move = &the_board.moves[the_board.viewed_move]; // quick access to current move
  568.  
  569.       ///////////////////////////////////
  570.       // are we in parts placement mode ?
  571.       if (the_board.game_state == STATE_SETUPPOSITION)
  572.       {
  573.          // quick access to line and column
  574.          line = the_board.hovered_position[0];
  575.          column = the_board.hovered_position[1];
  576.  
  577.          // figure out the color of the part we are placing
  578.          if (the_scene.gui.partspick_selectedpart == tolower (the_scene.gui.partspick_selectedpart))
  579.             part_color = COLOR_BLACK; // black color
  580.          else
  581.             part_color = COLOR_WHITE; // white color
  582.  
  583.          // are we erasing a king ? if so, replace it at its default location
  584.          if ((current_move->slots[line][column].part == PART_KING) && (current_move->slots[line][column].color == COLOR_BLACK)
  585.              && (the_scene.gui.partspick_selectedpart != 'k'))
  586.          {
  587.             // is it ALREADY the king's default location ? if so, give up
  588.             if ((line == 7) && (column == 4))
  589.             {
  590.                Audio_PlaySound (SOUNDTYPE_ILLEGALMOVE, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.04f); // play the "illegal move" sound
  591.                return (DefWindowProc (hWnd, message, wParam, lParam)); // call the default window message proc
  592.             }
  593.  
  594.             Move_SetSlot (current_move, 7, 4, COLOR_BLACK, PART_KING); // replace black king
  595.          }
  596.          else if ((current_move->slots[line][column].part == PART_KING) && (current_move->slots[line][column].color == COLOR_WHITE)
  597.                   && (the_scene.gui.partspick_selectedpart != 'K'))
  598.          {
  599.             // is it ALREADY the king's default location ? if so, give up
  600.             if ((line == 0) && (column == 4))
  601.             {
  602.                Audio_PlaySound (SOUNDTYPE_ILLEGALMOVE, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.04f); // play the "illegal move" sound
  603.                return (DefWindowProc (hWnd, message, wParam, lParam)); // call the default window message proc
  604.             }
  605.  
  606.             Move_SetSlot (current_move, 0, 4, COLOR_WHITE, PART_KING); // replace white king
  607.          }
  608.  
  609.          // place the selected part on the board
  610.          if (tolower (the_scene.gui.partspick_selectedpart) == 'p')
  611.             Move_SetSlot (current_move, line, column, part_color, PART_PAWN); // pawn
  612.          else if (tolower (the_scene.gui.partspick_selectedpart) == 'r')
  613.             Move_SetSlot (current_move, line, column, part_color, PART_ROOK); // rook
  614.          else if (tolower (the_scene.gui.partspick_selectedpart) == 'n')
  615.             Move_SetSlot (current_move, line, column, part_color, PART_KNIGHT); // knight
  616.          else if (tolower (the_scene.gui.partspick_selectedpart) == 'b')
  617.             Move_SetSlot (current_move, line, column, part_color, PART_BISHOP); // bishop
  618.          else if (tolower (the_scene.gui.partspick_selectedpart) == 'q')
  619.             Move_SetSlot (current_move, line, column, part_color, PART_QUEEN); // queen
  620.          else if (tolower (the_scene.gui.partspick_selectedpart) == 'k')
  621.          {
  622.             // parse the board for other kings of the same color and erase them
  623.             for (index_line = 0; index_line < 8; index_line++)
  624.                for (index_column = 0; index_column < 8; index_column++)
  625.                   if ((current_move->slots[index_line][index_column].color == part_color)
  626.                       && (current_move->slots[index_line][index_column].part == PART_KING))
  627.                      memset (&current_move->slots[index_line][index_column], 0, sizeof (boardslot_t)); // erase this slot
  628.  
  629.             Move_SetSlot (current_move, line, column, part_color, PART_KING); // king
  630.          }
  631.          else
  632.             Move_SetSlot (current_move, line, column, 0, PART_NONE); // no part
  633.  
  634.          // figure out which sound to play
  635.          if (the_scene.gui.partspick_selectedpart != ' ')
  636.             Audio_PlaySound (SOUNDTYPE_MOVE, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.04f); // play a move sound when placing a part
  637.  
  638.          the_scene.update = true; // update the 3D scene
  639.  
  640.          // call the default window message processing function to keep things going
  641.          return (DefWindowProc (hWnd, message, wParam, lParam));
  642.       }
  643.       // end of parts placement mode
  644.       //////////////////////////////
  645.  
  646.       // does a selection NOT exist yet ?
  647.       if (!IS_VALID_POSITION (the_board.selected_position))
  648.       {
  649.          // is there a selectable part at this location ?
  650.          if ((current_move->slots[the_board.hovered_position[0]][the_board.hovered_position[1]].part != PART_NONE)
  651.             && (current_move->slots[the_board.hovered_position[0]][the_board.hovered_position[1]].color == Board_ColorToMove (&the_board)))
  652.          {
  653.             // mark the selected position as selected (and remember it)
  654.             the_board.selected_position[0] = the_board.hovered_position[0];
  655.             the_board.selected_position[1] = the_board.hovered_position[1];
  656.          }
  657.  
  658.          the_scene.update = true; // update the 3D scene
  659.  
  660.          // call the default window message processing function to keep things going
  661.          return (DefWindowProc (hWnd, message, wParam, lParam));
  662.       }
  663.  
  664.       // a selection exists already
  665.  
  666.       // is it the slot that was previously selected ? (i.e, user wants to "unselect" it)
  667.       if ((the_board.hovered_position[0] == the_board.selected_position[0]) && (the_board.hovered_position[1] == the_board.selected_position[1]))
  668.       {
  669.          // forget the selected position
  670.          the_board.selected_position[0] = -1;
  671.          the_board.selected_position[1] = -1;
  672.  
  673.          the_scene.update = true; // update the 3D scene
  674.  
  675.          // call the default window message processing function to keep things going
  676.          return (DefWindowProc (hWnd, message, wParam, lParam));
  677.       }
  678.  
  679.       // else is it another part of the same color ? (i.e, user wants to change the part he selected)
  680.       else if ((current_move->slots[the_board.hovered_position[0]][the_board.hovered_position[1]].part != PART_NONE)
  681.                && (current_move->slots[the_board.hovered_position[0]][the_board.hovered_position[1]].color == Board_ColorToMove (&the_board)))
  682.       {
  683.          // mark the selected position as selected (and remember it)
  684.          the_board.selected_position[0] = the_board.hovered_position[0];
  685.          the_board.selected_position[1] = the_board.hovered_position[1];
  686.  
  687.          the_scene.update = true; // update the 3D scene
  688.  
  689.          // call the default window message processing function to keep things going
  690.          return (DefWindowProc (hWnd, message, wParam, lParam));
  691.       }
  692.  
  693.       // else is it a possible move ?
  694.       else if ((current_move->slots[the_board.hovered_position[0]][the_board.hovered_position[1]].flags & FLAG_POSSIBLEMOVE)
  695.                || (current_move->slots[the_board.hovered_position[0]][the_board.hovered_position[1]].flags & FLAG_TAKEABLE))
  696.       {
  697.          // are we in check after the move ? (FIXME: call EP version of this func for en passant moves)
  698.          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)))
  699.          {
  700.             Audio_PlaySound (SOUNDTYPE_ILLEGALMOVE, 17.5f - (7 - the_board.hovered_position[1]) * 5.0f, 17.5f - the_board.hovered_position[0] * 5.0f, 0.04f); // play the "illegal move" sound
  701.  
  702.             the_scene.update = true; // update the 3D scene
  703.  
  704.             // call the default window message processing function to keep things going
  705.             return (DefWindowProc (hWnd, message, wParam, lParam));
  706.          }
  707.  
  708.          ////////////////////
  709.          // movement is valid
  710.  
  711.          // do it
  712.          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);
  713.          current_move = &the_board.moves[the_board.move_count - 1]; // update current move pointer
  714.  
  715.          // are we in internet mode ?
  716.          if (remote_player != NULL)
  717.             the_board.reevaluate = false; // if so, don't reevaluate the board yet, let the server do it
  718.  
  719.          // was it a pawn being promoted ? if so, display the dialog box and wait for the reply
  720.          if ((current_move->slots[the_board.hovered_position[0]][the_board.hovered_position[1]].part == PART_PAWN)
  721.              && ((the_board.hovered_position[0] == 0) || (the_board.hovered_position[0] == 7)))
  722.             DialogBox_PawnPromotion (); // display the pawn promotion dialog box
  723.  
  724.          // else it was a normal move
  725.          else
  726.          {
  727.             Board_SetSelectedAndHovered (&the_board, -1, -1, -1, -1); // forget the hovered and selected positions
  728.             the_board.has_playerchanged = true; // and switch players
  729.          }
  730.  
  731.          the_scene.update = true; // update the 3D scene
  732.          animation_endtime = current_time + ANIMATION_DURATION; // play animation now
  733.  
  734.          // call the default window message processing function to keep things going
  735.          return (DefWindowProc (hWnd, message, wParam, lParam));
  736.       }
  737.  
  738.       // else it's another location
  739.  
  740.       Audio_PlaySound (SOUNDTYPE_ILLEGALMOVE, 17.5f - (7 - the_board.hovered_position[1]) * 5.0f, 17.5f - the_board.hovered_position[0] * 5.0f, 0.04f); // play the "illegal move" sound
  741.  
  742.       the_scene.update = true; // update the 3D scene
  743.  
  744.       // call the default window message processing function to keep things going
  745.       return (DefWindowProc (hWnd, message, wParam, lParam));
  746.    }
  747.  
  748.    ////////////////////////////////////////////////////////////////////////////////////////////////
  749.    // right mouse button push
  750.    else if (message == WM_RBUTTONDOWN)
  751.    {
  752.       // are we in animation OR are mouse commands NOT allowed ?
  753.       if ((animation_endtime + 1.0f >= current_time) || (command_ignoretime >= current_time))
  754.          return (DefWindowProc (hWnd, message, wParam, lParam)); // if so, call the default message proc to keep things going
  755.  
  756.       prevgui_x = GET_X_LPARAM (lParam); // remember mouse coordinates
  757.       prevgui_y = GET_Y_LPARAM (lParam);
  758.  
  759.       // if we click something, stop moving the table immediately
  760.  
  761.       // cycle through both players and change their view angles EXCEPT the opponent if he's human
  762.       for (viewer_index = 0; viewer_index < 2; viewer_index++)
  763.          if ((the_board.players[viewer_index].type == PLAYER_COMPUTER)
  764.                || (the_board.players[viewer_index].type == PLAYER_INTERNET)
  765.                || (viewer_index == current_viewer))
  766.          {
  767.             the_board.players[viewer_index].view_pitch = current_pitch;
  768.             the_board.players[viewer_index].view_yaw = current_yaw;
  769.          }
  770.  
  771.       rbutton_pushed = true; // remember button is clicked
  772.  
  773.       // call the default window message processing function to keep things going
  774.       return (DefWindowProc (hWnd, message, wParam, lParam));
  775.    }
  776.  
  777.    ////////////////////////////////////////////////////////////////////////////////////////////////
  778.    // right mouse button release
  779.    else if (message == WM_RBUTTONUP)
  780.    {
  781.       // are we in animation OR are mouse commands NOT allowed ?
  782.       if ((animation_endtime + 1.0f >= current_time) || (command_ignoretime >= current_time))
  783.          return (DefWindowProc (hWnd, message, wParam, lParam)); // if so, call the default message proc to keep things going
  784.  
  785.       rbutton_pushed = false; // remember button is released
  786.       the_scene.update = true; // update the 3D scene
  787.  
  788.       // call the default window message processing function to keep things going
  789.       return (DefWindowProc (hWnd, message, wParam, lParam));
  790.    }
  791.  
  792.    ////////////////////////////////////////////////////////////////////////////////////////////////
  793.    // left mouse button DOUBLE-click
  794.    else if (message == WM_LBUTTONDBLCLK)
  795.    {
  796.       // are we in animation OR are mouse commands NOT allowed ?
  797.       if ((animation_endtime + 1.0f >= current_time) || (command_ignoretime >= current_time))
  798.          return (DefWindowProc (hWnd, message, wParam, lParam)); // if so, call the default message proc to keep things going
  799.  
  800.       // get mouse coordinates
  801.       gui_x = GET_X_LPARAM (lParam);
  802.       gui_y = GET_Y_LPARAM (lParam);
  803.  
  804.       // are we in game and did we click the move comments area ?
  805.       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))
  806.       {
  807.          DialogBox_Comment (); // fire up the comment dialog box
  808.          return (DefWindowProc (hWnd, message, wParam, lParam)); // call the default window message processing function to keep things going
  809.       }
  810.  
  811.       // call the default window message processing function to keep things going
  812.       return (DefWindowProc (hWnd, message, wParam, lParam));
  813.    }
  814.  
  815.    ////////////////////////////////////////////////////////////////////////////////////////////////
  816.    // mouse move (while not in animation)
  817.    else if (message == WM_MOUSEMOVE)
  818.    {
  819.       // are we in animation OR are mouse commands NOT allowed ?
  820.       if ((animation_endtime + 1.0f >= current_time) || (command_ignoretime >= current_time))
  821.          return (DefWindowProc (hWnd, message, wParam, lParam)); // if so, call the default message proc to keep things going
  822.  
  823.       // get mouse coordinates
  824.       gui_x = GET_X_LPARAM (lParam);
  825.       gui_y = GET_Y_LPARAM (lParam);
  826.  
  827.       // handle button update status
  828.       the_scene.update |= Button_UpdateHoverState (&the_scene.gui.llarrow, gui_x, gui_y);
  829.       the_scene.update |= Button_UpdateHoverState (&the_scene.gui.larrow, gui_x, gui_y);
  830.       the_scene.update |= Button_UpdateHoverState (&the_scene.gui.rarrow, gui_x, gui_y);
  831.       the_scene.update |= Button_UpdateHoverState (&the_scene.gui.rrarrow, gui_x, gui_y);
  832.       the_scene.update |= Button_UpdateHoverState (&the_scene.gui.newgamebutton, gui_x, gui_y);
  833.       the_scene.update |= Button_UpdateHoverState (&the_scene.gui.opengamebutton, gui_x, gui_y);
  834.       the_scene.update |= Button_UpdateHoverState (&the_scene.gui.chatbutton, gui_x, gui_y);
  835.       the_scene.update |= Button_UpdateHoverState (&the_scene.gui.gamesbutton, gui_x, gui_y);
  836.       the_scene.update |= Button_UpdateHoverState (&the_scene.gui.peoplebutton, gui_x, gui_y);
  837.  
  838.       // are we in board closeup mode OR are we online AND do we NOT have the right to select anything ?
  839.       if (((current_distance == CLOSEUP_VIEW_DISTANCE) && (current_pitch == CLOSEUP_VIEW_PITCH))
  840.           || ((remote_player != NULL) && !remote_player->is_in_game))
  841.          return (DefWindowProc (hWnd, message, wParam, lParam)); // if so, call the default window message processing function to keep things going
  842.  
  843.       // is the parts selection line displayed AND is the mouse anywhere in it ?
  844.       if (the_scene.gui.is_partspick_displayed && Render_IsMouseInBox (gui_x, gui_y, 0.0f, 0.0f, 100.0f, 11.0f))
  845.       {
  846.          // for each selectable part...
  847.          for (part_index = 0; part_index < 13; part_index++)
  848.          {
  849.             // is the mouse hovering it ?
  850.             if (Render_IsMouseInBox (gui_x, gui_y, part_index * (100.0f / 13.0f), 0, 100.0f / 13.0f, 11.0f))
  851.             {
  852.                // was it NOT hovered before ?
  853.                if (the_scene.gui.partspick_hoveredpart != selectable_parts[part_index])
  854.                {
  855.                   the_scene.gui.partspick_hoveredpart = selectable_parts[part_index]; // mark it as hovered
  856.                   the_scene.update = true; // update the scene
  857.                }
  858.             }
  859.  
  860.             // else was it hovered before ?
  861.             else if (the_scene.gui.partspick_hoveredpart == selectable_parts[part_index])
  862.             {
  863.                the_scene.gui.partspick_hoveredpart = 0; // clear the hovered part
  864.                the_scene.update = true; // update the scene
  865.             }
  866.          }
  867.       }
  868.       else
  869.          the_scene.gui.partspick_hoveredpart = 0; // clear the hovered part
  870.  
  871.       // if right button was clicked, compute new pitch and yaw
  872.       if (rbutton_pushed)
  873.       {
  874.          // cycle through both players and change their view angles EXCEPT the opponent if he's human
  875.          for (viewer_index = 0; viewer_index < 2; viewer_index++)
  876.             if ((the_board.players[viewer_index].type == PLAYER_COMPUTER)
  877.                 || (the_board.players[viewer_index].type == PLAYER_INTERNET)
  878.                 || (viewer_index == current_viewer))
  879.             {
  880.                the_board.players[viewer_index].view_pitch += (gui_y - prevgui_y) * 0.3f;
  881.                if (the_board.players[viewer_index].view_pitch < MIN_VIEW_PITCH)
  882.                   the_board.players[viewer_index].view_pitch = MIN_VIEW_PITCH; // wrap angles around so that they
  883.                if (the_board.players[viewer_index].view_pitch > MAX_VIEW_PITCH)
  884.                   the_board.players[viewer_index].view_pitch = MAX_VIEW_PITCH; // stay in the min-max pitch bounds
  885.  
  886.                the_board.players[viewer_index].view_yaw += (gui_x - prevgui_x) * 0.3f;
  887.                the_board.players[viewer_index].view_yaw = WrapAngle (the_board.players[viewer_index].view_yaw);
  888.  
  889.                // save these as the new custom angles
  890.                the_board.players[viewer_index].custom_pitch = the_board.players[viewer_index].view_pitch;
  891.                the_board.players[viewer_index].custom_yaw = the_board.players[viewer_index].view_yaw;
  892.  
  893.                // when moving the table around, jump to ideal angles immediately
  894.                current_pitch = the_board.players[viewer_index].view_pitch;
  895.                current_yaw = the_board.players[viewer_index].view_yaw;
  896.             }
  897.  
  898.          the_scene.update = true; // button was clicked, update the 3D scene
  899.       }
  900.  
  901.       // else it's just the mouse wandering around ; have we the right to select something ?
  902.       else if ((the_board.viewed_move == the_board.move_count - 1) && (current_player->type == PLAYER_HUMAN) && (highlight_endtime < current_time))
  903.       {
  904.          // save the old positions
  905.          prev_hovered_position[0] = the_board.hovered_position[0];
  906.          prev_hovered_position[1] = the_board.hovered_position[1];
  907.  
  908.          // figure out the coordinates on table
  909.          Render_MouseToFloor (gui_x, gui_y, &board_x, &board_y);
  910.  
  911.          // translate them to board coordinates
  912.          the_board.hovered_position[0] = (int) floor ((20.0f - board_y) / 5.0f);
  913.          the_board.hovered_position[1] = 7 - (int) floor ((20.0f - board_x) / 5.0f);
  914.  
  915.          // do they differ from last time ?
  916.          if ((the_board.hovered_position[0] != prev_hovered_position[0]) || (the_board.hovered_position[1] != prev_hovered_position[1]))
  917.             the_scene.update = true; // if so, update scene
  918.       }
  919.  
  920.       // has the user the right to leave a comment AND is there no comment yet ?
  921.       if ((the_board.game_state >= STATE_PLAYING) && (the_board.viewed_move > 0)
  922.           && ((the_board.moves[the_board.viewed_move].comment == NULL) || (the_board.moves[the_board.viewed_move].comment[0] == 0)))
  923.       {
  924.          // is the mouse above the comments zone ? if so, display a dimmed hint text
  925.          if (Render_IsMouseInBox (gui_x, gui_y, 30.0f, 0.0f, 40.0f, 10.0f))
  926.          {
  927.             if (!the_scene.gui.comment_text.is_displayed)
  928.             {
  929.                Scene_UpdateText (&the_scene.gui.comment_text, RGBA_TO_RGBACOLOR (255, 255, 255, 127), DURATION_INFINITE, false, L"\n\n%s", LOCALIZE (L"DoubleClickToEnterComment"));
  930.                the_scene.update = true; // and update the scene
  931.             }
  932.          }
  933.          else
  934.          {
  935.             if (the_scene.gui.comment_text.is_displayed)
  936.             {
  937.                the_scene.gui.comment_text.is_displayed = false; // if not, erase the hint text
  938.                the_scene.update = true; // and update the scene
  939.             }
  940.          }
  941.       }
  942.  
  943.       // remember these coordinates for next time
  944.       prevgui_x = gui_x;
  945.       prevgui_y = gui_y;
  946.  
  947.       // call the default window message processing function to keep things going
  948.       return (DefWindowProc (hWnd, message, wParam, lParam));
  949.    }
  950.  
  951.    ////////////////////////////////////////////////////////////////////////////////////////////////
  952.    // mouse scroll
  953.    else if (message == WM_MOUSEWHEEL)
  954.    {
  955.       // are we in animation OR are mouse commands NOT allowed ?
  956.       if ((animation_endtime + 1.0f >= current_time) || (command_ignoretime >= current_time))
  957.          return (DefWindowProc (hWnd, message, wParam, lParam)); // if so, call the default message proc to keep things going
  958.  
  959.       // are we in board closeup mode OR are we online AND do we NOT have the right to select anything ?
  960.       if (((current_distance == CLOSEUP_VIEW_DISTANCE) && (current_pitch == CLOSEUP_VIEW_PITCH))
  961.           || ((remote_player != NULL) && !remote_player->is_in_game))
  962.          return (DefWindowProc (hWnd, message, wParam, lParam)); // if so, call the default window message processing function to keep things going
  963.  
  964.       // get mouse coordinates (strangely enough, the WM_MOUSEWHEEL message outputs ABSOLUTE coordinates, and we need to convert them...)
  965.       point.x = GET_X_LPARAM (lParam);
  966.       point.y = GET_Y_LPARAM (lParam);
  967.       ScreenToClient (hWnd, &point);
  968.       gui_x = point.x;
  969.       gui_y = point.y;
  970.  
  971.       // is a game history displayed and are we (roughly) hovering this area ?
  972.       if (the_scene.gui.history_text.is_displayed && Render_IsMouseInBox (gui_x, gui_y, 90.0f, 10.0f, 10.0f, 40.0f))
  973.       {
  974.          // we want to scroll through the game history rather than zooming in/out. Scroll up or scroll down ?
  975.          if ((GET_WHEEL_DELTA_WPARAM (wParam) > 0) && (the_scene.gui.larrow.state != 0))
  976.             SendMessage (hWnd, WM_COMMAND, MENUID_CHESSBOARD_PREVIOUSMOVE, NULL); // send a "previous move" event
  977.          else if ((GET_WHEEL_DELTA_WPARAM (wParam) < 0) && (the_scene.gui.rarrow.state != 0))
  978.             SendMessage (hWnd, WM_COMMAND, MENUID_CHESSBOARD_NEXTMOVE, NULL); // send a "next move" event
  979.  
  980.          return (DefWindowProc (hWnd, message, wParam, lParam)); // and call the default window message processing function to keep things going
  981.       }
  982.  
  983.       // we want to zoom in/out of the scene. Scroll up or scroll down ?
  984.       if (GET_WHEEL_DELTA_WPARAM (wParam) > 0)
  985.          the_board.players[current_viewer].view_distance = max (MIN_VIEW_DISTANCE, the_board.players[current_viewer].view_distance - 2.0f);
  986.       else if (GET_WHEEL_DELTA_WPARAM (wParam) < 0)
  987.          the_board.players[current_viewer].view_distance = min (MAX_VIEW_DISTANCE, the_board.players[current_viewer].view_distance + 2.0f);
  988.  
  989.       // save this as the new custom distance
  990.       the_board.players[current_viewer].custom_distance = the_board.players[current_viewer].view_distance;
  991.  
  992.       // when moving the table around, jump to ideal angles immediately
  993.       current_distance = the_board.players[current_viewer].view_distance;
  994.  
  995.       the_scene.update = true; // update the 3D scene
  996.  
  997.       // call the default window message processing function to keep things going
  998.       return (DefWindowProc (hWnd, message, wParam, lParam));
  999.    }
  1000.  
  1001.    ////////////////////////////////////////////////////////////////////////////////////////////////
  1002.    // keyboard release
  1003.    else if (message == WM_CHAR)
  1004.    {
  1005.       // are we in position setup mode ?
  1006.       if (the_board.game_state == STATE_SETUPPOSITION)
  1007.       {
  1008.          // is it the enter key ? if so, exit setup mode and start playing
  1009.          if (wParam == L'\r')
  1010.          {
  1011.             current_move = &the_board.moves[the_board.viewed_move]; // quick access to current move
  1012.  
  1013.             // make sure there is at least one king of each color (i.e. two kings)
  1014.             part_index = 0;
  1015.             for (line = 0; line < 8; line++)
  1016.                for (column = 0; column < 8; column++)
  1017.                   if (current_move->slots[line][column].part == PART_KING)
  1018.                      part_index++; // there's one king more on this board
  1019.             if (part_index != 2)
  1020.                return (DefWindowProc (hWnd, message, wParam, lParam)); // when at least one king is missing, we just can't leave edition mode
  1021.  
  1022.             // (in)validate the castling positions
  1023.             if ((current_move->slots[0][0].color == COLOR_WHITE) && (current_move->slots[0][0].part == PART_ROOK)
  1024.                 && (current_move->slots[0][4].color == COLOR_WHITE) && (current_move->slots[0][4].part == PART_KING))
  1025.                current_move->sides[COLOR_WHITE].longcastle_allowed = true; // white castling queenside allowed
  1026.             else
  1027.                current_move->sides[COLOR_WHITE].longcastle_allowed = false; // white castling queenside no longer possible
  1028.             if ((current_move->slots[0][7].color == COLOR_WHITE) && (current_move->slots[0][7].part == PART_ROOK)
  1029.                 && (current_move->slots[0][4].color == COLOR_WHITE) && (current_move->slots[0][4].part == PART_KING))
  1030.                current_move->sides[COLOR_WHITE].shortcastle_allowed = true; // white castling kingside allowed
  1031.             else
  1032.                current_move->sides[COLOR_WHITE].shortcastle_allowed = false; // white castling kingside no longer possible
  1033.             if ((current_move->slots[7][0].color == COLOR_BLACK) && (current_move->slots[7][0].part == PART_ROOK)
  1034.                 && (current_move->slots[7][4].color == COLOR_BLACK) && (current_move->slots[7][4].part == PART_KING))
  1035.                current_move->sides[COLOR_BLACK].longcastle_allowed = true; // white castling queenside allowed
  1036.             else
  1037.                current_move->sides[COLOR_BLACK].longcastle_allowed = false; // white castling queenside no longer possible
  1038.             if ((current_move->slots[7][7].color == COLOR_BLACK) && (current_move->slots[7][7].part == PART_ROOK)
  1039.                 && (current_move->slots[7][4].color == COLOR_BLACK) && (current_move->slots[7][4].part == PART_KING))
  1040.                current_move->sides[COLOR_BLACK].shortcastle_allowed = true; // white castling kingside allowed
  1041.             else
  1042.                current_move->sides[COLOR_BLACK].shortcastle_allowed = false; // white castling kingside no longer possible
  1043.  
  1044.             current_move->color = COLOR_BLACK; // so that game starts with white
  1045.  
  1046.             // validate this board in Forsyth-Edwards Notation and reset it
  1047.             Move_DescribeInFEN (current_move);
  1048.             wcscpy_s (fen_string, WCHAR_SIZEOF (fen_string), current_move->fen_string); // have a copy of fen string
  1049.             Board_Reset (&the_board, fen_string);
  1050.  
  1051.             the_board.game_state = STATE_PLAYING; // start the game now
  1052.             the_board.reevaluate = true; // evaluate board again
  1053.  
  1054.             the_scene.gui.central_text.disappear_time = current_time + 1.0f; // fade out help text now (FIXME: ugly)
  1055.             the_scene.update = true; // update the 3D scene
  1056.  
  1057.             // call the default window message processing function to keep things going
  1058.             return (DefWindowProc (hWnd, message, wParam, lParam));
  1059.          }
  1060.       }
  1061.  
  1062.       // else are we in internet mode ?
  1063.       else if (remote_player != NULL)
  1064.       {
  1065.          entered_ccreply = &the_scene.gui.entered_ccreply; // quick access to entered ccreply
  1066.          local_player = Player_FindByType (PLAYER_HUMAN); // quick access to local player
  1067.          if (local_player == NULL)
  1068.             return (DefWindowProc (hWnd, message, wParam, lParam)); // theoretically impossible condition, but better be sure
  1069.          if (selected_chatterchannel == NULL)
  1070.             return (DefWindowProc (hWnd, message, wParam, lParam)); // theoretically impossible condition, but better be sure
  1071.  
  1072.          // are we NOT entering text yet ?
  1073.          if (!the_scene.gui.is_entering_text)
  1074.          {
  1075.             // is it the space bar ?
  1076.             if (wParam == L' ')
  1077.             {
  1078.                the_scene.gui.is_entering_text = true; // remember we are entering text
  1079.  
  1080.                // reset the entered text buffer
  1081.                entered_ccreply->text = (wchar_t *) SAFE_malloc (1, sizeof (wchar_t), false);
  1082.                entered_ccreply->text[0] = 0; // only the null terminator will fit
  1083.                entered_ccreply->text_length = 0; // and set its length to zero
  1084.             }
  1085.          }
  1086.  
  1087.          // else we are currently in text entering mode
  1088.          else
  1089.          {
  1090.             // is the typed character a printable character ?
  1091.             if (iswprint (wParam))
  1092.             {
  1093.                // if so, reallocate space in the typed buffer (include null terminator)
  1094.                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);
  1095.                swprintf_s (&entered_ccreply->text[entered_ccreply->text_length], 1 + 1, L"%c", wParam); // append character
  1096.                entered_ccreply->text_length++; // buffer holds now one character more
  1097.             }
  1098.  
  1099.             // else is the typed character a backspace AND is there text to erase ?
  1100.             else if ((wParam == 0x08) && (entered_ccreply->text_length > 0))
  1101.             {
  1102.                // if so, reallocate space in the typed buffer (include null terminator)
  1103.                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);
  1104.                entered_ccreply->text[entered_ccreply->text_length - 1] = 0; // backspace, delete one character
  1105.                entered_ccreply->text_length--; // buffer holds now one character less
  1106.             }
  1107.  
  1108.             // else is the typed character the escape key ?
  1109.             else if (wParam == 0x1b)
  1110.             {
  1111.                SAFE_free ((void **) &entered_ccreply->text); // reset the entered text buffer
  1112.                entered_ccreply->text_length = 0; // and set its length to zero
  1113.                the_scene.gui.is_entering_text = false; // and exit from the text entering mode
  1114.             }
  1115.  
  1116.             // else is the typed character the enter key ?
  1117.             else if (wParam == 0x0d)
  1118.                the_scene.gui.is_entering_text = false; // enter, exit from the text entering mode (this will validate our reply)
  1119.  
  1120.             // else it's an illegal character
  1121.             else
  1122.                Audio_PlaySound (SOUNDTYPE_ILLEGALMOVE, 0.0f, 0.0f, 0.04f); // illegal character, beep the illegal move sound at the center of the board
  1123.          }
  1124.  
  1125.          the_scene.update = true; // update the 3D scene
  1126.  
  1127.          // call the default window message processing function to keep things going
  1128.          return (DefWindowProc (hWnd, message, wParam, lParam));
  1129.       }
  1130.    }
  1131.  
  1132.    ////////////////////////////////////////////////////////////////////////////////////////////////
  1133.    // mouse move outside the window
  1134.    else if (message == WM_NCMOUSEMOVE)
  1135.    {
  1136.       rbutton_pushed = false; // remember right button is released
  1137.  
  1138.       // call the default window message processing function to keep things going
  1139.       return (DefWindowProc (hWnd, message, wParam, lParam));
  1140.    }
  1141.  
  1142.    ////////////////////////////////////////////////////////////////////////////////////////////////
  1143.    // window repaint
  1144.    else if (message == WM_PAINT)
  1145.    {
  1146.       the_scene.update = true; // update the 3D scene
  1147.  
  1148.       // call the default window message processing function to keep things going
  1149.       return (DefWindowProc (hWnd, message, wParam, lParam));
  1150.    }
  1151.  
  1152.    // call the default window message processing function to keep things going
  1153.    return (DefWindowProc (hWnd, message, wParam, lParam));
  1154. }
  1155.  
  1156.  
  1157. static bool Button_IsHovered (guibutton_t *button, int gui_x, int gui_y)
  1158. {
  1159.    // handy wrapper that returns whether a particular GUI button is hovered when the mouse is at the given coordinates
  1160.  
  1161.    return (Render_IsMouseInBox (gui_x, gui_y, button->left, button->top, button->width, button->height));
  1162. }
  1163.  
  1164.  
  1165. static bool Button_UpdateHoverState (guibutton_t *button, int gui_x, int gui_y)
  1166. {
  1167.    // this function updates the hover state of a GUI button, and returns TRUE if the
  1168.    // scene needs to be updated, FALSE otherwise.
  1169.  
  1170.    // is the button displayed ?
  1171.    if (button->state != 0)
  1172.    {
  1173.       // is the mouse hovering it ?
  1174.       if (Render_IsMouseInBox (gui_x, gui_y, button->left, button->top, button->width, button->height))
  1175.       {
  1176.          // was it NOT hovered before ?
  1177.          if (button->state != 2)
  1178.          {
  1179.             button->state = 2; // mark it as hovered
  1180.             return (true); // return TRUE so as to update the scene
  1181.          }
  1182.       }
  1183.  
  1184.       // else was it hovered before ?
  1185.       else if (button->state == 2)
  1186.       {
  1187.          button->state = 1; // else mark it as not hovered
  1188.          return (true); // return TRUE so as to update the scene
  1189.       }
  1190.    }
  1191.  
  1192.    return (false); // no need to update the scene
  1193. }
  1194.