Subversion Repositories Games.Chess Giants

Rev

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

Rev Author Line No. Line
1 pmbaty 1
// scene.cpp
2
 
3
#include "common.h"
4
 
5
 
6
// prototypes of local functions
7
static void Scene_AddPart (scene_t *scene, int part_type, int part_color, float pos_x, float pos_y, float pos_z, float turn_yaw, float pitch);
8
static void Scene_AddTile (scene_t *scene, int tile_type, float scale, float pos_x, float pos_y, float pos_z, float turn_yaw);
9
 
10
 
11
// global variables used in this module only
12
static wchar_t connected_comment[4096];
13
static wchar_t filename[MAX_PATH];
14
static float simpleshadow_sizes[] =
15
{
16
   0, // #define PART_NONE 0
17
   2.5f, // #define PART_KING 1
18
   2.45f, // #define PART_QUEEN 2
19
   2.35f, // #define PART_BISHOP 3
20
   2.25f, // #define PART_KNIGHT 4
21
   2.15f, // #define PART_ROOK 5
22
   2.0f, // #define PART_PAWN 6
23
};
24
 
25
 
26
void Scene_Init (scene_t *scene, board_t *board)
27
{
28
   // this function initializes the scene objects array and inserts the chess table in it
29
 
30
   wchar_t format_string[4096];
31
 
32
   // allocate array for the board (3 objects) and zero it out
33
   scene->objects = (sceneobject_t *) SAFE_malloc (3, sizeof (sceneobject_t), true);
34
 
35
   // insert the table edges, the table and the board
36
   scene->objects[0].mesh_index = theme->trim_meshindex;
37
   scene->objects[0].texture_index = theme->trim_texture;
38
   scene->objects[0].scale = 1.0f;
39
   scene->objects[1].mesh_index = theme->table_meshindex;
40
   scene->objects[1].texture_index = theme->table_texture;
41
   scene->objects[1].scale = 1.0f;
42
   scene->objects[2].mesh_index = theme->board_meshindex;
43
   scene->objects[2].texture_index = theme->board_texture;
44
   scene->objects[2].scale = 1.0f;
45
   scene->object_count = 3;
46
 
47
   // reset the camera position for a cool slide-in effect, but only if autorotate is enabled
48
   if (options.want_autorotateon1vs1)
49
   {
50
      current_pitch = 5.0f; // autorotate is enabled, prepare for slide-in effect
51
      current_yaw = (Board_ColorToMove (board) == COLOR_WHITE ? 90.0f : -90.0f);
52
      current_distance = 40.0f;
53
   }
54
   else
55
   {
56
      current_pitch = 55.0f; // no autorotate, reset to standard view position directly
57
      current_yaw = (Board_ColorToMove (board) == COLOR_BLACK ? 90.0f : -90.0f);
58
      current_distance = 70.0f;
59
   }
60
 
61
   // build the connected comment string (we use it as a global variable)
62
   wcscpy_s (format_string, WCHAR_SIZEOF (format_string), LOCALIZE (L"YouAreConnectedToX"));
63
   wcscat_s (format_string, WCHAR_SIZEOF (format_string), L"\n");
64
   wcscat_s (format_string, WCHAR_SIZEOF (format_string), LOCALIZE (L"YouMayEitherDisplayThePlayersListOrTheSoughtGamesList"));
65
   wcscat_s (format_string, WCHAR_SIZEOF (format_string), L"\n");
66
   wcscat_s (format_string, WCHAR_SIZEOF (format_string), LOCALIZE (L"PleaseReferToTheInternetMenu"));
67
   swprintf_s (connected_comment, WCHAR_SIZEOF (connected_comment), format_string, options.network.server_address);
68
 
69
   // completely reset the whole GUI structure so as to NULL out all pointers
70
   memset (&scene->gui, 0, sizeof (scene->gui));
71
 
72
   // set the buttons locations
73
   Scene_SetButton (&scene->gui.larrow, 0.3f, 0.5f, 3.0f, 4.0f, larrow_spriteindex);
74
   Scene_SetButton (&scene->gui.rarrow, 3.3f, 0.5f, 3.0f, 4.0f, rarrow_spriteindex);
75
   Scene_SetButton (&scene->gui.chatbutton, 1.0f, 10.0f, 10.0f, 13.0f, chatbutton_spriteindex);
76
   Scene_SetButton (&scene->gui.gamesbutton, 1.0f, 35.0f, 10.0f, 13.0f, gamesbutton_spriteindex);
77
   Scene_SetButton (&scene->gui.peoplebutton, 1.0f, 60.0f, 10.0f, 13.0f, peoplebutton_spriteindex);
78
 
79
   // remember to update the scene
80
   scene->update = true;
81
 
82
   return; // finished
83
}
84
 
85
 
86
void Scene_Shutdown (scene_t *scene)
87
{
88
   // this function frees the memory space allocated for the 3D scene and clears its pointers
89
 
90
   int cchistory_index;
91
 
92
   // free GUI mallocated buffers
93
   SAFE_free ((void **) &scene->gui.arrow_text.buffer);
94
   scene->gui.arrow_text.is_displayed = false;
95
   SAFE_free ((void **) &scene->gui.comment_text.buffer);
96
   scene->gui.comment_text.is_displayed = false;
97
   SAFE_free ((void **) &scene->gui.history_text.buffer);
98
   scene->gui.history_text.is_displayed = false;
99
   SAFE_free ((void **) &scene->gui.clock_text.buffer);
100
   scene->gui.clock_text.is_displayed = false;
101
   SAFE_free ((void **) &scene->gui.turn_text.buffer);
102
   scene->gui.turn_text.is_displayed = false;
103
   SAFE_free ((void **) &scene->gui.central_text.buffer);
104
   scene->gui.central_text.is_displayed = false;
105
 
106
   // for each entry in the CC history...
107
   for (cchistory_index = 0; cchistory_index < scene->gui.cchistory_count; cchistory_index++)
108
      SAFE_free ((void **) &scene->gui.cchistory[cchistory_index].text); // free its text buffer
109
   SAFE_free ((void **) &scene->gui.cchistory); // free the GUI's CC history
110
   scene->gui.cchistory_count = 0;
111
 
112
   SAFE_free ((void **) &scene->objects); // free the scene objects array
113
   scene->object_count = 0;
114
 
115
   return; // finished
116
}
117
 
118
 
