# CookieBlob ANSI RPG — Build SOP v0.1

**Project:** CookieBlob ANSI RPG  
**Build type:** New standalone browser RPG with a WordPress-compatible room export  
**Version:** 0.1 vertical slice  
**Status:** Active build contract  
**Date:** 2026-07-18

---

## 1. Product decision

Build an old-school, turn-based browser RPG whose graphics are rendered as colored ANSI-style text rather than image assets. CookieBlob is the persistence and transmission layer: the player’s state survives room changes in browser `localStorage`, can be exported/imported as a JSON seed, and can be displayed or extended by CookieBlob widgets.

The first build is deliberately small. It proves the loop rather than attempting a full game:

> explore → inspect → choose → fight or negotiate → collect → unlock → save/export

The visual language is a terminal window, but the game is still a browser application. It must remain playable with buttons and keyboard controls; ANSI text is presentation, not a requirement that the player use a real terminal.

---

## 2. Scope of v0.1

### Included

- Four connected locations:
  - **The Terminal at the Edge** — starting room and status screen
  - **The Lower Archive** — exploration and item discovery
  - **The Sealed Door** — locked until the player has the brass key
  - **The Keeper’s Nook** — NPC encounter and alternate resolution
- One player character with:
  - HP
  - attack
  - defense
  - level
  - experience
  - gold
- One deterministic enemy: **Archive Goblin**
- Three combat actions:
  - strike
  - guard
  - flee
- One non-combat interaction with the Keeper
- Inventory containing the brass key and one optional healing ration
- Flags for discovered room, goblin defeated, Keeper resolution, and door opened
- Colored ANSI-style renderer using a fixed-width text grid and ANSI-inspired palette
- Responsive fallback for narrow screens
- Keyboard shortcuts:
  - `1`, `2`, `3` for combat actions
  - `E` for explore/continue
  - `I` for inventory
  - `S` for seed export
- CookieBlob persistence in `localStorage`
- JSON seed export/import
- Reset button
- Event log / transmission rail showing recent state-changing events
- One standalone `index.html`
- One WordPress-compatible `wordpress-room.html`

### Excluded from v0.1

- Accounts, server-side saves, multiplayer, or server authority
- Procedural maps
- Random combat outcomes
- Character classes, equipment slots, crafting, magic, quests, or shops
- External image assets
- Arbitrary HTML injected from state
- Cross-origin iframe communication
- A full ANSI terminal emulator
- Any claim that zo.pub itself runs the game

These exclusions protect the first test from becoming a framework project.

---

## 3. CookieBlob state contract

Use one namespaced key so this new game does not collide with the existing Archive Tree or other CookieBlob rooms.

```js
const STORAGE_KEY = "cookieblob_ansi_rpg_v01";
```

Canonical state shape:

```js
{
  version: "0.1",
  room: "terminal-edge",
  player: {
    name: "Wanderer",
    level: 1,
    xp: 0,
    next_xp: 20,
    hp: 12,
    max_hp: 12,
    attack: 4,
    defense: 1,
    gold: 0
  },
  inventory: [],
  flags: {
    lower_archive_seen: false,
    goblin_defeated: false,
    keeper_resolved: false,
    door_opened: false,
    ration_taken: false
  },
  combat: null,
  log: [],
  transmissions: [],
  visits: 0,
  updated_at: null
}
```

### State rules

1. `getState()` must parse defensively and fall back to a fresh state if JSON is invalid.
2. `saveState()` must write only JSON under `STORAGE_KEY`.
3. All state mutations pass through named reducer/action functions rather than scattered inline edits.
4. HP is clamped to `0..max_hp`.
5. Gold and XP cannot become negative.
6. Inventory additions are idempotent unless an item is explicitly consumable.
7. Every meaningful mutation appends a short event to `log` and updates `updated_at`.
8. The renderer must escape player-entered text before placing it into HTML.
9. Import accepts only the expected version and shape; invalid seeds are rejected without replacing current state.
10. Reset requires an explicit confirmation.

### CookieBlob features mapped to the RPG

