// scene.cpp
 
 
 
#include "common.h"
 
 
 
 
 
// prototypes of local functions
 
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);
 
static void Scene_AddTile (scene_t *scene, int tile_type, float scale, float pos_x, float pos_y, float pos_z, float turn_yaw);
 
 
 
 
 
// global variables used in this module only
 
static wchar_t connected_comment[4096];
 
static wchar_t filename[MAX_PATH];
 
static float simpleshadow_sizes[] =
 
{
 
   0, // #define PART_NONE 0
 
   2.5f, // #define PART_KING 1
 
   2.45f, // #define PART_QUEEN 2
 
   2.35f, // #define PART_BISHOP 3
 
   2.25f, // #define PART_KNIGHT 4
 
   2.15f, // #define PART_ROOK 5
 
   2.0f, // #define PART_PAWN 6
 
};
 
 
 
 
 
void Scene_Init (scene_t *scene, board_t *board)
 
{
 
   // this function initializes the scene objects array and inserts the chess table in it
 
 
 
   wchar_t format_string[4096];
 
 
 
   // allocate array for the board (3 objects) and zero it out
 
   scene->objects = (sceneobject_t *) SAFE_malloc (3, sizeof (sceneobject_t), true);
 
 
 
   // insert the table edges, the table and the board
 
   scene->objects[0].mesh_index = theme->trim_meshindex;
 
   scene->objects[0].texture_index = theme->trim_texture;
 
   scene->objects[0].scale = 1.0f;
 
   scene->objects[1].mesh_index = theme->table_meshindex;
 
   scene->objects[1].texture_index = theme->table_texture;
 
   scene->objects[1].scale = 1.0f;
 
   scene->objects[2].mesh_index = theme->board_meshindex;
 
   scene->objects[2].texture_index = theme->board_texture;
 
   scene->objects[2].scale = 1.0f;
 
   scene->object_count = 3;
 
 
 
   // reset the camera position for a cool slide-in effect, but only if autorotate is enabled
 
   if (options.want_autorotateon1vs1)
 
   {
 
      current_pitch = CLOSEUP_VIEW_PITCH; // autorotate is enabled, prepare for slide-in effect
 
      current_yaw = (Board_ColorToMove (board) == COLOR_WHITE ? 90.0f : -90.0f);
 
      current_distance = CLOSEUP_VIEW_DISTANCE;
 
      if (current_distance >= MIN_VIEW_DISTANCE)
 
         current_distance = MIN_VIEW_DISTANCE - 1.0f; // consistency check for stupid programmers
 
   }
 
   else
 
   {
 
      current_pitch = 55.0f; // no autorotate, reset to standard view position directly
 
      current_yaw = (Board_ColorToMove (board) == COLOR_BLACK ? 90.0f : -90.0f);
 
      current_distance = 70.0f;
 
      if (current_distance < MIN_VIEW_DISTANCE)
 
         current_distance = MIN_VIEW_DISTANCE; // consistency check for stupid programmers
 
   }
 
 
 
   // HACK to prevent the player to click and block the view angles while the slide-in is not finished
 
   command_ignoretime = current_time + 2.0f; // allow 2 seconds
 
 
 
   // build the connected comment string (we use it as a global variable)
 
   wcscpy_s (format_string, WCHAR_SIZEOF (format_string), LOCALIZE (L"YouAreConnectedToX"));
 
   wcscat_s (format_string, WCHAR_SIZEOF (format_string), L"\n");
 
   wcscat_s (format_string, WCHAR_SIZEOF (format_string), LOCALIZE (L"YouMayEitherDisplayThePlayersListOrTheSoughtGamesList"));
 
   wcscat_s (format_string, WCHAR_SIZEOF (format_string), L"\n");
 
   wcscat_s (format_string, WCHAR_SIZEOF (format_string), LOCALIZE (L"PleaseReferToTheInternetMenu"));
 
   swprintf_s (connected_comment, WCHAR_SIZEOF (connected_comment), format_string, options.network.server_address);
 
 
 
   // completely reset the whole GUI structure so as to NULL out all pointers
 
   memset (&scene->gui, 0, sizeof (scene->gui));
 
 
 
   // set the buttons locations
 
   Scene_SetButton (&scene->gui.larrow, 0.3f, 0.5f, 3.0f, 4.0f, larrow_spriteindex, NULL);
 
   Scene_SetButton (&scene->gui.rarrow, 3.3f, 0.5f, 3.0f, 4.0f, rarrow_spriteindex, NULL);
 
   Scene_SetButton (&scene->gui.newgamebutton, 20.0f, 65.0f, 20.0f, 26.0f, newgamebutton_spriteindex, L"\n\n\n\n\n\n\n%s", LOCALIZE (L"NewGame"));
 
   Scene_SetButton (&scene->gui.opengamebutton, 60.0f, 65.0f, 20.0f, 26.0f, opengamebutton_spriteindex, L"\n\n\n\n\n\n\n%s", LOCALIZE (L"OpenGame"));
 
   Scene_SetButton (&scene->gui.chatbutton, 1.0f, 10.0f, 10.0f, 13.0f, chatbutton_spriteindex, NULL);
 
   Scene_SetButton (&scene->gui.gamesbutton, 1.0f, 35.0f, 10.0f, 13.0f, gamesbutton_spriteindex, NULL);
 
   Scene_SetButton (&scene->gui.peoplebutton, 1.0f, 60.0f, 10.0f, 13.0f, peoplebutton_spriteindex, NULL);
 
 
 
   // remember to update the scene
 
   scene->update = true;
 
 
 
   return; // finished
 
}
 
 
 
 
 
void Scene_Shutdown (scene_t *scene)
 
{
 
   // this function frees the memory space allocated for the 3D scene and clears its pointers
 
 
 
   int cchistory_index;
 
 
 
   // free GUI mallocated buffers
 
   SAFE_free ((void **) &scene->gui.arrow_text.buffer);
 
   scene->gui.arrow_text.is_displayed = false;
 
   SAFE_free ((void **) &scene->gui.comment_text.buffer);
 
   scene->gui.comment_text.is_displayed = false;
 
   SAFE_free ((void **) &scene->gui.history_text.buffer);
 
   scene->gui.history_text.is_displayed = false;
 
   SAFE_free ((void **) &scene->gui.clock_text.buffer);
 
   scene->gui.clock_text.is_displayed = false;
 
   SAFE_free ((void **) &scene->gui.turn_text.buffer);
 
   scene->gui.turn_text.is_displayed = false;
 
   SAFE_free ((void **) &scene->gui.central_text.buffer);
 
   scene->gui.central_text.is_displayed = false;
 
 
 
   // for each entry in the CC history...
 
   for (cchistory_index = 0; cchistory_index < scene->gui.cchistory_count; cchistory_index++)
 
      SAFE_free ((void **) &scene->gui.cchistory[cchistory_index].text); // free its text buffer
 
   SAFE_free ((void **) &scene->gui.cchistory); // free the GUI's CC history
 
   scene->gui.cchistory_count = 0;
 
 
 
   SAFE_free ((void **) &scene->objects); // free the scene objects array
 
   scene->object_count = 0;
 
 
 
   return; // finished
 
}
 
 
 
 
 
void Scene_Update (scene_t *scene, board_t *board)
 
