|
|
|
|
@@ -7,6 +7,7 @@
|
|
|
|
|
#include <QPoint>
|
|
|
|
|
|
|
|
|
|
#include "Blueprint.h"
|
|
|
|
|
#include "Building.h"
|
|
|
|
|
#include "BuildingsConfig.h"
|
|
|
|
|
#include "BuildingSystem.h"
|
|
|
|
|
#include "BuildingType.h"
|
|
|
|
|
@@ -15,6 +16,7 @@
|
|
|
|
|
#include "Rotation.h"
|
|
|
|
|
#include "Simulation.h"
|
|
|
|
|
#include "SurfaceMask.h"
|
|
|
|
|
#include "Tick.h"
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// Helpers that mirror the production implementations under test.
|
|
|
|
|
@@ -58,6 +60,7 @@ struct BuildingSpec
|
|
|
|
|
std::vector<QPoint> bodyCells;
|
|
|
|
|
BuildingType type;
|
|
|
|
|
Rotation rotation;
|
|
|
|
|
std::string recipeId; // empty = none
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static Blueprint buildBlueprint(const std::vector<BuildingSpec>& specs)
|
|
|
|
|
@@ -83,6 +86,7 @@ static Blueprint buildBlueprint(const std::vector<BuildingSpec>& specs)
|
|
|
|
|
bb.type = s.type;
|
|
|
|
|
bb.rotation = s.rotation;
|
|
|
|
|
bb.offset = s.anchor - center;
|
|
|
|
|
bb.recipeId = s.recipeId;
|
|
|
|
|
bp.buildings.push_back(bb);
|
|
|
|
|
}
|
|
|
|
|
return bp;
|
|
|
|
|
@@ -581,3 +585,83 @@ TEST_CASE("Blueprint placement: insufficient blocks returns kInvalidEntityId and
|
|
|
|
|
REQUIRE(id == kInvalidEntityId);
|
|
|
|
|
REQUIRE(sim.buildingBlocksStock() == blocksBeforeAttempt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// Recipe / schematic capture and re-application
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
TEST_CASE("Blueprint: recipeId is stored in BlueprintBuilding", "[blueprint]")
|
|
|
|
|
{
|
|
|
|
|
const BuildingSpec spec{
|
|
|
|
|
QPoint(-5, 0), {QPoint(-5, 0)}, BuildingType::Miner, Rotation::East,
|
|
|
|
|
"mine_iron_ore"
|
|
|
|
|
};
|
|
|
|
|
const Blueprint bp = buildBlueprint({ spec });
|
|
|
|
|
|
|
|
|
|
REQUIRE(bp.buildings.size() == 1);
|
|
|
|
|
REQUIRE(bp.buildings[0].recipeId == "mine_iron_ore");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("Blueprint: building with no recipe has empty recipeId", "[blueprint]")
|
|
|
|
|
{
|
|
|
|
|
const BuildingSpec spec{
|
|
|
|
|
QPoint(-5, 0), {QPoint(-5, 0)}, BuildingType::Belt, Rotation::East
|
|
|
|
|
// recipeId defaults to ""
|
|
|
|
|
};
|
|
|
|
|
const Blueprint bp = buildBlueprint({ spec });
|
|
|
|
|
|
|
|
|
|
REQUIRE(bp.buildings[0].recipeId.empty());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("Blueprint placement: setRecipe on construction site stores recipe", "[blueprint]")
|
|
|
|
|
{
|
|
|
|
|
Simulation sim(loadConfig());
|
|
|
|
|
|
|
|
|
|
// Miner body cells: (0,0),(1,0),(0,1) — all at x < 0, valid for asteroid.
|
|
|
|
|
const EntityId id = sim.tryPlaceBuilding(BuildingType::Miner, QPoint(-2, 0), Rotation::East);
|
|
|
|
|
REQUIRE(id != kInvalidEntityId);
|
|
|
|
|
|
|
|
|
|
sim.buildings().setRecipe(id, "mine_iron_ore");
|
|
|
|
|
|
|
|
|
|
const ConstructionSite* site = sim.buildings().findSite(id);
|
|
|
|
|
REQUIRE(site != nullptr);
|
|
|
|
|
REQUIRE(site->recipeId == "mine_iron_ore");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("Blueprint placement: recipe transfers to building after construction completes",
|
|
|
|
|
"[blueprint]")
|
|
|
|
|
{
|
|
|
|
|
Simulation sim(loadConfig());
|
|
|
|
|
|
|
|
|
|
const EntityId id = sim.tryPlaceBuilding(BuildingType::Miner, QPoint(-2, 0), Rotation::East);
|
|
|
|
|
REQUIRE(id != kInvalidEntityId);
|
|
|
|
|
sim.buildings().setRecipe(id, "mine_copper_ore");
|
|
|
|
|
|
|
|
|
|
// Miner construction_time_seconds = 10 → completesAt = secondsToTicks(10) = 300.
|
|
|
|
|
// Run 301 ticks (0..300) to process the completion tick.
|
|
|
|
|
for (int i = 0; i <= static_cast<int>(secondsToTicks(10.0)); ++i)
|
|
|
|
|
{
|
|
|
|
|
sim.tick();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Building* b = sim.buildings().findBuilding(id);
|
|
|
|
|
REQUIRE(b != nullptr);
|
|
|
|
|
REQUIRE(b->recipeId == "mine_copper_ore");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("Blueprint placement: interceptor schematic is unlocked at game start", "[blueprint]")
|
|
|
|
|
{
|
|
|
|
|
// "interceptor" has available_from_start = true in the test config.
|
|
|
|
|
// This confirms the guard in placeBlueprintAtTile passes for start-unlocked schematics.
|
|
|
|
|
Simulation sim(loadConfig());
|
|
|
|
|
REQUIRE(sim.isSchematicUnlocked("interceptor"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("Blueprint placement: repair_ship schematic is locked at game start", "[blueprint]")
|
|
|
|
|
{
|
|
|
|
|
// "repair_ship" has available_from_start = false in the test config.
|
|
|
|
|
// This confirms the guard in placeBlueprintAtTile blocks locked schematics,
|
|
|
|
|
// leaving the shipyard's schematic unset.
|
|
|
|
|
Simulation sim(loadConfig());
|
|
|
|
|
REQUIRE_FALSE(sim.isSchematicUnlocked("repair_ship"));
|
|
|
|
|
}
|
|
|
|
|
|