target single-building transfers by hovering, not by footprint coincidence
This commit is contained in:
@@ -1050,18 +1050,25 @@ TEST_CASE("BuildingSystem: findRotateInPlaceTarget works for a symmetric multi-t
|
||||
|
||||
namespace
|
||||
{
|
||||
// A single-building blueprint, whose cursor sits on the ghost's own anchor unless a test
|
||||
// says otherwise. That gesture hit-tests the cursor for its transfer target.
|
||||
BlueprintGhostResolved resolveOne(const PlacementFixture& f, BuildingType type,
|
||||
QPoint anchor, Rotation rotation)
|
||||
{
|
||||
return resolveBlueprintGhost(f.state, f.cfg, type, anchor, rotation,
|
||||
/*blueprintHoldsOneBuilding*/ true);
|
||||
return resolveBlueprintGhost(f.state, f.cfg, type, anchor, rotation, anchor);
|
||||
}
|
||||
|
||||
BlueprintGhostResolved resolveOneHovering(const PlacementFixture& f, BuildingType type,
|
||||
QPoint anchor, Rotation rotation, QPoint cursorTile)
|
||||
{
|
||||
return resolveBlueprintGhost(f.state, f.cfg, type, anchor, rotation, cursorTile);
|
||||
}
|
||||
|
||||
// One ghost of a constellation: no cursor hit-test, judged purely on where it sits.
|
||||
BlueprintGhostResolved resolveInConstellation(const PlacementFixture& f, BuildingType type,
|
||||
QPoint anchor, Rotation rotation)
|
||||
{
|
||||
return resolveBlueprintGhost(f.state, f.cfg, type, anchor, rotation,
|
||||
/*blueprintHoldsOneBuilding*/ false);
|
||||
return resolveBlueprintGhost(f.state, f.cfg, type, anchor, rotation, std::nullopt);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -1148,8 +1155,9 @@ TEST_CASE("resolveBlueprintGhost: a single configurable building transfers its s
|
||||
|
||||
TEST_CASE("resolveBlueprintGhost: a transfer ignores the target's rotation", "[blueprint]")
|
||||
{
|
||||
// The rule transfer does not share with compatible overlap: a transfer never rotates
|
||||
// anything, so which way the target faces cannot matter (REQ-UI-BLUEPRINT-TRANSFER).
|
||||
// A transfer never rotates anything, so which way the target faces cannot matter
|
||||
// (REQ-UI-BLUEPRINT-TRANSFER). The ghost snaps to the target's facing rather than
|
||||
// keeping the blueprint's.
|
||||
PlacementFixture f;
|
||||
|
||||
const BuildingId id =
|
||||
@@ -1160,6 +1168,47 @@ TEST_CASE("resolveBlueprintGhost: a transfer ignores the target's rotation", "[b
|
||||
REQUIRE(resolved.action == BlueprintGhostAction::Transfer);
|
||||
REQUIRE(resolved.targetId.has_value());
|
||||
CHECK(*resolved.targetId == id);
|
||||
CHECK(resolved.ghostRotation == Rotation::East);
|
||||
}
|
||||
|
||||
TEST_CASE("resolveBlueprintGhost: a single-building blueprint transfers from anywhere on the target",
|
||||
"[blueprint]")
|
||||
{
|
||||
// The point of hit-testing the cursor instead of comparing footprints. Coincidence
|
||||
// needs the ghost's anchor to land on the target's own anchor, so with a 2x2 body
|
||||
// three of its four tiles missed and read as an ordinary overlap. Hovering any body
|
||||
// tile now targets it, and the ghost snaps onto the building
|
||||
// (REQ-UI-BLUEPRINT-TRANSFER).
|
||||
PlacementFixture f;
|
||||
|
||||
// Assembler body covers (-3,0),(-2,0),(-3,1),(-2,1); its anchor is (-3,0).
|
||||
const BuildingId id =
|
||||
f.bs.place(f.state, BuildingType::Assembler, QPoint(-3, 0), Rotation::East, 0).value();
|
||||
|
||||
const QPoint offAnchorTile(-2, 1);
|
||||
const BlueprintGhostResolved resolved =
|
||||
resolveOneHovering(f, BuildingType::Assembler, offAnchorTile, Rotation::East,
|
||||
offAnchorTile);
|
||||
REQUIRE(resolved.action == BlueprintGhostAction::Transfer);
|
||||
CHECK(*resolved.targetId == id);
|
||||
CHECK(resolved.ghostAnchor == QPoint(-3, 0));
|
||||
|
||||
// The same misaligned ghost inside a constellation still just overlaps invalidly:
|
||||
// a layout is placed where the blueprint puts it, and nothing snaps.
|
||||
CHECK(resolveInConstellation(f, BuildingType::Assembler, offAnchorTile, Rotation::East).action
|
||||
== BlueprintGhostAction::Invalid);
|
||||
}
|
||||
|
||||
TEST_CASE("resolveBlueprintGhost: hovering a different building type does not transfer",
|
||||
"[blueprint]")
|
||||
{
|
||||
PlacementFixture f;
|
||||
|
||||
f.bs.place(f.state, BuildingType::Smelter, QPoint(-3, 0), Rotation::East, 0);
|
||||
|
||||
// A miner blueprint over a smelter: the cursor hit-test only matches its own type.
|
||||
CHECK(resolveOne(f, BuildingType::Miner, QPoint(-3, 0), Rotation::East).action
|
||||
== BlueprintGhostAction::Invalid);
|
||||
}
|
||||
|
||||
TEST_CASE("resolveBlueprintGhost: a construction site is a transfer target too",
|
||||
@@ -1215,29 +1264,18 @@ TEST_CASE("resolveBlueprintGhost: a constellation transfers onto a matching buil
|
||||
CHECK(*resolved.targetId == id);
|
||||
}
|
||||
|
||||
TEST_CASE("resolveBlueprintGhost: only a single-building blueprint ignores target rotation",
|
||||
TEST_CASE("resolveBlueprintGhost: a constellation still requires a matching rotation",
|
||||
"[blueprint]")
|
||||
{
|
||||
// The one thing blueprint size still decides. Inside a constellation a differently
|
||||
// facing twin cannot be re-oriented, so it blocks the whole placement; alone, the
|
||||
// gesture transfers anyway because it rotates nothing (REQ-UI-BLUEPRINT-TRANSFER).
|
||||
//
|
||||
// A splitter, because the question only arises for a footprint that survives
|
||||
// rotation. A miner's body is L-shaped ("AA" / "A>"), so a rotated miner ghost covers
|
||||
// different tiles and coincides with nothing at all -- invalid for a reason that has
|
||||
// nothing to do with these rules.
|
||||
// Only the single-building gesture is forgiving about facing. A constellation's
|
||||
// ghosts stay where the blueprint puts them, and one that cannot be re-oriented to
|
||||
// match blocks the whole placement (REQ-UI-BLUEPRINT-OVERLAP).
|
||||
PlacementFixture f;
|
||||
|
||||
const BuildingId id =
|
||||
f.bs.place(f.state, BuildingType::Splitter, QPoint(-1, 0), Rotation::East, 0).value();
|
||||
f.bs.place(f.state, BuildingType::Splitter, QPoint(-1, 0), Rotation::East, 0);
|
||||
|
||||
CHECK(resolveInConstellation(f, BuildingType::Splitter, QPoint(-1, 0), Rotation::North).action
|
||||
== BlueprintGhostAction::Invalid);
|
||||
|
||||
const BlueprintGhostResolved alone =
|
||||
resolveOne(f, BuildingType::Splitter, QPoint(-1, 0), Rotation::North);
|
||||
REQUIRE(alone.action == BlueprintGhostAction::Transfer);
|
||||
CHECK(*alone.targetId == id);
|
||||
}
|
||||
|
||||
TEST_CASE("resolveBlueprintGhost: a constellation mixes transfers and plain overlaps",
|
||||
|
||||
Reference in New Issue
Block a user