#include "catch.hpp" #include "ConfigLoader.h" #include "ModulesConfig.h" static GameConfig loadConfig() { return ConfigLoader::loadFromDirectory(CONFIG_DIR); } static const ModuleDef* findModule(const GameConfig& cfg, const std::string& id) { for (const ModuleDef& m : cfg.modules.modules) { if (m.id == id) { return &m; } } return nullptr; } static const ModuleStatModifier* findModifier(const ModuleDef& def, const std::string& stat) { for (const ModuleStatModifier& sm : def.statModifiers) { if (sm.stat == stat) { return &sm; } } return nullptr; } TEST_CASE("ConfigLoader: loadModules parses modules.toml", "[config][modules]") { const GameConfig cfg = loadConfig(); REQUIRE(cfg.modules.modules.size() >= 2); const ModuleDef& armor = cfg.modules.modules[0]; CHECK(armor.id == "armor_plate"); CHECK(armor.surfaceMask.size() == 1); CHECK(armor.surfaceMask[0] == "OO"); CHECK(armor.materials.size() == 1); CHECK(armor.materials[0].item == "iron_ingot"); CHECK(armor.materials[0].amount == 2); CHECK(armor.playerProductionLevel == 1); CHECK(armor.productionTimeSeconds == Approx(3.0)); CHECK(armor.fillColor == "#808080"); CHECK(armor.glyph == "A"); REQUIRE(armor.statModifiers.size() == 1); CHECK(armor.statModifiers[0].stat == "hp"); CHECK(armor.statModifiers[0].modifierType == "multiplicative"); CHECK(armor.statModifiers[0].formula.evaluate(1.0) == Approx(1.5)); } TEST_CASE("ConfigLoader: loadModules parses additive modifiers", "[config][modules]") { const GameConfig cfg = loadConfig(); REQUIRE(cfg.modules.modules.size() >= 2); const ModuleDef& sensor = cfg.modules.modules[1]; CHECK(sensor.id == "sensor_booster"); REQUIRE(sensor.statModifiers.size() == 1); CHECK(sensor.statModifiers[0].stat == "sensor_range"); CHECK(sensor.statModifiers[0].modifierType == "additive"); CHECK(sensor.statModifiers[0].formula.evaluate(1.0) == Approx(100.0)); } TEST_CASE("ConfigLoader: multiplicative modifier with unit suffix is parsed (weapon_primer)", "[config][modules]") { const GameConfig cfg = loadConfig(); const ModuleDef* primer = findModule(cfg, "weapon_primer"); REQUIRE(primer != nullptr); REQUIRE(primer->statModifiers.size() == 1); const ModuleStatModifier* sm = findModifier(*primer, "attack_rate"); REQUIRE(sm != nullptr); CHECK(sm->modifierType == "multiplicative"); CHECK(sm->formula.evaluate(1.0) == Approx(1.2)); } TEST_CASE("ConfigLoader: weapon_stabilizer parses two multiplicative weapon modifiers", "[config][modules]") { const GameConfig cfg = loadConfig(); const ModuleDef* stab = findModule(cfg, "weapon_stabilizer"); REQUIRE(stab != nullptr); REQUIRE(stab->statModifiers.size() == 2); const ModuleStatModifier* rangeMod = findModifier(*stab, "attack_range"); REQUIRE(rangeMod != nullptr); CHECK(rangeMod->modifierType == "multiplicative"); CHECK(rangeMod->formula.evaluate(1.0) == Approx(1.5)); const ModuleStatModifier* rateMod = findModifier(*stab, "attack_rate"); REQUIRE(rateMod != nullptr); CHECK(rateMod->modifierType == "multiplicative"); CHECK(rateMod->formula.evaluate(1.0) == Approx(0.8)); } TEST_CASE("ConfigLoader: afterburner parses multiplicative speed and additive main_acceleration", "[config][modules]") { const GameConfig cfg = loadConfig(); const ModuleDef* ab = findModule(cfg, "afterburner"); REQUIRE(ab != nullptr); REQUIRE(ab->statModifiers.size() == 2); const ModuleStatModifier* speedMod = findModifier(*ab, "speed"); REQUIRE(speedMod != nullptr); CHECK(speedMod->modifierType == "multiplicative"); CHECK(speedMod->formula.evaluate(1.0) == Approx(1.6)); const ModuleStatModifier* accelMod = findModifier(*ab, "main_acceleration"); REQUIRE(accelMod != nullptr); CHECK(accelMod->modifierType == "additive"); CHECK(accelMod->formula.evaluate(1.0) == Approx(60.0)); } TEST_CASE("ConfigLoader: maneuvering_thrusters parses multiplicative speed and additive maneuvering_acceleration", "[config][modules]") { const GameConfig cfg = loadConfig(); const ModuleDef* mt = findModule(cfg, "maneuvering_thrusters"); REQUIRE(mt != nullptr); REQUIRE(mt->statModifiers.size() == 2); const ModuleStatModifier* speedMod = findModifier(*mt, "speed"); REQUIRE(speedMod != nullptr); CHECK(speedMod->modifierType == "multiplicative"); CHECK(speedMod->formula.evaluate(1.0) == Approx(1.2)); const ModuleStatModifier* accelMod = findModifier(*mt, "maneuvering_acceleration"); REQUIRE(accelMod != nullptr); CHECK(accelMod->modifierType == "additive"); CHECK(accelMod->formula.evaluate(1.0) == Approx(10.0)); } TEST_CASE("ConfigLoader: loadShips parses layout field", "[config][ships]") { const GameConfig cfg = loadConfig(); REQUIRE(!cfg.ships.ships.empty()); const ShipDef& ship = cfg.ships.ships[0]; REQUIRE(!ship.layout.empty()); CHECK(ship.layout[0] == "XOX"); CHECK(ship.layout[1] == "OOO"); CHECK(ship.layout[2] == "XOX"); }