allow to rotate buildings in place
This commit is contained in:
@@ -797,6 +797,112 @@ bool BuildingSystem::isTileOccupied(QPoint tile) const
|
||||
return m_tileOccupancy.count({tile.x(), tile.y()}) > 0;
|
||||
}
|
||||
|
||||
std::optional<EntityId> BuildingSystem::findRotateInPlaceTarget(
|
||||
BuildingType type, QPoint anchor, Rotation rot) const
|
||||
{
|
||||
const BuildingDef* def = findBuildingDef(type);
|
||||
if (!def) { return std::nullopt; }
|
||||
|
||||
const ParsedSurfaceMask mask = parseSurfaceMask(def->surfaceMask, rot);
|
||||
if (mask.bodyCells.empty()) { return std::nullopt; }
|
||||
|
||||
// All body cells must be occupied by the same entity.
|
||||
const QPoint firstAbs = anchor + mask.bodyCells[0];
|
||||
const auto firstIt = m_tileOccupancy.find({firstAbs.x(), firstAbs.y()});
|
||||
if (firstIt == m_tileOccupancy.end()) { return std::nullopt; }
|
||||
const EntityId candidateId = firstIt->second;
|
||||
|
||||
for (const QPoint& rel : mask.bodyCells)
|
||||
{
|
||||
const QPoint abs = anchor + rel;
|
||||
const auto it = m_tileOccupancy.find({abs.x(), abs.y()});
|
||||
if (it == m_tileOccupancy.end() || it->second != candidateId)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
// Verify the candidate is the same building type with the same cell count.
|
||||
for (const ConstructionSite& site : m_constructionQueue)
|
||||
{
|
||||
if (site.id != candidateId) { continue; }
|
||||
if (site.type != type) { return std::nullopt; }
|
||||
if (site.bodyCells.size() != mask.bodyCells.size()) { return std::nullopt; }
|
||||
return candidateId;
|
||||
}
|
||||
for (const Building& b : m_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 std::nullopt;
|
||||
}
|
||||
|
||||
void BuildingSystem::rotateInPlace(EntityId id, Rotation newRotation)
|
||||
{
|
||||
// Construction site path — just update rotation; no ports to recompute.
|
||||
for (ConstructionSite& site : m_constructionQueue)
|
||||
{
|
||||
if (site.id == id)
|
||||
{
|
||||
site.rotation = newRotation;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Operational building path.
|
||||
for (Building& b : m_buildings)
|
||||
{
|
||||
if (b.id != id) { continue; }
|
||||
|
||||
b.rotation = newRotation;
|
||||
|
||||
const BuildingDef* def = findBuildingDef(b.type);
|
||||
if (!def) { return; }
|
||||
const ParsedSurfaceMask mask = parseSurfaceMask(def->surfaceMask, newRotation);
|
||||
|
||||
b.outputPorts.clear();
|
||||
for (const Port& port : mask.outputPorts)
|
||||
{
|
||||
Port absPort;
|
||||
absPort.tile = b.anchor + port.tile;
|
||||
absPort.direction = port.direction;
|
||||
b.outputPorts.push_back(absPort);
|
||||
}
|
||||
b.inputPorts = computeInputPorts(b);
|
||||
|
||||
// Re-register with BeltSystem (items on tile are discarded).
|
||||
if (b.type == BuildingType::Belt)
|
||||
{
|
||||
m_belts.removeTile(b.anchor);
|
||||
m_belts.placeBelt(b.anchor, newRotation);
|
||||
}
|
||||
else if (b.type == BuildingType::Splitter)
|
||||
{
|
||||
m_belts.removeTile(b.anchor);
|
||||
assert(mask.outputPorts.size() >= 2);
|
||||
m_belts.placeSplitter(b.anchor,
|
||||
mask.outputPorts[0].direction,
|
||||
mask.outputPorts[1].direction);
|
||||
}
|
||||
else if (b.type == BuildingType::TunnelEntry)
|
||||
{
|
||||
m_belts.removeTile(b.anchor);
|
||||
m_belts.placeTunnelEntry(b.anchor, newRotation, m_config.world.tunnelMaxDistance);
|
||||
}
|
||||
else if (b.type == BuildingType::TunnelExit)
|
||||
{
|
||||
m_belts.removeTile(b.anchor);
|
||||
m_belts.placeTunnelExit(b.anchor, newRotation);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const Building* BuildingSystem::findNearestBuilding(QVector2D worldPos,
|
||||
BuildingType type) const
|
||||
{
|
||||
|
||||
@@ -74,6 +74,18 @@ public:
|
||||
std::vector<BeltTileInfo> allBeltTiles() const;
|
||||
bool isTileOccupied(QPoint tile) const;
|
||||
|
||||
// Returns the entity id of the building or construction site whose footprint
|
||||
// exactly coincides with the ghost (type, anchor, rot) and is of the same
|
||||
// building type. Returns nullopt otherwise.
|
||||
std::optional<EntityId> findRotateInPlaceTarget(BuildingType type,
|
||||
QPoint anchor,
|
||||
Rotation rot) const;
|
||||
|
||||
// Rotate an existing building or construction site to newRotation in place.
|
||||
// For belt-type operational buildings, re-registers with BeltSystem (items
|
||||
// currently on the tile are discarded by BeltSystem::removeTile).
|
||||
void rotateInPlace(EntityId id, Rotation newRotation);
|
||||
|
||||
// Find nearest operational building of the given type; nullptr if none.
|
||||
const Building* findNearestBuilding(QVector2D worldPos, BuildingType type) const;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user