281 lines
28 KiB
Markdown
281 lines
28 KiB
Markdown
# Native port status and reference checks
|
||
|
||
## Reproducible reference
|
||
|
||
The private `.reference/pokecrystal` checkout is pinned to `7a7881d0d62e0ddbd82dcf10e7116807487ac651`. With RGBDS 1.0.3, its `crystal11` build is byte-identical to the supplied English US/Europe revision 1.1 ROM. `npm run check:reference` verifies the files and commit. It checks every ROM and RAM address in `src/game/rom/profile.ts` against generated symbols, event constants against `constants/event_flags.asm`, and map-group bounds against `data/maps/maps.asm`.
|
||
|
||
To recreate the private reference:
|
||
|
||
```sh
|
||
git clone https://github.com/pret/pokecrystal.git .reference/pokecrystal
|
||
git -C .reference/pokecrystal checkout 7a7881d0d62e0ddbd82dcf10e7116807487ac651
|
||
make -C .reference/pokecrystal crystal11 -j4
|
||
npm run check:reference
|
||
```
|
||
|
||
Install RGBDS 1.0.3 separately. No script downloads a game ROM. `CRYSTAL_ROM_PATH` optionally selects another local location; checks otherwise use `Pokemon - Crystal Version (UE) (V1.1) [C][!].gbc` at the root.
|
||
|
||
The reference checkout, rebuilt ROM, extracted graphics, saves and gameplay screenshots must stay local and ignored. The shipping profile contains addresses and format constants, not game content.
|
||
|
||
## Routine map
|
||
|
||
These paths refer to the pinned [PRET source](https://github.com/pret/pokecrystal/tree/7a7881d0d62e0ddbd82dcf10e7116807487ac651).
|
||
|
||
| Native module | PRET reference | Evidence and limits |
|
||
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| `game/rom/lz.ts` | `home/decompress.asm` | All LZ command types have unit vectors; six tilesets match original ASM execution byte-for-byte. |
|
||
| `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. |
|
||
| 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/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. |
|
||
|
||
Supported event opcodes are explicit in the interpreter switch. Audio commands consume their operands, but sound sequencing is pending; those opcodes do not imply complete audiovisual behavior. `pause` holds the current text or waits the original number of overworld ticks.
|
||
|
||
Scripts run on a private draft. The world presents each yielded effect, then resumes the script with the player's answer or when the animation ends. The draft is committed only when the script ends. A save made during a scene returns the committed state, and coordinate events rewind to the source of the triggering walk or two-tile jump. Saves during movement also retain the source tile until arrival events run, including across map connections. A failed scene restores the positions it had changed. As in `LoadMapObjects`, map objects spawn when the map loads. Later flag changes do not remove objects already shown; only `appear` and `disappear` do.
|
||
|
||
The opening scenes currently run end to end from the ROM: Mom's Pokégear, weekday and DST scene, the teacher at the west edge, the rival at the lab window, Elm's introduction with its yes/no loop, the lab's exit guard, the starter choice with `pokepic`, `givepoke` and the nickname prompt, Elm's directions and the aide's Potion through the ROM's own `GiveItemScript`. `callasm` accepts only explicitly empty routines or native translations: `HalveMoney`, `GetWhiteoutSpawn`, the item ball's `TryReceiveItem` and the fruit tree routines. The naming screen is a native text field limited to the characters the original offers.
|
||
|
||
## Actual emulator comparison
|
||
|
||
`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, 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.
|
||
|
||
`tests/reference-random.spec.ts` also executes the unpatched
|
||
`BattleCommand_Critical`, `BattleCommand_CheckHit` and
|
||
`BattleCommand_DamageVariation` routines. The original Link RNG and the native
|
||
random callback start from the same ten-byte stream. Each case compares the
|
||
result, modified accuracy, miss damage and final RNG stream/cursor, covering
|
||
skipped draws and rejection sampling across stream regeneration. Each browser
|
||
runs 2,560 critical cases, 2,914 accuracy cases and 2,149 damage-variation cases.
|
||
Critical coverage includes every random byte at all five reachable stages,
|
||
Focus Energy, Scope Lens and the species-specific items. Accuracy coverage
|
||
includes all stage pairs, Foresight, BrightPowder, perfect accuracy, X Accuracy,
|
||
rain/Thunder, Dream Eater, drain/substitute failure and Jump Kick miss damage.
|
||
Damage coverage includes 0/1's no-draw path, rounding and 16-bit boundaries.
|
||
Mutating each command's threshold or divisor makes its comparison fail.
|
||
|
||
These are isolated command comparisons using controlled Link RNG inputs, not
|
||
a claim of Link battle support or matching the hardware-timed non-Link RNG.
|
||
Protect's text path and the refused Lock-On/Fly/Dig paths are outside these
|
||
fixtures. Capture decision comparisons are described below; whole-battle
|
||
reference comparisons remain open.
|
||
|
||
## Validation
|
||
|
||
```sh
|
||
npm test
|
||
npm run test:e2e
|
||
npm run check:reference
|
||
npm run check:vendor
|
||
npm run build
|
||
npm run check:release
|
||
npm run build:reference
|
||
```
|
||
|
||
User-ROM tests skip when the file is absent, including public CI. Synthetic tests still run. Comparing with rebuilt graphics also needs the private PRET build. Do not report skipped private checks as CI passes.
|
||
|
||
Native production is `dist/`; the optional reference output is `dist-reference/`. Release checks reject emulator dependencies, binary game files and embedded binary assets in the native output. Source review is still required when adding assets or data.
|
||
|
||
## Save schema
|
||
|
||
Native saves are version 3. Version 2 added scene values by owning map, phone contacts, the weekday offset and DST flag, and positions of objects that scripts moved on the current map. Version 3 adds the player ID, party, bag, Pokédex seen/caught sets and money. Early version-3 backups without money receive the original starting balance; explicitly invalid balances are rejected. Restores recompute each Pokémon's stats from its DVs and reject any mismatch, and check every item against its ROM pocket. Older saves migrate with new-game defaults; a missing player ID is drawn as a new game draws it. Objects are restored from the save on continue, and only the TILES callback runs, as in `MapSetupScript_Continue`.
|
||
|
||
## Scripted trainer battle foundation
|
||
|
||
`game/rom/trainers.ts` decodes `TrainerGroups`, `TrainerClassAttributes`, `TrainerClassNames`, `TrainerClassDVs` and map trainer headers. Tests cover all 542 ROM records across 67 classes; CAL2 construction refuses its SRAM-dependent Mystery Gift party.
|
||
|
||
`game/battle/ai.ts` translates `AIChooseMove`, `AI_Redundant` and the Basic, Setup, Types, Offensive, Opportunist, Aggressive, Cautious, Status and Risky layers. It preserves the original random tie selection, known redundancy quirks, HP thresholds, exclusions and the deterministic pre-variation damage estimate used by Aggressive and Risky. Smart runs only for move effects that have no `AI_Smart_EffectHandlers` entry (Normal Hit, Attack Down, Defense Down; enough for the first rival). Other effects are refused before any random draw. Synthetic scoring vectors establish the tested branches; original-engine AI execution parity is still pending.
|
||
|
||
`loadtrainer`, `winlosstext` and `startbattle` run scripted trainer battles through the existing battle UI. Tests cover Joey's ROM party/text/reward, RUN refusal, trainer experience, Amulet Coin, CANLOSE behavior, money limits, transactional rollback and midbattle restore. Chromium and WebKit exercise the UI with a test-only script.
|
||
|
||
## Map trainers
|
||
|
||
`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: 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.
|
||
|
||
A trainer sighting whose script is refused is not restarted until the player moves. This is a native safeguard, not original behavior.
|
||
|
||
## Next slice
|
||
|
||
Route 29, Cherrygrove, Route 30 and Mr. Pokémon's house are enabled with item balls, hidden items, fruit trees and the daily reset, the Pokémon Center nurse, the recorded whiteout spawn, map trainers, the first rival battle, Elm's special call, capture and the catching tutorial. NPCs roam. The PC and boxes are ported. Next: party, bag and Pokégear menus over the native models. Other specials stay refused until translated.
|
||
|
||
## Reflect and Light Screen
|
||
|
||
`BattleCommand_Screen` in `engine/battle/effect_commands.asm` and `HandleScreens`
|
||
in `engine/battle/core.asm` now run natively. Each screen has a side-owned
|
||
five-turn counter, including its setup turn. Repeated use fails without
|
||
refreshing it; send-outs preserve it. Expiration runs after defrost, before
|
||
healing items, player before enemy and Light Screen before Reflect. Messages
|
||
come from the ROM. The existing damage path doubles the appropriate defense
|
||
and retains the original critical/stage rule. Safeguard and other screen
|
||
flags remain refused. Seven local-ROM tests cover move execution, counters,
|
||
PP, damage stats and send-out persistence; no screen-routine emulator parity
|
||
or physical-device validation is claimed.
|
||
|
||
## Mr. Pokémon, the first rival and special calls
|
||
|
||
`MrPokemonsHouse.asm`, `CherrygroveRivalScene*` and the `ElmsLab.asm` officer
|
||
and Egg scripts run from the ROM. Supporting translations: `blackoutmod`,
|
||
`reloadmap` (returns to the script; native presentation keeps the map),
|
||
`readvar VAR_SPECIALPHONECALL` and `memcall` (only through
|
||
`wCallerContact + PHONE_CONTACT_SCRIPT2_BANK`). Specials: `NameRival` (native
|
||
naming field, ROM title and `InitName` default), `FadeOutMusic`, and the
|
||
presentation-only fades. `CountStep`'s `CheckSpecialPhoneCall` reads the
|
||
queued `SpecialPhoneCallList` entry. It checks `SpecialCallOnlyWhenOutside`
|
||
or `SpecialCallWhereverYouAre`, then runs `CheckSpecialPhoneCall.script` with
|
||
that entry's caller script. `RingTwice_StartCall` and `InitCallReceiveDelay`
|
||
are accepted without effect, because ringing presentation and random calls are
|
||
not ported. `HangUp` shows "Click!" for its 140 frames. Local-ROM tests cover
|
||
the house, both rival triggers with all three parties, a rival loss, the call
|
||
indoors, outdoors and on reload, the officer's naming (including blank names
|
||
and reload at the naming screen), and the Egg and Poké Ball handovers. Browser
|
||
tests cover the house, the rival battle and the naming field.
|
||
|
||
## Capture and the catching tutorial
|
||
|
||
`PokeBallEffect`, `BallMultiplierFunctionTable` (Poké, Great, Ultra and
|
||
Master Balls), `GetPokeBallWobble` with `WobbleProbabilities`,
|
||
`UseBallInTrainerBattle` and `BattleMenu_Pack`'s `.UseItem` in
|
||
`engine/items/item_effects.asm`, `engine/battle_anims/pokeball_wobble.asm`
|
||
and `engine/battle/core.asm`. The catch rate is computed with the original
|
||
8- and 16-bit arithmetic and kept bugs (no bonus for BRN/PSN/PAR, overflow
|
||
above 341 maximum HP). Only the random draws are kept from the throw
|
||
animation: one per shake until the ball clicks or the Pokémon breaks free.
|
||
The caught Pokémon follows the second `LoadEnemyMon` (the wild item roll
|
||
is redrawn and then restored), `GeneratePartyMonStats` with `wBattleMode`
|
||
set (wild moves, PP and HP) and `SetCaughtData`. Pokédex registration,
|
||
`NewDexDataText`, `AskGiveNicknameText` and the naming field with
|
||
`InitName`'s default follow. The ball is taken from the selected slot, as
|
||
`RemoveItemFromPocket` does with `wCurItemQuantity`.
|
||
|
||
`Script_catchtutorial` and `CatchTutorial` run the battle as
|
||
`BATTLETYPE_TUTORIAL`: `<PLAYER>` reads "DUDE" from the ROM, `DoBattle`
|
||
skips the send-out, and the auto input's PACK and ball choice go straight
|
||
to `PokeBallEffect`, which always catches and keeps nothing (`.FinishTutorial`).
|
||
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: 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
|
||
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.
|
||
|
||
`tests/reference-capture.spec.ts` compares the production capture decision
|
||
and shake loop with `PokeBallEffect`'s calculation and the original
|
||
`GetPokeBallWobble`. Each browser runs 3,220 capture cases and 7,424 shake
|
||
cases: every catch-rate byte and supported multiplier, HP scaling and
|
||
wrapping, status bonuses, success-draw equality, every random byte at each
|
||
wobble-table entry, early/later escapes, and the Master Ball/tutorial
|
||
bypasses. Final rates, outcomes, shake counts and random-draw counts match.
|
||
The native decision and shake loop are private helpers called by the actual
|
||
ball-use path; existing capture integration tests still exercise that path.
|
||
|
||
Unlike the battle commands, capture calls hardware `Random`. This fixture
|
||
patches its entry in the private in-page ROM copy to consume supplied bytes,
|
||
enters after the capture UI setup, and inserts a return after the decision.
|
||
The catch arithmetic, ball multipliers and wobble routine are unmodified.
|
||
It does not compare hardware RNG timing, animation, caught-mon reloading,
|
||
party/PC writes, or a whole battle. No real held item has `HELD_CATCH_CHANCE`,
|
||
so that synthetic effect remains covered by pure vectors only. Maximum HP
|
||
342 and 683 are omitted from emulator execution because their divisor is
|
||
zero; synthetic tests verify the native refusal. Deliberate mutations of
|
||
success equality, formula rounding and shake equality fail the comparisons.
|
||
|
||
## Switching, battle items and the stats screen
|
||
|
||
`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 and
|
||
dispatches every non-ball effect usable in battle:
|
||
|
||
- Party items (`.BattleField`): `RestoreHPEffect` (`ItemRestoreHP`,
|
||
`HealingHPAmounts`, `RestoreHealth`), `StatusHealingEffect`
|
||
(`UseStatusHealer`, `StatusHealingActions`, `HealStatus`,
|
||
`IsItemUsedOnConfusedMon`), `FullRestoreEffect`, `ReviveEffect` (half or
|
||
full HP; a revived participant shares the experience again through
|
||
`wBattleParticipantsIncludingFainted`), the bitter Energypowder, Energy
|
||
Root, Heal Powder and Revival Herb (`ChangeHappiness` and "It looks
|
||
bitter…"), `BitterBerryEffect` and `RestorePPEffect` (Ether, Max Ether,
|
||
Elixer, Max Elixer, MysteryBerry; `GetMaxPPOfMove`, `BattleRestorePP`, a
|
||
`move` prompt for `MoveSelectionScreen` whose B reopens the party menu).
|
||
- Battle-only items (`.BattleOnly`): `XItemEffect` (`XItemStats`,
|
||
`RaiseStat` with `hBattleTurn` on the player, used even when the stat
|
||
cannot rise, and the X-item happiness gain), X Accuracy, Guard Spec and
|
||
Dire Hit (set once; a second use keeps the item), and `PokeDollEffect`
|
||
(ends a wild battle as a draw; Oak's warning in a trainer battle).
|
||
|
||
"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. `UseItemText` waits for a button. `ChangeHappiness` reads
|
||
`HappinessChanges` and updates the battle Pokémon's copy only when
|
||
`wCurPartyMon` equals the 1-based `wPartyMenuCursor`, as in the original;
|
||
fainting and levelling up use it too, and `LevelUpHappinessMod` gives the
|
||
larger gain on the map where the Pokémon was caught.
|
||
|
||
`CalcPlayerStats` reapplies the paralysis and burn penalties to the side
|
||
named by `hBattleTurn` after a switch of turns, which can be the enemy.
|
||
`hBattleTurn` at the battle menu is modelled: `DoBattle` leaves the
|
||
player's turn, `HandleHealingItems` ends a full turn on the enemy's, and
|
||
the trainer AI reports when `AI_Types`, `AI_Status` or `AIDamageCalc`
|
||
set it. A status heal after a full turn therefore penalises a paralysed
|
||
or burned enemy again, as the original does. Remaining gaps: the special
|
||
Kurt and Park balls.
|
||
|
||
`engine/pokemon/stats_screen.asm`: the stats screen's three pages (HP,
|
||
status, types, experience to the next level; item and moves with PP; ID,
|
||
OT and stats) with the ROM's strings, `TypeNames`, `GetGender` and the
|
||
front picture. It opens from BattleMonMenu (B returns to the party menu,
|
||
as `BattleMenuPKMN_ReturnFromStats` does) and from Bill's PC, where a
|
||
boxed Pokémon shows `CalcTempmonStats`' full HP and no status.
|
||
|
||
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, and every item effect above, including the reapplied enemy penalty
|
||
and the caught-location level-up bonus. Trainer AI tests pin its
|
||
`hBattleTurn` reporting. The reference suite runs the original
|
||
`SendOutMonText` (its final jump to `BattleTextbox` patched to `ret` in the
|
||
test's ROM copy), `WithdrawMonText`'s text routine, `RestoreHealth`,
|
||
`RestorePP`, `ChangeHappiness` and `CalcPlayerStats` with both values of
|
||
`hBattleTurn`: 300, 300, 200, 300 and 200 cases. Browser tests switch
|
||
through the party menu, use a Potion from the Items pocket and page
|
||
through the stats screen. `WithdrawMonText` still assumes the leftover
|
||
multiplicand high byte is zero; a whole battle has not been compared with
|
||
the original.
|