// window_main.cpp
 
 
 
#include "../common.h"
 
 
 
 
 
// global variables used in this module only
 
static wchar_t folder_path[MAX_PATH];
 
 
 
 
 
// prototypes of local functions
 
static bool Button_IsHovered (guibutton_t *button, int gui_x, int gui_y);
 
static bool Button_UpdateHoverState (guibutton_t *button, int gui_x, int gui_y);
 
 
 
 
 
LRESULT CALLBACK WindowProc_Main (HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam)
 
{
 
   // this is the main message handler for the program
 
 
 
   static wchar_t fen_string[128];
 
   static char selectable_parts[14] = "PRNBQK kqbnrp";
 
   static int prevgui_x = 0;
 
   static int prevgui_y = 0;
 
   static bool ctrl_pressed = false;
 
   static bool rbutton_pushed = false;
 
 
 
   unsigned short wParam_hiword;
 
   unsigned short wParam_loword;
 
   int prev_hovered_position[2];
 
   player_t *current_player;
 
   player_t *opposite_player;
 
   player_t *local_player;
 
   player_t *remote_player;
 
   ccreply_t *entered_ccreply;
 
   boardmove_t *current_move;
 
   float board_x;
 
   float board_y;
 
   int gui_x;
 
   int gui_y;
 
   int line;
 
   int column;
 
   int index_line;
 
   int index_column;
 
   int part_index;
 
   int part_color;
 
   int viewer_index;
 
 
 
   // filter out the commonly used message values
 
   wParam_hiword = HIWORD (wParam);
 
   wParam_loword = LOWORD (wParam);
 
 
 
   ////////////////////////////////////////////////////////////////////////////////////////////////
 
   // has the window just been fired up ?
 
   if (message == WM_CREATE)
 
   {
 
      the_scene.gui.is_entering_text = false; // we are NOT entering text yet
 
 
 
      // call the default window message processing function to keep things going
 
      return (DefWindowProc (hWnd, message, wParam, lParam));
 
   }
 
 
 
   ////////////////////////////////////////////////////////////////////////////////////////////////
 
   // else have we clicked on the close button ?
 
   else if (message == WM_CLOSE)
 
   {
 
      if ((the_board.game_state == STATE_PLAYING) && ((the_board.move_count > 1) || (Player_FindByType (PLAYER_INTERNET) != NULL)))
 
         DialogBox_Quit (); // if a game has started OR if we are online against somebody, ask for confirmation
 
      else
 
         is_dialogbox_quit_validated = true; // if game hasn't started yet, quit without question
 
 
 
      return (0); // don't let Windows do the default processing on this message
 
   }
 
 
 
   ////////////////////////////////////////////////////////////////////////////////////////////////
 
   // else is the user closing its session OR the window is being destroyed ?
 
   else if ((message == WM_QUERYENDSESSION) || (message == WM_ENDSESSION) || (message == WM_DESTROY))
 
      terminate_everything = true; // suicide (for some reason, PostQuitMessage() is unreliable !?)
 
 
 
   /////////////////
 
   // MENU EVENTS //
 
   /////////////////
 
 
 
   ///////////////
 
   // menu command
 
   else if (message == WM_COMMAND)
 
   {
 
      // game menu, new game
 
      if (wParam_loword == MENUID_GAME_NEWGAME)
 
      {
 
         if ((the_board.game_state == STATE_PLAYING) && (the_board.move_count > 1))
 
            DialogBox_Resign (); // if a game is playing, ask to resign first
 
         else
 
            DialogBox_NewGame (); // game not started yet, show the new game dialog box
 
      }
 
 
 
      // game menu, setup start position
 
      else if (wParam_loword == MENUID_GAME_SETUPPOSITION)
 
      {
 
         if ((the_board.game_state == STATE_PLAYING) && (the_board.move_count > 1))
 
            DialogBox_Resign (); // if a game is playing, ask to resign first
 
         else
 
         {
 
            current_move = &the_board.moves[0]; // quick access to start move
 
            memset (¤t_move->slots, 0, sizeof (current_move->slots)); // erase all slots
 
 
 
            the_board.game_state = STATE_SETUPPOSITION; // game not started yet, enter board setup mode
 
            the_board.reevaluate = true; // evaluate board again
 
 
 
            // display the "please choose start position" phrase in the middle of the screen
 
            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),
 
                           999999.0f, true, LOCALIZE (L"SetupMode"));
 
            the_scene.gui.partspick_selectedpart = ' '; // no selected part yet
 
            the_scene.update = true; // and update the 3D scene
 
         }
 
      }
 
 
 
      // game menu, load game
 
      else if (wParam_loword == MENUID_GAME_LOAD)
 
         DialogBox_Load (); // fire up the load dialog box
 
 
 
      // game menu, save game
 
      else if (wParam_loword == MENUID_GAME_SAVE)
 
      {
 
         if (save_pathname[0] != 0)
 
            is_dialogbox_save_validated = true; // if the filename is known, save it directly
 
         else
 
            DialogBox_Save (false); // else fire up the save dialog box
 
      }
 
 
 
      // game menu, save as
 
      else if (wParam_loword == MENUID_GAME_SAVEAS)
 
         DialogBox_Save (false); // fire up the save dialog box
 
 
 
      // game menu, save position as
 
      else if (wParam_loword == MENUID_GAME_SAVEPOSITIONAS)
 
         DialogBox_SavePosition (); // fire up the save position dialog box
 
 
 
      // game menu, pause
 
      else if (wParam_loword == MENUID_GAME_PAUSE)
 
      {
 
         is_paused ^= true; // toggle game pause state on/off
 
         CheckMenuItem (GetMenu (hWnd), MENUID_GAME_PAUSE, (is_paused ? MF_CHECKED : MF_UNCHECKED)); // highlight the menu entry as necessary
 
         the_board.reevaluate = true; // evaluate board again
 
      }
 
 
 
      // game menu, resign
 
      else if (wParam_loword == MENUID_GAME_RESIGN)
 
         DialogBox_Resign (); // if a game has started, ask for confirmation
 
 
 
      // game menu, statistics (stats are only available in online mode)
 
      else if (wParam_loword == MENUID_GAME_STATISTICS)
 
      {
 
         local_player = Player_FindByType (PLAYER_HUMAN);
 
         if (local_player != NULL)
 
            PlayerCard_FindOrCreate (local_player->name); // display player's own player card
 
      }
 
 
 
      // game menu, options
 
      else if (wParam_loword == MENUID_GAME_OPTIONS)
 
         DialogBox_Options (); // fire up the options dialog box
 
 
 
      // game menu, quit
 
      else if (wParam_loword == MENUID_GAME_QUIT)
 
      {
 
         if ((the_board.game_state == STATE_PLAYING) && ((the_board.move_count > 1) || (Player_FindByType (PLAYER_INTERNET) != NULL)))
 
            DialogBox_Quit (); // if a game has started OR if we are online against somebody, ask for confirmation
 
         else
 
            is_dialogbox_quit_validated = true; // if game hasn't started yet, quit without question
 
      }
 
 
 
      // chessboard menu, suggest move
 
      else if (wParam_loword == MENUID_CHESSBOARD_SUGGESTMOVE)
 
         the_board.players[current_viewer].wants_hint = true; // remember this player wants a hint
 
 
 
      // chessboard menu, cancel last move
 
      else if (wParam_loword == MENUID_CHESSBOARD_CANCELLASTMOVE)
 
         the_board.players[current_viewer].wants_cancel = true; // remember this player wants to cancel his last move
 
 
 
      // chessboard menu, comment move
 
      else if (wParam_loword == MENUID_CHESSBOARD_COMMENTMOVE)
 
         DialogBox_Comment (); // fire up the comment dialog box
 
 
 
      // chessboard menu, go to move
 
      else if (wParam_loword == MENUID_CHESSBOARD_GOTOMOVE)
 
         DialogBox_GoToMove (); // fire up the go to move dialog box
 
 
 
      // chessboard menu, swap sides
 
      else if (wParam_loword == MENUID_CHESSBOARD_SWAPSIDES)
 
         the_board.want_playerswap = true; // remember board sides are to be swapped
 
 
 
      // chessboard menu, rename players
 
      else if (wParam_loword == MENUID_CHESSBOARD_RENAMESIDES)
 
         DialogBox_RenameSides(); // fire up the rename sides dialog box
 
 
 
      // chessboard menu, beginning of game
 
      else if (wParam_loword == MENUID_CHESSBOARD_BEGINNINGOFGAME)
 
      {
 
         the_board.viewed_move = 0; // enter view mode and go to the beginning of the game
 
         Audio_PlaySound (SOUNDTYPE_CLICK, 0.0f, 0.0f, 0.04f); // make a click sound at the center of the board
 
         the_board.reevaluate = true; // evaluate board again
 
      }
 
 
 
      // chessboard menu, previous move (only if arrow is enabled)
 
      else if ((wParam_loword == MENUID_CHESSBOARD_PREVIOUSMOVE) && (the_scene.gui.larrow.state != 0))
 
      {
 
         the_board.viewed_move--; // enter view mode and go back one move
 
         Audio_PlaySound (SOUNDTYPE_CLICK, 0.0f, 0.0f, 0.04f); // make a click sound at the center of the board
 
         the_board.reevaluate = true; // evaluate board again
 
      }
 
 
 
      // chessboard menu, next move (only if arrow is enabled)
 
      else if ((wParam_loword == MENUID_CHESSBOARD_NEXTMOVE) && (the_scene.gui.rarrow.state != 0))
 
      {
 
         the_board.viewed_move++; // enter view mode and go forward one move
 
         Audio_PlaySound (SOUNDTYPE_CLICK, 0.0f, 0.0f, 0.04f); // make a click sound at the center of the board
 
         the_board.reevaluate = true; // evaluate board again
 
      }
 
 
 
      // chessboard menu, current state of game
 
      else if (wParam_loword == MENUID_CHESSBOARD_CURRENTSTATEOFGAME)
 
      {
 
         the_board.viewed_move = the_board.move_count - 1; // enter view mode and go to the current state of the game
 
         Audio_PlaySound (SOUNDTYPE_CLICK, 0.0f, 0.0f, 0.04f); // make a click sound at the center of the board
 
         the_board.reevaluate = true; // evaluate board again
 
      }
 
 
 
      // chessboard menu, top view
 
      else if (wParam_loword == MENUID_CHESSBOARD_TOPVIEW)
 
      {
 
         // cycle through both players and change their view angles EXCEPT the opponent if he's human
 
         for (viewer_index = 0; viewer_index < 2; viewer_index++)
 
            if ((the_board.players[viewer_index].type == PLAYER_COMPUTER)
 
                || (the_board.players[viewer_index].type == PLAYER_INTERNET)
 
                || (viewer_index == current_viewer))
 
            {
 
               the_board.players[viewer_index].view_pitch = 89.99f;
 
               the_board.players[viewer_index].view_yaw = (current_viewer == COLOR_BLACK ? 90.0f : -90.0f);
 
               the_board.players[viewer_index].view_distance = 58.0f;
 
            }
 
      }
 
 
 
      // chessboard menu, default view
 
      else if (wParam_loword == MENUID_CHESSBOARD_DEFAULTVIEW)
 
      {
 
         // cycle through both players and change their view angles EXCEPT the opponent if he's human
 
         for (viewer_index = 0; viewer_index < 2; viewer_index++)
 
            if ((the_board.players[viewer_index].type == PLAYER_COMPUTER)
 
                || (the_board.players[viewer_index].type == PLAYER_INTERNET)
 
                || (viewer_index == current_viewer))
 
            {
 
               the_board.players[viewer_index].view_pitch = 55.0f;
 
               the_board.players[viewer_index].view_yaw = (current_viewer == COLOR_BLACK ? 90.0f : -90.0f);
 
               the_board.players[viewer_index].view_distance = 70.0f;
 
            }
 
      }
 
 
 
      // chessboard menu, reset view
 
      else if (wParam_loword == MENUID_CHESSBOARD_RESETVIEW)
 
      {
 
         // cycle through both players and change their view angles EXCEPT the opponent if he's human
 
         for (viewer_index = 0; viewer_index < 2; viewer_index++)
 
            if ((the_board.players[viewer_index].type == PLAYER_COMPUTER)
 
                || (the_board.players[viewer_index].type == PLAYER_INTERNET)
 
                || (viewer_index == current_viewer))
 
            {
 
               the_board.players[viewer_index].view_pitch = the_board.players[current_viewer].custom_pitch;
 
               the_board.players[viewer_index].view_yaw = the_board.players[current_viewer].custom_yaw;
 
               the_board.players[viewer_index].view_distance = the_board.players[current_viewer].custom_distance;
 
            }
 
      }
 
 
 
      // chessboard menu, zoom in
 
      else if (wParam_loword == MENUID_CHESSBOARD_ZOOMIN)
 
      {
 
         // scroll up & save this as the new custom distance
 
         the_board.players[current_viewer].view_distance = max (48.0f, the_board.players[current_viewer].view_distance - 2.0f);
 
         the_board.players[current_viewer].custom_distance = the_board.players[current_viewer].view_distance;
 
         the_scene.update = true; // update the 3D scene
 
      }
 
 
 
      // chessboard menu, zoom out
 
      else if (wParam_loword == MENUID_CHESSBOARD_ZOOMOUT)
 
      {
 
         // scroll down & save this as the new custom distance
 
         the_board.players[current_viewer].view_distance = min (100.0f, the_board.players[current_viewer].view_distance + 2.0f);
 
         the_board.players[current_viewer].custom_distance = the_board.players[current_viewer].view_distance;
 
         the_scene.update = true; // update the 3D scene
 
      }
 
 
 
      // chessboard menu, change appearance
 
      else if (wParam_loword == MENUID_CHESSBOARD_CHANGEAPPEARANCE)
 
         DialogBox_ChangeAppearance ();
 
 
 
      // chessboard menu, display windows desktop
 
      else if (wParam_loword == MENUID_CHESSBOARD_DISPLAYWINDOWSDESKTOP)
 
         ShowWindow (hWnd, SW_MINIMIZE);
 
 
 
      // internet menu, show online players
 
      else if (wParam_loword == MENUID_INTERNET_SHOWONLINEPLAYERS)
 
         Window_Opponents ();
 
 
 
      // internet menu, show sought games
 
      else if (wParam_loword == MENUID_INTERNET_SHOWSOUGHTGAMES)
 
         Window_Sought ();
 
 
 
      // internet menu, seek game
 
      else if (wParam_loword == MENUID_INTERNET_SEEKGAME)
 
         DialogBox_SendSeek ();
 
 
 
      // internet menu, chatter channels
 
      else if (wParam_loword == MENUID_INTERNET_CHATTERCHANNELS)
 
         Window_ChatterChannels ();
 
 
 
      // internet menu, enter chat text
 
      else if (wParam_loword == MENUID_INTERNET_ENTERCHATTEXT)
 
         PostMessage (hWnd, WM_CHAR, L' ', 0); // emulate a space bar hit
 
 
 
      // internet menu, display player card
 
      else if (wParam_loword == MENUID_INTERNET_DISPLAYPLAYERCARD)
 
         DialogBox_PlayerInfoName ();
 
 
 
      // internet menu, display your card
 
      else if (wParam_loword == MENUID_INTERNET_DISPLAYYOURCARD)
 
      {
 
         local_player = Player_FindByType (PLAYER_HUMAN);
 
         if (local_player != NULL)
 
            PlayerCard_FindOrCreate (local_player->name); // display player's own player card
 
      }
 
 
 
      // internet menu, MOTD
 
      else if (wParam_loword == MENUID_INTERNET_MOTD)
 
         Window_MOTD ();
 
 
 
      // help menu, help
 
      else if (wParam_loword == MENUID_HELP_HELP)
 
      {
 
         swprintf_s (folder_path, WCHAR_SIZEOF (folder_path), L"%s\\Chess Giants (%s).html", app_path, languages[language_id].name);
 
         if (_waccess (folder_path, 0) != 0)
 
            swprintf_s (folder_path, WCHAR_SIZEOF (folder_path), L"%s\\Chess Giants (English).html", app_path);
 
         ShellExecute (NULL, L"open", folder_path, NULL, NULL, SW_MAXIMIZE); // fire up the help file
 
      }
 
 
 
      // help menu, get chess games
 
      else if (wParam_loword == MENUID_HELP_GETCHESSGAMES)
 
         ShellExecute (NULL, L"open", L"http://www.chessgames.com", NULL, NULL, SW_MAXIMIZE); // fire up the browser
 
 
 
      // help menu, add/modify visual themes
 
      else if (wParam_loword == MENUID_HELP_ADDMODIFYVISUALTHEMES)
 
      {
 
         swprintf_s (folder_path, WCHAR_SIZEOF (folder_path), L"%s\\themes", app_path);
 
         ShellExecute (NULL, L"open", folder_path, NULL, NULL, SW_SHOWNORMAL); // fire up Windows Explorer
 
      }
 
 
 
      // help menu, add/modify engines
 
      else if (wParam_loword == MENUID_HELP_ADDMODIFYENGINES)
 
      {
 
         swprintf_s (folder_path, WCHAR_SIZEOF (folder_path), L"%s\\engines", app_path);
 
         ShellExecute (NULL, L"open", folder_path, NULL, NULL, SW_SHOWNORMAL); // fire up Windows Explorer
 
      }
 
 
 
      // help menu, add/modify translations
 
      else if (wParam_loword == MENUID_HELP_ADDMODIFYTRANSLATIONS)
 
      {
 
         swprintf_s (folder_path, WCHAR_SIZEOF (folder_path), L"%s\\data\\languages", app_path);
 
         ShellExecute (NULL, L"open", folder_path, NULL, NULL, SW_SHOWNORMAL); // fire up Windows Explorer
 
      }
 
 
 
      // help menu, about
 
      else if (wParam_loword == MENUID_HELP_ABOUT)
 
         DialogBox_About (); // show the About dialog box
 
 
 
      // call the default window message processing function to keep things going
 
      return (DefWindowProc (hWnd, message, wParam, lParam));
 
   }
 
 
 
   /////////////////////////////////////////////////////
 
   // ctrl key press or release (while not in animation)
 
   else if ((message == WM_KEYDOWN) && (wParam == VK_CONTROL) && (animation_endtime + 1.0f < current_time))
 
   {
 
      ctrl_pressed = true; // remember the ctrl key is pressed
 
 
 
      // call the default window message processing function to keep things going
 
      return (DefWindowProc (hWnd, message, wParam, lParam));
 
   }
 
   else if ((message == WM_KEYUP) && (wParam == VK_CONTROL) && (animation_endtime + 1.0f < current_time))
 
   {
 
      ctrl_pressed = false; // remember the ctrl key is released
 
      rbutton_pushed = false; // remember button is released
 
      the_scene.update = true; // update the 3D scene
 
 
 
      // call the default window message processing function to keep things going
 
      return (DefWindowProc (hWnd, message, wParam, lParam));
 
   }
 
 
 
   //////////////////
 
   // MOUSE EVENTS //
 
   //////////////////
 
 
 
   ////////////////////////////////////////////////////////////////////////////////////////////////
 
   // left mouse button push
 
   else if (message == WM_LBUTTONDOWN)
 
   {
 
      // are we in animation OR are mouse commands NOT allowed ?
 
      if ((animation_endtime + 1.0f >= current_time) || (command_ignoretime >= current_time))
 
         return (DefWindowProc (hWnd, message, wParam, lParam)); // if so, call the default message proc to keep things going
 
 
 
      // is the ctrl key pressed (emulates a right click) ?
 
      if (ctrl_pressed)
 
      {
 
         prevgui_x = GET_X_LPARAM (lParam); // remember mouse coordinates
 
         prevgui_y = GET_Y_LPARAM (lParam);
 
 
 
         // if we click something, stop moving the table immediately
 
 
 
         // cycle through both players and change their view angles EXCEPT the opponent if he's human
 
         for (viewer_index = 0; viewer_index < 2; viewer_index++)
 
            if ((the_board.players[viewer_index].type == PLAYER_COMPUTER)
 
                || (the_board.players[viewer_index].type == PLAYER_INTERNET)
 
                || (viewer_index == current_viewer))
 
            {
 
               the_board.players[viewer_index].view_pitch = current_pitch;
 
               the_board.players[viewer_index].view_yaw = current_yaw;
 
            }
 
 
 
         rbutton_pushed = true; // remember button is clicked
 
      }
 
 
 
      // call the default window message processing function to keep things going
 
      return (DefWindowProc (hWnd, message, wParam, lParam));
 
   }
 
 
 
   ////////////////////////////////////////////////////////////////////////////////////////////////
 
   // left mouse button release
 
   else if (message == WM_LBUTTONUP)
 
   {
 
      // are we in animation OR are mouse commands NOT allowed ?
 
      if ((animation_endtime + 1.0f >= current_time) || (command_ignoretime >= current_time))
 
         return (DefWindowProc (hWnd, message, wParam, lParam)); // if so, call the default message proc to keep things going
 
 
 
      // is the ctrl key pressed (emulates a right click) ?
 
      if (ctrl_pressed)
 
      {
 
         rbutton_pushed = false; // remember button is released
 
         the_scene.update = true; // update the 3D scene
 
 
 
         // call the default window message processing function to keep things going
 
         return (DefWindowProc (hWnd, message, wParam, lParam));
 
      }
 
 
 
      prevgui_x = GET_X_LPARAM (lParam); // remember mouse coordinates
 
      prevgui_y = GET_Y_LPARAM (lParam);
 
 
 
      // get mouse coordinates
 
      gui_x = GET_X_LPARAM (lParam);
 
      gui_y = GET_Y_LPARAM (lParam);
 
 
 
      // is the left arrow displayed AND is the mouse hovering it ?
 
      if ((the_scene.gui.larrow.state != 0) && Button_IsHovered (&the_scene.gui.larrow, gui_x, gui_y))
 
         SendMessage (hWnd, WM_COMMAND, MENUID_CHESSBOARD_PREVIOUSMOVE, NULL); // send a "previous move" event
 
 
 
      // is the right arrow displayed AND is the mouse hovering it ?
 
      if ((the_scene.gui.rarrow.state != 0) && Button_IsHovered (&the_scene.gui.rarrow, gui_x, gui_y))
 
         SendMessage (hWnd, WM_COMMAND, MENUID_CHESSBOARD_NEXTMOVE, NULL); // send a "next move" event
 
 
 
      // is the chat button displayed AND is the mouse hovering it ?
 
      if ((the_scene.gui.chatbutton.state != 0) && Button_IsHovered (&the_scene.gui.chatbutton, gui_x, gui_y))
 
      {
 
         opposite_player = Player_FindByType (PLAYER_INTERNET); // get a hand on the remote player
 
 
 
         // find or create the corresponding interlocutor structure and fire it up
 
         if (opposite_player != NULL)
 
            Interlocutor_FindOrCreate (opposite_player->name);
 
      }
 
 
 
      // is the games button displayed AND is the mouse hovering it ?
 
      if ((the_scene.gui.gamesbutton.state != 0) && Button_IsHovered (&the_scene.gui.gamesbutton, gui_x, gui_y))
 
         Window_Sought (); // if so, display the sought games window
 
 
 
      // is the people button displayed AND is the mouse hovering it ?
 
      if ((the_scene.gui.peoplebutton.state != 0) && Button_IsHovered (&the_scene.gui.peoplebutton, gui_x, gui_y))
 
         Window_Opponents (); // if so, display the opponents window
 
 
 
      // is the parts selection line displayed AND is the mouse anywhere near it ?
 
      if (the_scene.gui.is_partspick_displayed && Render_IsMouseInBox (gui_x, gui_y, 0.0f, 0.0f, 100.0f, 11.0f))
 
      {
 
         // for each selectable part, if the mouse is on it, mark it as selected
 
         for (part_index = 0; part_index < 13; part_index++)
 
            if (Render_IsMouseInBox (gui_x, gui_y, part_index * (100.0f / 13.0f), 0, 100.0f / 13.0f, 11.0f))
 
            {
 
               the_scene.gui.partspick_selectedpart = selectable_parts[part_index]; // mark it as selected
 
               Audio_PlaySound (SOUNDTYPE_CLICK, 0.0f, 0.0f, 0.04f); // make a click sound at the center of the board
 
               break; // no need to search further if one selection was found
 
            }
 
      }
 
 
 
      // get current player and see if we're online
 
      current_player = Player_GetCurrent ();
 
      remote_player = Player_FindByType (PLAYER_INTERNET);
 
 
 
      // if we are not allowed to select anything, don't even try
 
      if (is_paused || (current_player->type != PLAYER_HUMAN) || (the_board.viewed_move != the_board.move_count - 1)
 
          || ((remote_player != NULL) && !remote_player->is_in_game))
 
      {
 
         the_scene.update = true; // update the 3D scene
 
 
 
         // call the default window message processing function to keep things going
 
         return (DefWindowProc (hWnd, message, wParam, lParam));
 
      }
 
 
 
      // it's a single click. The user clicked on a square.
 
 
 
      // figure out the coordinates on table
 
      Render_MouseToFloor (gui_x, gui_y, &board_x, &board_y);
 
 
 
      // translate them to board coordinates
 
      the_board.hovered_position[0] = (int) floor ((20.0f - board_y) / 5.0f);
 
      the_board.hovered_position[1] = 7 - (int) floor ((20.0f - board_x) / 5.0f);
 
      highlight_endtime = 0; // stop highlighting anything
 
 
 
      // if it's outside the table, end the job
 
      if (!IS_VALID_POSITION (the_board.hovered_position))
 
      {
 
         the_scene.update = true; // update the 3D scene
 
 
 
         // call the default window message processing function to keep things going
 
         return (DefWindowProc (hWnd, message, wParam, lParam));
 
      }
 
 
 
      // we clicked a valid slot on the table
 
 
 
      current_move = &the_board.moves[the_board.viewed_move]; // quick access to current move
 
 
 
      ///////////////////////////////////
 
      // are we in parts placement mode ?
 
      if (the_board.game_state == STATE_SETUPPOSITION)
 
      {
 
         // quick access to line and column
 
         line = the_board.hovered_position[0];
 
         column = the_board.hovered_position[1];
 
 
 
         // figure out the color of the part we are placing
 
         if (the_scene.gui.partspick_selectedpart == tolower (the_scene.gui.partspick_selectedpart))
 
            part_color = COLOR_BLACK; // black color
 
         else
 
            part_color = COLOR_WHITE; // white color
 
 
 
         // are we erasing a king ? if so, replace it at its default location
 
         if ((current_move->slots[line][column].part == PART_KING) && (current_move->slots[line][column].color == COLOR_BLACK)
 
             && (the_scene.gui.partspick_selectedpart != 'k'))
 
         {
 
            // is it ALREADY the king's default location ? if so, give up
 
            if ((line == 7) && (column == 4))
 
            {
 
               Audio_PlaySound (SOUNDTYPE_ILLEGALMOVE, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.04f); // play the "illegal move" sound
 
               return (DefWindowProc (hWnd, message, wParam, lParam)); // call the default window message proc
 
            }
 
 
 
            Move_SetSlot (current_move, 7, 4, COLOR_BLACK, PART_KING); // replace black king
 
         }
 
         else if ((current_move->slots[line][column].part == PART_KING) && (current_move->slots[line][column].color == COLOR_WHITE)
 
                  && (the_scene.gui.partspick_selectedpart != 'K'))
 
         {
 
            // is it ALREADY the king's default location ? if so, give up
 
            if ((line == 0) && (column == 4))
 
            {
 
               Audio_PlaySound (SOUNDTYPE_ILLEGALMOVE, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.04f); // play the "illegal move" sound
 
               return (DefWindowProc (hWnd, message, wParam, lParam)); // call the default window message proc
 
            }
 
 
 
            Move_SetSlot (current_move, 0, 4, COLOR_WHITE, PART_KING); // replace white king
 
         }
 
 
 
         // place the selected part on the board
 
         if (tolower (the_scene.gui.partspick_selectedpart) == 'p')
 
            Move_SetSlot (current_move, line, column, part_color, PART_PAWN); // pawn
 
         else if (tolower (the_scene.gui.partspick_selectedpart) == 'r')
 
            Move_SetSlot (current_move, line, column, part_color, PART_ROOK); // rook
 
         else if (tolower (the_scene.gui.partspick_selectedpart) == 'n')
 
            Move_SetSlot (current_move, line, column, part_color, PART_KNIGHT); // knight
 
         else if (tolower (the_scene.gui.partspick_selectedpart) == 'b')
 
            Move_SetSlot (current_move, line, column, part_color, PART_BISHOP); // bishop
 
         else if (tolower (the_scene.gui.partspick_selectedpart) == 'q')
 
            Move_SetSlot (current_move, line, column, part_color, PART_QUEEN); // queen
 
         else if (tolower (the_scene.gui.partspick_selectedpart) == 'k')
 
         {
 
            // parse the board for other kings of the same color and erase them
 
            for (index_line = 0; index_line < 8; index_line++)
 
               for (index_column = 0; index_column < 8; index_column++)
 
                  if ((current_move->slots[index_line][index_column].color == part_color)
 
                      && (current_move->slots[index_line][index_column].part == PART_KING))
 
                     memset (¤t_move->slots[index_line][index_column], 0, sizeof (boardslot_t)); // erase this slot
 
 
 
            Move_SetSlot (current_move, line, column, part_color, PART_KING); // king
 
         }
 
         else
 
            Move_SetSlot (current_move, line, column, 0, PART_NONE); // no part
 
 
 
         // figure out which sound to play
 
         if (the_scene.gui.partspick_selectedpart != ' ')
 
            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
 
 
 
         the_scene.update = true; // update the 3D scene
 
 
 
         // call the default window message processing function to keep things going
 
         return (DefWindowProc (hWnd, message, wParam, lParam));
 
      }
 
      // end of parts placement mode
 
      //////////////////////////////
 
 
 
      // does a selection NOT exist yet ?
 
      if (!IS_VALID_POSITION (the_board.selected_position))
 
      {
 
         // is there a selectable part at this location ?
 
         if ((current_move->slots[the_board.hovered_position[0]][the_board.hovered_position[1]].part != PART_NONE)
 
            && (current_move->slots[the_board.hovered_position[0]][the_board.hovered_position[1]].color == Board_ColorToMove (&the_board)))
 
         {
 
            // mark the selected position as selected (and remember it)
 
            the_board.selected_position[0] = the_board.hovered_position[0];
 
            the_board.selected_position[1] = the_board.hovered_position[1];
 
         }
 
 
 
         the_scene.update = true; // update the 3D scene
 
 
 
         // call the default window message processing function to keep things going
 
         return (DefWindowProc (hWnd, message, wParam, lParam));
 
      }
 
 
 
      // a selection exists already
 
 
 
      // is it the slot that was previously selected ? (i.e, user wants to "unselect" it)
 
      if ((the_board.hovered_position[0] == the_board.selected_position[0]) && (the_board.hovered_position[1] == the_board.selected_position[1]))
 
      {
 
         // forget the selected position
 
         the_board.selected_position[0] = -1;
 
         the_board.selected_position[1] = -1;
 
 
 
         the_scene.update = true; // update the 3D scene
 
 
 
         // call the default window message processing function to keep things going
 
         return (DefWindowProc (hWnd, message, wParam, lParam));
 
      }
 
 
 
      // else is it another part of the same color ? (i.e, user wants to change the part he selected)
 
      else if ((current_move->slots[the_board.hovered_position[0]][the_board.hovered_position[1]].part != PART_NONE)
 
               && (current_move->slots[the_board.hovered_position[0]][the_board.hovered_position[1]].color == Board_ColorToMove (&the_board)))
 
      {
 
         // mark the selected position as selected (and remember it)
 
         the_board.selected_position[0] = the_board.hovered_position[0];
 
         the_board.selected_position[1] = the_board.hovered_position[1];
 
 
 
         the_scene.update = true; // update the 3D scene
 
 
 
         // call the default window message processing function to keep things going
 
         return (DefWindowProc (hWnd, message, wParam, lParam));
 
      }
 
 
 
      // else is it a possible move ?
 
      else if ((current_move->slots[the_board.hovered_position[0]][the_board.hovered_position[1]].flags & FLAG_POSSIBLEMOVE)
 
               || (current_move->slots[the_board.hovered_position[0]][the_board.hovered_position[1]].flags & FLAG_TAKEABLE))
 
      {
 
         // are we in check after the move ? (FIXME: call EP version of this func for en passant moves)
 
         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)))
 
         {
 
            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
 
 
 
            the_scene.update = true; // update the 3D scene
 
 
 
            // call the default window message processing function to keep things going
 
            return (DefWindowProc (hWnd, message, wParam, lParam));
 
         }
 
 
 
         ////////////////////
 
         // movement is valid
 
 
 
         // do it
 
         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);
 
         current_move = &the_board.moves[the_board.move_count - 1]; // update current move pointer
 
 
 
         // are we in internet mode ?
 
         if (remote_player != NULL)
 
            the_board.reevaluate = false; // if so, don't reevaluate the board yet, let the server do it
 
 
 
         // was it a pawn being promoted ? if so, display the dialog box and wait for the reply
 
         if ((current_move->slots[the_board.hovered_position[0]][the_board.hovered_position[1]].part == PART_PAWN)
 
             && ((the_board.hovered_position[0] == 0) || (the_board.hovered_position[0] == 7)))
 
            DialogBox_PawnPromotion (); // display the pawn promotion dialog box
 
 
 
         // else it was a normal move
 
         else
 
         {
 
            Board_SetSelectedAndHovered (&the_board, -1, -1, -1, -1); // forget the hovered and selected positions
 
            the_board.has_playerchanged = true; // and switch players
 
         }
 
 
 
         the_scene.update = true; // update the 3D scene
 
         animation_endtime = current_time + ANIMATION_DURATION; // play animation now
 
         sound_playtime = current_time + ANIMATION_DURATION - 0.1f; // play sound near the end of animation
 
 
 
         // call the default window message processing function to keep things going
 
         return (DefWindowProc (hWnd, message, wParam, lParam));
 
      }
 
 
 
      // else it's another location
 
 
 
      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
 
 
 
      the_scene.update = true; // update the 3D scene
 
 
 
      // call the default window message processing function to keep things going
 
      return (DefWindowProc (hWnd, message, wParam, lParam));
 
   }
 
 
 
   ////////////////////////////////////////////////////////////////////////////////////////////////
 
   // right mouse button push
 
   else if (message == WM_RBUTTONDOWN)
 
   {
 
      // are we in animation OR are mouse commands NOT allowed ?
 
      if ((animation_endtime + 1.0f >= current_time) || (command_ignoretime >= current_time))
 
         return (DefWindowProc (hWnd, message, wParam, lParam)); // if so, call the default message proc to keep things going
 
 
 
      prevgui_x = GET_X_LPARAM (lParam); // remember mouse coordinates
 
      prevgui_y = GET_Y_LPARAM (lParam);
 
 
 
      // if we click something, stop moving the table immediately
 
 
 
      // cycle through both players and change their view angles EXCEPT the opponent if he's human
 
      for (viewer_index = 0; viewer_index < 2; viewer_index++)
 
         if ((the_board.players[viewer_index].type == PLAYER_COMPUTER)
 
               || (the_board.players[viewer_index].type == PLAYER_INTERNET)
 
               || (viewer_index == current_viewer))
 
         {
 
            the_board.players[viewer_index].view_pitch = current_pitch;
 
            the_board.players[viewer_index].view_yaw = current_yaw;
 
         }
 
 
 
      rbutton_pushed = true; // remember button is clicked
 
 
 
      // call the default window message processing function to keep things going
 
      return (DefWindowProc (hWnd, message, wParam, lParam));
 
   }
 
 
 
   ////////////////////////////////////////////////////////////////////////////////////////////////
 
   // right mouse button release
 
   else if (message == WM_RBUTTONUP)
 
   {
 
      // are we in animation OR are mouse commands NOT allowed ?
 
      if ((animation_endtime + 1.0f >= current_time) || (command_ignoretime >= current_time))
 
         return (DefWindowProc (hWnd, message, wParam, lParam)); // if so, call the default message proc to keep things going
 
 
 
      rbutton_pushed = false; // remember button is released
 
      the_scene.update = true; // update the 3D scene
 
 
 
      // call the default window message processing function to keep things going
 
      return (DefWindowProc (hWnd, message, wParam, lParam));
 
   }
 
 
 
   ////////////////////////////////////////////////////////////////////////////////////////////////
 
   // left mouse button DOUBLE-click
 
   else if (message == WM_LBUTTONDBLCLK)
 
   {
 
      // are we in animation OR are mouse commands NOT allowed ?
 
      if ((animation_endtime + 1.0f >= current_time) || (command_ignoretime >= current_time))
 
         return (DefWindowProc (hWnd, message, wParam, lParam)); // if so, call the default message proc to keep things going
 
 
 
      // get mouse coordinates
 
      gui_x = GET_X_LPARAM (lParam);
 
      gui_y = GET_Y_LPARAM (lParam);
 
 
 
      // are we in game and did we click the move comments area ?
 
      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))
 
      {
 
         DialogBox_Comment (); // fire up the comment dialog box
 
         return (DefWindowProc (hWnd, message, wParam, lParam)); // call the default window message processing function to keep things going
 
      }
 
 
 
      // call the default window message processing function to keep things going
 
      return (DefWindowProc (hWnd, message, wParam, lParam));
 
   }
 
 
 
   ////////////////////////////////////////////////////////////////////////////////////////////////
 
   // mouse move (while not in animation)
 
   else if (message == WM_MOUSEMOVE)
 
   {
 
      // are we in animation OR are mouse commands NOT allowed ?
 
      if ((animation_endtime + 1.0f >= current_time) || (command_ignoretime >= current_time))
 
         return (DefWindowProc (hWnd, message, wParam, lParam)); // if so, call the default message proc to keep things going
 
 
 
      // get mouse coordinates
 
      gui_x = GET_X_LPARAM (lParam);
 
      gui_y = GET_Y_LPARAM (lParam);
 
 
 
      // handle button update status
 
      the_scene.update |= Button_UpdateHoverState (&the_scene.gui.larrow, gui_x, gui_y);
 
      the_scene.update |= Button_UpdateHoverState (&the_scene.gui.rarrow, gui_x, gui_y);
 
      the_scene.update |= Button_UpdateHoverState (&the_scene.gui.chatbutton, gui_x, gui_y);
 
      the_scene.update |= Button_UpdateHoverState (&the_scene.gui.gamesbutton, gui_x, gui_y);
 
      the_scene.update |= Button_UpdateHoverState (&the_scene.gui.peoplebutton, gui_x, gui_y);
 
 
 
      // see if we're online
 
      remote_player = Player_FindByType (PLAYER_INTERNET);
 
 
 
      // are we online AND do we NOT have the right to select anything ?
 
      if ((remote_player != NULL) && !remote_player->is_in_game)
 
         return (DefWindowProc (hWnd, message, wParam, lParam)); // if so, call the default window message processing function to keep things going
 
 
 
      // is the parts selection line displayed AND is the mouse anywhere in it ?
 
      if (the_scene.gui.is_partspick_displayed && Render_IsMouseInBox (gui_x, gui_y, 0.0f, 0.0f, 100.0f, 11.0f))
 
      {
 
         // for each selectable part...
 
         for (part_index = 0; part_index < 13; part_index++)
 
         {
 
            // is the mouse hovering it ?
 
            if (Render_IsMouseInBox (gui_x, gui_y, part_index * (100.0f / 13.0f), 0, 100.0f / 13.0f, 11.0f))
 
            {
 
               // was it NOT hovered before ?
 
               if (the_scene.gui.partspick_hoveredpart != selectable_parts[part_index])
 
               {
 
                  the_scene.gui.partspick_hoveredpart = selectable_parts[part_index]; // mark it as hovered
 
                  the_scene.update = true; // update the scene
 
               }
 
            }
 
 
 
            // else was it hovered before ?
 
            else if (the_scene.gui.partspick_hoveredpart == selectable_parts[part_index])
 
            {
 
               the_scene.gui.partspick_hoveredpart = 0; // clear the hovered part
 
               the_scene.update = true; // update the scene
 
            }
 
         }
 
      }
 
      else
 
         the_scene.gui.partspick_hoveredpart = 0; // clear the hovered part
 
 
 
      // get current and opposite players
 
      current_player = Player_GetCurrent ();
 
      opposite_player = Player_GetOpposite ();
 
 
 
      // if right button was clicked, compute new pitch and yaw
 
      if (rbutton_pushed)
 
      {
 
         // cycle through both players and change their view angles EXCEPT the opponent if he's human
 
         for (viewer_index = 0; viewer_index < 2; viewer_index++)
 
            if ((the_board.players[viewer_index].type == PLAYER_COMPUTER)
 
                || (the_board.players[viewer_index].type == PLAYER_INTERNET)
 
                || (viewer_index == current_viewer))
 
            {
 
               the_board.players[viewer_index].view_pitch += (gui_y - prevgui_y) * 0.3f;
 
               if (the_board.players[viewer_index].view_pitch < 10.0f)
 
                  the_board.players[viewer_index].view_pitch = 10.0f; // wrap angles around so that they
 
               if (the_board.players[viewer_index].view_pitch > 89.99f)
 
                  the_board.players[viewer_index].view_pitch = 89.99f; // stay in the [10, 89.99] bounds
 
 
 
               the_board.players[viewer_index].view_yaw += (gui_x - prevgui_x) * 0.3f;
 
               the_board.players[viewer_index].view_yaw = WrapAngle (the_board.players[viewer_index].view_yaw);
 
 
 
               // save these as the new custom angles
 
               the_board.players[viewer_index].custom_pitch = the_board.players[viewer_index].view_pitch;
 
               the_board.players[viewer_index].custom_yaw = the_board.players[viewer_index].view_yaw;
 
 
 
               // when moving the table around, jump to ideal angles immediately
 
               current_pitch = the_board.players[viewer_index].view_pitch;
 
               current_yaw = the_board.players[viewer_index].view_yaw;
 
            }
 
 
 
         the_scene.update = true; // button was clicked, update the 3D scene
 
      }
 
 
 
      // else it's just the mouse wandering around ; have we the right to select something ?
 
      else if ((the_board.viewed_move == the_board.move_count - 1) && (current_player->type == PLAYER_HUMAN) && (highlight_endtime < current_time))
 
      {
 
         // save the old positions
 
         prev_hovered_position[0] = the_board.hovered_position[0];
 
         prev_hovered_position[1] = the_board.hovered_position[1];
 
 
 
         // figure out the coordinates on table
 
         Render_MouseToFloor (gui_x, gui_y, &board_x, &board_y);
 
 
 
         // translate them to board coordinates
 
         the_board.hovered_position[0] = (int) floor ((20.0f - board_y) / 5.0f);
 
         the_board.hovered_position[1] = 7 - (int) floor ((20.0f - board_x) / 5.0f);
 
 
 
         // do they differ from last time ?
 
         if ((the_board.hovered_position[0] != prev_hovered_position[0]) || (the_board.hovered_position[1] != prev_hovered_position[1]))
 
            the_scene.update = true; // if so, update scene
 
      }
 
 
 
      // has the user the right to leave a command AND is there no comment yet ?
 
      if ((the_board.game_state >= STATE_PLAYING) && (the_board.viewed_move > 0)
 
          && ((the_board.moves[the_board.viewed_move].comment == NULL) || (the_board.moves[the_board.viewed_move].comment[0] == 0)))
 
      {
 
         // is the mouse above the comments zone ? if so, display a dimmed hint text
 
         if (Render_IsMouseInBox (gui_x, gui_y, 30.0f, 0.0f, 40.0f, 10.0f))
 
         {
 
            if (!the_scene.gui.comment_text.is_displayed)
 
            {
 
               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"));
 
               the_scene.update = true; // and update the scene
 
            }
 
         }
 
         else
 
         {
 
            if (the_scene.gui.comment_text.is_displayed)
 
            {
 
               the_scene.gui.comment_text.is_displayed = false; // if not, erase the hint text
 
               the_scene.update = true; // and update the scene
 
            }
 
         }
 
      }
 
 
 
      // remember these coordinates for next time
 
      prevgui_x = gui_x;
 
      prevgui_y = gui_y;
 
 
 
      // call the default window message processing function to keep things going
 
      return (DefWindowProc (hWnd, message, wParam, lParam));
 
   }
 
 
 
   ////////////////////////////////////////////////////////////////////////////////////////////////
 
   // mouse scroll
 
   else if (message == WM_MOUSEWHEEL)
 
   {
 
      // are we in animation OR are mouse commands NOT allowed ?
 
      if ((animation_endtime + 1.0f >= current_time) || (command_ignoretime >= current_time))
 
         return (DefWindowProc (hWnd, message, wParam, lParam)); // if so, call the default message proc to keep things going
 
 
 
      // see if we're online
 
      remote_player = Player_FindByType (PLAYER_INTERNET);
 
 
 
      // are we online AND do we NOT have the right to select anything ?
 
      if ((remote_player != NULL) && !remote_player->is_in_game)
 
         return (DefWindowProc (hWnd, message, wParam, lParam)); // if so, call the default window message processing function to keep things going
 
 
 
      // scroll up / scroll down ?
 
      if (GET_WHEEL_DELTA_WPARAM (wParam) > 0)
 
         the_board.players[current_viewer].view_distance = max (48.0f, the_board.players[current_viewer].view_distance - 2.0f);
 
      else if (GET_WHEEL_DELTA_WPARAM (wParam) < 0)
 
         the_board.players[current_viewer].view_distance = min (100.0f, the_board.players[current_viewer].view_distance + 2.0f);
 
 
 
      // save this as the new custom distance
 
      the_board.players[current_viewer].custom_distance = the_board.players[current_viewer].view_distance;
 
 
 
      // when moving the table around, jump to ideal angles immediately
 
      current_distance = the_board.players[current_viewer].view_distance;
 
 
 
      the_scene.update = true; // update the 3D scene
 
 
 
      // call the default window message processing function to keep things going
 
      return (DefWindowProc (hWnd, message, wParam, lParam));
 
   }
 
 
 
   ////////////////////////////////////////////////////////////////////////////////////////////////
 
   // keyboard release
 
   else if (message == WM_CHAR)
 
   {
 
      // are we in position setup mode ?
 
      if (the_board.game_state == STATE_SETUPPOSITION)
 
      {
 
         // is it the enter key ? if so, exit setup mode and start playing
 
         if (wParam == L'\r')
 
         {
 
            current_move = &the_board.moves[the_board.viewed_move]; // quick access to current move
 
 
 
            // (in)validate the castling positions
 
            if ((current_move->slots[0][0].color == COLOR_WHITE) && (current_move->slots[0][0].part == PART_ROOK)
 
                && (current_move->slots[0][4].color == COLOR_WHITE) && (current_move->slots[0][4].part == PART_KING))
 
               current_move->sides[COLOR_WHITE].longcastle_allowed = true; // white castling queenside allowed
 
            else
 
               current_move->sides[COLOR_WHITE].longcastle_allowed = false; // white castling queenside no longer possible
 
            if ((current_move->slots[0][7].color == COLOR_WHITE) && (current_move->slots[0][7].part == PART_ROOK)
 
                && (current_move->slots[0][4].color == COLOR_WHITE) && (current_move->slots[0][4].part == PART_KING))
 
               current_move->sides[COLOR_WHITE].shortcastle_allowed = true; // white castling kingside allowed
 
            else
 
               current_move->sides[COLOR_WHITE].shortcastle_allowed = false; // white castling kingside no longer possible
 
            if ((current_move->slots[7][0].color == COLOR_BLACK) && (current_move->slots[7][0].part == PART_ROOK)
 
                && (current_move->slots[7][4].color == COLOR_BLACK) && (current_move->slots[7][4].part == PART_KING))
 
               current_move->sides[COLOR_BLACK].longcastle_allowed = true; // white castling queenside allowed
 
            else
 
               current_move->sides[COLOR_BLACK].longcastle_allowed = false; // white castling queenside no longer possible
 
            if ((current_move->slots[7][7].color == COLOR_BLACK) && (current_move->slots[7][7].part == PART_ROOK)
 
                && (current_move->slots[7][4].color == COLOR_BLACK) && (current_move->slots[7][4].part == PART_KING))
 
               current_move->sides[COLOR_BLACK].shortcastle_allowed = true; // white castling kingside allowed
 
            else
 
               current_move->sides[COLOR_BLACK].shortcastle_allowed = false; // white castling kingside no longer possible
 
 
 
            current_move->color = COLOR_BLACK; // so that game starts with white
 
 
 
            // validate this board in Forsyth-Edwards Notation and reset it
 
            Move_DescribeInFEN (current_move);
 
            wcscpy_s (fen_string, WCHAR_SIZEOF (fen_string), current_move->fen_string); // have a copy of fen string
 
            Board_Reset (&the_board, fen_string);
 
 
 
            the_board.game_state = STATE_PLAYING; // start the game now
 
            the_board.reevaluate = true; // evaluate board again
 
 
 
            the_scene.gui.central_text.disappear_time = current_time + 1.0f; // fade out help text now (FIXME: ugly)
 
            the_scene.update = true; // update the 3D scene
 
 
 
            // call the default window message processing function to keep things going
 
            return (DefWindowProc (hWnd, message, wParam, lParam));
 
         }
 
      }
 
 
 
      // else are we in internet mode ?
 
      else if (Player_FindByType (PLAYER_INTERNET) != NULL)
 
      {
 
         entered_ccreply = &the_scene.gui.entered_ccreply; // quick access to entered ccreply
 
         local_player = Player_FindByType (PLAYER_HUMAN); // quick access to local player
 
         if (local_player == NULL)
 
            return (DefWindowProc (hWnd, message, wParam, lParam)); // theoretically impossible condition, but better be sure
 
         if (selected_chatterchannel == NULL)
 
            return (DefWindowProc (hWnd, message, wParam, lParam)); // theoretically impossible condition, but better be sure
 
 
 
         // are we NOT entering text yet ?
 
         if (!the_scene.gui.is_entering_text)
 
         {
 
            // is it the space bar ?
 
            if (wParam == L' ')
 
            {
 
               the_scene.gui.is_entering_text = true; // remember we are entering text
 
 
 
               // reset the entered text buffer
 
               entered_ccreply->text = (wchar_t *) SAFE_malloc (1, sizeof (wchar_t), false);
 
               entered_ccreply->text[0] = 0; // only the null terminator will fit
 
               entered_ccreply->text_length = 0; // and set its length to zero
 
            }
 
         }
 
 
 
         // else we are currently in text entering mode
 
         else
 
         {
 
            // is the typed character a printable character ?
 
            if (iswprint (wParam))
 
            {
 
               // if so, reallocate space in the typed buffer (include null terminator)
 
               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);
 
               swprintf_s (&entered_ccreply->text[entered_ccreply->text_length], 1 + 1, L"%c", wParam); // append character
 
               entered_ccreply->text_length++; // buffer holds now one character more
 
            }
 
 
 
            // else is the typed character a backspace AND is there text to erase ?
 
            else if ((wParam == 0x08) && (entered_ccreply->text_length > 0))
 
            {
 
               // if so, reallocate space in the typed buffer (include null terminator)
 
               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);
 
               entered_ccreply->text[entered_ccreply->text_length - 1] = 0; // backspace, delete one character
 
               entered_ccreply->text_length--; // buffer holds now one character less
 
            }
 
 
 
            // else is the typed character the escape key ?
 
            else if (wParam == 0x1b)
 
            {
 
               SAFE_free ((void **) &entered_ccreply->text); // reset the entered text buffer
 
               entered_ccreply->text_length = 0; // and set its length to zero
 
               the_scene.gui.is_entering_text = false; // and exit from the text entering mode
 
            }
 
 
 
            // else is the typed character the enter key ?
 
            else if (wParam == 0x0d)
 
               the_scene.gui.is_entering_text = false; // enter, exit from the text entering mode (this will validate our reply)
 
 
 
            // else it's an illegal character
 
            else
 
               Audio_PlaySound (SOUNDTYPE_ILLEGALMOVE, 0.0f, 0.0f, 0.04f); // illegal character, beep the illegal move sound at the center of the board
 
         }
 
 
 
         the_scene.update = true; // update the 3D scene
 
 
 
         // call the default window message processing function to keep things going
 
         return (DefWindowProc (hWnd, message, wParam, lParam));
 
      }
 
   }
 
 
 
   ////////////////////////////////////////////////////////////////////////////////////////////////
 
   // mouse move outside the window
 
   else if (message == WM_NCMOUSEMOVE)
 
   {
 
      rbutton_pushed = false; // remember right button is released
 
 
 
      // call the default window message processing function to keep things going
 
      return (DefWindowProc (hWnd, message, wParam, lParam));
 
   }
 
 
 
   ////////////////////////////////////////////////////////////////////////////////////////////////
 
   // window repaint
 
   else if (message == WM_PAINT)
 
   {
 
      the_scene.update = true; // update the 3D scene
 
 
 
      // call the default window message processing function to keep things going
 
      return (DefWindowProc (hWnd, message, wParam, lParam));
 
   }
 
 
 
   // call the default window message processing function to keep things going
 
   return (DefWindowProc (hWnd, message, wParam, lParam));
 
}
 
 
 
 
 
