Facing a Headbutt tree asks to HEADBUTT it, and HEADBUTT from the party menu skips the question. ShakeHeadbuttTree shakes the tree as a sprite over grass, then TreeMonEncounter scores the facing tile against the trainer ID and draws from the map's common or rare tree table. A Pokémon that falls fights as BATTLETYPE_TREE: "fell out of the tree!", and the time of day's AsleepTreeMons species start asleep. Smashable rocks run SmashRockScript, and ROCK SMASH from the party menu acts on the facing rock; after the rock breaks, RockMonEncounter gives the map's rock set a 40% chance of a wild battle. Closes #26 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1091 lines
60 KiB
Markdown
1091 lines
60 KiB
Markdown
# Recovered Claude session
|
||
|
||
Latest continuation: see **Headbutt and Rock Smash: 2026-10-02**
|
||
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`.
|
||
The session stopped at its usage limit at 09:54 UTC, during research, without a final handoff.
|
||
|
||
## Resumed increment: completed 2026-09-23
|
||
|
||
The movement increment below is complete. The remaining sections preserve the
|
||
original recovery context.
|
||
|
||
- Added directional two-tile ledge jumps with the existing arc/shadow animation.
|
||
Unsupported terrain, invalid destinations and occupied landings remain refused.
|
||
- Corrected directional mats to wait for exit input. Warp arrivals keep their
|
||
actual coordinates and appropriate facing; doors, stairs and caves animate a
|
||
downward exit. Removing the old inward teleport also corrected Elm's scripted
|
||
introduction position.
|
||
- Mid-walk, mid-jump and mid-connection saves retain the source position. Landing
|
||
scenes rewind to the actual source of a jump, rather than the intervening wall.
|
||
- Restored compatibility with early version-3 saves lacking the money field.
|
||
- Replaced the scratch diagnostic with synthetic movement regressions and
|
||
optional-ROM tests covering five Route 29 ledge types. Route 29 is enabled only
|
||
inside that test fixture, not in production.
|
||
- Fixed the battle test suite's eager ROM read so missing-ROM runs really skip
|
||
private tests. Updated browser stair inputs and added an exit-mat save/reload
|
||
flow. Updated the roadmap and architecture to distinguish the existing battle
|
||
code from its unfinished integration.
|
||
|
||
Final checks: 75 unit tests passed with the local ROM; without it, 55 passed and
|
||
20 private tests skipped. All 24 Chromium/WebKit browser tests passed. Native and
|
||
reference builds, release isolation, pinned-reference checks, vendor checks and
|
||
`git diff --check` passed. No commit or push was made.
|
||
|
||
Next: validate the overworld-to-battle browser flow and complete Route 29's
|
||
remaining script, capture/tutorial and item interactions before enabling the
|
||
route. Player turn timing also remains a documented comparison gap.
|
||
|
||
## Scope and current state
|
||
|
||
The original request was to read ARCHITECTURE.md, PLAN.md and PRESENTATION_PLAN.md
|
||
and continue the native port. The working tree contains substantial uncommitted
|
||
work from multiple increments; preserve it when continuing.
|
||
|
||
- Opening scenes, scripted movement, starter selection, naming, the aide's Potion
|
||
and version 3 saves are implemented and documented.
|
||
- Native battle modules, ROM tables, damage/type parity tests, encounter handling,
|
||
battle UI and whiteout handling have been added. These later changes are not
|
||
fully reflected in the architecture and roadmap documents.
|
||
- Only the original six maps are enabled in `NATIVE_MAPS`. Route 29 (`24:3`)
|
||
remains disabled. Passing battle unit tests does not establish a playable
|
||
Route 29 encounter or the full opening quest.
|
||
|
||
## Exact stopping point
|
||
|
||
Claude was preparing to revise player movement in `src/game/world/world.ts`.
|
||
Its last investigation concerned spawn facing after warps. It had not implemented
|
||
the planned ledge/warp revision.
|
||
|
||
The pinned local PRET checkout contains the relevant routines:
|
||
|
||
- `engine/overworld/player_movement.asm`: `.TryStep`, `.TryJump`,
|
||
`.CheckLandPerms`, `.CheckWarp` and the direction tables.
|
||
- `engine/overworld/tile_events.asm`: `CheckWarpCollision` and
|
||
`CheckDirectionalWarp`.
|
||
- `engine/overworld/player_object.asm`: `SpawnPlayer`.
|
||
- `engine/overworld/map_objects.asm`: movement/turn timing.
|
||
|
||
Research found that ledge tops have land permission; the jump check uses the
|
||
current tile and facing after an ordinary step fails. Doors use collision `$71`,
|
||
downward warp mats `$70`, and stairs `$7a`. Directional warp mats require their
|
||
designated input rather than an immediate warp simply upon entry. Verify these
|
||
rules against the local routines before changing the existing movement flow.
|
||
|
||
`tests/zz-scratch.test.ts` is Claude's unfinished diagnostic: it logs local-ROM
|
||
warp and ledge collisions, contains no assertions and unconditionally requires
|
||
the private ROM. Replace it with meaningful optional-ROM or synthetic tests
|
||
before treating the suite as ready for public CI.
|
||
|
||
## Validation after recovery
|
||
|
||
Run against the current working tree with the local ROM and reference checkout:
|
||
|
||
- `npm test`: 45 passed, including the assertion-free scratch diagnostic.
|
||
- `npm run build`: passed.
|
||
- `npm run test:e2e`: 22 passed across Chromium and WebKit, including native
|
||
damage/type comparisons against original routines in the reference emulator.
|
||
- `npm run check:reference`: ROM, pinned rebuild and profile addresses match.
|
||
- `npm run check:release`: native output excludes emulator and game assets.
|
||
- `npm run check:vendor`: passed.
|
||
|
||
These checks do not establish complete battle behavior, browser battle UI flows,
|
||
Route 29 progression or physical-device compatibility. No gameplay source was
|
||
changed during session recovery.
|
||
|
||
## Original next-increment notes (movement now completed)
|
||
|
||
Finish ledge and directional-warp movement with focused regression tests,
|
||
including interrupted movement/save behavior and preservation of the existing
|
||
opening scenes. Audit Route 29 callbacks, scripts and unsupported destinations
|
||
before enabling it. Then test wild encounters and battle controls through the
|
||
browser, including win, escape, loss and save/reload, and reconcile the roadmap
|
||
with the verified behavior.
|
||
|
||
## Battle integration increment: 2026-09-23 (resumed from Codex)
|
||
|
||
Codex committed and pushed `c08d250`, then stopped mid-increment with the
|
||
keyboard/D-pad battle test failing. Claude Code completed it:
|
||
|
||
- Browser battle flows run through the real UI via `tests/battle-fixture.ts`
|
||
(encounter selection only; production maps stay gated): win with level-up and
|
||
Smokescreen, escape, whiteout, keyboard/D-pad menus, mid-battle backup restore
|
||
and unsupported-effect rollback. `tests/battle-world.test.ts` covers the same
|
||
at world level, plus pending stat experience and whiteout callback rollback.
|
||
- Saves accept stored stats between the no-stat-exp and full-stat-exp values,
|
||
since `GiveExperiencePoints` awards stat experience without `CalcMonStats`.
|
||
- A script warp (e.g. whiteout) validates its destination and commits healing,
|
||
money loss and destination callbacks together; aborts restore state and map.
|
||
- Keyboard and on-screen D-pad navigate battle menus (`battle-selected`); A
|
||
continues messages, B leaves the move list.
|
||
- Fixed a phone-width layout bug: a long notice sat in the Play button's auto
|
||
grid column and crushed the status text, pushing the game panel off-screen.
|
||
Below 720px the notice now has its own row.
|
||
- Test fixes: the navigation test now presses A through the intro message, and
|
||
uses a real pointer click on the D-pad (synthetic pointer events cannot be
|
||
captured).
|
||
|
||
Final checks: 82 unit tests with the local ROM (55 passed, 27 skipped without
|
||
it); 36 Chromium/WebKit browser tests; native and reference builds; release,
|
||
vendor and reference checks; `git diff --check`. Not committed.
|
||
|
||
Next: Route 29 (`24:3`) scripts, catching tutorial and items before enabling it;
|
||
then capture, switching and PACK in battle.
|
||
|
||
## Route 29 and Cherrygrove increment: 2026-09-23
|
||
|
||
Enabled Route 29, its Route 46 gate, Cherrygrove City and its five buildings.
|
||
Item balls, the Pokémon Center nurse and the Poké Mart were each ported by a
|
||
Sonnet sub-agent in an isolated worktree, reviewed against the disassembly and
|
||
merged; the rest was done directly.
|
||
|
||
- Fruit trees run `FruitTreeScript`; a native `CheckDailyResetTimer` clears
|
||
the daily engine flags once per game day. `VAR_BOXSPACE` reads an empty box.
|
||
- Item balls run `FindItemInBallScript` with a native `TryReceiveItem`.
|
||
- The nurse runs `PokecenterNurseScript`; `HealParty` now applies
|
||
`ComputeMaxPP`'s PP Up clamp. `EnterMapWarp`'s spawn recording makes
|
||
whiteouts return to the last Pokémon Center entered.
|
||
- Standard marts run natively (`StandardMart`/`BuyMenu`/`SellMenu`).
|
||
- Battle command `recoil` (Struggle) was the only refusal found by a local
|
||
stress run of 360 Route 29 battles across starters and times of day.
|
||
- Save format 3 gained `fruitTrees`, `dailyReset` and `lastSpawnMap`
|
||
(migrated by absence).
|
||
- The phone-width status bar layout and parity test map counts were fixed.
|
||
|
||
Still refused: `catchtutorial` and Cherrygrove's rival scene (both after the
|
||
Mystery Egg), Route 30/46. NPCs do not roam yet.
|
||
|
||
Next: NPC roaming movement (`map_objects.asm` random walk/spin, radius and
|
||
visibility rules, deterministic RNG in tests), trainer sight lines and trainer
|
||
battles, Route 30 and Mr. Pokémon, capture and the catching tutorial.
|
||
|
||
## Trainer AI continuation: 2026-09-24
|
||
|
||
The trainer foundation was subsequently committed through `3e92936`. The next
|
||
AI increment translates Opportunist, Aggressive and Risky scoring, including
|
||
the ROM's stall/reckless/risky tables and Aggressive/Risky's deterministic
|
||
pre-variation damage estimate. Smart is now the only refused move-scoring layer,
|
||
so the rival remains gated. Continue with Smart before the rival, or with
|
||
trainer sight lines and normal trainer-object scripts for the already-supported
|
||
Youngster and Bug Catcher classes on Route 30.
|
||
|
||
## AI layer review: 2026-09-24 (resumed from Codex)
|
||
|
||
Claude Code checked Codex's uncommitted Opportunist, Aggressive and Risky layers
|
||
against `engine/battle/ai/scoring.asm`. The HP thresholds, `AI_50_50` (≥128),
|
||
`79 percent - 1` (200), last-wins ties, the power < 2 and reckless exclusions,
|
||
the strict-borrow KO test and the constant-damage paths all match.
|
||
|
||
One fix: `AIDamageCalc` goes through `EnemyAttackDamage` and
|
||
`BattleCommand_DamageCalc`, and both read `wCriticalHit`. A flag left over from
|
||
an earlier move therefore selects unboosted stats (ignoring screens) and
|
||
doubles the estimate. `enemyAIDamage` now reads the native `criticalHit`
|
||
instead of hard-coding `false`. It also reuses `truncate`. A local-ROM test
|
||
fixes the estimate at 4, 3 (Reflect) and 6 (leftover critical).
|
||
|
||
Known side effects of `AIDamageCalc` that are not modelled: it overwrites
|
||
`wCurDamage`, `wTypeModifier`, `wAttackMissed` and `hBattleTurn`. `CheckTurn`
|
||
resets the modifier and miss flag before each move. Whether the `wCurDamage`
|
||
overwrite can reach Counter/Mirror Coat still needs checking before those
|
||
effects are ported.
|
||
|
||
Final checks: 147 unit tests with the local ROM; 42 Chromium/WebKit browser
|
||
tests; native and reference builds; reference, release and vendor checks;
|
||
`git diff --check`. Not committed.
|
||
|
||
Next (unchanged): Smart AI before the rival, or trainer sight lines and
|
||
normal trainer-object scripts for Route 30's Youngster and Bug Catcher.
|
||
|
||
## Route 30 trainers: 2026-09-24
|
||
|
||
Chose option 2 from the review above: trainer sight lines and trainer-object
|
||
scripts. Both Route 30 trainers have two Pokémon, so the increment also ports
|
||
the trainer's send-out and switch AI. Route 30 and its Berry house are now
|
||
enabled in production.
|
||
|
||
- World: `_CheckTrainerBattle` at step end (before warps, coordinate events and
|
||
encounters) and on idle ticks; `TalkToTrainer` for `OBJECTTYPE_TRAINER`;
|
||
`BGEVENT_ITEM` hidden items. A refused sighting is not restarted until the
|
||
player moves (native safeguard against a rollback loop).
|
||
- Interpreter: `loadtemptrainer`, `trainertext`, `trainerflagaction`,
|
||
`scripttalkafter`, `endifjustbattled`, `checkjustbattled`, `writeobjectxy`,
|
||
`encountermusic`, `loadmem wRunningTrainerBattleScript`, `readmem
|
||
wHiddenItemID`, and callasm `TrainerWalkToPlayer`/`SetMemEvent`.
|
||
`applymovementlasttalked wMovementBuffer` decodes the native buffer.
|
||
- Battle: multi-Pokémon trainer parties (`HandleEnemyMonFaint`,
|
||
`EnemyPartyMonEntrance`, `FindMonInOTPartyToSwitchIntoBattle` with its
|
||
`BattleRandom` fallback), `AI_SwitchOrTryItem`/`CheckAbleToSwitch`/`AI_Switch`
|
||
with the original type-matchup, `e`-carry and quarter-HP bugs, and
|
||
`wPlayerUsedMoves`. `BattleTrainer` now requires `switchFlags`.
|
||
- Stress (local, not committed): 1,080 Route 30 battles; the only refusal was
|
||
Chikorita's Reflect at level 12 (`screen`), a pre-existing gap.
|
||
|
||
Final checks: 160 unit tests with the local ROM; 44 Chromium/WebKit browser
|
||
tests including the Route 30 sighting through the UI; native and reference
|
||
builds; reference, release and vendor checks; `git diff --check`. Not committed.
|
||
|
||
Next: Mr. Pokémon's house (Oak, Pokédex, Mystery Egg) and the return to Elm;
|
||
Reflect/Light Screen; then Smart AI before the rival. Joey (after the Egg)
|
||
needs `askforphonenumber` and the phone standard scripts.
|
||
|
||
## Reflect and Light Screen: 2026-09-24
|
||
|
||
Completed the battle gap found in the Route 30 stress run. `screen` now runs
|
||
both moves from ROM effect scripts. Independent five-turn counters include
|
||
the setup turn, do not refresh on repeated use, survive enemy send-outs and
|
||
expire with ROM text in `HandleScreens` order. The existing damage path handles
|
||
physical/special defense and critical stage rules.
|
||
|
||
Seven new local-ROM regressions cover player and enemy use, PP on failure,
|
||
concurrent durations, expiration order, send-out persistence and damage stats.
|
||
Also moved the NPC test's eager ROM read into `beforeAll`, restoring its
|
||
intended skip behavior when no ROM is available.
|
||
|
||
Checks: 167 unit tests with the local ROM; 76 passed and 91 skipped without it;
|
||
44 Chromium/WebKit browser tests; native and reference builds; reference,
|
||
release and vendor checks; `git diff --check`. Screen routines were reviewed
|
||
against pinned PRET source, not compared by executing the original routines.
|
||
Changes are uncommitted.
|
||
|
||
Next: Mr. Pokémon's house and the Mystery Egg; Smart AI before enabling the
|
||
rival; capture and the catching tutorial. Joey's post-Egg phone scripts remain
|
||
unsupported.
|
||
|
||
## Mr. Pokémon and the return to Elm: 2026-09-24 (resumed from Codex)
|
||
|
||
Codex had enabled Mr. Pokémon's house and added `NameRival`, `blackoutmod`,
|
||
`reloadmap`, the queued Elm call and the no-handler Smart AI subset. It
|
||
stopped with all four quest browser tests failing and without docs. Claude
|
||
Code finished the increment:
|
||
|
||
- The rival scene needed `special FadeOutMusic` (audio only, now accepted).
|
||
- The quest fixture's constant `() => 0` RNG hung `BattleCommand_DamageVariation`,
|
||
which redraws until the byte is at least 217. The fixture now uses a seeded LCG.
|
||
Relocating to Cherrygrove had also carried the house's moved Mr. Pokémon and
|
||
Oak onto the Guide Gent and rival (ids 2 and 3), producing an unrestorable
|
||
save. The fixture now clears objects and starts off the Guide Gent's tile.
|
||
- The browser test expected `specialPhoneCall` 1; `SPECIALCALL_ROBBED` is 2.
|
||
- `NameRival` yielded a naming choice with zero pages, because `promptbutton`
|
||
had already flushed them. The dialogue never reached its last page, so the
|
||
officer scene soft-locked. It now shows the ROM's "RIVAL'S NAME?" and reads
|
||
the "SILVER" default from ROM. As in `InitName`, blank names of any number of
|
||
spaces take the default. The naming input's accessible label no longer says
|
||
"Nickname".
|
||
- Elm's call now runs the ROM's `CheckSpecialPhoneCall.script` →
|
||
`Script_ReceivePhoneCall` wrapper. It reads the condition and caller script
|
||
from `SpecialPhoneCallList` instead of hard-coding id 2, adds `memcall`, and
|
||
shows `HangUp`'s timed "Click!". Ringing and the caller name box are still
|
||
not presented.
|
||
- Added tests: the rival scene for all three starters and both triggers, a
|
||
`CANLOSE` loss, reload after each scene, the Elm call (indoors, outdoors,
|
||
mid-call reload), the officer naming (named, blank, all-space, reload at
|
||
the naming screen), the Egg handover and the aide's Poké Balls, plus a
|
||
browser naming-field test.
|
||
|
||
Checks: 191 unit tests with the local ROM (80 passed, 111 skipped without it);
|
||
50 Chromium/WebKit browser tests; native and reference builds; reference,
|
||
release and vendor checks; `git diff --check`. Not committed.
|
||
|
||
Next: capture and the Route 29 catching tutorial (`catchtutorial`), the
|
||
first remaining refusal in the opening quest. Then the ringing presentation,
|
||
player switching and items in battle. Joey's post-Egg phone scripts remain
|
||
unsupported.
|
||
|
||
## Capture and the catching tutorial: 2026-09-24
|
||
|
||
Continued the roadmap with the first refusal remaining in the opening quest.
|
||
|
||
- Battle PACK opens the Balls pocket and runs `PokeBallEffect`: the Poké,
|
||
Great, Ultra and Master Ball multipliers, the original HP arithmetic
|
||
(including the overflow above 341 maximum HP), the sleep/freeze bonus
|
||
(BRN/PSN/PAR add nothing), `HELD_CATCH_CHANCE` and the catch draw.
|
||
`GetPokeBallWobble` draws per shake from `WobbleProbabilities` and picks
|
||
the break-free text. Trainer battles block the ball but still use it and
|
||
the turn. `catchRate` is exported for pure vectors.
|
||
- A catch reloads the Pokémon as `LoadEnemyMon` does (the wild item roll is
|
||
redrawn, then the original item restored), keeps HP/status/item/DVs,
|
||
restores the moves and PP it appeared with, adds it to the party with
|
||
`SetCaughtData`, registers it and offers a nickname (`InitName` default
|
||
for blank names). `NewDexDataText` shows when the Pokédex is owned.
|
||
`caughtData` is now shared with `givepoke`.
|
||
- `catchtutorial` runs `BATTLETYPE_TUTORIAL`: "DUDE" from the ROM as
|
||
`<PLAYER>`, no send-out, an automatic catch that keeps nothing, and
|
||
prompts that continue on their own. Both Route 29 triggers, declining and
|
||
`CatchingTutorialDudeScript`'s repeat offer run from the ROM.
|
||
- Text: `TX_LOW` ($05) is read as a line break (`ItemUsedText` needs it).
|
||
- UI: PACK lists the Balls pocket (B closes it); the in-battle nickname
|
||
prompt reuses the naming field. The help dialog and README describe the
|
||
current milestone.
|
||
- Refused: a catch with six Pokémon (the PC is not ported), other balls,
|
||
non-ball items in battle and a transformed target.
|
||
- Stress (local, not committed): 2,160 wild battles, 2,047 catches, no
|
||
refusals.
|
||
|
||
Checks: 207 unit tests with the local ROM (82 passed, 125 skipped without
|
||
it); 54 Chromium/WebKit browser tests; native and reference builds;
|
||
reference, release and vendor checks; `git diff --check`. The existing
|
||
keyboard/D-pad test now steps through the enabled PACK button. No
|
||
emulator comparison of the capture routines was run. Not committed.
|
||
|
||
Next: the PC and boxes (catching with a full party, `SendMonIntoBox`, Bill's
|
||
call), then player switching and other items in battle, and party and bag
|
||
menus. The Dude's back picture, ball animation and Pokédex page are not
|
||
presented.
|
||
|
||
|
||
## Overworld foundation recovery: 2026-09-25
|
||
|
||
Resumed Claude Code session `5d15e2b5-2020-4f49-83e7-eddee08c2315`, whose user
|
||
request was “Complete the overworld foundation.” Its last successful check
|
||
compared Johto and Ice Path animation with the original VRAM; the session then
|
||
hit its usage limit. The previous handoff ended at capture and was stale.
|
||
|
||
Preserved and validated Claude's uncommitted implementation:
|
||
|
||
- Turn delays, bumps, ice and currents, forced movement, dynamic return warps,
|
||
elevator menus, pits and arrival timing. Six original-engine traces cover
|
||
outdoor, connected, ledge, interior, scripted-follow and cave movement.
|
||
- Surf, Cut, Strength, Whirlpool/Waterfall interaction paths and cycling rules;
|
||
Strength boulders can fall into script-controlled holes. Later maps are
|
||
enabled only inside tests, not in the production map list.
|
||
- Working map blocks, `changeblock`, `changemapblocks`, rollback and minimap
|
||
invalidation; cave/water/Kanto encounter tables and `wildoff`.
|
||
- Additional NPC movement, variable sprites and Pokémon icons; all movement
|
||
command decoding and teleport/fishing/skyfall/shake/flicker presentation.
|
||
- Tileset animations on private graphics, grass sprite priority and special
|
||
map palettes. Johto and Ice Path match 96 original VRAM frames after matching
|
||
the reference animation state, including the scrolling scratch buffer.
|
||
|
||
Recovery changes:
|
||
|
||
- Removed `zz-grid.test.ts` and `zz-explore.spec.ts`, temporary diagnostics with
|
||
no assertions. The grid diagnostic also read a private ROM unconditionally.
|
||
- Added grass-priority/transparency and special-palette regressions, and ran
|
||
all 36 tilesets for 2,048 animation frames each while checking that cached
|
||
ROM graphics stay unchanged. `NUM_TILESETS` is verified by `check:reference`.
|
||
- Reconciled PLAN, ARCHITECTURE and NATIVE_PORT with the actual implementation
|
||
and its remaining gaps.
|
||
|
||
Validation: 271 unit tests passed with the local ROM. With an absent
|
||
`CRYSTAL_ROM_PATH`, 127 passed and 144 skipped. The full Chromium/WebKit run
|
||
passed 64 tests with 8 intentional WebKit skips; two of those passes were the
|
||
subsequently removed exploratory test, leaving 62 substantive browser checks.
|
||
The eight new original-engine comparisons run in Chromium only. Native and
|
||
reference builds, reference addresses/constants, release isolation and vendor
|
||
checks passed. No physical-device or CI run was performed. Changes remain
|
||
uncommitted; no push was made.
|
||
|
||
The whole foundation milestone is not complete. Continue with:
|
||
|
||
1. Room decorations: the two decoration specials still implement empty slots;
|
||
stored choices, ownership and the selection flow are absent.
|
||
2. Player bag-menu access to `GameWorld.useBicycle()`. Its rules are tested,
|
||
but it is not reachable through the current game controls.
|
||
3. Remaining reference acceptance: movement traces exclude the original's
|
||
map-entry tick and compare position/facing/selected actor positions, not
|
||
event flags. Most special poses and palettes lack executed-reference or
|
||
screenshot comparisons. The Celebi forest animation stays on frame one.
|
||
|
||
Do not clear those checklist items or enable later maps based on the existing
|
||
movement and rendering tests alone.
|
||
|
||
|
||
## Overworld foundation completed: 2026-09-25 (resumed from Codex)
|
||
|
||
After the recovery above, Codex continued with "Complete the foundation" and
|
||
stopped during its final validation, before updating this file. It had
|
||
already marked the milestone done in PLAN.md. Its work:
|
||
|
||
- Room decorations (`world/decorations.ts`): the initial bed and Town Map
|
||
poster, ownership through `DecorationAttributes` event flags, the bedroom
|
||
PC's selection and put-away flows (including left/right doll placement),
|
||
room blocks and variable sprites from saved choices, save validation and
|
||
migration of saves without decorations. The poster opens the native map.
|
||
- Start/Select opens Key Items; the Bicycle follows the native indoor and
|
||
outdoor rules, with keyboard and touch controls (`foundation-browser.spec.ts`).
|
||
- Movement traces now compare event flags as well as positions. Pose checks
|
||
compare direction, visibility and vertical offset during scripted
|
||
movement. An executed `LoadMapPals` matches 192 native palette cases.
|
||
Restless Ilex Forest and Tin Tower animation match original VRAM.
|
||
- Fixed fishing and flicker pose timing against the original routines.
|
||
|
||
Claude Code finished the increment. Codex had just added two pose traces.
|
||
Tree shake passed. Skyfall departure (`skyfall_top`) failed:
|
||
`StepFunction_SkyfallTop` leaves `OBJECT_SPRITE_Y_OFFSET` at $60 on the tick
|
||
that ends the command, and the offset persists through later steps. Actors
|
||
now keep a `yOffset`. Skyfall, teleports, the fishing bite and jumps clear it,
|
||
because those routines write the offset again. In the game, only the player
|
||
uses `skyfall_top` (Ruins of Alph chambers, Burned Tower), just before a warp.
|
||
The traces now number fourteen. PLAN and NATIVE_PORT were updated to match.
|
||
|
||
Checks: 276 unit tests with the local ROM (127 passed, 149 skipped without
|
||
it); 81 Chromium/WebKit browser tests passed and 19 were skipped intentionally
|
||
(the engine parity tests run in Chromium only); native and
|
||
reference builds; reference, vendor and release checks; `git diff --check`.
|
||
Not committed.
|
||
|
||
Next (PLAN section 2): the PC and boxes (a catch with a full party,
|
||
`SendMonIntoBox`, Bill's call), then the introduction's naming and clock
|
||
screens. Also open there: player switching and non-ball items in battle,
|
||
controlled-seed comparisons beyond damage and type effectiveness, and a
|
||
continuous opening-quest playthrough with save/reload. Later maps stay gated
|
||
until their scripts and story are supported.
|
||
|
||
## The PC and boxes: 2026-09-25
|
||
|
||
The foundation above was committed and pushed as `451464f`. This increment
|
||
continues PLAN section 2 with its first open item.
|
||
|
||
- `party/storage.ts`: 14 boxes of `BoxMon` (a party struct without status,
|
||
HP and stats), the current box, box names (null is the ROM's "BOX" and
|
||
number) and 50 PC item stacks. `SendGetMonIntoFromBox` deposit and
|
||
withdraw, `RestorePPOfDepositedPokemon` and `CalcLevel` are shared helpers.
|
||
`maxPP` (ComputeMaxPP) now also serves `HealParty`.
|
||
- `scripts/pc.ts`: `PokemonCenterPC` and the house PC. The Pokémon Center PC
|
||
opens Bill's PC (deposit, withdraw, release, change and name boxes) and the
|
||
player's PC (item withdraw, deposit and toss, the empty mail box), plus
|
||
Oak's rating once the Pokédex is owned. The house PC adds DECORATION. Menu
|
||
strings, lists and texts are read from the ROM (`PC` table in the profile,
|
||
verified by `check:reference`). The generic menu effect gained per-item
|
||
`details` (levels, box counts). A `notice` effect covers unported
|
||
options without aborting.
|
||
- Facing tiles now use the ROM's `TileCollisionStdScripts` in any direction,
|
||
as in the original (the old table only fired when facing up).
|
||
- Capture with a full party: `SendMonIntoBox` puts the catch first in the
|
||
current box, with nickname and `BallSentToPCText`. A full box keeps the ball
|
||
(`Ball_BoxIsFullMessage`). Filling the box sets `BATTLERESULT_BOX_FULL`.
|
||
`reloadmapafterbattle` then queues Bill's call through a new `memscript`
|
||
effect (`LoadMemScript`). It is kept in memory only and runs before scene
|
||
scripts.
|
||
- Save format 3 gained `storage` and `pcItems`, migrated by absence.
|
||
|
||
Not ported: MOVE PKMN W/O MAIL and the stats screen (notices), printing,
|
||
`givepoke` with a full party (still refused; unreachable in the open maps),
|
||
the save messages after a box switch, and the tutorial with a full party and
|
||
box (the original loops forever; refused).
|
||
|
||
Checks: 290 unit tests with the local ROM (130 passed, 160 skipped without
|
||
it); 83 Chromium/WebKit browser tests passed, 19 skipped intentionally
|
||
(including a new Pokémon Center PC flow in both browsers); native and
|
||
reference builds; reference, vendor and release checks; `git diff --check`.
|
||
Not committed.
|
||
|
||
Next (PLAN section 2): the introduction's naming and clock screens; player
|
||
switching and non-ball items in battle; MOVE PKMN W/O MAIL and the stats
|
||
screen with the party menu; a continuous opening playthrough.
|
||
|
||
## The introduction's naming and clock: 2026-09-25
|
||
|
||
The PC increment above was committed as `3bdca00`. This increment finishes
|
||
the next open item of PLAN section 2.
|
||
|
||
- `scripts/intro.ts` (`IntroRun`): `InitGender`, `OakSpeech` with
|
||
`InitClock` and `NamePlayer`, as a native generator the world runs like a
|
||
script (`GameWorld.beginIntro`). Texts, menus, preset names and time
|
||
strings come from the ROM through a new `INTRO` profile table verified by
|
||
`check:reference`.
|
||
- New effects: `portrait` (Oak's trainer picture or the player's, decoded
|
||
by `PokemonData.trainerPic`, `playerPic` and `trainerPalette`) and a
|
||
`clock` stepper choice.
|
||
- Game clock: `clock.minuteOffset` is added to the device time for time of
|
||
day, weekday, NPC hours and the daily reset. The host's `deviceWeekday`
|
||
became `clockDay`.
|
||
- `GameSession.load` calls the runtime's optional `newGame()` when nothing
|
||
is saved. Saves gained `intro` (true while pending; a save made then
|
||
restarts the introduction). The shell's name/character fields are gone.
|
||
- Browser fixture specs strip `this.world?.beginIntro();` from the served
|
||
runtime, since their worlds stand in for saved sessions. Fresh-load tests
|
||
play the intro with `tests/intro-driver.ts`.
|
||
|
||
Not presented: palette fades, Wooper's cry, the picture slide while
|
||
naming, `ShrinkPlayer` and music. Intentional change: the steppers start at
|
||
the device time instead of 10:00.
|
||
|
||
Checks: 298 unit tests with the local ROM; native and reference builds;
|
||
reference, vendor and release checks; `git diff --check`; 87
|
||
Chromium/WebKit browser tests passed, 19 skipped intentionally (including
|
||
the new intro flow and a reload during it, in both browsers). Committed and
|
||
pushed to `main` as `b13fd20`.
|
||
|
||
Next (PLAN section 2): player switching and non-ball items in battle; MOVE
|
||
PKMN W/O MAIL and the stats screen with the party menu; a continuous opening
|
||
playthrough.
|
||
|
||
## Remaining opening-milestone items: 2026-09-30
|
||
|
||
The introduction was committed and pushed as `b13fd20` (handoff note
|
||
`eb03a20`). Asked to go over what was left and do the quickest items
|
||
first, this session took PLAN section 2's open items in that order:
|
||
|
||
1. Evolution decoder: `PokemonData.evolutions` returns typed level, item,
|
||
trade, happiness and stat records (Tyrogue's are four bytes). A
|
||
local-ROM test compares all 251 lists with the pinned
|
||
`evos_attacks.asm`. Evolving is not ported.
|
||
2. Reference vectors: `tests/reference-parity.spec.ts` now also calls the
|
||
original `CalcMonStats` (300 cases, including levels above 100 for the
|
||
999 cap; the trampoline sets BC, DE and HL) and `CalcExpAtLevel` (all
|
||
600 growth/level pairs). A mutation (removing the cap) was confirmed to
|
||
fail. Symbol loading and the ROM hand-off are shared helpers now. The
|
||
checklist item stays open for random-driven routines (accuracy,
|
||
criticals, damage variation, capture).
|
||
3. Continuous playthrough: `tests/playthrough.test.ts` plays a new game
|
||
from the intro to the catching tutorial through `GameWorld` inputs with
|
||
a seeded RNG, reloading a JSON save after every stage. Wild battles are
|
||
fought, or fled below half HP (fighting everything whited out on Route
|
||
29). The rival battle is lost with this seed (`CANLOSE`), and the story
|
||
continues. The stages share one world, so run the whole file.
|
||
4. Switching and healing items in battle (details in NATIVE_PORT):
|
||
- PKMN/`TryPlayerSwitch`/`PlayerSwitch`/`BattleMonEntrance` with
|
||
`SendOutMonText`/`WithdrawMonText`; `AskUseNextPokemon`,
|
||
`ForcePlayerMonChoice`, `DoubleSwitch`; Shift mode's
|
||
`OfferSwitch`. New `party` prompt (`menu`, `cancel`) answered through
|
||
`battleChoose` (a slot or -1), and `{ action: "pkmn" }`.
|
||
- Items pocket: `CheckItemContext`, `ItemEffects` dispatch by ROM
|
||
address, `RestoreHPEffect` and `StatusHealingEffect` with the ROM's
|
||
tables and party menu texts. `PARTYMENUTEXT_*` values are PRET's
|
||
($F0-based; `check:reference` caught a wrong value).
|
||
- UI: PKMN party list with the SWITCH/STATS/CANCEL submenu (STATS
|
||
disabled), and ITEM/BALL POCKET tabs in PACK (opens on Items). The
|
||
keyboard test now passes through the enabled PKMN button.
|
||
|
||
Known limits: `hBattleTurn` at the battle menu is not modelled everywhere,
|
||
so a status heal that would make `CalcPlayerStats` reapply a penalty to a
|
||
paralysed or burned enemy is refused. `WithdrawMonText` treats the
|
||
multiplicand's leftover high byte as zero. X items, Poké Doll, Full
|
||
Restore, revives and the stats screen are not ported.
|
||
|
||
Checks: 324 unit tests with the local ROM (130 passed, 194 skipped
|
||
without it); 93 Chromium/WebKit browser tests passed, 19 skipped
|
||
intentionally; native and reference builds; reference, release and
|
||
vendor checks; `git diff --check`. Committed and pushed to `main` as
|
||
`f8e0f9c`.
|
||
|
||
Next (PLAN section 2): controlled-seed comparisons of random-driven
|
||
battle routines. Then PLAN section 3/4 items: the party and bag menus
|
||
outside battle (with the stats screen and MOVE PKMN W/O MAIL), evolution
|
||
itself, and the remaining battle items.
|
||
|
||
## Battle items, hBattleTurn and the stats screen: 2026-09-30
|
||
|
||
After `f8e0f9c`/`cfd083c`, the user asked for the items the previous
|
||
summary listed as refused or not ported:
|
||
|
||
- Every non-ball item effect usable in battle, found by reading each
|
||
item's battle context and `ItemEffects` entry from the ROM: Full
|
||
Restore, Revive/Max Revive, Energypowder, Energy Root, Heal Powder,
|
||
Revival Herb, Bitter Berry, Ether/Max Ether/Elixer/Max Elixer/
|
||
MysteryBerry (new `move` prompt), X items, X Accuracy, Guard Spec, Dire
|
||
Hit and Poké Doll. `ChangeHappiness` now reads `HappinessChanges`
|
||
(including the `wPartyMenuCursor` quirk), which also fixed level-ups:
|
||
`LevelUpHappinessMod`'s larger gain where the Pokémon was caught was
|
||
missing (new `landmark` battle option). A second participant set
|
||
(`wBattleParticipantsIncludingFainted`) serves revives.
|
||
- `hBattleTurn` at the battle menu: `DoBattle` ends on the player's turn,
|
||
full turns end on the enemy's, and `chooseTrainerMove` now returns
|
||
`enemyTurn` when `AI_Types`, `AI_Status` or `AIDamageCalc` ran. The
|
||
status-heal refusal is gone; the enemy's penalty is reapplied as in the
|
||
original.
|
||
- Stats screen: three pages from ROM strings (`STATS` profile table,
|
||
verified by `check:reference`), `TypeNames` and `GetGender`. It opens from
|
||
BattleMonMenu and from Bill's PC (a `stats` script effect;
|
||
`world.statsScreen`).
|
||
- Emulator comparisons: a new reference test runs `SendOutMonText` (with
|
||
its final `jp BattleTextbox` patched to `ret` in the in-page ROM copy),
|
||
`WithdrawMonText`'s text routine, `RestoreHealth` (with `wHPBuffer1` set
|
||
as `ItemRestoreHP` leaves it), `RestorePP`, `ChangeHappiness` and
|
||
`CalcPlayerStats` with both turns. A mutation of `calcPlayerStats` was
|
||
confirmed to fail. The pure helpers are exported from `battle.ts`.
|
||
|
||
Remaining: Kurt's and the Park Ball; the leftover multiplicand byte in
|
||
`WithdrawMonText` is assumed zero; no whole battle has been compared with
|
||
the original.
|
||
|
||
Checks: 337 unit tests with the local ROM (131 passed, 206 skipped
|
||
without it); 97 Chromium/WebKit browser tests passed, 19 skipped
|
||
intentionally; native and reference builds; reference, release and
|
||
vendor checks; `git diff --check`. Committed and pushed to `main` as
|
||
`b13e09e`.
|
||
|
||
## Controlled random battle comparisons: 2026-09-30
|
||
|
||
Resumed from `be7575a` with a clean working tree. The user requested a bounded
|
||
increment suitable for a limited session. Continued PLAN section 2 with
|
||
accuracy, criticals and damage variation; capture stays as the next item.
|
||
|
||
- `tests/reference-random.spec.ts` runs the original, unpatched
|
||
`BattleCommand_Critical`, `BattleCommand_CheckHit` and
|
||
`BattleCommand_DamageVariation` in the reference emulator. The native
|
||
commands receive the same ten-byte Link RNG stream. Results include the
|
||
final RNG stream/cursor, so extra or missing draws are detected too.
|
||
- Per browser: 2,560 critical cases (all random bytes at all five reachable
|
||
stages and held-item exceptions), 2,914 accuracy cases (all stage pairs,
|
||
Foresight, BrightPowder, bypasses, Dream Eater/substitute and Jump Kick
|
||
miss damage), and 2,149 variation cases (0/1, rounding, 16-bit boundaries,
|
||
rejected draws and RNG stream regeneration).
|
||
- Shared ROM input and symbol helpers moved from `reference-parity.spec.ts`
|
||
into `tests/reference-fixture.ts`. No ROM bytes or extracted assets added.
|
||
- Deliberately changing critical `<` to `<=`, accuracy `>=` to `>`, and the
|
||
damage divisor from 255 to 256 each made its comparison fail. All three
|
||
mutations were reverted; no production gameplay change was needed.
|
||
|
||
Boundary: these compare isolated commands with controlled random inputs.
|
||
They do not compare a whole battle, the hardware-timed non-Link RNG, or the
|
||
Protect text path. Lock-On/Fly/Dig remain refused. This does not enable Link
|
||
battles or later maps.
|
||
|
||
Checks: 337 unit tests passed with the local ROM; 131 passed and 206
|
||
skipped with an absent ROM. All 103 Chromium/WebKit browser tests passed,
|
||
with the existing 19 intentional WebKit skips. Native and reference builds,
|
||
reference, vendor and release checks, and `git diff --check` passed. No CI
|
||
or physical-device run. This increment was initially left uncommitted and
|
||
is included in the subsequent capture-comparison delivery below.
|
||
|
||
Next: controlled-input capture comparisons (`PokeBallEffect` and
|
||
`GetPokeBallWobble`), then the party/bag menus outside battle, MOVE PKMN W/O
|
||
MAIL and evolution. Keep section 2's capture checklist open until verified.
|
||
|
||
|
||
## Controlled capture comparisons: 2026-09-30
|
||
|
||
Continued PLAN section 2 from the existing uncommitted accuracy, critical and
|
||
damage-variation comparisons. Preserved that work and completed its next
|
||
capture-comparison item.
|
||
|
||
- `tests/reference-capture.spec.ts` compares the native capture decision with
|
||
the calculation inside `PokeBallEffect`, then its shake loop with
|
||
`GetPokeBallWobble`. Per browser: 3,220 capture cases and 7,424 shake cases,
|
||
including supported ball multipliers, all catch-rate bytes, HP scaling and
|
||
overflow, status bonuses, equality boundaries, every random byte at each
|
||
wobble-table entry, every escape count and guaranteed catches. Final rate,
|
||
caught/escaped outcome, shake count and random-draw count agree.
|
||
- Capture uses hardware `Random`, not `BattleRandom`. The private in-page ROM
|
||
copy replaces `Random` with a supplied-byte reader; the fixture enters
|
||
after the UI setup and inserts a return after `wWildMon` is written.
|
||
Catch arithmetic, multipliers and `GetPokeBallWobble` remain unmodified.
|
||
- Extracted `rollCapture` and `ballWobbles` as private production helpers
|
||
called by `pokeBallEffect`, without changing gameplay behavior. Added
|
||
maximum HP 683 beside 342 in the synthetic divide-by-zero regression.
|
||
- Deliberately changing success `<=` to `<`, formula floor to ceiling, and
|
||
shake `>=` to `>` each failed the comparisons. Mutations were reverted.
|
||
|
||
Boundary: isolated capture decision/shakes with supplied random bytes, not
|
||
hardware RNG timing, animation, caught-mon reloading, party/PC writes or a
|
||
whole battle. No actual item has `HELD_CATCH_CHANCE`; that effect remains
|
||
covered by pure vectors. Native division-by-zero refusal remains in place;
|
||
the emulator fixture omits the hanging HP inputs. No additional balls or
|
||
maps are enabled.
|
||
|
||
Checks: 337 unit tests passed with the local ROM; 131 passed and 206 skipped
|
||
with an absent ROM. All 107 Chromium/WebKit browser tests passed, with the
|
||
existing 19 intentional WebKit skips. The four new capture tests skip cleanly
|
||
without a ROM. Native and reference builds, reference, vendor and release
|
||
checks, and `git diff --check` passed. No CI or physical-device run.
|
||
The accuracy, critical, damage-variation and capture comparisons were committed
|
||
together as `00972ba` and pushed to `origin/main`; the remote commit was
|
||
verified. This handoff-only follow-up records that delivery.
|
||
|
||
Next: party and bag menus outside battle, MOVE PKMN W/O MAIL and evolution.
|
||
PLAN section 2 is complete within its recorded acceptance boundaries.
|
||
|
||
|
||
## Start menu, party menu and pack: 2026-09-30
|
||
|
||
Resumed from `60e06ae` (clean tree) with "continue" from the handoff: the
|
||
next plan items were the party and bag menus outside battle, then
|
||
evolution.
|
||
|
||
- `src/game/scripts/start-menu.ts` (`StartMenuRun`): Start opens the
|
||
original start menu (items and descriptions from `StartMenu.Items`, list
|
||
from `SetUpMenuItems`). POKéMON: party menu (`PartyMenuStrings`), the
|
||
submenu from `MonMenuOptions` (field moves first), STATS, SWITCH,
|
||
MOVE (swap moves with PP), ITEM (GIVE from the pack with swap and
|
||
pack-full refund, TAKE). PACK: four pockets with left/right, the item
|
||
submenus from `MenuHeader_*.MenuData` by permissions and field menu,
|
||
TOSS with quantity and confirmation, GIVE, and USE through `ItemEffects`
|
||
addresses: HP items, status healers, Full Restore, revives, bitter
|
||
herbs, Ethers/Elixers/MysteryBerry and PP Up (one step, because
|
||
`wUsePPUp` aliases `wTempPP`), and the Bicycle (queued script after the
|
||
menu closes; Oak's warning where it cannot be used).
|
||
- It runs on a script draft; saves while open keep the prior state. Menu
|
||
cursors persist in memory and reset after battle (`CleanUpBattleRAM`);
|
||
the party cursor now reaches the battle's `ChangeHappiness` quirk.
|
||
- Select keeps the Key Items shortcut (browser tests moved from Enter to
|
||
Shift). New `pack` script effect/choice; `MENU` profile table checked by
|
||
`check:reference`; menu, pocket and PP constants moved into the verified
|
||
`C` block; `ENGINE_POKEGEAR` added and verified. The text decoder gained
|
||
`<POKE>` ($24), needed for "POKéGEAR".
|
||
- Notices without changes: POKéDEX, POKéGEAR, trainer card, OPTION, field
|
||
moves from the menu, MAIL (giving or reading), SEL, evolution stones,
|
||
Rare Candy, vitamins and other field items.
|
||
|
||
Checks: 353 unit tests with the local ROM (16 new in
|
||
`tests/start-menu.test.ts`); 109 Chromium/WebKit browser tests passed, 19
|
||
skipped intentionally (one new start-menu flow in both browsers); native
|
||
and reference builds, reference, vendor and release checks; `git diff
|
||
--check`. No emulator comparison of the menus; no CI or physical-device run.
|
||
|
||
Next: evolution (`EvolveAfterBattle` after battles and from stones/Rare
|
||
Candy, with the level-up flags the battle must record), then vitamins,
|
||
MOVE PKMN W/O MAIL, SEL/registered items and field moves from the menu.
|
||
|
||
The start menu increment was committed and pushed as `1af2bea`.
|
||
|
||
## Evolution: 2026-09-30
|
||
|
||
Continued in the same session after `1af2bea`.
|
||
|
||
- `src/game/scripts/evolution.ts` (`PartyEvolution`): `EvolveAfterBattle`
|
||
walking the raw evolution bytes with the original's skip counts
|
||
(forced evolutions misread `EVOLVE_STAT` like the original), the texts,
|
||
a timed `evolve` effect (40 ticks, then seven wait/flash rounds; B only in
|
||
the waits and never for stones), nickname update only for unnicknamed
|
||
Pokémon, stats with stat experience, HP rise, `LearnLevelMoves`,
|
||
`LearnMove`/`ForgetMove` outside battle, and the Pokédex.
|
||
- The battle records `wEvolvableFlags` on level-up and returns them; after a
|
||
win the world runs the evolution as a script interlude before resuming
|
||
the battle's script (same draft, commits together).
|
||
- PACK: Rare Candy (level, exp, HP rise that revives, happiness, level-up
|
||
text, stat page, moves, evolution), evolution stones (ABLE/NOT ABLE party
|
||
labels with the original 10-byte/3-step check; Everstone and no-match
|
||
have no effect) and vitamins (stat experience high byte +10 up to 100).
|
||
- UI: the picture box shows the flashing evolution; the pack's pocket tabs
|
||
moved into their own compact row (`#mart-tabs`).
|
||
|
||
Checks: 360 unit tests with the local ROM (5 new start-menu evolution
|
||
tests, 2 new battle-world tests); 111 Chromium/WebKit browser tests passed,
|
||
19 skipped intentionally (a new Rare Candy evolution flow in both
|
||
browsers); native and reference builds, reference, vendor and release
|
||
checks; `git diff --check`. No emulator comparison of evolution or the
|
||
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).
|
||
|
||
## The Pokégear, phone calls and Mom's savings: 2026-10-01
|
||
|
||
Resumed with "Continue the plan" after `3456aa4`, taking the recorded next
|
||
item (the Pokégear).
|
||
|
||
- `src/game/scripts/pokegear.ts` (`Pokegear`): POKéGEAR in the start menu
|
||
runs the clock, map and phone cards with the original joypad rules,
|
||
yielding a `pokegear` effect per card answered with a button name. The
|
||
world routes A/B/directions/Start/Select to it, keeps `world.pokegear` on
|
||
screen during call texts and refreshes the clock every tick. The radio
|
||
card (Goldenrod only) gives a notice.
|
||
- Calls: no-service text, two "……" rings, out-of-hours or same-map scripts,
|
||
otherwise the callee script from `PhoneContacts` in `ScriptRun` on the
|
||
start menu's draft (adopted with `Object.assign` when it ends), then
|
||
`HangUp`. Elm's and Mom's callee scripts run; Mom's lecture after the Dude
|
||
sets `ENGINE_MOM_SAVING_MONEY`.
|
||
- Interpreter: `random`, `givemoney`/`takemoney`/`checkmoney`/`getmoney`
|
||
for both accounts, `getcurlandmarkname`, `getlandmarkname` (operand order
|
||
checked against the macros), `ScriptHost.phoneService`, `landmarkName`.
|
||
`HangUp` is now "Click!" 10 ticks, then "……" 60 ticks (it was "Click!" for
|
||
140 ticks, double the original's 140 frames); two `mr-pokemon` assertions
|
||
were updated.
|
||
- Mom's savings: `momsMoney` in `ScriptState` and saves (migrated by absence
|
||
to 0). Battles take `mom: { saving, money }` and return `momMoney`;
|
||
`WinTrainerBattle.give_money` sends a quarter while she saves some.
|
||
`MomTriesToBuySomething` refuses once her balance reaches ¥900 (not
|
||
reachable in the open area).
|
||
- Map headers decode `phoneService`. New `POKEGEAR` profile table and OW
|
||
constants (landmark limits, card/Mom engine flags, money results), all
|
||
verified by `check:reference`.
|
||
- UI: `#pokegear` panel (card labels, clock, Town Map canvas with the
|
||
cursor frame, phone list with ▶/▷ and the submenu); the textbox is the
|
||
native dialogue, hidden on the map card; the minimap hides while it is
|
||
open. `drawTownMap` takes an optional card (region, player, cursor).
|
||
|
||
Checks: 381 unit tests with the local ROM (131 passed, 250 skipped without
|
||
it; new `tests/pokegear.test.ts`, plus Mom tests in `trainer-battle` and
|
||
`trainer-world`); 113 Chromium/WebKit browser tests passed, 19 skipped
|
||
intentionally (a new Pokégear flow in both browsers); native and reference
|
||
builds; reference, vendor and release checks; `git diff --check`. No
|
||
emulator comparison of the Pokégear; no CI or physical-device run. Not
|
||
committed.
|
||
|
||
Next: `BankOfMom` (talking to Mom after the Egg still refuses with special
|
||
34), then Mom's purchases (`MomTriesToBuySomething`, `wWhichMomItem`,
|
||
`wMomItemTriggerBalance`), the trainer card and options; later the Fly map
|
||
and Teleport/Dig warps.
|
||
|
||
## Mom's bank and her purchases: 2026-10-01
|
||
|
||
Resumed with "Continue the plan" on top of the uncommitted Pokégear
|
||
increment above (still not committed), taking the recorded next items.
|
||
|
||
- `src/game/scripts/mom.ts` (`bankOfMom`): `BankOfMom` as a special. First
|
||
visit: `MomLeavingText1`–`3` and the saving question; later: "Or is this
|
||
about your money?", then GET/SAVE/CHANGE/CANCEL from
|
||
`BankOfMom_MenuHeader`. The amount box is a `mombank` effect answered
|
||
with button names (the world routes A, B and directions to it like the
|
||
Pokégear, via `buttonChoice`). `DigitQuantities` is read from the ROM;
|
||
`GiveMoney`/`TakeMoney` limits, cursor 0–5 starting on 5, zero or B
|
||
cancels, refusals return to a fresh box. Declining runs `DSTChecks`
|
||
(near-midnight booklet refusal; `shiftClockHour` wraps `minuteOffset`
|
||
into ±720 and moves `weekdayOffset` the other way).
|
||
- `MomTriesToBuySomething` is ported (it had to be: deposits make the ¥900
|
||
trigger reachable at once, and it refused after every trainer win):
|
||
MomItems_2 triggers, the ¥2300 `wMomItemTriggerBalance` steps with the
|
||
exact-match `RandomRange(5)` MomItems_1 purchase, PC items (full PC
|
||
cancels), doll decoration events, and the call through the existing
|
||
`memscript` path (`Script_ReceivePhoneCall` with
|
||
`Mom_GetScriptPointer`'s item or doll script). `.ASMFunction`'s cost and
|
||
`wWhichMomItem` are applied when the call is queued. `randomRange` is
|
||
shared with `Script_random`.
|
||
- Saves gained `momItem` (0–10) and `momItemTriggerBalance`, migrated by
|
||
absence to 0 and ¥2300.
|
||
- New `MOM` profile table and extra `POKEGEAR` entries (MomItems lists,
|
||
item/doll scripts), verified by `check:reference`.
|
||
- UI: the amount box in the dialogue choice (`.mom-bank`, a blinking digit
|
||
that respects reduced motion) with ◀ ▼ ▲ ▶, OK and CANCEL buttons.
|
||
Checked visually at desktop and 390px widths.
|
||
|
||
Checks: 398 unit tests with the local ROM (132 passed, 266 skipped without
|
||
it; new `tests/mom.test.ts`, purchase tests replace the refusal test in
|
||
`tests/trainer-world.test.ts`); 115 Chromium/WebKit browser tests passed,
|
||
19 skipped intentionally (new deposit flow in both browsers); native and
|
||
reference builds; reference, vendor and release checks; `git diff --check`.
|
||
No emulator comparison of the bank or purchases; no CI or physical-device
|
||
run. Not committed.
|
||
|
||
Next: the trainer card and options; later the Fly map and Teleport/Dig
|
||
warps.
|
||
|
||
## Trainer card and play time: 2026-10-01
|
||
|
||
Resumed with "continue the plan" after `38b9562` (clean tree), taking the
|
||
recorded next item (the trainer card, board card #23). The Pokégear and
|
||
Mom's bank above were committed as `209778c`.
|
||
|
||
- `src/game/scripts/trainer-card.ts` (`TrainerCard`): the player's name in
|
||
the start menu runs the card and returns to the start menu. Each page is
|
||
a `trainercard` effect answered with a button (a `buttonChoice` like the
|
||
Pokégear): page 1 A/right → page 2; page 2 A leaves, left → page 1; B
|
||
leaves from both. The Kanto page (unreferenced code) is not shown. Labels
|
||
from `.Name_Money`, `.Dex_PlayTime` and `.Badges`; the Pokédex line only
|
||
with `ENGINE_POKEDEX`; badges from the Johto badge engine flags.
|
||
- `src/game/world/game-time.ts`: `wGameTime` and `GameTimer` (60 frames a
|
||
second, capped at 999:59:59.00). `GameWorld.gameTime` advances two
|
||
frames per tick unless the introduction is pending; `beginIntro` resets
|
||
it. `save()` during a scene returns the committed state with the current
|
||
play time. Saves gained `gameTime`, migrated by absence to zero.
|
||
- The world refreshes the card's time, blinking colon (every 32 frames on
|
||
page 1) and badge step (every 8 frames on page 2, only with badges) each
|
||
tick, counting from the page load.
|
||
- `src/game/rendering/trainer-card.ts`: the Chris/Kris card picture
|
||
(tile $1c left blank, as the corner covers it) and page 2's leader faces
|
||
with the owned badges' sprites, coloured by `_CGB_TrainerCard`'s
|
||
attribute map. UI: `#trainer-card` panel, frame colour from the other
|
||
gender's palette; checked visually at desktop and 390px widths.
|
||
- New `TRAINER_CARD` profile table, OW constants (`ENGINE_MINERALBADGE`,
|
||
`NUM_JOHTO_BADGES`, `PREDEFPAL_CGB_BADGE`, the leaders' trainer classes).
|
||
`check:reference` now understands the `trainerclass` macro.
|
||
- Tests that compare a save before and after a refused or rolled-back
|
||
action now ignore the play time (`withoutPlayTime` in
|
||
`tests/world-driver.ts`), which keeps counting.
|
||
|
||
Checks: 407 unit tests with the local ROM (134 passed, 273 skipped without
|
||
it; new `tests/trainer-card.test.ts`); 117 Chromium/WebKit browser tests
|
||
passed, 19 skipped intentionally (a new trainer card flow in both
|
||
browsers); native and reference builds; reference, vendor and release
|
||
checks; `git diff --check`. No emulator comparison of the card; no CI or
|
||
physical-device run.
|
||
|
||
Next: the options screen (board #24), then the Fly map and Teleport/Dig
|
||
warps.
|
||
|
||
The trainer card increment was committed as `4769856` (`Closes #23`; the
|
||
hook moved the card to Fait). Not pushed.
|
||
|
||
## Options: 2026-10-01
|
||
|
||
Continued in the same session with board card #24.
|
||
|
||
- `src/game/scripts/options.ts` (`OptionsMenu`): OPTION in the start menu
|
||
runs `_Option` on `state.options` (the start menu's draft) and returns to
|
||
the start menu. `options` effect answered with buttons; Start/B leave,
|
||
A on CANCEL leaves; `OptionsControl` wrapping; each option routine ported
|
||
from its bits (text speed's `.Save` keeps only the high nybble, so bit 3
|
||
is lost and bit 4 kept). Labels from `StringOptions` (read with a longer
|
||
`readString` limit), settings from the routines' strings.
|
||
- `GameOptions` = `wOptions`, `wTextboxFrame`, `wGBPrinterBrightness`,
|
||
`wOptions2`; `defaultOptions()` is `DefaultOptions`. In `ScriptState` and
|
||
saves (migrated by absence; frame above 7 refused).
|
||
- Consumers: `Battle` takes `shift` (from `BATTLE_SHIFT`) and
|
||
`CheckWhetherToAskSwitch` honours it; `StartMenu` drops descriptions
|
||
without `MENU_ACCOUNT`. Text speed, battle scene, sound, print and frame
|
||
are stored only.
|
||
- New `OPTIONS` profile table and OW option constants, verified by
|
||
`check:reference` (decimal literals: the checker skips `0b` values, which
|
||
a deliberately wrong `TEXT_DELAY_MED` confirmed).
|
||
- UI: `#options` panel, labels in a fixed column; checked at desktop and
|
||
390px widths.
|
||
|
||
Checks: 413 unit tests with the local ROM (134 passed, 279 skipped without
|
||
it; new `tests/options.test.ts` and a Set-mode test in
|
||
`tests/battle-switch.test.ts`); 119 Chromium/WebKit browser tests passed,
|
||
19 skipped intentionally (a new options flow in both browsers); native and
|
||
reference builds; reference, vendor and release checks; `git diff --check`.
|
||
No emulator comparison; no CI or physical-device run.
|
||
|
||
Next: the Fly map and Teleport/Dig/Escape Rope warps (board #25), which
|
||
need script commands after a warp; then Headbutt and Rock Smash (#26).
|
||
|
||
The options increment was committed as `c4cc0e7` (`Closes #24`). Not pushed.
|
||
|
||
## Fly, Dig, Teleport and the Escape Rope: 2026-10-02
|
||
|
||
Resumed with "continue" on a clean tree after `c4cc0e7`, taking board card
|
||
#25.
|
||
|
||
- `src/game/scripts/fly-map.ts` (`flyMap`): `_FlyMap`/`FlyMap` inside the
|
||
start menu run, before the world queues `FlyFunction.FlyScript`. Region
|
||
by the current landmark (Kanto only with Indigo Plateau's flypoint, else
|
||
Johto without the player icon), default New Bark/Indigo, up/down over
|
||
visited flypoints with wrapping (refused if none is visited, where the
|
||
original hangs), A returns the spawn, B goes back to the party menu.
|
||
`flymap` effect answered with buttons (`FLY_MAP_BUTTONS`).
|
||
- Visits: `assets.spawnEngineFlag` inverts the ROM's `EngineFlags` over
|
||
`wVisitedSpawns` (Union Cave and the Fast Ship have no flag).
|
||
`assets.spawnPoint`/`spawnIndex` read `SpawnPoints` (`IsSpawnPoint`).
|
||
- Interpreter: `newloadmap` handles `MAPSETUP_WARP`/`TELEPORT`/`FLY`
|
||
(spawn from `fieldMove.spawn` or `GetWhiteoutSpawn`) and `MAPSETUP_DOOR`
|
||
(`fieldMove.warp`, resolved by the world), then stops; the `warp` effect
|
||
carries `resume` unless only `end`/`endall` follows, and refuses inside a
|
||
call. The world's `finishScript` starts the rest of the script right after
|
||
entering the map, with the same field-move context (`ActiveScript.fieldMove`).
|
||
`door` warps keep the facing unless `FACE_DOWN_ON_WARP`.
|
||
`FieldMoveContext` is now one exported type.
|
||
- `fly` effect (`FlyFromAnim` 65 ticks, `FlyToAnim` 33): `poses()` is empty,
|
||
`world.flyingIcon` replays `SpriteAnimFunc_FlyFrom`/`FlyTo` with the
|
||
ROM sine table, and the renderer draws `assets.monIcon` with
|
||
`RED_WALK`'s nine-frame flaps in object palette 0. Leaves not drawn.
|
||
- World: `SaveDigWarp` in `prepareWarp` (outdoor to `CheckIndoorMap`, not
|
||
from 15:10/15:12); `digWarpMove` for Dig and the Escape Rope (with
|
||
`SpecialKabutoChamber`); Teleport through `IsSpawnPoint`; an unported
|
||
destination gives a notice. Saves gained `digWarp` (absent → null).
|
||
- Start menu: `{ flyMap: true }` verdict, `host.flyTo`, `host.escapeRope`
|
||
(`MENU.EscapeRopeEffect`: `UseDisposableItem` only on success; failure
|
||
falls to Oak's warning), `host.visitedSpawn`.
|
||
- UI: `#fly-map` with the bubble and the Town Map (`drawTownMap` takes an
|
||
optional `player` and a `species` icon). Checked visually at desktop and
|
||
390px widths, including the flight and landing.
|
||
|
||
Checks: 423 unit tests with the local ROM (134 passed, 289 skipped without
|
||
it; new `tests/fly-dig-teleport.test.ts`); 121 Chromium/WebKit browser tests
|
||
passed, 19 skipped intentionally (a new Fly flow in both browsers, with a
|
||
"fly" fixture room); native and reference builds; reference, vendor and
|
||
release checks; `git diff --check`. No emulator comparison of the flight
|
||
paths or warps; no CI or physical-device run. None of it is reachable in
|
||
normal play yet (no Storm Badge, caves or further flypoints); Teleport
|
||
works after a Pokémon Center visit for a Pokémon that knows it.
|
||
|
||
Next: Headbutt and Rock Smash encounters (board #26).
|
||
|
||
The increment was committed as `a7aceda` (`Closes #25`). Not pushed.
|
||
|
||
## Manual test cards: 2026-10-02
|
||
|
||
Asked for the checks only a person can do. The board gained an "A tester"
|
||
column (between En cours and Fait) with #46 (Fly map, flight and landing),
|
||
#47 (Teleport and the Escape Rope outdoors), #48 (touch screens, part of
|
||
#40), #49 (screen reader, related to #38) and #50 (Dig and the Escape Rope
|
||
in Dark Cave, blocked by #31). `scripts/project-board.mjs` now also reads
|
||
that column, so a commit mentioning one of those cards moves it instead of
|
||
adding it to a second column.
|
||
|
||
`tests/human-test-saves.test.ts` writes the backups the cards use, only with
|
||
`MAKE_TEST_SAVES=1` and the local ROM: `test-saves/fly-teleport.crystal-save.json`
|
||
(ignored by git, as it holds names read from the ROM). It reloads each save
|
||
in a fresh world before writing it; importing it through Savegames and using
|
||
Teleport then Fly was checked once in Chromium and WebKit.
|
||
|
||
Not made into cards, because they can be automated: emulator comparisons
|
||
of the flight paths and of the Teleport/Dig warps through the reference
|
||
harness.
|
||
|
||
## Headbutt and Rock Smash: 2026-10-02
|
||
|
||
Resumed with "continue the plan" on a clean tree after `ee55d34`, taking
|
||
board card #26.
|
||
|
||
- `src/game/scripts/treemons.ts`: `treeMonEncounter` (`TreeMonEncounter`
|
||
with `GetTreeMonSet`, `GetTreeMons`, `GetTreeScore` and `GetTreeMon`,
|
||
including the rare table after the common one), `rockMonEncounter`
|
||
(`RockMonEncounter`'s 40%) and `treeScore`, all read from the ROM's
|
||
`TreeMonMaps`, `RockMonMaps` and `TreeMons`.
|
||
- Interpreter: `callasm` runs `TreeMonEncounter` (facing tile in
|
||
`wPlayerMapX/Y` coordinates, +4), `RockMonEncounter`, `HasRockSmash`
|
||
(sets `wCurPartyMon`) and `ShakeHeadbuttTree` (a `headbutt` effect after
|
||
the text). A `tempWild` local stands for `wTempWildMonSpecies`/
|
||
`wCurPartyLevel`: `randomwildmon` reads it, `readmem wTempWildMonSpecies`
|
||
answers from it, and the battle clears it with the tree battle type, as
|
||
`CleanUpBattleRAM` does. The `battle` effect gained `tree`.
|
||
- World: facing a tree runs `AskHeadbuttScript`; HEADBUTT and ROCK SMASH
|
||
from the party menu queue `HeadbuttFromMenuScript` and
|
||
`RockSmashFromMenuScript`, the latter with the rock as `hLastTalked`
|
||
(`MenuFieldMove.lastTalked`). Talking to a rock already ran
|
||
`SmashRockScript`. The `headbutt` effect lasts 20 ticks
|
||
(`HEADBUTT_TICKS`) and `world.headbuttTree` gives the renderer the square
|
||
and frames; objects do not move meanwhile.
|
||
- Battle: `BATTLETYPE_TREE` (`tree` option) prints
|
||
`PokemonFellFromTreeText` and puts the time of day's `AsleepTreeMons`
|
||
species to sleep for `TREEMON_SLEEP_TURNS` (`CheckSleepingTreeMon`). The
|
||
skipped cry is not presented anyway.
|
||
- Renderer: tile $05 over the tree's four tiles for 36 frames, and the
|
||
`HeadbuttTreeGFX` sprite in `PAL_OW_TREE` with the frameset's timing
|
||
(three video frames a step, frame 2 mirrored on every other pass). Checked
|
||
in Chromium with close-up screenshots across the shake.
|
||
- New profile entries (OVERWORLD, BATTLE, `RAM.tempWildMonSpecies`) and
|
||
constants (`BATTLETYPE_TREE`, `TREEMON_SLEEP_TURNS`, `NUM_TREEMON_SETS`),
|
||
all verified by `check:reference`.
|
||
|
||
Checks: 435 unit tests with the local ROM (434 passed, 1 skipped; 135
|
||
passed, 300 skipped without it; new `tests/headbutt-rock-smash.test.ts`,
|
||
and the Rock Smash test in `tests/overworld-rom.test.ts` now smashes the
|
||
rock and meets a Krabby); 123 Chromium/WebKit browser tests passed, 19
|
||
skipped intentionally (a new Headbutt flow with a "headbutt" fixture room
|
||
below a Route 29 tree); native and reference builds; reference, vendor and
|
||
release checks; `git diff --check`. No emulator comparison of the tree
|
||
scores or the shake; no CI or physical-device run. Unreachable in normal
|
||
play so far: no open map has rocks, and Headbutt (the Ilex Forest tutor)
|
||
and Rock Smash (a TM on Route 36) are not obtainable yet.
|
||
|
||
Next: the remaining field items, MAIL and pack reordering (board #27).
|