{
 
   // this function updates the scene objects to display with what's currently on the board
 
 
 
   static bool rooksound_played = false; // hack to have two sounds when a king castles
 
 
 
   boardslot_t *boardslot;
 
   boardmove_t *currentmove;
 
   player_t *local_player;
 
   player_t *network_player;
 
   player_t *current_player;
 
   player_t *opposite_player;
 
   wchar_t *history_text; // mallocated
 
   int historytext_size;
 
   unsigned char takenpart_type;
 
   int movement_direction;
 
   int line;
 
   int column;
 
   int pos_index;
 
   int part_index;
 
   int move_index;
 
   int start_index;
 
   int length;
 
   int threat_line;
 
   int threat_column;
 
   int minutes;
 
   int seconds;
 
   float flaticons_yaw;
 
   float source_x;
 
   float source_y;
 
   float target_x;
 
   float target_y;
 
   float current_x;
 
   float current_y;
 
   float current_z;
 
   int movement_diffco;
 
   int movement_diffli;
 
   float movement_maxheight;
 
   float movement_ratio;
 
 
 
   // get the current player (we'll need it), its opponent and see if we're online
 
   current_player = Player_GetCurrent ();
 
   opposite_player = Player_GetOpposite ();
 
   network_player = Player_FindByType (PLAYER_INTERNET);
 
 
 
   // determine display yaw (for flat icons) according to camera angle
 
   if ((current_yaw > 45.0f) && (current_yaw <= 135.0f))
 
      flaticons_yaw = 180.0f;
 
   else if ((current_yaw > -45.0f) && (current_yaw <= 45.0f))
 
      flaticons_yaw = 90.0f;
 
   else if ((current_yaw > -135.0f) && (current_yaw <= -45.0f))
 
      flaticons_yaw = 0.0f;
 
   else
 
      flaticons_yaw = -90.0f;
 
 
 
   // get the current move
 
   currentmove = &board->moves[board->viewed_move]; // quick access to current move
 
 
 
   // fetch the background sprite from the theme
 
   if (want_custombackground)
 
      scene->background_spriteindex = custombg.sprite_index; // use the custom background if specified
 
   else
 
      scene->background_spriteindex = theme->bg.sprite_index; // else use the theme's supplied background
 
 
 
   // shrink the scene objects array to leave just the board (3 objects)
 
   scene->objects = (sceneobject_t *) SAFE_realloc (scene->objects, scene->object_count, 3, sizeof (sceneobject_t), false);
 
   scene->object_count = 3;
 
 
 
   // update the board theme
 
   scene->objects[0].mesh_index = theme->trim_meshindex;
 
   scene->objects[0].texture_index = theme->trim_texture;
 
   scene->objects[0].material_index = theme->trim_material;
 
   scene->objects[0].scale = 1.0f;
 
   scene->objects[1].mesh_index = theme->table_meshindex;
 
   scene->objects[1].texture_index = theme->table_texture;
 
   scene->objects[1].material_index = theme->table_material;
 
   scene->objects[1].scale = 1.0f;
 
   scene->objects[2].mesh_index = theme->board_meshindex;
 
   scene->objects[2].texture_index = theme->board_texture;
 
   scene->objects[2].material_index = theme->board_material;
 
   scene->objects[2].scale = 1.0f;
 
 
 
   // draw the grid numbers if we want them
 
   if (want_grid)
 
      Scene_AddTile (scene, theme->grid_texture, 30.0f, 0.0f, 0.0f, 0.01f, 0.0f);
 
 
 
   ////////////////////////////////////////////////////////////////////////////////////////////////
 
   // recompute the slot textures (only in play mode when we render the real board)
 
 
 
   // erase all the slot flags
 
   for (line = 0; line < 8; line++)
 
      for (column = 0; column < 8; column++)
 
         currentmove->slots[line][column].flags = FLAG_NONE; // because we will recompute them
 
 
 
   // cycle through all the grid again and see if either king is in check
 
   for (line = 0; line < 8; line++)
 
      for (column = 0; column < 8; column++)
 
      {
 
         boardslot = ¤tmove->slots[line][column]; // quick access to grid slot
 
 
 
         if (boardslot->part != PART_KING)
 
            continue; // if this slot is not a king, skip it
 
 
 
         // is this king currently threatened ?
 
         if (Move_IsKingThreatenedAtLocation (currentmove, boardslot->color, line, column, &threat_line, &threat_column))
 
         {
 
            currentmove->slots[line][column].flags |= FLAG_CHECK; // mark it as threatened
 
            currentmove->slots[threat_line][threat_column].flags |= FLAG_THREAT; // and who threatens it
 
         }
 
      }
 
 
 
   // are we in play mode ? i.e, are we rendering the last move ?
 
   if (board->viewed_move == board->move_count - 1)
 
   {
 
      // mark the selected position as selected
 
      if (IS_VALID_POSITION (board->selected_position))
 
         currentmove->slots[board->selected_position[0]][board->selected_position[1]].flags |= FLAG_SELECTED;
 
 
 
      // now cycle through all the grid again and see if some slots need to be coloured
 
      for (line = 0; line < 8; line++)
 
         for (column = 0; column < 8; column++)
 
         {
 
            boardslot = ¤tmove->slots[line][column]; // quick access to grid slot
 
 
 
            if (!(boardslot->flags & FLAG_SELECTED))
 
               continue; // if this slot is not selected, skip it
 
 
 
            //////////////////////////////////////////////////////////////////////////////////////////
 
            ////////////////////////////////////////// PAWN //////////////////////////////////////////
 
            //////////////////////////////////////////////////////////////////////////////////////////
 
            // is it a pawn ?
 
            if (boardslot->part == PART_PAWN)
 
            {
 
               // figure out movement direction
 
               if (boardslot->color == COLOR_WHITE)
 
                  movement_direction = 1;
 
               else
 
                  movement_direction = -1;
 
 
 
               // if pawn has still room to move forward, it can
 
               if ((((line < 7) && (movement_direction == 1)) || ((line > 0) && (movement_direction == -1)))
 
                   && (currentmove->slots[line + movement_direction][column].part == PART_NONE))
 
               {
 
                  currentmove->slots[line + movement_direction][column].flags |= FLAG_POSSIBLEMOVE;
 
 
 
                  // if pawn is still in its initial slot, it can move twice forward
 
                  if ((((line == 1) && (movement_direction == 1)) || ((line == 6) && (movement_direction == -1)))
 
                      && (currentmove->slots[line + movement_direction * 2][column].part == PART_NONE))
 
                     currentmove->slots[line + movement_direction * 2][column].flags |= FLAG_POSSIBLEMOVE;
 
               }
 
 
 
               // see if pawn can take a piece on its left
 
               if ((column > 0) && (currentmove->slots[line + movement_direction][column - 1].part != PART_NONE)
 
                   && (currentmove->slots[line + movement_direction][column - 1].color != currentmove->slots[line][column].color))
 
                  currentmove->slots[line + movement_direction][column - 1].flags |= FLAG_TAKEABLE;
 
 
 
               // see if pawn can take a piece on its right
 
               if ((column < 7) && (currentmove->slots[line + movement_direction][column + 1].part != PART_NONE)
 
                   && (currentmove->slots[line + movement_direction][column + 1].color != currentmove->slots[line][column].color))
 
                  currentmove->slots[line + movement_direction][column + 1].flags |= FLAG_TAKEABLE;
 
 
 
               // if previous move was a pawn rush, see if pawn can take "en passant"
 
               if ((currentmove->part == PART_PAWN)
 
                     && (currentmove->target[1] == currentmove->source[1]) // pawn moved in column
 
                     && (abs (currentmove->target[0] - currentmove->source[0]) == 2) // pawn rushed
 
                     && (currentmove->target[0] == line) // pawn is in line with us
 
                     && (abs (currentmove->target[1] - column) == 1)) // pawn is next to us
 
                  currentmove->slots[line + movement_direction][currentmove->target[1]].flags |= FLAG_TAKEABLE;
 
            }
 
 
 
            //////////////////////////////////////////////////////////////////////////////////////////
 
            ////////////////////////////////////////// ROOK //////////////////////////////////////////
 
            //////////////////////////////////////////////////////////////////////////////////////////
 
            // else is it a rook ?
 
            else if (boardslot->part == PART_ROOK)
 
            {
 
               // see how far rook can move upwards
 
               for (pos_index = line + 1; pos_index < 8; pos_index++)
 
               {
 
                  if (currentmove->slots[pos_index][column].part != PART_NONE)
 
                  {
 
                     if (currentmove->slots[pos_index][column].color != currentmove->slots[line][column].color)
 
                        currentmove->slots[pos_index][column].flags |= FLAG_TAKEABLE;
 
                     break;
 
                  }
 
                  currentmove->slots[pos_index][column].flags |= FLAG_POSSIBLEMOVE;
 
               }
 
 
 
               // see how far rook can move downwards
 
               for (pos_index = line - 1; pos_index >= 0; pos_index--)
 
               {
 
                  if (currentmove->slots[pos_index][column].part != PART_NONE)
 
                  {
 
                     if (currentmove->slots[pos_index][column].color != currentmove->slots[line][column].color)
 
                        currentmove->slots[pos_index][column].flags |= FLAG_TAKEABLE;
 
                     break;
 
                  }
 
                  currentmove->slots[pos_index][column].flags |= FLAG_POSSIBLEMOVE;
 
               }
 
 
 
               // see how far rook can move left
 
               for (pos_index = column - 1; pos_index >= 0; pos_index--)
 
               {
 
                  if (currentmove->slots[line][pos_index].part != PART_NONE)
 
                  {
 
                     if (currentmove->slots[line][pos_index].color != currentmove->slots[line][column].color)
 
                        currentmove->slots[line][pos_index].flags |= FLAG_TAKEABLE;
 
                     break;
 
                  }
 
                  currentmove->slots[line][pos_index].flags |= FLAG_POSSIBLEMOVE;
 
               }
 
 
 
               // see how far rook can move right
 
               for (pos_index = column + 1; pos_index < 8; pos_index++)
 
               {
 
                  if (currentmove->slots[line][pos_index].part != PART_NONE)
 
                  {
 
                     if (currentmove->slots[line][pos_index].color != currentmove->slots[line][column].color)
 
                        currentmove->slots[line][pos_index].flags |= FLAG_TAKEABLE;
 
                     break;
 
                  }
 
                  currentmove->slots[line][pos_index].flags |= FLAG_POSSIBLEMOVE;
 
               }
 
            }
 
 
 
            //////////////////////////////////////////////////////////////////////////////////////////
 
            ///////////////////////////////////////// KNIGHT /////////////////////////////////////////
 
            //////////////////////////////////////////////////////////////////////////////////////////
 
            // else is it a knight ?
 
            else if (boardslot->part == PART_KNIGHT)
 
            {
 
               // peek knight's NNW move
 
               if ((column > 0) && (line < 6))
 
                  if (currentmove->slots[line + 2][column - 1].part == PART_NONE)
 
                     currentmove->slots[line + 2][column - 1].flags |= FLAG_POSSIBLEMOVE;
 
                  else if (currentmove->slots[line + 2][column - 1].color != currentmove->slots[line][column].color)
 
                     currentmove->slots[line + 2][column - 1].flags |= FLAG_TAKEABLE;
 
 
 
               // peek knight's NNE move
 
               if ((column < 7) && (line < 6))
 
                  if (currentmove->slots[line + 2][column + 1].part == PART_NONE)
 
                     currentmove->slots[line + 2][column + 1].flags |= FLAG_POSSIBLEMOVE;
 
                  else if (currentmove->slots[line + 2][column + 1].color != currentmove->slots[line][column].color)
 
                     currentmove->slots[line + 2][column + 1].flags |= FLAG_TAKEABLE;
 
 
 
               // peek knight's ENE move
 
               if ((column < 6) && (line < 7))
 
                  if (currentmove->slots[line + 1][column + 2].part == PART_NONE)
 
                     currentmove->slots[line + 1][column + 2].flags |= FLAG_POSSIBLEMOVE;
 
                  else if (currentmove->slots[line + 1][column + 2].color != currentmove->slots[line][column].color)
 
                     currentmove->slots[line + 1][column + 2].flags |= FLAG_TAKEABLE;
 
 
 
               // peek knight's ESE move
 
               if ((column < 6) && (line > 0))
 
                  if (currentmove->slots[line - 1][column + 2].part == PART_NONE)
 
                     currentmove->slots[line - 1][column + 2].flags |= FLAG_POSSIBLEMOVE;
 
                  else if (currentmove->slots[line - 1][column + 2].color != currentmove->slots[line][column].color)
 
                     currentmove->slots[line - 1][column + 2].flags |= FLAG_TAKEABLE;
 
 
 
               // peek knight's SSW move
 
               if ((column > 0) && (line > 1))
 
                  if (currentmove->slots[line - 2][column - 1].part == PART_NONE)
 
                     currentmove->slots[line - 2][column - 1].flags |= FLAG_POSSIBLEMOVE;
 
                  else if (currentmove->slots[line - 2][column - 1].color != currentmove->slots[line][column].color)
 
                     currentmove->slots[line - 2][column - 1].flags |= FLAG_TAKEABLE;
 
 
 
               // peek knight's SSE move
 
               if ((column < 7) && (line > 1))
 
                  if (currentmove->slots[line - 2][column + 1].part == PART_NONE)
 
                     currentmove->slots[line - 2][column + 1].flags |= FLAG_POSSIBLEMOVE;
 
                  else if (currentmove->slots[line - 2][column + 1].color != currentmove->slots[line][column].color)
 
                     currentmove->slots[line - 2][column + 1].flags |= FLAG_TAKEABLE;
 
 
 
               // peek knight's WNW move
 
               if ((column > 1) && (line < 7))
 
                  if (currentmove->slots[line + 1][column - 2].part == PART_NONE)
 
                     currentmove->slots[line + 1][column - 2].flags |= FLAG_POSSIBLEMOVE;
 
                  else if (currentmove->slots[line + 1][column - 2].color != currentmove->slots[line][column].color)
 
                     currentmove->slots[line + 1][column - 2].flags |= FLAG_TAKEABLE;
 
 
 
               // peek knight's WSW move
 
               if ((column > 1) && (line > 0))
 
                  if (currentmove->slots[line - 1][column - 2].part == PART_NONE)
 
                     currentmove->slots[line - 1][column - 2].flags |= FLAG_POSSIBLEMOVE;
 
                  else if (currentmove->slots[line - 1][column - 2].color != currentmove->slots[line][column].color)
 
                     currentmove->slots[line - 1][column - 2].flags |= FLAG_TAKEABLE;
 
            }
 
 
 
            //////////////////////////////////////////////////////////////////////////////////////////
 
            ///////////////////////////////////////// BISHOP /////////////////////////////////////////
 
            //////////////////////////////////////////////////////////////////////////////////////////
 
            // else is it a bishop ?
 
            else if (boardslot->part == PART_BISHOP)
 
            {
 
               // see how far bishop can move NE
 
               for (pos_index = 1; pos_index < 8; pos_index++)
 
               {
 
                  if ((line + pos_index > 7) || (column + pos_index > 7))
 
                     break;
 
                  if (currentmove->slots[line + pos_index][column + pos_index].part != PART_NONE)
 
                  {
 
                     if (currentmove->slots[line + pos_index][column + pos_index].color != currentmove->slots[line][column].color)
 
                        currentmove->slots[line + pos_index][column + pos_index].flags |= FLAG_TAKEABLE;
 
                     break;
 
                  }
 
                  currentmove->slots[line + pos_index][column + pos_index].flags |= FLAG_POSSIBLEMOVE;
 
               }
 
 
 
               // see how far bishop can move SE
 
               for (pos_index = 1; pos_index < 8; pos_index++)
 
               {
 
                  if ((line - pos_index < 0) || (column + pos_index > 7))
 
                     break;
 
                  if (currentmove->slots[line - pos_index][column + pos_index].part != PART_NONE)
 
                  {
 
                     if (currentmove->slots[line - pos_index][column + pos_index].color != currentmove->slots[line][column].color)
 
                        currentmove->slots[line - pos_index][column + pos_index].flags |= FLAG_TAKEABLE;
 
                     break;
 
                  }
 
                  currentmove->slots[line - pos_index][column + pos_index].flags |= FLAG_POSSIBLEMOVE;
 
               }
 
 
 
               // see how far bishop can move NW
 
               for (pos_index = 1; pos_index < 8; pos_index++)
 
               {
 
                  if ((line + pos_index > 7) || (column - pos_index < 0))
 
                     break;
 
                  if (currentmove->slots[line + pos_index][column - pos_index].part != PART_NONE)
 
                  {
 
                     if (currentmove->slots[line + pos_index][column - pos_index].color != currentmove->slots[line][column].color)
 
                        currentmove->slots[line + pos_index][column - pos_index].flags |= FLAG_TAKEABLE;
 
                     break;
 
                  }
 
                  currentmove->slots[line + pos_index][column - pos_index].flags |= FLAG_POSSIBLEMOVE;
 
               }
 
 
 
               // see how far bishop can move SW
 
               for (pos_index = 1; pos_index < 8; pos_index++)
 
               {
 
                  if ((line - pos_index < 0) || (column - pos_index < 0))
 
                     break;
 
                  if (currentmove->slots[line - pos_index][column - pos_index].part != PART_NONE)
 
                  {
 
                     if (currentmove->slots[line - pos_index][column - pos_index].color != currentmove->slots[line][column].color)
 
                        currentmove->slots[line - pos_index][column - pos_index].flags |= FLAG_TAKEABLE;
 
                     break;
 
                  }
 
                  currentmove->slots[line - pos_index][column - pos_index].flags |= FLAG_POSSIBLEMOVE;
 
               }
 
            }
 
 
 
            //////////////////////////////////////////////////////////////////////////////////////////
 
            ///////////////////////////////////////// QUEEN //////////////////////////////////////////
 
            //////////////////////////////////////////////////////////////////////////////////////////
 
            // else is it a queen ?
 
            else if (boardslot->part == PART_QUEEN)
 
            {
 
               // see how far queen can move NE
 
               for (pos_index = 1; pos_index < 8; pos_index++)
 
               {
 
                  if ((line + pos_index > 7) || (column + pos_index > 7))
 
                     break;
 
                  if (currentmove->slots[line + pos_index][column + pos_index].part != PART_NONE)
 
                  {
 
                     if (currentmove->slots[line + pos_index][column + pos_index].color != currentmove->slots[line][column].color)
 
                        currentmove->slots[line + pos_index][column + pos_index].flags |= FLAG_TAKEABLE;
 
                     break;
 
                  }
 
                  currentmove->slots[line + pos_index][column + pos_index].flags |= FLAG_POSSIBLEMOVE;
 
               }
 
 
 
               // see how far queen can move SE
 
               for (pos_index = 1; pos_index < 8; pos_index++)
 
               {
 
                  if ((line - pos_index < 0) || (column + pos_index > 7))
 
                     break;
 
                  if (currentmove->slots[line - pos_index][column + pos_index].part != PART_NONE)
 
                  {
 
                     if (currentmove->slots[line - pos_index][column + pos_index].color != currentmove->slots[line][column].color)
 
                        currentmove->slots[line - pos_index][column + pos_index].flags |= FLAG_TAKEABLE;
 
                     break;
 
                  }
 
                  currentmove->slots[line - pos_index][column + pos_index].flags |= FLAG_POSSIBLEMOVE;
 
               }
 
 
 
               // see how far queen can move NW
 
               for (pos_index = 1; pos_index < 8; pos_index++)
 
               {
 
                  if ((line + pos_index > 7) || (column - pos_index < 0))
 
                     break;
 
                  if (currentmove->slots[line + pos_index][column - pos_index].part != PART_NONE)
 
                  {
 
                     if (currentmove->slots[line + pos_index][column - pos_index].color != currentmove->slots[line][column].color)
 
                        currentmove->slots[line + pos_index][column - pos_index].flags |= FLAG_TAKEABLE;
 
                     break;
 
                  }
 
                  currentmove->slots[line + pos_index][column - pos_index].flags |= FLAG_POSSIBLEMOVE;
 
               }
 
 
 
               // see how far queen can move SW
 
               for (pos_index = 1; pos_index < 8; pos_index++)
 
               {
 
                  if ((line - pos_index < 0) || (column - pos_index < 0))
 
                     break;
 
                  if (currentmove->slots[line - pos_index][column - pos_index].part != PART_NONE)
 
                  {
 
                     if (currentmove->slots[line - pos_index][column - pos_index].color != currentmove->slots[line][column].color)
 
                        currentmove->slots[line - pos_index][column - pos_index].flags |= FLAG_TAKEABLE;
 
                     break;
 
                  }
 
                  currentmove->slots[line - pos_index][column - pos_index].flags |= FLAG_POSSIBLEMOVE;
 
               }
 
 
 
               // see how far queen can move upwards
 
               for (pos_index = line + 1; pos_index < 8; pos_index++)
 
               {
 
                  if (currentmove->slots[pos_index][column].part != PART_NONE)
 
                  {
 
                     if (currentmove->slots[pos_index][column].color != currentmove->slots[line][column].color)
 
                        currentmove->slots[pos_index][column].flags |= FLAG_TAKEABLE;
 
                     break;
 
                  }
 
                  currentmove->slots[pos_index][column].flags |= FLAG_POSSIBLEMOVE;
 
               }
 
 
 
               // see how far queen can move downwards
 
               for (pos_index = line - 1; pos_index >= 0; pos_index--)
 
               {
 
                  if (currentmove->slots[pos_index][column].part != PART_NONE)
 
                  {
 
                     if (currentmove->slots[pos_index][column].color != currentmove->slots[line][column].color)
 
                        currentmove->slots[pos_index][column].flags |= FLAG_TAKEABLE;
 
                     break;
 
                  }
 
                  currentmove->slots[pos_index][column].flags |= FLAG_POSSIBLEMOVE;
 
               }
 
 
 
               // see how far queen can move left
 
               for (pos_index = column - 1; pos_index >= 0; pos_index--)
 
               {
 
                  if (currentmove->slots[line][pos_index].part != PART_NONE)
 
                  {
 
                     if (currentmove->slots[line][pos_index].color != currentmove->slots[line][column].color)
 
                        currentmove->slots[line][pos_index].flags |= FLAG_TAKEABLE;
 
                     break;
 
                  }
 
                  currentmove->slots[line][pos_index].flags |= FLAG_POSSIBLEMOVE;
 
               }
 
 
 
               // see how far queen can move right
 
               for (pos_index = column + 1; pos_index < 8; pos_index++)
 
               {
 
                  if (currentmove->slots[line][pos_index].part != PART_NONE)
 
                  {
 
                     if (currentmove->slots[line][pos_index].color != currentmove->slots[line][column].color)
 
                        currentmove->slots[line][pos_index].flags |= FLAG_TAKEABLE;
 
                     break;
 
                  }
 
                  currentmove->slots[line][pos_index].flags |= FLAG_POSSIBLEMOVE;
 
               }
 
            }
 
 
 
            //////////////////////////////////////////////////////////////////////////////////////////
 
            ////////////////////////////////////////// KING //////////////////////////////////////////
 
            //////////////////////////////////////////////////////////////////////////////////////////
 
            // else is it a king ?
 
            else if (boardslot->part == PART_KING)
 
            {
 
               // see if king can move NE
 
               if ((line < 7) && (column < 7))
 
               {
 
                  if (currentmove->slots[line + 1][column + 1].part == PART_NONE)
 
                     currentmove->slots[line + 1][column + 1].flags |= FLAG_POSSIBLEMOVE;
 
                  else if (currentmove->slots[line + 1][column + 1].color != currentmove->slots[line][column].color)
 
                     currentmove->slots[line + 1][column + 1].flags |= FLAG_TAKEABLE;
 
               }
 
 
 
               // see if king can move SE
 
               if ((line > 0) && (column < 7))
 
               {
 
                  if (currentmove->slots[line - 1][column + 1].part == PART_NONE)
 
                     currentmove->slots[line - 1][column + 1].flags |= FLAG_POSSIBLEMOVE;
 
                  else if (currentmove->slots[line - 1][column + 1].color != currentmove->slots[line][column].color)
 
                     currentmove->slots[line - 1][column + 1].flags |= FLAG_TAKEABLE;
 
               }
 
 
 
               // see if king can move NW
 
               if ((line < 7) && (column > 0))
 
               {
 
                  if (currentmove->slots[line + 1][column - 1].part == PART_NONE)
 
                     currentmove->slots[line + 1][column - 1].flags |= FLAG_POSSIBLEMOVE;
 
                  else if (currentmove->slots[line + 1][column - 1].color != currentmove->slots[line][column].color)
 
                     currentmove->slots[line + 1][column - 1].flags |= FLAG_TAKEABLE;
 
               }
 
 
 
               // see if king can move SW
 
               if ((line > 0) && (column > 0))
 
               {
 
                  if (currentmove->slots[line - 1][column - 1].part == PART_NONE)
 
                     currentmove->slots[line - 1][column - 1].flags |= FLAG_POSSIBLEMOVE;
 
                  else if (currentmove->slots[line - 1][column - 1].color != currentmove->slots[line][column].color)
 
                     currentmove->slots[line - 1][column - 1].flags |= FLAG_TAKEABLE;
 
               }
 
 
 
               // see if king can move upwards
 
               if (line < 7)
 
               {
 
                  if (currentmove->slots[line + 1][column].part == PART_NONE)
 
                     currentmove->slots[line + 1][column].flags |= FLAG_POSSIBLEMOVE;
 
                  else if (currentmove->slots[line + 1][column].color != currentmove->slots[line][column].color)
 
                     currentmove->slots[line + 1][column].flags |= FLAG_TAKEABLE;
 
               }
 
 
 
               // see if king can move downwards
 
               if (line > 0)
 
               {
 
                  if (currentmove->slots[line - 1][column].part == PART_NONE)
 
                     currentmove->slots[line - 1][column].flags |= FLAG_POSSIBLEMOVE;
 
                  else if (currentmove->slots[line - 1][column].color != currentmove->slots[line][column].color)
 
                     currentmove->slots[line - 1][column].flags |= FLAG_TAKEABLE;
 
               }
 
 
 
               // see if king can move right
 
               if (column < 7)
 
               {
 
                  if (currentmove->slots[line][column + 1].part == PART_NONE)
 
                     currentmove->slots[line][column + 1].flags |= FLAG_POSSIBLEMOVE;
 
                  else if (currentmove->slots[line][column + 1].color != currentmove->slots[line][column].color)
 
                     currentmove->slots[line][column + 1].flags |= FLAG_TAKEABLE;
 
               }
 
 
 
               // see if king can move left
 
               if (column > 0)
 
               {
 
                  if (currentmove->slots[line][column - 1].part == PART_NONE)
 
                     currentmove->slots[line][column - 1].flags |= FLAG_POSSIBLEMOVE;
 
                  else if (currentmove->slots[line][column - 1].color != currentmove->slots[line][column].color)
 
                     currentmove->slots[line][column - 1].flags |= FLAG_TAKEABLE;
 
               }
 
 
 
               // can king castle bishopside ?
 
               if (currentmove->sides[boardslot->color].shortcastle_allowed // no parts have moved yet
 
                   && (currentmove->slots[line][5].part == PART_NONE) // no other part...
 
                   && (currentmove->slots[line][6].part == PART_NONE) // ...in the way
 
                   && !Move_IsCheck (currentmove, boardslot->color) // king not currently in check
 
                   && !Move_IsKingThreatenedAtLocation (currentmove, boardslot->color, line, 5, &threat_line, &threat_column)) // king's way safe
 
                  currentmove->slots[line][column + 2].flags |= FLAG_POSSIBLEMOVE; // allow castling bishopside
 
 
 
               // can king castle queenside ?
 
               if (currentmove->sides[boardslot->color].longcastle_allowed // no parts have moved yet
 
                   && (currentmove->slots[line][3].part == PART_NONE) // no other part...
 
                   && (currentmove->slots[line][2].part == PART_NONE) // ...is...
 
                   && (currentmove->slots[line][1].part == PART_NONE) // ...in the way
 
                   && !Move_IsCheck (currentmove, boardslot->color) // king not currently in check
 
                   && !Move_IsKingThreatenedAtLocation (currentmove, boardslot->color, line, 3, &threat_line, &threat_column)) // king's way safe
 
                  currentmove->slots[line][column - 2].flags |= FLAG_POSSIBLEMOVE; // allow castling queenside
 
            }
 
         }
 
   } // end if (play mode)
 
 
 
   ////////////////////////////////////////////////////////////////////////////////////////////////
 
   // now place the parts that are off-board (taken parts)
 
 
 
   // but only if we want them displayed
 
   if (options.want_takenparts)
 
   {
 
      // draw the white player's taken parts, place the part off the board (watch out for the hack...)
 
      for (part_index = 0; part_index < currentmove->sides[COLOR_WHITE].takenpart_count; part_index++)
 
      {
 
         takenpart_type = currentmove->sides[COLOR_WHITE].takenparts[part_index];
 
 
 
         // do we want the 3D models or the flat icons ?
 
         // we do when EITHER we don't want flat icons OR the current player is looking downstraight
 
         if (!want_flaticons || (current_pitch < MAX_PITCH_FOR_FLAT_ICONS))
 
            Scene_AddPart (scene, takenpart_type, COLOR_BLACK, (part_index < MAX_STACKABLE_PARTS ? 23.2f : 26.8f),
 
                           (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),
 
                           0.04f, (takenpart_type == PART_PAWN ? -90.0f : 100.0f), 0.0f); // rotate pawns 90°
 
         else
 
            Scene_AddTile (scene, theme->flattextures[COLOR_BLACK][takenpart_type], 1.25f, (part_index < MAX_STACKABLE_PARTS ? 23.2f : 26.8f),
 
                           20.0f - (part_index < MAX_STACKABLE_PARTS ? part_index : (part_index - MAX_STACKABLE_PARTS)) * 2.5f, 0.07f, flaticons_yaw);
 
      }
 
 
 
      // now draw the black player's taken parts, place the part off the board (watch out for the hack...)
 
      for (part_index = 0; part_index < currentmove->sides[COLOR_BLACK].takenpart_count; part_index++)
 
      {
 
         takenpart_type = currentmove->sides[COLOR_BLACK].takenparts[part_index];
 
 
 
         // do we want the 3D models or the flat icons ?
 
         // we do when EITHER we don't want flat icons OR the current player is looking downstraight
 
         if (!want_flaticons || (current_pitch < MAX_PITCH_FOR_FLAT_ICONS))
 
            Scene_AddPart (scene, takenpart_type, COLOR_WHITE, (part_index < MAX_STACKABLE_PARTS ? -23.2f : -26.8f),
 
                           (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),
 
                           0.04f, (takenpart_type == PART_PAWN ? -90.0f : 100.0f), 0.0f); // rotate pawns 90°
 
         else
 
            Scene_AddTile (scene, theme->flattextures[COLOR_WHITE][takenpart_type], 1.25f, (part_index < MAX_STACKABLE_PARTS ? -23.2f : -26.8f),
 
                           -20.0f + (part_index < MAX_STACKABLE_PARTS ? part_index : (part_index - MAX_STACKABLE_PARTS)) * 2.5f, 0.07f, flaticons_yaw);
 
      }
 
   }
 
 
 
   ////////////////////////////////////////////////////////////////////////////////////////////////
 
   // now draw the textured slots
 
 
 
   // cycle through all the board slots...
 
   for (line = 0; line < 8; line++)
 
      for (column = 0; column < 8; column++)
 
      {
 
         if (currentmove->slots[line][column].flags == FLAG_NONE)
 
            continue; // skip everything that doesn't need to be drawn
 
 
 
         // draw the texture we want. Only one texture is allowed, so PRIORITY MATTERS.
 
         if (currentmove->slots[line][column].flags & FLAG_SELECTED)
 
            Scene_AddTile (scene, theme->selected_textureindex, 2.5f, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.02f, 0.0f);
 
         else if (options.want_threats && currentmove->slots[line][column].flags & FLAG_CHECK)
 
            Scene_AddTile (scene, theme->check_textureindex, 2.5f, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.02f, 0.0f);
 
         else if (options.want_possiblemoves && currentmove->slots[line][column].flags & FLAG_POSSIBLEMOVE)
 
            Scene_AddTile (scene, theme->possiblemove_textureindex, 2.5f, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.02f, 0.0f);
 
         else if (options.want_possiblemoves && currentmove->slots[line][column].flags & FLAG_TAKEABLE)
 
            Scene_AddTile (scene, theme->takeable_textureindex, 2.5f, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.02f, 0.0f);
 
         else if (options.want_threats && currentmove->slots[line][column].flags & FLAG_THREAT)
 
            Scene_AddTile (scene, theme->threat_textureindex, 2.5f, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.02f, 0.0f);
 
      }
 
 
 
   // is it time to draw the hovered slot and last move textures on the board slots ?
 
   if (animation_endtime < current_time)
 
   {
 
      // add the hovered slot tile (only when it should not be hidden because of highlighting)
 
      if (!is_paused && IS_VALID_POSITION (board->hovered_position)
 
          && ((highlight_endtime < current_time) || ((int) ((highlight_endtime - current_time) * 20.0f) % 2 == 1)))
 
         Scene_AddTile (scene, theme->hovered_textureindex, 3.1f,
 
                        17.5f - (7 - board->hovered_position[1]) * 5.0f,
 
                        17.5f - board->hovered_position[0] * 5.0f,
 
                        0.03f, 0.0f); // hovered tile
 
 
 
      // and the previous move source and target
 
      if (options.want_lastmove && (board->move_count > 1) && (board->viewed_move > 0)
 
          && IS_VALID_POSITION (board->moves[board->viewed_move].source)
 
          && IS_VALID_POSITION (board->moves[board->viewed_move].target))
 
      {
 
         Scene_AddTile (scene, theme->lastmovesource_textureindex, 2.5f,
 
                        17.5f - (7 - board->moves[board->viewed_move].source[1]) * 5.0f,
 
                        17.5f - board->moves[board->viewed_move].source[0] * 5.0f,
 
                        0.04f, 0.0f); // previous move source
 
         Scene_AddTile (scene, theme->lastmovetarget_textureindex, 2.5f,
 
                        17.5f - (7 - board->moves[board->viewed_move].target[1]) * 5.0f,
 
                        17.5f - board->moves[board->viewed_move].target[0] * 5.0f,
 
                        0.04f, 0.0f); // previous move target
 
      }
 
   }
 
 
 
   ////////////////////////////////////////////////////////////////////////////////////////////////
 
   // now draw the 3D parts that are still in play
 
 
 
   // cycle through all the grid and place all static parts
 
   for (line = 0; line < 8; line++)
 
   {
 
      for (column = 0; column < 8; column++)
 
      {
 
         boardslot = ¤tmove->slots[line][column]; // quick access to grid slot
 
 
 
         // is there nothing on this grid slot ?
 
         if (boardslot->part == PART_NONE)
 
            continue; // then don't draw anything on it
 
 
 
         // is this part not animated ? i.e.:
 
         // has no movement happened yet
 
         // OR does the movement NOT land on this slot
 
         //    AND is it NOT the tower concerned by a castle ?
 
         if ((board->viewed_move == 0)
 
             || (((line != currentmove->target[0]) || (column != currentmove->target[1]))
 
                 && ((towupper (currentmove->pgntext[0]) != L'O')
 
                     || (line != (currentmove->color == COLOR_WHITE ? 0 : 7)) || (column != (currentmove->target[1] == 2 ? 3 : 5)))))
 
         {
 
            // do we want the 3D models or the flat icons ?
 
            // we do when EITHER we don't want flat icons OR the player playing the current move is looking downstraight
 
            if (!want_flaticons || (current_pitch < MAX_PITCH_FOR_FLAT_ICONS))
 
               Scene_AddPart (scene, boardslot->part, boardslot->color, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.04f, 0.0f, 0.0f);
 
            else
 
               Scene_AddTile (scene, theme->flattextures[boardslot->color][boardslot->part], 2.5f, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.05f, flaticons_yaw);
 
         }
 
      } // end for (column = 0; column < 8; column++)
 
   } // end for (line = 0; line < 8; line++)
 
 
 
   // now do another pass and draw the part(s) that are currently moving
 
   for (line = 0; line < 8; line++)
 
   {
 
      for (column = 0; column < 8; column++)
 
      {
 
         boardslot = ¤tmove->slots[line][column]; // quick access to grid slot
 
 
 
         // is there nothing on this grid slot ?
 
         if (boardslot->part == PART_NONE)
 
            continue; // then don't draw anything on it
 
 
 
         // has a movement happened yet AND does it land on this slot ?
 
         if ((board->viewed_move > 0)
 
             && (line == currentmove->target[0]) && (column == currentmove->target[1]))
 
         {
 
            // do we want animations AND is it still time to play the animation ?
 
            if (options.want_animations && (animation_endtime > current_time))
 
            {
 
               // get the source and target X and Y positions
 
               source_x = 17.5f - (7 - currentmove->source[1]) * 5.0f;
 
               source_y = 17.5f - currentmove->source[0] * 5.0f;
 
               target_x = 17.5f - (7 - currentmove->target[1]) * 5.0f;
 
               target_y = 17.5f - currentmove->target[0] * 5.0f;
 
 
 
               // compute the movement completion ratio between 0 and 1
 
               movement_ratio = 1.0f - (animation_endtime - current_time) / ANIMATION_DURATION;
 
 
 
               // compute the current X an Y based on movement timing
 
               current_x = source_x + (target_x - source_x) * movement_ratio;
 
               current_y = source_y + (target_y - source_y) * movement_ratio;
 
 
 
               // do we want the 3D models or the flat icons ?
 
               // we do when EITHER we don't want flat icons OR the player playing the current move is looking downstraight
 
               if (!want_flaticons || (current_pitch < MAX_PITCH_FOR_FLAT_ICONS))
 
               {
 
                  // height is a sine positive, max height is proportional to travelled distance
 
                  movement_diffco = abs (currentmove->target[1] - currentmove->source[1]);
 
                  movement_diffli = abs (currentmove->target[0] - currentmove->source[0]);
 
                  movement_maxheight = 0.5f + (float) movement_diffco + (float) movement_diffli;
 
                  if (currentmove->part == PART_KNIGHT)
 
                     movement_maxheight *= 2.0f; // knights jump high
 
                  else if ((currentmove->part == PART_KING) && (movement_diffco == 2))
 
                     movement_maxheight *= 5.0f; // kings jump VERY high when castling too
 
                  else if (movement_maxheight > 5.0f)
 
                     movement_maxheight = 5.0f; // all other parts just hover above the ground
 
                  current_z = 0.04f + (float) sin (MATH_PI * movement_ratio) * movement_maxheight;
 
 
 
                  // make this part move realistically
 
                  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));
 
               }
 
               else
 
                  Scene_AddTile (scene, theme->flattextures[boardslot->color][boardslot->part], 2.5f, current_x, current_y, 0.07f, flaticons_yaw);
 
            }
 
            else
 
            {
 
               // do we want the 3D models or the flat icons ?
 
               // we do when EITHER we don't want flat icons OR the current player is looking downstraight
 
               if (!want_flaticons || (current_pitch < MAX_PITCH_FOR_FLAT_ICONS))
 
                  Scene_AddPart (scene, boardslot->part, boardslot->color, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.04f, 0.0f, 0.0f);
 
               else
 
                  Scene_AddTile (scene, theme->flattextures[boardslot->color][boardslot->part], 2.5f, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.05f, flaticons_yaw);
 
            }
 
 
 
            // is it time to play a move sound ?
 
            if ((sound_playtime != 0) && (sound_playtime < current_time))
 
            {
 
               // is the current player in checkmate, in check, in stalemate or is it a capture ? (to play the right sound)
 
               // read as: was the last move an opponent's move AND did it put us to check ?
 
               if (currentmove->is_check)
 
               {
 
                  Audio_PlaySound (SOUNDTYPE_MOVE, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.04f); // play the normal move sound
 
 
 
                  // is it a checkmate or a normal check ? (checkmate == check + stalemate)
 
                  if (currentmove->is_stalemate)
 
                     Audio_PlaySound (board->players[currentmove->color].type == PLAYER_HUMAN ? SOUNDTYPE_VICTORY : SOUNDTYPE_DEFEAT, 0.0f, 0.0f, 0.04f); // if so, play endgame sound at the center of the board
 
                  else
 
                     Audio_PlaySound (SOUNDTYPE_CHECK, 0.0f, 0.0f, 0.04f); // else play the check sound at the center of the board
 
               }
 
               else if (currentmove->is_stalemate)
 
               {
 
                  Audio_PlaySound (SOUNDTYPE_MOVE, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.04f); // play the normal move sound
 
                  Audio_PlaySound (SOUNDTYPE_DEFEAT, 0.0f, 0.0f, 0.04f); // on stalemate, play defeat sound at the center of the board
 
               }
 
               else if (currentmove->has_captured)
 
                  Audio_PlaySound (SOUNDTYPE_PIECETAKEN, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.04f); // on capture, play the capture sound
 
               else
 
                  Audio_PlaySound (SOUNDTYPE_MOVE, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.04f); // play the normal move sound
 
 
 
               sound_playtime = 0; // mark this animation as completed and sound played
 
            }
 
         }
 
 
 
         // else has a movement happened yet AND is this movement a castle AND is this the concerned tower ?
 
         else if ((board->viewed_move > 0)
 
                  && (towupper (currentmove->pgntext[0]) == L'O') // either O-O-O or O-O
 
                  && (line == (currentmove->color == COLOR_WHITE ? 0 : 7)) && (column == (currentmove->target[1] == 2 ? 3 : 5)))
 
         {
 
            // do we want animations AND is it still time to play the animation ? (castling rooks move faster)
 
            if (options.want_animations && (animation_endtime - (0.5f * ANIMATION_DURATION) > current_time))
 
            {
 
               // get the source and target X and Y positions
 
               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
 
               source_y = 17.5f - line * 5.0f;
 
               target_x = 17.5f - (7 - column) * 5.0f;
 
               target_y = 17.5f - line * 5.0f;
 
 
 
               // compute the movement completion ratio between 0 and 1 (castling rooks move faster)
 
               movement_ratio = min (1.0f, 1.0f - (animation_endtime - (0.5f * ANIMATION_DURATION) - current_time) / (0.5f * ANIMATION_DURATION));
 
 
 
               // compute the current X an Y based on movement timing
 
               current_x = source_x + (target_x - source_x) * movement_ratio;
 
               current_y = source_y + (target_y - source_y) * movement_ratio;
 
 
 
               // height is a sine positive, max height is proportional to travelled distance
 
               movement_maxheight = 1.0f; // castling rook will barely hover above the ground
 
               current_z = 0.04f + (float) sin (MATH_PI * movement_ratio) * movement_maxheight;
 
 
 
               // make this part move realistically - do we want the 3D models or the flat icons ?
 
               // we do when EITHER we don't want flat icons OR the player playing the current move is looking downstraight
 
               if (!want_flaticons || (current_pitch < MAX_PITCH_FOR_FLAT_ICONS))
 
                  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));
 
               else
 
                  Scene_AddTile (scene, theme->flattextures[boardslot->color][boardslot->part], 2.5f, current_x, current_y, 0.09f, flaticons_yaw);
 
 
 
               if (movement_ratio < 0.9f)
 
                  rooksound_played = false; // if the rook is still moving, reset the "sound played" flag
 
               else if (!rooksound_played)
 
               {
 
                  Audio_PlaySound (SOUNDTYPE_MOVE, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.04f); // when the rook has landed, play a move sound
 
                  rooksound_played = true; // remember this is no longer to be done
 
               }
 
            }
 
            else
 
            {
 
               // do we want the 3D models or the flat icons ?
 
               // we do when EITHER we don't want flat icons OR the current player is looking downstraight
 
               if (!want_flaticons || (current_pitch < MAX_PITCH_FOR_FLAT_ICONS))
 
                  Scene_AddPart (scene, boardslot->part, boardslot->color, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.04f, 0.0f, 0.0f);
 
               else
 
                  Scene_AddTile (scene, theme->flattextures[boardslot->color][boardslot->part], 2.5f, 17.5f - (7 - column) * 5.0f, 17.5f - line * 5.0f, 0.05f, flaticons_yaw);
 
            }
 
         }
 
      } // end for (column = 0; column < 8; column++)
 
   } // end for (line = 0; line < 8; line++)
 
 
 
   ////////////////////////////////////////////////////////////////////////////////////
 
   // now draw the sepia overlay if we're in history view mode or if the game is paused
 
 
 
   if (options.want_sepiafilter && (is_paused || ((board->move_count > 1) && (board->viewed_move != board->move_count - 1))))
 
      scene->overlay_spriteindex = sepia_spriteindex; // use the sepia filter
 
   else
 
      scene->overlay_spriteindex = -1; // else use natural colors
 
 
 
   ////////////////////////////////////////////////////////////////////////////////////////////////
 
   // now draw the move comment text
 
 
 
   // 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
 
   if ((currentmove->comment != NULL) && (currentmove->comment[0] != 0))
 
      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);
 
   else if ((network_player != NULL) && network_player->is_logged_in && !network_player->is_in_game)
 
      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);
 
   else if (RGBACOLOR_ALPHA (scene->gui.comment_text.color) >= 128) // HACK: don't clear if a dimmed hint text is already here
 
      scene->gui.comment_text.is_displayed = false; // else clear comment text
 
 
 
   ////////////////////////////////////////////////////////////////////////////////////////////////
 
   // now draw the game clock
 
 
 
   // do we want to print the game clock ?
 
   if (options.want_clock && (board->move_count > 1) && (board->game_state == STATE_PLAYING))
 
   {
 
      network_player = Player_FindByType (PLAYER_INTERNET); // quick access to network player
 
 
 
      // are we in Internet play ? if so, count time down, else count it up
 
      if ((network_player != NULL) && network_player->is_in_game)
 
         seconds = Player_GetCurrent ()->remaining_seconds - (int) (current_time - board->lastmove_time); // total seconds first
 
      else
 
         seconds = (int) (current_time - stoppage_time - board->lastmove_time); // total seconds first, take pauses in account
 
 
 
      minutes = seconds / 60; // minutes
 
      seconds -= 60 * minutes; // remaining seconds
 
 
 
      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
 
   }
 
   else
 
      scene->gui.clock_text.is_displayed = false;
 
 
 
   ////////////////////////////////////////////////////////////////////////////////////////////////
 
   // now draw the turn text
 
 
 
   // do we want to print the player's turn AND has the game not ended yet ? if so, copy it, else leave it clear
 
   if (options.want_turn && (board->game_state == STATE_PLAYING))
 
   {
 
      if (Board_ColorToMove (board) == COLOR_BLACK)
 
      {
 
         if (the_scene.gui.turn_text.color != 0x000000C0)
 
            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"));
 
      }
 
      else
 
      {
 
         if (the_scene.gui.turn_text.color != 0xFFFFFF80)
 
            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"));
 
      }
 
   }
 
   else
 
      scene->gui.turn_text.is_displayed = false;
 
 
 
   ////////////////////////////////////////////////////////////////////////////////////////////////
 
   // now draw the game history text
 
 
 
   // do we want to display the game history ? if so, display the game history text in PGN
 
   if (options.want_history && (board->move_count > 1))
 
   {
 
      // first off, see where we start (for long games, we don't want to display EVERYTHING...
 
      if (board->viewed_move > 30)
 
         start_index = board->viewed_move - 30; // display 30 moves maximum
 
      else
 
         start_index = 1;
 
 
 
      // allocate an arbitrary length history text string buffer (assume each move can't be longer than 15 characters)
 
      historytext_size = 15 * (1 + (board->viewed_move + 1) - start_index);
 
      history_text = (wchar_t *) SAFE_malloc (historytext_size, sizeof (wchar_t), false);
 
      history_text[0] = 0; // and reset it
 
 
 
      // now for each move we want to display...
 
      for (move_index = start_index; move_index <= board->viewed_move; move_index++)
 
      {
 
         length = wcslen (history_text); // get current text length
 
 
 
         // every move pair, append move pair number
 
         if (move_index % 2 == 1)
 
            swprintf_s (&history_text[length], historytext_size - length, L"%d.   ", 1 + move_index / 2);
 
 
 
         // append move text
 
         wcscat_s (history_text, historytext_size, board->moves[move_index].pgntext);
 
         wcscat_s (history_text, historytext_size, L"   ");
 
 
 
         // every odd move, drop a newline
 
         if (move_index % 2 == 0)
 
            wcscat_s (history_text, historytext_size, L"\n");
 
      }
 
 
 
      // add 50% alpha to game history color and transmit it to 3D engine
 
      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);
 
      SAFE_free ((void **) &history_text);
 
   }
 
   else
 
      scene->gui.history_text.is_displayed = false; // if we don't need to display the game history, free the buffer
 
 
 
   ////////////////////////////////////////////////////////////////////////////////////////////////
 
   // now draw the chat area
 
 
 
   // is a valid chatter channel selected ?
 
   if ((selected_chatterchannel != NULL) && (chatterchannel_count > 0) && ((local_player = Player_FindByType (PLAYER_HUMAN)) != NULL))
 
   {
 
      // set the chatter's name
 
      wcscpy_s (scene->gui.entered_ccreply.nickname, WCHAR_SIZEOF (scene->gui.entered_ccreply.nickname), local_player->name);
 
 
 
      // correct or update the channel name and color
 
      if (selected_chatterchannel->theme[0] != 0)
 
         wcscpy_s (scene->gui.entered_ccreply.channelname, WCHAR_SIZEOF (scene->gui.entered_ccreply.channelname), selected_chatterchannel->theme);
 
      else
 
         swprintf_s (scene->gui.entered_ccreply.channelname, WCHAR_SIZEOF (scene->gui.entered_ccreply.channelname), L"%s %d", LOCALIZE (L"ChatterChannels_ColumnChannelNumber"), selected_chatterchannel->id);
 
      scene->gui.entered_ccreply.color = RGBACOLOR_FULLALPHA (selected_chatterchannel->color); // full bright for entering text
 
   }
 
 
 
   // display parts pick line in position setup mode only
 
   scene->gui.is_partspick_displayed = (board->game_state == STATE_SETUPPOSITION ? true : false);
 
 
 
   //////////////////////////
 
   // error and notifications
 
 
 
   // is the current player a computer AND are we playing a game right now
 
   // AND has the computer been thinking for more than 5 seconds AND is there no "thinking" text yet ?
 
   if ((board->players[Board_ColorToMove (board)].type == PLAYER_COMPUTER) && (board->game_state == STATE_PLAYING)
 
       && (board->lastmove_time + 5.0f < current_time) && !scene->gui.central_text.is_displayed)
 
   {
 
      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),
 
                     999999.0f, true, LOCALIZE (L"Thinking")); // if so, display the "thinking" phrase in the middle of the screen
 
      the_scene.gui.want_spinwheel = true; // start spinning wheel
 
   }
 
 
 
   // is there a network player AND is our socket gone AWOL ?
 
   else if ((network_player != NULL) && (network_player->our_socket == INVALID_SOCKET))
 
   {
 
      // is there nothing in the center of the screen yet ?
 
      if (!the_scene.gui.central_text.is_displayed)
 
         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),
 
                        999999.0f, true, LOCALIZE (L"Error_ConnectionToChessServerLost")); // display "error" in the middle of the screen
 
      the_scene.overlay_spriteindex = sepia_spriteindex; // display sepia filter if no connection
 
   }
 
 
 
   // is the game paused ?
 
   if (is_paused)
 
      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, 255),
 
                     1.0f, false, LOCALIZE (L"Paused")); // if so, display the "paused" phrase in the middle of the screen
 
 
 
   return; // finished, scene is updated
 
}
 
 
 
 
 