| CookieBlob feature | RPG use |
|---|---|
| Persistent local state | room, player, inventory, flags, combat, log |
| Cross-room continuity | same namespaced state across standalone room pages or pasted WordPress rooms |
| Widget/transmission data | event summaries and optional incoming notices |
| Exportable seed | download current state as `cookieblob-ansi-rpg-seed.json` |
| Importable seed | resume or share a run on another browser |
| Room registry pattern | each room declares its room ID and available actions |
| Reset/replay | start a clean run without touching other CookieBlob games |

---

## 4. Architecture

The prototype remains dependency-free HTML/CSS/JavaScript so it can be tested as a file and pasted into a WordPress Custom HTML block.

```text
index.html / wordpress-room.html
        |
        +-- state: load / validate / save
        |
        +-- actions: explore, inspect, combat, talk, open, reset
        |
        +-- reducer: pure state transitions
        |
        +-- renderer: ANSI grid + HUD + buttons + log
        |
        +-- seed I/O: export / import JSON
```

### Implementation boundaries

- The reducer owns game rules.
- The renderer owns presentation.
- The room configuration owns room text and available actions.
- The seed module owns serialization and validation.
- No action may directly modify DOM elements as a substitute for state mutation.
- The standalone and WordPress files should share the same rule vocabulary. If code duplication is necessary for WordPress portability, copy from the standalone prototype and record any intentional difference.

### Suggested action vocabulary

```js
START_RUN
ENTER_ROOM
EXPLORE_LOWER_ARCHIVE
INSPECT_RELIC
TAKE_RATION
START_COMBAT
COMBAT_STRIKE
COMBAT_GUARD
COMBAT_FLEE
TALK_KEEPER
ACCEPT_KEEPER_OFFER
REFUSE_KEEPER_OFFER
OPEN_SEALED_DOOR
EXPORT_SEED
IMPORT_SEED
RESET_RUN
```

---

## 5. ANSI-style visual system

The renderer uses a fixed-width character grid. It is not required to emit literal terminal escape sequences; browser-safe CSS classes provide the color layer.

### Palette

| Token | Meaning | Suggested color |
|---|---|---|
| `ansi-white` | ordinary text | `#d7e0d4` |
| `ansi-gray` | metadata / dim text | `#778277` |
| `ansi-green` | player / success / healing | `#69d17d` |
| `ansi-yellow` | item / choice / warning | `#e7c65f` |
| `ansi-cyan` | room / information / transmission | `#62d5d8` |
| `ansi-blue` | water / archive depth / defense | `#6289df` |
| `ansi-red` | damage / danger / enemy | `#e86a6a` |
| `ansi-magenta` | rare state / Keeper / threshold | `#c17de8` |

### Rendering rules

- Use `font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace`.
- Preserve whitespace with `<pre>` or CSS `white-space: pre`.
- Use a fixed logical width of approximately 48–64 columns and allow horizontal scrolling rather than wrapping the map.
- Every map glyph has a semantic class where practical: player, wall, door, enemy, item, NPC, floor.
- Keep a text description or `aria-label` for major map states so the scene is not color-dependent.
- Do not use color alone to communicate availability, damage, or locked state.
- The game UI can include normal HTML buttons below the ANSI panel for accessibility and mobile use.

### Initial scene glyphs

```text
+------------------------------------------------+
|              THE TERMINAL AT THE EDGE         |
|                                                |
|       .........          [SEALED DOOR]         |
|       .   @   .                #              |
|       .       .                #              |
|       .........          .......              |
|                                                |
|  @ YOU     # WALL     $ ITEM     ! ENEMY       |
+------------------------------------------------+
```

The player marker is `@`; the renderer must still provide a legend.

---

## 6. Vertical-slice game design

### Room 1 — The Terminal at the Edge

Purpose: establish the interface and show the player state.

Actions:

- `ENTER LOWER ARCHIVE` → moves to `lower-archive`, sets `lower_archive_seen`
- `INSPECT TERMINAL` → adds a lore log entry
- `EXPORT SEED` → downloads current state

