# World Architect

> Designs tile-based levels for a Phaser game using only the tile IDs and entity IDs from the GDD. Outputs a JSON array of level objects (tilemap + spawns + goal). Use after game-designer has produced a valid GDD and the orchestrator needs level data.

- Skill: `ar9av/world-architect` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add ar9av/world-architect`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ar9av/world-architect/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: Ar9av (https://skillmd.com/u/ar9av)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/ar9av/world-architect

---


# World Architect

Builds levels for the Phaser tilemap loader. Inputs a GDD, outputs a JSON array of levels.

## When to use

After `game-designer` produces a valid GDD. The orchestrator passes the GDD; you produce `levels.json`.

## Output contract

Single JSON array. Each element:

```jsonc
{
  "id":     "<e.g. '1-1'>",
  "theme":  "<matches gdd.levelHints.themes[i]>",
  "size":   [<width>, <height>],         // tiles, must match levelHints.size
  "tiles":  number[][],                   // 2D array of palette indices, [row][col]
  "spawns": [
    { "entity": "<ENTITY_ID>", "x": <col>, "y": <row>, "facing"?: "up"|"down"|"left"|"right" }
  ],
  "goal":   { "kind": "tile" | "entity", "x"?: <col>, "y"?: <row>, "entityId"?: "<ID>" }
}
```

## Hard constraints

- `tiles[][]` dimensions match `size`. Outer rings are impassable tiles.
- All tile values are integer indices into `gdd.tilesetPalette` (0-based).
- Exactly one player spawn per level, on a passable tile, not overlapping any other spawn.
- All other spawns on passable tiles.
- Coordinates: `(x = column, y = row)`, origin top-left.
- **Platformer**: bottom row solid (impassable). Player one tile above the floor. Sprinkle 2-4 floating platforms.
- **Top-down**: open rooms with wall obstacles. Corridors ≥ 2 tiles wide.
- 2-6 enemies per level.
- `goal`: if `kind === "tile"`, must be reachable from spawn; if `kind === "entity"`, that entity must be in `spawns`.

## Process

1. Read the GDD from `game-state.json`.
2. For each `levelHints.themes[i]` (or `count` if no themes), build one level.
3. Start with the impassable border, then carve out walkable interior, then place obstacles, then place spawns, then place goal.
4. Run `scripts/validate_levels.mjs <levels-file> <gdd-file>` before returning.
5. Save to `game-state.json` under `levels`, also write `public/data/levels.json`.

## Validation rules (encoded in `scripts/validate_levels.mjs`)

- `tiles.length === size[1]` and every row's length === `size[0]`.
- Every tile value in `[0, palette.length)`.
- Exactly one player spawn per level, on a passable tile.
- All spawn coordinates within bounds and on passable tiles.
- Border ring is impassable.
- For platformer: `tiles[height-1][*]` all impassable.

## Examples

See `references/level-examples.md` for a 16×12 top-down level and a 22×12 platformer level (used by the playtester fixtures).

## NPC dialogue (RPG genre)

For `top-down-rpg` games, after levels are written, run the NPC dialogue generator. It reads NPC entity personality fields from the GDD and generates 6-8 dialogue lines per NPC using Claude Haiku, writing them to `public/data/npc-dialogue.json`:

```bash
node --env-file=~/.all-skills/.env scripts/gen_npc_dialogue.mjs <project-dir>
```

Game.js loads this file at runtime and falls back to built-in lines if absent. The personality block on each NPC entity drives the dialogue style — ensure the game-designer includes it for best results.

## Scripts

- `scripts/validate_levels.mjs <levels-file> <gdd-file>` — fails non-zero with reason if invalid.
- `scripts/gen_npc_dialogue.mjs <project-dir>` — generates NPC dialogue from personality (RPG).

## References

- `references/level-schema.json` — JSON Schema.
- `references/level-examples.md` — one level per genre with annotation.

