ref: 6866df277f7f7685ae087c9e54d537873a941575
dir: /home.asm/
; The rst vectors are unused. SECTION "rst 00", ROM0 [$00] rst $38 SECTION "rst 08", ROM0 [$08] rst $38 SECTION "rst 10", ROM0 [$10] rst $38 SECTION "rst 18", ROM0 [$18] rst $38 SECTION "rst 20", ROM0 [$20] rst $38 SECTION "rst 28", ROM0 [$28] rst $38 SECTION "rst 30", ROM0 [$30] rst $38 SECTION "rst 38", ROM0 [$38] rst $38 ; Hardware interrupts SECTION "vblank", ROM0 [$40] jp VBlank SECTION "hblank", ROM0 [$48] rst $38 SECTION "timer", ROM0 [$50] jp Timer SECTION "serial", ROM0 [$58] jp Serial SECTION "joypad", ROM0 [$60] reti SECTION "Home", ROM0 DisableLCD:: xor a ld [rIF], a ld a, [rIE] ld b, a res 0, a ld [rIE], a .wait ld a, [rLY] cp LY_VBLANK jr nz, .wait ld a, [rLCDC] and $ff ^ rLCDC_ENABLE_MASK ld [rLCDC], a ld a, b ld [rIE], a ret EnableLCD:: ld a, [rLCDC] set rLCDC_ENABLE, a ld [rLCDC], a ret ClearSprites:: xor a ld hl, wOAMBuffer ld b, 40 * 4 .loop ld [hli], a dec b jr nz, .loop ret HideSprites:: ld a, 160 ld hl, wOAMBuffer ld de, 4 ld b, 40 .loop ld [hl], a add hl, de dec b jr nz, .loop ret INCLUDE "home/copy.asm" SECTION "Entry", ROM0 [$100] nop jp Start SECTION "Header", ROM0 [$104] ; The header is generated by rgbfix. ; The space here is allocated to prevent code from being overwritten. ds $150 - $104 SECTION "Main", ROM0 Start:: cp GBC jr z, .gbc xor a jr .ok .gbc ld a, 0 .ok ld [wGBC], a jp Init INCLUDE "home/joypad.asm" INCLUDE "data/map_header_pointers.asm" INCLUDE "home/overworld.asm" CheckForUserInterruption:: ; 12f8 (0:12f8) ; 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 ld a, [hJoyHeld] cp D_UP + SELECT + B_BUTTON jr z, .input ld 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:: ; 1313 (0:1313) ld b,a ld a,[H_LOADEDROMBANK] push af ld a,[wPredefParentBank] ld [H_LOADEDROMBANK],a ld [$2000],a ld a,b add a add a ld c,a ld b,0 add hl,bc ld bc,4 ld de,wCurrentTileBlockMapViewPointer call CopyData pop af ld [H_LOADEDROMBANK],a ld [$2000],a ret DrawHPBar:: ; 1336 (0:1336) ; Draw an HP bar d tiles long, and fill it to e pixels. ; If c is nonzero, show at least a sliver regardless. ; The right end of the bar changes with [wListMenuID]. push hl push de push bc ; Left ld a, $71 ; "HP:" ld [hli], a ld a, $62 ld [hli], a push hl ; Middle ld a, $63 ; empty .draw ld [hli],a dec d jr nz, .draw ; Right ld a,[wListMenuID] dec a ld a, $6d ; status screen and battle jr z, .ok dec a ; pokemon menu .ok ld [hl],a pop hl ld a, e and a jr nz, .fill ; If c iz nonzero, draw a pixel anyway. ld a, c and a jr z, .done ld e, 1 .fill ld a, e sub 8 jr c, .partial ld e, a ld a, $6b ; full ld [hli], a ld a, e and a jr z, .done jr .fill .partial ; Fill remaining pixels at the end if necessary. ld a, $63 ; empty add e ld [hl], a .done pop bc pop de pop hl ret ; loads pokemon data from one of multiple sources to wcf98 ; loads base stats to W_MONHDEXNUM ; INPUT: ; [wWhichPokemon] = index of pokemon within party/box ; [wcc49] = source ; 00: player's party ; 01: enemy's party ; 02: current box ; 03: daycare ; OUTPUT: ; [wcf91] = pokemon ID ; wcf98 = base address of pokemon data ; W_MONHDEXNUM = base address of base stats LoadMonData:: ; 1372 (0:1372) ld hl, LoadMonData_ ld b, BANK(LoadMonData_) jp Bankswitch Func_137a:: ; 137a (0:137a) ; Write c to [wd0dc + b]. Unused. ld hl, wd0dc ld e, b ld d, 0 add hl, de ld a, c ld [hl], a ret LoadFlippedFrontSpriteByMonIndex:: ; 1384 (0:1384) ld a, 1 ld [W_SPRITEFLIPPED], a LoadFrontSpriteByMonIndex:: ; 1389 (0:1389) push hl ld a, [wd11e] push af ld a, [wcf91] ld [wd11e], a predef IndexToPokedex ld hl, wd11e ld a, [hl] pop bc ld [hl], b and a pop hl jr z, .invalidDexNumber ; dex #0 invalid cp NUM_POKEMON + 1 jr c, .validDexNumber ; dex >#151 invalid .invalidDexNumber ld a, RHYDON ; $1 ld [wcf91], a ret .validDexNumber push hl ld de, vFrontPic call LoadMonFrontSprite pop hl ld a, [H_LOADEDROMBANK] push af ld a, Bank(asm_3f0d0) ld [H_LOADEDROMBANK], a ld [$2000], a xor a ld [$ffe1], a call asm_3f0d0 xor a ld [W_SPRITEFLIPPED], a pop af ld [H_LOADEDROMBANK], a ld [$2000], a ret PlayCry:: ; 13d0 (0:13d0) ; Play monster a's cry. call GetCryData call PlaySound jp WaitForSoundToFinish GetCryData:: ; 13d9 (0:13d9) ; Load cry data for monster a. dec a ld c, a ld b, 0 ld hl, CryData add hl, bc add hl, bc add hl, bc ld a, Bank(CryData) call BankswitchHome ld a, [hli] ld b, a ; cry id ld a, [hli] ld [wc0f1], a ld a, [hl] ld [wc0f2], a call BankswitchBack ; Cry headers have 3 channels, ; and start from index $14, ; so add 3 times the cry id. ld a, b ld c, $14 rlca ; * 2 add b add c ret DisplayPartyMenu:: ; 13fc (0:13fc) ld a,[hTilesetType] push af xor a ld [hTilesetType],a call GBPalWhiteOutWithDelay3 call ClearSprites call PartyMenuInit call DrawPartyMenu jp HandlePartyMenuInput GoBackToPartyMenu:: ; 1411 (0:1411) ld a,[hTilesetType] push af xor a ld [hTilesetType],a call PartyMenuInit call RedrawPartyMenu jp HandlePartyMenuInput PartyMenuInit:: ; 1420 (0:1420) ld a, 1 ; hardcoded bank call BankswitchHome call LoadHpBarAndStatusTilePatterns ld hl, wd730 set 6, [hl] ; turn off letter printing delay xor a ld [wcc49], a ld [wcc37], a ld hl, wTopMenuItemY inc a ld [hli], a ; top menu item Y xor a ld [hli], a ; top menu item X ld a, [wcc2b] push af ld [hli], a ; current menu item ID inc hl ld a, [wPartyCount] and a ; are there more than 0 pokemon in the party? jr z, .storeMaxMenuItemID dec a ; if party is not empty, the max menu item ID is ([wPartyCount] - 1) ; otherwise, it is 0 .storeMaxMenuItemID ld [hli], a ; max menu item ID ld a, [wd11f] and a ld a, A_BUTTON + B_BUTTON jr z, .next xor a ld [wd11f], a inc a .next ld [hli], a ; menu watched keys pop af ld [hl], a ; old menu item ID ret HandlePartyMenuInput:: ; 145a (0:145a) ld a,1 ld [wMenuWrappingEnabled],a ld a,$40 ld [wd09b],a call HandleMenuInputPokemonSelection call PlaceUnfilledArrowMenuCursor ld b,a xor a ld [wd09b],a ld a,[wCurrentMenuItem] ld [wcc2b],a ld hl,wd730 res 6,[hl] ; turn on letter printing delay ld a,[wMenuItemToSwap] and a jp nz,.swappingPokemon pop af ld [hTilesetType],a bit 1,b jr nz,.noPokemonChosen ld a,[wPartyCount] and a jr z,.noPokemonChosen ld a,[wCurrentMenuItem] ld [wWhichPokemon],a ld hl,wPartySpecies ld b,0 ld c,a add hl,bc ld a,[hl] ld [wcf91],a ld [wBattleMonSpecies2],a call BankswitchBack and a ret .noPokemonChosen call BankswitchBack scf ret .swappingPokemon bit 1,b ; was the B button pressed? jr z,.handleSwap ; if not, handle swapping the pokemon .cancelSwap ; if the B button was pressed callba ErasePartyMenuCursors xor a ld [wMenuItemToSwap],a ld [wd07d],a call RedrawPartyMenu jr HandlePartyMenuInput .handleSwap ld a,[wCurrentMenuItem] ld [wWhichPokemon],a callba SwitchPartyMon jr HandlePartyMenuInput DrawPartyMenu:: ; 14d4 (0:14d4) ld hl, DrawPartyMenu_ jr DrawPartyMenuCommon RedrawPartyMenu:: ; 14d9 (0:14d9) ld hl, RedrawPartyMenu_ DrawPartyMenuCommon:: ; 14dc (0:14dc) ld b, BANK(RedrawPartyMenu_) jp Bankswitch ; prints a pokemon's status condition ; INPUT: ; de = address of status condition ; hl = destination address PrintStatusCondition:: ; 14e1 (0:14e1) push de dec de dec de ; de = address of current HP ld a,[de] ld b,a dec de ld a,[de] or b ; is the pokemon's HP zero? pop de jr nz,PrintStatusConditionNotFainted ; if the pokemon's HP is 0, print "FNT" ld a,"F" ld [hli],a ld a,"N" ld [hli],a ld [hl],"T" and a ret PrintStatusConditionNotFainted ; 14f6 ld a,[H_LOADEDROMBANK] push af ld a,BANK(PrintStatusAilment) ld [H_LOADEDROMBANK],a ld [$2000],a call PrintStatusAilment ; print status condition pop bc ld a,b ld [H_LOADEDROMBANK],a ld [$2000],a ret ; function to print pokemon level, leaving off the ":L" if the level is at least 100 ; INPUT: ; hl = destination address ; [wcfb9] = level PrintLevel:: ; 150b (0:150b) ld a,$6e ; ":L" tile ID ld [hli],a ld c,2 ; number of digits ld a,[wcfb9] ; level cp a,100 jr c,PrintLevelCommon ; if level at least 100, write over the ":L" tile dec hl inc c ; increment number of digits to 3 jr PrintLevelCommon ; prints the level without leaving off ":L" regardless of level ; INPUT: ; hl = destination address ; [wcfb9] = level PrintLevelFull:: ; 151b (0:151b) ld a,$6e ; ":L" tile ID ld [hli],a ld c,3 ; number of digits ld a,[wcfb9] ; level PrintLevelCommon:: ; 1523 (0:1523) ld [wd11e],a ld de,wd11e ld b,$41 ; no leading zeroes, left-aligned, one byte jp PrintNumber Func_152e:: ; 152e (0:152e) ; Unused. ld hl,wd0dc ld c,a ld b,0 add hl,bc ld a,[hl] ret ; copies the base stat data of a pokemon to W_MONHDEXNUM (W_MONHEADER) ; INPUT: ; [wd0b5] = pokemon ID GetMonHeader:: ; 1537 (0:1537) ld a,[H_LOADEDROMBANK] push af ld a,BANK(BaseStats) ld [H_LOADEDROMBANK],a ld [$2000],a push bc push de push hl ld a,[wd11e] push af ld a,[wd0b5] ld [wd11e],a ld de,FossilKabutopsPic ld b,$66 ; size of Kabutops fossil and Ghost sprites cp a,FOSSIL_KABUTOPS ; Kabutops fossil jr z,.specialID ld de,GhostPic cp a,MON_GHOST ; Ghost jr z,.specialID ld de,FossilAerodactylPic ld b,$77 ; size of Aerodactyl fossil sprite cp a,FOSSIL_AERODACTYL ; Aerodactyl fossil jr z,.specialID cp a,MEW jr z,.mew predef IndexToPokedex ; convert pokemon ID in [wd11e] to pokedex number ld a,[wd11e] dec a ld bc,28 ld hl,BaseStats call AddNTimes ld de,W_MONHEADER ld bc,28 call CopyData jr .done .specialID ld hl,W_MONHSPRITEDIM ld [hl],b ; write sprite dimensions inc hl ld [hl],e ; write front sprite pointer inc hl ld [hl],d jr .done .mew ld hl,MewBaseStats ld de,W_MONHEADER ld bc,28 ld a,BANK(MewBaseStats) call FarCopyData .done ld a,[wd0b5] ld [W_MONHDEXNUM],a pop af ld [wd11e],a pop hl pop de pop bc pop af ld [H_LOADEDROMBANK],a ld [$2000],a ret ; copy party pokemon's name to wcd6d GetPartyMonName2:: ; 15b4 (0:15b4) ld a,[wWhichPokemon] ; index within party ld hl,wPartyMonNicks ; this is called more often GetPartyMonName:: ; 15ba (0:15ba) push hl push bc call SkipFixedLengthTextEntries ; add 11 to hl, a times ld de,wcd6d push de ld bc,11 call CopyData pop de pop bc pop hl ret ; function to print a BCD (Binary-coded decimal) number ; de = address of BCD number ; hl = destination address ; c = flags and length ; bit 7: if set, do not print leading zeroes ; if unset, print leading zeroes ; bit 6: if set, left-align the string (do not pad empty digits with spaces) ; if unset, right-align the string ; bit 5: if set, print currency symbol at the beginning of the string ; if unset, do not print the currency symbol ; bits 0-4: length of BCD number in bytes ; Note that bits 5 and 7 are modified during execution. The above reflects ; their meaning at the beginning of the functions's execution. PrintBCDNumber:: ; 15cd (0:15cd) ld b,c ; save flags in b res 7,c res 6,c res 5,c ; c now holds the length bit 5,b jr z,.loop bit 7,b jr nz,.loop ld [hl],"¥" inc hl .loop ld a,[de] swap a call PrintBCDDigit ; print upper digit ld a,[de] call PrintBCDDigit ; print lower digit inc de dec c jr nz,.loop bit 7,b ; were any non-zero digits printed? jr z,.done ; if so, we are done .numberEqualsZero ; if every digit of the BCD number is zero bit 6,b ; left or right alignment? jr nz,.skipRightAlignmentAdjustment dec hl ; if the string is right-aligned, it needs to be moved back one space .skipRightAlignmentAdjustment bit 5,b jr z,.skipCurrencySymbol ld [hl],"¥" inc hl .skipCurrencySymbol ld [hl],"0" call PrintLetterDelay inc hl .done ret PrintBCDDigit:: ; 1604 (0:1604) and $f and a jr z,.zeroDigit .nonzeroDigit bit 7,b ; have any non-space characters been printed? jr z,.outputDigit ; if bit 7 is set, then no numbers have been printed yet bit 5,b ; print the currency symbol? jr z,.skipCurrencySymbol ld [hl],"¥" inc hl res 5,b .skipCurrencySymbol res 7,b ; unset 7 to indicate that a nonzero digit has been reached .outputDigit add a,"0" ld [hli],a jp PrintLetterDelay .zeroDigit bit 7,b ; either printing leading zeroes or already reached a nonzero digit? jr z,.outputDigit ; if so, print a zero digit bit 6,b ; left or right alignment? ret nz inc hl ; if right-aligned, "print" a space by advancing the pointer ret ; uncompresses the front or back sprite of the specified mon ; assumes the corresponding mon header is already loaded ; hl contains offset to sprite pointer ($b for front or $d for back) UncompressMonSprite:: ; 1627 (0:1627) ld bc,W_MONHEADER add hl,bc ld a,[hli] ld [W_SPRITEINPUTPTR],a ; fetch sprite input pointer ld a,[hl] ld [W_SPRITEINPUTPTR+1],a ; define (by index number) the bank that a pokemon's image is in ; index = Mew, bank 1 ; index = Kabutops fossil, bank $B ; index < $1F, bank 9 ; $1F ≤ index < $4A, bank $A ; $4A ≤ index < $74, bank $B ; $74 ≤ index < $99, bank $C ; $99 ≤ index, bank $D ld a,[wcf91] ; XXX name for this ram location ld b,a cp MEW ld a,BANK(MewPicFront) jr z,.GotBank ld a,b cp FOSSIL_KABUTOPS ld a,BANK(FossilKabutopsPic) jr z,.GotBank ld a,b cp TANGELA + 1 ld a,BANK(TangelaPicFront) jr c,.GotBank ld a,b cp MOLTRES + 1 ld a,BANK(MoltresPicFront) jr c,.GotBank ld a,b cp BEEDRILL + 2 ld a,BANK(BeedrillPicFront) jr c,.GotBank ld a,b cp STARMIE + 1 ld a,BANK(StarmiePicFront) jr c,.GotBank ld a,BANK(VictreebelPicFront) .GotBank jp UncompressSpriteData ; de: destination location LoadMonFrontSprite:: ; 1665 (0:1665) push de ld hl, W_MONHFRONTSPRITE - W_MONHEADER call UncompressMonSprite ld hl, W_MONHSPRITEDIM ld a, [hli] ld c, a pop de ; fall through ; postprocesses uncompressed sprite chunks to a 2bpp sprite and loads it into video ram ; calculates alignment parameters to place both sprite chunks in the center of the 7*7 tile sprite buffers ; de: destination location ; a,c: sprite dimensions (in tiles of 8x8 each) LoadUncompressedSpriteData:: ; 1672 (0:1672) push de and $f ld [H_SPRITEWIDTH], a ; each byte contains 8 pixels (in 1bpp), so tiles=bytes for width ld b, a ld a, $7 sub b ; 7-w inc a ; 8-w srl a ; (8-w)/2 ; horizontal center (in tiles, rounded up) ld b, a add a add a add a sub b ; 7*((8-w)/2) ; skip for horizontal center (in tiles) ld [H_SPRITEOFFSET], a ld a, c swap a and $f ld b, a add a add a add a ; 8*tiles is height in bytes ld [H_SPRITEHEIGHT], a ; $ff8c ld a, $7 sub b ; 7-h ; skip for vertical center (in tiles, relative to current column) ld b, a ld a, [H_SPRITEOFFSET] add b ; 7*((8-w)/2) + 7-h ; combined overall offset (in tiles) add a add a add a ; 8*(7*((8-w)/2) + 7-h) ; combined overall offset (in bytes) ld [H_SPRITEOFFSET], a xor a ld [$4000], a ld hl, S_SPRITEBUFFER0 call ZeroSpriteBuffer ; zero buffer 0 ld de, S_SPRITEBUFFER1 ld hl, S_SPRITEBUFFER0 call AlignSpriteDataCentered ; copy and align buffer 1 to 0 (containing the MSB of the 2bpp sprite) ld hl, S_SPRITEBUFFER1 call ZeroSpriteBuffer ; zero buffer 1 ld de, S_SPRITEBUFFER2 ld hl, S_SPRITEBUFFER1 call AlignSpriteDataCentered ; copy and align buffer 2 to 1 (containing the LSB of the 2bpp sprite) pop de jp InterlaceMergeSpriteBuffers ; copies and aligns the sprite data properly inside the sprite buffer ; sprite buffers are 7*7 tiles in size, the loaded sprite is centered within this area AlignSpriteDataCentered:: ; 16c2 (0:16c2) ld a, [H_SPRITEOFFSET] ld b, $0 ld c, a add hl, bc ld a, [H_SPRITEWIDTH] ; $ff8b .columnLoop push af push hl ld a, [H_SPRITEHEIGHT] ; $ff8c ld c, a .columnInnerLoop ld a, [de] inc de ld [hli], a dec c jr nz, .columnInnerLoop pop hl ld bc, 7*8 ; 7 tiles add hl, bc ; advance one full column pop af dec a jr nz, .columnLoop ret ; fills the sprite buffer (pointed to in hl) with zeros ZeroSpriteBuffer:: ; 16df (0:16df) ld bc, SPRITEBUFFERSIZE .nextByteLoop xor a ld [hli], a dec bc ld a, b or c jr nz, .nextByteLoop ret ; combines the (7*7 tiles, 1bpp) sprite chunks in buffer 0 and 1 into a 2bpp sprite located in buffer 1 through 2 ; in the resulting sprite, the rows of the two source sprites are interlaced ; de: output address InterlaceMergeSpriteBuffers:: ; 16ea (0:16ea) xor a ld [$4000], a push de ld hl, S_SPRITEBUFFER2 + (SPRITEBUFFERSIZE - 1) ; destination: end of buffer 2 ld de, S_SPRITEBUFFER1 + (SPRITEBUFFERSIZE - 1) ; source 2: end of buffer 1 ld bc, S_SPRITEBUFFER0 + (SPRITEBUFFERSIZE - 1) ; source 1: end of buffer 0 ld a, SPRITEBUFFERSIZE/2 ; $c4 ld [H_SPRITEINTERLACECOUNTER], a ; $ff8b .interlaceLoop ld a, [de] dec de ld [hld], a ; write byte of source 2 ld a, [bc] dec bc ld [hld], a ; write byte of source 1 ld a, [de] dec de ld [hld], a ; write byte of source 2 ld a, [bc] dec bc ld [hld], a ; write byte of source 1 ld a, [H_SPRITEINTERLACECOUNTER] ; $ff8b dec a ld [H_SPRITEINTERLACECOUNTER], a ; $ff8b jr nz, .interlaceLoop ld a, [W_SPRITEFLIPPED] and a jr z, .notFlipped ld bc, 2*SPRITEBUFFERSIZE ld hl, S_SPRITEBUFFER1 .swapLoop swap [hl] ; if flipped swap nybbles in all bytes inc hl dec bc ld a, b or c jr nz, .swapLoop .notFlipped pop hl ld de, S_SPRITEBUFFER1 ld c, (2*SPRITEBUFFERSIZE)/16 ; $31, number of 16 byte chunks to be copied ld a, [H_LOADEDROMBANK] ld b, a jp CopyVideoData INCLUDE "data/collision.asm" INCLUDE "home/copy2.asm" INCLUDE "home/text.asm" INCLUDE "home/vcopy.asm" INCLUDE "home/init.asm" INCLUDE "home/vblank.asm" INCLUDE "home/fade.asm" INCLUDE "home/serial.asm" INCLUDE "home/timer.asm" INCLUDE "home/audio.asm" UpdateSprites:: ; 2429 (0:2429) ld a, [wUpdateSpritesEnabled] dec a ret nz ld a, [H_LOADEDROMBANK] push af ld a, Bank(_UpdateSprites) ld [H_LOADEDROMBANK], a ld [$2000], a call _UpdateSprites pop af ld [H_LOADEDROMBANK], a ld [$2000], a ret INCLUDE "data/mart_inventories.asm" TextScriptEndingChar:: ; 24d6 (0:24d6) db "@" TextScriptEnd:: ; 24d7 (0:24d7) ld hl,TextScriptEndingChar ret ExclamationText:: ; 24db (0:24db) TX_FAR _ExclamationText db "@" GroundRoseText:: ; 24e0 (0:24e0) TX_FAR _GroundRoseText db "@" BoulderText:: ; 24e5 (0:24e5) TX_FAR _BoulderText db "@" MartSignText:: ; 24ea (0:24ea) TX_FAR _MartSignText db "@" PokeCenterSignText:: ; 24ef (0:24ef) TX_FAR _PokeCenterSignText db "@" Predef5CText:: ; 24f4 (0:24f4) ; XXX better label (what does predef $5C do?) db $08 ; asm predef PickupItem jp TextScriptEnd INCLUDE "home/pic.asm" ResetPlayerSpriteData:: ; 28a6 (0:28a6) ld hl, wSpriteStateData1 call ResetPlayerSpriteData_ClearSpriteData ld hl, wSpriteStateData2 call ResetPlayerSpriteData_ClearSpriteData ld a, $1 ld [wSpriteStateData1], a ld [wSpriteStateData2 + $0e], a ld hl, wSpriteStateData1 + 4 ld [hl], $3c ; set Y screen pos inc hl inc hl ld [hl], $40 ; set X screen pos ret ; overwrites sprite data with zeroes ResetPlayerSpriteData_ClearSpriteData:: ; 28c4 (0:28c4) ld bc, $10 xor a jp FillMemory Func_28cb:: ; 28cb (0:28cb) ld a, [wMusicHeaderPointer] and a jr nz, .asm_28dc ld a, [wd72c] bit 1, a ret nz ld a, $77 ld [$ff24], a ret .asm_28dc ld a, [wcfc9] and a jr z, .asm_28e7 dec a ld [wcfc9], a ret .asm_28e7 ld a, [wcfc8] ld [wcfc9], a ld a, [$ff24] and a jr z, .asm_2903 ld b, a and $f dec a ld c, a ld a, b and $f0 swap a dec a swap a or c ld [$ff24], a ret .asm_2903 ld a, [wMusicHeaderPointer] ld b, a xor a ld [wMusicHeaderPointer], a ld a, $ff ld [wc0ee], a call PlaySound ld a, [wc0f0] ld [wc0ef], a ld a, b ld [wc0ee], a jp PlaySound ; this function is used to display sign messages, sprite dialog, etc. ; INPUT: [$ff8c] = sprite ID or text ID DisplayTextID:: ; 2920 (0:2920) ld a,[H_LOADEDROMBANK] push af callba DisplayTextIDInit ; initialization ld hl,wcf11 bit 0,[hl] res 0,[hl] jr nz,.skipSwitchToMapBank ld a,[W_CURMAP] call SwitchToMapRomBank .skipSwitchToMapBank ld a,30 ; half a second ld [H_FRAMECOUNTER],a ; used as joypad poll timer ld hl,W_MAPTEXTPTR ld a,[hli] ld h,[hl] ld l,a ; hl = map text pointer ld d,$00 ld a,[$ff8c] ; text ID ld [wSpriteIndex],a and a jp z,DisplayStartMenu cp a,$d3 jp z,DisplaySafariGameOverText cp a,$d0 jp z,DisplayPokemonFaintedText cp a,$d1 jp z,DisplayPlayerBlackedOutText cp a,$d2 jp z,DisplayRepelWoreOffText ld a,[W_NUMSPRITES] ld e,a ld a,[$ff8c] ; sprite ID cp e jr z,.spriteHandling jr nc,.skipSpriteHandling .spriteHandling ; get the text ID of the sprite push hl push de push bc callba UpdateSpriteFacingOffsetAndDelayMovement ; update the graphics of the sprite the player is talking to (to face the right direction) pop bc pop de ld hl,W_MAPSPRITEDATA ; NPC text entries ld a,[$ff8c] dec a add a add l ld l,a jr nc,.noCarry inc h .noCarry inc hl ld a,[hl] ; a = text ID of the sprite pop hl .skipSpriteHandling ; look up the address of the text in the map's text entries dec a ld e,a sla e add hl,de ld a,[hli] ld h,[hl] ld l,a ; hl = address of the text ld a,[hl] ; a = first byte of text ; check first byte of text for special cases cp a,$fe ; Pokemart NPC jp z,DisplayPokemartDialogue cp a,$ff ; Pokemon Center NPC jp z,DisplayPokemonCenterDialogue cp a,$fc ; Item Storage PC jp z,FuncTX_ItemStoragePC cp a,$fd ; Bill's PC jp z,FuncTX_BillsPC cp a,$f9 ; Pokemon Center PC jp z,FuncTX_PokemonCenterPC cp a,$f5 ; Vending Machine jr nz,.notVendingMachine callba VendingMachineMenu ; jump banks to vending machine routine jr AfterDisplayingTextID .notVendingMachine cp a,$f7 ; slot machine jp z,FuncTX_SlotMachine cp a,$f6 ; cable connection NPC in Pokemon Center jr nz,.notSpecialCase callab CableClubNPC jr AfterDisplayingTextID .notSpecialCase call Func_3c59 ; display the text ld a,[wDoNotWaitForButtonPressAfterDisplayingText] and a jr nz,HoldTextDisplayOpen AfterDisplayingTextID:: ; 29d6 (0:29d6) ld a,[wcc47] and a jr nz,HoldTextDisplayOpen call WaitForTextScrollButtonPress ; wait for a button press after displaying all the text ; loop to hold the dialogue box open as long as the player keeps holding down the A button HoldTextDisplayOpen:: ; 29df (0:29df) call Joypad ld a,[hJoyHeld] bit 0,a ; is the A button being pressed? jr nz,HoldTextDisplayOpen CloseTextDisplay:: ; 29e8 (0:29e8) ld a,[W_CURMAP] call SwitchToMapRomBank ld a,$90 ld [hVBlankWY],a ; move the window off the screen call DelayFrame call LoadGBPal xor a ld [H_AUTOBGTRANSFERENABLED],a ; disable continuous WRAM to VRAM transfer each V-blank ; loop to make sprites face the directions they originally faced before the dialogue ld hl,wSpriteStateData2 + $19 ld c,$0f ld de,$0010 .restoreSpriteFacingDirectionLoop ld a,[hl] dec h ld [hl],a inc h add hl,de dec c jr nz,.restoreSpriteFacingDirectionLoop ld a,BANK(InitMapSprites) ld [H_LOADEDROMBANK],a ld [$2000],a call InitMapSprites ; reload sprite tile pattern data (since it was partially overwritten by text tile patterns) ld hl,wcfc4 res 0,[hl] ld a,[wd732] bit 3,a ; used fly warp call z,LoadPlayerSpriteGraphics call LoadCurrentMapView pop af ld [H_LOADEDROMBANK],a ld [$2000],a jp UpdateSprites DisplayPokemartDialogue:: ; 2a2e (0:2a2e) push hl ld hl,PokemartGreetingText call PrintText pop hl inc hl call LoadItemList ld a,$02 ld [wListMenuID],a ; selects between subtypes of menus ld a,[H_LOADEDROMBANK] push af ld a,Bank(DisplayPokemartDialogue_) ld [H_LOADEDROMBANK],a ld [$2000],a call DisplayPokemartDialogue_ pop af ld [H_LOADEDROMBANK],a ld [$2000],a jp AfterDisplayingTextID PokemartGreetingText:: ; 2a55 (0:2a55) TX_FAR _PokemartGreetingText db "@" LoadItemList:: ; 2a5a (0:2a5a) ld a,$01 ld [wUpdateSpritesEnabled],a ld a,h ld [wd128],a ld a,l ld [wd129],a ld de,wStringBuffer2 + 11 .loop ld a,[hli] ld [de],a inc de cp a,$ff jr nz,.loop ret DisplayPokemonCenterDialogue:: ; 2a72 (0:2a72) xor a ld [$ff8b],a ld [$ff8c],a ld [$ff8d],a inc hl ld a,[H_LOADEDROMBANK] push af ld a,Bank(DisplayPokemonCenterDialogue_) ld [H_LOADEDROMBANK],a ld [$2000],a call DisplayPokemonCenterDialogue_ pop af ld [H_LOADEDROMBANK],a ld [$2000],a jp AfterDisplayingTextID DisplaySafariGameOverText:: ; 2a90 (0:2a90) callab PrintSafariGameOverText jp AfterDisplayingTextID DisplayPokemonFaintedText:: ; 2a9b (0:2a9b) ld hl,PokemonFaintedText call PrintText jp AfterDisplayingTextID PokemonFaintedText:: ; 2aa4 (0:2aa4) TX_FAR _PokemonFaintedText db "@" DisplayPlayerBlackedOutText:: ; 2aa9 (0:2aa9) ld hl,PlayerBlackedOutText call PrintText ld a,[wd732] res 5,a ; reset forced to use bike bit ld [wd732],a jp HoldTextDisplayOpen PlayerBlackedOutText:: ; 2aba (0:2aba) TX_FAR _PlayerBlackedOutText db "@" DisplayRepelWoreOffText:: ; 2abf (0:2abf) ld hl,RepelWoreOffText call PrintText jp AfterDisplayingTextID RepelWoreOffText:: ; 2ac8 (0:2ac8) TX_FAR _RepelWoreOffText db "@" INCLUDE "engine/menu/start_menu.asm" ; function to count how many bits are set in a string of bytes ; INPUT: ; hl = address of string of bytes ; b = length of string of bytes ; OUTPUT: ; [wd11e] = number of set bits CountSetBits:: ; 2b7f (0:2b7f) ld c,0 .loop ld a,[hli] ld e,a ld d,8 .innerLoop ; count how many bits are set in the current byte srl e ld a,0 adc c ld c,a dec d jr nz,.innerLoop dec b jr nz,.loop ld a,c ld [wd11e],a ; store number of set bits ret ; subtracts the amount the player paid from their money ; sets carry flag if there is enough money and unsets carry flag if not SubtractAmountPaidFromMoney:: ; 2b96 (0:2b96) ld b,BANK(SubtractAmountPaidFromMoney_) ld hl,SubtractAmountPaidFromMoney_ jp Bankswitch ; adds the amount the player sold to their money AddAmountSoldToMoney:: ; 2b9e (0:2b9e) ld de,wPlayerMoney + 2 ld hl,$ffa1 ; total price of items ld c,3 ; length of money in bytes predef AddBCDPredef ; add total price to money ld a,$13 ld [wd125],a call DisplayTextBoxID ; redraw money text box ld a, (SFX_02_5a - SFX_Headers_02) / 3 call PlaySoundWaitForCurrent ; play sound jp WaitForSoundToFinish ; wait until sound is done playing ; function to remove an item (in varying quantities) from the player's bag or PC box ; INPUT: ; HL = address of inventory (either wNumBagItems or wNumBoxItems) ; [wWhichPokemon] = index (within the inventory) of the item to remove ; [wcf96] = quantity to remove RemoveItemFromInventory:: ; 2bbb (0:2bbb) ld a,[H_LOADEDROMBANK] push af ld a,BANK(RemoveItemFromInventory_) ld [H_LOADEDROMBANK],a ld [$2000],a call RemoveItemFromInventory_ pop af ld [H_LOADEDROMBANK],a ld [$2000],a ret ; function to add an item (in varying quantities) to the player's bag or PC box ; INPUT: ; HL = address of inventory (either wNumBagItems or wNumBoxItems) ; [wcf91] = item ID ; [wcf96] = item quantity ; sets carry flag if successful, unsets carry flag if unsuccessful AddItemToInventory:: ; 2bcf (0:2bcf) push bc ld a,[H_LOADEDROMBANK] push af ld a,BANK(AddItemToInventory_) ld [H_LOADEDROMBANK],a ld [$2000],a call AddItemToInventory_ pop bc ld a,b ld [H_LOADEDROMBANK],a ld [$2000],a pop bc ret ; INPUT: ; [wListMenuID] = list menu ID ; [wcf8b] = address of the list (2 bytes) DisplayListMenuID:: ; 2be6 (0:2be6) xor a ld [H_AUTOBGTRANSFERENABLED],a ; disable auto-transfer ld a,1 ld [hJoy7],a ; joypad state update flag ld a,[W_BATTLETYPE] and a ; is it the Old Man battle? jr nz,.specialBattleType ld a,$01 ; hardcoded bank jr .bankswitch .specialBattleType ; Old Man battle ld a, Bank(DisplayBattleMenu) .bankswitch call BankswitchHome ld hl,wd730 set 6,[hl] ; turn off letter printing delay xor a ld [wMenuItemToSwap],a ; 0 means no item is currently being swapped ld [wd12a],a ld a,[wcf8b] ld l,a ld a,[wcf8c] ld h,a ; hl = address of the list ld a,[hl] ld [wd12a],a ; [wd12a] = number of list entries ld a,$0d ; list menu text box ID ld [wd125],a call DisplayTextBoxID ; draw the menu text box call UpdateSprites ; disable sprites behind the text box ; the code up to .skipMovingSprites appears to be useless hlCoord 4, 2 ; coordinates of upper left corner of menu text box ld de,$090e ; height and width of menu text box ld a,[wListMenuID] and a ; is it a PC pokemon list? jr nz,.skipMovingSprites call UpdateSprites ; move sprites .skipMovingSprites ld a,1 ; max menu item ID is 1 if the list has less than 2 entries ld [wcc37],a ld a,[wd12a] cp a,2 ; does the list have less than 2 entries? jr c,.setMenuVariables ld a,2 ; max menu item ID is 2 if the list has at least 2 entries .setMenuVariables ld [wMaxMenuItem],a ld a,4 ld [wTopMenuItemY],a ld a,5 ld [wTopMenuItemX],a ld a,A_BUTTON | B_BUTTON | SELECT ld [wMenuWatchedKeys],a ld c,10 call DelayFrames DisplayListMenuIDLoop:: ; 2c53 (0:2c53) xor a ld [H_AUTOBGTRANSFERENABLED],a ; disable transfer call PrintListMenuEntries ld a,1 ld [H_AUTOBGTRANSFERENABLED],a ; enable transfer call Delay3 ld a,[W_BATTLETYPE] and a ; is it the Old Man battle? jr z,.notOldManBattle .oldManBattle ld a,"▶" Coorda 5, 4 ; place menu cursor in front of first menu entry ld c,80 call DelayFrames xor a ld [wCurrentMenuItem],a hlCoord 5, 4 ld a,l ld [wMenuCursorLocation],a ld a,h ld [wMenuCursorLocation + 1],a jr .buttonAPressed .notOldManBattle call LoadGBPal call HandleMenuInput push af call PlaceMenuCursor pop af bit 0,a ; was the A button pressed? jp z,.checkOtherKeys .buttonAPressed ld a,[wCurrentMenuItem] call PlaceUnfilledArrowMenuCursor ld a,$01 ld [wd12e],a ld [wd12d],a xor a ld [wcc37],a ld a,[wCurrentMenuItem] ld c,a ld a,[wListScrollOffset] add c ld c,a ld a,[wd12a] ; number of list entries and a ; is the list empty? jp z,ExitListMenu ; if so, exit the menu dec a cp c ; did the player select Cancel? jp c,ExitListMenu ; if so, exit the menu ld a,c ld [wWhichPokemon],a ld a,[wListMenuID] cp a,ITEMLISTMENU jr nz,.skipMultiplying ; if it's an item menu sla c ; item entries are 2 bytes long, so multiply by 2 .skipMultiplying ld a,[wcf8b] ld l,a ld a,[wcf8c] ld h,a inc hl ; hl = beginning of list entries ld b,0 add hl,bc ld a,[hl] ld [wcf91],a ld a,[wListMenuID] and a ; is it a PC pokemon list? jr z,.pokemonList push hl call GetItemPrice pop hl ld a,[wListMenuID] cp a,ITEMLISTMENU jr nz,.skipGettingQuantity ; if it's an item menu inc hl ld a,[hl] ; a = item quantity ld [wcf97],a .skipGettingQuantity ld a,[wcf91] ld [wd0b5],a ld a,BANK(ItemNames) ld [wPredefBank],a call GetName jr .storeChosenEntry .pokemonList ld hl,wPartyCount ld a,[wcf8b] cp l ; is it a list of party pokemon or box pokemon? ld hl,wPartyMonNicks jr z,.getPokemonName ld hl, wBoxMonNicks ; box pokemon names .getPokemonName ld a,[wWhichPokemon] call GetPartyMonName .storeChosenEntry ; store the menu entry that the player chose and return ld de,wcd6d call CopyStringToCF4B ; copy name to wcf4b ld a,$01 ld [wd12e],a ld a,[wCurrentMenuItem] ld [wd12d],a xor a ld [hJoy7],a ; joypad state update flag ld hl,wd730 res 6,[hl] ; turn on letter printing delay jp BankswitchBack .checkOtherKeys ; check B, SELECT, Up, and Down keys bit 1,a ; was the B button pressed? jp nz,ExitListMenu ; if so, exit the menu bit 2,a ; was the select button pressed? jp nz,HandleItemListSwapping ; if so, allow the player to swap menu entries ld b,a bit 7,b ; was Down pressed? ld hl,wListScrollOffset jr z,.upPressed .downPressed ld a,[hl] add a,3 ld b,a ld a,[wd12a] ; number of list entries cp b ; will going down scroll past the Cancel button? jp c,DisplayListMenuIDLoop inc [hl] ; if not, go down jp DisplayListMenuIDLoop .upPressed ld a,[hl] and a jp z,DisplayListMenuIDLoop dec [hl] jp DisplayListMenuIDLoop DisplayChooseQuantityMenu:: ; 2d57 (0:2d57) ; text box dimensions/coordinates for just quantity hlCoord 15, 9 ld b,1 ; height ld c,3 ; width ld a,[wListMenuID] cp a,PRICEDITEMLISTMENU jr nz,.drawTextBox ; text box dimensions/coordinates for quantity and price hlCoord 7, 9 ld b,1 ; height ld c,11 ; width .drawTextBox call TextBoxBorder hlCoord 16, 10 ld a,[wListMenuID] cp a,PRICEDITEMLISTMENU jr nz,.printInitialQuantity hlCoord 8, 10 .printInitialQuantity ld de,InitialQuantityText call PlaceString xor a ld [wcf96],a ; initialize current quantity to 0 jp .incrementQuantity .waitForKeyPressLoop call JoypadLowSensitivity ld a,[hJoyPressed] ; newly pressed buttons bit 0,a ; was the A button pressed? jp nz,.buttonAPressed bit 1,a ; was the B button pressed? jp nz,.buttonBPressed bit 6,a ; was Up pressed? jr nz,.incrementQuantity bit 7,a ; was Down pressed? jr nz,.decrementQuantity jr .waitForKeyPressLoop .incrementQuantity ld a,[wcf97] ; max quantity inc a ld b,a ld hl,wcf96 ; current quantity inc [hl] ld a,[hl] cp b jr nz,.handleNewQuantity ; wrap to 1 if the player goes above the max quantity ld a,1 ld [hl],a jr .handleNewQuantity .decrementQuantity ld hl,wcf96 ; current quantity dec [hl] jr nz,.handleNewQuantity ; wrap to the max quantity if the player goes below 1 ld a,[wcf97] ; max quantity ld [hl],a .handleNewQuantity hlCoord 17, 10 ld a,[wListMenuID] cp a,PRICEDITEMLISTMENU jr nz,.printQuantity .printPrice ld c,$03 ld a,[wcf96] ld b,a ld hl,$ff9f ; total price ; initialize total price to 0 xor a ld [hli],a ld [hli],a ld [hl],a .addLoop ; loop to multiply the individual price by the quantity to get the total price ld de,$ffa1 ld hl,$ff8d push bc predef AddBCDPredef ; add the individual price to the current sum pop bc dec b jr nz,.addLoop ld a,[$ff8e] and a ; should the price be halved (for selling items)? jr z,.skipHalvingPrice xor a ld [$ffa2],a ld [$ffa3],a ld a,$02 ld [$ffa4],a predef DivideBCDPredef3 ; halves the price ; store the halved price ld a,[$ffa2] ld [$ff9f],a ld a,[$ffa3] ld [$ffa0],a ld a,[$ffa4] ld [$ffa1],a .skipHalvingPrice hlCoord 12, 10 ld de,SpacesBetweenQuantityAndPriceText call PlaceString ld de,$ff9f ; total price ld c,$a3 call PrintBCDNumber hlCoord 9, 10 .printQuantity ld de,wcf96 ; current quantity ld bc,$8102 ; print leading zeroes, 1 byte, 2 digits call PrintNumber jp .waitForKeyPressLoop .buttonAPressed ; the player chose to make the transaction xor a ld [wMenuItemToSwap],a ; 0 means no item is currently being swapped ret .buttonBPressed ; the player chose to cancel the transaction xor a ld [wMenuItemToSwap],a ; 0 means no item is currently being swapped ld a,$ff ret InitialQuantityText:: ; 2e30 (0:2e30) db "×01@" SpacesBetweenQuantityAndPriceText:: ; 2e34 (0:2e34) db " @" ExitListMenu:: ; 2e3b (0:2e3b) ld a,[wCurrentMenuItem] ld [wd12d],a ld a,$02 ld [wd12e],a ld [wcc37],a xor a ld [hJoy7],a ld hl,wd730 res 6,[hl] call BankswitchBack xor a ld [wMenuItemToSwap],a ; 0 means no item is currently being swapped scf ret PrintListMenuEntries:: ; 2e5a (0:2e5a) hlCoord 5, 3 ld b,$09 ld c,$0e call ClearScreenArea ld a,[wcf8b] ld e,a ld a,[wcf8c] ld d,a inc de ; de = beginning of list entries ld a,[wListScrollOffset] ld c,a ld a,[wListMenuID] cp a,ITEMLISTMENU ld a,c jr nz,.skipMultiplying ; if it's an item menu ; item entries are 2 bytes long, so multiply by 2 sla a sla c .skipMultiplying add e ld e,a jr nc,.noCarry inc d .noCarry hlCoord 6, 4 ; coordinates of first list entry name ld b,4 ; print 4 names .loop ld a,b ld [wWhichPokemon],a ld a,[de] ld [wd11e],a cp a,$ff jp z,.printCancelMenuItem push bc push de push hl push hl push de ld a,[wListMenuID] and a jr z,.pokemonPCMenu cp a,$01 jr z,.movesMenu .itemMenu call GetItemName jr .placeNameString .pokemonPCMenu push hl ld hl,wPartyCount ld a,[wcf8b] cp l ; is it a list of party pokemon or box pokemon? ld hl,wPartyMonNicks jr z,.getPokemonName ld hl, wBoxMonNicks ; box pokemon names .getPokemonName ld a,[wWhichPokemon] ld b,a ld a,4 sub b ld b,a ld a,[wListScrollOffset] add b call GetPartyMonName pop hl jr .placeNameString .movesMenu call GetMoveName .placeNameString call PlaceString pop de pop hl ld a,[wcf93] and a ; should prices be printed? jr z,.skipPrintingItemPrice .printItemPrice push hl ld a,[de] ld de,ItemPrices ld [wcf91],a call GetItemPrice ; get price pop hl ld bc,20 + 5 ; 1 row down and 5 columns right add hl,bc ld c,$a3 ; no leading zeroes, right-aligned, print currency symbol, 3 bytes call PrintBCDNumber .skipPrintingItemPrice ld a,[wListMenuID] and a jr nz,.skipPrintingPokemonLevel .printPokemonLevel ld a,[wd11e] push af push hl ld hl,wPartyCount ld a,[wcf8b] cp l ; is it a list of party pokemon or box pokemon? ld a,$00 jr z,.next ld a,$02 .next ld [wcc49],a ld hl,wWhichPokemon ld a,[hl] ld b,a ld a,$04 sub b ld b,a ld a,[wListScrollOffset] add b ld [hl],a call LoadMonData ; load pokemon info ld a,[wcc49] and a ; is it a list of party pokemon or box pokemon? jr z,.skipCopyingLevel .copyLevel ld a,[wcf9b] ld [wcfb9],a .skipCopyingLevel pop hl ld bc,$001c add hl,bc call PrintLevel ; print level pop af ld [wd11e],a .skipPrintingPokemonLevel pop hl pop de inc de ld a,[wListMenuID] cp a,ITEMLISTMENU jr nz,.nextListEntry .printItemQuantity ld a,[wd11e] ld [wcf91],a call IsKeyItem ; check if item is unsellable ld a,[wd124] and a ; is the item unsellable? jr nz,.skipPrintingItemQuantity ; if so, don't print the quantity push hl ld bc,20 + 8 ; 1 row down and 8 columns right add hl,bc ld a,"×" ld [hli],a ld a,[wd11e] push af ld a,[de] ld [wcf97],a push de ld de,wd11e ld [de],a ld bc,$0102 call PrintNumber pop de pop af ld [wd11e],a pop hl .skipPrintingItemQuantity inc de pop bc inc c push bc inc c ld a,[wMenuItemToSwap] ; ID of item chosen for swapping (counts from 1) and a ; is an item being swapped? jr z,.nextListEntry sla a cp c ; is it this item? jr nz,.nextListEntry dec hl ld a,$ec ; unfilled right arrow menu cursor to indicate an item being swapped ld [hli],a .nextListEntry ld bc,2 * 20 ; 2 rows add hl,bc pop bc inc c dec b jp nz,.loop ld bc,-8 add hl,bc ld a,$ee ; down arrow ld [hl],a ret .printCancelMenuItem ld de,ListMenuCancelText jp PlaceString ListMenuCancelText:: ; 2f97 (0:2f97) db "CANCEL@" GetMonName:: ; 2f9e (0:2f9e) push hl ld a,[H_LOADEDROMBANK] push af ld a,BANK(MonsterNames) ; 07 ld [H_LOADEDROMBANK],a ld [$2000],a ld a,[wd11e] dec a ld hl,MonsterNames ; 421E ld c,10 ld b,0 call AddNTimes ld de,wcd6d push de ld bc,10 call CopyData ld hl,wcd77 ld [hl], "@" pop de pop af ld [H_LOADEDROMBANK],a ld [$2000],a pop hl ret GetItemName:: ; 2fcf (0:2fcf) ; given an item ID at [wd11e], store the name of the item into a string ; starting at wcd6d push hl push bc ld a,[wd11e] cp HM_01 ; is this a TM/HM? jr nc,.Machine ld [wd0b5],a ld a,ITEM_NAME ld [W_LISTTYPE],a ld a,BANK(ItemNames) ld [wPredefBank],a call GetName jr .Finish .Machine call GetMachineName .Finish ld de,wcd6d ; pointer to where item name is stored in RAM pop bc pop hl ret GetMachineName:: ; 2ff3 (0:2ff3) ; copies the name of the TM/HM in [wd11e] to wcd6d push hl push de push bc ld a,[wd11e] push af cp TM_01 ; is this a TM? [not HM] jr nc,.WriteTM ; if HM, then write "HM" and add 5 to the item ID, so we can reuse the ; TM printing code add 5 ld [wd11e],a ld hl,HiddenPrefix ; points to "HM" ld bc,2 jr .WriteMachinePrefix .WriteTM ld hl,TechnicalPrefix ; points to "TM" ld bc,2 .WriteMachinePrefix ld de,wcd6d call CopyData ; now get the machine number and convert it to text ld a,[wd11e] sub TM_01 - 1 ld b,$F6 ; "0" .FirstDigit sub 10 jr c,.SecondDigit inc b jr .FirstDigit .SecondDigit add 10 push af ld a,b ld [de],a inc de pop af ld b,$F6 ; "0" add b ld [de],a inc de ld a,"@" ld [de],a pop af ld [wd11e],a pop bc pop de pop hl ret TechnicalPrefix:: ; 303c (0:303c) db "TM" HiddenPrefix:: ; 303e (0:303e) db "HM" ; sets carry if item is HM, clears carry if item is not HM ; Input: a = item ID IsItemHM:: ; 3040 (0:3040) cp a,HM_01 jr c,.notHM cp a,TM_01 ret .notHM and a ret ; sets carry if move is an HM, clears carry if move is not an HM ; Input: a = move ID IsMoveHM:: ; 3049 (0:3049) ld hl,HMMoves ld de,1 jp IsInArray HMMoves:: ; 3052 (0:3052) db CUT,FLY,SURF,STRENGTH,FLASH db $ff ; terminator GetMoveName:: ; 3058 (0:3058) push hl ld a,MOVE_NAME ld [W_LISTTYPE],a ld a,[wd11e] ld [wd0b5],a ld a,BANK(MoveNames) ld [wPredefBank],a call GetName ld de,wcd6d ; pointer to where move name is stored in RAM pop hl ret ; reloads text box tile patterns, current map view, and tileset tile patterns ReloadMapData:: ; 3071 (0:3071) ld a,[H_LOADEDROMBANK] push af ld a,[W_CURMAP] call SwitchToMapRomBank call DisableLCD call LoadTextBoxTilePatterns call LoadCurrentMapView call LoadTilesetTilePatternData call EnableLCD pop af ld [H_LOADEDROMBANK],a ld [$2000],a ret ; reloads tileset tile patterns ReloadTilesetTilePatterns:: ; 3090 (0:3090) ld a,[H_LOADEDROMBANK] push af ld a,[W_CURMAP] call SwitchToMapRomBank call DisableLCD call LoadTilesetTilePatternData call EnableLCD pop af ld [H_LOADEDROMBANK],a ld [$2000],a ret ; shows the town map and lets the player choose a destination to fly to ChooseFlyDestination:: ; 30a9 (0:30a9) ld hl,wd72e res 4,[hl] ld b, BANK(LoadTownMap_Fly) ld hl, LoadTownMap_Fly jp Bankswitch ; causes the text box to close without waiting for a button press after displaying text DisableWaitingAfterTextDisplay:: ; 30b6 (0:30b6) 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: ; [wcd6a] = success ; 00: unsucessful ; 01: successful ; 02: not able to be used right now, no extra menu displayed (only certain items use this) UseItem:: ; 30bc (0:30bc) ld b,BANK(UseItem_) ld hl,UseItem_ jp Bankswitch ; confirms the item toss and then tosses the item ; INPUT: ; hl = address of inventory (either wNumBagItems or wNumBoxItems) ; [wcf91] = item ID ; [wWhichPokemon] = index of item within inventory ; [wcf96] = quantity to toss ; OUTPUT: ; clears carry flag if the item is tossed, sets carry flag if not TossItem:: ; 30c4 (0:30c4) ld a,[H_LOADEDROMBANK] push af ld a,BANK(TossItem_) ld [H_LOADEDROMBANK],a ld [$2000],a call TossItem_ pop de ld a,d ld [H_LOADEDROMBANK],a ld [$2000],a ret ; checks if an item is a key item ; INPUT: ; [wcf91] = item ID ; OUTPUT: ; [wd124] = result ; 00: item is not key item ; 01: item is key item IsKeyItem:: ; 30d9 (0:30d9) push hl push de push bc callba IsKeyItem_ pop bc pop de pop hl ret ; function to draw various text boxes ; INPUT: ; [wd125] = text box ID DisplayTextBoxID:: ; 30e8 (0:30e8) ld a,[H_LOADEDROMBANK] push af ld a,BANK(DisplayTextBoxID_) ld [H_LOADEDROMBANK],a ld [$2000],a call DisplayTextBoxID_ pop bc ld a,b ld [H_LOADEDROMBANK],a ld [$2000],a ret ; 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:: ; 30fd (0:30fd) 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:: ; 310e (0:310e) 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 ld a, [H_LOADEDROMBANK] push af ld a, [wNPCMovementScriptBank] ld [H_LOADEDROMBANK], a ld [$2000], a ld a, [wNPCMovementScriptFunctionNum] call CallFunctionInTable pop af ld [H_LOADEDROMBANK], a ld [$2000], a ret .NPCMovementScriptPointerTables dw ProfOakMovementScriptPointerTable dw PewterMuseumGuyMovementScriptPointerTable dw PewterGymGuyMovementScriptPointerTable .playerStepOutFromDoor ld b, BANK(PlayerStepOutFromDoor) ld hl, PlayerStepOutFromDoor jp Bankswitch EndNPCMovementScript:: ; 314e (0:314e) ld b, BANK(_EndNPCMovementScript) ld hl, _EndNPCMovementScript jp Bankswitch EmptyFunc2:: ; 3156 (0:3156) ret ; stores hl in [W_TRAINERHEADERPTR] StoreTrainerHeaderPointer:: ; 3157 (0:3157) ld a, h ld [W_TRAINERHEADERPTR], a ld a, l ld [W_TRAINERHEADERPTR+1], a ret ; executes the current map script from the function pointer array provided in hl. ; a: map script index to execute (unless overridden by [wd733] bit 4) ExecuteCurMapScriptInTable:: ; 3160 (0:3160) push af push de call StoreTrainerHeaderPointer pop hl pop af push hl ld hl, W_FLAGS_D733 bit 4, [hl] res 4, [hl] jr z, .useProvidedIndex ; test if map script index was overridden manually ld a, [W_CURMAPSCRIPT] .useProvidedIndex pop hl ld [W_CURMAPSCRIPT], a call CallFunctionInTable ld a, [W_CURMAPSCRIPT] ret LoadGymLeaderAndCityName:: ; 317f (0:317f) push de ld de, wGymCityName ld bc, $11 call CopyData ; load city name pop hl ld de, wGymLeaderName ld bc, $b jp CopyData ; load gym leader name ; reads specific information from trainer header (pointed to at W_TRAINERHEADERPTR) ; a: offset in header data ; 0 -> flag's bit (into wTrainerHeaderFlagBit) ; 2 -> flag's byte ptr (into hl) ; 4 -> before battle text (into hl) ; 6 -> after battle text (into hl) ; 8 -> end battle text (into hl) ReadTrainerHeaderInfo:: ; 3193 (0:3193) push de push af ld d, $0 ld e, a ld hl, W_TRAINERHEADERPTR ld a, [hli] ld l, [hl] ld h, a add hl, de pop af and a jr nz, .nonZeroOffset ld a, [hl] ld [wTrainerHeaderFlagBit], a ; store flag's bit jr .done .nonZeroOffset cp $2 jr z, .readPointer ; read flag's byte ptr cp $4 jr z, .readPointer ; read before battle text cp $6 jr z, .readPointer ; read after battle text cp $8 jr z, .readPointer ; read end battle text cp $a jr nz, .done ld a, [hli] ; read end battle text (2) but override the result afterwards (XXX why, bug?) ld d, [hl] ld e, a jr .done .readPointer ld a, [hli] ld h, [hl] ld l, a .done pop de ret TrainerFlagAction:: predef_jump FlagActionPredef TalkToTrainer:: ; 31cc (0:31cc) call StoreTrainerHeaderPointer xor a call ReadTrainerHeaderInfo ; read flag's bit ld a, $2 call ReadTrainerHeaderInfo ; read flag's byte ptr ld a, [wTrainerHeaderFlagBit] ld c, a ld b, $2 call TrainerFlagAction ; read trainer's flag ld a, c and a jr z, .trainerNotYetFought ; test trainer's flag ld a, $6 call ReadTrainerHeaderInfo ; print after battle text jp PrintText .trainerNotYetFought ; 0x31ed ld a, $4 call ReadTrainerHeaderInfo ; print before battle text call PrintText ld a, $a call ReadTrainerHeaderInfo ; (?) does nothing apparently (maybe bug in ReadTrainerHeaderInfo) push de ld a, $8 call ReadTrainerHeaderInfo ; read end battle text pop de call SaveEndBattleTextPointers ld hl, W_FLAGS_D733 set 4, [hl] ; activate map script index override (index is set below) ld hl, wFlags_0xcd60 bit 0, [hl] ; test if player is already engaging the trainer (because the trainer saw the player) ret nz ; if the player talked to the trainer of his own volition call EngageMapTrainer ld hl, W_CURMAPSCRIPT inc [hl] ; increment map script index before StartTrainerBattle increments it again (next script function is usually EndTrainerBattle) jp StartTrainerBattle ; checks if any trainers are seeing the player and wanting to fight CheckFightingMapTrainers:: ; 3219 (0:3219) call CheckForEngagingTrainers ld a, [wSpriteIndex] cp $ff jr nz, .trainerEngaging xor a ld [wSpriteIndex], a ld [wTrainerHeaderFlagBit], a ret .trainerEngaging ld hl, W_FLAGS_D733 set 3, [hl] ld [wcd4f], a xor a ld [wcd50], a predef EmotionBubble ld a, D_RIGHT | D_LEFT | D_UP | D_DOWN ld [wJoyIgnore], a xor a ldh [$b4], a call TrainerWalkUpToPlayer_Bank0 ld hl, W_CURMAPSCRIPT inc [hl] ; increment map script index (next script function is usually DisplayEnemyTrainerTextAndStartBattle) ret ; display the before battle text after the enemy trainer has walked up to the player's sprite DisplayEnemyTrainerTextAndStartBattle:: ; 324c (0:324c) ld a, [wd730] and $1 ret nz ; return if the enemy trainer hasn't finished walking to the player's sprite ld [wJoyIgnore], a ld a, [wSpriteIndex] ld [hSpriteIndexOrTextID], a call DisplayTextID ; fall through StartTrainerBattle:: ; 325d (0:325d) xor a ld [wJoyIgnore], a call InitBattleEnemyParameters ld hl, wd72d set 6, [hl] set 7, [hl] ld hl, wd72e set 1, [hl] ld hl, W_CURMAPSCRIPT inc [hl] ; increment map script index (next script function is usually EndTrainerBattle) ret EndTrainerBattle:: ; 3275 (0:3275) ld hl, wd126 set 5, [hl] set 6, [hl] ld hl, wd72d res 7, [hl] ld hl, wFlags_0xcd60 res 0, [hl] ; player is no longer engaged by any trainer ld a, [W_ISINBATTLE] ; W_ISINBATTLE cp $ff jp z, ResetButtonPressedAndMapScript ld a, $2 call ReadTrainerHeaderInfo ld a, [wTrainerHeaderFlagBit] ld c, a ld b, $1 call TrainerFlagAction ; flag trainer as fought ld a, [W_ENEMYMONORTRAINERCLASS] cp $c8 jr nc, .skipRemoveSprite ; test if trainer was fought (in that case skip removing the corresponding sprite) ld hl, W_MISSABLEOBJECTLIST ld de, $2 ld a, [wSpriteIndex] call IsInArray ; search for sprite ID inc hl ld a, [hl] ld [wcc4d], a ; load corresponding missable object index and remove it predef HideObject .skipRemoveSprite ld hl, wd730 bit 4, [hl] res 4, [hl] ret nz ResetButtonPressedAndMapScript:: ; 32c1 (0:32c1) xor a ld [wJoyIgnore], a ld [hJoyHeld], a ld [hJoyPressed], a ld [hJoyReleased], a ld [W_CURMAPSCRIPT], a ; reset battle status ret ; calls TrainerWalkUpToPlayer TrainerWalkUpToPlayer_Bank0:: ; 32cf (0:32cf) ld b, BANK(TrainerWalkUpToPlayer) ld hl, TrainerWalkUpToPlayer jp Bankswitch ; sets opponent type and mon set/lvl based on the engaging trainer data InitBattleEnemyParameters:: ; 32d7 (0:32d7) ld a, [wEngagedTrainerClass] ld [W_CUROPPONENT], a ; wd059 ld [W_ENEMYMONORTRAINERCLASS], a cp $c8 ld a, [wEngagedTrainerSet] ; wcd2e jr c, .noTrainer ld [W_TRAINERNO], a ; wd05d ret .noTrainer ld [W_CURENEMYLVL], a ; W_CURENEMYLVL ret GetSpritePosition1:: ; 32ef (0:32ef) ld hl, _GetSpritePosition1 jr asm_3301 GetSpritePosition2:: ; 32f4 (0:32f4) ld hl, _GetSpritePosition2 jr asm_3301 ; 0x32f7 $8 SetSpritePosition1:: ; 32f9 (0:32f9) ld hl, _SetSpritePosition1 jr asm_3301 SetSpritePosition2:: ; 32fe (0:32fe) ld hl, _SetSpritePosition2 asm_3301:: ; 3301 (0:3301) ld b, BANK(_GetSpritePosition1) ; BANK(_GetSpritePosition2), BANK(_SetSpritePosition1), BANK(_SetSpritePosition2) jp Bankswitch ; indirect jump to one of the four functions CheckForEngagingTrainers:: ; 3306 (0:3306) xor a call ReadTrainerHeaderInfo ; read trainer flag's bit (unused) ld d, h ; store trainer header address in de ld e, l .trainerLoop call StoreTrainerHeaderPointer ; set trainer header pointer to current trainer ld a, [de] ld [wSpriteIndex], a ; store trainer flag's bit ld [wTrainerHeaderFlagBit], a cp $ff ret z ld a, $2 call ReadTrainerHeaderInfo ; read trainer flag's byte ptr ld b, $2 ld a, [wTrainerHeaderFlagBit] ld c, a call TrainerFlagAction ; read trainer flag ld a, c and a jr nz, .trainerAlreadyFought push hl push de push hl xor a call ReadTrainerHeaderInfo ; get trainer header pointer inc hl ld a, [hl] ; read trainer engage distance pop hl ld [wTrainerEngageDistance], a ld a, [wSpriteIndex] swap a ld [wTrainerSpriteOffset], a ; wWhichTrade predef TrainerEngage pop de pop hl ld a, [wTrainerSpriteOffset] ; wWhichTrade and a ret nz ; break if the trainer is engaging .trainerAlreadyFought ld hl, $c add hl, de ld d, h ld e, l jr .trainerLoop ; hl = text if the player wins ; de = text if the player loses SaveEndBattleTextPointers:: ; 3354 (0:3354) ld a, [H_LOADEDROMBANK] ld [wEndBattleTextRomBank], a ld a, h ld [wEndBattleWinTextPointer], a ld a, l ld [wEndBattleWinTextPointer + 1], a ld a, d ld [wEndBattleLoseTextPointer], a ld a, e ld [wEndBattleLoseTextPointer + 1], a ret ; loads data of some trainer on the current map and plays pre-battle music ; [wSpriteIndex]: sprite ID of trainer who is engaged EngageMapTrainer:: ; 336a (0:336a) ld hl, W_MAPSPRITEEXTRADATA ld d, $0 ld a, [wSpriteIndex] dec a add a ld e, a add hl, de ; seek to engaged trainer data ld a, [hli] ; load trainer class ld [wEngagedTrainerClass], a ld a, [hl] ; load trainer mon set ld [wEnemyMonAttackMod], a ; wcd2e jp PlayTrainerMusic PrintEndBattleText:: ; 3381 (0:3381) push hl ld hl, wd72d bit 7, [hl] res 7, [hl] pop hl ret z ld a, [H_LOADEDROMBANK] push af ld a, [wEndBattleTextRomBank] ld [H_LOADEDROMBANK], a ld [MBC1RomBank], a push hl callba SaveTrainerName ld hl, TrainerEndBattleText call PrintText pop hl pop af ld [H_LOADEDROMBANK], a ld [MBC1RomBank], a callba FreezeEnemyTrainerSprite jp WaitForSoundToFinish GetSavedEndBattleTextPointer:: ; 33b7 (0:33b7) ld a, [wBattleResult] and a ; won battle jr nz, .lostBattle ld a, [wEndBattleWinTextPointer] ld h, a ld a, [wEndBattleWinTextPointer + 1] ld l, a ret .lostBattle ld a, [wEndBattleLoseTextPointer] ld h, a ld a, [wEndBattleLoseTextPointer + 1] ld l, a ret TrainerEndBattleText:: ; 33cf (0:33cf) TX_FAR _TrainerNameText db $08 call GetSavedEndBattleTextPointer call TextCommandProcessor jp TextScriptEnd ; XXX unused? Func_33dd:: ; 33dd (0:33dd) ld a, [wFlags_0xcd60] bit 0, a ret nz call EngageMapTrainer xor a ret PlayTrainerMusic:: ; 33e8 (0:33e8) ld a, [wEngagedTrainerClass] cp $c8 + SONY1 ret z cp $c8 + SONY2 ret z cp $c8 + SONY3 ret z ld a, [W_GYMLEADERNO] ; W_GYMLEADERNO and a ret nz xor a ld [wMusicHeaderPointer], a ld a, $ff call PlaySound ; stop music ld a, BANK(Music_MeetEvilTrainer) ld [wc0ef], a ld [wc0f0], a ld a, [wEngagedTrainerClass] ld b, a ld hl, EvilTrainerList .evilTrainerListLoop ld a, [hli] cp $ff jr z, .noEvilTrainer cp b jr nz, .evilTrainerListLoop ld a, MUSIC_MEET_EVIL_TRAINER jr .PlaySound .noEvilTrainer ld hl, FemaleTrainerList .femaleTrainerListLoop ld a, [hli] cp $ff jr z, .maleTrainer cp b jr nz, .femaleTrainerListLoop ld a, MUSIC_MEET_FEMALE_TRAINER jr .PlaySound .maleTrainer ld a, MUSIC_MEET_MALE_TRAINER .PlaySound ld [wc0ee], a jp PlaySound INCLUDE "data/trainer_types.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:: ; 3442 (0:3442) 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 FuncTX_ItemStoragePC:: ; 3460 (0:3460) call SaveScreenTilesToBuffer2 ld b, BANK(PlayerPC) ld hl, PlayerPC jr bankswitchAndContinue FuncTX_BillsPC:: ; 346a (0:346a) call SaveScreenTilesToBuffer2 ld b, BANK(BillsPC_) ld hl, BillsPC_ jr bankswitchAndContinue FuncTX_SlotMachine:: ; 3474 (0:3474) ; XXX find a better name for this function ; special_F7 ld b,BANK(CeladonPrizeMenu) ld hl,CeladonPrizeMenu bankswitchAndContinue:: ; 3479 (0:3479) call Bankswitch jp HoldTextDisplayOpen ; continue to main text-engine function FuncTX_PokemonCenterPC:: ; 347f (0:347f) ld b, BANK(ActivatePC) ld hl, ActivatePC jr bankswitchAndContinue StartSimulatingJoypadStates:: ; 3486 (0:3486) xor a ld [wOverrideSimulatedJoypadStatesMask], a ld [wSpriteStateData2 + $06], a ; player's sprite movement byte 1 ld hl, wd730 set 7, [hl] ret IsItemInBag:: ; 3493 (0:3493) ; given an item_id in b ; set zero flag if item isn't in player's bag ; else reset zero flag ; related to Pokémon Tower and ghosts predef IsItemInBag_ ld a,b and a ret DisplayPokedex:: ; 349b (0:349b) ld [wd11e], a ld b, BANK(Func_7c18) ld hl, Func_7c18 jp Bankswitch SetSpriteFacingDirectionAndDelay:: ; 34a6 (0:34a6) call SetSpriteFacingDirection ld c, $6 jp DelayFrames SetSpriteFacingDirection:: ; 34ae (0:34ae) ld a, $9 ld [H_SPRITEDATAOFFSET], a call GetPointerWithinSpriteStateData1 ld a, [$ff8d] ld [hl], a ret SetSpriteImageIndexAfterSettingFacingDirection:: ; 34b9 (0:34b9) 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: ; [wWhichTrade] = if there is match, the matching array index ; sets carry if the coordinates are in the array, clears carry if not ArePlayerCoordsInArray:: ; 34bf (0:34bf) ld a,[W_YCOORD] ld b,a ld a,[W_XCOORD] ld c,a ; fallthrough CheckCoords:: ; 34c7 (0:34c7) xor a ld [wWhichTrade],a .loop ld a,[hli] cp a,$ff ; reached terminator? jr z,.notInArray push hl ld hl,wWhichTrade 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 ; [H_SPRITEINDEX] = index of boulder sprite ; OUTPUT: ; [wWhichTrade] = if there is match, the matching array index ; sets carry if the coordinates are in the array, clears carry if not CheckBoulderCoords:: ; 34e4 (0:34e4) push hl ld hl, wSpriteStateData2 + $04 ld a, [H_SPRITEINDEX] 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:: ; 34fc (0:34fc) ld h, $c1 jr _GetPointerWithinSpriteStateData GetPointerWithinSpriteStateData2:: ; 3500 (0:3500) ld h, $c2 _GetPointerWithinSpriteStateData: ld a, [H_SPRITEDATAOFFSET] ld b, a ld a, [H_SPRITEINDEX] swap a add b ld l, a ret ; decodes a $ff-terminated RLEncoded list ; each entry is a pair of bytes <byte value> <repetitions> ; the final $ff will be replicated in the output list and a contains the number of bytes written ; de: input list ; hl: output list DecodeRLEList:: ; 350c (0:350c) xor a ld [wRLEByteCount], a ; count written bytes here .listLoop ld a, [de] cp $ff jr z, .endOfList ld [H_DOWNARROWBLINKCNT1], a ; store byte value to be written inc de ld a, [de] ld b, $0 ld c, a ; number of bytes to be written ld a, [wRLEByteCount] add c ld [wRLEByteCount], a ; update total number of written bytes ld a, [H_DOWNARROWBLINKCNT1] ; $ff8b call FillMemory ; write a c-times to output inc de jr .listLoop .endOfList ld a, $ff ld [hl], a ; write final $ff ld a, [wRLEByteCount] inc a ; include sentinel in counting ret ; sets movement byte 1 for sprite [$FF8C] to $FE and byte 2 to [$FF8D] SetSpriteMovementBytesToFE:: ; 3533 (0:3533) push hl call GetSpriteMovementByte1Pointer ld [hl], $fe call GetSpriteMovementByte2Pointer ld a, [$ff8d] ld [hl], a pop hl ret ; sets both movement bytes for sprite [$FF8C] to $FF SetSpriteMovementBytesToFF:: ; 3541 (0:3541) push hl call GetSpriteMovementByte1Pointer ld [hl],$FF call GetSpriteMovementByte2Pointer ld [hl],$FF ; prevent person from walking? pop hl ret ; returns the sprite movement byte 1 pointer for sprite [$FF8C] in hl GetSpriteMovementByte1Pointer:: ; 354e (0:354e) ld h,$C2 ld a,[H_SPRITEINDEX] ; the sprite to move swap a add a,6 ld l,a ret ; returns the sprite movement byte 2 pointer for sprite [$FF8C] in hl GetSpriteMovementByte2Pointer:: ; 3558 (0:3558) push de ld hl,W_MAPSPRITEDATA ld a,[$FF8C] ; the sprite to move dec a add a ld d,0 ld e,a add hl,de pop de ret GetTrainerInformation:: ; 3566 (0:3566) call GetTrainerName ld a, [W_ISLINKBATTLE] ; W_ISLINKBATTLE and a jr nz, .linkBattle ld a, Bank(TrainerPicAndMoneyPointers) call BankswitchHome ld a, [W_TRAINERCLASS] ; wd031 dec a ld hl, TrainerPicAndMoneyPointers ld bc, $5 call AddNTimes ld de, wd033 ld a, [hli] ld [de], a inc de ld a, [hli] ld [de], a ld de, wd046 ld a, [hli] ld [de], a inc de ld a, [hli] ld [de], a jp BankswitchBack .linkBattle ld hl, wd033 ld de, RedPicFront ld [hl], e inc hl ld [hl], d ret GetTrainerName:: ; 359e (0:359e) ld b, BANK(GetTrainerName_) ld hl, GetTrainerName_ jp Bankswitch HasEnoughMoney:: ; Check if the player has at least as much ; money as the 3-byte BCD value at $ff9f. ld de, wPlayerMoney ld hl, $ff9f ld c, 3 jp StringCmp HasEnoughCoins:: ; Check if the player has at least as many ; coins as the 2-byte BCD value at $ffa0. ld de, wPlayerCoins ld hl, $ffa0 ld c, 2 jp StringCmp BankswitchHome:: ; 35bc (0:35bc) ; switches to bank # in a ; Only use this when in the home bank! ld [wcf09],a ld a,[H_LOADEDROMBANK] ld [wcf08],a ld a,[wcf09] ld [H_LOADEDROMBANK],a ld [$2000],a ret BankswitchBack:: ; 35cd (0:35cd) ; returns from BankswitchHome ld a,[wcf08] ld [H_LOADEDROMBANK],a ld [$2000],a ret Bankswitch:: ; 35d6 (0:35d6) ; self-contained bankswitch, use this when not in the home bank ; switches to the bank in b ld a,[H_LOADEDROMBANK] push af ld a,b ld [H_LOADEDROMBANK],a ld [$2000],a ld bc,.Return push bc jp [hl] .Return pop bc ld a,b ld [H_LOADEDROMBANK],a ld [$2000],a ret ; displays yes/no choice ; yes -> set carry YesNoChoice:: ; 35ec (0:35ec) call SaveScreenTilesToBuffer1 call InitYesNoTextBoxParameters jr DisplayYesNoChoice Func_35f4:: ; 35f4 (0:35f4) ld a, $14 ld [wd125], a call InitYesNoTextBoxParameters jp DisplayTextBoxID InitYesNoTextBoxParameters:: ; 35ff (0:35ff) xor a ld [wd12c], a hlCoord 14, 7 ld bc, $80f ret YesNoChoicePokeCenter:: ; 360a (0:360a) call SaveScreenTilesToBuffer1 ld a, $6 ld [wd12c], a hlCoord 11, 6 ld bc, $80c jr DisplayYesNoChoice Func_361a:: ; 361a (0:361a) call SaveScreenTilesToBuffer1 ld a, $3 ld [wd12c], a hlCoord 12, 7 ld bc, $080d DisplayYesNoChoice:: ; 3628 (0:3628) ld a, $14 ld [wd125], a call DisplayTextBoxID jp LoadScreenTilesFromBuffer1 ; calculates the difference |a-b|, setting carry flag if a<b CalcDifference:: ; 3633 (0:3633) sub b ret nc cpl add $1 scf ret MoveSprite:: ; 363a (0:363a) ; move the sprite [$FF8C] with the movement pointed to by de ; actually only copies the movement data to wcc5b for later call SetSpriteMovementBytesToFF MoveSprite_:: ; 363d (0:363d) push hl push bc call GetSpriteMovementByte1Pointer xor a ld [hl],a ld hl,wcc5b ld c,0 .loop ld a,[de] ld [hli],a inc de inc c cp a,$FF ; have we reached the end of the movement data? jr nz,.loop ld a,c ld [wcf0f],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 [$ffe5] by [$ffe6] and stores the quotient in [$ffe7] DivideBytes:: ; 366b (0:366b) push hl ld hl, $ffe7 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:: ld a, [rLCDC] bit 7, a ; is the LCD enabled? jr nz, .on .off ld hl, FontGraphics ld de, vFont ld bc, $400 ld a, BANK(FontGraphics) jp FarCopyDataDouble ; if LCD is off, transfer all at once .on ld de, FontGraphics ld hl, vFont ld bc, BANK(FontGraphics) << 8 | $80 jp CopyVideoDataDouble ; if LCD is on, transfer during V-blank LoadTextBoxTilePatterns:: ld a, [rLCDC] bit 7, a ; is the LCD enabled? jr nz, .on .off ld hl, TextBoxGraphics ld de, vChars2 + $600 ld bc, $200 ld a, BANK(TextBoxGraphics) jp FarCopyData2 ; if LCD is off, transfer all at once .on ld de, TextBoxGraphics ld hl, vChars2 + $600 ld bc, BANK(TextBoxGraphics) << 8 | $20 jp CopyVideoData ; if LCD is on, transfer during V-blank LoadHpBarAndStatusTilePatterns:: ld a, [rLCDC] bit 7, a ; is the LCD enabled? jr nz, .on .off ld hl, HpBarAndStatusGraphics ld de, vChars2 + $620 ld bc, $1e0 ld a, BANK(HpBarAndStatusGraphics) jp FarCopyData2 ; if LCD is off, transfer all at once .on ld de, HpBarAndStatusGraphics ld hl, vChars2 + $620 ld bc, BANK(HpBarAndStatusGraphics) << 8 | $1e 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:: ; 36eb (0:36eb) ; Decompress pic at a:de. ld hl, W_SPRITEINPUTPTR ld [hl], e inc hl ld [hl], d jp UncompressSpriteData SaveScreenTilesToBuffer2:: ; 36f4 (0:36f4) ld hl, wTileMap ld de, wTileMapBackup2 ld bc, $168 call CopyData ret LoadScreenTilesFromBuffer2:: ; 3701 (0:3701) call LoadScreenTilesFromBuffer2DisableBGTransfer ld a, $1 ld [H_AUTOBGTRANSFERENABLED], a ; $ffba ret ; loads screen tiles stored in wTileMapBackup2 but leaves H_AUTOBGTRANSFERENABLED disabled LoadScreenTilesFromBuffer2DisableBGTransfer:: ; 3709 (0:3709) xor a ld [H_AUTOBGTRANSFERENABLED], a ; $ffba ld hl, wTileMapBackup2 ld de, wTileMap ld bc, $168 call CopyData ret SaveScreenTilesToBuffer1:: ; 3719 (0:3719) ld hl, wTileMap ld de, wTileMapBackup ld bc, $168 jp CopyData LoadScreenTilesFromBuffer1:: ; 3725 (0:3725) xor a ld [H_AUTOBGTRANSFERENABLED], a ; $ffba ld hl, wTileMapBackup ld de, wTileMap ld bc, $168 call CopyData ld a, $1 ld [H_AUTOBGTRANSFERENABLED], a ; $ffba ret DelayFrames:: ; 3739 (0:3739) ; wait n frames, where n is the value in c call DelayFrame dec c jr nz,DelayFrames ret PlaySoundWaitForCurrent:: ; 3740 (0:3740) push af call WaitForSoundToFinish pop af jp PlaySound ; Wait for sound to finish playing WaitForSoundToFinish:: ; 3748 (0:3748) ld a, [wd083] and $80 ret nz push hl .asm_374f ld hl, wc02a xor a or [hl] inc hl or [hl] inc hl inc hl or [hl] jr nz, .asm_374f pop hl ret NamePointers:: ; 375d (0:375d) dw MonsterNames dw MoveNames dw UnusedNames dw ItemNames dw wPartyMonOT ; player's OT names list dw wEnemyMonOT ; enemy's OT names list dw TrainerNames GetName:: ; 376b (0:376b) ; arguments: ; [wd0b5] = which name ; [wd0b6] = which list (W_LISTTYPE) ; [wPredefBank] = bank of list ; ; returns pointer to name in de ld a,[wd0b5] ld [wd11e],a ; TM names are separate from item names. ; BUG: This applies to all names instead of just items. cp HM_01 jp nc, GetMachineName ld a,[H_LOADEDROMBANK] push af push hl push bc push de ld a,[W_LISTTYPE] ;List3759_entrySelector dec a jr nz,.otherEntries ;1 = MON_NAMES call GetMonName ld hl,11 add hl,de ld e,l ld d,h jr .gotPtr .otherEntries ; $378d ;2-7 = OTHER ENTRIES ld a,[wPredefBank] ld [H_LOADEDROMBANK],a ld [$2000],a ld a,[W_LISTTYPE] ;VariousNames' entryID dec a add a ld d,0 ld e,a jr nc,.skip inc d .skip ; $37a0 ld hl,NamePointers add hl,de ld a,[hli] ld [$ff96],a ld a,[hl] ld [$ff95],a ld a,[$ff95] ld h,a ld a,[$ff96] ld l,a ld a,[wd0b5] ld b,a ld c,0 .nextName ld d,h ld e,l .nextChar ld a,[hli] cp a, "@" jr nz,.nextChar inc c ;entry counter ld a,b ;wanted entry cp c jr nz,.nextName ld h,d ld l,e ld de,wcd6d ld bc,$0014 call CopyData .gotPtr ; $37cd ld a,e ld [wcf8d],a ld a,d ld [wcf8e],a pop de pop bc pop hl pop af ld [H_LOADEDROMBANK],a ld [$2000],a ret GetItemPrice:: ; 37df (0:37df) ld a, [H_LOADEDROMBANK] push af ld a, [wListMenuID] ; wListMenuID cp MOVESLISTMENU ld a, BANK(ItemPrices) jr nz, .asm_37ed ld a, $f ; hardcoded Bank .asm_37ed ld [H_LOADEDROMBANK], a ld [$2000], a ld hl, wcf8f ld a, [hli] ld h, [hl] ld l, a ld a, [wcf91] cp HM_01 jr nc, .asm_3812 ld bc, $3 .asm_3802 add hl, bc dec a jr nz, .asm_3802 dec hl ld a, [hld] ld [$ff8d], a ld a, [hld] ld [H_DOWNARROWBLINKCNT2], a ; $ff8c ld a, [hl] ld [H_DOWNARROWBLINKCNT1], a ; $ff8b jr .asm_381c .asm_3812 ld a, Bank(GetMachinePrice) ld [H_LOADEDROMBANK], a ld [$2000], a call GetMachinePrice .asm_381c ld de, H_DOWNARROWBLINKCNT1 ; $ff8b pop af ld [H_LOADEDROMBANK], a ld [$2000], a ret ; copies a string from [de] to [wcf4b] CopyStringToCF4B:: ; 3826 (0:3826) ld hl, wcf4b ; fall through ; copies a string from [de] to [hl] CopyString:: ; 3829 (0:3829) ld a, [de] inc de ld [hli], a cp "@" jr nz, CopyString ret ; this function is used when lower button sensitivity is wanted (e.g. menus) ; OUTPUT: [hJoy5] = pressed buttons in usual format ; there are two flags that control its functionality, [hJoy6] and [hJoy7] ; there are esentially 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:: ; 3831 (0:3831) call Joypad ld a,[hJoy7] ; flag and a ; get all currently pressed buttons or only newly pressed buttons? ld a,[hJoyPressed] ; newly pressed buttons jr z,.storeButtonState ld a,[hJoyHeld] ; all currently pressed buttons .storeButtonState ld [hJoy5],a ld 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 ld [H_FRAMECOUNTER],a ret .noNewlyPressedButtons ld a,[H_FRAMECOUNTER] and a ; is the delay over? jr z,.delayOver .delayNotOver xor a ld [hJoy5],a ; report no buttons as pressed ret .delayOver ; if [hJoy6] = 0 and A or B is pressed, report no buttons as pressed ld a,[hJoyHeld] and A_BUTTON | B_BUTTON jr z,.setShortDelay ld a,[hJoy6] ; flag and a jr nz,.setShortDelay xor a ld [hJoy5],a .setShortDelay ld a,5 ; 1/12 of a second delay ld [H_FRAMECOUNTER],a ret WaitForTextScrollButtonPress:: ; 3865 (0:3865) ld a, [H_DOWNARROWBLINKCNT1] push af ld a, [H_DOWNARROWBLINKCNT2] push af xor a ld [H_DOWNARROWBLINKCNT1], a ld a, $6 ld [H_DOWNARROWBLINKCNT2], 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 Func_5a5f ld a, [hJoy5] and A_BUTTON | B_BUTTON jr z, .loop pop af ld [H_DOWNARROWBLINKCNT2], a pop af ld [H_DOWNARROWBLINKCNT1], a ret ; (unless in link battle) waits for A or B being pressed and outputs the scrolling sound effect ManualTextScroll:: ; 3898 (0:3898) ld a, [W_ISLINKBATTLE] cp $4 jr z, .inLinkBattle call WaitForTextScrollButtonPress ld a, (SFX_02_40 - SFX_Headers_02) / 3 jp PlaySound .inLinkBattle ld c, $41 jp DelayFrames ; function to do multiplication ; all values are big endian ; INPUT ; FF96-FF98 = multiplicand ; FF99 = multiplier ; OUTPUT ; FF95-FF98 = product Multiply:: ; 38ac (0:38ac) push hl push bc callab _Multiply pop bc pop hl ret ; function to do division ; all values are big endian ; INPUT ; FF95-FF98 = dividend ; FF99 = divisor ; b = number of bytes in the dividend (starting from FF95) ; OUTPUT ; FF95-FF98 = quotient ; FF99 = remainder Divide:: ; 38b9 (0:38b9) push hl push de push bc ld a,[H_LOADEDROMBANK] push af ld a,Bank(_Divide) ld [H_LOADEDROMBANK],a ld [$2000],a call _Divide pop af ld [H_LOADEDROMBANK],a ld [$2000],a pop bc pop de pop hl ret ; This function is used to wait a short period after printing a letter to the ; screen unless the player presses the A/B button or the delay is turned off ; through the [wd730] or [wd358] flags. PrintLetterDelay:: ; 38d3 (0:38d3) ld a,[wd730] bit 6,a ret nz ld a,[wd358] bit 1,a ret z push hl push de push bc ld a,[wd358] bit 0,a jr z,.waitOneFrame ld a,[W_OPTIONS] and $f ld [H_FRAMECOUNTER],a jr .checkButtons .waitOneFrame ld a,1 ld [H_FRAMECOUNTER],a .checkButtons call Joypad ld 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 ld a,[H_FRAMECOUNTER] and a jr nz,.checkButtons .done pop bc pop de pop hl ret ; Copies [hl, bc) to [de, bc - hl). ; In other words, the source data is from hl up to but not including bc, ; and the destination is de. CopyDataUntil:: ; 3913 (0:3913) ld a,[hli] ld [de],a inc de ld a,h cp b jr nz,CopyDataUntil ld a,l cp c jr nz,CopyDataUntil ret ; Function to remove a pokemon from the party or the current box. ; wWhichPokemon determines the pokemon. ; [wcf95] == 0 specifies the party. ; [wcf95] != 0 specifies the current box. RemovePokemon:: ; 391f (0:391f) ld hl, _RemovePokemon ld b, BANK(_RemovePokemon) jp Bankswitch AddPartyMon:: ; 3927 (0:3927) push hl push de push bc callba _AddPartyMon pop bc pop de pop hl ret ; calculates all 5 stats of current mon and writes them to [de] CalcStats:: ; 3936 (0:3936) ld c, $0 .statsLoop inc c call CalcStat ld a, [H_MULTIPLICAND+1] ld [de], a inc de ld a, [H_MULTIPLICAND+2] ld [de], a inc de ld a, c cp $5 jr nz, .statsLoop ret ; calculates stat c of current mon ; c: stat to calc (HP=1,Atk=2,Def=3,Spd=4,Spc=5) ; b: consider stat exp? ; hl: base ptr to stat exp values ([hl + 2*c - 1] and [hl + 2*c]) CalcStat:: ; 394a (0:394a) push hl push de push bc ld a, b ld d, a push hl ld hl, W_MONHEADER ld b, $0 add hl, bc ld a, [hl] ; read base value of stat ld e, a pop hl push hl sla c ld a, d and a jr z, .statExpDone ; consider stat exp? add hl, bc ; skip to corresponding stat exp value .statExpLoop ; calculates ceil(Sqrt(stat exp)) in b xor a ld [H_MULTIPLICAND], a ld [H_MULTIPLICAND+1], a inc b ; increment current stat exp bonus ld a, b cp $ff jr z, .statExpDone ld [H_MULTIPLICAND+2], a ld [H_MULTIPLIER], a call Multiply ld a, [hld] ld d, a ld a, [$ff98] sub d ld a, [hli] ld d, a ld a, [$ff97] sbc d ; test if (current stat exp bonus)^2 < stat exp jr c, .statExpLoop .statExpDone srl c pop hl push bc ld bc, $b ; skip to stat IV values add hl, bc pop bc ld a, c cp $2 jr z, .getAttackIV cp $3 jr z, .getDefenseIV cp $4 jr z, .getSpeedIV cp $5 jr z, .getSpecialIV .getHpIV push bc ld a, [hl] ; Atk IV swap a and $1 sla a sla a sla a ld b, a ld a, [hli] ; Def IV and $1 sla a sla a add b ld b, a ld a, [hl] ; Spd IV swap a and $1 sla a add b ld b, a ld a, [hl] ; Spc IV and $1 add b ; HP IV: LSB of the other 4 IVs pop bc jr .calcStatFromIV .getAttackIV ld a, [hl] swap a and $f jr .calcStatFromIV .getDefenseIV ld a, [hl] and $f jr .calcStatFromIV .getSpeedIV inc hl ld a, [hl] swap a and $f jr .calcStatFromIV .getSpecialIV inc hl ld a, [hl] and $f .calcStatFromIV ld d, $0 add e ld e, a jr nc, .noCarry inc d ; de = Base + IV .noCarry sla e rl d ; de = (Base + IV) * 2 srl b srl b ; b = ceil(Sqrt(stat exp)) / 4 ld a, b add e jr nc, .noCarry2 inc d ; da = (Base + IV) * 2 + ceil(Sqrt(stat exp)) / 4 .noCarry2 ld [H_MULTIPLICAND+2], a ld a, d ld [H_MULTIPLICAND+1], a xor a ld [H_MULTIPLICAND], a ld a, [W_CURENEMYLVL] ; W_CURENEMYLVL ld [H_MULTIPLIER], a call Multiply ; ((Base + IV) * 2 + ceil(Sqrt(stat exp)) / 4) * Level ld a, [H_MULTIPLICAND] ld [H_DIVIDEND], a ld a, [H_MULTIPLICAND+1] ld [H_DIVIDEND+1], a ld a, [H_MULTIPLICAND+2] ld [H_DIVIDEND+2], a ld a, $64 ld [H_DIVISOR], a ld a, $3 ld b, a call Divide ; (((Base + IV) * 2 + ceil(Sqrt(stat exp)) / 4) * Level) / 100 ld a, c cp $1 ld a, $5 jr nz, .notHPStat ld a, [W_CURENEMYLVL] ; W_CURENEMYLVL ld b, a ld a, [H_MULTIPLICAND+2] add b ld [H_MULTIPLICAND+2], a jr nc, .noCarry3 ld a, [H_MULTIPLICAND+1] inc a ld [H_MULTIPLICAND+1], a ; HP: (((Base + IV) * 2 + ceil(Sqrt(stat exp)) / 4) * Level) / 100 + Level .noCarry3 ld a, $a .notHPStat ld b, a ld a, [H_MULTIPLICAND+2] add b ld [H_MULTIPLICAND+2], a jr nc, .noCarry4 ld a, [H_MULTIPLICAND+1] inc a ; non-HP: (((Base + IV) * 2 + ceil(Sqrt(stat exp)) / 4) * Level) / 100 + 5 ld [H_MULTIPLICAND+1], a ; HP: (((Base + IV) * 2 + ceil(Sqrt(stat exp)) / 4) * Level) / 100 + Level + 10 .noCarry4 ld a, [H_MULTIPLICAND+1] ; check for overflow (>999) cp $4 jr nc, .overflow cp $3 jr c, .noOverflow ld a, [H_MULTIPLICAND+2] cp $e8 jr c, .noOverflow .overflow ld a, $3 ; overflow: cap at 999 ld [H_MULTIPLICAND+1], a ld a, $e7 ld [H_MULTIPLICAND+2], a .noOverflow pop bc pop de pop hl ret AddEnemyMonToPlayerParty:: ; 3a53 (0:3a53) ld a, [H_LOADEDROMBANK] push af ld a, BANK(_AddEnemyMonToPlayerParty) ld [H_LOADEDROMBANK], a ld [$2000], a call _AddEnemyMonToPlayerParty pop bc ld a, b ld [H_LOADEDROMBANK], a ld [$2000], a ret Func_3a68:: ; 3a68 (0:3a68) ld a, [H_LOADEDROMBANK] push af ld a, BANK(Func_f51e) ld [H_LOADEDROMBANK], a ld [$2000], a call Func_f51e pop bc ld a, b ld [H_LOADEDROMBANK], a ld [$2000], a ret ; skips a text entries, each of size $b (like trainer name, OT name, rival name, ...) ; hl: base pointer, will be incremented by $b * a SkipFixedLengthTextEntries:: ; 3a7d (0:3a7d) and a ret z ld bc, $b .skipLoop add hl, bc dec a jr nz, .skipLoop ret AddNTimes:: ; 3a87 (0:3a87) ; add bc to hl a times and a ret z .loop add hl,bc dec a jr nz,.loop ret ; Compare strings, c bytes in length, at de and hl. ; Often used to compare big endian numbers in battle calculations. StringCmp:: ; 3a8e (0:3a8e) ld a,[de] cp [hl] ret nz inc de inc hl dec c jr nz,StringCmp ret ; INPUT: ; a = oam block index (each block is 4 oam entries) ; b = Y coordinate of upper left corner of sprite ; c = X coordinate of upper left corner of sprite ; de = base address of 4 tile number and attribute pairs WriteOAMBlock:: ; 3a97 (0:3a97) ld h,wOAMBuffer / $100 swap a ; multiply by 16 ld l,a call .writeOneEntry ; upper left push bc ld a,8 add c ld c,a call .writeOneEntry ; upper right pop bc ld a,8 add b ld b,a call .writeOneEntry ; lower left ld a,8 add c ld c,a ; lower right .writeOneEntry ld [hl],b ; Y coordinate inc hl ld [hl],c ; X coordinate inc hl ld a,[de] ; tile number inc de ld [hli],a ld a,[de] ; attribute inc de ld [hli],a ret HandleMenuInput:: ; 3abe (0:3abe) xor a ld [wd09b],a HandleMenuInputPokemonSelection:: ; 3ac2 (0:3ac2) ld a,[H_DOWNARROWBLINKCNT1] push af ld a,[H_DOWNARROWBLINKCNT2] push af ; save existing values on stack xor a ld [H_DOWNARROWBLINKCNT1],a ; blinking down arrow timing value 1 ld a,$06 ld [H_DOWNARROWBLINKCNT2],a ; blinking down arrow timing value 2 .loop1 xor a ld [W_SUBANIMTRANSFORM],a ; counter for pokemon shaking animation call PlaceMenuCursor call Delay3 .loop2 push hl ld a,[wd09b] and a ; is it a pokemon selection menu? jr z,.getJoypadState callba AnimatePartyMon ; shake mini sprite of selected pokemon .getJoypadState pop hl call JoypadLowSensitivity ld 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 ld [H_DOWNARROWBLINKCNT2],a pop af ld [H_DOWNARROWBLINKCNT1],a ; restore previous values xor a ld [wMenuWrappingEnabled],a ; disable menu wrapping ret .keyPressed xor a ld [wcc4b],a ld 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 ld 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_02_40 - SFX_Headers_02) / 3 call PlaySound ; play sound .skipPlayingSound pop af ld [H_DOWNARROWBLINKCNT2],a pop af ld [H_DOWNARROWBLINKCNT1],a ; restore previous values xor a ld [wMenuWrappingEnabled],a ; disable menu wrapping ld a,[hJoy5] ret .noWrappingAround ld a,[wcc37] and a ; should we return if the user tried to go past the top or bottom? jr z,.checkOtherKeys jr .checkIfAButtonOrBButtonPressed PlaceMenuCursor:: ; 3b7c (0:3b7c) ld a,[wTopMenuItemY] and a ; is the y coordinate 0? jr z,.adjustForXCoord ld hl,wTileMap ld bc,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 ld a,[$fff6] bit 1,a ; is the menu double spaced? jr z,.doubleSpaced1 ld bc,20 jr .getOldMenuItemScreenPosition .doubleSpaced1 ld bc,40 .getOldMenuItemScreenPosition pop af .oldMenuItemLoop add hl,bc dec a jr nz,.oldMenuItemLoop .checkForArrow1 ld a,[hl] cp a,"▶" ; was an arrow next to the previously selected menu item? jr nz,.skipClearingArrow .clearArrow ld a,[wTileBehindCursor] ld [hl],a .skipClearingArrow pop hl ld a,[wCurrentMenuItem] and a jr z,.checkForArrow2 push af ld a,[$fff6] bit 1,a ; is the menu double spaced? jr z,.doubleSpaced2 ld bc,20 jr .getCurrentMenuItemScreenPosition .doubleSpaced2 ld bc,40 .getCurrentMenuItemScreenPosition pop af .currentMenuItemLoop add hl,bc dec a jr nz,.currentMenuItemLoop .checkForArrow2 ld a,[hl] cp a,"▶" ; has the right arrow already been placed? jr z,.skipSavingTile ; if so, don't lose the saved tile ld [wTileBehindCursor],a ; save tile before overwriting with right arrow .skipSavingTile ld a,"▶" ; place right arrow ld [hl],a ld a,l ld [wMenuCursorLocation],a ld a,h ld [wMenuCursorLocation + 1],a ld a,[wCurrentMenuItem] ld [wLastMenuItem],a ret ; This is used to mark a menu cursor other than the one currently being ; manipulated. In the case of submenus, this is used to show the location of ; the menu cursor in the parent menu. In the case of swapping items in list, ; this is used to mark the item that was first chosen to be swapped. PlaceUnfilledArrowMenuCursor:: ; 3bec (0:3bec) ld b,a ld a,[wMenuCursorLocation] ld l,a ld a,[wMenuCursorLocation + 1] ld h,a ld [hl],$ec ; outline of right arrow ld a,b ret ; Replaces the menu cursor with a blank space. EraseMenuCursor:: ; 3bf9 (0:3bf9) ld a,[wMenuCursorLocation] ld l,a ld a,[wMenuCursorLocation + 1] ld h,a ld [hl]," " ret ; This toggles a blinking down arrow at hl on and off after a delay has passed. ; This is often called even when no blinking is occurring. ; The reason is that most functions that call this initialize H_DOWNARROWBLINKCNT1 to 0. ; The effect is that if the tile at hl is initialized with a down arrow, ; this function will toggle that down arrow on and off, but if the tile isn't ; initliazed with a down arrow, this function does nothing. ; That allows this to be called without worrying about if a down arrow should ; be blinking. HandleDownArrowBlinkTiming:: ; 3c04 (0:3c04) ld a,[hl] ld b,a ld a,$ee ; down arrow cp b jr nz,.downArrowOff .downArrowOn ld a,[H_DOWNARROWBLINKCNT1] dec a ld [H_DOWNARROWBLINKCNT1],a ret nz ld a,[H_DOWNARROWBLINKCNT2] dec a ld [H_DOWNARROWBLINKCNT2],a ret nz ld a," " ld [hl],a ld a,$ff ld [H_DOWNARROWBLINKCNT1],a ld a,$06 ld [H_DOWNARROWBLINKCNT2],a ret .downArrowOff ld a,[H_DOWNARROWBLINKCNT1] and a ret z dec a ld [H_DOWNARROWBLINKCNT1],a ret nz dec a ld [H_DOWNARROWBLINKCNT1],a ld a,[H_DOWNARROWBLINKCNT2] dec a ld [H_DOWNARROWBLINKCNT2],a ret nz ld a,$06 ld [H_DOWNARROWBLINKCNT2],a ld a,$ee ; down arrow ld [hl],a ret ; The following code either enables or disables the automatic drawing of ; text boxes by DisplayTextID. Both functions cause DisplayTextID to wait ; for a button press after displaying text (unless [wcc47] is set). EnableAutoTextBoxDrawing:: ; 3c3c (0:3c3c) xor a jr AutoTextBoxDrawingCommon DisableAutoTextBoxDrawing:: ; 3c3f (0:3c3f) ld a,$01 AutoTextBoxDrawingCommon:: ; 3c41 (0:3c41) ld [wAutoTextBoxDrawingControl],a xor a ld [wDoNotWaitForButtonPressAfterDisplayingText],a ; make DisplayTextID wait for button press ret PrintText:: ; 3c49 (0:3c49) ; Print text hl at (1, 14). push hl ld a,1 ld [wd125],a call DisplayTextBoxID call UpdateSprites call Delay3 pop hl Func_3c59:: ; 3c59 (0:3c59) bcCoord 1, 14 jp TextCommandProcessor PrintNumber:: ; 3c5f ; Print the c-digit, b-byte value at de. ; Allows 2 to 7 digits. For 1-digit numbers, add ; the value to char "0" instead of calling PrintNumber. ; Flags LEADING_ZEROES and LEFT_ALIGN can be given ; in bits 7 and 6 of b respectively. LEADING_ZEROES EQU 7 LEFT_ALIGN EQU 6 push bc xor a ld [H_PASTLEADINGZEROES], a ld [H_NUMTOPRINT], a ld [H_NUMTOPRINT + 1], a ld a, b and $f cp 1 jr z, .byte cp 2 jr z, .word .long ld a, [de] ld [H_NUMTOPRINT], a inc de ld a, [de] ld [H_NUMTOPRINT + 1], a inc de ld a, [de] ld [H_NUMTOPRINT + 2], a jr .start .word ld a, [de] ld [H_NUMTOPRINT + 1], a inc de ld a, [de] ld [H_NUMTOPRINT + 2], a jr .start .byte ld a, [de] ld [H_NUMTOPRINT + 2], a .start push de ld d, b ld a, c ld b, a xor a ld c, a ld a, b cp 2 jr z, .tens cp 3 jr z, .hundreds cp 4 jr z, .thousands cp 5 jr z, .ten_thousands cp 6 jr z, .hundred_thousands print_digit: macro if (\1) / $10000 ld a, \1 / $10000 % $100 else xor a endc ld [H_POWEROFTEN + 0], a if (\1) / $100 ld a, \1 / $100 % $100 else xor a endc ld [H_POWEROFTEN + 1], a ld a, \1 / $1 % $100 ld [H_POWEROFTEN + 2], a call .PrintDigit call .NextDigit endm .millions print_digit 1000000 .hundred_thousands print_digit 100000 .ten_thousands print_digit 10000 .thousands print_digit 1000 .hundreds print_digit 100 .tens ld c, 0 ld a, [H_NUMTOPRINT + 2] .mod cp 10 jr c, .ok sub 10 inc c jr .mod .ok ld b, a ld a, [H_PASTLEADINGZEROES] or c ld [H_PASTLEADINGZEROES], a jr nz, .past call .PrintLeadingZero jr .next .past ld a, "0" add c ld [hl], a .next call .NextDigit .ones ld a, "0" add b ld [hli], a pop de dec de pop bc ret .PrintDigit: ; Divide by the current decimal place. ; Print the quotient, and keep the modulus. ld c, 0 .loop ld a, [H_POWEROFTEN] ld b, a ld a, [H_NUMTOPRINT] ld [H_SAVEDNUMTOPRINT], a cp b jr c, .underflow0 sub b ld [H_NUMTOPRINT], a ld a, [H_POWEROFTEN + 1] ld b, a ld a, [H_NUMTOPRINT + 1] ld [H_SAVEDNUMTOPRINT + 1], a cp b jr nc, .noborrow1 ld a, [H_NUMTOPRINT] or 0 jr z, .underflow1 dec a ld [H_NUMTOPRINT], a ld a, [H_NUMTOPRINT + 1] .noborrow1 sub b ld [H_NUMTOPRINT + 1], a ld a, [H_POWEROFTEN + 2] ld b, a ld a, [H_NUMTOPRINT + 2] ld [H_SAVEDNUMTOPRINT + 2], a cp b jr nc, .noborrow2 ld a, [H_NUMTOPRINT + 1] and a jr nz, .borrowed ld a, [H_NUMTOPRINT] and a jr z, .underflow2 dec a ld [H_NUMTOPRINT], a xor a .borrowed dec a ld [H_NUMTOPRINT + 1], a ld a, [H_NUMTOPRINT + 2] .noborrow2 sub b ld [H_NUMTOPRINT + 2], a inc c jr .loop .underflow2 ld a, [H_SAVEDNUMTOPRINT + 1] ld [H_NUMTOPRINT + 1], a .underflow1 ld a, [H_SAVEDNUMTOPRINT] ld [H_NUMTOPRINT], a .underflow0 ld a, [H_PASTLEADINGZEROES] or c jr z, .PrintLeadingZero ld a, "0" add c ld [hl], a ld [H_PASTLEADINGZEROES], a ret .PrintLeadingZero: bit LEADING_ZEROES, d ret z ld [hl], "0" ret .NextDigit: ; Increment unless the number is left-aligned, ; leading zeroes are not printed, and no digits ; have been printed yet. bit LEADING_ZEROES, d jr nz, .inc bit LEFT_ALIGN, d jr z, .inc ld a, [H_PASTLEADINGZEROES] and a ret z .inc inc hl ret CallFunctionInTable:: JumpTable:: ; 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:: ; 3dbe (0:3dbe) call ClearSprites ld a, $1 ld [wUpdateSpritesEnabled], a call ReloadMapSpriteTilePatterns call LoadScreenTilesFromBuffer2 call LoadTextBoxTilePatterns call GoPAL_SET_CF1C jr Delay3 GBPalWhiteOutWithDelay3:: call GBPalWhiteOut Delay3:: ; The bg map is updated each frame in thirds. ; Wait three frames to let the bg map fully update. ld c, 3 jp DelayFrames GBPalNormal:: ; Reset BGP and OBP0. ld a, %11100100 ; 3210 ld [rBGP], a ld a, %11010000 ; 3100 ld [rOBP0], a ret GBPalWhiteOut:: ; White out all palettes. xor a ld [rBGP],a ld [rOBP0],a ld [rOBP1],a ret GoPAL_SET_CF1C:: ; 3ded (0:3ded) ld b,$ff GoPAL_SET:: ; 3def (0:3def) ld a,[wOnSGB] and a ret z predef_jump Func_71ddf 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:: ; 3e08 (0:3e08) ld hl, wcfc4 ld a, [hl] push af res 0, [hl] push hl xor a ld [W_SPRITESETID], a call DisableLCD callba InitMapSprites call EnableLCD pop hl pop af ld [hl], a call LoadPlayerSpriteGraphics call LoadFontTilePatterns jp UpdateSprites GiveItem:: ; Give player quantity c of item b, ; and copy the item's name to wcf4b. ; Return carry on success. ld a, b ld [wd11e], a ld [wcf91], a ld a, c ld [wcf96], 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 [W_CURENEMYLVL], a xor a ld [wcc49], a ld b, BANK(_GivePokemon) ld hl, _GivePokemon jp Bankswitch Random:: ; Return a random number in a. ; For battles, use BattleRandom. push hl push de push bc callba Random_ ld a, [hRandomAdd] pop bc pop de pop hl ret INCLUDE "home/predef.asm" Func_3ead:: ; 3ead (0:3ead) ld b, BANK(CinnabarGymQuiz_1eb0a) ld hl, CinnabarGymQuiz_1eb0a jp Bankswitch CheckForHiddenObjectOrBookshelfOrCardKeyDoor:: ; 3eb5 (0:3eb5) ld a, [H_LOADEDROMBANK] push af ld a, [hJoyHeld] bit 0, a ; A button jr z, .nothingFound ; A button is pressed ld a, Bank(CheckForHiddenObject) ld [MBC1RomBank], a ld [H_LOADEDROMBANK], a call CheckForHiddenObject ld a, [$ffee] and a jr nz, .hiddenObjectNotFound ld a, [wHiddenObjectFunctionRomBank] ld [MBC1RomBank], a ld [H_LOADEDROMBANK], a ld de, .returnAddress push de jp [hl] .returnAddress xor a jr .done .hiddenObjectNotFound callba PrintBookshelfText ld a, [$ffdb] and a jr z, .done .nothingFound ld a, $ff .done ld [$ffeb], a pop af ld [MBC1RomBank], a ld [H_LOADEDROMBANK], a ret PrintPredefTextID:: ; 3ef5 (0:3ef5) ld [H_DOWNARROWBLINKCNT2], a ; $ff8c ld hl, TextPredefs call SetMapTextPointer ld hl, wcf11 set 0, [hl] call DisplayTextID RestoreMapTextPointer:: ; 3f05 (0:3f05) ld hl, W_MAPTEXTPTR ld a, [$ffec] ld [hli], a ld a, [$ffec + 1] ld [hl], a ret SetMapTextPointer:: ; 3f0f (0:3f0f) ld a, [W_MAPTEXTPTR] ld [$ffec], a ld a, [W_MAPTEXTPTR + 1] ld [$ffec + 1], a ld a, l ld [W_MAPTEXTPTR], a ld a, h ld [W_MAPTEXTPTR + 1], a ret TextPredefs:: add_tx_pre CardKeySuccessText ; 01 add_tx_pre CardKeyFailText ; 02 add_tx_pre RedBedroomPC ; 03 add_tx_pre RedBedroomSNESText ; 04 add_tx_pre PushStartText ; 05 add_tx_pre SaveOptionText ; 06 add_tx_pre StrengthsAndWeaknessesText ; 07 add_tx_pre OakLabEmailText ; 08 add_tx_pre AerodactylFossilText ; 09 add_tx_pre Route15UpstairsBinocularsText ; 0A add_tx_pre KabutopsFossilText ; 0B add_tx_pre GymStatueText1 ; 0C add_tx_pre GymStatueText2 ; 0D add_tx_pre BookcaseText ; 0E add_tx_pre ViridianCityPokecenterBenchGuyText ; 0F add_tx_pre PewterCityPokecenterBenchGuyText ; 10 add_tx_pre CeruleanCityPokecenterBenchGuyText ; 11 add_tx_pre LavenderCityPokecenterBenchGuyText ; 12 add_tx_pre VermilionCityPokecenterBenchGuyText ; 13 add_tx_pre CeladonCityPokecenterBenchGuyText ; 14 add_tx_pre CeladonCityHotelText ; 15 add_tx_pre FuchsiaCityPokecenterBenchGuyText ; 16 add_tx_pre CinnabarIslandPokecenterBenchGuyText ; 17 add_tx_pre SaffronCityPokecenterBenchGuyText ; 18 add_tx_pre MtMoonPokecenterBenchGuyText ; 19 add_tx_pre RockTunnelPokecenterBenchGuyText ; 1A add_tx_pre UnusedBenchGuyText1 ; 1B add_tx_pre UnusedBenchGuyText2 ; 1C add_tx_pre UnusedBenchGuyText3 ; 1D add_tx_pre TerminatorText_62508 ; 1E add_tx_pre PredefText1f ; 1F add_tx_pre ViridianSchoolNotebook ; 20 add_tx_pre ViridianSchoolBlackboard ; 21 add_tx_pre JustAMomentText ; 22 add_tx_pre PredefText23 ; 23 add_tx_pre FoundHiddenItemText ; 24 add_tx_pre HiddenItemBagFullText ; 25 add_tx_pre VermilionGymTrashText ; 26 add_tx_pre IndigoPlateauHQText ; 27 add_tx_pre GameCornerOutOfOrderText ; 28 add_tx_pre GameCornerOutToLunchText ; 29 add_tx_pre GameCornerSomeonesKeysText ; 2A add_tx_pre FoundHiddenCoinsText ; 2B add_tx_pre DroppedHiddenCoinsText ; 2C add_tx_pre BillsHouseMonitorText ; 2D add_tx_pre BillsHouseInitiatedText ; 2E add_tx_pre BillsHousePokemonList ; 2F add_tx_pre MagazinesText ; 30 add_tx_pre CinnabarGymQuiz ; 31 add_tx_pre GameCornerNoCoinsText ; 32 add_tx_pre GameCornerCoinCaseText ; 33 add_tx_pre LinkCableHelp ; 34 add_tx_pre TMNotebook ; 35 add_tx_pre FightingDojoText ; 36 add_tx_pre FightingDojoText_52a10 ; 37 add_tx_pre FightingDojoText_52a1d ; 38 add_tx_pre NewBicycleText ; 39 add_tx_pre IndigoPlateauStatues ; 3A add_tx_pre VermilionGymTrashSuccesText1 ; 3B add_tx_pre VermilionGymTrashSuccesText2 ; 3C add_tx_pre VermilionGymTrashSuccesText3 ; 3D add_tx_pre VermilionGymTrashFailText ; 3E add_tx_pre TownMapText ; 3F add_tx_pre BookOrSculptureText ; 40 add_tx_pre ElevatorText ; 41 add_tx_pre PokemonStuffText ; 42