### Room 2 — The Lower Archive

Purpose: exploration and first consequence.

Actions:

- `SEARCH THE SHELVES` → reveals the ration once, sets `ration_taken` when taken
- `APPROACH THE GOBLIN` → starts combat if not defeated
- `RETURN TO TERMINAL` → moves to `terminal-edge`
- `GO TO SEALED DOOR` → moves to `sealed-door`

### Combat — Archive Goblin

Enemy state:

```js
{ id: "archive-goblin", name: "Archive Goblin", hp: 8, max_hp: 8, attack: 3, defense: 0 }
```

Deterministic rules:

- `STRIKE`: player damage is `max(1, player.attack - enemy.defense)`; if enemy survives, enemy deals `max(1, enemy.attack - player.defense)`.
- `GUARD`: player takes `max(0, enemy.attack - player.defense - 2)` damage; guard ends after the enemy turn.
- `FLEE`: returns to the Lower Archive with no reward; it always succeeds in v0.1.
- At enemy HP `0`, set `goblin_defeated`, grant `12 XP`, grant `3 gold`, append a transmission, and return to the Lower Archive.
- At player HP `0`, mark the run defeated, restore player HP to `max_hp`, return to `terminal-edge`, and preserve the log. Do not delete the run.
- Level up at `20 XP`: level becomes 2, max HP increases by 3, HP restores to max, attack increases by 1.

### Room 3 — The Sealed Door

Purpose: test gating and a clear reward.

- Without `brass-key`, render a locked state and a return action.
- After `goblin_defeated`, the goblin drops `brass-key` exactly once.
- `USE BRASS KEY` sets `door_opened` and moves to `keeper-nook`.

### Room 4 — The Keeper’s Nook

Purpose: test non-combat choice and flags.

The Keeper offers one of two resolutions:

- `LISTEN TO THE KEEPER` → sets `keeper_resolved`, grants a lore transmission, returns to the door
- `TAKE THE KEEPER'S TOKEN` → sets `keeper_resolved`, grants `keeper-token`, returns to the door

Both are valid. The first slice proves branching state, not moral scoring.

---

## 7. Accessibility and input

- Every action is a real `<button>` with visible focus.
- Buttons include text labels even when the ANSI scene communicates the same action.
- Keyboard shortcuts must not fire while focus is inside a text input or textarea.
- The current room, HP, combat status, and latest event must be available as ordinary text.
- Color classes must have sufficient contrast against the terminal background.
- Use `aria-live="polite"` for the event/status region.
- Never require hover to discover an action.

---

## 8. Test-first build sequence

### Stage 1 — Rule core

1. Create the folder and this SOP.
2. Implement `defaultState`, validation, persistence, and pure action/reducer functions.
3. Add a small in-page fixture runner or console-safe assertions for:
   - fresh state
   - invalid JSON fallback
   - strike damage
   - guard damage
   - flee
   - goblin defeat reward
   - door lock/unlock
   - level-up threshold
   - seed round trip

### Stage 2 — ANSI renderer

1. Render the terminal scene from state.
2. Render room-specific glyphs from state.
3. Add palette classes and legend.
4. Add text HUD, buttons, event log, and responsive behavior.
5. Confirm renderer refreshes after every action without page reload.

### Stage 3 — Standalone browser prototype

1. Build `index.html`.
2. Open it in a browser using a local HTTP server or file mode.
3. Exercise the acceptance matrix below.
4. Export a seed, reset, import the seed, and verify the run resumes.

### Stage 4 — WordPress room export

1. Copy the proven rule and renderer behavior into `wordpress-room.html`.
2. Prefix all CSS selectors to avoid theme collisions.
3. Avoid module imports and external dependencies.
4. Test in an isolated page or Custom HTML block.
5. Confirm the CookieBlob key remains namespaced and does not touch `tenchi_blob` or `cookieblob_state`.

### Stage 5 — Package and publish

1. Update `test-report.md` with actual results and known browser limitations.
2. Keep only intended artifacts in this folder.
3. Sync the folder to `cookieblob-ansi-rpg-v01` with `zopub sync`.
4. Report the collection URL.

