#include "sdlpp.hpp" #include #include #include #include #define COLIDER_ID 0x00000001 #define BRICK_ID 0x00000002 #define GAME_OVER 0x00000003 #define SHADOW_ID 0x00000004 #define BORDER_ID 0x00000005 #define FLOOR_ID 0x00000006 #define LEFT_BORDER 0.3 #define RIGHT_BORDER 0.7 #define BOTTOM_BORDER 1 #define TOP_BORDER 0.16 #define BLOCK_SIZE 0.04 #define TICKS_TILL_FALL 500 #define TICKS_TILL_DESCEND 50 #define TICKS_TILL_MOVE 100 #define TETRIS_BRICK 0 #define TETRIS_T 1 #define TETRIS_L_RIGHT 2 #define TETRIS_Z_RIGHT 3 #define TETRIS_LINE 4 #define TETRIS_L_LEFT 5 #define TETRIS_Z_LEFT 6 #define PAUSE_PAUSE 1 #define PAUSE_GAME_OVER 2 int pause = 0; int pause_select = 0; int pause_max = 2; int game_over_select = 0; int game_over_max = 1; int ticks_till_fall = TICKS_TILL_FALL; int ticks_till_descend = TICKS_TILL_DESCEND; int ticks_till_movement = TICKS_TILL_MOVE; std::vector< std::shared_ptr< SDLPP::RectangleRender > > pause_options; std::vector< std::shared_ptr< SDLPP::RectangleRender > > game_over_options; std::shared_ptr< SDLPP::TextRenderer > score_texture; std::shared_ptr< SDLPP::Renderer > active_renderer; int score = 0; bool update_score = false; bool checked_line = false; bool wait_for_anim = false; std::vector< int > bag = { 28, 28, 28, 28, 28, 28, 28 }; std::shared_ptr< SDLPP::Font > font; std::shared_ptr< SDLPP::Scene > active_scene; std::shared_ptr< SDLPP::Scene > pause_scene; std::shared_ptr< SDLPP::Scene > game_over_scene; class TetrisBlock : public SDLPP::RectangleRender { public: TetrisBlock() = delete; TetrisBlock( double x, double y, double w, double h, const std::shared_ptr< SDLPP::Renderer > &r, const std::string &img_or_color, bool is_polygon, int index, std::shared_ptr scene ) : RectangleRender( x, y, w, h, r, img_or_color, is_polygon ) { _index = index; bag[_index]--; _scene = scene; } TetrisBlock( const TetrisBlock &other ) : TetrisBlock(other.getDoubleRect().first.first,other.getDoubleRect().first.second,other.getDoubleRect().second.first,other.getDoubleRect().second.second,other.getRenderer(), other.getColor(), true, other._index, other._scene) {} ~TetrisBlock() { bag[_index]++; } virtual std::shared_ptr copySelf() { return std::make_shared(*this); } std::shared_ptr copyInScene() { auto ret = std::shared_ptr(new TetrisBlock(*this)); _scene->addObject(ret); return ret; } bool isSamePos(const SDLPP::RenderObject &other) const { auto mypos = getPos(); auto otherpos = other.getPos(); auto diff1 = mypos.first - otherpos.first; diff1 = (diff1 < 0) * (-1) * diff1 + (diff1 > 0) * diff1; auto diff2 = mypos.second - otherpos.second; diff2 = (diff2 < 0) * (-1) * diff2 + (diff2 > 0) * diff2; return diff1 < 0.0001 && diff2 < 0.0001; } private: int _index = 0; std::shared_ptr _scene; }; 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 ); ret->setOutlineColor( outline ); ret->addCollision( SDLPP::Rect( 0.1, 0.1, 0.8, 0.8 ) ); ret->setId( BRICK_ID ); ret->centerX(); scene->addObject( ret ); return ret; } class TetrisPiece { public: TetrisPiece() { original_pos.reserve( 4 ); } void addPiece( std::shared_ptr< TetrisBlock > piece, int x, int y ) { pieces.push_back( piece ); pieces_rel_position.push_back( { 0, 0, 0, 0 } ); // done this way for SPEEEEEEED // left pieces_rel_position.back()[0] = ( x < 0 ) * ( -1 ) * x; // right pieces_rel_position.back()[1] = ( x > 0 ) * x; // top pieces_rel_position.back()[2] = ( y < 0 ) * ( -1 ) * y; // bottom pieces_rel_position.back()[3] = ( y > 0 ) * y; } void rotate() { if(!rotate_allowed) return; for ( unsigned long i = 0; i < pieces.size(); i++ ) { auto &piece = pieces[i]; auto &positions = pieces_rel_position[i]; auto position = piece->getPos(); original_pos[i] = position; position.first += positions[0] * BLOCK_SIZE; position.first -= positions[1] * BLOCK_SIZE; position.second += positions[2] * BLOCK_SIZE; position.second -= positions[3] * BLOCK_SIZE; auto bottom = positions[3]; auto top = positions[2]; positions[3] = positions[1]; positions[2] = positions[0]; positions[1] = top; positions[0] = bottom; position.first -= positions[0] * BLOCK_SIZE; position.first += positions[1] * BLOCK_SIZE; position.second -= positions[2] * BLOCK_SIZE; position.second += positions[3] * BLOCK_SIZE; piece->setPos( position.first, position.second ); } } void revert() { for ( unsigned long i = 0; i < pieces.size(); i++ ) { auto &piece = pieces[i]; auto &positions = pieces_rel_position[i]; piece->setPos( original_pos[i].first, original_pos[i].second ); auto top = positions[1]; auto bottom = positions[0]; positions[1] = positions[3]; positions[0] = positions[2]; positions[2] = top; positions[3] = bottom; } } std::vector< std::shared_ptr< TetrisBlock > > &getObjects() { return pieces; } void setPos( double x, double y ) { for ( unsigned long i = 0; i < pieces.size(); i++ ) { auto &piece = pieces[i]; auto &positions = pieces_rel_position[i]; std::pair pos = {x, y}; pos.first -= positions[0] * BLOCK_SIZE; pos.first += positions[1] * BLOCK_SIZE; pos.second -= positions[2] * BLOCK_SIZE; pos.second += positions[3] * BLOCK_SIZE; piece->setPos( pos.first, pos.second ); } } void setPos(const std::pair &pos) { setPos(pos.first, pos.second); } std::pair getPos() { auto &piece = pieces[0]; auto &relpositions = pieces_rel_position[0]; auto pos = piece->getPos(); pos.first += relpositions[0] * BLOCK_SIZE; pos.first -= relpositions[1] * BLOCK_SIZE; pos.second += relpositions[2] * BLOCK_SIZE; pos.second -= relpositions[3] * BLOCK_SIZE; return pos; } void clear() { pieces.clear(); pieces_rel_position.clear(); } void startDescend() { descend = true; } void stopDescend() { descend = false; } void startMovement() { userMovement += 1; } void stopMovement() { userMovement -= 1; } bool isDescending() { return descend; } bool isMoving() { return userMovement > 0; } bool isLeft(const SDLPP::RenderObject &block) const { return isPosition(block, 0); } bool isRight(const SDLPP::RenderObject &block) const { return isPosition(block, 1); } void movePiece(double x, double y) { for ( auto &block : getObjects() ) { auto pos = block->getPos(); block->setPos( pos.first + x, pos.second + y ); } } void disableRotation() { rotate_allowed = false; } void turnIntoShadow() { for(auto &block : getObjects() ) { block->setId(SHADOW_ID); block->setColor("#AAAAAAAA"); } } std::shared_ptr copySelf() { auto ret = std::make_shared(); for(int i = 0; i < 4; i++) { auto block = pieces[i]->copyInScene(); block->centerX(); ret->addBlockInPos(block, pieces_rel_position[i]); } return ret; } void destroy() { for(auto &x : getObjects()) { x->destroy(); } } void addMovement(int x, int y) { movement.first += x; movement.second += y; } std::pair getMovement() const { return movement; } private: bool isPosition(const SDLPP::RenderObject &block, int pos) const { for(int i = 0; i < 4; i++) { if(pieces[i]->isSamePos(block)) { return pieces_rel_position[i][pos] != 0; } } return false; } void resetBlock(int index, std::shared_ptr< TetrisBlock > piece) { piece->setPos(pieces[index]->getPos()); pieces[index] = piece; } void addBlockInPos(std::shared_ptr piece, const std::vector &relpos) { pieces.push_back( piece ); pieces_rel_position.push_back(relpos); } std::vector< std::vector< int > > pieces_rel_position; std::vector< std::shared_ptr< TetrisBlock > > pieces; std::vector< std::pair< double, double > > original_pos; bool descend = false; int userMovement = 0; bool rotate_allowed = true; std::pair movement = {0,0}; }; std::vector< std::shared_ptr< SDLPP::RectangleRender > > line_coliders; std::shared_ptr< TetrisPiece > cur_object; std::shared_ptr< TetrisPiece > next_object; std::shared_ptr< TetrisPiece > cur_shadow; std::shared_ptr< SDLPP::RectangleRender > shadow_colider; void doInput( std::shared_ptr< SDLPP::Scene > scene ); void doInputPause(); void doInputGameOver(); bool quit = false; std::mutex movement_mutex; 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 = "#FF0000"; auto outline = "#AA0000"; 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 = "#00FF00"; auto outline = "#00AA00"; 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 = "#0000FF"; auto outline = "#0000AA"; 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 = "#FF00FF"; auto outline = "#AA00AA"; 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 = "#FFFF00"; auto outline = "#AAAA00"; 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 = "#00FFFF"; auto outline = "#00AAAA"; 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 = "#FFFFFF"; auto outline = "#AAAAAA"; 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; } std::vector< std::shared_ptr< TetrisPiece > ( * )( std::shared_ptr< SDLPP::Renderer >, std::shared_ptr< SDLPP::Scene > ) > tetrisFunctions = { tetrisBrick, tetrisT, tetrisLRight, tetrisZRight, tetrisLine, tetrisLLeft, tetrisZLeft, }; void addStuff( SDLPP::Scene &scene, std::shared_ptr< SDLPP::Renderer > &r ) { auto bg = std::make_shared< SDLPP::RectangleRender >( 0, 0, 10, 10, r, "#222222", true ); bg->setPermanent(); scene.addObject( bg ); auto left_barrier = std::make_shared< SDLPP::RectangleRender >( LEFT_BORDER - 0.02, 0, 0.02, BOTTOM_BORDER, r, "#FF000080", true ); left_barrier->centerX(); left_barrier->setStatic(); scene.addObject( left_barrier ); auto right_barrier = std::make_shared< SDLPP::RectangleRender >( RIGHT_BORDER, 0, 0.02, BOTTOM_BORDER, r, "#FF000080", true ); right_barrier->centerX(); right_barrier->setStatic(); scene.addObject( right_barrier ); auto bottom_barrier = std::make_shared< SDLPP::RectangleRender >( LEFT_BORDER - 0.02, BOTTOM_BORDER, RIGHT_BORDER - LEFT_BORDER + 0.04, 0.02, r, "#FF000080", true ); bottom_barrier->centerX(); bottom_barrier->setStatic(); scene.addObject( bottom_barrier ); auto tetris = std::make_shared< SDLPP::TextRenderer >( 0.4, 0, 0.2, 0.1, r, *font, "TETRIS", "FFFFFF", "000000", 5 ); tetris->centerX(); tetris->setStatic(); scene.addObject( tetris ); auto next = std::make_shared< SDLPP::TextRenderer >( RIGHT_BORDER + 0.1, 0.35, 0.2, 0.1, r, *font, "NEXT", "FFFFFF", "000000", 5, SDLPP_TEXT_CENTER ); next->centerX(); next->setStatic(); scene.addObject( next ); double posy = 1; auto gameover = std::make_shared< SDLPP::RectangleRender >( 0.5, 0, 0, TOP_BORDER + BLOCK_SIZE, r ); auto gameover_collision = SDLPP::Rect( -1, 0, -1, 0.9 ); gameover_collision.setInfinite(); gameover->addCollision( gameover_collision ); gameover->setId( GAME_OVER ); gameover->setColiderColor( "FF0000" ); gameover->setStatic(); scene.addObject( gameover ); auto score_text = std::make_shared< SDLPP::TextRenderer >( RIGHT_BORDER + 0.1, 0.1, 0.2, 0.1, r, *font, "SCORE", "#FFFFFF", "#000000", 5, SDLPP_TEXT_CENTER ); score_text->centerX(); score_text->setStatic(); scene.addObject( score_text ); score_texture = std::make_shared< SDLPP::TextRenderer >( RIGHT_BORDER + 0.1, 0.2, 0.2, 0.1, r, *font, std::to_string( score ), "FFFFFF", "000000", 5, SDLPP_TEXT_TOP ); score_texture->centerX(); score_texture->setStatic(); scene.addObject( score_texture ); for ( int i = 0; i < 20; i++ ) { posy -= BLOCK_SIZE; auto colider = std::make_shared< SDLPP::RectangleRender >( LEFT_BORDER, posy, RIGHT_BORDER - LEFT_BORDER, BLOCK_SIZE, r ); colider->addCollision(SDLPP::Rect( 0.01, 0.1, 0.98, 0.8 )); colider->setId( COLIDER_ID ); colider->setStatic(); colider->centerX(); line_coliders.push_back( colider ); scene.addObject( colider ); } auto shcol = std::make_shared< SDLPP::RectangleRender >( 0, TOP_BORDER, BLOCK_SIZE, BOTTOM_BORDER - TOP_BORDER, r ); shcol->addCollision(SDLPP::Rect( 0.1, 0.01, 0.8, 0.98 )); shcol->setId( COLIDER_ID ); shcol->setStatic(); shcol->centerX(); shadow_colider = shcol; scene.addObject( shadow_colider ); auto border = std::make_shared< SDLPP::RectangleRender >( LEFT_BORDER - 1, TOP_BORDER, 1, BOTTOM_BORDER, r); border->setId( BORDER_ID ); border->setStatic(); border->centerX(); border->addCollision(SDLPP::Rect( 0, 0, 0.99, 1)); border->setColiderColor("#FF00FF"); scene.addObject(border); border = std::make_shared< SDLPP::RectangleRender >( RIGHT_BORDER, TOP_BORDER, 1, BOTTOM_BORDER, r); border->setId( BORDER_ID ); border->setStatic(); border->centerX(); border->addCollision(SDLPP::Rect( 0.01, 0, 1, 1)); border->setColiderColor("#FF00FF"); scene.addObject(border); auto floor = std::make_shared< SDLPP::RectangleRender >( LEFT_BORDER, BOTTOM_BORDER, RIGHT_BORDER - LEFT_BORDER, 1, r); floor->setId( FLOOR_ID ); floor->setStatic(); floor->centerX(); floor->addCollision(SDLPP::Rect(0, 0.01, 1, 1)); floor->setColiderColor("#00FF00"); scene.addObject(floor); } void updateScore() { score_texture->setText( *font, std::to_string( score ), "#FFFFFF", "#000000", 5 ); } void addPause( SDLPP::Scene &scene, std::shared_ptr< SDLPP::Renderer > &r ) { auto bg = std::make_shared< SDLPP::RectangleRender >( 0, 0, 10, 10, r, "#00000080", true ); bg->setId( 123 ); bg->setPermanent( true ); scene.addObject( bg ); auto y = std::make_shared< SDLPP::TextRenderer >( 0.25, 0.1, 0.5, 0.3, r ); y->setText( *font, "PAUSED", "#FFFFFF", "#000000", 5 ); y->setId( 0 ); y->centerX(); scene.addObject( y ); auto resume = std::make_shared< SDLPP::TextRenderer >( 0.4, 0.51, 0.2, 0.08, r ); resume->setText( *font, "Resume", "#FFFFFF", "#000000", 5 ); resume->setColor( "#FFFFFF40" ); resume->centerX(); scene.addObject( resume ); pause_options.push_back( resume ); auto restart = std::make_shared< SDLPP::TextRenderer >( 0.4, 0.61, 0.2, 0.08, r ); restart->setText( *font, "Restart", "#FFFFFF", "#000000", 5 ); restart->centerX(); scene.addObject( restart ); pause_options.push_back( restart ); auto quit = std::make_shared< SDLPP::TextRenderer >( 0.4, 0.71, 0.2, 0.08, r ); quit->setText( *font, "Quit Game", "#FFFFFF", "#000000", 5 ); quit->centerX(); scene.addObject( quit ); pause_options.push_back( quit ); } void addGameOver( SDLPP::Scene &scene, std::shared_ptr< SDLPP::Renderer > &r ) { auto bg = std::make_shared< SDLPP::RectangleRender >( 0, 0, 10, 10, r, "#00000080", true ); bg->setId( 123 ); bg->setPermanent( true ); scene.addObject( bg ); auto y = std::make_shared< SDLPP::TextRenderer >( 0.25, 0.1, 0.5, 0.3, r ); y->setText( *font, "GAME OVER", "#FFFFFF", "#000000", 5 ); y->setId( 0 ); y->centerX(); scene.addObject( y ); auto restart = std::make_shared< SDLPP::TextRenderer >( 0.4, 0.5, 0.2, 0.1, r ); restart->setText( *font, "Restart", "#FFFFFF", "#000000", 5 ); restart->centerX(); restart->setColor( "#FFFFFF40" ); scene.addObject( restart ); game_over_options.push_back( restart ); auto quit = std::make_shared< SDLPP::TextRenderer >( 0.4, 0.7, 0.2, 0.1, r ); quit->setText( *font, "Quit Game", "#FFFFFF", "#000000", 5 ); quit->centerX(); scene.addObject( quit ); game_over_options.push_back( quit ); } void quitGame() { std::cout << "Quitting!" << std::endl; quit = true; } bool checkRotation( std::shared_ptr piece, SDLPP::Scene &scene ) { bool ret = true; bool crash = true; int left = 0x01; int right = 0x02; int bottom = 0x04; int flags = 0; // game board limits while ( crash ) { crash = false; flags = 0; for ( auto &block : piece->getObjects() ) { auto pos = block->getPos(); if ( pos.first < LEFT_BORDER - 0.01 ) { flags |= left; crash = true; break; } else if ( pos.first > RIGHT_BORDER - BLOCK_SIZE + 0.01 ) { crash = true; flags |= right; break; } else if ( pos.second >= BOTTOM_BORDER ) { crash = true; flags |= bottom; break; } } if ( crash ) { if ( flags & bottom || (flags & left && flags & right) ) { piece->revert(); ret = false; } else { if( flags & left ) piece->movePiece(BLOCK_SIZE, 0); else if( flags & right ) piece->movePiece(-BLOCK_SIZE, 0); } } } // blocks crash = true; bool was_left = false; bool was_right = false; bool is_left = false; bool is_right = false; while ( crash ) { is_left = false; is_right = false; crash = false; for(auto &block : piece->getObjects()) { auto collisions = scene.getCollisions(*block, {BRICK_ID}); if(collisions.size() == 1) continue; for(auto &col : collisions) { crash = true; is_left |= piece->isLeft(*col); is_right |= piece->isRight(*col); } } if(!crash) break; if((is_left && is_right) || (is_left && was_right) || (is_right && was_left)) { piece->revert(); ret = false; break; } was_left = is_left; was_right = is_right; if(is_left) piece->movePiece(BLOCK_SIZE, 0); if(is_right) piece->movePiece(-BLOCK_SIZE, 0); // either bottom or up if(!is_left && !is_right) { piece->revert(); ret = false; break; } } return ret; } void updateShadow(SDLPP::Scene &scene) { if(!cur_object) { cur_shadow->destroy(); cur_shadow.reset(); return; } cur_shadow->setPos(cur_object->getPos()); double shadow_drop = BOTTOM_BORDER; auto &invalid_objects = cur_object->getObjects(); for( auto &x : cur_shadow->getObjects() ) { if(BOTTOM_BORDER - x->getPos().second < shadow_drop) shadow_drop = BOTTOM_BORDER - x->getPos().second; shadow_colider->setPos(x->getPos().first, TOP_BORDER); auto collisions = scene.getCollisions( *shadow_colider, { BRICK_ID } ); auto curY = x->getPos().second; for(auto &col : collisions) { auto colY = col->getPos().second; if(std::find(invalid_objects.begin(), invalid_objects.end(), col) != invalid_objects.end()) continue; auto possible_drop = colY - curY; if(possible_drop < shadow_drop && possible_drop >= 0) shadow_drop = colY - curY; } } shadow_drop -= BLOCK_SIZE; cur_shadow->setPos(cur_shadow->getPos().first, cur_shadow->getPos().second + shadow_drop); } bool validPos(SDLPP::Scene &scene, std::shared_ptr piece) { auto ret = true; for ( auto &x : piece->getObjects() ) { if(x->getId() != 2) std::cout << "ID: " << x->getId() << std::endl; auto collisions = scene.getCollisions( *x, { BRICK_ID, FLOOR_ID, BORDER_ID } ); if ( collisions.size() > 1 ) { ret = false; break; } } return ret; } void handleKeyDown( SDL_Keycode key, SDLPP::Scene &scene ) { switch ( key ) { case SDLK_ESCAPE: { pause = PAUSE_PAUSE; pause_scene->updateSizeAndPosition(); std::thread pauseThread( doInputPause ); pauseThread.detach(); } break; case SDLK_LEFT: case SDLK_a: if(!cur_object) break; cur_object->movePiece(-BLOCK_SIZE, 0); if(!validPos(scene, cur_object)) cur_object->movePiece(BLOCK_SIZE, 0); updateShadow(scene); ticks_till_movement = 2*TICKS_TILL_MOVE; cur_object->startMovement(); cur_object->addMovement(-1,0); break; case SDLK_RIGHT: case SDLK_d: if(!cur_object) break; cur_object->movePiece(BLOCK_SIZE, 0); if(!validPos(scene, cur_object)) cur_object->movePiece(-BLOCK_SIZE, 0); updateShadow(scene); ticks_till_movement = 2*TICKS_TILL_MOVE; cur_object->startMovement(); cur_object->addMovement(1,0); break; case SDLK_DOWN: case SDLK_s: if(!cur_object) break; cur_object->startDescend(); cur_object->addMovement(0,1); break; case SDLK_UP: case SDLK_w: if(!cur_object) break; cur_object->rotate(); if( checkRotation( cur_object, scene ) ) cur_shadow->rotate(); updateShadow(scene); break; case SDLK_r: scene.getRenderer().setRenderColiders( !scene.getRenderer().getRenderColiders() ); default: break; } } void handleKeyUp( SDL_Keycode key ) { switch(key) { case SDLK_DOWN: case SDLK_s: if(!cur_object) break; if(cur_object->isDescending()) { cur_object->stopDescend(); cur_object->addMovement(0,-1); ticks_till_descend = TICKS_TILL_DESCEND; } break; case SDLK_LEFT: case SDLK_a: if(!cur_object) break; if(cur_object->isMoving()) { cur_object->stopMovement(); cur_object->addMovement(1,0); if(cur_object->isMoving()) ticks_till_movement = TICKS_TILL_MOVE; } break; case SDLK_RIGHT: case SDLK_d: if(!cur_object) break; if(cur_object->isMoving()) { cur_object->stopDescend(); cur_object->addMovement(-1,0); if(cur_object->isMoving()) ticks_till_movement = TICKS_TILL_MOVE; } default: break; } } void handleKeyDownPause( SDL_Keycode key ) { switch ( key ) { case SDLK_ESCAPE: { pause = 0; active_scene->setPrevTicks( SDL_GetTicks() ); std::thread inputThread( doInput, active_scene ); inputThread.detach(); } break; case SDLK_r: active_scene->getRenderer().setRenderColiders( !active_scene->getRenderer().getRenderColiders() ); break; case SDLK_s: case SDLK_DOWN: pause_options[pause_select]->unsetColor(); pause_select++; if ( pause_select > pause_max ) pause_select = 0; pause_options[pause_select]->setColor( "FFFFFF40" ); break; case SDLK_w: case SDLK_UP: pause_options[pause_select]->unsetColor(); pause_select--; if ( pause_select < 0 ) pause_select = pause_max; pause_options[pause_select]->setColor( "FFFFFF40" ); break; case SDLK_RETURN: switch ( pause_select ) { case 0: { pause = 0; active_scene->setPrevTicks( SDL_GetTicks() ); std::thread inputThread( doInput, active_scene ); inputThread.detach(); } break; case 1: { pause = 0; cur_object.reset(); checked_line = true; next_object.reset(); score = 0; update_score = true; active_scene->resetScene(); active_scene->setPrevTicks( SDL_GetTicks() ); next_object = tetrisFunctions[std::rand() / ( ( RAND_MAX + 1u ) / 7 )]( active_scene->getRendererShared(), active_scene ); next_object->setPos( 0.9, 0.5 ); std::thread inputThread( doInput, active_scene ); inputThread.detach(); } break; case 2: quitGame(); default: break; } default: break; } } void handleKeyDownGameOver( SDL_Keycode key ) { switch ( key ) { case SDLK_r: active_scene->getRenderer().setRenderColiders( !active_scene->getRenderer().getRenderColiders() ); break; case SDLK_s: case SDLK_DOWN: game_over_options[game_over_select]->unsetColor(); game_over_select++; if ( game_over_select > game_over_max ) game_over_select = 0; game_over_options[game_over_select]->setColor( "FFFFFF40" ); break; case SDLK_w: case SDLK_UP: game_over_options[game_over_select]->unsetColor(); game_over_select--; if ( game_over_select < 0 ) game_over_select = game_over_max; game_over_options[game_over_select]->setColor( "FFFFFF40" ); break; case SDLK_RETURN: switch ( game_over_select ) { case 0: { // TODO reset function pause = 0; cur_object.reset(); checked_line = true; next_object.reset(); score = 0; update_score = true; active_scene->resetScene(); active_scene->setPrevTicks( SDL_GetTicks() ); next_object = tetrisFunctions[std::rand() / ( ( RAND_MAX + 1u ) / 7 )]( active_scene->getRendererShared(), active_scene ); next_object->setPos( 0.9, 0.5 ); std::thread inputThread( doInput, active_scene ); inputThread.detach(); } break; case 1: quitGame(); default: break; } default: break; } } void pollEvents( SDLPP::Scene &scene ) { SDL_Event event; while ( SDL_PollEvent( &event ) != 0 ) { switch ( event.type ) { case SDL_QUIT: quitGame(); break; case SDL_KEYDOWN: if ( !event.key.repeat ) handleKeyDown( event.key.keysym.sym, scene ); break; case SDL_KEYUP: handleKeyUp( event.key.keysym.sym ); break; case SDL_WINDOWEVENT: if ( event.window.event == SDL_WINDOWEVENT_RESIZED ) scene.updateSizeAndPosition(); default: break; } } } void pollEventsPause() { SDL_Event event; while ( SDL_PollEvent( &event ) != 0 ) { switch ( event.type ) { case SDL_QUIT: quitGame(); break; case SDL_KEYDOWN: if ( !event.key.repeat ) handleKeyDownPause( event.key.keysym.sym ); break; case SDL_WINDOWEVENT: if ( event.window.event == SDL_WINDOWEVENT_RESIZED ) { active_scene->updateSizeAndPosition(); pause_scene->updateSizeAndPosition(); } default: break; } } } void pollEventsGameOver() { SDL_Event event; while ( SDL_PollEvent( &event ) != 0 ) { switch ( event.type ) { case SDL_QUIT: quitGame(); break; case SDL_KEYDOWN: if ( !event.key.repeat ) handleKeyDownGameOver( event.key.keysym.sym ); break; case SDL_WINDOWEVENT: if ( event.window.event == SDL_WINDOWEVENT_RESIZED ) { active_scene->updateSizeAndPosition(); game_over_scene->updateSizeAndPosition(); } default: break; } } } void moveThem( std::shared_ptr< SDLPP::Scene > scene, int ticks ) { auto movement = cur_object->getMovement(); ticks_till_fall -= ticks; if ( cur_object->isDescending() ) ticks_till_descend -= ticks; if ( cur_object->isMoving() ) ticks_till_movement -= ticks; if ( ticks_till_fall > 0 ) { if ( cur_object->isDescending() && ticks_till_descend <= 0 ) { ticks_till_descend = TICKS_TILL_DESCEND; cur_object->movePiece(0, movement.second * BLOCK_SIZE); if(!validPos(*scene, cur_object)) { cur_object->movePiece(0, movement.second * -BLOCK_SIZE); return; } else goto check_floor; } if ( cur_object->isMoving() && ticks_till_movement <= 0 ) { ticks_till_movement = TICKS_TILL_MOVE; cur_object->movePiece(movement.first * BLOCK_SIZE, 0); if(!validPos(*scene, cur_object)) { cur_object->movePiece(movement.first * -BLOCK_SIZE, 0); return; } else goto check_floor; } return; } ticks_till_fall = TICKS_TILL_FALL; cur_object->movePiece(0, BLOCK_SIZE); check_floor: bool fell = false; for ( auto &x : cur_object->getObjects() ) { auto collisions = scene->getCollisions( *x, { BRICK_ID, FLOOR_ID } ); if ( collisions.size() > 1 ) { fell = true; break; } } if ( fell ) { cur_object->movePiece(0, -BLOCK_SIZE); for ( auto &block : cur_object->getObjects() ) { if ( scene->getCollisions( *block, { GAME_OVER } ).size() > 0 ) { pause = PAUSE_GAME_OVER; game_over_scene->updateSizeAndPosition(); std::thread pauseThread( doInputGameOver ); pauseThread.detach(); } } cur_object.reset(); } updateShadow(*scene); } void doInput( std::shared_ptr< SDLPP::Scene > scene ) { FPSmanager gFPS; SDL_initFramerate( &gFPS ); SDL_setFramerate( &gFPS, 200 ); auto base = SDL_GetTicks(); while ( !quit && !pause ) { base = SDL_GetTicks(); SDL_framerateDelay( &gFPS ); std::lock_guard< std::mutex > guard( movement_mutex ); pollEvents( *scene ); if ( cur_object ) { moveThem( scene, SDL_GetTicks() - base ); continue; } for ( auto &colider : line_coliders ) { auto collisions = scene->getCollisions( *colider, { BRICK_ID } ); while ( collisions.size() == 10 ) { score += 10; update_score = true; for ( auto &col : collisions ) { col->destroy(); } auto colider_y = colider->getPos().second; for ( auto &elem : scene->getObjects() ) { if ( elem->getId() != BRICK_ID ) continue; auto pos = elem->getPos(); if ( pos.second < colider_y && pos.first >= LEFT_BORDER && pos.first <= RIGHT_BORDER ) { elem->setPos( pos.first, pos.second + BLOCK_SIZE ); } } using namespace std::chrono_literals; wait_for_anim = true; while ( wait_for_anim ) { std::this_thread::sleep_for( 0.1s ); } collisions = scene->getCollisions( *colider, { BRICK_ID } ); } } checked_line = true; } } void doInputPause() { FPSmanager gFPS; SDL_initFramerate( &gFPS ); SDL_setFramerate( &gFPS, 200 ); while ( pause ) { SDL_framerateDelay( &gFPS ); pollEventsPause(); if ( !pause ) break; } } void doInputGameOver() { FPSmanager gFPS; SDL_initFramerate( &gFPS ); SDL_setFramerate( &gFPS, 200 ); while ( pause ) { SDL_framerateDelay( &gFPS ); pollEventsGameOver(); if ( !pause ) break; } } int main() { SDLPP::init(); SDLPP::Window w( "Tetris clone!" ); w.setResizable(true); auto renderer = std::make_shared< SDLPP::Renderer >( w ); active_renderer = renderer; renderer->setBlendMode( SDL_BLENDMODE_BLEND ); auto main_scene = std::make_shared< SDLPP::Scene >( renderer ); active_scene = main_scene; font = std::make_shared< SDLPP::Font >( "testfont.ttf", 96 ); addStuff( *main_scene, renderer ); main_scene->saveScene(); pause_scene = std::make_shared< SDLPP::Scene >( renderer ); addPause( *pause_scene, renderer ); game_over_scene = std::make_shared(renderer); addGameOver(*game_over_scene, renderer); auto base = SDL_GetTicks(); int frames = 0; std::srand( std::time( nullptr ) ); FPSmanager gFPS; SDL_initFramerate( &gFPS ); SDL_setFramerate( &gFPS, 60 ); std::thread inputThread( doInput, main_scene ); inputThread.detach(); next_object = tetrisFunctions[std::rand() / ( ( RAND_MAX + 1u ) / 7 )]( renderer, main_scene ); next_object->setPos( 0.9, 0.5 ); while ( !quit ) { SDL_framerateDelay( &gFPS ); if ( !cur_object && checked_line ) { std::lock_guard< std::mutex > guard( movement_mutex ); cur_object = next_object; cur_object->setPos( 0.5, TOP_BORDER - BLOCK_SIZE ); cur_shadow = cur_object->copySelf(); cur_shadow->turnIntoShadow(); for(auto &piece : cur_shadow->getObjects()) { active_scene->moveZ(piece, -4); } updateShadow(*main_scene); auto rand_index = std::rand() / ( ( RAND_MAX + 1u ) / 7 ); int retries = 0; while ( bag[rand_index] < 4 ) { rand_index = ( rand_index + 1 ) % 7; retries++; if ( retries == 7 ) quitGame(); } next_object.reset(); next_object = tetrisFunctions[rand_index]( renderer, main_scene ); next_object->setPos( 0.9, 0.5 ); checked_line = false; } if ( update_score ) { updateScore(); update_score = false; } main_scene->renderScene(); if ( pause == PAUSE_PAUSE ) { pause_scene->renderScene( false ); } else if ( pause == PAUSE_GAME_OVER ) { game_over_scene->renderScene( false ); } main_scene->presentScene(); wait_for_anim = false; frames++; if ( SDL_GetTicks() - base >= 1000 ) { base = SDL_GetTicks(); std::cout << "FPS: " << frames << std::endl; frames = 0; } } return 0; }