shithub: pokered

ref: 94e609b4bcbb0c6469a2fa0b3ba235d776e269ef
dir: /home/overworld.asm/

View raw version
HandleMidJump::
; Handle the player jumping down
; a ledge in the overworld.
	jpba _HandleMidJump

EnterMap::
; Load a new map.
	ld a, $ff
	ld [wJoyIgnore], a
	call LoadMapData
	callba ClearVariablesAfterLoadingMapData
	ld hl, wd72c
	bit 0, [hl] ; has the player already made 3 steps since the last battle?
	jr z, .skipGivingThreeStepsOfNoRandomBattles
	ld a, 3 ; minimum number of steps between battles
	ld [wNumberOfNoRandomBattleStepsLeft], a
.skipGivingThreeStepsOfNoRandomBattles
	ld hl, wd72e
	bit 5, [hl] ; did a battle happen immediately before this?
	res 5, [hl] ; unset the "battle just happened" flag
	call z, ResetUsingStrengthOutOfBattleBit
	call nz, MapEntryAfterBattle
	ld hl, wd732
	ld a, [hl]
	and 1 << 4 | 1 << 3 ; fly warp or dungeon warp
	jr z, .didNotEnterUsingFlyWarpOrDungeonWarp
	res 3, [hl]
	callba EnterMapAnim
	call UpdateSprites
.didNotEnterUsingFlyWarpOrDungeonWarp
	callba CheckForceBikeOrSurf ; handle currents in SF islands and forced bike riding in cycling road
	ld hl, wd72d
	res 5, [hl]
	call UpdateSprites
	ld hl, wd126
	set 5, [hl]
	set 6, [hl]
	xor a
	ld [wJoyIgnore], a

OverworldLoop::
	call DelayFrame
OverworldLoopLessDelay::
	call DelayFrame
	call LoadGBPal
	ld a,[wd736]
	bit 6,a ; jumping down a ledge?
	call nz, HandleMidJump
	ld a,[wWalkCounter]
	and a
	jp nz,.moveAhead ; if the player sprite has not yet completed the walking animation
	call JoypadOverworld ; get joypad state (which is possibly simulated)
	callba SafariZoneCheck
	ld a,[wSafariZoneGameOver]
	and a
	jp nz,WarpFound2
	ld hl,wd72d
	bit 3,[hl]
	res 3,[hl]
	jp nz,WarpFound2
	ld a,[wd732]
	and a,1 << 4 | 1 << 3 ; fly warp or dungeon warp
	jp nz,HandleFlyWarpOrDungeonWarp
	ld a,[wCurOpponent]
	and a
	jp nz,.newBattle
	ld a,[wd730]
	bit 7,a ; are we simulating button presses?
	jr z,.notSimulating
	ld a,[hJoyHeld]
	jr .checkIfStartIsPressed
.notSimulating
	ld a,[hJoyPressed]
.checkIfStartIsPressed
	bit 3,a ; start button
	jr z,.startButtonNotPressed
; if START is pressed
	xor a
	ld [hSpriteIndexOrTextID],a ; start menu text ID
	jp .displayDialogue
.startButtonNotPressed
	bit 0,a ; A button
	jp z,.checkIfDownButtonIsPressed
; if A is pressed
	ld a,[wd730]
	bit 2,a
	jp nz,.noDirectionButtonsPressed
	call IsPlayerCharacterBeingControlledByGame
	jr nz,.checkForOpponent
	call CheckForHiddenObjectOrBookshelfOrCardKeyDoor
	ld a,[$ffeb]
	and a
	jp z,OverworldLoop ; jump if a hidden object or bookshelf was found, but not if a card key door was found
	call IsSpriteOrSignInFrontOfPlayer
	ld a,[hSpriteIndexOrTextID]
	and a
	jp z,OverworldLoop
.displayDialogue
	predef GetTileAndCoordsInFrontOfPlayer
	call UpdateSprites
	ld a,[wFlags_0xcd60]
	bit 2,a
	jr nz,.checkForOpponent
	bit 0,a
	jr nz,.checkForOpponent
	aCoord 8, 9
	ld [wTilePlayerStandingOn],a ; unused?
	call DisplayTextID ; display either the start menu or the NPC/sign text
	ld a,[wEnteringCableClub]
	and a
	jr z,.checkForOpponent
	dec a
	ld a,0
	ld [wEnteringCableClub],a
	jr z,.changeMap
; XXX can this code be reached?
	predef LoadSAV
	ld a,[wCurMap]
	ld [wDestinationMap],a
	call SpecialWarpIn
	ld a,[wCurMap]
	call SwitchToMapRomBank ; switch to the ROM bank of the current map
	ld hl,wCurMapTileset
	set 7,[hl]
.changeMap
	jp EnterMap
.checkForOpponent
	ld a,[wCurOpponent]
	and a
	jp nz,.newBattle
	jp OverworldLoop
.noDirectionButtonsPressed
	ld hl,wFlags_0xcd60
	res 2,[hl]
	call UpdateSprites
	ld a,1
	ld [wCheckFor180DegreeTurn],a
	ld a,[wPlayerMovingDirection] ; the direction that was pressed last time
	and a
	jp z,OverworldLoop
; if a direction was pressed last time
	ld [wPlayerLastStopDirection],a ; save the last direction
	xor a
	ld [wPlayerMovingDirection],a ; zero the direction
	jp OverworldLoop
.checkIfDownButtonIsPressed
	ld a,[hJoyHeld] ; current joypad state
	bit 7,a ; down button
	jr z,.checkIfUpButtonIsPressed
	ld a,1
	ld [wSpriteStateData1 + 3],a ; delta Y
	ld a,PLAYER_DIR_DOWN
	jr .handleDirectionButtonPress
.checkIfUpButtonIsPressed
	bit 6,a ; up button
	jr z,.checkIfLeftButtonIsPressed
	ld a,-1
	ld [wSpriteStateData1 + 3],a ; delta Y
	ld a,PLAYER_DIR_UP
	jr .handleDirectionButtonPress
.checkIfLeftButtonIsPressed
	bit 5,a ; left button
	jr z,.checkIfRightButtonIsPressed
	ld a,-1
	ld [wSpriteStateData1 + 5],a ; delta X
	ld a,PLAYER_DIR_LEFT
	jr .handleDirectionButtonPress
.checkIfRightButtonIsPressed
	bit 4,a ; right button
	jr z,.noDirectionButtonsPressed
	ld a,1 ; PLAYER_DIR_RIGHT
	ld [wSpriteStateData1 + 5],a ; delta X
.handleDirectionButtonPress
	ld [wPlayerDirection],a ; new direction
	ld a,[wd730]
	bit 7,a ; are we simulating button presses?
	jr nz,.noDirectionChange ; ignore direction changes if we are
	ld a,[wCheckFor180DegreeTurn]
	and a
	jr z,.noDirectionChange
	ld a,[wPlayerDirection] ; new direction
	ld b,a
	ld a,[wPlayerLastStopDirection] ; old direction
	cp b
	jr z,.noDirectionChange
; Check whether the player did a 180-degree turn.
; It appears that this code was supposed to show the player rotate by having
; the player's sprite face an intermediate direction before facing the opposite
; direction (instead of doing an instantaneous about-face), but the intermediate
; direction is only set for a short period of time. It is unlikely for it to
; ever be visible because DelayFrame is called at the start of OverworldLoop and
; normally not enough cycles would be executed between then and the time the
; direction is set for V-blank to occur while the direction is still set.
	swap a ; put old direction in upper half
	or b ; put new direction in lower half
	cp a,(PLAYER_DIR_DOWN << 4) | PLAYER_DIR_UP ; change dir from down to up
	jr nz,.notDownToUp
	ld a,PLAYER_DIR_LEFT
	ld [wPlayerMovingDirection],a
	jr .holdIntermediateDirectionLoop
.notDownToUp
	cp a,(PLAYER_DIR_UP << 4) | PLAYER_DIR_DOWN ; change dir from up to down
	jr nz,.notUpToDown
	ld a,PLAYER_DIR_RIGHT
	ld [wPlayerMovingDirection],a
	jr .holdIntermediateDirectionLoop
.notUpToDown
	cp a,(PLAYER_DIR_RIGHT << 4) | PLAYER_DIR_LEFT ; change dir from right to left
	jr nz,.notRightToLeft
	ld a,PLAYER_DIR_DOWN
	ld [wPlayerMovingDirection],a
	jr .holdIntermediateDirectionLoop
.notRightToLeft
	cp a,(PLAYER_DIR_LEFT << 4) | PLAYER_DIR_RIGHT ; change dir from left to right
	jr nz,.holdIntermediateDirectionLoop
	ld a,PLAYER_DIR_UP
	ld [wPlayerMovingDirection],a
.holdIntermediateDirectionLoop
	ld hl,wFlags_0xcd60
	set 2,[hl]
	ld hl,wCheckFor180DegreeTurn
	dec [hl]
	jr nz,.holdIntermediateDirectionLoop
	ld a,[wPlayerDirection]
	ld [wPlayerMovingDirection],a
	call NewBattle
	jp c,.battleOccurred
	jp OverworldLoop
.noDirectionChange
	ld a,[wPlayerDirection] ; current direction
	ld [wPlayerMovingDirection],a ; save direction
	call UpdateSprites
	ld a,[wWalkBikeSurfState]
	cp a,$02 ; surfing
	jr z,.surfing
; not surfing
	call CollisionCheckOnLand
	jr nc,.noCollision
; collision occurred
	push hl
	ld hl,wd736
	bit 2,[hl] ; standing on warp flag
	pop hl
	jp z,OverworldLoop
; collision occurred while standing on a warp
	push hl
	call ExtraWarpCheck ; sets carry if there is a potential to warp
	pop hl
	jp c,CheckWarpsCollision
	jp OverworldLoop
.surfing
	call CollisionCheckOnWater
	jp c,OverworldLoop