119
void Scene_Update (scene_t *scene, board_t *board)
120
{
121
   // this function updates the scene objects to display with what's currently on the board
122
 
123
   static bool rooksound_played = false; // hack to have two sounds when a king castles
124
 
125
   boardslot_t *boardslot;
126
   boardmove_t *currentmove;
127
   player_t *local_player;
128
   player_t *network_player;
129
   player_t *current_player;
130
   wchar_t *history_text; // mallocated
131
   int historytext_size;
132
   unsigned char takenpart_type;
133
   int movement_direction;
134
   int line;
135
   int column;
136
   int pos_index;
137
   int part_index;
138
   int move_index;
139
   int start_index;
140
   int length;
141
   int threat_line;
142
   int threat_column;
143
   int minutes;
144
   int seconds;
145
   float yaw;
146
   float source_x;
147
   float source_y;
148
   float target_x;
149
   float target_y;
150
   float current_x;
151
   float current_y;
152
   float current_z;
153
   int movement_diffco;
154
   int movement_diffli;
155
   float movement_maxheight;
156
   float movement_ratio;
157
 
158
   // get the current player (we'll need it) and see if we're online
159
   current_player = Player_GetCurrent ();
160
   network_player = Player_FindByType (PLAYER_INTERNET);
161
 
162
   // fetch the background sprite from the theme
163
   if (want_custombackground)
164
      scene->background_spriteindex = custombg.sprite_index; // use the custom background if specified
165
   else
166
      scene->background_spriteindex = theme->bg.sprite_index; // else use the theme's supplied background
167
 
168
   // shrink the scene objects array to leave just the board (3 objects)
169
   scene->objects = (sceneobject_t *) SAFE_realloc (scene->objects, scene->object_count, 3, sizeof (sceneobject_t), false);
170
   scene->object_count = 3;
171
 
172
   // update the board theme
173
   scene->objects[0].mesh_index = theme->trim_meshindex;
174
   scene->objects[0].texture_index = theme->trim_texture;
175
   scene->objects[0].material_index = theme->trim_material;
176
   scene->objects[0].scale = 1.0f;
177
   scene->objects[1].mesh_index = theme->table_meshindex;
178
   scene->objects[1].texture_index = theme->table_texture;
179
   scene->objects[1].material_index = theme->table_material;
180
   scene->objects[1].scale = 1.0f;
181
   scene->objects[2].mesh_index = theme->board_meshindex;
182
   scene->objects[2].texture_index = theme->board_texture;
183
   scene->objects[2].material_index = theme->board_material;
184
   scene->objects[2].scale = 1.0f;
185
 
186
   // draw the grid numbers if we want them
187
   if (want_grid)
188
      Scene_AddTile (scene, theme->grid_texture, 30.0f, 0.0f, 0.0f, 0.01f, 0.0f);
189
 
190
   ////////////////////////////////////////////////////////////////////////////////////////////////
191
   // draw the 3D parts that are still in play
192
 
193
   currentmove = &board->moves[board->viewed_move]; // quick access to current move
194
 
195
   // if we want the 3D parts, cycle through all the grid and place them
196
   if (!want_flaticons || (current_player->view_pitch < MAX_PITCH_FOR_FLAT_ICONS))
197
      for (line = 0; line < 8; line++)
198
         for (column = 0; column < 8; column++)
199
         {
200
            boardslot = &currentmove->slots[line][column]; // quick access to grid slot
201
 
202
            // is there nothing on this grid slot ?
203
            if (boardslot->part == PART_NONE)
204
               continue; // then don't draw anything on it
205
 
206
            // has a movement happened yet AND does it land on this slot ?
207
            if ((board->viewed_move > 0)
208
                && (line == currentmove->target[0]) && (column == currentmove->target[1]))
209
            {
210
               // do we want animations AND is it still time to play the animation ?
211
               if (options.want_animations && (animation_endtime > current_time))
212
               {
213
                  // get the source and target X and Y positions
214
                  source_x = 17.5f - (7 - currentmove->source[1]) * 5.0f;
215
                  source_y = 17.5f - currentmove->source[0] * 5.0f;
216
                  target_x = 17.5f - (7 - currentmove->target[1]) * 5.0f;
217
                  target_y = 17.5f - currentmove->target[0] * 5.0f;
218
 
219
                  // compute the movement completion ratio between 0 and 1
220
                  movement_ratio = 1.0f - (animation_endtime - current_time) / ANIMATION_DURATION;
221
 
222
                  // compute the current X an Y based on movement timing
223
                  current_x = source_x + (target_x - source_x) * movement_ratio;
224
                  current_y = source_y + (target_y - source_y) * movement_ratio;
225
 
226
                  // height is a sine positive, max height is proportional to travelled distance
227
                  movement_diffco = abs (currentmove->target[1] - currentmove->source[1]);
228
                  movement_diffli = abs (currentmove->target[0] - currentmove->source[0]);
229
                  movement_maxheight = 0.5f + (float) movement_diffco + (float) movement_diffli;
230
                  if (currentmove->part == PART_KNIGHT)
231
                     movement_maxheight *= 2.0f; // knights jump high
232
                  else if ((currentmove->part == PART_KING) && (movement_diffco == 2))
233
                     movement_maxheight *= 5.0f; // kings jump VERY high when castling too
234
                  else if (movement_maxheight > 5.0f)
235
                     movement_maxheight = 5.0f; // all other parts just hover above the ground
236
                  current_z = 0.04f + sin (MATH_PI * movement_ratio) * movement_maxheight;
237
 
238
                  // make this part move realistically
239
                  Scene_AddPart (scene, boardslot->part, boardslot->color, current_x, current_y, current_z, 0.0f, (boardslot->color == COLOR_BLACK ? -1 : 1) * min (current_z * 3.0f, 10.0f));
240
               }
241
               else
242
                  Scene_AddPart (scene, boardslot->part, boardslot->color, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.04f, 0.0f, 0.0f);
243
 
244
               // is it time to play a move sound ?
245
               if ((sound_playtime != 0) && (sound_playtime < current_time))
246
               {
247
                  // is the current player in check ? (to play the right sound)
248
                  // read as: was the last move an opponent's move AND did it put us to check ?
249
                  if (currentmove->is_check)
250
                  {
251
                     // is it a checkmate or a normal check ? (checkmate == check + stalemate)
252
                     if (currentmove->is_stalemate)
253
                        Audio_PlaySound (board->players[currentmove->color].type == PLAYER_HUMAN ? SOUNDTYPE_VICTORY : SOUNDTYPE_DEFEAT); // if so, play endgame sound
254
                     else
255
                        Audio_PlaySound (SOUNDTYPE_CHECK); // else play the check sound
256
                  }
257
                  else
258
                  {
259
                     // is it a stalemate, a capture or a normal move ?
260
                     if (currentmove->is_stalemate)
261
                        Audio_PlaySound (SOUNDTYPE_DEFEAT); // on stalemate, play defeat sound
262
                     else if (currentmove->has_captured)
263
                        Audio_PlaySound (SOUNDTYPE_PIECETAKEN); // on capture, play the capture sound
264
                     else
265
                        Audio_PlaySound (SOUNDTYPE_MOVE); // else play the normal move sound
266
                  }
267
 
268
                  sound_playtime = 0; // mark this animation as completed and sound played
269
               }
270
            }
271
 
272
            // else has a movement happened yet AND is this movement a castle AND is this the concerned tower ?
273
            else if ((board->viewed_move > 0)
274
                     && (towupper (currentmove->pgntext[0]) == L'O') // either O-O-O or O-O
275
                     && (line == (currentmove->color == COLOR_WHITE ? 0 : 7)) && (column == (currentmove->target[1] == 2 ? 3 : 5)))
276
            {
277
               // do we want animations AND is it still time to play the animation ? (castling rooks move faster)
278
               if (options.want_animations && (animation_endtime - (0.5f * ANIMATION_DURATION) > current_time))
279
               {
280
                  // get the source and target X and Y positions
281
                  source_x = 17.5f - (7 - (currentmove->target[1] == 2 ? 0 : 7)) * 5.0f; // if king moves left, then rook starts on column a, else it starts on column h
282
                  source_y = 17.5f - line * 5.0f;
283
                  target_x = 17.5f - (7 - column) * 5.0f;
284
                  target_y = 17.5f - line * 5.0f;
285
 
286
                  // compute the movement completion ratio between 0 and 1 (castling rooks move faster)
287
                  movement_ratio = min (1.0f, 1.0f - (animation_endtime - (0.5f * ANIMATION_DURATION) - current_time) / (0.5f * ANIMATION_DURATION));
288
 
289
                  // compute the current X an Y based on movement timing
290
                  current_x = source_x + (target_x - source_x) * movement_ratio;
291
                  current_y = source_y + (target_y - source_y) * movement_ratio;
292
 
293
                  // height is a sine positive, max height is proportional to travelled distance
294
                  movement_maxheight = 1.0f; // castling rook will barely hover above the ground
295
                  current_z = 0.04f + sin (MATH_PI * movement_ratio) * movement_maxheight;
296
 
297
                  // make this part move realistically
298
                  Scene_AddPart (scene, boardslot->part, boardslot->color, current_x, current_y, current_z, 0.0f, (boardslot->color == COLOR_BLACK ? -1 : 1) * min (current_z * 3.0f, 10.0f));
299
 
300
                  if (movement_ratio < 0.9f)
301
                     rooksound_played = false; // if the rook is still moving, reset the "sound played" flag
302
                  else if (!rooksound_played)
303
                  {
304
                     Audio_PlaySound (SOUNDTYPE_MOVE); // when the rook has landed, play a move sound
305
                     rooksound_played = true; // remember this is no longer to be done
306
                  }
307
               }
308
               else
309
                  Scene_AddPart (scene, boardslot->part, boardslot->color, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.04f, 0.0f, 0.0f);
310
            }
311
 
312
            // else this part is static, so draw a static object
313
            else
314
               Scene_AddPart (scene, boardslot->part, boardslot->color, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.04f, 0.0f, 0.0f);
315
         }
316
 
317
   ////////////////////////////////////////////////////////////////////////////////////////////////
318
   // now recompute the slot textures (only in play mode when we render the real board)
319
 
320
   // erase all the slot flags
321
   for (line = 0; line < 8; line++)
322
      for (column = 0; column < 8; column++)
323
         currentmove->slots[line][column].flags = FLAG_NONE; // because we will recompute them
324
 
325
   // cycle through all the grid again and see if either king is in check
326
   for (line = 0; line < 8; line++)
327
      for (column = 0; column < 8; column++)
328
      {
329
         boardslot = &currentmove->slots[line][column]; // quick access to grid slot
330
 
331
         if (boardslot->part != PART_KING)
332
            continue; // if this slot is not a king, skip it
333
 
334
         // is this king currently threatened ?
335
         if (Move_IsKingThreatenedAtLocation (currentmove, boardslot->color, line, column, &threat_line, &threat_column))
336
         {
337
            currentmove->slots[line][column].flags |= FLAG_CHECK; // mark it as threatened
338
            currentmove->slots[threat_line][threat_column].flags |= FLAG_THREAT; // and who threatens it
339
         }
340
      }
341
 
342
   // are we in play mode ? i.e, are we rendering the last move ?
343
   if (board->viewed_move == board->move_count - 1)
344
   {
345
      // mark the selected position as selected
346
      if (IS_VALID_POSITION (board->selected_position))
347
         currentmove->slots[board->selected_position[0]][board->selected_position[1]].flags |= FLAG_SELECTED;
348
 
349
      // now cycle through all the grid again and see if some slots need to be coloured
350
      for (line = 0; line < 8; line++)
351
         for (column = 0; column < 8; column++)
352
         {
353
            boardslot = &currentmove->slots[line][column]; // quick access to grid slot
354
 
355
            if (!(boardslot->flags & FLAG_SELECTED))
356
               continue; // if this slot is not selected, skip it
357
 
358
            //////////////////////////////////////////////////////////////////////////////////////////
359
            ////////////////////////////////////////// PAWN //////////////////////////////////////////
360
            //////////////////////////////////////////////////////////////////////////////////////////
361
            // is it a pawn ?
362
            if (boardslot->part == PART_PAWN)
363
            {
364
               // figure out movement direction
365
               if (boardslot->color == COLOR_WHITE)
366
                  movement_direction = 1;
367
               else
368
                  movement_direction = -1;
369
 
370
               // if pawn has still room to move forward, it can
371
               if ((((line < 7) && (movement_direction == 1)) || ((line > 0) && (movement_direction == -1)))
372
                   && (currentmove->slots[line + movement_direction][column].part == PART_NONE))
373
               {
374
                  currentmove->slots[line + movement_direction][column].flags |= FLAG_POSSIBLEMOVE;
375
 
376
                  // if pawn is still in its initial slot, it can move twice forward
377
                  if ((((line == 1) && (movement_direction == 1)) || ((line == 6) && (movement_direction == -1)))
378
                      && (currentmove->slots[line + movement_direction * 2][column].part == PART_NONE))
379
                     currentmove->slots[line + movement_direction * 2][column].flags |= FLAG_POSSIBLEMOVE;
380
               }
381
 
382
               // see if pawn can take a piece on its left
383
               if ((column > 0) && (currentmove->slots[line + movement_direction][column - 1].part != PART_NONE)
384
                   && (currentmove->slots[line + movement_direction][column - 1].color != currentmove->slots[line][column].color))
385
                  currentmove->slots[line + movement_direction][column - 1].flags |= FLAG_TAKEABLE;
386
 
387
               // see if pawn can take a piece on its right
388
               if ((column < 7) && (currentmove->slots[line + movement_direction][column + 1].part != PART_NONE)
389
                   && (currentmove->slots[line + movement_direction][column + 1].color != currentmove->slots[line][column].color))
390
                  currentmove->slots[line + movement_direction][column + 1].flags |= FLAG_TAKEABLE;
391
 
392
               // if previous move was a pawn rush, see if pawn can take "en passant"
393
               if ((currentmove->part == PART_PAWN)
394
                     && (currentmove->target[1] == currentmove->source[1]) // pawn moved in column
395
                     && (abs (currentmove->target[0] - currentmove->source[0]) == 2) // pawn rushed
396
                     && (currentmove->target[0] == line) // pawn is in line with us
397
                     && (abs (currentmove->target[1] - column) == 1)) // pawn is next to us
398
                  currentmove->slots[line + movement_direction][currentmove->target[1]].flags |= FLAG_TAKEABLE;
399
            }
400
 
401
            //////////////////////////////////////////////////////////////////////////////////////////
402
            ////////////////////////////////////////// ROOK //////////////////////////////////////////
403
            //////////////////////////////////////////////////////////////////////////////////////////
404
            // else is it a rook ?
405
            else if (boardslot->part == PART_ROOK)
406
            {
407
               // see how far rook can move upwards
408
               for (pos_index = line + 1; pos_index < 8; pos_index++)
409
               {
410
                  if (currentmove->slots[pos_index][column].part != PART_NONE)
411
                  {
412
                     if (currentmove->slots[pos_index][column].color != currentmove->slots[line][column].color)
413
                        currentmove->slots[pos_index][column].flags |= FLAG_TAKEABLE;
414
                     break;
415
                  }
416
                  currentmove->slots[pos_index][column].flags |= FLAG_POSSIBLEMOVE;
417
               }
418
 
419
               // see how far rook can move downwards
420
               for (pos_index = line - 1; pos_index >= 0; pos_index--)
421
               {
422
                  if (currentmove->slots[pos_index][column].part != PART_NONE)
423
                  {
424
                     if (currentmove->slots[pos_index][column].color != currentmove->slots[line][column].color)
425
                        currentmove->slots[pos_index][column].flags |= FLAG_TAKEABLE;
426
                     break;
427
                  }
428
                  currentmove->slots[pos_index][column].flags |= FLAG_POSSIBLEMOVE;
429
               }
430
 
431
               // see how far rook can move left
432
               for (pos_index = column - 1; pos_index >= 0; pos_index--)
433
               {
434
                  if (currentmove->slots[line][pos_index].part != PART_NONE)
435
                  {
436
                     if (currentmove->slots[line][pos_index].color != currentmove->slots[line][column].color)
437
                        currentmove->slots[line][pos_index].flags |= FLAG_TAKEABLE;
438
                     break;
439
                  }
440
                  currentmove->slots[line][pos_index].flags |= FLAG_POSSIBLEMOVE;
441
               }
442
 
443
               // see how far rook can move right
444
               for (pos_index = column + 1; pos_index < 8; pos_index++)
445
               {
446
                  if (currentmove->slots[line][pos_index].part != PART_NONE)
447
                  {
448
                     if (currentmove->slots[line][pos_index].color != currentmove->slots[line][column].color)
449
                        currentmove->slots[line][pos_index].flags |= FLAG_TAKEABLE;
450
                     break;
451
                  }
452
                  currentmove->slots[line][pos_index].flags |= FLAG_POSSIBLEMOVE;
453
               }
454
            }
455
 
456
            //////////////////////////////////////////////////////////////////////////////////////////
457
            ///////////////////////////////////////// KNIGHT /////////////////////////////////////////
458
            //////////////////////////////////////////////////////////////////////////////////////////
459
            // else is it a knight ?
460
            else if (boardslot->part == PART_KNIGHT)
461
            {
462
               // peek knight's NNW move
463
               if ((column > 0) && (line < 6))
464
                  if (currentmove->slots[line + 2][column - 1].part == PART_NONE)
465
                     currentmove->slots[line + 2][column - 1].flags |= FLAG_POSSIBLEMOVE;
466
                  else if (currentmove->slots[line + 2][column - 1].color != currentmove->slots[line][column].color)
467
                     currentmove->slots[line + 2][column - 1].flags |= FLAG_TAKEABLE;
468
 
469
               // peek knight's NNE move
470
               if ((column < 7) && (line < 6))
471
                  if (currentmove->slots[line + 2][column + 1].part == PART_NONE)
472
                     currentmove->slots[line + 2][column + 1].flags |= FLAG_POSSIBLEMOVE;
473
                  else if (currentmove->slots[line + 2][column + 1].color != currentmove->slots[line][column].color)
474
                     currentmove->slots[line + 2][column + 1].flags |= FLAG_TAKEABLE;
475
 
476
               // peek knight's ENE move
477
               if ((column < 6) && (line < 7))
478
                  if (currentmove->slots[line + 1][column + 2].part == PART_NONE)
479
                     currentmove->slots[line + 1][column + 2].flags |= FLAG_POSSIBLEMOVE;
480
                  else if (currentmove->slots[line + 1][column + 2].color != currentmove->slots[line][column].color)
481
                     currentmove->slots[line + 1][column + 2].flags |= FLAG_TAKEABLE;
482
 
483
               // peek knight's ESE move
484
               if ((column < 6) && (line > 0))
485
                  if (currentmove->slots[line - 1][column + 2].part == PART_NONE)
486
                     currentmove->slots[line - 1][column + 2].flags |= FLAG_POSSIBLEMOVE;
487
                  else if (currentmove->slots[line - 1][column + 2].color != currentmove->slots[line][column].color)
488
                     currentmove->slots[line - 1][column + 2].flags |= FLAG_TAKEABLE;
489
 
490
               // peek knight's SSW move
491
               if ((column > 0) && (line > 1))
492
                  if (currentmove->slots[line - 2][column - 1].part == PART_NONE)
493
                     currentmove->slots[line - 2][column - 1].flags |= FLAG_POSSIBLEMOVE;
494
                  else if (currentmove->slots[line - 2][column - 1].color != currentmove->slots[line][column].color)
495
                     currentmove->slots[line - 2][column - 1].flags |= FLAG_TAKEABLE;
496
 
497
               // peek knight's SSE move
498
               if ((column < 7) && (line > 1))
499
                  if (currentmove->slots[line - 2][column + 1].part == PART_NONE)
500
                     currentmove->slots[line - 2][column + 1].flags |= FLAG_POSSIBLEMOVE;
501
                  else if (currentmove->slots[line - 2][column + 1].color != currentmove->slots[line][column].color)
502
                     currentmove->slots[line - 2][column + 1].flags |= FLAG_TAKEABLE;
503
 
504
               // peek knight's WNW move
505
               if ((column > 1) && (line < 7))
506
                  if (currentmove->slots[line + 1][column - 2].part == PART_NONE)
507
                     currentmove->slots[line + 1][column - 2].flags |= FLAG_POSSIBLEMOVE;
508
                  else if (currentmove->slots[line + 1][column - 2].color != currentmove->slots[line][column].color)
509
                     currentmove->slots[line + 1][column - 2].flags |= FLAG_TAKEABLE;
510
 
511
               // peek knight's WSW move
512
               if ((column > 1) && (line > 0))
513
                  if (currentmove->slots[line - 1][column - 2].part == PART_NONE)
514
                     currentmove->slots[line - 1][column - 2].flags |= FLAG_POSSIBLEMOVE;
515
                  else if (currentmove->slots[line - 1][column - 2].color != currentmove->slots[line][column].color)
516
                     currentmove->slots[line - 1][column - 2].flags |= FLAG_TAKEABLE;
517
            }
518
 
519
            //////////////////////////////////////////////////////////////////////////////////////////
520
            ///////////////////////////////////////// BISHOP /////////////////////////////////////////
521
            //////////////////////////////////////////////////////////////////////////////////////////
522
            // else is it a bishop ?
523
            else if (boardslot->part == PART_BISHOP)
524
            {
525
               // see how far bishop can move NE
526
               for (pos_index = 1; pos_index < 8; pos_index++)
527
               {
528
                  if ((line + pos_index > 7) || (column + pos_index > 7))
529
                     break;
530
                  if (currentmove->slots[line + pos_index][column + pos_index].part != PART_NONE)
531
                  {
532
                     if (currentmove->slots[line + pos_index][column + pos_index].color != currentmove->slots[line][column].color)
533
                        currentmove->slots[line + pos_index][column + pos_index].flags |= FLAG_TAKEABLE;
534
                     break;
535
                  }
536
                  currentmove->slots[line + pos_index][column + pos_index].flags |= FLAG_POSSIBLEMOVE;
537
               }
538
 
539
               // see how far bishop can move SE
540
               for (pos_index = 1; pos_index < 8; pos_index++)
541
               {
542
                  if ((line - pos_index < 0) || (column + pos_index > 7))
543
                     break;
544
                  if (currentmove->slots[line - pos_index][column + pos_index].part != PART_NONE)
545
                  {
546
                     if (currentmove->slots[line - pos_index][column + pos_index].color != currentmove->slots[line][column].color)
547
                        currentmove->slots[line - pos_index][column + pos_index].flags |= FLAG_TAKEABLE;
548
                     break;
549
                  }
550
                  currentmove->slots[line - pos_index][column + pos_index].flags |= FLAG_POSSIBLEMOVE;
551
               }
552
 
553
               // see how far bishop can move NW
554
               for (pos_index = 1; pos_index < 8; pos_index++)
555
               {
556
                  if ((line + pos_index > 7) || (column - pos_index < 0))
557
                     break;
558
                  if (currentmove->slots[line + pos_index][column - pos_index].part != PART_NONE)
559
                  {
560
                     if (currentmove->slots[line + pos_index][column - pos_index].color != currentmove->slots[line][column].color)
561
                        currentmove->slots[line + pos_index][column - pos_index].flags |= FLAG_TAKEABLE;
562
                     break;
563
                  }
564
                  currentmove->slots[line + pos_index][column - pos_index].flags |= FLAG_POSSIBLEMOVE;
565
               }
566
 
567
               // see how far bishop can move SW
568
               for (pos_index = 1; pos_index < 8; pos_index++)
569
               {
570
                  if ((line - pos_index < 0) || (column - pos_index < 0))
571
                     break;
572
                  if (currentmove->slots[line - pos_index][column - pos_index].part != PART_NONE)
573
                  {
574
                     if (currentmove->slots[line - pos_index][column - pos_index].color != currentmove->slots[line][column].color)
575
                        currentmove->slots[line - pos_index][column - pos_index].flags |= FLAG_TAKEABLE;
576
                     break;
577
                  }
578
                  currentmove->slots[line - pos_index][column - pos_index].flags |= FLAG_POSSIBLEMOVE;
579
               }
580
            }
581
 
582
            //////////////////////////////////////////////////////////////////////////////////////////
583
            ///////////////////////////////////////// QUEEN //////////////////////////////////////////
584
            //////////////////////////////////////////////////////////////////////////////////////////
585
            // else is it a queen ?
586
            else if (boardslot->part == PART_QUEEN)
587
            {
588
               // see how far queen can move NE
589
               for (pos_index = 1; pos_index < 8; pos_index++)
590
               {
591
                  if ((line + pos_index > 7) || (column + pos_index > 7))
592
                     break;
593
                  if (currentmove->slots[line + pos_index][column + pos_index].part != PART_NONE)
594
                  {
595
                     if (currentmove->slots[line + pos_index][column + pos_index].color != currentmove->slots[line][column].color)
596
                        currentmove->slots[line + pos_index][column + pos_index].flags |= FLAG_TAKEABLE;
597
                     break;
598
                  }
599
                  currentmove->slots[line + pos_index][column + pos_index].flags |= FLAG_POSSIBLEMOVE;
600
               }
601
 
602
               // see how far queen can move SE
603
               for (pos_index = 1; pos_index < 8; pos_index++)
604
               {
605
                  if ((line - pos_index < 0) || (column + pos_index > 7))
606
                     break;
607
                  if (currentmove->slots[line - pos_index][column + pos_index].part != PART_NONE)
608
                  {
609
                     if (currentmove->slots[line - pos_index][column + pos_index].color != currentmove->slots[line][column].color)
610
                        currentmove->slots[line - pos_index][column + pos_index].flags |= FLAG_TAKEABLE;
611
                     break;
612
                  }
613
                  currentmove->slots[line - pos_index][column + pos_index].flags |= FLAG_POSSIBLEMOVE;
614
               }
615
 
616
               // see how far queen can move NW
617
               for (pos_index = 1; pos_index < 8; pos_index++)
618
               {
619
                  if ((line + pos_index > 7) || (column - pos_index < 0))
620
                     break;
621
                  if (currentmove->slots[line + pos_index][column - pos_index].part != PART_NONE)
622
                  {
623
                     if (currentmove->slots[line + pos_index][column - pos_index].color != currentmove->slots[line][column].color)
624
                        currentmove->slots[line + pos_index][column - pos_index].flags |= FLAG_TAKEABLE;
625
                     break;
626
                  }
627
                  currentmove->slots[line + pos_index][column - pos_index].flags |= FLAG_POSSIBLEMOVE;
628
               }
629
 
630
               // see how far queen can move SW
631
               for (pos_index = 1; pos_index < 8; pos_index++)
632
               {
633
                  if ((line - pos_index < 0) || (column - pos_index < 0))
634
                     break;
635
                  if (currentmove->slots[line - pos_index][column - pos_index].part != PART_NONE)
636
                  {
637
                     if (currentmove->slots[line - pos_index][column - pos_index].color != currentmove->slots[line][column].color)
638
                        currentmove->slots[line - pos_index][column - pos_index].flags |= FLAG_TAKEABLE;
639
                     break;
640
                  }
641
                  currentmove->slots[line - pos_index][column - pos_index].flags |= FLAG_POSSIBLEMOVE;
642
               }
643
 
644
               // see how far queen can move upwards
645
               for (pos_index = line + 1; pos_index < 8; pos_index++)
646
               {
647
                  if (currentmove->slots[pos_index][column].part != PART_NONE)
648
                  {
649
                     if (currentmove->slots[pos_index][column].color != currentmove->slots[line][column].color)
650
                        currentmove->slots[pos_index][column].flags |= FLAG_TAKEABLE;
651
                     break;
652
                  }
653
                  currentmove->slots[pos_index][column].flags |= FLAG_POSSIBLEMOVE;
654
               }
655
 
656
               // see how far queen can move downwards
657
               for (pos_index = line - 1; pos_index >= 0; pos_index--)
658
               {
659
                  if (currentmove->slots[pos_index][column].part != PART_NONE)
660
                  {
661
                     if (currentmove->slots[pos_index][column].color != currentmove->slots[line][column].color)
662
                        currentmove->slots[pos_index][column].flags |= FLAG_TAKEABLE;
663
                     break;
664
                  }
665
                  currentmove->slots[pos_index][column].flags |= FLAG_POSSIBLEMOVE;
666
               }
667
 
668
               // see how far queen can move left
669
               for (pos_index = column - 1; pos_index >= 0; pos_index--)
670
               {
671
                  if (currentmove->slots[line][pos_index].part != PART_NONE)
672
                  {
673
                     if (currentmove->slots[line][pos_index].color != currentmove->slots[line][column].color)
674
                        currentmove->slots[line][pos_index].flags |= FLAG_TAKEABLE;
675
                     break;
676
                  }
677
                  currentmove->slots[line][pos_index].flags |= FLAG_POSSIBLEMOVE;
678
               }
679
 
680
               // see how far queen can move right
681
               for (pos_index = column + 1; pos_index < 8; pos_index++)
682
               {
683
                  if (currentmove->slots[line][pos_index].part != PART_NONE)
684
                  {
685
                     if (currentmove->slots[line][pos_index].color != currentmove->slots[line][column].color)
686
                        currentmove->slots[line][pos_index].flags |= FLAG_TAKEABLE;
687
                     break;
688
                  }
689
                  currentmove->slots[line][pos_index].flags |= FLAG_POSSIBLEMOVE;
690
               }
691
            }
692
 
693
            //////////////////////////////////////////////////////////////////////////////////////////
694
            ////////////////////////////////////////// KING //////////////////////////////////////////
695
            //////////////////////////////////////////////////////////////////////////////////////////
696
            // else is it a king ?
697
            else if (boardslot->part == PART_KING)
698
            {
699
               // see if king can move NE
700
               if ((line < 7) && (column < 7))
701
               {
702
                  if (currentmove->slots[line + 1][column + 1].part == PART_NONE)
703
                     currentmove->slots[line + 1][column + 1].flags |= FLAG_POSSIBLEMOVE;
704
                  else if (currentmove->slots[line + 1][column + 1].color != currentmove->slots[line][column].color)
705
                     currentmove->slots[line + 1][column + 1].flags |= FLAG_TAKEABLE;
706
               }
707
 
708
               // see if king can move SE
709
               if ((line > 0) && (column < 7))
710
               {
711
                  if (currentmove->slots[line - 1][column + 1].part == PART_NONE)
712
                     currentmove->slots[line - 1][column + 1].flags |= FLAG_POSSIBLEMOVE;
713
                  else if (currentmove->slots[line - 1][column + 1].color != currentmove->slots[line][column].color)
714
                     currentmove->slots[line - 1][column + 1].flags |= FLAG_TAKEABLE;
715
               }
716
 
717
               // see if king can move NW
718
               if ((line < 7) && (column > 0))
719
               {
720
                  if (currentmove->slots[line + 1][column - 1].part == PART_NONE)
721
                     currentmove->slots[line + 1][column - 1].flags |= FLAG_POSSIBLEMOVE;
722
                  else if (currentmove->slots[line + 1][column - 1].color != currentmove->slots[line][column].color)
723
                     currentmove->slots[line + 1][column - 1].flags |= FLAG_TAKEABLE;
724
               }
725
 
726
               // see if king can move SW
727
               if ((line > 0) && (column > 0))
728
               {
729
                  if (currentmove->slots[line - 1][column - 1].part == PART_NONE)
730
                     currentmove->slots[line - 1][column - 1].flags |= FLAG_POSSIBLEMOVE;
731
                  else if (currentmove->slots[line - 1][column - 1].color != currentmove->slots[line][column].color)
732
                     currentmove->slots[line - 1][column - 1].flags |= FLAG_TAKEABLE;
733
               }
734
 
735
               // see if king can move upwards
736
               if (line < 7)
737
               {
738
                  if (currentmove->slots[line + 1][column].part == PART_NONE)
739
                     currentmove->slots[line + 1][column].flags |= FLAG_POSSIBLEMOVE;
740
                  else if (currentmove->slots[line + 1][column].color != currentmove->slots[line][column].color)
741
                     currentmove->slots[line + 1][column].flags |= FLAG_TAKEABLE;
742
               }
743
 
744
               // see if king can move downwards
745
               if (line > 0)
746
               {
747
                  if (currentmove->slots[line - 1][column].part == PART_NONE)
748
                     currentmove->slots[line - 1][column].flags |= FLAG_POSSIBLEMOVE;
749
                  else if (currentmove->slots[line - 1][column].color != currentmove->slots[line][column].color)
750
                     currentmove->slots[line - 1][column].flags |= FLAG_TAKEABLE;
751
               }
752
 
753
               // see if king can move right
754
               if (column < 7)
755
               {
756
                  if (currentmove->slots[line][column + 1].part == PART_NONE)
757
                     currentmove->slots[line][column + 1].flags |= FLAG_POSSIBLEMOVE;
758
                  else if (currentmove->slots[line][column + 1].color != currentmove->slots[line][column].color)
759
                     currentmove->slots[line][column + 1].flags |= FLAG_TAKEABLE;
760
               }
761
 
762
               // see if king can move left
763
               if (column > 0)
764
               {
765
                  if (currentmove->slots[line][column - 1].part == PART_NONE)
766
                     currentmove->slots[line][column - 1].flags |= FLAG_POSSIBLEMOVE;
767
                  else if (currentmove->slots[line][column - 1].color != currentmove->slots[line][column].color)
768
                     currentmove->slots[line][column - 1].flags |= FLAG_TAKEABLE;
769
               }
770
 
771
               // can king castle bishopside ?
772
               if (currentmove->sides[boardslot->color].shortcastle_allowed // no parts have moved yet
773
                   && (currentmove->slots[line][5].part == PART_NONE) // no other part...
774
                   && (currentmove->slots[line][6].part == PART_NONE) // ...in the way
775
                   && !Move_IsCheck (currentmove, boardslot->color) // king not currently in check
776
                   && !Move_IsKingThreatenedAtLocation (currentmove, boardslot->color, line, 5, &threat_line, &threat_column)) // king's way safe
777
                  currentmove->slots[line][column + 2].flags |= FLAG_POSSIBLEMOVE; // allow castling bishopside
778
 
779
               // can king castle queenside ?
780
               if (currentmove->sides[boardslot->color].longcastle_allowed // no parts have moved yet
781
                   && (currentmove->slots[line][3].part == PART_NONE) // no other part...
782
                   && (currentmove->slots[line][2].part == PART_NONE) // ...is...
783
                   && (currentmove->slots[line][1].part == PART_NONE) // ...in the way
784
                   && !Move_IsCheck (currentmove, boardslot->color) // king not currently in check
785
                   && !Move_IsKingThreatenedAtLocation (currentmove, boardslot->color, line, 3, &threat_line, &threat_column)) // king's way safe
786
                  currentmove->slots[line][column - 2].flags |= FLAG_POSSIBLEMOVE; // allow castling queenside
787
            }
788
         }
789
   } // end if (play mode)
790
 
791
   ////////////////////////////////////////////////////////////////////////////////////////////////
792
   // and finally place the parts that are off-board (taken parts)
793
 
794
   // but only if we want them displayed
795
   if (options.want_takenparts)
796
   {
797
      // draw the white player's taken parts, place the part off the board (watch out for the hack...)
798
      for (part_index = 0; part_index < currentmove->sides[COLOR_WHITE].takenpart_count; part_index++)
799
      {
800
         takenpart_type = currentmove->sides[COLOR_WHITE].takenparts[part_index];
801
         Scene_AddPart (scene, takenpart_type, COLOR_BLACK, (part_index < MAX_STACKABLE_PARTS ? 23.2f : 26.8f),
802
                        (part_index % MAX_STACKABLE_PARTS == 0 ? 23.2f : scene->objects[scene->object_count - 1].y - (scene->objects[scene->object_count - 1].simpleshadow_size + simpleshadow_sizes[takenpart_type]) * 0.75f),
803
                        0.04f, (takenpart_type == PART_PAWN ? -90.0f : 100.0f), 0.0f); // rotate pawns 90°
804
      }
805
 
806
      // now draw the black player's taken parts, place the part off the board (watch out for the hack...)
807
      for (part_index = 0; part_index < currentmove->sides[COLOR_BLACK].takenpart_count; part_index++)
808
      {
809
         takenpart_type = currentmove->sides[COLOR_BLACK].takenparts[part_index];
810
         Scene_AddPart (scene, takenpart_type, COLOR_WHITE, (part_index < MAX_STACKABLE_PARTS ? -23.2f : -26.8f),
811
                        (part_index % MAX_STACKABLE_PARTS == 0 ? -23.2f : scene->objects[scene->object_count - 1].y + (scene->objects[scene->object_count - 1].simpleshadow_size + simpleshadow_sizes[takenpart_type]) * 0.75f),
812
                        0.04f, (takenpart_type == PART_PAWN ? -90.0f : 100.0f), 0.0f); // rotate pawns 90°
813
      }
814
   }
815
 
816
   ////////////////////////////////////////////////////////////////////////////////////////////////
817
   // now draw the textured slots
818
 
819
   // cycle through all the board slots...
820
   for (line = 0; line < 8; line++)
821
      for (column = 0; column < 8; column++)
822
      {
823
         if (currentmove->slots[line][column].flags == FLAG_NONE)
824
            continue; // skip everything that doesn't need to be drawn
825
 
826
         // draw the texture we want. Only one texture is allowed, so PRIORITY MATTERS.
827
         if (currentmove->slots[line][column].flags & FLAG_SELECTED)
828
            Scene_AddTile (scene, theme->selected_textureindex, 2.5f, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.02f, 0.0f);
829
         else if (options.want_threats && currentmove->slots[line][column].flags & FLAG_CHECK)
830
            Scene_AddTile (scene, theme->check_textureindex, 2.5f, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.02f, 0.0f);
831
         else if (options.want_possiblemoves && currentmove->slots[line][column].flags & FLAG_POSSIBLEMOVE)
832
            Scene_AddTile (scene, theme->possiblemove_textureindex, 2.5f, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.02f, 0.0f);
833
         else if (options.want_possiblemoves && currentmove->slots[line][column].flags & FLAG_TAKEABLE)
834
            Scene_AddTile (scene, theme->takeable_textureindex, 2.5f, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.02f, 0.0f);
835
         else if (options.want_threats && currentmove->slots[line][column].flags & FLAG_THREAT)
836
            Scene_AddTile (scene, theme->threat_textureindex, 2.5f, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.02f, 0.0f);
837
      }
838
 
839
   // is it time to draw the hovered slot and last move textures on the board slots ?
840
   if (animation_endtime < current_time)
841
   {
842
      // add the hovered slot tile (only when it should not be hidden because of highlighting)
843
      if (IS_VALID_POSITION (board->hovered_position)
844
          && ((highlight_endtime < current_time) || ((int) ((highlight_endtime - current_time) * 20.0f) % 2 == 1)))
845
         Scene_AddTile (scene, theme->hovered_textureindex, 3.1f,
846
                        17.5f - (7 - board->hovered_position[1]) * 5.0f,
847
                        17.5f - board->hovered_position[0] * 5.0f,
848
                        0.03f, 0.0f); // hovered tile
849
 
850
      // and the previous move source and target
851
      if (options.want_lastmove && (board->move_count > 1) && (board->viewed_move > 0)
852
          && IS_VALID_POSITION (board->moves[board->viewed_move].source)
853
          && IS_VALID_POSITION (board->moves[board->viewed_move].target))
854
      {
855
         Scene_AddTile (scene, theme->lastmovesource_textureindex, 2.5f,
856
                        17.5f - (7 - board->moves[board->viewed_move].source[1]) * 5.0f,
857
                        17.5f - board->moves[board->viewed_move].source[0] * 5.0f,
858
                        0.04f, 0.0f); // previous move source
859
         Scene_AddTile (scene, theme->lastmovetarget_textureindex, 2.5f,
860
                        17.5f - (7 - board->moves[board->viewed_move].target[1]) * 5.0f,
861
                        17.5f - board->moves[board->viewed_move].target[0] * 5.0f,
862
                        0.04f, 0.0f); // previous move target
863
      }
864
   }
865
 
866
   ////////////////////////////////////////////////////////////////////////////////////////////////
867
   // now draw the flat icons
868
 
869
   // but only if we want them
870
   if (want_flaticons && (current_player->view_pitch >= MAX_PITCH_FOR_FLAT_ICONS))
871
   {
872
      // determine display yaw according to camera angle
873
      if ((current_yaw > 45.0f) && (current_yaw <= 135.0f))
874
         yaw = 180.0f;
875
      else if ((current_yaw > -45.0f) && (current_yaw <= 45.0f))
876
         yaw = 90.0f;
877
      else if ((current_yaw > -135.0f) && (current_yaw <= -45.0f))
878
         yaw = 0.0f;
879
      else
880
         yaw = -90.0f;
881
 
882
      // cycle through all board slots and display the right part icon
883
      for (line = 0; line < 8; line++)
884
         for (column = 0; column < 8; column++)
885
         {
886
            boardslot = &currentmove->slots[line][column]; // quick access to grid slot
887
 
888
            // is there nothing on this grid slot ?
889
            if (boardslot->part == PART_NONE)
890
               continue; // then don't draw anything on it
891
 
892
            // white or black color ? pick up the right tile and display it
893
            Scene_AddTile (scene, theme->flattextures[boardslot->color][boardslot->part], 2.5f, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.05f, yaw);
894
         }
895
   }
896
 
897
   ///////////////////////////////////////////////////////////
898
   // now draw the sepia overlay if we're in history view mode
899
 
900
   if (options.want_sepiafilter && (board->move_count > 1) && (board->viewed_move != board->move_count - 1))
901
      scene->overlay_spriteindex = sepia_spriteindex; // use the sepia filter
902
   else
903
      scene->overlay_spriteindex = -1; // else use natural colors
904
 
905
   ////////////////////////////////////////////////////////////////////////////////////////////////
906
   // now draw the move comment text
907
 
908
   // does the move we are viewing have a comment ? if so, copy it, else leave it clear. Also if we're online, display a help text
909
   if ((currentmove->comment != NULL) && (currentmove->comment[0] != 0))
910
      Scene_SetText (&scene->gui.comment_text, 53.3f, 0.5f, 93.0f, ALIGN_CENTER, ALIGN_TOP, ALIGN_LEFT, chat_fontindex, RGBA_TO_RGBACOLOR (255, 255, 255, 191), 999999.0f, false, currentmove->comment);
911
   else if ((network_player != NULL) && network_player->is_logged_in && !network_player->is_in_game)
912
      Scene_SetText (&scene->gui.comment_text, 53.3f, 0.5f, 93.0f, ALIGN_CENTER, ALIGN_TOP, ALIGN_LEFT, chat_fontindex, RGBA_TO_RGBACOLOR (255, 255, 255, 191), 999999.0f, false, connected_comment);
913
   else if (RGBACOLOR_ALPHA (scene->gui.comment_text.color) >= 128) // HACK: don't clear if a dimmed hint text is already here
914
      scene->gui.comment_text.is_displayed = false; // else clear comment text
915
 
916
   ////////////////////////////////////////////////////////////////////////////////////////////////
917
   // now draw the game clock
918
 
919
   // do we want to print the game clock ?
920
   if (options.want_clock && (board->move_count > 1) && (board->game_state == STATE_PLAYING))
921
   {
922
      network_player = Player_FindByType (PLAYER_INTERNET); // quick access to network player
923
 
924
      // are we in Internet play ? if so, count time down, else count it up
925
      if ((network_player != NULL) && network_player->is_in_game)
926
         seconds = Player_GetCurrent ()->remaining_seconds - (int) (current_time - board->lastmove_time); // total seconds first
927
      else
928
         seconds = (int) (current_time - board->lastmove_time); // total seconds first
929
 
930
      minutes = seconds / 60; // minutes
931
      seconds -= 60 * minutes; // remaining seconds
932
 
933
      Scene_SetText (&the_scene.gui.clock_text, 99.0f, 66.6f, -1, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_RIGHT, players_fontindex, RGBACOLOR_SETALPHA (options.clock_color, 0x7f), 999999.0f, false, L"%02d:%02d  ", minutes, seconds); // note: last space is alt+255
934
   }
935
   else
936
      scene->gui.clock_text.is_displayed = false;
937
 
938
   ////////////////////////////////////////////////////////////////////////////////////////////////
939
   // now draw the turn text
940
 
941
   // do we want to print the player's turn AND has the game not ended yet ? if so, copy it, else leave it clear
942
   if (options.want_turn && (board->game_state == STATE_PLAYING))
943
   {
944
      if (Board_ColorToMove (board) == COLOR_BLACK)
945
      {
946
         if (the_scene.gui.turn_text.color != 0x000000C0)
947
            Scene_SetText (&the_scene.gui.turn_text, 99.0f, 100.0f, -1, ALIGN_RIGHT, ALIGN_BOTTOM, ALIGN_RIGHT, players_fontindex, 0x000000C0, 999999.0f, true, LOCALIZE (L"BlackMoves"));
948
      }
949
      else
950
      {
951
         if (the_scene.gui.turn_text.color != 0xFFFFFF80)
952
            Scene_SetText (&the_scene.gui.turn_text, 99.0f, 100.0f, -1, ALIGN_RIGHT, ALIGN_BOTTOM, ALIGN_RIGHT, players_fontindex, 0xFFFFFF80, 999999.0f, true, LOCALIZE (L"WhiteMoves"));
953
      }
954
   }
955
   else
956
      scene->gui.turn_text.is_displayed = false;
957
 
958
   ////////////////////////////////////////////////////////////////////////////////////////////////
959
   // now draw the game history text
960
 
961
   // do we want to display the game history ? if so, display the game history text in PGN
962
   if (options.want_history && (board->move_count > 1))
963
   {
964
      // first off, see where we start (for long games, we don't want to display EVERYTHING...
965
      if (board->viewed_move > 30)
966
         start_index = board->viewed_move - 30; // display 30 moves maximum
967
      else
968
         start_index = 1;
969
 
970
      // allocate an arbitrary length history text string buffer (assume each move can't be longer than 15 characters)
971
      historytext_size = 15 * (1 + (board->viewed_move + 1) - start_index);
972
      history_text = (wchar_t *) SAFE_malloc (historytext_size, sizeof (wchar_t), false);
973
      history_text[0] = 0; // and reset it
974
 
975
      // now for each move we want to display...
976
      for (move_index = start_index; move_index <= board->viewed_move; move_index++)
977
      {
978
         length = wcslen (history_text); // get current text length
979
 
980
         // every move pair, append move pair number
981
         if (move_index % 2 == 1)
982
            swprintf_s (&history_text[length], historytext_size - length, L"%d.   ", 1 + move_index / 2);
983
 
984
         // append move text
985
         wcscat_s (history_text, historytext_size, board->moves[move_index].pgntext);
986
         wcscat_s (history_text, historytext_size, L"   ");
987
 
988
         // every odd move, drop a newline
989
         if (move_index % 2 == 0)
990
            wcscat_s (history_text, historytext_size, L"\n");
991
      }
992
 
993
      // add 50% alpha to game history color and transmit it to 3D engine
994
      Scene_SetText (&the_scene.gui.history_text, 100.0f, 50.0f, -1, ALIGN_RIGHT, ALIGN_BOTTOM, ALIGN_LEFT, chat_fontindex, RGBACOLOR_SETALPHA (options.history_color, 0x7f), 999999.0f, false, history_text);
995
      SAFE_free ((void **) &history_text);
996
   }
997
   else
998
      scene->gui.history_text.is_displayed = false; // if we don't need to display the game history, free the buffer
999
 
1000
   ////////////////////////////////////////////////////////////////////////////////////////////////
1001
   // now draw the chat area
1002
 
1003
   // is a valid chatter channel selected ?
1004
   if ((selected_chatterchannel != NULL) && (chatterchannel_count > 0) && ((local_player = Player_FindByType (PLAYER_HUMAN)) != NULL))
1005
   {
1006
      // set the chatter's name
1007
      wcscpy_s (scene->gui.entered_ccreply.nickname, WCHAR_SIZEOF (scene->gui.entered_ccreply.nickname), local_player->name);
1008
 
1009
      // correct or update the channel name and color
1010
      if (selected_chatterchannel->theme[0] != 0)
1011
         wcscpy_s (scene->gui.entered_ccreply.channelname, WCHAR_SIZEOF (scene->gui.entered_ccreply.channelname), selected_chatterchannel->theme);
1012
      else
1013
         swprintf_s (scene->gui.entered_ccreply.channelname, WCHAR_SIZEOF (scene->gui.entered_ccreply.channelname), L"%s %d", LOCALIZE (L"ChatterChannels_ColumnChannelNumber"), selected_chatterchannel->id);
1014
      scene->gui.entered_ccreply.color = RGBACOLOR_FULLALPHA (selected_chatterchannel->color); // full bright for entering text
1015
   }
1016
 
1017
   // display parts pick line in position setup mode only
1018
   scene->gui.is_partspick_displayed = (board->game_state == STATE_SETUPPOSITION ? true : false);
1019
 
1020
   //////////////////////////
1021
   // error and notifications
1022
 
1023
   // is the current player a computer AND are we playing a game right now
1024
   // AND has the computer been thinking for more than 5 seconds AND is there no "thinking" text yet ?
1025
   if ((board->players[Board_ColorToMove (board)].type == PLAYER_COMPUTER) && (board->game_state == STATE_PLAYING)
1026
       && (board->lastmove_time + 5.0f < current_time) && !scene->gui.central_text.is_displayed)
1027
   {
1028
      Scene_SetText (&the_scene.gui.central_text, 50.0f, 40.0f, -1, ALIGN_CENTER, ALIGN_CENTER, ALIGN_CENTER, centermsg_fontindex, RGBA_TO_RGBACOLOR (255, 255, 255, 191),
1029
                     999999.0f, true, LOCALIZE (L"Thinking")); // if so, display the "thinking" phrase in the middle of the screen
1030
      the_scene.gui.want_spinwheel = true; // start spinning wheel
1031
   }
1032
 
1033
   // is there a network player AND is our socket gone AWOL ?
1034
   else if ((network_player != NULL) && (network_player->our_socket == INVALID_SOCKET))
1035
   {
1036
      // is there nothing in the center of the screen yet ?
1037
      if (!the_scene.gui.central_text.is_displayed)
1038
         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),
1039
                        999999.0f, true, LOCALIZE (L"Error_ConnectionToChessServerLost")); // display "error" in the middle of the screen
