Finish the opening milestone's switching, items and acceptance run
- Decode evolution lists from the ROM; all 251 match the pinned PRET source. - Compare CalcMonStats and CalcExpAtLevel against the original routines in the reference emulator. - Add a continuous opening playthrough from the intro to the catching tutorial, reloading a JSON save after every stage. - Port player switching in battle: the PKMN menu, forced choices after a faint, Shift mode's offer and double faints, with the original texts. - Port HP and status healing items in battle through the ROM's ItemEffects, HealingHPAmounts and StatusHealingActions tables. - Add the battle party menu and the Items pocket to the UI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
eb03a20d8f
commit
f8e0f9c65a
20 files changed
+1902
-96
No files matched your search
+53
-4
@@ -32,7 +32,7 @@ These paths refer to the pinned [PRET source](https://github.com/pret/pokecrysta
|
||||
| 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/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, learnsets, move PP, item pockets, growth-rate exp and front pictures (`FixPicBank`, `PadFrontpic`). Starter pictures match rebuilt bytes. |
|
||||
| `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. |
|
||||
| Text interpreter | `macros/scripts/text.asm`, `constants/charmap.asm`, `home/text.asm` | Literal/far text, RAM names and string buffers from native state, numbers of dots, and sound or pause cues (consumed). `text_asm` and other RAM reads are refused. |
|
||||
|
||||
@@ -46,7 +46,7 @@ The opening scenes currently run end to end from the ROM: Mom's Pokégear, weekd
|
||||
|
||||
`tests/reference-parity.spec.ts` loads the user's ROM into the reference emulator and places a small original test trampoline in emulated WRAM. It calls the ROM's `FarDecompress` routine at `$0b40`, then sets a return sentinel. Every output byte is compared with the native decoder for six supported map tilesets. This executes the original routine rather than comparing two copies of our code.
|
||||
|
||||
The same suite compares 400 damage cases and 400 type-effectiveness cases per browser against the original battle routines. Battle unit tests cover win, escape, loss, experience, level-up and move-learning flows. This establishes parity only for the compared routines and inputs. It does not prove complete gameplay, rendering, battles, audio, timing or physical-device parity. Extend controlled comparisons as each subsystem is translated.
|
||||
The same suite compares 400 damage cases and 400 type-effectiveness cases per browser against the original battle routines, 300 `CalcMonStats` cases (with and without stat experience, including levels above 100 that reach the 999 cap) and every `CalcExpAtLevel` value for the six growth rates and levels 1–100. Battle unit tests cover win, escape, loss, experience, level-up and move-learning flows, and `tests/playthrough.test.ts` plays the whole opening quest from a new game through `GameWorld`'s inputs, with save/reload after each stage. This establishes parity only for the compared routines and inputs. It does not prove complete gameplay, rendering, battles, audio, timing or physical-device parity. Extend controlled comparisons as each subsystem is translated.
|
||||
|
||||
## Validation
|
||||
|
||||
@@ -80,7 +80,7 @@ Native saves are version 3. Version 2 added scene values by owning map, phone co
|
||||
|
||||
`home/trainers.asm` and `engine/events/trainer_scripts.asm`: talking to an `OBJECTTYPE_TRAINER` object runs `TalkToTrainerScript` with the object's header copied as `wTempTrainer`; `_CheckTrainerBattle` runs `SeenByTrainerScript` with `wSeenTrainerDistance`. The world checks sight in `PlayerEvents` order: before warps, coordinate events and encounters at the end of a step (a seen player's tile events are then skipped, as `DisableEvents` does), and on idle ticks before scene scripts and input. Walls and objects do not block sight. `TrainerWalkToPlayer`/`ComputePathToWalkToPlayer` build the raw `wMovementBuffer` bytes, which `applymovementlasttalked` decodes with the normal movement reader. `wRunningTrainerBattleScript` lives in the script run: zero at the start, set by `loadmem` after the battle so `endifjustbattled` ends the after-script. Trainer commands without a map trainer are refused. `MomTriesToBuySomething` after scripted battles does nothing while Mom's savings (not ported) are zero.
|
||||
|
||||
`engine/battle/core.asm` and `engine/battle/ai/items.asm`, `switch.asm`: when a trainer's Pokémon faints and others remain, `UpdateBattleStateAndExperienceAfterEnemyFaint` zeroes its party HP and runs `NewEnemyMonStatus`; `EnemyPartyMonEntrance` then sends out the Pokémon chosen by `FindMonInOTPartyToSwitchIntoBattle`. When every candidate is threatened it draws `BattleRandom & 7`, which tests pin. `AI_SwitchOrTryItem` runs before either side moves: `CheckAbleToSwitch`, the Often/Rarely/Sometimes thresholds, `AI_TrySwitch` and `AI_Switch` (withdraw text, HP and status back to the party). `CheckTypeMatchup` ignores the type its switch-AI callers load and uses `BATTLE_VARS_MOVE_TYPE`, so every such matchup uses the enemy's chosen move type; `FindEnemyMonsWithASuperEffectiveMove` never resets its result between Pokémon; `FindEnemyMonsWithAtLeastQuarterMaxHP` rotates HP bits instead of multiplying. All three bugs are kept. `UsedMoveText` records `wPlayerUsedMoves`. Refused: the Shift-mode offer to switch (`OfferSwitch`), `AskUseNextPokemon` with other Pokémon left, Pursuit on a switching Pokémon, Perish Song, Spikes, and trainer items. Side effects of the type scans on the move structs (overwritten types and animation) are not modelled; the next `ParseEnemyAction`/`ParsePlayerAction` reloads them.
|
||||
`engine/battle/core.asm` and `engine/battle/ai/items.asm`, `switch.asm`: when a trainer's Pokémon faints and others remain, `UpdateBattleStateAndExperienceAfterEnemyFaint` zeroes its party HP and runs `NewEnemyMonStatus`; `EnemyPartyMonEntrance` then sends out the Pokémon chosen by `FindMonInOTPartyToSwitchIntoBattle`. When every candidate is threatened it draws `BattleRandom & 7`, which tests pin. `AI_SwitchOrTryItem` runs before either side moves: `CheckAbleToSwitch`, the Often/Rarely/Sometimes thresholds, `AI_TrySwitch` and `AI_Switch` (withdraw text, HP and status back to the party). `CheckTypeMatchup` ignores the type its switch-AI callers load and uses `BATTLE_VARS_MOVE_TYPE`, so every such matchup uses the enemy's chosen move type; `FindEnemyMonsWithASuperEffectiveMove` never resets its result between Pokémon; `FindEnemyMonsWithAtLeastQuarterMaxHP` rotates HP bits instead of multiplying. All three bugs are kept. `UsedMoveText` records `wPlayerUsedMoves`. Refused: Pursuit on a switching Pokémon, Perish Song, Spikes, and trainer items. Side effects of the type scans on the move structs (overwritten types and animation) are not modelled; the next `ParseEnemyAction`/`ParsePlayerAction` reloads them.
|
||||
|
||||
`BGEVENT_ITEM` runs `HiddenItemScript` with `wHiddenItemData`; `SetMemEvent` sets the item's flag.
|
||||
|
||||
@@ -147,7 +147,7 @@ Prompts continue on their own. The Dude's back picture, the ball animation,
|
||||
capture music and the Pokédex page (`NewPokedexEntry`) are not presented.
|
||||
`TX_LOW` text is now read as a line break.
|
||||
|
||||
Refused: `SendMonIntoBox` (a catch with a full party), the other ball
|
||||
Refused: the other ball
|
||||
multipliers, Park/Safari Balls, a transformed target and a catch-rate
|
||||
division by zero. Pure vectors cover the catch formula; local-ROM tests
|
||||
cover capture, every break-free message, Master Ball draws, blank
|
||||
@@ -155,3 +155,52 @@ nicknames, the blocked trainer ball, full-party rollback, both tutorial
|
||||
triggers, declining and the repeat offer, and mid-battle reloads. Browser
|
||||
tests cover PACK, catching and naming. No emulator comparison of the
|
||||
capture routines has been run.
|
||||
|
||||
## Switching and healing items in battle
|
||||
|
||||
`engine/battle/core.asm`: `BattleMenu_PKMN` and `TryPlayerSwitch` ("is
|
||||
already out", "can't be recalled" while wrapped or blocked by Mean Look,
|
||||
`CheckIfCurPartyMonIsFitToFight` with its fainted and Egg texts),
|
||||
`PlayerSwitch` and `BattleMonEntrance`. The switch runs `ParseEnemyAction`
|
||||
first, and `ParsePlayerAction` then skips it, so the random draws happen
|
||||
once. `SendOutMonText` and `WithdrawMonText` choose their cheer from the
|
||||
enemy's HP as a byte percentage of max HP / 4, including
|
||||
`wEnemyHPAtTimeOfPlayerSwitch`. A maximum HP below 4 would divide by zero
|
||||
and freeze the original; it is refused. `PursuitSwitch` reads the enemy
|
||||
move's effect as `GetMoveEffect` does, and Pursuit is refused.
|
||||
|
||||
After a faint, `HandlePlayerMonFaint` runs `AskUseNextPokemon` (wild
|
||||
battles only; NO tries to run, compared with the first party Pokémon's
|
||||
stored Speed) and `ForcePlayerMonChoice`, whose party menu cannot be
|
||||
cancelled. A double faint in a trainer battle goes through `DoubleSwitch`,
|
||||
and a fainted Pokémon gains no share of the experience. When a trainer
|
||||
sends out the next Pokémon, `CheckWhetherToAskSwitch` and `OfferSwitch` run
|
||||
Shift mode's offer (the only battle style, since options are not ported);
|
||||
switching clears both participant sets as the original does.
|
||||
`NewBattleMonStatus` also clears the enemy's wrap count and Mean Look.
|
||||
|
||||
`engine/items/item_effects.asm` and `pack.asm`: `BattlePack` shows the Items
|
||||
and Balls pockets. `CheckItemContext` gives Oak's "This isn't the time"
|
||||
text for items with no battle use. `ItemEffects` is read from the ROM:
|
||||
`RestoreHPEffect` (`ItemRestoreHP`, `HealingHPAmounts`, `RestoreHealth`) and
|
||||
`StatusHealingEffect` (`UseStatusHealer`, `StatusHealingActions`,
|
||||
`HealStatus`, `IsItemUsedOnConfusedMon`) run with `UseItem_SelectMon`, the
|
||||
party menu action texts and `UseDisposableItem`. "Won't have any effect"
|
||||
and a cancelled menu keep the item and the turn. `wItemEffectSucceeded`
|
||||
shares `wBattlePlayerAction`'s byte, so a used item takes the turn.
|
||||
`CalcPlayerStats` reapplies the paralysis and burn penalties to the side
|
||||
named by `hBattleTurn` after a switch of turns, which can be the enemy.
|
||||
Because `hBattleTurn` at the battle menu is not modelled everywhere, a
|
||||
status heal that would reapply a penalty to a paralysed or burned enemy is
|
||||
refused. Other item effects (X items, Poké Doll, Full Restore, revives,
|
||||
Ether) are refused.
|
||||
|
||||
Tests: local-ROM battles cover switching, cancelling, the withdrawal texts,
|
||||
forced choices (including a fainted pick and a cancelled forced menu), run
|
||||
after NO, Shift mode's offer (accepted and declined), a trainer double
|
||||
faint, Potion, Max Potion, a Berry on a benched Pokémon, no-effect cases,
|
||||
Antidote and PSNCUREBERRY, Parlyz Heal restoring Speed, Oak's warning and a
|
||||
refused X Attack. Browser tests switch through the party menu and its
|
||||
SWITCH/STATS/CANCEL submenu and use a Potion from the Items pocket. The
|
||||
stats screen is not ported. No emulator comparison of these routines has
|
||||
been run.
|
||||
Reference in new issue
Block a user