make BuildingSystem stateless: FactoryState becomes a parameter
This commit is contained in:
@@ -56,7 +56,7 @@
|
||||
struct Fixture
|
||||
{
|
||||
GameConfig cfg;
|
||||
FactoryState state;
|
||||
FactoryState state = makeFactoryState(cfg);
|
||||
BeltSystem belts;
|
||||
BuildingId nextBuildingId;
|
||||
int stock;
|
||||
@@ -79,7 +79,7 @@ struct Fixture
|
||||
, nextBuildingId(1)
|
||||
, stock(0)
|
||||
, rng(42)
|
||||
, buildings(cfg, state, belts,
|
||||
, buildings(cfg, belts,
|
||||
[this]() { return nextBuildingId++; },
|
||||
[this](int n) { stock += n; },
|
||||
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
|
||||
@@ -952,12 +952,12 @@ TEST_CASE("BehaviorSystem: full-cargo salvage ship moves toward SalvageBay", "[b
|
||||
{
|
||||
Fixture f;
|
||||
|
||||
const BuildingId bayId = f.buildings.place(BuildingType::SalvageBay,
|
||||
const BuildingId bayId = f.buildings.place(f.state, BuildingType::SalvageBay,
|
||||
QPoint(-4, 0), Rotation::East, 0).value();
|
||||
Tick t = 0;
|
||||
for (int i = 0; i < 500; ++i)
|
||||
{
|
||||
f.buildings.tickConstruction(t++);
|
||||
f.buildings.tickConstruction(f.state, t++);
|
||||
if (findBuilding(f.state, bayId) != nullptr)
|
||||
{
|
||||
break;
|
||||
@@ -987,12 +987,12 @@ TEST_CASE("SalvagerSystem: full-cargo ship at its SalvageBay hands over cargo",
|
||||
{
|
||||
Fixture f;
|
||||
|
||||
const BuildingId bayId = f.buildings.place(BuildingType::SalvageBay,
|
||||
const BuildingId bayId = f.buildings.place(f.state, BuildingType::SalvageBay,
|
||||
QPoint(-4, 0), Rotation::East, 0).value();
|
||||
Tick t = 0;
|
||||
for (int i = 0; i < 500; ++i)
|
||||
{
|
||||
f.buildings.tickConstruction(t++);
|
||||
f.buildings.tickConstruction(f.state, t++);
|
||||
if (findBuilding(f.state, bayId) != nullptr) { break; }
|
||||
}
|
||||
const Building* bay = findBuilding(f.state, bayId);
|
||||
|
||||
@@ -666,7 +666,7 @@ TEST_CASE("Blueprint placement: setRecipe on construction site stores recipe", "
|
||||
const BuildingId id = SimulationTestAccess::place(sim,BuildingType::Miner, QPoint(-2, 0), Rotation::East).value();
|
||||
REQUIRE(id != kInvalidBuildingId);
|
||||
|
||||
SimulationTestAccess::buildings(sim).setRecipe(id, "mine_iron_ore");
|
||||
SimulationTestAccess::buildings(sim).setRecipe(SimulationTestAccess::state(sim), id, "mine_iron_ore");
|
||||
|
||||
const ConstructionSite* site = findSite(sim.getFactoryState(), id);
|
||||
REQUIRE(site != nullptr);
|
||||
@@ -680,7 +680,7 @@ TEST_CASE("Blueprint placement: recipe transfers to building after construction
|
||||
|
||||
const BuildingId id = SimulationTestAccess::place(sim,BuildingType::Miner, QPoint(-2, 0), Rotation::East).value();
|
||||
REQUIRE(id != kInvalidBuildingId);
|
||||
SimulationTestAccess::buildings(sim).setRecipe(id, "mine_copper_ore");
|
||||
SimulationTestAccess::buildings(sim).setRecipe(SimulationTestAccess::state(sim), id, "mine_copper_ore");
|
||||
|
||||
// Miner construction_time_seconds = 10 → completesAt = secondsToTicks(10) = 300.
|
||||
// Run 301 ticks (0..300) to process the completion tick.
|
||||
@@ -726,7 +726,7 @@ TEST_CASE("Blueprint creation: a construction site's recipe is captured", "[blue
|
||||
const BuildingId id =
|
||||
SimulationTestAccess::place(sim, BuildingType::Miner, QPoint(-2, 0), Rotation::East).value();
|
||||
REQUIRE(id != kInvalidBuildingId);
|
||||
SimulationTestAccess::buildings(sim).setRecipe(id, "mine_iron_ore");
|
||||
SimulationTestAccess::buildings(sim).setRecipe(SimulationTestAccess::state(sim), id, "mine_iron_ore");
|
||||
|
||||
const Blueprint bp = captureBlueprintFromSelection(sim, { id });
|
||||
|
||||
@@ -743,7 +743,7 @@ TEST_CASE("Blueprint creation: mixed operational building and construction site
|
||||
const BuildingId idA =
|
||||
SimulationTestAccess::place(sim, BuildingType::Miner, QPoint(-2, 0), Rotation::East).value();
|
||||
REQUIRE(idA != kInvalidBuildingId);
|
||||
SimulationTestAccess::buildings(sim).setRecipe(idA, "mine_iron_ore");
|
||||
SimulationTestAccess::buildings(sim).setRecipe(SimulationTestAccess::state(sim), idA, "mine_iron_ore");
|
||||
for (int i = 0; i <= static_cast<int>(secondsToTicks(10.0)); ++i) { sim.tick(); }
|
||||
REQUIRE(findBuilding(sim.getFactoryState(), idA) != nullptr);
|
||||
|
||||
@@ -751,7 +751,7 @@ TEST_CASE("Blueprint creation: mixed operational building and construction site
|
||||
const BuildingId idB =
|
||||
SimulationTestAccess::place(sim, BuildingType::Miner, QPoint(-6, 0), Rotation::East).value();
|
||||
REQUIRE(idB != kInvalidBuildingId);
|
||||
SimulationTestAccess::buildings(sim).setRecipe(idB, "mine_copper_ore");
|
||||
SimulationTestAccess::buildings(sim).setRecipe(SimulationTestAccess::state(sim), idB, "mine_copper_ore");
|
||||
REQUIRE(findSite(sim.getFactoryState(), idB) != nullptr);
|
||||
|
||||
const Blueprint bp = captureBlueprintFromSelection(sim, { idA, idB });
|
||||
@@ -854,7 +854,7 @@ TEST_CASE("Blueprint placement: setShipLayout on construction site stores layout
|
||||
pm.rotation = Rotation::East;
|
||||
layout.placedModules.push_back(pm);
|
||||
|
||||
SimulationTestAccess::buildings(sim).setShipLayout(id, layout);
|
||||
SimulationTestAccess::buildings(sim).setShipLayout(SimulationTestAccess::state(sim), id, layout);
|
||||
|
||||
const ConstructionSite* site = findSite(sim.getFactoryState(), id);
|
||||
REQUIRE(site != nullptr);
|
||||
@@ -878,7 +878,7 @@ TEST_CASE("Blueprint placement: ship layout transfers to building after construc
|
||||
pm.rotation = Rotation::North;
|
||||
layout.placedModules.push_back(pm);
|
||||
|
||||
SimulationTestAccess::buildings(sim).setShipLayout(id, layout);
|
||||
SimulationTestAccess::buildings(sim).setShipLayout(SimulationTestAccess::state(sim), id, layout);
|
||||
|
||||
// Shipyard construction_time_seconds = 30 in the test config.
|
||||
double constructionTime = 0.0;
|
||||
|
||||
@@ -37,8 +37,7 @@ BuildingId placeOperational(Simulation& sim, const GameConfig& cfg,
|
||||
{
|
||||
const BuildingDef* def = findDef(cfg, type);
|
||||
REQUIRE(def != nullptr);
|
||||
return SimulationTestAccess::buildings(sim).placeImmediate(
|
||||
type, def->surfaceMask, anchor, Rotation::East);
|
||||
return SimulationTestAccess::buildings(sim).placeImmediate(SimulationTestAccess::state(sim), type, def->surfaceMask, anchor, Rotation::East);
|
||||
}
|
||||
|
||||
const ShipDef* findAvailableSchematic(const GameConfig& cfg)
|
||||
@@ -67,7 +66,7 @@ TEST_CASE("readBuildingConfig returns a miner's selected recipe", "[copyconfig]"
|
||||
Simulation sim(loadTestConfig(), 7);
|
||||
|
||||
const BuildingId id = placeOperational(sim, cfg, BuildingType::Miner, QPoint(0, 0));
|
||||
SimulationTestAccess::buildings(sim).setRecipe(id, "mine_iron_ore");
|
||||
SimulationTestAccess::buildings(sim).setRecipe(SimulationTestAccess::state(sim), id, "mine_iron_ore");
|
||||
|
||||
const std::optional<BuildingConfig> config = readBuildingConfig(sim, id);
|
||||
REQUIRE(config.has_value());
|
||||
@@ -103,8 +102,8 @@ TEST_CASE("readBuildingConfig returns a shipyard's schematic and layout",
|
||||
REQUIRE(schematic != nullptr);
|
||||
|
||||
const BuildingId id = placeOperational(sim, cfg, BuildingType::Shipyard, QPoint(0, 0));
|
||||
SimulationTestAccess::buildings(sim).setRecipe(id, schematic->id);
|
||||
SimulationTestAccess::buildings(sim).setShipLayout(id, ShipLayoutConfig{});
|
||||
SimulationTestAccess::buildings(sim).setRecipe(SimulationTestAccess::state(sim), id, schematic->id);
|
||||
SimulationTestAccess::buildings(sim).setShipLayout(SimulationTestAccess::state(sim), id, ShipLayoutConfig{});
|
||||
|
||||
const std::optional<BuildingConfig> config = readBuildingConfig(sim, id);
|
||||
REQUIRE(config.has_value());
|
||||
@@ -126,7 +125,7 @@ TEST_CASE("readBuildingConfig reads a queued construction site", "[copyconfig]")
|
||||
REQUIRE(findBuilding(sim.getFactoryState(), id) == nullptr);
|
||||
REQUIRE(findSite(sim.getFactoryState(), id) != nullptr);
|
||||
|
||||
SimulationTestAccess::buildings(sim).setRecipe(id, "mine_iron_ore");
|
||||
SimulationTestAccess::buildings(sim).setRecipe(SimulationTestAccess::state(sim), id, "mine_iron_ore");
|
||||
|
||||
const std::optional<BuildingConfig> config = readBuildingConfig(sim, id);
|
||||
REQUIRE(config.has_value());
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -52,7 +52,7 @@ static entt::entity findWeaponChild(EntityAdmin& admin, entt::entity ship)
|
||||
struct CombatFixture
|
||||
{
|
||||
GameConfig cfg;
|
||||
FactoryState state;
|
||||
FactoryState state = makeFactoryState(cfg);
|
||||
std::mt19937 rng;
|
||||
EntityAdmin admin;
|
||||
BuildingId nextBuildingId;
|
||||
@@ -67,7 +67,7 @@ struct CombatFixture
|
||||
, nextBuildingId(1)
|
||||
, belts(cfg.world.beltSpeed_tps)
|
||||
, ships(cfg, admin)
|
||||
, buildings(cfg, state, belts,
|
||||
, buildings(cfg, belts,
|
||||
[this]() { return nextBuildingId++; },
|
||||
[](int){},
|
||||
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
|
||||
|
||||
@@ -47,7 +47,7 @@ TEST_CASE("apply(PlaceBuildingCommand) with recipe matches place-then-setRecipe"
|
||||
|
||||
const BuildingId id =
|
||||
SimulationTestAccess::place(viaDirect, BuildingType::Miner, QPoint(-2, 0), Rotation::East).value();
|
||||
SimulationTestAccess::buildings(viaDirect).setRecipe(id, "mine_iron_ore");
|
||||
SimulationTestAccess::buildings(viaDirect).setRecipe(SimulationTestAccess::state(viaDirect), id, "mine_iron_ore");
|
||||
|
||||
REQUIRE(viaCommand.computeStateChecksum() == viaDirect.computeStateChecksum());
|
||||
}
|
||||
|
||||
@@ -62,8 +62,7 @@ static const BuildingDef* findShipyardDef(const GameConfig& cfg)
|
||||
|
||||
static BuildingId placeShipyard(Simulation& sim, const BuildingDef& yardDef)
|
||||
{
|
||||
return SimulationTestAccess::buildings(sim).placeImmediate(
|
||||
BuildingType::Shipyard,
|
||||
return SimulationTestAccess::buildings(sim).placeImmediate(SimulationTestAccess::state(sim), BuildingType::Shipyard,
|
||||
yardDef.surfaceMask,
|
||||
QPoint(0, 0),
|
||||
Rotation::East);
|
||||
@@ -73,7 +72,7 @@ static void fillMaterials(Simulation& sim, BuildingId yardId,
|
||||
const ShipDef& def,
|
||||
const ShipLayoutConfig& layout)
|
||||
{
|
||||
SimulationTestAccess::buildings(sim).forEachBuilding([&](Building& b) {
|
||||
SimulationTestAccess::buildings(sim).forEachBuilding(SimulationTestAccess::state(sim), [&](Building& b) {
|
||||
if (b.id != yardId)
|
||||
{
|
||||
return;
|
||||
@@ -208,7 +207,7 @@ TEST_CASE("Shipyard: setShipLayout reinitializes buffers with module materials",
|
||||
REQUIRE(yardDef != nullptr);
|
||||
|
||||
const BuildingId yardId = placeShipyard(sim, *yardDef);
|
||||
SimulationTestAccess::buildings(sim).setRecipe(yardId,"interceptor");
|
||||
SimulationTestAccess::buildings(sim).setRecipe(SimulationTestAccess::state(sim), yardId,"interceptor");
|
||||
|
||||
ShipLayoutConfig layout;
|
||||
PlacedModule pm;
|
||||
@@ -217,7 +216,7 @@ TEST_CASE("Shipyard: setShipLayout reinitializes buffers with module materials",
|
||||
pm.rotation = Rotation::East;
|
||||
layout.placedModules.push_back(pm);
|
||||
|
||||
SimulationTestAccess::buildings(sim).setShipLayout(yardId, layout);
|
||||
SimulationTestAccess::buildings(sim).setShipLayout(SimulationTestAccess::state(sim), yardId, layout);
|
||||
|
||||
const Building* b = findBuilding(sim.getFactoryState(), yardId);
|
||||
REQUIRE(b != nullptr);
|
||||
@@ -237,7 +236,7 @@ TEST_CASE("Shipyard: setShipLayout cancels in-progress production",
|
||||
REQUIRE(yardDef != nullptr);
|
||||
|
||||
const BuildingId yardId = placeShipyard(sim, *yardDef);
|
||||
SimulationTestAccess::buildings(sim).setRecipe(yardId,"interceptor");
|
||||
SimulationTestAccess::buildings(sim).setRecipe(SimulationTestAccess::state(sim), yardId,"interceptor");
|
||||
|
||||
// Fill materials and tick to start production.
|
||||
ShipLayoutConfig emptyLayout;
|
||||
@@ -256,7 +255,7 @@ TEST_CASE("Shipyard: setShipLayout cancels in-progress production",
|
||||
pm.rotation = Rotation::East;
|
||||
layout.placedModules.push_back(pm);
|
||||
|
||||
SimulationTestAccess::buildings(sim).setShipLayout(yardId, layout);
|
||||
SimulationTestAccess::buildings(sim).setShipLayout(SimulationTestAccess::state(sim), yardId, layout);
|
||||
|
||||
const Building* b2 = findBuilding(sim.getFactoryState(), yardId);
|
||||
REQUIRE(b2 != nullptr);
|
||||
@@ -279,7 +278,7 @@ TEST_CASE("Shipyard: builds a bare hull when no layout is configured",
|
||||
REQUIRE(yardDef != nullptr);
|
||||
|
||||
const BuildingId yardId = placeShipyard(sim, *yardDef);
|
||||
SimulationTestAccess::buildings(sim).setRecipe(yardId, "interceptor");
|
||||
SimulationTestAccess::buildings(sim).setRecipe(SimulationTestAccess::state(sim), yardId, "interceptor");
|
||||
// Deliberately no setShipLayout: recipe set, layout left unconfigured.
|
||||
|
||||
// Charge only the base-hull materials (an empty layout adds none).
|
||||
@@ -314,7 +313,7 @@ TEST_CASE("Shipyard: setRecipe clears ship layout", "[modules][shipyard]")
|
||||
REQUIRE(yardDef != nullptr);
|
||||
|
||||
const BuildingId yardId = placeShipyard(sim, *yardDef);
|
||||
SimulationTestAccess::buildings(sim).setRecipe(yardId,"interceptor");
|
||||
SimulationTestAccess::buildings(sim).setRecipe(SimulationTestAccess::state(sim), yardId,"interceptor");
|
||||
|
||||
ShipLayoutConfig layout;
|
||||
PlacedModule pm;
|
||||
@@ -322,13 +321,13 @@ TEST_CASE("Shipyard: setRecipe clears ship layout", "[modules][shipyard]")
|
||||
pm.position = QPoint(0, 0);
|
||||
pm.rotation = Rotation::East;
|
||||
layout.placedModules.push_back(pm);
|
||||
SimulationTestAccess::buildings(sim).setShipLayout(yardId, layout);
|
||||
SimulationTestAccess::buildings(sim).setShipLayout(SimulationTestAccess::state(sim), yardId, layout);
|
||||
|
||||
const Building* b1 = findBuilding(sim.getFactoryState(), yardId);
|
||||
REQUIRE(b1 != nullptr);
|
||||
REQUIRE(b1->shipLayout.has_value());
|
||||
|
||||
SimulationTestAccess::buildings(sim).setRecipe(yardId,"destroyer");
|
||||
SimulationTestAccess::buildings(sim).setRecipe(SimulationTestAccess::state(sim), yardId,"destroyer");
|
||||
|
||||
const Building* b2 = findBuilding(sim.getFactoryState(), yardId);
|
||||
REQUIRE(b2 != nullptr);
|
||||
@@ -343,7 +342,7 @@ TEST_CASE("Shipyard: setRecipe with unchanged recipe keeps ship layout",
|
||||
REQUIRE(yardDef != nullptr);
|
||||
|
||||
const BuildingId yardId = placeShipyard(sim, *yardDef);
|
||||
SimulationTestAccess::buildings(sim).setRecipe(yardId,"interceptor");
|
||||
SimulationTestAccess::buildings(sim).setRecipe(SimulationTestAccess::state(sim), yardId,"interceptor");
|
||||
|
||||
ShipLayoutConfig layout;
|
||||
PlacedModule pm;
|
||||
@@ -351,14 +350,14 @@ TEST_CASE("Shipyard: setRecipe with unchanged recipe keeps ship layout",
|
||||
pm.position = QPoint(0, 0);
|
||||
pm.rotation = Rotation::East;
|
||||
layout.placedModules.push_back(pm);
|
||||
SimulationTestAccess::buildings(sim).setShipLayout(yardId, layout);
|
||||
SimulationTestAccess::buildings(sim).setShipLayout(SimulationTestAccess::state(sim), yardId, layout);
|
||||
|
||||
const Building* b1 = findBuilding(sim.getFactoryState(), yardId);
|
||||
REQUIRE(b1 != nullptr);
|
||||
REQUIRE(b1->shipLayout.has_value());
|
||||
|
||||
// Re-selecting the same recipe must be a no-op and preserve the layout.
|
||||
SimulationTestAccess::buildings(sim).setRecipe(yardId,"interceptor");
|
||||
SimulationTestAccess::buildings(sim).setRecipe(SimulationTestAccess::state(sim), yardId,"interceptor");
|
||||
|
||||
const Building* b2 = findBuilding(sim.getFactoryState(), yardId);
|
||||
REQUIRE(b2 != nullptr);
|
||||
|
||||
@@ -58,8 +58,7 @@ static const BuildingDef* findShipyardDef(const GameConfig& cfg)
|
||||
|
||||
static BuildingId placeShipyard(Simulation& sim, const BuildingDef& yardDef)
|
||||
{
|
||||
return SimulationTestAccess::buildings(sim).placeImmediate(
|
||||
BuildingType::Shipyard,
|
||||
return SimulationTestAccess::buildings(sim).placeImmediate(SimulationTestAccess::state(sim), BuildingType::Shipyard,
|
||||
yardDef.surfaceMask,
|
||||
QPoint(0, 0),
|
||||
Rotation::East);
|
||||
@@ -75,7 +74,7 @@ static int countShips(Simulation& sim)
|
||||
|
||||
static void fillMaterials(Simulation& sim, BuildingId yardId, const ShipDef& def)
|
||||
{
|
||||
SimulationTestAccess::buildings(sim).forEachBuilding([&](Building& b)
|
||||
SimulationTestAccess::buildings(sim).forEachBuilding(SimulationTestAccess::state(sim), [&](Building& b)
|
||||
{
|
||||
if (b.id != yardId)
|
||||
{
|
||||
@@ -107,7 +106,7 @@ TEST_CASE("Shipyard: spawns a player ship after production cycle completes",
|
||||
const BuildingId yardId = placeShipyard(sim, *yardDef);
|
||||
REQUIRE(yardId != kInvalidBuildingId);
|
||||
|
||||
SimulationTestAccess::buildings(sim).setRecipe(yardId, def->id);
|
||||
SimulationTestAccess::buildings(sim).setRecipe(SimulationTestAccess::state(sim), yardId, def->id);
|
||||
fillMaterials(sim, yardId, *def);
|
||||
|
||||
// First tick: materials consumed, production cycle starts — no ship yet.
|
||||
@@ -166,7 +165,7 @@ TEST_CASE("Shipyard: does not spawn with insufficient materials", "[shipyard]")
|
||||
const int shipsBefore = countShips(sim);
|
||||
|
||||
const BuildingId yardId = placeShipyard(sim, *yardDef);
|
||||
SimulationTestAccess::buildings(sim).setRecipe(yardId, def->id);
|
||||
SimulationTestAccess::buildings(sim).setRecipe(SimulationTestAccess::state(sim), yardId, def->id);
|
||||
// Materials remain at zero (default after setRecipe); no cycle starts.
|
||||
|
||||
const Tick cycleTicks = secondsToTicks(def->schematic.productionTimeSeconds);
|
||||
@@ -188,7 +187,7 @@ TEST_CASE("Shipyard: spawns a second ship after materials replenished", "[shipya
|
||||
REQUIRE(yardDef != nullptr);
|
||||
|
||||
const BuildingId yardId = placeShipyard(sim, *yardDef);
|
||||
SimulationTestAccess::buildings(sim).setRecipe(yardId, def->id);
|
||||
SimulationTestAccess::buildings(sim).setRecipe(SimulationTestAccess::state(sim), yardId, def->id);
|
||||
|
||||
const Tick cycleTicks = secondsToTicks(def->schematic.productionTimeSeconds);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "BuildingId.h"
|
||||
#include "BuildingType.h"
|
||||
#include "Rotation.h"
|
||||
#include "FactoryState.h"
|
||||
#include "Simulation.h"
|
||||
|
||||
class BeltSystem;
|
||||
@@ -25,6 +26,7 @@ class BuildingSystem;
|
||||
struct SimulationTestAccess
|
||||
{
|
||||
static BuildingSystem& buildings(Simulation& sim) { return sim.getBuildingsMutable(); }
|
||||
static FactoryState& state(Simulation& sim) { return sim.m_factoryState; }
|
||||
static BeltSystem& belts(Simulation& sim) { return sim.getBeltsMutable(); }
|
||||
|
||||
static std::optional<BuildingId> place(Simulation& sim, BuildingType type,
|
||||
|
||||
Reference in New Issue
Block a user