diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c2dcc4..cca3134 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,8 +3,10 @@ cmake_minimum_required(VERSION 3.5) project(SDL_TD VERSION 0.1 LANGUAGES CXX) # Find SDL2 -find_package(SDL2 QUIET) +find_package(SDL2 REQUIRED) find_package(SDL2_mixer REQUIRED) +find_package(SDL2_image REQUIRED) +find_package(SDL2_ttf REQUIRED) # Set SDL include directories and libraries set(SDL_INCLUDE_DIRS ${SDL2_INCLUDE_DIRS}) diff --git a/src/Game.cpp b/src/Game.cpp index f045617..5b25379 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -1,6 +1,6 @@ #include "Game.hpp" -Game::Game( ) : window(nullptr, SDL_DestroyWindow) { } +Game::Game( ) : window(nullptr, SDL_DestroyWindow), sound(make_unique( )) { } bool Game::init(const char* title, int w, int h) { window.reset(SDL_CreateWindow( @@ -34,15 +34,6 @@ bool Game::init(const char* title, int w, int h) { handleWindowResize( ); - bgm = std::shared_ptr(Mix_LoadMUS("assets/sound_tracks/bgm.mp3"), [ ](Mix_Music* m) { - Mix_FreeMusic(m); - }); - - if (!bgm) { - SDL_Log("Failed to load background music: %s", Mix_GetError( )); - return false; - } - return true; } @@ -53,7 +44,7 @@ void Game::run( ) { gameRenderer->renderStartScreen( ); } - Mix_PlayMusic(bgm.get( ), -1); + sound->PlayMusic(MusicName::MAIN_THEME); lastUpdateTime = SDL_GetTicks( ); while (!gameState.gameover && !gameBoard->isCollision( )) { @@ -62,15 +53,10 @@ void Game::run( ) { update( ); render( ); } - gameState.gameover = true; - Mix_PauseMusic( ); - 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, game_over, 0); - } + gameState.gameover = true; + sound->PauseMusic( ); + sound->PlaySound(SoundName::GAME_OVER); while (gameState.gameover) { if (gameState.quit) return; @@ -83,7 +69,6 @@ void Game::run( ) { void Game::inputHandler( ) { SDL_Event event; - Mix_Chunk* movePieceSound = Mix_LoadWAV("assets/sound_effects/move_piece.wav"); while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { SDL_Quit( ); @@ -93,43 +78,33 @@ void Game::inputHandler( ) { case SDLK_LEFT: case SDLK_a: if (!gameState.gameover && !gameState.startSequence) - gameBoard->tryMoveCurrentTetromino(-1, 0); - if (movePieceSound == nullptr) - SDL_Log("Failed to play rotate sound effect: %s", Mix_GetError( )); - else { - Mix_PlayChannel(-1, movePieceSound, 0); - } + if (gameBoard->tryMoveCurrentTetromino(-1, 0)) + sound->PlaySound(SoundName::MOVE_PIECE); break; case SDLK_RIGHT: case SDLK_d: if (!gameState.gameover && !gameState.startSequence) - gameBoard->tryMoveCurrentTetromino(1, 0); - if (movePieceSound == nullptr) - SDL_Log("Failed to play rotate sound effect: %s", Mix_GetError( )); - else { - Mix_PlayChannel(-1, movePieceSound, 0); - } + if (gameBoard->tryMoveCurrentTetromino(1, 0)) + sound->PlaySound(SoundName::MOVE_PIECE); break; case SDLK_DOWN: case SDLK_s: - if (!gameState.gameover && !gameState.startSequence) + if (!gameState.gameover && !gameState.startSequence) { gameBoard->moveToBottom( ); + sound->PlaySound(SoundName::PIECE_LANDED); + } break; case SDLK_SPACE: if (!gameState.gameover && !gameState.startSequence) - gameBoard->tryRotateCurrentTetromino( ); + if (gameBoard->tryRotateCurrentTetromino( )) + sound->PlaySound(SoundName::ROTATE_PIECE); break; case SDLK_ESCAPE: break; case SDLK_g: 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( )); - else { - Mix_PlayChannel(-1, menuSound, 0); - } + sound->PlaySound(SoundName::MENU); } break; case SDLK_r: @@ -149,11 +124,10 @@ void Game::inputHandler( ) { Mix_VolumeMusic(Mix_GetMusicVolume(bgm.get( )) - 8); break; case SDLK_m: - if (Mix_PausedMusic( )) - Mix_ResumeMusic( ); + if (true) + sound->ResumeMusic( ); else - Mix_PauseMusic( ); - + sound->PauseMusic( ); break; default: break; diff --git a/src/Game.hpp b/src/Game.hpp index 0042aa6..64c5cbd 100644 --- a/src/Game.hpp +++ b/src/Game.hpp @@ -9,6 +9,7 @@ extern "C" { #include "Renderer.hpp" #include "GameBoard.hpp" +#include "Sound.hpp" using namespace std; @@ -37,6 +38,8 @@ private: bool quit = false; } gameState; + const unique_ptr sound; + public: Game( ); diff --git a/src/GameBoard.cpp b/src/GameBoard.cpp index 36ae544..e2deb7d 100644 --- a/src/GameBoard.cpp +++ b/src/GameBoard.cpp @@ -2,7 +2,9 @@ #include GameBoard::GameBoard( ) - : lockedTetrominos(20, vector(10, 0)), lockedColors(20, std::vector(10, { 0, 0, 0, 255 })), score(0), level(0), lines(0), collision(false) { + : lockedTetrominos(20, vector(10, 0)), + lockedColors(20, std::vector(10, { 0, 0, 0, 255 })), score(0), level(0), lines(0), collision(false), + sound(make_unique( )) { spawnNewTetromino( ); } @@ -16,20 +18,15 @@ bool GameBoard::tryMoveCurrentTetromino(int dx, int dy) { return true; } -void GameBoard::tryRotateCurrentTetromino( ) { - if (!currentTetromino) return; +bool GameBoard::tryRotateCurrentTetromino( ) { + if (!currentTetromino) return false; currentTetromino->rotate(*this); - if (checkCollision(*currentTetromino)) + if (checkCollision(*currentTetromino)) { for (int i = 0; i < 3; i++) currentTetromino->rotate(*this); - else { - Mix_Chunk* rotateSound = Mix_LoadWAV("assets/sound_effects/rotate_piece.wav"); - if (rotateSound == nullptr) - SDL_Log("Failed to play rotate sound effect: %s", Mix_GetError( )); - else { - Mix_PlayChannel(-1, rotateSound, 0); - } + return false; } + return true; } bool GameBoard::checkCollision(const Tetromino& tetromino) const { @@ -108,12 +105,7 @@ void GameBoard::lockTetromino( ) { } } - Mix_Chunk* pieceLanded = Mix_LoadWAV("assets/sound_effects/piece_landed.wav"); - if (pieceLanded == nullptr) - SDL_Log("Failed to play sound effect: %s", Mix_GetError( )); - else { - Mix_PlayChannel(-1, pieceLanded, 0); - } + sound->PlaySound(SoundName::PIECE_LANDED); } void GameBoard::clearLines( ) { diff --git a/src/GameBoard.hpp b/src/GameBoard.hpp index 555cfae..4aa66f2 100644 --- a/src/GameBoard.hpp +++ b/src/GameBoard.hpp @@ -8,6 +8,7 @@ extern "C" { #include #include #include "Tetromino.hpp" +#include "Sound.hpp" class GameBoard { private: @@ -27,13 +28,15 @@ private: int level; int lines; + const unique_ptr sound; + public: GameBoard( ); void update( ); bool tryMoveCurrentTetromino(int dx, int dy); - void tryRotateCurrentTetromino( ); + bool tryRotateCurrentTetromino( ); bool isValidPosition(const vector>& shape, int x, int y) const; - void moveToBottom( ); + bool moveToBottom( ); const bool isCollision( ) const; const int getScore( ) const; diff --git a/src/Sound.cpp b/src/Sound.cpp new file mode 100644 index 0000000..68980a8 --- /dev/null +++ b/src/Sound.cpp @@ -0,0 +1,109 @@ +#include "Sound.hpp" + +Sound::Sound( ) { + if (!cachedSounds) { + cachedSounds = make_unique>>( ); + + cachedSounds->emplace( + SoundName::GAME_OVER, + std::shared_ptr(Mix_LoadWAV("assets/sound_effects/game_over.wav"), [ ](Mix_Chunk* r) { Mix_FreeChunk(r); }) + ); + cachedSounds->emplace( + SoundName::LINE_CLEAR, + std::shared_ptr(Mix_LoadWAV("assets/sound_effects/line_clear.wav"), [ ](Mix_Chunk* r) { Mix_FreeChunk(r); }) + ); + cachedSounds->emplace( + SoundName::MOVE_PIECE, + std::shared_ptr(Mix_LoadWAV("assets/sound_effects/move_piece.wav"), [ ](Mix_Chunk* r) { Mix_FreeChunk(r); }) + ); + cachedSounds->emplace( + SoundName::PIECE_LANDED, + std::shared_ptr(Mix_LoadWAV("assets/sound_effects/piece_landed.wav"), [ ](Mix_Chunk* r) { Mix_FreeChunk(r); }) + ); + cachedSounds->emplace( + SoundName::ROCKET_ENDING, + std::shared_ptr(Mix_LoadWAV("assets/sound_effects/rocket_ending.wav"), [ ](Mix_Chunk* r) { Mix_FreeChunk(r); }) + ); + cachedSounds->emplace( + SoundName::TETRIS_LINE_CLEAR, + std::shared_ptr(Mix_LoadWAV("assets/sound_effects/tetris_line_clear.wav"), [ ](Mix_Chunk* r) { Mix_FreeChunk(r); }) + ); + cachedSounds->emplace( + SoundName::LEVEL_UP, + std::shared_ptr(Mix_LoadWAV("assets/sound_effects/level_up.wav"), [ ](Mix_Chunk* r) { Mix_FreeChunk(r); }) + ); + cachedSounds->emplace( + SoundName::MENU, + std::shared_ptr(Mix_LoadWAV("assets/sound_effects/menu.wav"), [ ](Mix_Chunk* r) { Mix_FreeChunk(r); }) + ); + cachedSounds->emplace( + SoundName::PIECE_FALLING_AFTER_LINE_CLEAR, + std::shared_ptr(Mix_LoadWAV("assets/sound_effects/piece_falling_after_line_clear.wav"), [ ](Mix_Chunk* r) { Mix_FreeChunk(r); }) + ); + cachedSounds->emplace( + SoundName::PLAYER_SENDING_BLOCKS, + std::shared_ptr(Mix_LoadWAV("assets/sound_effects/player_sending_blocks.wav"), [ ](Mix_Chunk* r) { Mix_FreeChunk(r); }) + ); + cachedSounds->emplace( + SoundName::ROTATE_PIECE, + std::shared_ptr(Mix_LoadWAV("assets/sound_effects/rotate_piece.wav"), [ ](Mix_Chunk* r) { Mix_FreeChunk(r); }) + ); + } + if (!cachedMusic) { + cachedMusic = make_unique>>( ); + cachedMusic->emplace( + MusicName::MAIN_THEME, + shared_ptr(Mix_LoadMUS("assets/sound_tracks/bgm.mp3"), [ ](Mix_Music* r) {Mix_FreeMusic(r);}) + ); + } +} + +bool Sound::PlaySound(SoundName soundName, int loop) { + auto it = cachedSounds->find(soundName); + if (it == cachedSounds->end( ) || !it->second) { + return false; + } + + Mix_PlayChannel(-1, it->second.get( ), loop); + + return true; +} + +bool Sound::PlayMusic(MusicName musicName, int loop) { + auto it = cachedMusic->find(musicName); + if (it == cachedMusic->end( ) || !it->second) { + return false; + } + + Mix_PlayMusic(it->second.get( ), loop); + + return true; +} + +bool Sound::PauseMusic( ) { + if (Mix_PlayingMusic( ) != 0) + Mix_PauseMusic( ); +} + +bool Sound::ResumeMusic( ) { + if (Mix_PausedMusic( ) == 0) + Mix_ResumeMusic( ); +} + +bool Sound::IncreaseVolume( ) { + int currentVolume = Mix_Volume(-1, -1); + if (currentVolume < MIX_MAX_VOLUME) { + Mix_Volume(-1, currentVolume + 2); + return true; + } + return false; +} + +bool Sound::DecreaseVolume( ) { + int currentVolume = Mix_Volume(-1, -1); + if (currentVolume > 0) { + Mix_Volume(-1, currentVolume - 2); + return true; + } + return false; +} diff --git a/src/Sound.hpp b/src/Sound.hpp new file mode 100644 index 0000000..02cb8fa --- /dev/null +++ b/src/Sound.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include + +extern "C" { +#include +} + +enum class SoundName { + GAME_OVER, + LINE_CLEAR, + MOVE_PIECE, + PIECE_LANDED, + ROCKET_ENDING, + TETRIS_LINE_CLEAR, + LEVEL_UP, + MENU, + PIECE_FALLING_AFTER_LINE_CLEAR, + PLAYER_SENDING_BLOCKS, + ROTATE_PIECE +}; + +enum class MusicName { + MAIN_THEME +}; + +using namespace std; + +class Sound { +private: + static unique_ptr >> cachedSounds; + static unique_ptr >> cachedMusic; +public: + Sound( ); + + bool PlaySound(SoundName soundName, int loop = 0); + bool PlayMusic(MusicName musicName, int loop = -1); + + bool PauseMusic( ); + bool ResumeMusic( ); + bool IncreaseVolume( ); + bool DecreaseVolume( ); + +};