One stratum of the Lattice: a grid of tiles plus the fixtures placed in it.
A level is generated by build/2 from a generator and a depth, and is thereafter
immutable except through unseal/1. Entities, items and the player live in Scriber.Game,
not here.
Tiles are held in a map keyed by {x, y}; a coordinate with no entry reads as :rock.
Every stratum is the same size, given by the struct's :width and :height.
What is in a stratum
Rooms joined by L-shaped corridors, and three fixtures:
- a console at
:console, in a room between the first and the last — where the gate is opened and shards are spent, seeScriber.Console - a gate at
:gate, in the last room placed — the way down, solid untilunseal/1turns it into a:stair - the player's spawn at
:spawn, the centre of the first room placed
A gate cannot be opened from here; Scriber.Console accepts the access code and the caller
then invokes unseal/1.
Example
{level, rng} = Scriber.Level.build(Cauldron2D.Rng.new(7), 1)
Scriber.Level.at(level, level.spawn)
#=> :floor
Summary
Functions
The tile at point. Any coordinate with no tile, inside the level or out, is :rock.
Generate a stratum at depth, returning {level, rng} with the generator advanced.
The centre tile of a room rectangle, rounding down on both axes.
The level with the door at point opened: a floor tile, sight passing through. Any other tile is left alone.
Every walkable tile, sorted, so the same level always yields the same list.
Whether point lets sight through, as Cauldron2D.Grid.Fov asks it.
Open the gate: set unsealed? and turn the :gate tile into a :stair.
Whether point can be stood on.
Types
@type rect() :: %{ x: integer(), y: integer(), width: pos_integer(), height: pos_integer() }
@type t() :: %Scriber.Level{ console: point() | nil, depth: pos_integer(), gate: point() | nil, height: pos_integer(), rooms: [rect()], spawn: point(), tiles: %{required(point()) => tile()}, unsealed?: boolean(), width: pos_integer() }
@type tile() :: :rock | :wall | :floor | :door | :console | :gate | :stair
Functions
The tile at point. Any coordinate with no tile, inside the level or out, is :rock.
@spec build(Cauldron2D.Rng.t(), pos_integer()) :: {t(), Cauldron2D.Rng.t()}
Generate a stratum at depth, returning {level, rng} with the generator advanced.
Rooms are placed by rejection: a fixed number of candidate rectangles are tried and each is
kept only if it clears every room already placed by one tile. Rooms are then joined in
placement order by L-shaped corridors, fixtures are placed, and every floor tile with no
neighbour gets a :wall. From stratum 2 the corridor tile at each room's
threshold is a :door, which blocks sight until it is opened by walking through it.
Generation never fails — a crowded draw yields fewer rooms, and
the degenerate case of no rooms at all yields a level with spawn: {1, 1} and no gate or
console.
The same rng and depth always produce the same level.
The centre tile of a room rectangle, rounding down on both axes.
The level with the door at point opened: a floor tile, sight passing through. Any other tile is left alone.
Every walkable tile, sorted, so the same level always yields the same list.
Whether point lets sight through, as Cauldron2D.Grid.Fov asks it.
True for everything except :rock, :wall and a :door not yet opened. This is not the
inverse of walkable?/2:
a sealed :gate blocks movement but not sight.
Open the gate: set unsealed? and turn the :gate tile into a :stair.
Call this once the access code has been accepted; see Scriber.Lattice.unseal/2. A level
with no gate is returned unchanged. Calling it twice is harmless.
Whether point can be stood on.
True for :floor, :door, :console and :stair. A :gate is walkable only while the
level is unsealed. :rock and :wall are never walkable.