ReadonlywidthReadonlyheightReadonlyfeatureReadonlyuint8ReadonlyroadReadonlyriverPer-cell bitmask of incoming river edges (bit e = a river enters across edge e). Supports confluences — multiple tributaries entering one cell. The cell byte keeps only the primary (lowest) incoming for compatibility.
ReadonlyfeatureReadonlywaterPer-cell water surface elevation (elevation index, same units as getElevation).
Populated by computeWaterSurfaces. World-space Y = getWaterSurface(col, row) * elevScale.
Initialized to 0 (sea level). Non-water cells retain 0 and should not be queried.
ReadonlyshorePer-cell hex distance to the nearest land-adjacent cell of the same water
body (0 = touches land, 255 = far open water / capped). Populated by
computeWaterSurfaces alongside surfaces. Drives the shallow→deep color
gradient in the water surface geometry. Non-water cells retain 0.
ReadonlycellSparse per-cell metadata channel: arbitrary JSON-serializable game data
(ownership, yields, quest flags, grazing state…) keyed by flat cell index
(row * width + col). Rides through save/load and .hexpack — values must
survive JSON.stringify/JSON.parse round-trips (no functions, no class
instances, no cycles). Prefer the getCellData/setCellData accessors;
the raw map is exposed for serializers and bulk iteration.
Total number of cells (width × height).
See the field doc: changes whenever map-wide river rendering could.
Returns true if (col, row) is within the map boundaries.
Returns the feature density level (0–3) for the given cell and scatter layer. Returns 0 if the layer index is out of range or no feature layers were allocated.
Sets the feature density level for the given cell and scatter layer.
value is clamped to 0–3 (2-bit). No-op if the layer index is out of range.
Returns the metadata value stored under key for the cell, or undefined
if the cell has no entry for that key. Values are arbitrary
JSON-serializable game data — see cellData.
Stores a metadata value under key for the cell. Passing undefined
deletes the key (and drops the cell's record entirely once its last key is
removed, keeping the store sparse). Values must be JSON-serializable —
they ride through save/load and .hexpack via JSON.stringify.
No-op for out-of-bounds cells.
Returns the cell's full metadata record, or undefined if the cell has
none. The record is the live object — treat it as read-only and mutate
through setCellData so sparse-store invariants hold.
Returns true if the cell has any metadata entries.
Removes all metadata entries for the cell.
Call after writing river, terrain, or elevation data through the raw arrays.
Returns the raw flag bitmask for a cell. Use hasFlag for individual flag checks.
Sets one or more flag bits on a cell (OR).
Clears one or more flag bits on a cell (AND NOT).
Returns true if all bits in flag are set on the cell.
Returns true if the cell has any river data (incoming or outgoing).
Returns true if at least one river flows into this cell from a neighbour.
Returns true if a river flows out of this cell to a neighbour.
Returns true if the cell is a river source or terminus (has exactly one of incoming/outgoing).
Returns the PRIMARY incoming river edge index (0–5), or -1 if none.
With multiple tributaries this is the lowest-indexed incoming edge; use
hasRiverIncomingThroughEdge / getIncomingRiverMask for the full set.
Returns the outgoing river edge index (0–5), or -1 if none.
Bitmask of ALL incoming river edges (bit e = edge e).
Returns true if a river flows into this cell across the given edge.
Set the outgoing river direction for this cell (does NOT update the neighbour).
ADD an incoming river through the given edge (does NOT update the neighbour). Multiple incoming edges per cell are supported (confluences); adding an edge never disturbs existing ones. The primary incoming direction is kept as the lowest set edge.
Remove one incoming river edge, keeping any others (does NOT update the neighbour).
Clear the outgoing river direction, keeping incoming tributaries (does NOT update the neighbour).
Clear all river data for this cell.
Returns true if a road passes through the given edge (0–5) of the cell.
Returns true if the cell has a road on any edge.
Set or clear a road through the given edge. Does NOT update the neighbour cell.
The cell on the other side of the given edge, plus that cell's edge index
for the shared edge. Returns null when the neighbour is off-map.
Needs the layout orientation because edge indices are orientation-specific.
Set or clear a road through an edge on BOTH cells that share it — road rendering requires the half-edges to agree, and keeping that invariant by hand is error-prone. Returns the affected cells (one if the neighbour is off-map) so callers can mark them dirty.
Start a transaction for a multi-event interaction (e.g. a paint stroke):
mutate through the returned MapTransaction as events arrive, then call
commit() once to get a replayable MapEdit for undo/redo.
Mutations apply to the map immediately; the transaction only records
before/after snapshots of the touched cells.
Run a batch of edits as a single undoable unit.
Returns a MapEdit; pass its cells to
ChunkManager.markDirtyCells() and keep it for undo/redo.
Iterates over every cell in row-major order, calling cb(col, row) for each.
Zero out all cell data so the map can be regenerated in place.
Returns the pre-computed water surface elevation for a cell (elevation index units).
Multiply by your elevScale to get world-space Y.
Returns 0 for non-water cells and before computeWaterSurfaces has been called.
Hex distance to the nearest land-adjacent cell of the same water body
(0 = shore cell, larger = deeper open water, capped at 255).
Populated by computeWaterSurfaces. Returns 0 for non-water cells.
BFS flood-fill that finds every connected water body and records its surface
elevation in waterSurfaces plus each cell's distance-to-shore in
shoreDistances. The surface is max(0, maxFloorElevation + 1):
one step above the highest floor cell, clamped so ocean bodies (floor ≤ −1)
always surface at 0. Elevated lakes (floor ≥ 0) surface one step above their floor.
Called automatically by ChunkManager.update() before any dirty chunk rebuilds,
and by generators / deserializers after map data is written. Call it yourself
after bulk edits if you need getWaterSurface to be accurate before the next
ChunkManager.update().
Predicate that returns true for liquid terrain indices.
Defaults to the built-in water terrain (index 5). Pass a custom predicate
when using additional liquid terrain types.
OptionaldirtyRegions: readonly { colStart: number; colEnd: number; rowStart: number; rowEnd: number }[]When provided, only water bodies intersecting these
cell-coordinate regions are re-flooded; everything else keeps its current
values. Regions MUST extend at least one cell beyond the edited cells so
bodies merely adjacent to an edit are re-seeded (ChunkManager passes
its dirty chunk bounds expanded by one). Requires surfaces to have been
fully computed once before.
BFS from (col, row) that returns all cells in the same connected water body.
Returns an empty array if the starting cell is not a water cell.
Useful for editor tools that need to paint a whole lake consistently.
Row-major rectangular hex map backed by a flat ArrayBuffer. Coordinates are offset (col, row) internally; all public API accepts either offset or cube (HexCoord) coordinates.
Memory layout per cell (CELL_STRIDE bytes): [terrain: Uint8][elevation: Int8][flags: Uint8][reserved: Uint8]