From 3d4c0445d130ff19fc001da9a7ccc5062d39534c Mon Sep 17 00:00:00 2001 From: Malte Langkabel Date: Sun, 12 Jul 2026 20:37:22 +0200 Subject: [PATCH] Add config-defined tooltip to header building blocks stock Implement REQ-UI-BLOCKS-TOOLTIP: an optional world.toml [world].building_blocks_tooltip, threaded into HeaderBar via a new const GameConfig* parameter and shown as the building blocks stock label's hover tooltip. Omitted when unset. Author the text in the app config and extend ConfigLoaderTest to cover the field. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01DZR44tA8sn4dPqDzAVXyps --- bin/app/data/config/world.toml | 1 + bin/test/data/config/world.toml | 1 + src/lib/config/ConfigLoader.cpp | 6 ++++++ src/lib/config/WorldConfig.h | 7 +++++++ src/test/ConfigLoaderTest.cpp | 5 +++++ src/ui/HeaderBar.cpp | 7 ++++++- src/ui/HeaderBar.h | 3 ++- src/ui/MainWindow.cpp | 2 +- 8 files changed, 29 insertions(+), 3 deletions(-) diff --git a/bin/app/data/config/world.toml b/bin/app/data/config/world.toml index b5ed7e5..6ded2f6 100644 --- a/bin/app/data/config/world.toml +++ b/bin/app/data/config/world.toml @@ -10,6 +10,7 @@ tunnel_max_distance_tiles = 10 departure_interval_seconds = 20 orbit_factor = 0.8 rally_orbit_radius_tiles = 5.0 +building_blocks_tooltip = "Building blocks are the currency for construction. Spend them to place buildings and to expand the asteroid. Produce building blocks in your factory and deliver them to the HQ on a belt to grow your stock." [regions] asteroid_width_tiles = 60 diff --git a/bin/test/data/config/world.toml b/bin/test/data/config/world.toml index 4ec5bb6..7aaef45 100644 --- a/bin/test/data/config/world.toml +++ b/bin/test/data/config/world.toml @@ -10,6 +10,7 @@ tunnel_max_distance_tiles = 10 departure_interval_seconds = 20 orbit_factor = 0.8 rally_orbit_radius_tiles = 5.0 +building_blocks_tooltip = "Spend building blocks to build; deliver them to the HQ to gain more." [regions] asteroid_width_tiles = 40 diff --git a/src/lib/config/ConfigLoader.cpp b/src/lib/config/ConfigLoader.cpp index 6965066..4935071 100644 --- a/src/lib/config/ConfigLoader.cpp +++ b/src/lib/config/ConfigLoader.cpp @@ -273,6 +273,12 @@ WorldConfig ConfigLoader::loadWorld(const std::string& path) cfg.orbitFactor = requireDouble(tbl["world"]["orbit_factor"], file, "world.orbit_factor"); cfg.rallyOrbitRadius_tiles = requireDouble(tbl["world"]["rally_orbit_radius_tiles"], file, "world.rally_orbit_radius_tiles"); + if (const std::optional tip = + tbl["world"]["building_blocks_tooltip"].value()) + { + cfg.buildingBlocksTooltip = *tip; + } + cfg.regions.asteroidWidth_tiles = static_cast(requireInt(tbl["regions"]["asteroid_width_tiles"], file, "regions.asteroid_width_tiles")); cfg.regions.playerBufferWidth_tiles = static_cast(requireInt(tbl["regions"]["player_buffer_width_tiles"], file, "regions.player_buffer_width_tiles")); cfg.regions.contestZoneWidth_tiles = static_cast(requireInt(tbl["regions"]["contest_zone_width_tiles"], file, "regions.contest_zone_width_tiles")); diff --git a/src/lib/config/WorldConfig.h b/src/lib/config/WorldConfig.h index 035505b..40a5ac4 100644 --- a/src/lib/config/WorldConfig.h +++ b/src/lib/config/WorldConfig.h @@ -1,5 +1,8 @@ #pragma once +#include +#include + #include "Formula.h" // Region widths are in tiles (REQ-GW-REGIONS). @@ -75,6 +78,10 @@ struct WorldConfig double orbitFactor; // REQ-SHP-ORBIT (multiplies tool range for orbit radius) double rallyOrbitRadius_tiles; // REQ-SHP-ORBIT (fixed orbit radius around the rally point) + // Optional hover-tooltip for the header building blocks stock display + // (REQ-UI-BLOCKS-TOOLTIP). Presentation-only; the simulation ignores it. + std::optional buildingBlocksTooltip; + WorldRegions regions; WorldExpansion expansion; WorldPush push; diff --git a/src/test/ConfigLoaderTest.cpp b/src/test/ConfigLoaderTest.cpp index f06e8df..5b00856 100644 --- a/src/test/ConfigLoaderTest.cpp +++ b/src/test/ConfigLoaderTest.cpp @@ -83,6 +83,11 @@ TEST_CASE("ConfigLoader loads the committed bin/config/ configs end-to-end", "[c REQUIRE(cfg.world.rallyOrbitRadius_tiles == Approx(5.0)); REQUIRE(cfg.world.scrapPerThreat == Approx(1.0)); + // Optional header building blocks tooltip (REQ-UI-BLOCKS-TOOLTIP). + REQUIRE(cfg.world.buildingBlocksTooltip.has_value()); + REQUIRE(*cfg.world.buildingBlocksTooltip == + "Spend building blocks to build; deliver them to the HQ to gain more."); + // Spot-check that a config-derived formula computes as expected. // threat_rate_formula = "x": evaluates to the input value. REQUIRE(cfg.world.waves.threatRateFormula.evaluate(1.0) == Approx(1.0)); diff --git a/src/ui/HeaderBar.cpp b/src/ui/HeaderBar.cpp index 2602fa6..aec8887 100644 --- a/src/ui/HeaderBar.cpp +++ b/src/ui/HeaderBar.cpp @@ -17,7 +17,7 @@ const double HeaderBar::kSpeeds[] = { 0.0, 0.5, 1.0, 2.0, 10.0 }; const int HeaderBar::kSpeedCount = 5; -HeaderBar::HeaderBar(QWidget* parent) +HeaderBar::HeaderBar(const GameConfig* config, QWidget* parent) : QWidget(parent) { QHBoxLayout* layout = new QHBoxLayout(this); @@ -26,6 +26,11 @@ HeaderBar::HeaderBar(QWidget* parent) m_timeLabel = new QLabel("00:00", this); m_blocksLabel = new QLabel(tr("Building Blocks: 0"), this); + if (config->world.buildingBlocksTooltip) + { + m_blocksLabel->setToolTip( + QString::fromStdString(*config->world.buildingBlocksTooltip)); + } m_artifactsLabel = new QLabel(tr("Artifacts: 0/?"), this); m_bossLabel = new QLabel(tr("Boss Wave #1 Next boss: 5:00"), this); layout->addWidget(m_timeLabel); diff --git a/src/ui/HeaderBar.h b/src/ui/HeaderBar.h index b2cd47a..1438e19 100644 --- a/src/ui/HeaderBar.h +++ b/src/ui/HeaderBar.h @@ -9,6 +9,7 @@ #include "BuildingBlocksChangedEvent.h" #include "EventHandler.h" #include "ExpansionCostChangedEvent.h" +#include "GameConfig.h" #include "GameSpeedChangedEvent.h" #include "Tick.h" #include "TickAdvancedEvent.h" @@ -27,7 +28,7 @@ class HeaderBar : public QWidget, Q_OBJECT public: - explicit HeaderBar(QWidget* parent = nullptr); + explicit HeaderBar(const GameConfig* config, QWidget* parent = nullptr); ~HeaderBar() override; private slots: diff --git a/src/ui/MainWindow.cpp b/src/ui/MainWindow.cpp index 3265e8a..b19ff92 100644 --- a/src/ui/MainWindow.cpp +++ b/src/ui/MainWindow.cpp @@ -42,7 +42,7 @@ MainWindow::MainWindow(Simulation* sim, const std::string& configDir, setWindowTitle(tr("Dota Factory")); resize(1280, 768); - m_headerBar = new HeaderBar(this); + m_headerBar = new HeaderBar(&sim->config(), this); m_gameWorldView = new GameWorldView(sim, &sim->config(), &m_visuals, m_configDir, m_replay.get(), this);