fix issue where shipyard did not produce anything

This commit is contained in:
2026-04-21 22:17:48 +02:00
parent 2523cd6a1b
commit 393c49e1bb
12 changed files with 393 additions and 16 deletions

View File

@@ -367,6 +367,8 @@ ShipsConfig ConfigLoader::loadShips(const std::string& path)
def.blueprint.materials = parseIngredients(materials, file, bpPath + ".materials");
def.blueprint.playerProductionLevel = static_cast<int>(requireInt(
bpMt["player_production_level"], file, bpPath + ".player_production_level"));
def.blueprint.productionTimeSeconds = requireDouble(
bpMt["production_time_seconds"], file, bpPath + ".production_time_seconds");
}
// Threat

View File

@@ -13,6 +13,7 @@ struct ShipBlueprint
{
std::vector<RecipeIngredient> materials;
int playerProductionLevel;
double productionTimeSeconds;
};
// Wave scheduling cost (REQ-WAV-THREAT-COST). Ships with cost_formula that

View File

@@ -11,11 +11,13 @@ BuildingSystem::BuildingSystem(const GameConfig& config,
BeltSystem& belts,
std::function<EntityId()> allocateId,
std::function<void(int)> addBuildingBlocks,
std::function<void(const std::string&, QVector2D)> spawnShip,
std::mt19937& rng)
: m_config(config)
, m_belts(belts)
, m_allocateId(std::move(allocateId))
, m_addBuildingBlocks(std::move(addBuildingBlocks))
, m_spawnShip(std::move(spawnShip))
, m_rng(rng)
{
}
@@ -49,6 +51,18 @@ const RecipeDef* BuildingSystem::findRecipe(const std::string& id,
return nullptr;
}
const ShipDef* BuildingSystem::findShipDef(const std::string& id) const
{
for (const ShipDef& def : m_config.ships.ships)
{
if (def.id == id)
{
return &def;
}
}
return nullptr;
}
void BuildingSystem::initBuffers(Building& b, const RecipeDef& recipe) const
{
b.inputBuffer.counts.clear();
@@ -86,6 +100,25 @@ void BuildingSystem::initBuffers(Building& b, const RecipeDef& recipe) const
}
}
void BuildingSystem::initShipyardBuffers(Building& b) const
{
b.inputBuffer.counts.clear();
b.inputBuffer.caps.clear();
b.outputBuffer.items.clear();
b.outputBuffer.capacity = 0;
const ShipDef* def = findShipDef(b.recipeId);
if (!def)
{
return;
}
for (const RecipeIngredient& ing : def->blueprint.materials)
{
const ItemType type{ing.item};
b.inputBuffer.counts[type] = 0;
b.inputBuffer.caps[type] = 2 * ing.amount;
}
}
std::vector<Port> BuildingSystem::computeInputPorts(const Building& b) const
{
// Build lookup sets for quick membership checks.
@@ -323,10 +356,17 @@ void BuildingSystem::setRecipe(EntityId id, const std::string& recipeId)
if (!recipeId.empty())
{
const RecipeDef* recipe = findRecipe(recipeId, building.type);
if (recipe)
if (building.type == BuildingType::Shipyard)
{
initBuffers(building, *recipe);
initShipyardBuffers(building);
}
else
{
const RecipeDef* recipe = findRecipe(recipeId, building.type);
if (recipe)
{
initBuffers(building, *recipe);
}
}
}
return;
@@ -394,10 +434,17 @@ void BuildingSystem::tickConstruction(Tick currentTick)
if (!building.recipeId.empty())
{
const RecipeDef* recipe = findRecipe(building.recipeId, building.type);
if (recipe)
if (building.type == BuildingType::Shipyard)
{
initBuffers(building, *recipe);
initShipyardBuffers(building);
}
else
{
const RecipeDef* recipe = findRecipe(building.recipeId, building.type);
if (recipe)
{
initBuffers(building, *recipe);
}
}
}
@@ -443,10 +490,13 @@ void BuildingSystem::tickBeltPull()
continue;
}
const RecipeDef* recipe = findRecipe(building.recipeId, building.type);
if (!recipe || recipe->inputs.empty())
if (building.type != BuildingType::Shipyard)
{
continue;
const RecipeDef* recipe = findRecipe(building.recipeId, building.type);
if (!recipe || recipe->inputs.empty())
{
continue;
}
}
for (const Port& port : building.inputPorts)
@@ -593,6 +643,73 @@ void BuildingSystem::tickProduction(Tick currentTick)
}
}
void BuildingSystem::tickShipyardProduction(Tick currentTick)
{
for (Building& building : m_buildings)
{
if (building.type != BuildingType::Shipyard)
{
continue;
}
if (building.recipeId.empty())
{
continue;
}
const ShipDef* shipDef = findShipDef(building.recipeId);
if (!shipDef)
{
continue;
}
// If a cycle is in progress, check for completion.
if (building.production)
{
if (currentTick >= building.production->completesAt)
{
if (!building.outputPorts.empty())
{
const Port& p = building.outputPorts[0];
const QVector2D spawnPos(p.tile.x() + 0.5f, p.tile.y() + 0.5f);
m_spawnShip(building.recipeId, spawnPos);
}
building.production = std::nullopt;
}
continue;
}
// Idle: check if all materials are available to start a new cycle.
bool inputsOk = true;
for (const RecipeIngredient& ing : shipDef->blueprint.materials)
{
const ItemType type{ing.item};
const std::map<ItemType, int>::const_iterator it =
building.inputBuffer.counts.find(type);
const int have = (it != building.inputBuffer.counts.end()) ? it->second : 0;
if (have < ing.amount)
{
inputsOk = false;
break;
}
}
if (!inputsOk)
{
continue;
}
// Consume materials and start the production cycle.
for (const RecipeIngredient& ing : shipDef->blueprint.materials)
{
building.inputBuffer.counts[ItemType{ing.item}] -= ing.amount;
}
Production prod;
prod.recipeId = building.recipeId;
prod.completesAt = currentTick
+ secondsToTicks(shipDef->blueprint.productionTimeSeconds);
building.production = std::move(prod);
}
}
void BuildingSystem::tickBeltPush()
{
for (Building& building : m_buildings)

View File

@@ -18,6 +18,7 @@
#include "EntityId.h"
#include "GameConfig.h"
#include "Rotation.h"
#include "ShipsConfig.h"
#include "Tick.h"
// Manages building placement, construction queuing, and the per-tick
@@ -31,6 +32,7 @@ public:
BeltSystem& belts,
std::function<EntityId()> allocateId,
std::function<void(int)> addBuildingBlocks,
std::function<void(const std::string&, QVector2D)> spawnShip,
std::mt19937& rng);
// -- Placement / demolish ------------------------------------------------
@@ -52,6 +54,7 @@ public:
void tickConstruction(Tick currentTick);
void tickBeltPull();
void tickProduction(Tick currentTick);
void tickShipyardProduction(Tick currentTick);
void tickBeltPush();
// -- Queries -------------------------------------------------------------
@@ -113,15 +116,18 @@ private:
const BuildingDef* findBuildingDef(BuildingType type) const;
const RecipeDef* findRecipe(const std::string& id, BuildingType type) const;
const ShipDef* findShipDef(const std::string& id) const;
void initBuffers(Building& b, const RecipeDef& recipe) const;
void initShipyardBuffers(Building& b) const;
std::vector<Port> computeInputPorts(const Building& b) const;
std::vector<Item> rollReprocessingOutput(const RecipeDef& recipe);
const GameConfig& m_config;
BeltSystem& m_belts;
std::function<EntityId()> m_allocateId;
std::function<void(int)> m_addBuildingBlocks;
std::mt19937& m_rng;
const GameConfig& m_config;
BeltSystem& m_belts;
std::function<EntityId()> m_allocateId;
std::function<void(int)> m_addBuildingBlocks;
std::function<void(const std::string&, QVector2D)> m_spawnShip;
std::mt19937& m_rng;
std::vector<Building> m_buildings;
std::deque<ConstructionSite> m_constructionQueue;

