store ship module layout in shipyard blueprint

This commit is contained in:
2026-06-04 21:38:46 +02:00
parent 42b51cc6f4
commit 3e19e44f24
6 changed files with 252 additions and 18 deletions

View File

@@ -1,5 +1,6 @@
#include "catch.hpp"
#include <optional>
#include <stdexcept>
#include <vector>
@@ -10,6 +11,7 @@
#include "BlueprintSerializer.h"
#include "BuildingType.h"
#include "Rotation.h"
#include "ShipLayout.h"
namespace
{
@@ -26,13 +28,15 @@ Blueprint makeBlueprintWith(const std::vector<BlueprintBuilding>& buildings,
BlueprintBuilding makeBuilding(BuildingType type,
Rotation rotation,
QPoint offset,
std::string recipeId = "")
std::string recipeId = "",
std::optional<ShipLayoutConfig> shipLayout = std::nullopt)
{
BlueprintBuilding b;
b.type = type;
b.rotation = rotation;
b.offset = offset;
b.recipeId = std::move(recipeId);
b.type = type;
b.rotation = rotation;
b.offset = offset;
b.recipeId = std::move(recipeId);
b.shipLayout = std::move(shipLayout);
return b;
}
@@ -185,3 +189,77 @@ TEST_CASE("BlueprintSerializer: unknown building type throws", "[serializer]")
BlueprintSerializer::deserialize(badToml),
std::runtime_error);
}
// ---------------------------------------------------------------------------
// Ship layout round-trip through TOML serialization
// ---------------------------------------------------------------------------
TEST_CASE("BlueprintSerializer: shipyard with modules round-trips ship layout", "[serializer]")
{
ShipLayoutConfig layout;
PlacedModule pmA;
pmA.moduleId = "laser_cannon";
pmA.position = QPoint(1, 1);
pmA.rotation = Rotation::East;
PlacedModule pmB;
pmB.moduleId = "sensor_booster";
pmB.position = QPoint(0, 1);
pmB.rotation = Rotation::South;
layout.placedModules = { pmA, pmB };
const Blueprint original = makeBlueprintWith(
{ makeBuilding(BuildingType::Shipyard, Rotation::East, QPoint(0, 0),
"interceptor", layout) },
"Gunship");
const std::string toml = BlueprintSerializer::serialize({ original });
const std::vector<Blueprint> loaded = BlueprintSerializer::deserialize(toml);
REQUIRE(loaded.size() == 1);
REQUIRE(loaded[0].buildings.size() == 1);
const BlueprintBuilding& b = loaded[0].buildings[0];
REQUIRE(b.shipLayout.has_value());
REQUIRE(b.shipLayout->placedModules.size() == 2);
REQUIRE(b.shipLayout->placedModules[0].moduleId == "laser_cannon");
REQUIRE(b.shipLayout->placedModules[0].position == QPoint(1, 1));
REQUIRE(b.shipLayout->placedModules[0].rotation == Rotation::East);
REQUIRE(b.shipLayout->placedModules[1].moduleId == "sensor_booster");
REQUIRE(b.shipLayout->placedModules[1].position == QPoint(0, 1));
REQUIRE(b.shipLayout->placedModules[1].rotation == Rotation::South);
}
TEST_CASE("BlueprintSerializer: building without ship layout round-trips with nullopt", "[serializer]")
{
// A shipyard with no shipLayout set — no modules key should be emitted,
// and deserialization must leave shipLayout as nullopt.
const Blueprint original = makeBlueprintWith(
{ makeBuilding(BuildingType::Shipyard, Rotation::East, QPoint(0, 0), "interceptor") },
"EmptyYard");
const std::string toml = BlueprintSerializer::serialize({ original });
const std::vector<Blueprint> loaded = BlueprintSerializer::deserialize(toml);
REQUIRE(loaded.size() == 1);
REQUIRE(loaded[0].buildings.size() == 1);
REQUIRE_FALSE(loaded[0].buildings[0].shipLayout.has_value());
}
TEST_CASE("BlueprintSerializer: shipyard with empty modules list round-trips", "[serializer]")
{
// An explicitly empty layout (player cleared all modules) should round-trip
// as a present but empty ShipLayoutConfig, distinct from nullopt.
ShipLayoutConfig emptyLayout;
const Blueprint original = makeBlueprintWith(
{ makeBuilding(BuildingType::Shipyard, Rotation::East, QPoint(0, 0),
"interceptor", emptyLayout) },
"EmptyLayout");
const std::string toml = BlueprintSerializer::serialize({ original });
const std::vector<Blueprint> loaded = BlueprintSerializer::deserialize(toml);
REQUIRE(loaded.size() == 1);
REQUIRE(loaded[0].buildings.size() == 1);
REQUIRE(loaded[0].buildings[0].shipLayout.has_value());
REQUIRE(loaded[0].buildings[0].shipLayout->placedModules.empty());
}