Subversion Repositories Games.Chess Giants

Rev

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