Fix Salvage Bay drop-off not working by adding config-driven buffer capacity

This commit is contained in:
2026-07-09 20:11:12 +02:00
parent cc38bf95fa
commit 2ddf13238c
9 changed files with 83 additions and 2 deletions

View File

@@ -1,5 +1,6 @@
#pragma once
#include <optional>
#include <string>
#include <vector>
@@ -18,6 +19,10 @@ struct BuildingDef
// Stored as raw strings here; parsing into per-cell tiles + output ports
// happens when buildings are placed, not at load time.
std::vector<std::string> surfaceMask;
// Output-buffer holding size for buildings without a recipe-driven buffer.
// Only the Salvage Bay sets this (REQ-BLD-SALVAGE-BAY).
std::optional<int> outputBufferCapacity;
};
struct BuildingsConfig

View File

@@ -337,6 +337,12 @@ BuildingsConfig ConfigLoader::loadBuildings(const std::string& path)
def.constructionTimeSeconds = requireDouble(mt["construction_time_seconds"], file, elemPath + ".construction_time_seconds");
def.surfaceMask = requireStringArray(mt["surface_mask"], file, elemPath + ".surface_mask");
if (mt.contains("output_buffer_capacity"))
{
def.outputBufferCapacity = static_cast<int>(
requireInt(mt["output_buffer_capacity"], file, elemPath + ".output_buffer_capacity"));
}
const std::optional<BuildingType> parsedType = parseBuildingType(def.id);
if (!parsedType)
{

View File

@@ -154,6 +154,16 @@ void BuildingSystem::initShipyardBuffers(Building& b) const
}
}
void BuildingSystem::initSalvageBayBuffer(Building& b) const
{
// Salvage Bay has no recipe-driven buffer; its output-buffer holding size for
// ship drop-off is config-defined (REQ-BLD-SALVAGE-BAY).
b.outputBuffer.items.clear();
const BuildingDef* def = findBuildingDef(BuildingType::SalvageBay);
b.outputBuffer.capacity =
(def && def->outputBufferCapacity) ? *def->outputBufferCapacity : 0;
}
std::vector<Port> BuildingSystem::computeInputPorts(const Building& b) const
{
// Build lookup sets for quick membership checks.
@@ -577,7 +587,11 @@ void BuildingSystem::tickConstruction(Tick currentTick)
}
building.inputPorts = computeInputPorts(building);
if (!building.recipeId.empty())
if (building.type == BuildingType::SalvageBay)
{
initSalvageBayBuffer(building);
}
else if (!building.recipeId.empty())
{
if (building.type == BuildingType::Shipyard)
{
@@ -1238,6 +1252,11 @@ BuildingId BuildingSystem::placeImmediate(BuildingType type,
}
building.inputPorts = computeInputPorts(building);
if (type == BuildingType::SalvageBay)
{
initSalvageBayBuffer(building);
}
m_buildings.push_back(std::move(building));
return id;
}

View File

@@ -170,6 +170,7 @@ private:
const ModuleDef* findModuleDef(const std::string& id) const;
void initBuffers(Building& b, const RecipeDef& recipe) const;
void initShipyardBuffers(Building& b) const;
void initSalvageBayBuffer(Building& b) const;
std::vector<Port> computeInputPorts(const Building& b) const;
std::vector<Item> rollReprocessingOutput(const RecipeDef& recipe);
bool bodyCellsWithinWorldBounds(

View File

@@ -984,6 +984,45 @@ TEST_CASE("BehaviorSystem: full-cargo salvage ship moves toward SalvageBay", "[b
REQUIRE(i.target.x() < pos(f.admin, ship).value.x());
}
TEST_CASE("SalvagerSystem: full-cargo ship at its SalvageBay hands over cargo", "[behavior]")
{
Fixture f;
const BuildingId bayId = f.buildings.place(BuildingType::SalvageBay,
QPoint(-4, 0), Rotation::East, 0);
Tick t = 0;
for (int i = 0; i < 500; ++i)
{
f.buildings.tickConstruction(t++);
if (f.buildings.findBuilding(bayId) != nullptr) { break; }
}
const Building* bay = f.buildings.findBuilding(bayId);
REQUIRE(bay != nullptr);
// Config-driven output-buffer capacity is applied on placement (REQ-BLD-SALVAGE-BAY).
REQUIRE(bay->outputBuffer.capacity == 20);
const QVector2D bayCenter(bay->anchor.x() + bay->footprint.width() / 2.0f,
bay->anchor.y() + bay->footprint.height() / 2.0f);
const ShipLayoutConfig salvageLayout = makeSingleModuleLayout("salvager");
const entt::entity ship = f.ships.spawn("salvage_ship", bayCenter, false, salvageLayout);
f.admin.get<PositionComponent>(ship).value = bayCenter;
CargoComponent& cargo = f.admin.get<CargoComponent>(ship);
cargo.current = cargo.maxCapacity; // full cargo
const int before = cargo.current;
REQUIRE(before > 0);
f.admin.get<DeliverScrapBehavior>(ship).deliveryBay = bayId;
f.salvageTick();
// One unit handed over from cargo into the bay's output buffer.
REQUIRE(f.admin.get<CargoComponent>(ship).current == before - 1);
const Building* bayAfter = f.buildings.findBuilding(bayId);
REQUIRE(bayAfter != nullptr);
REQUIRE(bayAfter->outputBuffer.items.size() == 1);
REQUIRE(bayAfter->outputBuffer.items.front().type.id == "scrap");
}
// ---------------------------------------------------------------------------
// Collection range (per-module)
// ---------------------------------------------------------------------------

View File

@@ -103,6 +103,15 @@ TEST_CASE("ConfigLoader loads the committed bin/config/ configs end-to-end", "[c
REQUIRE(minerIt != cfg.buildings.buildings.end());
REQUIRE(minerIt->cost == 15);
REQUIRE(minerIt->surfaceMask.size() == 2);
// Miner has no output-buffer-capacity override; the Salvage Bay does.
REQUIRE_FALSE(minerIt->outputBufferCapacity.has_value());
const auto salvageBayIt = std::find_if(
cfg.buildings.buildings.begin(), cfg.buildings.buildings.end(),
[](const BuildingDef& b) { return b.type == BuildingType::SalvageBay; });
REQUIRE(salvageBayIt != cfg.buildings.buildings.end());
REQUIRE(salvageBayIt->outputBufferCapacity.has_value());
REQUIRE(*salvageBayIt->outputBufferCapacity == 20);
// recipes.toml — reprocessing cycle has three weighted outputs.
const auto reproIt = std::find_if(