# Section 29 - Routes 22-28 Source: `../section-29-routes-22-28.txt` (the FAQ numbers this chapter "35 > Routes 22-28") Maps covered: `MAP_PALLET_TOWN`, `MAP_OAKS_LAB`, `MAP_VIRIDIAN_CITY`, `MAP_ROUTE_22`, `MAP_VICTORY_ROAD_GATE`, `MAP_ROUTE_28`, `MAP_ROUTE_28_STEEL_WING_HOUSE`, and the east edge of `MAP_SILVER_CAVE_OUTSIDE` (+ `MAP_SILVER_CAVE_POKECENTER_1F`). Badges / key milestones in this section: no badge. The milestone is `EVENT_OPENED_MT_SILVER`, set by Prof. Oak once `VAR_BADGES == NUM_BADGES` (16). Items: TM42 Dream Eater, TM47 Steel Wing, hidden Rare Candy. Two structural facts the walkthrough hides, both verified against the block data and both load-bearing for a bot: 1. **Oak's line is not flavour - it is the gate.** `EVENT_OPENED_MT_SILVER` *removes* the black belt standing at `VICTORY_ROAD_GATE` (7,5). That NPC occupies the only tile joining the gate's central corridor to its western vestibule, i.e. to the two `ROUTE_28` warps. Until Oak sets the flag, Route 28 and Mt. Silver are physically unreachable. See section 3. 2. **`object_event`'s trailing `EVENT_*` HIDES the object when the flag is SET.** `CheckObjectFlag` (`engine/overworld/map_objects_2.asm:32-61`) jumps to `.masked` when `EventFlagAction`/`CHECK_FLAG` returns non-zero. The macro comment in `macros/scripts/maps.asm:132` ("event flag: an `EVENT_*` constant, or -1 to always appear") does not state the polarity and is easy to read backwards. The port already has this right (`src/world/gen2/Events.lua:40-44`). Coordinates below are walk cells (the raw numbers in the `warp_event` / `bg_event` / `object_event` rows), origin top-left, x east, y south. --- ## 1. Route order | # | Map constant | asm file | Enter via | Leaves via | Why (walkthrough beat) | |---|---|---|---|---|---| | 0 | `MAP_PALLET_TOWN` | `maps/PalletTown.asm` | Fly, `SPAWN_PALLET` = (5,6) (`data/maps/spawn_points.asm:14`) | warp 3 at (12,11) -> `OAKS_LAB` warp 1 | "fly back to Pallet Town" | | 1 | `MAP_OAKS_LAB` | `maps/OaksLab.asm` | warp 1 at (4,11) (warp 2 at (5,11) is the twin tile) | same warps back to `PALLET_TOWN` warp 3 | talk to `Oak` at (4,2); with 16 badges this runs `.OpenMtSilver` and sets `EVENT_OPENED_MT_SILVER` | | 2 | `MAP_VIRIDIAN_CITY` | `maps/ViridianCity.asm` | Fly, `SPAWN_VIRIDIAN` = (23,26) (`data/maps/spawn_points.asm:15`), which is the Pokecenter warp tile | west map edge, cells y=14..17 at x=0 | "fly back to Viridian City, and then head west to Route 22" | | 2a | `MAP_VIRIDIAN_CITY` (Cut pocket) | same | Cut the tree at (8,22), approached from (9,22) facing LEFT | the same tile back east | the TM42 Dream Eater fisher at (6,23). The FAQ files this under "Items on Route 22"; it is **not** on Route 22 | | 3 | `MAP_ROUTE_22` | `maps/Route22.asm` | east map edge at x=39, y=6..9 (`connection east, ViridianCity, VIRIDIAN_CITY, -4`) | warp 1 at (13,5) -> `VICTORY_ROAD_GATE` warp 1 | "keep heading north, west, then north" | | 4 | `MAP_VICTORY_ROAD_GATE` | `maps/VictoryRoadGate.asm` | warp 1 at (17,7) (warp 2 at (18,7) is the twin) | warp 7 at (1,7) / warp 8 at (2,7) -> `ROUTE_28` warp 2 | "heading west instead of north" | | 5 | `MAP_ROUTE_28` (main body) | `maps/Route28.asm` | warp 2 at (33,5) | west map edge at x=0, y=12..13 (`connection west, SilverCaveOutside, SILVER_CAVE_OUTSIDE, -9`) | "head west towards Mount Silver"; the Route 28 sign is read from (30,5) | | 6 | `MAP_SILVER_CAVE_OUTSIDE` | `maps/SilverCaveOutside.asm` | east map edge at x=39, y=30..31 | warp 1 at (23,19) -> `SILVER_CAVE_POKECENTER_1F` warp 1 | "There's also a Pokémon Center here". Sets `ENGINE_FLYPOINT_SILVER_CAVE` on entry | | 7 | `MAP_SILVER_CAVE_POKECENTER_1F` | `maps/SilverCavePokecenter1F.asm` | warp 1 at (3,7) (twin at (4,7)) | same | heal; the last Pokecenter before Mt. Silver | | 8 | `MAP_SILVER_CAVE_OUTSIDE` (east shelf) | same | Cut (31,24) then Cut (34,23) | east map edge at x=39, y=21 | "cut the trees east" | | 9 | `MAP_ROUTE_28` (isolated west strip) | `maps/Route28.asm` | west map edge at x=0, y=3 | warp 1 at (7,3) -> `ROUTE_28_STEEL_WING_HOUSE` warp 1 | the strip that holds the Steel Wing house door and the hidden Rare Candy | | 10 | `MAP_ROUTE_28_STEEL_WING_HOUSE` | `maps/Route28SteelWingHouse.asm` | warp 1 at (2,7) (twin at (3,7)) | same | `Celebrity` at (2,3) gives TM47 Steel Wing | | 11 | `MAP_SILVER_CAVE_OUTSIDE` warp 2 at (18,11) -> `SILVER_CAVE_ROOM_1` | `maps/SilverCaveRoom1.asm` | - | - | **next section** (Mt. Silver / Red). Stop here. | The FAQ's chapter title says "Routes 22-28" but the prose never enters Route 23, 24, 25, 26 or 27. Routes 26/27 are section 16's, Route 23 is section 17/18's. ## 2. Maps ### MAP_PALLET_TOWN - Script: `maps/PalletTown.asm` (included from `data/maps/scripts.asm:171`) - Blocks: `maps/PalletTown.blk` (`data/maps/blocks.asm:88`) - Header: `data/maps/maps.asm:312` `map PalletTown, TILESET_KANTO, TOWN, LANDMARK_PALLET_TOWN, MUSIC_PALLET_TOWN, FALSE, PALETTE_AUTO, FISHGROUP_OCEAN` - Attributes: `data/maps/attributes.asm:271` `map_attributes PalletTown, PALLET_TOWN, $0f` - Dimensions: `constants/map_constants.asm:288` `map_const PALLET_TOWN, 10, 9` -> 10x9 blocks = 20x18 cells. Group `PALLET` (13), map id 2. - Connections: `connection north, Route1, ROUTE_1, 0`; `connection south, Route21, ROUTE_21, 0` - Callbacks: `callback MAPCALLBACK_NEWMAP, PalletTownFlypointCallback` (sym `4e:46ac`) -> `setflag ENGINE_FLYPOINT_PALLET` **Warps** (`def_warp_events`, `maps/PalletTown.asm:73-76`) | idx | x | y | destination map | dest warp | |---|---|---|---|---| | 1 | 5 | 5 | `REDS_HOUSE_1F` | 1 | | 2 | 13 | 5 | `BLUES_HOUSE` | 1 | | 3 | 12 | 11 | `OAKS_LAB` | 1 | **Coord events** - `def_coord_events` is empty. **BG events** (`def_bg_events`, `maps/PalletTown.asm:80-84`) | x | y | type | script/item | |---|---|---|---| | 7 | 9 | `BGEVENT_READ` | `PalletTownSign` (sym `4e:46b6`) | | 3 | 5 | `BGEVENT_READ` | `RedsHouseSign` | | 13 | 13 | `BGEVENT_READ` | `OaksLabSign` (sym `4e:46bc`) | | 11 | 5 | `BGEVENT_READ` | `BluesHouseSign` | **Object events** (`def_object_events`, `maps/PalletTown.asm:86-88`) | const | sprite | x | y | movement | type | script label | event flag | |---|---|---|---|---|---|---|---| | `PALLETTOWN_TEACHER` | `SPRITE_TEACHER` | 3 | 8 | `SPRITEMOVEDATA_WANDER` (r 2,2) | `OBJECTTYPE_SCRIPT` | `PalletTownTeacherScript` | -1 | | `PALLETTOWN_FISHER` | `SPRITE_FISHER` | 12 | 14 | `SPRITEMOVEDATA_WALK_LEFT_RIGHT` (r 2,0) | `OBJECTTYPE_SCRIPT` | `PalletTownFisherScript` | -1 | **Route from the Fly landing to the lab door** (BFS over `PalletTown.blk` + `data/tilesets/kanto_collision.asm`): (5,6) -> (9,6) -> (9,12) -> (12,12) -> (12,11). No ledges, no field moves. --- ### MAP_OAKS_LAB - Script: `maps/OaksLab.asm` (`data/maps/scripts.asm:341`) - Blocks: `maps/OaksLab.blk` (`data/maps/blocks.asm:892`) - Header: `data/maps/maps.asm:316` `map OaksLab, TILESET_LAB, INDOOR, LANDMARK_PALLET_TOWN, MUSIC_POKEMON_TALK, FALSE, PALETTE_DAY, FISHGROUP_SHORE` - Attributes: `data/maps/attributes.asm:579` `map_attributes OaksLab, OAKS_LAB, $00`, no connections - Dimensions: `constants/map_constants.asm:292` `map_const OAKS_LAB, 5, 6` -> 5x6 blocks = 10x12 cells. Group `PALLET` (13), map id 6. - Callbacks / scene scripts: both empty (`OaksLabNoopScene` is marked unreferenced). **Warps** (`maps/OaksLab.asm:260-262`) | idx | x | y | destination map | dest warp | |---|---|---|---|---| | 1 | 4 | 11 | `PALLET_TOWN` | 3 | | 2 | 5 | 11 | `PALLET_TOWN` | 3 | **Coord events** - none. **BG events** (`maps/OaksLab.asm:266-282`) | x | y | type | script/item | |---|---|---|---| | 6,7,8,9 | 1 | `BGEVENT_READ` | `OaksLabBookshelf` -> `jumpstd DifficultBookshelfScript` | | 0,1,2,3 | 7 | `BGEVENT_READ` | `OaksLabBookshelf` | | 6,7,8,9 | 7 | `BGEVENT_READ` | `OaksLabBookshelf` | | 4 | 0 | `BGEVENT_READ` | `OaksLabPoster1` | | 5 | 0 | `BGEVENT_READ` | `OaksLabPoster2` | | 9 | 3 | `BGEVENT_READ` | `OaksLabTrashcan` | | 0 | 1 | `BGEVENT_READ` | `OaksLabPC` | **Object events** (`maps/OaksLab.asm:284-288`) | const | sprite | x | y | movement | type | script label | event flag | |---|---|---|---|---|---|---|---| | `OAKSLAB_OAK` | `SPRITE_OAK` | 4 | 2 | `SPRITEMOVEDATA_STANDING_DOWN` | `OBJECTTYPE_SCRIPT` | `Oak` (sym `59:58c3`) | -1 | | `OAKSLAB_SCIENTIST1` | `SPRITE_SCIENTIST` | 1 | 8 | `WALK_LEFT_RIGHT` (r 1,0), `PAL_NPC_BLUE` | `OBJECTTYPE_SCRIPT` | `OaksAssistant1Script` (sym `59:590a`) | -1 | | `OAKSLAB_SCIENTIST2` | `SPRITE_SCIENTIST` | 8 | 9 | `WALK_UP_DOWN` (r 0,1), `PAL_NPC_BLUE` | `OBJECTTYPE_SCRIPT` | `OaksAssistant2Script` | -1 | | `OAKSLAB_SCIENTIST3` | `SPRITE_SCIENTIST` | 1 | 4 | `WANDER` (r 1,1), `PAL_NPC_BLUE` | `OBJECTTYPE_SCRIPT` | `OaksAssistant3Script` | -1 | Oak stands at (4,2); the player talks to him from (4,3) facing UP. Cell (4,3) is `COLL_FLOOR` in `data/tilesets/lab_collision.asm`, and (4,11)/(5,11) are the `WARP_CARPET_DOWN` pair, so the approach is a straight walk up the middle of the room. **Scripts of interest** `Oak` (`maps/OaksLab.asm:15-54`, sym `59:58c3`) - the only script in this section that writes a story flag: ``` faceplayer / opentext checkevent EVENT_OPENED_MT_SILVER ; already done -> .CheckPokedex iftrue .CheckPokedex checkevent EVENT_TALKED_TO_OAK_IN_KANTO iftrue .CheckBadges writetext OakWelcomeKantoText / promptbutton setevent EVENT_TALKED_TO_OAK_IN_KANTO .CheckBadges: readvar VAR_BADGES ifequal NUM_BADGES, .OpenMtSilver ; 16 ifequal NUM_JOHTO_BADGES, .Complain ; 8 -> "you're not collecting KANTO BADGES" sjump .AhGood ; 9..15 -> "come see me when you get them all" .OpenMtSilver: writetext OakOpenMtSilverText / promptbutton setevent EVENT_OPENED_MT_SILVER sjump .CheckPokedex .CheckPokedex: writetext OakLabDexCheckText / waitbutton special ProfOaksPCBoot writetext OakLabGoodbyeText / waitbutton / closetext / end ``` Bot-relevant: the branch is `ifequal`, not `ifgreater`, and `VAR_BADGES` is the **popcount of both badge bytes** (`engine/overworld/variables.asm` -> `CountSetBits` over `wJohtoBadges` + `wKantoBadges`; the port mirrors this at `src/world/gen2/World.lua:1240-1245`). So the flag is only set on a visit with exactly 16 badges; 9-15 badges gives `.AhGood` and no flag. Every arm falls through to `special ProfOaksPCBoot`, which prints the Pokedex rating - budget several textbox advances for it. **Flags and events** | constant | defined in | read/written by | meaning for a bot | |---|---|---|---| | `EVENT_OPENED_MT_SILVER` | `constants/event_flags.asm:1265` | set by `Oak`; read by `Oak` and by the `VICTORYROADGATE_BLACK_BELT1` object row | **the section's gate.** While clear, the black belt at `VICTORY_ROAD_GATE` (7,5) exists and blocks the ROUTE_28 warps | | `EVENT_TALKED_TO_OAK_IN_KANTO` | `constants/event_flags.asm:224` | set/read by `Oak` | first-visit greeting only, no gameplay effect | --- ### MAP_VIRIDIAN_CITY - Script: `maps/ViridianCity.asm` (`data/maps/scripts.asm:168`) - Blocks: `maps/ViridianCity.blk` (`data/maps/blocks.asm:235`) - Header: `data/maps/maps.asm:457` `map ViridianCity, TILESET_KANTO, TOWN, LANDMARK_VIRIDIAN_CITY, MUSIC_VIRIDIAN_CITY, FALSE, PALETTE_AUTO, FISHGROUP_POND` - Attributes: `data/maps/attributes.asm:259` `map_attributes ViridianCity, VIRIDIAN_CITY, $0f` - Dimensions: `constants/map_constants.asm:423` `map_const VIRIDIAN_CITY, 20, 18` -> 20x18 blocks = 40x36 cells. Group `VIRIDIAN` (23), map id 3. - Connections: `connection north, Route2, ROUTE_2, 5`; `connection south, Route1, ROUTE_1, 10`; `connection west, Route22, ROUTE_22, 4` - Callbacks: `callback MAPCALLBACK_NEWMAP, ViridianCityFlypointCallback` (sym `4e:4005`) -> `setflag ENGINE_FLYPOINT_VIRIDIAN` **Warps** (`maps/ViridianCity.asm:219-224`) | idx | x | y | destination map | dest warp | |---|---|---|---|---| | 1 | 32 | 7 | `VIRIDIAN_GYM` | 1 | | 2 | 21 | 9 | `VIRIDIAN_NICKNAME_SPEECH_HOUSE` | 1 | | 3 | 23 | 15 | `TRAINER_HOUSE_1F` | 1 | | 4 | 29 | 19 | `VIRIDIAN_MART` | 2 | | 5 | 23 | 25 | `VIRIDIAN_POKECENTER_1F` | 1 | **Coord events** - `def_coord_events` is empty. **BG events** (`maps/ViridianCity.asm:228-234`) | x | y | type | script/item | |---|---|---|---| | 17 | 17 | `BGEVENT_READ` | `ViridianCitySign` | | 27 | 7 | `BGEVENT_READ` | `ViridianGymSign` | | 19 | 1 | `BGEVENT_READ` | `ViridianCityWelcomeSign` | | 21 | 15 | `BGEVENT_READ` | `TrainerHouseSign` | | 24 | 25 | `BGEVENT_READ` | `ViridianCityPokecenterSign` -> `jumpstd PokecenterSignScript` | | 30 | 19 | `BGEVENT_READ` | `ViridianCityMartSign` -> `jumpstd MartSignScript` | **Object events** (`maps/ViridianCity.asm:236-240`) | const | sprite | x | y | movement | type | script label | event flag | |---|---|---|---|---|---|---|---| | `VIRIDIANCITY_GRAMPS1` | `SPRITE_GRAMPS` | 18 | 5 | `WANDER` (r 2,2) | `OBJECTTYPE_SCRIPT` | `ViridianCityCoffeeGramps` (sym `4e:4009`) | -1 | | `VIRIDIANCITY_GRAMPS2` | `SPRITE_GRAMPS` | 30 | 8 | `STANDING_DOWN`, `PAL_NPC_BLUE` | `OBJECTTYPE_SCRIPT` | `ViridianCityGrampsNearGym` (sym `4e:401e`) | -1 | | `VIRIDIANCITY_FISHER` | `SPRITE_FISHER` | 6 | 23 | `STANDING_DOWN`, `PAL_NPC_RED` | `OBJECTTYPE_SCRIPT` | `ViridianCityDreamEaterFisher` (sym `4e:4032`) | -1 | | `VIRIDIANCITY_YOUNGSTER` | `SPRITE_YOUNGSTER` | 17 | 21 | `WANDER` (r 3,3), `PAL_NPC_GREEN` | `OBJECTTYPE_SCRIPT` | `ViridianCityYoungsterScript` (sym `4e:404d`) | -1 | **Scripts of interest** `ViridianCityDreamEaterFisher` (`maps/ViridianCity.asm:50-65`, sym `4e:4032`): ``` faceplayer / opentext checkevent EVENT_GOT_TM42_DREAM_EATER iftrue .GotDreamEater writetext ViridianCityDreamEaterFisherText / promptbutton verbosegiveitem TM_DREAM_EATER iffalse .NoRoomForDreamEater ; bag full -> flag NOT set, retryable setevent EVENT_GOT_TM42_DREAM_EATER .GotDreamEater: writetext ...GotDreamEaterText / waitbutton .NoRoomForDreamEater: closetext / end ``` `TM_DREAM_EATER` is item `$ea` (`constants/item_constants.asm:263`, the 42nd `add_tm` after `DEF TM01 EQU const_value` at line 219 - the two bare `const` rows `ITEM_C3` and `ITEM_DC` do not consume TM numbers, which is why TM42 is Dream Eater and TM47 is Steel Wing). **Cut gate (verified from block data)** `ViridianCity.blk` block (4,11) is `$34`, whose `tilecoll` row is `CUT_TREE, FLOOR, WALL, WALL` (`data/tilesets/kanto_collision.asm`, block `34`), so the `COLL_CUT_TREE` quadrant is the top-left = **cell (8,22)**. A BFS over the collision grid says the fisher's pocket (x=4..7, y=22..26) has exactly one non-ledge entrance and it is that tile. Route: - Pokecenter door (23,25) -> step down to (23,26) - (23,26) -> (14,26) -> (14,23) -> (10,23) -> (10,22) -> (9,22) - face LEFT at (9,22), Cut (8,22) - (8,22) -> (7,22) -> (6,22) -> (6,23) faces the fisher at... he *is* at (6,23); stand at (6,22) and face DOWN, or (7,23) and face LEFT. The pocket's south edge (x=4..7, y=26) is `COLL_HOP_DOWN`, i.e. one-way out. The only other `$34`/`$60`/`$32`/`$33`/`$35` block on this map is (7,2) = cell (14,4), which is not on any route in this section. **Items** | item | how obtained | source (label / bg_event / hidden) | one-time flag | |---|---|---|---| | TM42 Dream Eater | talk to the fisher at (6,23) after Cutting (8,22) | `ViridianCityDreamEaterFisher` | `EVENT_GOT_TM42_DREAM_EATER` (`constants/event_flags.asm:223`) | **Trainers** - none on this map. **Wild encounters** - `FISHGROUP_POND` per the header; no `def_grass_wildmons VIRIDIAN_CITY` row is needed by this section. --- ### MAP_ROUTE_22 - Script: `maps/Route22.asm` (`data/maps/scripts.asm:169`) - 28 lines, one sign and one warp, nothing else - Blocks: `maps/Route22.blk` (`data/maps/blocks.asm:310`, 180 bytes) - Header: `data/maps/maps.asm:456` `map Route22, TILESET_KANTO, ROUTE, LANDMARK_ROUTE_22, MUSIC_ROUTE_3, FALSE, PALETTE_AUTO, FISHGROUP_POND` - Attributes: `data/maps/attributes.asm:264` `map_attributes Route22, ROUTE_22, $2c` - Dimensions: `constants/map_constants.asm:422` `map_const ROUTE_22, 20, 9` -> 20x9 blocks = 40x18 cells. Group `VIRIDIAN` (23), map id 2. - Connections: `connection east, ViridianCity, VIRIDIAN_CITY, -4` (only one). Crossing east from `(39, y)` lands on Viridian City `(0, y + 8)`; crossing west from Viridian `(0, y)` lands on Route 22 `(39, y - 8)`. - Scene scripts / callbacks: both empty. **Warps** (`maps/Route22.asm:19-20`) | idx | x | y | destination map | dest warp | |---|---|---|---|---| | 1 | 13 | 5 | `VICTORY_ROAD_GATE` | 1 | **Coord events** - none. **BG events** (`maps/Route22.asm:24-25`) | x | y | type | script/item | |---|---|---|---| | 15 | 7 | `BGEVENT_READ` | `VictoryRoadEntranceSign` (sym `4e:44f9`) - read from (14,7) facing RIGHT | **Object events** - `def_object_events` is empty. **There are no trainers and no item balls on Route 22**, which matches the FAQ. The FAQ's "Items on Route 22: TM42 Dream Eater" is wrong; that TM is a Viridian City NPC gift (above). **Traversal (verified by BFS over `Route22.blk` + `kanto_collision.asm`)** The map is a set of shelves joined by one-way ledges. Entering from Viridian at `(39, 6..9)`, the *only* path to the gate warp is: ``` (39,7) -> (35,7) -> (35,11) (35,12) is COLL_HOP_DOWN -> hop to (35,14) [ledge, one-way] (35,14) -> (33,14) -> (33,13) -> (33,12) [the one non-ledge gap] (33,12) -> (33,11) grass -> (31,8) -> (31,7) -> (31,5) [the y=4..5 plaza] (31,5) -> (18,5) (18,6) is COLL_HOP_DOWN -> hop to (18,8) [ledge, one-way] (18,8) grass -> (18,12) -> (14,12) -> (14,8) -> (13,7) -> (13,6) -> (13,5) warp 1 ``` Ledge semantics: a `COLL_HOP_*` tile is `LAND_TILE` and can be *walked onto*; pressing the matching direction while standing on it jumps **two** cells, over the wall beneath. There is no ledge-free route: `(18,7)` and `(35,13)` are `COLL_WALL`. Reachable tall grass in that component: `(18..21, 8..11)` and `(30..33, 8..11)`. Surfable water adjacent to it: `(22..25, 6)` from the plaza row y=5, and `(22..25, 9)` from the (22..25,10..11) shelf. **Wild encounters** - `data/wild/kanto_grass.asm:1033`, `def_grass_wildmons ROUTE_22`, encounter rate `10 percent` in all three time slots. Slot probabilities are 30/30/20/10/5/4/1 (`data/wild/probabilities.asm:6-15`). | slot | morn | day | nite | |---|---|---|---| | 1 (30%) | L3 RATTATA | L3 RATTATA | L3 RATTATA | | 2 (30%) | L3 SPEAROW | L3 SPEAROW | L3 RATTATA | | 3 (20%) | L5 SPEAROW | L5 SPEAROW | L5 RATTATA | | 4 (10%) | L4 DODUO | L4 DODUO | L4 RATTATA | | 5 (5%) | L6 PONYTA | L6 PONYTA | L6 PONYTA | | 6 (4%) | L7 FEAROW | L7 FEAROW | L7 RATTATA | | 7 (1%) | L7 FEAROW | L7 FEAROW | L7 RATTATA | Water (`data/wild/kanto_water.asm:68`, `def_water_wildmons ROUTE_22`, rate `2 percent`, slot probabilities 60/30/10): L10 POLIWAG, L5 POLIWAG, L10 POLIWHIRL. The FAQ's "#060 Poliwag (surfing)" is right; it omits Doduo, Ponyta and Fearow from the grass list. Fishing: `FISHGROUP_POND` (`data/wild/fish.asm:72-85`) - Old rod MAGIKARP/POLIWAG, Good rod MAGIKARP/POLIWAG + a time group, Super rod POLIWAG/MAGIKARP + a time group. Headbutt: Route 22 has **no** row in `data/wild/treemon_maps.asm`. --- ### MAP_VICTORY_ROAD_GATE - Script: `maps/VictoryRoadGate.asm` (`data/maps/scripts.asm:452`) - Blocks: `maps/VictoryRoadGate.blk` (`data/maps/blocks.asm:943`, 90 bytes) - Header: `data/maps/maps.asm:467` `map VictoryRoadGate, TILESET_GATE, GATE, LANDMARK_ROUTE_26, MUSIC_INDIGO_PLATEAU, FALSE, PALETTE_DAY, FISHGROUP_SHORE` (note the landmark is `LANDMARK_ROUTE_26`, not Route 22 or 28 - relevant if a bot keys anything off the Pokegear location readout) - Attributes: `data/maps/attributes.asm:666` `map_attributes VictoryRoadGate, VICTORY_ROAD_GATE, $00`, no connections - Dimensions: `constants/map_constants.asm:433` `map_const VICTORY_ROAD_GATE, 10, 9` -> 10x9 blocks = 20x18 cells. Group `VIRIDIAN` (23), map id 13. - Scene variable: `data/maps/scenes.asm:16` `scene_var VICTORY_ROAD_GATE, wVictoryRoadGateSceneID` (sym `01:d6bf`) - Scene scripts (`maps/VictoryRoadGate.asm:7-9`): `SCENE_VICTORYROADGATE_BADGE_CHECK` = 0 -> `VictoryRoadGateNoop1Scene` (bare `end`); `SCENE_VICTORYROADGATE_NOOP` = 1 -> `VictoryRoadGateNoop2Scene` (bare `end`). Both constants are minted by the `scene_script` macro's `scene_const` (`macros/scripts/maps.asm:12-33`), which is why grepping `constants/` for them finds nothing. **Warps** (`maps/VictoryRoadGate.asm:101-109`) | idx | x | y | destination map | dest warp | |---|---|---|---|---| | 1 | 17 | 7 | `ROUTE_22` | 1 | | 2 | 18 | 7 | `ROUTE_22` | 1 | | 3 | 9 | 17 | `ROUTE_26` | 1 | | 4 | 10 | 17 | `ROUTE_26` | 1 | | 5 | 9 | 0 | `VICTORY_ROAD` | 1 | | 6 | 10 | 0 | `VICTORY_ROAD` | 1 | | 7 | 1 | 7 | `ROUTE_28` | 2 | | 8 | 2 | 7 | `ROUTE_28` | 2 | **Coord events** (`maps/VictoryRoadGate.asm:111-112`) | scene | x | y | script label | effect | |---|---|---|---|---| | `SCENE_VICTORYROADGATE_BADGE_CHECK` (0) | 10 | 11 | `VictoryRoadGateBadgeCheckScript` (sym `5f:4fdc`) | `turnobject PLAYER, LEFT` then the officer's speech; `< 8` Johto badges -> `applymovement PLAYER, VictoryRoadGateStepDownMovement` (one step DOWN); 8 badges -> `setscene SCENE_VICTORYROADGATE_NOOP` and the trip-wire never fires again | A bot arriving from Route 22 (warp 1/2) never crosses (10,11): the east-west hall is at y=5 and the badge check sits in the southern stub from Route 26. The check is therefore *not* a gate on this section's route. **BG events** - `def_bg_events` is empty. **Object events** (`maps/VictoryRoadGate.asm:116-119`) | const | sprite | x | y | movement | type | script label | event flag | |---|---|---|---|---|---|---|---| | `VICTORYROADGATE_OFFICER` | `SPRITE_OFFICER` | 8 | 11 | `STANDING_RIGHT` | `OBJECTTYPE_SCRIPT` | `VictoryRoadGateOfficerScript` (sym `5f:4fe2`) | -1 | | `VICTORYROADGATE_BLACK_BELT1` | `SPRITE_BLACK_BELT` | 7 | 5 | `STANDING_RIGHT` | `OBJECTTYPE_SCRIPT` | `VictoryRoadGateLeftBlackBeltScript` (sym `5f:5000`) | `EVENT_OPENED_MT_SILVER` | | `VICTORYROADGATE_BLACK_BELT2` | `SPRITE_BLACK_BELT` | 12 | 5 | `STANDING_LEFT` | `OBJECTTYPE_SCRIPT` | `VictoryRoadGateRightBlackBeltScript` (sym `5f:5003`) | `EVENT_FOUGHT_SNORLAX` | **Geometry (from `VictoryRoadGate.blk` + `data/tilesets/gate_collision.asm`)** The building is a plus: one 20-wide, **one-cell-tall** hall at y=5, a north stub (x=8..11, y=0..4) to Victory Road, a south stub (x=8..11, y=6..17) to Route 26 with the officer's counter at y=10..13, and two 4-wide vestibules at y=6..7 - west (x=0..3, the Route 28 warps) and east (x=16..19, the Route 22 warps). Cells (7,5) and (12,5) are the only tiles joining the centre to the west and east vestibules respectively, and each is exactly where a black belt stands. BFS from the Route 22 warp (17,7), toggling the two NPCs: | NPCs present | reach `ROUTE_28` warp (1,7) | reach Victory Road door (9,0) | reach badge coord (10,11) | |---|---|---|---| | neither | yes | yes | yes | | left only (`EVENT_OPENED_MT_SILVER` clear) | **no** | yes | yes | | right only (`EVENT_FOUGHT_SNORLAX` clear) | no | no | no | | both | no | no | no | **Trainers** - none. --- ### MAP_ROUTE_28 - Script: `maps/Route28.asm` (`data/maps/scripts.asm:131`) - one sign, one hidden item, no NPCs - Blocks: `maps/Route28.blk` (`data/maps/blocks.asm:15`, 180 bytes) - Header: `data/maps/maps.asm:396` `map Route28, TILESET_KANTO, ROUTE, LANDMARK_ROUTE_28, MUSIC_INDIGO_PLATEAU, FALSE, PALETTE_AUTO, FISHGROUP_POND` - Attributes: `data/maps/attributes.asm:174` `map_attributes Route28, ROUTE_28, $2c` - Dimensions: `constants/map_constants.asm:366` `map_const ROUTE_28, 20, 9` -> 20x9 blocks = 40x18 cells. Group `SILVER` (19), map id 1. - Connections: `connection west, SilverCaveOutside, SILVER_CAVE_OUTSIDE, -9`. Crossing west from `(0, y)` lands on Silver Cave Outside `(39, y + 18)`; crossing east from Silver Cave Outside `(39, y)` lands on Route 28 `(0, y - 18)`. - Scene scripts / callbacks: both empty. **Route 28 has no flypoint callback** - the Fly destination near Mt. Silver is `SILVER_CAVE_OUTSIDE`. **Warps** (`maps/Route28.asm:19-21`) | idx | x | y | destination map | dest warp | |---|---|---|---|---| | 1 | 7 | 3 | `ROUTE_28_STEEL_WING_HOUSE` | 1 | | 2 | 33 | 5 | `VICTORY_ROAD_GATE` | 7 | **Coord events** - none. **BG events** (`maps/Route28.asm:25-27`) | x | y | type | script/item | |---|---|---|---| | 31 | 5 | `BGEVENT_READ` | `Route28Sign` (sym `4a:4d4d`) - read from (30,5) facing RIGHT | | 25 | 2 | `BGEVENT_ITEM` | `Route28HiddenRareCandy` (sym `4a:4d50`) = `hiddenitem RARE_CANDY, EVENT_ROUTE_28_HIDDEN_RARE_CANDY` | `RARE_CANDY` is item `$20` (`constants/item_constants.asm:40`). The hidden-item row is three bytes (`dwb flag, item`), not a script; the port carries it on the bg event as `hiddenItem = { item, event }` (`src/world/gen2/HiddenItems.lua:7-12`). **Object events** - `def_object_events` is empty. No trainers, no item balls. **Geometry: Route 28 is two disconnected halves** BFS over `Route28.blk` from the Victory Road Gate landing (33,6): - the **main body** reaches the sign approach (30,5) and the west edge only at y=12..13 -> Silver Cave Outside (39, 30..31); - it does **not** reach (7,3), (25,3) or any of y=3..4. BFS from the west edge at (0,3) - the **isolated north-west strip** - reaches the house door (7,3) and the hidden-item approach (25,3), and then falls one-way into the main body over the `COLL_HOP_DOWN` row at y=4 (x=4..9, landing in the grass at y=6). So: - entering the strip: only from `SILVER_CAVE_OUTSIDE (39,21)` -> `ROUTE_28 (0,3)`, which itself needs two Cuts (see below); - inside the strip: (0,3) -> (5,3) -> (5,4) -> (7,4) -> north into the door (7,3); and (7,4) -> (21,4) -> (21,3) -> (25,3), then face UP for the hidden Rare Candy at (25,2); - leaving the strip: hop any of the y=4 ledges (x=4..9) down into the main body, which is one-way. Re-entering means going round through Silver Cave Outside again. Main-body route from the gate to Silver Cave Outside: (33,6) -> (28,6) -> (28,8), hop (27,8)->(27,10), west along y=10 to (18,10) -> (18,9) -> (10,9) -> (10,10) -> (8,10), hop (8,10)->(8,12), west along y=12 to (0,12). **Items** | item | how obtained | source | one-time flag | |---|---|---|---| | Rare Candy | hidden, face (25,2) from (25,3) and press A, or use the Itemfinder | `Route28HiddenRareCandy` bg_event | `EVENT_ROUTE_28_HIDDEN_RARE_CANDY` (`constants/event_flags.asm:173`) | The FAQ lists "Items: TM47 Steel Wing" for Route 28 and never mentions the Rare Candy. TM47 is in the house, not on the route. **Wild encounters** - `data/wild/kanto_grass.asm:1227`, `def_grass_wildmons ROUTE_28`, rate `10 percent` all day. The table is `IF DEF(_GOLD) / ELIF DEF(_SILVER)`: | slot | Gold morn/day | Gold nite | Silver morn/day | Silver nite | |---|---|---|---|---| | 1 (30%) | L39 TANGELA | L39 TANGELA | L39 TANGELA | L39 TANGELA | | 2 (30%) | L40 PONYTA | L40 PONYTA | L40 PONYTA | L40 PONYTA | | 3 (20%) | L40 URSARING | L40 URSARING | L40 DONPHAN | L40 DONPHAN | | 4 (10%) | L42 RAPIDASH | L40 SNEASEL | L42 RAPIDASH | L40 SNEASEL | | 5 (5%) | L41 DODUO | L42 RAPIDASH | L41 DODUO | L42 RAPIDASH | | 6 (4%) | L43 DODRIO | L42 RAPIDASH | L43 DODRIO | L42 RAPIDASH | | 7 (1%) | L43 DODRIO | L42 RAPIDASH | L43 DODRIO | L42 RAPIDASH | So on **Gold** the FAQ's Donphan does not appear (Ursaring takes that slot), and **Sneasel is nite-only**. Tangela is the 30% lead slot at every time of day, which makes the FAQ's "consider catching a Tangela" cheap to satisfy. Water (`data/wild/kanto_water.asm:110`, rate `2 percent`): L40 POLIWAG, L35 POLIWAG, L40 POLIWHIRL. Fishing: `FISHGROUP_POND`, same table as Route 22. Headbutt: `data/wild/treemon_maps.asm:9` `treemon_map ROUTE_28, TREEMON_SET_NONE` - headbutting a Route 28 tree yields nothing. --- ### MAP_ROUTE_28_STEEL_WING_HOUSE - Script: `maps/Route28SteelWingHouse.asm` (`data/maps/scripts.asm:388`) - Blocks: `data/maps/blocks.asm:181` (`Route28SteelWingHouse_Blocks`) - Header: `data/maps/maps.asm:399` `map Route28SteelWingHouse, TILESET_HOUSE, INDOOR, LANDMARK_ROUTE_28, MUSIC_AZALEA_TOWN, FALSE, PALETTE_DAY, FISHGROUP_SHORE` - Attributes: `data/maps/attributes.asm:624`, no connections - Dimensions: `constants/map_constants.asm:369` `map_const ROUTE_28_STEEL_WING_HOUSE, 4, 4` -> 8x8 cells. Group `SILVER` (19), map id 4. - Scene scripts: one `scene_script Route28SteelWingHouseNoopScene` marked "unusable" in the source; body is a bare `end`. **Warps** (`maps/Route28SteelWingHouse.asm:74-76`) | idx | x | y | destination map | dest warp | |---|---|---|---|---| | 1 | 2 | 7 | `ROUTE_28` | 1 | | 2 | 3 | 7 | `ROUTE_28` | 1 | **Coord events** - none. **BG events** (`maps/Route28SteelWingHouse.asm:80-82`) | x | y | type | script/item | |---|---|---|---| | 0 | 1 | `BGEVENT_READ` | `CelebrityHouseBookshelf` -> `jumpstd MagazineBookshelfScript` | | 1 | 1 | `BGEVENT_READ` | `CelebrityHouseBookshelf` | **Object events** (`maps/Route28SteelWingHouse.asm:84-86`) | const | sprite | x | y | movement | type | script label | event flag | |---|---|---|---|---|---|---|---| | `ROUTE28STEELWINGHOUSE_CELEBRITY` | `SPRITE_COOLTRAINER_F` | 2 | 3 | `STANDING_DOWN`, `PAL_NPC_GREEN` | `OBJECTTYPE_SCRIPT` | `Celebrity` (sym `5c:4be9`) | -1 | | `ROUTE28STEELWINGHOUSE_FEAROW` | `SPRITE_MOLTRES` | 6 | 5 | `SPRITEMOVEDATA_POKEMON`, `PAL_NPC_BROWN` | `OBJECTTYPE_SCRIPT` | `CelebritysFearow` (sym `5c:4c06`) | -1 | The pet is a Fearow but reuses `SPRITE_MOLTRES` recoloured with `PAL_NPC_BROWN`; `CelebritysFearow` does `cry FEAROW`. Worth knowing if a bot asserts on sprite ids. **Scripts of interest** `Celebrity` (`maps/Route28SteelWingHouse.asm:14-31`, sym `5c:4be9`): ``` faceplayer / opentext checkevent EVENT_GOT_TM47_STEEL_WING iftrue .AlreadyGotItem writetext CelebrityText1 / promptbutton verbosegiveitem TM_STEEL_WING iffalse .Done ; bag full -> flag NOT set, retryable setevent EVENT_GOT_TM47_STEEL_WING .Done: closetext / end ``` Note the shape differs from the Dream Eater fisher: there is no "thanks" text after a successful give, the script just closes. **Items** | item | how obtained | source | one-time flag | |---|---|---|---| | TM47 Steel Wing (`$ef`) | talk to `Celebrity` at (2,3) from (2,4) facing UP | `Celebrity` | `EVENT_GOT_TM47_STEEL_WING` (`constants/event_flags.asm:125`) | --- ### MAP_SILVER_CAVE_OUTSIDE (east edge only - the rest is the next section) - Script: `maps/SilverCaveOutside.asm` (`data/maps/scripts.asm:124`) - Blocks: `maps/SilverCaveOutside.blk` (`data/maps/blocks.asm:559`, 360 bytes) - Header: `data/maps/maps.asm:397` `map SilverCaveOutside, TILESET_KANTO, TOWN, LANDMARK_SILVER_CAVE, MUSIC_INDIGO_PLATEAU, FALSE, PALETTE_AUTO, FISHGROUP_POND` - Attributes: `data/maps/attributes.asm:164` `map_attributes SilverCaveOutside, SILVER_CAVE_OUTSIDE, $2c` - Dimensions: `constants/map_constants.asm:367` `map_const SILVER_CAVE_OUTSIDE, 20, 18` -> 40x36 cells. Group `SILVER` (19), map id 2. - Connections: `connection east, Route28, ROUTE_28, 9` - Callbacks: `callback MAPCALLBACK_NEWMAP, SilverCaveOutsideFlypointCallback` (sym `49:5f1a`) -> `setflag ENGINE_FLYPOINT_SILVER_CAVE`. Spawn `SPAWN_MT_SILVER` = (23,20) (`data/maps/spawn_points.asm:39`), i.e. the tile below the Pokecenter door - this is the "fly back to the Pokémon Center on Route 28" the FAQ means. **Warps** (`maps/SilverCaveOutside.asm:27-29`) | idx | x | y | destination map | dest warp | |---|---|---|---|---| | 1 | 23 | 19 | `SILVER_CAVE_POKECENTER_1F` | 1 | | 2 | 18 | 11 | `SILVER_CAVE_ROOM_1` | 1 | **BG events** (`maps/SilverCaveOutside.asm:33-36`) | x | y | type | script/item | |---|---|---|---| | 24 | 19 | `BGEVENT_READ` | `MtSilverPokecenterSign` (sym `49:5f1e`) | | 17 | 13 | `BGEVENT_READ` | `MtSilverSign` (sym `49:5f21`) | | 9 | 25 | `BGEVENT_ITEM` | `SilverCaveOutsideHiddenFullRestore` (sym `49:5f24`) = `hiddenitem FULL_RESTORE, EVENT_SILVER_CAVE_OUTSIDE_HIDDEN_FULL_RESTORE` (`constants/event_flags.asm:194`) - **next section's**, and not reachable from the east side without Surf | **Coord events / object events** - both empty. **The two Cut trees (verified from block data)** `data/collision/field_move_blocks.asm:24-31`, `CutTreeBlockPointers.kanto`, lists the cuttable blocks for `TILESET_KANTO`: `$0b -> $0a` (grass, animation 1) and `$32 -> $6d`, `$33 -> $6c`, `$34 -> $6f`, `$35 -> $4c`, `$60 -> $6e` (trees, animation 0). `SilverCaveOutside.blk` contains exactly two of them: | block coord | block id | `tilecoll` row | `COLL_CUT_TREE` cell | replacement block | |---|---|---|---|---| | (17,11) | `$60` | `FLOOR, WALL, CUT_TREE, WALL` | **(34,23)** | `$6e` | | (15,12) | `$35` | `FLOOR, CUT_TREE, WALL, FLOOR` | **(31,24)** | `$4c` | BFS from the Route 28 landing (39,30): - with no cuts: the Pokecenter door (23,19), the cave mouth (18,11) and the approach tile (30,24) are reachable; (39,21) is **not**; - after cutting (31,24) only: (34,24) becomes reachable, (39,21) still not; - after cutting both: (39,21) is reachable -> `ROUTE_28 (0,3)`. Route from the Pokecenter door to the house, waypoint by waypoint: ``` (23,19) door -> (23,20) -> (25,20) -> (25,22) -> (30,22) -> (30,24) face RIGHT, Cut (31,24) (31,24) -> (31,25) -> (34,25) -> (34,24) face UP, Cut (34,23) (34,23) -> (34,21) -> (39,21) -> cross east -> ROUTE_28 (0,3) (0,3) -> (5,3) -> (5,4) -> (7,4) -> face UP into warp 1 at (7,3) ``` --- ### MAP_SILVER_CAVE_POKECENTER_1F - Script: `maps/SilverCavePokecenter1F.asm` (`data/maps/scripts.asm:387`) - Header: `data/maps/maps.asm:398` `map SilverCavePokecenter1F, TILESET_POKECENTER, INDOOR, LANDMARK_SILVER_CAVE, MUSIC_POKEMON_CENTER, FALSE, PALETTE_DAY, FISHGROUP_SHORE` - Attributes: `data/maps/attributes.asm:623` - Dimensions: `constants/map_constants.asm:368` `map_const SILVER_CAVE_POKECENTER_1F, 5, 4` -> 10x8 cells. Group `SILVER` (19), map id 3. **Warps** (`maps/SilverCavePokecenter1F.asm:33-36`) | idx | x | y | destination map | dest warp | |---|---|---|---|---| | 1 | 3 | 7 | `SILVER_CAVE_OUTSIDE` | 1 | | 2 | 4 | 7 | `SILVER_CAVE_OUTSIDE` | 1 | | 3 | 0 | 7 | `POKECENTER_2F` | 1 | **Object events** (`maps/SilverCavePokecenter1F.asm:42-44`) | const | sprite | x | y | movement | type | script label | event flag | |---|---|---|---|---|---|---|---| | `SILVERCAVEPOKECENTER1F_NURSE` | `SPRITE_NURSE` | 3 | 1 | `STANDING_DOWN` | `OBJECTTYPE_SCRIPT` | `SilverCavePokecenter1FNurseScript` (sym `5c:4b2b`) -> `jumpstd PokecenterNurseScript` | -1 | | `SILVERCAVEPOKECENTER1F_GRANNY` | `SPRITE_GRANNY` | 1 | 5 | `STANDING_LEFT` (r 2,1) | `OBJECTTYPE_SCRIPT` | `SilverCavePokecenter1FGrannyScript` (sym `5c:4b2e`) | -1 | No mart clerk on this map - the FAQ's "fly to Mahogany Town's Pokemart to buy some" is correct precisely because there is nothing to buy here. --- ## 3. Blockers and gates | Gate | Enforced by (file:label) | Requirement | Unlock condition | |---|---|---|---| | Route 28 / Mt. Silver | `maps/VictoryRoadGate.asm:118` object row `VICTORYROADGATE_BLACK_BELT1` at (7,5) + `engine/overworld/map_objects_2.asm:32` `CheckObjectFlag` | `EVENT_OPENED_MT_SILVER` set, which hides the NPC and frees the only tile between the gate's centre and its west vestibule | talk to `Oak` (`maps/OaksLab.asm:15`) with `VAR_BADGES == NUM_BADGES` (16; `constants/ram_constants.asm:273`) | | Route 22 from the gate | `maps/VictoryRoadGate.asm:119` object row `VICTORYROADGATE_BLACK_BELT2` at (12,5) | `EVENT_FOUGHT_SNORLAX` (`constants/event_flags.asm:1266`) | set long before this section (Kanto). Listed because it uses the same mechanism and a bot that clears flags for testing will wall itself in | | Victory Road Gate south corridor | `maps/VictoryRoadGate.asm:112` coord event (10,11) -> `VictoryRoadGateBadgeCheckScript` | `readvar VAR_BADGES` `ifgreater NUM_JOHTO_BADGES - 1` i.e. >= 8 badges | already satisfied; and the Route 22 -> Route 28 path never touches (10,11) | | Dream Eater fisher (Viridian) | `data/collision/field_move_blocks.asm:24` `.kanto` block `$34` at Viridian block (4,11) -> `COLL_CUT_TREE` at cell (8,22) | HM01 Cut in the party **and** `ENGINE_HIVEBADGE` | `engine/events/overworld.asm:1741` `TryCutOW`: `CheckPartyMove CUT`, then `ld de, ENGINE_HIVEBADGE / call CheckEngineFlag`; failure runs `CantCutScript` | | Steel Wing house / Route 28 west strip | two `COLL_CUT_TREE` cells on `SILVER_CAVE_OUTSIDE`: (31,24) (block `$35`) and (34,23) (block `$60`) | same Cut + `ENGINE_HIVEBADGE` check | as above. Both trees are required - cutting only (31,24) does not open (39,21) | | Fly to Pallet / Viridian / Mt. Silver | `engine/events/overworld.asm:543-545` `FlyFunction.TryFly`: `ld de, ENGINE_STORMBADGE / call CheckBadge`, then `GetMapEnvironment` / `CheckOutdoorMap` | STORMBADGE + HM02 Fly + the destination's `ENGINE_FLYPOINT_*` bit | `ENGINE_FLYPOINT_PALLET` (`constants/engine_flags.asm:67`), `ENGINE_FLYPOINT_VIRIDIAN` (68), `ENGINE_FLYPOINT_SILVER_CAVE` (90), each set by that map's `MAPCALLBACK_NEWMAP` the first time you walk in | | Surf (Route 22 / Route 28 water) | `engine/events/overworld.asm:340` and `:490`, `ld de, ENGINE_FOGBADGE` | FOGBADGE + HM03 Surf | optional in this section | | Route 22 traversal | `Route22.blk` collision: `(35,12)` and `(18,6)` are `COLL_HOP_DOWN` with `COLL_WALL` beneath | ledge hopping (walk onto the ledge tile, then press the matching direction to jump two cells) | no item needed, but there is **no** ledge-free route to the gate warp | | Route 28 west strip re-entry | `Route28.blk` y=4 ledge row (x=4..9) | one-way; falling off it drops you into the main body | re-enter via `SILVER_CAVE_OUTSIDE (39,21)` | ## 4. Bot checklist Preconditions for the whole section: 16 badges, HM01 Cut + HIVEBADGE, HM02 Fly + STORMBADGE, `EVENT_FOUGHT_SNORLAX` set, `ENGINE_FLYPOINT_PALLET` and `ENGINE_FLYPOINT_VIRIDIAN` already visited. | # | Map | Target | Intent | Precondition | Postcondition | |---|---|---|---|---|---| | 1 | anywhere outdoor | `SPAWN_PALLET` | Fly | STORMBADGE, `ENGINE_FLYPOINT_PALLET` | player at `PALLET_TOWN` (5,6) | | 2 | `PALLET_TOWN` | (12,11) | walk (5,6)->(9,6)->(9,12)->(12,12)->(12,11) | - | warp to `OAKS_LAB` (4,11) | | 3 | `OAKS_LAB` | (4,3) facing UP | walk, then talk to `OAKSLAB_OAK` | `VAR_BADGES == 16` | `EVENT_OPENED_MT_SILVER` set; expect `EVENT_TALKED_TO_OAK_IN_KANTO` on the first visit and a `ProfOaksPCBoot` dex-rating textbox chain | | 4 | `OAKS_LAB` | (4,11) | walk into warp 1 | - | back on `PALLET_TOWN` (12,11) | | 5 | anywhere outdoor | `SPAWN_VIRIDIAN` | Fly | `ENGINE_FLYPOINT_VIRIDIAN` | player at `VIRIDIAN_CITY` (23,26) | | 6 (optional) | `VIRIDIAN_CITY` | (9,22) facing LEFT | walk (23,26)->(14,26)->(14,23)->(10,23)->(10,22)->(9,22), then Cut | Cut + HIVEBADGE, `EVENT_GOT_TM42_DREAM_EATER` clear | block at (8,22) replaced with `$6f` | | 7 (optional) | `VIRIDIAN_CITY` | `VIRIDIANCITY_FISHER` at (6,23), talk from (6,22) facing DOWN | talk | bag has room | TM42 Dream Eater; `EVENT_GOT_TM42_DREAM_EATER` set | | 8 | `VIRIDIAN_CITY` | west edge, x=0 at y=15 | walk (0,17)->(0,15) then step LEFT | - | map change to `ROUTE_22` (39,7) | | 9 | `ROUTE_22` | (35,12) | walk (39,7)->(35,7)->(35,11)->(35,12) | - | standing on a `COLL_HOP_DOWN` tile | | 10 | `ROUTE_22` | press DOWN | ledge hop | - | player at (35,14) | | 11 | `ROUTE_22` | (33,12) | walk (35,14)->(33,14)->(33,13)->(33,12) | - | - | | 12 | `ROUTE_22` | (31,5) | walk (33,12)->(33,11)->(31,11)->(31,8)->(31,7)->(31,6)->(31,5) - crosses the tall grass at (30..33, 8..11) | - | expect wild encounters, 10% per step in grass | | 13 | `ROUTE_22` | (18,6) | walk west along y=5 to (18,5), then DOWN onto (18,6) | - | standing on a ledge tile | | 14 | `ROUTE_22` | press DOWN | ledge hop | - | player at (18,8), in the west grass patch | | 15 | `ROUTE_22` | (13,5) | walk (18,8)->(18,12)->(14,12)->(14,8)->(13,8)->(13,7)->(13,6)->(13,5) | - | warp to `VICTORY_ROAD_GATE` (17,7) | | 16 | `VICTORY_ROAD_GATE` | (1,7) | walk (17,7)->(17,5)->west along y=5->(1,5)->(1,7) | `EVENT_OPENED_MT_SILVER` **and** `EVENT_FOUGHT_SNORLAX` set (both black belts hidden) | warp to `ROUTE_28` (33,5) | | 17 | `ROUTE_28` | (0,12) | walk (33,5)->(33,6)->(28,6)->(28,8), hop (27,8)->(27,10), west y=10 to (18,10)->(18,9)->(10,9)->(10,10)->(8,10), hop ->(8,12), west to (0,12) | - | map change to `SILVER_CAVE_OUTSIDE` (39,30) | | 18 | `SILVER_CAVE_OUTSIDE` | (23,20) | walk north-west to the Pokecenter door | - | `ENGINE_FLYPOINT_SILVER_CAVE` set by `MAPCALLBACK_NEWMAP` | | 19 | `SILVER_CAVE_OUTSIDE` | (23,19) | step UP into warp 1 | - | `SILVER_CAVE_POKECENTER_1F` (3,7) | | 20 | `SILVER_CAVE_POKECENTER_1F` | nurse at (3,1), talk from (3,2) facing UP | heal | - | party restored | | 21 | `SILVER_CAVE_POKECENTER_1F` | (3,7) | step DOWN into warp 1 | - | `SILVER_CAVE_OUTSIDE` (23,19) | | 22 | `SILVER_CAVE_OUTSIDE` | (30,24) facing RIGHT | walk (23,20)->(25,20)->(25,22)->(30,22)->(30,24), then Cut | Cut + HIVEBADGE | (31,24) replaced with `$4c` | | 23 | `SILVER_CAVE_OUTSIDE` | (34,24) facing UP | walk (31,24)->(31,25)->(34,25)->(34,24), then Cut | Cut + HIVEBADGE | (34,23) replaced with `$6e` | | 24 | `SILVER_CAVE_OUTSIDE` | (39,21) | walk (34,23)->(34,21)->(39,21), step RIGHT | - | map change to `ROUTE_28` (0,3) | | 25 | `ROUTE_28` | (25,3) facing UP | walk (0,3)->(5,3)->(5,4)->(7,4)->(21,4)->(21,3)->(25,3), press A | `EVENT_ROUTE_28_HIDDEN_RARE_CANDY` clear | Rare Candy; flag set | | 26 | `ROUTE_28` | (7,3) | walk back west along y=4 to (7,4), step UP | - | warp to `ROUTE_28_STEEL_WING_HOUSE` (2,7) | | 27 | `ROUTE_28_STEEL_WING_HOUSE` | `Celebrity` at (2,3), talk from (2,4) facing UP | talk | bag has room, `EVENT_GOT_TM47_STEEL_WING` clear | TM47 Steel Wing; flag set | | 28 | `ROUTE_28_STEEL_WING_HOUSE` | (2,7) | step DOWN into warp 1 | - | `ROUTE_28` (7,3) | | 29 | `ROUTE_28` | any y=4 ledge (x=4..9) | walk to it and press DOWN | - | one-way drop into the Route 28 main body (grass at y=6) - the strip cannot be re-entered from here | | 30 | - | shopping run | Fly to Mahogany, buy, Fly back to `SPAWN_MT_SILVER` | `ENGINE_FLYPOINT_SILVER_CAVE` set in step 18 | ready for `SILVER_CAVE_OUTSIDE` warp 2 at (18,11) - next section | ## 5. Port coverage | Beat | Port file | Status | |---|---|---| | Map headers, dimensions, warps / coord / bg / object event tables for all of these maps | `src/import/RomExtractorGen2.lua:787-862` (`warps`, `coordEvents`, `bgEvents`, `objects`), consumed by `src/world/gen2/Map.lua` | implemented, and generic - no per-map work needed | | Map connections and the offset landing maths | `src/world/gen2/Map.lua:71-92` `Map.connectionLanding` | implemented (same block-offset maths this doc's route uses) | | `object_event` event-flag masking (the black-belt gate) | `src/world/gen2/Events.lua:40-44` `Events:objectVisible`, called from `src/world/gen2/World.lua:5106` and `:5122` | implemented, with the correct "flag set -> hidden" polarity | | `Oak`'s opcode chain (`checkevent` / `readvar` / `setevent` / `special`) | `src/script/gen2/Vm.lua:187` (`checkevent`), `:670` (`readvar`), `src/world/gen2/World.lua:1240-1245` (`VAR_BADGES` = popcount of both badge bytes) | implemented | | `special ProfOaksPCBoot` (the dex rating after every Oak conversation) | `src/script/gen2/Specials.lua:1778` `H.ProfOaksPCBoot` | implemented, including the `OakRatings` table and the fanfare wait | | `verbosegiveitem` for TM42 / TM47, including the bag-full `iffalse` arm | `src/script/gen2/Vm.lua:490-498` | implemented | | `hiddenitem` (Route 28 Rare Candy) via `BGEVENT_ITEM` | `src/world/gen2/HiddenItems.lua` (whole file), wired at `src/world/gen2/World.lua:30` and `:3423` | implemented | | `jumpstd` for `PokecenterNurseScript` / `MagazineBookshelfScript` / `DifficultBookshelfScript` / `PokecenterSignScript` / `MartSignScript` | `src/script/gen2/Vm.lua:742-747` | implemented (std scripts are extracted and run through the same VM) | | Cut: badge + party-move check, `CutTreeBlockPointers` lookup, block replacement | `src/world/gen2/FieldMoves.lua:101-107` (badge map), `:183-237` (`field_move_blocks.asm` transcribed), `:454`, `:605-619` | implemented | | Fly: STORMBADGE check, flypoint table, `ENGINE_FLYPOINT_*` bits | `src/world/gen2/FieldMoves.lua:336-380` (`FLYPOINTS`, incl. `SPAWN_PALLET` 52, `SPAWN_VIRIDIAN` 53, `SPAWN_MT_SILVER` 75), `src/ui/TownMap.lua:146-208`, `src/world/gen2/World.lua:4292` | implemented | | Scene scripts / `setscene` / coord-event dispatch (Victory Road Gate badge check) | `src/world/gen2/World.lua:470` (`mapScenes`), `:5013-5033` | implemented | | `cry FEAROW` on the celebrity's pet | `src/script/gen2/Vm.lua:621` | implemented | | **Ledge hopping (`COLL_HOP_DOWN` / `HOP_LEFT` / `HOP_RIGHT` / `HOP_DOWN_LEFT` / `HOP_DOWN_RIGHT`)** | `src/world/gen2/Player.lua:50-83` `Player:tryMove` - plain single-cell step, no `CheckLedge` equivalent; `src/world/gen2/Permissions.lua` maps `$a0..$a7` to `LAND` so the tiles are walkable but never jump | **missing.** Route 22 cannot be crossed without it: `(35,12)` and `(18,6)` are ledges over `COLL_WALL`, and there is no alternative route to the gate warp. Route 28's west strip also exits only by ledge | | Route 22 / Route 28 / Silver Cave wild tables | generic (`data/wild/*` extracted by `RomExtractorGen2.lua`) | implemented as data; not separately verified for these three maps | | A driver that walks any of this | `tests/drivers/gold_*.lua` (25 drivers; none touches Kanto or Mt. Silver) | missing | ## 6. Unresolved / verify by hand - **"Items on Route 22: TM42 Dream Eater"** - the asm puts the Dream Eater NPC on `MAP_VIRIDIAN_CITY` at (6,23) (`maps/ViridianCity.asm:239`). `maps/Route22.asm` has an empty `def_object_events` and no item bg event at all. The FAQ's prose ("If you cut the tree to the west of Viridian City") is right; its item header is wrong. - **"There's also a Pokémon Center here" (Route 28)** - there is no Pokecenter on `MAP_ROUTE_28`. The nearest one is `MAP_SILVER_CAVE_POKECENTER_1F`, entered from `MAP_SILVER_CAVE_OUTSIDE` warp 1 at (23,19), one map west. - **"Items [Route 28]: TM47 Steel Wing"** - TM47 is inside `MAP_ROUTE_28_STEEL_WING_HOUSE`. Route 28 itself has one item, the hidden Rare Candy at (25,2), which the FAQ never mentions. - **"#232 Donphan (Silver only)"** - correct as far as it goes, but the FAQ omits what Gold puts in that slot (Ursaring, 20%) and omits Doduo/Dodrio entirely. It also does not say Sneasel is nite-only. - **Route 22 Pokemon list** - the FAQ lists only Spearow, Rattata and Poliwag. `data/wild/kanto_grass.asm:1033` also has Doduo (10%), Ponyta (5%) and Fearow (4%+1%) in the morn/day tables. - **The FAQ's "keep heading north, west, then north"** is a fair prose summary but omits the two mandatory ledge hops. The waypoint list in section 4 was derived by BFS over `Route22.blk` against `data/tilesets/kanto_collision.asm` with the ledge rule "step onto a `COLL_HOP_*` tile, then a matching press moves two cells". That rule was inferred from the collision permission table (`$a0..$a7` are `LAND_TILE`) and from the fact that every ledge on these maps has `COLL_WALL` directly beyond it; the exact `DoPlayerMovement` ledge branch in `engine/overworld/player_movement.asm` was **not** read line by line. Worth confirming before a driver depends on the two-cell jump distance. - **Route 28's "unreachable" rows** - `SILVER_CAVE_OUTSIDE`'s east edge is also walkable at y=14..18 and y=33..35, which map to `ROUTE_28` y=-4..0 and y=15..17. Those Route 28 rows are outside the component reachable from either the gate or the west strip, so they look like filler; not chased further. - **Mt. Silver entry itself** - `maps/SilverCaveOutside.asm` has no coord event, no callback check and no NPC guarding warp 2 at (18,11), and `EVENT_OPENED_MT_SILVER` is referenced in exactly two places (`maps/OaksLab.asm:18,43` and `maps/VictoryRoadGate.asm:118`). So the flag gates the *corridor*, not the cave mouth. If a save is hacked past the gate NPC, the cave is open. - **Party/level requirements** - the FAQ's "at least 10 Revives, 10 Hyper Potions, 5 Escape Ropes, 5 Max Repels, and 50 Ultra Balls" is advice, not a coded gate. Nothing in the asm checks the bag here.