; 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 "エーボタンおすと" next "つうしんおこなわれるよ!" next "ビーボタンおすと" next "つうしんちゅうし します" 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"