void Scene_AddCCReply (scene_t *scene, wchar_t *nickname, wchar_t *channelname, unsigned long color_rgbx, wchar_t *fmt, ...)
 
{
 
   // helper function to add a CC reply on display
 
 
 
   static wchar_t message[4096];
 
   ccreply_t re;
 
   va_list argptr;
 
 
 
   // concatenate all the arguments in one string
 
   va_start (argptr, fmt);
 
   _vsnwprintf_s (message, WCHAR_SIZEOF (message), _TRUNCATE, fmt, argptr);
 
   va_end (argptr);
 
 
 
   // now put the text in place
 
   memset (&re, 0, sizeof (re)); // reset the structure we're about to fill
 
 
 
   wcscpy_s (re.nickname, WCHAR_SIZEOF (re.nickname), nickname); // copy nickname
 
   wcscpy_s (re.channelname, WCHAR_SIZEOF (re.channelname), channelname); // copy channel name
 
 
 
   re.text_length = wcslen (message); // get text length
 
   re.text = (wchar_t *) SAFE_malloc (re.text_length + 1, sizeof (wchar_t), false); // allocate text space (include null terminator)
 
   wcscpy_s (re.text, re.text_length + 1, message); // get the text
 
 
 
   re.color = RGBACOLOR_SETALPHA (color_rgbx, 0xC0); // copy reply color and force a slightly transparent alpha
 
   re.arrival_time = current_time; // save CC reply arrival time
 
 
 
   // reallocate CC history array to hold now one reply more
 
   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);
 
   memcpy (&the_scene.gui.cchistory[the_scene.gui.cchistory_count], &re, sizeof (ccreply_t));
 
   the_scene.gui.cchistory_count++; // CC history holds now one reply more
 
 
 
   return; // finished, announcement text is set
 
}
 
 
 
 
 
