diff --git a/CMakeLists.txt b/CMakeLists.txt index c3c527a..0cfc851 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ target_link_libraries(SDL_TD ) file(GLOB ASSETS - "assets/*.png" + "assets/*" ) foreach(ASSET ${ASSETS}) diff --git a/assets/BORDER.png b/assets/BORDER.png new file mode 100644 index 0000000..f9a11c3 Binary files /dev/null and b/assets/BORDER.png differ diff --git a/assets/I.png b/assets/I.png deleted file mode 100644 index ab42d1c..0000000 Binary files a/assets/I.png and /dev/null differ diff --git a/assets/I_END.png b/assets/I_END.png new file mode 100644 index 0000000..d73ca8a Binary files /dev/null and b/assets/I_END.png differ diff --git a/assets/I_ENDR.png b/assets/I_ENDR.png new file mode 100644 index 0000000..d99c063 Binary files /dev/null and b/assets/I_ENDR.png differ diff --git a/assets/I_MID.png b/assets/I_MID.png new file mode 100644 index 0000000..6908305 Binary files /dev/null and b/assets/I_MID.png differ diff --git a/assets/I_MIDR.png b/assets/I_MIDR.png new file mode 100644 index 0000000..593e853 Binary files /dev/null and b/assets/I_MIDR.png differ diff --git a/assets/I_START.png b/assets/I_START.png new file mode 100644 index 0000000..7d247a2 Binary files /dev/null and b/assets/I_START.png differ diff --git a/assets/I_STARTR.png b/assets/I_STARTR.png new file mode 100644 index 0000000..0e6a956 Binary files /dev/null and b/assets/I_STARTR.png differ diff --git a/assets/J.png b/assets/J.png index 70aa092..ff19261 100644 Binary files a/assets/J.png and b/assets/J.png differ diff --git a/assets/L.png b/assets/L.png index 9edaaec..aa94a9d 100644 Binary files a/assets/L.png and b/assets/L.png differ diff --git a/assets/O.png b/assets/O.png index 1258fe2..36f8d95 100644 Binary files a/assets/O.png and b/assets/O.png differ diff --git a/assets/S.png b/assets/S.png index 152fa39..39b64b5 100644 Binary files a/assets/S.png and b/assets/S.png differ diff --git a/assets/T.png b/assets/T.png new file mode 100644 index 0000000..5b80ed6 Binary files /dev/null and b/assets/T.png differ diff --git a/assets/Z.png b/assets/Z.png index 09db90e..c3d556d 100644 Binary files a/assets/Z.png and b/assets/Z.png differ diff --git a/assets/tetris-gb.ttf b/assets/tetris-gb.ttf new file mode 100644 index 0000000..f3e95f8 Binary files /dev/null and b/assets/tetris-gb.ttf differ diff --git a/assets/title.png b/assets/title.png new file mode 100644 index 0000000..e7f0bd1 Binary files /dev/null and b/assets/title.png differ diff --git a/assets/title_bg.png b/assets/title_bg.png new file mode 100644 index 0000000..750f9e4 Binary files /dev/null and b/assets/title_bg.png differ diff --git a/src/Game.cpp b/src/Game.cpp index bd24426..3488619 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -19,6 +19,8 @@ bool Game::init(const char* title, int w, int h) { return false; } + SDL_SetWindowMinimumSize(window.get( ), 800, 650); + renderer = std::shared_ptr( SDL_CreateRenderer(window.get( ), -1, SDL_RENDERER_ACCELERATED), [ ](SDL_Renderer* r) { SDL_DestroyRenderer(r); } @@ -28,11 +30,15 @@ bool Game::init(const char* title, int w, int h) { SDL_Log("Failed to create renderer: %s", SDL_GetError( )); return false; } + int ww, wh; + SDL_GetWindowSize(window.get( ), &ww, &wh); - gameRenderer = make_shared(renderer); + gameRenderer = make_shared(renderer, ww, wh); gameBoard = make_shared( ); - bgm = std::shared_ptr(Mix_LoadMUS("/home/crylia/Dokumente/git/tetris_sdl/assets/bgm.mp3"), [ ](Mix_Music* m) { + handleWindowResize( ); + + bgm = std::shared_ptr(Mix_LoadMUS("assets/bgm.mp3"), [ ](Mix_Music* m) { Mix_FreeMusic(m); }); @@ -73,7 +79,10 @@ void Game::run( ) { void Game::inputHandler( ) { SDL_Event event; while (SDL_PollEvent(&event)) { - if (event.type == SDL_QUIT) { SDL_Quit( ); } else if (event.type == SDL_KEYDOWN) { + if (event.type == SDL_QUIT) { + SDL_Quit( ); + quit = true; + } else if (event.type == SDL_KEYDOWN) { switch (event.key.keysym.sym) { case SDLK_LEFT: case SDLK_a: @@ -106,10 +115,25 @@ void Game::inputHandler( ) { default: break; } + } else if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) { + handleWindowResize( ); } } } +void Game::handleWindowResize( ) { + int windowWidth, windowHeight; + SDL_GetWindowSize(window.get( ), &windowWidth, &windowHeight); + + gameRenderer->setBlockSize(windowHeight / gameBoard->getLockedTetrominos( ).size( )); + + int offsetX = (windowWidth - gameBoard->getLockedTetrominos( )[0].size( ) * gameRenderer->getBlockSize( )) / 2; + int offsetY = (windowHeight - gameBoard->getLockedTetrominos( ).size( ) * gameRenderer->getBlockSize( )) / 2; + gameRenderer->setOffset(offsetX, offsetY); + + gameRenderer->setWindowSize(windowWidth, windowHeight); +} + void Game::update( ) { Uint32 currentTime = SDL_GetTicks( ); Uint32 deltaTime = currentTime - lastUpdateTime; @@ -122,7 +146,7 @@ void Game::update( ) { void Game::render( ) { //Set default color to black - SDL_SetRenderDrawColor(renderer.get( ), 20, 20, 20, 255); + SDL_SetRenderDrawColor(renderer.get( ), 248, 248, 248, 255); SDL_RenderClear(renderer.get( )); gameRenderer->renderBoard(*gameBoard); diff --git a/src/Game.hpp b/src/Game.hpp index 50d3cdd..1134472 100644 --- a/src/Game.hpp +++ b/src/Game.hpp @@ -18,6 +18,8 @@ private: void render( ); void inputHandler( ); + void handleWindowResize( ); + unique_ptr window; shared_ptr renderer; diff --git a/src/GameBoard.cpp b/src/GameBoard.cpp index f4ee67b..1669046 100644 --- a/src/GameBoard.cpp +++ b/src/GameBoard.cpp @@ -2,7 +2,7 @@ #include GameBoard::GameBoard( ) - : lockedTetrominos(20, vector(10, 0)), lockedColors(20, std::vector(10, { 0, 0, 0, 0 })), score(0), level(1), collision(false) { + : lockedTetrominos(20, vector(10, 0)), lockedColors(20, std::vector(10, { 0, 0, 0, 255 })), score(0), level(1), collision(false) { spawnNewTetromino( ); } @@ -41,37 +41,86 @@ bool GameBoard::checkCollision(const Tetromino& tetromino) const { void GameBoard::lockTetromino( ) { const auto& shape = currentTetromino->getShape( ); int x = currentTetromino->getX( ), y = currentTetromino->getY( ); + double angle = currentTetromino->getRotationAngle( ); + TetrominoShape tetrominoShape = currentTetromino->getShapeEnumn( ); - for (int row = 0; row < shape.size( ); ++row) - for (int col = 0; col < shape[row].size( ); ++col) - if (shape[row][col] != 0) { + if (tetrominoShape == TetrominoShape::I) { + if (angle == 90 || angle == 270) { + for (int col = 0; col < shape[0].size( ); ++col) { int lockedTetrominosX = x + col; - int lockedTetrominosY = y + row; - if (lockedTetrominosY >= 0 && lockedTetrominosX < height) { - lockedTetrominos[lockedTetrominosY][lockedTetrominosX] = 1; + int lockedTetrominosY = y; + + if (lockedTetrominosY >= 0 && lockedTetrominosX >= 0 && lockedTetrominosX < width) { + if (col == 0) { + lockedTetrominos[lockedTetrominosY][lockedTetrominosX] = static_cast(TetrominoShape::I_ENDR) + 1; + } else if (col == 3) { + lockedTetrominos[lockedTetrominosY][lockedTetrominosX] = static_cast(TetrominoShape::I_STARTR) + 1; + } else { + lockedTetrominos[lockedTetrominosY][lockedTetrominosX] = static_cast(TetrominoShape::I_MIDR) + 1; + } lockedColors[lockedTetrominosY][lockedTetrominosX] = currentTetromino->getColor( ); } } + } else { + for (int row = 0; row < shape.size( ); ++row) { + for (int col = 0; col < shape[row].size( ); ++col) { + if (shape[row][col] != 0) { + int lockedTetrominosX = x; + int lockedTetrominosY = y + row; + + if (lockedTetrominosY >= 0 && lockedTetrominosX >= 0 && lockedTetrominosX < width && lockedTetrominosY < height) { + if (row == 0) { + lockedTetrominos[lockedTetrominosY][lockedTetrominosX] = static_cast(TetrominoShape::I_END) + 1; + } else if (row == 3) { + lockedTetrominos[lockedTetrominosY][lockedTetrominosX] = static_cast(TetrominoShape::I_START) + 1; + } else { + lockedTetrominos[lockedTetrominosY][lockedTetrominosX] = static_cast(TetrominoShape::I_MID) + 1; + } + lockedColors[lockedTetrominosY][lockedTetrominosX] = currentTetromino->getColor( ); + } + } + } + } + } + } else { + for (int row = 0; row < shape.size( ); ++row) { + for (int col = 0; col < shape[row].size( ); ++col) { + if (shape[row][col] != 0) { + int lockedTetrominosX = x + col; + int lockedTetrominosY = y + row; + + if (lockedTetrominosY >= 0 && lockedTetrominosX >= 0 && lockedTetrominosX < width && lockedTetrominosY < height) { + lockedTetrominos[lockedTetrominosY][lockedTetrominosX] = static_cast(tetrominoShape) + 1; + lockedColors[lockedTetrominosY][lockedTetrominosX] = currentTetromino->getColor( ); + } + } + } + } + } } void GameBoard::clearLines( ) { - for (int row = 0; row < height; row++) - if (all_of(lockedTetrominos[row].begin( ), lockedTetrominos[row].end( ), [ ](int cell) {return cell != 0;})) { + for (int row = 0; row < height; row++) { + if (all_of(lockedTetrominos[row].begin( ), lockedTetrominos[row].end( ), [ ](int cell) { return cell != 0; })) { lockedTetrominos.erase(lockedTetrominos.begin( ) + row); - lockedTetrominos.insert(lockedTetrominos.begin( ), vector(width, 0)); - score += 100; - if ((score % 1000) == 0) - level++; - } -} + lockedColors.erase(lockedColors.begin( ) + row); + lockedTetrominos.insert(lockedTetrominos.begin( ), vector(width, 0)); + lockedColors.insert(lockedColors.begin( ), vector(width, { 0, 0, 0, 0 })); + + score += 100; + if (score % 1000 == 0) { + level++; + } + } + } +} void GameBoard::spawnNewTetromino( ) { random_device dev; mt19937 rng(dev( )); uniform_int_distribution dist6(0, static_cast(TetrominoShape::COUNT) - 1); TetrominoShape shape = static_cast(dist6(rng)); currentTetromino = make_unique(shape); - currentTetromino->setTexture(shape); currentTetromino->move(width / 2 - 1, 0); if (checkCollision(*currentTetromino)) @@ -117,4 +166,5 @@ void GameBoard::reset( ) { lockedColors.clear( ); score = 0; level = 1; + collision = false; } diff --git a/src/Renderer.cpp b/src/Renderer.cpp index 130646b..b0d0778 100644 --- a/src/Renderer.cpp +++ b/src/Renderer.cpp @@ -1,18 +1,27 @@ #include "Renderer.hpp" #include -Renderer::Renderer(shared_ptr renderer) : renderer(renderer), blockSize(30) { +Renderer::Renderer(shared_ptr renderer, int w, int h) : renderer(renderer), blockSize(30), windowHeight(h), windowWidth(w) { setRenderer(renderer); } void Renderer::setRenderer(shared_ptr renderer) { this->renderer = renderer; - loadTexture("/home/crylia/Dokumente/git/tetris_sdl/assets/single.png", TetrominoShape::I); - loadTexture("/home/crylia/Dokumente/git/tetris_sdl/assets/single.png", TetrominoShape::J); - loadTexture("/home/crylia/Dokumente/git/tetris_sdl/assets/single.png", TetrominoShape::L); - loadTexture("/home/crylia/Dokumente/git/tetris_sdl/assets/single.png", TetrominoShape::S); - loadTexture("/home/crylia/Dokumente/git/tetris_sdl/assets/single.png", TetrominoShape::O); - loadTexture("/home/crylia/Dokumente/git/tetris_sdl/assets/single.png", TetrominoShape::Z); + 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) { @@ -29,14 +38,22 @@ void Renderer::drawScoreboard(int score, int level) { } void Renderer::drawGrid(const GameBoard& board) { - SDL_SetRenderDrawColor(renderer.get( ), 100, 100, 100, 255); + int borderThickness = 10, gridThickness = 1; + int innerBorderThickness = blockSize / 4; - int w = board.getLockedTetrominos( )[0].size( ), h = board.getLockedTetrominos( ).size( ); - for (int x = 0; x <= w; ++x) - SDL_RenderDrawLine(renderer.get( ), x * blockSize, 0, x * blockSize, h * blockSize); + int w = board.getLockedTetrominos( )[0].size( ); + int h = board.getLockedTetrominos( ).size( ); - for (int y = 0; y <= h; ++y) - SDL_RenderDrawLine(renderer.get( ), 0, y * blockSize, w * blockSize, y * blockSize); + int totalWidth = w * blockSize; + int totalHeight = h * blockSize; + + 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); + } } void Renderer::drawLockedBlocks(const GameBoard& gameBoard) { @@ -47,16 +64,19 @@ void Renderer::drawLockedBlocks(const GameBoard& gameBoard) { for (int col = 0; col < lockedTetrominos[row].size( ); ++col) { int blockType = lockedTetrominos[row][col]; if (blockType != 0) { - SDL_Rect block = { col * blockSize, row * blockSize, blockSize, blockSize }; + SDL_Rect block = { offsetX + col * blockSize, offsetY + row * blockSize, blockSize, blockSize }; - SDL_SetTextureBlendMode(textures[TetrominoShape::I], SDL_BLENDMODE_BLEND); + TetrominoShape shape = static_cast(blockType - 1); + TetrisAssets asset = shapeToAsset(shape); - SDL_Color color = lockedColors[row][col]; - if (textures[TetrominoShape::I]) { - SDL_SetTextureColorMod(textures[TetrominoShape::I], color.r, color.g, color.b); - renderTexture(TetrominoShape::I, block.x, block.y, block.w, block.h); + 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_SetRenderDrawColor(renderer.get( ), color.r, color.g, color.b, 255); + SDL_Log("Texture not found for asset: %d", asset); + SDL_SetRenderDrawColor(renderer.get( ), 255, 0, 0, 255); SDL_RenderFillRect(renderer.get( ), &block); } } @@ -64,52 +84,203 @@ void Renderer::drawLockedBlocks(const GameBoard& gameBoard) { } } +TetrisAssets Renderer::shapeToAsset(const TetrominoShape shape) { + switch (shape) { + case TetrominoShape::I: + return TetrisAssets::I; + case TetrominoShape::O: + return TetrisAssets::O; + case TetrominoShape::T: + return TetrisAssets::T; + case TetrominoShape::J: + return TetrisAssets::J; + case TetrominoShape::L: + return TetrisAssets::L; + case TetrominoShape::S: + return TetrisAssets::S; + case TetrominoShape::Z: + return TetrisAssets::Z; + case TetrominoShape::I_END: + return TetrisAssets::I_END; + case TetrominoShape::I_START: + return TetrisAssets::I_START; + case TetrominoShape::I_MID: + return TetrisAssets::I_MID; + case TetrominoShape::I_ENDR: + return TetrisAssets::I_ENDR; + case TetrominoShape::I_STARTR: + return TetrisAssets::I_STARTR; + case TetrominoShape::I_MIDR: + return TetrisAssets::I_MIDR; + default: + break; + } +} + void Renderer::drawTetromino(const Tetromino& tetromino) { const auto& shape = tetromino.getShape( ); int x = tetromino.getX( ), y = tetromino.getY( ); - auto t = tetromino.getTexture( ); double angle = tetromino.getRotationAngle( ); - SDL_SetTextureBlendMode(textures[t], SDL_BLENDMODE_BLEND); - SDL_Color color = tetromino.getColor( ); - SDL_SetTextureColorMod(textures[t], color.r, color.g, color.b); + TetrominoShape tetrominoShape = tetromino.getShapeEnumn( ); - 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 = { (x + col) * blockSize, (y + row) * blockSize, blockSize, blockSize }; - SDL_Point center = { blockSize / 2, blockSize / 2 }; - SDL_RenderCopyEx(renderer.get( ), textures[t], nullptr, &destRect, angle, ¢er, SDL_FLIP_NONE); + 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) { + 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); + + if (angle == 90) { + for (int i = 0; i < 4; ++i) { + SDL_Rect destRect = { offsetX + (x + i) * blockSize, offsetY + y * blockSize, blockSize, blockSize }; + SDL_RenderCopyEx(renderer.get( ), textures[TetrisAssets::I_MID], nullptr, &destRect, angle, nullptr, SDL_FLIP_NONE); + } + + SDL_Rect startRect = { offsetX + (x + 0) * blockSize, offsetY + y * blockSize, blockSize, blockSize }; + SDL_RenderCopyEx(renderer.get( ), textures[TetrisAssets::I_START], nullptr, &startRect, angle, nullptr, SDL_FLIP_NONE); + + SDL_Rect endRect = { offsetX + (x + 3) * blockSize, offsetY + y * blockSize, blockSize, blockSize }; + SDL_RenderCopyEx(renderer.get( ), textures[TetrisAssets::I_END], nullptr, &endRect, angle, nullptr, SDL_FLIP_NONE); + } else if (angle == 270) { + for (int i = 0; i < 4; ++i) { + SDL_Rect destRect = { offsetX + (x + i) * blockSize, offsetY + y * blockSize, blockSize, blockSize }; + SDL_RenderCopyEx(renderer.get( ), textures[TetrisAssets::I_MID], nullptr, &destRect, angle, nullptr, SDL_FLIP_NONE); + } + + SDL_Rect startRect = { offsetX + (x + 0) * blockSize, offsetY + y * blockSize, blockSize, blockSize }; + SDL_RenderCopyEx(renderer.get( ), textures[TetrisAssets::I_END], nullptr, &startRect, angle, nullptr, SDL_FLIP_NONE); + + SDL_Rect endRect = { offsetX + (x + 3) * blockSize, offsetY + y * blockSize, blockSize, blockSize }; + SDL_RenderCopyEx(renderer.get( ), textures[TetrisAssets::I_START], nullptr, &endRect, angle, nullptr, SDL_FLIP_NONE); + } else { + 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 TetrominoShape shape) { +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[shape] = texture; + textures[asset] = texture; return true; } -void Renderer::renderTexture(const TetrominoShape shape, int x, int y, int width, int height) { +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[shape], nullptr, &destRect); + SDL_RenderCopy(renderer.get( ), textures[asset], nullptr, &destRect); } void Renderer::renderStartScreen( ) { - SDL_SetRenderDrawColor(renderer.get( ), 0, 0, 0, 255); + SDL_SetRenderDrawColor(renderer.get( ), 248, 248, 248, 255); SDL_RenderClear(renderer.get( )); - renderText("Tetris", 100, 100, 128, SDL_Color{ 200,200,200 }); + int borderThickness = 20; + SDL_Rect borderRect = { + borderThickness, + borderThickness, + windowWidth - 2 * borderThickness, + windowHeight - 2 * borderThickness + }; + 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 titleHeight = static_cast(titleWidth * 0.315f); + + SDL_Rect titleRect = { + (windowWidth / 2) - (titleWidth / 2), + static_cast(windowHeight * 0.2f), + titleWidth, + titleHeight + }; + + SDL_RenderCopy(renderer.get( ), titleTexture, nullptr, &titleRect); + SDL_DestroyTexture(titleTexture); + + SDL_Surface* titleBg = IMG_Load("assets/title_bg.png"); + if (!titleBg) { + SDL_Log("Failed to load background surface: %s", SDL_GetError( )); + return; + } + + 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 - 100, 16, SDL_Color{ 0, 0, 0 }); SDL_RenderPresent(renderer.get( )); } + + void Renderer::renderGameOver(int score, int level) { SDL_SetRenderDrawColor(renderer.get( ), 0, 0, 0, 128); SDL_RenderClear(renderer.get( )); @@ -123,7 +294,7 @@ void Renderer::renderGameOver(int score, int level) { } void Renderer::renderText(string text, int x, int y, int size, SDL_Color color) { - TTF_Font* font = TTF_OpenFont("/usr/share/fonts/TTF/OpenSans-Regular.ttf", size); + 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( )); @@ -149,3 +320,21 @@ void Renderer::renderText(string text, int x, int y, int size, SDL_Color color) SDL_DestroyTexture(texture); TTF_CloseFont(font); } + +const int Renderer::getBlockSize( ) const { return blockSize; } + +void Renderer::setBlockSize(int newBlockSize) { blockSize = newBlockSize; } + +void Renderer::setOffset(int newX, int newY) { + offsetX = newX; + offsetY = newY; +} + +const int Renderer::getOffsetX( ) const { return offsetX; } + +const int Renderer::getOffsetY( ) const { return offsetY; } + +void Renderer::setWindowSize(int w, int h) { + windowWidth = w; + windowHeight = h; +} diff --git a/src/Renderer.hpp b/src/Renderer.hpp index df17a25..79e62ba 100644 --- a/src/Renderer.hpp +++ b/src/Renderer.hpp @@ -12,6 +12,24 @@ extern "C" { #include "GameBoard.hpp" +enum class TetrisAssets { + BORDER, + SINGLE, + I, + I_MID, + I_START, + I_END, + I_MIDR, + I_STARTR, + I_ENDR, + J, + L, + O, + S, + Z, + T, +}; + class Renderer { private: void drawGrid(const GameBoard& gameBoard); @@ -19,19 +37,34 @@ private: void drawTetromino(const Tetromino& tetromino); void drawScoreboard(int score, int level); - shared_ptr renderer; - int blockSize; + TetrisAssets shapeToAsset(const TetrominoShape shape); - unordered_map textures; + shared_ptr renderer; + + int blockSize; + int offsetX; + int offsetY; + int screenSpacing = 50; + int windowHeight = 0; + int windowWidth = 0; + + unordered_map textures; public: - Renderer(shared_ptr renderer); + Renderer(shared_ptr renderer, int w, int h); void setRenderer(shared_ptr renderer); void renderBoard(const GameBoard& gb); - bool loadTexture(const string& filePath, const TetrominoShape shape); - void renderTexture(const TetrominoShape shape, int x, int y, int width, int height); + 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); void renderText(string text, int x, int y, int size, SDL_Color color); + + const int getBlockSize( ) const; + void setBlockSize(int newBlockSize); + void setOffset(int newX, int newY); + const int getOffsetX( ) const; + const int getOffsetY( ) const; + void setWindowSize(int w, int h); }; diff --git a/src/Tetromino.cpp b/src/Tetromino.cpp index 26d97eb..47f42b6 100644 --- a/src/Tetromino.cpp +++ b/src/Tetromino.cpp @@ -1,7 +1,7 @@ #include "Tetromino.hpp" #include "GameBoard.hpp" - -Tetromino::Tetromino(TetrominoShape shape) : x(0), y(0) { +#include +Tetromino::Tetromino(TetrominoShape shape) : x(0), y(0), textureShape(shape) { initializeShape(shape); currentRotationState = 1; @@ -16,29 +16,44 @@ Tetromino::Tetromino(TetrominoShape shape) : x(0), y(0) { switch (dist6(rng)) { case 0: - color.r = 255; + color.r = 0; + color.g = 191; + color.b = 255; break; case 1: - color.g = 255; + color.r = 255; + color.g = 215; + color.b = 0; break; case 2: - color.b = 255; + color.r = 138; + color.g = 43; + color.b = 226; break; case 3: - color.r = 255; - color.g = 255; + color.r = 0; + color.g = 204; + color.b = 102; break; case 4: - color.g = 255; - color.b = 255; + color.r = 255; + color.g = 69; + color.b = 0; break; case 5: + color.r = 30; + color.g = 144; color.b = 255; + break; + case 6: color.r = 255; + color.g = 140; + color.b = 0; break; default: break; } + } void Tetromino::initializeShape(TetrominoShape s) { @@ -61,6 +76,9 @@ void Tetromino::initializeShape(TetrominoShape s) { case TetrominoShape::L: shape = { {0, 0, 1}, {1, 1, 1} }; break; + case TetrominoShape::T: + shape = { {1, 1, 1}, {0, 1, 0} }; + break; default: shape = { {0} }; break; @@ -102,11 +120,9 @@ double Tetromino::getRotationAngle( ) const { } const vector>& Tetromino::getShape( ) const { return shape; } +const TetrominoShape Tetromino::getShapeEnumn( ) const { return textureShape; } int Tetromino::getX( ) const { return x; } int Tetromino::getY( ) const { return y; } -void Tetromino::setTexture(const TetrominoShape shape) { this->textureShape = shape; } -const TetrominoShape Tetromino::getTexture( ) const { return textureShape; } - SDL_Color Tetromino::getColor( ) const { return color; } diff --git a/src/Tetromino.hpp b/src/Tetromino.hpp index db49086..d07782c 100644 --- a/src/Tetromino.hpp +++ b/src/Tetromino.hpp @@ -17,7 +17,14 @@ enum class TetrominoShape { S, Z, J, - COUNT //Used to know the ammount of shapes + T, + COUNT, //Used to know the ammount of shapes + I_END, + I_START, + I_MID, + I_ENDR, + I_STARTR, + I_MIDR, }; class GameBoard; @@ -42,10 +49,9 @@ public: double getRotationAngle( ) const; const vector>& getShape( ) const; + const TetrominoShape getShapeEnumn( ) const; int getX( ) const; int getY( ) const; - void setTexture(const TetrominoShape textureName); - const TetrominoShape getTexture( ) const; SDL_Color getColor( ) const; };