View File

@@ -29,6 +29,15 @@ Simulation::Simulation(const GameConfig& config, unsigned int seed)
m_beltSystem,
[this]() { return allocateId(); },
[this](int amount) { m_buildingBlocksStock += amount; },
[this](const std::string& id, QVector2D pos) {
const std::map<std::string, BlueprintState>::const_iterator it =
m_blueprintLevels.find(id);
if (it == m_blueprintLevels.end() || !it->second.unlocked)
{
return;
}
m_shipSystem->spawn(id, it->second.level, pos, /*isEnemy=*/false);
},
m_rng);
m_shipSystem = std::make_unique<ShipSystem>(config, [this]() { return allocateId(); });
m_scrapSystem = std::make_unique<ScrapSystem>([this]() { return allocateId(); });
@@ -70,6 +79,15 @@ void Simulation::reset(unsigned int seed)
m_beltSystem,
[this]() { return allocateId(); },
[this](int amount) { m_buildingBlocksStock += amount; },
[this](const std::string& id, QVector2D pos) {
const std::map<std::string, BlueprintState>::const_iterator it =
m_blueprintLevels.find(id);
if (it == m_blueprintLevels.end() || !it->second.unlocked)
{
return;
}
m_shipSystem->spawn(id, it->second.level, pos, /*isEnemy=*/false);
},
m_rng);
m_shipSystem = std::make_unique<ShipSystem>(m_config, [this]() { return allocateId(); });
m_scrapSystem = std::make_unique<ScrapSystem>([this]() { return allocateId(); });
@@ -105,6 +123,7 @@ void Simulation::tick()
m_buildingSystem->tickConstruction(m_currentTick);
m_buildingSystem->tickBeltPull(); // step 3
m_buildingSystem->tickProduction(m_currentTick); // step 4
m_buildingSystem->tickShipyardProduction(m_currentTick); // step 4b
m_buildingSystem->tickBeltPush(); // step 5
m_beltSystem.tick(); // step 6