shithub: pokecrystal

ref: 7883e8f431a0b8f995c8f581af248b53896e5aa5
dir: /engine/link/mystery_gift.asm/

View raw version
; 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"