diff --git a/src/lib/sim/BuildingSystem.cpp b/src/lib/sim/BuildingSystem.cpp index cf07d1c..f9f9954 100644 --- a/src/lib/sim/BuildingSystem.cpp +++ b/src/lib/sim/BuildingSystem.cpp @@ -1719,29 +1719,25 @@ void BuildingSystem::rotateInPlace(BuildingId id, Rotation newRotation) // the new port set (REQ-MAT-INPUT-INTAKE). b.incomingItems.assign(b.inputPorts.size(), {}); - // Re-register with BeltSystem (items on tile are discarded). - if (b.type == BuildingType::Belt) + // Re-register with BeltSystem (items on tile are discarded). A splitter's + // filters live in BeltSystem and would be lost by removeTile, so capture + // them first and hand them back to reregisterBeltTile (REQ-BLD-SPLITTER). + if (isBeltSubsystemType(b.type)) { + std::vector splitterFilterA; + std::vector splitterFilterB; + if (b.type == BuildingType::Splitter) + { + if (const std::optional info = + m_belts.getSplitterInfo(b.anchor)) + { + splitterFilterA = info->filterA; + splitterFilterB = info->filterB; + } + } + 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_tiles); - } - else if (b.type == BuildingType::TunnelExit) - { - m_belts.removeTile(b.anchor); - m_belts.placeTunnelExit(b.anchor, newRotation); + reregisterBeltTile(b, splitterFilterA, splitterFilterB); } return; diff --git a/src/test/BuildingTest.cpp b/src/test/BuildingTest.cpp index a2cbfa0..8ca6df9 100644 --- a/src/test/BuildingTest.cpp +++ b/src/test/BuildingTest.cpp @@ -1427,6 +1427,36 @@ TEST_CASE("BuildingSystem: rotateInPlace re-registers a belt tile with BeltSyste REQUIRE(belts.tryPutItem(QPoint(0, 0), makeItem("iron_ore"))); } +TEST_CASE("BuildingSystem: rotateInPlace preserves the output filters of a splitter " + "(REQ-BLD-SPLITTER)", "[building][rotate-in-place]") +{ + PlacementFixture f; + + const QPoint tile(5, 5); + const BuildingId id = f.bs.place(BuildingType::Splitter, tile, Rotation::East, 0).value(); + + // Run until construction completes, so the splitter is registered with BeltSystem. + Tick tick = 0; + while (f.bs.getAllBuildings().empty() && tick < 100000) + { + runTicks(f.bs, f.belts, 1, tick); + } + REQUIRE(f.bs.getAllBuildings().size() == 1); + + const std::vector filterA{ ItemType{"iron_ore"} }; + const std::vector filterB{ ItemType{"copper_ore"} }; + f.belts.setSplitterFilters(tile, filterA, filterB); + + f.bs.rotateInPlace(id, Rotation::North); + + // The tile is re-registered with BeltSystem carrying the filters it had before + // the rotation — rotating must not reset a configured splitter to "accept all". + const std::optional info = f.belts.getSplitterInfo(tile); + REQUIRE(info.has_value()); + REQUIRE(info->filterA == filterA); + REQUIRE(info->filterB == filterB); +} + TEST_CASE("BuildingSystem: splitter filters configured on a construction site carry over " "to the built splitter (REQ-BLD-SITE-CONFIG)", "[building]") {