pokecrystal-board/engine/link/mystery_gift.asm

1855 lines
33 KiB
NASM

; hMGRole values
IR_RECEIVER EQU 1
IR_SENDER EQU 2
; hMGStatusFlags error bits
MG_WRONG_CHECKSUM_F EQU 0
MG_TIMED_OUT_F EQU 1
MG_CANCELED_F EQU 4
MG_WRONG_PREFIX_F EQU 7
; hMGStatusFlags values
MG_WRONG_CHECKSUM EQU 1 << MG_WRONG_CHECKSUM_F
MG_TIMED_OUT EQU 1 << MG_TIMED_OUT_F
MG_CANCELED EQU 1 << MG_CANCELED_F
MG_WRONG_PREFIX EQU 1 << MG_WRONG_PREFIX_F
MG_NOT_OKAY EQU MG_WRONG_CHECKSUM | MG_TIMED_OUT | MG_CANCELED | MG_WRONG_PREFIX
MG_OKAY EQU $ff ^ MG_NOT_OKAY
MG_START_END EQU %11111111
REGION_PREFIX EQU $96
REGION_CODE EQU $90 ; USA
MESSAGE_PREFIX EQU $5a
DoMysteryGift:
call ClearTilemap
call ClearSprites
call WaitBGMap
call InitMysteryGiftLayout
hlcoord 3, 8
ld de, .String_PressAToLink_BToCancel
call PlaceString
call WaitBGMap
; Prepare the first of two messages for wMysteryGiftPartnerData
farcall StageDataForMysteryGift
call ClearMysteryGiftTrainer
ld a, 2
ld [wMysteryGiftMessageCount], a
ld a, wMysteryGiftPartnerDataEnd - wMysteryGiftPartnerData
ld [wMysteryGiftStagedDataLength], a
ldh a, [rIE]
push af
call ExchangeMysteryGiftData
ld d, a
xor a
ldh [rIF], a
pop af
ldh [rIE], a
push de
call ClearTilemap
call EnableLCD
call WaitBGMap
ld b, SCGB_DIPLOMA
call GetSGBLayout
call SetPalettes
pop de
hlcoord 2, 8
ld a, d
ld de, .MysteryGiftCanceledText ; Link has been canceled
cp MG_CANCELED
jp z, .LinkCanceled
cp MG_OKAY
jp nz, .CommunicationError
ld a, [wMysteryGiftGameVersion]
cp POKEMON_PIKACHU_2_VERSION
jr z, .skip_checks
call .CheckAlreadyGotFiveGiftsToday
ld hl, .MysteryGiftFiveADayText ; Only 5 gifts a day
jp nc, .PrintTextAndExit
call .CheckAlreadyGotAGiftFromThatPerson
ld hl, .MysteryGiftOneADayText ; Only one gift a day per person
jp c, .PrintTextAndExit
.skip_checks
ld a, [wMysteryGiftPlayerBackupItem]
and a
jp nz, .GiftWaiting
ld a, [wMysteryGiftPartnerBackupItem]
and a
jp nz, .FriendNotReady
ld a, [wMysteryGiftGameVersion]
cp POKEMON_PIKACHU_2_VERSION
jr z, .skip_append_save
call .AddMysteryGiftPartnerID
ld a, [wMysteryGiftGameVersion]
cp 4 ; ???
jr z, .skip_append_save
call .SaveMysteryGiftTrainerName
farcall RestoreMobileEventIndex
farcall StubbedTrainerRankings_MysteryGift
farcall BackupMobileEventIndex
.skip_append_save
ld a, [wMysteryGiftPartnerSentDeco]
and a
jr z, .SentItem
; sent decoration
ld a, [wMysteryGiftPartnerWhichDeco]
ld c, a
farcall MysteryGiftGetDecoration
push bc
call CheckAndSetMysteryGiftDecorationAlreadyReceived
pop bc
jr nz, .SentItem
; keep the decoration if it wasn't already received
callfar GetDecorationName_c
ld h, d
ld l, e
ld de, wStringBuffer1
ld bc, ITEM_NAME_LENGTH
call CopyBytes
ld hl, .MysteryGiftSentHomeText ; sent decoration to home
jr .PrintTextAndExit
.SentItem:
call GetMysteryGiftBank
ld a, [wMysteryGiftPartnerWhichItem]
ld c, a
farcall MysteryGiftGetItem
ld a, c
ld [sBackupMysteryGiftItem], a
ld [wNamedObjectIndexBuffer], a
call CloseSRAM
call GetItemName
ld hl, .MysteryGiftSentText ; sent item/decoration
jr .PrintTextAndExit
.LinkCanceled:
ld hl, .MysteryGiftCanceledText ; Link has been canceled
jr .PrintTextAndExit
.CommunicationError:
ld hl, .MysteryGiftCommErrorText ; Communication error
call PrintText
jp DoMysteryGift
.GiftWaiting:
ld hl, .RetrieveMysteryGiftText ; receive gift at counter
jr .PrintTextAndExit
.FriendNotReady:
ld hl, .YourFriendIsNotReadyText ; friend not ready
; fallthrough
.PrintTextAndExit:
call PrintText
ld a, LCDC_DEFAULT
ldh [rLCDC], a
ret
.String_PressAToLink_BToCancel:
db "Press A to"
next "link IR-Device"
next "Press B to"
next "cancel it."
db "@"
.MysteryGiftCanceledText:
text_far _MysteryGiftCanceledText
text_end
.MysteryGiftCommErrorText:
text_far _MysteryGiftCommErrorText
text_end
.RetrieveMysteryGiftText:
text_far _RetrieveMysteryGiftText
text_end
.YourFriendIsNotReadyText:
text_far _YourFriendIsNotReadyText
text_end
.MysteryGiftFiveADayText:
text_far _MysteryGiftFiveADayText
text_end
.MysteryGiftOneADayText:
text_far _MysteryGiftOneADayText
text_end
.MysteryGiftSentText:
text_far _MysteryGiftSentText
text_end
.MysteryGiftSentHomeText:
text_far _MysteryGiftSentHomeText
text_end
.CheckAlreadyGotFiveGiftsToday:
call GetMysteryGiftBank
ld a, [sNumDailyMysteryGiftPartnerIDs]
cp MAX_MYSTERY_GIFT_PARTNERS
jp CloseSRAM
.CheckAlreadyGotAGiftFromThatPerson:
call GetMysteryGiftBank
ld a, [wMysteryGiftPartnerID]
ld b, a
ld a, [wMysteryGiftPartnerID + 1]
ld c, a
ld a, [sNumDailyMysteryGiftPartnerIDs]
ld d, a
ld hl, sDailyMysteryGiftPartnerIDs
.loop
ld a, d
and a
jr z, .No
ld a, [hli]
cp b
jr nz, .skip
ld a, [hl]
cp c
jr z, .Yes
.skip
inc hl
dec d
jr .loop
.Yes:
scf
.No:
jp CloseSRAM
.AddMysteryGiftPartnerID:
call GetMysteryGiftBank
ld hl, sNumDailyMysteryGiftPartnerIDs
ld a, [hl]
inc [hl]
ld hl, sDailyMysteryGiftPartnerIDs ; could have done "inc hl" instead
ld e, a
ld d, 0
add hl, de
add hl, de
ld a, [wMysteryGiftPartnerID]
ld [hli], a
ld a, [wMysteryGiftPartnerID + 1]
ld [hl], a
jp CloseSRAM
.SaveMysteryGiftTrainerName:
call GetMysteryGiftBank
ld a, TRUE
ld [sMysteryGiftTrainerHouseFlag], a
ld hl, wMysteryGiftPartnerName
ld de, sMysteryGiftPartnerName
ld bc, NAME_LENGTH
call CopyBytes
assert sMysteryGiftPartnerName + NAME_LENGTH == sMysteryGiftUnusedFlag
ld a, TRUE
ld [de], a
inc de
assert sMysteryGiftUnusedFlag + 1 == sMysteryGiftTrainer
ld hl, wMysteryGiftTrainer
ld bc, wMysteryGiftTrainerEnd - wMysteryGiftTrainer
call CopyBytes
jp CloseSRAM
ExchangeMysteryGiftData:
di
farcall ClearChannels
call InitializeMysteryGiftInterrupts
.restart
call BeginIRCommunication
call InitializeIRCommunicationRoles
ldh a, [hMGStatusFlags]
cp MG_CANCELED
jp z, EndOrContinueIRCommunication
cp MG_OKAY
jr nz, .restart
ldh a, [hMGRole]
cp IR_SENDER
jr z, SenderExchangeMysteryGiftDataPayloads
; receiver
ld hl, hMGExchangedByte
ld b, 1
call TryReceivingIRDataBlock
jr nz, .failed
call ReceiveIRDataPayload_GotRegionPrefix
jp nz, EndOrContinueIRCommunication
jr ReceiverExchangeMysteryGiftDataPayloads_GotPayload
.failed
; Delay frame
.wait_frame
ldh a, [rLY]
cp LY_VBLANK
jr c, .wait_frame
ld c, LOW(rRP)
ld a, rRP_ENABLE_READ_MASK
ldh [c], a
ld b, 60 * 4 ; 4 seconds
.continue
push bc
call MysteryGift_UpdateJoypad
ld b, 1 << rRP_RECEIVING
ld c, LOW(rRP)
.in_vblank
ldh a, [c]
and b
ld b, a
ldh a, [rLY]
cp LY_VBLANK
jr nc, .in_vblank
.wait_vblank
ldh a, [c]
and b
ld b, a
ldh a, [rLY]
cp LY_VBLANK
jr c, .wait_vblank
ld a, b
pop bc
; Restart if the 4-second timeout has elapsed
dec b
jr z, .restart
; Restart if rRP is not receiving data
or a
jr nz, .restart
; Check if we've pressed the B button to cancel
ldh a, [hMGJoypadReleased]
bit B_BUTTON_F, a
jr z, .continue
ld a, MG_CANCELED
ldh [hMGStatusFlags], a
jp EndOrContinueIRCommunication
ReceiverExchangeMysteryGiftDataPayloads:
; Receive the data payload
call ReceiveIRDataPayload
jp nz, EndOrContinueIRCommunication
; fallthrough
ReceiverExchangeMysteryGiftDataPayloads_GotPayload:
; Switch roles
call BeginSendingIRCommunication
jp nz, EndOrContinueIRCommunication
; Send the data payload
call SendIRDataPayload
jp nz, EndOrContinueIRCommunication
; Switch roles
call BeginReceivingIRCommunication
jp nz, EndOrContinueIRCommunication
; Receive an empty block
call ReceiveEmptyIRDataBlock
jp EndOrContinueIRCommunication
SenderExchangeMysteryGiftDataPayloads:
; Send the data payload
call SendIRDataPayload
jp nz, EndOrContinueIRCommunication
; Switch roles
call BeginReceivingIRCommunication
jp nz, EndOrContinueIRCommunication
; Receive the data payload
call ReceiveIRDataPayload
jp nz, EndOrContinueIRCommunication
; Switch roles
call BeginSendingIRCommunication
jp nz, EndOrContinueIRCommunication
; Send an empty block
call SendEmptyIRDataBlock
jp EndOrContinueIRCommunication
ReceiveIRDataPayload:
; Receive the region prefix
ld hl, hMGExchangedByte
ld b, 1
call TryReceivingIRDataBlock
ret nz
; fallthrough
ReceiveIRDataPayload_GotRegionPrefix:
; Receive an empty block
call ReceiveEmptyIRDataBlock
ldh a, [hMGStatusFlags]
cp MG_OKAY
ret nz
; Verify the received region prefix
ldh a, [hMGExchangedByte]
cp REGION_PREFIX
jp nz, WrongMysteryGiftRegion
ld a, REGION_CODE
ldh [hMGExchangedByte], a
; Switch roles
call BeginSendingIRCommunication
ret nz
; Send the region code
ld hl, hMGExchangedByte
ld b, 1
call TrySendingIRDataBlock
ret nz
; Send an empty block
call SendEmptyIRDataBlock
ldh a, [hMGStatusFlags]
cp MG_OKAY
ret nz
; Switch roles
call BeginReceivingIRCommunication
ret nz
; Receive the staged data
ld hl, wMysteryGiftTrainer
ld a, [wMysteryGiftStagedDataLength]
ld b, a
call TryReceivingIRDataBlock
ret nz
; Receive an empty block
call ReceiveEmptyIRDataBlock
ldh a, [hMGStatusFlags]
cp MG_OKAY
ret
SendIRDataPayload:
; Send the region prefix
ld a, REGION_PREFIX
ldh [hMGExchangedByte], a
ld hl, hMGExchangedByte
ld b, 1
call TrySendingIRDataBlock
ret nz
; Send an empty block
call SendEmptyIRDataBlock
ldh a, [hMGStatusFlags]
cp MG_OKAY
ret nz
; Switch roles
call BeginReceivingIRCommunication
ret nz
; Receive the region code
ld hl, hMGExchangedByte
ld b, 1
call TryReceivingIRDataBlock
ret nz
; Receive an empty block
call ReceiveEmptyIRDataBlock
ldh a, [hMGStatusFlags]
cp MG_OKAY
ret nz
; Verify the received region code
ldh a, [hMGExchangedByte]
cp REGION_CODE
jp nz, WrongMysteryGiftRegion
; Switch roles
call BeginSendingIRCommunication
ret nz
; Send the staged data
ld hl, wMysteryGiftStaging
ld a, [wMysteryGiftStagedDataLength]
ld b, a
call TrySendingIRDataBlock
ret nz
; Send an empty block
call SendEmptyIRDataBlock
ldh a, [hMGStatusFlags]
cp MG_OKAY
ret
EndOrContinueIRCommunication:
nop
ldh a, [hMGStatusFlags]
; Quit if player canceled
cp MG_CANCELED
jr z, .quit
; Quit if there was a communication error
cp MG_OKAY
jr nz, .quit
; Quit if all messages are sent/received
ld hl, wMysteryGiftMessageCount
dec [hl]
jr z, .quit
; Quit if communicating with Pokémon Pikachu 2 device
ld hl, wMysteryGiftTrainer
ld de, wMysteryGiftPartnerData
ld bc, wMysteryGiftPartnerDataEnd - wMysteryGiftPartnerData
call CopyBytes
ld a, [wMysteryGiftTrainer] ; first byte is the version
cp POKEMON_PIKACHU_2_VERSION
jr nc, .quit
; Prepare the second message for wMysteryGiftTrainer
farcall StagePartyDataForMysteryGift
call ClearMysteryGiftTrainer
ld a, wMysteryGiftTrainerEnd - wMysteryGiftTrainer
ld [wMysteryGiftStagedDataLength], a
ldh a, [hMGRole]
cp IR_SENDER
jr z, .sender
; receiver
call BeginReceivingIRCommunication
jr nz, EndOrContinueIRCommunication
jp ReceiverExchangeMysteryGiftDataPayloads
.sender
call BeginSendingIRCommunication
jr nz, EndOrContinueIRCommunication
jp SenderExchangeMysteryGiftDataPayloads
.quit
ldh a, [hMGStatusFlags]
push af
call EndIRCommunication
xor a
ldh [rIF], a
ldh a, [rIE]
or 1 << VBLANK
ldh [rIE], a
ei
call DelayFrame
pop af
ret
Function104c2d:
di
farcall ClearChannels
call InitializeMysteryGiftInterrupts
.loop2
call BeginIRCommunication
call InitializeIRCommunicationRoles
ldh a, [hMGStatusFlags]
cp MG_CANCELED
jp z, Function104d1c
cp MG_OKAY
jr nz, .loop2
ldh a, [hMGRole]
cp IR_SENDER
jr z, .sender
; receiver
call Function104c8a
jp nz, Function104d1c
call BeginSendingIRCommunication
jp nz, Function104d1c
call Function104cd2
jp nz, Function104d1c
call BeginReceivingIRCommunication
jp nz, Function104d1c
call ReceiveEmptyIRDataBlock
jp Function104d1c
.sender
call Function104cd2
jp nz, Function104d1c
call BeginReceivingIRCommunication
jp nz, Function104d1c
call Function104c8a
jp nz, Function104d1c
call BeginSendingIRCommunication
jp nz, Function104d1c
call SendEmptyIRDataBlock
jp Function104d1c
Function104c8a:
ld hl, hMGExchangedByte
ld b, 1
call TryReceivingIRDataBlock
ret nz
call ReceiveEmptyIRDataBlock
ldh a, [hMGStatusFlags]
cp MG_OKAY
ret nz
ldh a, [hMGExchangedByte]
cp $3c
jp nz, WrongMysteryGiftRegion
swap a
ldh [hMGExchangedByte], a
call BeginSendingIRCommunication
ret nz
ld hl, hMGExchangedByte
ld b, 1
call TrySendingIRDataBlock
ret nz
call SendEmptyIRDataBlock
ldh a, [hMGStatusFlags]
cp MG_OKAY
ret nz
call BeginReceivingIRCommunication
ret nz
ld hl, wMysteryGiftTrainer
ld a, [wMysteryGiftStagedDataLength]
ld b, a
call TryReceivingIRDataBlock
ret nz
call ReceiveEmptyIRDataBlock
ldh a, [hMGStatusFlags]
cp MG_OKAY
ret
Function104cd2:
ld a, $3c
ldh [hMGExchangedByte], a
ld hl, hMGExchangedByte
ld b, 1
call TrySendingIRDataBlock
ret nz
call SendEmptyIRDataBlock
ldh a, [hMGStatusFlags]
cp MG_OKAY
ret nz
call BeginReceivingIRCommunication
ret nz
ld hl, hMGExchangedByte
ld b, 1
call TryReceivingIRDataBlock
ret nz
call ReceiveEmptyIRDataBlock
ldh a, [hMGStatusFlags]
cp MG_OKAY
ret nz
ldh a, [hMGExchangedByte]
swap a
cp $3c
jp nz, WrongMysteryGiftRegion
call BeginSendingIRCommunication
ret nz
ld hl, wMysteryGiftStaging
ld a, [wMysteryGiftStagedDataLength]
ld b, a
call TrySendingIRDataBlock
ret nz
call SendEmptyIRDataBlock
ldh a, [hMGStatusFlags]
cp MG_OKAY
ret
Function104d1c:
nop
ldh a, [hMGStatusFlags]
push af
call EndIRCommunication
xor a
ldh [rIF], a
ldh a, [rIE]
or 1 << VBLANK
ldh [rIE], a
ei
call DelayFrame
pop af
ret
WrongMysteryGiftRegion:
ld a, MG_WRONG_PREFIX
ldh [hMGStatusFlags], a
and a
ret
BeginSendingIRCommunication:
call BeginIRCommunication
call SendIRHelloMessage
ldh a, [hMGStatusFlags]
cp MG_OKAY
ret
BeginReceivingIRCommunication:
call BeginIRCommunication
call ReceiveIRHelloMessage
ldh a, [hMGStatusFlags]
cp MG_OKAY
ret
TrySendingIRDataBlock:
call SendIRDataBlock
ldh a, [hMGStatusFlags]
cp MG_OKAY
ret
TryReceivingIRDataBlock:
call ReceiveIRDataBlock
ldh a, [hMGStatusFlags]
cp MG_OKAY
ret
InitializeMysteryGiftInterrupts:
call StartFastIRTimer
ld a, 1 << TIMER
ldh [rIE], a
xor a
ldh [rIF], a
call BeginIRCommunication
; waits for ~$40400 cycles = ~0.25 seconds
xor a
ld b, a
.busy_wait
inc a
jr nz, .busy_wait
inc b
jr nz, .busy_wait
ret
StartFastIRTimer:
; Starts a 65,536 Hz timer that interrupts every 3 increments (21,845 Hz).
xor a
ldh [rTAC], a
ld a, -2
ldh [rTMA], a
ldh [rTIMA], a
ld a, rTAC_65536_HZ
ldh [rTAC], a
or 1 << rTAC_ON
ldh [rTAC], a
ret
StartSlowIRTimer:
; Starts a 65,536 Hz timer that interrupts every 256 increments (256 Hz).
xor a
ldh [rTAC], a
ldh [rTMA], a
ldh [rTIMA], a
ld a, rTAC_65536_HZ
ldh [rTAC], a
or 1 << rTAC_ON
ldh [rTAC], a
ret
BeginIRCommunication:
ld a, rRP_ENABLE_READ_MASK
call ToggleIRCommunication
ld a, IR_RECEIVER
ldh [hMGRole], a
ret
EndIRCommunication:
xor a
call ToggleIRCommunication
ld a, rTAC_65536_HZ
ldh [rTAC], a
ret
ReceiveInfraredLEDOn:
; Count interrupts of the partner's IR LED on; quit after 256-d interrupts.
.recv_loop
inc d
ret z
xor a
ldh [rIF], a
halt
ldh a, [c]
bit rRP_RECEIVING, a
jr z, .recv_loop
or a
ret
ReceiveInfraredLEDOff:
; Count interrupts of the partner's IR LED off; quit after 256-d interrupts.
.no_recv_loop
inc d
ret z
xor a
ldh [rIF], a
halt
ldh a, [c]
bit rRP_RECEIVING, a
jr nz, .no_recv_loop
or a
ret
SendInfraredLEDOn:
; Holds the IR LED on for d-1 interrupts.
ld a, rRP_ENABLE_READ_MASK | (1 << rRP_LED_ON)
ldh [c], a
.wait
dec d
ret z
xor a
ldh [rIF], a
halt
jr .wait
SendInfraredLEDOff:
; Holds the IR LED off for d-1 interrupts.
ld a, rRP_ENABLE_READ_MASK
ldh [c], a
.wait
dec d
ret z
xor a
ldh [rIF], a
halt
jr .wait
InitializeIRCommunicationRoles:
ld d, 0
ld e, d
ld a, IR_RECEIVER
ldh [hMGRole], a
.loop
call MysteryGift_UpdateJoypad
ld b, 1 << rRP_RECEIVING
ld c, LOW(rRP)
; Check if we've pressed the B button to cancel
ldh a, [hMGJoypadReleased]
bit B_BUTTON_F, a
jr z, .not_canceled
ld a, MG_CANCELED
ldh [hMGStatusFlags], a
ret
.not_canceled
; Check if we've pressed the A button to start sending
bit A_BUTTON_F, a
jr nz, SendIRHelloMessageAfterDelay
; If rRP is not receiving data, keep checking for input
ldh a, [c]
and b
jr nz, .loop
; fallthrough
ReceiveIRHelloMessage:
ld c, LOW(rRP)
ld d, 0
ld e, d
call ReceiveInfraredLEDOff
jp z, InfraredLEDReceiveTimedOut
ld d, e
call ReceiveInfraredLEDOn
jp z, InfraredLEDReceiveTimedOut
call ReceiveInfraredLEDOff
jp z, InfraredLEDReceiveTimedOut
call ReceiveInfraredLEDOn
jp z, InfraredLEDReceiveTimedOut
ld a, MG_OKAY
ldh [hMGStatusFlags], a
ld d, 61
call SendInfraredLEDOff
ld d, 5
call SendInfraredLEDOn
ld d, 21
call SendInfraredLEDOff
ld d, 5
call SendInfraredLEDOn
ld d, 5
call SendInfraredLEDOff
ret
SendIRHelloMessageAfterDelay:
; Wait a random amount of time
call Random
ld e, a
and $f
ld d, a
.wait_loop
dec de
ld a, d
or e
jr nz, .wait_loop
; fallthrough
SendIRHelloMessage:
ld a, IR_SENDER
ldh [hMGRole], a
ld c, LOW(rRP)
ld d, 0
ld e, d
ld d, 61
call SendInfraredLEDOff
ld d, 5
call SendInfraredLEDOn
ld d, 21
call SendInfraredLEDOff
ld d, 5
call SendInfraredLEDOn
ld d, 5
call SendInfraredLEDOff
ld d, e
call ReceiveInfraredLEDOff
jp z, InfraredLEDReceiveTimedOut
ld d, e
call ReceiveInfraredLEDOn
jp z, InfraredLEDReceiveTimedOut
call ReceiveInfraredLEDOff
jp z, InfraredLEDReceiveTimedOut
call ReceiveInfraredLEDOn
jp z, InfraredLEDReceiveTimedOut
ld d, 61
call SendInfraredLEDOff
ld a, MG_OKAY
ldh [hMGStatusFlags], a
ret
ToggleIRCommunication:
ldh [rRP], a
ld a, MG_START_END
ldh [hMGStatusFlags], a
ret
SendIRDataBlock:
; Send b bytes of data in three messages:
; 1. two bytes: MESSAGE_PREFIX and the length b
; 2. b bytes: the actual data
; 3. two bytes: a little-endian checksum
; Then receive a one-byte acknowledgement message: the status.
xor a
ldh [hMGChecksum + 0], a
ldh [hMGChecksum + 1], a
push hl
push bc
ld c, LOW(rRP)
ld d, 61
call SendInfraredLEDOff
ld hl, hMGExchangedWord
ld a, MESSAGE_PREFIX
ld [hli], a
ld [hl], b
dec hl
ld b, 2
call SendIRDataMessage
pop bc
pop hl
call SendIRDataMessage
ldh a, [hMGChecksum + 0]
ldh [hMGExchangedWord + 0], a
ldh a, [hMGChecksum + 1]
ldh [hMGExchangedWord + 1], a
push hl
ld hl, hMGExchangedWord
ld b, 2
call SendIRDataMessage
ld hl, hMGStatusFlags
ld b, 1
call ReceiveIRDataMessage
ldh a, [hMGExchangedWord + 0]
ldh [hMGChecksum + 0], a
ldh a, [hMGExchangedWord + 1]
ldh [hMGChecksum + 1], a
pop hl
ret
SendIRDataMessage:
; Send b bytes of data one bit at a time, and update the checksum.
ld c, LOW(rRP)
ld d, 5
call SendInfraredLEDOff
ld d, 5
call SendInfraredLEDOn
ld d, 21
call SendInfraredLEDOff
; b = -b - 1; then count up to 0
ld a, b
cpl
ld b, a
ld a, -12
ldh [rTMA], a
.byte_loop
inc b
jr z, .done
ld a, 8
ldh [hMGNumBits], a
; Get the next data byte
ld a, [hli]
ld e, a
; Add the next data byte to the checksum
ldh a, [hMGChecksum + 0]
add e
ldh [hMGChecksum + 0], a
ldh a, [hMGChecksum + 1]
adc 0
ldh [hMGChecksum + 1], a
; Send each bit of the byte
.bit_loop
xor a
ldh [rIF], a
halt
ld a, rRP_ENABLE_READ_MASK | (1 << rRP_LED_ON)
ldh [rRP], a
; Turn the LED off for longer if the bit is 1
ld d, 1
ld a, e
rlca
ld e, a
jr nc, .wait
inc d
.wait
ldh a, [rTIMA]
cp -8
jr c, .wait
ld a, rRP_ENABLE_READ_MASK
ldh [rRP], a
dec d
jr z, .no_halt
xor a
ldh [rIF], a
halt
.no_halt
ldh a, [hMGNumBits]
dec a
jr z, .byte_loop
ldh [hMGNumBits], a
jr .bit_loop
.done
ld a, -2
ldh [rTMA], a
xor a
ldh [rIF], a
halt
ld d, 5
call SendInfraredLEDOn
ld d, 17
call SendInfraredLEDOff
ret
InfraredLEDReceiveTimedOut:
ldh a, [hMGStatusFlags]
or MG_TIMED_OUT
ldh [hMGStatusFlags], a
ret
ReceivedWrongIRChecksum:
ldh a, [hMGStatusFlags]
or MG_WRONG_CHECKSUM
ldh [hMGStatusFlags], a
ret
ReceivedWrongIRMessagePrefix:
ldh a, [hMGStatusFlags]
or MG_WRONG_PREFIX
ldh [hMGStatusFlags], a
ret
ReceiveIRDataBlock:
; Receive b bytes of data in three messages:
; 1. two bytes: MESSAGE_PREFIX and the length b
; 2. b bytes: the actual data
; 3. two bytes: a little-endian checksum
; Then send a one-byte acknowledgement message: the status.
xor a
ldh [hMGChecksum + 0], a
ldh [hMGChecksum + 1], a
push bc
push hl
ld hl, hMGExchangedWord
ld b, 2
call ReceiveIRDataMessage
ldh a, [hMGExchangedWord + 1]
ldh [hMGUnusedMsgLength], a
ld b, a
pop hl
pop af
cp b
jp c, ReceivedWrongIRMessagePrefix
ldh a, [hMGExchangedWord + 0]
cp MESSAGE_PREFIX
jp nz, ReceivedWrongIRMessagePrefix
call ReceiveIRDataMessage
ldh a, [hMGChecksum + 0]
ld d, a
ldh a, [hMGChecksum + 1]
ld e, a
push hl
push de
ld hl, hMGExchangedWord
ld b, 2
call ReceiveIRDataMessage
pop de
ld hl, hMGExchangedWord
ld a, [hli]
xor d
ld b, a
ld a, [hl]
xor e
or b
call nz, ReceivedWrongIRChecksum
push de
ld d, 61
call SendInfraredLEDOff
ld hl, hMGStatusFlags
ld b, 1
call SendIRDataMessage
pop de
pop hl
ld a, d
ldh [hMGChecksum + 0], a
ld a, e
ldh [hMGChecksum + 1], a
ret
ReceiveIRDataMessage:
ld c, LOW(rRP)
ld d, 0
call ReceiveInfraredLEDOff
jp z, InfraredLEDReceiveTimedOut
ld d, 0
call ReceiveInfraredLEDOn
jp z, InfraredLEDReceiveTimedOut
ld d, 0
call ReceiveInfraredLEDOff
jp z, InfraredLEDReceiveTimedOut
ld a, b
cpl
ld b, a
xor a
ldh [hMGPrevTIMA], a
call StartSlowIRTimer
.main_loop
inc b
jr z, .done
ld a, 8
ldh [hMGNumBits], a
.inner_loop
ld d, 0
.recv_loop
inc d
jr z, .recv_done
ldh a, [c]
bit rRP_RECEIVING, a
jr z, .recv_loop
ld d, 0
.recv_done
.send_loop
inc d
jr z, .send_done
ldh a, [c]
bit rRP_RECEIVING, a
jr nz, .send_loop
.send_done
ldh a, [hMGPrevTIMA]
ld d, a
ldh a, [rTIMA]
ldh [hMGPrevTIMA], a
sub d
cp $12
jr c, .zero
set 0, e
jr .ok
.zero
res 0, e
.ok
ldh a, [hMGNumBits]
dec a
ldh [hMGNumBits], a
jr z, .continue
ld a, e
rlca
ld e, a
jr .inner_loop
.continue
ld a, e
ld [hli], a
ldh a, [hMGChecksum + 0]
add e
ldh [hMGChecksum + 0], a
ldh a, [hMGChecksum + 1]
adc 0
ldh [hMGChecksum + 1], a
jr .main_loop
.done
call StartFastIRTimer
xor a
ldh [rIF], a
ld d, 0
call ReceiveInfraredLEDOn
jp z, InfraredLEDReceiveTimedOut
ld d, 16
call SendInfraredLEDOff
ret
SendEmptyIRDataBlock:
ld b, 0
jp SendIRDataBlock
ReceiveEmptyIRDataBlock:
ld b, 0
jp ReceiveIRDataBlock
MysteryGift_UpdateJoypad:
; We can only get four inputs at a time.
; We take d-pad first for no particular reason.
ld a, R_DPAD
ldh [rJOYP], a
; Read twice to give the request time to take.
ldh a, [rJOYP]
ldh a, [rJOYP]
; The Joypad register output is in the lo nybble (inversed).
; We make the hi nybble of our new container d-pad input.
cpl
and $f
swap a
; We'll keep this in b for now.
ld b, a
; Buttons make 8 total inputs (A, B, Select, Start).
; We can fit this into one byte.
ld a, R_BUTTONS
ldh [rJOYP], a
; Wait for input to stabilize.
rept 6
ldh a, [rJOYP]
endr
; Buttons take the lo nybble.
cpl
and $f
or b
ld c, a
; To get the delta we xor the last frame's input with the new one.
ldh a, [hMGJoypadPressed]
xor c
; Released this frame:
and c
ldh [hMGJoypadReleased], a
; Pressed this frame:
ld a, c
ldh [hMGJoypadPressed], a
ld a, $30
; Reset the joypad register since we're done with it.
ldh [rJOYP], a
ret
CheckAndSetMysteryGiftDecorationAlreadyReceived:
; Return nz if decoration c was already received; otherwise receive it.
call GetMysteryGiftBank
ld d, 0
ld b, CHECK_FLAG
ld hl, sMysteryGiftDecorationsReceived
lda_predef SmallFarFlagAction
push hl
push bc
call Predef
call CloseSRAM
ld a, c
and a
pop bc
pop hl
ret nz
call GetMysteryGiftBank
ld b, SET_FLAG
predef SmallFarFlagAction
call CloseSRAM
xor a
ret
CopyMysteryGiftReceivedDecorationsToPC:
call GetMysteryGiftBank
ld c, 0
.loop
push bc
ld d, 0
ld b, CHECK_FLAG
ld hl, sMysteryGiftDecorationsReceived
predef SmallFarFlagAction
ld a, c
and a
pop bc
jr z, .skip
push bc
callfar SetSpecificDecorationFlag
pop bc
.skip
inc c
ld a, c
cp NUM_NON_TROPHY_DECOS
jr c, .loop
jp CloseSRAM
UnlockMysteryGift:
; If [sMysteryGiftUnlocked] was -1, this sets both
; [sMysteryGiftUnlocked] and [sMysteryGiftItem] to 0.
call GetMysteryGiftBank
ld hl, sMysteryGiftUnlocked
ld a, [hl]
inc a
jr nz, .ok
ld [hld], a
assert sMysteryGiftUnlocked - 1 == sMysteryGiftItem
ld [hl], a
.ok
jp CloseSRAM
ResetDailyMysteryGiftLimitIfUnlocked:
call GetMysteryGiftBank
ld a, [sNumDailyMysteryGiftPartnerIDs]
cp -1 ; locked?
jr z, .dont_clear
xor a
ld [sNumDailyMysteryGiftPartnerIDs], a
.dont_clear
jp CloseSRAM
BackupMysteryGift:
; Copies [sMysteryGiftItem] to [sBackupMysteryGiftItem],
; and [sMysteryGiftUnlocked] to [sNumDailyMysteryGiftPartnerIDs].
call GetMysteryGiftBank
ld hl, sMysteryGiftItem
ld de, sBackupMysteryGiftItem
ld a, [hli]
ld [de], a
inc de
assert sMysteryGiftItem + 1 == sMysteryGiftUnlocked
assert sBackupMysteryGiftItem + 1 == sNumDailyMysteryGiftPartnerIDs
ld a, [hl]
ld [de], a
jp CloseSRAM
RestoreMysteryGift:
; Copies [sBackupMysteryGiftItem] to [sMysteryGiftItem],
; and [sNumDailyMysteryGiftPartnerIDs] to [sMysteryGiftUnlocked].
call GetMysteryGiftBank
ld hl, sBackupMysteryGiftItem
ld de, sMysteryGiftItem
ld a, [hli]
ld [de], a
inc de
assert sBackupMysteryGiftItem + 1 == sNumDailyMysteryGiftPartnerIDs
assert sMysteryGiftItem + 1 == sMysteryGiftUnlocked
ld a, [hl]
ld [de], a
jp CloseSRAM
ClearMysteryGiftTrainer:
ld hl, wMysteryGiftTrainer
xor a
ld b, wMysteryGiftTrainerEnd - wMysteryGiftTrainer
.loop
ld [hli], a
dec b
jr nz, .loop
ret
GetMysteryGiftBank:
ld a, BANK(sMysteryGiftData)
jp OpenSRAM
StagePartyDataForMysteryGift:
; You will be sending this data to your mystery gift partner.
; Structure is the same as a trainer with species and moves
; defined.
ld a, BANK(sPokemonData)
call OpenSRAM
ld de, wMysteryGiftStaging
ld bc, sPokemonData + wPartyMons - wPokemonData
ld hl, sPokemonData + wPartySpecies - wPokemonData
.loop
ld a, [hli]
cp -1
jr z, .party_end
cp EGG
jr z, .next
push hl
; copy level
ld hl, MON_LEVEL
add hl, bc
ld a, [hl]
ld [de], a
inc de
; copy species
ld hl, MON_SPECIES
add hl, bc
ld a, [hl]
ld [de], a
inc de
; copy moves
ld hl, MON_MOVES
add hl, bc
push bc
ld bc, NUM_MOVES
call CopyBytes
pop bc
pop hl
.next
push hl
ld hl, PARTYMON_STRUCT_LENGTH
add hl, bc
ld b, h
ld c, l
pop hl
jr .loop
.party_end
ld a, -1
ld [de], a
ld a, wMysteryGiftTrainerEnd - wMysteryGiftTrainer
ld [wUnusedMysteryGiftStagedDataLength], a
jp CloseSRAM
InitMysteryGiftLayout:
call ClearBGPalettes
call DisableLCD
ld hl, MysteryGiftGFX
ld de, vTiles2 tile $00
ld a, BANK(MysteryGiftGFX)
ld bc, $43 tiles
call FarCopyBytes
hlcoord 0, 0
ld a, $42
ld bc, SCREEN_HEIGHT * SCREEN_WIDTH
call ByteFill
hlcoord 3, 7
lb bc, 9, 15
call ClearBox
hlcoord 0, 0
ld a, $0
ld [hli], a
inc a
ld [hl], a
hlcoord 0, 1
inc a
ld [hli], a
inc a
ld [hl], a
hlcoord 7, 1
ld a, $12
call .Load5GFX
hlcoord 2, 2
ld a, $17
call .Load16GFX
hlcoord 2, 3
ld a, $27
call .Load16GFX
hlcoord 9, 4
ld a, $37
ld [hli], a
inc a
ld [hl], a
hlcoord 1, 2
ld [hl], $4
hlcoord 1, 3
ld a, $5
call .Load14Column
ld a, $9
hlcoord 18, 5
call .Load11Column
hlcoord 2, 5
ld a, $b
call .Load16Row
hlcoord 2, 16
ld a, $7
call .Load16Row
hlcoord 2, 5
ld a, $d
call .Load5GFX
hlcoord 7, 5
ld [hl], $c
hlcoord 18, 5
ld [hl], $a
hlcoord 18, 16
ld [hl], $8
hlcoord 1, 16
ld [hl], $6
hlcoord 2, 6
ld a, $3a
call .Load16Row
hlcoord 2, 15
ld a, $40
call .Load16Row
hlcoord 2, 6
ld a, $3c
call .Load9Column
hlcoord 17, 6
ld a, $3e
call .Load9Column
hlcoord 2, 6
ld [hl], $39
hlcoord 17, 6
ld [hl], $3b
hlcoord 2, 15
ld [hl], $3f
hlcoord 17, 15
ld [hl], $41
call EnableLCD
call WaitBGMap
ld b, SCGB_MYSTERY_GIFT
call GetSGBLayout
call SetPalettes
ret
.Load5GFX:
ld b, 5
jr .gfx_loop
.Load6GFX: ; unreferenced
ld b, 6
jr .gfx_loop
.Load16GFX:
ld b, 16
.gfx_loop
ld [hli], a
inc a
dec b
jr nz, .gfx_loop
ret
.Load9Column:
ld b, 9
jr .col_loop
.Load11Column:
ld b, 11
jr .col_loop
.Load14Column:
ld b, 14
.col_loop
ld [hl], a
ld de, SCREEN_WIDTH
add hl, de
dec b
jr nz, .col_loop
ret
.Load16Row:
ld b, 16
.row_loop
ld [hli], a
dec b
jr nz, .row_loop
ret
MysteryGiftGFX:
INCBIN "gfx/mystery_gift/mystery_gift.2bpp"
Function105688:
call ClearTilemap
call ClearSprites
call WaitBGMap
call Function1057d7
hlcoord 3, 8
ld de, String_PressAToLink_BToCancel_JP
call PlaceString
call WaitBGMap
call Function10578c
call ClearMysteryGiftTrainer
ld a, $24
ld [wMysteryGiftStagedDataLength], a
ldh a, [rIE]
push af
call Function104c2d
ld d, a
xor a
ldh [rIF], a
pop af
ldh [rIE], a
ld a, d
cp $10
jp z, Function105712
cp %01101100
jp nz, Function10571a
call Function1056eb
ld c, 60
call DelayFrames
call Function105777
ld hl, MysteryGiftReceivedCardText
call PrintText
ld de, wMysteryGiftTrainer
farcall Function8ac70
ld a, c
ld [wDeciramBuffer], a
ld hl, MysteryGiftNotRegisteredCardText
jr c, PrintTextAndExit_JP
ld hl, MysteryGiftListedCardText
jr PrintTextAndExit_JP
Function1056eb:
ld c, 16
.loop
ld hl, wVirtualOAMSprite00YCoord
ld b, 8
.dec_y_loop
dec [hl]
rept SPRITEOAMSTRUCT_LENGTH
inc hl
endr
dec b
jr nz, .dec_y_loop
ld hl, wVirtualOAMSprite08YCoord
ld b, 8
.inc_y_loop
inc [hl]
rept SPRITEOAMSTRUCT_LENGTH
inc hl
endr
dec b
jr nz, .inc_y_loop
dec c
ret z
push bc
ld c, 4
call DelayFrames
pop bc
jr .loop
Function105712:
call Function105777
ld hl, MysteryGiftLinkCancelledText
jr PrintTextAndExit_JP
Function10571a:
call Function105777
ld hl, MysteryGiftLinkCommErrorText
call PrintText
jp Function105688
PrintTextAndExit_JP:
call PrintText
ld a, LCDC_DEFAULT
ldh [rLCDC], a
ret
String_PressAToLink_BToCancel_JP:
db "エーボタン<WO>おすと"
next "つうしん<PKMN>おこなわれるよ!"
next "ビーボタン<WO>おすと"
next "つうしん<WO>ちゅうし します"
db "@"
MysteryGiftReceivedCardText:
text_far _MysteryGiftReceivedCardText
text_end
MysteryGiftListedCardText:
text_far _MysteryGiftListedCardText
text_end
MysteryGiftNotRegisteredCardText:
text_far _MysteryGiftNotRegisteredCardText
text_end
MysteryGiftLinkCancelledText:
text_far _MysteryGiftLinkCancelledText
text_end
MysteryGiftLinkCommErrorText:
text_far _MysteryGiftLinkCommErrorText
text_end
Function105777:
call ClearSprites
call ClearTilemap
call EnableLCD
call WaitBGMap
ld b, SCGB_DIPLOMA
call GetSGBLayout
call SetPalettes
ret
Function10578c:
ld de, wLinkData
ld a, BANK(sPlayerData)
call OpenSRAM
ld hl, sPlayerData + wPlayerName - wPlayerData
ld bc, NAME_LENGTH
call CopyBytes
ld hl, sPlayerData + wPlayerID - wPlayerData
ld bc, 2
call CopyBytes
ld hl, sPlayerData + wSecretID - wPlayerData
ld bc, 2
call CopyBytes
call CloseSRAM
ld a, BANK(sCrystalData)
call OpenSRAM
ld a, [sCrystalData + 0]
ld [de], a
inc de
ld a, BANK(s4_a603) ; aka BANK(s4_a007) ; MBC30 bank used by JP Crystal; inaccessible by MBC3
call OpenSRAM
ld hl, s4_a603 ; address of MBC30 bank
ld bc, $8
call CopyBytes
ld hl, s4_a007 ; address of MBC30 bank
ld bc, $c
call CopyBytes
call CloseSRAM
ret
Function1057d7:
call ClearBGPalettes
call DisableLCD
ld hl, CardTradeGFX
ld de, vTiles2 tile $00
ld a, BANK(CardTradeGFX)
ld bc, $40 tiles
call FarCopyBytes
ld hl, CardTradeSpriteGFX
ld de, vTiles0 tile $00
ld a, BANK(CardTradeSpriteGFX)
ld bc, 8 tiles
call FarCopyBytes
hlcoord 0, 0
ld a, $3f
ld bc, SCREEN_HEIGHT * SCREEN_WIDTH
call ByteFill
hlcoord 3, 7
lb bc, 9, 15
call ClearBox
hlcoord 0, 0
ld a, $0
ld [hli], a
inc a
ld [hl], a
hlcoord 0, 1
inc a
ld [hli], a
inc a
ld [hl], a
hlcoord 4, 2
ld a, $13
call .Load11Row
hlcoord 4, 3
ld a, $1e
call .Load12Row
hlcoord 4, 4
ld a, $2a
call .Load12Row
hlcoord 1, 2
ld [hl], $4
hlcoord 1, 3
ld a, $5
call .Load14Column
ld a, $9
hlcoord 18, 5
call .Load11Column
hlcoord 2, 5
ld a, $b
call .Load16Row
hlcoord 2, 16
ld a, $7
call .Load16Row
hlcoord 2, 5
ld a, $d
call .Load6Row
hlcoord 8, 5
ld [hl], $c
hlcoord 18, 5
ld [hl], $a
hlcoord 18, 16
ld [hl], $8
hlcoord 1, 16
ld [hl], $6
hlcoord 2, 6
ld a, $37
call .Load16Row
hlcoord 2, 15
ld a, $3d
call .Load16Row
hlcoord 2, 6
ld a, $39
call .Load9Column
hlcoord 17, 6
ld a, $3b
call .Load9Column
hlcoord 2, 6
ld [hl], $36
hlcoord 17, 6
ld [hl], $38
hlcoord 2, 15
ld [hl], $3c
hlcoord 17, 15
ld [hl], $3e
ld de, wVirtualOAMSprite00
ld hl, .OAM_data
ld bc, 16 * SPRITEOAMSTRUCT_LENGTH
call CopyBytes
call EnableLCD
call WaitBGMap
ld b, $2
farcall GetMysteryGift_MobileAdapterLayout
jp SetPalettes
.Load6Row:
ld b, 6
jr .row_loop
.Load11Row:
ld b, 11
jr .row_loop
.Load12Row:
ld b, 12
.row_loop
ld [hli], a
inc a
dec b
jr nz, .row_loop
ret
.Load9Column:
ld b, 9
jr .column_loop
.Load11Column:
ld b, 11
jr .column_loop
.Load14Column:
ld b, 14
.column_loop
ld [hl], a
ld de, SCREEN_WIDTH
add hl, de
dec b
jr nz, .column_loop
ret
.Load16Row:
ld b, 16
.row_loop_no_inc
ld [hli], a
dec b
jr nz, .row_loop_no_inc
ret
.OAM_data:
dbsprite 6, 2, 4, 1, $00, 0
dbsprite 7, 2, 4, 1, $01, 0
dbsprite 8, 2, 4, 1, $02, 0
dbsprite 9, 2, 4, 1, $03, 0
dbsprite 6, 3, 4, 1, $04, 0
dbsprite 7, 3, 4, 1, $05, 0
dbsprite 8, 3, 4, 1, $06, 0
dbsprite 9, 3, 4, 1, $07, 0
dbsprite 11, 0, 4, 1, $00, 0
dbsprite 12, 0, 4, 1, $01, 0
dbsprite 13, 0, 4, 1, $02, 0
dbsprite 14, 0, 4, 1, $03, 0
dbsprite 11, 1, 4, 1, $04, 0
dbsprite 12, 1, 4, 1, $05, 0
dbsprite 13, 1, 4, 1, $06, 0
dbsprite 14, 1, 4, 1, $07, 0
CardTradeGFX:
INCBIN "gfx/mystery_gift/card_trade.2bpp"
CardTradeSpriteGFX:
INCBIN "gfx/mystery_gift/card_sprite.2bpp"