move blueprints out of the sidebar into Ctrl+C / Ctrl+V dialogs
This commit is contained in:
@@ -56,7 +56,7 @@ static Rotation rotCCW(Rotation r)
|
||||
return Rotation::East;
|
||||
}
|
||||
|
||||
// Mirror of BlueprintPanel::createBlueprintFromSelection: given per-building
|
||||
// Mirror of BlueprintLibrary::createBlueprintFromSelection: given per-building
|
||||
// (anchor, bodyCells, type, rotation), compute Blueprint with floor-division
|
||||
// bounding-box center and per-building tile offsets.
|
||||
struct BuildingSpec
|
||||
@@ -149,7 +149,7 @@ static void applyRotationCCW(Blueprint& bp, const GameConfig& cfg)
|
||||
}
|
||||
}
|
||||
|
||||
// Mirrors BlueprintPanel::createBlueprintFromSelection's player-placeable filter:
|
||||
// Mirrors BlueprintLibrary::createBlueprintFromSelection's player-placeable filter:
|
||||
// building types absent from buildings.toml (HQ, stations) or with playerPlaceable=false
|
||||
// are silently excluded before the bounding-box center and offsets are computed.
|
||||
static Blueprint buildBlueprintFiltered(const std::vector<BuildingSpec>& specs,
|
||||
|
||||
@@ -82,7 +82,7 @@ TEST_CASE("Only one mode is active at a time", "[buildmode]")
|
||||
TEST_CASE("Entering builder mode announces that blueprint mode ended", "[buildmode]")
|
||||
{
|
||||
// Regression: entering builder mode used to drop the blueprint silently, so the
|
||||
// blueprint panel kept its button highlighted for a mode that was over.
|
||||
// blueprint library kept tracking an active blueprint for a mode that was over.
|
||||
BuildModeController controller;
|
||||
controller.enterBlueprintMode(makeBlueprint());
|
||||
|
||||
|
||||
@@ -139,3 +139,100 @@ TEST_CASE("readBuildingConfig returns nullopt for an unknown id", "[copyconfig]"
|
||||
Simulation sim(loadTestConfig(), 7);
|
||||
CHECK_FALSE(readBuildingConfig(sim, kInvalidBuildingId).has_value());
|
||||
}
|
||||
|
||||
// The derived facts a blueprint card shows (REQ-UI-BLUEPRINT-CARD). They live here
|
||||
// rather than in the dialog so they can be tested: src/ui is off the test include path.
|
||||
|
||||
namespace
|
||||
{
|
||||
Blueprint makeBlueprint(const std::vector<BuildingType>& types)
|
||||
{
|
||||
Blueprint blueprint;
|
||||
for (const BuildingType type : types)
|
||||
{
|
||||
BlueprintBuilding building;
|
||||
building.type = type;
|
||||
building.rotation = Rotation::East;
|
||||
blueprint.buildings.push_back(building);
|
||||
}
|
||||
return blueprint;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("computeBlueprintCost sums the constituent placement costs", "[blueprint]")
|
||||
{
|
||||
const GameConfig cfg = loadTestConfig();
|
||||
|
||||
// belt 2 + belt 2 + miner 15.
|
||||
const Blueprint blueprint = makeBlueprint(
|
||||
{BuildingType::Belt, BuildingType::Belt, BuildingType::Miner});
|
||||
CHECK(computeBlueprintCost(blueprint, cfg.buildings) == 19);
|
||||
}
|
||||
|
||||
TEST_CASE("computeBlueprintCost ignores types absent from buildings.toml", "[blueprint]")
|
||||
{
|
||||
const GameConfig cfg = loadTestConfig();
|
||||
REQUIRE(cfg.buildings.findBuildingDef(BuildingType::Hq) == nullptr);
|
||||
|
||||
const Blueprint blueprint = makeBlueprint({BuildingType::Belt, BuildingType::Hq});
|
||||
CHECK(computeBlueprintCost(blueprint, cfg.buildings) == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("computeBlueprintCost is zero for an empty blueprint", "[blueprint]")
|
||||
{
|
||||
const GameConfig cfg = loadTestConfig();
|
||||
CHECK(computeBlueprintCost(Blueprint{}, cfg.buildings) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("summarizeBlueprintContents orders by descending count", "[blueprint]")
|
||||
{
|
||||
const GameConfig cfg = loadTestConfig();
|
||||
|
||||
const Blueprint blueprint = makeBlueprint(
|
||||
{BuildingType::Belt, BuildingType::Smelter, BuildingType::Miner,
|
||||
BuildingType::Miner, BuildingType::Smelter, BuildingType::Miner});
|
||||
|
||||
const std::vector<BlueprintContentEntry> summary =
|
||||
summarizeBlueprintContents(blueprint, cfg.buildings);
|
||||
REQUIRE(summary.size() == 3);
|
||||
CHECK(summary[0].buildingName == "Miner");
|
||||
CHECK(summary[0].count == 3);
|
||||
CHECK(summary[1].buildingName == "Smelter");
|
||||
CHECK(summary[1].count == 2);
|
||||
CHECK(summary[2].buildingName == "Belt");
|
||||
CHECK(summary[2].count == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("summarizeBlueprintContents breaks ties in buildings.toml order", "[blueprint]")
|
||||
{
|
||||
const GameConfig cfg = loadTestConfig();
|
||||
|
||||
// Miner is declared after Belt in buildings.toml, which is the order the build
|
||||
// button bar lays its buttons out, so Belt wins the tie despite the equal counts
|
||||
// and despite sorting later by enum value.
|
||||
const Blueprint blueprint = makeBlueprint(
|
||||
{BuildingType::Miner, BuildingType::Belt, BuildingType::Miner, BuildingType::Belt});
|
||||
|
||||
const std::vector<BlueprintContentEntry> summary =
|
||||
summarizeBlueprintContents(blueprint, cfg.buildings);
|
||||
REQUIRE(summary.size() == 2);
|
||||
CHECK(summary[0].buildingName == "Belt");
|
||||
CHECK(summary[1].buildingName == "Miner");
|
||||
}
|
||||
|
||||
TEST_CASE("summarizeBlueprintContents spells multi-word ids as display names", "[blueprint]")
|
||||
{
|
||||
const GameConfig cfg = loadTestConfig();
|
||||
|
||||
const Blueprint blueprint = makeBlueprint({BuildingType::TunnelEntry});
|
||||
const std::vector<BlueprintContentEntry> summary =
|
||||
summarizeBlueprintContents(blueprint, cfg.buildings);
|
||||
REQUIRE(summary.size() == 1);
|
||||
CHECK(summary[0].buildingName == "Tunnel Entry");
|
||||
}
|
||||
|
||||
TEST_CASE("summarizeBlueprintContents is empty for an empty blueprint", "[blueprint]")
|
||||
{
|
||||
const GameConfig cfg = loadTestConfig();
|
||||
CHECK(summarizeBlueprintContents(Blueprint{}, cfg.buildings).empty());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user