From 3990351a16232278d42b542f651398ad4b6593de Mon Sep 17 00:00:00 2001 From: mlangkabel Date: Tue, 4 Aug 2026 18:24:38 +0200 Subject: [PATCH] share BuildingSystem's free functions instead of copying them --- src/lib/core/BuildingType.cpp | 14 +++++++++ src/lib/core/BuildingType.h | 10 ++++++ src/lib/core/CMakeLists.txt | 1 + src/lib/core/PortGeometry.h | 45 +++++++++++++++++++++++++++ src/lib/sim/BuildingSystem.cpp | 52 +------------------------------- src/ui/GameWorldView.cpp | 19 +++--------- src/ui/SelectedBuildingPanel.cpp | 24 +++------------ 7 files changed, 80 insertions(+), 85 deletions(-) create mode 100644 src/lib/core/PortGeometry.h diff --git a/src/lib/core/BuildingType.cpp b/src/lib/core/BuildingType.cpp index 7b3ed7f..3725c3f 100644 --- a/src/lib/core/BuildingType.cpp +++ b/src/lib/core/BuildingType.cpp @@ -38,3 +38,17 @@ std::string buildingTypeId(BuildingType type) } return ""; } + +bool isAutoRecipeBuildingType(BuildingType type) +{ + return type == BuildingType::Smelter + || type == BuildingType::ReprocessingPlant; +} + +bool isBeltSubsystemType(BuildingType type) +{ + return type == BuildingType::Belt + || type == BuildingType::Splitter + || type == BuildingType::TunnelEntry + || type == BuildingType::TunnelExit; +} diff --git a/src/lib/core/BuildingType.h b/src/lib/core/BuildingType.h index 885a9c0..cec4259 100644 --- a/src/lib/core/BuildingType.h +++ b/src/lib/core/BuildingType.h @@ -29,3 +29,13 @@ std::optional parseBuildingType(const std::string& id); // Canonical id string for a BuildingType. The inverse of parseBuildingType. std::string buildingTypeId(BuildingType type); + +// Smelter and Reprocessing Plant have no player-selected recipe +// (REQ-BLD-SMELTER, REQ-BLD-REPROCESSING). They auto-process whatever inputs +// they receive, matching against every recipe of their building type. +bool isAutoRecipeBuildingType(BuildingType type); + +// Belts, splitters, and tunnel ends keep their runtime data in the belt subsystem +// rather than in the Building instance, so placing/removing them must register or +// unregister a tile with BeltSystem. +bool isBeltSubsystemType(BuildingType type); diff --git a/src/lib/core/CMakeLists.txt b/src/lib/core/CMakeLists.txt index 716e04d..dd67e69 100644 --- a/src/lib/core/CMakeLists.txt +++ b/src/lib/core/CMakeLists.txt @@ -9,6 +9,7 @@ SET(HDRS ${CMAKE_CURRENT_SOURCE_DIR}/ItemType.h ${CMAKE_CURRENT_SOURCE_DIR}/Item.h ${CMAKE_CURRENT_SOURCE_DIR}/Port.h + ${CMAKE_CURRENT_SOURCE_DIR}/PortGeometry.h ${CMAKE_CURRENT_SOURCE_DIR}/SchematicChoiceOption.h ${CMAKE_CURRENT_SOURCE_DIR}/DisplayName.h ${CMAKE_CURRENT_SOURCE_DIR}/BeltDragPath.h diff --git a/src/lib/core/PortGeometry.h b/src/lib/core/PortGeometry.h new file mode 100644 index 0000000..dc18583 --- /dev/null +++ b/src/lib/core/PortGeometry.h @@ -0,0 +1,45 @@ +#pragma once + +#include + +#include "Rotation.h" + +// Geometry of a building's input/output ports. A Port names the tile *outside* the +// building together with the direction items flow across it; these helpers give the +// building body tile on the other side of that edge, which is where the virtual +// input/output belt lives. +// +// Shared by the simulation (which moves items across the edge) and the renderer +// (which draws the virtual belt), so the two cannot disagree about which tile a +// port belongs to. + +// The building body tile that owns an output port, given the port's outside tile +// (port.tile) and its facing direction. The virtual output belt occupies this tile +// and flows toward port.tile (REQ-MAT-OUTPUT-EMERGE). +inline QPoint outputBodyTile(QPoint portTile, Rotation direction) +{ + switch (direction) + { + case Rotation::East: return portTile + QPoint(-1, 0); + case Rotation::West: return portTile + QPoint( 1, 0); + case Rotation::North: return portTile + QPoint( 0, 1); + case Rotation::South: return portTile + QPoint( 0, -1); + } + return portTile; +} + +// The building body tile an input port feeds into, given the port's outside belt +// tile (port.tile) and its inward flow direction. The virtual input belt occupies +// this tile and flows from the outer edge (progress 0.0) to the centre (0.5) +// (REQ-MAT-INPUT-INTAKE). +inline QPoint inputBodyTile(QPoint portTile, Rotation inwardDirection) +{ + switch (inwardDirection) + { + case Rotation::East: return portTile + QPoint( 1, 0); + case Rotation::West: return portTile + QPoint(-1, 0); + case Rotation::North: return portTile + QPoint( 0, -1); + case Rotation::South: return portTile + QPoint( 0, 1); + } + return portTile; +} diff --git a/src/lib/sim/BuildingSystem.cpp b/src/lib/sim/BuildingSystem.cpp index f1bdc62..5f4909f 100644 --- a/src/lib/sim/BuildingSystem.cpp +++ b/src/lib/sim/BuildingSystem.cpp @@ -6,63 +6,13 @@ #include #include +#include "PortGeometry.h" #include "StateChecksum.h" #include "SurfaceMask.h" #include "tracing.h" namespace { -// Smelter and Reprocessing Plant have no player-selected recipe -// (REQ-BLD-SMELTER, REQ-BLD-REPROCESSING). They auto-process whatever inputs -// they receive, matching against every recipe of their building type. -bool isAutoRecipeBuildingType(BuildingType type) -{ - return type == BuildingType::Smelter - || type == BuildingType::ReprocessingPlant; -} - -// Belts, splitters, and tunnel ends keep their runtime data in the belt subsystem -// rather than in the Building instance, so placing/removing them must register or -// unregister a tile with BeltSystem. -bool isBeltSubsystemType(BuildingType type) -{ - return type == BuildingType::Belt - || type == BuildingType::Splitter - || type == BuildingType::TunnelEntry - || type == BuildingType::TunnelExit; -} - -// The building body tile that owns an output port, given the port's outside tile -// (port.tile) and its facing direction. The virtual output belt occupies this tile -// and flows toward port.tile (REQ-MAT-OUTPUT-EMERGE). -QPoint outputBodyTile(QPoint portTile, Rotation direction) -{ - switch (direction) - { - case Rotation::East: return portTile + QPoint(-1, 0); - case Rotation::West: return portTile + QPoint( 1, 0); - case Rotation::North: return portTile + QPoint( 0, 1); - case Rotation::South: return portTile + QPoint( 0, -1); - } - return portTile; -} - -// The building body tile an input port feeds into, given the port's outside belt -// tile (port.tile) and its inward flow direction. The virtual input belt occupies -// this tile and flows from the outer edge (progress 0.0) to the centre (0.5) -// (REQ-MAT-INPUT-INTAKE). -QPoint inputBodyTile(QPoint portTile, Rotation inwardDirection) -{ - switch (inwardDirection) - { - case Rotation::East: return portTile + QPoint( 1, 0); - case Rotation::West: return portTile + QPoint(-1, 0); - case Rotation::North: return portTile + QPoint( 0, -1); - case Rotation::South: return portTile + QPoint( 0, 1); - } - return portTile; -} - // An input belt accepts a new item at progress 0.0 only when it holds fewer than // three items and the entry slot is clear (nothing within a quarter tile of 0.0), // matching the belt packing used elsewhere (REQ-GW-BELT-CAPACITY). diff --git a/src/ui/GameWorldView.cpp b/src/ui/GameWorldView.cpp index 518ca33..ed47171 100644 --- a/src/ui/GameWorldView.cpp +++ b/src/ui/GameWorldView.cpp @@ -50,6 +50,7 @@ #include "HealthComponent.h" #include "HqProxyComponent.h" #include "ItemIconCache.h" +#include "PortGeometry.h" #include "PositionComponent.h" #include "RepairBehavior.h" #include "SalvageScrapBehavior.h" @@ -166,18 +167,6 @@ Rotation rotateCounterClockwise(Rotation r) return Rotation::East; } -QPoint portBodyTile(QPoint portTile, Rotation direction) -{ - switch (direction) - { - case Rotation::East: return portTile + QPoint(-1, 0); - case Rotation::West: return portTile + QPoint( 1, 0); - case Rotation::North: return portTile + QPoint( 0, 1); - case Rotation::South: return portTile + QPoint( 0, -1); - } - return portTile; -} - // Fill color for a building's status light per its production state // (REQ-UI-STATUS-LIGHT). QColor statusLightFill(ProductionStatus status, const StatusLightVisuals& sl) @@ -1341,7 +1330,7 @@ void GameWorldView::drawBuildings(QPainter& painter) for (const Port& port : b.outputPorts) { - drawPortGlyph(painter, portBodyTile(port.tile, port.direction), + drawPortGlyph(painter, outputBodyTile(port.tile, port.direction), port.direction, bv.outline, /*centered*/ false); } @@ -1441,7 +1430,7 @@ void GameWorldView::drawBuildings(QPainter& painter) for (const Port& port : siteMask.outputPorts) { const QPoint absBody = s.anchor - + portBodyTile(port.tile, port.direction); + + outputBodyTile(port.tile, port.direction); drawPortGlyph(painter, absBody, port.direction, bv.outline, /*centered*/ false); } @@ -2215,7 +2204,7 @@ void GameWorldView::drawBuildingGhost(QPainter& painter, BuildingType type, for (const Port& port : parsed.outputPorts) { - drawPortGlyph(painter, anchorTile + portBodyTile(port.tile, port.direction), + drawPortGlyph(painter, anchorTile + outputBodyTile(port.tile, port.direction), port.direction, lineColor, /*centered*/ false); } diff --git a/src/ui/SelectedBuildingPanel.cpp b/src/ui/SelectedBuildingPanel.cpp index 84bc502..74ad01b 100644 --- a/src/ui/SelectedBuildingPanel.cpp +++ b/src/ui/SelectedBuildingPanel.cpp @@ -84,20 +84,6 @@ bool hasRecipeSelection(BuildingType type) || type == BuildingType::Shipyard; } -// Auto-recipe buildings have no selected recipe; their production is driven by -// whatever inputs they receive. -bool isAutoRecipeBuilding(BuildingType type) -{ - return type == BuildingType::Smelter - || type == BuildingType::ReprocessingPlant; -} - -bool isBeltLike(BuildingType type) -{ - return type == BuildingType::Belt || type == BuildingType::Splitter - || type == BuildingType::TunnelEntry || type == BuildingType::TunnelExit; -} - QString rotationLabel(Rotation r) { switch (r) @@ -313,7 +299,7 @@ void SelectedBuildingPanel::buildSingle(BuildingId id) // Belt "Clear" removes items from a live belt tile; a construction site has // none and is not registered with BeltSystem yet, so hide it for sites. - if (isBeltLike(type) && !m_singleIsSite) + if (isBeltSubsystemType(type) && !m_singleIsSite) { m_clearBeltBtn->show(); } @@ -394,7 +380,7 @@ void SelectedBuildingPanel::refreshBuffers(const Building* b) // Auto-recipe buildings (Smelter, Reprocessing Plant) have no selected // recipe; while a cycle runs, resolve the recipe actually in production so // the cycle time and progress can be shown (REQ-UI-PRODUCTION-PROGRESS). - if (!recipe && isAutoRecipeBuilding(b->type) && b->production.has_value()) + if (!recipe && isAutoRecipeBuildingType(b->type) && b->production.has_value()) { recipe = m_config->recipes.findRecipeDef(b->production->recipeId, b->type); } @@ -488,7 +474,7 @@ void SelectedBuildingPanel::refreshBuffers(const Building* b) } if (isProductionBuilding(b->type) - && (recipe || shipDef || isAutoRecipeBuilding(b->type))) + && (recipe || shipDef || isAutoRecipeBuildingType(b->type))) { if (recipe || shipDef) { @@ -683,7 +669,7 @@ void SelectedBuildingPanel::buildMulti(const std::vector& ids) { text += buildingTypeName(entry.first) + " x " + QString::number(entry.second) + "\n"; - if (isBeltLike(entry.first)) + if (isBeltSubsystemType(entry.first)) { hasBelt = true; } @@ -832,7 +818,7 @@ void SelectedBuildingPanel::onClearBelt() for (BuildingId id : m_selectedBuildingIds) { const Building* b = m_sim->getBuildings().findBuilding(id); - if (b && isBeltLike(b->type)) + if (b && isBeltSubsystemType(b->type)) { for (const QPoint& cell : b->bodyCells) {