From 6b956198062d325f13946dce961692a17f3b3241 Mon Sep 17 00:00:00 2001 From: mlangkabel Date: Sat, 6 Jun 2026 12:13:05 +0200 Subject: [PATCH] add units in config files --- bin/app/data/config/modules.toml | 12 ++-- bin/app/data/config/ships.toml | 12 ++-- bin/app/data/config/stations.toml | 8 +-- bin/app/data/config/world.toml | 14 ++--- bin/balancing/data/balancing.toml | 12 ++-- bin/test/data/config/modules.toml | 14 ++--- bin/test/data/config/ships.toml | 48 +++++++-------- bin/test/data/config/stations.toml | 8 +-- bin/test/data/config/world.toml | 14 ++--- src/balancing/ArenaSimulation.cpp | 14 ++--- src/balancing/ArenaView.cpp | 12 ++-- src/balancing/BalancingConfig.cpp | 12 ++-- src/balancing/BalancingConfig.h | 6 +- src/lib/config/ConfigLoader.cpp | 97 +++++++++++++++--------------- src/lib/config/WorldConfig.h | 14 ++--- src/lib/sim/BuildingSystem.cpp | 4 +- src/lib/sim/Simulation.cpp | 8 +-- src/lib/sim/WaveSystem.cpp | 8 +-- src/lib/utility/tracing.cpp | 1 + src/test/BehaviorSystemTest.cpp | 4 +- src/test/ConfigLoaderTest.cpp | 50 +++++++-------- src/test/ShipModuleTest.cpp | 2 +- src/test/ShipTest.cpp | 12 ++-- src/ui/GameWorldView.cpp | 6 +- 24 files changed, 198 insertions(+), 194 deletions(-) diff --git a/bin/app/data/config/modules.toml b/bin/app/data/config/modules.toml index 8b1ffe1..840e7bf 100644 --- a/bin/app/data/config/modules.toml +++ b/bin/app/data/config/modules.toml @@ -10,8 +10,8 @@ glyph = "L" [module.weapon] damage_formula = "2" -attack_range_formula = "50" -attack_rate_formula = "2.0" +attack_range_m_formula = "50" +attack_rate_hz_formula = "2.0" [[module]] id = "salvager" @@ -24,9 +24,9 @@ fill_color = "#AACC44" glyph = "Sv" [module.salvage] -collection_range_formula = "500" +collection_range_m_formula = "500" cargo_capacity_formula = "10" -collection_rate_formula = "0.5" +collection_rate_hz_formula = "0.5" [[module]] id = "repair_tool" @@ -39,5 +39,5 @@ fill_color = "#66CCFF" glyph = "Rp" [module.repair] -repair_rate_formula = "5 + x" -repair_range_formula = "800" +repair_rate_hz_formula = "5 + x" +repair_range_m_formula = "800" diff --git a/bin/app/data/config/ships.toml b/bin/app/data/config/ships.toml index c84891c..c14c35f 100644 --- a/bin/app/data/config/ships.toml +++ b/bin/app/data/config/ships.toml @@ -16,14 +16,14 @@ cost_formula = "10" hp_formula = "3" [ship.movement] -speed_formula = "40" -main_acceleration_formula = "80" -maneuvering_acceleration_formula = "40" -angular_acceleration_formula = "12.56" -max_rotation_speed_formula = "6.28" +speed_mps_formula = "40" +main_acceleration_mpss_formula = "80" +maneuvering_acceleration_mpss_formula = "40" +angular_acceleration_radpss_formula = "12.56" +max_rotation_speed_radps_formula = "6.28" [ship.sensor] -sensor_range_formula = "150" +sensor_range_m_formula = "150" [ship.loot] scrap_drop = 2 diff --git a/bin/app/data/config/stations.toml b/bin/app/data/config/stations.toml index cbde215..94ba18b 100644 --- a/bin/app/data/config/stations.toml +++ b/bin/app/data/config/stations.toml @@ -14,8 +14,8 @@ surface_mask = [ level = 1 hp_formula = "300" damage_formula = "5" -range_formula = "200" -fire_rate_formula = "1" +range_m_formula = "200" +fire_rate_hz_formula = "1" scrap_drop_formula = "10" [enemy_station] @@ -25,6 +25,6 @@ surface_mask = [ ] hp_formula = "300 + 150*x" damage_formula = "2 + 1*x" -range_formula = "200" -fire_rate_formula = "1.0 + 0.2*x" +range_m_formula = "200" +fire_rate_hz_formula = "1.0 + 0.2*x" scrap_drop_formula = "10 + 5*x" diff --git a/bin/app/data/config/world.toml b/bin/app/data/config/world.toml index 623b49f..cfca2be 100644 --- a/bin/app/data/config/world.toml +++ b/bin/app/data/config/world.toml @@ -5,21 +5,21 @@ starting_building_blocks = 1000 scrap_despawn_seconds = 30 tile_size_m = 10 belt_speed_mps = 20 -tunnel_max_distance = 10 +tunnel_max_distance_tiles = 10 departure_interval_seconds = 20 [regions] -asteroid_width = 40 -player_buffer_width = 20 -contest_zone_width = 60 -enemy_buffer_width = 20 +asteroid_width_tiles = 40 +player_buffer_width_tiles = 20 +contest_zone_width_tiles = 60 +enemy_buffer_width_tiles = 20 [expansion] -columns_per_expansion = 10 +columns_per_expansion_tiles = 10 cost_building_blocks = 200 [push] -push_expand_columns = 10 +push_expand_columns_tiles = 10 boss_advance_seconds = 60 [waves] diff --git a/bin/balancing/data/balancing.toml b/bin/balancing/data/balancing.toml index 4e2cf5b..7b3a8b1 100644 --- a/bin/balancing/data/balancing.toml +++ b/bin/balancing/data/balancing.toml @@ -1,9 +1,9 @@ [[arena]] name = "Fighters vs Sniper" height_tiles = 20 -player_buffer_width = 10 -contest_zone_width = 60 -enemy_buffer_width = 10 +player_buffer_width_tiles = 10 +contest_zone_width_tiles = 60 +enemy_buffer_width_tiles = 10 [[arena.team]] name = "Alpha" @@ -29,9 +29,9 @@ enemy_buffer_width = 10 [[arena]] name = "Stations and Ships" height_tiles = 60 -player_buffer_width = 15 -contest_zone_width = 40 -enemy_buffer_width = 15 +player_buffer_width_tiles = 15 +contest_zone_width_tiles = 40 +enemy_buffer_width_tiles = 15 [[arena.team]] name = "Fortified" diff --git a/bin/test/data/config/modules.toml b/bin/test/data/config/modules.toml index b686296..1ab756a 100644 --- a/bin/test/data/config/modules.toml +++ b/bin/test/data/config/modules.toml @@ -22,7 +22,7 @@ fill_color = "#40A0FF" glyph = "S" [module.sensor] -added_sensor_range_formula = "100" +added_sensor_range_m_formula = "100" [[module]] id = "weapon_upgrade" @@ -49,8 +49,8 @@ glyph = "L" [module.weapon] damage_formula = "2" -attack_range_formula = "50" -attack_rate_formula = "2.0" +attack_range_m_formula = "50" +attack_rate_hz_formula = "2.0" [[module]] id = "salvager" @@ -63,9 +63,9 @@ fill_color = "#AACC44" glyph = "Sv" [module.salvage] -collection_range_formula = "500" +collection_range_m_formula = "500" cargo_capacity_formula = "10" -collection_rate_formula = "0.5" +collection_rate_hz_formula = "0.5" [[module]] id = "repair_tool" @@ -78,5 +78,5 @@ fill_color = "#66CCFF" glyph = "Rp" [module.repair] -repair_rate_formula = "5 + x" -repair_range_formula = "800" +repair_rate_hz_formula = "5 + x" +repair_range_m_formula = "800" diff --git a/bin/test/data/config/ships.toml b/bin/test/data/config/ships.toml index ad78f78..1e5f7ce 100644 --- a/bin/test/data/config/ships.toml +++ b/bin/test/data/config/ships.toml @@ -16,14 +16,14 @@ cost_formula = "5 + 1*x" hp_formula = "40 + 5*x" [ship.movement] -speed_formula = "2000 + 50*x" -main_acceleration_formula = "1000000" -maneuvering_acceleration_formula = "1000000" -angular_acceleration_formula = "100000" -max_rotation_speed_formula = "100000" +speed_mps_formula = "2000 + 50*x" +main_acceleration_mpss_formula = "1000000" +maneuvering_acceleration_mpss_formula = "1000000" +angular_acceleration_radpss_formula = "100000" +max_rotation_speed_radps_formula = "100000" [ship.sensor] -sensor_range_formula = "2000" +sensor_range_m_formula = "2000" [ship.loot] scrap_drop = 2 @@ -47,14 +47,14 @@ cost_formula = "10 + 2*x" hp_formula = "120 + 15*x" [ship.movement] -speed_formula = "1200" -main_acceleration_formula = "1000000" -maneuvering_acceleration_formula = "1000000" -angular_acceleration_formula = "100000" -max_rotation_speed_formula = "100000" +speed_mps_formula = "1200" +main_acceleration_mpss_formula = "1000000" +maneuvering_acceleration_mpss_formula = "1000000" +angular_acceleration_radpss_formula = "100000" +max_rotation_speed_radps_formula = "100000" [ship.sensor] -sensor_range_formula = "3000" +sensor_range_m_formula = "3000" [ship.loot] scrap_drop = 4 @@ -77,14 +77,14 @@ cost_formula = "0" hp_formula = "40 + 4*x" [ship.movement] -speed_formula = "1100" -main_acceleration_formula = "1000000" -maneuvering_acceleration_formula = "1000000" -angular_acceleration_formula = "100000" -max_rotation_speed_formula = "100000" +speed_mps_formula = "1100" +main_acceleration_mpss_formula = "1000000" +maneuvering_acceleration_mpss_formula = "1000000" +angular_acceleration_radpss_formula = "100000" +max_rotation_speed_radps_formula = "100000" [ship.sensor] -sensor_range_formula = "2500" +sensor_range_m_formula = "2500" [ship.loot] scrap_drop = 2 @@ -107,14 +107,14 @@ cost_formula = "0" hp_formula = "60 + 5*x" [ship.movement] -speed_formula = "1300" -main_acceleration_formula = "1000000" -maneuvering_acceleration_formula = "1000000" -angular_acceleration_formula = "100000" -max_rotation_speed_formula = "100000" +speed_mps_formula = "1300" +main_acceleration_mpss_formula = "1000000" +maneuvering_acceleration_mpss_formula = "1000000" +angular_acceleration_radpss_formula = "100000" +max_rotation_speed_radps_formula = "100000" [ship.sensor] -sensor_range_formula = "2500" +sensor_range_m_formula = "2500" [ship.loot] scrap_drop = 2 diff --git a/bin/test/data/config/stations.toml b/bin/test/data/config/stations.toml index c7558af..b5c8a99 100644 --- a/bin/test/data/config/stations.toml +++ b/bin/test/data/config/stations.toml @@ -14,8 +14,8 @@ surface_mask = [ level = 5 hp_formula = "300 + 40*x" damage_formula = "5 + 4*x" -range_formula = "3000 + 200*x" -fire_rate_formula = "0.5 + 0.2*x" +range_m_formula = "3000 + 200*x" +fire_rate_hz_formula = "0.5 + 0.2*x" scrap_drop_formula = "x" [enemy_station] @@ -25,6 +25,6 @@ surface_mask = [ ] hp_formula = "300 + 150*x" damage_formula = "20 + 10*x" -range_formula = "3500 + 200*x" -fire_rate_formula = "1.0 + 0.2*x" +range_m_formula = "3500 + 200*x" +fire_rate_hz_formula = "1.0 + 0.2*x" scrap_drop_formula = "10 + 5*x" diff --git a/bin/test/data/config/world.toml b/bin/test/data/config/world.toml index f754191..4b50be7 100644 --- a/bin/test/data/config/world.toml +++ b/bin/test/data/config/world.toml @@ -5,21 +5,21 @@ starting_building_blocks = 100 scrap_despawn_seconds = 30 tile_size_m = 10 belt_speed_mps = 20 -tunnel_max_distance = 10 +tunnel_max_distance_tiles = 10 departure_interval_seconds = 20 [regions] -asteroid_width = 40 -player_buffer_width = 10 -contest_zone_width = 30 -enemy_buffer_width = 15 +asteroid_width_tiles = 40 +player_buffer_width_tiles = 10 +contest_zone_width_tiles = 30 +enemy_buffer_width_tiles = 15 [expansion] -columns_per_expansion = 10 +columns_per_expansion_tiles = 10 cost_building_blocks = 200 [push] -push_expand_columns = 20 +push_expand_columns_tiles = 20 boss_advance_seconds = 60 [waves] diff --git a/src/balancing/ArenaSimulation.cpp b/src/balancing/ArenaSimulation.cpp index 423b476..75e1b62 100644 --- a/src/balancing/ArenaSimulation.cpp +++ b/src/balancing/ArenaSimulation.cpp @@ -73,9 +73,9 @@ BuildingId ArenaSimulation::allocateBuildingId() void ArenaSimulation::placeStructures() { - const int totalWidth = m_arenaConfig.playerBufferWidth - + m_arenaConfig.contestZoneWidth - + m_arenaConfig.enemyBufferWidth; + const int totalWidth = m_arenaConfig.playerBufferWidth_tiles + + m_arenaConfig.contestZoneWidth_tiles + + m_arenaConfig.enemyBufferWidth_tiles; const int midY = m_arenaConfig.heightTiles / 2; // Team 1 HQ — ECS proxy entity, player faction (isEnemy=false). @@ -182,9 +182,9 @@ void ArenaSimulation::placeStructures() void ArenaSimulation::spawnShips() { - const int contestStart = m_arenaConfig.playerBufferWidth; - const int team2Start = contestStart + m_arenaConfig.contestZoneWidth; - const int totalWidth = team2Start + m_arenaConfig.enemyBufferWidth; + const int contestStart = m_arenaConfig.playerBufferWidth_tiles; + const int team2Start = contestStart + m_arenaConfig.contestZoneWidth_tiles; + const int totalWidth = team2Start + m_arenaConfig.enemyBufferWidth_tiles; std::uniform_real_distribution yDist(0.0f, static_cast(m_arenaConfig.heightTiles)); @@ -192,7 +192,7 @@ void ArenaSimulation::spawnShips() // Team 1: isEnemy=false, spawn in player buffer zone. { std::uniform_real_distribution xDist(0.0f, - static_cast(m_arenaConfig.playerBufferWidth)); + static_cast(m_arenaConfig.playerBufferWidth_tiles)); for (const ArenaShipEntry& entry : m_arenaConfig.teams[0].ships) { diff --git a/src/balancing/ArenaView.cpp b/src/balancing/ArenaView.cpp index dfc6684..3ee13b4 100644 --- a/src/balancing/ArenaView.cpp +++ b/src/balancing/ArenaView.cpp @@ -158,9 +158,9 @@ void ArenaView::paintGL() float ArenaView::tilePx() const { const ArenaConfig& ac = m_sim->arenaConfig(); - const int totalWidth = ac.playerBufferWidth - + ac.contestZoneWidth - + ac.enemyBufferWidth; + const int totalWidth = ac.playerBufferWidth_tiles + + ac.contestZoneWidth_tiles + + ac.enemyBufferWidth_tiles; const int totalHeight = ac.heightTiles; if (totalWidth <= 0 || totalHeight <= 0) { return 1.0f; } @@ -205,9 +205,9 @@ std::optional ArenaView::entityPosition(entt::entity entity) const void ArenaView::drawTiles(QPainter& painter) { const ArenaConfig& ac = m_sim->arenaConfig(); - const int totalWidth = ac.playerBufferWidth - + ac.contestZoneWidth - + ac.enemyBufferWidth; + const int totalWidth = ac.playerBufferWidth_tiles + + ac.contestZoneWidth_tiles + + ac.enemyBufferWidth_tiles; const int totalHeight = ac.heightTiles; painter.setPen(Qt::NoPen); diff --git a/src/balancing/BalancingConfig.cpp b/src/balancing/BalancingConfig.cpp index 654c354..84eb426 100644 --- a/src/balancing/BalancingConfig.cpp +++ b/src/balancing/BalancingConfig.cpp @@ -81,12 +81,12 @@ BalancingConfig loadBalancingConfig(const std::string& path) arena.name = requireString((*arenaTbl)["name"], prefix + ".name"); arena.heightTiles = static_cast( requireInt((*arenaTbl)["height_tiles"], prefix + ".height_tiles")); - arena.playerBufferWidth = static_cast( - requireInt((*arenaTbl)["player_buffer_width"], prefix + ".player_buffer_width")); - arena.contestZoneWidth = static_cast( - requireInt((*arenaTbl)["contest_zone_width"], prefix + ".contest_zone_width")); - arena.enemyBufferWidth = static_cast( - requireInt((*arenaTbl)["enemy_buffer_width"], prefix + ".enemy_buffer_width")); + arena.playerBufferWidth_tiles = static_cast( + requireInt((*arenaTbl)["player_buffer_width_tiles"], prefix + ".player_buffer_width_tiles")); + arena.contestZoneWidth_tiles = static_cast( + requireInt((*arenaTbl)["contest_zone_width_tiles"], prefix + ".contest_zone_width_tiles")); + arena.enemyBufferWidth_tiles = static_cast( + requireInt((*arenaTbl)["enemy_buffer_width_tiles"], prefix + ".enemy_buffer_width_tiles")); const toml::array* teamArray = (*arenaTbl)["team"].as_array(); if (!teamArray || teamArray->size() != 2) diff --git a/src/balancing/BalancingConfig.h b/src/balancing/BalancingConfig.h index 03cbc3d..3eb237b 100644 --- a/src/balancing/BalancingConfig.h +++ b/src/balancing/BalancingConfig.h @@ -34,9 +34,9 @@ struct ArenaConfig { std::string name; int heightTiles; - int playerBufferWidth; - int contestZoneWidth; - int enemyBufferWidth; + int playerBufferWidth_tiles; + int contestZoneWidth_tiles; + int enemyBufferWidth_tiles; ArenaTeamConfig teams[2]; }; diff --git a/src/lib/config/ConfigLoader.cpp b/src/lib/config/ConfigLoader.cpp index 452ce1d..35a76bf 100644 --- a/src/lib/config/ConfigLoader.cpp +++ b/src/lib/config/ConfigLoader.cpp @@ -266,18 +266,18 @@ WorldConfig ConfigLoader::loadWorld(const std::string& path) cfg.scrapDespawnSeconds = requireDouble(tbl["world"]["scrap_despawn_seconds"], file, "world.scrap_despawn_seconds"); 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 = static_cast(requireInt(tbl["world"]["tunnel_max_distance"], file, "world.tunnel_max_distance")); + 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.regions.asteroidWidth = static_cast(requireInt(tbl["regions"]["asteroid_width"], file, "regions.asteroid_width")); - cfg.regions.playerBufferWidth = static_cast(requireInt(tbl["regions"]["player_buffer_width"], file, "regions.player_buffer_width")); - cfg.regions.contestZoneWidth = static_cast(requireInt(tbl["regions"]["contest_zone_width"], file, "regions.contest_zone_width")); - cfg.regions.enemyBufferWidth = static_cast(requireInt(tbl["regions"]["enemy_buffer_width"], file, "regions.enemy_buffer_width")); + 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.expansion.columnsPerExpansion = static_cast(requireInt(tbl["expansion"]["columns_per_expansion"], file, "expansion.columns_per_expansion")); - cfg.expansion.costBuildingBlocks = static_cast(requireInt(tbl["expansion"]["cost_building_blocks"], file, "expansion.cost_building_blocks")); + cfg.expansion.columnsPerExpansion_tiles = static_cast(requireInt(tbl["expansion"]["columns_per_expansion_tiles"], file, "expansion.columns_per_expansion_tiles")); + cfg.expansion.costBuildingBlocks = static_cast(requireInt(tbl["expansion"]["cost_building_blocks"], file, "expansion.cost_building_blocks")); - cfg.push.pushExpandColumns = static_cast(requireInt(tbl["push"]["push_expand_columns"], file, "push.push_expand_columns")); + 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.waves.threatRateFormula = requireFormula(tbl["waves"]["threat_rate_formula"], file, "waves.threat_rate_formula"); @@ -441,11 +441,11 @@ ShipsConfig ConfigLoader::loadShips(const std::string& path) const std::string mPath = elemPath + ".movement"; const toml::table& mTable = requireTable(mt["movement"], file, mPath); toml::table& mMt = const_cast(mTable); - def.movement.speedFormula = requireFormula(mMt["speed_formula"], file, mPath + ".speed_formula"); - def.movement.mainAccelerationFormula = requireFormula(mMt["main_acceleration_formula"], file, mPath + ".main_acceleration_formula"); - def.movement.maneuveringAccelerationFormula = requireFormula(mMt["maneuvering_acceleration_formula"], file, mPath + ".maneuvering_acceleration_formula"); - def.movement.angularAccelerationFormula = requireFormula(mMt["angular_acceleration_formula"], file, mPath + ".angular_acceleration_formula"); - def.movement.maxRotationSpeedFormula = requireFormula(mMt["max_rotation_speed_formula"], file, mPath + ".max_rotation_speed_formula"); + def.movement.speedFormula = requireFormula(mMt["speed_mps_formula"], file, mPath + ".speed_mps_formula"); + def.movement.mainAccelerationFormula = requireFormula(mMt["main_acceleration_mpss_formula"], file, mPath + ".main_acceleration_mpss_formula"); + def.movement.maneuveringAccelerationFormula = requireFormula(mMt["maneuvering_acceleration_mpss_formula"], file, mPath + ".maneuvering_acceleration_mpss_formula"); + def.movement.angularAccelerationFormula = requireFormula(mMt["angular_acceleration_radpss_formula"], file, mPath + ".angular_acceleration_radpss_formula"); + def.movement.maxRotationSpeedFormula = requireFormula(mMt["max_rotation_speed_radps_formula"], file, mPath + ".max_rotation_speed_radps_formula"); } // Sensor @@ -453,7 +453,7 @@ ShipsConfig ConfigLoader::loadShips(const std::string& path) const std::string snsPath = elemPath + ".sensor"; const toml::table& snsTable = requireTable(mt["sensor"], file, snsPath); toml::table& snsMt = const_cast(snsTable); - def.sensor.sensorRangeFormula = requireFormula(snsMt["sensor_range_formula"], file, snsPath + ".sensor_range_formula"); + def.sensor.sensorRangeFormula = requireFormula(snsMt["sensor_range_m_formula"], file, snsPath + ".sensor_range_m_formula"); } // Loot @@ -500,8 +500,8 @@ StationsConfig ConfigLoader::loadStations(const std::string& path) 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_formula"], file, p + ".range_formula"); - cfg.playerStation.fireRateFormula = requireFormula(tbl[p]["fire_rate_formula"], file, p + ".fire_rate_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"); } @@ -511,8 +511,8 @@ StationsConfig ConfigLoader::loadStations(const std::string& path) 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_formula"], file, p + ".range_formula"); - cfg.enemyStation.fireRateFormula = requireFormula(tbl[p]["fire_rate_formula"], file, p + ".fire_rate_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"); } @@ -520,24 +520,27 @@ StationsConfig ConfigLoader::loadStations(const std::string& path) } // 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"}, - {"sensor", "sensor_range"}, - {"weapon", "damage"}, - {"weapon", "attack_range"}, - {"weapon", "attack_rate"}, - {"salvage", "collection_range"}, - {"salvage", "cargo_capacity"}, - {"salvage", "collection_rate"}, - {"repair", "repair_rate"}, - {"repair", "repair_range"}, + {"health", "hp", ""}, + {"movement", "speed", "_mps"}, + {"sensor", "sensor_range", "_m"}, + {"weapon", "damage", ""}, + {"weapon", "attack_range", "_m"}, + {"weapon", "attack_rate", "_hz"}, + {"salvage", "collection_range", "_m"}, + {"salvage", "cargo_capacity", ""}, + {"salvage", "collection_rate", "_hz"}, + {"repair", "repair_rate", "_hz"}, + {"repair", "repair_range", "_m"}, }; ModulesConfig ConfigLoader::loadModules(const std::string& path) @@ -592,7 +595,7 @@ ModulesConfig ConfigLoader::loadModules(const std::string& path) elemPath + "." + se.category); toml::table& catMt = const_cast(catTable); - const std::string addedKey = std::string("added_") + se.stat + "_formula"; + const std::string addedKey = std::string("added_") + se.stat + se.addedKeySuffix + "_formula"; const std::string multipliedKey = std::string("multiplied_") + se.stat + "_formula"; if (catMt.contains(addedKey)) @@ -622,16 +625,16 @@ ModulesConfig ConfigLoader::loadModules(const std::string& path) 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_formula") || wMt.contains("attack_range_formula") - || wMt.contains("attack_rate_formula")) + if (wMt.contains("damage_formula") || wMt.contains("attack_range_m_formula") + || wMt.contains("attack_rate_hz_formula")) { ModuleWeaponCapability cap; cap.damageFormula = requireFormula(wMt["damage_formula"], file, wPath + ".damage_formula"); - cap.attackRangeFormula = requireFormula(wMt["attack_range_formula"], - file, wPath + ".attack_range_formula"); - cap.attackRateFormula = requireFormula(wMt["attack_rate_formula"], - file, wPath + ".attack_rate_formula"); + cap.attackRangeFormula = requireFormula(wMt["attack_range_m_formula"], + file, wPath + ".attack_range_m_formula"); + cap.attackRateFormula = requireFormula(wMt["attack_rate_hz_formula"], + file, wPath + ".attack_rate_hz_formula"); def.weaponCapability = std::move(cap); } } @@ -642,16 +645,16 @@ ModulesConfig ConfigLoader::loadModules(const std::string& path) 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_formula") || sMt.contains("cargo_capacity_formula") - || sMt.contains("collection_rate_formula")) + if (sMt.contains("collection_range_m_formula") || sMt.contains("cargo_capacity_formula") + || sMt.contains("collection_rate_hz_formula")) { ModuleSalvageCapability cap; - cap.collectionRangeFormula = requireFormula(sMt["collection_range_formula"], - file, sPath + ".collection_range_formula"); + cap.collectionRangeFormula = requireFormula(sMt["collection_range_m_formula"], + file, sPath + ".collection_range_m_formula"); cap.cargoCapacityFormula = requireFormula(sMt["cargo_capacity_formula"], file, sPath + ".cargo_capacity_formula"); - cap.collectionRateFormula = requireFormula(sMt["collection_rate_formula"], - file, sPath + ".collection_rate_formula"); + cap.collectionRateFormula = requireFormula(sMt["collection_rate_hz_formula"], + file, sPath + ".collection_rate_hz_formula"); def.salvageCapability = std::move(cap); } } @@ -662,13 +665,13 @@ ModulesConfig ConfigLoader::loadModules(const std::string& path) 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_formula") || rMt.contains("repair_range_formula")) + if (rMt.contains("repair_rate_hz_formula") || rMt.contains("repair_range_m_formula")) { ModuleRepairCapability cap; - cap.repairRateFormula = requireFormula(rMt["repair_rate_formula"], - file, rPath + ".repair_rate_formula"); - cap.repairRangeFormula = requireFormula(rMt["repair_range_formula"], - file, rPath + ".repair_range_formula"); + cap.repairRateFormula = requireFormula(rMt["repair_rate_hz_formula"], + file, rPath + ".repair_rate_hz_formula"); + cap.repairRangeFormula = requireFormula(rMt["repair_range_m_formula"], + file, rPath + ".repair_range_m_formula"); def.repairCapability = std::move(cap); } } diff --git a/src/lib/config/WorldConfig.h b/src/lib/config/WorldConfig.h index c8d7192..be92c7f 100644 --- a/src/lib/config/WorldConfig.h +++ b/src/lib/config/WorldConfig.h @@ -5,23 +5,23 @@ // Region widths are in tiles (REQ-GW-REGIONS). struct WorldRegions { - int asteroidWidth; - int playerBufferWidth; - int contestZoneWidth; - int enemyBufferWidth; + int asteroidWidth_tiles; + int playerBufferWidth_tiles; + int contestZoneWidth_tiles; + int enemyBufferWidth_tiles; }; // Asteroid expansion (REQ-EXP-UNLOCK, REQ-EXP-COST). struct WorldExpansion { - int columnsPerExpansion; + int columnsPerExpansion_tiles; int costBuildingBlocks; }; // Push effects (REQ-PSH-*, REQ-WAV-BOSS-ADVANCE). struct WorldPush { - int pushExpandColumns; + int pushExpandColumns_tiles; double bossAdvanceSeconds; // boss countdown advanced by this much per push }; @@ -47,7 +47,7 @@ struct WorldConfig double scrapDespawnSeconds; // REQ-RES-SCRAP-DROP double tileSize_m; // metres per tile (REQ-GW-TILE-SIZE) double beltSpeed_tps; // REQ-GW-BELT-SPEED (tiles/s, converted from m/s in config) - int tunnelMaxDistance; // REQ-BLD-TUNNEL-PAIR + int tunnelMaxDistance_tiles; // REQ-BLD-TUNNEL-PAIR double departureIntervalSeconds; // REQ-SHP-RALLY WorldRegions regions; diff --git a/src/lib/sim/BuildingSystem.cpp b/src/lib/sim/BuildingSystem.cpp index 87c64b7..17c62b6 100644 --- a/src/lib/sim/BuildingSystem.cpp +++ b/src/lib/sim/BuildingSystem.cpp @@ -493,7 +493,7 @@ void BuildingSystem::tickConstruction(Tick currentTick) } else if (front.type == BuildingType::TunnelEntry) { - m_belts.placeTunnelEntry(front.anchor, front.rotation, m_config.world.tunnelMaxDistance); + m_belts.placeTunnelEntry(front.anchor, front.rotation, m_config.world.tunnelMaxDistance_tiles); } else if (front.type == BuildingType::TunnelExit) { @@ -993,7 +993,7 @@ void BuildingSystem::rotateInPlace(BuildingId id, Rotation newRotation) else if (b.type == BuildingType::TunnelEntry) { m_belts.removeTile(b.anchor); - m_belts.placeTunnelEntry(b.anchor, newRotation, m_config.world.tunnelMaxDistance); + m_belts.placeTunnelEntry(b.anchor, newRotation, m_config.world.tunnelMaxDistance_tiles); } else if (b.type == BuildingType::TunnelExit) { diff --git a/src/lib/sim/Simulation.cpp b/src/lib/sim/Simulation.cpp index 09ce63d..e04c89a 100644 --- a/src/lib/sim/Simulation.cpp +++ b/src/lib/sim/Simulation.cpp @@ -239,7 +239,7 @@ void Simulation::placeInitialStructures() const ParsedSurfaceMask psParsed = parseSurfaceMask(m_config.stations.playerStation.surfaceMask, Rotation::East); const int psAnchorX = - m_config.world.regions.playerBufferWidth - psParsed.footprint.width(); + m_config.world.regions.playerBufferWidth_tiles - psParsed.footprint.width(); const double psLevel = static_cast(m_config.stations.playerStation.level); const float psHp = static_cast( m_config.stations.playerStation.hpFormula.evaluate(psLevel)); @@ -309,9 +309,9 @@ void Simulation::placeEnemyStationSet(int generation) const ParsedSurfaceMask esParsed = parseSurfaceMask(m_config.stations.enemyStation.surfaceMask, Rotation::East); - const int rightEdgeX = m_config.world.regions.playerBufferWidth - + m_config.world.regions.contestZoneWidth - + generation * m_config.world.push.pushExpandColumns; + const int rightEdgeX = m_config.world.regions.playerBufferWidth_tiles + + m_config.world.regions.contestZoneWidth_tiles + + generation * m_config.world.push.pushExpandColumns_tiles; const int anchorX = rightEdgeX - esParsed.footprint.width(); const double genD = static_cast(generation); diff --git a/src/lib/sim/WaveSystem.cpp b/src/lib/sim/WaveSystem.cpp index 9fedf6d..76a195b 100644 --- a/src/lib/sim/WaveSystem.cpp +++ b/src/lib/sim/WaveSystem.cpp @@ -186,11 +186,11 @@ std::vector WaveSystem::selectWaveShips(double& budget, // Enemy spawn buffer X range for the current generation. const float leftX = static_cast( - m_config.world.regions.playerBufferWidth - + m_config.world.regions.contestZoneWidth - + m_generation * m_config.world.push.pushExpandColumns); + m_config.world.regions.playerBufferWidth_tiles + + m_config.world.regions.contestZoneWidth_tiles + + m_generation * m_config.world.push.pushExpandColumns_tiles); const float rightX = leftX - + static_cast(m_config.world.regions.enemyBufferWidth) - 1.0f; + + static_cast(m_config.world.regions.enemyBufferWidth_tiles) - 1.0f; std::uniform_real_distribution xDist(leftX, rightX); std::uniform_int_distribution yDist(0, worldHeightTiles - 1); diff --git a/src/lib/utility/tracing.cpp b/src/lib/utility/tracing.cpp index d685d90..261c801 100644 --- a/src/lib/utility/tracing.cpp +++ b/src/lib/utility/tracing.cpp @@ -1,5 +1,6 @@ #include "tracing.h" +#include #include #include diff --git a/src/test/BehaviorSystemTest.cpp b/src/test/BehaviorSystemTest.cpp index b32945b..c325764 100644 --- a/src/test/BehaviorSystemTest.cpp +++ b/src/test/BehaviorSystemTest.cpp @@ -712,7 +712,7 @@ static int totalSalvageCurrent(EntityAdmin& admin, entt::entity ship) TEST_CASE("BehaviorSystem: salvage module does not collect scrap beyond its collection range", "[behavior]") { - // collection_range_formula = "50"; scrap at distance 55 must not be collected. + // collection_range_m_formula = "50"; scrap at distance 55 must not be collected. Fixture f; const ShipLayoutConfig salvageLayout = makeSingleModuleLayout("salvager"); const entt::entity ship = f.ships.spawn("salvage_ship", 1, QVector2D(0.0f, 0.0f), @@ -728,7 +728,7 @@ TEST_CASE("BehaviorSystem: salvage module does not collect scrap beyond its coll TEST_CASE("BehaviorSystem: salvage module collects scrap within its collection range", "[behavior]") { - // collection_range_formula = "50"; scrap at distance 45 must be collected. + // collection_range_m_formula = "50"; scrap at distance 45 must be collected. Fixture f; const ShipLayoutConfig salvageLayout = makeSingleModuleLayout("salvager"); const entt::entity ship = f.ships.spawn("salvage_ship", 1, QVector2D(0.0f, 0.0f), diff --git a/src/test/ConfigLoaderTest.cpp b/src/test/ConfigLoaderTest.cpp index 5829ce7..7cfb053 100644 --- a/src/test/ConfigLoaderTest.cpp +++ b/src/test/ConfigLoaderTest.cpp @@ -71,10 +71,10 @@ TEST_CASE("ConfigLoader loads the committed bin/config/ configs end-to-end", "[c REQUIRE(cfg.world.heightTiles == 60); REQUIRE(cfg.world.refundPercentage == 75); REQUIRE(cfg.world.beltSpeed_tps == Approx(2.0)); - REQUIRE(cfg.world.regions.asteroidWidth == 40); - REQUIRE(cfg.world.regions.playerBufferWidth == 10); - REQUIRE(cfg.world.regions.enemyBufferWidth == 15); - REQUIRE(cfg.world.expansion.columnsPerExpansion == 10); + REQUIRE(cfg.world.regions.asteroidWidth_tiles == 40); + REQUIRE(cfg.world.regions.playerBufferWidth_tiles == 10); + REQUIRE(cfg.world.regions.enemyBufferWidth_tiles == 15); + REQUIRE(cfg.world.expansion.columnsPerExpansion_tiles == 10); REQUIRE(cfg.world.push.bossAdvanceSeconds == Approx(60.0)); // Spot-check that a config-derived formula computes as expected. @@ -161,21 +161,21 @@ scrap_despawn_seconds = 30 tile_size_m = 10 belt_speed_mps = 20 starting_building_blocks = 100 -tunnel_max_distance = 10 +tunnel_max_distance_tiles = 10 departure_interval_seconds = 20 [regions] -asteroid_width = 40 -player_buffer_width = 10 -contest_zone_width = 30 -# enemy_buffer_width intentionally missing +asteroid_width_tiles = 40 +player_buffer_width_tiles = 10 +contest_zone_width_tiles = 30 +# enemy_buffer_width_tiles intentionally missing [expansion] -columns_per_expansion = 10 +columns_per_expansion_tiles = 10 cost_building_blocks = 200 [push] -push_expand_columns = 20 +push_expand_columns_tiles = 20 scaling_factor = 1.2 [waves] @@ -194,7 +194,7 @@ spawn_duration_seconds = 10 catch (const std::runtime_error& e) { const std::string msg = e.what(); - REQUIRE(msg.find("enemy_buffer_width") != std::string::npos); + REQUIRE(msg.find("enemy_buffer_width_tiles") != std::string::npos); } } @@ -209,21 +209,21 @@ scrap_despawn_seconds = 30 tile_size_m = 10 belt_speed_mps = 20 starting_building_blocks = 100 -tunnel_max_distance = 10 +tunnel_max_distance_tiles = 10 departure_interval_seconds = 20 [regions] -asteroid_width = 40 -player_buffer_width = 10 -contest_zone_width = 30 -enemy_buffer_width = 15 +asteroid_width_tiles = 40 +player_buffer_width_tiles = 10 +contest_zone_width_tiles = 30 +enemy_buffer_width_tiles = 15 [expansion] -columns_per_expansion = 10 +columns_per_expansion_tiles = 10 cost_building_blocks = 200 [push] -push_expand_columns = 20 +push_expand_columns_tiles = 20 boss_advance_seconds = 60 [waves] @@ -259,17 +259,17 @@ tile_size_m = 10 belt_speed_mps = 20 [regions] -asteroid_width = 40 -player_buffer_width = 10 -contest_zone_width = 30 -enemy_buffer_width = 15 +asteroid_width_tiles = 40 +player_buffer_width_tiles = 10 +contest_zone_width_tiles = 30 +enemy_buffer_width_tiles = 15 [expansion] -columns_per_expansion = 10 +columns_per_expansion_tiles = 10 cost_building_blocks = 200 [push] -push_expand_columns = 20 +push_expand_columns_tiles = 20 scaling_factor = 1.2 [waves] diff --git a/src/test/ShipModuleTest.cpp b/src/test/ShipModuleTest.cpp index 1668856..8263e38 100644 --- a/src/test/ShipModuleTest.cpp +++ b/src/test/ShipModuleTest.cpp @@ -154,7 +154,7 @@ TEST_CASE("Ship spawn: additive sensor module applies correctly", "[modules]") QVector2D(5.0f, 5.0f), false, layout); REQUIRE(sim.admin().isValid(e)); - // sensor_booster has added_sensor_range_formula = "100" m → 100/10 = 10 tiles + // sensor_booster has added_sensor_range_m_formula = "100" m → 100/10 = 10 tiles // final = baseRange_tiles * 1.0 + 10 = baseRange_tiles + 10 CHECK(sim.admin().get(e).value_tiles == Approx(baseRange_tiles + 10.0f)); } diff --git a/src/test/ShipTest.cpp b/src/test/ShipTest.cpp index 4b78180..3f1b5e7 100644 --- a/src/test/ShipTest.cpp +++ b/src/test/ShipTest.cpp @@ -110,10 +110,10 @@ TEST_CASE("ShipSystem: interceptor level 1 stats match config formulas", "[ship] // hp_formula = "40 + 5*x" at x=1 → 45 REQUIRE(admin.get(e).maxHp == Approx(45.0f)); REQUIRE(admin.get(e).hp == Approx(45.0f)); - // sensor_range_formula = "2000" m → 2000/10 = 200 tiles + // sensor_range_m_formula = "2000" m → 2000/10 = 200 tiles REQUIRE(admin.get(e).value_tiles == Approx(200.0f)); - // laser_cannon: damage_formula = "2", attack_range_formula = "50" m → 50/10 = 5 tiles + // laser_cannon: damage_formula = "2", attack_range_m_formula = "50" m → 50/10 = 5 tiles const entt::entity wc = firstWeaponChild(admin, e); REQUIRE(admin.isValid(wc)); REQUIRE(admin.get(wc).damage == Approx(2.0f)); @@ -141,7 +141,7 @@ TEST_CASE("ShipSystem: interceptor level 0 maxSpeed_tpt matches formula / tileSi const entt::entity e = ss.spawn("interceptor", 0, QVector2D(0.0f, 0.0f)); - // speed_formula = "2000 + 50*x" m/s at x=0 → 2000 m/s; maxSpeed_tpt = 2000/(10*30) + // speed_mps_formula = "2000 + 50*x" m/s at x=0 → 2000 m/s; maxSpeed_tpt = 2000/(10*30) const float expected = 2000.0f / 10.0f / static_cast(kTickRateHz); REQUIRE(admin.get(e).maxSpeed_tpt == Approx(expected)); } @@ -175,7 +175,7 @@ TEST_CASE("ShipSystem: salvage_ship cargo capacity matches config", "[ship]") const ShipLayoutConfig layout = makeSingleModuleLayout("salvager"); const entt::entity e = ss.spawn("salvage_ship", 1, QVector2D(0.0f, 0.0f), false, layout); - // salvager: cargo_capacity_formula = "10", collection_range_formula = "500" m → 500/10 = 50 tiles + // salvager: cargo_capacity_formula = "10", collection_range_m_formula = "500" m → 500/10 = 50 tiles const entt::entity sc = firstSalvageChild(admin, e); REQUIRE(admin.isValid(sc)); REQUIRE(admin.get(sc).capacity == 10); @@ -214,12 +214,12 @@ TEST_CASE("ShipSystem: repair_ship level 1 repair stats match config formulas", const ShipLayoutConfig layout = makeSingleModuleLayout("repair_tool"); const entt::entity e = ss.spawn("repair_ship", 1, QVector2D(0.0f, 0.0f), false, layout); - // repair_tool: repair_rate_formula = "5 + x" at x=1 → 6 / kTickRateHz + // repair_tool: repair_rate_hz_formula = "5 + x" at x=1 → 6 / kTickRateHz const float expectedRate = 6.0f / static_cast(kTickRateHz); const entt::entity rc = firstRepairChild(admin, e); REQUIRE(admin.isValid(rc)); REQUIRE(admin.get(rc).ratePerTick == Approx(expectedRate)); - // repair_range_formula = "800" m → 800/10 = 80 tiles + // repair_range_m_formula = "800" m → 800/10 = 80 tiles REQUIRE(admin.get(rc).range_tiles == Approx(80.0f)); REQUIRE(admin.get(e).maxRepairRange_tiles == Approx(80.0f)); } diff --git a/src/ui/GameWorldView.cpp b/src/ui/GameWorldView.cpp index f4d829b..a350256 100644 --- a/src/ui/GameWorldView.cpp +++ b/src/ui/GameWorldView.cpp @@ -352,7 +352,7 @@ QRect GameWorldView::viewportRect() const float GameWorldView::asteroidLeftEdge() const { - float leftX = -static_cast(m_config->world.regions.asteroidWidth); + float leftX = -static_cast(m_config->world.regions.asteroidWidth_tiles); for (const Building& b : m_sim->buildings().allBuildings()) { for (const QPoint& cell : b.bodyCells) @@ -368,8 +368,8 @@ float GameWorldView::asteroidLeftEdge() const float GameWorldView::enemyStationRightEdge() const { - float rightX = static_cast(m_config->world.regions.playerBufferWidth - + m_config->world.regions.contestZoneWidth); + float rightX = static_cast(m_config->world.regions.playerBufferWidth_tiles + + m_config->world.regions.contestZoneWidth_tiles); m_sim->admin().forEach( [&rightX](entt::entity /*e*/, const StationBodyComponent& sb, const FactionComponent& f) {