allow to (multi) select scrap

This commit is contained in:
2026-07-13 21:09:01 +02:00
parent 8c4fb78fc9
commit 535d4f8f24
11 changed files with 403 additions and 19 deletions

View File

@@ -2,11 +2,22 @@
#include <QVector2D>
#include <algorithm>
#include "DespawnAtComponent.h"
#include "EntityAdmin.h"
#include "EntityHitTest.h"
#include "ScrapDataComponent.h"
#include "ScrapSystem.h"
namespace
{
bool contains(const std::vector<entt::entity>& v, entt::entity e)
{
return std::find(v.begin(), v.end(), e) != v.end();
}
} // namespace
// ---------------------------------------------------------------------------
// Spawn
// ---------------------------------------------------------------------------
@@ -140,3 +151,78 @@ TEST_CASE("ScrapSystem: allScrapInfo returns all spawned scrap", "[scrap]")
const std::vector<ScrapInfo> info = ss.allScrapInfo();
REQUIRE(info.size() == 2);
}
TEST_CASE("ScrapSystem: allScrapInfo reports each pile's remaining amount", "[scrap]")
{
EntityAdmin admin;
ScrapSystem 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.allScrapInfo();
REQUIRE(info.size() == 2);
for (const ScrapInfo& i : info)
{
if (i.entity == a) { REQUIRE(i.amount == 3); }
else if (i.entity == b) { REQUIRE(i.amount == 6); }
else { FAIL("unexpected scrap entity"); }
}
}
// ---------------------------------------------------------------------------
// Selection hit-testing (REQ-UI-SCRAP-CLICK-SELECT, REQ-UI-SCRAP-MULTI-SELECT)
// ---------------------------------------------------------------------------
TEST_CASE("scrapAtWorldPos returns the pile near a point and null when far", "[scrap]")
{
EntityAdmin admin;
ScrapSystem 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));
}
TEST_CASE("scrapAtWorldPos returns the nearest of several piles", "[scrap]")
{
EntityAdmin admin;
ScrapSystem 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));
}
TEST_CASE("entityAtWorldPos never returns a scrap pile", "[scrap]")
{
EntityAdmin admin;
ScrapSystem ss(admin);
ss.spawn(QVector2D(3.0f, 4.0f), 5, 100);
// Scrap 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]")
{
EntityAdmin admin;
ScrapSystem 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));
REQUIRE(hit.size() == 2);
REQUIRE(contains(hit, inA));
REQUIRE(contains(hit, inB));
REQUIRE_FALSE(contains(hit, outX));
}