diff --git a/core/include/vf/Rail.h b/core/include/vf/Rail.h new file mode 100644 index 0000000..6984b7d --- /dev/null +++ b/core/include/vf/Rail.h @@ -0,0 +1,103 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "World.h" +#include "Circuit.h" +#include "Block.h" +#include "Math.h" + +namespace vf { + +constexpr int poweredRailReach = 8; + +enum class RailDir : uint8_t { + North, // -Z + South, // +Z + East, // +X + West, // -X +}; + +enum class RailShape : uint8_t { + NorthSouth, + EastWest, + AscendNorth, + AscendSouth, + AscendEast, + AscendWest, + CurveSouthEast, + CurveSouthWest, + CurveNorthWest, + CurveNorthEast, +}; + +struct RailStep { + Vec3i nextPos; + RailDir exitDir = RailDir::North; + bool valid = false; +}; + +inline bool isRailBlock(Block b) { + return b == Block::Rail || b == Block::PoweredRail || + b == Block::DetectorRail || b == Block::ActivatorRail; +} + +RailDir opposite(RailDir dir); +Vec3i railDelta(RailDir dir); +bool isAscending(RailShape shape); +RailDir ascendUpDir(RailShape shape); +std::pair railEnds(RailShape shape); + +// Auto-connects a rail to its neighbours. Curves are only produced for plain +// rails (Block::Rail); powered/detector/activator rails stay straight or +// ascending. When `switched` is true a straight-capable rail that also has a +// perpendicular neighbour takes the turn instead of going straight, modelling a +// powered junction. +RailShape inferRailShape(const World& world, const Vec3i& pos, bool switched = false); + +// Follows the track one cell from `pos` given an entry heading. Handles curves +// (heading rotates) and slopes (steps up onto an ascending rail or down onto a +// descending one). `valid` is false when the track dead-ends: a derail. +RailStep advanceOnRail(const World& world, const Vec3i& pos, RailDir moveDir, bool switched = false); + +// Set of powered-rail cells that end up energised: every rail directly powered +// by redstone plus connected powered rails within `poweredRailReach` along the +// straight line in each direction. +std::unordered_set activePoweredRails( + const World& world, + const std::vector& poweredRails, + const std::function& directlyPowered); + +std::unordered_set activePoweredRails( + const World& world, + const std::vector& poweredRails, + const RedstoneCircuit& circuit); + +// Adjacency graph over a set of rail cells. Each node links to the rail cells +// its two ends connect to, following curves and slopes. Backs cart routing: +// distance queries, one-shot track tracing and connectivity checks. +class RailGraph { +public: + void build(const World& world, const std::vector& railCells, bool switched = false); + + bool contains(const Vec3i& pos) const { return adjacency.find(pos) != adjacency.end(); } + std::vector neighbours(const Vec3i& pos) const; + std::size_t size() const { return adjacency.size(); } + + // Walks the track from `start` heading `dir`, returning the ordered cells it + // visits (including `start`) until a derail, a revisit (closed loop) or + // `maxSteps` cells. + std::vector trace(const World& world, const Vec3i& start, RailDir dir, int maxSteps) const; + + // Cell count of the shortest connection between two rails, or -1 when they + // are not connected within the graph. + int distance(const Vec3i& a, const Vec3i& b) const; + +private: + std::unordered_map, Vec3iHash> adjacency; +}; + +} // namespace vf