.noCollision
	ld a,$08
	ld [wWalkCounter],a
	jr .moveAhead2
.moveAhead
	ld a,[wd736]
	bit 7,a
	jr z,.noSpinning
	callba LoadSpinnerArrowTiles ; spin while moving
.noSpinning
	call UpdateSprites
.moveAhead2
	ld hl,wFlags_0xcd60
	res 2,[hl]
	ld a,[wWalkBikeSurfState]
	dec a ; riding a bike?
	jr nz,.normalPlayerSpriteAdvancement
	ld a,[wd736]
	bit 6,a ; jumping a ledge?
	jr nz,.normalPlayerSpriteAdvancement
	call BikeSpeedup ; if riding a bike and not jumping a ledge
.normalPlayerSpriteAdvancement
	call AdvancePlayerSprite
	ld a,[wWalkCounter]
	and a
	jp nz,CheckMapConnections ; it seems like this check will never succeed (the other place where CheckMapConnections is run works)
; walking animation finished
	ld a,[wd730]
	bit 7,a
	jr nz,.doneStepCounting ; if button presses are being simulated, don't count steps
; step counting
	ld hl,wStepCounter
	dec [hl]
	ld a,[wd72c]
	bit 0,a
	jr z,.doneStepCounting
	ld hl,wNumberOfNoRandomBattleStepsLeft
	dec [hl]
	jr nz,.doneStepCounting
	ld hl,wd72c
	res 0,[hl] ; indicate that the player has stepped thrice since the last battle
.doneStepCounting
	CheckEvent EVENT_IN_SAFARI_ZONE
	jr z,.notSafariZone
	callba SafariZoneCheckSteps
	ld a,[wSafariZoneGameOver]
	and a
	jp nz,WarpFound2
.notSafariZone
	ld a,[wIsInBattle]
	and a
	jp nz,CheckWarpsNoCollision
	predef ApplyOutOfBattlePoisonDamage ; also increment daycare mon exp
	ld a,[wOutOfBattleBlackout]
	and a
	jp nz,HandleBlackOut ; if all pokemon fainted
.newBattle
	call NewBattle
	ld hl,wd736
	res 2,[hl] ; standing on warp flag
	jp nc,CheckWarpsNoCollision ; check for warps if there was no battle
.battleOccurred
	ld hl,wd72d
	res 6,[hl]
	ld hl,wFlags_D733
	res 3,[hl]
	ld hl,wd126
	set 5,[hl]
	set 6,[hl]
	xor a
	ld [hJoyHeld],a
	ld a,[wCurMap]
	cp a,CINNABAR_GYM
	jr nz,.notCinnabarGym
	SetEvent EVENT_2A7
.notCinnabarGym
	ld hl,wd72e
	set 5,[hl]
	ld a,[wCurMap]
	cp a,OAKS_LAB
	jp z,.noFaintCheck ; no blacking out if the player lost to the rival in Oak's lab
	callab AnyPartyAlive
	ld a,d
	and a
	jr z,.allPokemonFainted
.noFaintCheck
	ld c,10
	call DelayFrames
	jp EnterMap
.allPokemonFainted
	ld a,$ff
	ld [wIsInBattle],a
	call RunMapScript
	jp HandleBlackOut

; function to determine if there will be a battle and execute it (either a trainer battle or wild battle)
; sets carry if a battle occurred and unsets carry if not
NewBattle:: ; 0683 (0:0683)
	ld a,[wd72d]
	bit 4,a
	jr nz,.noBattle
	call IsPlayerCharacterBeingControlledByGame
	jr nz,.noBattle ; no battle if the player character is under the game's control
	ld a,[wd72e]
	bit 4,a
	jr nz,.noBattle
	jpba InitBattle
.noBattle
	and a
	ret

; function to make bikes twice as fast as walking
BikeSpeedup:: ; 06a0 (0:06a0)
	ld a,[wNPCMovementScriptPointerTableNum]
	and a
	ret nz
	ld a,[wCurMap]
	cp a,ROUTE_17 ; Cycling Road
	jr nz,.goFaster
	ld a,[hJoyHeld]
	and a,D_UP | D_LEFT | D_RIGHT
	ret nz
.goFaster
	jp AdvancePlayerSprite

; check if the player has stepped onto a warp after having not collided
CheckWarpsNoCollision:: ; 06b4 (0:06b4)
	ld a,[wNumberOfWarps]
	and a
	jp z,CheckMapConnections
	ld a,[wNumberOfWarps]
	ld b,0
	ld c,a
	ld a,[wYCoord]
	ld d,a
	ld a,[wXCoord]
	ld e,a
	ld hl,wWarpEntries
CheckWarpsNoCollisionLoop:: ; 06cc (0:06cc)
	ld a,[hli] ; check if the warp's Y position matches
	cp d
	jr nz,CheckWarpsNoCollisionRetry1
	ld a,[hli] ; check if the warp's X position matches
	cp e
	jr nz,CheckWarpsNoCollisionRetry2
; if a match was found
	push hl
	push bc
	ld hl,wd736
	set 2,[hl] ; standing on warp flag
	callba IsPlayerStandingOnDoorTileOrWarpTile
	pop bc
	pop hl
	jr c,WarpFound1 ; jump if standing on door or warp
	push hl
	push bc
	call ExtraWarpCheck
	pop bc
	pop hl
	jr nc,CheckWarpsNoCollisionRetry2
; if the extra check passed
	ld a,[wFlags_D733]
	bit 2,a
	jr nz,WarpFound1
	push de
	push bc
	call Joypad
	pop bc
	pop de
	ld a,[hJoyHeld]
	and a,D_DOWN | D_UP | D_LEFT | D_RIGHT
	jr z,CheckWarpsNoCollisionRetry2 ; if directional buttons aren't being pressed, do not pass through the warp
	jr WarpFound1

; check if the player has stepped onto a warp after having collided
CheckWarpsCollision:: ; 0706 (0:0706)
	ld a,[wNumberOfWarps]
	ld c,a
	ld hl,wWarpEntries
.loop
	ld a,[hli] ; Y coordinate of warp
	ld b,a
	ld a,[wYCoord]
	cp b
	jr nz,.retry1
	ld a,[hli] ; X coordinate of warp
	ld b,a
	ld a,[wXCoord]
	cp b
	jr nz,.retry2
	ld a,[hli]
	ld [wDestinationWarpID],a
	ld a,[hl]
	ld [hWarpDestinationMap],a
	jr WarpFound2
.retry1
	inc hl
.retry2
	inc hl
	inc hl
	dec c
	jr nz,.loop
	jp OverworldLoop

CheckWarpsNoCollisionRetry1:: ; 072f (0:072f)
	inc hl
CheckWarpsNoCollisionRetry2:: ; 0730 (0:0730)
	inc hl
	inc hl
	jp ContinueCheckWarpsNoCollisionLoop

WarpFound1:: ; 0735 (0:0735)
	ld a,[hli]
	ld [wDestinationWarpID],a
	ld a,[hli]
	ld [hWarpDestinationMap],a

WarpFound2:: ; 073c (0:073c)
	ld a,[wNumberOfWarps]
	sub c
	ld [wWarpedFromWhichWarp],a ; save ID of used warp
	ld a,[wCurMap]
	ld [wWarpedFromWhichMap],a
	call CheckIfInOutsideMap
	jr nz,.indoorMaps
; this is for handling "outside" maps that can't have the 0xFF destination map
	ld a,[wCurMap]
	ld [wLastMap],a
	ld a,[wCurMapWidth]
	ld [wUnusedD366],a ; not read
	ld a,[hWarpDestinationMap]
	ld [wCurMap],a
	cp a,ROCK_TUNNEL_1
	jr nz,.notRockTunnel
	ld a,$06
	ld [wMapPalOffset],a
	call GBFadeOutToBlack
.notRockTunnel
	call PlayMapChangeSound
	jr .done
; for maps that can have the 0xFF destination map, which means to return to the outside map; not all these maps are necessarily indoors, though
.indoorMaps
	ld a,[hWarpDestinationMap] ; destination map
	cp a,$ff
	jr z,.goBackOutside
; if not going back to the previous map
	ld [wCurMap],a
	callba IsPlayerStandingOnWarpPadOrHole
	ld a,[wStandingOnWarpPadOrHole]
	dec a ; is the player on a warp pad?
	jr nz,.notWarpPad
; if the player is on a warp pad
	ld hl,wd732
	set 3,[hl]
	call LeaveMapAnim
	jr .skipMapChangeSound
.notWarpPad
	call PlayMapChangeSound
.skipMapChangeSound
	ld hl,wd736
	res 0,[hl]
	res 1,[hl]
	jr .done
.goBackOutside
	ld a,[wLastMap]
	ld [wCurMap],a
	call PlayMapChangeSound
	xor a
	ld [wMapPalOffset],a
.done
	ld hl,wd736
	set 0,[hl] ; have the player's sprite step out from the door (if there is one)
	call IgnoreInputForHalfSecond
	jp EnterMap

ContinueCheckWarpsNoCollisionLoop:: ; 07b5 (0:07b5)
	inc b ; increment warp number
	dec c ; decrement number of warps
	jp nz,CheckWarpsNoCollisionLoop

; if no matching warp was found
CheckMapConnections:: ; 07ba (0:07ba)
.checkWestMap
	ld a,[wXCoord]
	cp a,$ff
	jr nz,.checkEastMap
	ld a,[wMapConn3Ptr]
	ld [wCurMap],a
	ld a,[wWestConnectedMapXAlignment] ; new X coordinate upon entering west map
	ld [wXCoord],a
	ld a,[wYCoord]
	ld c,a
	ld a,[wWestConnectedMapYAlignment] ; Y adjustment upon entering west map
	add c
	ld c,a
	ld [wYCoord],a
	ld a,[wWestConnectedMapViewPointer] ; pointer to upper left corner of map without adjustment for Y position
	ld l,a
	ld a,[wWestConnectedMapViewPointer + 1]
	ld h,a
	srl c
	jr z,.savePointer1
