Open Kanto: the S.S. Aqua, the Kanto gyms, the Magnet Train, Mt. Silver and Red (Closes #33)
Checks / web (push) Failing after 18s
Private ROM checks / private-rom (push) Failing after 25m40s

All 146 Kanto maps, Route 22, Route 28 and Silver Cave are open. The
Magnet Train, Snorlax and the Poké Flute channel, the Diploma, the Trainer
House, Bill's grandpa, Oak's Pokédex rating in Pallet and Goldenrod's
postgame move tutor are ported; big objects cover 2x2 tiles. The Magnet
Train and the Diploma are compared with the cartridge frame by frame.

The playthrough continues from the Hall of Fame through the S.S. Aqua,
the eight Kanto badges, Oak opening Mt. Silver and Red's defeat and
credits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
kawaandClaude Opus 5.5 committed 2026-10-17 15:53:57 +02:00
1 parent f05fd8ebee
commit d3be732118
22 files changed
+1877 -31

No files matched your search

+4 -2
View File
@@ -51,7 +51,7 @@ These paths refer to the pinned [PRET source](https://github.com/pret/pokecrysta
| `game/scripts/screen.ts`, `game/rendering/tile-screen.ts`, `game/rom/font.ts` | `home/tilemap.asm`, `engine/gfx/load_font.asm` (`_LoadStandardFont`, `LoadFrame`) | Tile screens: a 20x18 tilemap with background palettes and sprites over it, built by a minigame or special from ROM graphics and answered with buttons. Blinking sprites follow bit 4 of the frame counter. Overlay screens (a menu box) leave the map visible around them. The standard font and the option's textbox frame load as 2bpp tiles. |
| `game/scripts/unown-puzzle.ts` | `engine/games/unown_puzzle.asm`, `engine/events/specials.asm` (`UnownPuzzle`), `engine/gfx/cgb_layouts.asm` (`_CGB_UnownPuzzle`) | `_UnownPuzzle`: `InitUnownPuzzlePiecePositions` with Random, the d-pad rules, picking up and placing pieces, `CheckSolvedUnownPuzzle`, and the graphics (`ConvertLoadedPuzzlePieces`' doubling, the piece borders, the cursor and START/CANCEL, the OAM layouts) and palettes from the ROM. Sounds are not played. |
| `game/scripts/ruins.ts` | `engine/events/unown_walls.asm`, `data/events/unown_walls.asm` | `DisplayUnownWords` as an overlay screen with the chamber tileset's Unown letters, `HoOhChamber` and `OmanyteChamber`. |
| `game/scripts/goldenrod.ts`, `game/world/day-timers.ts` | `engine/events/name_rater.asm`, `haircut.asm`, `lucky_number.asm`, `engine/overworld/time.asm`, `engine/menus/intro_menu.asm` | Name Rater ownership checks, ROM haircut happiness tables, Lucky Number party and box matching with first-winner ties, saved number and Friday countdown with 140-day rollover. |
| `game/scripts/goldenrod.ts`, `game/world/day-timers.ts` | `engine/events/name_rater.asm`, `haircut.asm`, `lucky_number.asm`, `move_tutor.asm` (`MoveTutor`, `CheckCanLearnMoveTutorMove`), `engine/items/tmhm.asm` (`ChooseMonToLearnTMHM`), `engine/overworld/time.asm`, `engine/menus/intro_menu.asm` | Name Rater ownership checks, ROM haircut happiness tables, Lucky Number party and box matching with first-winner ties, saved number and Friday countdown with 140-day rollover. The move tutor's three moves come from TMHMMoves after the HMs; its party menu marks ABLE or NOT ABLE and returns to itself until a Pokémon learns the move or B. |
| Goldenrod shops, gifts and prizes | `engine/items/mart.asm`, `engine/pokemon/move_mon.asm` (`GivePoke`), `engine/events/specials.asm` (`GameCornerPrizeMonCheckDex`), `engine/pokedex/new_pokedex_entry.asm` | All five mart types, gift OT/nickname and MAIL, full-party gifts to the box, coin commands and balance boxes, and first-owned prize registration with two A/B pages. Boxed gifts approximate an unspecified stale enemy held item as zero. SlotMachine and CardFlip run as described below. |
| Route 35, Route 36, Route 37, Ecruteak and Burned Tower | `home/trainers.asm` (`_CheckTrainerBattle`), `mobile/mobile_41.asm` (`CheckMobileAdapterStatusSpecial`), `engine/overworld/overworld.asm` (`LoadUsedSpritesGFX`, `RefreshSprites`) | These maps' scripts (Floria and the Sudowoodo Squirtbottle battle, Arthur, Sunny, the Itemfinder house, the Kimono Girls and HM03 Surf, Eusine and the rival's Burned Tower cutscene) run on already-ported primitives. `checkTrainers` skips a map trainer whose script is `ObjectEvent` (the Burned Tower rival) once its own flag is set: the original reads `ObjectEvent`'s bytes as flag $f251, bit 1 of `wNumKeyItems` through echo RAM. `tests/reference-trainer-sight.spec.ts` runs `_CheckTrainerBattle` for every `wNumKeyItems` 0-6: the original matches the port only with two, three or six key items; otherwise it reports a sighting and `LoadTrainer_continue` copies `ObjectEvent`'s own bytes into `wTempTrainer` as a trainer header (class $26 SWIMMERM, ID $16), one past SWIMMERM's last trainer. `ReadTrainerParty`'s name-skipping loop then runs off the end of its table into the next one with no padding between them, landing on a real, undamaged record: SwimmerF's ELAINE (a single level 21 Staryu), shown with SWIMMERM's sprite, cry and class text — confirmed by running `ReadTrainerParty` and `Battle_GetTrainerName` themselves. That battle is never reached cleanly: `StartBattleWithMapTrainerScript` first prints the decoded seen text ($46ee in the map scripts' bank, the middle of `SproutTower1FStatueText`). Its first byte, the space $7f, is dispatched as a text command; `TextCommands` has 23 entries, so the pointer comes from home code beyond the table and execution jumps to $0428, inside the VBlank handlers. What follows depends on the whole CPU and stack state, so the port keeps skipping the sighting rather than inventing an outcome. The pursuit stops here: the trainer check runs before `CheckTileEvent`'s warp, so the sighting is reachable after the cutscene's `warpcheck`, but no end-to-end emulator replay of the scene was made (the repository has no cartridge-save export). `CheckMobileAdapterStatusSpecial` is unused outside the Japanese release and always clears its result; `LoadUsedSpritesGFX`/`RefreshSprites` are VRAM sprite tile loading, presentation only. |
| `game/world/roam-mons.ts` | `engine/overworld/wildmons.asm` (`InitRoamMons`, `CheckEncounterRoamMon`, `UpdateRoamMons`, `JumpRoamMons`), `data/wild/roammon_maps.asm` | Raikou and Entei roam from `InitRoamMons`; Suicune's slot is kept but not started. `UpdateRoamMons` runs on connections, door and pit warps and after one wild battle in sixteen; `JumpRoamMons` on Fly, Teleport and Continue. A roaming beast can take a grass encounter, and the battle writes its HP back or clears the slot. |
@@ -62,6 +62,8 @@ These paths refer to the pinned [PRET source](https://github.com/pret/pokecrysta
| `game/party/day-care.ts`, `game/scripts/day-care.ts`, Day-Care and Route 34 | `engine/events/daycare.asm`, `engine/pokemon/breeding.asm`, `engine/pokemon/move_mon.asm`, `engine/pokemon/breedmon_level_growth.asm`, `DayCareStep`, `_GiveOddEgg` | Deposit refusals, level growth/price, move learning on retrieval, compatibility and egg moves/DVs/species, the egg countdown/chance, collection and Odd Egg tables run from the user's ROM. Retrieval keeps the original experience truncation. The save holds both parents and the prepared egg, active/compatible bits, countdown and mother index, with flag consistency validation and absence migration. Day-Care map objects resolve their deposited species' ROM icons. Six controlled-RNG cartridge comparisons cover routine state and byte consumption; native tests cover map scripts and save/reload. Keeper UI/audio timing and fence OAM/palettes are not reference-validated. Hatching is the row above. |
| `game/party/hall-of-fame.ts`, `game/scripts/hall-of-fame.ts`, `game/scripts/pc.ts` | `engine/events/halloffame.asm` (`GetHallOfFameParty`, `_HallOfFamePC`, `LoadHOFTeam`, `DisplayHOFMon`), `engine/menus/save.asm` (`AddHallOfFameEntry`), `engine/events/pokecenter_pc.asm` (`.ChooseWhichPCListToUse`, `HallOfFamePC`) | Up to 30 recorded teams, newest first, with the capped win count; saved and migrated by absence, separate from the League flag. Nothing records a team until the League is ported. The PC lists HALL OF FAME from the count; pages browse with A, Start and B as the original does, and keep its unreachable "HOF Master!" title. A cartridge comparison of 255 teams checks the tilemap, attributes, graphics and palette; the frontpic animation (`HOF_AnimateFrontpic`) is not presented. |
| `game/scripts/league.ts`, the League, the Hall of Fame and the credits | `engine/events/halloffame.asm` (`HallOfFame`, `RedCredits`, `AnimateHallOfFame`, `AnimateHOFMonEntrance`, `HOF_SlideBackpic`, `HOF_SlideFrontpic`, `HOF_AnimatePlayerPic`), `engine/movie/credits.asm` (`Credits`, `ParseCredits`, `Credits_LoadBorderGFX`, `Credits_LYOverride`, `ConstructCreditsTilemap`, `GetCreditsPalette`), `engine/movie/init_hof_credits.asm`, `engine/events/prof_oaks_pc.asm` (`ProfOaksPCBoot`, `ProfOaksPCRating`, `Rate`, `FindOakRating`), `engine/gfx/player_gfx.asm` (`GetPlayerBackpic`, `HOF_LoadTrainerFrontpic`), `home/vblank.asm` (`VBlank_Credits`), `engine/overworld/scripting.asm` (`Script_halloffame`, `Script_credits`, `Script_wait`), `home/text.asm` (`PlaceEnemysName`) | The ceremony records the team and win, pauses the play time and shows each Pokémon's slides and page, the player's card and Oak's rating; the credits run frame by frame (text by thirds, banner tiles by halves, palette VBlanks, border scroll), with each command group's measured spill frames; after the Hall of Fame the world continues at New Bark as Continue would, after Red at Mt. Silver. Checked against the cartridge by `tests/reference-credits.spec.ts` and `tests/reference-hall-of-fame-ceremony.spec.ts`. |
| `game/scripts/kanto.ts` | `engine/events/specials.asm` (`SnorlaxAwake`, `TrainerHouse`), `engine/events/haircut.asm` (`BillsGrandfather`), `engine/events/diploma.asm` (`PlaceDiplomaOnScreen`, `Diploma`, `PrintDiploma`), `engine/gfx/cgb_layouts.asm` (`_CGB_Diploma`) | Snorlax wakes to the Poké Flute channel (wMapMusic) from `.ProximityCoords`; the Trainer House always fights CAL, as without a Mystery Gift partner; Bill's grandpa reads the party Pokémon shown. The Diploma is drawn from DiplomaGFX and its tilemap with the player's name in `PalPacket_Diploma`'s palette; printing it finds no printer. Checked by `tests/reference-kanto.spec.ts`. |
| `game/scripts/magnet-train.ts` | `engine/events/magnet_train.asm` (`MagnetTrain`, `MagnetTrain_Jumptable`, `MagnetTrain_UpdateLYOverrides`, `MagnetTrain_LoadGFX_PlayMusic`, `DrawMagnetTrain`, `MagnetTrainBGTiles`, `MagnetTrainTilemap`), `engine/overworld/scripting.asm` (`Script_newloadmap`'s `MAPSETUP_TRAIN`) | The ride frame by frame both ways: the player's sprite (Chris or Kris) running its animation with the last frame flipped, the bushes' and train's row scrolls, the station's palettes with an extra palette frame and eight frames of teardown. Checked against the cartridge by `tests/reference-magnet-train.spec.ts`. |
| `game/scripts/tin-tower.ts`, Tin Tower and Whirl Islands | `engine/pokemon/search_owned.asm` (`BeastsCheck`, `MonCheck`, `CheckOwnMonAnywhere`, `CheckOwnMon`) | `CheckOwnMonAnywhere` returns nothing with an empty party, then reads the party structs' species (an Egg counts as its species), `sBox` and the thirteen other `sBoxN` copies, matching the player's ID and the OT's first five letters (`NAME_LENGTH_JAPANESE - 2` compares, stopping early at a shared terminator); the Day-Care is not searched, preserving the original's explicitly documented bug even now that deposits run. The original searches the current `sBox` and skips its numbered `sBoxN` copy; the port searches each logical box once. The sages, Suicune, Ho-Oh and Lugia run on ported primitives (`BATTLETYPE_SUICUNE`, `BATTLETYPE_FORCEITEM`). `tests/tin-tower.test.ts` and two playthrough stages cover them. |
| `game/scripts/battle-tower.ts`, the Battle Tower | `engine/events/battle_tower/battle_tower.asm` (`BattleTowerAction`, `RunBattleTowerTrainer`, `ReadBTTrainerParty`, `CopyBTTrainer_FromBT_OT_TowBT_OTTemp`, `BattleTower_GiveReward`, `BattleTower_RandomlyChooseReward`, `LoadOpponentTrainerAndPokemonWithOTSprite`, `CheckForBattleTowerRules`), `engine/events/battle_tower/load_trainer.asm`, `engine/events/battle_tower/rules.asm`, `engine/events/battle_tower/trainer_text.asm`, `mobile/mobile_46.asm` (`_BattleTowerRoomMenu`, `BattleTower_LevelCheck`, `BattleTower_UbersCheck`), `mobile/mobile_5f.asm` (`Menu_ChallengeExplanationCancel`), `engine/link/link.asm` (`TryQuickSave`), `engine/battle/core.asm`, `engine/battle/effect_commands.asm`, `engine/battle/misc.asm` (`DoBadgeTypeBoosts`) | The Battle Tower's SRAM and the work RAM its scripts share are saved as `battleTower`. The trainer sampler keeps its index as the running byte after a repeated trainer; the Pokémon sampler starts again from `hRandomAdd`; both fold `Random`'s `hRandomAdd` into a byte below a power-of-two mask, which the port draws from its random source. `BattleTowerText` reads the class's gender (1.1 behaviour) and picks the greeting from `hRandomAdd` (0-24 male, 0-14 female), keeping it for the end texts. Each battle start counts the battle and marks the challenge in progress, as `CopyBTTrainer_FromBT_OT_TowBT_OTTemp` does; `LoadPokemonData` then returns the party, Pokédex and Unown Dex to the quick save's. The level menu shows the levels as a list; its CANCEL/B question and the mobile code's YES/NO flow are modelled from their outcomes (YES leaves with $a, NO picks a level again). `TryQuickSave` asks the overwrite question for the player's own save and shows the saving texts; the port keeps the place and Pokémon data `LoadPokemonData` and `Reset` return to. The native game saves itself, so the original's "left without saving" state (a reset during a challenge) cannot arise: a reload replays the room's script. `Reset` returns the player to the quick save's place with the 1F scene set to check the challenge; the original's rollback of money gained by Pay Day is not reproduced. In battle, `wInBattleTowerBattle` turns off obedience, `DoBadgeTypeBoosts`, `BadgeStatBoosts`, experience, the pack (BattleMenu_Pack says items can't be used), AI items and prize money, uses Falkner's AI attributes, keeps Sleep to `%011` and skips the AI's random failure for Sleep, Poison, stat-lowering and Paralyze moves; a loss shows BattleTowerText and does not white out. `DoBadgeTypeBoosts` was added to every battle (it was missing). BATTLETOWERACTION_LEVEL_CHECK and _UBERS_CHECK and the mobile actions are reached only from unreferenced scripts and stay refused. `tests/battle-tower.test.ts`, `tests/battle-tower-engine.test.ts` and `tests/reference-badges.spec.ts` cover them. |
| `game/scripts/slot-machine.ts`, `game/scripts/card-flip.ts` | `engine/games/slot_machine.asm`, `engine/games/card_flip.asm`, `engine/gfx/cgb_layouts.asm` | Both on a realtime tile screen (two frames per tick, buffered buttons). Slots: reel strips, spin rates and scrolling, bias tables with the lucky machine and seven streak, reel 3's Golem and Chansey replay with their sprites, payouts. Card Flip: deck, 12×11 board, cursor sprites and `ChooseCard_HandleJoypad`, paytable. Tilemaps, all 48 cursor positions, selected reel states and whole Golem/Chansey/Egg sequences are checked against the ROM (`tests/reference-game-corner.spec.ts`, which runs `CardFlip_UpdateCursorOAM`, `Slots_UpdateReelPositionAndOAM`/`.SpinReel` and the sprite-animation engine's own Golem/Chansey/Egg routines): the cursor, the reel symbols and the three creatures each anchor an OAM-relative pixel pair 8 (x) and 16 (y) beyond the screen's, as `cursorSprites` reads; `depixel`'s own two arguments end up swapped by `_InitSpriteAnimStruct` (confirmed against the reference emulator), so Golem/Chansey/Egg's X and Y bases were swapped to match, and Golem's own OAM table X-flips its right half. `PlaySpriteAnimations` is compared frame by frame over a three-Golem run and a Chansey round with two eggs (shadow OAM, `wSlotsDelay` and `hSCY`): frames last 8 ticks (the first draw plus 7 decrements); Golem's frames 3 and 4 flip the whole OAM set (offsets become −8−offset); a sprite removed by `DeinitializeSprite` is still drawn that tick, because `UpdateAnimFrame` runs after the animation function; the Golem's roll toggles `hSCY` by ±2, which the tile screen shows with the space tiles `_SlotMachine` filled the BG map with. The rows scrolled in use palette 0, since the attribute map beyond row 18 is not reproduced. No sound. |
@@ -415,7 +417,7 @@ message placement and palettes against the cartridge. Start prints; A/B
close the letter. `scripts/printer.ts` shares the absent-printer error with
Bill's PC, Pokédex PRNT and Photo Studio; B cancels and A leaves it open.
Print previews, printer music and alternating link-check status are omitted;
Unown stamps and the Kanto diploma remain unreachable. A scripted
Unown stamps remain unreachable; the Diploma prints to the same absent printer. A scripted
`givepoke` with MAIL (`GivePokeMail`) is refused.
`Pack_InterpretJoypad`'s Select runs `SwitchItemsInBag` on the bag
+14 -4
View File
@@ -481,7 +481,7 @@ The numbered sections below keep their full scope. Each one opens with a checkli
- The radio (`game/scripts/radio.ts`): `PlayRadioShow` and every station frame by frame: Oak's Pokémon Talk (routes, Pokémon, adverbs and adjectives, the Pokémon Channel jingle), the Pokédex Show, Pokémon Music and Let's All Sing (march or lullaby by weekday), the Lucky Channel (with `CheckLuckyNumberShowFlag`), Places and People, Buena's Password (from 6 PM, the day's password; midnight and off-air texts), Team Rocket's broadcast during the takeover, and the Unown, Poké Flute and Lake of Rage stations' music. Lines show for 100 frames and scroll as `PrintRadioLine` and `RadioScroll` do. Station songs become the map music and keep playing after the radio is left, as in the original (`ExitPokegearRadio_HandleMusic`).
- The Pokégear's radio card runs every frame: up and down turn the knob by two, `RadioChannels` picks the station the place can hear (Johto, the Ruins, the Lake of Rage area, Kanto with the EXPN card). House radios (`Radio1Script`, `Radio2Script`) run `MapRadio`: the station's name in quotes for 100 frames, then the station until A or B.
- Buena's show at the Radio Tower: `AskRememberPassword`, `BuenasPassword` (the category's three words, B disabled) and `BuenaPrize` (prizes and points beside the Blue Card balance, confirm, pay, receive). Tests: `tests/phone.test.ts` (rematches, a random call, no call without a caller, the daily reset, swarms, each station's lines and music, the radio card, a house radio, Buena's password and prizes) and `tests/radio-browser.spec.ts` (the radio card in the browser).
- Not ported: the radio card's tuning dial graphics (the frequency is shown as text), holding up or down to turn the knob continuously, the Poké Flute channel waking Snorlax (Kanto is closed), and calls that use the mobile adapter.
- Not ported: the radio card's tuning dial graphics (the frequency is shown as text), holding up or down to turn the knob continuously, and calls that use the mobile adapter. The Poké Flute channel wakes Vermilion's Snorlax (board #33, increment B).
## Implemented: save slots (board #37)
@@ -495,7 +495,7 @@ The numbered sections below keep their full scope. Each one opens with a checkli
## Implemented: MAIL stationery and printer cancellation
- `ReadAnyMail` now draws all ten stationery types from the ROM, including their color planes, palettes, message/author positions and the mirrored Portrait MAIL picture (Unown A). A/B return; Start opens printing, and B cancels back to the unchanged letter. The accessible message and author remain available with the canvas.
- Nonempty Bill's PC boxes, Pokédex PRNT, MAIL and the Photo Studio share `SendScreenToPrinter`'s absent-printer result (`PRINTER_ERROR_2`). A does not dismiss the error; B cancels. Empty boxes still show their original refusal. The print preview, printer music and alternating link-check status are not presented. Unown stamps and the diploma remain unreachable with the unported Unown mode and Kanto maps.
- Nonempty Bill's PC boxes, Pokédex PRNT, MAIL and the Photo Studio share `SendScreenToPrinter`'s absent-printer result (`PRINTER_ERROR_2`). A does not dismiss the error; B cancels. Empty boxes still show their original refusal. The print preview, printer music and alternating link-check status are not presented. Unown stamps remain unreachable with the unported Unown mode; the Diploma prints to the same absent printer.
- Controlled cartridge comparisons cover all ten stationery tilemaps, used graphics, message/author placement and palettes; the font is loaded separately from ROM. Chromium and mobile WebKit checks cover writing, reading, printing and returning to MAIL. Unit checks cover the four reachable print paths and unchanged stored data.
## Implemented: Day-Care and breeding (board #29, egg creation)
@@ -539,11 +539,21 @@ The numbered sections below keep their full scope. Each one opens with a checkli
- Tests: `tests/league.test.ts` (Route 27's first step into Kanto, the gate's badge check, the ceremony's records without Eggs, the paused play time, the 200-win cap and 30 teams, B on the credits, the text-box decoder, the renderer's wrap and row scroll). The playthrough gains five stages: back to Ice Path for HM07, home to New Bark, Route 27, Tohjo Falls' waterfall, Route 26 and Victory Road, Indigo Plateau and the Elite Four, Lance and the Hall of Fame (the driver now fights carefully from here: accuracy-weighed moves, Full Heals for poison, paralysis and burns, healing below two thirds). Test saves: `indigo-plateau`, `hall-of-fame-new-bark`, `hall-of-fame-ceremony`.
- Not presented: `ANIM_MON_HOF` (pictures at rest, as in the PC), the credits' first 36 frames of setup are shown white without their exact frame count, the rating text appears whole rather than letter by letter. The Plateau's rival (after Mt. Moon) and Kanto are increments B and C.
## Implemented: Kanto, Mt. Silver and Red (board #33, increment B)
- Open: Olivine's and Vermilion's ports and the S.S. Aqua (Elm's S.S. Ticket, the lost girl, the lazy sailor and the captain), all 146 Kanto maps (Vermilion, Saffron and its stations, Cerulean, the Power Plant, Lavender and its Radio Tower, Rock Tunnel, Celadon, Fuchsia, Cinnabar and the Seafoam Gym, Pallet Town and Oak's lab, Viridian, Pewter, Diglett's Cave, Mt. Moon, Cycling Road and every route between them), Route 22, Route 28, Silver Cave and Red. Only the Pokémon Centers' upstairs (the link rooms and the unused beta floors) and the beta Safari Zone stay closed.
- `MagnetTrain` (`game/scripts/magnet-train.ts`): the ride both ways, frame by frame (`MagnetTrain_Jumptable`, `MagnetTrain_UpdateLYOverrides`, the player's sprite animation flipped on its last frame, the scrolling bushes and train bands, the extra palette frame and eight frames of teardown); `newloadmap` with `MAPSETUP_TRAIN` arrives at the other station.
- `SnorlaxAwake`: the Poké Flute channel left playing on the Pokégear's radio, beside Vermilion's Snorlax (`.ProximityCoords`). Big objects (`BIG_OBJECT`) cover 2×2 tiles for collisions and talking. `Diploma` and `PrintDiploma` (`PlaceDiplomaOnScreen`, with `PREDEFPAL_DIPLOMA`; no printer, B cancels). `TrainerHouse` fights its default trainer, CAL (no Mystery Gift partner). `BillsGrandfather` shows a party Pokémon. `ProfOaksPCBoot` lets Oak rate the Pokédex in Pallet before he opens Mt. Silver for sixteen badges.
- `MoveTutor` (`game/scripts/goldenrod.ts`): Goldenrod's tutor on Wednesdays and Saturdays after the League, for 4000 coins, through `ChooseMonToLearnTMHM`'s ABLE/NOT ABLE party menu (an Egg plays SFX_WRONG, an incompatible Pokémon or one that knows the move returns to the menu) and `LearnMove`.
- Reference: `tests/reference-magnet-train.spec.ts` runs the cartridge's `MagnetTrain` with interrupts after a boot and compares every frame (OAM, the hSCX bands, BG map and attributes, the icon's tiles and the palettes in CRAM) both ways, for Chris and Kris. `tests/reference-kanto.spec.ts` compares `PlaceDiplomaOnScreen` (tilemap, tiles and palette) for several names and frames.
- Tests: `tests/kanto.test.ts` (Snorlax's coordinates, 2×2 block and flute battle; Bill's grandpa's Everstone; the Trainer House's CAL; the Magnet Train both ways; the Diploma and its printer; Oak opening Mt. Silver; the move tutor; the rival in Mt. Moon and at Indigo Plateau on Mondays but not Tuesdays). The playthrough continues with fourteen stages: the S.S. Aqua, the eight Kanto badges in the order Thunder, Marsh, Cascade (with the Power Plant's manager, the Machine Part and Misty's date), Boulder (after the power returns, Rock Tunnel, the EXPN card and Snorlax), Volcano, Soul (Blue on Cinnabar), Rainbow (Cycling Road) and Earth, Oak opening Mt. Silver, training outside Silver Cave, TM26 from Victory Road, and Red with the credits, ending at `SPAWN_MT_SILVER`. Test saves: `vermilion-city`, `saffron-city`, `celadon-city`, `mt-silver`, `red-beaten`.
- Not presented: Mystery Gift's Trainer House opponents and printing; the Diploma's B prompt cursor (the page has no ▼).
## Native battles: current state
The battle core, ROM tables, encounter hooks, battle UI and whiteout hooks are present. Unit tests cover wins, escape, loss, experience, move learning and capture; reference tests compare damage, type effectiveness, stat calculation and experience curves. Browser tests drive a test-only wild encounter through the real UI: victory with level-up and move learning, escape, whiteout, catching with a nickname, keyboard and on-screen D-pad menu navigation, mid-battle backup restore, rollback of unsupported effects, switching through the party menu and a Potion from the Items pocket. Real Route 29 encounters now use the ROM's encounter tables. Every ball can be thrown; only the contest's Park Ball handling remains.
This is still not a complete new game. The Pokédex's area view, search and other modes, Kanto and the postgame remain unfinished. The start menu opens the party menu and the pack, and Select uses the registered item, such as the Bicycle; the PCs store Pokémon, items and decorations. Surfing and terrain animation now have native implementations; Kanto's maps remain gated. Save slots, online Link, friends and real-world steps come later.
This is still not a complete new game. The Pokédex's area view, search and other modes remain unfinished. The start menu opens the party menu and the pack, and Select uses the registered item, such as the Bicycle; the PCs store Pokémon, items and decorations. Surfing and terrain animation now have native implementations. Save slots, online Link, friends and real-world steps come later.
## 1. Complete the overworld foundation
@@ -635,7 +645,7 @@ Acceptance: finish the opening quest, including encounters and the rival battle,
- [x] Breeding, eggs and the rest of PC storage (board #29)
- [ ] Field moves, gyms, Rocket events, the phone and Pokégear, puzzles, minigames, daily events, roaming Pokémon and the postgame
- [x] Johto beyond Route 30: every city, gym, Rocket event, puzzle and minigame to the Rising Badge, Tin Tower, Whirl Islands and the Battle Tower
- [ ] The League, Kanto and the final battle
- [x] The League, Kanto and the final battle (board #33)
- [x] Music and sound through Web Audio (board #34; move animations and minigame sounds remain)
- [ ] Acceptance: main-game and postgame playthroughs, battle regression vectors, long sessions and save recovery