From 5e3060998d40b400f6d126adeb82220ba9726fd6 Mon Sep 17 00:00:00 2001 From: Karn Kaul Date: Mon, 19 Jan 2026 19:19:50 -0800 Subject: [PATCH 1/4] Upgrade le2d to v0.4.8 --- ext/depzip.jsonc | 2 +- ext/src.zip | Bin 11148366 -> 11149514 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/depzip.jsonc b/ext/depzip.jsonc index e3dcbbf..d98fab9 100644 --- a/ext/depzip.jsonc +++ b/ext/depzip.jsonc @@ -2,7 +2,7 @@ "packages": [ { "uri": "karnkaul/le2d", - "branch": "v0.4.7" + "branch": "v0.4.8" } ] } \ No newline at end of file diff --git a/ext/src.zip b/ext/src.zip index 44a7cb05b20349127185658e56a93b2cf11a12f3..fe87156cf0838dc763fa7ae35d141f9d1adb6206 100644 GIT binary patch delta 17113 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 From 972575745a4d22c9e2246510206d08ebfec97bf8 Mon Sep 17 00:00:00 2001 From: Karn Kaul Date: Mon, 19 Jan 2026 19:25:37 -0800 Subject: [PATCH 2/4] Move clear color to theme --- lib/include/chomper/theme.hpp | 6 ++++++ lib/src/engine.cpp | 5 ++--- 2 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 lib/include/chomper/theme.hpp diff --git a/lib/include/chomper/theme.hpp b/lib/include/chomper/theme.hpp new file mode 100644 index 0000000..460eb75 --- /dev/null +++ b/lib/include/chomper/theme.hpp @@ -0,0 +1,6 @@ +#pragma once +#include + +namespace chomper::theme { +constexpr auto clearColor_v = kvf::Color{glm::vec4{.34f, .54f, .2f, 1.f}}; +} // namespace chomper::theme diff --git a/lib/src/engine.cpp b/lib/src/engine.cpp index de52ee2..fcdd666 100644 --- a/lib/src/engine.cpp +++ b/lib/src/engine.cpp @@ -2,6 +2,7 @@ #include "chomper/build_version.hpp" #include "chomper/inclusive_range.hpp" #include "chomper/runtimes/entrypoint.hpp" +#include "chomper/theme.hpp" #include "chomper/viewport.hpp" #include #include @@ -12,8 +13,6 @@ namespace chomper { namespace { -constexpr auto clearColor_v = kvf::Color{glm::vec4{.34f, .54f, .2f, 1.f}}; - std::unique_ptr createEntrypoint(Engine& engine) { return std::make_unique(&engine); } @@ -52,7 +51,7 @@ void Engine::run() { m_runtime->tick(scaledDt); // render runtime. - auto& renderer = m_context->begin_render(clearColor_v); + auto& renderer = m_context->begin_render(theme::clearColor_v); renderer.viewport = viewport_v; renderer.polygon_mode = m_runtimeState.wireframe ? vk::PolygonMode::eLine : vk::PolygonMode::eFill; From d4d7b0ed2e4079d1f587276afa5433891d8e464b Mon Sep 17 00:00:00 2001 From: Karn Kaul Date: Mon, 19 Jan 2026 19:35:22 -0800 Subject: [PATCH 3/4] Add some flair to countdown Games should not have "static" screens with nothing happening. --- lib/include/chomper/runtimes/game.hpp | 5 +- lib/include/chomper/ui/countdown.hpp | 34 ++++++++++++++ lib/src/runtimes/game.cpp | 18 ++++---- lib/src/ui/countdown.cpp | 66 +++++++++++++++++++++++++++ 4 files changed, 112 insertions(+), 11 deletions(-) create mode 100644 lib/include/chomper/ui/countdown.hpp create mode 100644 lib/src/ui/countdown.cpp diff --git a/lib/include/chomper/runtimes/game.hpp b/lib/include/chomper/runtimes/game.hpp index f1b563c..abf2f7d 100644 --- a/lib/include/chomper/runtimes/game.hpp +++ b/lib/include/chomper/runtimes/game.hpp @@ -3,6 +3,7 @@ #include "chomper/engine.hpp" #include "chomper/player.hpp" #include "chomper/runtime.hpp" +#include "chomper/ui/countdown.hpp" #include "chomper/world.hpp" #include #include @@ -10,6 +11,7 @@ #include #include #include +#include #include namespace chomper::runtime { @@ -57,7 +59,6 @@ class Game : public IRuntime, public klib::Pinned { std::vector m_emptyTiles{}; - le::drawable::Text m_countdownText{}; - kvf::Seconds m_countdown{3}; + std::optional m_countdown{}; }; } // namespace chomper::runtime diff --git a/lib/include/chomper/ui/countdown.hpp b/lib/include/chomper/ui/countdown.hpp new file mode 100644 index 0000000..c939dc8 --- /dev/null +++ b/lib/include/chomper/ui/countdown.hpp @@ -0,0 +1,34 @@ +#pragma once +#include "le2d/drawable/shape.hpp" +#include "le2d/drawable/text.hpp" +#include + +namespace chomper::ui { +class Countdown { + public: + static constexpr auto textHeight_v = le::TextHeight{120}; + + explicit Countdown(gsl::not_null font, le::TextHeight textHeight = textHeight_v, kvf::Seconds timer = 3s); + + [[nodiscard]] auto getRemain() const -> kvf::Seconds { + return m_remain; + } + + void tick(kvf::Seconds dt); + void draw(le::IRenderer& renderer) const; + + private: + void setTimerText(std::chrono::seconds value); + void updateSector(); + + gsl::not_null m_font; + le::TextHeight m_textHeight{120}; + + le::drawable::Sector m_sector{}; + le::drawable::Circle m_background{}; + le::drawable::Text m_text{}; + + kvf::Seconds m_timer{}; + kvf::Seconds m_remain{}; +}; +} // namespace chomper::ui diff --git a/lib/src/runtimes/game.cpp b/lib/src/runtimes/game.cpp index 525f9dc..875eec7 100644 --- a/lib/src/runtimes/game.cpp +++ b/lib/src/runtimes/game.cpp @@ -9,11 +9,9 @@ namespace chomper::runtime { namespace { -constexpr auto countdownParams_v = le::drawable::Text::Params{ - .height = le::TextHeight{60}, -}; constexpr auto collectibleAmount_v = 10; } // namespace + using ActionValue = le::input::action::Value; Game::Game(gsl::not_null engine) : m_engine(engine), m_mapping(&engine->getInputRouter()) { @@ -24,7 +22,7 @@ Game::Game(gsl::not_null engine) : m_engine(engine), m_mapping(&engine- spawnCollectibles(); - m_countdownText.set_string(engine->getResources().getMainFont(), "3", countdownParams_v); + m_countdown.emplace(&engine->getResources().getMainFont()); } void Game::tick(kvf::Seconds const dt) { @@ -34,9 +32,11 @@ void Game::tick(kvf::Seconds const dt) { } ImGui::End(); - if (m_countdown.count() > 0) { - m_countdown -= dt; - m_countdownText.set_string(m_engine->getResources().getMainFont(), std::format("{}", static_cast(m_countdown.count() + 1)), countdownParams_v); + if (m_countdown) { + m_countdown->tick(dt); + if (m_countdown->getRemain() <= 0s) { + m_countdown.reset(); + } return; } @@ -56,8 +56,8 @@ void Game::render(le::IRenderer& renderer) const { collectible.draw(renderer); } m_player->draw(renderer); - if (m_countdown.count() > 0) { - m_countdownText.draw(renderer); + if (m_countdown) { + m_countdown->draw(renderer); } } diff --git a/lib/src/ui/countdown.cpp b/lib/src/ui/countdown.cpp new file mode 100644 index 0000000..8823e3a --- /dev/null +++ b/lib/src/ui/countdown.cpp @@ -0,0 +1,66 @@ +#include "chomper/ui/countdown.hpp" +#include "chomper/theme.hpp" +#include + +namespace chomper::ui { +Countdown::Countdown(gsl::not_null font, le::TextHeight const textHeight, kvf::Seconds const timer) + : m_font(font), m_textHeight(textHeight), m_timer(timer), m_remain(timer) { + auto const diameter = float(m_textHeight) + 30.0f; + m_background.create(diameter); + m_background.tint = theme::clearColor_v; +} + +void Countdown::tick(kvf::Seconds const dt) { + if (m_remain <= 0s) { + return; + } + + auto const prevSeconds = std::chrono::duration_cast(m_remain); + m_remain -= dt; + auto const currSeconds = std::chrono::duration_cast(m_remain); + if (prevSeconds > currSeconds) { + setTimerText(currSeconds + 1s); + } + + updateSector(); +} + +void Countdown::draw(le::IRenderer& renderer) const { + if (m_remain <= 0s) { + return; + } + + m_sector.draw(renderer); + m_background.draw(renderer); + m_text.draw(renderer); +} + +void Countdown::setTimerText(std::chrono::seconds const value) { + auto const params = le::drawable::Text::Params{ + .height = m_textHeight, + .expand = le::drawable::TextExpand::eBoth, + }; + auto const text = std::format("{}", value.count()); + m_text.set_string(*m_font, text, params); + + // technically this isn't correct y-centering because parts of glyphs can be above/below the baseline, + // and Text::get_size() is insufficient to adjust for that. + // here, since each displayed glyph (0-9) is entirely above the baseline (unlike say 'g'), + // it can be pushed down by half the size and it will "look" y-centered. + // this is what is known as a "hack". + m_text.transform.position.y = -0.5f * m_text.get_size().y; +} + +void Countdown::updateSector() { + KLIB_ASSERT(m_timer > 0s); + auto const ratio = m_remain / m_timer; + static constexpr auto degreesBegin{90.0f}; + auto const degreesEnd = degreesBegin + (ratio * 360.0f); + auto const diameter = m_background.get_diameter() + 30.0f; + auto const sectorParams = le::shape::Sector::Params{ + .degrees_begin = degreesBegin, + .degrees_end = degreesEnd, + }; + m_sector.create(diameter, sectorParams); +} +} // namespace chomper::ui From fcce5d7e4054bfa2f34aa794dcc2d6c2f5e2dde7 Mon Sep 17 00:00:00 2001 From: Karn Kaul Date: Mon, 19 Jan 2026 19:41:18 -0800 Subject: [PATCH 4/4] Cleanup --- lib/include/chomper/ui/countdown.hpp | 2 +- lib/src/ui/countdown.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/include/chomper/ui/countdown.hpp b/lib/include/chomper/ui/countdown.hpp index c939dc8..3eaadc2 100644 --- a/lib/include/chomper/ui/countdown.hpp +++ b/lib/include/chomper/ui/countdown.hpp @@ -22,7 +22,7 @@ class Countdown { void updateSector(); gsl::not_null m_font; - le::TextHeight m_textHeight{120}; + le::TextHeight m_textHeight{}; le::drawable::Sector m_sector{}; le::drawable::Circle m_background{}; diff --git a/lib/src/ui/countdown.cpp b/lib/src/ui/countdown.cpp index 8823e3a..88bf6bf 100644 --- a/lib/src/ui/countdown.cpp +++ b/lib/src/ui/countdown.cpp @@ -46,7 +46,7 @@ void Countdown::setTimerText(std::chrono::seconds const value) { // technically this isn't correct y-centering because parts of glyphs can be above/below the baseline, // and Text::get_size() is insufficient to adjust for that. // here, since each displayed glyph (0-9) is entirely above the baseline (unlike say 'g'), - // it can be pushed down by half the size and it will "look" y-centered. + // it can be pushed down by half the size and it will "look" consistently y-centered. // this is what is known as a "hack". m_text.transform.position.y = -0.5f * m_text.get_size().y; } @@ -54,11 +54,11 @@ void Countdown::setTimerText(std::chrono::seconds const value) { void Countdown::updateSector() { KLIB_ASSERT(m_timer > 0s); auto const ratio = m_remain / m_timer; - static constexpr auto degreesBegin{90.0f}; - auto const degreesEnd = degreesBegin + (ratio * 360.0f); + static constexpr auto degreesBegin_v{90.0f}; + auto const degreesEnd = degreesBegin_v + (ratio * 360.0f); auto const diameter = m_background.get_diameter() + 30.0f; auto const sectorParams = le::shape::Sector::Params{ - .degrees_begin = degreesBegin, + .degrees_begin = degreesBegin_v, .degrees_end = degreesEnd, }; m_sector.create(diameter, sectorParams);