diff options
Diffstat (limited to 'home')
34 files changed, 1717 insertions, 0 deletions
diff --git a/home/array.asm b/home/array.asm new file mode 100644 index 00000000..256c58e1 --- /dev/null +++ b/home/array.asm @@ -0,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 diff --git a/home/array2.asm b/home/array2.asm new file mode 100644 index 00000000..f4ed8389 --- /dev/null +++ b/home/array2.asm @@ -0,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 diff --git a/home/compare.asm b/home/compare.asm new file mode 100644 index 00000000..e2da7857 --- /dev/null +++ b/home/compare.asm @@ -0,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 diff --git a/home/copy_string.asm b/home/copy_string.asm new file mode 100644 index 00000000..7f86e501 --- /dev/null +++ b/home/copy_string.asm @@ -0,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 diff --git a/home/count_set_bits.asm b/home/count_set_bits.asm new file mode 100644 index 00000000..73f7fa9c --- /dev/null +++ b/home/count_set_bits.asm @@ -0,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 diff --git a/home/delay.asm b/home/delay.asm new file mode 100644 index 00000000..850b23e9 --- /dev/null +++ b/home/delay.asm @@ -0,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 diff --git a/home/fade_audio.asm b/home/fade_audio.asm new file mode 100644 index 00000000..4ad2607d --- /dev/null +++ b/home/fade_audio.asm @@ -0,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 diff --git a/home/give.asm b/home/give.asm new file mode 100644 index 00000000..cbfd0310 --- /dev/null +++ b/home/give.asm @@ -0,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 diff --git a/home/hidden_objects.asm b/home/hidden_objects.asm new file mode 100644 index 00000000..eccf1c8f --- /dev/null +++ b/home/hidden_objects.asm @@ -0,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 diff --git a/home/inventory.asm b/home/inventory.asm new file mode 100644 index 00000000..ebaa4047 --- /dev/null +++ b/home/inventory.asm @@ -0,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 diff --git a/home/item.asm b/home/item.asm new file mode 100644 index 00000000..f27d46f4 --- /dev/null +++ b/home/item.asm @@ -0,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 diff --git a/home/item_price.asm b/home/item_price.asm new file mode 100644 index 00000000..98eb31b1 --- /dev/null +++ b/home/item_price.asm @@ -0,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 diff --git a/home/joypad2.asm b/home/joypad2.asm new file mode 100644 index 00000000..139dd3f5 --- /dev/null +++ b/home/joypad2.asm @@ -0,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 diff --git a/home/load_font.asm b/home/load_font.asm new file mode 100644 index 00000000..3b56d3a8 --- /dev/null +++ b/home/load_font.asm @@ -0,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 diff --git a/home/map_objects.asm b/home/map_objects.asm new file mode 100644 index 00000000..02555e35 --- /dev/null +++ b/home/map_objects.asm @@ -0,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 diff --git a/home/math.asm b/home/math.asm new file mode 100644 index 00000000..b081b540 --- /dev/null +++ b/home/math.asm @@ -0,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 diff --git a/home/money.asm b/home/money.asm new file mode 100644 index 00000000..a62835ea --- /dev/null +++ b/home/money.asm @@ -0,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 diff --git a/home/move_mon.asm b/home/move_mon.asm index cf7213b1..3e69e6d0 100644 --- 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. diff --git a/home/npc_movement.asm b/home/npc_movement.asm new file mode 100644 index 00000000..4914d3df --- /dev/null +++ b/home/npc_movement.asm @@ -0,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 diff --git a/home/oam.asm b/home/oam.asm new file mode 100644 index 00000000..8a940d97 --- /dev/null +++ b/home/oam.asm @@ -0,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 diff --git a/home/overworld.asm b/home/overworld.asm index acc108c7..0408bf32 100644 --- a/home/overworld.asm +++ b/home/overworld.asm @@ -2421,3 +2421,54 @@ ForceBikeOrSurf:: 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 diff --git a/home/palettes.asm b/home/palettes.asm new file mode 100644 index 00000000..08dcdc85 --- /dev/null +++ b/home/palettes.asm @@ -0,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 diff --git a/home/pathfinding.asm b/home/pathfinding.asm new file mode 100644 index 00000000..aca5a763 --- /dev/null +++ b/home/pathfinding.asm @@ -0,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 diff --git a/home/predef_text.asm b/home/predef_text.asm new file mode 100644 index 00000000..b494a2c8 --- /dev/null +++ b/home/predef_text.asm @@ -0,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" diff --git a/home/print_text.asm b/home/print_text.asm new file mode 100644 index 00000000..049ddfb4 --- /dev/null +++ b/home/print_text.asm @@ -0,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 diff --git a/home/random.asm b/home/random.asm new file mode 100644 index 00000000..33a24425 --- /dev/null +++ b/home/random.asm @@ -0,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 diff --git a/home/reload_sprites.asm b/home/reload_sprites.asm new file mode 100644 index 00000000..8a08d64d --- /dev/null +++ b/home/reload_sprites.asm @@ -0,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 diff --git a/home/reload_tiles.asm b/home/reload_tiles.asm new file mode 100644 index 00000000..6228395c --- /dev/null +++ b/home/reload_tiles.asm @@ -0,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 diff --git a/home/reset_player_sprite.asm b/home/reset_player_sprite.asm new file mode 100644 index 00000000..72df31a1 --- /dev/null +++ b/home/reset_player_sprite.asm @@ -0,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 diff --git a/home/textbox.asm b/home/textbox.asm new file mode 100644 index 00000000..5067b044 --- /dev/null +++ b/home/textbox.asm @@ -0,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 diff --git a/home/tilemap.asm b/home/tilemap.asm new file mode 100644 index 00000000..afee7097 --- /dev/null +++ b/home/tilemap.asm @@ -0,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 diff --git a/home/trainers2.asm b/home/trainers2.asm new file mode 100644 index 00000000..950eabb2 --- /dev/null +++ b/home/trainers2.asm @@ -0,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_ diff --git a/home/update_sprites.asm b/home/update_sprites.asm new file mode 100644 index 00000000..80ea14d3 --- /dev/null +++ b/home/update_sprites.asm @@ -0,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 diff --git a/home/window.asm b/home/window.asm new file mode 100644 index 00000000..e83f9585 --- /dev/null +++ b/home/window.asm @@ -0,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 |