Subversion Repositories Games.Chess Giants

Rev

Rev 24 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

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