diff options
author | yenatch <yenatch@gmail.com> | 2014-05-25 10:26:42 -0700 |
---|---|---|
committer | yenatch <yenatch@gmail.com> | 2014-05-25 10:26:42 -0700 |
commit | 1ee8de792ba5bf45290a4cb49b6bc3ee6aa539f3 (patch) | |
tree | d6d45ca25ff056cd8b2f15a18bc3a4aa8ae2c978 | |
parent | d307c854991f5eb24e12935e6906ad81ed244334 (diff) |
Move bank 0 into home.asm.
-rw-r--r-- | home.asm | 10171 | ||||
-rwxr-xr-x | main.asm | 10172 |
2 files changed, 10172 insertions, 10171 deletions
diff --git a/home.asm b/home.asm new file mode 100644 index 00000000..6a84e1a0 --- /dev/null +++ b/home.asm @@ -0,0 +1,10171 @@ +; The rst vectors are unused. +SECTION "rst00", ROM0[$00] + rst $38 +SECTION "rst08", ROM0[$08] + rst $38 +SECTION "rst10", ROM0[$10] + rst $38 +SECTION "rst18", ROM0[$18] + rst $38 +SECTION "rst20", ROM0[$20] + rst $38 +SECTION "rst28", ROM0[$28] + rst $38 +SECTION "rst30", ROM0[$30] + rst $38 +SECTION "rst38", ROM0[$38] + rst $38 + +; interrupts +SECTION "vblank", ROM0[$40] + jp VBlank +SECTION "lcdc", ROM0[$48] + rst $38 +SECTION "timer", ROM0[$50] + jp Timer +SECTION "serial", ROM0[$58] + jp Serial +SECTION "joypad", ROM0[$60] + reti + + +SECTION "bank0",ROM0[$61] + +DisableLCD:: + xor a + ld [rIF], a + ld a, [rIE] + ld b, a + res 0, a + ld [rIE], a + +.wait + ld a, [rLY] + cp LY_VBLANK + jr nz, .wait + + ld a, [rLCDC] + and $ff ^ rLCDC_ENABLE_MASK + ld [rLCDC], a + ld a, b + ld [rIE], a + ret + +EnableLCD:: + ld a, [rLCDC] + set rLCDC_ENABLE, a + ld [rLCDC], a + ret + +ClearSprites:: + xor a + ld hl, wOAMBuffer + ld b, 40 * 4 +.loop + ld [hli], a + dec b + jr nz, .loop + ret + +HideSprites:: + ld a, 160 + ld hl, wOAMBuffer + ld de, 4 + ld b, 40 +.loop + ld [hl], a + add hl, de + dec b + jr nz, .loop + ret + +FarCopyData:: +; Copy bc bytes from a:hl to de. + ld [wBuffer], a + ld a, [H_LOADEDROMBANK] + push af + ld a, [wBuffer] + ld [H_LOADEDROMBANK], a + ld [MBC3RomBank], a + call CopyData + pop af + ld [H_LOADEDROMBANK], a + ld [MBC3RomBank], a + ret + +CopyData:: +; Copy bc bytes from hl to de. + ld a, [hli] + ld [de], a + inc de + dec bc + ld a, c + or b + jr nz, CopyData + ret + + +SECTION "Entry", ROM0[$100] + nop + jp Start + + +SECTION "Start", ROM0[$150] + +Start:: + cp GBC + jr z, .gbc + xor a + jr .ok +.gbc + ld a, 0 +.ok + ld [wGBC], a + jp Init + + +ReadJoypad:: +; Poll joypad input. +; Unlike the hardware register, button +; presses are indicated by a set bit. + + ld a, 1 << 5 ; select direction keys + ld c, 0 + + ld [rJOYP], a + rept 6 + ld a, [rJOYP] + endr + cpl + and %1111 + swap a + ld b, a + + ld a, 1 << 4 ; select button keys + ld [rJOYP], a + rept 10 + ld a, [rJOYP] + endr + cpl + and %1111 + or b + + ld [H_JOYPADSTATE], a + + ld a, 1 << 4 + 1 << 5 ; deselect keys + ld [rJOYP], a + ret + +GetJoypadState:: +; Update the joypad state variables: +; [H_NEWLYRELEASEDBUTTONS] keys released since last time +; [H_NEWLYPRESSEDBUTTONS] keys pressed since last time +; [H_CURRENTPRESSEDBUTTONS] currently pressed keys + homecall _GetJoypadState + ret + + +INCLUDE "data/map_header_pointers.asm" + +HandleMidJump:: +; Handle the player jumping down +; a ledge in the overworld. + ld b, BANK(_HandleMidJump) + ld hl, _HandleMidJump + jp Bankswitch + +EnterMap:: +; Load a new map. + ld a, $ff + ld [wJoypadForbiddenButtonsMask], a + call LoadMapData + callba Func_c335 ; initialize map variables + ld hl, $d72c + bit 0, [hl] + jr z, .doNotCountSteps + ld a, 3 + ld [$d13c], a ; some kind of step counter (counts up to 3 steps?) +.doNotCountSteps + ld hl, $d72e + bit 5, [hl] ; did a battle happen immediately before this? + res 5, [hl] ; unset the "battle just happened" flag + call z, Func_12e7 + call nz, MapEntryAfterBattle + ld hl, $d732 + ld a, [hl] + and 1 << 4 | 1 << 3 + jr z, .didNotFlyOrTeleportIn + res 3, [hl] + callba Func_70510 ; display fly/teleport in graphical effect + call UpdateSprites +.didNotFlyOrTeleportIn + callba CheckForceBikeOrSurf ; handle currents in SF islands and forced bike riding in cycling road + ld hl, $d72d + res 5, [hl] + call UpdateSprites + ld hl, $d126 + set 5, [hl] + set 6, [hl] + xor a + ld [wJoypadForbiddenButtonsMask], a + +OverworldLoop:: + call DelayFrame +OverworldLoopLessDelay:: + call DelayFrame + call LoadGBPal + ld a,[$d736] + 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 GetJoypadStateOverworld ; get joypad state (which is possibly simulated) + callba SafariZoneCheck + ld a,[$da46] + and a + jp nz,WarpFound2 + ld hl,$d72d + bit 3,[hl] + res 3,[hl] + jp nz,WarpFound2 + ld a,[$d732] + and a,$18 + jp nz,HandleFlyOrTeleportAway + ld a,[W_CUROPPONENT] + and a + jp nz,.newBattle + ld a,[$d730] + bit 7,a ; are we simulating button presses? + jr z,.notSimulating + ld a,[H_CURRENTPRESSEDBUTTONS] + jr .checkIfStartIsPressed +.notSimulating + ld a,[H_NEWLYPRESSEDBUTTONS] +.checkIfStartIsPressed + bit 3,a ; start button + jr z,.startButtonNotPressed +; if START is pressed + xor a + ld [$ff8c],a ; the $2920 ID for the start menu is 0 + jp .displayDialogue +.startButtonNotPressed + bit 0,a ; A button + jp z,.checkIfDownButtonIsPressed +; if A is pressed + ld a,[$d730] + bit 2,a + jp nz,.noDirectionButtonsPressed + call Func_30fd + jr nz,.checkForOpponent + call Func_3eb5 ; check for hidden items, PC's, etc. + ld a,[$ffeb] + and a + jp z,OverworldLoop + call IsSpriteOrSignInFrontOfPlayer ; check for sign or sprite in front of the player + ld a,[$ff8c] ; $2920 ID for NPC/sign text, if any + and a + jp z,OverworldLoop +.displayDialogue + ld a,$35 + call Predef ; check what is in front of the player + call UpdateSprites ; move sprites + ld a,[wFlags_0xcd60] + bit 2,a + jr nz,.checkForOpponent + bit 0,a + jr nz,.checkForOpponent + FuncCoord 8, 9 ; $c45c + ld a,[Coord] + ld [$cf0e],a + call DisplayTextID ; display either the start menu or the NPC/sign text + ld a,[$cc47] + and a + jr z,.checkForOpponent + dec a + ld a,$00 + ld [$cc47],a + jr z,.changeMap + ld a,$52 + call Predef + ld a,[W_CURMAP] + ld [$d71a],a + call Func_62ce + ld a,[W_CURMAP] + call SwitchToMapRomBank ; switch to the ROM bank of the current map + ld hl,$d367 + set 7,[hl] +.changeMap + jp EnterMap +.checkForOpponent + ld a,[W_CUROPPONENT] + and a + jp nz,.newBattle + jp OverworldLoop +.noDirectionButtonsPressed + ld hl,wFlags_0xcd60 + res 2,[hl] + call UpdateSprites ; move sprites + ld a,$01 + ld [$cc4b],a + ld a,[$d528] ; the direction that was pressed last time + and a + jp z,OverworldLoop +; if a direction was pressed last time + ld [$d529],a ; save the last direction + xor a + ld [$d528],a ; zero the direction + jp OverworldLoop +.checkIfDownButtonIsPressed + ld a,[H_CURRENTPRESSEDBUTTONS] ; current joypad state + bit 7,a ; down button + jr z,.checkIfUpButtonIsPressed + ld a,$01 + ld [$c103],a + ld a,$04 + jr .handleDirectionButtonPress +.checkIfUpButtonIsPressed + bit 6,a ; up button + jr z,.checkIfLeftButtonIsPressed + ld a,$ff + ld [$c103],a + ld a,$08 + jr .handleDirectionButtonPress +.checkIfLeftButtonIsPressed + bit 5,a ; left button + jr z,.checkIfRightButtonIsPressed + ld a,$ff + ld [$c105],a + ld a,$02 + jr .handleDirectionButtonPress +.checkIfRightButtonIsPressed + bit 4,a ; right button + jr z,.noDirectionButtonsPressed + ld a,$01 + ld [$c105],a +.handleDirectionButtonPress + ld [$d52a],a ; new direction + ld a,[$d730] + bit 7,a ; are we simulating button presses? + jr nz,.noDirectionChange ; ignore direction changes if we are + ld a,[$cc4b] + and a + jr z,.noDirectionChange + ld a,[$d52a] ; new direction + ld b,a + ld a,[$d529] ; old direction + cp b + jr z,.noDirectionChange +; the code below is strange +; it computes whether or not the player did a 180 degree turn, but then overwrites the result +; also, it does a seemingly pointless loop afterwards + swap a ; put old direction in upper half + or b ; put new direction in lower half + cp a,$48 ; change dir from down to up + jr nz,.notDownToUp + ld a,$02 + ld [$d528],a + jr .oddLoop +.notDownToUp + cp a,$84 ; change dir from up to down + jr nz,.notUpToDown + ld a,$01 + ld [$d528],a + jr .oddLoop +.notUpToDown + cp a,$12 ; change dir from right to left + jr nz,.notRightToLeft + ld a,$04 + ld [$d528],a + jr .oddLoop +.notRightToLeft + cp a,$21 ; change dir from left to right + jr nz,.oddLoop + ld a,$08 + ld [$d528],a +.oddLoop + ld hl,wFlags_0xcd60 + set 2,[hl] + ld hl,$cc4b + dec [hl] + jr nz,.oddLoop + ld a,[$d52a] + ld [$d528],a + call NewBattle + jp c,.battleOccurred + jp OverworldLoop +.noDirectionChange + ld a,[$d52a] ; current direction + ld [$d528],a ; save direction + call UpdateSprites ; move sprites + ld a,[$d700] + cp a,$02 ; surfing + jr z,.surfing +; not surfing + call CollisionCheckOnLand + jr nc,.noCollision + push hl + ld hl,$d736 + bit 2,[hl] + pop hl + jp z,OverworldLoop + 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,[$d736] + bit 7,a + jr z,.noSpinning + callba LoadSpinnerArrowTiles ; spin while moving +.noSpinning + call UpdateSprites ; move sprites +.moveAhead2 + ld hl,wFlags_0xcd60 + res 2,[hl] + ld a,[$d700] + dec a ; riding a bike? + jr nz,.normalPlayerSpriteAdvancement + ld a,[$d736] + 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,[$d730] + bit 7,a + jr nz,.doneStepCounting ; if button presses are being simulated, don't count steps +; step counting + ld hl,$d13b ; step counter + dec [hl] + ld a,[$d72c] + bit 0,a + jr z,.doneStepCounting + ld hl,$d13c + dec [hl] + jr nz,.doneStepCounting + ld hl,$d72c + res 0,[hl] +.doneStepCounting + ld a,[$d790] + bit 7,a ; in the safari zone? + jr z,.notSafariZone + callba SafariZoneCheckSteps + ld a,[$da46] + and a + jp nz,WarpFound2 +.notSafariZone + ld a,[W_ISINBATTLE] + and a + jp nz,CheckWarpsNoCollision + ld a,$13 + call Predef ; decrement HP of poisoned pokemon + ld a,[$d12d] + and a + jp nz,HandleBlackOut ; if all pokemon fainted +.newBattle + call NewBattle + ld hl,$d736 + res 2,[hl] + jp nc,CheckWarpsNoCollision ; check for warps if there was no battle +.battleOccurred + ld hl,$d72d + res 6,[hl] + ld hl,W_FLAGS_D733 + res 3,[hl] + ld hl,$d126 + set 5,[hl] + set 6,[hl] + xor a + ld [H_CURRENTPRESSEDBUTTONS],a ; clear joypad state + ld a,[W_CURMAP] + cp a,CINNABAR_GYM + jr nz,.notCinnabarGym + ld hl,$d79b + set 7,[hl] +.notCinnabarGym + ld hl,$d72e + set 5,[hl] + ld a,[W_CURMAP] + cp a,OAKS_LAB + jp z,.noFaintCheck + callab AnyPlayerPokemonAliveCheck ; check if all the player's pokemon fainted + ld a,d + and a + jr z,.allPokemonFainted +.noFaintCheck + ld c,$0a + call DelayFrames + jp EnterMap +.allPokemonFainted + ld a,$ff + ld [$d057],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,[$d72d] + bit 4,a + jr nz,.noBattle + call Func_30fd + jr nz,.noBattle + ld a,[$d72e] + bit 4,a + jr nz,.noBattle + ld b, BANK(InitBattle) + ld hl, InitBattle + jp Bankswitch ; determines if a battle will occur and runs the battle if so +.noBattle + and a + ret + +; function to make bikes twice as fast as walking +BikeSpeedup:: ; 06a0 (0:06a0) + ld a,[$cc57] + and a + ret nz + ld a,[W_CURMAP] + cp a,ROUTE_17 ; Cycling Road + jr nz,.goFaster + ld a,[H_CURRENTPRESSEDBUTTONS] ; current joypad state + and a,%01110000 ; bit mask for up, left, right buttons + ret nz +.goFaster + jp AdvancePlayerSprite + +; check if the player has stepped onto a warp after having not collided +CheckWarpsNoCollision:: ; 06b4 (0:06b4) + ld a,[$d3ae] ; number of warps + and a + jp z,CheckMapConnections + ld a,[$d3ae] ; number of warps + ld b,$00 + ld c,a + ld a,[W_YCOORD] + ld d,a + ld a,[W_XCOORD] + ld e,a + ld hl,$d3af ; start of warp entries +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,$d736 + set 2,[hl] + callba Func_c49d ; check if the player sprite is standing on a "door" tile + pop bc + pop hl + jr c,WarpFound1 ; if it is, go to 0735 + push hl + push bc + call ExtraWarpCheck ; sets carry if the warp is confirmed + pop bc + pop hl + jr nc,CheckWarpsNoCollisionRetry2 +; if the extra check passed + ld a,[W_FLAGS_D733] + bit 2,a + jr nz,WarpFound1 + push de + push bc + call GetJoypadState + pop bc + pop de + ld a,[H_CURRENTPRESSEDBUTTONS] ; current joypad state + and a,%11110000 ; bit mask for directional buttons + 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,[$d3ae] ; number of warps + ld c,a + ld hl,$d3af ; start of warp entries +.loop + ld a,[hli] ; Y coordinate of warp + ld b,a + ld a,[W_YCOORD] + cp b + jr nz,.retry1 + ld a,[hli] ; X coordinate of warp + ld b,a + ld a,[W_XCOORD] + cp b + jr nz,.retry2 + ld a,[hli] + ld [$d42f],a ; save target warp ID + ld a,[hl] + ld [$ff8b],a ; save target map + 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 [$d42f],a ; save target warp ID + ld a,[hli] + ld [$ff8b],a ; save target map + +WarpFound2:: ; 073c (0:073c) + ld a,[$d3ae] ; number of warps + sub c + ld [$d73b],a ; save ID of used warp + ld a,[W_CURMAP] + ld [$d73c],a + call CheckIfInOutsideMap + jr nz,.indoorMaps +; this is for handling "outside" maps that can't have the 0xFF destination map + ld a,[W_CURMAP] + ld [wLastMap],a + ld a,[W_CURMAPWIDTH] + ld [$d366],a + ld a,[$ff8b] ; destination map number + ld [W_CURMAP],a ; change current map to destination map + cp a,ROCK_TUNNEL_1 + jr nz,.notRockTunnel + ld a,$06 + ld [$d35d],a + call GBFadeIn1 +.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,[$ff8b] ; destination map + cp a,$ff + jr z,.goBackOutside +; if not going back to the previous map + ld [W_CURMAP],a ; current map number + callba Func_70787 ; check if the warp was a Silph Co. teleporter + ld a,[$cd5b] + dec a + jr nz,.notTeleporter +; if it's a Silph Co. teleporter + ld hl,$d732 + set 3,[hl] + call DoFlyOrTeleportAwayGraphics + jr .skipMapChangeSound +.notTeleporter + call PlayMapChangeSound +.skipMapChangeSound + ld hl,$d736 + res 0,[hl] + res 1,[hl] + jr .done +.goBackOutside + ld a,[wLastMap] + ld [W_CURMAP],a + call PlayMapChangeSound + xor a + ld [$d35d],a +.done + ld hl,$d736 + set 0,[hl] + call Func_12da + 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,[W_XCOORD] + cp a,$ff + jr nz,.checkEastMap + ld a,[$d387] + ld [W_CURMAP],a + ld a,[$d38f] ; new X coordinate upon entering west map + ld [W_XCOORD],a + ld a,[W_YCOORD] + ld c,a + ld a,[$d38e] ; Y adjustment upon entering west map + add c + ld c,a + ld [W_YCOORD],a + ld a,[$d390] ; pointer to upper left corner of map without adjustment for Y position + ld l,a + ld a,[$d391] + ld h,a + srl c + jr z,.savePointer1 +.pointerAdjustmentLoop1 + ld a,[$d38d] ; width of connected map + add a,$06 + ld e,a + ld d,$00 + ld b,$00 + add hl,de + dec c + jr nz,.pointerAdjustmentLoop1 +.savePointer1 + ld a,l + ld [$d35f],a ; pointer to upper left corner of current tile block map section + ld a,h + ld [$d360],a + jp .loadNewMap +.checkEastMap + ld b,a + ld a,[$d525] ; map width + cp b + jr nz,.checkNorthMap + ld a,[$d392] + ld [W_CURMAP],a + ld a,[$d39a] ; new X coordinate upon entering east map + ld [W_XCOORD],a + ld a,[W_YCOORD] + ld c,a + ld a,[$d399] ; Y adjustment upon entering east map + add c + ld c,a + ld [W_YCOORD],a + ld a,[$d39b] ; pointer to upper left corner of map without adjustment for Y position + ld l,a + ld a,[$d39c] + ld h,a + srl c + jr z,.savePointer2 +.pointerAdjustmentLoop2 + ld a,[$d398] + add a,$06 + ld e,a + ld d,$00 + ld b,$00 + add hl,de + dec c + jr nz,.pointerAdjustmentLoop2 +.savePointer2 + ld a,l + ld [$d35f],a ; pointer to upper left corner of current tile block map section + ld a,h + ld [$d360],a + jp .loadNewMap +.checkNorthMap + ld a,[W_YCOORD] + cp a,$ff + jr nz,.checkSouthMap + ld a,[$d371] + ld [W_CURMAP],a + ld a,[$d378] ; new Y coordinate upon entering north map + ld [W_YCOORD],a + ld a,[W_XCOORD] + ld c,a + ld a,[$d379] ; X adjustment upon entering north map + add c + ld c,a + ld [W_XCOORD],a + ld a,[$d37a] ; pointer to upper left corner of map without adjustment for X position + ld l,a + ld a,[$d37b] + ld h,a + ld b,$00 + srl c + add hl,bc + ld a,l + ld [$d35f],a ; pointer to upper left corner of current tile block map section + ld a,h + ld [$d360],a + jp .loadNewMap +.checkSouthMap + ld b,a + ld a,[$d524] + cp b + jr nz,.didNotEnterConnectedMap + ld a,[$d37c] + ld [W_CURMAP],a + ld a,[$d383] ; new Y coordinate upon entering south map + ld [W_YCOORD],a + ld a,[W_XCOORD] + ld c,a + ld a,[$d384] ; X adjustment upon entering south map + add c + ld c,a + ld [W_XCOORD],a + ld a,[$d385] ; pointer to upper left corner of map without adjustment for X position + ld l,a + ld a,[$d386] + ld h,a + ld b,$00 + srl c + add hl,bc + ld a,l + ld [$d35f],a ; pointer to upper left corner of current tile block map section + ld a,h + ld [$d360],a +.loadNewMap ; load the connected map that was entered + call LoadMapHeader + call Func_2312 ; music + ld b,$09 + call GoPAL_SET +; 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) + FuncCoord 8, 8 ; $c448 + ld a,[Coord] ; 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_02_57 - SFX_Headers_02) / 3 + jr .playSound +.didNotGoThroughDoor + ld a,(SFX_02_5c - SFX_Headers_02) / 3 +.playSound + call PlaySound + ld a,[$d35d] + and a + ret nz + jp GBFadeIn1 + +CheckIfInOutsideMap:: ; 08e1 (0:08e1) +; If the player is in an outside map (a town or route), set the z flag + ld a, [W_CURMAPTILESET] + 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, [W_CURMAP] + 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, [W_CURMAPTILESET] + 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, Func_c3ff + jr .doBankswitch +.useFunction2 + ld hl, Func_c44e +.doBankswitch + ld b, BANK(Func_c44e) + jp Bankswitch + +MapEntryAfterBattle:: ; 091f (0:091f) + callba Func_c35f ; function that appears to disable warp testing after collisions if the player is standing on a warp + ld a,[$d35d] + and a + jp z,GBFadeIn2 + jp LoadGBPal + +HandleBlackOut:: +; For when all the player's pokemon faint. +; Does not print the "blacked out" message. + + call GBFadeIn1 + ld a, $08 + call StopMusic + ld hl, $d72e + res 5, [hl] + ld a, Bank(Func_40b0) ; Bank(Func_40b0) and Bank(Func_62ce) need to be equal. + ld [H_LOADEDROMBANK], a + ld [$2000], a + call Func_40b0 + call Func_62ce + call Func_2312 + jp Func_5d5f + +StopMusic:: + ld [wMusicHeaderPointer], a + ld a, $ff + ld [$c0ee], a + call PlaySound +.wait + ld a, [wMusicHeaderPointer] + and a + jr nz, .wait + jp StopAllSounds + +HandleFlyOrTeleportAway:: + call UpdateSprites + call Delay3 + xor a + ld [$cf0b], a + ld [$d700], a + ld [$d057], a + ld [$d35d], a + ld hl, $d732 + set 2, [hl] + res 5, [hl] + call DoFlyOrTeleportAwayGraphics + ld a, Bank(Func_62ce) + ld [H_LOADEDROMBANK], a + ld [$2000], a + call Func_62ce + jp Func_5d5f + +DoFlyOrTeleportAwayGraphics:: + ld b, BANK(_DoFlyOrTeleportAwayGraphics) + ld hl, _DoFlyOrTeleportAwayGraphics + jp Bankswitch + +LoadPlayerSpriteGraphics:: +; Load sprite graphics based on whether the player is standing, biking, or surfing. + + ; 0: standing + ; 1: biking + ; 2: surfing + + ld a, [$d700] + dec a + jr z, .ridingBike + + ld a, [$ffd7] + 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 [$d700], a + ld [$d11a], a + jp LoadWalkingPlayerSpriteGraphics + +.determineGraphics + ld a, [$d700] + 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, [W_CURMAP] + cp ROUTE_23 + jr z, .allowed + cp INDIGO_PLATEAU + jr z, .allowed + + ld a, [W_CURMAPTILESET] + 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,[$d52e] + ld l,a + ld a,[$d52f] + ld h,a + ld de,$9000 + ld bc,$0600 + ld a,[$d52b] + 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,$c6e8 + ld a,[$d3ad] ; background tile number + 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,$c6e8 + ld a,[W_CURMAPWIDTH] + ld [$ff8c],a + add a,$06 ; border (east and west) + ld [$ff8b],a ; map width + border + ld b,$00 + ld c,a +; make space for north border (next 3 lines) + add hl,bc + add hl,bc + add hl,bc + ld c,$03 + add hl,bc ; this puts us past the (west) border + ld a,[$d36a] ; tile map pointer + ld e,a + ld a,[$d36b] + ld d,a ; de = tile map pointer + ld a,[W_CURMAPHEIGHT] + ld b,a +.rowLoop ; copy one row each iteration + push hl + ld a,[$ff8c] ; 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,[$ff8b] ; map width + border + add l + ld l,a + jr nc,.noCarry + inc h +.noCarry + dec b + jr nz,.rowLoop +.northConnection + ld a,[$d371] + cp a,$ff + jr z,.southConnection + call SwitchToMapRomBank + ld a,[$d372] + ld l,a + ld a,[$d373] + ld h,a + ld a,[$d374] + ld e,a + ld a,[$d375] + ld d,a + ld a,[$d376] + ld [$ff8b],a + ld a,[$d377] + ld [$ff8c],a + call LoadNorthSouthConnectionsTileMap +.southConnection + ld a,[$d37c] + cp a,$ff + jr z,.westConnection + call SwitchToMapRomBank + ld a,[$d37d] + ld l,a + ld a,[$d37e] + ld h,a + ld a,[$d37f] + ld e,a + ld a,[$d380] + ld d,a + ld a,[$d381] + ld [$ff8b],a + ld a,[$d382] + ld [$ff8c],a + call LoadNorthSouthConnectionsTileMap +.westConnection + ld a,[$d387] + cp a,$ff + jr z,.eastConnection + call SwitchToMapRomBank + ld a,[$d388] + ld l,a + ld a,[$d389] + ld h,a + ld a,[$d38a] + ld e,a + ld a,[$d38b] + ld d,a + ld a,[$d38c] + ld b,a + ld a,[$d38d] + ld [$ff8b],a + call LoadEastWestConnectionsTileMap +.eastConnection + ld a,[$d392] + cp a,$ff + jr z,.done + call SwitchToMapRomBank + ld a,[$d393] + ld l,a + ld a,[$d394] + ld h,a + ld a,[$d395] + ld e,a + ld a,[$d396] + ld d,a + ld a,[$d397] + ld b,a + ld a,[$d398] + ld [$ff8b],a + call LoadEastWestConnectionsTileMap +.done + ret + +LoadNorthSouthConnectionsTileMap:: ; 0ade (0:0ade) + ld c,$03 +.loop + push de + push hl + ld a,[$ff8b] ; width of connection + ld b,a +.innerLoop + ld a,[hli] + ld [de],a + inc de + dec b + jr nz,.innerLoop + pop hl + pop de + ld a,[$ff8c] ; width of connected map + add l + ld l,a + jr nc,.noCarry1 + inc h +.noCarry1 + ld a,[W_CURMAPWIDTH] + add a,$06 + 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,$03 +.innerLoop + ld a,[hli] + ld [de],a + inc de + dec c + jr nz,.innerLoop + pop de + pop hl + ld a,[$ff8b] ; width of connected map + add l + ld l,a + jr nc,.noCarry1 + inc h +.noCarry1 + ld a,[W_CURMAPWIDTH] + add a,$06 + 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 [$FF8C] +; if not, [$FF8C] is set to 0 +IsSpriteOrSignInFrontOfPlayer:: ; 0b23 (0:0b23) + xor a + ld [$ff8c],a + ld a,[$d4b0] ; number of signs in the map + and a + jr z,.extendRangeOverCounter +; if there are signs + ld a,$35 + call Predef ; get the coordinates in front of the player in de + ld hl,$d4b1 ; start of sign coordinates + ld a,[$d4b0] ; number of signs in the map + ld b,a + ld c,$00 +.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,$d4d1 ; start of sign text ID's + ld b,$00 + dec c + add hl,bc + ld a,[hl] + ld [$ff8c],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 + ld a,$35 + call Predef ; get the tile in front of the player in c + ld hl,$d532 ; list of tiles that extend talking range (counter tiles) + ld b,$03 + 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 [$FF8C] +IsSpriteInFrontOfPlayer:: ; 0b6b (0:0b6b) + ld d,$10 ; talking range in pixels (normal range) +IsSpriteInFrontOfPlayer2:: ; 0b6d (0:0b6d) + ld bc,$3c40 ; Y and X position of player sprite + ld a,[$c109] ; direction the player is facing +.checkIfPlayerFacingUp + cp a,$04 + jr nz,.checkIfPlayerFacingDown +; facing up + ld a,b + sub d + ld b,a + ld a,$08 + jr .doneCheckingDirection +.checkIfPlayerFacingDown + cp a,$00 + jr nz,.checkIfPlayerFacingRight +; facing down + ld a,b + add d + ld b,a + ld a,$04 + jr .doneCheckingDirection +.checkIfPlayerFacingRight + cp a,$0c + jr nz,.playerFacingLeft +; facing right + ld a,c + add d + ld c,a + ld a,$01 + jr .doneCheckingDirection +.playerFacingLeft +; facing left + ld a,c + sub d + ld c,a + ld a,$02 +.doneCheckingDirection + ld [$d52a],a + ld a,[$d4e1] ; number of sprites + and a + ret z +; if there are sprites + ld hl,$c110 + 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 + set 7,[hl] + ld a,e + ld [$ff8c],a ; store sprite ID + 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,[$d736] + bit 6,a ; is the player jumping? + jr nz,.noCollision +; if not jumping a ledge + ld a,[$cd38] + and a + jr nz,.noCollision + ld a,[$d52a] ; the direction that the player is trying to go in + ld d,a + ld a,[$c10c] ; 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 [$ff8c],a + call IsSpriteInFrontOfPlayer ; check for sprite collisions again? when does the above check fail to detect a sprite collision? + ld a,[$ff8c] + 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,[$c02a] + cp a,(SFX_02_5b - SFX_Headers_02) / 3 ; check if collision sound is already playing + jr z,.setCarry + ld a,(SFX_02_5b - SFX_Headers_02) / 3 + 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) + ld a,$35 + call Predef ; get tile in front of player + ld a,[$cfc6] ; tile in front of player + ld c,a + ld hl,$d530 ; 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 + ld a,$35 + call Predef ; 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,[$d736] + bit 6,a ; is the player jumping? + ret nz +; if not jumping + +Func_c44:: ; 0c44 (0:0c44) + FuncCoord 8, 9 ; $c45c + ld a,[Coord] ; tile the player is on + ld [$cf0e],a + +CheckForTilePairCollisions:: ; 0c4a (0:0c4a) + ld a,[$cfc6] ; tile in front of the player + ld c,a +.tilePairCollisionLoop + ld a,[W_CURMAPTILESET] ; 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,[$cf0e] ; 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,[$d52b] ; tile data ROM bank + ld [H_LOADEDROMBANK],a + ld [$2000],a ; switch to ROM bank that contains tile data + ld a,[$d35f] ; address of upper left corner of current map view + ld e,a + ld a,[$d360] + 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,[W_CURMAPWIDTH] + add a,$06 + 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,[W_YBLOCKCOORD] + and a + jr z,.adjustForXCoordWithinTileBlock + ld bc,$0030 + add hl,bc +.adjustForXCoordWithinTileBlock + ld a,[W_XBLOCKCOORD] + and a + jr z,.copyToVisibleAreaBuffer + ld bc,$0002 + add hl,bc +.copyToVisibleAreaBuffer + ld de,wTileMap ; base address for the tiles that are directly transfered to VRAM during V-blank + ld b,$12 +.rowLoop2 + ld c,$14 +.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 [$2000],a ; restore previous ROM bank + ret + +AdvancePlayerSprite:: ; 0d27 (0:0d27) + ld a,[$c103] ; delta Y + ld b,a + ld a,[$c105] ; 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,[W_YCOORD] + add b + ld [W_YCOORD],a + ld a,[W_XCOORD] + add c + ld [W_XCOORD],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,[$d526] + ld e,a + and a,$e0 + ld d,a + ld a,e + add a,$02 + and a,$1f + or d + ld [$d526],a + jr .adjustXCoordWithinBlock +.checkIfMovingWest + cp a,$ff + jr nz,.checkIfMovingSouth +; moving west + ld a,[$d526] + ld e,a + and a,$e0 + ld d,a + ld a,e + sub a,$02 + and a,$1f + or d + ld [$d526],a + jr .adjustXCoordWithinBlock +.checkIfMovingSouth + ld a,b + cp a,$01 + jr nz,.checkIfMovingNorth +; moving south + ld a,[$d526] + add a,$40 + ld [$d526],a + jr nc,.adjustXCoordWithinBlock + ld a,[$d527] + inc a + and a,$03 + or a,$98 + ld [$d527],a + jr .adjustXCoordWithinBlock +.checkIfMovingNorth + cp a,$ff + jr nz,.adjustXCoordWithinBlock +; moving north + ld a,[$d526] + sub a,$40 + ld [$d526],a + jr nc,.adjustXCoordWithinBlock + ld a,[$d527] + dec a + and a,$03 + or a,$98 + ld [$d527],a +.adjustXCoordWithinBlock + ld a,c + and a + jr z,.pointlessJump ; mistake? +.pointlessJump + ld hl,W_XBLOCKCOORD + 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,$d4e3 + inc [hl] + ld de,$d35f + 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,$d4e3 + dec [hl] + ld de,$d35f + call MoveTileBlockMapPointerWest + jr .updateMapView +.adjustYCoordWithinBlock + ld hl,W_YBLOCKCOORD + 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,$d4e2 + inc [hl] + ld de,$d35f + ld a,[W_CURMAPWIDTH] + 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,$d4e2 + dec [hl] + ld de,$d35f + ld a,[W_CURMAPWIDTH] + call MoveTileBlockMapPointerNorth +.updateMapView + call LoadCurrentMapView + ld a,[$c103] ; 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,[$c105] ; 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,[$c103] ; delta Y + ld b,a + ld a,[$c105] ; delta X + ld c,a + sla b + sla c + ld a,[$ffaf] + add b + ld [$ffaf],a ; update background scroll Y + ld a,[$ffae] + add c + ld [$ffae],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,$c114 + ld a,[$d4e1] ; 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,$06 + 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,$06 + 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) + FuncCoord 0, 0 + ld hl,Coord + call ScheduleRowRedrawHelper + ld a,[$d526] + ld [H_SCREENEDGEREDRAWADDR],a + ld a,[$d527] + ld [H_SCREENEDGEREDRAWADDR + 1],a + ld a,REDRAWROW + ld [H_SCREENEDGEREDRAW],a + ret + +ScheduleRowRedrawHelper:: ; 0ea6 (0:0ea6) + ld de,wScreenEdgeTiles + ld c,$28 +.loop + ld a,[hli] + ld [de],a + inc de + dec c + jr nz,.loop + ret + +ScheduleSouthRowRedraw:: ; 0eb2 (0:0eb2) + FuncCoord 0,16 + ld hl,Coord + call ScheduleRowRedrawHelper + ld a,[$d526] + ld l,a + ld a,[$d527] + ld h,a + ld bc,$0200 + add hl,bc + ld a,h + and a,$03 + or a,$98 + ld [H_SCREENEDGEREDRAWADDR + 1],a + ld a,l + ld [H_SCREENEDGEREDRAWADDR],a + ld a,REDRAWROW + ld [H_SCREENEDGEREDRAW],a + ret + +ScheduleEastColumnRedraw:: ; 0ed3 (0:0ed3) + FuncCoord 18,0 + ld hl,Coord + call ScheduleColumnRedrawHelper + ld a,[$d526] + ld c,a + and a,$e0 + ld b,a + ld a,c + add a,18 + and a,$1f + or b + ld [H_SCREENEDGEREDRAWADDR],a + ld a,[$d527] + ld [H_SCREENEDGEREDRAWADDR + 1],a + ld a,REDRAWCOL + ld [H_SCREENEDGEREDRAW],a + ret + +ScheduleColumnRedrawHelper:: ; 0ef2 (0:0ef2) + ld de,wScreenEdgeTiles + ld c,$12 +.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) + FuncCoord 0,0 + ld hl,Coord + call ScheduleColumnRedrawHelper + ld a,[$d526] + ld [H_SCREENEDGEREDRAWADDR],a + ld a,[$d527] + ld [H_SCREENEDGEREDRAWADDR + 1],a + ld a,REDRAWCOL + ld [H_SCREENEDGEREDRAW],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,[$d52c] ; pointer to tiles + ld l,a + ld a,[$d52d] + 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 +GetJoypadStateOverworld:: ; 0f4d (0:0f4d) + xor a + ld [$c103],a + ld [$c105],a + call RunMapScript + call GetJoypadState + ld a,[W_FLAGS_D733] + bit 3,a ; check if a trainer wants a challenge + jr nz,.notForcedDownwards + ld a,[W_CURMAP] + cp a,ROUTE_17 ; Cycling Road + jr nz,.notForcedDownwards + ld a,[H_CURRENTPRESSEDBUTTONS] ; current joypad state + and a,%11110011 ; bit mask for all directions and A/B + jr nz,.notForcedDownwards + ld a,%10000000 ; down pressed + ld [H_CURRENTPRESSEDBUTTONS],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,[$d730] + bit 7,a + ret z +; if simulating button presses + ld a,[H_CURRENTPRESSEDBUTTONS] ; current joypad state + ld b,a + ld a,[$cd3b] ; bit mask for button presses that override simulated ones + and b + ret nz ; return if the simulated button presses are overridden + ld hl,$cd38 ; index of current simulated button press + dec [hl] + ld a,[hl] + cp a,$ff + jr z,.doneSimulating ; if the end of the simulated button presses has been reached + ld hl,$ccd3 ; base address of simulated button presses +; add offset to base address + add l + ld l,a + jr nc,.noCarry + inc h +.noCarry + ld a,[hl] + ld [H_CURRENTPRESSEDBUTTONS],a ; store simulated button press in joypad state + and a + ret nz + ld [H_NEWLYPRESSEDBUTTONS],a + ld [H_NEWLYRELEASEDBUTTONS],a + ret +; if done simulating button presses +.doneSimulating + xor a + ld [$cd3a],a + ld [$cd38],a + ld [$ccd3],a + ld [wJoypadForbiddenButtonsMask],a + ld [H_CURRENTPRESSEDBUTTONS],a + ld hl,$d736 + ld a,[hl] + and a,$f8 + ld [hl],a + ld hl,$d730 + 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,[$d730] + bit 7,a + jp nz,.noCollision ; return and clear carry if button presses are being simulated + ld a,[$d52a] ; the direction that the player is trying to go in + ld d,a + ld a,[$c10c] ; 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 + ld a,$35 + call Predef ; get tile in front of player (puts it in c and [$CFC6]) + ld a,[$cfc6] ; 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,$d530 ; 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,[$c02a] + cp a,(SFX_02_5b - SFX_Headers_02) / 3 ; check if collision sound is already playing + jr z,.setCarry + ld a,(SFX_02_5b - SFX_Headers_02) / 3 + call PlaySound ; play collision sound (if it's not already playing) +.setCarry + scf + jr .done +.noCollision + and a +.done + ret +.stopSurfing + xor a + ld [$d700],a + call LoadPlayerSpriteGraphics + call Func_2307 + jr .noCollision +.checkIfVermilionDockTileset + ld a, [W_CURMAPTILESET] ; 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 Func_f225 ; check if the player is pushing a boulder + ld a,[wFlags_0xcd60] + bit 1,a ; is the player pushing a boulder? + jr z,.afterBoulderEffect + callba Func_f2b5 ; displays dust effect when pushing a boulder +.afterBoulderEffect + pop bc + pop de + pop hl + call Func_310e + ld a,[W_CURMAP] ; current map number + call SwitchToMapRomBank ; change to the ROM bank the map's data is in + ld hl,W_MAPSCRIPTPTR + 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 ; $4180 + ld hl,$8000 + jr LoadPlayerSpriteGraphicsCommon + +LoadSurfingPlayerSpriteGraphics:: ; 1055 (0:1055) + ld de,SeelSprite + ld hl,$8000 + jr LoadPlayerSpriteGraphicsCommon + +LoadBikePlayerSpriteGraphics:: ; 105d (0:105d) + ld de,RedCyclingSprite + ld hl,$8000 + +LoadPlayerSpriteGraphicsCommon:: ; 1063 (0:1063) + push de + push hl + ld bc,(BANK(RedSprite) << 8) + $0c + call CopyVideoData + pop hl + pop de + ld a,$c0 + add e + ld e,a + jr nc,.noCarry + inc d +.noCarry + set 3,h + ld bc,$050c + jp CopyVideoData + +; function to load data from the map header +LoadMapHeader:: ; 107c (0:107c) + callba Func_f113 + ld a,[W_CURMAPTILESET] + ld [$d119],a + ld a,[W_CURMAP] + call SwitchToMapRomBank + ld a,[W_CURMAPTILESET] + ld b,a + res 7,a + ld [W_CURMAPTILESET],a + ld [$ff8b],a + bit 7,b + ret nz + ld hl,MapHeaderPointers + ld a,[W_CURMAP] + 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,$d367 + 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 [$d371],a + ld [$d37c],a + ld [$d387],a + ld [$d392],a +; copy connection data (if any) to WRAM + ld a,[W_MAPCONNECTIONS] + ld b,a +.checkNorth + bit 3,b + jr z,.checkSouth + ld de,W_MAPCONN1PTR + call CopyMapConnectionHeader +.checkSouth + bit 2,b + jr z,.checkWest + ld de,W_MAPCONN2PTR + call CopyMapConnectionHeader +.checkWest + bit 1,b + jr z,.checkEast + ld de,W_MAPCONN3PTR + call CopyMapConnectionHeader +.checkEast + bit 0,b + jr z,.getObjectDataPointer + ld de,W_MAPCONN4PTR + call CopyMapConnectionHeader +.getObjectDataPointer + ld a,[hli] + ld [$d3a9],a + ld a,[hli] + ld [$d3aa],a + push hl + ld a,[$d3a9] + ld l,a + ld a,[$d3aa] + ld h,a ; hl = base of object data + ld de,$d3ad ; background tile ID + ld a,[hli] + ld [de],a ; save background tile ID +.loadWarpData + ld a,[hli] ; number of warps + ld [$d3ae],a ; save the number of warps + and a ; are there any warps? + jr z,.loadSignData ; if not, skip this + ld c,a + ld de,$d3af ; base address of warps +.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 [$d4b0],a ; save the number of signs + and a ; are there any signs? + jr z,.loadSpriteData ; if not, skip this + ld c,a + ld de,$d4d1 ; base address of sign text IDs + ld a,d + ld [$ff95],a + ld a,e + ld [$ff96],a + ld de,$d4b1 ; base address of sign coordinates +.signLoop + ld a,[hli] + ld [de],a + inc de + ld a,[hli] + ld [de],a + inc de + push de + ld a,[$ff95] + ld d,a + ld a,[$ff96] + ld e,a + ld a,[hli] + ld [de],a + inc de + ld a,d + ld [$ff95],a + ld a,e + ld [$ff96],a + pop de + dec c + jr nz,.signLoop +.loadSpriteData + ld a,[$d72e] + 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 [$d4e1],a ; save the number of sprites + push hl +; zero C110-C1FF and C210-C2FF + ld hl,$c110 + ld de,$c210 + 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,$c112 + ld de,$0010 + ld c,$0f +.disableSpriteEntriesLoop + ld [hl],$ff + add hl,de + dec c + jr nz,.disableSpriteEntriesLoop + pop hl + ld de,$c110 + ld a,[$d4e1] ; 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 [$ff8d],a ; save movement byte 2 + ld a,[hli] + ld [$ff8e],a ; save text ID and flags byte + push bc + push hl + ld b,$00 + ld hl,W_MAPSPRITEDATA + add hl,bc + ld a,[$ff8d] + ld [hli],a ; store movement byte 2 in byte 0 of sprite entry + ld a,[$ff8e] + ld [hl],a ; this appears pointless, since the value is overwritten immediately after + ld a,[$ff8e] + ld [$ff8d],a + and a,$3f + ld [hl],a ; store text ID in byte 1 of sprite entry + pop hl + ld a,[$ff8d] + bit 6,a + jr nz,.trainerSprite + bit 7,a + jr nz,.itemBallSprite + jr .regularSprite +.trainerSprite + ld a,[hli] + ld [$ff8d],a ; save trainer class + ld a,[hli] + ld [$ff8e],a ; save trainer number (within class) + push hl + ld hl,W_MAPSPRITEEXTRADATA + add hl,bc + ld a,[$ff8d] + ld [hli],a ; store trainer class in byte 0 of the entry + ld a,[$ff8e] + ld [hl],a ; store trainer number in byte 1 of the entry + pop hl + jr .nextSprite +.itemBallSprite + ld a,[hli] + ld [$ff8d],a ; save item number + push hl + ld hl,W_MAPSPRITEEXTRADATA + add hl,bc + ld a,[$ff8d] + 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,W_MAPSPRITEEXTRADATA + 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 + ld a,$19 + call Predef ; load tileset data + callab LoadWildData ; load wild pokemon data + pop hl ; restore hl from before going to the warp/sign/sprite data (this value was saved for seemingly no purpose) + ld a,[W_CURMAPHEIGHT] ; map height in 4x4 tile blocks + add a ; double it + ld [$d524],a ; store map height in 2x2 tile blocks + ld a,[W_CURMAPWIDTH] ; map width in 4x4 tile blocks + add a ; double it + ld [$d525],a ; map width in 2x2 tile blocks + ld a,[W_CURMAP] + ld c,a + ld b,$00 + ld a,[H_LOADEDROMBANK] + push af + ld a, BANK(MapSongBanks) + ld [H_LOADEDROMBANK],a + ld [$2000],a + ld hl, MapSongBanks + add hl,bc + add hl,bc + ld a,[hli] + ld [$d35b],a ; music 1 + ld a,[hl] + ld [$d35c],a ; music 2 + pop af + ld [H_LOADEDROMBANK],a + ld [$2000],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 [$d527],a + xor a + ld [$d526],a + ld [$ffaf],a + ld [$ffae],a + ld [wWalkCounter],a + ld [$d119],a + ld [$d11a],a + ld [$d3a8],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 + ld hl,wTileMap + ld de,$9800 + ld b,$12 +.vramCopyLoop + ld c,$14 +.vramCopyInnerLoop + ld a,[hli] + ld [de],a + inc e + dec c + jr nz,.vramCopyInnerLoop + ld a,$0c + add e + ld e,a + jr nc,.noCarry + inc d +.noCarry + dec b + jr nz,.vramCopyLoop + ld a,$01 + ld [$cfcb],a + call EnableLCD + ld b,$09 + call GoPAL_SET + call LoadPlayerSpriteGraphics + ld a,[$d732] + and a,$18 ; did the player fly or teleport in? + jr nz,.restoreRomBank + ld a,[W_FLAGS_D733] + bit 1,a + jr nz,.restoreRomBank + call Func_235f ; music related + call Func_2312 ; music related +.restoreRomBank + pop af + ld [H_LOADEDROMBANK],a + ld [$2000],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 [$2000],a ; switch to map ROM bank + pop bc + pop hl + ret + +Func_12da:: ; 12da (0:12da) + ld a, $1e + ld [$d13a], a + ld hl, $d730 + ld a, [hl] + or $26 + ld [hl], a + ret + +Func_12e7:: ; 12e7 (0:12e7) + ld hl, $d728 + res 0, [hl] + ret + +ForceBikeOrSurf:: ; 12ed (0:12ed) + ld b, BANK(RedSprite) + ld hl, LoadPlayerSpriteGraphics + call Bankswitch + jp Func_2307 ; update map/player state? + +; this is used to check if the player wants to interrupt the opening sequence at several points +; XXX is this used anywhere else? +; INPUT: +; c = number of frames to wait +; sets carry if Up+Select+B, Start, or A is pressed within c frames +; unsets carry otherwise +CheckForUserInterruption:: ; 12f8 (0:12f8) + call DelayFrame + push bc + call GetJoypadStateLowSensitivity + pop bc + ld a,[H_CURRENTPRESSEDBUTTONS] ; currently pressed buttons + cp a,%01000110 ; Up, Select button, B button + jr z,.setCarry ; if all three keys are pressed + ld a,[$ffb5] ; either newly pressed buttons or currently pressed buttons at low sampling rate + and a,%00001001 ; Start button, A button + jr nz,.setCarry ; if either key is pressed + dec c + jr nz,CheckForUserInterruption +.unsetCarry + and a + ret +.setCarry + scf + ret + +; function to load position data for destination warp when switching maps +; INPUT: +; a = ID of destination warp within destination map +LoadDestinationWarpPosition:: ; 1313 (0:1313) + ld b,a + ld a,[H_LOADEDROMBANK] + push af + ld a,[wPredefParentBank] + ld [H_LOADEDROMBANK],a + ld [$2000],a + ld a,b + add a + add a + ld c,a + ld b,0 + add hl,bc + ld bc,4 + ld de,$d35f + call CopyData + pop af + ld [H_LOADEDROMBANK],a + ld [$2000],a + ret + +; INPUT: +; c: if nonzero, show at least a sliver of health +; d = number of HP bar sections (normally 6) +; e = health (in eighths of bar sections) (normally out of 48) +DrawHPBar:: ; 1336 (0:1336) + push hl + push de + push bc + ld a,$71 ; left of HP bar tile 1 + ld [hli],a + ld a,$62 ; left of HP bar tile 2 + ld [hli],a + push hl + ld a,$63 ; empty bar section tile +.drawEmptyBarLoop + ld [hli],a + dec d + jr nz,.drawEmptyBarLoop + ld a,[$cf94] + dec a ; what should the right of HP bar tile be? + ld a,$6d ; right of HP bar tile, in status screen and battles + jr z,.writeTile + dec a ; right of HP bar tile, in pokemon menu +.writeTile + ld [hl],a + pop hl + ld a,e + and a ; is there enough health to show up on the HP bar? + jr nz,.loop ; if so, draw the HP bar + ld a,c + and a ; should a sliver of health be shown no matter what? + jr z,.done + ld e,1 ; if so, fill one eighth of a bar section +; loop to draw every full bar section +.loop + ld a,e + sub a,8 + jr c,.drawPartialBarSection + ld e,a + ld a,$6b ; filled bar section tile + ld [hli],a + ld a,e + and a + jr z,.done + jr .loop +; draws a partial bar section at the end (if necessary) +; there are 7 possible partial bar sections from 1/8 to 7/8 full +.drawPartialBarSection + ld a,$63 ; empty bar section tile + add e ; add e to get the appropriate partial bar section tile + ld [hl],a ; write the tile +.done + pop bc + pop de + pop hl + ret + +; loads pokemon data from one of multiple sources to $cf98 +; loads base stats to $d0b8 +; INPUT: +; [$cf92] = index of pokemon within party/box +; [$cc49] = source +; 00: player's party +; 01: enemy's party +; 02: current box +; 03: daycare +; OUTPUT: +; [$cf91] = pokemon ID +; $cf98 = base address of pokemon data +; $d0b8 = base address of base stats +LoadMonData:: ; 1372 (0:1372) + ld hl,LoadMonData_ + ld b,BANK(LoadMonData_) + jp Bankswitch + +; writes c to $d0dc+b +Func_137a:: ; 137a (0:137a) + ld hl, $d0dc + ld e, b + ld d, $0 + add hl, de + ld a, c + ld [hl], a + ret + +LoadFlippedFrontSpriteByMonIndex:: ; 1384 (0:1384) + ld a, $1 + ld [W_SPRITEFLIPPED], a + +LoadFrontSpriteByMonIndex:: ; 1389 (0:1389) + push hl + ld a, [$d11e] + push af + ld a, [$cf91] + ld [$d11e], a + ld a, $3a + call Predef ; indirect jump to IndexToPokedex (41010 (10:5010)) + ld hl, $d11e + ld a, [hl] + pop bc + ld [hl], b + and a + pop hl + jr z, .invalidDexNumber ; dex #0 invalid + cp 151 + 1 + jr c, .validDexNumber ; dex >#151 invalid +.invalidDexNumber + ld a, RHYDON ; $1 + ld [$cf91], a + ret +.validDexNumber + push hl + ld de, $9000 + call LoadMonFrontSprite + pop hl + ld a, [H_LOADEDROMBANK] + push af + ld a, Bank(asm_3f0d0) + ld [H_LOADEDROMBANK], a + ld [$2000], a + xor a + ld [$ffe1], a + call asm_3f0d0 + xor a + ld [W_SPRITEFLIPPED], a + pop af + ld [H_LOADEDROMBANK], a + ld [$2000], a + ret + +; plays the cry of a pokemon +; INPUT: +; a = pokemon ID +PlayCry:: ; 13d0 (0:13d0) + call GetCryData + call PlaySound ; play cry + jp WaitForSoundToFinish ; wait for sound to be done playing + +; gets a pokemon's cry data +; INPUT: +; a = pokemon ID +GetCryData:: ; 13d9 (0:13d9) + dec a + ld c,a + ld b,0 + ld hl,CryData + add hl,bc + add hl,bc + add hl,bc + ld a,Bank(CryData) + call BankswitchHome + ld a,[hli] + ld b,a + ld a,[hli] + ld [$c0f1],a + ld a,[hl] + ld [$c0f2],a + call BankswitchBack + ld a,b ; a = cryID + ld c,$14 ; base sound ID for pokemon cries + rlca + add b ; a = cryID * 3 + add c ; a = $14 + cryID * 3 + ret + +DisplayPartyMenu:: ; 13fc (0:13fc) + ld a,[$ffd7] + push af + xor a + ld [$ffd7],a + call GBPalWhiteOutWithDelay3 + call ClearSprites + call PartyMenuInit + call DrawPartyMenu + jp HandlePartyMenuInput + +GoBackToPartyMenu:: ; 1411 (0:1411) + ld a,[$ffd7] + push af + xor a + ld [$ffd7],a + call PartyMenuInit + call RedrawPartyMenu + jp HandlePartyMenuInput + +PartyMenuInit:: ; 1420 (0:1420) + ld a,$01 + call BankswitchHome + call LoadHpBarAndStatusTilePatterns + ld hl,$d730 + set 6,[hl] ; turn off letter printing delay + xor a + ld [$cc49],a + ld [$cc37],a + ld hl,wTopMenuItemY + inc a + ld [hli],a ; top menu item Y + xor a + ld [hli],a ; top menu item X + ld a,[$cc2b] + push af + ld [hli],a ; current menu item ID + inc hl + ld a,[W_NUMINPARTY] + and a ; are there more than 0 pokemon in the party? + jr z,.storeMaxMenuItemID + dec a +; if party is not empty, the max menu item ID is ([W_NUMINPARTY] - 1) +; otherwise, it is 0 +.storeMaxMenuItemID + ld [hli],a ; max menu item ID + ld a,[$d11f] + and a + ld a,%00000011 ; A button and B button + jr z,.next + xor a + ld [$d11f],a + inc a +.next + ld [hli],a ; menu watched keys + pop af + ld [hl],a ; old menu item ID + ret + +HandlePartyMenuInput:: ; 145a (0:145a) + ld a,1 + ld [$cc4a],a + ld a,$40 + ld [$d09b],a + call HandleMenuInputPokemonSelection + call PlaceUnfilledArrowMenuCursor + ld b,a + xor a + ld [$d09b],a + ld a,[wCurrentMenuItem] + ld [$cc2b],a + ld hl,$d730 + res 6,[hl] ; turn on letter printing delay + ld a,[$cc35] + and a + jp nz,.swappingPokemon + pop af + ld [$ffd7],a + bit 1,b + jr nz,.noPokemonChosen + ld a,[W_NUMINPARTY] + and a + jr z,.noPokemonChosen + ld a,[wCurrentMenuItem] + ld [wWhichPokemon],a + ld hl,W_PARTYMON1 + ld b,0 + ld c,a + add hl,bc + ld a,[hl] + ld [$cf91],a + ld [$cfd9],a + call BankswitchBack + and a + ret +.noPokemonChosen + call BankswitchBack + scf + ret +.swappingPokemon + bit 1,b ; was the B button pressed? + jr z,.handleSwap ; if not, handle swapping the pokemon +.cancelSwap ; if the B button was pressed + callba ErasePartyMenuCursors + xor a + ld [$cc35],a + ld [$d07d],a + call RedrawPartyMenu + jr HandlePartyMenuInput +.handleSwap + ld a,[wCurrentMenuItem] + ld [wWhichPokemon],a + callba SwitchPartyMon + jr HandlePartyMenuInput + +DrawPartyMenu:: ; 14d4 (0:14d4) + ld hl, DrawPartyMenu_ + jr DrawPartyMenuCommon + +RedrawPartyMenu:: ; 14d9 (0:14d9) + ld hl, RedrawPartyMenu_ + +DrawPartyMenuCommon:: ; 14dc (0:14dc) + ld b, BANK(RedrawPartyMenu_) + jp Bankswitch + +; prints a pokemon's status condition +; INPUT: +; de = address of status condition +; hl = destination address +PrintStatusCondition:: ; 14e1 (0:14e1) + push de + dec de + dec de ; de = address of current HP + ld a,[de] + ld b,a + dec de + ld a,[de] + or b ; is the pokemon's HP zero? + pop de + jr nz,PrintStatusConditionNotFainted +; if the pokemon's HP is 0, print "FNT" + ld a,"F" + ld [hli],a + ld a,"N" + ld [hli],a + ld [hl],"T" + and a + ret +PrintStatusConditionNotFainted ; 14f6 + ld a,[H_LOADEDROMBANK] + push af + ld a,BANK(PrintStatusAilment) + ld [H_LOADEDROMBANK],a + ld [$2000],a + call PrintStatusAilment ; print status condition + pop bc + ld a,b + ld [H_LOADEDROMBANK],a + ld [$2000],a + ret + +; function to print pokemon level, leaving off the ":L" if the level is at least 100 +; INPUT: +; hl = destination address +; [$cfb9] = level +PrintLevel:: ; 150b (0:150b) + ld a,$6e ; ":L" tile ID + ld [hli],a + ld c,2 ; number of digits + ld a,[$cfb9] ; level + cp a,100 + jr c,PrintLevelCommon +; if level at least 100, write over the ":L" tile + dec hl + inc c ; increment number of digits to 3 + jr PrintLevelCommon + +; prints the level without leaving off ":L" regardless of level +; INPUT: +; hl = destination address +; [$cfb9] = level +PrintLevelFull:: ; 151b (0:151b) + ld a,$6e ; ":L" tile ID + ld [hli],a + ld c,3 ; number of digits + ld a,[$cfb9] ; level + +PrintLevelCommon:: ; 1523 (0:1523) + ld [$d11e],a + ld de,$d11e + ld b,$41 ; no leading zeroes, left-aligned, one byte + jp PrintNumber + +Func_152e:: ; 152e (0:152e) + ld hl,$d0dc + ld c,a + ld b,0 + add hl,bc + ld a,[hl] + ret + +; copies the base stat data of a pokemon to $D0B8 (W_MONHEADER) +; INPUT: +; [$D0B5] = pokemon ID +GetMonHeader:: ; 1537 (0:1537) + ld a,[H_LOADEDROMBANK] + push af + ld a,BANK(BaseStats) + ld [H_LOADEDROMBANK],a + ld [$2000],a + push bc + push de + push hl + ld a,[$d11e] + push af + ld a,[$d0b5] + ld [$d11e],a + ld de,FossilKabutopsPic + ld b,$66 ; size of Kabutops fossil and Ghost sprites + cp a,FOSSIL_KABUTOPS ; Kabutops fossil + jr z,.specialID + ld de,GhostPic + cp a,MON_GHOST ; Ghost + jr z,.specialID + ld de,FossilAerodactylPic + ld b,$77 ; size of Aerodactyl fossil sprite + cp a,FOSSIL_AERODACTYL ; Aerodactyl fossil + jr z,.specialID + cp a,MEW + jr z,.mew + ld a,$3a + call Predef ; convert pokemon ID in [$D11E] to pokedex number + ld a,[$d11e] + dec a + ld bc,28 + ld hl,BaseStats + call AddNTimes + ld de,W_MONHEADER + ld bc,28 + call CopyData + jr .done +.specialID + ld hl,W_MONHSPRITEDIM + ld [hl],b ; write sprite dimensions + inc hl + ld [hl],e ; write front sprite pointer + inc hl + ld [hl],d + jr .done +.mew + ld hl,MewBaseStats + ld de,W_MONHEADER + ld bc,28 + ld a,BANK(MewBaseStats) + call FarCopyData +.done + ld a,[$d0b5] + ld [$d0b8],a + pop af + ld [$d11e],a + pop hl + pop de + pop bc + pop af + ld [H_LOADEDROMBANK],a + ld [$2000],a + ret + +; copy party pokemon's name to $CD6D +GetPartyMonName2:: ; 15b4 (0:15b4) + ld a,[wWhichPokemon] ; index within party + ld hl,W_PARTYMON1NAME + +; this is called more often +GetPartyMonName:: ; 15ba (0:15ba) + push hl + push bc + call SkipFixedLengthTextEntries ; add 11 to hl, a times + ld de,$cd6d + push de + ld bc,11 + call CopyData + pop de + pop bc + pop hl + ret + +; function to print a BCD (Binary-coded decimal) number +; de = address of BCD number +; hl = destination address +; c = flags and length +; bit 7: if set, do not print leading zeroes +; if unset, print leading zeroes +; bit 6: if set, left-align the string (do not pad empty digits with spaces) +; if unset, right-align the string +; bit 5: if set, print currency symbol at the beginning of the string +; if unset, do not print the currency symbol +; bits 0-4: length of BCD number in bytes +; Note that bits 5 and 7 are modified during execution. The above reflects +; their meaning at the beginning of the functions's execution. +PrintBCDNumber:: ; 15cd (0:15cd) + ld b,c ; save flags in b + res 7,c + res 6,c + res 5,c ; c now holds the length + bit 5,b + jr z,.loop + bit 7,b + jr nz,.loop + ld [hl],"¥" + inc hl +.loop + ld a,[de] + swap a + call PrintBCDDigit ; print upper digit + ld a,[de] + call PrintBCDDigit ; print lower digit + inc de + dec c + jr nz,.loop + bit 7,b ; were any non-zero digits printed? + jr z,.done ; if so, we are done +.numberEqualsZero ; if every digit of the BCD number is zero + bit 6,b ; left or right alignment? + jr nz,.skipRightAlignmentAdjustment + dec hl ; if the string is right-aligned, it needs to be moved back one space +.skipRightAlignmentAdjustment + bit 5,b + jr z,.skipCurrencySymbol + ld [hl],"¥" + inc hl +.skipCurrencySymbol + ld [hl],"0" + call PrintLetterDelay + inc hl +.done + ret + +PrintBCDDigit:: ; 1604 (0:1604) + and a,%00001111 + and a + jr z,.zeroDigit +.nonzeroDigit + bit 7,b ; have any non-space characters been printed? + jr z,.outputDigit +; if bit 7 is set, then no numbers have been printed yet + bit 5,b ; print the currency symbol? + jr z,.skipCurrencySymbol + ld [hl],"¥" + inc hl + res 5,b +.skipCurrencySymbol + res 7,b ; unset 7 to indicate that a nonzero digit has been reached +.outputDigit + add a,"0" + ld [hli],a + jp PrintLetterDelay +.zeroDigit + bit 7,b ; either printing leading zeroes or already reached a nonzero digit? + jr z,.outputDigit ; if so, print a zero digit + bit 6,b ; left or right alignment? + ret nz + inc hl ; if right-aligned, "print" a space by advancing the pointer + ret + +; uncompresses the front or back sprite of the specified mon +; assumes the corresponding mon header is already loaded +; hl contains offset to sprite pointer ($b for front or $d for back) +UncompressMonSprite:: ; 1627 (0:1627) + ld bc,W_MONHEADER + add hl,bc + ld a,[hli] + ld [W_SPRITEINPUTPTR],a ; fetch sprite input pointer + ld a,[hl] + ld [W_SPRITEINPUTPTR+1],a +; define (by index number) the bank that a pokemon's image is in +; index = Mew, bank 1 +; index = Kabutops fossil, bank $B +; index < $1F, bank 9 +; $1F ≤ index < $4A, bank $A +; $4A ≤ index < $74, bank $B +; $74 ≤ index < $99, bank $C +; $99 ≤ index, bank $D + ld a,[$CF91] ; XXX name for this ram location + ld b,a + cp MEW + ld a,BANK(MewPicFront) + jr z,.GotBank + ld a,b + cp FOSSIL_KABUTOPS + ld a,BANK(FossilKabutopsPic) + jr z,.GotBank + ld a,b + cp TANGELA + 1 + ld a,BANK(TangelaPicFront) + jr c,.GotBank + ld a,b + cp MOLTRES + 1 + ld a,BANK(MoltresPicFront) + jr c,.GotBank + ld a,b + cp BEEDRILL + 2 + ld a,BANK(BeedrillPicFront) + jr c,.GotBank + ld a,b + cp STARMIE + 1 + ld a,BANK(StarmiePicFront) + jr c,.GotBank + ld a,BANK(VictreebelPicFront) +.GotBank + jp UncompressSpriteData + +; de: destination location +LoadMonFrontSprite:: ; 1665 (0:1665) + push de + ld hl, W_MONHFRONTSPRITE - W_MONHEADER + call UncompressMonSprite + ld hl, W_MONHSPRITEDIM + ld a, [hli] + ld c, a + pop de + ; fall through + +; postprocesses uncompressed sprite chunks to a 2bpp sprite and loads it into video ram +; calculates alignment parameters to place both sprite chunks in the center of the 7*7 tile sprite buffers +; de: destination location +; a,c: sprite dimensions (in tiles of 8x8 each) +LoadUncompressedSpriteData:: ; 1672 (0:1672) + push de + and $f + ld [H_SPRITEWIDTH], a ; each byte contains 8 pixels (in 1bpp), so tiles=bytes for width + ld b, a + ld a, $7 + sub b ; 7-w + inc a ; 8-w + srl a ; (8-w)/2 ; horizontal center (in tiles, rounded up) + ld b, a + add a + add a + add a + sub b ; 7*((8-w)/2) ; skip for horizontal center (in tiles) + ld [H_SPRITEOFFSET], a + ld a, c + swap a + and $f + ld b, a + add a + add a + add a ; 8*tiles is height in bytes + ld [H_SPRITEHEIGHT], a ; $ff8c + ld a, $7 + sub b ; 7-h ; skip for vertical center (in tiles, relative to current column) + ld b, a + ld a, [H_SPRITEOFFSET] + add b ; 7*((8-w)/2) + 7-h ; combined overall offset (in tiles) + add a + add a + add a ; 8*(7*((8-w)/2) + 7-h) ; combined overall offset (in bytes) + ld [H_SPRITEOFFSET], a + xor a + ld [$4000], a + ld hl, S_SPRITEBUFFER0 + call ZeroSpriteBuffer ; zero buffer 0 + ld de, S_SPRITEBUFFER1 + ld hl, S_SPRITEBUFFER0 + call AlignSpriteDataCentered ; copy and align buffer 1 to 0 (containing the MSB of the 2bpp sprite) + ld hl, S_SPRITEBUFFER1 + call ZeroSpriteBuffer ; zero buffer 1 + ld de, S_SPRITEBUFFER2 + ld hl, S_SPRITEBUFFER1 + call AlignSpriteDataCentered ; copy and align buffer 2 to 1 (containing the LSB of the 2bpp sprite) + pop de + jp InterlaceMergeSpriteBuffers + +; copies and aligns the sprite data properly inside the sprite buffer +; sprite buffers are 7*7 tiles in size, the loaded sprite is centered within this area +AlignSpriteDataCentered:: ; 16c2 (0:16c2) + ld a, [H_SPRITEOFFSET] + ld b, $0 + ld c, a + add hl, bc + ld a, [H_SPRITEWIDTH] ; $ff8b +.columnLoop + push af + push hl + ld a, [H_SPRITEHEIGHT] ; $ff8c + ld c, a +.columnInnerLoop + ld a, [de] + inc de + ld [hli], a + dec c + jr nz, .columnInnerLoop + pop hl + ld bc, 7*8 ; 7 tiles + add hl, bc ; advance one full column + pop af + dec a + jr nz, .columnLoop + ret + +; fills the sprite buffer (pointed to in hl) with zeros +ZeroSpriteBuffer:: ; 16df (0:16df) + ld bc, SPRITEBUFFERSIZE +.nextByteLoop + xor a + ld [hli], a + dec bc + ld a, b + or c + jr nz, .nextByteLoop + ret + +; combines the (7*7 tiles, 1bpp) sprite chunks in buffer 0 and 1 into a 2bpp sprite located in buffer 1 through 2 +; in the resulting sprite, the rows of the two source sprites are interlaced +; de: output address +InterlaceMergeSpriteBuffers:: ; 16ea (0:16ea) + xor a + ld [$4000], a + push de + ld hl, S_SPRITEBUFFER2 + (SPRITEBUFFERSIZE - 1) ; destination: end of buffer 2 + ld de, S_SPRITEBUFFER1 + (SPRITEBUFFERSIZE - 1) ; source 2: end of buffer 1 + ld bc, S_SPRITEBUFFER0 + (SPRITEBUFFERSIZE - 1) ; source 1: end of buffer 0 + ld a, SPRITEBUFFERSIZE/2 ; $c4 + ld [H_SPRITEINTERLACECOUNTER], a ; $ff8b +.interlaceLoop + ld a, [de] + dec de + ld [hld], a ; write byte of source 2 + ld a, [bc] + dec bc + ld [hld], a ; write byte of source 1 + ld a, [de] + dec de + ld [hld], a ; write byte of source 2 + ld a, [bc] + dec bc + ld [hld], a ; write byte of source 1 + ld a, [H_SPRITEINTERLACECOUNTER] ; $ff8b + dec a + ld [H_SPRITEINTERLACECOUNTER], a ; $ff8b + jr nz, .interlaceLoop + ld a, [W_SPRITEFLIPPED] + and a + jr z, .notFlipped + ld bc, 2*SPRITEBUFFERSIZE + ld hl, S_SPRITEBUFFER1 +.swapLoop + swap [hl] ; if flipped swap nybbles in all bytes + inc hl + dec bc + ld a, b + or c + jr nz, .swapLoop +.notFlipped + pop hl + ld de, S_SPRITEBUFFER1 + ld c, (2*SPRITEBUFFERSIZE)/16 ; $31, number of 16 byte chunks to be copied + ld a, [H_LOADEDROMBANK] + ld b, a + jp CopyVideoData + +Underground_Coll:: ; 172f (0:172f) + INCBIN "gfx/tilesets/underground.tilecoll" +Overworld_Coll:: ; 1735 (0:1735) + INCBIN "gfx/tilesets/overworld.tilecoll" +RedsHouse1_Coll:: +RedsHouse2_Coll:: ; 1749 (0:1749) + INCBIN "gfx/tilesets/reds_house.tilecoll" +Mart_Coll +Pokecenter_Coll:: ; 1753 (0:1753) + INCBIN "gfx/tilesets/pokecenter.tilecoll" +Dojo_Coll:: +Gym_Coll:: ; 1759 (0:1759) + INCBIN "gfx/tilesets/gym.tilecoll" +Forest_Coll:: ; 1765 (0:1765) + INCBIN "gfx/tilesets/forest.tilecoll" +House_Coll:: ; 1775 (0:1775) + INCBIN "gfx/tilesets/house.tilecoll" +ForestGate_Coll:: +Museum_Coll:: +Gate_Coll:: ; 177f (0:177f) + INCBIN "gfx/tilesets/gate.tilecoll" +Ship_Coll:: ; 178a (0:178a) + INCBIN "gfx/tilesets/ship.tilecoll" +ShipPort_Coll:: ; 1795 (0:1795) + INCBIN "gfx/tilesets/ship_port.tilecoll" +Cemetery_Coll:: ; 179a (0:179a) + INCBIN "gfx/tilesets/cemetery.tilecoll" +Interior_Coll:: ; 17a2 (0:17a2) + INCBIN "gfx/tilesets/interior.tilecoll" +Cavern_Coll:: ; 17ac (0:17ac) + INCBIN "gfx/tilesets/cavern.tilecoll" +Lobby_Coll:: ; 17b8 (0:17b8) + INCBIN "gfx/tilesets/lobby.tilecoll" +Mansion_Coll:: ; 17c0 (0:17c0) + INCBIN "gfx/tilesets/mansion.tilecoll" +Lab_Coll:: ; 17ca (0:17ca) + INCBIN "gfx/tilesets/lab.tilecoll" +Club_Coll:: ; 17d1 (0:17d1) + INCBIN "gfx/tilesets/club.tilecoll" +Facility_Coll:: ; 17dd (0:17dd) + INCBIN "gfx/tilesets/facility.tilecoll" +Plateau_Coll:: ; 17f0 (0:17f0) + INCBIN "gfx/tilesets/plateau.tilecoll" + +; does the same thing as FarCopyData at 009D +; only difference is that it uses [$ff8b] instead of [$cee9] for a temp value +; copy bc bytes of data from a:hl to de +FarCopyData2:: ; 17f7 (0:17f7) + ld [$ff8b],a + ld a,[H_LOADEDROMBANK] + push af + ld a,[$ff8b] + ld [H_LOADEDROMBANK],a + ld [$2000],a + call CopyData + pop af + ld [H_LOADEDROMBANK],a + ld [$2000],a + ret + +; does a far copy but the source is de and the destination is hl +; copy bc bytes of data from a:de to hl +FarCopyData3:: ; 180d (0:180d) + ld [$ff8b],a + ld a,[H_LOADEDROMBANK] + push af + ld a,[$ff8b] + ld [H_LOADEDROMBANK],a + ld [$2000],a + push hl + push de + push de + ld d,h + ld e,l + pop hl + call CopyData + pop de + pop hl + pop af + ld [H_LOADEDROMBANK],a + ld [$2000],a + ret + +; copies each source byte to the destination twice (next to each other) +; copy bc source bytes from a:hl to de +FarCopyDataDouble:: ; 182b (0:182b) + ld [$ff8b],a + ld a,[H_LOADEDROMBANK] + push af + ld a,[$ff8b] + ld [H_LOADEDROMBANK],a + ld [$2000],a +.loop + ld a,[hli] + ld [de],a + inc de + ld [de],a + inc de + dec bc + ld a,c + or b + jr nz,.loop + pop af + ld [H_LOADEDROMBANK],a + ld [$2000],a + ret + +; copy (c * 16) bytes from b:de to hl during V-blank +; transfers up to 128 bytes per V-blank +CopyVideoData:: ; 1848 (0:1848) + ld a,[H_AUTOBGTRANSFERENABLED] ; save auto-transfer enabled flag + push af + xor a + ld [H_AUTOBGTRANSFERENABLED],a ; disable auto-transfer while copying + ld a,[H_LOADEDROMBANK] + ld [$ff8b],a + ld a,b + ld [H_LOADEDROMBANK],a + ld [$2000],a + ld a,e + ld [H_VBCOPYSRC],a + ld a,d + ld [H_VBCOPYSRC + 1],a + ld a,l + ld [H_VBCOPYDEST],a + ld a,h + ld [H_VBCOPYDEST + 1],a +.loop + ld a,c + cp a,8 ; are there more than 128 bytes left to copy? + jr nc,.copyMaxSize ; only copy up to 128 bytes at a time +.copyRemainder + ld [H_VBCOPYSIZE],a + call DelayFrame ; wait for V-blank handler to perform the copy + ld a,[$ff8b] + ld [H_LOADEDROMBANK],a + ld [$2000],a + pop af + ld [H_AUTOBGTRANSFERENABLED],a ; restore original auto-transfer enabled flag + ret +.copyMaxSize + ld a,8 ; 128 bytes + ld [H_VBCOPYSIZE],a + call DelayFrame ; wait for V-blank handler to perform the copy + ld a,c + sub a,8 + ld c,a + jr .loop + +; copy (c * 8) source bytes from b:de to hl during V-blank +; copies each source byte to the destination twice (next to each other) +; transfers up to 64 source bytes per V-blank +CopyVideoDataDouble:: ; 1886 (0:1886) + ld a,[H_AUTOBGTRANSFERENABLED] ; save auto-transfer enabled flag + push af + xor a + ld [H_AUTOBGTRANSFERENABLED],a ; disable auto-transfer while copying + ld a,[H_LOADEDROMBANK] + ld [$ff8b],a + ld a,b + ld [H_LOADEDROMBANK],a + ld [$2000],a + ld a,e + ld [H_VBCOPYDOUBLESRC],a + ld a,d + ld [H_VBCOPYDOUBLESRC + 1],a + ld a,l + ld [H_VBCOPYDOUBLEDEST],a + ld a,h + ld [H_VBCOPYDOUBLEDEST + 1],a +.loop + ld a,c + cp a,8 ; are there more than 64 source bytes left to copy? + jr nc,.copyMaxSize ; only copy up to 64 source bytes at a time +.copyRemainder + ld [H_VBCOPYDOUBLESIZE],a + call DelayFrame ; wait for V-blank handler to perform the copy + ld a,[$ff8b] + ld [H_LOADEDROMBANK],a + ld [$2000],a + pop af + ld [H_AUTOBGTRANSFERENABLED],a ; restore original auto-transfer enabled flag + ret +.copyMaxSize + ld a,8 ; 64 source bytes + ld [H_VBCOPYDOUBLESIZE],a + call DelayFrame ; wait for V-blank handler to perform the copy + ld a,c + sub a,8 + ld c,a + jr .loop + +; clears an area of the screen +; INPUT: +; hl = address of upper left corner of the area +; b = height +; c = width +ClearScreenArea:: ; 18c4 (0:18c4) + ld a,$7F ; blank tile + ld de,20 ; screen width +.loop + push hl + push bc +.innerLoop + ld [hli],a + dec c + jr nz,.innerLoop + pop bc + pop hl + add hl,de + dec b + jr nz,.loop + ret + +; copies the screen tile buffer from WRAM to VRAM +; copying is done in 3 chunks of 6 rows each +; b: high byte of VRAM destination address ($98 or $9c for window tile map 0 or 1 resp.) +CopyScreenTileBufferToVRAM:: ; 18d6 (0:18d6) + ld c, $6 + ld hl, $0000 + ld de, wTileMap + call InitScreenTileBufferTransferParameters + call DelayFrame + ld hl, $600 + ld de, wTileMap + 20 * 6 ; $c418 + call InitScreenTileBufferTransferParameters + call DelayFrame + ld hl, $c00 + ld de, wTileMap + 20 * 12 ; $c490 + call InitScreenTileBufferTransferParameters + jp DelayFrame + +InitScreenTileBufferTransferParameters:: ; 18fc (0:18fc) + ld a, d + ld [H_VBCOPYBGSRC+1], a + call GetRowColAddressBgMap + ld a, l + ld [H_VBCOPYBGDEST], a ; $ffc3 + ld a, h + ld [H_VBCOPYBGDEST+1], a + ld a, c + ld [H_VBCOPYBGNUMROWS], a ; $ffc5 + ld a, e + ld [H_VBCOPYBGSRC], a ; $ffc1 + ret + +ClearScreen:: ; 190f (0:190f) +; clears all tiles in the tilemap, +; then wait three frames + ld bc,$0168 ; tilemap size + inc b + ld hl,wTileMap ; TILEMAP_START + ld a,$7F ; $7F is blank tile +.loop + ld [hli],a + dec c + jr nz,.loop + dec b + jr nz,.loop + jp Delay3 + +TextBoxBorder:: ; 1922 (0:1922) +; draw a text box +; upper-left corner at coordinates hl +; height b +; width c + + ; first row + push hl + ld a,"┌" + ld [hli],a + inc a ; horizontal border ─ + call NPlaceChar + inc a ; upper-right border ┐ + ld [hl],a + + ; middle rows + pop hl + ld de,20 + add hl,de ; skip the top row + +.PlaceRow + push hl + ld a,"│" + ld [hli],a + ld a," " + call NPlaceChar + ld [hl],"│" + + pop hl + ld de,20 + add hl,de ; move to next row + dec b + jr nz,.PlaceRow + + ; bottom row + ld a,"└" + ld [hli],a + ld a,"─" + call NPlaceChar + ld [hl],"┘" + ret +; +NPlaceChar:: ; 194f (0:194f) +; place a row of width c of identical characters + ld d,c +.loop + ld [hli],a + dec d + jr nz,.loop + ret + +PlaceString:: ; 1955 (0:1955) + push hl +PlaceNextChar:: ; 1956 (0:1956) + ld a,[de] + + cp "@" + jr nz,.PlaceText + ld b,h + ld c,l + pop hl + ret + +.PlaceText + cp $4E + jr nz,.next + ld bc,$0028 + ld a,[$FFF6] + bit 2,a + jr z,.next2 + ld bc,$14 +.next2 + pop hl + add hl,bc + push hl + jp Next19E8 + +.next + cp $4F + jr nz,.next3 + pop hl + FuncCoord 1, 16 ; $c4e1 + ld hl,Coord + push hl + jp Next19E8 + +.next3 ; Check against a dictionary + and a + jp z,Char00 + cp $4C + jp z,Char4C + cp $4B + jp z,Char4B + cp $51 + jp z,Char51 + cp $49 + jp z,Char49 + cp $52 + jp z,Char52 + cp $53 + jp z,Char53 + cp $54 + jp z,Char54 + cp $5B + jp z,Char5B + cp $5E + jp z,Char5E + cp $5C + jp z,Char5C + cp $5D + jp z,Char5D + cp $55 + jp z,Char55 + cp $56 + jp z,Char56 + cp $57 + jp z,Char57 + cp $58 + jp z,Char58 + cp $4A + jp z,Char4A + cp $5F + jp z,Char5F + cp $59 + jp z,Char59 + cp $5A + jp z,Char5A + ld [hli],a + call PrintLetterDelay +Next19E8:: ; 19e8 (0:19e8) + inc de + jp PlaceNextChar + +Char00:: ; 19ec (0:19ec) + ld b,h + ld c,l + pop hl + ld de,Char00Text + dec de + ret + +Char00Text:: ; 0x19f4 “%d ERROR.” + TX_FAR _Char00Text + db "@" + +Char52:: ; 0x19f9 player’s name + push de + ld de,W_PLAYERNAME + jr FinishDTE + +Char53:: ; 19ff (0:19ff) ; rival’s name + push de + ld de,W_RIVALNAME + jr FinishDTE + +Char5D:: ; 1a05 (0:1a05) ; TRAINER + push de + ld de,Char5DText + jr FinishDTE + +Char5C:: ; 1a0b (0:1a0b) ; TM + push de + ld de,Char5CText + jr FinishDTE + +Char5B:: ; 1a11 (0:1a11) ; PC + push de + ld de,Char5BText + jr FinishDTE + +Char5E:: ; 1a17 (0:1a17) ; ROCKET + push de + ld de,Char5EText + jr FinishDTE + +Char54:: ; 1a1d (0:1a1d) ; POKé + push de + ld de,Char54Text + jr FinishDTE + +Char56:: ; 1a23 (0:1a23) ; …… + push de + ld de,Char56Text + jr FinishDTE + +Char4A:: ; 1a29 (0:1a29) ; PKMN + push de + ld de,Char4AText + jr FinishDTE + +Char59:: ; 1a2f (0:1a2f) +; depending on whose turn it is, print +; enemy active monster’s name, prefixed with “Enemy ” +; or +; player active monster’s name +; (like Char5A but flipped) + ld a,[H_WHOSETURN] + xor 1 + jr MonsterNameCharsCommon + +Char5A:: ; 1a35 (0:1a35) +; depending on whose turn it is, print +; player active monster’s name +; or +; enemy active monster’s name, prefixed with “Enemy ” + ld a,[H_WHOSETURN] +MonsterNameCharsCommon:: ; 1a37 (0:1a37) + push de + and a + jr nz,.Enemy + ld de,W_PLAYERMONNAME ; player active monster name + jr FinishDTE + +.Enemy ; 1A40 + ; print “Enemy ” + ld de,Char5AText + call PlaceString + + ld h,b + ld l,c + ld de,W_ENEMYMONNAME ; enemy active monster name + +FinishDTE:: ; 1a4b (0:1a4b) + call PlaceString + ld h,b + ld l,c + pop de + inc de + jp PlaceNextChar + +Char5CText:: ; 1a55 (0:1a55) + db "TM@" +Char5DText:: ; 1a58 (0:1a58) + db "TRAINER@" +Char5BText:: ; 1a60 (0:1a60) + db "PC@" +Char5EText:: ; 1a63 (0:1a63) + db "ROCKET@" +Char54Text:: ; 1a6a (0:1a6a) + db "POKé@" +Char56Text:: ; 1a6f (0:1a6f) + db "……@" +Char5AText:: ; 1a72 (0:1a72) + db "Enemy @" +Char4AText:: ; 1a79 (0:1a79) + db $E1,$E2,"@" ; PKMN + +Char55:: ; 1a7c (0:1a7c) + push de + ld b,h + ld c,l + ld hl,Char55Text + call TextCommandProcessor + ld h,b + ld l,c + pop de + inc de + jp PlaceNextChar + +Char55Text:: ; 1a8c (0:1a8c) +; equivalent to Char4B + TX_FAR _Char55Text + db "@" + +Char5F:: ; 1a91 (0:1a91) +; ends a Pokédex entry + ld [hl],"." + pop hl + ret + +Char58:: ; 1a95 (0:1a95) + ld a,[$D12B] + cp 4 + jp z,Next1AA2 + ld a,$EE + FuncCoord 18, 16 ; $c4f2 + ld [Coord],a +Next1AA2:: ; 1aa2 (0:1aa2) + call ProtectedDelay3 + call ManualTextScroll + ld a,$7F + FuncCoord 18, 16 ; $c4f2 + ld [Coord],a +Char57:: ; 1aad (0:1aad) + pop hl + ld de,Char58Text + dec de + ret + +Char58Text:: ; 1ab3 (0:1ab3) + db "@" + +Char51:: ; 1ab4 (0:1ab4) + push de + ld a,$EE + FuncCoord 18, 16 ; $c4f2 + ld [Coord],a + call ProtectedDelay3 + call ManualTextScroll + FuncCoord 1, 13 ; $c4a5 + ld hl,Coord + ld bc,$0412 + call ClearScreenArea + ld c,$14 + call DelayFrames + pop de + FuncCoord 1, 14 ; $c4b9 + ld hl,Coord + jp Next19E8 + +Char49:: ; 1ad5 (0:1ad5) + push de + ld a,$EE + FuncCoord 18, 16 ; $c4f2 + ld [Coord],a + call ProtectedDelay3 + call ManualTextScroll + FuncCoord 1, 10 ; $c469 + ld hl,Coord + ld bc,$0712 + call ClearScreenArea + ld c,$14 + call DelayFrames + pop de + pop hl + FuncCoord 1, 11 ; $c47d + ld hl,Coord + push hl + jp Next19E8 + +Char4B:: ; 1af8 (0:1af8) + ld a,$EE + FuncCoord 18, 16 ; $c4f2 + ld [Coord],a + call ProtectedDelay3 + push de + call ManualTextScroll + pop de + ld a,$7F + FuncCoord 18, 16 ; $c4f2 + ld [Coord],a + ;fall through +Char4C:: ; 1b0a (0:1b0a) + push de + call Next1B18 + call Next1B18 + FuncCoord 1, 16 ; $c4e1 + ld hl,Coord + pop de + jp Next19E8 + +Next1B18:: ; 1b18 (0:1b18) + FuncCoord 0, 14 ; $c4b8 + ld hl,Coord + FuncCoord 0, 13 ; $c4a4 + ld de,Coord + ld b,$3C +.next + ld a,[hli] + ld [de],a + inc de + dec b + jr nz,.next + FuncCoord 1, 16 ; $c4e1 + ld hl,Coord + ld a,$7F + ld b,$12 +.next2 + ld [hli],a + dec b + jr nz,.next2 + + ; wait five frames + ld b,5 +.WaitFrame + call DelayFrame + dec b + jr nz,.WaitFrame + + ret + +ProtectedDelay3:: ; 1b3a (0:1b3a) + push bc + call Delay3 + pop bc + ret + +TextCommandProcessor:: ; 1b40 (0:1b40) + ld a,[$d358] + push af + set 1,a + ld e,a + ld a,[$fff4] + xor e + ld [$d358],a + ld a,c + ld [$cc3a],a + ld a,b + ld [$cc3b],a + +NextTextCommand:: ; 1b55 (0:1b55) + ld a,[hli] + cp a, "@" ; terminator + jr nz,.doTextCommand + pop af + ld [$d358],a + ret +.doTextCommand + push hl + cp a,$17 + jp z,TextCommand17 + cp a,$0e + jp nc,TextCommand0B ; if a != 0x17 and a >= 0xE, go to command 0xB +; if a < 0xE, use a jump table + ld hl,TextCommandJumpTable + push bc + add a + ld b,$00 + ld c,a + add hl,bc + pop bc + ld a,[hli] + ld h,[hl] + ld l,a + jp [hl] + +; draw box +; 04AAAABBCC +; AAAA = address of upper left corner +; BB = height +; CC = width +TextCommand04:: ; 1b78 (0:1b78) + pop hl + ld a,[hli] + ld e,a + ld a,[hli] + ld d,a + ld a,[hli] + ld b,a + ld a,[hli] + ld c,a + push hl + ld h,d + ld l,e + call TextBoxBorder + pop hl + jr NextTextCommand + +; place string inline +; 00{string} +TextCommand00:: ; 1b8a (0:1b8a) + pop hl + ld d,h + ld e,l + ld h,b + ld l,c + call PlaceString + ld h,d + ld l,e + inc hl + jr NextTextCommand + +; place string from RAM +; 01AAAA +; AAAA = address of string +TextCommand01:: ; 1b97 (0:1b97) + pop hl + ld a,[hli] + ld e,a + ld a,[hli] + ld d,a + push hl + ld h,b + ld l,c + call PlaceString + pop hl + jr NextTextCommand + +; print BCD number +; 02AAAABB +; AAAA = address of BCD number +; BB +; bits 0-4 = length in bytes +; bits 5-7 = unknown flags +TextCommand02:: ; 1ba5 (0:1ba5) + pop hl + ld a,[hli] + ld e,a + ld a,[hli] + ld d,a + ld a,[hli] + push hl + ld h,b + ld l,c + ld c,a + call PrintBCDNumber + ld b,h + ld c,l + pop hl + jr NextTextCommand + +; repoint destination address +; 03AAAA +; AAAA = new destination address +TextCommand03:: ; 1bb7 (0:1bb7) + pop hl + ld a,[hli] + ld [$cc3a],a + ld c,a + ld a,[hli] + ld [$cc3b],a + ld b,a + jp NextTextCommand + +; repoint destination to second line of dialogue text box +; 05 +; (no arguments) +TextCommand05:: ; 1bc5 (0:1bc5) + pop hl + FuncCoord 1, 16 ; $c4e1 + ld bc,Coord ; address of second line of dialogue text box + jp NextTextCommand + +; blink arrow and wait for A or B to be pressed +; 06 +; (no arguments) +TextCommand06:: ; 1bcc (0:1bcc) + ld a,[W_ISLINKBATTLE] + cp a,$04 + jp z,TextCommand0D + ld a,$ee ; down arrow + FuncCoord 18, 16 ; $c4f2 + ld [Coord],a ; place down arrow in lower right corner of dialogue text box + push bc + call ManualTextScroll ; blink arrow and wait for A or B to be pressed + pop bc + ld a," " + FuncCoord 18, 16 ; $c4f2 + ld [Coord],a ; overwrite down arrow with blank space + pop hl + jp NextTextCommand + +; scroll text up one line +; 07 +; (no arguments) +TextCommand07:: ; 1be7 (0:1be7) + ld a," " + FuncCoord 18, 16 ; $c4f2 + ld [Coord],a ; place blank space in lower right corner of dialogue text box + call Next1B18 ; scroll up text + call Next1B18 + pop hl + FuncCoord 1, 16 ; $c4e1 + ld bc,Coord ; address of second line of dialogue text box + jp NextTextCommand + +; execute asm inline +; 08{code} +TextCommand08:: ; 1bf9 (0:1bf9) + pop hl + ld de,NextTextCommand + push de ; return address + jp [hl] + +; print decimal number (converted from binary number) +; 09AAAABB +; AAAA = address of number +; BB +; bits 0-3 = how many digits to display +; bits 4-7 = how long the number is in bytes +TextCommand09:: ; 1bff (0:1bff) + pop hl + ld a,[hli] + ld e,a + ld a,[hli] + ld d,a + ld a,[hli] + push hl + ld h,b + ld l,c + ld b,a + and a,$0f + ld c,a + ld a,b + and a,$f0 + swap a + set 6,a + ld b,a + call PrintNumber + ld b,h + ld c,l + pop hl + jp NextTextCommand + +; wait half a second if the user doesn't hold A or B +; 0A +; (no arguments) +TextCommand0A:: ; 1c1d (0:1c1d) + push bc + call GetJoypadState + ld a,[H_CURRENTPRESSEDBUTTONS] + and a,%00000011 ; A and B buttons + jr nz,.skipDelay + ld c,30 + call DelayFrames +.skipDelay + pop bc + pop hl + jp NextTextCommand + +; plays sounds +; this actually handles various command ID's, not just 0B +; (no arguments) +TextCommand0B:: ; 1c31 (0:1c31) + pop hl + push bc + dec hl + ld a,[hli] + ld b,a ; b = command number that got us here + push hl + ld hl,TextCommandSounds +.loop + ld a,[hli] + cp b + jr z,.matchFound + inc hl + jr .loop +.matchFound + cp a,$14 + jr z,.pokemonCry + cp a,$15 + jr z,.pokemonCry + cp a,$16 + jr z,.pokemonCry + ld a,[hl] + call PlaySound + call WaitForSoundToFinish + pop hl + pop bc + jp NextTextCommand +.pokemonCry + push de + ld a,[hl] + call PlayCry + pop de + pop hl + pop bc + jp NextTextCommand + +; format: text command ID, sound ID or cry ID +TextCommandSounds:: ; 1c64 (0:1c64) + db $0B,(SFX_02_3a - SFX_Headers_02) / 3 + db $12,(SFX_02_46 - SFX_Headers_02) / 3 + db $0E,(SFX_02_41 - SFX_Headers_02) / 3 + db $0F,(SFX_02_3a - SFX_Headers_02) / 3 + db $10,(SFX_02_3b - SFX_Headers_02) / 3 + db $11,(SFX_02_42 - SFX_Headers_02) / 3 + db $13,(SFX_02_44 - SFX_Headers_02) / 3 + db $14,NIDORINA ; used in OakSpeech + db $15,PIDGEOT ; used in SaffronCityText12 + db $16,DEWGONG ; unused? + +; draw ellipses +; 0CAA +; AA = number of ellipses to draw +TextCommand0C:: ; 1c78 (0:1c78) + pop hl + ld a,[hli] + ld d,a + push hl + ld h,b + ld l,c +.loop + ld a,$75 ; ellipsis + ld [hli],a + push de + call GetJoypadState + pop de + ld a,[H_CURRENTPRESSEDBUTTONS] ; joypad state + and a,%00000011 ; is A or B button pressed? + jr nz,.skipDelay ; if so, skip the delay + ld c,10 + call DelayFrames +.skipDelay + dec d + jr nz,.loop + ld b,h + ld c,l + pop hl + jp NextTextCommand + +; wait for A or B to be pressed +; 0D +; (no arguments) +TextCommand0D:: ; 1c9a (0:1c9a) + push bc + call ManualTextScroll ; wait for A or B to be pressed + pop bc + pop hl + jp NextTextCommand + +; process text commands in another ROM bank +; 17AAAABB +; AAAA = address of text commands +; BB = bank +TextCommand17:: ; 1ca3 (0:1ca3) + pop hl + ld a,[H_LOADEDROMBANK] + push af + ld a,[hli] + ld e,a + ld a,[hli] + ld d,a + ld a,[hli] + ld [H_LOADEDROMBANK],a + ld [$2000],a + push hl + ld l,e + ld h,d + call TextCommandProcessor + pop hl + pop af + ld [H_LOADEDROMBANK],a + ld [$2000],a + jp NextTextCommand + +TextCommandJumpTable:: ; 1cc1 (0:1cc1) + dw TextCommand00 + dw TextCommand01 + dw TextCommand02 + dw TextCommand03 + dw TextCommand04 + dw TextCommand05 + dw TextCommand06 + dw TextCommand07 + dw TextCommand08 + dw TextCommand09 + dw TextCommand0A + dw TextCommand0B + dw TextCommand0C + dw TextCommand0D + +; this function seems to be used only once +; it store the address of a row and column of the VRAM background map in hl +; INPUT: h - row, l - column, b - high byte of background tile map address in VRAM +GetRowColAddressBgMap:: ; 1cdd (0:1cdd) + xor a + srl h + rr a + srl h + rr a + srl h + rr a + or l + ld l,a + ld a,b + or h + ld h,a + ret + +; clears a VRAM background map with blank space tiles +; INPUT: h - high byte of background tile map address in VRAM +ClearBgMap:: ; 1cf0 (0:1cf0) + ld a," " + jr .next + ld a,l +.next + ld de,$400 ; size of VRAM background map + ld l,e +.loop + ld [hli],a + dec e + jr nz,.loop + dec d + jr nz,.loop + ret + +; When the player takes a step, a row or column of 2x2 tile blocks at the edge +; of the screen toward which they moved is exposed and has to be redrawn. +; This function does the redrawing. +RedrawExposedScreenEdge:: ; 1d01 (0:1d01) + ld a,[H_SCREENEDGEREDRAW] + and a + ret z + ld b,a + xor a + ld [H_SCREENEDGEREDRAW],a + dec b + jr nz,.redrawRow +.redrawColumn + ld hl,wScreenEdgeTiles + ld a,[H_SCREENEDGEREDRAWADDR] + ld e,a + ld a,[H_SCREENEDGEREDRAWADDR + 1] + ld d,a + ld c,18 ; screen height +.loop1 + ld a,[hli] + ld [de],a + inc de + ld a,[hli] + ld [de],a + ld a,31 + add e + ld e,a + jr nc,.noCarry + inc d +.noCarry +; the following 4 lines wrap us from bottom to top if necessary + ld a,d + and a,$03 + or a,$98 + ld d,a + dec c + jr nz,.loop1 + xor a + ld [H_SCREENEDGEREDRAW],a + ret +.redrawRow + ld hl,wScreenEdgeTiles + ld a,[H_SCREENEDGEREDRAWADDR] + ld e,a + ld a,[H_SCREENEDGEREDRAWADDR + 1] + ld d,a + push de + call .drawHalf ; draw upper half + pop de + ld a,32 ; width of VRAM background map + add e + ld e,a + ; draw lower half +.drawHalf + ld c,10 +.loop2 + ld a,[hli] + ld [de],a + inc de + ld a,[hli] + ld [de],a + ld a,e + inc a +; the following 6 lines wrap us from the right edge to the left edge if necessary + and a,$1f + ld b,a + ld a,e + and a,$e0 + or b + ld e,a + dec c + jr nz,.loop2 + ret + +; This function automatically transfers tile number data from the tile map at +; wTileMap to VRAM during V-blank. Note that it only transfers one third of the +; background per V-blank. It cycles through which third it draws. +; This transfer is turned off when walking around the map, but is turned +; on when talking to sprites, battling, using menus, etc. This is because +; the above function, RedrawExposedScreenEdge, is used when walking to +; improve efficiency. +AutoBgMapTransfer:: ; 1d57 (0:1d57) + ld a,[H_AUTOBGTRANSFERENABLED] + and a + ret z + ld hl,[sp + 0] + ld a,h + ld [H_SPTEMP],a + ld a,l + ld [H_SPTEMP + 1],a ; save stack pinter + ld a,[H_AUTOBGTRANSFERPORTION] + and a + jr z,.transferTopThird + dec a + jr z,.transferMiddleThird +.transferBottomThird + FuncCoord 0,12 + ld hl,Coord + ld sp,hl + ld a,[H_AUTOBGTRANSFERDEST + 1] + ld h,a + ld a,[H_AUTOBGTRANSFERDEST] + ld l,a + ld de,(12 * 32) + add hl,de + xor a ; TRANSFERTOP + jr .doTransfer +.transferTopThird + FuncCoord 0,0 + ld hl,Coord + ld sp,hl + ld a,[H_AUTOBGTRANSFERDEST + 1] + ld h,a + ld a,[H_AUTOBGTRANSFERDEST] + ld l,a + ld a,TRANSFERMIDDLE + jr .doTransfer +.transferMiddleThird + FuncCoord 0,6 + ld hl,Coord + ld sp,hl + ld a,[H_AUTOBGTRANSFERDEST + 1] + ld h,a + ld a,[H_AUTOBGTRANSFERDEST] + ld l,a + ld de,(6 * 32) + add hl,de + ld a,TRANSFERBOTTOM +.doTransfer + ld [H_AUTOBGTRANSFERPORTION],a ; store next portion + ld b,6 + +; unrolled loop and using pop for speed +TransferBgRows:: ; 1d9e (0:1d9e) + pop de + ld [hl],e + inc l + ld [hl],d + inc l + pop de + ld [hl],e + inc l + ld [hl],d + inc l + pop de + ld [hl],e + inc l + ld [hl],d + inc l + pop de + ld [hl],e + inc l + ld [hl],d + inc l + pop de + ld [hl],e + inc l + ld [hl],d + inc l + pop de + ld [hl],e + inc l + ld [hl],d + inc l + pop de + ld [hl],e + inc l + ld [hl],d + inc l + pop de + ld [hl],e + inc l + ld [hl],d + inc l + pop de + ld [hl],e + inc l + ld [hl],d + inc l + pop de + ld [hl],e + inc l + ld [hl],d + ld a,13 + add l + ld l,a + jr nc,.noCarry + inc h +.noCarry + dec b + jr nz,TransferBgRows + ld a,[H_SPTEMP] + ld h,a + ld a,[H_SPTEMP + 1] + ld l,a + ld sp,hl ; restore stack pointer + ret + +; Copies [H_VBCOPYBGNUMROWS] rows from H_VBCOPYBGSRC to H_VBCOPYBGDEST. +; If H_VBCOPYBGSRC is XX00, the transfer is disabled. +VBlankCopyBgMap:: ; 1de1 (0:1de1) + ld a,[H_VBCOPYBGSRC] ; doubles as enabling byte + and a + ret z + ld hl,[sp + 0] + ld a,h + ld [H_SPTEMP],a + ld a,l + ld [H_SPTEMP + 1],a ; save stack pointer + ld a,[H_VBCOPYBGSRC] + ld l,a + ld a,[H_VBCOPYBGSRC + 1] + ld h,a + ld sp,hl + ld a,[H_VBCOPYBGDEST] + ld l,a + ld a,[H_VBCOPYBGDEST + 1] + ld h,a + ld a,[H_VBCOPYBGNUMROWS] + ld b,a + xor a + ld [H_VBCOPYBGSRC],a ; disable transfer so it doesn't continue next V-blank + jr TransferBgRows + + +VBlankCopyDouble:: +; Copy [H_VBCOPYDOUBLESIZE] 1bpp tiles +; from H_VBCOPYDOUBLESRC to H_VBCOPYDOUBLEDEST. + +; While we're here, convert to 2bpp. +; The process is straightforward: +; copy each byte twice. + + ld a, [H_VBCOPYDOUBLESIZE] + and a + ret z + + ld hl, [sp + 0] + ld a, h + ld [H_SPTEMP], a + ld a, l + ld [H_SPTEMP + 1], a + + ld a, [H_VBCOPYDOUBLESRC] + ld l, a + ld a, [H_VBCOPYDOUBLESRC + 1] + ld h, a + ld sp, hl + + ld a, [H_VBCOPYDOUBLEDEST] + ld l, a + ld a, [H_VBCOPYDOUBLEDEST + 1] + ld h, a + + ld a, [H_VBCOPYDOUBLESIZE] + ld b, a + xor a ; transferred + ld [H_VBCOPYDOUBLESIZE], a + +.loop + rept 3 + pop de + ld [hl], e + inc l + ld [hl], e + inc l + ld [hl], d + inc l + ld [hl], d + inc l + endr + + pop de + ld [hl], e + inc l + ld [hl], e + inc l + ld [hl], d + inc l + ld [hl], d + inc hl + dec b + jr nz, .loop + + ld a, l + ld [H_VBCOPYDOUBLEDEST], a + ld a, h + ld [H_VBCOPYDOUBLEDEST + 1], a + + ld hl, [sp + 0] + ld a, l + ld [H_VBCOPYDOUBLESRC], a + ld a, h + ld [H_VBCOPYDOUBLESRC + 1], a + + ld a, [H_SPTEMP] + ld h, a + ld a, [H_SPTEMP + 1] + ld l, a + ld sp, hl + + ret + + +VBlankCopy:: +; Copy [H_VBCOPYSIZE] 2bpp tiles +; from H_VBCOPYSRC to H_VBCOPYDEST. + +; Source and destination addresses +; are updated, so transfer can +; continue in subsequent calls. + + ld a, [H_VBCOPYSIZE] + and a + ret z + + ld hl, [sp + 0] + ld a, h + ld [H_SPTEMP], a + ld a, l + ld [H_SPTEMP + 1], a + + ld a, [H_VBCOPYSRC] + ld l, a + ld a, [H_VBCOPYSRC + 1] + ld h, a + ld sp, hl + + ld a, [H_VBCOPYDEST] + ld l, a + ld a, [H_VBCOPYDEST + 1] + ld h, a + + ld a, [H_VBCOPYSIZE] + ld b, a + xor a ; transferred + ld [H_VBCOPYSIZE], a + +.loop + rept 7 + pop de + ld [hl], e + inc l + ld [hl], d + inc l + endr + + pop de + ld [hl], e + inc l + ld [hl], d + inc hl + dec b + jr nz, .loop + + ld a, l + ld [H_VBCOPYDEST], a + ld a, h + ld [H_VBCOPYDEST + 1], a + + ld hl, [sp + 0] + ld a, l + ld [H_VBCOPYSRC], a + ld a, h + ld [H_VBCOPYSRC + 1], a + + ld a, [H_SPTEMP] + ld h, a + ld a, [H_SPTEMP + 1] + ld l, a + ld sp, hl + + ret + + +UpdateMovingBgTiles:: +; Animate water and flower +; tiles in the overworld. + + ld a, [$ffd7] + and a + ret z + + ld a, [$ffd8] + inc a + ld [$ffd8], a + cp 20 + ret c + cp 21 + jr z, .flower + + ld hl, $9140 + ld c, $10 + + ld a, [$d085] + inc a + and 7 + ld [$d085], a + + and 4 + jr nz, .left +.right + ld a, [hl] + rrca + ld [hli], a + dec c + jr nz, .right + jr .done +.left + ld a, [hl] + rlca + ld [hli], a + dec c + jr nz, .left +.done + ld a, [$ffd7] + rrca + ret nc + xor a + ld [$ffd8], a + ret + +.flower + xor a + ld [$ffd8], a + + ld a, [$d085] + and 3 + cp 2 + ld hl, FlowerTile1 + jr c, .copy + ld hl, FlowerTile2 + jr z, .copy + ld hl, FlowerTile3 +.copy + ld de, $9030 + ld c, $10 +.loop + ld a, [hli] + ld [de], a + inc de + dec c + jr nz, .loop + ret + +FlowerTile1: INCBIN "gfx/tilesets/flower/flower1.2bpp" +FlowerTile2: INCBIN "gfx/tilesets/flower/flower2.2bpp" +FlowerTile3: INCBIN "gfx/tilesets/flower/flower3.2bpp" + + +SoftReset:: + call StopAllSounds + call GBPalWhiteOut + ld c, $20 + call DelayFrames + ; fallthrough + +Init:: +; Program init. + +rLCDC_DEFAULT EQU %11100011 +; * LCD enabled +; * Window tile map at $9C00 +; * Window display enabled +; * BG and window tile data at $8800 +; * BG tile map at $9800 +; * 8x8 OBJ size +; * OBJ display enabled +; * BG display enabled + + di + + xor a + ld [rIF], a + ld [rIE], a + ld [$ff43], a + ld [$ff42], a + ld [$ff01], a + ld [$ff02], a + ld [$ff4b], a + ld [$ff4a], a + ld [$ff06], a + ld [$ff07], a + ld [$ff47], a + ld [$ff48], a + ld [$ff49], a + + ld a, rLCDC_ENABLE_MASK + ld [rLCDC], a + call DisableLCD + + ld sp, wStack + + ld hl, $c000 ; start of WRAM + ld bc, $2000 ; size of WRAM +.loop + ld [hl], 0 + inc hl + dec bc + ld a, b + or c + jr nz, .loop + + call ClearVram + + ld hl, $ff80 + ld bc, $ffff - $ff80 + call FillMemory + + call ClearSprites + + ld a, Bank(WriteDMACodeToHRAM) + ld [H_LOADEDROMBANK], a + ld [MBC3RomBank], a + call WriteDMACodeToHRAM + + xor a + ld [$ffd7], a + ld [$ff41], a + ld [$ffae], a + ld [$ffaf], a + ld [$ff0f], a + ld a, 1 << VBLANK + 1 << TIMER + 1 << SERIAL + ld [rIE], a + + ld a, 144 ; move the window off-screen + ld [$ffb0], a + ld [rWY], a + ld a, 7 + ld [rWX], a + + ld a, $ff + ld [$ffaa], a + + ld h, $9800 / $100 ; bg map 0 + call ClearBgMap + ld h, $9c00 / $100 ; bg map 1 + call ClearBgMap + + ld a, rLCDC_DEFAULT + ld [rLCDC], a + ld a, $10 + ld [H_SOFTRESETCOUNTER], a + call StopAllSounds + + ei + + ld a, $40 ; PREDEF_SGB_BORDER + call Predef + + ld a, $1f + ld [$c0ef], a + ld [$c0f0], a + ld a, $9c + ld [$ffbd], a + xor a + ld [$ffbc], a + dec a + ld [$cfcb], a + + ld a, $32 ; PREDEF_INTRO + call Predef + + call DisableLCD + call ClearVram + call GBPalNormal + call ClearSprites + ld a, rLCDC_DEFAULT + ld [rLCDC], a + + jp SetDefaultNamesBeforeTitlescreen + +ClearVram: + ld hl, $8000 + ld bc, $2000 + xor a + jp FillMemory + + +StopAllSounds:: + ld a, Bank(Func_9876) + ld [$c0ef], a + ld [$c0f0], a + xor a + ld [wMusicHeaderPointer], a + ld [$c0ee], a + ld [$cfca], a + dec a + jp PlaySound + + +VBlank:: + + push af + push bc + push de + push hl + + ld a, [H_LOADEDROMBANK] + ld [$d122], a + + ld a, [$ffae] + ld [rSCX], a + ld a, [$ffaf] + ld [rSCY], a + + ld a, [$d0a0] + and a + jr nz, .ok + ld a, [$ffb0] + ld [rWY], a +.ok + + call AutoBgMapTransfer + call VBlankCopyBgMap + call RedrawExposedScreenEdge + call VBlankCopy + call VBlankCopyDouble + call UpdateMovingBgTiles + call $ff80 ; hOAMDMA + ld a, Bank(PrepareOAMData) + ld [H_LOADEDROMBANK], a + ld [MBC3RomBank], a + call PrepareOAMData + + ; VBlank-sensitive operations end. + + call Random + + ld a, [H_VBLANKOCCURRED] + and a + jr z, .vblanked + xor a + ld [H_VBLANKOCCURRED], a +.vblanked + + ld a, [H_FRAMECOUNTER] + and a + jr z, .decced + dec a + ld [H_FRAMECOUNTER], a +.decced + + call Func_28cb + + ld a, [$c0ef] ; music ROM bank + ld [H_LOADEDROMBANK], a + ld [MBC3RomBank], a + + cp BANK(Func_9103) + jr nz, .notbank2 +.bank2 + call Func_9103 + jr .afterMusic +.notbank2 + cp 8 + jr nz, .bank1F +.bank8 + call Func_2136e + call Func_21879 + jr .afterMusic +.bank1F + call Func_7d177 +.afterMusic + + callba Func_18dee ; keep track of time played + + ld a, [$fff9] + and a + call z, ReadJoypad + + ld a, [$d122] + ld [H_LOADEDROMBANK], a + ld [MBC3RomBank], a + + pop hl + pop de + pop bc + pop af + reti + + +DelayFrame:: +; Wait for the next vblank interrupt. +; As a bonus, this saves battery. + +NOT_VBLANKED EQU 1 + + ld a, NOT_VBLANKED + ld [H_VBLANKOCCURRED], a +.halt + ; XXX this is a hack--rgbasm adds + ; a nop after halts by default. + db $76 ; halt + + ld a, [H_VBLANKOCCURRED] + and a + jr nz, .halt + ret + + +; These routines manage gradual fading +; (e.g., entering a doorway) +LoadGBPal:: ; 20ba (0:20ba) + ld a,[$d35d] ;tells if cur.map is dark (requires HM5_FLASH?) + ld b,a + ld hl,GBPalTable_00 ;16 + ld a,l + sub b + ld l,a + jr nc,.jr0 + dec h +.jr0 + ld a,[hli] + ld [rBGP],a + ld a,[hli] + ld [rOBP0],a + ld a,[hli] + ld [rOBP1],a + ret + +GBFadeOut1:: ; 20d1 (0:20d1) + ld hl,IncGradGBPalTable_01 ;0d + ld b,$04 + jr GBFadeOutCommon + +GBFadeOut2:: ; 20d8 (0:20d8) + ld hl,IncGradGBPalTable_02 ;1c + ld b,$03 + +GBFadeOutCommon:: ; 20dd (0:20dd) + ld a,[hli] + ld [rBGP],a + ld a,[hli] + ld [rOBP0],a + ld a,[hli] + ld [rOBP1],a + ld c,8 + call DelayFrames + dec b + jr nz,GBFadeOutCommon + ret + +GBFadeIn1:: ; 20ef (0:20ef) + ld hl,DecGradGBPalTable_01 ;18 + ld b,$04 + jr GBFadeInCommon + +GBFadeIn2:: ; 20f6 (0:20f6) + ld hl,DecGradGBPalTable_02 ;21 + ld b,$03 + +GBFadeInCommon:: ; 20fb (0:20fb) + ld a,[hld] + ld [rOBP1],a + ld a,[hld] + ld [rOBP0],a + ld a,[hld] + ld [rBGP],a + ld c,8 + call DelayFrames + dec b + jr nz,GBFadeInCommon + ret + +IncGradGBPalTable_01:: ; 210d (0:210d) + db %11111111 ;BG Pal + db %11111111 ;OBJ Pal 1 + db %11111111 ;OBJ Pal 2 + ;and so on... + db %11111110 + db %11111110 + db %11111000 + + db %11111001 + db %11100100 + db %11100100 +GBPalTable_00:: ; 2116 (0:2116) + db %11100100 + db %11010000 +DecGradGBPalTable_01:: ; 2118 (0:2118) + db %11100000 + ;19 + db %11100100 + db %11010000 + db %11100000 +IncGradGBPalTable_02:: ; 211c (0:211c) + db %10010000 + db %10000000 + db %10010000 + + db %01000000 + db %01000000 +DecGradGBPalTable_02:: ; 2121 (0:2121) + db %01000000 + + db %00000000 + db %00000000 + db %00000000 + +Serial:: ; 2125 (0:2125) + push af + push bc + push de + push hl + ld a, [$ffaa] + inc a + jr z, .asm_2142 + ld a, [$ff01] + ld [$ffad], a + ld a, [$ffac] + ld [$ff01], a + ld a, [$ffaa] + cp $2 + jr z, .asm_2162 + ld a, $80 + ld [$ff02], a + jr .asm_2162 +.asm_2142 + ld a, [$ff01] + ld [$ffad], a + ld [$ffaa], a + cp $2 + jr z, .asm_215f + xor a + ld [$ff01], a + ld a, $3 + ld [rDIV], a ; $ff04 +.asm_2153 + ld a, [rDIV] ; $ff04 + bit 7, a + jr nz, .asm_2153 + ld a, $80 + ld [$ff02], a + jr .asm_2162 +.asm_215f + xor a + ld [$ff01], a +.asm_2162 + ld a, $1 + ld [$ffa9], a + ld a, $fe + ld [$ffac], a + pop hl + pop de + pop bc + pop af + reti + +Func_216f:: ; 216f (0:216f) + ld a, $1 + ld [$ffab], a +.asm_2173 + ld a, [hl] + ld [$ffac], a + call Func_219a + push bc + ld b, a + inc hl + ld a, $30 +.asm_217e + dec a + jr nz, .asm_217e + ld a, [$ffab] + and a + ld a, b + pop bc + jr z, .asm_2192 + dec hl + cp $fd + jr nz, .asm_2173 + xor a + ld [$ffab], a + jr .asm_2173 +.asm_2192 + ld [de], a + inc de + dec bc + ld a, b + or c + jr nz, .asm_2173 + ret + +Func_219a:: ; 219a (0:219a) + xor a + ld [$ffa9], a + ld a, [$ffaa] + cp $2 + jr nz, .asm_21a7 + ld a, $81 + ld [$ff02], a +.asm_21a7 + ld a, [$ffa9] + and a + jr nz, .asm_21f1 + ld a, [$ffaa] + cp $1 + jr nz, .asm_21cc + call Func_2237 + jr z, .asm_21cc + call Func_2231 + push hl + ld hl, $cc48 + inc [hl] + jr nz, .asm_21c3 + dec hl + inc [hl] +.asm_21c3 + pop hl + call Func_2237 + jr nz, .asm_21a7 + jp Func_223f +.asm_21cc + ld a, [rIE] ; $ffff + and $f + cp $8 + jr nz, .asm_21a7 + ld a, [W_NUMHITS] ; $d074 + dec a + ld [W_NUMHITS], a ; $d074 + jr nz, .asm_21a7 + ld a, [$d075] + dec a + ld [$d075], a + jr nz, .asm_21a7 + ld a, [$ffaa] + cp $1 + jr z, .asm_21f1 + ld a, $ff +.asm_21ee + dec a + jr nz, .asm_21ee +.asm_21f1 + xor a + ld [$ffa9], a + ld a, [rIE] ; $ffff + and $f + sub $8 + jr nz, .asm_2204 + ld [W_NUMHITS], a ; $d074 + ld a, $50 + ld [$d075], a +.asm_2204 + ld a, [$ffad] + cp $fe + ret nz + call Func_2237 + jr z, .asm_221f + push hl + ld hl, $cc48 + ld a, [hl] + dec a + ld [hld], a + inc a + jr nz, .asm_2219 + dec [hl] +.asm_2219 + pop hl + call Func_2237 + jr z, Func_223f +.asm_221f + ld a, [rIE] ; $ffff + and $f + cp $8 + ld a, $fe + ret z + ld a, [hl] + ld [$ffac], a + call DelayFrame + jp Func_219a + +Func_2231:: ; 2231 (0:2231) + ld a, $f +.asm_2233 + dec a + jr nz, .asm_2233 + ret + +Func_2237:: ; 2237 (0:2237) + push hl + ld hl, $cc47 + ld a, [hli] + or [hl] + pop hl + ret + +Func_223f:: ; 223f (0:223f) + dec a + ld [$cc47], a + ld [$cc48], a + ret + +Func_2247:: ; 2247 (0:2247) + ld hl, $cc42 + ld de, $cc3d + ld c, $2 + ld a, $1 + ld [$ffab], a +.asm_2253 + call DelayFrame + ld a, [hl] + ld [$ffac], a + call Func_219a + ld b, a + inc hl + ld a, [$ffab] + and a + ld a, $0 + ld [$ffab], a + jr nz, .asm_2253 + ld a, b + ld [de], a + inc de + dec c + jr nz, .asm_2253 + ret + +Func_226e:: ; 226e (0:226e) + call SaveScreenTilesToBuffer1 + callab PrintWaitingText + call Func_227f + jp LoadScreenTilesFromBuffer1 + +Func_227f:: ; 227f (0:227f) + ld a, $ff + ld [$cc3e], a +.asm_2284 + call Func_22c3 + call DelayFrame + call Func_2237 + jr z, .asm_22a0 + push hl + ld hl, $cc48 + dec [hl] + jr nz, .asm_229f + dec hl + dec [hl] + jr nz, .asm_229f + pop hl + xor a + jp Func_223f +.asm_229f + pop hl +.asm_22a0 + ld a, [$cc3e] + inc a + jr z, .asm_2284 + ld b, $a +.asm_22a8 + call DelayFrame + call Func_22c3 + dec b + jr nz, .asm_22a8 + ld b, $a +.asm_22b3 + call DelayFrame + call Func_22ed + dec b + jr nz, .asm_22b3 + ld a, [$cc3e] + ld [$cc3d], a + ret + +Func_22c3:: ; 22c3 (0:22c3) + call asm_22d7 + ld a, [$cc42] + add $60 + ld [$ffac], a + ld a, [$ffaa] + cp $2 + jr nz, asm_22d7 + ld a, $81 + ld [$ff02], a +asm_22d7:: ; 22d7 (0:22d7) + ld a, [$ffad] + ld [$cc3d], a + and $f0 + cp $60 + ret nz + xor a + ld [$ffad], a + ld a, [$cc3d] + and $f + ld [$cc3e], a + ret + +Func_22ed:: ; 22ed (0:22ed) + xor a + ld [$ffac], a + ld a, [$ffaa] + cp $2 + ret nz + ld a, $81 + ld [$ff02], a + ret + +Func_22fa:: ; 22fa (0:22fa) + ld a, $2 + ld [$ff01], a + xor a + ld [$ffad], a + ld a, $80 + ld [$ff02], a + ret + +; timer interrupt is apparently not invoked anyway +Timer:: ; 2306 (0:2306) + reti + +Func_2307:: ; 2307 (0:2307) + call WaitForSoundToFinish + xor a + ld c, a + ld d, a + ld [$cfca], a + jr asm_2324 + +Func_2312:: ; 2312 (0:2312) + ld c, $a + ld d, $0 + ld a, [$d72e] + bit 5, a + jr z, asm_2324 + xor a + ld [$cfca], a + ld c, $8 + ld d, c +asm_2324:: ; 2324 (0:2324) + ld a, [$d700] + and a + jr z, .asm_2343 + cp $2 + jr z, .asm_2332 + ld a, MUSIC_BIKE_RIDING + jr .asm_2334 +.asm_2332 + ld a, MUSIC_SURFING +.asm_2334 + ld b, a + ld a, d + and a + ld a, Bank(Func_7d8ea) + jr nz, .asm_233e + ld [$c0ef], a +.asm_233e + ld [$c0f0], a + jr .asm_234c +.asm_2343 + ld a, [$d35b] + ld b, a + call Func_2385 + jr c, .asm_2351 +.asm_234c + ld a, [$cfca] + cp b + ret z +.asm_2351 + ld a, c + ld [wMusicHeaderPointer], a + ld a, b + ld [$cfca], a + ld [$c0ee], a + jp PlaySound + +Func_235f:: ; 235f (0:235f) + ld a, [$c0ef] + ld b, a + cp $2 + jr nz, .checkForBank08 +.bank02 + ld hl, Func_9103 + jr .asm_2378 +.checkForBank08 + cp $8 + jr nz, .bank1F +.bank08 + ld hl, Func_21879 + jr .asm_2378 +.bank1F + ld hl, Func_7d177 +.asm_2378 + ld c, $6 +.asm_237a + push bc + push hl + call Bankswitch + pop hl + pop bc + dec c + jr nz, .asm_237a + ret + +Func_2385:: ; 2385 (0:2385) + ld a, [$d35c] + ld e, a + ld a, [$c0ef] + cp e + jr nz, .asm_2394 + ld [$c0f0], a + and a + ret +.asm_2394 + ld a, c + and a + ld a, e + jr nz, .asm_239c + ld [$c0ef], a +.asm_239c + ld [$c0f0], a + scf + ret + +PlayMusic:: ; 23a1 (0:23a1) + ld b, a + ld [$c0ee], a + xor a + ld [wMusicHeaderPointer], a + ld a, c + ld [$c0ef], a + ld [$c0f0], a + ld a, b + +; plays music specified by a. If value is $ff, music is stopped +PlaySound:: ; 23b1 (0:23b1) + push hl + push de + push bc + ld b, a + ld a, [$c0ee] + and a + jr z, .asm_23c8 + xor a + ld [$c02a], a + ld [$c02b], a + ld [$c02c], a + ld [$c02d], a +.asm_23c8 + ld a, [wMusicHeaderPointer] + and a + jr z, .asm_23e3 + ld a, [$c0ee] + and a + jr z, .asm_2425 + xor a + ld [$c0ee], a + ld a, [$cfca] + cp $ff + jr nz, .asm_2414 + xor a + ld [wMusicHeaderPointer], a +.asm_23e3 + xor a + ld [$c0ee], a + ld a, [H_LOADEDROMBANK] + ld [$ffb9], a + ld a, [$c0ef] + ld [H_LOADEDROMBANK], a + ld [$2000], a + cp $2 + jr nz, .checkForBank08 +.bank02 + ld a, b + call Func_9876 + jr .asm_240b +.checkForBank08 + cp $8 + jr nz, .bank1F +.bank08 + ld a, b + call Func_22035 + jr .asm_240b +.bank1F + ld a, b + call Func_7d8ea +.asm_240b + ld a, [$ffb9] + ld [H_LOADEDROMBANK], a + ld [$2000], a + jr .asm_2425 +.asm_2414 + ld a, b + ld [$cfca], a + ld a, [wMusicHeaderPointer] + ld [$cfc8], a + ld [$cfc9], a + ld a, b + ld [wMusicHeaderPointer], a +.asm_2425 + pop bc + pop de + pop hl + ret + +UpdateSprites:: ; 2429 (0:2429) + ld a, [$cfcb] + dec a + ret nz + ld a, [H_LOADEDROMBANK] + push af + ld a, Bank(_UpdateSprites) + ld [H_LOADEDROMBANK], a + ld [$2000], a + call _UpdateSprites + pop af + ld [H_LOADEDROMBANK], a + ld [$2000], a + ret + +INCLUDE "data/mart_inventories.asm" + +TextScriptEndingChar:: ; 24d6 (0:24d6) + db "@" +TextScriptEnd:: ; 24d7 (0:24d7) + ld hl,TextScriptEndingChar + ret + +ExclamationText:: ; 24db (0:24db) + TX_FAR _ExclamationText + db "@" + +GroundRoseText:: ; 24e0 (0:24e0) + TX_FAR _GroundRoseText + db "@" + +BoulderText:: ; 24e5 (0:24e5) + TX_FAR _BoulderText + db "@" + +MartSignText:: ; 24ea (0:24ea) + TX_FAR _MartSignText + db "@" + +PokeCenterSignText:: ; 24ef (0:24ef) + TX_FAR _PokeCenterSignText + db "@" + +Predef5CText:: ; 24f4 (0:24f4) +; XXX better label (what does predef $5C do?) + db $08 ; asm + ld a, $5c + call Predef + jp TextScriptEnd + +; bankswitches and runs _UncompressSpriteData +; bank is given in a, sprite input stream is pointed to in W_SPRITEINPUTPTR +UncompressSpriteData:: ; 24fd (0:24fd) + ld b, a + ld a, [H_LOADEDROMBANK] + push af + ld a, b + ld [H_LOADEDROMBANK], a + ld [$2000], a + ld a, $a + ld [$0], a + xor a + ld [$4000], a + call _UncompressSpriteData + pop af + ld [H_LOADEDROMBANK], a + ld [$2000], a + ret + +; initializes necessary data to load a sprite and runs UncompressSpriteDataLoop +_UncompressSpriteData:: ; 251a (0:251a) + ld hl, S_SPRITEBUFFER1 + ld c, (2*SPRITEBUFFERSIZE) % $100 + ld b, (2*SPRITEBUFFERSIZE) / $100 + xor a + call FillMemory ; clear sprite buffer 1 and 2 + ld a, $1 + ld [W_SPRITEINPUTBITCOUNTER], a + ld a, $3 + ld [W_SPRITEOUTPUTBITOFFSET], a + xor a + ld [W_SPRITECURPOSX], a + ld [W_SPRITECURPOSY], a + ld [W_SPRITELOADFLAGS], a ; $d0a8 + call ReadNextInputByte ; first byte of input determines sprite width (high nybble) and height (low nybble) in tiles (8x8 pixels) + ld b, a + and $f + add a + add a + add a + ld [W_SPRITEHEIGHT], a + ld a, b + swap a + and $f + add a + add a + add a + ld [W_SPRITEWITDH], a + call ReadNextInputBit + ld [W_SPRITELOADFLAGS], a ; initialite bit1 to 0 and bit0 to the first input bit + ; this will load two chunks of data to S_SPRITEBUFFER1 and S_SPRITEBUFFER2 + ; bit 0 decides in which one the first chunk is placed + ; fall through + +; uncompresses a chunk from the sprite input data stream (pointed to at $d0da) into S_SPRITEBUFFER1 or S_SPRITEBUFFER2 +; each chunk is a 1bpp sprite. A 2bpp sprite consist of two chunks which are merged afterwards +; note that this is an endless loop which is terminated during a call to MoveToNextBufferPosition by manipulating the stack +UncompressSpriteDataLoop:: ; 2556 (0:2556) + ld hl, S_SPRITEBUFFER1 + ld a, [W_SPRITELOADFLAGS] ; $d0a8 + bit 0, a + jr z, .useSpriteBuffer1 ; check which buffer to use + ld hl, S_SPRITEBUFFER2 +.useSpriteBuffer1 + call StoreSpriteOutputPointer + ld a, [W_SPRITELOADFLAGS] ; $d0a8 + bit 1, a + jr z, .startDecompression ; check if last iteration + call ReadNextInputBit ; if last chunk, read 1-2 bit unpacking mode + and a + jr z, .unpackingMode0 ; 0 -> mode 0 + call ReadNextInputBit ; 1 0 -> mode 1 + inc a ; 1 1 -> mode 2 +.unpackingMode0 + ld [W_SPRITEUNPACKMODE], a +.startDecompression + call ReadNextInputBit + and a + jr z, .readRLEncodedZeros ; if first bit is 0, the input starts with zeroes, otherwise with (non-zero) input +.readNextInput + call ReadNextInputBit + ld c, a + call ReadNextInputBit + sla c + or c ; read next two bits into c + and a + jr z, .readRLEncodedZeros ; 00 -> RLEncoded zeroes following + call WriteSpriteBitsToBuffer ; otherwise write input to output and repeat + call MoveToNextBufferPosition + jr .readNextInput +.readRLEncodedZeros + ld c, $0 ; number of zeroes it length encoded, the number +.countConsecutiveOnesLoop ; of consecutive ones determines the number of bits the number has + call ReadNextInputBit + and a + jr z, .countConsecutiveOnesFinished + inc c + jr .countConsecutiveOnesLoop +.countConsecutiveOnesFinished + ld a, c + add a + ld hl, LengthEncodingOffsetList + add l + ld l, a + jr nc, .noCarry + inc h +.noCarry + ld a, [hli] ; read offset that is added to the number later on + ld e, a ; adding an offset of 2^length - 1 makes every integer uniquely + ld d, [hl] ; representable in the length encoding and saves bits + push de + inc c + ld e, $0 + ld d, e +.readNumberOfZerosLoop ; reads the next c+1 bits of input + call ReadNextInputBit + or e + ld e, a + dec c + jr z, .readNumberOfZerosDone + sla e + rl d + jr .readNumberOfZerosLoop +.readNumberOfZerosDone + pop hl ; add the offset + add hl, de + ld e, l + ld d, h +.writeZerosLoop + ld b, e + xor a ; write 00 to buffer + call WriteSpriteBitsToBuffer + ld e, b + call MoveToNextBufferPosition + dec de + ld a, d + and a + jr nz, .continueLoop + ld a, e + and a +.continueLoop + jr nz, .writeZerosLoop + jr .readNextInput + +; moves output pointer to next position +; also cancels the calling function if the all output is done (by removing the return pointer from stack) +; and calls postprocessing functions according to the unpack mode +MoveToNextBufferPosition:: ; 25d8 (0:25d8) + ld a, [W_SPRITEHEIGHT] + ld b, a + ld a, [W_SPRITECURPOSY] + inc a + cp b + jr z, .curColumnDone + ld [W_SPRITECURPOSY], a + ld a, [W_SPRITEOUTPUTPTR] + inc a + ld [W_SPRITEOUTPUTPTR], a + ret nz + ld a, [W_SPRITEOUTPUTPTR+1] + inc a + ld [W_SPRITEOUTPUTPTR+1], a + ret +.curColumnDone + xor a + ld [W_SPRITECURPOSY], a + ld a, [W_SPRITEOUTPUTBITOFFSET] + and a + jr z, .bitOffsetsDone + dec a + ld [W_SPRITEOUTPUTBITOFFSET], a + ld hl, W_SPRITEOUTPUTPTRCACHED + ld a, [hli] + ld [W_SPRITEOUTPUTPTR], a + ld a, [hl] + ld [W_SPRITEOUTPUTPTR+1], a + ret +.bitOffsetsDone + ld a, $3 + ld [W_SPRITEOUTPUTBITOFFSET], a + ld a, [W_SPRITECURPOSX] + add $8 + ld [W_SPRITECURPOSX], a + ld b, a + ld a, [W_SPRITEWITDH] + cp b + jr z, .allColumnsDone + ld a, [W_SPRITEOUTPUTPTR] + ld l, a + ld a, [W_SPRITEOUTPUTPTR+1] + ld h, a + inc hl + jp StoreSpriteOutputPointer +.allColumnsDone + pop hl + xor a + ld [W_SPRITECURPOSX], a + ld a, [W_SPRITELOADFLAGS] ; $d0a8 + bit 1, a + jr nz, .done ; test if there is one more sprite to go + xor $1 + set 1, a + ld [W_SPRITELOADFLAGS], a ; $d0a8 + jp UncompressSpriteDataLoop +.done + jp UnpackSprite + +; writes 2 bits (from a) to the output buffer (pointed to from W_SPRITEOUTPUTPTR) +WriteSpriteBitsToBuffer:: ; 2649 (0:2649) + ld e, a + ld a, [W_SPRITEOUTPUTBITOFFSET] + and a + jr z, .offset0 + cp $2 + jr c, .offset1 + jr z, .offset2 + rrc e ; offset 3 + rrc e + jr .offset0 +.offset1 + sla e + sla e + jr .offset0 +.offset2 + swap e +.offset0 + ld a, [W_SPRITEOUTPUTPTR] + ld l, a + ld a, [W_SPRITEOUTPUTPTR+1] + ld h, a + ld a, [hl] + or e + ld [hl], a + ret + +; reads next bit from input stream and returns it in a +ReadNextInputBit:: ; 2670 (0:2670) + ld a, [W_SPRITEINPUTBITCOUNTER] + dec a + jr nz, .curByteHasMoreBitsToRead + call ReadNextInputByte + ld [W_SPRITEINPUTCURBYTE], a + ld a, $8 +.curByteHasMoreBitsToRead + ld [W_SPRITEINPUTBITCOUNTER], a + ld a, [W_SPRITEINPUTCURBYTE] + rlca + ld [W_SPRITEINPUTCURBYTE], a + and $1 + ret + +; reads next byte from input stream and returns it in a +ReadNextInputByte:: ; 268b (0:268b) + ld a, [W_SPRITEINPUTPTR] + ld l, a + ld a, [W_SPRITEINPUTPTR+1] + ld h, a + ld a, [hli] + ld b, a + ld a, l + ld [W_SPRITEINPUTPTR], a + ld a, h + ld [W_SPRITEINPUTPTR+1], a + ld a, b + ret + +; the nth item is 2^n - 1 +LengthEncodingOffsetList:: ; 269f (0:269f) + dw %0000000000000001 + dw %0000000000000011 + dw %0000000000000111 + dw %0000000000001111 + dw %0000000000011111 + dw %0000000000111111 + dw %0000000001111111 + dw %0000000011111111 + dw %0000000111111111 + dw %0000001111111111 + dw %0000011111111111 + dw %0000111111111111 + dw %0001111111111111 + dw %0011111111111111 + dw %0111111111111111 + dw %1111111111111111 + +; unpacks the sprite data depending on the unpack mode +UnpackSprite:: ; 26bf (0:26bf) + ld a, [W_SPRITEUNPACKMODE] + cp $2 + jp z, UnpackSpriteMode2 + and a + jp nz, XorSpriteChunks + ld hl, S_SPRITEBUFFER1 + call SpriteDifferentialDecode + ld hl, S_SPRITEBUFFER2 + ; fall through + +; decodes differential encoded sprite data +; input bit value 0 preserves the current bit value and input bit value 1 toggles it (starting from initial value 0). +SpriteDifferentialDecode:: ; 26d4 (0:26d4) + xor a + ld [W_SPRITECURPOSX], a + ld [W_SPRITECURPOSY], a + call StoreSpriteOutputPointer + ld a, [W_SPRITEFLIPPED] + and a + jr z, .notFlipped + ld hl, DecodeNybble0TableFlipped + ld de, DecodeNybble1TableFlipped + jr .storeDecodeTablesPointers +.notFlipped + ld hl, DecodeNybble0Table + ld de, DecodeNybble1Table +.storeDecodeTablesPointers + ld a, l + ld [W_SPRITEDECODETABLE0PTR], a + ld a, h + ld [W_SPRITEDECODETABLE0PTR+1], a + ld a, e + ld [W_SPRITEDECODETABLE1PTR], a + ld a, d + ld [W_SPRITEDECODETABLE1PTR+1], a + ld e, $0 ; last decoded nybble, initialized to 0 +.decodeNextByteLoop + ld a, [W_SPRITEOUTPUTPTR] + ld l, a + ld a, [W_SPRITEOUTPUTPTR+1] + ld h, a + ld a, [hl] + ld b, a + swap a + and $f + call DifferentialDecodeNybble ; decode high nybble + swap a + ld d, a + ld a, b + and $f + call DifferentialDecodeNybble ; decode low nybble + or d + ld b, a + ld a, [W_SPRITEOUTPUTPTR] + ld l, a + ld a, [W_SPRITEOUTPUTPTR+1] + ld h, a + ld a, b + ld [hl], a ; write back decoded data + ld a, [W_SPRITEHEIGHT] + add l ; move on to next column + jr nc, .noCarry + inc h +.noCarry + ld [W_SPRITEOUTPUTPTR], a + ld a, h + ld [W_SPRITEOUTPUTPTR+1], a + ld a, [W_SPRITECURPOSX] + add $8 + ld [W_SPRITECURPOSX], a + ld b, a + ld a, [W_SPRITEWITDH] + cp b + jr nz, .decodeNextByteLoop ; test if current row is done + xor a + ld e, a + ld [W_SPRITECURPOSX], a + ld a, [W_SPRITECURPOSY] ; move on to next row + inc a + ld [W_SPRITECURPOSY], a + ld b, a + ld a, [W_SPRITEHEIGHT] + cp b + jr z, .done ; test if all rows finished + ld a, [W_SPRITEOUTPUTPTRCACHED] + ld l, a + ld a, [W_SPRITEOUTPUTPTRCACHED+1] + ld h, a + inc hl + call StoreSpriteOutputPointer + jr .decodeNextByteLoop +.done + xor a + ld [W_SPRITECURPOSY], a + ret + +; decodes the nybble stored in a. Last decoded data is assumed to be in e (needed to determine if initial value is 0 or 1) +DifferentialDecodeNybble:: ; 276d (0:276d) + srl a ; c=a%2, a/=2 + ld c, $0 + jr nc, .evenNumber + ld c, $1 +.evenNumber + ld l, a + ld a, [W_SPRITEFLIPPED] + and a + jr z, .notFlipped ; determine if initial value is 0 or one + bit 3, e ; if flipped, consider MSB of last data + jr .selectLookupTable +.notFlipped + bit 0, e ; else consider LSB +.selectLookupTable + ld e, l + jr nz, .initialValue1 ; load the appropriate table + ld a, [W_SPRITEDECODETABLE0PTR] + ld l, a + ld a, [W_SPRITEDECODETABLE0PTR+1] + jr .tableLookup +.initialValue1 + ld a, [W_SPRITEDECODETABLE1PTR] + ld l, a + ld a, [W_SPRITEDECODETABLE1PTR+1] +.tableLookup + ld h, a + ld a, e + add l + ld l, a + jr nc, .noCarry + inc h +.noCarry + ld a, [hl] + bit 0, c + jr nz, .selectLowNybble + swap a ; select high nybble +.selectLowNybble + and $f + ld e, a ; update last decoded data + ret + +DecodeNybble0Table:: ; 27a7 (0:27a7) + dn $0, $1 + dn $3, $2 + dn $7, $6 + dn $4, $5 + dn $f, $e + dn $c, $d + dn $8, $9 + dn $b, $a +DecodeNybble1Table:: ; 27af (0:27af) + dn $f, $e + dn $c, $d + dn $8, $9 + dn $b, $a + dn $0, $1 + dn $3, $2 + dn $7, $6 + dn $4, $5 +DecodeNybble0TableFlipped:: ; 27b7 (0:27b7) + dn $0, $8 + dn $c, $4 + dn $e, $6 + dn $2, $a + dn $f, $7 + dn $3, $b + dn $1, $9 + dn $d, $5 +DecodeNybble1TableFlipped:: ; 27bf (0:27bf) + dn $f, $7 + dn $3, $b + dn $1, $9 + dn $d, $5 + dn $0, $8 + dn $c, $4 + dn $e, $6 + dn $2, $a + +; combines the two loaded chunks with xor (the chunk loaded second is the destination). The source chunk is differeintial decoded beforehand. +XorSpriteChunks:: ; 27c7 (0:27c7) + xor a + ld [W_SPRITECURPOSX], a + ld [W_SPRITECURPOSY], a + call ResetSpriteBufferPointers + ld a, [W_SPRITEOUTPUTPTR] ; points to buffer 1 or 2, depending on flags + ld l, a + ld a, [W_SPRITEOUTPUTPTR+1] + ld h, a + call SpriteDifferentialDecode ; decode buffer 1 or 2, depending on flags + call ResetSpriteBufferPointers + ld a, [W_SPRITEOUTPUTPTR] ; source buffer, points to buffer 1 or 2, depending on flags + ld l, a + ld a, [W_SPRITEOUTPUTPTR+1] + ld h, a + ld a, [W_SPRITEOUTPUTPTRCACHED] ; destination buffer, points to buffer 2 or 1, depending on flags + ld e, a + ld a, [W_SPRITEOUTPUTPTRCACHED+1] + ld d, a +.xorChunksLoop + ld a, [W_SPRITEFLIPPED] + and a + jr z, .notFlipped + push de + ld a, [de] + ld b, a + swap a + and $f + call ReverseNybble ; if flipped reverse the nybbles in the destination buffer + swap a + ld c, a + ld a, b + and $f + call ReverseNybble + or c + pop de + ld [de], a +.notFlipped + ld a, [hli] + ld b, a + ld a, [de] + xor b + ld [de], a + inc de + ld a, [W_SPRITECURPOSY] + inc a + ld [W_SPRITECURPOSY], a ; go to next row + ld b, a + ld a, [W_SPRITEHEIGHT] + cp b + jr nz, .xorChunksLoop ; test if column finished + xor a + ld [W_SPRITECURPOSY], a + ld a, [W_SPRITECURPOSX] + add $8 + ld [W_SPRITECURPOSX], a ; go to next column + ld b, a + ld a, [W_SPRITEWITDH] + cp b + jr nz, .xorChunksLoop ; test if all columns finished + xor a + ld [W_SPRITECURPOSX], a + ret + +; reverses the bits in the nybble given in register a +ReverseNybble:: ; 2837 (0:2837) + ld de, NybbleReverseTable + add e + ld e, a + jr nc, .asm_283f + inc d +.asm_283f + ld a, [de] + ret + +; resets sprite buffer pointers to buffer 1 and 2, depending on W_SPRITELOADFLAGS +ResetSpriteBufferPointers:: ; 2841 (0:2841) + ld a, [W_SPRITELOADFLAGS] ; $d0a8 + bit 0, a + jr nz, .buffer2Selected + ld de, S_SPRITEBUFFER1 + ld hl, S_SPRITEBUFFER2 + jr .storeBufferPointers +.buffer2Selected + ld de, S_SPRITEBUFFER2 + ld hl, S_SPRITEBUFFER1 +.storeBufferPointers + ld a, l + ld [W_SPRITEOUTPUTPTR], a + ld a, h + ld [W_SPRITEOUTPUTPTR+1], a + ld a, e + ld [W_SPRITEOUTPUTPTRCACHED], a + ld a, d + ld [W_SPRITEOUTPUTPTRCACHED+1], a + ret + +; maps each nybble to its reverse +NybbleReverseTable:: ; 2867 (0:2867) + db $0, $8, $4, $c, $2, $a, $6 ,$e, $1, $9, $5, $d, $3, $b, $7 ,$f + +; combines the two loaded chunks with xor (the chunk loaded second is the destination). Both chunks are differeintial decoded beforehand. +UnpackSpriteMode2:: ; 2877 (0:2877) + call ResetSpriteBufferPointers + ld a, [W_SPRITEFLIPPED] + push af + xor a + ld [W_SPRITEFLIPPED], a ; temporarily clear flipped flag for decoding the destination chunk + ld a, [W_SPRITEOUTPUTPTRCACHED] + ld l, a + ld a, [W_SPRITEOUTPUTPTRCACHED+1] + ld h, a + call SpriteDifferentialDecode + call ResetSpriteBufferPointers + pop af + ld [W_SPRITEFLIPPED], a + jp XorSpriteChunks + +; stores hl into the output pointers +StoreSpriteOutputPointer:: ; 2897 (0:2897) + ld a, l + ld [W_SPRITEOUTPUTPTR], a + ld [W_SPRITEOUTPUTPTRCACHED], a + ld a, h + ld [W_SPRITEOUTPUTPTR+1], a + ld [W_SPRITEOUTPUTPTRCACHED+1], a + ret + +ResetPlayerSpriteData:: ; 28a6 (0:28a6) + ld hl, wSpriteStateData1 + call ResetPlayerSpriteData_ClearSpriteData + ld hl, wSpriteStateData2 + call ResetPlayerSpriteData_ClearSpriteData + ld a, $1 + ld [wSpriteStateData1], a + ld [$c20e], a + ld hl, $c104 + ld [hl], $3c ; set Y screen pos + inc hl + inc hl + ld [hl], $40 ; set X screen pos + ret + +; overwrites sprite data with zeroes +ResetPlayerSpriteData_ClearSpriteData:: ; 28c4 (0:28c4) + ld bc, $10 + xor a + jp FillMemory + +Func_28cb:: ; 28cb (0:28cb) + ld a, [wMusicHeaderPointer] + and a + jr nz, .asm_28dc + ld a, [$d72c] + bit 1, a + ret nz + ld a, $77 + ld [$ff24], a + ret +.asm_28dc + ld a, [$cfc9] + and a + jr z, .asm_28e7 + dec a + ld [$cfc9], a + ret +.asm_28e7 + ld a, [$cfc8] + ld [$cfc9], a + ld a, [$ff24] + and a + jr z, .asm_2903 + ld b, a + and $f + dec a + ld c, a + ld a, b + and $f0 + swap a + dec a + swap a + or c + ld [$ff24], a + ret +.asm_2903 + ld a, [wMusicHeaderPointer] + ld b, a + xor a + ld [wMusicHeaderPointer], a + ld a, $ff + ld [$c0ee], a + call PlaySound + ld a, [$c0f0] + ld [$c0ef], a + ld a, b + ld [$c0ee], a + jp PlaySound + +; this function is used to display sign messages, sprite dialog, etc. +; INPUT: [$ff8c] = sprite ID or text ID +DisplayTextID:: ; 2920 (0:2920) + ld a,[H_LOADEDROMBANK] + push af + callba DisplayTextIDInit ; initialization + ld hl,$cf11 + bit 0,[hl] + res 0,[hl] + jr nz,.skipSwitchToMapBank + ld a,[W_CURMAP] + call SwitchToMapRomBank +.skipSwitchToMapBank + ld a,30 ; half a second + ld [H_FRAMECOUNTER],a ; used as joypad poll timer + ld hl,W_MAPTEXTPTR + ld a,[hli] + ld h,[hl] + ld l,a ; hl = map text pointer + ld d,$00 + ld a,[$ff8c] ; text ID + ld [$cf13],a + and a + jp z,DisplayStartMenu + cp a,$d3 ; safari game over + jp z,DisplaySafariGameOverText + cp a,$d0 ; fainted + jp z,DisplayPokemonFaintedText + cp a,$d1 ; blacked out + jp z,DisplayPlayerBlackedOutText + cp a,$d2 ; repel wore off + jp z,DisplayRepelWoreOffText + ld a,[$d4e1] ; number of sprites + ld e,a + ld a,[$ff8c] ; sprite ID + cp e + jr z,.spriteHandling + jr nc,.skipSpriteHandling +.spriteHandling +; get the text ID of the sprite + push hl + push de + push bc + callba Func_13074 ; update the graphics of the sprite the player is talking to (to face the right direction) + pop bc + pop de + ld hl,W_MAPSPRITEDATA ; NPC text entries + ld a,[$ff8c] + dec a + add a + add l + ld l,a + jr nc,.noCarry + inc h +.noCarry + inc hl + ld a,[hl] ; a = text ID of the sprite + pop hl +.skipSpriteHandling +; look up the address of the text in the map's text entries + dec a + ld e,a + sla e + add hl,de + ld a,[hli] + ld h,[hl] + ld l,a ; hl = address of the text + ld a,[hl] ; a = first byte of text +; check first byte of text for special cases + cp a,$fe ; Pokemart NPC + jp z,DisplayPokemartDialogue + cp a,$ff ; Pokemon Center NPC + jp z,DisplayPokemonCenterDialogue + cp a,$fc ; Item Storage PC + jp z,FuncTX_ItemStoragePC + cp a,$fd ; Bill's PC + jp z,FuncTX_BillsPC + cp a,$f9 ; Pokemon Center PC + jp z,FuncTX_PokemonCenterPC + cp a,$f5 ; Vending Machine + jr nz,.notVendingMachine + callba VendingMachineMenu ; jump banks to vending machine routine + jr AfterDisplayingTextID +.notVendingMachine + cp a,$f7 ; slot machine + jp z,FuncTX_SlotMachine + cp a,$f6 ; cable connection NPC in Pokemon Center + jr nz,.notSpecialCase + callab CableClubNPC + jr AfterDisplayingTextID +.notSpecialCase + call Func_3c59 ; display the text + ld a,[$cc3c] + and a + jr nz,HoldTextDisplayOpen + +AfterDisplayingTextID:: ; 29d6 (0:29d6) + ld a,[$cc47] + and a + jr nz,HoldTextDisplayOpen + call WaitForTextScrollButtonPress ; wait for a button press after displaying all the text + +; loop to hold the dialogue box open as long as the player keeps holding down the A button +HoldTextDisplayOpen:: ; 29df (0:29df) + call GetJoypadState + ld a,[H_CURRENTPRESSEDBUTTONS] + bit 0,a ; is the A button being pressed? + jr nz,HoldTextDisplayOpen + +CloseTextDisplay:: ; 29e8 (0:29e8) + ld a,[W_CURMAP] + call SwitchToMapRomBank + ld a,$90 + ld [$ffb0],a ; move the window off the screen + call DelayFrame + call LoadGBPal + xor a + ld [H_AUTOBGTRANSFERENABLED],a ; disable continuous WRAM to VRAM transfer each V-blank +; loop to make sprites face the directions they originally faced before the dialogue + ld hl,$c219 + ld c,$0f + ld de,$0010 +.restoreSpriteFacingDirectionLoop + ld a,[hl] + dec h + ld [hl],a + inc h + add hl,de + dec c + jr nz,.restoreSpriteFacingDirectionLoop + ld a,BANK(InitMapSprites) + ld [H_LOADEDROMBANK],a + ld [$2000],a + call InitMapSprites ; reload sprite tile pattern data (since it was partially overwritten by text tile patterns) + ld hl,$cfc4 + res 0,[hl] + ld a,[$d732] + bit 3,a + call z,LoadPlayerSpriteGraphics + call LoadCurrentMapView + pop af + ld [H_LOADEDROMBANK],a + ld [$2000],a + jp UpdateSprites ; move sprites + +DisplayPokemartDialogue:: ; 2a2e (0:2a2e) + push hl + ld hl,PokemartGreetingText + call PrintText + pop hl + inc hl + call LoadItemList + ld a,$02 + ld [$cf94],a ; selects between subtypes of menus + ld a,[H_LOADEDROMBANK] + push af + ld a,Bank(DisplayPokemartDialogue_) + ld [H_LOADEDROMBANK],a + ld [$2000],a + call DisplayPokemartDialogue_ + pop af + ld [H_LOADEDROMBANK],a + ld [$2000],a + jp AfterDisplayingTextID + +PokemartGreetingText:: ; 2a55 (0:2a55) + TX_FAR _PokemartGreetingText + db "@" + +LoadItemList:: ; 2a5a (0:2a5a) + ld a,$01 + ld [$cfcb],a + ld a,h + ld [$d128],a + ld a,l + ld [$d129],a + ld de,$cf7b +.loop + ld a,[hli] + ld [de],a + inc de + cp a,$ff + jr nz,.loop + ret + +DisplayPokemonCenterDialogue:: ; 2a72 (0:2a72) + xor a + ld [$ff8b],a + ld [$ff8c],a + ld [$ff8d],a + inc hl + ld a,[H_LOADEDROMBANK] + push af + ld a,Bank(DisplayPokemonCenterDialogue_) + ld [H_LOADEDROMBANK],a + ld [$2000],a + call DisplayPokemonCenterDialogue_ + pop af + ld [H_LOADEDROMBANK],a + ld [$2000],a + jp AfterDisplayingTextID + +DisplaySafariGameOverText:: ; 2a90 (0:2a90) + callab PrintSafariGameOverText + jp AfterDisplayingTextID + +DisplayPokemonFaintedText:: ; 2a9b (0:2a9b) + ld hl,PokemonFaintedText + call PrintText + jp AfterDisplayingTextID + +PokemonFaintedText:: ; 2aa4 (0:2aa4) + TX_FAR _PokemonFaintedText + db "@" + +DisplayPlayerBlackedOutText:: ; 2aa9 (0:2aa9) + ld hl,PlayerBlackedOutText + call PrintText + ld a,[$d732] + res 5,a + ld [$d732],a + jp HoldTextDisplayOpen + +PlayerBlackedOutText:: ; 2aba (0:2aba) + TX_FAR _PlayerBlackedOutText + db "@" + +DisplayRepelWoreOffText:: ; 2abf (0:2abf) + ld hl,RepelWoreOffText + call PrintText + jp AfterDisplayingTextID + +RepelWoreOffText:: ; 2ac8 (0:2ac8) + TX_FAR _RepelWoreOffText + db "@" + +INCLUDE "engine/menu/start_menu.asm" + +; function to count how many bits are set in a string of bytes +; INPUT: +; hl = address of string of bytes +; b = length of string of bytes +; OUTPUT: +; [$D11E] = number of set bits +CountSetBits:: ; 2b7f (0:2b7f) + ld c,0 +.loop + ld a,[hli] + ld e,a + ld d,8 +.innerLoop ; count how many bits are set in the current byte + srl e + ld a,0 + adc c + ld c,a + dec d + jr nz,.innerLoop + dec b + jr nz,.loop + ld a,c + ld [$d11e],a ; store number of set bits + ret + +; subtracts the amount the player paid from their money +; sets carry flag if there is enough money and unsets carry flag if not +SubtractAmountPaidFromMoney:: ; 2b96 (0:2b96) + ld b,BANK(SubtractAmountPaidFromMoney_) + ld hl,SubtractAmountPaidFromMoney_ + jp Bankswitch + +; adds the amount the player sold to their money +AddAmountSoldToMoney:: ; 2b9e (0:2b9e) + ld de,wPlayerMoney + 2 + ld hl,$ffa1 ; total price of items + ld c,3 ; length of money in bytes + ld a,$0b + call Predef ; add total price to money + ld a,$13 + ld [$d125],a + call DisplayTextBoxID ; redraw money text box + ld a, (SFX_02_5a - SFX_Headers_02) / 3 + call PlaySoundWaitForCurrent ; play sound + jp WaitForSoundToFinish ; wait until sound is done playing + +; function to remove an item (in varying quantities) from the player's bag or PC box +; INPUT: +; HL = address of inventory (either wNumBagItems or wNumBoxItems) +; [$CF92] = index (within the inventory) of the item to remove +; [$CF96] = quantity to remove +RemoveItemFromInventory:: ; 2bbb (0:2bbb) + ld a,[H_LOADEDROMBANK] + push af + ld a,BANK(RemoveItemFromInventory_) + ld [H_LOADEDROMBANK],a + ld [$2000],a + call RemoveItemFromInventory_ + pop af + ld [H_LOADEDROMBANK],a + ld [$2000],a + ret + +; function to add an item (in varying quantities) to the player's bag or PC box +; INPUT: +; HL = address of inventory (either wNumBagItems or wNumBoxItems) +; [$CF91] = item ID +; [$CF96] = item quantity +; sets carry flag if successful, unsets carry flag if unsuccessful +AddItemToInventory:: ; 2bcf (0:2bcf) + push bc + ld a,[H_LOADEDROMBANK] + push af + ld a,BANK(AddItemToInventory_) + ld [H_LOADEDROMBANK],a + ld [$2000],a + call AddItemToInventory_ + pop bc + ld a,b + ld [H_LOADEDROMBANK],a + ld [$2000],a + pop bc + ret + +; INPUT: +; [wListMenuID] = list menu ID +; [$cf8b] = address of the list (2 bytes) +DisplayListMenuID:: ; 2be6 (0:2be6) + xor a + ld [H_AUTOBGTRANSFERENABLED],a ; disable auto-transfer + ld a,1 + ld [$ffb7],a ; joypad state update flag + ld a,[W_BATTLETYPE] + and a ; is it the Old Man battle? + jr nz,.specialBattleType + ld a,$01 ; hardcoded bank + jr .bankswitch +.specialBattleType ; Old Man battle + ld a, Bank(OldManItemList) +.bankswitch + call BankswitchHome + ld hl,$d730 + set 6,[hl] ; turn off letter printing delay + xor a + ld [$cc35],a ; 0 means no item is currently being swapped + ld [$d12a],a + ld a,[$cf8b] + ld l,a + ld a,[$cf8c] + ld h,a ; hl = address of the list + ld a,[hl] + ld [$d12a],a ; [$d12a] = number of list entries + ld a,$0d ; list menu text box ID + ld [$d125],a + call DisplayTextBoxID ; draw the menu text box + call UpdateSprites ; move sprites + FuncCoord 4,2 ; coordinates of upper left corner of menu text box + ld hl,Coord + ld de,$090e ; height and width of menu text box + ld a,[wListMenuID] + and a ; is it a PC pokemon list? + jr nz,.skipMovingSprites + call UpdateSprites ; move sprites +.skipMovingSprites + ld a,1 ; max menu item ID is 1 if the list has less than 2 entries + ld [$cc37],a + ld a,[$d12a] + cp a,2 ; does the list have less than 2 entries? + jr c,.setMenuVariables + ld a,2 ; max menu item ID is 2 if the list has at least 2 entries +.setMenuVariables + ld [wMaxMenuItem],a + ld a,4 + ld [wTopMenuItemY],a + ld a,5 + ld [wTopMenuItemX],a + ld a,%00000111 ; A button, B button, Select button + ld [wMenuWatchedKeys],a + ld c,10 + call DelayFrames + +DisplayListMenuIDLoop:: ; 2c53 (0:2c53) + xor a + ld [H_AUTOBGTRANSFERENABLED],a ; disable transfer + call PrintListMenuEntries + ld a,1 + ld [H_AUTOBGTRANSFERENABLED],a ; enable transfer + call Delay3 + ld a,[W_BATTLETYPE] + and a ; is it the Old Man battle? + jr z,.notOldManBattle +.oldManBattle + ld a,"▶" + FuncCoord 5,4 + ld [Coord],a ; place menu cursor in front of first menu entry + ld c,80 + call DelayFrames + xor a + ld [wCurrentMenuItem],a + ld hl,Coord + ld a,l + ld [wMenuCursorLocation],a + ld a,h + ld [wMenuCursorLocation + 1],a + jr .buttonAPressed +.notOldManBattle + call LoadGBPal + call HandleMenuInput + push af + call PlaceMenuCursor + pop af + bit 0,a ; was the A button pressed? + jp z,.checkOtherKeys +.buttonAPressed + ld a,[wCurrentMenuItem] + call PlaceUnfilledArrowMenuCursor + ld a,$01 + ld [$d12e],a + ld [$d12d],a + xor a + ld [$cc37],a + ld a,[wCurrentMenuItem] + ld c,a + ld a,[wListScrollOffset] + add c + ld c,a + ld a,[$d12a] ; number of list entries + and a ; is the list empty? + jp z,ExitListMenu ; if so, exit the menu + dec a + cp c ; did the player select Cancel? + jp c,ExitListMenu ; if so, exit the menu + ld a,c + ld [wWhichPokemon],a + ld a,[wListMenuID] + cp a,ITEMLISTMENU + jr nz,.skipMultiplying +; if it's an item menu + sla c ; item entries are 2 bytes long, so multiply by 2 +.skipMultiplying + ld a,[$cf8b] + ld l,a + ld a,[$cf8c] + ld h,a + inc hl ; hl = beginning of list entries + ld b,0 + add hl,bc + ld a,[hl] + ld [$cf91],a + ld a,[wListMenuID] + and a ; is it a PC pokemon list? + jr z,.pokemonList + push hl + call GetItemPrice + pop hl + ld a,[wListMenuID] + cp a,ITEMLISTMENU + jr nz,.skipGettingQuantity +; if it's an item menu + inc hl + ld a,[hl] ; a = item quantity + ld [$cf97],a +.skipGettingQuantity + ld a,[$cf91] + ld [$d0b5],a + ld a,$01 + ld [$d0b7],a + call GetName + jr .storeChosenEntry +.pokemonList + ld hl,W_NUMINPARTY + ld a,[$cf8b] + cp l ; is it a list of party pokemon or box pokemon? + ld hl,W_PARTYMON1NAME + jr z,.getPokemonName + ld hl, W_BOXMON1NAME ; box pokemon names +.getPokemonName + ld a,[wWhichPokemon] + call GetPartyMonName +.storeChosenEntry ; store the menu entry that the player chose and return + ld de,$cd6d + call CopyStringToCF4B ; copy name to $cf4b + ld a,$01 + ld [$d12e],a + ld a,[wCurrentMenuItem] + ld [$d12d],a + xor a + ld [$ffb7],a ; joypad state update flag + ld hl,$d730 + res 6,[hl] ; turn on letter printing delay + jp BankswitchBack +.checkOtherKeys ; check B, SELECT, Up, and Down keys + bit 1,a ; was the B button pressed? + jp nz,ExitListMenu ; if so, exit the menu + bit 2,a ; was the select button pressed? + jp nz,HandleItemListSwapping ; if so, allow the player to swap menu entries + ld b,a + bit 7,b ; was Down pressed? + ld hl,wListScrollOffset + jr z,.upPressed +.downPressed + ld a,[hl] + add a,3 + ld b,a + ld a,[$d12a] ; number of list entries + cp b ; will going down scroll past the Cancel button? + jp c,DisplayListMenuIDLoop + inc [hl] ; if not, go down + jp DisplayListMenuIDLoop +.upPressed + ld a,[hl] + and a + jp z,DisplayListMenuIDLoop + dec [hl] + jp DisplayListMenuIDLoop + +DisplayChooseQuantityMenu:: ; 2d57 (0:2d57) +; text box dimensions/coordinates for just quantity + FuncCoord 15,9 + ld hl,Coord + ld b,1 ; height + ld c,3 ; width + ld a,[wListMenuID] + cp a,PRICEDITEMLISTMENU + jr nz,.drawTextBox +; text box dimensions/coordinates for quantity and price + FuncCoord 7,9 + ld hl,Coord + ld b,1 ; height + ld c,11 ; width +.drawTextBox + call TextBoxBorder + FuncCoord 16,10 + ld hl,Coord + ld a,[wListMenuID] + cp a,PRICEDITEMLISTMENU + jr nz,.printInitialQuantity + FuncCoord 8,10 + ld hl,Coord +.printInitialQuantity + ld de,InitialQuantityText + call PlaceString + xor a + ld [$cf96],a ; initialize current quantity to 0 + jp .incrementQuantity +.waitForKeyPressLoop + call GetJoypadStateLowSensitivity + ld a,[H_NEWLYPRESSEDBUTTONS] ; newly pressed buttons + bit 0,a ; was the A button pressed? + jp nz,.buttonAPressed + bit 1,a ; was the B button pressed? + jp nz,.buttonBPressed + bit 6,a ; was Up pressed? + jr nz,.incrementQuantity + bit 7,a ; was Down pressed? + jr nz,.decrementQuantity + jr .waitForKeyPressLoop +.incrementQuantity + ld a,[$cf97] ; max quantity + inc a + ld b,a + ld hl,$cf96 ; current quantity + inc [hl] + ld a,[hl] + cp b + jr nz,.handleNewQuantity +; wrap to 1 if the player goes above the max quantity + ld a,1 + ld [hl],a + jr .handleNewQuantity +.decrementQuantity + ld hl,$cf96 ; current quantity + dec [hl] + jr nz,.handleNewQuantity +; wrap to the max quantity if the player goes below 1 + ld a,[$cf97] ; max quantity + ld [hl],a +.handleNewQuantity + FuncCoord 17,10 + ld hl,Coord + ld a,[wListMenuID] + cp a,PRICEDITEMLISTMENU + jr nz,.printQuantity +.printPrice + ld c,$03 + ld a,[$cf96] + ld b,a + ld hl,$ff9f ; total price +; initialize total price to 0 + xor a + ld [hli],a + ld [hli],a + ld [hl],a +.addLoop ; loop to multiply the individual price by the quantity to get the total price + ld de,$ffa1 + ld hl,$ff8d + push bc + ld a,$0b + call Predef ; add the individual price to the current sum + pop bc + dec b + jr nz,.addLoop + ld a,[$ff8e] + and a ; should the price be halved (for selling items)? + jr z,.skipHalvingPrice + xor a + ld [$ffa2],a + ld [$ffa3],a + ld a,$02 + ld [$ffa4],a + ld a,$0d + call Predef ; halves the price +; store the halved price + ld a,[$ffa2] + ld [$ff9f],a + ld a,[$ffa3] + ld [$ffa0],a + ld a,[$ffa4] + ld [$ffa1],a +.skipHalvingPrice + FuncCoord 12,10 + ld hl,Coord + ld de,SpacesBetweenQuantityAndPriceText + call PlaceString + ld de,$ff9f ; total price + ld c,$a3 + call PrintBCDNumber + FuncCoord 9,10 + ld hl,Coord +.printQuantity + ld de,$cf96 ; current quantity + ld bc,$8102 ; print leading zeroes, 1 byte, 2 digits + call PrintNumber + jp .waitForKeyPressLoop +.buttonAPressed ; the player chose to make the transaction + xor a + ld [$cc35],a ; 0 means no item is currently being swapped + ret +.buttonBPressed ; the player chose to cancel the transaction + xor a + ld [$cc35],a ; 0 means no item is currently being swapped + ld a,$ff + ret + +InitialQuantityText:: ; 2e30 (0:2e30) + db "×01@" + +SpacesBetweenQuantityAndPriceText:: ; 2e34 (0:2e34) + db " @" + +ExitListMenu:: ; 2e3b (0:2e3b) + ld a,[wCurrentMenuItem] + ld [$d12d],a + ld a,$02 + ld [$d12e],a + ld [$cc37],a + xor a + ld [$ffb7],a + ld hl,$d730 + res 6,[hl] + call BankswitchBack + xor a + ld [$cc35],a ; 0 means no item is currently being swapped + scf + ret + +PrintListMenuEntries:: ; 2e5a (0:2e5a) + FuncCoord 5, 3 ; $c3e1 + ld hl,Coord + ld b,$09 + ld c,$0e + call ClearScreenArea + ld a,[$cf8b] + ld e,a + ld a,[$cf8c] + ld d,a + inc de ; de = beginning of list entries + ld a,[wListScrollOffset] + ld c,a + ld a,[wListMenuID] + cp a,ITEMLISTMENU + ld a,c + jr nz,.skipMultiplying +; if it's an item menu +; item entries are 2 bytes long, so multiply by 2 + sla a + sla c +.skipMultiplying + add e + ld e,a + jr nc,.noCarry + inc d +.noCarry + FuncCoord 6,4 ; coordinates of first list entry name + ld hl,Coord + ld b,4 ; print 4 names +.loop + ld a,b + ld [wWhichPokemon],a + ld a,[de] + ld [$d11e],a + cp a,$ff + jp z,.printCancelMenuItem + push bc + push de + push hl + push hl + push de + ld a,[wListMenuID] + and a + jr z,.pokemonPCMenu + cp a,$01 + jr z,.movesMenu +.itemMenu + call GetItemName + jr .placeNameString +.pokemonPCMenu + push hl + ld hl,W_NUMINPARTY + ld a,[$cf8b] + cp l ; is it a list of party pokemon or box pokemon? + ld hl,W_PARTYMON1NAME + jr z,.getPokemonName + ld hl, W_BOXMON1NAME ; box pokemon names +.getPokemonName + ld a,[wWhichPokemon] + ld b,a + ld a,4 + sub b + ld b,a + ld a,[wListScrollOffset] + add b + call GetPartyMonName + pop hl + jr .placeNameString +.movesMenu + call GetMoveName +.placeNameString + call PlaceString + pop de + pop hl + ld a,[$cf93] + and a ; should prices be printed? + jr z,.skipPrintingItemPrice +.printItemPrice + push hl + ld a,[de] + ld de,ItemPrices + ld [$cf91],a + call GetItemPrice ; get price + pop hl + ld bc,20 + 5 ; 1 row down and 5 columns right + add hl,bc + ld c,$a3 ; no leading zeroes, right-aligned, print currency symbol, 3 bytes + call PrintBCDNumber +.skipPrintingItemPrice + ld a,[wListMenuID] + and a + jr nz,.skipPrintingPokemonLevel +.printPokemonLevel + ld a,[$d11e] + push af + push hl + ld hl,W_NUMINPARTY + ld a,[$cf8b] + cp l ; is it a list of party pokemon or box pokemon? + ld a,$00 + jr z,.next + ld a,$02 +.next + ld [$cc49],a + ld hl,wWhichPokemon + ld a,[hl] + ld b,a + ld a,$04 + sub b + ld b,a + ld a,[wListScrollOffset] + add b + ld [hl],a + call LoadMonData ; load pokemon info + ld a,[$cc49] + and a ; is it a list of party pokemon or box pokemon? + jr z,.skipCopyingLevel +.copyLevel + ld a,[$cf9b] + ld [$cfb9],a +.skipCopyingLevel + pop hl + ld bc,$001c + add hl,bc + call PrintLevel ; print level + pop af + ld [$d11e],a +.skipPrintingPokemonLevel + pop hl + pop de + inc de + ld a,[wListMenuID] + cp a,ITEMLISTMENU + jr nz,.nextListEntry +.printItemQuantity + ld a,[$d11e] + ld [$cf91],a + call IsKeyItem ; check if item is unsellable + ld a,[$d124] + and a ; is the item unsellable? + jr nz,.skipPrintingItemQuantity ; if so, don't print the quantity + push hl + ld bc,20 + 8 ; 1 row down and 8 columns right + add hl,bc + ld a,"×" + ldi [hl],a + ld a,[$d11e] + push af + ld a,[de] + ld [$cf97],a + push de + ld de,$d11e + ld [de],a + ld bc,$0102 + call PrintNumber + pop de + pop af + ld [$d11e],a + pop hl +.skipPrintingItemQuantity + inc de + pop bc + inc c + push bc + inc c + ld a,[$cc35] ; ID of item chosen for swapping (counts from 1) + and a ; is an item being swapped? + jr z,.nextListEntry + sla a + cp c ; is it this item? + jr nz,.nextListEntry + dec hl + ld a,$ec ; unfilled right arrow menu cursor to indicate an item being swapped + ld [hli],a +.nextListEntry + ld bc,2 * 20 ; 2 rows + add hl,bc + pop bc + inc c + dec b + jp nz,.loop + ld bc,-8 + add hl,bc + ld a,$ee ; down arrow + ld [hl],a + ret +.printCancelMenuItem + ld de,ListMenuCancelText + jp PlaceString + +ListMenuCancelText:: ; 2f97 (0:2f97) + db "CANCEL@" + +GetMonName:: ; 2f9e (0:2f9e) + push hl + ld a,[H_LOADEDROMBANK] + push af + ld a,BANK(MonsterNames) ; 07 + ld [H_LOADEDROMBANK],a + ld [$2000],a + ld a,[$d11e] + dec a + ld hl,MonsterNames ; 421E + ld c,10 + ld b,0 + call AddNTimes + ld de,$cd6d + push de + ld bc,10 + call CopyData + ld hl,$cd77 + ld [hl], "@" + pop de + pop af + ld [H_LOADEDROMBANK],a + ld [$2000],a + pop hl + ret + +GetItemName:: ; 2fcf (0:2fcf) +; given an item ID at [$D11E], store the name of the item into a string +; starting at $CD6D + push hl + push bc + ld a,[$D11E] + cp HM_01 ; is this a TM/HM? + jr nc,.Machine + + ld [$D0B5],a + ld a,ITEM_NAME + ld [W_LISTTYPE],a + ld a,BANK(ItemNames) + ld [$D0B7],a + call GetName + jr .Finish + +.Machine + call GetMachineName +.Finish + ld de,$CD6D ; pointer to where item name is stored in RAM + pop bc + pop hl + ret + +GetMachineName:: ; 2ff3 (0:2ff3) +; copies the name of the TM/HM in [$D11E] to $CD6D + push hl + push de + push bc + ld a,[$D11E] + push af + cp TM_01 ; is this a TM? [not HM] + jr nc,.WriteTM +; if HM, then write "HM" and add 5 to the item ID, so we can reuse the +; TM printing code + add 5 + ld [$D11E],a + ld hl,HiddenPrefix ; points to "HM" + ld bc,2 + jr .WriteMachinePrefix +.WriteTM + ld hl,TechnicalPrefix ; points to "TM" + ld bc,2 +.WriteMachinePrefix + ld de,$CD6D + call CopyData + +; now get the machine number and convert it to text + ld a,[$D11E] + sub TM_01 - 1 + ld b,$F6 ; "0" +.FirstDigit + sub 10 + jr c,.SecondDigit + inc b + jr .FirstDigit +.SecondDigit + add 10 + push af + ld a,b + ld [de],a + inc de + pop af + ld b,$F6 ; "0" + add b + ld [de],a + inc de + ld a,"@" + ld [de],a + + pop af + ld [$D11E],a + pop bc + pop de + pop hl + ret + +TechnicalPrefix:: ; 303c (0:303c) + db "TM" +HiddenPrefix:: ; 303e (0:303e) + db "HM" + +; sets carry if item is HM, clears carry if item is not HM +; Input: a = item ID +IsItemHM:: ; 3040 (0:3040) + cp a,HM_01 + jr c,.notHM + cp a,TM_01 + ret +.notHM + and a + ret + +; sets carry if move is an HM, clears carry if move is not an HM +; Input: a = move ID +IsMoveHM:: ; 3049 (0:3049) + ld hl,HMMoves + ld de,1 + jp IsInArray + +HMMoves:: ; 3052 (0:3052) + db CUT,FLY,SURF,STRENGTH,FLASH + db $ff ; terminator + +GetMoveName:: ; 3058 (0:3058) + push hl + ld a,MOVE_NAME + ld [W_LISTTYPE],a + ld a,[$d11e] + ld [$d0b5],a + ld a,BANK(MoveNames) + ld [$d0b7],a + call GetName + ld de,$cd6d ; pointer to where move name is stored in RAM + pop hl + ret + +; reloads text box tile patterns, current map view, and tileset tile patterns +ReloadMapData:: ; 3071 (0:3071) + ld a,[H_LOADEDROMBANK] + push af + ld a,[W_CURMAP] + call SwitchToMapRomBank + call DisableLCD + call LoadTextBoxTilePatterns + call LoadCurrentMapView + call LoadTilesetTilePatternData + call EnableLCD + pop af + ld [H_LOADEDROMBANK],a + ld [$2000],a + ret + +; reloads tileset tile patterns +ReloadTilesetTilePatterns:: ; 3090 (0:3090) + ld a,[H_LOADEDROMBANK] + push af + ld a,[W_CURMAP] + call SwitchToMapRomBank + call DisableLCD + call LoadTilesetTilePatternData + call EnableLCD + pop af + ld [H_LOADEDROMBANK],a + ld [$2000],a + ret + +; shows the town map and lets the player choose a destination to fly to +ChooseFlyDestination:: ; 30a9 (0:30a9) + ld hl,$d72e + res 4,[hl] + ld b, BANK(LoadTownMap_Fly) + ld hl, LoadTownMap_Fly + jp Bankswitch + +; causes the text box to close waithout waiting for a button press after displaying text +DisableWaitingAfterTextDisplay:: ; 30b6 (0:30b6) + ld a,$01 + ld [$cc3c],a + ret + +; uses an item +; UseItem is used with dummy items to perform certain other functions as well +; INPUT: +; [$cf91] = item ID +; OUTPUT: +; [$cd6a] = success +; 00: unsucessful +; 01: successful +; 02: not able to be used right now, no extra menu displayed (only certain items use this) +UseItem:: ; 30bc (0:30bc) + ld b,BANK(UseItem_) + ld hl,UseItem_ + jp Bankswitch + +; confirms the item toss and then tosses the item +; INPUT: +; hl = address of inventory (either wNumBagItems or wNumBoxItems) +; [$cf91] = item ID +; [$cf92] = index of item within inventory +; [$cf96] = quantity to toss +; OUTPUT: +; clears carry flag if the item is tossed, sets carry flag if not +TossItem:: ; 30c4 (0:30c4) + ld a,[H_LOADEDROMBANK] + push af + ld a,BANK(TossItem_) + ld [H_LOADEDROMBANK],a + ld [$2000],a + call TossItem_ + pop de + ld a,d + ld [H_LOADEDROMBANK],a + ld [$2000],a + ret + +; checks if an item is a key item +; INPUT: +; [$cf91] = item ID +; OUTPUT: +; [$d124] = result +; 00: item is not key item +; 01: item is key item +IsKeyItem:: ; 30d9 (0:30d9) + push hl + push de + push bc + callba IsKeyItem_ + pop bc + pop de + pop hl + ret + +; function to draw various text boxes +; INPUT: +; [$D125] = text box ID +DisplayTextBoxID:: ; 30e8 (0:30e8) + ld a,[H_LOADEDROMBANK] + push af + ld a,BANK(DisplayTextBoxID_) + ld [H_LOADEDROMBANK],a + ld [$2000],a + call DisplayTextBoxID_ + pop bc + ld a,b + ld [H_LOADEDROMBANK],a + ld [$2000],a + ret + +Func_30fd:: ; 30fd (0:30fd) + ld a, [$cc57] + and a + ret nz + ld a, [$d736] + bit 1, a + ret nz + ld a, [$d730] + and $80 + ret + +Func_310e:: ; 310e (0:310e) + ld hl, $d736 + bit 0, [hl] + res 0, [hl] + jr nz, .asm_3146 + ld a, [$cc57] + and a + ret z + dec a + add a + ld d, $0 + ld e, a + ld hl, .pointerTable_3140 + add hl, de + ld a, [hli] + ld h, [hl] + ld l, a + ld a, [H_LOADEDROMBANK] + push af + ld a, [$cc58] + ld [H_LOADEDROMBANK], a + ld [$2000], a + ld a, [$cf10] + call CallFunctionInTable + pop af + ld [H_LOADEDROMBANK], a + ld [$2000], a + ret +.pointerTable_3140 + dw PointerTable_1a442 + dw PointerTable_1a510 + dw PointerTable_1a57d +.asm_3146 + ld b, BANK(Func_1a3e0) + ld hl, Func_1a3e0 + jp Bankswitch + +Func_314e:: ; 314e (0:314e) + ld b, BANK(Func_1a41d) + ld hl, Func_1a41d + jp Bankswitch + +Func_3156:: ; 3156 (0:3156) + ret + +; stores hl in [W_TRAINERHEADERPTR] +StoreTrainerHeaderPointer:: ; 3157 (0:3157) + ld a, h + ld [W_TRAINERHEADERPTR], a + ld a, l + ld [W_TRAINERHEADERPTR+1], a + ret + +; executes the current map script from the function pointer array provided in hl. +; a: map script index to execute (unless overridden by [$d733] bit 4) +ExecuteCurMapScriptInTable:: ; 3160 (0:3160) + push af + push de + call StoreTrainerHeaderPointer + pop hl + pop af + push hl + ld hl, W_FLAGS_D733 + bit 4, [hl] + res 4, [hl] + jr z, .useProvidedIndex ; test if map script index was overridden manually + ld a, [W_CURMAPSCRIPT] +.useProvidedIndex + pop hl + ld [W_CURMAPSCRIPT], a + call CallFunctionInTable + ld a, [W_CURMAPSCRIPT] + ret + +LoadGymLeaderAndCityName:: ; 317f (0:317f) + push de + ld de, wGymCityName + ld bc, $11 + call CopyData ; load city name + pop hl + ld de, wGymLeaderName + ld bc, $b + jp CopyData ; load gym leader name + +; reads specific information from trainer header (pointed to at W_TRAINERHEADERPTR) +; a: offset in header data +; 0 -> flag's bit (into wTrainerHeaderFlagBit) +; 2 -> flag's byte ptr (into hl) +; 4 -> before battle text (into hl) +; 6 -> after battle text (into hl) +; 8 -> end battle text (into hl) +ReadTrainerHeaderInfo:: ; 3193 (0:3193) + push de + push af + ld d, $0 + ld e, a + ld hl, W_TRAINERHEADERPTR + ld a, [hli] + ld l, [hl] + ld h, a + add hl, de + pop af + and a + jr nz, .nonZeroOffset + ld a, [hl] + ld [wTrainerHeaderFlagBit], a ; store flag's bit + jr .done +.nonZeroOffset + cp $2 + jr z, .readPointer ; read flag's byte ptr + cp $4 + jr z, .readPointer ; read before battle text + cp $6 + jr z, .readPointer ; read after battle text + cp $8 + jr z, .readPointer ; read end battle text + cp $a + jr nz, .done + ld a, [hli] ; read end battle text (2) but override the result afterwards (XXX why, bug?) + ld d, [hl] + ld e, a + jr .done +.readPointer + ld a, [hli] + ld h, [hl] + ld l, a +.done + pop de + ret + +TrainerFlagAction:: + ld a, $10 ; FlagActionPredef + jp Predef + +; direct talking to a trainer (rather than getting seen by one) +TalkToTrainer:: ; 31cc (0:31cc) + call StoreTrainerHeaderPointer + xor a + call ReadTrainerHeaderInfo ; read flag's bit + ld a, $2 + call ReadTrainerHeaderInfo ; read flag's byte ptr + ld a, [wTrainerHeaderFlagBit] + ld c, a + ld b, $2 + call TrainerFlagAction ; read trainer's flag + ld a, c + and a + jr z, .trainerNotYetFought ; test trainer's flag + ld a, $6 + call ReadTrainerHeaderInfo ; print after battle text + jp PrintText +.trainerNotYetFought ; 0x31ed + ld a, $4 + call ReadTrainerHeaderInfo ; print before battle text + call PrintText + ld a, $a + call ReadTrainerHeaderInfo ; (?) does nothing apparently (maybe bug in ReadTrainerHeaderInfo) + push de + ld a, $8 + call ReadTrainerHeaderInfo ; read end battle text + pop de + call PreBattleSaveRegisters + ld hl, W_FLAGS_D733 + set 4, [hl] ; activate map script index override (index is set below) + ld hl, wFlags_0xcd60 + bit 0, [hl] ; test if player is already being engaged by another trainer + ret nz + call EngageMapTrainer + ld hl, W_CURMAPSCRIPT + inc [hl] ; progress map script index (assuming it was 0 before) to start pre-battle routines + jp Func_325d + +; checks if any trainers are seeing the player and wanting to fight +CheckFightingMapTrainers:: ; 3219 (0:3219) + call CheckForEngagingTrainers + ld a, [$cf13] + cp $ff + jr nz, .trainerEngaging + xor a + ld [$cf13], a + ld [wTrainerHeaderFlagBit], a + ret +.trainerEngaging + ld hl, W_FLAGS_D733 + set 3, [hl] + ld [$cd4f], a + xor a + ld [$cd50], a + ld a, $4c + call Predef + ld a, D_RIGHT | D_LEFT | D_UP | D_DOWN + ld [wJoypadForbiddenButtonsMask], a + xor a + ldh [$b4], a + call TrainerWalkUpToPlayer_Bank0 + ld hl, W_CURMAPSCRIPT + inc [hl] ; progress to battle phase 1 (engaging) + ret + +Func_324c:: ; 324c (0:324c) + ld a, [$d730] + and $1 + ret nz + ld [wJoypadForbiddenButtonsMask], a + ld a, [$cf13] + ld [H_DOWNARROWBLINKCNT2], a ; $ff8c + call DisplayTextID + +Func_325d:: ; 325d (0:325d) + xor a + ld [wJoypadForbiddenButtonsMask], a + call InitBattleEnemyParameters + ld hl, $d72d + set 6, [hl] + set 7, [hl] + ld hl, $d72e + set 1, [hl] + ld hl, W_CURMAPSCRIPT + inc [hl] ; progress to battle phase 2 (battling) + ret + +EndTrainerBattle:: ; 3275 (0:3275) + ld hl, $d126 + set 5, [hl] + set 6, [hl] + ld hl, $d72d + res 7, [hl] + ld hl, wFlags_0xcd60 + res 0, [hl] ; player is no longer engaged by any trainer + ld a, [W_ISINBATTLE] ; $d057 + cp $ff + jp z, ResetButtonPressedAndMapScript + ld a, $2 + call ReadTrainerHeaderInfo + ld a, [wTrainerHeaderFlagBit] + ld c, a + ld b, $1 + call TrainerFlagAction ; flag trainer as fought + ld a, [W_ENEMYMONORTRAINERCLASS] + cp $c8 + jr nc, .skipRemoveSprite ; test if trainer was fought (in that case skip removing the corresponding sprite) + ld hl, W_MISSABLEOBJECTLIST + ld de, $2 + ld a, [$cf13] + call IsInArray ; search for sprite ID + inc hl + ld a, [hl] + ld [$cc4d], a ; load corresponding missable object index and remove it + ld a, $11 + call Predef ; indirect jump to RemoveMissableObject (f1d7 (3:71d7)) +.skipRemoveSprite + ld hl, $d730 + bit 4, [hl] + res 4, [hl] + ret nz + +ResetButtonPressedAndMapScript:: ; 32c1 (0:32c1) + xor a + ld [wJoypadForbiddenButtonsMask], a + ld [H_CURRENTPRESSEDBUTTONS], a + ld [H_NEWLYPRESSEDBUTTONS], a + ld [H_NEWLYRELEASEDBUTTONS], a + ld [W_CURMAPSCRIPT], a ; reset battle status + ret + +; calls TrainerWalkUpToPlayer +TrainerWalkUpToPlayer_Bank0:: ; 32cf (0:32cf) + ld b, BANK(TrainerWalkUpToPlayer) + ld hl, TrainerWalkUpToPlayer + jp Bankswitch + +; sets opponent type and mon set/lvl based on the engaging trainer data +InitBattleEnemyParameters:: ; 32d7 (0:32d7) + ld a, [wEngagedTrainerClass] + ld [W_CUROPPONENT], a ; $d059 + ld [W_ENEMYMONORTRAINERCLASS], a + cp $c8 + ld a, [wEngagedTrainerSet] ; $cd2e + jr c, .noTrainer + ld [W_TRAINERNO], a ; $d05d + ret +.noTrainer + ld [W_CURENEMYLVL], a ; $d127 + ret + +Func_32ef:: ; 32ef (0:32ef) + ld hl, Func_567f9 + jr asm_3301 + +Func_32f4:: ; 32f4 (0:32f4) + ld hl, Func_56819 + jr asm_3301 ; 0x32f7 $8 + +Func_32f9:: ; 32f9 (0:32f9) + ld hl, Func_5683d + jr asm_3301 + +Func_32fe:: ; 32fe (0:32fe) + ld hl, Func_5685d +asm_3301:: ; 3301 (0:3301) + ld b, BANK(Func_567f9) ; BANK(Func_56819), BANK(Func_5683d), BANK(Func_5685d) + jp Bankswitch ; indirect jump to one of the four functions + +CheckForEngagingTrainers:: ; 3306 (0:3306) + xor a + call ReadTrainerHeaderInfo ; read trainer flag's bit (unused) + ld d, h ; store trainer header address in de + ld e, l +.trainerLoop + call StoreTrainerHeaderPointer ; set trainer header pointer to current trainer + ld a, [de] + ld [$cf13], a ; store trainer flag's bit + ld [wTrainerHeaderFlagBit], a + cp $ff + ret z + ld a, $2 + call ReadTrainerHeaderInfo ; read trainer flag's byte ptr + ld b, $2 + ld a, [wTrainerHeaderFlagBit] + ld c, a + call TrainerFlagAction ; read trainer flag + ld a, c + and a + jr nz, .trainerAlreadyFought + push hl + push de + push hl + xor a + call ReadTrainerHeaderInfo ; get trainer header pointer + inc hl + ld a, [hl] ; read trainer engage distance + pop hl + ld [wTrainerEngageDistance], a + ld a, [$cf13] + swap a + ld [wTrainerSpriteOffset], a ; $cd3d + ld a, $39 + call Predef ; indirect jump to CheckEngagePlayer (5690f (15:690f)) + pop de + pop hl + ld a, [wTrainerSpriteOffset] ; $cd3d + and a + ret nz ; break if the trainer is engaging +.trainerAlreadyFought + ld hl, $c + add hl, de + ld d, h + ld e, l + jr .trainerLoop + +; saves loaded rom bank and hl as well as de registers +PreBattleSaveRegisters:: ; 3354 (0:3354) + ld a, [H_LOADEDROMBANK] + ld [W_PBSTOREDROMBANK], a + ld a, h + ld [W_PBSTOREDREGISTERH], a + ld a, l + ld [W_PBSTOREDREGISTERL], a + ld a, d + ld [W_PBSTOREDREGISTERD], a + ld a, e + ld [W_PBSTOREDREGISTERE], a + ret + +; loads data of some trainer on the current map and plays pre-battle music +; [$cf13]: sprite ID of trainer who is engaged +EngageMapTrainer:: ; 336a (0:336a) + ld hl, W_MAPSPRITEEXTRADATA + ld d, $0 + ld a, [$cf13] + dec a + add a + ld e, a + add hl, de ; seek to engaged trainer data + ld a, [hli] ; load trainer class + ld [wEngagedTrainerClass], a + ld a, [hl] ; load trainer mon set + ld [wEnemyMonAttackMod], a ; $cd2e + jp PlayTrainerMusic + +Func_3381:: ; 3381 (0:3381) + push hl + ld hl, $d72d + bit 7, [hl] + res 7, [hl] + pop hl + ret z + ld a, [H_LOADEDROMBANK] + push af + ld a, [W_PBSTOREDROMBANK] + ld [H_LOADEDROMBANK], a + ld [$2000], a + push hl + callba SaveTrainerName + ld hl, TrainerNameText + call PrintText + pop hl + pop af + ld [H_LOADEDROMBANK], a + ld [$2000], a + callba Func_1a5e7 + jp WaitForSoundToFinish + +Func_33b7:: ; 33b7 (0:33b7) + ld a, [$cf0b] + and a + jr nz, .asm_33c6 + ld a, [W_PBSTOREDREGISTERH] + ld h, a + ld a, [W_PBSTOREDREGISTERL] + ld l, a + ret +.asm_33c6 + ld a, [W_PBSTOREDREGISTERD] + ld h, a + ld a, [W_PBSTOREDREGISTERE] + ld l, a + ret + +TrainerNameText:: ; 33cf (0:33cf) + TX_FAR _TrainerNameText + db $08 + +Func_33d4:: ; 33d4 (0:33d4) + call Func_33b7 + call TextCommandProcessor + jp TextScriptEnd + +Func_33dd:: ; 33dd (0:33dd) + ld a, [wFlags_0xcd60] + bit 0, a + ret nz + call EngageMapTrainer + xor a + ret + +PlayTrainerMusic:: ; 33e8 (0:33e8) + ld a, [wEngagedTrainerClass] + cp $c8 + SONY1 + ret z + cp $c8 + SONY2 + ret z + cp $c8 + SONY3 + ret z + ld a, [W_GYMLEADERNO] ; $d05c + and a + ret nz + xor a + ld [wMusicHeaderPointer], a + ld a, $ff + call PlaySound ; stop music + ld a, BANK(Music_MeetEvilTrainer) + ld [$c0ef], a + ld [$c0f0], a + ld a, [wEngagedTrainerClass] + ld b, a + ld hl, EvilTrainerList +.evilTrainerListLoop + ld a, [hli] + cp $ff + jr z, .noEvilTrainer + cp b + jr nz, .evilTrainerListLoop + ld a, MUSIC_MEET_EVIL_TRAINER + jr .PlaySound +.noEvilTrainer + ld hl, FemaleTrainerList +.femaleTrainerListLoop + ld a, [hli] + cp $ff + jr z, .maleTrainer + cp b + jr nz, .femaleTrainerListLoop + ld a, MUSIC_MEET_FEMALE_TRAINER + jr .PlaySound +.maleTrainer + ld a, MUSIC_MEET_MALE_TRAINER +.PlaySound + ld [$c0ee], a + jp PlaySound + +INCLUDE "data/trainer_types.asm" + +Func_3442:: ; 3442 (0:3442) + ld a, [hli] + cp $ff + ret z + cp b + jr nz, .asm_345b + ld a, [hli] + cp c + jr nz, .asm_345c + ld a, [hli] + ld d, [hl] + ld e, a + ld hl, $ccd3 + call DecodeRLEList + dec a + ld [$cd38], a + ret +.asm_345b + inc hl +.asm_345c + inc hl + inc hl + jr Func_3442 + +FuncTX_ItemStoragePC:: ; 3460 (0:3460) + call SaveScreenTilesToBuffer2 + ld b, BANK(PlayerPC) + ld hl, PlayerPC + jr bankswitchAndContinue + +FuncTX_BillsPC:: ; 346a (0:346a) + call SaveScreenTilesToBuffer2 + ld b, BANK(Func_214c2) + ld hl, Func_214c2 + jr bankswitchAndContinue + +FuncTX_SlotMachine:: ; 3474 (0:3474) +; XXX find a better name for this function +; special_F7 + ld b,BANK(CeladonPrizeMenu) + ld hl,CeladonPrizeMenu +bankswitchAndContinue:: ; 3479 (0:3479) + call Bankswitch + jp HoldTextDisplayOpen ; continue to main text-engine function + +FuncTX_PokemonCenterPC:: ; 347f (0:347f) + ld b, BANK(ActivatePC) + ld hl, ActivatePC + jr bankswitchAndContinue + +Func_3486:: ; 3486 (0:3486) + xor a + ld [$cd3b], a + ld [$c206], a + ld hl, $d730 + set 7, [hl] + ret + +IsItemInBag:: ; 3493 (0:3493) +; given an item_id in b +; set zero flag if item isn't in player's bag +; else reset zero flag +; related to Pokémon Tower and ghosts + ld a,$1C + call Predef + ld a,b + and a + ret + +DisplayPokedex:: ; 349b (0:349b) + ld [$d11e], a + ld b, BANK(Func_7c18) + ld hl, Func_7c18 + jp Bankswitch + +Func_34a6:: ; 34a6 (0:34a6) + call Func_34ae + ld c, $6 + jp DelayFrames + +Func_34ae:: ; 34ae (0:34ae) + ld a, $9 + ld [H_DOWNARROWBLINKCNT1], a ; $ff8b + call Func_34fc + ld a, [$ff8d] + ld [hl], a + ret + +Func_34b9:: ; 34b9 (0:34b9) + ld de, $fff9 + add hl, de + ld [hl], a + ret + +; tests if the player's coordinates are in a specified array +; INPUT: +; hl = address of array +; OUTPUT: +; [$cd3d] = if there is match, the matching array index +; sets carry if the coordinates are in the array, clears carry if not +ArePlayerCoordsInArray:: ; 34bf (0:34bf) + ld a,[W_YCOORD] + ld b,a + ld a,[W_XCOORD] + ld c,a + ; fallthrough + +CheckCoords:: ; 34c7 (0:34c7) + xor a + ld [$cd3d],a +.loop + ld a,[hli] + cp a,$ff ; reached terminator? + jr z,.notInArray + push hl + ld hl,$cd3d + inc [hl] + pop hl +.compareYCoord + cp b + jr z,.compareXCoord + inc hl + jr .loop +.compareXCoord + ld a,[hli] + cp c + jr nz,.loop +.inArray + scf + ret +.notInArray + and a + ret + +; tests if a boulder's coordinates are in a specified array +; INPUT: +; hl = address of array +; ff8c = which boulder to check? XXX +; OUTPUT: +; [$cd3d] = if there is match, the matching array index +; sets carry if the coordinates are in the array, clears carry if not +CheckBoulderCoords:: ; 34e4 (0:34e4) + push hl + ld hl, $c204 + ld a, [$ff8c] + swap a + ld d, $0 + ld e, a + add hl, de + ld a, [hli] + sub $4 ; because sprite coordinates are offset by 4 + ld b, a + ld a, [hl] + sub $4 ; because sprite coordinates are offset by 4 + ld c, a + pop hl + jp CheckCoords + +Func_34fc:: ; 34fc (0:34fc) + ld h, $c1 + jr asm_3502 + +Func_3500:: ; 3500 (0:3500) + ld h, $c2 +asm_3502:: ; 3502 (0:3502) + ld a, [H_DOWNARROWBLINKCNT1] ; $ff8b + ld b, a + ld a, [H_DOWNARROWBLINKCNT2] ; $ff8c + swap a + add b + ld l, a + ret + +; decodes a $ff-terminated RLEncoded list +; each entry is a pair of bytes <byte value> <repetitions> +; the final $ff will be replicated in the output list and a contains the number of bytes written +; de: input list +; hl: output list +DecodeRLEList:: ; 350c (0:350c) + xor a + ld [wRLEByteCount], a ; count written bytes here +.listLoop + ld a, [de] + cp $ff + jr z, .endOfList + ld [H_DOWNARROWBLINKCNT1], a ; store byte value to be written + inc de + ld a, [de] + ld b, $0 + ld c, a ; number of bytes to be written + ld a, [wRLEByteCount] + add c + ld [wRLEByteCount], a ; update total number of written bytes + ld a, [H_DOWNARROWBLINKCNT1] ; $ff8b + call FillMemory ; write a c-times to output + inc de + jr .listLoop +.endOfList + ld a, $ff + ld [hl], a ; write final $ff + ld a, [wRLEByteCount] + inc a ; include sentinel in counting + ret + +; sets movement byte 1 for sprite [$FF8C] to $FE and byte 2 to [$FF8D] +SetSpriteMovementBytesToFE:: ; 3533 (0:3533) + push hl + call GetSpriteMovementByte1Pointer + ld [hl], $fe + call GetSpriteMovementByte2Pointer + ld a, [$ff8d] + ld [hl], a + pop hl + ret + +; sets both movement bytes for sprite [$FF8C] to $FF +SetSpriteMovementBytesToFF:: ; 3541 (0:3541) + push hl + call GetSpriteMovementByte1Pointer + ld [hl],$FF + call GetSpriteMovementByte2Pointer + ld [hl],$FF ; prevent person from walking? + pop hl + ret + +; returns the sprite movement byte 1 pointer for sprite [$FF8C] in hl +GetSpriteMovementByte1Pointer:: ; 354e (0:354e) + ld h,$C2 + ld a,[$FF8C] ; the sprite to move + swap a + add a,6 + ld l,a + ret + +; returns the sprite movement byte 2 pointer for sprite [$FF8C] in hl +GetSpriteMovementByte2Pointer:: ; 3558 (0:3558) + push de + ld hl,W_MAPSPRITEDATA + ld a,[$FF8C] ; the sprite to move + dec a + add a + ld d,0 + ld e,a + add hl,de + pop de + ret + +GetTrainerInformation:: ; 3566 (0:3566) + call GetTrainerName + ld a, [W_ISLINKBATTLE] ; $d12b + and a + jr nz, .linkBattle + ld a, Bank(TrainerPicAndMoneyPointers) + call BankswitchHome + ld a, [W_TRAINERCLASS] ; $d031 + dec a + ld hl, TrainerPicAndMoneyPointers + ld bc, $5 + call AddNTimes + ld de, $d033 + ld a, [hli] + ld [de], a + inc de + ld a, [hli] + ld [de], a + ld de, $d046 + ld a, [hli] + ld [de], a + inc de + ld a, [hli] + ld [de], a + jp BankswitchBack +.linkBattle + ld hl, $d033 + ld de, RedPicFront + ld [hl], e + inc hl + ld [hl], d + ret + +GetTrainerName:: ; 359e (0:359e) + ld b, BANK(GetTrainerName_) + ld hl, GetTrainerName_ + jp Bankswitch + +; tests if player's money are at least as much as [$ff9f] +; sets carry flag if not enough money +; sets zero flag if amounts match exactly +HasEnoughMoney:: ; 35a6 (0:35a6) + ld de, wPlayerMoney ; $d347 + ld hl, $ff9f + ld c, $3 + jp StringCmp + +; tests if player's game corner coins are at least as many as [$ffa0] +; sets carry flag if not enough coins +; sets zero flag if amounts match exactly +HasEnoughCoins:: ; 35b1 (0:35b1) + ld de, wPlayerCoins + ld hl, $ffa0 + ld c, $2 + jp StringCmp + +BankswitchHome:: ; 35bc (0:35bc) +; switches to bank # in a +; Only use this when in the home bank! + ld [$CF09],a + ld a,[H_LOADEDROMBANK] + ld [$CF08],a + ld a,[$CF09] + ld [H_LOADEDROMBANK],a + ld [$2000],a + ret + +BankswitchBack:: ; 35cd (0:35cd) +; returns from BankswitchHome + ld a,[$CF08] + ld [H_LOADEDROMBANK],a + ld [$2000],a + ret + +Bankswitch:: ; 35d6 (0:35d6) +; self-contained bankswitch, use this when not in the home bank +; switches to the bank in b + ld a,[H_LOADEDROMBANK] + push af + ld a,b + ld [H_LOADEDROMBANK],a + ld [$2000],a + ld bc,.Return + push bc + jp [hl] +.Return + pop bc + ld a,b + ld [H_LOADEDROMBANK],a + ld [$2000],a + ret + +; displays yes/no choice +; yes -> set carry +YesNoChoice:: ; 35ec (0:35ec) + call SaveScreenTilesToBuffer1 + call InitYesNoTextBoxParameters + jr DisplayYesNoChoice + +Func_35f4:: ; 35f4 (0:35f4) + ld a, $14 + ld [$d125], a + call InitYesNoTextBoxParameters + jp DisplayTextBoxID + +InitYesNoTextBoxParameters:: ; 35ff (0:35ff) + xor a + ld [$d12c], a + FuncCoord 14, 7 ; $c43a + ld hl, Coord + ld bc, $80f + ret + +YesNoChoicePokeCenter:: ; 360a (0:360a) + call SaveScreenTilesToBuffer1 + ld a, $6 + ld [$d12c], a + FuncCoord 11, 6 ; $c423 + ld hl, Coord + ld bc, $80c + jr DisplayYesNoChoice + +Func_361a:: ; 361a (0:361a) + call SaveScreenTilesToBuffer1 + ld a, $3 + ld [$d12c], a + FuncCoord 12, 7 ; $c438 + ld hl, Coord + ld bc, $080d +DisplayYesNoChoice:: ; 3628 (0:3628) + ld a, $14 + ld [$d125], a + call DisplayTextBoxID + jp LoadScreenTilesFromBuffer1 + +; calculates the difference |a-b|, setting carry flag if a<b +CalcDifference:: ; 3633 (0:3633) + sub b + ret nc + cpl + add $1 + scf + ret + +MoveSprite:: ; 363a (0:363a) +; move the sprite [$FF8C] with the movement pointed to by de +; actually only copies the movement data to $CC5B for later + call SetSpriteMovementBytesToFF +MoveSprite_:: ; 363d (0:363d) + push hl + push bc + call GetSpriteMovementByte1Pointer + xor a + ld [hl],a + ld hl,$CC5B + ld c,0 + +.loop + ld a,[de] + ld [hli],a + inc de + inc c + cp a,$FF ; have we reached the end of the movement data? + jr nz,.loop + + ld a,c + ld [$CF0F],a ; number of steps taken + + pop bc + ld hl,$D730 + set 0,[hl] + pop hl + xor a + ld [$CD3B],a + ld [$CCD3],a + dec a + ld [wJoypadForbiddenButtonsMask],a + ld [$CD3A],a + ret + +Func_366b:: ; 366b (0:366b) + push hl + ld hl, $ffe7 + xor a + ld [hld], a + ld a, [hld] + and a + jr z, .asm_367e + ld a, [hli] +.asm_3676 + sub [hl] + jr c, .asm_367e + inc hl + inc [hl] + dec hl + jr .asm_3676 +.asm_367e + pop hl + ret + +; copies the tile patterns for letters and numbers into VRAM +LoadFontTilePatterns:: ; 3680 (0:3680) + ld a,[rLCDC] + bit 7,a ; is the LCD enabled? + jr nz,.lcdEnabled +.lcdDisabled + ld hl,FontGraphics + ld de,$8800 + ld bc,$400 + ld a,BANK(FontGraphics) + jp FarCopyDataDouble ; if LCD is off, transfer all at once +.lcdEnabled + ld de,FontGraphics + ld hl,$8800 + ld bc,(BANK(FontGraphics) << 8 | $80) + jp CopyVideoDataDouble ; if LCD is on, transfer during V-blank + +; copies the text box tile patterns into VRAM +LoadTextBoxTilePatterns:: ; 36a0 (0:36a0) + ld a,[rLCDC] + bit 7,a ; is the LCD enabled? + jr nz,.lcdEnabled +.lcdDisabled + ld hl,TextBoxGraphics + ld de,$9600 + ld bc,$0200 + ld a,BANK(TextBoxGraphics) + jp FarCopyData2 ; if LCD is off, transfer all at once +.lcdEnabled + ld de,TextBoxGraphics + ld hl,$9600 + ld bc,(BANK(TextBoxGraphics) << 8 | $20) + jp CopyVideoData ; if LCD is on, transfer during V-blank + +; copies HP bar and status display tile patterns into VRAM +LoadHpBarAndStatusTilePatterns:: ; 36c0 (0:36c0) + ld a,[rLCDC] + bit 7,a ; is the LCD enabled? + jr nz,.lcdEnabled +.lcdDisabled + ld hl,HpBarAndStatusGraphics + ld de,$9620 + ld bc,$01e0 + ld a,BANK(HpBarAndStatusGraphics) + jp FarCopyData2 ; if LCD is off, transfer all at once +.lcdEnabled + ld de,HpBarAndStatusGraphics + ld hl,$9620 + ld bc,(BANK(HpBarAndStatusGraphics) << 8 | $1e) + jp CopyVideoData ; if LCD is on, transfer during V-blank + +;Fills memory range with the specified byte. +;input registers a = fill_byte, bc = length, hl = address +FillMemory:: ; 36e0 (0:36e0) + push de + ld d, a +.loop + ld a, d + ldi [hl], a + dec bc + ld a, b + or c + jr nz, .loop + pop de + ret + +; loads sprite that de points to +; bank of sprite is given in a +UncompressSpriteFromDE:: ; 36eb (0:36eb) + ld hl, W_SPRITEINPUTPTR + ld [hl], e + inc hl + ld [hl], d + jp UncompressSpriteData + +SaveScreenTilesToBuffer2:: ; 36f4 (0:36f4) + ld hl, wTileMap + ld de, wTileMapBackup2 + ld bc, $168 + call CopyData + ret + +LoadScreenTilesFromBuffer2:: ; 3701 (0:3701) + call LoadScreenTilesFromBuffer2DisableBGTransfer + ld a, $1 + ld [H_AUTOBGTRANSFERENABLED], a ; $ffba + ret + +; loads screen tiles stored in wTileMapBackup2 but leaves H_AUTOBGTRANSFERENABLED disabled +LoadScreenTilesFromBuffer2DisableBGTransfer:: ; 3709 (0:3709) + xor a + ld [H_AUTOBGTRANSFERENABLED], a ; $ffba + ld hl, wTileMapBackup2 + ld de, wTileMap + ld bc, $168 + call CopyData + ret + +SaveScreenTilesToBuffer1:: ; 3719 (0:3719) + ld hl, wTileMap + ld de, wTileMapBackup + ld bc, $168 + jp CopyData + +LoadScreenTilesFromBuffer1:: ; 3725 (0:3725) + xor a + ld [H_AUTOBGTRANSFERENABLED], a ; $ffba + ld hl, wTileMapBackup + ld de, wTileMap + ld bc, $168 + call CopyData + ld a, $1 + ld [H_AUTOBGTRANSFERENABLED], a ; $ffba + ret + +DelayFrames:: ; 3739 (0:3739) +; wait n frames, where n is the value in c + call DelayFrame + dec c + jr nz,DelayFrames + ret + +PlaySoundWaitForCurrent:: ; 3740 (0:3740) + push af + call WaitForSoundToFinish + pop af + jp PlaySound + +; Wait for sound to finish playing +WaitForSoundToFinish:: ; 3748 (0:3748) + ld a, [$d083] + and $80 + ret nz + push hl +.asm_374f + ld hl, $c02a + xor a + or [hl] + inc hl + or [hl] + inc hl + inc hl + or [hl] + jr nz, .asm_374f + pop hl + ret + +NamePointers:: ; 375d (0:375d) + dw MonsterNames + dw MoveNames + dw UnusedNames + dw ItemNames + dw W_PARTYMON1OT ; player's OT names list + dw W_ENEMYMON1OT ; enemy's OT names list + dw TrainerNames + +GetName:: ; 376b (0:376b) +; arguments: +; [$D0B5] = which name +; [$D0B6] = which list (W_LISTTYPE) +; [$D0B7] = bank of list +; +; returns pointer to name in de + ld a,[$d0b5] + ld [$d11e],a + cp a,$C4 ;it's TM/HM + jp nc,GetMachineName + ld a,[H_LOADEDROMBANK] + push af + push hl + push bc + push de + ld a,[W_LISTTYPE] ;List3759_entrySelector + dec a + jr nz,.otherEntries + ;1 = MON_NAMES + call GetMonName + ld hl,11 + add hl,de + ld e,l + ld d,h + jr .gotPtr +.otherEntries ; $378d + ;2-7 = OTHER ENTRIES + ld a,[$d0b7] + ld [H_LOADEDROMBANK],a + ld [$2000],a + ld a,[W_LISTTYPE] ;VariousNames' entryID + dec a + add a + ld d,0 + ld e,a + jr nc,.skip + inc d +.skip ; $37a0 + ld hl,NamePointers + add hl,de + ld a,[hli] + ld [$ff96],a + ld a,[hl] + ld [$ff95],a + ld a,[$ff95] + ld h,a + ld a,[$ff96] + ld l,a + ld a,[$d0b5] + ld b,a + ld c,0 +.nextName + ld d,h + ld e,l +.nextChar + ld a,[hli] + cp a, "@" + jr nz,.nextChar + inc c ;entry counter + ld a,b ;wanted entry + cp c + jr nz,.nextName + ld h,d + ld l,e + ld de,$cd6d + ld bc,$0014 + call CopyData +.gotPtr ; $37cd + ld a,e + ld [$cf8d],a + ld a,d + ld [$cf8e],a + pop de + pop bc + pop hl + pop af + ld [H_LOADEDROMBANK],a + ld [$2000],a + ret + +GetItemPrice:: ; 37df (0:37df) + ld a, [H_LOADEDROMBANK] + push af + ld a, [wListMenuID] ; $cf94 + cp $1 + ld a, $1 ; hardcoded Bank + jr nz, .asm_37ed + ld a, $f ; hardcoded Bank +.asm_37ed + ld [H_LOADEDROMBANK], a + ld [$2000], a + ld hl, $cf8f + ld a, [hli] + ld h, [hl] + ld l, a + ld a, [$cf91] + cp HM_01 + jr nc, .asm_3812 + ld bc, $3 +.asm_3802 + add hl, bc + dec a + jr nz, .asm_3802 + dec hl + ld a, [hld] + ld [$ff8d], a + ld a, [hld] + ld [H_DOWNARROWBLINKCNT2], a ; $ff8c + ld a, [hl] + ld [H_DOWNARROWBLINKCNT1], a ; $ff8b + jr .asm_381c +.asm_3812 + ld a, Bank(GetMachinePrice) + ld [H_LOADEDROMBANK], a + ld [$2000], a + call GetMachinePrice +.asm_381c + ld de, H_DOWNARROWBLINKCNT1 ; $ff8b + pop af + ld [H_LOADEDROMBANK], a + ld [$2000], a + ret + +; copies a string from [de] to [$cf4b] +CopyStringToCF4B:: ; 3826 (0:3826) + ld hl, $cf4b + ; fall through + +; copies a string from [de] to [hl] +CopyString:: ; 3829 (0:3829) + ld a, [de] + inc de + ld [hli], a + cp "@" + jr nz, CopyString + ret + +; this function is used when lower button sensitivity is wanted (e.g. menus) +; OUTPUT: [$ffb5] = pressed buttons in usual format +; there are two flags that control its functionality, [$ffb6] and [$ffb7] +; there are esentially three modes of operation +; 1. Get newly pressed buttons only +; ([$ffb7] == 0, [$ffb6] == any) +; Just copies [H_NEWLYPRESSEDBUTTONS] to [$ffb5]. +; 2. Get currently pressed buttons at low sample rate with delay +; ([$ffb7] == 1, [$ffb6] != 0) +; If the user holds down buttons for more than half a second, +; report buttons as being pressed up to 12 times per second thereafter. +; If the user holds down buttons for less than half a second, +; report only one button press. +; 3. Same as 2, but report no buttons as pressed if A or B is held down. +; ([$ffb7] == 1, [$ffb6] == 0) +GetJoypadStateLowSensitivity:: ; 3831 (0:3831) + call GetJoypadState + ld a,[$ffb7] ; flag + and a ; get all currently pressed buttons or only newly pressed buttons? + ld a,[H_NEWLYPRESSEDBUTTONS] ; newly pressed buttons + jr z,.storeButtonState + ld a,[H_CURRENTPRESSEDBUTTONS] ; all currently pressed buttons +.storeButtonState + ld [$ffb5],a + ld a,[H_NEWLYPRESSEDBUTTONS] ; newly pressed buttons + and a ; have any buttons been newly pressed since last check? + jr z,.noNewlyPressedButtons +.newlyPressedButtons + ld a,30 ; half a second delay + ld [H_FRAMECOUNTER],a + ret +.noNewlyPressedButtons + ld a,[H_FRAMECOUNTER] + and a ; is the delay over? + jr z,.delayOver +.delayNotOver + xor a + ld [$ffb5],a ; report no buttons as pressed + ret +.delayOver +; if [$ffb6] = 0 and A or B is pressed, report no buttons as pressed + ld a,[H_CURRENTPRESSEDBUTTONS] + and a,%00000011 ; A and B buttons + jr z,.setShortDelay + ld a,[$ffb6] ; flag + and a + jr nz,.setShortDelay + xor a + ld [$ffb5],a +.setShortDelay + ld a,5 ; 1/12 of a second delay + ld [H_FRAMECOUNTER],a + ret + +WaitForTextScrollButtonPress:: ; 3865 (0:3865) + ld a, [H_DOWNARROWBLINKCNT1] ; $ff8b + push af + ld a, [H_DOWNARROWBLINKCNT2] ; $ff8c + push af + xor a + ld [H_DOWNARROWBLINKCNT1], a ; $ff8b + ld a, $6 + ld [H_DOWNARROWBLINKCNT2], a ; $ff8c +.asm_3872 + push hl + ld a, [$d09b] + and a + jr z, .asm_387c + call Func_716c6 +.asm_387c + FuncCoord 18, 16 ; $c4f2 + ld hl, Coord + call HandleDownArrowBlinkTiming + pop hl + call GetJoypadStateLowSensitivity + ld a, $2d + call Predef ; indirect jump to Func_5a5f (5a5f (1:5a5f)) + ld a, [$ffb5] + and A_BUTTON | B_BUTTON + jr z, .asm_3872 + pop af + ld [H_DOWNARROWBLINKCNT2], a ; $ff8c + pop af + ld [H_DOWNARROWBLINKCNT1], a ; $ff8b + ret + +; (unlass in link battle) waits for A or B being pressed and outputs the scrolling sound effect +ManualTextScroll:: ; 3898 (0:3898) + ld a, [W_ISLINKBATTLE] ; $d12b + cp $4 + jr z, .inLinkBattle + call WaitForTextScrollButtonPress + ld a, (SFX_02_40 - SFX_Headers_02) / 3 + jp PlaySound +.inLinkBattle + ld c, $41 + jp DelayFrames + +; function to do multiplication +; all values are big endian +; INPUT +; FF96-FF98 = multiplicand +; FF99 = multiplier +; OUTPUT +; FF95-FF98 = product +Multiply:: ; 38ac (0:38ac) + push hl + push bc + callab _Multiply + pop bc + pop hl + ret + +; function to do division +; all values are big endian +; INPUT +; FF95-FF98 = dividend +; FF99 = divisor +; b = number of bytes in the dividend (starting from FF95) +; OUTPUT +; FF95-FF98 = quotient +; FF99 = remainder +Divide:: ; 38b9 (0:38b9) + push hl + push de + push bc + ld a,[H_LOADEDROMBANK] + push af + ld a,Bank(_Divide) + ld [H_LOADEDROMBANK],a + ld [$2000],a + call _Divide + pop af + ld [H_LOADEDROMBANK],a + ld [$2000],a + pop bc + pop de + pop hl + ret + +; This function is used to wait a short period after printing a letter to the +; screen unless the player presses the A/B button or the delay is turned off +; through the [$d730] or [$d358] flags. +PrintLetterDelay:: ; 38d3 (0:38d3) + ld a,[$d730] + bit 6,a + ret nz + ld a,[$d358] + bit 1,a + ret z + push hl + push de + push bc + ld a,[$d358] + bit 0,a + jr z,.waitOneFrame + ld a,[$d355] + and a,$0f + ld [H_FRAMECOUNTER],a + jr .checkButtons +.waitOneFrame + ld a,1 + ld [H_FRAMECOUNTER],a +.checkButtons + call GetJoypadState + ld a,[H_CURRENTPRESSEDBUTTONS] +.checkAButton + bit 0,a ; is the A button pressed? + jr z,.checkBButton + jr .endWait +.checkBButton + bit 1,a ; is the B button pressed? + jr z,.buttonsNotPressed +.endWait + call DelayFrame + jr .done +.buttonsNotPressed ; if neither A nor B is pressed + ld a,[H_FRAMECOUNTER] + and a + jr nz,.checkButtons +.done + pop bc + pop de + pop hl + ret + +; Copies [hl, bc) to [de, bc - hl). +; In other words, the source data is from hl up to but not including bc, +; and the destination is de. +CopyDataUntil:: ; 3913 (0:3913) + ld a,[hli] + ld [de],a + inc de + ld a,h + cp b + jr nz,CopyDataUntil + ld a,l + cp c + jr nz,CopyDataUntil + ret + +; Function to remove a pokemon from the party or the current box. +; wWhichPokemon determines the pokemon. +; [$cf95] == 0 specifies the party. +; [$cf95] != 0 specifies the current box. +RemovePokemon:: ; 391f (0:391f) + ld hl, _RemovePokemon + ld b, BANK(_RemovePokemon) + jp Bankswitch + +AddPokemonToParty:: ; 3927 (0:3927) + push hl + push de + push bc + callba _AddPokemonToParty + pop bc + pop de + pop hl + ret + +; calculates all 5 stats of current mon and writes them to [de] +CalcStats:: ; 3936 (0:3936) + ld c, $0 +.statsLoop + inc c + call CalcStat + ld a, [H_MULTIPLICAND+1] + ld [de], a + inc de + ld a, [H_MULTIPLICAND+2] + ld [de], a + inc de + ld a, c + cp $5 + jr nz, .statsLoop + ret + +; calculates stat c of current mon +; c: stat to calc (HP=1,Atk=2,Def=3,Spd=4,Spc=5) +; b: consider stat exp? +; hl: base ptr to stat exp values ([hl + 2*c - 1] and [hl + 2*c]) +CalcStat:: ; 394a (0:394a) + push hl + push de + push bc + ld a, b + ld d, a + push hl + ld hl, W_MONHEADER + ld b, $0 + add hl, bc + ld a, [hl] ; read base value of stat + ld e, a + pop hl + push hl + sla c + ld a, d + and a + jr z, .statExpDone ; consider stat exp? + add hl, bc ; skip to corresponding stat exp value +.statExpLoop ; calculates ceil(Sqrt(stat exp)) in b + xor a + ld [H_MULTIPLICAND], a + ld [H_MULTIPLICAND+1], a + inc b ; increment current stat exp bonus + ld a, b + cp $ff + jr z, .statExpDone + ld [H_MULTIPLICAND+2], a + ld [H_MULTIPLIER], a + call Multiply + ld a, [hld] + ld d, a + ld a, [$ff98] + sub d + ld a, [hli] + ld d, a + ld a, [$ff97] + sbc d ; test if (current stat exp bonus)^2 < stat exp + jr c, .statExpLoop +.statExpDone + srl c + pop hl + push bc + ld bc, $b ; skip to stat IV values + add hl, bc + pop bc + ld a, c + cp $2 + jr z, .getAttackIV + cp $3 + jr z, .getDefenseIV + cp $4 + jr z, .getSpeedIV + cp $5 + jr z, .getSpecialIV +.getHpIV + push bc + ld a, [hl] ; Atk IV + swap a + and $1 + sla a + sla a + sla a + ld b, a + ld a, [hli] ; Def IV + and $1 + sla a + sla a + add b + ld b, a + ld a, [hl] ; Spd IV + swap a + and $1 + sla a + add b + ld b, a + ld a, [hl] ; Spc IV + and $1 + add b ; HP IV: LSB of the other 4 IVs + pop bc + jr .calcStatFromIV +.getAttackIV + ld a, [hl] + swap a + and $f + jr .calcStatFromIV +.getDefenseIV + ld a, [hl] + and $f + jr .calcStatFromIV +.getSpeedIV + inc hl + ld a, [hl] + swap a + and $f + jr .calcStatFromIV +.getSpecialIV + inc hl + ld a, [hl] + and $f +.calcStatFromIV + ld d, $0 + add e + ld e, a + jr nc, .noCarry + inc d ; de = Base + IV +.noCarry + sla e + rl d ; de = (Base + IV) * 2 + srl b + srl b ; b = ceil(Sqrt(stat exp)) / 4 + ld a, b + add e + jr nc, .noCarry2 + inc d ; da = (Base + IV) * 2 + ceil(Sqrt(stat exp)) / 4 +.noCarry2 + ld [H_MULTIPLICAND+2], a + ld a, d + ld [H_MULTIPLICAND+1], a + xor a + ld [H_MULTIPLICAND], a + ld a, [W_CURENEMYLVL] ; $d127 + ld [H_MULTIPLIER], a + call Multiply ; ((Base + IV) * 2 + ceil(Sqrt(stat exp)) / 4) * Level + ld a, [H_MULTIPLICAND] + ld [H_DIVIDEND], a + ld a, [H_MULTIPLICAND+1] + ld [H_DIVIDEND+1], a + ld a, [H_MULTIPLICAND+2] + ld [H_DIVIDEND+2], a + ld a, $64 + ld [H_DIVISOR], a + ld a, $3 + ld b, a + call Divide ; (((Base + IV) * 2 + ceil(Sqrt(stat exp)) / 4) * Level) / 100 + ld a, c + cp $1 + ld a, $5 + jr nz, .notHPStat + ld a, [W_CURENEMYLVL] ; $d127 + ld b, a + ld a, [H_MULTIPLICAND+2] + add b + ld [H_MULTIPLICAND+2], a + jr nc, .noCarry3 + ld a, [H_MULTIPLICAND+1] + inc a + ld [H_MULTIPLICAND+1], a ; HP: (((Base + IV) * 2 + ceil(Sqrt(stat exp)) / 4) * Level) / 100 + Level +.noCarry3 + ld a, $a +.notHPStat + ld b, a + ld a, [H_MULTIPLICAND+2] + add b + ld [H_MULTIPLICAND+2], a + jr nc, .noCarry4 + ld a, [H_MULTIPLICAND+1] + inc a ; non-HP: (((Base + IV) * 2 + ceil(Sqrt(stat exp)) / 4) * Level) / 100 + 5 + ld [H_MULTIPLICAND+1], a ; HP: (((Base + IV) * 2 + ceil(Sqrt(stat exp)) / 4) * Level) / 100 + Level + 10 +.noCarry4 + ld a, [H_MULTIPLICAND+1] ; check for overflow (>999) + cp $4 + jr nc, .overflow + cp $3 + jr c, .noOverflow + ld a, [H_MULTIPLICAND+2] + cp $e8 + jr c, .noOverflow +.overflow + ld a, $3 ; overflow: cap at 999 + ld [H_MULTIPLICAND+1], a + ld a, $e7 + ld [H_MULTIPLICAND+2], a +.noOverflow + pop bc + pop de + pop hl + ret + +AddEnemyMonToPlayerParty:: ; 3a53 (0:3a53) + ld a, [H_LOADEDROMBANK] + push af + ld a, BANK(_AddEnemyMonToPlayerParty) + ld [H_LOADEDROMBANK], a + ld [$2000], a + call _AddEnemyMonToPlayerParty + pop bc + ld a, b + ld [H_LOADEDROMBANK], a + ld [$2000], a + ret + +Func_3a68:: ; 3a68 (0:3a68) + ld a, [H_LOADEDROMBANK] + push af + ld a, BANK(Func_f51e) + ld [H_LOADEDROMBANK], a + ld [$2000], a + call Func_f51e + pop bc + ld a, b + ld [H_LOADEDROMBANK], a + ld [$2000], a + ret + +; skips a text entries, each of size $b (like trainer name, OT name, rival name, ...) +; hl: base pointer, will be incremented by $b * a +SkipFixedLengthTextEntries:: ; 3a7d (0:3a7d) + and a + ret z + ld bc, $b +.skipLoop + add hl, bc + dec a + jr nz, .skipLoop + ret + +AddNTimes:: ; 3a87 (0:3a87) +; add bc to hl a times + and a + ret z +.loop + add hl,bc + dec a + jr nz,.loop + ret + +; Compare strings, c bytes in length, at de and hl. +; Often used to compare big endian numbers in battle calculations. +StringCmp:: ; 3a8e (0:3a8e) + ld a,[de] + cp [hl] + ret nz + inc de + inc hl + dec c + jr nz,StringCmp + ret + +; INPUT: +; a = oam block index (each block is 4 oam entries) +; b = Y coordinate of upper left corner of sprite +; c = X coordinate of upper left corner of sprite +; de = base address of 4 tile number and attribute pairs +WriteOAMBlock:: ; 3a97 (0:3a97) + ld h,$c3 + swap a ; multiply by 16 + ld l,a + call .writeOneEntry ; upper left + push bc + ld a,8 + add c + ld c,a + call .writeOneEntry ; upper right + pop bc + ld a,8 + add b + ld b,a + call .writeOneEntry ; lower left + ld a,8 + add c + ld c,a + ; lower right +.writeOneEntry + ld [hl],b ; Y coordinate + inc hl + ld [hl],c ; X coordinate + inc hl + ld a,[de] ; tile number + inc de + ld [hli],a + ld a,[de] ; attribute + inc de + ld [hli],a + ret + +HandleMenuInput:: ; 3abe (0:3abe) + xor a + ld [$d09b],a + +HandleMenuInputPokemonSelection:: ; 3ac2 (0:3ac2) + ld a,[H_DOWNARROWBLINKCNT1] + push af + ld a,[H_DOWNARROWBLINKCNT2] + push af ; save existing values on stack + xor a + ld [H_DOWNARROWBLINKCNT1],a ; blinking down arrow timing value 1 + ld a,$06 + ld [H_DOWNARROWBLINKCNT2],a ; blinking down arrow timing value 2 +.loop1 + xor a + ld [$d08b],a ; counter for pokemon shaking animation + call PlaceMenuCursor + call Delay3 +.loop2 + push hl + ld a,[$d09b] + and a ; is it a pokemon selection menu? + jr z,.getJoypadState + callba AnimatePartyMon ; shake mini sprite of selected pokemon +.getJoypadState + pop hl + call GetJoypadStateLowSensitivity + ld a,[$ffb5] + and a ; was a key pressed? + jr nz,.keyPressed + push hl + FuncCoord 18,11 ; coordinates of blinking down arrow in some menus + ld hl,Coord + call HandleDownArrowBlinkTiming ; blink down arrow (if any) + pop hl + ld a,[wMenuJoypadPollCount] + dec a + jr z,.giveUpWaiting + jr .loop2 +.giveUpWaiting +; if a key wasn't pressed within the specified number of checks + pop af + ld [H_DOWNARROWBLINKCNT2],a + pop af + ld [H_DOWNARROWBLINKCNT1],a ; restore previous values + xor a + ld [wMenuWrappingEnabled],a ; disable menu wrapping + ret +.keyPressed + xor a + ld [$cc4b],a + ld a,[$ffb5] + ld b,a + bit 6,a ; pressed Up key? + jr z,.checkIfDownPressed +.upPressed + ld a,[wCurrentMenuItem] ; selected menu item + and a ; already at the top of the menu? + jr z,.alreadyAtTop +.notAtTop + dec a + ld [wCurrentMenuItem],a ; move selected menu item up one space + jr .checkOtherKeys +.alreadyAtTop + ld a,[wMenuWrappingEnabled] + and a ; is wrapping around enabled? + jr z,.noWrappingAround + ld a,[wMaxMenuItem] + ld [wCurrentMenuItem],a ; wrap to the bottom of the menu + jr .checkOtherKeys +.checkIfDownPressed + bit 7,a + jr z,.checkOtherKeys +.downPressed + ld a,[wCurrentMenuItem] + inc a + ld c,a + ld a,[wMaxMenuItem] + cp c + jr nc,.notAtBottom +.alreadyAtBottom + ld a,[wMenuWrappingEnabled] + and a ; is wrapping around enabled? + jr z,.noWrappingAround + ld c,$00 ; wrap from bottom to top +.notAtBottom + ld a,c + ld [wCurrentMenuItem],a +.checkOtherKeys + ld a,[wMenuWatchedKeys] + and b ; does the menu care about any of the pressed keys? + jp z,.loop1 +.checkIfAButtonOrBButtonPressed + ld a,[$ffb5] + and a,%00000011 ; pressed A button or B button? + jr z,.skipPlayingSound +.AButtonOrBButtonPressed + push hl + ld hl,wFlags_0xcd60 + bit 5,[hl] + pop hl + jr nz,.skipPlayingSound + ld a,(SFX_02_40 - SFX_Headers_02) / 3 + call PlaySound ; play sound +.skipPlayingSound + pop af + ld [H_DOWNARROWBLINKCNT2],a + pop af + ld [H_DOWNARROWBLINKCNT1],a ; restore previous values + xor a + ld [wMenuWrappingEnabled],a ; disable menu wrapping + ld a,[$ffb5] + ret +.noWrappingAround + ld a,[$cc37] + and a ; should we return if the user tried to go past the top or bottom? + jr z,.checkOtherKeys + jr .checkIfAButtonOrBButtonPressed + +PlaceMenuCursor:: ; 3b7c (0:3b7c) + ld a,[wTopMenuItemY] + and a ; is the y coordinate 0? + jr z,.adjustForXCoord + ld hl,wTileMap + ld bc,20 ; screen width +.topMenuItemLoop + add hl,bc + dec a + jr nz,.topMenuItemLoop +.adjustForXCoord + ld a,[wTopMenuItemX] + ld b,$00 + ld c,a + add hl,bc + push hl + ld a,[wLastMenuItem] + and a ; was the previous menu id 0? + jr z,.checkForArrow1 + push af + ld a,[$fff6] + bit 1,a ; is the menu double spaced? + jr z,.doubleSpaced1 + ld bc,20 + jr .getOldMenuItemScreenPosition +.doubleSpaced1 + ld bc,40 +.getOldMenuItemScreenPosition + pop af +.oldMenuItemLoop + add hl,bc + dec a + jr nz,.oldMenuItemLoop +.checkForArrow1 + ld a,[hl] + cp a,"▶" ; was an arrow next to the previously selected menu item? + jr nz,.skipClearingArrow +.clearArrow + ld a,[wTileBehindCursor] + ld [hl],a +.skipClearingArrow + pop hl + ld a,[wCurrentMenuItem] + and a + jr z,.checkForArrow2 + push af + ld a,[$fff6] + bit 1,a ; is the menu double spaced? + jr z,.doubleSpaced2 + ld bc,20 + jr .getCurrentMenuItemScreenPosition +.doubleSpaced2 + ld bc,40 +.getCurrentMenuItemScreenPosition + pop af +.currentMenuItemLoop + add hl,bc + dec a + jr nz,.currentMenuItemLoop +.checkForArrow2 + ld a,[hl] + cp a,"▶" ; has the right arrow already been placed? + jr z,.skipSavingTile ; if so, don't lose the saved tile + ld [wTileBehindCursor],a ; save tile before overwriting with right arrow +.skipSavingTile + ld a,"▶" ; place right arrow + ld [hl],a + ld a,l + ld [wMenuCursorLocation],a + ld a,h + ld [wMenuCursorLocation + 1],a + ld a,[wCurrentMenuItem] + ld [wLastMenuItem],a + ret + +; This is used to mark a menu cursor other than the one currently being +; manipulated. In the case of submenus, this is used to show the location of +; the menu cursor in the parent menu. In the case of swapping items in list, +; this is used to mark the item that was first chosen to be swapped. +PlaceUnfilledArrowMenuCursor:: ; 3bec (0:3bec) + ld b,a + ld a,[wMenuCursorLocation] + ld l,a + ld a,[wMenuCursorLocation + 1] + ld h,a + ld [hl],$ec ; outline of right arrow + ld a,b + ret + +; Replaces the menu cursor with a blank space. +EraseMenuCursor:: ; 3bf9 (0:3bf9) + ld a,[wMenuCursorLocation] + ld l,a + ld a,[wMenuCursorLocation + 1] + ld h,a + ld [hl]," " + ret + +; This toggles a blinking down arrow at hl on and off after a delay has passed. +; This is often called even when no blinking is occurring. +; The reason is that most functions that call this initialize H_DOWNARROWBLINKCNT1 to 0. +; The effect is that if the tile at hl is initialized with a down arrow, +; this function will toggle that down arrow on and off, but if the tile isn't +; initliazed with a down arrow, this function does nothing. +; That allows this to be called without worrying about if a down arrow should +; be blinking. +HandleDownArrowBlinkTiming:: ; 3c04 (0:3c04) + ld a,[hl] + ld b,a + ld a,$ee ; down arrow + cp b + jr nz,.downArrowOff +.downArrowOn + ld a,[H_DOWNARROWBLINKCNT1] + dec a + ld [H_DOWNARROWBLINKCNT1],a + ret nz + ld a,[H_DOWNARROWBLINKCNT2] + dec a + ld [H_DOWNARROWBLINKCNT2],a + ret nz + ld a," " + ld [hl],a + ld a,$ff + ld [H_DOWNARROWBLINKCNT1],a + ld a,$06 + ld [H_DOWNARROWBLINKCNT2],a + ret +.downArrowOff + ld a,[H_DOWNARROWBLINKCNT1] + and a + ret z + dec a + ld [H_DOWNARROWBLINKCNT1],a + ret nz + dec a + ld [H_DOWNARROWBLINKCNT1],a + ld a,[H_DOWNARROWBLINKCNT2] + dec a + ld [H_DOWNARROWBLINKCNT2],a + ret nz + ld a,$06 + ld [H_DOWNARROWBLINKCNT2],a + ld a,$ee ; down arrow + ld [hl],a + ret + +; The following code either enables or disables the automatic drawing of +; text boxes by DisplayTextID. Both functions cause DisplayTextID to wait +; for a button press after displaying text (unless [$cc47] is set). + +EnableAutoTextBoxDrawing:: ; 3c3c (0:3c3c) + xor a + jr AutoTextBoxDrawingCommon + +DisableAutoTextBoxDrawing:: ; 3c3f (0:3c3f) + ld a,$01 + +AutoTextBoxDrawingCommon:: ; 3c41 (0:3c41) + ld [$cf0c],a ; control text box drawing + xor a + ld [$cc3c],a ; make DisplayTextID wait for button press + ret + +PrintText:: ; 3c49 (0:3c49) +; given a pointer in hl, print the text there + push hl + ld a,1 + ld [$D125],a + call DisplayTextBoxID + call UpdateSprites + call Delay3 + pop hl +Func_3c59:: ; 3c59 (0:3c59) + FuncCoord 1,14 + ld bc,Coord ;$C4B9 + jp TextCommandProcessor + +; converts a big-endian binary number into decimal and prints it +; INPUT: +; b = flags and number of bytes +; bit 7: if set, print leading zeroes +; if unset, do not print leading zeroes +; bit 6: if set, left-align the string (do not pad empty digits with spaces) +; if unset, right-align the string +; bits 4-5: unused +; bits 0-3: number of bytes (only 1 - 3 bytes supported) +; c = number of decimal digits +; de = address of the number (big-endian) +PrintNumber:: ; 3c5f (0:3c5f) + push bc + xor a + ld [H_PASTLEADINGZEROES],a + ld [H_NUMTOPRINT],a + ld [H_NUMTOPRINT + 1],a + ld a,b + and a,%00001111 + cp a,1 + jr z,.oneByte + cp a,2 + jr z,.twoBytes +.threeBytes + ld a,[de] + ld [H_NUMTOPRINT],a + inc de + ld a,[de] + ld [H_NUMTOPRINT + 1],a + inc de + ld a,[de] + ld [H_NUMTOPRINT + 2],a + jr .checkNumDigits +.twoBytes + ld a,[de] + ld [H_NUMTOPRINT + 1],a + inc de + ld a,[de] + ld [H_NUMTOPRINT + 2],a + jr .checkNumDigits +.oneByte + ld a,[de] + ld [H_NUMTOPRINT + 2],a +.checkNumDigits + push de + ld d,b + ld a,c + ld b,a + xor a + ld c,a + ld a,b ; a = number of decimal digits + cp a,2 + jr z,.tensPlace + cp a,3 + jr z,.hundredsPlace + cp a,4 + jr z,.thousandsPlace + cp a,5 + jr z,.tenThousandsPlace + cp a,6 + jr z,.hundredThousandsPlace +.millionsPlace + ld a,1000000 >> 16 + ld [H_POWEROFTEN],a + ld a,(1000000 >> 8) & $FF + ld [H_POWEROFTEN + 1],a + ld a,1000000 & $FF + ld [H_POWEROFTEN + 2],a + call PrintNumber_PrintDigit + call PrintNumber_AdvancePointer +.hundredThousandsPlace + ld a,100000 >> 16 + ld [H_POWEROFTEN],a + ld a,(100000 >> 8) & $FF + ld [H_POWEROFTEN + 1],a + ld a,100000 & $FF + ld [H_POWEROFTEN + 2],a + call PrintNumber_PrintDigit + call PrintNumber_AdvancePointer +.tenThousandsPlace + xor a + ld [H_POWEROFTEN],a + ld a,10000 >> 8 + ld [H_POWEROFTEN + 1],a + ld a,10000 & $FF + ld [H_POWEROFTEN + 2],a + call PrintNumber_PrintDigit + call PrintNumber_AdvancePointer +.thousandsPlace + xor a + ld [H_POWEROFTEN],a + ld a,1000 >> 8 + ld [H_POWEROFTEN + 1],a + ld a,1000 & $FF + ld [H_POWEROFTEN + 2],a + call PrintNumber_PrintDigit + call PrintNumber_AdvancePointer +.hundredsPlace + xor a + ld [H_POWEROFTEN],a + xor a + ld [H_POWEROFTEN + 1],a + ld a,100 + ld [H_POWEROFTEN + 2],a + call PrintNumber_PrintDigit + call PrintNumber_AdvancePointer +.tensPlace + ld c,00 + ld a,[H_NUMTOPRINT + 2] +.loop + cp a,10 + jr c,.underflow + sub a,10 + inc c + jr .loop +.underflow + ld b,a + ld a,[H_PASTLEADINGZEROES] + or c + ld [H_PASTLEADINGZEROES],a + jr nz,.pastLeadingZeroes + call PrintNumber_PrintLeadingZero + jr .advancePointer +.pastLeadingZeroes + ld a,"0" + add c + ld [hl],a +.advancePointer + call PrintNumber_AdvancePointer +.onesPlace + ld a,"0" + add b + ld [hli],a + pop de + dec de + pop bc + ret + +; prints a decimal digit +; This works by repeatedely subtracting a power of ten until the number becomes negative. +; The number of subtractions it took in order to make the number negative is the digit for the current number place. +; The last value that the number had before becoming negative is kept as the new value of the number. +; A more succinct description is that the number is divided by a power of ten +; and the quotient becomes the digit while the remainder is stored as the new value of the number. +PrintNumber_PrintDigit:: ; 3d25 (0:3d25) + ld c,0 ; counts number of loop iterations to determine the decimal digit +.loop + ld a,[H_POWEROFTEN] + ld b,a + ld a,[H_NUMTOPRINT] + ld [H_SAVEDNUMTOPRINT],a + cp b + jr c,.underflow0 + sub b + ld [H_NUMTOPRINT],a + ld a,[H_POWEROFTEN + 1] + ld b,a + ld a,[H_NUMTOPRINT + 1] + ld [H_SAVEDNUMTOPRINT + 1],a + cp b + jr nc,.noBorrowForByte1 +.byte1BorrowFromByte0 + ld a,[H_NUMTOPRINT] + or a,0 + jr z,.underflow1 + dec a + ld [H_NUMTOPRINT],a + ld a,[H_NUMTOPRINT + 1] +.noBorrowForByte1 + sub b + ld [H_NUMTOPRINT + 1],a + ld a,[H_POWEROFTEN + 2] + ld b,a + ld a,[H_NUMTOPRINT + 2] + ld [H_SAVEDNUMTOPRINT + 2],a + cp b + jr nc,.noBorrowForByte2 +.byte2BorrowFromByte1 + ld a,[H_NUMTOPRINT + 1] + and a + jr nz,.finishByte2BorrowFromByte1 +.byte2BorrowFromByte0 + ld a,[H_NUMTOPRINT] + and a + jr z,.underflow2 + dec a + ld [H_NUMTOPRINT],a + xor a +.finishByte2BorrowFromByte1 + dec a + ld [H_NUMTOPRINT + 1],a + ld a,[H_NUMTOPRINT + 2] +.noBorrowForByte2 + sub b + ld [H_NUMTOPRINT + 2],a + inc c + jr .loop +.underflow2 + ld a,[H_SAVEDNUMTOPRINT + 1] + ld [H_NUMTOPRINT + 1],a +.underflow1 + ld a,[H_SAVEDNUMTOPRINT] + ld [H_NUMTOPRINT],a +.underflow0 + ld a,[H_PASTLEADINGZEROES] + or c + jr z,PrintNumber_PrintLeadingZero + ld a,"0" + add c + ld [hl],a + ld [H_PASTLEADINGZEROES],a + ret + +; prints a leading zero unless they are turned off in the flags +PrintNumber_PrintLeadingZero:: ; 3d83 (0:3d83) + bit 7,d ; print leading zeroes? + ret z + ld [hl],"0" + ret + +; increments the pointer unless leading zeroes are not being printed, +; the number is left-aligned, and no nonzero digits have been printed yet +PrintNumber_AdvancePointer:: ; 3d89 (0:3d89) + bit 7,d ; print leading zeroes? + jr nz,.incrementPointer + bit 6,d ; left alignment or right alignment? + jr z,.incrementPointer + ld a,[H_PASTLEADINGZEROES] + and a + ret z +.incrementPointer + inc hl + ret + +; calls a function from a table of function pointers +; INPUT: +; a = index within table +; hl = address of function pointer table +CallFunctionInTable:: ; 3d97 (0:3d97) + push hl + push de + push bc + add a + ld d,0 + ld e,a + add hl,de + ld a,[hli] + ld h,[hl] + ld l,a + ld de,.returnAddress + push de + jp [hl] +.returnAddress + pop bc + pop de + pop hl + ret + + +IsInArray:: +; Search an array at hl for the value in a. +; Entry size is de bytes. +; Return count b and carry if found. + ld b, 0 + +IsInRestOfArray:: + ld c, a +.loop + ld a, [hl] + cp -1 + jr z, .notfound + cp c + jr z, .found + inc b + add hl, de + jr .loop + +.notfound + and a + ret + +.found + scf + ret + + +Func_3dbe:: ; 3dbe (0:3dbe) + call ClearSprites + ld a, $1 + ld [$cfcb], a + call Func_3e08 + call LoadScreenTilesFromBuffer2 + call LoadTextBoxTilePatterns + call GoPAL_SET_CF1C + jr Delay3 + + +GBPalWhiteOutWithDelay3:: + call GBPalWhiteOut + +Delay3:: +; The bg map is updated each frame in thirds. +; Wait three frames to let the bg map fully update. + ld c, 3 + jp DelayFrames + +GBPalNormal:: +; Reset BGP and OBP0. + ld a, %11100100 ; 3210 + ld [rBGP], a + ld a, %11010000 ; 3100 + ld [rOBP0], a + ret + +GBPalWhiteOut:: +; White out all palettes. + xor a + ld [rBGP],a + ld [rOBP0],a + ld [rOBP1],a + ret + + +GoPAL_SET_CF1C:: ; 3ded (0:3ded) + ld b,$ff +GoPAL_SET:: ; 3def (0:3def) + ld a,[$cf1b] + and a + ret z + ld a,$45 + jp Predef + +GetHealthBarColor:: +; Return at hl the palette of +; an HP bar e pixels long. + ld a, e + cp 27 + ld d, 0 ; green + jr nc, .gotColor + cp 10 + inc d ; yellow + jr nc, .gotColor + inc d ; red +.gotColor + ld [hl], d + ret + +Func_3e08:: ; 3e08 (0:3e08) + ld hl, $cfc4 + ld a, [hl] + push af + res 0, [hl] + push hl + xor a + ld [W_SPRITESETID], a ; $d3a8 + call DisableLCD + callba InitMapSprites + call EnableLCD + pop hl + pop af + ld [hl], a + call LoadPlayerSpriteGraphics + call LoadFontTilePatterns + jp UpdateSprites + + +GiveItem:: +; Give player quantity c of item b, +; and copy the item's name to $cf4b. +; Return carry on success. + ld a, b + ld [$d11e], a + ld [$cf91], a + ld a, c + ld [$cf96], a + ld hl,wNumBagItems + call AddItemToInventory + ret nc + call GetItemName + call CopyStringToCF4B + scf + ret + +GivePokemon:: +; Give the player monster b at level c. + ld a, b + ld [$cf91], a + ld a, c + ld [$d127], a + xor a + ld [$cc49], a + ld b, BANK(_GivePokemon) + ld hl, _GivePokemon + jp Bankswitch + + +Random:: +; Return a random number in a. +; For battles, use BattleRandom. + push hl + push de + push bc + callba Random_ + ld a,[hRandomAdd] + pop bc + pop de + pop hl + ret + + +Predef:: +; Call predefined function a. +; To preserve other registers, have the +; destination call GetPredefRegisters. + + ; Save the predef id for GetPredefPointer. + ld [wPredefID], a + + ; A hack for LoadDestinationWarpPosition. + ; See Func_c754 (predef $19). + ld a, [H_LOADEDROMBANK] + ld [wPredefParentBank], a + + push af + ld a, BANK(GetPredefPointer) + ld [H_LOADEDROMBANK], a + ld [$2000], a + + call GetPredefPointer + + ld a, [wPredefBank] + ld [H_LOADEDROMBANK], a + ld [$2000], a + + ld de, .done + push de + jp [hl] +.done + + pop af + ld [H_LOADEDROMBANK], a + ld [$2000], a + ret + +GetPredefRegisters:: +; Restore the contents of register pairs +; when GetPredefPointer was called. + ld a, [wPredefRegisters + 0] + ld h, a + ld a, [wPredefRegisters + 1] + ld l, a + ld a, [wPredefRegisters + 2] + ld d, a + ld a, [wPredefRegisters + 3] + ld e, a + ld a, [wPredefRegisters + 4] + ld b, a + ld a, [wPredefRegisters + 5] + ld c, a + ret + + +Func_3ead:: ; 3ead (0:3ead) + ld b, BANK(CinnabarGymQuiz_1eb0a) + ld hl, CinnabarGymQuiz_1eb0a + jp Bankswitch + +Func_3eb5:: ; 3eb5 (0:3eb5) + ld a, [H_LOADEDROMBANK] + push af + ld a, [H_CURRENTPRESSEDBUTTONS] + bit 0, a + jr z, .asm_3eea + ld a, Bank(Func_469a0) + ld [$2000], a + ld [H_LOADEDROMBANK], a + call Func_469a0 + ld a, [$ffee] + and a + jr nz, .asm_3edd + ld a, [$cd3e] + ld [$2000], a + ld [H_LOADEDROMBANK], a + ld de, .asm_3eda + push de + jp [hl] +.asm_3eda + xor a + jr .asm_3eec +.asm_3edd + callba PrintBookshelfText + ld a, [$ffdb] + and a + jr z, .asm_3eec +.asm_3eea + ld a, $ff +.asm_3eec + ld [$ffeb], a + pop af + ld [$2000], a + ld [H_LOADEDROMBANK], a + ret + +PrintPredefTextID:: ; 3ef5 (0:3ef5) + ld [H_DOWNARROWBLINKCNT2], a ; $ff8c + ld hl, PointerTable_3f22 + call Func_3f0f + ld hl, $cf11 + set 0, [hl] + call DisplayTextID + +Func_3f05:: ; 3f05 (0:3f05) + ld hl, W_MAPTEXTPTR ; $d36c + ld a, [$ffec] + ld [hli], a + ld a, [$ffed] + ld [hl], a + ret + +Func_3f0f:: ; 3f0f (0:3f0f) + ld a, [W_MAPTEXTPTR] ; $d36c + ld [$ffec], a + ld a, [$d36d] + ld [$ffed], a + ld a, l + ld [W_MAPTEXTPTR], a ; $d36c + ld a, h + ld [$d36d], a + ret + +PointerTable_3f22:: ; 3f22 (0:3f22) + dw CardKeySuccessText ; id = 01 + dw CardKeyFailText ; id = 02 + dw RedBedroomPC ; id = 03 + dw RedBedroomSNESText ; id = 04 + dw PushStartText ; id = 05 + dw SaveOptionText ; id = 06 + dw StrengthsAndWeaknessesText ; id = 07 + dw OakLabEmailText ; id = 08 + dw AerodactylFossilText ; id = 09 + dw Route15UpstairsBinocularsText ; id = 0A + dw KabutopsFossilText ; id = 0B + dw GymStatueText1 ; id = 0C + dw GymStatueText2 ; id = 0D + dw BookcaseText ; id = 0E + dw ViridianCityPokecenterBenchGuyText ; id = 0F + dw PewterCityPokecenterBenchGuyText ; id = 10 + dw CeruleanCityPokecenterBenchGuyText ; id = 11 + dw LavenderCityPokecenterBenchGuyText ; id = 12 + dw VermilionCityPokecenterBenchGuyText ; id = 13 + dw CeladonCityPokecenterBenchGuyText ; id = 14 + dw CeladonCityHotelText ; id = 15 + dw FuchsiaCityPokecenterBenchGuyText ; id = 16 + dw CinnabarIslandPokecenterBenchGuyText ; id = 17 + dw SaffronCityPokecenterBenchGuyText ; id = 18 + dw MtMoonPokecenterBenchGuyText ; id = 19 + dw RockTunnelPokecenterBenchGuyText ; id = 1A + dw UnusedBenchGuyText1 ; id = 1B + dw UnusedBenchGuyText2 ; id = 1C + dw UnusedBenchGuyText3 ; id = 1D + dw TerminatorText_62508 ; id = 1E + dw PredefText1f ; id = 1F + dw ViridianSchoolNotebook ; id = 20 + dw ViridianSchoolBlackboard ; id = 21 + dw JustAMomentText ; id = 22 + dw PredefText23 ; id = 23 + dw FoundHiddenItemText ; id = 24 + dw HiddenItemBagFullText ; id = 25 + dw VermilionGymTrashText ; id = 26 + dw IndigoPlateauHQText ; id = 27 + dw GameCornerOutOfOrderText ; id = 28 + dw GameCornerOutToLunchText ; id = 29 + dw GameCornerSomeonesKeysText ; id = 2A + dw FoundHiddenCoinsText ; id = 2B + dw DroppedHiddenCoinsText ; id = 2C + dw BillsHouseMonitorText ; id = 2D + dw BillsHouseInitiatedText ; id = 2E + dw BillsHousePokemonList ; id = 2F + dw MagazinesText ; id = 30 + dw CinnabarGymQuiz ; id = 31 + dw GameCornerNoCoinsText ; id = 32 + dw GameCornerCoinCaseText ; id = 33 + dw LinkCableHelp ; id = 34 + dw TMNotebook ; id = 35 + dw FightingDojoText ; id = 36 + dw FightingDojoText_52a10 ; id = 37 + dw FightingDojoText_52a1d ; id = 38 + dw NewBicycleText ; id = 39 + dw IndigoPlateauStatues ; id = 3A + dw VermilionGymTrashSuccesText1 ; id = 3B + dw VermilionGymTrashSuccesText2 ; id = 3C + dw VermilionGymTrashSuccesText3 ; id = 3D + dw VermilionGymTrashFailText ; id = 3E + dw TownMapText ; id = 3F + dw BookOrSculptureText ; id = 40 + dw ElevatorText ; id = 41 + dw PokemonStuffText ; id = 42 @@ -1,10177 +1,7 @@ INCLUDE "constants.asm" -; The rst vectors are unused. -SECTION "rst00", ROM0[$00] - rst $38 -SECTION "rst08", ROM0[$08] - rst $38 -SECTION "rst10", ROM0[$10] - rst $38 -SECTION "rst18", ROM0[$18] - rst $38 -SECTION "rst20", ROM0[$20] - rst $38 -SECTION "rst28", ROM0[$28] - rst $38 -SECTION "rst30", ROM0[$30] - rst $38 -SECTION "rst38", ROM0[$38] - rst $38 - -; interrupts -SECTION "vblank", ROM0[$40] - jp VBlank -SECTION "lcdc", ROM0[$48] - rst $38 -SECTION "timer", ROM0[$50] - jp Timer -SECTION "serial", ROM0[$58] - jp Serial -SECTION "joypad", ROM0[$60] - reti - - -SECTION "bank0",ROM0[$61] - -DisableLCD:: - xor a - ld [rIF], a - ld a, [rIE] - ld b, a - res 0, a - ld [rIE], a - -.wait - ld a, [rLY] - cp LY_VBLANK - jr nz, .wait - - ld a, [rLCDC] - and $ff ^ rLCDC_ENABLE_MASK - ld [rLCDC], a - ld a, b - ld [rIE], a - ret - -EnableLCD:: - ld a, [rLCDC] - set rLCDC_ENABLE, a - ld [rLCDC], a - ret - -ClearSprites:: - xor a - ld hl, wOAMBuffer - ld b, 40 * 4 -.loop - ld [hli], a - dec b - jr nz, .loop - ret - -HideSprites:: - ld a, 160 - ld hl, wOAMBuffer - ld de, 4 - ld b, 40 -.loop - ld [hl], a - add hl, de - dec b - jr nz, .loop - ret - -FarCopyData:: -; Copy bc bytes from a:hl to de. - ld [wBuffer], a - ld a, [H_LOADEDROMBANK] - push af - ld a, [wBuffer] - ld [H_LOADEDROMBANK], a - ld [MBC3RomBank], a - call CopyData - pop af - ld [H_LOADEDROMBANK], a - ld [MBC3RomBank], a - ret - -CopyData:: -; Copy bc bytes from hl to de. - ld a, [hli] - ld [de], a - inc de - dec bc - ld a, c - or b - jr nz, CopyData - ret - - -SECTION "Entry", ROM0[$100] - nop - jp Start - - -SECTION "Start", ROM0[$150] - -Start:: - cp GBC - jr z, .gbc - xor a - jr .ok -.gbc - ld a, 0 -.ok - ld [wGBC], a - jp Init - - -ReadJoypad:: -; Poll joypad input. -; Unlike the hardware register, button -; presses are indicated by a set bit. - - ld a, 1 << 5 ; select direction keys - ld c, 0 - - ld [rJOYP], a - rept 6 - ld a, [rJOYP] - endr - cpl - and %1111 - swap a - ld b, a - - ld a, 1 << 4 ; select button keys - ld [rJOYP], a - rept 10 - ld a, [rJOYP] - endr - cpl - and %1111 - or b - - ld [H_JOYPADSTATE], a - - ld a, 1 << 4 + 1 << 5 ; deselect keys - ld [rJOYP], a - ret - -GetJoypadState:: -; Update the joypad state variables: -; [H_NEWLYRELEASEDBUTTONS] keys released since last time -; [H_NEWLYPRESSEDBUTTONS] keys pressed since last time -; [H_CURRENTPRESSEDBUTTONS] currently pressed keys - homecall _GetJoypadState - ret - - -INCLUDE "data/map_header_pointers.asm" - -HandleMidJump:: -; Handle the player jumping down -; a ledge in the overworld. - ld b, BANK(_HandleMidJump) - ld hl, _HandleMidJump - jp Bankswitch - -EnterMap:: -; Load a new map. - ld a, $ff - ld [wJoypadForbiddenButtonsMask], a - call LoadMapData - callba Func_c335 ; initialize map variables - ld hl, $d72c - bit 0, [hl] - jr z, .doNotCountSteps - ld a, 3 - ld [$d13c], a ; some kind of step counter (counts up to 3 steps?) -.doNotCountSteps - ld hl, $d72e - bit 5, [hl] ; did a battle happen immediately before this? - res 5, [hl] ; unset the "battle just happened" flag - call z, Func_12e7 - call nz, MapEntryAfterBattle - ld hl, $d732 - ld a, [hl] - and 1 << 4 | 1 << 3 - jr z, .didNotFlyOrTeleportIn - res 3, [hl] - callba Func_70510 ; display fly/teleport in graphical effect - call UpdateSprites -.didNotFlyOrTeleportIn - callba CheckForceBikeOrSurf ; handle currents in SF islands and forced bike riding in cycling road - ld hl, $d72d - res 5, [hl] - call UpdateSprites - ld hl, $d126 - set 5, [hl] - set 6, [hl] - xor a - ld [wJoypadForbiddenButtonsMask], a - -OverworldLoop:: - call DelayFrame -OverworldLoopLessDelay:: - call DelayFrame - call LoadGBPal - ld a,[$d736] - 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 GetJoypadStateOverworld ; get joypad state (which is possibly simulated) - callba SafariZoneCheck - ld a,[$da46] - and a - jp nz,WarpFound2 - ld hl,$d72d - bit 3,[hl] - res 3,[hl] - jp nz,WarpFound2 - ld a,[$d732] - and a,$18 - jp nz,HandleFlyOrTeleportAway - ld a,[W_CUROPPONENT] - and a - jp nz,.newBattle - ld a,[$d730] - bit 7,a ; are we simulating button presses? - jr z,.notSimulating - ld a,[H_CURRENTPRESSEDBUTTONS] - jr .checkIfStartIsPressed -.notSimulating - ld a,[H_NEWLYPRESSEDBUTTONS] -.checkIfStartIsPressed - bit 3,a ; start button - jr z,.startButtonNotPressed -; if START is pressed - xor a - ld [$ff8c],a ; the $2920 ID for the start menu is 0 - jp .displayDialogue -.startButtonNotPressed - bit 0,a ; A button - jp z,.checkIfDownButtonIsPressed -; if A is pressed - ld a,[$d730] - bit 2,a - jp nz,.noDirectionButtonsPressed - call Func_30fd - jr nz,.checkForOpponent - call Func_3eb5 ; check for hidden items, PC's, etc. - ld a,[$ffeb] - and a - jp z,OverworldLoop - call IsSpriteOrSignInFrontOfPlayer ; check for sign or sprite in front of the player - ld a,[$ff8c] ; $2920 ID for NPC/sign text, if any - and a - jp z,OverworldLoop -.displayDialogue - ld a,$35 - call Predef ; check what is in front of the player - call UpdateSprites ; move sprites - ld a,[wFlags_0xcd60] - bit 2,a - jr nz,.checkForOpponent - bit 0,a - jr nz,.checkForOpponent - FuncCoord 8, 9 ; $c45c - ld a,[Coord] - ld [$cf0e],a - call DisplayTextID ; display either the start menu or the NPC/sign text - ld a,[$cc47] - and a - jr z,.checkForOpponent - dec a - ld a,$00 - ld [$cc47],a - jr z,.changeMap - ld a,$52 - call Predef - ld a,[W_CURMAP] - ld [$d71a],a - call Func_62ce - ld a,[W_CURMAP] - call SwitchToMapRomBank ; switch to the ROM bank of the current map - ld hl,$d367 - set 7,[hl] -.changeMap - jp EnterMap -.checkForOpponent - ld a,[W_CUROPPONENT] - and a - jp nz,.newBattle - jp OverworldLoop -.noDirectionButtonsPressed - ld hl,wFlags_0xcd60 - res 2,[hl] - call UpdateSprites ; move sprites - ld a,$01 - ld [$cc4b],a - ld a,[$d528] ; the direction that was pressed last time - and a - jp z,OverworldLoop -; if a direction was pressed last time - ld [$d529],a ; save the last direction - xor a - ld [$d528],a ; zero the direction - jp OverworldLoop -.checkIfDownButtonIsPressed - ld a,[H_CURRENTPRESSEDBUTTONS] ; current joypad state - bit 7,a ; down button - jr z,.checkIfUpButtonIsPressed - ld a,$01 - ld [$c103],a - ld a,$04 - jr .handleDirectionButtonPress -.checkIfUpButtonIsPressed - bit 6,a ; up button - jr z,.checkIfLeftButtonIsPressed - ld a,$ff - ld [$c103],a - ld a,$08 - jr .handleDirectionButtonPress -.checkIfLeftButtonIsPressed - bit 5,a ; left button - jr z,.checkIfRightButtonIsPressed - ld a,$ff - ld [$c105],a - ld a,$02 - jr .handleDirectionButtonPress -.checkIfRightButtonIsPressed - bit 4,a ; right button - jr z,.noDirectionButtonsPressed - ld a,$01 - ld [$c105],a -.handleDirectionButtonPress - ld [$d52a],a ; new direction - ld a,[$d730] - bit 7,a ; are we simulating button presses? - jr nz,.noDirectionChange ; ignore direction changes if we are - ld a,[$cc4b] - and a - jr z,.noDirectionChange - ld a,[$d52a] ; new direction - ld b,a - ld a,[$d529] ; old direction - cp b - jr z,.noDirectionChange -; the code below is strange -; it computes whether or not the player did a 180 degree turn, but then overwrites the result -; also, it does a seemingly pointless loop afterwards - swap a ; put old direction in upper half - or b ; put new direction in lower half - cp a,$48 ; change dir from down to up - jr nz,.notDownToUp - ld a,$02 - ld [$d528],a - jr .oddLoop -.notDownToUp - cp a,$84 ; change dir from up to down - jr nz,.notUpToDown - ld a,$01 - ld [$d528],a - jr .oddLoop -.notUpToDown - cp a,$12 ; change dir from right to left - jr nz,.notRightToLeft - ld a,$04 - ld [$d528],a - jr .oddLoop -.notRightToLeft - cp a,$21 ; change dir from left to right - jr nz,.oddLoop - ld a,$08 - ld [$d528],a -.oddLoop - ld hl,wFlags_0xcd60 - set 2,[hl] - ld hl,$cc4b - dec [hl] - jr nz,.oddLoop - ld a,[$d52a] - ld [$d528],a - call NewBattle - jp c,.battleOccurred - jp OverworldLoop -.noDirectionChange - ld a,[$d52a] ; current direction - ld [$d528],a ; save direction - call UpdateSprites ; move sprites - ld a,[$d700] - cp a,$02 ; surfing - jr z,.surfing -; not surfing - call CollisionCheckOnLand - jr nc,.noCollision - push hl - ld hl,$d736 - bit 2,[hl] - pop hl - jp z,OverworldLoop - 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,[$d736] - bit 7,a - jr z,.noSpinning - callba LoadSpinnerArrowTiles ; spin while moving -.noSpinning - call UpdateSprites ; move sprites -.moveAhead2 - ld hl,wFlags_0xcd60 - res 2,[hl] - ld a,[$d700] - dec a ; riding a bike? - jr nz,.normalPlayerSpriteAdvancement - ld a,[$d736] - 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,[$d730] - bit 7,a - jr nz,.doneStepCounting ; if button presses are being simulated, don't count steps -; step counting - ld hl,$d13b ; step counter - dec [hl] - ld a,[$d72c] - bit 0,a - jr z,.doneStepCounting - ld hl,$d13c - dec [hl] - jr nz,.doneStepCounting - ld hl,$d72c - res 0,[hl] -.doneStepCounting - ld a,[$d790] - bit 7,a ; in the safari zone? - jr z,.notSafariZone - callba SafariZoneCheckSteps - ld a,[$da46] - and a - jp nz,WarpFound2 -.notSafariZone - ld a,[W_ISINBATTLE] - and a - jp nz,CheckWarpsNoCollision - ld a,$13 - call Predef ; decrement HP of poisoned pokemon - ld a,[$d12d] - and a - jp nz,HandleBlackOut ; if all pokemon fainted -.newBattle - call NewBattle - ld hl,$d736 - res 2,[hl] - jp nc,CheckWarpsNoCollision ; check for warps if there was no battle -.battleOccurred - ld hl,$d72d - res 6,[hl] - ld hl,W_FLAGS_D733 - res 3,[hl] - ld hl,$d126 - set 5,[hl] - set 6,[hl] - xor a - ld [H_CURRENTPRESSEDBUTTONS],a ; clear joypad state - ld a,[W_CURMAP] - cp a,CINNABAR_GYM - jr nz,.notCinnabarGym - ld hl,$d79b - set 7,[hl] -.notCinnabarGym - ld hl,$d72e - set 5,[hl] - ld a,[W_CURMAP] - cp a,OAKS_LAB - jp z,.noFaintCheck - callab AnyPlayerPokemonAliveCheck ; check if all the player's pokemon fainted - ld a,d - and a - jr z,.allPokemonFainted -.noFaintCheck - ld c,$0a - call DelayFrames - jp EnterMap -.allPokemonFainted - ld a,$ff - ld [$d057],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,[$d72d] - bit 4,a - jr nz,.noBattle - call Func_30fd - jr nz,.noBattle - ld a,[$d72e] - bit 4,a - jr nz,.noBattle - ld b, BANK(InitBattle) - ld hl, InitBattle - jp Bankswitch ; determines if a battle will occur and runs the battle if so -.noBattle - and a - ret - -; function to make bikes twice as fast as walking -BikeSpeedup:: ; 06a0 (0:06a0) - ld a,[$cc57] - and a - ret nz - ld a,[W_CURMAP] - cp a,ROUTE_17 ; Cycling Road - jr nz,.goFaster - ld a,[H_CURRENTPRESSEDBUTTONS] ; current joypad state - and a,%01110000 ; bit mask for up, left, right buttons - ret nz -.goFaster - jp AdvancePlayerSprite - -; check if the player has stepped onto a warp after having not collided -CheckWarpsNoCollision:: ; 06b4 (0:06b4) - ld a,[$d3ae] ; number of warps - and a - jp z,CheckMapConnections - ld a,[$d3ae] ; number of warps - ld b,$00 - ld c,a - ld a,[W_YCOORD] - ld d,a - ld a,[W_XCOORD] - ld e,a - ld hl,$d3af ; start of warp entries -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,$d736 - set 2,[hl] - callba Func_c49d ; check if the player sprite is standing on a "door" tile - pop bc - pop hl - jr c,WarpFound1 ; if it is, go to 0735 - push hl - push bc - call ExtraWarpCheck ; sets carry if the warp is confirmed - pop bc - pop hl - jr nc,CheckWarpsNoCollisionRetry2 -; if the extra check passed - ld a,[W_FLAGS_D733] - bit 2,a - jr nz,WarpFound1 - push de - push bc - call GetJoypadState - pop bc - pop de - ld a,[H_CURRENTPRESSEDBUTTONS] ; current joypad state - and a,%11110000 ; bit mask for directional buttons - 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,[$d3ae] ; number of warps - ld c,a - ld hl,$d3af ; start of warp entries -.loop - ld a,[hli] ; Y coordinate of warp - ld b,a - ld a,[W_YCOORD] - cp b - jr nz,.retry1 - ld a,[hli] ; X coordinate of warp - ld b,a - ld a,[W_XCOORD] - cp b - jr nz,.retry2 - ld a,[hli] - ld [$d42f],a ; save target warp ID - ld a,[hl] - ld [$ff8b],a ; save target map - 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 [$d42f],a ; save target warp ID - ld a,[hli] - ld [$ff8b],a ; save target map - -WarpFound2:: ; 073c (0:073c) - ld a,[$d3ae] ; number of warps - sub c - ld [$d73b],a ; save ID of used warp - ld a,[W_CURMAP] - ld [$d73c],a - call CheckIfInOutsideMap - jr nz,.indoorMaps -; this is for handling "outside" maps that can't have the 0xFF destination map - ld a,[W_CURMAP] - ld [wLastMap],a - ld a,[W_CURMAPWIDTH] - ld [$d366],a - ld a,[$ff8b] ; destination map number - ld [W_CURMAP],a ; change current map to destination map - cp a,ROCK_TUNNEL_1 - jr nz,.notRockTunnel - ld a,$06 - ld [$d35d],a - call GBFadeIn1 -.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,[$ff8b] ; destination map - cp a,$ff - jr z,.goBackOutside -; if not going back to the previous map - ld [W_CURMAP],a ; current map number - callba Func_70787 ; check if the warp was a Silph Co. teleporter - ld a,[$cd5b] - dec a - jr nz,.notTeleporter -; if it's a Silph Co. teleporter - ld hl,$d732 - set 3,[hl] - call DoFlyOrTeleportAwayGraphics - jr .skipMapChangeSound -.notTeleporter - call PlayMapChangeSound -.skipMapChangeSound - ld hl,$d736 - res 0,[hl] - res 1,[hl] - jr .done -.goBackOutside - ld a,[wLastMap] - ld [W_CURMAP],a - call PlayMapChangeSound - xor a - ld [$d35d],a -.done - ld hl,$d736 - set 0,[hl] - call Func_12da - 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,[W_XCOORD] - cp a,$ff - jr nz,.checkEastMap - ld a,[$d387] - ld [W_CURMAP],a - ld a,[$d38f] ; new X coordinate upon entering west map - ld [W_XCOORD],a - ld a,[W_YCOORD] - ld c,a - ld a,[$d38e] ; Y adjustment upon entering west map - add c - ld c,a - ld [W_YCOORD],a - ld a,[$d390] ; pointer to upper left corner of map without adjustment for Y position - ld l,a - ld a,[$d391] - ld h,a - srl c - jr z,.savePointer1 -.pointerAdjustmentLoop1 - ld a,[$d38d] ; width of connected map - add a,$06 - ld e,a - ld d,$00 - ld b,$00 - add hl,de - dec c - jr nz,.pointerAdjustmentLoop1 -.savePointer1 - ld a,l - ld [$d35f],a ; pointer to upper left corner of current tile block map section - ld a,h - ld [$d360],a - jp .loadNewMap -.checkEastMap - ld b,a - ld a,[$d525] ; map width - cp b - jr nz,.checkNorthMap - ld a,[$d392] - ld [W_CURMAP],a - ld a,[$d39a] ; new X coordinate upon entering east map - ld [W_XCOORD],a - ld a,[W_YCOORD] - ld c,a - ld a,[$d399] ; Y adjustment upon entering east map - add c - ld c,a - ld [W_YCOORD],a - ld a,[$d39b] ; pointer to upper left corner of map without adjustment for Y position - ld l,a - ld a,[$d39c] - ld h,a - srl c - jr z,.savePointer2 -.pointerAdjustmentLoop2 - ld a,[$d398] - add a,$06 - ld e,a - ld d,$00 - ld b,$00 - add hl,de - dec c - jr nz,.pointerAdjustmentLoop2 -.savePointer2 - ld a,l - ld [$d35f],a ; pointer to upper left corner of current tile block map section - ld a,h - ld [$d360],a - jp .loadNewMap -.checkNorthMap - ld a,[W_YCOORD] - cp a,$ff - jr nz,.checkSouthMap - ld a,[$d371] - ld [W_CURMAP],a - ld a,[$d378] ; new Y coordinate upon entering north map - ld [W_YCOORD],a - ld a,[W_XCOORD] - ld c,a - ld a,[$d379] ; X adjustment upon entering north map - add c - ld c,a - ld [W_XCOORD],a - ld a,[$d37a] ; pointer to upper left corner of map without adjustment for X position - ld l,a - ld a,[$d37b] - ld h,a - ld b,$00 - srl c - add hl,bc - ld a,l - ld [$d35f],a ; pointer to upper left corner of current tile block map section - ld a,h - ld [$d360],a - jp .loadNewMap -.checkSouthMap - ld b,a - ld a,[$d524] - cp b - jr nz,.didNotEnterConnectedMap - ld a,[$d37c] - ld [W_CURMAP],a - ld a,[$d383] ; new Y coordinate upon entering south map - ld [W_YCOORD],a - ld a,[W_XCOORD] - ld c,a - ld a,[$d384] ; X adjustment upon entering south map - add c - ld c,a - ld [W_XCOORD],a - ld a,[$d385] ; pointer to upper left corner of map without adjustment for X position - ld l,a - ld a,[$d386] - ld h,a - ld b,$00 - srl c - add hl,bc - ld a,l - ld [$d35f],a ; pointer to upper left corner of current tile block map section - ld a,h - ld [$d360],a -.loadNewMap ; load the connected map that was entered - call LoadMapHeader - call Func_2312 ; music - ld b,$09 - call GoPAL_SET -; 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) - FuncCoord 8, 8 ; $c448 - ld a,[Coord] ; 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_02_57 - SFX_Headers_02) / 3 - jr .playSound -.didNotGoThroughDoor - ld a,(SFX_02_5c - SFX_Headers_02) / 3 -.playSound - call PlaySound - ld a,[$d35d] - and a - ret nz - jp GBFadeIn1 - -CheckIfInOutsideMap:: ; 08e1 (0:08e1) -; If the player is in an outside map (a town or route), set the z flag - ld a, [W_CURMAPTILESET] - 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, [W_CURMAP] - 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, [W_CURMAPTILESET] - 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, Func_c3ff - jr .doBankswitch -.useFunction2 - ld hl, Func_c44e -.doBankswitch - ld b, BANK(Func_c44e) - jp Bankswitch - -MapEntryAfterBattle:: ; 091f (0:091f) - callba Func_c35f ; function that appears to disable warp testing after collisions if the player is standing on a warp - ld a,[$d35d] - and a - jp z,GBFadeIn2 - jp LoadGBPal - -HandleBlackOut:: -; For when all the player's pokemon faint. -; Does not print the "blacked out" message. - - call GBFadeIn1 - ld a, $08 - call StopMusic - ld hl, $d72e - res 5, [hl] - ld a, Bank(Func_40b0) ; Bank(Func_40b0) and Bank(Func_62ce) need to be equal. - ld [H_LOADEDROMBANK], a - ld [$2000], a - call Func_40b0 - call Func_62ce - call Func_2312 - jp Func_5d5f - -StopMusic:: - ld [wMusicHeaderPointer], a - ld a, $ff - ld [$c0ee], a - call PlaySound -.wait - ld a, [wMusicHeaderPointer] - and a - jr nz, .wait - jp StopAllSounds - -HandleFlyOrTeleportAway:: - call UpdateSprites - call Delay3 - xor a - ld [$cf0b], a - ld [$d700], a - ld [$d057], a - ld [$d35d], a - ld hl, $d732 - set 2, [hl] - res 5, [hl] - call DoFlyOrTeleportAwayGraphics - ld a, Bank(Func_62ce) - ld [H_LOADEDROMBANK], a - ld [$2000], a - call Func_62ce - jp Func_5d5f - -DoFlyOrTeleportAwayGraphics:: - ld b, BANK(_DoFlyOrTeleportAwayGraphics) - ld hl, _DoFlyOrTeleportAwayGraphics - jp Bankswitch - -LoadPlayerSpriteGraphics:: -; Load sprite graphics based on whether the player is standing, biking, or surfing. - - ; 0: standing - ; 1: biking - ; 2: surfing - - ld a, [$d700] - dec a - jr z, .ridingBike - - ld a, [$ffd7] - 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 [$d700], a - ld [$d11a], a - jp LoadWalkingPlayerSpriteGraphics - -.determineGraphics - ld a, [$d700] - 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, [W_CURMAP] - cp ROUTE_23 - jr z, .allowed - cp INDIGO_PLATEAU - jr z, .allowed - - ld a, [W_CURMAPTILESET] - 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,[$d52e] - ld l,a - ld a,[$d52f] - ld h,a - ld de,$9000 - ld bc,$0600 - ld a,[$d52b] - 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,$c6e8 - ld a,[$d3ad] ; background tile number - 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,$c6e8 - ld a,[W_CURMAPWIDTH] - ld [$ff8c],a - add a,$06 ; border (east and west) - ld [$ff8b],a ; map width + border - ld b,$00 - ld c,a -; make space for north border (next 3 lines) - add hl,bc - add hl,bc - add hl,bc - ld c,$03 - add hl,bc ; this puts us past the (west) border - ld a,[$d36a] ; tile map pointer - ld e,a - ld a,[$d36b] - ld d,a ; de = tile map pointer - ld a,[W_CURMAPHEIGHT] - ld b,a -.rowLoop ; copy one row each iteration - push hl - ld a,[$ff8c] ; 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,[$ff8b] ; map width + border - add l - ld l,a - jr nc,.noCarry - inc h -.noCarry - dec b - jr nz,.rowLoop -.northConnection - ld a,[$d371] - cp a,$ff - jr z,.southConnection - call SwitchToMapRomBank - ld a,[$d372] - ld l,a - ld a,[$d373] - ld h,a - ld a,[$d374] - ld e,a - ld a,[$d375] - ld d,a - ld a,[$d376] - ld [$ff8b],a - ld a,[$d377] - ld [$ff8c],a - call LoadNorthSouthConnectionsTileMap -.southConnection - ld a,[$d37c] - cp a,$ff - jr z,.westConnection - call SwitchToMapRomBank - ld a,[$d37d] - ld l,a - ld a,[$d37e] - ld h,a - ld a,[$d37f] - ld e,a - ld a,[$d380] - ld d,a - ld a,[$d381] - ld [$ff8b],a - ld a,[$d382] - ld [$ff8c],a - call LoadNorthSouthConnectionsTileMap -.westConnection - ld a,[$d387] - cp a,$ff - jr z,.eastConnection - call SwitchToMapRomBank - ld a,[$d388] - ld l,a - ld a,[$d389] - ld h,a - ld a,[$d38a] - ld e,a - ld a,[$d38b] - ld d,a - ld a,[$d38c] - ld b,a - ld a,[$d38d] - ld [$ff8b],a - call LoadEastWestConnectionsTileMap -.eastConnection - ld a,[$d392] - cp a,$ff - jr z,.done - call SwitchToMapRomBank - ld a,[$d393] - ld l,a - ld a,[$d394] - ld h,a - ld a,[$d395] - ld e,a - ld a,[$d396] - ld d,a - ld a,[$d397] - ld b,a - ld a,[$d398] - ld [$ff8b],a - call LoadEastWestConnectionsTileMap -.done - ret - -LoadNorthSouthConnectionsTileMap:: ; 0ade (0:0ade) - ld c,$03 -.loop - push de - push hl - ld a,[$ff8b] ; width of connection - ld b,a -.innerLoop - ld a,[hli] - ld [de],a - inc de - dec b - jr nz,.innerLoop - pop hl - pop de - ld a,[$ff8c] ; width of connected map - add l - ld l,a - jr nc,.noCarry1 - inc h -.noCarry1 - ld a,[W_CURMAPWIDTH] - add a,$06 - 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,$03 -.innerLoop - ld a,[hli] - ld [de],a - inc de - dec c - jr nz,.innerLoop - pop de - pop hl - ld a,[$ff8b] ; width of connected map - add l - ld l,a - jr nc,.noCarry1 - inc h -.noCarry1 - ld a,[W_CURMAPWIDTH] - add a,$06 - 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 [$FF8C] -; if not, [$FF8C] is set to 0 -IsSpriteOrSignInFrontOfPlayer:: ; 0b23 (0:0b23) - xor a - ld [$ff8c],a - ld a,[$d4b0] ; number of signs in the map - and a - jr z,.extendRangeOverCounter -; if there are signs - ld a,$35 - call Predef ; get the coordinates in front of the player in de - ld hl,$d4b1 ; start of sign coordinates - ld a,[$d4b0] ; number of signs in the map - ld b,a - ld c,$00 -.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,$d4d1 ; start of sign text ID's - ld b,$00 - dec c - add hl,bc - ld a,[hl] - ld [$ff8c],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 - ld a,$35 - call Predef ; get the tile in front of the player in c - ld hl,$d532 ; list of tiles that extend talking range (counter tiles) - ld b,$03 - 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 [$FF8C] -IsSpriteInFrontOfPlayer:: ; 0b6b (0:0b6b) - ld d,$10 ; talking range in pixels (normal range) -IsSpriteInFrontOfPlayer2:: ; 0b6d (0:0b6d) - ld bc,$3c40 ; Y and X position of player sprite - ld a,[$c109] ; direction the player is facing -.checkIfPlayerFacingUp - cp a,$04 - jr nz,.checkIfPlayerFacingDown -; facing up - ld a,b - sub d - ld b,a - ld a,$08 - jr .doneCheckingDirection -.checkIfPlayerFacingDown - cp a,$00 - jr nz,.checkIfPlayerFacingRight -; facing down - ld a,b - add d - ld b,a - ld a,$04 - jr .doneCheckingDirection -.checkIfPlayerFacingRight - cp a,$0c - jr nz,.playerFacingLeft -; facing right - ld a,c - add d - ld c,a - ld a,$01 - jr .doneCheckingDirection -.playerFacingLeft -; facing left - ld a,c - sub d - ld c,a - ld a,$02 -.doneCheckingDirection - ld [$d52a],a - ld a,[$d4e1] ; number of sprites - and a - ret z -; if there are sprites - ld hl,$c110 - 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 - set 7,[hl] - ld a,e - ld [$ff8c],a ; store sprite ID - 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,[$d736] - bit 6,a ; is the player jumping? - jr nz,.noCollision -; if not jumping a ledge - ld a,[$cd38] - and a - jr nz,.noCollision - ld a,[$d52a] ; the direction that the player is trying to go in - ld d,a - ld a,[$c10c] ; 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 [$ff8c],a - call IsSpriteInFrontOfPlayer ; check for sprite collisions again? when does the above check fail to detect a sprite collision? - ld a,[$ff8c] - 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,[$c02a] - cp a,(SFX_02_5b - SFX_Headers_02) / 3 ; check if collision sound is already playing - jr z,.setCarry - ld a,(SFX_02_5b - SFX_Headers_02) / 3 - 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) - ld a,$35 - call Predef ; get tile in front of player - ld a,[$cfc6] ; tile in front of player - ld c,a - ld hl,$d530 ; 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 - ld a,$35 - call Predef ; 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,[$d736] - bit 6,a ; is the player jumping? - ret nz -; if not jumping - -Func_c44:: ; 0c44 (0:0c44) - FuncCoord 8, 9 ; $c45c - ld a,[Coord] ; tile the player is on - ld [$cf0e],a - -CheckForTilePairCollisions:: ; 0c4a (0:0c4a) - ld a,[$cfc6] ; tile in front of the player - ld c,a -.tilePairCollisionLoop - ld a,[W_CURMAPTILESET] ; 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,[$cf0e] ; 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,[$d52b] ; tile data ROM bank - ld [H_LOADEDROMBANK],a - ld [$2000],a ; switch to ROM bank that contains tile data - ld a,[$d35f] ; address of upper left corner of current map view - ld e,a - ld a,[$d360] - 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,[W_CURMAPWIDTH] - add a,$06 - 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,[W_YBLOCKCOORD] - and a - jr z,.adjustForXCoordWithinTileBlock - ld bc,$0030 - add hl,bc -.adjustForXCoordWithinTileBlock - ld a,[W_XBLOCKCOORD] - and a - jr z,.copyToVisibleAreaBuffer - ld bc,$0002 - add hl,bc -.copyToVisibleAreaBuffer - ld de,wTileMap ; base address for the tiles that are directly transfered to VRAM during V-blank - ld b,$12 -.rowLoop2 - ld c,$14 -.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 [$2000],a ; restore previous ROM bank - ret - -AdvancePlayerSprite:: ; 0d27 (0:0d27) - ld a,[$c103] ; delta Y - ld b,a - ld a,[$c105] ; 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,[W_YCOORD] - add b - ld [W_YCOORD],a - ld a,[W_XCOORD] - add c - ld [W_XCOORD],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,[$d526] - ld e,a - and a,$e0 - ld d,a - ld a,e - add a,$02 - and a,$1f - or d - ld [$d526],a - jr .adjustXCoordWithinBlock -.checkIfMovingWest - cp a,$ff - jr nz,.checkIfMovingSouth -; moving west - ld a,[$d526] - ld e,a - and a,$e0 - ld d,a - ld a,e - sub a,$02 - and a,$1f - or d - ld [$d526],a - jr .adjustXCoordWithinBlock -.checkIfMovingSouth - ld a,b - cp a,$01 - jr nz,.checkIfMovingNorth -; moving south - ld a,[$d526] - add a,$40 - ld [$d526],a - jr nc,.adjustXCoordWithinBlock - ld a,[$d527] - inc a - and a,$03 - or a,$98 - ld [$d527],a - jr .adjustXCoordWithinBlock -.checkIfMovingNorth - cp a,$ff - jr nz,.adjustXCoordWithinBlock -; moving north - ld a,[$d526] - sub a,$40 - ld [$d526],a - jr nc,.adjustXCoordWithinBlock - ld a,[$d527] - dec a - and a,$03 - or a,$98 - ld [$d527],a -.adjustXCoordWithinBlock - ld a,c - and a - jr z,.pointlessJump ; mistake? -.pointlessJump - ld hl,W_XBLOCKCOORD - 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,$d4e3 - inc [hl] - ld de,$d35f - 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,$d4e3 - dec [hl] - ld de,$d35f - call MoveTileBlockMapPointerWest - jr .updateMapView -.adjustYCoordWithinBlock - ld hl,W_YBLOCKCOORD - 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,$d4e2 - inc [hl] - ld de,$d35f - ld a,[W_CURMAPWIDTH] - 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,$d4e2 - dec [hl] - ld de,$d35f - ld a,[W_CURMAPWIDTH] - call MoveTileBlockMapPointerNorth -.updateMapView - call LoadCurrentMapView - ld a,[$c103] ; 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,[$c105] ; 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,[$c103] ; delta Y - ld b,a - ld a,[$c105] ; delta X - ld c,a - sla b - sla c - ld a,[$ffaf] - add b - ld [$ffaf],a ; update background scroll Y - ld a,[$ffae] - add c - ld [$ffae],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,$c114 - ld a,[$d4e1] ; 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,$06 - 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,$06 - 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) - FuncCoord 0, 0 - ld hl,Coord - call ScheduleRowRedrawHelper - ld a,[$d526] - ld [H_SCREENEDGEREDRAWADDR],a - ld a,[$d527] - ld [H_SCREENEDGEREDRAWADDR + 1],a - ld a,REDRAWROW - ld [H_SCREENEDGEREDRAW],a - ret - -ScheduleRowRedrawHelper:: ; 0ea6 (0:0ea6) - ld de,wScreenEdgeTiles - ld c,$28 -.loop - ld a,[hli] - ld [de],a - inc de - dec c - jr nz,.loop - ret - -ScheduleSouthRowRedraw:: ; 0eb2 (0:0eb2) - FuncCoord 0,16 - ld hl,Coord - call ScheduleRowRedrawHelper - ld a,[$d526] - ld l,a - ld a,[$d527] - ld h,a - ld bc,$0200 - add hl,bc - ld a,h - and a,$03 - or a,$98 - ld [H_SCREENEDGEREDRAWADDR + 1],a - ld a,l - ld [H_SCREENEDGEREDRAWADDR],a - ld a,REDRAWROW - ld [H_SCREENEDGEREDRAW],a - ret - -ScheduleEastColumnRedraw:: ; 0ed3 (0:0ed3) - FuncCoord 18,0 - ld hl,Coord - call ScheduleColumnRedrawHelper - ld a,[$d526] - ld c,a - and a,$e0 - ld b,a - ld a,c - add a,18 - and a,$1f - or b - ld [H_SCREENEDGEREDRAWADDR],a - ld a,[$d527] - ld [H_SCREENEDGEREDRAWADDR + 1],a - ld a,REDRAWCOL - ld [H_SCREENEDGEREDRAW],a - ret - -ScheduleColumnRedrawHelper:: ; 0ef2 (0:0ef2) - ld de,wScreenEdgeTiles - ld c,$12 -.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) - FuncCoord 0,0 - ld hl,Coord - call ScheduleColumnRedrawHelper - ld a,[$d526] - ld [H_SCREENEDGEREDRAWADDR],a - ld a,[$d527] - ld [H_SCREENEDGEREDRAWADDR + 1],a - ld a,REDRAWCOL - ld [H_SCREENEDGEREDRAW],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,[$d52c] ; pointer to tiles - ld l,a - ld a,[$d52d] - 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 -GetJoypadStateOverworld:: ; 0f4d (0:0f4d) - xor a - ld [$c103],a - ld [$c105],a - call RunMapScript - call GetJoypadState - ld a,[W_FLAGS_D733] - bit 3,a ; check if a trainer wants a challenge - jr nz,.notForcedDownwards - ld a,[W_CURMAP] - cp a,ROUTE_17 ; Cycling Road - jr nz,.notForcedDownwards - ld a,[H_CURRENTPRESSEDBUTTONS] ; current joypad state - and a,%11110011 ; bit mask for all directions and A/B - jr nz,.notForcedDownwards - ld a,%10000000 ; down pressed - ld [H_CURRENTPRESSEDBUTTONS],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,[$d730] - bit 7,a - ret z -; if simulating button presses - ld a,[H_CURRENTPRESSEDBUTTONS] ; current joypad state - ld b,a - ld a,[$cd3b] ; bit mask for button presses that override simulated ones - and b - ret nz ; return if the simulated button presses are overridden - ld hl,$cd38 ; index of current simulated button press - dec [hl] - ld a,[hl] - cp a,$ff - jr z,.doneSimulating ; if the end of the simulated button presses has been reached - ld hl,$ccd3 ; base address of simulated button presses -; add offset to base address - add l - ld l,a - jr nc,.noCarry - inc h -.noCarry - ld a,[hl] - ld [H_CURRENTPRESSEDBUTTONS],a ; store simulated button press in joypad state - and a - ret nz - ld [H_NEWLYPRESSEDBUTTONS],a - ld [H_NEWLYRELEASEDBUTTONS],a - ret -; if done simulating button presses -.doneSimulating - xor a - ld [$cd3a],a - ld [$cd38],a - ld [$ccd3],a - ld [wJoypadForbiddenButtonsMask],a - ld [H_CURRENTPRESSEDBUTTONS],a - ld hl,$d736 - ld a,[hl] - and a,$f8 - ld [hl],a - ld hl,$d730 - 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,[$d730] - bit 7,a - jp nz,.noCollision ; return and clear carry if button presses are being simulated - ld a,[$d52a] ; the direction that the player is trying to go in - ld d,a - ld a,[$c10c] ; 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 - ld a,$35 - call Predef ; get tile in front of player (puts it in c and [$CFC6]) - ld a,[$cfc6] ; 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,$d530 ; 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,[$c02a] - cp a,(SFX_02_5b - SFX_Headers_02) / 3 ; check if collision sound is already playing - jr z,.setCarry - ld a,(SFX_02_5b - SFX_Headers_02) / 3 - call PlaySound ; play collision sound (if it's not already playing) -.setCarry - scf - jr .done -.noCollision - and a -.done - ret -.stopSurfing - xor a - ld [$d700],a - call LoadPlayerSpriteGraphics - call Func_2307 - jr .noCollision -.checkIfVermilionDockTileset - ld a, [W_CURMAPTILESET] ; 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 Func_f225 ; check if the player is pushing a boulder - ld a,[wFlags_0xcd60] - bit 1,a ; is the player pushing a boulder? - jr z,.afterBoulderEffect - callba Func_f2b5 ; displays dust effect when pushing a boulder -.afterBoulderEffect - pop bc - pop de - pop hl - call Func_310e - ld a,[W_CURMAP] ; current map number - call SwitchToMapRomBank ; change to the ROM bank the map's data is in - ld hl,W_MAPSCRIPTPTR - 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 ; $4180 - ld hl,$8000 - jr LoadPlayerSpriteGraphicsCommon - -LoadSurfingPlayerSpriteGraphics:: ; 1055 (0:1055) - ld de,SeelSprite - ld hl,$8000 - jr LoadPlayerSpriteGraphicsCommon - -LoadBikePlayerSpriteGraphics:: ; 105d (0:105d) - ld de,RedCyclingSprite - ld hl,$8000 - -LoadPlayerSpriteGraphicsCommon:: ; 1063 (0:1063) - push de - push hl - ld bc,(BANK(RedSprite) << 8) + $0c - call CopyVideoData - pop hl - pop de - ld a,$c0 - add e - ld e,a - jr nc,.noCarry - inc d -.noCarry - set 3,h - ld bc,$050c - jp CopyVideoData - -; function to load data from the map header -LoadMapHeader:: ; 107c (0:107c) - callba Func_f113 - ld a,[W_CURMAPTILESET] - ld [$d119],a - ld a,[W_CURMAP] - call SwitchToMapRomBank - ld a,[W_CURMAPTILESET] - ld b,a - res 7,a - ld [W_CURMAPTILESET],a - ld [$ff8b],a - bit 7,b - ret nz - ld hl,MapHeaderPointers - ld a,[W_CURMAP] - 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,$d367 - 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 [$d371],a - ld [$d37c],a - ld [$d387],a - ld [$d392],a -; copy connection data (if any) to WRAM - ld a,[W_MAPCONNECTIONS] - ld b,a -.checkNorth - bit 3,b - jr z,.checkSouth - ld de,W_MAPCONN1PTR - call CopyMapConnectionHeader -.checkSouth - bit 2,b - jr z,.checkWest - ld de,W_MAPCONN2PTR - call CopyMapConnectionHeader -.checkWest - bit 1,b - jr z,.checkEast - ld de,W_MAPCONN3PTR - call CopyMapConnectionHeader -.checkEast - bit 0,b - jr z,.getObjectDataPointer - ld de,W_MAPCONN4PTR - call CopyMapConnectionHeader -.getObjectDataPointer - ld a,[hli] - ld [$d3a9],a - ld a,[hli] - ld [$d3aa],a - push hl - ld a,[$d3a9] - ld l,a - ld a,[$d3aa] - ld h,a ; hl = base of object data - ld de,$d3ad ; background tile ID - ld a,[hli] - ld [de],a ; save background tile ID -.loadWarpData - ld a,[hli] ; number of warps - ld [$d3ae],a ; save the number of warps - and a ; are there any warps? - jr z,.loadSignData ; if not, skip this - ld c,a - ld de,$d3af ; base address of warps -.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 [$d4b0],a ; save the number of signs - and a ; are there any signs? - jr z,.loadSpriteData ; if not, skip this - ld c,a - ld de,$d4d1 ; base address of sign text IDs - ld a,d - ld [$ff95],a - ld a,e - ld [$ff96],a - ld de,$d4b1 ; base address of sign coordinates -.signLoop - ld a,[hli] - ld [de],a - inc de - ld a,[hli] - ld [de],a - inc de - push de - ld a,[$ff95] - ld d,a - ld a,[$ff96] - ld e,a - ld a,[hli] - ld [de],a - inc de - ld a,d - ld [$ff95],a - ld a,e - ld [$ff96],a - pop de - dec c - jr nz,.signLoop -.loadSpriteData - ld a,[$d72e] - 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 [$d4e1],a ; save the number of sprites - push hl -; zero C110-C1FF and C210-C2FF - ld hl,$c110 - ld de,$c210 - 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,$c112 - ld de,$0010 - ld c,$0f -.disableSpriteEntriesLoop - ld [hl],$ff - add hl,de - dec c - jr nz,.disableSpriteEntriesLoop - pop hl - ld de,$c110 - ld a,[$d4e1] ; 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 [$ff8d],a ; save movement byte 2 - ld a,[hli] - ld [$ff8e],a ; save text ID and flags byte - push bc - push hl - ld b,$00 - ld hl,W_MAPSPRITEDATA - add hl,bc - ld a,[$ff8d] - ld [hli],a ; store movement byte 2 in byte 0 of sprite entry - ld a,[$ff8e] - ld [hl],a ; this appears pointless, since the value is overwritten immediately after - ld a,[$ff8e] - ld [$ff8d],a - and a,$3f - ld [hl],a ; store text ID in byte 1 of sprite entry - pop hl - ld a,[$ff8d] - bit 6,a - jr nz,.trainerSprite - bit 7,a - jr nz,.itemBallSprite - jr .regularSprite -.trainerSprite - ld a,[hli] - ld [$ff8d],a ; save trainer class - ld a,[hli] - ld [$ff8e],a ; save trainer number (within class) - push hl - ld hl,W_MAPSPRITEEXTRADATA - add hl,bc - ld a,[$ff8d] - ld [hli],a ; store trainer class in byte 0 of the entry - ld a,[$ff8e] - ld [hl],a ; store trainer number in byte 1 of the entry - pop hl - jr .nextSprite -.itemBallSprite - ld a,[hli] - ld [$ff8d],a ; save item number - push hl - ld hl,W_MAPSPRITEEXTRADATA - add hl,bc - ld a,[$ff8d] - 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,W_MAPSPRITEEXTRADATA - 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 - ld a,$19 - call Predef ; load tileset data - callab LoadWildData ; load wild pokemon data - pop hl ; restore hl from before going to the warp/sign/sprite data (this value was saved for seemingly no purpose) - ld a,[W_CURMAPHEIGHT] ; map height in 4x4 tile blocks - add a ; double it - ld [$d524],a ; store map height in 2x2 tile blocks - ld a,[W_CURMAPWIDTH] ; map width in 4x4 tile blocks - add a ; double it - ld [$d525],a ; map width in 2x2 tile blocks - ld a,[W_CURMAP] - ld c,a - ld b,$00 - ld a,[H_LOADEDROMBANK] - push af - ld a, BANK(MapSongBanks) - ld [H_LOADEDROMBANK],a - ld [$2000],a - ld hl, MapSongBanks - add hl,bc - add hl,bc - ld a,[hli] - ld [$d35b],a ; music 1 - ld a,[hl] - ld [$d35c],a ; music 2 - pop af - ld [H_LOADEDROMBANK],a - ld [$2000],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 [$d527],a - xor a - ld [$d526],a - ld [$ffaf],a - ld [$ffae],a - ld [wWalkCounter],a - ld [$d119],a - ld [$d11a],a - ld [$d3a8],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 - ld hl,wTileMap - ld de,$9800 - ld b,$12 -.vramCopyLoop - ld c,$14 -.vramCopyInnerLoop - ld a,[hli] - ld [de],a - inc e - dec c - jr nz,.vramCopyInnerLoop - ld a,$0c - add e - ld e,a - jr nc,.noCarry - inc d -.noCarry - dec b - jr nz,.vramCopyLoop - ld a,$01 - ld [$cfcb],a - call EnableLCD - ld b,$09 - call GoPAL_SET - call LoadPlayerSpriteGraphics - ld a,[$d732] - and a,$18 ; did the player fly or teleport in? - jr nz,.restoreRomBank - ld a,[W_FLAGS_D733] - bit 1,a - jr nz,.restoreRomBank - call Func_235f ; music related - call Func_2312 ; music related -.restoreRomBank - pop af - ld [H_LOADEDROMBANK],a - ld [$2000],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 [$2000],a ; switch to map ROM bank - pop bc - pop hl - ret - -Func_12da:: ; 12da (0:12da) - ld a, $1e - ld [$d13a], a - ld hl, $d730 - ld a, [hl] - or $26 - ld [hl], a - ret - -Func_12e7:: ; 12e7 (0:12e7) - ld hl, $d728 - res 0, [hl] - ret - -ForceBikeOrSurf:: ; 12ed (0:12ed) - ld b, BANK(RedSprite) - ld hl, LoadPlayerSpriteGraphics - call Bankswitch - jp Func_2307 ; update map/player state? - -; this is used to check if the player wants to interrupt the opening sequence at several points -; XXX is this used anywhere else? -; INPUT: -; c = number of frames to wait -; sets carry if Up+Select+B, Start, or A is pressed within c frames -; unsets carry otherwise -CheckForUserInterruption:: ; 12f8 (0:12f8) - call DelayFrame - push bc - call GetJoypadStateLowSensitivity - pop bc - ld a,[H_CURRENTPRESSEDBUTTONS] ; currently pressed buttons - cp a,%01000110 ; Up, Select button, B button - jr z,.setCarry ; if all three keys are pressed - ld a,[$ffb5] ; either newly pressed buttons or currently pressed buttons at low sampling rate - and a,%00001001 ; Start button, A button - jr nz,.setCarry ; if either key is pressed - dec c - jr nz,CheckForUserInterruption -.unsetCarry - and a - ret -.setCarry - scf - ret - -; function to load position data for destination warp when switching maps -; INPUT: -; a = ID of destination warp within destination map -LoadDestinationWarpPosition:: ; 1313 (0:1313) - ld b,a - ld a,[H_LOADEDROMBANK] - push af - ld a,[wPredefParentBank] - ld [H_LOADEDROMBANK],a - ld [$2000],a - ld a,b - add a - add a - ld c,a - ld b,0 - add hl,bc - ld bc,4 - ld de,$d35f - call CopyData - pop af - ld [H_LOADEDROMBANK],a - ld [$2000],a - ret - -; INPUT: -; c: if nonzero, show at least a sliver of health -; d = number of HP bar sections (normally 6) -; e = health (in eighths of bar sections) (normally out of 48) -DrawHPBar:: ; 1336 (0:1336) - push hl - push de - push bc - ld a,$71 ; left of HP bar tile 1 - ld [hli],a - ld a,$62 ; left of HP bar tile 2 - ld [hli],a - push hl - ld a,$63 ; empty bar section tile -.drawEmptyBarLoop - ld [hli],a - dec d - jr nz,.drawEmptyBarLoop - ld a,[$cf94] - dec a ; what should the right of HP bar tile be? - ld a,$6d ; right of HP bar tile, in status screen and battles - jr z,.writeTile - dec a ; right of HP bar tile, in pokemon menu -.writeTile - ld [hl],a - pop hl - ld a,e - and a ; is there enough health to show up on the HP bar? - jr nz,.loop ; if so, draw the HP bar - ld a,c - and a ; should a sliver of health be shown no matter what? - jr z,.done - ld e,1 ; if so, fill one eighth of a bar section -; loop to draw every full bar section -.loop - ld a,e - sub a,8 - jr c,.drawPartialBarSection - ld e,a - ld a,$6b ; filled bar section tile - ld [hli],a - ld a,e - and a - jr z,.done - jr .loop -; draws a partial bar section at the end (if necessary) -; there are 7 possible partial bar sections from 1/8 to 7/8 full -.drawPartialBarSection - ld a,$63 ; empty bar section tile - add e ; add e to get the appropriate partial bar section tile - ld [hl],a ; write the tile -.done - pop bc - pop de - pop hl - ret - -; loads pokemon data from one of multiple sources to $cf98 -; loads base stats to $d0b8 -; INPUT: -; [$cf92] = index of pokemon within party/box -; [$cc49] = source -; 00: player's party -; 01: enemy's party -; 02: current box -; 03: daycare -; OUTPUT: -; [$cf91] = pokemon ID -; $cf98 = base address of pokemon data -; $d0b8 = base address of base stats -LoadMonData:: ; 1372 (0:1372) - ld hl,LoadMonData_ - ld b,BANK(LoadMonData_) - jp Bankswitch - -; writes c to $d0dc+b -Func_137a:: ; 137a (0:137a) - ld hl, $d0dc - ld e, b - ld d, $0 - add hl, de - ld a, c - ld [hl], a - ret - -LoadFlippedFrontSpriteByMonIndex:: ; 1384 (0:1384) - ld a, $1 - ld [W_SPRITEFLIPPED], a - -LoadFrontSpriteByMonIndex:: ; 1389 (0:1389) - push hl - ld a, [$d11e] - push af - ld a, [$cf91] - ld [$d11e], a - ld a, $3a - call Predef ; indirect jump to IndexToPokedex (41010 (10:5010)) - ld hl, $d11e - ld a, [hl] - pop bc - ld [hl], b - and a - pop hl - jr z, .invalidDexNumber ; dex #0 invalid - cp 151 + 1 - jr c, .validDexNumber ; dex >#151 invalid -.invalidDexNumber - ld a, RHYDON ; $1 - ld [$cf91], a - ret -.validDexNumber - push hl - ld de, $9000 - call LoadMonFrontSprite - pop hl - ld a, [H_LOADEDROMBANK] - push af - ld a, Bank(asm_3f0d0) - ld [H_LOADEDROMBANK], a - ld [$2000], a - xor a - ld [$ffe1], a - call asm_3f0d0 - xor a - ld [W_SPRITEFLIPPED], a - pop af - ld [H_LOADEDROMBANK], a - ld [$2000], a - ret - -; plays the cry of a pokemon -; INPUT: -; a = pokemon ID -PlayCry:: ; 13d0 (0:13d0) - call GetCryData - call PlaySound ; play cry - jp WaitForSoundToFinish ; wait for sound to be done playing - -; gets a pokemon's cry data -; INPUT: -; a = pokemon ID -GetCryData:: ; 13d9 (0:13d9) - dec a - ld c,a - ld b,0 - ld hl,CryData - add hl,bc - add hl,bc - add hl,bc - ld a,Bank(CryData) - call BankswitchHome - ld a,[hli] - ld b,a - ld a,[hli] - ld [$c0f1],a - ld a,[hl] - ld [$c0f2],a - call BankswitchBack - ld a,b ; a = cryID - ld c,$14 ; base sound ID for pokemon cries - rlca - add b ; a = cryID * 3 - add c ; a = $14 + cryID * 3 - ret - -DisplayPartyMenu:: ; 13fc (0:13fc) - ld a,[$ffd7] - push af - xor a - ld [$ffd7],a - call GBPalWhiteOutWithDelay3 - call ClearSprites - call PartyMenuInit - call DrawPartyMenu - jp HandlePartyMenuInput - -GoBackToPartyMenu:: ; 1411 (0:1411) - ld a,[$ffd7] - push af - xor a - ld [$ffd7],a - call PartyMenuInit - call RedrawPartyMenu - jp HandlePartyMenuInput - -PartyMenuInit:: ; 1420 (0:1420) - ld a,$01 - call BankswitchHome - call LoadHpBarAndStatusTilePatterns - ld hl,$d730 - set 6,[hl] ; turn off letter printing delay - xor a - ld [$cc49],a - ld [$cc37],a - ld hl,wTopMenuItemY - inc a - ld [hli],a ; top menu item Y - xor a - ld [hli],a ; top menu item X - ld a,[$cc2b] - push af - ld [hli],a ; current menu item ID - inc hl - ld a,[W_NUMINPARTY] - and a ; are there more than 0 pokemon in the party? - jr z,.storeMaxMenuItemID - dec a -; if party is not empty, the max menu item ID is ([W_NUMINPARTY] - 1) -; otherwise, it is 0 -.storeMaxMenuItemID - ld [hli],a ; max menu item ID - ld a,[$d11f] - and a - ld a,%00000011 ; A button and B button - jr z,.next - xor a - ld [$d11f],a - inc a -.next - ld [hli],a ; menu watched keys - pop af - ld [hl],a ; old menu item ID - ret - -HandlePartyMenuInput:: ; 145a (0:145a) - ld a,1 - ld [$cc4a],a - ld a,$40 - ld [$d09b],a - call HandleMenuInputPokemonSelection - call PlaceUnfilledArrowMenuCursor - ld b,a - xor a - ld [$d09b],a - ld a,[wCurrentMenuItem] - ld [$cc2b],a - ld hl,$d730 - res 6,[hl] ; turn on letter printing delay - ld a,[$cc35] - and a - jp nz,.swappingPokemon - pop af - ld [$ffd7],a - bit 1,b - jr nz,.noPokemonChosen - ld a,[W_NUMINPARTY] - and a - jr z,.noPokemonChosen - ld a,[wCurrentMenuItem] - ld [wWhichPokemon],a - ld hl,W_PARTYMON1 - ld b,0 - ld c,a - add hl,bc - ld a,[hl] - ld [$cf91],a - ld [$cfd9],a - call BankswitchBack - and a - ret -.noPokemonChosen - call BankswitchBack - scf - ret -.swappingPokemon - bit 1,b ; was the B button pressed? - jr z,.handleSwap ; if not, handle swapping the pokemon -.cancelSwap ; if the B button was pressed - callba ErasePartyMenuCursors - xor a - ld [$cc35],a - ld [$d07d],a - call RedrawPartyMenu - jr HandlePartyMenuInput -.handleSwap - ld a,[wCurrentMenuItem] - ld [wWhichPokemon],a - callba SwitchPartyMon - jr HandlePartyMenuInput - -DrawPartyMenu:: ; 14d4 (0:14d4) - ld hl, DrawPartyMenu_ - jr DrawPartyMenuCommon - -RedrawPartyMenu:: ; 14d9 (0:14d9) - ld hl, RedrawPartyMenu_ - -DrawPartyMenuCommon:: ; 14dc (0:14dc) - ld b, BANK(RedrawPartyMenu_) - jp Bankswitch - -; prints a pokemon's status condition -; INPUT: -; de = address of status condition -; hl = destination address -PrintStatusCondition:: ; 14e1 (0:14e1) - push de - dec de - dec de ; de = address of current HP - ld a,[de] - ld b,a - dec de - ld a,[de] - or b ; is the pokemon's HP zero? - pop de - jr nz,PrintStatusConditionNotFainted -; if the pokemon's HP is 0, print "FNT" - ld a,"F" - ld [hli],a - ld a,"N" - ld [hli],a - ld [hl],"T" - and a - ret -PrintStatusConditionNotFainted ; 14f6 - ld a,[H_LOADEDROMBANK] - push af - ld a,BANK(PrintStatusAilment) - ld [H_LOADEDROMBANK],a - ld [$2000],a - call PrintStatusAilment ; print status condition - pop bc - ld a,b - ld [H_LOADEDROMBANK],a - ld [$2000],a - ret - -; function to print pokemon level, leaving off the ":L" if the level is at least 100 -; INPUT: -; hl = destination address -; [$cfb9] = level -PrintLevel:: ; 150b (0:150b) - ld a,$6e ; ":L" tile ID - ld [hli],a - ld c,2 ; number of digits - ld a,[$cfb9] ; level - cp a,100 - jr c,PrintLevelCommon -; if level at least 100, write over the ":L" tile - dec hl - inc c ; increment number of digits to 3 - jr PrintLevelCommon - -; prints the level without leaving off ":L" regardless of level -; INPUT: -; hl = destination address -; [$cfb9] = level -PrintLevelFull:: ; 151b (0:151b) - ld a,$6e ; ":L" tile ID - ld [hli],a - ld c,3 ; number of digits - ld a,[$cfb9] ; level - -PrintLevelCommon:: ; 1523 (0:1523) - ld [$d11e],a - ld de,$d11e - ld b,$41 ; no leading zeroes, left-aligned, one byte - jp PrintNumber - -Func_152e:: ; 152e (0:152e) - ld hl,$d0dc - ld c,a - ld b,0 - add hl,bc - ld a,[hl] - ret - -; copies the base stat data of a pokemon to $D0B8 (W_MONHEADER) -; INPUT: -; [$D0B5] = pokemon ID -GetMonHeader:: ; 1537 (0:1537) - ld a,[H_LOADEDROMBANK] - push af - ld a,BANK(BaseStats) - ld [H_LOADEDROMBANK],a - ld [$2000],a - push bc - push de - push hl - ld a,[$d11e] - push af - ld a,[$d0b5] - ld [$d11e],a - ld de,FossilKabutopsPic - ld b,$66 ; size of Kabutops fossil and Ghost sprites - cp a,FOSSIL_KABUTOPS ; Kabutops fossil - jr z,.specialID - ld de,GhostPic - cp a,MON_GHOST ; Ghost - jr z,.specialID - ld de,FossilAerodactylPic - ld b,$77 ; size of Aerodactyl fossil sprite - cp a,FOSSIL_AERODACTYL ; Aerodactyl fossil - jr z,.specialID - cp a,MEW - jr z,.mew - ld a,$3a - call Predef ; convert pokemon ID in [$D11E] to pokedex number - ld a,[$d11e] - dec a - ld bc,28 - ld hl,BaseStats - call AddNTimes - ld de,W_MONHEADER - ld bc,28 - call CopyData - jr .done -.specialID - ld hl,W_MONHSPRITEDIM - ld [hl],b ; write sprite dimensions - inc hl - ld [hl],e ; write front sprite pointer - inc hl - ld [hl],d - jr .done -.mew - ld hl,MewBaseStats - ld de,W_MONHEADER - ld bc,28 - ld a,BANK(MewBaseStats) - call FarCopyData -.done - ld a,[$d0b5] - ld [$d0b8],a - pop af - ld [$d11e],a - pop hl - pop de - pop bc - pop af - ld [H_LOADEDROMBANK],a - ld [$2000],a - ret - -; copy party pokemon's name to $CD6D -GetPartyMonName2:: ; 15b4 (0:15b4) - ld a,[wWhichPokemon] ; index within party - ld hl,W_PARTYMON1NAME - -; this is called more often -GetPartyMonName:: ; 15ba (0:15ba) - push hl - push bc - call SkipFixedLengthTextEntries ; add 11 to hl, a times - ld de,$cd6d - push de - ld bc,11 - call CopyData - pop de - pop bc - pop hl - ret - -; function to print a BCD (Binary-coded decimal) number -; de = address of BCD number -; hl = destination address -; c = flags and length -; bit 7: if set, do not print leading zeroes -; if unset, print leading zeroes -; bit 6: if set, left-align the string (do not pad empty digits with spaces) -; if unset, right-align the string -; bit 5: if set, print currency symbol at the beginning of the string -; if unset, do not print the currency symbol -; bits 0-4: length of BCD number in bytes -; Note that bits 5 and 7 are modified during execution. The above reflects -; their meaning at the beginning of the functions's execution. -PrintBCDNumber:: ; 15cd (0:15cd) - ld b,c ; save flags in b - res 7,c - res 6,c - res 5,c ; c now holds the length - bit 5,b - jr z,.loop - bit 7,b - jr nz,.loop - ld [hl],"¥" - inc hl -.loop - ld a,[de] - swap a - call PrintBCDDigit ; print upper digit - ld a,[de] - call PrintBCDDigit ; print lower digit - inc de - dec c - jr nz,.loop - bit 7,b ; were any non-zero digits printed? - jr z,.done ; if so, we are done -.numberEqualsZero ; if every digit of the BCD number is zero - bit 6,b ; left or right alignment? - jr nz,.skipRightAlignmentAdjustment - dec hl ; if the string is right-aligned, it needs to be moved back one space -.skipRightAlignmentAdjustment - bit 5,b - jr z,.skipCurrencySymbol - ld [hl],"¥" - inc hl -.skipCurrencySymbol - ld [hl],"0" - call PrintLetterDelay - inc hl -.done - ret - -PrintBCDDigit:: ; 1604 (0:1604) - and a,%00001111 - and a - jr z,.zeroDigit -.nonzeroDigit - bit 7,b ; have any non-space characters been printed? - jr z,.outputDigit -; if bit 7 is set, then no numbers have been printed yet - bit 5,b ; print the currency symbol? - jr z,.skipCurrencySymbol - ld [hl],"¥" - inc hl - res 5,b -.skipCurrencySymbol - res 7,b ; unset 7 to indicate that a nonzero digit has been reached -.outputDigit - add a,"0" - ld [hli],a - jp PrintLetterDelay -.zeroDigit - bit 7,b ; either printing leading zeroes or already reached a nonzero digit? - jr z,.outputDigit ; if so, print a zero digit - bit 6,b ; left or right alignment? - ret nz - inc hl ; if right-aligned, "print" a space by advancing the pointer - ret - -; uncompresses the front or back sprite of the specified mon -; assumes the corresponding mon header is already loaded -; hl contains offset to sprite pointer ($b for front or $d for back) -UncompressMonSprite:: ; 1627 (0:1627) - ld bc,W_MONHEADER - add hl,bc - ld a,[hli] - ld [W_SPRITEINPUTPTR],a ; fetch sprite input pointer - ld a,[hl] - ld [W_SPRITEINPUTPTR+1],a -; define (by index number) the bank that a pokemon's image is in -; index = Mew, bank 1 -; index = Kabutops fossil, bank $B -; index < $1F, bank 9 -; $1F ≤ index < $4A, bank $A -; $4A ≤ index < $74, bank $B -; $74 ≤ index < $99, bank $C -; $99 ≤ index, bank $D - ld a,[$CF91] ; XXX name for this ram location - ld b,a - cp MEW - ld a,BANK(MewPicFront) - jr z,.GotBank - ld a,b - cp FOSSIL_KABUTOPS - ld a,BANK(FossilKabutopsPic) - jr z,.GotBank - ld a,b - cp TANGELA + 1 - ld a,BANK(TangelaPicFront) - jr c,.GotBank - ld a,b - cp MOLTRES + 1 - ld a,BANK(MoltresPicFront) - jr c,.GotBank - ld a,b - cp BEEDRILL + 2 - ld a,BANK(BeedrillPicFront) - jr c,.GotBank - ld a,b - cp STARMIE + 1 - ld a,BANK(StarmiePicFront) - jr c,.GotBank - ld a,BANK(VictreebelPicFront) -.GotBank - jp UncompressSpriteData - -; de: destination location -LoadMonFrontSprite:: ; 1665 (0:1665) - push de - ld hl, W_MONHFRONTSPRITE - W_MONHEADER - call UncompressMonSprite - ld hl, W_MONHSPRITEDIM - ld a, [hli] - ld c, a - pop de - ; fall through - -; postprocesses uncompressed sprite chunks to a 2bpp sprite and loads it into video ram -; calculates alignment parameters to place both sprite chunks in the center of the 7*7 tile sprite buffers -; de: destination location -; a,c: sprite dimensions (in tiles of 8x8 each) -LoadUncompressedSpriteData:: ; 1672 (0:1672) - push de - and $f - ld [H_SPRITEWIDTH], a ; each byte contains 8 pixels (in 1bpp), so tiles=bytes for width - ld b, a - ld a, $7 - sub b ; 7-w - inc a ; 8-w - srl a ; (8-w)/2 ; horizontal center (in tiles, rounded up) - ld b, a - add a - add a - add a - sub b ; 7*((8-w)/2) ; skip for horizontal center (in tiles) - ld [H_SPRITEOFFSET], a - ld a, c - swap a - and $f - ld b, a - add a - add a - add a ; 8*tiles is height in bytes - ld [H_SPRITEHEIGHT], a ; $ff8c - ld a, $7 - sub b ; 7-h ; skip for vertical center (in tiles, relative to current column) - ld b, a - ld a, [H_SPRITEOFFSET] - add b ; 7*((8-w)/2) + 7-h ; combined overall offset (in tiles) - add a - add a - add a ; 8*(7*((8-w)/2) + 7-h) ; combined overall offset (in bytes) - ld [H_SPRITEOFFSET], a - xor a - ld [$4000], a - ld hl, S_SPRITEBUFFER0 - call ZeroSpriteBuffer ; zero buffer 0 - ld de, S_SPRITEBUFFER1 - ld hl, S_SPRITEBUFFER0 - call AlignSpriteDataCentered ; copy and align buffer 1 to 0 (containing the MSB of the 2bpp sprite) - ld hl, S_SPRITEBUFFER1 - call ZeroSpriteBuffer ; zero buffer 1 - ld de, S_SPRITEBUFFER2 - ld hl, S_SPRITEBUFFER1 - call AlignSpriteDataCentered ; copy and align buffer 2 to 1 (containing the LSB of the 2bpp sprite) - pop de - jp InterlaceMergeSpriteBuffers - -; copies and aligns the sprite data properly inside the sprite buffer -; sprite buffers are 7*7 tiles in size, the loaded sprite is centered within this area -AlignSpriteDataCentered:: ; 16c2 (0:16c2) - ld a, [H_SPRITEOFFSET] - ld b, $0 - ld c, a - add hl, bc - ld a, [H_SPRITEWIDTH] ; $ff8b -.columnLoop - push af - push hl - ld a, [H_SPRITEHEIGHT] ; $ff8c - ld c, a -.columnInnerLoop - ld a, [de] - inc de - ld [hli], a - dec c - jr nz, .columnInnerLoop - pop hl - ld bc, 7*8 ; 7 tiles - add hl, bc ; advance one full column - pop af - dec a - jr nz, .columnLoop - ret - -; fills the sprite buffer (pointed to in hl) with zeros -ZeroSpriteBuffer:: ; 16df (0:16df) - ld bc, SPRITEBUFFERSIZE -.nextByteLoop - xor a - ld [hli], a - dec bc - ld a, b - or c - jr nz, .nextByteLoop - ret - -; combines the (7*7 tiles, 1bpp) sprite chunks in buffer 0 and 1 into a 2bpp sprite located in buffer 1 through 2 -; in the resulting sprite, the rows of the two source sprites are interlaced -; de: output address -InterlaceMergeSpriteBuffers:: ; 16ea (0:16ea) - xor a - ld [$4000], a - push de - ld hl, S_SPRITEBUFFER2 + (SPRITEBUFFERSIZE - 1) ; destination: end of buffer 2 - ld de, S_SPRITEBUFFER1 + (SPRITEBUFFERSIZE - 1) ; source 2: end of buffer 1 - ld bc, S_SPRITEBUFFER0 + (SPRITEBUFFERSIZE - 1) ; source 1: end of buffer 0 - ld a, SPRITEBUFFERSIZE/2 ; $c4 - ld [H_SPRITEINTERLACECOUNTER], a ; $ff8b -.interlaceLoop - ld a, [de] - dec de - ld [hld], a ; write byte of source 2 - ld a, [bc] - dec bc - ld [hld], a ; write byte of source 1 - ld a, [de] - dec de - ld [hld], a ; write byte of source 2 - ld a, [bc] - dec bc - ld [hld], a ; write byte of source 1 - ld a, [H_SPRITEINTERLACECOUNTER] ; $ff8b - dec a - ld [H_SPRITEINTERLACECOUNTER], a ; $ff8b - jr nz, .interlaceLoop - ld a, [W_SPRITEFLIPPED] - and a - jr z, .notFlipped - ld bc, 2*SPRITEBUFFERSIZE - ld hl, S_SPRITEBUFFER1 -.swapLoop - swap [hl] ; if flipped swap nybbles in all bytes - inc hl - dec bc - ld a, b - or c - jr nz, .swapLoop -.notFlipped - pop hl - ld de, S_SPRITEBUFFER1 - ld c, (2*SPRITEBUFFERSIZE)/16 ; $31, number of 16 byte chunks to be copied - ld a, [H_LOADEDROMBANK] - ld b, a - jp CopyVideoData - -Underground_Coll:: ; 172f (0:172f) - INCBIN "gfx/tilesets/underground.tilecoll" -Overworld_Coll:: ; 1735 (0:1735) - INCBIN "gfx/tilesets/overworld.tilecoll" -RedsHouse1_Coll:: -RedsHouse2_Coll:: ; 1749 (0:1749) - INCBIN "gfx/tilesets/reds_house.tilecoll" -Mart_Coll -Pokecenter_Coll:: ; 1753 (0:1753) - INCBIN "gfx/tilesets/pokecenter.tilecoll" -Dojo_Coll:: -Gym_Coll:: ; 1759 (0:1759) - INCBIN "gfx/tilesets/gym.tilecoll" -Forest_Coll:: ; 1765 (0:1765) - INCBIN "gfx/tilesets/forest.tilecoll" -House_Coll:: ; 1775 (0:1775) - INCBIN "gfx/tilesets/house.tilecoll" -ForestGate_Coll:: -Museum_Coll:: -Gate_Coll:: ; 177f (0:177f) - INCBIN "gfx/tilesets/gate.tilecoll" -Ship_Coll:: ; 178a (0:178a) - INCBIN "gfx/tilesets/ship.tilecoll" -ShipPort_Coll:: ; 1795 (0:1795) - INCBIN "gfx/tilesets/ship_port.tilecoll" -Cemetery_Coll:: ; 179a (0:179a) - INCBIN "gfx/tilesets/cemetery.tilecoll" -Interior_Coll:: ; 17a2 (0:17a2) - INCBIN "gfx/tilesets/interior.tilecoll" -Cavern_Coll:: ; 17ac (0:17ac) - INCBIN "gfx/tilesets/cavern.tilecoll" -Lobby_Coll:: ; 17b8 (0:17b8) - INCBIN "gfx/tilesets/lobby.tilecoll" -Mansion_Coll:: ; 17c0 (0:17c0) - INCBIN "gfx/tilesets/mansion.tilecoll" -Lab_Coll:: ; 17ca (0:17ca) - INCBIN "gfx/tilesets/lab.tilecoll" -Club_Coll:: ; 17d1 (0:17d1) - INCBIN "gfx/tilesets/club.tilecoll" -Facility_Coll:: ; 17dd (0:17dd) - INCBIN "gfx/tilesets/facility.tilecoll" -Plateau_Coll:: ; 17f0 (0:17f0) - INCBIN "gfx/tilesets/plateau.tilecoll" - -; does the same thing as FarCopyData at 009D -; only difference is that it uses [$ff8b] instead of [$cee9] for a temp value -; copy bc bytes of data from a:hl to de -FarCopyData2:: ; 17f7 (0:17f7) - ld [$ff8b],a - ld a,[H_LOADEDROMBANK] - push af - ld a,[$ff8b] - ld [H_LOADEDROMBANK],a - ld [$2000],a - call CopyData - pop af - ld [H_LOADEDROMBANK],a - ld [$2000],a - ret - -; does a far copy but the source is de and the destination is hl -; copy bc bytes of data from a:de to hl -FarCopyData3:: ; 180d (0:180d) - ld [$ff8b],a - ld a,[H_LOADEDROMBANK] - push af - ld a,[$ff8b] - ld [H_LOADEDROMBANK],a - ld [$2000],a - push hl - push de - push de - ld d,h - ld e,l - pop hl - call CopyData - pop de - pop hl - pop af - ld [H_LOADEDROMBANK],a - ld [$2000],a - ret - -; copies each source byte to the destination twice (next to each other) -; copy bc source bytes from a:hl to de -FarCopyDataDouble:: ; 182b (0:182b) - ld [$ff8b],a - ld a,[H_LOADEDROMBANK] - push af - ld a,[$ff8b] - ld [H_LOADEDROMBANK],a - ld [$2000],a -.loop - ld a,[hli] - ld [de],a - inc de - ld [de],a - inc de - dec bc - ld a,c - or b - jr nz,.loop - pop af - ld [H_LOADEDROMBANK],a - ld [$2000],a - ret - -; copy (c * 16) bytes from b:de to hl during V-blank -; transfers up to 128 bytes per V-blank -CopyVideoData:: ; 1848 (0:1848) - ld a,[H_AUTOBGTRANSFERENABLED] ; save auto-transfer enabled flag - push af - xor a - ld [H_AUTOBGTRANSFERENABLED],a ; disable auto-transfer while copying - ld a,[H_LOADEDROMBANK] - ld [$ff8b],a - ld a,b - ld [H_LOADEDROMBANK],a - ld [$2000],a - ld a,e - ld [H_VBCOPYSRC],a - ld a,d - ld [H_VBCOPYSRC + 1],a - ld a,l - ld [H_VBCOPYDEST],a - ld a,h - ld [H_VBCOPYDEST + 1],a -.loop - ld a,c - cp a,8 ; are there more than 128 bytes left to copy? - jr nc,.copyMaxSize ; only copy up to 128 bytes at a time -.copyRemainder - ld [H_VBCOPYSIZE],a - call DelayFrame ; wait for V-blank handler to perform the copy - ld a,[$ff8b] - ld [H_LOADEDROMBANK],a - ld [$2000],a - pop af - ld [H_AUTOBGTRANSFERENABLED],a ; restore original auto-transfer enabled flag - ret -.copyMaxSize - ld a,8 ; 128 bytes - ld [H_VBCOPYSIZE],a - call DelayFrame ; wait for V-blank handler to perform the copy - ld a,c - sub a,8 - ld c,a - jr .loop - -; copy (c * 8) source bytes from b:de to hl during V-blank -; copies each source byte to the destination twice (next to each other) -; transfers up to 64 source bytes per V-blank -CopyVideoDataDouble:: ; 1886 (0:1886) - ld a,[H_AUTOBGTRANSFERENABLED] ; save auto-transfer enabled flag - push af - xor a - ld [H_AUTOBGTRANSFERENABLED],a ; disable auto-transfer while copying - ld a,[H_LOADEDROMBANK] - ld [$ff8b],a - ld a,b - ld [H_LOADEDROMBANK],a - ld [$2000],a - ld a,e - ld [H_VBCOPYDOUBLESRC],a - ld a,d - ld [H_VBCOPYDOUBLESRC + 1],a - ld a,l - ld [H_VBCOPYDOUBLEDEST],a - ld a,h - ld [H_VBCOPYDOUBLEDEST + 1],a -.loop - ld a,c - cp a,8 ; are there more than 64 source bytes left to copy? - jr nc,.copyMaxSize ; only copy up to 64 source bytes at a time -.copyRemainder - ld [H_VBCOPYDOUBLESIZE],a - call DelayFrame ; wait for V-blank handler to perform the copy - ld a,[$ff8b] - ld [H_LOADEDROMBANK],a - ld [$2000],a - pop af - ld [H_AUTOBGTRANSFERENABLED],a ; restore original auto-transfer enabled flag - ret -.copyMaxSize - ld a,8 ; 64 source bytes - ld [H_VBCOPYDOUBLESIZE],a - call DelayFrame ; wait for V-blank handler to perform the copy - ld a,c - sub a,8 - ld c,a - jr .loop - -; clears an area of the screen -; INPUT: -; hl = address of upper left corner of the area -; b = height -; c = width -ClearScreenArea:: ; 18c4 (0:18c4) - ld a,$7F ; blank tile - ld de,20 ; screen width -.loop - push hl - push bc -.innerLoop - ld [hli],a - dec c - jr nz,.innerLoop - pop bc - pop hl - add hl,de - dec b - jr nz,.loop - ret - -; copies the screen tile buffer from WRAM to VRAM -; copying is done in 3 chunks of 6 rows each -; b: high byte of VRAM destination address ($98 or $9c for window tile map 0 or 1 resp.) -CopyScreenTileBufferToVRAM:: ; 18d6 (0:18d6) - ld c, $6 - ld hl, $0000 - ld de, wTileMap - call InitScreenTileBufferTransferParameters - call DelayFrame - ld hl, $600 - ld de, wTileMap + 20 * 6 ; $c418 - call InitScreenTileBufferTransferParameters - call DelayFrame - ld hl, $c00 - ld de, wTileMap + 20 * 12 ; $c490 - call InitScreenTileBufferTransferParameters - jp DelayFrame - -InitScreenTileBufferTransferParameters:: ; 18fc (0:18fc) - ld a, d - ld [H_VBCOPYBGSRC+1], a - call GetRowColAddressBgMap - ld a, l - ld [H_VBCOPYBGDEST], a ; $ffc3 - ld a, h - ld [H_VBCOPYBGDEST+1], a - ld a, c - ld [H_VBCOPYBGNUMROWS], a ; $ffc5 - ld a, e - ld [H_VBCOPYBGSRC], a ; $ffc1 - ret - -ClearScreen:: ; 190f (0:190f) -; clears all tiles in the tilemap, -; then wait three frames - ld bc,$0168 ; tilemap size - inc b - ld hl,wTileMap ; TILEMAP_START - ld a,$7F ; $7F is blank tile -.loop - ld [hli],a - dec c - jr nz,.loop - dec b - jr nz,.loop - jp Delay3 - -TextBoxBorder:: ; 1922 (0:1922) -; draw a text box -; upper-left corner at coordinates hl -; height b -; width c - - ; first row - push hl - ld a,"┌" - ld [hli],a - inc a ; horizontal border ─ - call NPlaceChar - inc a ; upper-right border ┐ - ld [hl],a - - ; middle rows - pop hl - ld de,20 - add hl,de ; skip the top row - -.PlaceRow - push hl - ld a,"│" - ld [hli],a - ld a," " - call NPlaceChar - ld [hl],"│" - - pop hl - ld de,20 - add hl,de ; move to next row - dec b - jr nz,.PlaceRow - - ; bottom row - ld a,"└" - ld [hli],a - ld a,"─" - call NPlaceChar - ld [hl],"┘" - ret -; -NPlaceChar:: ; 194f (0:194f) -; place a row of width c of identical characters - ld d,c -.loop - ld [hli],a - dec d - jr nz,.loop - ret - -PlaceString:: ; 1955 (0:1955) - push hl -PlaceNextChar:: ; 1956 (0:1956) - ld a,[de] - - cp "@" - jr nz,.PlaceText - ld b,h - ld c,l - pop hl - ret - -.PlaceText - cp $4E - jr nz,.next - ld bc,$0028 - ld a,[$FFF6] - bit 2,a - jr z,.next2 - ld bc,$14 -.next2 - pop hl - add hl,bc - push hl - jp Next19E8 - -.next - cp $4F - jr nz,.next3 - pop hl - FuncCoord 1, 16 ; $c4e1 - ld hl,Coord - push hl - jp Next19E8 - -.next3 ; Check against a dictionary - and a - jp z,Char00 - cp $4C - jp z,Char4C - cp $4B - jp z,Char4B - cp $51 - jp z,Char51 - cp $49 - jp z,Char49 - cp $52 - jp z,Char52 - cp $53 - jp z,Char53 - cp $54 - jp z,Char54 - cp $5B - jp z,Char5B - cp $5E - jp z,Char5E - cp $5C - jp z,Char5C - cp $5D - jp z,Char5D - cp $55 - jp z,Char55 - cp $56 - jp z,Char56 - cp $57 - jp z,Char57 - cp $58 - jp z,Char58 - cp $4A - jp z,Char4A - cp $5F - jp z,Char5F - cp $59 - jp z,Char59 - cp $5A - jp z,Char5A - ld [hli],a - call PrintLetterDelay -Next19E8:: ; 19e8 (0:19e8) - inc de - jp PlaceNextChar - -Char00:: ; 19ec (0:19ec) - ld b,h - ld c,l - pop hl - ld de,Char00Text - dec de - ret - -Char00Text:: ; 0x19f4 “%d ERROR.” - TX_FAR _Char00Text - db "@" - -Char52:: ; 0x19f9 player’s name - push de - ld de,W_PLAYERNAME - jr FinishDTE - -Char53:: ; 19ff (0:19ff) ; rival’s name - push de - ld de,W_RIVALNAME - jr FinishDTE - -Char5D:: ; 1a05 (0:1a05) ; TRAINER - push de - ld de,Char5DText - jr FinishDTE - -Char5C:: ; 1a0b (0:1a0b) ; TM - push de - ld de,Char5CText - jr FinishDTE - -Char5B:: ; 1a11 (0:1a11) ; PC - push de - ld de,Char5BText - jr FinishDTE - -Char5E:: ; 1a17 (0:1a17) ; ROCKET - push de - ld de,Char5EText - jr FinishDTE - -Char54:: ; 1a1d (0:1a1d) ; POKé - push de - ld de,Char54Text - jr FinishDTE - -Char56:: ; 1a23 (0:1a23) ; …… - push de - ld de,Char56Text - jr FinishDTE - -Char4A:: ; 1a29 (0:1a29) ; PKMN - push de - ld de,Char4AText - jr FinishDTE - -Char59:: ; 1a2f (0:1a2f) -; depending on whose turn it is, print -; enemy active monster’s name, prefixed with “Enemy ” -; or -; player active monster’s name -; (like Char5A but flipped) - ld a,[H_WHOSETURN] - xor 1 - jr MonsterNameCharsCommon - -Char5A:: ; 1a35 (0:1a35) -; depending on whose turn it is, print -; player active monster’s name -; or -; enemy active monster’s name, prefixed with “Enemy ” - ld a,[H_WHOSETURN] -MonsterNameCharsCommon:: ; 1a37 (0:1a37) - push de - and a - jr nz,.Enemy - ld de,W_PLAYERMONNAME ; player active monster name - jr FinishDTE - -.Enemy ; 1A40 - ; print “Enemy ” - ld de,Char5AText - call PlaceString - - ld h,b - ld l,c - ld de,W_ENEMYMONNAME ; enemy active monster name - -FinishDTE:: ; 1a4b (0:1a4b) - call PlaceString - ld h,b - ld l,c - pop de - inc de - jp PlaceNextChar - -Char5CText:: ; 1a55 (0:1a55) - db "TM@" -Char5DText:: ; 1a58 (0:1a58) - db "TRAINER@" -Char5BText:: ; 1a60 (0:1a60) - db "PC@" -Char5EText:: ; 1a63 (0:1a63) - db "ROCKET@" -Char54Text:: ; 1a6a (0:1a6a) - db "POKé@" -Char56Text:: ; 1a6f (0:1a6f) - db "……@" -Char5AText:: ; 1a72 (0:1a72) - db "Enemy @" -Char4AText:: ; 1a79 (0:1a79) - db $E1,$E2,"@" ; PKMN - -Char55:: ; 1a7c (0:1a7c) - push de - ld b,h - ld c,l - ld hl,Char55Text - call TextCommandProcessor - ld h,b - ld l,c - pop de - inc de - jp PlaceNextChar - -Char55Text:: ; 1a8c (0:1a8c) -; equivalent to Char4B - TX_FAR _Char55Text - db "@" - -Char5F:: ; 1a91 (0:1a91) -; ends a Pokédex entry - ld [hl],"." - pop hl - ret - -Char58:: ; 1a95 (0:1a95) - ld a,[$D12B] - cp 4 - jp z,Next1AA2 - ld a,$EE - FuncCoord 18, 16 ; $c4f2 - ld [Coord],a -Next1AA2:: ; 1aa2 (0:1aa2) - call ProtectedDelay3 - call ManualTextScroll - ld a,$7F - FuncCoord 18, 16 ; $c4f2 - ld [Coord],a -Char57:: ; 1aad (0:1aad) - pop hl - ld de,Char58Text - dec de - ret - -Char58Text:: ; 1ab3 (0:1ab3) - db "@" - -Char51:: ; 1ab4 (0:1ab4) - push de - ld a,$EE - FuncCoord 18, 16 ; $c4f2 - ld [Coord],a - call ProtectedDelay3 - call ManualTextScroll - FuncCoord 1, 13 ; $c4a5 - ld hl,Coord - ld bc,$0412 - call ClearScreenArea - ld c,$14 - call DelayFrames - pop de - FuncCoord 1, 14 ; $c4b9 - ld hl,Coord - jp Next19E8 - -Char49:: ; 1ad5 (0:1ad5) - push de - ld a,$EE - FuncCoord 18, 16 ; $c4f2 - ld [Coord],a - call ProtectedDelay3 - call ManualTextScroll - FuncCoord 1, 10 ; $c469 - ld hl,Coord - ld bc,$0712 - call ClearScreenArea - ld c,$14 - call DelayFrames - pop de - pop hl - FuncCoord 1, 11 ; $c47d - ld hl,Coord - push hl - jp Next19E8 - -Char4B:: ; 1af8 (0:1af8) - ld a,$EE - FuncCoord 18, 16 ; $c4f2 - ld [Coord],a - call ProtectedDelay3 - push de - call ManualTextScroll - pop de - ld a,$7F - FuncCoord 18, 16 ; $c4f2 - ld [Coord],a - ;fall through -Char4C:: ; 1b0a (0:1b0a) - push de - call Next1B18 - call Next1B18 - FuncCoord 1, 16 ; $c4e1 - ld hl,Coord - pop de - jp Next19E8 - -Next1B18:: ; 1b18 (0:1b18) - FuncCoord 0, 14 ; $c4b8 - ld hl,Coord - FuncCoord 0, 13 ; $c4a4 - ld de,Coord - ld b,$3C -.next - ld a,[hli] - ld [de],a - inc de - dec b - jr nz,.next - FuncCoord 1, 16 ; $c4e1 - ld hl,Coord - ld a,$7F - ld b,$12 -.next2 - ld [hli],a - dec b - jr nz,.next2 - - ; wait five frames - ld b,5 -.WaitFrame - call DelayFrame - dec b - jr nz,.WaitFrame - - ret - -ProtectedDelay3:: ; 1b3a (0:1b3a) - push bc - call Delay3 - pop bc - ret - -TextCommandProcessor:: ; 1b40 (0:1b40) - ld a,[$d358] - push af - set 1,a - ld e,a - ld a,[$fff4] - xor e - ld [$d358],a - ld a,c - ld [$cc3a],a - ld a,b - ld [$cc3b],a - -NextTextCommand:: ; 1b55 (0:1b55) - ld a,[hli] - cp a, "@" ; terminator - jr nz,.doTextCommand - pop af - ld [$d358],a - ret -.doTextCommand - push hl - cp a,$17 - jp z,TextCommand17 - cp a,$0e - jp nc,TextCommand0B ; if a != 0x17 and a >= 0xE, go to command 0xB -; if a < 0xE, use a jump table - ld hl,TextCommandJumpTable - push bc - add a - ld b,$00 - ld c,a - add hl,bc - pop bc - ld a,[hli] - ld h,[hl] - ld l,a - jp [hl] - -; draw box -; 04AAAABBCC -; AAAA = address of upper left corner -; BB = height -; CC = width -TextCommand04:: ; 1b78 (0:1b78) - pop hl - ld a,[hli] - ld e,a - ld a,[hli] - ld d,a - ld a,[hli] - ld b,a - ld a,[hli] - ld c,a - push hl - ld h,d - ld l,e - call TextBoxBorder - pop hl - jr NextTextCommand - -; place string inline -; 00{string} -TextCommand00:: ; 1b8a (0:1b8a) - pop hl - ld d,h - ld e,l - ld h,b - ld l,c - call PlaceString - ld h,d - ld l,e - inc hl - jr NextTextCommand - -; place string from RAM -; 01AAAA -; AAAA = address of string -TextCommand01:: ; 1b97 (0:1b97) - pop hl - ld a,[hli] - ld e,a - ld a,[hli] - ld d,a - push hl - ld h,b - ld l,c - call PlaceString - pop hl - jr NextTextCommand - -; print BCD number -; 02AAAABB -; AAAA = address of BCD number -; BB -; bits 0-4 = length in bytes -; bits 5-7 = unknown flags -TextCommand02:: ; 1ba5 (0:1ba5) - pop hl - ld a,[hli] - ld e,a - ld a,[hli] - ld d,a - ld a,[hli] - push hl - ld h,b - ld l,c - ld c,a - call PrintBCDNumber - ld b,h - ld c,l - pop hl - jr NextTextCommand - -; repoint destination address -; 03AAAA -; AAAA = new destination address -TextCommand03:: ; 1bb7 (0:1bb7) - pop hl - ld a,[hli] - ld [$cc3a],a - ld c,a - ld a,[hli] - ld [$cc3b],a - ld b,a - jp NextTextCommand - -; repoint destination to second line of dialogue text box -; 05 -; (no arguments) -TextCommand05:: ; 1bc5 (0:1bc5) - pop hl - FuncCoord 1, 16 ; $c4e1 - ld bc,Coord ; address of second line of dialogue text box - jp NextTextCommand - -; blink arrow and wait for A or B to be pressed -; 06 -; (no arguments) -TextCommand06:: ; 1bcc (0:1bcc) - ld a,[W_ISLINKBATTLE] - cp a,$04 - jp z,TextCommand0D - ld a,$ee ; down arrow - FuncCoord 18, 16 ; $c4f2 - ld [Coord],a ; place down arrow in lower right corner of dialogue text box - push bc - call ManualTextScroll ; blink arrow and wait for A or B to be pressed - pop bc - ld a," " - FuncCoord 18, 16 ; $c4f2 - ld [Coord],a ; overwrite down arrow with blank space - pop hl - jp NextTextCommand - -; scroll text up one line -; 07 -; (no arguments) -TextCommand07:: ; 1be7 (0:1be7) - ld a," " - FuncCoord 18, 16 ; $c4f2 - ld [Coord],a ; place blank space in lower right corner of dialogue text box - call Next1B18 ; scroll up text - call Next1B18 - pop hl - FuncCoord 1, 16 ; $c4e1 - ld bc,Coord ; address of second line of dialogue text box - jp NextTextCommand - -; execute asm inline -; 08{code} -TextCommand08:: ; 1bf9 (0:1bf9) - pop hl - ld de,NextTextCommand - push de ; return address - jp [hl] - -; print decimal number (converted from binary number) -; 09AAAABB -; AAAA = address of number -; BB -; bits 0-3 = how many digits to display -; bits 4-7 = how long the number is in bytes -TextCommand09:: ; 1bff (0:1bff) - pop hl - ld a,[hli] - ld e,a - ld a,[hli] - ld d,a - ld a,[hli] - push hl - ld h,b - ld l,c - ld b,a - and a,$0f - ld c,a - ld a,b - and a,$f0 - swap a - set 6,a - ld b,a - call PrintNumber - ld b,h - ld c,l - pop hl - jp NextTextCommand - -; wait half a second if the user doesn't hold A or B -; 0A -; (no arguments) -TextCommand0A:: ; 1c1d (0:1c1d) - push bc - call GetJoypadState - ld a,[H_CURRENTPRESSEDBUTTONS] - and a,%00000011 ; A and B buttons - jr nz,.skipDelay - ld c,30 - call DelayFrames -.skipDelay - pop bc - pop hl - jp NextTextCommand - -; plays sounds -; this actually handles various command ID's, not just 0B -; (no arguments) -TextCommand0B:: ; 1c31 (0:1c31) - pop hl - push bc - dec hl - ld a,[hli] - ld b,a ; b = command number that got us here - push hl - ld hl,TextCommandSounds -.loop - ld a,[hli] - cp b - jr z,.matchFound - inc hl - jr .loop -.matchFound - cp a,$14 - jr z,.pokemonCry - cp a,$15 - jr z,.pokemonCry - cp a,$16 - jr z,.pokemonCry - ld a,[hl] - call PlaySound - call WaitForSoundToFinish - pop hl - pop bc - jp NextTextCommand -.pokemonCry - push de - ld a,[hl] - call PlayCry - pop de - pop hl - pop bc - jp NextTextCommand - -; format: text command ID, sound ID or cry ID -TextCommandSounds:: ; 1c64 (0:1c64) - db $0B,(SFX_02_3a - SFX_Headers_02) / 3 - db $12,(SFX_02_46 - SFX_Headers_02) / 3 - db $0E,(SFX_02_41 - SFX_Headers_02) / 3 - db $0F,(SFX_02_3a - SFX_Headers_02) / 3 - db $10,(SFX_02_3b - SFX_Headers_02) / 3 - db $11,(SFX_02_42 - SFX_Headers_02) / 3 - db $13,(SFX_02_44 - SFX_Headers_02) / 3 - db $14,NIDORINA ; used in OakSpeech - db $15,PIDGEOT ; used in SaffronCityText12 - db $16,DEWGONG ; unused? - -; draw ellipses -; 0CAA -; AA = number of ellipses to draw -TextCommand0C:: ; 1c78 (0:1c78) - pop hl - ld a,[hli] - ld d,a - push hl - ld h,b - ld l,c -.loop - ld a,$75 ; ellipsis - ld [hli],a - push de - call GetJoypadState - pop de - ld a,[H_CURRENTPRESSEDBUTTONS] ; joypad state - and a,%00000011 ; is A or B button pressed? - jr nz,.skipDelay ; if so, skip the delay - ld c,10 - call DelayFrames -.skipDelay - dec d - jr nz,.loop - ld b,h - ld c,l - pop hl - jp NextTextCommand - -; wait for A or B to be pressed -; 0D -; (no arguments) -TextCommand0D:: ; 1c9a (0:1c9a) - push bc - call ManualTextScroll ; wait for A or B to be pressed - pop bc - pop hl - jp NextTextCommand - -; process text commands in another ROM bank -; 17AAAABB -; AAAA = address of text commands -; BB = bank -TextCommand17:: ; 1ca3 (0:1ca3) - pop hl - ld a,[H_LOADEDROMBANK] - push af - ld a,[hli] - ld e,a - ld a,[hli] - ld d,a - ld a,[hli] - ld [H_LOADEDROMBANK],a - ld [$2000],a - push hl - ld l,e - ld h,d - call TextCommandProcessor - pop hl - pop af - ld [H_LOADEDROMBANK],a - ld [$2000],a - jp NextTextCommand - -TextCommandJumpTable:: ; 1cc1 (0:1cc1) - dw TextCommand00 - dw TextCommand01 - dw TextCommand02 - dw TextCommand03 - dw TextCommand04 - dw TextCommand05 - dw TextCommand06 - dw TextCommand07 - dw TextCommand08 - dw TextCommand09 - dw TextCommand0A - dw TextCommand0B - dw TextCommand0C - dw TextCommand0D - -; this function seems to be used only once -; it store the address of a row and column of the VRAM background map in hl -; INPUT: h - row, l - column, b - high byte of background tile map address in VRAM -GetRowColAddressBgMap:: ; 1cdd (0:1cdd) - xor a - srl h - rr a - srl h - rr a - srl h - rr a - or l - ld l,a - ld a,b - or h - ld h,a - ret - -; clears a VRAM background map with blank space tiles -; INPUT: h - high byte of background tile map address in VRAM -ClearBgMap:: ; 1cf0 (0:1cf0) - ld a," " - jr .next - ld a,l -.next - ld de,$400 ; size of VRAM background map - ld l,e -.loop - ld [hli],a - dec e - jr nz,.loop - dec d - jr nz,.loop - ret - -; When the player takes a step, a row or column of 2x2 tile blocks at the edge -; of the screen toward which they moved is exposed and has to be redrawn. -; This function does the redrawing. -RedrawExposedScreenEdge:: ; 1d01 (0:1d01) - ld a,[H_SCREENEDGEREDRAW] - and a - ret z - ld b,a - xor a - ld [H_SCREENEDGEREDRAW],a - dec b - jr nz,.redrawRow -.redrawColumn - ld hl,wScreenEdgeTiles - ld a,[H_SCREENEDGEREDRAWADDR] - ld e,a - ld a,[H_SCREENEDGEREDRAWADDR + 1] - ld d,a - ld c,18 ; screen height -.loop1 - ld a,[hli] - ld [de],a - inc de - ld a,[hli] - ld [de],a - ld a,31 - add e - ld e,a - jr nc,.noCarry - inc d -.noCarry -; the following 4 lines wrap us from bottom to top if necessary - ld a,d - and a,$03 - or a,$98 - ld d,a - dec c - jr nz,.loop1 - xor a - ld [H_SCREENEDGEREDRAW],a - ret -.redrawRow - ld hl,wScreenEdgeTiles - ld a,[H_SCREENEDGEREDRAWADDR] - ld e,a - ld a,[H_SCREENEDGEREDRAWADDR + 1] - ld d,a - push de - call .drawHalf ; draw upper half - pop de - ld a,32 ; width of VRAM background map - add e - ld e,a - ; draw lower half -.drawHalf - ld c,10 -.loop2 - ld a,[hli] - ld [de],a - inc de - ld a,[hli] - ld [de],a - ld a,e - inc a -; the following 6 lines wrap us from the right edge to the left edge if necessary - and a,$1f - ld b,a - ld a,e - and a,$e0 - or b - ld e,a - dec c - jr nz,.loop2 - ret - -; This function automatically transfers tile number data from the tile map at -; wTileMap to VRAM during V-blank. Note that it only transfers one third of the -; background per V-blank. It cycles through which third it draws. -; This transfer is turned off when walking around the map, but is turned -; on when talking to sprites, battling, using menus, etc. This is because -; the above function, RedrawExposedScreenEdge, is used when walking to -; improve efficiency. -AutoBgMapTransfer:: ; 1d57 (0:1d57) - ld a,[H_AUTOBGTRANSFERENABLED] - and a - ret z - ld hl,[sp + 0] - ld a,h - ld [H_SPTEMP],a - ld a,l - ld [H_SPTEMP + 1],a ; save stack pinter - ld a,[H_AUTOBGTRANSFERPORTION] - and a - jr z,.transferTopThird - dec a - jr z,.transferMiddleThird -.transferBottomThird - FuncCoord 0,12 - ld hl,Coord - ld sp,hl - ld a,[H_AUTOBGTRANSFERDEST + 1] - ld h,a - ld a,[H_AUTOBGTRANSFERDEST] - ld l,a - ld de,(12 * 32) - add hl,de - xor a ; TRANSFERTOP - jr .doTransfer -.transferTopThird - FuncCoord 0,0 - ld hl,Coord - ld sp,hl - ld a,[H_AUTOBGTRANSFERDEST + 1] - ld h,a - ld a,[H_AUTOBGTRANSFERDEST] - ld l,a - ld a,TRANSFERMIDDLE - jr .doTransfer -.transferMiddleThird - FuncCoord 0,6 - ld hl,Coord - ld sp,hl - ld a,[H_AUTOBGTRANSFERDEST + 1] - ld h,a - ld a,[H_AUTOBGTRANSFERDEST] - ld l,a - ld de,(6 * 32) - add hl,de - ld a,TRANSFERBOTTOM -.doTransfer - ld [H_AUTOBGTRANSFERPORTION],a ; store next portion - ld b,6 - -; unrolled loop and using pop for speed -TransferBgRows:: ; 1d9e (0:1d9e) - pop de - ld [hl],e - inc l - ld [hl],d - inc l - pop de - ld [hl],e - inc l - ld [hl],d - inc l - pop de - ld [hl],e - inc l - ld [hl],d - inc l - pop de - ld [hl],e - inc l - ld [hl],d - inc l - pop de - ld [hl],e - inc l - ld [hl],d - inc l - pop de - ld [hl],e - inc l - ld [hl],d - inc l - pop de - ld [hl],e - inc l - ld [hl],d - inc l - pop de - ld [hl],e - inc l - ld [hl],d - inc l - pop de - ld [hl],e - inc l - ld [hl],d - inc l - pop de - ld [hl],e - inc l - ld [hl],d - ld a,13 - add l - ld l,a - jr nc,.noCarry - inc h -.noCarry - dec b - jr nz,TransferBgRows - ld a,[H_SPTEMP] - ld h,a - ld a,[H_SPTEMP + 1] - ld l,a - ld sp,hl ; restore stack pointer - ret - -; Copies [H_VBCOPYBGNUMROWS] rows from H_VBCOPYBGSRC to H_VBCOPYBGDEST. -; If H_VBCOPYBGSRC is XX00, the transfer is disabled. -VBlankCopyBgMap:: ; 1de1 (0:1de1) - ld a,[H_VBCOPYBGSRC] ; doubles as enabling byte - and a - ret z - ld hl,[sp + 0] - ld a,h - ld [H_SPTEMP],a - ld a,l - ld [H_SPTEMP + 1],a ; save stack pointer - ld a,[H_VBCOPYBGSRC] - ld l,a - ld a,[H_VBCOPYBGSRC + 1] - ld h,a - ld sp,hl - ld a,[H_VBCOPYBGDEST] - ld l,a - ld a,[H_VBCOPYBGDEST + 1] - ld h,a - ld a,[H_VBCOPYBGNUMROWS] - ld b,a - xor a - ld [H_VBCOPYBGSRC],a ; disable transfer so it doesn't continue next V-blank - jr TransferBgRows - - -VBlankCopyDouble:: -; Copy [H_VBCOPYDOUBLESIZE] 1bpp tiles -; from H_VBCOPYDOUBLESRC to H_VBCOPYDOUBLEDEST. - -; While we're here, convert to 2bpp. -; The process is straightforward: -; copy each byte twice. - - ld a, [H_VBCOPYDOUBLESIZE] - and a - ret z - - ld hl, [sp + 0] - ld a, h - ld [H_SPTEMP], a - ld a, l - ld [H_SPTEMP + 1], a - - ld a, [H_VBCOPYDOUBLESRC] - ld l, a - ld a, [H_VBCOPYDOUBLESRC + 1] - ld h, a - ld sp, hl - - ld a, [H_VBCOPYDOUBLEDEST] - ld l, a - ld a, [H_VBCOPYDOUBLEDEST + 1] - ld h, a - - ld a, [H_VBCOPYDOUBLESIZE] - ld b, a - xor a ; transferred - ld [H_VBCOPYDOUBLESIZE], a - -.loop - rept 3 - pop de - ld [hl], e - inc l - ld [hl], e - inc l - ld [hl], d - inc l - ld [hl], d - inc l - endr - - pop de - ld [hl], e - inc l - ld [hl], e - inc l - ld [hl], d - inc l - ld [hl], d - inc hl - dec b - jr nz, .loop - - ld a, l - ld [H_VBCOPYDOUBLEDEST], a - ld a, h - ld [H_VBCOPYDOUBLEDEST + 1], a - - ld hl, [sp + 0] - ld a, l - ld [H_VBCOPYDOUBLESRC], a - ld a, h - ld [H_VBCOPYDOUBLESRC + 1], a - - ld a, [H_SPTEMP] - ld h, a - ld a, [H_SPTEMP + 1] - ld l, a - ld sp, hl - - ret - - -VBlankCopy:: -; Copy [H_VBCOPYSIZE] 2bpp tiles -; from H_VBCOPYSRC to H_VBCOPYDEST. - -; Source and destination addresses -; are updated, so transfer can -; continue in subsequent calls. - - ld a, [H_VBCOPYSIZE] - and a - ret z - - ld hl, [sp + 0] - ld a, h - ld [H_SPTEMP], a - ld a, l - ld [H_SPTEMP + 1], a - - ld a, [H_VBCOPYSRC] - ld l, a - ld a, [H_VBCOPYSRC + 1] - ld h, a - ld sp, hl - - ld a, [H_VBCOPYDEST] - ld l, a - ld a, [H_VBCOPYDEST + 1] - ld h, a - - ld a, [H_VBCOPYSIZE] - ld b, a - xor a ; transferred - ld [H_VBCOPYSIZE], a - -.loop - rept 7 - pop de - ld [hl], e - inc l - ld [hl], d - inc l - endr - - pop de - ld [hl], e - inc l - ld [hl], d - inc hl - dec b - jr nz, .loop - - ld a, l - ld [H_VBCOPYDEST], a - ld a, h - ld [H_VBCOPYDEST + 1], a - - ld hl, [sp + 0] - ld a, l - ld [H_VBCOPYSRC], a - ld a, h - ld [H_VBCOPYSRC + 1], a - - ld a, [H_SPTEMP] - ld h, a - ld a, [H_SPTEMP + 1] - ld l, a - ld sp, hl - - ret - - -UpdateMovingBgTiles:: -; Animate water and flower -; tiles in the overworld. - - ld a, [$ffd7] - and a - ret z - - ld a, [$ffd8] - inc a - ld [$ffd8], a - cp 20 - ret c - cp 21 - jr z, .flower - - ld hl, $9140 - ld c, $10 - - ld a, [$d085] - inc a - and 7 - ld [$d085], a - - and 4 - jr nz, .left -.right - ld a, [hl] - rrca - ld [hli], a - dec c - jr nz, .right - jr .done -.left - ld a, [hl] - rlca - ld [hli], a - dec c - jr nz, .left -.done - ld a, [$ffd7] - rrca - ret nc - xor a - ld [$ffd8], a - ret - -.flower - xor a - ld [$ffd8], a - - ld a, [$d085] - and 3 - cp 2 - ld hl, FlowerTile1 - jr c, .copy - ld hl, FlowerTile2 - jr z, .copy - ld hl, FlowerTile3 -.copy - ld de, $9030 - ld c, $10 -.loop - ld a, [hli] - ld [de], a - inc de - dec c - jr nz, .loop - ret - -FlowerTile1: INCBIN "gfx/tilesets/flower/flower1.2bpp" -FlowerTile2: INCBIN "gfx/tilesets/flower/flower2.2bpp" -FlowerTile3: INCBIN "gfx/tilesets/flower/flower3.2bpp" - - -SoftReset:: - call StopAllSounds - call GBPalWhiteOut - ld c, $20 - call DelayFrames - ; fallthrough - -Init:: -; Program init. - -rLCDC_DEFAULT EQU %11100011 -; * LCD enabled -; * Window tile map at $9C00 -; * Window display enabled -; * BG and window tile data at $8800 -; * BG tile map at $9800 -; * 8x8 OBJ size -; * OBJ display enabled -; * BG display enabled - - di - - xor a - ld [rIF], a - ld [rIE], a - ld [$ff43], a - ld [$ff42], a - ld [$ff01], a - ld [$ff02], a - ld [$ff4b], a - ld [$ff4a], a - ld [$ff06], a - ld [$ff07], a - ld [$ff47], a - ld [$ff48], a - ld [$ff49], a - - ld a, rLCDC_ENABLE_MASK - ld [rLCDC], a - call DisableLCD - - ld sp, wStack - - ld hl, $c000 ; start of WRAM - ld bc, $2000 ; size of WRAM -.loop - ld [hl], 0 - inc hl - dec bc - ld a, b - or c - jr nz, .loop - - call ClearVram - - ld hl, $ff80 - ld bc, $ffff - $ff80 - call FillMemory - - call ClearSprites - - ld a, Bank(WriteDMACodeToHRAM) - ld [H_LOADEDROMBANK], a - ld [MBC3RomBank], a - call WriteDMACodeToHRAM - - xor a - ld [$ffd7], a - ld [$ff41], a - ld [$ffae], a - ld [$ffaf], a - ld [$ff0f], a - ld a, 1 << VBLANK + 1 << TIMER + 1 << SERIAL - ld [rIE], a - - ld a, 144 ; move the window off-screen - ld [$ffb0], a - ld [rWY], a - ld a, 7 - ld [rWX], a - - ld a, $ff - ld [$ffaa], a - - ld h, $9800 / $100 ; bg map 0 - call ClearBgMap - ld h, $9c00 / $100 ; bg map 1 - call ClearBgMap - - ld a, rLCDC_DEFAULT - ld [rLCDC], a - ld a, $10 - ld [H_SOFTRESETCOUNTER], a - call StopAllSounds - - ei - - ld a, $40 ; PREDEF_SGB_BORDER - call Predef - - ld a, $1f - ld [$c0ef], a - ld [$c0f0], a - ld a, $9c - ld [$ffbd], a - xor a - ld [$ffbc], a - dec a - ld [$cfcb], a - - ld a, $32 ; PREDEF_INTRO - call Predef - - call DisableLCD - call ClearVram - call GBPalNormal - call ClearSprites - ld a, rLCDC_DEFAULT - ld [rLCDC], a - - jp SetDefaultNamesBeforeTitlescreen - -ClearVram: - ld hl, $8000 - ld bc, $2000 - xor a - jp FillMemory - - -StopAllSounds:: - ld a, Bank(Func_9876) - ld [$c0ef], a - ld [$c0f0], a - xor a - ld [wMusicHeaderPointer], a - ld [$c0ee], a - ld [$cfca], a - dec a - jp PlaySound - - -VBlank:: - - push af - push bc - push de - push hl - - ld a, [H_LOADEDROMBANK] - ld [$d122], a - - ld a, [$ffae] - ld [rSCX], a - ld a, [$ffaf] - ld [rSCY], a - - ld a, [$d0a0] - and a - jr nz, .ok - ld a, [$ffb0] - ld [rWY], a -.ok - - call AutoBgMapTransfer - call VBlankCopyBgMap - call RedrawExposedScreenEdge - call VBlankCopy - call VBlankCopyDouble - call UpdateMovingBgTiles - call $ff80 ; hOAMDMA - ld a, Bank(PrepareOAMData) - ld [H_LOADEDROMBANK], a - ld [MBC3RomBank], a - call PrepareOAMData - - ; VBlank-sensitive operations end. - - call Random - - ld a, [H_VBLANKOCCURRED] - and a - jr z, .vblanked - xor a - ld [H_VBLANKOCCURRED], a -.vblanked - - ld a, [H_FRAMECOUNTER] - and a - jr z, .decced - dec a - ld [H_FRAMECOUNTER], a -.decced - - call Func_28cb - - ld a, [$c0ef] ; music ROM bank - ld [H_LOADEDROMBANK], a - ld [MBC3RomBank], a - - cp BANK(Func_9103) - jr nz, .notbank2 -.bank2 - call Func_9103 - jr .afterMusic -.notbank2 - cp 8 - jr nz, .bank1F -.bank8 - call Func_2136e - call Func_21879 - jr .afterMusic -.bank1F - call Func_7d177 -.afterMusic - - callba Func_18dee ; keep track of time played - - ld a, [$fff9] - and a - call z, ReadJoypad - - ld a, [$d122] - ld [H_LOADEDROMBANK], a - ld [MBC3RomBank], a - - pop hl - pop de - pop bc - pop af - reti - - -DelayFrame:: -; Wait for the next vblank interrupt. -; As a bonus, this saves battery. - -NOT_VBLANKED EQU 1 - - ld a, NOT_VBLANKED - ld [H_VBLANKOCCURRED], a -.halt - ; XXX this is a hack--rgbasm adds - ; a nop after halts by default. - db $76 ; halt - - ld a, [H_VBLANKOCCURRED] - and a - jr nz, .halt - ret - - -; These routines manage gradual fading -; (e.g., entering a doorway) -LoadGBPal:: ; 20ba (0:20ba) - ld a,[$d35d] ;tells if cur.map is dark (requires HM5_FLASH?) - ld b,a - ld hl,GBPalTable_00 ;16 - ld a,l - sub b - ld l,a - jr nc,.jr0 - dec h -.jr0 - ld a,[hli] - ld [rBGP],a - ld a,[hli] - ld [rOBP0],a - ld a,[hli] - ld [rOBP1],a - ret - -GBFadeOut1:: ; 20d1 (0:20d1) - ld hl,IncGradGBPalTable_01 ;0d - ld b,$04 - jr GBFadeOutCommon - -GBFadeOut2:: ; 20d8 (0:20d8) - ld hl,IncGradGBPalTable_02 ;1c - ld b,$03 - -GBFadeOutCommon:: ; 20dd (0:20dd) - ld a,[hli] - ld [rBGP],a - ld a,[hli] - ld [rOBP0],a - ld a,[hli] - ld [rOBP1],a - ld c,8 - call DelayFrames - dec b - jr nz,GBFadeOutCommon - ret - -GBFadeIn1:: ; 20ef (0:20ef) - ld hl,DecGradGBPalTable_01 ;18 - ld b,$04 - jr GBFadeInCommon - -GBFadeIn2:: ; 20f6 (0:20f6) - ld hl,DecGradGBPalTable_02 ;21 - ld b,$03 - -GBFadeInCommon:: ; 20fb (0:20fb) - ld a,[hld] - ld [rOBP1],a - ld a,[hld] - ld [rOBP0],a - ld a,[hld] - ld [rBGP],a - ld c,8 - call DelayFrames - dec b - jr nz,GBFadeInCommon - ret - -IncGradGBPalTable_01:: ; 210d (0:210d) - db %11111111 ;BG Pal - db %11111111 ;OBJ Pal 1 - db %11111111 ;OBJ Pal 2 - ;and so on... - db %11111110 - db %11111110 - db %11111000 - - db %11111001 - db %11100100 - db %11100100 -GBPalTable_00:: ; 2116 (0:2116) - db %11100100 - db %11010000 -DecGradGBPalTable_01:: ; 2118 (0:2118) - db %11100000 - ;19 - db %11100100 - db %11010000 - db %11100000 -IncGradGBPalTable_02:: ; 211c (0:211c) - db %10010000 - db %10000000 - db %10010000 - - db %01000000 - db %01000000 -DecGradGBPalTable_02:: ; 2121 (0:2121) - db %01000000 - - db %00000000 - db %00000000 - db %00000000 - -Serial:: ; 2125 (0:2125) - push af - push bc - push de - push hl - ld a, [$ffaa] - inc a - jr z, .asm_2142 - ld a, [$ff01] - ld [$ffad], a - ld a, [$ffac] - ld [$ff01], a - ld a, [$ffaa] - cp $2 - jr z, .asm_2162 - ld a, $80 - ld [$ff02], a - jr .asm_2162 -.asm_2142 - ld a, [$ff01] - ld [$ffad], a - ld [$ffaa], a - cp $2 - jr z, .asm_215f - xor a - ld [$ff01], a - ld a, $3 - ld [rDIV], a ; $ff04 -.asm_2153 - ld a, [rDIV] ; $ff04 - bit 7, a - jr nz, .asm_2153 - ld a, $80 - ld [$ff02], a - jr .asm_2162 -.asm_215f - xor a - ld [$ff01], a -.asm_2162 - ld a, $1 - ld [$ffa9], a - ld a, $fe - ld [$ffac], a - pop hl - pop de - pop bc - pop af - reti - -Func_216f:: ; 216f (0:216f) - ld a, $1 - ld [$ffab], a -.asm_2173 - ld a, [hl] - ld [$ffac], a - call Func_219a - push bc - ld b, a - inc hl - ld a, $30 -.asm_217e - dec a - jr nz, .asm_217e - ld a, [$ffab] - and a - ld a, b - pop bc - jr z, .asm_2192 - dec hl - cp $fd - jr nz, .asm_2173 - xor a - ld [$ffab], a - jr .asm_2173 -.asm_2192 - ld [de], a - inc de - dec bc - ld a, b - or c - jr nz, .asm_2173 - ret - -Func_219a:: ; 219a (0:219a) - xor a - ld [$ffa9], a - ld a, [$ffaa] - cp $2 - jr nz, .asm_21a7 - ld a, $81 - ld [$ff02], a -.asm_21a7 - ld a, [$ffa9] - and a - jr nz, .asm_21f1 - ld a, [$ffaa] - cp $1 - jr nz, .asm_21cc - call Func_2237 - jr z, .asm_21cc - call Func_2231 - push hl - ld hl, $cc48 - inc [hl] - jr nz, .asm_21c3 - dec hl - inc [hl] -.asm_21c3 - pop hl - call Func_2237 - jr nz, .asm_21a7 - jp Func_223f -.asm_21cc - ld a, [rIE] ; $ffff - and $f - cp $8 - jr nz, .asm_21a7 - ld a, [W_NUMHITS] ; $d074 - dec a - ld [W_NUMHITS], a ; $d074 - jr nz, .asm_21a7 - ld a, [$d075] - dec a - ld [$d075], a - jr nz, .asm_21a7 - ld a, [$ffaa] - cp $1 - jr z, .asm_21f1 - ld a, $ff -.asm_21ee - dec a - jr nz, .asm_21ee -.asm_21f1 - xor a - ld [$ffa9], a - ld a, [rIE] ; $ffff - and $f - sub $8 - jr nz, .asm_2204 - ld [W_NUMHITS], a ; $d074 - ld a, $50 - ld [$d075], a -.asm_2204 - ld a, [$ffad] - cp $fe - ret nz - call Func_2237 - jr z, .asm_221f - push hl - ld hl, $cc48 - ld a, [hl] - dec a - ld [hld], a - inc a - jr nz, .asm_2219 - dec [hl] -.asm_2219 - pop hl - call Func_2237 - jr z, Func_223f -.asm_221f - ld a, [rIE] ; $ffff - and $f - cp $8 - ld a, $fe - ret z - ld a, [hl] - ld [$ffac], a - call DelayFrame - jp Func_219a - -Func_2231:: ; 2231 (0:2231) - ld a, $f -.asm_2233 - dec a - jr nz, .asm_2233 - ret - -Func_2237:: ; 2237 (0:2237) - push hl - ld hl, $cc47 - ld a, [hli] - or [hl] - pop hl - ret - -Func_223f:: ; 223f (0:223f) - dec a - ld [$cc47], a - ld [$cc48], a - ret - -Func_2247:: ; 2247 (0:2247) - ld hl, $cc42 - ld de, $cc3d - ld c, $2 - ld a, $1 - ld [$ffab], a -.asm_2253 - call DelayFrame - ld a, [hl] - ld [$ffac], a - call Func_219a - ld b, a - inc hl - ld a, [$ffab] - and a - ld a, $0 - ld [$ffab], a - jr nz, .asm_2253 - ld a, b - ld [de], a - inc de - dec c - jr nz, .asm_2253 - ret - -Func_226e:: ; 226e (0:226e) - call SaveScreenTilesToBuffer1 - callab PrintWaitingText - call Func_227f - jp LoadScreenTilesFromBuffer1 - -Func_227f:: ; 227f (0:227f) - ld a, $ff - ld [$cc3e], a -.asm_2284 - call Func_22c3 - call DelayFrame - call Func_2237 - jr z, .asm_22a0 - push hl - ld hl, $cc48 - dec [hl] - jr nz, .asm_229f - dec hl - dec [hl] - jr nz, .asm_229f - pop hl - xor a - jp Func_223f -.asm_229f - pop hl -.asm_22a0 - ld a, [$cc3e] - inc a - jr z, .asm_2284 - ld b, $a -.asm_22a8 - call DelayFrame - call Func_22c3 - dec b - jr nz, .asm_22a8 - ld b, $a -.asm_22b3 - call DelayFrame - call Func_22ed - dec b - jr nz, .asm_22b3 - ld a, [$cc3e] - ld [$cc3d], a - ret - -Func_22c3:: ; 22c3 (0:22c3) - call asm_22d7 - ld a, [$cc42] - add $60 - ld [$ffac], a - ld a, [$ffaa] - cp $2 - jr nz, asm_22d7 - ld a, $81 - ld [$ff02], a -asm_22d7:: ; 22d7 (0:22d7) - ld a, [$ffad] - ld [$cc3d], a - and $f0 - cp $60 - ret nz - xor a - ld [$ffad], a - ld a, [$cc3d] - and $f - ld [$cc3e], a - ret - -Func_22ed:: ; 22ed (0:22ed) - xor a - ld [$ffac], a - ld a, [$ffaa] - cp $2 - ret nz - ld a, $81 - ld [$ff02], a - ret - -Func_22fa:: ; 22fa (0:22fa) - ld a, $2 - ld [$ff01], a - xor a - ld [$ffad], a - ld a, $80 - ld [$ff02], a - ret - -; timer interrupt is apparently not invoked anyway -Timer:: ; 2306 (0:2306) - reti - -Func_2307:: ; 2307 (0:2307) - call WaitForSoundToFinish - xor a - ld c, a - ld d, a - ld [$cfca], a - jr asm_2324 - -Func_2312:: ; 2312 (0:2312) - ld c, $a - ld d, $0 - ld a, [$d72e] - bit 5, a - jr z, asm_2324 - xor a - ld [$cfca], a - ld c, $8 - ld d, c -asm_2324:: ; 2324 (0:2324) - ld a, [$d700] - and a - jr z, .asm_2343 - cp $2 - jr z, .asm_2332 - ld a, MUSIC_BIKE_RIDING - jr .asm_2334 -.asm_2332 - ld a, MUSIC_SURFING -.asm_2334 - ld b, a - ld a, d - and a - ld a, Bank(Func_7d8ea) - jr nz, .asm_233e - ld [$c0ef], a -.asm_233e - ld [$c0f0], a - jr .asm_234c -.asm_2343 - ld a, [$d35b] - ld b, a - call Func_2385 - jr c, .asm_2351 -.asm_234c - ld a, [$cfca] - cp b - ret z -.asm_2351 - ld a, c - ld [wMusicHeaderPointer], a - ld a, b - ld [$cfca], a - ld [$c0ee], a - jp PlaySound - -Func_235f:: ; 235f (0:235f) - ld a, [$c0ef] - ld b, a - cp $2 - jr nz, .checkForBank08 -.bank02 - ld hl, Func_9103 - jr .asm_2378 -.checkForBank08 - cp $8 - jr nz, .bank1F -.bank08 - ld hl, Func_21879 - jr .asm_2378 -.bank1F - ld hl, Func_7d177 -.asm_2378 - ld c, $6 -.asm_237a - push bc - push hl - call Bankswitch - pop hl - pop bc - dec c - jr nz, .asm_237a - ret - -Func_2385:: ; 2385 (0:2385) - ld a, [$d35c] - ld e, a - ld a, [$c0ef] - cp e - jr nz, .asm_2394 - ld [$c0f0], a - and a - ret -.asm_2394 - ld a, c - and a - ld a, e - jr nz, .asm_239c - ld [$c0ef], a -.asm_239c - ld [$c0f0], a - scf - ret - -PlayMusic:: ; 23a1 (0:23a1) - ld b, a - ld [$c0ee], a - xor a - ld [wMusicHeaderPointer], a - ld a, c - ld [$c0ef], a - ld [$c0f0], a - ld a, b - -; plays music specified by a. If value is $ff, music is stopped -PlaySound:: ; 23b1 (0:23b1) - push hl - push de - push bc - ld b, a - ld a, [$c0ee] - and a - jr z, .asm_23c8 - xor a - ld [$c02a], a - ld [$c02b], a - ld [$c02c], a - ld [$c02d], a -.asm_23c8 - ld a, [wMusicHeaderPointer] - and a - jr z, .asm_23e3 - ld a, [$c0ee] - and a - jr z, .asm_2425 - xor a - ld [$c0ee], a - ld a, [$cfca] - cp $ff - jr nz, .asm_2414 - xor a - ld [wMusicHeaderPointer], a -.asm_23e3 - xor a - ld [$c0ee], a - ld a, [H_LOADEDROMBANK] - ld [$ffb9], a - ld a, [$c0ef] - ld [H_LOADEDROMBANK], a - ld [$2000], a - cp $2 - jr nz, .checkForBank08 -.bank02 - ld a, b - call Func_9876 - jr .asm_240b -.checkForBank08 - cp $8 - jr nz, .bank1F -.bank08 - ld a, b - call Func_22035 - jr .asm_240b -.bank1F - ld a, b - call Func_7d8ea -.asm_240b - ld a, [$ffb9] - ld [H_LOADEDROMBANK], a - ld [$2000], a - jr .asm_2425 -.asm_2414 - ld a, b - ld [$cfca], a - ld a, [wMusicHeaderPointer] - ld [$cfc8], a - ld [$cfc9], a - ld a, b - ld [wMusicHeaderPointer], a -.asm_2425 - pop bc - pop de - pop hl - ret - -UpdateSprites:: ; 2429 (0:2429) - ld a, [$cfcb] - dec a - ret nz - ld a, [H_LOADEDROMBANK] - push af - ld a, Bank(_UpdateSprites) - ld [H_LOADEDROMBANK], a - ld [$2000], a - call _UpdateSprites - pop af - ld [H_LOADEDROMBANK], a - ld [$2000], a - ret - -INCLUDE "data/mart_inventories.asm" - -TextScriptEndingChar:: ; 24d6 (0:24d6) - db "@" -TextScriptEnd:: ; 24d7 (0:24d7) - ld hl,TextScriptEndingChar - ret - -ExclamationText:: ; 24db (0:24db) - TX_FAR _ExclamationText - db "@" - -GroundRoseText:: ; 24e0 (0:24e0) - TX_FAR _GroundRoseText - db "@" - -BoulderText:: ; 24e5 (0:24e5) - TX_FAR _BoulderText - db "@" - -MartSignText:: ; 24ea (0:24ea) - TX_FAR _MartSignText - db "@" - -PokeCenterSignText:: ; 24ef (0:24ef) - TX_FAR _PokeCenterSignText - db "@" - -Predef5CText:: ; 24f4 (0:24f4) -; XXX better label (what does predef $5C do?) - db $08 ; asm - ld a, $5c - call Predef - jp TextScriptEnd - -; bankswitches and runs _UncompressSpriteData -; bank is given in a, sprite input stream is pointed to in W_SPRITEINPUTPTR -UncompressSpriteData:: ; 24fd (0:24fd) - ld b, a - ld a, [H_LOADEDROMBANK] - push af - ld a, b - ld [H_LOADEDROMBANK], a - ld [$2000], a - ld a, $a - ld [$0], a - xor a - ld [$4000], a - call _UncompressSpriteData - pop af - ld [H_LOADEDROMBANK], a - ld [$2000], a - ret - -; initializes necessary data to load a sprite and runs UncompressSpriteDataLoop -_UncompressSpriteData:: ; 251a (0:251a) - ld hl, S_SPRITEBUFFER1 - ld c, (2*SPRITEBUFFERSIZE) % $100 - ld b, (2*SPRITEBUFFERSIZE) / $100 - xor a - call FillMemory ; clear sprite buffer 1 and 2 - ld a, $1 - ld [W_SPRITEINPUTBITCOUNTER], a - ld a, $3 - ld [W_SPRITEOUTPUTBITOFFSET], a - xor a - ld [W_SPRITECURPOSX], a - ld [W_SPRITECURPOSY], a - ld [W_SPRITELOADFLAGS], a ; $d0a8 - call ReadNextInputByte ; first byte of input determines sprite width (high nybble) and height (low nybble) in tiles (8x8 pixels) - ld b, a - and $f - add a - add a - add a - ld [W_SPRITEHEIGHT], a - ld a, b - swap a - and $f - add a - add a - add a - ld [W_SPRITEWITDH], a - call ReadNextInputBit - ld [W_SPRITELOADFLAGS], a ; initialite bit1 to 0 and bit0 to the first input bit - ; this will load two chunks of data to S_SPRITEBUFFER1 and S_SPRITEBUFFER2 - ; bit 0 decides in which one the first chunk is placed - ; fall through - -; uncompresses a chunk from the sprite input data stream (pointed to at $d0da) into S_SPRITEBUFFER1 or S_SPRITEBUFFER2 -; each chunk is a 1bpp sprite. A 2bpp sprite consist of two chunks which are merged afterwards -; note that this is an endless loop which is terminated during a call to MoveToNextBufferPosition by manipulating the stack -UncompressSpriteDataLoop:: ; 2556 (0:2556) - ld hl, S_SPRITEBUFFER1 - ld a, [W_SPRITELOADFLAGS] ; $d0a8 - bit 0, a - jr z, .useSpriteBuffer1 ; check which buffer to use - ld hl, S_SPRITEBUFFER2 -.useSpriteBuffer1 - call StoreSpriteOutputPointer - ld a, [W_SPRITELOADFLAGS] ; $d0a8 - bit 1, a - jr z, .startDecompression ; check if last iteration - call ReadNextInputBit ; if last chunk, read 1-2 bit unpacking mode - and a - jr z, .unpackingMode0 ; 0 -> mode 0 - call ReadNextInputBit ; 1 0 -> mode 1 - inc a ; 1 1 -> mode 2 -.unpackingMode0 - ld [W_SPRITEUNPACKMODE], a -.startDecompression - call ReadNextInputBit - and a - jr z, .readRLEncodedZeros ; if first bit is 0, the input starts with zeroes, otherwise with (non-zero) input -.readNextInput - call ReadNextInputBit - ld c, a - call ReadNextInputBit - sla c - or c ; read next two bits into c - and a - jr z, .readRLEncodedZeros ; 00 -> RLEncoded zeroes following - call WriteSpriteBitsToBuffer ; otherwise write input to output and repeat - call MoveToNextBufferPosition - jr .readNextInput -.readRLEncodedZeros - ld c, $0 ; number of zeroes it length encoded, the number -.countConsecutiveOnesLoop ; of consecutive ones determines the number of bits the number has - call ReadNextInputBit - and a - jr z, .countConsecutiveOnesFinished - inc c - jr .countConsecutiveOnesLoop -.countConsecutiveOnesFinished - ld a, c - add a - ld hl, LengthEncodingOffsetList - add l - ld l, a - jr nc, .noCarry - inc h -.noCarry - ld a, [hli] ; read offset that is added to the number later on - ld e, a ; adding an offset of 2^length - 1 makes every integer uniquely - ld d, [hl] ; representable in the length encoding and saves bits - push de - inc c - ld e, $0 - ld d, e -.readNumberOfZerosLoop ; reads the next c+1 bits of input - call ReadNextInputBit - or e - ld e, a - dec c - jr z, .readNumberOfZerosDone - sla e - rl d - jr .readNumberOfZerosLoop -.readNumberOfZerosDone - pop hl ; add the offset - add hl, de - ld e, l - ld d, h -.writeZerosLoop - ld b, e - xor a ; write 00 to buffer - call WriteSpriteBitsToBuffer - ld e, b - call MoveToNextBufferPosition - dec de - ld a, d - and a - jr nz, .continueLoop - ld a, e - and a -.continueLoop - jr nz, .writeZerosLoop - jr .readNextInput - -; moves output pointer to next position -; also cancels the calling function if the all output is done (by removing the return pointer from stack) -; and calls postprocessing functions according to the unpack mode -MoveToNextBufferPosition:: ; 25d8 (0:25d8) - ld a, [W_SPRITEHEIGHT] - ld b, a - ld a, [W_SPRITECURPOSY] - inc a - cp b - jr z, .curColumnDone - ld [W_SPRITECURPOSY], a - ld a, [W_SPRITEOUTPUTPTR] - inc a - ld [W_SPRITEOUTPUTPTR], a - ret nz - ld a, [W_SPRITEOUTPUTPTR+1] - inc a - ld [W_SPRITEOUTPUTPTR+1], a - ret -.curColumnDone - xor a - ld [W_SPRITECURPOSY], a - ld a, [W_SPRITEOUTPUTBITOFFSET] - and a - jr z, .bitOffsetsDone - dec a - ld [W_SPRITEOUTPUTBITOFFSET], a - ld hl, W_SPRITEOUTPUTPTRCACHED - ld a, [hli] - ld [W_SPRITEOUTPUTPTR], a - ld a, [hl] - ld [W_SPRITEOUTPUTPTR+1], a - ret -.bitOffsetsDone - ld a, $3 - ld [W_SPRITEOUTPUTBITOFFSET], a - ld a, [W_SPRITECURPOSX] - add $8 - ld [W_SPRITECURPOSX], a - ld b, a - ld a, [W_SPRITEWITDH] - cp b - jr z, .allColumnsDone - ld a, [W_SPRITEOUTPUTPTR] - ld l, a - ld a, [W_SPRITEOUTPUTPTR+1] - ld h, a - inc hl - jp StoreSpriteOutputPointer -.allColumnsDone - pop hl - xor a - ld [W_SPRITECURPOSX], a - ld a, [W_SPRITELOADFLAGS] ; $d0a8 - bit 1, a - jr nz, .done ; test if there is one more sprite to go - xor $1 - set 1, a - ld [W_SPRITELOADFLAGS], a ; $d0a8 - jp UncompressSpriteDataLoop -.done - jp UnpackSprite - -; writes 2 bits (from a) to the output buffer (pointed to from W_SPRITEOUTPUTPTR) -WriteSpriteBitsToBuffer:: ; 2649 (0:2649) - ld e, a - ld a, [W_SPRITEOUTPUTBITOFFSET] - and a - jr z, .offset0 - cp $2 - jr c, .offset1 - jr z, .offset2 - rrc e ; offset 3 - rrc e - jr .offset0 -.offset1 - sla e - sla e - jr .offset0 -.offset2 - swap e -.offset0 - ld a, [W_SPRITEOUTPUTPTR] - ld l, a - ld a, [W_SPRITEOUTPUTPTR+1] - ld h, a - ld a, [hl] - or e - ld [hl], a - ret - -; reads next bit from input stream and returns it in a -ReadNextInputBit:: ; 2670 (0:2670) - ld a, [W_SPRITEINPUTBITCOUNTER] - dec a - jr nz, .curByteHasMoreBitsToRead - call ReadNextInputByte - ld [W_SPRITEINPUTCURBYTE], a - ld a, $8 -.curByteHasMoreBitsToRead - ld [W_SPRITEINPUTBITCOUNTER], a - ld a, [W_SPRITEINPUTCURBYTE] - rlca - ld [W_SPRITEINPUTCURBYTE], a - and $1 - ret - -; reads next byte from input stream and returns it in a -ReadNextInputByte:: ; 268b (0:268b) - ld a, [W_SPRITEINPUTPTR] - ld l, a - ld a, [W_SPRITEINPUTPTR+1] - ld h, a - ld a, [hli] - ld b, a - ld a, l - ld [W_SPRITEINPUTPTR], a - ld a, h - ld [W_SPRITEINPUTPTR+1], a - ld a, b - ret - -; the nth item is 2^n - 1 -LengthEncodingOffsetList:: ; 269f (0:269f) - dw %0000000000000001 - dw %0000000000000011 - dw %0000000000000111 - dw %0000000000001111 - dw %0000000000011111 - dw %0000000000111111 - dw %0000000001111111 - dw %0000000011111111 - dw %0000000111111111 - dw %0000001111111111 - dw %0000011111111111 - dw %0000111111111111 - dw %0001111111111111 - dw %0011111111111111 - dw %0111111111111111 - dw %1111111111111111 - -; unpacks the sprite data depending on the unpack mode -UnpackSprite:: ; 26bf (0:26bf) - ld a, [W_SPRITEUNPACKMODE] - cp $2 - jp z, UnpackSpriteMode2 - and a - jp nz, XorSpriteChunks - ld hl, S_SPRITEBUFFER1 - call SpriteDifferentialDecode - ld hl, S_SPRITEBUFFER2 - ; fall through - -; decodes differential encoded sprite data -; input bit value 0 preserves the current bit value and input bit value 1 toggles it (starting from initial value 0). -SpriteDifferentialDecode:: ; 26d4 (0:26d4) - xor a - ld [W_SPRITECURPOSX], a - ld [W_SPRITECURPOSY], a - call StoreSpriteOutputPointer - ld a, [W_SPRITEFLIPPED] - and a - jr z, .notFlipped - ld hl, DecodeNybble0TableFlipped - ld de, DecodeNybble1TableFlipped - jr .storeDecodeTablesPointers -.notFlipped - ld hl, DecodeNybble0Table - ld de, DecodeNybble1Table -.storeDecodeTablesPointers - ld a, l - ld [W_SPRITEDECODETABLE0PTR], a - ld a, h - ld [W_SPRITEDECODETABLE0PTR+1], a - ld a, e - ld [W_SPRITEDECODETABLE1PTR], a - ld a, d - ld [W_SPRITEDECODETABLE1PTR+1], a - ld e, $0 ; last decoded nybble, initialized to 0 -.decodeNextByteLoop - ld a, [W_SPRITEOUTPUTPTR] - ld l, a - ld a, [W_SPRITEOUTPUTPTR+1] - ld h, a - ld a, [hl] - ld b, a - swap a - and $f - call DifferentialDecodeNybble ; decode high nybble - swap a - ld d, a - ld a, b - and $f - call DifferentialDecodeNybble ; decode low nybble - or d - ld b, a - ld a, [W_SPRITEOUTPUTPTR] - ld l, a - ld a, [W_SPRITEOUTPUTPTR+1] - ld h, a - ld a, b - ld [hl], a ; write back decoded data - ld a, [W_SPRITEHEIGHT] - add l ; move on to next column - jr nc, .noCarry - inc h -.noCarry - ld [W_SPRITEOUTPUTPTR], a - ld a, h - ld [W_SPRITEOUTPUTPTR+1], a - ld a, [W_SPRITECURPOSX] - add $8 - ld [W_SPRITECURPOSX], a - ld b, a - ld a, [W_SPRITEWITDH] - cp b - jr nz, .decodeNextByteLoop ; test if current row is done - xor a - ld e, a - ld [W_SPRITECURPOSX], a - ld a, [W_SPRITECURPOSY] ; move on to next row - inc a - ld [W_SPRITECURPOSY], a - ld b, a - ld a, [W_SPRITEHEIGHT] - cp b - jr z, .done ; test if all rows finished - ld a, [W_SPRITEOUTPUTPTRCACHED] - ld l, a - ld a, [W_SPRITEOUTPUTPTRCACHED+1] - ld h, a - inc hl - call StoreSpriteOutputPointer - jr .decodeNextByteLoop -.done - xor a - ld [W_SPRITECURPOSY], a - ret - -; decodes the nybble stored in a. Last decoded data is assumed to be in e (needed to determine if initial value is 0 or 1) -DifferentialDecodeNybble:: ; 276d (0:276d) - srl a ; c=a%2, a/=2 - ld c, $0 - jr nc, .evenNumber - ld c, $1 -.evenNumber - ld l, a - ld a, [W_SPRITEFLIPPED] - and a - jr z, .notFlipped ; determine if initial value is 0 or one - bit 3, e ; if flipped, consider MSB of last data - jr .selectLookupTable -.notFlipped - bit 0, e ; else consider LSB -.selectLookupTable - ld e, l - jr nz, .initialValue1 ; load the appropriate table - ld a, [W_SPRITEDECODETABLE0PTR] - ld l, a - ld a, [W_SPRITEDECODETABLE0PTR+1] - jr .tableLookup -.initialValue1 - ld a, [W_SPRITEDECODETABLE1PTR] - ld l, a - ld a, [W_SPRITEDECODETABLE1PTR+1] -.tableLookup - ld h, a - ld a, e - add l - ld l, a - jr nc, .noCarry - inc h -.noCarry - ld a, [hl] - bit 0, c - jr nz, .selectLowNybble - swap a ; select high nybble -.selectLowNybble - and $f - ld e, a ; update last decoded data - ret - -DecodeNybble0Table:: ; 27a7 (0:27a7) - dn $0, $1 - dn $3, $2 - dn $7, $6 - dn $4, $5 - dn $f, $e - dn $c, $d - dn $8, $9 - dn $b, $a -DecodeNybble1Table:: ; 27af (0:27af) - dn $f, $e - dn $c, $d - dn $8, $9 - dn $b, $a - dn $0, $1 - dn $3, $2 - dn $7, $6 - dn $4, $5 -DecodeNybble0TableFlipped:: ; 27b7 (0:27b7) - dn $0, $8 - dn $c, $4 - dn $e, $6 - dn $2, $a - dn $f, $7 - dn $3, $b - dn $1, $9 - dn $d, $5 -DecodeNybble1TableFlipped:: ; 27bf (0:27bf) - dn $f, $7 - dn $3, $b - dn $1, $9 - dn $d, $5 - dn $0, $8 - dn $c, $4 - dn $e, $6 - dn $2, $a - -; combines the two loaded chunks with xor (the chunk loaded second is the destination). The source chunk is differeintial decoded beforehand. -XorSpriteChunks:: ; 27c7 (0:27c7) - xor a - ld [W_SPRITECURPOSX], a - ld [W_SPRITECURPOSY], a - call ResetSpriteBufferPointers - ld a, [W_SPRITEOUTPUTPTR] ; points to buffer 1 or 2, depending on flags - ld l, a - ld a, [W_SPRITEOUTPUTPTR+1] - ld h, a - call SpriteDifferentialDecode ; decode buffer 1 or 2, depending on flags - call ResetSpriteBufferPointers - ld a, [W_SPRITEOUTPUTPTR] ; source buffer, points to buffer 1 or 2, depending on flags - ld l, a - ld a, [W_SPRITEOUTPUTPTR+1] - ld h, a - ld a, [W_SPRITEOUTPUTPTRCACHED] ; destination buffer, points to buffer 2 or 1, depending on flags - ld e, a - ld a, [W_SPRITEOUTPUTPTRCACHED+1] - ld d, a -.xorChunksLoop - ld a, [W_SPRITEFLIPPED] - and a - jr z, .notFlipped - push de - ld a, [de] - ld b, a - swap a - and $f - call ReverseNybble ; if flipped reverse the nybbles in the destination buffer - swap a - ld c, a - ld a, b - and $f - call ReverseNybble - or c - pop de - ld [de], a -.notFlipped - ld a, [hli] - ld b, a - ld a, [de] - xor b - ld [de], a - inc de - ld a, [W_SPRITECURPOSY] - inc a - ld [W_SPRITECURPOSY], a ; go to next row - ld b, a - ld a, [W_SPRITEHEIGHT] - cp b - jr nz, .xorChunksLoop ; test if column finished - xor a - ld [W_SPRITECURPOSY], a - ld a, [W_SPRITECURPOSX] - add $8 - ld [W_SPRITECURPOSX], a ; go to next column - ld b, a - ld a, [W_SPRITEWITDH] - cp b - jr nz, .xorChunksLoop ; test if all columns finished - xor a - ld [W_SPRITECURPOSX], a - ret - -; reverses the bits in the nybble given in register a -ReverseNybble:: ; 2837 (0:2837) - ld de, NybbleReverseTable - add e - ld e, a - jr nc, .asm_283f - inc d -.asm_283f - ld a, [de] - ret - -; resets sprite buffer pointers to buffer 1 and 2, depending on W_SPRITELOADFLAGS -ResetSpriteBufferPointers:: ; 2841 (0:2841) - ld a, [W_SPRITELOADFLAGS] ; $d0a8 - bit 0, a - jr nz, .buffer2Selected - ld de, S_SPRITEBUFFER1 - ld hl, S_SPRITEBUFFER2 - jr .storeBufferPointers -.buffer2Selected - ld de, S_SPRITEBUFFER2 - ld hl, S_SPRITEBUFFER1 -.storeBufferPointers - ld a, l - ld [W_SPRITEOUTPUTPTR], a - ld a, h - ld [W_SPRITEOUTPUTPTR+1], a - ld a, e - ld [W_SPRITEOUTPUTPTRCACHED], a - ld a, d - ld [W_SPRITEOUTPUTPTRCACHED+1], a - ret - -; maps each nybble to its reverse -NybbleReverseTable:: ; 2867 (0:2867) - db $0, $8, $4, $c, $2, $a, $6 ,$e, $1, $9, $5, $d, $3, $b, $7 ,$f - -; combines the two loaded chunks with xor (the chunk loaded second is the destination). Both chunks are differeintial decoded beforehand. -UnpackSpriteMode2:: ; 2877 (0:2877) - call ResetSpriteBufferPointers - ld a, [W_SPRITEFLIPPED] - push af - xor a - ld [W_SPRITEFLIPPED], a ; temporarily clear flipped flag for decoding the destination chunk - ld a, [W_SPRITEOUTPUTPTRCACHED] - ld l, a - ld a, [W_SPRITEOUTPUTPTRCACHED+1] - ld h, a - call SpriteDifferentialDecode - call ResetSpriteBufferPointers - pop af - ld [W_SPRITEFLIPPED], a - jp XorSpriteChunks - -; stores hl into the output pointers -StoreSpriteOutputPointer:: ; 2897 (0:2897) - ld a, l - ld [W_SPRITEOUTPUTPTR], a - ld [W_SPRITEOUTPUTPTRCACHED], a - ld a, h - ld [W_SPRITEOUTPUTPTR+1], a - ld [W_SPRITEOUTPUTPTRCACHED+1], a - ret - -ResetPlayerSpriteData:: ; 28a6 (0:28a6) - ld hl, wSpriteStateData1 - call ResetPlayerSpriteData_ClearSpriteData - ld hl, wSpriteStateData2 - call ResetPlayerSpriteData_ClearSpriteData - ld a, $1 - ld [wSpriteStateData1], a - ld [$c20e], a - ld hl, $c104 - ld [hl], $3c ; set Y screen pos - inc hl - inc hl - ld [hl], $40 ; set X screen pos - ret - -; overwrites sprite data with zeroes -ResetPlayerSpriteData_ClearSpriteData:: ; 28c4 (0:28c4) - ld bc, $10 - xor a - jp FillMemory - -Func_28cb:: ; 28cb (0:28cb) - ld a, [wMusicHeaderPointer] - and a - jr nz, .asm_28dc - ld a, [$d72c] - bit 1, a - ret nz - ld a, $77 - ld [$ff24], a - ret -.asm_28dc - ld a, [$cfc9] - and a - jr z, .asm_28e7 - dec a - ld [$cfc9], a - ret -.asm_28e7 - ld a, [$cfc8] - ld [$cfc9], a - ld a, [$ff24] - and a - jr z, .asm_2903 - ld b, a - and $f - dec a - ld c, a - ld a, b - and $f0 - swap a - dec a - swap a - or c - ld [$ff24], a - ret -.asm_2903 - ld a, [wMusicHeaderPointer] - ld b, a - xor a - ld [wMusicHeaderPointer], a - ld a, $ff - ld [$c0ee], a - call PlaySound - ld a, [$c0f0] - ld [$c0ef], a - ld a, b - ld [$c0ee], a - jp PlaySound - -; this function is used to display sign messages, sprite dialog, etc. -; INPUT: [$ff8c] = sprite ID or text ID -DisplayTextID:: ; 2920 (0:2920) - ld a,[H_LOADEDROMBANK] - push af - callba DisplayTextIDInit ; initialization - ld hl,$cf11 - bit 0,[hl] - res 0,[hl] - jr nz,.skipSwitchToMapBank - ld a,[W_CURMAP] - call SwitchToMapRomBank -.skipSwitchToMapBank - ld a,30 ; half a second - ld [H_FRAMECOUNTER],a ; used as joypad poll timer - ld hl,W_MAPTEXTPTR - ld a,[hli] - ld h,[hl] - ld l,a ; hl = map text pointer - ld d,$00 - ld a,[$ff8c] ; text ID - ld [$cf13],a - and a - jp z,DisplayStartMenu - cp a,$d3 ; safari game over - jp z,DisplaySafariGameOverText - cp a,$d0 ; fainted - jp z,DisplayPokemonFaintedText - cp a,$d1 ; blacked out - jp z,DisplayPlayerBlackedOutText - cp a,$d2 ; repel wore off - jp z,DisplayRepelWoreOffText - ld a,[$d4e1] ; number of sprites - ld e,a - ld a,[$ff8c] ; sprite ID - cp e - jr z,.spriteHandling - jr nc,.skipSpriteHandling -.spriteHandling -; get the text ID of the sprite - push hl - push de - push bc - callba Func_13074 ; update the graphics of the sprite the player is talking to (to face the right direction) - pop bc - pop de - ld hl,W_MAPSPRITEDATA ; NPC text entries - ld a,[$ff8c] - dec a - add a - add l - ld l,a - jr nc,.noCarry - inc h -.noCarry - inc hl - ld a,[hl] ; a = text ID of the sprite - pop hl -.skipSpriteHandling -; look up the address of the text in the map's text entries - dec a - ld e,a - sla e - add hl,de - ld a,[hli] - ld h,[hl] - ld l,a ; hl = address of the text - ld a,[hl] ; a = first byte of text -; check first byte of text for special cases - cp a,$fe ; Pokemart NPC - jp z,DisplayPokemartDialogue - cp a,$ff ; Pokemon Center NPC - jp z,DisplayPokemonCenterDialogue - cp a,$fc ; Item Storage PC - jp z,FuncTX_ItemStoragePC - cp a,$fd ; Bill's PC - jp z,FuncTX_BillsPC - cp a,$f9 ; Pokemon Center PC - jp z,FuncTX_PokemonCenterPC - cp a,$f5 ; Vending Machine - jr nz,.notVendingMachine - callba VendingMachineMenu ; jump banks to vending machine routine - jr AfterDisplayingTextID -.notVendingMachine - cp a,$f7 ; slot machine - jp z,FuncTX_SlotMachine - cp a,$f6 ; cable connection NPC in Pokemon Center - jr nz,.notSpecialCase - callab CableClubNPC - jr AfterDisplayingTextID -.notSpecialCase - call Func_3c59 ; display the text - ld a,[$cc3c] - and a - jr nz,HoldTextDisplayOpen - -AfterDisplayingTextID:: ; 29d6 (0:29d6) - ld a,[$cc47] - and a - jr nz,HoldTextDisplayOpen - call WaitForTextScrollButtonPress ; wait for a button press after displaying all the text - -; loop to hold the dialogue box open as long as the player keeps holding down the A button -HoldTextDisplayOpen:: ; 29df (0:29df) - call GetJoypadState - ld a,[H_CURRENTPRESSEDBUTTONS] - bit 0,a ; is the A button being pressed? - jr nz,HoldTextDisplayOpen - -CloseTextDisplay:: ; 29e8 (0:29e8) - ld a,[W_CURMAP] - call SwitchToMapRomBank - ld a,$90 - ld [$ffb0],a ; move the window off the screen - call DelayFrame - call LoadGBPal - xor a - ld [H_AUTOBGTRANSFERENABLED],a ; disable continuous WRAM to VRAM transfer each V-blank -; loop to make sprites face the directions they originally faced before the dialogue - ld hl,$c219 - ld c,$0f - ld de,$0010 -.restoreSpriteFacingDirectionLoop - ld a,[hl] - dec h - ld [hl],a - inc h - add hl,de - dec c - jr nz,.restoreSpriteFacingDirectionLoop - ld a,BANK(InitMapSprites) - ld [H_LOADEDROMBANK],a - ld [$2000],a - call InitMapSprites ; reload sprite tile pattern data (since it was partially overwritten by text tile patterns) - ld hl,$cfc4 - res 0,[hl] - ld a,[$d732] - bit 3,a - call z,LoadPlayerSpriteGraphics - call LoadCurrentMapView - pop af - ld [H_LOADEDROMBANK],a - ld [$2000],a - jp UpdateSprites ; move sprites - -DisplayPokemartDialogue:: ; 2a2e (0:2a2e) - push hl - ld hl,PokemartGreetingText - call PrintText - pop hl - inc hl - call LoadItemList - ld a,$02 - ld [$cf94],a ; selects between subtypes of menus - ld a,[H_LOADEDROMBANK] - push af - ld a,Bank(DisplayPokemartDialogue_) - ld [H_LOADEDROMBANK],a - ld [$2000],a - call DisplayPokemartDialogue_ - pop af - ld [H_LOADEDROMBANK],a - ld [$2000],a - jp AfterDisplayingTextID - -PokemartGreetingText:: ; 2a55 (0:2a55) - TX_FAR _PokemartGreetingText - db "@" - -LoadItemList:: ; 2a5a (0:2a5a) - ld a,$01 - ld [$cfcb],a - ld a,h - ld [$d128],a - ld a,l - ld [$d129],a - ld de,$cf7b -.loop - ld a,[hli] - ld [de],a - inc de - cp a,$ff - jr nz,.loop - ret - -DisplayPokemonCenterDialogue:: ; 2a72 (0:2a72) - xor a - ld [$ff8b],a - ld [$ff8c],a - ld [$ff8d],a - inc hl - ld a,[H_LOADEDROMBANK] - push af - ld a,Bank(DisplayPokemonCenterDialogue_) - ld [H_LOADEDROMBANK],a - ld [$2000],a - call DisplayPokemonCenterDialogue_ - pop af - ld [H_LOADEDROMBANK],a - ld [$2000],a - jp AfterDisplayingTextID - -DisplaySafariGameOverText:: ; 2a90 (0:2a90) - callab PrintSafariGameOverText - jp AfterDisplayingTextID - -DisplayPokemonFaintedText:: ; 2a9b (0:2a9b) - ld hl,PokemonFaintedText - call PrintText - jp AfterDisplayingTextID - -PokemonFaintedText:: ; 2aa4 (0:2aa4) - TX_FAR _PokemonFaintedText - db "@" - -DisplayPlayerBlackedOutText:: ; 2aa9 (0:2aa9) - ld hl,PlayerBlackedOutText - call PrintText - ld a,[$d732] - res 5,a - ld [$d732],a - jp HoldTextDisplayOpen - -PlayerBlackedOutText:: ; 2aba (0:2aba) - TX_FAR _PlayerBlackedOutText - db "@" - -DisplayRepelWoreOffText:: ; 2abf (0:2abf) - ld hl,RepelWoreOffText - call PrintText - jp AfterDisplayingTextID - -RepelWoreOffText:: ; 2ac8 (0:2ac8) - TX_FAR _RepelWoreOffText - db "@" - -INCLUDE "engine/menu/start_menu.asm" - -; function to count how many bits are set in a string of bytes -; INPUT: -; hl = address of string of bytes -; b = length of string of bytes -; OUTPUT: -; [$D11E] = number of set bits -CountSetBits:: ; 2b7f (0:2b7f) - ld c,0 -.loop - ld a,[hli] - ld e,a - ld d,8 -.innerLoop ; count how many bits are set in the current byte - srl e - ld a,0 - adc c - ld c,a - dec d - jr nz,.innerLoop - dec b - jr nz,.loop - ld a,c - ld [$d11e],a ; store number of set bits - ret - -; subtracts the amount the player paid from their money -; sets carry flag if there is enough money and unsets carry flag if not -SubtractAmountPaidFromMoney:: ; 2b96 (0:2b96) - ld b,BANK(SubtractAmountPaidFromMoney_) - ld hl,SubtractAmountPaidFromMoney_ - jp Bankswitch - -; adds the amount the player sold to their money -AddAmountSoldToMoney:: ; 2b9e (0:2b9e) - ld de,wPlayerMoney + 2 - ld hl,$ffa1 ; total price of items - ld c,3 ; length of money in bytes - ld a,$0b - call Predef ; add total price to money - ld a,$13 - ld [$d125],a - call DisplayTextBoxID ; redraw money text box - ld a, (SFX_02_5a - SFX_Headers_02) / 3 - call PlaySoundWaitForCurrent ; play sound - jp WaitForSoundToFinish ; wait until sound is done playing - -; function to remove an item (in varying quantities) from the player's bag or PC box -; INPUT: -; HL = address of inventory (either wNumBagItems or wNumBoxItems) -; [$CF92] = index (within the inventory) of the item to remove -; [$CF96] = quantity to remove -RemoveItemFromInventory:: ; 2bbb (0:2bbb) - ld a,[H_LOADEDROMBANK] - push af - ld a,BANK(RemoveItemFromInventory_) - ld [H_LOADEDROMBANK],a - ld [$2000],a - call RemoveItemFromInventory_ - pop af - ld [H_LOADEDROMBANK],a - ld [$2000],a - ret - -; function to add an item (in varying quantities) to the player's bag or PC box -; INPUT: -; HL = address of inventory (either wNumBagItems or wNumBoxItems) -; [$CF91] = item ID -; [$CF96] = item quantity -; sets carry flag if successful, unsets carry flag if unsuccessful -AddItemToInventory:: ; 2bcf (0:2bcf) - push bc - ld a,[H_LOADEDROMBANK] - push af - ld a,BANK(AddItemToInventory_) - ld [H_LOADEDROMBANK],a - ld [$2000],a - call AddItemToInventory_ - pop bc - ld a,b - ld [H_LOADEDROMBANK],a - ld [$2000],a - pop bc - ret - -; INPUT: -; [wListMenuID] = list menu ID -; [$cf8b] = address of the list (2 bytes) -DisplayListMenuID:: ; 2be6 (0:2be6) - xor a - ld [H_AUTOBGTRANSFERENABLED],a ; disable auto-transfer - ld a,1 - ld [$ffb7],a ; joypad state update flag - ld a,[W_BATTLETYPE] - and a ; is it the Old Man battle? - jr nz,.specialBattleType - ld a,$01 ; hardcoded bank - jr .bankswitch -.specialBattleType ; Old Man battle - ld a, Bank(OldManItemList) -.bankswitch - call BankswitchHome - ld hl,$d730 - set 6,[hl] ; turn off letter printing delay - xor a - ld [$cc35],a ; 0 means no item is currently being swapped - ld [$d12a],a - ld a,[$cf8b] - ld l,a - ld a,[$cf8c] - ld h,a ; hl = address of the list - ld a,[hl] - ld [$d12a],a ; [$d12a] = number of list entries - ld a,$0d ; list menu text box ID - ld [$d125],a - call DisplayTextBoxID ; draw the menu text box - call UpdateSprites ; move sprites - FuncCoord 4,2 ; coordinates of upper left corner of menu text box - ld hl,Coord - ld de,$090e ; height and width of menu text box - ld a,[wListMenuID] - and a ; is it a PC pokemon list? - jr nz,.skipMovingSprites - call UpdateSprites ; move sprites -.skipMovingSprites - ld a,1 ; max menu item ID is 1 if the list has less than 2 entries - ld [$cc37],a - ld a,[$d12a] - cp a,2 ; does the list have less than 2 entries? - jr c,.setMenuVariables - ld a,2 ; max menu item ID is 2 if the list has at least 2 entries -.setMenuVariables - ld [wMaxMenuItem],a - ld a,4 - ld [wTopMenuItemY],a - ld a,5 - ld [wTopMenuItemX],a - ld a,%00000111 ; A button, B button, Select button - ld [wMenuWatchedKeys],a - ld c,10 - call DelayFrames - -DisplayListMenuIDLoop:: ; 2c53 (0:2c53) - xor a - ld [H_AUTOBGTRANSFERENABLED],a ; disable transfer - call PrintListMenuEntries - ld a,1 - ld [H_AUTOBGTRANSFERENABLED],a ; enable transfer - call Delay3 - ld a,[W_BATTLETYPE] - and a ; is it the Old Man battle? - jr z,.notOldManBattle -.oldManBattle - ld a,"▶" - FuncCoord 5,4 - ld [Coord],a ; place menu cursor in front of first menu entry - ld c,80 - call DelayFrames - xor a - ld [wCurrentMenuItem],a - ld hl,Coord - ld a,l - ld [wMenuCursorLocation],a - ld a,h - ld [wMenuCursorLocation + 1],a - jr .buttonAPressed -.notOldManBattle - call LoadGBPal - call HandleMenuInput - push af - call PlaceMenuCursor - pop af - bit 0,a ; was the A button pressed? - jp z,.checkOtherKeys -.buttonAPressed - ld a,[wCurrentMenuItem] - call PlaceUnfilledArrowMenuCursor - ld a,$01 - ld [$d12e],a - ld [$d12d],a - xor a - ld [$cc37],a - ld a,[wCurrentMenuItem] - ld c,a - ld a,[wListScrollOffset] - add c - ld c,a - ld a,[$d12a] ; number of list entries - and a ; is the list empty? - jp z,ExitListMenu ; if so, exit the menu - dec a - cp c ; did the player select Cancel? - jp c,ExitListMenu ; if so, exit the menu - ld a,c - ld [wWhichPokemon],a - ld a,[wListMenuID] - cp a,ITEMLISTMENU - jr nz,.skipMultiplying -; if it's an item menu - sla c ; item entries are 2 bytes long, so multiply by 2 -.skipMultiplying - ld a,[$cf8b] - ld l,a - ld a,[$cf8c] - ld h,a - inc hl ; hl = beginning of list entries - ld b,0 - add hl,bc - ld a,[hl] - ld [$cf91],a - ld a,[wListMenuID] - and a ; is it a PC pokemon list? - jr z,.pokemonList - push hl - call GetItemPrice - pop hl - ld a,[wListMenuID] - cp a,ITEMLISTMENU - jr nz,.skipGettingQuantity -; if it's an item menu - inc hl - ld a,[hl] ; a = item quantity - ld [$cf97],a -.skipGettingQuantity - ld a,[$cf91] - ld [$d0b5],a - ld a,$01 - ld [$d0b7],a - call GetName - jr .storeChosenEntry -.pokemonList - ld hl,W_NUMINPARTY - ld a,[$cf8b] - cp l ; is it a list of party pokemon or box pokemon? - ld hl,W_PARTYMON1NAME - jr z,.getPokemonName - ld hl, W_BOXMON1NAME ; box pokemon names -.getPokemonName - ld a,[wWhichPokemon] - call GetPartyMonName -.storeChosenEntry ; store the menu entry that the player chose and return - ld de,$cd6d - call CopyStringToCF4B ; copy name to $cf4b - ld a,$01 - ld [$d12e],a - ld a,[wCurrentMenuItem] - ld [$d12d],a - xor a - ld [$ffb7],a ; joypad state update flag - ld hl,$d730 - res 6,[hl] ; turn on letter printing delay - jp BankswitchBack -.checkOtherKeys ; check B, SELECT, Up, and Down keys - bit 1,a ; was the B button pressed? - jp nz,ExitListMenu ; if so, exit the menu - bit 2,a ; was the select button pressed? - jp nz,HandleItemListSwapping ; if so, allow the player to swap menu entries - ld b,a - bit 7,b ; was Down pressed? - ld hl,wListScrollOffset - jr z,.upPressed -.downPressed - ld a,[hl] - add a,3 - ld b,a - ld a,[$d12a] ; number of list entries - cp b ; will going down scroll past the Cancel button? - jp c,DisplayListMenuIDLoop - inc [hl] ; if not, go down - jp DisplayListMenuIDLoop -.upPressed - ld a,[hl] - and a - jp z,DisplayListMenuIDLoop - dec [hl] - jp DisplayListMenuIDLoop - -DisplayChooseQuantityMenu:: ; 2d57 (0:2d57) -; text box dimensions/coordinates for just quantity - FuncCoord 15,9 - ld hl,Coord - ld b,1 ; height - ld c,3 ; width - ld a,[wListMenuID] - cp a,PRICEDITEMLISTMENU - jr nz,.drawTextBox -; text box dimensions/coordinates for quantity and price - FuncCoord 7,9 - ld hl,Coord - ld b,1 ; height - ld c,11 ; width -.drawTextBox - call TextBoxBorder - FuncCoord 16,10 - ld hl,Coord - ld a,[wListMenuID] - cp a,PRICEDITEMLISTMENU - jr nz,.printInitialQuantity - FuncCoord 8,10 - ld hl,Coord -.printInitialQuantity - ld de,InitialQuantityText - call PlaceString - xor a - ld [$cf96],a ; initialize current quantity to 0 - jp .incrementQuantity -.waitForKeyPressLoop - call GetJoypadStateLowSensitivity - ld a,[H_NEWLYPRESSEDBUTTONS] ; newly pressed buttons - bit 0,a ; was the A button pressed? - jp nz,.buttonAPressed - bit 1,a ; was the B button pressed? - jp nz,.buttonBPressed - bit 6,a ; was Up pressed? - jr nz,.incrementQuantity - bit 7,a ; was Down pressed? - jr nz,.decrementQuantity - jr .waitForKeyPressLoop -.incrementQuantity - ld a,[$cf97] ; max quantity - inc a - ld b,a - ld hl,$cf96 ; current quantity - inc [hl] - ld a,[hl] - cp b - jr nz,.handleNewQuantity -; wrap to 1 if the player goes above the max quantity - ld a,1 - ld [hl],a - jr .handleNewQuantity -.decrementQuantity - ld hl,$cf96 ; current quantity - dec [hl] - jr nz,.handleNewQuantity -; wrap to the max quantity if the player goes below 1 - ld a,[$cf97] ; max quantity - ld [hl],a -.handleNewQuantity - FuncCoord 17,10 - ld hl,Coord - ld a,[wListMenuID] - cp a,PRICEDITEMLISTMENU - jr nz,.printQuantity -.printPrice - ld c,$03 - ld a,[$cf96] - ld b,a - ld hl,$ff9f ; total price -; initialize total price to 0 - xor a - ld [hli],a - ld [hli],a - ld [hl],a -.addLoop ; loop to multiply the individual price by the quantity to get the total price - ld de,$ffa1 - ld hl,$ff8d - push bc - ld a,$0b - call Predef ; add the individual price to the current sum - pop bc - dec b - jr nz,.addLoop - ld a,[$ff8e] - and a ; should the price be halved (for selling items)? - jr z,.skipHalvingPrice - xor a - ld [$ffa2],a - ld [$ffa3],a - ld a,$02 - ld [$ffa4],a - ld a,$0d - call Predef ; halves the price -; store the halved price - ld a,[$ffa2] - ld [$ff9f],a - ld a,[$ffa3] - ld [$ffa0],a - ld a,[$ffa4] - ld [$ffa1],a -.skipHalvingPrice - FuncCoord 12,10 - ld hl,Coord - ld de,SpacesBetweenQuantityAndPriceText - call PlaceString - ld de,$ff9f ; total price - ld c,$a3 - call PrintBCDNumber - FuncCoord 9,10 - ld hl,Coord -.printQuantity - ld de,$cf96 ; current quantity - ld bc,$8102 ; print leading zeroes, 1 byte, 2 digits - call PrintNumber - jp .waitForKeyPressLoop -.buttonAPressed ; the player chose to make the transaction - xor a - ld [$cc35],a ; 0 means no item is currently being swapped - ret -.buttonBPressed ; the player chose to cancel the transaction - xor a - ld [$cc35],a ; 0 means no item is currently being swapped - ld a,$ff - ret - -InitialQuantityText:: ; 2e30 (0:2e30) - db "×01@" - -SpacesBetweenQuantityAndPriceText:: ; 2e34 (0:2e34) - db " @" - -ExitListMenu:: ; 2e3b (0:2e3b) - ld a,[wCurrentMenuItem] - ld [$d12d],a - ld a,$02 - ld [$d12e],a - ld [$cc37],a - xor a - ld [$ffb7],a - ld hl,$d730 - res 6,[hl] - call BankswitchBack - xor a - ld [$cc35],a ; 0 means no item is currently being swapped - scf - ret - -PrintListMenuEntries:: ; 2e5a (0:2e5a) - FuncCoord 5, 3 ; $c3e1 - ld hl,Coord - ld b,$09 - ld c,$0e - call ClearScreenArea - ld a,[$cf8b] - ld e,a - ld a,[$cf8c] - ld d,a - inc de ; de = beginning of list entries - ld a,[wListScrollOffset] - ld c,a - ld a,[wListMenuID] - cp a,ITEMLISTMENU - ld a,c - jr nz,.skipMultiplying -; if it's an item menu -; item entries are 2 bytes long, so multiply by 2 - sla a - sla c -.skipMultiplying - add e - ld e,a - jr nc,.noCarry - inc d -.noCarry - FuncCoord 6,4 ; coordinates of first list entry name - ld hl,Coord - ld b,4 ; print 4 names -.loop - ld a,b - ld [wWhichPokemon],a - ld a,[de] - ld [$d11e],a - cp a,$ff - jp z,.printCancelMenuItem - push bc - push de - push hl - push hl - push de - ld a,[wListMenuID] - and a - jr z,.pokemonPCMenu - cp a,$01 - jr z,.movesMenu -.itemMenu - call GetItemName - jr .placeNameString -.pokemonPCMenu - push hl - ld hl,W_NUMINPARTY - ld a,[$cf8b] - cp l ; is it a list of party pokemon or box pokemon? - ld hl,W_PARTYMON1NAME - jr z,.getPokemonName - ld hl, W_BOXMON1NAME ; box pokemon names -.getPokemonName - ld a,[wWhichPokemon] - ld b,a - ld a,4 - sub b - ld b,a - ld a,[wListScrollOffset] - add b - call GetPartyMonName - pop hl - jr .placeNameString -.movesMenu - call GetMoveName -.placeNameString - call PlaceString - pop de - pop hl - ld a,[$cf93] - and a ; should prices be printed? - jr z,.skipPrintingItemPrice -.printItemPrice - push hl - ld a,[de] - ld de,ItemPrices - ld [$cf91],a - call GetItemPrice ; get price - pop hl - ld bc,20 + 5 ; 1 row down and 5 columns right - add hl,bc - ld c,$a3 ; no leading zeroes, right-aligned, print currency symbol, 3 bytes - call PrintBCDNumber -.skipPrintingItemPrice - ld a,[wListMenuID] - and a - jr nz,.skipPrintingPokemonLevel -.printPokemonLevel - ld a,[$d11e] - push af - push hl - ld hl,W_NUMINPARTY - ld a,[$cf8b] - cp l ; is it a list of party pokemon or box pokemon? - ld a,$00 - jr z,.next - ld a,$02 -.next - ld [$cc49],a - ld hl,wWhichPokemon - ld a,[hl] - ld b,a - ld a,$04 - sub b - ld b,a - ld a,[wListScrollOffset] - add b - ld [hl],a - call LoadMonData ; load pokemon info - ld a,[$cc49] - and a ; is it a list of party pokemon or box pokemon? - jr z,.skipCopyingLevel -.copyLevel - ld a,[$cf9b] - ld [$cfb9],a -.skipCopyingLevel - pop hl - ld bc,$001c - add hl,bc - call PrintLevel ; print level - pop af - ld [$d11e],a -.skipPrintingPokemonLevel - pop hl - pop de - inc de - ld a,[wListMenuID] - cp a,ITEMLISTMENU - jr nz,.nextListEntry -.printItemQuantity - ld a,[$d11e] - ld [$cf91],a - call IsKeyItem ; check if item is unsellable - ld a,[$d124] - and a ; is the item unsellable? - jr nz,.skipPrintingItemQuantity ; if so, don't print the quantity - push hl - ld bc,20 + 8 ; 1 row down and 8 columns right - add hl,bc - ld a,"×" - ldi [hl],a - ld a,[$d11e] - push af - ld a,[de] - ld [$cf97],a - push de - ld de,$d11e - ld [de],a - ld bc,$0102 - call PrintNumber - pop de - pop af - ld [$d11e],a - pop hl -.skipPrintingItemQuantity - inc de - pop bc - inc c - push bc - inc c - ld a,[$cc35] ; ID of item chosen for swapping (counts from 1) - and a ; is an item being swapped? - jr z,.nextListEntry - sla a - cp c ; is it this item? - jr nz,.nextListEntry - dec hl - ld a,$ec ; unfilled right arrow menu cursor to indicate an item being swapped - ld [hli],a -.nextListEntry - ld bc,2 * 20 ; 2 rows - add hl,bc - pop bc - inc c - dec b - jp nz,.loop - ld bc,-8 - add hl,bc - ld a,$ee ; down arrow - ld [hl],a - ret -.printCancelMenuItem - ld de,ListMenuCancelText - jp PlaceString - -ListMenuCancelText:: ; 2f97 (0:2f97) - db "CANCEL@" - -GetMonName:: ; 2f9e (0:2f9e) - push hl - ld a,[H_LOADEDROMBANK] - push af - ld a,BANK(MonsterNames) ; 07 - ld [H_LOADEDROMBANK],a - ld [$2000],a - ld a,[$d11e] - dec a - ld hl,MonsterNames ; 421E - ld c,10 - ld b,0 - call AddNTimes - ld de,$cd6d - push de - ld bc,10 - call CopyData - ld hl,$cd77 - ld [hl], "@" - pop de - pop af - ld [H_LOADEDROMBANK],a - ld [$2000],a - pop hl - ret - -GetItemName:: ; 2fcf (0:2fcf) -; given an item ID at [$D11E], store the name of the item into a string -; starting at $CD6D - push hl - push bc - ld a,[$D11E] - cp HM_01 ; is this a TM/HM? - jr nc,.Machine - - ld [$D0B5],a - ld a,ITEM_NAME - ld [W_LISTTYPE],a - ld a,BANK(ItemNames) - ld [$D0B7],a - call GetName - jr .Finish - -.Machine - call GetMachineName -.Finish - ld de,$CD6D ; pointer to where item name is stored in RAM - pop bc - pop hl - ret - -GetMachineName:: ; 2ff3 (0:2ff3) -; copies the name of the TM/HM in [$D11E] to $CD6D - push hl - push de - push bc - ld a,[$D11E] - push af - cp TM_01 ; is this a TM? [not HM] - jr nc,.WriteTM -; if HM, then write "HM" and add 5 to the item ID, so we can reuse the -; TM printing code - add 5 - ld [$D11E],a - ld hl,HiddenPrefix ; points to "HM" - ld bc,2 - jr .WriteMachinePrefix -.WriteTM - ld hl,TechnicalPrefix ; points to "TM" - ld bc,2 -.WriteMachinePrefix - ld de,$CD6D - call CopyData - -; now get the machine number and convert it to text - ld a,[$D11E] - sub TM_01 - 1 - ld b,$F6 ; "0" -.FirstDigit - sub 10 - jr c,.SecondDigit - inc b - jr .FirstDigit -.SecondDigit - add 10 - push af - ld a,b - ld [de],a - inc de - pop af - ld b,$F6 ; "0" - add b - ld [de],a - inc de - ld a,"@" - ld [de],a - - pop af - ld [$D11E],a - pop bc - pop de - pop hl - ret - -TechnicalPrefix:: ; 303c (0:303c) - db "TM" -HiddenPrefix:: ; 303e (0:303e) - db "HM" - -; sets carry if item is HM, clears carry if item is not HM -; Input: a = item ID -IsItemHM:: ; 3040 (0:3040) - cp a,HM_01 - jr c,.notHM - cp a,TM_01 - ret -.notHM - and a - ret - -; sets carry if move is an HM, clears carry if move is not an HM -; Input: a = move ID -IsMoveHM:: ; 3049 (0:3049) - ld hl,HMMoves - ld de,1 - jp IsInArray - -HMMoves:: ; 3052 (0:3052) - db CUT,FLY,SURF,STRENGTH,FLASH - db $ff ; terminator - -GetMoveName:: ; 3058 (0:3058) - push hl - ld a,MOVE_NAME - ld [W_LISTTYPE],a - ld a,[$d11e] - ld [$d0b5],a - ld a,BANK(MoveNames) - ld [$d0b7],a - call GetName - ld de,$cd6d ; pointer to where move name is stored in RAM - pop hl - ret - -; reloads text box tile patterns, current map view, and tileset tile patterns -ReloadMapData:: ; 3071 (0:3071) - ld a,[H_LOADEDROMBANK] - push af - ld a,[W_CURMAP] - call SwitchToMapRomBank - call DisableLCD - call LoadTextBoxTilePatterns - call LoadCurrentMapView - call LoadTilesetTilePatternData - call EnableLCD - pop af - ld [H_LOADEDROMBANK],a - ld [$2000],a - ret - -; reloads tileset tile patterns -ReloadTilesetTilePatterns:: ; 3090 (0:3090) - ld a,[H_LOADEDROMBANK] - push af - ld a,[W_CURMAP] - call SwitchToMapRomBank - call DisableLCD - call LoadTilesetTilePatternData - call EnableLCD - pop af - ld [H_LOADEDROMBANK],a - ld [$2000],a - ret - -; shows the town map and lets the player choose a destination to fly to -ChooseFlyDestination:: ; 30a9 (0:30a9) - ld hl,$d72e - res 4,[hl] - ld b, BANK(LoadTownMap_Fly) - ld hl, LoadTownMap_Fly - jp Bankswitch - -; causes the text box to close waithout waiting for a button press after displaying text -DisableWaitingAfterTextDisplay:: ; 30b6 (0:30b6) - ld a,$01 - ld [$cc3c],a - ret - -; uses an item -; UseItem is used with dummy items to perform certain other functions as well -; INPUT: -; [$cf91] = item ID -; OUTPUT: -; [$cd6a] = success -; 00: unsucessful -; 01: successful -; 02: not able to be used right now, no extra menu displayed (only certain items use this) -UseItem:: ; 30bc (0:30bc) - ld b,BANK(UseItem_) - ld hl,UseItem_ - jp Bankswitch - -; confirms the item toss and then tosses the item -; INPUT: -; hl = address of inventory (either wNumBagItems or wNumBoxItems) -; [$cf91] = item ID -; [$cf92] = index of item within inventory -; [$cf96] = quantity to toss -; OUTPUT: -; clears carry flag if the item is tossed, sets carry flag if not -TossItem:: ; 30c4 (0:30c4) - ld a,[H_LOADEDROMBANK] - push af - ld a,BANK(TossItem_) - ld [H_LOADEDROMBANK],a - ld [$2000],a - call TossItem_ - pop de - ld a,d - ld [H_LOADEDROMBANK],a - ld [$2000],a - ret - -; checks if an item is a key item -; INPUT: -; [$cf91] = item ID -; OUTPUT: -; [$d124] = result -; 00: item is not key item -; 01: item is key item -IsKeyItem:: ; 30d9 (0:30d9) - push hl - push de - push bc - callba IsKeyItem_ - pop bc - pop de - pop hl - ret - -; function to draw various text boxes -; INPUT: -; [$D125] = text box ID -DisplayTextBoxID:: ; 30e8 (0:30e8) - ld a,[H_LOADEDROMBANK] - push af - ld a,BANK(DisplayTextBoxID_) - ld [H_LOADEDROMBANK],a - ld [$2000],a - call DisplayTextBoxID_ - pop bc - ld a,b - ld [H_LOADEDROMBANK],a - ld [$2000],a - ret - -Func_30fd:: ; 30fd (0:30fd) - ld a, [$cc57] - and a - ret nz - ld a, [$d736] - bit 1, a - ret nz - ld a, [$d730] - and $80 - ret - -Func_310e:: ; 310e (0:310e) - ld hl, $d736 - bit 0, [hl] - res 0, [hl] - jr nz, .asm_3146 - ld a, [$cc57] - and a - ret z - dec a - add a - ld d, $0 - ld e, a - ld hl, .pointerTable_3140 - add hl, de - ld a, [hli] - ld h, [hl] - ld l, a - ld a, [H_LOADEDROMBANK] - push af - ld a, [$cc58] - ld [H_LOADEDROMBANK], a - ld [$2000], a - ld a, [$cf10] - call CallFunctionInTable - pop af - ld [H_LOADEDROMBANK], a - ld [$2000], a - ret -.pointerTable_3140 - dw PointerTable_1a442 - dw PointerTable_1a510 - dw PointerTable_1a57d -.asm_3146 - ld b, BANK(Func_1a3e0) - ld hl, Func_1a3e0 - jp Bankswitch - -Func_314e:: ; 314e (0:314e) - ld b, BANK(Func_1a41d) - ld hl, Func_1a41d - jp Bankswitch - -Func_3156:: ; 3156 (0:3156) - ret - -; stores hl in [W_TRAINERHEADERPTR] -StoreTrainerHeaderPointer:: ; 3157 (0:3157) - ld a, h - ld [W_TRAINERHEADERPTR], a - ld a, l - ld [W_TRAINERHEADERPTR+1], a - ret - -; executes the current map script from the function pointer array provided in hl. -; a: map script index to execute (unless overridden by [$d733] bit 4) -ExecuteCurMapScriptInTable:: ; 3160 (0:3160) - push af - push de - call StoreTrainerHeaderPointer - pop hl - pop af - push hl - ld hl, W_FLAGS_D733 - bit 4, [hl] - res 4, [hl] - jr z, .useProvidedIndex ; test if map script index was overridden manually - ld a, [W_CURMAPSCRIPT] -.useProvidedIndex - pop hl - ld [W_CURMAPSCRIPT], a - call CallFunctionInTable - ld a, [W_CURMAPSCRIPT] - ret - -LoadGymLeaderAndCityName:: ; 317f (0:317f) - push de - ld de, wGymCityName - ld bc, $11 - call CopyData ; load city name - pop hl - ld de, wGymLeaderName - ld bc, $b - jp CopyData ; load gym leader name - -; reads specific information from trainer header (pointed to at W_TRAINERHEADERPTR) -; a: offset in header data -; 0 -> flag's bit (into wTrainerHeaderFlagBit) -; 2 -> flag's byte ptr (into hl) -; 4 -> before battle text (into hl) -; 6 -> after battle text (into hl) -; 8 -> end battle text (into hl) -ReadTrainerHeaderInfo:: ; 3193 (0:3193) - push de - push af - ld d, $0 - ld e, a - ld hl, W_TRAINERHEADERPTR - ld a, [hli] - ld l, [hl] - ld h, a - add hl, de - pop af - and a - jr nz, .nonZeroOffset - ld a, [hl] - ld [wTrainerHeaderFlagBit], a ; store flag's bit - jr .done -.nonZeroOffset - cp $2 - jr z, .readPointer ; read flag's byte ptr - cp $4 - jr z, .readPointer ; read before battle text - cp $6 - jr z, .readPointer ; read after battle text - cp $8 - jr z, .readPointer ; read end battle text - cp $a - jr nz, .done - ld a, [hli] ; read end battle text (2) but override the result afterwards (XXX why, bug?) - ld d, [hl] - ld e, a - jr .done -.readPointer - ld a, [hli] - ld h, [hl] - ld l, a -.done - pop de - ret - -TrainerFlagAction:: - ld a, $10 ; FlagActionPredef - jp Predef - -; direct talking to a trainer (rather than getting seen by one) -TalkToTrainer:: ; 31cc (0:31cc) - call StoreTrainerHeaderPointer - xor a - call ReadTrainerHeaderInfo ; read flag's bit - ld a, $2 - call ReadTrainerHeaderInfo ; read flag's byte ptr - ld a, [wTrainerHeaderFlagBit] - ld c, a - ld b, $2 - call TrainerFlagAction ; read trainer's flag - ld a, c - and a - jr z, .trainerNotYetFought ; test trainer's flag - ld a, $6 - call ReadTrainerHeaderInfo ; print after battle text - jp PrintText -.trainerNotYetFought ; 0x31ed - ld a, $4 - call ReadTrainerHeaderInfo ; print before battle text - call PrintText - ld a, $a - call ReadTrainerHeaderInfo ; (?) does nothing apparently (maybe bug in ReadTrainerHeaderInfo) - push de - ld a, $8 - call ReadTrainerHeaderInfo ; read end battle text - pop de - call PreBattleSaveRegisters - ld hl, W_FLAGS_D733 - set 4, [hl] ; activate map script index override (index is set below) - ld hl, wFlags_0xcd60 - bit 0, [hl] ; test if player is already being engaged by another trainer - ret nz - call EngageMapTrainer - ld hl, W_CURMAPSCRIPT - inc [hl] ; progress map script index (assuming it was 0 before) to start pre-battle routines - jp Func_325d - -; checks if any trainers are seeing the player and wanting to fight -CheckFightingMapTrainers:: ; 3219 (0:3219) - call CheckForEngagingTrainers - ld a, [$cf13] - cp $ff - jr nz, .trainerEngaging - xor a - ld [$cf13], a - ld [wTrainerHeaderFlagBit], a - ret -.trainerEngaging - ld hl, W_FLAGS_D733 - set 3, [hl] - ld [$cd4f], a - xor a - ld [$cd50], a - ld a, $4c - call Predef - ld a, D_RIGHT | D_LEFT | D_UP | D_DOWN - ld [wJoypadForbiddenButtonsMask], a - xor a - ldh [$b4], a - call TrainerWalkUpToPlayer_Bank0 - ld hl, W_CURMAPSCRIPT - inc [hl] ; progress to battle phase 1 (engaging) - ret - -Func_324c:: ; 324c (0:324c) - ld a, [$d730] - and $1 - ret nz - ld [wJoypadForbiddenButtonsMask], a - ld a, [$cf13] - ld [H_DOWNARROWBLINKCNT2], a ; $ff8c - call DisplayTextID - -Func_325d:: ; 325d (0:325d) - xor a - ld [wJoypadForbiddenButtonsMask], a - call InitBattleEnemyParameters - ld hl, $d72d - set 6, [hl] - set 7, [hl] - ld hl, $d72e - set 1, [hl] - ld hl, W_CURMAPSCRIPT - inc [hl] ; progress to battle phase 2 (battling) - ret - -EndTrainerBattle:: ; 3275 (0:3275) - ld hl, $d126 - set 5, [hl] - set 6, [hl] - ld hl, $d72d - res 7, [hl] - ld hl, wFlags_0xcd60 - res 0, [hl] ; player is no longer engaged by any trainer - ld a, [W_ISINBATTLE] ; $d057 - cp $ff - jp z, ResetButtonPressedAndMapScript - ld a, $2 - call ReadTrainerHeaderInfo - ld a, [wTrainerHeaderFlagBit] - ld c, a - ld b, $1 - call TrainerFlagAction ; flag trainer as fought - ld a, [W_ENEMYMONORTRAINERCLASS] - cp $c8 - jr nc, .skipRemoveSprite ; test if trainer was fought (in that case skip removing the corresponding sprite) - ld hl, W_MISSABLEOBJECTLIST - ld de, $2 - ld a, [$cf13] - call IsInArray ; search for sprite ID - inc hl - ld a, [hl] - ld [$cc4d], a ; load corresponding missable object index and remove it - ld a, $11 - call Predef ; indirect jump to RemoveMissableObject (f1d7 (3:71d7)) -.skipRemoveSprite - ld hl, $d730 - bit 4, [hl] - res 4, [hl] - ret nz - -ResetButtonPressedAndMapScript:: ; 32c1 (0:32c1) - xor a - ld [wJoypadForbiddenButtonsMask], a - ld [H_CURRENTPRESSEDBUTTONS], a - ld [H_NEWLYPRESSEDBUTTONS], a - ld [H_NEWLYRELEASEDBUTTONS], a - ld [W_CURMAPSCRIPT], a ; reset battle status - ret - -; calls TrainerWalkUpToPlayer -TrainerWalkUpToPlayer_Bank0:: ; 32cf (0:32cf) - ld b, BANK(TrainerWalkUpToPlayer) - ld hl, TrainerWalkUpToPlayer - jp Bankswitch - -; sets opponent type and mon set/lvl based on the engaging trainer data -InitBattleEnemyParameters:: ; 32d7 (0:32d7) - ld a, [wEngagedTrainerClass] - ld [W_CUROPPONENT], a ; $d059 - ld [W_ENEMYMONORTRAINERCLASS], a - cp $c8 - ld a, [wEngagedTrainerSet] ; $cd2e - jr c, .noTrainer - ld [W_TRAINERNO], a ; $d05d - ret -.noTrainer - ld [W_CURENEMYLVL], a ; $d127 - ret - -Func_32ef:: ; 32ef (0:32ef) - ld hl, Func_567f9 - jr asm_3301 - -Func_32f4:: ; 32f4 (0:32f4) - ld hl, Func_56819 - jr asm_3301 ; 0x32f7 $8 - -Func_32f9:: ; 32f9 (0:32f9) - ld hl, Func_5683d - jr asm_3301 - -Func_32fe:: ; 32fe (0:32fe) - ld hl, Func_5685d -asm_3301:: ; 3301 (0:3301) - ld b, BANK(Func_567f9) ; BANK(Func_56819), BANK(Func_5683d), BANK(Func_5685d) - jp Bankswitch ; indirect jump to one of the four functions - -CheckForEngagingTrainers:: ; 3306 (0:3306) - xor a - call ReadTrainerHeaderInfo ; read trainer flag's bit (unused) - ld d, h ; store trainer header address in de - ld e, l -.trainerLoop - call StoreTrainerHeaderPointer ; set trainer header pointer to current trainer - ld a, [de] - ld [$cf13], a ; store trainer flag's bit - ld [wTrainerHeaderFlagBit], a - cp $ff - ret z - ld a, $2 - call ReadTrainerHeaderInfo ; read trainer flag's byte ptr - ld b, $2 - ld a, [wTrainerHeaderFlagBit] - ld c, a - call TrainerFlagAction ; read trainer flag - ld a, c - and a - jr nz, .trainerAlreadyFought - push hl - push de - push hl - xor a - call ReadTrainerHeaderInfo ; get trainer header pointer - inc hl - ld a, [hl] ; read trainer engage distance - pop hl - ld [wTrainerEngageDistance], a - ld a, [$cf13] - swap a - ld [wTrainerSpriteOffset], a ; $cd3d - ld a, $39 - call Predef ; indirect jump to CheckEngagePlayer (5690f (15:690f)) - pop de - pop hl - ld a, [wTrainerSpriteOffset] ; $cd3d - and a - ret nz ; break if the trainer is engaging -.trainerAlreadyFought - ld hl, $c - add hl, de - ld d, h - ld e, l - jr .trainerLoop - -; saves loaded rom bank and hl as well as de registers -PreBattleSaveRegisters:: ; 3354 (0:3354) - ld a, [H_LOADEDROMBANK] - ld [W_PBSTOREDROMBANK], a - ld a, h - ld [W_PBSTOREDREGISTERH], a - ld a, l - ld [W_PBSTOREDREGISTERL], a - ld a, d - ld [W_PBSTOREDREGISTERD], a - ld a, e - ld [W_PBSTOREDREGISTERE], a - ret - -; loads data of some trainer on the current map and plays pre-battle music -; [$cf13]: sprite ID of trainer who is engaged -EngageMapTrainer:: ; 336a (0:336a) - ld hl, W_MAPSPRITEEXTRADATA - ld d, $0 - ld a, [$cf13] - dec a - add a - ld e, a - add hl, de ; seek to engaged trainer data - ld a, [hli] ; load trainer class - ld [wEngagedTrainerClass], a - ld a, [hl] ; load trainer mon set - ld [wEnemyMonAttackMod], a ; $cd2e - jp PlayTrainerMusic - -Func_3381:: ; 3381 (0:3381) - push hl - ld hl, $d72d - bit 7, [hl] - res 7, [hl] - pop hl - ret z - ld a, [H_LOADEDROMBANK] - push af - ld a, [W_PBSTOREDROMBANK] - ld [H_LOADEDROMBANK], a - ld [$2000], a - push hl - callba SaveTrainerName - ld hl, TrainerNameText - call PrintText - pop hl - pop af - ld [H_LOADEDROMBANK], a - ld [$2000], a - callba Func_1a5e7 - jp WaitForSoundToFinish - -Func_33b7:: ; 33b7 (0:33b7) - ld a, [$cf0b] - and a - jr nz, .asm_33c6 - ld a, [W_PBSTOREDREGISTERH] - ld h, a - ld a, [W_PBSTOREDREGISTERL] - ld l, a - ret -.asm_33c6 - ld a, [W_PBSTOREDREGISTERD] - ld h, a - ld a, [W_PBSTOREDREGISTERE] - ld l, a - ret - -TrainerNameText:: ; 33cf (0:33cf) - TX_FAR _TrainerNameText - db $08 - -Func_33d4:: ; 33d4 (0:33d4) - call Func_33b7 - call TextCommandProcessor - jp TextScriptEnd - -Func_33dd:: ; 33dd (0:33dd) - ld a, [wFlags_0xcd60] - bit 0, a - ret nz - call EngageMapTrainer - xor a - ret - -PlayTrainerMusic:: ; 33e8 (0:33e8) - ld a, [wEngagedTrainerClass] - cp $c8 + SONY1 - ret z - cp $c8 + SONY2 - ret z - cp $c8 + SONY3 - ret z - ld a, [W_GYMLEADERNO] ; $d05c - and a - ret nz - xor a - ld [wMusicHeaderPointer], a - ld a, $ff - call PlaySound ; stop music - ld a, BANK(Music_MeetEvilTrainer) - ld [$c0ef], a - ld [$c0f0], a - ld a, [wEngagedTrainerClass] - ld b, a - ld hl, EvilTrainerList -.evilTrainerListLoop - ld a, [hli] - cp $ff - jr z, .noEvilTrainer - cp b - jr nz, .evilTrainerListLoop - ld a, MUSIC_MEET_EVIL_TRAINER - jr .PlaySound -.noEvilTrainer - ld hl, FemaleTrainerList -.femaleTrainerListLoop - ld a, [hli] - cp $ff - jr z, .maleTrainer - cp b - jr nz, .femaleTrainerListLoop - ld a, MUSIC_MEET_FEMALE_TRAINER - jr .PlaySound -.maleTrainer - ld a, MUSIC_MEET_MALE_TRAINER -.PlaySound - ld [$c0ee], a - jp PlaySound - -INCLUDE "data/trainer_types.asm" - -Func_3442:: ; 3442 (0:3442) - ld a, [hli] - cp $ff - ret z - cp b - jr nz, .asm_345b - ld a, [hli] - cp c - jr nz, .asm_345c - ld a, [hli] - ld d, [hl] - ld e, a - ld hl, $ccd3 - call DecodeRLEList - dec a - ld [$cd38], a - ret -.asm_345b - inc hl -.asm_345c - inc hl - inc hl - jr Func_3442 - -FuncTX_ItemStoragePC:: ; 3460 (0:3460) - call SaveScreenTilesToBuffer2 - ld b, BANK(PlayerPC) - ld hl, PlayerPC - jr bankswitchAndContinue - -FuncTX_BillsPC:: ; 346a (0:346a) - call SaveScreenTilesToBuffer2 - ld b, BANK(Func_214c2) - ld hl, Func_214c2 - jr bankswitchAndContinue - -FuncTX_SlotMachine:: ; 3474 (0:3474) -; XXX find a better name for this function -; special_F7 - ld b,BANK(CeladonPrizeMenu) - ld hl,CeladonPrizeMenu -bankswitchAndContinue:: ; 3479 (0:3479) - call Bankswitch - jp HoldTextDisplayOpen ; continue to main text-engine function - -FuncTX_PokemonCenterPC:: ; 347f (0:347f) - ld b, BANK(ActivatePC) - ld hl, ActivatePC - jr bankswitchAndContinue - -Func_3486:: ; 3486 (0:3486) - xor a - ld [$cd3b], a - ld [$c206], a - ld hl, $d730 - set 7, [hl] - ret - -IsItemInBag:: ; 3493 (0:3493) -; given an item_id in b -; set zero flag if item isn't in player's bag -; else reset zero flag -; related to Pokémon Tower and ghosts - ld a,$1C - call Predef - ld a,b - and a - ret - -DisplayPokedex:: ; 349b (0:349b) - ld [$d11e], a - ld b, BANK(Func_7c18) - ld hl, Func_7c18 - jp Bankswitch - -Func_34a6:: ; 34a6 (0:34a6) - call Func_34ae - ld c, $6 - jp DelayFrames - -Func_34ae:: ; 34ae (0:34ae) - ld a, $9 - ld [H_DOWNARROWBLINKCNT1], a ; $ff8b - call Func_34fc - ld a, [$ff8d] - ld [hl], a - ret - -Func_34b9:: ; 34b9 (0:34b9) - ld de, $fff9 - add hl, de - ld [hl], a - ret - -; tests if the player's coordinates are in a specified array -; INPUT: -; hl = address of array -; OUTPUT: -; [$cd3d] = if there is match, the matching array index -; sets carry if the coordinates are in the array, clears carry if not -ArePlayerCoordsInArray:: ; 34bf (0:34bf) - ld a,[W_YCOORD] - ld b,a - ld a,[W_XCOORD] - ld c,a - ; fallthrough - -CheckCoords:: ; 34c7 (0:34c7) - xor a - ld [$cd3d],a -.loop - ld a,[hli] - cp a,$ff ; reached terminator? - jr z,.notInArray - push hl - ld hl,$cd3d - inc [hl] - pop hl -.compareYCoord - cp b - jr z,.compareXCoord - inc hl - jr .loop -.compareXCoord - ld a,[hli] - cp c - jr nz,.loop -.inArray - scf - ret -.notInArray - and a - ret - -; tests if a boulder's coordinates are in a specified array -; INPUT: -; hl = address of array -; ff8c = which boulder to check? XXX -; OUTPUT: -; [$cd3d] = if there is match, the matching array index -; sets carry if the coordinates are in the array, clears carry if not -CheckBoulderCoords:: ; 34e4 (0:34e4) - push hl - ld hl, $c204 - ld a, [$ff8c] - swap a - ld d, $0 - ld e, a - add hl, de - ld a, [hli] - sub $4 ; because sprite coordinates are offset by 4 - ld b, a - ld a, [hl] - sub $4 ; because sprite coordinates are offset by 4 - ld c, a - pop hl - jp CheckCoords - -Func_34fc:: ; 34fc (0:34fc) - ld h, $c1 - jr asm_3502 - -Func_3500:: ; 3500 (0:3500) - ld h, $c2 -asm_3502:: ; 3502 (0:3502) - ld a, [H_DOWNARROWBLINKCNT1] ; $ff8b - ld b, a - ld a, [H_DOWNARROWBLINKCNT2] ; $ff8c - swap a - add b - ld l, a - ret - -; decodes a $ff-terminated RLEncoded list -; each entry is a pair of bytes <byte value> <repetitions> -; the final $ff will be replicated in the output list and a contains the number of bytes written -; de: input list -; hl: output list -DecodeRLEList:: ; 350c (0:350c) - xor a - ld [wRLEByteCount], a ; count written bytes here -.listLoop - ld a, [de] - cp $ff - jr z, .endOfList - ld [H_DOWNARROWBLINKCNT1], a ; store byte value to be written - inc de - ld a, [de] - ld b, $0 - ld c, a ; number of bytes to be written - ld a, [wRLEByteCount] - add c - ld [wRLEByteCount], a ; update total number of written bytes - ld a, [H_DOWNARROWBLINKCNT1] ; $ff8b - call FillMemory ; write a c-times to output - inc de - jr .listLoop -.endOfList - ld a, $ff - ld [hl], a ; write final $ff - ld a, [wRLEByteCount] - inc a ; include sentinel in counting - ret - -; sets movement byte 1 for sprite [$FF8C] to $FE and byte 2 to [$FF8D] -SetSpriteMovementBytesToFE:: ; 3533 (0:3533) - push hl - call GetSpriteMovementByte1Pointer - ld [hl], $fe - call GetSpriteMovementByte2Pointer - ld a, [$ff8d] - ld [hl], a - pop hl - ret - -; sets both movement bytes for sprite [$FF8C] to $FF -SetSpriteMovementBytesToFF:: ; 3541 (0:3541) - push hl - call GetSpriteMovementByte1Pointer - ld [hl],$FF - call GetSpriteMovementByte2Pointer - ld [hl],$FF ; prevent person from walking? - pop hl - ret - -; returns the sprite movement byte 1 pointer for sprite [$FF8C] in hl -GetSpriteMovementByte1Pointer:: ; 354e (0:354e) - ld h,$C2 - ld a,[$FF8C] ; the sprite to move - swap a - add a,6 - ld l,a - ret - -; returns the sprite movement byte 2 pointer for sprite [$FF8C] in hl -GetSpriteMovementByte2Pointer:: ; 3558 (0:3558) - push de - ld hl,W_MAPSPRITEDATA - ld a,[$FF8C] ; the sprite to move - dec a - add a - ld d,0 - ld e,a - add hl,de - pop de - ret - -GetTrainerInformation:: ; 3566 (0:3566) - call GetTrainerName - ld a, [W_ISLINKBATTLE] ; $d12b - and a - jr nz, .linkBattle - ld a, Bank(TrainerPicAndMoneyPointers) - call BankswitchHome - ld a, [W_TRAINERCLASS] ; $d031 - dec a - ld hl, TrainerPicAndMoneyPointers - ld bc, $5 - call AddNTimes - ld de, $d033 - ld a, [hli] - ld [de], a - inc de - ld a, [hli] - ld [de], a - ld de, $d046 - ld a, [hli] - ld [de], a - inc de - ld a, [hli] - ld [de], a - jp BankswitchBack -.linkBattle - ld hl, $d033 - ld de, RedPicFront - ld [hl], e - inc hl - ld [hl], d - ret - -GetTrainerName:: ; 359e (0:359e) - ld b, BANK(GetTrainerName_) - ld hl, GetTrainerName_ - jp Bankswitch - -; tests if player's money are at least as much as [$ff9f] -; sets carry flag if not enough money -; sets zero flag if amounts match exactly -HasEnoughMoney:: ; 35a6 (0:35a6) - ld de, wPlayerMoney ; $d347 - ld hl, $ff9f - ld c, $3 - jp StringCmp - -; tests if player's game corner coins are at least as many as [$ffa0] -; sets carry flag if not enough coins -; sets zero flag if amounts match exactly -HasEnoughCoins:: ; 35b1 (0:35b1) - ld de, wPlayerCoins - ld hl, $ffa0 - ld c, $2 - jp StringCmp - -BankswitchHome:: ; 35bc (0:35bc) -; switches to bank # in a -; Only use this when in the home bank! - ld [$CF09],a - ld a,[H_LOADEDROMBANK] - ld [$CF08],a - ld a,[$CF09] - ld [H_LOADEDROMBANK],a - ld [$2000],a - ret - -BankswitchBack:: ; 35cd (0:35cd) -; returns from BankswitchHome - ld a,[$CF08] - ld [H_LOADEDROMBANK],a - ld [$2000],a - ret - -Bankswitch:: ; 35d6 (0:35d6) -; self-contained bankswitch, use this when not in the home bank -; switches to the bank in b - ld a,[H_LOADEDROMBANK] - push af - ld a,b - ld [H_LOADEDROMBANK],a - ld [$2000],a - ld bc,.Return - push bc - jp [hl] -.Return - pop bc - ld a,b - ld [H_LOADEDROMBANK],a - ld [$2000],a - ret - -; displays yes/no choice -; yes -> set carry -YesNoChoice:: ; 35ec (0:35ec) - call SaveScreenTilesToBuffer1 - call InitYesNoTextBoxParameters - jr DisplayYesNoChoice - -Func_35f4:: ; 35f4 (0:35f4) - ld a, $14 - ld [$d125], a - call InitYesNoTextBoxParameters - jp DisplayTextBoxID - -InitYesNoTextBoxParameters:: ; 35ff (0:35ff) - xor a - ld [$d12c], a - FuncCoord 14, 7 ; $c43a - ld hl, Coord - ld bc, $80f - ret - -YesNoChoicePokeCenter:: ; 360a (0:360a) - call SaveScreenTilesToBuffer1 - ld a, $6 - ld [$d12c], a - FuncCoord 11, 6 ; $c423 - ld hl, Coord - ld bc, $80c - jr DisplayYesNoChoice - -Func_361a:: ; 361a (0:361a) - call SaveScreenTilesToBuffer1 - ld a, $3 - ld [$d12c], a - FuncCoord 12, 7 ; $c438 - ld hl, Coord - ld bc, $080d -DisplayYesNoChoice:: ; 3628 (0:3628) - ld a, $14 - ld [$d125], a - call DisplayTextBoxID - jp LoadScreenTilesFromBuffer1 - -; calculates the difference |a-b|, setting carry flag if a<b -CalcDifference:: ; 3633 (0:3633) - sub b - ret nc - cpl - add $1 - scf - ret - -MoveSprite:: ; 363a (0:363a) -; move the sprite [$FF8C] with the movement pointed to by de -; actually only copies the movement data to $CC5B for later - call SetSpriteMovementBytesToFF -MoveSprite_:: ; 363d (0:363d) - push hl - push bc - call GetSpriteMovementByte1Pointer - xor a - ld [hl],a - ld hl,$CC5B - ld c,0 - -.loop - ld a,[de] - ld [hli],a - inc de - inc c - cp a,$FF ; have we reached the end of the movement data? - jr nz,.loop - - ld a,c - ld [$CF0F],a ; number of steps taken - - pop bc - ld hl,$D730 - set 0,[hl] - pop hl - xor a - ld [$CD3B],a - ld [$CCD3],a - dec a - ld [wJoypadForbiddenButtonsMask],a - ld [$CD3A],a - ret - -Func_366b:: ; 366b (0:366b) - push hl - ld hl, $ffe7 - xor a - ld [hld], a - ld a, [hld] - and a - jr z, .asm_367e - ld a, [hli] -.asm_3676 - sub [hl] - jr c, .asm_367e - inc hl - inc [hl] - dec hl - jr .asm_3676 -.asm_367e - pop hl - ret - -; copies the tile patterns for letters and numbers into VRAM -LoadFontTilePatterns:: ; 3680 (0:3680) - ld a,[rLCDC] - bit 7,a ; is the LCD enabled? - jr nz,.lcdEnabled -.lcdDisabled - ld hl,FontGraphics - ld de,$8800 - ld bc,$400 - ld a,BANK(FontGraphics) - jp FarCopyDataDouble ; if LCD is off, transfer all at once -.lcdEnabled - ld de,FontGraphics - ld hl,$8800 - ld bc,(BANK(FontGraphics) << 8 | $80) - jp CopyVideoDataDouble ; if LCD is on, transfer during V-blank - -; copies the text box tile patterns into VRAM -LoadTextBoxTilePatterns:: ; 36a0 (0:36a0) - ld a,[rLCDC] - bit 7,a ; is the LCD enabled? - jr nz,.lcdEnabled -.lcdDisabled - ld hl,TextBoxGraphics - ld de,$9600 - ld bc,$0200 - ld a,BANK(TextBoxGraphics) - jp FarCopyData2 ; if LCD is off, transfer all at once -.lcdEnabled - ld de,TextBoxGraphics - ld hl,$9600 - ld bc,(BANK(TextBoxGraphics) << 8 | $20) - jp CopyVideoData ; if LCD is on, transfer during V-blank - -; copies HP bar and status display tile patterns into VRAM -LoadHpBarAndStatusTilePatterns:: ; 36c0 (0:36c0) - ld a,[rLCDC] - bit 7,a ; is the LCD enabled? - jr nz,.lcdEnabled -.lcdDisabled - ld hl,HpBarAndStatusGraphics - ld de,$9620 - ld bc,$01e0 - ld a,BANK(HpBarAndStatusGraphics) - jp FarCopyData2 ; if LCD is off, transfer all at once -.lcdEnabled - ld de,HpBarAndStatusGraphics - ld hl,$9620 - ld bc,(BANK(HpBarAndStatusGraphics) << 8 | $1e) - jp CopyVideoData ; if LCD is on, transfer during V-blank - -;Fills memory range with the specified byte. -;input registers a = fill_byte, bc = length, hl = address -FillMemory:: ; 36e0 (0:36e0) - push de - ld d, a -.loop - ld a, d - ldi [hl], a - dec bc - ld a, b - or c - jr nz, .loop - pop de - ret - -; loads sprite that de points to -; bank of sprite is given in a -UncompressSpriteFromDE:: ; 36eb (0:36eb) - ld hl, W_SPRITEINPUTPTR - ld [hl], e - inc hl - ld [hl], d - jp UncompressSpriteData - -SaveScreenTilesToBuffer2:: ; 36f4 (0:36f4) - ld hl, wTileMap - ld de, wTileMapBackup2 - ld bc, $168 - call CopyData - ret - -LoadScreenTilesFromBuffer2:: ; 3701 (0:3701) - call LoadScreenTilesFromBuffer2DisableBGTransfer - ld a, $1 - ld [H_AUTOBGTRANSFERENABLED], a ; $ffba - ret - -; loads screen tiles stored in wTileMapBackup2 but leaves H_AUTOBGTRANSFERENABLED disabled -LoadScreenTilesFromBuffer2DisableBGTransfer:: ; 3709 (0:3709) - xor a - ld [H_AUTOBGTRANSFERENABLED], a ; $ffba - ld hl, wTileMapBackup2 - ld de, wTileMap - ld bc, $168 - call CopyData - ret - -SaveScreenTilesToBuffer1:: ; 3719 (0:3719) - ld hl, wTileMap - ld de, wTileMapBackup - ld bc, $168 - jp CopyData - -LoadScreenTilesFromBuffer1:: ; 3725 (0:3725) - xor a - ld [H_AUTOBGTRANSFERENABLED], a ; $ffba - ld hl, wTileMapBackup - ld de, wTileMap - ld bc, $168 - call CopyData - ld a, $1 - ld [H_AUTOBGTRANSFERENABLED], a ; $ffba - ret - -DelayFrames:: ; 3739 (0:3739) -; wait n frames, where n is the value in c - call DelayFrame - dec c - jr nz,DelayFrames - ret - -PlaySoundWaitForCurrent:: ; 3740 (0:3740) - push af - call WaitForSoundToFinish - pop af - jp PlaySound - -; Wait for sound to finish playing -WaitForSoundToFinish:: ; 3748 (0:3748) - ld a, [$d083] - and $80 - ret nz - push hl -.asm_374f - ld hl, $c02a - xor a - or [hl] - inc hl - or [hl] - inc hl - inc hl - or [hl] - jr nz, .asm_374f - pop hl - ret - -NamePointers:: ; 375d (0:375d) - dw MonsterNames - dw MoveNames - dw UnusedNames - dw ItemNames - dw W_PARTYMON1OT ; player's OT names list - dw W_ENEMYMON1OT ; enemy's OT names list - dw TrainerNames - -GetName:: ; 376b (0:376b) -; arguments: -; [$D0B5] = which name -; [$D0B6] = which list (W_LISTTYPE) -; [$D0B7] = bank of list -; -; returns pointer to name in de - ld a,[$d0b5] - ld [$d11e],a - cp a,$C4 ;it's TM/HM - jp nc,GetMachineName - ld a,[H_LOADEDROMBANK] - push af - push hl - push bc - push de - ld a,[W_LISTTYPE] ;List3759_entrySelector - dec a - jr nz,.otherEntries - ;1 = MON_NAMES - call GetMonName - ld hl,11 - add hl,de - ld e,l - ld d,h - jr .gotPtr -.otherEntries ; $378d - ;2-7 = OTHER ENTRIES - ld a,[$d0b7] - ld [H_LOADEDROMBANK],a - ld [$2000],a - ld a,[W_LISTTYPE] ;VariousNames' entryID - dec a - add a - ld d,0 - ld e,a - jr nc,.skip - inc d -.skip ; $37a0 - ld hl,NamePointers - add hl,de - ld a,[hli] - ld [$ff96],a - ld a,[hl] - ld [$ff95],a - ld a,[$ff95] - ld h,a - ld a,[$ff96] - ld l,a - ld a,[$d0b5] - ld b,a - ld c,0 -.nextName - ld d,h - ld e,l -.nextChar - ld a,[hli] - cp a, "@" - jr nz,.nextChar - inc c ;entry counter - ld a,b ;wanted entry - cp c - jr nz,.nextName - ld h,d - ld l,e - ld de,$cd6d - ld bc,$0014 - call CopyData -.gotPtr ; $37cd - ld a,e - ld [$cf8d],a - ld a,d - ld [$cf8e],a - pop de - pop bc - pop hl - pop af - ld [H_LOADEDROMBANK],a - ld [$2000],a - ret - -GetItemPrice:: ; 37df (0:37df) - ld a, [H_LOADEDROMBANK] - push af - ld a, [wListMenuID] ; $cf94 - cp $1 - ld a, $1 ; hardcoded Bank - jr nz, .asm_37ed - ld a, $f ; hardcoded Bank -.asm_37ed - ld [H_LOADEDROMBANK], a - ld [$2000], a - ld hl, $cf8f - ld a, [hli] - ld h, [hl] - ld l, a - ld a, [$cf91] - cp HM_01 - jr nc, .asm_3812 - ld bc, $3 -.asm_3802 - add hl, bc - dec a - jr nz, .asm_3802 - dec hl - ld a, [hld] - ld [$ff8d], a - ld a, [hld] - ld [H_DOWNARROWBLINKCNT2], a ; $ff8c - ld a, [hl] - ld [H_DOWNARROWBLINKCNT1], a ; $ff8b - jr .asm_381c -.asm_3812 - ld a, Bank(GetMachinePrice) - ld [H_LOADEDROMBANK], a - ld [$2000], a - call GetMachinePrice -.asm_381c - ld de, H_DOWNARROWBLINKCNT1 ; $ff8b - pop af - ld [H_LOADEDROMBANK], a - ld [$2000], a - ret - -; copies a string from [de] to [$cf4b] -CopyStringToCF4B:: ; 3826 (0:3826) - ld hl, $cf4b - ; fall through - -; copies a string from [de] to [hl] -CopyString:: ; 3829 (0:3829) - ld a, [de] - inc de - ld [hli], a - cp "@" - jr nz, CopyString - ret - -; this function is used when lower button sensitivity is wanted (e.g. menus) -; OUTPUT: [$ffb5] = pressed buttons in usual format -; there are two flags that control its functionality, [$ffb6] and [$ffb7] -; there are esentially three modes of operation -; 1. Get newly pressed buttons only -; ([$ffb7] == 0, [$ffb6] == any) -; Just copies [H_NEWLYPRESSEDBUTTONS] to [$ffb5]. -; 2. Get currently pressed buttons at low sample rate with delay -; ([$ffb7] == 1, [$ffb6] != 0) -; If the user holds down buttons for more than half a second, -; report buttons as being pressed up to 12 times per second thereafter. -; If the user holds down buttons for less than half a second, -; report only one button press. -; 3. Same as 2, but report no buttons as pressed if A or B is held down. -; ([$ffb7] == 1, [$ffb6] == 0) -GetJoypadStateLowSensitivity:: ; 3831 (0:3831) - call GetJoypadState - ld a,[$ffb7] ; flag - and a ; get all currently pressed buttons or only newly pressed buttons? - ld a,[H_NEWLYPRESSEDBUTTONS] ; newly pressed buttons - jr z,.storeButtonState - ld a,[H_CURRENTPRESSEDBUTTONS] ; all currently pressed buttons -.storeButtonState - ld [$ffb5],a - ld a,[H_NEWLYPRESSEDBUTTONS] ; newly pressed buttons - and a ; have any buttons been newly pressed since last check? - jr z,.noNewlyPressedButtons -.newlyPressedButtons - ld a,30 ; half a second delay - ld [H_FRAMECOUNTER],a - ret -.noNewlyPressedButtons - ld a,[H_FRAMECOUNTER] - and a ; is the delay over? - jr z,.delayOver -.delayNotOver - xor a - ld [$ffb5],a ; report no buttons as pressed - ret -.delayOver -; if [$ffb6] = 0 and A or B is pressed, report no buttons as pressed - ld a,[H_CURRENTPRESSEDBUTTONS] - and a,%00000011 ; A and B buttons - jr z,.setShortDelay - ld a,[$ffb6] ; flag - and a - jr nz,.setShortDelay - xor a - ld [$ffb5],a -.setShortDelay - ld a,5 ; 1/12 of a second delay - ld [H_FRAMECOUNTER],a - ret - -WaitForTextScrollButtonPress:: ; 3865 (0:3865) - ld a, [H_DOWNARROWBLINKCNT1] ; $ff8b - push af - ld a, [H_DOWNARROWBLINKCNT2] ; $ff8c - push af - xor a - ld [H_DOWNARROWBLINKCNT1], a ; $ff8b - ld a, $6 - ld [H_DOWNARROWBLINKCNT2], a ; $ff8c -.asm_3872 - push hl - ld a, [$d09b] - and a - jr z, .asm_387c - call Func_716c6 -.asm_387c - FuncCoord 18, 16 ; $c4f2 - ld hl, Coord - call HandleDownArrowBlinkTiming - pop hl - call GetJoypadStateLowSensitivity - ld a, $2d - call Predef ; indirect jump to Func_5a5f (5a5f (1:5a5f)) - ld a, [$ffb5] - and A_BUTTON | B_BUTTON - jr z, .asm_3872 - pop af - ld [H_DOWNARROWBLINKCNT2], a ; $ff8c - pop af - ld [H_DOWNARROWBLINKCNT1], a ; $ff8b - ret - -; (unlass in link battle) waits for A or B being pressed and outputs the scrolling sound effect -ManualTextScroll:: ; 3898 (0:3898) - ld a, [W_ISLINKBATTLE] ; $d12b - cp $4 - jr z, .inLinkBattle - call WaitForTextScrollButtonPress - ld a, (SFX_02_40 - SFX_Headers_02) / 3 - jp PlaySound -.inLinkBattle - ld c, $41 - jp DelayFrames - -; function to do multiplication -; all values are big endian -; INPUT -; FF96-FF98 = multiplicand -; FF99 = multiplier -; OUTPUT -; FF95-FF98 = product -Multiply:: ; 38ac (0:38ac) - push hl - push bc - callab _Multiply - pop bc - pop hl - ret - -; function to do division -; all values are big endian -; INPUT -; FF95-FF98 = dividend -; FF99 = divisor -; b = number of bytes in the dividend (starting from FF95) -; OUTPUT -; FF95-FF98 = quotient -; FF99 = remainder -Divide:: ; 38b9 (0:38b9) - push hl - push de - push bc - ld a,[H_LOADEDROMBANK] - push af - ld a,Bank(_Divide) - ld [H_LOADEDROMBANK],a - ld [$2000],a - call _Divide - pop af - ld [H_LOADEDROMBANK],a - ld [$2000],a - pop bc - pop de - pop hl - ret - -; This function is used to wait a short period after printing a letter to the -; screen unless the player presses the A/B button or the delay is turned off -; through the [$d730] or [$d358] flags. -PrintLetterDelay:: ; 38d3 (0:38d3) - ld a,[$d730] - bit 6,a - ret nz - ld a,[$d358] - bit 1,a - ret z - push hl - push de - push bc - ld a,[$d358] - bit 0,a - jr z,.waitOneFrame - ld a,[$d355] - and a,$0f - ld [H_FRAMECOUNTER],a - jr .checkButtons -.waitOneFrame - ld a,1 - ld [H_FRAMECOUNTER],a -.checkButtons - call GetJoypadState - ld a,[H_CURRENTPRESSEDBUTTONS] -.checkAButton - bit 0,a ; is the A button pressed? - jr z,.checkBButton - jr .endWait -.checkBButton - bit 1,a ; is the B button pressed? - jr z,.buttonsNotPressed -.endWait - call DelayFrame - jr .done -.buttonsNotPressed ; if neither A nor B is pressed - ld a,[H_FRAMECOUNTER] - and a - jr nz,.checkButtons -.done - pop bc - pop de - pop hl - ret - -; Copies [hl, bc) to [de, bc - hl). -; In other words, the source data is from hl up to but not including bc, -; and the destination is de. -CopyDataUntil:: ; 3913 (0:3913) - ld a,[hli] - ld [de],a - inc de - ld a,h - cp b - jr nz,CopyDataUntil - ld a,l - cp c - jr nz,CopyDataUntil - ret - -; Function to remove a pokemon from the party or the current box. -; wWhichPokemon determines the pokemon. -; [$cf95] == 0 specifies the party. -; [$cf95] != 0 specifies the current box. -RemovePokemon:: ; 391f (0:391f) - ld hl, _RemovePokemon - ld b, BANK(_RemovePokemon) - jp Bankswitch - -AddPokemonToParty:: ; 3927 (0:3927) - push hl - push de - push bc - callba _AddPokemonToParty - pop bc - pop de - pop hl - ret - -; calculates all 5 stats of current mon and writes them to [de] -CalcStats:: ; 3936 (0:3936) - ld c, $0 -.statsLoop - inc c - call CalcStat - ld a, [H_MULTIPLICAND+1] - ld [de], a - inc de - ld a, [H_MULTIPLICAND+2] - ld [de], a - inc de - ld a, c - cp $5 - jr nz, .statsLoop - ret - -; calculates stat c of current mon -; c: stat to calc (HP=1,Atk=2,Def=3,Spd=4,Spc=5) -; b: consider stat exp? -; hl: base ptr to stat exp values ([hl + 2*c - 1] and [hl + 2*c]) -CalcStat:: ; 394a (0:394a) - push hl - push de - push bc - ld a, b - ld d, a - push hl - ld hl, W_MONHEADER - ld b, $0 - add hl, bc - ld a, [hl] ; read base value of stat - ld e, a - pop hl - push hl - sla c - ld a, d - and a - jr z, .statExpDone ; consider stat exp? - add hl, bc ; skip to corresponding stat exp value -.statExpLoop ; calculates ceil(Sqrt(stat exp)) in b - xor a - ld [H_MULTIPLICAND], a - ld [H_MULTIPLICAND+1], a - inc b ; increment current stat exp bonus - ld a, b - cp $ff - jr z, .statExpDone - ld [H_MULTIPLICAND+2], a - ld [H_MULTIPLIER], a - call Multiply - ld a, [hld] - ld d, a - ld a, [$ff98] - sub d - ld a, [hli] - ld d, a - ld a, [$ff97] - sbc d ; test if (current stat exp bonus)^2 < stat exp - jr c, .statExpLoop -.statExpDone - srl c - pop hl - push bc - ld bc, $b ; skip to stat IV values - add hl, bc - pop bc - ld a, c - cp $2 - jr z, .getAttackIV - cp $3 - jr z, .getDefenseIV - cp $4 - jr z, .getSpeedIV - cp $5 - jr z, .getSpecialIV -.getHpIV - push bc - ld a, [hl] ; Atk IV - swap a - and $1 - sla a - sla a - sla a - ld b, a - ld a, [hli] ; Def IV - and $1 - sla a - sla a - add b - ld b, a - ld a, [hl] ; Spd IV - swap a - and $1 - sla a - add b - ld b, a - ld a, [hl] ; Spc IV - and $1 - add b ; HP IV: LSB of the other 4 IVs - pop bc - jr .calcStatFromIV -.getAttackIV - ld a, [hl] - swap a - and $f - jr .calcStatFromIV -.getDefenseIV - ld a, [hl] - and $f - jr .calcStatFromIV -.getSpeedIV - inc hl - ld a, [hl] - swap a - and $f - jr .calcStatFromIV -.getSpecialIV - inc hl - ld a, [hl] - and $f -.calcStatFromIV - ld d, $0 - add e - ld e, a - jr nc, .noCarry - inc d ; de = Base + IV -.noCarry - sla e - rl d ; de = (Base + IV) * 2 - srl b - srl b ; b = ceil(Sqrt(stat exp)) / 4 - ld a, b - add e - jr nc, .noCarry2 - inc d ; da = (Base + IV) * 2 + ceil(Sqrt(stat exp)) / 4 -.noCarry2 - ld [H_MULTIPLICAND+2], a - ld a, d - ld [H_MULTIPLICAND+1], a - xor a - ld [H_MULTIPLICAND], a - ld a, [W_CURENEMYLVL] ; $d127 - ld [H_MULTIPLIER], a - call Multiply ; ((Base + IV) * 2 + ceil(Sqrt(stat exp)) / 4) * Level - ld a, [H_MULTIPLICAND] - ld [H_DIVIDEND], a - ld a, [H_MULTIPLICAND+1] - ld [H_DIVIDEND+1], a - ld a, [H_MULTIPLICAND+2] - ld [H_DIVIDEND+2], a - ld a, $64 - ld [H_DIVISOR], a - ld a, $3 - ld b, a - call Divide ; (((Base + IV) * 2 + ceil(Sqrt(stat exp)) / 4) * Level) / 100 - ld a, c - cp $1 - ld a, $5 - jr nz, .notHPStat - ld a, [W_CURENEMYLVL] ; $d127 - ld b, a - ld a, [H_MULTIPLICAND+2] - add b - ld [H_MULTIPLICAND+2], a - jr nc, .noCarry3 - ld a, [H_MULTIPLICAND+1] - inc a - ld [H_MULTIPLICAND+1], a ; HP: (((Base + IV) * 2 + ceil(Sqrt(stat exp)) / 4) * Level) / 100 + Level -.noCarry3 - ld a, $a -.notHPStat - ld b, a - ld a, [H_MULTIPLICAND+2] - add b - ld [H_MULTIPLICAND+2], a - jr nc, .noCarry4 - ld a, [H_MULTIPLICAND+1] - inc a ; non-HP: (((Base + IV) * 2 + ceil(Sqrt(stat exp)) / 4) * Level) / 100 + 5 - ld [H_MULTIPLICAND+1], a ; HP: (((Base + IV) * 2 + ceil(Sqrt(stat exp)) / 4) * Level) / 100 + Level + 10 -.noCarry4 - ld a, [H_MULTIPLICAND+1] ; check for overflow (>999) - cp $4 - jr nc, .overflow - cp $3 - jr c, .noOverflow - ld a, [H_MULTIPLICAND+2] - cp $e8 - jr c, .noOverflow -.overflow - ld a, $3 ; overflow: cap at 999 - ld [H_MULTIPLICAND+1], a - ld a, $e7 - ld [H_MULTIPLICAND+2], a -.noOverflow - pop bc - pop de - pop hl - ret - -AddEnemyMonToPlayerParty:: ; 3a53 (0:3a53) - ld a, [H_LOADEDROMBANK] - push af - ld a, BANK(_AddEnemyMonToPlayerParty) - ld [H_LOADEDROMBANK], a - ld [$2000], a - call _AddEnemyMonToPlayerParty - pop bc - ld a, b - ld [H_LOADEDROMBANK], a - ld [$2000], a - ret - -Func_3a68:: ; 3a68 (0:3a68) - ld a, [H_LOADEDROMBANK] - push af - ld a, BANK(Func_f51e) - ld [H_LOADEDROMBANK], a - ld [$2000], a - call Func_f51e - pop bc - ld a, b - ld [H_LOADEDROMBANK], a - ld [$2000], a - ret - -; skips a text entries, each of size $b (like trainer name, OT name, rival name, ...) -; hl: base pointer, will be incremented by $b * a -SkipFixedLengthTextEntries:: ; 3a7d (0:3a7d) - and a - ret z - ld bc, $b -.skipLoop - add hl, bc - dec a - jr nz, .skipLoop - ret - -AddNTimes:: ; 3a87 (0:3a87) -; add bc to hl a times - and a - ret z -.loop - add hl,bc - dec a - jr nz,.loop - ret - -; Compare strings, c bytes in length, at de and hl. -; Often used to compare big endian numbers in battle calculations. -StringCmp:: ; 3a8e (0:3a8e) - ld a,[de] - cp [hl] - ret nz - inc de - inc hl - dec c - jr nz,StringCmp - ret - -; INPUT: -; a = oam block index (each block is 4 oam entries) -; b = Y coordinate of upper left corner of sprite -; c = X coordinate of upper left corner of sprite -; de = base address of 4 tile number and attribute pairs -WriteOAMBlock:: ; 3a97 (0:3a97) - ld h,$c3 - swap a ; multiply by 16 - ld l,a - call .writeOneEntry ; upper left - push bc - ld a,8 - add c - ld c,a - call .writeOneEntry ; upper right - pop bc - ld a,8 - add b - ld b,a - call .writeOneEntry ; lower left - ld a,8 - add c - ld c,a - ; lower right -.writeOneEntry - ld [hl],b ; Y coordinate - inc hl - ld [hl],c ; X coordinate - inc hl - ld a,[de] ; tile number - inc de - ld [hli],a - ld a,[de] ; attribute - inc de - ld [hli],a - ret - -HandleMenuInput:: ; 3abe (0:3abe) - xor a - ld [$d09b],a - -HandleMenuInputPokemonSelection:: ; 3ac2 (0:3ac2) - ld a,[H_DOWNARROWBLINKCNT1] - push af - ld a,[H_DOWNARROWBLINKCNT2] - push af ; save existing values on stack - xor a - ld [H_DOWNARROWBLINKCNT1],a ; blinking down arrow timing value 1 - ld a,$06 - ld [H_DOWNARROWBLINKCNT2],a ; blinking down arrow timing value 2 -.loop1 - xor a - ld [$d08b],a ; counter for pokemon shaking animation - call PlaceMenuCursor - call Delay3 -.loop2 - push hl - ld a,[$d09b] - and a ; is it a pokemon selection menu? - jr z,.getJoypadState - callba AnimatePartyMon ; shake mini sprite of selected pokemon -.getJoypadState - pop hl - call GetJoypadStateLowSensitivity - ld a,[$ffb5] - and a ; was a key pressed? - jr nz,.keyPressed - push hl - FuncCoord 18,11 ; coordinates of blinking down arrow in some menus - ld hl,Coord - call HandleDownArrowBlinkTiming ; blink down arrow (if any) - pop hl - ld a,[wMenuJoypadPollCount] - dec a - jr z,.giveUpWaiting - jr .loop2 -.giveUpWaiting -; if a key wasn't pressed within the specified number of checks - pop af - ld [H_DOWNARROWBLINKCNT2],a - pop af - ld [H_DOWNARROWBLINKCNT1],a ; restore previous values - xor a - ld [wMenuWrappingEnabled],a ; disable menu wrapping - ret -.keyPressed - xor a - ld [$cc4b],a - ld a,[$ffb5] - ld b,a - bit 6,a ; pressed Up key? - jr z,.checkIfDownPressed -.upPressed - ld a,[wCurrentMenuItem] ; selected menu item - and a ; already at the top of the menu? - jr z,.alreadyAtTop -.notAtTop - dec a - ld [wCurrentMenuItem],a ; move selected menu item up one space - jr .checkOtherKeys -.alreadyAtTop - ld a,[wMenuWrappingEnabled] - and a ; is wrapping around enabled? - jr z,.noWrappingAround - ld a,[wMaxMenuItem] - ld [wCurrentMenuItem],a ; wrap to the bottom of the menu - jr .checkOtherKeys -.checkIfDownPressed - bit 7,a - jr z,.checkOtherKeys -.downPressed - ld a,[wCurrentMenuItem] - inc a - ld c,a - ld a,[wMaxMenuItem] - cp c - jr nc,.notAtBottom -.alreadyAtBottom - ld a,[wMenuWrappingEnabled] - and a ; is wrapping around enabled? - jr z,.noWrappingAround - ld c,$00 ; wrap from bottom to top -.notAtBottom - ld a,c - ld [wCurrentMenuItem],a -.checkOtherKeys - ld a,[wMenuWatchedKeys] - and b ; does the menu care about any of the pressed keys? - jp z,.loop1 -.checkIfAButtonOrBButtonPressed - ld a,[$ffb5] - and a,%00000011 ; pressed A button or B button? - jr z,.skipPlayingSound -.AButtonOrBButtonPressed - push hl - ld hl,wFlags_0xcd60 - bit 5,[hl] - pop hl - jr nz,.skipPlayingSound - ld a,(SFX_02_40 - SFX_Headers_02) / 3 - call PlaySound ; play sound -.skipPlayingSound - pop af - ld [H_DOWNARROWBLINKCNT2],a - pop af - ld [H_DOWNARROWBLINKCNT1],a ; restore previous values - xor a - ld [wMenuWrappingEnabled],a ; disable menu wrapping - ld a,[$ffb5] - ret -.noWrappingAround - ld a,[$cc37] - and a ; should we return if the user tried to go past the top or bottom? - jr z,.checkOtherKeys - jr .checkIfAButtonOrBButtonPressed - -PlaceMenuCursor:: ; 3b7c (0:3b7c) - ld a,[wTopMenuItemY] - and a ; is the y coordinate 0? - jr z,.adjustForXCoord - ld hl,wTileMap - ld bc,20 ; screen width -.topMenuItemLoop - add hl,bc - dec a - jr nz,.topMenuItemLoop -.adjustForXCoord - ld a,[wTopMenuItemX] - ld b,$00 - ld c,a - add hl,bc - push hl - ld a,[wLastMenuItem] - and a ; was the previous menu id 0? - jr z,.checkForArrow1 - push af - ld a,[$fff6] - bit 1,a ; is the menu double spaced? - jr z,.doubleSpaced1 - ld bc,20 - jr .getOldMenuItemScreenPosition -.doubleSpaced1 - ld bc,40 -.getOldMenuItemScreenPosition - pop af -.oldMenuItemLoop - add hl,bc - dec a - jr nz,.oldMenuItemLoop -.checkForArrow1 - ld a,[hl] - cp a,"▶" ; was an arrow next to the previously selected menu item? - jr nz,.skipClearingArrow -.clearArrow - ld a,[wTileBehindCursor] - ld [hl],a -.skipClearingArrow - pop hl - ld a,[wCurrentMenuItem] - and a - jr z,.checkForArrow2 - push af - ld a,[$fff6] - bit 1,a ; is the menu double spaced? - jr z,.doubleSpaced2 - ld bc,20 - jr .getCurrentMenuItemScreenPosition -.doubleSpaced2 - ld bc,40 -.getCurrentMenuItemScreenPosition - pop af -.currentMenuItemLoop - add hl,bc - dec a - jr nz,.currentMenuItemLoop -.checkForArrow2 - ld a,[hl] - cp a,"▶" ; has the right arrow already been placed? - jr z,.skipSavingTile ; if so, don't lose the saved tile - ld [wTileBehindCursor],a ; save tile before overwriting with right arrow -.skipSavingTile - ld a,"▶" ; place right arrow - ld [hl],a - ld a,l - ld [wMenuCursorLocation],a - ld a,h - ld [wMenuCursorLocation + 1],a - ld a,[wCurrentMenuItem] - ld [wLastMenuItem],a - ret - -; This is used to mark a menu cursor other than the one currently being -; manipulated. In the case of submenus, this is used to show the location of -; the menu cursor in the parent menu. In the case of swapping items in list, -; this is used to mark the item that was first chosen to be swapped. -PlaceUnfilledArrowMenuCursor:: ; 3bec (0:3bec) - ld b,a - ld a,[wMenuCursorLocation] - ld l,a - ld a,[wMenuCursorLocation + 1] - ld h,a - ld [hl],$ec ; outline of right arrow - ld a,b - ret - -; Replaces the menu cursor with a blank space. -EraseMenuCursor:: ; 3bf9 (0:3bf9) - ld a,[wMenuCursorLocation] - ld l,a - ld a,[wMenuCursorLocation + 1] - ld h,a - ld [hl]," " - ret - -; This toggles a blinking down arrow at hl on and off after a delay has passed. -; This is often called even when no blinking is occurring. -; The reason is that most functions that call this initialize H_DOWNARROWBLINKCNT1 to 0. -; The effect is that if the tile at hl is initialized with a down arrow, -; this function will toggle that down arrow on and off, but if the tile isn't -; initliazed with a down arrow, this function does nothing. -; That allows this to be called without worrying about if a down arrow should -; be blinking. -HandleDownArrowBlinkTiming:: ; 3c04 (0:3c04) - ld a,[hl] - ld b,a - ld a,$ee ; down arrow - cp b - jr nz,.downArrowOff -.downArrowOn - ld a,[H_DOWNARROWBLINKCNT1] - dec a - ld [H_DOWNARROWBLINKCNT1],a - ret nz - ld a,[H_DOWNARROWBLINKCNT2] - dec a - ld [H_DOWNARROWBLINKCNT2],a - ret nz - ld a," " - ld [hl],a - ld a,$ff - ld [H_DOWNARROWBLINKCNT1],a - ld a,$06 - ld [H_DOWNARROWBLINKCNT2],a - ret -.downArrowOff - ld a,[H_DOWNARROWBLINKCNT1] - and a - ret z - dec a - ld [H_DOWNARROWBLINKCNT1],a - ret nz - dec a - ld [H_DOWNARROWBLINKCNT1],a - ld a,[H_DOWNARROWBLINKCNT2] - dec a - ld [H_DOWNARROWBLINKCNT2],a - ret nz - ld a,$06 - ld [H_DOWNARROWBLINKCNT2],a - ld a,$ee ; down arrow - ld [hl],a - ret - -; The following code either enables or disables the automatic drawing of -; text boxes by DisplayTextID. Both functions cause DisplayTextID to wait -; for a button press after displaying text (unless [$cc47] is set). - -EnableAutoTextBoxDrawing:: ; 3c3c (0:3c3c) - xor a - jr AutoTextBoxDrawingCommon - -DisableAutoTextBoxDrawing:: ; 3c3f (0:3c3f) - ld a,$01 - -AutoTextBoxDrawingCommon:: ; 3c41 (0:3c41) - ld [$cf0c],a ; control text box drawing - xor a - ld [$cc3c],a ; make DisplayTextID wait for button press - ret - -PrintText:: ; 3c49 (0:3c49) -; given a pointer in hl, print the text there - push hl - ld a,1 - ld [$D125],a - call DisplayTextBoxID - call UpdateSprites - call Delay3 - pop hl -Func_3c59:: ; 3c59 (0:3c59) - FuncCoord 1,14 - ld bc,Coord ;$C4B9 - jp TextCommandProcessor - -; converts a big-endian binary number into decimal and prints it -; INPUT: -; b = flags and number of bytes -; bit 7: if set, print leading zeroes -; if unset, do not print leading zeroes -; bit 6: if set, left-align the string (do not pad empty digits with spaces) -; if unset, right-align the string -; bits 4-5: unused -; bits 0-3: number of bytes (only 1 - 3 bytes supported) -; c = number of decimal digits -; de = address of the number (big-endian) -PrintNumber:: ; 3c5f (0:3c5f) - push bc - xor a - ld [H_PASTLEADINGZEROES],a - ld [H_NUMTOPRINT],a - ld [H_NUMTOPRINT + 1],a - ld a,b - and a,%00001111 - cp a,1 - jr z,.oneByte - cp a,2 - jr z,.twoBytes -.threeBytes - ld a,[de] - ld [H_NUMTOPRINT],a - inc de - ld a,[de] - ld [H_NUMTOPRINT + 1],a - inc de - ld a,[de] - ld [H_NUMTOPRINT + 2],a - jr .checkNumDigits -.twoBytes - ld a,[de] - ld [H_NUMTOPRINT + 1],a - inc de - ld a,[de] - ld [H_NUMTOPRINT + 2],a - jr .checkNumDigits -.oneByte - ld a,[de] - ld [H_NUMTOPRINT + 2],a -.checkNumDigits - push de - ld d,b - ld a,c - ld b,a - xor a - ld c,a - ld a,b ; a = number of decimal digits - cp a,2 - jr z,.tensPlace - cp a,3 - jr z,.hundredsPlace - cp a,4 - jr z,.thousandsPlace - cp a,5 - jr z,.tenThousandsPlace - cp a,6 - jr z,.hundredThousandsPlace -.millionsPlace - ld a,1000000 >> 16 - ld [H_POWEROFTEN],a - ld a,(1000000 >> 8) & $FF - ld [H_POWEROFTEN + 1],a - ld a,1000000 & $FF - ld [H_POWEROFTEN + 2],a - call PrintNumber_PrintDigit - call PrintNumber_AdvancePointer -.hundredThousandsPlace - ld a,100000 >> 16 - ld [H_POWEROFTEN],a - ld a,(100000 >> 8) & $FF - ld [H_POWEROFTEN + 1],a - ld a,100000 & $FF - ld [H_POWEROFTEN + 2],a - call PrintNumber_PrintDigit - call PrintNumber_AdvancePointer -.tenThousandsPlace - xor a - ld [H_POWEROFTEN],a - ld a,10000 >> 8 - ld [H_POWEROFTEN + 1],a - ld a,10000 & $FF - ld [H_POWEROFTEN + 2],a - call PrintNumber_PrintDigit - call PrintNumber_AdvancePointer -.thousandsPlace - xor a - ld [H_POWEROFTEN],a - ld a,1000 >> 8 - ld [H_POWEROFTEN + 1],a - ld a,1000 & $FF - ld [H_POWEROFTEN + 2],a - call PrintNumber_PrintDigit - call PrintNumber_AdvancePointer -.hundredsPlace - xor a - ld [H_POWEROFTEN],a - xor a - ld [H_POWEROFTEN + 1],a - ld a,100 - ld [H_POWEROFTEN + 2],a - call PrintNumber_PrintDigit - call PrintNumber_AdvancePointer -.tensPlace - ld c,00 - ld a,[H_NUMTOPRINT + 2] -.loop - cp a,10 - jr c,.underflow - sub a,10 - inc c - jr .loop -.underflow - ld b,a - ld a,[H_PASTLEADINGZEROES] - or c - ld [H_PASTLEADINGZEROES],a - jr nz,.pastLeadingZeroes - call PrintNumber_PrintLeadingZero - jr .advancePointer -.pastLeadingZeroes - ld a,"0" - add c - ld [hl],a -.advancePointer - call PrintNumber_AdvancePointer -.onesPlace - ld a,"0" - add b - ld [hli],a - pop de - dec de - pop bc - ret - -; prints a decimal digit -; This works by repeatedely subtracting a power of ten until the number becomes negative. -; The number of subtractions it took in order to make the number negative is the digit for the current number place. -; The last value that the number had before becoming negative is kept as the new value of the number. -; A more succinct description is that the number is divided by a power of ten -; and the quotient becomes the digit while the remainder is stored as the new value of the number. -PrintNumber_PrintDigit:: ; 3d25 (0:3d25) - ld c,0 ; counts number of loop iterations to determine the decimal digit -.loop - ld a,[H_POWEROFTEN] - ld b,a - ld a,[H_NUMTOPRINT] - ld [H_SAVEDNUMTOPRINT],a - cp b - jr c,.underflow0 - sub b - ld [H_NUMTOPRINT],a - ld a,[H_POWEROFTEN + 1] - ld b,a - ld a,[H_NUMTOPRINT + 1] - ld [H_SAVEDNUMTOPRINT + 1],a - cp b - jr nc,.noBorrowForByte1 -.byte1BorrowFromByte0 - ld a,[H_NUMTOPRINT] - or a,0 - jr z,.underflow1 - dec a - ld [H_NUMTOPRINT],a - ld a,[H_NUMTOPRINT + 1] -.noBorrowForByte1 - sub b - ld [H_NUMTOPRINT + 1],a - ld a,[H_POWEROFTEN + 2] - ld b,a - ld a,[H_NUMTOPRINT + 2] - ld [H_SAVEDNUMTOPRINT + 2],a - cp b - jr nc,.noBorrowForByte2 -.byte2BorrowFromByte1 - ld a,[H_NUMTOPRINT + 1] - and a - jr nz,.finishByte2BorrowFromByte1 -.byte2BorrowFromByte0 - ld a,[H_NUMTOPRINT] - and a - jr z,.underflow2 - dec a - ld [H_NUMTOPRINT],a - xor a -.finishByte2BorrowFromByte1 - dec a - ld [H_NUMTOPRINT + 1],a - ld a,[H_NUMTOPRINT + 2] -.noBorrowForByte2 - sub b - ld [H_NUMTOPRINT + 2],a - inc c - jr .loop -.underflow2 - ld a,[H_SAVEDNUMTOPRINT + 1] - ld [H_NUMTOPRINT + 1],a -.underflow1 - ld a,[H_SAVEDNUMTOPRINT] - ld [H_NUMTOPRINT],a -.underflow0 - ld a,[H_PASTLEADINGZEROES] - or c - jr z,PrintNumber_PrintLeadingZero - ld a,"0" - add c - ld [hl],a - ld [H_PASTLEADINGZEROES],a - ret - -; prints a leading zero unless they are turned off in the flags -PrintNumber_PrintLeadingZero:: ; 3d83 (0:3d83) - bit 7,d ; print leading zeroes? - ret z - ld [hl],"0" - ret - -; increments the pointer unless leading zeroes are not being printed, -; the number is left-aligned, and no nonzero digits have been printed yet -PrintNumber_AdvancePointer:: ; 3d89 (0:3d89) - bit 7,d ; print leading zeroes? - jr nz,.incrementPointer - bit 6,d ; left alignment or right alignment? - jr z,.incrementPointer - ld a,[H_PASTLEADINGZEROES] - and a - ret z -.incrementPointer - inc hl - ret - -; calls a function from a table of function pointers -; INPUT: -; a = index within table -; hl = address of function pointer table -CallFunctionInTable:: ; 3d97 (0:3d97) - push hl - push de - push bc - add a - ld d,0 - ld e,a - add hl,de - ld a,[hli] - ld h,[hl] - ld l,a - ld de,.returnAddress - push de - jp [hl] -.returnAddress - pop bc - pop de - pop hl - ret - - -IsInArray:: -; Search an array at hl for the value in a. -; Entry size is de bytes. -; Return count b and carry if found. - ld b, 0 - -IsInRestOfArray:: - ld c, a -.loop - ld a, [hl] - cp -1 - jr z, .notfound - cp c - jr z, .found - inc b - add hl, de - jr .loop - -.notfound - and a - ret - -.found - scf - ret - - -Func_3dbe:: ; 3dbe (0:3dbe) - call ClearSprites - ld a, $1 - ld [$cfcb], a - call Func_3e08 - call LoadScreenTilesFromBuffer2 - call LoadTextBoxTilePatterns - call GoPAL_SET_CF1C - jr Delay3 - - -GBPalWhiteOutWithDelay3:: - call GBPalWhiteOut - -Delay3:: -; The bg map is updated each frame in thirds. -; Wait three frames to let the bg map fully update. - ld c, 3 - jp DelayFrames - -GBPalNormal:: -; Reset BGP and OBP0. - ld a, %11100100 ; 3210 - ld [rBGP], a - ld a, %11010000 ; 3100 - ld [rOBP0], a - ret - -GBPalWhiteOut:: -; White out all palettes. - xor a - ld [rBGP],a - ld [rOBP0],a - ld [rOBP1],a - ret - - -GoPAL_SET_CF1C:: ; 3ded (0:3ded) - ld b,$ff -GoPAL_SET:: ; 3def (0:3def) - ld a,[$cf1b] - and a - ret z - ld a,$45 - jp Predef - -GetHealthBarColor:: -; Return at hl the palette of -; an HP bar e pixels long. - ld a, e - cp 27 - ld d, 0 ; green - jr nc, .gotColor - cp 10 - inc d ; yellow - jr nc, .gotColor - inc d ; red -.gotColor - ld [hl], d - ret - -Func_3e08:: ; 3e08 (0:3e08) - ld hl, $cfc4 - ld a, [hl] - push af - res 0, [hl] - push hl - xor a - ld [W_SPRITESETID], a ; $d3a8 - call DisableLCD - callba InitMapSprites - call EnableLCD - pop hl - pop af - ld [hl], a - call LoadPlayerSpriteGraphics - call LoadFontTilePatterns - jp UpdateSprites - - -GiveItem:: -; Give player quantity c of item b, -; and copy the item's name to $cf4b. -; Return carry on success. - ld a, b - ld [$d11e], a - ld [$cf91], a - ld a, c - ld [$cf96], a - ld hl,wNumBagItems - call AddItemToInventory - ret nc - call GetItemName - call CopyStringToCF4B - scf - ret - -GivePokemon:: -; Give the player monster b at level c. - ld a, b - ld [$cf91], a - ld a, c - ld [$d127], a - xor a - ld [$cc49], a - ld b, BANK(_GivePokemon) - ld hl, _GivePokemon - jp Bankswitch - - -Random:: -; Return a random number in a. -; For battles, use BattleRandom. - push hl - push de - push bc - callba Random_ - ld a,[hRandomAdd] - pop bc - pop de - pop hl - ret - - -Predef:: -; Call predefined function a. -; To preserve other registers, have the -; destination call GetPredefRegisters. - - ; Save the predef id for GetPredefPointer. - ld [wPredefID], a - - ; A hack for LoadDestinationWarpPosition. - ; See Func_c754 (predef $19). - ld a, [H_LOADEDROMBANK] - ld [wPredefParentBank], a - - push af - ld a, BANK(GetPredefPointer) - ld [H_LOADEDROMBANK], a - ld [$2000], a - - call GetPredefPointer - - ld a, [wPredefBank] - ld [H_LOADEDROMBANK], a - ld [$2000], a - - ld de, .done - push de - jp [hl] -.done - - pop af - ld [H_LOADEDROMBANK], a - ld [$2000], a - ret - -GetPredefRegisters:: -; Restore the contents of register pairs -; when GetPredefPointer was called. - ld a, [wPredefRegisters + 0] - ld h, a - ld a, [wPredefRegisters + 1] - ld l, a - ld a, [wPredefRegisters + 2] - ld d, a - ld a, [wPredefRegisters + 3] - ld e, a - ld a, [wPredefRegisters + 4] - ld b, a - ld a, [wPredefRegisters + 5] - ld c, a - ret - - -Func_3ead:: ; 3ead (0:3ead) - ld b, BANK(CinnabarGymQuiz_1eb0a) - ld hl, CinnabarGymQuiz_1eb0a - jp Bankswitch - -Func_3eb5:: ; 3eb5 (0:3eb5) - ld a, [H_LOADEDROMBANK] - push af - ld a, [H_CURRENTPRESSEDBUTTONS] - bit 0, a - jr z, .asm_3eea - ld a, Bank(Func_469a0) - ld [$2000], a - ld [H_LOADEDROMBANK], a - call Func_469a0 - ld a, [$ffee] - and a - jr nz, .asm_3edd - ld a, [$cd3e] - ld [$2000], a - ld [H_LOADEDROMBANK], a - ld de, .asm_3eda - push de - jp [hl] -.asm_3eda - xor a - jr .asm_3eec -.asm_3edd - callba PrintBookshelfText - ld a, [$ffdb] - and a - jr z, .asm_3eec -.asm_3eea - ld a, $ff -.asm_3eec - ld [$ffeb], a - pop af - ld [$2000], a - ld [H_LOADEDROMBANK], a - ret - -PrintPredefTextID:: ; 3ef5 (0:3ef5) - ld [H_DOWNARROWBLINKCNT2], a ; $ff8c - ld hl, PointerTable_3f22 - call Func_3f0f - ld hl, $cf11 - set 0, [hl] - call DisplayTextID - -Func_3f05:: ; 3f05 (0:3f05) - ld hl, W_MAPTEXTPTR ; $d36c - ld a, [$ffec] - ld [hli], a - ld a, [$ffed] - ld [hl], a - ret - -Func_3f0f:: ; 3f0f (0:3f0f) - ld a, [W_MAPTEXTPTR] ; $d36c - ld [$ffec], a - ld a, [$d36d] - ld [$ffed], a - ld a, l - ld [W_MAPTEXTPTR], a ; $d36c - ld a, h - ld [$d36d], a - ret - -PointerTable_3f22:: ; 3f22 (0:3f22) - dw CardKeySuccessText ; id = 01 - dw CardKeyFailText ; id = 02 - dw RedBedroomPC ; id = 03 - dw RedBedroomSNESText ; id = 04 - dw PushStartText ; id = 05 - dw SaveOptionText ; id = 06 - dw StrengthsAndWeaknessesText ; id = 07 - dw OakLabEmailText ; id = 08 - dw AerodactylFossilText ; id = 09 - dw Route15UpstairsBinocularsText ; id = 0A - dw KabutopsFossilText ; id = 0B - dw GymStatueText1 ; id = 0C - dw GymStatueText2 ; id = 0D - dw BookcaseText ; id = 0E - dw ViridianCityPokecenterBenchGuyText ; id = 0F - dw PewterCityPokecenterBenchGuyText ; id = 10 - dw CeruleanCityPokecenterBenchGuyText ; id = 11 - dw LavenderCityPokecenterBenchGuyText ; id = 12 - dw VermilionCityPokecenterBenchGuyText ; id = 13 - dw CeladonCityPokecenterBenchGuyText ; id = 14 - dw CeladonCityHotelText ; id = 15 - dw FuchsiaCityPokecenterBenchGuyText ; id = 16 - dw CinnabarIslandPokecenterBenchGuyText ; id = 17 - dw SaffronCityPokecenterBenchGuyText ; id = 18 - dw MtMoonPokecenterBenchGuyText ; id = 19 - dw RockTunnelPokecenterBenchGuyText ; id = 1A - dw UnusedBenchGuyText1 ; id = 1B - dw UnusedBenchGuyText2 ; id = 1C - dw UnusedBenchGuyText3 ; id = 1D - dw TerminatorText_62508 ; id = 1E - dw PredefText1f ; id = 1F - dw ViridianSchoolNotebook ; id = 20 - dw ViridianSchoolBlackboard ; id = 21 - dw JustAMomentText ; id = 22 - dw PredefText23 ; id = 23 - dw FoundHiddenItemText ; id = 24 - dw HiddenItemBagFullText ; id = 25 - dw VermilionGymTrashText ; id = 26 - dw IndigoPlateauHQText ; id = 27 - dw GameCornerOutOfOrderText ; id = 28 - dw GameCornerOutToLunchText ; id = 29 - dw GameCornerSomeonesKeysText ; id = 2A - dw FoundHiddenCoinsText ; id = 2B - dw DroppedHiddenCoinsText ; id = 2C - dw BillsHouseMonitorText ; id = 2D - dw BillsHouseInitiatedText ; id = 2E - dw BillsHousePokemonList ; id = 2F - dw MagazinesText ; id = 30 - dw CinnabarGymQuiz ; id = 31 - dw GameCornerNoCoinsText ; id = 32 - dw GameCornerCoinCaseText ; id = 33 - dw LinkCableHelp ; id = 34 - dw TMNotebook ; id = 35 - dw FightingDojoText ; id = 36 - dw FightingDojoText_52a10 ; id = 37 - dw FightingDojoText_52a1d ; id = 38 - dw NewBicycleText ; id = 39 - dw IndigoPlateauStatues ; id = 3A - dw VermilionGymTrashSuccesText1 ; id = 3B - dw VermilionGymTrashSuccesText2 ; id = 3C - dw VermilionGymTrashSuccesText3 ; id = 3D - dw VermilionGymTrashFailText ; id = 3E - dw TownMapText ; id = 3F - dw BookOrSculptureText ; id = 40 - dw ElevatorText ; id = 41 - dw PokemonStuffText ; id = 42 +INCLUDE "home.asm" SECTION "bank1",ROMX,BANK[$1] |