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