mainly refractoring, some fixes
|
Before Width: | Height: | Size: 240 B After Width: | Height: | Size: 240 B |
|
Before Width: | Height: | Size: 223 B After Width: | Height: | Size: 223 B |
|
Before Width: | Height: | Size: 242 B After Width: | Height: | Size: 242 B |
|
Before Width: | Height: | Size: 231 B After Width: | Height: | Size: 231 B |
|
Before Width: | Height: | Size: 246 B After Width: | Height: | Size: 246 B |
|
Before Width: | Height: | Size: 231 B After Width: | Height: | Size: 231 B |
|
Before Width: | Height: | Size: 225 B After Width: | Height: | Size: 225 B |
|
Before Width: | Height: | Size: 205 B After Width: | Height: | Size: 205 B |
|
Before Width: | Height: | Size: 210 B After Width: | Height: | Size: 210 B |
|
Before Width: | Height: | Size: 223 B After Width: | Height: | Size: 223 B |
|
Before Width: | Height: | Size: 224 B After Width: | Height: | Size: 224 B |
|
Before Width: | Height: | Size: 210 B After Width: | Height: | Size: 210 B |
|
Before Width: | Height: | Size: 236 B After Width: | Height: | Size: 236 B |
BIN
assets/sprites/game_over.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
assets/sprites/please_try_again_text.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
74
src/Game.cpp
@@ -1,10 +1,6 @@
|
|||||||
#include "Game.hpp"
|
#include "Game.hpp"
|
||||||
|
|
||||||
Game::Game( ) :
|
Game::Game( ) : window(nullptr, SDL_DestroyWindow) { }
|
||||||
window(nullptr, SDL_DestroyWindow),
|
|
||||||
gameOver(false),
|
|
||||||
startSequence(true),
|
|
||||||
quit(false) { }
|
|
||||||
|
|
||||||
bool Game::init(const char* title, int w, int h) {
|
bool Game::init(const char* title, int w, int h) {
|
||||||
window.reset(SDL_CreateWindow(
|
window.reset(SDL_CreateWindow(
|
||||||
@@ -31,12 +27,14 @@ bool Game::init(const char* title, int w, int h) {
|
|||||||
int ww, wh;
|
int ww, wh;
|
||||||
SDL_GetWindowSize(window.get( ), &ww, &wh);
|
SDL_GetWindowSize(window.get( ), &ww, &wh);
|
||||||
|
|
||||||
|
gameState.startSequence = true;
|
||||||
|
|
||||||
gameRenderer = make_shared<Renderer>(renderer, ww, wh);
|
gameRenderer = make_shared<Renderer>(renderer, ww, wh);
|
||||||
gameBoard = make_shared<GameBoard>( );
|
gameBoard = make_shared<GameBoard>( );
|
||||||
|
|
||||||
handleWindowResize( );
|
handleWindowResize( );
|
||||||
|
|
||||||
bgm = std::shared_ptr<Mix_Music>(Mix_LoadMUS("assets/bgm.mp3"), [ ](Mix_Music* m) {
|
bgm = std::shared_ptr<Mix_Music>(Mix_LoadMUS("assets/sound_tracks/bgm.mp3"), [ ](Mix_Music* m) {
|
||||||
Mix_FreeMusic(m);
|
Mix_FreeMusic(m);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -49,8 +47,8 @@ bool Game::init(const char* title, int w, int h) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Game::run( ) {
|
void Game::run( ) {
|
||||||
while (startSequence) {
|
while (gameState.startSequence) {
|
||||||
if (quit) return;
|
if (gameState.quit) return;
|
||||||
inputHandler( );
|
inputHandler( );
|
||||||
gameRenderer->renderStartScreen( );
|
gameRenderer->renderStartScreen( );
|
||||||
}
|
}
|
||||||
@@ -58,27 +56,26 @@ void Game::run( ) {
|
|||||||
Mix_PlayMusic(bgm.get( ), -1);
|
Mix_PlayMusic(bgm.get( ), -1);
|
||||||
|
|
||||||
lastUpdateTime = SDL_GetTicks( );
|
lastUpdateTime = SDL_GetTicks( );
|
||||||
while (!gameOver && !gameBoard->isCollision( )) {
|
while (!gameState.gameover && !gameBoard->isCollision( )) {
|
||||||
if (quit) return;
|
if (gameState.quit) return;
|
||||||
inputHandler( );
|
inputHandler( );
|
||||||
update( );
|
update( );
|
||||||
render( );
|
render( );
|
||||||
}
|
}
|
||||||
gameOver = true;
|
gameState.gameover = true;
|
||||||
Mix_PauseMusic( );
|
Mix_PauseMusic( );
|
||||||
|
|
||||||
Mix_Chunk* rotateSound = Mix_LoadWAV("assets/sound_effects/game_over.wav");
|
Mix_Chunk* game_over = Mix_LoadWAV("assets/sound_effects/game_over.wav");
|
||||||
if (rotateSound == nullptr)
|
if (game_over == nullptr)
|
||||||
SDL_Log("Failed to play rotate sound effect: %s", Mix_GetError( ));
|
SDL_Log("Failed to play rotate sound effect: %s", Mix_GetError( ));
|
||||||
else {
|
else {
|
||||||
Mix_PlayChannel(-1, rotateSound, 0);
|
Mix_PlayChannel(-1, game_over, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (gameOver) {
|
while (gameState.gameover) {
|
||||||
if (quit) return;
|
if (gameState.quit) return;
|
||||||
inputHandler( );
|
inputHandler( );
|
||||||
render( );
|
gameRenderer->renderGameOver(gameBoard->getWidth( ), gameBoard->getHeight( ));
|
||||||
gameRenderer->renderGameOver(gameBoard->getScore( ), gameBoard->getLevel( ), gameBoard->getLines( ));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,12 +85,13 @@ void Game::inputHandler( ) {
|
|||||||
while (SDL_PollEvent(&event)) {
|
while (SDL_PollEvent(&event)) {
|
||||||
if (event.type == SDL_QUIT) {
|
if (event.type == SDL_QUIT) {
|
||||||
SDL_Quit( );
|
SDL_Quit( );
|
||||||
quit = true;
|
gameState.quit = true;
|
||||||
} else if (event.type == SDL_KEYDOWN) {
|
} else if (event.type == SDL_KEYDOWN) {
|
||||||
switch (event.key.keysym.sym) {
|
switch (event.key.keysym.sym) {
|
||||||
case SDLK_LEFT:
|
case SDLK_LEFT:
|
||||||
case SDLK_a:
|
case SDLK_a:
|
||||||
gameBoard->tryMoveCurrentTetromino(-1, 0);
|
if (!gameState.gameover && !gameState.startSequence)
|
||||||
|
gameBoard->tryMoveCurrentTetromino(-1, 0);
|
||||||
if (movePieceSound == nullptr)
|
if (movePieceSound == nullptr)
|
||||||
SDL_Log("Failed to play rotate sound effect: %s", Mix_GetError( ));
|
SDL_Log("Failed to play rotate sound effect: %s", Mix_GetError( ));
|
||||||
else {
|
else {
|
||||||
@@ -102,7 +100,8 @@ void Game::inputHandler( ) {
|
|||||||
break;
|
break;
|
||||||
case SDLK_RIGHT:
|
case SDLK_RIGHT:
|
||||||
case SDLK_d:
|
case SDLK_d:
|
||||||
gameBoard->tryMoveCurrentTetromino(1, 0);
|
if (!gameState.gameover && !gameState.startSequence)
|
||||||
|
gameBoard->tryMoveCurrentTetromino(1, 0);
|
||||||
if (movePieceSound == nullptr)
|
if (movePieceSound == nullptr)
|
||||||
SDL_Log("Failed to play rotate sound effect: %s", Mix_GetError( ));
|
SDL_Log("Failed to play rotate sound effect: %s", Mix_GetError( ));
|
||||||
else {
|
else {
|
||||||
@@ -111,16 +110,18 @@ void Game::inputHandler( ) {
|
|||||||
break;
|
break;
|
||||||
case SDLK_DOWN:
|
case SDLK_DOWN:
|
||||||
case SDLK_s:
|
case SDLK_s:
|
||||||
gameBoard->moveToBottom( );
|
if (!gameState.gameover && !gameState.startSequence)
|
||||||
|
gameBoard->moveToBottom( );
|
||||||
break;
|
break;
|
||||||
case SDLK_SPACE:
|
case SDLK_SPACE:
|
||||||
gameBoard->tryRotateCurrentTetromino( );
|
if (!gameState.gameover && !gameState.startSequence)
|
||||||
|
gameBoard->tryRotateCurrentTetromino( );
|
||||||
break;
|
break;
|
||||||
case SDLK_ESCAPE:
|
case SDLK_ESCAPE:
|
||||||
break;
|
break;
|
||||||
case SDLK_g:
|
case SDLK_g:
|
||||||
if (startSequence) {
|
if (gameState.startSequence) {
|
||||||
startSequence = false;
|
gameState.startSequence = false;
|
||||||
Mix_Chunk* menuSound = Mix_LoadWAV("assets/sound_effects/menu.wav");
|
Mix_Chunk* menuSound = Mix_LoadWAV("assets/sound_effects/menu.wav");
|
||||||
if (menuSound == nullptr)
|
if (menuSound == nullptr)
|
||||||
SDL_Log("Failed to play rotate sound effect: %s", Mix_GetError( ));
|
SDL_Log("Failed to play rotate sound effect: %s", Mix_GetError( ));
|
||||||
@@ -135,7 +136,7 @@ void Game::inputHandler( ) {
|
|||||||
break;
|
break;
|
||||||
case SDLK_q:
|
case SDLK_q:
|
||||||
SDL_Quit( );
|
SDL_Quit( );
|
||||||
quit = true;
|
gameState.quit = true;
|
||||||
case SDLK_EQUALS:
|
case SDLK_EQUALS:
|
||||||
SDL_Log("Test %d", Mix_GetMusicVolume(bgm.get( )));
|
SDL_Log("Test %d", Mix_GetMusicVolume(bgm.get( )));
|
||||||
Mix_VolumeMusic(Mix_GetMusicVolume(bgm.get( )) + 8);
|
Mix_VolumeMusic(Mix_GetMusicVolume(bgm.get( )) + 8);
|
||||||
@@ -177,10 +178,10 @@ void Game::handleWindowResize( ) {
|
|||||||
int windowWidth, windowHeight;
|
int windowWidth, windowHeight;
|
||||||
SDL_GetWindowSize(window.get( ), &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 offsetX = (windowWidth - gameBoard->getWidth( ) * gameRenderer->getBlockSize( )) / 2;
|
||||||
int offsetY = (windowHeight - gameBoard->getLockedTetrominos( ).size( ) * gameRenderer->getBlockSize( )) / 2;
|
int offsetY = (windowHeight - gameBoard->getHeight( ) * gameRenderer->getBlockSize( )) / 2;
|
||||||
gameRenderer->setOffset(offsetX, offsetY);
|
gameRenderer->setOffset(offsetX, offsetY);
|
||||||
|
|
||||||
gameRenderer->setWindowSize(windowWidth, windowHeight);
|
gameRenderer->setWindowSize(windowWidth, windowHeight);
|
||||||
@@ -197,23 +198,22 @@ void Game::update( ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Game::render( ) {
|
void Game::render( ) {
|
||||||
//Set default color to black
|
// Background color
|
||||||
SDL_SetRenderDrawColor(renderer.get( ), 248, 248, 248, 255);
|
SDL_SetRenderDrawColor(renderer.get( ), 248, 248, 248, 255);
|
||||||
SDL_RenderClear(renderer.get( ));
|
SDL_RenderClear(renderer.get( ));
|
||||||
|
|
||||||
gameRenderer->renderBoard(*gameBoard);
|
gameRenderer->renderBoard(gameBoard);
|
||||||
gameRenderer->renderTetrominoPreview(gameBoard->getNextTetromino( ));
|
gameRenderer->renderTetrominoPreview(gameBoard->getNextTetromino( ));
|
||||||
|
|
||||||
SDL_RenderPresent(renderer.get( ));
|
SDL_RenderPresent(renderer.get( ));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Game::isGameOver( ) { return gameOver; }
|
|
||||||
void Game::setGameOver(bool value) { gameOver = value; }
|
|
||||||
|
|
||||||
void Game::restart( ) {
|
void Game::restart( ) {
|
||||||
gameOver = false;
|
gameState.gameover = false;
|
||||||
startSequence = true;
|
gameState.startSequence = true;
|
||||||
gameBoard = make_shared<GameBoard>( );
|
gameBoard = make_shared<GameBoard>( );
|
||||||
}
|
}
|
||||||
|
|
||||||
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; }
|
||||||
|
|||||||
13
src/Game.hpp
@@ -31,9 +31,11 @@ private:
|
|||||||
Uint32 lastUpdateTime = 0;
|
Uint32 lastUpdateTime = 0;
|
||||||
int dropInterval = 1000;
|
int dropInterval = 1000;
|
||||||
|
|
||||||
bool gameOver;
|
struct GameState {
|
||||||
bool startSequence;
|
bool gameover = false;
|
||||||
bool quit;
|
bool startSequence = false;
|
||||||
|
bool quit = false;
|
||||||
|
} gameState;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Game( );
|
Game( );
|
||||||
@@ -41,8 +43,9 @@ public:
|
|||||||
bool init(const char* title, int w, int h);
|
bool init(const char* title, int w, int h);
|
||||||
void run( );
|
void run( );
|
||||||
void restart( );
|
void restart( );
|
||||||
bool isGameOver( );
|
|
||||||
void setGameOver(bool value);
|
const bool isGameOver( ) const;
|
||||||
|
const void setGameOver(bool value);
|
||||||
|
|
||||||
const bool isGameQuit( ) const;
|
const bool isGameQuit( ) const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ GameBoard::GameBoard( )
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool GameBoard::tryMoveCurrentTetromino(int dx, int dy) {
|
bool GameBoard::tryMoveCurrentTetromino(int dx, int dy) {
|
||||||
|
if (!currentTetromino) return;
|
||||||
currentTetromino->move(dx, dy);
|
currentTetromino->move(dx, dy);
|
||||||
if (checkCollision(*currentTetromino)) {
|
if (checkCollision(*currentTetromino)) {
|
||||||
currentTetromino->move(-dx, -dy);
|
currentTetromino->move(-dx, -dy);
|
||||||
@@ -16,6 +17,7 @@ bool GameBoard::tryMoveCurrentTetromino(int dx, int dy) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GameBoard::tryRotateCurrentTetromino( ) {
|
void GameBoard::tryRotateCurrentTetromino( ) {
|
||||||
|
if (!currentTetromino) return;
|
||||||
currentTetromino->rotate(*this);
|
currentTetromino->rotate(*this);
|
||||||
if (checkCollision(*currentTetromino))
|
if (checkCollision(*currentTetromino))
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
@@ -181,10 +183,15 @@ void GameBoard::spawnNewTetromino( ) {
|
|||||||
collision = true;
|
collision = true;
|
||||||
lockedTetrominos.clear( );
|
lockedTetrominos.clear( );
|
||||||
lockedColors.clear( );
|
lockedColors.clear( );
|
||||||
|
currentTetromino = nullptr;
|
||||||
|
nextTetromino = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameBoard::update( ) {
|
void GameBoard::update( ) {
|
||||||
|
if (!currentTetromino)
|
||||||
|
spawnNewTetromino( );
|
||||||
|
|
||||||
if (!tryMoveCurrentTetromino(0, 1)) {
|
if (!tryMoveCurrentTetromino(0, 1)) {
|
||||||
lockTetromino( );
|
lockTetromino( );
|
||||||
clearLines( );
|
clearLines( );
|
||||||
@@ -218,12 +225,7 @@ const shared_ptr<Tetromino> GameBoard::getNextTetromino( ) const { return nextTe
|
|||||||
|
|
||||||
const vector<vector<int>>& GameBoard::getLockedTetrominos( ) const { return lockedTetrominos; }
|
const vector<vector<int>>& GameBoard::getLockedTetrominos( ) const { return lockedTetrominos; }
|
||||||
const vector<vector<SDL_Color>>& GameBoard::getLockedColors( ) const { return lockedColors; }
|
const vector<vector<SDL_Color>>& GameBoard::getLockedColors( ) const { return lockedColors; }
|
||||||
const Tetromino& GameBoard::getCurrentTetromino( ) const { return *currentTetromino; }
|
const shared_ptr<Tetromino> GameBoard::getCurrentTetromino( ) const { return currentTetromino; }
|
||||||
|
|
||||||
void GameBoard::reset( ) {
|
const int GameBoard::getWidth( ) const { return width; }
|
||||||
lockedTetrominos.clear( );
|
const int GameBoard::getHeight( ) const { return height; }
|
||||||
lockedColors.clear( );
|
|
||||||
score = 0;
|
|
||||||
level = 1;
|
|
||||||
collision = false;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ public:
|
|||||||
void tryRotateCurrentTetromino( );
|
void tryRotateCurrentTetromino( );
|
||||||
bool isValidPosition(const vector<vector<int>>& shape, int x, int y) const;
|
bool isValidPosition(const vector<vector<int>>& shape, int x, int y) const;
|
||||||
void moveToBottom( );
|
void moveToBottom( );
|
||||||
void reset( );
|
|
||||||
|
|
||||||
const bool isCollision( ) const;
|
const bool isCollision( ) const;
|
||||||
const int getScore( ) const;
|
const int getScore( ) const;
|
||||||
@@ -44,5 +43,8 @@ public:
|
|||||||
|
|
||||||
const vector<vector<int>>& getLockedTetrominos( ) const;
|
const vector<vector<int>>& getLockedTetrominos( ) const;
|
||||||
const vector<vector<SDL_Color>>& getLockedColors( ) const;
|
const vector<vector<SDL_Color>>& getLockedColors( ) const;
|
||||||
const Tetromino& getCurrentTetromino( ) const;
|
const shared_ptr<Tetromino> getCurrentTetromino( ) const;
|
||||||
|
|
||||||
|
const int getWidth( ) const;
|
||||||
|
const int getHeight( ) const;
|
||||||
};
|
};
|
||||||
|
|||||||
545
src/Renderer.cpp
@@ -2,147 +2,164 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
Renderer::Renderer(shared_ptr<SDL_Renderer> renderer, int w, int h) : renderer(renderer), blockSize(30), windowHeight(h), windowWidth(w) {
|
Renderer::Renderer(shared_ptr<SDL_Renderer> 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<SDL_Renderer> renderer) {
|
void Renderer::renderBoard(const shared_ptr<GameBoard> gameBoard) {
|
||||||
this->renderer = renderer;
|
drawWall(gameBoard->getWidth( ), gameBoard->getHeight( ));
|
||||||
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);
|
|
||||||
drawLockedBlocks(gameBoard);
|
drawLockedBlocks(gameBoard);
|
||||||
drawTetromino(gameBoard.getCurrentTetromino( ));
|
drawTetromino(gameBoard->getCurrentTetromino( ));
|
||||||
drawScoreboard(gameBoard.getScore( ), gameBoard.getLevel( ), gameBoard.getLines( ));
|
drawScoreboard(gameBoard->getScore( ), gameBoard->getLevel( ), gameBoard->getLines( ));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::drawScoreboard(int score, int level, int lines) {
|
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
|
// 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, (windowWidth / 2) - (blockSize * 6) - blockSize / 8, windowHeight };
|
||||||
|
|
||||||
SDL_Rect blackRect = { 0, 0, blackAreaWidth, windowHeight };
|
|
||||||
SDL_SetRenderDrawColor(renderer.get( ), 0, 0, 0, 255);
|
SDL_SetRenderDrawColor(renderer.get( ), 0, 0, 0, 255);
|
||||||
SDL_RenderFillRect(renderer.get( ), &blackRect);
|
SDL_RenderFillRect(renderer.get( ), &blackRect);
|
||||||
|
|
||||||
SDL_Surface* scoreboardSurface = IMG_Load("assets/scoreboard.png");
|
auto surf = unique_ptr<SDL_Surface, decltype(&SDL_FreeSurface)>(IMG_Load("assets/sprites/scoreboard.png"), SDL_FreeSurface);
|
||||||
if (!scoreboardSurface) {
|
renderTexture("assets/sprites/scoreboard.png", windowWidth - surf->w * static_cast<float>(windowHeight) / surf->h, 0, surf->w * static_cast<float>(windowHeight) / surf->h, windowHeight);
|
||||||
SDL_Log("Failed to load title surface: %s", SDL_GetError( ));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto scoreboardTexture = SDL_CreateTextureFromSurface(renderer.get( ), scoreboardSurface);
|
renderText(fmt::format("{0}", score), windowWidth - 30, 97, 32, SDL_Color{ 0,0,0 }, HAlign::RIGHT);
|
||||||
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<int>(width * static_cast<float>(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}", level), windowWidth - 92, 230, 32, SDL_Color{ 0,0,0 });
|
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 });
|
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) {
|
void Renderer::drawWall(const int w, const int h) {
|
||||||
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;
|
|
||||||
int innerBorderThickness = blockSize / 4;
|
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) {
|
for (int y = 0; y < (h + (innerBorderThickness * h)); ++y) {
|
||||||
SDL_Rect leftBlock = { offsetX - blockSize, offsetY + y * blockSize, blockSize, blockSize };
|
SDL_Color color{ 165, 42, 42 };
|
||||||
renderTexture(TetrisAssets::BORDER, leftBlock.x, leftBlock.y - (innerBorderThickness * y + 1), leftBlock.w, leftBlock.h);
|
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);
|
||||||
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) {
|
void Renderer::drawLockedBlocks(const shared_ptr<GameBoard> gameBoard) {
|
||||||
const auto& lockedTetrominos = gameBoard.getLockedTetrominos( );
|
const auto& lockedTetrominos = gameBoard->getLockedTetrominos( );
|
||||||
const auto& lockedColors = gameBoard.getLockedColors( );
|
const auto& lockedColors = gameBoard->getLockedColors( );
|
||||||
|
|
||||||
for (int row = 0; row < lockedTetrominos.size( ); ++row) {
|
for (int row = 0; row < lockedTetrominos.size( ); ++row) {
|
||||||
for (int col = 0; col < lockedTetrominos[row].size( ); ++col) {
|
for (int col = 0; col < lockedTetrominos[row].size( ); ++col) {
|
||||||
int blockType = lockedTetrominos[row][col];
|
int blockType = lockedTetrominos[row][col];
|
||||||
if (blockType != 0) {
|
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<TetrominoShape>(blockType - 1))],
|
||||||
|
offsetX + col * blockSize,
|
||||||
|
offsetY + row * blockSize,
|
||||||
|
blockSize,
|
||||||
|
blockSize,
|
||||||
|
color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TetrominoShape shape = static_cast<TetrominoShape>(blockType - 1);
|
void Renderer::drawTetromino(const shared_ptr<Tetromino> tetromino) {
|
||||||
TetrisAssets asset = shapeToAsset(shape);
|
if (!tetromino) return;
|
||||||
|
|
||||||
if (textures[asset]) {
|
int x = tetromino->getX( ), y = tetromino->getY( );
|
||||||
SDL_Color color = lockedColors[row][col];
|
|
||||||
SDL_SetTextureBlendMode(textures[asset], SDL_BLENDMODE_BLEND);
|
if (tetromino->getShapeEnumn( ) == TetrominoShape::I) {
|
||||||
SDL_SetTextureColorMod(textures[asset], color.r, color.g, color.b);
|
double angle = tetromino->getRotationAngle( );
|
||||||
renderTexture(asset, block.x, block.y, block.w, block.h);
|
|
||||||
} else {
|
if (angle == 90 || angle == 270) {
|
||||||
SDL_Log("Texture not found for asset: %d", asset);
|
for (int i = 0; i < 4; ++i) {
|
||||||
SDL_SetRenderDrawColor(renderer.get( ), 255, 0, 0, 255);
|
renderTexture(
|
||||||
SDL_RenderFillRect(renderer.get( ), &block);
|
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) {
|
switch (shape) {
|
||||||
case TetrominoShape::I:
|
case TetrominoShape::I:
|
||||||
return TetrisAssets::I;
|
return TetrisAssets::I;
|
||||||
@@ -171,80 +188,11 @@ TetrisAssets Renderer::shapeToAsset(const TetrominoShape shape) {
|
|||||||
case TetrominoShape::I_MIDR:
|
case TetrominoShape::I_MIDR:
|
||||||
return TetrisAssets::I_MIDR;
|
return TetrisAssets::I_MIDR;
|
||||||
default:
|
default:
|
||||||
|
return TetrisAssets::I;
|
||||||
break;
|
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( ) {
|
void Renderer::renderStartScreen( ) {
|
||||||
SDL_SetRenderDrawColor(renderer.get( ), 248, 248, 248, 255);
|
SDL_SetRenderDrawColor(renderer.get( ), 248, 248, 248, 255);
|
||||||
SDL_RenderClear(renderer.get( ));
|
SDL_RenderClear(renderer.get( ));
|
||||||
@@ -259,161 +207,171 @@ void Renderer::renderStartScreen( ) {
|
|||||||
SDL_SetRenderDrawColor(renderer.get( ), 0, 0, 0, 255);
|
SDL_SetRenderDrawColor(renderer.get( ), 0, 0, 0, 255);
|
||||||
SDL_RenderDrawRect(renderer.get( ), &borderRect);
|
SDL_RenderDrawRect(renderer.get( ), &borderRect);
|
||||||
|
|
||||||
SDL_Surface* titleSurface = IMG_Load("assets/title.png");
|
int titleWidth = static_cast<int>(windowWidth * 0.8f);
|
||||||
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<int>(windowWidth * titleScaleFactor);
|
|
||||||
int titleHeight = static_cast<int>(titleWidth * 0.315f);
|
int titleHeight = static_cast<int>(titleWidth * 0.315f);
|
||||||
|
|
||||||
SDL_Rect titleRect = {
|
renderTexture(
|
||||||
(windowWidth / 2) - (titleWidth / 2),
|
"assets/sprites/title.png",
|
||||||
static_cast<int>(windowHeight * 0.2f),
|
(windowWidth / 2) - (titleWidth / 2),
|
||||||
titleWidth,
|
static_cast<int>(windowHeight * 0.160f),
|
||||||
titleHeight
|
titleWidth,
|
||||||
};
|
titleHeight
|
||||||
|
);
|
||||||
|
|
||||||
SDL_RenderCopy(renderer.get( ), titleTexture, nullptr, &titleRect);
|
auto titleBg = unique_ptr<SDL_Surface, decltype(&SDL_FreeSurface)>(IMG_Load("assets/sprites/title_bg.png"), SDL_FreeSurface);
|
||||||
SDL_DestroyTexture(titleTexture);
|
|
||||||
|
|
||||||
SDL_Surface* titleBg = IMG_Load("assets/title_bg.png");
|
int titleBgWidth = static_cast<int>(windowWidth * 0.8f);
|
||||||
if (!titleBg) {
|
int titleBgHeight = static_cast<int>(titleBgWidth * (static_cast<float>(titleBg->h) / titleBg->w));
|
||||||
SDL_Log("Failed to load background surface: %s", SDL_GetError( ));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto titleBgTexture = SDL_CreateTextureFromSurface(renderer.get( ), titleBg);
|
renderTexture(
|
||||||
int originalBgWidth = titleBg->w;
|
"assets/sprites/title_bg.png",
|
||||||
int originalBgHeight = titleBg->h;
|
(windowWidth / 2) - (titleBgWidth / 2),
|
||||||
SDL_FreeSurface(titleBg);
|
static_cast<int>(windowHeight * 0.5f),
|
||||||
if (!titleBgTexture) {
|
titleBgWidth,
|
||||||
SDL_Log("Failed to create background texture: %s", SDL_GetError( ));
|
titleBgHeight
|
||||||
return;
|
);
|
||||||
}
|
renderText("press G to start", (windowWidth / 2), windowHeight - 70, 16, SDL_Color{ 0, 0, 0 }, HAlign::CENTER);
|
||||||
|
|
||||||
float bgScaleFactor = 0.8f;
|
|
||||||
int titleBgWidth = static_cast<int>(windowWidth * bgScaleFactor);
|
|
||||||
int titleBgHeight = static_cast<int>(titleBgWidth * (static_cast<float>(originalBgHeight) / originalBgWidth));
|
|
||||||
if (titleBgHeight > windowHeight * 0.4f) {
|
|
||||||
titleBgHeight = static_cast<int>(windowHeight * 0.4f);
|
|
||||||
titleBgWidth = static_cast<int>(titleBgHeight * (static_cast<float>(originalBgWidth) / originalBgHeight));
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_Rect titleBgRect = {
|
|
||||||
(windowWidth / 2) - (titleBgWidth / 2),
|
|
||||||
static_cast<int>(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 });
|
|
||||||
|
|
||||||
SDL_RenderPresent(renderer.get( ));
|
SDL_RenderPresent(renderer.get( ));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::renderGameOver(int score, int level, int lines) {
|
void Renderer::renderGameOver(int gbWidth, int gbHeight) {
|
||||||
int fontSize = 24;
|
//Needed to draw the Walls again
|
||||||
int lineSpacing = fontSize + 10;
|
drawWall(gbWidth, gbHeight);
|
||||||
|
|
||||||
int totalHeight = 5 * lineSpacing;
|
auto gameOver = unique_ptr<SDL_Surface, decltype(&SDL_FreeSurface)>(IMG_Load("assets/sprites/game_over.png"), SDL_FreeSurface);
|
||||||
|
|
||||||
int centerX = (windowWidth / 2);
|
int gameOverWidth = static_cast<int>(windowWidth * 0.3f);
|
||||||
int centerY = (windowHeight / 2) - (totalHeight / 2);
|
int gameOverHeight = static_cast<int>(gameOverWidth * (static_cast<float>(gameOver->h) / gameOver->w));
|
||||||
|
|
||||||
renderText("game", centerX, centerY, fontSize, SDL_Color{ 0, 0, 0 });
|
renderTexture(
|
||||||
renderText("over", centerX, centerY + lineSpacing, fontSize, SDL_Color{ 0, 0, 0 });
|
"assets/sprites/game_over.png",
|
||||||
renderText("please", centerX, centerY + 2 * lineSpacing, fontSize, SDL_Color{ 0, 0, 0 });
|
(windowWidth / 2) - (gameOverWidth / 2),
|
||||||
renderText("try", centerX, centerY + 3 * lineSpacing, fontSize, SDL_Color{ 0, 0, 0 });
|
static_cast<int>(windowHeight * 0.15f),
|
||||||
renderText("again @", centerX, centerY + 4 * lineSpacing, fontSize, SDL_Color{ 0, 0, 0 });
|
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( ));
|
SDL_RenderPresent(renderer.get( ));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::renderTetrominoPreview(const shared_ptr<Tetromino> nextTetromino) {
|
void Renderer::renderTetrominoPreview(const shared_ptr<Tetromino> nextTetromino) {
|
||||||
const auto& shape = nextTetromino->getShape( );
|
if (!nextTetromino) return;
|
||||||
|
|
||||||
int x = nextTetromino->getX( ), y = nextTetromino->getY( );
|
int x = nextTetromino->getX( ), y = nextTetromino->getY( );
|
||||||
|
|
||||||
double angle = nextTetromino->getRotationAngle( );
|
if (nextTetromino->getShapeEnumn( ) == TetrominoShape::I) {
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (int i = 0; i < 4; ++i) {
|
||||||
SDL_Rect destRect = { (windowWidth - 155) + (x + i) * blockSize, (windowHeight - 120) + y * blockSize, blockSize, blockSize };
|
renderTexture(
|
||||||
SDL_RenderCopy(renderer.get( ), textures[TetrisAssets::I_MIDR], nullptr, &destRect);
|
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 };
|
renderTexture(
|
||||||
SDL_RenderCopy(renderer.get( ), textures[TetrisAssets::I_ENDR], nullptr, &startRect);
|
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 };
|
renderTexture(
|
||||||
SDL_RenderCopy(renderer.get( ), textures[TetrisAssets::I_STARTR], nullptr, &endRect);
|
textures[TetrisAssets::I_STARTR],
|
||||||
|
(windowWidth - 155) + (x + 3) * blockSize,
|
||||||
|
(windowHeight - 120) + y * blockSize,
|
||||||
|
blockSize,
|
||||||
|
blockSize,
|
||||||
|
nextTetromino->getColor( )
|
||||||
|
);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
for (int row = 0; row < shape.size( ); ++row) {
|
for (int row = 0; row < nextTetromino->getShape( ).size( ); ++row) {
|
||||||
for (int col = 0; col < shape[row].size( ); ++col) {
|
for (int col = 0; col < nextTetromino->getShape( )[row].size( ); ++col) {
|
||||||
if (shape[row][col] != 0) {
|
if (nextTetromino->getShape( )[row][col] != 0) {
|
||||||
SDL_Rect destRect = { (windowWidth - 140) + col * blockSize, (windowHeight - 130) + row * blockSize, blockSize, blockSize };
|
renderTexture(
|
||||||
SDL_Point center = { blockSize / 2, blockSize / 2 };
|
textures[shapeToAsset(nextTetromino->getShapeEnumn( ))],
|
||||||
SDL_RenderCopy(renderer.get( ), textures[shapeToAsset(tetrominoShape)], nullptr, &destRect);
|
(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) {
|
const Renderer::TextDimensions Renderer::renderText(const string& text, int x, int y, int fontSize, SDL_Color color, HAlign hAlign, VAlign vAlign) {
|
||||||
TTF_Font* font = TTF_OpenFont("assets/tetris-gb.ttf", size);
|
auto font = unique_ptr<TTF_Font, decltype(&TTF_CloseFont)>(TTF_OpenFont("assets/font/tetris-gb.ttf", fontSize), TTF_CloseFont);
|
||||||
SDL_Surface* surface = TTF_RenderText_Solid(font, text.c_str( ), color);
|
if (!font) { SDL_Log("Failed to create font: %s", TTF_GetError( )); return{ 0,0,0,0 }; }
|
||||||
if (!surface) {
|
|
||||||
SDL_Log("Failed to create surface: %s", TTF_GetError( ));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer.get( ), surface);
|
auto surface = unique_ptr<SDL_Surface, decltype(&SDL_FreeSurface)>(TTF_RenderText_Solid(font.get( ), text.c_str( ), color), SDL_FreeSurface);
|
||||||
if (!texture) {
|
if (!surface) { SDL_Log("Failed to create surface: %s", TTF_GetError( ));return{ 0,0,0,0 }; }
|
||||||
SDL_Log("Failed to create texture from surface: %s", SDL_GetError( ));
|
|
||||||
SDL_FreeSurface(surface);
|
auto texture = unique_ptr<SDL_Texture, decltype(&SDL_DestroyTexture)>(SDL_CreateTextureFromSurface(renderer.get( ), surface.get( )), SDL_DestroyTexture);
|
||||||
TTF_CloseFont(font);
|
if (!texture) { SDL_Log("Failed to create texture from surface: %s", SDL_GetError( ));return{ 0,0,0,0 }; }
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int width = surface->w;
|
int width = surface->w;
|
||||||
int height = surface->h;
|
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_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);
|
const Renderer::TextureDimensions Renderer::renderTexture(
|
||||||
SDL_DestroyTexture(texture);
|
const string& texturePath, int x, int y, int width, int height,
|
||||||
TTF_CloseFont(font);
|
SDL_Color color, float scale, HAlign textHAlign, VAlign textVAlign) {
|
||||||
|
|
||||||
|
auto surface = unique_ptr <SDL_Surface, decltype(&SDL_FreeSurface)>(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_Texture, decltype(&SDL_DestroyTexture)>(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<int>((width == 0 ? surface->w : width) * scale);
|
||||||
|
int textureHeight = static_cast<int>((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; }
|
const int Renderer::getBlockSize( ) const { return blockSize; }
|
||||||
@@ -433,3 +391,4 @@ void Renderer::setWindowSize(int w, int h) {
|
|||||||
windowWidth = w;
|
windowWidth = w;
|
||||||
windowHeight = h;
|
windowHeight = h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,35 +32,60 @@ enum class TetrisAssets {
|
|||||||
|
|
||||||
class Renderer {
|
class Renderer {
|
||||||
private:
|
private:
|
||||||
void drawGrid(const GameBoard& gameBoard);
|
void drawWall(const int w, const int h);
|
||||||
void drawLockedBlocks(const GameBoard& gameBoard);
|
void drawLockedBlocks(const shared_ptr<GameBoard> gameBoard);
|
||||||
void drawTetromino(const Tetromino& tetromino);
|
void drawTetromino(const shared_ptr<Tetromino> tetromino);
|
||||||
void drawScoreboard(int score, int level, int lines);
|
void drawScoreboard(int score, int level, int lines);
|
||||||
|
|
||||||
TetrisAssets shapeToAsset(const TetrominoShape shape);
|
const bool loadTexture(const string& filePath, const TetrisAssets shape);
|
||||||
|
|
||||||
shared_ptr<SDL_Renderer> renderer;
|
const TetrisAssets shapeToAsset(const TetrominoShape shape) const;
|
||||||
|
|
||||||
|
const shared_ptr<SDL_Renderer> renderer;
|
||||||
|
|
||||||
int blockSize;
|
int blockSize;
|
||||||
int offsetX;
|
int offsetX, offsetY;
|
||||||
int offsetY;
|
int windowHeight = 0, windowWidth = 0;
|
||||||
int screenSpacing = 50;
|
|
||||||
int windowHeight = 0;
|
unordered_map<TetrisAssets, string> textures;
|
||||||
int windowWidth = 0;
|
|
||||||
|
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<TetrisAssets, SDL_Texture*> textures;
|
|
||||||
public:
|
public:
|
||||||
Renderer(shared_ptr<SDL_Renderer> renderer, int w, int h);
|
Renderer(shared_ptr<SDL_Renderer> renderer, int w, int h);
|
||||||
|
|
||||||
void setRenderer(shared_ptr<SDL_Renderer> renderer);
|
void renderBoard(const shared_ptr<GameBoard> gameBoard);
|
||||||
void renderBoard(const GameBoard& gb);
|
|
||||||
|
|
||||||
bool loadTexture(const string& filePath, const TetrisAssets shape);
|
|
||||||
void renderTexture(const TetrisAssets shape, int x, int y, int width, int height);
|
|
||||||
void renderStartScreen( );
|
void renderStartScreen( );
|
||||||
void renderGameOver(int score, int level, int lines);
|
void renderGameOver(int gbWidth, int gbHeight);
|
||||||
void renderText(string text, int x, int y, int size, SDL_Color color);
|
const TextDimensions renderText(
|
||||||
void renderRightAlignedText(const std::string& text, int x, int y, int fontSize, const SDL_Color& color);
|
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<Tetromino> nextTetromino);
|
void renderTetrominoPreview(const shared_ptr<Tetromino> nextTetromino);
|
||||||
|
|
||||||
const int getBlockSize( ) const;
|
const int getBlockSize( ) const;
|
||||||
|
|||||||