Files
dota_factory/src/lib/sim/BuildingBuffers.cpp

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;
}
}