#include "BuildingBuffers.h" #include #include #include #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& caps, BuildingType type, const RecipeDef& recipe) { std::map 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& 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& splitterFilterA, const std::vector& 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; } }