Allow to fly to any map and from any map - pret/pokered GitHub Wiki
In vanilla, we can fly only from certain maps (the ones in the "open": cities, routes, but for example from the top of the Celadon Condominiums or from the top of the Celadon Store) and to certain maps (namely, only cities, even though Route 4 and Route 10 have Pokémon Centers). We can modify both of these behaviours.
Fly TO any map
Let's use Route 4's and Route 10's Pokecenters as example destinations, if nothing else because that makes a lot of sense and they are already "prepared" in vanilla code (one could speculate that GF intended to indeed be able to fly there). The following procedure can be applied to any destination map, as long as you add the appropriate entries in data/maps/special_warps.asm. The idea is to expand the allowed destinations. This is achieved by editing the code that handles the fly action.
constants/map_constants.asm
Edit ; Indoor maps, such as houses, use this as the Map ID in their exit warps
; This map ID takes the player back to the last outdoor map they were on, stored in wLastMap
DEF LAST_MAP EQU -1
+
+DEF NUM_FLY_LOCATIONS EQU NUM_CITY_MAPS + 2
+
+ const_def NUM_CITY_MAPS
+ const FLYLOC_ROUTE_4_CENTER
+ const FLYLOC_ROUTE_10_CENTER
The +2
allows to hold 2 more destinations. Modify it according to your needs; as a reminder, this tutorial covers how to add exactly 2 fly destinations.
engine/items/town_map.asm
Edit.wrapToEndOfList
- ld hl, wFlyLocationsList + NUM_CITY_MAPS
+ ld hl, wFlyLocationsList + NUM_FLY_LOCATIONS
jr .pressedDown
BuildFlyLocationsList:
ld hl, wFlyAnimUsingCoordList
ld [hl], $ff
- inc hl
+ inc hl ; it's wFlyLocationsList
ld a, [wTownVisitedFlag]
ld e, a
ld a, [wTownVisitedFlag + 1]
ld d, a
- lb bc, 0, NUM_CITY_MAPS
+ lb bc, 0, NUM_FLY_LOCATIONS
.loop
srl d
rr e
ld a, NOT_VISITED
- jr nc, .notVisited
+ jr nc, .gotValue
ld a, b ; store the map number of the town if it has been visited
-.notVisited
+ cp FLYLOC_ROUTE_4_CENTER
+ jr c, .gotValue
+ ld a, ROUTE_4
+ jr z, .gotValue
+ ld a, ROUTE_10
+.gotValue
ld [hl], a
inc hl
inc b
dec c
jr nz, .loop
ld [hl], $ff
ret
What's happening here is that we are looping over "visited" locations to add new targets.
This list gets created every time we open the map.
The vanilla code expects only city-like entries, hence the value of b
corresponds exactly to the index value of the city we want to add.
This is NOT the case for ROUTE_4 and ROUTE_10. Thus, we need some more hacky way to handle these two entries.
There are for sure other options, but this is what I came up with. Feel free to improve on this approach!
Btw, the very first edit is of course not necessary since it's a mere comment, but I personally think it helps a lot for clarity.
The .notVisited
label was renamed to match its new usage.
ram/wram.asm
Edit-wFlyLocationsList:: ds NUM_CITY_MAPS + 2
+wFlyLocationsList:: ds NUM_FLY_LOCATIONS + 2 ; edited, to allow fly to Route 4 and Route 10
We need to allocate 2 more bytes for the wram variable we use in the code above. Again, if you add more than 2 destinations, modify this 2 accordingly.
-wTownVisitedFlag:: flag_array NUM_CITY_MAPS
+wTownVisitedFlag:: flag_array NUM_FLY_LOCATIONS
This is not terribly necessary, but it's good habit to have it.
What happens here is that flag_array
allocates a number of bytes equal to the number on its right divided by 8.
In our specific case, adding 2 more bits does not make us require 1 extra byte.
If that was the case for you because you add more fly destinations, remember you'll end up with 1 more byte here, so be careful.
Mark the pokecenters as visited
You have multiple choices to do that.
- When talking to the nurse
- When entering the pokecenter
engine/events/pokecenter.asm
Option 1: EditDisplayPokemonCenterDialogue_::
+; new, for setting Route 4 and Route 10 Pokecenters fly locations
+ ld a, [wCurMap]
+ cp MT_MOON_POKECENTER
+ jr nz, .checkRockTunnelPokecenter
+ lb bc, FLAG_SET, FLYLOC_ROUTE_4_CENTER
+ ld hl, wTownVisitedFlag ; mark town as visited (for flying)
+ predef FlagActionPredef
+ jr .regularCenter
+.checkRockTunnelPokecenter
+ cp ROCK_TUNNEL_POKECENTER
+ jr nz, .regularCenter
+ lb bc, FLAG_SET, FLYLOC_ROUTE_10_CENTER
+ ld hl, wTownVisitedFlag ; mark town as visited (for flying)
+ predef FlagActionPredef
+.regularCenter
+; back to vanilla
call SaveScreenTilesToBuffer1 ; save screen
ld hl, PokemonCenterWelcomeText
call PrintText
We're done! We need to make these locations being registered. There are many ways to do so, but what I ended up with (just in case FLY was acquired earlier in the game, to avoid cheesing a route), is to have these fly destinations registered upon talking with the nurse in their respective Pokecenters. Btw, some of the code above it inspired from pokeyellow, so you can take a look there for other special behaviours you may want to give to your nurses.
Option 2: Edit the pokecenters scripts
Edit scripts/MtMoonPokecenter.asm:
MtMoonPokecenter_Script:
+ call .markAsVisited
call Serial_TryEstablishingExternallyClockedConnection
jp EnableAutoTextBoxDrawing
+.markAsVisited
+ ld hl, wCurrentMapScriptFlags
+ bit BIT_CUR_MAP_LOADED_1, [hl]
+ res BIT_CUR_MAP_LOADED_1, [hl]
+ ret z
+ lb bc, FLAG_SET, FLYLOC_ROUTE_4_CENTER
+ ld hl, wTownVisitedFlag
+ predef_jump FlagActionPredef
Edit scripts/RockTunnelPokecenter.asm:
RockTunnelPokecenter_Script:
+ call .markAsVisited
call Serial_TryEstablishingExternallyClockedConnection
jp EnableAutoTextBoxDrawing
+.markAsVisited
+ ld hl, wCurrentMapScriptFlags
+ bit BIT_CUR_MAP_LOADED_1, [hl]
+ res BIT_CUR_MAP_LOADED_1, [hl]
+ ret z
+ lb bc, FLAG_SET, FLYLOC_ROUTE_10_CENTER
+ ld hl, wTownVisitedFlag
+ predef_jump FlagActionPredef
We're done! We need to make these locations being registered. There are many ways to do so, but what I ended up with (just in case FLY was acquired earlier in the game, to avoid cheesing a route), is to have these fly destinations registered upon entering the pokecenter.
Fly FROM any map
Why can't we fly from the top of the Celadon Store Center? Or from the top of the Celadon Condominiums? Or, well, Viridian Forest? Let's handle this! (be careful if you make the Safari Zone flyable from, as it may mess up with the Safari Game) It's as simple as it follows.
engine/menus/start_sub_menus.asm
Edit.fly
bit BIT_THUNDERBADGE, a
jp z, .newBadgeRequired
call CheckIfInOutsideMap
jr z, .canFly
+; new block to make "open-air" maps flyable
+ ld a, [wCurMap]
+ cp CELADON_MART_ROOF
+ jr z, .canFly
+ cp CELADON_MANSION_ROOF
+ jr z, .canFly
+ cp VERMILION_DOCK
+ jr z, .canFly
+ cp SS_ANNE_BOW
+ jr z, .canFly
+; end of new block to make "open-air" maps flyable
ld a, [wWhichPokemon]
ld hl, wPartyMonNicks
call GetPartyMonName
ld hl, .cannotFlyHereText
call PrintText
jp .loop
.canFly
And we are done!