From ed7e15946fc989df544c5b21812dd2c78806d50f Mon Sep 17 00:00:00 2001 From: Rene Kievits Date: Wed, 16 Oct 2024 04:59:04 +0200 Subject: [PATCH] mainly refractoring, some fixes --- assets/{ => font}/tetris-gb.ttf | Bin assets/{ => sound_tracks}/bgm.mp3 | Bin assets/{ => sprites}/I_END.png | Bin assets/{ => sprites}/I_ENDR.png | Bin assets/{ => sprites}/I_MID.png | Bin assets/{ => sprites}/I_MIDR.png | Bin assets/{ => sprites}/I_START.png | Bin assets/{ => sprites}/I_STARTR.png | Bin assets/{ => sprites}/J.png | Bin assets/{ => sprites}/L.png | Bin assets/{ => sprites}/O.png | Bin assets/{ => sprites}/S.png | Bin assets/{ => sprites}/T.png | Bin assets/{ => sprites}/Z.png | Bin assets/{ => sprites}/border.png | Bin assets/sprites/game_over.png | Bin 0 -> 3625 bytes assets/sprites/please_try_again_text.png | Bin 0 -> 1440 bytes assets/{ => sprites}/scoreboard.png | Bin assets/{ => sprites}/single.png | Bin assets/{ => sprites}/title.png | Bin assets/{ => sprites}/title_bg.png | Bin src/Game.cpp | 74 +-- src/Game.hpp | 13 +- src/GameBoard.cpp | 18 +- src/GameBoard.hpp | 6 +- src/Renderer.cpp | 545 +++++++++++------------ src/Renderer.hpp | 61 ++- 27 files changed, 354 insertions(+), 363 deletions(-) rename assets/{ => font}/tetris-gb.ttf (100%) rename assets/{ => sound_tracks}/bgm.mp3 (100%) rename assets/{ => sprites}/I_END.png (100%) rename assets/{ => sprites}/I_ENDR.png (100%) rename assets/{ => sprites}/I_MID.png (100%) rename assets/{ => sprites}/I_MIDR.png (100%) rename assets/{ => sprites}/I_START.png (100%) rename assets/{ => sprites}/I_STARTR.png (100%) rename assets/{ => sprites}/J.png (100%) rename assets/{ => sprites}/L.png (100%) rename assets/{ => sprites}/O.png (100%) rename assets/{ => sprites}/S.png (100%) rename assets/{ => sprites}/T.png (100%) rename assets/{ => sprites}/Z.png (100%) rename assets/{ => sprites}/border.png (100%) create mode 100644 assets/sprites/game_over.png create mode 100644 assets/sprites/please_try_again_text.png rename assets/{ => sprites}/scoreboard.png (100%) rename assets/{ => sprites}/single.png (100%) rename assets/{ => sprites}/title.png (100%) rename assets/{ => sprites}/title_bg.png (100%) diff --git a/assets/tetris-gb.ttf b/assets/font/tetris-gb.ttf similarity index 100% rename from assets/tetris-gb.ttf rename to assets/font/tetris-gb.ttf diff --git a/assets/bgm.mp3 b/assets/sound_tracks/bgm.mp3 similarity index 100% rename from assets/bgm.mp3 rename to assets/sound_tracks/bgm.mp3 diff --git a/assets/I_END.png b/assets/sprites/I_END.png similarity index 100% rename from assets/I_END.png rename to assets/sprites/I_END.png diff --git a/assets/I_ENDR.png b/assets/sprites/I_ENDR.png similarity index 100% rename from assets/I_ENDR.png rename to assets/sprites/I_ENDR.png diff --git a/assets/I_MID.png b/assets/sprites/I_MID.png similarity index 100% rename from assets/I_MID.png rename to assets/sprites/I_MID.png diff --git a/assets/I_MIDR.png b/assets/sprites/I_MIDR.png similarity index 100% rename from assets/I_MIDR.png rename to assets/sprites/I_MIDR.png diff --git a/assets/I_START.png b/assets/sprites/I_START.png similarity index 100% rename from assets/I_START.png rename to assets/sprites/I_START.png diff --git a/assets/I_STARTR.png b/assets/sprites/I_STARTR.png similarity index 100% rename from assets/I_STARTR.png rename to assets/sprites/I_STARTR.png diff --git a/assets/J.png b/assets/sprites/J.png similarity index 100% rename from assets/J.png rename to assets/sprites/J.png diff --git a/assets/L.png b/assets/sprites/L.png similarity index 100% rename from assets/L.png rename to assets/sprites/L.png diff --git a/assets/O.png b/assets/sprites/O.png similarity index 100% rename from assets/O.png rename to assets/sprites/O.png diff --git a/assets/S.png b/assets/sprites/S.png similarity index 100% rename from assets/S.png rename to assets/sprites/S.png diff --git a/assets/T.png b/assets/sprites/T.png similarity index 100% rename from assets/T.png rename to assets/sprites/T.png diff --git a/assets/Z.png b/assets/sprites/Z.png similarity index 100% rename from assets/Z.png rename to assets/sprites/Z.png diff --git a/assets/border.png b/assets/sprites/border.png similarity index 100% rename from assets/border.png rename to assets/sprites/border.png diff --git a/assets/sprites/game_over.png b/assets/sprites/game_over.png new file mode 100644 index 0000000000000000000000000000000000000000..08e28a51df760c13652c7ac0ee80af179714ecb3 GIT binary patch literal 3625 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 literal 0 HcmV?d00001 diff --git a/assets/sprites/please_try_again_text.png b/assets/sprites/please_try_again_text.png new file mode 100644 index 0000000000000000000000000000000000000000..2cce8ed2684e316c2f7996545205b984bc887ee7 GIT binary patch literal 1440 zcmV;R1z-A!P)PRgn}$y8&xt!+_z26Il|sdi7XlEiv$yLRglR zLySw9mxVh$nWEn5FY_8NA*i7Oo8h}AFg~`$4F^WGX`ep5a#ZkkChji z1ZHCT80KkieREFBGOI__htB+G)2wa!_3Zg>lO(2{HDXzm3tVT?Lsph)F|7iP00=lf zZPESvgyi#V1BNId5PxzkynV`!$|wCpj!OpnTL?UY;{ZrqtOX%_kvY5glWRP2Z|g1h zXM^xwT9-~zhAA_*Wh@^*E%V~q%4j-d@g%qLeGc$rRyA5s^U^UOCL@J2hv6AP0A2)p zkM^6!nSRl_O)HEmtT)<>ObH1>x2Ua#X%0hs%5NBg27O04y=?>NZax^$3?*iI-d$?8$JWu<^KH^(0-oKWX`}`1j?Yh(i=m; zs>()jEyEx_Fu)ml%fCi>`1tuAma@+yF=L<>zB<;fd5XDi^Rhq5>iqR`& zvA6U`%To_HS~ek#gGTq&2=YQZEQ@BW!O3Ks@-QHW2e_p+9x+yw!IN>r24Hvim9hPb zGI(xA&^IH^MBSP9{3MwzkWrX1>ac_NGGdS@R)a@Fj2ivTK#DSE=_5b3Xj7W={{D+2 z>nHUyuGqx<`sHC_+MDg;nz>FqmqBJ6v7#8(Bfz~Fa0I<)==);8GidRM3@-*8LGKy* zz8LTfT0A1dl?>oyo^XeC%;i+1L#UtS%TbiWk&dbE0P>IHvJOXeVRE&Kp-UUnSxgsg%P8aWu)8R66G%U`MPJ8rR{dq-3H1i#to4sJ{ z0su+z`4DS#V|9F$is$0ynPFiH>|xp3Ri%E_sz+U>OE&%Smm`99%1pr&Wzxw6X&sPAw zgt#r3!>^-7f7-Rg5n5o>4Wt_ zyU#beUT750QtV`SY7*A7jbmNHaRAIecA>Lpllmm8t-66ib|?}o!FBX`yS}OsPfFj$DS^xAJ~kfe@Be;<_wVoF^zjki{`z7C9H)PO$ZctI9|j;J z1Tq&VsN_x?3lKN~$b~ex1JI7ey7LtM*tV3KjLh<*wu%KR2m=EE^w-ay;qUkFfTx75 z=kGs0P8*O91EAsxxyUo>j8Y%pYjK1YN%qjZLPBVzhCtyWPorD4h`!+(Sk;TlV1_NC z$5x#)0Hf%X>IbZNLK$QACCs+QA=2{o+nZ;a#X}3iEa7xIt&AK|EtK1LaSizvuu>aE zJ*t2n8mqNMTC^mo`%s|NxT1nB7E3%2lbHfX09w+u0to<4#LmVr=C`*mwmh=7Cj984 zA7((y?g{|F&}wUJ(6^$DoFKuZ)g4;vg-WK^+9}>>=42H>RRna_<|qp63b#u3xYO3w z-jo-ltpVX{_;LDYqxstJFaZ1L@}Slw#eZq`qX4vmj?W)nPh58R=qt!?9gE;#D0000(renderer, ww, wh); gameBoard = make_shared( ); handleWindowResize( ); - bgm = std::shared_ptr(Mix_LoadMUS("assets/bgm.mp3"), [ ](Mix_Music* m) { + bgm = std::shared_ptr(Mix_LoadMUS("assets/sound_tracks/bgm.mp3"), [ ](Mix_Music* m) { Mix_FreeMusic(m); }); @@ -49,8 +47,8 @@ bool Game::init(const char* title, int w, int h) { } void Game::run( ) { - while (startSequence) { - if (quit) return; + while (gameState.startSequence) { + if (gameState.quit) return; inputHandler( ); gameRenderer->renderStartScreen( ); } @@ -58,27 +56,26 @@ void Game::run( ) { Mix_PlayMusic(bgm.get( ), -1); lastUpdateTime = SDL_GetTicks( ); - while (!gameOver && !gameBoard->isCollision( )) { - if (quit) return; + while (!gameState.gameover && !gameBoard->isCollision( )) { + if (gameState.quit) return; inputHandler( ); update( ); render( ); } - gameOver = true; + gameState.gameover = true; Mix_PauseMusic( ); - Mix_Chunk* rotateSound = Mix_LoadWAV("assets/sound_effects/game_over.wav"); - if (rotateSound == nullptr) + Mix_Chunk* game_over = Mix_LoadWAV("assets/sound_effects/game_over.wav"); + if (game_over == nullptr) SDL_Log("Failed to play rotate sound effect: %s", Mix_GetError( )); else { - Mix_PlayChannel(-1, rotateSound, 0); + Mix_PlayChannel(-1, game_over, 0); } - while (gameOver) { - if (quit) return; + while (gameState.gameover) { + if (gameState.quit) return; inputHandler( ); - render( ); - gameRenderer->renderGameOver(gameBoard->getScore( ), gameBoard->getLevel( ), gameBoard->getLines( )); + gameRenderer->renderGameOver(gameBoard->getWidth( ), gameBoard->getHeight( )); } } @@ -88,12 +85,13 @@ void Game::inputHandler( ) { while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { SDL_Quit( ); - quit = true; + gameState.quit = true; } else if (event.type == SDL_KEYDOWN) { switch (event.key.keysym.sym) { case SDLK_LEFT: case SDLK_a: - gameBoard->tryMoveCurrentTetromino(-1, 0); + if (!gameState.gameover && !gameState.startSequence) + gameBoard->tryMoveCurrentTetromino(-1, 0); if (movePieceSound == nullptr) SDL_Log("Failed to play rotate sound effect: %s", Mix_GetError( )); else { @@ -102,7 +100,8 @@ void Game::inputHandler( ) { break; case SDLK_RIGHT: case SDLK_d: - gameBoard->tryMoveCurrentTetromino(1, 0); + if (!gameState.gameover && !gameState.startSequence) + gameBoard->tryMoveCurrentTetromino(1, 0); if (movePieceSound == nullptr) SDL_Log("Failed to play rotate sound effect: %s", Mix_GetError( )); else { @@ -111,16 +110,18 @@ void Game::inputHandler( ) { break; case SDLK_DOWN: case SDLK_s: - gameBoard->moveToBottom( ); + if (!gameState.gameover && !gameState.startSequence) + gameBoard->moveToBottom( ); break; case SDLK_SPACE: - gameBoard->tryRotateCurrentTetromino( ); + if (!gameState.gameover && !gameState.startSequence) + gameBoard->tryRotateCurrentTetromino( ); break; case SDLK_ESCAPE: break; case SDLK_g: - if (startSequence) { - startSequence = false; + if (gameState.startSequence) { + gameState.startSequence = false; Mix_Chunk* menuSound = Mix_LoadWAV("assets/sound_effects/menu.wav"); if (menuSound == nullptr) SDL_Log("Failed to play rotate sound effect: %s", Mix_GetError( )); @@ -135,7 +136,7 @@ void Game::inputHandler( ) { break; case SDLK_q: SDL_Quit( ); - quit = true; + gameState.quit = true; case SDLK_EQUALS: SDL_Log("Test %d", Mix_GetMusicVolume(bgm.get( ))); Mix_VolumeMusic(Mix_GetMusicVolume(bgm.get( )) + 8); @@ -177,10 +178,10 @@ void Game::handleWindowResize( ) { int windowWidth, windowHeight; SDL_GetWindowSize(window.get( ), &windowWidth, &windowHeight); - gameRenderer->setBlockSize(windowHeight / gameBoard->getLockedTetrominos( ).size( )); + gameRenderer->setBlockSize(windowHeight / gameBoard->getHeight( )); - int offsetX = (windowWidth - gameBoard->getLockedTetrominos( )[0].size( ) * gameRenderer->getBlockSize( )) / 2; - int offsetY = (windowHeight - gameBoard->getLockedTetrominos( ).size( ) * gameRenderer->getBlockSize( )) / 2; + int offsetX = (windowWidth - gameBoard->getWidth( ) * gameRenderer->getBlockSize( )) / 2; + int offsetY = (windowHeight - gameBoard->getHeight( ) * gameRenderer->getBlockSize( )) / 2; gameRenderer->setOffset(offsetX, offsetY); gameRenderer->setWindowSize(windowWidth, windowHeight); @@ -197,23 +198,22 @@ void Game::update( ) { } void Game::render( ) { - //Set default color to black + // Background color SDL_SetRenderDrawColor(renderer.get( ), 248, 248, 248, 255); SDL_RenderClear(renderer.get( )); - gameRenderer->renderBoard(*gameBoard); + gameRenderer->renderBoard(gameBoard); gameRenderer->renderTetrominoPreview(gameBoard->getNextTetromino( )); SDL_RenderPresent(renderer.get( )); } -bool Game::isGameOver( ) { return gameOver; } -void Game::setGameOver(bool value) { gameOver = value; } - void Game::restart( ) { - gameOver = false; - startSequence = true; + gameState.gameover = false; + gameState.startSequence = true; gameBoard = make_shared( ); } -const bool Game::isGameQuit( )const { return quit; } +const bool Game::isGameOver( ) const { return gameState.gameover; } +const void Game::setGameOver(bool value) { gameState.gameover = value; } +const bool Game::isGameQuit( )const { return gameState.quit; } diff --git a/src/Game.hpp b/src/Game.hpp index 1134472..0042aa6 100644 --- a/src/Game.hpp +++ b/src/Game.hpp @@ -31,9 +31,11 @@ private: Uint32 lastUpdateTime = 0; int dropInterval = 1000; - bool gameOver; - bool startSequence; - bool quit; + struct GameState { + bool gameover = false; + bool startSequence = false; + bool quit = false; + } gameState; public: Game( ); @@ -41,8 +43,9 @@ public: bool init(const char* title, int w, int h); void run( ); void restart( ); - bool isGameOver( ); - void setGameOver(bool value); + + const bool isGameOver( ) const; + const void setGameOver(bool value); const bool isGameQuit( ) const; }; diff --git a/src/GameBoard.cpp b/src/GameBoard.cpp index 1ce0705..464a14d 100644 --- a/src/GameBoard.cpp +++ b/src/GameBoard.cpp @@ -7,6 +7,7 @@ GameBoard::GameBoard( ) } bool GameBoard::tryMoveCurrentTetromino(int dx, int dy) { + if (!currentTetromino) return; currentTetromino->move(dx, dy); if (checkCollision(*currentTetromino)) { currentTetromino->move(-dx, -dy); @@ -16,6 +17,7 @@ bool GameBoard::tryMoveCurrentTetromino(int dx, int dy) { } void GameBoard::tryRotateCurrentTetromino( ) { + if (!currentTetromino) return; currentTetromino->rotate(*this); if (checkCollision(*currentTetromino)) for (int i = 0; i < 3; i++) @@ -181,10 +183,15 @@ void GameBoard::spawnNewTetromino( ) { collision = true; lockedTetrominos.clear( ); lockedColors.clear( ); + currentTetromino = nullptr; + nextTetromino = nullptr; } } void GameBoard::update( ) { + if (!currentTetromino) + spawnNewTetromino( ); + if (!tryMoveCurrentTetromino(0, 1)) { lockTetromino( ); clearLines( ); @@ -218,12 +225,7 @@ const shared_ptr GameBoard::getNextTetromino( ) const { return nextTe const vector>& GameBoard::getLockedTetrominos( ) const { return lockedTetrominos; } const vector>& GameBoard::getLockedColors( ) const { return lockedColors; } -const Tetromino& GameBoard::getCurrentTetromino( ) const { return *currentTetromino; } +const shared_ptr GameBoard::getCurrentTetromino( ) const { return currentTetromino; } -void GameBoard::reset( ) { - lockedTetrominos.clear( ); - lockedColors.clear( ); - score = 0; - level = 1; - collision = false; -} +const int GameBoard::getWidth( ) const { return width; } +const int GameBoard::getHeight( ) const { return height; } diff --git a/src/GameBoard.hpp b/src/GameBoard.hpp index c291e79..555cfae 100644 --- a/src/GameBoard.hpp +++ b/src/GameBoard.hpp @@ -34,7 +34,6 @@ public: void tryRotateCurrentTetromino( ); bool isValidPosition(const vector>& shape, int x, int y) const; void moveToBottom( ); - void reset( ); const bool isCollision( ) const; const int getScore( ) const; @@ -44,5 +43,8 @@ public: const vector>& getLockedTetrominos( ) const; const vector>& getLockedColors( ) const; - const Tetromino& getCurrentTetromino( ) const; + const shared_ptr getCurrentTetromino( ) const; + + const int getWidth( ) const; + const int getHeight( ) const; }; diff --git a/src/Renderer.cpp b/src/Renderer.cpp index 3ee3718..0b4e82a 100644 --- a/src/Renderer.cpp +++ b/src/Renderer.cpp @@ -2,147 +2,164 @@ #include Renderer::Renderer(shared_ptr renderer, int w, int h) : renderer(renderer), blockSize(30), windowHeight(h), windowWidth(w) { - setRenderer(renderer); + textures[TetrisAssets::SINGLE] = "assets/sprites/single.png"; + textures[TetrisAssets::BORDER] = "assets/sprites/border.png"; + textures[TetrisAssets::J] = "assets/sprites/J.png"; + textures[TetrisAssets::L] = "assets/sprites/L.png"; + textures[TetrisAssets::T] = "assets/sprites/T.png"; + textures[TetrisAssets::O] = "assets/sprites/O.png"; + textures[TetrisAssets::S] = "assets/sprites/S.png"; + textures[TetrisAssets::Z] = "assets/sprites/Z.png"; + textures[TetrisAssets::I] = "assets/sprites/I.png"; + textures[TetrisAssets::I_END] = "assets/sprites/I_END.png"; + textures[TetrisAssets::I_MID] = "assets/sprites/I_MID.png"; + textures[TetrisAssets::I_START] = "assets/sprites/I_START.png"; + textures[TetrisAssets::I_ENDR] = "assets/sprites/I_ENDR.png"; + textures[TetrisAssets::I_MIDR] = "assets/sprites/I_MIDR.png"; + textures[TetrisAssets::I_STARTR] = "assets/sprites/I_STARTR.png"; } -void Renderer::setRenderer(shared_ptr renderer) { - this->renderer = renderer; - loadTexture("assets/single.png", TetrisAssets::SINGLE); - loadTexture("assets/border.png", TetrisAssets::BORDER); - loadTexture("assets/J.png", TetrisAssets::J); - loadTexture("assets/L.png", TetrisAssets::L); - loadTexture("assets/T.png", TetrisAssets::T); - loadTexture("assets/O.png", TetrisAssets::O); - loadTexture("assets/S.png", TetrisAssets::S); - loadTexture("assets/Z.png", TetrisAssets::Z); - loadTexture("assets/I_MID.png", TetrisAssets::I); - loadTexture("assets/I_END.png", TetrisAssets::I_END); - loadTexture("assets/I_MID.png", TetrisAssets::I_MID); - loadTexture("assets/I_START.png", TetrisAssets::I_START); - loadTexture("assets/I_ENDR.png", TetrisAssets::I_ENDR); - loadTexture("assets/I_MIDR.png", TetrisAssets::I_MIDR); - loadTexture("assets/I_STARTR.png", TetrisAssets::I_STARTR); -} - -void Renderer::renderBoard(const GameBoard& gameBoard) { - drawGrid(gameBoard); +void Renderer::renderBoard(const shared_ptr gameBoard) { + drawWall(gameBoard->getWidth( ), gameBoard->getHeight( )); drawLockedBlocks(gameBoard); - drawTetromino(gameBoard.getCurrentTetromino( )); - drawScoreboard(gameBoard.getScore( ), gameBoard.getLevel( ), gameBoard.getLines( )); + drawTetromino(gameBoard->getCurrentTetromino( )); + drawScoreboard(gameBoard->getScore( ), gameBoard->getLevel( ), gameBoard->getLines( )); } void Renderer::drawScoreboard(int score, int level, int lines) { - int gapSize = blockSize / 8; - // 6 Because the gameBoard is 10 blocks, half that is 5 + 1 for the wall = 6 - int blackAreaWidth = (windowWidth / 2) - (blockSize * 6) - gapSize; - - SDL_Rect blackRect = { 0, 0, blackAreaWidth, windowHeight }; + SDL_Rect blackRect = { 0, 0, (windowWidth / 2) - (blockSize * 6) - blockSize / 8, windowHeight }; SDL_SetRenderDrawColor(renderer.get( ), 0, 0, 0, 255); SDL_RenderFillRect(renderer.get( ), &blackRect); - SDL_Surface* scoreboardSurface = IMG_Load("assets/scoreboard.png"); - if (!scoreboardSurface) { - SDL_Log("Failed to load title surface: %s", SDL_GetError( )); - return; - } + auto surf = unique_ptr(IMG_Load("assets/sprites/scoreboard.png"), SDL_FreeSurface); + renderTexture("assets/sprites/scoreboard.png", windowWidth - surf->w * static_cast(windowHeight) / surf->h, 0, surf->w * static_cast(windowHeight) / surf->h, windowHeight); - auto scoreboardTexture = SDL_CreateTextureFromSurface(renderer.get( ), scoreboardSurface); - SDL_FreeSurface(scoreboardSurface); - if (!scoreboardTexture) { - SDL_Log("Failed to create title texture: %s", SDL_GetError( )); - return; - } - - int width, height; - SDL_QueryTexture(scoreboardTexture, nullptr, nullptr, &width, &height); - - int scoreboardWidth = static_cast(width * static_cast(windowHeight) / height); - - SDL_Rect scoreboardRect = { - windowWidth - scoreboardWidth, - 0, - scoreboardWidth, - windowHeight - }; - - - SDL_RenderCopy(renderer.get( ), scoreboardTexture, nullptr, &scoreboardRect); - SDL_DestroyTexture(scoreboardTexture); - - renderRightAlignedText(fmt::format("{0}", score), windowWidth - 30, 97, 32, SDL_Color{ 0,0,0 }); + renderText(fmt::format("{0}", score), windowWidth - 30, 97, 32, SDL_Color{ 0,0,0 }, HAlign::RIGHT); renderText(fmt::format("{0}", level), windowWidth - 92, 230, 32, SDL_Color{ 0,0,0 }); renderText(fmt::format("{0}", lines), windowWidth - 92, 330, 32, SDL_Color{ 0,0,0 }); } -void Renderer::renderRightAlignedText(const std::string& text, int x, int y, int fontSize, const SDL_Color& color) { - TTF_Font* font = TTF_OpenFont("assets/tetris-gb.ttf", fontSize); - if (!font) { - SDL_Log("Failed to open font: %s", TTF_GetError( )); - return; - } - - int textWidth; - TTF_SizeText(font, text.c_str( ), &textWidth, nullptr); - - int rightAlignedX = x - textWidth; - - renderText(text, rightAlignedX, y, fontSize, color); - - TTF_CloseFont(font); -} - -void Renderer::drawGrid(const GameBoard& board) { - int borderThickness = 10, gridThickness = 1; +void Renderer::drawWall(const int w, const int h) { int innerBorderThickness = blockSize / 4; - int w = board.getLockedTetrominos( )[0].size( ); - int h = board.getLockedTetrominos( ).size( ); - - int totalWidth = w * blockSize; - int totalHeight = h * blockSize; - - SDL_SetTextureBlendMode(textures[TetrisAssets::BORDER], SDL_BLENDMODE_BLEND); - SDL_SetTextureColorMod(textures[TetrisAssets::BORDER], 165, 42, 42); - for (int y = 0; y < (h + (innerBorderThickness * h)); ++y) { - SDL_Rect leftBlock = { offsetX - blockSize, offsetY + y * blockSize, blockSize, blockSize }; - renderTexture(TetrisAssets::BORDER, leftBlock.x, leftBlock.y - (innerBorderThickness * y + 1), leftBlock.w, leftBlock.h); - - SDL_Rect rightBlock = { offsetX + totalWidth, offsetY + y * blockSize, blockSize, blockSize }; - renderTexture(TetrisAssets::BORDER, rightBlock.x, rightBlock.y - (innerBorderThickness * y + 1), rightBlock.w, rightBlock.h); + SDL_Color color{ 165, 42, 42 }; + renderTexture(textures[TetrisAssets::BORDER], offsetX - blockSize, (offsetY + y * blockSize) - (innerBorderThickness * y + 1), blockSize, blockSize, color); + renderTexture(textures[TetrisAssets::BORDER], offsetX + w * blockSize, (offsetY + y * blockSize) - (innerBorderThickness * y + 1), blockSize, blockSize, color); } } -void Renderer::drawLockedBlocks(const GameBoard& gameBoard) { - const auto& lockedTetrominos = gameBoard.getLockedTetrominos( ); - const auto& lockedColors = gameBoard.getLockedColors( ); +void Renderer::drawLockedBlocks(const shared_ptr gameBoard) { + const auto& lockedTetrominos = gameBoard->getLockedTetrominos( ); + const auto& lockedColors = gameBoard->getLockedColors( ); for (int row = 0; row < lockedTetrominos.size( ); ++row) { for (int col = 0; col < lockedTetrominos[row].size( ); ++col) { int blockType = lockedTetrominos[row][col]; if (blockType != 0) { - SDL_Rect block = { offsetX + col * blockSize, offsetY + row * blockSize, blockSize, blockSize }; + SDL_Color color = lockedColors[row][col]; + renderTexture( + textures[shapeToAsset(static_cast(blockType - 1))], + offsetX + col * blockSize, + offsetY + row * blockSize, + blockSize, + blockSize, + color); + } + } + } +} - TetrominoShape shape = static_cast(blockType - 1); - TetrisAssets asset = shapeToAsset(shape); +void Renderer::drawTetromino(const shared_ptr tetromino) { + if (!tetromino) return; - if (textures[asset]) { - SDL_Color color = lockedColors[row][col]; - SDL_SetTextureBlendMode(textures[asset], SDL_BLENDMODE_BLEND); - SDL_SetTextureColorMod(textures[asset], color.r, color.g, color.b); - renderTexture(asset, block.x, block.y, block.w, block.h); - } else { - SDL_Log("Texture not found for asset: %d", asset); - SDL_SetRenderDrawColor(renderer.get( ), 255, 0, 0, 255); - SDL_RenderFillRect(renderer.get( ), &block); + int x = tetromino->getX( ), y = tetromino->getY( ); + + if (tetromino->getShapeEnumn( ) == TetrominoShape::I) { + double angle = tetromino->getRotationAngle( ); + + if (angle == 90 || angle == 270) { + for (int i = 0; i < 4; ++i) { + renderTexture( + textures[TetrisAssets::I_MIDR], + offsetX + (x + i) * blockSize, + offsetY + y * blockSize, + blockSize, + blockSize, + tetromino->getColor( ) + ); + } + renderTexture( + textures[TetrisAssets::I_ENDR], + offsetX + (x + 0) * blockSize, + offsetY + y * blockSize, + blockSize, + blockSize, + tetromino->getColor( ) + ); + renderTexture( + textures[TetrisAssets::I_STARTR], + offsetX + (x + 3) * blockSize, + offsetY + y * blockSize, + blockSize, + blockSize, + tetromino->getColor( ) + ); + } else { + renderTexture( + textures[TetrisAssets::I_END], + offsetX + x * blockSize, + offsetY + y * blockSize, + blockSize, + blockSize, + tetromino->getColor( ) + ); + renderTexture( + textures[TetrisAssets::I_MID], + offsetX + x * blockSize, + offsetY + (y + 1) * blockSize, + blockSize, + blockSize, + tetromino->getColor( ) + ); + renderTexture( + textures[TetrisAssets::I_MID], + offsetX + x * blockSize, + offsetY + (y + 2) * blockSize, + blockSize, + blockSize, + tetromino->getColor( ) + ); + renderTexture( + textures[TetrisAssets::I_START], + offsetX + x * blockSize, + offsetY + (y + 3) * blockSize, + blockSize, + blockSize, + tetromino->getColor( ) + ); + } + } else { + const auto& shape = tetromino->getShape( ); + for (int row = 0; row < shape.size( ); ++row) { + for (int col = 0; col < shape[row].size( ); ++col) { + if (shape[row][col] != 0) { + renderTexture( + textures[shapeToAsset(tetromino->getShapeEnumn( ))], + offsetX + (x + col) * blockSize, + offsetY + (y + row) * blockSize, + blockSize, + blockSize, + tetromino->getColor( ) + ); } } } } } -TetrisAssets Renderer::shapeToAsset(const TetrominoShape shape) { +const TetrisAssets Renderer::shapeToAsset(const TetrominoShape shape) const { switch (shape) { case TetrominoShape::I: return TetrisAssets::I; @@ -171,80 +188,11 @@ TetrisAssets Renderer::shapeToAsset(const TetrominoShape shape) { case TetrominoShape::I_MIDR: return TetrisAssets::I_MIDR; default: + return TetrisAssets::I; break; } } -void Renderer::drawTetromino(const Tetromino& tetromino) { - const auto& shape = tetromino.getShape( ); - int x = tetromino.getX( ), y = tetromino.getY( ); - - double angle = tetromino.getRotationAngle( ); - - TetrominoShape tetrominoShape = tetromino.getShapeEnumn( ); - - SDL_SetTextureBlendMode(textures[shapeToAsset(tetrominoShape)], SDL_BLENDMODE_BLEND); - SDL_Color color = tetromino.getColor( ); - SDL_SetTextureColorMod(textures[shapeToAsset(tetrominoShape)], color.r, color.g, color.b); - - if (tetrominoShape == TetrominoShape::I) { - if (angle == 90 || angle == 270) { - SDL_SetTextureColorMod(textures[TetrisAssets::I_STARTR], color.r, color.g, color.b); - SDL_SetTextureColorMod(textures[TetrisAssets::I_MIDR], color.r, color.g, color.b); - SDL_SetTextureColorMod(textures[TetrisAssets::I_ENDR], color.r, color.g, color.b); - for (int i = 0; i < 4; ++i) { - SDL_Rect destRect = { offsetX + (x + i) * blockSize, offsetY + y * blockSize, blockSize, blockSize }; - SDL_RenderCopy(renderer.get( ), textures[TetrisAssets::I_MIDR], nullptr, &destRect); - } - - SDL_Rect startRect = { offsetX + (x + 0) * blockSize, offsetY + y * blockSize, blockSize, blockSize }; - SDL_RenderCopy(renderer.get( ), textures[TetrisAssets::I_ENDR], nullptr, &startRect); - - SDL_Rect endRect = { offsetX + (x + 3) * blockSize, offsetY + y * blockSize, blockSize, blockSize }; - SDL_RenderCopy(renderer.get( ), textures[TetrisAssets::I_STARTR], nullptr, &endRect); - } else { - SDL_SetTextureColorMod(textures[TetrisAssets::I_START], color.r, color.g, color.b); - SDL_SetTextureColorMod(textures[TetrisAssets::I_MID], color.r, color.g, color.b); - SDL_SetTextureColorMod(textures[TetrisAssets::I_END], color.r, color.g, color.b); - - SDL_Rect destRect1 = { offsetX + (x + 0) * blockSize, offsetY + (y + 0) * blockSize, blockSize, blockSize }; - SDL_Rect destRect2 = { offsetX + (x + 0) * blockSize, offsetY + (y + 1) * blockSize, blockSize, blockSize }; - SDL_Rect destRect3 = { offsetX + (x + 0) * blockSize, offsetY + (y + 2) * blockSize, blockSize, blockSize }; - SDL_Rect destRect4 = { offsetX + (x + 0) * blockSize, offsetY + (y + 3) * blockSize, blockSize, blockSize }; - - renderTexture(TetrisAssets::I_END, destRect1.x, destRect1.y, destRect1.w, destRect1.h); - renderTexture(TetrisAssets::I_MID, destRect2.x, destRect2.y, destRect2.w, destRect2.h); - renderTexture(TetrisAssets::I_MID, destRect3.x, destRect3.y, destRect3.w, destRect3.h); - renderTexture(TetrisAssets::I_START, destRect4.x, destRect4.y, destRect4.w, destRect4.h); - } - } else { - for (int row = 0; row < shape.size( ); ++row) { - for (int col = 0; col < shape[row].size( ); ++col) { - if (shape[row][col] != 0) { - SDL_Rect destRect = { offsetX + (x + col) * blockSize, offsetY + (y + row) * blockSize, blockSize, blockSize }; - SDL_Point center = { blockSize / 2, blockSize / 2 }; - SDL_RenderCopy(renderer.get( ), textures[shapeToAsset(tetrominoShape)], nullptr, &destRect); - } - } - } - } -} - -bool Renderer::loadTexture(const string& filePath, const TetrisAssets asset) { - SDL_Texture* texture = IMG_LoadTexture(renderer.get( ), filePath.c_str( )); - if (!texture) { - SDL_Log("Failed to load Texture: %s\n%s", filePath, SDL_GetError( )); - return false; - } - textures[asset] = texture; - return true; -} - -void Renderer::renderTexture(const TetrisAssets asset, int x, int y, int width, int height) { - SDL_Rect destRect{ x,y,width,height }; - SDL_RenderCopy(renderer.get( ), textures[asset], nullptr, &destRect); -} - void Renderer::renderStartScreen( ) { SDL_SetRenderDrawColor(renderer.get( ), 248, 248, 248, 255); SDL_RenderClear(renderer.get( )); @@ -259,161 +207,171 @@ void Renderer::renderStartScreen( ) { SDL_SetRenderDrawColor(renderer.get( ), 0, 0, 0, 255); SDL_RenderDrawRect(renderer.get( ), &borderRect); - SDL_Surface* titleSurface = IMG_Load("assets/title.png"); - if (!titleSurface) { - SDL_Log("Failed to load title surface: %s", SDL_GetError( )); - return; - } - - auto titleTexture = SDL_CreateTextureFromSurface(renderer.get( ), titleSurface); - SDL_FreeSurface(titleSurface); - if (!titleTexture) { - SDL_Log("Failed to create title texture: %s", SDL_GetError( )); - return; - } - - float titleScaleFactor = 0.5f; - int titleWidth = static_cast(windowWidth * titleScaleFactor); + int titleWidth = static_cast(windowWidth * 0.8f); int titleHeight = static_cast(titleWidth * 0.315f); - SDL_Rect titleRect = { - (windowWidth / 2) - (titleWidth / 2), - static_cast(windowHeight * 0.2f), - titleWidth, - titleHeight - }; + renderTexture( + "assets/sprites/title.png", + (windowWidth / 2) - (titleWidth / 2), + static_cast(windowHeight * 0.160f), + titleWidth, + titleHeight + ); - SDL_RenderCopy(renderer.get( ), titleTexture, nullptr, &titleRect); - SDL_DestroyTexture(titleTexture); + auto titleBg = unique_ptr(IMG_Load("assets/sprites/title_bg.png"), SDL_FreeSurface); - SDL_Surface* titleBg = IMG_Load("assets/title_bg.png"); - if (!titleBg) { - SDL_Log("Failed to load background surface: %s", SDL_GetError( )); - return; - } + int titleBgWidth = static_cast(windowWidth * 0.8f); + int titleBgHeight = static_cast(titleBgWidth * (static_cast(titleBg->h) / titleBg->w)); - auto titleBgTexture = SDL_CreateTextureFromSurface(renderer.get( ), titleBg); - int originalBgWidth = titleBg->w; - int originalBgHeight = titleBg->h; - SDL_FreeSurface(titleBg); - if (!titleBgTexture) { - SDL_Log("Failed to create background texture: %s", SDL_GetError( )); - return; - } - - float bgScaleFactor = 0.8f; - int titleBgWidth = static_cast(windowWidth * bgScaleFactor); - int titleBgHeight = static_cast(titleBgWidth * (static_cast(originalBgHeight) / originalBgWidth)); - if (titleBgHeight > windowHeight * 0.4f) { - titleBgHeight = static_cast(windowHeight * 0.4f); - titleBgWidth = static_cast(titleBgHeight * (static_cast(originalBgWidth) / originalBgHeight)); - } - - SDL_Rect titleBgRect = { - (windowWidth / 2) - (titleBgWidth / 2), - static_cast(windowHeight * 0.5f), - titleBgWidth, - titleBgHeight - }; - - SDL_RenderCopy(renderer.get( ), titleBgTexture, nullptr, &titleBgRect); - SDL_DestroyTexture(titleBgTexture); - - int w, h; - TTF_Font* font = TTF_OpenFont("assets/tetris-gb.ttf", 16); - TTF_SizeText(font, "PRESS START", &w, &h); - TTF_CloseFont(font); - - renderText("press G to start", (windowWidth / 2) - (w / 2), windowHeight - 70, 16, SDL_Color{ 0, 0, 0 }); + renderTexture( + "assets/sprites/title_bg.png", + (windowWidth / 2) - (titleBgWidth / 2), + static_cast(windowHeight * 0.5f), + titleBgWidth, + titleBgHeight + ); + renderText("press G to start", (windowWidth / 2), windowHeight - 70, 16, SDL_Color{ 0, 0, 0 }, HAlign::CENTER); SDL_RenderPresent(renderer.get( )); } -void Renderer::renderGameOver(int score, int level, int lines) { - int fontSize = 24; - int lineSpacing = fontSize + 10; +void Renderer::renderGameOver(int gbWidth, int gbHeight) { + //Needed to draw the Walls again + drawWall(gbWidth, gbHeight); - int totalHeight = 5 * lineSpacing; + auto gameOver = unique_ptr(IMG_Load("assets/sprites/game_over.png"), SDL_FreeSurface); - int centerX = (windowWidth / 2); - int centerY = (windowHeight / 2) - (totalHeight / 2); + int gameOverWidth = static_cast(windowWidth * 0.3f); + int gameOverHeight = static_cast(gameOverWidth * (static_cast(gameOver->h) / gameOver->w)); - renderText("game", centerX, centerY, fontSize, SDL_Color{ 0, 0, 0 }); - renderText("over", centerX, centerY + lineSpacing, fontSize, SDL_Color{ 0, 0, 0 }); - renderText("please", centerX, centerY + 2 * lineSpacing, fontSize, SDL_Color{ 0, 0, 0 }); - renderText("try", centerX, centerY + 3 * lineSpacing, fontSize, SDL_Color{ 0, 0, 0 }); - renderText("again @", centerX, centerY + 4 * lineSpacing, fontSize, SDL_Color{ 0, 0, 0 }); + renderTexture( + "assets/sprites/game_over.png", + (windowWidth / 2) - (gameOverWidth / 2), + static_cast(windowHeight * 0.15f), + gameOverWidth, + gameOverHeight + ); + + SDL_Color col{ 255,255,255 }; + renderTexture("assets/sprites/please_try_again_text.png", windowWidth / 2, windowHeight * 0.7f, 0, 0, col, 3.5f, HAlign::CENTER); SDL_RenderPresent(renderer.get( )); } void Renderer::renderTetrominoPreview(const shared_ptr nextTetromino) { - const auto& shape = nextTetromino->getShape( ); + if (!nextTetromino) return; + int x = nextTetromino->getX( ), y = nextTetromino->getY( ); - double angle = nextTetromino->getRotationAngle( ); - - TetrominoShape tetrominoShape = nextTetromino->getShapeEnumn( ); - - SDL_SetTextureBlendMode(textures[shapeToAsset(tetrominoShape)], SDL_BLENDMODE_BLEND); - SDL_Color color = nextTetromino->getColor( ); - SDL_SetTextureColorMod(textures[shapeToAsset(tetrominoShape)], color.r, color.g, color.b); - - if (tetrominoShape == TetrominoShape::I) { - SDL_SetTextureColorMod(textures[TetrisAssets::I_STARTR], color.r, color.g, color.b); - SDL_SetTextureColorMod(textures[TetrisAssets::I_MIDR], color.r, color.g, color.b); - SDL_SetTextureColorMod(textures[TetrisAssets::I_ENDR], color.r, color.g, color.b); - + if (nextTetromino->getShapeEnumn( ) == TetrominoShape::I) { for (int i = 0; i < 4; ++i) { - SDL_Rect destRect = { (windowWidth - 155) + (x + i) * blockSize, (windowHeight - 120) + y * blockSize, blockSize, blockSize }; - SDL_RenderCopy(renderer.get( ), textures[TetrisAssets::I_MIDR], nullptr, &destRect); + renderTexture( + textures[TetrisAssets::I_MIDR], + (windowWidth - 155) + (x + i) * blockSize, + (windowHeight - 120) + y * blockSize, + blockSize, + blockSize, + nextTetromino->getColor( ) + ); } - SDL_Rect startRect = { (windowWidth - 155) + (x + 0) * blockSize, (windowHeight - 120) + y * blockSize, blockSize, blockSize }; - SDL_RenderCopy(renderer.get( ), textures[TetrisAssets::I_ENDR], nullptr, &startRect); + renderTexture( + textures[TetrisAssets::I_ENDR], + (windowWidth - 155) + x * blockSize, + (windowHeight - 120) + y * blockSize, + blockSize, + blockSize, + nextTetromino->getColor( ) + ); - SDL_Rect endRect = { (windowWidth - 155) + (x + 3) * blockSize, (windowHeight - 120) + y * blockSize, blockSize, blockSize }; - SDL_RenderCopy(renderer.get( ), textures[TetrisAssets::I_STARTR], nullptr, &endRect); + renderTexture( + textures[TetrisAssets::I_STARTR], + (windowWidth - 155) + (x + 3) * blockSize, + (windowHeight - 120) + y * blockSize, + blockSize, + blockSize, + nextTetromino->getColor( ) + ); } else { - for (int row = 0; row < shape.size( ); ++row) { - for (int col = 0; col < shape[row].size( ); ++col) { - if (shape[row][col] != 0) { - SDL_Rect destRect = { (windowWidth - 140) + col * blockSize, (windowHeight - 130) + row * blockSize, blockSize, blockSize }; - SDL_Point center = { blockSize / 2, blockSize / 2 }; - SDL_RenderCopy(renderer.get( ), textures[shapeToAsset(tetrominoShape)], nullptr, &destRect); + for (int row = 0; row < nextTetromino->getShape( ).size( ); ++row) { + for (int col = 0; col < nextTetromino->getShape( )[row].size( ); ++col) { + if (nextTetromino->getShape( )[row][col] != 0) { + renderTexture( + textures[shapeToAsset(nextTetromino->getShapeEnumn( ))], + (windowWidth - 140) + col * blockSize, + (windowHeight - 130) + row * blockSize, + blockSize, + blockSize, + nextTetromino->getColor( ) + ); } } } } } -void Renderer::renderText(string text, int x, int y, int size, SDL_Color color) { - TTF_Font* font = TTF_OpenFont("assets/tetris-gb.ttf", size); - SDL_Surface* surface = TTF_RenderText_Solid(font, text.c_str( ), color); - if (!surface) { - SDL_Log("Failed to create surface: %s", TTF_GetError( )); - return; - } +const Renderer::TextDimensions Renderer::renderText(const string& text, int x, int y, int fontSize, SDL_Color color, HAlign hAlign, VAlign vAlign) { + auto font = unique_ptr(TTF_OpenFont("assets/font/tetris-gb.ttf", fontSize), TTF_CloseFont); + if (!font) { SDL_Log("Failed to create font: %s", TTF_GetError( )); return{ 0,0,0,0 }; } - SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer.get( ), surface); - if (!texture) { - SDL_Log("Failed to create texture from surface: %s", SDL_GetError( )); - SDL_FreeSurface(surface); - TTF_CloseFont(font); - return; - } + auto surface = unique_ptr(TTF_RenderText_Solid(font.get( ), text.c_str( ), color), SDL_FreeSurface); + if (!surface) { SDL_Log("Failed to create surface: %s", TTF_GetError( ));return{ 0,0,0,0 }; } + + auto texture = unique_ptr(SDL_CreateTextureFromSurface(renderer.get( ), surface.get( )), SDL_DestroyTexture); + if (!texture) { SDL_Log("Failed to create texture from surface: %s", SDL_GetError( ));return{ 0,0,0,0 }; } int width = surface->w; int height = surface->h; + if (hAlign == HAlign::CENTER) + x -= width / 2; + else if (hAlign == HAlign::RIGHT) + x -= width; + + if (vAlign == VAlign::CENTER) + y -= height / 2; + else if (vAlign == VAlign::BOTTOM) + y -= height; + + SDL_Rect destRect = { x, y, width, height }; + SDL_RenderCopy(renderer.get( ), texture.get( ), nullptr, &destRect); - SDL_RenderCopy(renderer.get( ), texture, nullptr, &destRect); + return TextDimensions{ x, y, width, height }; +} - SDL_FreeSurface(surface); - SDL_DestroyTexture(texture); - TTF_CloseFont(font); +const Renderer::TextureDimensions Renderer::renderTexture( + const string& texturePath, int x, int y, int width, int height, + SDL_Color color, float scale, HAlign textHAlign, VAlign textVAlign) { + + auto surface = unique_ptr (IMG_Load(texturePath.c_str( )), SDL_FreeSurface); + if (!surface) { SDL_Log("Failed to load surface from %s: %s", texturePath, SDL_GetError( ));return{ 0,0,0,0 }; } + + auto texture = unique_ptr(SDL_CreateTextureFromSurface(renderer.get( ), surface.get( )), SDL_DestroyTexture); + if (!texture) { SDL_Log("Failed to create texture from surface: %s", SDL_GetError( )); return{ 0,0,0,0 }; } + + SDL_SetTextureBlendMode(texture.get( ), SDL_BLENDMODE_BLEND); + SDL_SetTextureColorMod(texture.get( ), color.r, color.g, color.b); + + int textureWidth = static_cast((width == 0 ? surface->w : width) * scale); + int textureHeight = static_cast((height == 0 ? surface->h : height) * scale); + + if (textHAlign == HAlign::CENTER) + x -= textureWidth / 2; + else if (textHAlign == HAlign::RIGHT) + x -= textureWidth; + + + if (textVAlign == VAlign::CENTER) + y -= textureHeight / 2; + else if (textVAlign == VAlign::BOTTOM) + y -= textureHeight; + + SDL_Rect rect{ x,y,textureWidth,textureHeight }; + SDL_RenderCopy(renderer.get( ), texture.get( ), nullptr, &rect); + + return { x,y,textureWidth, textureHeight }; } const int Renderer::getBlockSize( ) const { return blockSize; } @@ -433,3 +391,4 @@ void Renderer::setWindowSize(int w, int h) { windowWidth = w; windowHeight = h; } + diff --git a/src/Renderer.hpp b/src/Renderer.hpp index 06a48e7..2011de9 100644 --- a/src/Renderer.hpp +++ b/src/Renderer.hpp @@ -32,35 +32,60 @@ enum class TetrisAssets { class Renderer { private: - void drawGrid(const GameBoard& gameBoard); - void drawLockedBlocks(const GameBoard& gameBoard); - void drawTetromino(const Tetromino& tetromino); + void drawWall(const int w, const int h); + void drawLockedBlocks(const shared_ptr gameBoard); + void drawTetromino(const shared_ptr tetromino); void drawScoreboard(int score, int level, int lines); - TetrisAssets shapeToAsset(const TetrominoShape shape); + const bool loadTexture(const string& filePath, const TetrisAssets shape); - shared_ptr renderer; + const TetrisAssets shapeToAsset(const TetrominoShape shape) const; + + const shared_ptr renderer; int blockSize; - int offsetX; - int offsetY; - int screenSpacing = 50; - int windowHeight = 0; - int windowWidth = 0; + int offsetX, offsetY; + int windowHeight = 0, windowWidth = 0; + + unordered_map textures; + + enum class HAlign { + LEFT, + CENTER, + RIGHT, + }; + + enum class VAlign { + TOP, + CENTER, + BOTTOM, + }; + + struct TextDimensions { + const int x, y, w, h; + const int fontSize; + }; + + struct TextureDimensions { + const int x, y, w, h; + const SDL_Color color; + }; - unordered_map textures; public: Renderer(shared_ptr renderer, int w, int h); - void setRenderer(shared_ptr renderer); - void renderBoard(const GameBoard& gb); + void renderBoard(const shared_ptr gameBoard); - bool loadTexture(const string& filePath, const TetrisAssets shape); - void renderTexture(const TetrisAssets shape, int x, int y, int width, int height); void renderStartScreen( ); - void renderGameOver(int score, int level, int lines); - void renderText(string text, int x, int y, int size, SDL_Color color); - void renderRightAlignedText(const std::string& text, int x, int y, int fontSize, const SDL_Color& color); + void renderGameOver(int gbWidth, int gbHeight); + const TextDimensions renderText( + const string& text, int x, int y, int fontSize, + SDL_Color color, HAlign textHAlign = HAlign::LEFT, VAlign textVAlign = VAlign::TOP + ); + const TextureDimensions renderTexture( + const string& texturePath, int x, int y, int width = 0, int height = 0, + SDL_Color color = { 255,255,255 }, float scale = 1.0f, HAlign textHAlign = HAlign::LEFT, VAlign textVAlign = VAlign::TOP + ); void renderTetrominoPreview(const shared_ptr nextTetromino); const int getBlockSize( ) const;