#pragma once #include #include #include #include #include #include #include "BuildingType.h" #include "BuildingId.h" #include "entt/entity/entity.hpp" #include "BeltSlot.h" #include "Item.h" #include "ItemType.h" #include "Port.h" #include "Rotation.h" #include "ShipLayout.h" #include "Tick.h" // Per-material input buffer for a production building. struct InputBuffer { std::map counts; // current item counts per material std::map caps; // max items per material (2× per-cycle requirement) }; // Per-material output buffer for a production building. The items are held in one // production-ordered queue -- that is the order they leave at the output port // (REQ-MAT-OUTPUT-EMERGE) -- while the capacity is per item type, so one item's backlog // never occupies another's room (REQ-MAT-OUTPUT-BUFFER). struct OutputBuffer { std::vector items; // production order; feeds the output belt std::map caps; // max items per material (2x its per-cycle amount) }; // Active production cycle for a building. struct Production { std::string recipeId; Tick completesAt = 0; std::vector chosenOutputs; // resolved at cycle start (reprocessing rolls here) }; // A building placed on the map that is still under construction. // Occupies tiles but does not produce. struct ConstructionSite { BuildingId id = kInvalidBuildingId; QPoint anchor; // top-left of body bounding box QSize footprint; std::vector bodyCells; // absolute world tile coordinates Rotation rotation = Rotation::East; BuildingType type = BuildingType::Miner; std::string recipeId; // may be configured before completion Tick completesAt = 0; // 0 = queued but not yet started std::optional shipLayout; // Splitter output filters configured before completion (REQ-BLD-SITE-CONFIG). // Empty = accept all; applied to the BeltSystem when the splitter is built. std::vector splitterFilterA; std::vector splitterFilterB; }; // A fully constructed, operational building. struct Building { BuildingId id = kInvalidBuildingId; QPoint anchor; // top-left of body bounding box QSize footprint; Rotation rotation = Rotation::East; BuildingType type = BuildingType::Miner; std::string recipeId; // empty = none selected InputBuffer inputBuffer; OutputBuffer outputBuffer; std::optional production; // Items currently emerging from each output port on its virtual output belt // (REQ-MAT-OUTPUT-EMERGE); one lane per output port, parallel to outputPorts. // Each lane holds slots at progress [0.5, 1.0], front (highest progress) first. // An emerging item still counts as residing in the output buffer until it hands // off onto a real belt at progress 1.0. std::vector> emergingItems; // Total items held on the output side: buffered plus still-emerging. The // output-buffer capacity rule (REQ-MAT-OUTPUT-BUFFER) counts emerging items, // since they have not yet left the building. int getOutputItemCount() const { int count = static_cast(outputBuffer.items.size()); for (const std::vector& lane : emergingItems) { count += static_cast(lane.size()); } return count; } // The same over one material, which is what its own capacity is measured against // (REQ-MAT-OUTPUT-BUFFER). int getOutputItemCount(const ItemType& type) const { int count = 0; for (const Item& item : outputBuffer.items) { if (item.type == type) { ++count; } } for (const std::vector& lane : emergingItems) { for (const BeltItemSlot& slot : lane) { if (slot.item.type == type) { ++count; } } } return count; } // Items currently travelling inward on each input port's virtual input belt // (REQ-MAT-INPUT-INTAKE); one lane per input port, parallel to inputPorts. Each // lane holds slots at progress [0.0, 0.5], front (highest progress) first. An // in-transit item has reserved a slot in its per-material input buffer but is // not yet consumable — it enters the buffer only on reaching progress 0.5. std::vector> incomingItems; // Buffered plus in-transit count of one input material. The acceptance/space // test (REQ-MAT-INPUT-PORTS, REQ-MAT-INPUT-INTAKE) counts in-transit items, so // buffered + reserved never exceeds the material's cap (REQ-MAT-INPUT-BUFFER). int pendingInputCount(const ItemType& type) const { int count = 0; const std::map::const_iterator it = inputBuffer.counts.find(type); if (it != inputBuffer.counts.end()) { count = it->second; } for (const std::vector& lane : incomingItems) { for (const BeltItemSlot& slot : lane) { if (slot.item.type == type) { ++count; } } } return count; } // Pre-computed from surface mask at placement; in absolute world coordinates. std::vector bodyCells; std::vector outputPorts; std::vector inputPorts; // perimeter tiles (minus output-port tiles), // direction pointing INTO building // Module layout for shipyards (REQ-MOD-LAYOUT). std::optional shipLayout; // True while this building sits in the deconstruction queue (REQ-BLD-DECON-QUEUE). // A queued building stops operating immediately (all tick loops skip it) but keeps // occupying its tiles until its deconstruction completes. bool queuedForDeconstruction = false; };