From bf579bb76ecd2a2e77bf1a7f3c2c4016ef142dcb Mon Sep 17 00:00:00 2001 From: Malte Langkabel Date: Mon, 3 Aug 2026 22:36:08 +0200 Subject: [PATCH] extract loadModules into ConfigLoaderModules.cpp Continues the ConfigLoader.cpp domain split. StatEntry/kKnownStats are only used by loadModules, so they move along as a domain-local anonymous-namespace table rather than into TomlHelpers. Pure move; no logic change. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG --- src/lib/config/CMakeLists.txt | 1 + src/lib/config/ConfigLoader.cpp | 169 ----------------------- src/lib/config/ConfigLoaderModules.cpp | 182 +++++++++++++++++++++++++ 3 files changed, 183 insertions(+), 169 deletions(-) create mode 100644 src/lib/config/ConfigLoaderModules.cpp diff --git a/src/lib/config/CMakeLists.txt b/src/lib/config/CMakeLists.txt index 268f3a8..7dbd62f 100644 --- a/src/lib/config/CMakeLists.txt +++ b/src/lib/config/CMakeLists.txt @@ -26,6 +26,7 @@ SET(SRCS ${CMAKE_CURRENT_SOURCE_DIR}/ConfigLoaderRecipes.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ConfigLoaderShips.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ConfigLoaderStations.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ConfigLoaderModules.cpp ${CMAKE_CURRENT_SOURCE_DIR}/SurfaceMask.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BlueprintSerializer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ShipLayoutBlueprintSerializer.cpp diff --git a/src/lib/config/ConfigLoader.cpp b/src/lib/config/ConfigLoader.cpp index 613eb88..9558616 100644 --- a/src/lib/config/ConfigLoader.cpp +++ b/src/lib/config/ConfigLoader.cpp @@ -13,175 +13,6 @@ // --- Per-file loaders ----------------------------------------------------- -// Known category→stat mappings for module stat modifier discovery. -// addedKeySuffix: unit suffix appended before "_formula" for additive modifier keys only. -// Multiplicative modifier keys are always dimensionless and carry no suffix. -struct StatEntry -{ - const char* category; - const char* stat; - const char* addedKeySuffix; -}; - -static const StatEntry kKnownStats[] = { - {"health", "hp", ""}, - {"movement", "speed", "_mps"}, - {"movement", "main_acceleration", "_mpss"}, - {"movement", "maneuvering_acceleration", "_mpss"}, - {"sensor", "sensor_range", "_m"}, - {"weapon", "damage", ""}, - {"weapon", "attack_range", "_m"}, - {"weapon", "attack_rate", "_hz"}, - {"salvage", "collection_range", "_m"}, - {"salvage", "collection_rate", "_hz"}, - {"cargo", "cargo_capacity", ""}, - {"repair", "repair_rate", "_hz"}, - {"repair", "repair_range", "_m"}, -}; - -ModulesConfig ConfigLoader::loadModules(const std::string& path) -{ - const std::string file = "modules.toml"; - toml::table tbl = parseFile(path, file); - - ModulesConfig cfg; - - if (!tbl.contains("module")) - { - return cfg; - } - - const toml::array& arr = requireArray(tbl["module"], file, "module"); - - for (std::size_t i = 0; i < arr.size(); ++i) - { - const std::string elemPath = "module[" + std::to_string(i) + "]"; - const toml::table* st = arr[i].as_table(); - if (st == nullptr) - { - throw 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( - 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"); - - if (mt.contains("tooltip")) - { - def.tooltip = requireString(mt["tooltip"], file, elemPath + ".tooltip"); - } - - // Materials - { - const toml::array& materials = requireArray(mt["materials"], file, elemPath + ".materials"); - def.materials = parseIngredients(materials, file, elemPath + ".materials"); - } - - // Stat modifiers from [module.] sub-tables - for (const StatEntry& se : kKnownStats) - { - if (!mt.contains(se.category)) - { - continue; - } - const toml::table& catTable = requireTable(mt[se.category], file, - elemPath + "." + se.category); - toml::table& catMt = const_cast(catTable); - - const std::string addedKey = std::string("added_") + se.stat + se.addedKeySuffix; - const std::string multipliedKey = std::string("multiplied_") + se.stat + se.addedKeySuffix; - - if (catMt.contains(addedKey)) - { - ModuleStatModifier mod; - mod.stat = se.stat; - mod.modifierType = "additive"; - mod.value = requireDouble(catMt[addedKey], file, - elemPath + "." + se.category + "." + addedKey); - def.statModifiers.push_back(std::move(mod)); - } - - if (catMt.contains(multipliedKey)) - { - ModuleStatModifier mod; - mod.stat = se.stat; - mod.modifierType = "multiplicative"; - mod.value = requireDouble(catMt[multipliedKey], file, - elemPath + "." + se.category + "." + multipliedKey); - def.statModifiers.push_back(std::move(mod)); - } - } - - // Weapon capability section: [module.weapon] with base stat formulas - if (mt.contains("weapon")) - { - const std::string wPath = elemPath + ".weapon"; - const toml::table& wTable = 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"], - file, wPath + ".damage")); - cap.attackRange_m = static_cast(requireDouble(wMt["attack_range_m"], - file, wPath + ".attack_range_m")); - cap.attackRate_hz = static_cast(requireDouble(wMt["attack_rate_hz"], - file, wPath + ".attack_rate_hz")); - def.weaponCapability = std::move(cap); - } - } - - // Salvage capability section: [module.salvage] with base stat formulas - if (mt.contains("salvage")) - { - const std::string sPath = elemPath + ".salvage"; - const toml::table& sTable = 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"], - file, sPath + ".collection_range_m")); - cap.cargoCapacity = static_cast(requireDouble(sMt["cargo_capacity"], - file, sPath + ".cargo_capacity")); - cap.collectionRate_hz = static_cast(requireDouble(sMt["collection_rate_hz"], - file, sPath + ".collection_rate_hz")); - def.salvageCapability = std::move(cap); - } - } - - // Repair capability section: [module.repair] with base stat formulas - if (mt.contains("repair")) - { - const std::string rPath = elemPath + ".repair"; - const toml::table& rTable = 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"], - file, rPath + ".repair_rate_hz")); - cap.repairAmountHp = static_cast(requireDouble(rMt["repair_amount_hp"], - file, rPath + ".repair_amount_hp")); - cap.repairRange_m = static_cast(requireDouble(rMt["repair_range_m"], - file, rPath + ".repair_range_m")); - def.repairCapability = std::move(cap); - } - } - - cfg.modules.push_back(std::move(def)); - } - - return cfg; -} - UnlocksConfig ConfigLoader::loadUnlocks(const std::string& path) { const std::string file = "unlocks.toml"; diff --git a/src/lib/config/ConfigLoaderModules.cpp b/src/lib/config/ConfigLoaderModules.cpp new file mode 100644 index 0000000..f93caa3 --- /dev/null +++ b/src/lib/config/ConfigLoaderModules.cpp @@ -0,0 +1,182 @@ +#include "ConfigLoader.h" + +#include +#include + +#include "toml.hpp" + +#include "TomlHelpers.h" + +namespace +{ + +// Known category→stat mappings for module stat modifier discovery. +// addedKeySuffix: unit suffix appended before "_formula" for additive modifier keys only. +// Multiplicative modifier keys are always dimensionless and carry no suffix. +struct StatEntry +{ + const char* category; + const char* stat; + const char* addedKeySuffix; +}; + +static const StatEntry kKnownStats[] = { + {"health", "hp", ""}, + {"movement", "speed", "_mps"}, + {"movement", "main_acceleration", "_mpss"}, + {"movement", "maneuvering_acceleration", "_mpss"}, + {"sensor", "sensor_range", "_m"}, + {"weapon", "damage", ""}, + {"weapon", "attack_range", "_m"}, + {"weapon", "attack_rate", "_hz"}, + {"salvage", "collection_range", "_m"}, + {"salvage", "collection_rate", "_hz"}, + {"cargo", "cargo_capacity", ""}, + {"repair", "repair_rate", "_hz"}, + {"repair", "repair_range", "_m"}, +}; + +} // namespace + +ModulesConfig ConfigLoader::loadModules(const std::string& path) +{ + const std::string file = "modules.toml"; + toml::table tbl = parseFile(path, file); + + ModulesConfig cfg; + + if (!tbl.contains("module")) + { + return cfg; + } + + const toml::array& arr = requireArray(tbl["module"], file, "module"); + + for (std::size_t i = 0; i < arr.size(); ++i) + { + const std::string elemPath = "module[" + std::to_string(i) + "]"; + const toml::table* st = arr[i].as_table(); + if (st == nullptr) + { + throw 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( + 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"); + + if (mt.contains("tooltip")) + { + def.tooltip = requireString(mt["tooltip"], file, elemPath + ".tooltip"); + } + + // Materials + { + const toml::array& materials = requireArray(mt["materials"], file, elemPath + ".materials"); + def.materials = parseIngredients(materials, file, elemPath + ".materials"); + } + + // Stat modifiers from [module.] sub-tables + for (const StatEntry& se : kKnownStats) + { + if (!mt.contains(se.category)) + { + continue; + } + const toml::table& catTable = requireTable(mt[se.category], file, + elemPath + "." + se.category); + toml::table& catMt = const_cast(catTable); + + const std::string addedKey = std::string("added_") + se.stat + se.addedKeySuffix; + const std::string multipliedKey = std::string("multiplied_") + se.stat + se.addedKeySuffix; + + if (catMt.contains(addedKey)) + { + ModuleStatModifier mod; + mod.stat = se.stat; + mod.modifierType = "additive"; + mod.value = requireDouble(catMt[addedKey], file, + elemPath + "." + se.category + "." + addedKey); + def.statModifiers.push_back(std::move(mod)); + } + + if (catMt.contains(multipliedKey)) + { + ModuleStatModifier mod; + mod.stat = se.stat; + mod.modifierType = "multiplicative"; + mod.value = requireDouble(catMt[multipliedKey], file, + elemPath + "." + se.category + "." + multipliedKey); + def.statModifiers.push_back(std::move(mod)); + } + } + + // Weapon capability section: [module.weapon] with base stat formulas + if (mt.contains("weapon")) + { + const std::string wPath = elemPath + ".weapon"; + const toml::table& wTable = 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"], + file, wPath + ".damage")); + cap.attackRange_m = static_cast(requireDouble(wMt["attack_range_m"], + file, wPath + ".attack_range_m")); + cap.attackRate_hz = static_cast(requireDouble(wMt["attack_rate_hz"], + file, wPath + ".attack_rate_hz")); + def.weaponCapability = std::move(cap); + } + } + + // Salvage capability section: [module.salvage] with base stat formulas + if (mt.contains("salvage")) + { + const std::string sPath = elemPath + ".salvage"; + const toml::table& sTable = 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"], + file, sPath + ".collection_range_m")); + cap.cargoCapacity = static_cast(requireDouble(sMt["cargo_capacity"], + file, sPath + ".cargo_capacity")); + cap.collectionRate_hz = static_cast(requireDouble(sMt["collection_rate_hz"], + file, sPath + ".collection_rate_hz")); + def.salvageCapability = std::move(cap); + } + } + + // Repair capability section: [module.repair] with base stat formulas + if (mt.contains("repair")) + { + const std::string rPath = elemPath + ".repair"; + const toml::table& rTable = 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"], + file, rPath + ".repair_rate_hz")); + cap.repairAmountHp = static_cast(requireDouble(rMt["repair_amount_hp"], + file, rPath + ".repair_amount_hp")); + cap.repairRange_m = static_cast(requireDouble(rMt["repair_range_m"], + file, rPath + ".repair_range_m")); + def.repairCapability = std::move(cap); + } + } + + cfg.modules.push_back(std::move(def)); + } + + return cfg; +}