# Waymark design notes

GameLab experiment: **a turn-based expedition RPG played by walking.** The controller is your feet, the map is every place you walk, and the save file never leaves your browser.

## Constraints (deliberate)

- **Static only.** HTML, CSS and ES modules. No build step, no server, no accounts, no dependencies.
- **No third parties at runtime.** No map tiles, CDNs, analytics or remote fonts. The coastline (Natural Earth, public domain) and the pixel font ship with the app.
- **localStorage is the database; IndexedDB is the media shelf.** Journal state uses namespaced localStorage keys, split small so saves only rewrite what changed. Photos and voice live in IndexedDB as raw bytes (media.js), because localStorage is capped near 5 MB per site in every browser and a single afternoon of photos filled it. Export ("dispatches") is a first-class feature because the browser can clear either.
- **Mobile first, desktop testable.** Simulated walk mode with its own separate journal (`waymark.v2sim.*`).
- **Battles only start when you're standing still** (under about 0.8 m/s for 4 s).

## Files

| File | Role |
| --- | --- |
| `index.html`, `styles.css` | Shell and the pixel field-journal look |
| `app.js` | Sensors, the GPS fix pipeline, encounter gate, sim mode, saving |
| `ui.js` | Shared app context and DOM helpers |
| `geo.js` | Geometry: ENU, Web Mercator, great circles, device rotation, projection, seeded RNG, line simplification |
| `tiles.js` | Zoom-13 tiles of 256x256 squares; RLE codecs for revealed bits, heat and distinct days; trail codec |
| `world.js` | Movement classifier (walk / ride / flight / glitch), reveal, trails, journeys, expeditions, outposts |
| `store.js` | v2 persistence, export, merge-import, GeoJSON, legacy v1 cleanup |
| `migrate.js` | v1 to v2 (in place and from v1 archive files) |
| `constructs.js` | Six attractor families, stats measured from the orbit, elements from scene colour, moves, XP, plates |
| `battle.js` | Seeded, pure turn-based battle engine |
| `encounters.js` | Spawns (new ground, gone-wild marks, rival), cooldowns, standing-still detector, Hermit spot |
| `npc.js` | Ada Pell, Quartermaster Ivo, Wren Halloway, the Static Hermit; quests, achievements, ranks |
| `journal-gen.js` | Auto-written daily log from a seeded phrase bank; Markdown export |
| `pixel.js`, `font5x7.js` | Palette, bitmap text, sprites, 9-slice frames |
| `particles.js` | Camera-pixel particle constructs; still sketches for plates |
| `ar.js`, `map.js`, `journal.js`, `dispatch.js`, `marks.js`, `dialog.js`, `battle-view.js`, `chip.js` | Screens, dialogue, battle UI, chip sound |
| `tools/` | `font-source.txt` + `build-font.py` (font data and WOFF2) |
| `tests/` | Node tests: `node --test gamelab/projects/waymark/tests/game.test.mjs` (includes world tests) and `geo.test.mjs` |

## World scale

- Web Mercator world coordinates for the chart. AR stays in local metres.
- Squares are zoom-21 cells (about 15 m in Iowa). A visited zoom-13 tile stores revealed bits, 64x64 heat blocks and 64x64 distinct-day blocks, run-length encoded: usually a few hundred bytes.
- Movement: walking under 3 m/s reveals 25 m around you. 3 to 45 m/s is riding (dotted line, optional 1-square reveal). Faster, or any jump over 100 km, is a flight: a great-circle journey, no reveal. Impossible single hops are dropped; three in a row count as a reset.
- Expeditions cluster everything within 40 km. Names are coordinates until you rename them (no reverse geocoding).
- Trails are stored per expedition per day with delta encoding and simplified (3 m tolerance) once the day ends.

## Constructs

1. One 96x72 camera frame (or a seeded stand-in scene in sim mode).
2. Element from the dominant hue weighted by saturation; palette = 8 most common colours.
3. Family weighted by rarity, latitude and night. Parameter sets that collapse into short cycles are re-rolled.
4. Base stats: HP from orbit coverage, defense from coverage and density, attack from concentration, speed from a divergence (Lyapunov-style) estimate.
5. 3,000 to 4,200 particles keep their pixel colour and ease from their pixel position onto the orbit.

## Balance knobs

- Encounter chance per newly revealed square: 0.7% / 1.5% / 3% (rare / normal / frequent); cooldown 6 / 3 / 1.5 min; daily cap 6 / 12 / 20.
- Gone-wild marks: 30% on return to a mark older than 7 days, once a week per mark.
- Ink: 1 per 250 m walked, 1 per 20 new squares, 1 per wild win, 3 per rival win.
- Walking XP: 1 point per 10 m for each party member. Healing: 5% per 100 m; a fainted construct comes back at 50% after 300 m.
- Outposts: one ~60 m block visited on 5 distinct days.

## Ideas for later

- WebXR hit-test for sub-metre placement at a mark (Android).
- Time-lapse of the fog lifting.
- Trade bundles: export a few plates for a friend to import.
- IndexedDB for media if 5 MB becomes the limit.