1040
      the_scene.overlay_spriteindex = sepia_spriteindex; // display sepia filter if no connection
1041
   }
1042
 
1043
   return; // finished, scene is updated
1044
}
1045
 
1046
 
1047
void Scene_AddCCReply (scene_t *scene, wchar_t *nickname, wchar_t *channelname, unsigned long color_rgbx, wchar_t *fmt, ...)
1048
{
1049
   // helper function to add a CC reply on display
1050
 
1051
   static wchar_t message[4096];
1052
   ccreply_t re;
1053
   va_list argptr;
1054
 
1055
   // concatenate all the arguments in one string
1056
   va_start (argptr, fmt);
1057
   _vsnwprintf_s (message, WCHAR_SIZEOF (message), _TRUNCATE, fmt, argptr);
1058
   va_end (argptr);
1059
 
1060
   // now put the text in place
1061
   memset (&re, 0, sizeof (re)); // reset the structure we're about to fill
1062
 
1063
   wcscpy_s (re.nickname, WCHAR_SIZEOF (re.nickname), nickname); // copy nickname
1064
   wcscpy_s (re.channelname, WCHAR_SIZEOF (re.channelname), channelname); // copy channel name
1065
 
1066
   re.text_length = wcslen (message); // get text length
1067
   re.text = (wchar_t *) SAFE_malloc (re.text_length + 1, sizeof (wchar_t), false); // allocate text space (include null terminator)
1068
   wcscpy_s (re.text, re.text_length + 1, message); // get the text
1069
 
1070
   re.color = RGBACOLOR_SETALPHA (color_rgbx, 0xC0); // copy reply color and force a slightly transparent alpha
1071
   re.arrival_time = current_time; // save CC reply arrival time
1072
 
1073
   // reallocate CC history array to hold now one reply more
1074
   the_scene.gui.cchistory = (ccreply_t *) SAFE_realloc (the_scene.gui.cchistory, the_scene.gui.cchistory_count, the_scene.gui.cchistory_count + 1, sizeof (ccreply_t), false);
1075
   memcpy (&the_scene.gui.cchistory[the_scene.gui.cchistory_count], &re, sizeof (ccreply_t));
1076
   the_scene.gui.cchistory_count++; // CC history holds now one reply more
1077
 
1078
   return; // finished, announcement text is set
1079
}
1080
 
