ref: bbf2f51a02b2544f1bef32a5868503b474ae2fef
parent: 51ac538c25f8c0a6d88101569a17f02d09855d31
author: Rangi <remy.oukaour+rangi42@gmail.com>
date: Tue Jul 7 14:50:58 EDT 2020
Move all code out of home.asm into home/ This results in 64 home/*.asm files, comparable to pokecrystal's 57.
--- a/home.asm
+++ b/home.asm
@@ -18,64 +18,16 @@
INCLUDE "home/start.asm"
INCLUDE "home/joypad.asm"
+
INCLUDE "data/maps/map_header_pointers.asm"
-INCLUDE "home/overworld.asm"
-CheckForUserInterruption::
-; Return carry if Up+Select+B, Start or A are pressed in c frames.
-; Used only in the intro and title screen.
- call DelayFrame
-
- push bc
- call JoypadLowSensitivity
- pop bc
-
- ldh a, [hJoyHeld]
- cp D_UP + SELECT + B_BUTTON
- jr z, .input
-
- ldh a, [hJoy5]
- and START | A_BUTTON
- jr nz, .input
-
- dec c
- jr nz, CheckForUserInterruption
-
- and a
- ret
-
-.input
- scf
- ret
-
-; function to load position data for destination warp when switching maps
-; INPUT:
-; a = ID of destination warp within destination map
-LoadDestinationWarpPosition::
- ld b, a
- ldh a, [hLoadedROMBank]
- push af
- ld a, [wPredefParentBank]
- ldh [hLoadedROMBank], a
- ld [MBC1RomBank], a
- ld a, b
- add a
- add a
- ld c, a
- ld b, 0
- add hl, bc
- ld bc, 4
- ld de, wCurrentTileBlockMapViewPointer
- call CopyData
- pop af
- ldh [hLoadedROMBank], a
- ld [MBC1RomBank], a
- ret
-
+INCLUDE "home/overworld.asm"
INCLUDE "home/pokemon.asm"
INCLUDE "home/print_bcd.asm"
INCLUDE "home/pics.asm"
+
INCLUDE "data/tilesets/collision_tile_ids.asm"
+
INCLUDE "home/copy2.asm"
INCLUDE "home/text.asm"
INCLUDE "home/vcopy.asm"
@@ -85,1724 +37,51 @@
INCLUDE "home/serial.asm"
INCLUDE "home/timer.asm"
INCLUDE "home/audio.asm"
+INCLUDE "home/update_sprites.asm"
-UpdateSprites::
- ld a, [wUpdateSpritesEnabled]
- dec a
- ret nz
- ldh a, [hLoadedROMBank]
- push af
- ld a, BANK(_UpdateSprites)
- ldh [hLoadedROMBank], a
- ld [MBC1RomBank], a
- call _UpdateSprites
- pop af
- ldh [hLoadedROMBank], a
- ld [MBC1RomBank], a
- ret
-
INCLUDE "data/items/marts.asm"
+
INCLUDE "home/overworld_text.asm"
INCLUDE "home/uncompress.asm"
-
-ResetPlayerSpriteData::
- ld hl, wSpriteStateData1
- call ResetPlayerSpriteData_ClearSpriteData
- ld hl, wSpriteStateData2
- call ResetPlayerSpriteData_ClearSpriteData
- ld a, $1
- ld [wSpritePlayerStateData1PictureID], a
- ld [wSpritePlayerStateData2ImageBaseOffset], a
- ld hl, wSpritePlayerStateData1YPixels
- 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::
- ld bc, $10
- xor a
- jp FillMemory
-
-FadeOutAudio::
- ld a, [wAudioFadeOutControl]
- and a ; currently fading out audio?
- jr nz, .fadingOut
- ld a, [wd72c]
- bit 1, a
- ret nz
- ld a, $77
- ldh [rNR50], a
- ret
-.fadingOut
- ld a, [wAudioFadeOutCounter]
- and a
- jr z, .counterReachedZero
- dec a
- ld [wAudioFadeOutCounter], a
- ret
-.counterReachedZero
- ld a, [wAudioFadeOutCounterReloadValue]
- ld [wAudioFadeOutCounter], a
- ldh a, [rNR50]
- and a ; has the volume reached 0?
- jr z, .fadeOutComplete
- ld b, a
- and $f
- dec a
- ld c, a
- ld a, b
- and $f0
- swap a
- dec a
- swap a
- or c
- ldh [rNR50], a
- ret
-.fadeOutComplete
- ld a, [wAudioFadeOutControl]
- ld b, a
- xor a
- ld [wAudioFadeOutControl], a
- ld a, SFX_STOP_ALL_MUSIC
- ld [wNewSoundID], a
- call PlaySound
- ld a, [wAudioSavedROMBank]
- ld [wAudioROMBank], a
- ld a, b
- ld [wNewSoundID], a
- jp PlaySound
-
+INCLUDE "home/reset_player_sprite.asm"
+INCLUDE "home/fade_audio.asm"
INCLUDE "home/text_script.asm"
INCLUDE "home/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:
-; [wNumSetBits] = number of set bits
-CountSetBits::
- 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 [wNumSetBits], a
- ret
-
-; subtracts the amount the player paid from their money
-; OUTPUT: carry = 0(success) or 1(fail because there is not enough money)
-SubtractAmountPaidFromMoney::
- farjp SubtractAmountPaidFromMoney_
-
-; adds the amount the player sold to their money
-AddAmountSoldToMoney::
- ld de, wPlayerMoney + 2
- ld hl, hMoney + 2 ; total price of items
- ld c, 3 ; length of money in bytes
- predef AddBCDPredef ; add total price to money
- ld a, MONEY_BOX
- ld [wTextBoxID], a
- call DisplayTextBoxID ; redraw money text box
- ld a, SFX_PURCHASE
- call PlaySoundWaitForCurrent
- jp WaitForSoundToFinish
-
-; 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)
-; [wWhichPokemon] = index (within the inventory) of the item to remove
-; [wItemQuantity] = quantity to remove
-RemoveItemFromInventory::
- ldh a, [hLoadedROMBank]
- push af
- ld a, BANK(RemoveItemFromInventory_)
- ldh [hLoadedROMBank], a
- ld [MBC1RomBank], a
- call RemoveItemFromInventory_
- pop af
- ldh [hLoadedROMBank], a
- ld [MBC1RomBank], 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)
-; [wcf91] = item ID
-; [wItemQuantity] = item quantity
-; sets carry flag if successful, unsets carry flag if unsuccessful
-AddItemToInventory::
- push bc
- ldh a, [hLoadedROMBank]
- push af
- ld a, BANK(AddItemToInventory_)
- ldh [hLoadedROMBank], a
- ld [MBC1RomBank], a
- call AddItemToInventory_
- pop bc
- ld a, b
- ldh [hLoadedROMBank], a
- ld [MBC1RomBank], a
- pop bc
- ret
-
+INCLUDE "home/count_set_bits.asm"
+INCLUDE "home/inventory.asm"
INCLUDE "home/list_menu.asm"
INCLUDE "home/names.asm"
-
-; reloads text box tile patterns, current map view, and tileset tile patterns
-ReloadMapData::
- ldh a, [hLoadedROMBank]
- push af
- ld a, [wCurMap]
- call SwitchToMapRomBank
- call DisableLCD
- call LoadTextBoxTilePatterns
- call LoadCurrentMapView
- call LoadTilesetTilePatternData
- call EnableLCD
- pop af
- ldh [hLoadedROMBank], a
- ld [MBC1RomBank], a
- ret
-
-; reloads tileset tile patterns
-ReloadTilesetTilePatterns::
- ldh a, [hLoadedROMBank]
- push af
- ld a, [wCurMap]
- call SwitchToMapRomBank
- call DisableLCD
- call LoadTilesetTilePatternData
- call EnableLCD
- pop af
- ldh [hLoadedROMBank], a
- ld [MBC1RomBank], a
- ret
-
-; shows the town map and lets the player choose a destination to fly to
-ChooseFlyDestination::
- ld hl, wd72e
- res 4, [hl]
- farjp LoadTownMap_Fly
-
-; causes the text box to close without waiting for a button press after displaying text
-DisableWaitingAfterTextDisplay::
- ld a, $01
- ld [wDoNotWaitForButtonPressAfterDisplayingText], a
- ret
-
-; uses an item
-; UseItem is used with dummy items to perform certain other functions as well
-; INPUT:
-; [wcf91] = item ID
-; OUTPUT:
-; [wActionResultOrTookBattleTurn] = success
-; 00: unsuccessful
-; 01: successful
-; 02: not able to be used right now, no extra menu displayed (only certain items use this)
-UseItem::
- farjp UseItem_
-
-; confirms the item toss and then tosses the item
-; INPUT:
-; hl = address of inventory (either wNumBagItems or wNumBoxItems)
-; [wcf91] = item ID
-; [wWhichPokemon] = index of item within inventory
-; [wItemQuantity] = quantity to toss
-; OUTPUT:
-; clears carry flag if the item is tossed, sets carry flag if not
-TossItem::
- ldh a, [hLoadedROMBank]
- push af
- ld a, BANK(TossItem_)
- ldh [hLoadedROMBank], a
- ld [MBC1RomBank], a
- call TossItem_
- pop de
- ld a, d
- ldh [hLoadedROMBank], a
- ld [MBC1RomBank], a
- ret
-
-; checks if an item is a key item
-; INPUT:
-; [wcf91] = item ID
-; OUTPUT:
-; [wIsKeyItem] = result
-; 00: item is not key item
-; 01: item is key item
-IsKeyItem::
- push hl
- push de
- push bc
- farcall IsKeyItem_
- pop bc
- pop de
- pop hl
- ret
-
-; function to draw various text boxes
-; INPUT:
-; [wTextBoxID] = text box ID
-; b, c = y, x cursor position (TWO_OPTION_MENU only)
-DisplayTextBoxID::
- ldh a, [hLoadedROMBank]
- push af
- ld a, BANK(DisplayTextBoxID_)
- ldh [hLoadedROMBank], a
- ld [MBC1RomBank], a
- call DisplayTextBoxID_
- pop bc
- ld a, b
- ldh [hLoadedROMBank], a
- ld [MBC1RomBank], a
- ret
-
-; not zero if an NPC movement script is running, the player character is
-; automatically stepping down from a door, or joypad states are being simulated
-IsPlayerCharacterBeingControlledByGame::
- ld a, [wNPCMovementScriptPointerTableNum]
- and a
- ret nz
- ld a, [wd736]
- bit 1, a ; currently stepping down from door bit
- ret nz
- ld a, [wd730]
- and $80
- ret
-
-RunNPCMovementScript::
- ld hl, wd736
- bit 0, [hl]
- res 0, [hl]
- jr nz, .playerStepOutFromDoor
- ld a, [wNPCMovementScriptPointerTableNum]
- and a
- ret z
- dec a
- add a
- ld d, 0
- ld e, a
- ld hl, .NPCMovementScriptPointerTables
- add hl, de
- ld a, [hli]
- ld h, [hl]
- ld l, a
- ldh a, [hLoadedROMBank]
- push af
- ld a, [wNPCMovementScriptBank]
- ldh [hLoadedROMBank], a
- ld [MBC1RomBank], a
- ld a, [wNPCMovementScriptFunctionNum]
- call CallFunctionInTable
- pop af
- ldh [hLoadedROMBank], a
- ld [MBC1RomBank], a
- ret
-
-.NPCMovementScriptPointerTables
- dw PalletMovementScriptPointerTable
- dw PewterMuseumGuyMovementScriptPointerTable
- dw PewterGymGuyMovementScriptPointerTable
-.playerStepOutFromDoor
- farjp PlayerStepOutFromDoor
-
-EndNPCMovementScript::
- farjp _EndNPCMovementScript
-
-EmptyFunc2::
- ret
-
+INCLUDE "home/reload_tiles.asm"
+INCLUDE "home/item.asm"
+INCLUDE "home/textbox.asm"
+INCLUDE "home/npc_movement.asm"
INCLUDE "home/trainers.asm"
-
-; checks if the player's coordinates match an arrow movement tile's coordinates
-; and if so, decodes the RLE movement data
-; b = player Y
-; c = player X
-DecodeArrowMovementRLE::
- ld a, [hli]
- cp $ff
- ret z ; no match in the list
- cp b
- jr nz, .nextArrowMovementTileEntry1
- ld a, [hli]
- cp c
- jr nz, .nextArrowMovementTileEntry2
- ld a, [hli]
- ld d, [hl]
- ld e, a
- ld hl, wSimulatedJoypadStatesEnd
- call DecodeRLEList
- dec a
- ld [wSimulatedJoypadStatesIndex], a
- ret
-.nextArrowMovementTileEntry1
- inc hl
-.nextArrowMovementTileEntry2
- inc hl
- inc hl
- jr DecodeArrowMovementRLE
-
-TextScript_ItemStoragePC::
- call SaveScreenTilesToBuffer2
- ld b, BANK(PlayerPC)
- ld hl, PlayerPC
- jr bankswitchAndContinue
-
-TextScript_BillsPC::
- call SaveScreenTilesToBuffer2
- ld b, BANK(BillsPC_)
- ld hl, BillsPC_
- jr bankswitchAndContinue
-
-TextScript_GameCornerPrizeMenu::
-; XXX find a better name for this function
-; special_F7
- ld b, BANK(CeladonPrizeMenu)
- ld hl, CeladonPrizeMenu
-bankswitchAndContinue::
- call Bankswitch
- jp HoldTextDisplayOpen ; continue to main text-engine function
-
-TextScript_PokemonCenterPC::
- ld b, BANK(ActivatePC)
- ld hl, ActivatePC
- jr bankswitchAndContinue
-
-StartSimulatingJoypadStates::
- xor a
- ld [wOverrideSimulatedJoypadStatesMask], a
- ld [wSpritePlayerStateData2MovementByte1], a
- ld hl, wd730
- set 7, [hl]
- ret
-
-IsItemInBag::
-; 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
- predef GetQuantityOfItemInBag
- ld a, b
- and a
- ret
-
-DisplayPokedex::
- ld [wd11e], a
- farjp _DisplayPokedex
-
-SetSpriteFacingDirectionAndDelay::
- call SetSpriteFacingDirection
- ld c, 6
- jp DelayFrames
-
-SetSpriteFacingDirection::
- ld a, $9
- ldh [hSpriteDataOffset], a
- call GetPointerWithinSpriteStateData1
- ldh a, [hSpriteFacingDirection]
- ld [hl], a
- ret
-
-SetSpriteImageIndexAfterSettingFacingDirection::
- ld de, -7
- add hl, de
- ld [hl], a
- ret
-
-; tests if the player's coordinates are in a specified array
-; INPUT:
-; hl = address of array
-; OUTPUT:
-; [wCoordIndex] = if there is match, the matching array index
-; sets carry if the coordinates are in the array, clears carry if not
-ArePlayerCoordsInArray::
- ld a, [wYCoord]
- ld b, a
- ld a, [wXCoord]
- ld c, a
- ; fallthrough
-
-CheckCoords::
- xor a
- ld [wCoordIndex], a
-.loop
- ld a, [hli]
- cp $ff ; reached terminator?
- jr z, .notInArray
- push hl
- ld hl, wCoordIndex
- 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
-; [hSpriteIndex] = index of boulder sprite
-; OUTPUT:
-; [wCoordIndex] = if there is match, the matching array index
-; sets carry if the coordinates are in the array, clears carry if not
-CheckBoulderCoords::
- push hl
- ld hl, wSpritePlayerStateData2MapY
- ldh a, [hSpriteIndex]
- 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
-
-GetPointerWithinSpriteStateData1::
- ld h, $c1
- jr _GetPointerWithinSpriteStateData
-
-GetPointerWithinSpriteStateData2::
- ld h, $c2
-
-_GetPointerWithinSpriteStateData:
- ldh a, [hSpriteDataOffset]
- ld b, a
- ldh a, [hSpriteIndex]
- 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::
- xor a
- ld [wRLEByteCount], a ; count written bytes here
-.listLoop
- ld a, [de]
- cp $ff
- jr z, .endOfList
- ldh [hRLEByteValue], 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
- ldh a, [hRLEByteValue]
- 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 [hSpriteIndex] to $FE and byte 2 to [hSpriteMovementByte2]
-SetSpriteMovementBytesToFE::
- push hl
- call GetSpriteMovementByte1Pointer
- ld [hl], $fe
- call GetSpriteMovementByte2Pointer
- ldh a, [hSpriteMovementByte2]
- ld [hl], a
- pop hl
- ret
-
-; sets both movement bytes for sprite [hSpriteIndex] to $FF
-SetSpriteMovementBytesToFF::
- 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 [hSpriteIndex] in hl
-GetSpriteMovementByte1Pointer::
- ld h, $C2
- ldh a, [hSpriteIndex]
- swap a
- add 6
- ld l, a
- ret
-
-; returns the sprite movement byte 2 pointer for sprite [hSpriteIndex] in hl
-GetSpriteMovementByte2Pointer::
- push de
- ld hl, wMapSpriteData
- ldh a, [hSpriteIndex]
- dec a
- add a
- ld d, 0
- ld e, a
- add hl, de
- pop de
- ret
-
-GetTrainerInformation::
- call GetTrainerName
- ld a, [wLinkState]
- and a
- jr nz, .linkBattle
- ld a, BANK(TrainerPicAndMoneyPointers)
- call BankswitchHome
- ld a, [wTrainerClass]
- dec a
- ld hl, TrainerPicAndMoneyPointers
- ld bc, $5
- call AddNTimes
- ld de, wTrainerPicPointer
- ld a, [hli]
- ld [de], a
- inc de
- ld a, [hli]
- ld [de], a
- ld de, wTrainerBaseMoney
- ld a, [hli]
- ld [de], a
- inc de
- ld a, [hli]
- ld [de], a
- jp BankswitchBack
-.linkBattle
- ld hl, wTrainerPicPointer
- ld de, RedPicFront
- ld [hl], e
- inc hl
- ld [hl], d
- ret
-
-GetTrainerName::
- farjp GetTrainerName_
-
-HasEnoughMoney::
-; Check if the player has at least as much
-; money as the 3-byte BCD value at hMoney.
- ld de, wPlayerMoney
- ld hl, hMoney
- ld c, 3
- jp StringCmp
-
-HasEnoughCoins::
-; Check if the player has at least as many
-; coins as the 2-byte BCD value at hCoins.
- ld de, wPlayerCoins
- ld hl, hCoins
- ld c, 2
- jp StringCmp
-
+INCLUDE "home/map_objects.asm"
+INCLUDE "home/trainers2.asm"
+INCLUDE "home/money.asm"
INCLUDE "home/bankswitch.asm"
INCLUDE "home/yes_no.asm"
-
-; calculates the difference |a-b|, setting carry flag if a<b
-CalcDifference::
- sub b
- ret nc
- cpl
- add $1
- scf
- ret
-
-MoveSprite::
-; move the sprite [hSpriteIndex] with the movement pointed to by de
-; actually only copies the movement data to wNPCMovementDirections for later
- call SetSpriteMovementBytesToFF
-MoveSprite_::
- push hl
- push bc
- call GetSpriteMovementByte1Pointer
- xor a
- ld [hl], a
- ld hl, wNPCMovementDirections
- ld c, 0
-
-.loop
- ld a, [de]
- ld [hli], a
- inc de
- inc c
- cp $FF ; have we reached the end of the movement data?
- jr nz, .loop
-
- ld a, c
- ld [wNPCNumScriptedSteps], a ; number of steps taken
-
- pop bc
- ld hl, wd730
- set 0, [hl]
- pop hl
- xor a
- ld [wOverrideSimulatedJoypadStatesMask], a
- ld [wSimulatedJoypadStatesEnd], a
- dec a
- ld [wJoyIgnore], a
- ld [wWastedByteCD3A], a
- ret
-
-; divides [hDividend2] by [hDivisor2] and stores the quotient in [hQuotient2]
-DivideBytes::
- push hl
- ld hl, hQuotient2
- xor a
- ld [hld], a
- ld a, [hld]
- and a
- jr z, .done
- ld a, [hli]
-.loop
- sub [hl]
- jr c, .done
- inc hl
- inc [hl]
- dec hl
- jr .loop
-.done
- pop hl
- ret
-
-LoadFontTilePatterns::
- ldh a, [rLCDC]
- bit 7, a ; is the LCD enabled?
- jr nz, .on
-.off
- ld hl, FontGraphics
- ld de, vFont
- ld bc, FontGraphicsEnd - FontGraphics
- ld a, BANK(FontGraphics)
- jp FarCopyDataDouble ; if LCD is off, transfer all at once
-.on
- ld de, FontGraphics
- ld hl, vFont
- lb bc, BANK(FontGraphics), (FontGraphicsEnd - FontGraphics) / $8
- jp CopyVideoDataDouble ; if LCD is on, transfer during V-blank
-
-LoadTextBoxTilePatterns::
- ldh a, [rLCDC]
- bit 7, a ; is the LCD enabled?
- jr nz, .on
-.off
- ld hl, TextBoxGraphics
- ld de, vChars2 tile $60
- ld bc, TextBoxGraphicsEnd - TextBoxGraphics
- ld a, BANK(TextBoxGraphics)
- jp FarCopyData2 ; if LCD is off, transfer all at once
-.on
- ld de, TextBoxGraphics
- ld hl, vChars2 tile $60
- lb bc, BANK(TextBoxGraphics), (TextBoxGraphicsEnd - TextBoxGraphics) / $10
- jp CopyVideoData ; if LCD is on, transfer during V-blank
-
-LoadHpBarAndStatusTilePatterns::
- ldh a, [rLCDC]
- bit 7, a ; is the LCD enabled?
- jr nz, .on
-.off
- ld hl, HpBarAndStatusGraphics
- ld de, vChars2 tile $62
- ld bc, HpBarAndStatusGraphicsEnd - HpBarAndStatusGraphics
- ld a, BANK(HpBarAndStatusGraphics)
- jp FarCopyData2 ; if LCD is off, transfer all at once
-.on
- ld de, HpBarAndStatusGraphics
- ld hl, vChars2 tile $62
- lb bc, BANK(HpBarAndStatusGraphics), (HpBarAndStatusGraphicsEnd - HpBarAndStatusGraphics) / $10
- jp CopyVideoData ; if LCD is on, transfer during V-blank
-
-FillMemory::
-; Fill bc bytes at hl with a.
- push de
- ld d, a
-.loop
- ld a, d
- ld [hli], a
- dec bc
- ld a, b
- or c
- jr nz, .loop
- pop de
- ret
-
-UncompressSpriteFromDE::
-; Decompress pic at a:de.
- ld hl, wSpriteInputPtr
- ld [hl], e
- inc hl
- ld [hl], d
- jp UncompressSpriteData
-
-SaveScreenTilesToBuffer2::
- hlcoord 0, 0
- ld de, wTileMapBackup2
- ld bc, SCREEN_WIDTH * SCREEN_HEIGHT
- call CopyData
- ret
-
-LoadScreenTilesFromBuffer2::
- call LoadScreenTilesFromBuffer2DisableBGTransfer
- ld a, 1
- ldh [hAutoBGTransferEnabled], a
- ret
-
-; loads screen tiles stored in wTileMapBackup2 but leaves hAutoBGTransferEnabled disabled
-LoadScreenTilesFromBuffer2DisableBGTransfer::
- xor a
- ldh [hAutoBGTransferEnabled], a
- ld hl, wTileMapBackup2
- decoord 0, 0
- ld bc, SCREEN_WIDTH * SCREEN_HEIGHT
- call CopyData
- ret
-
-SaveScreenTilesToBuffer1::
- hlcoord 0, 0
- ld de, wTileMapBackup
- ld bc, SCREEN_WIDTH * SCREEN_HEIGHT
- jp CopyData
-
-LoadScreenTilesFromBuffer1::
- xor a
- ldh [hAutoBGTransferEnabled], a
- ld hl, wTileMapBackup
- decoord 0, 0
- ld bc, SCREEN_WIDTH * SCREEN_HEIGHT
- call CopyData
- ld a, 1
- ldh [hAutoBGTransferEnabled], a
- ret
-
-DelayFrames::
-; wait c frames
- call DelayFrame
- dec c
- jr nz, DelayFrames
- ret
-
-PlaySoundWaitForCurrent::
- push af
- call WaitForSoundToFinish
- pop af
- jp PlaySound
-
-; Wait for sound to finish playing
-WaitForSoundToFinish::
- ld a, [wLowHealthAlarm]
- and $80
- ret nz
- push hl
-.waitLoop
- ld hl, wChannelSoundIDs + Ch5
- xor a
- or [hl]
- inc hl
- or [hl]
- inc hl
- inc hl
- or [hl]
- jr nz, .waitLoop
- pop hl
- ret
-
+INCLUDE "home/pathfinding.asm"
+INCLUDE "home/load_font.asm"
+INCLUDE "home/tilemap.asm"
+INCLUDE "home/delay.asm"
INCLUDE "home/names2.asm"
-
-GetItemPrice::
-; Stores item's price as BCD at hItemPrice (3 bytes)
-; Input: [wcf91] = item id
- ldh a, [hLoadedROMBank]
- push af
- ld a, [wListMenuID]
- cp MOVESLISTMENU
- ld a, BANK(ItemPrices)
- jr nz, .ok
- ld a, $f ; hardcoded Bank
-.ok
- ldh [hLoadedROMBank], a
- ld [MBC1RomBank], a
- ld hl, wItemPrices
- ld a, [hli]
- ld h, [hl]
- ld l, a
- ld a, [wcf91] ; a contains item id
- cp HM01
- jr nc, .getTMPrice
- ld bc, $3
-.loop
- add hl, bc
- dec a
- jr nz, .loop
- dec hl
- ld a, [hld]
- ldh [hItemPrice + 2], a
- ld a, [hld]
- ldh [hItemPrice + 1], a
- ld a, [hl]
- ldh [hItemPrice], a
- jr .done
-.getTMPrice
- ld a, BANK(GetMachinePrice)
- ldh [hLoadedROMBank], a
- ld [MBC1RomBank], a
- call GetMachinePrice
-.done
- ld de, hItemPrice
- pop af
- ldh [hLoadedROMBank], a
- ld [MBC1RomBank], a
- ret
-
-; copies a string from [de] to [wcf4b]
-CopyStringToCF4B::
- ld hl, wcf4b
- ; fall through
-
-; copies a string from [de] to [hl]
-CopyString::
- 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: [hJoy5] = pressed buttons in usual format
-; there are two flags that control its functionality, [hJoy6] and [hJoy7]
-; there are essentially three modes of operation
-; 1. Get newly pressed buttons only
-; ([hJoy7] == 0, [hJoy6] == any)
-; Just copies [hJoyPressed] to [hJoy5].
-; 2. Get currently pressed buttons at low sample rate with delay
-; ([hJoy7] == 1, [hJoy6] != 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.
-; ([hJoy7] == 1, [hJoy6] == 0)
-JoypadLowSensitivity::
- call Joypad
- ldh a, [hJoy7] ; flag
- and a ; get all currently pressed buttons or only newly pressed buttons?
- ldh a, [hJoyPressed] ; newly pressed buttons
- jr z, .storeButtonState
- ldh a, [hJoyHeld] ; all currently pressed buttons
-.storeButtonState
- ldh [hJoy5], a
- ldh a, [hJoyPressed] ; 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
- ldh [hFrameCounter], a
- ret
-.noNewlyPressedButtons
- ldh a, [hFrameCounter]
- and a ; is the delay over?
- jr z, .delayOver
-.delayNotOver
- xor a
- ldh [hJoy5], a ; report no buttons as pressed
- ret
-.delayOver
-; if [hJoy6] = 0 and A or B is pressed, report no buttons as pressed
- ldh a, [hJoyHeld]
- and A_BUTTON | B_BUTTON
- jr z, .setShortDelay
- ldh a, [hJoy6] ; flag
- and a
- jr nz, .setShortDelay
- xor a
- ldh [hJoy5], a
-.setShortDelay
- ld a, 5 ; 1/12 of a second delay
- ldh [hFrameCounter], a
- ret
-
-WaitForTextScrollButtonPress::
- ldh a, [hDownArrowBlinkCount1]
- push af
- ldh a, [hDownArrowBlinkCount2]
- push af
- xor a
- ldh [hDownArrowBlinkCount1], a
- ld a, $6
- ldh [hDownArrowBlinkCount2], a
-.loop
- push hl
- ld a, [wTownMapSpriteBlinkingEnabled]
- and a
- jr z, .skipAnimation
- call TownMapSpriteBlinkingAnimation
-.skipAnimation
- hlcoord 18, 16
- call HandleDownArrowBlinkTiming
- pop hl
- call JoypadLowSensitivity
- predef CableClub_Run
- ldh a, [hJoy5]
- and A_BUTTON | B_BUTTON
- jr z, .loop
- pop af
- ldh [hDownArrowBlinkCount2], a
- pop af
- ldh [hDownArrowBlinkCount1], a
- ret
-
-; (unless in link battle) waits for A or B being pressed and outputs the scrolling sound effect
-ManualTextScroll::
- ld a, [wLinkState]
- cp LINK_STATE_BATTLING
- jr z, .inLinkBattle
- call WaitForTextScrollButtonPress
- ld a, SFX_PRESS_AB
- jp PlaySound
-.inLinkBattle
- ld c, 65
- jp DelayFrames
-
-; function to do multiplication
-; all values are big endian
-; INPUT
-; FF96-FF98 = multiplicand
-; FF99 = multiplier
-; OUTPUT
-; FF95-FF98 = product
-Multiply::
- push hl
- push bc
- callfar _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::
- push hl
- push de
- push bc
- ldh a, [hLoadedROMBank]
- push af
- ld a, BANK(_Divide)
- ldh [hLoadedROMBank], a
- ld [MBC1RomBank], a
- call _Divide
- pop af
- ldh [hLoadedROMBank], a
- ld [MBC1RomBank], 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 [wd730] or [wLetterPrintingDelayFlags] flags.
-PrintLetterDelay::
- ld a, [wd730]
- bit 6, a
- ret nz
- ld a, [wLetterPrintingDelayFlags]
- bit 1, a
- ret z
- push hl
- push de
- push bc
- ld a, [wLetterPrintingDelayFlags]
- bit 0, a
- jr z, .waitOneFrame
- ld a, [wOptions]
- and $f
- ldh [hFrameCounter], a
- jr .checkButtons
-.waitOneFrame
- ld a, 1
- ldh [hFrameCounter], a
-.checkButtons
- call Joypad
- ldh a, [hJoyHeld]
-.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
- ldh a, [hFrameCounter]
- and a
- jr nz, .checkButtons
-.done
- pop bc
- pop de
- pop hl
- ret
-
-; Copies [hl, bc) to [de, de + bc - hl).
-; In other words, the source data is from hl up to but not including bc,
-; and the destination is de.
-CopyDataUntil::
- 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
-
+INCLUDE "home/item_price.asm"
+INCLUDE "home/copy_string.asm"
+INCLUDE "home/joypad2.asm"
+INCLUDE "home/math.asm"
+INCLUDE "home/print_text.asm"
INCLUDE "home/move_mon.asm"
-
-; skips a text entries, each of size NAME_LENGTH (like trainer name, OT name, rival name, ...)
-; hl: base pointer, will be incremented by NAME_LENGTH * a
-SkipFixedLengthTextEntries::
- and a
- ret z
- ld bc, NAME_LENGTH
-.skipLoop
- add hl, bc
- dec a
- jr nz, .skipLoop
- ret
-
-AddNTimes::
-; 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::
- 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::
- ld h, HIGH(wOAMBuffer)
- 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::
- xor a
- ld [wPartyMenuAnimMonEnabled], a
-
-HandleMenuInput_::
- ldh a, [hDownArrowBlinkCount1]
- push af
- ldh a, [hDownArrowBlinkCount2]
- push af ; save existing values on stack
- xor a
- ldh [hDownArrowBlinkCount1], a ; blinking down arrow timing value 1
- ld a, 6
- ldh [hDownArrowBlinkCount2], a ; blinking down arrow timing value 2
-.loop1
- xor a
- ld [wAnimCounter], a ; counter for pokemon shaking animation
- call PlaceMenuCursor
- call Delay3
-.loop2
- push hl
- ld a, [wPartyMenuAnimMonEnabled]
- and a ; is it a pokemon selection menu?
- jr z, .getJoypadState
- farcall AnimatePartyMon ; shake mini sprite of selected pokemon
-.getJoypadState
- pop hl
- call JoypadLowSensitivity
- ldh a, [hJoy5]
- and a ; was a key pressed?
- jr nz, .keyPressed
- push hl
- hlcoord 18, 11 ; coordinates of blinking down arrow in some menus
- 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
- ldh [hDownArrowBlinkCount2], a
- pop af
- ldh [hDownArrowBlinkCount1], a ; restore previous values
- xor a
- ld [wMenuWrappingEnabled], a ; disable menu wrapping
- ret
-.keyPressed
- xor a
- ld [wCheckFor180DegreeTurn], a
- ldh a, [hJoy5]
- 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
- ldh a, [hJoy5]
- and A_BUTTON | B_BUTTON
- jr z, .skipPlayingSound
-.AButtonOrBButtonPressed
- push hl
- ld hl, wFlags_0xcd60
- bit 5, [hl]
- pop hl
- jr nz, .skipPlayingSound
- ld a, SFX_PRESS_AB
- call PlaySound
-.skipPlayingSound
- pop af
- ldh [hDownArrowBlinkCount2], a
- pop af
- ldh [hDownArrowBlinkCount1], a ; restore previous values
- xor a
- ld [wMenuWrappingEnabled], a ; disable menu wrapping
- ldh a, [hJoy5]
- ret
-.noWrappingAround
- ld a, [wMenuWatchMovingOutOfBounds]
- and a ; should we return if the user tried to go past the top or bottom?
- jr z, .checkOtherKeys
- jr .checkIfAButtonOrBButtonPressed
-
-PlaceMenuCursor::
- ld a, [wTopMenuItemY]
- and a ; is the y coordinate 0?
- jr z, .adjustForXCoord
- hlcoord 0, 0
- ld bc, SCREEN_WIDTH
-.topMenuItemLoop
- add hl, bc
- dec a
- jr nz, .topMenuItemLoop
-.adjustForXCoord
- ld a, [wTopMenuItemX]
- ld b, 0
- ld c, a
- add hl, bc
- push hl
- ld a, [wLastMenuItem]
- and a ; was the previous menu id 0?
- jr z, .checkForArrow1
- push af
- ldh a, [hFlagsFFF6]
- 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 "▶" ; 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
- ldh a, [hFlagsFFF6]
- 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 "▶" ; 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::
- 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::
- 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 hDownArrowBlinkCount1 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
-; initialized 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::
- ld a, [hl]
- ld b, a
- ld a, "▼"
- cp b
- jr nz, .downArrowOff
-.downArrowOn
- ldh a, [hDownArrowBlinkCount1]
- dec a
- ldh [hDownArrowBlinkCount1], a
- ret nz
- ldh a, [hDownArrowBlinkCount2]
- dec a
- ldh [hDownArrowBlinkCount2], a
- ret nz
- ld a, " "
- ld [hl], a
- ld a, $ff
- ldh [hDownArrowBlinkCount1], a
- ld a, $06
- ldh [hDownArrowBlinkCount2], a
- ret
-.downArrowOff
- ldh a, [hDownArrowBlinkCount1]
- and a
- ret z
- dec a
- ldh [hDownArrowBlinkCount1], a
- ret nz
- dec a
- ldh [hDownArrowBlinkCount1], a
- ldh a, [hDownArrowBlinkCount2]
- dec a
- ldh [hDownArrowBlinkCount2], a
- ret nz
- ld a, $06
- ldh [hDownArrowBlinkCount2], a
- ld a, "▼"
- 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 [wEnteringCableClub] is set).
-
-EnableAutoTextBoxDrawing::
- xor a
- jr AutoTextBoxDrawingCommon
-
-DisableAutoTextBoxDrawing::
- ld a, $01
-
-AutoTextBoxDrawingCommon::
- ld [wAutoTextBoxDrawingControl], a
- xor a
- ld [wDoNotWaitForButtonPressAfterDisplayingText], a ; make DisplayTextID wait for button press
- ret
-
-PrintText::
-; Print text hl at (1, 14).
- push hl
- ld a, MESSAGE_BOX
- ld [wTextBoxID], a
- call DisplayTextBoxID
- call UpdateSprites
- call Delay3
- pop hl
-PrintText_NoCreatingTextBox::
- bccoord 1, 14
- jp TextCommandProcessor
-
+INCLUDE "home/array.asm"
+INCLUDE "home/compare.asm"
+INCLUDE "home/oam.asm"
+INCLUDE "home/window.asm"
INCLUDE "home/print_num.asm"
-
-CallFunctionInTable::
-; Call function a in jumptable hl.
-; de is not preserved.
- 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
-
-RestoreScreenTilesAndReloadTilePatterns::
- call ClearSprites
- ld a, $1
- ld [wUpdateSpritesEnabled], a
- call ReloadMapSpriteTilePatterns
- call LoadScreenTilesFromBuffer2
- call LoadTextBoxTilePatterns
- call RunDefaultPaletteCommand
- 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
- ldh [rBGP], a
- ld a, %11010000 ; 3100
- ldh [rOBP0], a
- ret
-
-GBPalWhiteOut::
-; White out all palettes.
- xor a
- ldh [rBGP], a
- ldh [rOBP0], a
- ldh [rOBP1], a
- ret
-
-RunDefaultPaletteCommand::
- ld b, SET_PAL_DEFAULT
-RunPaletteCommand::
- ld a, [wOnSGB]
- and a
- ret z
- predef_jump _RunPaletteCommand
-
-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
-
-; Copy the current map's sprites' tile patterns to VRAM again after they have
-; been overwritten by other tile patterns.
-ReloadMapSpriteTilePatterns::
- ld hl, wFontLoaded
- ld a, [hl]
- push af
- res 0, [hl]
- push hl
- xor a
- ld [wSpriteSetID], a
- call DisableLCD
- farcall 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 wcf4b.
-; Return carry on success.
- ld a, b
- ld [wd11e], a
- ld [wcf91], a
- ld a, c
- ld [wItemQuantity], 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 [wcf91], a
- ld a, c
- ld [wCurEnemyLVL], a
- xor a ; PLAYER_PARTY_DATA
- ld [wMonDataLocation], a
- farjp _GivePokemon
-
-Random::
-; Return a random number in a.
-; For battles, use BattleRandom.
- push hl
- push de
- push bc
- farcall Random_
- ldh a, [hRandomAdd]
- pop bc
- pop de
- pop hl
- ret
-
+INCLUDE "home/array2.asm"
+INCLUDE "home/palettes.asm"
+INCLUDE "home/reload_sprites.asm"
+INCLUDE "home/give.asm"
+INCLUDE "home/random.asm"
INCLUDE "home/predef.asm"
-
-UpdateCinnabarGymGateTileBlocks::
- farjp UpdateCinnabarGymGateTileBlocks_
-
-CheckForHiddenObjectOrBookshelfOrCardKeyDoor::
- ldh a, [hLoadedROMBank]
- push af
- ldh a, [hJoyHeld]
- bit 0, a ; A button
- jr z, .nothingFound
-; A button is pressed
- ld a, BANK(CheckForHiddenObject)
- ld [MBC1RomBank], a
- ldh [hLoadedROMBank], a
- call CheckForHiddenObject
- ldh a, [hDidntFindAnyHiddenObject]
- and a
- jr nz, .hiddenObjectNotFound
- ld a, [wHiddenObjectFunctionRomBank]
- ld [MBC1RomBank], a
- ldh [hLoadedROMBank], a
- ld de, .returnAddress
- push de
- jp hl
-.returnAddress
- xor a
- jr .done
-.hiddenObjectNotFound
- farcall PrintBookshelfText
- ldh a, [hFFDB]
- and a
- jr z, .done
-.nothingFound
- ld a, $ff
-.done
- ldh [hItemAlreadyFound], a
- pop af
- ld [MBC1RomBank], a
- ldh [hLoadedROMBank], a
- ret
-
-PrintPredefTextID::
- ldh [hSpriteIndexOrTextID], a
- ld hl, TextPredefs
- call SetMapTextPointer
- ld hl, wTextPredefFlag
- set 0, [hl]
- call DisplayTextID
-
-RestoreMapTextPointer::
- ld hl, wMapTextPtr
- ldh a, [hSavedMapTextPtr]
- ld [hli], a
- ldh a, [hSavedMapTextPtr + 1]
- ld [hl], a
- ret
-
-SetMapTextPointer::
- ld a, [wMapTextPtr]
- ldh [hSavedMapTextPtr], a
- ld a, [wMapTextPtr + 1]
- ldh [hSavedMapTextPtr + 1], a
- ld a, l
- ld [wMapTextPtr], a
- ld a, h
- ld [wMapTextPtr + 1], a
- ret
-
-INCLUDE "data/text_predef_pointers.asm"
+INCLUDE "home/hidden_objects.asm"
+INCLUDE "home/predef_text.asm"
--- /dev/null
+++ b/home/array.asm
@@ -1,0 +1,21 @@
+; skips a text entries, each of size NAME_LENGTH (like trainer name, OT name, rival name, ...)
+; hl: base pointer, will be incremented by NAME_LENGTH * a
+SkipFixedLengthTextEntries::
+ and a
+ ret z
+ ld bc, NAME_LENGTH
+.skipLoop
+ add hl, bc
+ dec a
+ jr nz, .skipLoop
+ ret
+
+AddNTimes::
+; add bc to hl a times
+ and a
+ ret z
+.loop
+ add hl, bc
+ dec a
+ jr nz, .loop
+ ret
--- /dev/null
+++ b/home/array2.asm
@@ -1,0 +1,47 @@
+CallFunctionInTable::
+; Call function a in jumptable hl.
+; de is not preserved.
+ 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
--- /dev/null
+++ b/home/compare.asm
@@ -1,0 +1,11 @@
+; Compare strings, c bytes in length, at de and hl.
+; Often used to compare big endian numbers in battle calculations.
+StringCmp::
+ ld a, [de]
+ cp [hl]
+ ret nz
+ inc de
+ inc hl
+ dec c
+ jr nz, StringCmp
+ ret
--- /dev/null
+++ b/home/copy_string.asm
@@ -1,0 +1,13 @@
+; copies a string from [de] to [wcf4b]
+CopyStringToCF4B::
+ ld hl, wcf4b
+ ; fall through
+
+; copies a string from [de] to [hl]
+CopyString::
+ ld a, [de]
+ inc de
+ ld [hli], a
+ cp "@"
+ jr nz, CopyString
+ ret
--- /dev/null
+++ b/home/count_set_bits.asm
@@ -1,0 +1,24 @@
+; 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:
+; [wNumSetBits] = number of set bits
+CountSetBits::
+ 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 [wNumSetBits], a
+ ret
--- /dev/null
+++ b/home/delay.asm
@@ -1,0 +1,31 @@
+DelayFrames::
+; wait c frames
+ call DelayFrame
+ dec c
+ jr nz, DelayFrames
+ ret
+
+PlaySoundWaitForCurrent::
+ push af
+ call WaitForSoundToFinish
+ pop af
+ jp PlaySound
+
+; Wait for sound to finish playing
+WaitForSoundToFinish::
+ ld a, [wLowHealthAlarm]
+ and $80
+ ret nz
+ push hl
+.waitLoop
+ ld hl, wChannelSoundIDs + Ch5
+ xor a
+ or [hl]
+ inc hl
+ or [hl]
+ inc hl
+ inc hl
+ or [hl]
+ jr nz, .waitLoop
+ pop hl
+ ret
--- /dev/null
+++ b/home/fade_audio.asm
@@ -1,0 +1,48 @@
+FadeOutAudio::
+ ld a, [wAudioFadeOutControl]
+ and a ; currently fading out audio?
+ jr nz, .fadingOut
+ ld a, [wd72c]
+ bit 1, a
+ ret nz
+ ld a, $77
+ ldh [rNR50], a
+ ret
+.fadingOut
+ ld a, [wAudioFadeOutCounter]
+ and a
+ jr z, .counterReachedZero
+ dec a
+ ld [wAudioFadeOutCounter], a
+ ret
+.counterReachedZero
+ ld a, [wAudioFadeOutCounterReloadValue]
+ ld [wAudioFadeOutCounter], a
+ ldh a, [rNR50]
+ and a ; has the volume reached 0?
+ jr z, .fadeOutComplete
+ ld b, a
+ and $f
+ dec a
+ ld c, a
+ ld a, b
+ and $f0
+ swap a
+ dec a
+ swap a
+ or c
+ ldh [rNR50], a
+ ret
+.fadeOutComplete
+ ld a, [wAudioFadeOutControl]
+ ld b, a
+ xor a
+ ld [wAudioFadeOutControl], a
+ ld a, SFX_STOP_ALL_MUSIC
+ ld [wNewSoundID], a
+ call PlaySound
+ ld a, [wAudioSavedROMBank]
+ ld [wAudioROMBank], a
+ ld a, b
+ ld [wNewSoundID], a
+ jp PlaySound
--- /dev/null
+++ b/home/give.asm
@@ -1,0 +1,26 @@
+GiveItem::
+; Give player quantity c of item b,
+; and copy the item's name to wcf4b.
+; Return carry on success.
+ ld a, b
+ ld [wd11e], a
+ ld [wcf91], a
+ ld a, c
+ ld [wItemQuantity], 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 [wcf91], a
+ ld a, c
+ ld [wCurEnemyLVL], a
+ xor a ; PLAYER_PARTY_DATA
+ ld [wMonDataLocation], a
+ farjp _GivePokemon
--- /dev/null
+++ b/home/hidden_objects.asm
@@ -1,0 +1,39 @@
+UpdateCinnabarGymGateTileBlocks::
+ farjp UpdateCinnabarGymGateTileBlocks_
+
+CheckForHiddenObjectOrBookshelfOrCardKeyDoor::
+ ldh a, [hLoadedROMBank]
+ push af
+ ldh a, [hJoyHeld]
+ bit 0, a ; A button
+ jr z, .nothingFound
+; A button is pressed
+ ld a, BANK(CheckForHiddenObject)
+ ld [MBC1RomBank], a
+ ldh [hLoadedROMBank], a
+ call CheckForHiddenObject
+ ldh a, [hDidntFindAnyHiddenObject]
+ and a
+ jr nz, .hiddenObjectNotFound
+ ld a, [wHiddenObjectFunctionRomBank]
+ ld [MBC1RomBank], a
+ ldh [hLoadedROMBank], a
+ ld de, .returnAddress
+ push de
+ jp hl
+.returnAddress
+ xor a
+ jr .done
+.hiddenObjectNotFound
+ farcall PrintBookshelfText
+ ldh a, [hFFDB]
+ and a
+ jr z, .done
+.nothingFound
+ ld a, $ff
+.done
+ ldh [hItemAlreadyFound], a
+ pop af
+ ld [MBC1RomBank], a
+ ldh [hLoadedROMBank], a
+ ret
--- /dev/null
+++ b/home/inventory.asm
@@ -1,0 +1,55 @@
+; subtracts the amount the player paid from their money
+; OUTPUT: carry = 0(success) or 1(fail because there is not enough money)
+SubtractAmountPaidFromMoney::
+ farjp SubtractAmountPaidFromMoney_
+
+; adds the amount the player sold to their money
+AddAmountSoldToMoney::
+ ld de, wPlayerMoney + 2
+ ld hl, hMoney + 2 ; total price of items
+ ld c, 3 ; length of money in bytes
+ predef AddBCDPredef ; add total price to money
+ ld a, MONEY_BOX
+ ld [wTextBoxID], a
+ call DisplayTextBoxID ; redraw money text box
+ ld a, SFX_PURCHASE
+ call PlaySoundWaitForCurrent
+ jp WaitForSoundToFinish
+
+; 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)
+; [wWhichPokemon] = index (within the inventory) of the item to remove
+; [wItemQuantity] = quantity to remove
+RemoveItemFromInventory::
+ ldh a, [hLoadedROMBank]
+ push af
+ ld a, BANK(RemoveItemFromInventory_)
+ ldh [hLoadedROMBank], a
+ ld [MBC1RomBank], a
+ call RemoveItemFromInventory_
+ pop af
+ ldh [hLoadedROMBank], a
+ ld [MBC1RomBank], 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)
+; [wcf91] = item ID
+; [wItemQuantity] = item quantity
+; sets carry flag if successful, unsets carry flag if unsuccessful
+AddItemToInventory::
+ push bc
+ ldh a, [hLoadedROMBank]
+ push af
+ ld a, BANK(AddItemToInventory_)
+ ldh [hLoadedROMBank], a
+ ld [MBC1RomBank], a
+ call AddItemToInventory_
+ pop bc
+ ld a, b
+ ldh [hLoadedROMBank], a
+ ld [MBC1RomBank], a
+ pop bc
+ ret
--- /dev/null
+++ b/home/item.asm
@@ -1,0 +1,49 @@
+; uses an item
+; UseItem is used with dummy items to perform certain other functions as well
+; INPUT:
+; [wcf91] = item ID
+; OUTPUT:
+; [wActionResultOrTookBattleTurn] = success
+; 00: unsuccessful
+; 01: successful
+; 02: not able to be used right now, no extra menu displayed (only certain items use this)
+UseItem::
+ farjp UseItem_
+
+; confirms the item toss and then tosses the item
+; INPUT:
+; hl = address of inventory (either wNumBagItems or wNumBoxItems)
+; [wcf91] = item ID
+; [wWhichPokemon] = index of item within inventory
+; [wItemQuantity] = quantity to toss
+; OUTPUT:
+; clears carry flag if the item is tossed, sets carry flag if not
+TossItem::
+ ldh a, [hLoadedROMBank]
+ push af
+ ld a, BANK(TossItem_)
+ ldh [hLoadedROMBank], a
+ ld [MBC1RomBank], a
+ call TossItem_
+ pop de
+ ld a, d
+ ldh [hLoadedROMBank], a
+ ld [MBC1RomBank], a
+ ret
+
+; checks if an item is a key item
+; INPUT:
+; [wcf91] = item ID
+; OUTPUT:
+; [wIsKeyItem] = result
+; 00: item is not key item
+; 01: item is key item
+IsKeyItem::
+ push hl
+ push de
+ push bc
+ farcall IsKeyItem_
+ pop bc
+ pop de
+ pop hl
+ ret
--- /dev/null
+++ b/home/item_price.asm
@@ -1,0 +1,44 @@
+GetItemPrice::
+; Stores item's price as BCD at hItemPrice (3 bytes)
+; Input: [wcf91] = item id
+ ldh a, [hLoadedROMBank]
+ push af
+ ld a, [wListMenuID]
+ cp MOVESLISTMENU
+ ld a, BANK(ItemPrices)
+ jr nz, .ok
+ ld a, $f ; hardcoded Bank
+.ok
+ ldh [hLoadedROMBank], a
+ ld [MBC1RomBank], a
+ ld hl, wItemPrices
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ld a, [wcf91] ; a contains item id
+ cp HM01
+ jr nc, .getTMPrice
+ ld bc, $3
+.loop
+ add hl, bc
+ dec a
+ jr nz, .loop
+ dec hl
+ ld a, [hld]
+ ldh [hItemPrice + 2], a
+ ld a, [hld]
+ ldh [hItemPrice + 1], a
+ ld a, [hl]
+ ldh [hItemPrice], a
+ jr .done
+.getTMPrice
+ ld a, BANK(GetMachinePrice)
+ ldh [hLoadedROMBank], a
+ ld [MBC1RomBank], a
+ call GetMachinePrice
+.done
+ ld de, hItemPrice
+ pop af
+ ldh [hLoadedROMBank], a
+ ld [MBC1RomBank], a
+ ret
--- /dev/null
+++ b/home/joypad2.asm
@@ -1,0 +1,95 @@
+; this function is used when lower button sensitivity is wanted (e.g. menus)
+; OUTPUT: [hJoy5] = pressed buttons in usual format
+; there are two flags that control its functionality, [hJoy6] and [hJoy7]
+; there are essentially three modes of operation
+; 1. Get newly pressed buttons only
+; ([hJoy7] == 0, [hJoy6] == any)
+; Just copies [hJoyPressed] to [hJoy5].
+; 2. Get currently pressed buttons at low sample rate with delay
+; ([hJoy7] == 1, [hJoy6] != 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.
+; ([hJoy7] == 1, [hJoy6] == 0)
+JoypadLowSensitivity::
+ call Joypad
+ ldh a, [hJoy7] ; flag
+ and a ; get all currently pressed buttons or only newly pressed buttons?
+ ldh a, [hJoyPressed] ; newly pressed buttons
+ jr z, .storeButtonState
+ ldh a, [hJoyHeld] ; all currently pressed buttons
+.storeButtonState
+ ldh [hJoy5], a
+ ldh a, [hJoyPressed] ; 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
+ ldh [hFrameCounter], a
+ ret
+.noNewlyPressedButtons
+ ldh a, [hFrameCounter]
+ and a ; is the delay over?
+ jr z, .delayOver
+.delayNotOver
+ xor a
+ ldh [hJoy5], a ; report no buttons as pressed
+ ret
+.delayOver
+; if [hJoy6] = 0 and A or B is pressed, report no buttons as pressed
+ ldh a, [hJoyHeld]
+ and A_BUTTON | B_BUTTON
+ jr z, .setShortDelay
+ ldh a, [hJoy6] ; flag
+ and a
+ jr nz, .setShortDelay
+ xor a
+ ldh [hJoy5], a
+.setShortDelay
+ ld a, 5 ; 1/12 of a second delay
+ ldh [hFrameCounter], a
+ ret
+
+WaitForTextScrollButtonPress::
+ ldh a, [hDownArrowBlinkCount1]
+ push af
+ ldh a, [hDownArrowBlinkCount2]
+ push af
+ xor a
+ ldh [hDownArrowBlinkCount1], a
+ ld a, $6
+ ldh [hDownArrowBlinkCount2], a
+.loop
+ push hl
+ ld a, [wTownMapSpriteBlinkingEnabled]
+ and a
+ jr z, .skipAnimation
+ call TownMapSpriteBlinkingAnimation
+.skipAnimation
+ hlcoord 18, 16
+ call HandleDownArrowBlinkTiming
+ pop hl
+ call JoypadLowSensitivity
+ predef CableClub_Run
+ ldh a, [hJoy5]
+ and A_BUTTON | B_BUTTON
+ jr z, .loop
+ pop af
+ ldh [hDownArrowBlinkCount2], a
+ pop af
+ ldh [hDownArrowBlinkCount1], a
+ ret
+
+; (unless in link battle) waits for A or B being pressed and outputs the scrolling sound effect
+ManualTextScroll::
+ ld a, [wLinkState]
+ cp LINK_STATE_BATTLING
+ jr z, .inLinkBattle
+ call WaitForTextScrollButtonPress
+ ld a, SFX_PRESS_AB
+ jp PlaySound
+.inLinkBattle
+ ld c, 65
+ jp DelayFrames
--- /dev/null
+++ b/home/load_font.asm
@@ -1,0 +1,47 @@
+LoadFontTilePatterns::
+ ldh a, [rLCDC]
+ bit 7, a ; is the LCD enabled?
+ jr nz, .on
+.off
+ ld hl, FontGraphics
+ ld de, vFont
+ ld bc, FontGraphicsEnd - FontGraphics
+ ld a, BANK(FontGraphics)
+ jp FarCopyDataDouble ; if LCD is off, transfer all at once
+.on
+ ld de, FontGraphics
+ ld hl, vFont
+ lb bc, BANK(FontGraphics), (FontGraphicsEnd - FontGraphics) / $8
+ jp CopyVideoDataDouble ; if LCD is on, transfer during V-blank
+
+LoadTextBoxTilePatterns::
+ ldh a, [rLCDC]
+ bit 7, a ; is the LCD enabled?
+ jr nz, .on
+.off
+ ld hl, TextBoxGraphics
+ ld de, vChars2 tile $60
+ ld bc, TextBoxGraphicsEnd - TextBoxGraphics
+ ld a, BANK(TextBoxGraphics)
+ jp FarCopyData2 ; if LCD is off, transfer all at once
+.on
+ ld de, TextBoxGraphics
+ ld hl, vChars2 tile $60
+ lb bc, BANK(TextBoxGraphics), (TextBoxGraphicsEnd - TextBoxGraphics) / $10
+ jp CopyVideoData ; if LCD is on, transfer during V-blank
+
+LoadHpBarAndStatusTilePatterns::
+ ldh a, [rLCDC]
+ bit 7, a ; is the LCD enabled?
+ jr nz, .on
+.off
+ ld hl, HpBarAndStatusGraphics
+ ld de, vChars2 tile $62
+ ld bc, HpBarAndStatusGraphicsEnd - HpBarAndStatusGraphics
+ ld a, BANK(HpBarAndStatusGraphics)
+ jp FarCopyData2 ; if LCD is off, transfer all at once
+.on
+ ld de, HpBarAndStatusGraphics
+ ld hl, vChars2 tile $62
+ lb bc, BANK(HpBarAndStatusGraphics), (HpBarAndStatusGraphicsEnd - HpBarAndStatusGraphics) / $10
+ jp CopyVideoData ; if LCD is on, transfer during V-blank
--- /dev/null
+++ b/home/map_objects.asm
@@ -1,0 +1,248 @@
+; checks if the player's coordinates match an arrow movement tile's coordinates
+; and if so, decodes the RLE movement data
+; b = player Y
+; c = player X
+DecodeArrowMovementRLE::
+ ld a, [hli]
+ cp $ff
+ ret z ; no match in the list
+ cp b
+ jr nz, .nextArrowMovementTileEntry1
+ ld a, [hli]
+ cp c
+ jr nz, .nextArrowMovementTileEntry2
+ ld a, [hli]
+ ld d, [hl]
+ ld e, a
+ ld hl, wSimulatedJoypadStatesEnd
+ call DecodeRLEList
+ dec a
+ ld [wSimulatedJoypadStatesIndex], a
+ ret
+.nextArrowMovementTileEntry1
+ inc hl
+.nextArrowMovementTileEntry2
+ inc hl
+ inc hl
+ jr DecodeArrowMovementRLE
+
+TextScript_ItemStoragePC::
+ call SaveScreenTilesToBuffer2
+ ld b, BANK(PlayerPC)
+ ld hl, PlayerPC
+ jr bankswitchAndContinue
+
+TextScript_BillsPC::
+ call SaveScreenTilesToBuffer2
+ ld b, BANK(BillsPC_)
+ ld hl, BillsPC_
+ jr bankswitchAndContinue
+
+TextScript_GameCornerPrizeMenu::
+; XXX find a better name for this function
+; special_F7
+ ld b, BANK(CeladonPrizeMenu)
+ ld hl, CeladonPrizeMenu
+bankswitchAndContinue::
+ call Bankswitch
+ jp HoldTextDisplayOpen ; continue to main text-engine function
+
+TextScript_PokemonCenterPC::
+ ld b, BANK(ActivatePC)
+ ld hl, ActivatePC
+ jr bankswitchAndContinue
+
+StartSimulatingJoypadStates::
+ xor a
+ ld [wOverrideSimulatedJoypadStatesMask], a
+ ld [wSpritePlayerStateData2MovementByte1], a
+ ld hl, wd730
+ set 7, [hl]
+ ret
+
+IsItemInBag::
+; 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
+ predef GetQuantityOfItemInBag
+ ld a, b
+ and a
+ ret
+
+DisplayPokedex::
+ ld [wd11e], a
+ farjp _DisplayPokedex
+
+SetSpriteFacingDirectionAndDelay::
+ call SetSpriteFacingDirection
+ ld c, 6
+ jp DelayFrames
+
+SetSpriteFacingDirection::
+ ld a, $9
+ ldh [hSpriteDataOffset], a
+ call GetPointerWithinSpriteStateData1
+ ldh a, [hSpriteFacingDirection]
+ ld [hl], a
+ ret
+
+SetSpriteImageIndexAfterSettingFacingDirection::
+ ld de, -7
+ add hl, de
+ ld [hl], a
+ ret
+
+; tests if the player's coordinates are in a specified array
+; INPUT:
+; hl = address of array
+; OUTPUT:
+; [wCoordIndex] = if there is match, the matching array index
+; sets carry if the coordinates are in the array, clears carry if not
+ArePlayerCoordsInArray::
+ ld a, [wYCoord]
+ ld b, a
+ ld a, [wXCoord]
+ ld c, a
+ ; fallthrough
+
+CheckCoords::
+ xor a
+ ld [wCoordIndex], a
+.loop
+ ld a, [hli]
+ cp $ff ; reached terminator?
+ jr z, .notInArray
+ push hl
+ ld hl, wCoordIndex
+ 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
+; [hSpriteIndex] = index of boulder sprite
+; OUTPUT:
+; [wCoordIndex] = if there is match, the matching array index
+; sets carry if the coordinates are in the array, clears carry if not
+CheckBoulderCoords::
+ push hl
+ ld hl, wSpritePlayerStateData2MapY
+ ldh a, [hSpriteIndex]
+ 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
+
+GetPointerWithinSpriteStateData1::
+ ld h, $c1
+ jr _GetPointerWithinSpriteStateData
+
+GetPointerWithinSpriteStateData2::
+ ld h, $c2
+
+_GetPointerWithinSpriteStateData:
+ ldh a, [hSpriteDataOffset]
+ ld b, a
+ ldh a, [hSpriteIndex]
+ 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::
+ xor a
+ ld [wRLEByteCount], a ; count written bytes here
+.listLoop
+ ld a, [de]
+ cp $ff
+ jr z, .endOfList
+ ldh [hRLEByteValue], 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
+ ldh a, [hRLEByteValue]
+ 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 [hSpriteIndex] to $FE and byte 2 to [hSpriteMovementByte2]
+SetSpriteMovementBytesToFE::
+ push hl
+ call GetSpriteMovementByte1Pointer
+ ld [hl], $fe
+ call GetSpriteMovementByte2Pointer
+ ldh a, [hSpriteMovementByte2]
+ ld [hl], a
+ pop hl
+ ret
+
+; sets both movement bytes for sprite [hSpriteIndex] to $FF
+SetSpriteMovementBytesToFF::
+ 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 [hSpriteIndex] in hl
+GetSpriteMovementByte1Pointer::
+ ld h, $C2
+ ldh a, [hSpriteIndex]
+ swap a
+ add 6
+ ld l, a
+ ret
+
+; returns the sprite movement byte 2 pointer for sprite [hSpriteIndex] in hl
+GetSpriteMovementByte2Pointer::
+ push de
+ ld hl, wMapSpriteData
+ ldh a, [hSpriteIndex]
+ dec a
+ add a
+ ld d, 0
+ ld e, a
+ add hl, de
+ pop de
+ ret
--- /dev/null
+++ b/home/math.asm
@@ -1,0 +1,41 @@
+; function to do multiplication
+; all values are big endian
+; INPUT
+; FF96-FF98 = multiplicand
+; FF99 = multiplier
+; OUTPUT
+; FF95-FF98 = product
+Multiply::
+ push hl
+ push bc
+ callfar _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::
+ push hl
+ push de
+ push bc
+ ldh a, [hLoadedROMBank]
+ push af
+ ld a, BANK(_Divide)
+ ldh [hLoadedROMBank], a
+ ld [MBC1RomBank], a
+ call _Divide
+ pop af
+ ldh [hLoadedROMBank], a
+ ld [MBC1RomBank], a
+ pop bc
+ pop de
+ pop hl
+ ret
--- /dev/null
+++ b/home/money.asm
@@ -1,0 +1,15 @@
+HasEnoughMoney::
+; Check if the player has at least as much
+; money as the 3-byte BCD value at hMoney.
+ ld de, wPlayerMoney
+ ld hl, hMoney
+ ld c, 3
+ jp StringCmp
+
+HasEnoughCoins::
+; Check if the player has at least as many
+; coins as the 2-byte BCD value at hCoins.
+ ld de, wPlayerCoins
+ ld hl, hCoins
+ ld c, 2
+ jp StringCmp
--- a/home/move_mon.asm
+++ b/home/move_mon.asm
@@ -1,3 +1,18 @@
+; Copies [hl, bc) to [de, de + bc - hl).
+; In other words, the source data is from hl up to but not including bc,
+; and the destination is de.
+CopyDataUntil::
+ 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.
; [wRemoveMonFromBox] == 0 specifies the party.
--- /dev/null
+++ b/home/npc_movement.asm
@@ -1,0 +1,54 @@
+; not zero if an NPC movement script is running, the player character is
+; automatically stepping down from a door, or joypad states are being simulated
+IsPlayerCharacterBeingControlledByGame::
+ ld a, [wNPCMovementScriptPointerTableNum]
+ and a
+ ret nz
+ ld a, [wd736]
+ bit 1, a ; currently stepping down from door bit
+ ret nz
+ ld a, [wd730]
+ and $80
+ ret
+
+RunNPCMovementScript::
+ ld hl, wd736
+ bit 0, [hl]
+ res 0, [hl]
+ jr nz, .playerStepOutFromDoor
+ ld a, [wNPCMovementScriptPointerTableNum]
+ and a
+ ret z
+ dec a
+ add a
+ ld d, 0
+ ld e, a
+ ld hl, .NPCMovementScriptPointerTables
+ add hl, de
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ldh a, [hLoadedROMBank]
+ push af
+ ld a, [wNPCMovementScriptBank]
+ ldh [hLoadedROMBank], a
+ ld [MBC1RomBank], a
+ ld a, [wNPCMovementScriptFunctionNum]
+ call CallFunctionInTable
+ pop af
+ ldh [hLoadedROMBank], a
+ ld [MBC1RomBank], a
+ ret
+
+.NPCMovementScriptPointerTables
+ dw PalletMovementScriptPointerTable
+ dw PewterMuseumGuyMovementScriptPointerTable
+ dw PewterGymGuyMovementScriptPointerTable
+.playerStepOutFromDoor
+ farjp PlayerStepOutFromDoor
+
+EndNPCMovementScript::
+ farjp _EndNPCMovementScript
+
+EmptyFunc2::
+ ret
--- /dev/null
+++ b/home/oam.asm
@@ -1,0 +1,36 @@
+; 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::
+ ld h, HIGH(wOAMBuffer)
+ 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
--- a/home/overworld.asm
+++ b/home/overworld.asm
@@ -2421,3 +2421,54 @@
ld hl, LoadPlayerSpriteGraphics
call Bankswitch
jp PlayDefaultMusic ; update map/player state?
+
+CheckForUserInterruption::
+; Return carry if Up+Select+B, Start or A are pressed in c frames.
+; Used only in the intro and title screen.
+ call DelayFrame
+
+ push bc
+ call JoypadLowSensitivity
+ pop bc
+
+ ldh a, [hJoyHeld]
+ cp D_UP + SELECT + B_BUTTON
+ jr z, .input
+
+ ldh a, [hJoy5]
+ and START | A_BUTTON
+ jr nz, .input
+
+ dec c
+ jr nz, CheckForUserInterruption
+
+ and a
+ ret
+
+.input
+ scf
+ ret
+
+; function to load position data for destination warp when switching maps
+; INPUT:
+; a = ID of destination warp within destination map
+LoadDestinationWarpPosition::
+ ld b, a
+ ldh a, [hLoadedROMBank]
+ push af
+ ld a, [wPredefParentBank]
+ ldh [hLoadedROMBank], a
+ ld [MBC1RomBank], a
+ ld a, b
+ add a
+ add a
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld bc, 4
+ ld de, wCurrentTileBlockMapViewPointer
+ call CopyData
+ pop af
+ ldh [hLoadedROMBank], a
+ ld [MBC1RomBank], a
+ ret
--- /dev/null
+++ b/home/palettes.asm
@@ -1,0 +1,57 @@
+RestoreScreenTilesAndReloadTilePatterns::
+ call ClearSprites
+ ld a, $1
+ ld [wUpdateSpritesEnabled], a
+ call ReloadMapSpriteTilePatterns
+ call LoadScreenTilesFromBuffer2
+ call LoadTextBoxTilePatterns
+ call RunDefaultPaletteCommand
+ 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
+ ldh [rBGP], a
+ ld a, %11010000 ; 3100
+ ldh [rOBP0], a
+ ret
+
+GBPalWhiteOut::
+; White out all palettes.
+ xor a
+ ldh [rBGP], a
+ ldh [rOBP0], a
+ ldh [rOBP1], a
+ ret
+
+RunDefaultPaletteCommand::
+ ld b, SET_PAL_DEFAULT
+RunPaletteCommand::
+ ld a, [wOnSGB]
+ and a
+ ret z
+ predef_jump _RunPaletteCommand
+
+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
--- /dev/null
+++ b/home/pathfinding.asm
@@ -1,0 +1,65 @@
+; calculates the difference |a-b|, setting carry flag if a<b
+CalcDifference::
+ sub b
+ ret nc
+ cpl
+ add $1
+ scf
+ ret
+
+MoveSprite::
+; move the sprite [hSpriteIndex] with the movement pointed to by de
+; actually only copies the movement data to wNPCMovementDirections for later
+ call SetSpriteMovementBytesToFF
+MoveSprite_::
+ push hl
+ push bc
+ call GetSpriteMovementByte1Pointer
+ xor a
+ ld [hl], a
+ ld hl, wNPCMovementDirections
+ ld c, 0
+
+.loop
+ ld a, [de]
+ ld [hli], a
+ inc de
+ inc c
+ cp $FF ; have we reached the end of the movement data?
+ jr nz, .loop
+
+ ld a, c
+ ld [wNPCNumScriptedSteps], a ; number of steps taken
+
+ pop bc
+ ld hl, wd730
+ set 0, [hl]
+ pop hl
+ xor a
+ ld [wOverrideSimulatedJoypadStatesMask], a
+ ld [wSimulatedJoypadStatesEnd], a
+ dec a
+ ld [wJoyIgnore], a
+ ld [wWastedByteCD3A], a
+ ret
+
+; divides [hDividend2] by [hDivisor2] and stores the quotient in [hQuotient2]
+DivideBytes::
+ push hl
+ ld hl, hQuotient2
+ xor a
+ ld [hld], a
+ ld a, [hld]
+ and a
+ jr z, .done
+ ld a, [hli]
+.loop
+ sub [hl]
+ jr c, .done
+ inc hl
+ inc [hl]
+ dec hl
+ jr .loop
+.done
+ pop hl
+ ret
--- /dev/null
+++ b/home/predef_text.asm
@@ -1,0 +1,28 @@
+PrintPredefTextID::
+ ldh [hSpriteIndexOrTextID], a
+ ld hl, TextPredefs
+ call SetMapTextPointer
+ ld hl, wTextPredefFlag
+ set 0, [hl]
+ call DisplayTextID
+
+RestoreMapTextPointer::
+ ld hl, wMapTextPtr
+ ldh a, [hSavedMapTextPtr]
+ ld [hli], a
+ ldh a, [hSavedMapTextPtr + 1]
+ ld [hl], a
+ ret
+
+SetMapTextPointer::
+ ld a, [wMapTextPtr]
+ ldh [hSavedMapTextPtr], a
+ ld a, [wMapTextPtr + 1]
+ ldh [hSavedMapTextPtr + 1], a
+ ld a, l
+ ld [wMapTextPtr], a
+ ld a, h
+ ld [wMapTextPtr + 1], a
+ ret
+
+INCLUDE "data/text_predef_pointers.asm"
--- /dev/null
+++ b/home/print_text.asm
@@ -1,0 +1,45 @@
+; 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 [wd730] or [wLetterPrintingDelayFlags] flags.
+PrintLetterDelay::
+ ld a, [wd730]
+ bit 6, a
+ ret nz
+ ld a, [wLetterPrintingDelayFlags]
+ bit 1, a
+ ret z
+ push hl
+ push de
+ push bc
+ ld a, [wLetterPrintingDelayFlags]
+ bit 0, a
+ jr z, .waitOneFrame
+ ld a, [wOptions]
+ and $f
+ ldh [hFrameCounter], a
+ jr .checkButtons
+.waitOneFrame
+ ld a, 1
+ ldh [hFrameCounter], a
+.checkButtons
+ call Joypad
+ ldh a, [hJoyHeld]
+.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
+ ldh a, [hFrameCounter]
+ and a
+ jr nz, .checkButtons
+.done
+ pop bc
+ pop de
+ pop hl
+ ret
--- /dev/null
+++ b/home/random.asm
@@ -1,0 +1,12 @@
+Random::
+; Return a random number in a.
+; For battles, use BattleRandom.
+ push hl
+ push de
+ push bc
+ farcall Random_
+ ldh a, [hRandomAdd]
+ pop bc
+ pop de
+ pop hl
+ ret
--- /dev/null
+++ b/home/reload_sprites.asm
@@ -1,0 +1,19 @@
+; Copy the current map's sprites' tile patterns to VRAM again after they have
+; been overwritten by other tile patterns.
+ReloadMapSpriteTilePatterns::
+ ld hl, wFontLoaded
+ ld a, [hl]
+ push af
+ res 0, [hl]
+ push hl
+ xor a
+ ld [wSpriteSetID], a
+ call DisableLCD
+ farcall InitMapSprites
+ call EnableLCD
+ pop hl
+ pop af
+ ld [hl], a
+ call LoadPlayerSpriteGraphics
+ call LoadFontTilePatterns
+ jp UpdateSprites
--- /dev/null
+++ b/home/reload_tiles.asm
@@ -1,0 +1,41 @@
+; reloads text box tile patterns, current map view, and tileset tile patterns
+ReloadMapData::
+ ldh a, [hLoadedROMBank]
+ push af
+ ld a, [wCurMap]
+ call SwitchToMapRomBank
+ call DisableLCD
+ call LoadTextBoxTilePatterns
+ call LoadCurrentMapView
+ call LoadTilesetTilePatternData
+ call EnableLCD
+ pop af
+ ldh [hLoadedROMBank], a
+ ld [MBC1RomBank], a
+ ret
+
+; reloads tileset tile patterns
+ReloadTilesetTilePatterns::
+ ldh a, [hLoadedROMBank]
+ push af
+ ld a, [wCurMap]
+ call SwitchToMapRomBank
+ call DisableLCD
+ call LoadTilesetTilePatternData
+ call EnableLCD
+ pop af
+ ldh [hLoadedROMBank], a
+ ld [MBC1RomBank], a
+ ret
+
+; shows the town map and lets the player choose a destination to fly to
+ChooseFlyDestination::
+ ld hl, wd72e
+ res 4, [hl]
+ farjp LoadTownMap_Fly
+
+; causes the text box to close without waiting for a button press after displaying text
+DisableWaitingAfterTextDisplay::
+ ld a, $01
+ ld [wDoNotWaitForButtonPressAfterDisplayingText], a
+ ret
--- /dev/null
+++ b/home/reset_player_sprite.asm
@@ -1,0 +1,20 @@
+ResetPlayerSpriteData::
+ ld hl, wSpriteStateData1
+ call ResetPlayerSpriteData_ClearSpriteData
+ ld hl, wSpriteStateData2
+ call ResetPlayerSpriteData_ClearSpriteData
+ ld a, $1
+ ld [wSpritePlayerStateData1PictureID], a
+ ld [wSpritePlayerStateData2ImageBaseOffset], a
+ ld hl, wSpritePlayerStateData1YPixels
+ 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::
+ ld bc, $10
+ xor a
+ jp FillMemory
--- /dev/null
+++ b/home/textbox.asm
@@ -1,0 +1,16 @@
+; function to draw various text boxes
+; INPUT:
+; [wTextBoxID] = text box ID
+; b, c = y, x cursor position (TWO_OPTION_MENU only)
+DisplayTextBoxID::
+ ldh a, [hLoadedROMBank]
+ push af
+ ld a, BANK(DisplayTextBoxID_)
+ ldh [hLoadedROMBank], a
+ ld [MBC1RomBank], a
+ call DisplayTextBoxID_
+ pop bc
+ ld a, b
+ ldh [hLoadedROMBank], a
+ ld [MBC1RomBank], a
+ ret
--- /dev/null
+++ b/home/tilemap.asm
@@ -1,0 +1,61 @@
+FillMemory::
+; Fill bc bytes at hl with a.
+ push de
+ ld d, a
+.loop
+ ld a, d
+ ld [hli], a
+ dec bc
+ ld a, b
+ or c
+ jr nz, .loop
+ pop de
+ ret
+
+UncompressSpriteFromDE::
+; Decompress pic at a:de.
+ ld hl, wSpriteInputPtr
+ ld [hl], e
+ inc hl
+ ld [hl], d
+ jp UncompressSpriteData
+
+SaveScreenTilesToBuffer2::
+ hlcoord 0, 0
+ ld de, wTileMapBackup2
+ ld bc, SCREEN_WIDTH * SCREEN_HEIGHT
+ call CopyData
+ ret
+
+LoadScreenTilesFromBuffer2::
+ call LoadScreenTilesFromBuffer2DisableBGTransfer
+ ld a, 1
+ ldh [hAutoBGTransferEnabled], a
+ ret
+
+; loads screen tiles stored in wTileMapBackup2 but leaves hAutoBGTransferEnabled disabled
+LoadScreenTilesFromBuffer2DisableBGTransfer::
+ xor a
+ ldh [hAutoBGTransferEnabled], a
+ ld hl, wTileMapBackup2
+ decoord 0, 0
+ ld bc, SCREEN_WIDTH * SCREEN_HEIGHT
+ call CopyData
+ ret
+
+SaveScreenTilesToBuffer1::
+ hlcoord 0, 0
+ ld de, wTileMapBackup
+ ld bc, SCREEN_WIDTH * SCREEN_HEIGHT
+ jp CopyData
+
+LoadScreenTilesFromBuffer1::
+ xor a
+ ldh [hAutoBGTransferEnabled], a
+ ld hl, wTileMapBackup
+ decoord 0, 0
+ ld bc, SCREEN_WIDTH * SCREEN_HEIGHT
+ call CopyData
+ ld a, 1
+ ldh [hAutoBGTransferEnabled], a
+ ret
--- /dev/null
+++ b/home/trainers2.asm
@@ -1,0 +1,35 @@
+GetTrainerInformation::
+ call GetTrainerName
+ ld a, [wLinkState]
+ and a
+ jr nz, .linkBattle
+ ld a, BANK(TrainerPicAndMoneyPointers)
+ call BankswitchHome
+ ld a, [wTrainerClass]
+ dec a
+ ld hl, TrainerPicAndMoneyPointers
+ ld bc, $5
+ call AddNTimes
+ ld de, wTrainerPicPointer
+ ld a, [hli]
+ ld [de], a
+ inc de
+ ld a, [hli]
+ ld [de], a
+ ld de, wTrainerBaseMoney
+ ld a, [hli]
+ ld [de], a
+ inc de
+ ld a, [hli]
+ ld [de], a
+ jp BankswitchBack
+.linkBattle
+ ld hl, wTrainerPicPointer
+ ld de, RedPicFront
+ ld [hl], e
+ inc hl
+ ld [hl], d
+ ret
+
+GetTrainerName::
+ farjp GetTrainerName_
--- /dev/null
+++ b/home/update_sprites.asm
@@ -1,0 +1,14 @@
+UpdateSprites::
+ ld a, [wUpdateSpritesEnabled]
+ dec a
+ ret nz
+ ldh a, [hLoadedROMBank]
+ push af
+ ld a, BANK(_UpdateSprites)
+ ldh [hLoadedROMBank], a
+ ld [MBC1RomBank], a
+ call _UpdateSprites
+ pop af
+ ldh [hLoadedROMBank], a
+ ld [MBC1RomBank], a
+ ret
--- /dev/null
+++ b/home/window.asm
@@ -1,0 +1,294 @@
+HandleMenuInput::
+ xor a
+ ld [wPartyMenuAnimMonEnabled], a
+
+HandleMenuInput_::
+ ldh a, [hDownArrowBlinkCount1]
+ push af
+ ldh a, [hDownArrowBlinkCount2]
+ push af ; save existing values on stack
+ xor a
+ ldh [hDownArrowBlinkCount1], a ; blinking down arrow timing value 1
+ ld a, 6
+ ldh [hDownArrowBlinkCount2], a ; blinking down arrow timing value 2
+.loop1
+ xor a
+ ld [wAnimCounter], a ; counter for pokemon shaking animation
+ call PlaceMenuCursor
+ call Delay3
+.loop2
+ push hl
+ ld a, [wPartyMenuAnimMonEnabled]
+ and a ; is it a pokemon selection menu?
+ jr z, .getJoypadState
+ farcall AnimatePartyMon ; shake mini sprite of selected pokemon
+.getJoypadState
+ pop hl
+ call JoypadLowSensitivity
+ ldh a, [hJoy5]
+ and a ; was a key pressed?
+ jr nz, .keyPressed
+ push hl
+ hlcoord 18, 11 ; coordinates of blinking down arrow in some menus
+ 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
+ ldh [hDownArrowBlinkCount2], a
+ pop af
+ ldh [hDownArrowBlinkCount1], a ; restore previous values
+ xor a
+ ld [wMenuWrappingEnabled], a ; disable menu wrapping
+ ret
+.keyPressed
+ xor a
+ ld [wCheckFor180DegreeTurn], a
+ ldh a, [hJoy5]
+ 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
+ ldh a, [hJoy5]
+ and A_BUTTON | B_BUTTON
+ jr z, .skipPlayingSound
+.AButtonOrBButtonPressed
+ push hl
+ ld hl, wFlags_0xcd60
+ bit 5, [hl]
+ pop hl
+ jr nz, .skipPlayingSound
+ ld a, SFX_PRESS_AB
+ call PlaySound
+.skipPlayingSound
+ pop af
+ ldh [hDownArrowBlinkCount2], a
+ pop af
+ ldh [hDownArrowBlinkCount1], a ; restore previous values
+ xor a
+ ld [wMenuWrappingEnabled], a ; disable menu wrapping
+ ldh a, [hJoy5]
+ ret
+.noWrappingAround
+ ld a, [wMenuWatchMovingOutOfBounds]
+ and a ; should we return if the user tried to go past the top or bottom?
+ jr z, .checkOtherKeys
+ jr .checkIfAButtonOrBButtonPressed
+
+PlaceMenuCursor::
+ ld a, [wTopMenuItemY]
+ and a ; is the y coordinate 0?
+ jr z, .adjustForXCoord
+ hlcoord 0, 0
+ ld bc, SCREEN_WIDTH
+.topMenuItemLoop
+ add hl, bc
+ dec a
+ jr nz, .topMenuItemLoop
+.adjustForXCoord
+ ld a, [wTopMenuItemX]
+ ld b, 0
+ ld c, a
+ add hl, bc
+ push hl
+ ld a, [wLastMenuItem]
+ and a ; was the previous menu id 0?
+ jr z, .checkForArrow1
+ push af
+ ldh a, [hFlagsFFF6]
+ 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 "▶" ; 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
+ ldh a, [hFlagsFFF6]
+ 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 "▶" ; 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::
+ 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::
+ 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 hDownArrowBlinkCount1 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
+; initialized 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::
+ ld a, [hl]
+ ld b, a
+ ld a, "▼"
+ cp b
+ jr nz, .downArrowOff
+.downArrowOn
+ ldh a, [hDownArrowBlinkCount1]
+ dec a
+ ldh [hDownArrowBlinkCount1], a
+ ret nz
+ ldh a, [hDownArrowBlinkCount2]
+ dec a
+ ldh [hDownArrowBlinkCount2], a
+ ret nz
+ ld a, " "
+ ld [hl], a
+ ld a, $ff
+ ldh [hDownArrowBlinkCount1], a
+ ld a, $06
+ ldh [hDownArrowBlinkCount2], a
+ ret
+.downArrowOff
+ ldh a, [hDownArrowBlinkCount1]
+ and a
+ ret z
+ dec a
+ ldh [hDownArrowBlinkCount1], a
+ ret nz
+ dec a
+ ldh [hDownArrowBlinkCount1], a
+ ldh a, [hDownArrowBlinkCount2]
+ dec a
+ ldh [hDownArrowBlinkCount2], a
+ ret nz
+ ld a, $06
+ ldh [hDownArrowBlinkCount2], a
+ ld a, "▼"
+ 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 [wEnteringCableClub] is set).
+
+EnableAutoTextBoxDrawing::
+ xor a
+ jr AutoTextBoxDrawingCommon
+
+DisableAutoTextBoxDrawing::
+ ld a, $01
+
+AutoTextBoxDrawingCommon::
+ ld [wAutoTextBoxDrawingControl], a
+ xor a
+ ld [wDoNotWaitForButtonPressAfterDisplayingText], a ; make DisplayTextID wait for button press
+ ret
+
+PrintText::
+; Print text hl at (1, 14).
+ push hl
+ ld a, MESSAGE_BOX
+ ld [wTextBoxID], a
+ call DisplayTextBoxID
+ call UpdateSprites
+ call Delay3
+ pop hl
+PrintText_NoCreatingTextBox::
+ bccoord 1, 14
+ jp TextCommandProcessor