diff options
| author | dannye <33dannye@gmail.com> | 2020-11-04 00:06:44 -0600 |
|---|---|---|
| committer | dannye <33dannye@gmail.com> | 2020-11-04 00:06:44 -0600 |
| commit | 5647ca687b92954dcf37a6ea6bfbc9a341c32de4 (patch) | |
| tree | dde1937a1bfdb3a835f4155e1c2eb8f1aaf86f63 /home | |
| parent | Merge pull request #55 from Deokishisu/patch-1 (diff) | |
| download | pokeyellow-5647ca687b92954dcf37a6ea6bfbc9a341c32de4.tar.gz pokeyellow-5647ca687b92954dcf37a6ea6bfbc9a341c32de4.tar.xz pokeyellow-5647ca687b92954dcf37a6ea6bfbc9a341c32de4.zip | |
Sync with pokered
Diffstat (limited to 'home')
66 files changed, 5338 insertions, 743 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/audio.asm b/home/audio.asm index fa7020ee..bf2537a7 100644 --- a/home/audio.asm +++ b/home/audio.asm @@ -93,7 +93,7 @@ UpdateMusicCTimes:: .loop push bc push hl - callba Audio1_UpdateMusic + farcall Audio1_UpdateMusic pop hl pop bc dec c @@ -144,11 +144,11 @@ Func_2223:: ld [wChannelSoundIDs + Ch6], a ld [wChannelSoundIDs + Ch7], a ld [wChannelSoundIDs + Ch8], a - ld [rNR10], a + ldh [rNR10], a ret StopAllMusic:: - ld a, $FF + ld a, SFX_STOP_ALL_MUSIC ld [wNewSoundID], a ; plays music specified by a. If value is $ff, music is stopped PlaySound:: @@ -200,7 +200,7 @@ PlaySound:: ret GetNextMusicByte:: - ld a, [H_LOADEDROMBANK] + ldh a, [hLoadedROMBank] push af ld a, [wAudioROMBank] call BankswitchCommon @@ -256,7 +256,7 @@ StopAllAudio:: ret DetermineAudioFunction:: - ld a, [H_LOADEDROMBANK] + ldh a, [hLoadedROMBank] push af ld a, [wAudioROMBank] call BankswitchCommon diff --git a/home/bankswitch.asm b/home/bankswitch.asm new file mode 100644 index 00000000..3e85e955 --- /dev/null +++ b/home/bankswitch.asm @@ -0,0 +1,15 @@ +BankswitchHome:: +; switches to bank # in a +; Only use this when in the home bank! + ld [wBankswitchHomeTemp], a + ldh a, [hLoadedROMBank] + ld [wBankswitchHomeSavedROMBank], a + ld a, [wBankswitchHomeTemp] + call BankswitchCommon + ret + +BankswitchBack:: +; returns from BankswitchHome + ld a, [wBankswitchHomeSavedROMBank] + call BankswitchCommon + ret diff --git a/home/clear_sprites.asm b/home/clear_sprites.asm new file mode 100644 index 00000000..34f4b4ec --- /dev/null +++ b/home/clear_sprites.asm @@ -0,0 +1,21 @@ +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 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.asm b/home/copy.asm index d2228405..b239d951 100644 --- a/home/copy.asm +++ b/home/copy.asm @@ -1,7 +1,7 @@ FarCopyData:: ; Copy bc bytes from a:hl to de. ld [wFarCopyDataSavedROMBank], a - ld a, [H_LOADEDROMBANK] + ldh a, [hLoadedROMBank] push af ld a, [wFarCopyDataSavedROMBank] call BankswitchCommon @@ -12,17 +12,17 @@ FarCopyData:: CopyData:: ; Copy bc bytes from hl to de. - ld a,b + ld a, b and a jr z, .copybytes - ld a,c + ld a, c and a ; is lower byte 0 jr z, .loop inc b ; if not, increment b as there are <$100 bytes to copy .loop call .copybytes dec b - jr nz,.loop + jr nz, .loop ret .copybytes @@ -34,41 +34,41 @@ CopyData:: ret CopyVideoDataAlternate:: - ld a, [rLCDC] - bit 7,a ; LCD enabled? + ldh a, [rLCDC] + bit 7, a ; LCD enabled? jp nz, CopyVideoData ; if yes, then copy video data push hl - ld h,d - ld l,e + ld h, d + ld l, e pop de - ld a,b ; save bank + ld a, b ; save bank push af swap c - ld a,$f + ld a, $f and c - ld b,a - ld a,$f0 + ld b, a + ld a, $f0 and c - ld c,a + ld c, a pop af jp FarCopyData CopyVideoDataDoubleAlternate:: - ld a, [rLCDC] - bit 7,a ; LCD enabled? + ldh a, [rLCDC] + bit 7, a ; LCD enabled? jp nz, CopyVideoDataDouble ; if yes, then copy video data push de - ld d,h - ld e,l - ld a,b + ld d, h + ld e, l + ld a, b push af ; save bank to switch to - ld h,$0 - ld l,c - add hl,hl ; get raw length of bytes to copy - add hl,hl - add hl,hl - ld b,h - ld c,l + ld h, $0 + ld l, c + add hl, hl ; get raw length of bytes to copy + add hl, hl + add hl, hl + ld b, h + ld c, l pop af pop hl jp FarCopyDataDouble diff --git a/home/copy2.asm b/home/copy2.asm index c14112bb..bff26570 100644 --- a/home/copy2.asm +++ b/home/copy2.asm @@ -2,7 +2,7 @@ FarCopyDataDouble:: ; Expand bc bytes of 1bpp image data ; from a:de to 2bpp data at hl. ld [wFarCopyDataSavedROMBank], a - ld a, [H_LOADEDROMBANK] + ldh a, [hLoadedROMBank] push af ld a, [wFarCopyDataSavedROMBank] call BankswitchCommon @@ -38,26 +38,26 @@ CopyVideoData:: ; tiles from b:de to hl, 8 tiles at a time. ; This takes c/8 frames. - ld a, [H_AUTOBGTRANSFERENABLED] + ldh a, [hAutoBGTransferEnabled] push af xor a ; disable auto-transfer while copying - ld [H_AUTOBGTRANSFERENABLED], a + ldh [hAutoBGTransferEnabled], a - ld a, [H_LOADEDROMBANK] + ldh a, [hLoadedROMBank] push af ld a, b call BankswitchCommon ld a, e - ld [H_VBCOPYSRC], a + ldh [hVBlankCopySource], a ld a, d - ld [H_VBCOPYSRC + 1], a + ldh [hVBlankCopySource + 1], a ld a, l - ld [H_VBCOPYDEST], a + ldh [hVBlankCopyDest], a ld a, h - ld [H_VBCOPYDEST + 1], a + ldh [hVBlankCopyDest + 1], a .loop ld a, c @@ -65,17 +65,17 @@ CopyVideoData:: jr nc, .keepgoing .done - ld [H_VBCOPYSIZE], a + ldh [hVBlankCopySize], a call DelayFrame pop af call BankswitchCommon pop af - ld [H_AUTOBGTRANSFERENABLED], a + ldh [hAutoBGTransferEnabled], a ret .keepgoing ld a, 8 - ld [H_VBCOPYSIZE], a + ldh [hVBlankCopySize], a call DelayFrame ld a, c sub 8 @@ -86,25 +86,25 @@ CopyVideoDataDouble:: ; Wait for the next VBlank, then copy c 1bpp ; tiles from b:de to hl, 8 tiles at a time. ; This takes c/8 frames. - ld a, [H_AUTOBGTRANSFERENABLED] + ldh a, [hAutoBGTransferEnabled] push af xor a ; disable auto-transfer while copying - ld [H_AUTOBGTRANSFERENABLED], a - ld a, [H_LOADEDROMBANK] + ldh [hAutoBGTransferEnabled], a + ldh a, [hLoadedROMBank] push af ld a, b call BankswitchCommon ld a, e - ld [H_VBCOPYDOUBLESRC], a + ldh [hVBlankCopyDoubleSource], a ld a, d - ld [H_VBCOPYDOUBLESRC + 1], a + ldh [hVBlankCopyDoubleSource + 1], a ld a, l - ld [H_VBCOPYDOUBLEDEST], a + ldh [hVBlankCopyDoubleDest], a ld a, h - ld [H_VBCOPYDOUBLEDEST + 1], a + ldh [hVBlankCopyDoubleDest + 1], a .loop ld a, c @@ -112,17 +112,17 @@ CopyVideoDataDouble:: jr nc, .keepgoing .done - ld [H_VBCOPYDOUBLESIZE], a + ldh [hVBlankCopyDoubleSize], a call DelayFrame pop af call BankswitchCommon pop af - ld [H_AUTOBGTRANSFERENABLED], a + ldh [hAutoBGTransferEnabled], a ret .keepgoing ld a, 8 - ld [H_VBCOPYDOUBLESIZE], a + ldh [hVBlankCopyDoubleSize], a call DelayFrame ld a, c sub 8 @@ -131,10 +131,10 @@ CopyVideoDataDouble:: FillMemory:: push af - ld a,b + ld a, b and a jr z, .eightbitcopyamount - ld a,c + ld a, c and a jr z, .mulitpleof0x100 .eightbitcopyamount @@ -142,7 +142,7 @@ FillMemory:: .mulitpleof0x100 pop af .loop - ld [hli],a + ld [hli], a dec c jr nz, .loop dec b @@ -154,7 +154,7 @@ GetFarByte:: ; and return it in a push bc ld b, a - ld a, [H_LOADEDROMBANK] + ldh a, [hLoadedROMBank] push af ld a, b call BankswitchCommon @@ -190,32 +190,32 @@ CopyScreenTileBufferToVRAM:: ld c, 6 ld hl, $600 * 0 - coord de, 0, 6 * 0 + decoord 0, 6 * 0 call .setup call DelayFrame ld hl, $600 * 1 - coord de, 0, 6 * 1 + decoord 0, 6 * 1 call .setup call DelayFrame ld hl, $600 * 2 - coord de, 0, 6 * 2 + decoord 0, 6 * 2 call .setup jp DelayFrame .setup ld a, d - ld [H_VBCOPYBGSRC+1], a + ldh [hVBlankCopyBGSource+1], a call GetRowColAddressBgMap ld a, l - ld [H_VBCOPYBGDEST], a + ldh [hVBlankCopyBGDest], a ld a, h - ld [H_VBCOPYBGDEST+1], a + ldh [hVBlankCopyBGDest+1], a ld a, c - ld [H_VBCOPYBGNUMROWS], a + ldh [hVBlankCopyBGNumRows], a ld a, e - ld [H_VBCOPYBGSRC], a + ldh [hVBlankCopyBGSource], a ret ClearScreen:: @@ -223,7 +223,7 @@ ClearScreen:: ; for the bg map to update. ld bc, 20 * 18 inc b - coord hl, 0, 0 + hlcoord 0, 0 ld a, " " .loop ld [hli], a 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..eba865c2 --- /dev/null +++ b/home/delay.asm @@ -0,0 +1,32 @@ +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] + and a + jr nz, .waitLoop + pop hl + ret diff --git a/home/fade.asm b/home/fade.asm index 1259f92b..f38878b1 100644 --- a/home/fade.asm +++ b/home/fade.asm @@ -11,11 +11,11 @@ LoadGBPal:: dec h .ok ld a, [hli] - ld [rBGP], a + ldh [rBGP], a ld a, [hli] - ld [rOBP0], a + ldh [rOBP0], a ld a, [hli] - ld [rOBP1], a + ldh [rOBP1], a call UpdateGBCPal_BGP call UpdateGBCPal_OBP0 call UpdateGBCPal_OBP1 @@ -32,11 +32,11 @@ GBFadeOutToWhite:: GBFadeIncCommon: ld a, [hli] - ld [rBGP], a + ldh [rBGP], a ld a, [hli] - ld [rOBP0], a + ldh [rOBP0], a ld a, [hli] - ld [rOBP1], a + ldh [rOBP1], a call UpdateGBCPal_BGP call UpdateGBCPal_OBP0 call UpdateGBCPal_OBP1 @@ -57,11 +57,11 @@ GBFadeInFromWhite:: GBFadeDecCommon: ld a, [hld] - ld [rOBP1], a + ldh [rOBP1], a ld a, [hld] - ld [rOBP0], a + ldh [rOBP0], a ld a, [hld] - ld [rBGP], a + ldh [rBGP], a call UpdateGBCPal_BGP call UpdateGBCPal_OBP0 call UpdateGBCPal_OBP1 diff --git a/home/fade_audio.asm b/home/fade_audio.asm new file mode 100644 index 00000000..f4e77c3f --- /dev/null +++ b/home/fade_audio.asm @@ -0,0 +1,46 @@ +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 + call StopAllMusic + 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/header.asm b/home/header.asm new file mode 100644 index 00000000..671f29b1 --- /dev/null +++ b/home/header.asm @@ -0,0 +1,81 @@ +; rst vectors (unused) + +SECTION "rst0", ROM0[$0000] + rst $38 + + ds $08 - @, 0 ; unused + +SECTION "rst8", ROM0[$0008] + rst $38 + + ds $10 - @, 0 ; unused + +SECTION "rst10", ROM0[$0010] + rst $38 + + ds $18 - @, 0 ; unused + +SECTION "rst18", ROM0[$0018] + rst $38 + + ds $20 - @, 0 ; unused + +SECTION "rst20", ROM0[$0020] + rst $38 + + ds $28 - @, 0 ; unused + +SECTION "rst28", ROM0[$0028] + rst $38 + + ds $30 - @, 0 ; unused + +SECTION "rst30", ROM0[$0030] + rst $38 + + ds $38 - @, 0 ; unused + +SECTION "rst38", ROM0[$0038] + rst $38 + + ds $40 - @, 0 ; unused + + +; Game Boy hardware interrupts + +SECTION "vblank", ROM0[$0040] + jp VBlank + + ds $48 - @, 0 ; unused + +SECTION "lcd", ROM0[$0048] + jp LCDC + + ds $50 - @, 0 ; unused + +SECTION "timer", ROM0[$0050] + jp Timer + + ds $58 - @, 0 ; unused + +SECTION "serial", ROM0[$0058] + jp Serial + + ds $60 - @, 0 ; unused + +SECTION "joypad", ROM0[$0060] + reti + + +SECTION "Header", ROM0[$0100] + +Start:: +; Nintendo requires all Game Boy ROMs to begin with a nop ($00) and a jp ($C3) +; to the starting address. + nop + jp _Start + +; The Game Boy cartridge header data is patched over by rgbfix. +; This makes sure it doesn't get used for anything else. + + ds $0150 - @ diff --git a/home/hidden_objects.asm b/home/hidden_objects.asm new file mode 100644 index 00000000..40c49c3e --- /dev/null +++ b/home/hidden_objects.asm @@ -0,0 +1,35 @@ +UpdateCinnabarGymGateTileBlocks:: + farcall UpdateCinnabarGymGateTileBlocks_ + ret + +CheckForHiddenObjectOrBookshelfOrCardKeyDoor:: + ldh a, [hLoadedROMBank] + push af + ldh a, [hJoyHeld] + bit 0, a ; A button + jr z, .nothingFound +; A button is pressed + callbs CheckForHiddenObject + ldh a, [hDidntFindAnyHiddenObject] + and a + jr nz, .hiddenObjectNotFound + xor a + ldh [hItemAlreadyFound], a + ld a, [wHiddenObjectFunctionRomBank] + call BankswitchCommon + call JumpToAddress + ldh a, [hItemAlreadyFound] + jr .done +.hiddenObjectNotFound + predef GetTileAndCoordsInFrontOfPlayer + farcall PrintBookshelfText + ldh a, [hFFDB] + and a + jr z, .done +.nothingFound + ld a, $ff +.done + ldh [hItemAlreadyFound], a + pop af + call BankswitchCommon + ret diff --git a/home/init.asm b/home/init.asm index a2f7f786..878f329a 100644 --- a/home/init.asm +++ b/home/init.asm @@ -21,28 +21,28 @@ rLCDC_DEFAULT EQU %11100011 di xor a - ld [rIF], a - ld [rIE], a - ld [rSCX], a - ld [rSCY], a - ld [rSB], a - ld [rSC], a - ld [rWX], a - ld [rWY], a - ld [rTMA], a - ld [rTAC], a - ld [rBGP], a - ld [rOBP0], a - ld [rOBP1], a + ldh [rIF], a + ldh [rIE], a + ldh [rSCX], a + ldh [rSCY], a + ldh [rSB], a + ldh [rSC], a + ldh [rWX], a + ldh [rWY], a + ldh [rTMA], a + ldh [rTAC], a + ldh [rBGP], a + ldh [rOBP0], a + ldh [rOBP1], a ld a, rLCDC_ENABLE_MASK - ld [rLCDC], a + ldh [rLCDC], a call DisableLCD ld sp, wStack - ld hl, $c000 ; start of WRAM - ld bc, $2000 ; size of WRAM + ld hl, WRAM0_Begin + ld bc, WRAM1_End - WRAM0_Begin .loop ld [hl], 0 inc hl @@ -53,46 +53,46 @@ rLCDC_DEFAULT EQU %11100011 call ClearVram - ld hl, $ff80 - ld bc, $fffe - $ff80 + ld hl, HRAM_Begin + ld bc, HRAM_End - HRAM_Begin - 1 call FillMemory call ClearSprites - ld a, Bank(WriteDMACodeToHRAM) - ld [H_LOADEDROMBANK], a + ld a, BANK(WriteDMACodeToHRAM) + ldh [hLoadedROMBank], a ld [MBC1RomBank], a call WriteDMACodeToHRAM xor a - ld [hTilesetType], a - ld [rSTAT], a - ld [hSCX], a - ld [hSCY], a - ld [rIF], a + ldh [hTilesetType], a + ldh [rSTAT], a + ldh [hSCX], a + ldh [hSCY], a + ldh [rIF], a ld [wc0f3], a ld [wc0f4], a ld a, 1 << VBLANK + 1 << TIMER + 1 << SERIAL - ld [rIE], a + ldh [rIE], a ld a, 144 ; move the window off-screen - ld [hWY], a - ld [rWY], a + ldh [hWY], a + ldh [rWY], a ld a, 7 - ld [rWX], a + ldh [rWX], a ld a, CONNECTION_NOT_ESTABLISHED - ld [hSerialConnectionStatus], a + ldh [hSerialConnectionStatus], a - ld h, vBGMap0 / $100 + ld h, HIGH(vBGMap0) call ClearBgMap - ld h, vBGMap1 / $100 + ld h, HIGH(vBGMap1) call ClearBgMap ld a, rLCDC_DEFAULT - ld [rLCDC], a + ldh [rLCDC], a ld a, 16 - ld [hSoftReset], a + ldh [hSoftReset], a call StopAllSounds ei @@ -103,9 +103,9 @@ rLCDC_DEFAULT EQU %11100011 ld [wAudioROMBank], a ld [wAudioSavedROMBank], a ld a, $9c - ld [H_AUTOBGTRANSFERDEST + 1], a + ldh [hAutoBGTransferDest + 1], a xor a - ld [H_AUTOBGTRANSFERDEST], a + ldh [hAutoBGTransferDest], a dec a ld [wUpdateSpritesEnabled], a @@ -116,13 +116,13 @@ rLCDC_DEFAULT EQU %11100011 call GBPalNormal call ClearSprites ld a, rLCDC_DEFAULT - ld [rLCDC], a + ldh [rLCDC], a jp SetDefaultNamesBeforeTitlescreen -ClearVram: - ld hl, $8000 - ld bc, $2000 +ClearVram:: + ld hl, VRAM_Begin + ld bc, VRAM_End - VRAM_Begin xor a jp FillMemory diff --git a/home/inventory.asm b/home/inventory.asm new file mode 100644 index 00000000..1100666f --- /dev/null +++ b/home/inventory.asm @@ -0,0 +1,38 @@ +; 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:: + homecall RemoveItemFromInventory_ + 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 + homecall_sf AddItemToInventory_ + 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..d8c2068e --- /dev/null +++ b/home/item_price.asm @@ -0,0 +1,39 @@ +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 + call BankswitchCommon + 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 + callbs GetMachinePrice +.done + ld de, hItemPrice + pop af + call BankswitchCommon + ret diff --git a/home/joypad.asm b/home/joypad.asm new file mode 100644 index 00000000..15f10fe2 --- /dev/null +++ b/home/joypad.asm @@ -0,0 +1,5 @@ +Joypad:: + homecall_jump _Joypad + +ReadJoypad:: + homecall_jump ReadJoypad_ diff --git a/home/joypad2.asm b/home/joypad2.asm new file mode 100644 index 00000000..30f9f788 --- /dev/null +++ b/home/joypad2.asm @@ -0,0 +1,100 @@ +; 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 + push de + push bc + callfar TownMapSpriteBlinkingAnimation + pop bc + pop de +.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 + call WaitForSoundToFinish + ld a, SFX_PRESS_AB + jp PlaySound +.inLinkBattle + ld c, 65 + jp DelayFrames diff --git a/home/lcd.asm b/home/lcd.asm new file mode 100644 index 00000000..72fc70ed --- /dev/null +++ b/home/lcd.asm @@ -0,0 +1,25 @@ +DisableLCD:: + xor a + ldh [rIF], a + ldh a, [rIE] + ld b, a + res 0, a + ldh [rIE], a + +.wait + ldh a, [rLY] + cp LY_VBLANK + jr nz, .wait + + ldh a, [rLCDC] + and $ff ^ rLCDC_ENABLE_MASK + ldh [rLCDC], a + ld a, b + ldh [rIE], a + ret + +EnableLCD:: + ldh a, [rLCDC] + set rLCDC_ENABLE, a + ldh [rLCDC], a + ret diff --git a/home/lcdc.asm b/home/lcdc.asm index 6172dd0d..dd3fb2fb 100644 --- a/home/lcdc.asm +++ b/home/lcdc.asm @@ -1,15 +1,15 @@ LCDC:: push af - ld a, [hLCDCPointer] ; doubles as enabling byte + ldh a, [hLCDCPointer] ; doubles as enabling byte and a jr z, .noLCDCInterrupt push hl ; [C700 + [rLY]] --> [FF00 + [hLCDCPointer]] - ld a, [rLY] + ldh a, [rLY] ld l, a ld h, wLYOverrides / $100 ld h, [hl] ; h != not part of pointer - ld a, [hLCDCPointer] + ldh a, [hLCDCPointer] ld l, a ld a, h ld h, $ff diff --git a/home/list_menu.asm b/home/list_menu.asm new file mode 100644 index 00000000..f0480460 --- /dev/null +++ b/home/list_menu.asm @@ -0,0 +1,523 @@ +; INPUT: +; [wListMenuID] = list menu ID +; [wListPointer] = address of the list (2 bytes) +DisplayListMenuID:: + xor a + ldh [hAutoBGTransferEnabled], a ; disable auto-transfer + ld a, 1 + ldh [hJoy7], a ; joypad state update flag + ld a, [wBattleType] + 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 [wListCount], a + ld a, [wListPointer] + ld l, a + ld a, [wListPointer + 1] + ld h, a ; hl = address of the list + ld a, [hl] ; the first byte is the number of entries in the list + ld [wListCount], a + ld a, LIST_MENU_BOX + ld [wTextBoxID], 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 + lb de, 9, 14 ; height and width of menu text box + ld a, [wListMenuID] + and a ; PCPOKEMONLISTMENU? + jr nz, .skipMovingSprites + call UpdateSprites +.skipMovingSprites + ld a, 1 ; max menu item ID is 1 if the list has less than 2 entries + ld [wMenuWatchMovingOutOfBounds], a + ld a, [wListCount] + cp 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:: + xor a + ldh [hAutoBGTransferEnabled], a ; disable transfer + call PrintListMenuEntries + ld a, 1 + ldh [hAutoBGTransferEnabled], a ; enable transfer + call Delay3 + ld a, [wBattleType] + and a ; is it the Old Man battle? + jr z, .notOldManBattle +.oldManBattle + ld a, "▶" + ldcoord_a 5, 4 ; place menu cursor in front of first menu entry + ld c, 20 + 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 + +; pointless because both values are overwritten before they are read + ld a, $01 + ld [wMenuExitMethod], a + ld [wChosenMenuItem], a + + xor a + ld [wMenuWatchMovingOutOfBounds], a + ld a, [wCurrentMenuItem] + ld c, a + ld a, [wListScrollOffset] + add c + ld c, a + ld a, [wListCount] + 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 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, [wListPointer] + ld l, a + ld a, [wListPointer + 1] + 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 ; PCPOKEMONLISTMENU? + jr z, .pokemonList + push hl + call GetItemPrice + pop hl + ld a, [wListMenuID] + cp ITEMLISTMENU + jr nz, .skipGettingQuantity +; if it's an item menu + inc hl + ld a, [hl] ; a = item quantity + ld [wMaxItemQuantity], 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, [wListPointer] + 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, CHOSE_MENU_ITEM + ld [wMenuExitMethod], a + ld a, [wCurrentMenuItem] + ld [wChosenMenuItem], a + xor a + ldh [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 3 + ld b, a + ld a, [wListCount] + 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:: +; text box dimensions/coordinates for just quantity + hlcoord 15, 9 + lb bc, 1, 3 ; height and width + ld a, [wListMenuID] + cp PRICEDITEMLISTMENU + jr nz, .drawTextBox +; text box dimensions/coordinates for quantity and price + hlcoord 7, 9 + lb bc, 1, 11 ; height and width +.drawTextBox + call TextBoxBorder + hlcoord 16, 10 + ld a, [wListMenuID] + cp PRICEDITEMLISTMENU + jr nz, .printInitialQuantity + hlcoord 8, 10 +.printInitialQuantity + ld de, InitialQuantityText + call PlaceString + xor a + ld [wItemQuantity], a ; initialize current quantity to 0 + jp .incrementQuantity +.waitForKeyPressLoop + call JoypadLowSensitivity + ldh 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, [wMaxItemQuantity] + inc a + ld b, a + ld hl, wItemQuantity ; 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, wItemQuantity ; current quantity + dec [hl] + jr nz, .handleNewQuantity +; wrap to the max quantity if the player goes below 1 + ld a, [wMaxItemQuantity] + ld [hl], a +.handleNewQuantity + hlcoord 17, 10 + ld a, [wListMenuID] + cp PRICEDITEMLISTMENU + jr nz, .printQuantity +.printPrice + ld c, $03 + ld a, [wItemQuantity] + ld b, a + ld hl, hMoney ; 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, hMoney + 2 + ld hl, hItemPrice + 2 + push bc + predef AddBCDPredef ; add the individual price to the current sum + pop bc + dec b + jr nz, .addLoop + ldh a, [hHalveItemPrices] + and a ; should the price be halved (for selling items)? + jr z, .skipHalvingPrice + xor a + ldh [hDivideBCDDivisor], a + ldh [hDivideBCDDivisor + 1], a + ld a, $02 + ldh [hDivideBCDDivisor + 2], a + predef DivideBCDPredef3 ; halves the price +; store the halved price + ldh a, [hDivideBCDQuotient] + ldh [hMoney], a + ldh a, [hDivideBCDQuotient + 1] + ldh [hMoney + 1], a + ldh a, [hDivideBCDQuotient + 2] + ldh [hMoney + 2], a +.skipHalvingPrice + hlcoord 12, 10 + ld de, SpacesBetweenQuantityAndPriceText + call PlaceString + ld de, hMoney ; total price + ld c, $a3 + call PrintBCDNumber + hlcoord 9, 10 +.printQuantity + ld de, wItemQuantity ; current quantity + lb bc, LEADING_ZEROES | 1, 2 ; 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:: + db "×01@" + +SpacesBetweenQuantityAndPriceText:: + db " @" + +ExitListMenu:: + ld a, [wCurrentMenuItem] + ld [wChosenMenuItem], a + ld a, CANCELLED_MENU + ld [wMenuExitMethod], a + ld [wMenuWatchMovingOutOfBounds], a + xor a + ldh [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:: + hlcoord 5, 3 + lb bc, 9, 14 + call ClearScreenArea + ld a, [wListPointer] + ld e, a + ld a, [wListPointer + 1] + ld d, a + inc de ; de = beginning of list entries + ld a, [wListScrollOffset] + ld c, a + ld a, [wListMenuID] + cp ITEMLISTMENU + ld a, c + jr nz, .skipMultiplying +; if it's an item menu +; item entries are 2 bytes long, so multiply by 2 + add 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 $ff + jp z, .printCancelMenuItem + push bc + push de + push hl + push hl + push de + ld a, [wListMenuID] + and a ; PCPOKEMONLISTMENU? + jr z, .pokemonPCMenu + cp MOVESLISTMENU + jr z, .movesMenu +.itemMenu + call GetItemName + jr .placeNameString +.pokemonPCMenu + push hl + ld hl, wPartyCount + ld a, [wListPointer] + 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, [wPrintItemPrices] + 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, SCREEN_WIDTH + 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 ; PCPOKEMONLISTMENU? + jr nz, .skipPrintingPokemonLevel +.printPokemonLevel + ld a, [wd11e] + push af + push hl + ld hl, wPartyCount + ld a, [wListPointer] + cp l ; is it a list of party pokemon or box pokemon? + ld a, PLAYER_PARTY_DATA + jr z, .next + ld a, BOX_DATA +.next + ld [wMonDataLocation], 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 + ld a, [wMonDataLocation] + and a ; is it a list of party pokemon or box pokemon? + jr z, .skipCopyingLevel +.copyLevel + ld a, [wLoadedMonBoxLevel] + ld [wLoadedMonLevel], a +.skipCopyingLevel + pop hl + ld bc, $1c + add hl, bc + call PrintLevel + pop af + ld [wd11e], a +.skipPrintingPokemonLevel + pop hl + pop de + inc de + ld a, [wListMenuID] + cp ITEMLISTMENU + jr nz, .nextListEntry +.printItemQuantity + ld a, [wd11e] + ld [wcf91], a + call IsKeyItem ; check if item is unsellable + ld a, [wIsKeyItem] + and a ; is the item unsellable? + jr nz, .skipPrintingItemQuantity ; if so, don't print the quantity + push hl + ld bc, SCREEN_WIDTH + 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 [wMaxItemQuantity], a + push de + ld de, wd11e + ld [de], a + lb bc, 1, 2 + 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 + add a + cp c ; is it this item? + jr nz, .nextListEntry + dec hl + ld a, "▷" + ld [hli], a +.nextListEntry + ld bc, 2 * SCREEN_WIDTH ; 2 rows + add hl, bc + pop bc + inc c + dec b + jp nz, .loop + ld bc, -8 + add hl, bc + ld a, "▼" + ld [hl], a + ret +.printCancelMenuItem + ld de, ListMenuCancelText + jp PlaceString + +ListMenuCancelText:: + db "CANCEL@" diff --git a/home/load_font.asm b/home/load_font.asm new file mode 100644 index 00000000..8ed0f0a7 --- /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 FarCopyData ; 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 FarCopyData ; 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..51206cc5 --- /dev/null +++ b/home/map_objects.asm @@ -0,0 +1,322 @@ +; 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 + +IsSurfingPikachuInParty:: +; set bit 6 of wd472 if true +; also calls Func_3467, which is a bankswitch to IsStarterPikachuInOurParty + ld a, [wd472] + and $3f + ld [wd472], a + ld hl, wPartyMon1 + ld c, PARTY_LENGTH + ld b, SURF +.loop + ld a, [hl] + cp PIKACHU + jr nz, .notPikachu + push hl + ld de, $8 + add hl, de + ld a, [hli] + cp b ; does pikachu have surf as one of its moves + jr z, .hasSurf + ld a, [hli] + cp b + jr z, .hasSurf + ld a, [hli] + cp b + jr z, .hasSurf + ld a, [hli] + cp b + jr nz, .noSurf +.hasSurf + ld a, [wd472] + set 6, a + ld [wd472], a +.noSurf + pop hl +.notPikachu + ld de, wPartyMon2 - wPartyMon1 + add hl, de + dec c + jr nz, .loop + call Func_3467 + ret + +Func_3467:: + push hl + push bc + callfar IsStarterPikachuInOurParty + pop bc + pop hl + ret nc + ld a, [wd472] + set 7, a + ld [wd472], a + ret + +DisplayPokedex:: + ld [wd11e], a + farjp _DisplayPokedex + +SetSpriteFacingDirectionAndDelay:: + call SetSpriteFacingDirection + ld c, 6 + jp DelayFrames + +SetSpriteFacingDirection:: + ld a, SPRITESTATEDATA1_FACINGDIRECTION + ldh [hSpriteDataOffset], a + call GetPointerWithinSpriteStateData1 + ldh a, [hSpriteFacingDirection] + ld [hl], a + ret + +SetSpriteImageIndexAfterSettingFacingDirection:: + ld de, SPRITESTATEDATA1_IMAGEINDEX - SPRITESTATEDATA1_FACINGDIRECTION + add hl, de + ld [hl], a + ret + +SpriteFunc_34a1:: + ldh a, [hSpriteIndex] + swap a + add $e + ld l, a + ld h, $c2 + ld c, [hl] + dec c + swap c + ldh a, [hSpriteOffset] + add c + ld c, a + ldh a, [hSpriteHeight] + swap a + add $2 + ld l, a + dec h + ld [hl], c + 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, HIGH(wSpriteStateData1) + jr _GetPointerWithinSpriteStateData + +GetPointerWithinSpriteStateData2:: + ld h, HIGH(wSpriteStateData2) + +_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 e, a + ld d, 0 + add hl, de + pop de + ret diff --git a/home/math.asm b/home/math.asm new file mode 100644 index 00000000..59eda606 --- /dev/null +++ b/home/math.asm @@ -0,0 +1,33 @@ +; 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 + homecall _Divide + 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 new file mode 100644 index 00000000..c766fbd5 --- /dev/null +++ b/home/move_mon.asm @@ -0,0 +1,239 @@ +; 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. +; [wRemoveMonFromBox] != 0 specifies the current box. +RemovePokemon:: + jpfar _RemovePokemon + +AddPartyMon:: + push hl + push de + push bc + farcall _AddPartyMon + pop bc + pop de + pop hl + ret + +; calculates all 5 stats of current mon and writes them to [de] +CalcStats:: + ld c, $0 +.statsLoop + inc c + call CalcStat + ldh a, [hMultiplicand+1] + ld [de], a + inc de + ldh a, [hMultiplicand+2] + ld [de], a + inc de + ld a, c + cp NUM_STATS + 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:: + push hl + push de + push bc + ld a, b + ld d, a + push hl + ld hl, wMonHeader + 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 + ldh [hMultiplicand], a + ldh [hMultiplicand+1], a + inc b ; increment current stat exp bonus + ld a, b + cp $ff + jr z, .statExpDone + ldh [hMultiplicand+2], a + ldh [hMultiplier], a + call Multiply + ld a, [hld] + ld d, a + ldh a, [hProduct + 3] + sub d + ld a, [hli] + ld d, a + ldh a, [hProduct + 2] + sbc d ; test if (current stat exp bonus)^2 < stat exp + jr c, .statExpLoop +.statExpDone + srl c + pop hl + push bc + ld bc, wPartyMon1DVs - (wPartyMon1HPExp - 1) ; also wEnemyMonDVs - wEnemyMonHP + 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 ; de = (Base + IV) * 2 + ceil(Sqrt(stat exp)) / 4 +.noCarry2 + ldh [hMultiplicand+2], a + ld a, d + ldh [hMultiplicand+1], a + xor a + ldh [hMultiplicand], a + ld a, [wCurEnemyLVL] + ldh [hMultiplier], a + call Multiply ; ((Base + IV) * 2 + ceil(Sqrt(stat exp)) / 4) * Level + ldh a, [hMultiplicand] + ldh [hDividend], a + ldh a, [hMultiplicand+1] + ldh [hDividend+1], a + ldh a, [hMultiplicand+2] + ldh [hDividend+2], a + ld a, $64 + ldh [hDivisor], 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 ; + 5 for non-HP stat + jr nz, .notHPStat + ld a, [wCurEnemyLVL] + ld b, a + ldh a, [hMultiplicand+2] + add b + ldh [hMultiplicand+2], a + jr nc, .noCarry3 + ldh a, [hMultiplicand+1] + inc a + ldh [hMultiplicand+1], a ; HP: (((Base + IV) * 2 + ceil(Sqrt(stat exp)) / 4) * Level) / 100 + Level +.noCarry3 + ld a, 10 ; +10 for HP stat +.notHPStat + ld b, a + ldh a, [hMultiplicand+2] + add b + ldh [hMultiplicand+2], a + jr nc, .noCarry4 + ldh a, [hMultiplicand+1] + inc a ; non-HP: (((Base + IV) * 2 + ceil(Sqrt(stat exp)) / 4) * Level) / 100 + 5 + ldh [hMultiplicand+1], a ; HP: (((Base + IV) * 2 + ceil(Sqrt(stat exp)) / 4) * Level) / 100 + Level + 10 +.noCarry4 + ldh a, [hMultiplicand+1] ; check for overflow (>999) + cp HIGH(MAX_STAT_VALUE) + 1 + jr nc, .overflow + cp HIGH(MAX_STAT_VALUE) + jr c, .noOverflow + ldh a, [hMultiplicand+2] + cp LOW(MAX_STAT_VALUE) + 1 + jr c, .noOverflow +.overflow + ld a, HIGH(MAX_STAT_VALUE) ; overflow: cap at 999 + ldh [hMultiplicand+1], a + ld a, LOW(MAX_STAT_VALUE) + ldh [hMultiplicand+2], a +.noOverflow + pop bc + pop de + pop hl + ret + +AddEnemyMonToPlayerParty:: + homecall_sf _AddEnemyMonToPlayerParty + ret + +MoveMon:: + homecall_sf _MoveMon + ret diff --git a/home/names.asm b/home/names.asm new file mode 100644 index 00000000..f7751fa0 --- /dev/null +++ b/home/names.asm @@ -0,0 +1,141 @@ +GetMonName:: + push hl + ldh a, [hLoadedROMBank] + push af + ld a, BANK(MonsterNames) + ldh [hLoadedROMBank], a + ld [MBC1RomBank], a + ld a, [wd11e] + dec a + ld hl, MonsterNames + ld c, 10 + ld b, 0 + call AddNTimes + ld de, wcd6d + push de + ld bc, 10 + call CopyData + ld hl, wcd6d + 10 + ld [hl], "@" + pop de + pop af + ldh [hLoadedROMBank], a + ld [MBC1RomBank], a + pop hl + ret + +GetItemName:: +; 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 HM01 ; is this a TM/HM? + jr nc, .Machine + + ld [wd0b5], a + ld a, ITEM_NAME + ld [wNameListType], 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:: +; copies the name of the TM/HM in [wd11e] to wcd6d + push hl + push de + push bc + ld a, [wd11e] + push af + cp TM01 ; is this a TM? [not HM] + jr nc, .WriteTM +; if HM, then write "HM" and add NUM_HMS to the item ID, so we can reuse the +; TM printing code + add NUM_HMS + 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 TM01 - 1 + ld b, "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, "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:: + db "TM" +HiddenPrefix:: + db "HM" + +; sets carry if item is HM, clears carry if item is not HM +; Input: a = item ID +IsItemHM:: + cp HM01 + jr c, .notHM + cp TM01 + 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:: + ld hl, HMMoves + ld de, 1 + jp IsInArray + +HMMoves:: +INCLUDE "data/moves/hm_moves.asm" + +GetMoveName:: + push hl + ld a, MOVE_NAME + ld [wNameListType], 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 diff --git a/home/names2.asm b/home/names2.asm new file mode 100644 index 00000000..9c8ca00c --- /dev/null +++ b/home/names2.asm @@ -0,0 +1,92 @@ +NamePointers:: +; entries correspond to *_NAME constants + dw MonsterNames + dw MoveNames + dw UnusedBadgeNames + dw ItemNames + dw wPartyMonOT ; player's OT names list + dw wEnemyMonOT ; enemy's OT names list + dw TrainerNames + +GetName:: +; arguments: +; [wd0b5] = which name +; [wNameListType] = which list +; [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 HM01 + jp nc, GetMachineName + + ldh a, [hLoadedROMBank] + push af + push hl + push bc + push de + ld a, [wNameListType] ;List3759_entrySelector + dec a + jr nz, .otherEntries + ;1 = MON_NAMES + call GetMonName + ld hl, NAME_LENGTH + add hl, de + ld e, l + ld d, h + jr .gotPtr +.otherEntries + ;2-7 = OTHER ENTRIES + ld a, [wPredefBank] + call BankswitchCommon + ld a, [wNameListType] ;VariousNames' entryID + dec a + add a + ld d, 0 + ld e, a + jr nc, .skip + inc d +.skip + ld hl, NamePointers + add hl, de + ld a, [hli] + ldh [hSwapTemp + 1], a + ld a, [hl] + ldh [hSwapTemp], a + ldh a, [hSwapTemp] + ld h, a + ldh a, [hSwapTemp + 1] + ld l, a + ld a, [wd0b5] + ld b, a + ld c, 0 +.nextName + ld d, h + ld e, l +.nextChar + ld a, [hli] + cp "@" + 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, $14 + call CopyData +.gotPtr + ld a, e + ld [wUnusedCF8D], a + ld a, d + ld [wUnusedCF8D + 1], a + pop de + pop bc + pop hl + pop af + call BankswitchCommon + ret diff --git a/home/npc_movement.asm b/home/npc_movement.asm new file mode 100644 index 00000000..200d3983 --- /dev/null +++ b/home/npc_movement.asm @@ -0,0 +1,52 @@ +; 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] + call BankswitchCommon + ld a, [wNPCMovementScriptFunctionNum] + call CallFunctionInTable + pop af + call BankswitchCommon + ret + +.NPCMovementScriptPointerTables + dw PalletMovementScriptPointerTable + dw PewterMuseumGuyMovementScriptPointerTable + dw PewterGymGuyMovementScriptPointerTable +.playerStepOutFromDoor + farjp PlayerStepOutFromDoor + +EndNPCMovementScript:: + farjp _EndNPCMovementScript + +DebugPressedOrHeldB:: + 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 8608cbfe..0cd63cf7 100644 --- a/home/overworld.asm +++ b/home/overworld.asm @@ -3,7 +3,7 @@ EnterMap:: ld a, $ff ld [wJoyIgnore], a call LoadMapData - callba ClearVariablesOnEnterMap + farcall ClearVariablesOnEnterMap ld hl, wd72c bit 0, [hl] ; has the player already made 3 steps since the last battle? jr z, .skipGivingThreeStepsOfNoRandomBattles @@ -19,7 +19,7 @@ EnterMap:: ld a, [hl] and 1 << 4 | 1 << 3 ; fly warp or dungeon warp jr z, .didNotEnterUsingFlyWarpOrDungeonWarp - callba EnterMapAnim + farcall EnterMapAnim call UpdateSprites ld hl, wd732 res 3, [hl] @@ -27,7 +27,7 @@ EnterMap:: res 4, [hl] .didNotEnterUsingFlyWarpOrDungeonWarp call IsSurfingPikachuInParty - callba CheckForceBikeOrSurf ; handle currents in SF islands and forced bike riding in cycling road + farcall CheckForceBikeOrSurf ; handle currents in SF islands and forced bike riding in cycling road ld hl, wd732 bit 4, [hl] res 4, [hl] @@ -51,7 +51,7 @@ OverworldLoopLessDelay:: and a jp nz, .moveAhead ; if the player sprite has not yet completed the walking animation call JoypadOverworld ; get joypad state (which is possibly simulated) - callba SafariZoneCheck + farcall SafariZoneCheck ld a, [wSafariZoneGameOver] and a jp nz, WarpFound2 @@ -68,16 +68,16 @@ OverworldLoopLessDelay:: ld a, [wd730] bit 7, a ; are we simulating button presses? jr z, .notSimulating - ld a, [hJoyHeld] + ldh a, [hJoyHeld] jr .checkIfStartIsPressed .notSimulating - ld a, [hJoyPressed] + ldh a, [hJoyPressed] .checkIfStartIsPressed bit 3, a ; start button jr z, .startButtonNotPressed ; if START is pressed - xor a - ld [hSpriteIndexOrTextID], a ; start menu text ID + xor a ; TEXT_START_MENU + ldh [hSpriteIndexOrTextID], a jp .displayDialogue .startButtonNotPressed bit 0, a ; A button @@ -89,14 +89,14 @@ OverworldLoopLessDelay:: call IsPlayerCharacterBeingControlledByGame jr nz, .checkForOpponent call CheckForHiddenObjectOrBookshelfOrCardKeyDoor - ld a, [$ffeb] + ldh a, [hItemAlreadyFound] and a jp z, OverworldLoop ; jump if a hidden object or bookshelf was found, but not if a card key door was found xor a ld [wd436], a ; new yellow address call IsSpriteOrSignInFrontOfPlayer call Func_0ffe - ld a, [hSpriteIndexOrTextID] + ldh a, [hSpriteIndexOrTextID] and a jp z, OverworldLoop .displayDialogue @@ -107,7 +107,7 @@ OverworldLoopLessDelay:: jr nz, .checkForOpponent bit 0, a jr nz, .checkForOpponent - aCoord 8, 9 + lda_coord 8, 9 ld [wTilePlayerStandingOn], a ; unused? call DisplayTextID ; display either the start menu or the NPC/sign text ld a, [wEnteringCableClub] @@ -141,11 +141,11 @@ OverworldLoopLessDelay:: jp OverworldLoop .checkIfDownButtonIsPressed - ld a, [hJoyHeld] ; current joypad state + ldh a, [hJoyHeld] ; current joypad state bit 7, a ; down button jr z, .checkIfUpButtonIsPressed ld a, 1 - ld [wSpriteStateData1 + 3], a ; delta Y + ld [wSpritePlayerStateData1YStepVector], a ld a, PLAYER_DIR_DOWN jr .handleDirectionButtonPress @@ -153,7 +153,7 @@ OverworldLoopLessDelay:: bit 6, a ; up button jr z, .checkIfLeftButtonIsPressed ld a, -1 - ld [wSpriteStateData1 + 3], a ; delta Y + ld [wSpritePlayerStateData1YStepVector], a ld a, PLAYER_DIR_UP jr .handleDirectionButtonPress @@ -161,7 +161,7 @@ OverworldLoopLessDelay:: bit 5, a ; left button jr z, .checkIfRightButtonIsPressed ld a, -1 - ld [wSpriteStateData1 + 5], a ; delta X + ld [wSpritePlayerStateData1XStepVector], a ld a, PLAYER_DIR_LEFT jr .handleDirectionButtonPress @@ -169,7 +169,7 @@ OverworldLoopLessDelay:: bit 4, a ; right button jr z, .noDirectionButtonsPressed ld a, 1 - ld [wSpriteStateData1 + 5], a ; delta X + ld [wSpritePlayerStateData1XStepVector], a ld a, 1 .handleDirectionButtonPress @@ -228,7 +228,7 @@ OverworldLoopLessDelay:: .noCollision ld a, $08 ld [wWalkCounter], a - callab Func_fcc08 + callfar Func_fcc08 jr .moveAhead2 .moveAhead @@ -249,7 +249,7 @@ OverworldLoopLessDelay:: call StepCountCheck CheckEvent EVENT_IN_SAFARI_ZONE ; in the safari zone? jr z, .notSafariZone - callba SafariZoneCheckSteps + farcall SafariZoneCheckSteps ld a, [wSafariZoneGameOver] and a jp nz, WarpFound2 @@ -275,7 +275,7 @@ OverworldLoopLessDelay:: set 5, [hl] set 6, [hl] xor a - ld [hJoyHeld], a + ldh [hJoyHeld], a ld a, [wCurMap] cp CINNABAR_GYM jr nz, .notCinnabarGym @@ -286,7 +286,7 @@ OverworldLoopLessDelay:: ld a, [wCurMap] cp OAKS_LAB jp z, .noFaintCheck ; no blacking out if the player lost to the rival in Oak's lab - callab AnyPartyAlive + callfar AnyPartyAlive ld a, d and a jr z, AllPokemonFainted @@ -330,7 +330,7 @@ NewBattle:: ld a, [wd72e] bit 4, a jr nz, .noBattle - jpba InitBattle + farjp InitBattle .noBattle and a ret @@ -349,7 +349,7 @@ DoBikeSpeedup:: ld a, [wCurMap] cp ROUTE_17 ; Cycling Road jr nz, .goFaster - ld a, [hJoyHeld] + ldh a, [hJoyHeld] and D_UP | D_LEFT | D_RIGHT ret nz .goFaster @@ -381,7 +381,7 @@ CheckWarpsNoCollisionLoop:: push bc ld hl, wd736 set 2, [hl] ; standing on warp flag - callba IsPlayerStandingOnDoorTileOrWarpTile + farcall IsPlayerStandingOnDoorTileOrWarpTile pop bc pop hl jr c, WarpFound1 ; jump if standing on door or warp @@ -400,7 +400,7 @@ CheckWarpsNoCollisionLoop:: call Joypad pop bc pop de - ld a, [hJoyHeld] + ldh a, [hJoyHeld] and D_DOWN | D_UP | D_LEFT | D_RIGHT jr z, CheckWarpsNoCollisionRetry2 ; if directional buttons aren't being pressed, do not pass through the warp jr WarpFound1 @@ -435,7 +435,7 @@ CheckWarpsCollision:: ld a, [hli] ld [wDestinationWarpID], a ld a, [hl] - ld [hWarpDestinationMap], a + ldh [hWarpDestinationMap], a jr WarpFound2 .retry1 inc hl @@ -450,7 +450,7 @@ WarpFound1:: ld a, [hli] ld [wDestinationWarpID], a ld a, [hli] - ld [hWarpDestinationMap], a + ldh [hWarpDestinationMap], a WarpFound2:: ld a, [wNumberOfWarps] @@ -465,7 +465,7 @@ WarpFound2:: ld [wLastMap], a ld a, [wCurMapWidth] ld [wUnusedD366], a ; not read - ld a, [hWarpDestinationMap] + ldh a, [hWarpDestinationMap] ld [wCurMap], a cp ROCK_TUNNEL_1F jr nz, .notRockTunnel @@ -473,19 +473,19 @@ WarpFound2:: ld [wMapPalOffset], a call GBFadeOutToBlack .notRockTunnel - callab SetPikachuSpawnOutside + callfar SetPikachuSpawnOutside call PlayMapChangeSound jr .done ; for maps that can have the 0xFF destination map, which means to return to the outside map ; not all these maps are necessarily indoors, though .indoorMaps - ld a, [hWarpDestinationMap] ; destination map - cp $ff + ldh a, [hWarpDestinationMap] ; destination map + cp LAST_MAP jr z, .goBackOutside ; if not going back to the previous map ld [wCurMap], a - callba IsPlayerStandingOnWarpPadOrHole + farcall IsPlayerStandingOnWarpPadOrHole ld a, [wStandingOnWarpPadOrHole] dec a ; is the player on a warp pad? jr nz, .notWarpPad @@ -500,11 +500,11 @@ WarpFound2:: ld hl, wd736 res 0, [hl] res 1, [hl] - callab SetPikachuSpawnWarpPad + callfar SetPikachuSpawnWarpPad jr .done .goBackOutside - callab SetPikachuSpawnBackOutside + callfar SetPikachuSpawnBackOutside ld a, [wLastMap] ld [wCurMap], a call PlayMapChangeSound @@ -654,7 +654,7 @@ CheckMapConnections:: ld b, SET_PAL_OVERWORLD call RunPaletteCommand ; Since the sprite set shouldn't change, this will just update VRAM slots at -; $C2XE without loading any tile patterns. +; x#SPRITESTATEDATA2_IMAGEBASEOFFSET without loading any tile patterns. call InitMapSprites call LoadTileBlockMap jp OverworldLoopLessDelay @@ -669,7 +669,7 @@ PlayMapChangeSound:: jr z, .didNotGoThroughDoor cp CEMETERY jr z, .didNotGoThroughDoor - aCoord 8, 8 ; upper left tile of the 4x4 square the player's sprite is standing on + lda_coord 8, 8 ; upper left tile of the 4x4 square the player's sprite is standing on cp $0b ; door tile in tileset 0 jr nz, .didNotGoThroughDoor ld a, SFX_GO_INSIDE @@ -728,7 +728,7 @@ ExtraWarpCheck:: jp Bankswitch MapEntryAfterBattle:: - callba IsPlayerStandingOnWarp ; for enabling warp testing after collisions + farcall IsPlayerStandingOnWarp ; for enabling warp testing after collisions ld a, [wMapPalOffset] and a jp z, GBFadeInFromWhite @@ -744,7 +744,7 @@ HandleBlackOut:: ld hl, wd72e res 5, [hl] switchbank SpecialWarpIn ; also Bank(SpecialEnterMap) - callab ResetStatusAndHalveMoneyOnBlackout + callfar ResetStatusAndHalveMoneyOnBlackout call SpecialWarpIn call PlayDefaultMusicFadeOutCurrent jp SpecialEnterMap @@ -774,7 +774,7 @@ HandleFlyWarpOrDungeonWarp:: jp SpecialEnterMap LeaveMapAnim:: - jpba _LeaveMapAnim + farjp _LeaveMapAnim Func_07c4:: ld a, [wWalkBikeSurfState] @@ -799,7 +799,7 @@ LoadPlayerSpriteGraphics:: dec a jr z, .ridingBike - ld a, [hTilesetType] + ldh a, [hTilesetType] and a jr nz, .determineGraphics jr .startWalking @@ -853,7 +853,7 @@ IsBikeRidingAllowed:: scf ret -INCLUDE "data/bike_riding_tilesets.asm" +INCLUDE "data/tilesets/bike_riding_tilesets.asm" ; load the tile pattern data of the current tileset into VRAM LoadTilesetTilePatternData:: @@ -878,9 +878,9 @@ LoadTileBlockMap:: ; a 3-byte border at the edges of the map is kept so that there is space for map connections ld hl, wOverworldMap ld a, [wCurMapWidth] - ld [hMapWidth], a + ldh [hMapWidth], a add MAP_BORDER * 2 ; east and west - ld [hMapStride], a ; map width + border + ldh [hMapStride], a ; map width + border ld b, 0 ld c, a ; make space for north border (next 3 lines) @@ -897,7 +897,7 @@ LoadTileBlockMap:: ld b, a .rowLoop ; copy one row each iteration push hl - ld a, [hMapWidth] ; map width (without border) + ldh a, [hMapWidth] ; map width (without border) ld c, a .rowInnerLoop ld a, [de] @@ -907,7 +907,7 @@ LoadTileBlockMap:: jr nz, .rowInnerLoop ; add the map width plus the border to the base address of the current row to get the next row's address pop hl - ld a, [hMapStride] ; map width + border + ldh a, [hMapStride] ; map width + border add l ld l, a jr nc, .noCarry @@ -929,9 +929,9 @@ LoadTileBlockMap:: ld a, [wNorthConnectionStripDest + 1] ld d, a ld a, [wNorthConnectionStripWidth] - ld [hNorthSouthConnectionStripWidth], a + ldh [hNorthSouthConnectionStripWidth], a ld a, [wNorthConnectedMapWidth] - ld [hNorthSouthConnectedMapWidth], a + ldh [hNorthSouthConnectedMapWidth], a call LoadNorthSouthConnectionsTileMap .southConnection ld a, [wMapConn2Ptr] @@ -947,9 +947,9 @@ LoadTileBlockMap:: ld a, [wSouthConnectionStripDest + 1] ld d, a ld a, [wSouthConnectionStripWidth] - ld [hNorthSouthConnectionStripWidth], a + ldh [hNorthSouthConnectionStripWidth], a ld a, [wSouthConnectedMapWidth] - ld [hNorthSouthConnectedMapWidth], a + ldh [hNorthSouthConnectedMapWidth], a call LoadNorthSouthConnectionsTileMap .westConnection ld a, [wMapConn3Ptr] @@ -967,7 +967,7 @@ LoadTileBlockMap:: ld a, [wWestConnectionStripHeight] ld b, a ld a, [wWestConnectedMapWidth] - ld [hEastWestConnectedMapWidth], a + ldh [hEastWestConnectedMapWidth], a call LoadEastWestConnectionsTileMap .eastConnection ld a, [wMapConn4Ptr] @@ -985,7 +985,7 @@ LoadTileBlockMap:: ld a, [wEastConnectionStripHeight] ld b, a ld a, [wEastConnectedMapWidth] - ld [hEastWestConnectedMapWidth], a + ldh [hEastWestConnectedMapWidth], a call LoadEastWestConnectionsTileMap .done ret @@ -995,7 +995,7 @@ LoadNorthSouthConnectionsTileMap:: .loop push de push hl - ld a, [hNorthSouthConnectionStripWidth] + ldh a, [hNorthSouthConnectionStripWidth] ld b, a .innerLoop ld a, [hli] @@ -1005,7 +1005,7 @@ LoadNorthSouthConnectionsTileMap:: jr nz, .innerLoop pop hl pop de - ld a, [hNorthSouthConnectedMapWidth] + ldh a, [hNorthSouthConnectedMapWidth] add l ld l, a jr nc, .noCarry1 @@ -1034,7 +1034,7 @@ LoadEastWestConnectionsTileMap:: jr nz, .innerLoop pop de pop hl - ld a, [hEastWestConnectedMapWidth] + ldh a, [hEastWestConnectedMapWidth] add l ld l, a jr nc, .noCarry1 @@ -1055,7 +1055,7 @@ LoadEastWestConnectionsTileMap:: ; if so, carry is set. otherwise, carry is cleared IsSpriteOrSignInFrontOfPlayer:: xor a - ld [hSpriteIndexOrTextID], a + ldh [hSpriteIndexOrTextID], a ld a, [wNumSigns] and a jr z, .extendRangeOverCounter @@ -1081,7 +1081,7 @@ IsSpriteInFrontOfPlayer:: ld d, $10 ; talking range in pixels (normal range) IsSpriteInFrontOfPlayer2:: lb bc, $3c, $40 ; Y and X position of player sprite - ld a, [wSpritePlayerStateData1FacingDirection] ; direction the player is facing + ld a, [wSpritePlayerStateData1FacingDirection] .checkIfPlayerFacingUp cp SPRITE_FACING_UP jr nz, .checkIfPlayerFacingDown @@ -1120,7 +1120,7 @@ IsSpriteInFrontOfPlayer2:: ld a, PLAYER_DIR_LEFT .doneCheckingDirection ld [wPlayerDirection], a - ld hl, wSpriteStateData1 + $10 + ld hl, wSprite01StateData1 ; yellow does not have the "if sprites are existant" check ld e, $01 ld d, $f @@ -1157,11 +1157,11 @@ IsSpriteInFrontOfPlayer2:: ld a, l and $f0 inc a - ld l, a ; hl = $c1x1 + ld l, a ; hl = x#SPRITESTATEDATA1_MOVEMENTSTATUS set 7, [hl] ; set flag to make the sprite face the player ld a, e - ld [hSpriteIndexOrTextID], a - ld a, [hSpriteIndexOrTextID] ; possible useless read because a already has the value of the read address + ldh [hSpriteIndexOrTextID], a + ldh a, [hSpriteIndexOrTextID] ; possible useless read because a already has the value of the read address cp $f jr nz, .dontwritetowd436 ld a, $FF @@ -1197,7 +1197,7 @@ SignLoop:: dec c add hl, bc ld a, [hl] - ld [hSpriteIndexOrTextID], a ; store sign text ID + ldh [hSpriteIndexOrTextID], a ; store sign text ID pop bc pop hl scf @@ -1221,16 +1221,16 @@ CollisionCheckOnLand:: jr nz, .noCollision ; no collisions when the player's movements are being controlled by the game ld a, [wPlayerDirection] ; the direction that the player is trying to go in ld d, a - ld a, [wSpriteStateData1 + 12] ; the player sprite's collision data (bit field) (set in the sprite movement code) + ld a, [wSpritePlayerStateData1CollisionData] and d ; check if a sprite is in the direction the player is trying to go nop ; ??? why is this in the code jr nz, .collision xor a - ld [hSpriteIndexOrTextID], a + ldh [hSpriteIndexOrTextID], a call IsSpriteInFrontOfPlayer ; check for sprite collisions again? when does the above check fail to detect a sprite collision? jr nc, .asm_0a5c res 7, [hl] - ld a, [hSpriteIndexOrTextID] + ldh a, [hSpriteIndexOrTextID] and a ; was there a sprite collision? jr z, .asm_0a5c ; if no sprite collision @@ -1238,7 +1238,7 @@ CollisionCheckOnLand:: jr nz, .collision call CheckPikachuFollowingPlayer jr nz, .collision - ld a, [hJoyHeld] + ldh a, [hJoyHeld] and $2 jr nz, .asm_0a5c ld hl, wd435 @@ -1284,7 +1284,7 @@ CheckForJumpingAndTilePairCollisions:: predef GetTileAndCoordsInFrontOfPlayer ; get the tile in front of the player push de push bc - callba HandleLedges ; check if the player is trying to jump a ledge + farcall HandleLedges ; check if the player is trying to jump a ledge pop bc pop de pop hl @@ -1295,7 +1295,7 @@ CheckForJumpingAndTilePairCollisions:: ; if not jumping CheckForTilePairCollisions2:: - aCoord 8, 9 ; tile the player is on + lda_coord 8, 9 ; tile the player is on ld [wTilePlayerStandingOn], a CheckForTilePairCollisions:: @@ -1343,34 +1343,11 @@ CheckForTilePairCollisions:: and a ret -; FORMAT: tileset number, tile 1, tile 2 -; terminated by 0xFF -; these entries indicate that the player may not cross between tile 1 and tile 2 -; it's mainly used to simulate differences in elevation - -TilePairCollisionsLand:: - db CAVERN, $20, $05 - db CAVERN, $41, $05 - db FOREST, $30, $2E - db CAVERN, $2A, $05 - db CAVERN, $05, $21 - db FOREST, $52, $2E - db FOREST, $55, $2E - db FOREST, $56, $2E - db FOREST, $20, $2E - db FOREST, $5E, $2E - db FOREST, $5F, $2E - db $FF - -TilePairCollisionsWater:: - db FOREST, $14, $2E - db FOREST, $48, $2E - db CAVERN, $14, $05 - db $FF +INCLUDE "data/tilesets/pair_collision_tile_ids.asm" ; this builds a tile map from the tile block map based on the current X/Y coordinates of the player's character LoadCurrentMapView:: - ld a, [H_LOADEDROMBANK] + ldh a, [hLoadedROMBank] push af ld a, [wTilesetBank] ; tile data ROM bank call BankswitchCommon ; switch to ROM bank that contains tile data @@ -1421,21 +1398,21 @@ LoadCurrentMapView:: dec b jr nz, .rowLoop ld hl, wTileMapBackup - ld bc, $0000 + ld bc, $0 .adjustForYCoordWithinTileBlock ld a, [wYBlockCoord] and a jr z, .adjustForXCoordWithinTileBlock - ld bc, $0030 + ld bc, $30 add hl, bc .adjustForXCoordWithinTileBlock ld a, [wXBlockCoord] and a jr z, .copyToVisibleAreaBuffer - ld bc, $0002 + ld bc, $2 add hl, bc .copyToVisibleAreaBuffer - coord de, 0, 0 ; base address for the tiles that are directly transferred to VRAM during V-blank + decoord 0, 0 ; base address for the tiles that are directly transferred to VRAM during V-blank ld b, SCREEN_HEIGHT .rowLoop2 ld c, SCREEN_WIDTH @@ -1473,14 +1450,14 @@ AdvancePlayerSprite:: ; the portion of the map that was newly exposed due to the player's movement ScheduleNorthRowRedraw:: - coord hl, 0, 0 + hlcoord 0, 0 call CopyToRedrawRowOrColumnSrcTiles ld a, [wMapViewVRAMPointer] - ld [hRedrawRowOrColumnDest], a + ldh [hRedrawRowOrColumnDest], a ld a, [wMapViewVRAMPointer + 1] - ld [hRedrawRowOrColumnDest + 1], a + ldh [hRedrawRowOrColumnDest + 1], a ld a, REDRAW_ROW - ld [hRedrawRowOrColumnMode], a + ldh [hRedrawRowOrColumnMode], a ret CopyToRedrawRowOrColumnSrcTiles:: @@ -1495,26 +1472,26 @@ CopyToRedrawRowOrColumnSrcTiles:: ret ScheduleSouthRowRedraw:: - coord hl, 0, 16 + hlcoord 0, 16 call CopyToRedrawRowOrColumnSrcTiles ld a, [wMapViewVRAMPointer] ld l, a ld a, [wMapViewVRAMPointer + 1] ld h, a - ld bc, $0200 + ld bc, $200 add hl, bc ld a, h and $03 or $98 - ld [hRedrawRowOrColumnDest + 1], a + ldh [hRedrawRowOrColumnDest + 1], a ld a, l - ld [hRedrawRowOrColumnDest], a + ldh [hRedrawRowOrColumnDest], a ld a, REDRAW_ROW - ld [hRedrawRowOrColumnMode], a + ldh [hRedrawRowOrColumnMode], a ret ScheduleEastColumnRedraw:: - coord hl, 18, 0 + hlcoord 18, 0 call ScheduleColumnRedrawHelper ld a, [wMapViewVRAMPointer] ld c, a @@ -1524,11 +1501,11 @@ ScheduleEastColumnRedraw:: add 18 and $1f or b - ld [hRedrawRowOrColumnDest], a + ldh [hRedrawRowOrColumnDest], a ld a, [wMapViewVRAMPointer + 1] - ld [hRedrawRowOrColumnDest + 1], a + ldh [hRedrawRowOrColumnDest + 1], a ld a, REDRAW_COL - ld [hRedrawRowOrColumnMode], a + ldh [hRedrawRowOrColumnMode], a ret ScheduleColumnRedrawHelper:: @@ -1552,14 +1529,14 @@ ScheduleColumnRedrawHelper:: ret ScheduleWestColumnRedraw:: - coord hl, 0, 0 + hlcoord 0, 0 call ScheduleColumnRedrawHelper ld a, [wMapViewVRAMPointer] - ld [hRedrawRowOrColumnDest], a + ldh [hRedrawRowOrColumnDest], a ld a, [wMapViewVRAMPointer + 1] - ld [hRedrawRowOrColumnDest + 1], a + ldh [hRedrawRowOrColumnDest + 1], a ld a, REDRAW_COL - ld [hRedrawRowOrColumnMode], a + ldh [hRedrawRowOrColumnMode], a ret ; function to write the tiles that make up a tile block to memory @@ -1597,7 +1574,7 @@ DrawTileBlock:: ld a, [de] ld [hl], a inc de - ld bc, $0015 + ld bc, $15 add hl, bc pop bc dec c @@ -1607,8 +1584,8 @@ DrawTileBlock:: ; function to update joypad state and simulate button presses JoypadOverworld:: xor a - ld [wSpriteStateData1 + 3], a - ld [wSpriteStateData1 + 5], a + ld [wSpritePlayerStateData1YStepVector], a + ld [wSpritePlayerStateData1XStepVector], a call RunMapScript call Joypad call ForceBikeDown @@ -1622,11 +1599,11 @@ ForceBikeDown:: ld a, [wCurMap] cp ROUTE_17 ; Cycling Road ret nz - ld a, [hJoyHeld] + ldh a, [hJoyHeld] and D_DOWN | D_UP | D_LEFT | D_RIGHT | B_BUTTON | A_BUTTON ret nz ld a, D_DOWN - ld [hJoyHeld], a ; on the cycling road, if there isn't a trainer and the player isn't pressing buttons, simulate a down press + ldh [hJoyHeld], a ; on the cycling road, if there isn't a trainer and the player isn't pressing buttons, simulate a down press ret AreInputsSimulated:: @@ -1634,18 +1611,18 @@ AreInputsSimulated:: bit 7, a ret z ; if simulating button presses - ld a, [hJoyHeld] + ldh a, [hJoyHeld] ld b, a ld a, [wOverrideSimulatedJoypadStatesMask] ; bit mask for button presses that override simulated ones and b ret nz ; return if the simulated button presses are overridden call GetSimulatedInput jr nc, .doneSimulating - ld [hJoyHeld], a ; store simulated button press in joypad state + ldh [hJoyHeld], a ; store simulated button press in joypad state and a ret nz - ld [hJoyPressed], a - ld [hJoyReleased], a + ldh [hJoyPressed], a + ldh [hJoyReleased], a ret ; if done simulating button presses @@ -1655,7 +1632,7 @@ AreInputsSimulated:: ld [wSimulatedJoypadStatesIndex], a ld [wSimulatedJoypadStatesEnd], a ld [wJoyIgnore], a - ld [hJoyHeld], a + ldh [hJoyHeld], a ld hl, wd736 ld a, [hl] and $f8 @@ -1694,14 +1671,14 @@ CollisionCheckOnWater:: jp nz, .noCollision ; return and clear carry if button presses are being simulated ld a, [wPlayerDirection] ; the direction that the player is trying to go in ld d, a - ld a, [wSpriteStateData1 + 12] ; the player sprite's collision data (bit field) (set in the sprite movement code) + ld a, [wSpritePlayerStateData1CollisionData] and d ; check if a sprite is in the direction the player is trying to go jr nz, .collision ld hl, TilePairCollisionsWater call CheckForJumpingAndTilePairCollisions jr c, .collision predef GetTileAndCoordsInFrontOfPlayer ; get tile in front of player (puts it in c and [wTileInFrontOfPlayer]) - callab IsNextTileShoreOrWater + callfar IsNextTileShoreOrWater jr c, .noCollision ld a, [wTileInFrontOfPlayer] ; tile in front of player ld c, a @@ -1742,11 +1719,11 @@ RunMapScript:: push hl push de push bc - callba TryPushingBoulder + farcall TryPushingBoulder ld a, [wFlags_0xcd60] bit 1, a ; play boulder dust animation jr z, .afterBoulderEffect - callba DoBoulderDustAnimation + farcall DoBoulderDustAnimation .afterBoulderEffect pop bc pop de @@ -1795,8 +1772,9 @@ LoadSurfingPlayerSpriteGraphics:: jr LoadPlayerSpriteGraphicsCommon LoadBikePlayerSpriteGraphics:: - ld b, BANK(RedCyclingSprite) - ld de, RedCyclingSprite + ld b, BANK(RedBikeSprite) + ld de, RedBikeSprite + LoadPlayerSpriteGraphicsCommon:: ld hl, vNPCSprites push de @@ -1819,12 +1797,12 @@ LoadPlayerSpriteGraphicsCommon:: ; function to load data from the map header LoadMapHeader:: - callba MarkTownVisitedAndLoadMissableObjects + farcall MarkTownVisitedAndLoadMissableObjects jr asm_0dbd Func_0db5:: ; XXX - callba LoadUnusedBluesHouseMissableObjectData -asm_0dbd + farcall LoadUnusedBluesHouseMissableObjectData +asm_0dbd: ld a, [wCurMapTileset] ld [wUnusedD119], a ld a, [wCurMap] @@ -1833,7 +1811,7 @@ asm_0dbd ld b, a res 7, a ld [wCurMapTileset], a - ld [hPreviousTileset], a + ldh [hPreviousTileset], a bit 7, b ret nz call GetMapHeaderPointer @@ -1921,9 +1899,9 @@ asm_0dbd ld a, [wd72e] bit 5, a ; did a battle happen immediately before this? jr nz, .skip_pika_spawn - callab SchedulePikachuSpawnForAfterText + callfar SchedulePikachuSpawnForAfterText .skip_pika_spawn - callab LoadWildData + callfar LoadWildData pop hl ; restore hl from before going to the warp/sign/sprite data (this value was saved for seemingly no purpose) ld a, [wCurMapHeight] ; map height in 4x4 tile blocks add a ; double it @@ -1934,7 +1912,7 @@ asm_0dbd ld a, [wCurMap] ld c, a ld b, $00 - ld a, [H_LOADEDROMBANK] + ldh a, [hLoadedROMBank] push af switchbank MapSongBanks ld hl, MapSongBanks @@ -1982,7 +1960,7 @@ CopySignData:: ; function to load map data LoadMapData:: - ld a, [H_LOADEDROMBANK] + ldh a, [hLoadedROMBank] push af call DisableLCD call ResetMapVariables @@ -2017,7 +1995,7 @@ LoadScreenRelatedData:: ret ReloadMapAfterSurfingMinigame:: - ld a, [H_LOADEDROMBANK] + ldh a, [hLoadedROMBank] push af call DisableLCD call ResetMapVariables @@ -2034,7 +2012,7 @@ ReloadMapAfterSurfingMinigame:: jr asm_0f4d ReloadMapAfterPrinter:: - ld a, [H_LOADEDROMBANK] + ldh a, [hLoadedROMBank] push af ld a, [wCurMap] call SwitchToMapRomBank @@ -2042,7 +2020,7 @@ ReloadMapAfterPrinter:: pop af call BankswitchCommon asm_0f4d: - jpab SetMapSpecificScriptFlagsOnMapReload + jpfar SetMapSpecificScriptFlagsOnMapReload ret ; useless? ResetMapVariables:: @@ -2050,8 +2028,8 @@ ResetMapVariables:: ld [wMapViewVRAMPointer + 1], a xor a ld [wMapViewVRAMPointer], a - ld [hSCY], a - ld [hSCX], a + ldh [hSCY], a + ldh [hSCX], a ld [wWalkCounter], a ld [wUnusedD119], a ld [wSpriteSetID], a @@ -2094,16 +2072,16 @@ SwitchToMapRomBank:: ld hl, MapHeaderBanks add hl, bc ld a, [hl] - ld [$ffe8], a ; save map ROM bank + ldh [hMapROMBank], a ; save map ROM bank call BankswitchBack - ld a, [$ffe8] + ldh a, [hMapROMBank] call BankswitchCommon pop bc pop hl ret GetMapHeaderPointer:: - ld a, [H_LOADEDROMBANK] + ldh a, [hLoadedROMBank] push af switchbank MapHeaderPointers push de @@ -2146,17 +2124,17 @@ HandleMidJump:: ld a, [wd736] bit 6, a ; jumping down a ledge? ret z - callba _HandleMidJump + farcall _HandleMidJump ret IsSpinning:: ld a, [wd736] bit 7, a ret z ; no spinning - jpba LoadSpinnerArrowTiles ; spin while moving + farjp LoadSpinnerArrowTiles ; spin while moving Func_0ffe:: - jpab IsPlayerTalkingToPikachu + jpfar IsPlayerTalkingToPikachu InitSprites:: ld a, [hli] @@ -2178,27 +2156,27 @@ InitSprites:: ret z ; don't copy sprite data if not ld b, a ld c, $0 - ld de, wSpriteStateData1 + $10 + ld de, wSprite01StateData1 ; copy sprite stuff? .loadSpriteLoop ld a, [hli] - ld [de], a ; store picture ID at C1X0 + ld [de], a ; x#SPRITESTATEDATA1_PICTUREID inc d ld a, e add $4 ld e, a ld a, [hli] - ld [de], a ; store Y position at C2X4 + ld [de], a ; x#SPRITESTATEDATA2_MAPY inc e ld a, [hli] - ld [de], a ; store X position at C2X5 + ld [de], a ; x#SPRITESTATEDATA2_MAPX inc e ld a, [hli] - ld [de], a ; store movement byte 1 at C2X6 + ld [de], a ; x#SPRITESTATEDATA2_MOVEMENTBYTE1 ld a, [hli] - ld [$ff8d], a ; save movement byte 2 + ldh [hLoadSpriteTemp1], a ; save movement byte 2 ld a, [hli] - ld [$ff8e], a ; save text ID and flags byte + ldh [hLoadSpriteTemp2], a ; save text ID and flags byte push bc call LoadSprite pop bc @@ -2213,10 +2191,10 @@ InitSprites:: ret ZeroSpriteStateData:: -; zero C110-C1EF and C210-C2EF -; C1F0-C1FF and C2F0-C2FF is used for Pikachu - ld hl, wSpriteStateData1 + $10 - ld de, wSpriteStateData2 + $10 +; zero out sprite state data for sprites 01-14 +; sprite 15 is used for Pikachu + ld hl, wSprite01StateData1 + ld de, wSprite01StateData2 xor a ld b, 14 * $10 .loop @@ -2228,8 +2206,8 @@ ZeroSpriteStateData:: ret DisableRegularSprites:: -; initialize all C100-C1FF sprite entries to disabled (other than player's and pikachu) - ld hl, wSpriteStateData1 + 1 * $10 + 2 +; disable SPRITESTATEDATA1_IMAGEINDEX (set to $ff) for sprites 01-14 + ld hl, wSprite01StateData1ImageIndex ld de, $10 ld c, $e .loop @@ -2244,16 +2222,16 @@ LoadSprite:: ld b, $0 ld hl, wMapSpriteData add hl, bc - ld a, [$ff8d] + ldh a, [hLoadSpriteTemp1] ld [hli], a ; store movement byte 2 in byte 0 of sprite entry - ld a, [$ff8e] + ldh a, [hLoadSpriteTemp2] ld [hl], a ; this appears pointless, since the value is overwritten immediately after - ld a, [$ff8e] - ld [$ff8d], a + ldh a, [hLoadSpriteTemp2] + ldh [hLoadSpriteTemp1], a and $3f ld [hl], a ; store text ID in byte 1 of sprite entry pop hl - ld a, [$ff8d] + ldh a, [hLoadSpriteTemp1] bit 6, a jr nz, .trainerSprite bit 7, a @@ -2271,28 +2249,79 @@ LoadSprite:: .trainerSprite ld a, [hli] - ld [$ff8d], a ; save trainer class + ldh [hLoadSpriteTemp1], a ; save trainer class ld a, [hli] - ld [$ff8e], a ; save trainer number (within class) + ldh [hLoadSpriteTemp2], a ; save trainer number (within class) push hl ld hl, wMapSpriteExtraData add hl, bc - ld a, [$ff8d] + ldh a, [hLoadSpriteTemp1] ld [hli], a ; store trainer class in byte 0 of the entry - ld a, [$ff8e] + ldh a, [hLoadSpriteTemp2] ld [hl], a ; store trainer number in byte 1 of the entry pop hl ret .itemBallSprite ld a, [hli] - ld [$ff8d], a ; save item number + ldh [hLoadSpriteTemp1], a ; save item number push hl ld hl, wMapSpriteExtraData add hl, bc - ld a, [$ff8d] + ldh a, [hLoadSpriteTemp1] ld [hli], a ; store item number in byte 0 of the entry xor a ld [hl], a ; zero byte 1, since it is not used pop hl ret + +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/overworld_text.asm b/home/overworld_text.asm new file mode 100644 index 00000000..7ea23fe4 --- /dev/null +++ b/home/overworld_text.asm @@ -0,0 +1,31 @@ +TextScriptEndingText:: + text_end + +TextScriptEnd:: + ld hl, TextScriptEndingText + ret + +ExclamationText:: + text_far _ExclamationText + text_end + +GroundRoseText:: + text_far _GroundRoseText + text_end + +BoulderText:: + text_far _BoulderText + text_end + +MartSignText:: + text_far _MartSignText + text_end + +PokeCenterSignText:: + text_far _PokeCenterSignText + text_end + +PickUpItemText:: + text_asm + predef PickUpItem + jp TextScriptEnd diff --git a/home/palettes.asm b/home/palettes.asm new file mode 100644 index 00000000..3b944a03 --- /dev/null +++ b/home/palettes.asm @@ -0,0 +1,63 @@ +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 + call UpdateGBCPal_BGP + call UpdateGBCPal_OBP0 + call UpdateGBCPal_OBP1 + ret + +GBPalWhiteOut:: +; White out all palettes. + xor a + ldh [rBGP], a + ldh [rOBP0], a + ldh [rOBP1], a + call UpdateGBCPal_BGP + call UpdateGBCPal_OBP0 + call UpdateGBCPal_OBP1 + 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..53f98847 --- /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 -1 ; 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/pics.asm b/home/pics.asm new file mode 100644 index 00000000..f3b3f1b5 --- /dev/null +++ b/home/pics.asm @@ -0,0 +1,198 @@ +; 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:: + ld bc, wMonHeader + add hl, bc + ld a, [hli] + ld [wSpriteInputPtr], a ; fetch sprite input pointer + ld a, [hl] + ld [wSpriteInputPtr+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:: + push de + ld hl, wMonHFrontSprite - wMonHeader + call UncompressMonSprite + ld hl, wMonHSpriteDim + 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:: + push de + and $f + ldh [hSpriteWidth], 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) + ldh [hSpriteOffset], a + ld a, c + swap a + and $f + ld b, a + add a + add a + add a ; 8*tiles is height in bytes + ldh [hSpriteHeight], a + ld a, $7 + sub b ; 7-h ; skip for vertical center (in tiles, relative to current column) + ld b, a + ldh a, [hSpriteOffset] + 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) + ldh [hSpriteOffset], a + ld a, $0 + call SwitchSRAMBankAndLatchClockData + ld hl, sSpriteBuffer0 + call ZeroSpriteBuffer ; zero buffer 0 + ld de, sSpriteBuffer1 + ld hl, sSpriteBuffer0 + call AlignSpriteDataCentered ; copy and align buffer 1 to 0 (containing the MSB of the 2bpp sprite) + ld hl, sSpriteBuffer1 + call ZeroSpriteBuffer ; zero buffer 1 + ld de, sSpriteBuffer2 + ld hl, sSpriteBuffer1 + call AlignSpriteDataCentered ; copy and align buffer 2 to 1 (containing the LSB of the 2bpp sprite) + call PrepareRTCDataAndDisableSRAM + 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:: + ldh a, [hSpriteOffset] + ld b, $0 + ld c, a + add hl, bc + ldh a, [hSpriteWidth] +.columnLoop + push af + push hl + ldh a, [hSpriteHeight] + 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:: + 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:: + ld a, $0 + call SwitchSRAMBankAndLatchClockData + push de + ld hl, sSpriteBuffer2 + (SPRITEBUFFERSIZE - 1) ; destination: end of buffer 2 + ld de, sSpriteBuffer1 + (SPRITEBUFFERSIZE - 1) ; source 2: end of buffer 1 + ld bc, sSpriteBuffer0 + (SPRITEBUFFERSIZE - 1) ; source 1: end of buffer 0 + ld a, SPRITEBUFFERSIZE/2 ; $c4 + ldh [hSpriteInterlaceCounter], a +.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 + ldh a, [hSpriteInterlaceCounter] + dec a + ldh [hSpriteInterlaceCounter], a + jr nz, .interlaceLoop + ld a, [wSpriteFlipped] + and a + jr z, .notFlipped + ld bc, 2*SPRITEBUFFERSIZE + ld hl, sSpriteBuffer1 +.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, sSpriteBuffer1 + ld c, (2*SPRITEBUFFERSIZE)/16 ; $31, number of 16 byte chunks to be copied + ldh a, [hLoadedROMBank] + ld b, a + call CopyVideoData + jp PrepareRTCDataAndDisableSRAM diff --git a/home/pikachu.asm b/home/pikachu.asm index 336c80b3..c276b3d4 100755 --- a/home/pikachu.asm +++ b/home/pikachu.asm @@ -55,7 +55,7 @@ SpawnPikachu:: ld a, [hl] dec a swap a - ld [hTilePlayerStandingOn], a + ldh [hTilePlayerStandingOn], a homecall SpawnPikachu_ ; 3f:46d5 ret @@ -83,7 +83,7 @@ Pikachu_IsInArray:: GetPikachuMovementScriptByte:: push hl push bc - ld a, [H_LOADEDROMBANK] + ldh a, [hLoadedROMBank] push af ld a, [wPikachuMovementScriptBank] call BankswitchCommon @@ -105,7 +105,7 @@ GetPikachuMovementScriptByte:: ret ApplyPikachuMovementData:: - ld a, [H_LOADEDROMBANK] + ldh a, [hLoadedROMBank] ld b, a push af callbs ApplyPikachuMovementData_ diff --git a/home/play_time.asm b/home/play_time.asm index 47b2a690..d6c95fbd 100644 --- a/home/play_time.asm +++ b/home/play_time.asm @@ -1,10 +1,10 @@ -TrackPlayTime: +TrackPlayTime:: call CountDownIgnoreInputBitReset ld hl, wd47a bit 0, [hl] jr nz, .maxIGT - ld a,[wd732] - bit 0,a + ld a, [wd732] + bit 0, a ret z ld a, [wPlayTimeMaxed] and a @@ -66,6 +66,6 @@ CountDownIgnoreInputBitReset: ld [wd730], a ret z xor a - ld [hJoyPressed], a - ld [hJoyHeld], a + ldh [hJoyPressed], a + ldh [hJoyHeld], a ret diff --git a/home/pokemon.asm b/home/pokemon.asm new file mode 100644 index 00000000..3e08906f --- /dev/null +++ b/home/pokemon.asm @@ -0,0 +1,474 @@ +DrawHPBar:: +; 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 [wHPBarType]. + + 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, [wHPBarType] + 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 is 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 wLoadedMon +; loads base stats to wMonHeader +; INPUT: +; [wWhichPokemon] = index of pokemon within party/box +; [wMonDataLocation] = source +; 00: player's party +; 01: enemy's party +; 02: current box +; 03: daycare +; OUTPUT: +; [wcf91] = pokemon ID +; wLoadedMon = base address of pokemon data +; wMonHeader = base address of base stats +LoadMonData:: + jpfar LoadMonData_ + +OverwritewMoves:: +; Write c to [wMoves + b]. Unused. + ld hl, wMoves + ld e, b + ld d, 0 + add hl, de + ld a, c + ld [hl], a + ret + +LoadFlippedFrontSpriteByMonIndex:: + ld a, 1 + ld [wSpriteFlipped], a + +LoadFrontSpriteByMonIndex:: + 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 + ldh a, [hLoadedROMBank] + push af + switchbank CopyUncompressedPicToHL + xor a + ldh [hStartTileID], a + call CopyUncompressedPicToHL + xor a + ld [wSpriteFlipped], a + pop af + jp BankswitchCommon + + +PlayCry:: +; Play monster a's cry. + push bc + ld b, a + ld a, [wLowHealthAlarm] + push af + xor a + ld [wLowHealthAlarm], a + ld a, b + call GetCryData + call PlaySound + call WaitForSoundToFinish + pop af + ld [wLowHealthAlarm], a + pop bc + ret + +GetCryData:: +; 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 [wFrequencyModifier], a + ld a, [hl] + ld [wTempoModifier], a + call BankswitchBack + + ; Cry headers have 3 channels, + ; and start from index CRY_SFX_START, + ; so add 3 times the cry id. + ld a, b + ld c, CRY_SFX_START + rlca ; * 2 + add b + add c + ret + +DisplayPartyMenu:: + ldh a, [hTilesetType] + push af + xor a + ldh [hTilesetType], a + call GBPalWhiteOutWithDelay3 + call ClearSprites + call PartyMenuInit + call DrawPartyMenu + jp HandlePartyMenuInput + +GoBackToPartyMenu:: + ldh a, [hTilesetType] + push af + xor a + ldh [hTilesetType], a + call PartyMenuInit + call RedrawPartyMenu + jp HandlePartyMenuInput + +PartyMenuInit:: + ld a, 1 ; hardcoded bank + call BankswitchHome + call LoadHpBarAndStatusTilePatterns + ld hl, wd730 + set 6, [hl] ; turn off letter printing delay + xor a ; PLAYER_PARTY_DATA + ld [wMonDataLocation], a + ld [wMenuWatchMovingOutOfBounds], a + ld hl, wTopMenuItemY + inc a + ld [hli], a ; top menu item Y + xor a + ld [hli], a ; top menu item X + ld a, [wPartyAndBillsPCSavedMenuItem] + 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, [wForcePlayerToChooseMon] + and a + ld a, A_BUTTON | B_BUTTON + jr z, .next + xor a + ld [wForcePlayerToChooseMon], a + inc a ; a = A_BUTTON +.next + ld [hli], a ; menu watched keys + pop af + ld [hl], a ; old menu item ID + ret + +HandlePartyMenuInput:: + ld a, 1 + ld [wMenuWrappingEnabled], a + ld a, $40 + ld [wPartyMenuAnimMonEnabled], a + call HandleMenuInput_ + push af ; save hJoy5 OR wMenuWrapping enabled, if no inputs were selected within a certain period of time + bit 1, a ; was B button pressed? + ld a, $0 + ld [wPartyMenuAnimMonEnabled], a + ld a, [wCurrentMenuItem] + ld [wPartyAndBillsPCSavedMenuItem], a + jr nz, .asm_1258 + ld a, [wCurrentMenuItem] + ld [wWhichPokemon], a + callfar IsThisPartymonStarterPikachu_Party + jr nc, .asm_1258 + call CheckPikachuFollowingPlayer + jr nz, .asm_128f +.asm_1258 + pop af + call PlaceUnfilledArrowMenuCursor + ld b, a + ld hl, wd730 + res 6, [hl] ; turn on letter printing delay + ld a, [wMenuItemToSwap] + and a + jp nz, .swappingPokemon + pop af + ldh [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 +.asm_128f + pop af + ld hl, PartyMenuText_12cc + call PrintText + xor a + ld [wMenuItemToSwap], a + pop af + ldh [hTilesetType], a +.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 + farcall ErasePartyMenuCursors + xor a + ld [wMenuItemToSwap], a + ld [wPartyMenuTypeOrMessageID], a + call RedrawPartyMenu + jp HandlePartyMenuInput +.handleSwap + ld a, [wCurrentMenuItem] + ld [wWhichPokemon], a + farcall SwitchPartyMon + jp HandlePartyMenuInput + +PartyMenuText_12cc:: + text_far _SleepingPikachuText1 + text_end + +DrawPartyMenu:: + ld hl, DrawPartyMenu_ + jr DrawPartyMenuCommon + +RedrawPartyMenu:: + ld hl, RedrawPartyMenu_ + +DrawPartyMenuCommon:: + ld b, BANK(RedrawPartyMenu_) + jp Bankswitch + +; prints a pokemon's status condition +; INPUT: +; de = address of status condition +; hl = destination address +PrintStatusCondition:: + 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:: + homecall_jump_sf PrintStatusAilment + +; function to print pokemon level, leaving off the ":L" if the level is at least 100 +; INPUT: +; hl = destination address +; [wLoadedMonLevel] = level +PrintLevel:: + ld a, "<LV>" ; ":L" tile ID + ld [hli], a + ld c, 2 ; number of digits + ld a, [wLoadedMonLevel] ; level + cp 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 +; [wLoadedMonLevel] = level +PrintLevelFull:: + ld a, "<LV>" ; ":L" tile ID + ld [hli], a + ld c, 3 ; number of digits + ld a, [wLoadedMonLevel] ; level + +PrintLevelCommon:: + ld [wd11e], a + ld de, wd11e + ld b, LEFT_ALIGN | 1 ; 1 byte + jp PrintNumber + +GetwMoves:: +; Unused. Returns the move at index a from wMoves in a + ld hl, wMoves + ld c, a + ld b, 0 + add hl, bc + ld a, [hl] + ret + +; copies the base stat data of a pokemon to wMonHeader +; INPUT: +; [wd0b5] = pokemon ID +GetMonHeader:: + ldh a, [hLoadedROMBank] + push af + switchbank BaseStats + 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 FOSSIL_KABUTOPS ; Kabutops fossil + jr z, .specialID + ld de, GhostPic + cp MON_GHOST ; Ghost + jr z, .specialID + ld de, FossilAerodactylPic + ld b, $77 ; size of Aerodactyl fossil sprite + cp FOSSIL_AERODACTYL ; Aerodactyl fossil + jr z, .specialID + ;cp MEW + ;jr z, .mew + predef IndexToPokedex ; convert pokemon ID in [wd11e] to pokedex number + ld a, [wd11e] + dec a + ld bc, MonBaseStatsEnd - MonBaseStats + ld hl, BaseStats + call AddNTimes + ld de, wMonHeader + ld bc, MonBaseStatsEnd - MonBaseStats + call CopyData + jr .done +.specialID + ld hl, wMonHSpriteDim + ld [hl], b ; write sprite dimensions + inc hl + ld [hl], e ; write front sprite pointer + inc hl + ld [hl], d +.done + ld a, [wd0b5] + ld [wMonHIndex], a + pop af + ld [wd11e], a + pop hl + pop de + pop bc + pop af + call BankswitchCommon + ret + +; copy party pokemon's name to wcd6d +GetPartyMonName2:: + ld a, [wWhichPokemon] ; index within party + ld hl, wPartyMonNicks + +; this is called more often +GetPartyMonName:: + push hl + push bc + call SkipFixedLengthTextEntries ; add NAME_LENGTH to hl, a times + ld de, wcd6d + push de + ld bc, NAME_LENGTH + call CopyData + pop de + pop bc + pop hl + ret diff --git a/home/predef.asm b/home/predef.asm index dfa10772..a0889e0e 100644 --- a/home/predef.asm +++ b/home/predef.asm @@ -8,12 +8,12 @@ Predef:: ; A hack for LoadDestinationWarpPosition. ; See LoadTilesetHeader (predef $19). - ld a, [H_LOADEDROMBANK] + ldh a, [hLoadedROMBank] ld [wPredefParentBank], a push af ld a, BANK(GetPredefPointer) - ld [H_LOADEDROMBANK], a + ldh [hLoadedROMBank], a ld [MBC1RomBank], a call GetPredefPointer 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_bcd.asm b/home/print_bcd.asm new file mode 100644 index 00000000..38aedd3c --- /dev/null +++ b/home/print_bcd.asm @@ -0,0 +1,77 @@ +; 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:: + 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:: + 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 "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 diff --git a/home/print_num.asm b/home/print_num.asm new file mode 100644 index 00000000..e2628b1f --- /dev/null +++ b/home/print_num.asm @@ -0,0 +1,219 @@ +PrintNumber:: +; 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. + push bc + xor a + ldh [hPastLeadingZeros], a + ldh [hNumToPrint], a + ldh [hNumToPrint + 1], a + ld a, b + and $f + cp 1 + jr z, .byte + cp 2 + jr z, .word +.long + ld a, [de] + ldh [hNumToPrint], a + inc de + ld a, [de] + ldh [hNumToPrint + 1], a + inc de + ld a, [de] + ldh [hNumToPrint + 2], a + jr .start + +.word + ld a, [de] + ldh [hNumToPrint + 1], a + inc de + ld a, [de] + ldh [hNumToPrint + 2], a + jr .start + +.byte + ld a, [de] + ldh [hNumToPrint + 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 + ldh [hPowerOf10 + 0], a + +IF (\1) / $100 + ld a, \1 / $100 % $100 +ELSE + xor a +ENDC + ldh [hPowerOf10 + 1], a + + ld a, \1 / $1 % $100 + ldh [hPowerOf10 + 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 + ldh a, [hNumToPrint + 2] +.mod + cp 10 + jr c, .ok + sub 10 + inc c + jr .mod +.ok + + ld b, a + ldh a, [hPastLeadingZeros] + or c + ldh [hPastLeadingZeros], 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 + ldh a, [hPowerOf10] + ld b, a + ldh a, [hNumToPrint] + ldh [hSavedNumToPrint], a + cp b + jr c, .underflow0 + sub b + ldh [hNumToPrint], a + ldh a, [hPowerOf10 + 1] + ld b, a + ldh a, [hNumToPrint + 1] + ldh [hSavedNumToPrint + 1], a + cp b + jr nc, .noborrow1 + + ldh a, [hNumToPrint] + or 0 + jr z, .underflow1 + dec a + ldh [hNumToPrint], a + ldh a, [hNumToPrint + 1] +.noborrow1 + + sub b + ldh [hNumToPrint + 1], a + ldh a, [hPowerOf10 + 2] + ld b, a + ldh a, [hNumToPrint + 2] + ldh [hSavedNumToPrint + 2], a + cp b + jr nc, .noborrow2 + + ldh a, [hNumToPrint + 1] + and a + jr nz, .borrowed + + ldh a, [hNumToPrint] + and a + jr z, .underflow2 + dec a + ldh [hNumToPrint], a + xor a +.borrowed + + dec a + ldh [hNumToPrint + 1], a + ldh a, [hNumToPrint + 2] +.noborrow2 + sub b + ldh [hNumToPrint + 2], a + inc c + jr .loop + +.underflow2 + ldh a, [hSavedNumToPrint + 1] + ldh [hNumToPrint + 1], a +.underflow1 + ldh a, [hSavedNumToPrint] + ldh [hNumToPrint], a +.underflow0 + ldh a, [hPastLeadingZeros] + or c + jr z, .PrintLeadingZero + + ld a, "0" + add c + ld [hl], a + ldh [hPastLeadingZeros], a + ret + +.PrintLeadingZero: + bit 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 BIT_LEADING_ZEROES, d + jr nz, .inc + bit BIT_LEFT_ALIGN, d + jr z, .inc + ldh a, [hPastLeadingZeros] + and a + ret z +.inc + inc hl + ret 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..63a71d26 --- /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 + call 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..ae9f6544 --- /dev/null +++ b/home/reload_tiles.asm @@ -0,0 +1,67 @@ +; 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 + call BankswitchCommon + ret + +; reloads tileset tile patterns +ReloadTilesetTilePatterns:: + ldh a, [hLoadedROMBank] + push af + ld a, [wCurMap] + call SwitchToMapRomBank + call DisableLCD + call LoadTilesetTilePatternData + call EnableLCD + pop af + call BankswitchCommon + 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 + +PrinterSerial:: + homecall PrinterSerial_ + ret + +SerialFunction:: + ld a, [wPrinterConnectionOpen] + bit 0, a + ret z + ld a, [wPrinterOpcode] + and a + ret nz + ld hl, wOverworldMap + 650 + inc [hl] + ld a, [hl] + cp $6 + ret c + xor a + ld [hl], a + ld a, $0c + ld [wPrinterOpcode], a + ld a, $88 + ldh [rSB], a + ld a, $1 + ldh [rSC], a + ld a, START_TRANSFER_INTERNAL_CLOCK + ldh [rSC], a + ret + +; 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..2765d613 --- /dev/null +++ b/home/reset_player_sprite.asm @@ -0,0 +1,21 @@ +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 + call FillMemory + ret diff --git a/home/serial.asm b/home/serial.asm index 5fe7fb81..ef914933 100644 --- a/home/serial.asm +++ b/home/serial.asm @@ -6,46 +6,46 @@ Serial:: ld a, [wPrinterConnectionOpen] bit 0, a jp nz, PrinterSerial__ - ld a, [hSerialConnectionStatus] + ldh a, [hSerialConnectionStatus] inc a jr z, .connectionNotYetEstablished - ld a, [rSB] - ld [hSerialReceiveData], a - ld a, [hSerialSendData] - ld [rSB], a - ld a, [hSerialConnectionStatus] + ldh a, [rSB] + ldh [hSerialReceiveData], a + ldh a, [hSerialSendData] + ldh [rSB], a + ldh a, [hSerialConnectionStatus] cp USING_INTERNAL_CLOCK jr z, .done ; using external clock ld a, START_TRANSFER_EXTERNAL_CLOCK - ld [rSC], a + ldh [rSC], a jr .done .connectionNotYetEstablished - ld a, [rSB] - ld [hSerialReceiveData], a - ld [hSerialConnectionStatus], a + ldh a, [rSB] + ldh [hSerialReceiveData], a + ldh [hSerialConnectionStatus], a cp USING_INTERNAL_CLOCK jr z, .usingInternalClock ; using external clock xor a - ld [rSB], a + ldh [rSB], a ld a, $3 - ld [rDIV], a + ldh [rDIV], a .waitLoop - ld a, [rDIV] + ldh a, [rDIV] bit 7, a jr nz, .waitLoop ld a, START_TRANSFER_EXTERNAL_CLOCK - ld [rSC], a + ldh [rSC], a jr .done .usingInternalClock xor a - ld [rSB], a + ldh [rSB], a .done ld a, $1 - ld [hSerialReceivedNewData], a + ldh [hSerialReceivedNewData], a ld a, SERIAL_NO_DATA_BYTE - ld [hSerialSendData], a + ldh [hSerialSendData], a pop hl pop de pop bc @@ -57,10 +57,10 @@ Serial:: ; bc = length of data Serial_ExchangeBytes:: ld a, 1 - ld [hSerialIgnoringInitialData], a + ldh [hSerialIgnoringInitialData], a .loop ld a, [hl] - ld [hSerialSendData], a + ldh [hSerialSendData], a call Serial_ExchangeByte push bc ld b, a @@ -69,7 +69,7 @@ Serial_ExchangeBytes:: .waitLoop dec a jr nz, .waitLoop - ld a, [hSerialIgnoringInitialData] + ldh a, [hSerialIgnoringInitialData] and a ld a, b pop bc @@ -78,7 +78,7 @@ Serial_ExchangeBytes:: cp SERIAL_PREAMBLE_BYTE jr nz, .loop xor a - ld [hSerialIgnoringInitialData], a + ldh [hSerialIgnoringInitialData], a jr .loop .storeReceivedByte ld [de], a @@ -91,17 +91,17 @@ Serial_ExchangeBytes:: Serial_ExchangeByte:: xor a - ld [hSerialReceivedNewData], a - ld a, [hSerialConnectionStatus] + ldh [hSerialReceivedNewData], a + ldh a, [hSerialConnectionStatus] cp USING_INTERNAL_CLOCK jr nz, .loop ld a, START_TRANSFER_INTERNAL_CLOCK - ld [rSC], a + ldh [rSC], a .loop - ld a, [hSerialReceivedNewData] + ldh a, [hSerialReceivedNewData] and a jr nz, .ok - ld a, [hSerialConnectionStatus] + ldh a, [hSerialConnectionStatus] cp USING_EXTERNAL_CLOCK jr nz, .doNotIncrementUnknownCounter call IsUnknownCounterZero @@ -119,7 +119,7 @@ Serial_ExchangeByte:: jr nz, .loop jp SetUnknownCounterToFFFF .doNotIncrementUnknownCounter - ld a, [rIE] + ldh a, [rIE] and (1 << SERIAL) | (1 << TIMER) | (1 << LCD_STAT) | (1 << VBLANK) cp (1 << SERIAL) jr nz, .loop @@ -131,7 +131,7 @@ Serial_ExchangeByte:: dec a ld [wUnknownSerialCounter2 + 1], a jr nz, .loop - ld a, [hSerialConnectionStatus] + ldh a, [hSerialConnectionStatus] cp USING_EXTERNAL_CLOCK jr z, .ok ld a, 255 @@ -140,8 +140,8 @@ Serial_ExchangeByte:: jr nz, .waitLoop .ok xor a - ld [hSerialReceivedNewData], a - ld a, [rIE] + ldh [hSerialReceivedNewData], a + ldh a, [rIE] and (1 << SERIAL) | (1 << TIMER) | (1 << LCD_STAT) | (1 << VBLANK) sub (1 << SERIAL) jr nz, .skipReloadingUnknownCounter2 @@ -149,7 +149,7 @@ Serial_ExchangeByte:: ld a, $50 ld [wUnknownSerialCounter2 + 1], a .skipReloadingUnknownCounter2 - ld a, [hSerialReceiveData] + ldh a, [hSerialReceiveData] cp SERIAL_NO_DATA_BYTE ret nz call IsUnknownCounterZero @@ -167,13 +167,13 @@ Serial_ExchangeByte:: call IsUnknownCounterZero jr z, SetUnknownCounterToFFFF .done - ld a, [rIE] + ldh a, [rIE] and (1 << SERIAL) | (1 << TIMER) | (1 << LCD_STAT) | (1 << VBLANK) cp (1 << SERIAL) ld a, SERIAL_NO_DATA_BYTE ret z ld a, [hl] - ld [hSerialSendData], a + ldh [hSerialSendData], a call DelayFrame jp Serial_ExchangeByte @@ -206,18 +206,18 @@ Serial_ExchangeLinkMenuSelection:: ld de, wLinkMenuSelectionReceiveBuffer ld c, 2 ; number of bytes to save ld a, 1 - ld [hSerialIgnoringInitialData], a + ldh [hSerialIgnoringInitialData], a .loop call DelayFrame ld a, [hl] - ld [hSerialSendData], a + ldh [hSerialSendData], a call Serial_ExchangeByte ld b, a inc hl - ld a, [hSerialIgnoringInitialData] + ldh a, [hSerialIgnoringInitialData] and a ld a, 0 - ld [hSerialIgnoringInitialData], a + ldh [hSerialIgnoringInitialData], a jr nz, .loop ld a, b ld [de], a @@ -228,7 +228,7 @@ Serial_ExchangeLinkMenuSelection:: Serial_PrintWaitingTextAndSyncAndExchangeNybble:: call SaveScreenTilesToBuffer1 - callab PrintWaitingText + callfar PrintWaitingText call Serial_SyncAndExchangeNybble jp LoadScreenTilesFromBuffer1 @@ -276,20 +276,20 @@ Serial_ExchangeNybble:: call .doExchange ld a, [wSerialExchangeNybbleSendData] add $60 - ld [hSerialSendData], a - ld a, [hSerialConnectionStatus] + ldh [hSerialSendData], a + ldh a, [hSerialConnectionStatus] cp USING_INTERNAL_CLOCK jr nz, .doExchange ld a, START_TRANSFER_INTERNAL_CLOCK - ld [rSC], a + ldh [rSC], a .doExchange - ld a, [hSerialReceiveData] + ldh a, [hSerialReceiveData] ld [wSerialExchangeNybbleTempReceiveData], a and $f0 cp $60 ret nz xor a - ld [hSerialReceiveData], a + ldh [hSerialReceiveData], a ld a, [wSerialExchangeNybbleTempReceiveData] and $f ld [wSerialExchangeNybbleReceiveData], a @@ -297,21 +297,21 @@ Serial_ExchangeNybble:: Serial_SendZeroByte:: xor a - ld [hSerialSendData], a - ld a, [hSerialConnectionStatus] + ldh [hSerialSendData], a + ldh a, [hSerialConnectionStatus] cp USING_INTERNAL_CLOCK ret nz ld a, START_TRANSFER_INTERNAL_CLOCK - ld [rSC], a + ldh [rSC], a ret Serial_TryEstablishingExternallyClockedConnection:: ld a, ESTABLISH_CONNECTION_WITH_EXTERNAL_CLOCK - ld [rSB], a + ldh [rSB], a xor a - ld [hSerialReceiveData], a + ldh [hSerialReceiveData], a ld a, START_TRANSFER_EXTERNAL_CLOCK - ld [rSC], a + ldh [rSC], a ret PrinterSerial__:: diff --git a/home/start.asm b/home/start.asm new file mode 100644 index 00000000..379436ae --- /dev/null +++ b/home/start.asm @@ -0,0 +1,10 @@ +_Start:: + cp GBC + jr z, .gbc + xor a + jr .ok +.gbc + ld a, 1 +.ok + ldh [hGBC], a + jp Init diff --git a/home/start_menu.asm b/home/start_menu.asm new file mode 100755 index 00000000..bd1b24bf --- /dev/null +++ b/home/start_menu.asm @@ -0,0 +1,84 @@ +DisplayStartMenu:: + switchbank StartMenu_Pokedex ; also bank for other functions + ld a, [wWalkBikeSurfState] ; walking/biking/surfing + ld [wWalkBikeSurfStateCopy], a + ld a, SFX_START_MENU + call PlaySound + +RedisplayStartMenu:: + farcall DrawStartMenu +RedisplayStartMenu_DoNotDrawStartMenu:: + farcall PrintSafariZoneSteps ; print Safari Zone info, if in Safari Zone + call UpdateSprites +.loop + call HandleMenuInput + ld b, a +.checkIfUpPressed + bit 6, a ; was Up pressed? + jr z, .checkIfDownPressed + ld a, [wCurrentMenuItem] ; menu selection + and a + jr nz, .loop + ld a, [wLastMenuItem] + and a + jr nz, .loop +; if the player pressed tried to go past the top item, wrap around to the bottom + CheckEvent EVENT_GOT_POKEDEX + ld a, 6 ; there are 7 menu items with the pokedex, so the max index is 6 + jr nz, .wrapMenuItemId + dec a ; there are only 6 menu items without the pokedex +.wrapMenuItemId + ld [wCurrentMenuItem], a + call EraseMenuCursor + jr .loop +.checkIfDownPressed + bit 7, a + jr z, .buttonPressed +; if the player pressed tried to go past the bottom item, wrap around to the top + CheckEvent EVENT_GOT_POKEDEX + ld a, [wCurrentMenuItem] + ld c, 7 ; there are 7 menu items with the pokedex + jr nz, .checkIfPastBottom + dec c ; there are only 6 menu items without the pokedex +.checkIfPastBottom + cp c + jr nz, .loop +; the player went past the bottom, so wrap to the top + xor a + ld [wCurrentMenuItem], a + call EraseMenuCursor + jr .loop +.buttonPressed ; A, B, or Start button pressed + call PlaceUnfilledArrowMenuCursor + ld a, [wCurrentMenuItem] + ld [wBattleAndStartSavedMenuItem], a ; save current menu selection + ld a, b + and %00001010 ; was the Start button or B button pressed? + jp nz, CloseStartMenu + call SaveScreenTilesToBuffer2 ; copy background from wTileMap to wTileMapBackup2 + CheckEvent EVENT_GOT_POKEDEX + ld a, [wCurrentMenuItem] + jr nz, .displayMenuItem + inc a ; adjust position to account for missing pokedex menu item +.displayMenuItem + cp 0 + jp z, StartMenu_Pokedex + cp 1 + jp z, StartMenu_Pokemon + cp 2 + jp z, StartMenu_Item + cp 3 + jp z, StartMenu_TrainerInfo + cp 4 + jp z, StartMenu_SaveReset + cp 5 + jp z, StartMenu_Option + +; EXIT falls through to here +CloseStartMenu:: + call Joypad + ldh a, [hJoyPressed] + bit 0, a ; was A button newly pressed? + jr nz, CloseStartMenu + call LoadTextBoxTilePatterns + jp CloseTextDisplay diff --git a/home/text.asm b/home/text.asm index f78227eb..78d69138 100644 --- a/home/text.asm +++ b/home/text.asm @@ -5,9 +5,9 @@ TextBoxBorder:: push hl ld a, "┌" ld [hli], a - inc a ; ─ - call NPlaceChar - inc a ; ┐ + inc a ; "─" + call .PlaceChars + inc a ; "┐" ld [hl], a pop hl @@ -20,7 +20,7 @@ TextBoxBorder:: ld a, "│" ld [hli], a ld a, " " - call NPlaceChar + call .PlaceChars ld [hl], "│" pop hl @@ -33,11 +33,11 @@ TextBoxBorder:: ld a, "└" ld [hli], a ld a, "─" - call NPlaceChar + call .PlaceChars ld [hl], "┘" ret -NPlaceChar:: +.PlaceChars:: ; Place char a c times. ld d, c .loop @@ -48,21 +48,21 @@ NPlaceChar:: PlaceString:: push hl + PlaceNextChar:: ld a, [de] - cp "@" - jr nz, Char4ETest + jr nz, .NotTerminator ld b, h ld c, l pop hl ret -Char4ETest:: - cp $4E ; next - jr nz, .char4FTest +.NotTerminator + cp "<NEXT>" + jr nz, .NotNext ld bc, 2 * SCREEN_WIDTH - ld a, [hFlags_0xFFFA] + ldh a, [hFlagsFFFA] bit 2, a jr z, .ok ld bc, SCREEN_WIDTH @@ -70,142 +70,101 @@ Char4ETest:: pop hl add hl, bc push hl - jp PlaceNextChar_inc + jp NextChar -.char4FTest - cp $4F ; line - jr nz, .next3 +.NotNext + cp "<LINE>" + jr nz, .NotLine pop hl - coord hl, 1, 16 + hlcoord 1, 16 push hl - jp PlaceNextChar_inc + jp NextChar -.next3 ; Check against a dictionary -dict: macro -if \1 == 0 - and a -else - cp \1 -endc - jp z, \2 -endm +.NotLine - dict $00, Char00 ; error - dict $4C, Char4C ; autocont - dict $4B, Char4B ; cont_ - dict $51, Char51 ; para - dict $49, Char49 ; page - dict $52, Char52 ; player - dict $53, Char53 ; rival - dict $54, Char54 ; POKé - dict $5B, Char5B ; PC - dict $5E, Char5E ; ROCKET - dict $5C, Char5C ; TM - dict $5D, Char5D ; TRAINER - dict $55, Char55 ; cont - dict $56, Char56 ; 6 dots - dict $57, Char57 ; done - dict $58, Char58 ; prompt - dict $4A, Char4A ; PKMN - dict $5F, Char5F ; dex - dict $59, Char59 ; TARGET - dict $5A, Char5A ; USER +; Check against a dictionary + dict "<NULL>", NullChar + dict "<SCROLL>", _ContTextNoPause + dict "<_CONT>", _ContText + dict "<PARA>", Paragraph + dict "<PAGE>", PageChar + dict "<PLAYER>", PrintPlayerName + dict "<RIVAL>", PrintRivalName + dict "#", PlacePOKe + dict "<PC>", PCChar + dict "<ROCKET>", RocketChar + dict "<TM>", TMChar + dict "<TRAINER>", TrainerChar + dict "<CONT>", ContText + dict "<……>", SixDotsChar + dict "<DONE>", DoneText + dict "<PROMPT>", PromptText + dict "<PKMN>", PlacePKMN + dict "<DEXEND>", PlaceDexEnd + dict "<TARGET>", PlaceMoveTargetsName + dict "<USER>", PlaceMoveUsersName ld [hli], a call PrintLetterDelay -PlaceNextChar_inc:: + +NextChar:: inc de jp PlaceNextChar -Char00:: +NullChar:: ld b, h ld c, l pop hl - ld de, Char00Text + ld de, TextIDErrorText dec de ret -Char00Text:: ; “%d ERROR.” - TX_FAR _Char00Text - db "@" +TextIDErrorText:: ; "[hSpriteIndexOrTextID] ERROR." + text_far _TextIDErrorText + text_end -Char52:: ; player’s name +print_name: MACRO push de - ld de, wPlayerName - jr FinishDTE + ld de, \1 + jr PlaceCommandCharacter +ENDM -Char53:: ; rival’s name - push de - ld de, wRivalName - jr FinishDTE +PrintPlayerName:: print_name wPlayerName +PrintRivalName:: print_name wRivalName -Char5D:: ; TRAINER - push de - ld de, Char5DText - jr FinishDTE - -Char5C:: ; TM - push de - ld de, Char5CText - jr FinishDTE +TrainerChar:: print_name TrainerCharText +TMChar:: print_name TMCharText +PCChar:: print_name PCCharText +RocketChar:: print_name RocketCharText +PlacePOKe:: print_name PlacePOKeText +SixDotsChar:: print_name SixDotsCharText +PlacePKMN:: print_name PlacePKMNText -Char5B:: ; PC - push de - ld de, Char5BText - jr FinishDTE - -Char5E:: ; ROCKET - push de - ld de, Char5EText - jr FinishDTE - -Char54:: ; POKé - push de - ld de, Char54Text - jr FinishDTE - -Char56:: ; …… - push de - ld de, Char56Text - jr FinishDTE - -Char4A:: ; PKMN - push de - ld de, Char4AText - jr FinishDTE - -Char59:: -; depending on whose turn it is, print -; enemy active monster’s name, prefixed with “Enemy ” -; or -; player active monster’s name -; (like Char5A but flipped) - ld a, [H_WHOSETURN] +PlaceMoveTargetsName:: + ldh a, [hWhoseTurn] xor 1 - jr MonsterNameCharsCommon + jr PlaceMoveUsersName.place + +PlaceMoveUsersName:: + ldh a, [hWhoseTurn] -Char5A:: -; depending on whose turn it is, print -; player active monster’s name -; or -; enemy active monster’s name, prefixed with “Enemy ” - ld a, [H_WHOSETURN] -MonsterNameCharsCommon:: +.place: push de and a - jr nz, .Enemy - ld de, wBattleMonNick ; player active monster name - jr FinishDTE + jr nz, .enemy -.Enemy - ; print “Enemy ” - ld de, Char5AText + ld de, wBattleMonNick + jr PlaceCommandCharacter + +.enemy + ld de, EnemyText call PlaceString ld h, b ld l, c - ld de, wEnemyMonNick ; enemy active monster name + ld de, wEnemyMonNick + ; fallthrough -FinishDTE:: +PlaceCommandCharacter:: call PlaceString ld h, b ld l, c @@ -213,28 +172,20 @@ FinishDTE:: inc de jp PlaceNextChar -Char5CText:: - db "TM@" -Char5DText:: - db "TRAINER@" -Char5BText:: - db "PC@" -Char5EText:: - db "ROCKET@" -Char54Text:: - db "POKé@" -Char56Text:: - db "……@" -Char5AText:: - db "Enemy @" -Char4AText:: - db $E1, $E2, "@" ; PKMN +TMCharText:: db "TM@" +TrainerCharText:: db "TRAINER@" +PCCharText:: db "PC@" +RocketCharText:: db "ROCKET@" +PlacePOKeText:: db "POKé@" +SixDotsCharText:: db "……@" +EnemyText:: db "Enemy @" +PlacePKMNText:: db "<PK><MN>@" -Char55:: +ContText:: push de ld b, h ld c, l - ld hl, Char55Text + ld hl, ContCharText call TextCommandProcessor ld h, b ld l, c @@ -242,101 +193,99 @@ Char55:: inc de jp PlaceNextChar -Char55Text:: -; equivalent to Char4B - TX_FAR _Char55Text - db "@" +ContCharText:: + text_far _ContCharText + text_end -Char5F:: -; ends a Pokédex entry +PlaceDexEnd:: ld [hl], "." pop hl ret -Char58:: ; prompt +PromptText:: ld a, [wLinkState] cp LINK_STATE_BATTLING jp z, .ok ld a, "▼" - Coorda 18, 16 + ldcoord_a 18, 16 .ok call ProtectedDelay3 call ManualTextScroll ld a, " " - Coorda 18, 16 -Char57:: ; done + ldcoord_a 18, 16 + +DoneText:: pop hl - ld de, Char58Text + ld de, .stop dec de ret -Char58Text:: - db "@" +.stop: + text_end -Char51:: ; para +Paragraph:: push de ld a, "▼" - Coorda 18, 16 + ldcoord_a 18, 16 call ProtectedDelay3 call ManualTextScroll - coord hl, 1, 13 + hlcoord 1, 13 lb bc, 4, 18 call ClearScreenArea ld c, 20 call DelayFrames pop de - coord hl, 1, 14 - jp PlaceNextChar_inc + hlcoord 1, 14 + jp NextChar -Char49:: - ld a, [hFlags_0xFFFA] +PageChar:: + ldh a, [hFlagsFFFA] bit 3, a - jr z, .Char49 - ld a, $4e - jp Char4ETest + jr z, .pageChar + ld a, "<NEXT>" + jp PlaceNextChar.NotTerminator -.Char49 +.pageChar push de ld a, "▼" - Coorda 18, 16 + ldcoord_a 18, 16 call ProtectedDelay3 call ManualTextScroll - coord hl, 1, 10 + hlcoord 1, 10 lb bc, 7, 18 call ClearScreenArea ld c, 20 call DelayFrames pop de pop hl - coord hl, 1, 11 + hlcoord 1, 11 push hl - jp PlaceNextChar_inc + jp NextChar -Char4B:: +_ContText:: ld a, "▼" - Coorda 18, 16 + ldcoord_a 18, 16 call ProtectedDelay3 push de call ManualTextScroll pop de ld a, " " - Coorda 18, 16 - ;fall through -Char4C:: + ldcoord_a 18, 16 +_ContTextNoPause:: push de call ScrollTextUpOneLine call ScrollTextUpOneLine - coord hl, 1, 16 + hlcoord 1, 16 pop de - jp PlaceNextChar_inc + jp NextChar ; move both rows of text in the normal text box up one row ; always called twice in a row ; first time, copy the two rows of text to the "in between" rows that are usually emtpy ; second time, copy the bottom row of text into the top row of text ScrollTextUpOneLine:: - coord hl, 0, 14 ; top row of text - coord de, 0, 13 ; empty line above text + hlcoord 0, 14 ; top row of text + decoord 0, 13 ; empty line above text ld b, SCREEN_WIDTH * 3 .copyText ld a, [hli] @@ -344,7 +293,7 @@ ScrollTextUpOneLine:: inc de dec b jr nz, .copyText - coord hl, 1, 16 + hlcoord 1, 16 ld a, " " ld b, SCREEN_WIDTH - 2 .clearText @@ -352,7 +301,6 @@ ScrollTextUpOneLine:: dec b jr nz, .clearText - ; wait five frames ld b, 5 .WaitFrame call DelayFrame @@ -372,7 +320,7 @@ TextCommandProcessor:: push af set 1, a ld e, a - ld a, [$fff9] + ldh a, [hClearLetterPrintingDelayFlags] xor e ld [wLetterPrintingDelayFlags], a ld a, c @@ -382,18 +330,18 @@ TextCommandProcessor:: NextTextCommand:: ld a, [hli] - cp "@" ; terminator - jr nz, .doTextCommand + cp TX_END + jr nz, .TextCommand pop af ld [wLetterPrintingDelayFlags], a ret -.doTextCommand + +.TextCommand: push hl - cp $17 - jp z, TextCommand17 - cp $0e - jp nc, TextCommand0B ; if a != 0x17 and a >= 0xE, go to command 0xB -; if a < 0xE, use a jump table + cp TX_FAR + jp z, TextCommand_FAR + cp TX_SOUND_POKEDEX_RATING + jp nc, TextCommand_SOUND ld hl, TextCommandJumpTable push bc add a @@ -406,12 +354,8 @@ NextTextCommand:: ld l, a jp hl -; draw box -; 04AAAABBCC -; AAAA = address of upper left corner -; BB = height -; CC = width -TextCommand04:: +TextCommand_BOX:: +; draw a box (height, width) pop hl ld a, [hli] ld e, a @@ -428,9 +372,8 @@ TextCommand04:: pop hl jr NextTextCommand -; place string inline -; 00{string} -TextCommand00:: +TextCommand_START:: +; write text until "@" pop hl ld d, h ld e, l @@ -442,10 +385,8 @@ TextCommand00:: inc hl jr NextTextCommand -; place string from RAM -; 01AAAA -; AAAA = address of string -TextCommand01:: +TextCommand_RAM:: +; write text from a ram address (little endian) pop hl ld a, [hli] ld e, a @@ -458,13 +399,8 @@ TextCommand01:: pop hl jr NextTextCommand -; print BCD number -; 02AAAABB -; AAAA = address of BCD number -; BB -; bits 0-4 = length in bytes -; bits 5-7 = unknown flags -TextCommand02:: +TextCommand_BCD:: +; write bcd from address, typically ram pop hl ld a, [hli] ld e, a @@ -481,10 +417,8 @@ TextCommand02:: pop hl jr NextTextCommand -; repoint destination address -; 03AAAA -; AAAA = new destination address -TextCommand03:: +TextCommand_MOVE:: +; move to a new tile pop hl ld a, [hli] ld [wTextDest], a @@ -494,58 +428,47 @@ TextCommand03:: ld b, a jp NextTextCommand -; repoint destination to second line of dialogue text box -; 05 -; (no arguments) -TextCommand05:: +TextCommand_LOW:: +; write text at (1,16) pop hl - coord bc, 1, 16 ; address of second line of dialogue text box + bccoord 1, 16 ; second line of dialogue text box jp NextTextCommand -; blink arrow and wait for A or B to be pressed -; 06 -; (no arguments) -TextCommand06:: +TextCommand_PROMPT_BUTTON:: +; wait for button press; show arrow ld a, [wLinkState] cp LINK_STATE_BATTLING - jp z, TextCommand0D + jp z, TextCommand_WAIT_BUTTON ld a, "▼" - Coorda 18, 16 ; place down arrow in lower right corner of dialogue text box + ldcoord_a 18, 16 ; place down arrow in lower right corner of dialogue text box push bc call ManualTextScroll ; blink arrow and wait for A or B to be pressed pop bc ld a, " " - Coorda 18, 16 ; overwrite down arrow with blank space + ldcoord_a 18, 16 ; overwrite down arrow with blank space pop hl jp NextTextCommand -; scroll text up one line -; 07 -; (no arguments) -TextCommand07:: +TextCommand_SCROLL:: +; pushes text up two lines and sets the BC cursor to the border tile +; below the first character column of the text box. ld a, " " - Coorda 18, 16 ; place blank space in lower right corner of dialogue text box + ldcoord_a 18, 16 ; place blank space in lower right corner of dialogue text box call ScrollTextUpOneLine call ScrollTextUpOneLine pop hl - coord bc, 1, 16 ; address of second line of dialogue text box + bccoord 1, 16 ; second line of dialogue text box jp NextTextCommand -; execute asm inline -; 08{code} -TextCommand08:: +TextCommand_START_ASM:: +; run assembly code pop hl ld de, NextTextCommand - push de ; return address + push de jp hl -; print decimal number (converted from binary number) -; 09AAAABB -; AAAA = address of number -; BB -; bits 0-3 = how many digits to display -; bits 4-7 = how long the number is in bytes -TextCommand09:: +TextCommand_NUM:: +; print a number pop hl ld a, [hli] ld e, a @@ -569,45 +492,42 @@ TextCommand09:: pop hl jp NextTextCommand -; wait half a second if the user doesn't hold A or B -; 0A -; (no arguments) -TextCommand0A:: +TextCommand_PAUSE:: +; wait for button press or 30 frames push bc call Joypad - ld a, [hJoyHeld] + ldh a, [hJoyHeld] and A_BUTTON | B_BUTTON - jr nz, .skipDelay - ld c, 30 + jr nz, .done + ld c, 30 ; half a second call DelayFrames -.skipDelay +.done pop bc pop hl jp NextTextCommand -; plays sounds -; this actually handles various command ID's, not just 0B -; (no arguments) -TextCommand0B:: +TextCommand_SOUND:: +; play a sound effect from TextCommandSounds pop hl push bc dec hl ld a, [hli] - ld b, a ; b = command number that got us here + ld b, a ; b = text command number that got us here push hl ld hl, TextCommandSounds .loop ld a, [hli] cp b - jr z, .matchFound + jr z, .play inc hl jr .loop -.matchFound - cp $14 + +.play + cp TX_SOUND_CRY_PIKACHU jr z, .pokemonCry - cp $15 + cp TX_SOUND_CRY_PIDGEOT jr z, .pokemonCry - cp $16 + cp TX_SOUND_CRY_DEWGONG jr z, .pokemonCry ld a, [hl] call PlaySound @@ -615,6 +535,7 @@ TextCommand0B:: pop hl pop bc jp NextTextCommand + .pokemonCry push de ld a, [hl] @@ -624,95 +545,95 @@ TextCommand0B:: pop bc jp NextTextCommand -; format: text command ID, sound ID or cry ID TextCommandSounds:: - db $0B, SFX_GET_ITEM_1 - db $12, SFX_TURN_OFF_PC - db $0E, SFX_POKEDEX_RATING - db $0F, SFX_GET_ITEM_1 - db $10, SFX_GET_ITEM_2 - db $11, SFX_GET_KEY_ITEM - db $13, SFX_TRADE_MACHINE - db $14, PIKACHU ; used in OakSpeech - db $15, PIDGEOT ; used in SaffronCityText12 - db $16, DEWGONG ; unused? + db TX_SOUND_GET_ITEM_1, SFX_GET_ITEM_1 ; actually plays SFX_LEVEL_UP when the battle music engine is loaded + db TX_SOUND_CAUGHT_MON, SFX_CAUGHT_MON + db TX_SOUND_POKEDEX_RATING, SFX_POKEDEX_RATING ; unused + db TX_SOUND_GET_ITEM_1_DUPLICATE, SFX_GET_ITEM_1 ; unused + db TX_SOUND_GET_ITEM_2, SFX_GET_ITEM_2 + db TX_SOUND_GET_KEY_ITEM, SFX_GET_KEY_ITEM + db TX_SOUND_DEX_PAGE_ADDED, SFX_DEX_PAGE_ADDED + db TX_SOUND_CRY_PIKACHU, PIKACHU ; used in OakSpeech + db TX_SOUND_CRY_PIDGEOT, PIDGEOT ; used in SaffronCityText12 + db TX_SOUND_CRY_DEWGONG, DEWGONG ; unused -; draw ellipses -; 0CAA -; AA = number of ellipses to draw -TextCommand0C:: +TextCommand_DOTS:: +; wait for button press or 30 frames while printing "…"s pop hl ld a, [hli] ld d, a push hl ld h, b ld l, c + .loop ld a, "…" ld [hli], a push de call Joypad pop de - ld a, [hJoyHeld] ; joypad state + ldh a, [hJoyHeld] ; joypad state and A_BUTTON | B_BUTTON - jr nz, .skipDelay ; if so, skip the delay + jr nz, .next ; if so, skip the delay ld c, 10 call DelayFrames -.skipDelay +.next dec d jr nz, .loop + ld b, h ld c, l pop hl jp NextTextCommand -; wait for A or B to be pressed -; 0D -; (no arguments) -TextCommand0D:: +TextCommand_WAIT_BUTTON:: +; wait for button press; don't show arrow push bc - call ManualTextScroll ; wait for A or B to be pressed + call ManualTextScroll pop bc pop hl jp NextTextCommand -; process text commands in another ROM bank -; 17AAAABB -; AAAA = address of text commands -; BB = bank -TextCommand17:: +TextCommand_FAR:: +; write text from a different bank (little endian) pop hl - ld a, [H_LOADEDROMBANK] + ldh a, [hLoadedROMBank] push af + ld a, [hli] ld e, a ld a, [hli] ld d, a ld a, [hli] - ld [H_LOADEDROMBANK], a + + ldh [hLoadedROMBank], a ld [MBC1RomBank], a + push hl ld l, e ld h, d call TextCommandProcessor pop hl + pop af - ld [H_LOADEDROMBANK], a + ldh [hLoadedROMBank], a ld [MBC1RomBank], a jp NextTextCommand TextCommandJumpTable:: - dw TextCommand00 - dw TextCommand01 - dw TextCommand02 - dw TextCommand03 - dw TextCommand04 - dw TextCommand05 - dw TextCommand06 - dw TextCommand07 - dw TextCommand08 - dw TextCommand09 - dw TextCommand0A - dw TextCommand0B - dw TextCommand0C - dw TextCommand0D +; entries correspond to TX_* constants (see macros/scripts/text.asm) + dw TextCommand_START ; TX_START + dw TextCommand_RAM ; TX_RAM + dw TextCommand_BCD ; TX_BCD + dw TextCommand_MOVE ; TX_MOVE + dw TextCommand_BOX ; TX_BOX + dw TextCommand_LOW ; TX_LOW + dw TextCommand_PROMPT_BUTTON ; TX_PROMPT_BUTTON + dw TextCommand_SCROLL ; TX_SCROLL + dw TextCommand_START_ASM ; TX_START_ASM + dw TextCommand_NUM ; TX_NUM + dw TextCommand_PAUSE ; TX_PAUSE + dw TextCommand_SOUND ; TX_SOUND_GET_ITEM_1 (also handles other TX_SOUND_* commands) + dw TextCommand_DOTS ; TX_DOTS + dw TextCommand_WAIT_BUTTON ; TX_WAIT_BUTTON + ; greater TX_* constants are handled directly by NextTextCommand diff --git a/home/text_script.asm b/home/text_script.asm new file mode 100644 index 00000000..1847e23b --- /dev/null +++ b/home/text_script.asm @@ -0,0 +1,225 @@ +; this function is used to display sign messages, sprite dialog, etc. +; INPUT: [hSpriteIndexOrTextID] = sprite ID or text ID +DisplayTextID:: + ldh a, [hLoadedROMBank] + push af + farcall DisplayTextIDInit ; initialization + ld hl, wTextPredefFlag + bit 0, [hl] + res 0, [hl] + jr nz, .skipSwitchToMapBank + ld a, [wCurMap] + call SwitchToMapRomBank +.skipSwitchToMapBank + ld a, 30 ; half a second + ldh [hFrameCounter], a ; used as joypad poll timer + ld hl, wMapTextPtr + ld a, [hli] + ld h, [hl] + ld l, a ; hl = map text pointer + ld d, $00 + ldh a, [hSpriteIndexOrTextID] ; text ID + ld [wSpriteIndex], a + + dict TEXT_START_MENU, DisplayStartMenu + dict TEXT_PIKACHU_ANIM, DisplayPikachuEmotion + dict TEXT_SAFARI_GAME_OVER, DisplaySafariGameOverText + dict TEXT_MON_FAINTED, DisplayPokemonFaintedText + dict TEXT_BLACKED_OUT, DisplayPlayerBlackedOutText + dict TEXT_REPEL_WORE_OFF, DisplayRepelWoreOffText + + ld a, [wNumSprites] + ld e, a + ldh a, [hSpriteIndexOrTextID] ; sprite ID + cp e + jr z, .spriteHandling + jr nc, .skipSpriteHandling +.spriteHandling +; get the text ID of the sprite + push hl + ;push de + ;push bc + ;farcall UpdateSpriteFacingOffsetAndDelayMovement ; update the graphics of the sprite the player is talking to (to face the right direction) + ;pop bc + ;pop de + ld hl, wMapSpriteData ; NPC text entries + ldh a, [hSpriteIndexOrTextID] + dec a + add a + ld e, a + ld d, $0 + add hl, de + 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 + ld d, $0 + add hl, de + 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 + +dict2: MACRO + cp \1 + jr nz, .not\@ + \2 + jr AfterDisplayingTextID +.not\@ +ENDM + + dict TX_SCRIPT_MART, DisplayPokemartDialogue + dict TX_SCRIPT_POKECENTER_NURSE, DisplayPokemonCenterDialogue + dict TX_SCRIPT_PLAYERS_PC, TextScript_ItemStoragePC + dict TX_SCRIPT_BILLS_PC, TextScript_BillsPC + dict TX_SCRIPT_POKECENTER_PC, TextScript_PokemonCenterPC + dict2 TX_SCRIPT_VENDING_MACHINE, farcall VendingMachineMenu + dict TX_SCRIPT_PRIZE_VENDOR, TextScript_GameCornerPrizeMenu + dict2 TX_SCRIPT_CABLE_CLUB_RECEPTIONIST, callfar CableClubNPC + + call PrintText_NoCreatingTextBox ; display the text + ld a, [wDoNotWaitForButtonPressAfterDisplayingText] + and a + jr nz, HoldTextDisplayOpen + +AfterDisplayingTextID:: + ld a, [wEnteringCableClub] + 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:: + call Joypad + ldh a, [hJoyHeld] + bit 0, a ; is the A button being pressed? + jr nz, HoldTextDisplayOpen + +CloseTextDisplay:: + ld a, [wCurMap] + call SwitchToMapRomBank + ld a, $90 + ldh [hWY], a ; move the window off the screen + call DelayFrame + call LoadGBPal + xor a + ldh [hAutoBGTransferEnabled], 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, wSprite01StateData2OrigFacingDirection + ld c, $0f + ld de, $10 +.restoreSpriteFacingDirectionLoop + ld a, [hl] ; x#SPRITESTATEDATA2_ORIGFACINGDIRECTION + dec h + ld [hl], a ; [x#SPRITESTATEDATA1_FACINGDIRECTION] + inc h + add hl, de + dec c + jr nz, .restoreSpriteFacingDirectionLoop + call InitMapSprites ; reload sprite tile pattern data (since it was partially overwritten by text tile patterns) + ld hl, wFontLoaded + res 0, [hl] + ld a, [wd732] + bit 3, a ; used fly warp + call z, LoadPlayerSpriteGraphics + call LoadCurrentMapView + pop af + call BankswitchCommon + jp UpdateSprites + +DisplayPokemartDialogue:: + push hl + ld hl, PokemartGreetingText + call PrintText + pop hl + inc hl + call LoadItemList + ld a, PRICEDITEMLISTMENU + ld [wListMenuID], a + homecall DisplayPokemartDialogue_ + jp AfterDisplayingTextID + +PokemartGreetingText:: + text_far _PokemartGreetingText + text_end + +LoadItemList:: + ld a, 1 + ld [wUpdateSpritesEnabled], a + ld a, h + ld [wItemListPointer], a + ld a, l + ld [wItemListPointer + 1], a + ld de, wItemList +.loop + ld a, [hli] + ld [de], a + inc de + cp $ff + jr nz, .loop + ret + +DisplayPokemonCenterDialogue:: +; zeroing these doesn't appear to serve any purpose + xor a + ldh [hItemPrice], a + ldh [hItemPrice + 1], a + ldh [hItemPrice + 2], a + + inc hl + homecall DisplayPokemonCenterDialogue_ + jp AfterDisplayingTextID + +DisplaySafariGameOverText:: + callfar PrintSafariGameOverText + jp AfterDisplayingTextID + +DisplayPokemonFaintedText:: + ld hl, PokemonFaintedText + call PrintText + jp AfterDisplayingTextID + +PokemonFaintedText:: + text_far _PokemonFaintedText + text_end + +DisplayPlayerBlackedOutText:: + ld hl, PlayerBlackedOutText + call PrintText + ld a, [wd732] + res 5, a ; reset forced to use bike bit + ld [wd732], a + CheckEvent EVENT_IN_SAFARI_ZONE + jr z, .didnotblackoutinsafari + xor a + ld [wNumSafariBalls], a + ld [wSafariSteps], a + ld [wSafariSteps + 1], a + EventFlagAddressa EVENT_IN_SAFARI_ZONE + ld [wcf0d], a + ld [wSafariZoneGateCurScript], a +.didnotblackoutinsafari + jp HoldTextDisplayOpen + +PlayerBlackedOutText:: + text_far _PlayerBlackedOutText + text_end + +DisplayRepelWoreOffText:: + ld hl, RepelWoreOffText + call PrintText + jp AfterDisplayingTextID + +RepelWoreOffText:: + text_far _RepelWoreOffText + text_end + +DisplayPikachuEmotion:: + callfar TalkToPikachu + jp CloseTextDisplay diff --git a/home/textbox.asm b/home/textbox.asm new file mode 100644 index 00000000..f95c72c7 --- /dev/null +++ b/home/textbox.asm @@ -0,0 +1,7 @@ +; function to draw various text boxes +; INPUT: +; [wTextBoxID] = text box ID +; b, c = y, x cursor position (TWO_OPTION_MENU only) +DisplayTextBoxID:: + homecall_sf DisplayTextBoxID_ + ret diff --git a/home/tilemap.asm b/home/tilemap.asm new file mode 100644 index 00000000..ea247763 --- /dev/null +++ b/home/tilemap.asm @@ -0,0 +1,45 @@ +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 + jp CopyData + +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 + jp CopyData + +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/trainers.asm b/home/trainers.asm new file mode 100644 index 00000000..f407652a --- /dev/null +++ b/home/trainers.asm @@ -0,0 +1,426 @@ +; stores hl in [wTrainerHeaderPtr] +StoreTrainerHeaderPointer:: + ld a, h + ld [wTrainerHeaderPtr], a + ld a, l + ld [wTrainerHeaderPtr+1], a + ret + +; executes the current map script from the function pointer array provided in de. +; a: map script index to execute (unless overridden by [wd733] bit 4) +; hl: trainer header pointer +ExecuteCurMapScriptInTable:: + push af + push de + call StoreTrainerHeaderPointer + pop hl + pop af + push hl + ld hl, wFlags_D733 + bit 4, [hl] + res 4, [hl] + jr z, .useProvidedIndex ; test if map script index was overridden manually + ld a, [wCurMapScript] +.useProvidedIndex + pop hl + ld [wCurMapScript], a + call CallFunctionInTable + ld a, [wCurMapScript] + ret + +LoadGymLeaderAndCityName:: + push de + ld de, wGymCityName + ld bc, $11 + call CopyData ; load city name + pop hl + ld de, wGymLeaderName + ld bc, NAME_LENGTH + jp CopyData ; load gym leader name + +; reads specific information from trainer header (pointed to at wTrainerHeaderPtr) +; 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:: + push de + push af + ld d, $0 + ld e, a + ld hl, wTrainerHeaderPtr + 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:: + 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, FLAG_TEST + 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 + 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, wFlags_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, wCurMapScript + 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:: + call CheckForEngagingTrainers + ld a, [wSpriteIndex] + cp $ff + jr nz, .trainerEngaging +.trainerNotEngaging + xor a + ld [wSpriteIndex], a + ld [wTrainerHeaderFlagBit], a + ret +.trainerEngaging + ld hl, wFlags_D733 + set 3, [hl] + ld [wEmotionBubbleSpriteIndex], a + xor a ; EXCLAMATION_BUBBLE + ld [wWhichEmotionBubble], a + predef EmotionBubble + ld a, D_RIGHT | D_LEFT | D_UP | D_DOWN + ld [wJoyIgnore], a + xor a + ldh [hJoyHeld], a + call TrainerWalkUpToPlayer_Bank0 + ld hl, wCurMapScript + 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:: + 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] + ldh [hSpriteIndexOrTextID], a + call DisplayTextID + ; fall through + +StartTrainerBattle:: + xor a + ld [wJoyIgnore], a + call InitBattleEnemyParameters + ld hl, wd72d + set 6, [hl] + set 7, [hl] + ld hl, wd72e + set 1, [hl] + ld hl, wCurMapScript + inc [hl] ; increment map script index (next script function is usually EndTrainerBattle) + ret + +EndTrainerBattle:: + ld hl, wCurrentMapScriptFlags + 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, [wIsInBattle] + cp $ff + jp z, ResetButtonPressedAndMapScript + ld a, $2 + call ReadTrainerHeaderInfo + ld a, [wTrainerHeaderFlagBit] + ld c, a + ld b, FLAG_SET + call TrainerFlagAction ; flag trainer as fought + ld a, [wEnemyMonOrTrainerClass] + cp OPP_ID_OFFSET + jr nc, .skipRemoveSprite ; test if trainer was fought (in that case skip removing the corresponding sprite) + ld hl, wMissableObjectList + ld de, $2 + ld a, [wSpriteIndex] + call IsInArray ; search for sprite ID + inc hl + ld a, [hl] + ld [wMissableObjectIndex], a ; load corresponding missable object index and remove it + predef HideObject +.skipRemoveSprite + ld hl, wd730 + bit 4, [hl] + res 4, [hl] + ret nz + +ResetButtonPressedAndMapScript:: + xor a + ld [wJoyIgnore], a + ldh [hJoyHeld], a + ldh [hJoyPressed], a + ldh [hJoyReleased], a + ld [wCurMapScript], a ; reset battle status + ret + +; calls TrainerWalkUpToPlayer +TrainerWalkUpToPlayer_Bank0:: + farjp TrainerWalkUpToPlayer + +; sets opponent type and mon set/lvl based on the engaging trainer data +InitBattleEnemyParameters:: + ld a, [wEngagedTrainerClass] + ld [wCurOpponent], a + ld [wEnemyMonOrTrainerClass], a + cp OPP_ID_OFFSET + ld a, [wEngagedTrainerSet] + jr c, .noTrainer + ld [wTrainerNo], a + ret +.noTrainer + ld [wCurEnemyLVL], a + ret + +GetSpritePosition1:: + ld hl, _GetSpritePosition1 + jr SpritePositionBankswitch + +GetSpritePosition2:: + ld hl, _GetSpritePosition2 + jr SpritePositionBankswitch + +SetSpritePosition1:: + ld hl, _SetSpritePosition1 + jr SpritePositionBankswitch + +SetSpritePosition2:: + ld hl, _SetSpritePosition2 +SpritePositionBankswitch:: + ld b, BANK(_GetSpritePosition1) ; BANK(_GetSpritePosition2), BANK(_SetSpritePosition1), BANK(_SetSpritePosition2) + jp Bankswitch ; indirect jump to one of the four functions + +CheckForEngagingTrainers:: + 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 -1 + ret z + ld a, $2 + call ReadTrainerHeaderInfo ; read trainer flag's byte ptr + ld b, FLAG_TEST + ld a, [wTrainerHeaderFlagBit] + ld c, a + call TrainerFlagAction ; read trainer flag + ld a, c + and a ; has the trainer already been defeated? + jr nz, .continue + 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 + predef TrainerEngage + pop de + pop hl + ld a, [wTrainerSpriteOffset] + and a + ret nz ; break if the trainer is engaging +.continue + 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:: + ldh a, [hLoadedROMBank] + 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:: + ld hl, wMapSpriteExtraData + 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 [wEngagedTrainerSet], a + jp PlayTrainerMusic + +PrintEndBattleText:: + push hl + ld hl, wd72d + bit 7, [hl] + res 7, [hl] + pop hl + ret z + ldh a, [hLoadedROMBank] + push af + ld a, [wEndBattleTextRomBank] + ldh [hLoadedROMBank], a + ld [MBC1RomBank], a + push hl + farcall SaveTrainerName + ld hl, TrainerEndBattleText + call PrintText + pop hl + pop af + ldh [hLoadedROMBank], a + ld [MBC1RomBank], a + farcall FreezeEnemyTrainerSprite + jp WaitForSoundToFinish + +GetSavedEndBattleTextPointer:: + 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:: + text_far _TrainerNameText + text_asm + call GetSavedEndBattleTextPointer + call TextCommandProcessor + jp TextScriptEnd + +PlayTrainerMusic:: + ld a, [wEngagedTrainerClass] + cp OPP_RIVAL1 + ret z + cp OPP_RIVAL2 + ret z + cp OPP_RIVAL3 + ret z + ld a, [wGymLeaderNo] + and a + ret nz + xor a + ld [wAudioFadeOutControl], a + call StopAllMusic + ld a, BANK(Music_MeetEvilTrainer) + ld [wAudioROMBank], a + ld [wAudioSavedROMBank], 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 [wNewSoundID], a + jp PlaySound + +INCLUDE "data/trainers/encounter_types.asm" diff --git a/home/trainers2.asm b/home/trainers2.asm new file mode 100644 index 00000000..78fb6463 --- /dev/null +++ b/home/trainers2.asm @@ -0,0 +1,54 @@ +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 + call IsFightingJessieJames + jp BankswitchBack +.linkBattle + ld hl, wTrainerPicPointer + ld de, RedPicFront + ld [hl], e + inc hl + ld [hl], d + ret + +IsFightingJessieJames:: + ld a, [wTrainerClass] + cp ROCKET + ret nz + ld a, [wTrainerNo] + cp $2a + ret c + ld de, JessieJamesPic + cp $2e + jr c, .dummy + ld de, JessieJamesPic ; possibly meant to add another pic +.dummy + ld hl, wTrainerPicPointer + ld a, e + ld [hli], a + ld [hl], d + ret + +GetTrainerName:: + farjp GetTrainerName_ diff --git a/home/pic.asm b/home/uncompress.asm index 236efe9d..182821aa 100644 --- a/home/pic.asm +++ b/home/uncompress.asm @@ -2,11 +2,11 @@ ; bank is given in a, sprite input stream is pointed to in wSpriteInputPtr UncompressSpriteData:: ld b, a - ld a, [H_LOADEDROMBANK] + ldh a, [hLoadedROMBank] push af ld a, b call BankswitchCommon - ld a,$0 + ld a, $0 call SwitchSRAMBankAndLatchClockData call _UncompressSpriteData call PrepareRTCDataAndDisableSRAM @@ -17,8 +17,8 @@ UncompressSpriteData:: ; initializes necessary data to load a sprite and runs UncompressSpriteDataLoop _UncompressSpriteData:: ld hl, sSpriteBuffer1 - ld c, (2*SPRITEBUFFERSIZE) % $100 - ld b, (2*SPRITEBUFFERSIZE) / $100 + ld c, LOW(2 * SPRITEBUFFERSIZE) + ld b, HIGH(2 * SPRITEBUFFERSIZE) xor a call FillMemory ; clear sprite buffer 1 and 2 ld a, $1 diff --git a/home/update_sprites.asm b/home/update_sprites.asm new file mode 100644 index 00000000..0c32dad8 --- /dev/null +++ b/home/update_sprites.asm @@ -0,0 +1,15 @@ +UpdateSprites:: + ld a, [wUpdateSpritesEnabled] + dec a + ret nz + ldh a, [hLoadedROMBank] + push af + switchbank _UpdateSprites + ld a, $ff + ld [wUpdateSpritesEnabled], a + call _UpdateSprites + ld a, $1 + ld [wUpdateSpritesEnabled], a + pop af + call BankswitchCommon + ret diff --git a/home/vblank.asm b/home/vblank.asm index 764e5277..ed39714f 100644 --- a/home/vblank.asm +++ b/home/vblank.asm @@ -5,24 +5,24 @@ VBlank:: push de push hl - ld a, [rVBK] ; vram bank + ldh a, [rVBK] ; vram bank push af xor a - ld [rVBK], a ; reset vram bank to 0 + ldh [rVBK], a ; reset vram bank to 0 - ld a, [H_LOADEDROMBANK] + ldh a, [hLoadedROMBank] ld [wVBlankSavedROMBank], a - ld a, [hSCX] - ld [rSCX], a - ld a, [hSCY] - ld [rSCY], a + ldh a, [hSCX] + ldh [rSCX], a + ldh a, [hSCY] + ldh [rSCY], a ld a, [wDisableVBlankWYUpdate] and a jr nz, .ok - ld a, [hWY] - ld [rWY], a + ldh a, [hWY] + ldh [rWY], a .ok call AutoBgMapTransfer @@ -31,9 +31,9 @@ VBlank:: call VBlankCopy call VBlankCopyDouble call UpdateMovingBgTiles - call $ff80 ; hOAMDMA + call hDMARoutine ld a, BANK(PrepareOAMData) - ld [H_LOADEDROMBANK], a + ldh [hLoadedROMBank], a ld [MBC1RomBank], a call PrepareOAMData @@ -43,18 +43,18 @@ VBlank:: call Random call ReadJoypad - ld a, [H_VBLANKOCCURRED] + ldh a, [hVBlankOccurred] and a jr z, .skipZeroing xor a - ld [H_VBLANKOCCURRED], a + ldh [hVBlankOccurred], a .skipZeroing - ld a, [H_FRAMECOUNTER] + ldh a, [hFrameCounter] and a jr z, .skipDec dec a - ld [H_FRAMECOUNTER], a + ldh [hFrameCounter], a .skipDec call FadeOutAudio @@ -65,11 +65,11 @@ VBlank:: call SerialFunction ld a, [wVBlankSavedROMBank] - ld [H_LOADEDROMBANK], a + ldh [hLoadedROMBank], a ld [MBC1RomBank], a pop af - ld [rVBK],a + ldh [rVBK], a pop hl pop de @@ -85,10 +85,10 @@ DelayFrame:: NOT_VBLANKED EQU 1 ld a, NOT_VBLANKED - ld [H_VBLANKOCCURRED], a + ldh [hVBlankOccurred], a .halt halt - ld a, [H_VBLANKOCCURRED] + ldh a, [hVBlankOccurred] and a jr nz, .halt ret diff --git a/home/vcopy.asm b/home/vcopy.asm index bec679c4..9b6efb3a 100644 --- a/home/vcopy.asm +++ b/home/vcopy.asm @@ -40,19 +40,19 @@ ClearBgMap:: ; However, this function is also called repeatedly to redraw the whole screen ; when necessary. It is also used in trade animation and elevator code. RedrawRowOrColumn:: - ld a, [hRedrawRowOrColumnMode] + ldh a, [hRedrawRowOrColumnMode] and a ret z ld b, a xor a - ld [hRedrawRowOrColumnMode], a + ldh [hRedrawRowOrColumnMode], a dec b jr nz, .redrawRow .redrawColumn ld hl, wRedrawRowOrColumnSrcTiles - ld a, [hRedrawRowOrColumnDest] + ldh a, [hRedrawRowOrColumnDest] ld e, a - ld a, [hRedrawRowOrColumnDest + 1] + ldh a, [hRedrawRowOrColumnDest + 1] ld d, a ld c, SCREEN_HEIGHT .loop1 @@ -75,13 +75,13 @@ RedrawRowOrColumn:: dec c jr nz, .loop1 xor a - ld [hRedrawRowOrColumnMode], a + ldh [hRedrawRowOrColumnMode], a ret .redrawRow ld hl, wRedrawRowOrColumnSrcTiles - ld a, [hRedrawRowOrColumnDest] + ldh a, [hRedrawRowOrColumnDest] ld e, a - ld a, [hRedrawRowOrColumnDest + 1] + ldh a, [hRedrawRowOrColumnDest + 1] ld d, a push de call .DrawHalf ; draw upper half @@ -120,59 +120,59 @@ RedrawRowOrColumn:: ; the above function, RedrawRowOrColumn, is used when walking to ; improve efficiency. AutoBgMapTransfer:: - ld a, [H_AUTOBGTRANSFERENABLED] + ldh a, [hAutoBGTransferEnabled] and a ret z - ld [H_SPTEMP], sp ; save stack pointer - ld a, [H_AUTOBGTRANSFERPORTION] + ld [hSPTemp], sp ; save stack pointer + ldh a, [hAutoBGTransferPortion] and a jr z, .transferTopThird dec a jr z, .transferMiddleThird .transferBottomThird - coord hl, 0, 12 + hlcoord 0, 12 ld sp, hl - ld a, [H_AUTOBGTRANSFERDEST + 1] + ldh a, [hAutoBGTransferDest + 1] ld h, a - ld a, [H_AUTOBGTRANSFERDEST] + ldh a, [hAutoBGTransferDest] ld l, a ld de, (12 * 32) add hl, de xor a ; TRANSFERTOP jr .doTransfer .transferTopThird - coord hl, 0, 0 + hlcoord 0, 0 ld sp, hl - ld a, [H_AUTOBGTRANSFERDEST + 1] + ldh a, [hAutoBGTransferDest + 1] ld h, a - ld a, [H_AUTOBGTRANSFERDEST] + ldh a, [hAutoBGTransferDest] ld l, a ld a, TRANSFERMIDDLE jr .doTransfer .transferMiddleThird - coord hl, 0, 6 + hlcoord 0, 6 ld sp, hl - ld a, [H_AUTOBGTRANSFERDEST + 1] + ldh a, [hAutoBGTransferDest + 1] ld h, a - ld a, [H_AUTOBGTRANSFERDEST] + ldh a, [hAutoBGTransferDest] ld l, a ld de, (6 * 32) add hl, de ld a, TRANSFERBOTTOM .doTransfer - ld [H_AUTOBGTRANSFERPORTION], a ; store next portion + ldh [hAutoBGTransferPortion], a ; store next portion ld b, 6 TransferBgRows:: ; unrolled loop and using pop for speed - rept 20 / 2 - 1 + REPT 20 / 2 - 1 pop de ld [hl], e inc l ld [hl], d inc l - endr + ENDR pop de ld [hl], e @@ -188,68 +188,68 @@ TransferBgRows:: dec b jr nz, TransferBgRows - ld a, [H_SPTEMP] + ldh a, [hSPTemp] ld l, a - ld a, [H_SPTEMP + 1] + ldh a, [hSPTemp + 1] ld h, a ld sp, hl ret -; Copies [H_VBCOPYBGNUMROWS] rows from H_VBCOPYBGSRC to H_VBCOPYBGDEST. -; If H_VBCOPYBGSRC is XX00, the transfer is disabled. +; Copies [hVBlankCopyBGNumRows] rows from hVBlankCopyBGSource to hVBlankCopyBGDest. +; If hVBlankCopyBGSource is XX00, the transfer is disabled. VBlankCopyBgMap:: - ld a, [H_VBCOPYBGSRC] ; doubles as enabling byte + ldh a, [hVBlankCopyBGSource] ; doubles as enabling byte and a ret z - ld [H_SPTEMP], sp ; save stack pointer - ld a, [H_VBCOPYBGSRC] + ld [hSPTemp], sp ; save stack pointer + ldh a, [hVBlankCopyBGSource] ld l, a - ld a, [H_VBCOPYBGSRC + 1] + ldh a, [hVBlankCopyBGSource + 1] ld h, a ld sp, hl - ld a, [H_VBCOPYBGDEST] + ldh a, [hVBlankCopyBGDest] ld l, a - ld a, [H_VBCOPYBGDEST + 1] + ldh a, [hVBlankCopyBGDest + 1] ld h, a - ld a, [H_VBCOPYBGNUMROWS] + ldh a, [hVBlankCopyBGNumRows] ld b, a xor a - ld [H_VBCOPYBGSRC], a ; disable transfer so it doesn't continue next V-blank + ldh [hVBlankCopyBGSource], a ; disable transfer so it doesn't continue next V-blank jr TransferBgRows VBlankCopyDouble:: -; Copy [H_VBCOPYDOUBLESIZE] 1bpp tiles -; from H_VBCOPYDOUBLESRC to H_VBCOPYDOUBLEDEST. +; Copy [hVBlankCopyDoubleSize] 1bpp tiles +; from hVBlankCopyDoubleSource to hVBlankCopyDoubleDest. ; While we're here, convert to 2bpp. ; The process is straightforward: ; copy each byte twice. - ld a, [H_VBCOPYDOUBLESIZE] + ldh a, [hVBlankCopyDoubleSize] and a ret z - ld [H_SPTEMP], sp ; save stack pointer + ld [hSPTemp], sp ; save stack pointer - ld a, [H_VBCOPYDOUBLESRC] + ldh a, [hVBlankCopyDoubleSource] ld l, a - ld a, [H_VBCOPYDOUBLESRC + 1] + ldh a, [hVBlankCopyDoubleSource + 1] ld h, a ld sp, hl - ld a, [H_VBCOPYDOUBLEDEST] + ldh a, [hVBlankCopyDoubleDest] ld l, a - ld a, [H_VBCOPYDOUBLEDEST + 1] + ldh a, [hVBlankCopyDoubleDest + 1] ld h, a - ld a, [H_VBCOPYDOUBLESIZE] + ldh a, [hVBlankCopyDoubleSize] ld b, a xor a ; transferred - ld [H_VBCOPYDOUBLESIZE], a + ldh [hVBlankCopyDoubleSize], a .loop - rept 3 + REPT 3 pop de ld [hl], e inc l @@ -259,7 +259,7 @@ VBlankCopyDouble:: inc l ld [hl], d inc l - endr + ENDR pop de ld [hl], e @@ -273,13 +273,13 @@ VBlankCopyDouble:: dec b jr nz, .loop - ld [H_VBCOPYDOUBLESRC], sp + ld [hVBlankCopyDoubleSource], sp ld sp, hl ; load destination into sp to save time with ld [$xxxx], sp - ld [H_VBCOPYDOUBLEDEST], sp + ld [hVBlankCopyDoubleDest], sp - ld a, [H_SPTEMP] + ldh a, [hSPTemp] ld l, a - ld a, [H_SPTEMP + 1] + ldh a, [hSPTemp + 1] ld h, a ld sp, hl @@ -287,42 +287,42 @@ VBlankCopyDouble:: VBlankCopy:: -; Copy [H_VBCOPYSIZE] 2bpp tiles (or 16 * [H_VBCOPYSIZE] tile map entries) -; from H_VBCOPYSRC to H_VBCOPYDEST. +; Copy [hVBlankCopySize] 2bpp tiles (or 16 * [hVBlankCopySize] tile map entries) +; from hVBlankCopySource to hVBlankCopyDest. ; Source and destination addresses are updated, ; so transfer can continue in subsequent calls. - ld a, [H_VBCOPYSIZE] + ldh a, [hVBlankCopySize] and a ret z - ld [H_SPTEMP], sp + ld [hSPTemp], sp - ld a, [H_VBCOPYSRC] + ldh a, [hVBlankCopySource] ld l, a - ld a, [H_VBCOPYSRC + 1] + ldh a, [hVBlankCopySource + 1] ld h, a ld sp, hl - ld a, [H_VBCOPYDEST] + ldh a, [hVBlankCopyDest] ld l, a - ld a, [H_VBCOPYDEST + 1] + ldh a, [hVBlankCopyDest + 1] ld h, a - ld a, [H_VBCOPYSIZE] + ldh a, [hVBlankCopySize] ld b, a xor a ; transferred - ld [H_VBCOPYSIZE], a + ldh [hVBlankCopySize], a .loop - rept 7 + REPT 7 pop de ld [hl], e inc l ld [hl], d inc l - endr + ENDR pop de ld [hl], e @@ -332,13 +332,13 @@ VBlankCopy:: dec b jr nz, .loop - ld [H_VBCOPYSRC], sp + ld [hVBlankCopySource], sp ld sp, hl - ld [H_VBCOPYDEST], sp + ld [hVBlankCopyDest], sp - ld a, [H_SPTEMP] + ldh a, [hSPTemp] ld l, a - ld a, [H_SPTEMP + 1] + ldh a, [hSPTemp + 1] ld h, a ld sp, hl @@ -349,17 +349,17 @@ UpdateMovingBgTiles:: ; Animate water and flower ; tiles in the overworld. - ld a, [hTilesetType] + ldh a, [hTilesetType] and a ret z ; no animations if indoors (or if a menu set this to 0) - ld a,[rLY] + ldh a, [rLY] cp $90 ; check if not in vblank period??? (maybe if vblank is too long) ret c - ld a, [hMovingBGTilesCounter1] + ldh a, [hMovingBGTilesCounter1] inc a - ld [hMovingBGTilesCounter1], a + ldh [hMovingBGTilesCounter1], a cp 20 ret c cp 21 @@ -367,7 +367,7 @@ UpdateMovingBgTiles:: ; water - ld hl, vTileset + $14 * $10 + ld hl, vTileset tile $14 ld c, $10 ld a, [wMovingBGTilesCounter2] @@ -391,17 +391,17 @@ UpdateMovingBgTiles:: dec c jr nz, .left .done - ld a, [hTilesetType] + ldh a, [hTilesetType] rrca ret nc ; if in a cave, no flower animations xor a - ld [hMovingBGTilesCounter1], a + ldh [hMovingBGTilesCounter1], a ret .flower xor a - ld [hMovingBGTilesCounter1], a + ldh [hMovingBGTilesCounter1], a ld a, [wMovingBGTilesCounter2] and 3 @@ -412,7 +412,7 @@ UpdateMovingBgTiles:: jr z, .copy ld hl, FlowerTile3 .copy - ld de, vTileset + $3 * $10 + ld de, vTileset tile $03 ld c, $10 .loop ld a, [hli] diff --git a/home/window.asm b/home/window.asm new file mode 100644 index 00000000..8ccb9de9 --- /dev/null +++ b/home/window.asm @@ -0,0 +1,292 @@ +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 0, a ; pressed A key? + jr nz, .checkOtherKeys + 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 + ld bc, 40 + push af + ldh a, [hFlagsFFFA] + bit 1, a ; is the menu double spaced? + jr z, .doubleSpaced1 + ld bc, 20 +.doubleSpaced1 + 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 + ld bc, 40 + push af + ldh a, [hFlagsFFFA] + bit 1, a ; is the menu double spaced? + jr z, .doubleSpaced2 + ld bc, 20 +.doubleSpaced2 + 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], "▷" + 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, TRUE + +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 diff --git a/home/yes_no.asm b/home/yes_no.asm new file mode 100644 index 00000000..28d6c7aa --- /dev/null +++ b/home/yes_no.asm @@ -0,0 +1,40 @@ +; displays yes/no choice +; yes -> set carry +YesNoChoice:: + call SaveScreenTilesToBuffer1 + call InitYesNoTextBoxParameters + jr DisplayYesNoChoice + +Func_35f7:: + ld a, TWO_OPTION_MENU + ld [wTextBoxID], a + call InitYesNoTextBoxParameters + jp DisplayTextBoxID + +InitYesNoTextBoxParameters:: + xor a ; YES_NO_MENU + ld [wTwoOptionMenuID], a + hlcoord 14, 7 + lb bc, 8, 15 + ret + +YesNoChoicePokeCenter:: + call SaveScreenTilesToBuffer1 + ld a, HEAL_CANCEL_MENU + ld [wTwoOptionMenuID], a + hlcoord 11, 6 + lb bc, 8, 12 + jr DisplayYesNoChoice + +WideYesNoChoice:: ; unused + call SaveScreenTilesToBuffer1 + ld a, WIDE_YES_NO_MENU + ld [wTwoOptionMenuID], a + hlcoord 12, 7 + lb bc, 8, 13 + +DisplayYesNoChoice:: + ld a, TWO_OPTION_MENU + ld [wTextBoxID], a + call DisplayTextBoxID + jp LoadScreenTilesFromBuffer1 |