1081
 
1082
void Scene_AddAnnouncement (scene_t *scene, wchar_t *fmt, ...)
1083
{
1084
   // helper function to set the announcement (red) text on display
1085
 
1086
   static wchar_t message[4096];
1087
   ccreply_t re;
1088
   va_list argptr;
1089
 
1090
   // concatenate all the arguments in one string
1091
   va_start (argptr, fmt);
1092
   _vsnwprintf_s (message, WCHAR_SIZEOF (message), _TRUNCATE, fmt, argptr);
1093
   va_end (argptr);
1094
 
1095
   // now put the text in place
1096
   memset (&re, 0, sizeof (re)); // reset the structure we're about to fill
1097
 
1098
   re.text_length = wcslen (message); // get text length
1099
   re.text = (wchar_t *) SAFE_malloc (re.text_length + 1, sizeof (wchar_t), false); // allocate text space (include null terminator)
1100
   wcscpy_s (re.text, re.text_length + 1, message); // get the text
1101
 
1102
   wcscpy_s (re.channelname, WCHAR_SIZEOF (re.channelname), LOCALIZE (L"ImportantMessage"));
1103
   re.color = RGBA_TO_RGBACOLOR (192, 0, 0, 0xE0); // fair red, a bit more opaque than normal messages
1104
   re.arrival_time = current_time; // save announcement arrival time
1105
 
1106
   // reallocate CC history array to hold now one reply more
1107
   the_scene.gui.cchistory = (ccreply_t *) SAFE_realloc (the_scene.gui.cchistory, the_scene.gui.cchistory_count, the_scene.gui.cchistory_count + 1, sizeof (ccreply_t), false);
1108
   memcpy (&the_scene.gui.cchistory[the_scene.gui.cchistory_count], &re, sizeof (ccreply_t));
1109
   the_scene.gui.cchistory_count++; // CC history holds now one reply more
1110
 
1111
   return; // finished, announcement text is set
1112
}
1113
 
