-- Rift crossings: a rare, shimmering tear in a floor from depth six on (see -- builder.lua's placement, gated behind a carried rift shard) that a player -- can step through into a short, self-contained pocket floor - generated -- once from the same world seed as everything else (see Branches.build) at -- a deliberately harsher effective depth than the floor the rift itself sits -- on, so what waits behind it is always a genuine step up in danger and in -- reward. Unlike horde.lua's arena (a one-way trial that replaces the run's -- level outright), a rift crossing is round-trip: Game:enterRift remembers -- exactly where the player stood before stepping through, and Game:exitRift -- restores it to the byte, so nothing about the main dungeon left behind is -- ever perturbed by the detour. local Rng = require("rng") local Builder = require("builder") local Level = require("level") local Branches = {} -- How much harder a rift's own pocket floor is generated than the main-track -- depth its entrance sits on - fixed rather than randomised, so a rift found -- at a given depth always promises the same order of danger and reward. Branches.DEPTH_BONUS = 4 -- Distinguishes a rift's own private rng stream from both the main dungeon's -- per-depth stream (Dungeon:levelSeed's own salt) and every other salted -- stream in the codebase, so discovering a rift can never perturb - or be -- perturbed by - any of them. local SALT = 0xC2B2AE3D27D4EB4F -- The deterministic seed a rift found on `depth` always builds its pocket -- floor from, a pure function of the world seed and the origin depth alone - -- never the main dungeon's own per-depth rng stream - so the exact same rift -- always opens onto the exact same pocket floor no matter how many times (or -- in what order) the run's other floors happen to be visited first. function Branches.seedFor(worldSeed, depth) return math.tointeger(worldSeed ~ (depth * SALT)) or 0 end -- Builds the pocket floor a rift found at `depth` opens onto: an ordinary -- Builder.build floor, just generated Branches.DEPTH_BONUS deeper than the -- rift's own origin so its monsters and loot are drawn from a rung the -- player has not reached yet on the main track. `options` is the run's own -- dungeon options (mutators, class, graves), copied rather than mutated, with -- maxDepth pinned one short of the pocket's own depth - short, not equal or -- past it, so neither of Builder.build's two maxDepth-gated passes ever fire -- in a pocket floor: no chasm (there is deliberately no lower pocket floor to -- fall into) and, far more importantly, no Amulet of Warren - a rift is a -- richer fight, never a shortcut past the real dungeon's own win condition. function Branches.build(worldSeed, depth, options) local pocketDepth = depth + Branches.DEPTH_BONUS local rng = Rng.new(Branches.seedFor(worldSeed, depth)) local pocketOptions = {} for k, v in pairs(options or {}) do pocketOptions[k] = v end pocketOptions.maxDepth = pocketDepth - 1 return Builder.build(pocketDepth, rng, pocketOptions) end -- Every rift's own pocket floor, serialized exactly like Dungeon:toData -- serializes the main track: a depth-sorted array of {depth, level} pairs, so -- a save taken mid-crossing (or with a rift merely discovered and never -- entered) restores every pocket floor already generated byte-for-byte, dead -- monsters and all - and stores nothing extra for a rift a run never stepped -- through at all. function Branches.levelsToData(riftLevels) local depths = {} for depth in pairs(riftLevels or {}) do depths[#depths + 1] = depth end table.sort(depths) local stored = {} for _, depth in ipairs(depths) do stored[#stored + 1] = { depth = depth, level = riftLevels[depth]:toData() } end return stored end function Branches.levelsFromData(stored) local riftLevels = {} for _, entry in ipairs(stored or {}) do riftLevels[entry.depth] = Level.fromData(entry.level) end return riftLevels end return Branches