diff options
Diffstat (limited to 'src/games')
| -rw-r--r-- | src/games/Game.cpp | 14 | ||||
| -rw-r--r-- | src/games/Game.hpp | 5 | ||||
| -rw-r--r-- | src/games/minesweeper/Minesweeper.cpp | 387 | ||||
| -rw-r--r-- | src/games/minesweeper/Minesweeper.hpp | 109 | ||||
| -rw-r--r-- | src/games/snake/Snake.cpp | 24 | ||||
| -rw-r--r-- | src/games/snake/Snake.hpp | 8 | ||||
| -rw-r--r-- | src/games/tetris/Tetris.cpp | 75 | ||||
| -rw-r--r-- | src/games/tetris/Tetris.hpp | 21 | 
8 files changed, 376 insertions, 267 deletions
diff --git a/src/games/Game.cpp b/src/games/Game.cpp index 628f5d4..4f7b7a6 100644 --- a/src/games/Game.cpp +++ b/src/games/Game.cpp @@ -9,23 +9,23 @@  std::unique_ptr<Game> -Game::Select(GameType type) +Game::Select(GameType type, RenderGroup& render_group)  {      switch (type) {          case NO_GAME: {              return nullptr; -        } +        } break;          case TETRIS: { -            return std::make_unique<Tetris>(); +            return std::make_unique<Tetris>(render_group);          } break;          case SNAKE: { -            return std::make_unique<Snake>(); +            return std::make_unique<Snake>(render_group);          } break;          case MINESWEEPER: { -            return std::make_unique<Minesweeper>(); +            return std::make_unique<Minesweeper>(render_group);          } break;          InvalidDefaultCase; @@ -35,5 +35,7 @@ Game::Select(GameType type)  } -Game::~Game() {} +Game::~Game() +{ +} diff --git a/src/games/Game.hpp b/src/games/Game.hpp index 9af98b4..b09b618 100644 --- a/src/games/Game.hpp +++ b/src/games/Game.hpp @@ -17,10 +17,11 @@ public:          MINESWEEPER      }; +    static std::unique_ptr<Game> Select(GameType type, RenderGroup& render_group); +      Game() = default;      virtual ~Game(); -    static std::unique_ptr<Game> Select(GameType type); -    virtual bool Update(std::vector<SDL_Event> &events, RenderGroup &render_group) = 0; +    virtual bool Update(std::vector<SDL_Event> &events) = 0;  }; diff --git a/src/games/minesweeper/Minesweeper.cpp b/src/games/minesweeper/Minesweeper.cpp index abb9515..8f20268 100644 --- a/src/games/minesweeper/Minesweeper.cpp +++ b/src/games/minesweeper/Minesweeper.cpp @@ -3,72 +3,94 @@  #include <algorithm>  #include <random> +#include <cstdio> -// Todo: draw a difficulty selection menu at top-mid. +// Todo: winning condition (maybe: total_cells - uncovered_cells = mine_count) -// Note: How many mines? -// - Beginner: 8x8 or 9x9 grid with 10 mines. -// - Intermediate: 16x16 grid with 40 mines. -// - Expert: 30x16 grid with 99 mines. +Minesweeper::Minesweeper(RenderGroup& render_group) +    : m_render_group {render_group} +{ +    m_font.Init("fonts/dejavu_ttf/DejaVuSansMono.ttf", 16); +    for (uint32_t i = 0; i < m_digit_glyphs.size(); ++i) { +        m_font.LoadGlyph(m_digit_glyphs[i], i + '0'); +    } +} -Minesweeper::Minesweeper() +void +Minesweeper::Reset(Difficulty difficulty)  { -    float map_width = static_cast<float>(m_MapWidth); -    float map_height = static_cast<float>(m_MapHeight); -    float cell_size = 0.8f * std::min(m_WorldHeight / MAX_MAP_HEIGHT, m_WorldWidth / MAX_MAP_WIDTH); +    int32_t mine_count; +    if (difficulty == beginner) { +        m_grid_width = 8; +        m_grid_height = 8; +        mine_count = 10; +    } +    else if(difficulty == intermediate) { +        m_grid_width = 16; +        m_grid_height = 16; +        mine_count = 40; +    } +    else { +        m_grid_width = 30; +        m_grid_height = 16; +        mine_count = 99; +    } + + +    float cell_size = 0.8f * std::min(m_world_height / MAX_MAP_HEIGHT, m_world_width / MAX_MAP_WIDTH);      float cell_size_without_border = 0.8f * cell_size; -    m_MapViewPos = { -        0.1f * cell_size_without_border + (m_WorldWidth - cell_size * map_width) / 2, -        0.1f * cell_size_without_border + (m_WorldHeight - cell_size * map_height) / 2 +    V2F32 grid_size = { +        (float)m_grid_width * cell_size, +        (float)m_grid_height * cell_size +    }; +    m_grid_pos = { +        (m_world_width - grid_size.x) / 2, +        (m_world_height - grid_size.y) / 2,      }; -    m_CellOuterViewSize = {cell_size, cell_size}; -    m_CellInnerViewSize = {cell_size_without_border, cell_size_without_border}; -    m_Font.Init("fonts/dejavu_ttf/DejaVuSansMono.ttf", 32); -    for (uint32_t i = 0; i < m_DigitGlyphs.size(); ++i) { -        m_Font.LoadGlyph(m_DigitGlyphs[i], i + '0'); -    } +    m_cell_outer_size = {cell_size, cell_size}; +    m_cell_inner_size = {cell_size_without_border, cell_size_without_border}; -    Reinit(); -} - -void Minesweeper::Reinit() { -    int32_t mine_count = 40; -    memset(m_IsCoveredBitmap, 0xff, sizeof(m_IsCoveredBitmap)); -    memset(m_IsFlaggedBitmap, 0 , sizeof(m_IsFlaggedBitmap)); +    memset(m_is_covered_bitmap, 0xff, sizeof(m_is_covered_bitmap)); +    memset(m_is_flagged_bitmap, 0 , sizeof(m_is_flagged_bitmap));      InitIsMineBitmap(mine_count);      InitAdjacentMineCounters();  } -void Minesweeper::InitIsMineBitmap(int32_t mine_count) { -    assert(mine_count < m_MapWidth * m_MapHeight); +void +Minesweeper::InitIsMineBitmap(int32_t mine_count) +{ +    assert(mine_count < m_grid_width * m_grid_height); -    memset(m_IsMineBitmap, 0 , sizeof(m_IsMineBitmap)); +    memset(m_is_mine_bitmap, 0 , sizeof(m_is_mine_bitmap));      std::mt19937 rng((std::random_device()())); -    std::uniform_int_distribution<int32_t> dist(0, m_MapWidth * m_MapHeight - 1); +    std::uniform_int_distribution<int32_t> dist(0, m_grid_width * m_grid_height - 1); +      while (mine_count) {          int32_t random_pos = dist(rng); -        int32_t x = random_pos / m_MapWidth; -        int32_t y = random_pos % m_MapWidth; +        int32_t y = random_pos / m_grid_width; +        int32_t x = random_pos % m_grid_width;          if (!IsMine(x, y)) { -            m_IsMineBitmap[y] |= 1 << x; +            m_is_mine_bitmap[y] |= 1 << x;              mine_count--;          }      }  } -void Minesweeper::InitAdjacentMineCounters() { -    for (int32_t y = 0; y < m_MapHeight; y++) { +void +Minesweeper::InitAdjacentMineCounters() +{ +    for (int32_t y = 0; y < m_grid_height; y++) {          int32_t y0 = y > 0 ? y-1 : y; -        int32_t y1 = y < m_MapHeight-1 ? y+1 : y; +        int32_t y1 = y < m_grid_height-1 ? y+1 : y; -        for (int32_t x = 0; x < m_MapHeight; x++) { +        for (int32_t x = 0; x < m_grid_width; x++) {              int32_t x0 = x > 0 ? x-1 : x; -            int32_t x1 = x < m_MapWidth-1 ? x+1 : x; +            int32_t x1 = x < m_grid_width-1 ? x+1 : x;              int32_t adjacent_mine_counter = 0;              for (int32_t inner_y = y0; inner_y <= y1; inner_y++) { @@ -82,71 +104,78 @@ void Minesweeper::InitAdjacentMineCounters() {                  adjacent_mine_counter = -1;              } -            m_AdjacentMineCounters[y * m_MapWidth + x] = adjacent_mine_counter; +            m_adjacent_mine_counts[y * m_grid_width + x] = adjacent_mine_counter;          }      }  } -bool Minesweeper::Update(std::vector<SDL_Event> &events, RenderGroup &render_group) { +bool +Minesweeper::Update(std::vector<SDL_Event>& events) +{      Color clear_color = {0.3f, 0.2f, 0.3f}; -    render_group.SetCameraSize(4.0f, 3.0f); -    render_group.Clear(clear_color); - -    if (m_RunState == MinesweeperRunState::Restart) { -        Reinit(); -        m_RunState = MinesweeperRunState::Resume; -    } +    m_render_group.SetCameraSize(4.0f, 3.0f); +    m_render_group.Clear(clear_color);      for (SDL_Event &event : events) { -        if (m_RunState == MinesweeperRunState::Exit) { +        if (m_run_state == exit) {              return false;          } -        else if (m_RunState == MinesweeperRunState::Pause) { -            ProcessEventDuringPause(event, render_group); +        else if (m_run_state == pause) { +            ProcessEventDuringPause(event);          } -        else if (m_RunState == MinesweeperRunState::Resume) { -            ProcessEventDuringResume(event, render_group); +        else if (m_run_state == resume) { +            ProcessEventDuringResume(event);          }      } -    if (m_RunState == MinesweeperRunState::Pause) { -        DrawPauseMenu(render_group); +    if (m_run_state == pause) { +        DrawBoard(); +        DrawPauseMenu();      } -    else if (m_RunState == MinesweeperRunState::GameOver) { -        DrawGameOverMenu(render_group); +    else if (m_run_state == start_menu) { +        DrawStartMenu(); +    } +    else if (m_run_state == resume) { +        DrawBoard(); +    } +    else if (m_run_state == game_over) { +        DrawBoard(); +        DrawGameOverMenu();      } -    DrawBoard(render_group); - -    bool keep_running = m_RunState != MinesweeperRunState::Exit; +    bool keep_running = m_run_state != exit;      return keep_running;  } -void Minesweeper::ProcessEventDuringPause(SDL_Event &event, RenderGroup &render_group) { +void +Minesweeper::ProcessEventDuringPause(SDL_Event &event) +{      switch (event.type) {      case SDL_EVENT_KEY_DOWN: {          if (event.key.key == SDLK_ESCAPE) { -            m_RunState = MinesweeperRunState::Resume; +            m_run_state = resume;          }      } break;      default:;      }  } -void Minesweeper::ProcessEventDuringResume(SDL_Event &event, RenderGroup &render_group) { +void +Minesweeper::ProcessEventDuringResume(SDL_Event &event) +{      switch (event.type) {      case SDL_EVENT_KEY_DOWN: {          if (event.key.key == SDLK_ESCAPE) { -            m_RunState = MinesweeperRunState::Pause; +            m_run_state = pause;          }      } break;          case SDL_EVENT_MOUSE_BUTTON_DOWN: { -            V2F32 click_screen_pos = {event.button.x, (float)render_group.m_ScreenHeight -1 - event.button.y}; -            V2F32 click_view_pos = ScreenPosToViewPos(click_screen_pos, render_group); +            V2F32 click_screen_pos = {event.button.x, (float)m_render_group.m_ScreenHeight -1 - event.button.y}; +            V2F32 click_view_pos = ScreenPosToViewPos(click_screen_pos, m_render_group); -        float x_adjusted = click_view_pos.x - m_MapViewPos.x; -        float y_adjusted = click_view_pos.y - m_MapViewPos.y; +        float x_adjusted = click_view_pos.x - m_grid_pos.x; +        float y_adjusted = click_view_pos.y - m_grid_pos.y;          if (x_adjusted < 0.0f) {              break;          } @@ -154,20 +183,21 @@ void Minesweeper::ProcessEventDuringResume(SDL_Event &event, RenderGroup &render              break;          } -        int32_t x = (int32_t)(x_adjusted / m_CellOuterViewSize.x); -        int32_t y = (int32_t)(y_adjusted / m_CellOuterViewSize.y); -        if (x >= m_MapWidth) { +        int32_t x = (int32_t)(x_adjusted / m_cell_outer_size.x); +        int32_t y = (int32_t)(y_adjusted / m_cell_outer_size.y); +        if (x >= m_grid_width) {              break;          } -        if (y >= m_MapHeight) { +        if (y >= m_grid_height) {              break;          }          if (event.button.button == 1) {              if (IsCovered(x, y)) {                  if (IsMine(x, y)) { -                    m_IsCoveredBitmap[y] &= ~(1 << x); -                    m_RunState = MinesweeperRunState::GameOver; +                    m_is_covered_bitmap[y] &= ~(1 << x); +                    UncoverMines(); +                    m_run_state = game_over;                  }                  else {                      Uncover(x, y); @@ -186,20 +216,21 @@ void Minesweeper::ProcessEventDuringResume(SDL_Event &event, RenderGroup &render      }  } -// Todo: maybe find a more efficient non-naive solution -void Minesweeper::Uncover(int32_t x, int32_t y) { +void +Minesweeper::Uncover(int32_t x, int32_t y) +{      if (x < 0)            return; -    if (x >= m_MapWidth)  return; +    if (x >= m_grid_width)  return;      if (y < 0)            return; -    if (y >= m_MapHeight) return; +    if (y >= m_grid_height) return;      if (!IsCovered(x, y)) return; -    m_IsCoveredBitmap[y] &= ~(1 << x); +    m_is_covered_bitmap[y] &= ~(1 << x);      if (IsFlagged(x, y)) {          ToggleFlag(x, y);      } -    if (m_AdjacentMineCounters[y*m_MapWidth + x] > 0) { +    if (m_adjacent_mine_counts[y*m_grid_width + x] > 0) {          return;      }      Uncover(x-1, y-1); @@ -214,137 +245,191 @@ void Minesweeper::Uncover(int32_t x, int32_t y) {      Uncover(x+1, y+1);  } -void Minesweeper::ToggleFlag(int32_t x, int32_t y) { -    m_IsFlaggedBitmap[y] ^= (1 << x); +void +Minesweeper::UncoverMines() +{ +    for (int32_t y{0}; y < m_grid_height; ++y) { +        for (int32_t x{0}; x < m_grid_width; ++x) { +            if (IsMine(x, y) && IsCovered(x, y))  { +                m_is_covered_bitmap[y] &= ~(1 << x); +            } +        } +    } +} + +void +Minesweeper::ToggleFlag(int32_t x, int32_t y) +{ +    m_is_flagged_bitmap[y] ^= (1 << x);  } -bool Minesweeper::IsCovered(int32_t x, int32_t y) { -    bool is_covered = m_IsCoveredBitmap[y] & 1 << x; +bool +Minesweeper::IsCovered(int32_t x, int32_t y) +{ +    bool is_covered = m_is_covered_bitmap[y] & 1 << x;      return is_covered;  } -bool Minesweeper::IsFlagged(int32_t x, int32_t y) { -    bool is_flagged = m_IsFlaggedBitmap[y] & 1 << x; +bool +Minesweeper::IsFlagged(int32_t x, int32_t y) +{ +    bool is_flagged = m_is_flagged_bitmap[y] & 1 << x;      return is_flagged;  } -bool Minesweeper::IsMine(int32_t x, int32_t y) { -    bool is_mine = m_IsMineBitmap[y] & 1 << x; +bool +Minesweeper::IsMine(int32_t x, int32_t y) +{ +    bool is_mine = m_is_mine_bitmap[y] & 1 << x;      return is_mine;  } -V2F32 Minesweeper::ScreenPosToViewPos(V2F32 screen_pos, RenderGroup &render_group) { +V2F32 +Minesweeper::ScreenPosToViewPos(V2F32 screen_pos, RenderGroup &render_group) +{      // e.g. [0, 1024] -> [0, 1] -> [0, 4]      // e.g. [0,  768] -> [0, 1] -> [0, 3]      float screen_width = (float)render_group.m_ScreenWidth;      float screen_height = (float)render_group.m_ScreenHeight;      V2F32 view_pos; -    view_pos.x = (screen_pos.x / screen_width) * m_WorldWidth; -    view_pos.y = (screen_pos.y / screen_height) * m_WorldHeight; +    view_pos.x = (screen_pos.x / screen_width) * m_world_width; +    view_pos.y = (screen_pos.y / screen_height) * m_world_height;      return view_pos;  } -void Minesweeper::DrawPauseMenu(RenderGroup &render_group) { +void +Minesweeper::DrawPauseMenu() +{      ImGui::Begin("MinesweeperPause");      if (ImGui::Button("Resume")) { -        m_RunState = MinesweeperRunState::Resume; +        m_run_state = resume; +    } +    if (ImGui::Button("Restart")) { +        m_run_state = start_menu;      }      if (ImGui::Button("Exit")) { -        m_RunState = MinesweeperRunState::Exit; +        m_run_state = exit;      }      ImGui::End();  } -void Minesweeper::DrawGameOverMenu(RenderGroup &render_group) { -    ImGui::Begin("MinesweeperGameOver"); -    ImGui::Text("Score = ???"); -    if (ImGui::Button("Restart")) { -        m_RunState = MinesweeperRunState::Restart; +void +Minesweeper::DrawStartMenu() +{ +    ImGui::Begin("MinesweeperStartMenu"); +    if (ImGui::RadioButton("beginner", m_difficulty == beginner ? true : false)) { +        m_difficulty = beginner; +    } +    if (ImGui::RadioButton("intermediate", m_difficulty == intermediate ? true : false)) { +        m_difficulty = intermediate; +    } +    if (ImGui::RadioButton("expert", m_difficulty == expert ? true : false)) { +        m_difficulty = expert; +    } +    if (ImGui::Button("Start")) { +        Reset(m_difficulty); +        m_run_state = resume;      }      if (ImGui::Button("Exit")) { -        m_RunState = MinesweeperRunState::Exit; +        m_run_state = exit;      }      ImGui::End();  } -void Minesweeper::DrawBoard(RenderGroup &render_group) { -    Color covered_cell_color = {0.4f, 0.4f, 0.4f}; -    Color uncovered_cell_color = {0.2f, 0.2f, 0.2f}; -    Color flag_color = {0.6f, 0.3f, 03.f}; -    Color mine_color = {0.8f, 0.2f, 0.2f}; +void +Minesweeper::DrawGameOverMenu() +{ +    ImGui::Begin("MinesweeperGameOverMenu"); +    if (ImGui::Button("Play Again")) { +        m_run_state = start_menu; +    } +    if (ImGui::Button("Exit")) { +        m_run_state = exit; +    } +    ImGui::End(); +} -    V2F32 flag_draw_size = {m_CellInnerViewSize.x * 0.5f, m_CellInnerViewSize.y * 0.5f}; -    V2F32 flag_draw_offset = { -        (m_CellInnerViewSize.x - flag_draw_size.x) / 2, -        (m_CellInnerViewSize.y - flag_draw_size.y) / 2 +void +Minesweeper::DrawBoard() +{ +    Color covered_cell_color {0.6f, 0.6f, 0.6f}; +    Color uncovered_cell_color {0.4f, 0.4f, 0.4f}; +    Color mine_color {0.8f, 0.2f, 0.2f}; + +    Color flag_color {0.6f, 0.3f, 03.f}; +    V2F32 flag_size = {m_cell_inner_size.x * 0.5f, m_cell_inner_size.y * 0.5f}; +    V2F32 flag_offset = { +        (m_cell_inner_size.x - flag_size.x) / 2, +        (m_cell_inner_size.y - flag_size.y) / 2      }; -    // Temporary: Drawing Glyph Test -    render_group.PushBitmap( -            {100.0f, 100.0f, 10.0f}, -            m_DigitGlyphs[1].bitmap.width, -            m_DigitGlyphs[1].bitmap.height, -            m_DigitGlyphs[1].bitmap.pixels.get()); - - -    for (int32_t y = 0; y < m_MapHeight; y++) { -        for (int32_t x = 0; x < m_MapWidth; x++) { -            V2F32 world_pos = { -                m_MapViewPos.x + (float)x * m_CellOuterViewSize.x, -                m_MapViewPos.y + (float)y * m_CellOuterViewSize.y, +    for (int32_t y = 0; y < m_grid_height; y++) { +        for (int32_t x = 0; x < m_grid_width; x++) { +            V2F32 cell_pos = { +                m_grid_pos.x + (float)x * m_cell_outer_size.x, +                m_grid_pos.y + (float)y * m_cell_outer_size.y, +            }; +            RectF32 cell_rect = { +                cell_pos.x, cell_pos.y, +                cell_pos.x + m_cell_inner_size.x, cell_pos.y + m_cell_inner_size.y              }; +              bool is_covered = IsCovered(x, y);              bool is_flagged = IsFlagged(x, y);              bool is_mine = IsMine(x, y);              if (is_covered) { -                RectF32 cell_world_rect = { -                    world_pos.x, world_pos.y, -                    world_pos.x + m_CellInnerViewSize.x, world_pos.y + m_CellInnerViewSize.y -                }; -                render_group.PushRectangle(cell_world_rect, 0.0f, covered_cell_color); +                m_render_group.PushRectangle(cell_rect, 0.0f, covered_cell_color);                  if (is_flagged) { -                    V3F32 flag_world_pos = { -                        world_pos.x + flag_draw_offset.x, -                        world_pos.y + flag_draw_offset.y, +                    V3F32 flag_pos = { +                        cell_pos.x + flag_offset.x, +                        cell_pos.y + flag_offset.y,                          1.0f                      }; -                    RectF32 flag_world_rect = { -                        flag_world_pos.x, -                        flag_world_pos.y, -                        flag_world_pos.x + m_CellInnerViewSize.x, -                        flag_world_pos.y + m_CellInnerViewSize.y, +                    RectF32 flag_rect = { +                        flag_pos.x, +                        flag_pos.y, +                        flag_pos.x + flag_size.x, +                        flag_pos.y + flag_size.y,                      }; -                    render_group.PushRectangle(flag_world_rect, flag_world_pos.z, flag_color); +                    m_render_group.PushRectangle(flag_rect, flag_pos.z, flag_color);                  }              }              else { -                V3F32 mine_world_pos = { -                    world_pos.x, -                    world_pos.y, -                    1.0f -                };                  if (is_mine) { -                    RectF32 mine_world_rect = { -                        mine_world_pos.x, -                        mine_world_pos.y, -                        mine_world_pos.x + m_CellInnerViewSize.x, -                        mine_world_pos.y + m_CellInnerViewSize.y, +                    V3F32 mine_pos = { +                        cell_pos.x, +                        cell_pos.y, +                        1.0f +                    }; +                    RectF32 mine_rect = { +                        mine_pos.x, +                        mine_pos.y, +                        mine_pos.x + m_cell_inner_size.x, +                        mine_pos.y + m_cell_inner_size.y,                      }; -                    render_group.PushRectangle(mine_world_rect, mine_world_pos.z, mine_color); +                    m_render_group.PushRectangle(mine_rect, mine_pos.z, mine_color);                  }                  else { -                    RectF32 mine_world_rect = { -                        mine_world_pos.x, -                        mine_world_pos.y, -                        mine_world_pos.x + m_CellInnerViewSize.x, -                        mine_world_pos.y + m_CellInnerViewSize.y, +                    m_render_group.PushRectangle(cell_rect, 0.0f, uncovered_cell_color); + +                    // Todo: Figure out how to scale this properly. +                    //       256.0f is a random number that just works for now. +                    V3F32 mine_count_pos = { +                        256.0f * cell_pos.x, +                        256.0f * cell_pos.y, +                        256.0f                      }; -                    render_group.PushRectangle(mine_world_rect, mine_world_pos.z, uncovered_cell_color); +                    size_t mine_count_val = (size_t)m_adjacent_mine_counts[y*m_grid_width + x]; +                    m_render_group.PushBitmap( +                            mine_count_pos, +                            m_digit_glyphs[mine_count_val].bitmap.width, +                            m_digit_glyphs[mine_count_val].bitmap.height, +                            m_digit_glyphs[mine_count_val].bitmap.pixels.get());                  }              }          } diff --git a/src/games/minesweeper/Minesweeper.hpp b/src/games/minesweeper/Minesweeper.hpp index b081806..b89743e 100644 --- a/src/games/minesweeper/Minesweeper.hpp +++ b/src/games/minesweeper/Minesweeper.hpp @@ -1,5 +1,7 @@  #pragma once +#include "imgui.h" +#include "renderer/RenderGroup.hpp"  #include <games/Game.hpp>  #include <common/Font.hpp> @@ -18,73 +20,86 @@ namespace std {  } -enum class MinesweeperRunState { -    Resume, -    Pause, -    GameOver, -    Restart, -    Exit -}; -  class Minesweeper : public Game { -    public: -        Minesweeper(); -        ~Minesweeper() = default; +public: +    enum RunState { +        start_menu, +        pause, +        resume, +        game_over, +        exit +    }; + +    enum Difficulty { +        beginner, +        intermediate, +        expert +    }; + +public: +    Minesweeper(RenderGroup& render_group); +    ~Minesweeper() = default; + +    bool Update(std::vector<SDL_Event>& events) override; + +    void ProcessEventDuringPause(SDL_Event& event); +    void ProcessEventDuringResume(SDL_Event& event); -        bool Update(std::vector<SDL_Event> &events, RenderGroup &render_group) override; -        void ProcessEventDuringPause(SDL_Event &event, RenderGroup &render_group); -        void ProcessEventDuringResume(SDL_Event &event, RenderGroup &render_group); +private: +    void Reset(Difficulty Difficulty); +    void InitIsMineBitmap(int32_t mine_count); +    void InitAdjacentMineCounters(); +    void UncoverMines(); +    void Uncover(int32_t x, int32_t y); +    void ToggleFlag(int32_t x, int32_t y); -    private: -        void Reinit(); -        void InitIsMineBitmap(int32_t mine_count); -        void InitAdjacentMineCounters(); +    bool IsCovered(int32_t x, int32_t y); +    bool IsFlagged(int32_t x, int32_t y); +    bool IsMine(int32_t x, int32_t y); -        void Uncover(int32_t x, int32_t y); -        void ToggleFlag(int32_t x, int32_t y); -        bool IsCovered(int32_t x, int32_t y); -        bool IsFlagged(int32_t x, int32_t y); -        bool IsMine(int32_t x, int32_t y); +    V2F32 ScreenPosToViewPos(V2F32 screen_pos, RenderGroup& render_group); -        V2F32 ScreenPosToViewPos(V2F32 screen_pos, RenderGroup &render_group); +private: +    void DrawBoard(); +    void DrawStartMenu(); +    void DrawPauseMenu(); +    void DrawGameOverMenu(); -    private: -        void DrawPauseMenu(RenderGroup &render_group); -        void DrawGameOverMenu(RenderGroup &render_group); -        void DrawBoard(RenderGroup &render_group); +private: +    static constexpr int32_t MAX_MAP_HEIGHT = 32; +    static constexpr int32_t MAX_MAP_WIDTH = 32; +    static constexpr std::string_view s_FontFilepath = "./fonts/dejavu_ttf/DejaVuSans.ttf"; -    private: -        static constexpr int32_t MAX_MAP_HEIGHT = 32; -        static constexpr int32_t MAX_MAP_WIDTH = 32; -        static constexpr std::string_view s_FontFilepath = "./fonts/dejavu_ttf/DejaVuSans.ttf"; +private: +    RenderGroup& m_render_group; -    private: -        MinesweeperRunState m_RunState = MinesweeperRunState::Resume; +    RunState m_run_state = start_menu; +    Difficulty m_difficulty = beginner; -        float m_WorldWidth = 4.0f; -        float m_WorldHeight = 3.0f; +    float m_world_width = 4.0f; +    float m_world_height = 3.0f; -        int32_t m_MapWidth = 16; -        int32_t m_MapHeight = 16; +    int32_t m_grid_width; +    int32_t m_grid_height; -        V2F32 m_MapViewPos; -        V2F32 m_CellOuterViewSize; -        V2F32 m_CellInnerViewSize; +    V2F32 m_grid_pos; +    V2F32 m_cell_outer_size; +    V2F32 m_cell_inner_size; -        uint32_t m_IsCoveredBitmap[MAX_MAP_HEIGHT] {}; -        uint32_t m_IsFlaggedBitmap[MAX_MAP_HEIGHT] {}; -        uint32_t m_IsMineBitmap[MAX_MAP_HEIGHT] {}; -        int32_t m_AdjacentMineCounters[MAX_MAP_WIDTH * MAX_MAP_HEIGHT] {}; +    uint32_t m_is_covered_bitmap[MAX_MAP_HEIGHT] {}; +    uint32_t m_is_flagged_bitmap[MAX_MAP_HEIGHT] {}; +    uint32_t m_is_mine_bitmap[MAX_MAP_HEIGHT] {}; +    int32_t m_adjacent_mine_counts[MAX_MAP_WIDTH * MAX_MAP_HEIGHT] {}; -        Font m_Font; -        std::array<Glyph, 9> m_DigitGlyphs; +    Font m_font; +    std::array<Glyph, 9> m_digit_glyphs;  }; diff --git a/src/games/snake/Snake.cpp b/src/games/snake/Snake.cpp index 6a95521..aa8cedf 100644 --- a/src/games/snake/Snake.cpp +++ b/src/games/snake/Snake.cpp @@ -2,7 +2,9 @@  #include <imgui.h> -Snake::Snake () { +Snake::Snake(RenderGroup& render_group) +    : m_RenderGroup{render_group} +{      m_IsPaused = false;      m_IsRunning = true; @@ -33,9 +35,9 @@ Snake::Snake () {      m_Dist = std::uniform_int_distribution<int32_t>(0, m_MapWidth * m_MapHeight - 3);      SpawnFood(); -    } +} -bool Snake::Update(std::vector<SDL_Event> &events, RenderGroup &render_group) { +bool Snake::Update(std::vector<SDL_Event> &events) {      uint64_t milliseconds_since_t0 = SDL_GetTicks();      uint64_t milliseconds_since_t0_last = m_LastMillisecondsSinceT0;      uint64_t dt_in_milliseconds = milliseconds_since_t0 - milliseconds_since_t0_last; @@ -44,8 +46,8 @@ bool Snake::Update(std::vector<SDL_Event> &events, RenderGroup &render_group) {      Color clear_color = {0.3f, 0.3f, 0.3f, 1.0f}; -    render_group.SetCameraSize(4.0f, 3.0f); -    render_group.Clear(clear_color); +    m_RenderGroup.SetCameraSize(4.0f, 3.0f); +    m_RenderGroup.Clear(clear_color);      for (SDL_Event &event : events) { @@ -66,7 +68,7 @@ bool Snake::Update(std::vector<SDL_Event> &events, RenderGroup &render_group) {      } -    Draw(render_group); +    Draw();      DoImgui();      return m_IsRunning; @@ -249,7 +251,7 @@ void Snake::SpawnFood() {      m_FoodPosition = {bit0_x, bit0_y};  } -void Snake::Draw(RenderGroup &render_group) { +void Snake::Draw() {      float world_width = 4.0f;      float world_height = 3.0f; @@ -275,7 +277,7 @@ void Snake::Draw(RenderGroup &render_group) {          map_world_pos.y + map_world_dim.y      };      Color bg_color = {0.0f, 0.0f, 0.0f, 1.0f}; -    render_group.PushRectangle(map_world_rect, map_world_pos.z, bg_color); +    m_RenderGroup.PushRectangle(map_world_rect, map_world_pos.z, bg_color);      /* draw snake */ @@ -304,7 +306,7 @@ void Snake::Draw(RenderGroup &render_group) {              };              Color color = {0.3f, 0.3f, 0.3f, 1.0f}; -            render_group.PushRectangle(world_rect, world_pos.z, color); +            m_RenderGroup.PushRectangle(world_rect, world_pos.z, color);              tail++;          }          tail = 0; @@ -332,7 +334,7 @@ void Snake::Draw(RenderGroup &render_group) {          };          Color color = {0.3f, 0.3f, 0.3f, 1.0f}; -        render_group.PushRectangle(world_rect, world_pos.z, color); +        m_RenderGroup.PushRectangle(world_rect, world_pos.z, color);          tail++;      } @@ -351,7 +353,7 @@ void Snake::Draw(RenderGroup &render_group) {          pos.y + dim.y      };      Color color = {0.3f, 0.6f, 0.4f, 1.0f}; -    render_group.PushRectangle(rect, pos.z, color); +    m_RenderGroup.PushRectangle(rect, pos.z, color);  }  void Snake::DoImgui() { diff --git a/src/games/snake/Snake.hpp b/src/games/snake/Snake.hpp index f04ad16..ef1fd34 100644 --- a/src/games/snake/Snake.hpp +++ b/src/games/snake/Snake.hpp @@ -17,8 +17,8 @@ public:  public: -    Snake(); -    bool Update(std::vector<SDL_Event> &events, RenderGroup &render_group) override; +    Snake(RenderGroup& render_group); +    bool Update(std::vector<SDL_Event> &events) override;  private: @@ -28,7 +28,7 @@ private:      void MaybeMoveSnake(float dt_in_seconds);      void SpawnFood(); -    void Draw(RenderGroup &render_group); +    void Draw();      void DoImgui(); @@ -37,6 +37,8 @@ private:      static constexpr int32_t MAX_MAP_WIDTH = 16;      static constexpr int32_t MAX_MAP_HEIGHT = 16; +    RenderGroup& m_RenderGroup; +      bool m_IsPaused;      bool m_IsRunning; diff --git a/src/games/tetris/Tetris.cpp b/src/games/tetris/Tetris.cpp index f2a7384..8f07df0 100644 --- a/src/games/tetris/Tetris.cpp +++ b/src/games/tetris/Tetris.cpp @@ -9,7 +9,8 @@  // Todo: change to new font scaling api in imgui first  // Todo: test text with hardcoded gap + dummy to ensure it gets placed as expected -Tetris::Tetris() : +Tetris::Tetris(RenderGroup& m_RenderGroup) : +    m_RenderGroup(m_RenderGroup),      m_ActiveTetromino(m_Board),      m_NextTetromino(m_Board)  { @@ -34,10 +35,10 @@ void Tetris::Restart() {      m_SoftdropCounter = 0;  } -bool Tetris::Update(std::vector<SDL_Event> &events, RenderGroup &render_group) { +bool Tetris::Update(std::vector<SDL_Event> &events) {      Color clear_color = {0.2f, 0.2f, 0.2f, 1.0f}; -    render_group.SetCameraSize(4.0f, 3.0f); -    render_group.Clear(clear_color); +    m_RenderGroup.SetCameraSize(4.0f, 3.0f); +    m_RenderGroup.Clear(clear_color);      if (m_RunningState == TetrisRunningState::Restart) {          Restart(); @@ -71,7 +72,7 @@ bool Tetris::Update(std::vector<SDL_Event> &events, RenderGroup &render_group) {          }      } -    Draw(render_group); +    Draw();      bool keep_running = m_RunningState != TetrisRunningState::Exit; @@ -210,26 +211,26 @@ void Tetris::HandleGameOver() {      }  } -void Tetris::Draw(RenderGroup &render_group) { -    m_Board.Draw(m_Level, render_group); -    m_ActiveTetromino.Draw(render_group); +void Tetris::Draw() { +    m_Board.Draw(m_Level, m_RenderGroup); +    m_ActiveTetromino.Draw(m_RenderGroup); -    DrawNextTetromino(render_group); -    DrawStatistics(render_group); -    DrawLineCounter(render_group); -    DrawLevel(render_group); -    DrawScore(render_group); +    DrawNextTetromino(); +    DrawStatistics(); +    DrawLineCounter(); +    DrawLevel(); +    DrawScore();      // Todo: Use transparency      if (m_RunningState  == TetrisRunningState::Pause) { -        DrawPauseMenu(render_group); +        DrawPauseMenu();      }      else if (m_RunningState == TetrisRunningState::GameOver) { -        DrawGameOverMenu(render_group); +        DrawGameOverMenu();      }  } -void Tetris::DrawPauseMenu(RenderGroup &render_group) { +void Tetris::DrawPauseMenu() {      ImGui::Begin("TetrisPause", nullptr, s_MenuImGuiWindowFlags);      if (ImGui::Button("Resume")) {          m_RunningState = TetrisRunningState::Resume; @@ -243,7 +244,7 @@ void Tetris::DrawPauseMenu(RenderGroup &render_group) {      ImGui::End();  } -void Tetris::DrawGameOverMenu(RenderGroup &render_group) { +void Tetris::DrawGameOverMenu() {      ImGui::Begin("TetrisGameOver", nullptr, s_MenuImGuiWindowFlags);      ImGui::Text("Score = %d", m_Score);      ImGui::Text("HighScore = %d", m_HighScore); @@ -256,9 +257,9 @@ void Tetris::DrawGameOverMenu(RenderGroup &render_group) {      ImGui::End();  } -void Tetris::DrawLineCounter(RenderGroup &render_group) { +void Tetris::DrawLineCounter() {      V2F32 view_pos = {0.5f, 2.6f}; -    ImVec2 screen_pos = render_group.ViewPosToScreenPosImGui(view_pos); +    ImVec2 screen_pos = m_RenderGroup.ViewPosToScreenPosImGui(view_pos);      ImGui::SetNextWindowPos(screen_pos);      ImGui::Begin("TetrisLines", nullptr, s_DefaultImGuiWindowFlags); @@ -266,38 +267,38 @@ void Tetris::DrawLineCounter(RenderGroup &render_group) {      ImGui::End();  } -void Tetris::DrawStatistics(RenderGroup &render_group) { +void Tetris::DrawStatistics() {      V2F32 view_tetrominoes_pos = {0.4f, 1.8f};      V2F32 view_advance = {0.0f, 0.2f};      V2F32 view_text_title_pos = view_tetrominoes_pos + V2F32(0.02f, 0.4f); -    ImVec2 screen_text_title_pos = render_group.ViewPosToScreenPosImGui(view_text_title_pos); +    ImVec2 screen_text_title_pos = m_RenderGroup.ViewPosToScreenPosImGui(view_text_title_pos);      V2F32 view_text_pos = view_tetrominoes_pos + V2F32(0.4f, 0.16f);      V2F32 view_text_gap = {0.0f, 0.124f}; -    ImVec2 screen_text_pos = render_group.ViewPosToScreenPosImGui(view_text_pos); -    ImVec2 screen_text_gap = render_group.ViewSizeToScreenSizeImGui(view_text_gap); +    ImVec2 screen_text_pos = m_RenderGroup.ViewPosToScreenPosImGui(view_text_pos); +    ImVec2 screen_text_gap = m_RenderGroup.ViewSizeToScreenSizeImGui(view_text_gap); -    Tetromino::Draw(Tetromino::t_piece, 0, view_tetrominoes_pos, 0.5f, render_group); +    Tetromino::Draw(Tetromino::t_piece, 0, view_tetrominoes_pos, 0.5f, m_RenderGroup);      view_tetrominoes_pos.y -= view_advance.y; -    Tetromino::Draw(Tetromino::j_piece, 0, view_tetrominoes_pos, 0.5f, render_group); +    Tetromino::Draw(Tetromino::j_piece, 0, view_tetrominoes_pos, 0.5f, m_RenderGroup);      view_tetrominoes_pos.y -= view_advance.y; -    Tetromino::Draw(Tetromino::z_piece, 0, view_tetrominoes_pos, 0.5f, render_group); +    Tetromino::Draw(Tetromino::z_piece, 0, view_tetrominoes_pos, 0.5f, m_RenderGroup);      view_tetrominoes_pos.y -= view_advance.y; -    Tetromino::Draw(Tetromino::o_piece, 0, view_tetrominoes_pos, 0.5f, render_group); +    Tetromino::Draw(Tetromino::o_piece, 0, view_tetrominoes_pos, 0.5f, m_RenderGroup);      view_tetrominoes_pos.y -= view_advance.y; -    Tetromino::Draw(Tetromino::s_piece, 0, view_tetrominoes_pos, 0.5f, render_group); +    Tetromino::Draw(Tetromino::s_piece, 0, view_tetrominoes_pos, 0.5f, m_RenderGroup);      view_tetrominoes_pos.y -= view_advance.y; -    Tetromino::Draw(Tetromino::l_piece, 0, view_tetrominoes_pos, 0.5f, render_group); +    Tetromino::Draw(Tetromino::l_piece, 0, view_tetrominoes_pos, 0.5f, m_RenderGroup);      view_tetrominoes_pos.y -= view_advance.y; -    Tetromino::Draw(Tetromino::i_piece, 0, view_tetrominoes_pos, 0.5f, render_group); +    Tetromino::Draw(Tetromino::i_piece, 0, view_tetrominoes_pos, 0.5f, m_RenderGroup);      view_tetrominoes_pos.y -= view_advance.y; @@ -328,9 +329,9 @@ void Tetris::DrawStatistics(RenderGroup &render_group) {      ImGui::End();  } -void Tetris::DrawScore(RenderGroup &render_group) { +void Tetris::DrawScore() {      V2F32 view_pos = {3.0f, 2.2f}; -    ImVec2 screen_pos = render_group.ViewPosToScreenPosImGui(view_pos); +    ImVec2 screen_pos = m_RenderGroup.ViewPosToScreenPosImGui(view_pos);      ImGui::SetNextWindowPos(screen_pos);      ImGui::Begin("TetrisScore", nullptr, s_DefaultImGuiWindowFlags); @@ -339,9 +340,9 @@ void Tetris::DrawScore(RenderGroup &render_group) {      ImGui::End();  } -void Tetris::DrawNextTetromino(RenderGroup &render_group) { +void Tetris::DrawNextTetromino() {      V2F32 text_view_pos = {3.0f, 1.8f}; -    ImVec2 text_screen_pos = render_group.ViewPosToScreenPosImGui(text_view_pos); +    ImVec2 text_screen_pos = m_RenderGroup.ViewPosToScreenPosImGui(text_view_pos);      ImGui::SetNextWindowPos(text_screen_pos);      ImGui::Begin("TetrisNextTetromino", nullptr, s_DefaultImGuiWindowFlags); @@ -350,12 +351,12 @@ void Tetris::DrawNextTetromino(RenderGroup &render_group) {      V2F32 tetromino_view_pos = {3.0, 1.4f}; -    Tetromino::Draw(m_NextTetromino.GetId(), 0, tetromino_view_pos, 0.5f, render_group); +    Tetromino::Draw(m_NextTetromino.GetId(), 0, tetromino_view_pos, 0.5f, m_RenderGroup);  } -void Tetris::DrawLevel(RenderGroup &render_group) { +void Tetris::DrawLevel() {      V2F32 view_pos = {3.0f, 1.2f}; -    ImVec2 screen_pos = render_group.ViewPosToScreenPosImGui(view_pos); +    ImVec2 screen_pos = m_RenderGroup.ViewPosToScreenPosImGui(view_pos);      ImGui::SetNextWindowPos(screen_pos);      ImGui::Begin("TetrisLevel", nullptr, s_DefaultImGuiWindowFlags); diff --git a/src/games/tetris/Tetris.hpp b/src/games/tetris/Tetris.hpp index 42981db..f4a8caf 100644 --- a/src/games/tetris/Tetris.hpp +++ b/src/games/tetris/Tetris.hpp @@ -17,8 +17,8 @@ enum class TetrisRunningState {  class Tetris : public Game {  public: -    Tetris(); -    bool Update(std::vector<SDL_Event> &events, RenderGroup& render_group) override; +    Tetris(RenderGroup& render_group); +    bool Update(std::vector<SDL_Event> &events) override;      void HandleTetrominoPlacement();  private: @@ -29,15 +29,15 @@ private:      uint32_t GetHarddropCount(float dt);      void HandleGameOver(); -    void Draw(RenderGroup &render_group); -    void DrawLineCounter(RenderGroup &render_group); -    void DrawStatistics(RenderGroup &render_group); -    void DrawScore(RenderGroup &render_group); -    void DrawNextTetromino(RenderGroup &render_group); -    void DrawLevel(RenderGroup &render_group); +    void Draw(); +    void DrawLineCounter(); +    void DrawStatistics(); +    void DrawScore(); +    void DrawNextTetromino(); +    void DrawLevel(); -    void DrawPauseMenu(RenderGroup &render_group); -    void DrawGameOverMenu(RenderGroup &render_group); +    void DrawPauseMenu(); +    void DrawGameOverMenu();  private:      static constexpr ImGuiWindowFlags s_MenuImGuiWindowFlags = ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_AlwaysAutoResize; @@ -45,6 +45,7 @@ private:  private: +    RenderGroup& m_RenderGroup;      TetrisRunningState m_RunningState = TetrisRunningState::Resume;      float m_DtInSecondsRemaining = 0.0f;  | 
