share BuildingSystem's free functions instead of copying them
Four of the five file-local helpers in BuildingSystem.cpp had duplicates elsewhere: isAutoRecipeBuildingType and isBeltSubsystemType were re-spelled as isAutoRecipeBuilding and isBeltLike in SelectedBuildingPanel.cpp, and outputBodyTile was copied verbatim as portBodyTile in GameWorldView.cpp. Same predicates, different names, so a change to one would silently not reach the others. The two BuildingType predicates move to BuildingType.h, which already hosts the free functions over that enum and is already included by both lib and ui. The port geometry moves to a new PortGeometry.h; inputBodyTile has no duplicate but is outputBodyTile's counterpart and belongs beside it — the sim moves items across the port edge and the renderer draws the virtual belt there, so the two must agree on which tile a port owns. inputLaneEntryFree stays file-local: single use, and tied to BeltItemSlot rather than to building types or port geometry. Verified the six moved bodies are character-identical to their originals. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG
This commit is contained in:
@@ -38,3 +38,17 @@ std::string buildingTypeId(BuildingType type)
|
|||||||
}
|
}
|
||||||
return "";
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -29,3 +29,13 @@ std::optional<BuildingType> parseBuildingType(const std::string& id);
|
|||||||
|
|
||||||
// Canonical id string for a BuildingType. The inverse of parseBuildingType.
|
// Canonical id string for a BuildingType. The inverse of parseBuildingType.
|
||||||
std::string buildingTypeId(BuildingType type);
|
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);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ SET(HDRS
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/ItemType.h
|
${CMAKE_CURRENT_SOURCE_DIR}/ItemType.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Item.h
|
${CMAKE_CURRENT_SOURCE_DIR}/Item.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Port.h
|
${CMAKE_CURRENT_SOURCE_DIR}/Port.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/PortGeometry.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/SchematicChoiceOption.h
|
${CMAKE_CURRENT_SOURCE_DIR}/SchematicChoiceOption.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/DisplayName.h
|
${CMAKE_CURRENT_SOURCE_DIR}/DisplayName.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/BeltDragPath.h
|
${CMAKE_CURRENT_SOURCE_DIR}/BeltDragPath.h
|
||||||
|
|||||||
45
src/lib/core/PortGeometry.h
Normal file
45
src/lib/core/PortGeometry.h
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QPoint>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
@@ -6,63 +6,13 @@
|
|||||||
#include <random>
|
#include <random>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
|
#include "PortGeometry.h"
|
||||||
#include "StateChecksum.h"
|
#include "StateChecksum.h"
|
||||||
#include "SurfaceMask.h"
|
#include "SurfaceMask.h"
|
||||||
#include "tracing.h"
|
#include "tracing.h"
|
||||||
|
|
||||||
namespace
|
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
|
// 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),
|
// 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).
|
// matching the belt packing used elsewhere (REQ-GW-BELT-CAPACITY).
|
||||||
|
|||||||
@@ -50,6 +50,7 @@
|
|||||||
#include "HealthComponent.h"
|
#include "HealthComponent.h"
|
||||||
#include "HqProxyComponent.h"
|
#include "HqProxyComponent.h"
|
||||||
#include "ItemIconCache.h"
|
#include "ItemIconCache.h"
|
||||||
|
#include "PortGeometry.h"
|
||||||
#include "PositionComponent.h"
|
#include "PositionComponent.h"
|
||||||
#include "RepairBehavior.h"
|
#include "RepairBehavior.h"
|
||||||
#include "SalvageScrapBehavior.h"
|
#include "SalvageScrapBehavior.h"
|
||||||
@@ -166,18 +167,6 @@ Rotation rotateCounterClockwise(Rotation r)
|
|||||||
return Rotation::East;
|
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
|
// Fill color for a building's status light per its production state
|
||||||
// (REQ-UI-STATUS-LIGHT).
|
// (REQ-UI-STATUS-LIGHT).
|
||||||
QColor statusLightFill(ProductionStatus status, const StatusLightVisuals& sl)
|
QColor statusLightFill(ProductionStatus status, const StatusLightVisuals& sl)
|
||||||
@@ -1341,7 +1330,7 @@ void GameWorldView::drawBuildings(QPainter& painter)
|
|||||||
|
|
||||||
for (const Port& port : b.outputPorts)
|
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);
|
port.direction, bv.outline, /*centered*/ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1441,7 +1430,7 @@ void GameWorldView::drawBuildings(QPainter& painter)
|
|||||||
for (const Port& port : siteMask.outputPorts)
|
for (const Port& port : siteMask.outputPorts)
|
||||||
{
|
{
|
||||||
const QPoint absBody = s.anchor
|
const QPoint absBody = s.anchor
|
||||||
+ portBodyTile(port.tile, port.direction);
|
+ outputBodyTile(port.tile, port.direction);
|
||||||
drawPortGlyph(painter, absBody, port.direction, bv.outline,
|
drawPortGlyph(painter, absBody, port.direction, bv.outline,
|
||||||
/*centered*/ false);
|
/*centered*/ false);
|
||||||
}
|
}
|
||||||
@@ -2215,7 +2204,7 @@ void GameWorldView::drawBuildingGhost(QPainter& painter, BuildingType type,
|
|||||||
|
|
||||||
for (const Port& port : parsed.outputPorts)
|
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);
|
port.direction, lineColor, /*centered*/ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,20 +84,6 @@ bool hasRecipeSelection(BuildingType type)
|
|||||||
|| type == BuildingType::Shipyard;
|
|| 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)
|
QString rotationLabel(Rotation r)
|
||||||
{
|
{
|
||||||
switch (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
|
// 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.
|
// 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();
|
m_clearBeltBtn->show();
|
||||||
}
|
}
|
||||||
@@ -394,7 +380,7 @@ void SelectedBuildingPanel::refreshBuffers(const Building* b)
|
|||||||
// Auto-recipe buildings (Smelter, Reprocessing Plant) have no selected
|
// Auto-recipe buildings (Smelter, Reprocessing Plant) have no selected
|
||||||
// recipe; while a cycle runs, resolve the recipe actually in production so
|
// recipe; while a cycle runs, resolve the recipe actually in production so
|
||||||
// the cycle time and progress can be shown (REQ-UI-PRODUCTION-PROGRESS).
|
// 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);
|
recipe = m_config->recipes.findRecipeDef(b->production->recipeId, b->type);
|
||||||
}
|
}
|
||||||
@@ -488,7 +474,7 @@ void SelectedBuildingPanel::refreshBuffers(const Building* b)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isProductionBuilding(b->type)
|
if (isProductionBuilding(b->type)
|
||||||
&& (recipe || shipDef || isAutoRecipeBuilding(b->type)))
|
&& (recipe || shipDef || isAutoRecipeBuildingType(b->type)))
|
||||||
{
|
{
|
||||||
if (recipe || shipDef)
|
if (recipe || shipDef)
|
||||||
{
|
{
|
||||||
@@ -683,7 +669,7 @@ void SelectedBuildingPanel::buildMulti(const std::vector<BuildingId>& ids)
|
|||||||
{
|
{
|
||||||
text += buildingTypeName(entry.first) + " x "
|
text += buildingTypeName(entry.first) + " x "
|
||||||
+ QString::number(entry.second) + "\n";
|
+ QString::number(entry.second) + "\n";
|
||||||
if (isBeltLike(entry.first))
|
if (isBeltSubsystemType(entry.first))
|
||||||
{
|
{
|
||||||
hasBelt = true;
|
hasBelt = true;
|
||||||
}
|
}
|
||||||
@@ -832,7 +818,7 @@ void SelectedBuildingPanel::onClearBelt()
|
|||||||
for (BuildingId id : m_selectedBuildingIds)
|
for (BuildingId id : m_selectedBuildingIds)
|
||||||
{
|
{
|
||||||
const Building* b = m_sim->getBuildings().findBuilding(id);
|
const Building* b = m_sim->getBuildings().findBuilding(id);
|
||||||
if (b && isBeltLike(b->type))
|
if (b && isBeltSubsystemType(b->type))
|
||||||
{
|
{
|
||||||
for (const QPoint& cell : b->bodyCells)
|
for (const QPoint& cell : b->bodyCells)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user