diff options
author | ElectroDeoxys <ElectroDeoxys@gmail.com> | 2021-09-27 11:56:10 +0100 |
---|---|---|
committer | ElectroDeoxys <ElectroDeoxys@gmail.com> | 2021-09-27 11:56:10 +0100 |
commit | 7825b5ef0f09a877142ea1eb221e895bb60a0253 (patch) | |
tree | 9d46dfebd219919f5144786caf3ebfdf7cdab3a7 /src/engine/link | |
parent | 48f83527c769441b6c123f3382d90e2e962ef9a0 (diff) |
Split bank 6
Diffstat (limited to 'src/engine/link')
-rw-r--r-- | src/engine/link/card_pop.asm | 399 | ||||
-rw-r--r-- | src/engine/link/ir_core.asm | 531 | ||||
-rw-r--r-- | src/engine/link/ir_functions.asm | 323 | ||||
-rw-r--r-- | src/engine/link/link_duel.asm | 179 | ||||
-rw-r--r-- | src/engine/link/printer.asm | 1124 |
5 files changed, 2556 insertions, 0 deletions
diff --git a/src/engine/link/card_pop.asm b/src/engine/link/card_pop.asm new file mode 100644 index 0000000..5f809ef --- /dev/null +++ b/src/engine/link/card_pop.asm @@ -0,0 +1,399 @@ +_DoCardPop: +; loads scene for Card Pop! screen +; then checks if console is SGB +; and issues an error message in case it is + call SetSpriteAnimationsAsVBlankFunction + ld a,SCENE_CARD_POP + lb bc, 0, 0 + call LoadScene + ldtx hl, AreYouBothReadyToCardPopText + call PrintScrollableText_NoTextBoxLabel + call RestoreVBlankFunction + ldtx hl, CardPopCannotBePlayedWithTheGameBoyText + ld a, [wConsole] + cp CONSOLE_SGB + jr z, .error + +; initiate the communications + call PauseSong + call SetSpriteAnimationsAsVBlankFunction + ld a, SCENE_GAMEBOY_LINK_CONNECTING + lb bc, 0, 0 + call LoadScene + ldtx hl, PositionGameBoyColorsAndPressAButtonText + call DrawWideTextBox_PrintText + call EnableLCD + call HandleCardPopCommunications + push af + push hl + call ClearRP + call RestoreVBlankFunction + pop hl + pop af + jr c, .error + +; show the received card detail page +; and play the corresponding song + ld a, [wLoadedCard1ID] + call AddCardToCollectionAndUpdateAlbumProgress + ld hl, wLoadedCard1Name + ld a, [hli] + ld h, [hl] + ld l, a + call LoadTxRam2 + ld a, PLAYER_TURN + ldh [hWhoseTurn], a + ld a, SFX_5D + call PlaySFX +.wait_sfx + call AssertSFXFinished + or a + jr nz, .wait_sfx + ld a, [wCardPopCardObtainSong] + call PlaySong + ldtx hl, ReceivedThroughCardPopText + bank1call _DisplayCardDetailScreen + call ResumeSong + lb de, $38, $9f + call SetupText + bank1call OpenCardPage_FromHand + ret + +.error +; show Card Pop! error scene +; and print text in hl + push hl + call ResumeSong + call SetSpriteAnimationsAsVBlankFunction + ld a, SCENE_CARD_POP_ERROR + lb bc, 0, 0 + call LoadScene + pop hl + call PrintScrollableText_NoTextBoxLabel + call RestoreVBlankFunction + ret + +; handles all communications to the other device to do Card Pop! +; returns carry if Card Pop! is unsuccessful +; and returns in hl the corresponding error text ID +HandleCardPopCommunications: +; copy CardPopNameList from SRAM to WRAM + call EnableSRAM + ld hl, sCardPopNameList + ld de, wCardPopNameList + ld bc, CARDPOP_NAME_LIST_SIZE + call CopyDataHLtoDE + call DisableSRAM + + ld a, IRPARAM_CARD_POP + call InitIRCommunications +.asm_19cc9 + call TryReceiveIRRequest ; receive request + jr nc, .asm_19d05 + bit 1, a + jr nz, .fail + call TrySendIRRequest ; send request + jr c, .asm_19cc9 + +; do the player name search, then transmit the result + call ExchangeIRCommunicationParameters + jr c, .fail + ld hl, wCardPopNameList + ld de, wOtherPlayerCardPopNameList + ld c, 0 ; $100 bytes = CARDPOP_NAME_LIST_SIZE + call RequestDataTransmissionThroughIR + jr c, .fail + call LookUpNameInCardPopNameList + ld hl, wCardPopNameSearchResult + ld de, wCardPopNameSearchResult + ld c, 1 + call RequestDataReceivalThroughIR + jr c, .fail + call SetIRCommunicationErrorCode_NoError + jr c, .fail + call ExecuteReceivedIRCommands + jr c, .fail + jr .check_search_result + +.asm_19d05 + call ExecuteReceivedIRCommands + ld a, [wIRCommunicationErrorCode] + or a + jr nz, .fail + call RequestCloseIRCommunication + jr c, .fail + +.check_search_result + ld a, [wCardPopNameSearchResult] + or a + jr z, .success + ; not $00, means the name was found in the list + ldtx hl, CannotCardPopWithFriendPreviouslyPoppedWithText + scf + ret + +.success + call DecideCardToReceiveFromCardPop + +; increment number of times Card Pop! was done +; and write the other player's name to sCardPopNameList +; the spot where this is written in the list is derived +; from the lower nybble of sTotalCardPopsDone +; that means that after 16 Card Pop!, the older +; names start to get overwritten + call EnableSRAM + ld hl, sTotalCardPopsDone + ld a, [hl] + inc [hl] + and $0f + swap a ; *NAME_BUFFER_LENGTH + ld l, a + ld h, $0 + ld de, sCardPopNameList + add hl, de + ld de, wNameBuffer + ld c, NAME_BUFFER_LENGTH +.loop_write_name + ld a, [de] + inc de + ld [hli], a + dec c + jr nz, .loop_write_name + call DisableSRAM + or a + ret + +.fail + ldtx hl, ThePopWasntSuccessfulText + scf + ret + +; looks up the name in wNameBuffer in wCardPopNameList +; used to know whether this save file has done Card Pop! +; with the other player already +; returns carry and wCardPopNameSearchResult = $ff if the name was found; +; returns no carry and wCardPopNameSearchResult = $00 otherwise +LookUpNameInCardPopNameList: +; searches for other player's name in this game's name list + ld hl, wCardPopNameList + ld c, CARDPOP_NAME_LIST_MAX_ELEMS +.loop_own_card_pop_name_list + push hl + ld de, wNameBuffer + call .CompareNames + pop hl + jr nc, .found_name + ld de, NAME_BUFFER_LENGTH + add hl, de + dec c + jr nz, .loop_own_card_pop_name_list + +; name was not found in wCardPopNameList + +; searches for this player's name in the other game's name list +; this is useless since it discards the result from the name comparisons +; as a result this loop will always return no carry + call EnableSRAM + ld hl, wOtherPlayerCardPopNameList + ld c, CARDPOP_NAME_LIST_MAX_ELEMS +.loop_other_card_pop_name_list + push hl + ld de, sPlayerName + call .CompareNames ; discards result from comparison + pop hl + ld de, NAME_BUFFER_LENGTH + add hl, de + dec c + jr nz, .loop_other_card_pop_name_list + xor a + jr .no_carry + +.found_name + ld a, $ff + scf +.no_carry + call DisableSRAM + ld [wCardPopNameSearchResult], a ; $00 if name was not found, $ff otherwise + ret + +; compares names in hl and de +; if they are different, return carry +.CompareNames + ld b, NAME_BUFFER_LENGTH +.loop_chars + ld a, [de] + inc de + cp [hl] + jr nz, .not_same + inc hl + dec b + jr nz, .loop_chars + or a + ret +.not_same + scf + ret + +; loads in wLoadedCard1 a random card to be received +; this selection is done based on the rarity that is +; decided from the names of both participants +; the card will always be a Pokemon card that is not +; from a Promotional set, with the exception +; of Venusaur1 and Mew2 +; output: +; - e = card ID chosen +DecideCardToReceiveFromCardPop: + ld a, PLAYER_TURN + ldh [hWhoseTurn], a + call EnableSRAM + ld hl, sPlayerName + call CalculateNameHash + call DisableSRAM + push de + ld hl, wNameBuffer + call CalculateNameHash + pop bc + +; de = other player's name hash +; bc = this player's name hash + +; updates RNG values to subtraction of these two hashes + ld hl, wRNG1 + ld a, b + sub d + ld d, a ; b - d + ld [hli], a ; wRNG1 + ld a, c + sub e + ld e, a ; c - e + ld [hli], a ; wRNG2 + ld [hl], $0 ; wRNGCounter + +; depending on the values obtained from the hashes, +; determine which rarity card to give to the player +; along with the song to play with each rarity +; the probabilities of each possibility can be calculated +; as follows (given 2 random player names): +; 101/256 ~ 39% for Circle +; 90/256 ~ 35% for Diamond +; 63/256 ~ 25% for Star +; 1/256 ~ .4% for Venusaur1 or Mew2 + ld a, e + cp 5 + jr z, .venusaur1_or_mew2 + cp 64 + jr c, .star_rarity ; < 64 + cp 154 + jr c, .diamond_rarity ; < 154 + ; >= 154 + + ld a, MUSIC_BOOSTER_PACK + ld b, CIRCLE + jr .got_rarity +.diamond_rarity + ld a, MUSIC_BOOSTER_PACK + ld b, DIAMOND + jr .got_rarity +.star_rarity + ld a, MUSIC_MATCH_VICTORY + ld b, STAR +.got_rarity + ld [wCardPopCardObtainSong], a + ld a, b + call CreateCardPopCandidateList + ; shuffle candidates and pick first from list + call ShuffleCards + ld a, [hl] + ld e, a +.got_card_id + ld d, $0 + call LoadCardDataToBuffer1_FromCardID + ld a, e + ret + +.venusaur1_or_mew2 +; choose either Venusaur1 or Mew2 +; depending on whether the lower +; bit of d is unset or set, respectively + ld a, MUSIC_MEDAL + ld [wCardPopCardObtainSong], a + ld e, VENUSAUR1 + ld a, d + and $1 ; get lower bit + jr z, .got_card_id + ld e, MEW2 + jr .got_card_id + +; lists in wCardPopCardCandidates all cards that: +; - are Pokemon cards; +; - have the same rarity as input register a; +; - are not from Promotional set. +; input: +; - a = card rarity +; output: +; - a = number of candidates +CreateCardPopCandidateList: + ld hl, wPlayerDeck + push hl + push de + push bc + ld b, a + + lb de, 0, GRASS_ENERGY +.loop_card_ids + call LoadCardDataToBuffer1_FromCardID + jr c, .count ; no more card IDs + ld a, [wLoadedCard1Type] + and TYPE_ENERGY + jr nz, .next_card_id ; not Pokemon card + ld a, [wLoadedCard1Rarity] + cp b + jr nz, .next_card_id ; not equal rarity + ld a, [wLoadedCard1Set] + and $f0 + cp PROMOTIONAL + jr z, .next_card_id ; no promos + ld [hl], e + inc hl +.next_card_id + inc de + jr .loop_card_ids + +; count all the cards that were listed +; and return it in a +.count + ld [hl], $00 ; invalid card ID as end of list + ld hl, wPlayerDeck + ld c, -1 +.loop_count + inc c + ld a, [hli] + or a + jr nz, .loop_count + ld a, c + pop bc + pop de + pop hl + ret + +; creates a unique two-byte hash from the name given in hl +; the low byte is calculated by simply adding up all characters +; the high byte is calculated by xoring all characters together +; input: +; - hl = points to the start of the name buffer +; output: +; - de = hash +CalculateNameHash: + ld c, NAME_BUFFER_LENGTH + ld de, $0 +.loop + ld a, e + add [hl] + ld e, a + ld a, d + xor [hl] + ld d, a + inc hl + dec c + jr nz, .loop + ret diff --git a/src/engine/link/ir_core.asm b/src/engine/link/ir_core.asm new file mode 100644 index 0000000..ab9eaae --- /dev/null +++ b/src/engine/link/ir_core.asm @@ -0,0 +1,531 @@ +; if carry flag is set, only delays +; if carry not set: +; - set rRP to $c1, wait; +; - set rRP to $c0, wait; +; - return +Func_19674: + jr c, .delay_once + ld [hl], $c1 + ld a, 5 + jr .loop_delay_1 ; jump to possibly to add more cycles? +.loop_delay_1 + dec a + jr nz, .loop_delay_1 + ld [hl], $c0 + ld a, 14 + jr .loop_delay_2 ; jump to possibly to add more cycles? +.loop_delay_2 + dec a + jr nz, .loop_delay_2 + ret + +.delay_once + ld a, 21 + jr .loop_delay_3 ; jump to possibly to add more cycles? +.loop_delay_3 + dec a + jr nz, .loop_delay_3 + nop + ret + +; input a = byte to transmit through IR +TransmitByteThroughIR: + push hl + ld hl, rRP + push de + push bc + ld b, a + scf ; carry set + call Func_19674 + or a ; carry not set + call Func_19674 + ld c, 8 + ld c, 8 ; number of input bits +.loop + ld a, $00 + rr b + call Func_19674 + dec c + jr nz, .loop + pop bc + pop de + pop hl + ldh a, [rJOYP] + bit 1, a ; P11 + jr z, ReturnZFlagUnsetAndCarryFlagSet + xor a ; return z set + ret + +; same as ReceiveByteThroughIR but +; returns $0 in a if there's an error in IR +ReceiveByteThroughIR_ZeroIfUnsuccessful: + call ReceiveByteThroughIR + ret nc + xor a + ret + +; returns carry if there's some time out +; and output in register a of $ff +; otherwise returns in a some sequence of bits +; related to how rRP sets/unsets bit 1 +ReceiveByteThroughIR: + push de + push bc + push hl + +; waits for bit 1 in rRP to be unset +; up to $100 loops + ld b, 0 + ld hl, rRP +.wait_ir + bit 1, [hl] + jr z, .ok + dec b + jr nz, .wait_ir + ; looped around $100 times + ; return $ff and carry set + pop hl + pop bc + pop de + scf + ld a, $ff + ret + +.ok +; delay for some cycles + ld a, 15 +.loop_delay + dec a + jr nz, .loop_delay + +; loop for each bit + ld e, 8 +.loop + ld a, $01 + ; possibly delay cycles? + ld b, 9 + ld b, 9 + ld b, 9 + ld b, 9 + +; checks for bit 1 in rRP +; if in any of the checks it is unset, +; then a is set to 0 +; this is done a total of 9 times + bit 1, [hl] + jr nz, .asm_196ec + xor a +.asm_196ec + bit 1, [hl] + jr nz, .asm_196f1 + xor a +.asm_196f1 + dec b + jr nz, .asm_196ec + ; one bit received + rrca + rr d + dec e + jr nz, .loop + ld a, d ; has bits set for each "cycle" that bit 1 was not unset + pop hl + pop bc + pop de + or a + ret + +ReturnZFlagUnsetAndCarryFlagSet: + ld a, $ff + or a ; z not set + scf ; carry set + ret + +; called when expecting to transmit data +Func_19705: + ld hl, rRP +.asm_19708 + ldh a, [rJOYP] + bit 1, a + jr z, ReturnZFlagUnsetAndCarryFlagSet + ld a, $aa ; request + call TransmitByteThroughIR + push hl + pop hl + call ReceiveByteThroughIR_ZeroIfUnsuccessful + cp $33 ; acknowledge + jr nz, .asm_19708 + xor a + ret + +; called when expecting to receive data +Func_1971e: + ld hl, rRP +.asm_19721 + ldh a, [rJOYP] + bit 1, a + jr z, ReturnZFlagUnsetAndCarryFlagSet + call ReceiveByteThroughIR_ZeroIfUnsuccessful + cp $aa ; request + jr nz, .asm_19721 + ld a, $33 ; acknowledge + call TransmitByteThroughIR + xor a + ret + +ReturnZFlagUnsetAndCarryFlagSet2: + jp ReturnZFlagUnsetAndCarryFlagSet + +TransmitIRDataBuffer: + call Func_19705 + jr c, ReturnZFlagUnsetAndCarryFlagSet2 + ld a, $49 + call TransmitByteThroughIR + ld a, $52 + call TransmitByteThroughIR + ld hl, wIRDataBuffer + ld c, 8 + jr TransmitNBytesFromHLThroughIR + +ReceiveIRDataBuffer: + call Func_1971e + jr c, ReturnZFlagUnsetAndCarryFlagSet2 + call ReceiveByteThroughIR + cp $49 + jr nz, ReceiveIRDataBuffer + call ReceiveByteThroughIR + cp $52 + jr nz, ReceiveIRDataBuffer + ld hl, wIRDataBuffer + ld c, 8 + jr ReceiveNBytesToHLThroughIR + +; hl = start of data to transmit +; c = number of bytes to transmit +TransmitNBytesFromHLThroughIR: + ld b, $0 +.loop_data_bytes + ld a, b + add [hl] + ld b, a + ld a, [hli] + call TransmitByteThroughIR + jr c, .asm_1977c + dec c + jr nz, .loop_data_bytes + ld a, b + cpl + inc a + call TransmitByteThroughIR +.asm_1977c + ret + +; hl = address to write received data +; c = number of bytes to be received +ReceiveNBytesToHLThroughIR: + ld b, 0 +.loop_data_bytes + call ReceiveByteThroughIR + jr c, ReturnZFlagUnsetAndCarryFlagSet2 + ld [hli], a + add b + ld b, a + dec c + jr nz, .loop_data_bytes + call ReceiveByteThroughIR + add b + or a + jr nz, ReturnZFlagUnsetAndCarryFlagSet2 + ret + +; disables interrupts, and sets joypad and IR communication port +; switches to CGB normal speed +StartIRCommunications: + di + call SwitchToCGBNormalSpeed + ld a, P14 + ldh [rJOYP], a + ld a, $c0 + ldh [rRP], a + ret + +; reenables interrupts, and switches CGB back to double speed +CloseIRCommunications: + ld a, P14 | P15 + ldh [rJOYP], a +.wait_vblank_on + ldh a, [rSTAT] + and STAT_LCDC_STATUS + cp STAT_ON_VBLANK + jr z, .wait_vblank_on +.wait_vblank_off + ldh a, [rSTAT] + and STAT_LCDC_STATUS + cp STAT_ON_VBLANK + jr nz, .wait_vblank_off + call SwitchToCGBDoubleSpeed + ei + ret + +; set rRP to 0 +ClearRP: + ld a, $00 + ldh [rRP], a + ret + +; expects to receive a command (IRCMD_* constant) +; in wIRDataBuffer + 1, then calls the subroutine +; corresponding to that command +ExecuteReceivedIRCommands: + call StartIRCommunications +.loop_commands + call ReceiveIRDataBuffer + jr c, .error + jr nz, .loop_commands + ld hl, wIRDataBuffer + 1 + ld a, [hl] + ld hl, .CmdPointerTable + cp NUM_IR_COMMANDS + jr nc, .loop_commands ; invalid command + call .JumpToCmdPointer ; execute command + jr .loop_commands +.error + call CloseIRCommunications + xor a + scf + ret + +.JumpToCmdPointer + add a ; *2 + add l + ld l, a + ld a, 0 + adc h + ld h, a + ld a, [hli] + ld h, [hl] + ld l, a +.jp_hl + jp hl + +.CmdPointerTable + dw .Close ; IRCMD_CLOSE + dw .ReturnWithoutClosing ; IRCMD_RETURN_WO_CLOSING + dw .TransmitData ; IRCMD_TRANSMIT_DATA + dw .ReceiveData ; IRCMD_RECEIVE_DATA + dw .CallFunction ; IRCMD_CALL_FUNCTION + +; closes the IR communications +; pops hl so that the sp points +; to the return address of ExecuteReceivedIRCommands +.Close + pop hl + call CloseIRCommunications + or a + ret + +; returns without closing the IR communications +; will continue the command loop +.ReturnWithoutClosing + or a + ret + +; receives an address and number of bytes +; and transmits starting at that address +.TransmitData + call Func_19705 + ret c + call LoadRegistersFromIRDataBuffer + jp TransmitNBytesFromHLThroughIR + +; receives an address and number of bytes +; and writes the data received to that address +.ReceiveData + call LoadRegistersFromIRDataBuffer + ld l, e + ld h, d + call ReceiveNBytesToHLThroughIR + jr c, .asm_19812 + sub b + call TransmitByteThroughIR +.asm_19812 + ret + +; receives an address to call, then stores +; the registers in the IR data buffer +.CallFunction + call LoadRegistersFromIRDataBuffer + call .jp_hl + call StoreRegistersInIRDataBuffer + ret + +; returns carry set if request sent was not acknowledged +TrySendIRRequest: + call StartIRCommunications + ld hl, rRP + ld c, 4 +.send_request + ld a, $aa ; request + push bc + call TransmitByteThroughIR + push bc + pop bc + call ReceiveByteThroughIR_ZeroIfUnsuccessful + pop bc + cp $33 ; acknowledgement + jr z, .received_ack + dec c + jr nz, .send_request + scf + jr .close + +.received_ack + xor a +.close + push af + call CloseIRCommunications + pop af + ret + +; returns carry set if request was not received +TryReceiveIRRequest: + call StartIRCommunications + ld hl, rRP +.wait_request + call ReceiveByteThroughIR_ZeroIfUnsuccessful + cp $aa ; request + jr z, .send_ack + ldh a, [rJOYP] + cpl + and P10 | P11 + jr z, .wait_request + scf + jr .close + +.send_ack + ld a, $33 ; acknowledgement + call TransmitByteThroughIR + xor a +.close + push af + call CloseIRCommunications + pop af + ret + +; sends request for other device to close current communication +RequestCloseIRCommunication: + call StartIRCommunications + ld a, IRCMD_CLOSE + ld [wIRDataBuffer + 1], a + call TransmitIRDataBuffer +; fallthrough + +; calls CloseIRCommunications while preserving af +SafelyCloseIRCommunications: + push af + call CloseIRCommunications + pop af + ret + +; sends a request for data to be transmitted +; from the other device +; hl = start of data to request to transmit +; de = address to write data received +; c = length of data +RequestDataTransmissionThroughIR: + ld a, IRCMD_TRANSMIT_DATA + call TransmitRegistersThroughIR + push de + push bc + call Func_1971e + pop bc + pop hl + jr c, SafelyCloseIRCommunications + call ReceiveNBytesToHLThroughIR + jr SafelyCloseIRCommunications + +; transmits data to be written in the other device +; hl = start of data to transmit +; de = address for other device to write data +; c = length of data +RequestDataReceivalThroughIR: + ld a, IRCMD_RECEIVE_DATA + call TransmitRegistersThroughIR + call TransmitNBytesFromHLThroughIR + jr c, SafelyCloseIRCommunications + call ReceiveByteThroughIR + jr c, SafelyCloseIRCommunications + add b + jr nz, .asm_1989e + xor a + jr SafelyCloseIRCommunications +.asm_1989e + call ReturnZFlagUnsetAndCarryFlagSet + jr SafelyCloseIRCommunications + +; first stores all the current registers in wIRDataBuffer +; then transmits it through IR +TransmitRegistersThroughIR: + push hl + push de + push bc + call StoreRegistersInIRDataBuffer + call StartIRCommunications + call TransmitIRDataBuffer + pop bc + pop de + pop hl + ret nc + inc sp + inc sp + jr SafelyCloseIRCommunications + +; stores af, hl, de and bc in wIRDataBuffer +StoreRegistersInIRDataBuffer: + push de + push hl + push af + ld hl, wIRDataBuffer + pop de + ld [hl], e ; <- f + inc hl + ld [hl], d ; <- a + inc hl + pop de + ld [hl], e ; <- l + inc hl + ld [hl], d ; <- h + inc hl + pop de + ld [hl], e ; <- e + inc hl + ld [hl], d ; <- d + inc hl + ld [hl], c ; <- c + inc hl + ld [hl], b ; <- b + ret + +; loads all the registers that were stored +; from StoreRegistersInIRDataBuffer +LoadRegistersFromIRDataBuffer: + ld hl, wIRDataBuffer + ld e, [hl] + inc hl + ld d, [hl] + inc hl + push de + ld e, [hl] + inc hl + ld d, [hl] + inc hl + push de + ld e, [hl] + inc hl + ld d, [hl] + inc hl + ld c, [hl] + inc hl + ld b, [hl] + pop hl + pop af + ret diff --git a/src/engine/link/ir_functions.asm b/src/engine/link/ir_functions.asm new file mode 100644 index 0000000..140dccb --- /dev/null +++ b/src/engine/link/ir_functions.asm @@ -0,0 +1,323 @@ +; hl = text ID +LoadLinkConnectingScene: + push hl + call SetSpriteAnimationsAsVBlankFunction + ld a, SCENE_GAMEBOY_LINK_CONNECTING + lb bc, 0, 0 + call LoadScene + pop hl + call DrawWideTextBox_PrintText + call EnableLCD + ret + +; shows Link Not Connected scene +; then asks the player whether they want to try again +; if the player selects "no", return carry +; input: +; - hl = text ID +LoadLinkNotConnectedSceneAndAskWhetherToTryAgain: + push hl + call RestoreVBlankFunction + call SetSpriteAnimationsAsVBlankFunction + ld a, SCENE_GAMEBOY_LINK_NOT_CONNECTED + lb bc, 0, 0 + call LoadScene + pop hl + call DrawWideTextBox_WaitForInput + ldtx hl, WouldYouLikeToTryAgainText + call YesOrNoMenuWithText_SetCursorToYes +; fallthrough + +ClearRPAndRestoreVBlankFunction: + push af + call ClearRP + call RestoreVBlankFunction + pop af + ret + +; prepares IR communication parameter data +; a = a IRPARAM_* constant for the function of this connection +InitIRCommunications: + ld hl, wOwnIRCommunicationParams + ld [hl], a + inc hl + ld [hl], $50 + inc hl + ld [hl], $4b + inc hl + ld [hl], $31 + ld a, $ff + ld [wIRCommunicationErrorCode], a + ld a, PLAYER_TURN + ldh [hWhoseTurn], a +; clear wNameBuffer and wOpponentName + xor a + ld [wNameBuffer], a + ld hl, wOpponentName + ld [hli], a + ld [hl], a +; loads player's name from SRAM +; to wDefaultText + call EnableSRAM + ld hl, sPlayerName + ld de, wDefaultText + ld c, NAME_BUFFER_LENGTH +.loop + ld a, [hli] + ld [de], a + inc de + dec c + jr nz, .loop + call DisableSRAM + ret + +; returns carry if communication was unsuccessful +; if a = 0, then it was a communication error +; if a = 1, then operation was cancelled by the player +PrepareSendCardOrDeckConfigurationThroughIR: + call InitIRCommunications + +; pressing A button triggers request for IR communication +.loop_frame + call DoFrame + ldh a, [hKeysPressed] + bit B_BUTTON_F, a + jr nz, .b_btn + ldh a, [hKeysHeld] + bit A_BUTTON_F, a + jr z, .loop_frame +; a btn + call TrySendIRRequest + jr nc, .request_success + or a + jr z, .loop_frame + xor a + scf + ret + +.b_btn + ; cancelled by the player + ld a, $01 + scf + ret + +.request_success + call ExchangeIRCommunicationParameters + ret c + ld a, [wOtherIRCommunicationParams + 3] + cp $31 + jr nz, SetIRCommunicationErrorCode_Error + or a + ret + +; exchanges player names and IR communication parameters +; checks whether parameters for communication match +; and if they don't, an error is issued +ExchangeIRCommunicationParameters: + ld hl, wOwnIRCommunicationParams + ld de, wOtherIRCommunicationParams + ld c, 4 + call RequestDataTransmissionThroughIR + jr c, .error + ld hl, wOtherIRCommunicationParams + 1 + ld a, [hli] + cp $50 + jr nz, .error + ld a, [hli] + cp $4b + jr nz, .error + ld a, [wOwnIRCommunicationParams] + ld hl, wOtherIRCommunicationParams + cp [hl] ; do parameters match? + jr nz, SetIRCommunicationErrorCode_Error + +; receives wDefaultText from other device +; and writes it to wNameBuffer + ld hl, wDefaultText + ld de, wNameBuffer + ld c, NAME_BUFFER_LENGTH + call RequestDataTransmissionThroughIR + jr c, .error +; transmits wDefaultText to be +; written in wNameBuffer in the other device + ld hl, wDefaultText + ld de, wNameBuffer + ld c, NAME_BUFFER_LENGTH + call RequestDataReceivalThroughIR + jr c, .error + or a + ret + +.error + xor a + scf + ret + +SetIRCommunicationErrorCode_Error: + ld hl, wIRCommunicationErrorCode + ld [hl], $01 + ld de, wIRCommunicationErrorCode + ld c, 1 + call RequestDataReceivalThroughIR + call RequestCloseIRCommunication + ld a, $01 + scf + ret + +SetIRCommunicationErrorCode_NoError: + ld hl, wOwnIRCommunicationParams + ld [hl], $00 + ld de, wIRCommunicationErrorCode + ld c, 1 + call RequestDataReceivalThroughIR + ret c + call RequestCloseIRCommunication + or a + ret + +; makes device receptive to receive data from other device +; to write in wDuelTempList (either list of cards or a deck configuration) +; returns carry if some error occurred +TryReceiveCardOrDeckConfigurationThroughIR: + call InitIRCommunications +.loop_receive_request + xor a + ld [wDuelTempList], a + call TryReceiveIRRequest + jr nc, .receive_data + bit 1, a + jr nz, .cancelled + jr .loop_receive_request +.receive_data + call ExecuteReceivedIRCommands + ld a, [wIRCommunicationErrorCode] + or a + ret z ; no error + xor a + scf + ret + +.cancelled + ld a, $01 + scf + ret + +; returns carry if card(s) wasn't successfully sent +_SendCard: + call StopMusic + ldtx hl, SendingACardText + call LoadLinkConnectingScene + ld a, IRPARAM_SEND_CARDS + call PrepareSendCardOrDeckConfigurationThroughIR + jr c, .fail + + ; send cards + xor a + ld [wDuelTempList + DECK_SIZE], a + ld hl, wDuelTempList + ld e, l + ld d, h + ld c, DECK_SIZE + 1 + call RequestDataReceivalThroughIR + jr c, .fail + call SetIRCommunicationErrorCode_NoError + jr c, .fail + call ExecuteReceivedIRCommands + jr c, .fail + ld a, [wOwnIRCommunicationParams + 1] + cp $4f + jr nz, .fail + call PlayCardPopSong + xor a + call ClearRPAndRestoreVBlankFunction + ret + +.fail + call PlayCardPopSong + ldtx hl, CardTransferWasntSuccessful1Text + call LoadLinkNotConnectedSceneAndAskWhetherToTryAgain + jr nc, _SendCard ; loop back and try again + ; failed + scf + ret + +PlayCardPopSong: + ld a, MUSIC_CARD_POP + jp PlaySong + +_ReceiveCard: + call StopMusic + ldtx hl, ReceivingACardText + call LoadLinkConnectingScene + ld a, IRPARAM_SEND_CARDS + call TryReceiveCardOrDeckConfigurationThroughIR + ld a, $4f + ld [wOwnIRCommunicationParams + 1], a + ld hl, wOwnIRCommunicationParams + ld e, l + ld d, h + ld c, 4 + call RequestDataReceivalThroughIR + jr c, .fail + call RequestCloseIRCommunication + jr c, .fail + call PlayCardPopSong + or a + call ClearRPAndRestoreVBlankFunction + ret + +.fail + call PlayCardPopSong + ldtx hl, CardTransferWasntSuccessful2Text + call LoadLinkNotConnectedSceneAndAskWhetherToTryAgain + jr nc, _ReceiveCard + scf + ret + +_SendDeckConfiguration: + call StopMusic + ldtx hl, SendingADeckConfigurationText + call LoadLinkConnectingScene + ld a, IRPARAM_SEND_DECK + call PrepareSendCardOrDeckConfigurationThroughIR + jr c, .fail + ld hl, wDuelTempList + ld e, l + ld d, h + ld c, DECK_STRUCT_SIZE + call RequestDataReceivalThroughIR + jr c, .fail + call SetIRCommunicationErrorCode_NoError + jr c, .fail + call PlayCardPopSong + call ClearRPAndRestoreVBlankFunction + or a + ret + +.fail + call PlayCardPopSong + ldtx hl, DeckConfigurationTransferWasntSuccessful1Text + call LoadLinkNotConnectedSceneAndAskWhetherToTryAgain + jr nc, _SendDeckConfiguration + scf + ret + +_ReceiveDeckConfiguration: + call StopMusic + ldtx hl, ReceivingDeckConfigurationText + call LoadLinkConnectingScene + ld a, IRPARAM_SEND_DECK + call TryReceiveCardOrDeckConfigurationThroughIR + jr c, .fail + call PlayCardPopSong + call ClearRPAndRestoreVBlankFunction + or a + ret + +.fail + call PlayCardPopSong + ldtx hl, DeckConfigurationTransferWasntSuccessful2Text + call LoadLinkNotConnectedSceneAndAskWhetherToTryAgain + jr nc, _ReceiveDeckConfiguration ; loop back and try again + scf + ret diff --git a/src/engine/link/link_duel.asm b/src/engine/link/link_duel.asm new file mode 100644 index 0000000..fc484d0 --- /dev/null +++ b/src/engine/link/link_duel.asm @@ -0,0 +1,179 @@ +; sets up to start a link duel +; decides which device will pick the number of prizes +; then exchanges names and duels between the players +; and starts the main duel routine +_SetUpAndStartLinkDuel: + ld hl, sp+$00 + ld a, l + ld [wDuelReturnAddress + 0], a + ld a, h + ld [wDuelReturnAddress + 1], a + call SetSpriteAnimationsAsVBlankFunction + + ld a, SCENE_GAMEBOY_LINK_TRANSMITTING + lb bc, 0, 0 + call LoadScene + + bank1call LoadPlayerDeck + call SwitchToCGBNormalSpeed + bank1call DecideLinkDuelVariables + push af + call RestoreVBlankFunction + pop af + jp c, .error + + ld a, DUELIST_TYPE_PLAYER + ld [wPlayerDuelistType], a + ld a, DUELIST_TYPE_LINK_OPP + ld [wOpponentDuelistType], a + ld a, DUELTYPE_LINK + ld [wDuelType], a + + call EmptyScreen + ld a, [wSerialOp] + cp $29 + jr nz, .asm_1a540 + + ld a, PLAYER_TURN + ldh [hWhoseTurn], a + call .ExchangeNamesAndDecks + jr c, .error + lb de, 6, 2 + lb bc, 8, 6 + call DrawRegularTextBox + lb de, 7, 4 + call InitTextPrinting + ldtx hl, PrizesCardsText + call ProcessTextFromID + ldtx hl, ChooseTheNumberOfPrizesText + call DrawWideTextBox_PrintText + call EnableLCD + call .PickNumberOfPrizeCards + ld a, [wNPCDuelPrizes] + call SerialSend8Bytes + jr .prizes_decided + +.asm_1a540 + ld a, OPPONENT_TURN + ldh [hWhoseTurn], a + call .ExchangeNamesAndDecks + jr c, .error + ldtx hl, PleaseWaitDecidingNumberOfPrizesText + call DrawWideTextBox_PrintText + call EnableLCD + call SerialRecv8Bytes + ld [wNPCDuelPrizes], a + +.prizes_decided + call ExchangeRNG + ld a, LINK_OPP_PIC + ld [wOpponentPortrait], a + ldh a, [hWhoseTurn] + push af + call EmptyScreen + bank1call SetDefaultPalettes + ld a, SHUFFLE_DECK + ld [wDuelDisplayedScreen], a + bank1call DrawDuelistPortraitsAndNames + ld a, OPPONENT_TURN + ldh [hWhoseTurn], a + ld a, [wNPCDuelPrizes] + ld l, a + ld h, $00 + call LoadTxRam3 + ldtx hl, BeginAPrizeDuelWithText + call DrawWideTextBox_WaitForInput + pop af + ldh [hWhoseTurn], a + call ExchangeRNG + bank1call StartDuel_VSLinkOpp + call SwitchToCGBDoubleSpeed + ret + +.error + ld a, -1 + ld [wDuelResult], a + call SetSpriteAnimationsAsVBlankFunction + + ld a, SCENE_GAMEBOY_LINK_NOT_CONNECTED + lb bc, 0, 0 + call LoadScene + + ldtx hl, TransmissionErrorText + call DrawWideTextBox_WaitForInput + call RestoreVBlankFunction + call ResetSerial + ret + +.ExchangeNamesAndDecks + ld de, wDefaultText + push de + call CopyPlayerName + pop hl + ld de, wNameBuffer + ld c, NAME_BUFFER_LENGTH + call SerialExchangeBytes + ret c + xor a + ld hl, wOpponentName + ld [hli], a + ld [hl], a + ld hl, wPlayerDeck + ld de, wOpponentDeck + ld c, DECK_SIZE + call SerialExchangeBytes + ret + +; handles player choice of number of prize cards +; pressing left/right makes it decrease/increase respectively +; selection is confirmed by pressing A button +.PickNumberOfPrizeCards + ld a, PRIZES_4 + ld [wNPCDuelPrizes], a + xor a + ld [wPrizeCardSelectionFrameCounter], a +.loop_input + call DoFrame + ld a, [wNPCDuelPrizes] + add SYM_0 + ld e, a + ; check frame counter so that it + ; either blinks or shows number + ld hl, wPrizeCardSelectionFrameCounter + ld a, [hl] + inc [hl] + and $10 + jr z, .no_blink + ld e, SYM_SPACE +.no_blink + ld a, e + lb bc, 9, 6 + call WriteByteToBGMap0 + + ldh a, [hDPadHeld] + ld b, a + ld a, [wNPCDuelPrizes] + bit D_LEFT_F, b + jr z, .check_d_right + dec a + cp PRIZES_2 + jr nc, .got_prize_count + ld a, PRIZES_6 ; wrap around to 6 + jr .got_prize_count + +.check_d_right + bit D_RIGHT_F, b + jr z, .check_a_btn + inc a + cp PRIZES_6 + 1 + jr c, .got_prize_count + ld a, PRIZES_2 +.got_prize_count + ld [wNPCDuelPrizes], a + xor a + ld [wPrizeCardSelectionFrameCounter], a + +.check_a_btn + bit A_BUTTON_F, b + jr z, .loop_input + ret diff --git a/src/engine/link/printer.asm b/src/engine/link/printer.asm new file mode 100644 index 0000000..110fde4 --- /dev/null +++ b/src/engine/link/printer.asm @@ -0,0 +1,1124 @@ +; sends serial data to printer +; if there's an error in connection, +; show Printer Not Connected scene with error message +_PreparePrinterConnection: + ld bc, $0 + lb de, PRINTERPKT_DATA, $0 + call SendPrinterPacket + ret nc ; return if no error + + ld hl, wPrinterStatus + ld a, [hl] + or a + jr nz, .asm_19e55 + ld [hl], $ff +.asm_19e55 + ld a, [hl] + cp $ff + jr z, ShowPrinterIsNotConnected +; fallthrough + +; shows message on screen depending on wPrinterStatus +; also shows SCENE_GAMEBOY_PRINTER_NOT_CONNECTED. +HandlePrinterError: + ld a, [wPrinterStatus] + cp $ff + jr z, .cable_or_printer_switch + or a + jr z, .interrupted + bit PRINTER_ERROR_BATTERIES_LOST_CHARGE, a + jr nz, .batteries_lost_charge + bit PRINTER_ERROR_CABLE_PRINTER_SWITCH, a + jr nz, .cable_or_printer_switch + bit PRINTER_ERROR_PAPER_JAMMED, a + jr nz, .jammed_printer + + ldtx hl, PrinterPacketErrorText + ld a, $04 + jr ShowPrinterConnectionErrorScene +.cable_or_printer_switch + ldtx hl, CheckCableOrPrinterSwitchText + ld a, $02 + jr ShowPrinterConnectionErrorScene +.jammed_printer + ldtx hl, PrinterPaperIsJammedText + ld a, $03 + jr ShowPrinterConnectionErrorScene +.batteries_lost_charge + ldtx hl, BatteriesHaveLostTheirChargeText + ld a, $01 + jr ShowPrinterConnectionErrorScene +.interrupted + ldtx hl, PrintingWasInterruptedText + call DrawWideTextBox_WaitForInput + scf + ret + +ShowPrinterIsNotConnected: + ldtx hl, PrinterIsNotConnectedText + ld a, $02 +; fallthrough + +; a = error code +; hl = text ID to print in text box +ShowPrinterConnectionErrorScene: + push hl + ; unnecessary loading TxRam, since the text data + ; already incorporate the error number + ld l, a + ld h, $00 + call LoadTxRam3 + + call SetSpriteAnimationsAsVBlankFunction + ld a, SCENE_GAMEBOY_PRINTER_NOT_CONNECTED + lb bc, 0, 0 + call LoadScene + pop hl + call DrawWideTextBox_WaitForInput + call RestoreVBlankFunction + scf + ret + +; main card printer function +Func_19eb4: + ld e, a + ld d, $0 + call LoadCardDataToBuffer1_FromCardID + call SetSpriteAnimationsAsVBlankFunction + ld a, SCENE_GAMEBOY_PRINTER_TRANSMITTING + lb bc, 0, 0 + call LoadScene + ld a, 20 + call CopyCardNameAndLevel + ld [hl], TX_END + ld hl, $0 + call LoadTxRam2 + ldtx hl, NowPrintingText + call DrawWideTextBox_PrintText + call EnableLCD + call PrepareForPrinterCommunications + call DrawTopCardInfoInSRAMGfxBuffer0 + call Func_19f87 + call DrawCardPicInSRAMGfxBuffer2 + call Func_19f99 + jr c, .error + call DrawBottomCardInfoInSRAMGfxBuffer0 + call Func_1a011 + jr c, .error + call RestoreVBlankFunction + call ResetPrinterCommunicationSettings + or a + ret +.error + call RestoreVBlankFunction + call ResetPrinterCommunicationSettings + jp HandlePrinterError + +DrawCardPicInSRAMGfxBuffer2: + ld hl, wLoadedCard1Gfx + ld a, [hli] + ld h, [hl] + ld l, a + ld de, sGfxBuffer2 + call Func_37a5 + ; draw card's picture in sGfxBuffer2 + ld a, $40 + lb hl, 12, 1 + lb de, 2, 68 + lb bc, 16, 12 + call FillRectangle + ret + +; writes the tiles necessary to draw +; the card's information in sGfxBuffer0 +; this includes card's type, lv, HP and attacks if Pokemon card +; or otherwise just the card's name and type symbol +DrawTopCardInfoInSRAMGfxBuffer0: + call Func_1a025 + call Func_212f + + ; draw empty text box frame + ld hl, sGfxBuffer0 + ld a, $34 + lb de, $30, $31 + ld b, 20 + call CopyLine + ld c, 15 +.loop_lines + xor a ; SYM_SPACE + lb de, $36, $37 + ld b, 20 + call CopyLine + dec c + jr nz, .loop_lines + + ; draw card type symbol + ld a, $38 + lb hl, 1, 2 + lb de, 1, 65 + lb bc, 2, 2 + call FillRectangle + ; print card's name + lb de, 4, 65 + ld hl, wLoadedCard1Name + call InitTextPrinting_ProcessTextFromPointerToID + +; prints card's type, lv, HP and attacks if it's a Pokemon card + ld a, [wLoadedCard1Type] + cp TYPE_ENERGY + jr nc, .skip_pokemon_data + inc a ; symbol corresponding to card's type (color) + lb bc, 18, 65 + call WriteByteToBGMap0 + ld a, SYM_Lv + lb bc, 11, 66 + call WriteByteToBGMap0 + ld a, [wLoadedCard1Level] + lb bc, 12, 66 + bank1call WriteTwoDigitNumberInTxSymbolFormat + ld a, SYM_HP + lb bc, 15, 66 + call WriteByteToBGMap0 + ld a, [wLoadedCard1EffectCommands] + inc b + bank1call WriteTwoByteNumberInTxSymbolFormat +.skip_pokemon_data + ret + +Func_19f87: + call TryInitPrinterCommunications + ret c + ld hl, sGfxBuffer0 + call Func_1a0cc + ret c + call Func_1a0cc + call Func_1a111 + ret + +Func_19f99: + call TryInitPrinterCommunications + ret c + ld hl, sGfxBuffer0 + $8 tiles + ld c, $06 +.asm_19fa2 + call Func_1a0cc + ret c + dec c + jr nz, .asm_19fa2 + call Func_1a111 + ret + +; writes the tiles necessary to draw +; the card's information in sGfxBuffer0 +; this includes card's Retreat cost, Weakness, Resistance, +; and attack if it's Pokemon card +; or otherwise just the card's description. +DrawBottomCardInfoInSRAMGfxBuffer0: + call Func_1a025 + xor a + ld [wCardPageType], a + ld hl, sGfxBuffer0 + ld b, 20 + ld c, 9 +.loop_lines + xor a ; SYM_SPACE + lb de, $36, $37 + call CopyLine + dec c + jr nz, .loop_lines + ld a, $35 + lb de, $32, $33 + call CopyLine + + ld a, [wLoadedCard1Type] + cp TYPE_ENERGY + jr nc, .not_pkmn_card + ld hl, RetreatWeakResistData + call PlaceTextItems + ld c, 66 + bank1call DisplayCardPage_PokemonOverview.attacks + ld a, SYM_No + lb bc, 15, 72 + call WriteByteToBGMap0 + inc b + ld a, [wLoadedCard1PokedexNumber] + bank1call WriteTwoByteNumberInTxSymbolFormat + ret + +.not_pkmn_card + bank1call SetNoLineSeparation + lb de, 1, 66 + ld a, SYM_No + call InitTextPrintingInTextbox + ld hl, wLoadedCard1NonPokemonDescription + call ProcessTextFromPointerToID + bank1call SetOneLineSeparation + ret + +RetreatWeakResistData: + textitem 1, 70, RetreatText + textitem 1, 71, WeaknessText + textitem 1, 72, ResistanceText + db $ff + +Func_1a011: + call TryInitPrinterCommunications + ret c + ld hl, sGfxBuffer0 + ld c, $05 +.asm_1a01a + call Func_1a0cc + ret c + dec c + jr nz, .asm_1a01a + call Func_1a108 + ret + +; calls setup text and sets wTilePatternSelector +Func_1a025: + lb de, $40, $bf + call SetupText + ld a, $a4 + ld [wTilePatternSelector], a + xor a + ld [wTilePatternSelectorCorrection], a + ret + +; switches to CGB normal speed, resets serial +; enables SRAM and switches to SRAM1 +; and clears sGfxBuffer0 +PrepareForPrinterCommunications: + call SwitchToCGBNormalSpeed + call ResetSerial + ld a, $10 + ld [wce9b], a + call EnableSRAM + ld a, [sPrinterContrastLevel] + ld [wPrinterContrastLevel], a + call DisableSRAM + ldh a, [hBankSRAM] + ld [wce8f], a + ld a, BANK("SRAM1") + call BankswitchSRAM + call EnableSRAM +; fallthrough + +ClearPrinterGfxBuffer: + ld hl, sGfxBuffer0 + ld bc, $400 +.loop + xor a + ld [hli], a + dec bc + ld a, c + or b + jr nz, .loop + xor a + ld [wce9f], a + ret + +; reverts settings changed by PrepareForPrinterCommunications +ResetPrinterCommunicationSettings: + push af + call SwitchToCGBDoubleSpeed + ld a, [wce8f] + call BankswitchSRAM + call DisableSRAM + lb de, $30, $bf + call SetupText + pop af + ret + +; send some bytes through serial +Func_1a080: ; unreferenced + ld bc, $0 + lb de, PRINTERPKT_NUL, $0 + jp SendPrinterPacket + +; tries initiating the communications for +; sending data to printer +; returns carry if operation was cancelled +; by pressing B button or serial transfer took long +TryInitPrinterCommunications: + xor a + ld [wPrinterInitAttempts], a +.wait_input + call DoFrame + ldh a, [hKeysHeld] + and B_BUTTON + jr nz, .b_button + ld bc, $0 + lb de, PRINTERPKT_NUL, $0 + call SendPrinterPacket + jr c, .delay + and (1 << PRINTER_STATUS_BUSY) | (1 << PRINTER_STATUS_PRINTING) + jr nz, .wait_input + +.init + ld bc, $0 + lb de, PRINTERPKT_INIT, $0 + call SendPrinterPacket + jr nc, .no_carry + ld hl, wPrinterInitAttempts + inc [hl] + ld a, [hl] + cp 3 + jr c, .wait_input + ; time out + scf + ret +.no_carry + ret + +.b_button + xor a + ld [wPrinterStatus], a + scf + ret + +.delay + ld c, 10 +.delay_loop + call DoFrame + dec c + jr nz, .delay_loop + jr .init + +; loads tiles given by map in hl to sGfxBuffer5 +; copies first 20 tiles, then offsets by 2 tiles +; and copies another 20 +Func_1a0cc: + push bc + ld de, sGfxBuffer5 + call .Copy20Tiles + call .Copy20Tiles + push hl + call CompressDataForPrinterSerialTransfer + call SendPrinterPacket + pop hl + pop bc + ret + +; copies 20 tiles given by hl to de +; then adds 2 tiles to hl +.Copy20Tiles ; 1a0e0 (6:60e0) + push hl + ld c, 20 +.loop_tiles + ld a, [hli] + call .CopyTile + dec c + jr nz, .loop_tiles + pop hl + ld bc, 2 tiles + add hl, bc + ret + +; copies a tile to de +; a = tile to get from sGfxBuffer1 +.CopyTile ; 1a0f0 (6:60f0) + push hl + push bc + ld l, a + ld h, $00 + add hl, hl + add hl, hl + add hl, hl + add hl, hl ; *TILE_SIZE + ld bc, sGfxBuffer1 + add hl, bc + ld c, TILE_SIZE +.loop_copy + ld a, [hli] + ld [de], a + inc de + dec c + jr nz, .loop_copy + pop bc + pop hl + ret + +Func_1a108: + call GetPrinterContrastSerialData + push hl + lb hl, $3, $1 + jr SendPrinterInstructionPacket + +Func_1a111: + call GetPrinterContrastSerialData + push hl + ld hl, wce9b + ld a, [hl] + ld [hl], $00 + ld h, a + ld l, $01 +; fallthrough + +SendPrinterInstructionPacket: + push hl + ld bc, $0 + lb de, PRINTERPKT_DATA, $0 + call SendPrinterPacket + jr c, .asm_1a135 + ld hl, sp+$00 ; contrast level bytes + ld bc, $4 ; instruction packets are 4 bytes in size + lb de, PRINTERPKT_PRINT_INSTRUCTION, $0 + call SendPrinterPacket +.asm_1a135 + pop hl + pop hl + ret + +; returns in h and l the bytes +; to be sent through serial to the printer +; for the set contrast level +GetPrinterContrastSerialData: + ld a, [wPrinterContrastLevel] + ld e, a + ld d, $00 + ld hl, .contrast_level_data + add hl, de + ld h, [hl] + ld l, $e4 + ret + +.contrast_level_data + db $00, $20, $40, $60, $7f + +Func_1a14b: ; unreferenced + ld a, $01 + jr .asm_1a15d + ld a, $02 + jr .asm_1a15d + ld a, $03 + jr .asm_1a15d + ld a, $04 + jr .asm_1a15d + ld a, $05 +.asm_1a15d + ld [wce9d], a + scf + ret + +; a = saved deck index to print +_PrintDeckConfiguration: +; copies selected deck from SRAM to wDuelTempList + call EnableSRAM + ld l, a + ld h, DECK_STRUCT_SIZE + call HtimesL + ld de, sSavedDeck1 + add hl, de + ld de, wDuelTempList + ld bc, DECK_STRUCT_SIZE + call CopyDataHLtoDE + call DisableSRAM + + call ShowPrinterTransmitting + call PrepareForPrinterCommunications + call Func_1a025 + call Func_212f + lb de, 0, 64 + lb bc, 20, 4 + call DrawRegularTextBoxDMG + lb de, 4, 66 + call InitTextPrinting + ld hl, wDuelTempList ; print deck name + call ProcessText + ldtx hl, DeckPrinterText + call ProcessTextFromID + + ld a, 5 + ld [wPrinterHorizontalOffset], a + ld hl, wPrinterTotalCardCount + xor a + ld [hli], a + ld [hl], a + ld [wPrintOnlyStarRarity], a + + ld hl, wCurDeckCards +.loop_cards + ld a, [hl] + or a + jr z, .asm_1a1d6 + ld e, a + ld d, $00 + call LoadCardDataToBuffer1_FromCardID + + ; find out this card's count + ld a, [hli] + ld b, a + ld c, 1 +.loop_card_count + cp [hl] + jr nz, .got_card_count + inc hl + inc c + jr .loop_card_count + +.got_card_count + ld a, c + ld [wPrinterCardCount], a + call LoadCardInfoForPrinter + call AddToPrinterGfxBuffer + jr c, .printer_error + jr .loop_cards + +.asm_1a1d6 + call SendCardListToPrinter + jr c, .printer_error + call ResetPrinterCommunicationSettings + call RestoreVBlankFunction + or a + ret + +.printer_error + call ResetPrinterCommunicationSettings + call RestoreVBlankFunction + jp HandlePrinterError + +SendCardListToPrinter: + ld a, [wPrinterHorizontalOffset] + cp 1 + jr z, .skip_load_gfx + call LoadGfxBufferForPrinter + ret c +.skip_load_gfx + call TryInitPrinterCommunications + ret c + call Func_1a108 + ret +; 0z1a1ff + +; increases printer horizontal offset by 2 +AddToPrinterGfxBuffer: + push hl + ld hl, wPrinterHorizontalOffset + inc [hl] + inc [hl] + ld a, [hl] + pop hl + ; return no carry if below 18 + cp 18 + ccf + ret nc + ; >= 18 +; fallthrough + +; copies Gfx to Gfx buffer and sends some serial data +; returns carry set if unsuccessful +LoadGfxBufferForPrinter: + push hl + call TryInitPrinterCommunications + jr c, .set_carry + ld a, [wPrinterHorizontalOffset] + srl a + ld c, a + ld hl, sGfxBuffer0 +.loop_gfx_buffer + call Func_1a0cc + jr c, .set_carry + dec c + jr nz, .loop_gfx_buffer + call Func_1a111 + jr c, .set_carry + + call ClearPrinterGfxBuffer + ld a, 1 + ld [wPrinterHorizontalOffset], a + pop hl + or a + ret + +.set_carry + pop hl + scf + ret + +; load symbol, name, level and card count to buffer +LoadCardInfoForPrinter: + push hl + ld a, [wPrinterHorizontalOffset] + or %1000000 + ld e, a + ld d, 3 + ld a, [wPrintOnlyStarRarity] + or a + jr nz, .skip_card_symbol + ld hl, wPrinterTotalCardCount + ld a, [hli] + or [hl] + call z, DrawCardSymbol +.skip_card_symbol + ld a, 14 + call CopyCardNameAndLevel + call InitTextPrinting + ld hl, wDefaultText + call ProcessText + ld a, [wPrinterHorizontalOffset] + or %1000000 + ld c, a + ld b, 16 + ld a, SYM_CROSS + call WriteByteToBGMap0 + inc b + ld a, [wPrinterCardCount] + bank1call WriteTwoDigitNumberInTxSymbolFormat + pop hl + ret + +_PrintCardList: +; if Select button is held when printing card list +; only print cards with Star rarity (excluding Promotional cards) +; even if it's not marked as seen in the collection + ld e, FALSE + ldh a, [hKeysHeld] + and SELECT + jr z, .no_select + inc e ; TRUE +.no_select + ld a, e + ld [wPrintOnlyStarRarity], a + + call ShowPrinterTransmitting + call CreateTempCardCollection + ld de, wDefaultText + call CopyPlayerName + call PrepareForPrinterCommunications + call Func_1a025 + call Func_212f + + lb de, 0, 64 + lb bc, 20, 4 + call DrawRegularTextBoxDMG + ld a, PLAYER_TURN + ldh [hWhoseTurn], a + lb de, 2, 66 + call InitTextPrinting + ld hl, wDefaultText + call ProcessText + ldtx hl, AllCardsOwnedText + call ProcessTextFromID + ld a, [wPrintOnlyStarRarity] + or a + jr z, .asm_1a2c2 + ld a, TX_HALF2FULL + call ProcessSpecialTextCharacter + lb de, 3, 84 + call Func_22ca +.asm_1a2c2 + ld a, $ff + ld [wCurPrinterCardType], a + xor a + ld hl, wPrinterTotalCardCount + ld [hli], a + ld [hl], a + ld [wPrinterNumCardTypes], a + ld a, 5 + ld [wPrinterHorizontalOffset], a + + ld e, GRASS_ENERGY +.loop_cards + push de + ld d, $00 + call LoadCardDataToBuffer1_FromCardID + jr c, .done_card_loop + ld d, HIGH(wTempCardCollection) + ld a, [de] ; card ID count in collection + ld [wPrinterCardCount], a + call .LoadCardTypeEntry + jr c, .printer_error_pop_de + + ld a, [wPrintOnlyStarRarity] + or a + jr z, .all_owned_cards_mode + ld a, [wLoadedCard1Set] + and %11110000 + cp PROMOTIONAL + jr z, .next_card + ld a, [wLoadedCard1Rarity] + cp STAR + jr nz, .next_card + ; not Promotional, and Star rarity + ld hl, wPrinterCardCount + res CARD_NOT_OWNED_F, [hl] + jr .got_card_count + +.all_owned_cards_mode + ld a, [wPrinterCardCount] + or a + jr z, .next_card + cp CARD_NOT_OWNED + jr z, .next_card ; ignore not owned cards + +.got_card_count + ld a, [wPrinterCardCount] + and CARD_COUNT_MASK + ld c, a + + ; add to total card count + ld hl, wPrinterTotalCardCount + add [hl] + ld [hli], a + ld a, 0 + adc [hl] + ld [hl], a + + ; add to current card type count + ld hl, wPrinterCurCardTypeCount + ld a, c + add [hl] + ld [hli], a + ld a, 0 + adc [hl] + ld [hl], a + + ld hl, wPrinterNumCardTypes + inc [hl] + ld hl, wce98 + inc [hl] + call LoadCardInfoForPrinter + call AddToPrinterGfxBuffer + jr c, .printer_error_pop_de +.next_card + pop de + inc e + jr .loop_cards + +.printer_error_pop_de + pop de +.printer_error + call ResetPrinterCommunicationSettings + call RestoreVBlankFunction + jp HandlePrinterError + +.done_card_loop + pop de + ; add separator line + ld a, [wPrinterHorizontalOffset] + dec a + or $40 + ld c, a + ld b, 0 + call BCCoordToBGMap0Address + ld a, $35 + lb de, $35, $35 + ld b, 20 + call CopyLine + call AddToPrinterGfxBuffer + jr c, .printer_error + + ld hl, wPrinterTotalCardCount + ld c, [hl] + inc hl + ld b, [hl] + ldtx hl, TotalNumberOfCardsText + call .PrintTextWithNumber + jr c, .printer_error + ld a, [wPrintOnlyStarRarity] + or a + jr nz, .done + ld a, [wPrinterNumCardTypes] + ld c, a + ld b, 0 + ldtx hl, TypesOfCardsText + call .PrintTextWithNumber + jr c, .printer_error + +.done + call SendCardListToPrinter + jr c, .printer_error + call ResetPrinterCommunicationSettings + call RestoreVBlankFunction + or a + ret + +; prints text ID given in hl +; with decimal representation of +; the number given in bc +; hl = text ID +; bc = number +.PrintTextWithNumber + push bc + ld a, [wPrinterHorizontalOffset] + dec a + or $40 + ld e, a + ld d, 2 + call InitTextPrinting + call ProcessTextFromID + ld d, 14 + call InitTextPrinting + pop hl + call TwoByteNumberToTxSymbol_TrimLeadingZeros + ld hl, wStringBuffer + call ProcessText + call AddToPrinterGfxBuffer + ret + +; loads this card's type icon and text +; if it's a new card type that hasn't been printed yet +.LoadCardTypeEntry + ld a, [wLoadedCard1Type] + ld c, a + cp TYPE_ENERGY + jr c, .got_type ; jump if Pokemon card + ld c, $08 + cp TYPE_TRAINER + jr nc, .got_type ; jump if Trainer card + ld c, $07 +.got_type + ld hl, wCurPrinterCardType + ld a, [hl] + cp c + ret z ; already handled this card type + + ; show corresponding icon and text + ; for this new card type + ld a, c + ld [hl], a ; set it as current card type + add a + add c ; *3 + ld c, a + ld b, $00 + ld hl, .IconTextList + add hl, bc + ld a, [wPrinterHorizontalOffset] + dec a + or %1000000 + ld e, a + ld d, 1 + ld a, [hli] + push hl + lb bc, 2, 2 + lb hl, 1, 2 + call FillRectangle + pop hl + ld d, 3 + inc e + call InitTextPrinting + ld a, [hli] + ld h, [hl] + ld l, a + call ProcessTextFromID + + call AddToPrinterGfxBuffer + ld hl, wPrinterCurCardTypeCount + xor a + ld [hli], a + ld [hl], a + ld [wce98], a + ret + +.IconTextList + ; Fire + db $e0 ; icon tile + tx FirePokemonText + + ; Grass + db $e4 ; icon tile + tx GrassPokemonText + + ; Lightning + db $e8 ; icon tile + tx LightningPokemonText + + ; Water + db $ec ; icon tile + tx WaterPokemonText + + ; Fighting + db $f0 ; icon tile + tx FightingPokemonText + + ; Psychic + db $f4 ; icon tile + tx PsychicPokemonText + + ; Colorless + db $f8 ; icon tile + tx ColorlessPokemonText + + ; Energy + db $fc ; icon tile + tx EnergyCardText + + ; Trainer + db $dc ; icon tile + tx TrainerCardText + +ShowPrinterTransmitting: + call SetSpriteAnimationsAsVBlankFunction + ld a, SCENE_GAMEBOY_PRINTER_TRANSMITTING + lb bc, 0, 0 + call LoadScene + ldtx hl, NowPrintingPleaseWaitText + call DrawWideTextBox_PrintText + call EnableLCD + ret + +; compresses $28 tiles in sGfxBuffer5 +; and writes it in sGfxBuffer5 + $28 tiles. +; compressed data has 2 commands to instruct on how to decompress it. +; - a command byte with bit 7 not set, means to copy that many + 1 +; bytes that are following it literally. +; - a command byte with bit 7 set, means to copy the following byte +; that many times + 2 (after masking the top bit of command byte). +; returns in bc the size of the compressed data and +; in de the packet type data. +CompressDataForPrinterSerialTransfer: + ld hl, sGfxBuffer5 + ld de, sGfxBuffer5 + $28 tiles + ld bc, $28 tiles +.loop_remaining_data + ld a, $ff + inc b + dec b + jr nz, .check_compression + ld a, c +.check_compression + push bc + push de + ld c, a + call CheckDataCompression + ld a, e + ld c, e + pop de + jr c, .copy_byte + ld a, c + ld b, c + dec a + ld [de], a ; number of bytes to copy literally - 1 + inc de +.copy_literal_sequence + ld a, [hli] + ld [de], a + inc de + dec c + jr nz, .copy_literal_sequence + ld c, b + jr .sub_added_bytes + +.copy_byte + ld a, c + dec a + dec a + or %10000000 ; set high bit + ld [de], a ; = (n times to copy - 2) | %10000000 + inc de + ld a, [hl] ; byte to copy n times + ld [de], a + inc de + ld b, $0 + add hl, bc + +.sub_added_bytes + ld a, c + cpl + inc a + pop bc + add c + ld c, a + ld a, $ff + adc b + ld b, a + or c + jr nz, .loop_remaining_data + + ld hl, $10000 - (sGfxBuffer5 + $28 tiles) + add hl, de ; gets the size of the compressed data + ld c, l + ld b, h + ld hl, sGfxBuffer5 + $28 tiles + lb de, PRINTERPKT_DATA, $1 + ret + +; checks whether the next byte sequence in hl, up to c bytes, can be compressed +; returns carry if the next sequence of bytes can be compressed, +; i.e. has at least 3 consecutive bytes with the same value. +; in that case, returns in e the number of consecutive +; same value bytes that were found. +; if there are no bytes with same value, then count as many bytes left +; as possible until either there are no more remaining data bytes, +; or until a sequence of 3 bytes with the same value are found. +; in that case, the number of bytes in this sequence is returned in e. +CheckDataCompression: + push hl + ld e, c + ld a, c +; if number of remaining bytes is less than 4 +; then no point in compressing + cp 4 + jr c, .no_carry + +; check first if there are at least +; 3 consecutive bytes with the same value + ld b, c + ld a, [hli] + cp [hl] + inc hl + jr nz, .literal_copy ; not same + cp [hl] + inc hl + jr nz, .literal_copy ; not same + +; 3 consecutive bytes were found with same value +; keep track of how many consecutive bytes +; with the same value there are in e + dec c + dec c + dec c + ld e, 3 +.loop_same_value + cp [hl] + jr nz, .set_carry ; exit when a different byte is found + inc hl + inc e + dec c + jr z, .set_carry ; exit when there is no more remaining data + bit 5, e + ; exit if number of consecutive bytes >= $20 + jr z, .loop_same_value +.set_carry + pop hl + scf + ret + +.literal_copy +; consecutive bytes are not the same value +; count the number of bytes there are left +; until a sequence of 3 bytes with the same value is found + pop hl + push hl + ld c, b ; number of remaining bytes + ld e, 1 + ld a, [hli] + dec c + jr z, .no_carry ; exit if no more data +.reset_same_value_count + ld d, 2 ; number of consecutive same value bytes to exit +.next_byte + inc e + dec c + jr z, .no_carry + bit 7, e + jr nz, .no_carry ; exit if >= $80 + cp [hl] + jr z, .same_consecutive_value + ld a, [hli] + jr .reset_same_value_count +.no_carry + pop hl + or a + ret + +.same_consecutive_value + inc hl + dec d + jr nz, .next_byte + ; 3 consecutive bytes with same value found + ; discard the last 3 bytes in the sequence + dec e + dec e + dec e + jr .no_carry |