1114
 
1115
void Scene_SetButton (guibutton_t *button, float left, float top, float width, float height, int sprite_index)
1116
{
1117
   // helper function to set up a GUI button
1118
 
1119
   button->left = left;
1120
   button->top = top;
1121
   button->width = width;
1122
   button->height = height;
1123
   button->sprite_index = sprite_index;
1124
 
1125
   return; // finished, button is set
1126
}
1127
 
1128
 
1129
void Scene_SetText (guitext_t *text, float xpos_percent, float ypos_percent, float maxwidth_percent, int horizontal_align, int vertical_align, int text_align, int font_index, unsigned long color_rgba, float duration, bool want_fade, wchar_t *fmt, ...)
1130
{
1131
   // helper function to set some text on display
1132
 
1133
   static wchar_t message[4096];
1134
   va_list argptr;
1135
   int length;
1136
 
1137
   // concatenate all the arguments in one string
1138
   va_start (argptr, fmt);
1139
   _vsnwprintf_s (message, WCHAR_SIZEOF (message), _TRUNCATE, fmt, argptr);
1140
   va_end (argptr);
1141
 
1142
   text->xpos_percent = xpos_percent; // save text's X position, in percents from left to right
1143
   text->ypos_percent = ypos_percent; // save text's Y position, in percents from top to bottom
1144
   text->maxwidth_percent = maxwidth_percent; // save text's max width before word wrapping
1145
   text->horizontal_align = horizontal_align; // save text's horizontal alignment regarding the X position
1146
   text->vertical_align = vertical_align; // save text's vertical alignment regarding the Y position
1147
   text->text_align = text_align; // save text's horizontal alignment inside the bounding rectangle
1148
   text->font_index = font_index; // save the index of the font with which to display this text
1149
   text->color = color_rgba; // text's color, in RGBA
1150
 
1151
   // now put the text in place
1152
   length = wcslen (message) + 1; // include null terminator
1153
   text->buffer = (wchar_t *) SAFE_realloc (text->buffer, text->buffer_size, length, sizeof (wchar_t), false);
1154
   wcscpy_s (text->buffer, length, message); // copy message text
1155
   text->buffer_size = length; // and save buffer length
1156
 
1157
   text->appear_time = current_time; // save text arrival time
1158
   text->disappear_time = current_time + duration; // make it last duration seconds
1159
   text->want_fade = want_fade; // remember if text needs to be faded in and out
1160
 
1161
   // mark this text for display
1162
   text->is_displayed = true;
1163
 
1164
   return; // finished, text is set
1165
}
1166
 
