re-cover copy-settings through single-building blueprints
This commit is contained in:
@@ -69,16 +69,9 @@ bool isPlacementValid(const FactoryState& state, const GameConfig& config,Buildi
|
||||
}
|
||||
|
||||
|
||||
std::optional<BuildingId> findRotateInPlaceTarget(const FactoryState& state, const GameConfig& config,
|
||||
BuildingType type, QPoint anchor, Rotation rot)
|
||||
std::optional<CoincidingBuilding> findCoincidingSameTypeBuilding(const FactoryState& state,
|
||||
const GameConfig& config, BuildingType type, QPoint anchor, Rotation rot)
|
||||
{
|
||||
// Tunnel Entries and Tunnel Exits cannot be rotated in place; re-orienting a
|
||||
// tunnel requires deconstructing and re-placing it (REQ-BLD-ROTATE-IN-PLACE).
|
||||
if (type == BuildingType::TunnelEntry || type == BuildingType::TunnelExit)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const BuildingDef* def = config.buildings.findBuildingDef(type);
|
||||
if (!def) { return std::nullopt; }
|
||||
|
||||
@@ -106,20 +99,88 @@ std::optional<BuildingId> findRotateInPlaceTarget(const FactoryState& state, con
|
||||
if (site.id != candidateId) { continue; }
|
||||
if (site.type != type) { return std::nullopt; }
|
||||
if (site.bodyCells.size() != mask.bodyCells.size()) { return std::nullopt; }
|
||||
return candidateId;
|
||||
return CoincidingBuilding{candidateId, site.rotation};
|
||||
}
|
||||
for (const Building& b : state.buildings)
|
||||
{
|
||||
if (b.id != candidateId) { continue; }
|
||||
if (b.type != type) { return std::nullopt; }
|
||||
if (b.bodyCells.size() != mask.bodyCells.size()) { return std::nullopt; }
|
||||
return candidateId;
|
||||
return CoincidingBuilding{candidateId, b.rotation};
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
std::optional<BuildingId> findRotateInPlaceTarget(const FactoryState& state, const GameConfig& config,
|
||||
BuildingType type, QPoint anchor, Rotation rot)
|
||||
{
|
||||
// Tunnel Entries and Tunnel Exits cannot be rotated in place; re-orienting a
|
||||
// tunnel requires deconstructing and re-placing it (REQ-BLD-ROTATE-IN-PLACE).
|
||||
if (type == BuildingType::TunnelEntry || type == BuildingType::TunnelExit)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const std::optional<CoincidingBuilding> target =
|
||||
findCoincidingSameTypeBuilding(state, config, type, anchor, rot);
|
||||
if (!target.has_value()) { return std::nullopt; }
|
||||
return target->id;
|
||||
}
|
||||
|
||||
|
||||
BlueprintGhostResolved resolveBlueprintGhost(const FactoryState& state, const GameConfig& config,
|
||||
BuildingType type, QPoint anchor, Rotation rotation, bool blueprintHoldsOneBuilding)
|
||||
{
|
||||
// Terrain and world bounds first: nothing rescues a ghost hanging off the asteroid
|
||||
// (REQ-BLD-PLACE-VALID condition (a)).
|
||||
if (!isPlacementValid(state, config, type, anchor, rotation))
|
||||
{
|
||||
return BlueprintGhostResolved{BlueprintGhostAction::Invalid, std::nullopt};
|
||||
}
|
||||
|
||||
const std::optional<CoincidingBuilding> coinciding =
|
||||
findCoincidingSameTypeBuilding(state, config, type, anchor, rotation);
|
||||
if (coinciding.has_value())
|
||||
{
|
||||
// A single configurable building hands its settings over, whatever way the target
|
||||
// faces: a transfer never rotates anything, so the target's facing does not matter
|
||||
// (REQ-UI-BLUEPRINT-TRANSFER). Tested before the overlap rule, which then governs
|
||||
// only what this does not claim.
|
||||
if (blueprintHoldsOneBuilding && isConfigurableBuildingType(type))
|
||||
{
|
||||
return BlueprintGhostResolved{BlueprintGhostAction::Transfer, coinciding->id};
|
||||
}
|
||||
|
||||
// Otherwise the building the blueprint wants must already be there in full,
|
||||
// facing the same way, because nothing here may re-orient it
|
||||
// (REQ-UI-BLUEPRINT-OVERLAP). Tunnels are not excluded: nothing is rotated, so
|
||||
// the reason for their REQ-BLD-ROTATE-IN-PLACE exception does not arise.
|
||||
if (coinciding->rotation == rotation)
|
||||
{
|
||||
return BlueprintGhostResolved{BlueprintGhostAction::CompatibleOverlap,
|
||||
coinciding->id};
|
||||
}
|
||||
return BlueprintGhostResolved{BlueprintGhostAction::Invalid, std::nullopt};
|
||||
}
|
||||
|
||||
// No coincidence: any occupancy at all is an ordinary overlap
|
||||
// (REQ-BLD-PLACE-VALID condition (b)).
|
||||
const BuildingDef* def = config.buildings.findBuildingDef(type);
|
||||
if (!def) { return BlueprintGhostResolved{BlueprintGhostAction::Invalid, std::nullopt}; }
|
||||
const ParsedSurfaceMask parsed = parseSurfaceMask(def->surfaceMask, rotation);
|
||||
for (const QPoint& relativeCell : parsed.bodyCells)
|
||||
{
|
||||
if (isTileOccupied(state, anchor + relativeCell))
|
||||
{
|
||||
return BlueprintGhostResolved{BlueprintGhostAction::Invalid, std::nullopt};
|
||||
}
|
||||
}
|
||||
return BlueprintGhostResolved{BlueprintGhostAction::PlaceNew, std::nullopt};
|
||||
}
|
||||
|
||||
|
||||
bool canPlaceBuilding(const FactoryState& state, const GameConfig& config,
|
||||
BuildingType type, QPoint anchor, Rotation rotation)
|
||||
{
|
||||
|
||||
@@ -30,9 +30,27 @@ bool bodyCellsWithinWorldBounds(const FactoryState& state, const GameConfig& con
|
||||
bool isPlacementValid(const FactoryState& state, const GameConfig& config,
|
||||
BuildingType type, QPoint anchor, Rotation rotation);
|
||||
|
||||
// An existing building or site whose footprint a ghost exactly covers.
|
||||
struct CoincidingBuilding
|
||||
{
|
||||
BuildingId id;
|
||||
Rotation rotation;
|
||||
};
|
||||
|
||||
// The building or site whose footprint a ghost of the given type/anchor/rotation
|
||||
// exactly covers: same type, same body cells, a single owner. Operational buildings
|
||||
// and construction sites alike. Rotation is not part of the test -- the target's own
|
||||
// facing is reported so each caller can apply its own rule to it.
|
||||
std::optional<CoincidingBuilding> findCoincidingSameTypeBuilding(const FactoryState& state,
|
||||
const GameConfig& config,
|
||||
BuildingType type,
|
||||
QPoint anchor,
|
||||
Rotation rot);
|
||||
|
||||
// The building or site that a ghost of the given type/anchor/rotation would
|
||||
// rotate in place rather than replace: same type, same body cells, one owner
|
||||
// (REQ-BLD-ROTATE-IN-PLACE). Tunnels never qualify.
|
||||
// (REQ-BLD-ROTATE-IN-PLACE). Tunnels never qualify. Builder mode only; blueprint
|
||||
// placement mode never rotates anything -- see resolveBlueprintGhost.
|
||||
std::optional<BuildingId> findRotateInPlaceTarget(const FactoryState& state,
|
||||
const GameConfig& config,
|
||||
BuildingType type, QPoint anchor,
|
||||
@@ -46,6 +64,36 @@ std::optional<BuildingId> findRotateInPlaceTarget(const FactoryState& state,
|
||||
bool canPlaceBuilding(const FactoryState& state, const GameConfig& config,
|
||||
BuildingType type, QPoint anchor, Rotation rotation);
|
||||
|
||||
// What one ghost of a blueprint would do at its resolved tile.
|
||||
enum class BlueprintGhostAction
|
||||
{
|
||||
PlaceNew, // free, valid cells: a new construction site, charged for
|
||||
CompatibleOverlap, // the same building is already there: left untouched, free
|
||||
// (REQ-UI-BLUEPRINT-OVERLAP)
|
||||
Transfer, // hand this blueprint's settings to the building already there,
|
||||
// free (REQ-UI-BLUEPRINT-TRANSFER)
|
||||
Invalid // terrain, bounds, or an overlap that is neither of the above
|
||||
};
|
||||
|
||||
struct BlueprintGhostResolved
|
||||
{
|
||||
BlueprintGhostAction action;
|
||||
std::optional<BuildingId> targetId; // set for CompatibleOverlap and Transfer
|
||||
};
|
||||
|
||||
// Classifies one ghost of a blueprint against the current factory state
|
||||
// (REQ-UI-BLUEPRINT-OVERLAP, REQ-UI-BLUEPRINT-TRANSFER). `blueprintHoldsOneBuilding` is
|
||||
// the blueprint's stored size, counted before locked types are dropped, so the gesture
|
||||
// does not change behavior as the player unlocks things.
|
||||
//
|
||||
// Shared by the ghost coloring and the click path so a preview cannot disagree with what
|
||||
// the click then does -- the same reason resolveBeltDragPath is shared.
|
||||
BlueprintGhostResolved resolveBlueprintGhost(const FactoryState& state,
|
||||
const GameConfig& config,
|
||||
BuildingType type, QPoint anchor,
|
||||
Rotation rotation,
|
||||
bool blueprintHoldsOneBuilding);
|
||||
|
||||
// What a belt drag would do to one tile of its path (REQ-BLD-BELT-DRAG).
|
||||
enum class BeltTileAction
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user