#pragma once #include #include #include #include #include #include #include #include #include "Item.h" #include "ItemType.h" #include "Port.h" #include "Rotation.h" // Carries item type and fractional world position for the renderer. // worldPos is in tile units (1 tile = 1.0 unit); origin matches tile coords. struct VisualItem { ItemType type; QPointF worldPos; }; // Isolated belt-and-splitter transport layer. See architecture.md §Belt Subsystem. // // Buildings interact only through tryPutItem / tryTakeItem. // Rendering reads only through forEachVisualItem. // No other system inspects tile contents. class BeltSystem { public: explicit BeltSystem(double beltSpeedTilesPerSecond); // -- Placement ----------------------------------------------------------- // Register a new belt tile. Any items already on this tile are cleared. void placeBelt(QPoint tile, Rotation direction); // Register a new tunnel entry tile. void placeTunnelEntry(QPoint tile, Rotation direction, int maxDistance); // Register a new tunnel exit tile. void placeTunnelExit(QPoint tile, Rotation direction); // Register a new splitter tile. outputA and outputB are the two exit // directions (e.g. West and East for a default-rotation splitter). // Items entering from any adjacent belt whose direction points into this // tile are held and routed to one of the two outputs. void placeSplitter(QPoint tile, Rotation outputA, Rotation outputB); // Remove a belt or splitter tile (on demolish). Items are discarded. void removeTile(QPoint tile); // -- Splitter filter configuration (REQ-BLD-SPLITTER) ------------------- // filterA / filterB: empty means "accept all". void setSplitterFilters(QPoint tile, const std::vector& filterA, const std::vector& filterB); struct SplitterInfo { Rotation outputA; Rotation outputB; std::vector filterA; std::vector filterB; }; std::optional getSplitterInfo(QPoint tile) const; // -- Port interface (buildings <-> belts) -------------------------------- // port.tile = the belt tile adjacent to the building // port.direction = direction items flow on that tile // // tryPutItem: place item onto tile. // Returns false if the tile is not a belt/splitter, or tile full. // fromDir: travel direction of the item (used for splitter animation). bool tryPutItem(QPoint tile, Item item, Rotation fromDir = Rotation::West); // tryTakeItem: remove and return the leading item from port.tile. // Returns nullopt if tile is not a belt, direction mismatches, or tile empty. std::optional tryTakeItem(Port port); // peekItem: return the type of the leading item without removing it. // Returns nullopt if tile is not a belt, direction mismatches, or tile empty. std::optional peekItem(Port port) const; // -- Maintenance --------------------------------------------------------- void clearTiles(const std::vector& tiles); // REQ-UI-BELT-CLEAR void tick(); // -- Rendering ----------------------------------------------------------- void forEachVisualItem(QRect viewportTiles, std::function visit) const; private: void advanceProgress(); void advanceTunnelProgress(); void moveItemsToNextTile(); void moveTunnelItems(); void routeSplitterItems(); // Place item into back slot of an existing belt tile at progress 0. // Returns false if tile is not a belt or is full. bool tryPlaceOnBelt(QPoint tile, Item item); // Push an item to any tile type (belt, splitter, tunnel entry, tunnel exit). bool tryPushToTile(QPoint dest, Item item, Rotation fromDir); void reevaluateTunnelPairing(); static std::pair key(QPoint tile); static QPoint adjacentTile(QPoint tile, Rotation dir); // Returns the world-space centre of a slot given tile origin and progress. static QPointF slotWorldPos(QPoint tile, Rotation dir, double progress); struct BeltItemSlot { Item item; double progress; // [0.0, 1.0]: 0 = just entered, 1 = at output edge }; struct BeltTile { Rotation direction; // front (highest progress) at index 0; back (just entered) at end. Max 4. std::vector itemSlots; }; struct SplitterTile { Rotation outputA; Rotation outputB; std::vector filterA; // empty = accept all std::vector filterB; bool nextOutputIsA; // alternation state // Unassigned items: [0] = routing candidate (higher progress, caps at 0.5). Max 2. std::vector back; std::vector backDir; // feeding belt direction, parallel to back std::optional frontA; // progress [0, 1]; routed to outputA std::optional frontB; // progress [0, 1]; routed to outputB }; struct TunnelEntryTile { Rotation direction; int maxDistance; // front (highest progress) at index 0; back at end. Max 4. std::vector itemSlots; }; struct TunnelExitTile { Rotation direction; // front (highest progress) at index 0; back at end. Max 4. std::vector itemSlots; }; struct TunnelTransitItem { Item item; double progress; }; struct TunnelLink { QPoint entryTile; QPoint exitTile; double length; std::vector items; // front (highest progress) to back }; double m_progressPerTick; // beltSpeedTilesPerSecond / kTickRateHz std::map, BeltTile> m_belts; std::map, SplitterTile> m_splitters; std::map, TunnelEntryTile> m_tunnelEntries; std::map, TunnelExitTile> m_tunnelExits; std::vector m_tunnelLinks; };