Subversion Repositories Games.Chess Giants

Rev

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