void Scene_AddAnnouncement (scene_t *scene, wchar_t *fmt, ...)
 
{
 
   // helper function to set the announcement (red) text on display
 
 
 
   static wchar_t message[4096];
 
   ccreply_t re;
 
   va_list argptr;
 
 
 
   // concatenate all the arguments in one string
 
   va_start (argptr, fmt);
 
   _vsnwprintf_s (message, WCHAR_SIZEOF (message), _TRUNCATE, fmt, argptr);
 
   va_end (argptr);
 
 
 
   // now put the text in place
 
   memset (&re, 0, sizeof (re)); // reset the structure we're about to fill
 
 
 
   re.text_length = wcslen (message); // get text length
 
   re.text = (wchar_t *) SAFE_malloc (re.text_length + 1, sizeof (wchar_t), false); // allocate text space (include null terminator)
 
   wcscpy_s (re.text, re.text_length + 1, message); // get the text
 
 
 
   wcscpy_s (re.channelname, WCHAR_SIZEOF (re.channelname), LOCALIZE (L"ImportantMessage"));
 
   re.color = RGBA_TO_RGBACOLOR (192, 0, 0, 0xE0); // fair red, a bit more opaque than normal messages
 
   re.arrival_time = current_time; // save announcement arrival time
 
 
 
   // reallocate CC history array to hold now one reply more
 
   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);
 
   memcpy (&the_scene.gui.cchistory[the_scene.gui.cchistory_count], &re, sizeof (ccreply_t));
 
   the_scene.gui.cchistory_count++; // CC history holds now one reply more
 
 
 
   return; // finished, announcement text is set
 
}
 
 
 
 
 
