show threat rate in debug output
This commit is contained in:
@@ -337,6 +337,85 @@ TEST_CASE("BuildingSystem: miner output buffer stalls when full", "[building]")
|
||||
REQUIRE_FALSE(b->production.has_value());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// REQ-UI-DEBUG-OVERLAY production counts
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
TEST_CASE("BuildingSystem: productionBuildingCount excludes construction sites", "[building]")
|
||||
{
|
||||
const GameConfig cfg = loadConfig();
|
||||
BeltSystem belts(cfg.world.beltSpeed_tps);
|
||||
int stock = 0;
|
||||
std::mt19937 rng(0);
|
||||
BuildingId nextBuildingId = 1;
|
||||
BuildingSystem bs(cfg, belts,
|
||||
[&nextBuildingId]() { return nextBuildingId++; },
|
||||
[&stock](int n) { stock += n; },
|
||||
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
|
||||
[](const std::string&) -> bool { return true; },
|
||||
rng);
|
||||
|
||||
const BuildingId minerId = bs.place(BuildingType::Miner, QPoint(0, 0), Rotation::East, 0);
|
||||
const BuildingId smelterId = bs.place(BuildingType::Smelter, QPoint(10, 0), Rotation::East, 0);
|
||||
(void)smelterId;
|
||||
|
||||
Tick tick = 0;
|
||||
// Both still under construction.
|
||||
REQUIRE(bs.productionBuildingCount() == 0);
|
||||
|
||||
// The queue builds one at a time: miner (10s) completes at tick 300, then
|
||||
// the smelter (15s) starts and completes at tick 300 + 450 = 750.
|
||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(10.0)) + 1, tick);
|
||||
REQUIRE(bs.productionBuildingCount() == 1);
|
||||
|
||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(15.0)), tick);
|
||||
REQUIRE(bs.productionBuildingCount() == 2);
|
||||
|
||||
// Neither has a recipe selected, so neither has an active cycle.
|
||||
REQUIRE(bs.activeProductionBuildingCount() == 0);
|
||||
|
||||
bs.setRecipe(minerId, "mine_iron_ore");
|
||||
runTicks(bs, belts, 1, tick);
|
||||
REQUIRE(bs.activeProductionBuildingCount() == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("BuildingSystem: activeProductionBuildingCount tracks production cycle state",
|
||||
"[building]")
|
||||
{
|
||||
const GameConfig cfg = loadConfig();
|
||||
BeltSystem belts(cfg.world.beltSpeed_tps);
|
||||
int stock = 0;
|
||||
std::mt19937 rng(0);
|
||||
BuildingId nextBuildingId = 1;
|
||||
BuildingSystem bs(cfg, belts,
|
||||
[&nextBuildingId]() { return nextBuildingId++; },
|
||||
[&stock](int n) { stock += n; },
|
||||
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
|
||||
[](const std::string&) -> bool { return true; },
|
||||
rng);
|
||||
|
||||
const BuildingId id = bs.place(BuildingType::Miner, QPoint(0, 0), Rotation::East, 0);
|
||||
bs.setRecipe(id, "mine_iron_ore");
|
||||
|
||||
Tick tick = 0;
|
||||
// Not yet operational while under construction.
|
||||
REQUIRE(bs.activeProductionBuildingCount() == 0);
|
||||
|
||||
// Construction completes at tick 300; cycle 1 starts the same tick (completesAt=330).
|
||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(10.0)) + 1, tick);
|
||||
REQUIRE(bs.activeProductionBuildingCount() == 1);
|
||||
|
||||
// Run cycles 1 and 2 to completion (1s each); cycle 3 stalls once the
|
||||
// output buffer (capacity 2) is full (REQ-MAT-OUTPUT-BUFFER).
|
||||
runTicks(bs, belts, 2 * static_cast<int>(secondsToTicks(1.0)) + 1, tick);
|
||||
|
||||
const Building* b = bs.findBuilding(id);
|
||||
REQUIRE(b != nullptr);
|
||||
REQUIRE(static_cast<int>(b->outputBuffer.items.size()) == 2);
|
||||
REQUIRE_FALSE(b->production.has_value());
|
||||
REQUIRE(bs.activeProductionBuildingCount() == 0);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Belt pull → input buffer
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -51,6 +51,35 @@ TEST_CASE("WaveSystem: threat accumulates at boss wave counter rate", "[wave]")
|
||||
REQUIRE(ws.threatLevel() == Approx(1.0));
|
||||
}
|
||||
|
||||
TEST_CASE("WaveSystem: threatAccumulationRate matches the rate formula outside quiet windows",
|
||||
"[wave]")
|
||||
{
|
||||
const GameConfig cfg = loadConfig();
|
||||
std::mt19937 rng(42);
|
||||
WaveSystem ws(cfg, rng);
|
||||
|
||||
// threat_rate_formula = "x", boss wave counter starts at 1 → rate = 1 threat/s.
|
||||
REQUIRE(ws.threatAccumulationRate() == Approx(1.0));
|
||||
}
|
||||
|
||||
TEST_CASE("WaveSystem: threatAccumulationRate is 0 during a quiet window", "[wave]")
|
||||
{
|
||||
GameConfig cfg = loadConfig();
|
||||
// Start with the boss countdown already at the pre-boss quiet threshold.
|
||||
cfg.world.waves.bossCountdownSeconds = cfg.world.waves.bossQuietBeforeSeconds;
|
||||
std::mt19937 rng(42);
|
||||
WaveSystem ws(cfg, rng);
|
||||
|
||||
REQUIRE(ws.threatAccumulationRate() == Approx(0.0));
|
||||
|
||||
const double before = ws.threatLevel();
|
||||
for (int i = 0; i < static_cast<int>(secondsToTicks(1.0)); ++i)
|
||||
{
|
||||
ws.tickThreatAccumulation();
|
||||
}
|
||||
REQUIRE(ws.threatLevel() == Approx(before));
|
||||
}
|
||||
|
||||
TEST_CASE("WaveSystem: generation starts at 0 and increments on station destruction", "[wave]")
|
||||
{
|
||||
const GameConfig cfg = loadConfig();
|
||||
|
||||
Reference in New Issue
Block a user