ref: 31c3c94d64e1ac1e40c95acfda7de8b99b4f302b
dir: /engine/link/mystery_gift.asm/
; 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 ~MG_NOT_OKAY MG_START_END EQU %11111111 REGION_PREFIX EQU $96 REGION_CODE EQU $90 ; USA MESSAGE_PREFIX EQU $5a NAME_CARD_PREFIX EQU $3c 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 vc_patch infrared_fake_0 if DEF(_CRYSTAL11_VC) farcall StagePartyDataForMysteryGift call ClearMysteryGiftTrainer nop else ld a, 2 ld [wMysteryGiftMessageCount], a ld a, wMysteryGiftPartnerDataEnd - wMysteryGiftPartnerData ld [wMysteryGiftStagedDataLength], a endc vc_patch_end ldh a, [rIE] push af call ExchangeMysteryGiftData vc_hook infrared_fake_4 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 RESERVED_GAME_VERSION 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 [wNamedObjectIndex], 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: vc_hook infrared_fake_2 vc_patch infrared_fake_1 if DEF(_CRYSTAL11_VC) ld d, $ef .loop dec d ld a, d or a jr nz, .loop vc_hook infrared_fake_3 nop cp MG_CANCELED .restart ; same location as unpatched .restart ret z nop nop cp MG_OKAY jr nz, ExchangeMysteryGiftData ret else di farcall ClearChannels call InitializeIRCommunicationInterrupts .restart call BeginIRCommunication call InitializeIRCommunicationRoles ldh a, [hMGStatusFlags] endc vc_patch_end cp MG_CANCELED jp z, EndOrContinueMysteryGiftIRCommunication 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 ReceiveMysteryGiftDataPayload_GotRegionPrefix jp nz, EndOrContinueMysteryGiftIRCommunication 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 EndOrContinueMysteryGiftIRCommunication ReceiverExchangeMysteryGiftDataPayloads: ; Receive the data payload call ReceiveMysteryGiftDataPayload jp nz, EndOrContinueMysteryGiftIRCommunication ; fallthrough ReceiverExchangeMysteryGiftDataPayloads_GotPayload: ; Switch roles call BeginSendingIRCommunication jp nz, EndOrContinueMysteryGiftIRCommunication ; Send the data payload call SendMysteryGiftDataPayload jp nz, EndOrContinueMysteryGiftIRCommunication ; Switch roles call BeginReceivingIRCommunication jp nz, EndOrContinueMysteryGiftIRCommunication ; Receive an empty block call ReceiveEmptyIRDataBlock jp EndOrContinueMysteryGiftIRCommunication SenderExchangeMysteryGiftDataPayloads: ; Send the data payload call SendMysteryGiftDataPayload jp nz, EndOrContinueMysteryGiftIRCommunication ; Switch roles call BeginReceivingIRCommunication jp nz, EndOrContinueMysteryGiftIRCommunication ; Receive the data payload call ReceiveMysteryGiftDataPayload jp nz, EndOrContinueMysteryGiftIRCommunication ; Switch roles call BeginSendingIRCommunication jp nz, EndOrContinueMysteryGiftIRCommunication ; Send an empty block call SendEmptyIRDataBlock jp EndOrContinueMysteryGiftIRCommunication ReceiveMysteryGiftDataPayload: ; Receive the region prefix ld hl, hMGExchangedByte ld b, 1 call TryReceivingIRDataBlock ret nz ; fallthrough ReceiveMysteryGiftDataPayload_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 SendMysteryGiftDataPayload: ; 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 EndOrContinueMysteryGiftIRCommunication: 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, EndOrContinueMysteryGiftIRCommunication jp ReceiverExchangeMysteryGiftDataPayloads .sender call BeginSendingIRCommunication jr nz, EndOrContinueMysteryGiftIRCommunication 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 ExchangeNameCardData: di farcall ClearChannels call InitializeIRCommunicationInterrupts .restart call BeginIRCommunication call InitializeIRCommunicationRoles ldh a, [hMGStatusFlags] cp MG_CANCELED jp z, EndNameCardIRCommunication cp MG_OKAY jr nz, .restart ldh a, [hMGRole] cp IR_SENDER jr z, .sender ; receiver ; Receive the data payload call ReceiveNameCardDataPayload jp nz, EndNameCardIRCommunication ; Switch roles call BeginSendingIRCommunication jp nz, EndNameCardIRCommunication ; Send the data payload call SendNameCardDataPayload jp nz, EndNameCardIRCommunication ; Switch roles call BeginReceivingIRCommunication jp nz, EndNameCardIRCommunication ; Receive an empty block call ReceiveEmptyIRDataBlock jp EndNameCardIRCommunication .sender ; Send the data payload call SendNameCardDataPayload jp nz, EndNameCardIRCommunication ; Switch roles call BeginReceivingIRCommunication jp nz, EndNameCardIRCommunication ; Receive the data payload call ReceiveNameCardDataPayload jp nz, EndNameCardIRCommunication ; Switch roles call BeginSendingIRCommunication jp nz, EndNameCardIRCommunication ; Send an empty block call SendEmptyIRDataBlock jp EndNameCardIRCommunication ReceiveNameCardDataPayload: ; Receive the Name Card prefix 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 Name Card prefix ldh a, [hMGExchangedByte] cp NAME_CARD_PREFIX jp nz, WrongMysteryGiftRegion swap a ldh [hMGExchangedByte], a ; Switch roles call BeginSendingIRCommunication ret nz ; Send the swapped Name Card prefix 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, wNameCardData ld a, [wMysteryGiftStagedDataLength] ld b, a call TryReceivingIRDataBlock ret nz ; Receive an empty block call ReceiveEmptyIRDataBlock ldh a, [hMGStatusFlags] cp MG_OKAY ret SendNameCardDataPayload: ; Send the Name Card prefix ld a, NAME_CARD_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 swapped Name Card prefix 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 swapped Name Card prefix ldh a, [hMGExchangedByte] swap a cp NAME_CARD_PREFIX 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 EndNameCardIRCommunication: 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 InitializeIRCommunicationInterrupts: 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" DoNameCardSwap: call ClearTilemap call ClearSprites call WaitBGMap call InitNameCardLayout hlcoord 3, 8 ld de, .String_PressAToLink_BToCancel_JP call PlaceString call WaitBGMap call StageDataForNameCard call ClearMysteryGiftTrainer ld a, wNameCardDataEnd - wNameCardData ld [wMysteryGiftStagedDataLength], a ldh a, [rIE] push af call ExchangeNameCardData ld d, a xor a ldh [rIF], a pop af ldh [rIE], a ld a, d cp $10 jp z, .LinkCanceled cp MG_OKAY jp nz, .CommunicationError call .SlideNameCardUpOffScreen ld c, 60 call DelayFrames call .ClearScreen ld hl, .NameCardReceivedCardText call PrintText ld de, wNameCardData farcall Function8ac70 ld a, c ld [wTextDecimalByte], a ld hl, .NameCardNotRegisteredCardText jr c, .PrintTextAndExit ld hl, .NameCardListedCardText jr .PrintTextAndExit .SlideNameCardUpOffScreen: 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 .LinkCanceled: call .ClearScreen ld hl, .NameCardLinkCancelledText jr .PrintTextAndExit .CommunicationError: call .ClearScreen ld hl, .NameCardCommErrorText call PrintText jp DoNameCardSwap .PrintTextAndExit: call PrintText ld a, LCDC_DEFAULT ldh [rLCDC], a ret .String_PressAToLink_BToCancel_JP: db "エーボタン<WO>おすと" next "つうしん<PKMN>おこなわれるよ!" next "ビーボタン<WO>おすと" next "つうしん<WO>ちゅうし します" db "@" .NameCardReceivedCardText: text_far _NameCardReceivedCardText text_end .NameCardListedCardText: text_far _NameCardListedCardText text_end .NameCardNotRegisteredCardText: text_far _NameCardNotRegisteredCardText text_end .NameCardLinkCancelledText: text_far _NameCardLinkCancelledText text_end .NameCardCommErrorText: text_far _NameCardLinkCommErrorText text_end .ClearScreen: call ClearSprites call ClearTilemap call EnableLCD call WaitBGMap ld b, SCGB_DIPLOMA call GetSGBLayout call SetPalettes ret StageDataForNameCard: ld de, wMysteryGiftStaging 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, 12 call CopyBytes call CloseSRAM ret InitNameCardLayout: 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, .NameCardOAMData ld bc, 16 * SPRITEOAMSTRUCT_LENGTH call CopyBytes call EnableLCD call WaitBGMap ld b, CRYSTAL_CGB_NAME_CARD farcall GetCrystalCGBLayout 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 .NameCardOAMData: 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"