#include "ConfigLoader.h" #include #include #include #include #include #include #include "toml.hpp" #include "Rotation.h" #include "ShipLayout.h" #include "TomlHelpers.h" namespace { Rotation parseRotationString(const std::string& s) { if (s == "east") { return Rotation::East; } if (s == "south") { return Rotation::South; } if (s == "west") { return Rotation::West; } return Rotation::North; } std::vector parsePlacedModules(const toml::array& arr, const std::string& file, const std::string& path) { std::vector result; result.reserve(arr.size()); for (std::size_t i = 0; i < arr.size(); ++i) { const std::string elemPath = path + "[" + std::to_string(i) + "]"; const toml::table* t = arr[i].as_table(); if (t == nullptr) { continue; } toml::table& mt = const_cast(*t); const std::optional type = mt["type"].value(); const std::optional x = mt["x"].value(); const std::optional y = mt["y"].value(); const std::optional rot = mt["rotation"].value(); if (!type || !x || !y || !rot) { continue; } PlacedModule pm; pm.moduleId = *type; pm.position = QPoint(static_cast(*x), static_cast(*y)); pm.rotation = parseRotationString(*rot); result.push_back(std::move(pm)); } return result; } } // namespace ShipsConfig ConfigLoader::loadShips(const std::string& path) { const std::string file = "ships.toml"; toml::table tbl = parseFile(path, file); ShipsConfig cfg; const toml::array& arr = requireArray(tbl["ship"], file, "ship"); for (std::size_t i = 0; i < arr.size(); ++i) { const std::string elemPath = "ship[" + 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); ShipDef def; def.id = requireString(mt["id"], file, elemPath + ".id"); def.layout = requireStringArray(mt["layout"], file, elemPath + ".layout"); // Schematic { const std::string bpPath = elemPath + ".schematic"; const toml::table& bpTable = 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( 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); toml::table& hMt = const_cast(hTable); def.health.hp = static_cast(requireDouble(hMt["hp"], file, hPath + ".hp")); } // Movement { const std::string mPath = elemPath + ".movement"; const toml::table& mTable = 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")); } // Sensor { const std::string snsPath = elemPath + ".sensor"; const toml::table& snsTable = 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")); } // Optional: default_modules (REQ-WAV-DEFAULT-MODULES) if (mt.contains("default_modules")) { const toml::array& modArr = requireArray(mt["default_modules"], file, elemPath + ".default_modules"); def.defaultModules = parsePlacedModules(modArr, file, elemPath + ".default_modules"); } cfg.ships.push_back(std::move(def)); } return cfg; }