Rename ship/station scrap drop entities to "debris"

This commit is contained in:
2026-07-23 20:51:05 +02:00
parent 11daa61714
commit 8b71fe1a03
48 changed files with 468 additions and 443 deletions

View File

@@ -38,7 +38,7 @@
#include "SalvageScrapBehavior.h"
#include "SalvagerComponent.h"
#include "SalvagerSystem.h"
#include "ScrapSystem.h"
#include "DebrisSystem.h"
#include "SelectedBehaviorComponent.h"
#include "SensorRangeComponent.h"
#include "ShipIdentityComponent.h"
@@ -70,7 +70,7 @@ struct Fixture
RepairSystem repair;
MovementIntentSystem movementIntent;
DynamicBodySystem dynamicBody;
ScrapSystem scraps;
DebrisSystem scraps;
Tick tick;
std::vector<BeamFiredEvent> beamEvents;
@@ -1288,7 +1288,7 @@ TEST_CASE("SensorRange: salvage ship ignores scrap beyond sensor range", "[senso
f.decide();
REQUIRE_FALSE(f.admin.get<SalvageScrapBehavior>(ship).scrapTarget.has_value());
REQUIRE_FALSE(f.admin.get<SalvageScrapBehavior>(ship).debrisTarget.has_value());
REQUIRE(intent(f.admin, ship).target.x() > pos(f.admin, ship).value.x());
}

View File

@@ -13,7 +13,7 @@ add_files(
BuildingTest.cpp
BuildingConfigTest.cpp
ShipTest.cpp
ScrapTest.cpp
DebrisTest.cpp
BehaviorSystemTest.cpp
WaveSystemTest.cpp
CombatSystemTest.cpp

View File

@@ -14,8 +14,8 @@
#include "HealthComponent.h"
#include "HqProxyComponent.h"
#include "ModuleOwnerComponent.h"
#include "ScrapDataComponent.h"
#include "ScrapSystem.h"
#include "DebrisComponent.h"
#include "DebrisSystem.h"
#include "ShipSystem.h"
#include "Simulation.h"
#include "AttackBehavior.h"
@@ -408,7 +408,7 @@ TEST_CASE("CombatSystem: scrap is spawned on ship death", "[combat]")
Simulation sim(loadConfig(), 42);
// 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
// (REQ-RES-DEBRIS-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.getShips().spawn("interceptor",
@@ -417,9 +417,9 @@ TEST_CASE("CombatSystem: scrap is spawned on ship death", "[combat]")
sim.tick();
const std::vector<ScrapInfo> scraps = sim.getScraps().getAllScrapInfo();
const std::vector<DebrisInfo> scraps = sim.getDebrisSystem().getAllDebrisInfo();
REQUIRE(scraps.size() == 1);
CHECK(sim.getAdmin().get<ScrapDataComponent>(scraps[0].entity).amount == 59);
CHECK(sim.getAdmin().get<DebrisComponent>(scraps[0].entity).amount == 59);
}
TEST_CASE("CombatSystem: HQ death sets game over", "[combat]")

View File

