From b722955f7ebf1bf7f01637642e358042592bf5b1 Mon Sep 17 00:00:00 2001 From: Malte Langkabel Date: Sun, 2 Aug 2026 20:22:39 +0200 Subject: [PATCH] fix splitter filters being lost when rotating in place MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rotateInPlace re-implemented the belt-tile re-registration switch inline instead of calling reregisterBeltTile, and its splitter branch omitted the setSplitterFilters call the canonical version has. Since an operational splitter keeps its filters only in BeltSystem, removeTile discarded them and rotating a configured splitter silently reset it to "accept all". Replace the duplicated switch with a call to reregisterBeltTile, capturing the filters beforehand via getSplitterInfo — the same idiom deconstruct already uses. This removes the second copy of the switch that allowed the two to drift apart in the first place. Add a regression test; the existing [rotate-in-place] cases covered belt tiles only, which is why this went unnoticed. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01GH8ZMRY3vhxxXcaUxBqxkk --- src/lib/sim/BuildingSystem.cpp | 38 +++++++++++++++------------------- src/test/BuildingTest.cpp | 30 +++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 21 deletions(-) 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]") {