add tests for salvager range and cooldown
This commit is contained in:
@@ -103,6 +103,22 @@ static ShipLayoutConfig makeSingleModuleLayout(const std::string& moduleId)
|
||||
return layout;
|
||||
}
|
||||
|
||||
static ShipLayoutConfig makeTwoModuleLayout(const std::string& moduleId)
|
||||
{
|
||||
ShipLayoutConfig layout;
|
||||
PlacedModule pm1;
|
||||
pm1.moduleId = moduleId;
|
||||
pm1.position = QPoint(0, 0);
|
||||
pm1.rotation = Rotation::East;
|
||||
layout.placedModules.push_back(pm1);
|
||||
PlacedModule pm2;
|
||||
pm2.moduleId = moduleId;
|
||||
pm2.position = QPoint(0, 1);
|
||||
pm2.rotation = Rotation::East;
|
||||
layout.placedModules.push_back(pm2);
|
||||
return layout;
|
||||
}
|
||||
|
||||
static entt::entity firstSalvageChild(EntityAdmin& admin, entt::entity ship)
|
||||
{
|
||||
entt::entity result = entt::null;
|
||||
@@ -452,6 +468,165 @@ TEST_CASE("BehaviorSystem: full-cargo salvage ship moves toward SalvageBay", "[b
|
||||
REQUIRE(i.target.x() < pos(f.admin, ship).value.x());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Collection range (per-module)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static int totalSalvageCurrent(EntityAdmin& admin, entt::entity ship)
|
||||
{
|
||||
int total = 0;
|
||||
admin.forEach<SalvageCargoComponent, ModuleOwnerComponent>(
|
||||
[&](entt::entity /*ce*/, const SalvageCargoComponent& c, const ModuleOwnerComponent& o)
|
||||
{
|
||||
if (o.owner == ship) { total += c.current; }
|
||||
});
|
||||
return total;
|
||||
}
|
||||
|
||||
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.
|
||||
Fixture f;
|
||||
const ShipLayoutConfig salvageLayout = makeSingleModuleLayout("salvage_bay_module");
|
||||
const entt::entity ship = f.ships.spawn("salvage_ship", 1, QVector2D(0.0f, 0.0f),
|
||||
false, salvageLayout);
|
||||
f.scraps.spawn(QVector2D(55.0f, 0.0f), 1, 100000);
|
||||
|
||||
f.ships.clearMovementIntents();
|
||||
f.ai.tickSalvageBehavior(f.admin, f.scraps, f.buildings);
|
||||
|
||||
REQUIRE(f.admin.get<SalvageCargoComponent>(firstSalvageChild(f.admin, ship)).current == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("BehaviorSystem: salvage module collects scrap within its collection range",
|
||||
"[behavior]")
|
||||
{
|
||||
// collection_range_formula = "50"; scrap at distance 45 must be collected.
|
||||
Fixture f;
|
||||
const ShipLayoutConfig salvageLayout = makeSingleModuleLayout("salvage_bay_module");
|
||||
const entt::entity ship = f.ships.spawn("salvage_ship", 1, QVector2D(0.0f, 0.0f),
|
||||
false, salvageLayout);
|
||||
f.scraps.spawn(QVector2D(45.0f, 0.0f), 1, 100000);
|
||||
|
||||
f.ships.clearMovementIntents();
|
||||
f.ai.tickSalvageBehavior(f.admin, f.scraps, f.buildings);
|
||||
|
||||
REQUIRE(f.admin.get<SalvageCargoComponent>(firstSalvageChild(f.admin, ship)).current == 1);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Collection rate (per-module cooldown)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
TEST_CASE("BehaviorSystem: salvage collection sets cooldown on module", "[behavior]")
|
||||
{
|
||||
Fixture f;
|
||||
const ShipLayoutConfig salvageLayout = makeSingleModuleLayout("salvage_bay_module");
|
||||
const entt::entity ship = f.ships.spawn("salvage_ship", 1, QVector2D(0.0f, 0.0f),
|
||||
false, salvageLayout);
|
||||
f.scraps.spawn(QVector2D(0.0f, 0.0f), 1, 100000);
|
||||
|
||||
f.ships.clearMovementIntents();
|
||||
f.ai.tickSalvageBehavior(f.admin, f.scraps, f.buildings);
|
||||
|
||||
const SalvageCargoComponent& cargo =
|
||||
f.admin.get<SalvageCargoComponent>(firstSalvageChild(f.admin, ship));
|
||||
REQUIRE(cargo.current == 1);
|
||||
REQUIRE(cargo.cooldownTicksRemaining == cargo.collectionIntervalTicks);
|
||||
}
|
||||
|
||||
TEST_CASE("BehaviorSystem: salvage module on cooldown does not collect scrap", "[behavior]")
|
||||
{
|
||||
Fixture f;
|
||||
const ShipLayoutConfig salvageLayout = makeSingleModuleLayout("salvage_bay_module");
|
||||
const entt::entity ship = f.ships.spawn("salvage_ship", 1, QVector2D(0.0f, 0.0f),
|
||||
false, salvageLayout);
|
||||
f.scraps.spawn(QVector2D(0.0f, 0.0f), 1, 100000);
|
||||
|
||||
f.admin.get<SalvageCargoComponent>(firstSalvageChild(f.admin, ship)).cooldownTicksRemaining = 10;
|
||||
|
||||
f.ships.clearMovementIntents();
|
||||
f.ai.tickSalvageBehavior(f.admin, f.scraps, f.buildings);
|
||||
|
||||
REQUIRE(f.admin.get<SalvageCargoComponent>(firstSalvageChild(f.admin, ship)).current == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("BehaviorSystem: salvage module collects again after cooldown expires", "[behavior]")
|
||||
{
|
||||
Fixture f;
|
||||
const ShipLayoutConfig salvageLayout = makeSingleModuleLayout("salvage_bay_module");
|
||||
const entt::entity ship = f.ships.spawn("salvage_ship", 1, QVector2D(0.0f, 0.0f),
|
||||
false, salvageLayout);
|
||||
const entt::entity sc = firstSalvageChild(f.admin, ship);
|
||||
|
||||
f.scraps.spawn(QVector2D(0.0f, 0.0f), 1, 100000);
|
||||
f.ships.clearMovementIntents();
|
||||
f.ai.tickSalvageBehavior(f.admin, f.scraps, f.buildings);
|
||||
REQUIRE(f.admin.get<SalvageCargoComponent>(sc).current == 1);
|
||||
|
||||
// Shorten cooldown to 1 tick and place a second scrap.
|
||||
f.admin.get<SalvageCargoComponent>(sc).cooldownTicksRemaining = 1;
|
||||
f.scraps.spawn(QVector2D(0.0f, 0.0f), 1, 100000);
|
||||
|
||||
// Next tick: cooldown decrements to 0, module collects the second scrap.
|
||||
f.ships.clearMovementIntents();
|
||||
f.ai.tickSalvageBehavior(f.admin, f.scraps, f.buildings);
|
||||
|
||||
REQUIRE(f.admin.get<SalvageCargoComponent>(sc).current == 2);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Multiple salvage modules
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
TEST_CASE("BehaviorSystem: two salvage modules collect independently in same tick", "[behavior]")
|
||||
{
|
||||
Fixture f;
|
||||
const ShipLayoutConfig salvageLayout = makeTwoModuleLayout("salvage_bay_module");
|
||||
const entt::entity ship = f.ships.spawn("salvage_ship", 1, QVector2D(0.0f, 0.0f),
|
||||
false, salvageLayout);
|
||||
|
||||
f.scraps.spawn(QVector2D(0.0f, 0.0f), 1, 100000);
|
||||
f.scraps.spawn(QVector2D(0.0f, 0.0f), 1, 100000);
|
||||
|
||||
f.ships.clearMovementIntents();
|
||||
f.ai.tickSalvageBehavior(f.admin, f.scraps, f.buildings);
|
||||
|
||||
REQUIRE(totalSalvageCurrent(f.admin, ship) == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("BehaviorSystem: second salvage module does not collect when first module is on cooldown",
|
||||
"[behavior]")
|
||||
{
|
||||
// One module on cooldown, one ready: only the ready module collects.
|
||||
Fixture f;
|
||||
const ShipLayoutConfig salvageLayout = makeTwoModuleLayout("salvage_bay_module");
|
||||
const entt::entity ship = f.ships.spawn("salvage_ship", 1, QVector2D(0.0f, 0.0f),
|
||||
false, salvageLayout);
|
||||
|
||||
// Put the first salvage child on cooldown.
|
||||
entt::entity blocked = entt::null;
|
||||
f.admin.forEach<SalvageCargoComponent, ModuleOwnerComponent>(
|
||||
[&](entt::entity ce, SalvageCargoComponent& c, const ModuleOwnerComponent& o)
|
||||
{
|
||||
if (o.owner == ship && blocked == entt::null)
|
||||
{
|
||||
c.cooldownTicksRemaining = 99;
|
||||
blocked = ce;
|
||||
}
|
||||
});
|
||||
|
||||
f.scraps.spawn(QVector2D(0.0f, 0.0f), 1, 100000);
|
||||
f.scraps.spawn(QVector2D(0.0f, 0.0f), 1, 100000);
|
||||
|
||||
f.ships.clearMovementIntents();
|
||||
f.ai.tickSalvageBehavior(f.admin, f.scraps, f.buildings);
|
||||
|
||||
// Only one module was ready, so only one scrap is collected.
|
||||
REQUIRE(totalSalvageCurrent(f.admin, ship) == 1);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Sensor range — spawn
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user