50 KiB
Section 32 - Raikou, Entei, and Suicune
Source: ../section-32-raikou-entei-and-suicune.txt
Maps covered: MAP_ROUTE_29, MAP_ROUTE_30, MAP_ROUTE_31, MAP_ROUTE_32,
MAP_ROUTE_33, MAP_ROUTE_34, MAP_ROUTE_35, MAP_ROUTE_36, MAP_ROUTE_37,
MAP_ROUTE_38, MAP_ROUTE_39, MAP_ROUTE_42, MAP_ROUTE_43, MAP_ROUTE_44,
MAP_ROUTE_45, MAP_ROUTE_46 (the sixteen RoamMaps entries), plus
MAP_VIOLET_CITY as the Fly anchor the walkthrough names.
Badges / key milestones in this section: none. The milestone is three catches:
RAIKOU, ENTEI, SUICUNE. Every one of them clears its own roam slot
permanently.
This section is a mechanic, not a walk. There is no scripted trigger anywhere
on these sixteen maps that has anything to do with the beasts; the entire
feature lives in engine/overworld/wildmons.asm,
engine/battle/core.asm and data/wild/roammon_maps.asm. Section 2 therefore
opens with a "roam engine" block before the per-map blocks, because that block
is the part a bot actually has to implement.
1. Route order
The walkthrough gives a policy, not an itinerary: cross route boundaries as fast
as possible, because every crossing is one UpdateRoamMons call. The named
concrete loop is the Violet City / Route 36 hub.
| # | Map constant | asm file | Enter via | Leaves via | Why (walkthrough beat) |
|---|---|---|---|---|---|
| 0 | MAP_BURNED_TOWER_B1F |
maps/BurnedTowerB1F.asm |
(earlier section) | (earlier section) | special InitRoamMons at ReleaseTheBeasts is the only thing that ever creates the three roamers. Out of scope here; owned by the Burned Tower section. |
| 1 | MAP_VIOLET_CITY |
maps/VioletCity.asm |
Fly (MAPSETUP_TELEPORT) |
west connection | "fly to Violet City, then stay on a bike and keep switching between Routes 35, 36, and 37". Note the Fly itself runs JumpRoamMons, i.e. it scatters the beasts before the loop starts. |
| 2 | MAP_ROUTE_36 |
maps/Route36.asm |
west connection from Violet City | south / north connections | Route 36 is the four-way roam junction (ROUTE_35, ROUTE_31, ROUTE_32, ROUTE_37) and the only map with a direct edge to Violet City. |
| 3 | MAP_ROUTE_35 |
maps/Route35.asm |
south connection from Route 36 | north connection back | Half of the "left right left right" oscillation. Each crossing = one UpdateRoamMons. |
| 4 | MAP_ROUTE_37 |
maps/Route37.asm |
north connection from Route 36 | south connection back | Other half of the oscillation, and Entei's starting route. |
| 5 | MAP_ROUTE_42 |
maps/Route42.asm |
west connection from Ecruteak City | east connection to Mahogany Town | "go to Route 42 with the Max Repel". Raikou's starting route, the other four-way roam junction, and it carries a free ULTRA_BALL item ball. |
| - | MAP_SILVER_CAVE_OUTSIDE / MAP_SILVER_CAVE_ROOM_* |
maps/SilverCaveOutside.asm etc. |
- | - | "sharing EXP at Mount Silver" to raise the False Swipe Scyther. Mount Silver belongs to a later section; only the level ceiling (see section 3) matters here. |
Spill-over note: the walkthrough's Master Ball aside points at the Elm / Radio Tower reward, and its Scyther training points at Mount Silver. Both are other sections' maps; nothing about them is transcribed here.
2. Maps
The roam engine (not a map)
This is the part of the section that is code rather than map data. Everything below was read out of the files named.
State: roam_struct (macros/ram.asm, 7 bytes each, three of them)
| field | symbol (Raikou slot) | address | meaning |
|---|---|---|---|
| Species | wRoamMon1Species |
01:dd1a |
0 once caught or defeated, which is what retires the slot forever |
| Level | wRoamMon1Level |
01:dd1b |
always 40, never changes (no EXP in the struct) |
| MapGroup | wRoamMon1MapGroup |
01:dd1c |
GROUP_N_A when retired |
| MapNumber | wRoamMon1MapNumber |
01:dd1d |
MAP_N_A when retired |
| HP | wRoamMon1HP |
01:dd1e |
one byte; 0 means "stats not rolled yet" |
| DVs | wRoamMon1DVs |
01:dd1f |
rolled once, on the first encounter, then kept |
Entei is slot 2 (wRoamMon2Species 01:dd21), Suicune slot 3
(wRoamMon3Species 01:dd28). Slot order is load bearing:
CheckEncounterRoamMon indexes the structs by a random 0..2 and
GetRoamMonHP walks them by species.
Player-position backup: wRoamMons_CurMapGroup 01:dd30,
wRoamMons_LastMapGroup 01:dd32 (_BackUpMapIndices, 0a:6942).
Creation: InitRoamMons (engine/overworld/wildmons.asm:488, 0a:67d7)
Called exactly once, from maps/BurnedTowerB1F.asm:65, at the tail of
ReleaseTheBeasts right after setscene SCENE_BURNEDTOWERB1F_NOOP and
setevent EVENT_RELEASED_THE_BEASTS. It writes:
| slot | species | level | starting map |
|---|---|---|---|
| 1 | RAIKOU |
40 | MAP_ROUTE_42 (GROUP_ROUTE_42) |
| 2 | ENTEI |
40 | MAP_ROUTE_37 (GROUP_ROUTE_37) |
| 3 | SUICUNE |
40 | MAP_ROUTE_38 (GROUP_ROUTE_38) |
HP is written as 0 (xor a ; generate new stats).
The map graph: RoamMaps (data/wild/roammon_maps.asm, 0a:695b,
NUM_ROAMMON_MAPS = 16). Transcribed verbatim:
| start map | reachable maps |
|---|---|
ROUTE_29 |
ROUTE_30, ROUTE_46 |
ROUTE_30 |
ROUTE_29, ROUTE_31 |
ROUTE_31 |
ROUTE_30, ROUTE_32, ROUTE_36 |
ROUTE_32 |
ROUTE_36, ROUTE_31, ROUTE_33 |
ROUTE_33 |
ROUTE_32, ROUTE_34 |
ROUTE_34 |
ROUTE_33, ROUTE_35 |
ROUTE_35 |
ROUTE_34, ROUTE_36 |
ROUTE_36 |
ROUTE_35, ROUTE_31, ROUTE_32, ROUTE_37 |
ROUTE_37 |
ROUTE_36, ROUTE_38, ROUTE_42 |
ROUTE_38 |
ROUTE_37, ROUTE_39, ROUTE_42 |
ROUTE_39 |
ROUTE_38 |
ROUTE_42 |
ROUTE_43, ROUTE_44, ROUTE_37, ROUTE_38 |
ROUTE_43 |
ROUTE_42, ROUTE_44 |
ROUTE_44 |
ROUTE_42, ROUTE_43, ROUTE_45 |
ROUTE_45 |
ROUTE_44, ROUTE_46 |
ROUTE_46 |
ROUTE_45, ROUTE_29 |
The file's own comment: "Notably missing are Route 40 and Route 41, which are water routes." That is the walkthrough's "the exceptions are Routes 40 and 41".
Movement: UpdateRoamMons (engine/overworld/wildmons.asm:578, 0a:6859)
.Update per live beast, then _BackUpMapIndices. One random byte does double
duty:
and %00011111; if the result is zero (1 in 32) it callsJumpRoamMoninstead, i.e. a completely randomRoamMapsentry.- otherwise
and %11of that same masked value is the connection index. An index>=the entry's connection count re-rolls, and so does a candidate equal towRoamMons_LastMapGroup/wRoamMons_LastMapNumber, which is the map the player was on before the one they are on now. That last check is what stops a beast shadowing a player who paces one boundary.
Because the jump test and the index share one byte, they are not independent rolls.
When UpdateRoamMons runs (data/maps/setup_scripts.asm)
| map setup script | contains UpdateRoamMons? |
reached by |
|---|---|---|
MapSetupScript_Connection |
yes (line 93) | walking across a map connection, engine/overworld/events.asm:1013 reloadend MAPSETUP_CONNECTION |
MapSetupScript_Train |
yes (line 121) | magnet train, and everything that falls into it |
MapSetupScript_Door |
yes, by falling through into _Train |
normal door/stair warps, engine/overworld/events.asm:993; Dig / Escape Rope, engine/events/overworld.asm:836 |
MapSetupScript_Fall |
yes, by falling through _Door into _Train |
hole falls |
MapSetupScript_Warp |
no | warp script opcode (engine/overworld/scripting.asm:1958), whiteout (engine/events/whiteout.asm:21), new game |
MapSetupScript_ReloadMap |
no | reloadmap |
MapSetupScript_Teleport |
no, runs JumpRoamMons instead (line 29) |
Fly (engine/events/overworld.asm:604), Teleport (:921) |
So the walkthrough's "they change route when you change route or enter/exit a
city, cave, or building" is exactly right: connections and door warps both
update. A scripted warp does not.
Scattering: JumpRoamMons (:677, 0a:68e0) / JumpRoamMon (:710,
0a:6919). Picks a uniformly random RoamMaps entry, re-rolling only while it
lands on the player's own current map. Two callers:
MapSetupScript_Teleport(Fly / Teleport). This is the walkthrough's "Flying will reset the roaming legendary's location".engine/menus/intro_menu.asm:283,farcall JumpRoamMons, on Continue. Loading a save scatters all three.
Meeting one: CheckEncounterRoamMon (:531, 0a:681a), called from the
very top of ChooseWildEncounter (0a:66ab), which itself is called from
TryWildEncounter (0a:6643) after the map's encounter-rate roll has already
passed. Order of gates:
CheckOnWater-> on water, no roamer. (Suicune cannot be met while surfing.)- One random byte,
cp 100-> 100/256 pass. and %00000011,jr z-> 3/4 of those pass. Running total 75/256, about 29.3%, which the asm comments itself.dec a-> slot 0, 1 or 2, evenly.- That one slot's stored map group/number must equal the player's. There is no re-roll onto another beast, so two beasts on your route still get one roll each.
- On success:
wTempWildMonSpecies= the beast,wCurPartyLevel= 40,wBattleType=BATTLETYPE_ROAMING, andChooseWildEncounterreturns carry straight to.startwildbattle, skipping the map's own slot table entirely. A roamer replaces the normal encounter.
Sweet Scent is a separate, better door. engine/events/sweet_scent.asm:31
SweetScentEncounter runs CanEncounterWildMon, then only checks that
GetMapEncounterRate is nonzero (ld a, b / and a / jr z), then calls
ChooseWildEncounter directly. It never performs the percentage roll and never
reaches CheckRepelEffect. So every Sweet Scent use in grass on a roam route is
a guaranteed encounter with a flat ~9.8% chance (1/3 of 75/256) of being the
specific beast standing there. The walkthrough does not mention this.
Battle behaviour (engine/battle/core.asm)
- DVs,
:5832-:5870: if the struct's HP byte is nonzero the stored DVs are reused, otherwise fresh DVs are rolled and written back. A beast you chase all game is one individual. - HP,
:6068-:6090: same test. Zero takes.InitRoamHP, which writes the mon's full HP into the struct on the first encounter. Non-zero loads the stored value intowEnemyMonHP + 1. This is the walkthrough's "once you've weakened a legendary once, it will remain at that HP". The comment notes only the low byte is stored because Raikou and Entei are under 256 HP at level 40. TryEnemyFlee(:711,0f:44fa), called at the head of the enemy's half of the turn in both orders (Battle_EnemyFirst:823,Battle_PlayerFirst:885). It bails (beast stays) only if: trainer battle,SUBSTATUS_CANT_RUNon the player (Mean Look / Spider Web), a livewEnemyWrapCount, or the enemy isFRZor asleep. OtherwiseAlwaysFleeMons(data/wild/flee_mons.asm,0f:4568) is exactlyRAIKOU, ENTEI, SUICUNEand it flees with no roll at all. This is "the roaming legendaries will all run after one attack or one ball throw": a failed ball setswBattlePlayerAction = BATTLEPLAYERACTION_USEITEM,DetermineMoveOrdersends it to.player_first, andBattle_PlayerFirststill reachescall TryEnemyFlee. Note paralysis does not stop the flee; only sleep and freeze do.BattleEnd_HandleRoamMons(:8237,0f:76ac): onWIN(which covers a catch) it zeroes HP, sets the map pair toGROUP_N_A/MAP_N_Aand zeroes the species, retiring the slot. On anything else it bankswEnemyMonHP + 1into the struct and callsUpdateRoamMons. Its.not_roamingtail is the other half: after any other wild battle,BattleRandom and $fgives a 1-in-16 chance the beasts move anyway.
Tracking: FindNest (engine/overworld/wildmons.asm:28, 0a:6560). Its
.RoamMon1/.RoamMon2/.RoamMon3 tails append the roamer's current map to the
nest list whenever wNamedObjectIndex matches the slot's species, and only for
the Johto pass (ld a, e / and a / jr nz, .kanto returns before them). The one
caller is engine/pokegear/pokegear.asm:2430, inside Pokedex_GetArea
(24:5c7f), i.e. the Pokedex AREA screen, which is drawn with the Pokegear
town-map engine and headed <MON>'S NEST. See section 6 for the wording
mismatch with the walkthrough.
Catch rate (engine/items/item_effects.asm, PokeBallEffect 03:6926)
- All three beasts have
db 3 ; catch rate(data/pokemon/base_stats/raikou.asm,entei.asm,suicune.asm). MASTER_BALLshort-circuits to.catch_without_failbefore any multiplier.UltraBallMultiplier(03:6c92) issla b, a flat x2 -> effective 6.FastBallMultiplier(03:6dbc) carries a documented bug: it is intended to x4 anything in the threeFleeMonstables, but thejr nz, .nextwhere the author meantjr nz, .looplimits it to the first three entries ofSometimesFleeMons(Magnemite, Grimer, Tangela). Fast Balls are worthless on the beasts in Gold/Silver.- Status: the
.statuscheckblock is also buggy.+10forFRZ/SLPworks; theBRN/PSN/PAR+5branch is unreachable because theld a, [wEnemyMonStatus]reload is commented out. Paralysis gives no catch bonus at all, which contradicts the walkthrough's "asleep or paralyzed".
Roam map set (the other twelve)
These twelve maps carry no roamer-specific script or object; they matter only as
graph nodes and as places to stand in grass. Header rows are from
data/maps/maps.asm, dimensions from constants/map_constants.asm.
| Map constant | asm file | group | W x H (blocks) | tileset / environment / music | header line |
|---|---|---|---|---|---|
MAP_ROUTE_29 |
maps/Route29.asm |
NEW_BARK |
30 x 9 | TILESET_JOHTO / ROUTE / MUSIC_ROUTE_29 |
data/maps/maps.asm:474 |
MAP_ROUTE_30 |
maps/Route30.asm |
CHERRYGROVE |
10 x 27 | TILESET_JOHTO / ROUTE / MUSIC_ROUTE_30 |
:508 |
MAP_ROUTE_31 |
maps/Route31.asm |
CHERRYGROVE |
20 x 9 | TILESET_JOHTO / ROUTE / MUSIC_ROUTE_30 |
:509 |
MAP_ROUTE_32 |
maps/Route32.asm |
VIOLET |
10 x 45 | TILESET_JOHTO / ROUTE / MUSIC_ROUTE_30 |
:247 |
MAP_ROUTE_33 |
maps/Route33.asm |
AZALEA |
10 x 9 | TILESET_JOHTO_MODERN / ROUTE / MUSIC_ROUTE_30 |
:231 |
MAP_ROUTE_34 |
maps/Route34.asm |
GOLDENROD |
10 x 27 | TILESET_JOHTO_MODERN / ROUTE / MUSIC_ROUTE_36 |
:268 |
MAP_ROUTE_38 |
maps/Route38.asm |
OLIVINE |
20 x 9 | TILESET_JOHTO / ROUTE / MUSIC_ROUTE_37 |
:61 |
MAP_ROUTE_39 |
maps/Route39.asm |
OLIVINE |
10 x 18 | TILESET_JOHTO / ROUTE / MUSIC_ROUTE_37 |
:62 |
MAP_ROUTE_43 |
maps/Route43.asm |
LAKE_OF_RAGE |
10 x 27 | TILESET_JOHTO / ROUTE / MUSIC_LAKE_OF_RAGE |
:241 |
MAP_ROUTE_44 |
maps/Route44.asm |
MAHOGANY |
30 x 9 | TILESET_JOHTO / ROUTE / MUSIC_LAKE_OF_RAGE |
:73 |
MAP_ROUTE_45 |
maps/Route45.asm |
BLACKTHORN |
10 x 45 | TILESET_JOHTO / ROUTE / MUSIC_ROUTE_36 |
:185 |
MAP_ROUTE_46 |
maps/Route46.asm |
BLACKTHORN |
10 x 18 | TILESET_JOHTO / ROUTE / MUSIC_ROUTE_36 |
:186 |
Excluded on purpose, and confirmed absent from RoamMaps:
MAP_ROUTE_40 (maps/Route40.asm, CIANWOOD, 10 x 18,
data/maps/maps.asm:442) and MAP_ROUTE_41 (maps/Route41.asm, CIANWOOD,
25 x 27, :443).
MAP_VIOLET_CITY
- Script:
maps/VioletCity.asm - Header:
data/maps/maps.asm:251->TILESET_JOHTO,TOWN,LANDMARK_VIOLET_CITY,MUSIC_VIOLET_CITY, phoneFALSE,PALETTE_AUTO,FISHGROUP_POND - Dimensions:
constants/map_constants.asm:230,map_const VIOLET_CITY, 20, 18 - Connections (
data/maps/attributes.asm:127): southRoute32(ROUTE_32, 0), westRoute36(ROUTE_36, 0), eastRoute31(ROUTE_31, 9)
Included only because it is the walkthrough's Fly anchor and the one city with a
direct edge onto a roam route. It is not itself a roam map, so arriving here
never gives a beast encounter; it only ticks UpdateRoamMons on the way in and
out.
Warps (def_warp_events)
| idx | x | y | destination map | dest warp |
|---|---|---|---|---|
| 1 | 9 | 17 | VIOLET_MART |
2 |
| 2 | 18 | 17 | VIOLET_GYM |
1 |
| 3 | 30 | 17 | EARLS_POKEMON_ACADEMY |
1 |
| 4 | 3 | 15 | VIOLET_NICKNAME_SPEECH_HOUSE |
1 |
| 5 | 31 | 25 | VIOLET_POKECENTER_1F |
1 |
| 6 | 21 | 29 | VIOLET_KYLES_HOUSE |
1 |
| 7 | 23 | 5 | SPROUT_TOWER_1F |
1 |
| 8 | 39 | 24 | ROUTE_31_VIOLET_GATE |
1 |
| 9 | 39 | 25 | ROUTE_31_VIOLET_GATE |
2 |
Every one of these nine is a door warp, i.e. MAPSETUP_DOOR, i.e. one
UpdateRoamMons in and one on the way out. Stepping in and out of the Pokecenter
is a legal, very short roam tick.
Coord events (def_coord_events): none.
BG events (def_bg_events)
| x | y | type | script/item |
|---|---|---|---|
| 24 | 20 | BGEVENT_READ |
VioletCitySign |
| 15 | 17 | BGEVENT_READ |
VioletGymSign |
| 24 | 8 | BGEVENT_READ |
SproutTowerSign |
| 27 | 17 | BGEVENT_READ |
EarlsPokemonAcademySign |
| 32 | 25 | BGEVENT_READ |
VioletCityPokecenterSign |
| 10 | 17 | BGEVENT_READ |
VioletCityMartSign |
| 37 | 14 | BGEVENT_ITEM |
VioletCityHiddenHyperPotion |
Object events (def_object_events)
| const | sprite | x | y | movement | type | script label | event flag |
|---|---|---|---|---|---|---|---|
| (1) | SPRITE_FISHER |
13 | 16 | SPINRANDOM_SLOW |
OBJECTTYPE_SCRIPT |
VioletCityEarlScript |
EVENT_VIOLET_CITY_EARL |
| (2) | SPRITE_LASS |
28 | 28 | WANDER |
OBJECTTYPE_SCRIPT |
VioletCityLassScript |
-1 |
| (3) | SPRITE_SUPER_NERD |
24 | 14 | WANDER |
OBJECTTYPE_SCRIPT |
VioletCitySuperNerdScript |
-1 |
| (4) | SPRITE_GRAMPS |
17 | 20 | WALK_LEFT_RIGHT |
OBJECTTYPE_SCRIPT |
VioletCityGrampsScript |
-1 |
| (5) | SPRITE_YOUNGSTER |
5 | 18 | SPINRANDOM_SLOW |
OBJECTTYPE_SCRIPT |
VioletCityYoungsterScript |
-1 |
| (6) | SPRITE_FRUIT_TREE |
14 | 29 | STILL |
OBJECTTYPE_SCRIPT |
VioletCityFruitTree |
-1 |
| (7) | SPRITE_POKE_BALL |
4 | 1 | STILL |
OBJECTTYPE_ITEMBALL |
VioletCityPPUp |
EVENT_VIOLET_CITY_PP_UP |
| (8) | SPRITE_POKE_BALL |
35 | 5 | STILL |
OBJECTTYPE_ITEMBALL |
VioletCityRareCandy |
EVENT_VIOLET_CITY_RARE_CANDY |
Wild encounters: none relevant (town, no grass on the walked path).
MAP_ROUTE_36
- Script:
maps/Route36.asm - Header:
data/maps/maps.asm:249->TILESET_JOHTO,ROUTE,LANDMARK_ROUTE_36,MUSIC_ROUTE_36, phoneFALSE,PALETTE_AUTO,FISHGROUP_SHORE - Dimensions:
constants/map_constants.asm:228,map_const ROUTE_36, 30, 9 - Connections (
data/maps/attributes.asm:206): northRoute37(ROUTE_37, 10), southRoute35(ROUTE_35, 0), eastVioletCity(VIOLET_CITY, 0) - Roam edges (
RoamMaps):ROUTE_35,ROUTE_31,ROUTE_32,ROUTE_37. One of the two four-way junctions, so a beast standing here has the widest spread.
Warps (def_warp_events)
| idx | x | y | destination map | dest warp |
|---|---|---|---|---|
| 1 | 18 | 8 | ROUTE_36_NATIONAL_PARK_GATE |
3 |
| 2 | 18 | 9 | ROUTE_36_NATIONAL_PARK_GATE |
4 |
| 3 | 47 | 13 | ROUTE_36_RUINS_OF_ALPH_GATE |
1 |
| 4 | 48 | 13 | ROUTE_36_RUINS_OF_ALPH_GATE |
2 |
Coord events (def_coord_events): none.
BG events (def_bg_events)
| x | y | type | script/item |
|---|---|---|---|
| 29 | 1 | BGEVENT_READ |
Route36TrainerTips2 |
| 45 | 11 | BGEVENT_READ |
RuinsOfAlphNorthSign |
| 55 | 7 | BGEVENT_READ |
Route36Sign |
| 21 | 7 | BGEVENT_READ |
Route36TrainerTips1 |
Object events (def_object_events)
| const | sprite | x | y | movement | type | script label | event flag |
|---|---|---|---|---|---|---|---|
ROUTE36_YOUNGSTER1 |
SPRITE_YOUNGSTER |
20 | 12 | STANDING_RIGHT |
OBJECTTYPE_TRAINER (sight 2) |
TrainerPsychicMark |
-1 |
ROUTE36_YOUNGSTER2 |
SPRITE_YOUNGSTER |
31 | 14 | STANDING_LEFT |
OBJECTTYPE_TRAINER (sight 5) |
TrainerSchoolboyAlan1 |
-1 |
ROUTE36_WEIRD_TREE |
SPRITE_WEIRD_TREE |
35 | 9 | SUDOWOODO |
OBJECTTYPE_SCRIPT |
SudowoodoScript |
EVENT_ROUTE_36_SUDOWOODO |
ROUTE36_LASS1 |
SPRITE_LASS |
51 | 8 | WALK_LEFT_RIGHT |
OBJECTTYPE_SCRIPT |
Route36LassScript |
-1 |
ROUTE36_FISHER |
SPRITE_FISHER |
44 | 9 | STANDING_LEFT |
OBJECTTYPE_SCRIPT |
Route36RockSmashGuyScript |
-1 |
ROUTE36_FRUIT_TREE |
SPRITE_FRUIT_TREE |
21 | 4 | STILL |
OBJECTTYPE_SCRIPT |
Route36FruitTree (FRUITTREE_ROUTE_36) |
-1 |
ROUTE36_ARTHUR |
SPRITE_YOUNGSTER |
46 | 6 | WANDER |
OBJECTTYPE_SCRIPT |
ArthurScript |
EVENT_ROUTE_36_ARTHUR_OF_THURSDAY |
Scripts of interest
Route36ArthurCallback(MAPCALLBACK_OBJECTS):readvar VAR_WEEKDAY,ifequal THURSDAY->appear ROUTE36_ARTHUR, elsedisappear. Irrelevant to the beasts but it is the map's only callback, so a bot that emulates map callbacks must run it.SudowoodoScript: the one thing on this map that can physically block the Route 36 loop.checkitem SQUIRTBOTTLE; without it the tree just shakes and the tile stays solid. With it:loadwildmon SUDOWOODO, 20,startbattle,setevent EVENT_FOUGHT_SUDOWOODO, thendisappear ROUTE36_WEIRD_TREEon either branch (DidntCatchSudowoodoalso disappears it). OnceEVENT_ROUTE_36_SUDOWOODOis cleared the tile is free for good.
Flags and events
| constant | defined in | read/written by | meaning for a bot |
|---|---|---|---|
EVENT_ROUTE_36_SUDOWOODO |
constants/event_flags.asm:1178 |
object_event visibility; cleared by disappear in SudowoodoScript |
while set, x=35,y=9 is impassable |
EVENT_FOUGHT_SUDOWOODO |
constants/event_flags.asm |
set in WateredWeirdTreeScript |
gates the Rock Smash gift and the lass's line |
EVENT_ROUTE_36_ARTHUR_OF_THURSDAY |
constants/event_flags.asm |
Route36ArthurCallback |
day-of-week NPC |
Wild encounters (data/wild/johto_grass.asm, def_grass_wildmons ROUTE_36,
rates 10 percent morn/day/nite, Gold column)
| slot | morn | day | nite |
|---|---|---|---|
| 1 | 12 NIDORAN_M |
12 NIDORAN_M |
12 NIDORAN_M |
| 2 | 12 NIDORAN_F |
12 NIDORAN_F |
12 NIDORAN_F |
| 3 | 13 PIDGEY |
14 PIDGEY |
13 HOOTHOOT |
| 4 | 13 GROWLITHE |
13 GROWLITHE |
13 GROWLITHE |
| 5 | 13 STANTLER |
13 STANTLER |
13 STANTLER |
| 6 | 15 PIDGEY |
15 GROWLITHE |
15 HOOTHOOT |
| 7 | 15 PIDGEY |
15 GROWLITHE |
15 HOOTHOOT |
(Silver swaps GROWLITHE for VULPIX and reverses the Nidoran pair.) Max wild
level 15, which matters for the Max Repel plan in section 3.
MAP_ROUTE_35
- Script:
maps/Route35.asm - Header:
data/maps/maps.asm:248->TILESET_JOHTO,ROUTE,LANDMARK_ROUTE_35,MUSIC_ROUTE_36, phoneFALSE,PALETTE_AUTO,FISHGROUP_POND - Dimensions:
constants/map_constants.asm:227,map_const ROUTE_35, 10, 18 - Connections (
data/maps/attributes.asm:202): northRoute36(ROUTE_36, 0), southGoldenrodCity(GOLDENROD_CITY, -5) - Roam edges:
ROUTE_34,ROUTE_35->ROUTE_34,ROUTE_36
Warps (def_warp_events)
| idx | x | y | destination map | dest warp |
|---|---|---|---|---|
| 1 | 9 | 33 | ROUTE_35_GOLDENROD_GATE |
1 |
| 2 | 10 | 33 | ROUTE_35_GOLDENROD_GATE |
2 |
| 3 | 3 | 5 | ROUTE_35_NATIONAL_PARK_GATE |
3 |
Coord events (def_coord_events): none.
BG events (def_bg_events)
| x | y | type | script/item |
|---|---|---|---|
| 1 | 7 | BGEVENT_READ |
Route35Sign |
| 11 | 31 | BGEVENT_READ |
Route35Sign |
Object events (def_object_events)
| const | sprite | x | y | movement | type | script label | event flag |
|---|---|---|---|---|---|---|---|
| (1) | SPRITE_YOUNGSTER |
3 | 19 | STANDING_RIGHT |
OBJECTTYPE_TRAINER (4) |
TrainerCamperIvan |
-1 |
| (2) | SPRITE_YOUNGSTER |
8 | 20 | STANDING_DOWN |
OBJECTTYPE_TRAINER (3) |
TrainerCamperElliot |
-1 |
| (3) | SPRITE_LASS |
7 | 20 | STANDING_DOWN |
OBJECTTYPE_TRAINER (3) |
TrainerPicnickerBrooke |
-1 |
| (4) | SPRITE_LASS |
11 | 24 | STANDING_LEFT |
OBJECTTYPE_TRAINER (3) |
TrainerPicnickerKim |
-1 |
| (5) | SPRITE_YOUNGSTER |
14 | 28 | STANDING_DOWN |
OBJECTTYPE_TRAINER (0) |
TrainerBirdKeeperBryan |
-1 |
| (6) | SPRITE_FISHER |
2 | 10 | SPINCOUNTERCLOCKWISE |
OBJECTTYPE_TRAINER (2) |
TrainerFirebreatherWalt |
-1 |
| (7) | SPRITE_BUG_CATCHER |
16 | 7 | STANDING_DOWN |
OBJECTTYPE_TRAINER (3) |
TrainerBugCatcherArnie |
-1 |
| (8) | SPRITE_SUPER_NERD |
5 | 10 | SPINRANDOM_FAST |
OBJECTTYPE_TRAINER (2) |
TrainerJugglerIrwin |
-1 |
| (9) | SPRITE_OFFICER |
5 | 6 | STANDING_DOWN |
OBJECTTYPE_SCRIPT |
TrainerOfficerDirk |
-1 |
| (10) | SPRITE_FRUIT_TREE |
2 | 25 | STILL |
OBJECTTYPE_SCRIPT |
Route35FruitTree (FRUITTREE_ROUTE_35) |
-1 |
| (11) | SPRITE_POKE_BALL |
13 | 16 | STILL |
OBJECTTYPE_ITEMBALL |
Route35TMRollout (TM_ROLLOUT) |
EVENT_ROUTE_35_TM_ROLLOUT |
Scripts of interest
TrainerOfficerDirk(maps/Route35.asm:223): anOBJECTTYPE_SCRIPT, not a sight trainer.checktime NITE-> only battles at night, guarded byEVENT_BEAT_OFFICER_DIRK,loadtrainer OFFICER, DIRK. A bot oscillating Route 35 / Route 36 at night will be pulled into this battle once. Note that a normal wild-or-trainer battle is not wasted time:BattleEnd_HandleRoamMons's.not_roamingtail moves the beasts 1 time in 16.
Wild encounters (data/wild/johto_grass.asm, def_grass_wildmons ROUTE_35,
rates 10 percent all three, Gold column)
| slot | morn | day | nite |
|---|---|---|---|
| 1 | 12 NIDORAN_M |
12 NIDORAN_M |
12 NIDORAN_M |
| 2 | 12 NIDORAN_F |
12 NIDORAN_F |
12 NIDORAN_F |
| 3 | 14 DROWZEE |
14 DROWZEE |
14 DROWZEE |
| 4 | 10 ABRA |
10 ABRA |
10 ABRA |
| 5 | 14 PIDGEY |
14 PIDGEY |
14 HOOTHOOT |
| 6 | 10 DITTO |
10 DITTO |
10 DITTO |
| 7 | 12 YANMA |
12 YANMA |
12 YANMA |
Max wild level 14.
MAP_ROUTE_37
- Script:
maps/Route37.asm - Header:
data/maps/maps.asm:250->TILESET_JOHTO,ROUTE,LANDMARK_ROUTE_37,MUSIC_ROUTE_36, phoneFALSE,PALETTE_AUTO,FISHGROUP_SHORE - Dimensions:
constants/map_constants.asm:229,map_const ROUTE_37, 10, 9 - Connections (
data/maps/attributes.asm:211): northEcruteakCity(ECRUTEAK_CITY, -5), southRoute36(ROUTE_36, -10) - Roam edges:
ROUTE_36,ROUTE_38,ROUTE_42. Entei's starting map.
Warps (def_warp_events): none. Route 37 is reached only by connection,
which makes it the cheapest possible UpdateRoamMons tick in the loop: one step
across the boundary and one step back.
Coord events (def_coord_events): none.
BG events (def_bg_events)
| x | y | type | script/item |
|---|---|---|---|
| 5 | 3 | BGEVENT_READ |
Route37Sign |
| 4 | 2 | BGEVENT_ITEM |
Route37HiddenEther (hiddenitem ETHER, EVENT_ROUTE_37_HIDDEN_ETHER) |
Object events (def_object_events)
| const | sprite | x | y | movement | type | script label | event flag |
|---|---|---|---|---|---|---|---|
ROUTE37_WEIRD_TREE1 |
SPRITE_WEIRD_TREE |
6 | 12 | STANDING_DOWN |
OBJECTTYPE_TRAINER (1) |
TrainerTwinsAnnandanne1 |
-1 |
ROUTE37_WEIRD_TREE2 |
SPRITE_WEIRD_TREE |
7 | 12 | STANDING_DOWN |
OBJECTTYPE_TRAINER (1) |
TrainerTwinsAnnandanne2 |
-1 |
ROUTE37_YOUNGSTER |
SPRITE_YOUNGSTER |
9 | 6 | STANDING_LEFT |
OBJECTTYPE_TRAINER (3) |
TrainerPsychicGreg |
-1 |
ROUTE37_FRUIT_TREE1 |
SPRITE_FRUIT_TREE |
13 | 5 | STILL |
OBJECTTYPE_SCRIPT |
Route37FruitTree1 (FRUITTREE_ROUTE_37_1) |
-1 |
ROUTE37_SUNNY |
SPRITE_BUG_CATCHER |
16 | 8 | WANDER |
OBJECTTYPE_SCRIPT |
SunnyScript |
EVENT_ROUTE_37_SUNNY_OF_SUNDAY |
ROUTE37_FRUIT_TREE2 |
SPRITE_FRUIT_TREE |
16 | 5 | STILL |
OBJECTTYPE_SCRIPT |
Route37FruitTree2 (FRUITTREE_ROUTE_37_2) |
-1 |
ROUTE37_FRUIT_TREE3 |
SPRITE_FRUIT_TREE |
15 | 7 | STILL |
OBJECTTYPE_SCRIPT |
Route37FruitTree3 (FRUITTREE_ROUTE_37_3) |
-1 |
Scripts of interest
Route37SunnyCallback(MAPCALLBACK_OBJECTS):readvar VAR_WEEKDAY,ifequal SUNDAY->appear ROUTE37_SUNNY, elsedisappear.SunnyScript:verbosegiveitem MAGNETguarded byEVENT_GOT_MAGNET_FROM_SUNNYandEVENT_MET_SUNNY_OF_SUNDAY.
Wild encounters (data/wild/johto_grass.asm, def_grass_wildmons ROUTE_37,
rates 10 percent all three, Gold column)
| slot | morn | day | nite |
|---|---|---|---|
| 1 | 13 PIDGEY |
13 PIDGEY |
13 SPINARAK |
| 2 | 15 STANTLER |
15 STANTLER |
15 STANTLER |
| 3 | 15 PIDGEY |
15 PIDGEY |
15 HOOTHOOT |
| 4 | 14 GROWLITHE |
14 GROWLITHE |
14 GROWLITHE |
| 5 | 15 PIDGEY |
15 PIDGEOTTO |
15 SPINARAK |
| 6 | 15 PIDGEY |
16 GROWLITHE |
15 SPINARAK |
| 7 | 15 PIDGEY |
16 GROWLITHE |
15 SPINARAK |
Max wild level 16.
MAP_ROUTE_42
- Script:
maps/Route42.asm - Header:
data/maps/maps.asm:72->TILESET_JOHTO,ROUTE,LANDMARK_ROUTE_42,MUSIC_LAKE_OF_RAGE, phoneFALSE,PALETTE_AUTO,FISHGROUP_LAKE - Dimensions:
constants/map_constants.asm:60,map_const ROUTE_42, 30, 9 - Connections (
data/maps/attributes.asm:231): westEcruteakCity(ECRUTEAK_CITY, -9), eastMahoganyTown(MAHOGANY_TOWN, 0) - Roam edges:
ROUTE_43,ROUTE_44,ROUTE_37,ROUTE_38. The other four-way junction, and Raikou's starting map. This is the walkthrough's Max Repel spot.
Warps (def_warp_events)
| idx | x | y | destination map | dest warp |
|---|---|---|---|---|
| 1 | 0 | 8 | ROUTE_42_ECRUTEAK_GATE |
3 |
| 2 | 0 | 9 | ROUTE_42_ECRUTEAK_GATE |
4 |
| 3 | 10 | 5 | MOUNT_MORTAR_1F_OUTSIDE |
1 |
| 4 | 28 | 9 | MOUNT_MORTAR_1F_OUTSIDE |
2 |
| 5 | 46 | 7 | MOUNT_MORTAR_1F_OUTSIDE |
3 |
Coord events (def_coord_events): none.
BG events (def_bg_events)
| x | y | type | script/item |
|---|---|---|---|
| 4 | 10 | BGEVENT_READ |
Route42Sign1 |
| 7 | 5 | BGEVENT_READ |
MtMortarSign1 |
| 45 | 9 | BGEVENT_READ |
MtMortarSign2 |
| 54 | 8 | BGEVENT_READ |
Route42Sign2 |
| 16 | 11 | BGEVENT_ITEM |
Route42HiddenMaxPotion (hiddenitem MAX_POTION, EVENT_ROUTE_42_HIDDEN_MAX_POTION) |
Object events (def_object_events)
| const | sprite | x | y | movement | type | script label | event flag |
|---|---|---|---|---|---|---|---|
| (1) | SPRITE_FISHER |
40 | 10 | STANDING_LEFT |
OBJECTTYPE_TRAINER (1) |
TrainerFisherChris |
-1 |
| (2) | SPRITE_POKEFAN_M |
51 | 9 | SPINRANDOM_FAST |
OBJECTTYPE_TRAINER (3) |
TrainerHikerBenjamin |
-1 |
| (3) | SPRITE_SUPER_NERD |
47 | 8 | STANDING_DOWN |
OBJECTTYPE_TRAINER (3) |
TrainerPokemaniacShane |
-1 |
| (4) | SPRITE_FRUIT_TREE |
27 | 16 | STILL |
OBJECTTYPE_SCRIPT |
Route42FruitTree1 (FRUITTREE_ROUTE_42_1) |
-1 |
| (5) | SPRITE_FRUIT_TREE |
28 | 16 | STILL |
OBJECTTYPE_SCRIPT |
Route42FruitTree2 (FRUITTREE_ROUTE_42_2) |
-1 |
| (6) | SPRITE_FRUIT_TREE |
29 | 16 | STILL |
OBJECTTYPE_SCRIPT |
Route42FruitTree3 (FRUITTREE_ROUTE_42_3) |
-1 |
| (7) | SPRITE_POKE_BALL |
6 | 4 | STILL |
OBJECTTYPE_ITEMBALL |
Route42UltraBall (itemball ULTRA_BALL) |
EVENT_ROUTE_42_ULTRA_BALL |
| (8) | SPRITE_POKE_BALL |
33 | 8 | STILL |
OBJECTTYPE_ITEMBALL |
Route42SuperPotion (itemball SUPER_POTION) |
EVENT_ROUTE_42_SUPER_POTION |
Items
| item | how obtained | source (label / bg_event / hidden) | one-time flag |
|---|---|---|---|
ULTRA_BALL |
item ball at x=6, y=4 | Route42UltraBall |
EVENT_ROUTE_42_ULTRA_BALL |
SUPER_POTION |
item ball at x=33, y=8 | Route42SuperPotion |
EVENT_ROUTE_42_SUPER_POTION |
MAX_POTION |
hidden, x=16, y=11 | Route42HiddenMaxPotion |
EVENT_ROUTE_42_HIDDEN_MAX_POTION |
Wild encounters (data/wild/johto_grass.asm, def_grass_wildmons ROUTE_42,
rates 10 percent all three, Gold column)
| slot | morn | day | nite |
|---|---|---|---|
| 1 | 15 MANKEY |
15 MANKEY |
15 MANKEY |
| 2 | 13 MAREEP |
13 MAREEP |
13 MAREEP |
| 3 | 14 SPEAROW |
14 SPEAROW |
14 ZUBAT |
| 4 | 16 SPEAROW |
16 SPEAROW |
16 ZUBAT |
| 5 | 15 FLAAFFY |
15 FLAAFFY |
15 FLAAFFY |
| 6 | 17 FLAAFFY |
17 FLAAFFY |
17 FLAAFFY |
| 7 | 17 FLAAFFY |
17 FLAAFFY |
17 FLAAFFY |
Max wild level 17. Every grass slot on this map is below 39, which is precisely why the walkthrough's Max Repel plan works here (section 3).
Trainers on the four detailed maps
Constants from constants/trainer_constants.asm, parties from
data/trainers/parties.asm (group and index in the file's own comment).
| const | class | id | party (data/trainers/parties.asm) |
script label | map |
|---|---|---|---|---|---|
MARK |
PSYCHIC_T |
PSYCHIC_T (7) |
13 ABRA (Teleport, Flash), 13 ABRA (Teleport, Flash), 15 KADABRA (Teleport, Kinesis, Confusion) |
TrainerPsychicMark |
Route 36 |
ALAN1 |
SCHOOLBOY |
SCHOOLBOY (3) |
16 TANGELA |
TrainerSchoolboyAlan1 |
Route 36 |
IVAN |
CAMPER |
CAMPER (3) |
10 DIGLETT, 10 ZUBAT, 14 DIGLETT |
TrainerCamperIvan |
Route 35 |
ELLIOT |
CAMPER |
CAMPER (4) |
13 SANDSHREW, 15 MARILL |
TrainerCamperElliot |
Route 35 |
BROOKE |
PICNICKER |
PICNICKER (3) |
16 PIKACHU (ThunderShock, Growl, Quick Attack, Double Team) |
TrainerPicnickerBrooke |
Route 35 |
KIM |
PICNICKER |
PICNICKER (4) |
15 VULPIX |
TrainerPicnickerKim |
Route 35 |
BRYAN |
BIRD_KEEPER |
BIRD_KEEPER (3) |
12 PIDGEY, 14 PIDGEOTTO |
TrainerBirdKeeperBryan |
Route 35 |
WALT |
FIREBREATHER |
FIREBREATHER (6) |
11 MAGMAR, 13 MAGMAR |
TrainerFirebreatherWalt |
Route 35 |
ARNIE1 |
BUG_CATCHER |
BUG_CATCHER (8) |
15 VENONAT |
TrainerBugCatcherArnie |
Route 35 |
IRWIN1 |
JUGGLER |
JUGGLER (1) |
2 / 6 / 10 / 14 VOLTORB |
TrainerJugglerIrwin |
Route 35 |
DIRK |
OFFICER |
(see OfficerGroup) |
night only | TrainerOfficerDirk |
Route 35 |
ANNANDANNE1 |
TWINS |
TWINS (2) |
16 CLEFAIRY (Growl, Encore, DoubleSlap, Metronome), 16 JIGGLYPUFF (Sing, Defense Curl, Pound, Disable) |
TrainerTwinsAnnandanne1 |
Route 37 |
ANNANDANNE2 |
TWINS |
TWINS (3) |
same two, order swapped | TrainerTwinsAnnandanne2 |
Route 37 |
GREG |
PSYCHIC_T |
PSYCHIC_T (5) |
17 DROWZEE (Hypnosis, Disable, Dream Eater) |
TrainerPsychicGreg |
Route 37 |
CHRIS1 |
FISHER |
FISHER (7) |
18 QWILFISH |
TrainerFisherChris |
Route 42 |
BENJAMIN |
HIKER |
HIKER (6) |
14 DIGLETT, 14 GEODUDE, 16 DUGTRIO |
TrainerHikerBenjamin |
Route 42 |
SHANE |
POKEMANIAC |
POKEMANIAC (4) |
16 NIDORINA, 16 NIDORINO |
TrainerPokemaniacShane |
Route 42 |
All of these are one-and-done (EVENT_BEAT_* in the trainer macro row). By
this point in the game they are already beaten and are not obstacles.
The beasts as battle opponents
data/pokemon/base_stats/{raikou,entei,suicune}.asm, level from
InitRoamMons, moves from data/pokemon/evos_attacks.asm (last four learned at
or before 40).
| species | types | base HP/Atk/Def/Spe/SpA/SpD | catch rate | level 40 moveset |
|---|---|---|---|---|
RAIKOU |
Electric | 90 / 85 / 75 / 115 / 115 / 100 | 3 | LEER, THUNDERSHOCK, ROAR, QUICK_ATTACK |
ENTEI |
Fire | 115 / 115 / 85 / 100 / 90 / 75 | 3 | LEER, EMBER, ROAR, FIRE_SPIN |
SUICUNE |
Water | 100 / 75 / 115 / 85 / 90 / 115 | 3 | LEER, WATER_GUN, ROAR, GUST |
All three know Roar. BattleCommand_ForceSwitch's wild-target branch
(engine/battle/effect_commands.asm:4913, .force_player_switch at :5010
-> .wild_succeed_playeristarget at :5041) compares
wCurPartyLevel against wBattleMonLevel and, when the wild level is greater
or equal, succeeds unconditionally: wForcedSwitch = TRUE and wBattleResult =
DRAW, ending the battle on the spot. Against a lead at level 40 or below that is
a guaranteed battle-ender if the beast picks Roar. The DRAW result then routes
through BattleEnd_HandleRoamMons's non-WIN arm, so the beast keeps its damage
and moves.
3. Blockers and gates
| Gate | Enforced by (file:label) | Requirement | Unlock condition |
|---|---|---|---|
| The beasts do not exist at all | maps/BurnedTowerB1F.asm:65 special InitRoamMons, guarded by coord_event 9, 5, SCENE_BURNEDTOWERB1F_RELEASE_THE_BEASTS, ReleaseTheBeasts |
walk the Burned Tower B1F trigger | EVENT_RELEASED_THE_BEASTS set, SCENE_BURNEDTOWERB1F_NOOP |
| No roamer on water | engine/overworld/wildmons.asm:534 CheckEncounterRoamMon call CheckOnWater / jr z, .DontEncounterRoamMon |
be on a land tile | none, this is absolute |
| No roamer off the sixteen routes | data/wild/roammon_maps.asm RoamMaps + the map compare in CheckEncounterRoamMon:546 |
stand on the beast's stored map | none |
| No roamer where there is no wild table | engine/overworld/wildmons.asm:270 LoadWildMonDataPointer returning nc -> .nowildbattle before CheckEncounterRoamMon runs |
map must have grass/water data | none |
| No roamer in the Bug Contest | engine/overworld/events.asm:1194 ChooseWildEncounter_BugContest never calls CheckEncounterRoamMon |
leave the contest | none |
| Route 36 tile x=35, y=9 blocked | maps/Route36.asm SudowoodoScript, checkitem SQUIRTBOTTLE |
SQUIRTBOTTLE in bag, then win/lose/flee the level 20 Sudowoodo |
EVENT_ROUTE_36_SUDOWOODO cleared by disappear |
| Beast escapes after one player action | engine/battle/core.asm:711 TryEnemyFlee + data/wild/flee_mons.asm AlwaysFleeMons |
trap it: SUBSTATUS_CANT_RUN (Mean Look / Spider Web), a live wEnemyWrapCount, or inflict SLP / FRZ |
any one of those four makes TryEnemyFlee return no-carry |
| Repel filters the wild, not the beast | engine/overworld/wildmons.asm:366 CheckRepelEffect: ld a, [wCurPartyLevel] / cp [hl] / jr nc, .encounter where hl is the first non-fainted party mon's level |
roamer level is 40, so the lead must be level 40 or lower or the roamer is repelled too | Route 42 grass tops out at level 17, so a level 39 lead plus MAX_REPEL (250 steps, engine/items/item_effects.asm:2058) blocks every normal encounter and passes the beast. A level 41+ lead blocks the beast as well. |
| Fast Ball does nothing here | engine/items/item_effects.asm:986 FastBallMultiplier, documented bug (jr nz, .next instead of jr nz, .loop) |
use ULTRA_BALL (x2, 03:6c92) or MASTER_BALL (short-circuit) |
none, the bug is in the shipped ROM |
| Paralysis gives no catch bonus | engine/items/item_effects.asm:344 .statuscheck, the ld a, [wEnemyMonStatus] reload is commented out |
SLP or FRZ only (+10 to the rate) |
none |
| Ultra Ball / Max Repel supply | data/items/marts.asm:216 MartBlackthorn, :384 MartIndigoPlateau |
money | both marts stock ULTRA_BALL and MAX_REPEL |
Nothing in this section is badge- or HM-gated on its own. The HMs needed to reach individual roam routes (Surf for Route 41's neighbours, Whirlpool, etc.) belong to their own sections; all sixteen roam maps are walkable by the time the beasts are loose.
4. Bot checklist
Preconditions for the whole section: EVENT_RELEASED_THE_BEASTS set,
save.roamers (port) / the three roam_structs (cart) populated, BICYCLE in
the bag, SQUIRTBOTTLE already used on Route 36.
- Locate. Map
MAP_*any, action: open Pokedex -> selectRAIKOU/ENTEI/SUICUNE-> AREA. Precondition: species is in the seen dex (set by the first encounter, not by the Burned Tower cutscene). Postcondition: the blinking nest icon is the beast's current map (FindNest.RoamMon1/2/3). If the species is retired the icon is absent. - Anchor. Fly to
MAP_VIOLET_CITY. Warning: the Fly itself runsJumpRoamMons, so any location read from step 1 is stale afterwards. Do step 1 after the Fly, not before. - Oscillate (cheap ticks). From Violet City walk west onto
MAP_ROUTE_36(connection,data/maps/attributes.asm:209). Then repeat: step north ontoMAP_ROUTE_37and back, step south ontoMAP_ROUTE_35and back. Each single boundary crossing is oneMapSetupScript_Connectionand therefore oneUpdateRoamMons. Route 37 has zero warps, so the boundary step is the only thing that happens there. - Alternative tick. Any door warp works too: on
MAP_VIOLET_CITYstep onto warp 5 (x=31, y=25,VIOLET_POKECENTER_1F) and back out. That isMAPSETUP_DOOR->MapSetupScript_Train->UpdateRoamMons, and it also heals. - Fish for the encounter. Stand in grass on the map the beast is on and
walk. Each step that passes
CanEncounterWildMonand the map's 10% rate roll then has a 75/256 roamer roll, of which 1/3 selects your beast. Postcondition:wBattleType = BATTLETYPE_ROAMING. - Better: Sweet Scent. Same tile, use
SWEET_SCENT. It skips the 10% roll and the repel check entirely (engine/events/sweet_scent.asm:31), so every use is an encounter and ~9.8% of uses are the specific beast. This is the highest-throughput option and the walkthrough does not mention it. - Repel filter (optional). Lead a party mon of level 39 or 40 (a raised
Scyther works; it learns
FALSE_SWIPEat level 18 perdata/pokemon/evos_attacks.asm:1674, and at 39 its auto-generated four are False Swipe / Agility / Wing Attack / Slash). UseMAX_REPEL(250 steps). Precondition: lead level <= 40. Postcondition: only the level 40 beast can still trigger. - Save. Before every encounter. Battle outcomes are irreversible: a
WINretires the slot for the rest of the file. - In battle, turn 1 only. Options, in order of value:
MASTER_BALL-> guaranteed, but there is only one and there are three beasts.- Trap first (
MEAN_LOOK/SPIDER_WEB, or a wrap move), which makesTryEnemyFleereturn no-carry and buys unlimited turns. This is the only way to legitimately chip HP and land status. - Otherwise: one
FALSE_SWIPE(leaves 1 HP, banked into the struct for next time) or oneULTRA_BALL. Either way the beast is gone at the end of the turn. - Do not count on
Roar: if the beast picks it against a lead of level 40 or lower the battle ends immediately as a DRAW.
- After the battle.
BattleEnd_HandleRoamMonshas already banked HP and moved the beast. Return to step 1. - Repeat per beast. Three independent slots. Retiring one does not affect
the other two; their
RoamMapswalk is unchanged.
5. Port coverage
| Beat | Port file | Status |
|---|---|---|
roam_struct on the save (species/level/map/hp/dvs, three slots in Raikou/Entei/Suicune order) |
src/core/gen2/Roamers.lua:41-47, :184-217 |
implemented |
InitRoamMons reachable through the real special dispatch |
src/script/gen2/Specials.lua:1843-1848, src/core/gen2/Roamers.lua:196 |
implemented |
RoamMaps graph, all 16 rows, Routes 40/41 excluded |
src/core/gen2/Roamers.lua:58-75 |
implemented, but hardcoded: Roamers.mapTable looks for encounters.roamMaps and the extractor does not emit it yet (:77-85) |
.Update shared-byte jump/index roll, last-map re-roll |
src/core/gen2/Roamers.lua:142-158 |
implemented |
JumpRoamMon random entry, re-roll off the player's map |
src/core/gen2/Roamers.lua:117-126 |
implemented |
_BackUpMapIndices |
src/core/gen2/Roamers.lua:223-234 |
implemented |
UpdateRoamMons on CONNECTION / DOOR / FALL / TRAIN |
src/world/gen2/World.lua:211-215 (MAPSETUP_ROAM_UPDATE), :2626-2633 |
implemented, and the set matches data/maps/setup_scripts.asm exactly |
JumpRoamMons on TELEPORT (Fly / Teleport) |
src/world/gen2/World.lua:215, :2617-2624 |
implemented |
JumpRoamMons on Continue (engine/menus/intro_menu.asm:283) |
- | missing: Roamers.jumpAll has exactly one caller, roamMonsBeforeLoad, which is keyed on MAPSETUP_TELEPORT. Loading a save does not scatter the beasts. |
CheckEncounterRoamMon (water gate, 100/256, and %11, slot map compare, no re-roll) |
src/core/gen2/Roamers.lua:288-301; called from src/world/gen2/World.lua:3043 and :3790 |
implemented |
| Roamer check ordered after the map's encounter-rate roll | src/world/gen2/World.lua:3043 vs :3053-3060 |
partial: the port runs Roamers.checkEncounter before Encounter.triggers, so a beast can appear on a step whose 10% roll would have failed. The asm order is TryWildEncounter.EncounterRate -> ChooseWildEncounter -> CheckEncounterRoamMon. Beast encounter rate is inflated by roughly 1/rate. |
| Sweet Scent path also consults the roamers, and skips the rate roll | src/world/gen2/World.lua:3785-3796 |
implemented, and the "nonzero rate only" check at :3786 matches engine/events/sweet_scent.asm:38 |
CheckRepelEffect (lead level vs wCurPartyLevel) |
- | missing: save.repelSteps counts down in src/world/gen2/StepEvents.lua:98-101 and prints the wore-off text, but nothing in World:tryWildEncounter consults it. The walkthrough's Max Repel plan is a no-op in the port today. |
| DV roll on first encounter, DVs kept afterwards | src/core/gen2/Roamers.lua:311-328 |
implemented |
| HP banked in a single byte, reloaded on the next encounter | src/core/gen2/Roamers.lua:182, :311-328, :341-354 |
implemented |
BattleEnd_HandleRoamMons WIN/caught retires the slot |
src/core/gen2/Roamers.lua:345-350, wired at src/world/gen2/World.lua:4475-4478 |
implemented |
.not_roaming 1-in-16 drift after any other wild battle |
src/core/gen2/Roamers.lua:359-363 |
implemented |
AlwaysFleeMons / OftenFleeMons / SometimesFleeMons and the shared roll byte |
src/core/gen2/Roamers.lua:370-380, src/battle/gen2/Battle.lua:1639-1651 |
implemented |
| Sleep / freeze suppress the flee | src/battle/gen2/Battle.lua:1641-1642 |
implemented |
SUBSTATUS_CANT_RUN (Mean Look) and wEnemyWrapCount suppress the flee |
- | missing, and the port says so itself at src/battle/gen2/Battle.lua:1632-1635. There is currently no way to trap a beast, which removes the only legitimate multi-turn strategy. |
| Failed ball still costs the turn, so the beast flees | src/ui/gen2/BattleState.lua:900-903 -> Battle:takeTurn{kind="item"} -> Battle:tryEnemyFlee (:1877) |
implemented |
MASTER_BALL never fails; ULTRA_BALL x2 |
src/battle/gen2/Catching.lua:29-30, :62-63 |
implemented |
FastBallMultiplier bug (x4 only for Magnemite/Grimer/Tangela) |
src/battle/gen2/Catching.lua:39 (FAST_BALL = 1) |
partial: the port is correct for the beasts (flat x1) but drops the x4 the three buggy species do get |
Pokedex AREA screen shows the roamer's current map (FindNest.RoamMon1/2/3) |
src/ui/TownMap.lua:167-198 |
missing: nest mode scans only the map wild-slot tables for the species; there is no roamer branch, so a located beast never appears on the AREA map |
BattleCommand_ForceSwitch (Roar from a wild beast ends the battle as a DRAW) |
- | missing: EFFECT_FORCE_SWITCH exists only as an AI scoring hook (src/battle/gen2/Ai.lua:559); there is no effect handler, so a roamer's Roar does not end the battle |
| Headless coverage | tests/gen2_world_test.lua:2203-2271, tests/gen2_roamers_test.lua, tests/drivers/gold_roamers.lua |
implemented: the driver runs the real InitRoamMons special, asserts the three starting routes, and exercises MAPSETUP_DOOR ticks |
6. Unresolved / verify by hand
- "They will show up on the map in Pokegear." The only caller of
FindNestisengine/pokegear/pokegear.asm:2430, insidePokedex_GetArea(24:5c7f), whose header string is'S NEST(.PlaceString_MonsNest). That is the Pokedex AREA screen, which is drawn with the Pokegear town map code but is not reached from the Pokegear menu. Nothing in_TownMap(engine/pokegear/pokegear.asm:1709) plots roamers. Treat the walkthrough's wording as loose. - "After the initial encounter ... they will show up."
FindNestitself has no "encountered" gate; it only compareswNamedObjectIndexwith the slot's species. The real gate is that you must be able to select the species in the Pokedex at all, i.e. it must be SEEN. I could not find anything inmaps/BurnedTowerB1F.asmthat marks the three as seen duringReleaseTheBeasts(it onlyappears sprites and plays cries), so in practice the first battle is what unlocks the AREA view. Not proven by a singleSetSeenMoncall; worth a hardware check. - "asleep or paralyzed." Sleep and freeze are worth
+10to the catch rate (engine/items/item_effects.asm:344). Paralysis is worth nothing because of the commented-outld a, [wEnemyMonStatus]reload in the same block, which the file itself flags as a bug. The advice is half wrong on the cart. - "they occasionally will jump a few routes." The asm's only "jump" is the
1-in-32
and %00011111 / jr z, JumpRoamMonbranch inside.Update, which is not "a few routes" but a uniformly random one of the sixteen. The observed behaviour matches loosely; the mechanism does not. - "as quickly as every 10 seconds." No timer anywhere in the roam code. Roam movement is purely event-driven (map setup scripts and battle ends). The ten seconds is a human estimate of how long two boundary crossings take.
- Roar vs the level-39 Scyther plan.
BattleCommand_ForceSwitch's wild branch readswCurPartyLevelfor the wild mon's level and compares it withwBattleMonLevel; at 40 vs 39 it takes.wild_succeed_playeristargetunconditionally. I did not trace whetherwCurPartyLevelis still the wild level at that point after a mid-battle player switch (several routines write that variable), so the "guaranteed Roar" claim is asserted from the code as written and should be confirmed on hardware before a bot relies on it. - Bike and encounter rate. The walkthrough leans on biking.
CanEncounterWildMon(engine/overworld/events.asm:1164) andGetMapEncounterRatehave no bicycle term, so the bike changes travel speed only, not encounter odds. Stated here because the opposite is widely believed. - Suicune's starting route vs the walkthrough's framing.
InitRoamMonsplaces Suicune onROUTE_38, not at the Tin Tower or Route 42. The section text never states a starting route, so there is no contradiction, but a bot that assumes "Suicune is the one you chase last" has no asm basis for it. - Route dimensions vs event coordinates.
map_constwidths/heights are in blocks;warp_event/object_event/bg_eventcoordinates are in the 2x-finer walk grid (e.g.ROUTE_35is 10 x 18 blocks and carries a warp at y=33). Every coordinate in this document is the raw asm value, unconverted.