#include "sdlpp.hpp" #include #include #include #include #define COLIDER_ID 0x00000001 #define BRICK_ID 0x00000002 #define GAME_OVER 0x00000003 bool pause = false; int pause_select = 0; int pause_max = 1; int ticks_till_next = 1500; int ticks_till_fall = 500; int ticks_till_descend = 50; std::vector< std::shared_ptr< SDLPP::RectangleRender > > pause_options; std::shared_ptr< SDLPP::TextRenderer > score_texture; std::shared_ptr< SDLPP::Renderer > active_renderer; int score = 0; bool update_score = false; std::shared_ptr< SDLPP::Font > font; std::shared_ptr< SDLPP::Scene > active_scene; std::shared_ptr< SDLPP::Scene > pause_scene; class TetrisPiece { public: void addPiece( std::shared_ptr< SDLPP::RectangleRender > 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() { for ( unsigned long i = 0; i < pieces.size(); i++ ) { auto &piece = pieces[i]; auto &positions = pieces_rel_position[i]; auto position = piece->getPos(); position.first += positions[0] * 0.04; position.first -= positions[1] * 0.04; position.second += positions[2] * 0.04; position.second -= positions[3] * 0.04; 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] * 0.04; position.first += positions[1] * 0.04; position.second -= positions[2] * 0.04; position.second += positions[3] * 0.04; piece->setPos( position.first, position.second ); } } std::vector< std::shared_ptr< SDLPP::RectangleRender > > &getObjects() { return pieces; } void setPos( double x, double y ) { for ( unsigned long i = 0; i < pieces.size(); i++ ) { auto &piece = pieces[i]; auto pos = piece->getPos(); piece->setPos( x + pos.first - default_x, y + pos.second - default_y ); } setDefPos( x, y ); } void setDefPos( double x, double y ) { default_x = x; default_y = y; } void clear() { pieces.clear(); pieces_rel_position.clear(); } void startDescend() { descend = true; } void stopDescend() { descend = false; } bool isDescending() { return descend; } private: std::vector< std::vector< int > > pieces_rel_position; std::vector< std::shared_ptr< SDLPP::RectangleRender > > pieces; double default_x; double default_y; bool descend = false; }; std::vector< std::shared_ptr< SDLPP::RectangleRender > > line_coliders; TetrisPiece cur_object; TetrisPiece next_object; void doInput( std::shared_ptr< SDLPP::Scene > scene ); void doInputPause(); bool quit = false; std::mutex movement_mutex; std::shared_ptr< SDLPP::RectangleRender > createTetrisBlock( double x, double y, const std::string &color, const std::string &outline, std::shared_ptr< SDLPP::Renderer > renderer, std::shared_ptr< SDLPP::Scene > scene ) { auto ret = std::make_shared< SDLPP::RectangleRender >( x, y, 0.04, 0.04, renderer, color, true ); 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; } TetrisPiece tetrisBrick( std::shared_ptr< SDLPP::Renderer > renderer, std::shared_ptr< SDLPP::Scene > scene ) { TetrisPiece retPiece{}; auto color = "#FF0000"; auto outline = "#AA0000"; retPiece.addPiece( createTetrisBlock( 0.46, 0.16, color, outline, renderer, scene ), 0, 0 ); retPiece.addPiece( createTetrisBlock( 0.5, 0.16, color, outline, renderer, scene ), 0, 0 ); retPiece.addPiece( createTetrisBlock( 0.46, 0.20, color, outline, renderer, scene ), 0, 0 ); retPiece.addPiece( createTetrisBlock( 0.5, 0.20, color, outline, renderer, scene ), 0, 0 ); retPiece.setDefPos( 0.5, 0.16 ); return retPiece; } TetrisPiece tetrisT( std::shared_ptr< SDLPP::Renderer > renderer, std::shared_ptr< SDLPP::Scene > scene ) { TetrisPiece retPiece{}; auto color = "#00FF00"; auto outline = "#00AA00"; retPiece.addPiece( createTetrisBlock( 0.46, 0.20, color, outline, renderer, scene ), -1, 0 ); retPiece.addPiece( createTetrisBlock( 0.5, 0.20, color, outline, renderer, scene ), 0, 0 ); retPiece.addPiece( createTetrisBlock( 0.5, 0.16, color, outline, renderer, scene ), 0, -1 ); retPiece.addPiece( createTetrisBlock( 0.54, 0.20, color, outline, renderer, scene ), 1, 0 ); retPiece.setDefPos( 0.5, 0.16 ); return retPiece; } TetrisPiece tetrisLRight( std::shared_ptr< SDLPP::Renderer > renderer, std::shared_ptr< SDLPP::Scene > scene ) { TetrisPiece retPiece{}; auto color = "#0000FF"; auto outline = "#0000AA"; retPiece.addPiece( createTetrisBlock( 0.46, 0.20, color, outline, renderer, scene ), -2, 0 ); retPiece.addPiece( createTetrisBlock( 0.5, 0.20, color, outline, renderer, scene ), -1, 0 ); retPiece.addPiece( createTetrisBlock( 0.54, 0.20, color, outline, renderer, scene ), 0, 0 ); retPiece.addPiece( createTetrisBlock( 0.54, 0.16, color, outline, renderer, scene ), 0, -1 ); retPiece.setDefPos( 0.5, 0.16 ); return retPiece; } TetrisPiece tetrisZRight( std::shared_ptr< SDLPP::Renderer > renderer, std::shared_ptr< SDLPP::Scene > scene ) { TetrisPiece retPiece{}; auto color = "#FF00FF"; auto outline = "#AA00AA"; retPiece.addPiece( createTetrisBlock( 0.46, 0.20, color, outline, renderer, scene ), -1, 0 ); retPiece.addPiece( createTetrisBlock( 0.5, 0.20, color, outline, renderer, scene ), 0, 0 ); retPiece.addPiece( createTetrisBlock( 0.5, 0.16, color, outline, renderer, scene ), 0, -1 ); retPiece.addPiece( createTetrisBlock( 0.54, 0.16, color, outline, renderer, scene ), 1, -1 ); retPiece.setDefPos( 0.5, 0.16 ); return retPiece; } TetrisPiece tetrisLine( std::shared_ptr< SDLPP::Renderer > renderer, std::shared_ptr< SDLPP::Scene > scene ) { TetrisPiece retPiece{}; auto color = "#FFFF00"; auto outline = "#AAAA00"; retPiece.addPiece( createTetrisBlock( 0.42, 0.16, color, outline, renderer, scene ), -1, 0 ); retPiece.addPiece( createTetrisBlock( 0.46, 0.16, color, outline, renderer, scene ), 0, 0 ); retPiece.addPiece( createTetrisBlock( 0.5, 0.16, color, outline, renderer, scene ), 1, 0 ); retPiece.addPiece( createTetrisBlock( 0.54, 0.16, color, outline, renderer, scene ), 2, 0 ); retPiece.setDefPos( 0.5, 0.16 ); return retPiece; } TetrisPiece tetrisLLeft( std::shared_ptr< SDLPP::Renderer > renderer, std::shared_ptr< SDLPP::Scene > scene ) { TetrisPiece retPiece{}; auto color = "#00FFFF"; auto outline = "#00AAAA"; retPiece.addPiece( createTetrisBlock( 0.46, 0.16, color, outline, renderer, scene ), 0, -1 ); retPiece.addPiece( createTetrisBlock( 0.46, 0.20, color, outline, renderer, scene ), 0, 0 ); retPiece.addPiece( createTetrisBlock( 0.5, 0.20, color, outline, renderer, scene ), 1, 0 ); retPiece.addPiece( createTetrisBlock( 0.54, 0.20, color, outline, renderer, scene ), 2, 0 ); retPiece.setDefPos( 0.5, 0.16 ); return retPiece; } TetrisPiece tetrisZLeft( std::shared_ptr< SDLPP::Renderer > renderer, std::shared_ptr< SDLPP::Scene > scene ) { TetrisPiece retPiece{}; auto color = "#FFFFFF"; auto outline = "#AAAAAA"; retPiece.addPiece( createTetrisBlock( 0.46, 0.16, color, outline, renderer, scene ), -1, 0 ); retPiece.addPiece( createTetrisBlock( 0.5, 0.16, color, outline, renderer, scene ), 0, 0 ); retPiece.addPiece( createTetrisBlock( 0.5, 0.20, color, outline, renderer, scene ), 0, 1 ); retPiece.addPiece( createTetrisBlock( 0.54, 0.20, color, outline, renderer, scene ), 1, 1 ); retPiece.setDefPos( 0.5, 0.16 ); return retPiece; } std::vector< 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, "#101090FF", true ); bg->setPermanent( true ); scene.addObject( bg ); auto left_barrier = std::make_shared< SDLPP::RectangleRender >( 0.28, 0, 0.02, 1, r, "#FF000080", true ); left_barrier->centerX(); scene.addObject( left_barrier ); auto right_barrier = std::make_shared< SDLPP::RectangleRender >( 0.7, 0, 0.02, 1, r, "#FF000080", true ); right_barrier->centerX(); scene.addObject( right_barrier ); auto bottom_barrier = std::make_shared< SDLPP::RectangleRender >( 0.28, 1, 0.44, 0.02, r, "#FF000080", true ); bottom_barrier->centerX(); 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(); scene.addObject( tetris ); auto next = std::make_shared< SDLPP::TextRenderer >( 0.8, 0.35, 0.2, 0.1, r, *font, "NEXT", "FFFFFF", "000000", 5, SDLPP_TEXT_CENTER ); next->centerX(); scene.addObject( next ); double posy = 1; auto gameover = std::make_shared< SDLPP::RectangleRender >( 0.5, 0, 0, 0.195, r ); auto gameover_collision = SDLPP::Rect( -1, 0, -1, 1 ); gameover_collision.setInfinite(); gameover->addCollision( gameover_collision ); gameover->setId( GAME_OVER ); gameover->setColiderColor( "FF0000" ); scene.addObject( gameover ); auto score_text = std::make_shared< SDLPP::TextRenderer >( 0.8, 0.1, 0.2, 0.1, r, *font, "SCORE", "#FFFFFF", "#000000", 5, SDLPP_TEXT_CENTER ); score_text->centerX(); scene.addObject( score_text ); score_texture = std::make_shared< SDLPP::TextRenderer >( 0.8, 0.2, 0.2, 0.1, r, *font, std::to_string( score ), "FFFFFF", "000000", 5, SDLPP_TEXT_TOP ); score_texture->centerX(); scene.addObject( score_texture ); for ( int i = 0; i < 20; i++ ) { posy -= 0.04; auto colider = std::make_shared< SDLPP::RectangleRender >( 0.3, posy, 0.04, 0.04, r ); auto colider_colider = SDLPP::Rect( -1, 0.1, -1, 0.8 ); colider_colider.setInfinite(); colider->addCollision( colider_colider ); colider->setId( COLIDER_ID ); line_coliders.push_back( colider ); scene.addObject( colider ); } } 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.5, 0.2, 0.1, r ); resume->setText( *font, "Resume", "#FFFFFF", "#000000", 5 ); resume->setColor( "#FFFFFF40" ); resume->centerX(); scene.addObject( resume ); pause_options.push_back( resume ); 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 ); pause_options.push_back( quit ); } void quitGame() { std::cout << "Quitting!" << std::endl; quit = true; } void handleKeyDown( SDL_Keycode key, SDLPP::Scene &scene ) { bool crash = false; std::lock_guard< std::mutex > guard( movement_mutex ); switch ( key ) { case SDLK_ESCAPE: { pause = true; pause_scene->updateSizeAndPosition(); std::thread pauseThread( doInputPause ); pauseThread.detach(); } break; case SDLK_LEFT: case SDLK_a: for ( auto &x : cur_object.getObjects() ) { auto pos = x->getPos(); // 0.31 because doubles if ( pos.first < 0.31 ) crash = true; x->setPos( pos.first - 0.04, pos.second ); } for ( auto &x : cur_object.getObjects() ) { auto collisions = scene.getCollisions( *x, { BRICK_ID } ); if ( collisions.size() > 1 ) crash = true; } if ( crash ) { for ( auto &x : cur_object.getObjects() ) { auto pos = x->getPos(); x->setPos( pos.first + 0.04, pos.second ); } } break; case SDLK_RIGHT: case SDLK_d: for ( auto &x : cur_object.getObjects() ) { auto pos = x->getPos(); // 0.65 because doubles if ( pos.first > 0.65 ) { crash = true; } x->setPos( pos.first + 0.04, pos.second ); } for ( auto &x : cur_object.getObjects() ) { auto collisions = scene.getCollisions( *x, { BRICK_ID } ); if ( collisions.size() > 1 ) { crash = true; } } if ( crash ) { for ( auto &x : cur_object.getObjects() ) { auto pos = x->getPos(); x->setPos( pos.first - 0.04, pos.second ); } } break; case SDLK_DOWN: case SDLK_s: cur_object.startDescend(); break; case SDLK_UP: case SDLK_w: cur_object.rotate(); break; case SDLK_r: scene.getRenderer().setRenderColiders( !scene.getRenderer().getRenderColiders() ); default: break; } } void handleKeyUp( SDL_Keycode key ) { if ( key == SDLK_DOWN || key == SDLK_s ) { cur_object.stopDescend(); } } void handleKeyDownPause( SDL_Keycode key ) { switch ( key ) { case SDLK_ESCAPE: { pause = false; 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 = false; active_scene->setPrevTicks( SDL_GetTicks() ); 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 moveThem( std::shared_ptr< SDLPP::Scene > scene, int ticks ) { std::lock_guard< std::mutex > guard( movement_mutex ); ticks_till_fall -= ticks; if ( cur_object.isDescending() ) ticks_till_descend -= ticks; if ( ticks_till_fall > 0 ) { if ( cur_object.isDescending() && ticks_till_descend <= 0 ) { ticks_till_descend = 50; goto fall; } return; } ticks_till_fall = 500; fall: for ( auto &x : cur_object.getObjects() ) { auto pos = x->getPos(); x->setPos( pos.first, pos.second + 0.04 ); } bool fell = false; for ( auto &x : cur_object.getObjects() ) { auto collisions = scene->getCollisions( *x, { BRICK_ID } ); if ( collisions.size() > 1 ) { fell = true; break; } if ( x->getPos().second >= 1 ) { fell = true; break; } } if ( fell ) { for ( auto &x : cur_object.getObjects() ) { auto pos = x->getPos(); x->setPos( pos.first, pos.second - 0.04 ); } for ( auto &block : cur_object.getObjects() ) { if ( scene->getCollisions( *block, { GAME_OVER } ).size() > 0 ) { std::cout << "You lost" << std::endl; quitGame(); } } cur_object.clear(); } } 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 ); pollEvents( *scene ); scene->movement(); if ( cur_object.getObjects().size() != 0 ) { moveThem( scene, SDL_GetTicks() - base ); continue; } std::lock_guard< std::mutex > guard( movement_mutex ); for ( auto &colider : line_coliders ) { auto collisions = scene->getCollisions( *colider, { BRICK_ID } ); while ( collisions.size() == 10 ) { score += 10; // updateScore(); 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 ) { elem->setPos( pos.first, pos.second + 0.04 ); } } using namespace std::chrono_literals; std::this_thread::sleep_for( 0.1s ); collisions = scene->getCollisions( *colider, { BRICK_ID } ); } } } } void doInputPause() { FPSmanager gFPS; SDL_initFramerate( &gFPS ); SDL_setFramerate( &gFPS, 200 ); while ( pause ) { SDL_framerateDelay( &gFPS ); pollEventsPause(); if ( !pause ) break; } } int main() { SDLPP::init(); SDLPP::Window w( "Tetris clone!" ); 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 ); pause_scene = std::make_shared< SDLPP::Scene >( renderer ); addPause( *pause_scene, renderer ); int 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.getObjects().size() != 0 ) { ticks_till_next = 1500; } else { ticks_till_next -= SDL_GetTicks() - base; if ( ticks_till_next <= 0 && cur_object.getObjects().size() == 0 ) { std::lock_guard< std::mutex > guard( movement_mutex ); cur_object = next_object; cur_object.setPos( 0.5, 0.16 ); next_object = tetrisFunctions[std::rand() / ( ( RAND_MAX + 1u ) / 7 )]( renderer, main_scene ); next_object.setPos( 0.9, 0.5 ); } } if ( update_score ) { updateScore(); update_score = false; } main_scene->renderScene(); if ( pause ) { pause_scene->renderScene( false ); } main_scene->presentScene(); frames++; if ( SDL_GetTicks() - base >= 1000 ) { base = SDL_GetTicks(); std::cout << "FPS: " << frames << std::endl; frames = 0; } } }