143 lines
4.6 KiB
C++
143 lines
4.6 KiB
C++
#include "BuildingBuffers.h"
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <map>
|
|
|
|
#include "BuildingType.h"
|
|
#include "ItemType.h"
|
|
#include "ModulesConfig.h"
|
|
#include "ShipsConfig.h"
|
|
|
|
namespace
|
|
{
|
|
|
|
// Folds the output capacities one recipe implies into `caps`: twice each produced
|
|
// item's per-cycle amount (REQ-MAT-OUTPUT-BUFFER). A Reprocessing Plant rolls exactly
|
|
// one of its outputs per cycle (REQ-BLD-REPROCESSING), so its per-cycle amount for an
|
|
// item is that one outcome's amount rather than a sum over the entries.
|
|
//
|
|
// Where a cap is already present the larger wins, which is how an auto-recipe building
|
|
// unions the recipes of its type -- the same rule its input caps follow.
|
|
void addOutputCaps(std::map<ItemType, int>& caps, BuildingType type,
|
|
const RecipeDef& recipe)
|
|
{
|
|
std::map<ItemType, int> perCycle;
|
|
for (const RecipeOutput& out : recipe.outputs)
|
|
{
|
|
const ItemType item{out.item};
|
|
if (type == BuildingType::ReprocessingPlant)
|
|
{
|
|
perCycle[item] = std::max(perCycle[item], out.amount);
|
|
}
|
|
else
|
|
{
|
|
perCycle[item] += out.amount;
|
|
}
|
|
}
|
|
|
|
for (const std::pair<const ItemType, int>& entry : perCycle)
|
|
{
|
|
caps[entry.first] = std::max(caps[entry.first], 2 * entry.second);
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void initBuffers(Building& b, const RecipeDef& recipe)
|
|
{
|
|
b.inputBuffer.counts.clear();
|
|
b.inputBuffer.caps.clear();
|
|
for (const RecipeIngredient& ing : recipe.inputs)
|
|
{
|
|
const ItemType type{ing.item};
|
|
b.inputBuffer.counts[type] = 0;
|
|
b.inputBuffer.caps[type] = 2 * ing.amount;
|
|
}
|
|
|
|
b.outputBuffer.items.clear();
|
|
b.outputBuffer.caps.clear();
|
|
addOutputCaps(b.outputBuffer.caps, b.type, recipe);
|
|
}
|
|
|
|
void initShipyardBuffers(const GameConfig& config, Building& b)
|
|
{
|
|
b.inputBuffer.counts.clear();
|
|
b.inputBuffer.caps.clear();
|
|
// A shipyard spawns a ship rather than producing items, so it holds no output
|
|
// buffer at all (REQ-MAT-OUTPUT-BUFFER, REQ-BLD-SHIPYARD).
|
|
b.outputBuffer.items.clear();
|
|
b.outputBuffer.caps.clear();
|
|
const ShipDef* def = config.ships.findShipDef(b.recipeId);
|
|
if (!def)
|
|
{
|
|
return;
|
|
}
|
|
for (const RecipeIngredient& ing : def->schematic.materials)
|
|
{
|
|
const ItemType type{ing.item};
|
|
b.inputBuffer.counts[type] = 0;
|
|
b.inputBuffer.caps[type] = 2 * ing.amount;
|
|
}
|
|
if (b.shipLayout.has_value())
|
|
{
|
|
for (const PlacedModule& pm : b.shipLayout->placedModules)
|
|
{
|
|
const ModuleDef* modDef = config.modules.findModuleDef(pm.moduleId);
|
|
if (!modDef)
|
|
{
|
|
continue;
|
|
}
|
|
for (const RecipeIngredient& ing : modDef->materials)
|
|
{
|
|
const ItemType type{ing.item};
|
|
b.inputBuffer.counts.try_emplace(type, 0);
|
|
b.inputBuffer.caps[type] += 2 * ing.amount;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void initSalvageBayBuffer(const GameConfig& config, Building& b)
|
|
{
|
|
// Salvage Bay has no recipe-driven buffer; scrap is the only thing it ever holds,
|
|
// and that single buffer's holding size for ship drop-off is config-defined
|
|
// (REQ-BLD-SALVAGE-BAY).
|
|
b.outputBuffer.items.clear();
|
|
b.outputBuffer.caps.clear();
|
|
const BuildingDef* def = config.buildings.findBuildingDef(BuildingType::SalvageBay);
|
|
b.outputBuffer.caps[ItemType{"scrap"}] =
|
|
(def && def->outputBufferCapacity) ? *def->outputBufferCapacity : 0;
|
|
}
|
|
|
|
|
|
void reregisterBeltTile(BeltSystem& belts, const GameConfig& config,
|
|
const Building& building,
|
|
const std::vector<ItemType>& splitterFilterA,
|
|
const std::vector<ItemType>& splitterFilterB)
|
|
{
|
|
switch (building.type)
|
|
{
|
|
case BuildingType::Belt:
|
|
belts.placeBelt(building.anchor, building.rotation);
|
|
break;
|
|
case BuildingType::Splitter:
|
|
assert(building.outputPorts.size() >= 2);
|
|
belts.placeSplitter(building.anchor,
|
|
building.outputPorts[0].direction,
|
|
building.outputPorts[1].direction);
|
|
belts.setSplitterFilters(building.anchor, splitterFilterA, splitterFilterB);
|
|
break;
|
|
case BuildingType::TunnelEntry:
|
|
belts.placeTunnelEntry(building.anchor, building.rotation,
|
|
config.world.tunnelMaxDistance_tiles);
|
|
break;
|
|
case BuildingType::TunnelExit:
|
|
belts.placeTunnelExit(building.anchor, building.rotation);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|