@@ -212,7 +212,7 @@ TEST_CASE("Missing field in world.toml is rejected with the field path", "[confi
height_tiles = 60
refund_percentage = 75
deconstruction_time_seconds = 0.1
scrap_despawn_seconds = 30
debris_despawn_seconds = 30
scrap_per_threat = 0.01
tile_size_m = 10
belt_speed_mps = 20
@@ -264,7 +264,7 @@ TEST_CASE("Malformed formula in world.toml is rejected with field identification
height_tiles = 60
refund_percentage = 75
deconstruction_time_seconds = 0.1
scrap_despawn_seconds = 30
debris_despawn_seconds = 30
scrap_per_threat = 0.01
tile_size_m = 10
belt_speed_mps = 20
@@ -317,7 +317,7 @@ TEST_CASE("Inverted wave gap range is rejected", "[config]")
height_tiles = 60
refund_percentage = 75
deconstruction_time_seconds = 0.1
scrap_despawn_seconds = 30
debris_despawn_seconds = 30
scrap_per_threat = 0.01
tile_size_m = 10
belt_speed_mps = 20

View File

@@ -8,8 +8,8 @@
#include "DespawnAtComponent.h"
#include "EntityAdmin.h"
#include "EntityHitTest.h"
#include "ScrapDataComponent.h"
#include "ScrapSystem.h"
#include "DebrisComponent.h"
#include "DebrisSystem.h"
namespace
{
@@ -23,15 +23,15 @@ bool contains(const std::vector<entt::entity>& v, entt::entity e)
// Spawn
// ---------------------------------------------------------------------------
TEST_CASE("ScrapSystem: spawn returns a valid entity with correct scrap data", "[scrap]")
TEST_CASE("DebrisSystem: spawn returns a valid entity with correct debris data", "[debris]")
{
EntityAdmin admin;
ScrapSystem ss(admin);
DebrisSystem ss(admin);
const entt::entity e = ss.spawn(QVector2D(3.0f, 4.0f), 5, 100);
REQUIRE(admin.isValid(e));
REQUIRE(admin.get<ScrapDataComponent>(e).amount == 5);
REQUIRE(admin.get<DebrisComponent>(e).amount == 5);
REQUIRE(admin.get<DespawnAtComponent>(e).tick == 100);
}
@@ -39,10 +39,10 @@ TEST_CASE("ScrapSystem: spawn returns a valid entity with correct scrap data", "
// Despawn timing
// ---------------------------------------------------------------------------
TEST_CASE("ScrapSystem: scrap still present one tick before despawnAt", "[scrap]")
TEST_CASE("DebrisSystem: debris still present one tick before despawnAt", "[debris]")
{
EntityAdmin admin;
ScrapSystem ss(admin);
DebrisSystem ss(admin);
const entt::entity e = ss.spawn(QVector2D(0.0f, 0.0f), 1, 50);
@@ -50,10 +50,10 @@ TEST_CASE("ScrapSystem: scrap still present one tick before despawnAt", "[scrap]
REQUIRE(admin.isValid(e));
}
TEST_CASE("ScrapSystem: scrap removed at despawnAt tick", "[scrap]")
TEST_CASE("DebrisSystem: debris removed at despawnAt tick", "[debris]")
{
EntityAdmin admin;
ScrapSystem ss(admin);
DebrisSystem ss(admin);
const entt::entity e = ss.spawn(QVector2D(0.0f, 0.0f), 1, 50);
@@ -65,10 +65,10 @@ TEST_CASE("ScrapSystem: scrap removed at despawnAt tick", "[scrap]")
// Selective removal
// ---------------------------------------------------------------------------
TEST_CASE("ScrapSystem: tickDespawn removes only expired scraps", "[scrap]")
TEST_CASE("DebrisSystem: tickDespawn removes only expired debris", "[debris]")
{
EntityAdmin admin;
ScrapSystem ss(admin);
DebrisSystem ss(admin);
const entt::entity earlyE = ss.spawn(QVector2D(0.0f, 0.0f), 1, 30);
const entt::entity lateE = ss.spawn(QVector2D(1.0f, 0.0f), 2, 60);
@@ -83,10 +83,10 @@ TEST_CASE("ScrapSystem: tickDespawn removes only expired scraps", "[scrap]")
// Consume
// ---------------------------------------------------------------------------
TEST_CASE("ScrapSystem: consume returns amount and destroys entity", "[scrap]")
TEST_CASE("DebrisSystem: consume returns amount and destroys entity", "[debris]")
{
EntityAdmin admin;
ScrapSystem ss(admin);
DebrisSystem ss(admin);
const entt::entity e = ss.spawn(QVector2D(0.0f, 0.0f), 7, 100);
@@ -96,10 +96,10 @@ TEST_CASE("ScrapSystem: consume returns amount and destroys entity", "[scrap]")
REQUIRE_FALSE(admin.isValid(e));
}
TEST_CASE("ScrapSystem: consume returns nullopt for invalid entity", "[scrap]")
TEST_CASE("DebrisSystem: consume returns nullopt for invalid entity", "[debris]")
{
EntityAdmin admin;
ScrapSystem ss(admin);
DebrisSystem ss(admin);
const std::optional<int> amount = ss.consume(entt::null);
REQUIRE_FALSE(amount.has_value());
@@ -109,118 +109,118 @@ TEST_CASE("ScrapSystem: consume returns nullopt for invalid entity", "[scrap]")
// collectOne
// ---------------------------------------------------------------------------
TEST_CASE("ScrapSystem: collectOne depletes one scrap and keeps the pile until empty", "[scrap]")
TEST_CASE("DebrisSystem: collectOne depletes one scrap and keeps the debris until empty", "[debris]")
{
EntityAdmin admin;
ScrapSystem ss(admin);
DebrisSystem ss(admin);
const entt::entity e = ss.spawn(QVector2D(0.0f, 0.0f), 3, 100);
REQUIRE(ss.collectOne(e));
REQUIRE(admin.isValid(e));
REQUIRE(admin.get<ScrapDataComponent>(e).amount == 2);
REQUIRE(admin.get<DebrisComponent>(e).amount == 2);
REQUIRE(ss.collectOne(e));
REQUIRE(admin.isValid(e));
REQUIRE(admin.get<ScrapDataComponent>(e).amount == 1);
REQUIRE(admin.get<DebrisComponent>(e).amount == 1);
// Final unit collected: the pile is removed once depleted.
// Final unit collected: the debris is removed once depleted.
REQUIRE(ss.collectOne(e));
REQUIRE_FALSE(admin.isValid(e));
}
TEST_CASE("ScrapSystem: collectOne returns false for an invalid entity", "[scrap]")
TEST_CASE("DebrisSystem: collectOne returns false for an invalid entity", "[debris]")
{
EntityAdmin admin;
ScrapSystem ss(admin);
DebrisSystem ss(admin);
REQUIRE_FALSE(ss.collectOne(entt::null));
}
// ---------------------------------------------------------------------------
// allScrapInfo
// getAllDebrisInfo
// ---------------------------------------------------------------------------
TEST_CASE("ScrapSystem: allScrapInfo returns all spawned scrap", "[scrap]")
TEST_CASE("DebrisSystem: getAllDebrisInfo returns all spawned debris", "[debris]")
{
EntityAdmin admin;
ScrapSystem ss(admin);
DebrisSystem ss(admin);
ss.spawn(QVector2D(1.0f, 2.0f), 3, 100);
ss.spawn(QVector2D(4.0f, 5.0f), 6, 200);
const std::vector<ScrapInfo> info = ss.getAllScrapInfo();
const std::vector<DebrisInfo> info = ss.getAllDebrisInfo();
REQUIRE(info.size() == 2);
}
TEST_CASE("ScrapSystem: allScrapInfo reports each pile's remaining amount", "[scrap]")
TEST_CASE("DebrisSystem: getAllDebrisInfo reports each debris entry.s remaining amount", "[debris]")
{
EntityAdmin admin;
ScrapSystem ss(admin);
DebrisSystem ss(admin);
const entt::entity a = ss.spawn(QVector2D(1.0f, 2.0f), 3, 100);
const entt::entity b = ss.spawn(QVector2D(4.0f, 5.0f), 6, 200);
const std::vector<ScrapInfo> info = ss.getAllScrapInfo();
const std::vector<DebrisInfo> info = ss.getAllDebrisInfo();
REQUIRE(info.size() == 2);
for (const ScrapInfo& i : info)
for (const DebrisInfo& i : info)
{
if (i.entity == a) { REQUIRE(i.amount == 3); }
else if (i.entity == b) { REQUIRE(i.amount == 6); }
else { FAIL("unexpected scrap entity"); }
else { FAIL("unexpected debris entity"); }
}
}
// ---------------------------------------------------------------------------
// Selection hit-testing (REQ-UI-SCRAP-CLICK-SELECT, REQ-UI-SCRAP-MULTI-SELECT)
// Selection hit-testing (REQ-UI-DEBRIS-CLICK-SELECT, REQ-UI-DEBRIS-MULTI-SELECT)
// ---------------------------------------------------------------------------
TEST_CASE("scrapAtWorldPos returns the pile near a point and null when far", "[scrap]")
TEST_CASE("debrisAtWorldPos returns the debris near a point and null when far", "[debris]")
{
EntityAdmin admin;
ScrapSystem ss(admin);
DebrisSystem ss(admin);
const entt::entity e = ss.spawn(QVector2D(3.0f, 4.0f), 5, 100);
// Extra parens keep Catch from decomposing the comparison, which is ambiguous
// between Catch's expression templates and entt's entity operator==.
REQUIRE((scrapAtWorldPos(admin, QVector2D(3.1f, 4.0f)) == e));
REQUIRE((scrapAtWorldPos(admin, QVector2D(10.0f, 10.0f)) == entt::null));
REQUIRE((debrisAtWorldPos(admin, QVector2D(3.1f, 4.0f)) == e));
REQUIRE((debrisAtWorldPos(admin, QVector2D(10.0f, 10.0f)) == entt::null));
}
TEST_CASE("scrapAtWorldPos returns the nearest of several piles", "[scrap]")
TEST_CASE("debrisAtWorldPos returns the nearest of several debris", "[debris]")
{
EntityAdmin admin;
ScrapSystem ss(admin);
DebrisSystem ss(admin);
const entt::entity near = ss.spawn(QVector2D(2.0f, 2.0f), 1, 100);
ss.spawn(QVector2D(2.4f, 2.0f), 1, 100);
REQUIRE((scrapAtWorldPos(admin, QVector2D(2.05f, 2.0f)) == near));
REQUIRE((debrisAtWorldPos(admin, QVector2D(2.05f, 2.0f)) == near));
}
TEST_CASE("entityAtWorldPos never returns a scrap pile", "[scrap]")
TEST_CASE("entityAtWorldPos never returns debris", "[debris]")
{
EntityAdmin admin;
ScrapSystem ss(admin);
DebrisSystem ss(admin);
ss.spawn(QVector2D(3.0f, 4.0f), 5, 100);
// Scrap has no HealthComponent, so the actor hit-test ignores it entirely.
// Debris has no HealthComponent, so the actor hit-test ignores it entirely.
REQUIRE((entityAtWorldPos(admin, QVector2D(3.0f, 4.0f)) == entt::null));
}
TEST_CASE("scrapInBox returns exactly the piles inside the tile rectangle", "[scrap]")
TEST_CASE("debrisInBox returns exactly the debris inside the tile rectangle", "[debris]")
{
EntityAdmin admin;
ScrapSystem ss(admin);
DebrisSystem ss(admin);
const entt::entity inA = ss.spawn(QVector2D(1.2f, 2.7f), 1, 100); // tile (1,2)
const entt::entity inB = ss.spawn(QVector2D(4.9f, 5.1f), 1, 100); // tile (4,5)
const entt::entity outX = ss.spawn(QVector2D(10.0f, 10.0f), 1, 100);
// Box given in reversed corner order to confirm normalization.
const std::vector<entt::entity> hit = scrapInBox(admin, QPoint(5, 5), QPoint(0, 0));
const std::vector<entt::entity> hit = debrisInBox(admin, QPoint(5, 5), QPoint(0, 0));
REQUIRE(hit.size() == 2);
REQUIRE(contains(hit, inA));
@@ -228,7 +228,7 @@ TEST_CASE("scrapInBox returns exactly the piles inside the tile rectangle", "[sc
REQUIRE_FALSE(contains(hit, outX));
}
TEST_CASE("actorsInBox returns living ships and stations, excluding scrap and dead actors",
TEST_CASE("actorsInBox returns living ships and stations, excluding debris and dead actors",
"[actor]")
{
EntityAdmin admin;
@@ -256,8 +256,8 @@ TEST_CASE("actorsInBox returns living ships and stations, excluding scrap and de
const entt::entity station = admin.spawnStation(
QPoint(2, 2), QSize(2, 1), stationCells, 200.0f, 200.0f, true);
// Scrap and the HQ proxy are never actors.
admin.spawnScrap(QVector2D(1.0f, 1.0f), 5, Tick(1000));
// Debris and the HQ proxy are never actors.
admin.spawnDebris(QVector2D(1.0f, 1.0f), 5, Tick(1000));
admin.spawnHqProxy(QVector2D(0.5f, 0.5f), 500.0f, 500.0f);
const std::vector<entt::entity> hit = actorsInBox(admin, QPoint(5, 5), QPoint(0, 0));

View File

@@ -225,7 +225,7 @@ TEST_CASE("ShipSystem: salvage_ship cargo capacity matches config", "[ship]")
REQUIRE(admin.get<CargoComponent>(e).maxCapacity == 10);
REQUIRE(admin.get<CargoComponent>(e).current == 0);
REQUIRE_FALSE(admin.get<DeliverScrapBehavior>(e).deliveryBay.has_value());
REQUIRE_FALSE(admin.get<SalvageScrapBehavior>(e).scrapTarget.has_value());
REQUIRE_FALSE(admin.get<SalvageScrapBehavior>(e).debrisTarget.has_value());
REQUIRE(admin.get<SalvageScrapBehavior>(e).maxCollectionRange_tiles == Approx(50.0f));
}