Derive ship scrap drop from threat
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
#include "HealthComponent.h"
|
||||
#include "HqProxyComponent.h"
|
||||
#include "ModuleOwnerComponent.h"
|
||||
#include "ScrapDataComponent.h"
|
||||
#include "ScrapSystem.h"
|
||||
#include "ShipSystem.h"
|
||||
#include "Simulation.h"
|
||||
@@ -406,24 +407,19 @@ TEST_CASE("CombatSystem: scrap is spawned on ship death", "[combat]")
|
||||
{
|
||||
Simulation sim(loadConfig(), 42);
|
||||
|
||||
const ShipDef* droppingDef = nullptr;
|
||||
for (const ShipDef& def : sim.config().ships.ships)
|
||||
{
|
||||
if (def.loot.scrapDrop > 0)
|
||||
{
|
||||
droppingDef = &def;
|
||||
break;
|
||||
}
|
||||
}
|
||||
REQUIRE(droppingDef != nullptr);
|
||||
|
||||
const entt::entity ship = sim.ships().spawn(droppingDef->id, 1,
|
||||
// Scrap dropped on death is derived from the ship's as-built threat cost
|
||||
// (REQ-RES-SCRAP-DROP): round(threat * scrap_per_threat). The interceptor's
|
||||
// threat is 59.0 and the test config sets scrap_per_threat = 1.0, so it drops
|
||||
// round(59.0 * 1.0) = 59 scrap.
|
||||
const entt::entity ship = sim.ships().spawn("interceptor", 1,
|
||||
QVector2D(10.0f, 10.0f));
|
||||
sim.admin().get<HealthComponent>(ship).hp = -1.0f;
|
||||
|
||||
sim.tick();
|
||||
|
||||
REQUIRE(!sim.scraps().allScrapInfo().empty());
|
||||
const std::vector<ScrapInfo> scraps = sim.scraps().allScrapInfo();
|
||||
REQUIRE(scraps.size() == 1);
|
||||
CHECK(sim.admin().get<ScrapDataComponent>(scraps[0].entity).amount == 59);
|
||||
}
|
||||
|
||||
TEST_CASE("CombatSystem: HQ death sets game over", "[combat]")
|
||||
|
||||
@@ -78,6 +78,7 @@ TEST_CASE("ConfigLoader loads the committed bin/config/ configs end-to-end", "[c
|
||||
REQUIRE(cfg.world.push.bossAdvanceSeconds == Approx(60.0));
|
||||
REQUIRE(cfg.world.orbitFactor == Approx(0.8));
|
||||
REQUIRE(cfg.world.rallyOrbitRadius_tiles == Approx(5.0));
|
||||
REQUIRE(cfg.world.scrapPerThreat == Approx(1.0));
|
||||
|
||||
// Spot-check that a config-derived formula computes as expected.
|
||||
// threat_rate_formula = "x": evaluates to the input value.
|
||||
@@ -167,6 +168,7 @@ TEST_CASE("Missing field in world.toml is rejected with the field path", "[confi
|
||||
height_tiles = 60
|
||||
refund_percentage = 75
|
||||
scrap_despawn_seconds = 30
|
||||
scrap_per_threat = 0.01
|
||||
tile_size_m = 10
|
||||
belt_speed_mps = 20
|
||||
starting_building_blocks = 100
|
||||
@@ -217,6 +219,7 @@ TEST_CASE("Malformed formula in world.toml is rejected with field identification
|
||||
height_tiles = 60
|
||||
refund_percentage = 75
|
||||
scrap_despawn_seconds = 30
|
||||
scrap_per_threat = 0.01
|
||||
tile_size_m = 10
|
||||
belt_speed_mps = 20
|
||||
starting_building_blocks = 100
|
||||
@@ -268,6 +271,7 @@ TEST_CASE("Inverted wave gap range is rejected", "[config]")
|
||||
height_tiles = 60
|
||||
refund_percentage = 75
|
||||
scrap_despawn_seconds = 30
|
||||
scrap_per_threat = 0.01
|
||||
tile_size_m = 10
|
||||
belt_speed_mps = 20
|
||||
|
||||
|
||||
@@ -41,16 +41,15 @@ TEST_CASE("ThreatCostCalculator: assembler takes max across recipes", "[threat]"
|
||||
CHECK(table.itemThreat.at("circuit_board") == Approx(28.0));
|
||||
}
|
||||
|
||||
TEST_CASE("ThreatCostCalculator: scrap threat from cheapest ship", "[threat]")
|
||||
TEST_CASE("ThreatCostCalculator: scrap threat is 1 / scrap_per_threat", "[threat]")
|
||||
{
|
||||
const GameConfig cfg = loadConfig();
|
||||
const ThreatCostTable& table = cfg.threatCosts;
|
||||
|
||||
// Cheapest ship by scrap_drop is interceptor (scrap_drop=2).
|
||||
// Interceptor threat: 10 + iron_ingot(4)*3 + circuit_board(28)*1
|
||||
// + laser_cannon(5 + iron_ingot(4)*1) = 10 + 12 + 28 + 9 = 59.0
|
||||
// scrapThreat = 59.0 / 2 = 29.5
|
||||
CHECK(table.scrapThreat == Approx(29.5));
|
||||
// REQ-THREAT-SCRAP: scrap threat is the constant 1 / world.scrap_per_threat.
|
||||
// The test config sets scrap_per_threat = 1.0, so scrapThreat = 1.0.
|
||||
CHECK(table.scrapThreat == Approx(1.0 / cfg.world.scrapPerThreat));
|
||||
CHECK(table.scrapThreat == Approx(1.0));
|
||||
}
|
||||
|
||||
TEST_CASE("ThreatCostCalculator: reprocessing-only item threat", "[threat]")
|
||||
@@ -59,8 +58,8 @@ TEST_CASE("ThreatCostCalculator: reprocessing-only item threat", "[threat]")
|
||||
const ThreatCostTable& table = cfg.threatCosts;
|
||||
|
||||
// advanced_alloy: reprocessing recipe with scrap*5, duration 3.0, probability 0.1
|
||||
// (29.5 * 5 + 3.0) / 0.1 = 1505.0
|
||||
CHECK(table.itemThreat.at("advanced_alloy") == Approx(1505.0));
|
||||
// scrapThreat = 1.0 (= 1 / scrap_per_threat), so (1.0 * 5 + 3.0) / 0.1 = 80.0
|
||||
CHECK(table.itemThreat.at("advanced_alloy") == Approx(80.0));
|
||||
}
|
||||
|
||||
TEST_CASE("ThreatCostCalculator: ship threat with default modules", "[threat]")
|
||||
|
||||
Reference in New Issue
Block a user