void Scene_SetButton (guibutton_t *button, float left, float top, float width, float height, int sprite_index, wchar_t *fmt, ...)
 
{
 
   // helper function to set up a GUI button
 
 
 
   va_list argptr;
 
 
 
   button->left = left;
 
   button->top = top;
 
   button->width = width;
 
   button->height = height;
 
   button->sprite_index = sprite_index;
 
 
 
   // concatenate all the arguments in one string
 
   if (fmt != NULL)
 
   {
 
      va_start (argptr, fmt);
 
      _vsnwprintf_s (button->text, WCHAR_SIZEOF (button->text), _TRUNCATE, fmt, argptr);
 
      va_end (argptr);
 
   }
 
   else
 
      button->text[0] = 0; // (unless no text is specified for this button)
 
 
 
   return; // finished, button is set
 
}
 
 
 
 
 
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, ...)
 
{
 
   // helper function to set some text on display
 
 
 
   static wchar_t message[4096];
 
   va_list argptr;
 
   int length;
 
 
 
   // concatenate all the arguments in one string
 
   va_start (argptr, fmt);
 
   _vsnwprintf_s (message, WCHAR_SIZEOF (message), _TRUNCATE, fmt, argptr);
 
   va_end (argptr);
 
 
 
   text->xpos_percent = xpos_percent; // save text's X position, in percents from left to right
 
   text->ypos_percent = ypos_percent; // save text's Y position, in percents from top to bottom
 
   text->maxwidth_percent = maxwidth_percent; // save text's max width before word wrapping
 
   text->horizontal_align = horizontal_align; // save text's horizontal alignment regarding the X position
 
   text->vertical_align = vertical_align; // save text's vertical alignment regarding the Y position
 
   text->text_align = text_align; // save text's horizontal alignment inside the bounding rectangle
 
   text->font_index = font_index; // save the index of the font with which to display this text
 
   text->color = color_rgba; // text's color, in RGBA
 
 
 
   // now put the text in place
 
   length = wcslen (message) + 1; // include null terminator
 
   text->buffer = (wchar_t *) SAFE_realloc (text->buffer, text->buffer_size, length, sizeof (wchar_t), false);
 
   wcscpy_s (text->buffer, length, message); // copy message text
 
   text->buffer_size = length; // and save buffer length
 
 
 
   text->appear_time = current_time; // save text arrival time
 
   text->disappear_time = current_time + duration; // make it last duration seconds
 
   text->want_fade = want_fade; // remember if text needs to be faded in and out
 
 
 
   // mark this text for display
 
   text->is_displayed = true;
 
 
 
   return; // finished, text is set
 
}
 
 
 
 
 
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)
 
