cargo component refactoring

This commit is contained in:
2026-06-21 18:35:02 +02:00
parent 665060bcd2
commit a472ec196c
17 changed files with 188 additions and 114 deletions

View File

@@ -33,9 +33,10 @@
#include "RepairSystem.h"
#include "RepairToolComponent.h"
#include "RetreatBehavior.h"
#include "CargoComponent.h"
#include "Rotation.h"
#include "SalvageCargoComponent.h"
#include "SalvageScrapBehavior.h"
#include "SalvagerComponent.h"
#include "SalvagerSystem.h"
#include "ScrapSystem.h"
#include "SelectedBehaviorComponent.h"
@@ -179,8 +180,8 @@ static ShipLayoutConfig makeTwoModuleLayout(const std::string& moduleId)
static entt::entity firstSalvageChild(EntityAdmin& admin, entt::entity ship)
{
entt::entity result = entt::null;
admin.forEach<SalvageCargoComponent, ModuleOwnerComponent>(
[&](entt::entity ce, const SalvageCargoComponent&, const ModuleOwnerComponent& o)
admin.forEach<SalvagerComponent, ModuleOwnerComponent>(
[&](entt::entity ce, const SalvagerComponent&, const ModuleOwnerComponent& o)
{
if (o.owner == ship && result == entt::null) { result = ce; }
});
@@ -944,7 +945,7 @@ TEST_CASE("BehaviorSystem: salvage ship collects scrap on arrival", "[behavior]"
const entt::entity sc = firstSalvageChild(f.admin, ship);
REQUIRE(f.admin.isValid(sc));
REQUIRE(f.admin.get<SalvageCargoComponent>(sc).current == 1);
REQUIRE(f.admin.get<CargoComponent>(ship).current == 1);
REQUIRE_FALSE(f.admin.isValid(scrapEntity));
}
@@ -969,10 +970,9 @@ TEST_CASE("BehaviorSystem: full-cargo salvage ship moves toward SalvageBay", "[b
const entt::entity ship = f.ships.spawn("salvage_ship", 1, QVector2D(5.0f, 0.0f),
false, salvageLayout);
{
const entt::entity sc = firstSalvageChild(f.admin, ship);
REQUIRE(f.admin.isValid(sc));
SalvageCargoComponent& cargo = f.admin.get<SalvageCargoComponent>(sc);
cargo.current = cargo.capacity; // full cargo
REQUIRE(f.admin.isValid(firstSalvageChild(f.admin, ship)));
CargoComponent& cargo = f.admin.get<CargoComponent>(ship);
cargo.current = cargo.maxCapacity; // full cargo
}
f.decide();
@@ -990,13 +990,9 @@ TEST_CASE("BehaviorSystem: full-cargo salvage ship moves toward SalvageBay", "[b
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;
return admin.hasAll<CargoComponent>(ship)
? admin.get<CargoComponent>(ship).current
: 0;
}
TEST_CASE("SalvagerSystem: module does not collect scrap beyond its collection range",
@@ -1011,7 +1007,7 @@ TEST_CASE("SalvagerSystem: module does not collect scrap beyond its collection r
f.runSalvageCollect();
REQUIRE(f.admin.get<SalvageCargoComponent>(firstSalvageChild(f.admin, ship)).current == 0);
REQUIRE(f.admin.get<CargoComponent>(ship).current == 0);
}
TEST_CASE("SalvagerSystem: module collects scrap within its collection range",
@@ -1026,7 +1022,7 @@ TEST_CASE("SalvagerSystem: module collects scrap within its collection range",
f.runSalvageCollect();
REQUIRE(f.admin.get<SalvageCargoComponent>(firstSalvageChild(f.admin, ship)).current == 1);
REQUIRE(f.admin.get<CargoComponent>(ship).current == 1);
}
// ---------------------------------------------------------------------------
@@ -1045,10 +1041,10 @@ TEST_CASE("SalvagerSystem: collection sets cooldown on module", "[behavior]")
// collected until mid-beam (REQ-SHP-SALVAGE), so cargo is still empty now.
f.salvageTick();
const SalvageCargoComponent& cargo =
f.admin.get<SalvageCargoComponent>(firstSalvageChild(f.admin, ship));
REQUIRE(cargo.current == 0);
REQUIRE(cargo.cooldownTicksRemaining == cargo.collectionIntervalTicks);
REQUIRE(f.admin.get<CargoComponent>(ship).current == 0);
const SalvagerComponent& salvager =
f.admin.get<SalvagerComponent>(firstSalvageChild(f.admin, ship));
REQUIRE(salvager.cooldownTicksRemaining == salvager.collectionIntervalTicks);
}
TEST_CASE("SalvagerSystem: module on cooldown does not collect scrap", "[behavior]")
@@ -1059,11 +1055,11 @@ TEST_CASE("SalvagerSystem: module on cooldown does not collect scrap", "[behavio
false, salvageLayout);
f.scraps.spawn(QVector2D(0.0f, 0.0f), 1, 100000);
f.admin.get<SalvageCargoComponent>(firstSalvageChild(f.admin, ship)).cooldownTicksRemaining = 10;
f.admin.get<SalvagerComponent>(firstSalvageChild(f.admin, ship)).cooldownTicksRemaining = 10;
f.runSalvageCollect();
REQUIRE(f.admin.get<SalvageCargoComponent>(firstSalvageChild(f.admin, ship)).current == 0);
REQUIRE(f.admin.get<CargoComponent>(ship).current == 0);
}
TEST_CASE("SalvagerSystem: module collects again after cooldown expires", "[behavior]")
@@ -1076,17 +1072,17 @@ TEST_CASE("SalvagerSystem: module collects again after cooldown expires", "[beha
f.scraps.spawn(QVector2D(0.0f, 0.0f), 1, 100000);
f.runSalvageCollect();
REQUIRE(f.admin.get<SalvageCargoComponent>(sc).current == 1);
REQUIRE(f.admin.get<CargoComponent>(ship).current == 1);
// Shorten cooldown to 1 tick and place a second scrap.
f.admin.get<SalvageCargoComponent>(sc).cooldownTicksRemaining = 1;
f.admin.get<SalvagerComponent>(sc).cooldownTicksRemaining = 1;
f.scraps.spawn(QVector2D(0.0f, 0.0f), 1, 100000);
// Once the cooldown expires the module starts another cycle and collects the
// second scrap after the mid-beam delay.
f.runSalvageCollect();
REQUIRE(f.admin.get<SalvageCargoComponent>(sc).current == 2);
REQUIRE(f.admin.get<CargoComponent>(ship).current == 2);
}
// ---------------------------------------------------------------------------
@@ -1119,12 +1115,12 @@ TEST_CASE("SalvagerSystem: second salvage module does not collect when first is
// 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)
f.admin.forEach<SalvagerComponent, ModuleOwnerComponent>(
[&](entt::entity ce, SalvagerComponent& s, const ModuleOwnerComponent& o)
{
if (o.owner == ship && blocked == entt::null)
{
c.cooldownTicksRemaining = 99;
s.cooldownTicksRemaining = 99;
blocked = ce;
}
});

View File

@@ -19,9 +19,10 @@
#include "RepairBehavior.h"
#include "RepairToolComponent.h"
#include "RetreatBehavior.h"
#include "CargoComponent.h"
#include "Rotation.h"
#include "SalvageCargoComponent.h"
#include "SalvageScrapBehavior.h"
#include "SalvagerComponent.h"
#include "SelectedBehaviorComponent.h"
#include "SensorRangeComponent.h"
#include "ShipLayout.h"
@@ -52,8 +53,8 @@ static entt::entity firstWeaponChild(EntityAdmin& admin, entt::entity ship)
static entt::entity firstSalvageChild(EntityAdmin& admin, entt::entity ship)
{
entt::entity result = entt::null;
admin.forEach<SalvageCargoComponent, ModuleOwnerComponent>(
[&](entt::entity ce, const SalvageCargoComponent&, const ModuleOwnerComponent& o)
admin.forEach<SalvagerComponent, ModuleOwnerComponent>(
[&](entt::entity ce, const SalvagerComponent&, const ModuleOwnerComponent& o)
{
if (o.owner == ship && result == entt::null) { result = ce; }
});
@@ -220,8 +221,9 @@ TEST_CASE("ShipSystem: salvage_ship cargo capacity matches config", "[ship]")
// salvager: cargo_capacity_formula = "10", collection_range_m_formula = "500" m → 500/10 = 50 tiles
const entt::entity sc = firstSalvageChild(admin, e);
REQUIRE(admin.isValid(sc));
REQUIRE(admin.get<SalvageCargoComponent>(sc).capacity == 10);
REQUIRE(admin.get<SalvageCargoComponent>(sc).current == 0);
// Cargo capacity is now a ship-level pool (REQ-MOD-CARGO-CAPACITY).
REQUIRE(admin.get<CargoComponent>(e).maxCapacity == 10);
REQUIRE(admin.get<CargoComponent>(e).current == 0);
REQUIRE(admin.get<DeliverScrapBehavior>(e).deliveryBay == kInvalidBuildingId);
REQUIRE_FALSE(admin.get<SalvageScrapBehavior>(e).scrapTarget.has_value());
REQUIRE(admin.get<SalvageScrapBehavior>(e).maxCollectionRange_tiles == Approx(50.0f));