Implement REQ-BLD-COPY-CONFIG: copy building settings with Shift
Shift+right-click a building copies its settings (miner/assembler recipe, shipyard schematic + module layout, or splitter output filters) into a temporary UI cache; Shift+left-click stamps them onto another building of the same type. The gesture is active only in the default selection mode, works on operational buildings and construction sites, and the cache clears when Shift is released (or on Restart). Paste reuses the existing configuration commands (SetRecipe, SetShipLayout, SetSplitterFilters, SetSiteSplitterFilters), so it inherits their buffer- clearing / mid-cycle-cancel semantics and replays for free — no new command kinds or serializer changes. Extract the recipe/schematic/layout/filter reader into a shared readBuildingConfig() (lib/sim), handling buildings and sites alike, and reuse it from BlueprintPanel to remove the duplicated capture logic. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VGNNLeFWhVzvxkK9qVXP2K
This commit is contained in:
133
src/test/BuildingConfigTest.cpp
Normal file
133
src/test/BuildingConfigTest.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
#include "catch.hpp"
|
||||
|
||||
#include "Building.h"
|
||||
#include "BuildingConfig.h"
|
||||
#include "BuildingSystem.h"
|
||||
#include "BuildingType.h"
|
||||
#include "ConfigLoader.h"
|
||||
#include "GameConfig.h"
|
||||
#include "Rotation.h"
|
||||
#include "ShipLayout.h"
|
||||
#include "ShipsConfig.h"
|
||||
#include "Simulation.h"
|
||||
#include "SimulationTestAccess.h"
|
||||
|
||||
// readBuildingConfig underpins the copy-settings gesture (REQ-BLD-COPY-CONFIG):
|
||||
// it extracts a building's recipe / schematic / layout / splitter filters so they
|
||||
// can be stamped onto a same-type building. It reads operational buildings and
|
||||
// construction sites alike.
|
||||
|
||||
namespace
|
||||
{
|
||||
GameConfig loadConfig()
|
||||
{
|
||||
return ConfigLoader::loadFromDirectory(CONFIG_DIR);
|
||||
}
|
||||
|
||||
const BuildingDef* findDef(const GameConfig& cfg, BuildingType type)
|
||||
{
|
||||
for (const BuildingDef& def : cfg.buildings.buildings)
|
||||
{
|
||||
if (def.type == type) { return &def; }
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BuildingId placeOperational(Simulation& sim, const GameConfig& cfg,
|
||||
BuildingType type, QPoint anchor)
|
||||
{
|
||||
const BuildingDef* def = findDef(cfg, type);
|
||||
REQUIRE(def != nullptr);
|
||||
return SimulationTestAccess::buildings(sim).placeImmediate(
|
||||
type, def->surfaceMask, anchor, Rotation::East);
|
||||
}
|
||||
|
||||
const ShipDef* findAvailableSchematic(const GameConfig& cfg)
|
||||
{
|
||||
for (const ShipDef& def : cfg.ships.ships)
|
||||
{
|
||||
if (def.unlockAtStationLevel == -1) { return &def; }
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("readBuildingConfig returns a miner's selected recipe", "[copyconfig]")
|
||||
{
|
||||
const GameConfig cfg = loadConfig();
|
||||
Simulation sim(loadConfig(), 7);
|
||||
|
||||
const BuildingId id = placeOperational(sim, cfg, BuildingType::Miner, QPoint(0, 0));
|
||||
SimulationTestAccess::buildings(sim).setRecipe(id, "mine_iron_ore");
|
||||
|
||||
const std::optional<BuildingConfig> config = readBuildingConfig(sim, id);
|
||||
REQUIRE(config.has_value());
|
||||
CHECK(config->type == BuildingType::Miner);
|
||||
REQUIRE(config->recipeId.has_value());
|
||||
CHECK(*config->recipeId == "mine_iron_ore");
|
||||
CHECK_FALSE(config->isSplitter);
|
||||
CHECK_FALSE(config->shipLayout.has_value());
|
||||
}
|
||||
|
||||
TEST_CASE("readBuildingConfig leaves recipe unset when nothing is selected",
|
||||
"[copyconfig]")
|
||||
{
|
||||
const GameConfig cfg = loadConfig();
|
||||
Simulation sim(loadConfig(), 7);
|
||||
|
||||
const BuildingId id = placeOperational(sim, cfg, BuildingType::Assembler, QPoint(0, 0));
|
||||
|
||||
const std::optional<BuildingConfig> config = readBuildingConfig(sim, id);
|
||||
REQUIRE(config.has_value());
|
||||
CHECK(config->type == BuildingType::Assembler);
|
||||
CHECK_FALSE(config->recipeId.has_value()); // nothing to copy
|
||||
CHECK_FALSE(config->isSplitter);
|
||||
}
|
||||
|
||||
TEST_CASE("readBuildingConfig returns a shipyard's schematic and layout",
|
||||
"[copyconfig]")
|
||||
{
|
||||
const GameConfig cfg = loadConfig();
|
||||
Simulation sim(loadConfig(), 7);
|
||||
|
||||
const ShipDef* schematic = findAvailableSchematic(cfg);
|
||||
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{});
|
||||
|
||||
const std::optional<BuildingConfig> config = readBuildingConfig(sim, id);
|
||||
REQUIRE(config.has_value());
|
||||
CHECK(config->type == BuildingType::Shipyard);
|
||||
REQUIRE(config->recipeId.has_value());
|
||||
CHECK(*config->recipeId == schematic->id);
|
||||
CHECK(config->shipLayout.has_value());
|
||||
}
|
||||
|
||||
TEST_CASE("readBuildingConfig reads a queued construction site", "[copyconfig]")
|
||||
{
|
||||
const GameConfig cfg = loadConfig();
|
||||
Simulation sim(loadConfig(), 7);
|
||||
|
||||
// A placed miner enters the construction queue as a site (not yet operational).
|
||||
const BuildingId id =
|
||||
SimulationTestAccess::place(sim, BuildingType::Miner, QPoint(-2, 0), Rotation::East);
|
||||
REQUIRE(id != kInvalidBuildingId);
|
||||
REQUIRE(sim.buildings().findBuilding(id) == nullptr);
|
||||
REQUIRE(sim.buildings().findSite(id) != nullptr);
|
||||
|
||||
SimulationTestAccess::buildings(sim).setRecipe(id, "mine_iron_ore");
|
||||
|
||||
const std::optional<BuildingConfig> config = readBuildingConfig(sim, id);
|
||||
REQUIRE(config.has_value());
|
||||
CHECK(config->type == BuildingType::Miner);
|
||||
REQUIRE(config->recipeId.has_value());
|
||||
CHECK(*config->recipeId == "mine_iron_ore");
|
||||
}
|
||||
|
||||
TEST_CASE("readBuildingConfig returns nullopt for an unknown id", "[copyconfig]")
|
||||
{
|
||||
Simulation sim(loadConfig(), 7);
|
||||
CHECK_FALSE(readBuildingConfig(sim, kInvalidBuildingId).has_value());
|
||||
}
|
||||
@@ -9,6 +9,7 @@ add_files(
|
||||
BeltSystemTest.cpp
|
||||
SurfaceMaskTest.cpp
|
||||
BuildingTest.cpp
|
||||
BuildingConfigTest.cpp
|
||||
ShipTest.cpp
|
||||
ScrapTest.cpp
|
||||
BehaviorSystemTest.cpp
|
||||
|
||||
Reference in New Issue
Block a user