.pointerAdjustmentLoop1
	ld a,[wWestConnectedMapWidth] ; width of connected map
	add a,MAP_BORDER * 2
	ld e,a
	ld d,0
	ld b,0
	add hl,de
	dec c
	jr nz,.pointerAdjustmentLoop1
.savePointer1
	ld a,l
	ld [wCurrentTileBlockMapViewPointer],a ; pointer to upper left corner of current tile block map section
	ld a,h
	ld [wCurrentTileBlockMapViewPointer + 1],a
	jp .loadNewMap
.checkEastMap
	ld b,a
	ld a,[wCurrentMapWidth2] ; map width
	cp b
	jr nz,.checkNorthMap
	ld a,[wMapConn4Ptr]
	ld [wCurMap],a
	ld a,[wEastConnectedMapXAlignment] ; new X coordinate upon entering east map
	ld [wXCoord],a
	ld a,[wYCoord]
	ld c,a
	ld a,[wEastConnectedMapYAlignment] ; Y adjustment upon entering east map
	add c
	ld c,a
	ld [wYCoord],a
	ld a,[wEastConnectedMapViewPointer] ; pointer to upper left corner of map without adjustment for Y position
	ld l,a
	ld a,[wEastConnectedMapViewPointer + 1]
	ld h,a
	srl c
	jr z,.savePointer2
.pointerAdjustmentLoop2
	ld a,[wEastConnectedMapWidth]
	add a,MAP_BORDER * 2
	ld e,a
	ld d,0
	ld b,0
	add hl,de
	dec c
	jr nz,.pointerAdjustmentLoop2
.savePointer2
	ld a,l
	ld [wCurrentTileBlockMapViewPointer],a ; pointer to upper left corner of current tile block map section
	ld a,h
	ld [wCurrentTileBlockMapViewPointer + 1],a
	jp .loadNewMap
.checkNorthMap
	ld a,[wYCoord]
	cp a,$ff
	jr nz,.checkSouthMap
	ld a,[wMapConn1Ptr]
	ld [wCurMap],a
	ld a,[wNorthConnectedMapYAlignment] ; new Y coordinate upon entering north map
	ld [wYCoord],a
	ld a,[wXCoord]
	ld c,a
	ld a,[wNorthConnectedMapXAlignment] ; X adjustment upon entering north map
	add c
	ld c,a
	ld [wXCoord],a
	ld a,[wNorthConnectedMapViewPointer] ; pointer to upper left corner of map without adjustment for X position
	ld l,a
	ld a,[wNorthConnectedMapViewPointer + 1]
	ld h,a
	ld b,0
	srl c
	add hl,bc
	ld a,l
	ld [wCurrentTileBlockMapViewPointer],a ; pointer to upper left corner of current tile block map section
	ld a,h
	ld [wCurrentTileBlockMapViewPointer + 1],a
	jp .loadNewMap
.checkSouthMap
	ld b,a
	ld a,[wCurrentMapHeight2]
	cp b
	jr nz,.didNotEnterConnectedMap
	ld a,[wMapConn2Ptr]
	ld [wCurMap],a
	ld a,[wSouthConnectedMapYAlignment] ; new Y coordinate upon entering south map
	ld [wYCoord],a
	ld a,[wXCoord]
	ld c,a
	ld a,[wSouthConnectedMapXAlignment] ; X adjustment upon entering south map
	add c
	ld c,a
	ld [wXCoord],a
	ld a,[wSouthConnectedMapViewPointer] ; pointer to upper left corner of map without adjustment for X position
	ld l,a
	ld a,[wSouthConnectedMapViewPointer + 1]
	ld h,a
	ld b,0
	srl c
	add hl,bc
	ld a,l
	ld [wCurrentTileBlockMapViewPointer],a ; pointer to upper left corner of current tile block map section
	ld a,h
	ld [wCurrentTileBlockMapViewPointer + 1],a
.loadNewMap ; load the connected map that was entered
	call LoadMapHeader
	call PlayDefaultMusicFadeOutCurrent
	ld b, SET_PAL_OVERWORLD
	call RunPaletteCommand
; Since the sprite set shouldn't change, this will just update VRAM slots at
; $C2XE without loading any tile patterns.
	callba InitMapSprites
	call LoadTileBlockMap
	jp OverworldLoopLessDelay
.didNotEnterConnectedMap
	jp OverworldLoop

; function to play a sound when changing maps
PlayMapChangeSound:: ; 08c9 (0:08c9)
	aCoord 8, 8 ; upper left tile of the 4x4 square the player's sprite is standing on
	cp a,$0b ; door tile in tileset 0
	jr nz,.didNotGoThroughDoor
	ld a,SFX_GO_INSIDE
	jr .playSound
.didNotGoThroughDoor
	ld a,SFX_GO_OUTSIDE
.playSound
	call PlaySound
	ld a,[wMapPalOffset]
	and a
	ret nz
	jp GBFadeOutToBlack

CheckIfInOutsideMap:: ; 08e1 (0:08e1)
; If the player is in an outside map (a town or route), set the z flag
	ld a, [wCurMapTileset]
	and a ; most towns/routes have tileset 0 (OVERWORLD)
	ret z
	cp PLATEAU ; Route 23 / Indigo Plateau
	ret

; this function is an extra check that sometimes has to pass in order to warp, beyond just standing on a warp
; the "sometimes" qualification is necessary because of CheckWarpsNoCollision's behavior
; depending on the map, either "function 1" or "function 2" is used for the check
; "function 1" passes when the player is at the edge of the map and is facing towards the outside of the map
; "function 2" passes when the the tile in front of the player is among a certain set
; sets carry if the check passes, otherwise clears carry
ExtraWarpCheck:: ; 08e9 (0:08e9)
	ld a, [wCurMap]
	cp SS_ANNE_3
	jr z, .useFunction1
	cp ROCKET_HIDEOUT_1
	jr z, .useFunction2
	cp ROCKET_HIDEOUT_2
	jr z, .useFunction2
	cp ROCKET_HIDEOUT_4
	jr z, .useFunction2
	cp ROCK_TUNNEL_1
	jr z, .useFunction2
	ld a, [wCurMapTileset]
	and a ; outside tileset (OVERWORLD)
	jr z, .useFunction2
	cp SHIP ; S.S. Anne tileset
	jr z, .useFunction2
	cp SHIP_PORT ; Vermilion Port tileset
	jr z, .useFunction2
	cp PLATEAU ; Indigo Plateau tileset
	jr z, .useFunction2
.useFunction1
	ld hl, IsPlayerFacingEdgeOfMap
	jr .doBankswitch
.useFunction2
	ld hl, IsWarpTileInFrontOfPlayer
.doBankswitch
	ld b, BANK(IsWarpTileInFrontOfPlayer)
	jp Bankswitch

MapEntryAfterBattle:: ; 091f (0:091f)
	callba IsPlayerStandingOnWarp ; for enabling warp testing after collisions
	ld a,[wMapPalOffset]
	and a
	jp z,GBFadeInFromWhite
	jp LoadGBPal

HandleBlackOut::
; For when all the player's pokemon faint.
; Does not print the "blacked out" message.

	call GBFadeOutToBlack
	ld a, $08
	call StopMusic
	ld hl, wd72e
	res 5, [hl]
	ld a, Bank(ResetStatusAndHalveMoneyOnBlackout) ; also Bank(SpecialWarpIn) and Bank(SpecialEnterMap)
	ld [H_LOADEDROMBANK], a
	ld [MBC1RomBank], a
	call ResetStatusAndHalveMoneyOnBlackout
	call SpecialWarpIn
	call PlayDefaultMusicFadeOutCurrent
	jp SpecialEnterMap

StopMusic::
	ld [wAudioFadeOutControl], a
	ld a, $ff
	ld [wNewSoundID], a
	call PlaySound
.wait
	ld a, [wAudioFadeOutControl]
	and a
	jr nz, .wait
	jp StopAllSounds

HandleFlyWarpOrDungeonWarp::
	call UpdateSprites
	call Delay3
	xor a
	ld [wBattleResult], a
	ld [wWalkBikeSurfState], a
	ld [wIsInBattle], a
	ld [wMapPalOffset], a
	ld hl, wd732
	set 2, [hl] ; fly warp or dungeon warp
	res 5, [hl] ; forced to ride bike
	call LeaveMapAnim
	ld a, Bank(SpecialWarpIn)
	ld [H_LOADEDROMBANK], a
	ld [MBC1RomBank], a
	call SpecialWarpIn
	jp SpecialEnterMap

LeaveMapAnim::
	jpba _LeaveMapAnim

LoadPlayerSpriteGraphics::
; Load sprite graphics based on whether the player is standing, biking, or surfing.

	; 0: standing
	; 1: biking
	; 2: surfing

	ld a, [wWalkBikeSurfState]
	dec a
	jr z, .ridingBike

	ld a, [hTilesetType]
	and a
	jr nz, .determineGraphics
	jr .startWalking

.ridingBike
	; If the bike can't be used,
	; start walking instead.
	call IsBikeRidingAllowed
	jr c, .determineGraphics

.startWalking
	xor a
	ld [wWalkBikeSurfState], a
	ld [wWalkBikeSurfStateCopy], a
	jp LoadWalkingPlayerSpriteGraphics

.determineGraphics
	ld a, [wWalkBikeSurfState]
	and a
	jp z, LoadWalkingPlayerSpriteGraphics
	dec a
	jp z, LoadBikePlayerSpriteGraphics
	dec a
	jp z, LoadSurfingPlayerSpriteGraphics
	jp LoadWalkingPlayerSpriteGraphics

