Files
poketcg/src/engine/home.asm
T
2020-12-20 15:21:24 +00:00

11834 lines
231 KiB
NASM

; rst vectors
SECTION "rst00", ROM0
ret
SECTION "rst08", ROM0
ret
SECTION "rst10", ROM0
ret
SECTION "rst18", ROM0
jp Bank1Call
SECTION "rst20", ROM0
jp RST20
SECTION "rst28", ROM0
jp FarCall
SECTION "rst30", ROM0
ret
SECTION "rst38", ROM0
ret
; interrupts
SECTION "vblank", ROM0
jp VBlankHandler
SECTION "lcdc", ROM0
call wLCDCFunctionTrampoline
reti
SECTION "timer", ROM0
jp TimerHandler
SECTION "serial", ROM0
jp SerialHandler
SECTION "joypad", ROM0
reti
SECTION "romheader", ROM0
nop
jp Start
SECTION "start", ROM0
Start: ; 0150 (0:0150)
di
ld sp, $fffe
push af
xor a
ld [rIF], a
ld [rIE], a
call ZeroRAM
ld a, $1
call BankswitchROM
xor a
call BankswitchSRAM
call BankswitchVRAM0
call DisableLCD
pop af
ld [wInitialA], a
call DetectConsole
ld a, $20
ld [wTileMapFill], a
call SetupVRAM
call SetupRegisters
call SetupPalettes
call SetupSound
call SetupTimer
call ResetSerial
call CopyDMAFunction
call ValidateSRAM
ld a, BANK(GameLoop)
call BankswitchROM
ld sp, $e000
jp GameLoop
; vblank interrupt handler
VBlankHandler: ; 019b (0:019b)
push af
push bc
push de
push hl
ldh a, [hBankROM]
push af
ld hl, wReentrancyFlag
bit IN_VBLANK, [hl]
jr nz, .done
set IN_VBLANK, [hl]
ld a, [wVBlankOAMCopyToggle]
or a
jr z, .no_oam_copy
call hDMAFunction ; DMA-copy $ca00-$ca9f to OAM memory
xor a
ld [wVBlankOAMCopyToggle], a
.no_oam_copy
; flush scaling/windowing parameters
ldh a, [hSCX]
ld [rSCX], a
ldh a, [hSCY]
ld [rSCY], a
ldh a, [hWX]
ld [rWX], a
ldh a, [hWY]
ld [rWY], a
; flush LCDC
ld a, [wLCDC]
ld [rLCDC], a
ei
call wVBlankFunctionTrampoline
call FlushPalettesIfRequested
ld hl, wVBlankCounter
inc [hl]
ld hl, wReentrancyFlag
res IN_VBLANK, [hl]
.done
pop af
call BankswitchROM
pop hl
pop de
pop bc
pop af
reti
; timer interrupt handler
TimerHandler: ; 01e6 (0:01e6)
push af
push hl
push de
push bc
ei
call SerialTimerHandler
; only trigger every fourth interrupt ≈ 60.24 Hz
ld hl, wTimerCounter
ld a, [hl]
inc [hl]
and $3
jr nz, .done
; increment the 60-60-60-255-255 counter
call IncrementPlayTimeCounter
; check in-timer flag
ld hl, wReentrancyFlag
bit IN_TIMER, [hl]
jr nz, .done
set IN_TIMER, [hl]
ldh a, [hBankROM]
push af
ld a, BANK(SoundTimerHandler)
call BankswitchROM
call SoundTimerHandler
pop af
call BankswitchROM
; clear in-timer flag
ld hl, wReentrancyFlag
res IN_TIMER, [hl]
.done
pop bc
pop de
pop hl
pop af
reti
; increment play time counter by a tick
IncrementPlayTimeCounter: ; 021c (0:021c)
ld a, [wPlayTimeCounterEnable]
or a
ret z
ld hl, wPlayTimeCounter
inc [hl]
ld a, [hl]
cp 60
ret c
ld [hl], $0
inc hl
inc [hl]
ld a, [hl]
cp 60
ret c
ld [hl], $0
inc hl
inc [hl]
ld a, [hl]
cp 60
ret c
ld [hl], $0
inc hl
inc [hl]
ret nz
inc hl
inc [hl]
ret
; setup timer to 16384/68 ≈ 240.94 Hz
SetupTimer: ; 0241 (0:0241)
ld b, -68 ; Value for Normal Speed
call CheckForCGB
jr c, .set_timer
ld a, [rKEY1]
and $80
jr z, .set_timer
ld b, $100 - 2 * 68 ; Value for CGB Double Speed
.set_timer
ld a, b
ld [rTMA], a
ld a, TAC_16384_HZ
ld [rTAC], a
ld a, TAC_START | TAC_16384_HZ
ld [rTAC], a
ret
; return carry if not CGB
CheckForCGB: ; 025c (0:025c)
ld a, [wConsole]
cp CONSOLE_CGB
ret z
scf
ret
; wait for VBlankHandler to finish unless lcd is off
WaitForVBlank: ; 0264 (0:0264)
push hl
ld a, [wLCDC]
bit LCDC_ENABLE_F, a
jr z, .lcd_off
ld hl, wVBlankCounter
ld a, [hl]
.wait_vblank
halt
nop
cp [hl]
jr z, .wait_vblank
.lcd_off
pop hl
ret
; turn LCD on
EnableLCD: ; 0277 (0:0277)
ld a, [wLCDC] ;
bit LCDC_ENABLE_F, a ;
ret nz ; assert that LCD is off
or LCDC_ON ;
ld [wLCDC], a ;
ld [rLCDC], a ; turn LCD on
ld a, FLUSH_ALL_PALS
ld [wFlushPaletteFlags], a
ret
; wait for vblank, then turn LCD off
DisableLCD: ; 028a (0:028a)
ld a, [rLCDC] ;
bit LCDC_ENABLE_F, a ;
ret z ; assert that LCD is on
ld a, [rIE]
ld [wIE], a
res INT_VBLANK, a ;
ld [rIE], a ; disable vblank interrupt
.wait_vblank
ld a, [rLY] ;
cp LY_VBLANK ;
jr nz, .wait_vblank ; wait for vblank
ld a, [rLCDC] ;
and LCDC_OFF ;
ld [rLCDC], a ;
ld a, [wLCDC] ;
and LCDC_OFF ;
ld [wLCDC], a ; turn LCD off
xor a
ld [rBGP], a
ld [rOBP0], a
ld [rOBP1], a
ld a, [wIE]
ld [rIE], a
ret
; set OBJ size: 8x8
Set_OBJ_8x8: ; 02b9 (0:02b9)
ld a, [wLCDC]
and LCDC_OBJ8
ld [wLCDC], a
ret
; set OBJ size: 8x16
Set_OBJ_8x16: ; 02c2 (0:02c2)
ld a, [wLCDC]
or LCDC_OBJ16
ld [wLCDC], a
ret
; set Window Display on
Set_WD_on: ; 02cb (0:02cb)
ld a, [wLCDC]
or LCDC_WINON
ld [wLCDC], a
ret
; set Window Display off
Set_WD_off: ; 02d4 (0:02d4)
ld a, [wLCDC]
and LCDC_WINOFF
ld [wLCDC], a
ret
; enable timer interrupt
EnableInt_Timer: ; 02dd (0:02dd)
ld a, [rIE]
or 1 << INT_TIMER
ld [rIE], a
ret
; enable vblank interrupt
EnableInt_VBlank: ; 02e4 (0:02e4)
ld a, [rIE]
or 1 << INT_VBLANK
ld [rIE], a
ret
; enable lcdc interrupt on hblank mode
EnableInt_HBlank: ; 02eb (0:02eb)
ld a, [rSTAT]
or 1 << STAT_MODE_HBLANK
ld [rSTAT], a
xor a
ld [rIF], a
ld a, [rIE]
or 1 << INT_LCD_STAT
ld [rIE], a
ret
; disable lcdc interrupt and the hblank mode trigger
DisableInt_HBlank: ; 02fb (0:02fb)
ld a, [rSTAT]
and ~(1 << STAT_MODE_HBLANK)
ld [rSTAT], a
xor a
ld [rIF], a
ld a, [rIE]
and ~(1 << INT_LCD_STAT)
ld [rIE], a
ret
; initialize scroll, window, and lcdc registers, set trampoline functions
; for the lcdc and vblank interrupts, latch clock data, and enable SRAM/RTC
SetupRegisters: ; 030b (0:030b)
xor a
ld [rSCY], a
ld [rSCX], a
ld [rWY], a
ld [rWX], a
ld [wcab0], a
ld [wcab1], a
ld [wcab2], a
ldh [hSCX], a
ldh [hSCY], a
ldh [hWX], a
ldh [hWY], a
xor a
ld [wReentrancyFlag], a
ld a, $c3 ; $c3 = jp nn
ld [wLCDCFunctionTrampoline], a
ld [wVBlankFunctionTrampoline], a
ld hl, wVBlankFunctionTrampoline + 1
ld [hl], LOW(NoOp) ;
inc hl ; load `jp NoOp`
ld [hl], HIGH(NoOp) ;
ld a, LCDC_BGON | LCDC_OBJON | LCDC_OBJ16 | LCDC_WIN9C00
ld [wLCDC], a
ld a, $1
ld [MBC3LatchClock], a
ld a, SRAM_ENABLE
ld [MBC3SRamEnable], a
NoOp: ; 0348 (0:0348)
ret
; sets wConsole and, if CGB, selects WRAM bank 1 and switches to double speed mode
DetectConsole: ; 0349 (0:0349)
ld b, CONSOLE_CGB
cp GBC
jr z, .got_console
call DetectSGB
ld b, CONSOLE_DMG
jr nc, .got_console
call InitSGB
ld b, CONSOLE_SGB
.got_console
ld a, b
ld [wConsole], a
cp CONSOLE_CGB
ret nz
ld a, $01
ld [rSVBK], a
call SwitchToCGBDoubleSpeed
ret
; initialize the palettes (both monochrome and color)
SetupPalettes: ; 036a (0:036a)
ld hl, wBGP
ld a, %11100100
ld [rBGP], a
ld [hli], a ; wBGP
ld [rOBP0], a
ld [rOBP1], a
ld [hli], a ; wOBP0
ld [hl], a ; wOBP1
xor a
ld [wFlushPaletteFlags], a
ld a, [wConsole]
cp CONSOLE_CGB
ret nz
ld de, wBackgroundPalettesCGB
ld c, 16
.copy_pals_loop
ld hl, InitialPalette
ld b, CGB_PAL_SIZE
.copy_bytes_loop
ld a, [hli]
ld [de], a
inc de
dec b
jr nz, .copy_bytes_loop
dec c
jr nz, .copy_pals_loop
call FlushAllCGBPalettes
ret
InitialPalette: ; 0399 (0:0399)
rgb 28, 28, 24
rgb 21, 21, 16
rgb 10, 10, 08
rgb 00, 00, 00
; clear VRAM tile data ([wTileMapFill] should be an empty tile)
SetupVRAM: ; 03a1 (0:03a1)
call FillTileMap
call CheckForCGB
jr c, .vram0
call BankswitchVRAM1
call .vram0
call BankswitchVRAM0
.vram0
ld hl, v0Tiles0
ld bc, v0BGMap0 - v0Tiles0
.loop
xor a
ld [hli], a
dec bc
ld a, b
or c
jr nz, .loop
ret
; fill VRAM0 BG map 0 with [wTileMapFill] and VRAM1 BG map 0 with $00
FillTileMap: ; 03c0 (0:03c0)
call BankswitchVRAM0
ld hl, v0BGMap0
ld bc, v0BGMap1 - v0BGMap0
.vram0_loop
ld a, [wTileMapFill]
ld [hli], a
dec bc
ld a, c
or b
jr nz, .vram0_loop
ld a, [wConsole]
cp CONSOLE_CGB
ret nz
call BankswitchVRAM1
ld hl, v1BGMap0
ld bc, v1BGMap1 - v1BGMap0
.vram1_loop
xor a
ld [hli], a
dec bc
ld a, c
or b
jr nz, .vram1_loop
call BankswitchVRAM0
ret
; zero work RAM, stack area, and high RAM ($C000-$DFFF, $FF80-$FFEF)
ZeroRAM: ; 03ec (0:03ec)
ld hl, $c000
ld bc, $e000 - $c000
.zero_wram_loop
xor a
ld [hli], a
dec bc
ld a, c
or b
jr nz, .zero_wram_loop
ld c, LOW($ff80)
ld b, $fff0 - $ff80
xor a
.zero_hram_loop
ld [$ff00+c], a
inc c
dec b
jr nz, .zero_hram_loop
ret
; Flush all non-CGB and CGB palettes
FlushAllPalettes: ; 0404 (0:0404)
ld a, FLUSH_ALL_PALS
jr FlushPalettes
; Flush non-CGB palettes and a single CGB palette,
; provided in a as an index between 0-7 (BGP) or 8-15 (OBP)
FlushPalette: ; 0408 (0:0408)
or FLUSH_ONE_PAL
jr FlushPalettes
; Set wBGP to the specified value, flush non-CGB palettes, and the first CGB palette.
SetBGP: ; 040c (0:040c)
ld [wBGP], a
; fallthrough
; Flush non-CGB palettes and the first CGB palette
FlushPalette0:
ld a, FLUSH_ONE_PAL
; fallthrough
FlushPalettes:
ld [wFlushPaletteFlags], a
ld a, [wLCDC]
rla
ret c
push hl
push de
push bc
call FlushPalettesIfRequested
pop bc
pop de
pop hl
ret
; Set wOBP0 to the specified value, flush non-CGB palettes, and the first CGB palette.
SetOBP0: ; 0423 (0:0423)
ld [wOBP0], a
jr FlushPalette0
; Set wOBP1 to the specified value, flush non-CGB palettes, and the first CGB palette.
SetOBP1: ; 0428 (0:0428)
ld [wOBP1], a
jr FlushPalette0
; Flushes non-CGB palettes from [wBGP], [wOBP0], [wOBP1] as well as CGB
; palettes from [wBackgroundPalettesCGB..wBackgroundPalettesCGB+$3f] (BG palette)
; and [wObjectPalettesCGB+$00..wObjectPalettesCGB+$3f] (sprite palette).
; Only flushes if [wFlushPaletteFlags] is nonzero, and only flushes
; a single CGB palette if bit6 of that location is reset.
FlushPalettesIfRequested: ; 042d (0:042d)
ld a, [wFlushPaletteFlags]
or a
ret z
; flush grayscale (non-CGB) palettes
ld hl, wBGP
ld a, [hli]
ld [rBGP], a
ld a, [hli]
ld [rOBP0], a
ld a, [hl]
ld [rOBP1], a
ld a, [wConsole]
cp CONSOLE_CGB
jr z, .CGB
.done
xor a
ld [wFlushPaletteFlags], a
ret
.CGB
; flush a single CGB BG or OB palette
; if bit6 (FLUSH_ALL_PALS_F) of [wFlushPaletteFlags] is set, flush all 16 of them
ld a, [wFlushPaletteFlags]
bit FLUSH_ALL_PALS_F, a
jr nz, FlushAllCGBPalettes
ld b, CGB_PAL_SIZE
call CopyCGBPalettes
jr .done
FlushAllCGBPalettes: ; 0458 (0:0458)
; flush 8 BGP palettes
xor a
ld b, 8 palettes
call CopyCGBPalettes
; flush 8 OBP palettes
ld a, CGB_PAL_SIZE
ld b, 8 palettes
call CopyCGBPalettes
jr FlushPalettesIfRequested.done
; copy b bytes of CGB palette data starting at
; (wBackgroundPalettesCGB + a palettes) into rBGPD or rOGPD.
CopyCGBPalettes: ; 0467 (0:0467)
add a
add a
add a
ld e, a
ld d, $0
ld hl, wBackgroundPalettesCGB
add hl, de
ld c, LOW(rBGPI)
bit 6, a ; was a between 0-7 (BGP), or between 8-15 (OBP)?
jr z, .copy
ld c, LOW(rOBPI)
.copy
and %10111111
ld e, a
.next_byte
ld a, e
ld [$ff00+c], a
inc c
.wait
ld a, [rSTAT]
and 1 << STAT_BUSY ; wait until hblank or vblank
jr nz, .wait
ld a, [hl]
ld [$ff00+c], a
ld a, [$ff00+c]
cp [hl]
jr nz, .wait
inc hl
dec c
inc e
dec b
jr nz, .next_byte
ret
; reads struct:
; x (1 byte), y (1 byte), data (n bytes), $00
; writes data to BGMap0-translated x,y
; important: make sure VRAM can be accessed first, else use WriteDataBlockToBGMap0
UnsafeWriteDataBlockToBGMap0: ; 0492 (0:0492)
ld a, [hli]
ld b, a
ld a, [hli]
ld c, a
call BCCoordToBGMap0Address
jr .next
.loop
ld [de], a
inc de
.next
ld a, [hli]
or a
jr nz, .loop
ret
; initialize the screen by emptying the tilemap. used during screen transitions
EmptyScreen: ; 04a2 (0:04a2)
call DisableLCD
call FillTileMap
xor a
ld [wDuelDisplayedScreen], a
ld a, [wConsole]
cp CONSOLE_SGB
ret nz
call EnableLCD
ld hl, AttrBlkPacket_EmptyScreen
call SendSGB
call DisableLCD
ret
AttrBlkPacket_EmptyScreen: ; 04bf (0:04bf)
sgb ATTR_BLK, 1 ; sgb_command, length
db 1 ; number of data sets
; Control Code, Color Palette Designation, X1, Y1, X2, Y2
db ATTR_BLK_CTRL_INSIDE + ATTR_BLK_CTRL_LINE, 0 << 0 + 0 << 2, 0, 0, 19, 17 ; data set 1
ds 6 ; data set 2
ds 2 ; data set 3
; returns v*BGMap0 + BG_MAP_WIDTH * c + b in de.
; used to map coordinates at bc to a BGMap0 address.
BCCoordToBGMap0Address: ; 04cf (0:04cf)
ld l, c
ld h, $0
add hl, hl
add hl, hl
add hl, hl
add hl, hl
add hl, hl
ld c, b
ld b, HIGH(v0BGMap0)
add hl, bc
ld e, l
ld d, h
ret
; read joypad data to refresh hKeysHeld, hKeysPressed, and hKeysReleased
; the A + B + Start + Select combination resets the game
ReadJoypad: ; 04de (0:04de)
ld a, JOY_BTNS_SELECT
ld [rJOYP], a
ld a, [rJOYP]
ld a, [rJOYP]
cpl
and JOY_INPUT_MASK
swap a
ld b, a ; buttons data
ld a, JOY_DPAD_SELECT
ld [rJOYP], a
ld a, [rJOYP]
ld a, [rJOYP]
ld a, [rJOYP]
ld a, [rJOYP]
ld a, [rJOYP]
ld a, [rJOYP]
cpl
and JOY_INPUT_MASK
or b
ld c, a ; dpad data
cpl
ld b, a ; buttons data
ldh a, [hKeysHeld]
xor c
and b
ldh [hKeysReleased], a
ldh a, [hKeysHeld]
xor c
and c
ld b, a
ldh [hKeysPressed], a
ldh a, [hKeysHeld]
and BUTTONS
cp BUTTONS
jr nz, SaveButtonsHeld
; A + B + Start + Select: reset game
call ResetSerial
; fallthrough
Reset: ; 051b (0:051b)
ld a, [wInitialA]
di
jp Start
SaveButtonsHeld:
ld a, c
ldh [hKeysHeld], a
ld a, JOY_BTNS_SELECT | JOY_DPAD_SELECT
ld [rJOYP], a
ret
; clear joypad hmem data
ClearJoypad: ; 052a (0:052a)
push hl
ld hl, hDPadRepeat
xor a
ld [hli], a ; hDPadRepeat
ld [hli], a ; hKeysReleased
ld [hli], a ; hDPadHeld
ld [hli], a ; hKeysHeld
ld [hli], a ; hKeysPressed
pop hl
ret
; calls DoFrame a times
DoAFrames: ; 0536 (0:0536)
.loop
push af
call DoFrame
pop af
dec a
jr nz, .loop
ret
; updates background, sprites and other game variables, halts until vblank, and reads user input
; if wcad5 is not 0, the game can be paused (and resumed) by pressing the SELECT button
DoFrame: ; 053f (0:053f)
push af
push hl
push de
push bc
ld hl, wDoFrameFunction ; context-specific function
call CallIndirect
call WaitForVBlank
call ReadJoypad
call HandleDPadRepeat
ld a, [wcad5]
or a
jr z, .done
ldh a, [hKeysPressed]
and SELECT
jr z, .done
.game_paused_loop
call WaitForVBlank
call ReadJoypad
call HandleDPadRepeat
ldh a, [hKeysPressed]
and SELECT
jr z, .game_paused_loop
.done
pop bc
pop de
pop hl
pop af
ret
; handle D-pad repeat counter
; used to quickly scroll through menus when a relevant D-pad key is held
HandleDPadRepeat: ; 0572 (0:0572)
ldh a, [hKeysHeld]
ldh [hDPadHeld], a
and D_PAD
jr z, .done
ld hl, hDPadRepeat
ldh a, [hKeysPressed]
and D_PAD
jr z, .dpad_key_held
ld [hl], 24
ret
.dpad_key_held
dec [hl]
jr nz, .done
ld [hl], 6
ret
.done
ldh a, [hKeysPressed]
and BUTTONS
ldh [hDPadHeld], a
ret
; copy DMA to hDMAFunction
CopyDMAFunction: ; 0593 (0:0593)
ld c, LOW(hDMAFunction)
ld b, JumpToFunctionInTable - DMA
ld hl, DMA
.loop
ld a, [hli]
ld [$ff00+c], a
inc c
dec b
jr nz, .loop
ret
; CopyDMAFunction copies this function to hDMAFunction ($ff83)
DMA: ; 05a1 (0:05a1)
ld a, HIGH(wOAM)
ld [rDMA], a
ld a, $28
.wait
dec a
jr nz, .wait
ret
; jumps to index a in pointer table hl
JumpToFunctionInTable: ; 05ab (0:05ab)
add a
add l
ld l, a
ld a, $0
adc h
ld h, a
ld a, [hli]
ld h, [hl]
ld l, a
jp hl
; call function at [hl] if non-NULL
CallIndirect: ; 05b6 (0:05b6)
push af
ld a, [hli]
or [hl]
jr nz, .call_hl
pop af
ret
.call_hl
ld a, [hld]
ld l, [hl]
ld h, a
pop af
; fallthrough
CallHL: ; 05c1 (0:05c1)
jp hl
; converts the two-digit BCD number provided in a to text (ascii) format,
; writes them to [wStringBuffer] and [wStringBuffer + 1], and to the BGMap0 address at bc
WriteTwoDigitBCDNumber: ; 05c2 (0:05c2)
push hl
push bc
push de
ld hl, wStringBuffer
push hl
push bc
call WriteBCDNumberInTextFormat
pop bc
call BCCoordToBGMap0Address
pop hl
ld b, 2
call JPHblankCopyDataHLtoDE
pop de
pop bc
pop hl
ret
; converts the one-digit BCD number provided in the lower nybble of a to text
; (ascii) format, and writes it to [wStringBuffer] and to the BGMap0 address at bc
WriteOneDigitBCDNumber: ; 05db (0:05db)
push hl
push bc
push de
ld hl, wStringBuffer
push hl
push bc
call WriteBCDDigitInTextFormat
pop bc
call BCCoordToBGMap0Address
pop hl
ld b, 1
call JPHblankCopyDataHLtoDE
pop de
pop bc
pop hl
ret
; converts the four-digit BCD number provided in h and l to text (ascii) format,
; writes them to [wStringBuffer] through [wStringBuffer + 3], and to the BGMap0 address at bc
WriteFourDigitBCDNumber: ; 05f4 (0:05f4)
push hl
push bc
push de
ld e, l
ld d, h
ld hl, wStringBuffer
push hl
push bc
ld a, d
call WriteBCDNumberInTextFormat
ld a, e
call WriteBCDNumberInTextFormat
pop bc
call BCCoordToBGMap0Address
pop hl
ld b, 4
call JPHblankCopyDataHLtoDE
pop de
pop bc
pop hl
ret
; given two BCD digits in the two nybbles of register a,
; write them in text (ascii) format to hl (most significant nybble first).
; numbers above 9 end up converted to half-width font tiles.
WriteBCDNumberInTextFormat: ; 0614 (0:0614)
push af
swap a
call WriteBCDDigitInTextFormat
pop af
; fallthrough
; given a BCD digit in the (lower nybble) of register a, write it in text (ascii)
; format to hl. numbers above 9 end up converted to half-width font tiles.
WriteBCDDigitInTextFormat:
and $0f
add "0"
cp "9" + 1
jr c, .write_num
add $07
.write_num
ld [hli], a
ret
; converts the one-byte number at a to text (ascii) format,
; and writes it to [wStringBuffer] and the BGMap0 address at bc
WriteOneByteNumber: ; 0627 (0:0627)
push bc
push hl
ld l, a
ld h, $00
ld de, wStringBuffer
push de
push bc
ld bc, -100
call TwoByteNumberToText.get_digit
ld bc, -10
call TwoByteNumberToText.get_digit
ld bc, -1
call TwoByteNumberToText.get_digit
pop bc
call BCCoordToBGMap0Address
pop hl
ld b, 3
call JPHblankCopyDataHLtoDE
pop hl
pop bc
ret
; converts the two-byte number at hl to text (ascii) format,
; and writes it to [wStringBuffer] and the BGMap0 address at bc
WriteTwoByteNumber: ; 0650 (0:0650)
push bc
ld de, wStringBuffer
push de
call TwoByteNumberToText
call BCCoordToBGMap0Address
pop hl
ld b, 5
call JPHblankCopyDataHLtoDE
pop bc
ret
; convert the number at hl to text (ascii) format and write it to de
TwoByteNumberToText: ; 0663 (0:0663)
push bc
ld bc, -10000
call .get_digit
ld bc, -1000
call .get_digit
ld bc, -100
call .get_digit
ld bc, -10
call .get_digit
ld bc, -1
call .get_digit
xor a ; TX_END
ld [de], a
pop bc
ret
.get_digit
ld a, "0" - 1
.substract_loop
inc a
add hl, bc
jr c, .substract_loop
ld [de], a
inc de
ld a, l
sub c
ld l, a
ld a, h
sbc b
ld h, a
ret
; reads structs:
; x (1 byte), y (1 byte), data (n bytes), $00
; x (1 byte), y (1 byte), data (n bytes), $00
; ...
; $ff
; for each struct, writes data to BGMap0-translated x,y
WriteDataBlocksToBGMap0: ; 0695 (0:0695)
call WriteDataBlockToBGMap0
bit 7, [hl] ; check for $ff
jr z, WriteDataBlocksToBGMap0
ret
; reads struct:
; x (1 byte), y (1 byte), data (n bytes), $00
; writes data to BGMap0-translated x,y
WriteDataBlockToBGMap0: ; 069d (0:069d)
ld b, [hl]
inc hl
ld c, [hl]
inc hl
push hl ; hl = addr of data
push bc ; b,c = x,y
ld b, -1
.find_zero_loop
inc b
ld a, [hli]
or a
jr nz, .find_zero_loop
ld a, b ; length of data
pop bc ; x,y
push af
call BCCoordToBGMap0Address
pop af
ld b, a ; length of data
pop hl ; addr of data
or a
jr z, .move_to_next
push bc
push hl
call SafeCopyDataHLtoDE ; copy data to de (BGMap0 translated x,y)
pop hl
pop bc
.move_to_next
inc b ; length of data += 1 (to account for the last $0)
ld c, b
ld b, 0
add hl, bc ; point to next structure
ret
; writes a to [v*BGMap0 + BG_MAP_WIDTH * c + b]
WriteByteToBGMap0: ; 06c3 (0:06c3)
push af
ld a, [wLCDC]
rla
jr c, .lcd_on
pop af
push hl
push de
push bc
push af
call BCCoordToBGMap0Address
pop af
ld [de], a
pop bc
pop de
pop hl
ret
.lcd_on
pop af
; fallthrough
; writes a to [v*BGMap0 + BG_MAP_WIDTH * c + b] during hblank
HblankWriteByteToBGMap0: ; 06d9
push hl
push de
push bc
ld hl, wTempByte
push hl
ld [hl], a
call BCCoordToBGMap0Address
pop hl
ld b, 1
call HblankCopyDataHLtoDE
pop bc
pop de
pop hl
ret
; copy a bytes of data from hl to vBGMap0 address pointed to by coord at bc
CopyDataToBGMap0: ; 06ee (0:06ee)
push bc
push hl
push af
call BCCoordToBGMap0Address
pop af
ld b, a
pop hl
call SafeCopyDataHLtoDE
pop bc
ret
; copy b bytes of data from hl to de
; if LCD on, copy during h-blank only
SafeCopyDataHLtoDE: ; 6fc (0:6fc)
ld a, [wLCDC]
rla
jr c, JPHblankCopyDataHLtoDE
.lcd_off_loop
ld a, [hli]
ld [de], a
inc de
dec b
jr nz, .lcd_off_loop
ret
JPHblankCopyDataHLtoDE: ; 0709 (0:0709)
jp HblankCopyDataHLtoDE
; copy c bytes of data from hl to de, b times.
; used to copy gfx data with c = TILE_SIZE
CopyGfxData: ; 070c (0:070c)
ld a, [wLCDC]
rla
jr nc, .next_tile
.hblank_copy
push bc
push hl
push de
ld b, c
call JPHblankCopyDataHLtoDE
ld b, $0
pop hl
add hl, bc
ld e, l
ld d, h
pop hl
add hl, bc
pop bc
dec b
jr nz, .hblank_copy
ret
.next_tile
push bc
.copy_tile
ld a, [hli]
ld [de], a
inc de
dec c
jr nz, .copy_tile
pop bc
dec b
jr nz, .next_tile
ret
; copy bc bytes from hl to de. preserves all regsters except af
CopyDataHLtoDE_SaveRegisters: ; 0732 (0:0732)
push hl
push de
push bc
call CopyDataHLtoDE
pop bc
pop de
pop hl
ret
; copy bc bytes from hl to de
CopyDataHLtoDE: ; 073c (0:073c)
ld a, [hli]
ld [de], a
inc de
dec bc
ld a, c
or b
jr nz, CopyDataHLtoDE
ret
; switch to rombank (a + top2 of h shifted down),
; set top2 of h to 01 (switchable ROM bank area),
; return old rombank id on top-of-stack
BankpushROM: ; 0745 (0:0745)
push hl
push bc
push af
push de
ld e, l
ld d, h
ld hl, sp+$9
ld b, [hl]
dec hl
ld c, [hl]
dec hl
ld [hl], b
dec hl
ld [hl], c
ld hl, sp+$9
ldh a, [hBankROM]
ld [hld], a
ld [hl], $0
ld a, d
rlca
rlca
and $3
ld b, a
res 7, d
set 6, d ; $4000 ≤ de ≤ $7fff
ld l, e
ld h, d
pop de
pop af
add b
call BankswitchROM
pop bc
ret
; switch to rombank a,
; return old rombank id on top-of-stack
BankpushROM2: ; 076f (0:076f)
push hl
push bc
push af
push de
ld e, l
ld d, h
ld hl, sp+$9
ld b, [hl]
dec hl
ld c, [hl]
dec hl
ld [hl], b
dec hl
ld [hl], c
ld hl, sp+$9
ldh a, [hBankROM]
ld [hld], a
ld [hl], $0
ld l, e
ld h, d
pop de
pop af
call BankswitchROM
pop bc
ret
; restore rombank from top-of-stack
BankpopROM: ; 078e (0:078e)
push hl
push de
ld hl, sp+$7
ld a, [hld]
call BankswitchROM
dec hl
ld d, [hl]
dec hl
ld e, [hl]
inc hl
inc hl
ld [hl], e
inc hl
ld [hl], d
pop de
pop hl
pop af
ret
; switch ROM bank to a
BankswitchROM: ; 07a3 (0:07a3)
ldh [hBankROM], a
ld [MBC3RomBank], a
ret
; switch SRAM bank to a
BankswitchSRAM: ; 07a9 (0:07a9)
push af
ldh [hBankSRAM], a
ld [MBC3SRamBank], a
ld a, SRAM_ENABLE
ld [MBC3SRamEnable], a
pop af
ret
; enable external RAM (SRAM)
EnableSRAM: ; 07b6 (0:07b6)
push af
ld a, SRAM_ENABLE
ld [MBC3SRamEnable], a
pop af
ret
; disable external RAM (SRAM)
DisableSRAM: ; 07be (0:07be)
push af
xor a ; SRAM_DISABLE
ld [MBC3SRamEnable], a
pop af
ret
; set current dest VRAM bank to 0
BankswitchVRAM0: ; 07c5 (0:07c5)
push af
xor a
ldh [hBankVRAM], a
ld [rVBK], a
pop af
ret
; set current dest VRAM bank to 1
BankswitchVRAM1: ; 07cd (0:07cd)
push af
ld a, $1
ldh [hBankVRAM], a
ld [rVBK], a
pop af
ret
; set current dest VRAM bank to a
BankswitchVRAM: ; 07d6 (0:07d6)
ldh [hBankVRAM], a
ld [rVBK], a
ret
; switch to CGB Normal Speed Mode if playing on CGB and current mode is Double Speed Mode
SwitchToCGBNormalSpeed: ; 7db (0:7db)
call CheckForCGB
ret c
ld hl, rKEY1
bit 7, [hl]
ret z
jr CGBSpeedSwitch
; switch to CGB Double Speed Mode if playing on CGB and current mode is Normal Speed Mode
SwitchToCGBDoubleSpeed: ; 07e7 (0:07e7)
call CheckForCGB
ret c
ld hl, rKEY1
bit 7, [hl]
ret nz
; fallthrough
; switch between CGB Double Speed Mode and Normal Speed Mode
CGBSpeedSwitch: ; 07f1 (0:07f1)
ld a, [rIE]
push af
xor a
ld [rIE], a
set 0, [hl]
xor a
ld [rIF], a
ld [rIE], a
ld a, $30
ld [rJOYP], a
stop
call SetupTimer
pop af
ld [rIE], a
ret
; validate the saved data in SRAM
; it must contain with the sequence $04, $21, $05 at s0a000
ValidateSRAM: ; 080b (0:080b)
xor a
call BankswitchSRAM
ld hl, $a000
ld bc, $2000 / 2
.check_pattern_loop
ld a, [hli]
cp $41
jr nz, .check_sequence
ld a, [hli]
cp $93
jr nz, .check_sequence
dec bc
ld a, c
or b
jr nz, .check_pattern_loop
call RestartSRAM
scf
call Func_4050
call DisableSRAM
ret
.check_sequence
ld hl, s0a000
ld a, [hli]
cp $04
jr nz, .restart_sram
ld a, [hli]
cp $21
jr nz, .restart_sram
ld a, [hl]
cp $05
jr nz, .restart_sram
ret
.restart_sram
call RestartSRAM
or a
call Func_4050
call DisableSRAM
ret
; zero all SRAM banks and set s0a000 to $04, $21, $05
RestartSRAM: ; 084d (0:084d)
ld a, 3
.clear_loop
call ClearSRAMBank
dec a
cp -1
jr nz, .clear_loop
ld hl, s0a000
ld [hl], $04
inc hl
ld [hl], $21
inc hl
ld [hl], $05
ret
; zero the loaded SRAM bank
ClearSRAMBank: ; 0863 (0:0863)
push af
call BankswitchSRAM
call EnableSRAM
ld hl, $a000
ld bc, $2000
.loop
xor a
ld [hli], a
dec bc
ld a, c
or b
jr nz, .loop
pop af
ret
; returns h * l in hl
HtimesL: ; 0879 (0:0879)
push de
ld a, h
ld e, l
ld d, $0
ld l, d
ld h, d
jr .asm_887
.asm_882
add hl, de
.asm_883
sla e
rl d
.asm_887
srl a
jr c, .asm_882
jr nz, .asm_883
pop de
ret
; return a random number between 0 and a (exclusive) in a
Random: ; 088f (0:088f)
push hl
ld h, a
call UpdateRNGSources
ld l, a
call HtimesL
ld a, h
pop hl
ret
; get the next random numbers of the wRNG1 and wRNG2 sequences
UpdateRNGSources: ; 089b (0:089b)
push hl
push de
ld hl, wRNG1
ld a, [hli]
ld d, [hl] ; wRNG2
inc hl
ld e, a
ld a, d
rlca
rlca
xor e
rra
push af
ld a, d
xor e
ld d, a
ld a, [hl] ; wRNGCounter
xor e
ld e, a
pop af
rl e
rl d
ld a, d
xor e
inc [hl] ; wRNGCounter
dec hl
ld [hl], d ; wRNG2
dec hl
ld [hl], e ; wRNG1
pop de
pop hl
ret
Func_08bf: ; 08bf (0:08bf)
ld hl, wcad6
ld [hl], e
inc hl
ld [hl], d
ld hl, wcad8
ld [hl], $1
inc hl
xor a
ld [hli], a
ld [hli], a
ld [hli], a
ld [hli], a
ld [hl], b
inc hl
ld [hli], a ; 0
ld [hl], $ef
ld h, b
ld l, $0
xor a
.asm_8d9
ld [hl], a
inc l
jr nz, .asm_8d9
ret
Func_08de: ; 08de (0:08de)
push hl
push de
.asm_8e0
push bc
call Func_08ef
ld [de], a
inc de
pop bc
dec bc
ld a, c
or b
jr nz, .asm_8e0
pop de
pop hl
ret
Func_08ef: ; 08ef (0:08ef)
ld hl, wcadc
ld a, [hl]
or a
jr z, .asm_902
dec [hl]
inc hl
.asm_8f8
ld b, [hl]
inc hl
ld c, [hl]
inc [hl]
inc hl
ld a, [bc]
ld c, [hl]
inc [hl]
ld [bc], a
ret
.asm_902
ld hl, wcad6
ld c, [hl]
inc hl
ld b, [hl]
inc hl
dec [hl]
inc hl
jr nz, .asm_914
dec hl
ld [hl], $8
inc hl
ld a, [bc]
inc bc
ld [hl], a
.asm_914
rl [hl]
ld a, [bc]
inc bc
jr nc, .asm_92a
ld hl, wcad6
ld [hl], c
inc hl
ld [hl], b
ld hl, wcadd
ld b, [hl]
inc hl
inc hl
ld c, [hl]
inc [hl]
ld [bc], a
ret
.asm_92a
ld [wcade], a
ld hl, wcada
bit 0, [hl]
jr nz, .asm_94a
set 0, [hl]
inc hl
ld a, [bc]
inc bc
ld [hli], a
swap a
.asm_93c
and $f
inc a
ld [hli], a
push hl
ld hl, wcad6
ld [hl], c
inc hl
ld [hl], b
pop hl
jr .asm_8f8
.asm_94a
res 0, [hl]
inc hl
ld a, [hli]
jr .asm_93c
; set attributes for [hl] sprites starting from wOAM + [wOAMOffset] / 4
; return carry if reached end of wOAM before finishing
SetManyObjectsAttributes: ; 950 (0:950)
push hl
ld a, [wOAMOffset]
ld c, a
ld b, HIGH(wOAM)
cp 40 * 4
jr nc, .beyond_oam
pop hl
ld a, [hli] ; [hl] = how many obj?
.copy_obj_loop
push af
ld a, [hli]
add e
ld [bc], a ; Y Position <- [hl + 1 + 4*i] + e
inc bc
ld a, [hli]
add d
ld [bc], a ; X Position <- [hl + 2 + 4*i] + d
inc bc
ld a, [hli]
ld [bc], a ; Tile/Pattern Number <- [hl + 3 + 4*i]
inc bc
ld a, [hli]
ld [bc], a ; Attributes/Flags <- [hl + 4 + 4*i]
inc bc
ld a, c
cp 40 * 4
jr nc, .beyond_oam
pop af
dec a
jr nz, .copy_obj_loop
or a
.done
ld hl, wOAMOffset
ld [hl], c
ret
.beyond_oam
pop hl
scf
jr .done
; for the sprite at wOAM + [wOAMOffset] / 4, set its attributes from registers e, d, c, b
; return carry if [wOAMOffset] > 40 * 4 (beyond the end of wOAM)
SetOneObjectAttributes: ; 097f (0:097f)
push hl
ld a, [wOAMOffset]
ld l, a
ld h, HIGH(wOAM)
cp 40 * 4
jr nc, .beyond_oam
ld [hl], e ; Y Position
inc hl
ld [hl], d ; X Position
inc hl
ld [hl], c ; Tile/Pattern Number
inc hl
ld [hl], b ; Attributes/Flags
inc hl
ld a, l
ld [wOAMOffset], a
pop hl
or a
ret
.beyond_oam
pop hl
scf
ret
; set the Y Position and X Position of all sprites in wOAM to $00
ZeroObjectPositions: ; 099c (0:099c)
xor a
ld [wOAMOffset], a
ld hl, wOAM
ld c, 40
xor a
.loop
ld [hli], a
ld [hli], a
inc hl
inc hl
dec c
jr nz, .loop
ret
; RST18
; this function affects the stack so that it returns to the pointer following
; the rst call. similar to rst 28, except this always loads bank 1
Bank1Call: ; 09ae (0:09ae)
push hl
push hl
push hl
push hl
push de
push af
ld hl, sp+$d
ld d, [hl]
dec hl
ld e, [hl]
dec hl
ld [hl], $0
dec hl
ldh a, [hBankROM]
ld [hld], a
ld [hl], $9
dec hl
ld [hl], $dc
dec hl
inc de
ld a, [de]
ld [hld], a
dec de
ld a, [de]
ld [hl], a
ld a, $1
; fallthrough
Bank1Call_FarCall_Common: ; 09ce (0:09ce)
call BankswitchROM
ld hl, sp+$d
inc de
inc de
ld [hl], d
dec hl
ld [hl], e
pop af
pop de
pop hl
ret
; switch to the ROM bank at sp+4
SwitchToBankAtSP: ; 9dc (0:9dc)
push af
push hl
ld hl, sp+$04
ld a, [hl]
call BankswitchROM
pop hl
pop af
inc sp
inc sp
ret
; RST28
; this function affects the stack so that it returns
; to the three byte pointer following the rst call
FarCall: ; 09e9 (0:09e9)
push hl
push hl
push hl
push hl
push de
push af
ld hl, sp+$d
ld d, [hl]
dec hl
ld e, [hl]
dec hl
ld [hl], $0
dec hl
ldh a, [hBankROM]
ld [hld], a
ld [hl], $9
dec hl
ld [hl], $dc
dec hl
inc de
inc de
ld a, [de]
ld [hld], a
dec de
ld a, [de]
ld [hl], a
dec de
ld a, [de]
inc de
jr Bank1Call_FarCall_Common
; setup SNES memory $810-$867 and palette
InitSGB: ; 0a0d (0:0a0d)
ld hl, MaskEnPacket_Freeze
call SendSGB
ld hl, DataSndPacket1
call SendSGB
ld hl, DataSndPacket2
call SendSGB
ld hl, DataSndPacket3
call SendSGB
ld hl, DataSndPacket4
call SendSGB
ld hl, DataSndPacket5
call SendSGB
ld hl, DataSndPacket6
call SendSGB
ld hl, DataSndPacket7
call SendSGB
ld hl, DataSndPacket8
call SendSGB
ld hl, Pal01Packet_InitSGB
call SendSGB
ld hl, MaskEnPacket_Cancel
call SendSGB
ret
DataSndPacket1: ; 0a50 (0:0a50)
sgb DATA_SND, 1 ; sgb_command, length
dwb $085d, $00 ; destination address, bank
db $0b ; number of bytes to write
db $8c, $d0, $f4, $60, $00, $00, $00, $00, $00, $00, $00 ; data bytes
DataSndPacket2: ; 0a60 (0:0a60)
sgb DATA_SND, 1 ; sgb_command, length
dwb $0852, $00 ; destination address, bank
db $0b ; number of bytes to write
db $a9, $e7, $9f, $01, $c0, $7e, $e8, $e8, $e8, $e8, $e0 ; data bytes
DataSndPacket3: ; 0a70 (0:0a70)
sgb DATA_SND, 1 ; sgb_command, length
dwb $0847, $00 ; destination address, bank
db $0b ; number of bytes to write
db $c4, $d0, $16, $a5, $cb, $c9, $05, $d0, $10, $a2, $28 ; data bytes
DataSndPacket4: ; 0a80 (0:0a80)
sgb DATA_SND, 1 ; sgb_command, length
dwb $083c, $00 ; destination address, bank
db $0b ; number of bytes to write
db $f0, $12, $a5, $c9, $c9, $c8, $d0, $1c, $a5, $ca, $c9 ; data bytes
DataSndPacket5: ; 0a90 (0:0a90)
sgb DATA_SND, 1 ; sgb_command, length
dwb $0831, $00 ; destination address, bank
db $0b ; number of bytes to write
db $0c, $a5, $ca, $c9, $7e, $d0, $06, $a5, $cb, $c9, $7e ; data bytes
DataSndPacket6: ; 0aa0 (0:0aa0)
sgb DATA_SND, 1 ; sgb_command, length
dwb $0826, $00 ; destination address, bank
db $0b ; number of bytes to write
db $39, $cd, $48, $0c, $d0, $34, $a5, $c9, $c9, $80, $d0 ; data bytes
DataSndPacket7: ; 0ab0 (0:0ab0)
sgb DATA_SND, 1 ; sgb_command, length
dwb $081b, $00 ; destination address, bank
db $0b ; number of bytes to write
db $ea, $ea, $ea, $ea, $ea, $a9, $01, $cd, $4f, $0c, $d0 ; data bytes
DataSndPacket8: ; 0ac0 (0:0ac0)
sgb DATA_SND, 1 ; sgb_command, length
dwb $0810, $00 ; destination address, bank
db $0b ; number of bytes to write
db $4c, $20, $08, $ea, $ea, $ea, $ea, $ea, $60, $ea, $ea ; data bytes
MaskEnPacket_Freeze: ; 0ad0 (0:0ad0)
sgb MASK_EN, 1 ; sgb_command, length
db MASK_EN_FREEZE_SCREEN
ds $0e
MaskEnPacket_Cancel: ; 0ae0 (0:0ae0)
sgb MASK_EN, 1 ; sgb_command, length
db MASK_EN_CANCEL_MASK
ds $0e
Pal01Packet_InitSGB: ; 0af0 (0:0af0)
sgb PAL01, 1 ; sgb_command, length
rgb 28, 28, 24
rgb 20, 20, 16
rgb 8, 8, 8
rgb 0, 0, 0
rgb 31, 0, 0
rgb 15, 0, 0
rgb 7, 0, 0
db $00
Pal23Packet_0b00: ; 0b00 (0:0b00)
sgb PAL23, 1 ; sgb_command, length
rgb 0, 31, 0
rgb 0, 15, 0
rgb 0, 7, 0
rgb 0, 0, 0
rgb 0, 0, 31
rgb 0, 0, 15
rgb 0, 0, 7
db $00
AttrBlkPacket_0b10: ; 0b10 (0:0b10)
sgb ATTR_BLK, 1 ; sgb_command, length
db 1 ; number of data sets
; Control Code, Color Palette Designation, X1, Y1, X2, Y2
db ATTR_BLK_CTRL_INSIDE + ATTR_BLK_CTRL_LINE, 1 << 0 + 2 << 2, 5, 5, 10, 10 ; data set 1
ds 6 ; data set 2
ds 2 ; data set 3
; send SGB packet at hl (or packets, if length > 1)
SendSGB: ; 0b20 (0:0b20)
ld a, [hl]
and $7
ret z ; return if packet length is 0
ld b, a ; length (1-7)
ld c, LOW(rJOYP)
.send_packets_loop
push bc
ld a, $0
ld [$ff00+c], a
ld a, P15 | P14
ld [$ff00+c], a
ld b, SGB_PACKET_SIZE
.send_packet_loop
ld e, $8
ld a, [hli]
ld d, a
.read_byte_loop
bit 0, d
ld a, P14 ; '1' bit
jr nz, .transfer_bit
ld a, P15 ; '0' bit
.transfer_bit
ld [$ff00+c], a
ld a, P15 | P14
ld [$ff00+c], a
rr d
dec e
jr nz, .read_byte_loop
dec b
jr nz, .send_packet_loop
ld a, P15 ; stop bit
ld [$ff00+c], a
ld a, P15 | P14
ld [$ff00+c], a
pop bc
dec b
jr nz, .send_packets_loop
ld bc, 4
call Wait
ret
; SGB hardware detection
; return carry if SGB detected and disable multi-controller mode before returning
DetectSGB: ; 0b59 (0:0b59)
ld bc, 60
call Wait
ld hl, MltReq2Packet
call SendSGB
ld a, [rJOYP]
and %11
cp SNES_JOYPAD1
jr nz, .sgb
ld a, P15
ld [rJOYP], a
ld a, [rJOYP]
ld a, [rJOYP]
ld a, P15 | P14
ld [rJOYP], a
ld a, P14
ld [rJOYP], a
ld a, [rJOYP]
ld a, [rJOYP]
ld a, [rJOYP]
ld a, [rJOYP]
ld a, [rJOYP]
ld a, [rJOYP]
ld a, P15 | P14
ld [rJOYP], a
ld a, [rJOYP]
ld a, [rJOYP]
ld a, [rJOYP]
ld a, [rJOYP]
and %11
cp SNES_JOYPAD1
jr nz, .sgb
ld hl, MltReq1Packet
call SendSGB
or a
ret
.sgb
ld hl, MltReq1Packet
call SendSGB
scf
ret
MltReq1Packet: ; 0bab (0:0bab)
sgb MLT_REQ, 1 ; sgb_command, length
db MLT_REQ_1_PLAYER
ds $0e
MltReq2Packet: ; 0bbb (0:0bbb)
sgb MLT_REQ, 1 ; sgb_command, length
db MLT_REQ_2_PLAYERS
ds $0e
; fill v*Tiles1 and v*Tiles2 with data at hl
; write $0d sequences of $80,$81,$82,...,$94 separated each by $0c bytes to v*BGMap0
; send the SGB packet at de
Func_0bcb: ; 0bcb (0:0bcb)
di
push de
.wait_vbalnk
ld a, [rLY]
cp LY_VBLANK + 3
jr nz, .wait_vbalnk
ld a, LCDC_BGON | LCDC_OBJON | LCDC_WIN9C00
ld [rLCDC], a
ld a, %11100100
ld [rBGP], a
ld de, v0Tiles1
ld bc, v0BGMap0 - v0Tiles1
.tiles_loop
ld a, [hli]
ld [de], a
inc de
dec bc
ld a, b
or c
jr nz, .tiles_loop
ld hl, v0BGMap0
ld de, $000c
ld a, $80
ld c, $0d
.bgmap_outer_loop
ld b, $14
.bgmap_inner_loop
ld [hli], a
inc a
dec b
jr nz, .bgmap_inner_loop
add hl, de
dec c
jr nz, .bgmap_outer_loop
ld a, LCDC_BGON | LCDC_OBJON | LCDC_WIN9C00 | LCDC_ON
ld [rLCDC], a
pop hl
call SendSGB
ei
ret
; loops 63000 * bc cycles (~15 * bc ms)
Wait: ; 0c08 (0:0c08)
ld de, 1750
.loop
nop
nop
nop
dec de
ld a, d
or e
jr nz, .loop
dec bc
ld a, b
or c
jr nz, Wait
ret
; copy b bytes of data from hl to de, but only during hblank
HblankCopyDataHLtoDE: ; 0c19 (0:0c19)
push bc
.loop
ei
di
ld a, [rSTAT] ;
and STAT_LCDC_STATUS ;
jr nz, .loop ; assert hblank
ld a, [hl]
ld [de], a
ld a, [rSTAT] ;
and STAT_LCDC_STATUS ;
jr nz, .loop ; assert still in hblank
ei
inc hl
inc de
dec b
jr nz, .loop
pop bc
ret
; copy c bytes of data from de to hl, but only during hblank
HblankCopyDataDEtoHL: ; 0c32 (0:0c32)
push bc
.loop
ei
di
ld a, [rSTAT] ;
and STAT_LCDC_STATUS ;
jr nz, .loop ; assert hblank
ld a, [de]
ld [hl], a
ld a, [rSTAT] ;
and STAT_LCDC_STATUS ;
jr nz, .loop ; assert still in hblank
ei
inc hl
inc de
dec c
jr nz, .loop
pop bc
ret
; returns a *= 10
ATimes10: ; 0c4b (0:0c4b)
push de
ld e, a
add a
add a
add e
add a
pop de
ret
; returns hl *= 10
HLTimes10: ; 0c53 (0:0c53)
push de
ld l, a
ld e, a
ld h, $00
ld d, h
add hl, hl
add hl, hl
add hl, de
add hl, hl
pop de
ret
; returns a /= 10
; returns carry if a % 10 >= 5
ADividedBy10: ; 0c5f (0:0c5f)
push de
ld e, -1
.asm_c62
inc e
sub 10
jr nc, .asm_c62
add 5
ld a, e
pop de
ret
; Save a pointer to a list, given at de, to wListPointer
SetListPointer: ; 0c6c (0:0c6c)
push hl
ld hl, wListPointer
ld [hl], e
inc hl
ld [hl], d
pop hl
ret
; Return the current element of the list at wListPointer,
; and advance the list to the next element
GetNextElementOfList: ; 0c75 (0:0c75)
push hl
push de
ld hl, wListPointer
ld e, [hl]
inc hl
ld d, [hl]
ld a, [de]
inc de
; fallthrough
SetListToNextPosition: ; 0c7f (0:0c7f)
ld [hl], d
dec hl
ld [hl], e
pop de
pop hl
ret
; Set the current element of the list at wListPointer to a,
; and advance the list to the next element
SetNextElementOfList: ; 0c85 (0:0c85)
push hl
push de
ld hl, wListPointer
ld e, [hl]
inc hl
ld d, [hl]
ld [de], a
inc de
jr SetListToNextPosition
; called at roughly 240Hz by TimerHandler
SerialTimerHandler: ; 0c91 (0:0c91)
ld a, [wSerialOp]
cp $29
jr z, .begin_transfer
cp $12
jr z, .check_for_timeout
ret
.begin_transfer
ld a, [rSC] ;
add a ; make sure that no serial transfer is active
ret c ;
ld a, SC_INTERNAL
ld [rSC], a ; use internal clock
ld a, SC_START | SC_INTERNAL
ld [rSC], a ; use internal clock, set transfer start flag
ret
.check_for_timeout
; sets bit7 of [wSerialFlags] if the serial interrupt hasn't triggered
; within four timer interrupts (60Hz)
ld a, [wSerialCounter]
ld hl, wSerialCounter2
cp [hl]
ld [hl], a
ld hl, wSerialTimeoutCounter
jr nz, .clear_timeout_counter
inc [hl]
ld a, [hl]
cp 4
ret c
ld hl, wSerialFlags
set 7, [hl]
ret
.clear_timeout_counter
ld [hl], $0
ret
Func_0cc5: ; 0cc5 (0:0cc5)
ld hl, wSerialRecvCounter
or a
jr nz, .asm_cdc
ld a, [hl]
or a
ret z
ld [hl], $00
ld a, [wSerialRecvBuf]
ld e, $12
cp $29
jr z, .asm_cfa
xor a
scf
ret
.asm_cdc
ld a, $29
ld [rSB], a
ld a, SC_INTERNAL
ld [rSC], a
ld a, SC_START | SC_INTERNAL
ld [rSC], a
.asm_ce8
ld a, [hl]
or a
jr z, .asm_ce8
ld [hl], $00
ld a, [wSerialRecvBuf]
ld e, $29
cp $12
jr z, .asm_cfa
xor a
scf
ret
.asm_cfa
xor a
ld [wSerialSendBufIndex], a
ld [wcb80], a
ld [wSerialSendBufToggle], a
ld [wSerialSendSave], a
ld [wcba3], a
ld [wSerialRecvIndex], a
ld [wSerialRecvCounter], a
ld [wSerialLastReadCA], a
ld a, e
cp $29
jr nz, .asm_d21
ld bc, $800
.asm_d1b
dec bc
ld a, c
or b
jr nz, .asm_d1b
ld a, e
.asm_d21
ld [wSerialOp], a
scf
ret
SerialHandler: ; 0d26 (0:0d26)
push af
push hl
push de
push bc
ld a, [wce63] ;
or a ;
jr z, .asm_d35 ; if [wce63] nonzero:
call Func_3189 ; ?
jr .done ; return
.asm_d35
ld a, [wSerialOp] ;
or a ;
jr z, .asm_d55 ; skip ahead if [wSerialOp] zero
; send/receive a byte
ld a, [rSB]
call SerialHandleRecv
call SerialHandleSend ; returns byte to actually send
push af
.wait_for_completion
ld a, [rSC]
add a
jr c, .wait_for_completion
pop af
; end send/receive
ld [rSB], a ; prepare sending byte (from Func_0dc8?)
ld a, [wSerialOp]
cp $29
jr z, .done ; if [wSerialOp] != $29, use external clock
jr .asm_d6a ; and prepare for next byte. either way, return
.asm_d55
ld a, $1
ld [wSerialRecvCounter], a
ld a, [rSB]
ld [wSerialRecvBuf], a
ld a, $ac
ld [rSB], a
ld a, [wSerialRecvBuf]
cp $12 ; if [wSerialRecvBuf] != $12, use external clock
jr z, .done ; and prepare for next byte. either way, return
.asm_d6a
ld a, SC_START | SC_EXTERNAL
ld [rSC], a ; transfer start, use external clock
.done
ld hl, wSerialCounter
inc [hl]
pop bc
pop de
pop hl
pop af
reti
; handles a byte read from serial transfer by decoding it and storing it into
; the receive buffer
SerialHandleRecv: ; 0d77 (0:0d77)
ld hl, wSerialLastReadCA
ld e, [hl]
dec e
jr z, .last_was_ca
cp $ac
ret z ; return if read_data == $ac
cp $ca
jr z, .read_ca
or a
jr z, .read_00_or_ff
cp $ff
jr nz, .read_data
.read_00_or_ff
ld hl, wSerialFlags
set 6, [hl]
ret
.read_ca
inc [hl] ; inc [wSerialLastReadCA]
ret
.last_was_ca
; if last byte read was $ca, flip all bits of data received
ld [hl], $0
cpl
jr .handle_byte
.read_data
; flip top2 bits of data received
xor $c0
.handle_byte
push af
ld a, [wSerialRecvIndex]
ld e, a
ld a, [wcba3]
dec a
and $1f
cp e
jr z, .set_flag_and_return
ld d, $0
; store into receive buffer
ld hl, wSerialRecvBuf
add hl, de
pop af
ld [hl], a
; increment buffer index (mod 32)
ld a, e
inc a
and $1f
ld [wSerialRecvIndex], a
; increment received bytes counter & clear flags
ld hl, wSerialRecvCounter
inc [hl]
xor a
ld [wSerialFlags], a
ret
.set_flag_and_return
pop af
ld hl, wSerialFlags
set 0, [hl]
ret
; prepares a byte to send over serial transfer, either from the send-save byte
; slot or the send buffer
SerialHandleSend: ; 0dc8 (0:0dc8)
ld hl, wSerialSendSave
ld a, [hl]
or a
jr nz, .send_saved
ld hl, wSerialSendBufToggle
ld a, [hl]
or a
jr nz, .send_buf
; no more data--send $ac to indicate this
ld a, $ac
ret
.send_saved
ld a, [hl]
ld [hl], $0
ret
.send_buf
; grab byte to send from send buffer, increment buffer index
; and decrement to-send length
dec [hl]
ld a, [wSerialSendBufIndex]
ld e, a
ld d, $0
ld hl, wSerialSendBuf
add hl, de
inc a
and $1f
ld [wSerialSendBufIndex], a
ld a, [hl]
; flip top2 bits of sent data
xor $c0
cp $ac
jr z, .send_escaped
cp $ca
jr z, .send_escaped
cp $ff
jr z, .send_escaped
or a
jr z, .send_escaped
ret
.send_escaped
; escape tricky data by prefixing it with $ca and flipping all bits
; instead of just top2
xor $c0
cpl
ld [wSerialSendSave], a
ld a, $ca
ret
; store byte at a in wSerialSendBuf for sending
SerialSendByte: ; 0e0a (0:0e0a)
push hl
push de
push bc
push af
.asm_e0e
ld a, [wcb80]
ld e, a
ld a, [wSerialSendBufIndex]
dec a
and $1f
cp e
jr z, .asm_e0e
ld d, $0
ld a, e
inc a
and $1f
ld [wcb80], a
ld hl, wSerialSendBuf
add hl, de
pop af
ld [hl], a
ld hl, wSerialSendBufToggle
inc [hl]
pop bc
pop de
pop hl
ret
; sets carry if [wSerialRecvCounter] nonzero
Func_0e32: ; 0e32 (0:0e32)
ld a, [wSerialRecvCounter]
or a
ret z
scf
ret
; receive byte in wSerialRecvBuf
SerialRecvByte: ; 0e39 (0:0e39)
push hl
ld hl, wSerialRecvCounter
ld a, [hl]
or a
jr nz, .asm_e49
pop hl
ld a, [wSerialFlags]
or a
ret nz
scf
ret
.asm_e49
push de
dec [hl]
ld a, [wcba3]
ld e, a
ld d, $0
ld hl, wSerialRecvBuf
add hl, de
ld a, [hl]
push af
ld a, e
inc a
and $1f
ld [wcba3], a
pop af
pop de
pop hl
or a
ret
; exchange c bytes. send bytes at hl and store received bytes in de
SerialExchangeBytes: ; 0e63 (0:0e63)
ld b, c
.asm_e64
ld a, b
sub c
jr c, .asm_e6c
cp $1f
jr nc, .asm_e75
.asm_e6c
inc c
dec c
jr z, .asm_e75
ld a, [hli]
call SerialSendByte
dec c
.asm_e75
inc b
dec b
jr z, .asm_e81
call SerialRecvByte
jr c, .asm_e81
ld [de], a
inc de
dec b
.asm_e81
ld a, [wSerialFlags]
or a
jr nz, .asm_e8c
ld a, c
or b
jr nz, .asm_e64
ret
.asm_e8c
scf
ret
; go into slave mode (external clock) for serial transfer?
Func_0e8e: ; 0e8e (0:0e8e)
call ClearSerialData
ld a, $12
ld [rSB], a ; send $12
ld a, SC_START | SC_EXTERNAL
ld [rSC], a ; use external clock, set transfer start flag
ld a, [rIF]
and ~(1 << INT_SERIAL)
ld [rIF], a ; clear serial interrupt flag
ld a, [rIE]
or 1 << INT_SERIAL ; enable serial interrupt
ld [rIE], a
ret
; disable serial interrupt, and clear rSB, rSC, and serial registers in WRAM
ResetSerial: ; 0ea6 (0:0ea6)
ld a, [rIE]
and ~(1 << INT_SERIAL)
ld [rIE], a
xor a
ld [rSB], a
ld [rSC], a
; fallthrough
; zero serial registers in WRAM
ClearSerialData: ; 0eb1 (0:0eb1)
ld hl, wSerialOp
ld bc, wSerialEnd - wSerialOp
.loop
xor a
ld [hli], a
dec bc
ld a, c
or b
jr nz, .loop
ret
; store bc bytes from hl in wSerialSendBuf for sending
SerialSendBytes: ; 0ebf (0:0ebf)
push bc
.send_loop
ld a, [hli]
call SerialSendByte
ld a, [wSerialFlags]
or a
jr nz, .done
dec bc
ld a, c
or b
jr nz, .send_loop
pop bc
or a
ret
.done
pop bc
scf
ret
; receive bc bytes in wSerialRecvBuf and save them to hl
SerialRecvBytes: ; 0ed5 (0:0ed5)
push bc
.recv_loop
call SerialRecvByte
jr nc, .save_byte
halt
nop
jr .recv_loop
.save_byte
ld [hli], a
ld a, [wSerialFlags]
or a
jr nz, .done
dec bc
ld a, c
or b
jr nz, .recv_loop
pop bc
or a
ret
.done
pop bc
scf
ret
Func_0ef1: ; 0ef1 (0:0ef1)
ld de, wcb79
ld hl, sp+$fe
ld a, l
ld [de], a
inc de
ld a, h
ld [de], a
inc de
pop hl
push hl
ld a, l
ld [de], a
inc de
ld a, h
ld [de], a
or a
ret
Func_0f05: ; 0f05 (0:0f05)
push hl
ld hl, wcb7b
ld a, [hli]
or [hl]
pop hl
ret z
ld hl, wcb79
ld a, [hli]
ld h, a
ld l, a
ld sp, hl
ld hl, wcb7b
ld a, [hli]
ld h, [hl]
ld l, a
push hl
scf
ret
Func_0f1d: ; 0f1d (0:0f1d)
ld a, [wSerialFlags]
or a
jr nz, .asm_f27
call Func_0e32
ret nc
.asm_f27
ld a, $01
call BankswitchROM
ld hl, wcbf7
ld a, [hli]
ld h, [hl]
ld l, a
ld sp, hl
scf
ret
; load the number at wSerialFlags (error code?) to TxRam3, print
; TransmissionErrorText, exit the duel, and reset serial registers.
DuelTransmissionError: ; 0f35 (0:0f35)
ld a, [wSerialFlags]
ld l, a
ld h, 0
call LoadTxRam3
ldtx hl, TransmissionErrorText
call DrawWideTextBox_WaitForInput
ld a, -1
ld [wDuelResult], a
ld hl, wDuelReturnAddress
ld a, [hli]
ld h, [hl]
ld l, a
ld sp, hl
xor a
call PlaySong
call ResetSerial
ret
; exchange RNG during a link duel between both games
ExchangeRNG: ; 0f58 (0:0f58)
ld a, [wDuelType]
cp DUELTYPE_LINK
jr z, .link_duel
ret
.link_duel
ld a, DUELVARS_DUELIST_TYPE
call GetTurnDuelistVariable
or a ; cp DUELIST_TYPE_PLAYER
jr z, .player_turn
; link opponent's turn
ld hl, wOppRNG1
ld de, wRNG1
jr .exchange
.player_turn
ld hl, wRNG1
ld de, wOppRNG1
.exchange
ld c, 3 ; wRNG1, wRNG2, and wRNGCounter
call SerialExchangeBytes
jp c, DuelTransmissionError
ret
; sets hOppActionTableIndex to an AI action specified in register a.
; send 10 bytes of data to the other game from hOppActionTableIndex, hTempCardIndex_ff9f,
; hTemp_ffa0, and hTempPlayAreaLocation_ffa1, and hTempRetreatCostCards.
; finally exchange RNG data.
; the receiving side will use this data to read the OPP_ACTION_* value in
; [hOppActionTableIndex] and match it by calling the correspoding OppAction* function
SetOppAction_SerialSendDuelData: ; 0f7f (0:0f7f)
push hl
push bc
ldh [hOppActionTableIndex], a
ld a, DUELVARS_DUELIST_TYPE
call GetNonTurnDuelistVariable
cp DUELIST_TYPE_LINK_OPP
jr nz, .not_link
ld hl, hOppActionTableIndex
ld bc, 10
call SerialSendBytes
call ExchangeRNG
.not_link
pop bc
pop hl
ret
; receive 10 bytes of data from wSerialRecvBuf and store them into hOppActionTableIndex,
; hTempCardIndex_ff9f, hTemp_ffa0, and hTempPlayAreaLocation_ffa1,
; and hTempRetreatCostCards. also exchange RNG data.
SerialRecvDuelData: ; 0f9b (0:0f9b)
push hl
push bc
ld hl, hOppActionTableIndex
ld bc, 10
call SerialRecvBytes
call ExchangeRNG
pop bc
pop hl
ret
; serial send 8 bytes at f, a, l, h, e, d, c, b
; only during a duel against a link opponent
SerialSend8Bytes: ; 0fac (0:0fac)
push hl
push af
ld a, DUELVARS_DUELIST_TYPE
call GetNonTurnDuelistVariable
cp DUELIST_TYPE_LINK_OPP
jr z, .link
pop af
pop hl
ret
.link
pop af
pop hl
push af
push hl
push de
push bc
push de
push hl
push af
ld hl, wTempSerialBuf
pop de
ld [hl], e
inc hl
ld [hl], d
inc hl
pop de
ld [hl], e
inc hl
ld [hl], d
inc hl
pop de
ld [hl], e
inc hl
ld [hl], d
inc hl
ld [hl], c
inc hl
ld [hl], b
ld hl, wTempSerialBuf
ld bc, 8
call SerialSendBytes
jp c, DuelTransmissionError
pop bc
pop de
pop hl
pop af
ret
; serial recv 8 bytes to f, a, l, h, e, d, c, b
SerialRecv8Bytes: ; 0fe9 (0:0fe9)
ld hl, wTempSerialBuf
ld bc, 8
push hl
call SerialRecvBytes
jp c, DuelTransmissionError
pop hl
ld e, [hl]
inc hl
ld d, [hl]
inc hl
push de
ld e, [hl]
inc hl
ld d, [hl]
inc hl
push de
ld e, [hl]
inc hl
ld d, [hl]
inc hl
ld c, [hl]
inc hl
ld b, [hl]
pop hl
pop af
ret
; save duel state to SRAM
; called between each two-player turn, just after player draws card (ROM bank 1 loaded)
SaveDuelStateToSRAM: ; 100b (0:100b)
ld a, $2
call BankswitchSRAM
; save duel data to sCurrentDuel
call SaveDuelData
xor a
call BankswitchSRAM
call EnableSRAM
ld hl, s0a008
ld a, [hl]
inc [hl]
call DisableSRAM
; select hl = SRAM3:(a000 + $400 * [s0a008] & $3)
; save wDuelTurns, non-turn holder's arena card ID, turn holder's arena card ID
and $3
add HIGH($a000) / 4
ld l, $0
ld h, a
add hl, hl
add hl, hl
ld a, $3
call BankswitchSRAM
push hl
ld a, DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
call GetCardIDFromDeckIndex
ld a, e
ld [wTempTurnDuelistCardID], a
call SwapTurn
ld a, DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
call GetCardIDFromDeckIndex
ld a, e
ld [wTempNonTurnDuelistCardID], a
call SwapTurn
pop hl
push hl
call EnableSRAM
ld a, [wDuelTurns]
ld [hli], a
ld a, [wTempNonTurnDuelistCardID]
ld [hli], a
ld a, [wTempTurnDuelistCardID]
ld [hli], a
; save duel data to SRAM3:(a000 + $400 * [s0a008] & $3) + $0010
pop hl
ld de, $0010
add hl, de
ld e, l
ld d, h
call DisableSRAM
bank1call SaveDuelDataToDE
xor a
call BankswitchSRAM
ret
; copies the deck pointed to by de to wPlayerDeck or wOpponentDeck (depending on whose turn it is)
CopyDeckData: ; 1072 (0:1072)
ld hl, wPlayerDeck
ldh a, [hWhoseTurn]
cp PLAYER_TURN
jr z, .copy_deck_data
ld hl, wOpponentDeck
.copy_deck_data
; start by putting a terminator at the end of the deck
push hl
ld bc, DECK_SIZE - 1
add hl, bc
ld [hl], $0
pop hl
push hl
.next_card
ld a, [de]
inc de
ld b, a
or a
jr z, .done
ld a, [de]
inc de
ld c, a
.card_quantity_loop
ld [hl], c
inc hl
dec b
jr nz, .card_quantity_loop
jr .next_card
.done
ld hl, wcce9
ld a, [de]
inc de
ld [hli], a
ld a, [de]
ld [hl], a
pop hl
ld bc, DECK_SIZE - 1
add hl, bc
ld a, [hl]
or a
ret nz
debug_ret
scf
ret
; return, in register a, the amount of prizes that the turn holder has not yet drawn
CountPrizes: ; 10aa (0:10aa)
push hl
ld a, DUELVARS_PRIZES
call GetTurnDuelistVariable
ld l, a
xor a
.count_loop
rr l
adc $00
inc l
dec l
jr nz, .count_loop
pop hl
ret
; shuffles the turn holder's deck
; if less than 60 cards remain in the deck, it makes sure that the rest are ignored
ShuffleDeck: ; 10bc (0:10bc)
ldh a, [hWhoseTurn]
ld h, a
ld d, a
ld a, DECK_SIZE
ld l, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
sub [hl]
ld b, a
ld a, DUELVARS_DECK_CARDS
add [hl]
ld l, a ; hl = DUELVARS_DECK_CARDS + [DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK]
ld a, b ; a = number of cards in the deck
call ShuffleCards
ret
; draw a card from the turn holder's deck, saving its location as CARD_LOCATION_JUST_DRAWN.
; returns carry if deck is empty, nc if a card was succesfully drawn.
; AddCardToHand is meant to be called next (unless this function returned carry).
DrawCardFromDeck: ; 10cf (0:10cf)
push hl
ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
call GetTurnDuelistVariable
cp DECK_SIZE
jr nc, .empty_deck
inc a
ld [hl], a ; increment number of cards not in deck
add DUELVARS_DECK_CARDS - 1 ; point to top card in the deck
ld l, a
ld a, [hl] ; grab card number (0-59) from wPlayerDeckCards or wOpponentDeckCards array
ld l, a
ld [hl], CARD_LOCATION_JUST_DRAWN ; temporarily write to corresponding card location variable
pop hl
or a
ret
.empty_deck
pop hl
scf
ret
; add a card to the top of the turn holder's deck
; the card is identified by register a, which contains the deck index (0-59) of the card
ReturnCardToDeck: ; 10e8 (0:10e8)
push hl
push af
ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
call GetTurnDuelistVariable
dec a
ld [hl], a ; decrement number of cards not in deck
add DUELVARS_DECK_CARDS
ld l, a ; point to top deck card
pop af
ld [hl], a ; set top deck card
ld l, a
ld [hl], CARD_LOCATION_DECK
ld a, l
pop hl
ret
; search a card in the turn holder's deck, extract it, and set its location to
; CARD_LOCATION_JUST_DRAWN. AddCardToHand is meant to be called next.
; the card is identified by register a, which contains the deck index (0-59) of the card.
SearchCardInDeckAndAddToHand: ; 10fc (0:10fc)
push af
push hl
push de
push bc
ld c, a
ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
call GetTurnDuelistVariable
ld a, DECK_SIZE
sub [hl]
inc [hl] ; increment number of cards not in deck
ld b, a ; DECK_SIZE - [DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK] (number of cards in deck)
ld l, c
set CARD_LOCATION_JUST_DRAWN_F, [hl]
ld l, DUELVARS_DECK_CARDS + DECK_SIZE - 1
ld e, l
ld d, h ; hl = de = DUELVARS_DECK_CARDS + DECK_SIZE - 1 (last card)
inc b
jr .match
.loop
ld a, [hld]
cp c
jr z, .match
ld [de], a
dec de
.match
dec b
jr nz, .loop
pop bc
pop de
pop hl
pop af
ret
; adds a card to the turn holder's hand and increments the number of cards in the hand
; the card is identified by register a, which contains the deck index (0-59) of the card
AddCardToHand: ; 1123 (0:1123)
push af
push hl
push de
ld e, a
ld l, a
ldh a, [hWhoseTurn]
ld h, a
; write CARD_LOCATION_HAND into the location of this card
ld [hl], CARD_LOCATION_HAND
; increment number of cards in hand
ld l, DUELVARS_NUMBER_OF_CARDS_IN_HAND
inc [hl]
; add card to hand
ld a, DUELVARS_HAND - 1
add [hl]
ld l, a
ld [hl], e
pop de
pop hl
pop af
ret
; removes a card from the turn holder's hand and decrements the number of cards in the hand
; the card is identified by register a, which contains the deck index (0-59) of the card
RemoveCardFromHand: ; 1139 (0:1139)
push af
push hl
push bc
push de
ld c, a
ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND
call GetTurnDuelistVariable
or a
jr z, .done ; done if no cards in hand
ld b, a ; number of cards in hand
ld l, DUELVARS_HAND
ld e, l
ld d, h
.next_card
ld a, [hli]
cp c
jr nz, .no_match
push hl
ld l, DUELVARS_NUMBER_OF_CARDS_IN_HAND
dec [hl]
pop hl
jr .done_card
.no_match
ld [de], a ; keep any card that doesn't match in the player's hand
inc de
.done_card
dec b
jr nz, .next_card
.done
pop de
pop bc
pop hl
pop af
ret
; moves a card to the turn holder's discard pile, as long as it is in the hand
; the card is identified by register a, which contains the deck index (0-59) of the card
MoveHandCardToDiscardPile: ; 1160 (0:1160)
call GetTurnDuelistVariable
ld a, [hl]
and $ff ^ CARD_LOCATION_JUST_DRAWN
cp CARD_LOCATION_HAND
ret nz ; return if card not in hand
ld a, l
call RemoveCardFromHand
; fallthrough
; puts the turn holder's card with the deck index (0-59) given in a into the discard pile
PutCardInDiscardPile: ; 116a (0:116a)
push af
push hl
push de
call GetTurnDuelistVariable
ld [hl], CARD_LOCATION_DISCARD_PILE
ld e, l
ld l, DUELVARS_NUMBER_OF_CARDS_IN_DISCARD_PILE
inc [hl]
ld a, DUELVARS_DECK_CARDS - 1
add [hl]
ld l, a
ld [hl], e ; save card to DUELVARS_DECK_CARDS + [DUELVARS_NUMBER_OF_CARDS_IN_DISCARD_PILE]
pop de
pop hl
pop af
ret
; search a card in the turn holder's discard pile, extract it, and set its location to
; CARD_LOCATION_JUST_DRAWN. AddCardToHand is meant to be called next.
; the card is identified by register a, which contains the deck index (0-59) of the card
MoveDiscardPileCardToHand: ; 1182 (0:1182)
push hl
push de
push bc
call GetTurnDuelistVariable
set CARD_LOCATION_JUST_DRAWN_F, [hl]
ld b, l
ld l, DUELVARS_NUMBER_OF_CARDS_IN_DISCARD_PILE
ld a, [hl]
or a
jr z, .done ; done if no cards in discard pile
ld c, a
dec [hl] ; decrement number of cards in discard pile
ld l, DUELVARS_DECK_CARDS
ld e, l
ld d, h ; de = hl = DUELVARS_DECK_CARDS
.next_card
ld a, [hli]
cp b
jr z, .match
ld [de], a
inc de
.match
dec c
jr nz, .next_card
ld a, b
.done
pop bc
pop de
pop hl
ret
; return in the z flag whether turn holder's prize a (0-7) has been drawn or not
; z: drawn, nz: not drawn
CheckPrizeTaken: ; 11a5 (0:11a5)
ld e, a
ld d, 0
ld hl, PowersOf2
add hl, de
ld a, [hl]
ld e, a
cpl
ld d, a
ld a, DUELVARS_PRIZES
call GetTurnDuelistVariable
and e
ret
PowersOf2:
db $01, $02, $04, $08, $10, $20, $40, $80
; fill wDuelTempList with the turn holder's discard pile cards (their 0-59 deck indexes)
; return carry if the turn holder has no cards in the discard pile
CreateDiscardPileCardList: ; 11bf (0:11bf)
ldh a, [hWhoseTurn]
ld h, a
ld l, DUELVARS_NUMBER_OF_CARDS_IN_DISCARD_PILE
ld b, [hl]
ld a, DUELVARS_DECK_CARDS - 1
add [hl] ; point to last card in discard pile
ld l, a
ld de, wDuelTempList
inc b
jr .begin_loop
.next_card_loop
ld a, [hld]
ld [de], a
inc de
.begin_loop
dec b
jr nz, .next_card_loop
ld a, $ff ; $ff-terminated
ld [de], a
ld l, DUELVARS_NUMBER_OF_CARDS_IN_DISCARD_PILE
ld a, [hl]
or a
ret nz
scf
ret
; fill wDuelTempList with the turn holder's remaining deck cards (their 0-59 deck indexes)
; return carry if the turn holder has no cards left in the deck
CreateDeckCardList: ; 11df (0:11df)
ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
call GetTurnDuelistVariable
cp DECK_SIZE
jr nc, .no_cards_left_in_deck
ld a, DECK_SIZE
sub [hl]
ld c, a
ld b, a ; c = b = DECK_SIZE - [DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK]
ld a, [hl]
add DUELVARS_DECK_CARDS
ld l, a ; l = DUELVARS_DECK_CARDS + [DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK]
inc b
ld de, wDuelTempList
jr .begin_loop
.next_card
ld a, [hli]
ld [de], a
inc de
.begin_loop
dec b
jr nz, .next_card
ld a, $ff ; $ff-terminated
ld [de], a
ld a, c
or a
ret
.no_cards_left_in_deck
ld a, $ff
ld [wDuelTempList], a
scf
ret
; fill wDuelTempList with the turn holder's energy cards
; in the arena or in a bench slot (their 0-59 deck indexes).
; if a == 0: search in CARD_LOCATION_ARENA
; if a != 0: search in CARD_LOCATION_BENCH_[A]
; return carry if no energy cards were found
CreateArenaOrBenchEnergyCardList: ; 120a (0:120a)
or CARD_LOCATION_PLAY_AREA
ld c, a
ld de, wDuelTempList
ld a, DUELVARS_CARD_LOCATIONS
call GetTurnDuelistVariable
.next_card_loop
ld a, [hl]
cp c
jr nz, .skip_card ; jump if not in specified play area location
ld a, l
call LoadCardDataToBuffer2_FromDeckIndex
ld a, [wLoadedCard2Type]
and 1 << TYPE_ENERGY_F
jr z, .skip_card ; jump if Pokemon or trainer card
ld a, l
ld [de], a ; add to wDuelTempList
inc de
.skip_card
inc l
ld a, l
cp DECK_SIZE
jr c, .next_card_loop
; all cards checked
ld a, $ff
ld [de], a
ld a, [wDuelTempList]
cp $ff
jr z, .no_energies_found
or a
ret
.no_energies_found
scf
ret
; fill wDuelTempList with the turn holder's hand cards (their 0-59 deck indexes)
; return carry if the turn holder has no cards in hand
; and outputs in a number of cards.
CreateHandCardList: ; 123b (0:123b)
call FindLastCardInHand
inc b
jr .skip_card
.check_next_card_loop
ld a, [hld]
push hl
ld l, a
bit CARD_LOCATION_JUST_DRAWN_F, [hl]
pop hl
jr nz, .skip_card
ld [de], a
inc de
.skip_card
dec b
jr nz, .check_next_card_loop
ld a, $ff ; $ff-terminated
ld [de], a
ld l, DUELVARS_NUMBER_OF_CARDS_IN_HAND
ld a, [hl]
or a
ret nz
scf
ret
; sort the turn holder's hand cards by ID (highest to lowest ID)
; makes use of wDuelTempList
SortHandCardsByID: ; 1258 (0:1258)
call FindLastCardInHand
.loop
ld a, [hld]
ld [de], a
inc de
dec b
jr nz, .loop
ld a, $ff
ld [de], a
call SortCardsInDuelTempListByID
call FindLastCardInHand
.loop2
ld a, [de]
inc de
ld [hld], a
dec b
jr nz, .loop2
ret
; returns:
; b = turn holder's number of cards in hand (DUELVARS_NUMBER_OF_CARDS_IN_HAND)
; hl = pointer to turn holder's last (newest) card in DUELVARS_HAND
; de = wDuelTempList
FindLastCardInHand: ; 1271 (0:1271)
ldh a, [hWhoseTurn]
ld h, a
ld l, DUELVARS_NUMBER_OF_CARDS_IN_HAND
ld b, [hl]
ld a, DUELVARS_HAND - 1
add [hl]
ld l, a
ld de, wDuelTempList
ret
; shuffles the deck by swapping the position of each card with the position of another random card
; input:
; a = how many cards to shuffle
; hl = DUELVARS_DECK_CARDS + [DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK]
ShuffleCards: ; 127f (0:127f)
or a
ret z ; return if deck is empty
push hl
push de
push bc
ld c, a
ld b, a
ld e, l
ld d, h
.shuffle_next_card_loop
push bc
push de
ld a, c
call Random
add e
ld e, a
ld a, $0
adc d
ld d, a
ld a, [de]
ld b, [hl]
ld [hl], a
ld a, b
ld [de], a
pop de
pop bc
inc hl
dec b
jr nz, .shuffle_next_card_loop
pop bc
pop de
pop hl
ret
; sort a $ff-terminated list of deck index cards by ID (lowest to highest ID).
; the list is wDuelTempList.
SortCardsInDuelTempListByID: ; 12a3 (0:12a3)
ld hl, hTempListPtr_ff99
ld [hl], LOW(wDuelTempList)
inc hl
ld [hl], HIGH(wDuelTempList)
jr SortCardsInListByID_CheckForListTerminator
; sort a $ff-terminated list of deck index cards by ID (lowest to highest ID).
; the pointer to the list is given in hTempListPtr_ff99.
; sorting by ID rather than deck index means that the order of equal (same ID) cards does not matter,
; even if they have a different deck index.
SortCardsInListByID: ; 12ad (0:12ad)
; load [hTempListPtr_ff99] into hl and de
ld hl, hTempListPtr_ff99
ld a, [hli]
ld h, [hl]
ld l, a
ld e, l
ld d, h
; get ID of card with deck index at [de]
ld a, [de]
call GetCardIDFromDeckIndex_bc
ld a, c
ldh [hTempCardID_ff9b], a
ld a, b
ldh [hTempCardID_ff9b + 1], a ; 0
; hl = [hTempListPtr_ff99] + 1
inc hl
jr .check_list_end
.next_card_in_list
ld a, [hl]
call GetCardIDFromDeckIndex_bc
ldh a, [hTempCardID_ff9b + 1]
cp b
jr nz, .go
ldh a, [hTempCardID_ff9b]
cp c
.go
jr c, .not_lower_id
; this card has the lowest ID of those checked so far
ld e, l
ld d, h
ld a, c
ldh [hTempCardID_ff9b], a
ld a, b
ldh [hTempCardID_ff9b + 1], a
.not_lower_id
inc hl
.check_list_end
bit 7, [hl] ; $ff is the list terminator
jr z, .next_card_in_list
; reached list terminator
ld hl, hTempListPtr_ff99
push hl
ld a, [hli]
ld h, [hl]
ld l, a
; swap the lowest ID card found with the card in the current list position
ld c, [hl]
ld a, [de]
ld [hl], a
ld a, c
ld [de], a
pop hl
; [hTempListPtr_ff99] += 1 (point hl to next card in list)
inc [hl]
jr nz, SortCardsInListByID_CheckForListTerminator
inc hl
inc [hl]
; fallthrough
SortCardsInListByID_CheckForListTerminator: ; 12ef (0:12ef)
ld hl, hTempListPtr_ff99
ld a, [hli]
ld h, [hl]
ld l, a
bit 7, [hl] ; $ff is the list terminator
jr z, SortCardsInListByID
ret
; returns, in register bc, the id of the card with the deck index specified in register a
; preserves hl
GetCardIDFromDeckIndex_bc: ; 12fa (0:12fa)
push hl
call _GetCardIDFromDeckIndex
ld c, a
ld b, $0
pop hl
ret
; return [wDuelTempList + a] in a and in hTempCardIndex_ff98
GetCardInDuelTempList_OnlyDeckIndex: ; 1303 (0:1303)
push hl
push de
ld e, a
ld d, $0
ld hl, wDuelTempList
add hl, de
ld a, [hl]
ldh [hTempCardIndex_ff98], a
pop de
pop hl
ret
; given the deck index (0-59) of a card in [wDuelTempList + a], return:
; - the id of the card with that deck index in register de
; - [wDuelTempList + a] in hTempCardIndex_ff98 and in register a
GetCardInDuelTempList: ; 1312 (0:1312)
push hl
ld e, a
ld d, $0
ld hl, wDuelTempList
add hl, de
ld a, [hl]
ldh [hTempCardIndex_ff98], a
call GetCardIDFromDeckIndex
pop hl
ldh a, [hTempCardIndex_ff98]
ret
; returns, in register de, the id of the card with the deck index (0-59) specified by register a
; preserves af and hl
GetCardIDFromDeckIndex: ; 1324 (0:1324)
push af
push hl
call _GetCardIDFromDeckIndex
ld e, a
ld d, $0
pop hl
pop af
ret
; remove card c from wDuelTempList (it contains a $ff-terminated list of deck indexes)
; returns carry if no matches were found.
RemoveCardFromDuelTempList: ; 132f (0:132f)
push hl
push de
push bc
ld hl, wDuelTempList
ld e, l
ld d, h
ld c, a
ld b, $00
.next
ld a, [hli]
cp $ff
jr z, .end_of_list
cp c
jr z, .match
ld [de], a
inc de
inc b
.match
jr .next
.end_of_list
ld [de], a
ld a, b
or a
jr nz, .done
scf
.done
pop bc
pop de
pop hl
ret
; return the number of cards in wDuelTempList in a
CountCardsInDuelTempList: ; 1351 (0:1351)
push hl
push bc
ld hl, wDuelTempList
ld b, -1
.loop
inc b
ld a, [hli]
cp $ff
jr nz, .loop
ld a, b
pop bc
pop hl
ret
; returns, in register a, the id of the card with the deck index (0-59) specified in register a
_GetCardIDFromDeckIndex: ; 1362 (0:1362)
push de
ld e, a
ld d, $0
ld hl, wPlayerDeck
ldh a, [hWhoseTurn]
cp PLAYER_TURN
jr z, .load_card_from_deck
ld hl, wOpponentDeck
.load_card_from_deck
add hl, de
ld a, [hl]
pop de
ret
; load data of card with deck index a (0-59) to wLoadedCard1
LoadCardDataToBuffer1_FromDeckIndex: ; 1376 (0:1376)
push hl
push de
push bc
push af
call GetCardIDFromDeckIndex
call LoadCardDataToBuffer1_FromCardID
pop af
ld hl, wLoadedCard1
bank1call ConvertSpecialTrainerCardToPokemon
ld a, e
pop bc
pop de
pop hl
ret
; load data of card with deck index a (0-59) to wLoadedCard2
LoadCardDataToBuffer2_FromDeckIndex: ; 138c (0:138c)
push hl
push de
push bc
push af
call GetCardIDFromDeckIndex
call LoadCardDataToBuffer2_FromCardID
pop af
ld hl, wLoadedCard2
bank1call ConvertSpecialTrainerCardToPokemon
ld a, e
pop bc
pop de
pop hl
ret
; evolve a turn holder's Pokemon card in the play area slot determined by hTempPlayAreaLocation_ff9d
; into another turn holder's Pokemon card identifier by its deck index (0-59) in hTempCardIndex_ff98.
; return nc if evolution was successful.
EvolvePokemonCardIfPossible: ; 13a2 (0:13a2)
; first make sure the attempted evolution is viable
ldh a, [hTempCardIndex_ff98]
ld d, a
ldh a, [hTempPlayAreaLocation_ff9d]
ld e, a
call CheckIfCanEvolveInto
ret c ; return if it's not capable of evolving into the selected Pokemon
; fallthrough
; evolve a turn holder's Pokemon card in the play area slot determined by hTempPlayAreaLocation_ff9d
; into another turn holder's Pokemon card identifier by its deck index (0-59) in hTempCardIndex_ff98.
EvolvePokemonCard: ; 13ac (0:13ac)
; place the evolved Pokemon card in the play area location of the pre-evolved Pokemon card
ldh a, [hTempPlayAreaLocation_ff9d]
ld e, a
add DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
ld [wccee], a ; save pre-evolved Pokemon card into wccee
call LoadCardDataToBuffer2_FromDeckIndex
ldh a, [hTempCardIndex_ff98]
ld [hl], a
call LoadCardDataToBuffer1_FromDeckIndex
ldh a, [hTempCardIndex_ff98]
call PutHandCardInPlayArea
; update the Pokemon's HP with the difference
ldh a, [hTempPlayAreaLocation_ff9d] ; derp
ld a, e
add DUELVARS_ARENA_CARD_HP
call GetTurnDuelistVariable
ld a, [wLoadedCard2HP]
ld c, a
ld a, [wLoadedCard1HP]
sub c
add [hl]
ld [hl], a
; reset status (if in arena) and set the flag that prevents it from evolving again this turn
ld a, e
add DUELVARS_ARENA_CARD_FLAGS
ld l, a
ld [hl], $00
ld a, e
add DUELVARS_ARENA_CARD_CHANGED_TYPE
ld l, a
ld [hl], $00
ld a, e
or a
call z, ClearAllStatusConditions
; set the new evolution stage of the card
ldh a, [hTempPlayAreaLocation_ff9d]
add DUELVARS_ARENA_CARD_STAGE
call GetTurnDuelistVariable
ld a, [wLoadedCard1Stage]
ld [hl], a
or a
ret
; never executed
scf
ret
; check if the turn holder's Pokemon card at e can evolve into the turn holder's Pokemon card d.
; e is the play area location offset (PLAY_AREA_*) of the Pokemon trying to evolve.
; d is the deck index (0-59) of the Pokemon card that was selected to be the evolution target.
; return carry if can't evolve, plus nz if the reason for it is the card was played this turn.
CheckIfCanEvolveInto: ; 13f7 (0:13f7)
push de
ld a, e
add DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
call LoadCardDataToBuffer2_FromDeckIndex
ld a, d
call LoadCardDataToBuffer1_FromDeckIndex
ld hl, wLoadedCard2Name
ld de, wLoadedCard1PreEvoName
ld a, [de]
cp [hl]
jr nz, .cant_evolve ; jump if they are incompatible to evolve
inc de
inc hl
ld a, [de]
cp [hl]
jr nz, .cant_evolve ; jump if they are incompatible to evolve
pop de
ld a, e
add DUELVARS_ARENA_CARD_FLAGS
call GetTurnDuelistVariable
and CAN_EVOLVE_THIS_TURN
jr nz, .can_evolve
; if the card trying to evolve was played this turn, it can't evolve
ld a, $01
or a
scf
ret
.can_evolve
or a
ret
.cant_evolve
pop de
xor a
scf
ret
; check if the turn holder's Pokemon card at e can evolve this turn, and is a basic
; Pokemon card that whose second stage evolution is the turn holder's Pokemon card d.
; e is the play area location offset (PLAY_AREA_*) of the Pokemon trying to evolve.
; d is the deck index (0-59) of the Pokemon card that was selected to be the evolution target.
; return carry if not basic to stage 2 evolution, or if evolution not possible this turn.
CheckIfCanEvolveInto_BasicToStage2: ; 142b (0:142b)
ld a, e
add DUELVARS_ARENA_CARD_FLAGS
call GetTurnDuelistVariable
and CAN_EVOLVE_THIS_TURN
jr nz, .can_evolve
jr .cant_evolve
.can_evolve
ld a, e
add DUELVARS_ARENA_CARD
ld l, a
ld a, [hl]
call LoadCardDataToBuffer2_FromDeckIndex
ld a, d
call LoadCardDataToBuffer1_FromDeckIndex
ld hl, wLoadedCard1PreEvoName
ld e, [hl]
inc hl
ld d, [hl]
call LoadCardDataToBuffer1_FromName
ld hl, wLoadedCard2Name
ld de, wLoadedCard1PreEvoName
ld a, [de]
cp [hl]
jr nz, .cant_evolve
inc de
inc hl
ld a, [de]
cp [hl]
jr nz, .cant_evolve
or a
ret
.cant_evolve
xor a
scf
ret
; clear the status, all substatuses, and temporary duelvars of the turn holder's
; arena Pokemon. called when sending a new Pokemon into the arena.
; does not reset Headache, since it targets a player rather than a Pokemon.
ClearAllStatusConditions: ; 1461 (0:1461)
push hl
ldh a, [hWhoseTurn]
ld h, a
xor a
ld l, DUELVARS_ARENA_CARD_STATUS
ld [hl], a ; NO_STATUS
ld l, DUELVARS_ARENA_CARD_SUBSTATUS1
ld [hl], a
ld l, DUELVARS_ARENA_CARD_SUBSTATUS2
ld [hl], a
ld l, DUELVARS_ARENA_CARD_CHANGED_WEAKNESS
ld [hl], a
ld l, DUELVARS_ARENA_CARD_CHANGED_RESISTANCE
ld [hl], a
ld l, DUELVARS_ARENA_CARD_SUBSTATUS3
res SUBSTATUS3_THIS_TURN_DOUBLE_DAMAGE, [hl]
ld l, DUELVARS_ARENA_CARD_DISABLED_MOVE_INDEX
ld [hli], a
ld [hli], a
ld [hli], a
ld [hli], a
ld [hli], a
ld [hli], a
ld [hli], a
ld [hl], a
pop hl
ret
; Removes a Pokemon card from the hand and places it in the arena or first available bench slot.
; If the Pokemon is placed in the arena, the status conditions of the player's arena card are zeroed.
; input:
; a = deck index of the card
; return carry if there is no room for more Pokemon
PutHandPokemonCardInPlayArea: ; 1485 (0:1485)
push af
ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
call GetTurnDuelistVariable
cp MAX_PLAY_AREA_POKEMON
jr nc, .already_max_pkmn_in_play
inc [hl]
ld e, a ; play area offset to place card
pop af
push af
call PutHandCardInPlayArea
ld a, e
add DUELVARS_ARENA_CARD
ld l, a
pop af
ld [hl], a ; set card in arena or benchx
call LoadCardDataToBuffer2_FromDeckIndex
ld a, DUELVARS_ARENA_CARD_HP
add e
ld l, a
ld a, [wLoadedCard2HP]
ld [hl], a ; set card's HP
ld a, DUELVARS_ARENA_CARD_FLAGS
add e
ld l, a
ld [hl], $0
ld a, DUELVARS_ARENA_CARD_CHANGED_TYPE
add e
ld l, a
ld [hl], $0
ld a, DUELVARS_ARENA_CARD_ATTACHED_PLUSPOWER
add e
ld l, a
ld [hl], $0
ld a, DUELVARS_ARENA_CARD_ATTACHED_DEFENDER
add e
ld l, a
ld [hl], $0
ld a, DUELVARS_ARENA_CARD_STAGE
add e
ld l, a
ld a, [wLoadedCard2Stage]
ld [hl], a ; set card's evolution stage
ld a, e
or a
call z, ClearAllStatusConditions ; only call if Pokemon is being placed in the arena
ld a, e
or a
ret
.already_max_pkmn_in_play
pop af
scf
ret
; Removes a card from the hand and changes its location to arena or bench. Given that
; DUELVARS_ARENA_CARD or DUELVARS_BENCH aren't affected, this function is meant for energy and trainer cards.
; input:
; a = deck index of the card
; e = play area location offset (PLAY_AREA_*)
; returns:
; a = CARD_LOCATION_PLAY_AREA + e
PutHandCardInPlayArea: ; 14d2 (0:14d2)
call RemoveCardFromHand
call GetTurnDuelistVariable
ld a, e
or CARD_LOCATION_PLAY_AREA
ld [hl], a
ret
; move the Pokemon card of the turn holder in the
; PLAY_AREA_* location given in e to the discard pile
MovePlayAreaCardToDiscardPile: ; 14dd (0:14dd)
call EmptyPlayAreaSlot
ld l, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
dec [hl]
ld l, DUELVARS_CARD_LOCATIONS
.next_card
ld a, e
or CARD_LOCATION_PLAY_AREA
cp [hl]
jr nz, .not_in_location
push de
ld a, l
call PutCardInDiscardPile
pop de
.not_in_location
inc l
ld a, l
cp DECK_SIZE
jr c, .next_card
ret
; init a turn holder's play area slot to empty
; which slot (arena or benchx) is determined by the play area location offset (PLAY_AREA_*) in e
EmptyPlayAreaSlot: ; 14f8 (0:14f8)
ldh a, [hWhoseTurn]
ld h, a
ld d, -1
ld a, DUELVARS_ARENA_CARD
call .init_duelvar
ld d, 0
ld a, DUELVARS_ARENA_CARD_HP
call .init_duelvar
ld a, DUELVARS_ARENA_CARD_STAGE
call .init_duelvar
ld a, DUELVARS_ARENA_CARD_CHANGED_TYPE
call .init_duelvar
ld a, DUELVARS_ARENA_CARD_ATTACHED_DEFENDER
call .init_duelvar
ld a, DUELVARS_ARENA_CARD_ATTACHED_PLUSPOWER
.init_duelvar
add e
ld l, a
ld [hl], d
ret
; shift play area Pokemon of both players to the first available play area (arena + benchx) slots
ShiftAllPokemonToFirstPlayAreaSlots: ; 151e (0:151e)
call ShiftTurnPokemonToFirstPlayAreaSlots
call SwapTurn
call ShiftTurnPokemonToFirstPlayAreaSlots
call SwapTurn
ret
; shift play area Pokemon of the turn holder to the first available play area (arena + benchx) slots
ShiftTurnPokemonToFirstPlayAreaSlots: ; 152b (0:152b)
ld a, DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
lb de, PLAY_AREA_ARENA, PLAY_AREA_ARENA
.next_play_area_slot
bit 7, [hl]
jr nz, .empty_slot
call SwapPlayAreaPokemon
inc e
.empty_slot
inc hl
inc d
ld a, d
cp MAX_PLAY_AREA_POKEMON
jr nz, .next_play_area_slot
ret
; swap the data of the turn holder's arena Pokemon card with the
; data of the turn holder's Pokemon card in play area e.
; reset the status and all substatuses of the arena Pokemon before swapping.
; e is the play area location offset of the bench Pokemon (PLAY_AREA_*).
SwapArenaWithBenchPokemon: ; 1543 (0:1543)
call ClearAllStatusConditions
ld d, PLAY_AREA_ARENA
; fallthrough
; swap the data of the turn holder's Pokemon card in play area d with the
; data of the turn holder's Pokemon card in play area e.
; d and e are play area location offsets (PLAY_AREA_*).
SwapPlayAreaPokemon: ; 1548 (0:1548)
push bc
push de
push hl
ld a, e
cp d
jr z, .done
ldh a, [hWhoseTurn]
ld h, a
ld b, a
ld a, DUELVARS_ARENA_CARD
call .swap_duelvar
ld a, DUELVARS_ARENA_CARD_HP
call .swap_duelvar
ld a, DUELVARS_ARENA_CARD_FLAGS
call .swap_duelvar
ld a, DUELVARS_ARENA_CARD_STAGE
call .swap_duelvar
ld a, DUELVARS_ARENA_CARD_CHANGED_TYPE
call .swap_duelvar
ld a, DUELVARS_ARENA_CARD_ATTACHED_PLUSPOWER
call .swap_duelvar
ld a, DUELVARS_ARENA_CARD_ATTACHED_DEFENDER
call .swap_duelvar
set CARD_LOCATION_PLAY_AREA_F, d
set CARD_LOCATION_PLAY_AREA_F, e
ld l, DUELVARS_CARD_LOCATIONS
.update_card_locations_loop
; update card locations of the two swapped cards
ld a, [hl]
cp e
jr nz, .next1
ld a, d
jr .update_location
.next1
cp d
jr nz, .next2
ld a, e
.update_location
ld [hl], a
.next2
inc l
ld a, l
cp DECK_SIZE
jr c, .update_card_locations_loop
.done
pop hl
pop de
pop bc
ret
.swap_duelvar
ld c, a
add e ; play area location offset of card 1
ld l, a
ld a, c
add d ; play area location offset of card 2
ld c, a
ld a, [bc]
push af
ld a, [hl]
ld [bc], a
pop af
ld [hl], a
ret
; Find which and how many energy cards are attached to the turn holder's Pokemon card in the arena,
; or a Pokemon card in the bench, depending on the value of register e.
; input: e = location to check, i.e. PLAY_AREA_*
; Feedback is returned in wAttachedEnergies and wTotalAttachedEnergies.
GetPlayAreaCardAttachedEnergies: ; 159f (0:159f)
push hl
push de
push bc
xor a
ld c, NUM_TYPES
ld hl, wAttachedEnergies
.zero_energies_loop
ld [hli], a
dec c
jr nz, .zero_energies_loop
ld a, CARD_LOCATION_PLAY_AREA
or e ; if e is non-0, a bench location is checked instead
ld e, a
ldh a, [hWhoseTurn]
ld h, a
ld l, DUELVARS_CARD_LOCATIONS
ld c, DECK_SIZE
.next_card
ld a, [hl]
cp e
jr nz, .not_in_requested_location
push hl
push de
push bc
ld a, l
call LoadCardDataToBuffer2_FromDeckIndex
ld a, [wLoadedCard2Type]
bit TYPE_ENERGY_F, a
jr z, .not_an_energy_card
and TYPE_PKMN ; zero bit 3 to extract the type
ld e, a
ld d, $0
ld hl, wAttachedEnergies
add hl, de
inc [hl] ; increment the number of energy cards of this type
cp COLORLESS
jr nz, .not_colorless
inc [hl] ; each colorless energy counts as two
.not_an_energy_card
.not_colorless
pop bc
pop de
pop hl
.not_in_requested_location
inc l
dec c
jr nz, .next_card
; all 60 cards checked
ld hl, wAttachedEnergies
ld c, NUM_TYPES
xor a
.sum_attached_energies_loop
add [hl]
inc hl
dec c
jr nz, .sum_attached_energies_loop
ld [hl], a ; save to wTotalAttachedEnergies
pop bc
pop de
pop hl
ret
; returns in a how many times card e can be found in location b
; e = card id to search
; b = location to consider (CARD_LOCATION_*)
; h = PLAYER_TURN or OPPONENT_TURN
CountCardIDInLocation: ; 15ef (0:15ef)
push bc
ld l, DUELVARS_CARD_LOCATIONS
ld c, $0
.next_card
ld a, [hl]
cp b
jr nz, .unmatching_card_location_or_ID
ld a, l
push hl
call _GetCardIDFromDeckIndex
cp e
pop hl
jr nz, .unmatching_card_location_or_ID
inc c
.unmatching_card_location_or_ID
inc l
ld a, l
cp DECK_SIZE
jr c, .next_card
ld a, c
pop bc
ret
; returns [[hWhoseTurn] << 8 + a] in a and in [hl]
; i.e. duelvar a of the player whose turn it is
GetTurnDuelistVariable: ; 160b (0:160b)
ld l, a
ldh a, [hWhoseTurn]
ld h, a
ld a, [hl]
ret
; returns [([hWhoseTurn] ^ $1) << 8 + a] in a and in [hl]
; i.e. duelvar a of the player whose turn it is not
GetNonTurnDuelistVariable: ; 1611 (0:1611)
ld l, a
ldh a, [hWhoseTurn]
ld h, OPPONENT_TURN
cp PLAYER_TURN
jr z, .ok
ld h, PLAYER_TURN
.ok
ld a, [hl]
ret
; when playing a Pokemon card, initializes some variables according to the
; card played, and checks if the played card has Pokemon Power to show it to
; the player, and possibly to use it if it triggers when the card is played.
Func_161e: ; 161e (0:161e)
ldh a, [hTempCardIndex_ff98]
call ClearChangedTypesIfMuk
ldh a, [hTempCardIndex_ff98]
ld d, a
ld e, $00
call CopyMoveDataAndDamage_FromDeckIndex
call Func_16f6
ldh a, [hTempCardIndex_ff98]
ldh [hTempCardIndex_ff9f], a
call GetCardIDFromDeckIndex
ld a, e
ld [wTempTurnDuelistCardID], a
ld a, [wLoadedMoveCategory]
cp POKEMON_POWER
ret nz
call DisplayUsePokemonPowerScreen
ldh a, [hTempCardIndex_ff98]
call LoadCardDataToBuffer1_FromDeckIndex
ld hl, wLoadedCard1Name
ld a, [hli]
ld h, [hl]
ld l, a
call LoadTxRam2
ldtx hl, HavePokemonPowerText
call DrawWideTextBox_WaitForInput
call ExchangeRNG
ld a, [wLoadedCard1ID]
cp MUK
jr z, .use_pokemon_power
ld a, $01 ; check only Muk
call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0
jr nc, .use_pokemon_power
call DisplayUsePokemonPowerScreen
ldtx hl, UnableToUsePkmnPowerDueToToxicGasText
call DrawWideTextBox_WaitForInput
call ExchangeRNG
ret
.use_pokemon_power
ld hl, wLoadedMoveEffectCommands
ld a, [hli]
ld h, [hl]
ld l, a
ld a, $07
call CheckMatchingCommand
ret c ; return if command not found
bank1call DrawDuelMainScene
ldh a, [hTempCardIndex_ff9f]
call LoadCardDataToBuffer1_FromDeckIndex
ld de, wLoadedCard1Name
ld hl, wTxRam2
ld a, [de]
inc de
ld [hli], a
ld a, [de]
ld [hli], a
ld de, wLoadedMoveName
ld a, [de]
inc de
ld [hli], a
ld a, [de]
ld [hl], a
ldtx hl, WillUseThePokemonPowerText
call DrawWideTextBox_WaitForInput
call ExchangeRNG
call Func_7415
ld a, EFFECTCMDTYPE_PKMN_POWER_TRIGGER
call TryExecuteEffectCommandFunction
ret
; copies, given a card identified by register a (card ID):
; - e into wSelectedAttack and d into hTempCardIndex_ff9f
; - Move1 (if e == 0) or Move2 (if e == 1) data into wLoadedMove
; - Also from that move, its Damage field into wDamage
; finally, clears wNoDamageOrEffect and wDealtDamage
CopyMoveDataAndDamage_FromCardID: ; 16ad (0:16ad)
push de
push af
ld a, e
ld [wSelectedAttack], a
ld a, d
ldh [hTempCardIndex_ff9f], a
pop af
ld e, a
ld d, $00
call LoadCardDataToBuffer1_FromCardID
pop de
jr CopyMoveDataAndDamage
; copies, given a card identified by register d (0-59 deck index):
; - e into wSelectedAttack and d into hTempCardIndex_ff9f
; - Move1 (if e == 0) or Move2 (if e == 1) data into wLoadedMove
; - Also from that move, its Damage field into wDamage
; finally, clears wNoDamageOrEffect and wDealtDamage
CopyMoveDataAndDamage_FromDeckIndex: ; 16c0 (0:16c0)
ld a, e
ld [wSelectedAttack], a
ld a, d
ldh [hTempCardIndex_ff9f], a
call LoadCardDataToBuffer1_FromDeckIndex
; fallthrough
CopyMoveDataAndDamage:
ld a, [wLoadedCard1ID]
ld [wTempCardID_ccc2], a
ld hl, wLoadedCard1Move1
dec e
jr nz, .got_move
ld hl, wLoadedCard1Move2
.got_move
ld de, wLoadedMove
ld c, CARD_DATA_MOVE2 - CARD_DATA_MOVE1
.copy_loop
ld a, [hli]
ld [de], a
inc de
dec c
jr nz, .copy_loop
ld a, [wLoadedMoveDamage]
ld hl, wDamage
ld [hli], a
xor a
ld [hl], a
ld [wNoDamageOrEffect], a
ld hl, wDealtDamage
ld [hli], a
ld [hl], a
ret
; inits hTempCardIndex_ff9f and wTempTurnDuelistCardID to the turn holder's arena card,
; wTempNonTurnDuelistCardID to the non-turn holder's arena card, and zeroes other temp
; variables that only last between each two-player turn.
; this is called when a Pokemon card is played or when an attack is used
Func_16f6: ; 16f6 (0:16f6)
ld a, DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
ldh [hTempCardIndex_ff9f], a
call GetCardIDFromDeckIndex
ld a, e
ld [wTempTurnDuelistCardID], a
call SwapTurn
ld a, DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
call GetCardIDFromDeckIndex
ld a, e
ld [wTempNonTurnDuelistCardID], a
call SwapTurn
xor a
ld [wccec], a
ld [wEffectFunctionsFeedbackIndex], a
ld [wEffectFailed], a
ld [wIsDamageToSelf], a
ld [wccef], a
ld [wMetronomeEnergyCost], a
ld [wNoEffectFromStatus], a
bank1call ClearNonTurnTemporaryDuelvars_CopyStatus
ret
; Use an attack (from DuelMenu_Attack) or a Pokemon Power (from DuelMenu_PkmnPower)
UseAttackOrPokemonPower: ; 1730 (0:1730)
ld a, [wSelectedAttack]
ld [wPlayerAttackingMoveIndex], a
ldh a, [hTempCardIndex_ff9f]
ld [wPlayerAttackingCardIndex], a
ld a, [wTempCardID_ccc2]
ld [wPlayerAttackingCardID], a
ld a, [wLoadedMoveCategory]
cp POKEMON_POWER
jp z, UsePokemonPower
call Func_16f6
ld a, EFFECTCMDTYPE_INITIAL_EFFECT_1
call TryExecuteEffectCommandFunction
jp c, DrawWideTextBox_WaitForInput_ReturnCarry
call CheckSandAttackOrSmokescreenSubstatus
jr c, .sand_attack_smokescreen
ld a, EFFECTCMDTYPE_INITIAL_EFFECT_2
call TryExecuteEffectCommandFunction
jp c, ReturnCarry
call SendAttackDataToLinkOpponent
jr .next
.sand_attack_smokescreen
call SendAttackDataToLinkOpponent
call HandleSandAttackOrSmokescreenSubstatus
jp c, ClearNonTurnTemporaryDuelvars_ResetCarry
ld a, EFFECTCMDTYPE_INITIAL_EFFECT_2
call TryExecuteEffectCommandFunction
jp c, ReturnCarry
.next
ld a, OPPACTION_USE_ATTACK
call SetOppAction_SerialSendDuelData
ld a, EFFECTCMDTYPE_DISCARD_ENERGY
call TryExecuteEffectCommandFunction
call CheckSelfConfusionDamage
jp c, HandleConfusionDamageToSelf
call DrawDuelMainScene_PrintPokemonsAttackText
call WaitForWideTextBoxInput
call ExchangeRNG
ld a, EFFECTCMDTYPE_REQUIRE_SELECTION
call TryExecuteEffectCommandFunction
ld a, OPPACTION_ATTACK_ANIM_AND_DAMAGE
call SetOppAction_SerialSendDuelData
; fallthrough
PlayAttackAnimation_DealAttackDamage: ; 179a (0:179a)
call Func_7415
ld a, [wLoadedMoveCategory]
and RESIDUAL
jr nz, .deal_damage
call SwapTurn
call HandleNoDamageOrEffectSubstatus
call SwapTurn
.deal_damage
xor a
ldh [hTempPlayAreaLocation_ff9d], a
ld a, EFFECTCMDTYPE_BEFORE_DAMAGE
call TryExecuteEffectCommandFunction
call ApplyDamageModifiers_DamageToTarget
call Func_189d
ld hl, wDealtDamage
ld [hl], e
inc hl
ld [hl], d
ld b, $0
ld a, [wDamageEffectiveness]
ld c, a
ld a, DUELVARS_ARENA_CARD_HP
call GetNonTurnDuelistVariable
push de
push hl
call PlayMoveAnimation
call Func_741a
call WaitMoveAnimation
pop hl
pop de
call SubstractHP
ld a, [wDuelDisplayedScreen]
cp DUEL_MAIN_SCENE
jr nz, .skip_draw_huds
push hl
bank1call DrawDuelHUDs
pop hl
.skip_draw_huds
call PrintKnockedOutIfHLZero
jr Func_17fb
Func_17ed: ; 17ed (0:17ed)
call DrawWideTextBox_WaitForInput
xor a
ld hl, wDamage
ld [hli], a
ld [hl], a
ld a, NO_DAMAGE_OR_EFFECT_AGILITY
ld [wNoDamageOrEffect], a
; fallthrough
Func_17fb: ; 17fb (0:17fb)
ld a, [wTempNonTurnDuelistCardID]
push af
ld a, EFFECTCMDTYPE_AFTER_DAMAGE
call TryExecuteEffectCommandFunction
pop af
ld [wTempNonTurnDuelistCardID], a
call HandleStrikesBack_AgainstResidualMove
bank1call Func_6df1
call Func_1bb4
bank1call Func_7195
call Func_6e49
or a
ret
DisplayUsePokemonPowerScreen_WaitForInput: ; 1819 (0:1819)
push hl
call DisplayUsePokemonPowerScreen
pop hl
; fallthrough
DrawWideTextBox_WaitForInput_ReturnCarry: ; 181e (0:181e)
call DrawWideTextBox_WaitForInput
; fallthrough
ReturnCarry: ; 1821 (0:1821)
scf
ret
ClearNonTurnTemporaryDuelvars_ResetCarry: ; 1823 (0:1823)
bank1call ClearNonTurnTemporaryDuelvars
or a
ret
; called when attacker deals damage to itself due to confusion
; display the corresponding animation and deal damage to self
HandleConfusionDamageToSelf: ; 1828 (0:1828)
bank1call DrawDuelMainScene
ld a, 1
ld [wIsDamageToSelf], a
ldtx hl, DamageToSelfDueToConfusionText
call DrawWideTextBox_PrintText
ld a, $75
ld [wLoadedMoveAnimation], a
ld a, 20 ; damage
call DealConfusionDamageToSelf
call Func_1bb4
call Func_6e49
bank1call ClearNonTurnTemporaryDuelvars
or a
ret
; use Pokemon Power
UsePokemonPower: ; 184b (0:184b)
call Func_7415
ld a, EFFECTCMDTYPE_INITIAL_EFFECT_2
call TryExecuteEffectCommandFunction
jr c, DisplayUsePokemonPowerScreen_WaitForInput
ld a, EFFECTCMDTYPE_REQUIRE_SELECTION
call TryExecuteEffectCommandFunction
jr c, ReturnCarry
ld a, OPPACTION_USE_PKMN_POWER
call SetOppAction_SerialSendDuelData
call ExchangeRNG
ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT
call SetOppAction_SerialSendDuelData
ld a, EFFECTCMDTYPE_BEFORE_DAMAGE
call TryExecuteEffectCommandFunction
ld a, OPPACTION_DUEL_MAIN_SCENE
call SetOppAction_SerialSendDuelData
ret
; called by UseAttackOrPokemonPower (on an attack only)
; in a link duel, it's used to send the other game data about the
; attack being in use, triggering a call to OppAction_BeginUseAttack in the receiver
SendAttackDataToLinkOpponent: ; 1874 (0:1874)
ld a, [wccec]
or a
ret nz
ldh a, [hTemp_ffa0]
push af
ldh a, [hTempCardIndex_ff9f]
push af
ld a, $1
ld [wccec], a
ld a, [wPlayerAttackingCardIndex]
ldh [hTempCardIndex_ff9f], a
ld a, [wPlayerAttackingMoveIndex]
ldh [hTemp_ffa0], a
ld a, OPPACTION_BEGIN_ATTACK
call SetOppAction_SerialSendDuelData
call ExchangeRNG
pop af
ldh [hTempCardIndex_ff9f], a
pop af
ldh [hTemp_ffa0], a
ret
Func_189d: ; 189d (0:189d)
ld a, [wLoadedMoveCategory]
bit RESIDUAL_F, a
ret nz
ld a, [wNoDamageOrEffect]
or a
ret nz
ld a, e
or d
jr nz, .asm_18b9
ld a, DUELVARS_ARENA_CARD_SUBSTATUS2
call GetNonTurnDuelistVariable
or a
jr nz, .asm_18b9
ld a, [wEffectFunctionsFeedbackIndex]
or a
ret z
.asm_18b9
push de
call SwapTurn
xor a
ld [wTempPlayAreaLocation_cceb], a
call HandleTransparency
call SwapTurn
pop de
ret nc
bank1call DrawDuelMainScene
ld a, DUELVARS_ARENA_CARD_SUBSTATUS2
call GetNonTurnDuelistVariable
ld [hl], $0
ld de, 0
ret
; return carry and 1 into wGotHeadsFromConfusionCheck if damage will be dealt to oneself due to confusion
CheckSelfConfusionDamage: ; 18d7 (0:18d7)
xor a
ld [wGotHeadsFromConfusionCheck], a
ld a, DUELVARS_ARENA_CARD_STATUS
call GetTurnDuelistVariable
and CNF_SLP_PRZ
cp CONFUSED
jr z, .confused
or a
ret
.confused
ldtx de, ConfusionCheckDamageText
call TossCoin
jr c, .no_confusion_damage
ld a, 1
ld [wGotHeadsFromConfusionCheck], a
scf
ret
.no_confusion_damage
or a
ret
; play the trainer card with deck index at hTempCardIndex_ff98.
; a trainer card is like a move effect, with its own effect commands.
; return nc if the card was played, carry if it wasn't.
PlayTrainerCard: ; 18f9 (0:18f9)
call CheckCantUseTrainerDueToHeadache
jr c, .cant_use
ldh a, [hWhoseTurn]
ld h, a
ldh a, [hTempCardIndex_ff98]
ldh [hTempCardIndex_ff9f], a
call LoadNonPokemonCardEffectCommands
ld a, EFFECTCMDTYPE_INITIAL_EFFECT_1
call TryExecuteEffectCommandFunction
jr nc, .can_use
.cant_use
call DrawWideTextBox_WaitForInput
scf
ret
.can_use
ld a, EFFECTCMDTYPE_INITIAL_EFFECT_2
call TryExecuteEffectCommandFunction
jr c, .done
ld a, OPPACTION_PLAY_TRAINER
call SetOppAction_SerialSendDuelData
call DisplayUsedTrainerCardDetailScreen
call ExchangeRNG
ld a, EFFECTCMDTYPE_DISCARD_ENERGY
call TryExecuteEffectCommandFunction
ld a, EFFECTCMDTYPE_REQUIRE_SELECTION
call TryExecuteEffectCommandFunction
ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
call SetOppAction_SerialSendDuelData
ld a, EFFECTCMDTYPE_BEFORE_DAMAGE
call TryExecuteEffectCommandFunction
ldh a, [hTempCardIndex_ff9f]
call MoveHandCardToDiscardPile
call ExchangeRNG
.done
or a
ret
; loads the effect commands of a (trainer or energy) card with deck index (0-59) at hTempCardIndex_ff9f
; into wLoadedMoveEffectCommands. in practice, only used for trainer cards
LoadNonPokemonCardEffectCommands: ; 1944 (0:1944)
ldh a, [hTempCardIndex_ff9f]
call LoadCardDataToBuffer1_FromDeckIndex
ld hl, wLoadedCard1EffectCommands
ld de, wLoadedMoveEffectCommands
ld a, [hli]
ld [de], a
inc de
ld a, [hl]
ld [de], a
ret
; inflict recoil damage to self
; input:
; a = damage to deal
DealRecoilDamageToSelf: ; 1955 (0:1955)
push af
ld a, $7a
ld [wLoadedMoveAnimation], a
pop af
; fallthrough
DealConfusionDamageToSelf: ; 195c (0:195c)
ld hl, wDamage
ld [hli], a
ld [hl], 0
ld a, [wNoDamageOrEffect]
push af
xor a
ld [wNoDamageOrEffect], a
bank1call Func_7415
ld a, [wTempNonTurnDuelistCardID]
push af
ld a, [wTempTurnDuelistCardID]
ld [wTempNonTurnDuelistCardID], a
bank1call ApplyDamageModifiers_DamageToSelf ; this is at bank 0
ld a, [wDamageEffectiveness]
ld c, a
ld b, $0
ld a, DUELVARS_ARENA_CARD_HP
call GetTurnDuelistVariable
bank1call PlayAttackAnimation_DealAttackDamageSimple
call PrintKnockedOutIfHLZero
pop af
ld [wTempNonTurnDuelistCardID], a
pop af
ld [wNoDamageOrEffect], a
ret
; given a damage value at wDamage:
; - if the non-turn holder's arena card is weak to the turn holder's arena card color: double damage
; - if the non-turn holder's arena card resists the turn holder's arena card color: reduce damage by 30
; - also apply Pluspower, Defender, and other kinds of damage reduction accordingly
; return resulting damage in de
ApplyDamageModifiers_DamageToTarget: ; 1994 (0:1994)
xor a
ld [wDamageEffectiveness], a
ld hl, wDamage
ld a, [hli]
or [hl]
jr nz, .non_zero_damage
ld de, 0
ret
.non_zero_damage
xor a ; PLAY_AREA_ARENA
ldh [hTempPlayAreaLocation_ff9d], a
ld d, [hl]
dec hl
ld e, [hl]
bit 7, d
jr z, .safe
res 7, d ; cap at 2^15
xor a
ld [wDamageEffectiveness], a
call HandleDoubleDamageSubstatus
jr .check_pluspower_and_defender
.safe
call HandleDoubleDamageSubstatus
ld a, e
or d
ret z
ldh a, [hTempPlayAreaLocation_ff9d]
call GetPlayAreaCardColor
call TranslateColorToWR
ld b, a
call SwapTurn
call GetArenaCardWeakness
call SwapTurn
and b
jr z, .not_weak
sla e
rl d
ld hl, wDamageEffectiveness
set WEAKNESS, [hl]
.not_weak
call SwapTurn
call GetArenaCardResistance
call SwapTurn
and b
jr z, .check_pluspower_and_defender ; jump if not resistant
ld hl, -30
add hl, de
ld e, l
ld d, h
ld hl, wDamageEffectiveness
set RESISTANCE, [hl]
.check_pluspower_and_defender
ld b, CARD_LOCATION_ARENA
call ApplyAttachedPluspower
call SwapTurn
ld b, CARD_LOCATION_ARENA
call ApplyAttachedDefender
call HandleDamageReduction
bit 7, d
jr z, .no_underflow
ld de, 0
.no_underflow
call SwapTurn
ret
; convert a color to its equivalent WR_* (weakness/resistance) value
TranslateColorToWR: ; 1a0e (0:1a0e)
push hl
add LOW(InvertedPowersOf2)
ld l, a
ld a, HIGH(InvertedPowersOf2)
adc $0
ld h, a
ld a, [hl]
pop hl
ret
InvertedPowersOf2: ; 1a1a (0:1a1a)
db $80, $40, $20, $10, $08, $04, $02, $01
; given a damage value at wDamage:
; - if the turn holder's arena card is weak to its own color: double damage
; - if the turn holder's arena card resists its own color: reduce damage by 30
; return resulting damage in de
ApplyDamageModifiers_DamageToSelf: ; 1a22 (0:1a22)
xor a
ld [wDamageEffectiveness], a
ld hl, wDamage
ld a, [hli]
or [hl]
or a
jr z, .no_damage
ld d, [hl]
dec hl
ld e, [hl]
call GetArenaCardColor
call TranslateColorToWR
ld b, a
call GetArenaCardWeakness
and b
jr z, .not_weak
sla e
rl d
ld hl, wDamageEffectiveness
set WEAKNESS, [hl]
.not_weak
call GetArenaCardResistance
and b
jr z, .not_resistant
ld hl, -30
add hl, de
ld e, l
ld d, h
ld hl, wDamageEffectiveness
set RESISTANCE, [hl]
.not_resistant
ld b, CARD_LOCATION_ARENA
call ApplyAttachedPluspower
ld b, CARD_LOCATION_ARENA
call ApplyAttachedDefender
bit 7, d ; test for underflow
ret z
.no_damage
ld de, 0
ret
; increases de by 10 points for each Pluspower found in location b
ApplyAttachedPluspower: ; 1a69 (0:1a69)
push de
call GetTurnDuelistVariable
ld de, PLUSPOWER
call CountCardIDInLocation
ld l, a
ld h, 10
call HtimesL
pop de
add hl, de
ld e, l
ld d, h
ret
; reduces de by 20 points for each Defender found in location b
ApplyAttachedDefender: ; 1a7e (0:1a7e)
push de
call GetTurnDuelistVariable
ld de, DEFENDER
call CountCardIDInLocation
ld l, a
ld h, 20
call HtimesL
pop de
ld a, e
sub l
ld e, a
ld a, d
sbc h
ld d, a
ret
; hl: address to substract HP from
; de: how much HP to substract (damage to deal)
; returns carry if the HP does not become 0 as a result
SubstractHP: ; 1a96 (0:1a96)
push hl
push de
ld a, [hl]
sub e
ld [hl], a
ld a, $0
sbc d
and $80
jr z, .no_underflow
ld [hl], 0
.no_underflow
ld a, [hl]
or a
jr z, .zero
scf
.zero
pop de
pop hl
ret
; given a play area location offset in a, check if the turn holder's Pokemon card in
; that location has no HP left, and, if so, print that it was knocked out.
PrintPlayAreaCardKnockedOutIfNoHP: ; 1aac (0:1aac)
ld e, a
add DUELVARS_ARENA_CARD_HP
call GetTurnDuelistVariable
or a
ret nz ; return if arena card has non-0 HP
ld a, [wTempNonTurnDuelistCardID]
push af
ld a, e
add DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
call LoadCardDataToBuffer1_FromDeckIndex
ld a, [wLoadedCard1ID]
ld [wTempNonTurnDuelistCardID], a
call PrintKnockedOut
pop af
ld [wTempNonTurnDuelistCardID], a
scf
ret
PrintKnockedOutIfHLZero: ; 1ad0 (0:1ad0)
ld a, [hl] ; this is supposed to point to a remaining HP value after some form of damage calculation
or a
ret nz
; fallthrough
; print in a text box that the Pokemon card at wTempNonTurnDuelistCardID
; was knocked out and wait 40 frames
PrintKnockedOut: ; 1ad3 (0:1ad3)
ld a, [wTempNonTurnDuelistCardID]
ld e, a
call LoadCardDataToBuffer1_FromCardID
ld hl, wLoadedCard1Name
ld a, [hli]
ld h, [hl]
ld l, a
call LoadTxRam2
ldtx hl, WasKnockedOutText
call DrawWideTextBox_PrintText
ld a, 40
.wait_frames
call DoFrame
dec a
jr nz, .wait_frames
scf
ret
; deal damage to turn holder's Pokemon card at play area location at b (PLAY_AREA_*).
; damage to deal is given in de.
; shows the defending player's play area screen when dealing the damage
; instead of the main duel interface with regular attack animation.
DealDamageToPlayAreaPokemon_RegularAnim: ; 1af3 (0:1af3)
ld a, $78
ld [wLoadedMoveAnimation], a
; fallthrough
; deal damage to turn holder's Pokemon card at play area location at b (PLAY_AREA_*).
; damage to deal is given in de.
; shows the defending player's play area screen when dealing the damage
; instead of the main duel interface.
; plays animation that is loaded in wLoadedMoveAnimation.
DealDamageToPlayAreaPokemon: ; 1af8 (0:1af8)
ld a, b
ld [wTempPlayAreaLocation_cceb], a
or a ; cp PLAY_AREA_ARENA
jr nz, .skip_no_damage_or_effect_check
ld a, [wNoDamageOrEffect]
or a
ret nz
.skip_no_damage_or_effect_check
push hl
push de
push bc
xor a
ld [wNoDamageOrEffect], a
push de
ld a, [wTempPlayAreaLocation_cceb]
add DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
call GetCardIDFromDeckIndex
ld a, e
ld [wTempNonTurnDuelistCardID], a
pop de
ld a, [wTempPlayAreaLocation_cceb]
or a ; cp PLAY_AREA_ARENA
jr nz, .next
ld a, [wIsDamageToSelf]
or a
jr z, .turn_swapped
ld b, CARD_LOCATION_ARENA
call ApplyAttachedPluspower
jr .next
.turn_swapped
call SwapTurn
ld b, CARD_LOCATION_ARENA
call ApplyAttachedPluspower
call SwapTurn
.next
ld a, [wLoadedMoveCategory]
cp POKEMON_POWER
jr z, .skip_defender
ld a, [wTempPlayAreaLocation_cceb]
or CARD_LOCATION_PLAY_AREA
ld b, a
call ApplyAttachedDefender
.skip_defender
ld a, [wTempPlayAreaLocation_cceb]
or a ; cp PLAY_AREA_ARENA
jr nz, .in_bench
push de
call HandleNoDamageOrEffectSubstatus
pop de
call HandleDamageReduction
.in_bench
bit 7, d
jr z, .no_underflow
ld de, 0
.no_underflow
call HandleDamageReductionOrNoDamageFromPkmnPowerEffects
ld a, [wTempPlayAreaLocation_cceb]
ld b, a
or a ; cp PLAY_AREA_ARENA
jr nz, .benched
; if arena Pokemon, add damage at de to [wDealtDamage]
ld hl, wDealtDamage
ld a, e
add [hl]
ld [hli], a
ld a, d
adc [hl]
ld [hl], a
.benched
ld c, $00
add DUELVARS_ARENA_CARD_HP
call GetTurnDuelistVariable
push af
bank1call PlayAttackAnimation_DealAttackDamageSimple
pop af
or a
jr z, .skip_knocked_out
push de
call PrintKnockedOutIfHLZero
pop de
.skip_knocked_out
call HandleStrikesBack_AgainstDamagingMove
pop bc
pop de
pop hl
ret
; draw duel main scene, then print the "<Pokemon Lvxx>'s <attack>" text
; The Pokemon's name is the turn holder's arena Pokemon, and the
; attack's name is taken from wLoadedMoveName.
DrawDuelMainScene_PrintPokemonsAttackText: ; 1b8d (0:1b8d)
bank1call DrawDuelMainScene
; fallthrough
; print the "<Pokemon Lvxx>'s <attack>" text
; The Pokemon's name is the turn holder's arena Pokemon, and the
; attack's name is taken from wLoadedMoveName.
PrintPokemonsAttackText: ; 1b90 (0:1b90)
ld a, DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
call LoadCardDataToBuffer1_FromDeckIndex
ld a, 18
call CopyCardNameAndLevel
ld [hl], TX_END
; zero wTxRam2 so that the name & level text just loaded to wDefaultText is printed
ld hl, wTxRam2
xor a
ld [hli], a
ld [hli], a
ld a, [wLoadedMoveName]
ld [hli], a ; wTxRam2_b
ld a, [wLoadedMoveName + 1]
ld [hli], a
ldtx hl, PokemonsAttackText
call DrawWideTextBox_PrintText
ret
Func_1bb4: ; 1bb4 (0:1bb4)
call Func_3b31
bank1call DrawDuelMainScene
call DrawDuelHUDs
xor a
ldh [hTempPlayAreaLocation_ff9d], a
call Func_1bca
call WaitForWideTextBoxInput
call ExchangeRNG
ret
; prints one of the ThereWasNoEffectFrom*Text if wEffectFailed contains EFFECT_FAILED_NO_EFFECT,
; and prints WasUnsuccessfulText if wEffectFailed contains EFFECT_FAILED_UNSUCCESSFUL
Func_1bca: ; 1bca (0:1bca)
ld a, [wEffectFailed]
or a
ret z
cp $1
jr z, .no_effect_from_status
ldh a, [hTempPlayAreaLocation_ff9d]
add DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
call LoadCardDataToBuffer1_FromDeckIndex
ld a, 18
call CopyCardNameAndLevel
; zero wTxRam2 so that the name & level text just loaded to wDefaultText is printed
ld [hl], $0
ld hl, $0000
call LoadTxRam2
ld hl, wLoadedMoveName
ld de, wTxRam2_b
ld a, [hli]
ld [de], a
inc de
ld a, [hli]
ld [de], a
ldtx hl, WasUnsuccessfulText
call DrawWideTextBox_PrintText
scf
ret
.no_effect_from_status
call PrintThereWasNoEffectFromStatusText
call DrawWideTextBox_PrintText
scf
ret
; return in a the retreat cost of the turn holder's arena or bench Pokemon
; given the PLAY_AREA_* value in hTempPlayAreaLocation_ff9d
GetPlayAreaCardRetreatCost: ; 1c05 (0:1c05)
ldh a, [hTempPlayAreaLocation_ff9d]
add DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
call LoadCardDataToBuffer1_FromDeckIndex
call GetLoadedCard1RetreatCost
ret
; move the turn holder's card with ID at de to the discard pile
; if it's currently in the arena.
MoveCardToDiscardPileIfInArena: ; 1c13 (0:1c13)
ld c, e
ld b, d
ld l, DUELVARS_CARD_LOCATIONS
.next_card
ld a, [hl]
and CARD_LOCATION_ARENA
jr z, .skip ; jump if card not in arena
ld a, l
call GetCardIDFromDeckIndex
ld a, c
cp e
jr nz, .skip ; jump if not the card id provided in c
ld a, b
cp d ; card IDs are 8-bit so d is always 0
jr nz, .skip
ld a, l
push bc
call PutCardInDiscardPile
pop bc
.skip
inc l
ld a, l
cp DECK_SIZE
jr c, .next_card
ret
; calculate damage and max HP of card at PLAY_AREA_* in e.
; input:
; e = PLAY_AREA_* of card;
; output:
; a = damage;
; c = max HP.
GetCardDamageAndMaxHP: ; 1c35 (0:1c35)
push hl
push de
ld a, DUELVARS_ARENA_CARD
add e
call GetTurnDuelistVariable
call LoadCardDataToBuffer2_FromDeckIndex
pop de
push de
ld a, DUELVARS_ARENA_CARD_HP
add e
call GetTurnDuelistVariable
ld a, [wLoadedCard2HP]
ld c, a
sub [hl]
pop de
pop hl
ret
; check if a flag of wLoadedMove is set
; input:
; a = %fffffbbb, where
; fffff = flag address counting from wLoadedMoveFlag1
; bbb = flag bit
; return carry if the flag is set
CheckLoadedMoveFlag: ; 1c50 (0:1c50)
push hl
push de
push bc
ld c, a ; %fffffbbb
and $07
ld e, a
ld d, $00
ld hl, PowersOf2
add hl, de
ld b, [hl]
ld a, c
rra
rra
rra
and $1f
ld e, a ; %000fffff
ld hl, wLoadedMoveFlag1
add hl, de
ld a, [hl]
and b
jr z, .done
scf ; set carry if the move has this flag set
.done
pop bc
pop de
pop hl
ret
; returns [hWhoseTurn] <-- ([hWhoseTurn] ^ $1)
; As a side effect, this also returns a duelist variable in a similar manner to
; GetNonTurnDuelistVariable, but this function appears to be
; only called to swap the turn value.
SwapTurn: ; 1c72 (0:1c72)
push af
push hl
call GetNonTurnDuelistVariable
ld a, h
ldh [hWhoseTurn], a
pop hl
pop af
ret
; copy the TX_END-terminated player's name from sPlayerName to de
CopyPlayerName: ; 1c7d (0:1c7d)
call EnableSRAM
ld hl, sPlayerName
.loop
ld a, [hli]
ld [de], a
inc de
or a ; TX_END
jr nz, .loop
dec de
call DisableSRAM
ret
; copy the opponent's name to de
; if text ID at wOpponentName is non-0, copy it from there
; else, if text at wc500 is non-0, copy if from there
; else, copy Player2Text
CopyOpponentName: ; 1c8e (0:1c8e)
ld hl, wOpponentName
ld a, [hli]
or [hl]
jr z, .special_name
ld a, [hld]
ld l, [hl]
ld h, a
jp CopyText
.special_name
ld hl, wNameBuffer
ld a, [hl]
or a
jr z, .print_player2
jr CopyPlayerName.loop
.print_player2
ldtx hl, Player2Text
jp CopyText
; return, in hl, the total amount of cards owned anywhere, including duplicates
GetRawAmountOfCardsOwned: ; 1caa (0:1caa)
push de
push bc
call EnableSRAM
ld hl, $0000
ld de, sDeck1Cards
ld c, NUM_DECKS
.next_deck
ld a, [de]
or a
jr z, .skip_deck ; jump if deck empty
ld a, c
ld bc, DECK_SIZE
add hl, bc
ld c, a
.skip_deck
ld a, sDeck2Cards - sDeck1Cards
add e
ld e, a
ld a, $0
adc d
ld d, a ; de = sDeck*Cards[x]
dec c
jr nz, .next_deck
; hl = DECK_SIZE * (no. of non-empty decks)
ld de, sCardCollection
.next_card
ld a, [de]
bit CARD_NOT_OWNED_F, a
jr nz, .skip_card
ld c, a ; card count in sCardCollection
ld b, $0
add hl, bc
.skip_card
inc e
jr nz, .next_card ; assumes sCardCollection is $100 bytes long (CARD_COLLECTION_SIZE)
call DisableSRAM
pop bc
pop de
ret
; return carry if the count in sCardCollection plus the count in each deck (sDeck*)
; of the card with id given in a is 0 (if card not owned).
; also return the count (total owned amount) in a.
GetCardCountInCollectionAndDecks: ; 1ce1 (0:1ce1)
push hl
push de
push bc
call EnableSRAM
ld c, a
ld b, $0
ld hl, sDeck1Cards
ld d, NUM_DECKS
.next_deck
ld a, [hl]
or a
jr z, .deck_done ; jump if deck empty
push hl
ld e, DECK_SIZE
.next_card
ld a, [hli]
cp c
jr nz, .no_match
inc b ; this deck card matches card c
.no_match
dec e
jr nz, .next_card
pop hl
.deck_done
push de
ld de, sDeck2Cards - sDeck1Cards
add hl, de
pop de
dec d
jr nz, .next_deck
; all decks done
ld h, HIGH(sCardCollection)
ld l, c
ld a, [hl]
bit CARD_NOT_OWNED_F, a
jr nz, .done
add b ; if card seen, add b to count
.done
and CARD_COUNT_MASK
call DisableSRAM
pop bc
pop de
pop hl
or a
ret nz
scf
ret
; return carry if the count in sCardCollection of the card with id given in a is 0.
; also return the count (amount owned outside of decks) in a.
GetCardCountInCollection: ; 1d1d (0:1d1d)
push hl
call EnableSRAM
ld h, HIGH(sCardCollection)
ld l, a
ld a, [hl]
call DisableSRAM
pop hl
and CARD_COUNT_MASK
ret nz
scf
ret
; creates a list at wTempCardCollection of every card the player owns and how many
CreateTempCardCollection: ; 1d2e (0:1d2e)
call EnableSRAM
ld hl, sCardCollection
ld de, wTempCardCollection
ld bc, CARD_COLLECTION_SIZE
call CopyDataHLtoDE
ld de, sDeck1Name
call AddDeckCardsToTempCardCollection
ld de, sDeck2Name
call AddDeckCardsToTempCardCollection
ld de, sDeck3Name
call AddDeckCardsToTempCardCollection
ld de, sDeck4Name
call AddDeckCardsToTempCardCollection
call DisableSRAM
ret
; adds the cards from a deck to wTempCardCollection given de = sDeck*Name
AddDeckCardsToTempCardCollection: ; 1d59 (0:1d59)
ld a, [de]
or a
ret z ; return if empty name (empty deck)
ld hl, sDeck1Cards - sDeck1Name
add hl, de
ld e, l
ld d, h
ld h, HIGH(wTempCardCollection)
ld c, DECK_SIZE
.next_card_loop
ld a, [de] ; count of current card being added
inc de ; move to next card for next iteration
ld l, a
inc [hl] ; increment count
dec c
jr nz, .next_card_loop
ret
; add card with id given in a to sCardCollection, provided that
; the player has less than MAX_AMOUNT_OF_CARD (99) of them
AddCardToCollection: ; 1d6e (0:1d6e)
push hl
push de
push bc
ld l, a
push hl
call CreateTempCardCollection
pop hl
call EnableSRAM
ld h, HIGH(wTempCardCollection)
ld a, [hl]
and CARD_COUNT_MASK
cp MAX_AMOUNT_OF_CARD
jr nc, .already_max
ld h, HIGH(sCardCollection)
ld a, [hl]
and CARD_COUNT_MASK
inc a
ld [hl], a
.already_max
call DisableSRAM
pop bc
pop de
pop hl
ret
; remove a card with id given in a from sCardCollection (decrement its count if non-0)
RemoveCardFromCollection: ; 1d91 (0:1d91)
push hl
call EnableSRAM
ld h, HIGH(sCardCollection)
ld l, a
ld a, [hl]
and CARD_COUNT_MASK
jr z, .zero
dec a
ld [hl], a
.zero
call DisableSRAM
pop hl
ret
; return the amount of different cards that the player has collected in d
; return NUM_CARDS in e, minus 1 if VENUSAUR1 or MEW2 has not been collected (minus 2 if neither)
GetCardAlbumProgress: ; 1da4 (0:1da4)
push hl
call EnableSRAM
ld e, NUM_CARDS
ld h, HIGH(sCardCollection)
ld l, VENUSAUR1
bit CARD_NOT_OWNED_F, [hl]
jr z, .next1
dec e ; if VENUSAUR1 not owned
.next1
ld l, MEW2
bit CARD_NOT_OWNED_F, [hl]
jr z, .next2
dec e ; if MEW2 not owned
.next2
ld d, LOW(sCardCollection)
ld l, d
.next_card
bit CARD_NOT_OWNED_F, [hl]
jr nz, .skip
inc d ; if this card owned
.skip
inc l
jr nz, .next_card ; assumes sCardCollection is $100 bytes long (CARD_COLLECTION_SIZE)
call DisableSRAM
pop hl
ret
; copy c bytes of data from de to hl
; if LCD on, copy during h-blank only
SafeCopyDataDEtoHL: ; 1dca (0:1dca)
ld a, [wLCDC] ;
bit LCDC_ENABLE_F, a ;
jr nz, .lcd_on ; assert that LCD is on
.lcd_off_loop
ld a, [de]
inc de
ld [hli], a
dec c
jr nz, .lcd_off_loop
ret
.lcd_on
jp HblankCopyDataDEtoHL
; returns v*BGMap0 + BG_MAP_WIDTH * e + d in hl.
; used to map coordinates at de to a BGMap0 address.
DECoordToBGMap0Address: ; 1ddb (0:1ddb)
ld l, e
ld h, $0
add hl, hl
add hl, hl
add hl, hl
add hl, hl
add hl, hl
ld a, l
add d
ld l, a
ld a, h
adc HIGH(v0BGMap0)
ld h, a
ret
; Apply SCX and SCY correction to xy coordinates at de
AdjustCoordinatesForBGScroll: ; 1deb (0:1deb)
push af
ldh a, [hSCX]
rra
rra
rra
and $1f
add d
ld d, a
ldh a, [hSCY]
rra
rra
rra
and $1f
add e
ld e, a
pop af
ret
; Draws a bxc text box at de printing a name in the left side of the top border.
; The name's text id must be at hl when this function is called.
; Mostly used to print text boxes for talked-to NPCs, but occasionally used in duels as well.
DrawLabeledTextBox: ; 1e00 (0:1e00)
ld a, [wConsole]
cp CONSOLE_SGB
jr nz, .draw_textbox
ld a, [wTextBoxFrameType]
or a
jr z, .draw_textbox
; Console is SGB and frame type is != 0.
; The text box will be colorized so a SGB command needs to be sent as well
push de
push bc
call .draw_textbox
pop bc
pop de
jp ColorizeTextBoxSGB
.draw_textbox
push de
push bc
push hl
; top left tile of the box
ld hl, wc000
ld a, TX_SYMBOL
ld [hli], a
ld a, SYM_BOX_TOP_L
ld [hli], a
; white tile before the text
ld a, FW_SPACE
ld [hli], a
; text label
ld e, l
ld d, h
pop hl
call CopyText
ld hl, wc000 + 3
call GetTextLengthInTiles
ld l, e
ld h, d
; white tile after the text
ld a, TX_HALF2FULL
ld [hli], a
ld a, FW_SPACE
ld [hli], a
pop de
push de
ld a, d
sub b
sub $4
jr z, .draw_top_border_right_tile
ld b, a
.draw_top_border_line_loop
ld a, TX_SYMBOL
ld [hli], a
ld a, SYM_BOX_TOP
ld [hli], a
dec b
jr nz, .draw_top_border_line_loop
.draw_top_border_right_tile
ld a, TX_SYMBOL
ld [hli], a
ld a, SYM_BOX_TOP_R
ld [hli], a
ld [hl], TX_END
pop bc
pop de
push de
push bc
call InitTextPrinting
ld hl, wc000
call ProcessText
pop bc
pop de
ld a, [wConsole]
cp CONSOLE_CGB
jr z, .cgb
; DMG or SGB
inc e
call DECoordToBGMap0Address
; top border done, draw the rest of the text box
jr ContinueDrawingTextBoxDMGorSGB
.cgb
call DECoordToBGMap0Address
push de
call CopyCurrentLineAttrCGB ; BG Map attributes for current line, which is the top border
pop de
inc e
; top border done, draw the rest of the text box
jp ContinueDrawingTextBoxCGB
; Draws a bxc text box at de to print menu data in the overworld.
; Also used to print a text box during a duel.
; When talking to NPCs, DrawLabeledTextBox is used instead.
DrawRegularTextBox: ; 1e7c (0:1e7c)
ld a, [wConsole]
cp CONSOLE_CGB
jr z, DrawRegularTextBoxCGB
cp CONSOLE_SGB
jp z, DrawRegularTextBoxSGB
; fallthrough
DrawRegularTextBoxDMG: ; 1e88 (0:1e88)
call DECoordToBGMap0Address
; top line (border) of the text box
ld a, SYM_BOX_TOP
lb de, SYM_BOX_TOP_L, SYM_BOX_TOP_R
call CopyLine
; fallthrough
; continue drawing a labeled or regular textbox on DMG or SGB:
; body and bottom line of either type of textbox
ContinueDrawingTextBoxDMGorSGB:
dec c
dec c
.draw_text_box_body_loop
ld a, SYM_SPACE
lb de, SYM_BOX_LEFT, SYM_BOX_RIGHT
call CopyLine
dec c
jr nz, .draw_text_box_body_loop
; bottom line (border) of the text box
ld a, SYM_BOX_BOTTOM
lb de, SYM_BOX_BTM_L, SYM_BOX_BTM_R
; fallthrough
; copies b bytes of data to sp-$1f and to hl, and returns hl += BG_MAP_WIDTH
; d = value of byte 0
; e = value of byte b
; a = value of bytes [1, b-1]
; b is supposed to be BG_MAP_WIDTH or smaller, else the stack would get corrupted
CopyLine: ; 1ea5 (0:1ea5)
add sp, -BG_MAP_WIDTH
push hl
push bc
ld hl, sp+$4
dec b
dec b
push hl
ld [hl], d
inc hl
.loop
ld [hli], a
dec b
jr nz, .loop
ld [hl], e
pop de
pop bc
pop hl
push hl
push bc
ld c, b
ld b, $0
call SafeCopyDataDEtoHL
pop bc
pop de
; advance pointer BG_MAP_WIDTH positions and restore stack pointer
ld hl, BG_MAP_WIDTH
add hl, de
add sp, BG_MAP_WIDTH
ret
; DrawRegularTextBox branches here on CGB console
DrawRegularTextBoxCGB:
call DECoordToBGMap0Address
; top line (border) of the text box
ld a, SYM_BOX_TOP
lb de, SYM_BOX_TOP_L, SYM_BOX_TOP_R
call CopyCurrentLineTilesAndAttrCGB
; fallthrough
; continue drawing a labeled or regular textbox on CGB:
; body and bottom line of either type of textbox
ContinueDrawingTextBoxCGB:
dec c
dec c
.draw_text_box_body_loop
ld a, SYM_SPACE
lb de, SYM_BOX_LEFT, SYM_BOX_RIGHT
push hl
call CopyLine
pop hl
call BankswitchVRAM1
ld a, [wTextBoxFrameType] ; on CGB, wTextBoxFrameType determines the palette and the other attributes
ld e, a
ld d, a
xor a
call CopyLine
call BankswitchVRAM0
dec c
jr nz, .draw_text_box_body_loop
; bottom line (border) of the text box
ld a, SYM_BOX_BOTTOM
lb de, SYM_BOX_BTM_L, SYM_BOX_BTM_R
call CopyCurrentLineTilesAndAttrCGB
ret
; d = id of top left tile
; e = id of top right tile
; a = id of rest of tiles
; Assumes b = SCREEN_WIDTH and that VRAM bank 0 is loaded
CopyCurrentLineTilesAndAttrCGB: ; 1efb (0:1efb)
push hl
call CopyLine
pop hl
; fallthrough
CopyCurrentLineAttrCGB:
call BankswitchVRAM1
ld a, [wTextBoxFrameType] ; on CGB, wTextBoxFrameType determines the palette and the other attributes
ld e, a
ld d, a
call CopyLine
call BankswitchVRAM0
ret
; DrawRegularTextBox branches here on SGB console
DrawRegularTextBoxSGB: ; 1f0f (0:1f0f)
push bc
push de
call DrawRegularTextBoxDMG
pop de
pop bc
ld a, [wTextBoxFrameType]
or a
ret z
; fallthrough
ColorizeTextBoxSGB:
push bc
push de
ld hl, wTempSGBPacket
ld de, AttrBlkPacket_TextBox
ld c, SGB_PACKET_SIZE
.copy_sgb_command_loop
ld a, [de]
inc de
ld [hli], a
dec c
jr nz, .copy_sgb_command_loop
pop de
pop bc
ld hl, wTempSGBPacket + 4
; set X1, Y1 to d, e
ld [hl], d
inc hl
ld [hl], e
inc hl
; set X2, Y2 to d+b-1, e+c-1
ld a, d
add b
dec a
ld [hli], a
ld a, e
add c
dec a
ld [hli], a
ld a, [wTextBoxFrameType]
and $80
jr z, .send_packet
; reset ATTR_BLK_CTRL_INSIDE if bit 7 of wTextBoxFrameType is set.
; appears to be irrelevant, as the inside of a textbox uses the white color,
; which is the same in all four SGB palettes.
ld a, ATTR_BLK_CTRL_LINE
ld [wTempSGBPacket + 2], a
.send_packet
ld hl, wTempSGBPacket
call SendSGB
ret
AttrBlkPacket_TextBox: ; 1f4f (0:1f4f)
sgb ATTR_BLK, 1 ; sgb_command, length
db 1 ; number of data sets
; Control Code, Color Palette Designation, X1, Y1, X2, Y2
db ATTR_BLK_CTRL_INSIDE + ATTR_BLK_CTRL_LINE, 0 << 0 + 1 << 2, 0, 0, 0, 0 ; data set 1
ds 6 ; data set 2
ds 2 ; data set 3
; Fill a bxc rectangle at de and at sp-$26,
; using tile a and the subsequent ones in the following pattern:
; | a+0*l+0*h | a+0*l+1*h | a+0*l+2*h |
; | a+1*l+0*h | a+1*l+1*h | a+1*l+2*h |
; | a+2*l+0*h | a+2*l+1*h | a+2*l+2*h |
FillRectangle: ; 1f5f (0:1f5f)
push de
push af
push hl
add sp, -BG_MAP_WIDTH
call DECoordToBGMap0Address
.next_row
push hl
push bc
ld hl, sp+$25
ld d, [hl]
ld hl, sp+$27
ld a, [hl]
ld hl, sp+$4
push hl
.next_tile
ld [hli], a
add d
dec b
jr nz, .next_tile
pop de
pop bc
pop hl
push hl
push bc
ld c, b
ld b, 0
call SafeCopyDataDEtoHL
ld hl, sp+$24
ld a, [hl]
ld hl, sp+$27
add [hl]
ld [hl], a
pop bc
pop de
ld hl, BG_MAP_WIDTH
add hl, de
dec c
jr nz, .next_row
add sp, $24
pop de
ret
Func_1f96: ; 1f96 (0:1f96)
add sp, -10
ld hl, sp+0
ld [hli], a ; sp-10 <- a
ld [hl], $00 ; sp-9 <- 0
inc hl
ld a, [de]
inc de
ld [hli], a ; sp-8 <- [de]
ld [hl], $00 ; sp-7 <- 0
ld hl, sp+5
ld a, [de]
inc de
ld [hld], a ; sp-5 <- [de+1]
ld a, [de]
inc de
ld [hl], a ; sp-6 <- [de+2]
ld hl, sp+6
ld a, [de]
inc de
ld [hli], a ; sp-4 <- [de+3]
ld a, [de]
inc de
ld [hli], a ; sp-3 <- [de+4]
ld a, [de]
inc de
ld l, a ; l <- [de+5]
ld a, [de]
dec de
ld h, a ; h <- [de+6]
or l
jr z, .asm_1fbd
add hl, de
.asm_1fbd
ld e, l
ld d, h ; de += hl
ld hl, sp+8
ld [hl], e ; sp-2 <- e
inc hl
ld [hl], d ; sp-1 <- d
ld hl, sp+0
ld e, [hl] ; e <- sp
jr .asm_2013
push hl
push de
push hl
add sp, -4
ld hl, sp+0
ld [hl], c
inc hl
ld [hl], $00
inc hl
ld [hl], b
ld hl, sp+8
xor a
ld [hli], a
ld [hl], a
.asm_1fdb
call DoFrame
ld hl, sp+3
ld [hl], a
ld c, a
and $09
jr nz, .asm_2032
ld a, c
and $06
jr nz, .asm_203c
ld hl, sp+2
ld b, [hl]
ld hl, sp+0
ld a, [hl]
bit 6, c
jr nz, .asm_1ffe
bit 7, c
jr nz, .asm_2007
call Func_2046
jr .asm_1fdb
.asm_1ffe
dec a
bit 7, a
jr z, .asm_200c
ld a, b
dec a
jr .asm_200c
.asm_2007
inc a
cp b
jr c, .asm_200c
xor a
.asm_200c
ld e, a
call Func_2051
ld hl, sp+0
ld [hl], e
.asm_2013
inc hl
ld [hl], $00
inc hl
ld b, [hl]
inc hl
ld c, [hl]
ld hl, sp+8
ld a, [hli]
ld h, [hl]
ld l, a
or h
jr z, .asm_202d
ld a, e
ld de, .asm_2028
push de
jp hl
.asm_2028
jr nc, .asm_202d
ld hl, sp+0
ld [hl], a
.asm_202d
call Func_2046
jr .asm_1fdb
.asm_2032
call Func_2051
ld hl, sp+0
ld a, [hl]
add sp, 10
or a
ret
.asm_203c
call Func_2051
ld hl, sp+0
ld a, [hl]
add sp, 10
scf
ret
Func_2046: ; 2046 (0:2046)
ld hl, sp+3
ld a, [hl]
inc [hl]
and $0f
ret nz
bit 4, [hl]
jr z, Func_2055
; fallthrough
Func_2051: ; 2051 (0:2051)
ld hl, sp+9
jr Func_2057
Func_2055: ; 2055 (0:2055)
ld hl, sp+8
; fallthrough
Func_2057: ; 2057 (0:2057)
ld e, [hl]
ld hl, sp+2
ld a, [hl]
ld hl, sp+6
add [hl]
inc hl
ld c, a
ld b, [hl]
ld a, e
call HblankWriteByteToBGMap0
ret
; loads the four tiles of the card set 2 icon constant provided in register a
; returns carry if the specified set does not have an icon
LoadCardSet2Tiles: ; 2066 (0:2066)
and $7 ; mask out PRO
ld e, a
ld d, 0
ld hl, .tile_offsets
add hl, de
ld a, [hl]
cp -1
ccf
ret z
ld e, a
ld d, 0
ld hl, DuelOtherGraphics + $1d tiles
add hl, de
ld de, v0Tiles1 + $7c tiles
ld b, $04
call CopyFontsOrDuelGraphicsTiles
or a
ret
.tile_offsets
; PRO/NONE, JUNGLE, FOSSIL, -1, -1, -1, -1, GB
db -1, $0 tiles, $4 tiles, -1, -1, -1, -1, $8 tiles
; loads the Deck and Hand icons for the "Draw X card(s) from the deck." screen
LoadDuelDrawCardsScreenTiles: ; 208d (0:208d)
ld hl, DuelOtherGraphics + $29 tiles
ld de, v0Tiles1 + $74 tiles
ld b, $08
jp CopyFontsOrDuelGraphicsTiles
; loads the 8 tiles that make up the border of the main duel menu as well as the border
; of a large card picture (displayed after drawing the card or placing it in the arena).
LoadCardOrDuelMenuBorderTiles: ; 2098 (0:2098)
ld hl, DuelOtherGraphics + $15 tiles
ld de, v0Tiles1 + $50 tiles
ld b, $08
jr CopyFontsOrDuelGraphicsTiles
; loads the graphics of a card type header, used to display a picture of a card after drawing it
; or placing it in the arena. register e determines which header (TRAINER, ENERGY, PoKéMoN)
LoadCardTypeHeaderTiles: ; 20a2 (0:20a2)
ld d, a
ld e, 0
ld hl, DuelCardHeaderGraphics - $4000
add hl, de
ld de, v0Tiles1 + $60 tiles
ld b, $10
jr CopyFontsOrDuelGraphicsTiles
; loads the symbols that are displayed near the names of a list of cards in the hand or discard pile
LoadDuelCardSymbolTiles: ; 20b0 (0:20b0)
ld hl, DuelDmgSgbSymbolGraphics - $4000
ld a, [wConsole]
cp CONSOLE_CGB
jr nz, .copy
ld hl, DuelCgbSymbolGraphics - $4000
.copy
ld de, v0Tiles1 + $50 tiles
ld b, $30
jr CopyFontsOrDuelGraphicsTiles
; loads the symbols for Stage 1 Pkmn card, Stage 2 Pkmn card, and Trainer card.
; unlike LoadDuelCardSymbolTiles excludes the symbols for Basic Pkmn and all energies.
LoadDuelCardSymbolTiles2: ; 20c4 (0:20c4)
ld hl, DuelDmgSgbSymbolGraphics + $4 tiles - $4000
ld a, [wConsole]
cp CONSOLE_CGB
jr nz, .copy
ld hl, DuelCgbSymbolGraphics + $4 tiles - $4000
.copy
ld de, v0Tiles1 + $54 tiles
ld b, $c
jr CopyFontsOrDuelGraphicsTiles
; load the face down basic / stage1 / stage2 card images shown in the ckeck Pokemon screens
LoadDuelFaceDownCardTiles: ; 20d8 (0:20d8)
ld b, $10
jr LoadDuelCheckPokemonScreenTiles.got_num_tiles
; same as LoadDuelFaceDownCardTiles, plus also load the ACT / BPx tiles
LoadDuelCheckPokemonScreenTiles: ; 20dc (0:20dc)
ld b, $24
.got_num_tiles
ld hl, DuelDmgSgbSymbolGraphics + $30 tiles - $4000
ld a, [wConsole]
cp CONSOLE_CGB
jr nz, .copy
ld hl, DuelCgbSymbolGraphics + $30 tiles - $4000
.copy
ld de, v0Tiles1 + $50 tiles
jr CopyFontsOrDuelGraphicsTiles
; load the tiles for the "Placing the prizes..." screen
LoadPlacingThePrizesScreenTiles: ; 20f0 (0:20f0)
; load the Pokeball field tiles
ld hl, DuelOtherGraphics
ld de, v0Tiles1 + $20 tiles
ld b, $d
call CopyFontsOrDuelGraphicsTiles
; fallthrough
; load the Deck and the Discard Pile icons
LoadDeckAndDiscardPileIcons: ; 20fb (0:20fb)
ld hl, DuelDmgSgbSymbolGraphics + $54 tiles - $4000
ld a, [wConsole]
cp CONSOLE_CGB
jr nz, .copy
ld hl, DuelCgbSymbolGraphics + $54 tiles - $4000
.copy
ld de, v0Tiles1 + $50 tiles
ld b, $30
jr CopyFontsOrDuelGraphicsTiles
; load the tiles for the [O] and [X] symbols used to display the results of a coin toss
LoadDuelCoinTossResultTiles: ; 210f (0:210f)
ld hl, DuelOtherGraphics + $d tiles
ld de, v0Tiles2 + $30 tiles
ld b, $8
jr CopyFontsOrDuelGraphicsTiles
; load the tiles of the text characters used with TX_SYMBOL
LoadSymbolsFont: ; 2119 (0:2119)
ld hl, SymbolsFont - $4000
ld de, v0Tiles2 ; destination
ld b, (DuelCardHeaderGraphics - SymbolsFont) / TILE_SIZE ; number of tiles
; fallthrough
; if hl ≤ $3fff
; copy b tiles from Gfx1:(hl+$4000) to de
; if $4000 ≤ hl ≤ $7fff
; copy b tiles from Gfx2:hl to de
CopyFontsOrDuelGraphicsTiles: ; 2121 (0:2121)
ld a, BANK(Fonts); BANK(DuelGraphics)
call BankpushROM
ld c, TILE_SIZE
call CopyGfxData
call BankpopROM
ret
; this function copies gfx data into sram
Func_212f: ; 212f (0:212f)
ld hl, SymbolsFont - $4000
ld de, $a400
ld b, $30
call CopyFontsOrDuelGraphicsTiles
ld hl, DuelOtherGraphics + $150
ld de, $a700
ld b, $08
call CopyFontsOrDuelGraphicsTiles
call GetCardSymbolData
sub $d0
ld l, a
ld h, $00
add hl, hl
add hl, hl
add hl, hl
add hl, hl
ld de, DuelDmgSgbSymbolGraphics - $4000
add hl, de
ld de, $a780
ld b, $04
call CopyFontsOrDuelGraphicsTiles
ld hl, DuelDmgSgbSymbolGraphics - $4000
ld de, $b100
ld b, $30
jr CopyFontsOrDuelGraphicsTiles
; load the graphics and draw the duel box message given a BOXMSC_* constant in a
DrawDuelBoxMessage: ; 2167 (0:2167)
ld l, a
ld h, 40 tiles / 4 ; boxes are 10x4 tiles
call HtimesL
add hl, hl
add hl, hl
; hl = a * 40 tiles
ld de, DuelBoxMessages
add hl, de
ld de, v0Tiles1 + $20 tiles
ld b, 40
call CopyFontsOrDuelGraphicsTiles
ld a, $a0
lb hl, 1, 10
lb bc, 10, 4
lb de, 5, 4
jp FillRectangle
; load the tiles for the latin, katakana, and hiragana fonts into VRAM
; from gfx/fonts/full_width/3.1bpp and gfx/fonts/full_width/4.t3.1bpp
LoadFullWidthFontTiles: ; 2189 (0:2189)
ld hl, FullWidthFonts + $3cc tiles_1bpp - $4000
ld a, BANK(Fonts); BANK(DuelGraphics)
call BankpushROM
push hl
ld e, l
ld d, h
ld hl, v0Tiles0
call Copy1bppTiles
pop de
ld hl, v0Tiles2
call Copy1bppTiles
ld hl, v0Tiles1
call Copy1bppTiles
call BankpopROM
ret
; copy 128 1bpp tiles from de to hl as 2bpp
Copy1bppTiles: ; 21ab (0:21ab)
ld b, $80
.tile_loop
ld c, TILE_SIZE_1BPP
.pixel_loop
ld a, [de]
inc de
ld [hli], a
ld [hli], a
dec c
jr nz, .pixel_loop
dec b
jr nz, .tile_loop
ret
; similar to ProcessText except it calls InitTextPrinting first
; with the first two bytes of hl being used to set hTextBGMap0Address.
; (the caller to ProcessText usually calls InitTextPrinting first)
InitTextPrinting_ProcessText: ; 21ba (0:21ba)
push de
push bc
ld d, [hl]
inc hl
ld e, [hl]
inc hl
call InitTextPrinting
jr ProcessText.next_char
; reads the characters from the text at hl processes them. loops until
; TX_END is found. ignores TX_RAM1, TX_RAM2, and TX_RAM3 characters.
ProcessText: ; 21c5 (0:21c5)
push de
push bc
call InitTextFormat
jr .next_char
.char_loop
cp TX_CTRL_START
jr c, .character_pair
cp TX_CTRL_END
jr nc, .character_pair
call ProcessSpecialTextCharacter
jr .next_char
.character_pair
ld e, a ; first char
ld d, [hl] ; second char
call ClassifyTextCharacterPair
jr nc, .not_tx_fullwidth
inc hl
.not_tx_fullwidth
call Func_22ca
xor a
call ProcessSpecialTextCharacter
.next_char
ld a, [hli]
or a
jr nz, .char_loop
; TX_END
call TerminateHalfWidthText
pop bc
pop de
ret
; processes the text character provided in a checking for specific control characters.
; hl points to the text character coming right after the one loaded into a.
; returns carry if the character was not processed by this function.
ProcessSpecialTextCharacter: ; 21f2 (0:21f2)
or a ; TX_END
jr z, .tx_end
cp TX_HIRAGANA
jr z, .set_syllabary
cp TX_KATAKANA
jr z, .set_syllabary
cp "\n"
jr z, .end_of_line
cp TX_SYMBOL
jr z, .tx_symbol
cp TX_HALFWIDTH
jr z, .tx_halfwidth
cp TX_HALF2FULL
jr z, .tx_half2full
scf
ret
.tx_halfwidth
ld a, HALF_WIDTH
ld [wFontWidth], a
ret
.tx_half2full
call TerminateHalfWidthText
xor a ; FULL_WIDTH
ld [wFontWidth], a
ld a, TX_KATAKANA
ldh [hJapaneseSyllabary], a
ret
.set_syllabary
ldh [hJapaneseSyllabary], a
xor a
ret
.tx_symbol
ld a, [wFontWidth]
push af
ld a, HALF_WIDTH
ld [wFontWidth], a
call TerminateHalfWidthText
pop af
ld [wFontWidth], a
ldh a, [hffb0]
or a
jr nz, .skip_placing_tile
ld a, [hl]
push hl
call PlaceNextTextTile
pop hl
.skip_placing_tile
inc hl
.tx_end
ldh a, [hTextLineLength]
or a
ret z
ld b, a
ldh a, [hTextLineCurPos]
cp b
jr z, .end_of_line
xor a
ret
.end_of_line
call TerminateHalfWidthText
ld a, [wLineSeparation]
or a
call z, .next_line
.next_line
xor a
ldh [hTextLineCurPos], a
ldh a, [hTextHorizontalAlign]
add BG_MAP_WIDTH
ld b, a
; get current line's starting BGMap0 address
ldh a, [hTextBGMap0Address]
and $e0
; advance to next line
add b ; apply background scroll correction
ldh [hTextBGMap0Address], a
ldh a, [hTextBGMap0Address + 1]
adc $0
ldh [hTextBGMap0Address + 1], a
ld a, [wCurTextLine]
inc a
ld [wCurTextLine], a
xor a
ret
; calls InitTextFormat, selects tiles at $8800-$97FF for text, and clears the wc600.
; selects the first and last tile to be reserved for constructing text tiles in VRAM
; based on the values given in d and e respectively.
SetupText: ; 2275 (0:2275)
ld a, d
dec a
ld [wcd04], a
ld a, e
ldh [hffa8], a
call InitTextFormat
xor a
ldh [hffb0], a
ldh [hffa9], a
ld a, $88
ld [wTilePatternSelector], a
ld a, $80
ld [wTilePatternSelectorCorrection], a
ld hl, wc600
.clear_loop
xor a
ld [hl], a
inc l
jr nz, .clear_loop
ret
; wFontWidth <- FULL_WIDTH
; hTextLineCurPos <- 0
; wHalfWidthPrintState <- 0
; hJapaneseSyllabary <- TX_KATAKANA
InitTextFormat: ; 2298 (0:2298)
xor a ; FULL_WIDTH
ld [wFontWidth], a
ldh [hTextLineCurPos], a
ld [wHalfWidthPrintState], a
ld a, TX_KATAKANA
ldh [hJapaneseSyllabary], a
ret
; call InitTextPrinting
; hTextLineLength <- a
InitTextPrintingInTextbox: ; 22a6 (0:22a6)
push af
call InitTextPrinting
pop af
ldh [hTextLineLength], a
ret
; hTextHorizontalAlign <- d
; hTextLineLength <- 0
; wCurTextLine <- 0
; write BGMap0-translated DE to hTextBGMap0Address
; call InitTextFormat
InitTextPrinting: ; 22ae (0:22ae)
push hl
ld a, d
ldh [hTextHorizontalAlign], a
xor a
ldh [hTextLineLength], a
ld [wCurTextLine], a
call DECoordToBGMap0Address
ld a, l
ldh [hTextBGMap0Address], a
ld a, h
ldh [hTextBGMap0Address + 1], a
call InitTextFormat
xor a
ld [wHalfWidthPrintState], a
pop hl
ret
; requests a text tile to be generated and prints it in the screen
; different modes depending on hffb0:
; hffb0 == $0: generate and place text tile
; hffb0 == $2 (bit 1 set): only generate text tile?
; hffb0 == $1 (bit 0 set): not even generate it, but just update text buffers?
Func_22ca: ; 22ca (0:22ca)
push hl
push de
push bc
ldh a, [hffb0]
and $1
jr nz, .asm_22ed
call Func_2325
jr c, .tile_already_exists
or a
jr nz, .done
call GenerateTextTile
.tile_already_exists
ldh a, [hffb0]
and $2
jr nz, .done
ldh a, [hffa9]
call PlaceNextTextTile
.done
pop bc
pop de
pop hl
ret
.asm_22ed
call Func_235e
jr .done
; writes a to wCurTextTile and to the tile pointed to by hTextBGMap0Address,
; then increments hTextBGMap0Address and hTextLineCurPos
PlaceNextTextTile: ; 22f2 (0:22f2)
ld [wCurTextTile], a
ld hl, hTextBGMap0Address
ld e, [hl]
inc hl
ld d, [hl]
inc de
ld [hl], d
dec hl
ld [hl], e
dec de
ld l, e
ld h, d
ld de, wCurTextTile
ld c, 1
call SafeCopyDataDEtoHL
ld hl, hTextLineCurPos
inc [hl]
ret
; when terminating half-width text with "\n" or TX_END, or switching to full-width
; with TX_HALF2FULL or to symbols with TX_SYMBOL, check if it's necessary to append
; a half-width space to finish an incomplete character pair.
TerminateHalfWidthText: ; 230f (0:230f)
ld a, [wFontWidth]
or a ; FULL_WIDTH
ret z
ld a, [wHalfWidthPrintState]
or a
ret z ; return if the last printed character was the second of a pair
push hl
push de
push bc
ld e, " "
call Func_22ca
pop bc
pop de
pop hl
ret
Func_2325: ; 2325 (0:2325)
call Func_235e
ret c
or a
ret nz
ldh a, [hffa8]
ld hl, wcd04
cp [hl]
jr nz, .asm_2345
ldh a, [hffa9]
ld h, $c8
.asm_2337
ld l, a
ld a, [hl]
or a
jr nz, .asm_2337
ld h, $c9
ld c, [hl]
ld b, $c8
xor a
ld [bc], a
jr .asm_234a
.asm_2345
inc [hl]
jr nz, .asm_2349
inc [hl]
.asm_2349
ld l, [hl]
.asm_234a
ldh a, [hffa9]
ld c, a
ld b, $c9
ld a, l
ldh [hffa9], a
ld [bc], a
ld h, $c8
ld [hl], c
ld h, $c6
ld [hl], e
inc h ; $c7
ld [hl], d
ld b, l
xor a
ret
; search linked-list for text characters e/d (registers), if found hoist
; the result to head of list and return it. carry flag denotes success.
Func_235e: ; 235e (0:235e)
ld a, [wFontWidth]
or a
jr z, .print
call CaseHalfWidthLetter
; if [wHalfWidthPrintState] != 0, load it to d and print the pair of chars
; zero wHalfWidthPrintState for next iteration
ld a, [wHalfWidthPrintState]
ld d, a
or a
jr nz, .print
; if [wHalfWidthPrintState] == 0, don't print text in this iteration
; load the next value of register d into wHalfWidthPrintState
ld a, e
ld [wHalfWidthPrintState], a
ld a, $1
or a
ret ; nz
.print
xor a
ld [wHalfWidthPrintState], a
ldh a, [hffa9]
ld l, a ; l ← [hffa9]; index to to linked-list head
.asm_237d
ld h, $c6 ;
ld a, [hl] ; a ← key1[l] ;
or a ;
ret z ; if NULL, return a = 0 ;
cp e ; loop for e/d key in
jr nz, .asm_238a ; ; linked list
inc h ; $c7 ; ;
ld a, [hl] ; if key1[l] == e and ;
cp d ; key2[l] == d: ;
jr z, .asm_238f ; break ;
.asm_238a
ld h, $c8 ; ;
ld l, [hl] ; l ← next[l] ;
jr .asm_237d
.asm_238f
ldh a, [hffa9]
cp l
jr z, .asm_23af ; assert at least one iteration
ld c, a
ld b, $c9
ld a, l
ld [bc], a ; prev[i0] ← i
ldh [hffa9], a ; [hffa9] ← i (update linked-list head)
ld h, $c9
ld b, [hl]
ld [hl], $0 ; prev[i] ← 0
ld h, $c8
ld a, c
ld c, [hl]
ld [hl], a ; next[i] ← i0
ld l, b
ld [hl], c ; next[prev[i]] ← next[i]
ld h, $c9
inc c
dec c
jr z, .asm_23af ; if next[i] != NULL:
ld l, c ; l ← next[i]
ld [hl], b ; prev[next[i]] ← prev[i]
.asm_23af
scf ; set carry to indicate success
ret ; (return new linked-list head in a)
; uppercases e if [wUppercaseHalfWidthLetters] is nonzero
CaseHalfWidthLetter: ; 23b1 (0:23b1)
ld a, [wUppercaseHalfWidthLetters]
or a
ret z
ld a, e
cp $60
ret c
cp $7b
ret nc
sub "a" - "A"
ld e, a
ret
; iterates over text at hl until TX_END is found, and sets wFontWidth to
; FULL_WIDTH if the first character is TX_HALFWIDTH
; returns:
; b = length of text in tiles
; c = length of text in bytes
; a = -b
GetTextLengthInTiles: ; 23c1 (0:23c1)
ld a, [hl]
cp TX_HALFWIDTH
jr nz, .full_width
call GetTextLengthInHalfTiles
; return a = - ceil(b/2)
inc b
srl b
xor a
sub b
ret
.full_width
xor a ; FULL_WIDTH
ld [wFontWidth], a
; fallthrough
; iterates over text at hl until TX_END is found
; returns:
; b = length of text in half-tiles
; c = length of text in bytes
; a = -b
GetTextLengthInHalfTiles: ; 23d3 (0:23d3)
push hl
push de
lb bc, $00, $00
.char_loop
ld a, [hli]
or a ; TX_END
jr z, .tx_end
inc c ; any char except TX_END: c ++
; TX_FULLWIDTH, TX_SYMBOL, or > TX_CTRL_END : b ++
cp TX_CTRL_START
jr c, .character_pair
cp TX_CTRL_END
jr nc, .character_pair
cp TX_SYMBOL
jr nz, .char_loop
inc b
jr .next
.character_pair
ld e, a ; first char
ld d, [hl] ; second char
inc b
call ClassifyTextCharacterPair
jr nc, .char_loop
; TX_FULLWIDTH
.next
inc c ; TX_FULLWIDTH or TX_SYMBOL: c ++
inc hl
jr .char_loop
.tx_end
; return a = -b
xor a
sub b
pop de
pop hl
ret
; copy text of maximum length a (in tiles) from hl to de, then terminate
; the text with TX_END if it doesn't contain it already.
; fill any remaining bytes with spaces plus TX_END to match the length specified in a.
; return the text's actual length in characters (i.e. before the first TX_END) in e.
CopyTextData: ; 23fd (0:23fd)
ld [wTextMaxLength], a
ld a, [hl]
cp TX_HALFWIDTH
jr z, .half_width_text
ld a, [wTextMaxLength]
call .copyTextData
jr c, .fw_text_done
push hl
.fill_fw_loop
ld a, FW_SPACE
ld [hli], a
dec d
jr nz, .fill_fw_loop
ld [hl], TX_END
pop hl
.fw_text_done
ld a, e
ret
.half_width_text
ld a, [wTextMaxLength]
add a
call .copyTextData
jr c, .hw_text_done
push hl
.fill_hw_loop
ld a, " "
ld [hli], a
dec d
jr nz, .fill_hw_loop
ld [hl], TX_END
pop hl
.hw_text_done
ld a, e
ret
.copyTextData
push bc
ld c, l
ld b, h
ld l, e
ld h, d
ld d, a
ld e, 0
.loop
ld a, [bc]
or a ; TX_END
jr z, .done
inc bc
ld [hli], a
cp TX_CTRL_START
jr c, .character_pair
cp TX_CTRL_END
jr c, .loop
.character_pair
push de
ld e, a ; first char
ld a, [bc]
ld d, a ; second char
call ClassifyTextCharacterPair
jr nc, .not_tx_fullwidth
ld a, [bc]
inc bc
ld [hli], a
.not_tx_fullwidth
pop de
inc e ; return in e the amount of characters actually copied
dec d ; return in d the difference between the maximum length and e
jr nz, .loop
ld [hl], TX_END
pop bc
scf ; return carry if the text did not already end with TX_END
ret
.done
pop bc
or a
ret
; convert the number at hl to TX_SYMBOL text format and write it to wStringBuffer
; replace leading zeros with SYM_SPACE
TwoByteNumberToTxSymbol_TrimLeadingZeros: ; 245d (0:245d)
push de
push bc
ld de, wStringBuffer
push de
ld bc, -10000
call .get_digit
ld bc, -1000
call .get_digit
ld bc, -100
call .get_digit
ld bc, -10
call .get_digit
ld bc, -1
call .get_digit
xor a ; TX_END
ld [de], a
pop hl
ld e, 5
.digit_loop
inc hl
ld a, [hl]
cp SYM_0
jr nz, .done ; jump if not zero
ld [hl], SYM_SPACE ; trim leading zero
inc hl
dec e
jr nz, .digit_loop
dec hl
ld [hl], SYM_0
.done
dec hl
pop bc
pop de
ret
.get_digit
ld a, TX_SYMBOL
ld [de], a
inc de
ld a, SYM_0 - 1
.substract_loop
inc a
add hl, bc
jr c, .substract_loop
ld [de], a
inc de
ld a, l
sub c
ld l, a
ld a, h
sbc b
ld h, a
ret
; generates a text tile and copies it to VRAM
; if wFontWidth == FULL_WIDTH
; de = full-width font tile number
; if wFontWidth == HALF_WIDTH
; d = half-width character 1 (left)
; e = half-width character 2 (right)
; b = destination VRAM tile number
GenerateTextTile: ; 24ac (0:24ac)
push hl
push de
push bc
ld a, [wFontWidth]
or a
jr nz, .half_width
;.full_width
call CreateFullWidthFontTile_ConvertToTileDataAddress
call SafeCopyDataDEtoHL
.done
pop bc
pop de
pop hl
ret
.half_width
call CreateHalfWidthFontTile
call ConvertTileNumberToTileDataAddress
call SafeCopyDataDEtoHL
jr .done
; create, at wTextTileBuffer, a half-width font tile
; made from the ascii characters given in d and e
CreateHalfWidthFontTile: ; 24ca (0:24ca)
push bc
ldh a, [hBankROM]
push af
ld a, BANK(HalfWidthFont)
call BankswitchROM
; write the right half of the tile (first character) to wTextTileBuffer + 2n
push de
ld a, e
ld de, wTextTileBuffer
call CopyHalfWidthCharacterToDE
pop de
; write the left half of the tile (second character) to wTextTileBuffer + 2n+1
ld a, d
ld de, wTextTileBuffer + 1
call CopyHalfWidthCharacterToDE
; construct the 2bpp-converted half-width font tile
ld hl, wTextTileBuffer
ld b, TILE_SIZE_1BPP
.loop
ld a, [hli]
swap a
or [hl]
dec hl
ld [hli], a
ld [hli], a
dec b
jr nz, .loop
call BankpopROM
pop bc
ld de, wTextTileBuffer
ret
; copies a 1bpp tile corresponding to a half-width font character to de.
; the ascii value of the character to copy is provided in a.
; assumes BANK(HalfWidthFont) is already loaded.
CopyHalfWidthCharacterToDE: ; 24fa (0:24fa)
sub $20 ; HalfWidthFont begins at ascii $20
ld l, a
ld h, $0
add hl, hl
add hl, hl
add hl, hl
ld bc, HalfWidthFont
add hl, bc
ld b, TILE_SIZE_1BPP
.loop
ld a, [hli]
ld [de], a
inc de
inc de
dec b
jr nz, .loop
ret
; create, at wTextTileBuffer, a full-width font tile given its tile
; number within the full-width font graphics (FullWidthFonts) in de.
; return its v*Tiles address in hl, and return c = TILE_SIZE.
CreateFullWidthFontTile_ConvertToTileDataAddress: ; 2510 (0:2510)
push bc
call GetFullWidthFontTileOffset
call CreateFullWidthFontTile
pop bc
; fallthrough
; given a tile number in b, return its v*Tiles address in hl, and return c = TILE_SIZE
; wTilePatternSelector and wTilePatternSelectorCorrection are used to select the source:
; - if wTilePatternSelector == $80 and wTilePatternSelectorCorrection == $00 -> $8000-$8FFF
; - if wTilePatternSelector == $88 and wTilePatternSelectorCorrection == $80 -> $8800-$97FF
ConvertTileNumberToTileDataAddress: ; 2518 (0:2518)
ld hl, wTilePatternSelectorCorrection
ld a, b
xor [hl]
ld h, $0
ld l, a
add hl, hl
add hl, hl
add hl, hl
add hl, hl
ld a, [wTilePatternSelector]
ld b, a
ld c, $0
add hl, bc
ld c, TILE_SIZE
ret
; create, at wTextTileBuffer, a full-width font tile given its
; within the full-width font graphics (FullWidthFonts) in hl
CreateFullWidthFontTile: ; 252e (0:252e)
ld a, BANK(Fonts); BANK(DuelGraphics)
call BankpushROM
ld de, wTextTileBuffer
push de
ld c, TILE_SIZE_1BPP
.loop
ld a, [hli]
ld [de], a
inc de
ld [de], a
inc de
dec c
jr nz, .loop
pop de
call BankpopROM
ret
; given two text characters at de, use the char at e (first one)
; to determine which type of text this pair of characters belongs to.
; return carry if TX_FULLWIDTH1 to TX_FULLWIDTH4.
ClassifyTextCharacterPair: ; 2546 (0:2546)
ld a, [wFontWidth]
or a ; FULL_WIDTH
jr nz, .half_width
ld a, e
cp TX_CTRL_END
jr c, .continue_check
cp $60
jr nc, .not_katakana
ldh a, [hJapaneseSyllabary]
cp TX_KATAKANA
jr nz, .not_katakana
ld d, TX_KATAKANA
or a
ret
.half_width
; in half width mode, the first character goes in e, so leave them like that
or a
ret
.continue_check
cp TX_CTRL_START
jr c, .ath_font
.not_katakana
; 0_1_hiragana.1bpp (e < $60) or 0_2_digits_kanji1.1bpp (e >= $60)
ld d, $0
or a
ret
.ath_font
; TX_FULLWIDTH1 to TX_FULLWIDTH4
; swap d and e to put the TX_FULLWIDTH* character first
ld e, d
ld d, a
scf
ret
; convert the full-width font tile number at de to the
; equivalent offset within the full-width font tile graphics.
; if d == TX_KATAKANA: get tile from the 0_0_katakana.1bpp font.
; if d == TX_HIRAGANA or d == $0: get tile from the 0_1_hiragana.1bpp or 0_2_digits_kanji1.1bpp font.
; if d >= TX_FULLWIDTH1 and d <= TX_FULLWIDTH4: get tile from one of the other full-width fonts.
GetFullWidthFontTileOffset: ; 256d (0:256d)
ld bc, $50 tiles_1bpp
ld a, d
cp TX_HIRAGANA
jr z, .hiragana
cp TX_KATAKANA
jr nz, .get_address
ld bc, $0 tiles
ld a, e
sub $10 ; the first $10 are control characters, but this font's graphics start at $0
ld e, a
.hiragana
ld d, $0
.get_address
ld l, e
ld h, d
add hl, hl
add hl, hl
add hl, hl
add hl, bc
ret
; pointers to VRAM?
Unknown_2589: ; 2589 (0:2589)
db $18
dw $8140
dw $817e
dw $8180
dw $81ac
dw $81b8
dw $81bf
dw $81c8
dw $81ce
dw $81da
dw $81e8
dw $81f0
dw $81f7
dw $81fc
dw $81fc
dw $824f
dw $8258
dw $8260
dw $8279
dw $8281
dw $829a
dw $829f
dw $82f1
dw $8340
dw $837e
dw $8380
dw $8396
dw $839f
dw $83b6
dw $83bf
dw $83d6
dw $8440
dw $8460
dw $8470
dw $847e
dw $8480
dw $8491
dw $849f
dw $84be
dw $889f
dw $88fc
dw $8940
dw $9443
dw $9840
dw $9872
dw $989f
dw $98fc
dw $9940
dw $ffff
; initializes parameters for a card list (e.g. list of hand cards in a duel, or booster pack cards)
; input:
; a = list length
; de = initial page scroll offset, initial item (in the visible page)
; hl: 9 bytes with the rest of the parameters
InitializeCardListParameters: ; 25ea (0:25ea)
ld [wNumListItems], a
ld a, d
ld [wListScrollOffset], a
ld a, e
ld [wCurMenuItem], a
add d
ldh [hCurMenuItem], a
ld a, [hli]
ld [wCursorXPosition], a
ld a, [hli]
ld [wCursorYPosition], a
ld a, [hli]
ld [wListItemXPosition], a
ld a, [hli]
ld [wListItemNameMaxLength], a
ld a, [hli]
ld [wNumMenuItems], a
ld a, [hli]
ld [wCursorTile], a
ld a, [hli]
ld [wTileBehindCursor], a
ld a, [hli]
ld [wListFunctionPointer], a
ld a, [hli]
ld [wListFunctionPointer + 1], a
xor a
ld [wCursorBlinkCounter], a
ld a, 1
ld [wYDisplacementBetweenMenuItems], a
ret
; similar to HandleMenuInput, but conveniently returns parameters related to the
; state of the list in a, d, and e if A or B were pressed. also returns carry
; if A or B were pressed, nc otherwise. returns -1 in a if B was pressed.
; used for example in the Hand card list and Discard Pile card list screens.
HandleCardListInput: ; 2626 (0:2626)
call HandleMenuInput
ret nc
ld a, [wListScrollOffset]
ld d, a
ld a, [wCurMenuItem]
ld e, a
ldh a, [hCurMenuItem]
scf
ret
; initializes parameters for a menu, given the 8 bytes starting at hl,
; which are loaded to the following addresses:
; wCursorXPosition, wCursorYPosition, wYDisplacementBetweenMenuItems, wNumMenuItems,
; wCursorTile, wTileBehindCursor, wMenuFunctionPointer.
; also sets the current menu item (wCurMenuItem) to the one specified in register a.
InitializeMenuParameters: ; 2636 (0:2636)
ld [wCurMenuItem], a
ldh [hCurMenuItem], a
ld de, wCursorXPosition
ld b, wMenuFunctionPointer + $2 - wCursorXPosition
.loop
ld a, [hli]
ld [de], a
inc de
dec b
jr nz, .loop
xor a
ld [wCursorBlinkCounter], a
ret
; returns with the carry flag set if A or B were pressed
; returns a = 0 if A was pressed, a = -1 if B was pressed
; note: return values still subject to those of the function at [wMenuFunctionPointer] if any
HandleMenuInput: ; 264b (0:264b)
xor a
ld [wRefreshMenuCursorSFX], a
ldh a, [hDPadHeld]
or a
jr z, .up_down_done
ld b, a
ld a, [wNumMenuItems]
ld c, a
ld a, [wCurMenuItem]
bit D_UP_F, b
jr z, .not_up
dec a
bit 7, a
jr z, .handle_up_or_down
ld a, [wNumMenuItems]
dec a ; wrapping around, so load the bottommost item
jr .handle_up_or_down
.not_up
bit D_DOWN_F, b
jr z, .up_down_done
inc a
cp c
jr c, .handle_up_or_down
xor a ; wrapping around, so load the topmost item
.handle_up_or_down
push af
ld a, $1
ld [wRefreshMenuCursorSFX], a ; buffer sound for up/down
call EraseCursor
pop af
ld [wCurMenuItem], a
xor a
ld [wCursorBlinkCounter], a
.up_down_done
ld a, [wCurMenuItem]
ldh [hCurMenuItem], a
ld hl, wMenuFunctionPointer ; call the function if non-0 (periodically)
ld a, [hli]
or [hl]
jr z, .check_A_or_B
ld a, [hld]
ld l, [hl]
ld h, a
ldh a, [hCurMenuItem]
call CallHL
jr nc, RefreshMenuCursor_CheckPlaySFX
.A_pressed_draw_cursor
call DrawCursor2
.A_pressed
call PlayOpenOrExitScreenSFX
ld a, [wCurMenuItem]
ld e, a
ldh a, [hCurMenuItem]
scf
ret
.check_A_or_B
ldh a, [hKeysPressed]
and A_BUTTON | B_BUTTON
jr z, RefreshMenuCursor_CheckPlaySFX
and A_BUTTON
jr nz, .A_pressed_draw_cursor
; B button pressed
ld a, [wCurMenuItem]
ld e, a
ld a, $ff
ldh [hCurMenuItem], a
call PlayOpenOrExitScreenSFX
scf
ret
; plays an "open screen" sound (SFX_02) if [hCurMenuItem] != 0xff
; plays an "exit screen" sound (SFX_03) if [hCurMenuItem] == 0xff
PlayOpenOrExitScreenSFX: ; 26c0 (0:26c0)
push af
ldh a, [hCurMenuItem]
inc a
jr z, .play_exit_sfx
ld a, SFX_02
jr .play_sfx
.play_exit_sfx
ld a, SFX_03
.play_sfx
call PlaySFX
pop af
ret
; called once per frame when a menu is open
; play the sound effect at wRefreshMenuCursorSFX if non-0 and blink the
; cursor when wCursorBlinkCounter hits 16 (i.e. every 16 frames)
RefreshMenuCursor_CheckPlaySFX: ; 26d1 (0:26d1)
ld a, [wRefreshMenuCursorSFX]
or a
jr z, RefreshMenuCursor
call PlaySFX
; fallthrough
RefreshMenuCursor: ; 26da (0:26da)
ld hl, wCursorBlinkCounter
ld a, [hl]
inc [hl]
; blink the cursor every 16 frames
and $f
ret nz
ld a, [wCursorTile]
bit 4, [hl]
jr z, DrawCursor
; fallthrough
; set the tile at [wCursorXPosition],[wCursorYPosition] to [wTileBehindCursor]
EraseCursor: ; 26e9 (0:26e9)
ld a, [wTileBehindCursor]
; fallthrough
; set the tile at [wCursorXPosition],[wCursorYPosition] to a
DrawCursor: ; 26ec (0:26ec)
ld c, a
ld a, [wYDisplacementBetweenMenuItems]
ld l, a
ld a, [wCurMenuItem]
ld h, a
call HtimesL
ld a, l
ld hl, wCursorXPosition
ld d, [hl]
inc hl
add [hl]
ld e, a
call AdjustCoordinatesForBGScroll
ld a, c
ld c, e
ld b, d
call WriteByteToBGMap0
or a
ret
; set the tile at [wCursorXPosition],[wCursorYPosition] to [wCursorTile]
DrawCursor2: ; 270b (0:270b)
ld a, [wCursorTile]
jr DrawCursor
; set wCurMenuItem, and hCurMenuItem to a, and zero wCursorBlinkCounter
SetMenuItem: ; 2710 (0:2710)
ld [wCurMenuItem], a
ldh [hCurMenuItem], a
xor a
ld [wCursorBlinkCounter], a
ret
; handle input for the 2-row 3-column duel menu.
; only handles input not involving the B, START, or SELECT buttons, that is,
; navigating through the menu or selecting an item with the A button.
; other input in handled by PrintDuelMenuAndHandleInput.handle_input
HandleDuelMenuInput: ; 271a (0:271a)
ldh a, [hDPadHeld]
or a
jr z, .blink_cursor
ld b, a
ld hl, wCurMenuItem
and D_UP | D_DOWN
jr z, .check_left
ld a, [hl]
xor 1 ; move to the other menu item in the same column
jr .dpad_pressed
.check_left
bit D_LEFT_F, b
jr z, .check_right
ld a, [hl]
sub 2
jr nc, .dpad_pressed
; wrap to the rightmost item in the same row
and 1
add 4
jr .dpad_pressed
.check_right
bit D_RIGHT_F, b
jr z, .dpad_not_pressed
ld a, [hl]
add 2
cp 6
jr c, .dpad_pressed
; wrap to the leftmost item in the same row
and 1
.dpad_pressed
push af
ld a, SFX_01
call PlaySFX
call .erase_cursor
pop af
ld [wCurMenuItem], a
ldh [hCurMenuItem], a
xor a
ld [wCursorBlinkCounter], a
jr .blink_cursor
.dpad_not_pressed
ldh a, [hDPadHeld]
and A_BUTTON
jp nz, HandleMenuInput.A_pressed
.blink_cursor
; blink cursor every 16 frames
ld hl, wCursorBlinkCounter
ld a, [hl]
inc [hl]
and $f
ret nz
ld a, SYM_CURSOR_R
bit 4, [hl]
jr z, .draw_cursor
.erase_cursor
ld a, SYM_SPACE
.draw_cursor
ld e, a
ld a, [wCurMenuItem]
add a
ld c, a
ld b, $0
ld hl, DuelMenuCursorCoords
add hl, bc
ld b, [hl]
inc hl
ld c, [hl]
ld a, e
call WriteByteToBGMap0
ld a, [wCurMenuItem]
ld e, a
or a
ret
DuelMenuCursorCoords: ; 278d (0:278d)
db 2, 14 ; Hand
db 2, 16 ; Attack
db 8, 14 ; Check
db 8, 16 ; Pkmn Power
db 14, 14 ; Retreat
db 14, 16 ; Done
; print the items of a list of cards (hand cards in a duel, cards from a booster pack...)
; and initialize the parameters of the list given:
; wDuelTempList = card list source
; a = list length
; de = initial page scroll offset, initial item (in the visible page)
; hl: 9 bytes with the rest of the parameters
PrintCardListItems: ; 2799 (0:2799)
call InitializeCardListParameters
ld hl, wMenuFunctionPointer
ld a, LOW(CardListMenuFunction)
ld [hli], a
ld a, HIGH(CardListMenuFunction)
ld [hli], a
ld a, 2
ld [wYDisplacementBetweenMenuItems], a
ld a, 1
ld [wCardListIndicatorYPosition], a
; fallthrough
; like PrintCardListItems, except more parameters are already initialized
; called instead of PrintCardListItems to reload the list after moving up or down
ReloadCardListItems:
ld e, SYM_SPACE
ld a, [wListScrollOffset]
or a
jr z, .cant_go_up
ld e, SYM_CURSOR_U
.cant_go_up
ld a, [wCursorYPosition]
dec a
ld c, a
ld b, 18
ld a, e
call WriteByteToBGMap0
ld e, SYM_SPACE
ld a, [wListScrollOffset]
ld hl, wNumMenuItems
add [hl]
ld hl, wNumListItems
cp [hl]
jr nc, .cant_go_down
ld e, SYM_CURSOR_D
.cant_go_down
ld a, [wNumMenuItems]
add a
add c
dec a
ld c, a
ld a, e
call WriteByteToBGMap0
ld a, [wListScrollOffset]
ld e, a
ld d, $00
ld hl, wDuelTempList
add hl, de
ld a, [wNumMenuItems]
ld b, a
ld a, [wListItemXPosition]
ld d, a
ld a, [wCursorYPosition]
ld e, a
ld c, $00
.next_card
ld a, [hl]
cp $ff
jr z, .done
push hl
push bc
push de
call LoadCardDataToBuffer1_FromDeckIndex
call DrawCardSymbol
call InitTextPrinting
ld a, [wListItemNameMaxLength]
call CopyCardNameAndLevel
ld hl, wDefaultText
call ProcessText
pop de
pop bc
pop hl
inc hl
ld a, [wNumListItems]
dec a
inc c
cp c
jr c, .done
inc e
inc e
dec b
jr nz, .next_card
.done
ret
; reload a list of cards, except don't print their names
Func_2827: ; 2827 (0:2827)
ld a, $01
ldh [hffb0], a
call ReloadCardListItems
xor a
ldh [hffb0], a
ret
; convert the number at a to TX_SYMBOL text format and write it to wDefaultText
; if the first digit is a 0, delete it and shift the number one tile to the left
OneByteNumberToTxSymbol_TrimLeadingZerosAndAlign: ; 2832 (0:2832)
call OneByteNumberToTxSymbol
ld a, [hli]
cp SYM_0
jr nz, .not_zero
; shift number one tile to the left
ld a, [hld]
ld [hli], a
ld [hl], SYM_SPACE
.not_zero
ret
; this function is always loaded to wMenuFunctionPointer by PrintCardListItems
; takes care of things like handling page scrolling and calling the function at wListFunctionPointer
CardListMenuFunction: ; 283f (0:283f)
ldh a, [hDPadHeld]
ld b, a
ld a, [wNumMenuItems]
dec a
ld c, a
ld a, [wCurMenuItem]
bit D_UP_F, b
jr z, .not_up
cp c
jp nz, .continue
; we're at the top of the page
xor a
ld [wCurMenuItem], a ; set to first item
ld hl, wListScrollOffset
ld a, [hl]
or a ; can we scroll up?
jr z, .no_more_items
dec [hl] ; scroll page up
call ReloadCardListItems
jp .continue
.not_up
bit D_DOWN_F, b
jr z, .not_down
or a
jr nz, .not_last_visible_item
; we're at the bottom of the page
ld a, c
ld [wCurMenuItem], a ; set to last item
ld a, [wListScrollOffset]
add c
inc a
ld hl, wNumListItems
cp [hl] ; can we scroll down?
jr z, .no_more_items
ld hl, wListScrollOffset
inc [hl] ; scroll page down
call ReloadCardListItems
jp .continue
.not_last_visible_item
; this appears to be a redundant check
ld hl, wListScrollOffset
add [hl]
ld hl, wNumListItems
cp [hl]
jp c, .continue ; should always jump
ld hl, wCurMenuItem
dec [hl]
.no_more_items
xor a
ld [wRefreshMenuCursorSFX], a
jp .continue
.not_down
bit D_LEFT_F, b
jr z, .not_left
ld a, [wListScrollOffset]
or a
jr z, .continue
ld hl, wNumMenuItems
sub [hl]
jr c, .top_of_page_reached
ld [wListScrollOffset], a
call ReloadCardListItems
jr .continue
.top_of_page_reached
call EraseCursor
ld a, [wListScrollOffset]
ld hl, wCurMenuItem
add [hl]
ld c, a
ld hl, wNumMenuItems
sub [hl]
jr nc, .asm_28c4
add [hl]
.asm_28c4
ld [wCurMenuItem], a
xor a
ld [wListScrollOffset], a
ld [wRefreshMenuCursorSFX], a
call ReloadCardListItems
jr .continue
.not_left
bit D_RIGHT_F, b
jr z, .continue
ld a, [wNumMenuItems]
ld hl, wNumListItems
cp [hl]
jr nc, .continue
ld a, [wListScrollOffset]
ld hl, wNumMenuItems
add [hl]
ld c, a
add [hl]
dec a
ld hl, wNumListItems
cp [hl]
jr nc, .asm_28f9
ld a, c
ld [wListScrollOffset], a
call ReloadCardListItems
jr .continue
.asm_28f9
call EraseCursor
ld a, [wListScrollOffset]
ld hl, wCurMenuItem
add [hl]
ld c, a
ld a, [wNumListItems]
ld hl, wNumMenuItems
sub [hl]
ld [wListScrollOffset], a
ld b, a
ld a, c
sub b
jr nc, .asm_2914
add [hl]
.asm_2914
ld [wCurMenuItem], a
call ReloadCardListItems
.continue
ld a, [wListScrollOffset]
ld hl, wCurMenuItem
add [hl]
ldh [hCurMenuItem], a
ld a, [wCardListIndicatorYPosition]
cp $ff
jr z, .skip_printing_indicator
; print <sel_item>/<num_items>
ld c, a
ldh a, [hCurMenuItem]
inc a
call OneByteNumberToTxSymbol_TrimLeadingZeros
ld b, 13
ld a, 2
call CopyDataToBGMap0
ld b, 15
ld a, SYM_SLASH
call WriteByteToBGMap0
ld a, [wNumListItems]
call OneByteNumberToTxSymbol_TrimLeadingZeros
ld b, 16
ld a, 2
call CopyDataToBGMap0
.skip_printing_indicator
ld hl, wListFunctionPointer
ld a, [hli]
or [hl]
jr z, .no_list_function
ld a, [hld]
ld l, [hl]
ld h, a
ldh a, [hCurMenuItem]
jp hl ; execute the function at wListFunctionPointer
.no_list_function
ldh a, [hKeysPressed]
and A_BUTTON | B_BUTTON
ret z
and B_BUTTON
jr nz, .pressed_b
scf
ret
.pressed_b
ld a, $ff
ldh [hCurMenuItem], a
scf
ret
; convert the number at a to TX_SYMBOL text format and write it to wDefaultText
; replace leading zeros with SYM_SPACE
OneByteNumberToTxSymbol_TrimLeadingZeros: ; 296a (0:296a)
call OneByteNumberToTxSymbol
ld a, [hl]
cp SYM_0
ret nz
ld [hl], SYM_SPACE
ret
; convert the number at a to TX_SYMBOL text format and write it to wDefaultText
OneByteNumberToTxSymbol: ; 2974 (0:2974)
ld hl, wDefaultText
push hl
ld e, SYM_0 - 1
.first_digit_loop
inc e
sub 10
jr nc, .first_digit_loop
ld [hl], e ; first digit
inc hl
add SYM_0 + 10
ld [hli], a ; second digit
ld [hl], SYM_SPACE
pop hl
ret
; translate the TYPE_* constant in wLoadedCard1Type to an index for CardSymbolTable
CardTypeToSymbolID: ; 2988 (0:2988)
ld a, [wLoadedCard1Type]
cp TYPE_TRAINER
jr nc, .trainer_card
cp TYPE_ENERGY
jr c, .pokemon_card
; energy card
and 7 ; convert energy constant to type constant
ret
.trainer_card
ld a, 11
ret
.pokemon_card
ld a, [wLoadedCard1Stage] ; different symbol for each evolution stage
add 8
ret
; return the entry in CardSymbolTable of the TYPE_* constant in wLoadedCard1Type
; also return the first byte of said entry (starting tile number) in a
GetCardSymbolData: ; 299f (0:299f)
call CardTypeToSymbolID
add a
ld c, a
ld b, 0
ld hl, CardSymbolTable
add hl, bc
ld a, [hl]
ret
; draw, at de, the 2x2 tile card symbol associated to the TYPE_* constant in wLoadedCard1Type
DrawCardSymbol: ; 29ac (0:29ac)
push hl
push de
push bc
call GetCardSymbolData
dec d
dec d
dec e
ld a, [wConsole]
cp CONSOLE_CGB
jr nz, .tiles
; CGB-only attrs (palette)
push hl
inc hl
ld a, [hl]
lb bc, 2, 2
lb hl, 0, 0
call BankswitchVRAM1
call FillRectangle
call BankswitchVRAM0
pop hl
.tiles
ld a, [hl]
lb hl, 1, 2
lb bc, 2, 2
call FillRectangle
pop bc
pop de
pop hl
ret
CardSymbolTable:
; starting tile number, cgb palette (grey, yellow/red, green/blue, pink/orange)
db $e0, $01 ; TYPE_ENERGY_FIRE
db $e4, $02 ; TYPE_ENERGY_GRASS
db $e8, $01 ; TYPE_ENERGY_LIGHTNING
db $ec, $02 ; TYPE_ENERGY_WATER
db $f0, $03 ; TYPE_ENERGY_PSYCHIC
db $f4, $03 ; TYPE_ENERGY_FIGHTING
db $f8, $00 ; TYPE_ENERGY_DOUBLE_COLORLESS
db $fc, $02 ; TYPE_ENERGY_UNUSED
db $d0, $02 ; TYPE_PKMN_*, Basic
db $d4, $02 ; TYPE_PKMN_*, Stage 1
db $d8, $01 ; TYPE_PKMN_*, Stage 2
db $dc, $02 ; TYPE_TRAINER
; copy the name and level of the card at wLoadedCard1 to wDefaultText
; a = length in number of tiles (the resulting string will be padded with spaces to match it)
CopyCardNameAndLevel: ; 29f5 (0:29f5)
farcall _CopyCardNameAndLevel
ret
; sets cursor parameters for navigating in a text box, but using
; default values for the cursor tile (SYM_CURSOR_R) and the tile behind it (SYM_SPACE).
; d,e: coordinates of the cursor
SetCursorParametersForTextBox_Default: ; 29fa (0:29fa)
lb bc, SYM_CURSOR_R, SYM_SPACE ; cursor tile, tile behind cursor
call SetCursorParametersForTextBox
; fallthrough
; wait until A or B is pressed.
; return carry if A is pressed, nc if B is pressed. erase the cursor either way
WaitForButtonAorB: ; 2a00 (0:2a00)
call DoFrame
call RefreshMenuCursor
ldh a, [hKeysPressed]
bit A_BUTTON_F, a
jr nz, .a_pressed
bit B_BUTTON_F, a
jr z, WaitForButtonAorB
call EraseCursor
scf
ret
.a_pressed
call EraseCursor
or a
ret
; sets cursor parameters for navigating in a text box
; d,e: coordinates of the cursor
; b,c: tile numbers of the cursor and of the tile behind it
SetCursorParametersForTextBox: ; 2a1a (0:2a1a)
xor a
ld hl, wCurMenuItem
ld [hli], a
ld [hl], d ; wCursorXPosition
inc hl
ld [hl], e ; wCursorYPosition
inc hl
ld [hl], 0 ; wYDisplacementBetweenMenuItems
inc hl
ld [hl], 1 ; wNumMenuItems
inc hl
ld [hl], b ; wCursorTile
inc hl
ld [hl], c ; wTileBehindCursor
ld [wCursorBlinkCounter], a
ret
; draw a 20x6 text box aligned to the bottom of the screen,
; print the text at hl without letter delay, and wait for A or B pressed
DrawWideTextBox_PrintTextNoDelay_Wait: ; 2a30 (0:2a30)
call DrawWideTextBox_PrintTextNoDelay
jp WaitForWideTextBoxInput
; draw a 20x6 text box aligned to the bottom of the screen
; and print the text at hl without letter delay
DrawWideTextBox_PrintTextNoDelay: ; 2a36 (0:2a36)
push hl
call DrawWideTextBox
ld a, 19
jr DrawTextBox_PrintTextNoDelay
; draw a 12x6 text box aligned to the bottom left of the screen
; and print the text at hl without letter delay
DrawNarrowTextBox_PrintTextNoDelay: ; 2a3e (0:2a3e)
push hl
call DrawNarrowTextBox
ld a, 11
; fallthrough
DrawTextBox_PrintTextNoDelay: ; 2a44 (0:2a44)
lb de, 1, 14
call AdjustCoordinatesForBGScroll
call InitTextPrintingInTextbox
pop hl
ld a, l
or h
jp nz, PrintTextNoDelay
ld hl, wDefaultText
jp ProcessText
; draw a 20x6 text box aligned to the bottom of the screen
; and print the text at hl with letter delay
DrawWideTextBox_PrintText: ; 2a59 (0:2a59)
push hl
call DrawWideTextBox
ld a, 19
lb de, 1, 14
call AdjustCoordinatesForBGScroll
call InitTextPrintingInTextbox
call EnableLCD
pop hl
jp PrintText
; draw a 12x6 text box aligned to the bottom left of the screen
DrawNarrowTextBox: ; 2a6f (0:2a6f)
lb de, 0, 12
lb bc, 12, 6
call AdjustCoordinatesForBGScroll
call DrawRegularTextBox
ret
; draw a 12x6 text box aligned to the bottom left of the screen,
; print the text at hl without letter delay, and wait for A or B pressed
DrawNarrowTextBox_WaitForInput: ; 2a7c (0:2a7c)
call DrawNarrowTextBox_PrintTextNoDelay
xor a
ld hl, NarrowTextBoxMenuParameters
call InitializeMenuParameters
call EnableLCD
.wait_A_or_B_loop
call DoFrame
call RefreshMenuCursor
ldh a, [hKeysPressed]
and A_BUTTON | B_BUTTON
jr z, .wait_A_or_B_loop
ret
NarrowTextBoxMenuParameters: ; 2a96 (0:2a96)
db 10, 17 ; corsor x, cursor y
db 1 ; y displacement between items
db 1 ; number of items
db SYM_CURSOR_D ; cursor tile number
db SYM_BOX_BOTTOM ; tile behind cursor
dw $0000 ; function pointer if non-0
; draw a 20x6 text box aligned to the bottom of the screen
DrawWideTextBox: ; 2a9e (0:2a9e)
lb de, 0, 12
lb bc, 20, 6
call AdjustCoordinatesForBGScroll
call DrawRegularTextBox
ret
; draw a 20x6 text box aligned to the bottom of the screen,
; print the text at hl with letter delay, and wait for A or B pressed
DrawWideTextBox_WaitForInput: ; 2aab (0:2aab)
call DrawWideTextBox_PrintText
; fallthrough
; wait for A or B to be pressed on a wide (20x6) text box
WaitForWideTextBoxInput: ; 2aae (0:2aae)
xor a
ld hl, WideTextBoxMenuParameters
call InitializeMenuParameters
call EnableLCD
.wait_A_or_B_loop
call DoFrame
call RefreshMenuCursor
ldh a, [hKeysPressed]
and A_BUTTON | B_BUTTON
jr z, .wait_A_or_B_loop
call EraseCursor
ret
WideTextBoxMenuParameters: ; 2ac8 (0:2ac8)
db 18, 17 ; cursor x, cursor y
db 1 ; y displacement between items
db 1 ; number of items
db SYM_CURSOR_D ; cursor tile number
db SYM_BOX_BOTTOM ; tile behind cursor
dw $0000 ; function pointer if non-0
; display a two-item horizontal menu with custom text provided in hl and handle input
TwoItemHorizontalMenu: ; 2ad0 (0:2ad0)
call DrawWideTextBox_PrintText
lb de, 6, 16 ; x, y
ld a, d
ld [wLeftmostItemCursorX], a
lb bc, SYM_CURSOR_R, SYM_SPACE ; cursor tile, tile behind cursor
call SetCursorParametersForTextBox
ld a, 1
ld [wCurMenuItem], a
call EnableLCD
jp HandleYesOrNoMenu.refresh_menu
YesOrNoMenuWithText_SetCursorToYes: ; 2aeb (0:2aeb)
ld a, $01
ld [wDefaultYesOrNo], a
; fallthrough
; display a yes / no menu in a 20x8 textbox with custom text provided in hl and handle input
; wDefaultYesOrNo determines whether the cursor initially points to YES or to NO
; returns carry if "no" selected
YesOrNoMenuWithText: ; 2af0 (0:2af0)
call DrawWideTextBox_PrintText
; fallthrough
; prints the YES / NO menu items at coordinates x,y = 7,16 and handles input
; input: wDefaultYesOrNo. returns carry if "no" selected
YesOrNoMenu: ; 2af3 (0:2af3)
lb de, 7, 16 ; x, y
call PrintYesOrNoItems
lb de, 6, 16 ; x, y
jr HandleYesOrNoMenu
; prints the YES / NO menu items at coordinates x,y = 3,16 and handles input
; input: wDefaultYesOrNo. returns carry if "no" selected
YesOrNoMenuWithText_LeftAligned: ; 2afe (0:2afe)
call DrawNarrowTextBox_PrintTextNoDelay
lb de, 3, 16 ; x, y
call PrintYesOrNoItems
lb de, 2, 16 ; x, y
; fallthrough
HandleYesOrNoMenu:
ld a, d
ld [wLeftmostItemCursorX], a
lb bc, SYM_CURSOR_R, SYM_SPACE ; cursor tile, tile behind cursor
call SetCursorParametersForTextBox
ld a, [wDefaultYesOrNo]
ld [wCurMenuItem], a
call EnableLCD
jr .refresh_menu
.wait_button_loop
call DoFrame
call RefreshMenuCursor
ldh a, [hKeysPressed]
bit A_BUTTON_F, a
jr nz, .a_pressed
ldh a, [hDPadHeld]
and D_RIGHT | D_LEFT
jr z, .wait_button_loop
; left or right pressed, so switch to the other menu item
ld a, SFX_01
call PlaySFX
call EraseCursor
.refresh_menu
ld a, [wLeftmostItemCursorX]
ld c, a
; default to the second option (NO)
ld hl, wCurMenuItem
ld a, [hl]
xor $1
ld [hl], a
; x separation between left and right items is 4 tiles
add a
add a
add c
ld [wCursorXPosition], a
xor a
ld [wCursorBlinkCounter], a
jr .wait_button_loop
.a_pressed
ld a, [wCurMenuItem]
ldh [hCurMenuItem], a
or a
jr nz, .no
;.yes
ld [wDefaultYesOrNo], a ; 0
ret
.no
xor a
ld [wDefaultYesOrNo], a ; 0
ld a, 1
ldh [hCurMenuItem], a
scf
ret
; prints "YES NO" at de
PrintYesOrNoItems: ; 2b66 (0:2b66)
call AdjustCoordinatesForBGScroll
ldtx hl, YesOrNoText
call InitTextPrinting_ProcessTextFromID
ret
ContinueDuel: ; 2b70 (0:2b70)
ld a, BANK(_ContinueDuel)
call BankswitchROM
jp _ContinueDuel
; loads opponent deck at wOpponentDeckID to wOpponentDeck, and initializes wPlayerDuelistType.
; on a duel against Sam, also loads PRACTICE_PLAYER_DECK to wPlayerDeck.
; also, sets wRNG1, wRNG2, and wRNGCounter to $57.
LoadOpponentDeck: ; 2b78 (0:2b78)
xor a
ld [wIsPracticeDuel], a
ld a, [wOpponentDeckID]
cp SAMS_NORMAL_DECK_ID
jr z, .normal_sam_duel
or a ; cp SAMS_PRACTICE_DECK_ID
jr nz, .not_practice_duel
; only practice duels will display help messages, but
; any duel with Sam will force the PRACTICE_PLAYER_DECK
;.practice_sam_duel
inc a
ld [wIsPracticeDuel], a
.normal_sam_duel
xor a
ld [wOpponentDeckID], a
call SwapTurn
ld a, PRACTICE_PLAYER_DECK
call LoadDeck
call SwapTurn
ld hl, wRNG1
ld a, $57
ld [hli], a
ld [hli], a
ld [hl], a
xor a
.not_practice_duel
inc a
inc a ; convert from *_DECK_ID constant read from wOpponentDeckID to *_DECK constant
call LoadDeck
ld a, [wOpponentDeckID]
cp DECKS_END
jr c, .valid_deck
ld a, PRACTICE_PLAYER_DECK_ID
ld [wOpponentDeckID], a
.valid_deck
; set opponent as controlled by AI
ld a, DUELVARS_DUELIST_TYPE
call GetTurnDuelistVariable
ld a, [wOpponentDeckID]
or DUELIST_TYPE_AI_OPP
ld [hl], a
ret
AIDoAction_Turn: ; 2bbf (0:2bbf)
ld a, AIACTION_DO_TURN
jr AIDoAction
AIDoAction_StartDuel: ; 2bc3 (0:2bc3)
ld a, AIACTION_START_DUEL
jr AIDoAction
AIDoAction_ForcedSwitch: ; 2bc7 (0:2bc7)
ld a, AIACTION_FORCED_SWITCH
call AIDoAction
ldh [hTempPlayAreaLocation_ff9d], a
ret
AIDoAction_KOSwitch: ; 2bcf (0:2bcf)
ld a, AIACTION_KO_SWITCH
call AIDoAction
ldh [hTemp_ffa0], a
ret
AIDoAction_TakePrize: ; 2bd7 (0:2bd7)
ld a, AIACTION_TAKE_PRIZE
jr AIDoAction ; this line is not needed
; calls the appropriate AI routine to handle action,
; depending on the deck ID (see engine/deck_ai/deck_ai.asm)
; input:
; - a = AIACTION_* constant
AIDoAction: ; 2bdb (0:2bdb)
ld c, a
; load bank for Opponent Deck pointer table
ldh a, [hBankROM]
push af
ld a, BANK(DeckAIPointerTable)
call BankswitchROM
; load hl with the corresponding pointer
ld a, [wOpponentDeckID]
ld l, a
ld h, $0
add hl, hl ; two bytes per deck
ld de, DeckAIPointerTable
add hl, de
ld a, [hli]
ld h, [hl]
ld l, a
ld a, c
or a
jr nz, .not_zero
; if input was 0, copy deck data of turn player
ld e, [hl]
inc hl
ld d, [hl]
call CopyDeckData
jr .done
; jump to corresponding AI routine related to input
.not_zero
call JumpToFunctionInTable
.done
ld c, a
pop af
call BankswitchROM
ld a, c
ret
; writes n items of text each given in the following format in hl:
; x coord, y coord, text id
; $ff-terminated
PlaceTextItems: ; 2c08 (0:2c08)
ld d, [hl] ; x coord
inc hl
bit 7, d
ret nz ; return if no more items of text
ld e, [hl] ; y coord
inc hl ; hl = text id
call InitTextPrinting
push hl
call ProcessTextFromPointerToID
pop hl
inc hl
inc hl
jr PlaceTextItems ; do next item
; like ProcessTextFromID, except it calls InitTextPrinting first
InitTextPrinting_ProcessTextFromID: ; 2c1b (0:2c1b)
call InitTextPrinting
jr ProcessTextFromID
; like ProcessTextFromPointerToID, except it calls InitTextPrinting first
InitTextPrinting_ProcessTextFromPointerToID: ; 2c20 (0:2c20)
call InitTextPrinting
; fallthrough
; like ProcessTextFromID below, except a memory address containing a text ID is
; provided in hl rather than the text ID directly.
ProcessTextFromPointerToID: ; 2c23 (0:2c23)
ld a, [hli]
or [hl]
ret z
ld a, [hld]
ld l, [hl]
ld h, a
; fallthrough
; given the ID of a text in hl, reads the characters from it processes them.
; loops until TX_END is found. ignores TX_RAM1, TX_RAM2, and TX_RAM3 characters.
; restores original ROM bank before returning.
ProcessTextFromID: ; 2c29 (0:2c29)
ldh a, [hBankROM]
push af
call GetTextOffsetFromTextID
call ProcessText
pop af
call BankswitchROM
ret
; return, in a, the number of lines of the text given in hl as an ID
; this is calculated by counting the amount of '\n' characters and adding 1 to the result
CountLinesOfTextFromID: ; 2c37 (0:2c37)
push hl
push de
push bc
ldh a, [hBankROM]
push af
call GetTextOffsetFromTextID
ld c, $00
.char_loop
ld a, [hli]
or a ; TX_END
jr z, .end
cp TX_CTRL_END
jr nc, .char_loop
cp TX_HALFWIDTH
jr c, .skip
cp "\n"
jr nz, .char_loop
inc c
jr .char_loop
.skip
inc hl
jr .char_loop
.end
pop af
call BankswitchROM
ld a, c
inc a
pop bc
pop de
pop hl
ret
; call PrintScrollableText with text box label, then wait for the
; player to press A or B to advance the printed text
PrintScrollableText_WithTextBoxLabel: ; 2c62 (0:2c62)
call PrintScrollableText_WithTextBoxLabel_NoWait
jr WaitForPlayerToAdvanceText
PrintScrollableText_WithTextBoxLabel_NoWait: ; 2c67 (0:2c67)
push hl
ld hl, wTextBoxLabel
ld [hl], e
inc hl
ld [hl], d
pop hl
ld a, $01
jr PrintScrollableText
; call PrintScrollableText with no text box label, then wait for the
; player to press A or B to advance the printed text
PrintScrollableText_NoTextBoxLabel: ; 2c73 (0:2c73)
xor a
call PrintScrollableText
; fallthrough
; when a text box is full or the text is over, prompt the player to
; press A or B in order to clear the text and print the next lines.
WaitForPlayerToAdvanceText: ; 2c77 (0:2c77)
lb bc, SYM_CURSOR_D, SYM_BOX_BOTTOM ; cursor tile, tile behind cursor
lb de, 18, 17 ; x, y
call SetCursorParametersForTextBox
call WaitForButtonAorB
ret
; draws a text box, and prints the text with id at hl, with letter delay. unlike PrintText,
; PrintScrollableText also supports scrollable text, and prompts the user to press A or B
; to advance the page or close the text. register a determines whether the textbox is
; labeled or not. if labeled, the text id of the label is provided in wTextBoxLabel.
; PrintScrollableText is used mostly for overworld NPC text.
PrintScrollableText: ; 2c84 (0:2c84)
ld [wIsTextBoxLabeled], a
ldh a, [hBankROM]
push af
call GetTextOffsetFromTextID
call DrawTextReadyLabeledOrRegularTextBox
call ResetTxRam_WriteToTextHeader
.print_char_loop
ld a, [wTextSpeed]
ld c, a
inc c
jr .go
.nonzero_text_speed
ld a, [wTextSpeed]
cp 2
jr nc, .apply_delay
; if text speed is 1, pressing b ignores it
ldh a, [hKeysHeld]
and B_BUTTON
jr nz, .skip_delay
.apply_delay
push bc
call DoFrame
pop bc
.go
dec c
jr nz, .nonzero_text_speed
.skip_delay
call ProcessTextHeader
jr c, .asm_2cc3
ld a, [wCurTextLine]
cp 3
jr c, .print_char_loop
; two lines of text already printed, so need to advance text
call WaitForPlayerToAdvanceText
call DrawTextReadyLabeledOrRegularTextBox
jr .print_char_loop
.asm_2cc3
pop af
call BankswitchROM
ret
; zero wWhichTextHeader, wWhichTxRam2 and wWhichTxRam3, and set hJapaneseSyllabary to TX_KATAKANA
; fill wTextHeader1 with TX_KATAKANA, wFontWidth, hBankROM, and register bc for the text's pointer.
ResetTxRam_WriteToTextHeader: ; 2cc8 (0:2cc8)
xor a
ld [wWhichTextHeader], a
ld [wWhichTxRam2], a
ld [wWhichTxRam3], a
ld a, TX_KATAKANA
ld [hJapaneseSyllabary], a
; fallthrough
; fill the wTextHeader specified in wWhichTextHeader (0-3) with hJapaneseSyllabary,
; wFontWidth, hBankROM, and register bc for the text's pointer.
WriteToTextHeader: ; 2cd7 (0:2cd7)
push hl
call GetPointerToTextHeader
pop bc
ld a, [hJapaneseSyllabary]
ld [hli], a
ld a, [wFontWidth]
ld [hli], a
ldh a, [hBankROM]
ld [hli], a
ld [hl], c
inc hl
ld [hl], b
ret
; same as WriteToTextHeader, except it then increases wWhichTextHeader to
; set the next text header to the current one (usually, because
; it will soon be written to due to a TX_RAM command).
WriteToTextHeader_MoveToNext: ; 2ceb (0:2ceb)
call WriteToTextHeader
ld hl, wWhichTextHeader
inc [hl]
ret
; read the wTextHeader specified in wWhichTextHeader (0-3) and use the data to
; populate the corresponding memory addresses. also switch to the text's rombank
; and return the address of the next character in hl.
ReadTextHeader: ; 2cf3 (0:2cf3)
call GetPointerToTextHeader
ld a, [hli]
ld [hJapaneseSyllabary], a
ld a, [hli]
ld [wFontWidth], a
ld a, [hli]
call BankswitchROM
ld a, [hli]
ld h, [hl]
ld l, a
ret
; return in hl, the address of the wTextHeader specified in wWhichTextHeader (0-3)
GetPointerToTextHeader: ; 2d06 (0:2d06)
ld a, [wWhichTextHeader]
ld e, a
add a
add a
add e
ld e, a
ld d, $0
ld hl, wTextHeader1
add hl, de
ret
; draws a wide text box with or without label depending on wIsTextBoxLabeled
; if labeled, the label's text ID is provided in wTextBoxLabel
; calls InitTextPrintingInTextbox after drawing the text box
DrawTextReadyLabeledOrRegularTextBox: ; 2d15 (0:2d15)
push hl
lb de, 0, 12
lb bc, 20, 6
call AdjustCoordinatesForBGScroll
ld a, [wIsTextBoxLabeled]
or a
jr nz, .labeled
call DrawRegularTextBox
call EnableLCD
jr .init_text
.labeled
ld hl, wTextBoxLabel
ld a, [hli]
ld h, [hl]
ld l, a
call DrawLabeledTextBox
.init_text
lb de, 1, 14
call AdjustCoordinatesForBGScroll
ld a, 19
call InitTextPrintingInTextbox
pop hl
ret
; reads the incoming character from the current wTextHeader and processes it
; then updates the current wTextHeader to point to the next character.
; a TX_RAM command causes a switch to a wTextHeader in the level below, and a TX_END
; command terminates the text unless there is a pending wTextHeader in the above level.
ProcessTextHeader: ; 2d43 (0:2d43)
call ReadTextHeader
ld a, [hli]
or a ; TX_END
jr z, .tx_end
cp TX_CTRL_START
jr c, .character_pair
cp TX_CTRL_END
jr nc, .character_pair
call ProcessSpecialTextCharacter
jr nc, .processed_char
cp TX_RAM1
jr z, .tx_ram1
cp TX_RAM2
jr z, .tx_ram2
cp TX_RAM3
jr z, .tx_ram3
jr .processed_char
.character_pair
ld e, a ; first char
ld d, [hl] ; second char
call ClassifyTextCharacterPair
jr nc, .not_tx_fullwidth
inc hl
.not_tx_fullwidth
call Func_22ca
xor a
call ProcessSpecialTextCharacter
.processed_char
call WriteToTextHeader
or a
ret
.tx_end
ld a, [wWhichTextHeader]
or a
jr z, .no_more_text
; handle text header in the above level
dec a
ld [wWhichTextHeader], a
jr ProcessTextHeader
.no_more_text
call TerminateHalfWidthText
scf
ret
.tx_ram2
call WriteToTextHeader_MoveToNext
ld a, TX_KATAKANA
ld [hJapaneseSyllabary], a
xor a ; FULL_WIDTH
ld [wFontWidth], a
ld de, wTxRam2
ld hl, wWhichTxRam2
call HandleTxRam2Or3
ld a, l
or h
jr z, .empty
call GetTextOffsetFromTextID
call WriteToTextHeader
jr ProcessTextHeader
.empty
ld hl, wDefaultText
call WriteToTextHeader
jr ProcessTextHeader
.tx_ram3
call WriteToTextHeader_MoveToNext
ld de, wTxRam3
ld hl, wWhichTxRam3
call HandleTxRam2Or3
call TwoByteNumberToText_CountLeadingZeros
call WriteToTextHeader
jp ProcessTextHeader
.tx_ram1
call WriteToTextHeader_MoveToNext
call CopyPlayerNameOrTurnDuelistName
ld a, [wStringBuffer]
cp TX_HALFWIDTH
jr z, .tx_halfwidth
ld a, TX_HALF2FULL
call ProcessSpecialTextCharacter
.tx_halfwidth
call WriteToTextHeader
jp ProcessTextHeader
; input:
; de: wTxRam2 or wTxRam3
; hl: wWhichTxRam2 or wWhichTxRam3
; return, in hl, the contents of the contents of the
; wTxRam* buffer's current entry, and increment wWhichTxRam*.
HandleTxRam2Or3: ; 2de0 (0:2de0)
push de
ld a, [hl]
inc [hl]
add a
ld e, a
ld d, $0
pop hl
add hl, de
ld a, [hli]
ld h, [hl]
ld l, a
ret
; uses the two byte text id in hl to read the three byte text offset
; loads the correct bank for the specific text and returns the pointer in hl
GetTextOffsetFromTextID: ; 2ded (0:2ded)
push de
ld e, l
ld d, h
add hl, hl
add hl, de
set 6, h ; hl = (hl * 3) + $4000
ld a, BANK(TextOffsets)
call BankswitchROM
ld e, [hl]
inc hl
ld d, [hl]
inc hl
ld a, [hl]
ld h, d
rl h
rla
rl h
rla
add BANK(TextOffsets)
call BankswitchROM
res 7, d
set 6, d ; $4000 ≤ de ≤ $7fff
ld l, e
ld h, d
pop de
ret
; if [wFontWidth] == HALF_WIDTH:
; convert the number at hl to text (ascii) format and write it to wStringBuffer
; return c = 4 - leading_zeros
; if [wFontWidth] == FULL_WIDTH:
; convert the number at hl to TX_SYMBOL text format and write it to wStringBuffer
; replace leading zeros with SYM_SPACE
TwoByteNumberToText_CountLeadingZeros: ; 2e12 (0:2e12)
ld a, [wFontWidth]
or a ; FULL_WIDTH
jp z, TwoByteNumberToTxSymbol_TrimLeadingZeros
ld de, wStringBuffer
push de
call TwoByteNumberToText
pop hl
ld c, 4
.digit_loop
ld a, [hl]
cp "0"
ret nz
inc hl
dec c
jr nz, .digit_loop
ret
; in the overworld: copy the player's name to wStringBuffer
; in a duel: copy the name of the duelist whose turn it is to wStringBuffer
CopyPlayerNameOrTurnDuelistName: ; 2e2c (0:2e2c)
ld de, wStringBuffer
push de
ldh a, [hWhoseTurn]
cp OPPONENT_TURN
jp z, .opponent_turn
call CopyPlayerName
pop hl
ret
.opponent_turn
call CopyOpponentName
pop hl
ret
; prints text with id at hl, with letter delay, in a textbox area.
; the text must fit in the textbox; PrintScrollableText should be used instead.
PrintText: ; 2e41 (0:2e41)
ld a, l
or h
jr z, .from_ram
ldh a, [hBankROM]
push af
call GetTextOffsetFromTextID
call .print_text
pop af
call BankswitchROM
ret
.from_ram
ld hl, wDefaultText
.print_text
call ResetTxRam_WriteToTextHeader
.next_tile_loop
ldh a, [hKeysHeld]
ld b, a
ld a, [wTextSpeed]
inc a
cp 3
jr nc, .apply_delay
; if text speed is 1, pressing b ignores it
bit B_BUTTON_F, b
jr nz, .skip_delay
jr .apply_delay
.text_delay_loop
; wait a number of frames equal to [wTextSpeed] between printing each text tile
call DoFrame
.apply_delay
dec a
jr nz, .text_delay_loop
.skip_delay
call ProcessTextHeader
jr nc, .next_tile_loop
ret
; prints text with id at hl, without letter delay, in a textbox area.
; the text must fit in the textbox; PrintScrollableText should be used instead.
PrintTextNoDelay: ; 2e76 (0:2e76)
ldh a, [hBankROM]
push af
call GetTextOffsetFromTextID
call ResetTxRam_WriteToTextHeader
.next_tile_loop
call ProcessTextHeader
jr nc, .next_tile_loop
pop af
call BankswitchROM
ret
; copies a text given its id at hl, to de
; if hl is 0, the name of the turn duelist is copied instead
CopyText: ; 2e89 (0:2e89)
ld a, l
or h
jr z, .special
ldh a, [hBankROM]
push af
call GetTextOffsetFromTextID
.next_tile_loop
ld a, [hli]
ld [de], a
inc de
or a
jr nz, .next_tile_loop
pop af
call BankswitchROM
dec de
ret
.special
ldh a, [hWhoseTurn]
cp OPPONENT_TURN
jp z, CopyOpponentName
jp CopyPlayerName
; copy text of maximum length a (in tiles) from its ID at hl to de,
; then terminate the text with TX_END if it doesn't contain it already.
; fill any remaining bytes with spaces plus TX_END to match the length specified in a.
; return the text's actual length in characters (i.e. before the first TX_END) in e.
CopyTextData_FromTextID: ; 2ea9 (0:2ea9)
ldh [hff96], a
ldh a, [hBankROM]
push af
call GetTextOffsetFromTextID
ldh a, [hff96]
call CopyTextData
pop af
call BankswitchROM
ret
; text id (usually of a card name) for TX_RAM2
LoadTxRam2: ; 2ebb (0:2ebb)
ld a, l
ld [wTxRam2], a
ld a, h
ld [wTxRam2 + 1], a
ret
; a number between 0 and 65535 for TX_RAM3
LoadTxRam3: ; 2ec4 (0:2ec4)
ld a, l
ld [wTxRam3], a
ld a, h
ld [wTxRam3 + 1], a
ret
; load data of card with text id of name at de to wLoadedCard1
LoadCardDataToBuffer1_FromName: ; 2ecd (0:2ecd)
ld hl, CardPointers + 2 ; skip first $0000 pointer
ld a, BANK(CardPointers)
call BankpushROM2
.find_card_loop
ld a, [hli]
or [hl]
jr z, .done
push hl
ld a, [hld]
ld l, [hl]
ld h, a
ld a, BANK(CardPointers)
call BankpushROM2
ld bc, CARD_DATA_NAME
add hl, bc
ld a, [hli]
cp e
jr nz, .no_match
ld a, [hl]
cp d
.no_match
pop hl
pop hl
inc hl
jr nz, .find_card_loop
dec hl
ld a, [hld]
ld l, [hl]
ld h, a
ld a, BANK(CardPointers)
call BankpushROM2
ld de, wLoadedCard1
ld b, PKMN_CARD_DATA_LENGTH
.copy_card_loop
ld a, [hli]
ld [de], a
inc de
dec b
jr nz, .copy_card_loop
pop hl
.done
call BankpopROM
ret
; load data of card with id at e to wLoadedCard2
LoadCardDataToBuffer2_FromCardID: ; 2f0a (0:2f0a)
push hl
ld hl, wLoadedCard2
jr LoadCardDataToHL_FromCardID
; load data of card with id at e to wLoadedCard1
LoadCardDataToBuffer1_FromCardID: ; 2f10 (0:2f10)
push hl
ld hl, wLoadedCard1
; fallthrough
LoadCardDataToHL_FromCardID: ; 2f14 (0:2f14)
push de
push bc
push hl
call GetCardPointer
pop de
jr c, .done
ld a, BANK(CardPointers)
call BankpushROM2
ld b, PKMN_CARD_DATA_LENGTH
.copy_card_data_loop
ld a, [hli]
ld [de], a
inc de
dec b
jr nz, .copy_card_data_loop
call BankpopROM
or a
.done
pop bc
pop de
pop hl
ret
; return in a the type (TYPE_* constant) of the card with id at e
GetCardType: ; 2f32 (0:2f32)
push hl
call GetCardPointer
jr c, .done
ld a, BANK(CardPointers)
call BankpushROM2
ld l, [hl]
call BankpopROM
ld a, l
or a
.done
pop hl
ret
; return in a the 2-byte text id of the name of the card with id at e
GetCardName: ; 2f45 (0:2f45)
push hl
call GetCardPointer
jr c, .done
ld a, BANK(CardPointers)
call BankpushROM2
ld de, CARD_DATA_NAME
add hl, de
ld e, [hl]
inc hl
ld d, [hl]
call BankpopROM
or a
.done
pop hl
ret
; from the card id in a, returns type into a, rarity into b, and set into c
GetCardTypeRarityAndSet: ; 2f5d (0:2f5d)
push hl
push de
ld d, 0
ld e, a
call GetCardPointer
jr c, .done
ld a, BANK(CardPointers)
call BankpushROM2
ld e, [hl] ; CARD_DATA_TYPE
ld bc, CARD_DATA_RARITY
add hl, bc
ld b, [hl] ; CARD_DATA_RARITY
inc hl
ld c, [hl] ; CARD_DATA_SET
call BankpopROM
ld a, e
or a
.done
pop de
pop hl
ret
; return at hl the pointer to the data of the card with id at e
; return carry if e was out of bounds, so no pointer was returned
GetCardPointer: ; 2f7c (0:2f7c)
push de
push bc
ld l, e
ld h, $0
add hl, hl
ld bc, CardPointers
add hl, bc
ld a, h
cp HIGH(CardPointers + 2 + (2 * NUM_CARDS))
jr nz, .nz
ld a, l
cp LOW(CardPointers + 2 + (2 * NUM_CARDS))
.nz
ccf
jr c, .out_of_bounds
ld a, BANK(CardPointers)
call BankpushROM2
ld a, [hli]
ld h, [hl]
ld l, a
call BankpopROM
or a
.out_of_bounds
pop bc
pop de
ret
; input:
; hl = card_gfx_index
; de = where to load the card gfx to
; bc are supposed to be $30 (number of tiles of a card gfx) and TILE_SIZE respectively
; card_gfx_index = (<Name>CardGfx - CardGraphics) / 8 (using absolute ROM addresses)
; also copies the card's palette to wCardPalette
LoadCardGfx: ; 2fa0 (0:2fa0)
ldh a, [hBankROM]
push af
push hl
; first, get the bank with the card gfx is at
srl h
srl h
srl h
ld a, BANK(CardGraphics)
add h
call BankswitchROM
pop hl
; once we have the bank, get the pointer: multiply by 8 and discard the bank offset
add hl, hl
add hl, hl
add hl, hl
res 7, h
set 6, h ; $4000 ≤ hl ≤ $7fff
call CopyGfxData
ld b, CGB_PAL_SIZE
ld de, wCardPalette
.copy_card_palette
ld a, [hli]
ld [de], a
inc de
dec b
jr nz, .copy_card_palette
pop af
call BankswitchROM
ret
; identical to CopyFontsOrDuelGraphicsTiles
CopyFontsOrDuelGraphicsTiles2: ; 2fcb (0:2fcb)
ld a, BANK(Fonts); BANK(DuelGraphics)
call BankpushROM
ld c, TILE_SIZE
call CopyGfxData
call BankpopROM
ret
; Checks if the command type at a is one of the commands of the move or
; card effect currently in use, and executes its associated function if so.
; input:
; a = command type to check
; [wLoadedMoveEffectCommands] = pointer to list of commands of current move or trainer card
TryExecuteEffectCommandFunction: ; 2fd9 (0:2fd9)
push af
; grab pointer to command list from wLoadedMoveEffectCommands
ld hl, wLoadedMoveEffectCommands
ld a, [hli]
ld h, [hl]
ld l, a
pop af
call CheckMatchingCommand
jr nc, .execute_function
; return if no matching command was found
or a
ret
.execute_function
; execute the function at [wce22]:hl
ldh a, [hBankROM]
push af
ld a, [wce22]
call BankswitchROM
or a
call CallHL
push af
; restore original bank and return
pop bc
pop af
call BankswitchROM
push bc
pop af
ret
; input:
; a = command type to check
; hl = list of commands of current move or trainer card
; return nc if command type matching a is found, carry otherwise
CheckMatchingCommand: ; 2ffe (0:2ffe)
ld c, a
ld a, l
or h
jr nz, .not_null_pointer
; return carry if pointer is $0000
scf
ret
.not_null_pointer
ldh a, [hBankROM]
push af
ld a, BANK(EffectCommands)
call BankswitchROM
; store the bank number of command functions ($b) in wce22
ld a, $b
ld [wce22], a
.check_command_loop
ld a, [hli]
or a
jr z, .no_more_commands
cp c
jr z, .matching_command_found
; skip function pointer for this command and move to the next one
inc hl
inc hl
jr .check_command_loop
.matching_command_found
; load function pointer for this command
ld a, [hli]
ld h, [hl]
ld l, a
; restore bank and return nc
pop af
call BankswitchROM
or a
ret
.no_more_commands
; restore bank and return c
pop af
call BankswitchROM
scf
ret
; loads the deck id in a from DeckPointers and copies it to wPlayerDeck or to
; wOpponentDeck, depending on whose turn it is.
; sets carry flag if an invalid deck id is used.
LoadDeck: ; 302c (0:302c)
push hl
ld l, a
ld h, $0
ldh a, [hBankROM]
push af
ld a, BANK(DeckPointers)
call BankswitchROM
add hl, hl
ld de, DeckPointers
add hl, de
ld e, [hl]
inc hl
ld d, [hl]
ld a, d
or e
jr z, .null_pointer
call CopyDeckData
pop af
call BankswitchROM
pop hl
or a
ret
.null_pointer
pop af
call BankswitchROM
pop hl
scf
ret
; [wDamage] += a
AddToDamage: ; 3055 (0:3055)
push hl
ld hl, wDamage
add [hl]
ld [hli], a
ld a, 0
adc [hl]
ld [hl], a
pop hl
ret
; [wDamage] -= a
SubstractFromDamage: ; 3061 (0:3061)
push de
push hl
ld e, a
ld hl, wDamage
ld a, [hl]
sub e
ld [hli], a
ld a, [hl]
sbc 0
ld [hl], a
pop hl
pop de
ret
; function that executes one or more consecutive coin tosses during a duel (a = number of coin tosses),
; displaying each result ([O] or [X]) starting from the top left corner of the screen.
; text at de is printed in a text box during the coin toss.
; returns: the number of heads in a and in wCoinTossNumHeads, and carry if at least one heads
TossCoinATimes: ; 3071 (0:3071)
push hl
ld hl, wCoinTossScreenTextID
ld [hl], e
inc hl
ld [hl], d
bank1call _TossCoin
pop hl
ret
; function that executes a single coin toss during a duel.
; text at de is printed in a text box during the coin toss.
; returns: - carry, and 1 in a and in wCoinTossNumHeads if heads
; - nc, and 0 in a and in wCoinTossNumHeads if tails
TossCoin: ; 307d (0:307d)
push hl
ld hl, wCoinTossScreenTextID
ld [hl], e
inc hl
ld [hl], d
ld a, 1
bank1call _TossCoin
ld hl, wDuelDisplayedScreen
ld [hl], 0
pop hl
ret
; cp de, bc
CompareDEtoBC: ; 3090 (0:3090)
ld a, d
cp b
ret nz
ld a, e
cp c
ret
OpenDuelCheckMenu: ; 3096 (0:3096)
ldh a, [hBankROM]
push af
ld a, BANK(_OpenDuelCheckMenu)
call BankswitchROM
call _OpenDuelCheckMenu
pop af
call BankswitchROM
ret
OpenInPlayAreaScreen_FromSelectButton: ; 30a6 (0:30a6)
ldh a, [hBankROM]
push af
ld a, BANK(OpenInPlayAreaScreen)
call BankswitchROM
ld a, $1
ld [wInPlayAreaFromSelectButton], a
call OpenInPlayAreaScreen
pop bc
ld a, b
call BankswitchROM
ret
; loads tiles and icons to display Your Play Area / Opp. Play Area screen,
; and draws the screen according to the turn player
; input: h -> [wCheckMenuPlayAreaWhichDuelist] and l -> [wCheckMenuPlayAreaWhichLayout]
; similar to DrawYourOrOppPlayArea (bank 2) except it also draws a wide text box.
; this is because bank 2's DrawYourOrOppPlayArea is supposed to come from the Check Menu,
; so the text box is always already there.
DrawYourOrOppPlayAreaScreen_Bank0: ; 30bc (0:30bc)
ld a, h
ld [wCheckMenuPlayAreaWhichDuelist], a
ld a, l
ld [wCheckMenuPlayAreaWhichLayout], a
ldh a, [hBankROM]
push af
ld a, BANK(_DrawYourOrOppPlayAreaScreen)
call BankswitchROM
call _DrawYourOrOppPlayAreaScreen
call DrawWideTextBox
pop af
call BankswitchROM
ret
DrawPlayersPrizeAndBenchCards: ; 30d7 (0:30d7)
ldh a, [hBankROM]
push af
ld a, BANK(_DrawPlayersPrizeAndBenchCards)
call BankswitchROM
call _DrawPlayersPrizeAndBenchCards
pop af
call BankswitchROM
ret
Func_30e7: ; 30e7 (0:30e7)
ldh a, [hBankROM]
push af
ld a, BANK(Func_8764)
call BankswitchROM
call Func_8764
ld b, a
pop af
call BankswitchROM
ld a, b
ret
Func_30f9: ; 30f9 (0:30f9)
ld b, a
ldh a, [hBankROM]
push af
ld a, BANK(Func_8932)
call BankswitchROM
call Func_8932
pop af
call BankswitchROM
ret
Func_310a: ; 310a (0:310a)
ld [wce59], a
ldh a, [hBankROM]
push af
ld a, BANK(Func_8aaa)
call BankswitchROM
call Func_8aaa
pop af
call BankswitchROM
ret
Func_311d: ; 311d (0:311d)
ldh a, [hBankROM]
push af
ld a, BANK(Func_8b85)
call BankswitchROM
call Func_8b85
pop af
call BankswitchROM
ret
; serial transfer-related
Func_312d: ; 312d (0:312d)
push hl
ld hl, wce64
ld a, $88
ld [hli], a ; [wce64] ← $88
ld a, $33
ld [hli], a ; [wce65] ← $33
ld [hl], d ; [wce66] ← d
inc hl
ld [hl], e ; [wce67] ← e
inc hl
ld [hl], c ; [wce68] ← c
inc hl
ld [hl], b ; [wce69] ← b
inc hl
pop de
ld [hl], e ; [wce6a] ← l
inc hl
ld [hl], d ; [wce6b] ← h
inc hl
ld de, $ff45
ld [hl], e ; [wce6c] ← $45
inc hl
ld [hl], d ; [wce6d] ← $ff
ld hl, wce70
ld [hl], $64 ; [wce70] ← $64
inc hl
ld [hl], $ce ; [wce71] ← $ce
call Func_0e8e
ld a, $1
ld [wce63], a ; [wce63] ← 1
call Func_31fc
.asm_315d
call DoFrame
ld a, [wce63]
or a
jr nz, .asm_315d
call ResetSerial
ld bc, $05dc
.asm_316c
dec bc
ld a, b
or c
jr nz, .asm_316c
ld a, [wce6e]
cp $81
jr nz, .asm_3182
ld a, [wce6f]
ld l, a
and $f1
ld a, l
ret z
scf
ret
.asm_3182
ld a, $ff
ld [wce6f], a
scf
ret
Func_3189: ; 3189 (0:3189)
ld hl, PointerTable_3190
dec a
jp JumpToFunctionInTable
PointerTable_3190: ; 3190 (0:3190)
dw Func_31a8
dw Func_31a8
dw Func_31a8
dw Func_31a8
dw Func_31a8
dw Func_31b0
dw Func_31ca
dw Func_31dd
dw Func_31e5
dw Func_31ef
dw Func_31ea
dw Func_31f2
Func_31a8: ; 31a8 (0:31a8)
call Func_31fc
Func_31ab: ; 31ab (0:31ab)
ld hl, wce63
inc [hl]
ret
Func_31b0: ; 31b0 (0:31b0)
call Func_31ab
ld hl, wce68
ld a, [hli]
or [hl]
jr nz, .asm_31bf
call Func_31ab
jr Func_31dd
.asm_31bf
ld hl, wce6a
ld de, wce70
ld a, [hli]
ld [de], a
inc de
ld a, [hl]
ld [de], a
Func_31ca: ; 31ca (0:31ca)
call Func_31fc
ld hl, wce68
ld a, [hl]
dec [hl]
or a
jr nz, .asm_31d8
inc hl
dec [hl]
dec hl
.asm_31d8
ld a, [hli]
or [hl]
jr z, Func_31ab
ret
Func_31dd: ; 31dd (0:31dd)
ld a, [wce6c]
Func_31e0: ; 31e0 (0:31e0)
call Func_3212
jr Func_31ab
Func_31e5: ; 31e5 (0:31e5)
ld a, [wce6d]
jr Func_31e0
Func_31ea: ; 31ea (0:31ea)
ld a, [rSB]
ld [wce6e], a
Func_31ef: ; 31ef (0:31ef)
xor a
jr Func_31e0
Func_31f2: ; 31f2 (0:31f2)
ld a, [rSB]
ld [wce6f], a
xor a
ld [wce63], a
ret
Func_31fc: ; 31fc (0:31fc)
ld hl, wce70
ld e, [hl]
inc hl
ld d, [hl]
ld a, [de]
inc de
ld [hl], d
dec hl
ld [hl], e
ld e, a
ld hl, wce6c
add [hl]
ld [hli], a
ld a, $0
adc [hl]
ld [hl], a
ld a, e
; fallthrough
Func_3212: ; 3212 (0:3212)
ld [rSB], a
ld a, SC_INTERNAL
ld [rSC], a
ld a, SC_START | SC_INTERNAL
ld [rSC], a
ret
; doubles the damage at de if swords dance or focus energy was used
; in the last turn by the turn holder's arena Pokemon
HandleDoubleDamageSubstatus: ; 321d (0:321d)
ld a, DUELVARS_ARENA_CARD_SUBSTATUS3
call GetTurnDuelistVariable
bit SUBSTATUS3_THIS_TURN_DOUBLE_DAMAGE, [hl]
call nz, .double_damage_at_de
ld a, DUELVARS_ARENA_CARD_SUBSTATUS1
call GetTurnDuelistVariable
or a
call nz, .ret1
ld a, DUELVARS_ARENA_CARD_SUBSTATUS2
call GetTurnDuelistVariable
or a
call nz, .ret2
ret
.ret1
ret
.double_damage_at_de
ld a, e
or d
ret z
sla e
rl d
ret
.ret2
ret
; check if the attacking card (non-turn holder's arena card) has any substatus that
; reduces the damage dealt this turn (SUBSTATUS2).
; check if the defending card (turn holder's arena card) has any substatus that
; reduces the damage dealt to it this turn (SUBSTATUS1 or Pkmn Powers).
; damage is given in de as input and the possibly updated damage is also returned in de.
HandleDamageReduction: ; 3244 (0:3244)
call HandleDamageReductionExceptSubstatus2
ld a, DUELVARS_ARENA_CARD_SUBSTATUS2
call GetNonTurnDuelistVariable
or a
ret z
cp SUBSTATUS2_REDUCE_BY_20
jr z, .reduce_damage_by_20
cp SUBSTATUS2_POUNCE
jr z, .reduce_damage_by_10
cp SUBSTATUS2_GROWL
jr z, .reduce_damage_by_10
ret
.reduce_damage_by_20
ld hl, -20
add hl, de
ld e, l
ld d, h
ret
.reduce_damage_by_10
ld hl, -10
add hl, de
ld e, l
ld d, h
ret
; check if the defending card (turn holder's arena card) has any substatus that
; reduces the damage dealt to it this turn. (SUBSTATUS1 or Pkmn Powers)
; damage is given in de as input and the possibly updated damage is also returned in de.
HandleDamageReductionExceptSubstatus2: ; 3269 (0:3269)
ld a, [wNoDamageOrEffect]
or a
jr nz, .no_damage
ld a, DUELVARS_ARENA_CARD_SUBSTATUS1
call GetTurnDuelistVariable
or a
jr z, .not_affected_by_substatus1
cp SUBSTATUS1_NO_DAMAGE_STIFFEN
jr z, .no_damage
cp SUBSTATUS1_NO_DAMAGE_10
jr z, .no_damage
cp SUBSTATUS1_NO_DAMAGE_11
jr z, .no_damage
cp SUBSTATUS1_NO_DAMAGE_17
jr z, .no_damage
cp SUBSTATUS1_REDUCE_BY_10
jr z, .reduce_damage_by_10
cp SUBSTATUS1_REDUCE_BY_20
jr z, .reduce_damage_by_20
cp SUBSTATUS1_HARDEN
jr z, .prevent_less_than_40_damage
cp SUBSTATUS1_HALVE_DAMAGE
jr z, .halve_damage
.not_affected_by_substatus1
call CheckCannotUseDueToStatus
ret c
.pkmn_power
ld a, [wLoadedMoveCategory]
cp POKEMON_POWER
ret z
ld a, [wTempNonTurnDuelistCardID]
cp MR_MIME
jr z, .prevent_less_than_30_damage ; invisible wall
cp KABUTO
jr z, .halve_damage2 ; kabuto armor
ret
.no_damage
ld de, 0
ret
.reduce_damage_by_10
ld hl, -10
add hl, de
ld e, l
ld d, h
ret
.reduce_damage_by_20
ld hl, -20
add hl, de
ld e, l
ld d, h
ret
.prevent_less_than_40_damage
ld bc, 40
call CompareDEtoBC
ret nc
ld de, 0
ret
.halve_damage
sla d
rr e
bit 0, e
ret z
ld hl, -5
add hl, de
ld e, l
ld d, h
ret
.prevent_less_than_30_damage
ld a, [wLoadedMoveCategory]
cp POKEMON_POWER
ret z
ld bc, 30
call CompareDEtoBC
ret c
ld de, 0
ret
.halve_damage2
sla d
rr e
bit 0, e
ret z
ld hl, -5
add hl, de
ld e, l
ld d, h
ret
; check for Invisible Wall, Kabuto Armor, NShield, or Transparency, in order to
; possibly reduce or make zero the damage at de.
HandleDamageReductionOrNoDamageFromPkmnPowerEffects: ; 32f7 (0:32f7)
ld a, [wLoadedMoveCategory]
cp POKEMON_POWER
ret z
ld a, MUK
call CountPokemonIDInBothPlayAreas
ret c
ld a, [wTempPlayAreaLocation_cceb]
or a
call nz, HandleDamageReductionExceptSubstatus2.pkmn_power
push de ; push damage from call above, which handles Invisible Wall and Kabuto Armor
call HandleNoDamageOrEffectSubstatus.pkmn_power
call nc, HandleTransparency
pop de ; restore damage
ret nc
; if carry was set due to NShield or Transparency, damage is 0
ld de, 0
ret
; when MACHAMP is damaged, if its Strikes Back is active, the
; attacking Pokemon (turn holder's arena Pokemon) takes 10 damage.
; ignore if damage taken at de is 0.
; used to bounce back a damaging move.
HandleStrikesBack_AgainstDamagingMove: ; 3317 (0:3317)
ld a, e
or d
ret z
ld a, [wIsDamageToSelf]
or a
ret nz
ld a, [wTempNonTurnDuelistCardID] ; ID of defending Pokemon
cp MACHAMP
ret nz
ld a, MUK
call CountPokemonIDInBothPlayAreas
ret c
ld a, [wLoadedMoveCategory] ; category of attack used
cp POKEMON_POWER
ret z
ld a, [wTempPlayAreaLocation_cceb] ; defending Pokemon's PLAY_AREA_*
or a ; cp PLAY_AREA_ARENA
jr nz, .in_bench
call CheckCannotUseDueToStatus
ret c
.in_bench
push hl
push de
; substract 10 HP from attacking Pokemon (turn holder's arena Pokemon)
call SwapTurn
ld a, DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
call LoadCardDataToBuffer2_FromDeckIndex
ld a, DUELVARS_ARENA_CARD_HP
call GetTurnDuelistVariable
push af
push hl
ld de, 10
call SubstractHP
ld a, [wLoadedCard2ID]
ld [wTempNonTurnDuelistCardID], a
ld hl, 10
call LoadTxRam3
ld hl, wLoadedCard2Name
ld a, [hli]
ld h, [hl]
ld l, a
call LoadTxRam2
ldtx hl, ReceivesDamageDueToStrikesBackText
call DrawWideTextBox_WaitForInput
pop hl
pop af
or a
jr z, .not_knocked_out
xor a
call PrintPlayAreaCardKnockedOutIfNoHP
.not_knocked_out
call SwapTurn
pop de
pop hl
ret
; return carry if NShield or Transparency activate (if MEW1 or HAUNTER1 is
; the turn holder's arena Pokemon), and print their corresponding text if so
HandleNShieldAndTransparency: ; 337f (0:337f)
push de
ld a, DUELVARS_ARENA_CARD
add e
call GetTurnDuelistVariable
call GetCardIDFromDeckIndex
ld a, e
cp MEW1
jr z, .nshield
cp HAUNTER1
jr z, .transparency
.done
pop de
or a
ret
.nshield
ld a, DUELVARS_ARENA_CARD_STAGE
call GetNonTurnDuelistVariable
or a
jr z, .done
ld a, NO_DAMAGE_OR_EFFECT_NSHIELD
ld [wNoDamageOrEffect], a
ldtx hl, NoDamageOrEffectDueToNShieldText
.print_text
call DrawWideTextBox_WaitForInput
pop de
scf
ret
.transparency
xor a
ld [wDuelDisplayedScreen], a
ldtx de, TransparencyCheckText
call TossCoin
jr nc, .done
ld a, NO_DAMAGE_OR_EFFECT_TRANSPARENCY
ld [wNoDamageOrEffect], a
ldtx hl, NoDamageOrEffectDueToTransparencyText
jr .print_text
; return carry if the turn holder's arena Pokemon is under a condition that makes
; it unable to attack. also return in hl the text id to be displayed
HandleCantAttackSubstatus: ; 33c1 (0:33c1)
ld a, DUELVARS_ARENA_CARD_SUBSTATUS2
call GetTurnDuelistVariable
or a
ret z
ldtx hl, UnableToAttackDueToTailWagText
cp SUBSTATUS2_TAIL_WAG
jr z, .return_with_cant_attack
ldtx hl, UnableToAttackDueToLeerText
cp SUBSTATUS2_LEER
jr z, .return_with_cant_attack
ldtx hl, UnableToAttackDueToBoneAttackText
cp SUBSTATUS2_BONE_ATTACK
jr z, .return_with_cant_attack
or a
ret
.return_with_cant_attack
scf
ret
; return carry if the turn holder's arena Pokemon cannot use
; selected move at wSelectedAttack due to amnesia
HandleAmnesiaSubstatus: ; 33e1 (0:33e1)
ld a, DUELVARS_ARENA_CARD_SUBSTATUS2
call GetTurnDuelistVariable
or a
jr nz, .check_amnesia
ret
.check_amnesia
cp SUBSTATUS2_AMNESIA
jr z, .affected_by_amnesia
.not_the_disabled_move
or a
ret
.affected_by_amnesia
ld a, DUELVARS_ARENA_CARD_DISABLED_MOVE_INDEX
call GetTurnDuelistVariable
ld a, [wSelectedAttack]
cp [hl]
jr nz, .not_the_disabled_move
ldtx hl, UnableToUseAttackDueToAmnesiaText
scf
ret
; return carry if the turn holder's attack was unsuccessful due to sand attack or smokescreen effect
HandleSandAttackOrSmokescreenSubstatus: ; 3400 (0:3400)
call CheckSandAttackOrSmokescreenSubstatus
ret nc
call TossCoin
ld [wGotHeadsFromSandAttackOrSmokescreenCheck], a
ccf
ret nc
ldtx hl, AttackUnsuccessfulText
call DrawWideTextBox_WaitForInput
scf
ret
; return carry if the turn holder's arena card is under the effects of sand attack or smokescreen
CheckSandAttackOrSmokescreenSubstatus: ; 3414 (0:3414)
ld a, DUELVARS_ARENA_CARD_SUBSTATUS2
call GetTurnDuelistVariable
or a
ret z
ldtx de, SandAttackCheckText
cp SUBSTATUS2_SAND_ATTACK
jr z, .card_is_affected
ldtx de, SmokescreenCheckText
cp SUBSTATUS2_SMOKESCREEN
jr z, .card_is_affected
or a
ret
.card_is_affected
ld a, [wGotHeadsFromSandAttackOrSmokescreenCheck]
or a
ret nz
scf
ret
; return carry if the defending card (turn holder's arena card) is under a substatus
; that prevents any damage or effect dealt to it for a turn.
; also return the cause of the substatus in wNoDamageOrEffect
HandleNoDamageOrEffectSubstatus: ; 3432 (0:3432)
xor a
ld [wNoDamageOrEffect], a
ld a, [wLoadedMoveCategory]
cp POKEMON_POWER
ret z
ld a, DUELVARS_ARENA_CARD_SUBSTATUS1
call GetTurnDuelistVariable
ld e, NO_DAMAGE_OR_EFFECT_FLY
ldtx hl, NoDamageOrEffectDueToFlyText
cp SUBSTATUS1_FLY
jr z, .no_damage_or_effect
ld e, NO_DAMAGE_OR_EFFECT_BARRIER
ldtx hl, NoDamageOrEffectDueToBarrierText
cp SUBSTATUS1_BARRIER
jr z, .no_damage_or_effect
ld e, NO_DAMAGE_OR_EFFECT_AGILITY
ldtx hl, NoDamageOrEffectDueToAgilityText
cp SUBSTATUS1_AGILITY
jr z, .no_damage_or_effect
call CheckCannotUseDueToStatus
ccf
ret nc
.pkmn_power
ld a, [wTempNonTurnDuelistCardID]
cp MEW1
jr z, .neutralizing_shield
or a
ret
.no_damage_or_effect
ld a, e
ld [wNoDamageOrEffect], a
scf
ret
.neutralizing_shield
ld a, [wIsDamageToSelf]
or a
ret nz
; prevent damage if attacked by a non-basic Pokemon
ld a, [wTempTurnDuelistCardID]
ld e, a
ld d, $0
call LoadCardDataToBuffer2_FromCardID
ld a, [wLoadedCard2Stage]
or a
ret z
ld e, NO_DAMAGE_OR_EFFECT_NSHIELD
ldtx hl, NoDamageOrEffectDueToNShieldText
jr .no_damage_or_effect
; if the Pokemon being attacked is HAUNTER1, and its Transparency is active,
; there is a 50% chance that any damage or effect is prevented
; return carry if damage is prevented
HandleTransparency: ; 348a (0:348a)
ld a, [wTempNonTurnDuelistCardID]
cp HAUNTER1
jr z, .transparency
.done
or a
ret
.transparency
ld a, [wLoadedMoveCategory]
cp POKEMON_POWER
jr z, .done ; Transparency has no effect against Pkmn Powers
ld a, [wTempPlayAreaLocation_cceb]
call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0
jr c, .done
xor a
ld [wDuelDisplayedScreen], a
ldtx de, TransparencyCheckText
call TossCoin
ret nc
ld a, NO_DAMAGE_OR_EFFECT_TRANSPARENCY
ld [wNoDamageOrEffect], a
ldtx hl, NoDamageOrEffectDueToTransparencyText
scf
ret
; return carry and return the appropriate text id in hl if the target has an
; special status or power that prevents any damage or effect done to it this turn
; input: a = NO_DAMAGE_OR_EFFECT_*
CheckNoDamageOrEffect: ; 34b7 (0:34b7)
ld a, [wNoDamageOrEffect]
or a
ret z
bit 7, a
jr nz, .dont_print_text ; already been here so don't repeat the text
ld hl, wNoDamageOrEffect
set 7, [hl]
dec a
add a
ld e, a
ld d, $0
ld hl, NoDamageOrEffectTextIDTable
add hl, de
ld a, [hli]
ld h, [hl]
ld l, a
scf
ret
.dont_print_text
ld hl, $0000
scf
ret
NoDamageOrEffectTextIDTable: ; 34d8 (0:34d8)
tx NoDamageOrEffectDueToAgilityText ; NO_DAMAGE_OR_EFFECT_AGILITY
tx NoDamageOrEffectDueToBarrierText ; NO_DAMAGE_OR_EFFECT_BARRIER
tx NoDamageOrEffectDueToFlyText ; NO_DAMAGE_OR_EFFECT_FLY
tx NoDamageOrEffectDueToTransparencyText ; NO_DAMAGE_OR_EFFECT_TRANSPARENCY
tx NoDamageOrEffectDueToNShieldText ; NO_DAMAGE_OR_EFFECT_NSHIELD
; return carry if turn holder has Omanyte and its Clairvoyance Pkmn Power is active
IsClairvoyanceActive: ; 34e2 (0:34e2)
ld a, MUK
call CountPokemonIDInBothPlayAreas
ccf
ret nc
ld a, OMANYTE
call CountPokemonIDInPlayArea
ret
; returns carry if turn holder's arena card is paralyzed, asleep, confused,
; and/or toxic gas in play, meaning that move and/or pkmn power cannot be used
CheckCannotUseDueToStatus: ; 34ef (0:34ef)
xor a
; same as above, but if a is non-0, only toxic gas is checked
CheckCannotUseDueToStatus_OnlyToxicGasIfANon0: ; 34f0 (0:34f0)
or a
jr nz, .check_toxic_gas
ld a, DUELVARS_ARENA_CARD_STATUS
call GetTurnDuelistVariable
and CNF_SLP_PRZ
ldtx hl, CannotUseDueToStatusText
scf
jr nz, .done ; return carry
.check_toxic_gas
ld a, MUK
call CountPokemonIDInBothPlayAreas
ldtx hl, UnableDueToToxicGasText
.done
ret
; return, in a, the amount of times that the Pokemon card with a given ID is found in the
; play area of both duelists. Also return carry if the Pokemon card is at least found once.
; if the arena Pokemon is asleep, confused, or paralyzed (Pkmn Power-incapable), it doesn't count.
; input: a = Pokemon card ID to search
CountPokemonIDInBothPlayAreas: ; 3509 (0:3509)
push bc
ld [wTempPokemonID_ce7c], a
call CountPokemonIDInPlayArea
ld c, a
call SwapTurn
ld a, [wTempPokemonID_ce7c]
call CountPokemonIDInPlayArea
call SwapTurn
add c
or a
scf
jr nz, .found
or a
.found
pop bc
ret
; return, in a, the amount of times that the Pokemon card with a given ID is found in the
; turn holder's play area. Also return carry if the Pokemon card is at least found once.
; if the arena Pokemon is asleep, confused, or paralyzed (Pkmn Power-incapable), it doesn't count.
; input: a = Pokemon card ID to search
CountPokemonIDInPlayArea: ; 3525 (0:3525)
push hl
push de
push bc
ld [wTempPokemonID_ce7c], a
ld c, $0
ld a, DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
cp -1
jr z, .check_bench
call GetCardIDFromDeckIndex
ld a, [wTempPokemonID_ce7c]
cp e
jr nz, .check_bench
ld a, DUELVARS_ARENA_CARD_STATUS
call GetTurnDuelistVariable
and CNF_SLP_PRZ
jr nz, .check_bench
inc c
.check_bench
ld a, DUELVARS_BENCH
call GetTurnDuelistVariable
.next_bench_slot
ld a, [hli]
cp -1
jr z, .done
call GetCardIDFromDeckIndex
ld a, [wTempPokemonID_ce7c]
cp e
jr nz, .skip
inc c
.skip
inc b
jr .next_bench_slot
.done
ld a, c
or a
scf
jr nz, .found
or a
.found
pop bc
pop de
pop hl
ret
; return, in a, the retreat cost of the card in wLoadedCard1,
; adjusting for any Dodrio's Retreat Aid Pkmn Power that is active.
GetLoadedCard1RetreatCost: ; 356a (0:356a)
ld c, 0
ld a, DUELVARS_BENCH
call GetTurnDuelistVariable
.check_bench_loop
ld a, [hli]
cp -1
jr z, .no_more_bench
call GetCardIDFromDeckIndex
ld a, e
cp DODRIO
jr nz, .not_dodrio
inc c
.not_dodrio
jr .check_bench_loop
.no_more_bench
ld a, c
or a
jr nz, .dodrio_found
.muk_found
ld a, [wLoadedCard1RetreatCost] ; return regular retreat cost
ret
.dodrio_found
ld a, MUK
call CountPokemonIDInBothPlayAreas
jr c, .muk_found
ld a, [wLoadedCard1RetreatCost]
sub c ; apply Retreat Aid for each Pkmn Power-capable Dodrio
ret nc
xor a
ret
; return carry if the turn holder's arena Pokemon is affected by Acid and can't retreat
CheckCantRetreatDueToAcid: ; 3597 (0:3597)
ld a, DUELVARS_ARENA_CARD_SUBSTATUS2
call GetTurnDuelistVariable
or a
ret z
cp SUBSTATUS2_UNABLE_RETREAT
jr z, .cant_retreat
or a
ret
.cant_retreat
ldtx hl, UnableToRetreatDueToAcidText
scf
ret
; return carry if the turn holder is affected by Headache and trainer cards can't be used
CheckCantUseTrainerDueToHeadache: ; 35a9 (0:35a9)
ld a, DUELVARS_ARENA_CARD_SUBSTATUS3
call GetTurnDuelistVariable
or a
bit SUBSTATUS3_HEADACHE, [hl]
ret z
ldtx hl, UnableToUseTrainerDueToHeadacheText
scf
ret
; return carry if any duelist has Aerodactyl and its Prehistoric Power Pkmn Power is active
IsPrehistoricPowerActive: ; 35b7 (0:35b7)
ld a, AERODACTYL
call CountPokemonIDInBothPlayAreas
ret nc
ld a, MUK
call CountPokemonIDInBothPlayAreas
ldtx hl, UnableToEvolveDueToPrehistoricPowerText
ccf
ret
; clears some SUBSTATUS2 conditions from the turn holder's active Pokemon.
; more specifically, those conditions that reduce the damage from an attack
; or prevent the opposing Pokemon from attacking the substatus condition inducer.
ClearDamageReductionSubstatus2: ; 35c7 (0:35c7)
ld a, DUELVARS_ARENA_CARD_SUBSTATUS2
call GetTurnDuelistVariable
or a
ret z
cp SUBSTATUS2_REDUCE_BY_20
jr z, .zero
cp SUBSTATUS2_POUNCE
jr z, .zero
cp SUBSTATUS2_GROWL
jr z, .zero
cp SUBSTATUS2_TAIL_WAG
jr z, .zero
cp SUBSTATUS2_LEER
jr z, .zero
ret
.zero
ld [hl], 0
ret
; clears the SUBSTATUS1 and updates the double damage condition of the player about to start his turn
UpdateSubstatusConditions_StartOfTurn: ; 35e6 (0:35e6)
ld a, DUELVARS_ARENA_CARD_SUBSTATUS1
call GetTurnDuelistVariable
ld [hl], $0
or a
ret z
cp SUBSTATUS1_NEXT_TURN_DOUBLE_DAMAGE
ret nz
ld a, DUELVARS_ARENA_CARD_SUBSTATUS3
call GetTurnDuelistVariable
set SUBSTATUS3_THIS_TURN_DOUBLE_DAMAGE, [hl]
ret
; clears the SUBSTATUS2, Headache, and updates the double damage condition of the player ending his turn
UpdateSubstatusConditions_EndOfTurn: ; 35fa (0:35fa)
ld a, DUELVARS_ARENA_CARD_SUBSTATUS3
call GetTurnDuelistVariable
res SUBSTATUS3_HEADACHE, [hl]
push hl
ld a, DUELVARS_ARENA_CARD_SUBSTATUS2
call GetTurnDuelistVariable
xor a
ld [hl], a
ld a, DUELVARS_ARENA_CARD_SUBSTATUS1
call GetTurnDuelistVariable
pop hl
cp SUBSTATUS1_NEXT_TURN_DOUBLE_DAMAGE
ret z
res SUBSTATUS3_THIS_TURN_DOUBLE_DAMAGE, [hl]
ret
; return carry if turn holder has Blastoise and its Rain Dance Pkmn Power is active
IsRainDanceActive: ; 3615 (0:3615)
ld a, BLASTOISE
call CountPokemonIDInPlayArea
ret nc ; return if no Pkmn Power-capable Blastoise found in turn holder's play area
ld a, MUK
call CountPokemonIDInBothPlayAreas
ccf
ret
; return carry if card at [hTempCardIndex_ff98] is a water energy card AND
; if card at [hTempPlayAreaLocation_ff9d] is a water Pokemon card.
CheckRainDanceScenario: ; 3622 (0:3622)
ldh a, [hTempCardIndex_ff98]
call GetCardIDFromDeckIndex
call GetCardType
cp TYPE_ENERGY_WATER
jr nz, .done
ldh a, [hTempPlayAreaLocation_ff9d]
call GetPlayAreaCardColor
cp TYPE_PKMN_WATER
jr nz, .done
scf
ret
.done
or a
ret
; if the defending (non-turn) card's HP is 0 and the attacking (turn) card's HP
; is not, the attacking card faints if it was affected by destiny bond
HandleDestinyBondSubstatus: ; 363b (0:363b)
ld a, DUELVARS_ARENA_CARD_SUBSTATUS1
call GetNonTurnDuelistVariable
cp SUBSTATUS1_DESTINY_BOND
jr z, .check_hp
ret
.check_hp
ld a, DUELVARS_ARENA_CARD
call GetNonTurnDuelistVariable
cp -1
ret z
ld a, DUELVARS_ARENA_CARD_HP
call GetNonTurnDuelistVariable
or a
ret nz
ld a, DUELVARS_ARENA_CARD_HP
call GetTurnDuelistVariable
or a
ret z
ld [hl], 0
push hl
call DrawDuelMainScene
call DrawDuelHUDs
pop hl
ld l, DUELVARS_ARENA_CARD
ld a, [hl]
call LoadCardDataToBuffer2_FromDeckIndex
ld hl, wLoadedCard2Name
ld a, [hli]
ld h, [hl]
ld l, a
call LoadTxRam2
ldtx hl, KnockedOutDueToDestinyBondText
call DrawWideTextBox_WaitForInput
ret
; when MACHAMP is damaged, if its Strikes Back is active, the
; attacking Pokemon (turn holder's arena Pokemon) takes 10 damage.
; used to bounce back a move of the RESIDUAL category
HandleStrikesBack_AgainstResidualMove: ; 367b (0:367b)
ld a, [wTempNonTurnDuelistCardID]
cp MACHAMP
jr z, .strikes_back
ret
.strikes_back
ld a, [wLoadedMoveCategory]
and RESIDUAL
ret nz
ld a, [wDealtDamage]
or a
ret z
call SwapTurn
call CheckCannotUseDueToStatus
call SwapTurn
ret c
ld hl, 10 ; damage to be dealt to attacker
call ApplyStrikesBack_AgainstResidualMove
call nc, WaitForWideTextBoxInput
ret
ApplyStrikesBack_AgainstResidualMove: ; 36a2 (0:36a2)
push hl
call LoadTxRam3
ld a, [wTempTurnDuelistCardID]
ld e, a
ld d, $0
call LoadCardDataToBuffer2_FromCardID
ld hl, wLoadedCard2Name
ld a, [hli]
ld h, [hl]
ld l, a
call LoadTxRam2
ld a, DUELVARS_ARENA_CARD_HP
call GetTurnDuelistVariable
pop de
push af
push hl
call SubstractHP
ldtx hl, ReceivesDamageDueToStrikesBackText
call DrawWideTextBox_PrintText
pop hl
pop af
or a
ret z
call WaitForWideTextBoxInput
xor a
call PrintPlayAreaCardKnockedOutIfNoHP
call DrawDuelHUDs
scf
ret
; if the id of the card provided in register a as a deck index is MUK,
; clear the changed type of all arena and bench Pokemon
ClearChangedTypesIfMuk: ; 36d9 (0:36d9)
call GetCardIDFromDeckIndex
ld a, e
cp MUK
ret nz
call SwapTurn
call .zero_changed_types
call SwapTurn
.zero_changed_types
ld a, DUELVARS_ARENA_CARD_CHANGED_TYPE
call GetTurnDuelistVariable
ld c, MAX_PLAY_AREA_POKEMON
.zero_changed_types_loop
xor a
ld [hli], a
dec c
jr nz, .zero_changed_types_loop
ret
; return the turn holder's arena card's color in a, accounting for Venomoth's Shift Pokemon Power if active
GetArenaCardColor: ; 36f6 (0:36f6)
xor a
; fallthrough
; input: a = play area location offset (PLAY_AREA_*) of the desired card
; return the turn holder's card's color in a, accounting for Venomoth's Shift Pokemon Power if active
GetPlayAreaCardColor: ; 36f7 (0:36f7)
push hl
push de
ld e, a
add DUELVARS_ARENA_CARD_CHANGED_TYPE
call GetTurnDuelistVariable
bit HAS_CHANGED_COLOR_F, a
jr nz, .has_changed_color
.regular_color
ld a, e
add DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
call GetCardIDFromDeckIndex
call GetCardType
cp TYPE_TRAINER
jr nz, .got_type
ld a, COLORLESS
.got_type
pop de
pop hl
ret
.has_changed_color
ld a, e
call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0
jr c, .regular_color ; jump if can't use Shift
ld a, e
add DUELVARS_ARENA_CARD_CHANGED_TYPE
call GetTurnDuelistVariable
pop de
pop hl
and $f
ret
; return in a the weakness of the turn holder's arena or benchx Pokemon given the PLAY_AREA_* value in a
; if a == 0 and [DUELVARS_ARENA_CARD_CHANGED_WEAKNESS] != 0,
; return [DUELVARS_ARENA_CARD_CHANGED_WEAKNESS] instead
GetPlayAreaCardWeakness: ; 3729 (0:3729)
or a
jr z, GetArenaCardWeakness
add DUELVARS_ARENA_CARD
jr GetCardWeakness
; return in a the weakness of the turn holder's arena Pokemon
; if [DUELVARS_ARENA_CARD_CHANGED_WEAKNESS] != 0, return it instead
GetArenaCardWeakness: ; 3730 (0:3730)
ld a, DUELVARS_ARENA_CARD_CHANGED_WEAKNESS
call GetTurnDuelistVariable
or a
ret nz
ld a, DUELVARS_ARENA_CARD
; fallthrough
GetCardWeakness:
call GetTurnDuelistVariable
call LoadCardDataToBuffer2_FromDeckIndex
ld a, [wLoadedCard2Weakness]
ret
; return in a the resistance of the turn holder's arena or benchx Pokemon given the PLAY_AREA_* value in a
; if a == 0 and [DUELVARS_ARENA_CARD_CHANGED_RESISTANCE] != 0,
; return [DUELVARS_ARENA_CARD_CHANGED_RESISTANCE] instead
GetPlayAreaCardResistance: ; 3743 (0:3743)
or a
jr z, GetArenaCardResistance
add DUELVARS_ARENA_CARD
jr GetCardResistance
; return in a the resistance of the arena Pokemon
; if [DUELVARS_ARENA_CARD_CHANGED_RESISTANCE] != 0, return it instead
GetArenaCardResistance: ; 374a (0:374a)
ld a, DUELVARS_ARENA_CARD_CHANGED_RESISTANCE
call GetTurnDuelistVariable
or a
ret nz
ld a, DUELVARS_ARENA_CARD
; fallthrough
GetCardResistance:
call GetTurnDuelistVariable
call LoadCardDataToBuffer2_FromDeckIndex
ld a, [wLoadedCard2Resistance]
ret
; this function checks if turn holder's CHARIZARD energy burn is active, and if so, turns
; all energies at wAttachedEnergies except double colorless energies into fire energies
HandleEnergyBurn: ; 375d (0:375d)
ld a, DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
call GetCardIDFromDeckIndex
ld a, e
cp CHARIZARD
ret nz
xor a
call CheckCannotUseDueToStatus_OnlyToxicGasIfANon0
ret c
ld hl, wAttachedEnergies
ld c, NUM_COLORED_TYPES
xor a
.zero_next_energy
ld [hli], a
dec c
jr nz, .zero_next_energy
ld a, [wTotalAttachedEnergies]
ld [wAttachedEnergies], a
ret
SetupSound: ; 377f (0:377f)
farcall _SetupSound
ret
Func_3784: ; 3784 (0:3784)
xor a
PlaySong: ; 3785 (0:3785)
farcall _PlaySong
ret
; return a = 0: song finished, a = 1: song not finished
AssertSongFinished: ; 378a (0:378a)
farcall _AssertSongFinished
ret
; return a = 0: SFX finished, a = 1: SFX not finished
AssertSFXFinished: ; 378f (0:378f)
farcall _AssertSFXFinished
ret
Func_3794: ; 3794 (0:3794)
ld a, $04
PlaySFX: ; 3796 (0:3796)
farcall _PlaySFX
ret
PauseSong: ; 379b (0:379b)
farcall _PauseSong
ret
ResumeSong: ; 37a0 (0:37a0)
farcall _ResumeSong
ret
Func_37a5: ; 37a5 (0:37a5)
ldh a, [hBankROM]
push af
push hl
srl h
srl h
srl h
ld a, BANK(CardGraphics)
add h
call BankswitchROM
pop hl
add hl, hl
add hl, hl
add hl, hl
res 7, h
set 6, h ; $4000 ≤ hl ≤ $7fff
call Func_37c5
pop af
call BankswitchROM
ret
Func_37c5: ; 37c5 (0:37c5)
ld c, $08
.asm_37c7
ld b, $06
.asm_37c9
push bc
ld c, $08
.asm_37cc
ld b, $02
.asm_37ce
push bc
push hl
ld c, [hl]
ld b, $04
.asm_37d3
rr c
rra
sra a
dec b
jr nz, .asm_37d3
ld hl, $c0
add hl, de
ld [hli], a
inc hl
ld [hl], a
ld b, $04
.asm_37e4
rr c
rra
sra a
dec b
jr nz, .asm_37e4
ld [de], a
ld hl, $2
add hl, de
ld [hl], a
pop hl
pop bc
inc de
inc hl
dec b
jr nz, .asm_37ce
inc de
inc de
dec c
jr nz, .asm_37cc
pop bc
dec b
jr nz, .asm_37c9
ld a, $c0
add e
ld e, a
ld a, $00
adc d
ld d, a
dec c
jr nz, .asm_37c7
ret
Func_380e: ; 380e (0:380e)
ld a, [wd0c1]
bit 7, a
ret nz
ldh a, [hBankROM]
push af
ld a, BANK(SetScreenScrollWram)
call BankswitchROM
call SetScreenScrollWram
call Func_c554
ld a, BANK(Func_1c610)
call BankswitchROM
call Func_1c610
call Func_3cb4
ld a, BANK(Func_804d8)
call BankswitchROM
call Func_804d8
call UpdateRNGSources
pop af
call BankswitchROM
ret
; enable the play time counter and execute the game event at [wGameEvent].
; then return to the overworld, or restart the game (only after Credits).
ExecuteGameEvent: ; 383d (0:383d)
ld a, 1
ld [wPlayTimeCounterEnable], a
ldh a, [hBankROM]
push af
.loop
call _ExecuteGameEvent
jr nc, .restart
farcall LoadMap
jr .loop
.restart
pop af
call BankswitchROM
ret
; execute a game event at [wGameEvent] from GameEventPointerTable
_ExecuteGameEvent: ; 3855 (0:3855)
ld a, [wGameEvent]
cp NUM_GAME_EVENTS
jr c, .got_game_event
ld a, GAME_EVENT_CHALLENGE_MACHINE
.got_game_event
ld hl, GameEventPointerTable
jp JumpToFunctionInTable
GameEventPointerTable: ; 3864 (0:3864)
dw GameEvent_Overworld
dw GameEvent_Duel
dw GameEvent_BattleCenter
dw GameEvent_GiftCenter
dw GameEvent_Credits
dw GameEvent_ContinueDuel
dw GameEvent_ChallengeMachine
dw GameEvent_Overworld
GameEvent_Overworld: ; 3874 (0:3874)
scf
ret
GameEvent_GiftCenter: ; 3876 (0:3876)
ldh a, [hBankROM]
push af
call PauseSong
ld a, MUSIC_CARD_POP
call PlaySong
ld a, $3
ld [wd0c2], a
ld a, [wd10e]
or $10
ld [wd10e], a
farcall Func_b177
ld a, [wd10e]
and $ef
ld [wd10e], a
call ResumeSong
pop af
call BankswitchROM
scf
ret
GameEvent_BattleCenter: ; 38a3 (0:38a3)
ld a, $2
ld [wd0c2], a
xor a
ld [wd112], a
ld a, -1
ld [wDuelResult], a
ld a, MUSIC_DUEL_THEME_1
ld [wDuelTheme], a
ld a, MUSIC_CARD_POP
call PlaySong
bank1call Func_758f
scf
ret
GameEvent_Duel: ; 38c0 (0:38c0)
ld a, $1
ld [wd0c2], a
xor a
ld [wd112], a
call EnableSRAM
xor a
ld [$ba44], a
call DisableSRAM
call Func_3a3b
bank1call StartDuel
scf
ret
GameEvent_ChallengeMachine: ; 38db (0:38db)
ld a, $6
ld [wd111], a
call Func_39fc
call EnableSRAM
xor a
ld [$ba44], a
call DisableSRAM
.asm_38ed
farcall Func_131d3
ld a, $9
ld [wd111], a
call Func_39fc
scf
ret
GameEvent_ContinueDuel: ; 38fb (0:38fb)
xor a
ld [wd112], a
bank1call TryContinueDuel
call EnableSRAM
ld a, [$ba44]
call DisableSRAM
cp $ff
jr z, GameEvent_ChallengeMachine.asm_38ed
scf
ret
GameEvent_Credits: ; 3911 (0:3911)
farcall Credits_1d6ad
or a
ret
Func_3917: ; 3917 (0:3917)
ld a, EVENT_RECEIVED_LEGENDARY_CARD
farcall GetEventFlagValue
call EnableSRAM
ld [s0a00a], a
call DisableSRAM
ret
; return in a the permission byte corresponding to the current map's x,y coordinates at bc
GetPermissionOfMapPosition: ; 3927 (0:3927)
push hl
call GetPermissionByteOfMapPosition
ld a, [hl]
pop hl
ret
; set to a the permission byte corresponding to the current map's x,y coordinates at bc
SetPermissionOfMapPosition: ; 392e (0:392e)
push hl
push af
call GetPermissionByteOfMapPosition
pop af
ld [hl], a
pop hl
ret
; set the permission byte corresponding to the current map's x,y coordinates at bc
; to the value of register a anded by its current value
UpdatePermissionOfMapPosition: ; 3937 (0:3937)
push hl
push bc
push de
cpl
ld e, a
call GetPermissionByteOfMapPosition
ld a, [hl]
and e
ld [hl], a
pop de
pop bc
pop hl
ret
; returns in hl the address within wPermissionMap that corresponds to
; the current map's x,y coordinates at bc
GetPermissionByteOfMapPosition: ; 3946 (0:3946)
push bc
srl b
srl c
swap c
ld a, c
and $f0
or b
ld c, a
ld b, $0
ld hl, wPermissionMap
add hl, bc
pop bc
ret
Func_395a: ; 395a (0:395a)
ldh a, [hBankROM]
push af
ld a, [wTempPointerBank]
call BankswitchROM
call CopyGfxData
pop af
call BankswitchROM
ret
Unknown_396b: ; 396b (0:396b)
db $00, -$01, $01, $00, $00, $01, -$01, $00
; Movement offsets for player movements
PlayerMovementOffsetTable: ; 3973 (0:3973)
db 0, -2 ; move 2 tiles up
db 2, 0 ; move 2 tiles right
db 0, 2 ; move 2 tiles down
db -2, 0 ; move 2 tiles left
Unknown_397b: ; 397b (0:397b)
dw $0323
dw $0323
dw $0324
dw $0325
dw $0326
dw $0327
dw $0328
dw $0329
dw $032a
dw $032b
dw $032c
dw $032d
dw $032e
dw $032f
Func_3997: ; 3997 (0:3997)
ldh a, [hBankROM]
push af
ld a, BANK(Func_1c056)
call BankswitchROM
call Func_1c056
pop af
call BankswitchROM
ret
; returns in hl a pointer to the first element for the a'th NPC
GetLoadedNPCID: ; 39a7 (0:39a7)
ld l, LOADED_NPC_ID
call GetItemInLoadedNPCIndex
ret
; return in hl a pointer to the a'th items element l
GetItemInLoadedNPCIndex: ; 39ad (0:39ad)
push bc
cp LOADED_NPC_MAX
jr c, .asm_39b4
debug_ret
xor a
.asm_39b4
add a
add a
ld h, a
add a
add h
add l
ld l, a
ld h, $0
ld bc, wLoadedNPCs
add hl, bc
pop bc
ret
; Finds the index on wLoadedNPCs table of the npc in wTempNPC
; returns it in a and puts it into wLoadedNPCTempIndex
; c flag set if no npc found
FindLoadedNPC: ; 39c3 (0:39c3)
push hl
push bc
push de
xor a
ld [wLoadedNPCTempIndex], a
ld b, a
ld c, LOADED_NPC_MAX
ld de, LOADED_NPC_LENGTH
ld hl, wLoadedNPCs
ld a, [wTempNPC]
.findNPCLoop
cp [hl]
jr z, .foundNPCMatch
add hl, de
inc b
dec c
jr nz, .findNPCLoop
scf
jr z, .exit
.foundNPCMatch
ld a, b
ld [wLoadedNPCTempIndex], a
or a
.exit
pop de
pop bc
pop hl
ret
Func_39ea: ; 39ea (0:39ea)
push bc
ldh a, [hBankROM]
push af
ld a, $03
call BankswitchROM
ld a, [bc]
ld c, a
pop af
call BankswitchROM
ld a, c
pop bc
ret
Func_39fc: ; 39fc (0:39fc)
push hl
push bc
call AssertSongFinished
or a
push af
call Func_3a1f
ld c, a
pop af
jr z, .asm_3a11
ld a, c
ld hl, wd112
cp [hl]
jr z, .asm_3a1c
.asm_3a11
ld a, c
cp $1f
jr nc, .asm_3a1c
ld [wd112], a
call PlaySong
.asm_3a1c
pop bc
pop hl
ret
Func_3a1f: ; 3a1f (0:3a1f)
ld a, [wd3b8]
or a
jr z, .asm_3a37
ld a, [wd32e]
cp $2
jr z, .asm_3a37
cp $b
jr z, .asm_3a37
cp $c
jr z, .asm_3a37
ld a, MUSIC_RONALD
ret
.asm_3a37
ld a, [wd111]
ret
Func_3a3b: ; 3a3b (0:3a3b)
farcall Func_1124d
ret
Func_3a40: ; 3a40 (0:3a40)
farcall Func_11430
ret
Func_3a45: ; 3a45 (0:3a45)
farcall Func_11343
ret
Func_3a4a: ; 3a4a (0:3a4a)
farcall Func_115a3
ret
Func_3a4f: ; 3a4f (0:3a4f)
push af
push bc
push de
push hl
ld c, $00
farcall Func_1157c
pop hl
pop de
pop bc
pop af
ret
HandleMoveModeAPress: ; 3a5e (0:3a5e)
ldh a, [hBankROM]
push af
ld l, MAP_SCRIPT_OBJECTS
call GetMapScriptPointer
jr nc, .handleSecondAPressScript
ld a, BANK(FindPlayerMovementFromDirection)
call BankswitchROM
call FindPlayerMovementFromDirection
ld a, BANK(MapScripts)
call BankswitchROM
ld a, [wPlayerDirection]
ld d, a
.findAPressMatchLoop
ld a, [hli]
bit 7, a
jr nz, .handleSecondAPressScript
push bc
push hl
cp d
jr nz, .noMatch
ld a, [hli]
cp b
jr nz, .noMatch
ld a, [hli]
cp c
jr nz, .noMatch
ld a, [hli]
ld [wNextScript], a
ld a, [hli]
ld [wNextScript+1], a
ld a, [hli]
ld [wDefaultObjectText], a
ld a, [hli]
ld [wDefaultObjectText+1], a
ld a, [hli]
ld [wCurrentNPCNameTx], a
ld a, [hli]
ld [wCurrentNPCNameTx+1], a
pop hl
pop bc
pop af
call BankswitchROM
scf
ret
.noMatch
pop hl
ld bc, MAP_OBJECT_SIZE - 1
add hl, bc
pop bc
jr .findAPressMatchLoop
.handleSecondAPressScript
pop af
call BankswitchROM
ld l, MAP_SCRIPT_PRESSED_A
call CallMapScriptPointerIfExists
ret
; returns a map script pointer in hl given
; current map in wCurMap and which sub-script in l
; sets c if pointer is found
GetMapScriptPointer: ; 3abd (0:3abd)
push bc
push hl
ld a, [wCurMap]
ld l, a
ld h, $0
add hl, hl
add hl, hl
add hl, hl
add hl, hl
ld bc, MapScripts
add hl, bc
pop bc
ld b, $0
add hl, bc
ldh a, [hBankROM]
push af
ld a, BANK(MapScripts)
call BankswitchROM
ld a, [hli]
ld h, [hl]
ld l, a
pop af
call BankswitchROM
ld a, l
or h
jr nz, .asm_3ae5
scf
.asm_3ae5
ccf
pop bc
ret
Func_3ae8: ; 3ae8 (0:3ae8)
farcall Func_11f4e
ret
; finds a Script from the first byte and puts the next two bytes (usually arguments?) into cb
RunOverworldScript: ; 3aed (0:3aed)
ld hl, wScriptPointer
ld a, [hli]
ld h, [hl]
ld l, a
ld a, [hli]
ld c, [hl]
inc hl
ld b, [hl]
push bc
rlca
ld c, a
ld b, $0
ld hl, OverworldScriptTable
add hl, bc
ldh a, [hBankROM]
push af
ld a, BANK(OverworldScriptTable)
call BankswitchROM
ld a, [hli]
ld h, [hl]
ld l, a
pop af
call BankswitchROM
pop bc
jp hl
Func_3b11: ; 3b11 (0:3b11)
ldh a, [hBankROM]
push af
ld a, $04
call BankswitchROM
call $66d1
pop af
call BankswitchROM
ret
Func_3b21: ; 3b21 (0:3b21)
ldh a, [hBankROM]
push af
ld a, BANK(Func_1c8bc)
call BankswitchROM
call Func_1c8bc
pop af
call BankswitchROM
ret
Func_3b31: ; 3b31 (0:3b31)
ldh a, [hBankROM]
push af
ld a, BANK(Func_1cb18)
call BankswitchROM
call Func_1cb18
jr c, .asm_3b45
xor a
ld [wDoFrameFunction], a
ld [wDoFrameFunction + 1], a
.asm_3b45
call ZeroObjectPositions
ld a, 1
ld [wVBlankOAMCopyToggle], a
pop af
call BankswitchROM
ret
; return nc if wd42a, wd4c0, and wAnimationQueue[] are all equal to $ff
; nc means no animation is playing (or animation(s) has/have ended)
CheckAnyAnimationPlaying: ; 3b52 (0:3b52)
push hl
push bc
ld a, [wd42a]
ld hl, wd4c0
and [hl]
ld hl, wAnimationQueue
ld c, ANIMATION_QUEUE_LENGTH
.loop
and [hl]
inc hl
dec c
jr nz, .loop
cp $ff
pop bc
pop hl
ret
Func_3b6a: ; 3b6a (0:3b6a)
ld [wTempAnimation], a ; hold an animation temporarily
ldh a, [hBankROM]
push af
ld [wd4be], a
push hl
push bc
push de
ld a, $07
call BankswitchROM
ld a, [wTempAnimation]
cp $61
jr nc, .asm_3b90
ld hl, wd4ad
ld a, [wd4ac]
cp [hl]
jr nz, .asm_3b90
call CheckAnyAnimationPlaying
jr nc, .asm_3b95
.asm_3b90
call $4a31
jr .asm_3b9a
.asm_3b95
call $48ef
jr .asm_3b9a
.asm_3b9a
pop de
pop bc
pop hl
pop af
call BankswitchROM
ret
Func_3ba2: ; 3ba2 (0:3ba2)
ldh a, [hBankROM]
push af
ld a, $07
call BankswitchROM
call $4ac5
call Func_3cb4
pop af
call BankswitchROM
ret
Func_3bb5: ; 3bb5 (0:3bb5)
xor a
ld [wd4c0], a
ldh a, [hBankROM]
push af
ld a, [wd4be]
call BankswitchROM
call Func_3cb4
call CallHL2
pop af
call BankswitchROM
ld a, $80
ld [wd4c0], a
ret
; writes from hl the pointer to the function to be called by DoFrame
SetDoFrameFunction: ; 3bd2 (0:3bd2)
ld a, l
ld [wDoFrameFunction], a
ld a, h
ld [wDoFrameFunction + 1], a
ret
ResetDoFrameFunction: ; 3bdb (0:3bdb)
push hl
ld hl, $0000
call SetDoFrameFunction
pop hl
ret
Func_3be4: ; 3be4 (0:3be4)
ldh a, [hBankROM]
push af
ld a, [wTempPointerBank]
call BankswitchROM
call Func_08de
pop af
call BankswitchROM
ret
; Copies bc bytes from [wTempPointer] to de
CopyBankedDataToDE: ; 3bf5 (0:3bf5)
ldh a, [hBankROM]
push af
push hl
ld a, [wTempPointerBank]
call BankswitchROM
ld a, [wTempPointer]
ld l, a
ld a, [wTempPointer + 1]
ld h, a
call CopyDataHLtoDE_SaveRegisters
pop hl
pop af
call BankswitchROM
ret
; fill bc bytes of data at hl with a
FillMemoryWithA: ; 3c10 (0:3c10)
push hl
push de
push bc
ld e, a
.loop
ld [hl], e
inc hl
dec bc
ld a, b
or c
jr nz, .loop
pop bc
pop de
pop hl
ret
; fill 2*bc bytes of data at hl with d,e
FillMemoryWithDE: ; 3c1f (0:3c1f)
push hl
push bc
.loop
ld [hl], e
inc hl
ld [hl], d
inc hl
dec bc
ld a, b
or c
jr nz, .loop
pop bc
pop hl
ret
Func_3c2d: ; 3c2d (0:3c2d)
push hl
push af
ldh a, [hBankROM]
push af
push hl
ld hl, sp+$05
ld a, [hl]
call BankswitchROM
pop hl
ld a, [hl]
ld hl, sp+$03
ld [hl], a
pop af
call BankswitchROM
pop af
pop hl
ret
CallHL2: ; 3c45 (0:3c45)
jp hl
CallBC: ; 3c46 (0:3c46)
retbc
DoFrameIfLCDEnabled: ; 3c48 (0:3c48)
push af
ld a, [rLCDC]
bit LCDC_ENABLE_F, a
jr z, .done
push bc
push de
push hl
call DoFrame
pop hl
pop de
pop bc
.done
pop af
ret
; divides BC by DE. Stores result in BC and stores remainder in HL
DivideBCbyDE: ; 3c5a (0:3c5a)
ld hl, $0000
rl c
rl b
ld a, $10
.asm_3c63
ldh [hffb6], a
rl l
rl h
push hl
ld a, l
sub e
ld l, a
ld a, h
sbc d
ccf
jr nc, .asm_3c78
ld h, a
add sp, $2
scf
jr .asm_3c79
.asm_3c78
pop hl
.asm_3c79
rl c
rl b
ldh a, [hffb6]
dec a
jr nz, .asm_3c63
ret
CallPlaySong: ; 3c83 (0:3c83)
call PlaySong
ret
Func_3c87: ; 3c87 (0:3c87)
push af
call PauseSong
pop af
call PlaySong
call WaitForSongToFinish
call ResumeSong
ret
WaitForSongToFinish: ; 3c96 (0:3c96)
call DoFrameIfLCDEnabled
call AssertSongFinished
or a
jr nz, WaitForSongToFinish
ret
; clear [SOMETHING] - something relating to animations
Func_3ca0: ; 3ca0 (0:3ca0)
xor a
ld [wd5d7], a
Func_3ca4: ; 3ca4 (0:3ca4)
ldh a, [hBankROM]
push af
ld a, BANK(Func_1296e)
call BankswitchROM
call Func_1296e
pop af
call BankswitchROM
ret
Func_3cb4: ; 3cb4 (0:3cb4)
ldh a, [hBankROM]
push af
ld a, BANK(HandleAllSpriteAnimations)
call BankswitchROM
call HandleAllSpriteAnimations
pop af
call BankswitchROM
ret
; hl - pointer to animation frame
; wd5d6 - bank of animation frame
DrawSpriteAnimationFrame: ; 3cc4 (0:3cc4)
ldh a, [hBankROM]
push af
ld a, [wd5d6]
call BankswitchROM
ld a, [wCurrSpriteXPos]
cp $f0
ld a, 0
jr c, .notNearRight
dec a
.notNearRight
ld [wCurrSpriteRightEdgeCheck], a
ld a, [wCurrSpriteYPos]
cp $f0
ld a, 0
jr c, .setBottomEdgeCheck
dec a
.setBottomEdgeCheck
ld [wCurrSpriteBottomEdgeCheck], a
ld a, [hli]
or a
jp z, .done
ld c, a
.loop
push bc
push hl
ld b, 0
bit 7, [hl]
jr z, .beginY
dec b
.beginY
ld a, [wCurrSpriteAttributes]
bit OAM_Y_FLIP, a
jr z, .unflippedY
ld a, [hl]
add 8 ; size of a tile
ld c, a
ld a, 0
adc b
ld b, a
ld a, [wCurrSpriteYPos]
sub c
ld e, a
ld a, [wCurrSpriteBottomEdgeCheck]
sbc b
jr .finishYPosition
.unflippedY
ld a, [wCurrSpriteYPos]
add [hl]
ld e, a
ld a, [wCurrSpriteBottomEdgeCheck]
adc b
.finishYPosition
or a
jr nz, .endCurrentIteration
inc hl
ld b, 0
bit 7, [hl]
jr z, .beginX
dec b
.beginX
ld a, [wCurrSpriteAttributes]
bit OAM_X_FLIP, a
jr z, .unflippedX
ld a, [hl]
add 8 ; size of a tile
ld c, a
ld a, 0
adc b
ld b, a
ld a, [wCurrSpriteXPos]
sub c
ld d, a
ld a, [wCurrSpriteRightEdgeCheck]
sbc b
jr .finishXPosition
.unflippedX
ld a, [wCurrSpriteXPos]
add [hl]
ld d, a
ld a, [wCurrSpriteRightEdgeCheck]
adc b
.finishXPosition
or a
jr nz, .endCurrentIteration
inc hl
ld a, [wCurrSpriteTileID]
add [hl]
ld c, a
inc hl
ld a, [wCurrSpriteAttributes]
add [hl]
and OAM_PALETTE | (1 << OAM_OBP_NUM)
ld b, a
ld a, [wCurrSpriteAttributes]
xor [hl]
and (1 << OAM_X_FLIP) | (1 << OAM_Y_FLIP) | (1 << OAM_PRIORITY)
or b
ld b, a
inc hl
call SetOneObjectAttributes
.endCurrentIteration
pop hl
ld bc, 4 ; size of info for one sub tile
add hl, bc
pop bc
dec c
jr nz, .loop
.done
pop af
call BankswitchROM
ret
; Loads a pointer to the current animation frame into SPRITE_ANIM_FRAME_DATA_POINTER using
; the current frame's offset
; [wd4ca] - current frame offset
; wTempPointer* - Pointer to current Animation
GetAnimationFramePointer: ; 3d72 (0:3d72)
ldh a, [hBankROM]
push af
push hl
push hl
ld a, [wd4ca]
cp $ff
jr nz, .useLoadedOffset
ld de, SpriteNullAnimationPointer
xor a
jr .loadPointer
.useLoadedOffset
ld a, [wTempPointer]
ld l, a
ld a, [wTempPointer + 1]
ld h, a
ld a, [wTempPointerBank]
call BankswitchROM
ld a, [hli]
push af
ld a, [wd4ca]
rlca
ld e, [hl]
add e
ld e, a
inc hl
ld a, [hl]
adc 0
ld d, a
pop af
.loadPointer
add BANK(SpriteNullAnimationPointer)
pop hl
ld bc, SPRITE_ANIM_FRAME_BANK
add hl, bc
ld [hli], a
call BankswitchROM
ld a, [de]
ld [hli], a
inc de
ld a, [de]
ld [hl], a
pop hl
pop af
call BankswitchROM
ret
GetFirstSpriteAnimBufferProperty: ; 3db7 (0:3db7)
push bc
ld c, SPRITE_ANIM_ENABLED
call GetSpriteAnimBufferProperty
pop bc
ret
; return hl pointing to the property (byte) c of a sprite in wSpriteAnimBuffer.
; the sprite is identified by its index in wWhichSprite.
GetSpriteAnimBufferProperty: ; 3dbf (0:3dbf)
ld a, [wWhichSprite]
; fallthrough
GetSpriteAnimBufferProperty_SpriteInA:
cp SPRITE_ANIM_BUFFER_CAPACITY
jr c, .got_sprite
debug_ret
ld a, SPRITE_ANIM_BUFFER_CAPACITY - 1 ; default to last sprite
.got_sprite
push bc
swap a ; a *= SPRITE_ANIM_LENGTH
push af
and $f
ld b, a
pop af
and $f0
or c ; add the property offset
ld c, a
ld hl, wSpriteAnimBuffer
add hl, bc
pop bc
ret
Func_3ddb: ; 3ddb (0:3ddb)
push hl
push bc
ld c, SPRITE_ANIM_FLAGS
call GetSpriteAnimBufferProperty_SpriteInA
res 2, [hl]
pop bc
pop hl
ret
Func_3de7: ; 3de7 (0:3de7)
push hl
push bc
ld c, SPRITE_ANIM_FLAGS
call GetSpriteAnimBufferProperty_SpriteInA
set 2, [hl]
pop bc
pop hl
ret
Func_3df3: ; 3df3 (0:3df3)
push af
ldh a, [hBankROM]
push af
push hl
ld a, BANK(Func_12c7f)
call BankswitchROM
ld hl, sp+$5
ld a, [hl]
call Func_12c7f
call FlushAllPalettes
pop hl
pop af
call BankswitchROM
pop af
ld a, [wd61b]
ret
; draws player's portrait at b,c
Func_3e10: ; 3e10 (0:3e10)
ld a, $1
ld [wd61e], a
ld a, $62
; fallthrough
Func_3e17: ; 3e17 (0:3e17)
ld [wd131], a
ldh a, [hBankROM]
push af
ld a, $4
call BankswitchROM
call $6fc6
pop af
call BankswitchROM
ret
; draws opponent's portrait given in a at b,c
Func_3e2a: ; 3e2a (0:3e2a)
ld [wd61e], a
ld a, $63
jr Func_3e17
Func_3e31: ; 3e31 (0:3e31)
ldh a, [hBankROM]
push af
call Func_3cb4
ld a, $20
call BankswitchROM
call $44d8
pop af
call BankswitchROM
ret
; something window scroll
Func_3e44: ; 3e44 (0:3e44)
push af
push hl
push bc
push de
ld hl, wd657
bit 0, [hl]
jr nz, .done
set 0, [hl]
ld b, $00
ld hl, wd658
ld c, [hl]
inc [hl]
ld hl, wd64b
add hl, bc
ld a, [hl]
ldh [rWX], a
ld hl, rLCDC
cp $a7
jr c, .disable_sprites
set 1, [hl] ; enable sprites
jr .asm_3e6c
.disable_sprites
res 1, [hl] ; disable sprites
.asm_3e6c
ld hl, wd651
add hl, bc
ld a, [hl]
cp $8f
jr c, .asm_3e9a
ld a, [wd665]
or a
jr z, .asm_3e93
ld hl, wd659
ld de, wd64b
ld bc, $6
call CopyDataHLtoDE
ld hl, wd65f
ld de, wd651
ld bc, $6
call CopyDataHLtoDE
.asm_3e93
xor a
ld [wd665], a
ld [wd658], a
.asm_3e9a
ldh [rLYC], a
ld hl, wd657
res 0, [hl]
.done
pop de
pop bc
pop hl
pop af
ret
; apply background scroll for lines 0 to 96 using the values at BGScrollData
; skip if wApplyBGScroll is non-0
ApplyBackgroundScroll: ; 3ea6 (0:3ea6)
push af
push hl
call DisableInt_LYCoincidence
ld hl, rSTAT
res STAT_LYCFLAG, [hl] ; reset coincidence flag
ei
ld hl, wApplyBGScroll
ld a, [hl]
or a
jr nz, .done
inc [hl]
push bc
push de
xor a
ld [wNextScrollLY], a
.ly_loop
ld a, [wNextScrollLY]
ld b, a
.wait_ly
ldh a, [rLY]
cp $60
jr nc, .ly_over_0x60
cp b ; already hit LY=b?
jr c, .wait_ly
call GetNextBackgroundScroll
ld hl, rSTAT
.wait_hblank_or_vblank
bit STAT_BUSY, [hl]
jr nz, .wait_hblank_or_vblank
ldh [rSCX], a
ldh a, [rLY]
inc a
ld [wNextScrollLY], a
jr .ly_loop
.ly_over_0x60
xor a
ldh [rSCX], a
ld a, $00
ldh [rLYC], a
call GetNextBackgroundScroll
ldh [hSCX], a
pop de
pop bc
xor a
ld [wApplyBGScroll], a
call EnableInt_LYCoincidence
.done
pop hl
pop af
ret
BGScrollData: ; 3ef8 (0:3ef8)
db 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3
db 4, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1, 1, 0, 0
db 0, -1, -1, -1, -2, -2, -2, -3, -3, -3, -4, -4, -4, -4, -4, -4
db -5, -4, -4, -4, -4, -4, -4, -3, -3, -3, -2, -2, -2, -1, -1, -1
; 3f38
; x = BGScrollData[(wVBlankCounter + a) & $3f]
; return, in register a, x rotated right [wBGScrollMod]-1 times (max 3 times)
GetNextBackgroundScroll: ; 3f38 (0:3f38)
ld hl, wVBlankCounter
add [hl]
and $3f
ld c, a
ld b, $00
ld hl, BGScrollData
add hl, bc
ld a, [wBGScrollMod]
ld c, a
ld a, [hl]
dec c
jr z, .done
dec c
jr z, .halve
dec c
jr z, .quarter
; effectively zero
sra a
.quarter
sra a
.halve
sra a
.done
ret
; enable lcdc interrupt on LYC=LC coincidence
EnableInt_LYCoincidence: ; 3f5a (0:3f5a)
push hl
ld hl, rSTAT
set STAT_LYC, [hl]
xor a
ld hl, rIE
set INT_LCD_STAT, [hl]
pop hl
ret
; disable lcdc interrupt and the LYC=LC coincidence trigger
DisableInt_LYCoincidence: ; 3f68 (0:3f68)
push hl
ld hl, rSTAT
res STAT_LYC, [hl]
xor a
ld hl, rIE
res INT_LCD_STAT, [hl]
pop hl
ret
rept $6a
db $ff
endr
; jumps to 3f:hl, then switches to bank 3d
Bankswitch3dTo3f:: ; 3fe0 (0:3fe0)
push af
ld a, $3f
ldh [hBankROM], a
ld [MBC3RomBank], a
pop af
ld bc, .bankswitch3d
push bc
jp hl
.bankswitch3d
ld a, $3d
ldh [hBankROM], a
ld [MBC3RomBank], a
ret
rept $a
db $ff
endr