mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-14 23:31:27 +02:00
G2 support
This commit is contained in:
@@ -0,0 +1,611 @@
|
||||
# Section 30 - Lapras
|
||||
|
||||
Source: `../section-30-lapras.txt`
|
||||
Maps covered: `MAP_UNION_CAVE_1F`, `MAP_UNION_CAVE_B1F`, `MAP_UNION_CAVE_B2F`
|
||||
(approach only: `MAP_ROUTE_32`, whose warp into the cave is cited but which
|
||||
belongs to section 03)
|
||||
Badges / key milestones in this section: no badge. The milestone is the **static
|
||||
level-20 Lapras** on Union Cave B2F, which only exists on a Friday and only once
|
||||
per in-game day (`ENGINE_UNION_CAVE_LAPRAS`). Five optional trainers
|
||||
(Andrew, Calvin, Nick, Gwen, Emma) and two item balls sit on the way in.
|
||||
|
||||
This is a *revisit* section: Union Cave 1F/B1F were first walked in section 03.
|
||||
Everything here is reachable only with Surf, i.e. after the Fog Badge.
|
||||
|
||||
## 1. Route order
|
||||
|
||||
| # | Map constant | asm file | Enter via | Leaves via | Why (walkthrough beat) |
|
||||
|---|---|---|---|---|---|
|
||||
| 1 | `MAP_ROUTE_32` | `maps/Route32.asm` | walked south from Violet City (`connection south, Route32, ROUTE_32, 0` in `data/maps/attributes.asm`) | `warp_event 6, 79, UNION_CAVE_1F, 4` | "head to Violet City and then go south through Route 32 into Union Cave" |
|
||||
| 2 | `MAP_UNION_CAVE_1F` | `maps/UnionCave1F.asm` | warp 4 at `17, 3` (from Route 32) | `warp_event 3, 33, UNION_CAVE_B1F, 4` | walk past Daniel / Russell / Bill, surf the south-west lake, take the bottom-left ladder |
|
||||
| 3 | `MAP_UNION_CAVE_B1F` | `maps/UnionCaveB1F.asm` | warp 4 at `3, 33` | `warp_event 17, 31, UNION_CAVE_B2F, 1` | Pokemaniac Andrew stands at `5, 32` right next to the arrival ladder; surf east to Calvin at `17, 30`, then the ladder beside him |
|
||||
| 4 | `MAP_UNION_CAVE_B2F` | `maps/UnionCaveB2F.asm` | warp 1 at `5, 3` | same warp back up, or Escape Rope / Dig | Hyper Potion, Nick, Gwen, Emma, then the Lapras object at `11, 31` |
|
||||
|
||||
The walkthrough ends inside Union Cave B2F ("You can then use Escape Rope to
|
||||
leave Union Cave"); it does not spill into a neighbouring section's map.
|
||||
|
||||
## 2. Maps
|
||||
|
||||
### MAP_UNION_CAVE_1F
|
||||
|
||||
- Script: `maps/UnionCave1F.asm`
|
||||
- Blocks: `maps/UnionCave1F.blk`
|
||||
- Header: `data/maps/maps.asm:107` -> `map UnionCave1F, TILESET_CAVE, CAVE, LANDMARK_UNION_CAVE, MUSIC_UNION_CAVE, TRUE, PALETTE_NITE, FISHGROUP_LAKE`
|
||||
(the `TRUE` field is the phone-service flag: **no phone calls in Union Cave**)
|
||||
- Dimensions: `constants/map_constants.asm:94` -> `map_const UNION_CAVE_1F, 10, 18` (10x18 blocks = 20x36 walk cells)
|
||||
- Attributes: `data/maps/attributes.asm:426` -> `map_attributes UnionCave1F, UNION_CAVE_1F, $09`
|
||||
- Connections: none (no `connection` rows follow the attributes line)
|
||||
- Map group: `MapGroup_Dungeons` (`data/maps/maps.asm:77`)
|
||||
|
||||
**Warps** (`def_warp_events`)
|
||||
|
||||
| idx | x | y | destination map | dest warp |
|
||||
|---|---|---|---|---|
|
||||
| 1 | 5 | 19 | `UNION_CAVE_B1F` | 3 |
|
||||
| 2 | 3 | 33 | `UNION_CAVE_B1F` | 4 |
|
||||
| 3 | 17 | 31 | `ROUTE_33` | 1 |
|
||||
| 4 | 17 | 3 | `ROUTE_32` | 4 |
|
||||
|
||||
**Coord events** (`def_coord_events`)
|
||||
|
||||
None - the block is empty.
|
||||
|
||||
**BG events** (`def_bg_events`)
|
||||
|
||||
None. (`UnionCave1FUnusedSign` / `UnionCave1FUnusedSignText` exist in the file
|
||||
but are marked `; unreferenced` and are not in any `bg_event` row.)
|
||||
|
||||
**Object events** (`def_object_events`)
|
||||
|
||||
| const | sprite | x | y | movement | type | script label | event flag |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| `UNIONCAVE1F_POKEFAN_M1` | `SPRITE_POKEFAN_M` | 4 | 4 | `SPRITEMOVEDATA_SPINRANDOM_FAST` | `OBJECTTYPE_TRAINER`, sight 1 | `TrainerHikerDaniel` | -1 |
|
||||
| `UNIONCAVE1F_SUPER_NERD` | `SPRITE_SUPER_NERD` | 4 | 21 | `SPRITEMOVEDATA_STANDING_DOWN` | `OBJECTTYPE_TRAINER`, sight 2 | `TrainerPokemaniacLarry` | -1 |
|
||||
| `UNIONCAVE1F_POKEFAN_M2` | `SPRITE_POKEFAN_M` | 15 | 8 | `SPRITEMOVEDATA_STANDING_LEFT` | `OBJECTTYPE_TRAINER`, sight 3 | `TrainerHikerRussell` | -1 |
|
||||
| `UNIONCAVE1F_FISHER1` | `SPRITE_FISHER` | 16 | 31 | `SPRITEMOVEDATA_STANDING_UP` | `OBJECTTYPE_TRAINER`, sight 4 | `TrainerFirebreatherRay` | -1 |
|
||||
| `UNIONCAVE1F_FISHER2` | `SPRITE_FISHER` | 15 | 15 | `SPRITEMOVEDATA_SPINRANDOM_FAST` | `OBJECTTYPE_TRAINER`, sight 2 | `TrainerFirebreatherBill` | -1 |
|
||||
| `UNIONCAVE1F_POKE_BALL1` | `SPRITE_POKE_BALL` | 17 | 21 | `SPRITEMOVEDATA_STILL` | `OBJECTTYPE_ITEMBALL` | `UnionCave1FGreatBall` | `EVENT_UNION_CAVE_1F_GREAT_BALL` |
|
||||
| `UNIONCAVE1F_POKE_BALL2` | `SPRITE_POKE_BALL` | 4 | 2 | `SPRITEMOVEDATA_STILL` | `OBJECTTYPE_ITEMBALL` | `UnionCave1FPotion` | `EVENT_UNION_CAVE_1F_POTION` |
|
||||
| `UNIONCAVE1F_POKE_BALL3` | `SPRITE_POKE_BALL` | 4 | 17 | `SPRITEMOVEDATA_STILL` | `OBJECTTYPE_ITEMBALL` | `UnionCave1FXAttack` | `EVENT_UNION_CAVE_1F_X_ATTACK` |
|
||||
| `UNIONCAVE1F_POKE_BALL4` | `SPRITE_POKE_BALL` | 12 | 33 | `SPRITEMOVEDATA_STILL` | `OBJECTTYPE_ITEMBALL` | `UnionCave1FAwakening` | `EVENT_UNION_CAVE_1F_AWAKENING` |
|
||||
|
||||
**Scripts of interest**
|
||||
|
||||
- `UnionCave1F_MapScripts` - `def_scene_scripts` and `def_callbacks` are both
|
||||
empty. Nothing on 1F is time- or flag-gated; the floor is pure walking.
|
||||
- `PokemaniacLarryAfterBattleText` and `FirebreatherBillAfterBattleText` are the
|
||||
in-game hints the walkthrough is acting on: *"Every Friday, you can hear
|
||||
#MON roars from deep inside the cave."* and *"On weekends, you can hear
|
||||
strange roars from deep in the cave."* Only Larry's Friday line matches the
|
||||
code (see B2F callback below).
|
||||
|
||||
**Flags and events**
|
||||
|
||||
| constant | defined in | read/written by | meaning for a bot |
|
||||
|---|---|---|---|
|
||||
| `EVENT_UNION_CAVE_1F_GREAT_BALL` | `constants/event_flags.asm` | itemball object row | set once the ball is taken; masks the object |
|
||||
| `EVENT_UNION_CAVE_1F_POTION` | `constants/event_flags.asm` | itemball object row | same |
|
||||
| `EVENT_UNION_CAVE_1F_X_ATTACK` | `constants/event_flags.asm` | itemball object row | same |
|
||||
| `EVENT_UNION_CAVE_1F_AWAKENING` | `constants/event_flags.asm` | itemball object row | same |
|
||||
| `EVENT_BEAT_HIKER_DANIEL`, `EVENT_BEAT_HIKER_RUSSELL`, `EVENT_BEAT_POKEMANIAC_LARRY`, `EVENT_BEAT_FIREBREATHER_BILL`, `EVENT_BEAT_FIREBREATHER_RAY` | `constants/event_flags.asm` | 3rd argument of each `trainer` macro row | set = already beaten, no eyesight trigger |
|
||||
|
||||
**Items**
|
||||
|
||||
| item | how obtained | source (label / bg_event / hidden) | one-time flag |
|
||||
|---|---|---|---|
|
||||
| `GREAT_BALL` | item ball at 17,21 | `UnionCave1FGreatBall` (`itemball GREAT_BALL`) | `EVENT_UNION_CAVE_1F_GREAT_BALL` |
|
||||
| `POTION` | item ball at 4,2 | `UnionCave1FPotion` | `EVENT_UNION_CAVE_1F_POTION` |
|
||||
| `X_ATTACK` | item ball at 4,17 | `UnionCave1FXAttack` | `EVENT_UNION_CAVE_1F_X_ATTACK` |
|
||||
| `AWAKENING` | item ball at 12,33 | `UnionCave1FAwakening` | `EVENT_UNION_CAVE_1F_AWAKENING` |
|
||||
|
||||
**Trainers** (all optional on this pass; the walkthrough only mentions walking past them)
|
||||
|
||||
| const | class | id | party (`data/trainers/parties.asm` label) | script label | rematch/phone |
|
||||
|---|---|---|---|---|---|
|
||||
| `HIKER`, `DANIEL` | `HIKER` | `DANIEL` | `"DANIEL@"` | `TrainerHikerDaniel` | phone service disabled on this map (`TRUE` in the header) |
|
||||
| `HIKER`, `RUSSELL` | `HIKER` | `RUSSELL` | `"RUSSELL@"` | `TrainerHikerRussell` | as above |
|
||||
| `POKEMANIAC`, `LARRY` | `POKEMANIAC` | `LARRY` | `"LARRY@"` | `TrainerPokemaniacLarry` | as above |
|
||||
| `FIREBREATHER`, `BILL` | `FIREBREATHER` | `BILL` | `"BILL@"` | `TrainerFirebreatherBill` | as above |
|
||||
| `FIREBREATHER`, `RAY` | `FIREBREATHER` | `RAY` | `"RAY@"` | `TrainerFirebreatherRay` | as above |
|
||||
|
||||
(Party rows for these five were not transcribed - they belong to section 03 and
|
||||
the walkthrough tells the bot to walk past them, not fight them.)
|
||||
|
||||
**Wild encounters**
|
||||
|
||||
Grass/cave, `data/wild/johto_grass.asm:444` `def_grass_wildmons UNION_CAVE_1F`,
|
||||
rate `6 percent` for morn/day/nite alike. Gold list (identical across all three
|
||||
time slots):
|
||||
|
||||
| slot | level | species |
|
||||
|---|---|---|
|
||||
| 1 | 6 | `GEODUDE` |
|
||||
| 2 | 6 | `SANDSHREW` (Silver: `RATTATA`) |
|
||||
| 3 | 5 | `ZUBAT` |
|
||||
| 4 | 4 | `RATTATA` |
|
||||
| 5 | 7 | `ZUBAT` |
|
||||
| 6 | 6 | `ONIX` |
|
||||
| 7 | 6 | `ONIX` |
|
||||
|
||||
Water, `data/wild/johto_water.asm:12` `def_water_wildmons UNION_CAVE_1F`, rate
|
||||
`2 percent`: `15 WOOPER`, `20 QUAGSIRE`, `15 QUAGSIRE`.
|
||||
|
||||
Fishing group is `FISHGROUP_LAKE` (header row).
|
||||
|
||||
### MAP_UNION_CAVE_B1F
|
||||
|
||||
- Script: `maps/UnionCaveB1F.asm`
|
||||
- Blocks: `maps/UnionCaveB1F.blk`
|
||||
- Header: `data/maps/maps.asm:108` -> `map UnionCaveB1F, TILESET_CAVE, CAVE, LANDMARK_UNION_CAVE, MUSIC_UNION_CAVE, TRUE, PALETTE_NITE, FISHGROUP_LAKE`
|
||||
- Dimensions: `constants/map_constants.asm:95` -> `map_const UNION_CAVE_B1F, 10, 18`
|
||||
- Attributes: `data/maps/attributes.asm:427` -> `map_attributes UnionCaveB1F, UNION_CAVE_B1F, $09`
|
||||
- Connections: none
|
||||
- Symbols: `44:5415 UnionCaveB1F_MapScripts`, `44:56df UnionCaveB1F_MapEvents` (`pokegold.sym`)
|
||||
|
||||
**Warps** (`def_warp_events`)
|
||||
|
||||
| idx | x | y | destination map | dest warp |
|
||||
|---|---|---|---|---|
|
||||
| 1 | 3 | 3 | `RUINS_OF_ALPH_OUTSIDE` | 7 |
|
||||
| 2 | 3 | 11 | `RUINS_OF_ALPH_OUTSIDE` | 8 |
|
||||
| 3 | 7 | 19 | `UNION_CAVE_1F` | 1 |
|
||||
| 4 | 3 | 33 | `UNION_CAVE_1F` | 2 |
|
||||
| 5 | 17 | 31 | `UNION_CAVE_B2F` | 1 |
|
||||
|
||||
**Coord events** (`def_coord_events`)
|
||||
|
||||
None.
|
||||
|
||||
**BG events** (`def_bg_events`)
|
||||
|
||||
None.
|
||||
|
||||
**Object events** (`def_object_events`)
|
||||
|
||||
| const | sprite | x | y | movement | type | script label | event flag |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| `UNIONCAVEB1F_POKEFAN_M1` | `SPRITE_POKEFAN_M` | 10 | 4 | `SPRITEMOVEDATA_SPINRANDOM_FAST` | `OBJECTTYPE_TRAINER`, sight 1 | `TrainerHikerPhillip` | -1 |
|
||||
| `UNIONCAVEB1F_POKEFAN_M2` | `SPRITE_POKEFAN_M` | 17 | 10 | `SPRITEMOVEDATA_STANDING_DOWN` | `OBJECTTYPE_TRAINER`, sight 3 | `TrainerHikerLeonard` | -1 |
|
||||
| `UNIONCAVEB1F_SUPER_NERD1` | `SPRITE_SUPER_NERD` | 5 | 32 | `SPRITEMOVEDATA_STANDING_LEFT` | `OBJECTTYPE_TRAINER`, sight 3 | `TrainerPokemaniacAndrew` | -1 |
|
||||
| `UNIONCAVEB1F_SUPER_NERD2` | `SPRITE_SUPER_NERD` | 17 | 30 | `SPRITEMOVEDATA_STANDING_LEFT` | `OBJECTTYPE_TRAINER`, sight 3 | `TrainerPokemaniacCalvin` | -1 |
|
||||
| `UNIONCAVEB1F_POKE_BALL1` | `SPRITE_POKE_BALL` | 2 | 16 | `SPRITEMOVEDATA_STILL` | `OBJECTTYPE_ITEMBALL` | `UnionCaveB1FTMSwift` | `EVENT_UNION_CAVE_B1F_TM_SWIFT` |
|
||||
| `UNIONCAVEB1F_BOULDER` | `SPRITE_BOULDER` | 7 | 10 | `SPRITEMOVEDATA_STRENGTH_BOULDER` | `OBJECTTYPE_SCRIPT` | `UnionCaveB1FBoulder` | -1 |
|
||||
| `UNIONCAVEB1F_POKE_BALL2` | `SPRITE_POKE_BALL` | 17 | 23 | `SPRITEMOVEDATA_STILL` | `OBJECTTYPE_ITEMBALL` | `UnionCaveB1FXDefend` | `EVENT_UNION_CAVE_B1F_X_DEFEND` |
|
||||
|
||||
**Scripts of interest**
|
||||
|
||||
- `UnionCaveB1F_MapScripts` - both `def_scene_scripts` and `def_callbacks` are
|
||||
empty. Nothing on B1F is gated.
|
||||
- `TrainerPokemaniacAndrew` (`44:5417`) -
|
||||
`trainer POKEMANIAC, ANDREW, EVENT_BEAT_POKEMANIAC_ANDREW, PokemaniacAndrewSeenText, PokemaniacAndrewBeatenText, 0, .Script`.
|
||||
`.Script` is the boilerplate after-battle path:
|
||||
`endifjustbattled / opentext / writetext PokemaniacAndrewAfterBattleText / waitbutton / closetext / end`.
|
||||
No flags beyond the beaten flag, no items, no warps.
|
||||
- `TrainerPokemaniacCalvin` (`44:542b`) - identical shape with
|
||||
`EVENT_BEAT_POKEMANIAC_CALVIN` and `PokemaniacCalvinAfterBattleText`
|
||||
("You demonstrated on me!" is `PokemaniacCalvinBeatenText`, matching the
|
||||
walkthrough quote).
|
||||
- `UnionCaveB1FBoulder` - `jumpstd StrengthBoulderScript`. Not on the
|
||||
walkthrough's path to Lapras (it sits at `7, 10`, up near the Ruins of Alph
|
||||
ladders), but it is the one Strength-gated object on the floor.
|
||||
|
||||
**Flags and events**
|
||||
|
||||
| constant | defined in | read/written by | meaning for a bot |
|
||||
|---|---|---|---|
|
||||
| `EVENT_BEAT_POKEMANIAC_ANDREW` | `constants/event_flags.asm:751` | `TrainerPokemaniacAndrew` | set after the win; suppresses the eyesight trigger |
|
||||
| `EVENT_BEAT_POKEMANIAC_CALVIN` | `constants/event_flags.asm:752` | `TrainerPokemaniacCalvin` | same |
|
||||
| `EVENT_BEAT_HIKER_PHILLIP`, `EVENT_BEAT_HIKER_LEONARD` | `constants/event_flags.asm` | the two Hiker `trainer` rows | not on the Lapras path |
|
||||
| `EVENT_UNION_CAVE_B1F_TM_SWIFT` | `constants/event_flags.asm` | itemball object row | one-time TM_SWIFT |
|
||||
| `EVENT_UNION_CAVE_B1F_X_DEFEND` | `constants/event_flags.asm` | itemball object row | one-time X_DEFEND |
|
||||
|
||||
**Items**
|
||||
|
||||
| item | how obtained | source (label / bg_event / hidden) | one-time flag |
|
||||
|---|---|---|---|
|
||||
| `TM_SWIFT` | item ball at 2,16 | `UnionCaveB1FTMSwift` | `EVENT_UNION_CAVE_B1F_TM_SWIFT` |
|
||||
| `X_DEFEND` | item ball at 17,23 | `UnionCaveB1FXDefend` | `EVENT_UNION_CAVE_B1F_X_DEFEND` |
|
||||
|
||||
Neither is mentioned by the walkthrough; both are on the way to the B2F ladder.
|
||||
|
||||
**Trainers**
|
||||
|
||||
| const | class | id | party (`data/trainers/parties.asm` label) | script label | rematch/phone |
|
||||
|---|---|---|---|---|---|
|
||||
| `POKEMANIAC`, `ANDREW` | `POKEMANIAC` (`$1e`, `constants/trainer_constants.asm:245`) | `ANDREW` (id 2) | `"ANDREW@", TRAINERTYPE_NORMAL` at `data/trainers/parties.asm:1086`: L24 `MAROWAK`, L24 `MAROWAK` | `TrainerPokemaniacAndrew` | no phone (map header phone flag `TRUE`) |
|
||||
| `POKEMANIAC`, `CALVIN` | `POKEMANIAC` | `CALVIN` (id 3) | `"CALVIN@", TRAINERTYPE_NORMAL` at `data/trainers/parties.asm:1092`: L26 `KANGASKHAN` | `TrainerPokemaniacCalvin` | no phone |
|
||||
| `HIKER`, `PHILLIP` | `HIKER` | `PHILLIP` | `"PHILLIP@"` | `TrainerHikerPhillip` | off-path |
|
||||
| `HIKER`, `LEONARD` | `HIKER` | `LEONARD` | `"LEONARD@"` | `TrainerHikerLeonard` | off-path |
|
||||
|
||||
`TRAINERTYPE_NORMAL` means level+species only: no custom moves, no held items.
|
||||
|
||||
Prize money: `POKEMANIAC` base reward is `db 15` (`data/trainers/attributes.asm:179`
|
||||
`; Pokemaniac`). `ComputeTrainerReward` (`engine/battle/read_trainer_party.asm:300`)
|
||||
computes `base * wCurPartyLevel` where the level is the **last** party row, and
|
||||
`WinTrainerBattle` (`engine/battle/core.asm:2292`) pays that out four times
|
||||
(`ld c, 4` at the `.okay` loop). So Andrew = 15*24*4 = **1440**, Calvin =
|
||||
15*26*4 = **1560** - both match the walkthrough exactly.
|
||||
|
||||
**Wild encounters**
|
||||
|
||||
Grass/cave, `data/wild/johto_grass.asm:499` `def_grass_wildmons UNION_CAVE_B1F`,
|
||||
rate `6 percent` morn/day/nite. Gold list (same in all three slots):
|
||||
|
||||
| slot | level | species |
|
||||
|---|---|---|
|
||||
| 1 | 8 | `GEODUDE` |
|
||||
| 2 | 8 | `SANDSHREW` (Silver: `RATTATA`) |
|
||||
| 3 | 7 | `ZUBAT` |
|
||||
| 4 | 8 | `ONIX` |
|
||||
| 5 | 9 | `ZUBAT` |
|
||||
| 6 | 6 | `RATTATA` |
|
||||
| 7 | 6 | `RATTATA` |
|
||||
|
||||
Water, `data/wild/johto_water.asm:19`, rate `2 percent`: `15 WOOPER`,
|
||||
`20 QUAGSIRE`, `15 QUAGSIRE`.
|
||||
|
||||
### MAP_UNION_CAVE_B2F
|
||||
|
||||
This is the section's real content.
|
||||
|
||||
- Script: `maps/UnionCaveB2F.asm`
|
||||
- Blocks: `maps/UnionCaveB2F.blk` (`2b:466d UnionCaveB2F_Blocks`)
|
||||
- Header: `data/maps/maps.asm:109` -> `map UnionCaveB2F, TILESET_CAVE, CAVE, LANDMARK_UNION_CAVE, MUSIC_UNION_CAVE, TRUE, PALETTE_NITE, FISHGROUP_SHORE`
|
||||
(note: **`FISHGROUP_SHORE` here, not `FISHGROUP_LAKE` like the two floors above**)
|
||||
- Dimensions: `constants/map_constants.asm:96` -> `map_const UNION_CAVE_B2F, 10, 18` (20x36 walk cells)
|
||||
- Attributes: `data/maps/attributes.asm:428` -> `map_attributes UnionCaveB2F, UNION_CAVE_B2F, $09`
|
||||
- Connections: none
|
||||
- Symbols: `44:5759 UnionCaveB2F_MapScripts`, `44:575e UnionCaveB2FLaprasCallback`, `44:5770 UnionCaveLapras`, `44:59cb UnionCaveB2F_MapEvents`, `25:595d UnionCaveB2F_MapAttributes`
|
||||
|
||||
**Warps** (`def_warp_events`)
|
||||
|
||||
| idx | x | y | destination map | dest warp |
|
||||
|---|---|---|---|---|
|
||||
| 1 | 5 | 3 | `UNION_CAVE_B1F` | 5 |
|
||||
|
||||
One warp. There is no second exit: leaving is either back up this ladder, or
|
||||
Escape Rope / Dig (the walkthrough's suggestion).
|
||||
|
||||
**Coord events** (`def_coord_events`)
|
||||
|
||||
None - the block is empty. Nothing on B2F is a trip-wire; every event is an
|
||||
object you walk into the sight line of or press A on.
|
||||
|
||||
**BG events** (`def_bg_events`)
|
||||
|
||||
None.
|
||||
|
||||
**Object events** (`def_object_events`)
|
||||
|
||||
| const | sprite | x | y | movement | type | script label | event flag |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| `UNIONCAVEB2F_ROCKER` | `SPRITE_ROCKER` | 17 | 23 | `SPRITEMOVEDATA_STANDING_LEFT` | `OBJECTTYPE_TRAINER`, sight 5, `PAL_NPC_RED` | `TrainerCooltrainermNick` | -1 |
|
||||
| `UNIONCAVEB2F_COOLTRAINER_F1` | `SPRITE_COOLTRAINER_F` | 5 | 13 | `SPRITEMOVEDATA_SPINRANDOM_FAST` | `OBJECTTYPE_TRAINER`, sight 1, `PAL_NPC_RED` | `TrainerCooltrainerfGwen` | -1 |
|
||||
| `UNIONCAVEB2F_COOLTRAINER_F2` | `SPRITE_COOLTRAINER_F` | 3 | 28 | `SPRITEMOVEDATA_STANDING_DOWN` | `OBJECTTYPE_TRAINER`, sight 2, `PAL_NPC_RED` | `TrainerCooltrainerfEmma` | -1 |
|
||||
| `UNIONCAVEB2F_POKE_BALL1` | `SPRITE_POKE_BALL` | 16 | 2 | `SPRITEMOVEDATA_STILL` | `OBJECTTYPE_ITEMBALL` | `UnionCaveB2FElixer` | `EVENT_UNION_CAVE_B2F_ELIXER` |
|
||||
| `UNIONCAVEB2F_POKE_BALL2` | `SPRITE_POKE_BALL` | 12 | 19 | `SPRITEMOVEDATA_STILL` | `OBJECTTYPE_ITEMBALL` | `UnionCaveB2FHyperPotion` | `EVENT_UNION_CAVE_B2F_HYPER_POTION` |
|
||||
| `UNIONCAVEB2F_LAPRAS` | `SPRITE_SURF` | 11 | 31 | `SPRITEMOVEDATA_SWIM_WANDER`, radius x=1 y=1, `PAL_NPC_BLUE` | `OBJECTTYPE_SCRIPT` | `UnionCaveLapras` | `EVENT_UNION_CAVE_B2F_LAPRAS` |
|
||||
|
||||
Notes a bot needs:
|
||||
|
||||
- Lapras uses `SPRITE_SURF` (`constants/sprite_constants.asm:87`, id 53) - the
|
||||
same overworld sprite the player rides. It is not a Lapras sprite.
|
||||
- `SPRITEMOVEDATA_SWIM_WANDER` with radius 1,1 means the object **drifts within
|
||||
one cell of `11, 31`**. A driver must not hard-code an exact tile to face; it
|
||||
should walk to the neighbourhood and press A at whichever adjacent water cell
|
||||
the sprite is occupying.
|
||||
- Its event flag is `EVENT_UNION_CAVE_B2F_LAPRAS`. `CheckObjectFlag`
|
||||
(`engine/overworld/map_objects_2.asm:31`) masks an object when the flag is
|
||||
**set**, so "Lapras present" = flag clear.
|
||||
|
||||
**Scripts of interest**
|
||||
|
||||
`UnionCaveB2F_MapScripts` (`44:5759`)
|
||||
|
||||
```
|
||||
def_scene_scripts ; empty
|
||||
def_callbacks
|
||||
callback MAPCALLBACK_OBJECTS, UnionCaveB2FLaprasCallback
|
||||
```
|
||||
|
||||
`UnionCaveB2FLaprasCallback` (`44:575e`) - the whole Friday rule, six opcodes:
|
||||
|
||||
```
|
||||
checkflag ENGINE_UNION_CAVE_LAPRAS
|
||||
iftrue .NoAppear
|
||||
readvar VAR_WEEKDAY
|
||||
ifequal FRIDAY, .Appear
|
||||
.NoAppear:
|
||||
disappear UNIONCAVEB2F_LAPRAS
|
||||
endcallback
|
||||
.Appear:
|
||||
appear UNIONCAVEB2F_LAPRAS
|
||||
endcallback
|
||||
```
|
||||
|
||||
Control flow, spelled out:
|
||||
|
||||
1. `checkflag ENGINE_UNION_CAVE_LAPRAS` - a **daily** engine flag
|
||||
(`constants/engine_flags.asm:107`, in the `wDailyFlags2` block; bit
|
||||
`DAILYFLAGS2_UNION_CAVE_LAPRAS_F` = 1, `constants/ram_constants.asm:315`).
|
||||
Set means "already caught/fought it today" -> `.NoAppear`.
|
||||
2. `readvar VAR_WEEKDAY` (`constants/script_constants.asm:59`, id `$0b`) then
|
||||
`ifequal FRIDAY` (`constants/ram_constants.asm:218`, `FRIDAY` = 5, Sunday-based).
|
||||
Only Friday reaches `.Appear`.
|
||||
3. `appear` clears the object's event flag (`Script_appear`,
|
||||
`engine/overworld/scripting.asm:879`, `ld b, 0 ; clear`); `disappear` sets it
|
||||
(`Script_disappear`, `:887`, `ld b, 1 ; set`).
|
||||
|
||||
Because the callback is `MAPCALLBACK_OBJECTS`, it re-runs on **every map load**
|
||||
of B2F (`engine/overworld/map_setup.asm:81` `farcall LoadObjectMasks` follows
|
||||
it). So a bot that walks out and back in on a non-Friday will find Lapras gone.
|
||||
|
||||
`UnionCaveLapras` (`44:5770`) - the encounter itself:
|
||||
|
||||
```
|
||||
faceplayer
|
||||
cry LAPRAS
|
||||
loadwildmon LAPRAS, 20
|
||||
startbattle
|
||||
disappear UNIONCAVEB2F_LAPRAS
|
||||
setflag ENGINE_UNION_CAVE_LAPRAS
|
||||
reloadmapafterbattle
|
||||
end
|
||||
```
|
||||
|
||||
Load-bearing details:
|
||||
|
||||
- `loadwildmon LAPRAS, 20` - a level **20 wild** Lapras, matching the
|
||||
walkthrough. There is no `writevar VAR_BATTLETYPE` here, so unlike Ho-Oh /
|
||||
Lugia / the Red Gyarados this is an **ordinary wild battle**: no forced item,
|
||||
no forced shiny, no `BATTLETYPE_CANLOSE` mercy.
|
||||
- `disappear` and `setflag` run **unconditionally after the battle**, with no
|
||||
`iffalse` / `checkflag` guard on the outcome. So Lapras is consumed for the
|
||||
day whether you caught it, KO'd it, ran, or blacked out mid-battle. That is
|
||||
exactly why the walkthrough says to save first - a soft reset is the only
|
||||
recovery.
|
||||
- `setflag ENGINE_UNION_CAVE_LAPRAS` is the daily flag, not a permanent event.
|
||||
`CheckDailyResetTimer` (`engine/overworld/time.asm:88`) zeroes `wDailyFlags1`
|
||||
and `wDailyFlags2` whole once a day has elapsed, so it comes back next Friday.
|
||||
|
||||
`TrainerCooltrainermNick` (`44:577f`), `TrainerCooltrainerfGwen` (`44:5793`),
|
||||
`TrainerCooltrainerfEmma` (`44:57a7`) - all three are the plain
|
||||
`trainer <class>, <id>, EVENT_BEAT_*, <seen>, <beaten>, 0, .Script` shape with
|
||||
an `endifjustbattled / opentext / writetext ...AfterBattleText / waitbutton /
|
||||
closetext / end` follow-up. No items, no warps, no flags beyond their own
|
||||
`EVENT_BEAT_*`.
|
||||
|
||||
`CooltrainerfEmmaAfterBattleText` is the in-map hint for the whole section:
|
||||
*"Just once a week, a #MON comes to the water's edge. / I wanted to see that
|
||||
#MON..."*
|
||||
|
||||
`UnionCaveB2FElixer` = `itemball ELIXER`; `UnionCaveB2FHyperPotion` =
|
||||
`itemball HYPER_POTION` (`macros/scripts/maps.asm:155`, two raw bytes
|
||||
`db item, quantity`, not bytecode).
|
||||
|
||||
**Flags and events**
|
||||
|
||||
| constant | defined in | read/written by | meaning for a bot |
|
||||
|---|---|---|---|
|
||||
| `ENGINE_UNION_CAVE_LAPRAS` | `constants/engine_flags.asm:107` (backed by `data/events/engine_flags.asm:117` -> `wDailyFlags2` bit `DAILYFLAGS2_UNION_CAVE_LAPRAS_F`) | read by `UnionCaveB2FLaprasCallback`, set by `UnionCaveLapras` | "Lapras already used up today". Cleared wholesale by `CheckDailyResetTimer` (`engine/overworld/time.asm:88-97`) |
|
||||
| `EVENT_UNION_CAVE_B2F_LAPRAS` | `constants/event_flags.asm:1282` | written by the callback's `appear`/`disappear` | object visibility mask. **Clear = visible** (`engine/overworld/map_objects_2.asm:31` `CheckObjectFlag`) |
|
||||
| `VAR_WEEKDAY` | `constants/script_constants.asm:59` (`$0b`) | `readvar` in the callback | `wCurDay`, 0 = `SUNDAY` .. 6 = `SATURDAY`; `FRIDAY` = 5 (`constants/ram_constants.asm:218`) |
|
||||
| `EVENT_BEAT_COOLTRAINERM_NICK` | `constants/event_flags.asm:857` | `TrainerCooltrainermNick` | beaten flag |
|
||||
| `EVENT_BEAT_COOLTRAINERF_GWEN` | `constants/event_flags.asm:877` | `TrainerCooltrainerfGwen` | beaten flag |
|
||||
| `EVENT_BEAT_COOLTRAINERF_EMMA` | `constants/event_flags.asm:891` | `TrainerCooltrainerfEmma` | beaten flag |
|
||||
| `EVENT_UNION_CAVE_B2F_ELIXER` | `constants/event_flags.asm:1025` | itemball object row | one-time |
|
||||
| `EVENT_UNION_CAVE_B2F_HYPER_POTION` | `constants/event_flags.asm:1026` | itemball object row | one-time |
|
||||
|
||||
There are no `SCENE_*` values on this map: `def_scene_scripts` is empty, so
|
||||
`wMapScene` is never consulted here.
|
||||
|
||||
**Items**
|
||||
|
||||
| item | how obtained | source (label / bg_event / hidden) | one-time flag |
|
||||
|---|---|---|---|
|
||||
| `ELIXER` | item ball at 16,2 | `UnionCaveB2FElixer` (`itemball ELIXER`) | `EVENT_UNION_CAVE_B2F_ELIXER` |
|
||||
| `HYPER_POTION` | item ball at 12,19 | `UnionCaveB2FHyperPotion` (`itemball HYPER_POTION`) | `EVENT_UNION_CAVE_B2F_HYPER_POTION` |
|
||||
|
||||
The walkthrough names only the Hyper Potion ("Land on the right side to grab a
|
||||
Hyper Potion"). The Elixer at `16, 2` sits near the arrival ladder at `5, 3` and
|
||||
is never mentioned - free pickup for a bot on the way in.
|
||||
|
||||
**Trainers**
|
||||
|
||||
| const | class | id | party (`data/trainers/parties.asm` label) | script label | rematch/phone |
|
||||
|---|---|---|---|---|---|
|
||||
| `COOLTRAINERM`, `NICK` | `COOLTRAINERM` (`$1b`, `constants/trainer_constants.asm:184`) | `NICK` (id 1) | `"NICK@", TRAINERTYPE_MOVES` at `data/trainers/parties.asm:742` | `TrainerCooltrainermNick` | no phone (map phone flag `TRUE`) |
|
||||
| `COOLTRAINERF`, `GWEN` | `COOLTRAINERF` (`$1c`, `:205`) | `GWEN` (id 1) | `"GWEN@", TRAINERTYPE_NORMAL` at `:867` | `TrainerCooltrainerfGwen` | no phone |
|
||||
| `COOLTRAINERF`, `EMMA` | `COOLTRAINERF` | `EMMA` (id 15) | `"EMMA@", TRAINERTYPE_NORMAL` at `:954` | `TrainerCooltrainerfEmma` | no phone |
|
||||
|
||||
Parties verbatim:
|
||||
|
||||
`NICK` (`TRAINERTYPE_MOVES` - custom movesets, no items):
|
||||
|
||||
| # | level | species | moves |
|
||||
|---|---|---|---|
|
||||
| 1 | 26 | `CHARMANDER` | `EMBER`, `SMOKESCREEN`, `RAGE`, `SCARY_FACE` |
|
||||
| 2 | 26 | `SQUIRTLE` | `WITHDRAW`, `WATER_GUN`, `BITE`, `CURSE` |
|
||||
| 3 | 26 | `BULBASAUR` | `LEECH_SEED`, `POISONPOWDER`, `SLEEP_POWDER`, `RAZOR_LEAF` |
|
||||
|
||||
`GWEN` (`TRAINERTYPE_NORMAL`):
|
||||
|
||||
| # | level | species |
|
||||
|---|---|---|
|
||||
| 1 | 26 | `EEVEE` |
|
||||
| 2 | 22 | `FLAREON` |
|
||||
| 3 | 22 | `VAPOREON` |
|
||||
| 4 | 22 | `JOLTEON` |
|
||||
|
||||
`EMMA` (`TRAINERTYPE_NORMAL`): L28 `POLIWHIRL`, single mon.
|
||||
|
||||
Prize money (`base * last-party-level * 4`, see the B1F note): `COOLTRAINERM`
|
||||
and `COOLTRAINERF` both have `db 12 ; base reward`
|
||||
(`data/trainers/attributes.asm:161` `; Cooltrainerm`, `:167` `; Cooltrainerf`).
|
||||
Nick = 12*26*4 = **1248**, Gwen = 12*22*4 = **1056** (last row is the L22
|
||||
Jolteon), Emma = 12*28*4 = **1344**. All three match the walkthrough.
|
||||
|
||||
AI: both Cooltrainer classes carry the full
|
||||
`AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY`
|
||||
set and `SWITCH_SOMETIMES`; Pokemaniac is the weaker
|
||||
`AI_BASIC | AI_SETUP | AI_OFFENSIVE | AI_AGGRESSIVE | AI_STATUS`. None of the
|
||||
five hold items (`db NO_ITEM, NO_ITEM`).
|
||||
|
||||
**Wild encounters**
|
||||
|
||||
Grass/cave, `data/wild/johto_grass.asm:554` `def_grass_wildmons UNION_CAVE_B2F`,
|
||||
rate `4 percent`. This table is **not** split by version, and all three
|
||||
time-of-day slots are identical:
|
||||
|
||||
| slot | level | species |
|
||||
|---|---|---|
|
||||
| 1 | 22 | `ZUBAT` |
|
||||
| 2 | 22 | `RATICATE` |
|
||||
| 3 | 22 | `GOLBAT` |
|
||||
| 4 | 21 | `GEODUDE` |
|
||||
| 5 | 20 | `RATTATA` |
|
||||
| 6 | 23 | `ONIX` |
|
||||
| 7 | 23 | `ONIX` |
|
||||
|
||||
Water (the surf legs the walkthrough spends most of its time on),
|
||||
`data/wild/johto_water.asm:26` `def_water_wildmons UNION_CAVE_B2F`, rate
|
||||
`4 percent` (double the 1F/B1F water rate):
|
||||
|
||||
| slot | level | species |
|
||||
|---|---|---|
|
||||
| 1 | 15 | `TENTACOOL` |
|
||||
| 2 | 20 | `QUAGSIRE` |
|
||||
| 3 | 20 | `TENTACRUEL` |
|
||||
|
||||
Fishing group is `FISHGROUP_SHORE` (`data/maps/maps.asm:109`). No headbutt or
|
||||
rock-smash tables apply - it is a cave with no trees.
|
||||
|
||||
The walkthrough's Max Repel advice: `REPEL_STEPS` in the port mirrors
|
||||
`RepelEffect / SuperRepelEffect / MaxRepelEffect`; MAX_REPEL is 250 steps. With
|
||||
the B2F encounters capped at level 23 and the lead mon typically above that,
|
||||
Repel suppresses essentially all of the grass rolls.
|
||||
|
||||
## 3. Blockers and gates
|
||||
|
||||
| Gate | Enforced by (file:label) | Requirement | Unlock condition |
|
||||
|---|---|---|---|
|
||||
| Cannot cross any of the water on 1F / B1F / B2F | `engine/events/overworld.asm:322` `SurfFunction.TrySurf` (`ld de, ENGINE_FOGBADGE / call CheckBadge / jr c, .nofogbadge`), and the walk-into-water path `TrySurfOW` at `:469` | **Fog Badge** plus a party mon that knows `SURF` | beat Morty in Ecruteak Gym (section 07) |
|
||||
| Lapras object is absent 6 days out of 7 | `maps/UnionCaveB2F.asm:15` `UnionCaveB2FLaprasCallback` -> `readvar VAR_WEEKDAY / ifequal FRIDAY` | in-game weekday must equal `FRIDAY` (`constants/ram_constants.asm:218`, value 5) | set the GB clock / wait for Friday. Re-checked on every B2F map load (`MAPCALLBACK_OBJECTS`) |
|
||||
| Lapras is absent again after one encounter | same callback, first two opcodes: `checkflag ENGINE_UNION_CAVE_LAPRAS / iftrue .NoAppear`; the flag is set by `UnionCaveLapras` | flag must be clear | `CheckDailyResetTimer` (`engine/overworld/time.asm:88`) zeroes `wDailyFlags1`+`wDailyFlags2` once a real day has passed |
|
||||
| Lapras is consumed even on a failed attempt | `UnionCaveLapras`: `startbattle` is followed unconditionally by `disappear` + `setflag` - no `iffalse` on `wBattleResult` | none - this is the failure mode, not a gate | save before pressing A; soft-reset on a bad outcome |
|
||||
| Only one way out of B2F | `maps/UnionCaveB2F.asm` `def_warp_events` has exactly one row (`5, 3 -> UNION_CAVE_B1F, 5`) | - | Escape Rope / Dig short-circuits the return walk (B2F's tileset is `TILESET_CAVE`) |
|
||||
| Boulder at B1F `7, 10` | `UnionCaveB1FBoulder` -> `jumpstd StrengthBoulderScript` | Strength / Plain Badge | not on the Lapras path; noted for completeness |
|
||||
|
||||
Nothing in this section checks a badge count, a key item, or an `EVENT_*`
|
||||
story flag. The only two real gates are **Surf** and **Friday**.
|
||||
|
||||
## 4. Bot checklist
|
||||
|
||||
Preconditions for the whole section: Fog Badge, a party mon with `SURF`, the
|
||||
in-game weekday equal to `FRIDAY`, `ENGINE_UNION_CAVE_LAPRAS` clear, several
|
||||
Ultra Balls, a status move (Sleep/Paralysis) and a **save written immediately
|
||||
before the Lapras press**.
|
||||
|
||||
| # | Map | Target | Input intent | Precondition | Postcondition |
|
||||
|---|---|---|---|---|---|
|
||||
| 1 | `MAP_ROUTE_32` | warp at `6, 79` | walk onto the warp | on Route 32 | now in `MAP_UNION_CAVE_1F` at `17, 3` (warp 4) |
|
||||
| 2 | `MAP_UNION_CAVE_1F` | - | use `MAX_REPEL` from the pack | have one | 250 steps of encounter suppression |
|
||||
| 3 | `MAP_UNION_CAVE_1F` | avoid sight lines of `UNIONCAVE1F_POKEFAN_M2` (`15, 8`, sight 3, facing left) and `UNIONCAVE1F_FISHER2` (`15, 15`, sight 2, spinner) | walk south-west | - | `EVENT_BEAT_*` untouched if avoided; the spinner is not reliably avoidable, budget for the battle |
|
||||
| 4 | `MAP_UNION_CAVE_1F` | south-west water | press A facing water, or walk into it (`TrySurfOW`) | Fog Badge + `SURF` | `wPlayerState = PLAYER_SURF` |
|
||||
| 5 | `MAP_UNION_CAVE_1F` | warp 2 at `3, 33` | step onto it | - | now in `MAP_UNION_CAVE_B1F` at `3, 33` (warp 4) |
|
||||
| 6 | `MAP_UNION_CAVE_B1F` | `UNIONCAVEB1F_SUPER_NERD1` at `5, 32` (facing left, sight 3) | walk into the sight line / talk | - | battle Pokemaniac Andrew (2x L24 Marowak); sets `EVENT_BEAT_POKEMANIAC_ANDREW`, +1440 |
|
||||
| 7 | `MAP_UNION_CAVE_B1F` | surf east along the y~30 water | walk east on water | surfing | reach the eastern shelf |
|
||||
| 8 | `MAP_UNION_CAVE_B1F` | `UNIONCAVEB1F_SUPER_NERD2` at `17, 30` (facing left, sight 3) | walk into the sight line | - | battle Pokemaniac Calvin (L26 Kangaskhan); sets `EVENT_BEAT_POKEMANIAC_CALVIN`, +1560 |
|
||||
| 9 | `MAP_UNION_CAVE_B1F` | optional item ball `17, 23` | press A | `EVENT_UNION_CAVE_B1F_X_DEFEND` clear | `X_DEFEND` in bag, flag set |
|
||||
| 10 | `MAP_UNION_CAVE_B1F` | warp 5 at `17, 31` | step onto it | - | now in `MAP_UNION_CAVE_B2F` at `5, 3`; `MAPCALLBACK_OBJECTS` runs `UnionCaveB2FLaprasCallback` |
|
||||
| 11 | `MAP_UNION_CAVE_B2F` | **assert** `UNIONCAVEB2F_LAPRAS` is present | read the object list | weekday == FRIDAY and `ENGINE_UNION_CAVE_LAPRAS` clear | if absent, abort the run - nothing later will fix it |
|
||||
| 12 | `MAP_UNION_CAVE_B2F` | item ball `16, 2` | press A | `EVENT_UNION_CAVE_B2F_ELIXER` clear | `ELIXER` in bag (walkthrough omits this one) |
|
||||
| 13 | `MAP_UNION_CAVE_B2F` | surf south from the entrance shelf | walk south on water | surfing | approach the y~19-23 shelves |
|
||||
| 14 | `MAP_UNION_CAVE_B2F` | item ball `12, 19` (right-hand shore) | land, press A | `EVENT_UNION_CAVE_B2F_HYPER_POTION` clear | `HYPER_POTION` in bag |
|
||||
| 15 | `MAP_UNION_CAVE_B2F` | `UNIONCAVEB2F_ROCKER` at `17, 23` (facing left, sight 5 - the longest on the floor) | walk into the sight line | - | battle Cooltrainer Nick (L26 Charmander/Squirtle/Bulbasaur, custom moves); sets `EVENT_BEAT_COOLTRAINERM_NICK`, +1248 |
|
||||
| 16 | `MAP_UNION_CAVE_B2F` | cross to the left shore, head north to `UNIONCAVEB2F_COOLTRAINER_F1` at `5, 13` (spinner, sight 1) | walk adjacent | - | battle Cooltrainer Gwen (L26 Eevee, L22 Flareon/Vaporeon/Jolteon); sets `EVENT_BEAT_COOLTRAINERF_GWEN`, +1056 |
|
||||
| 17 | `MAP_UNION_CAVE_B2F` | walk the left path south to `UNIONCAVEB2F_COOLTRAINER_F2` at `3, 28` (facing down, sight 2) | walk into the sight line | - | battle Cooltrainer Emma (L28 Poliwhirl); sets `EVENT_BEAT_COOLTRAINERF_EMMA`, +1344 |
|
||||
| 18 | `MAP_UNION_CAVE_B2F` | **save** | open menu -> SAVE | Lapras still present | recoverable checkpoint |
|
||||
| 19 | `MAP_UNION_CAVE_B2F` | surf east/south-east from `3, 28` toward `11, 31` | walk on water | surfing | the Lapras object drifts within +/-1 cell of `11, 31` (`SWIM_WANDER`, radius 1,1) - re-scan its live position each step |
|
||||
| 20 | `MAP_UNION_CAVE_B2F` | press A facing `UNIONCAVEB2F_LAPRAS` | talk | object present | `UnionCaveLapras` runs: `cry LAPRAS`, `loadwildmon LAPRAS, 20`, `startbattle` |
|
||||
| 21 | battle | L20 wild Lapras | inflict sleep/paralysis, chip to yellow/red, throw `ULTRA_BALL`s | - | on any exit: `disappear` + `setflag ENGINE_UNION_CAVE_LAPRAS`, then `reloadmapafterbattle` |
|
||||
| 22 | - | verify Lapras in party/box | read party | - | if not caught, **reload the step-18 save**; do not walk out and back in, the callback will not re-place it |
|
||||
| 23 | `MAP_UNION_CAVE_B2F` | use `ESCAPE_ROPE` | pack -> use | in a cave tileset | back at the last Pokemon Center door |
|
||||
|
||||
Sight-line arithmetic for steps 6/8/15/16/17: `Trainers.sees` in the port
|
||||
(`src/world/gen2/Trainers.lua:98`) is the transcription of `home/trainers.asm` -
|
||||
the NPC must be facing the player's direction and the Chebyshev-free straight
|
||||
distance must be `<= sight`.
|
||||
|
||||
## 5. Port coverage
|
||||
|
||||
| Beat | Port file | Status |
|
||||
|---|---|---|
|
||||
| Union Cave 1F/B1F/B2F map data (headers, blocks, warps, objects) | `src/import/RomExtractorGen2.lua` (generic map/manifest extraction; per-map entries land in the private `data/generated/` cache) | implemented - data-driven, no per-map code |
|
||||
| `MAPCALLBACK_OBJECTS` dispatch on map load | `src/world/gen2/World.lua:5700` (`self:runMapCallback("MAPCALLBACK_OBJECTS")`, deliberately *before* the sprite rebuild) | implemented |
|
||||
| `readvar VAR_WEEKDAY` | `src/world/gen2/World.lua:101` (`VAR_WEEKDAY = 0x0b`) and `:1222` (`if varId == VAR_WEEKDAY then return self:weekday() end`) | implemented |
|
||||
| `checkflag` / `setflag` on `ENGINE_*` | `src/script/gen2/Vm.lua:195` / `:208` | implemented |
|
||||
| `ENGINE_UNION_CAVE_LAPRAS` as a daily flag | `src/core/gen2/Apricorns.lua:84` (`{ id = 88, name = "ENGINE_UNION_CAVE_LAPRAS" }` in the wDailyFlags1/2 id list) and `:344` (`save.dailyFlags = {}` on the daily wipe) | implemented |
|
||||
| `appear` / `disappear` object masking | `src/script/gen2/Opcodes.lua:115-116`, consumed by the World object rebuild | implemented |
|
||||
| `cry` | `src/script/gen2/Vm.lua:621` | implemented |
|
||||
| `loadwildmon` + `startbattle` + `reloadmapafterbattle` (the whole static-encounter shape) | `src/script/gen2/Vm.lua:836`, `:817`, `:886` | implemented |
|
||||
| Wild catch rate / Ultra Ball / status bonus | `src/battle/gen2/Catching.lua` (transcribes `PokeBallEffect`, including the two cart bugs) | implemented |
|
||||
| Water encounter tables while surfing | `src/world/gen2/FieldMoves.lua:175` ("standing in a cave rolls the grass list; surfing rolls the water" ...) + `src/battle/gen2/Encounter.lua` | implemented |
|
||||
| Surf gate on `ENGINE_FOGBADGE` | `src/world/gen2/FieldMoves.lua:106` (`SURF = "FOG"`) and `FieldMoves.surfFromMenu` at `:481` | implemented |
|
||||
| Trainer eyesight approach + battle | `src/world/gen2/Trainers.lua:98` (`Trainers.sees`), `src/world/gen2/World.lua:5212` / `:5227` / `:5242` | implemented |
|
||||
| Trainer prize money (base x level x 4, Mom's split) | `src/battle/gen2/Prize.lua:82` (`Prize.reward`), `:92` (`Prize.rewardLevel`) | implemented - and it already documents the `ld c, 4` that makes the walkthrough's figures right |
|
||||
| Repel step counting (the Max Repel advice) | `src/world/gen2/World.lua:317` (`REPEL_STEPS = { REPEL = 100, SUPER_REPEL = 200, MAX_REPEL = 250 }`), `:3366` | implemented |
|
||||
| **Item ball pickup** (`OBJECTTYPE_ITEMBALL`, i.e. the B2F Elixer / Hyper Potion and every ball on 1F/B1F) | extractor stores the item at `src/import/RomExtractorGen2.lua:2969` (`obj.itemball = readItemBall(...)`) and deliberately assigns **no** `scriptKey`; `World:talk` only dispatches on `npc.def.scriptKey` (`src/world/gen2/World.lua:5276`) | **missing** - no consumer of `obj.itemball` exists anywhere under `src/`. Pressing A on a Poke Ball object currently does nothing |
|
||||
| Escape Rope from a cave | `src/ui/PartyMenu.lua:634` is the Gen 1 DIG/ESCAPE_ROPE path; no Gen 2 equivalent found under `src/core/gen2/` or `src/world/gen2/` | **unverified / likely missing for Gen 2** - a driver should warp out manually |
|
||||
| A driver exercising the weekday-gated `MAPCALLBACK_OBJECTS` shape | `tests/drivers/gold_map_callbacks.lua:117-156` covers the same pattern on Route 29 (Tuscany, Tuesday) | implemented for the pattern, **not** for Union Cave B2F specifically - no `tests/drivers/gold_*` driver mentions Union Cave or Lapras |
|
||||
|
||||
Honest summary: everything the Lapras encounter itself needs is in place
|
||||
(callback dispatch, weekday var, daily flags, `loadwildmon`/`startbattle`,
|
||||
catching). The two gaps that would bite a bot on this route are **item balls
|
||||
are not pickable** and **no Gen 2 Escape Rope**, neither of which blocks Lapras.
|
||||
|
||||
## 6. Unresolved / verify by hand
|
||||
|
||||
- **"Lapras only appears on Friday" vs. the 1F NPC hints.** The code checks
|
||||
exactly `FRIDAY` (`UnionCaveB2FLaprasCallback`). But
|
||||
`FirebreatherBillAfterBattleText` on 1F says *"On weekends, you can hear
|
||||
strange roars from deep in the cave"* while `PokemaniacLarryAfterBattleText`
|
||||
says *"Every Friday"*. Larry is right, Bill's line is flavour that contradicts
|
||||
the callback. The walkthrough follows Larry and is correct.
|
||||
- **"once you've gotten HM Surf".** The asm imposes no HM/badge check on the
|
||||
Lapras callback or on `UnionCaveLapras`; Surf is a *reachability* requirement
|
||||
(B2F's Lapras sits at `11, 31` surrounded by water and the approach legs on
|
||||
1F/B1F cross water), enforced only by `SurfFunction.TrySurf`'s
|
||||
`ENGINE_FOGBADGE` check. The walkthrough's phrasing implies a scripted gate
|
||||
that does not exist.
|
||||
- **EXP figures.** The walkthrough quotes per-mon EXP (637 for a L24 Marowak,
|
||||
975 for a L26 Kangaskhan, 355/511/924/928/933/786, and two "?" entries). EXP
|
||||
is computed at runtime from base EXP x level, not stored per trainer, so none
|
||||
of these could be pinned to a table row. They are plausible but unverified
|
||||
here; `data/pokemon/base_stats/*.asm` would be the place to check.
|
||||
- **Party ordering.** The walkthrough lists Nick as Charmander / Bulbasaur /
|
||||
Squirtle and Gwen as Eevee / Vaporeon / Jolteon / Flareon. `parties.asm` has
|
||||
Nick as Charmander / **Squirtle** / **Bulbasaur** (`:742`) and Gwen as Eevee /
|
||||
**Flareon** / **Vaporeon** / **Jolteon** (`:867`). The sets match; the order
|
||||
does not. A bot planning lead matchups should use the asm order (it is also
|
||||
what decides the prize level: Gwen's last row is Jolteon L22).
|
||||
- **"Head almost all the way through till you see the guy on the cliff still
|
||||
spinning around, which is the third trainer that you spot."** Read against the
|
||||
1F object table this is `UNIONCAVE1F_FISHER2` (Firebreather Bill,
|
||||
`15, 15`, `SPRITEMOVEDATA_SPINRANDOM_FAST`), with Hiker Daniel (`4, 4`) and
|
||||
Hiker Russell (`15, 8`) as the first two. That reading is an inference from
|
||||
coordinates and movement data, not something the asm states; the exact walking
|
||||
order depends on the `.blk` layout, which was not decoded here.
|
||||
- **Which 1F ladder the walkthrough means.** "Down and to the left is a lake.
|
||||
Surf past it and head down the ladder" is read as 1F warp 2 (`3, 33` ->
|
||||
`UNION_CAVE_B1F` warp 4 at `3, 33`), because Andrew stands at B1F `5, 32`,
|
||||
immediately beside that arrival point. 1F warp 1 (`5, 19` -> B1F warp 3 at
|
||||
`7, 19`) would land nowhere near him. Verify against the block layout if a
|
||||
driver's pathing disagrees.
|
||||
- **B2F "right side" / "left side" landings.** The prose distinguishes a right
|
||||
and a left shore; the object coordinates (`12, 19` Hyper Potion and `17, 23`
|
||||
Nick on the right, `5, 13` Gwen and `3, 28` Emma on the left) are consistent
|
||||
with that, but the actual water/land split comes from `UnionCaveB2F.blk`,
|
||||
which was not decoded.
|
||||
- **Elixer at B2F `16, 2`.** Present in the asm, absent from the walkthrough.
|
||||
Not a contradiction, just an omission worth picking up.
|
||||
Reference in New Issue
Block a user