Files
Crystal/docs/PLAN.md
T
Sébastien QUEROLandClaude Opus 5.5 796fe8f6d5
Checks / web (push) Canceled after 0s
Open Route 29 and Cherrygrove City
- Enable Route 29, its Route 46 gate, Cherrygrove City and its buildings.
- Run item balls through FindItemInBallScript and fruit trees through
  FruitTreeScript, with a native daily reset of the daily engine flags.
- Port the Pokémon Center nurse (HealParty with the PP Up clamp,
  CheckPokerus, phone-call checks) and record the whiteout spawn on
  entering a Pokémon Center, as EnterMapWarp does.
- Port standard Poké Marts with keyboard, D-pad and pointer menus.
- Add the recoil battle command (Struggle) and VAR_BOXSPACE.
- Save fruit trees, the daily reset day and the last spawn map; older
  version-3 backups migrate by absence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 17:25:02 +02:00

162 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Full native port plan
## Direction
Translate Crystal's Game Boy assembly systems into a native TypeScript game. The browser runs the translated engine; the user's original ROM supplies data and assets. The emulator is a separate development reference for checking behavior, never a dependency of the native release.
PRET's [disassembly](https://github.com/pret/pokecrystal) is pinned at `7a7881d0d62e0ddbd82dcf10e7116807487ac651`. Its `crystal11` build with RGBDS 1.0.3 was verified byte-for-byte against the supplied English US/Europe 1.1 ROM. Required SHA-1: `f2f52230b536214ef7c9924f483392993e226cfb`. Other revisions require separate verified profiles.
The emulator-backed prototype is now at `/reference.html`. The default application is the native port. The previous plan to progressively modify an emulated ROM has been superseded.
## Implemented: first native exploration milestone
- Bounds-checked ROM access, banked pointers and native LZ decompression.
- On-demand extraction of six maps: New Bark Town, both floors of the player's home, Elm's lab, the neighbor's house and Elm's house.
- Metatiles, collision quadrants, palettes, roofs, sprites and facing tables read directly from the user's ROM.
- Native canvas rendering, camera scrolling, walking, solid-tile/NPC collision, doors and stairs within the supported area.
- A bounded interpreter for a subset of the game's own event bytecode, plus text decoding. This does not execute GB instructions.
- Original initial event flags, readable signs, bedroom radio, bookshelves and supported conversations. Unknown interactions abort without committing partial changes.
- Native DOM dialogue, name/avatar choice, touch/keyboard controls and pause/resume.
- A minimal Crystal-style shell with Game, Savegames, Map, Pokédex, Link and Friends tabs across the top. The game uses the available native canvas, and leaving Game releases input and pauses play.
- Existing save, backup export and backup import actions in Savegames. Map shares a live local minimap with the game and shows the current supported area, player position and coordinates. Pokédex, Link and Friends are clearly marked as planned rather than populated with placeholder data.
- Native JSON state inside the backup envelope, separate IndexedDB storage and a separate tab lock. No emulator snapshot or cartridge save migration is implied.
- Synthetic tests, local-ROM tests, browser checks, and comparisons against the original decompression routine running in the reference emulator.
- Native production output excludes the emulator and original game assets.
## Implemented: opening scenes
- Resumable event interpreter: text, yes/no and weekday choices, scripted movement, `follow`, emotes, pauses, scene variables, phone contacts, string buffers and translated specials. Unknown commands still abort without committing.
- Movement data decoder for turns, steps, slides, jumps, sleeps, facing locks, visibility and emotes, animated at the original 30 Hz overworld rate. Player walking now takes the original 8 ticks per tile, not half that.
- Coordinate events, scene scripts with `sdefer`, and NEWMAP/TILES/OBJECTS map callbacks, including the bedroom's own initialization callback. Objects spawn at map load as in the original.
- Mom's Pokégear, weekday and DST scene; the teacher at the town's west edge; the rival at the lab window; and Elm's introduction with the lab's exit guard, all from the ROM's scripts.
- Map headers for all 26 groups, ROM location names and connected maps drawn beyond the edges. Entering an unported connection is refused with its location name.
- Version 2 native saves with scenes, phone, game-clock settings and moved objects; version 1 saves migrate.
## Implemented: starter and first items
- ROM decoders for species names, base data, learnsets, moves, items, pockets, growth rates and front pictures.
- Native party generation for script gifts, following `GeneratePartyMonStats`: random DVs, level-up moves, PP, exp, stats, happiness and caught data. The Pokédex records the species as seen and caught.
- Bag pockets with the original capacities and stacking rules. `verbosegiveitem` runs the ROM's own `GiveItemScript`.
- Elm's Poké Balls, with a picture box, the nickname prompt with a native naming field, Elm's directions and phone number, and the aide's Potion.
- Version 3 saves with the player ID, party, bag and Pokédex; restores recompute stats and check item pockets.
## Implemented: ledges and warp movement
- Walkable ledge tops, directional two-tile jumps over 16 ticks, and jump shadows.
- Exit mats wait for input in their exit direction. Arriving on a mat preserves the actual position; doors, stairs and caves animate the downward exit step.
- Mid-step saves replay from the source tile, including jumps and map connections, so reload cannot bypass landing scenes or warps.
- Synthetic movement tests run without a ROM. Local-ROM tests exercise five Route 29 ledge types; browser checks cover stairs, opening scenes and an exit-mat save/reload.
## Done: Route 29 and Cherrygrove City
- Route 29, its Route 46 gate, Cherrygrove City and its five buildings are enabled. Route 30 and Route 46 remain closed.
- Item balls run the ROM's `FindItemInBallScript`. Fruit trees run `FruitTreeScript`, with picked trees saved and regrown by a native `CheckDailyResetTimer` that clears the daily engine flags once per game day.
- The Pokémon Center nurse runs `PokecenterNurseScript`: `HealParty` (with `ComputeMaxPP`'s PP Up clamp), `CheckPokerus` and the phone-call checks. Entering a Center from a town or route records the whiteout spawn, as `EnterMapWarp` does.
- Standard Poké Marts run `pokemart` natively from `StandardMart`, `BuyMenu` and `SellMenu`: stock from the ROM's `Marts` table, `ItemAttributes` prices, half-price selling and the original texts, with keyboard, D-pad and pointer menus. Special mart types remain refused.
- `VAR_BOXSPACE` reads an empty box: nothing can be caught or deposited yet.
- The battle engine gained `recoil` (Struggle). A local stress run of 360 Route 29 battles across all starters and times of day finishes without refusals.
- Save format 3 gains `fruitTrees`, `dailyReset` and `lastSpawnMap`; older backups migrate by absence.
- Still refused: the catching tutorial (`catchtutorial`) and Cherrygrove's rival scene, both reachable only after the Mystery Egg errand.
## In progress: native battles
The battle core, ROM tables, encounter hooks, battle UI and whiteout hooks are present. Unit tests cover wins, escape, loss, experience and move learning; reference tests compare damage and type effectiveness. Browser tests drive a test-only wild encounter through the real UI: victory with level-up and move learning, escape, whiteout, keyboard and on-screen D-pad menu navigation, mid-battle backup restore, and rollback of unsupported effects. Real Route 29 encounters now use the ROM's encounter tables. Capture, switching, items in battle, trainer battles and the rest of the opening quest remain unfinished.
This is still not a complete new game. Party/bag/Pokégear menus, story progression beyond Cherrygrove, NPC roaming, decorations, animated terrain, surfing and audio remain unfinished. Save slots, a Johto/Kanto atlas, Pokédex records, online Link, friends and real-world steps come later.
## 1. Complete the overworld foundation
Map headers, location names, connection offsets, connected-border drawing, coordinate events, scene scripts, map callbacks and scripted movement are in place (see above). Next: enable connected transitions as their maps are ported; add dynamic warps, elevators, ice, currents, remaining forced movement, surfing, cycling, cut trees and boulders. Player turn timing still needs a reference comparison.
Port decorations, tile changes (`changeblock`), NPC roaming movement types, trainer sight lines and the remaining movement commands. Complete terrain animation, layering and special palettes. Record deviations from the original until a comparison test closes them.
Acceptance: representative outdoor/interior/cave/connected-map traces produce matching positions, collisions, transitions and event changes in native and reference engines. Do not enable an area merely because its image can be decoded.
## 2. Opening story and first battle slice
Introduce native player, party, Pokémon, inventory, money and quest models. Read species, moves, growth curves, evolutions, encounters, trainers and items through typed ROM decoders. Define serialization/migrations before persisting new systems.
Expand script calls, choices, scene state, inventory commands and native special handlers. Translate routines reached through assembly calls into explicit native services; never fall back to emulator execution in the shipping game.
Meeting Mom, Elm's introduction, the starter, the aide's Potion, Route 29 and Cherrygrove run now. Next: NPC roaming movement, trainer sight lines and trainer battles, Route 30 and Mr. Pokémon, the Mystery Egg round trip with the catching tutorial and the rival battle; then the introduction's naming and clock screens.
Complete and validate the native battle engine: deterministic randomness, stats, move selection, accuracy/critical checks, damage, status, turn order, switching, fainting, experience, capture and rewards. Compare controlled seeds and states with the original, including integer rounding and overflow behavior.
Acceptance: finish the opening quest, including encounters and the rival battle, party/inventory changes and save/reload, without emulation or manually setting story flags.
## 3. Complete Crystal
Port remaining battle commands, move/item effects, AI, special battles, evolutions, breeding, happiness, eggs and PC storage. Gen II behavior is the baseline; list intentional changes separately.
Complete field moves, gyms, Rocket events, phone/Pokégear, shops, healing, puzzles, minigames, daily events, roaming Pokémon and postgame. Cover Johto, the League, Kanto and the final battle. Track unsupported script/special handlers and add fixtures before translating each one.
Port the music/sound sequencer and synthesize its channels through Web Audio, reading music, effects and wave data from the local ROM. No recordings or CPU emulator are needed in the release. Verify timing and audio unlocking on phones.
Acceptance: main-game/postgame playthroughs, representative optional content, battle regression vectors, long sessions, clock rollover, interruption and save recovery. No unknown command is silently skipped. The full port is not complete until these checks pass.
## 4. Modern mobile presentation and delivery
The first presentation increment replaces the landing page and handheld frame with a restrained Crystal-style interface. Game, Savegames, Map, Pokédex, Link and Friends stay above a viewport-filling native canvas. The renderer draws more world tiles as space grows instead of stretching a 160 × 144 image. The game and Map tab share a live local minimap; the atlas and unsupported tabs remain honest planned states. The linked UI reference could not be inspected through automated access because the site returned a Cloudflare challenge, so exact screenshot matching is not claimed.
Build touch interfaces directly over native models: party, pack, battle commands, PC and Pokégear. Keep UI separate from gameplay calculations. Add scalable text, configurable controls and accessible focus/announcements.
Replace the single logical native save with real slots only after party, inventory and story state have stable schemas. Each slot needs a durable ID, display metadata, independent autosave and explicit create, load, rename, duplicate, export, import and delete flows. Version every persisted model. Migrate the current singleton save into a default slot exactly once, preserve unknown or newer backups without rewriting them, and use atomic writes with recovery from the last valid record. Import must preview identity, version and conflicts before replacement.
Dependencies: stable serialization for every system stored in a slot, migration fixtures for every released schema, quota/error handling and a transaction boundary that also covers future Link and walking credits.
Acceptance: migrate existing browser data without loss, keep several independent playthroughs, recover from interrupted writes and reject incompatible or corrupt imports without changing a valid slot. Test create/load/delete/rename/duplicate, autosave isolation, export/import conflicts and upgrades from every released schema.
Add PWA installation/offline caching of the app shell only. ROM/data retention remains a separate explicit local preference; otherwise reselect the ROM after reopening. Add safe application updates, schema migrations, storage persistence/quota handling and portable backups. Investigate original `.sav` import only with verified structure, checksums and RTC conversion.
Acceptance: installation, offline startup, portrait/landscape, touch/audio, background/resume and updates on physical iPhone/Safari and Android/Chrome. Browser automation does not establish physical-device compatibility.
## 5. World map and Pokédex
Promote the current-area minimap into a Johto/Kanto atlas after all map groups, connections and location names can be decoded from the selected ROM. Derive region geometry, visited areas, player location and Fly destinations from native map and story state. Do not ship copied map artwork or claim navigation through maps whose collisions and events are still unsupported.
Build the Pokédex from typed ROM decoders for species names, base data, evolutions, moves and graphics. Seen, caught, forms and habitat or area views must come from native game state and encounter tables. Keep ROM-derived records unavailable until a verified ROM is selected.
Dependencies: complete map indexing and connections, stable player/story flags, party and capture flow, encounter decoding, species graphics and save migrations.
Acceptance: atlas positions and Pokédex records match pinned PRET fixtures and local-ROM comparisons; visited, seen and caught state survives slot switching, backup round trips and schema upgrades. The UI must distinguish decoded data from progress the player has actually unlocked.
## 6. Internet Link and friends
Implement a game-level protocol over native state. This architecture does not need to tunnel serial timing or synchronize GB CPU cycles.
Separate `LinkTransport`, `TradeSession` and `BattleSession`. Start with local peers, then private invite rooms using signaling and WebRTC data channels or a relay. Negotiate compatible schema/game versions and exchange only required game data. Never transmit ROMs or extracted assets.
Trades need durable preparation, a transaction ID, agreed completion, idempotent recovery and pre-trade backups for both players. Do not retry ambiguous completion automatically. Battles need deterministic state, agreed randomness, turn commands, state checks and a disconnect policy. Define validation, room expiry and persistence before public service access.
Add a separate account and relationship layer before Friends becomes active. Define a stable public identity that does not expose save-slot or ROM data. Support private invite creation, acceptance and expiry; friend removal; user blocking; and abuse reporting if user-generated text is ever added. Presence is opt-in, coarse and independently disableable. Privacy controls must cover discoverability, invite permissions and online status. Blocking must prevent new invites, presence disclosure and Link sessions in both directions.
Dependencies: native party, PC, battle and save-slot transactions; a versioned wire protocol; authenticated identity; signaling/relay operations; account recovery and deletion; rate limits, moderation operations and a published privacy policy. Link and Friends remain unavailable until these dependencies have production ownership and failure handling.
Acceptance: cross-network trades/battles and induced duplicate messages, latency, disconnects and reconnects at every phase, without duplication or lost Pokémon. Verify invite expiry, simultaneous relationship changes, remove/re-add, block/unblock, hidden presence, account deletion and attempts to join with incompatible game or schema versions.
## 7. Optional Pokéwalker-style egg assistance
This optional mobile feature uses real-world walking to assist egg hatching after native party, breeding, eggs and faithful in-game hatching are complete. The pinned Crystal engine decrements party egg counters from overworld walking in [`CountStep`](https://github.com/pret/pokecrystal/blob/7a7881d0d62e0ddbd82dcf10e7116807487ac651/engine/overworld/events.asm#L866-L900) and [`DoEggStep`](https://github.com/pret/pokecrystal/blob/7a7881d0d62e0ddbd82dcf10e7116807487ac651/engine/pokemon/breeding.asm#L174-L196); battles do not advance them. A battle-based bonus would therefore be an explicit, separately enabled extension rather than Crystal behavior.
Separate a platform `StepProvider`, local activity ledger and reward rules. Request read access to steps only, after an explicit explanation and opt-in. Keep real-world steps distinct from the in-game tile counter, show what interval will be credited and provide a disconnect/delete control. Do not request distance, heart rate, workouts or permission to write health records. Keep raw activity data on device by default.
An ordinary PWA cannot directly query Apple HealthKit, Core Motion pedometer history, Android Health Connect or Android step sensors. Reliable pocket and locked-screen behavior therefore needs a native companion or wrapper and a narrow bridge into the web game. On Apple platforms, evaluate read-only HealthKit step counts and Core Motion `CMPedometer`; HealthKit requires the capability, a purpose string and per-type authorization, while Core Motion exposes historical pedometer queries. On Android, prefer Health Connect `READ_STEPS` where available and evaluate `TYPE_STEP_COUNTER` with `ACTIVITY_RECOGNITION` as a device-local fallback. Handle absent hardware, unavailable services, denied or revoked access and partial history without blocking the game.
Normalize provider records into immutable local intervals with provider/source IDs, start/end times and step totals. Deduplicate overlapping samples before reward calculation. Commit each credit as one transaction containing a unique credit ID, the source interval set, the target slot and egg identity, the applied hatch-counter delta and resulting state version. Persist the ledger and game mutation atomically. Restoring an older backup must not make an already consumed interval eligible again; trading, moving or hatching an egg must close or retarget pending credits explicitly; wall-clock or timezone changes must not create new credit. Sensor callbacks only append provider observations, never mutate game state.
The first reward is bounded egg-hatch assistance for an egg currently in the party. Define and test the conversion rate and daily cap before release, never reduce an egg below the normal hatch trigger outside a safe game checkpoint, and show the pending effect before applying it. No party or no eligible egg means steps remain unspent or expire according to a documented rule; do not silently redirect them to another reward.
Store distribution is a release dependency, not an assumption. Apple says HealthKit must be used for health and fitness purposes and its review rules restrict health and Motion data use, so a game-only reward may not qualify for HealthKit/App Store approval. Google Play lists health-integrated games as an approved health-permission use case, but still requires a Health apps declaration and review of the requested permissions. Recheck both policies before implementation and submission. Core Motion or a device step sensor may reduce health-store scope, but still requires native permissions and review.
Dependencies: native party, breeding, eggs and hatching; stable egg identity; save-slot transactions and migrations; a native iOS and Android host; permission/privacy UI; and confirmed store eligibility for the chosen provider.
Acceptance: on supported physical iPhone and Android devices, a real walk with the phone locked is read with steps-only consent, credited to an eligible egg exactly once after reopening, and safely reaches the normal hatch flow. Exercise duplicate and overlapping samples, provider corrections, permission revocation, device reboot, offline use, timezone and clock changes, backup restore, slot switching, trade, egg movement and hatch between observation and credit. Denial, unsupported hardware and failed review eligibility leave the base game fully usable.
Official platform references: Apple [HealthKit authorization and capability setup](https://developer.apple.com/documentation/xcode/configuring-healthkit-access), [Core Motion pedometer history](<https://developer.apple.com/documentation/coremotion/cmpedometer/querypedometerdata(from:to:withhandler:)>) and [App Review Guidelines 2.5.1 and 5.1.3](https://developer.apple.com/app-store/review/guidelines/); Android [Health Connect step access](https://developer.android.com/health-and-fitness/health-connect/features/steps), [step counter sensor guidance](https://developer.android.com/develop/sensors-and-location/sensors/sensors_motion) and Google Play [Android health permissions guidance](https://support.google.com/googleplay/android-developer/answer/12991134?hl=en).
## Working method
For each subsystem: identify PRET routines/data, implement native code behind a narrow interface, compare controlled inputs against the reference, then integrate a playable scenario. Retain synthetic fixtures for public CI. Keep real-ROM state, graphics, audio, screenshots and reference builds in ignored local locations.
See [NATIVE_PORT.md](NATIVE_PORT.md) for the current routine map and verification commands.