diff options
| author | fschildt <florian.schildt@protonmail.com> | 2025-09-28 11:23:04 +0200 |
|---|---|---|
| committer | fschildt <florian.schildt@protonmail.com> | 2025-09-28 11:23:04 +0200 |
| commit | 4537e9bee3d054786857fa92824e2d9e8385bb36 (patch) | |
| tree | 92ee1fea597f8164d09885286b71d812f6890259 /src | |
| parent | 3d30e1ee9d1c9fb67cca8e3f178ba5fd05a2726e (diff) | |
minesweeper: now playable
Diffstat (limited to 'src')
| -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 | ||||
| -rw-r--r-- | src/main.cpp | 4 |
9 files changed, 378 insertions, 269 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; diff --git a/src/main.cpp b/src/main.cpp index 0cf89d7..91192b4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -154,7 +154,7 @@ main(int argc, char **argv) if (game) { - bool keep_game_running = game->Update(game_events, render_group); + bool keep_game_running = game->Update(game_events); if (!keep_game_running) { game.reset(); } @@ -162,7 +162,7 @@ main(int argc, char **argv) else { Game::GameType type = do_menu(render_group); if (type != Game::NO_GAME) { - game = Game::Select(type); + game = Game::Select(type, render_group); } } game_events.clear(); |
