#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> pause_options; std::shared_ptr score_texture; std::shared_ptr active_renderer; int score = 0; bool update_score = false; std::shared_ptr font; std::shared_ptr active_scene; std::shared_ptr pause_scene; class TetrisPiece { public: void addPiece(std::shared_ptr 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> &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> pieces_rel_position; std::vector> pieces; double default_x; double default_y; bool descend = false; }; std::vector> line_coliders; TetrisPiece cur_object; TetrisPiece next_object; void doInput(std::shared_ptr scene); void doInputPause(); bool quit = false; std::mutex movement_mutex; std::shared_ptr createTetrisBlock(double x, double y, const std::string &color, const std::string &outline, std::shared_ptr renderer, std::shared_ptr scene) { auto ret = std::make_shared(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 renderer, std::shared_ptr 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 renderer, std::shared_ptr 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 renderer, std::shared_ptr 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 renderer, std::shared_ptr 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 renderer, std::shared_ptr 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 renderer, std::shared_ptr 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 renderer, std::shared_ptr 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, std::shared_ptr)> tetrisFunctions = { tetrisBrick, tetrisT, tetrisLRight, tetrisZRight, tetrisLine, tetrisLLeft, tetrisZLeft, }; void addStuff(SDLPP::Scene &scene, std::shared_ptr &r) { auto bg = std::make_shared(0,0,10,10,r,"#101090FF", true); bg->setPermanent(true); scene.addObject(bg); auto left_barrier = std::make_shared(0.28,0,0.02,1,r,"#FF000080", true); left_barrier->centerX(); scene.addObject(left_barrier); auto right_barrier = std::make_shared(0.7,0,0.02,1,r,"#FF000080", true); right_barrier->centerX(); scene.addObject(right_barrier); auto bottom_barrier = std::make_shared(0.28,1,0.44,0.02,r,"#FF000080", true); bottom_barrier->centerX(); scene.addObject(bottom_barrier); auto tetris = std::make_shared(0.4, 0, 0.2, 0.1, r); tetris->setTexture(*font, "TETRIS", "#FFFFFF", "#000000", 5); tetris->centerX(); scene.addObject(tetris); auto next = std::make_shared(0.8, 0.35, 0.2, 0.1, r); next->setTexture(*font, "NEXT", "#FFFFFF", "#000000", 5); next->centerX(); scene.addObject(next); double posy = 1; auto gameover = std::make_shared(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(0.8, 0.1, 0.2, 0.1, r); score_text->setTexture(*font, "SCORE", "#FFFFFF", "#000000", 5); score_text->centerX(); scene.addObject(score_text); score_texture = std::make_shared(0.8, 0.2, 0.2, 0.1, r); score_texture->setTexture(*font, std::to_string(score), "#FFFFFF", "#000000", 5); score_texture->centerX(); score_texture->setId(123); scene.addObject(score_texture); for(int i = 0; i < 20; i++) { posy -= 0.04; auto colider = std::make_shared(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->setTexture(*font, std::to_string(score), "#FFFFFF", "#000000", 5); } void addPause(SDLPP::Scene &scene, std::shared_ptr &r) { auto bg = std::make_shared(0,0,10,10,r,"#00000080", true); bg->setId(123); bg->setPermanent(true); scene.addObject(bg); auto y = std::make_shared(0.25, 0.1, 0.5, 0.3, r); y->setTexture(*font, "PAUSED", "#FFFFFF", "#000000", 5); y->setId(0); y->centerX(); scene.addObject(y); auto resume = std::make_shared(0.4, 0.5, 0.2, 0.1, r); resume->setTexture(*font, "Resume", "#FFFFFF", "#000000", 5); resume->setColor("#FFFFFF40"); resume->centerX(); scene.addObject(resume); pause_options.push_back(resume); auto quit = std::make_shared(0.4, 0.7, 0.2, 0.1, r); quit->setTexture(*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 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 scene, int ticks) { std::lock_guard 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 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 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(w); active_renderer = renderer; renderer->setBlendMode(SDL_BLENDMODE_BLEND); auto main_scene = std::make_shared(renderer); active_scene = main_scene; font = std::make_shared("testfont.ttf", 96); addStuff(*main_scene, renderer); pause_scene = std::make_shared(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)/6)](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 guard(movement_mutex); cur_object = next_object; cur_object.setPos(0.5, 0.16); next_object = tetrisFunctions[std::rand()/((RAND_MAX + 1u)/6)](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(); printf("FPS: %d\n", frames); frames = 0; } } }