implement belt system
This commit is contained in:
117
src/lib/sim/BeltSystem.h
Normal file
117
src/lib/sim/BeltSystem.h
Normal file
@@ -0,0 +1,117 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <QPoint>
|
||||
#include <QPointF>
|
||||
#include <QRect>
|
||||
|
||||
#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 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<ItemType>& filterA,
|
||||
const std::vector<ItemType>& filterB);
|
||||
|
||||
// -- 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 port.tile entering from the opposite side.
|
||||
// Returns false if the tile is not a belt, direction mismatches, or tile full.
|
||||
bool tryPutItem(Port port, Item item);
|
||||
|
||||
// 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<Item> tryTakeItem(Port port);
|
||||
|
||||
// -- Maintenance ---------------------------------------------------------
|
||||
void clearTiles(const std::vector<QPoint>& tiles); // REQ-UI-BELT-CLEAR
|
||||
void tick();
|
||||
|
||||
// -- Rendering -----------------------------------------------------------
|
||||
void forEachVisualItem(QRect viewportTiles,
|
||||
std::function<void(VisualItem)> visit) const;
|
||||
|
||||
private:
|
||||
void advanceProgress();
|
||||
void moveItemsToNextTile();
|
||||
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);
|
||||
|
||||
static std::pair<int, int> 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;
|
||||
std::optional<BeltItemSlot> front; // higher progress; closer to output
|
||||
std::optional<BeltItemSlot> back; // lower progress; closer to input
|
||||
};
|
||||
|
||||
struct SplitterTile
|
||||
{
|
||||
Rotation outputA;
|
||||
Rotation outputB;
|
||||
std::vector<ItemType> filterA; // empty = accept all
|
||||
std::vector<ItemType> filterB;
|
||||
bool nextOutputIsA; // alternation state
|
||||
std::optional<Item> heldItem; // item buffered waiting to exit
|
||||
};
|
||||
|
||||
double m_progressPerTick; // beltSpeedTilesPerSecond / kTickRateHz
|
||||
|
||||
std::map<std::pair<int, int>, BeltTile> m_belts;
|
||||
std::map<std::pair<int, int>, SplitterTile> m_splitters;
|
||||
};
|
||||
Reference in New Issue
Block a user