480 lines
18 KiB
C++
480 lines
18 KiB
C++
#include "functions.hpp"
|
|
#include "custom_classes.hpp"
|
|
#include "global_vars.hpp"
|
|
#include "scenes.hpp"
|
|
#include <thread>
|
|
|
|
bool validPos( SDLPP::Scene &scene, std::shared_ptr< TetrisPiece > piece ) {
|
|
for ( auto &x : piece->getObjects() ) {
|
|
auto collisions = scene.getCollisions(
|
|
*x, { BRICK_ID, FLOOR_ID, BORDER_LEFT_ID, BORDER_RIGHT_ID } );
|
|
if ( collisions.size() > 1 )
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void updateShadow( SDLPP::Scene &scene ) {
|
|
if ( !g_cur_object ) {
|
|
g_cur_shadow->destroy();
|
|
g_cur_shadow.reset();
|
|
return;
|
|
}
|
|
if ( !g_cur_shadow )
|
|
return;
|
|
g_cur_shadow->setPos( g_cur_object->getPos() );
|
|
double shadow_drop = BOTTOM_BORDER;
|
|
auto &invalid_objects = g_cur_object->getObjects();
|
|
for ( auto &x : g_cur_shadow->getObjects() ) {
|
|
auto block_pos = x->getPos();
|
|
if ( BOTTOM_BORDER - block_pos.getY() < shadow_drop )
|
|
shadow_drop = BOTTOM_BORDER - block_pos.getY();
|
|
// set colider column's position to current block's X position
|
|
g_shadow_colider->setPos( block_pos.getX(), TOP_BORDER );
|
|
auto collisions =
|
|
scene.getCollisions( *g_shadow_colider, { BRICK_ID } );
|
|
auto curY = block_pos.getY();
|
|
for ( auto &col : collisions ) {
|
|
// if collision with g_cur_object, ignore
|
|
if ( std::find( invalid_objects.begin(), invalid_objects.end(),
|
|
col ) != invalid_objects.end() )
|
|
continue;
|
|
auto possible_drop = col->getPos().getY() - curY;
|
|
if ( possible_drop < shadow_drop && possible_drop >= 0 )
|
|
shadow_drop = possible_drop;
|
|
}
|
|
}
|
|
// we want the shadow to rest on top of the nearest floor
|
|
shadow_drop -= BLOCK_SIZE;
|
|
auto shadow_pos = g_cur_shadow->getPos();
|
|
g_cur_shadow->setPos( shadow_pos.getX(), shadow_pos.getY() + shadow_drop );
|
|
}
|
|
|
|
void moveThem( std::shared_ptr< SDLPP::Scene > scene, int ticks ) {
|
|
if ( !g_cur_object )
|
|
return;
|
|
// get movement generated by player
|
|
auto movement = g_cur_object->getMovement();
|
|
|
|
g_ticks_till_fall -= ticks;
|
|
if ( g_cur_object->isDescending() )
|
|
g_ticks_till_descend -= ticks;
|
|
if ( g_cur_object->isMoving() )
|
|
g_ticks_till_movement -= ticks;
|
|
if ( g_ticks_till_fall > 0 ) {
|
|
if ( g_cur_object->isMoving() && g_ticks_till_movement <= 0 ) {
|
|
g_ticks_till_movement = TICKS_TILL_MOVE;
|
|
g_cur_object->movePiece( movement.first * BLOCK_SIZE, 0 );
|
|
if ( !validPos( *scene, g_cur_object ) ) {
|
|
g_cur_object->movePiece( movement.first * -BLOCK_SIZE, 0 );
|
|
return;
|
|
} else
|
|
goto check_floor;
|
|
}
|
|
if ( g_cur_object->isDescending() && g_ticks_till_descend <= 0 ) {
|
|
g_ticks_till_descend = TICKS_TILL_DESCEND;
|
|
g_cur_object->movePiece( 0, movement.second * BLOCK_SIZE );
|
|
if ( !validPos( *scene, g_cur_object ) ) {
|
|
g_cur_object->movePiece( 0, movement.second * -BLOCK_SIZE );
|
|
return;
|
|
} else
|
|
goto check_floor;
|
|
}
|
|
return;
|
|
}
|
|
g_ticks_till_fall = TICKS_TILL_FALL;
|
|
g_cur_object->movePiece( 0, BLOCK_SIZE );
|
|
check_floor:
|
|
bool fell = false;
|
|
for ( auto &x : g_cur_object->getObjects() ) {
|
|
auto collisions = scene->getCollisions( *x, { BRICK_ID, FLOOR_ID } );
|
|
// we get 1 collision every time with ourselves
|
|
if ( collisions.size() > 1 ) {
|
|
fell = true;
|
|
break;
|
|
}
|
|
}
|
|
if ( fell ) {
|
|
g_cur_object->movePiece( 0, -BLOCK_SIZE );
|
|
for ( auto &block : g_cur_object->getObjects() ) {
|
|
if ( scene->getCollisions( *block, { GAME_OVER } ).size() > 0 ) {
|
|
g_update_scenes.push_back( g_game_over_scene );
|
|
g_active_scenes.push_back( g_game_over_scene );
|
|
g_input_functions.push_back( gameOverSceneInput );
|
|
#ifdef FEATURE
|
|
pauseBlocks();
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
g_cur_object.reset();
|
|
}
|
|
updateShadow( *scene );
|
|
}
|
|
|
|
void quitGame() {
|
|
std::cout << "Quitting!" << std::endl;
|
|
g_quit = true;
|
|
}
|
|
|
|
void resetGame() {
|
|
std::lock_guard< std::mutex > guard( g_render_mutex );
|
|
std::lock_guard< std::mutex > guard2( g_movement_mutex );
|
|
g_cur_object.reset();
|
|
g_checked_line = true;
|
|
g_next_object.reset();
|
|
g_score = 0;
|
|
g_update_score = true;
|
|
g_main_scene->resetScene();
|
|
g_main_scene->setPrevTicks( SDL_GetTicks() );
|
|
|
|
g_active_scenes = { g_main_scene };
|
|
g_input_functions = { mainSceneInput };
|
|
for ( int i = 0; i < 7; i++ )
|
|
g_bag[i] = 28;
|
|
}
|
|
|
|
int crashFlags( std::shared_ptr< TetrisPiece > piece,
|
|
std::shared_ptr< TetrisBlock > block, SDLPP::Scene &scene,
|
|
int left, int right, int bottom ) {
|
|
int retFlags = 0;
|
|
auto collisions = scene.getCollisions(
|
|
*block, { BORDER_LEFT_ID, BORDER_RIGHT_ID, FLOOR_ID } );
|
|
for ( auto &col : collisions ) {
|
|
switch ( col->getId() ) {
|
|
case BORDER_LEFT_ID:
|
|
retFlags |= left;
|
|
break;
|
|
case BORDER_RIGHT_ID:
|
|
retFlags |= right;
|
|
break;
|
|
case FLOOR_ID:
|
|
retFlags |= bottom;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
collisions = scene.getCollisions( *block, { BRICK_ID } );
|
|
if ( collisions.size() > 1 ) {
|
|
for ( auto &col : collisions ) {
|
|
if ( piece->isLeft( *col ) )
|
|
retFlags |= left;
|
|
else if ( piece->isRight( *col ) )
|
|
retFlags |= right;
|
|
else
|
|
retFlags |= bottom;
|
|
}
|
|
}
|
|
return retFlags;
|
|
}
|
|
|
|
bool checkRotation( std::shared_ptr< TetrisPiece > piece,
|
|
SDLPP::Scene &scene ) {
|
|
int crash = 0;
|
|
int cur_left = 0x01;
|
|
int cur_right = 0x02;
|
|
int was_left = 0x04;
|
|
int was_right = 0x08;
|
|
int bottom = 0x10;
|
|
int counter = 0;
|
|
do {
|
|
counter++;
|
|
if ( counter > 5 ) {
|
|
piece->revert();
|
|
return false;
|
|
}
|
|
crash = 0;
|
|
for ( auto &block : piece->getObjects() ) {
|
|
crash |=
|
|
crashFlags( piece, block, scene, cur_left, cur_right, bottom );
|
|
}
|
|
if ( crash & bottom || ( crash & cur_left && crash & cur_right ) ||
|
|
( crash & cur_left && crash & was_right ) ||
|
|
( crash & cur_right && crash & was_left ) ) {
|
|
piece->revert();
|
|
return false;
|
|
}
|
|
crash &= ~( was_left | was_right );
|
|
if ( crash & cur_left ) {
|
|
piece->movePiece( BLOCK_SIZE, 0 );
|
|
crash &= ~cur_left;
|
|
crash |= was_left;
|
|
}
|
|
if ( crash & cur_right ) {
|
|
piece->movePiece( -BLOCK_SIZE, 0 );
|
|
crash &= ~cur_right;
|
|
crash |= was_right;
|
|
}
|
|
} while ( crash );
|
|
return true;
|
|
}
|
|
|
|
std::shared_ptr< TetrisBlock >
|
|
createTetrisBlock( double x, double y, const std::string &color,
|
|
const std::string &outline, int index,
|
|
std::shared_ptr< SDLPP::Renderer > renderer,
|
|
std::shared_ptr< SDLPP::Scene > scene ) {
|
|
auto ret =
|
|
std::make_shared< TetrisBlock >( x, y, BLOCK_SIZE, BLOCK_SIZE, renderer,
|
|
color, true, index, scene, g_bag );
|
|
ret->setOutlineColor( outline );
|
|
ret->addCollision( SDLPP::RectColider( 0.1, 0.1, 0.8, 0.8 ) );
|
|
ret->setId( BRICK_ID );
|
|
ret->centerX();
|
|
scene->addObject( ret );
|
|
return ret;
|
|
}
|
|
|
|
std::shared_ptr< TetrisPiece >
|
|
tetrisBrick( std::shared_ptr< SDLPP::Renderer > renderer,
|
|
std::shared_ptr< SDLPP::Scene > scene ) {
|
|
auto retPiece = std::make_shared< TetrisPiece >();
|
|
auto color = colors["piece_brick"];
|
|
auto outline = colors["piece_brick_out"];
|
|
retPiece->addPiece( createTetrisBlock( 0.5 - BLOCK_SIZE, TOP_BORDER, color,
|
|
outline, TETRIS_BRICK, renderer,
|
|
scene ),
|
|
-1, 0 );
|
|
retPiece->addPiece( createTetrisBlock( 0.5, TOP_BORDER, color, outline,
|
|
TETRIS_BRICK, renderer, scene ),
|
|
0, 0 );
|
|
retPiece->addPiece(
|
|
createTetrisBlock( 0.5 - BLOCK_SIZE, TOP_BORDER + BLOCK_SIZE, color,
|
|
outline, TETRIS_BRICK, renderer, scene ),
|
|
-1, 1 );
|
|
retPiece->addPiece( createTetrisBlock( 0.5, TOP_BORDER + BLOCK_SIZE, color,
|
|
outline, TETRIS_BRICK, renderer,
|
|
scene ),
|
|
0, 1 );
|
|
retPiece->disableRotation();
|
|
return retPiece;
|
|
}
|
|
|
|
std::shared_ptr< TetrisPiece >
|
|
tetrisT( std::shared_ptr< SDLPP::Renderer > renderer,
|
|
std::shared_ptr< SDLPP::Scene > scene ) {
|
|
auto retPiece = std::make_shared< TetrisPiece >();
|
|
auto color = colors["piece_T"];
|
|
auto outline = colors["piece_T_out"];
|
|
retPiece->addPiece( createTetrisBlock( 0.5 - BLOCK_SIZE,
|
|
TOP_BORDER + BLOCK_SIZE, color,
|
|
outline, TETRIS_T, renderer, scene ),
|
|
-1, 0 );
|
|
retPiece->addPiece( createTetrisBlock( 0.5, TOP_BORDER + BLOCK_SIZE, color,
|
|
outline, TETRIS_T, renderer, scene ),
|
|
0, 0 );
|
|
retPiece->addPiece( createTetrisBlock( 0.5, TOP_BORDER, color, outline,
|
|
TETRIS_T, renderer, scene ),
|
|
0, -1 );
|
|
retPiece->addPiece( createTetrisBlock( 0.5 + BLOCK_SIZE,
|
|
TOP_BORDER + BLOCK_SIZE, color,
|
|
outline, TETRIS_T, renderer, scene ),
|
|
1, 0 );
|
|
return retPiece;
|
|
}
|
|
|
|
std::shared_ptr< TetrisPiece >
|
|
tetrisLRight( std::shared_ptr< SDLPP::Renderer > renderer,
|
|
std::shared_ptr< SDLPP::Scene > scene ) {
|
|
auto retPiece = std::make_shared< TetrisPiece >();
|
|
auto color = colors["piece_L_right"];
|
|
auto outline = colors["piece_L_right_out"];
|
|
retPiece->addPiece(
|
|
createTetrisBlock( 0.5 - BLOCK_SIZE, TOP_BORDER + BLOCK_SIZE, color,
|
|
outline, TETRIS_L_RIGHT, renderer, scene ),
|
|
-2, 0 );
|
|
retPiece->addPiece( createTetrisBlock( 0.5, TOP_BORDER + BLOCK_SIZE, color,
|
|
outline, TETRIS_L_RIGHT, renderer,
|
|
scene ),
|
|
-1, 0 );
|
|
retPiece->addPiece(
|
|
createTetrisBlock( 0.5 + BLOCK_SIZE, TOP_BORDER + BLOCK_SIZE, color,
|
|
outline, TETRIS_L_RIGHT, renderer, scene ),
|
|
0, 0 );
|
|
retPiece->addPiece( createTetrisBlock( 0.5 + BLOCK_SIZE, TOP_BORDER, color,
|
|
outline, TETRIS_L_RIGHT, renderer,
|
|
scene ),
|
|
0, -1 );
|
|
return retPiece;
|
|
}
|
|
|
|
std::shared_ptr< TetrisPiece >
|
|
tetrisZRight( std::shared_ptr< SDLPP::Renderer > renderer,
|
|
std::shared_ptr< SDLPP::Scene > scene ) {
|
|
auto retPiece = std::make_shared< TetrisPiece >();
|
|
auto color = colors["piece_Z_right"];
|
|
auto outline = colors["piece_Z_right_out"];
|
|
retPiece->addPiece(
|
|
createTetrisBlock( 0.5 - BLOCK_SIZE, TOP_BORDER + BLOCK_SIZE, color,
|
|
outline, TETRIS_Z_RIGHT, renderer, scene ),
|
|
-1, 0 );
|
|
retPiece->addPiece( createTetrisBlock( 0.5, TOP_BORDER + BLOCK_SIZE, color,
|
|
outline, TETRIS_Z_RIGHT, renderer,
|
|
scene ),
|
|
0, 0 );
|
|
retPiece->addPiece( createTetrisBlock( 0.5, TOP_BORDER, color, outline,
|
|
TETRIS_Z_RIGHT, renderer, scene ),
|
|
0, -1 );
|
|
retPiece->addPiece( createTetrisBlock( 0.5 + BLOCK_SIZE, TOP_BORDER, color,
|
|
outline, TETRIS_Z_RIGHT, renderer,
|
|
scene ),
|
|
1, -1 );
|
|
return retPiece;
|
|
}
|
|
|
|
std::shared_ptr< TetrisPiece >
|
|
tetrisLine( std::shared_ptr< SDLPP::Renderer > renderer,
|
|
std::shared_ptr< SDLPP::Scene > scene ) {
|
|
auto retPiece = std::make_shared< TetrisPiece >();
|
|
auto color = colors["piece_line"];
|
|
auto outline = colors["piece_line_out"];
|
|
retPiece->addPiece( createTetrisBlock( 0.5 - 2 * BLOCK_SIZE, TOP_BORDER,
|
|
color, outline, TETRIS_LINE,
|
|
renderer, scene ),
|
|
-1, 0 );
|
|
retPiece->addPiece( createTetrisBlock( 0.5 - BLOCK_SIZE, TOP_BORDER, color,
|
|
outline, TETRIS_LINE, renderer,
|
|
scene ),
|
|
0, 0 );
|
|
retPiece->addPiece( createTetrisBlock( 0.5, TOP_BORDER, color, outline,
|
|
TETRIS_LINE, renderer, scene ),
|
|
1, 0 );
|
|
retPiece->addPiece( createTetrisBlock( 0.5 + BLOCK_SIZE, TOP_BORDER, color,
|
|
outline, TETRIS_LINE, renderer,
|
|
scene ),
|
|
2, 0 );
|
|
return retPiece;
|
|
}
|
|
|
|
std::shared_ptr< TetrisPiece >
|
|
tetrisLLeft( std::shared_ptr< SDLPP::Renderer > renderer,
|
|
std::shared_ptr< SDLPP::Scene > scene ) {
|
|
auto retPiece = std::make_shared< TetrisPiece >();
|
|
auto color = colors["piece_L_left"];
|
|
auto outline = colors["piece_L_left_out"];
|
|
retPiece->addPiece( createTetrisBlock( 0.5 - BLOCK_SIZE, TOP_BORDER, color,
|
|
outline, TETRIS_L_LEFT, renderer,
|
|
scene ),
|
|
0, -1 );
|
|
retPiece->addPiece(
|
|
createTetrisBlock( 0.5 - BLOCK_SIZE, TOP_BORDER + BLOCK_SIZE, color,
|
|
outline, TETRIS_L_LEFT, renderer, scene ),
|
|
0, 0 );
|
|
retPiece->addPiece( createTetrisBlock( 0.5, TOP_BORDER + BLOCK_SIZE, color,
|
|
outline, TETRIS_L_LEFT, renderer,
|
|
scene ),
|
|
1, 0 );
|
|
retPiece->addPiece(
|
|
createTetrisBlock( 0.5 + BLOCK_SIZE, TOP_BORDER + BLOCK_SIZE, color,
|
|
outline, TETRIS_L_LEFT, renderer, scene ),
|
|
2, 0 );
|
|
return retPiece;
|
|
}
|
|
|
|
std::shared_ptr< TetrisPiece >
|
|
tetrisZLeft( std::shared_ptr< SDLPP::Renderer > renderer,
|
|
std::shared_ptr< SDLPP::Scene > scene ) {
|
|
auto retPiece = std::make_shared< TetrisPiece >();
|
|
auto color = colors["piece_Z_left"];
|
|
auto outline = colors["piece_Z_left_out"];
|
|
retPiece->addPiece( createTetrisBlock( 0.5 - BLOCK_SIZE, TOP_BORDER, color,
|
|
outline, TETRIS_Z_LEFT, renderer,
|
|
scene ),
|
|
-1, 0 );
|
|
retPiece->addPiece( createTetrisBlock( 0.5, TOP_BORDER, color, outline,
|
|
TETRIS_Z_LEFT, renderer, scene ),
|
|
0, 0 );
|
|
retPiece->addPiece( createTetrisBlock( 0.5, TOP_BORDER + BLOCK_SIZE, color,
|
|
outline, TETRIS_Z_LEFT, renderer,
|
|
scene ),
|
|
0, 1 );
|
|
retPiece->addPiece(
|
|
createTetrisBlock( 0.5 + BLOCK_SIZE, TOP_BORDER + BLOCK_SIZE, color,
|
|
outline, TETRIS_Z_LEFT, renderer, scene ),
|
|
1, 1 );
|
|
return retPiece;
|
|
}
|
|
|
|
void updateSize() {
|
|
// have to resize rectangles for the text to be resized correctly
|
|
g_main_scene->updateSizeAndPosition();
|
|
g_menu_scene->updateSizeAndPosition();
|
|
g_game_over_scene->updateSizeAndPosition();
|
|
g_options_scene->updateSizeAndPosition();
|
|
|
|
std::unordered_set< int > textObjects = { TEXT_ID, MENU_TEXT_ID,
|
|
MENU_ITEM_ID };
|
|
for ( auto &x : g_main_scene->getObjects( textObjects ) ) {
|
|
x->updateSizeAndPosition();
|
|
}
|
|
g_score_texture->updateSizeAndPosition();
|
|
for ( auto &x : g_menu_scene->getObjects( textObjects ) ) {
|
|
x->updateSizeAndPosition();
|
|
}
|
|
for ( auto &x : g_game_over_scene->getObjects( textObjects ) ) {
|
|
x->updateSizeAndPosition();
|
|
}
|
|
for ( auto &x : g_options_scene->getObjects( textObjects ) ) {
|
|
x->updateSizeAndPosition();
|
|
}
|
|
}
|
|
|
|
void updateBlocks() {
|
|
for ( auto &x : g_main_scene->getObjects( { BRICK_ID, SHADOW_ID } ) ) {
|
|
x->specialAction( PIECE_ACTION_UPDATE_BLOCK );
|
|
}
|
|
}
|
|
#ifdef FEATURE
|
|
void pauseBlocks() {
|
|
for ( auto &x : g_main_scene->getObjects( { BRICK_ID, SHADOW_ID } ) ) {
|
|
x->pauseAnimation();
|
|
}
|
|
}
|
|
|
|
void resumeBlocks() {
|
|
for ( auto &x : g_main_scene->getObjects( { BRICK_ID, SHADOW_ID } ) ) {
|
|
x->resumeAnimation();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void updateColors() {
|
|
for ( auto &x : g_main_scene->getObjects( { BRICK_ID, SHADOW_ID } ) ) {
|
|
x->specialAction( PIECE_ACTION_UPDATE_COLOR );
|
|
}
|
|
for ( auto &x : g_main_scene->getObjects( { BARRIER_ID } ) ) {
|
|
x->setColor( colors["barrier"] );
|
|
}
|
|
for ( auto &x : g_main_scene->getObjects( { BACKGROUND_ID } ) ) {
|
|
x->setColor( colors["background"] );
|
|
}
|
|
for ( auto &x : g_main_scene->getObjects( { SHADOW_ID } ) ) {
|
|
x->setColor( colors["shadow"] );
|
|
}
|
|
for ( auto &x : g_main_scene->getObjects( { LINE_ID } ) ) {
|
|
x->setColor( colors["line"] );
|
|
}
|
|
if ( g_font_config != nullptr ) {
|
|
g_font_config->setColor( colors["text"] );
|
|
g_font_config->setOutlineColor( colors["text_out"] );
|
|
for ( auto &x : g_main_scene->getObjects( { TEXT_ID } ) ) {
|
|
x->updateSizeAndPosition();
|
|
}
|
|
}
|
|
g_menu_options[g_menu_select]->setColor( colors["menu_item_background"] );
|
|
g_game_over_options[g_game_over_select]->setColor(
|
|
colors["menu_item_background"] );
|
|
g_options_options[g_options_select]->setColor(
|
|
colors["menu_item_background"] );
|
|
for ( auto &x : g_menu_scene->getObjects( { MENU_BACKGROUND_ID } ) ) {
|
|
x->setColor( colors["menu_background"] );
|
|
}
|
|
for ( auto &x : g_game_over_scene->getObjects( { MENU_BACKGROUND_ID } ) ) {
|
|
x->setColor( colors["menu_background"] );
|
|
}
|
|
for ( auto &x : g_options_scene->getObjects( { MENU_BACKGROUND_ID } ) ) {
|
|
x->setColor( colors["menu_background"] );
|
|
}
|
|
updateSize();
|
|
}
|