migrate every factory query off BuildingSystem onto the free functions
This commit is contained in:
@@ -488,6 +488,11 @@ const ArenaConfig& ArenaSimulation::getArenaConfig() const
|
|||||||
return m_arenaConfig;
|
return m_arenaConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const FactoryState& ArenaSimulation::getFactoryState() const
|
||||||
|
{
|
||||||
|
return m_factoryState;
|
||||||
|
}
|
||||||
|
|
||||||
const BuildingSystem& ArenaSimulation::getBuildings() const
|
const BuildingSystem& ArenaSimulation::getBuildings() const
|
||||||
{
|
{
|
||||||
return *m_buildingSystem;
|
return *m_buildingSystem;
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ public:
|
|||||||
|
|
||||||
const ArenaConfig& getArenaConfig() const;
|
const ArenaConfig& getArenaConfig() const;
|
||||||
const BuildingSystem& getBuildings() const;
|
const BuildingSystem& getBuildings() const;
|
||||||
|
const FactoryState& getFactoryState() const;
|
||||||
const ShipSystem& getShips() const;
|
const ShipSystem& getShips() const;
|
||||||
const DebrisSystem& getDebrisSystem() const;
|
const DebrisSystem& getDebrisSystem() const;
|
||||||
EntityAdmin& getAdmin();
|
EntityAdmin& getAdmin();
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "ArenaView.h"
|
#include "ArenaView.h"
|
||||||
|
#include "FactoryQueries.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
@@ -306,7 +307,7 @@ void ArenaView::drawTiles(QPainter& painter)
|
|||||||
|
|
||||||
void ArenaView::drawBuildings(QPainter& painter)
|
void ArenaView::drawBuildings(QPainter& painter)
|
||||||
{
|
{
|
||||||
for (const Building& b : m_sim->getBuildings().getAllBuildings())
|
for (const Building& b : getAllBuildings(m_sim->getFactoryState()))
|
||||||
{
|
{
|
||||||
const std::map<BuildingType, BuildingVisuals>::const_iterator it =
|
const std::map<BuildingType, BuildingVisuals>::const_iterator it =
|
||||||
m_visuals->buildings.find(b.type);
|
m_visuals->buildings.find(b.type);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "BuildingConfig.h"
|
#include "BuildingConfig.h"
|
||||||
|
#include "FactoryQueries.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
@@ -26,8 +27,8 @@ struct SelectedBuilding
|
|||||||
// (the HQ and defence stations, per REQ-UI-BLUEPRINT-CREATE).
|
// (the HQ and defence stations, per REQ-UI-BLUEPRINT-CREATE).
|
||||||
std::optional<SelectedBuilding> resolvePlaceable(const Simulation& sim, BuildingId id)
|
std::optional<SelectedBuilding> resolvePlaceable(const Simulation& sim, BuildingId id)
|
||||||
{
|
{
|
||||||
const Building* building = sim.getBuildings().findBuilding(id);
|
const Building* building = findBuilding(sim.getFactoryState(), id);
|
||||||
const ConstructionSite* site = building ? nullptr : sim.getBuildings().findSite(id);
|
const ConstructionSite* site = building ? nullptr : findSite(sim.getFactoryState(), id);
|
||||||
if (!building && !site)
|
if (!building && !site)
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
@@ -52,8 +53,8 @@ std::optional<SelectedBuilding> resolvePlaceable(const Simulation& sim, Building
|
|||||||
|
|
||||||
std::optional<BuildingConfig> readBuildingConfig(const Simulation& sim, BuildingId id)
|
std::optional<BuildingConfig> readBuildingConfig(const Simulation& sim, BuildingId id)
|
||||||
{
|
{
|
||||||
const Building* building = sim.getBuildings().findBuilding(id);
|
const Building* building = findBuilding(sim.getFactoryState(), id);
|
||||||
const ConstructionSite* site = building ? nullptr : sim.getBuildings().findSite(id);
|
const ConstructionSite* site = building ? nullptr : findSite(sim.getFactoryState(), id);
|
||||||
if (!building && !site)
|
if (!building && !site)
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|||||||
@@ -241,11 +241,11 @@ std::vector<Port> BuildingSystem::computeInputPorts(
|
|||||||
|
|
||||||
std::vector<Port> BuildingSystem::getInputPorts(BuildingId id) const
|
std::vector<Port> BuildingSystem::getInputPorts(BuildingId id) const
|
||||||
{
|
{
|
||||||
if (const Building* building = findBuilding(id))
|
if (const Building* building = findBuilding(m_state, id))
|
||||||
{
|
{
|
||||||
return building->inputPorts;
|
return building->inputPorts;
|
||||||
}
|
}
|
||||||
if (const ConstructionSite* site = findSite(id))
|
if (const ConstructionSite* site = findSite(m_state, id))
|
||||||
{
|
{
|
||||||
// A site stores no ports; derive its output ports from the mask (absolute)
|
// A site stores no ports; derive its output ports from the mask (absolute)
|
||||||
// and run the same input-edge scan (REQ-BLD-BELT-DRAG, REQ-MAT-INPUT-PORTS).
|
// and run the same input-edge scan (REQ-BLD-BELT-DRAG, REQ-MAT-INPUT-PORTS).
|
||||||
@@ -825,7 +825,7 @@ void BuildingSystem::cancelDeconstruction(BuildingId id)
|
|||||||
// Resume operation: clear the flag and re-register belt/tunnel/splitter
|
// Resume operation: clear the flag and re-register belt/tunnel/splitter
|
||||||
// tiles that were unregistered at enqueue (which re-pairs tunnels,
|
// tiles that were unregistered at enqueue (which re-pairs tunnels,
|
||||||
// REQ-BLD-TUNNEL-PAIR). Deconstruction progress is discarded; no refund.
|
// REQ-BLD-TUNNEL-PAIR). Deconstruction progress is discarded; no refund.
|
||||||
if (Building* building = findBuildingMutable(id))
|
if (Building* building = findBuilding(m_state, id))
|
||||||
{
|
{
|
||||||
building->queuedForDeconstruction = false;
|
building->queuedForDeconstruction = false;
|
||||||
reregisterBeltTile(*building, it->splitterFilterA, it->splitterFilterB);
|
reregisterBeltTile(*building, it->splitterFilterA, it->splitterFilterB);
|
||||||
@@ -838,12 +838,6 @@ void BuildingSystem::cancelDeconstruction(BuildingId id)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BuildingSystem::isQueuedForDeconstruction(BuildingId id) const
|
|
||||||
{
|
|
||||||
const Building* building = findBuilding(id);
|
|
||||||
return building && building->queuedForDeconstruction;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BuildingSystem::tickBeltPull()
|
void BuildingSystem::tickBeltPull()
|
||||||
{
|
{
|
||||||
TRACE();
|
TRACE();
|
||||||
@@ -941,7 +935,7 @@ bool BuildingSystem::tryDirectCoupleDeposit(BuildingId producerId,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Building* consumer = findBuildingMutable(*ownerId);
|
Building* consumer = findBuilding(m_state, *ownerId);
|
||||||
if (!consumer)
|
if (!consumer)
|
||||||
{
|
{
|
||||||
return false; // an unbuilt construction site, or not an operational building
|
return false; // an unbuilt construction site, or not an operational building
|
||||||
@@ -1263,41 +1257,6 @@ void BuildingSystem::forEachIncomingItem(
|
|||||||
// Queries
|
// Queries
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
const Building* BuildingSystem::findBuilding(BuildingId id) const
|
|
||||||
{
|
|
||||||
return ::findBuilding(m_state, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
Building* BuildingSystem::findBuildingMutable(BuildingId id)
|
|
||||||
{
|
|
||||||
return ::findBuilding(m_state, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
const ConstructionSite* BuildingSystem::findSite(BuildingId id) const
|
|
||||||
{
|
|
||||||
return ::findSite(m_state, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Building> BuildingSystem::getAllBuildings() const
|
|
||||||
{
|
|
||||||
return ::getAllBuildings(m_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<ConstructionSite> BuildingSystem::getAllSites() const
|
|
||||||
{
|
|
||||||
return ::getAllSites(m_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
int BuildingSystem::getProductionBuildingCount() const
|
|
||||||
{
|
|
||||||
return ::getProductionBuildingCount(m_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
int BuildingSystem::getActiveProductionBuildingCount() const
|
|
||||||
{
|
|
||||||
return ::getActiveProductionBuildingCount(m_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<const RecipeDef*>
|
std::vector<const RecipeDef*>
|
||||||
BuildingSystem::gatherCandidateRecipes(const Building& b) const
|
BuildingSystem::gatherCandidateRecipes(const Building& b) const
|
||||||
{
|
{
|
||||||
@@ -1471,11 +1430,6 @@ std::vector<BuildingSystem::BeltTileInfo> BuildingSystem::getAllBeltTiles() cons
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BuildingSystem::isTileOccupied(QPoint tile) const
|
|
||||||
{
|
|
||||||
return ::isTileOccupied(m_state, tile);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<BuildingId> BuildingSystem::findRotateInPlaceTarget(
|
std::optional<BuildingId> BuildingSystem::findRotateInPlaceTarget(
|
||||||
BuildingType type, QPoint anchor, Rotation rot) const
|
BuildingType type, QPoint anchor, Rotation rot) const
|
||||||
{
|
{
|
||||||
@@ -1591,17 +1545,6 @@ void BuildingSystem::rotateInPlace(BuildingId id, Rotation newRotation)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Building* BuildingSystem::findNearestBuilding(QVector2D worldPos,
|
|
||||||
BuildingType type) const
|
|
||||||
{
|
|
||||||
return ::findNearestBuilding(m_state, worldPos, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BuildingSystem::deliverScrapToSalvageBay(BuildingId bayId)
|
|
||||||
{
|
|
||||||
return ::deliverScrapToSalvageBay(m_state, bayId);
|
|
||||||
}
|
|
||||||
|
|
||||||
BuildingId BuildingSystem::placeImmediate(BuildingType type,
|
BuildingId BuildingSystem::placeImmediate(BuildingType type,
|
||||||
const std::vector<std::string>& surfaceMask,
|
const std::vector<std::string>& surfaceMask,
|
||||||
QPoint anchor, Rotation rotation)
|
QPoint anchor, Rotation rotation)
|
||||||
|
|||||||
@@ -94,7 +94,6 @@ public:
|
|||||||
void cancelDeconstruction(BuildingId id);
|
void cancelDeconstruction(BuildingId id);
|
||||||
|
|
||||||
// True if the building is currently in the deconstruction queue.
|
// True if the building is currently in the deconstruction queue.
|
||||||
bool isQueuedForDeconstruction(BuildingId id) const;
|
|
||||||
|
|
||||||
// Set the recipe (or schematic id for shipyard) on a building or queued
|
// Set the recipe (or schematic id for shipyard) on a building or queued
|
||||||
// construction site. Clears both buffers on an operational building.
|
// construction site. Clears both buffers on an operational building.
|
||||||
@@ -140,18 +139,12 @@ public:
|
|||||||
Rotation directionB; // Splitter: second output; Belt: same as directionA
|
Rotation directionB; // Splitter: second output; Belt: same as directionA
|
||||||
};
|
};
|
||||||
|
|
||||||
const Building* findBuilding(BuildingId id) const;
|
|
||||||
const ConstructionSite* findSite(BuildingId id) const;
|
|
||||||
std::vector<Building> getAllBuildings() const;
|
|
||||||
std::vector<ConstructionSite> getAllSites() const;
|
|
||||||
|
|
||||||
// REQ-UI-DEBUG-OVERLAY "Max Factory Production": count of completed
|
// REQ-UI-DEBUG-OVERLAY "Max Factory Production": count of completed
|
||||||
// (operational) Miner/Smelter/Assembler/ReprocessingPlant/Shipyard buildings.
|
// (operational) Miner/Smelter/Assembler/ReprocessingPlant/Shipyard buildings.
|
||||||
int getProductionBuildingCount() const;
|
|
||||||
|
|
||||||
// REQ-UI-DEBUG-OVERLAY "Current Factory Production": subset of the above
|
// REQ-UI-DEBUG-OVERLAY "Current Factory Production": subset of the above
|
||||||
// that currently has an active production cycle.
|
// that currently has an active production cycle.
|
||||||
int getActiveProductionBuildingCount() const;
|
|
||||||
|
|
||||||
// Production state for the UI status light (REQ-UI-STATUS-LIGHT). Returns
|
// Production state for the UI status light (REQ-UI-STATUS-LIGHT). Returns
|
||||||
// nullopt for building types that show no light (belts, splitters, tunnels,
|
// nullopt for building types that show no light (belts, splitters, tunnels,
|
||||||
@@ -159,7 +152,6 @@ public:
|
|||||||
// Producing while its output buffer holds scrap, Starved when empty.
|
// Producing while its output buffer holds scrap, Starved when empty.
|
||||||
std::optional<ProductionStatus> getProductionStatus(const Building& building) const;
|
std::optional<ProductionStatus> getProductionStatus(const Building& building) const;
|
||||||
std::vector<BeltTileInfo> getAllBeltTiles() const;
|
std::vector<BeltTileInfo> getAllBeltTiles() const;
|
||||||
bool isTileOccupied(QPoint tile) const;
|
|
||||||
|
|
||||||
// Visits every item currently emerging from a building output port on its
|
// Visits every item currently emerging from a building output port on its
|
||||||
// virtual output belt (REQ-MAT-OUTPUT-EMERGE), passing the item type and its
|
// virtual output belt (REQ-MAT-OUTPUT-EMERGE), passing the item type and its
|
||||||
@@ -186,8 +178,6 @@ public:
|
|||||||
// currently on the tile are discarded by BeltSystem::removeTile).
|
// currently on the tile are discarded by BeltSystem::removeTile).
|
||||||
void rotateInPlace(BuildingId id, Rotation newRotation);
|
void rotateInPlace(BuildingId id, Rotation newRotation);
|
||||||
|
|
||||||
// Find nearest operational building of the given type; nullptr if none.
|
|
||||||
const Building* findNearestBuilding(QVector2D worldPos, BuildingType type) const;
|
|
||||||
|
|
||||||
// Input-capable adjacent tiles for a building or construction site
|
// Input-capable adjacent tiles for a building or construction site
|
||||||
// (REQ-BLD-BELT-DRAG, REQ-MAT-INPUT-PORTS): each returned Port.tile is the
|
// (REQ-BLD-BELT-DRAG, REQ-MAT-INPUT-PORTS): each returned Port.tile is the
|
||||||
@@ -201,7 +191,6 @@ public:
|
|||||||
|
|
||||||
// Place one "scrap" item into a SalvageBay's output buffer.
|
// Place one "scrap" item into a SalvageBay's output buffer.
|
||||||
// Returns false if bay not found, wrong type, or output buffer is full.
|
// Returns false if bay not found, wrong type, or output buffer is full.
|
||||||
bool deliverScrapToSalvageBay(BuildingId bayId);
|
|
||||||
|
|
||||||
// Bypass the construction queue and create a fully-operational Building
|
// Bypass the construction queue and create a fully-operational Building
|
||||||
// immediately. Used for pre-placed structures (HQ, defence stations).
|
// immediately. Used for pre-placed structures (HQ, defence stations).
|
||||||
@@ -234,7 +223,6 @@ private:
|
|||||||
const std::vector<ItemType>& splitterFilterA,
|
const std::vector<ItemType>& splitterFilterA,
|
||||||
const std::vector<ItemType>& splitterFilterB);
|
const std::vector<ItemType>& splitterFilterB);
|
||||||
|
|
||||||
Building* findBuildingMutable(BuildingId id);
|
|
||||||
// True if the consumer would accept `type` at the given input port right now:
|
// True if the consumer would accept `type` at the given input port right now:
|
||||||
// it is a required input (or a building block for the HQ), the reservation-aware
|
// it is a required input (or a building block for the HQ), the reservation-aware
|
||||||
// buffer has room, and the input belt entry is free (REQ-MAT-INPUT-INTAKE).
|
// buffer has room, and the input belt entry is free (REQ-MAT-INPUT-INTAKE).
|
||||||
|
|||||||
@@ -77,6 +77,12 @@ bool isTileOccupied(const FactoryState& state, QPoint tile)
|
|||||||
return state.grid.isOccupied(tile);
|
return state.grid.isOccupied(tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isQueuedForDeconstruction(const FactoryState& state, BuildingId id)
|
||||||
|
{
|
||||||
|
const Building* building = findBuilding(state, id);
|
||||||
|
return building && building->queuedForDeconstruction;
|
||||||
|
}
|
||||||
|
|
||||||
const Building* findNearestBuilding(const FactoryState& state, QVector2D worldPos,
|
const Building* findNearestBuilding(const FactoryState& state, QVector2D worldPos,
|
||||||
BuildingType type)
|
BuildingType type)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ int getActiveProductionBuildingCount(const FactoryState& state);
|
|||||||
|
|
||||||
bool isTileOccupied(const FactoryState& state, QPoint tile);
|
bool isTileOccupied(const FactoryState& state, QPoint tile);
|
||||||
|
|
||||||
|
// True while the building is in the deconstruction queue (REQ-BLD-DECON-QUEUE).
|
||||||
|
bool isQueuedForDeconstruction(const FactoryState& state, BuildingId id);
|
||||||
|
|
||||||
// The nearest building of the given type to a world position, or nullptr when
|
// The nearest building of the given type to a world position, or nullptr when
|
||||||
// none exists. Distance is measured to the building's footprint centre.
|
// none exists. Distance is measured to the building's footprint centre.
|
||||||
const Building* findNearestBuilding(const FactoryState& state, QVector2D worldPos,
|
const Building* findNearestBuilding(const FactoryState& state, QVector2D worldPos,
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "Simulation.h"
|
#include "Simulation.h"
|
||||||
|
|
||||||
|
#include "FactoryQueries.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
@@ -811,12 +813,12 @@ double Simulation::getThreatAccumulationRate() const
|
|||||||
|
|
||||||
double Simulation::getMaxFactoryProductionThreatRate() const
|
double Simulation::getMaxFactoryProductionThreatRate() const
|
||||||
{
|
{
|
||||||
return static_cast<double>(m_buildingSystem->getProductionBuildingCount());
|
return static_cast<double>(getProductionBuildingCount(m_factoryState));
|
||||||
}
|
}
|
||||||
|
|
||||||
double Simulation::getCurrentFactoryProductionThreatRate() const
|
double Simulation::getCurrentFactoryProductionThreatRate() const
|
||||||
{
|
{
|
||||||
return static_cast<double>(m_buildingSystem->getActiveProductionBuildingCount());
|
return static_cast<double>(getActiveProductionBuildingCount(m_factoryState));
|
||||||
}
|
}
|
||||||
|
|
||||||
int Simulation::getBossWaveCounter() const
|
int Simulation::getBossWaveCounter() const
|
||||||
@@ -895,6 +897,11 @@ BuildingSystem& Simulation::getBuildingsMutable()
|
|||||||
return *m_buildingSystem;
|
return *m_buildingSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const FactoryState& Simulation::getFactoryState() const
|
||||||
|
{
|
||||||
|
return m_factoryState;
|
||||||
|
}
|
||||||
|
|
||||||
const BuildingSystem& Simulation::getBuildings() const
|
const BuildingSystem& Simulation::getBuildings() const
|
||||||
{
|
{
|
||||||
return *m_buildingSystem;
|
return *m_buildingSystem;
|
||||||
|
|||||||
@@ -119,6 +119,9 @@ public:
|
|||||||
// chokepoint) or, in tests, SimulationTestAccess — so production code cannot
|
// chokepoint) or, in tests, SimulationTestAccess — so production code cannot
|
||||||
// mutate the factory outside the recorded command path (docs/replay_design.md).
|
// mutate the factory outside the recorded command path (docs/replay_design.md).
|
||||||
const BuildingSystem& getBuildings() const;
|
const BuildingSystem& getBuildings() const;
|
||||||
|
|
||||||
|
// The factory's world data, for the free queries in FactoryQueries.h.
|
||||||
|
const FactoryState& getFactoryState() const;
|
||||||
const BeltSystem& getBelts() const;
|
const BeltSystem& getBelts() const;
|
||||||
ShipSystem& getShips();
|
ShipSystem& getShips();
|
||||||
const ShipSystem& getShips() const;
|
const ShipSystem& getShips() const;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
|
#include "FactoryQueries.h"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <random>
|
#include <random>
|
||||||
@@ -957,12 +958,12 @@ TEST_CASE("BehaviorSystem: full-cargo salvage ship moves toward SalvageBay", "[b
|
|||||||
for (int i = 0; i < 500; ++i)
|
for (int i = 0; i < 500; ++i)
|
||||||
{
|
{
|
||||||
f.buildings.tickConstruction(t++);
|
f.buildings.tickConstruction(t++);
|
||||||
if (f.buildings.findBuilding(bayId) != nullptr)
|
if (findBuilding(f.state, bayId) != nullptr)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
REQUIRE(f.buildings.findBuilding(bayId) != nullptr);
|
REQUIRE(findBuilding(f.state, bayId) != nullptr);
|
||||||
|
|
||||||
const ShipLayoutConfig salvageLayout = makeSingleModuleLayout("salvager");
|
const ShipLayoutConfig salvageLayout = makeSingleModuleLayout("salvager");
|
||||||
const entt::entity ship = f.ships.spawn("salvage_ship", QVector2D(5.0f, 0.0f),
|
const entt::entity ship = f.ships.spawn("salvage_ship", QVector2D(5.0f, 0.0f),
|
||||||
@@ -992,9 +993,9 @@ TEST_CASE("SalvagerSystem: full-cargo ship at its SalvageBay hands over cargo",
|
|||||||
for (int i = 0; i < 500; ++i)
|
for (int i = 0; i < 500; ++i)
|
||||||
{
|
{
|
||||||
f.buildings.tickConstruction(t++);
|
f.buildings.tickConstruction(t++);
|
||||||
if (f.buildings.findBuilding(bayId) != nullptr) { break; }
|
if (findBuilding(f.state, bayId) != nullptr) { break; }
|
||||||
}
|
}
|
||||||
const Building* bay = f.buildings.findBuilding(bayId);
|
const Building* bay = findBuilding(f.state, bayId);
|
||||||
REQUIRE(bay != nullptr);
|
REQUIRE(bay != nullptr);
|
||||||
// Config-driven output-buffer capacity is applied on placement (REQ-BLD-SALVAGE-BAY).
|
// Config-driven output-buffer capacity is applied on placement (REQ-BLD-SALVAGE-BAY).
|
||||||
REQUIRE(bay->outputBuffer.capacity == 20);
|
REQUIRE(bay->outputBuffer.capacity == 20);
|
||||||
@@ -1015,7 +1016,7 @@ TEST_CASE("SalvagerSystem: full-cargo ship at its SalvageBay hands over cargo",
|
|||||||
|
|
||||||
// One unit handed over from cargo into the bay's output buffer.
|
// One unit handed over from cargo into the bay's output buffer.
|
||||||
REQUIRE(f.admin.get<CargoComponent>(ship).current == before - 1);
|
REQUIRE(f.admin.get<CargoComponent>(ship).current == before - 1);
|
||||||
const Building* bayAfter = f.buildings.findBuilding(bayId);
|
const Building* bayAfter = findBuilding(f.state, bayId);
|
||||||
REQUIRE(bayAfter != nullptr);
|
REQUIRE(bayAfter != nullptr);
|
||||||
REQUIRE(bayAfter->outputBuffer.items.size() == 1);
|
REQUIRE(bayAfter->outputBuffer.items.size() == 1);
|
||||||
REQUIRE(bayAfter->outputBuffer.items.front().type.id == "scrap");
|
REQUIRE(bayAfter->outputBuffer.items.front().type.id == "scrap");
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
|
#include "FactoryQueries.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
@@ -529,9 +530,9 @@ TEST_CASE("Blueprint placement: buildings land at anchor + offset from cursor",
|
|||||||
|
|
||||||
REQUIRE(idA != kInvalidBuildingId);
|
REQUIRE(idA != kInvalidBuildingId);
|
||||||
REQUIRE(idB != kInvalidBuildingId);
|
REQUIRE(idB != kInvalidBuildingId);
|
||||||
REQUIRE(sim.getBuildings().isTileOccupied(cursor + offsetA)); // (-6, 0)
|
REQUIRE(isTileOccupied(sim.getFactoryState(), cursor + offsetA)); // (-6, 0)
|
||||||
REQUIRE(sim.getBuildings().isTileOccupied(cursor + offsetB)); // (-4, 0)
|
REQUIRE(isTileOccupied(sim.getFactoryState(), cursor + offsetB)); // (-4, 0)
|
||||||
REQUIRE_FALSE(sim.getBuildings().isTileOccupied(cursor)); // center not occupied
|
REQUIRE_FALSE(isTileOccupied(sim.getFactoryState(), cursor)); // center not occupied
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Blueprint placement: cost is deducted for each building in sequence", "[blueprint]")
|
TEST_CASE("Blueprint placement: cost is deducted for each building in sequence", "[blueprint]")
|
||||||
@@ -600,7 +601,7 @@ TEST_CASE("Simulation: tryPlaceBuilding rejects terrain-invalid placement and ch
|
|||||||
|
|
||||||
REQUIRE_FALSE(id.has_value());
|
REQUIRE_FALSE(id.has_value());
|
||||||
REQUIRE(sim.getBuildingBlocksStock() == startBlocks);
|
REQUIRE(sim.getBuildingBlocksStock() == startBlocks);
|
||||||
REQUIRE(sim.getBuildings().getAllSites().empty());
|
REQUIRE(getAllSites(sim.getFactoryState()).empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Simulation: tryPlaceBuilding accepts a valid asteroid spot, occupies tiles, charges cost",
|
TEST_CASE("Simulation: tryPlaceBuilding accepts a valid asteroid spot, occupies tiles, charges cost",
|
||||||
@@ -623,11 +624,11 @@ TEST_CASE("Simulation: tryPlaceBuilding accepts a valid asteroid spot, occupies
|
|||||||
|
|
||||||
REQUIRE(id != kInvalidBuildingId);
|
REQUIRE(id != kInvalidBuildingId);
|
||||||
REQUIRE(sim.getBuildingBlocksStock() == startBlocks - minerCost);
|
REQUIRE(sim.getBuildingBlocksStock() == startBlocks - minerCost);
|
||||||
REQUIRE(sim.getBuildings().isTileOccupied(QPoint(-3, 0)));
|
REQUIRE(isTileOccupied(sim.getFactoryState(), QPoint(-3, 0)));
|
||||||
REQUIRE(sim.getBuildings().isTileOccupied(QPoint(-2, 0)));
|
REQUIRE(isTileOccupied(sim.getFactoryState(), QPoint(-2, 0)));
|
||||||
REQUIRE(sim.getBuildings().isTileOccupied(QPoint(-3, 1)));
|
REQUIRE(isTileOccupied(sim.getFactoryState(), QPoint(-3, 1)));
|
||||||
// The output-port tile (1,1)+anchor = (-2,1) is not a body cell.
|
// The output-port tile (1,1)+anchor = (-2,1) is not a body cell.
|
||||||
REQUIRE_FALSE(sim.getBuildings().isTileOccupied(QPoint(-2, 1)));
|
REQUIRE_FALSE(isTileOccupied(sim.getFactoryState(), QPoint(-2, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -667,7 +668,7 @@ TEST_CASE("Blueprint placement: setRecipe on construction site stores recipe", "
|
|||||||
|
|
||||||
SimulationTestAccess::buildings(sim).setRecipe(id, "mine_iron_ore");
|
SimulationTestAccess::buildings(sim).setRecipe(id, "mine_iron_ore");
|
||||||
|
|
||||||
const ConstructionSite* site = sim.getBuildings().findSite(id);
|
const ConstructionSite* site = findSite(sim.getFactoryState(), id);
|
||||||
REQUIRE(site != nullptr);
|
REQUIRE(site != nullptr);
|
||||||
REQUIRE(site->recipeId == "mine_iron_ore");
|
REQUIRE(site->recipeId == "mine_iron_ore");
|
||||||
}
|
}
|
||||||
@@ -688,7 +689,7 @@ TEST_CASE("Blueprint placement: recipe transfers to building after construction
|
|||||||
sim.tick();
|
sim.tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
const Building* b = sim.getBuildings().findBuilding(id);
|
const Building* b = findBuilding(sim.getFactoryState(), id);
|
||||||
REQUIRE(b != nullptr);
|
REQUIRE(b != nullptr);
|
||||||
REQUIRE(b->recipeId == "mine_copper_ore");
|
REQUIRE(b->recipeId == "mine_copper_ore");
|
||||||
}
|
}
|
||||||
@@ -708,8 +709,8 @@ TEST_CASE("Blueprint creation: a construction site is captured", "[blueprint]")
|
|||||||
const BuildingId id =
|
const BuildingId id =
|
||||||
SimulationTestAccess::place(sim, BuildingType::Belt, QPoint(-2, 0), Rotation::East).value();
|
SimulationTestAccess::place(sim, BuildingType::Belt, QPoint(-2, 0), Rotation::East).value();
|
||||||
REQUIRE(id != kInvalidBuildingId);
|
REQUIRE(id != kInvalidBuildingId);
|
||||||
REQUIRE(sim.getBuildings().findSite(id) != nullptr);
|
REQUIRE(findSite(sim.getFactoryState(), id) != nullptr);
|
||||||
REQUIRE(sim.getBuildings().findBuilding(id) == nullptr);
|
REQUIRE(findBuilding(sim.getFactoryState(), id) == nullptr);
|
||||||
|
|
||||||
const Blueprint bp = captureBlueprintFromSelection(sim, { id });
|
const Blueprint bp = captureBlueprintFromSelection(sim, { id });
|
||||||
|
|
||||||
@@ -744,14 +745,14 @@ TEST_CASE("Blueprint creation: mixed operational building and construction site
|
|||||||
REQUIRE(idA != kInvalidBuildingId);
|
REQUIRE(idA != kInvalidBuildingId);
|
||||||
SimulationTestAccess::buildings(sim).setRecipe(idA, "mine_iron_ore");
|
SimulationTestAccess::buildings(sim).setRecipe(idA, "mine_iron_ore");
|
||||||
for (int i = 0; i <= static_cast<int>(secondsToTicks(10.0)); ++i) { sim.tick(); }
|
for (int i = 0; i <= static_cast<int>(secondsToTicks(10.0)); ++i) { sim.tick(); }
|
||||||
REQUIRE(sim.getBuildings().findBuilding(idA) != nullptr);
|
REQUIRE(findBuilding(sim.getFactoryState(), idA) != nullptr);
|
||||||
|
|
||||||
// Building B: place and configure, but leave as a construction site.
|
// Building B: place and configure, but leave as a construction site.
|
||||||
const BuildingId idB =
|
const BuildingId idB =
|
||||||
SimulationTestAccess::place(sim, BuildingType::Miner, QPoint(-6, 0), Rotation::East).value();
|
SimulationTestAccess::place(sim, BuildingType::Miner, QPoint(-6, 0), Rotation::East).value();
|
||||||
REQUIRE(idB != kInvalidBuildingId);
|
REQUIRE(idB != kInvalidBuildingId);
|
||||||
SimulationTestAccess::buildings(sim).setRecipe(idB, "mine_copper_ore");
|
SimulationTestAccess::buildings(sim).setRecipe(idB, "mine_copper_ore");
|
||||||
REQUIRE(sim.getBuildings().findSite(idB) != nullptr);
|
REQUIRE(findSite(sim.getFactoryState(), idB) != nullptr);
|
||||||
|
|
||||||
const Blueprint bp = captureBlueprintFromSelection(sim, { idA, idB });
|
const Blueprint bp = captureBlueprintFromSelection(sim, { idA, idB });
|
||||||
|
|
||||||
@@ -775,7 +776,7 @@ TEST_CASE("Blueprint creation: selectionHasPlaceableBuilding sees a construction
|
|||||||
const BuildingId id =
|
const BuildingId id =
|
||||||
SimulationTestAccess::place(sim, BuildingType::Miner, QPoint(-2, 0), Rotation::East).value();
|
SimulationTestAccess::place(sim, BuildingType::Miner, QPoint(-2, 0), Rotation::East).value();
|
||||||
REQUIRE(id != kInvalidBuildingId);
|
REQUIRE(id != kInvalidBuildingId);
|
||||||
REQUIRE(sim.getBuildings().findSite(id) != nullptr);
|
REQUIRE(findSite(sim.getFactoryState(), id) != nullptr);
|
||||||
REQUIRE(selectionHasPlaceableBuilding(sim, { id }));
|
REQUIRE(selectionHasPlaceableBuilding(sim, { id }));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -855,7 +856,7 @@ TEST_CASE("Blueprint placement: setShipLayout on construction site stores layout
|
|||||||
|
|
||||||
SimulationTestAccess::buildings(sim).setShipLayout(id, layout);
|
SimulationTestAccess::buildings(sim).setShipLayout(id, layout);
|
||||||
|
|
||||||
const ConstructionSite* site = sim.getBuildings().findSite(id);
|
const ConstructionSite* site = findSite(sim.getFactoryState(), id);
|
||||||
REQUIRE(site != nullptr);
|
REQUIRE(site != nullptr);
|
||||||
REQUIRE(site->shipLayout.has_value());
|
REQUIRE(site->shipLayout.has_value());
|
||||||
REQUIRE(site->shipLayout->placedModules.size() == 1);
|
REQUIRE(site->shipLayout->placedModules.size() == 1);
|
||||||
@@ -892,7 +893,7 @@ TEST_CASE("Blueprint placement: ship layout transfers to building after construc
|
|||||||
sim.tick();
|
sim.tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
const Building* b = sim.getBuildings().findBuilding(id);
|
const Building* b = findBuilding(sim.getFactoryState(), id);
|
||||||
REQUIRE(b != nullptr);
|
REQUIRE(b != nullptr);
|
||||||
REQUIRE(b->shipLayout.has_value());
|
REQUIRE(b->shipLayout.has_value());
|
||||||
REQUIRE(b->shipLayout->placedModules.size() == 1);
|
REQUIRE(b->shipLayout->placedModules.size() == 1);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
|
#include "FactoryQueries.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
@@ -122,8 +123,8 @@ TEST_CASE("readBuildingConfig reads a queued construction site", "[copyconfig]")
|
|||||||
const BuildingId id =
|
const BuildingId id =
|
||||||
SimulationTestAccess::place(sim, BuildingType::Miner, QPoint(-2, 0), Rotation::East).value();
|
SimulationTestAccess::place(sim, BuildingType::Miner, QPoint(-2, 0), Rotation::East).value();
|
||||||
REQUIRE(id != kInvalidBuildingId);
|
REQUIRE(id != kInvalidBuildingId);
|
||||||
REQUIRE(sim.getBuildings().findBuilding(id) == nullptr);
|
REQUIRE(findBuilding(sim.getFactoryState(), id) == nullptr);
|
||||||
REQUIRE(sim.getBuildings().findSite(id) != nullptr);
|
REQUIRE(findSite(sim.getFactoryState(), id) != nullptr);
|
||||||
|
|
||||||
SimulationTestAccess::buildings(sim).setRecipe(id, "mine_iron_ore");
|
SimulationTestAccess::buildings(sim).setRecipe(id, "mine_iron_ore");
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
|
#include "FactoryQueries.h"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <random>
|
#include <random>
|
||||||
@@ -125,11 +126,11 @@ TEST_CASE("BuildingSystem: place miner occupies expected body tiles", "[building
|
|||||||
REQUIRE(id != kInvalidBuildingId);
|
REQUIRE(id != kInvalidBuildingId);
|
||||||
|
|
||||||
// Miner mask ["AA","A>"] with East rotation → body at (0,0),(1,0),(0,1).
|
// Miner mask ["AA","A>"] with East rotation → body at (0,0),(1,0),(0,1).
|
||||||
REQUIRE(bs.isTileOccupied(QPoint(0, 0)));
|
REQUIRE(isTileOccupied(state_bs, QPoint(0, 0)));
|
||||||
REQUIRE(bs.isTileOccupied(QPoint(1, 0)));
|
REQUIRE(isTileOccupied(state_bs, QPoint(1, 0)));
|
||||||
REQUIRE(bs.isTileOccupied(QPoint(0, 1)));
|
REQUIRE(isTileOccupied(state_bs, QPoint(0, 1)));
|
||||||
// (1,1) is the output-port tile, NOT a body cell.
|
// (1,1) is the output-port tile, NOT a body cell.
|
||||||
REQUIRE_FALSE(bs.isTileOccupied(QPoint(1, 1)));
|
REQUIRE_FALSE(isTileOccupied(state_bs, QPoint(1, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- World-bounds rejection (REQ-BLD-PLACE-VALID) ---------------------------
|
// -- World-bounds rejection (REQ-BLD-PLACE-VALID) ---------------------------
|
||||||
@@ -142,8 +143,8 @@ TEST_CASE("BuildingSystem: place rejects a building above the world (y < 0)", "[
|
|||||||
// row sits above the world.
|
// row sits above the world.
|
||||||
const std::optional<BuildingId> id = f.bs.place(BuildingType::Miner, QPoint(0, -1), Rotation::East, 0);
|
const std::optional<BuildingId> id = f.bs.place(BuildingType::Miner, QPoint(0, -1), Rotation::East, 0);
|
||||||
REQUIRE_FALSE(id.has_value());
|
REQUIRE_FALSE(id.has_value());
|
||||||
REQUIRE(f.bs.getAllSites().empty());
|
REQUIRE(getAllSites(f.state).empty());
|
||||||
REQUIRE_FALSE(f.bs.isTileOccupied(QPoint(0, 0)));
|
REQUIRE_FALSE(isTileOccupied(f.state, QPoint(0, 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("BuildingSystem: place rejects a building below the world (y >= height)", "[building]")
|
TEST_CASE("BuildingSystem: place rejects a building below the world (y >= height)", "[building]")
|
||||||
@@ -156,7 +157,7 @@ TEST_CASE("BuildingSystem: place rejects a building below the world (y >= height
|
|||||||
const std::optional<BuildingId> id = f.bs.place(BuildingType::Miner,
|
const std::optional<BuildingId> id = f.bs.place(BuildingType::Miner,
|
||||||
QPoint(0, heightTiles - 1), Rotation::East, 0);
|
QPoint(0, heightTiles - 1), Rotation::East, 0);
|
||||||
REQUIRE_FALSE(id.has_value());
|
REQUIRE_FALSE(id.has_value());
|
||||||
REQUIRE(f.bs.getAllSites().empty());
|
REQUIRE(getAllSites(f.state).empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("BuildingSystem: place rejects a building left of the asteroid edge", "[building]")
|
TEST_CASE("BuildingSystem: place rejects a building left of the asteroid edge", "[building]")
|
||||||
@@ -167,7 +168,7 @@ TEST_CASE("BuildingSystem: place rejects a building left of the asteroid edge",
|
|||||||
const std::optional<BuildingId> id = f.bs.place(BuildingType::Miner,
|
const std::optional<BuildingId> id = f.bs.place(BuildingType::Miner,
|
||||||
QPoint(leftEdgeX - 1, 0), Rotation::East, 0);
|
QPoint(leftEdgeX - 1, 0), Rotation::East, 0);
|
||||||
REQUIRE_FALSE(id.has_value());
|
REQUIRE_FALSE(id.has_value());
|
||||||
REQUIRE(f.bs.getAllSites().empty());
|
REQUIRE(getAllSites(f.state).empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("BuildingSystem: place accepts a building flush against the world's left edge",
|
TEST_CASE("BuildingSystem: place accepts a building flush against the world's left edge",
|
||||||
@@ -180,7 +181,7 @@ TEST_CASE("BuildingSystem: place accepts a building flush against the world's le
|
|||||||
const BuildingId id = f.bs.place(BuildingType::Miner,
|
const BuildingId id = f.bs.place(BuildingType::Miner,
|
||||||
QPoint(leftEdgeX, 0), Rotation::East, 0).value();
|
QPoint(leftEdgeX, 0), Rotation::East, 0).value();
|
||||||
REQUIRE(id != kInvalidBuildingId);
|
REQUIRE(id != kInvalidBuildingId);
|
||||||
REQUIRE(f.bs.isTileOccupied(QPoint(leftEdgeX, 0)));
|
REQUIRE(isTileOccupied(f.state, QPoint(leftEdgeX, 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("BuildingSystem: place imposes no right-side bound (space extends rightward)",
|
TEST_CASE("BuildingSystem: place imposes no right-side bound (space extends rightward)",
|
||||||
@@ -240,9 +241,9 @@ TEST_CASE("BuildingSystem: placing a belt registers it with BeltSystem after con
|
|||||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(1.0)) + 1, tick);
|
runTicks(bs, belts, static_cast<int>(secondsToTicks(1.0)) + 1, tick);
|
||||||
|
|
||||||
REQUIRE(belts.tryPutItem(QPoint(5, 5), makeItem("iron_ore"), Rotation::East));
|
REQUIRE(belts.tryPutItem(QPoint(5, 5), makeItem("iron_ore"), Rotation::East));
|
||||||
REQUIRE(bs.getAllBuildings().size() == 1);
|
REQUIRE(getAllBuildings(state_bs).size() == 1);
|
||||||
REQUIRE(bs.getAllBuildings()[0].type == BuildingType::Belt);
|
REQUIRE(getAllBuildings(state_bs)[0].type == BuildingType::Belt);
|
||||||
REQUIRE(bs.getAllBuildings()[0].anchor == QPoint(5, 5));
|
REQUIRE(getAllBuildings(state_bs)[0].anchor == QPoint(5, 5));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("BuildingSystem: placed building enters construction queue", "[building]")
|
TEST_CASE("BuildingSystem: placed building enters construction queue", "[building]")
|
||||||
@@ -262,9 +263,9 @@ TEST_CASE("BuildingSystem: placed building enters construction queue", "[buildin
|
|||||||
|
|
||||||
const BuildingId id = bs.place(BuildingType::Miner, QPoint(0, 0), Rotation::East, 0).value();
|
const BuildingId id = bs.place(BuildingType::Miner, QPoint(0, 0), Rotation::East, 0).value();
|
||||||
|
|
||||||
REQUIRE(bs.getAllSites().size() == 1);
|
REQUIRE(getAllSites(state_bs).size() == 1);
|
||||||
REQUIRE(bs.getAllBuildings().empty());
|
REQUIRE(getAllBuildings(state_bs).empty());
|
||||||
REQUIRE(bs.findSite(id) != nullptr);
|
REQUIRE(findSite(state_bs, id) != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("BuildingSystem: deconstructing a construction site removes it instantly with full refund",
|
TEST_CASE("BuildingSystem: deconstructing a construction site removes it instantly with full refund",
|
||||||
@@ -279,8 +280,8 @@ TEST_CASE("BuildingSystem: deconstructing a construction site removes it instant
|
|||||||
const int refund = f.bs.deconstruct(id, 0);
|
const int refund = f.bs.deconstruct(id, 0);
|
||||||
|
|
||||||
REQUIRE(refund == 15); // Miner cost = 15
|
REQUIRE(refund == 15); // Miner cost = 15
|
||||||
REQUIRE_FALSE(f.bs.isTileOccupied(QPoint(0, 0)));
|
REQUIRE_FALSE(isTileOccupied(f.state, QPoint(0, 0)));
|
||||||
REQUIRE(f.bs.getAllSites().empty());
|
REQUIRE(getAllSites(f.state).empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -304,7 +305,7 @@ TEST_CASE("BuildingSystem: first queued building starts construction immediately
|
|||||||
rng);
|
rng);
|
||||||
|
|
||||||
bs.place(BuildingType::Miner, QPoint(0, 0), Rotation::East, 0);
|
bs.place(BuildingType::Miner, QPoint(0, 0), Rotation::East, 0);
|
||||||
REQUIRE(bs.getAllSites().front().completesAt > 0);
|
REQUIRE(getAllSites(state_bs).front().completesAt > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("BuildingSystem: second queued building waits (completesAt == 0)", "[building]")
|
TEST_CASE("BuildingSystem: second queued building waits (completesAt == 0)", "[building]")
|
||||||
@@ -325,9 +326,9 @@ TEST_CASE("BuildingSystem: second queued building waits (completesAt == 0)", "[b
|
|||||||
bs.place(BuildingType::Miner, QPoint(0, 0), Rotation::East, 0);
|
bs.place(BuildingType::Miner, QPoint(0, 0), Rotation::East, 0);
|
||||||
bs.place(BuildingType::Miner, QPoint(5, 5), Rotation::East, 0);
|
bs.place(BuildingType::Miner, QPoint(5, 5), Rotation::East, 0);
|
||||||
|
|
||||||
REQUIRE(bs.getAllSites().size() == 2);
|
REQUIRE(getAllSites(state_bs).size() == 2);
|
||||||
REQUIRE(bs.getAllSites()[0].completesAt > 0);
|
REQUIRE(getAllSites(state_bs)[0].completesAt > 0);
|
||||||
REQUIRE(bs.getAllSites()[1].completesAt == 0);
|
REQUIRE(getAllSites(state_bs)[1].completesAt == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("BuildingSystem: construction completes after configured duration", "[building]")
|
TEST_CASE("BuildingSystem: construction completes after configured duration", "[building]")
|
||||||
@@ -352,8 +353,8 @@ TEST_CASE("BuildingSystem: construction completes after configured duration", "[
|
|||||||
Tick tick = 0;
|
Tick tick = 0;
|
||||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(10.0)) + 1, tick);
|
runTicks(bs, belts, static_cast<int>(secondsToTicks(10.0)) + 1, tick);
|
||||||
|
|
||||||
REQUIRE(bs.getAllSites().empty());
|
REQUIRE(getAllSites(state_bs).empty());
|
||||||
REQUIRE(bs.findBuilding(id) != nullptr);
|
REQUIRE(findBuilding(state_bs, id) != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -363,11 +364,11 @@ TEST_CASE("BuildingSystem: construction completes after configured duration", "[
|
|||||||
// Runs ticks until the building with the given id is operational, or fails.
|
// Runs ticks until the building with the given id is operational, or fails.
|
||||||
static void runUntilBuilt(PlacementFixture& f, BuildingId id, Tick& tick)
|
static void runUntilBuilt(PlacementFixture& f, BuildingId id, Tick& tick)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 100000 && f.bs.findBuilding(id) == nullptr; ++i)
|
for (int i = 0; i < 100000 && findBuilding(f.state, id) == nullptr; ++i)
|
||||||
{
|
{
|
||||||
runTicks(f.bs, f.belts, 1, tick);
|
runTicks(f.bs, f.belts, 1, tick);
|
||||||
}
|
}
|
||||||
REQUIRE(f.bs.findBuilding(id) != nullptr);
|
REQUIRE(findBuilding(f.state, id) != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("BuildingSystem: deconstructing a built building queues it; refund credited on completion",
|
TEST_CASE("BuildingSystem: deconstructing a built building queues it; refund credited on completion",
|
||||||
@@ -384,15 +385,15 @@ TEST_CASE("BuildingSystem: deconstructing a built building queues it; refund cre
|
|||||||
// stopping it operating while its tiles stay occupied (REQ-BLD-DECON-QUEUE).
|
// stopping it operating while its tiles stay occupied (REQ-BLD-DECON-QUEUE).
|
||||||
const int refund = f.bs.deconstruct(id, tick);
|
const int refund = f.bs.deconstruct(id, tick);
|
||||||
REQUIRE(refund == 0);
|
REQUIRE(refund == 0);
|
||||||
REQUIRE(f.bs.isQueuedForDeconstruction(id));
|
REQUIRE(isQueuedForDeconstruction(f.state, id));
|
||||||
REQUIRE(f.bs.isTileOccupied(QPoint(0, 0)));
|
REQUIRE(isTileOccupied(f.state, QPoint(0, 0)));
|
||||||
REQUIRE(f.stock == 0);
|
REQUIRE(f.stock == 0);
|
||||||
|
|
||||||
// After the deconstruction time (0.1s = 3 ticks) it is removed and the partial
|
// After the deconstruction time (0.1s = 3 ticks) it is removed and the partial
|
||||||
// refund (15 * 75 / 100 = 11) is credited exactly once.
|
// refund (15 * 75 / 100 = 11) is credited exactly once.
|
||||||
runTicks(f.bs, f.belts, static_cast<int>(secondsToTicks(0.1)) + 1, tick);
|
runTicks(f.bs, f.belts, static_cast<int>(secondsToTicks(0.1)) + 1, tick);
|
||||||
REQUIRE(f.bs.findBuilding(id) == nullptr);
|
REQUIRE(findBuilding(f.state, id) == nullptr);
|
||||||
REQUIRE_FALSE(f.bs.isTileOccupied(QPoint(0, 0)));
|
REQUIRE_FALSE(isTileOccupied(f.state, QPoint(0, 0)));
|
||||||
REQUIRE(f.stock == 15 * f.cfg.world.refundPercentage / 100);
|
REQUIRE(f.stock == 15 * f.cfg.world.refundPercentage / 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,20 +410,20 @@ TEST_CASE("BuildingSystem: deconstruction queue removes one building at a time",
|
|||||||
// Queue both in one tick; 'a' is at the front of the deconstruction queue.
|
// Queue both in one tick; 'a' is at the front of the deconstruction queue.
|
||||||
f.bs.deconstruct(a, tick);
|
f.bs.deconstruct(a, tick);
|
||||||
f.bs.deconstruct(b, tick);
|
f.bs.deconstruct(b, tick);
|
||||||
REQUIRE(f.bs.isQueuedForDeconstruction(a));
|
REQUIRE(isQueuedForDeconstruction(f.state, a));
|
||||||
REQUIRE(f.bs.isQueuedForDeconstruction(b));
|
REQUIRE(isQueuedForDeconstruction(f.state, b));
|
||||||
|
|
||||||
// After one deconstruction interval only the front building is gone; the
|
// After one deconstruction interval only the front building is gone; the
|
||||||
// second is still queued and its refund not yet credited.
|
// second is still queued and its refund not yet credited.
|
||||||
runTicks(f.bs, f.belts, static_cast<int>(secondsToTicks(0.1)) + 1, tick);
|
runTicks(f.bs, f.belts, static_cast<int>(secondsToTicks(0.1)) + 1, tick);
|
||||||
REQUIRE(f.bs.findBuilding(a) == nullptr);
|
REQUIRE(findBuilding(f.state, a) == nullptr);
|
||||||
REQUIRE(f.bs.findBuilding(b) != nullptr);
|
REQUIRE(findBuilding(f.state, b) != nullptr);
|
||||||
REQUIRE(f.bs.isQueuedForDeconstruction(b));
|
REQUIRE(isQueuedForDeconstruction(f.state, b));
|
||||||
REQUIRE(f.stock == 15 * f.cfg.world.refundPercentage / 100);
|
REQUIRE(f.stock == 15 * f.cfg.world.refundPercentage / 100);
|
||||||
|
|
||||||
// The second drains next.
|
// The second drains next.
|
||||||
runTicks(f.bs, f.belts, static_cast<int>(secondsToTicks(0.1)) + 2, tick);
|
runTicks(f.bs, f.belts, static_cast<int>(secondsToTicks(0.1)) + 2, tick);
|
||||||
REQUIRE(f.bs.findBuilding(b) == nullptr);
|
REQUIRE(findBuilding(f.state, b) == nullptr);
|
||||||
REQUIRE(f.stock == 2 * (15 * f.cfg.world.refundPercentage / 100));
|
REQUIRE(f.stock == 2 * (15 * f.cfg.world.refundPercentage / 100));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,18 +438,18 @@ TEST_CASE("BuildingSystem: cancelling deconstruction resumes the building with n
|
|||||||
runUntilBuilt(f, id, tick);
|
runUntilBuilt(f, id, tick);
|
||||||
|
|
||||||
f.bs.deconstruct(id, tick);
|
f.bs.deconstruct(id, tick);
|
||||||
REQUIRE(f.bs.isQueuedForDeconstruction(id));
|
REQUIRE(isQueuedForDeconstruction(f.state, id));
|
||||||
|
|
||||||
// Un-queue before it drains: it operates again, no refund, tiles still occupied.
|
// Un-queue before it drains: it operates again, no refund, tiles still occupied.
|
||||||
f.bs.cancelDeconstruction(id);
|
f.bs.cancelDeconstruction(id);
|
||||||
REQUIRE_FALSE(f.bs.isQueuedForDeconstruction(id));
|
REQUIRE_FALSE(isQueuedForDeconstruction(f.state, id));
|
||||||
REQUIRE(f.bs.findBuilding(id) != nullptr);
|
REQUIRE(findBuilding(f.state, id) != nullptr);
|
||||||
REQUIRE(f.bs.isTileOccupied(QPoint(0, 0)));
|
REQUIRE(isTileOccupied(f.state, QPoint(0, 0)));
|
||||||
REQUIRE(f.stock == 0);
|
REQUIRE(f.stock == 0);
|
||||||
|
|
||||||
// It is never removed even after more than a deconstruction interval passes.
|
// It is never removed even after more than a deconstruction interval passes.
|
||||||
runTicks(f.bs, f.belts, static_cast<int>(secondsToTicks(0.1)) + 5, tick);
|
runTicks(f.bs, f.belts, static_cast<int>(secondsToTicks(0.1)) + 5, tick);
|
||||||
REQUIRE(f.bs.findBuilding(id) != nullptr);
|
REQUIRE(findBuilding(f.state, id) != nullptr);
|
||||||
REQUIRE(f.stock == 0);
|
REQUIRE(f.stock == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -466,7 +467,7 @@ TEST_CASE("BuildingSystem: queued belt stops transporting; cancel restores it",
|
|||||||
// accepts or transports items, though the tile stays occupied (REQ-BLD-DECON-QUEUE).
|
// accepts or transports items, though the tile stays occupied (REQ-BLD-DECON-QUEUE).
|
||||||
f.bs.deconstruct(id, tick);
|
f.bs.deconstruct(id, tick);
|
||||||
REQUIRE_FALSE(f.belts.tryPutItem(QPoint(0, 0), makeItem("iron_ore"), Rotation::East));
|
REQUIRE_FALSE(f.belts.tryPutItem(QPoint(0, 0), makeItem("iron_ore"), Rotation::East));
|
||||||
REQUIRE(f.bs.isTileOccupied(QPoint(0, 0)));
|
REQUIRE(isTileOccupied(f.state, QPoint(0, 0)));
|
||||||
|
|
||||||
// Un-queuing re-registers the belt tile so it transports again.
|
// Un-queuing re-registers the belt tile so it transports again.
|
||||||
f.bs.cancelDeconstruction(id);
|
f.bs.cancelDeconstruction(id);
|
||||||
@@ -519,9 +520,9 @@ TEST_CASE("BuildingSystem: second building starts after first completes", "[buil
|
|||||||
Tick tick = 0;
|
Tick tick = 0;
|
||||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(10.0)) + 1, tick);
|
runTicks(bs, belts, static_cast<int>(secondsToTicks(10.0)) + 1, tick);
|
||||||
|
|
||||||
REQUIRE(bs.getAllSites().size() == 1);
|
REQUIRE(getAllSites(state_bs).size() == 1);
|
||||||
REQUIRE(bs.getAllSites().front().id == id2);
|
REQUIRE(getAllSites(state_bs).front().id == id2);
|
||||||
REQUIRE(bs.getAllSites().front().completesAt > 0);
|
REQUIRE(getAllSites(state_bs).front().completesAt > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -553,7 +554,7 @@ TEST_CASE("BuildingSystem: miner produces iron_ore after recipe duration", "[bui
|
|||||||
static_cast<int>(secondsToTicks(10.0)) + static_cast<int>(secondsToTicks(1.0)) + 1,
|
static_cast<int>(secondsToTicks(10.0)) + static_cast<int>(secondsToTicks(1.0)) + 1,
|
||||||
tick);
|
tick);
|
||||||
|
|
||||||
const Building* b = bs.findBuilding(id);
|
const Building* b = findBuilding(state_bs, id);
|
||||||
REQUIRE(b != nullptr);
|
REQUIRE(b != nullptr);
|
||||||
// No belt at the output port, so the produced item emerges and stays on the
|
// No belt at the output port, so the produced item emerges and stays on the
|
||||||
// building's virtual output belt (REQ-MAT-OUTPUT-EMERGE).
|
// building's virtual output belt (REQ-MAT-OUTPUT-EMERGE).
|
||||||
@@ -591,7 +592,7 @@ TEST_CASE("BuildingSystem: miner output buffer stalls when full", "[building]")
|
|||||||
+ 2 * static_cast<int>(secondsToTicks(1.0)) + 2,
|
+ 2 * static_cast<int>(secondsToTicks(1.0)) + 2,
|
||||||
tick);
|
tick);
|
||||||
|
|
||||||
const Building* b = bs.findBuilding(id);
|
const Building* b = findBuilding(state_bs, id);
|
||||||
REQUIRE(b != nullptr);
|
REQUIRE(b != nullptr);
|
||||||
// Both produced items are held on the output side (buffer + emerging lane),
|
// Both produced items are held on the output side (buffer + emerging lane),
|
||||||
// which is what the capacity rule counts (REQ-MAT-OUTPUT-EMERGE).
|
// which is what the capacity rule counts (REQ-MAT-OUTPUT-EMERGE).
|
||||||
@@ -624,23 +625,23 @@ TEST_CASE("BuildingSystem: productionBuildingCount excludes construction sites",
|
|||||||
|
|
||||||
Tick tick = 0;
|
Tick tick = 0;
|
||||||
// Both still under construction.
|
// Both still under construction.
|
||||||
REQUIRE(bs.getProductionBuildingCount() == 0);
|
REQUIRE(getProductionBuildingCount(state_bs) == 0);
|
||||||
|
|
||||||
// The queue builds one at a time: miner (10s) completes at tick 300, then
|
// The queue builds one at a time: miner (10s) completes at tick 300, then
|
||||||
// the smelter (15s) starts and completes at tick 300 + 450 = 750.
|
// the smelter (15s) starts and completes at tick 300 + 450 = 750.
|
||||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(10.0)) + 1, tick);
|
runTicks(bs, belts, static_cast<int>(secondsToTicks(10.0)) + 1, tick);
|
||||||
REQUIRE(bs.getProductionBuildingCount() == 1);
|
REQUIRE(getProductionBuildingCount(state_bs) == 1);
|
||||||
|
|
||||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(15.0)), tick);
|
runTicks(bs, belts, static_cast<int>(secondsToTicks(15.0)), tick);
|
||||||
REQUIRE(bs.getProductionBuildingCount() == 2);
|
REQUIRE(getProductionBuildingCount(state_bs) == 2);
|
||||||
|
|
||||||
// Neither is producing yet: the miner has no recipe selected, and the
|
// Neither is producing yet: the miner has no recipe selected, and the
|
||||||
// smelter (auto-recipe, REQ-BLD-SMELTER) has no input feeding it.
|
// smelter (auto-recipe, REQ-BLD-SMELTER) has no input feeding it.
|
||||||
REQUIRE(bs.getActiveProductionBuildingCount() == 0);
|
REQUIRE(getActiveProductionBuildingCount(state_bs) == 0);
|
||||||
|
|
||||||
bs.setRecipe(minerId, "mine_iron_ore");
|
bs.setRecipe(minerId, "mine_iron_ore");
|
||||||
runTicks(bs, belts, 1, tick);
|
runTicks(bs, belts, 1, tick);
|
||||||
REQUIRE(bs.getActiveProductionBuildingCount() == 1);
|
REQUIRE(getActiveProductionBuildingCount(state_bs) == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("BuildingSystem: activeProductionBuildingCount tracks production cycle state",
|
TEST_CASE("BuildingSystem: activeProductionBuildingCount tracks production cycle state",
|
||||||
@@ -664,21 +665,21 @@ TEST_CASE("BuildingSystem: activeProductionBuildingCount tracks production cycle
|
|||||||
|
|
||||||
Tick tick = 0;
|
Tick tick = 0;
|
||||||
// Not yet operational while under construction.
|
// Not yet operational while under construction.
|
||||||
REQUIRE(bs.getActiveProductionBuildingCount() == 0);
|
REQUIRE(getActiveProductionBuildingCount(state_bs) == 0);
|
||||||
|
|
||||||
// Construction completes at tick 300; cycle 1 starts the same tick (completesAt=330).
|
// Construction completes at tick 300; cycle 1 starts the same tick (completesAt=330).
|
||||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(10.0)) + 1, tick);
|
runTicks(bs, belts, static_cast<int>(secondsToTicks(10.0)) + 1, tick);
|
||||||
REQUIRE(bs.getActiveProductionBuildingCount() == 1);
|
REQUIRE(getActiveProductionBuildingCount(state_bs) == 1);
|
||||||
|
|
||||||
// Run cycles 1 and 2 to completion (1s each); cycle 3 stalls once the
|
// Run cycles 1 and 2 to completion (1s each); cycle 3 stalls once the
|
||||||
// output buffer (capacity 2) is full (REQ-MAT-OUTPUT-BUFFER).
|
// output buffer (capacity 2) is full (REQ-MAT-OUTPUT-BUFFER).
|
||||||
runTicks(bs, belts, 2 * static_cast<int>(secondsToTicks(1.0)) + 1, tick);
|
runTicks(bs, belts, 2 * static_cast<int>(secondsToTicks(1.0)) + 1, tick);
|
||||||
|
|
||||||
const Building* b = bs.findBuilding(id);
|
const Building* b = findBuilding(state_bs, id);
|
||||||
REQUIRE(b != nullptr);
|
REQUIRE(b != nullptr);
|
||||||
REQUIRE(b->getOutputItemCount() == 2);
|
REQUIRE(b->getOutputItemCount() == 2);
|
||||||
REQUIRE_FALSE(b->production.has_value());
|
REQUIRE_FALSE(b->production.has_value());
|
||||||
REQUIRE(bs.getActiveProductionBuildingCount() == 0);
|
REQUIRE(getActiveProductionBuildingCount(state_bs) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -719,7 +720,7 @@ TEST_CASE("BuildingSystem: smelter input buffer fills from adjacent west-flowing
|
|||||||
|
|
||||||
bs.tickBeltPull();
|
bs.tickBeltPull();
|
||||||
|
|
||||||
const Building* b = bs.findBuilding(sid);
|
const Building* b = findBuilding(state_bs, sid);
|
||||||
REQUIRE(b != nullptr);
|
REQUIRE(b != nullptr);
|
||||||
// The item was accepted; it may still be travelling inward on the input belt,
|
// The item was accepted; it may still be travelling inward on the input belt,
|
||||||
// so count buffered + in-transit (REQ-MAT-INPUT-INTAKE).
|
// so count buffered + in-transit (REQ-MAT-INPUT-INTAKE).
|
||||||
@@ -754,7 +755,7 @@ TEST_CASE("BuildingSystem: accepted input travels inward before entering the buf
|
|||||||
belts.tick();
|
belts.tick();
|
||||||
bs.tickBeltPull(); // accepts the item onto the input belt at progress 0.0
|
bs.tickBeltPull(); // accepts the item onto the input belt at progress 0.0
|
||||||
|
|
||||||
const Building* b = bs.findBuilding(sid);
|
const Building* b = findBuilding(state_bs, sid);
|
||||||
REQUIRE(b != nullptr);
|
REQUIRE(b != nullptr);
|
||||||
// Reserved but not yet consumable: nothing in the buffer, but it counts against
|
// Reserved but not yet consumable: nothing in the buffer, but it counts against
|
||||||
// the cap via pendingInputCount.
|
// the cap via pendingInputCount.
|
||||||
@@ -802,7 +803,7 @@ TEST_CASE("BuildingSystem: input reservation caps buffered plus in-transit at th
|
|||||||
bs.tickBeltPull();
|
bs.tickBeltPull();
|
||||||
}
|
}
|
||||||
|
|
||||||
const Building* b = bs.findBuilding(id);
|
const Building* b = findBuilding(state_bs, id);
|
||||||
REQUIRE(b != nullptr);
|
REQUIRE(b != nullptr);
|
||||||
const int cap = b->inputBuffer.caps.at(ItemType{"scrap"});
|
const int cap = b->inputBuffer.caps.at(ItemType{"scrap"});
|
||||||
REQUIRE(cap > 0);
|
REQUIRE(cap > 0);
|
||||||
@@ -848,7 +849,7 @@ TEST_CASE("BuildingSystem: smelter auto-smelts ore without a recipe selection",
|
|||||||
// iron_ingot recipe cycle is 2s; run to completion.
|
// iron_ingot recipe cycle is 2s; run to completion.
|
||||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(2.0)) + 2, tick);
|
runTicks(bs, belts, static_cast<int>(secondsToTicks(2.0)) + 2, tick);
|
||||||
|
|
||||||
const Building* b = bs.findBuilding(sid);
|
const Building* b = findBuilding(state_bs, sid);
|
||||||
REQUIRE(b != nullptr);
|
REQUIRE(b != nullptr);
|
||||||
bool hasIronIngot = false;
|
bool hasIronIngot = false;
|
||||||
for (const Item& item : outputSideItems(*b))
|
for (const Item& item : outputSideItems(*b))
|
||||||
@@ -896,7 +897,7 @@ TEST_CASE("BuildingSystem: smelter runs a satisfiable recipe while an incomplete
|
|||||||
// copper_ingot cycle is 2.5s; run to completion.
|
// copper_ingot cycle is 2.5s; run to completion.
|
||||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(2.5)) + 2, tick);
|
runTicks(bs, belts, static_cast<int>(secondsToTicks(2.5)) + 2, tick);
|
||||||
|
|
||||||
const Building* b = bs.findBuilding(sid);
|
const Building* b = findBuilding(state_bs, sid);
|
||||||
REQUIRE(b != nullptr);
|
REQUIRE(b != nullptr);
|
||||||
|
|
||||||
// Copper was smelted; the lone iron_ore still waits for a second unit.
|
// Copper was smelted; the lone iron_ore still waits for a second unit.
|
||||||
@@ -984,7 +985,7 @@ TEST_CASE("BuildingSystem: output port couples directly into an adjacent input p
|
|||||||
// Smelter build (15s) + margin for coupling and a smelt cycle.
|
// Smelter build (15s) + margin for coupling and a smelt cycle.
|
||||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(30.0)), tick);
|
runTicks(bs, belts, static_cast<int>(secondsToTicks(30.0)), tick);
|
||||||
|
|
||||||
const Building* smelter = bs.findBuilding(smelterId);
|
const Building* smelter = findBuilding(state_bs, smelterId);
|
||||||
REQUIRE(smelter != nullptr);
|
REQUIRE(smelter != nullptr);
|
||||||
// iron_ore reached the smelter over the direct coupling and was smelted.
|
// iron_ore reached the smelter over the direct coupling and was smelted.
|
||||||
bool hasIronIngot = false;
|
bool hasIronIngot = false;
|
||||||
@@ -1024,8 +1025,8 @@ TEST_CASE("BuildingSystem: direct coupling to a non-consumer leaves the item stu
|
|||||||
// Both miners build sequentially (10s each), then the producer runs and jams.
|
// Both miners build sequentially (10s each), then the producer runs and jams.
|
||||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(25.0)), tick);
|
runTicks(bs, belts, static_cast<int>(secondsToTicks(25.0)), tick);
|
||||||
|
|
||||||
const Building* miner = bs.findBuilding(minerId);
|
const Building* miner = findBuilding(state_bs, minerId);
|
||||||
const Building* sink = bs.findBuilding(sinkId);
|
const Building* sink = findBuilding(state_bs, sinkId);
|
||||||
REQUIRE(miner != nullptr);
|
REQUIRE(miner != nullptr);
|
||||||
REQUIRE(sink != nullptr);
|
REQUIRE(sink != nullptr);
|
||||||
// Nothing was delivered, and the producer's output side has backed up to its cap.
|
// Nothing was delivered, and the producer's output side has backed up to its cap.
|
||||||
@@ -1063,14 +1064,14 @@ TEST_CASE("BuildingSystem: setRecipe clears output buffer and active production"
|
|||||||
tick);
|
tick);
|
||||||
|
|
||||||
{
|
{
|
||||||
const Building* b = bs.findBuilding(id);
|
const Building* b = findBuilding(state_bs, id);
|
||||||
REQUIRE(b != nullptr);
|
REQUIRE(b != nullptr);
|
||||||
REQUIRE(b->getOutputItemCount() > 0);
|
REQUIRE(b->getOutputItemCount() > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bs.setRecipe(id, "mine_copper_ore");
|
bs.setRecipe(id, "mine_copper_ore");
|
||||||
|
|
||||||
const Building* b = bs.findBuilding(id);
|
const Building* b = findBuilding(state_bs, id);
|
||||||
// Clearing the output buffer on a recipe change also discards emerging items
|
// Clearing the output buffer on a recipe change also discards emerging items
|
||||||
// (REQ-MAT-OUTPUT-EMERGE).
|
// (REQ-MAT-OUTPUT-EMERGE).
|
||||||
REQUIRE(b->getOutputItemCount() == 0);
|
REQUIRE(b->getOutputItemCount() == 0);
|
||||||
@@ -1106,7 +1107,7 @@ TEST_CASE("BuildingSystem: reprocessing plant output buffer capacity equals max
|
|||||||
Tick tick = 0;
|
Tick tick = 0;
|
||||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(25.0)) + 1, tick);
|
runTicks(bs, belts, static_cast<int>(secondsToTicks(25.0)) + 1, tick);
|
||||||
|
|
||||||
const Building* b = bs.findBuilding(id);
|
const Building* b = findBuilding(state_bs, id);
|
||||||
REQUIRE(b != nullptr);
|
REQUIRE(b != nullptr);
|
||||||
// reprocessing_cycle outputs: 2 iron_ingot (60%), 1 circuit_board (30%),
|
// reprocessing_cycle outputs: 2 iron_ingot (60%), 1 circuit_board (30%),
|
||||||
// 1 advanced_alloy (10%). Max per roll = 2. Capacity = 2 (1× max).
|
// 1 advanced_alloy (10%). Max per roll = 2. Capacity = 2 (1× max).
|
||||||
@@ -1153,7 +1154,7 @@ TEST_CASE("BuildingSystem: reprocessing plant produces one cycle output then sta
|
|||||||
// Verify all five scrap were accepted; some may still be travelling inward on
|
// Verify all five scrap were accepted; some may still be travelling inward on
|
||||||
// the input belt (REQ-MAT-INPUT-INTAKE), so count buffered + in-transit.
|
// the input belt (REQ-MAT-INPUT-INTAKE), so count buffered + in-transit.
|
||||||
{
|
{
|
||||||
const Building* b = bs.findBuilding(id);
|
const Building* b = findBuilding(state_bs, id);
|
||||||
REQUIRE(b != nullptr);
|
REQUIRE(b != nullptr);
|
||||||
REQUIRE(b->pendingInputCount(ItemType{"scrap"}) == 5);
|
REQUIRE(b->pendingInputCount(ItemType{"scrap"}) == 5);
|
||||||
}
|
}
|
||||||
@@ -1161,7 +1162,7 @@ TEST_CASE("BuildingSystem: reprocessing plant produces one cycle output then sta
|
|||||||
// Run production cycle (3s = 90 ticks + 1 for the completion tick).
|
// Run production cycle (3s = 90 ticks + 1 for the completion tick).
|
||||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(3.0)) + 1, tick);
|
runTicks(bs, belts, static_cast<int>(secondsToTicks(3.0)) + 1, tick);
|
||||||
|
|
||||||
const Building* b = bs.findBuilding(id);
|
const Building* b = findBuilding(state_bs, id);
|
||||||
REQUIRE(b != nullptr);
|
REQUIRE(b != nullptr);
|
||||||
// Cycle should have completed and output deposited.
|
// Cycle should have completed and output deposited.
|
||||||
REQUIRE_FALSE(b->outputBuffer.items.empty());
|
REQUIRE_FALSE(b->outputBuffer.items.empty());
|
||||||
@@ -1237,7 +1238,7 @@ TEST_CASE("BuildingSystem: findRotateInPlaceTarget returns the building id for a
|
|||||||
|
|
||||||
Tick tick = 0;
|
Tick tick = 0;
|
||||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(1.0)) + 1, tick);
|
runTicks(bs, belts, static_cast<int>(secondsToTicks(1.0)) + 1, tick);
|
||||||
REQUIRE(bs.getAllSites().empty());
|
REQUIRE(getAllSites(state_bs).empty());
|
||||||
|
|
||||||
const std::optional<BuildingId> result =
|
const std::optional<BuildingId> result =
|
||||||
bs.findRotateInPlaceTarget(BuildingType::Belt, QPoint(0, 0), Rotation::South);
|
bs.findRotateInPlaceTarget(BuildingType::Belt, QPoint(0, 0), Rotation::South);
|
||||||
@@ -1367,11 +1368,11 @@ TEST_CASE("BuildingSystem: rotateInPlace updates the rotation field of a constru
|
|||||||
rng);
|
rng);
|
||||||
|
|
||||||
const BuildingId id = bs.place(BuildingType::Belt, QPoint(0, 0), Rotation::East, 0).value();
|
const BuildingId id = bs.place(BuildingType::Belt, QPoint(0, 0), Rotation::East, 0).value();
|
||||||
REQUIRE(bs.findSite(id)->rotation == Rotation::East);
|
REQUIRE(findSite(state_bs, id)->rotation == Rotation::East);
|
||||||
|
|
||||||
bs.rotateInPlace(id, Rotation::North);
|
bs.rotateInPlace(id, Rotation::North);
|
||||||
|
|
||||||
REQUIRE(bs.findSite(id)->rotation == Rotation::North);
|
REQUIRE(findSite(state_bs, id)->rotation == Rotation::North);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("BuildingSystem: rotateInPlace preserves the construction progress of a queued site",
|
TEST_CASE("BuildingSystem: rotateInPlace preserves the construction progress of a queued site",
|
||||||
@@ -1391,12 +1392,12 @@ TEST_CASE("BuildingSystem: rotateInPlace preserves the construction progress of
|
|||||||
rng);
|
rng);
|
||||||
|
|
||||||
const BuildingId id = bs.place(BuildingType::Belt, QPoint(0, 0), Rotation::East, 0).value();
|
const BuildingId id = bs.place(BuildingType::Belt, QPoint(0, 0), Rotation::East, 0).value();
|
||||||
const Tick completesAt = bs.findSite(id)->completesAt;
|
const Tick completesAt = findSite(state_bs, id)->completesAt;
|
||||||
REQUIRE(completesAt > 0);
|
REQUIRE(completesAt > 0);
|
||||||
|
|
||||||
bs.rotateInPlace(id, Rotation::South);
|
bs.rotateInPlace(id, Rotation::South);
|
||||||
|
|
||||||
REQUIRE(bs.findSite(id)->completesAt == completesAt);
|
REQUIRE(findSite(state_bs, id)->completesAt == completesAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("BuildingSystem: rotateInPlace updates rotation and output port direction on an operational building",
|
TEST_CASE("BuildingSystem: rotateInPlace updates rotation and output port direction on an operational building",
|
||||||
@@ -1419,14 +1420,14 @@ TEST_CASE("BuildingSystem: rotateInPlace updates rotation and output port direct
|
|||||||
|
|
||||||
Tick tick = 0;
|
Tick tick = 0;
|
||||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(1.0)) + 1, tick);
|
runTicks(bs, belts, static_cast<int>(secondsToTicks(1.0)) + 1, tick);
|
||||||
REQUIRE(bs.findBuilding(id) != nullptr);
|
REQUIRE(findBuilding(state_bs, id) != nullptr);
|
||||||
|
|
||||||
const Building& before = *bs.findBuilding(id);
|
const Building& before = *findBuilding(state_bs, id);
|
||||||
REQUIRE(before.outputPorts[0].direction == Rotation::East);
|
REQUIRE(before.outputPorts[0].direction == Rotation::East);
|
||||||
|
|
||||||
bs.rotateInPlace(id, Rotation::North);
|
bs.rotateInPlace(id, Rotation::North);
|
||||||
|
|
||||||
const Building& after = *bs.findBuilding(id);
|
const Building& after = *findBuilding(state_bs, id);
|
||||||
REQUIRE(after.rotation == Rotation::North);
|
REQUIRE(after.rotation == Rotation::North);
|
||||||
REQUIRE(after.outputPorts[0].direction == Rotation::North);
|
REQUIRE(after.outputPorts[0].direction == Rotation::North);
|
||||||
}
|
}
|
||||||
@@ -1468,11 +1469,11 @@ TEST_CASE("BuildingSystem: rotateInPlace preserves the output filters of a split
|
|||||||
|
|
||||||
// Run until construction completes, so the splitter is registered with BeltSystem.
|
// Run until construction completes, so the splitter is registered with BeltSystem.
|
||||||
Tick tick = 0;
|
Tick tick = 0;
|
||||||
while (f.bs.getAllBuildings().empty() && tick < 100000)
|
while (getAllBuildings(f.state).empty() && tick < 100000)
|
||||||
{
|
{
|
||||||
runTicks(f.bs, f.belts, 1, tick);
|
runTicks(f.bs, f.belts, 1, tick);
|
||||||
}
|
}
|
||||||
REQUIRE(f.bs.getAllBuildings().size() == 1);
|
REQUIRE(getAllBuildings(f.state).size() == 1);
|
||||||
|
|
||||||
const std::vector<ItemType> filterA{ ItemType{"iron_ore"} };
|
const std::vector<ItemType> filterA{ ItemType{"iron_ore"} };
|
||||||
const std::vector<ItemType> filterB{ ItemType{"copper_ore"} };
|
const std::vector<ItemType> filterB{ ItemType{"copper_ore"} };
|
||||||
@@ -1496,7 +1497,7 @@ TEST_CASE("BuildingSystem: splitter filters configured on a construction site ca
|
|||||||
const QPoint tile(5, 5);
|
const QPoint tile(5, 5);
|
||||||
const BuildingId id = f.bs.place(BuildingType::Splitter, tile, Rotation::East, 0).value();
|
const BuildingId id = f.bs.place(BuildingType::Splitter, tile, Rotation::East, 0).value();
|
||||||
REQUIRE(id != kInvalidBuildingId);
|
REQUIRE(id != kInvalidBuildingId);
|
||||||
REQUIRE(f.bs.findSite(id) != nullptr);
|
REQUIRE(findSite(f.state, id) != nullptr);
|
||||||
|
|
||||||
// Configure an output filter on the still-queued splitter site.
|
// Configure an output filter on the still-queued splitter site.
|
||||||
const std::vector<ItemType> filterA{ ItemType{"iron_ore"} };
|
const std::vector<ItemType> filterA{ ItemType{"iron_ore"} };
|
||||||
@@ -1513,12 +1514,12 @@ TEST_CASE("BuildingSystem: splitter filters configured on a construction site ca
|
|||||||
|
|
||||||
// Run until construction completes.
|
// Run until construction completes.
|
||||||
Tick tick = 0;
|
Tick tick = 0;
|
||||||
while (f.bs.getAllBuildings().empty() && tick < 100000)
|
while (getAllBuildings(f.state).empty() && tick < 100000)
|
||||||
{
|
{
|
||||||
runTicks(f.bs, f.belts, 1, tick);
|
runTicks(f.bs, f.belts, 1, tick);
|
||||||
}
|
}
|
||||||
REQUIRE(f.bs.getAllBuildings().size() == 1);
|
REQUIRE(getAllBuildings(f.state).size() == 1);
|
||||||
REQUIRE(f.bs.getAllBuildings()[0].type == BuildingType::Splitter);
|
REQUIRE(getAllBuildings(f.state)[0].type == BuildingType::Splitter);
|
||||||
|
|
||||||
// The built splitter is registered with BeltSystem carrying the filters.
|
// The built splitter is registered with BeltSystem carrying the filters.
|
||||||
const std::optional<BeltSystem::SplitterInfo> builtInfo = f.belts.getSplitterInfo(tile);
|
const std::optional<BeltSystem::SplitterInfo> builtInfo = f.belts.getSplitterInfo(tile);
|
||||||
@@ -1669,10 +1670,10 @@ namespace
|
|||||||
|
|
||||||
// Advances the sim until the given site becomes an operational building, or a
|
// Advances the sim until the given site becomes an operational building, or a
|
||||||
// safety cap is reached.
|
// safety cap is reached.
|
||||||
void buildToCompletion(BuildingSystem& bs, BeltSystem& belts, BuildingId id,
|
void buildToCompletion(BuildingSystem& bs, const FactoryState& state,
|
||||||
Tick& tick)
|
BeltSystem& belts, BuildingId id, Tick& tick)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 20000 && bs.findBuilding(id) == nullptr; ++i)
|
for (int i = 0; i < 20000 && findBuilding(state, id) == nullptr; ++i)
|
||||||
{
|
{
|
||||||
runTicks(bs, belts, 1, tick);
|
runTicks(bs, belts, 1, tick);
|
||||||
}
|
}
|
||||||
@@ -1709,8 +1710,8 @@ TEST_CASE("BuildingSystem: getInputPorts matches between a site and the built bu
|
|||||||
const BuildingId id = f.bs.place(BuildingType::Miner, QPoint(0, 0), Rotation::East, 0).value();
|
const BuildingId id = f.bs.place(BuildingType::Miner, QPoint(0, 0), Rotation::East, 0).value();
|
||||||
|
|
||||||
const std::vector<Port> sitePorts = f.bs.getInputPorts(id);
|
const std::vector<Port> sitePorts = f.bs.getInputPorts(id);
|
||||||
buildToCompletion(f.bs, f.belts, id, tick);
|
buildToCompletion(f.bs, f.state, f.belts, id, tick);
|
||||||
REQUIRE(f.bs.findBuilding(id) != nullptr);
|
REQUIRE(findBuilding(f.state, id) != nullptr);
|
||||||
const std::vector<Port> builtPorts = f.bs.getInputPorts(id);
|
const std::vector<Port> builtPorts = f.bs.getInputPorts(id);
|
||||||
|
|
||||||
// The operational path (stored inputPorts) agrees with the site path (mask-derived).
|
// The operational path (stored inputPorts) agrees with the site path (mask-derived).
|
||||||
@@ -1725,7 +1726,7 @@ TEST_CASE("BuildingSystem: getInputPorts invariants hold for a rotated site", "[
|
|||||||
{
|
{
|
||||||
PlacementFixture f;
|
PlacementFixture f;
|
||||||
const BuildingId id = f.bs.place(BuildingType::Miner, QPoint(0, 0), Rotation::South, 0).value();
|
const BuildingId id = f.bs.place(BuildingType::Miner, QPoint(0, 0), Rotation::South, 0).value();
|
||||||
const ConstructionSite* site = f.bs.findSite(id);
|
const ConstructionSite* site = findSite(f.state, id);
|
||||||
REQUIRE(site != nullptr);
|
REQUIRE(site != nullptr);
|
||||||
|
|
||||||
std::set<std::pair<int, int>> bodySet;
|
std::set<std::pair<int, int>> bodySet;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
|
#include "FactoryQueries.h"
|
||||||
|
|
||||||
#include "Building.h"
|
#include "Building.h"
|
||||||
#include "BuildingSystem.h"
|
#include "BuildingSystem.h"
|
||||||
@@ -218,7 +219,7 @@ TEST_CASE("Shipyard: setShipLayout reinitializes buffers with module materials",
|
|||||||
|
|
||||||
SimulationTestAccess::buildings(sim).setShipLayout(yardId, layout);
|
SimulationTestAccess::buildings(sim).setShipLayout(yardId, layout);
|
||||||
|
|
||||||
const Building* b = sim.getBuildings().findBuilding(yardId);
|
const Building* b = findBuilding(sim.getFactoryState(), yardId);
|
||||||
REQUIRE(b != nullptr);
|
REQUIRE(b != nullptr);
|
||||||
// armor_plate needs 2 iron_ingot; interceptor needs 3 iron_ingot + 1 circuit_board
|
// armor_plate needs 2 iron_ingot; interceptor needs 3 iron_ingot + 1 circuit_board
|
||||||
// Total iron_ingot = 5, buffer cap = 2 * 5 = 10
|
// Total iron_ingot = 5, buffer cap = 2 * 5 = 10
|
||||||
@@ -243,7 +244,7 @@ TEST_CASE("Shipyard: setShipLayout cancels in-progress production",
|
|||||||
fillMaterials(sim, yardId, *def, emptyLayout);
|
fillMaterials(sim, yardId, *def, emptyLayout);
|
||||||
sim.tick();
|
sim.tick();
|
||||||
|
|
||||||
const Building* b1 = sim.getBuildings().findBuilding(yardId);
|
const Building* b1 = findBuilding(sim.getFactoryState(), yardId);
|
||||||
REQUIRE(b1 != nullptr);
|
REQUIRE(b1 != nullptr);
|
||||||
REQUIRE(b1->production.has_value());
|
REQUIRE(b1->production.has_value());
|
||||||
|
|
||||||
@@ -257,7 +258,7 @@ TEST_CASE("Shipyard: setShipLayout cancels in-progress production",
|
|||||||
|
|
||||||
SimulationTestAccess::buildings(sim).setShipLayout(yardId, layout);
|
SimulationTestAccess::buildings(sim).setShipLayout(yardId, layout);
|
||||||
|
|
||||||
const Building* b2 = sim.getBuildings().findBuilding(yardId);
|
const Building* b2 = findBuilding(sim.getFactoryState(), yardId);
|
||||||
REQUIRE(b2 != nullptr);
|
REQUIRE(b2 != nullptr);
|
||||||
CHECK_FALSE(b2->production.has_value());
|
CHECK_FALSE(b2->production.has_value());
|
||||||
}
|
}
|
||||||
@@ -323,13 +324,13 @@ TEST_CASE("Shipyard: setRecipe clears ship layout", "[modules][shipyard]")
|
|||||||
layout.placedModules.push_back(pm);
|
layout.placedModules.push_back(pm);
|
||||||
SimulationTestAccess::buildings(sim).setShipLayout(yardId, layout);
|
SimulationTestAccess::buildings(sim).setShipLayout(yardId, layout);
|
||||||
|
|
||||||
const Building* b1 = sim.getBuildings().findBuilding(yardId);
|
const Building* b1 = findBuilding(sim.getFactoryState(), yardId);
|
||||||
REQUIRE(b1 != nullptr);
|
REQUIRE(b1 != nullptr);
|
||||||
REQUIRE(b1->shipLayout.has_value());
|
REQUIRE(b1->shipLayout.has_value());
|
||||||
|
|
||||||
SimulationTestAccess::buildings(sim).setRecipe(yardId,"destroyer");
|
SimulationTestAccess::buildings(sim).setRecipe(yardId,"destroyer");
|
||||||
|
|
||||||
const Building* b2 = sim.getBuildings().findBuilding(yardId);
|
const Building* b2 = findBuilding(sim.getFactoryState(), yardId);
|
||||||
REQUIRE(b2 != nullptr);
|
REQUIRE(b2 != nullptr);
|
||||||
CHECK_FALSE(b2->shipLayout.has_value());
|
CHECK_FALSE(b2->shipLayout.has_value());
|
||||||
}
|
}
|
||||||
@@ -352,14 +353,14 @@ TEST_CASE("Shipyard: setRecipe with unchanged recipe keeps ship layout",
|
|||||||
layout.placedModules.push_back(pm);
|
layout.placedModules.push_back(pm);
|
||||||
SimulationTestAccess::buildings(sim).setShipLayout(yardId, layout);
|
SimulationTestAccess::buildings(sim).setShipLayout(yardId, layout);
|
||||||
|
|
||||||
const Building* b1 = sim.getBuildings().findBuilding(yardId);
|
const Building* b1 = findBuilding(sim.getFactoryState(), yardId);
|
||||||
REQUIRE(b1 != nullptr);
|
REQUIRE(b1 != nullptr);
|
||||||
REQUIRE(b1->shipLayout.has_value());
|
REQUIRE(b1->shipLayout.has_value());
|
||||||
|
|
||||||
// Re-selecting the same recipe must be a no-op and preserve the layout.
|
// Re-selecting the same recipe must be a no-op and preserve the layout.
|
||||||
SimulationTestAccess::buildings(sim).setRecipe(yardId,"interceptor");
|
SimulationTestAccess::buildings(sim).setRecipe(yardId,"interceptor");
|
||||||
|
|
||||||
const Building* b2 = sim.getBuildings().findBuilding(yardId);
|
const Building* b2 = findBuilding(sim.getFactoryState(), yardId);
|
||||||
REQUIRE(b2 != nullptr);
|
REQUIRE(b2 != nullptr);
|
||||||
REQUIRE(b2->shipLayout.has_value());
|
REQUIRE(b2->shipLayout.has_value());
|
||||||
REQUIRE(b2->shipLayout->placedModules.size() == 1);
|
REQUIRE(b2->shipLayout->placedModules.size() == 1);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
|
#include "FactoryQueries.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
@@ -214,7 +215,7 @@ TEST_CASE("Shipyard: spawns a second ship after materials replenished", "[shipya
|
|||||||
// Verify the shipyard production field cleared (i.e. the cycle completed
|
// Verify the shipyard production field cleared (i.e. the cycle completed
|
||||||
// and is not still running).
|
// and is not still running).
|
||||||
bool productionCleared = false;
|
bool productionCleared = false;
|
||||||
for (const Building& b : sim.getBuildings().getAllBuildings())
|
for (const Building& b : getAllBuildings(sim.getFactoryState()))
|
||||||
{
|
{
|
||||||
if (b.id == yardId)
|
if (b.id == yardId)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
|
#include "FactoryQueries.h"
|
||||||
|
|
||||||
#include <random>
|
#include <random>
|
||||||
|
|
||||||
@@ -100,7 +101,7 @@ TEST_CASE("WaveSystem: Simulation pre-places HQ + 2 player + 2 enemy stations",
|
|||||||
|
|
||||||
// HQ is still a Building (for belt integration).
|
// HQ is still a Building (for belt integration).
|
||||||
int hqCount = 0;
|
int hqCount = 0;
|
||||||
for (const Building& b : sim.getBuildings().getAllBuildings())
|
for (const Building& b : getAllBuildings(sim.getFactoryState()))
|
||||||
{
|
{
|
||||||
if (b.type == BuildingType::Hq) { ++hqCount; }
|
if (b.type == BuildingType::Hq) { ++hqCount; }
|
||||||
}
|
}
|
||||||
@@ -143,7 +144,7 @@ TEST_CASE("WaveSystem: HQ anchor is at asteroid right edge", "[wave]")
|
|||||||
{
|
{
|
||||||
const Simulation sim(loadTestConfig(), 42);
|
const Simulation sim(loadTestConfig(), 42);
|
||||||
|
|
||||||
for (const Building& b : sim.getBuildings().getAllBuildings())
|
for (const Building& b : getAllBuildings(sim.getFactoryState()))
|
||||||
{
|
{
|
||||||
if (b.type != BuildingType::Hq) { continue; }
|
if (b.type != BuildingType::Hq) { continue; }
|
||||||
// Rightmost body cell must be at x = -1 (asteroid right edge).
|
// Rightmost body cell must be at x = -1 (asteroid right edge).
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "GameWorldView.h"
|
#include "GameWorldView.h"
|
||||||
|
#include "FactoryQueries.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
@@ -577,7 +578,7 @@ QRect GameWorldView::getViewportRect() const
|
|||||||
float GameWorldView::getAsteroidLeftEdge() const
|
float GameWorldView::getAsteroidLeftEdge() const
|
||||||
{
|
{
|
||||||
float leftX = -static_cast<float>(m_sim->getCurrentAsteroidWidth_tiles());
|
float leftX = -static_cast<float>(m_sim->getCurrentAsteroidWidth_tiles());
|
||||||
for (const Building& b : m_sim->getBuildings().getAllBuildings())
|
for (const Building& b : getAllBuildings(m_sim->getFactoryState()))
|
||||||
{
|
{
|
||||||
for (const QPoint& cell : b.bodyCells)
|
for (const QPoint& cell : b.bodyCells)
|
||||||
{
|
{
|
||||||
@@ -672,7 +673,7 @@ bool GameWorldView::isValidPlacement(BuildingType type, QPoint anchor,
|
|||||||
bool anyOccupied = false;
|
bool anyOccupied = false;
|
||||||
for (const QPoint& relCell : parsed.bodyCells)
|
for (const QPoint& relCell : parsed.bodyCells)
|
||||||
{
|
{
|
||||||
if (m_sim->getBuildings().isTileOccupied(anchor + relCell))
|
if (isTileOccupied(m_sim->getFactoryState(), anchor + relCell))
|
||||||
{
|
{
|
||||||
anyOccupied = true;
|
anyOccupied = true;
|
||||||
break;
|
break;
|
||||||
@@ -688,7 +689,7 @@ bool GameWorldView::isValidPlacement(BuildingType type, QPoint anchor,
|
|||||||
|
|
||||||
std::optional<BuildingId> GameWorldView::buildingAtTile(QPoint tile) const
|
std::optional<BuildingId> GameWorldView::buildingAtTile(QPoint tile) const
|
||||||
{
|
{
|
||||||
for (const Building& b : m_sim->getBuildings().getAllBuildings())
|
for (const Building& b : getAllBuildings(m_sim->getFactoryState()))
|
||||||
{
|
{
|
||||||
for (const QPoint& cell : b.bodyCells)
|
for (const QPoint& cell : b.bodyCells)
|
||||||
{
|
{
|
||||||
@@ -703,7 +704,7 @@ std::optional<BuildingId> GameWorldView::buildingAtTile(QPoint tile) const
|
|||||||
|
|
||||||
std::optional<BuildingId> GameWorldView::siteAtTile(QPoint tile) const
|
std::optional<BuildingId> GameWorldView::siteAtTile(QPoint tile) const
|
||||||
{
|
{
|
||||||
for (const ConstructionSite& s : m_sim->getBuildings().getAllSites())
|
for (const ConstructionSite& s : getAllSites(m_sim->getFactoryState()))
|
||||||
{
|
{
|
||||||
for (const QPoint& cell : s.bodyCells)
|
for (const QPoint& cell : s.bodyCells)
|
||||||
{
|
{
|
||||||
@@ -725,7 +726,7 @@ std::vector<BuildingId> GameWorldView::buildingsInBox(QPoint cornerA, QPoint cor
|
|||||||
const int y1 = std::max(cornerA.y(), cornerB.y());
|
const int y1 = std::max(cornerA.y(), cornerB.y());
|
||||||
|
|
||||||
std::vector<BuildingId> ids;
|
std::vector<BuildingId> ids;
|
||||||
for (const Building& b : m_sim->getBuildings().getAllBuildings())
|
for (const Building& b : getAllBuildings(m_sim->getFactoryState()))
|
||||||
{
|
{
|
||||||
for (const QPoint& cell : b.bodyCells)
|
for (const QPoint& cell : b.bodyCells)
|
||||||
{
|
{
|
||||||
@@ -737,7 +738,7 @@ std::vector<BuildingId> GameWorldView::buildingsInBox(QPoint cornerA, QPoint cor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const ConstructionSite& s : m_sim->getBuildings().getAllSites())
|
for (const ConstructionSite& s : getAllSites(m_sim->getFactoryState()))
|
||||||
{
|
{
|
||||||
for (const QPoint& cell : s.bodyCells)
|
for (const QPoint& cell : s.bodyCells)
|
||||||
{
|
{
|
||||||
@@ -951,14 +952,14 @@ TunnelTileMap GameWorldView::collectTunnelTiles() const
|
|||||||
// single-cell tile, so a just-placed tunnel (not yet constructed) is matchable
|
// single-cell tile, so a just-placed tunnel (not yet constructed) is matchable
|
||||||
// (REQ-BLD-TUNNEL-MODE, REQ-BLD-TUNNEL-SELECT-HIGHLIGHT).
|
// (REQ-BLD-TUNNEL-MODE, REQ-BLD-TUNNEL-SELECT-HIGHLIGHT).
|
||||||
TunnelTileMap tunnels;
|
TunnelTileMap tunnels;
|
||||||
for (const Building& b : m_sim->getBuildings().getAllBuildings())
|
for (const Building& b : getAllBuildings(m_sim->getFactoryState()))
|
||||||
{
|
{
|
||||||
if (b.type == BuildingType::TunnelEntry || b.type == BuildingType::TunnelExit)
|
if (b.type == BuildingType::TunnelEntry || b.type == BuildingType::TunnelExit)
|
||||||
{
|
{
|
||||||
tunnels[b.anchor] = TunnelTileInfo{b.type, b.rotation};
|
tunnels[b.anchor] = TunnelTileInfo{b.type, b.rotation};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const ConstructionSite& s : m_sim->getBuildings().getAllSites())
|
for (const ConstructionSite& s : getAllSites(m_sim->getFactoryState()))
|
||||||
{
|
{
|
||||||
if (s.type == BuildingType::TunnelEntry || s.type == BuildingType::TunnelExit)
|
if (s.type == BuildingType::TunnelEntry || s.type == BuildingType::TunnelExit)
|
||||||
{
|
{
|
||||||
@@ -1034,7 +1035,7 @@ void GameWorldView::placeAtTile(QPoint tile)
|
|||||||
|| type == BuildingType::TunnelEntry
|
|| type == BuildingType::TunnelEntry
|
||||||
|| type == BuildingType::TunnelExit)
|
|| type == BuildingType::TunnelExit)
|
||||||
{
|
{
|
||||||
if (!m_sim->getBuildings().isTileOccupied(tile) && canAfford(type))
|
if (!isTileOccupied(m_sim->getFactoryState(), tile) && canAfford(type))
|
||||||
{
|
{
|
||||||
enqueuePlaceBuilding(type, tile, m_ghostRotation);
|
enqueuePlaceBuilding(type, tile, m_ghostRotation);
|
||||||
}
|
}
|
||||||
@@ -1061,7 +1062,7 @@ void GameWorldView::recomputeBeltDragPath(QPoint cursorTile)
|
|||||||
std::optional<BuildingType> targetType;
|
std::optional<BuildingType> targetType;
|
||||||
if (targetId.has_value())
|
if (targetId.has_value())
|
||||||
{
|
{
|
||||||
if (const Building* building = m_sim->getBuildings().findBuilding(*targetId))
|
if (const Building* building = findBuilding(m_sim->getFactoryState(), *targetId))
|
||||||
{
|
{
|
||||||
targetType = building->type;
|
targetType = building->type;
|
||||||
}
|
}
|
||||||
@@ -1069,7 +1070,7 @@ void GameWorldView::recomputeBeltDragPath(QPoint cursorTile)
|
|||||||
else if (std::optional<BuildingId> siteId = siteAtTile(cursorTile); siteId.has_value())
|
else if (std::optional<BuildingId> siteId = siteAtTile(cursorTile); siteId.has_value())
|
||||||
{
|
{
|
||||||
targetId = siteId;
|
targetId = siteId;
|
||||||
if (const ConstructionSite* site = m_sim->getBuildings().findSite(*siteId))
|
if (const ConstructionSite* site = findSite(m_sim->getFactoryState(), *siteId))
|
||||||
{
|
{
|
||||||
targetType = site->type;
|
targetType = site->type;
|
||||||
}
|
}
|
||||||
@@ -1298,7 +1299,7 @@ bool GameWorldView::drawBuildingIcon(QPainter& painter, BuildingType type,
|
|||||||
|
|
||||||
void GameWorldView::drawBuildings(QPainter& painter)
|
void GameWorldView::drawBuildings(QPainter& painter)
|
||||||
{
|
{
|
||||||
for (const Building& b : m_sim->getBuildings().getAllBuildings())
|
for (const Building& b : getAllBuildings(m_sim->getFactoryState()))
|
||||||
{
|
{
|
||||||
const std::map<BuildingType, BuildingVisuals>::const_iterator it =
|
const std::map<BuildingType, BuildingVisuals>::const_iterator it =
|
||||||
m_visuals->buildings.find(b.type);
|
m_visuals->buildings.find(b.type);
|
||||||
@@ -1365,7 +1366,7 @@ void GameWorldView::drawBuildings(QPainter& painter)
|
|||||||
}
|
}
|
||||||
|
|
||||||
painter.setOpacity(0.5);
|
painter.setOpacity(0.5);
|
||||||
for (const ConstructionSite& s : m_sim->getBuildings().getAllSites())
|
for (const ConstructionSite& s : getAllSites(m_sim->getFactoryState()))
|
||||||
{
|
{
|
||||||
const std::map<BuildingType, BuildingVisuals>::const_iterator it =
|
const std::map<BuildingType, BuildingVisuals>::const_iterator it =
|
||||||
m_visuals->buildings.find(s.type);
|
m_visuals->buildings.find(s.type);
|
||||||
@@ -1442,7 +1443,7 @@ void GameWorldView::drawBuildings(QPainter& painter)
|
|||||||
// after every building and construction site fill so a belt (or other tile)
|
// after every building and construction site fill so a belt (or other tile)
|
||||||
// placed directly below the HQ cannot overpaint the bar (REQ-UI-STATUS-LIGHT
|
// placed directly below the HQ cannot overpaint the bar (REQ-UI-STATUS-LIGHT
|
||||||
// neighbours case, same rationale as the selection highlights below).
|
// neighbours case, same rationale as the selection highlights below).
|
||||||
for (const Building& b : m_sim->getBuildings().getAllBuildings())
|
for (const Building& b : getAllBuildings(m_sim->getFactoryState()))
|
||||||
{
|
{
|
||||||
if (b.type != BuildingType::Hq) { continue; }
|
if (b.type != BuildingType::Hq) { continue; }
|
||||||
const QPointF tl = tileToWidget(b.anchor);
|
const QPointF tl = tileToWidget(b.anchor);
|
||||||
@@ -1473,12 +1474,12 @@ std::optional<QRectF> GameWorldView::footprintWidgetRect(BuildingId id) const
|
|||||||
std::optional<QPoint> anchor;
|
std::optional<QPoint> anchor;
|
||||||
std::optional<QSize> footprint;
|
std::optional<QSize> footprint;
|
||||||
|
|
||||||
if (const Building* b = m_sim->getBuildings().findBuilding(id))
|
if (const Building* b = findBuilding(m_sim->getFactoryState(), id))
|
||||||
{
|
{
|
||||||
anchor = b->anchor;
|
anchor = b->anchor;
|
||||||
footprint = b->footprint;
|
footprint = b->footprint;
|
||||||
}
|
}
|
||||||
else if (const ConstructionSite* s = m_sim->getBuildings().findSite(id))
|
else if (const ConstructionSite* s = findSite(m_sim->getFactoryState(), id))
|
||||||
{
|
{
|
||||||
anchor = s->anchor;
|
anchor = s->anchor;
|
||||||
footprint = s->footprint;
|
footprint = s->footprint;
|
||||||
@@ -1530,13 +1531,13 @@ void GameWorldView::drawCopyConfigFeedback(QPainter& painter)
|
|||||||
painter.setPen(Qt::NoPen);
|
painter.setPen(Qt::NoPen);
|
||||||
painter.setBrush(color);
|
painter.setBrush(color);
|
||||||
const BuildingType type = m_copiedConfig->type;
|
const BuildingType type = m_copiedConfig->type;
|
||||||
for (const Building& b : m_sim->getBuildings().getAllBuildings())
|
for (const Building& b : getAllBuildings(m_sim->getFactoryState()))
|
||||||
{
|
{
|
||||||
if (b.type != type) { continue; }
|
if (b.type != type) { continue; }
|
||||||
const std::optional<QRectF> rect = footprintWidgetRect(b.id);
|
const std::optional<QRectF> rect = footprintWidgetRect(b.id);
|
||||||
if (rect.has_value()) { painter.drawRect(*rect); }
|
if (rect.has_value()) { painter.drawRect(*rect); }
|
||||||
}
|
}
|
||||||
for (const ConstructionSite& s : m_sim->getBuildings().getAllSites())
|
for (const ConstructionSite& s : getAllSites(m_sim->getFactoryState()))
|
||||||
{
|
{
|
||||||
if (s.type != type) { continue; }
|
if (s.type != type) { continue; }
|
||||||
const std::optional<QRectF> rect = footprintWidgetRect(s.id);
|
const std::optional<QRectF> rect = footprintWidgetRect(s.id);
|
||||||
@@ -1571,7 +1572,7 @@ void GameWorldView::drawPortItems(QPainter& painter)
|
|||||||
const double margin = kPortMarginTiles * static_cast<double>(getTilePx());
|
const double margin = kPortMarginTiles * static_cast<double>(getTilePx());
|
||||||
|
|
||||||
QRegion clip(rect());
|
QRegion clip(rect());
|
||||||
for (const Building& b : m_sim->getBuildings().getAllBuildings())
|
for (const Building& b : getAllBuildings(m_sim->getFactoryState()))
|
||||||
{
|
{
|
||||||
if (b.type == BuildingType::Belt || b.type == BuildingType::Splitter
|
if (b.type == BuildingType::Belt || b.type == BuildingType::Splitter
|
||||||
|| b.type == BuildingType::TunnelEntry || b.type == BuildingType::TunnelExit)
|
|| b.type == BuildingType::TunnelEntry || b.type == BuildingType::TunnelExit)
|
||||||
@@ -1982,11 +1983,11 @@ void GameWorldView::drawSelectedTunnelConnections(QPainter& painter)
|
|||||||
std::optional<QPoint> anchor;
|
std::optional<QPoint> anchor;
|
||||||
std::optional<BuildingType> type;
|
std::optional<BuildingType> type;
|
||||||
Rotation rotation = Rotation::East;
|
Rotation rotation = Rotation::East;
|
||||||
if (const Building* b = m_sim->getBuildings().findBuilding(id))
|
if (const Building* b = findBuilding(m_sim->getFactoryState(), id))
|
||||||
{
|
{
|
||||||
anchor = b->anchor; type = b->type; rotation = b->rotation;
|
anchor = b->anchor; type = b->type; rotation = b->rotation;
|
||||||
}
|
}
|
||||||
else if (const ConstructionSite* s = m_sim->getBuildings().findSite(id))
|
else if (const ConstructionSite* s = findSite(m_sim->getFactoryState(), id))
|
||||||
{
|
{
|
||||||
anchor = s->anchor; type = s->type; rotation = s->rotation;
|
anchor = s->anchor; type = s->type; rotation = s->rotation;
|
||||||
}
|
}
|
||||||
@@ -2091,7 +2092,7 @@ void GameWorldView::drawOverlays(QPainter& painter)
|
|||||||
|
|
||||||
// Queued for deconstruction: tint every building currently in the
|
// Queued for deconstruction: tint every building currently in the
|
||||||
// deconstruction queue, regardless of mode (REQ-BLD-DECON-QUEUE).
|
// deconstruction queue, regardless of mode (REQ-BLD-DECON-QUEUE).
|
||||||
for (const Building& b : m_sim->getBuildings().getAllBuildings())
|
for (const Building& b : getAllBuildings(m_sim->getFactoryState()))
|
||||||
{
|
{
|
||||||
if (!b.queuedForDeconstruction) { continue; }
|
if (!b.queuedForDeconstruction) { continue; }
|
||||||
for (const QPoint& cell : b.bodyCells)
|
for (const QPoint& cell : b.bodyCells)
|
||||||
@@ -2106,12 +2107,12 @@ void GameWorldView::drawOverlays(QPainter& painter)
|
|||||||
{
|
{
|
||||||
for (BuildingId id : buildingsInBox(m_boxStartTile, m_boxCurrentTile))
|
for (BuildingId id : buildingsInBox(m_boxStartTile, m_boxCurrentTile))
|
||||||
{
|
{
|
||||||
const Building* b = m_sim->getBuildings().findBuilding(id);
|
const Building* b = findBuilding(m_sim->getFactoryState(), id);
|
||||||
if (b && b->type == BuildingType::Hq) { continue; }
|
if (b && b->type == BuildingType::Hq) { continue; }
|
||||||
const std::vector<QPoint>* cells = nullptr;
|
const std::vector<QPoint>* cells = nullptr;
|
||||||
const ConstructionSite* s = nullptr;
|
const ConstructionSite* s = nullptr;
|
||||||
if (b) { cells = &b->bodyCells; }
|
if (b) { cells = &b->bodyCells; }
|
||||||
else if ((s = m_sim->getBuildings().findSite(id))) { cells = &s->bodyCells; }
|
else if ((s = findSite(m_sim->getFactoryState(), id))) { cells = &s->bodyCells; }
|
||||||
if (cells)
|
if (cells)
|
||||||
{
|
{
|
||||||
for (const QPoint& cell : *cells)
|
for (const QPoint& cell : *cells)
|
||||||
@@ -2123,7 +2124,7 @@ void GameWorldView::drawOverlays(QPainter& painter)
|
|||||||
}
|
}
|
||||||
else if (m_deconstructMode && m_deconstructHoverBuildingId.has_value())
|
else if (m_deconstructMode && m_deconstructHoverBuildingId.has_value())
|
||||||
{
|
{
|
||||||
const Building* b = m_sim->getBuildings().findBuilding(*m_deconstructHoverBuildingId);
|
const Building* b = findBuilding(m_sim->getFactoryState(), *m_deconstructHoverBuildingId);
|
||||||
if (b)
|
if (b)
|
||||||
{
|
{
|
||||||
for (const QPoint& cell : b->bodyCells)
|
for (const QPoint& cell : b->bodyCells)
|
||||||
@@ -2748,7 +2749,7 @@ void GameWorldView::mouseReleaseEvent(QMouseEvent* event)
|
|||||||
|
|
||||||
if (m_deconstructMode)
|
if (m_deconstructMode)
|
||||||
{
|
{
|
||||||
const BuildingSystem& buildings = m_sim->getBuildings();
|
const FactoryState& factory = m_sim->getFactoryState();
|
||||||
|
|
||||||
// Split covered ids into construction sites (removed instantly) and
|
// Split covered ids into construction sites (removed instantly) and
|
||||||
// operational deconstructible buildings (the HQ is protected; player
|
// operational deconstructible buildings (the HQ is protected; player
|
||||||
@@ -2757,12 +2758,12 @@ void GameWorldView::mouseReleaseEvent(QMouseEvent* event)
|
|||||||
std::vector<BuildingId> operational;
|
std::vector<BuildingId> operational;
|
||||||
for (BuildingId id : boxIds)
|
for (BuildingId id : boxIds)
|
||||||
{
|
{
|
||||||
if (const Building* b = buildings.findBuilding(id))
|
if (const Building* b = findBuilding(factory, id))
|
||||||
{
|
{
|
||||||
if (b->type == BuildingType::Hq) { continue; }
|
if (b->type == BuildingType::Hq) { continue; }
|
||||||
operational.push_back(id);
|
operational.push_back(id);
|
||||||
}
|
}
|
||||||
else if (buildings.findSite(id))
|
else if (findSite(factory, id))
|
||||||
{
|
{
|
||||||
sites.push_back(id);
|
sites.push_back(id);
|
||||||
}
|
}
|
||||||
@@ -2784,7 +2785,7 @@ void GameWorldView::mouseReleaseEvent(QMouseEvent* event)
|
|||||||
bool allQueued = !operational.empty();
|
bool allQueued = !operational.empty();
|
||||||
for (BuildingId id : operational)
|
for (BuildingId id : operational)
|
||||||
{
|
{
|
||||||
if (!buildings.isQueuedForDeconstruction(id)) { allQueued = false; break; }
|
if (!isQueuedForDeconstruction(factory, id)) { allQueued = false; break; }
|
||||||
}
|
}
|
||||||
for (BuildingId id : operational)
|
for (BuildingId id : operational)
|
||||||
{
|
{
|
||||||
@@ -2795,7 +2796,7 @@ void GameWorldView::mouseReleaseEvent(QMouseEvent* event)
|
|||||||
command->id = id;
|
command->id = id;
|
||||||
enqueueCommand(command);
|
enqueueCommand(command);
|
||||||
}
|
}
|
||||||
else if (!buildings.isQueuedForDeconstruction(id))
|
else if (!isQueuedForDeconstruction(factory, id))
|
||||||
{
|
{
|
||||||
std::shared_ptr<DeconstructCommand> command =
|
std::shared_ptr<DeconstructCommand> command =
|
||||||
std::make_shared<DeconstructCommand>();
|
std::make_shared<DeconstructCommand>();
|
||||||
@@ -2997,7 +2998,7 @@ void GameWorldView::pasteConfigTo(BuildingId id)
|
|||||||
{
|
{
|
||||||
// Operational splitters are configured by tile; sites by BuildingId
|
// Operational splitters are configured by tile; sites by BuildingId
|
||||||
// (mirrors SelectedBuildingPanel::onSplitterFilterChanged).
|
// (mirrors SelectedBuildingPanel::onSplitterFilterChanged).
|
||||||
if (const Building* building = m_sim->getBuildings().findBuilding(id))
|
if (const Building* building = findBuilding(m_sim->getFactoryState(), id))
|
||||||
{
|
{
|
||||||
std::shared_ptr<SetSplitterFiltersCommand> command =
|
std::shared_ptr<SetSplitterFiltersCommand> command =
|
||||||
std::make_shared<SetSplitterFiltersCommand>();
|
std::make_shared<SetSplitterFiltersCommand>();
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "MainWindow.h"
|
#include "MainWindow.h"
|
||||||
|
#include "FactoryQueries.h"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <random>
|
#include <random>
|
||||||
@@ -277,9 +278,9 @@ void MainWindow::handleEvent(std::shared_ptr<const LayoutDialogRequestedEvent> e
|
|||||||
{
|
{
|
||||||
// A construction site has no Building yet; fall back to its site record so
|
// A construction site has no Building yet; fall back to its site record so
|
||||||
// the shipyard layout can be configured before it is built (REQ-BLD-SITE-CONFIG).
|
// the shipyard layout can be configured before it is built (REQ-BLD-SITE-CONFIG).
|
||||||
const Building* b = m_sim->getBuildings().findBuilding(event->shipyardId);
|
const Building* b = findBuilding(m_sim->getFactoryState(), event->shipyardId);
|
||||||
const ConstructionSite* s =
|
const ConstructionSite* s =
|
||||||
b ? nullptr : m_sim->getBuildings().findSite(event->shipyardId);
|
b ? nullptr : findSite(m_sim->getFactoryState(), event->shipyardId);
|
||||||
if (!b && !s)
|
if (!b && !s)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -304,9 +305,9 @@ void MainWindow::handleEvent(std::shared_ptr<const RecipeSelectionRequestedEvent
|
|||||||
|
|
||||||
// A construction site has no Building yet; fall back to its site record so
|
// A construction site has no Building yet; fall back to its site record so
|
||||||
// the recipe/schematic can be chosen before it is built (REQ-BLD-SITE-CONFIG).
|
// the recipe/schematic can be chosen before it is built (REQ-BLD-SITE-CONFIG).
|
||||||
const Building* b = m_sim->getBuildings().findBuilding(event->buildingId);
|
const Building* b = findBuilding(m_sim->getFactoryState(), event->buildingId);
|
||||||
const ConstructionSite* s =
|
const ConstructionSite* s =
|
||||||
b ? nullptr : m_sim->getBuildings().findSite(event->buildingId);
|
b ? nullptr : findSite(m_sim->getFactoryState(), event->buildingId);
|
||||||
if (!b && !s)
|
if (!b && !s)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "SelectedBuildingPanel.h"
|
#include "SelectedBuildingPanel.h"
|
||||||
|
#include "FactoryQueries.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
@@ -234,8 +235,8 @@ void SelectedBuildingPanel::buildSingle(BuildingId id)
|
|||||||
m_singleBuildingId = id;
|
m_singleBuildingId = id;
|
||||||
hideAllWidgets();
|
hideAllWidgets();
|
||||||
|
|
||||||
const Building* b = m_sim->getBuildings().findBuilding(id);
|
const Building* b = findBuilding(m_sim->getFactoryState(), id);
|
||||||
const ConstructionSite* s = b ? nullptr : m_sim->getBuildings().findSite(id);
|
const ConstructionSite* s = b ? nullptr : findSite(m_sim->getFactoryState(), id);
|
||||||
if (!b && !s)
|
if (!b && !s)
|
||||||
{
|
{
|
||||||
buildEmpty();
|
buildEmpty();
|
||||||
@@ -602,7 +603,7 @@ void SelectedBuildingPanel::refreshSelectionDisplay(RefreshReason reason)
|
|||||||
// category owns the panel there is none: yieldToFieldSelection() has cleared it, so
|
// category owns the panel there is none: yieldToFieldSelection() has cleared it, so
|
||||||
// this returns immediately and the field panel refreshes itself off the same events.
|
// this returns immediately and the field panel refreshes itself off the same events.
|
||||||
if (!m_singleBuildingId.has_value()) { return; }
|
if (!m_singleBuildingId.has_value()) { return; }
|
||||||
const Building* b = m_sim->getBuildings().findBuilding(*m_singleBuildingId);
|
const Building* b = findBuilding(m_sim->getFactoryState(), *m_singleBuildingId);
|
||||||
if (b)
|
if (b)
|
||||||
{
|
{
|
||||||
if (m_titleLabel->text().startsWith(tr("(Building) ")))
|
if (m_titleLabel->text().startsWith(tr("(Building) ")))
|
||||||
@@ -615,7 +616,7 @@ void SelectedBuildingPanel::refreshSelectionDisplay(RefreshReason reason)
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const ConstructionSite* s = m_sim->getBuildings().findSite(*m_singleBuildingId);
|
const ConstructionSite* s = findSite(m_sim->getFactoryState(), *m_singleBuildingId);
|
||||||
if (s)
|
if (s)
|
||||||
{
|
{
|
||||||
// A periodic tick only advances construction progress, so update just the
|
// A periodic tick only advances construction progress, so update just the
|
||||||
@@ -649,13 +650,13 @@ void SelectedBuildingPanel::buildMulti(const std::vector<BuildingId>& ids)
|
|||||||
std::map<BuildingType, int> counts;
|
std::map<BuildingType, int> counts;
|
||||||
for (BuildingId id : ids)
|
for (BuildingId id : ids)
|
||||||
{
|
{
|
||||||
const Building* b = m_sim->getBuildings().findBuilding(id);
|
const Building* b = findBuilding(m_sim->getFactoryState(), id);
|
||||||
if (b)
|
if (b)
|
||||||
{
|
{
|
||||||
counts[b->type]++;
|
counts[b->type]++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const ConstructionSite* s = m_sim->getBuildings().findSite(id);
|
const ConstructionSite* s = findSite(m_sim->getFactoryState(), id);
|
||||||
if (s)
|
if (s)
|
||||||
{
|
{
|
||||||
counts[s->type]++;
|
counts[s->type]++;
|
||||||
@@ -817,7 +818,7 @@ void SelectedBuildingPanel::onClearBelt()
|
|||||||
std::vector<QPoint> tiles;
|
std::vector<QPoint> tiles;
|
||||||
for (BuildingId id : m_selectedBuildingIds)
|
for (BuildingId id : m_selectedBuildingIds)
|
||||||
{
|
{
|
||||||
const Building* b = m_sim->getBuildings().findBuilding(id);
|
const Building* b = findBuilding(m_sim->getFactoryState(), id);
|
||||||
if (b && isBeltSubsystemType(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