Subversion Repositories Games.Chess Giants

Rev

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