diff --git a/README.md b/README.md index 298289c..9b1aeae 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,11 @@ North of Cherrygrove, Route 30's trainers battle you. Bug Catcher Don spots you Mr. Pokémon gives you the Mystery Egg, and Professor Oak gives you the Pokédex. Your rival challenges you in Cherrygrove on the way back, and Elm's aide gives you Poké Balls. Back on Route 29, the Dude shows you how to catch Pokémon. From then on, open PACK in a wild battle to throw a Poké Ball. Caught Pokémon join your party, and you can give them a nickname. In battle, PKMN switches Pokémon or opens a Pokémon's stats, and when one faints you choose the next. PACK's Items pocket uses healing items, revives, Ethers, X items and Poké Dolls. -Start opens the start menu. POKéMON shows your party: open a Pokémon's stats, change the order, rearrange its moves, or give and take held items. PACK shows your pockets: use Potions, status healers, revives, Ethers, PP Ups, vitamins, Rare Candy and evolution stones, give or toss items, and ride the Bicycle. Pokémon evolve when they level up after a won battle (press B during the flashes to stop it), and learn new moves outside battle too. Select opens Key Items, including the Bicycle when owned. The poster opens the Town Map. +Start opens the start menu. POKéMON shows your party: open a Pokémon's stats, change the order, rearrange its moves, or give and take held items. PACK shows your pockets: use Potions, status healers, revives, Ethers, PP Ups, vitamins, Rare Candy and evolution stones, give or toss items, and ride the Bicycle. Pokémon evolve when they level up after a won battle (press B during the flashes to stop it), and learn new moves outside battle too. Field moves work from the party menu too: Cut, Surf, Strength, Flash, Whirlpool, Waterfall, Sweet Scent, and Softboiled or Milk Drink to share HP. In PACK, SEL registers a key item such as the Bicycle, and Select then uses it. Bill's PC can move Pokémon between the party and any box. The poster opens the Town Map. The Pokémon Center PC opens Bill's PC, your own PC and, once you have the Pokédex, Professor Oak's rating. Deposit, withdraw and release Pokémon, and switch or name boxes. Store, withdraw or toss items. With six Pokémon in your party, a catch goes to the current box. When that box fills up, Bill calls you. The bedroom PC also lets you arrange owned decorations. -Route 31 and beyond, trade evolutions, the Pokégear, Pokédex and trainer card screens, field moves from the menu, MAIL, moving Pokémon between boxes, Kurt's special balls and audio are still being ported. Unported neighboring areas appear at the edges but cannot be entered yet. +Route 31 and beyond, trade evolutions, the Pokégear, Pokédex and trainer card screens, Fly, Dig, Teleport, Headbutt and Rock Smash, MAIL, Kurt's special balls and audio are still being ported. Unported neighboring areas appear at the edges but cannot be entered yet. ## Run diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index beafa27..a69f326 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -34,7 +34,7 @@ flowchart LR | `src/reference`, `src/emulator` | Original-game reference, excluded from the native build | | `reference/public` | Licensed emulator files for the reference build only | -`GameRuntime` is a lifecycle interface, not an emulator contract. Native state is validated JSON, schema version 3. It holds the map, position, facing, name/avatar, player ID and in-game tile count; event and engine flags and sprite variables; scene values, phone contacts, a queued special call, the rival's name, the game clock's minute offset, the weekday offset and DST flag, whether the introduction is still pending; positions of objects that scripts moved; and the party, bag, Pokédex sets, money and room decoration slots. Older saves without decorations migrate to the original bed and Town Map poster. Decoration imports validate slot categories, ownership and unique doll placement. Early version-3 backups without a money field migrate to the starting balance. It contains no graphics, music, dialogue, GB memory or ROM bytes. Versions 1 and 2 migrate with new-game defaults. Party Pokémon are validated by recomputing their stats from the ROM. The backup envelope retains the historical `coreId` field; native backups use it for their runtime/schema ID. +`GameRuntime` is a lifecycle interface, not an emulator contract. Native state is validated JSON, schema version 3. It holds the map, position, facing, name/avatar, player ID and in-game tile count; event and engine flags and sprite variables; scene values, phone contacts, a queued special call, the rival's name, the game clock's minute offset, the weekday offset and DST flag, whether the introduction is still pending; positions of objects that scripts moved; and the party, bag, Pokédex sets, money, registered item and room decoration slots. Older saves without decorations migrate to the original bed and Town Map poster. Decoration imports validate slot categories, ownership and unique doll placement. Early version-3 backups without a money field migrate to the starting balance. It contains no graphics, music, dialogue, GB memory or ROM bytes. Versions 1 and 2 migrate with new-game defaults. Party Pokémon are validated by recomputing their stats from the ROM. The backup envelope retains the historical `coreId` field; native backups use it for their runtime/schema ID. Saves made during an unfinished walk or jump retain its source tile and completed step count, so loading cannot skip a landing event or map transition. Directional mats wait for exit input; doors, stairs and caves force an animated downward step after arrival. @@ -56,11 +56,13 @@ state in version-3 saves, with defaults for older saves. Forced terrain and turn-in-place timing run through the idle movement path. Dynamic warps and elevators retain their return destination. Block mutations use a per-map working copy and a revision counter, so rollback and the minimap see the same terrain. -Surf, Cut and Strength use the ROM scripts. Select opens the Key Items -menu, pausing overworld ticks while choosing the Bicycle. Start opens +Surf, Cut and Strength use the ROM scripts. Select runs the registered item +through the same module as the start menu. Start opens `scripts/start-menu.ts`, a native run on a script draft like the introduction: the party menu and pack change only the draft, which commits when the menu -closes, and an item that queues a script (the Bicycle) starts it afterwards. +closes, and an item or field move that queues a script (the Bicycle, Cut, +Surf …) starts it afterwards with the context the world checked (the +Pokémon, the block to cut, the surfing state or a Sweet Scent encounter). The start menu, party menu and pocket cursors live in the world's memory, not in saves, and are cleared after each battle. `scripts/evolution.ts` translates `EvolveAfterBattle`, `LearnLevelMoves` and `LearnMove` over a diff --git a/docs/NATIVE_PORT.md b/docs/NATIVE_PORT.md index 38da339..2a8388f 100644 --- a/docs/NATIVE_PORT.md +++ b/docs/NATIVE_PORT.md @@ -27,12 +27,12 @@ These paths refer to the pinned [PRET source](https://github.com/pret/pokecrysta | `game/rom/assets.ts` maps/events | `data/maps/maps.asm`, `data/maps/attributes.asm`, `data/maps/scenes.asm`, `data/maps/landmarks.asm`, `macros/scripts/maps.asm` | Any map header in the ROM's 26 groups decodes; only maps explicitly listed in NATIVE_MAPS can be entered. Location names, scene-variable owners and connection offsets come from the ROM. | | Tiles/palettes | `home/map.asm`, `engine/gfx/color.asm`, `engine/tilesets/mapgroup_roofs.asm` | Graphics match rebuilt reference bytes. Connected maps draw beyond the edges with the current tileset, like the original strips. Native tileset animation and special palettes; Johto, Ice Path, restless Ilex Forest and Tin Tower each match 96 reference VRAM frames. 192 palette cases match the original routine. | | Sprites/rendering | `data/sprites/sprites.asm`, `data/sprites/facings.asm`, `data/sprites/emotes.asm` | Chris/Kris, standard NPCs, emote bubbles and jump shadows decoded. Walking frames follow `SetFacingStepAction`. Variable sprites and Pokémon icons decoded; grass priority supported. Owned room decorations support selection, removal, slot placement, descriptions and validated saves. | -| `game/world/world.ts` | `engine/overworld/events.asm`, `player_movement.asm`, `warp_connection.asm`, `data/collision/collision_permissions.asm` | 30 Hz overworld ticks, 8-tick walks, 16-tick ledge jumps, directional mats, animated door/stair exits, connection coordinates, events, scenes and callbacks. NPC random walks and spins follow `HandleNPCStep`. Forced movement, Surf, Cut, Strength, cycling rules, pits and elevators implemented. Fourteen reference traces compare turn timing, actors and event flags; map-entry boundaries verify the completed destination. Bicycle access uses the Key Items menu (Select) or the pack. | +| `game/world/world.ts` | `engine/overworld/events.asm`, `player_movement.asm`, `warp_connection.asm`, `data/collision/collision_permissions.asm` | 30 Hz overworld ticks, 8-tick walks, 16-tick ledge jumps, directional mats, animated door/stair exits, connection coordinates, events, scenes and callbacks. NPC random walks and spins follow `HandleNPCStep`. Forced movement, Surf, Cut, Strength, cycling rules, pits and elevators implemented. Fourteen reference traces compare turn timing, actors and event flags; map-entry boundaries verify the completed destination. The Bicycle is used from the pack or with Select. Field moves chosen from the party menu are checked here (`menuFieldMove`). | | Event interpreter | `macros/scripts/events.asm`, `engine/overworld/scripting.asm`, `engine/events/std_scripts.asm` | Resumable; yields text, choices, movement, emotes and pauses. Scenes, phone contacts, string buffers and object commands. Unknown commands and specials abort without committing. | | Movement interpreter | `macros/scripts/movement.asm`, `engine/overworld/movement.asm`, `engine/overworld/map_objects.asm` | Turns, steps, slides, jumps (two tiles with the original arc), sleeps, facing locks, visibility and emotes. `follow` takes one tile toward the leader's previous square. All movement opcodes decode, including teleport, fishing, skyfall, shake and flicker poses; Controlled reference scenes check spin, teleport, skyfall, fishing and flicker direction, visibility and lift. | | Specials | `data/events/special_pointers.asm`, `engine/rtc/timeset.asm`, `engine/overworld/decorations.asm` | Selected by routine address from the ROM table: `SetDayOfWeek`, initial DST flags, `RestartMapMusic` (no audio yet), room decoration toggles, `PokemonCenterPC` and `PlayersHousePC` (Bill's, the player's and Oak's PCs), the Town Map, `HealParty`, `HealMachineAnim` (presentation only), `CheckPokerus` and the `StubbedTrainerRankings_Healings` stub. | -| `game/scripts/pc.ts` | `engine/events/pokecenter_pc.asm`, `engine/pokemon/bills_pc_top.asm`, `engine/pokemon/bills_pc.asm`, `engine/events/prof_oaks_pc.asm`, `engine/events/std_collision.asm` | The Pokémon Center and house PCs: Bill's PC (deposit, withdraw, release, change and name boxes), item storage, the empty mail box and Oak's rating. Menu strings and texts come from the ROM; lists are native. Moving Pokémon, the stats screen and printing are not ported. | -| `game/scripts/start-menu.ts` | `engine/menus/start_menu.asm`, `engine/pokemon/mon_menu.asm`, `mon_submenu.asm`, `party_menu.asm`, `switchpartymons.asm`, `engine/items/pack.asm`, `engine/items/item_effects.asm` | The start menu, the party menu with the Pokémon submenu (STATS, SWITCH, MOVE, ITEM) and the pack (USE, GIVE, TOSS) with the field item effects listed below, including Rare Candy, stones and vitamins. Menu strings, prompts and texts come from the ROM; screens are native. Field moves from the menu, MAIL, SEL and the other field items give a notice. | +| `game/scripts/pc.ts` | `engine/events/pokecenter_pc.asm`, `engine/pokemon/bills_pc_top.asm`, `engine/pokemon/bills_pc.asm`, `engine/events/prof_oaks_pc.asm`, `engine/events/std_collision.asm` | The Pokémon Center and house PCs: Bill's PC (deposit, withdraw, release, change and name boxes, MOVE PKMN W/O MAIL, stats), item storage, the empty mail box and Oak's rating. Menu strings and texts come from the ROM; lists are native. Printing is not ported. | +| `game/scripts/start-menu.ts` | `engine/menus/start_menu.asm`, `engine/pokemon/mon_menu.asm`, `mon_submenu.asm`, `party_menu.asm`, `switchpartymons.asm`, `engine/items/pack.asm`, `engine/items/item_effects.asm` | The start menu, the party menu with the Pokémon submenu (STATS, SWITCH, MOVE, ITEM) and the pack (USE, GIVE, TOSS) with the field item effects listed below, including Rare Candy, stones and vitamins. Menu strings, prompts and texts come from the ROM; screens are native. Field moves from the menu (checked by the world), Softboiled and Milk Drink, SEL and the Select button (`SelectMenu`). MAIL and the other field items give a notice. | | `game/scripts/evolution.ts` | `engine/pokemon/evolve.asm`, `engine/pokemon/learn.asm`, `engine/movie/evolution_animation.asm`, `engine/pokemon/party_menu.asm` | `EvolveAfterBattle`/`EvolvePokemon` over the raw evolution bytes, the evolution texts and a timed, B-stoppable picture sequence, `UpdateSpeciesNameIfNotNicknamed`, `CalcMonStats` and the HP rise, `LearnLevelMoves`, `LearnMove`/`ForgetMove` outside battle and `PlacePartyMonEvoStoneCompatibility`. | | `game/rom/pokemon.ts` | `data/pokemon/base_stats`, `evos_attacks.asm`, `data/moves/moves.asm`, `data/items/attributes.asm`, `engine/gfx/load_pics.asm` | Species, move and item names, base data, evolution lists, learnsets, move PP, item pockets, growth-rate exp and front pictures (`FixPicBank`, `PadFrontpic`). Starter pictures match rebuilt bytes; all 251 evolution lists match the pinned source. | | `game/party` | `engine/pokemon/move_mon.asm`, `engine/pokemon/caught_data.asm`, `engine/items/items.asm` | `GeneratePartyMonStats` for script gifts: DVs, `FillMoves`, PP, exp, `CalcMonStatC`, happiness and caught data. `PutItemInPocket`, key items and `TossItem`. `SendMonIntoBox`, `SendGetMonIntoFromBox` (deposit and withdraw), `RestorePPOfDepositedPokemon` and `CalcLevel` for the PC's boxes and item storage. TMs and HMs are refused. | @@ -385,3 +385,55 @@ to its limit. `tests/battle-world.test.ts` wins a wild battle whose level-up evolves Cyndaquil, and checks that a non-participant that could evolve does not. A Chromium/WebKit test evolves through a Rare Candy. No emulator comparison of these routines yet. + +## Field moves from the menu, SEL and MOVE PKMN W/O MAIL + +`PokemonActionSubmenu` passes a field move to the world, which evaluates +the menu function's checks on the menu's draft for the chosen party slot: +`CheckBadge` with `BadgeRequiredText`, `CheckMapForSomethingToCut` +(`CheckCutCollision` and `CutTreeBlockPointers`), `SurfFunction` (Fog +Badge, forced cycling, already surfing, water permission, `CheckDirection` +and `CheckFacingObject`), `StrengthFunction`, `FlashFunction` +(`SpecialAerodactylChamber` by map attributes, which sets +`EVENT_WALL_OPENED_IN_AERODACTYL_CHAMBER`, or `DARKNESS_PALSET`), +`TryWhirlpoolMenu`, `CheckMapCanWaterfall`, and the checks of Fly, Dig, +Teleport, Headbutt and Rock Smash. A success queues `Script_CutFromMenu`, +`SurfFromMenuScript`, `Script_StrengthFromMenu`, `Script_UseFlash`, +`Script_WhirlpoolFromMenu`, `Script_WaterfallFromMenu` or +`SweetScentFromMenu.SweetScent` with the field-move context the interpreter +already reads (`GetPartyNickname`, the replacement block, the surfing +state). New interpreter support: `callasm BlindingFlash` (sets the Flash +flag), `callasm SweetScentEncounter` (the encounter the world drew with +`CanEncounterWildMon`, `GetMapEncounterRate` and `ChooseWildEncounter`, no +rate roll) and `UseFlashTextScript`'s sound-only `text_asm`. Fly's map, Dig +and Teleport's warps, `TreeMonEncounter` and `RockMonEncounter` are not +ported and give a notice after their checks. +`Softboiled_MilkDrinkFunction` runs in the menu with `GetOneFifthMaxHP`, +`RemoveHP` and `RestoreHealth`, refusing the user itself and Pokémon that +fainted or are at full HP. + +`RegisterItem` stores `wWhichRegisteredItem` (pocket in bits 6-7, 1-based +slot in bits 0-5) and `wRegisteredItem`, now saved as `registeredItem`. +`SelectMenu` runs as a start-menu run: `CheckRegisteredItem` (items by slot, +key items anywhere in their pocket, balls never because `.CheckBall`'s +carry test is inverted, TMs never; a miss clears the registration) and +`UseRegisteredItem` by field menu, with `wUsingItemWithSelect` choosing +`Script_GetOnBike_Register`/`Script_GetOffBike_Register`. `MayRegisterItemText` +and `ItemsOakWarningText` come from the ROM. + +`BillsPC_MovePKMNMenu`: `IsAnyMonHoldingMail`, then +`StartMoveMonWOMail_SaveGame`'s question (the native session saves by +itself, so no save messages follow). `_MovePKMNWithoutMail` treats the party +as box 0; lists are presented per box. `BillsPC_CheckMail_PreventBlackout`, +`BillsPC_CheckSpaceInDestination` and `MovePKMNWithoutMail_InsertMon` with +`.CheckTrivialMove`, `CalcBufferMonStats` for a boxed Pokémon joining the +party, and `InsertPokemonIntoBox`'s `RestorePPOfDepositedPokemon`. + +Tests: `tests/overworld-rom.test.ts` uses Surf, Cut, Strength, Flash, Sweet +Scent and Rock Smash from the menu (badge refusals, failure texts, the dark +map and the encounter); `tests/start-menu.test.ts` covers Softboiled, SEL, +Select with and without a registration, indoors, and a registration that +left its slot; `tests/pc.test.ts` moves Pokémon party to box, box to party, +within the party and within a full box, and refuses the last Pokémon and a +full destination. Browser tests register the Bicycle and ride it with +Select from the keyboard and the on-screen button. diff --git a/docs/PLAN.md b/docs/PLAN.md index 79eb3f9..5991f05 100644 --- a/docs/PLAN.md +++ b/docs/PLAN.md @@ -130,7 +130,7 @@ The numbered sections below keep their full scope. Each one opens with a checkli - Capture with a full party runs `SendMonIntoBox`: the Pokémon goes first in the current box, with restored PP and `SetBoxMonCaughtData`, can be nicknamed and shows `BallSentToPCText`. If the box is already full the ball is not used (`Ball_BoxIsFullMessage`). A catch that fills the box sets `BATTLERESULT_BOX_FULL`; `reloadmapafterbattle` then queues Bill's call (`Script_SpecialBillCall`) as a memory script that runs at the next `PlayerEvents` and is not saved, as in the original. - `VAR_BOXSPACE` reads the current box. - Save format 3 gained `storage` (boxes, the current box and box names; a null name is the ROM default) and `pcItems`, migrated by absence to empty storage. -- Not ported: MOVE PKMN W/O MAIL (a notice returns to the menu; the stats screen is now ported), printing, a `givepoke` with a full party (refused; no open map reaches it) and the tutorial with a full party and box (the original repeats the unusable ball). Changing boxes does not show the save messages; the native session saves continuously. +- Not ported: printing (MOVE PKMN W/O MAIL and the stats screen are ported since), a `givepoke` with a full party (refused; no open map reaches it) and the tutorial with a full party and box (the original repeats the unusable ball). Changing boxes does not show the save messages; the native session saves continuously. - Next: the introduction's naming and clock screens, player switching and other items in battle, then party and bag menus. ## Implemented: the introduction's naming and clock screens @@ -169,7 +169,7 @@ The numbered sections below keep their full scope. Each one opens with a checkli - PACK: the Items, Balls, Key and TM pockets (left and right change pockets; there are no TMs or HMs yet), and each item's submenu chosen by its permissions and field menu (`USE`, `GIVE`, `TOSS`, `SEL`, `QUIT`). TOSS asks how many and confirms. USE follows `UseItem`: Oak's warning without a field use, "You don't have a POKéMON!" for party items without one, and `_DoItemEffect` by the item's `ItemEffects` address: Potions and the other `HealingHPAmounts` items, status healers, Full Restore, Revive and Max Revive, the bitter Energypowder, Energy Root, Heal Powder and Revival Herb, Ethers, Elixers, the MysteryBerry and PP Up. The Bicycle closes the menu and runs `Script_GetOnBike` or `Script_GetOffBike` outdoors; elsewhere Oak objects, because `wFieldMoveSucceeded` shares `wItemEffectSucceeded`'s byte. - PP Up keeps a quirk: `wUsePPUp` shares `wTempPP`'s byte, so `ComputeMaxPP` adds one step (a fifth of the base PP, at most 7) to the current PP as well as the maximum. Sketch and moves with three PP Ups are "maxed out". - The menu runs on a script draft, like the PC. Changes commit when it closes; a save made while it is open keeps the state from before it. The start menu cursor, the party menu cursor and the last pocket stay in memory between openings and are cleared after a battle, as `CleanUpBattleRAM` does. The party cursor reaches the next battle's `ChangeHappiness`. -- Select keeps opening the Key Items shortcut, standing in for a registered item. Not ported: field moves from the menu, MAIL, SEL (registering items), moving a Pokémon's moves to another party member with left and right, item reordering with Select and the remaining field items such as Repel and Escape Rope (each gives a notice and changes nothing). Rare Candy, evolution stones and vitamins run since the evolution section below. +- Not ported: MAIL, moving a Pokémon's moves to another party member with left and right, item reordering with Select and the remaining field items such as Repel and Escape Rope (each gives a notice and changes nothing). Rare Candy, evolution stones and vitamins run since the evolution section below; field moves, SEL and Select since the section after it. ## Implemented: evolution @@ -180,11 +180,21 @@ The numbered sections below keep their full scope. Each one opens with a checkli - PACK: Rare Candy (`RareCandyEffect`: one level at the level's minimum experience; the HP gains the maximum HP's increase even for a fainted Pokémon; the caught-location happiness bonus; "grew to level", the stat box, new moves and a possible evolution), evolution stones (`EvoStoneEffect`: a forced `EvolvePokemon`; the party menu shows ABLE or NOT ABLE with the original's ten-byte, three-byte-step check; an Everstone or no matching evolution has no effect) and vitamins (`VitaminEffect`: ten more in the stat experience's high byte, up to 100, stats recomputed without changing the current HP, happiness). - Not presented: the evolution music, cries and the palette changes. Trade evolutions need Link. +## Implemented: field moves from the menu, SEL and moving Pokémon + +- Field moves in the Pokémon submenu run their menu functions: the world checks the badge (`CheckBadge`: "Sorry! A new BADGE is required."), the facing tile, water or object, then the menu closes and the ROM's `…FromMenu` script runs for the chosen Pokémon (no "Want to …?" question): Cut (`CheckMapForSomethingToCut`), Surf (including "already surfing" and forced cycling), Strength, Flash (a `PALETTE_DARK` map or the Aerodactyl chamber; `BlindingFlash` sets the Flash flag), Whirlpool and Waterfall. Failures print the ROM's messages and return to the party menu. +- Sweet Scent runs `SweetScentFromMenu`: where wild Pokémon can appear and the map has a rate, `ChooseWildEncounter` picks one without the rate roll and a battle starts; elsewhere "Looks like there's nothing here…". +- Softboiled and Milk Drink give a fifth of the user's maximum HP to another Pokémon that has not fainted and is not at full HP; the user needs more than that fifth. +- Fly (outdoors with the Storm Badge), Dig (in caves), Teleport (outdoors with a spawn point), Headbutt (facing a tree) and Rock Smash (facing a rock) run their checks and messages, then give a notice: the Fly map, dungeon and spawn warps, tree encounters and rock encounters are not ported. +- SEL registers an item that `CANT_SELECT` allows (in practice key items) in `wWhichRegisteredItem` and `wRegisteredItem`, which saves keep. Select runs `SelectMenu`: `CheckRegisteredItem` (a slot that no longer holds the item clears the registration; a registered ball is never found, as in the original) and `UseRegisteredItem` by field menu. The Bicycle uses `Script_GetOnBike_Register` and `Script_GetOffBike_Register`, without a message; elsewhere Oak objects. Without a registration Select explains SEL. This replaces the earlier Key Items shortcut. +- Bill's PC's MOVE PKMN W/O MAIL: refused while a party Pokémon holds MAIL, then "Each time you move a POKéMON, data will be saved. OK?". Choose the party or a box, a Pokémon (MOVE, STATS, CANCEL), then a box and the position to insert before. The party keeps its last Pokémon and its last healthy one, a different full box or party has "no room", a Pokémon moved down its own list lands one place earlier (`.CheckTrivialMove`), a boxed Pokémon joining the party gets its stats with full HP and no status (`CalcBufferMonStats`), and one entering a box gets its PP restored. The original's left/right box switching is presented as a list of boxes; after "no room" the box is chosen again rather than the position. +- Save format 3 gained `registeredItem`, migrated by absence to none. + ## 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. Only the special Kurt and Park balls remain unported among battle items. -This is still not a complete new game. The Pokégear, Pokédex and trainer card screens, story progression beyond Route 30 and Mr. Pokémon's house, and audio remain unfinished. The start menu opens the party menu and the pack, and the Key Items shortcut supports the Bicycle; the PCs store Pokémon, items and decorations. Surfing and terrain animation now have native implementations; later maps remain gated. Save slots, a Johto/Kanto atlas, Pokédex records, online Link, friends and real-world steps come later. +This is still not a complete new game. The Pokégear, Pokédex and trainer card screens, story progression beyond Route 30 and Mr. Pokémon's house, and audio 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; later maps remain gated. Save slots, a Johto/Kanto atlas, Pokédex records, online Link, friends and real-world steps come later. ## 1. Complete the overworld foundation @@ -196,7 +206,7 @@ This is still not a complete new game. The Pokégear, Pokédex and trainer card - [x] Connected transitions for every enabled map (New Bark Town to Route 30) - [x] Dynamic warps, elevators, ice, currents and forced movement - [x] Native Surf, Cut and Strength interactions, cycling rules and boulder holes -- [x] Bicycle access from the Key Items menu with keyboard and touch controls +- [x] Bicycle access from the pack or a registered Select, with keyboard and touch controls - [x] Tile changes (`changeblock`, `changemapblocks`) with rollback and minimap refresh - [x] Room decorations, ownership, selection, removal, save validation and migration - [x] Additional NPC movement types, variable sprites, Pokémon icons and movement-command decoding @@ -230,8 +240,8 @@ pixel-perfect screenshots or full Celebi story support. The bedroom PC selects and puts away owned decorations, including left/right doll placement. Room blocks, sprites and descriptions reflect saved choices. -The Town Map poster opens the native ROM-backed map. Select opens Key Items -(Start now opens the start menu); using the Bicycle follows the native +The Town Map poster opens the native ROM-backed map. Start opens the start +menu and Select the registered item; using the Bicycle follows the native outdoor/indoor rules. Browser checks cover both controls and decoration flows in Chromium and WebKit. Full item/Pokémon PC storage and the other bag pockets belong to later work. @@ -262,7 +272,7 @@ Introduce native player, party, Pokémon, inventory, money and quest models. Rea 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, Cherrygrove, Route 30's trainers, Mr. Pokémon's house, the Cherrygrove rival battle, the return to Elm, capture, the catching tutorial, the PC, the introduction's naming and clock screens, and switching and healing items in battle run now. The opening milestone is complete within the recorded acceptance boundaries. The start menu, party menu and pack now run outside battle, and Pokémon evolve. Next: MOVE PKMN W/O MAIL, SEL and field moves from the menu, then the Pokégear. +Meeting Mom, Elm's introduction, the starter, the aide's Potion, Route 29, Cherrygrove, Route 30's trainers, Mr. Pokémon's house, the Cherrygrove rival battle, the return to Elm, capture, the catching tutorial, the PC, the introduction's naming and clock screens, and switching and healing items in battle run now. The opening milestone is complete within the recorded acceptance boundaries. The start menu, party menu and pack now run outside battle, and Pokémon evolve. Field moves from the menu, SEL/Select and MOVE PKMN W/O MAIL run too. Next: the Pokégear, then the trainer card and options. 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. @@ -294,7 +304,8 @@ Acceptance: main-game/postgame playthroughs, representative optional content, ba - [x] Save, backup export and import for the single native save - [x] Touch battle commands, move list, PKMN party menu, PACK (Items and Balls pockets) and naming fields - [x] Start menu, party menu and pack outside battle (switching, moves, held items, healing and PP items, tossing, the Bicycle) -- [ ] Pokégear, Pokédex, trainer card and options screens; MOVE PKMN W/O MAIL +- [x] Bill's PC: MOVE PKMN W/O MAIL between the party and every box +- [ ] Pokégear, Pokédex, trainer card and options screens - [ ] Scalable text, configurable controls and fuller accessibility - [ ] Save slots with migration, atomic writes and import previews - [ ] Installable web app with offline app shell, safe updates and quota handling diff --git a/docs/SESSION_HANDOFF.md b/docs/SESSION_HANDOFF.md index 66d2cef..645d1d5 100644 --- a/docs/SESSION_HANDOFF.md +++ b/docs/SESSION_HANDOFF.md @@ -1,6 +1,7 @@ # Recovered Claude session -Latest continuation: see **Evolution: 2026-09-30** at the end of this file. Earlier sections are historical snapshots. +Latest continuation: see **Field moves, SEL and moving Pokémon: 2026-09-30** +at the end of this file. Earlier sections are historical snapshots. Recovered on 2026-09-23 from the local Claude Code transcript `~/.claude/projects/-Users-kawa-Crystal/4ac88f30-29e6-4fd5-a4b2-b6a318dd83b3.jsonl`. @@ -762,3 +763,35 @@ menus; no CI or physical-device run. Next: MOVE PKMN W/O MAIL, SEL/registered items with Select, field moves from the menu (Cut, Surf, Strength already have their ROM scripts), then the Pokégear. Trade evolutions wait for Link. + +The evolution increment was committed and pushed as `45275ba`. + +## Field moves, SEL and moving Pokémon: 2026-09-30 + +Resumed with "Continue" after `45275ba`, taking the recorded next items. + +- Field moves from the party menu: the world's `menuFieldMove` runs each + menu function's checks (badges, facing tile/water/object, dark map) and + either queues the ROM's `…FromMenu` script with the chosen Pokémon or + returns the ROM's failure texts. Cut, Surf, Strength, Flash, Whirlpool, + Waterfall and Sweet Scent work; Softboiled/Milk Drink run in the menu; + Fly, Dig, Teleport, Headbutt and Rock Smash give a notice after their + checks. Encounter selection was split out of `randomEncounter` so Sweet + Scent reuses it without the rate roll. +- SEL and Select: `registeredItem` saved (migrated by absence), Select runs + `SelectMenu` (bike via the `_Register` scripts, Oak's warning, "may be + registered", registration cleared when the slot changed). The invented + Key Items shortcut (UI, runtime flag and CSS) is removed. +- Bill's PC MOVE PKMN W/O MAIL, with lists per box. +- `check:reference` caught `SPRITEMOVEDATA_SMASHABLE_ROCK` written as 18 + instead of $18; fixed and covered by a Rock Smash test. + +Checks: 371 unit tests with the local ROM; 111 Chromium/WebKit browser +tests passed, 19 skipped intentionally (the two Key Items browser tests now +cover SEL/Select); native and reference builds, reference, vendor and +release checks; `git diff --check`. No emulator comparison; no CI or +physical-device run. + +Next: the Pokégear (clock, map, phone and radio cards), then the trainer +card and options; later the Fly map and Teleport/Dig warps (they need +script commands after a warp). diff --git a/scripts/check-reference.mjs b/scripts/check-reference.mjs index 0d96ff7..a4247e3 100644 --- a/scripts/check-reference.mjs +++ b/scripts/check-reference.mjs @@ -238,6 +238,7 @@ const eventNames = { playersHouseDoll1: "EVENT_PLAYERS_HOUSE_2F_DOLL_1", playersHouseDoll2: "EVENT_PLAYERS_HOUSE_2F_DOLL_2", playersHouseBigDoll: "EVENT_PLAYERS_HOUSE_2F_BIG_DOLL", + wallOpenedInAerodactylChamber: "EVENT_WALL_OPENED_IN_AERODACTYL_CHAMBER", }; for (const [key, name] of Object.entries(eventNames)) { const match = new RegExp(`\\b${key}:\\s*(\\d+)`).exec(profile); diff --git a/src/game/battle/constants.ts b/src/game/battle/constants.ts index b08624a..c0587a0 100644 --- a/src/game/battle/constants.ts +++ b/src/game/battle/constants.ts @@ -256,6 +256,8 @@ export const C = { KEY_ITEM_POCKET: 2, TM_HM_POCKET: 3, PP_UP: 0x3e, + SOFTBOILED: 0x87, + MILK_DRINK: 0xd0, RARE_CANDY: 0x20, HP_UP: 0x1a, PROTEIN: 0x1b, diff --git a/src/game/party/storage.ts b/src/game/party/storage.ts index 701fb2e..19cb7c5 100644 --- a/src/game/party/storage.ts +++ b/src/game/party/storage.ts @@ -85,6 +85,15 @@ export function withdrawnMon(data: PokemonData, box: BoxMon): PartyMon { return { ...mon, level, status: 0, hp: stats[0], stats }; } +/** A boxed Pokémon in MOVE PKMN W/O MAIL's wBufferMon: its stored level, + * CalcBufferMonStats' stats with stat experience, full HP (none for an + * Egg) and no status. */ +export function bufferMon(data: PokemonData, box: BoxMon): PartyMon { + const mon = structuredClone(box); + const stats = calcStats(data, mon.species, mon.dvs, mon.level, mon.statExp); + return { ...mon, status: 0, hp: mon.species === 0xfd ? 0 : stats[0], stats }; +} + /** ReceiveItem into wNumPCItems. */ export function receivePCItem( items: ItemSlot[], diff --git a/src/game/rom/assets.ts b/src/game/rom/assets.ts index ff39ece..6a956f4 100644 --- a/src/game/rom/assets.ts +++ b/src/game/rom/assets.ts @@ -124,6 +124,7 @@ export class CrystalAssets { tileset: r.byte(header + 1), environment: r.byte(header + 2), paletteMode: r.byte(header + 7) & 15, + attributes, blocks: r.slice(r.far(attributes + 3), blockWidth * blockHeight), warps: [], backgroundEvents: [], diff --git a/src/game/rom/profile.ts b/src/game/rom/profile.ts index 2f70e08..7a64b06 100644 --- a/src/game/rom/profile.ts +++ b/src/game/rom/profile.ts @@ -169,6 +169,7 @@ export const EVENTS = { playersHouseDoll1: 1858, playersHouseDoll2: 1859, playersHouseBigDoll: 1860, + wallOpenedInAerodactylChamber: 809, } as const; /** Engine flag indices named directly by translated routines. */ @@ -219,6 +220,26 @@ export const OVERWORLD = { IconPointers: 0x8ebbf, AskSurfScript: 0xca2c, "UsedSurfScript.stubbed_fn": 0xc9a2, + Script_CutFromMenu: 0xc7fe, + SurfFromMenuScript: 0xc983, + Script_StrengthFromMenu: 0xcd29, + Script_UseFlash: 0xc8e6, + UseFlashTextScript: 0xc8f3, + "UseFlashTextScript.BlankText": 0xc908, + BlindingFlash: 0x8c7e1, + Script_WhirlpoolFromMenu: 0xce0b, + Script_WaterfallFromMenu: 0xcb1c, + "SweetScentFromMenu.SweetScent": 0x506c8, + SweetScentEncounter: 0x506ef, + "CheckBadge.BadgeRequiredText": 0xc73d, + "FieldMoveFailed.CantUseItemText": 0xc780, + CutNothingText: 0xc7c9, + CantSurfText: 0xc9ae, + AlreadySurfingText: 0xc9b3, + "EscapeRopeOrDig.CantUseDigText": 0xcc26, + "TeleportFunction.CantUseTeleportText": 0xccb6, + "Softboiled_MilkDrinkFunction.ItemCantUseOnMonText": 0xf44a, + RuinsOfAlphAerodactylChamber_MapAttributes: 0x958e8, AskCutScript: 0xd1a9, "AskCutScript.CheckMap": 0xd1ba, CantCutScript: 0xd1cd, @@ -237,7 +258,9 @@ export const OVERWORLD = { ChrisStateSprites: 0x141b7, KrisStateSprites: 0x141c0, Script_GetOnBike: 0xd13e, + Script_GetOnBike_Register: 0xd14e, Script_GetOffBike: 0xd158, + Script_GetOffBike_Register: 0xd16b, Script_CantGetOffBike: 0xd171, UpdatePlayerSprite: 0xe4a, PlayMapMusic: 0x3cdf, @@ -387,6 +410,12 @@ export const PC = { PCString_BoxFull: 0xe3575, PCString_PartyFull: 0xe3586, PCString_NoReleasingEGGS: 0xe3597, + PCString_MoveToWhere: 0xe3500, + PCString_TheresNoRoom: 0xe3521, + "_MovePKMNWithoutMail.MenuData": 0xe28cb, + MoveMonWOMailSaveText: 0x152a6, + "BillsPC_MovePKMNMenu.PCMonHoldingMailText": 0xe4f9, + "MovePKMNWithoutMail_InsertMon.Saving_LeaveOn": 0xe3233, "BillsPC_PlaceChooseABoxString.ChooseABox": 0xe37a1, "BillsPC_PlaceEmptyBoxString_SFX.NoMonString": 0xe37d3, "BillsPC_PrintBoxName.Current": 0xe36f1, @@ -415,6 +444,11 @@ export const MENU = { MonMenuOptions: 0x24cd9, "GiveTakeItemMenuData.Items": 0x12ca3, String_MoveWhere: 0x1316b, + "MonMenu_Softboiled_MilkDrink.PokemonNotEnoughHPText": 0x12f00, + MayRegisterItemText: 0x13340, + RegisteredItemText: 0x10afd, + CantRegisterText: 0x10b02, + ItemsOakWarningText: 0x12a67, MailItems: 0xb9e80, PokemonSwapItemText: 0x12caf, PokemonHoldItemText: 0x12cb4, diff --git a/src/game/runtime.ts b/src/game/runtime.ts index 840de2f..9c8f3f2 100644 --- a/src/game/runtime.ts +++ b/src/game/runtime.ts @@ -9,7 +9,6 @@ import { WorldRenderer } from "./rendering/renderer"; export class NativeRuntime implements GameRuntime { readonly id = "crystal-native-world-v1"; world?: GameWorld; - fieldMenuOpen = false; private renderer?: WorldRenderer; private pressed = new Set -