// 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;
RECT rect;
POINT point;
// filter out the commonly used message values
wParam_hiword = HIWORD (wParam);
wParam_loword = LOWORD (wParam);
// get current and opposite players and see if we're online
current_player = Player_GetCurrent ();
opposite_player = Player_GetOpposite ();
remote_player = Player_FindByType (PLAYER_INTERNET);
////////////////////////////////////////////////////////////////////////////////////////////////
// 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 is the window being resized ?
else if ((message == WM_SIZE) || (message == WM_WINDOWPOSCHANGED))
{
GetWindowRect (hMainWnd, &rect); // grab the current window size and update the options accordingly
options.want_maximized = (rect.right - rect.left > GetSystemMetrics (SM_CXMAXIMIZED) * 90 / 100) && (rect.bottom - rect.top > GetSystemMetrics (SM_CYMAXIMIZED) * 80 / 100);
if (!options.want_maximized)
{
options.window_width = max (rect.right - rect.left, 640); // min window width
options.window_height = max (rect.bottom - rect.top, 480); // min window height (only save them when the window is not maximized)
}
return (0); // don't let Windows do the default processing on this message
}
////////////////////////////////////////////////////////////////////////////////////////////////
// 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) || (remote_player != 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)
{
// ANY menu choice makes the "new game" / "open game" front GUI button disappear
GUIBUTTON_DISABLE (the_scene.gui.newgamebutton);
GUIBUTTON_DISABLE (the_scene.gui.opengamebutton);
// game menu, new game
if (wParam_loword == MENUID_GAME_NEWGAME)
{
if ((the_board.game_state == STATE_PLAYING) && (the_board.move_count > 1))
DialogBox_Resign (RESIGNTYPE_NEWGAME); // 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)
Board_EnterSetupPosition (&the_board); // enter setup position mode
// game menu, load game
else if (wParam_loword == MENUID_GAME_LOAD)
{
if ((the_board.game_state == STATE_PLAYING) && (the_board.move_count > 1))
DialogBox_Resign (RESIGNTYPE_LOADGAME); // if a game is playing, ask to resign first
else
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 (RESIGNTYPE_UNDEFINED); // 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) || (remote_player != 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
}
// move menu, cancel last move
else if (wParam_loword == MENUID_MOVE_CANCELLASTMOVE)
the_board.players[current_viewer].wants_cancel = true; // remember this player wants to cancel his last move
// move menu, suggest move
else if (wParam_loword == MENUID_MOVE_SUGGESTMOVE)
the_board.players[current_viewer].wants_hint = true; // remember this player wants a hint
// move menu, comment move
else if (wParam_loword == MENUID_MOVE_COMMENTMOVE)
DialogBox_Comment (); // fire up the comment dialog box
// move menu, go to move
else if (wParam_loword == MENUID_MOVE_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, report a problem to the author
else if (wParam_loword == MENUID_HELP_REPORTAPROBLEM)
{
// send the game engine history to the author and display an error message if failed
if (MessageBox (hMainWnd, LOCALIZE (L"ReportAProblem"), LOCALIZE (L"ImportantMessage"), MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON3) == IDYES)
if (Debug_SendLogToAuthor ("user request", true)) // with user description
MessageBox (hMainWnd, LOCALIZE (L"MessageSent"), PROGRAM_NAME, MB_ICONINFORMATION | MB_OK);
else
MessageBox (hMainWnd, LOCALIZE (L"NoInternetConnection"), LOCALIZE (L"FatalError"), MB_ICONWARNING | MB_OK);
}
// 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
// are we in board closeup mode OR are we online AND do we NOT have the right to select anything ?
if (((current_distance == CLOSEUP_VIEW_DISTANCE) && (current_pitch == CLOSEUP_VIEW_PITCH))
|| ((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 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 "new game" button displayed AND is the mouse hovering it ?
if ((the_scene.gui.newgamebutton.state != 0) && Button_IsHovered (&the_scene.gui.newgamebutton, gui_x, gui_y))
{
GUIBUTTON_DISABLE (the_scene.gui.newgamebutton); // hide the "new game" button
GUIBUTTON_DISABLE (the_scene.gui.opengamebutton); // hide the "open game" button
DialogBox_NewGame (); // fire up the "new game" dialog box
}
// is the "open game" button displayed AND is the mouse hovering it ?
if ((the_scene.gui.opengamebutton.state != 0) && Button_IsHovered (&the_scene.gui.opengamebutton, gui_x, gui_y))
{
GUIBUTTON_DISABLE (the_scene.gui.newgamebutton); // hide the "new game" button
GUIBUTTON_DISABLE (the_scene.gui.opengamebutton); // hide the "open game" button
DialogBox_Load (); // fire up the "open game" dialog box
}
// 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))
{
// find or create the corresponding interlocutor structure and fire it up
if (remote_player != NULL)
Interlocutor_FindOrCreate (remote_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
// are we in board closeup mode OR are we online AND do we NOT have the right to select anything ?
if (((current_distance == CLOSEUP_VIEW_DISTANCE) && (current_pitch == CLOSEUP_VIEW_PITCH))
|| ((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
// handle game history navigation
if ((the_scene.gui.llarrow.state != 0) && Button_IsHovered (&the_scene.gui.llarrow, gui_x, gui_y))
SendMessage (hWnd, WM_COMMAND, MENUID_CHESSBOARD_BEGINNINGOFGAME, NULL); // send a "jump to beginning" event
else 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
else 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
else if ((the_scene.gui.rrarrow.state != 0) && Button_IsHovered (&the_scene.gui.rrarrow, gui_x, gui_y))
SendMessage (hWnd, WM_COMMAND, MENUID_CHESSBOARD_CURRENTSTATEOFGAME, NULL); // send a "jump to end" event
// 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
}
}
// 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)
|| ((the_board.game_state != STATE_SETUPPOSITION) && (the_board.game_state != STATE_PLAYING))
|| ((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
opposite_player->should_wakeup = true; // and switch players
}
the_scene.update = true; // update the 3D scene
animation_endtime = current_time + ANIMATION_DURATION; // play animation now
// 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.llarrow, gui_x, gui_y);
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.rrarrow, gui_x, gui_y);
the_scene.update |= Button_UpdateHoverState (&the_scene.gui.newgamebutton, gui_x, gui_y);
the_scene.update |= Button_UpdateHoverState (&the_scene.gui.opengamebutton, 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);
// are we in board closeup mode OR are we online AND do we NOT have the right to select anything ?
if (((current_distance == CLOSEUP_VIEW_DISTANCE) && (current_pitch == CLOSEUP_VIEW_PITCH))
|| ((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
// 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 < MIN_VIEW_PITCH)
the_board.players[viewer_index].view_pitch = MIN_VIEW_PITCH; // wrap angles around so that they
if (the_board.players[viewer_index].view_pitch > MAX_VIEW_PITCH)
the_board.players[viewer_index].view_pitch = MAX_VIEW_PITCH; // stay in the min-max pitch 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 comment 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_UpdateText (&the_scene.gui.comment_text, RGBA_TO_RGBACOLOR (255, 255, 255, 127), DURATION_INFINITE, false, L"\n\n%s", 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
// are we in board closeup mode OR are we online AND do we NOT have the right to select anything ?
if (((current_distance == CLOSEUP_VIEW_DISTANCE) && (current_pitch == CLOSEUP_VIEW_PITCH))
|| ((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
// get mouse coordinates (strangely enough, the WM_MOUSEWHEEL message outputs ABSOLUTE coordinates, and we need to convert them...)
point.x = GET_X_LPARAM (lParam);
point.y = GET_Y_LPARAM (lParam);
ScreenToClient (hWnd, &point);
gui_x = point.x;
gui_y = point.y;
// is a game history displayed and are we (roughly) hovering this area ?
if (the_scene.gui.history_text.is_displayed && Render_IsMouseInBox (gui_x, gui_y, 90.0f, 10.0f, 10.0f, 40.0f))
{
// we want to scroll through the game history rather than zooming in/out. Scroll up or scroll down ?
if ((GET_WHEEL_DELTA_WPARAM (wParam) > 0) && (the_scene.gui.larrow.state != 0))
SendMessage (hWnd, WM_COMMAND, MENUID_CHESSBOARD_PREVIOUSMOVE, NULL); // send a "previous move" event
else if ((GET_WHEEL_DELTA_WPARAM (wParam) < 0) && (the_scene.gui.rarrow.state != 0))
SendMessage (hWnd, WM_COMMAND, MENUID_CHESSBOARD_NEXTMOVE, NULL); // send a "next move" event
return (DefWindowProc (hWnd, message, wParam, lParam)); // and call the default window message processing function to keep things going
}
// we want to zoom in/out of the scene. Scroll up or scroll down ?
if (GET_WHEEL_DELTA_WPARAM (wParam) > 0)
the_board.players[current_viewer].view_distance = max (MIN_VIEW_DISTANCE, 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 (MAX_VIEW_DISTANCE, 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
// make sure there is at least one king of each color (i.e. two kings)
part_index = 0;
for (line = 0; line < 8; line++)
for (column = 0; column < 8; column++)
if (current_move->slots[line][column].part == PART_KING)
part_index++; // there's one king more on this board
if (part_index != 2)
return (DefWindowProc (hWnd, message, wParam, lParam)); // when at least one king is missing, we just can't leave edition mode
// (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 (remote_player != 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
}