diff --git a/src/dungeon.lua b/src/dungeon.lua new file mode 100644 index 0000000..1d598c3 --- /dev/null +++ b/src/dungeon.lua @@ -0,0 +1,60 @@ +-- Owns every floor. Each level's layout is derived from the world seed and its +-- depth alone, through a private rng that never touches the world rng stream, so +-- a floor generates identically no matter when it is first entered. Generated +-- floors are cached, which is what makes revisiting one restore its exact state. + +local Rng = require("rng") +local Builder = require("builder") +local Level = require("level") + +local Dungeon = {} +Dungeon.__index = Dungeon + +local SALT = 0x9E3779B97F4A7C15 + +function Dungeon.new(worldSeed, options) + return setmetatable({ + worldSeed = worldSeed, + options = options or {}, + levels = {}, + }, Dungeon) +end + +function Dungeon:levelSeed(depth) + return math.tointeger(self.worldSeed ~ (depth * SALT)) or 0 +end + +function Dungeon:get(depth) + if not self.levels[depth] then + local rng = Rng.new(self:levelSeed(depth)) + self.levels[depth] = Builder.build(depth, rng, self.options) + end + return self.levels[depth] +end + +function Dungeon:isGenerated(depth) + return self.levels[depth] ~= nil +end + +function Dungeon:toData() + local depths = {} + for depth in pairs(self.levels) do + depths[#depths + 1] = depth + end + table.sort(depths) + local stored = {} + for _, depth in ipairs(depths) do + stored[#stored + 1] = { depth = depth, level = self.levels[depth]:toData() } + end + return { worldSeed = self.worldSeed, options = self.options, levels = stored } +end + +function Dungeon.fromData(snapshot) + local dungeon = Dungeon.new(snapshot.worldSeed, snapshot.options) + for _, entry in ipairs(snapshot.levels) do + dungeon.levels[entry.depth] = Level.fromData(entry.level) + end + return dungeon +end + +return Dungeon