Subversion Repositories Games.Chess Giants

Rev

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