Planning document. Hardware facts reference GAMEBOY_DEV_GUIDE.md; toolchain facts reference RGBDS_GUIDE.md.
Fantasy: you are a pirate captain with a small ship, a blank map, and a whole ocean that has never existed before. Chart islands, find treasure, trade or plunder, upgrade your ship, and defeat the rival pirate fleet.
Design pillars (every feature decision gets checked against these):
- A world that fits in 4 bytes. The entire ocean is derived from one 32-bit seed. Nothing about the world is stored — only what the player has changed or discovered.
- Exploration is the core verb. Fog of war, filling in the map, spotting sails on the horizon, "X marks the spot." The map screen should feel like a pirate chart being inked in.
- Runs, not campaigns. 45–90 minute sessions with a win condition (find the legendary Treasure of the Nine Isles) and permadeath-lite stakes. A seed can be shared/typed in — daily-run and race potential for free.
- DMG-true. Must run perfectly on an original 1989 Game Boy. CGB color is an enhancement, never a requirement.
Touchstones: Sid Meier's Pirates! (structure: sail → port → quest → plunder), Elite (an entire galaxy from a seed — the original proof this works on 8-bit), Wind Waker (sailing + fog-of-war charting), GBHack (proof that heavy procgen + permadeath works on GB hardware), Dragon Warrior Monsters (how much a GB game can feel).
| Decision | Choice | Rationale |
|---|---|---|
| Base platform | DMG (4 shades) | Maximum compatibility (DMG, SGB, CGB, AGB, emulators, Analogue Pocket); forces disciplined budgets |
| CGB support | Enhanced ($80 header flag) |
Color palettes + double-speed generation when detected; game fully playable without |
| Cartridge | MBC5+RAM+BATTERY | 8 MiB ROM headroom, 32 KiB SRAM for saves + fog of war, guaranteed Double-Speed-safe, rumble optional |
| ROM size | 256 KiB (16 banks) target, room to grow | Code + data is small in procgen; art is the cost |
- RGBDS (rgbasm/rgblink/rgbfix/rgbgfx) — full control over banks, timing, and memory; we have complete local docs. Assembly is the right call for a game whose core loop is timing-critical scrolling + on-demand generation.
- hardware.inc for register definitions.
- BGB + SameBoy + Emulicious for debugging (BGB's
LD B,Bbreakpoints, Emulicious profiler); mGBA for quick sanity runs. - Aseprite (or any PNG editor) → rgbgfx for tiles/tilemaps/palettes. Keep per-asset flags in
*.flagsat-files. - hUGETracker + hUGEDriver for music (shanties!), CBT-FX or custom for SFX.
- evunit for unit-testing the generator and RNG in CI; Mooneye/Blargg ROMs for emulator sanity of the dev environment.
- make +
-M -MG -MPdependency tracking;rgblink -S romx=Nscramble builds in CI to catch bank-assumption bugs.
Alternative considered: GBDK-2020 C (GBHack proves it works for procgen roguelikes). Rejected for the main build because smooth scrolling + per-chunk generation + VRAM streaming wants cycle control, but a GBDK prototype of the generator is a sanctioned fast-path for tuning island shapes before porting to asm (see §8, M0).
This is the heart of the project. Everything derives from a 32-bit world seed chosen on the title screen (auto-rolled from entropy, or typed in).
- Coordinate hash (worldgen): a pure function
hash32(seed, x, y, salt)— deterministic, stateless. SplitMix32-style integer mixing (multiply-xorshift constants) is ~40 cycles per call in SM83 asm — cheap enough to run per-cell during scrolling. Never use a stateful PRNG for worldgen; the world must be regenerable on demand with zero storage. - Stateful PRNG (runtime events: loot rolls, AI dice, weather): xorshift32 (or 16-bit for speed where quality doesn't matter), seeded at new-game from
DIV+ joypad timing (GBHack's proven entropy trick: human START-press timing decorrelates runs).
WORLD 256 × 256 "cells" (1 cell = 1 map-screen = 20×18 tiles)
CHUNK generated on demand per cell, never stored whole
TILE 8×8 px, live viewport buffer in WRAM
- Cell type from
hash(seed, x, y): open sea (~72%), island cell (~20%), reef/rocks (~5%), port cell (~2.5%, must border land), special (~0.5%: sea monster lair, maelstrom, the Nine Isles). - Value noise for coastlines: sample hash at cell corners, bilinear-interpolate in Q4.4 fixed point → per-tile elevation; threshold → water/sand/jungle/mountain. Islands emerge from thresholded noise + cellular-automata smoothing pass (4–5 rule, 2 iterations — affordable per-cell, ~1–2 ms).
- Connectivity guarantee: port cells are placed by hash then validated against a cheap local rule (has sea route to cell edge); the Nine Isles are placed by a fixed algorithm (seeded angular distribution around world center) so the win condition is always reachable. Playability proofs by construction, not by search.
- Chunk generation on demand: entering a cell, generate its 20×18 tile map into WRAM. ~2–5 ms worst case on DMG — hidden inside the screen-transition fade. Interior of islands (docks, treasure sites, caves) generated the same way with a different
salt.
Per new game, in SRAM (32 KiB budget):
| Data | Size | Notes |
|---|---|---|
| World seed | 4 B | regenerates everything |
| Player state (pos, gold, crew, rum, hull, upgrades, flags) | ~64 B | |
| Fog of war bitmap | 8 KiB | 256×256 cells = 65,536 bits — exactly one SRAM bank. Charted = 1 |
| Port/market deltas (prices drift from seed baseline) | ~1 KiB | sparse table, only visited ports |
| Quest/bounty state | ~256 B | |
| High-seas "encounter cooldown" cells | ~256 B | |
| Magic number + checksum + version | 16 B | GBHack pattern: validate before trusting, 2 rotating save slots + autosave on port dock |
Total < 10 KiB of 32 KiB. Room for a second save slot.
Full-screen chart (160×144 viewport into a 256×256-cell map, smooth-scrolled): inked-in islands where explored, sea monsters doodled at rumor cells, "X" for treasure maps owned, ship icon. This is pure BG-map work and sells the fantasy for near-zero runtime cost. Build it early; it's the game's identity.
SAIL (explore, chart, encounter)
→ PORT (sell loot, repair, recruit, hear rumor, buy map fragment)
→ QUEST/TREASURE (follow map X, island interior, boss/dig)
→ upgrade ship → sail further out (harder seas) → ... → Nine Isles finale
- Sailing: 8-way movement, momentum + wind direction modifier, smooth pixel scrolling (SCX/SCY + tile-streaming), animated water (2-phase tile swap in VBlank).
- Encounters at sea: hash-spawned per cell with cooldown — merchant ships (trade or attack), pirates (fight or flee), storms (skill minigame), sea monsters (deep cells only).
- Combat: arcade broadside — positioning + timed volleys, damage to hull/sails/crew. Keep it on the sea layer; no separate combat screen for MVP.
- Ports: generated name (syllable table), market with 4 goods (rum, silk, spice, cannon) with seed-drifted prices, tavern rumors (point to undiscovered specials), shipyard upgrades (hull, sails, cannons, crew).
- Treasure maps: fragment rewards assemble into "cell X,Y + landmark hint"; digging triggers an island-interior minidungeon (one cell, ~3 screens, generator salt
0xTREASURE). - Win: collect 9 chart fragments (one per special isle, gated by escalating difficulty rings from world center) → final fleet battle → treasure.
Boarding actions, crew management beyond a number, melee combat, fishing minigame, multiplayer/link cable, SGB border, real-time day/night. (Link-cable 2P naval duel is the first post-1.0 candidate — serial protocol is simple, see dev guide §12.)
| Region | Use |
|---|---|
| $C000–CFFF (WRAM0) | Current cell tile buffer (20×18 = 360 B), scroll streaming ring, game state structs, OAM shadow buffer (160 B, ALIGN[8]), entity table (16 × 16 B) |
$D000–DFFF (WRAM0 cont. w/ -w) |
Map-screen cell cache (1 KiB), generator scratch (1 KiB unionized), decompression/scratch unions |
| $FF80–FFFE (HRAM) | OAM DMA routine, joypad state, scroll regs cache, hot loop vars |
Unionized WRAM sections (SECTION "Scratch", WRAM0, UNION) for generator scratch vs. combat scratch vs. map-screen scratch — they never coexist.
| Bank | Contents |
|---|---|
| 0 | Header, vectors, main loop, VBlank/STAT handlers, joypad, DMA, RNG/hash, generator core, far-call trampolines |
| 1–2 | Sailing mode (scroll engine, encounters, combat) |
| 3 | Port/market/tavern UI + island interiors |
| 4 | Map screen |
| 5–6 | Tilesets, tilemaps, attrmaps (rgbgfx output) |
| 7 | Music driver + songs, SFX |
| 8–15 | Text tables (names, rumors, dialog), quest data, spare |
Discipline: mode code calls across banks only through bank-0 trampolines; ASSERT BANK(x) == BANK(y) wherever co-banking is required. CI scramble builds (-S romx=8,wramx) prove no hidden assumptions.
- Sea/sailing: BG layer (tilemap $9800), 20×18 viewport, SCX/SCY fine scroll, column/row streaming on tile-boundary crossing (write during HBlank + VBlank; budget ~22 tile writes/frame on DMG CPU-copy — plan generator to fill WRAM, blit in VBlank).
- HUD: Window layer (WY=128 → bottom 2 rows), LCDC.1 toggled by LYC=LY STAT interrupt to keep sprites off the HUD (classic technique, dev guide §18).
- Ship/enemies/cannonballs: sprites (8×16 mode), ≤10/line enforced by design (max 4 ships + 4 balls + effects; flicker-rotation fallback).
- Screen transitions: BGP fade
$E4→$90→$40→$00covering cell regeneration. - CGB enhancements (runtime-detected): color palettes via rgbgfx
-c, palette-swap day/dusk/night, Double Speed only during generation bursts.
halt→ VBlank handler: OAM DMA, HUD/window regs, scroll regs, water animation, VRAM blit queue drain.- Main: input → sailing physics → entity AI → encounter rolls → generator slice (time-boxed, resumable across frames for big jobs).
Generator is written reentrant and sliceable: per-cell generation is split into phases (coast hash → CA smoothing → detail sprinkle) that can run one phase per frame if a cell is entered mid-scroll.
- Tiles (2bpp, ~256-tile budget for sea set): water ×4 frames, coast edges/corners (16 tile marching-squares set from the CA output), sand, jungle, mountain, dock, ship-deck interior set, cave set, port buildings, map-screen set (ink style!), HUD/font (charmap-driven text, dev guide §5 charmaps).
- Sprites: player ship (4 dirs × 2 frames, 8×16 metasprites), merchant, pirate brig, sea monster, cannonball, explosion, seagull ambience.
- Audio: 3–4 hUGETracker shanties (title, sailing, port, battle), SFX (cannon, splash, coin, seagull, storm).
- Text: name syllable tables, 40-ish rumor templates, market UI strings. All via charmap, compressed if ROM gets tight.
| Risk | Mitigation |
|---|---|
| Generation too slow on DMG (visible hitch at cell borders) | Time-boxed slices; precompute the next cell in the direction of travel during the current one; CGB double-speed burst; worst case: fade covers it. Profile with Emulicious from day one |
| Unplayable/boring worlds | Connectivity by construction (§3.2); a "world lint" test ROM that generates N seeds headless and asserts ports/finale reachable (run in CI via evunit/SameBoy scripting) |
| Scope creep (it's a pirate game — everything is tempting) | §4 cut-list is law; every feature must serve pillar 1–4 |
| SRAM save corruption | Magic + checksum + dual slots (GBHack pattern); validate on boot; autosave only at ports |
| 10-sprites-per-line flicker | Design encounters to ≤8 objects/line; rotation flicker as fallback |
| VRAM streaming overruns | Blit queue with hard per-frame budget; scroll speed capped so streaming never exceeds budget |
M0 — Generator lab (1–2 weeks, GBDK or asm) Standalone ROM: shows one generated cell, seed typed via d-pad. Tune noise thresholds, CA rules, coast tile marching-squares. Deliverable: pretty islands on demand, deterministic across reboots.
M1 — Sailing skeleton Scroll engine + streaming + ship physics + water animation + HUD window. Hand-built test sea. Deliverable: it feels like sailing.
M2 — Infinite ocean Generator wired to scroll; cell transitions; fog-of-war bitmap; map screen. Deliverable: the core fantasy works — sail anywhere, chart fills in, same seed = same world.
M3 — Ports & economy Docking, market, repair/recruit, rumors, save/load. Deliverable: a playable loop.
M4 — Danger Encounters, combat, storms, damage/death, permadeath flow. Deliverable: a game with stakes.
M5 — The Nine Isles (win condition) Treasure maps, island interiors, chart fragments, finale, ending. Deliverable: feature-complete.
M6 — Polish & ship Music/SFX final, CGB color pass, difficulty tuning via world-lint stats, title/attract mode, manual text, romusage budget check, hardware testing (flashcart on real DMG + GBC + GBA). Deliverable: v1.0 ROM.
- GBHack (github.com/statico/gbhack) — closest prior art: procgen + permadeath on GB. Steal: xorshift16 + DIV/timing seeding, BSP dungeon idea for island interiors, SRAM save validation, banked-generator layout, 150-byte bitpacked FOV.
- Elite galaxy generation — the philosophical ancestor: everything from a seed, nothing stored.
GAMEBOY_DEV_GUIDE.md§9 (scrolling/streaming), §18 (frame loop, shadow OAM, HUD window, save practices) — local.RGBDS_GUIDE.md§6/§9/§18 (sections, macros, build recipes) — local.- GBDev Discord #gbdev — playtesting and generator tuning feedback.
First concrete task: build the RGBDS project skeleton (Makefile from RGBDS_GUIDE §18, hardware.inc, header section, CI scramble build), then M0's coordinate-hash + noise demo on hardware.