{
 
   // helper function to add a specified part of the specified color to the rendered scene
 
 
 
   sceneobject_t *object;
 
   partcolor_t *partcolor;
 
 
 
   // reallocate space to hold one object more and blank it out
 
   scene->objects = (sceneobject_t *) SAFE_realloc (scene->objects, scene->object_count, scene->object_count + 1, sizeof (sceneobject_t), true);
 
 
 
   object = &scene->objects[scene->object_count]; // quick access to object
 
 
 
   object->mesh_index = theme->part_meshes[part_type]; // retrieve object mesh
 
   object->simpleshadow_size = simpleshadow_sizes[part_type]; // retrieve simple shadow size according to part type
 
   object->scale = 1.0f; // scale at 1 so far
 
 
 
   // set object texture and material
 
   partcolor = &theme->part_colors[part_color]; // quick access to part color struct
 
   object->texture_index = partcolor->texture;
 
   object->material_index = partcolor->material;
 
 
 
   // figure out object position on board
 
   object->x = pos_x;
 
   object->y = pos_y;
 
   object->z = pos_z;
 
 
 
   // turn a color's all parts' yaw 180 degrees so as both sides face each other
 
   if (part_color == COLOR_WHITE)
 
      object->yaw = 180.0f;
 
   else
 
      object->yaw = 0.0f;
 
 
 
   // and add the final turn pitch and yaw
 
   object->pitch = pitch;
 
   object->yaw = WrapAngle (object->yaw + turn_yaw);
 
 
 
   scene->object_count++; // array holds now one object more
 
   return; // finished
 
}
 
 
 
 
 
static void Scene_AddTile (scene_t *scene, int texture_index, float scale, float pos_x, float pos_y, float pos_z, float turn_yaw)
 
{
 
   // helper function to add a specified part of the specified color to the rendered scene
 
 
 
   sceneobject_t *object;
 
 
 
   // reallocate space to hold one object more and blank it out
 
   scene->objects = (sceneobject_t *) SAFE_realloc (scene->objects, scene->object_count, scene->object_count + 1, sizeof (sceneobject_t), true);
 
 
 
   object = &scene->objects[scene->object_count]; // quick access to object
 
 
 
   // save object data
 
   object->mesh_index = -1; // objects that have -1 as mesh index are tiles
 
   object->texture_index = texture_index;
 
   object->material_index = -1; // objects will use the default material
 
 
 
   // figure out object position on board
 
   object->x = pos_x;
 
   object->y = pos_y;
 
   object->z = pos_z;
 
   object->scale = scale;
 
 
 
   // turn tile as requested
 
   object->yaw = turn_yaw;
 
 
 
   scene->object_count++; // array holds now one object more
 
 
 
   return; // finished
 
}