Skip to content
Back to skills

Tinyworld Ghost World Gen

ASecurity

Use when changing ghost board generation, path / road / river continuity, deterministic regen, edge connections, or anything that runs inside makeGhostWorld in tiny-world-builder.html.

  • 1,587 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 26, 2026
devopsgo

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add jasonkneen/tiny-world-builder --skill tinyworld-ghost-world-gen --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Tinyworld Ghost World Gen?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Tinyworld Ghost World Gen
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/jasonkneen-tinyworld-ghost-world-gen/badge)](https://www.skillsdirectory.com/skills/jasonkneen-tinyworld-ghost-world-gen)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: tinyworld-ghost-world-gen
description: Use when changing ghost board generation, path / road / river continuity, deterministic regen, edge connections, or anything that runs inside makeGhostWorld in tiny-world-builder.html.
---

# Tiny World Ghost World Generation

`makeGhostWorld(boardX, boardZ)` produces the contents of a single
non-editable ghost board. It must be:

- **Deterministic.** Same `(boardX, boardZ)` always yields the same
  cells. Cached in `ghostBoardCells` keyed by `'bx,bz'`. Panning away
  and back must regenerate identically (we rely on this for the
  sticky-reveal jigsaw — if content shifted between regens the
  reveal cache would lie).
- **Connection-aware.** Paths and rivers must line up across board
  edges. The user should be able to walk a road from one ghost board
  into the next.

## The seeded RNG

`ghostHash(a, b, salt)` is a tiny mulberry-style 32-bit mix used for
all board-level decisions. Per-cell randomness goes through the older
`cellRand(x, z, salt)` but always with **global** coords
`(boardX * GRID + x, boardZ * GRID + z, salt)`, never local coords.
That guarantees a given world cell renders identically regardless of
which board it was sourced from.

## Connection rubric

- **Horizontal path Z is a function of `boardZ` only**
  (`pathZForRow(boardZ)`). Every board on that world row either has
  the path at the same Z or has no path on that row.
- **Vertical path X is a function of `boardX` only**
  (`pathXForCol(boardX)`). Every board in the column shares the same
  vertical-path X.
- Where horizontal and vertical paths coincide inside a board you
  get a crossroads "for free".
- **Rivers** are column-shared via `riverXForCol(boardX)` so they
  flow continuously down a column. A river that would collide with
  a vertical path is nudged one column over.
- **Bridges**: where a river crosses a horizontal path, drop a
  `kind: 'bridge'` tile so the path stays walkable.

Rough density knobs (tweak in the helpers themselves):

- ~30 % of world rows have no horizontal path
  (`(h % 100) < 30 → -1`).
- ~35 % of world cols have no vertical path.
- ~88 % of world cols have no river (so ~12 % do).

## Cross-board neighbours

The visual tile renderer needs to know what's on the *other side* of a
board edge — otherwise a path that exits east terminates with a stub
end-piece. The neighbour helpers handle this:

- `ghostCellAt(boardX, boardZ, x, z)` resolves any local coord. If
  `x` / `z` are out of `[0, GRID)` it walks into the adjacent board and
  pulls from its `makeGhostWorld(...)` result. If the wrap lands on
  board `(0, 0)` it reads from the home `world[][]` instead so user
  edits on the home board's edges feed the ghost adjacency too.
- `getGhostNeighbors(cells, x, z, prop, value, boardX, boardZ)` and
  `getGhostTerrainNeighbors(cells, x, z, boardX, boardZ)` use
  `ghostCellAt` so an edge tile sees the real neighbour, not `null`.

Always pass `boardX, boardZ` when calling these from inside
`buildGhostBoard`.

## Cells layout

The cell schema must match the home board so `setCell` /
`renderCellObject` work on ghost cells too. Always include the full
shape:

```
{ terrain, kind, floors, buildingType, fenceSide, extras }
```

Omitting fields (especially `extras: []`) caused subtle bugs in the
old generator when ghost data flowed through helpers that assumed the
full shape.

## Blank ghost boards

The Generate dialog can disable outside auto-fill. That path sets
`ghostBoardsBlank = true`, clears existing ghost boards, and lets
`makeGhostWorld(...)` return deterministic blank grass cells for every
off-home board. Keep this as an early return inside `makeGhostWorld` so
panning remains cheap and no generated scenery appears outside the
current generated board.

## Don't

- Don't make paths or rivers depend on both `boardX` and `boardZ` —
  that breaks edge continuity.
- Don't seed decoration with local coords. A tree at local (3,4) of
  board (1,2) must be identical to that same world cell reached from
  any other angle.
- Don't mutate `ghostBoardCells` from anywhere except
  `makeGhostWorld`. The reveal system relies on stable references.

## User overrides (exceptions)

Anything the user builds / erases on a ghost board is an *override* and
must survive map regeneration:

- The override lives in `world[gx][gz]` at **global** coords. There is
  no separate override map — `world[][]` is the single source of truth
  for user-built cells whether they sit on the home board or far out
  in ghost territory.
- `applyToolToCell` copies the generated ghost cell into
  `world[gx][gz]`, calls `removeGhostCellMesh(boardX, boardZ, lx, lz)`
  to strip the ghost board's mesh for that cell, then runs `applyTool`
  which calls `setCell` to render the home cellMesh at the global
  coord. The home cellMesh and the ghost board never both render at
  the same world position.
- `ghostCellAt(boardX, boardZ, x, z)` prefers `world[gx][gz]` over
  `makeGhostWorld(boardX, boardZ)[x][z]`. That keeps cross-board
  adjacency (paths joining, rivers continuing) correct even when the
  user has edited the joining tile.
- `buildGhostBoard` skips any local cell whose global coord exists in
  `world[][]` — those are owned by `setCell` / `cellMeshes`.

## Persistence

- `saveState` walks `Object.keys(world)` so every populated cell — home
  *and* far-flung overrides — is serialised, regardless of how far the
  user has panned.
- `applyState` restores both home cells (via the staggered drop-in
  loop) and out-of-home overrides (via a second `setCell` pass with
  `animate: false, forceTile: true`). The ghost boards regenerate
  deterministically around them and `ghostCellAt` + `buildGhostBoard`
  ensure overrides paint on top.

The contract: if a user can place / erase it, the world reloads with
that exact change re-applied, anywhere on the map, and the rest of the
ghost world regenerates around it.

## Validation

- Pan east across several boards — horizontal paths should run as a
  continuous strip; vertical paths and rivers should cross perfectly
  perpendicular.
- Pan a known board out of the preload radius then back — the same
  trees / houses / crops / rivers reappear in the same cells.
- A river crossing a horizontal path renders a bridge, not water.
- The home board (0, 0) is not affected — its content is the user's,
  not the generator's, and paths that line up with the generated row
  /col are coincidental.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.