IsBikeRidingAllowed::
; The bike can be used on Route 23 and Indigo Plateau,
; or maps with tilesets in BikeRidingTilesets.
; Return carry if biking is allowed.

	ld a, [wCurMap]
	cp ROUTE_23
	jr z, .allowed
	cp INDIGO_PLATEAU
	jr z, .allowed

	ld a, [wCurMapTileset]
	ld b, a
	ld hl, BikeRidingTilesets
.loop
	ld a, [hli]
	cp b
	jr z, .allowed
	inc a
	jr nz, .loop
	and a
	ret

.allowed
	scf
	ret

INCLUDE "data/bike_riding_tilesets.asm"

; load the tile pattern data of the current tileset into VRAM
LoadTilesetTilePatternData:: ; 09e8 (0:09e8)
	ld a,[wTileSetGFXPtr]
	ld l,a
	ld a,[wTileSetGFXPtr + 1]
	ld h,a
	ld de,vTileset
	ld bc,$600
	ld a,[wTileSetBank]
	jp FarCopyData2

; this loads the current maps complete tile map (which references blocks, not individual tiles) to C6E8
; it can also load partial tile maps of connected maps into a border of length 3 around the current map
LoadTileBlockMap:: ; 09fc (0:09fc)
; fill C6E8-CBFB with the background tile
	ld hl,wOverworldMap
	ld a,[wMapBackgroundTile]
	ld d,a
	ld bc,$0514
.backgroundTileLoop
	ld a,d
	ld [hli],a
	dec bc
	ld a,c
	or b
	jr nz,.backgroundTileLoop
; load tile map of current map (made of tile block IDs)
; a 3-byte border at the edges of the map is kept so that there is space for map connections
	ld hl,wOverworldMap
	ld a,[wCurMapWidth]
	ld [hMapWidth],a
	add a,MAP_BORDER * 2 ; east and west
	ld [hMapStride],a ; map width + border
	ld b,0
	ld c,a
; make space for north border (next 3 lines)
	add hl,bc
	add hl,bc
	add hl,bc
	ld c,MAP_BORDER
	add hl,bc ; this puts us past the (west) border
	ld a,[wMapDataPtr] ; tile map pointer
	ld e,a
	ld a,[wMapDataPtr + 1]
	ld d,a ; de = tile map pointer
	ld a,[wCurMapHeight]
	ld b,a
.rowLoop ; copy one row each iteration
	push hl
	ld a,[hMapWidth] ; map width (without border)
	ld c,a
.rowInnerLoop
	ld a,[de]
	inc de
	ld [hli],a
	dec c
	jr nz,.rowInnerLoop
; add the map width plus the border to the base address of the current row to get the next row's address
	pop hl
	ld a,[hMapStride] ; map width + border
	add l
	ld l,a
	jr nc,.noCarry
	inc h
.noCarry
	dec b
	jr nz,.rowLoop
.northConnection
	ld a,[wMapConn1Ptr]
	cp a,$ff
	jr z,.southConnection
	call SwitchToMapRomBank
	ld a,[wNorthConnectionStripSrc]
	ld l,a
	ld a,[wNorthConnectionStripSrc + 1]
	ld h,a
	ld a,[wNorthConnectionStripDest]
	ld e,a
	ld a,[wNorthConnectionStripDest + 1]
	ld d,a
	ld a,[wNorthConnectionStripWidth]
	ld [hNorthSouthConnectionStripWidth],a
	ld a,[wNorthConnectedMapWidth]
	ld [hNorthSouthConnectedMapWidth],a
	call LoadNorthSouthConnectionsTileMap
.southConnection
	ld a,[wMapConn2Ptr]
	cp a,$ff
	jr z,.westConnection
	call SwitchToMapRomBank
	ld a,[wSouthConnectionStripSrc]
	ld l,a
	ld a,[wSouthConnectionStripSrc + 1]
	ld h,a
	ld a,[wSouthConnectionStripDest]
	ld e,a
	ld a,[wSouthConnectionStripDest + 1]
	ld d,a
	ld a,[wSouthConnectionStripWidth]
	ld [hNorthSouthConnectionStripWidth],a
	ld a,[wSouthConnectedMapWidth]
	ld [hNorthSouthConnectedMapWidth],a
	call LoadNorthSouthConnectionsTileMap
.westConnection
	ld a,[wMapConn3Ptr]
	cp a,$ff
	jr z,.eastConnection
	call SwitchToMapRomBank
	ld a,[wWestConnectionStripSrc]
	ld l,a
	ld a,[wWestConnectionStripSrc + 1]
	ld h,a
	ld a,[wWestConnectionStripDest]
	ld e,a
	ld a,[wWestConnectionStripDest + 1]
	ld d,a
	ld a,[wWestConnectionStripHeight]
	ld b,a
	ld a,[wWestConnectedMapWidth]
	ld [hEastWestConnectedMapWidth],a
	call LoadEastWestConnectionsTileMap
.eastConnection
	ld a,[wMapConn4Ptr]
	cp a,$ff
	jr z,.done
	call SwitchToMapRomBank
	ld a,[wEastConnectionStripSrc]
	ld l,a
	ld a,[wEastConnectionStripSrc + 1]
	ld h,a
	ld a,[wEastConnectionStripDest]
	ld e,a
	ld a,[wEastConnectionStripDest + 1]
	ld d,a
	ld a,[wEastConnectionStripHeight]
	ld b,a
	ld a,[wEastConnectedMapWidth]
	ld [hEastWestConnectedMapWidth],a
	call LoadEastWestConnectionsTileMap
.done
	ret

LoadNorthSouthConnectionsTileMap:: ; 0ade (0:0ade)
	ld c,MAP_BORDER
.loop
	push de
	push hl
	ld a,[hNorthSouthConnectionStripWidth]
	ld b,a
.innerLoop
	ld a,[hli]
	ld [de],a
	inc de
	dec b
	jr nz,.innerLoop
	pop hl
	pop de
	ld a,[hNorthSouthConnectedMapWidth]
	add l
	ld l,a
	jr nc,.noCarry1
	inc h
.noCarry1
	ld a,[wCurMapWidth]
	add a,MAP_BORDER * 2
	add e
	ld e,a
	jr nc,.noCarry2
	inc d
.noCarry2
	dec c
	jr nz,.loop
	ret

LoadEastWestConnectionsTileMap:: ; 0b02 (0:0b02)
	push hl
	push de
	ld c,MAP_BORDER
.innerLoop
	ld a,[hli]
	ld [de],a
	inc de
	dec c
	jr nz,.innerLoop
	pop de
	pop hl
	ld a,[hEastWestConnectedMapWidth]
	add l
	ld l,a
	jr nc,.noCarry1
	inc h
.noCarry1
	ld a,[wCurMapWidth]
	add a,MAP_BORDER * 2
	add e
	ld e,a
	jr nc,.noCarry2
	inc d
.noCarry2
	dec b
	jr nz,LoadEastWestConnectionsTileMap
	ret

; function to check if there is a sign or sprite in front of the player
; if so, it is stored in [hSpriteIndexOrTextID]
; if not, [hSpriteIndexOrTextID] is set to 0
IsSpriteOrSignInFrontOfPlayer:: ; 0b23 (0:0b23)
	xor a
	ld [hSpriteIndexOrTextID],a
	ld a,[wNumSigns]
	and a
	jr z,.extendRangeOverCounter
; if there are signs
	predef GetTileAndCoordsInFrontOfPlayer ; get the coordinates in front of the player in de
	ld hl,wSignCoords
	ld a,[wNumSigns]
	ld b,a
	ld c,0
.signLoop
	inc c
	ld a,[hli] ; sign Y
	cp d
	jr z,.yCoordMatched
	inc hl
	jr .retry
.yCoordMatched
	ld a,[hli] ; sign X
	cp e
	jr nz,.retry
.xCoordMatched
; found sign
	push hl
	push bc
	ld hl,wSignTextIDs
	ld b,0
	dec c
	add hl,bc
	ld a,[hl]
	ld [hSpriteIndexOrTextID],a ; store sign text ID
	pop bc
	pop hl
	ret
.retry
	dec b
	jr nz,.signLoop
; check if the player is front of a counter in a pokemon center, pokemart, etc. and if so, extend the range at which he can talk to the NPC
.extendRangeOverCounter
	predef GetTileAndCoordsInFrontOfPlayer ; get the tile in front of the player in c
	ld hl,wTileSetTalkingOverTiles ; list of tiles that extend talking range (counter tiles)
	ld b,3
	ld d,$20 ; talking range in pixels (long range)
.counterTilesLoop
	ld a,[hli]
	cp c
	jr z,IsSpriteInFrontOfPlayer2 ; jumps if the tile in front of the player is a counter tile
	dec b
	jr nz,.counterTilesLoop

; part of the above function, but sometimes its called on its own, when signs are irrelevant
; the caller must zero [hSpriteIndexOrTextID]
IsSpriteInFrontOfPlayer:: ; 0b6b (0:0b6b)
	ld d,$10 ; talking range in pixels (normal range)
IsSpriteInFrontOfPlayer2:: ; 0b6d (0:0b6d)
	lb bc, $3c, $40 ; Y and X position of player sprite
	ld a,[wSpriteStateData1 + 9] ; direction the player is facing
.checkIfPlayerFacingUp
	cp SPRITE_FACING_UP
	jr nz,.checkIfPlayerFacingDown
; facing up
	ld a,b
	sub d
	ld b,a
	ld a,PLAYER_DIR_UP
	jr .doneCheckingDirection
.checkIfPlayerFacingDown
	cp SPRITE_FACING_DOWN
	jr nz,.checkIfPlayerFacingRight
; facing down
	ld a,b
	add d
	ld b,a
	ld a,PLAYER_DIR_DOWN
	jr .doneCheckingDirection
.checkIfPlayerFacingRight
	cp SPRITE_FACING_RIGHT
	jr nz,.playerFacingLeft