1167
 
1168
static void Scene_AddPart (scene_t *scene, int part_type, int part_color, float pos_x, float pos_y, float pos_z, float turn_yaw, float pitch)
1169
{
1170
   // helper function to add a specified part of the specified color to the rendered scene
1171
 
1172
   sceneobject_t *object;
1173
   partcolor_t *partcolor;
1174
 
1175
   // reallocate space to hold one object more and blank it out
1176
   scene->objects = (sceneobject_t *) SAFE_realloc (scene->objects, scene->object_count, scene->object_count + 1, sizeof (sceneobject_t), true);
1177
 
1178
   object = &scene->objects[scene->object_count]; // quick access to object
1179
 
1180
   object->mesh_index = theme->part_meshes[part_type]; // retrieve object mesh
1181
   object->simpleshadow_size = simpleshadow_sizes[part_type]; // retrieve simple shadow size according to part type
1182
   object->scale = 1.0f; // scale at 1 so far
1183
 
1184
   // set object texture and material
1185
   partcolor = &theme->part_colors[part_color]; // quick access to part color struct
1186
   object->texture_index = partcolor->texture;
1187
   object->material_index = partcolor->material;
1188
 
1189
   // figure out object position on board
1190
   object->x = pos_x;
1191
   object->y = pos_y;
1192
   object->z = pos_z;
1193
 
1194
   // turn a color's all parts' yaw 180 degrees so as both sides face each other
1195
   if (part_color == COLOR_WHITE)
1196
      object->yaw = 180.0f;
1197
   else
1198
      object->yaw = 0.0f;
1199
 
1200
   // and add the final turn pitch and yaw
1201
   object->pitch = pitch;
1202
   object->yaw = WrapAngle (object->yaw + turn_yaw);
1203
 
1204
   scene->object_count++; // array holds now one object more
1205
   return; // finished
1206
}
1207
 
1208
 
1209
static void Scene_AddTile (scene_t *scene, int texture_index, float scale, float pos_x, float pos_y, float pos_z, float turn_yaw)
1210
{
1211
   // helper function to add a specified part of the specified color to the rendered scene
1212
 
1213
   sceneobject_t *object;
1214
 
1215
   // reallocate space to hold one object more and blank it out
1216
   scene->objects = (sceneobject_t *) SAFE_realloc (scene->objects, scene->object_count, scene->object_count + 1, sizeof (sceneobject_t), true);
1217
 
1218
   object = &scene->objects[scene->object_count]; // quick access to object
1219
 
1220
   // save object data
1221
   object->mesh_index = -1; // objects that have -1 as mesh index are tiles
1222
   object->texture_index = texture_index;
1223
   object->material_index = -1; // objects will use the default material
1224
 
1225
   // figure out object position on board
1226
   object->x = pos_x;
1227
   object->y = pos_y;
1228
   object->z = pos_z;
1229
   object->scale = scale;
1230
 
1231
   // turn tile as requested
1232
   object->yaw = turn_yaw;
1233
 
1234
   scene->object_count++; // array holds now one object more
1235
 
1236
   return; // finished
1237
}