let any blueprint transfer configuration, not just single-building ones
Implements 0593f48. A coinciding building of a configurable type now takes
the blueprint's settings whatever the blueprint's size, so dropping a
constellation over a partial copy of itself configures what is already
standing there instead of leaving it blank.
The whole behaviour change is one branch in resolveBlueprintGhost: at a
matching rotation, a coinciding building transfers if its type has settings
and is a compatible overlap otherwise; at a differing rotation only a
single-building blueprint still transfers. GameWorldView and WorldRenderer
needed nothing -- they already switch on the action, already exclude
transfers from the cost, and already tint them.
blueprintHoldsOneBuilding survives only as that rotation carve-out, and the
header now says so.
Note for anyone testing this by hand: the carve-out is narrower than it
reads. It is only reachable for footprints that survive rotation -- 1x1
bodies like the splitter, or fully filled symmetric ones. A miner's body is
L-shaped ("AA" / "A>"), so a rotated miner ghost covers different tiles and
coincides with nothing at all. That is the coincidence test doing its job,
not these rules, but it cost one wrong test before it was noticed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K
This commit is contained in:
@@ -1198,24 +1198,72 @@ TEST_CASE("resolveBlueprintGhost: a single building with no settings overlaps in
|
||||
== BlueprintGhostAction::Invalid);
|
||||
}
|
||||
|
||||
TEST_CASE("resolveBlueprintGhost: nothing in a multi-building blueprint transfers",
|
||||
TEST_CASE("resolveBlueprintGhost: a constellation transfers onto a matching building",
|
||||
"[blueprint]")
|
||||
{
|
||||
// Even a configurable building coinciding with its twin only overlaps once the
|
||||
// blueprint holds more than one building (REQ-UI-BLUEPRINT-TRANSFER).
|
||||
// Blueprint size does not gate the transfer itself: a configurable building already
|
||||
// standing where the blueprint wants it, facing the same way, takes its settings
|
||||
// whatever else the blueprint holds (REQ-UI-BLUEPRINT-TRANSFER).
|
||||
PlacementFixture f;
|
||||
|
||||
const BuildingId id =
|
||||
f.bs.place(f.state, BuildingType::Miner, QPoint(-2, 0), Rotation::East, 0).value();
|
||||
|
||||
const BlueprintGhostResolved matching =
|
||||
const BlueprintGhostResolved resolved =
|
||||
resolveInConstellation(f, BuildingType::Miner, QPoint(-2, 0), Rotation::East);
|
||||
REQUIRE(matching.action == BlueprintGhostAction::CompatibleOverlap);
|
||||
CHECK(*matching.targetId == id);
|
||||
REQUIRE(resolved.action == BlueprintGhostAction::Transfer);
|
||||
CHECK(*resolved.targetId == id);
|
||||
}
|
||||
|
||||
// ... and a differently-facing twin blocks the whole constellation.
|
||||
CHECK(resolveInConstellation(f, BuildingType::Miner, QPoint(-2, 0), Rotation::North).action
|
||||
TEST_CASE("resolveBlueprintGhost: only a single-building blueprint ignores target 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.
|
||||
PlacementFixture f;
|
||||
|
||||
const BuildingId id =
|
||||
f.bs.place(f.state, BuildingType::Splitter, QPoint(-1, 0), Rotation::East, 0).value();
|
||||
|
||||
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",
|
||||
"[blueprint]")
|
||||
{
|
||||
// One drop can reconfigure some of the buildings already there while leaving others
|
||||
// alone: the split is by whether the type has settings at all, not by blueprint size
|
||||
// (REQ-UI-BLUEPRINT-OVERLAP).
|
||||
PlacementFixture f;
|
||||
|
||||
const BuildingId minerId =
|
||||
f.bs.place(f.state, BuildingType::Miner, QPoint(-2, 0), Rotation::East, 0).value();
|
||||
const BuildingId smelterId =
|
||||
f.bs.place(f.state, BuildingType::Smelter, QPoint(-5, 0), Rotation::East, 0).value();
|
||||
|
||||
const BlueprintGhostResolved miner =
|
||||
resolveInConstellation(f, BuildingType::Miner, QPoint(-2, 0), Rotation::East);
|
||||
REQUIRE(miner.action == BlueprintGhostAction::Transfer);
|
||||
CHECK(*miner.targetId == minerId);
|
||||
|
||||
// A smelter runs an implicit recipe (REQ-BLD-SMELTER), so there is nothing to hand
|
||||
// over and it is simply left as it is.
|
||||
const BlueprintGhostResolved smelter =
|
||||
resolveInConstellation(f, BuildingType::Smelter, QPoint(-5, 0), Rotation::East);
|
||||
REQUIRE(smelter.action == BlueprintGhostAction::CompatibleOverlap);
|
||||
CHECK(*smelter.targetId == smelterId);
|
||||
}
|
||||
|
||||
TEST_CASE("resolveBlueprintGhost: an identical tunnel is a compatible overlap", "[blueprint]")
|
||||
|
||||
Reference in New Issue
Block a user