static bool Button_IsHovered (guibutton_t *button, int gui_x, int gui_y)
 
{
 
   // handy wrapper that returns whether a particular GUI button is hovered when the mouse is at the given coordinates
 
 
 
   return (Render_IsMouseInBox (gui_x, gui_y, button->left, button->top, button->width, button->height));
 
}
 
 
 
 
 
static bool Button_UpdateHoverState (guibutton_t *button, int gui_x, int gui_y)
 
{
 
   // this function updates the hover state of a GUI button, and returns TRUE if the
 
   // scene needs to be updated, FALSE otherwise.
 
 
 
   // is the button displayed ?
 
   if (button->state != 0)
 
   {
 
      // is the mouse hovering it ?
 
      if (Render_IsMouseInBox (gui_x, gui_y, button->left, button->top, button->width, button->height))
 
      {
 
         // was it NOT hovered before ?
 
         if (button->state != 2)
 
         {
 
            button->state = 2; // mark it as hovered
 
            return (true); // return TRUE so as to update the scene
 
         }
 
      }
 
 
 
      // else was it hovered before ?
 
      else if (button->state == 2)
 
      {
 
         button->state = 1; // else mark it as not hovered
 
         return (true); // return TRUE so as to update the scene
 
      }
 
   }
 
 
 
   return (false); // no need to update the scene
 
}