---

## 9. Acceptance matrix

| ID | Test | Expected result |
|---|---|---|
| A1 | Open a fresh run | Terminal room appears; HP 12/12; no inventory |
| A2 | Refresh page | State remains in the same room |
| A3 | Enter Lower Archive | Room changes; `lower_archive_seen` is true |
| A4 | Search shelves twice | Ration appears once; second search does not duplicate it |
| A5 | Start goblin fight | Combat HUD appears with enemy HP 8/8 |
| A6 | Strike until victory | Deterministic damage; reward granted once; combat cleared |
| A7 | Guard | Reduced damage is shown and combat continues |
| A8 | Flee | Combat clears; player returns to Lower Archive; no reward |
| A9 | Lose combat | Player returns to Terminal with full HP; log remains |
| A10 | Visit Sealed Door before key | Door is visibly locked; no accidental unlock |
| A11 | Defeat goblin then visit door | Brass key is available exactly once |
| A12 | Use key | `door_opened` is true; Keeper’s Nook opens |
| A13 | Choose either Keeper action | `keeper_resolved` is true; corresponding log/transmission appears |
| A14 | Export seed | Valid JSON downloads with version `0.1` |
| A15 | Reset then import seed | Imported room, flags, inventory, log, and player stats match pre-reset state |
| A16 | Enter text containing `<` or `&` | Text displays literally; no HTML injection |
| A17 | Use keyboard shortcuts | Correct action fires; typing in inputs does not trigger shortcuts |
| A18 | Narrow viewport | ANSI panel remains readable via scroll; controls remain usable |
| A19 | Existing CookieBlob data | Existing `tenchi_blob` and `cookieblob_state` remain unchanged |
| A20 | WordPress paste test | Room renders without external dependencies or selector collisions |

A build is not ready for broader content until A1–A20 pass or each failure is documented.

---

## 10. Definition of done

The v0.1 slice is done when:

- `index.html` and `wordpress-room.html` exist.
- The complete vertical slice can be played from fresh state to Keeper resolution.
- The state survives refresh and seed round trip.
- The ANSI renderer is readable, colored, keyboard-accessible, and not color-only.
- Existing CookieBlob keys are untouched.
- The acceptance matrix is filled with actual results.
- The source package is synced to `https://zo.pub/tenchi/cookieblob-ansi-rpg-v01`.

The first implementation should favor a reliable, inspectable loop over visual complexity. If the ANSI scene and the game rules disagree, the state/reducer is authoritative and the renderer must be corrected.

## v0.2 Overland Extension

The v0.2 build extends the vertical slice with a persistent overland mode without changing the existing dungeon rules. The mode uses the same namespaced CookieBlob state object and portable seed format.

### Map contract

- Map identifier: `old-road-v01`
- Dimensions: 30 columns × 14 rows
- Passable terrain: plains (`.`), road (`=`), and landmark tiles
- Blocked terrain: forest (`^`), water (`~`), and boundary walls (`#`)
- Player marker: `@`
- Landmarks: `T` Wayfarer's Post, `+` Broken Shrine, `?` Ruined Watchtower, `A` Lower Archive, `M` Moss Crypt teaser

### Travel state

```js
{
  mode: "overland",
  room: "overland",
  overworld: { map_id, x, y, discovered, visited, steps, day, weather },
  travel: { rations, fatigue, last_event }
}
```

Arrow keys and WASD move one tile at a time. The first overland slice has no random encounters. Every accepted move persists coordinates and increments travel counters. A dungeon entered from `A` returns to the exact overland coordinate.

### v0.2 acceptance additions

1. Enter the Old Road from the Terminal.
2. Move with keyboard controls and reject blocked terrain.
3. Interact with the Wayfarer's Post, Broken Shrine, and Ruined Watchtower.
4. Enter the Lower Archive from the `A` marker.
5. Return from the dungeon to the same map tile.
6. Reload in overland mode and verify the map and counters persist.
7. Preserve v0.1 seed compatibility and CookieBlob key isolation.