; facing right
	ld a,c
	add d
	ld c,a
	ld a,PLAYER_DIR_RIGHT
	jr .doneCheckingDirection
.playerFacingLeft
; facing left
	ld a,c
	sub d
	ld c,a
	ld a,PLAYER_DIR_LEFT
.doneCheckingDirection
	ld [wPlayerDirection],a
	ld a,[wNumSprites] ; number of sprites
	and a
	ret z
; if there are sprites
	ld hl,wSpriteStateData1 + $10
	ld d,a
	ld e,$01
.spriteLoop
	push hl
	ld a,[hli] ; image (0 if no sprite)
	and a
	jr z,.nextSprite
	inc l
	ld a,[hli] ; sprite visibility
	inc a
	jr z,.nextSprite
	inc l
	ld a,[hli] ; Y location
	cp b
	jr nz,.nextSprite
	inc l
	ld a,[hl] ; X location
	cp c
	jr z,.foundSpriteInFrontOfPlayer
.nextSprite
	pop hl
	ld a,l
	add a,$10
	ld l,a
	inc e
	dec d
	jr nz,.spriteLoop
	ret
.foundSpriteInFrontOfPlayer
	pop hl
	ld a,l
	and a,$f0
	inc a
	ld l,a ; hl = $c1x1
	set 7,[hl] ; set flag to make the sprite face the player
	ld a,e
	ld [hSpriteIndexOrTextID],a
	ret

; function to check if the player will jump down a ledge and check if the tile ahead is passable (when not surfing)
; sets the carry flag if there is a collision, and unsets it if there isn't a collision
CollisionCheckOnLand:: ; 0bd1 (0:0bd1)
	ld a,[wd736]
	bit 6,a ; is the player jumping?
	jr nz,.noCollision
; if not jumping a ledge
	ld a,[wSimulatedJoypadStatesIndex]
	and a
	jr nz,.noCollision ; no collisions when the player's movements are being controlled by the game
	ld a,[wPlayerDirection] ; the direction that the player is trying to go in
	ld d,a
	ld a,[wSpriteStateData1 + 12] ; the player sprite's collision data (bit field) (set in the sprite movement code)
	and d ; check if a sprite is in the direction the player is trying to go
	jr nz,.collision
	xor a
	ld [hSpriteIndexOrTextID],a
	call IsSpriteInFrontOfPlayer ; check for sprite collisions again? when does the above check fail to detect a sprite collision?
	ld a,[hSpriteIndexOrTextID]
	and a ; was there a sprite collision?
	jr nz,.collision
; if no sprite collision
	ld hl,TilePairCollisionsLand
	call CheckForJumpingAndTilePairCollisions
	jr c,.collision
	call CheckTilePassable
	jr nc,.noCollision
