add units in config files
This commit is contained in:
@@ -10,8 +10,8 @@ glyph = "L"
|
|||||||
|
|
||||||
[module.weapon]
|
[module.weapon]
|
||||||
damage_formula = "2"
|
damage_formula = "2"
|
||||||
attack_range_formula = "50"
|
attack_range_m_formula = "50"
|
||||||
attack_rate_formula = "2.0"
|
attack_rate_hz_formula = "2.0"
|
||||||
|
|
||||||
[[module]]
|
[[module]]
|
||||||
id = "salvager"
|
id = "salvager"
|
||||||
@@ -24,9 +24,9 @@ fill_color = "#AACC44"
|
|||||||
glyph = "Sv"
|
glyph = "Sv"
|
||||||
|
|
||||||
[module.salvage]
|
[module.salvage]
|
||||||
collection_range_formula = "500"
|
collection_range_m_formula = "500"
|
||||||
cargo_capacity_formula = "10"
|
cargo_capacity_formula = "10"
|
||||||
collection_rate_formula = "0.5"
|
collection_rate_hz_formula = "0.5"
|
||||||
|
|
||||||
[[module]]
|
[[module]]
|
||||||
id = "repair_tool"
|
id = "repair_tool"
|
||||||
@@ -39,5 +39,5 @@ fill_color = "#66CCFF"
|
|||||||
glyph = "Rp"
|
glyph = "Rp"
|
||||||
|
|
||||||
[module.repair]
|
[module.repair]
|
||||||
repair_rate_formula = "5 + x"
|
repair_rate_hz_formula = "5 + x"
|
||||||
repair_range_formula = "800"
|
repair_range_m_formula = "800"
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ cost_formula = "10"
|
|||||||
hp_formula = "3"
|
hp_formula = "3"
|
||||||
|
|
||||||
[ship.movement]
|
[ship.movement]
|
||||||
speed_formula = "40"
|
speed_mps_formula = "40"
|
||||||
main_acceleration_formula = "80"
|
main_acceleration_mpss_formula = "80"
|
||||||
maneuvering_acceleration_formula = "40"
|
maneuvering_acceleration_mpss_formula = "40"
|
||||||
angular_acceleration_formula = "12.56"
|
angular_acceleration_radpss_formula = "12.56"
|
||||||
max_rotation_speed_formula = "6.28"
|
max_rotation_speed_radps_formula = "6.28"
|
||||||
|
|
||||||
[ship.sensor]
|
[ship.sensor]
|
||||||
sensor_range_formula = "150"
|
sensor_range_m_formula = "150"
|
||||||
|
|
||||||
[ship.loot]
|
[ship.loot]
|
||||||
scrap_drop = 2
|
scrap_drop = 2
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ surface_mask = [
|
|||||||
level = 1
|
level = 1
|
||||||
hp_formula = "300"
|
hp_formula = "300"
|
||||||
damage_formula = "5"
|
damage_formula = "5"
|
||||||
range_formula = "200"
|
range_m_formula = "200"
|
||||||
fire_rate_formula = "1"
|
fire_rate_hz_formula = "1"
|
||||||
scrap_drop_formula = "10"
|
scrap_drop_formula = "10"
|
||||||
|
|
||||||
[enemy_station]
|
[enemy_station]
|
||||||
@@ -25,6 +25,6 @@ surface_mask = [
|
|||||||
]
|
]
|
||||||
hp_formula = "300 + 150*x"
|
hp_formula = "300 + 150*x"
|
||||||
damage_formula = "2 + 1*x"
|
damage_formula = "2 + 1*x"
|
||||||
range_formula = "200"
|
range_m_formula = "200"
|
||||||
fire_rate_formula = "1.0 + 0.2*x"
|
fire_rate_hz_formula = "1.0 + 0.2*x"
|
||||||
scrap_drop_formula = "10 + 5*x"
|
scrap_drop_formula = "10 + 5*x"
|
||||||
|
|||||||
@@ -5,21 +5,21 @@ starting_building_blocks = 1000
|
|||||||
scrap_despawn_seconds = 30
|
scrap_despawn_seconds = 30
|
||||||
tile_size_m = 10
|
tile_size_m = 10
|
||||||
belt_speed_mps = 20
|
belt_speed_mps = 20
|
||||||
tunnel_max_distance = 10
|
tunnel_max_distance_tiles = 10
|
||||||
departure_interval_seconds = 20
|
departure_interval_seconds = 20
|
||||||
|
|
||||||
[regions]
|
[regions]
|
||||||
asteroid_width = 40
|
asteroid_width_tiles = 40
|
||||||
player_buffer_width = 20
|
player_buffer_width_tiles = 20
|
||||||
contest_zone_width = 60
|
contest_zone_width_tiles = 60
|
||||||
enemy_buffer_width = 20
|
enemy_buffer_width_tiles = 20
|
||||||
|
|
||||||
[expansion]
|
[expansion]
|
||||||
columns_per_expansion = 10
|
columns_per_expansion_tiles = 10
|
||||||
cost_building_blocks = 200
|
cost_building_blocks = 200
|
||||||
|
|
||||||
[push]
|
[push]
|
||||||
push_expand_columns = 10
|
push_expand_columns_tiles = 10
|
||||||
boss_advance_seconds = 60
|
boss_advance_seconds = 60
|
||||||
|
|
||||||
[waves]
|
[waves]
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
[[arena]]
|
[[arena]]
|
||||||
name = "Fighters vs Sniper"
|
name = "Fighters vs Sniper"
|
||||||
height_tiles = 20
|
height_tiles = 20
|
||||||
player_buffer_width = 10
|
player_buffer_width_tiles = 10
|
||||||
contest_zone_width = 60
|
contest_zone_width_tiles = 60
|
||||||
enemy_buffer_width = 10
|
enemy_buffer_width_tiles = 10
|
||||||
|
|
||||||
[[arena.team]]
|
[[arena.team]]
|
||||||
name = "Alpha"
|
name = "Alpha"
|
||||||
@@ -29,9 +29,9 @@ enemy_buffer_width = 10
|
|||||||
[[arena]]
|
[[arena]]
|
||||||
name = "Stations and Ships"
|
name = "Stations and Ships"
|
||||||
height_tiles = 60
|
height_tiles = 60
|
||||||
player_buffer_width = 15
|
player_buffer_width_tiles = 15
|
||||||
contest_zone_width = 40
|
contest_zone_width_tiles = 40
|
||||||
enemy_buffer_width = 15
|
enemy_buffer_width_tiles = 15
|
||||||
|
|
||||||
[[arena.team]]
|
[[arena.team]]
|
||||||
name = "Fortified"
|
name = "Fortified"
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ fill_color = "#40A0FF"
|
|||||||
glyph = "S"
|
glyph = "S"
|
||||||
|
|
||||||
[module.sensor]
|
[module.sensor]
|
||||||
added_sensor_range_formula = "100"
|
added_sensor_range_m_formula = "100"
|
||||||
|
|
||||||
[[module]]
|
[[module]]
|
||||||
id = "weapon_upgrade"
|
id = "weapon_upgrade"
|
||||||
@@ -49,8 +49,8 @@ glyph = "L"
|
|||||||
|
|
||||||
[module.weapon]
|
[module.weapon]
|
||||||
damage_formula = "2"
|
damage_formula = "2"
|
||||||
attack_range_formula = "50"
|
attack_range_m_formula = "50"
|
||||||
attack_rate_formula = "2.0"
|
attack_rate_hz_formula = "2.0"
|
||||||
|
|
||||||
[[module]]
|
[[module]]
|
||||||
id = "salvager"
|
id = "salvager"
|
||||||
@@ -63,9 +63,9 @@ fill_color = "#AACC44"
|
|||||||
glyph = "Sv"
|
glyph = "Sv"
|
||||||
|
|
||||||
[module.salvage]
|
[module.salvage]
|
||||||
collection_range_formula = "500"
|
collection_range_m_formula = "500"
|
||||||
cargo_capacity_formula = "10"
|
cargo_capacity_formula = "10"
|
||||||
collection_rate_formula = "0.5"
|
collection_rate_hz_formula = "0.5"
|
||||||
|
|
||||||
[[module]]
|
[[module]]
|
||||||
id = "repair_tool"
|
id = "repair_tool"
|
||||||
@@ -78,5 +78,5 @@ fill_color = "#66CCFF"
|
|||||||
glyph = "Rp"
|
glyph = "Rp"
|
||||||
|
|
||||||
[module.repair]
|
[module.repair]
|
||||||
repair_rate_formula = "5 + x"
|
repair_rate_hz_formula = "5 + x"
|
||||||
repair_range_formula = "800"
|
repair_range_m_formula = "800"
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ cost_formula = "5 + 1*x"
|
|||||||
hp_formula = "40 + 5*x"
|
hp_formula = "40 + 5*x"
|
||||||
|
|
||||||
[ship.movement]
|
[ship.movement]
|
||||||
speed_formula = "2000 + 50*x"
|
speed_mps_formula = "2000 + 50*x"
|
||||||
main_acceleration_formula = "1000000"
|
main_acceleration_mpss_formula = "1000000"
|
||||||
maneuvering_acceleration_formula = "1000000"
|
maneuvering_acceleration_mpss_formula = "1000000"
|
||||||
angular_acceleration_formula = "100000"
|
angular_acceleration_radpss_formula = "100000"
|
||||||
max_rotation_speed_formula = "100000"
|
max_rotation_speed_radps_formula = "100000"
|
||||||
|
|
||||||
[ship.sensor]
|
[ship.sensor]
|
||||||
sensor_range_formula = "2000"
|
sensor_range_m_formula = "2000"
|
||||||
|
|
||||||
[ship.loot]
|
[ship.loot]
|
||||||
scrap_drop = 2
|
scrap_drop = 2
|
||||||
@@ -47,14 +47,14 @@ cost_formula = "10 + 2*x"
|
|||||||
hp_formula = "120 + 15*x"
|
hp_formula = "120 + 15*x"
|
||||||
|
|
||||||
[ship.movement]
|
[ship.movement]
|
||||||
speed_formula = "1200"
|
speed_mps_formula = "1200"
|
||||||
main_acceleration_formula = "1000000"
|
main_acceleration_mpss_formula = "1000000"
|
||||||
maneuvering_acceleration_formula = "1000000"
|
maneuvering_acceleration_mpss_formula = "1000000"
|
||||||
angular_acceleration_formula = "100000"
|
angular_acceleration_radpss_formula = "100000"
|
||||||
max_rotation_speed_formula = "100000"
|
max_rotation_speed_radps_formula = "100000"
|
||||||
|
|
||||||
[ship.sensor]
|
[ship.sensor]
|
||||||
sensor_range_formula = "3000"
|
sensor_range_m_formula = "3000"
|
||||||
|
|
||||||
[ship.loot]
|
[ship.loot]
|
||||||
scrap_drop = 4
|
scrap_drop = 4
|
||||||
@@ -77,14 +77,14 @@ cost_formula = "0"
|
|||||||
hp_formula = "40 + 4*x"
|
hp_formula = "40 + 4*x"
|
||||||
|
|
||||||
[ship.movement]
|
[ship.movement]
|
||||||
speed_formula = "1100"
|
speed_mps_formula = "1100"
|
||||||
main_acceleration_formula = "1000000"
|
main_acceleration_mpss_formula = "1000000"
|
||||||
maneuvering_acceleration_formula = "1000000"
|
maneuvering_acceleration_mpss_formula = "1000000"
|
||||||
angular_acceleration_formula = "100000"
|
angular_acceleration_radpss_formula = "100000"
|
||||||
max_rotation_speed_formula = "100000"
|
max_rotation_speed_radps_formula = "100000"
|
||||||
|
|
||||||
[ship.sensor]
|
[ship.sensor]
|
||||||
sensor_range_formula = "2500"
|
sensor_range_m_formula = "2500"
|
||||||
|
|
||||||
[ship.loot]
|
[ship.loot]
|
||||||
scrap_drop = 2
|
scrap_drop = 2
|
||||||
@@ -107,14 +107,14 @@ cost_formula = "0"
|
|||||||
hp_formula = "60 + 5*x"
|
hp_formula = "60 + 5*x"
|
||||||
|
|
||||||
[ship.movement]
|
[ship.movement]
|
||||||
speed_formula = "1300"
|
speed_mps_formula = "1300"
|
||||||
main_acceleration_formula = "1000000"
|
main_acceleration_mpss_formula = "1000000"
|
||||||
maneuvering_acceleration_formula = "1000000"
|
maneuvering_acceleration_mpss_formula = "1000000"
|
||||||
angular_acceleration_formula = "100000"
|
angular_acceleration_radpss_formula = "100000"
|
||||||
max_rotation_speed_formula = "100000"
|
max_rotation_speed_radps_formula = "100000"
|
||||||
|
|
||||||
[ship.sensor]
|
[ship.sensor]
|
||||||
sensor_range_formula = "2500"
|
sensor_range_m_formula = "2500"
|
||||||
|
|
||||||
[ship.loot]
|
[ship.loot]
|
||||||
scrap_drop = 2
|
scrap_drop = 2
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ surface_mask = [
|
|||||||
level = 5
|
level = 5
|
||||||
hp_formula = "300 + 40*x"
|
hp_formula = "300 + 40*x"
|
||||||
damage_formula = "5 + 4*x"
|
damage_formula = "5 + 4*x"
|
||||||
range_formula = "3000 + 200*x"
|
range_m_formula = "3000 + 200*x"
|
||||||
fire_rate_formula = "0.5 + 0.2*x"
|
fire_rate_hz_formula = "0.5 + 0.2*x"
|
||||||
scrap_drop_formula = "x"
|
scrap_drop_formula = "x"
|
||||||
|
|
||||||
[enemy_station]
|
[enemy_station]
|
||||||
@@ -25,6 +25,6 @@ surface_mask = [
|
|||||||
]
|
]
|
||||||
hp_formula = "300 + 150*x"
|
hp_formula = "300 + 150*x"
|
||||||
damage_formula = "20 + 10*x"
|
damage_formula = "20 + 10*x"
|
||||||
range_formula = "3500 + 200*x"
|
range_m_formula = "3500 + 200*x"
|
||||||
fire_rate_formula = "1.0 + 0.2*x"
|
fire_rate_hz_formula = "1.0 + 0.2*x"
|
||||||
scrap_drop_formula = "10 + 5*x"
|
scrap_drop_formula = "10 + 5*x"
|
||||||
|
|||||||
@@ -5,21 +5,21 @@ starting_building_blocks = 100
|
|||||||
scrap_despawn_seconds = 30
|
scrap_despawn_seconds = 30
|
||||||
tile_size_m = 10
|
tile_size_m = 10
|
||||||
belt_speed_mps = 20
|
belt_speed_mps = 20
|
||||||
tunnel_max_distance = 10
|
tunnel_max_distance_tiles = 10
|
||||||
departure_interval_seconds = 20
|
departure_interval_seconds = 20
|
||||||
|
|
||||||
[regions]
|
[regions]
|
||||||
asteroid_width = 40
|
asteroid_width_tiles = 40
|
||||||
player_buffer_width = 10
|
player_buffer_width_tiles = 10
|
||||||
contest_zone_width = 30
|
contest_zone_width_tiles = 30
|
||||||
enemy_buffer_width = 15
|
enemy_buffer_width_tiles = 15
|
||||||
|
|
||||||
[expansion]
|
[expansion]
|
||||||
columns_per_expansion = 10
|
columns_per_expansion_tiles = 10
|
||||||
cost_building_blocks = 200
|
cost_building_blocks = 200
|
||||||
|
|
||||||
[push]
|
[push]
|
||||||
push_expand_columns = 20
|
push_expand_columns_tiles = 20
|
||||||
boss_advance_seconds = 60
|
boss_advance_seconds = 60
|
||||||
|
|
||||||
[waves]
|
[waves]
|
||||||
|
|||||||
@@ -73,9 +73,9 @@ BuildingId ArenaSimulation::allocateBuildingId()
|
|||||||
|
|
||||||
void ArenaSimulation::placeStructures()
|
void ArenaSimulation::placeStructures()
|
||||||
{
|
{
|
||||||
const int totalWidth = m_arenaConfig.playerBufferWidth
|
const int totalWidth = m_arenaConfig.playerBufferWidth_tiles
|
||||||
+ m_arenaConfig.contestZoneWidth
|
+ m_arenaConfig.contestZoneWidth_tiles
|
||||||
+ m_arenaConfig.enemyBufferWidth;
|
+ m_arenaConfig.enemyBufferWidth_tiles;
|
||||||
const int midY = m_arenaConfig.heightTiles / 2;
|
const int midY = m_arenaConfig.heightTiles / 2;
|
||||||
|
|
||||||
// Team 1 HQ — ECS proxy entity, player faction (isEnemy=false).
|
// Team 1 HQ — ECS proxy entity, player faction (isEnemy=false).
|
||||||
@@ -182,9 +182,9 @@ void ArenaSimulation::placeStructures()
|
|||||||
|
|
||||||
void ArenaSimulation::spawnShips()
|
void ArenaSimulation::spawnShips()
|
||||||
{
|
{
|
||||||
const int contestStart = m_arenaConfig.playerBufferWidth;
|
const int contestStart = m_arenaConfig.playerBufferWidth_tiles;
|
||||||
const int team2Start = contestStart + m_arenaConfig.contestZoneWidth;
|
const int team2Start = contestStart + m_arenaConfig.contestZoneWidth_tiles;
|
||||||
const int totalWidth = team2Start + m_arenaConfig.enemyBufferWidth;
|
const int totalWidth = team2Start + m_arenaConfig.enemyBufferWidth_tiles;
|
||||||
|
|
||||||
std::uniform_real_distribution<float> yDist(0.0f,
|
std::uniform_real_distribution<float> yDist(0.0f,
|
||||||
static_cast<float>(m_arenaConfig.heightTiles));
|
static_cast<float>(m_arenaConfig.heightTiles));
|
||||||
@@ -192,7 +192,7 @@ void ArenaSimulation::spawnShips()
|
|||||||
// Team 1: isEnemy=false, spawn in player buffer zone.
|
// Team 1: isEnemy=false, spawn in player buffer zone.
|
||||||
{
|
{
|
||||||
std::uniform_real_distribution<float> xDist(0.0f,
|
std::uniform_real_distribution<float> xDist(0.0f,
|
||||||
static_cast<float>(m_arenaConfig.playerBufferWidth));
|
static_cast<float>(m_arenaConfig.playerBufferWidth_tiles));
|
||||||
|
|
||||||
for (const ArenaShipEntry& entry : m_arenaConfig.teams[0].ships)
|
for (const ArenaShipEntry& entry : m_arenaConfig.teams[0].ships)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -158,9 +158,9 @@ void ArenaView::paintGL()
|
|||||||
float ArenaView::tilePx() const
|
float ArenaView::tilePx() const
|
||||||
{
|
{
|
||||||
const ArenaConfig& ac = m_sim->arenaConfig();
|
const ArenaConfig& ac = m_sim->arenaConfig();
|
||||||
const int totalWidth = ac.playerBufferWidth
|
const int totalWidth = ac.playerBufferWidth_tiles
|
||||||
+ ac.contestZoneWidth
|
+ ac.contestZoneWidth_tiles
|
||||||
+ ac.enemyBufferWidth;
|
+ ac.enemyBufferWidth_tiles;
|
||||||
const int totalHeight = ac.heightTiles;
|
const int totalHeight = ac.heightTiles;
|
||||||
if (totalWidth <= 0 || totalHeight <= 0) { return 1.0f; }
|
if (totalWidth <= 0 || totalHeight <= 0) { return 1.0f; }
|
||||||
|
|
||||||
@@ -205,9 +205,9 @@ std::optional<QVector2D> ArenaView::entityPosition(entt::entity entity) const
|
|||||||
void ArenaView::drawTiles(QPainter& painter)
|
void ArenaView::drawTiles(QPainter& painter)
|
||||||
{
|
{
|
||||||
const ArenaConfig& ac = m_sim->arenaConfig();
|
const ArenaConfig& ac = m_sim->arenaConfig();
|
||||||
const int totalWidth = ac.playerBufferWidth
|
const int totalWidth = ac.playerBufferWidth_tiles
|
||||||
+ ac.contestZoneWidth
|
+ ac.contestZoneWidth_tiles
|
||||||
+ ac.enemyBufferWidth;
|
+ ac.enemyBufferWidth_tiles;
|
||||||
const int totalHeight = ac.heightTiles;
|
const int totalHeight = ac.heightTiles;
|
||||||
|
|
||||||
painter.setPen(Qt::NoPen);
|
painter.setPen(Qt::NoPen);
|
||||||
|
|||||||
@@ -81,12 +81,12 @@ BalancingConfig loadBalancingConfig(const std::string& path)
|
|||||||
arena.name = requireString((*arenaTbl)["name"], prefix + ".name");
|
arena.name = requireString((*arenaTbl)["name"], prefix + ".name");
|
||||||
arena.heightTiles = static_cast<int>(
|
arena.heightTiles = static_cast<int>(
|
||||||
requireInt((*arenaTbl)["height_tiles"], prefix + ".height_tiles"));
|
requireInt((*arenaTbl)["height_tiles"], prefix + ".height_tiles"));
|
||||||
arena.playerBufferWidth = static_cast<int>(
|
arena.playerBufferWidth_tiles = static_cast<int>(
|
||||||
requireInt((*arenaTbl)["player_buffer_width"], prefix + ".player_buffer_width"));
|
requireInt((*arenaTbl)["player_buffer_width_tiles"], prefix + ".player_buffer_width_tiles"));
|
||||||
arena.contestZoneWidth = static_cast<int>(
|
arena.contestZoneWidth_tiles = static_cast<int>(
|
||||||
requireInt((*arenaTbl)["contest_zone_width"], prefix + ".contest_zone_width"));
|
requireInt((*arenaTbl)["contest_zone_width_tiles"], prefix + ".contest_zone_width_tiles"));
|
||||||
arena.enemyBufferWidth = static_cast<int>(
|
arena.enemyBufferWidth_tiles = static_cast<int>(
|
||||||
requireInt((*arenaTbl)["enemy_buffer_width"], prefix + ".enemy_buffer_width"));
|
requireInt((*arenaTbl)["enemy_buffer_width_tiles"], prefix + ".enemy_buffer_width_tiles"));
|
||||||
|
|
||||||
const toml::array* teamArray = (*arenaTbl)["team"].as_array();
|
const toml::array* teamArray = (*arenaTbl)["team"].as_array();
|
||||||
if (!teamArray || teamArray->size() != 2)
|
if (!teamArray || teamArray->size() != 2)
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ struct ArenaConfig
|
|||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
int heightTiles;
|
int heightTiles;
|
||||||
int playerBufferWidth;
|
int playerBufferWidth_tiles;
|
||||||
int contestZoneWidth;
|
int contestZoneWidth_tiles;
|
||||||
int enemyBufferWidth;
|
int enemyBufferWidth_tiles;
|
||||||
ArenaTeamConfig teams[2];
|
ArenaTeamConfig teams[2];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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.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.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.beltSpeed_tps = requireDouble(tbl["world"]["belt_speed_mps"], file, "world.belt_speed_mps") / cfg.tileSize_m;
|
||||||
cfg.tunnelMaxDistance = static_cast<int>(requireInt(tbl["world"]["tunnel_max_distance"], file, "world.tunnel_max_distance"));
|
cfg.tunnelMaxDistance_tiles = static_cast<int>(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.departureIntervalSeconds = requireDouble(tbl["world"]["departure_interval_seconds"], file, "world.departure_interval_seconds");
|
||||||
|
|
||||||
cfg.regions.asteroidWidth = static_cast<int>(requireInt(tbl["regions"]["asteroid_width"], file, "regions.asteroid_width"));
|
cfg.regions.asteroidWidth_tiles = static_cast<int>(requireInt(tbl["regions"]["asteroid_width_tiles"], file, "regions.asteroid_width_tiles"));
|
||||||
cfg.regions.playerBufferWidth = static_cast<int>(requireInt(tbl["regions"]["player_buffer_width"], file, "regions.player_buffer_width"));
|
cfg.regions.playerBufferWidth_tiles = static_cast<int>(requireInt(tbl["regions"]["player_buffer_width_tiles"], file, "regions.player_buffer_width_tiles"));
|
||||||
cfg.regions.contestZoneWidth = static_cast<int>(requireInt(tbl["regions"]["contest_zone_width"], file, "regions.contest_zone_width"));
|
cfg.regions.contestZoneWidth_tiles = static_cast<int>(requireInt(tbl["regions"]["contest_zone_width_tiles"], file, "regions.contest_zone_width_tiles"));
|
||||||
cfg.regions.enemyBufferWidth = static_cast<int>(requireInt(tbl["regions"]["enemy_buffer_width"], file, "regions.enemy_buffer_width"));
|
cfg.regions.enemyBufferWidth_tiles = static_cast<int>(requireInt(tbl["regions"]["enemy_buffer_width_tiles"], file, "regions.enemy_buffer_width_tiles"));
|
||||||
|
|
||||||
cfg.expansion.columnsPerExpansion = static_cast<int>(requireInt(tbl["expansion"]["columns_per_expansion"], file, "expansion.columns_per_expansion"));
|
cfg.expansion.columnsPerExpansion_tiles = static_cast<int>(requireInt(tbl["expansion"]["columns_per_expansion_tiles"], file, "expansion.columns_per_expansion_tiles"));
|
||||||
cfg.expansion.costBuildingBlocks = static_cast<int>(requireInt(tbl["expansion"]["cost_building_blocks"], file, "expansion.cost_building_blocks"));
|
cfg.expansion.costBuildingBlocks = static_cast<int>(requireInt(tbl["expansion"]["cost_building_blocks"], file, "expansion.cost_building_blocks"));
|
||||||
|
|
||||||
cfg.push.pushExpandColumns = static_cast<int>(requireInt(tbl["push"]["push_expand_columns"], file, "push.push_expand_columns"));
|
cfg.push.pushExpandColumns_tiles = static_cast<int>(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.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");
|
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 std::string mPath = elemPath + ".movement";
|
||||||
const toml::table& mTable = requireTable(mt["movement"], file, mPath);
|
const toml::table& mTable = requireTable(mt["movement"], file, mPath);
|
||||||
toml::table& mMt = const_cast<toml::table&>(mTable);
|
toml::table& mMt = const_cast<toml::table&>(mTable);
|
||||||
def.movement.speedFormula = requireFormula(mMt["speed_formula"], file, mPath + ".speed_formula");
|
def.movement.speedFormula = requireFormula(mMt["speed_mps_formula"], file, mPath + ".speed_mps_formula");
|
||||||
def.movement.mainAccelerationFormula = requireFormula(mMt["main_acceleration_formula"], file, mPath + ".main_acceleration_formula");
|
def.movement.mainAccelerationFormula = requireFormula(mMt["main_acceleration_mpss_formula"], file, mPath + ".main_acceleration_mpss_formula");
|
||||||
def.movement.maneuveringAccelerationFormula = requireFormula(mMt["maneuvering_acceleration_formula"], file, mPath + ".maneuvering_acceleration_formula");
|
def.movement.maneuveringAccelerationFormula = requireFormula(mMt["maneuvering_acceleration_mpss_formula"], file, mPath + ".maneuvering_acceleration_mpss_formula");
|
||||||
def.movement.angularAccelerationFormula = requireFormula(mMt["angular_acceleration_formula"], file, mPath + ".angular_acceleration_formula");
|
def.movement.angularAccelerationFormula = requireFormula(mMt["angular_acceleration_radpss_formula"], file, mPath + ".angular_acceleration_radpss_formula");
|
||||||
def.movement.maxRotationSpeedFormula = requireFormula(mMt["max_rotation_speed_formula"], file, mPath + ".max_rotation_speed_formula");
|
def.movement.maxRotationSpeedFormula = requireFormula(mMt["max_rotation_speed_radps_formula"], file, mPath + ".max_rotation_speed_radps_formula");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sensor
|
// Sensor
|
||||||
@@ -453,7 +453,7 @@ ShipsConfig ConfigLoader::loadShips(const std::string& path)
|
|||||||
const std::string snsPath = elemPath + ".sensor";
|
const std::string snsPath = elemPath + ".sensor";
|
||||||
const toml::table& snsTable = requireTable(mt["sensor"], file, snsPath);
|
const toml::table& snsTable = requireTable(mt["sensor"], file, snsPath);
|
||||||
toml::table& snsMt = const_cast<toml::table&>(snsTable);
|
toml::table& snsMt = const_cast<toml::table&>(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
|
// Loot
|
||||||
@@ -500,8 +500,8 @@ StationsConfig ConfigLoader::loadStations(const std::string& path)
|
|||||||
cfg.playerStation.level = static_cast<int>(requireInt(tbl[p]["level"], file, p + ".level"));
|
cfg.playerStation.level = static_cast<int>(requireInt(tbl[p]["level"], file, p + ".level"));
|
||||||
cfg.playerStation.hpFormula = requireFormula(tbl[p]["hp_formula"], file, p + ".hp_formula");
|
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.damageFormula = requireFormula(tbl[p]["damage_formula"], file, p + ".damage_formula");
|
||||||
cfg.playerStation.rangeFormula = requireFormula(tbl[p]["range_formula"], file, p + ".range_formula");
|
cfg.playerStation.rangeFormula = requireFormula(tbl[p]["range_m_formula"], file, p + ".range_m_formula");
|
||||||
cfg.playerStation.fireRateFormula = requireFormula(tbl[p]["fire_rate_formula"], file, p + ".fire_rate_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");
|
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.surfaceMask = requireStringArray(tbl[p]["surface_mask"], file, p + ".surface_mask");
|
||||||
cfg.enemyStation.hpFormula = requireFormula(tbl[p]["hp_formula"], file, p + ".hp_formula");
|
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.damageFormula = requireFormula(tbl[p]["damage_formula"], file, p + ".damage_formula");
|
||||||
cfg.enemyStation.rangeFormula = requireFormula(tbl[p]["range_formula"], file, p + ".range_formula");
|
cfg.enemyStation.rangeFormula = requireFormula(tbl[p]["range_m_formula"], file, p + ".range_m_formula");
|
||||||
cfg.enemyStation.fireRateFormula = requireFormula(tbl[p]["fire_rate_formula"], file, p + ".fire_rate_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");
|
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.
|
// 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
|
struct StatEntry
|
||||||
{
|
{
|
||||||
const char* category;
|
const char* category;
|
||||||
const char* stat;
|
const char* stat;
|
||||||
|
const char* addedKeySuffix;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const StatEntry kKnownStats[] = {
|
static const StatEntry kKnownStats[] = {
|
||||||
{"health", "hp"},
|
{"health", "hp", ""},
|
||||||
{"movement", "speed"},
|
{"movement", "speed", "_mps"},
|
||||||
{"sensor", "sensor_range"},
|
{"sensor", "sensor_range", "_m"},
|
||||||
{"weapon", "damage"},
|
{"weapon", "damage", ""},
|
||||||
{"weapon", "attack_range"},
|
{"weapon", "attack_range", "_m"},
|
||||||
{"weapon", "attack_rate"},
|
{"weapon", "attack_rate", "_hz"},
|
||||||
{"salvage", "collection_range"},
|
{"salvage", "collection_range", "_m"},
|
||||||
{"salvage", "cargo_capacity"},
|
{"salvage", "cargo_capacity", ""},
|
||||||
{"salvage", "collection_rate"},
|
{"salvage", "collection_rate", "_hz"},
|
||||||
{"repair", "repair_rate"},
|
{"repair", "repair_rate", "_hz"},
|
||||||
{"repair", "repair_range"},
|
{"repair", "repair_range", "_m"},
|
||||||
};
|
};
|
||||||
|
|
||||||
ModulesConfig ConfigLoader::loadModules(const std::string& path)
|
ModulesConfig ConfigLoader::loadModules(const std::string& path)
|
||||||
@@ -592,7 +595,7 @@ ModulesConfig ConfigLoader::loadModules(const std::string& path)
|
|||||||
elemPath + "." + se.category);
|
elemPath + "." + se.category);
|
||||||
toml::table& catMt = const_cast<toml::table&>(catTable);
|
toml::table& catMt = const_cast<toml::table&>(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";
|
const std::string multipliedKey = std::string("multiplied_") + se.stat + "_formula";
|
||||||
|
|
||||||
if (catMt.contains(addedKey))
|
if (catMt.contains(addedKey))
|
||||||
@@ -622,16 +625,16 @@ ModulesConfig ConfigLoader::loadModules(const std::string& path)
|
|||||||
const std::string wPath = elemPath + ".weapon";
|
const std::string wPath = elemPath + ".weapon";
|
||||||
const toml::table& wTable = requireTable(mt["weapon"], file, wPath);
|
const toml::table& wTable = requireTable(mt["weapon"], file, wPath);
|
||||||
toml::table& wMt = const_cast<toml::table&>(wTable);
|
toml::table& wMt = const_cast<toml::table&>(wTable);
|
||||||
if (wMt.contains("damage_formula") || wMt.contains("attack_range_formula")
|
if (wMt.contains("damage_formula") || wMt.contains("attack_range_m_formula")
|
||||||
|| wMt.contains("attack_rate_formula"))
|
|| wMt.contains("attack_rate_hz_formula"))
|
||||||
{
|
{
|
||||||
ModuleWeaponCapability cap;
|
ModuleWeaponCapability cap;
|
||||||
cap.damageFormula = requireFormula(wMt["damage_formula"],
|
cap.damageFormula = requireFormula(wMt["damage_formula"],
|
||||||
file, wPath + ".damage_formula");
|
file, wPath + ".damage_formula");
|
||||||
cap.attackRangeFormula = requireFormula(wMt["attack_range_formula"],
|
cap.attackRangeFormula = requireFormula(wMt["attack_range_m_formula"],
|
||||||
file, wPath + ".attack_range_formula");
|
file, wPath + ".attack_range_m_formula");
|
||||||
cap.attackRateFormula = requireFormula(wMt["attack_rate_formula"],
|
cap.attackRateFormula = requireFormula(wMt["attack_rate_hz_formula"],
|
||||||
file, wPath + ".attack_rate_formula");
|
file, wPath + ".attack_rate_hz_formula");
|
||||||
def.weaponCapability = std::move(cap);
|
def.weaponCapability = std::move(cap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -642,16 +645,16 @@ ModulesConfig ConfigLoader::loadModules(const std::string& path)
|
|||||||
const std::string sPath = elemPath + ".salvage";
|
const std::string sPath = elemPath + ".salvage";
|
||||||
const toml::table& sTable = requireTable(mt["salvage"], file, sPath);
|
const toml::table& sTable = requireTable(mt["salvage"], file, sPath);
|
||||||
toml::table& sMt = const_cast<toml::table&>(sTable);
|
toml::table& sMt = const_cast<toml::table&>(sTable);
|
||||||
if (sMt.contains("collection_range_formula") || sMt.contains("cargo_capacity_formula")
|
if (sMt.contains("collection_range_m_formula") || sMt.contains("cargo_capacity_formula")
|
||||||
|| sMt.contains("collection_rate_formula"))
|
|| sMt.contains("collection_rate_hz_formula"))
|
||||||
{
|
{
|
||||||
ModuleSalvageCapability cap;
|
ModuleSalvageCapability cap;
|
||||||
cap.collectionRangeFormula = requireFormula(sMt["collection_range_formula"],
|
cap.collectionRangeFormula = requireFormula(sMt["collection_range_m_formula"],
|
||||||
file, sPath + ".collection_range_formula");
|
file, sPath + ".collection_range_m_formula");
|
||||||
cap.cargoCapacityFormula = requireFormula(sMt["cargo_capacity_formula"],
|
cap.cargoCapacityFormula = requireFormula(sMt["cargo_capacity_formula"],
|
||||||
file, sPath + ".cargo_capacity_formula");
|
file, sPath + ".cargo_capacity_formula");
|
||||||
cap.collectionRateFormula = requireFormula(sMt["collection_rate_formula"],
|
cap.collectionRateFormula = requireFormula(sMt["collection_rate_hz_formula"],
|
||||||
file, sPath + ".collection_rate_formula");
|
file, sPath + ".collection_rate_hz_formula");
|
||||||
def.salvageCapability = std::move(cap);
|
def.salvageCapability = std::move(cap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -662,13 +665,13 @@ ModulesConfig ConfigLoader::loadModules(const std::string& path)
|
|||||||
const std::string rPath = elemPath + ".repair";
|
const std::string rPath = elemPath + ".repair";
|
||||||
const toml::table& rTable = requireTable(mt["repair"], file, rPath);
|
const toml::table& rTable = requireTable(mt["repair"], file, rPath);
|
||||||
toml::table& rMt = const_cast<toml::table&>(rTable);
|
toml::table& rMt = const_cast<toml::table&>(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;
|
ModuleRepairCapability cap;
|
||||||
cap.repairRateFormula = requireFormula(rMt["repair_rate_formula"],
|
cap.repairRateFormula = requireFormula(rMt["repair_rate_hz_formula"],
|
||||||
file, rPath + ".repair_rate_formula");
|
file, rPath + ".repair_rate_hz_formula");
|
||||||
cap.repairRangeFormula = requireFormula(rMt["repair_range_formula"],
|
cap.repairRangeFormula = requireFormula(rMt["repair_range_m_formula"],
|
||||||
file, rPath + ".repair_range_formula");
|
file, rPath + ".repair_range_m_formula");
|
||||||
def.repairCapability = std::move(cap);
|
def.repairCapability = std::move(cap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,23 +5,23 @@
|
|||||||
// Region widths are in tiles (REQ-GW-REGIONS).
|
// Region widths are in tiles (REQ-GW-REGIONS).
|
||||||
struct WorldRegions
|
struct WorldRegions
|
||||||
{
|
{
|
||||||
int asteroidWidth;
|
int asteroidWidth_tiles;
|
||||||
int playerBufferWidth;
|
int playerBufferWidth_tiles;
|
||||||
int contestZoneWidth;
|
int contestZoneWidth_tiles;
|
||||||
int enemyBufferWidth;
|
int enemyBufferWidth_tiles;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Asteroid expansion (REQ-EXP-UNLOCK, REQ-EXP-COST).
|
// Asteroid expansion (REQ-EXP-UNLOCK, REQ-EXP-COST).
|
||||||
struct WorldExpansion
|
struct WorldExpansion
|
||||||
{
|
{
|
||||||
int columnsPerExpansion;
|
int columnsPerExpansion_tiles;
|
||||||
int costBuildingBlocks;
|
int costBuildingBlocks;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Push effects (REQ-PSH-*, REQ-WAV-BOSS-ADVANCE).
|
// Push effects (REQ-PSH-*, REQ-WAV-BOSS-ADVANCE).
|
||||||
struct WorldPush
|
struct WorldPush
|
||||||
{
|
{
|
||||||
int pushExpandColumns;
|
int pushExpandColumns_tiles;
|
||||||
double bossAdvanceSeconds; // boss countdown advanced by this much per push
|
double bossAdvanceSeconds; // boss countdown advanced by this much per push
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ struct WorldConfig
|
|||||||
double scrapDespawnSeconds; // REQ-RES-SCRAP-DROP
|
double scrapDespawnSeconds; // REQ-RES-SCRAP-DROP
|
||||||
double tileSize_m; // metres per tile (REQ-GW-TILE-SIZE)
|
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)
|
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
|
double departureIntervalSeconds; // REQ-SHP-RALLY
|
||||||
|
|
||||||
WorldRegions regions;
|
WorldRegions regions;
|
||||||
|
|||||||
@@ -493,7 +493,7 @@ void BuildingSystem::tickConstruction(Tick currentTick)
|
|||||||
}
|
}
|
||||||
else if (front.type == BuildingType::TunnelEntry)
|
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)
|
else if (front.type == BuildingType::TunnelExit)
|
||||||
{
|
{
|
||||||
@@ -993,7 +993,7 @@ void BuildingSystem::rotateInPlace(BuildingId id, Rotation newRotation)
|
|||||||
else if (b.type == BuildingType::TunnelEntry)
|
else if (b.type == BuildingType::TunnelEntry)
|
||||||
{
|
{
|
||||||
m_belts.removeTile(b.anchor);
|
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)
|
else if (b.type == BuildingType::TunnelExit)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ void Simulation::placeInitialStructures()
|
|||||||
const ParsedSurfaceMask psParsed =
|
const ParsedSurfaceMask psParsed =
|
||||||
parseSurfaceMask(m_config.stations.playerStation.surfaceMask, Rotation::East);
|
parseSurfaceMask(m_config.stations.playerStation.surfaceMask, Rotation::East);
|
||||||
const int psAnchorX =
|
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<double>(m_config.stations.playerStation.level);
|
const double psLevel = static_cast<double>(m_config.stations.playerStation.level);
|
||||||
const float psHp = static_cast<float>(
|
const float psHp = static_cast<float>(
|
||||||
m_config.stations.playerStation.hpFormula.evaluate(psLevel));
|
m_config.stations.playerStation.hpFormula.evaluate(psLevel));
|
||||||
@@ -309,9 +309,9 @@ void Simulation::placeEnemyStationSet(int generation)
|
|||||||
const ParsedSurfaceMask esParsed =
|
const ParsedSurfaceMask esParsed =
|
||||||
parseSurfaceMask(m_config.stations.enemyStation.surfaceMask, Rotation::East);
|
parseSurfaceMask(m_config.stations.enemyStation.surfaceMask, Rotation::East);
|
||||||
|
|
||||||
const int rightEdgeX = m_config.world.regions.playerBufferWidth
|
const int rightEdgeX = m_config.world.regions.playerBufferWidth_tiles
|
||||||
+ m_config.world.regions.contestZoneWidth
|
+ m_config.world.regions.contestZoneWidth_tiles
|
||||||
+ generation * m_config.world.push.pushExpandColumns;
|
+ generation * m_config.world.push.pushExpandColumns_tiles;
|
||||||
const int anchorX = rightEdgeX - esParsed.footprint.width();
|
const int anchorX = rightEdgeX - esParsed.footprint.width();
|
||||||
|
|
||||||
const double genD = static_cast<double>(generation);
|
const double genD = static_cast<double>(generation);
|
||||||
|
|||||||
@@ -186,11 +186,11 @@ std::vector<WaveSystem::SpawnEntry> WaveSystem::selectWaveShips(double& budget,
|
|||||||
|
|
||||||
// Enemy spawn buffer X range for the current generation.
|
// Enemy spawn buffer X range for the current generation.
|
||||||
const float leftX = static_cast<float>(
|
const float leftX = static_cast<float>(
|
||||||
m_config.world.regions.playerBufferWidth
|
m_config.world.regions.playerBufferWidth_tiles
|
||||||
+ m_config.world.regions.contestZoneWidth
|
+ m_config.world.regions.contestZoneWidth_tiles
|
||||||
+ m_generation * m_config.world.push.pushExpandColumns);
|
+ m_generation * m_config.world.push.pushExpandColumns_tiles);
|
||||||
const float rightX = leftX
|
const float rightX = leftX
|
||||||
+ static_cast<float>(m_config.world.regions.enemyBufferWidth) - 1.0f;
|
+ static_cast<float>(m_config.world.regions.enemyBufferWidth_tiles) - 1.0f;
|
||||||
|
|
||||||
std::uniform_real_distribution<float> xDist(leftX, rightX);
|
std::uniform_real_distribution<float> xDist(leftX, rightX);
|
||||||
std::uniform_int_distribution<int> yDist(0, worldHeightTiles - 1);
|
std::uniform_int_distribution<int> yDist(0, worldHeightTiles - 1);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "tracing.h"
|
#include "tracing.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
|
|||||||
@@ -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",
|
TEST_CASE("BehaviorSystem: salvage module does not collect scrap beyond its collection range",
|
||||||
"[behavior]")
|
"[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;
|
Fixture f;
|
||||||
const ShipLayoutConfig salvageLayout = makeSingleModuleLayout("salvager");
|
const ShipLayoutConfig salvageLayout = makeSingleModuleLayout("salvager");
|
||||||
const entt::entity ship = f.ships.spawn("salvage_ship", 1, QVector2D(0.0f, 0.0f),
|
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",
|
TEST_CASE("BehaviorSystem: salvage module collects scrap within its collection range",
|
||||||
"[behavior]")
|
"[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;
|
Fixture f;
|
||||||
const ShipLayoutConfig salvageLayout = makeSingleModuleLayout("salvager");
|
const ShipLayoutConfig salvageLayout = makeSingleModuleLayout("salvager");
|
||||||
const entt::entity ship = f.ships.spawn("salvage_ship", 1, QVector2D(0.0f, 0.0f),
|
const entt::entity ship = f.ships.spawn("salvage_ship", 1, QVector2D(0.0f, 0.0f),
|
||||||
|
|||||||
@@ -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.heightTiles == 60);
|
||||||
REQUIRE(cfg.world.refundPercentage == 75);
|
REQUIRE(cfg.world.refundPercentage == 75);
|
||||||
REQUIRE(cfg.world.beltSpeed_tps == Approx(2.0));
|
REQUIRE(cfg.world.beltSpeed_tps == Approx(2.0));
|
||||||
REQUIRE(cfg.world.regions.asteroidWidth == 40);
|
REQUIRE(cfg.world.regions.asteroidWidth_tiles == 40);
|
||||||
REQUIRE(cfg.world.regions.playerBufferWidth == 10);
|
REQUIRE(cfg.world.regions.playerBufferWidth_tiles == 10);
|
||||||
REQUIRE(cfg.world.regions.enemyBufferWidth == 15);
|
REQUIRE(cfg.world.regions.enemyBufferWidth_tiles == 15);
|
||||||
REQUIRE(cfg.world.expansion.columnsPerExpansion == 10);
|
REQUIRE(cfg.world.expansion.columnsPerExpansion_tiles == 10);
|
||||||
REQUIRE(cfg.world.push.bossAdvanceSeconds == Approx(60.0));
|
REQUIRE(cfg.world.push.bossAdvanceSeconds == Approx(60.0));
|
||||||
|
|
||||||
// Spot-check that a config-derived formula computes as expected.
|
// Spot-check that a config-derived formula computes as expected.
|
||||||
@@ -161,21 +161,21 @@ scrap_despawn_seconds = 30
|
|||||||
tile_size_m = 10
|
tile_size_m = 10
|
||||||
belt_speed_mps = 20
|
belt_speed_mps = 20
|
||||||
starting_building_blocks = 100
|
starting_building_blocks = 100
|
||||||
tunnel_max_distance = 10
|
tunnel_max_distance_tiles = 10
|
||||||
departure_interval_seconds = 20
|
departure_interval_seconds = 20
|
||||||
|
|
||||||
[regions]
|
[regions]
|
||||||
asteroid_width = 40
|
asteroid_width_tiles = 40
|
||||||
player_buffer_width = 10
|
player_buffer_width_tiles = 10
|
||||||
contest_zone_width = 30
|
contest_zone_width_tiles = 30
|
||||||
# enemy_buffer_width intentionally missing
|
# enemy_buffer_width_tiles intentionally missing
|
||||||
|
|
||||||
[expansion]
|
[expansion]
|
||||||
columns_per_expansion = 10
|
columns_per_expansion_tiles = 10
|
||||||
cost_building_blocks = 200
|
cost_building_blocks = 200
|
||||||
|
|
||||||
[push]
|
[push]
|
||||||
push_expand_columns = 20
|
push_expand_columns_tiles = 20
|
||||||
scaling_factor = 1.2
|
scaling_factor = 1.2
|
||||||
|
|
||||||
[waves]
|
[waves]
|
||||||
@@ -194,7 +194,7 @@ spawn_duration_seconds = 10
|
|||||||
catch (const std::runtime_error& e)
|
catch (const std::runtime_error& e)
|
||||||
{
|
{
|
||||||
const std::string msg = e.what();
|
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
|
tile_size_m = 10
|
||||||
belt_speed_mps = 20
|
belt_speed_mps = 20
|
||||||
starting_building_blocks = 100
|
starting_building_blocks = 100
|
||||||
tunnel_max_distance = 10
|
tunnel_max_distance_tiles = 10
|
||||||
departure_interval_seconds = 20
|
departure_interval_seconds = 20
|
||||||
|
|
||||||
[regions]
|
[regions]
|
||||||
asteroid_width = 40
|
asteroid_width_tiles = 40
|
||||||
player_buffer_width = 10
|
player_buffer_width_tiles = 10
|
||||||
contest_zone_width = 30
|
contest_zone_width_tiles = 30
|
||||||
enemy_buffer_width = 15
|
enemy_buffer_width_tiles = 15
|
||||||
|
|
||||||
[expansion]
|
[expansion]
|
||||||
columns_per_expansion = 10
|
columns_per_expansion_tiles = 10
|
||||||
cost_building_blocks = 200
|
cost_building_blocks = 200
|
||||||
|
|
||||||
[push]
|
[push]
|
||||||
push_expand_columns = 20
|
push_expand_columns_tiles = 20
|
||||||
boss_advance_seconds = 60
|
boss_advance_seconds = 60
|
||||||
|
|
||||||
[waves]
|
[waves]
|
||||||
@@ -259,17 +259,17 @@ tile_size_m = 10
|
|||||||
belt_speed_mps = 20
|
belt_speed_mps = 20
|
||||||
|
|
||||||
[regions]
|
[regions]
|
||||||
asteroid_width = 40
|
asteroid_width_tiles = 40
|
||||||
player_buffer_width = 10
|
player_buffer_width_tiles = 10
|
||||||
contest_zone_width = 30
|
contest_zone_width_tiles = 30
|
||||||
enemy_buffer_width = 15
|
enemy_buffer_width_tiles = 15
|
||||||
|
|
||||||
[expansion]
|
[expansion]
|
||||||
columns_per_expansion = 10
|
columns_per_expansion_tiles = 10
|
||||||
cost_building_blocks = 200
|
cost_building_blocks = 200
|
||||||
|
|
||||||
[push]
|
[push]
|
||||||
push_expand_columns = 20
|
push_expand_columns_tiles = 20
|
||||||
scaling_factor = 1.2
|
scaling_factor = 1.2
|
||||||
|
|
||||||
[waves]
|
[waves]
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ TEST_CASE("Ship spawn: additive sensor module applies correctly", "[modules]")
|
|||||||
QVector2D(5.0f, 5.0f), false, layout);
|
QVector2D(5.0f, 5.0f), false, layout);
|
||||||
|
|
||||||
REQUIRE(sim.admin().isValid(e));
|
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
|
// final = baseRange_tiles * 1.0 + 10 = baseRange_tiles + 10
|
||||||
CHECK(sim.admin().get<SensorRangeComponent>(e).value_tiles == Approx(baseRange_tiles + 10.0f));
|
CHECK(sim.admin().get<SensorRangeComponent>(e).value_tiles == Approx(baseRange_tiles + 10.0f));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,10 +110,10 @@ TEST_CASE("ShipSystem: interceptor level 1 stats match config formulas", "[ship]
|
|||||||
// hp_formula = "40 + 5*x" at x=1 → 45
|
// hp_formula = "40 + 5*x" at x=1 → 45
|
||||||
REQUIRE(admin.get<HealthComponent>(e).maxHp == Approx(45.0f));
|
REQUIRE(admin.get<HealthComponent>(e).maxHp == Approx(45.0f));
|
||||||
REQUIRE(admin.get<HealthComponent>(e).hp == Approx(45.0f));
|
REQUIRE(admin.get<HealthComponent>(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<SensorRangeComponent>(e).value_tiles == Approx(200.0f));
|
REQUIRE(admin.get<SensorRangeComponent>(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);
|
const entt::entity wc = firstWeaponChild(admin, e);
|
||||||
REQUIRE(admin.isValid(wc));
|
REQUIRE(admin.isValid(wc));
|
||||||
REQUIRE(admin.get<WeaponComponent>(wc).damage == Approx(2.0f));
|
REQUIRE(admin.get<WeaponComponent>(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));
|
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<float>(kTickRateHz);
|
const float expected = 2000.0f / 10.0f / static_cast<float>(kTickRateHz);
|
||||||
REQUIRE(admin.get<DynamicBodyComponent>(e).maxSpeed_tpt == Approx(expected));
|
REQUIRE(admin.get<DynamicBodyComponent>(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 ShipLayoutConfig layout = makeSingleModuleLayout("salvager");
|
||||||
const entt::entity e = ss.spawn("salvage_ship", 1, QVector2D(0.0f, 0.0f), false, layout);
|
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);
|
const entt::entity sc = firstSalvageChild(admin, e);
|
||||||
REQUIRE(admin.isValid(sc));
|
REQUIRE(admin.isValid(sc));
|
||||||
REQUIRE(admin.get<SalvageCargoComponent>(sc).capacity == 10);
|
REQUIRE(admin.get<SalvageCargoComponent>(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 ShipLayoutConfig layout = makeSingleModuleLayout("repair_tool");
|
||||||
const entt::entity e = ss.spawn("repair_ship", 1, QVector2D(0.0f, 0.0f), false, layout);
|
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<float>(kTickRateHz);
|
const float expectedRate = 6.0f / static_cast<float>(kTickRateHz);
|
||||||
const entt::entity rc = firstRepairChild(admin, e);
|
const entt::entity rc = firstRepairChild(admin, e);
|
||||||
REQUIRE(admin.isValid(rc));
|
REQUIRE(admin.isValid(rc));
|
||||||
REQUIRE(admin.get<RepairToolComponent>(rc).ratePerTick == Approx(expectedRate));
|
REQUIRE(admin.get<RepairToolComponent>(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<RepairToolComponent>(rc).range_tiles == Approx(80.0f));
|
REQUIRE(admin.get<RepairToolComponent>(rc).range_tiles == Approx(80.0f));
|
||||||
REQUIRE(admin.get<RepairBehaviorComponent>(e).maxRepairRange_tiles == Approx(80.0f));
|
REQUIRE(admin.get<RepairBehaviorComponent>(e).maxRepairRange_tiles == Approx(80.0f));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -352,7 +352,7 @@ QRect GameWorldView::viewportRect() const
|
|||||||
|
|
||||||
float GameWorldView::asteroidLeftEdge() const
|
float GameWorldView::asteroidLeftEdge() const
|
||||||
{
|
{
|
||||||
float leftX = -static_cast<float>(m_config->world.regions.asteroidWidth);
|
float leftX = -static_cast<float>(m_config->world.regions.asteroidWidth_tiles);
|
||||||
for (const Building& b : m_sim->buildings().allBuildings())
|
for (const Building& b : m_sim->buildings().allBuildings())
|
||||||
{
|
{
|
||||||
for (const QPoint& cell : b.bodyCells)
|
for (const QPoint& cell : b.bodyCells)
|
||||||
@@ -368,8 +368,8 @@ float GameWorldView::asteroidLeftEdge() const
|
|||||||
|
|
||||||
float GameWorldView::enemyStationRightEdge() const
|
float GameWorldView::enemyStationRightEdge() const
|
||||||
{
|
{
|
||||||
float rightX = static_cast<float>(m_config->world.regions.playerBufferWidth
|
float rightX = static_cast<float>(m_config->world.regions.playerBufferWidth_tiles
|
||||||
+ m_config->world.regions.contestZoneWidth);
|
+ m_config->world.regions.contestZoneWidth_tiles);
|
||||||
m_sim->admin().forEach<StationBodyComponent, FactionComponent>(
|
m_sim->admin().forEach<StationBodyComponent, FactionComponent>(
|
||||||
[&rightX](entt::entity /*e*/, const StationBodyComponent& sb, const FactionComponent& f)
|
[&rightX](entt::entity /*e*/, const StationBodyComponent& sb, const FactionComponent& f)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user