From 2c9433cea6d0b00948cde5c895c6fc291f169366 Mon Sep 17 00:00:00 2001 From: Malte Langkabel Date: Tue, 4 Aug 2026 09:27:43 +0200 Subject: [PATCH] move the shared TOML helpers into the utility namespace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The names are generic (makeError, requireInt, parseFile), and at global scope with external linkage they would form an overload set with the same-named anonymous-namespace helpers in VisualsLoader.cpp and BalancingConfig.cpp the moment either file includes TomlHelpers.h — silently, since the signatures differ. Namespacing keeps that door shut. Call sites are qualified explicitly rather than pulled in with a using directive, matching how utility::getRandomInt and friends are already called. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG --- src/lib/config/ConfigLoader.cpp | 10 ++-- src/lib/config/ConfigLoaderBuildings.cpp | 22 +++---- src/lib/config/ConfigLoaderModules.cpp | 52 ++++++++-------- src/lib/config/ConfigLoaderRecipes.cpp | 30 +++++----- src/lib/config/ConfigLoaderShips.cpp | 40 ++++++------- src/lib/config/ConfigLoaderStations.cpp | 32 +++++----- src/lib/config/ConfigLoaderUnlocks.cpp | 20 +++---- src/lib/config/ConfigLoaderWorld.cpp | 76 ++++++++++++------------ src/lib/config/TomlHelpers.cpp | 5 ++ src/lib/config/TomlHelpers.h | 9 +++ 10 files changed, 155 insertions(+), 141 deletions(-) diff --git a/src/lib/config/ConfigLoader.cpp b/src/lib/config/ConfigLoader.cpp index 522921f..51fedbf 100644 --- a/src/lib/config/ConfigLoader.cpp +++ b/src/lib/config/ConfigLoader.cpp @@ -46,11 +46,11 @@ void validateUnlocks(const GameConfig& cfg) { if (valid.count(id) == 0) { - throw makeError(file, gPath, "grants unknown " + kind + " '" + id + "'"); + throw utility::makeError(file, gPath, "grants unknown " + kind + " '" + id + "'"); } if (!granted.insert(id).second) { - throw makeError(file, gPath, + throw utility::makeError(file, gPath, "grants " + kind + " '" + id + "' which is already granted by another unlock group"); } } @@ -61,13 +61,13 @@ void validateUnlocks(const GameConfig& cfg) const std::string gPath = "unlock '" + group.id + "'"; if (!groupIds.insert(group.id).second) { - throw makeError(file, gPath, "duplicate unlock group id"); + throw utility::makeError(file, gPath, "duplicate unlock group id"); } if (group.ships.empty() && group.modules.empty() && group.buildings.empty() && group.recipes.empty()) { - throw makeError(file, gPath, "grants no items (must grant at least one)"); + throw utility::makeError(file, gPath, "grants no items (must grant at least one)"); } checkGrants(group.ships, shipIds, grantedShipIds, "ship", gPath); @@ -83,7 +83,7 @@ void validateUnlocks(const GameConfig& cfg) { if (groupIds.count(req) == 0) { - throw makeError(file, "unlock '" + group.id + "'.requires", + throw utility::makeError(file, "unlock '" + group.id + "'.requires", "references unknown unlock group '" + req + "'"); } } diff --git a/src/lib/config/ConfigLoaderBuildings.cpp b/src/lib/config/ConfigLoaderBuildings.cpp index 1c17a23..cfa3dbf 100644 --- a/src/lib/config/ConfigLoaderBuildings.cpp +++ b/src/lib/config/ConfigLoaderBuildings.cpp @@ -11,10 +11,10 @@ BuildingsConfig ConfigLoader::loadBuildings(const std::string& path) { const std::string file = "buildings.toml"; - toml::table tbl = parseFile(path, file); + toml::table tbl = utility::parseFile(path, file); BuildingsConfig cfg; - const toml::array& arr = requireArray(tbl["building"], file, "building"); + const toml::array& arr = utility::requireArray(tbl["building"], file, "building"); for (std::size_t i = 0; i < arr.size(); ++i) { @@ -22,32 +22,32 @@ BuildingsConfig ConfigLoader::loadBuildings(const std::string& path) const toml::table* bt = arr[i].as_table(); if (bt == nullptr) { - throw makeError(file, elemPath, "not a table"); + throw utility::makeError(file, elemPath, "not a table"); } toml::table& mt = const_cast(*bt); BuildingDef def; - def.id = requireString(mt["id"], file, elemPath + ".id"); - def.cost = static_cast(requireInt(mt["cost"], file, elemPath + ".cost")); - def.playerPlaceable = requireBool(mt["player_placeable"], file, elemPath + ".player_placeable"); - def.constructionTimeSeconds = requireDouble(mt["construction_time_seconds"], file, elemPath + ".construction_time_seconds"); - def.surfaceMask = requireStringArray(mt["surface_mask"], file, elemPath + ".surface_mask"); + def.id = utility::requireString(mt["id"], file, elemPath + ".id"); + def.cost = static_cast(utility::requireInt(mt["cost"], file, elemPath + ".cost")); + def.playerPlaceable = utility::requireBool(mt["player_placeable"], file, elemPath + ".player_placeable"); + def.constructionTimeSeconds = utility::requireDouble(mt["construction_time_seconds"], file, elemPath + ".construction_time_seconds"); + def.surfaceMask = utility::requireStringArray(mt["surface_mask"], file, elemPath + ".surface_mask"); if (mt.contains("output_buffer_capacity")) { def.outputBufferCapacity = static_cast( - requireInt(mt["output_buffer_capacity"], file, elemPath + ".output_buffer_capacity")); + utility::requireInt(mt["output_buffer_capacity"], file, elemPath + ".output_buffer_capacity")); } if (mt.contains("tooltip")) { - def.tooltip = requireString(mt["tooltip"], file, elemPath + ".tooltip"); + def.tooltip = utility::requireString(mt["tooltip"], file, elemPath + ".tooltip"); } const std::optional parsedType = parseBuildingType(def.id); if (!parsedType) { - throw makeError(file, elemPath + ".id", "unknown building id '" + def.id + "'"); + throw utility::makeError(file, elemPath + ".id", "unknown building id '" + def.id + "'"); } def.type = *parsedType; diff --git a/src/lib/config/ConfigLoaderModules.cpp b/src/lib/config/ConfigLoaderModules.cpp index f93caa3..3c19c65 100644 --- a/src/lib/config/ConfigLoaderModules.cpp +++ b/src/lib/config/ConfigLoaderModules.cpp @@ -41,7 +41,7 @@ static const StatEntry kKnownStats[] = { ModulesConfig ConfigLoader::loadModules(const std::string& path) { const std::string file = "modules.toml"; - toml::table tbl = parseFile(path, file); + toml::table tbl = utility::parseFile(path, file); ModulesConfig cfg; @@ -50,7 +50,7 @@ ModulesConfig ConfigLoader::loadModules(const std::string& path) return cfg; } - const toml::array& arr = requireArray(tbl["module"], file, "module"); + const toml::array& arr = utility::requireArray(tbl["module"], file, "module"); for (std::size_t i = 0; i < arr.size(); ++i) { @@ -58,27 +58,27 @@ ModulesConfig ConfigLoader::loadModules(const std::string& path) const toml::table* st = arr[i].as_table(); if (st == nullptr) { - throw makeError(file, elemPath, "not a table"); + throw utility::makeError(file, elemPath, "not a table"); } toml::table& mt = const_cast(*st); ModuleDef def; - def.id = requireString(mt["id"], file, elemPath + ".id"); - def.surfaceMask = requireStringArray(mt["surface_mask"], file, elemPath + ".surface_mask"); - def.productionTimeSeconds = requireDouble( + def.id = utility::requireString(mt["id"], file, elemPath + ".id"); + def.surfaceMask = utility::requireStringArray(mt["surface_mask"], file, elemPath + ".surface_mask"); + def.productionTimeSeconds = utility::requireDouble( mt["production_time_seconds"], file, elemPath + ".production_time_seconds"); - def.fillColor = requireString(mt["fill_color"], file, elemPath + ".fill_color"); - def.glyph = requireString(mt["glyph"], file, elemPath + ".glyph"); + def.fillColor = utility::requireString(mt["fill_color"], file, elemPath + ".fill_color"); + def.glyph = utility::requireString(mt["glyph"], file, elemPath + ".glyph"); if (mt.contains("tooltip")) { - def.tooltip = requireString(mt["tooltip"], file, elemPath + ".tooltip"); + def.tooltip = utility::requireString(mt["tooltip"], file, elemPath + ".tooltip"); } // Materials { - const toml::array& materials = requireArray(mt["materials"], file, elemPath + ".materials"); - def.materials = parseIngredients(materials, file, elemPath + ".materials"); + const toml::array& materials = utility::requireArray(mt["materials"], file, elemPath + ".materials"); + def.materials = utility::parseIngredients(materials, file, elemPath + ".materials"); } // Stat modifiers from [module.] sub-tables @@ -88,7 +88,7 @@ ModulesConfig ConfigLoader::loadModules(const std::string& path) { continue; } - const toml::table& catTable = requireTable(mt[se.category], file, + const toml::table& catTable = utility::requireTable(mt[se.category], file, elemPath + "." + se.category); toml::table& catMt = const_cast(catTable); @@ -100,7 +100,7 @@ ModulesConfig ConfigLoader::loadModules(const std::string& path) ModuleStatModifier mod; mod.stat = se.stat; mod.modifierType = "additive"; - mod.value = requireDouble(catMt[addedKey], file, + mod.value = utility::requireDouble(catMt[addedKey], file, elemPath + "." + se.category + "." + addedKey); def.statModifiers.push_back(std::move(mod)); } @@ -110,7 +110,7 @@ ModulesConfig ConfigLoader::loadModules(const std::string& path) ModuleStatModifier mod; mod.stat = se.stat; mod.modifierType = "multiplicative"; - mod.value = requireDouble(catMt[multipliedKey], file, + mod.value = utility::requireDouble(catMt[multipliedKey], file, elemPath + "." + se.category + "." + multipliedKey); def.statModifiers.push_back(std::move(mod)); } @@ -120,17 +120,17 @@ ModulesConfig ConfigLoader::loadModules(const std::string& path) if (mt.contains("weapon")) { const std::string wPath = elemPath + ".weapon"; - const toml::table& wTable = requireTable(mt["weapon"], file, wPath); + const toml::table& wTable = utility::requireTable(mt["weapon"], file, wPath); toml::table& wMt = const_cast(wTable); if (wMt.contains("damage") || wMt.contains("attack_range_m") || wMt.contains("attack_rate_hz")) { ModuleWeaponCapability cap; - cap.damage = static_cast(requireDouble(wMt["damage"], + cap.damage = static_cast(utility::requireDouble(wMt["damage"], file, wPath + ".damage")); - cap.attackRange_m = static_cast(requireDouble(wMt["attack_range_m"], + cap.attackRange_m = static_cast(utility::requireDouble(wMt["attack_range_m"], file, wPath + ".attack_range_m")); - cap.attackRate_hz = static_cast(requireDouble(wMt["attack_rate_hz"], + cap.attackRate_hz = static_cast(utility::requireDouble(wMt["attack_rate_hz"], file, wPath + ".attack_rate_hz")); def.weaponCapability = std::move(cap); } @@ -140,17 +140,17 @@ ModulesConfig ConfigLoader::loadModules(const std::string& path) if (mt.contains("salvage")) { const std::string sPath = elemPath + ".salvage"; - const toml::table& sTable = requireTable(mt["salvage"], file, sPath); + const toml::table& sTable = utility::requireTable(mt["salvage"], file, sPath); toml::table& sMt = const_cast(sTable); if (sMt.contains("collection_range_m") || sMt.contains("cargo_capacity") || sMt.contains("collection_rate_hz")) { ModuleSalvageCapability cap; - cap.collectionRange_m = static_cast(requireDouble(sMt["collection_range_m"], + cap.collectionRange_m = static_cast(utility::requireDouble(sMt["collection_range_m"], file, sPath + ".collection_range_m")); - cap.cargoCapacity = static_cast(requireDouble(sMt["cargo_capacity"], + cap.cargoCapacity = static_cast(utility::requireDouble(sMt["cargo_capacity"], file, sPath + ".cargo_capacity")); - cap.collectionRate_hz = static_cast(requireDouble(sMt["collection_rate_hz"], + cap.collectionRate_hz = static_cast(utility::requireDouble(sMt["collection_rate_hz"], file, sPath + ".collection_rate_hz")); def.salvageCapability = std::move(cap); } @@ -160,16 +160,16 @@ ModulesConfig ConfigLoader::loadModules(const std::string& path) if (mt.contains("repair")) { const std::string rPath = elemPath + ".repair"; - const toml::table& rTable = requireTable(mt["repair"], file, rPath); + const toml::table& rTable = utility::requireTable(mt["repair"], file, rPath); toml::table& rMt = const_cast(rTable); if (rMt.contains("repair_rate_hz") || rMt.contains("repair_range_m")) { ModuleRepairCapability cap; - cap.repairRate_hz = static_cast(requireDouble(rMt["repair_rate_hz"], + cap.repairRate_hz = static_cast(utility::requireDouble(rMt["repair_rate_hz"], file, rPath + ".repair_rate_hz")); - cap.repairAmountHp = static_cast(requireDouble(rMt["repair_amount_hp"], + cap.repairAmountHp = static_cast(utility::requireDouble(rMt["repair_amount_hp"], file, rPath + ".repair_amount_hp")); - cap.repairRange_m = static_cast(requireDouble(rMt["repair_range_m"], + cap.repairRange_m = static_cast(utility::requireDouble(rMt["repair_range_m"], file, rPath + ".repair_range_m")); def.repairCapability = std::move(cap); } diff --git a/src/lib/config/ConfigLoaderRecipes.cpp b/src/lib/config/ConfigLoaderRecipes.cpp index c184aa1..29ea3a3 100644 --- a/src/lib/config/ConfigLoaderRecipes.cpp +++ b/src/lib/config/ConfigLoaderRecipes.cpp @@ -25,13 +25,13 @@ std::vector parseRecipeOutputs(const toml::array& arr, const toml::table* t = arr[i].as_table(); if (t == nullptr) { - throw makeError(file, elemPath, "not a table"); + throw utility::makeError(file, elemPath, "not a table"); } toml::table& mt = const_cast(*t); RecipeOutput out; - out.item = requireString(mt["item"], file, elemPath + ".item"); - out.amount = static_cast(requireInt(mt["amount"], file, elemPath + ".amount")); + out.item = utility::requireString(mt["item"], file, elemPath + ".item"); + out.amount = static_cast(utility::requireInt(mt["amount"], file, elemPath + ".amount")); if (const std::optional p = mt["probability"].value()) { out.probability = *p; @@ -50,10 +50,10 @@ std::vector parseRecipeOutputs(const toml::array& arr, RecipesConfig ConfigLoader::loadRecipes(const std::string& path) { const std::string file = "recipes.toml"; - toml::table tbl = parseFile(path, file); + toml::table tbl = utility::parseFile(path, file); RecipesConfig cfg; - const toml::array& arr = requireArray(tbl["recipe"], file, "recipe"); + const toml::array& arr = utility::requireArray(tbl["recipe"], file, "recipe"); for (std::size_t i = 0; i < arr.size(); ++i) { @@ -61,37 +61,37 @@ RecipesConfig ConfigLoader::loadRecipes(const std::string& path) const toml::table* rt = arr[i].as_table(); if (rt == nullptr) { - throw makeError(file, elemPath, "not a table"); + throw utility::makeError(file, elemPath, "not a table"); } toml::table& mt = const_cast(*rt); RecipeDef def; - def.id = requireString(mt["id"], file, elemPath + ".id"); - def.durationSeconds = requireDouble(mt["duration_seconds"], file, elemPath + ".duration_seconds"); + def.id = utility::requireString(mt["id"], file, elemPath + ".id"); + def.durationSeconds = utility::requireDouble(mt["duration_seconds"], file, elemPath + ".duration_seconds"); - const std::string buildingId = requireString(mt["building"], file, elemPath + ".building"); + const std::string buildingId = utility::requireString(mt["building"], file, elemPath + ".building"); const std::optional parsedType = parseBuildingType(buildingId); if (!parsedType) { - throw makeError(file, elemPath + ".building", + throw utility::makeError(file, elemPath + ".building", "unknown building id '" + buildingId + "'"); } def.building = *parsedType; if (def.building == BuildingType::Assembler && mt.contains("unlocked_at_start")) { - def.unlockedAtStart = requireBool(mt["unlocked_at_start"], file, + def.unlockedAtStart = utility::requireBool(mt["unlocked_at_start"], file, elemPath + ".unlocked_at_start"); } // inputs may be omitted (e.g. miner recipes). An empty array is fine. if (mt.contains("inputs")) { - const toml::array& inputs = requireArray(mt["inputs"], file, elemPath + ".inputs"); - def.inputs = parseIngredients(inputs, file, elemPath + ".inputs"); + const toml::array& inputs = utility::requireArray(mt["inputs"], file, elemPath + ".inputs"); + def.inputs = utility::parseIngredients(inputs, file, elemPath + ".inputs"); } - const toml::array& outputs = requireArray(mt["outputs"], file, elemPath + ".outputs"); + const toml::array& outputs = utility::requireArray(mt["outputs"], file, elemPath + ".outputs"); def.outputs = parseRecipeOutputs(outputs, file, elemPath + ".outputs"); // Optional icon item id (REQ-UI-RECIPE-ICON); defaults to the first output @@ -99,7 +99,7 @@ RecipesConfig ConfigLoader::loadRecipes(const std::string& path) // icon is not an error (REQ-UI-ITEM-ICON). if (mt.contains("icon")) { - def.icon = requireString(mt["icon"], file, elemPath + ".icon"); + def.icon = utility::requireString(mt["icon"], file, elemPath + ".icon"); } cfg.recipes.push_back(std::move(def)); diff --git a/src/lib/config/ConfigLoaderShips.cpp b/src/lib/config/ConfigLoaderShips.cpp index 41557b9..0d2fc6e 100644 --- a/src/lib/config/ConfigLoaderShips.cpp +++ b/src/lib/config/ConfigLoaderShips.cpp @@ -58,10 +58,10 @@ std::vector parsePlacedModules(const toml::array& arr, ShipsConfig ConfigLoader::loadShips(const std::string& path) { const std::string file = "ships.toml"; - toml::table tbl = parseFile(path, file); + toml::table tbl = utility::parseFile(path, file); ShipsConfig cfg; - const toml::array& arr = requireArray(tbl["ship"], file, "ship"); + const toml::array& arr = utility::requireArray(tbl["ship"], file, "ship"); for (std::size_t i = 0; i < arr.size(); ++i) { @@ -69,58 +69,58 @@ ShipsConfig ConfigLoader::loadShips(const std::string& path) const toml::table* st = arr[i].as_table(); if (st == nullptr) { - throw makeError(file, elemPath, "not a table"); + throw utility::makeError(file, elemPath, "not a table"); } toml::table& mt = const_cast(*st); ShipDef def; - def.id = requireString(mt["id"], file, elemPath + ".id"); - def.layout = requireStringArray(mt["layout"], file, elemPath + ".layout"); + def.id = utility::requireString(mt["id"], file, elemPath + ".id"); + def.layout = utility::requireStringArray(mt["layout"], file, elemPath + ".layout"); // Schematic { const std::string bpPath = elemPath + ".schematic"; - const toml::table& bpTable = requireTable(mt["schematic"], file, bpPath); + const toml::table& bpTable = utility::requireTable(mt["schematic"], file, bpPath); toml::table& bpMt = const_cast(bpTable); - const toml::array& materials = requireArray(bpMt["materials"], file, bpPath + ".materials"); - def.schematic.materials = parseIngredients(materials, file, bpPath + ".materials"); - def.schematic.productionTimeSeconds = requireDouble( + const toml::array& materials = utility::requireArray(bpMt["materials"], file, bpPath + ".materials"); + def.schematic.materials = utility::parseIngredients(materials, file, bpPath + ".materials"); + def.schematic.productionTimeSeconds = utility::requireDouble( bpMt["production_time_seconds"], file, bpPath + ".production_time_seconds"); } // Health { const std::string hPath = elemPath + ".health"; - const toml::table& hTable = requireTable(mt["health"], file, hPath); + const toml::table& hTable = utility::requireTable(mt["health"], file, hPath); toml::table& hMt = const_cast(hTable); - def.health.hp = static_cast(requireDouble(hMt["hp"], file, hPath + ".hp")); + def.health.hp = static_cast(utility::requireDouble(hMt["hp"], file, hPath + ".hp")); } // Movement { const std::string mPath = elemPath + ".movement"; - const toml::table& mTable = requireTable(mt["movement"], file, mPath); + const toml::table& mTable = utility::requireTable(mt["movement"], file, mPath); toml::table& mMt = const_cast(mTable); - def.movement.speed_mps = static_cast(requireDouble(mMt["speed_mps"], file, mPath + ".speed_mps")); - def.movement.mainAcceleration_mpss = static_cast(requireDouble(mMt["main_acceleration_mpss"], file, mPath + ".main_acceleration_mpss")); - def.movement.maneuveringAcceleration_mpss = static_cast(requireDouble(mMt["maneuvering_acceleration_mpss"], file, mPath + ".maneuvering_acceleration_mpss")); - def.movement.angularAcceleration_radpss = static_cast(requireDouble(mMt["angular_acceleration_radpss"], file, mPath + ".angular_acceleration_radpss")); - def.movement.maxRotationSpeed_radps = static_cast(requireDouble(mMt["max_rotation_speed_radps"], file, mPath + ".max_rotation_speed_radps")); + def.movement.speed_mps = static_cast(utility::requireDouble(mMt["speed_mps"], file, mPath + ".speed_mps")); + def.movement.mainAcceleration_mpss = static_cast(utility::requireDouble(mMt["main_acceleration_mpss"], file, mPath + ".main_acceleration_mpss")); + def.movement.maneuveringAcceleration_mpss = static_cast(utility::requireDouble(mMt["maneuvering_acceleration_mpss"], file, mPath + ".maneuvering_acceleration_mpss")); + def.movement.angularAcceleration_radpss = static_cast(utility::requireDouble(mMt["angular_acceleration_radpss"], file, mPath + ".angular_acceleration_radpss")); + def.movement.maxRotationSpeed_radps = static_cast(utility::requireDouble(mMt["max_rotation_speed_radps"], file, mPath + ".max_rotation_speed_radps")); } // Sensor { const std::string snsPath = elemPath + ".sensor"; - const toml::table& snsTable = requireTable(mt["sensor"], file, snsPath); + const toml::table& snsTable = utility::requireTable(mt["sensor"], file, snsPath); toml::table& snsMt = const_cast(snsTable); - def.sensor.sensorRange_m = static_cast(requireDouble(snsMt["sensor_range_m"], file, snsPath + ".sensor_range_m")); + def.sensor.sensorRange_m = static_cast(utility::requireDouble(snsMt["sensor_range_m"], file, snsPath + ".sensor_range_m")); } // Optional: default_modules (REQ-WAV-DEFAULT-MODULES) if (mt.contains("default_modules")) { - const toml::array& modArr = requireArray(mt["default_modules"], file, + const toml::array& modArr = utility::requireArray(mt["default_modules"], file, elemPath + ".default_modules"); def.defaultModules = parsePlacedModules(modArr, file, elemPath + ".default_modules"); diff --git a/src/lib/config/ConfigLoaderStations.cpp b/src/lib/config/ConfigLoaderStations.cpp index fcf4e6e..058462e 100644 --- a/src/lib/config/ConfigLoaderStations.cpp +++ b/src/lib/config/ConfigLoaderStations.cpp @@ -9,38 +9,38 @@ StationsConfig ConfigLoader::loadStations(const std::string& path) { const std::string file = "stations.toml"; - toml::table tbl = parseFile(path, file); + toml::table tbl = utility::parseFile(path, file); StationsConfig cfg; // HQ { const std::string p = "hq"; - cfg.hq.surfaceMask = requireStringArray(tbl[p]["surface_mask"], file, p + ".surface_mask"); - cfg.hq.hpFormula = requireFormula(tbl[p]["hp_formula"], file, p + ".hp_formula"); + cfg.hq.surfaceMask = utility::requireStringArray(tbl[p]["surface_mask"], file, p + ".surface_mask"); + cfg.hq.hpFormula = utility::requireFormula(tbl[p]["hp_formula"], file, p + ".hp_formula"); } // Player station { const std::string p = "player_station"; - cfg.playerStation.surfaceMask = requireStringArray(tbl[p]["surface_mask"], file, p + ".surface_mask"); - cfg.playerStation.level = static_cast(requireInt(tbl[p]["level"], file, p + ".level")); - cfg.playerStation.hpFormula = requireFormula(tbl[p]["hp_formula"], file, p + ".hp_formula"); - cfg.playerStation.damageFormula = requireFormula(tbl[p]["damage_formula"], file, p + ".damage_formula"); - cfg.playerStation.rangeFormula = requireFormula(tbl[p]["range_m_formula"], file, p + ".range_m_formula"); - cfg.playerStation.fireRateFormula = requireFormula(tbl[p]["fire_rate_hz_formula"], file, p + ".fire_rate_hz_formula"); - cfg.playerStation.scrapDropFormula = requireFormula(tbl[p]["scrap_drop_formula"], file, p + ".scrap_drop_formula"); + cfg.playerStation.surfaceMask = utility::requireStringArray(tbl[p]["surface_mask"], file, p + ".surface_mask"); + cfg.playerStation.level = static_cast(utility::requireInt(tbl[p]["level"], file, p + ".level")); + cfg.playerStation.hpFormula = utility::requireFormula(tbl[p]["hp_formula"], file, p + ".hp_formula"); + cfg.playerStation.damageFormula = utility::requireFormula(tbl[p]["damage_formula"], file, p + ".damage_formula"); + cfg.playerStation.rangeFormula = utility::requireFormula(tbl[p]["range_m_formula"], file, p + ".range_m_formula"); + cfg.playerStation.fireRateFormula = utility::requireFormula(tbl[p]["fire_rate_hz_formula"], file, p + ".fire_rate_hz_formula"); + cfg.playerStation.scrapDropFormula = utility::requireFormula(tbl[p]["scrap_drop_formula"], file, p + ".scrap_drop_formula"); } // Enemy station { const std::string p = "enemy_station"; - cfg.enemyStation.surfaceMask = requireStringArray(tbl[p]["surface_mask"], file, p + ".surface_mask"); - cfg.enemyStation.hpFormula = requireFormula(tbl[p]["hp_formula"], file, p + ".hp_formula"); - cfg.enemyStation.damageFormula = requireFormula(tbl[p]["damage_formula"], file, p + ".damage_formula"); - cfg.enemyStation.rangeFormula = requireFormula(tbl[p]["range_m_formula"], file, p + ".range_m_formula"); - cfg.enemyStation.fireRateFormula = requireFormula(tbl[p]["fire_rate_hz_formula"], file, p + ".fire_rate_hz_formula"); - cfg.enemyStation.scrapDropFormula = requireFormula(tbl[p]["scrap_drop_formula"], file, p + ".scrap_drop_formula"); + cfg.enemyStation.surfaceMask = utility::requireStringArray(tbl[p]["surface_mask"], file, p + ".surface_mask"); + cfg.enemyStation.hpFormula = utility::requireFormula(tbl[p]["hp_formula"], file, p + ".hp_formula"); + cfg.enemyStation.damageFormula = utility::requireFormula(tbl[p]["damage_formula"], file, p + ".damage_formula"); + cfg.enemyStation.rangeFormula = utility::requireFormula(tbl[p]["range_m_formula"], file, p + ".range_m_formula"); + cfg.enemyStation.fireRateFormula = utility::requireFormula(tbl[p]["fire_rate_hz_formula"], file, p + ".fire_rate_hz_formula"); + cfg.enemyStation.scrapDropFormula = utility::requireFormula(tbl[p]["scrap_drop_formula"], file, p + ".scrap_drop_formula"); } return cfg; diff --git a/src/lib/config/ConfigLoaderUnlocks.cpp b/src/lib/config/ConfigLoaderUnlocks.cpp index eb6442e..cc8d680 100644 --- a/src/lib/config/ConfigLoaderUnlocks.cpp +++ b/src/lib/config/ConfigLoaderUnlocks.cpp @@ -10,7 +10,7 @@ UnlocksConfig ConfigLoader::loadUnlocks(const std::string& path) { const std::string file = "unlocks.toml"; - toml::table tbl = parseFile(path, file); + toml::table tbl = utility::parseFile(path, file); UnlocksConfig cfg; if (!tbl.contains("unlock")) @@ -18,7 +18,7 @@ UnlocksConfig ConfigLoader::loadUnlocks(const std::string& path) return cfg; } - const toml::array& arr = requireArray(tbl["unlock"], file, "unlock"); + const toml::array& arr = utility::requireArray(tbl["unlock"], file, "unlock"); for (std::size_t i = 0; i < arr.size(); ++i) { @@ -26,33 +26,33 @@ UnlocksConfig ConfigLoader::loadUnlocks(const std::string& path) const toml::table* ut = arr[i].as_table(); if (ut == nullptr) { - throw makeError(file, elemPath, "not a table"); + throw utility::makeError(file, elemPath, "not a table"); } toml::table& mt = const_cast(*ut); UnlockGroupDef def; - def.id = requireString(mt["id"], file, elemPath + ".id"); + def.id = utility::requireString(mt["id"], file, elemPath + ".id"); def.stationLevel = static_cast( - requireInt(mt["station_level"], file, elemPath + ".station_level")); + utility::requireInt(mt["station_level"], file, elemPath + ".station_level")); if (mt.contains("requires")) { - def.requiredGroupIds = requireStringArray(mt["requires"], file, elemPath + ".requires"); + def.requiredGroupIds = utility::requireStringArray(mt["requires"], file, elemPath + ".requires"); } if (mt.contains("ships")) { - def.ships = requireStringArray(mt["ships"], file, elemPath + ".ships"); + def.ships = utility::requireStringArray(mt["ships"], file, elemPath + ".ships"); } if (mt.contains("modules")) { - def.modules = requireStringArray(mt["modules"], file, elemPath + ".modules"); + def.modules = utility::requireStringArray(mt["modules"], file, elemPath + ".modules"); } if (mt.contains("buildings")) { - def.buildings = requireStringArray(mt["buildings"], file, elemPath + ".buildings"); + def.buildings = utility::requireStringArray(mt["buildings"], file, elemPath + ".buildings"); } if (mt.contains("recipes")) { - def.recipes = requireStringArray(mt["recipes"], file, elemPath + ".recipes"); + def.recipes = utility::requireStringArray(mt["recipes"], file, elemPath + ".recipes"); } cfg.groups.push_back(std::move(def)); diff --git a/src/lib/config/ConfigLoaderWorld.cpp b/src/lib/config/ConfigLoaderWorld.cpp index a1a2fd3..4e55b96 100644 --- a/src/lib/config/ConfigLoaderWorld.cpp +++ b/src/lib/config/ConfigLoaderWorld.cpp @@ -10,22 +10,22 @@ WorldConfig ConfigLoader::loadWorld(const std::string& path) { const std::string file = "world.toml"; - toml::table tbl = parseFile(path, file); + toml::table tbl = utility::parseFile(path, file); WorldConfig cfg; - cfg.heightTiles = static_cast(requireInt(tbl["world"]["height_tiles"], file, "world.height_tiles")); - cfg.refundPercentage = static_cast(requireInt(tbl["world"]["refund_percentage"], file, "world.refund_percentage")); - cfg.deconstructionTimeSeconds = requireDouble(tbl["world"]["deconstruction_time_seconds"], file, "world.deconstruction_time_seconds"); - cfg.startingBuildingBlocks = static_cast(requireInt(tbl["world"]["starting_building_blocks"], file, "world.starting_building_blocks")); - cfg.debrisDespawnSeconds = requireDouble(tbl["world"]["debris_despawn_seconds"], file, "world.debris_despawn_seconds"); - cfg.scrapPerThreat = requireDouble(tbl["world"]["scrap_per_threat"], file, "world.scrap_per_threat"); - cfg.tileSize_m = requireDouble(tbl["world"]["tile_size_m"], file, "world.tile_size_m"); - cfg.beltSpeed_tps = requireDouble(tbl["world"]["belt_speed_mps"], file, "world.belt_speed_mps") / cfg.tileSize_m; - cfg.tunnelMaxDistance_tiles = static_cast(requireInt(tbl["world"]["tunnel_max_distance_tiles"], file, "world.tunnel_max_distance_tiles")); - cfg.departureIntervalSeconds = requireDouble(tbl["world"]["departure_interval_seconds"], file, "world.departure_interval_seconds"); - 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"); + cfg.heightTiles = static_cast(utility::requireInt(tbl["world"]["height_tiles"], file, "world.height_tiles")); + cfg.refundPercentage = static_cast(utility::requireInt(tbl["world"]["refund_percentage"], file, "world.refund_percentage")); + cfg.deconstructionTimeSeconds = utility::requireDouble(tbl["world"]["deconstruction_time_seconds"], file, "world.deconstruction_time_seconds"); + cfg.startingBuildingBlocks = static_cast(utility::requireInt(tbl["world"]["starting_building_blocks"], file, "world.starting_building_blocks")); + cfg.debrisDespawnSeconds = utility::requireDouble(tbl["world"]["debris_despawn_seconds"], file, "world.debris_despawn_seconds"); + cfg.scrapPerThreat = utility::requireDouble(tbl["world"]["scrap_per_threat"], file, "world.scrap_per_threat"); + cfg.tileSize_m = utility::requireDouble(tbl["world"]["tile_size_m"], file, "world.tile_size_m"); + cfg.beltSpeed_tps = utility::requireDouble(tbl["world"]["belt_speed_mps"], file, "world.belt_speed_mps") / cfg.tileSize_m; + cfg.tunnelMaxDistance_tiles = static_cast(utility::requireInt(tbl["world"]["tunnel_max_distance_tiles"], file, "world.tunnel_max_distance_tiles")); + cfg.departureIntervalSeconds = utility::requireDouble(tbl["world"]["departure_interval_seconds"], file, "world.departure_interval_seconds"); + cfg.orbitFactor = utility::requireDouble(tbl["world"]["orbit_factor"], file, "world.orbit_factor"); + cfg.rallyOrbitRadius_tiles = utility::requireDouble(tbl["world"]["rally_orbit_radius_tiles"], file, "world.rally_orbit_radius_tiles"); if (const std::optional tip = tbl["world"]["building_blocks_tooltip"].value()) @@ -39,41 +39,41 @@ WorldConfig ConfigLoader::loadWorld(const std::string& path) cfg.artifactTooltip = *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")); - cfg.regions.enemyBufferWidth_tiles = static_cast(requireInt(tbl["regions"]["enemy_buffer_width_tiles"], file, "regions.enemy_buffer_width_tiles")); + cfg.regions.asteroidWidth_tiles = static_cast(utility::requireInt(tbl["regions"]["asteroid_width_tiles"], file, "regions.asteroid_width_tiles")); + cfg.regions.playerBufferWidth_tiles = static_cast(utility::requireInt(tbl["regions"]["player_buffer_width_tiles"], file, "regions.player_buffer_width_tiles")); + cfg.regions.contestZoneWidth_tiles = static_cast(utility::requireInt(tbl["regions"]["contest_zone_width_tiles"], file, "regions.contest_zone_width_tiles")); + cfg.regions.enemyBufferWidth_tiles = static_cast(utility::requireInt(tbl["regions"]["enemy_buffer_width_tiles"], file, "regions.enemy_buffer_width_tiles")); - cfg.expansion.columnsPerExpansion_tiles = static_cast(requireInt(tbl["expansion"]["columns_per_expansion_tiles"], file, "expansion.columns_per_expansion_tiles")); - cfg.expansion.costBuildingBlocksFormula = requireFormula(tbl["expansion"]["cost_building_blocks_formula"], file, "expansion.cost_building_blocks_formula"); + cfg.expansion.columnsPerExpansion_tiles = static_cast(utility::requireInt(tbl["expansion"]["columns_per_expansion_tiles"], file, "expansion.columns_per_expansion_tiles")); + cfg.expansion.costBuildingBlocksFormula = utility::requireFormula(tbl["expansion"]["cost_building_blocks_formula"], file, "expansion.cost_building_blocks_formula"); - cfg.push.pushExpandColumns_tiles = static_cast(requireInt(tbl["push"]["push_expand_columns_tiles"], file, "push.push_expand_columns_tiles")); - cfg.push.bossAdvanceSeconds = requireDouble(tbl["push"]["boss_advance_seconds"], file, "push.boss_advance_seconds"); + cfg.push.pushExpandColumns_tiles = static_cast(utility::requireInt(tbl["push"]["push_expand_columns_tiles"], file, "push.push_expand_columns_tiles")); + cfg.push.bossAdvanceSeconds = utility::requireDouble(tbl["push"]["boss_advance_seconds"], file, "push.boss_advance_seconds"); - cfg.waves.threatRateFormula = requireFormula(tbl["waves"]["threat_rate_formula"], file, "waves.threat_rate_formula"); - cfg.waves.gapMinSeconds = requireDouble(tbl["waves"]["gap_min_seconds"], file, "waves.gap_min_seconds"); - cfg.waves.gapMaxSeconds = requireDouble(tbl["waves"]["gap_max_seconds"], file, "waves.gap_max_seconds"); - cfg.waves.spawnDurationSeconds = requireDouble(tbl["waves"]["spawn_duration_seconds"], file, "waves.spawn_duration_seconds"); - cfg.waves.bossCountdownSeconds = requireDouble(tbl["waves"]["boss_countdown_seconds"], file, "waves.boss_countdown_seconds"); - cfg.waves.bossThreatDurationSeconds = requireDouble(tbl["waves"]["boss_threat_duration_seconds"], file, "waves.boss_threat_duration_seconds"); - cfg.waves.bossQuietBeforeSeconds = requireDouble(tbl["waves"]["boss_quiet_before_seconds"], file, "waves.boss_quiet_before_seconds"); - cfg.waves.bossQuietAfterSeconds = requireDouble(tbl["waves"]["boss_quiet_after_seconds"], file, "waves.boss_quiet_after_seconds"); + cfg.waves.threatRateFormula = utility::requireFormula(tbl["waves"]["threat_rate_formula"], file, "waves.threat_rate_formula"); + cfg.waves.gapMinSeconds = utility::requireDouble(tbl["waves"]["gap_min_seconds"], file, "waves.gap_min_seconds"); + cfg.waves.gapMaxSeconds = utility::requireDouble(tbl["waves"]["gap_max_seconds"], file, "waves.gap_max_seconds"); + cfg.waves.spawnDurationSeconds = utility::requireDouble(tbl["waves"]["spawn_duration_seconds"], file, "waves.spawn_duration_seconds"); + cfg.waves.bossCountdownSeconds = utility::requireDouble(tbl["waves"]["boss_countdown_seconds"], file, "waves.boss_countdown_seconds"); + cfg.waves.bossThreatDurationSeconds = utility::requireDouble(tbl["waves"]["boss_threat_duration_seconds"], file, "waves.boss_threat_duration_seconds"); + cfg.waves.bossQuietBeforeSeconds = utility::requireDouble(tbl["waves"]["boss_quiet_before_seconds"], file, "waves.boss_quiet_before_seconds"); + cfg.waves.bossQuietAfterSeconds = utility::requireDouble(tbl["waves"]["boss_quiet_after_seconds"], file, "waves.boss_quiet_after_seconds"); if (cfg.waves.gapMinSeconds > cfg.waves.gapMaxSeconds) { - throw makeError(file, "waves", "gap_min_seconds > gap_max_seconds"); + throw utility::makeError(file, "waves", "gap_min_seconds > gap_max_seconds"); } - cfg.targeting.targetScoreFormula = requireFormula(tbl["targeting"]["target_score_formula"], file, "targeting.target_score_formula"); - cfg.targeting.overclaimPenaltyFormula = requireFormula(tbl["targeting"]["overclaim_penalty_formula"], file, "targeting.overclaim_penalty_formula"); - cfg.targeting.hysteresis = requireDouble(tbl["targeting"]["target_hysteresis"], file, "targeting.target_hysteresis"); + cfg.targeting.targetScoreFormula = utility::requireFormula(tbl["targeting"]["target_score_formula"], file, "targeting.target_score_formula"); + cfg.targeting.overclaimPenaltyFormula = utility::requireFormula(tbl["targeting"]["overclaim_penalty_formula"], file, "targeting.overclaim_penalty_formula"); + cfg.targeting.hysteresis = utility::requireDouble(tbl["targeting"]["target_hysteresis"], file, "targeting.target_hysteresis"); - cfg.artifacts.artifactChanceFormula = requireFormula(tbl["artifacts"]["artifact_chance_formula"], file, "artifacts.artifact_chance_formula"); - cfg.artifacts.artifactWinCount = static_cast(requireInt(tbl["artifacts"]["artifact_win_count"], file, "artifacts.artifact_win_count")); + cfg.artifacts.artifactChanceFormula = utility::requireFormula(tbl["artifacts"]["artifact_chance_formula"], file, "artifacts.artifact_chance_formula"); + cfg.artifacts.artifactWinCount = static_cast(utility::requireInt(tbl["artifacts"]["artifact_win_count"], file, "artifacts.artifact_win_count")); - cfg.scroll.panSpeedSlow_tps = requireDouble(tbl["scroll"]["pan_speed_slow_tiles_per_second"], file, "scroll.pan_speed_slow_tiles_per_second"); - cfg.scroll.panSpeedFast_tps = requireDouble(tbl["scroll"]["pan_speed_fast_tiles_per_second"], file, "scroll.pan_speed_fast_tiles_per_second"); - cfg.scroll.panRampBandWidth_tiles = static_cast(requireInt(tbl["scroll"]["pan_ramp_band_width_tiles"], file, "scroll.pan_ramp_band_width_tiles")); + cfg.scroll.panSpeedSlow_tps = utility::requireDouble(tbl["scroll"]["pan_speed_slow_tiles_per_second"], file, "scroll.pan_speed_slow_tiles_per_second"); + cfg.scroll.panSpeedFast_tps = utility::requireDouble(tbl["scroll"]["pan_speed_fast_tiles_per_second"], file, "scroll.pan_speed_fast_tiles_per_second"); + cfg.scroll.panRampBandWidth_tiles = static_cast(utility::requireInt(tbl["scroll"]["pan_ramp_band_width_tiles"], file, "scroll.pan_ramp_band_width_tiles")); return cfg; } diff --git a/src/lib/config/TomlHelpers.cpp b/src/lib/config/TomlHelpers.cpp index 0a3f3d5..81644f4 100644 --- a/src/lib/config/TomlHelpers.cpp +++ b/src/lib/config/TomlHelpers.cpp @@ -3,6 +3,9 @@ #include #include +namespace utility +{ + // --- Error helpers -------------------------------------------------------- std::runtime_error makeError(const std::string& file, @@ -165,3 +168,5 @@ toml::table parseFile(const std::string& path, const std::string& file) throw std::runtime_error(oss.str()); } } + +} // namespace utility diff --git a/src/lib/config/TomlHelpers.h b/src/lib/config/TomlHelpers.h index 39d3492..35dc8b6 100644 --- a/src/lib/config/TomlHelpers.h +++ b/src/lib/config/TomlHelpers.h @@ -13,6 +13,13 @@ // Shared TOML-parsing helpers used by two or more ConfigLoader per-domain // loaders. Helpers used by exactly one domain stay local to that domain's // .cpp file instead. +// +// Namespaced because the names are generic: VisualsLoader.cpp and +// BalancingConfig.cpp each have their own same-named helpers in anonymous +// namespaces, and unqualified globals here would form an overload set with +// them the moment either file includes this header. +namespace utility +{ // --- Error helpers ---------------------------------------------------------- @@ -59,3 +66,5 @@ std::vector parseIngredients(const toml::array& arr, const std::string& path); toml::table parseFile(const std::string& path, const std::string& file); + +} // namespace utility