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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG
This commit is contained in:
@@ -26,6 +26,7 @@ SET(SRCS
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/ConfigLoaderRecipes.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/ConfigLoaderRecipes.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ConfigLoaderShips.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/ConfigLoaderShips.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ConfigLoaderStations.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/ConfigLoaderStations.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/ConfigLoaderModules.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/SurfaceMask.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/SurfaceMask.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/BlueprintSerializer.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/BlueprintSerializer.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ShipLayoutBlueprintSerializer.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/ShipLayoutBlueprintSerializer.cpp
|
||||||
|
|||||||
@@ -13,175 +13,6 @@
|
|||||||
|
|
||||||
// --- Per-file loaders -----------------------------------------------------
|
// --- 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<toml::table&>(*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.<category>] 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<toml::table&>(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<toml::table&>(wTable);
|
|
||||||
if (wMt.contains("damage") || wMt.contains("attack_range_m")
|
|
||||||
|| wMt.contains("attack_rate_hz"))
|
|
||||||
{
|
|
||||||
ModuleWeaponCapability cap;
|
|
||||||
cap.damage = static_cast<float>(requireDouble(wMt["damage"],
|
|
||||||
file, wPath + ".damage"));
|
|
||||||
cap.attackRange_m = static_cast<float>(requireDouble(wMt["attack_range_m"],
|
|
||||||
file, wPath + ".attack_range_m"));
|
|
||||||
cap.attackRate_hz = static_cast<float>(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<toml::table&>(sTable);
|
|
||||||
if (sMt.contains("collection_range_m") || sMt.contains("cargo_capacity")
|
|
||||||
|| sMt.contains("collection_rate_hz"))
|
|
||||||
{
|
|
||||||
ModuleSalvageCapability cap;
|
|
||||||
cap.collectionRange_m = static_cast<float>(requireDouble(sMt["collection_range_m"],
|
|
||||||
file, sPath + ".collection_range_m"));
|
|
||||||
cap.cargoCapacity = static_cast<float>(requireDouble(sMt["cargo_capacity"],
|
|
||||||
file, sPath + ".cargo_capacity"));
|
|
||||||
cap.collectionRate_hz = static_cast<float>(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<toml::table&>(rTable);
|
|
||||||
if (rMt.contains("repair_rate_hz") || rMt.contains("repair_range_m"))
|
|
||||||
{
|
|
||||||
ModuleRepairCapability cap;
|
|
||||||
cap.repairRate_hz = static_cast<float>(requireDouble(rMt["repair_rate_hz"],
|
|
||||||
file, rPath + ".repair_rate_hz"));
|
|
||||||
cap.repairAmountHp = static_cast<float>(requireDouble(rMt["repair_amount_hp"],
|
|
||||||
file, rPath + ".repair_amount_hp"));
|
|
||||||
cap.repairRange_m = static_cast<float>(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)
|
UnlocksConfig ConfigLoader::loadUnlocks(const std::string& path)
|
||||||
{
|
{
|
||||||
const std::string file = "unlocks.toml";
|
const std::string file = "unlocks.toml";
|
||||||
|
|||||||
182
src/lib/config/ConfigLoaderModules.cpp
Normal file
182
src/lib/config/ConfigLoaderModules.cpp
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
#include "ConfigLoader.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#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<toml::table&>(*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.<category>] 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<toml::table&>(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<toml::table&>(wTable);
|
||||||
|
if (wMt.contains("damage") || wMt.contains("attack_range_m")
|
||||||
|
|| wMt.contains("attack_rate_hz"))
|
||||||
|
{
|
||||||
|
ModuleWeaponCapability cap;
|
||||||
|
cap.damage = static_cast<float>(requireDouble(wMt["damage"],
|
||||||
|
file, wPath + ".damage"));
|
||||||
|
cap.attackRange_m = static_cast<float>(requireDouble(wMt["attack_range_m"],
|
||||||
|
file, wPath + ".attack_range_m"));
|
||||||
|
cap.attackRate_hz = static_cast<float>(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<toml::table&>(sTable);
|
||||||
|
if (sMt.contains("collection_range_m") || sMt.contains("cargo_capacity")
|
||||||
|
|| sMt.contains("collection_rate_hz"))
|
||||||
|
{
|
||||||
|
ModuleSalvageCapability cap;
|
||||||
|
cap.collectionRange_m = static_cast<float>(requireDouble(sMt["collection_range_m"],
|
||||||
|
file, sPath + ".collection_range_m"));
|
||||||
|
cap.cargoCapacity = static_cast<float>(requireDouble(sMt["cargo_capacity"],
|
||||||
|
file, sPath + ".cargo_capacity"));
|
||||||
|
cap.collectionRate_hz = static_cast<float>(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<toml::table&>(rTable);
|
||||||
|
if (rMt.contains("repair_rate_hz") || rMt.contains("repair_range_m"))
|
||||||
|
{
|
||||||
|
ModuleRepairCapability cap;
|
||||||
|
cap.repairRate_hz = static_cast<float>(requireDouble(rMt["repair_rate_hz"],
|
||||||
|
file, rPath + ".repair_rate_hz"));
|
||||||
|
cap.repairAmountHp = static_cast<float>(requireDouble(rMt["repair_amount_hp"],
|
||||||
|
file, rPath + ".repair_amount_hp"));
|
||||||
|
cap.repairRange_m = static_cast<float>(requireDouble(rMt["repair_range_m"],
|
||||||
|
file, rPath + ".repair_range_m"));
|
||||||
|
def.repairCapability = std::move(cap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.modules.push_back(std::move(def));
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user