diff --git a/bin/app/data/config/modules.toml b/bin/app/data/config/modules.toml index 91f59c2..b409dab 100644 --- a/bin/app/data/config/modules.toml +++ b/bin/app/data/config/modules.toml @@ -57,7 +57,7 @@ fill_color = "#FF8040" glyph = "Rm" [module.weapon] -damage = 10 +damage = 14 attack_range_m = 70 attack_rate_hz = 1.5 @@ -76,7 +76,7 @@ fill_color = "#FF8040" glyph = "Rl" [module.weapon] -damage = 40 +damage = 52 attack_range_m = 100 attack_rate_hz = 0.8 @@ -94,8 +94,8 @@ fill_color = "#AACC44" glyph = "Sv" [module.salvage] -collection_range_m = 500 -cargo_capacity = 10 +collection_range_m = 60 +cargo_capacity = 20 collection_rate_hz = 0.5 @@ -110,7 +110,7 @@ glyph = "Rp" [module.repair] repair_rate_hz = 1 -repair_amount_hp = 6 +repair_amount_hp = 12 repair_range_m = 80 # ----------------------------------------------------------------------------- @@ -158,7 +158,7 @@ fill_color = "#808080" glyph = "A" [module.health] -added_hp = 40 +added_hp = 1000 [[module]] diff --git a/bin/app/data/config/ships.toml b/bin/app/data/config/ships.toml index 39394a2..876f8a0 100644 --- a/bin/app/data/config/ships.toml +++ b/bin/app/data/config/ships.toml @@ -31,14 +31,14 @@ materials = [{item = "drone_hull", amount = 1}] production_time_seconds = 1 [ship.health] -hp = 3 +hp = 60 [ship.movement] -speed_mps = 40 -main_acceleration_mpss = 2 -maneuvering_acceleration_mpss = 1 -angular_acceleration_radpss = 12.56 -max_rotation_speed_radps = 6.28 +speed_mps = 45 +main_acceleration_mpss = 60 +maneuvering_acceleration_mpss = 30 +angular_acceleration_radpss = 12 +max_rotation_speed_radps = 6 [ship.sensor] sensor_range_m = 150 @@ -66,12 +66,12 @@ materials = [{item = "frigate_hull", amount = 1}] production_time_seconds = 2 [ship.health] -hp = 30 +hp = 300 [ship.movement] -speed_mps = 30 -main_acceleration_mpss = 50 -maneuvering_acceleration_mpss = 25 +speed_mps = 35 +main_acceleration_mpss = 45 +maneuvering_acceleration_mpss = 22 angular_acceleration_radpss = 8 max_rotation_speed_radps = 4 @@ -102,12 +102,12 @@ materials = [{item = "destroyer_hull", amount = 1}] production_time_seconds = 3 [ship.health] -hp = 50 +hp = 550 [ship.movement] -speed_mps = 25 -main_acceleration_mpss = 40 -maneuvering_acceleration_mpss = 20 +speed_mps = 30 +main_acceleration_mpss = 35 +maneuvering_acceleration_mpss = 18 angular_acceleration_radpss = 6 max_rotation_speed_radps = 3 @@ -139,12 +139,12 @@ materials = [{item = "cruiser_hull", amount = 1}] production_time_seconds = 4 [ship.health] -hp = 120 +hp = 1500 [ship.movement] -speed_mps = 20 -main_acceleration_mpss = 30 -maneuvering_acceleration_mpss = 15 +speed_mps = 24 +main_acceleration_mpss = 25 +maneuvering_acceleration_mpss = 12 angular_acceleration_radpss = 4 max_rotation_speed_radps = 2 @@ -179,12 +179,12 @@ materials = [{item = "battlecruiser_hull", amount = 1}] production_time_seconds = 5 [ship.health] -hp = 180 +hp = 2700 [ship.movement] -speed_mps = 18 -main_acceleration_mpss = 25 -maneuvering_acceleration_mpss = 12 +speed_mps = 20 +main_acceleration_mpss = 20 +maneuvering_acceleration_mpss = 10 angular_acceleration_radpss = 3 max_rotation_speed_radps = 1.5 @@ -222,12 +222,12 @@ materials = [{item = "battleship_hull", amount = 1}] production_time_seconds = 6 [ship.health] -hp = 350 +hp = 7500 [ship.movement] -speed_mps = 14 -main_acceleration_mpss = 18 -maneuvering_acceleration_mpss = 8 +speed_mps = 15 +main_acceleration_mpss = 14 +maneuvering_acceleration_mpss = 7 angular_acceleration_radpss = 2 max_rotation_speed_radps = 1 @@ -267,12 +267,12 @@ materials = [{item = "dreadnought_hull", amount = 1}] production_time_seconds = 8 [ship.health] -hp = 800 +hp = 15500 [ship.movement] -speed_mps = 8 -main_acceleration_mpss = 10 -maneuvering_acceleration_mpss = 5 +speed_mps = 10 +main_acceleration_mpss = 8 +maneuvering_acceleration_mpss = 4 angular_acceleration_radpss = 1 max_rotation_speed_radps = 0.5 @@ -308,12 +308,12 @@ materials = [{item = "carrier_hull", amount = 1}] production_time_seconds = 8 [ship.health] -hp = 700 +hp = 15500 [ship.movement] -speed_mps = 9 -main_acceleration_mpss = 10 -maneuvering_acceleration_mpss = 5 +speed_mps = 10 +main_acceleration_mpss = 8 +maneuvering_acceleration_mpss = 4 angular_acceleration_radpss = 1 max_rotation_speed_radps = 0.5 diff --git a/bin/app/data/config/stations.toml b/bin/app/data/config/stations.toml index 94ba18b..15af5a8 100644 --- a/bin/app/data/config/stations.toml +++ b/bin/app/data/config/stations.toml @@ -1,10 +1,18 @@ +# stations.toml +# +# Combat-pass anchors (see docs/content_design.md, "Combat stat anchors"): +# a fresh player defence station holds one early parity wave unaided; the +# enemy station at level 0 matches the player station exactly and scales +# with the push level x. Station scrap drops stay authored (pushing rewards +# are tuned independently of ship production costs, REQ-RES-SCRAP-DROP). + [hq] surface_mask = [ "AAA", "AAA", "AAA", ] -hp_formula = "1000" +hp_formula = "5000" [player_station] surface_mask = [ @@ -12,19 +20,19 @@ surface_mask = [ "SS", ] level = 1 -hp_formula = "300" -damage_formula = "5" -range_m_formula = "200" +hp_formula = "3000" +damage_formula = "25" +range_m_formula = "120" fire_rate_hz_formula = "1" -scrap_drop_formula = "10" +scrap_drop_formula = "40" [enemy_station] surface_mask = [ "SS", "SS", ] -hp_formula = "300 + 150*x" -damage_formula = "2 + 1*x" -range_m_formula = "200" -fire_rate_hz_formula = "1.0 + 0.2*x" -scrap_drop_formula = "10 + 5*x" +hp_formula = "3000 + 1500*x" +damage_formula = "25 + 12*x" +range_m_formula = "120" +fire_rate_hz_formula = "1.0 + 0.1*x" +scrap_drop_formula = "40 + 30*x" diff --git a/bin/app/data/config/world.toml b/bin/app/data/config/world.toml index 3a2a92f..cdac851 100644 --- a/bin/app/data/config/world.toml +++ b/bin/app/data/config/world.toml @@ -2,7 +2,7 @@ height_tiles = 40 refund_percentage = 100 starting_building_blocks = 1000 -scrap_despawn_seconds = 30 +scrap_despawn_seconds = 120 scrap_per_threat = 0.25 tile_size_m = 10 belt_speed_mps = 20 diff --git a/bin/balancing/data/balancing.toml b/bin/balancing/data/balancing.toml index bf210ee..63b90ff 100644 --- a/bin/balancing/data/balancing.toml +++ b/bin/balancing/data/balancing.toml @@ -1,67 +1,396 @@ +# balancing.toml — canonical arena suite for the combat stats pass. +# +# Ship counts are chosen so both teams have (near-)equal total threat, +# using the verified fitted threat values from tools/threat_report.py: +# drone 10.5, frigate 47, destroyer 99, cruiser 233.5, +# battlecruiser 354.5, battleship 722.5, dreadnought 1491.5, +# carrier 1436.5, glass destroyer (8 small guns) 92, repair drone 17. +# Module arrays mirror the ships' default_modules loadouts unless a +# doctrine variant is the point of the arena. +# +# Expectations: mirror matches and equal-threat cross-tier matchups should +# be near-draws (power-per-threat rule); the two-to-one arena must be a +# decisive win for the larger team. "Cruisers vs carrier" is EXPECTED to be +# a loss for the carrier until the drone-launching capability exists — the +# hangar is 224 threat of dead weight. + +# --- mirrors (sanity: symmetric outcomes, fight duration in the 30-60 s band) --- + [[arena]] -name = "Fighters vs Sniper" -height_tiles = 20 +name = "Mirror: drones 20v20" +height_tiles = 30 player_buffer_width_tiles = 10 -contest_zone_width_tiles = 60 +contest_zone_width_tiles = 50 enemy_buffer_width_tiles = 10 [[arena.team]] name = "Alpha" [[arena.team.ship]] schematic = "drone" - count = 5 + count = 20 modules = [ - {type = "laser_cannon_s", x = 1, y = 1, rotation = "east"}, + {type = "railgun_s", x = 0, y = 0, rotation = "east"}, ] [[arena.team]] name = "Beta" [[arena.team.ship]] schematic = "drone" - count = 2 + count = 20 modules = [ - {type = "laser_cannon_s", x = 1, y = 1, rotation = "east"}, - {type = "weapon_stabilizer", x = 1, y = 1, rotation = "east"}, - {type = "weapon_stabilizer", x = 1, y = 1, rotation = "east"}, - {type = "weapon_upgrade", x = 1, y = 1, rotation = "east"}, - {type = "sensor_booster", x = 1, y = 1, rotation = "east"}, - {type = "sensor_booster", x = 1, y = 1, rotation = "east"}, + {type = "railgun_s", x = 0, y = 0, rotation = "east"}, ] [[arena]] -name = "Fighters vs Supported" -height_tiles = 20 +name = "Mirror: cruisers 6v6" +height_tiles = 30 player_buffer_width_tiles = 10 -contest_zone_width_tiles = 60 +contest_zone_width_tiles = 50 enemy_buffer_width_tiles = 10 [[arena.team]] - name = "Fighters" + name = "Alpha" [[arena.team.ship]] - schematic = "drone" - count = 5 + schematic = "cruiser" + count = 6 modules = [ - {type = "laser_cannon_s", x = 1, y = 1, rotation = "east"}, + {type = "railgun_m", x = 0, y = 1, rotation = "east"}, + {type = "railgun_m", x = 2, y = 1, rotation = "east"}, + {type = "armor_plates", x = 1, y = 0, rotation = "east"}, + {type = "maneuvering_thrusters", x = 1, y = 3, rotation = "east"}, ] [[arena.team]] - name = "Supported" + name = "Beta" [[arena.team.ship]] - schematic = "drone" - count = 3 + schematic = "cruiser" + count = 6 modules = [ - {type = "laser_cannon_s", x = 1, y = 1, rotation = "east"}, + {type = "railgun_m", x = 0, y = 1, rotation = "east"}, + {type = "railgun_m", x = 2, y = 1, rotation = "east"}, + {type = "armor_plates", x = 1, y = 0, rotation = "east"}, + {type = "maneuvering_thrusters", x = 1, y = 3, rotation = "east"}, ] +[[arena]] +name = "Mirror: battleships 2v2" +height_tiles = 30 +player_buffer_width_tiles = 10 +contest_zone_width_tiles = 50 +enemy_buffer_width_tiles = 10 + + [[arena.team]] + name = "Alpha" + [[arena.team.ship]] + schematic = "battleship" + count = 2 + modules = [ + {type = "railgun_l", x = 1, y = 0, rotation = "east"}, + {type = "railgun_m", x = 1, y = 3, rotation = "east"}, + {type = "railgun_m", x = 3, y = 3, rotation = "east"}, + {type = "weapon_stabilizer", x = 4, y = 1, rotation = "east"}, + {type = "railgun_s", x = 4, y = 0, rotation = "east"}, + {type = "railgun_s", x = 0, y = 1, rotation = "east"}, + ] + + [[arena.team]] + name = "Beta" + [[arena.team.ship]] + schematic = "battleship" + count = 2 + modules = [ + {type = "railgun_l", x = 1, y = 0, rotation = "east"}, + {type = "railgun_m", x = 1, y = 3, rotation = "east"}, + {type = "railgun_m", x = 3, y = 3, rotation = "east"}, + {type = "weapon_stabilizer", x = 4, y = 1, rotation = "east"}, + {type = "railgun_s", x = 4, y = 0, rotation = "east"}, + {type = "railgun_s", x = 0, y = 1, rotation = "east"}, + ] + +# --- equal-threat cross-tier matchups (power-per-threat: expect near-draws) --- + +[[arena]] +name = "Drone swarm vs cruisers (462 vs 467)" +height_tiles = 30 +player_buffer_width_tiles = 10 +contest_zone_width_tiles = 50 +enemy_buffer_width_tiles = 10 + + [[arena.team]] + name = "Swarm" + [[arena.team.ship]] + schematic = "drone" + count = 44 + modules = [ + {type = "railgun_s", x = 0, y = 0, rotation = "east"}, + ] + + [[arena.team]] + name = "Cruisers" + [[arena.team.ship]] + schematic = "cruiser" + count = 2 + modules = [ + {type = "railgun_m", x = 0, y = 1, rotation = "east"}, + {type = "railgun_m", x = 2, y = 1, rotation = "east"}, + {type = "armor_plates", x = 1, y = 0, rotation = "east"}, + {type = "maneuvering_thrusters", x = 1, y = 3, rotation = "east"}, + ] + +[[arena]] +name = "Frigates vs battleship (705 vs 723)" +height_tiles = 30 +player_buffer_width_tiles = 10 +contest_zone_width_tiles = 50 +enemy_buffer_width_tiles = 10 + + [[arena.team]] + name = "Frigates" + [[arena.team.ship]] + schematic = "frigate" + count = 15 + modules = [ + {type = "railgun_s", x = 1, y = 0, rotation = "east"}, + {type = "railgun_s", x = 2, y = 1, rotation = "east"}, + {type = "maneuvering_thrusters", x = 0, y = 1, rotation = "east"}, + ] + + [[arena.team]] + name = "Battleship" + [[arena.team.ship]] + schematic = "battleship" + count = 1 + modules = [ + {type = "railgun_l", x = 1, y = 0, rotation = "east"}, + {type = "railgun_m", x = 1, y = 3, rotation = "east"}, + {type = "railgun_m", x = 3, y = 3, rotation = "east"}, + {type = "weapon_stabilizer", x = 4, y = 1, rotation = "east"}, + {type = "railgun_s", x = 4, y = 0, rotation = "east"}, + {type = "railgun_s", x = 0, y = 1, rotation = "east"}, + ] + +[[arena]] +name = "Destroyers vs dreadnought (1485 vs 1492)" +height_tiles = 40 +player_buffer_width_tiles = 10 +contest_zone_width_tiles = 50 +enemy_buffer_width_tiles = 10 + + [[arena.team]] + name = "Destroyers" + [[arena.team.ship]] + schematic = "destroyer" + count = 15 + modules = [ + {type = "railgun_s", x = 0, y = 0, rotation = "east"}, + {type = "railgun_s", x = 2, y = 0, rotation = "east"}, + {type = "railgun_s", x = 4, y = 0, rotation = "east"}, + {type = "armor_plates", x = 0, y = 1, rotation = "east"}, + {type = "sensor_booster", x = 3, y = 1, rotation = "east"}, + ] + + [[arena.team]] + name = "Dreadnought" + [[arena.team.ship]] + schematic = "dreadnought" + count = 1 + modules = [ + {type = "railgun_l", x = 0, y = 1, rotation = "east"}, + {type = "railgun_l", x = 4, y = 1, rotation = "east"}, + {type = "railgun_l", x = 8, y = 1, rotation = "east"}, + {type = "armor_plates", x = 3, y = 0, rotation = "east"}, + {type = "armor_plates", x = 5, y = 0, rotation = "east"}, + {type = "armor_plates", x = 2, y = 4, rotation = "east"}, + {type = "armor_plates", x = 7, y = 4, rotation = "east"}, + {type = "railgun_s", x = 7, y = 0, rotation = "east"}, + ] + +[[arena]] +name = "Cruisers vs carrier (1401 vs 1437, carrier expected to lose)" +height_tiles = 40 +player_buffer_width_tiles = 10 +contest_zone_width_tiles = 50 +enemy_buffer_width_tiles = 10 + + [[arena.team]] + name = "Cruisers" + [[arena.team.ship]] + schematic = "cruiser" + count = 6 + modules = [ + {type = "railgun_m", x = 0, y = 1, rotation = "east"}, + {type = "railgun_m", x = 2, y = 1, rotation = "east"}, + {type = "armor_plates", x = 1, y = 0, rotation = "east"}, + {type = "maneuvering_thrusters", x = 1, y = 3, rotation = "east"}, + ] + + [[arena.team]] + name = "Carrier" + [[arena.team.ship]] + schematic = "carrier" + count = 1 + modules = [ + {type = "drone_hangar", x = 2, y = 0, rotation = "east"}, + {type = "railgun_m", x = 3, y = 2, rotation = "east"}, + {type = "railgun_m", x = 6, y = 2, rotation = "east"}, + {type = "armor_plates", x = 0, y = 1, rotation = "east"}, + {type = "armor_plates", x = 8, y = 1, rotation = "east"}, + {type = "sensor_booster", x = 3, y = 4, rotation = "east"}, + ] + +[[arena]] +name = "Mixed mid vs battlecruisers (1394 vs 1418)" +height_tiles = 40 +player_buffer_width_tiles = 10 +contest_zone_width_tiles = 50 +enemy_buffer_width_tiles = 10 + + [[arena.team]] + name = "Mixed" + [[arena.team.ship]] + schematic = "destroyer" + count = 7 + modules = [ + {type = "railgun_s", x = 0, y = 0, rotation = "east"}, + {type = "railgun_s", x = 2, y = 0, rotation = "east"}, + {type = "railgun_s", x = 4, y = 0, rotation = "east"}, + {type = "armor_plates", x = 0, y = 1, rotation = "east"}, + {type = "sensor_booster", x = 3, y = 1, rotation = "east"}, + ] + [[arena.team.ship]] + schematic = "cruiser" + count = 3 + modules = [ + {type = "railgun_m", x = 0, y = 1, rotation = "east"}, + {type = "railgun_m", x = 2, y = 1, rotation = "east"}, + {type = "armor_plates", x = 1, y = 0, rotation = "east"}, + {type = "maneuvering_thrusters", x = 1, y = 3, rotation = "east"}, + ] + + [[arena.team]] + name = "Battlecruisers" + [[arena.team.ship]] + schematic = "battlecruiser" + count = 4 + modules = [ + {type = "railgun_m", x = 0, y = 0, rotation = "east"}, + {type = "railgun_m", x = 4, y = 0, rotation = "east"}, + {type = "railgun_m", x = 2, y = 1, rotation = "east"}, + {type = "armor_plates", x = 2, y = 3, rotation = "east"}, + {type = "railgun_s", x = 1, y = 2, rotation = "east"}, + {type = "railgun_s", x = 4, y = 2, rotation = "east"}, + ] + +# --- asymmetric checks --- + +[[arena]] +name = "Two to one (must be decisive)" +height_tiles = 30 +player_buffer_width_tiles = 10 +contest_zone_width_tiles = 50 +enemy_buffer_width_tiles = 10 + + [[arena.team]] + name = "Six" + [[arena.team.ship]] + schematic = "frigate" + count = 6 + modules = [ + {type = "railgun_s", x = 1, y = 0, rotation = "east"}, + {type = "railgun_s", x = 2, y = 1, rotation = "east"}, + {type = "maneuvering_thrusters", x = 0, y = 1, rotation = "east"}, + ] + + [[arena.team]] + name = "Three" + [[arena.team.ship]] + schematic = "frigate" + count = 3 + modules = [ + {type = "railgun_s", x = 1, y = 0, rotation = "east"}, + {type = "railgun_s", x = 2, y = 1, rotation = "east"}, + {type = "maneuvering_thrusters", x = 0, y = 1, rotation = "east"}, + ] + +[[arena]] +name = "Armored vs glass destroyers (1188 vs 1196)" +height_tiles = 30 +player_buffer_width_tiles = 10 +contest_zone_width_tiles = 50 +enemy_buffer_width_tiles = 10 + + [[arena.team]] + name = "Armored" + [[arena.team.ship]] + schematic = "destroyer" + count = 12 + modules = [ + {type = "railgun_s", x = 0, y = 0, rotation = "east"}, + {type = "railgun_s", x = 2, y = 0, rotation = "east"}, + {type = "railgun_s", x = 4, y = 0, rotation = "east"}, + {type = "armor_plates", x = 0, y = 1, rotation = "east"}, + {type = "sensor_booster", x = 3, y = 1, rotation = "east"}, + ] + + [[arena.team]] + name = "Glass" + [[arena.team.ship]] + schematic = "destroyer" + count = 13 + modules = [ + {type = "railgun_s", x = 0, y = 0, rotation = "east"}, + {type = "railgun_s", x = 2, y = 0, rotation = "east"}, + {type = "railgun_s", x = 4, y = 0, rotation = "east"}, + {type = "railgun_s", x = 0, y = 1, rotation = "east"}, + {type = "railgun_s", x = 1, y = 1, rotation = "east"}, + {type = "railgun_s", x = 2, y = 1, rotation = "east"}, + {type = "railgun_s", x = 3, y = 1, rotation = "east"}, + {type = "railgun_s", x = 4, y = 1, rotation = "east"}, + ] + +[[arena]] +name = "Repair escort vs raw numbers (444 vs 444)" +height_tiles = 30 +player_buffer_width_tiles = 10 +contest_zone_width_tiles = 50 +enemy_buffer_width_tiles = 10 + + [[arena.team]] + name = "Escorted" + [[arena.team.ship]] + schematic = "frigate" + count = 8 + modules = [ + {type = "railgun_s", x = 1, y = 0, rotation = "east"}, + {type = "railgun_s", x = 2, y = 1, rotation = "east"}, + {type = "maneuvering_thrusters", x = 0, y = 1, rotation = "east"}, + ] + [[arena.team.ship]] + schematic = "drone" + count = 4 + modules = [ + {type = "repair_tool", x = 0, y = 0, rotation = "east"}, + ] + + [[arena.team]] + name = "Raw" + [[arena.team.ship]] + schematic = "frigate" + count = 9 + modules = [ + {type = "railgun_s", x = 1, y = 0, rotation = "east"}, + {type = "railgun_s", x = 2, y = 1, rotation = "east"}, + {type = "maneuvering_thrusters", x = 0, y = 1, rotation = "east"}, + ] [[arena.team.ship]] schematic = "drone" count = 2 modules = [ - {type = "repair_tool", x = 1, y = 1, rotation = "east"}, + {type = "railgun_s", x = 0, y = 0, rotation = "east"}, ] [[arena]] -name = "Stations and Ships" +name = "Station assault (2 stations + 105 vs 315)" height_tiles = 60 player_buffer_width_tiles = 15 contest_zone_width_tiles = 40 @@ -71,9 +400,9 @@ enemy_buffer_width_tiles = 15 name = "Fortified" [[arena.team.ship]] schematic = "drone" - count = 3 + count = 10 modules = [ - {type = "laser_cannon_s", x = 1, y = 1, rotation = "east"}, + {type = "railgun_s", x = 0, y = 0, rotation = "east"}, ] [[arena.team.station]] type = "player_station" @@ -90,7 +419,7 @@ enemy_buffer_width_tiles = 15 name = "Swarm" [[arena.team.ship]] schematic = "drone" - count = 8 + count = 30 modules = [ - {type = "laser_cannon_s", x = 1, y = 1, rotation = "east"}, + {type = "railgun_s", x = 0, y = 0, rotation = "east"}, ] diff --git a/docs/content_design.md b/docs/content_design.md index bb2a727..80533df 100644 --- a/docs/content_design.md +++ b/docs/content_design.md @@ -440,6 +440,73 @@ typical loadouts double as the `default_modules` for enemy waves): scrap-free recipe produces that item, mirroring the reprocessing-path rule (see progression_design.md action items). +### Combat stat anchors — first pass + +All combat stats derive from a few anchors so that power-per-threat holds +by construction; the arena suite in `bin/balancing/data/balancing.toml` +verifies it empirically. Change the anchors first, re-derive stats second. + +- **Weapon DPS per threat pays a concentration tax that grows with gun + size**: railgun_s 2 dmg × 2.0 Hz = 4.0 DPS at 6.5 threat (0.62), + range 50; railgun_m 14 × 1.5 = 21 at 43.5 (0.48), range 70; + railgun_l 52 × 0.8 = 41.6 at 102 (0.41), range 100. Arena round 1 + (see below) showed that at equal threat, concentrated fleets win + flawlessly with a flat-ish DPS curve — concentration itself (focus + survivability, range, no losses-to-attrition of own DPS) is worth + paying for, so bigger guns get less raw DPS per threat. +- **Hull HP = 15 per threat of hull contribution** (incl. ship base + time): drone 60, frigate 300, destroyer 550, cruiser 1500, + battlecruiser 2700, battleship 7500, dreadnought/carrier 15500. +- **Armor HP ≈ 31 per threat** — a strong premium over hull HP (15) + because armor is pure HP with no capability, and fights snowball: + killing enemies removes their DPS, while surviving merely delays — + so a point of HP must be cheaper than a point of DPS. + armor_plates adds 1000 HP at 32 threat (arena round 1: at 640 the + all-guns destroyer beat the armored one). +- **TTK check:** a mirror cruiser duel (51 DPS vs 2140 EHP) lasts ~42 s — + inside the 30–60 s parity-fight band implied by the 25-ship fleet + target. Capital mirrors run longer (~90 s), deliberately. +- **Repair ≈ 1 HP/s per threat:** repair_tool heals 12 HP × 1 Hz at 13 + threat (arena round 1: at 2 HP/s-per-threat the escorted team won + flawlessly — in-combat sustain effectively removes enemy DPS and must + be priced like DPS, not like HP). Salvager: collection range 60 (was + a nonsensical 500, beyond any sensor), cargo 20, 0.5 collections/s. + `scrap_despawn_seconds` raised 30 → 120: a capital kill drops + hundreds of scrap collected one per cycle, so 30 s despawned nearly + everything. +- **Mobility ladder is monotone in size** — small = fast and nimble + (drone 45 m/s, 60 accel) down to capitals (10 m/s, 8 accel); the old + placeholder had the drone as the least agile ship. Sensor ranges keep + the existing 150→350 ladder; all weapon ranges stay below sensor + ranges. +- **Weapon modifiers are capital economy:** ×1.2 damage at 22.5 threat + beats adding a gun once a ship carries more than ~68 threat of weapons + — so modifiers pay off on gun-heavy big hulls and are a waste on small + ones. Kept at ×1.2/×1.2/(×1.5 range, ×0.8 rate) deliberately. +- **Stations:** a fresh player station (3000 HP, 25 dmg × 1 Hz, + range 120) holds one early parity wave unaided; the enemy station at + level 0 matches it exactly and scales per push level + (+1500 HP, +12 dmg, +0.1 Hz per level). HQ 5000 HP. Range reduced + from 200 in round 1: at 200 (4× a small gun's range) two stations + annihilated a 3× threat swarm through approach fire alone — range, + not HP, is the station dominance lever. +- **Known imbalance, accepted:** the carrier loses its equal-threat + arena until the drone-launching capability exists — the hangar is 224 + threat of dead weight. Do not compensate with stats; fix by + implementing drones. + +**Arena round 1 results (2026-07-04):** mirrors snowball as expected +(40–50% of the winner survives) but team 1 won all three mirrors — +possible sim-side bias, under investigation (progression_design.md +action items 6–7). All four equal-threat cross-tier matchups were +flawless wins for the concentrated side; glass destroyers beat armored; +repair escort beat raw numbers flawlessly; two stations shrugged off a +3× threat swarm. Round-2 knob changes: concentration tax on m/l gun +DPS (17→14, 70→52 damage), armor 640→1000, repair 25→12, station range +200→120. Gun ranges deliberately untouched — if concentrated fleets +still win flawlessly in round 2, the range ladder (kiting) is the next +suspect, ahead of further DPS cuts. + ## Balancing targets (first pass, July 2026) The six root numbers for the balancing pass. Every derived value (threat diff --git a/docs/progression_design.md b/docs/progression_design.md index 6e9fb17..564f941 100644 --- a/docs/progression_design.md +++ b/docs/progression_design.md @@ -373,3 +373,15 @@ in `requirements.md` and the git history). Still open: one matching deposit tile). Touches REQ-BLD-MINER ("every asteroid tile is equivalent" no longer holds), REQ-GW-ASTEROID-EXPAND / REQ-EXP-*, `world.toml`, and `visuals.toml`. +6. **Investigate team-1 bias in balancing arenas.** In arena round 1 + (July 2026), team 1 won all three mirror matches with large margins + (8/20, 3/6, 1/2 survivors). Re-run mirrors repeatedly; if team 1 + wins consistently, find and fix the asymmetry (spawn geometry, tick + processing order, target-selection tie-breaking). Mirror matches + must be statistically symmetric or every arena result carries a + side-dependent error. +7. **Log remaining HP in the balancing arena results.** Win/loss alone + hides the margin ("1/1 battleship" can mean 95% or 5% HP left). Add + the surviving entities' remaining HP fraction (or a per-team total + EHP percentage) to the arena end state, so equal-threat matchups can + be tuned toward near-draws quantitatively.