#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 cycle yields exactly one output group // (REQ-MAT-OUTPUT-GROUP), so an item's per-cycle amount is the largest total any single // group produces of it -- summed within a group, whose items come together, and taken at // its maximum across groups, of which only one ever happens. // // Where a cap is already present the larger wins, which is how a cap unions across the // recipes it could be sized over -- the same rule the input caps follow. void addOutputCaps(std::map& caps, const RecipeDef& recipe) { std::map perCycle; for (const RecipeOutputGroup& group : recipe.outputGroups) { std::map inGroup; for (const RecipeOutput& out : group.items) { inGroup[ItemType{out.item}] += out.amount; } for (const std::pair& entry : inGroup) { perCycle[entry.first] = std::max(perCycle[entry.first], entry.second); } } 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, 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; } }