From 04657d33242f13dbbec8f66d133404e5847763ad Mon Sep 17 00:00:00 2001 From: acamilo Date: Wed, 23 Sep 2026 10:42:57 +0000 Subject: [PATCH] docs: macros.md 12.22, macros-wram.md section 12 --- docs/design/macros-wram.md | 38 ++++++++++++++++++++++++ docs/design/macros.md | 60 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/docs/design/macros-wram.md b/docs/design/macros-wram.md index 2b97299..70bd517 100644 --- a/docs/design/macros-wram.md +++ b/docs/design/macros-wram.md @@ -893,3 +893,41 @@ point of reading the figure; a border drawn somewhere the cursor is not parked i box; and a menu of more than two options is not this menu. What the pad makes of a readable prompt is `pokemon_red::macros::palette`'s business (`docs/design/macros.md` 12.12 and 12.20), not this accessor's. + +## 12. The people off the screen, and a battle decided (2026-09-23, `docs/design/macros.md` 12.22) + +Two readings row 58 added, both out of bytes the seam already had or a byte bracketed by two it had. + +### `state::offscreen_npcs` + +`CheckSpriteAvailability` (`engine/overworld/movement.asm`) writes `$ff` into +`SPRITESTATEDATA1_IMAGEINDEX` (offset 2) for a sprite that is a toggleable object switched off, that +is outside its window, or that stands on a text box's tiles. The window, for a sprite whose +`SPRITESTATEDATA2_MOVEMENTBYTE1` (offset 6) is `WALK` (`$fe`) or `STAY` (`$ff`), compares the +sprite's biased `MAPY` / `MAPX` (offsets 4 and 5) with `wYCoord` / `wXCoord`: drawn when equal, or +when the sprite's is greater by at most `SCREEN_HEIGHT / 2 - 1` (8) rows and +`SCREEN_WIDTH / 2 - 1` (9) columns. A scripted mover (movement byte below `WALK`) skips the test. + +So a `$ff` sprite of the loaded map whose coordinates fall **outside** the window, and whose +movement byte is `WALK` or above, is reported with those coordinates: the cartridge would hide it +for being off the screen whatever else were true, and a sprite it is not updating does not move. +Everything else `$ff` is not reported. Measured from the row-58 checkpoint at the Pewter Gym's +doormat: BROCK at (4, 1) and the Jr. Trainer at (3, 6) reported, the guide at (7, 10) drawn. + +What it cannot tell is a toggleable object switched off from one out of sight, because both are +`$ff` outside the window. Its one reader is the rung's own list of people; the ladder's person +places are Oak's lab and the gyms, and only the lab and Viridian Gym carry toggleable people +(`data/maps/toggleable_objects.asm`). + +### `poke::CUR_OPPONENT`, `wCurOpponent` + +Written when a battle is decided (`home/trainers.asm` for a trainer, the encounter check for a wild +one), cleared by `EndOfBattle` in the same block that clears `wIsInBattle`. Not in the generated +table: `ram/wram.asm` at the pinned commit declares `wIsInBattle:: db`, +`wPartyGainExpFlags:: flag_array PARTY_LENGTH` (one byte), `wCurOpponent:: db`, +`wBattleType:: db`, `wDamageMultipliers:: db`, `wGymLeaderNo:: db`, `wTrainerNo:: db`, and the +table's `wIsInBattle` (`$d057`), `wBattleType` (`$d05a`) and `wTrainerNo` (`$d05d`) are exactly +where that layout puts them, so the byte is `wBattleType - 1` = `$d059`, asserted against both +neighbours in `scene/tests.rs`. Measured on the cartridge in the Pewter Gym: `$00` in the +overworld, `$cd` (`OPP_JR_TRAINER_M`) from the last box of the trainer's challenge through the +219-frame transition and the battle. `controllable` reads it as zero. diff --git a/docs/design/macros.md b/docs/design/macros.md index 8cabcee..00fc5a8 100644 --- a/docs/design/macros.md +++ b/docs/design/macros.md @@ -1445,6 +1445,66 @@ Nothing presses for the fly and nothing is ranked: one button leaves a pad it co one wall moves to the tile that earned it, and one walk goes where it can. The decoder, the reward catalog, the adapter version, the roles and the compatibility string are untouched. +### 12.22 The rung's people are in the room when the screen does not show them (2026-09-23, row 58) + +Live on v0.5.3, rank 10, for twenty-five minutes: `GO OBJECTIVE` into the Pewter Gym, `GO OUT` +straight back out, with `GO ITEM`, `GO FRONTIER`, `YES` and `NO` mixed in. Per ten brain minutes +about 93 `GO OUT`, 47 `GO OBJECTIVE`, 200 starts in all, every one `done`, **no reward event of any +kind**, the exploration count frozen at 1,892. Check 10 saw ten distinct names and said nothing. +Surveyed from the live checkpoint with the route probe (`FLY_PROBE_CATCH=route`, +`FLY_PROBE_CATCH_MAP=54`), which reads the room on the fly's Nth arrival. + +- **The objective saw the room through the screen.** `objective_targets` read `npcs`, which is + what the cartridge *draws*, and `CheckSpriteAvailability` writes `$ff` into the image index of + every sprite outside a window of the player's coordinate. From the doormat at (4, 13) that window + holds the guide at (7, 10) and nobody else: BROCK at (4, 1) and the Jr. Trainer at (3, 6) are not + drawn. With the guide talked to (12.20), the rung's list was empty, so `GO OBJECTIVE` had nothing + to aim at inside and 12.5's rule -- the ways out are withheld while the rung's person is in the + room -- let `GO OUT` onto the pad. Outside, `GO OBJECTIVE` aimed at the gym's door. The pair + undoes itself in about a second, and nothing on either side of the door earns anything. +- **So the rung reads the people the cartridge hides only for being off the screen.** The window + is a function of `wYCoord`, `wXCoord` and the sprite's own biased coordinates, all already read, + so a sprite whose `$ff` falls outside it is one the cartridge would hide for that reason whatever + else were true, and a sprite the cartridge is not updating does not move + (`state::offscreen_npcs`). A `$ff` *inside* the window, or on a scripted mover, is not the + screen's and is not reported. Only the rung reads the list: a sprite outside the window may also + be a toggleable object switched off, which reads the same, so `GO NPC`, `TALK` and objects keep + what is drawn. +- **Facing any of the rung's people is the arrival.** 12.5 left out only the one ahead, which was + enough for one target; a gym names three, and in front of BROCK `GO OBJECTIVE` still had the + trainer to walk to. A fly facing a person the rung is waiting on has nothing left for a walk to + do, and `TALK` is the press. + +Three frames the seam read as the fly's own were the cartridge's, and each wrote a ledger entry that +emptied the room again once the first fix let the fly into it: + +- **A warp's tear.** `wCurMap` changes thirty-two frames before the header, the coordinates and the + warp table follow it, while the screen fades, and no joypad bit is set until the fade is over. + The seam read "map 54 at (16, 17)" -- Pewter City's doormat under the gym's id -- as an overworld + and dealt it a pad; a walk started there planned over the wrong map, and what it aimed at went + into the blocked ledger under the gym's id (live: `GO OUT` started and finished in 0.05 s). The + driver now reads a tear as the map byte having changed while the fly still stands on a warp of + the loaded table that leads to the map the byte names, deals it as `Unknown` with an empty pad, + and records no ground from it. Teleport pads -- Saffron Gym, two Silph Co. floors -- do not change + the map byte, so they are never a tear; a tear is bounded at ninety frames all the same. +- **A battle's transition.** Between a trainer's challenge closing and the battle screen there are + 219 frames with every joypad and script bit clear. The pad was dealt, a walk toward BROCK pressed + into the animation and gave up after three refused steps -- BROCK blocked for ten brain minutes -- + and the Jr. Trainer's conversation read as over, so the trainer the fly then lost to was + "talked to" for the session. `wCurOpponent` is set when a battle is decided and cleared by + `EndOfBattle` with `wIsInBattle`; it is not in the generated table and is derived as the byte + between two that are, both neighbours checked in a test (`macros-wram.md` section 12), and + `controllable` reads it. +- **A trainer walking up.** 12.4 reads a macro the cartridge ended by taking the joypad as a + refusal and wrote the target blocked and the tile pushed at once. A trainer who sees the fly + takes the joypad the same way. The entries now wait until the cartridge gives the joypad back: + in the overworld it was a refusal and is written as before; a battle teaches the ledgers nothing. + +Nothing is ranked, nothing presses for the fly, and no button is added to any pad: `GO OBJECTIVE` +has a person to walk to where it had none, `GO OUT` is withheld by 12.5's own rule, and three +frames that were never the fly's deal nothing. The decoder, the reward catalog, the adapter +version, the roles and the compatibility string are untouched. + ## 13. Shops and Pokémon Centers (the operator, 2026-09-17: "refactor the shop macros. make it a ## priority to visit the shop at least once per area; make shop macros item purchases. same ## for the Pokécenter. heal should be a macro.")