Subversion Repositories Games.Chess Giants

Rev

Rev 3 | Go to most recent revision | Details | Last modification | View Log | RSS feed

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