Add config-defined tooltips to build and module buttons
Implement REQ-UI-BUILD-TOOLTIP and REQ-MOD-UI-MODULE-TOOLTIP: an optional tooltip string on BuildingDef and ModuleDef, parsed from buildings.toml and modules.toml and shown as the build/module button's hover tooltip. Omitted when unset. Author descriptions for all buildings and modules in the app config, and extend ConfigLoaderTest to cover the optional field. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DZR44tA8sn4dPqDzAVXyps
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
[[building]]
|
||||
id = "belt"
|
||||
tooltip = "Transports items one tile at a time in the direction it faces."
|
||||
cost = 2
|
||||
player_placeable = true
|
||||
construction_time_seconds = 0.2
|
||||
@@ -7,6 +8,7 @@ surface_mask = ["A>"]
|
||||
|
||||
[[building]]
|
||||
id = "splitter"
|
||||
tooltip = "Splits an incoming item stream between two outputs, with optional per-output filters."
|
||||
cost = 3
|
||||
player_placeable = true
|
||||
construction_time_seconds = 0.5
|
||||
@@ -14,6 +16,7 @@ surface_mask = ["<A>"]
|
||||
|
||||
[[building]]
|
||||
id = "tunnel_entry"
|
||||
tooltip = "Sends items underground to a matching tunnel exit, letting belts cross."
|
||||
cost = 5
|
||||
player_placeable = true
|
||||
construction_time_seconds = 0.5
|
||||
@@ -21,6 +24,7 @@ surface_mask = ["A>"]
|
||||
|
||||
[[building]]
|
||||
id = "tunnel_exit"
|
||||
tooltip = "Receives items from a matching tunnel entry and pushes them onward."
|
||||
cost = 5
|
||||
player_placeable = true
|
||||
construction_time_seconds = 0.5
|
||||
@@ -28,6 +32,7 @@ surface_mask = ["A>"]
|
||||
|
||||
[[building]]
|
||||
id = "miner"
|
||||
tooltip = "Extracts a selected ore from the asteroid; every tile yields any ore."
|
||||
cost = 15
|
||||
player_placeable = true
|
||||
construction_time_seconds = 1
|
||||
@@ -37,6 +42,7 @@ surface_mask = [
|
||||
|
||||
[[building]]
|
||||
id = "smelter"
|
||||
tooltip = "Melts ore or scrap into basic materials. No recipe selection needed."
|
||||
cost = 20
|
||||
player_placeable = true
|
||||
construction_time_seconds = 1
|
||||
@@ -47,6 +53,7 @@ surface_mask = [
|
||||
|
||||
[[building]]
|
||||
id = "assembler"
|
||||
tooltip = "Crafts a selected recipe from the production tree into intermediate or final parts."
|
||||
cost = 35
|
||||
player_placeable = true
|
||||
construction_time_seconds = 1
|
||||
@@ -57,6 +64,7 @@ surface_mask = [
|
||||
|
||||
[[building]]
|
||||
id = "reprocessing_plant"
|
||||
tooltip = "Consumes scrap and yields one random higher-tier product per cycle."
|
||||
cost = 40
|
||||
player_placeable = true
|
||||
construction_time_seconds = 1
|
||||
@@ -68,6 +76,7 @@ surface_mask = [
|
||||
|
||||
[[building]]
|
||||
id = "shipyard"
|
||||
tooltip = "Builds autonomous combat ships from a selected schematic and module layout."
|
||||
cost = 60
|
||||
player_placeable = true
|
||||
construction_time_seconds = 1
|
||||
@@ -78,6 +87,7 @@ surface_mask = [
|
||||
|
||||
[[building]]
|
||||
id = "salvage_bay"
|
||||
tooltip = "Drop-off point where salvage ships unload collected scrap onto belts."
|
||||
cost = 25
|
||||
player_placeable = true
|
||||
construction_time_seconds = 1
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
[[module]]
|
||||
id = "railgun_s"
|
||||
tooltip = "Small railgun. Fast-firing, short range, low damage; fits any hull."
|
||||
unlock_at_station_level = -1
|
||||
surface_mask = ["O"]
|
||||
materials = [{item = "railgun_s_module", amount = 1}]
|
||||
@@ -47,6 +48,7 @@ attack_rate_hz = 2.0
|
||||
|
||||
[[module]]
|
||||
id = "railgun_m"
|
||||
tooltip = "Medium railgun. Higher damage at longer range; needs a 2x2 slot."
|
||||
unlock_at_station_level = 2
|
||||
surface_mask = [
|
||||
"OO",
|
||||
@@ -64,6 +66,7 @@ attack_rate_hz = 1.5
|
||||
|
||||
[[module]]
|
||||
id = "railgun_l"
|
||||
tooltip = "Large railgun. Heavy damage at long range; needs a 3x3 slot."
|
||||
unlock_at_station_level = 6
|
||||
unlock_requires = ["railgun_m"]
|
||||
surface_mask = [
|
||||
@@ -86,6 +89,7 @@ attack_rate_hz = 0.8
|
||||
|
||||
[[module]]
|
||||
id = "salvager"
|
||||
tooltip = "Collects scrap from wrecks and stores it in the ship's cargo hold."
|
||||
unlock_at_station_level = -1
|
||||
surface_mask = ["O"]
|
||||
materials = [{item = "salvager_module", amount = 1}]
|
||||
@@ -101,6 +105,7 @@ collection_rate_hz = 0.5
|
||||
|
||||
[[module]]
|
||||
id = "repair_tool"
|
||||
tooltip = "Repairs damaged friendly ships and defence stations within range."
|
||||
unlock_at_station_level = 0
|
||||
surface_mask = ["O"]
|
||||
materials = [{item = "repair_tool_module", amount = 1}]
|
||||
@@ -119,6 +124,7 @@ repair_range_m = 80
|
||||
|
||||
[[module]]
|
||||
id = "afterburner"
|
||||
tooltip = "Greatly boosts top speed and forward acceleration."
|
||||
unlock_at_station_level = 2
|
||||
surface_mask = ["OOO"]
|
||||
materials = [{item = "afterburner_module", amount = 1}]
|
||||
@@ -133,6 +139,7 @@ added_main_acceleration_mpss = 60
|
||||
|
||||
[[module]]
|
||||
id = "maneuvering_thrusters"
|
||||
tooltip = "Improves top speed and lateral/braking acceleration."
|
||||
unlock_at_station_level = 1
|
||||
surface_mask = ["OO"]
|
||||
materials = [{item = "maneuvering_thrusters_module", amount = 1}]
|
||||
@@ -150,6 +157,7 @@ added_maneuvering_acceleration_mpss = 10
|
||||
|
||||
[[module]]
|
||||
id = "armor_plates"
|
||||
tooltip = "Adds a large flat bonus to the ship's hit points."
|
||||
unlock_at_station_level = 0
|
||||
surface_mask = ["OO"]
|
||||
materials = [{item = "armor_plates_module", amount = 1}]
|
||||
@@ -163,6 +171,7 @@ added_hp = 1200
|
||||
|
||||
[[module]]
|
||||
id = "sensor_booster"
|
||||
tooltip = "Extends the ship's sensor range."
|
||||
unlock_at_station_level = 1
|
||||
surface_mask = ["OO"]
|
||||
materials = [{item = "sensor_booster_module", amount = 1}]
|
||||
@@ -179,6 +188,7 @@ added_sensor_range_m = 50
|
||||
|
||||
[[module]]
|
||||
id = "weapon_upgrade"
|
||||
tooltip = "Increases the damage of all weapons on the ship."
|
||||
unlock_at_station_level = 4
|
||||
surface_mask = [
|
||||
"OO",
|
||||
@@ -195,6 +205,7 @@ multiplied_damage = 1.2
|
||||
|
||||
[[module]]
|
||||
id = "weapon_primer"
|
||||
tooltip = "Increases the fire rate of all weapons on the ship."
|
||||
unlock_at_station_level = 4
|
||||
surface_mask = [
|
||||
"OO",
|
||||
@@ -211,6 +222,7 @@ multiplied_attack_rate_hz = 1.2
|
||||
|
||||
[[module]]
|
||||
id = "weapon_stabilizer"
|
||||
tooltip = "Extends weapon range at the cost of some fire rate."
|
||||
unlock_at_station_level = 3
|
||||
surface_mask = [
|
||||
"OO",
|
||||
@@ -234,6 +246,7 @@ multiplied_attack_rate_hz = 0.8
|
||||
|
||||
[[module]]
|
||||
id = "drone_bay"
|
||||
tooltip = "Drone launch bay (capability not yet implemented)."
|
||||
unlock_at_station_level = 5
|
||||
surface_mask = [
|
||||
"OO",
|
||||
@@ -246,6 +259,7 @@ glyph = "Db"
|
||||
|
||||
[[module]]
|
||||
id = "drone_hangar"
|
||||
tooltip = "Large drone hangar (capability not yet implemented)."
|
||||
unlock_at_station_level = 9
|
||||
surface_mask = [
|
||||
"OOOOOO",
|
||||
|
||||
@@ -84,6 +84,7 @@ cost = 25
|
||||
player_placeable = true
|
||||
construction_time_seconds = 15
|
||||
output_buffer_capacity = 20
|
||||
tooltip = "Drop-off point for salvage ships."
|
||||
surface_mask = [
|
||||
"SAA",
|
||||
"SAA>",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
[[module]]
|
||||
id = "armor_plate"
|
||||
tooltip = "Adds a large flat bonus to hit points."
|
||||
unlock_at_station_level = -1
|
||||
surface_mask = ["OO"]
|
||||
materials = [{item = "iron_ingot", amount = 2}]
|
||||
|
||||
@@ -23,6 +23,9 @@ struct BuildingDef
|
||||
// Output-buffer holding size for buildings without a recipe-driven buffer.
|
||||
// Only the Salvage Bay sets this (REQ-BLD-SALVAGE-BAY).
|
||||
std::optional<int> outputBufferCapacity;
|
||||
|
||||
// Optional hover-tooltip text for the build button (REQ-UI-BUILD-TOOLTIP).
|
||||
std::optional<std::string> tooltip;
|
||||
};
|
||||
|
||||
struct BuildingsConfig
|
||||
|
||||
@@ -343,6 +343,11 @@ BuildingsConfig ConfigLoader::loadBuildings(const std::string& path)
|
||||
requireInt(mt["output_buffer_capacity"], file, elemPath + ".output_buffer_capacity"));
|
||||
}
|
||||
|
||||
if (mt.contains("tooltip"))
|
||||
{
|
||||
def.tooltip = requireString(mt["tooltip"], file, elemPath + ".tooltip");
|
||||
}
|
||||
|
||||
const std::optional<BuildingType> parsedType = parseBuildingType(def.id);
|
||||
if (!parsedType)
|
||||
{
|
||||
@@ -603,6 +608,11 @@ ModulesConfig ConfigLoader::loadModules(const std::string& path)
|
||||
def.fillColor = requireString(mt["fill_color"], file, elemPath + ".fill_color");
|
||||
def.glyph = requireString(mt["glyph"], file, elemPath + ".glyph");
|
||||
|
||||
if (mt.contains("tooltip"))
|
||||
{
|
||||
def.tooltip = requireString(mt["tooltip"], file, elemPath + ".tooltip");
|
||||
}
|
||||
|
||||
// Materials
|
||||
{
|
||||
const toml::array& materials = requireArray(mt["materials"], file, elemPath + ".materials");
|
||||
|
||||
@@ -54,6 +54,10 @@ struct ModuleDef
|
||||
std::optional<ModuleWeaponCapability> weaponCapability;
|
||||
std::optional<ModuleSalvageCapability> salvageCapability;
|
||||
std::optional<ModuleRepairCapability> repairCapability;
|
||||
|
||||
// Optional hover-tooltip text for the module selection button
|
||||
// (REQ-MOD-UI-MODULE-TOOLTIP).
|
||||
std::optional<std::string> tooltip;
|
||||
};
|
||||
|
||||
struct ModulesConfig
|
||||
|
||||
@@ -113,6 +113,12 @@ TEST_CASE("ConfigLoader loads the committed bin/config/ configs end-to-end", "[c
|
||||
REQUIRE(salvageBayIt->outputBufferCapacity.has_value());
|
||||
REQUIRE(*salvageBayIt->outputBufferCapacity == 20);
|
||||
|
||||
// Optional per-building tooltip (REQ-UI-BUILD-TOOLTIP): the Salvage Bay
|
||||
// defines one; the Miner leaves it unset.
|
||||
REQUIRE(salvageBayIt->tooltip.has_value());
|
||||
REQUIRE(*salvageBayIt->tooltip == "Drop-off point for salvage ships.");
|
||||
REQUIRE_FALSE(minerIt->tooltip.has_value());
|
||||
|
||||
// recipes.toml — reprocessing cycle has three weighted outputs.
|
||||
const auto reproIt = std::find_if(
|
||||
cfg.recipes.recipes.begin(), cfg.recipes.recipes.end(),
|
||||
@@ -144,6 +150,21 @@ TEST_CASE("ConfigLoader loads the committed bin/config/ configs end-to-end", "[c
|
||||
REQUIRE(salvageShipIt != cfg.ships.ships.end());
|
||||
REQUIRE(salvageShipIt->defaultModules.empty());
|
||||
|
||||
// modules.toml — optional per-module tooltip (REQ-MOD-UI-MODULE-TOOLTIP):
|
||||
// armor_plate defines one; salvager leaves it unset.
|
||||
const auto armorPlateIt = std::find_if(
|
||||
cfg.modules.modules.begin(), cfg.modules.modules.end(),
|
||||
[](const ModuleDef& m) { return m.id == "armor_plate"; });
|
||||
REQUIRE(armorPlateIt != cfg.modules.modules.end());
|
||||
REQUIRE(armorPlateIt->tooltip.has_value());
|
||||
REQUIRE(*armorPlateIt->tooltip == "Adds a large flat bonus to hit points.");
|
||||
|
||||
const auto salvagerModuleIt = std::find_if(
|
||||
cfg.modules.modules.begin(), cfg.modules.modules.end(),
|
||||
[](const ModuleDef& m) { return m.id == "salvager"; });
|
||||
REQUIRE(salvagerModuleIt != cfg.modules.modules.end());
|
||||
REQUIRE_FALSE(salvagerModuleIt->tooltip.has_value());
|
||||
|
||||
// stations.toml
|
||||
REQUIRE(cfg.stations.playerStation.level == 5);
|
||||
REQUIRE(cfg.stations.playerStation.hpFormula.evaluate(5.0) == Approx(500.0)); // 300 + 40*5
|
||||
|
||||
@@ -42,6 +42,10 @@ BuildButtonGrid::BuildButtonGrid(const GameConfig* config, QWidget* parent)
|
||||
QPushButton* btn = new QPushButton(label, this);
|
||||
btn->setCheckable(true);
|
||||
btn->setFixedHeight(48);
|
||||
if (def.tooltip)
|
||||
{
|
||||
btn->setToolTip(QString::fromStdString(*def.tooltip));
|
||||
}
|
||||
layout->addWidget(btn, row, col);
|
||||
|
||||
const int idx = static_cast<int>(m_buttons.size());
|
||||
|
||||
@@ -461,6 +461,10 @@ ShipLayoutDialog::ShipLayoutDialog(const GameConfig* config,
|
||||
QPushButton* btn = new QPushButton(label, this);
|
||||
btn->setCheckable(true);
|
||||
btn->setFixedHeight(48);
|
||||
if (def.tooltip)
|
||||
{
|
||||
btn->setToolTip(QString::fromStdString(*def.tooltip));
|
||||
}
|
||||
buttonGrid->addWidget(btn, row, col);
|
||||
m_moduleButtons.push_back(btn);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user