.collision
	ld a,[wChannelSoundIDs + CH4]
	cp a,SFX_COLLISION ; check if collision sound is already playing
	jr z,.setCarry
	ld a,SFX_COLLISION
	call PlaySound ; play collision sound (if it's not already playing)
.setCarry
	scf
	ret
.noCollision
	and a
	ret

; function that checks if the tile in front of the player is passable
; clears carry if it is, sets carry if not
CheckTilePassable:: ; 0c10 (0:0c10)
	predef GetTileAndCoordsInFrontOfPlayer ; get tile in front of player
	ld a,[wTileInFrontOfPlayer] ; tile in front of player
	ld c,a
	ld hl,wTileSetCollisionPtr ; pointer to list of passable tiles
	ld a,[hli]
	ld h,[hl]
	ld l,a ; hl now points to passable tiles
.loop
	ld a,[hli]
	cp a,$ff
	jr z,.tileNotPassable
	cp c
	ret z
	jr .loop
.tileNotPassable
	scf
	ret

; check if the player is going to jump down a small ledge
; and check for collisions that only occur between certain pairs of tiles
; Input: hl - address of directional collision data
; sets carry if there is a collision and unsets carry if not
CheckForJumpingAndTilePairCollisions:: ; 0c2a (0:0c2a)
	push hl
	predef GetTileAndCoordsInFrontOfPlayer ; get the tile in front of the player
	push de
	push bc
	callba HandleLedges ; check if the player is trying to jump a ledge
	pop bc
	pop de
	pop hl
	and a
	ld a,[wd736]
	bit 6,a ; is the player jumping?
	ret nz
; if not jumping

CheckForTilePairCollisions2:: ; 0c44 (0:0c44)
	aCoord 8, 9 ; tile the player is on
	ld [wTilePlayerStandingOn],a

CheckForTilePairCollisions:: ; 0c4a (0:0c4a)
	ld a,[wTileInFrontOfPlayer]
	ld c,a
.tilePairCollisionLoop
	ld a,[wCurMapTileset] ; tileset number
	ld b,a
	ld a,[hli]
	cp a,$ff
	jr z,.noMatch
	cp b
	jr z,.tilesetMatches
	inc hl
.retry
	inc hl
	jr .tilePairCollisionLoop
.tilesetMatches
	ld a,[wTilePlayerStandingOn] ; tile the player is on
	ld b,a
	ld a,[hl]
	cp b
	jr z,.currentTileMatchesFirstInPair
	inc hl
	ld a,[hl]
	cp b
	jr z,.currentTileMatchesSecondInPair
	jr .retry
.currentTileMatchesFirstInPair
	inc hl
	ld a,[hl]
	cp c
	jr z,.foundMatch
	jr .tilePairCollisionLoop
.currentTileMatchesSecondInPair
	dec hl
	ld a,[hli]
	cp c
	inc hl
	jr nz,.tilePairCollisionLoop
.foundMatch
	scf
	ret
.noMatch
	and a
	ret

; FORMAT: tileset number, tile 1, tile 2
; terminated by 0xFF
; these entries indicate that the player may not cross between tile 1 and tile 2
; it's mainly used to simulate differences in elevation

TilePairCollisionsLand:: ; 0c7e (0:0c7e)
	db CAVERN, $20, $05
	db CAVERN, $41, $05
	db FOREST, $30, $2E
	db CAVERN, $2A, $05
	db CAVERN, $05, $21
	db FOREST, $52, $2E
	db FOREST, $55, $2E
	db FOREST, $56, $2E
	db FOREST, $20, $2E
	db FOREST, $5E, $2E
	db FOREST, $5F, $2E
	db $FF

TilePairCollisionsWater:: ; 0ca0 (0:0ca0)
	db FOREST, $14, $2E
	db FOREST, $48, $2E
	db CAVERN, $14, $05
	db $FF

; this builds a tile map from the tile block map based on the current X/Y coordinates of the player's character
LoadCurrentMapView:: ; 0caa (0:0caa)
	ld a,[H_LOADEDROMBANK]
	push af
	ld a,[wTileSetBank] ; tile data ROM bank
	ld [H_LOADEDROMBANK],a
	ld [MBC1RomBank],a ; switch to ROM bank that contains tile data
	ld a,[wCurrentTileBlockMapViewPointer] ; address of upper left corner of current map view
	ld e,a
	ld a,[wCurrentTileBlockMapViewPointer + 1]
	ld d,a
	ld hl,wTileMapBackup
	ld b,$05
.rowLoop ; each loop iteration fills in one row of tile blocks
	push hl
	push de
	ld c,$06
.rowInnerLoop ; loop to draw each tile block of the current row
	push bc
	push de
	push hl
	ld a,[de]
	ld c,a ; tile block number
	call DrawTileBlock
	pop hl
	pop de
	pop bc
	inc hl
	inc hl
	inc hl
	inc hl
	inc de
	dec c
	jr nz,.rowInnerLoop
; update tile block map pointer to next row's address
	pop de
	ld a,[wCurMapWidth]
	add a,MAP_BORDER * 2
	add e
	ld e,a
	jr nc,.noCarry
	inc d
.noCarry
; update tile map pointer to next row's address
	pop hl
	ld a,$60
	add l
	ld l,a
	jr nc,.noCarry2
	inc h
.noCarry2
	dec b
	jr nz,.rowLoop
	ld hl,wTileMapBackup
	ld bc,$0000
.adjustForYCoordWithinTileBlock
	ld a,[wYBlockCoord]
	and a
	jr z,.adjustForXCoordWithinTileBlock
	ld bc,$0030
	add hl,bc
.adjustForXCoordWithinTileBlock
	ld a,[wXBlockCoord]
	and a
	jr z,.copyToVisibleAreaBuffer
	ld bc,$0002
	add hl,bc
.copyToVisibleAreaBuffer
	coord de, 0, 0 ; base address for the tiles that are directly transferred to VRAM during V-blank
	ld b, SCREEN_HEIGHT
.rowLoop2
	ld c, SCREEN_WIDTH
.rowInnerLoop2
	ld a,[hli]
	ld [de],a
	inc de
	dec c
	jr nz,.rowInnerLoop2
	ld a,$04
	add l
	ld l,a
	jr nc,.noCarry3
	inc h
.noCarry3
	dec b
	jr nz,.rowLoop2
	pop af
	ld [H_LOADEDROMBANK],a
	ld [MBC1RomBank],a ; restore previous ROM bank
	ret

AdvancePlayerSprite:: ; 0d27 (0:0d27)
	ld a,[wSpriteStateData1 + 3] ; delta Y
	ld b,a
	ld a,[wSpriteStateData1 + 5] ; delta X
	ld c,a
	ld hl,wWalkCounter ; walking animation counter
	dec [hl]
	jr nz,.afterUpdateMapCoords
; if it's the end of the animation, update the player's map coordinates
	ld a,[wYCoord]
	add b
	ld [wYCoord],a
	ld a,[wXCoord]
	add c
	ld [wXCoord],a
.afterUpdateMapCoords
	ld a,[wWalkCounter] ; walking animation counter
	cp a,$07
	jp nz,.scrollBackgroundAndSprites
; if this is the first iteration of the animation
	ld a,c
	cp a,$01
	jr nz,.checkIfMovingWest
; moving east
	ld a,[wMapViewVRAMPointer]
	ld e,a
	and a,$e0
	ld d,a
	ld a,e
	add a,$02
	and a,$1f
	or d
	ld [wMapViewVRAMPointer],a
	jr .adjustXCoordWithinBlock
.checkIfMovingWest
	cp a,$ff
	jr nz,.checkIfMovingSouth
; moving west
	ld a,[wMapViewVRAMPointer]
	ld e,a
	and a,$e0
	ld d,a
	ld a,e
	sub a,$02
	and a,$1f
	or d
	ld [wMapViewVRAMPointer],a
	jr .adjustXCoordWithinBlock
.checkIfMovingSouth
	ld a,b
	cp a,$01
	jr nz,.checkIfMovingNorth
; moving south
	ld a,[wMapViewVRAMPointer]
	add a,$40
	ld [wMapViewVRAMPointer],a
	jr nc,.adjustXCoordWithinBlock
	ld a,[wMapViewVRAMPointer + 1]
	inc a
	and a,$03
	or a,$98
	ld [wMapViewVRAMPointer + 1],a
	jr .adjustXCoordWithinBlock
.checkIfMovingNorth
	cp a,$ff
	jr nz,.adjustXCoordWithinBlock
; moving north
	ld a,[wMapViewVRAMPointer]
	sub a,$40
	ld [wMapViewVRAMPointer],a
	jr nc,.adjustXCoordWithinBlock
	ld a,[wMapViewVRAMPointer + 1]
	dec a
	and a,$03
	or a,$98
	ld [wMapViewVRAMPointer + 1],a
.adjustXCoordWithinBlock
	ld a,c
	and a
	jr z,.pointlessJump ; mistake?
.pointlessJump
	ld hl,wXBlockCoord
	ld a,[hl]
	add c
	ld [hl],a
	cp a,$02
	jr nz,.checkForMoveToWestBlock
; moved into the tile block to the east
	xor a
	ld [hl],a
	ld hl,wXOffsetSinceLastSpecialWarp
	inc [hl]
	ld de,wCurrentTileBlockMapViewPointer
	call MoveTileBlockMapPointerEast
	jr .updateMapView
.checkForMoveToWestBlock
	cp a,$ff
	jr nz,.adjustYCoordWithinBlock
; moved into the tile block to the west
	ld a,$01
	ld [hl],a
	ld hl,wXOffsetSinceLastSpecialWarp
	dec [hl]
	ld de,wCurrentTileBlockMapViewPointer
	call MoveTileBlockMapPointerWest
	jr .updateMapView
.adjustYCoordWithinBlock
	ld hl,wYBlockCoord
	ld a,[hl]
	add b
	ld [hl],a
	cp a,$02
	jr nz,.checkForMoveToNorthBlock
; moved into the tile block to the south
	xor a
	ld [hl],a
	ld hl,wYOffsetSinceLastSpecialWarp
	inc [hl]
	ld de,wCurrentTileBlockMapViewPointer
	ld a,[wCurMapWidth]
	call MoveTileBlockMapPointerSouth
	jr .updateMapView
.checkForMoveToNorthBlock
	cp a,$ff
	jr nz,.updateMapView
; moved into the tile block to the north
	ld a,$01
	ld [hl],a
	ld hl,wYOffsetSinceLastSpecialWarp
	dec [hl]
	ld de,wCurrentTileBlockMapViewPointer
	ld a,[wCurMapWidth]
	call MoveTileBlockMapPointerNorth
.updateMapView
	call LoadCurrentMapView
	ld a,[wSpriteStateData1 + 3] ; delta Y
	cp a,$01
	jr nz,.checkIfMovingNorth2
; if moving south
	call ScheduleSouthRowRedraw
	jr .scrollBackgroundAndSprites
.checkIfMovingNorth2
	cp a,$ff
	jr nz,.checkIfMovingEast2
; if moving north
	call ScheduleNorthRowRedraw
	jr .scrollBackgroundAndSprites
.checkIfMovingEast2
	ld a,[wSpriteStateData1 + 5] ; delta X
	cp a,$01
	jr nz,.checkIfMovingWest2
; if moving east
	call ScheduleEastColumnRedraw
	jr .scrollBackgroundAndSprites
.checkIfMovingWest2
	cp a,$ff
	jr nz,.scrollBackgroundAndSprites
; if moving west
	call ScheduleWestColumnRedraw
.scrollBackgroundAndSprites
	ld a,[wSpriteStateData1 + 3] ; delta Y
	ld b,a
	ld a,[wSpriteStateData1 + 5] ; delta X
	ld c,a
	sla b
	sla c
	ld a,[hSCY]
	add b
	ld [hSCY],a ; update background scroll Y
	ld a,[hSCX]
	add c
	ld [hSCX],a ; update background scroll X
; shift all the sprites in the direction opposite of the player's motion
; so that the player appears to move relative to them
	ld hl,wSpriteStateData1 + $14
	ld a,[wNumSprites] ; number of sprites
	and a ; are there any sprites?
	jr z,.done
	ld e,a
.spriteShiftLoop
	ld a,[hl]
	sub b
	ld [hli],a
	inc l
	ld a,[hl]
	sub c
	ld [hl],a
	ld a,$0e
	add l
	ld l,a
	dec e
	jr nz,.spriteShiftLoop
.done
	ret

; the following four functions are used to move the pointer to the upper left
; corner of the tile block map in the direction of motion

MoveTileBlockMapPointerEast:: ; 0e65 (0:0e65)
	ld a,[de]
	add a,$01
	ld [de],a
	ret nc
	inc de
	ld a,[de]
	inc a
	ld [de],a
	ret

MoveTileBlockMapPointerWest:: ; 0e6f (0:0e6f)
	ld a,[de]
	sub a,$01
	ld [de],a
	ret nc
	inc de
	ld a,[de]
	dec a
	ld [de],a
	ret

MoveTileBlockMapPointerSouth:: ; 0e79 (0:0e79)
	add a,MAP_BORDER * 2
	ld b,a
	ld a,[de]
	add b
	ld [de],a
	ret nc
	inc de
	ld a,[de]
	inc a
	ld [de],a
	ret

MoveTileBlockMapPointerNorth:: ; 0e85 (0:0e85)
	add a,MAP_BORDER * 2
	ld b,a
	ld a,[de]
	sub b
	ld [de],a
	ret nc
	inc de
	ld a,[de]
	dec a
	ld [de],a
	ret

; the following 6 functions are used to tell the V-blank handler to redraw
; the portion of the map that was newly exposed due to the player's movement

ScheduleNorthRowRedraw:: ; 0e91 (0:0e91)
	coord hl, 0, 0
	call CopyToRedrawRowOrColumnSrcTiles
	ld a,[wMapViewVRAMPointer]
	ld [hRedrawRowOrColumnDest],a
	ld a,[wMapViewVRAMPointer + 1]
	ld [hRedrawRowOrColumnDest + 1],a
	ld a,REDRAW_ROW
	ld [hRedrawRowOrColumnMode],a
	ret

CopyToRedrawRowOrColumnSrcTiles:: ; 0ea6 (0:0ea6)
	ld de,wRedrawRowOrColumnSrcTiles
	ld c,2 * SCREEN_WIDTH
.loop
	ld a,[hli]
	ld [de],a
	inc de
	dec c
	jr nz,.loop
	ret

ScheduleSouthRowRedraw:: ; 0eb2 (0:0eb2)
	coord hl, 0, 16
	call CopyToRedrawRowOrColumnSrcTiles
	ld a,[wMapViewVRAMPointer]
	ld l,a
	ld a,[wMapViewVRAMPointer + 1]
	ld h,a
	ld bc,$0200
	add hl,bc
	ld a,h
	and a,$03
	or a,$98
	ld [hRedrawRowOrColumnDest + 1],a
	ld a,l
	ld [hRedrawRowOrColumnDest],a
	ld a,REDRAW_ROW
	ld [hRedrawRowOrColumnMode],a
	ret

ScheduleEastColumnRedraw:: ; 0ed3 (0:0ed3)
	coord hl, 18, 0
	call ScheduleColumnRedrawHelper
	ld a,[wMapViewVRAMPointer]
	ld c,a
	and a,$e0
	ld b,a
	ld a,c
	add a,18
	and a,$1f
	or b
	ld [hRedrawRowOrColumnDest],a
	ld a,[wMapViewVRAMPointer + 1]
	ld [hRedrawRowOrColumnDest + 1],a
	ld a,REDRAW_COL
	ld [hRedrawRowOrColumnMode],a
	ret

ScheduleColumnRedrawHelper:: ; 0ef2 (0:0ef2)
	ld de,wRedrawRowOrColumnSrcTiles
	ld c,SCREEN_HEIGHT
.loop
	ld a,[hli]
	ld [de],a
	inc de
	ld a,[hl]
	ld [de],a
	inc de
	ld a,19
	add l
	ld l,a
	jr nc,.noCarry
	inc h
.noCarry
	dec c
	jr nz,.loop
	ret

ScheduleWestColumnRedraw:: ; 0f08 (0:0f08)
	coord hl, 0, 0
	call ScheduleColumnRedrawHelper
	ld a,[wMapViewVRAMPointer]
	ld [hRedrawRowOrColumnDest],a
	ld a,[wMapViewVRAMPointer + 1]
	ld [hRedrawRowOrColumnDest + 1],a
	ld a,REDRAW_COL
	ld [hRedrawRowOrColumnMode],a
	ret

; function to write the tiles that make up a tile block to memory
; Input: c = tile block ID, hl = destination address
DrawTileBlock:: ; 0f1d (0:0f1d)
	push hl
	ld a,[wTileSetBlocksPtr] ; pointer to tiles
	ld l,a
	ld a,[wTileSetBlocksPtr + 1]
	ld h,a
	ld a,c
	swap a
	ld b,a
	and a,$f0
	ld c,a
	ld a,b
	and a,$0f
	ld b,a ; bc = tile block ID * 0x10
	add hl,bc
	ld d,h
	ld e,l ; de = address of the tile block's tiles
	pop hl
	ld c,$04 ; 4 loop iterations
.loop ; each loop iteration, write 4 tile numbers
	push bc
	ld a,[de]
	ld [hli],a
	inc de
	ld a,[de]
	ld [hli],a
	inc de
	ld a,[de]
	ld [hli],a
	inc de
	ld a,[de]
	ld [hl],a
	inc de
	ld bc,$0015
	add hl,bc
	pop bc
	dec c
	jr nz,.loop
	ret

; function to update joypad state and simulate button presses
JoypadOverworld:: ; 0f4d (0:0f4d)
	xor a
	ld [wSpriteStateData1 + 3],a
	ld [wSpriteStateData1 + 5],a
	call RunMapScript
	call Joypad
	ld a,[wFlags_D733]
	bit 3,a ; check if a trainer wants a challenge
	jr nz,.notForcedDownwards
	ld a,[wCurMap]
	cp a,ROUTE_17 ; Cycling Road
	jr nz,.notForcedDownwards
	ld a,[hJoyHeld]
	and a,D_DOWN | D_UP | D_LEFT | D_RIGHT | B_BUTTON | A_BUTTON
	jr nz,.notForcedDownwards
	ld a,D_DOWN
	ld [hJoyHeld],a ; on the cycling road, if there isn't a trainer and the player isn't pressing buttons, simulate a down press
.notForcedDownwards
	ld a,[wd730]
	bit 7,a
	ret z
; if simulating button presses
	ld a,[hJoyHeld]
	ld b,a
	ld a,[wOverrideSimulatedJoypadStatesMask] ; bit mask for button presses that override simulated ones
	and b
	ret nz ; return if the simulated button presses are overridden
	ld hl,wSimulatedJoypadStatesIndex
	dec [hl]
	ld a,[hl]
	cp a,$ff
	jr z,.doneSimulating ; if the end of the simulated button presses has been reached
	ld hl,wSimulatedJoypadStatesEnd
	add l
	ld l,a
	jr nc,.noCarry
	inc h
.noCarry
	ld a,[hl]
	ld [hJoyHeld],a ; store simulated button press in joypad state
	and a
	ret nz
	ld [hJoyPressed],a
	ld [hJoyReleased],a
	ret
; if done simulating button presses
.doneSimulating
	xor a
	ld [wWastedByteCD3A],a
	ld [wSimulatedJoypadStatesIndex],a
	ld [wSimulatedJoypadStatesEnd],a
	ld [wJoyIgnore],a
	ld [hJoyHeld],a
	ld hl,wd736
	ld a,[hl]
	and a,$f8
	ld [hl],a
	ld hl,wd730
	res 7,[hl]
	ret

; function to check the tile ahead to determine if the character should get on land or keep surfing
; sets carry if there is a collision and clears carry otherwise
; It seems that this function has a bug in it, but due to luck, it doesn't
; show up. After detecting a sprite collision, it jumps to the code that
; checks if the next tile is passable instead of just directly jumping to the
; "collision detected" code. However, it doesn't store the next tile in c,
; so the old value of c is used. 2429 is always called before this function,
; and 2429 always sets c to 0xF0. There is no 0xF0 background tile, so it
; is considered impassable and it is detected as a collision.
CollisionCheckOnWater:: ; 0fb7 (0:0fb7)
	ld a,[wd730]
	bit 7,a
	jp nz,.noCollision ; return and clear carry if button presses are being simulated
	ld a,[wPlayerDirection] ; the direction that the player is trying to go in
	ld d,a
	ld a,[wSpriteStateData1 + 12] ; the player sprite's collision data (bit field) (set in the sprite movement code)
	and d ; check if a sprite is in the direction the player is trying to go
	jr nz,.checkIfNextTileIsPassable ; bug?
	ld hl,TilePairCollisionsWater
	call CheckForJumpingAndTilePairCollisions
	jr c,.collision
	predef GetTileAndCoordsInFrontOfPlayer ; get tile in front of player (puts it in c and [wTileInFrontOfPlayer])
	ld a,[wTileInFrontOfPlayer] ; tile in front of player
	cp a,$14 ; water tile
	jr z,.noCollision ; keep surfing if it's a water tile
	cp a,$32 ; either the left tile of the S.S. Anne boarding platform or the tile on eastern coastlines (depending on the current tileset)
	jr z,.checkIfVermilionDockTileset
	cp a,$48 ; tile on right on coast lines in Safari Zone
	jr z,.noCollision ; keep surfing
; check if the [land] tile in front of the player is passable
.checkIfNextTileIsPassable
	ld hl,wTileSetCollisionPtr ; pointer to list of passable tiles
	ld a,[hli]
	ld h,[hl]
	ld l,a
.loop
	ld a,[hli]
	cp a,$ff
	jr z,.collision
	cp c
	jr z,.stopSurfing ; stop surfing if the tile is passable
	jr .loop
.collision
	ld a,[wChannelSoundIDs + CH4]
	cp a,SFX_COLLISION ; check if collision sound is already playing
	jr z,.setCarry
	ld a,SFX_COLLISION
	call PlaySound ; play collision sound (if it's not already playing)
.setCarry
	scf
	jr .done
.noCollision
	and a
.done
	ret
.stopSurfing
	xor a
	ld [wWalkBikeSurfState],a
	call LoadPlayerSpriteGraphics
	call PlayDefaultMusic
	jr .noCollision
.checkIfVermilionDockTileset
	ld a, [wCurMapTileset] ; tileset
	cp SHIP_PORT ; Vermilion Dock tileset
	jr nz, .noCollision ; keep surfing if it's not the boarding platform tile
	jr .stopSurfing ; if it is the boarding platform tile, stop surfing

; function to run the current map's script
RunMapScript:: ; 101b (0:101b)
	push hl
	push de
	push bc
	callba TryPushingBoulder
	ld a,[wFlags_0xcd60]
	bit 1,a ; play boulder dust animation
	jr z,.afterBoulderEffect
	callba DoBoulderDustAnimation
.afterBoulderEffect
	pop bc
	pop de
	pop hl
	call RunNPCMovementScript
	ld a,[wCurMap] ; current map number
	call SwitchToMapRomBank ; change to the ROM bank the map's data is in
	ld hl,wMapScriptPtr
	ld a,[hli]
	ld h,[hl]
	ld l,a
	ld de,.return
	push de
	jp [hl] ; jump to script
.return
	ret

LoadWalkingPlayerSpriteGraphics:: ; 104d (0:104d)
	ld de,RedSprite
	ld hl,vNPCSprites
	jr LoadPlayerSpriteGraphicsCommon

LoadSurfingPlayerSpriteGraphics:: ; 1055 (0:1055)
	ld de,SeelSprite
	ld hl,vNPCSprites
	jr LoadPlayerSpriteGraphicsCommon

LoadBikePlayerSpriteGraphics:: ; 105d (0:105d)
	ld de,RedCyclingSprite
	ld hl,vNPCSprites

LoadPlayerSpriteGraphicsCommon:: ; 1063 (0:1063)
	push de
	push hl
	lb bc, BANK(RedSprite), $0c
	call CopyVideoData
	pop hl
	pop de
	ld a,$c0
	add e
	ld e,a
	jr nc,.noCarry
	inc d
.noCarry
	set 3,h
	lb bc, BANK(RedSprite), $0c
	jp CopyVideoData

; function to load data from the map header
LoadMapHeader:: ; 107c (0:107c)
	callba MarkTownVisitedAndLoadMissableObjects
	ld a,[wCurMapTileset]
	ld [wUnusedD119],a
	ld a,[wCurMap]
	call SwitchToMapRomBank
	ld a,[wCurMapTileset]
	ld b,a
	res 7,a
	ld [wCurMapTileset],a
	ld [hPreviousTileset],a
	bit 7,b
	ret nz
	ld hl,MapHeaderPointers
	ld a,[wCurMap]
	sla a
	jr nc,.noCarry1
	inc h
.noCarry1
	add l
	ld l,a
	jr nc,.noCarry2
	inc h
.noCarry2
	ld a,[hli]
	ld h,[hl]
	ld l,a ; hl = base of map header
; copy the first 10 bytes (the fixed area) of the map data to D367-D370
	ld de,wCurMapTileset
	ld c,$0a
.copyFixedHeaderLoop
	ld a,[hli]
	ld [de],a
	inc de
	dec c
	jr nz,.copyFixedHeaderLoop
; initialize all the connected maps to disabled at first, before loading the actual values
	ld a,$ff
	ld [wMapConn1Ptr],a
	ld [wMapConn2Ptr],a
	ld [wMapConn3Ptr],a
	ld [wMapConn4Ptr],a
; copy connection data (if any) to WRAM
	ld a,[wMapConnections]
	ld b,a
.checkNorth
	bit 3,b
	jr z,.checkSouth
	ld de,wMapConn1Ptr
	call CopyMapConnectionHeader
.checkSouth
	bit 2,b
	jr z,.checkWest
	ld de,wMapConn2Ptr
	call CopyMapConnectionHeader
.checkWest
	bit 1,b
	jr z,.checkEast
	ld de,wMapConn3Ptr
	call CopyMapConnectionHeader
.checkEast
	bit 0,b
	jr z,.getObjectDataPointer
	ld de,wMapConn4Ptr
	call CopyMapConnectionHeader
.getObjectDataPointer
	ld a,[hli]
	ld [wObjectDataPointerTemp],a
	ld a,[hli]
	ld [wObjectDataPointerTemp + 1],a
	push hl
	ld a,[wObjectDataPointerTemp]
	ld l,a
	ld a,[wObjectDataPointerTemp + 1]
	ld h,a ; hl = base of object data
	ld de,wMapBackgroundTile
	ld a,[hli]
	ld [de],a
.loadWarpData
	ld a,[hli]
	ld [wNumberOfWarps],a
	and a
	jr z,.loadSignData
	ld c,a
	ld de,wWarpEntries
.warpLoop ; one warp per loop iteration
	ld b,$04
.warpInnerLoop
	ld a,[hli]
	ld [de],a
	inc de
	dec b
	jr nz,.warpInnerLoop
	dec c
	jr nz,.warpLoop
.loadSignData
	ld a,[hli] ; number of signs
	ld [wNumSigns],a
	and a ; are there any signs?
	jr z,.loadSpriteData ; if not, skip this
	ld c,a
	ld de,wSignTextIDs
	ld a,d
	ld [hSignCoordPointer],a
	ld a,e
	ld [hSignCoordPointer + 1],a
	ld de,wSignCoords
.signLoop
	ld a,[hli]
	ld [de],a
	inc de
	ld a,[hli]
	ld [de],a
	inc de
	push de
	ld a,[hSignCoordPointer]
	ld d,a
	ld a,[hSignCoordPointer + 1]
	ld e,a
	ld a,[hli]
	ld [de],a
	inc de
	ld a,d
	ld [hSignCoordPointer],a
	ld a,e
	ld [hSignCoordPointer + 1],a
	pop de
	dec c
	jr nz,.signLoop
.loadSpriteData
	ld a,[wd72e]
	bit 5,a ; did a battle happen immediately before this?
	jp nz,.finishUp ; if so, skip this because battles don't destroy this data
	ld a,[hli]
	ld [wNumSprites],a ; save the number of sprites
	push hl
; zero C110-C1FF and C210-C2FF
	ld hl,wSpriteStateData1 + $10
	ld de,wSpriteStateData2 + $10
	xor a
	ld b,$f0
.zeroSpriteDataLoop
	ld [hli],a
	ld [de],a
	inc e
	dec b
	jr nz,.zeroSpriteDataLoop
; initialize all C100-C1FF sprite entries to disabled (other than player's)
	ld hl,wSpriteStateData1 + $12
	ld de,$0010
	ld c,$0f
.disableSpriteEntriesLoop
	ld [hl],$ff
	add hl,de
	dec c
	jr nz,.disableSpriteEntriesLoop
	pop hl
	ld de,wSpriteStateData1 + $10
	ld a,[wNumSprites] ; number of sprites
	and a ; are there any sprites?
	jp z,.finishUp ; if there are no sprites, skip the rest
	ld b,a
	ld c,$00
.loadSpriteLoop
	ld a,[hli]
	ld [de],a ; store picture ID at C1X0
	inc d
	ld a,$04
	add e
	ld e,a
	ld a,[hli]
	ld [de],a ; store Y position at C2X4
	inc e
	ld a,[hli]
	ld [de],a ; store X position at C2X5
	inc e
	ld a,[hli]
	ld [de],a ; store movement byte 1 at C2X6
	ld a,[hli]
	ld [hLoadSpriteTemp1],a ; save movement byte 2
	ld a,[hli]
	ld [hLoadSpriteTemp2],a ; save text ID and flags byte
	push bc
	push hl
	ld b,$00
	ld hl,wMapSpriteData
	add hl,bc
	ld a,[hLoadSpriteTemp1]
	ld [hli],a ; store movement byte 2 in byte 0 of sprite entry
	ld a,[hLoadSpriteTemp2]
	ld [hl],a ; this appears pointless, since the value is overwritten immediately after
	ld a,[hLoadSpriteTemp2]
	ld [hLoadSpriteTemp1],a
	and a,$3f
	ld [hl],a ; store text ID in byte 1 of sprite entry
	pop hl
	ld a,[hLoadSpriteTemp1]
	bit 6,a
	jr nz,.trainerSprite
	bit 7,a
	jr nz,.itemBallSprite
	jr .regularSprite
.trainerSprite
	ld a,[hli]
	ld [hLoadSpriteTemp1],a ; save trainer class
	ld a,[hli]
	ld [hLoadSpriteTemp2],a ; save trainer number (within class)
	push hl
	ld hl,wMapSpriteExtraData
	add hl,bc
	ld a,[hLoadSpriteTemp1]
	ld [hli],a ; store trainer class in byte 0 of the entry
	ld a,[hLoadSpriteTemp2]
	ld [hl],a ; store trainer number in byte 1 of the entry
	pop hl
	jr .nextSprite
.itemBallSprite
	ld a,[hli]
	ld [hLoadSpriteTemp1],a ; save item number
	push hl
	ld hl,wMapSpriteExtraData
	add hl,bc
	ld a,[hLoadSpriteTemp1]
	ld [hli],a ; store item number in byte 0 of the entry
	xor a
	ld [hl],a ; zero byte 1, since it is not used
	pop hl
	jr .nextSprite
.regularSprite
	push hl
	ld hl,wMapSpriteExtraData
	add hl,bc
; zero both bytes, since regular sprites don't use this extra space
	xor a
	ld [hli],a
	ld [hl],a
	pop hl
.nextSprite
	pop bc
	dec d
	ld a,$0a
	add e
	ld e,a
	inc c
	inc c
	dec b
	jp nz,.loadSpriteLoop
.finishUp
	predef LoadTilesetHeader
	callab LoadWildData
	pop hl ; restore hl from before going to the warp/sign/sprite data (this value was saved for seemingly no purpose)
	ld a,[wCurMapHeight] ; map height in 4x4 tile blocks
	add a ; double it
	ld [wCurrentMapHeight2],a ; store map height in 2x2 tile blocks
	ld a,[wCurMapWidth] ; map width in 4x4 tile blocks
	add a ; double it
	ld [wCurrentMapWidth2],a ; map width in 2x2 tile blocks
	ld a,[wCurMap]
	ld c,a
	ld b,$00
	ld a,[H_LOADEDROMBANK]
	push af
	ld a, BANK(MapSongBanks)
	ld [H_LOADEDROMBANK],a
	ld [MBC1RomBank],a
	ld hl, MapSongBanks
	add hl,bc
	add hl,bc
	ld a,[hli]
	ld [wMapMusicSoundID],a ; music 1
	ld a,[hl]
	ld [wMapMusicROMBank],a ; music 2
	pop af
	ld [H_LOADEDROMBANK],a
	ld [MBC1RomBank],a
	ret

; function to copy map connection data from ROM to WRAM
; Input: hl = source, de = destination
CopyMapConnectionHeader:: ; 1238 (0:1238)
	ld c,$0b
.loop
	ld a,[hli]
	ld [de],a
	inc de
	dec c
	jr nz,.loop
	ret

; function to load map data
LoadMapData:: ; 1241 (0:1241)
	ld a,[H_LOADEDROMBANK]
	push af
	call DisableLCD
	ld a,$98
	ld [wMapViewVRAMPointer + 1],a
	xor a
	ld [wMapViewVRAMPointer],a
	ld [hSCY],a
	ld [hSCX],a
	ld [wWalkCounter],a
	ld [wUnusedD119],a
	ld [wWalkBikeSurfStateCopy],a
	ld [wSpriteSetID],a
	call LoadTextBoxTilePatterns
	call LoadMapHeader
	callba InitMapSprites ; load tile pattern data for sprites
	call LoadTileBlockMap
	call LoadTilesetTilePatternData
	call LoadCurrentMapView
; copy current map view to VRAM
	coord hl, 0, 0
	ld de,vBGMap0
	ld b,18
.vramCopyLoop
	ld c,20
.vramCopyInnerLoop
	ld a,[hli]
	ld [de],a
	inc e
	dec c
	jr nz,.vramCopyInnerLoop
	ld a,32 - 20
	add e
	ld e,a
	jr nc,.noCarry
	inc d
.noCarry
	dec b
	jr nz,.vramCopyLoop
	ld a,$01
	ld [wUpdateSpritesEnabled],a
	call EnableLCD
	ld b, SET_PAL_OVERWORLD
	call RunPaletteCommand
	call LoadPlayerSpriteGraphics
	ld a,[wd732]
	and a,1 << 4 | 1 << 3 ; fly warp or dungeon warp
	jr nz,.restoreRomBank
	ld a,[wFlags_D733]
	bit 1,a
	jr nz,.restoreRomBank
	call UpdateMusic6Times
	call PlayDefaultMusicFadeOutCurrent
.restoreRomBank
	pop af
	ld [H_LOADEDROMBANK],a
	ld [MBC1RomBank],a
	ret

; function to switch to the ROM bank that a map is stored in
; Input: a = map number
SwitchToMapRomBank:: ; 12bc (0:12bc)
	push hl
	push bc
	ld c,a
	ld b,$00
	ld a,Bank(MapHeaderBanks)
	call BankswitchHome ; switch to ROM bank 3
	ld hl,MapHeaderBanks
	add hl,bc
	ld a,[hl]
	ld [$ffe8],a ; save map ROM bank
	call BankswitchBack
	ld a,[$ffe8]
	ld [H_LOADEDROMBANK],a
	ld [MBC1RomBank],a ; switch to map ROM bank
	pop bc
	pop hl
	ret

IgnoreInputForHalfSecond: ; 12da (0:12da)
	ld a, 30
	ld [wIgnoreInputCounter], a
	ld hl, wd730
	ld a, [hl]
	or $26
	ld [hl], a ; set ignore input bit
	ret

ResetUsingStrengthOutOfBattleBit: ; 12e7 (0:12e7)
	ld hl, wd728
	res 0, [hl]
	ret

ForceBikeOrSurf:: ; 12ed (0:12ed)
	ld b, BANK(RedSprite)
	ld hl, LoadPlayerSpriteGraphics
	call Bankswitch
	jp PlayDefaultMusic ; update map/player state?