diff options
| author | ElectroDeoxys <ElectroDeoxys@gmail.com> | 2021-09-23 18:37:05 +0100 |
|---|---|---|
| committer | ElectroDeoxys <ElectroDeoxys@gmail.com> | 2021-09-23 18:37:05 +0100 |
| commit | ca15abf2ffc14fdb92079f6fccff09eb04305d18 (patch) | |
| tree | d558ad09e12c7e6ed497f64573f858eb1fb9fa60 /src/engine/duel | |
| parent | 8dee6b7a11e85d6d4b9f8ec9fb9d53a499fd37dc (diff) | |
Split bank1
Diffstat (limited to 'src/engine/duel')
| -rw-r--r-- | src/engine/duel/core.asm | 8415 |
1 files changed, 8415 insertions, 0 deletions
diff --git a/src/engine/duel/core.asm b/src/engine/duel/core.asm new file mode 100644 index 0000000..3b4b95c --- /dev/null +++ b/src/engine/duel/core.asm @@ -0,0 +1,8415 @@ +; try to resume a saved duel from the main menu +TryContinueDuel: + call SetupDuel + call LoadAndValidateDuelSaveData + ldtx hl, BackUpIsBrokenText + jr c, HandleFailedToContinueDuel +; fallthrough + +_ContinueDuel: + ld hl, sp+$00 + ld a, l + ld [wDuelReturnAddress], a + ld a, h + ld [wDuelReturnAddress + 1], a + call ClearJoypad + ld a, [wDuelTheme] + call PlaySong + xor a + ld [wDuelFinished], a + call DuelMainInterface + jp MainDuelLoop.between_turns + +HandleFailedToContinueDuel: + call DrawWideTextBox_WaitForInput + call ResetSerial + scf + ret + +; this function begins the duel after the opponent's graphics, name and deck have been introduced +; loads both player's decks and sets up the variables and resources required to begin a duel. +StartDuel_VSAIOpp: + ld a, PLAYER_TURN + ldh [hWhoseTurn], a + ld a, DUELIST_TYPE_PLAYER + ld [wPlayerDuelistType], a + ld a, [wNPCDuelDeckID] + ld [wOpponentDeckID], a + call LoadPlayerDeck + call SwapTurn + call LoadOpponentDeck + call SwapTurn + jr StartDuel + +StartDuel_VSLinkOpp: + ld a, MUSIC_DUEL_THEME_1 + ld [wDuelTheme], a + ld hl, wOpponentName + xor a + ld [hli], a + ld [hl], a + ld [wIsPracticeDuel], a +; fallthrough + +StartDuel: + ld hl, sp+$0 + ld a, l + ld [wDuelReturnAddress], a + ld a, h + ld [wDuelReturnAddress + 1], a + xor a + ld [wCurrentDuelMenuItem], a + call SetupDuel + ld a, [wNPCDuelPrizes] + ld [wDuelInitialPrizes], a + call InitVariablesToBeginDuel + ld a, [wDuelTheme] + call PlaySong + call HandleDuelSetup + ret c +; fallthrough + +; the loop returns here after every turn switch +MainDuelLoop: + xor a + ld [wCurrentDuelMenuItem], a + call UpdateSubstatusConditions_StartOfTurn + call DisplayDuelistTurnScreen + call HandleTurn + +.between_turns + call ExchangeRNG + ld a, [wDuelFinished] + or a + jr nz, .duel_finished + call UpdateSubstatusConditions_EndOfTurn + call HandleBetweenTurnsEvents + call Func_3b31 + call ExchangeRNG + ld a, [wDuelFinished] + or a + jr nz, .duel_finished + ld hl, wDuelTurns + inc [hl] + ld a, [wDuelType] + cp DUELTYPE_PRACTICE + jr z, .practice_duel + +.next_turn + call SwapTurn + jr MainDuelLoop + +.practice_duel + ld a, [wIsPracticeDuel] + or a + jr z, .next_turn + ld a, [hl] + cp 15 ; the practice duel lasts 15 turns (8 player turns and 7 opponent turns) + jr c, .next_turn + xor a ; DUEL_WIN + ld [wDuelResult], a + ret + +.duel_finished + call ZeroObjectPositionsAndToggleOAMCopy + call EmptyScreen + ld a, BOXMSG_DECISION + call DrawDuelBoxMessage + ldtx hl, DecisionText + call DrawWideTextBox_WaitForInput + call EmptyScreen + ldh a, [hWhoseTurn] + push af + ld a, PLAYER_TURN + ldh [hWhoseTurn], a + call DrawDuelistPortraitsAndNames + call PrintDuelResultStats + pop af + ldh [hWhoseTurn], a + call Func_3b21 + +; animate the duel result screen +; load the correct music and animation depending on result + ld a, [wDuelFinished] + cp TURN_PLAYER_WON + jr z, .active_duelist_won_duel + cp TURN_PLAYER_LOST + jr z, .active_duelist_lost_duel + ld a, DUEL_ANIM_DUEL_DRAW + ld c, MUSIC_MATCH_DRAW + ldtx hl, DuelWasADrawText + jr .handle_duel_finished +.active_duelist_won_duel + ldh a, [hWhoseTurn] + cp PLAYER_TURN + jr nz, .opponent_won_duel +.player_won_duel + xor a ; DUEL_WIN + ld [wDuelResult], a + ld a, DUEL_ANIM_DUEL_WIN + ld c, MUSIC_MATCH_VICTORY + ldtx hl, WonDuelText + jr .handle_duel_finished +.active_duelist_lost_duel + ldh a, [hWhoseTurn] + cp PLAYER_TURN + jr nz, .player_won_duel +.opponent_won_duel + ld a, DUEL_LOSS + ld [wDuelResult], a + ld a, DUEL_ANIM_DUEL_LOSS + ld c, MUSIC_MATCH_LOSS + ldtx hl, LostDuelText + +.handle_duel_finished + call PlayDuelAnimation + ld a, c + call PlaySong + ld a, OPPONENT_TURN + ldh [hWhoseTurn], a + call DrawWideTextBox_PrintText + call EnableLCD +.wait_song + call DoFrame + call AssertSongFinished + or a + jr nz, .wait_song + ld a, [wDuelFinished] + cp TURN_PLAYER_TIED + jr z, .tied_duel + call PlayDefaultSong + call WaitForWideTextBoxInput + call Func_3b31 + call ResetSerial + ld a, PLAYER_TURN + ldh [hWhoseTurn], a + ret + +.tied_duel + call WaitForWideTextBoxInput + call Func_3b31 + ld a, [wDuelTheme] + call PlaySong + ldtx hl, StartSuddenDeathMatchText + call DrawWideTextBox_WaitForInput + ld a, 1 + ld [wDuelInitialPrizes], a + call InitVariablesToBeginDuel + ld a, [wDuelType] + cp DUELTYPE_LINK + jr z, .link_duel + ld a, PLAYER_TURN + ldh [hWhoseTurn], a + call HandleDuelSetup + jp MainDuelLoop +.link_duel + call ExchangeRNG + ld h, PLAYER_TURN + ld a, [wSerialOp] + cp $29 + jr z, .got_turn + ld h, OPPONENT_TURN +.got_turn + ld a, h + ldh [hWhoseTurn], a + call HandleDuelSetup + jp nc, MainDuelLoop + ret + +; empty the screen, and setup text and graphics for a duel +SetupDuel: + xor a ; SYM_SPACE + ld [wTileMapFill], a + call ZeroObjectPositionsAndToggleOAMCopy + call EmptyScreen + call LoadSymbolsFont + call SetDefaultPalettes + lb de, $38, $9f + call SetupText + call EnableLCD + ret + +; handle the turn of the duelist identified by hWhoseTurn. +; if player's turn, display the animation of the player drawing the card at +; hTempCardIndex_ff98, and save the duel state to SRAM. +HandleTurn: + ld a, DUELVARS_DUELIST_TYPE + call GetTurnDuelistVariable + ld [wDuelistType], a + ld a, [wDuelTurns] + cp 2 + jr c, .skip_let_evolve ; jump if it's the turn holder's first turn + call SetAllPlayAreaPokemonCanEvolve +.skip_let_evolve + call InitVariablesToBeginTurn + call DisplayDrawOneCardScreen + call DrawCardFromDeck + jr nc, .deck_not_empty + ld a, TURN_PLAYER_LOST + ld [wDuelFinished], a + ret + +.deck_not_empty + ldh [hTempCardIndex_ff98], a + call AddCardToHand + ld a, [wDuelistType] + cp DUELIST_TYPE_PLAYER + jr z, .player_turn + +; opponent's turn + call SwapTurn + call IsClairvoyanceActive + call SwapTurn + call c, DisplayPlayerDrawCardScreen + jr DuelMainInterface + +; player's turn +.player_turn + call DisplayPlayerDrawCardScreen + call SaveDuelStateToSRAM +; fallthrough + +; when a practice duel turn needs to be restarted because the player did not +; follow the instructions correctly, the game loops back here +RestartPracticeDuelTurn: + ld a, PRACTICEDUEL_PRINT_TURN_INSTRUCTIONS + call DoPracticeDuelAction +; fallthrough + +; print the main interface during a duel, including background, Pokemon, HUDs and a text box. +; the bottom text box changes depending on whether the turn belongs to the player (show the duel menu), +; an AI opponent (print "Waiting..." and a reduced menu) or a link opponent (print "<Duelist> is thinking"). +DuelMainInterface: + call DrawDuelMainScene + ld a, [wDuelistType] + cp DUELIST_TYPE_PLAYER + jr z, PrintDuelMenuAndHandleInput + cp DUELIST_TYPE_LINK_OPP + jp z, DoLinkOpponentTurn + ; DUELIST_TYPE_AI_OPP + xor a + ld [wVBlankCounter], a + ld [wSkipDuelistIsThinkingDelay], a + ldtx hl, DuelistIsThinkingText + call DrawWideTextBox_PrintTextNoDelay + call AIDoAction_Turn + ld a, $ff + ld [wPlayerAttackingCardIndex], a + ld [wPlayerAttackingAttackIndex], a + ret + +PrintDuelMenuAndHandleInput: + call DrawWideTextBox + ld hl, DuelMenuData + call PlaceTextItems +.menu_items_printed + call SaveDuelData + ld a, [wDuelFinished] + or a + ret nz + ld a, [wCurrentDuelMenuItem] + call SetMenuItem + +.handle_input + call DoFrame + ldh a, [hKeysHeld] + and B_BUTTON + jr z, .b_not_held + ldh a, [hKeysPressed] + bit D_UP_F, a + jr nz, DuelMenuShortcut_OpponentPlayArea + bit D_DOWN_F, a + jr nz, DuelMenuShortcut_PlayerPlayArea + bit D_LEFT_F, a + jr nz, DuelMenuShortcut_PlayerDiscardPile + bit D_RIGHT_F, a + jr nz, DuelMenuShortcut_OpponentDiscardPile + bit START_F, a + jp nz, DuelMenuShortcut_OpponentActivePokemon + +.b_not_held + ldh a, [hKeysPressed] + and START + jp nz, DuelMenuShortcut_PlayerActivePokemon + ldh a, [hKeysPressed] + bit SELECT_F, a + jp nz, DuelMenuShortcut_BothActivePokemon + ld a, [wDebugSkipDuelMenuInput] + or a + jr nz, .handle_input + call HandleDuelMenuInput + ld a, e + ld [wCurrentDuelMenuItem], a + jr nc, .handle_input + ldh a, [hCurMenuItem] + ld hl, DuelMenuFunctionTable + jp JumpToFunctionInTable + +DuelMenuFunctionTable: + dw DuelMenu_Hand + dw DuelMenu_Attack + dw DuelMenu_Check + dw DuelMenu_PkmnPower + dw DuelMenu_Retreat + dw DuelMenu_Done + +DrawCardFromDeckToHand: + call DrawCardFromDeck + call nc, AddCardToHand + ld a, OPPACTION_DRAW_CARD + call SetOppAction_SerialSendDuelData + jp PrintDuelMenuAndHandleInput.menu_items_printed + +; triggered by pressing B + UP in the duel menu +DuelMenuShortcut_OpponentPlayArea: + call OpenNonTurnHolderPlayAreaScreen + jp DuelMainInterface + +; triggered by pressing B + DOWN in the duel menu +DuelMenuShortcut_PlayerPlayArea: + call OpenTurnHolderPlayAreaScreen + jp DuelMainInterface + +; triggered by pressing B + RIGHT in the duel menu +DuelMenuShortcut_OpponentDiscardPile: + call OpenNonTurnHolderDiscardPileScreen + jp c, PrintDuelMenuAndHandleInput + jp DuelMainInterface + +; triggered by pressing B + LEFT in the duel menu +DuelMenuShortcut_PlayerDiscardPile: + call OpenTurnHolderDiscardPileScreen + jp c, PrintDuelMenuAndHandleInput + jp DuelMainInterface + +; draw the non-turn holder's play area screen +OpenNonTurnHolderPlayAreaScreen: + call SwapTurn + call OpenTurnHolderPlayAreaScreen + call SwapTurn + ret + +; draw the turn holder's play area screen +OpenTurnHolderPlayAreaScreen: + call HasAlivePokemonInPlayArea + jp OpenPlayAreaScreenForViewing + +; draw the non-turn holder's discard pile screen +OpenNonTurnHolderDiscardPileScreen: + call SwapTurn + call OpenDiscardPileScreen + jp SwapTurn + +; draw the turn holder's discard pile screen +OpenTurnHolderDiscardPileScreen: + jp OpenDiscardPileScreen + +; draw the non-turn holder's hand screen. simpler version of OpenPlayerHandScreen +; used only for checking the cards rather than for playing them. +OpenNonTurnHolderHandScreen_Simple: + call SwapTurn + call OpenTurnHolderHandScreen_Simple + jp SwapTurn + +; draw the turn holder's hand screen. simpler version of OpenPlayerHandScreen +; used only for checking the cards rather than for playing them. +; used for example in the "Your Play Area" screen of the Check menu +OpenTurnHolderHandScreen_Simple: + call CreateHandCardList + jr c, .no_cards_in_hand + call InitAndDrawCardListScreenLayout + ld a, START + A_BUTTON + ld [wNoItemSelectionMenuKeys], a + jp DisplayCardList +.no_cards_in_hand + ldtx hl, NoCardsInHandText + jp DrawWideTextBox_WaitForInput + +; triggered by pressing B + START in the duel menu +DuelMenuShortcut_OpponentActivePokemon: + call SwapTurn + call OpenActivePokemonScreen + call SwapTurn + jp DuelMainInterface + +; triggered by pressing START in the duel menu +DuelMenuShortcut_PlayerActivePokemon: + call OpenActivePokemonScreen + jp DuelMainInterface + +; draw the turn holder's active Pokemon screen if it exists +OpenActivePokemonScreen: + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + cp -1 + ret z + call GetCardIDFromDeckIndex + call LoadCardDataToBuffer1_FromCardID + ld hl, wCurPlayAreaSlot + xor a + ld [hli], a + ld [hl], a ; wCurPlayAreaY + call OpenCardPage_FromCheckPlayArea + ret + +; triggered by selecting the "Pkmn Power" item in the duel menu +DuelMenu_PkmnPower: + call Func_6431 + jp c, DuelMainInterface + call UseAttackOrPokemonPower + jp DuelMainInterface + +; triggered by selecting the "Done" item in the duel menu +DuelMenu_Done: + ld a, PRACTICEDUEL_REPEAT_INSTRUCTIONS + call DoPracticeDuelAction + ; always jumps on practice duel (no action requires player to select Done) + jp c, RestartPracticeDuelTurn + ld a, OPPACTION_FINISH_NO_ATTACK + call SetOppAction_SerialSendDuelData + call ClearNonTurnTemporaryDuelvars + ret + +; triggered by selecting the "Retreat" item in the duel menu +DuelMenu_Retreat: + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + and CNF_SLP_PRZ + cp CONFUSED + ldh [hTemp_ffa0], a + jr nz, .not_confused + ld a, [wGotHeadsFromConfusionCheckDuringRetreat] + or a + jr nz, .unable_due_to_confusion + call CheckAbleToRetreat + jr c, .unable_to_retreat + call DisplayRetreatScreen + jr c, .done + ldtx hl, SelectPkmnOnBenchToSwitchWithActiveText + call DrawWideTextBox_WaitForInput + call OpenPlayAreaScreenForSelection + jr c, .done + ld [wBenchSelectedPokemon], a + ld a, [wBenchSelectedPokemon] + ldh [hTempPlayAreaLocation_ffa1], a + ld a, OPPACTION_ATTEMPT_RETREAT + call SetOppAction_SerialSendDuelData + call AttemptRetreat + jr nc, .done + call DrawDuelMainScene + +.unable_due_to_confusion + ldtx hl, UnableToRetreatText + call DrawWideTextBox_WaitForInput + jp PrintDuelMenuAndHandleInput + +.not_confused + ; note that the energy cards are discarded (DiscardRetreatCostCards), then returned + ; (ReturnRetreatCostCardsToArena), then discarded again for good (AttemptRetreat). + ; It's done this way so that the retreating Pokemon is listed with its energies updated + ; when the Play Area screen is shown to select the Pokemon to switch to. The reason why + ; AttemptRetreat is responsible for discarding the energy cards is because, if the + ; Pokemon is confused, it may not be able to retreat, so they cannot be discarded earlier. + call CheckAbleToRetreat + jr c, .unable_to_retreat + call DisplayRetreatScreen + jr c, .done + call DiscardRetreatCostCards + ldtx hl, SelectPkmnOnBenchToSwitchWithActiveText + call DrawWideTextBox_WaitForInput + call OpenPlayAreaScreenForSelection + ld [wBenchSelectedPokemon], a + ldh [hTempPlayAreaLocation_ffa1], a + push af + call ReturnRetreatCostCardsToArena + pop af + jp c, DuelMainInterface + ld a, OPPACTION_ATTEMPT_RETREAT + call SetOppAction_SerialSendDuelData + call AttemptRetreat + +.done + jp DuelMainInterface + +.unable_to_retreat + call DrawWideTextBox_WaitForInput + jp PrintDuelMenuAndHandleInput + +; triggered by selecting the "Hand" item in the duel menu +DuelMenu_Hand: + ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND + call GetTurnDuelistVariable + or a + jr nz, OpenPlayerHandScreen + ldtx hl, NoCardsInHandText + call DrawWideTextBox_WaitForInput + jp PrintDuelMenuAndHandleInput + +; draw the screen for the player's hand and handle user input to for example check +; a card or attempt to use a card, playing the card if possible in that case. +OpenPlayerHandScreen: + call CreateHandCardList + call InitAndDrawCardListScreenLayout + ldtx hl, PleaseSelectHandText + call SetCardListInfoBoxText + ld a, PLAY_CHECK + ld [wCardListItemSelectionMenuType], a +.handle_input + call DisplayCardList + push af + ld a, [wSortCardListByID] + or a + call nz, SortHandCardsByID + pop af + jp c, DuelMainInterface + ldh a, [hTempCardIndex_ff98] + call LoadCardDataToBuffer1_FromDeckIndex + ld a, [wLoadedCard1Type] + ld c, a + bit TYPE_TRAINER_F, c + jr nz, .trainer_card + bit TYPE_ENERGY_F, c + jr nz, PlayEnergyCard + call PlayPokemonCard + jr c, ReloadCardListScreen ; jump if card not played + jp DuelMainInterface +.trainer_card + call PlayTrainerCard + jr c, ReloadCardListScreen ; jump if card not played + jp DuelMainInterface + +; play the energy card with deck index at hTempCardIndex_ff98 +; c contains the type of energy card being played +PlayEnergyCard: + ld a, c + cp TYPE_ENERGY_WATER + jr nz, .not_water_energy + call IsRainDanceActive + jr c, .rain_dance_active + +.not_water_energy + ld a, [wAlreadyPlayedEnergy] + or a + jr nz, .already_played_energy + call HasAlivePokemonInPlayArea + call OpenPlayAreaScreenForSelection ; choose card to play energy card on + jp c, DuelMainInterface ; exit if no card was chosen +.play_energy_set_played + ld a, 1 + ld [wAlreadyPlayedEnergy], a +.play_energy + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTempPlayAreaLocation_ffa1], a + ld e, a + ldh a, [hTempCardIndex_ff98] + ldh [hTemp_ffa0], a + call PutHandCardInPlayArea + call PrintPlayAreaCardList_EnableLCD + ld a, OPPACTION_PLAY_ENERGY + call SetOppAction_SerialSendDuelData + call PrintAttachedEnergyToPokemon + jp DuelMainInterface + +.rain_dance_active + call HasAlivePokemonInPlayArea + call OpenPlayAreaScreenForSelection ; choose card to play energy card on + jp c, DuelMainInterface ; exit if no card was chosen + call CheckRainDanceScenario + jr c, .play_energy + ld a, [wAlreadyPlayedEnergy] + or a + jr z, .play_energy_set_played + ldtx hl, MayOnlyAttachOneEnergyCardText + call DrawWideTextBox_WaitForInput + jp OpenPlayerHandScreen + +.already_played_energy + ldtx hl, MayOnlyAttachOneEnergyCardText + call DrawWideTextBox_WaitForInput +; fallthrough + +; reload the card list screen after the card trying to play couldn't be played +ReloadCardListScreen: + call CreateHandCardList + ; skip doing the things that have already been done when initially opened + call DrawCardListScreenLayout + jp OpenPlayerHandScreen.handle_input + +; place a basic Pokemon card on the arena or bench, or place an stage 1 or 2 +; Pokemon card over a Pokemon card already in play to evolve it. +; the card to use is loaded in wLoadedCard1 and its deck index is at hTempCardIndex_ff98. +; return nc if the card was played, carry if it wasn't. +PlayPokemonCard: + ld a, [wLoadedCard1Stage] + or a ; BASIC + jr nz, .try_evolve ; jump if the card being played is a Stage 1 or 2 Pokemon + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp MAX_PLAY_AREA_POKEMON + jr nc, .no_space + ldh a, [hTempCardIndex_ff98] + ldh [hTemp_ffa0], a + call PutHandPokemonCardInPlayArea + ldh [hTempPlayAreaLocation_ff9d], a + add DUELVARS_ARENA_CARD_STAGE + call GetTurnDuelistVariable + ld [hl], BASIC + ld a, OPPACTION_PLAY_BASIC_PKMN + call SetOppAction_SerialSendDuelData + ldh a, [hTempCardIndex_ff98] + call LoadCardDataToBuffer1_FromDeckIndex + ld a, 20 + call CopyCardNameAndLevel + ld [hl], $00 + ld hl, $0000 + call LoadTxRam2 + ldtx hl, PlacedOnTheBenchText + call DrawWideTextBox_WaitForInput + call Func_161e + or a + ret + +.no_space + ldtx hl, NoSpaceOnTheBenchText + call DrawWideTextBox_WaitForInput + scf + ret + +.try_evolve + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld c, a + ldh a, [hTempCardIndex_ff98] + ld d, a + ld e, PLAY_AREA_ARENA + push de + push bc +.next_play_area_pkmn + push de + call CheckIfCanEvolveInto + pop de + jr nc, .can_evolve + inc e + dec c + jr nz, .next_play_area_pkmn + pop bc + pop de +.find_cant_evolve_reason_loop + push de + call CheckIfCanEvolveInto + pop de + ldtx hl, CantEvolvePokemonInSameTurnItsPlacedText + jr nz, .cant_same_turn + inc e + dec c + jr nz, .find_cant_evolve_reason_loop + ldtx hl, NoPokemonCapableOfEvolvingText +.cant_same_turn + ; don't bother opening the selection screen if there are no pokemon capable of evolving + call DrawWideTextBox_WaitForInput + scf + ret + +.can_evolve + pop bc + pop de + call IsPrehistoricPowerActive + jr c, .prehistoric_power + call HasAlivePokemonInPlayArea +.try_evolve_loop + call OpenPlayAreaScreenForSelection + jr c, .done + ldh a, [hTempCardIndex_ff98] + ldh [hTemp_ffa0], a + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hTempPlayAreaLocation_ffa1], a + call EvolvePokemonCardIfPossible + jr c, .try_evolve_loop ; jump if evolution wasn't successful somehow + ld a, OPPACTION_EVOLVE_PKMN + call SetOppAction_SerialSendDuelData + call PrintPlayAreaCardList_EnableLCD + call PrintPokemonEvolvedIntoPokemon + call Func_161e +.done + or a + ret + +.prehistoric_power + call DrawWideTextBox_WaitForInput + scf + ret + +; triggered by selecting the "Check" item in the duel menu +DuelMenu_Check: + call Func_3b31 + call OpenDuelCheckMenu + jp DuelMainInterface + +; triggered by pressing SELECT in the duel menu +DuelMenuShortcut_BothActivePokemon: + call Func_3b31 + call Func_4597 + jp DuelMainInterface + +Func_4597: + call OpenInPlayAreaScreen_FromSelectButton + ret c + call .Func_45a9 + ret c + call SwapTurn + call .Func_45a9 + call SwapTurn + ret + +.Func_45a9 + call HasAlivePokemonInPlayArea + ld a, $02 + ld [wcbd4], a + call OpenPlayAreaScreenForViewing + ldh a, [hKeysPressed] + and B_BUTTON + ret z + scf + ret + +; check if the turn holder's arena Pokemon is unable to retreat due to +; some status condition or due the bench containing no alive Pokemon. +; return carry if unable, nc if able. +CheckAbleToRetreat: + call CheckCantRetreatDueToAcid + ret c + call CheckIfActiveCardParalyzedOrAsleep + ret c + call HasAlivePokemonInBench + jr c, .unable_to_retreat + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + call LoadCardDataToBuffer1_FromCardID + ld a, [wLoadedCard1Type] + cp TYPE_TRAINER + jr z, .unable_to_retreat + call CheckIfEnoughEnergiesToRetreat + jr c, .not_enough_energies + or a + ret +.not_enough_energies + ld a, [wEnergyCardsRequiredToRetreat] + ld l, a + ld h, $00 + call LoadTxRam3 + ldtx hl, EnergyCardsRequiredToRetreatText + jr .done +.unable_to_retreat + ldtx hl, UnableToRetreatText +.done + scf + ret + +; check if the turn holder's arena Pokemon has enough energies attached to it +; in order to retreat. Return carry if it doesn't. +; load amount of energies required to wEnergyCardsRequiredToRetreat. +CheckIfEnoughEnergiesToRetreat: + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + xor a + ldh [hTempPlayAreaLocation_ff9d], a + call GetPlayAreaCardRetreatCost + ld [wEnergyCardsRequiredToRetreat], a + ld c, a + ld a, [wTotalAttachedEnergies] + cp c + ret c + ld [wNumRetreatEnergiesSelected], a + ld a, c + ld [wEnergyCardsRequiredToRetreat], a + or a + ret + +; display the screen that prompts the player to select energy cards to discard +; in order to retreat a Pokemon card. also handle input in order to display +; the amount of energy cards already selected, and return whenever enough +; energy cards have been selected or if the player declines to retreat. +DisplayRetreatScreen: + ld a, $ff + ldh [hTempRetreatCostCards], a + ld a, [wEnergyCardsRequiredToRetreat] + or a + ret z ; return if no energy cards are required at all + xor a + ld [wNumRetreatEnergiesSelected], a + call CreateArenaOrBenchEnergyCardList + call SortCardsInDuelTempListByID + ld a, LOW(hTempRetreatCostCards) + ld [wTempRetreatCostCardsPos], a + xor a + call DisplayEnergyDiscardScreen + ld a, [wEnergyCardsRequiredToRetreat] + ld [wEnergyDiscardMenuDenominator], a +.select_energies_loop + ld a, [wNumRetreatEnergiesSelected] + ld [wEnergyDiscardMenuNumerator], a + call HandleEnergyDiscardMenuInput + ret c + ldh a, [hTempCardIndex_ff98] + call LoadCardDataToBuffer2_FromDeckIndex + ; append selected energy card to hTempRetreatCostCards + ld hl, wTempRetreatCostCardsPos + ld c, [hl] + inc [hl] + ldh a, [hTempCardIndex_ff98] + ld [$ff00+c], a + ; accumulate selected energy card + ld c, 1 + ld a, [wLoadedCard2Type] + cp TYPE_ENERGY_DOUBLE_COLORLESS + jr nz, .not_double + inc c +.not_double + ld hl, wNumRetreatEnergiesSelected + ld a, [hl] + add c + ld [hl], a + ld hl, wEnergyCardsRequiredToRetreat + cp [hl] + jr nc, .enough + ; not enough energies selected yet + ldh a, [hTempCardIndex_ff98] + call RemoveCardFromDuelTempList + call DisplayEnergyDiscardMenu + jr .select_energies_loop +.enough + ; terminate hTempRetreatCostCards array with $ff + ld a, [wTempRetreatCostCardsPos] + ld c, a + ld a, $ff + ld [$ff00+c], a + or a + ret + +; display the screen that prompts the player to select energy cards to discard +; in order to retreat a Pokemon card or use an attack like Ember. includes the +; card's information and a menu to select the attached energy cards to discard. +; input: a = PLAY_AREA_* of the Pokemon trying to discard energies from. +DisplayEnergyDiscardScreen: + ld [wcbe0], a + call EmptyScreen + call LoadDuelCardSymbolTiles + call LoadDuelFaceDownCardTiles + ld a, [wcbe0] + ld hl, wCurPlayAreaSlot + ld [hli], a + ld [hl], 0 ; wCurPlayAreaY + call PrintPlayAreaCardInformation + xor a + ld [wEnergyDiscardMenuNumerator], a + inc a + ld [wEnergyDiscardMenuDenominator], a +; fallthrough + +; display the menu that belongs to the energy discard screen that lets the player +; select energy cards attached to a Pokemon card in order to retreat it or use +; an attack like Ember, Flamethrower... +DisplayEnergyDiscardMenu: + lb de, 0, 3 + lb bc, 20, 10 + call DrawRegularTextBox + ldtx hl, ChooseEnergyCardToDiscardText + call DrawWideTextBox_PrintTextNoDelay + call EnableLCD + call CountCardsInDuelTempList + ld hl, EnergyDiscardCardListParameters + lb de, 0, 0 ; initial page scroll offset, initial item (in the visible page) + call PrintCardListItems + ld a, 4 + ld [wCardListIndicatorYPosition], a + ret + +; if [wEnergyDiscardMenuDenominator] non-0: + ; prints "[wEnergyDiscardMenuNumerator]/[wEnergyDiscardMenuDenominator]" at 16,16 + ; where [wEnergyDiscardMenuNumerator] is the number of energy cards already selected to discard + ; and [wEnergyDiscardMenuDenominator] is the total number of energies that are required to discard. +; if [wEnergyDiscardMenuDenominator] == 0: + ; prints only "[wEnergyDiscardMenuNumerator]" +HandleEnergyDiscardMenuInput: + lb bc, 16, 16 + ld a, [wEnergyDiscardMenuDenominator] + or a + jr z, .print_single_number + ld a, [wEnergyDiscardMenuNumerator] + add SYM_0 + call WriteByteToBGMap0 + inc b + ld a, SYM_SLASH + call WriteByteToBGMap0 + inc b + ld a, [wEnergyDiscardMenuDenominator] + add SYM_0 + call WriteByteToBGMap0 + jr .wait_input +.print_single_number + ld a, [wEnergyDiscardMenuNumerator] + inc b + call WriteTwoDigitNumberInTxSymbolFormat +.wait_input + call DoFrame + call HandleCardListInput + jr nc, .wait_input + cp $ff ; B pressed? + jr z, .return_carry + call GetCardInDuelTempList_OnlyDeckIndex + or a + ret +.return_carry + scf + ret + +EnergyDiscardCardListParameters: + db 1, 5 ; cursor x, cursor y + db 4 ; item x + db 14 ; maximum length, in tiles, occupied by the name and level string of each card in the list + db 4 ; number of items selectable without scrolling + db SYM_CURSOR_R ; cursor tile number + db SYM_SPACE ; tile behind cursor + dw NULL ; function pointer if non-0 + +; triggered by selecting the "Attack" item in the duel menu +DuelMenu_Attack: + call HandleCantAttackSubstatus + jr c, .alert_cant_attack_and_cancel_menu + call CheckIfActiveCardParalyzedOrAsleep + jr nc, .can_attack +.alert_cant_attack_and_cancel_menu + call DrawWideTextBox_WaitForInput + jp PrintDuelMenuAndHandleInput + +.can_attack + xor a + ld [wSelectedDuelSubMenuItem], a +.try_open_attack_menu + call PrintAndLoadAttacksToDuelTempList + or a + jr nz, .open_attack_menu + ldtx hl, NoSelectableAttackText + call DrawWideTextBox_WaitForInput + jp PrintDuelMenuAndHandleInput + +.open_attack_menu + push af + ld a, [wSelectedDuelSubMenuItem] + ld hl, AttackMenuParameters + call InitializeMenuParameters + pop af + ld [wNumMenuItems], a + ldh a, [hWhoseTurn] + ld h, a + ld l, DUELVARS_ARENA_CARD + ld a, [hl] + call LoadCardDataToBuffer1_FromDeckIndex + +.wait_for_input + call DoFrame + ldh a, [hKeysPressed] + and START + jr nz, .display_selected_attack_info + call HandleMenuInput + jr nc, .wait_for_input + cp -1 ; was B pressed? + jp z, PrintDuelMenuAndHandleInput + ld [wSelectedDuelSubMenuItem], a + call CheckIfEnoughEnergiesToAttack + jr nc, .enough_energy + ldtx hl, NotEnoughEnergyCardsText + call DrawWideTextBox_WaitForInput + jr .try_open_attack_menu + +.enough_energy + ldh a, [hCurMenuItem] + add a + ld e, a + ld d, $00 + ld hl, wDuelTempList + add hl, de + ld d, [hl] ; card's deck index (0 to 59) + inc hl + ld e, [hl] ; attack index (0 or 1) + call CopyAttackDataAndDamage_FromDeckIndex + call HandleAmnesiaSubstatus + jr c, .cannot_use_due_to_amnesia + ld a, PRACTICEDUEL_VERIFY_PLAYER_TURN_ACTIONS + call DoPracticeDuelAction + ; if player did something wrong in the practice duel, jump in order to restart turn + jp c, RestartPracticeDuelTurn + call UseAttackOrPokemonPower + jp c, DuelMainInterface + ret + +.cannot_use_due_to_amnesia + call DrawWideTextBox_WaitForInput + jr .try_open_attack_menu + +.display_selected_attack_info + call OpenAttackPage + call DrawDuelMainScene + jp .try_open_attack_menu + +; draw the attack page of the card at wLoadedCard1 and of the attack selected in the Attack +; menu by hCurMenuItem, and listen for input in order to switch the page or to exit. +OpenAttackPage: + ld a, CARDPAGE_POKEMON_OVERVIEW + ld [wCardPageNumber], a + xor a + ld [wCurPlayAreaSlot], a + call EmptyScreen + call Func_3b31 + ld de, v0Tiles1 + $20 tiles + call LoadLoaded1CardGfx + call SetOBP1OrSGB3ToCardPalette + call SetBGP6OrSGB3ToCardPalette + call FlushAllPalettesOrSendPal23Packet + lb de, $38, $30 ; X Position and Y Position of top-left corner + call PlaceCardImageOAM + lb de, 6, 4 + call ApplyBGP6OrSGB3ToCardImage + ldh a, [hCurMenuItem] + ld [wSelectedDuelSubMenuItem], a + add a + ld e, a + ld d, $00 + ld hl, wDuelTempList + 1 + add hl, de + ld a, [hl] + or a + jr nz, .attack_2 + xor a ; ATTACKPAGE_ATTACK1_1 + jr .attack_1 + +.attack_2 + ld a, ATTACKPAGE_ATTACK2_1 + +.attack_1 + ld [wAttackPageNumber], a + +.open_page + call DisplayAttackPage + call EnableLCD + +.loop + call DoFrame + ; switch page (see SwitchAttackPage) if Right or Left pressed + ldh a, [hDPadHeld] + and D_RIGHT | D_LEFT + jr nz, .open_page + ; return to Attack menu if A or B pressed + ldh a, [hKeysPressed] + and A_BUTTON | B_BUTTON + jr z, .loop + ret + +AttackMenuParameters: + db 1, 13 ; cursor x, cursor y + db 2 ; y displacement between items + db 2 ; number of items + db SYM_CURSOR_R ; cursor tile number + db SYM_SPACE ; tile behind cursor + dw NULL ; function pointer if non-0 + +; display the card page with id at wAttackPageNumber of wLoadedCard1 +DisplayAttackPage: + ld a, [wAttackPageNumber] + ld hl, AttackPageDisplayPointerTable + jp JumpToFunctionInTable + +AttackPageDisplayPointerTable: + dw DisplayAttackPage_Attack1Page1 ; ATTACKPAGE_ATTACK1_1 + dw DisplayAttackPage_Attack1Page2 ; ATTACKPAGE_ATTACK1_2 + dw DisplayAttackPage_Attack2Page1 ; ATTACKPAGE_ATTACK2_1 + dw DisplayAttackPage_Attack2Page2 ; ATTACKPAGE_ATTACK2_2 + +; display ATTACKPAGE_ATTACK1_1 +DisplayAttackPage_Attack1Page1: + call DisplayCardPage_PokemonAttack1Page1 + jr SwitchAttackPage + +; display ATTACKPAGE_ATTACK1_2 if it exists. otherwise return in order +; to switch back to ATTACKPAGE_ATTACK1_1 and display it instead. +DisplayAttackPage_Attack1Page2: + ld hl, wLoadedCard1Atk1Description + 2 + ld a, [hli] + or [hl] + ret z + call DisplayCardPage_PokemonAttack1Page2 + jr SwitchAttackPage + +; display ATTACKPAGE_ATTACK2_1 +DisplayAttackPage_Attack2Page1: + call DisplayCardPage_PokemonAttack2Page1 + jr SwitchAttackPage + +; display ATTACKPAGE_ATTACK2_2 if it exists. otherwise return in order +; to switch back to ATTACKPAGE_ATTACK2_1 and display it instead. +DisplayAttackPage_Attack2Page2: + ld hl, wLoadedCard1Atk2Description + 2 + ld a, [hli] + or [hl] + ret z + call DisplayCardPage_PokemonAttack2Page2 +; fallthrough + +; switch to ATTACKPAGE_ATTACK*_2 if in ATTACKPAGE_ATTACK*_1 and vice versa. +; sets the next attack page to switch to if Right or Left are pressed. +SwitchAttackPage: + ld hl, wAttackPageNumber + ld a, $01 + xor [hl] + ld [hl], a + ret + +; given the card at hTempCardIndex_ff98, for each non-empty, non-Pokemon Power attack slot, +; prints its information at lines 13 (first attack, if any), and 15 (second attack, if any) +; also, copies zero, one, or both of the following to wDuelTempList, $ff terminated: +; if pokemon's first attack slot isn't empty or a Pokemon Power: <card_index>, 0 +; if pokemon's second attack slot isn't empty or a Pokemon Power: <card_index>, 1 +; return the amount of non-empty, non-Pokemon Power attacks in a. +PrintAndLoadAttacksToDuelTempList: + call DrawWideTextBox + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ldh [hTempCardIndex_ff98], a + call LoadCardDataToBuffer1_FromDeckIndex + ld c, 0 + ld b, 13 + ld hl, wDuelTempList + xor a + ld [wCardPageNumber], a + ld de, wLoadedCard1Atk1Name + call CheckAttackSlotEmptyOrPokemonPower + jr c, .check_second_atk_slot + ldh a, [hTempCardIndex_ff98] + ld [hli], a + xor a + ld [hli], a + inc c + push hl + push bc + ld e, b + ld hl, wLoadedCard1Atk1Name + call PrintAttackOrPkmnPowerInformation + pop bc + pop hl + inc b + inc b ; 15 + +.check_second_atk_slot + ld de, wLoadedCard1Atk2Name + call CheckAttackSlotEmptyOrPokemonPower + jr c, .done + ldh a, [hTempCardIndex_ff98] + ld [hli], a + ld a, $01 + ld [hli], a + inc c + push hl + push bc + ld e, b + ld hl, wLoadedCard1Atk2Name + call PrintAttackOrPkmnPowerInformation + pop bc + pop hl + +.done + ld a, c + ret + +; given de = wLoadedCard*Atk*Name, return carry if the attack is a +; Pkmn Power or if the attack slot is empty. +CheckAttackSlotEmptyOrPokemonPower: + push hl + push de + push bc + ld a, [de] + ld c, a + inc de + ld a, [de] + or c + jr z, .return_no_atk_found + ld hl, CARD_DATA_ATTACK1_CATEGORY - (CARD_DATA_ATTACK1_NAME + 1) + add hl, de + ld a, [hl] + and $ff ^ RESIDUAL + cp POKEMON_POWER + jr z, .return_no_atk_found + or a +.return + pop bc + pop de + pop hl + ret +.return_no_atk_found + scf + jr .return + +; check if the arena pokemon card has enough energy attached to it +; in order to use the selected attack. +; returns: carry if not enough energy, nc if enough energy. +CheckIfEnoughEnergiesToAttack: + push hl + push bc + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + call HandleEnergyBurn + ldh a, [hCurMenuItem] + add a + ld e, a + ld d, $0 + ld hl, wDuelTempList + add hl, de + ld d, [hl] ; card's deck index (0 to 59) + inc hl + ld e, [hl] ; attack index (0 or 1) + call _CheckIfEnoughEnergiesToAttack + pop bc + pop hl + ret + +; check if a pokemon card has enough energy attached to it in order to use an attack +; input: +; d = deck index of card (0 to 59) +; e = attack index (0 or 1) +; wAttachedEnergies and wTotalAttachedEnergies +; returns: carry if not enough energy, nc if enough energy. +_CheckIfEnoughEnergiesToAttack: + push de + ld a, d + call LoadCardDataToBuffer1_FromDeckIndex + pop bc + push bc + ld de, wLoadedCard1Atk1EnergyCost + ld a, c + or a + jr z, .got_atk + ld de, wLoadedCard1Atk2EnergyCost + +.got_atk + ld hl, CARD_DATA_ATTACK1_NAME - CARD_DATA_ATTACK1_ENERGY_COST + add hl, de + ld a, [hli] + or [hl] + jr z, .not_usable_or_not_enough_energies + ld hl, CARD_DATA_ATTACK1_CATEGORY - CARD_DATA_ATTACK1_ENERGY_COST + add hl, de + ld a, [hl] + cp POKEMON_POWER + jr z, .not_usable_or_not_enough_energies + xor a + ld [wAttachedEnergiesAccum], a + ld hl, wAttachedEnergies + ld c, (NUM_COLORED_TYPES) / 2 + +.next_energy_type_pair + ld a, [de] + swap a + call CheckIfEnoughEnergiesOfType + jr c, .not_usable_or_not_enough_energies + ld a, [de] + call CheckIfEnoughEnergiesOfType + jr c, .not_usable_or_not_enough_energies + inc de + dec c + jr nz, .next_energy_type_pair + ld a, [de] ; colorless energy + swap a + and $f + ld b, a + ld a, [wAttachedEnergiesAccum] + ld c, a + ld a, [wTotalAttachedEnergies] + sub c + cp b + jr c, .not_usable_or_not_enough_energies + or a +.done + pop de + ret + +.not_usable_or_not_enough_energies + scf + jr .done + +; given the amount of energies of a specific type required for an attack in the +; lower nybble of register a, test if the pokemon card has enough energies of that type +; to use the attack. Return carry if not enough energy, nc if enough energy. +CheckIfEnoughEnergiesOfType: + and $f + push af + push hl + ld hl, wAttachedEnergiesAccum + add [hl] + ld [hl], a ; accumulate the amount of energies required + pop hl + pop af + jr z, .enough_energies ; jump if no energies of this type are required + cp [hl] + ; jump if the energies required of this type are not more than the amount attached + jr z, .enough_energies + jr c, .enough_energies + inc hl + scf + ret + +.enough_energies + inc hl + or a + ret + +; return carry and the corresponding text in hl if the turn holder's +; arena Pokemon card is paralyzed or asleep. +CheckIfActiveCardParalyzedOrAsleep: + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + and CNF_SLP_PRZ + cp PARALYZED + jr z, .paralyzed + cp ASLEEP + jr z, .asleep + or a + ret +.paralyzed + ldtx hl, UnableDueToParalysisText + jr .return_with_status_condition +.asleep + ldtx hl, UnableDueToSleepText +.return_with_status_condition + scf + ret + +; display the animation of the turn duelist drawing one card at the beginning of the turn +; if there isn't any card left in the deck, let the player know with a text message +DisplayDrawOneCardScreen: + ld a, 1 +; fallthrough + +; display the animation of the turn duelist drawing number of cards that is in a. +; if there isn't any card left in the deck, let the player know with a text message. +; input: +; - a = number of cards to draw +DisplayDrawNCardsScreen: + push hl + push de + push bc + ld [wNumCardsTryingToDraw], a + xor a + ld [wNumCardsBeingDrawn], a + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + ld a, DECK_SIZE + sub [hl] + ld hl, wNumCardsTryingToDraw + cp [hl] + jr nc, .has_cards_left + ; trying to draw more cards than there are left in the deck + ld [hl], a ; 0 +.has_cards_left + ld a, [wDuelDisplayedScreen] + cp DRAW_CARDS + jr z, .portraits_drawn + cp SHUFFLE_DECK + jr z, .portraits_drawn + call EmptyScreen + call DrawDuelistPortraitsAndNames +.portraits_drawn + ld a, DRAW_CARDS + ld [wDuelDisplayedScreen], a + call PrintDeckAndHandIconsAndNumberOfCards + ld a, [wNumCardsTryingToDraw] + or a + jr nz, .can_draw + ; if wNumCardsTryingToDraw set to 0 before, it's because not enough cards in deck + ldtx hl, CannotDrawCardBecauseNoCardsInDeckText + call DrawWideTextBox_WaitForInput + jr .done +.can_draw + ld l, a + ld h, 0 + call LoadTxRam3 + ldtx hl, DrawCardsFromTheDeckText + call DrawWideTextBox_PrintText + call EnableLCD +.anim_drawing_cards_loop + call PlayTurnDuelistDrawAnimation + ld hl, wNumCardsBeingDrawn + inc [hl] + call PrintNumberOfHandAndDeckCards + ld a, [wNumCardsBeingDrawn] + ld hl, wNumCardsTryingToDraw + cp [hl] + jr c, .anim_drawing_cards_loop + ld c, 30 +.wait_loop + call DoFrame + call CheckSkipDelayAllowed + jr c, .done + dec c + jr nz, .wait_loop +.done + pop bc + pop de + pop hl + ret + +; animates the screen for Turn Duelist drawing a card +PlayTurnDuelistDrawAnimation: + call Func_3b21 + ld e, DUEL_ANIM_PLAYER_DRAW + ldh a, [hWhoseTurn] + cp PLAYER_TURN + jr z, .got_duelist + ld e, DUEL_ANIM_OPP_DRAW +.got_duelist + ld a, e + call PlayDuelAnimation + +.loop_anim + call DoFrame + call CheckSkipDelayAllowed + jr c, .done_anim + call CheckAnyAnimationPlaying + jr c, .loop_anim + +.done_anim + call Func_3b31 + ret + +; prints, for each duelist, the number of cards in the hand along with the +; hand icon, and the number of cards in the deck, along with the deck icon, +; according to each element's placement in the draw card(s) screen. +PrintDeckAndHandIconsAndNumberOfCards: + call LoadDuelDrawCardsScreenTiles + ld hl, DeckAndHandIconsTileData + call WriteDataBlocksToBGMap0 + ld a, [wConsole] + cp CONSOLE_CGB + jr nz, .not_cgb + call BankswitchVRAM1 + ld hl, DeckAndHandIconsCGBPalData + call WriteDataBlocksToBGMap0 + call BankswitchVRAM0 +.not_cgb + call PrintPlayerNumberOfHandAndDeckCards + call PrintOpponentNumberOfHandAndDeckCards + ret + +; prints, for each duelist, the number of cards in the hand, and the number +; of cards in the deck, according to their placement in the draw card(s) screen. +; input: wNumCardsBeingDrawn = number of cards being drawn (in order to add +; them to the hand cards and subtract them from the deck cards). +PrintNumberOfHandAndDeckCards: + ldh a, [hWhoseTurn] + cp PLAYER_TURN + jr nz, PrintOpponentNumberOfHandAndDeckCards +; fallthrough + +PrintPlayerNumberOfHandAndDeckCards: + ld a, [wPlayerNumberOfCardsInHand] + ld hl, wNumCardsBeingDrawn + add [hl] + ld d, a + ld a, DECK_SIZE + ld hl, wPlayerNumberOfCardsNotInDeck + sub [hl] + ld hl, wNumCardsBeingDrawn + sub [hl] + ld e, a + ld a, d + lb bc, 16, 10 + call WriteTwoDigitNumberInTxSymbolFormat + ld a, e + lb bc, 10, 10 + jp WriteTwoDigitNumberInTxSymbolFormat + +PrintOpponentNumberOfHandAndDeckCards: + ld a, [wOpponentNumberOfCardsInHand] + ld hl, wNumCardsBeingDrawn + add [hl] + ld d, a + ld a, DECK_SIZE + ld hl, wOpponentNumberOfCardsNotInDeck + sub [hl] + ld hl, wNumCardsBeingDrawn + sub [hl] + ld e, a + ld a, d + lb bc, 5, 3 + call WriteTwoDigitNumberInTxSymbolFormat + ld a, e + lb bc, 11, 3 + jp WriteTwoDigitNumberInTxSymbolFormat + +DeckAndHandIconsTileData: +; x, y, tiles[], 0 + db 4, 3, SYM_CROSS, 0 ; x for opponent's hand + db 10, 3, SYM_CROSS, 0 ; x for opponent's deck + db 8, 2, $f4, $f5, 0 ; opponent's deck icon + db 8, 3, $f6, $f7, 0 ; opponent's deck icon + db 2, 2, $f8, $f9, 0 ; opponent's hand icon + db 2, 3, $fa, $fb, 0 ; opponent's hand icon + db 9, 10, SYM_CROSS, 0 ; x for player's deck + db 15, 10, SYM_CROSS, 0 ; x for player's hand + db 7, 9, $f4, $f5, 0 ; player's deck icon + db 7, 10, $f6, $f7, 0 ; player's deck icon + db 13, 9, $f8, $f9, 0 ; player's hand icon + db 13, 10, $fa, $fb, 0 ; player's hand icon + db $ff + +DeckAndHandIconsCGBPalData: +; x, y, pals[], 0 + db 8, 2, $02, $02, 0 + db 8, 3, $02, $02, 0 + db 2, 2, $02, $02, 0 + db 2, 3, $02, $02, 0 + db 7, 9, $02, $02, 0 + db 7, 10, $02, $02, 0 + db 13, 9, $02, $02, 0 + db 13, 10, $02, $02, 0 + db $ff + +; draw the portraits of the two duelists and print their names. +; also draw an horizontal line separating the two sides. +DrawDuelistPortraitsAndNames: + call LoadSymbolsFont + ; player's name + ld de, wDefaultText + push de + call CopyPlayerName + lb de, 0, 11 + call InitTextPrinting + pop hl + call ProcessText + ; player's portrait + lb bc, 0, 5 + call DrawPlayerPortrait + ; opponent's name (aligned to the right) + ld de, wDefaultText + push de + call CopyOpponentName + pop hl + call GetTextLengthInTiles + push hl + add SCREEN_WIDTH + ld d, a + ld e, 0 + call InitTextPrinting + pop hl + call ProcessText + ; opponent's portrait + ld a, [wOpponentPortrait] + lb bc, 13, 1 + call Func_3e2a + ; middle line + call DrawDuelHorizontalSeparator + ret + +; print the number of prizes left, of active Pokemon, and of cards left in the deck +; of both duelists. this is called when the duel ends. +PrintDuelResultStats: + lb de, 8, 8 + call PrintDuelistResultStats + call SwapTurn + lb de, 1, 1 + call PrintDuelistResultStats + call SwapTurn + ret + +; print, at d,e, the number of prizes left, of active Pokemon, and of cards left in +; the deck of the turn duelist. b,c are used throughout as input coords for +; WriteTwoDigitNumberInTxSymbolFormat, and d,e for InitTextPrinting_ProcessTextFromID. +PrintDuelistResultStats: + call SetNoLineSeparation + ldtx hl, PrizesLeftActivePokemonCardsInDeckText + call InitTextPrinting_ProcessTextFromID + call SetOneLineSeparation + ld c, e + ld a, d + add 7 + ld b, a + inc a + inc a + ld d, a + call CountPrizes + call .print_x_cards + inc e + inc c + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ldtx hl, YesText + or a + jr nz, .pkmn_in_play_area + ldtx hl, NoneText +.pkmn_in_play_area + dec d + call InitTextPrinting_ProcessTextFromID + inc e + inc d + inc c + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + ld a, DECK_SIZE + sub [hl] +.print_x_cards + call WriteTwoDigitNumberInTxSymbolFormat + ldtx hl, CardsText + call InitTextPrinting_ProcessTextFromID + ret + +; display the animation of the player drawing the card at hTempCardIndex_ff98 +DisplayPlayerDrawCardScreen: + ldtx hl, YouDrewText + ldh a, [hTempCardIndex_ff98] +; fallthrough + +; display card detail when a card is drawn or played +; hl is text to display +; a is the card's deck index +DisplayCardDetailScreen: + call LoadCardDataToBuffer1_FromDeckIndex + call _DisplayCardDetailScreen + ret + +Func_4b38: + ld a, [wDuelTempList] + cp $ff + ret z + call InitAndDrawCardListScreenLayout + call CountCardsInDuelTempList ; list length + ld hl, CardListParameters ; other list params + lb de, 0, 0 ; initial page scroll offset, initial item (in the visible page) + call PrintCardListItems + ldtx hl, TheCardYouReceivedText + lb de, 1, 1 + call InitTextPrinting + call PrintTextNoDelay + ldtx hl, YouReceivedTheseCardsText + call DrawWideTextBox_WaitForInput + ret + +; handles the initial duel actions: +; - drawing starting hand and placing the Basic Pokemon cards +; - placing the appropriate number of prize cards +; - tossing coin to determine first player to go +HandleDuelSetup: +; init variables and shuffle cards + call InitializeDuelVariables + call SwapTurn + call InitializeDuelVariables + call SwapTurn + call PlayShuffleAndDrawCardsAnimation_BothDuelists + call ShuffleDeckAndDrawSevenCards + ldh [hTemp_ffa0], a + call SwapTurn + call ShuffleDeckAndDrawSevenCards + call SwapTurn + ld c, a + +; check if any Basic Pokémon cards were drawn + ldh a, [hTemp_ffa0] + ld b, a + and c + jr nz, .hand_cards_ok + ld a, b + or c + jr z, .neither_drew_basic_pkmn + ld a, b + or a + jr nz, .opp_drew_no_basic_pkmn + +;.player_drew_no_basic_pkmn +.ensure_player_basic_pkmn_loop + call DisplayNoBasicPokemonInHandScreenAndText + call InitializeDuelVariables + call PlayShuffleAndDrawCardsAnimation_TurnDuelist + call ShuffleDeckAndDrawSevenCards + jr c, .ensure_player_basic_pkmn_loop + jr .hand_cards_ok + +.opp_drew_no_basic_pkmn + call SwapTurn +.ensure_opp_basic_pkmn_loop + call DisplayNoBasicPokemonInHandScreenAndText + call InitializeDuelVariables + call PlayShuffleAndDrawCardsAnimation_TurnDuelist + call ShuffleDeckAndDrawSevenCards + jr c, .ensure_opp_basic_pkmn_loop + call SwapTurn + jr .hand_cards_ok + +.neither_drew_basic_pkmn + ldtx hl, NeitherPlayerHasBasicPkmnText + call DrawWideTextBox_WaitForInput + call DisplayNoBasicPokemonInHandScreen + call InitializeDuelVariables + call SwapTurn + call DisplayNoBasicPokemonInHandScreen + call InitializeDuelVariables + call SwapTurn + call PrintReturnCardsToDeckDrawAgain + jp HandleDuelSetup + +.hand_cards_ok + ldh a, [hWhoseTurn] + push af + ld a, PLAYER_TURN + ldh [hWhoseTurn], a + call ChooseInitialArenaAndBenchPokemon + call SwapTurn + call ChooseInitialArenaAndBenchPokemon + call SwapTurn + jp c, .error + call DrawPlayAreaToPlacePrizeCards + ldtx hl, PlacingThePrizesText + call DrawWideTextBox_WaitForInput + call ExchangeRNG + + ld a, [wDuelInitialPrizes] + ld l, a + ld h, 0 + call LoadTxRam3 + ldtx hl, PleasePlacePrizesText + call DrawWideTextBox_PrintText + call EnableLCD + call .PlacePrizes + call WaitForWideTextBoxInput + pop af + + ldh [hWhoseTurn], a + call InitTurnDuelistPrizes + call SwapTurn + call InitTurnDuelistPrizes + call SwapTurn + call EmptyScreen + ld a, BOXMSG_COIN_TOSS + call DrawDuelBoxMessage + ldtx hl, CoinTossToDecideWhoPlaysFirstText + call DrawWideTextBox_WaitForInput + ldh a, [hWhoseTurn] + cp PLAYER_TURN + jr nz, .opponent_turn + +; player flips coin + ld de, wDefaultText + call CopyPlayerName + ld hl, $0000 + call LoadTxRam2 + ldtx hl, YouPlayFirstText + ldtx de, IfHeadsDuelistPlaysFirstText + call TossCoin + jr c, .play_first + call SwapTurn + ldtx hl, YouPlaySecondText +.play_first + call DrawWideTextBox_WaitForInput + call ExchangeRNG + or a + ret + +.opponent_turn +; opp flips coin + ld de, wDefaultText + call CopyOpponentName + ld hl, $0000 + call LoadTxRam2 + ldtx hl, YouPlaySecondText + ldtx de, IfHeadsDuelistPlaysFirstText + call TossCoin + jr c, .play_second + call SwapTurn + ldtx hl, YouPlayFirstText +.play_second + call DrawWideTextBox_WaitForInput + call ExchangeRNG + or a + ret + +.error + pop af + ldh [hWhoseTurn], a + scf + ret + +; places the prize cards on both sides +; of the Play Area (player & opp) +.PlacePrizes + ld hl, .PrizeCardCoordinates + ld e, DECK_SIZE - 7 - 1 ; deck size - cards drawn - 1 + ld a, [wDuelInitialPrizes] + ld d, a + +.place_prize + push de + ld b, 20 ; frames to delay +.loop_delay + call DoFrame + call CheckSkipDelayAllowed + jr c, .skip_delay + dec b + jr nz, .loop_delay +.skip_delay + call .DrawPrizeTile + call .DrawPrizeTile + + push hl + ld a, SFX_08 + call PlaySFX + ; print new deck card number + lb bc, 3, 5 + ld a, e + call WriteTwoDigitNumberInTxSymbolFormat + lb bc, 18, 7 + ld a, e + call WriteTwoDigitNumberInTxSymbolFormat + pop hl + pop de + dec e ; decrease number of cards in deck + dec d ; decrease number of prize cards left + jr nz, .place_prize + ret + +.DrawPrizeTile + ld b, [hl] + inc hl + ld c, [hl] + inc hl + ld a, $ac ; prize card tile + jp WriteByteToBGMap0 + +.PrizeCardCoordinates +; player x, player y, opp x, opp y + db 5, 6, 14, 5 ; Prize 1 + db 6, 6, 13, 5 ; Prize 2 + db 5, 7, 14, 4 ; Prize 3 + db 6, 7, 13, 4 ; Prize 4 + db 5, 8, 14, 3 ; Prize 5 + db 6, 8, 13, 3 ; Prize 6 + +; have the turn duelist place, at the beginning of the duel, the active Pokemon +; and 0 more bench Pokemon, all of which must be basic Pokemon cards. +; also transmits the turn holder's duelvars to the other duelist in a link duel. +; called twice, once for each duelist. +ChooseInitialArenaAndBenchPokemon: + ld a, DUELVARS_DUELIST_TYPE + call GetTurnDuelistVariable + cp DUELIST_TYPE_PLAYER + jr z, .choose_arena + cp DUELIST_TYPE_LINK_OPP + jr z, .exchange_duelvars + +; AI opponent's turn + push af + push hl + call AIDoAction_StartDuel + pop hl + pop af + ld [hl], a + or a + ret + +; link opponent's turn +.exchange_duelvars + ldtx hl, TransmittingDataText + call DrawWideTextBox_PrintText + call ExchangeRNG + ld hl, wPlayerDuelVariables + ld de, wOpponentDuelVariables + ld c, (wOpponentDuelVariables - wPlayerDuelVariables) / 2 + call SerialExchangeBytes + jr c, .error + ld c, (wOpponentDuelVariables - wPlayerDuelVariables) / 2 + call SerialExchangeBytes + jr c, .error + ld a, DUELVARS_DUELIST_TYPE + call GetTurnDuelistVariable + ld [hl], DUELIST_TYPE_LINK_OPP + or a + ret +.error + jp DuelTransmissionError + +; player's turn (either AI or link duel) +; prompt (force) the player to choose a basic Pokemon card to place in the arena +.choose_arena + call EmptyScreen + ld a, BOXMSG_ARENA_POKEMON + call DrawDuelBoxMessage + ldtx hl, ChooseBasicPkmnToPlaceInArenaText + call DrawWideTextBox_WaitForInput + ld a, PRACTICEDUEL_DRAW_SEVEN_CARDS + call DoPracticeDuelAction +.choose_arena_loop + xor a + ldtx hl, PleaseChooseAnActivePokemonText + call DisplayPlaceInitialPokemonCardsScreen + jr c, .choose_arena_loop + ldh a, [hTempCardIndex_ff98] + call LoadCardDataToBuffer1_FromDeckIndex + ld a, PRACTICEDUEL_PLAY_GOLDEEN + call DoPracticeDuelAction + jr c, .choose_arena_loop + ldh a, [hTempCardIndex_ff98] + call PutHandPokemonCardInPlayArea + ldh a, [hTempCardIndex_ff98] + ldtx hl, PlacedInTheArenaText + call DisplayCardDetailScreen + jr .choose_bench + +; after choosing the active Pokemon, let the player place 0 or more basic Pokemon +; cards in the bench. loop until the player decides to stop placing Pokemon cards. +.choose_bench + call EmptyScreen + ld a, BOXMSG_BENCH_POKEMON + call DrawDuelBoxMessage + ldtx hl, ChooseUpTo5BasicPkmnToPlaceOnBenchText + call PrintScrollableText_NoTextBoxLabel + ld a, PRACTICEDUEL_PUT_STARYU_IN_BENCH + call DoPracticeDuelAction +.bench_loop + ld a, TRUE + ldtx hl, ChooseYourBenchPokemonText + call DisplayPlaceInitialPokemonCardsScreen + jr c, .bench_done + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp MAX_PLAY_AREA_POKEMON + jr nc, .no_space + ldh a, [hTempCardIndex_ff98] + call PutHandPokemonCardInPlayArea + ldh a, [hTempCardIndex_ff98] + ldtx hl, PlacedOnTheBenchText + call DisplayCardDetailScreen + ld a, PRACTICEDUEL_DONE_PUTTING_ON_BENCH + call DoPracticeDuelAction + jr .bench_loop + +.no_space + ldtx hl, NoSpaceOnTheBenchText + call DrawWideTextBox_WaitForInput + jr .bench_loop + +.bench_done + ld a, PRACTICEDUEL_VERIFY_INITIAL_PLAY + call DoPracticeDuelAction + jr c, .bench_loop + or a + ret + +; the turn duelist shuffles the deck unless it's a practice duel, then draws 7 cards +; returns $00 in a and carry if no basic Pokemon cards are drawn, and $01 in a otherwise +ShuffleDeckAndDrawSevenCards: + call InitializeDuelVariables + ld a, [wDuelType] + cp DUELTYPE_PRACTICE + jr z, .deck_ready + call ShuffleDeck + call ShuffleDeck +.deck_ready + ld b, 7 +.draw_loop + call DrawCardFromDeck + call AddCardToHand + dec b + jr nz, .draw_loop + ld a, DUELVARS_HAND + call GetTurnDuelistVariable + ld b, $00 + ld c, 7 +.cards_loop + ld a, [hli] + push hl + push bc + call LoadCardDataToBuffer1_FromDeckIndex + call IsLoadedCard1BasicPokemon.skip_mysterious_fossil_clefairy_doll + pop bc + pop hl + or b + ld b, a + dec c + jr nz, .cards_loop + ld a, b + or a + ret nz + xor a + scf + ret + +; return nc if the card at wLoadedCard1 is a basic Pokemon card +; MYSTERIOUS_FOSSIL and CLEFAIRY_DOLL do count as basic Pokemon cards +IsLoadedCard1BasicPokemon: + ld a, [wLoadedCard1ID] + cp MYSTERIOUS_FOSSIL + jr z, .basic + cp CLEFAIRY_DOLL + jr z, .basic +; fallthrough + +; return nc if the card at wLoadedCard1 is a basic Pokemon card +; MYSTERIOUS_FOSSIL and CLEFAIRY_DOLL do NOT count unless already checked +.skip_mysterious_fossil_clefairy_doll + ld a, [wLoadedCard1Type] + cp TYPE_ENERGY + jr nc, .energy_trainer_nonbasic + ld a, [wLoadedCard1Stage] + or a + jr nz, .energy_trainer_nonbasic + +; basic + ld a, $01 + ret ; z + +.energy_trainer_nonbasic + xor a + scf + ret + +.basic ; MYSTERIOUS_FOSSIL or CLEFAIRY_DOLL + ld a, $01 + or a + ret ; nz + +DisplayNoBasicPokemonInHandScreenAndText: + ldtx hl, ThereAreNoBasicPokemonInHand + call DrawWideTextBox_WaitForInput + call DisplayNoBasicPokemonInHandScreen +; fallthrough + +; prints ReturnCardsToDeckAndDrawAgainText in a textbox and calls ExchangeRNG +PrintReturnCardsToDeckDrawAgain: + ldtx hl, ReturnCardsToDeckAndDrawAgainText + call DrawWideTextBox_WaitForInput + call ExchangeRNG + ret + +; display a bare list of seven hand cards of the turn duelist, and the duelist's name above +; used to let the player know that there are no basic Pokemon in the hand and need to redraw +DisplayNoBasicPokemonInHandScreen: + call EmptyScreen + call LoadDuelCardSymbolTiles + lb de, 0, 0 + lb bc, 20, 18 + call DrawRegularTextBox + call CreateHandCardList + call CountCardsInDuelTempList + ld hl, NoBasicPokemonCardListParameters + lb de, 0, 0 + call PrintCardListItems + ldtx hl, DuelistHandText + lb de, 1, 1 + call InitTextPrinting + call PrintTextNoDelay + call EnableLCD + call WaitForWideTextBoxInput + ret + +NoBasicPokemonCardListParameters: + db 1, 3 ; cursor x, cursor y + db 4 ; item x + db 14 ; maximum length, in tiles, occupied by the name and level string of each card in the list + db 7 ; number of items selectable without scrolling + db SYM_CURSOR_R ; cursor tile number + db SYM_SPACE ; tile behind cursor + dw NULL ; function pointer if non-0 + +; used only during the practice duel with Sam. +; displays the list with the player's cards in hand, and the player's name above the list. +DisplayPracticeDuelPlayerHandScreen: + call CreateHandCardList + call EmptyScreen + call LoadDuelCardSymbolTiles + lb de, 0, 0 + lb bc, 20, 13 + call DrawRegularTextBox + call CountCardsInDuelTempList ; list length + ld hl, CardListParameters ; other list params + lb de, 0, 0 ; initial page scroll offset, initial item (in the visible page) + call PrintCardListItems + ldtx hl, DuelistHandText + lb de, 1, 1 + call InitTextPrinting + call PrintTextNoDelay + call EnableLCD + ret + +PlayShuffleAndDrawCardsAnimation_TurnDuelist: + ld b, DUEL_ANIM_PLAYER_SHUFFLE + ld c, DUEL_ANIM_PLAYER_DRAW + ldh a, [hWhoseTurn] + cp PLAYER_TURN + jr z, .play_anim + ld b, DUEL_ANIM_OPP_SHUFFLE + ld c, DUEL_ANIM_OPP_DRAW +.play_anim + ldtx hl, ShufflesTheDeckText + ldtx de, Drew7CardsText + jr PlayShuffleAndDrawCardsAnimation + +PlayShuffleAndDrawCardsAnimation_BothDuelists: + ld b, DUEL_ANIM_BOTH_SHUFFLE + ld c, DUEL_ANIM_BOTH_DRAW + ldtx hl, EachPlayerShuffleOpponentsDeckText + ldtx de, EachPlayerDraw7CardsText + ld a, [wDuelType] + cp DUELTYPE_PRACTICE + jr nz, PlayShuffleAndDrawCardsAnimation + ldtx hl, ThisIsJustPracticeDoNotShuffleText +; fallthrough + +; animate the shuffle and drawing screen +; input: +; b = shuffling animation index +; c = drawing animation index +; hl = text to print while shuffling +; de = text to print while drawing +PlayShuffleAndDrawCardsAnimation: + push bc + push de + push hl + call ZeroObjectPositionsAndToggleOAMCopy + call EmptyScreen + call DrawDuelistPortraitsAndNames + call LoadDuelDrawCardsScreenTiles + ld a, SHUFFLE_DECK + ld [wDuelDisplayedScreen], a + pop hl + call DrawWideTextBox_PrintText + call EnableLCD + ld a, [wDuelType] + cp DUELTYPE_PRACTICE + jr nz, .not_practice + call WaitForWideTextBoxInput + jr .print_deck_info + +.not_practice +; get the shuffling animation from input value of b + call Func_3b21 + ld hl, sp+$03 + ; play animation 3 times + ld a, [hl] + call PlayDuelAnimation + ld a, [hl] + call PlayDuelAnimation + ld a, [hl] + call PlayDuelAnimation + +.loop_shuffle_anim + call DoFrame + call CheckSkipDelayAllowed + jr c, .done_shuffle + call CheckAnyAnimationPlaying + jr c, .loop_shuffle_anim +.done_shuffle + call Func_3b31 + +.print_deck_info + xor a + ld [wNumCardsBeingDrawn], a + call PrintDeckAndHandIconsAndNumberOfCards + call Func_3b21 + pop hl + call DrawWideTextBox_PrintText +.draw_card +; get the draw animation from input value of c + ld hl, sp+$00 + ld a, [hl] + call PlayDuelAnimation + +.loop_drawing_anim + call DoFrame + call CheckSkipDelayAllowed + jr c, .done + call CheckAnyAnimationPlaying + jr c, .loop_drawing_anim + + ld hl, wNumCardsBeingDrawn + inc [hl] + ld hl, sp+$00 + ld a, [hl] + cp DUEL_ANIM_BOTH_DRAW + jr nz, .one_duelist_shuffled + ; if both duelists shuffled + call PrintDeckAndHandIconsAndNumberOfCards.not_cgb + jr .check_num_cards +.one_duelist_shuffled + call PrintNumberOfHandAndDeckCards + +.check_num_cards + ld a, [wNumCardsBeingDrawn] + cp 7 + jr c, .draw_card + + ld c, 30 +.wait_loop + call DoFrame + call CheckSkipDelayAllowed + jr c, .done + dec c + jr nz, .wait_loop + +.done + call Func_3b31 + pop bc + ret + +Func_4f2d: + ld a, [wDuelDisplayedScreen] + cp SHUFFLE_DECK + jr z, .skip_draw_scene + call ZeroObjectPositionsAndToggleOAMCopy + call EmptyScreen + call DrawDuelistPortraitsAndNames +.skip_draw_scene + ld a, SHUFFLE_DECK + ld [wDuelDisplayedScreen], a + +; if duelist has only one card in deck, +; skip shuffling animation + ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK + call GetTurnDuelistVariable + ld a, DECK_SIZE + sub [hl] + cp 2 + jr c, .one_card_in_deck + + ldtx hl, ShufflesTheDeckText + call DrawWideTextBox_PrintText + call EnableLCD + call Func_3b21 + +; load correct animation depending on turn duelist + ld e, DUEL_ANIM_PLAYER_SHUFFLE + ldh a, [hWhoseTurn] + cp PLAYER_TURN + jr z, .load_anim + ld e, DUEL_ANIM_OPP_SHUFFLE +.load_anim +; play animation 3 times + ld a, e + call PlayDuelAnimation + ld a, e + call PlayDuelAnimation + ld a, e + call PlayDuelAnimation + +.loop_anim + call DoFrame + call CheckSkipDelayAllowed + jr c, .done_anim + call CheckAnyAnimationPlaying + jr c, .loop_anim + +.done_anim + call Func_3b31 + ld a, $01 + ret + +.one_card_in_deck +; no animation, just print text and delay + ld l, a + ld h, $00 + call LoadTxRam3 + ldtx hl, DeckHasXCardsText + call DrawWideTextBox_PrintText + call EnableLCD + ld a, 60 +.loop_wait + call DoFrame + dec a + jr nz, .loop_wait + ld a, $01 + ret + +; draw the main scene during a duel, except the contents of the bottom text box, +; which depend on the type of duelist holding the turn. +; includes the background, both arena Pokemon, and both HUDs. +DrawDuelMainScene: + ld a, DUELVARS_DUELIST_TYPE + call GetTurnDuelistVariable + cp DUELIST_TYPE_PLAYER + jr z, .draw + ldh a, [hWhoseTurn] + push af + ld a, PLAYER_TURN + ldh [hWhoseTurn], a + call .draw + pop af + ldh [hWhoseTurn], a + ret +.draw +; first, load the graphics and draw the background scene + ld a, [wDuelDisplayedScreen] + cp DUEL_MAIN_SCENE + ret z + call ZeroObjectPositionsAndToggleOAMCopy + call EmptyScreen + call LoadSymbolsFont + ld a, DUEL_MAIN_SCENE + ld [wDuelDisplayedScreen], a + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ld de, v0Tiles1 + $50 tiles + call LoadPlayAreaCardGfx + call SetBGP7OrSGB2ToCardPalette + call SwapTurn + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ld de, v0Tiles1 + $20 tiles + call LoadPlayAreaCardGfx + call SetBGP6OrSGB3ToCardPalette + call FlushAllPalettesOrSendPal23Packet + call SwapTurn +; next, draw the Pokemon in the arena +;.place_player_arena_pkmn + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + cp -1 + jr z, .place_opponent_arena_pkmn + ld a, $d0 ; v0Tiles1 + $50 tiles + lb hl, 6, 1 + lb de, 0, 5 + lb bc, 8, 6 + call FillRectangle + call ApplyBGP7OrSGB2ToCardImage +.place_opponent_arena_pkmn + call SwapTurn + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + cp -1 + jr z, .place_other_elements + ld a, $a0 ; v0Tiles1 + $20 tiles + lb hl, 6, 1 + lb de, 12, 1 + lb bc, 8, 6 + call FillRectangle + call ApplyBGP6OrSGB3ToCardImage +.place_other_elements + call SwapTurn + ld hl, DuelEAndHPTileData + call WriteDataBlocksToBGMap0 + call DrawDuelHorizontalSeparator + call DrawDuelHUDs + call DrawWideTextBox + call EnableLCD + ret + +; draws the main elements of the main duel interface, including HUDs, HPs, card names +; and color symbols, attached cards, and other information, of both duelists. +DrawDuelHUDs: + ld a, DUELVARS_DUELIST_TYPE + call GetTurnDuelistVariable + cp DUELIST_TYPE_PLAYER + jr z, .draw_hud + ldh a, [hWhoseTurn] + push af + ld a, PLAYER_TURN + ldh [hWhoseTurn], a + call .draw_hud + pop af + ldh [hWhoseTurn], a + ret +.draw_hud + lb de, 1, 11 ; coordinates for player's arena card name and info icons + lb bc, 11, 8 ; coordinates for player's attached energies and HP bar + call DrawDuelHUD + lb bc, 8, 5 + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + call CheckPrintCnfSlpPrz + inc c + call CheckPrintPoisoned + inc c + call CheckPrintDoublePoisoned ; if double poisoned, print a second poison icon + call SwapTurn + lb de, 7, 0 ; coordinates for opponent's arena card name and info icons + lb bc, 3, 1 ; coordinates for opponent's attached energies and HP bar + call GetNonTurnDuelistVariable + call DrawDuelHUD + lb bc, 11, 6 + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + call CheckPrintCnfSlpPrz + dec c + call CheckPrintPoisoned + dec c + call CheckPrintDoublePoisoned ; if double poisoned, print a second poison icon + call SwapTurn + ret + +DrawDuelHUD: + ld hl, wHUDEnergyAndHPBarsX + ld [hl], b + inc hl + ld [hl], c ; wHUDEnergyAndHPBarsY + push de ; push coordinates for the arena card name + ld d, 1 ; opponent's info icons start in the second tile to the right + ld a, e + or a + jr z, .go + ld d, 15 ; player's info icons start in the 15th tile to the right +.go + push de + pop bc + + ; print the Pokemon icon along with the no. of play area Pokemon + ld a, SYM_POKEMON + call WriteByteToBGMap0 + inc b + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + add SYM_0 - 1 + call WriteByteToBGMap0 + inc b + + ; print the Prize icon along with the no. of prizes yet to draw + ld a, SYM_PRIZE + call WriteByteToBGMap0 + inc b + call CountPrizes + add SYM_0 + call WriteByteToBGMap0 + + ; print the arena Pokemon card name and level text + pop de + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + cp -1 + ret z + call LoadCardDataToBuffer1_FromDeckIndex + push de + ld a, 32 + call CopyCardNameAndLevel + ld [hl], TX_END + + ; print the arena Pokemon card color symbol just before the name + pop de + ld a, e + or a + jr nz, .print_color_icon + ld hl, wDefaultText + call GetTextLengthInTiles + add SCREEN_WIDTH + ld d, a +.print_color_icon + call InitTextPrinting + ld hl, wDefaultText + call ProcessText + push de + pop bc + call GetArenaCardColor + inc a ; TX_SYMBOL color tiles start at 1 + dec b ; place the color symbol one tile to the left of the start of the card's name + call JPWriteByteToBGMap0 + + ; print attached energies + ld hl, wHUDEnergyAndHPBarsX + ld b, [hl] + inc hl + ld c, [hl] ; wHUDEnergyAndHPBarsY + lb de, 9, PLAY_AREA_ARENA + call PrintPlayAreaCardAttachedEnergies + + ; print HP bar + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer1_FromDeckIndex + ld a, [wLoadedCard1HP] + ld d, a ; max HP + ld a, DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + ld e, a ; cur HP + call DrawHPBar + ld hl, wHUDEnergyAndHPBarsX + ld b, [hl] + inc hl + ld c, [hl] ; wHUDEnergyAndHPBarsY + inc c ; [wHUDEnergyAndHPBarsY] + 1 + call BCCoordToBGMap0Address + push de + ld hl, wDefaultText + ld b, HP_BAR_LENGTH / 2 ; first row of the HP bar + call SafeCopyDataHLtoDE + pop de + ld hl, BG_MAP_WIDTH + add hl, de + ld e, l + ld d, h + ld hl, wDefaultText + HP_BAR_LENGTH / 2 + ld b, HP_BAR_LENGTH / 2 ; second row of the HP bar + call SafeCopyDataHLtoDE + + ; print number of attached Pluspower and Defender with respective icon, if any + ld hl, wHUDEnergyAndHPBarsX + ld a, [hli] + add 6 + ld b, a + ld c, [hl] ; wHUDEnergyAndHPBarsY + inc c + ld a, DUELVARS_ARENA_CARD_ATTACHED_PLUSPOWER + call GetTurnDuelistVariable + or a + jr z, .check_defender + ld a, SYM_PLUSPOWER + call WriteByteToBGMap0 + inc b + ld a, [hl] ; number of attached Pluspower + add SYM_0 + call WriteByteToBGMap0 + dec b +.check_defender + ld a, DUELVARS_ARENA_CARD_ATTACHED_DEFENDER + call GetTurnDuelistVariable + or a + jr z, .done + inc c + ld a, SYM_DEFENDER + call WriteByteToBGMap0 + inc b + ld a, [hl] ; number of attached Defender + add SYM_0 + call WriteByteToBGMap0 +.done + ret + +; draws an horizontal line that separates the arena side of each duelist +; also colorizes the line on CGB +DrawDuelHorizontalSeparator: + ld hl, DuelHorizontalSeparatorTileData + call WriteDataBlocksToBGMap0 + ld a, [wConsole] + cp CONSOLE_CGB + ret nz + call BankswitchVRAM1 + ld hl, DuelHorizontalSeparatorCGBPalData + call WriteDataBlocksToBGMap0 + call BankswitchVRAM0 + ret + +DuelEAndHPTileData: +; x, y, tiles[], 0 + db 1, 1, SYM_E, 0 + db 1, 2, SYM_HP, 0 + db 9, 8, SYM_E, 0 + db 9, 9, SYM_HP, 0 + db $ff + +DuelHorizontalSeparatorTileData: +; x, y, tiles[], 0 + db 0, 4, $37, $37, $37, $37, $37, $37, $37, $37, $37, $31, $32, 0 + db 9, 5, $33, $34, 0 + db 9, 6, $33, $34, 0 + db 9, 7, $35, $36, $37, $37, $37, $37, $37, $37, $37, $37, $37, 0 + db $ff + +DuelHorizontalSeparatorCGBPalData: +; x, y, pals[], 0 + db 0, 4, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, 0 + db 9, 5, $02, $02, 0 + db 9, 6, $02, $02, 0 + db 9, 7, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, 0 + db $ff + +; if this is a practice duel, execute the practice duel action at wPracticeDuelAction +; if not a practice duel, always return nc +; the practice duel functions below return carry when something's wrong +DoPracticeDuelAction: + ld [wPracticeDuelAction], a + ld a, [wIsPracticeDuel] + or a + ret z + ld a, [wPracticeDuelAction] + ld hl, PracticeDuelActionTable + jp JumpToFunctionInTable + +PracticeDuelActionTable: + dw NULL + dw PracticeDuel_DrawSevenCards + dw PracticeDuel_PlayGoldeen + dw PracticeDuel_PutStaryuInBench + dw PracticeDuel_VerifyInitialPlay + dw PracticeDuel_DonePuttingOnBench + dw PracticeDuel_PrintTurnInstructions + dw PracticeDuel_VerifyPlayerTurnActions + dw PracticeDuel_RepeatInstructions + dw PracticeDuel_PlayStaryuFromBench + dw PracticeDuel_ReplaceKnockedOutPokemon + +PracticeDuel_DrawSevenCards: + call DisplayPracticeDuelPlayerHandScreen + call EnableLCD + ldtx hl, DrawSevenCardsPracticeDuelText + jp PrintPracticeDuelDrMasonInstructions + +PracticeDuel_PlayGoldeen: + ld a, [wLoadedCard1ID] + cp GOLDEEN + ret z + ldtx hl, ChooseGoldeenPracticeDuelText + ldtx de, DrMasonText + scf + jp PrintPracticeDuelDrMasonInstructions + +PracticeDuel_PutStaryuInBench: + call DisplayPracticeDuelPlayerHandScreen + call EnableLCD + ldtx hl, PutPokemonOnBenchPracticeDuelText + jp PrintPracticeDuelDrMasonInstructions + +PracticeDuel_VerifyInitialPlay: + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp 2 + ret z + ldtx hl, ChooseStaryuPracticeDuelText + scf + jp PrintPracticeDuelDrMasonInstructions + +PracticeDuel_DonePuttingOnBench: + call DisplayPracticeDuelPlayerHandScreen + call EnableLCD + ld a, $ff + ld [wPracticeDuelTurn], a + ldtx hl, PressBToFinishPracticeDuelText + jp PrintPracticeDuelDrMasonInstructions + +PracticeDuel_PrintTurnInstructions: + call DrawPracticeDuelInstructionsTextBox + call EnableLCD + ld a, [wDuelTurns] + ld hl, wPracticeDuelTurn + cp [hl] + ld [hl], a + ; calling PrintPracticeDuelInstructionsForCurrentTurn with a = 0 means that Dr. Mason's + ; instructions are also printed along with each of the point-by-point instructions + ld a, 0 + jp nz, PrintPracticeDuelInstructionsForCurrentTurn + ; if we're here, the player followed the current turn actions wrong and has to + ; repeat them. ask the player whether to show detailed instructions again, in + ; order to call PrintPracticeDuelInstructionsForCurrentTurn with a = 0 or a = 1. + ldtx de, DrMasonText + ldtx hl, NeedPracticeAgainPracticeDuelText + call PrintScrollableText_WithTextBoxLabel_NoWait + call YesOrNoMenu + jp PrintPracticeDuelInstructionsForCurrentTurn + +PracticeDuel_VerifyPlayerTurnActions: + ld a, [wDuelTurns] + srl a + ld hl, PracticeDuelTurnVerificationPointerTable + call JumpToFunctionInTable + ; return nc if player followed instructions correctly + ret nc +; fallthrough + +PracticeDuel_RepeatInstructions: + ldtx hl, FollowMyGuidancePracticeDuelText + call PrintPracticeDuelDrMasonInstructions + ; restart the turn from the saved data of the previous turn + ld a, $02 + call BankswitchSRAM + ld de, sCurrentDuel + call LoadSavedDuelData + xor a + call BankswitchSRAM + ; return carry in order to repeat instructions + scf + ret + +PracticeDuel_PlayStaryuFromBench: + ld a, [wDuelTurns] + cp 7 + jr z, .its_sam_turn_4 + or a + ret +.its_sam_turn_4 + ; ask player to choose Staryu from bench to replace knocked out Seaking + call DrawPracticeDuelInstructionsTextBox + call EnableLCD + ld hl, PracticeDuelText_SamTurn4 + jp PrintPracticeDuelInstructions + +PracticeDuel_ReplaceKnockedOutPokemon: + ldh a, [hTempPlayAreaLocation_ff9d] + cp PLAY_AREA_BENCH_1 + ret z + ; if player selected Drowzee instead (which is at PLAY_AREA_BENCH_2) + call HasAlivePokemonInBench + ldtx hl, SelectStaryuPracticeDuelText + scf +; fallthrough + +; print a text box with given the text id at hl, labeled as 'Dr. Mason' +PrintPracticeDuelDrMasonInstructions: + push af + ldtx de, DrMasonText + call PrintScrollableText_WithTextBoxLabel + pop af + ret + +INCLUDE "data/duel/practice_text.asm" + +; in a practice duel, draws the text box where the point-by-point +; instructions for the next player action will be written into +DrawPracticeDuelInstructionsTextBox: + call EmptyScreen + lb de, 0, 0 + lb bc, 20, 12 + call DrawRegularTextBox +; fallthrough + +; print "<Player>'s Turn [wDuelTurns]" (usually) as the textbox label +PrintPracticeDuelInstructionsTextBoxLabel: + ld a, [wDuelTurns] + cp 7 + jr z, .replace_due_to_knockout + ; load the player's turn number to TX_RAM3 in order to print it + srl a + inc a + ld l, a + ld h, $00 + call LoadTxRam3 + lb de, 1, 0 + call InitTextPrinting + ldtx hl, PlayersTurnPracticeDuelText + jp PrintText +.replace_due_to_knockout + ; when the player needs to replace a knocked out Pokemon, the label text is different + ; this happens at the end of Sam's fourth turn + lb de, 1, 0 + ldtx hl, ReplaceDueToKnockoutPracticeDuelText + jp InitTextPrinting_ProcessTextFromID + +; print the instructions of the current practice duel turn, taken from +; one of the structs in PracticeDuelTextPointerTable. +; if a != 0, only the point-by-point instructions are printed, otherwise +; Dr. Mason instructions are also shown in a textbox at the bottom of the screen. +PrintPracticeDuelInstructionsForCurrentTurn: + push af + ld a, [wDuelTurns] + and %11111110 + ld e, a + ld d, $00 + ld hl, PracticeDuelTextPointerTable + add hl, de + ld a, [hli] + ld h, [hl] + ld l, a + pop af + or a + jr nz, PrintPracticeDuelInstructions_Fast +; fallthrough + +; print practice duel instructions given hl = PracticeDuelText_* +; each practicetext entry (see above) contains a Dr. Mason text along with +; a numbered instruction text, that is later printed without text delay. +PrintPracticeDuelInstructions: + xor a + ld [wPracticeDuelTextY], a + ld a, l + ld [wPracticeDuelTextPointer], a + ld a, h + ld [wPracticeDuelTextPointer + 1], a +.print_instructions_loop + call PrintNextPracticeDuelInstruction + ld a, [hli] + ld [wPracticeDuelTextY], a + or a + jr z, PrintPracticeDuelLetsPlayTheGame + ld e, [hl] + inc hl + ld d, [hl] + inc hl + push hl + ld l, e + ld h, d + ldtx de, DrMasonText + call PrintScrollableText_WithTextBoxLabel + pop hl + ld e, [hl] + inc hl + ld d, [hl] + inc hl + push hl + call SetNoLineSeparation + ld l, e + ld h, d + ld a, [wPracticeDuelTextY] + ld e, a + ld d, 1 + call InitTextPrinting_ProcessTextFromID + call SetOneLineSeparation + pop hl + jr .print_instructions_loop + +; print the generic Dr. Mason's text that completes all his practice duel instructions +PrintPracticeDuelLetsPlayTheGame: + ldtx hl, LetsPlayTheGamePracticeDuelText + call PrintPracticeDuelDrMasonInstructions + ret + +; simplified version of PrintPracticeDuelInstructions that skips Dr. Mason's text +; and instead places the point-by-point instructions all at once. +PrintPracticeDuelInstructions_Fast: + ld a, [hli] + or a + jr z, PrintPracticeDuelLetsPlayTheGame + ld e, a ; y + ld d, 1 ; x + call PrintPracticeDuelNumberedInstruction + jr PrintPracticeDuelInstructions_Fast + +; print a practice duel point-by-point instruction at d,e, with text id at hl, +; that has been read from an entry of PracticeDuelText_* +PrintPracticeDuelNumberedInstruction: + inc hl + inc hl + ld c, [hl] + inc hl + ld b, [hl] + inc hl + push hl + ld l, c + ld h, b + call SetNoLineSeparation + call InitTextPrinting_ProcessTextFromID + call SetOneLineSeparation + pop hl + ret + +; print a single instruction bullet for the current turn +PrintNextPracticeDuelInstruction: + ld a, $01 + ldh [hffb0], a + push hl + call PrintPracticeDuelInstructionsTextBoxLabel + ld hl, wPracticeDuelTextPointer + ld a, [hli] + ld h, [hl] + ld l, a +.next + ld a, [wPracticeDuelTextY] + cp [hl] + jr c, .done + ld a, [hli] + or a + jr z, .done + ld e, a ; y + ld d, 1 ; x + call PrintPracticeDuelNumberedInstruction + jr .next +.done + pop hl + xor a + ldh [hffb0], a + ret + +PracticeDuelTurnVerificationPointerTable: + dw PracticeDuelVerify_Turn1 + dw PracticeDuelVerify_Turn2 + dw PracticeDuelVerify_Turn3 + dw PracticeDuelVerify_Turn4 + dw PracticeDuelVerify_Turn5 + dw PracticeDuelVerify_Turn6 + dw PracticeDuelVerify_Turn7Or8 + dw PracticeDuelVerify_Turn7Or8 + +PracticeDuelVerify_Turn1: + ld a, [wTempCardID_ccc2] + cp GOLDEEN + jp nz, ReturnWrongAction + ret + +PracticeDuelVerify_Turn2: + ld a, [wTempCardID_ccc2] + cp SEAKING + jp nz, ReturnWrongAction + ld a, [wSelectedAttack] + cp 1 + jp nz, ReturnWrongAction + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + ld a, [wAttachedEnergies + PSYCHIC] + or a + jr z, ReturnWrongAction + ret + +PracticeDuelVerify_Turn3: + ld a, [wTempCardID_ccc2] + cp SEAKING + jr nz, ReturnWrongAction + ld e, PLAY_AREA_BENCH_1 + call GetPlayAreaCardAttachedEnergies + ld a, [wAttachedEnergies + WATER] + or a + jr z, ReturnWrongAction + ret + +PracticeDuelVerify_Turn4: + ld a, [wPlayerNumberOfPokemonInPlayArea] + cp 3 + jr nz, ReturnWrongAction + ld e, PLAY_AREA_BENCH_2 + call GetPlayAreaCardAttachedEnergies + ld a, [wAttachedEnergies + WATER] + or a + jr z, ReturnWrongAction + ld a, [wTempCardID_ccc2] + cp SEAKING + jr nz, ReturnWrongAction + ld a, [wSelectedAttack] + cp 1 + jr nz, ReturnWrongAction + ret + +PracticeDuelVerify_Turn5: + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + ld a, [wAttachedEnergies + WATER] + cp 2 + jr nz, ReturnWrongAction + ld a, [wTempCardID_ccc2] + cp STARYU + jr nz, ReturnWrongAction + ret + +PracticeDuelVerify_Turn6: + ld e, PLAY_AREA_ARENA + call GetPlayAreaCardAttachedEnergies + ld a, [wAttachedEnergies + WATER] + cp 3 + jr nz, ReturnWrongAction + ld a, [wPlayerArenaCardHP] + cp 40 + jr nz, ReturnWrongAction + ld a, [wTempCardID_ccc2] + cp STARYU + jr nz, ReturnWrongAction + ret + +PracticeDuelVerify_Turn7Or8: + ld a, [wTempCardID_ccc2] + cp STARMIE + jr nz, ReturnWrongAction + ld a, [wSelectedAttack] + cp 1 + jr nz, ReturnWrongAction + ret + +ReturnWrongAction: + scf + ret + +; display BOXMSG_PLAYERS_TURN or BOXMSG_OPPONENTS_TURN and print +; DuelistTurnText in a textbox. also call ExchangeRNG. +DisplayDuelistTurnScreen: + call EmptyScreen + ld c, BOXMSG_PLAYERS_TURN + ldh a, [hWhoseTurn] + cp PLAYER_TURN + jr z, .got_turn + inc c ; BOXMSG_OPPONENTS_TURN +.got_turn + ld a, c + call DrawDuelBoxMessage + ldtx hl, DuelistTurnText + call DrawWideTextBox_WaitForInput + call ExchangeRNG + ret + +Unknown_54e2: ; unreferenced + db $00, $0c, $06, $0f, $00, $00, $00 + +DuelMenuData: + ; x, y, text id + textitem 3, 14, HandText + textitem 9, 14, CheckText + textitem 15, 14, RetreatText + textitem 3, 16, AttackText + textitem 9, 16, PKMNPowerText + textitem 15, 16, DoneText + db $ff + +; display the screen that prompts the player to choose a Pokemon card to +; place in the arena or in the bench at the beginning of the duel. +; input: + ; a = 0 -> prompted to place Pokemon card in arena + ; a = 1 -> prompted to place Pokemon card in bench +; return carry if no card was placed (only allowed for bench) +DisplayPlaceInitialPokemonCardsScreen: + ld [wPlacingInitialBenchPokemon], a + push hl + call CreateHandCardList + call InitAndDrawCardListScreenLayout + pop hl + call SetCardListInfoBoxText + ld a, PLAY_CHECK + ld [wCardListItemSelectionMenuType], a +.display_card_list + call DisplayCardList + jr nc, .card_selected + ; attempted to exit screen + ld a, [wPlacingInitialBenchPokemon] + or a + ; player is forced to place a Pokemon card in the arena + jr z, .display_card_list + ; in the bench, however, we can get away without placing anything + ; alternatively, the player doesn't want or can't place more bench Pokemon + scf + jr .done +.card_selected + ldh a, [hTempCardIndex_ff98] + call LoadCardDataToBuffer1_FromDeckIndex + call IsLoadedCard1BasicPokemon + jr nc, .done + ; invalid card selected, tell the player and go back + ldtx hl, YouCannotSelectThisCardText + call DrawWideTextBox_WaitForInput + call DrawCardListScreenLayout + jr .display_card_list +.done + ; valid basic Pokemon card selected, or no card selected (bench only) + push af + ld a, [wSortCardListByID] + or a + call nz, SortHandCardsByID + pop af + ret + +Func_5542: + call CreateDiscardPileCardList + ret c + call InitAndDrawCardListScreenLayout + call SetDiscardPileScreenTexts + call DisplayCardList + ret + +; draw the turn holder's discard pile screen +OpenDiscardPileScreen: + call CreateDiscardPileCardList + jr c, .discard_pile_empty + call InitAndDrawCardListScreenLayout + call SetDiscardPileScreenTexts + ld a, START + A_BUTTON + ld [wNoItemSelectionMenuKeys], a + call DisplayCardList + or a + ret +.discard_pile_empty + ldtx hl, TheDiscardPileHasNoCardsText + call DrawWideTextBox_WaitForInput + scf + ret + +; set wCardListHeaderText and SetCardListInfoBoxText to the text +; that correspond to the Discard Pile screen +SetDiscardPileScreenTexts: + ldtx de, YourDiscardPileText + ldh a, [hWhoseTurn] + cp PLAYER_TURN + jr z, .got_header_text + ldtx de, OpponentsDiscardPileText +.got_header_text + ldtx hl, ChooseTheCardYouWishToExamineText + call SetCardListHeaderText + ret + +SetCardListHeaderText: + ld a, e + ld [wCardListHeaderText], a + ld a, d + ld [wCardListHeaderText + 1], a +; fallthrough + +SetCardListInfoBoxText: + ld a, l + ld [wCardListInfoBoxText], a + ld a, h + ld [wCardListInfoBoxText + 1], a + ret + +Func_5591: + call InitAndDrawCardListScreenLayout + ld a, SELECT_CHECK + ld [wCardListItemSelectionMenuType], a + ret + +; draw the layout of the screen that displays the player's Hand card list or a +; Discard Pile card list, including a bottom-right image of the current card. +; since this loads the text for the Hand card list screen, SetDiscardPileScreenTexts +; is called after this if the screen corresponds to a Discard Pile list. +; the dimensions of text box where the card list is printed are 20x13, in order to accommodate +; another text box below it (wCardListInfoBoxText) as well as the image of the selected card. +InitAndDrawCardListScreenLayout: + xor a + ld hl, wSelectedDuelSubMenuItem + ld [hli], a + ld [hl], a + ld [wSortCardListByID], a + ld hl, wPrintSortNumberInCardListPtr + ld [hli], a + ld [hl], a + ld [wCardListItemSelectionMenuType], a + ld a, START + ld [wNoItemSelectionMenuKeys], a + ld hl, wCardListInfoBoxText + ldtx [hl], PleaseSelectHandText, & $ff + inc hl + ldtx [hl], PleaseSelectHandText, >> 8 + inc hl ; wCardListHeaderText + ldtx [hl], DuelistHandText, & $ff + inc hl + ldtx [hl], DuelistHandText, >> 8 +; fallthrough + +; same as InitAndDrawCardListScreenLayout, except that variables like wSelectedDuelSubMenuItem, +; wNoItemSelectionMenuKeys, wCardListInfoBoxText, wCardListHeaderText, etc already set by caller. +DrawCardListScreenLayout: + call ZeroObjectPositionsAndToggleOAMCopy + call EmptyScreen + call LoadSymbolsFont + call LoadDuelCardSymbolTiles + ; draw the surrounding box + lb de, 0, 0 + lb bc, 20, 13 + call DrawRegularTextBox + ; draw the image of the selected card + ld a, $a0 + lb hl, 6, 1 + lb de, 12, 12 + lb bc, 8, 6 + call FillRectangle + call ApplyBGP6OrSGB3ToCardImage + call Func_5744 + ld a, [wDuelTempList] + cp $ff + scf + ret z + or a + ret + +; displays a list of cards and handles input in order to navigate through the list, +; select a card, open a card page, etc. +; input: + ; - text IDs at wCardListInfoBoxText and wCardListHeaderText + ; - $ff-terminated list of cards to display at wDuelTempList + ; - wSelectedDuelSubMenuItem (initial item) and wSelectedDuelSubMenuScrollOffset + ; (initial page scroll offset). Usually both 0 to begin with the first card. +; returns carry if B is pressed to exit the card list screen. +; otherwise returns the selected card at hTempCardIndex_ff98 and at a. +DisplayCardList: + call DrawNarrowTextBox + call PrintCardListHeaderAndInfoBoxTexts +.reload_list + ; get the list length + call CountCardsInDuelTempList + ; get the position and scroll within the list + ld hl, wSelectedDuelSubMenuItem + ld e, [hl] ; initial item (in the visible page) + inc hl + ld d, [hl] ; initial page scroll offset + ld hl, CardListParameters ; other list params + call PrintCardListItems + call LoadSelectedCardGfx + call EnableLCD +.wait_button + call DoFrame + call Func_5690 + call HandleCardListInput + jr nc, .wait_button + ; refresh the position of the last checked card of the list, so that + ; the cursor points to said card when the list is reloaded + ld hl, wSelectedDuelSubMenuItem + ld [hl], e + inc hl + ld [hl], d + ldh a, [hKeysPressed] + ld b, a + bit SELECT_F, b + jr nz, .select_pressed + bit B_BUTTON_F, b + jr nz, .b_pressed + ld a, [wNoItemSelectionMenuKeys] + and b + jr nz, .open_card_page + ; display the item selection menu (PLAY|CHECK or SELECT|CHECK) for the selected card + ; open the card page if CHECK is selected + ldh a, [hCurMenuItem] + call GetCardInDuelTempList_OnlyDeckIndex + call CardListItemSelectionMenu + ; jump back if B pressed to exit the item selection menu + jr c, DisplayCardList + ldh a, [hTempCardIndex_ff98] + or a + ret +.select_pressed + ; sort cards by ID if SELECT is pressed and return to the first item + ld a, [wSortCardListByID] + or a + jr nz, .wait_button + call SortCardsInDuelTempListByID + xor a + ld hl, wSelectedDuelSubMenuItem + ld [hli], a + ld [hl], a + ld a, 1 + ld [wSortCardListByID], a + call EraseCursor + jr .reload_list +.open_card_page + ; open the card page directly, without an item selection menu + ; in this mode, D_UP and D_DOWN can be used to open the card page + ; of the card above and below the current card + ldh a, [hCurMenuItem] + call GetCardInDuelTempList + call LoadCardDataToBuffer1_FromDeckIndex + call OpenCardPage_FromCheckHandOrDiscardPile + ldh a, [hDPadHeld] + bit D_UP_F, a + jr nz, .up_pressed + bit D_DOWN_F, a + jr nz, .down_pressed + ; if B pressed, exit card page and reload the card list + call DrawCardListScreenLayout + jp DisplayCardList +.up_pressed + ldh a, [hCurMenuItem] + or a + jr z, .open_card_page ; if can't go up, reload card page of current card + dec a + jr .move_to_another_card +.down_pressed + call CountCardsInDuelTempList + ld b, a + ldh a, [hCurMenuItem] + inc a + cp b + jr nc, .open_card_page ; if can't go down, reload card page of current card +.move_to_another_card + ; update hCurMenuItem, and wSelectedDuelSubMenuScrollOffset. + ; this means that when navigating up/down through card pages, the page is + ; scrolled to reflect the movement, rather than the cursor going up/down. + ldh [hCurMenuItem], a + ld hl, wSelectedDuelSubMenuItem + ld [hl], $00 + inc hl + ld [hl], a + jr .open_card_page +.b_pressed + ldh a, [hCurMenuItem] + scf + ret + +Func_5690: + ldh a, [hDPadHeld] + and D_PAD + ret z + ld a, $01 + ldh [hffb0], a + call PrintCardListHeaderAndInfoBoxTexts + xor a + ldh [hffb0], a + ret + +; prints the text ID at wCardListHeaderText at 1,1 +; and the text ID at wCardListInfoBoxText at 1,14 +PrintCardListHeaderAndInfoBoxTexts: + lb de, 1, 14 + call AdjustCoordinatesForBGScroll + call InitTextPrinting + ld hl, wCardListInfoBoxText + ld a, [hli] + ld h, [hl] + ld l, a + call PrintTextNoDelay + ld hl, wCardListHeaderText + ld a, [hli] + ld h, [hl] + ld l, a + lb de, 1, 1 + call InitTextPrinting + call PrintTextNoDelay + ret + +; display the SELECT|CHECK or PLAY|CHECK menu when a card of a list is selected +; and handle input. return carry if b is pressed. +; input: wCardListItemSelectionMenuType +CardListItemSelectionMenu: + ld a, [wCardListItemSelectionMenuType] + or a + ret z + ldtx hl, SelectCheckText + ld a, [wCardListItemSelectionMenuType] + cp PLAY_CHECK + jr nz, .got_text + ldh a, [hTempCardIndex_ff98] + call LoadCardDataToBuffer1_FromDeckIndex + ldtx hl, PlayCheck2Text ; identical to PlayCheck1Text + ld a, [wLoadedCard1Type] + cp TYPE_TRAINER + jr nz, .got_text + ldtx hl, PlayCheck1Text +.got_text + call DrawNarrowTextBox_PrintTextNoDelay + ld hl, ItemSelectionMenuParameters + xor a + call InitializeMenuParameters +.wait_a_or_b + call DoFrame + call HandleMenuInput + jr nc, .wait_a_or_b + cp -1 + jr z, .b_pressed + ; A pressed + or a + ret z + ; CHECK option selected: open the card page + ldh a, [hTempCardIndex_ff98] + call LoadCardDataToBuffer1_FromDeckIndex + call OpenCardPage_FromHand + call DrawCardListScreenLayout +.b_pressed + scf + ret + +ItemSelectionMenuParameters: + db 1, 14 ; cursor x, cursor y + db 2 ; y displacement between items + db 2 ; number of items + db SYM_CURSOR_R ; cursor tile number + db SYM_SPACE ; tile behind cursor + dw NULL ; function pointer if non-0 + +CardListParameters: + db 1, 3 ; cursor x, cursor y + db 4 ; item x + db 14 ; maximum length, in tiles, occupied by the name and level string of each card in the list + db 5 ; number of items selectable without scrolling + db SYM_CURSOR_R ; cursor tile number + db SYM_SPACE ; tile behind cursor + dw CardListFunction ; function pointer if non-0 + +; return carry if any of the buttons is pressed, and load the graphics +; of the card pointed to by the cursor whenever a d-pad key is released. +; also return $ff unto hCurMenuItem if B is pressed. +CardListFunction: + ldh a, [hKeysPressed] + bit B_BUTTON_F, a + jr nz, .exit + and A_BUTTON | SELECT | START + jr nz, .action_button + ldh a, [hKeysReleased] + and D_PAD + jr nz, .reload_card_image ; jump if the D_PAD key was released this frame + ret +.exit + ld a, $ff + ldh [hCurMenuItem], a +.action_button + scf + ret +.reload_card_image + call LoadSelectedCardGfx + or a + ret + +Func_5735: + ld hl, wPrintSortNumberInCardListPtr + ld de, PrintSortNumberInCardList + ld [hl], e + inc hl + ld [hl], d + ld a, 1 + ld [wSortCardListByID], a + ret + +Func_5744: + ld hl, wPrintSortNumberInCardListPtr + jp CallIndirect + +; goes through list in wDuelTempList + 10 +; and prints the number stored in each entry +; beside the corresponding card in screen. +; used in lists for reordering cards in the Deck. +PrintSortNumberInCardList: + lb bc, 1, 2 + ld hl, wDuelTempList + 10 +.next + ld a, [hli] + cp $ff + jr z, .done + or a ; SYM_SPACE + jr z, .space + add SYM_0 ; load number symbol +.space + call WriteByteToBGMap0 + ; move two lines down + inc c + inc c + jr .next +.done + ret + +; draw the card page of the card at wLoadedCard1 and listen for input +; in order to switch the page or to exit. +; triggered by checking a hand card or a discard pile card in the Check menu. +; D_UP and D_DOWN exit the card page allowing the caller to load the card page +; of the card above or below in the list. +OpenCardPage_FromCheckHandOrDiscardPile: + ld a, B_BUTTON | D_UP | D_DOWN + ld [wCardPageExitKeys], a + xor a ; CARDPAGETYPE_NOT_PLAY_AREA + jr OpenCardPage + +; draw the card page of the card at wLoadedCard1 and listen for input +; in order to switch the page or to exit. +; triggered by checking an arena card or a bench card in the Check menu. +OpenCardPage_FromCheckPlayArea: + ld a, B_BUTTON + ld [wCardPageExitKeys], a + ld a, CARDPAGETYPE_PLAY_AREA + jr OpenCardPage + +; draw the card page of the card at wLoadedCard1 and listen for input +; in order to switch the page or to exit. +; triggered by checking a card in the Hand menu. +OpenCardPage_FromHand: + ld a, B_BUTTON + ld [wCardPageExitKeys], a + xor a ; CARDPAGETYPE_NOT_PLAY_AREA +; fallthrough + +; draw the card page of the card at wLoadedCard1 and listen for input +; in order to switch the page or to exit. +OpenCardPage: + ld [wCardPageType], a + call ZeroObjectPositionsAndToggleOAMCopy + call EmptyScreen + call Func_3b31 + ; load the graphics and display the card image of wLoadedCard1 + call LoadDuelCardSymbolTiles + ld de, v0Tiles1 + $20 tiles + call LoadLoaded1CardGfx + call SetOBP1OrSGB3ToCardPalette + call SetBGP6OrSGB3ToCardPalette + call FlushAllPalettesOrSendPal23Packet + lb de, $38, $30 ; X Position and Y Position of top-left corner + call PlaceCardImageOAM + lb de, 6, 4 + call ApplyBGP6OrSGB3ToCardImage + ; display the initial card page for the card at wLoadedCard1 + xor a + ld [wCardPageNumber], a +.load_next + call DisplayFirstOrNextCardPage + jr c, .done ; done if trying to advance past the last page with START or A_BUTTON + call EnableLCD +.input_loop + call DoFrame + ldh a, [hDPadHeld] + ld b, a + ld a, [wCardPageExitKeys] + and b + jr nz, .done + ; START and A_BUTTON advance to the next valid card page, but close it + ; after trying to advance from the last page + ldh a, [hKeysPressed] + and START | A_BUTTON + jr nz, .load_next + ; D_RIGHT and D_LEFT advance to the next and previous valid card page respectively. + ; however, unlike START and A_BUTTON, D_RIGHT past the last page goes back to the start. + ldh a, [hKeysPressed] + and D_RIGHT | D_LEFT + jr z, .input_loop + call DisplayCardPageOnLeftOrRightPressed + jr .input_loop +.done + ret + +; display the previous valid card page of the card at wLoadedCard1 if bit D_LEFT_F +; of a is set, and the first or next valid card page otherwise. +; DisplayFirstOrNextCardPage and DisplayPreviousCardPage only call DisplayCardPage +; when GoToFirstOrNextCardPage and GoToPreviousCardPage respectively return nc +; so the "call c, DisplayCardPage" instructions makes sure the card page switched +; to is always displayed. +DisplayCardPageOnLeftOrRightPressed: + bit D_LEFT_F, a + jr nz, .left +;.right + call DisplayFirstOrNextCardPage + call c, DisplayCardPage + ret +.left + call DisplayPreviousCardPage + call c, DisplayCardPage + ret + +; draws text box that covers the whole screen +; and prints the text ID in hl, then +; waits for Player input. +DrawWholeScreenTextBox: + push hl + call EmptyScreen + lb de, 0, 0 + lb bc, 20, 18 + call DrawRegularTextBox + ld a, 19 + lb de, 1, 1 + call InitTextPrintingInTextbox + call SetNoLineSeparation + pop hl + call ProcessTextFromID + call EnableLCD + call SetOneLineSeparation + call WaitForWideTextBoxInput + ret + +Func_5805: + call Func_3b31 + ld a, [wNumberPrizeCardsToTake] + ld l, a + ld h, $00 + call LoadTxRam3 + ld a, DUELVARS_DUELIST_TYPE + call GetTurnDuelistVariable + cp DUELIST_TYPE_PLAYER + jr nz, .opponent + +; player + ldtx hl, WillDrawNPrizesText + call DrawWideTextBox_WaitForInput + ld a, [wNumberPrizeCardsToTake] + call SelectPrizeCards + ld hl, hTemp_ffa0 + ld d, [hl] + inc hl + ld e, [hl] + call SerialSend8Bytes + +.return_has_prizes + call ExchangeRNG + ld a, DUELVARS_PRIZES + call GetTurnDuelistVariable + or a + ret nz + scf + ret + +.opponent + call Func_588a + ldtx hl, WillDrawNPrizesText + call DrawWideTextBox_PrintText + call CountPrizes + ld [wTempNumRemainingPrizeCards], a + ld a, DUELVARS_DUELIST_TYPE + call GetTurnDuelistVariable + cp DUELIST_TYPE_LINK_OPP + jr z, .link_opponent + call AIDoAction_TakePrize + ld c, 60 +.delay_loop + call DoFrame + dec c + jr nz, .delay_loop + jr .asm_586f + +.link_opponent + call SerialRecv8Bytes + ld a, DUELVARS_PRIZES + call GetTurnDuelistVariable + ld [hl], d + ld a, e + cp $ff + call nz, AddCardToHand + +.asm_586f + ld a, [wTempNumRemainingPrizeCards] + ld hl, wNumberPrizeCardsToTake + cp [hl] + jr nc, .asm_587e + ld l, a + ld h, $00 + call LoadTxRam3 +.asm_587e + farcall Func_82b6 + ldtx hl, DrewNPrizesText + call DrawWideTextBox_WaitForInput + jr .return_has_prizes + +Func_588a: + ld l, PLAYER_TURN + ldh a, [hWhoseTurn] + ld h, a + jp DrawYourOrOppPlayAreaScreen_Bank0 + +; display the previous valid card page +DisplayPreviousCardPage: + call GoToPreviousCardPage + jr nc, DisplayCardPage + ret + +; display the next valid card page or load the first valid card page if [wCardPageNumber] == 0 +DisplayFirstOrNextCardPage: + call GoToFirstOrNextCardPage + ret c +; fallthrough + +; display the card page with id at wCardPageNumber of wLoadedCard1 +DisplayCardPage: + ld a, [wCardPageNumber] + ld hl, CardPageDisplayPointerTable + call JumpToFunctionInTable + call EnableLCD + or a + ret + +; load the tiles and palette of the card selected in card list screen +LoadSelectedCardGfx: + ldh a, [hCurMenuItem] + call GetCardInDuelTempList + call LoadCardDataToBuffer1_FromCardID + ld de, v0Tiles1 + $20 tiles + call LoadLoaded1CardGfx + ld de, $c0c ; useless + call SetBGP6OrSGB3ToCardPalette + call FlushAllPalettesOrSendPal23Packet + ret + +CardPageDisplayPointerTable: + dw DrawDuelMainScene + dw DisplayCardPage_PokemonOverview ; CARDPAGE_POKEMON_OVERVIEW + dw DisplayCardPage_PokemonAttack1Page1 ; CARDPAGE_POKEMON_ATTACK1_1 + dw DisplayCardPage_PokemonAttack1Page2 ; CARDPAGE_POKEMON_ATTACK1_2 + dw DisplayCardPage_PokemonAttack2Page1 ; CARDPAGE_POKEMON_ATTACK2_1 + dw DisplayCardPage_PokemonAttack2Page2 ; CARDPAGE_POKEMON_ATTACK2_2 + dw DisplayCardPage_PokemonDescription ; CARDPAGE_POKEMON_DESCRIPTION + dw DrawDuelMainScene + dw DrawDuelMainScene + dw DisplayCardPage_Energy ; CARDPAGE_ENERGY + dw DisplayCardPage_Energy ; CARDPAGE_ENERGY + 1 + dw DrawDuelMainScene + dw DrawDuelMainScene + dw DisplayCardPage_TrainerPage1 ; CARDPAGE_TRAINER_1 + dw DisplayCardPage_TrainerPage2 ; CARDPAGE_TRAINER_2 + dw DrawDuelMainScene + +; given the current card page at [wCardPageNumber], go to the next valid card page or load +; the first valid card page of the current card at wLoadedCard1 if [wCardPageNumber] == 0 +GoToFirstOrNextCardPage: + ld a, [wCardPageNumber] + or a + jr nz, .advance_page + ; load the first page for this type of card + ld a, [wLoadedCard1Type] + ld b, a + ld a, CARDPAGE_ENERGY + bit TYPE_ENERGY_F, b + jr nz, .set_initial_page + ld a, CARDPAGE_TRAINER_1 + bit TYPE_TRAINER_F, b + jr nz, .set_initial_page + ld a, CARDPAGE_POKEMON_OVERVIEW +.set_initial_page + ld [wCardPageNumber], a + or a + ret +.advance_page + ld hl, wCardPageNumber + inc [hl] + ld a, [hl] + call SwitchCardPage + jr c, .set_card_page + ; stay in this page if it exists, or skip to previous page if it doesn't + or a + ret nz + ; non-existent page: skip to next + jr .advance_page +.set_card_page + ld [wCardPageNumber], a + ret + +; given the current card page at [wCardPageNumber], go to the previous +; valid card page for the current card at wLoadedCard1 +GoToPreviousCardPage: + ld hl, wCardPageNumber + dec [hl] + ld a, [hl] + call SwitchCardPage + jr c, .set_card_page + ; stay in this page if it exists, or skip to previous page if it doesn't + or a + ret nz + ; non-existent page: skip to previous + jr GoToPreviousCardPage +.set_card_page + ld [wCardPageNumber], a +.previous_page_loop + call SwitchCardPage + or a + jr nz, .stay + ld hl, wCardPageNumber + dec [hl] + jr .previous_page_loop +.stay + scf + ret + +; check if the card page trying to switch to is valid for the card at wLoadedCard1 +; return with the equivalent to one of these three actions: + ; stay in card page trying to switch to (nc, nz) + ; change to card page returned in a if D_LEFT/D_RIGHT pressed, or exit if A_BUTTON/START pressed (c) + ; non-existent page, so skip to next/previous (nc, z) +SwitchCardPage: + ld hl, CardPageSwitchPointerTable + jp JumpToFunctionInTable + +CardPageSwitchPointerTable: + dw CardPageSwitch_00 + dw CardPageSwitch_PokemonOverviewOrDescription ; CARDPAGE_POKEMON_OVERVIEW + dw CardPageSwitch_PokemonAttack1Page1 ; CARDPAGE_POKEMON_ATTACK1_1 + dw CardPageSwitch_PokemonAttack1Page2 ; CARDPAGE_POKEMON_ATTACK1_2 + dw CardPageSwitch_PokemonAttack2Page1 ; CARDPAGE_POKEMON_ATTACK2_1 + dw CardPageSwitch_PokemonAttack2Page2 ; CARDPAGE_POKEMON_ATTACK2_2 + dw CardPageSwitch_PokemonOverviewOrDescription ; CARDPAGE_POKEMON_DESCRIPTION + dw CardPageSwitch_PokemonEnd + dw CardPageSwitch_08 + dw CardPageSwitch_EnergyOrTrainerPage1 ; CARDPAGE_ENERGY + dw CardPageSwitch_TrainerPage2 ; CARDPAGE_ENERGY + 1 + dw CardPageSwitch_EnergyEnd + dw CardPageSwitch_0c + dw CardPageSwitch_EnergyOrTrainerPage1 ; CARDPAGE_TRAINER_1 + dw CardPageSwitch_TrainerPage2 ; CARDPAGE_TRAINER_2 + dw CardPageSwitch_TrainerEnd + +; return with CARDPAGE_POKEMON_DESCRIPTION +CardPageSwitch_00: + ld a, CARDPAGE_POKEMON_DESCRIPTION + scf + ret + +; return with current page +CardPageSwitch_PokemonOverviewOrDescription: + ld a, $1 + or a + ret ; nz + +; return with current page if [wLoadedCard1Atk1Name] non-0 +; (if card has at least one attack) +CardPageSwitch_PokemonAttack1Page1: + ld hl, wLoadedCard1Atk1Name + jr CheckCardPageExists + +; return with current page if [wLoadedCard1Atk1Description + 2] non-0 +; (if card's first attack has a two-page description) +CardPageSwitch_PokemonAttack1Page2: + ld hl, wLoadedCard1Atk1Description + 2 + jr CheckCardPageExists + +; return with current page if [wLoadedCard1Atk2Name] non-0 +; (if card has two attacks) +CardPageSwitch_PokemonAttack2Page1: + ld hl, wLoadedCard1Atk2Name + jr CheckCardPageExists + +; return with current page if [wLoadedCard1Atk1Description + 2] non-0 +; (if card's second attack has a two-page description) +CardPageSwitch_PokemonAttack2Page2: + ld hl, wLoadedCard1Atk2Description + 2 +; fallthrough + +CheckCardPageExists: + ld a, [hli] + or [hl] + ret + +; return with CARDPAGE_POKEMON_OVERVIEW +CardPageSwitch_PokemonEnd: + ld a, CARDPAGE_POKEMON_OVERVIEW + scf + ret + +; return with CARDPAGE_ENERGY + 1 +CardPageSwitch_08: + ld a, CARDPAGE_ENERGY + 1 + scf + ret + +; return with current page +CardPageSwitch_EnergyOrTrainerPage1: + ld a, $1 + or a + ret ; nz + +; return with current page if [wLoadedCard1NonPokemonDescription + 2] non-0 +; (if this trainer card has a two-page description) +CardPageSwitch_TrainerPage2: + ld hl, wLoadedCard1NonPokemonDescription + 2 + jr CheckCardPageExists + +; return with CARDPAGE_ENERGY +CardPageSwitch_EnergyEnd: + ld a, CARDPAGE_ENERGY + scf + ret + +; return with CARDPAGE_TRAINER_2 +CardPageSwitch_0c: + ld a, CARDPAGE_TRAINER_2 + scf + ret + +; return with CARDPAGE_TRAINER_1 +CardPageSwitch_TrainerEnd: + ld a, CARDPAGE_TRAINER_1 + scf + ret + +ZeroObjectPositionsAndToggleOAMCopy: + call ZeroObjectPositions + ld a, $01 + ld [wVBlankOAMCopyToggle], a + ret + +; place OAM for a 8x6 image, using object size 8x16 and obj palette 1. +; d, e: X Position and Y Position of the top-left corner. +; starting tile number is $a0 (v0Tiles1 + $20 tiles). +; used to draw the image of a card in the check card screens. +PlaceCardImageOAM: + call Set_OBJ_8x16 + ld l, $a0 + ld c, 8 ; number of rows +.next_column + ld b, 3 ; number of columns + push de +.next_row + push bc + ld c, l ; tile number + ld b, 1 ; attributes (palette) + call SetOneObjectAttributes + pop bc + inc l + inc l ; next 8x16 tile + ld a, 16 + add e ; Y Position += 16 (next 8x16 row) + ld e, a + dec b + jr nz, .next_row + pop de + ld a, 8 + add d ; X Position += 8 (next 8x16 column) + ld d, a + dec c + jr nz, .next_column + ld a, $01 + ld [wVBlankOAMCopyToggle], a + ret + +; given the deck index of a card in the play area (i.e. -1 indicates empty) +; load the graphics (tiles and palette) of the card to de +LoadPlayAreaCardGfx: + cp -1 + ret z + push de + call LoadCardDataToBuffer1_FromDeckIndex + pop de +; fallthrough + +; load the graphics (tiles and palette) of the card loaded in wLoadedCard1 to de +LoadLoaded1CardGfx: + ld hl, wLoadedCard1Gfx + ld a, [hli] + ld h, [hl] + ld l, a + lb bc, $30, TILE_SIZE + call LoadCardGfx + ret + +SetBGP7OrSGB2ToCardPalette: + ld a, [wConsole] + or a ; CONSOLE_DMG + ret z + cp CONSOLE_SGB + jr z, .sgb + ld a, $07 ; CGB BG Palette 7 + call CopyCGBCardPalette + ret +.sgb + ld hl, wCardPalette + ld de, wTempSGBPacket + 1 ; PAL Packet color #0 (PAL23's SGB2) + ld b, CGB_PAL_SIZE +.copy_pal_loop + ld a, [hli] + ld [de], a + inc de + dec b + jr nz, .copy_pal_loop + ret + +SetBGP6OrSGB3ToCardPalette: + ld a, [wConsole] + or a ; CONSOLE_DMG + ret z + cp CONSOLE_SGB + jr z, SetSGB3ToCardPalette + ld a, $06 ; CGB BG Palette 6 + call CopyCGBCardPalette + ret + +SetSGB3ToCardPalette: + ld hl, wCardPalette + 2 + ld de, wTempSGBPacket + 9 ; Pal Packet color #4 (PAL23's SGB3) + ld b, 6 + jr SetBGP7OrSGB2ToCardPalette.copy_pal_loop + +SetOBP1OrSGB3ToCardPalette: + ld a, $e4 + ld [wOBP0], a + ld a, [wConsole] + or a ; CONSOLE_DMG + ret z + cp CONSOLE_SGB + jr z, SetSGB3ToCardPalette + ld a, $09 ; CGB Object Palette 1 +; fallthrough + +CopyCGBCardPalette: + add a + add a + add a ; a *= CGB_PAL_SIZE + ld e, a + ld d, $00 + ld hl, wBackgroundPalettesCGB ; wObjectPalettesCGB - 8 palettes + add hl, de + ld de, wCardPalette + ld b, CGB_PAL_SIZE +.copy_pal_loop + ld a, [de] + inc de + ld [hli], a + dec b + jr nz, .copy_pal_loop + ret + +FlushAllPalettesOrSendPal23Packet: + ld a, [wConsole] + or a ; CONSOLE_DMG + ret z + cp CONSOLE_SGB + jr z, .sgb + call FlushAllPalettes + ret +.sgb +; sgb PAL23, 1 ; sgb_command, length +; rgb 28, 28, 24 +; colors 1-7 carried over + ld a, PAL23 << 3 + 1 + ld hl, wTempSGBPacket + ld [hli], a + ld a, LOW(24 << 10 | 28 << 5 | 28) + ld [hli], a + ld a, HIGH(24 << 10 | 28 << 5 | 28) + ld [hld], a + dec hl + xor a + ld [wTempSGBPacket + $f], a + call SendSGB + ret + +ApplyBGP6OrSGB3ToCardImage: + ld a, [wConsole] + or a ; CONSOLE_DMG + ret z + cp CONSOLE_SGB + jr z, .sgb + ld a, $06 ; CGB BG Palette 6 + call ApplyCardCGBAttributes + ret +.sgb + ld a, 3 << 0 + 3 << 2 ; Color Palette Designation +; fallthrough + +SendCardAttrBlkPacket: + call CreateCardAttrBlkPacket + call SendSGB + ret + +ApplyBGP7OrSGB2ToCardImage: + ld a, [wConsole] + or a ; CONSOLE_DMG + ret z + cp CONSOLE_SGB + jr z, .sgb + ld a, $07 ; CGB BG Palette 7 + call ApplyCardCGBAttributes + ret +.sgb + ld a, 2 << 0 + 2 << 2 ; Color Palette Designation + jr SendCardAttrBlkPacket + +Func_5a81: + ld a, [wConsole] + or a ; CONSOLE_DMG + ret z + cp CONSOLE_SGB + jr z, .sgb + lb de, 0, 5 + call ApplyBGP7OrSGB2ToCardImage + lb de, 12, 1 + call ApplyBGP6OrSGB3ToCardImage + ret +.sgb + ld a, 2 << 0 + 2 << 2 ; Data Set #1: Color Palette Designation + lb de, 0, 5 ; Data Set #1: X, Y + call CreateCardAttrBlkPacket + push hl + ld a, 2 + ld [wTempSGBPacket + 1], a ; set number of data sets to 2 + ld hl, wTempSGBPacket + 8 + ld a, 3 << 0 + 3 << 2 ; Data Set #2: Color Palette Designation + lb de, 12, 1 ; Data Set #2: X, Y + call CreateCardAttrBlkPacket_DataSet + pop hl + call SendSGB + ret + +CreateCardAttrBlkPacket: +; sgb ATTR_BLK, 1 ; sgb_command, length +; db 1 ; number of data sets + ld hl, wTempSGBPacket + push hl + ld [hl], ATTR_BLK << 3 + 1 + inc hl + ld [hl], 1 + inc hl + call CreateCardAttrBlkPacket_DataSet + xor a + ld [hli], a + ld [hli], a + ld [hli], a + ld [hli], a + pop hl + ret + +CreateCardAttrBlkPacket_DataSet: +; Control Code, Color Palette Designation, X1, Y1, X2, Y2 +; db ATTR_BLK_CTRL_INSIDE + ATTR_BLK_CTRL_LINE, a, d, e, d+7, e+5 ; data set 1 + ld [hl], ATTR_BLK_CTRL_INSIDE + ATTR_BLK_CTRL_LINE + inc hl + ld [hl], a + inc hl + ld [hl], d + inc hl + ld [hl], e + inc hl + ld a, 7 + add d + ld [hli], a + ld a, 5 + add e + ld [hli], a + ret + +; given the 8x6 card image with coordinates at de, fill its BGMap attributes with a +ApplyCardCGBAttributes: + call BankswitchVRAM1 + lb hl, 0, 0 + lb bc, 8, 6 + call FillRectangle + call BankswitchVRAM0 + ret + +; set the default game palettes for all three systems +; BGP and OBP0 on DMG +; SGB0 and SGB1 on SGB +; BGP0 to BGP5 and OBP1 on CGB +SetDefaultPalettes: + ld a, [wConsole] + cp CONSOLE_SGB + jr z, .sgb + cp CONSOLE_CGB + jr z, .cgb + ld a, $e4 + ld [wOBP0], a + ld [wBGP], a + ld a, $01 ; equivalent to FLUSH_ONE_PAL + ld [wFlushPaletteFlags], a + ret +.cgb + ld a, $04 + ld [wTextBoxFrameType], a + ld de, CGBDefaultPalettes + ld hl, wBackgroundPalettesCGB + ld c, 5 palettes + call .copy_de_to_hl + ld de, CGBDefaultPalettes + ld hl, wObjectPalettesCGB + ld c, CGB_PAL_SIZE + call .copy_de_to_hl + call FlushAllPalettes + ret +.sgb + ld a, $04 + ld [wTextBoxFrameType], a + ld a, PAL01 << 3 + 1 + ld hl, wTempSGBPacket + push hl + ld [hli], a + ld de, Pal01Packet_Default + ld c, $0e + call .copy_de_to_hl + ld [hl], c + pop hl + call SendSGB + ret + +.copy_de_to_hl + ld a, [de] + inc de + ld [hli], a + dec c + jr nz, .copy_de_to_hl + ret + +CGBDefaultPalettes: +; BGP0 and OBP0 + rgb 28, 28, 24 + rgb 21, 21, 16 + rgb 10, 10, 8 + rgb 0, 0, 0 +; BGP1 + rgb 28, 28, 24 + rgb 30, 29, 0 + rgb 30, 3, 0 + rgb 0, 0, 0 +; BGP2 + rgb 28, 28, 24 + rgb 0, 18, 0 + rgb 12, 11, 20 + rgb 0, 0, 0 +; BGP3 + rgb 28, 28, 24 + rgb 22, 0, 22 + rgb 27, 7, 3 + rgb 0, 0, 0 +; BGP4 + rgb 28, 28, 24 + rgb 26, 10, 0 + rgb 28, 0, 0 + rgb 0, 0, 0 + +; first and last byte of the packet not contained here (see SetDefaultPalettes.sgb) +Pal01Packet_Default: +; SGB0 + rgb 28, 28, 24 + rgb 21, 21, 16 + rgb 10, 10, 8 + rgb 0, 0, 0 +; SGB1 + rgb 26, 10, 0 + rgb 28, 0, 0 + rgb 0, 0, 0 + +JPWriteByteToBGMap0: + jp WriteByteToBGMap0 + +DisplayCardPage_PokemonOverview: + ld a, [wCardPageType] + or a ; CARDPAGETYPE_NOT_PLAY_AREA + jr nz, .play_area_card_page + +; CARDPAGETYPE_NOT_PLAY_AREA + ; print surrounding box, card name at 5,1, type, set 2, and rarity + call PrintPokemonCardPageGenericInformation + ; print fixed text and draw the card symbol associated to its TYPE_* + ld hl, CardPageRetreatWRTextData + call PlaceTextItems + ld hl, CardPageLvHPNoTextTileData + call WriteDataBlocksToBGMap0 + lb de, 3, 2 + call DrawCardSymbol + ; print pre-evolution's name (if any) + ld a, [wLoadedCard1Stage] + or a + jr z, .basic + ld hl, wLoadedCard1PreEvoName + lb de, 1, 3 + call InitTextPrinting_ProcessTextFromPointerToID +.basic + ; print card level and maximum HP + lb bc, 12, 2 + ld a, [wLoadedCard1Level] + call WriteTwoDigitNumberInTxSymbolFormat + lb bc, 16, 2 + ld a, [wLoadedCard1HP] + call WriteTwoByteNumberInTxSymbolFormat + jr .print_numbers_and_energies + +; CARDPAGETYPE_PLAY_AREA +.play_area_card_page + ; draw the surrounding box, and print fixed text + call DrawCardPageSurroundingBox + call LoadDuelCheckPokemonScreenTiles + ld hl, CardPageRetreatWRTextData + call PlaceTextItems + ld hl, CardPageNoTextTileData + call WriteDataBlocksToBGMap0 + ld a, 1 + ld [wCurPlayAreaY], a + ; print set 2 icon and rarity symbol at fixed positions + call DrawCardPageSet2AndRarityIcons + ; print (Y coord at [wCurPlayAreaY]) card name, level, type, energies, HP, location... + call PrintPlayAreaCardInformationAndLocation + +; common for both card page types +.print_numbers_and_energies + ; print Pokedex number in the bottom right corner (16,16) + lb bc, 16, 16 + ld a, [wLoadedCard1PokedexNumber] + call WriteTwoByteNumberInTxSymbolFormat + ; print the name, damage, and energy cost of each attack and/or Pokemon power that exists + ; first attack at 5,10 and second at 5,12 + lb bc, 5, 10 + +.attacks + ld e, c + ld hl, wLoadedCard1Atk1Name + call PrintAttackOrPkmnPowerInformation + inc c + inc c ; 12 + ld e, c + ld hl, wLoadedCard1Atk2Name + call PrintAttackOrPkmnPowerInformation + ; print the retreat cost (some amount of colorless energies) at 8,14 + inc c + inc c ; 14 + ld b, 8 + ld a, [wLoadedCard1RetreatCost] + ld e, a + inc e +.retreat_cost_loop + dec e + jr z, .retreat_cost_done + ld a, SYM_COLORLESS + call JPWriteByteToBGMap0 + inc b + jr .retreat_cost_loop +.retreat_cost_done + ; print the colors (energies) of the weakness(es) and resistance(s) + inc c ; 15 + ld a, [wCardPageType] + or a + jr z, .wr_from_loaded_card + ld a, [wCurPlayAreaSlot] + or a + jr nz, .wr_from_loaded_card + call GetArenaCardWeakness + ld d, a + call GetArenaCardResistance + ld e, a + jr .got_wr +.wr_from_loaded_card + ld a, [wLoadedCard1Weakness] + ld d, a + ld a, [wLoadedCard1Resistance] + ld e, a +.got_wr + ld a, d + ld b, 8 + call PrintCardPageWeaknessesOrResistances + inc c ; 16 + ld a, e + call PrintCardPageWeaknessesOrResistances + ret + +; displays the name, damage, and energy cost of an attack or Pokemon power. +; used in the Attack menu and in the card page of a Pokemon. +; input: + ; hl: pointer to attack 1 name in a atk_data_struct (which can be inside at card_data_struct) + ; e: Y coordinate to start printing the data at +PrintAttackOrPkmnPowerInformation: + ld a, [hli] + or [hl] + ret z + push bc + push hl + dec hl + ; print text ID pointed to by hl at 7,e + ld d, 7 + call InitTextPrinting_ProcessTextFromPointerToID + pop hl + inc hl + inc hl + ld a, [wCardPageNumber] + or a + jr nz, .print_damage + dec hl + ld a, [hli] + or [hl] + jr z, .print_damage + ; if in Attack menu and attack 1 description exists, print at 18,e: + ld b, 18 + ld c, e + ld a, SYM_ATK_DESCR + call WriteByteToBGMap0 +.print_damage + inc hl + inc hl + inc hl + push hl + ld a, [hl] + or a + jr z, .print_category + ; print attack damage at 15,(e+1) if non-0 + ld b, 15 ; unless damage has three digits, this is effectively 16 + ld c, e + inc c + call WriteTwoByteNumberInTxSymbolFormat +.print_category + pop hl + inc hl + ld a, [hl] + and $ff ^ RESIDUAL + jr z, .print_energy_cost + cp POKEMON_POWER + jr z, .print_pokemon_power + ; register a is DAMAGE_PLUS, DAMAGE_MINUS, or DAMAGE_X + ; print the damage modifier (+, -, x) at 18,(e+1) (after the damage value) + add SYM_PLUS - DAMAGE_PLUS + ld b, 18 + ld c, e + inc c + call WriteByteToBGMap0 + jr .print_energy_cost +.print_energy_cost + ld bc, CARD_DATA_ATTACK1_ENERGY_COST - CARD_DATA_ATTACK1_CATEGORY + add hl, bc + ld c, e + ld b, 2 ; bc = 2, e + lb de, NUM_TYPES / 2, 0 +.energy_loop + ld a, [hl] + swap a + call PrintEnergiesOfColor + ld a, [hli] + call PrintEnergiesOfColor + dec d + jr nz, .energy_loop + pop bc + ret +.print_pokemon_power + ; print "PKMN PWR" at 2,e + ld d, 2 + ldtx hl, PKMNPWRText + call InitTextPrinting_ProcessTextFromID + pop bc + ret + +; print the number of energies required of color (type) e, and return e ++ (next color). +; the requirement of the current color is provided as input in the lower nybble of a. +PrintEnergiesOfColor: + inc e + and $0f + ret z + push de + ld d, a +.print_energies_loop + ld a, e + call JPWriteByteToBGMap0 + inc b + dec d + jr nz, .print_energies_loop + pop de + ret + +; print the weaknesses or resistances of a Pokemon card, given in a, at b,c +PrintCardPageWeaknessesOrResistances: + push bc + push de + ld d, a + xor a ; FIRE +.loop + ; each WR_* constant is a different bit. rotate the value to find out + ; which bits are set and therefore which WR_* values are active. + ; a is kept updated with the equivalent TYPE_* constant. + inc a + cp 8 + jr nc, .done + rl d + jr nc, .loop + push af + call JPWriteByteToBGMap0 + inc b + pop af + jr .loop +.done + pop de + pop bc + ret + +; prints surrounding box, card name at 5,1, type, set 2, and rarity. +; used in all CARDPAGE_POKEMON_* and ATTACKPAGE_*, except in +; CARDPAGE_POKEMON_OVERVIEW when wCardPageType is CARDPAGETYPE_PLAY_AREA. +PrintPokemonCardPageGenericInformation: + call DrawCardPageSurroundingBox + lb de, 5, 1 + ld hl, wLoadedCard1Name + call InitTextPrinting_ProcessTextFromPointerToID + ld a, [wCardPageType] + or a + jr z, .from_loaded_card + ld a, [wCurPlayAreaSlot] + call GetPlayAreaCardColor + jr .got_color +.from_loaded_card + ld a, [wLoadedCard1Type] +.got_color + lb bc, 18, 1 + inc a + call JPWriteByteToBGMap0 + call DrawCardPageSet2AndRarityIcons + ret + +; draws the 20x18 surrounding box and also colorizes the card image +DrawCardPageSurroundingBox: + ld hl, wTextBoxFrameType + set 7, [hl] ; colorize textbox border also on SGB (with SGB1) + push hl + lb de, 0, 0 + lb bc, 20, 18 + call DrawRegularTextBox + pop hl + res 7, [hl] + lb de, 6, 4 + call ApplyBGP6OrSGB3ToCardImage + ret + +CardPageRetreatWRTextData: + textitem 1, 14, RetreatCostText + textitem 1, 15, WeaknessText + textitem 1, 16, ResistanceText + db $ff + +CardPageLvHPNoTextTileData: + db 11, 2, SYM_Lv, 0 + db 15, 2, SYM_HP, 0 +; continues to CardPageNoTextTileData + +CardPageNoTextTileData: + db 15, 16, SYM_No, 0 + db $ff + +DisplayCardPage_PokemonAttack1Page1: + ld hl, wLoadedCard1Atk1Name + ld de, wLoadedCard1Atk1Description + jr DisplayPokemonAttackCardPage + +DisplayCardPage_PokemonAttack1Page2: + ld hl, wLoadedCard1Atk1Name + ld de, wLoadedCard1Atk1Description + 2 + jr DisplayPokemonAttackCardPage + +DisplayCardPage_PokemonAttack2Page1: + ld hl, wLoadedCard1Atk2Name + ld de, wLoadedCard1Atk2Description + jr DisplayPokemonAttackCardPage + +DisplayCardPage_PokemonAttack2Page2: + ld hl, wLoadedCard1Atk2Name + ld de, wLoadedCard1Atk2Description + 2 +; fallthrough + +; input: + ; hl = address of the attack's name (text id) + ; de = address of the attack's description (either first or second text id) +DisplayPokemonAttackCardPage: + push de + push hl + ; print surrounding box, card name at 5,1, type, set 2, and rarity + call PrintPokemonCardPageGenericInformation + ; print name, damage, and energy cost of attack or Pokemon power starting at line 2 + ld e, 2 + pop hl + call PrintAttackOrPkmnPowerInformation + pop hl +; fallthrough + +; print, if non-null, the description of the trainer card, energy card, attack, +; or Pokemon power, given as a pointer to text id in hl, starting from 1,11 +PrintAttackOrNonPokemonCardDescription: + ld a, [hli] + or [hl] + ret z + dec hl + lb de, 1, 11 + call PrintAttackOrCardDescription + ret + +DisplayCardPage_PokemonDescription: + ; print surrounding box, card name at 5,1, type, set 2, and rarity + call PrintPokemonCardPageGenericInformation + call LoadDuelCardSymbolTiles2 + ; print "LENGTH", "WEIGHT", "Lv", and "HP" where it corresponds in the page + ld hl, CardPageLengthWeightTextData + call PlaceTextItems + ld hl, CardPageLvHPTextTileData + call WriteDataBlocksToBGMap0 + ; draw the card symbol associated to its TYPE_* at 3,2 + lb de, 3, 2 + call DrawCardSymbol + ; print the Level and HP numbers at 12,2 and 16,2 respectively + lb bc, 12, 2 + ld a, [wLoadedCard1Level] + call WriteTwoDigitNumberInTxSymbolFormat + lb bc, 16, 2 + ld a, [wLoadedCard1HP] + call WriteTwoByteNumberInTxSymbolFormat + ; print the Pokemon's category at 1,10 (just above the length and weight texts) + lb de, 1, 10 + ld hl, wLoadedCard1Category + call InitTextPrinting_ProcessTextFromPointerToID + ld a, TX_KATAKANA + call ProcessSpecialTextCharacter + ldtx hl, PokemonText + call ProcessTextFromID + ; print the length and weight values at 5,11 and 5,12 respectively + lb bc, 5, 11 + ld hl, wLoadedCard1Length + ld a, [hli] + ld l, [hl] + ld h, a + call PrintPokemonCardLength + lb bc, 5, 12 + ld hl, wLoadedCard1Weight + ld a, [hli] + ld h, [hl] + ld l, a + call PrintPokemonCardWeight + ldtx hl, LbsText + call InitTextPrinting_ProcessTextFromID + ; print the card's description without line separation + call SetNoLineSeparation + ld hl, wLoadedCard1Description + ld a, [hli] + ld h, [hl] + ld l, a + call CountLinesOfTextFromID + lb de, 1, 13 + cp 4 + jr nc, .print_description + inc e ; move a line down, as the description is short enough to fit in three lines +.print_description + ld a, 19 ; line length + call InitTextPrintingInTextbox + ld hl, wLoadedCard1Description + call ProcessTextFromPointerToID + call SetOneLineSeparation + ret + +; given a card rarity constant in a, and CardRarityTextIDs in hl, +; print the text character associated to it at d,e +PrintCardPageRarityIcon: + inc a + add a + ld c, a + ld b, $00 + add hl, bc + call InitTextPrinting_ProcessTextFromPointerToID + ret + +; prints the card's set 2 icon and the full width text character of the card's rarity +DrawCardPageSet2AndRarityIcons: + ld a, [wLoadedCard1Set] + call LoadCardSet2Tiles + jr c, .icon_done + ; draw the 2x2 set 2 icon of this card + ld a, $fc + lb hl, 1, 2 + lb bc, 2, 2 + lb de, 15, 8 + call FillRectangle +.icon_done + lb de, 18, 9 + ld hl, CardRarityTextIDs + ld a, [wLoadedCard1Rarity] + cp PROMOSTAR + call nz, PrintCardPageRarityIcon + ret + +CardPageLengthWeightTextData: + textitem 1, 11, LengthText + textitem 1, 12, WeightText + db $ff + +CardPageLvHPTextTileData: + db 11, 2, SYM_Lv, 0 + db 15, 2, SYM_HP, 0 + db $ff + +CardRarityTextIDs: + tx PromostarRarityText ; PROMOSTAR (unused) + tx CircleRarityText ; CIRCLE + tx DiamondRarityText ; DIAMOND + tx StarRarityText ; STAR + +DisplayCardPage_TrainerPage1: + xor a ; HEADER_TRAINER + ld hl, wLoadedCard1NonPokemonDescription + jr DisplayEnergyOrTrainerCardPage + +DisplayCardPage_TrainerPage2: + xor a ; HEADER_TRAINER + ld hl, wLoadedCard1NonPokemonDescription + 2 + jr DisplayEnergyOrTrainerCardPage + +DisplayCardPage_Energy: + ld a, HEADER_ENERGY + ld hl, wLoadedCard1NonPokemonDescription +; fallthrough + +; input: + ; a = HEADER_ENERGY or HEADER_TRAINER + ; hl = address of the card's description (text id) +DisplayEnergyOrTrainerCardPage: + push hl + call LoadCardTypeHeaderTiles + ; draw surrounding box + lb de, 0, 0 + lb bc, 20, 18 + call DrawRegularTextBox + ; print the card's name at 4,3 + lb de, 4, 3 + ld hl, wLoadedCard1Name + call InitTextPrinting_ProcessTextFromPointerToID + ; colorize the card image + lb de, 6, 4 + call ApplyBGP6OrSGB3ToCardImage + ; display the card type header + ld a, $e0 + lb hl, 1, 8 + lb de, 6, 1 + lb bc, 8, 2 + call FillRectangle + ; print the set 2 icon and rarity symbol of the card + call DrawCardPageSet2AndRarityIcons + pop hl + call PrintAttackOrNonPokemonCardDescription + ret + +; display the card details of the card in wLoadedCard1 +; print the text at hl +_DisplayCardDetailScreen: + push hl + call DrawLargePictureOfCard + ld a, 18 + call CopyCardNameAndLevel + ld [hl], TX_END + ld hl, 0 + call LoadTxRam2 + pop hl + call DrawWideTextBox_WaitForInput + ret + +; draw a large picture of the card loaded in wLoadedCard1, including its image +; and a header indicating the type of card (TRAINER, ENERGY, PoKéMoN) +DrawLargePictureOfCard: + call ZeroObjectPositionsAndToggleOAMCopy + call EmptyScreen + call LoadSymbolsFont + call SetDefaultPalettes + ld a, LARGE_CARD_PICTURE + ld [wDuelDisplayedScreen], a + call LoadCardOrDuelMenuBorderTiles + ld e, HEADER_TRAINER + ld a, [wLoadedCard1Type] + cp TYPE_TRAINER + jr z, .draw + ld e, HEADER_ENERGY + and TYPE_ENERGY + jr nz, .draw + ld e, HEADER_POKEMON +.draw + ld a, e + call LoadCardTypeHeaderTiles + ld de, v0Tiles1 + $20 tiles + call LoadLoaded1CardGfx + call SetBGP6OrSGB3ToCardPalette + call FlushAllPalettesOrSendPal23Packet + ld hl, LargeCardTileData + call WriteDataBlocksToBGMap0 + lb de, 6, 3 + call ApplyBGP6OrSGB3ToCardImage + ret + +LargeCardTileData: + db 5, 0, $d0, $d4, $d4, $d4, $d4, $d4, $d4, $d4, $d4, $d1, 0 ; top border + db 5, 1, $d6, $e0, $e1, $e2, $e3, $e4, $e5, $e6, $e7, $d7, 0 ; header top + db 5, 2, $d6, $e8, $e9, $ea, $eb, $ec, $ed, $ee, $ef, $d7, 0 ; header bottom + db 5, 3, $d6, $a0, $a6, $ac, $b2, $b8, $be, $c4, $ca, $d7, 0 ; image + db 5, 4, $d6, $a1, $a7, $ad, $b3, $b9, $bf, $c5, $cb, $d7, 0 ; image + db 5, 5, $d6, $a2, $a8, $ae, $b4, $ba, $c0, $c6, $cc, $d7, 0 ; image + db 5, 6, $d6, $a3, $a9, $af, $b5, $bb, $c1, $c7, $cd, $d7, 0 ; image + db 5, 7, $d6, $a4, $aa, $b0, $b6, $bc, $c2, $c8, $ce, $d7, 0 ; image + db 5, 8, $d6, $a5, $ab, $b1, $b7, $bd, $c3, $c9, $cf, $d7, 0 ; image + db 5, 9, $d6, 0 ; empty line 1 (left) + db 14, 9, $d7, 0 ; empty line 1 (right) + db 5, 10, $d6, 0 ; empty line 2 (left) + db 14, 10, $d7, 0 ; empty line 2 (right) + db 5, 11, $d2, $d5, $d5, $d5, $d5, $d5, $d5, $d5, $d5, $d3, 0 ; bottom border + db $ff + +; print lines of text with no separation between them +SetNoLineSeparation: + ld a, $01 +; fallthrough + +SetLineSeparation: + ld [wLineSeparation], a + ret + +; separate lines of text by an empty line +SetOneLineSeparation: + xor a + jr SetLineSeparation + +; given a number in hl, print it divided by 10 at b,c, with decimal part +; separated by a dot (unless it's 0). used to print a Pokemon card's weight. +PrintPokemonCardWeight: + push bc + ld de, -1 + ld bc, -10 +.divide_by_10_loop + inc de + add hl, bc + jr c, .divide_by_10_loop + ld bc, 10 + add hl, bc + pop bc + push hl + push bc + ld l, e + ld h, d + call TwoByteNumberToTxSymbol_TrimLeadingZeros_Bank1 + pop bc + pop hl + ld a, l + ld hl, wStringBuffer + 5 + or a + jr z, .decimal_done + ld [hl], SYM_DOT + inc hl + add SYM_0 + ld [hli], a +.decimal_done + ld [hl], 0 + push bc + call BCCoordToBGMap0Address + ld hl, wStringBuffer +.find_first_digit_loop + ld a, [hli] + or a + jr z, .find_first_digit_loop + dec hl + push hl + ld b, -1 +.get_number_length_loop + inc b + ld a, [hli] + or a + jr nz, .get_number_length_loop + pop hl + push bc + call SafeCopyDataHLtoDE + pop bc + pop de + ld a, b + add d + ld d, a + ret + +; given a number in h and another in l, print them formatted as <l>'<h>" at b,c. +; used to print the length (feet and inches) of a Pokemon card. +PrintPokemonCardLength: + push hl + ld l, h + ld h, $00 + ldtx de, FeetText ; ' + call .print_feet_or_inches + pop hl + ld h, $00 + ldtx de, InchesText ; " + call .print_feet_or_inches + ret + +.print_feet_or_inches +; keep track how many digits each number consists of in wPokemonLengthPrintOffset, +; in order to align the rest of the string. the text with id at de +; is printed after the number. + push de + push bc + call TwoByteNumberToTxSymbol_TrimLeadingZeros_Bank1 + ld a, b + inc a + ld [wPokemonLengthPrintOffset], a + pop bc + push bc + push hl + call BCCoordToBGMap0Address + ld a, [wPokemonLengthPrintOffset] + ld b, a + pop hl + call SafeCopyDataHLtoDE + pop bc + ld a, [wPokemonLengthPrintOffset] + add b + ld b, a + pop hl + push bc + ld e, c + ld d, b + call InitTextPrinting + call ProcessTextFromID + pop bc + inc b + ret + +; return carry if the turn holder has any Pokemon with non-zero HP on the bench. +; return how many Pokemon with non-zero HP in b. +; does this by calculating how many Pokemon in play area minus one +HasAlivePokemonInBench: + ld a, $01 + jr _HasAlivePokemonInPlayArea + +; return carry if the turn holder has any Pokemon with non-zero HP in the play area. +; return how many Pokemon with non-zero HP in b. +HasAlivePokemonInPlayArea: + xor a +; fallthrough + +_HasAlivePokemonInPlayArea: + ld [wExcludeArenaPokemon], a + ld b, a + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + sub b + ld c, a + ld a, DUELVARS_ARENA_CARD_HP + add b + call GetTurnDuelistVariable + ld b, 0 + inc c + xor a + ld [wPlayAreaScreenLoaded], a + ld [wcbd4], a + jr .next_pkmn +.loop + ld a, [hli] + or a + jr z, .next_pkmn ; jump if this play area Pokemon has 0 HP + inc b +.next_pkmn + dec c + jr nz, .loop + ld a, b + or a + ret nz + scf + ret + +OpenPlayAreaScreenForViewing: + ld a, START + A_BUTTON + jr DisplayPlayAreaScreen + +OpenPlayAreaScreenForSelection: + ld a, START +; fallthrough + +DisplayPlayAreaScreen: + ld [wNoItemSelectionMenuKeys], a + ldh a, [hTempCardIndex_ff98] + push af + ld a, [wPlayAreaScreenLoaded] + or a + jr nz, .skip_ahead + xor a + ld [wSelectedDuelSubMenuItem], a + inc a + ld [wPlayAreaScreenLoaded], a +.asm_6022 + call ZeroObjectPositionsAndToggleOAMCopy + call EmptyScreen + call LoadDuelCardSymbolTiles + call LoadDuelCheckPokemonScreenTiles + call PrintPlayAreaCardList + call EnableLCD +.skip_ahead + ld hl, PlayAreaScreenMenuParameters_ActivePokemonIncluded + ld a, [wExcludeArenaPokemon] + or a + jr z, .init_menu_params + ld hl, PlayAreaScreenMenuParameters_ActivePokemonExcluded +.init_menu_params + ld a, [wSelectedDuelSubMenuItem] + call InitializeMenuParameters + ld a, [wNumPlayAreaItems] + ld [wNumMenuItems], a +.asm_604c + call DoFrame + call Func_60dd + jr nc, .asm_6061 + cp $02 + jp z, .asm_60ac + pop af + ldh [hTempCardIndex_ff98], a + ld a, [wcbd4] ; useless + jr OpenPlayAreaScreenForSelection +.asm_6061 + call HandleMenuInput + jr nc, .asm_604c + ld a, e + ld [wSelectedDuelSubMenuItem], a + ld a, [wExcludeArenaPokemon] + add e + ld [wCurPlayAreaSlot], a + ld a, [wNoItemSelectionMenuKeys] + ld b, a + ldh a, [hKeysPressed] + and b + jr z, .asm_6091 + ld a, [wCurPlayAreaSlot] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + cp -1 + jr z, .asm_6022 + call GetCardIDFromDeckIndex + call LoadCardDataToBuffer1_FromCardID + call OpenCardPage_FromCheckPlayArea + jr .asm_6022 +.asm_6091 + ld a, [wExcludeArenaPokemon] + ld c, a + ldh a, [hCurMenuItem] + add c + ldh [hTempPlayAreaLocation_ff9d], a + ldh a, [hCurMenuItem] + cp $ff + jr z, .asm_60b5 + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + or a + jr nz, .asm_60ac + jr .skip_ahead +.asm_60ac + pop af + ldh [hTempCardIndex_ff98], a + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hCurMenuItem], a + or a + ret +.asm_60b5 + pop af + ldh [hTempCardIndex_ff98], a + ldh a, [hTempPlayAreaLocation_ff9d] + ldh [hCurMenuItem], a + scf + ret + +PlayAreaScreenMenuParameters_ActivePokemonIncluded: + db 0, 0 ; cursor x, cursor y + db 3 ; y displacement between items + db 6 ; number of items + db SYM_CURSOR_R ; cursor tile number + db SYM_SPACE ; tile behind cursor + dw PlayAreaScreenMenuFunction ; function pointer if non-0 + +PlayAreaScreenMenuParameters_ActivePokemonExcluded: + db 0, 3 ; cursor x, cursor y + db 3 ; y displacement between items + db 6 ; number of items + db SYM_CURSOR_R ; cursor tile number + db SYM_SPACE ; tile behind cursor + dw PlayAreaScreenMenuFunction ; function pointer if non-0 + +PlayAreaScreenMenuFunction: + ldh a, [hKeysPressed] + and A_BUTTON | B_BUTTON | START + ret z + bit B_BUTTON_F, a + jr z, .start_or_a + ld a, $ff + ldh [hCurMenuItem], a +.start_or_a + scf + ret + +Func_60dd: + ld a, [wcbd4] + or a + ret z + ldh a, [hKeysPressed] + and SELECT + ret z + ld a, [wcbd4] + cp $02 + jr z, .asm_6121 + xor a + ld [wCurrentDuelMenuItem], a +.asm_60f2 + call DrawDuelMainScene + ldtx hl, SelectingBenchPokemonHandExamineBackText + call DrawWideTextBox_PrintTextNoDelay + call Func_615c +.asm_60fe + call DoFrame + ldh a, [hKeysPressed] + and A_BUTTON + jr nz, .a_pressed + call Func_6137 + call RefreshMenuCursor + xor a + call Func_6862 + jr nc, .asm_60fe + ldh a, [hKeysPressed] + and SELECT + jr z, .asm_60f2 +.asm_6119 + call HasAlivePokemonInBench + ld a, $01 + ld [wcbd4], a +.asm_6121 + scf + ret +.a_pressed + ld a, [wCurrentDuelMenuItem] + cp 2 + jr z, .asm_6119 + or a + jr z, .asm_6132 + call OpenDuelCheckMenu + jr .asm_60f2 +.asm_6132 + call OpenTurnHolderHandScreen_Simple + jr .asm_60f2 + +Func_6137: + ldh a, [hDPadHeld] + bit 1, a + ret nz + and D_RIGHT | D_LEFT + ret z + ld b, a + ld a, [wCurrentDuelMenuItem] + bit D_LEFT_F, b + jr z, .asm_6150 + dec a + bit D_DOWN_F, a + jr z, .asm_6156 + ld a, 2 + jr .asm_6156 +.asm_6150 + inc a + cp 3 + jr c, .asm_6156 + xor a +.asm_6156 + ld [wCurrentDuelMenuItem], a + call EraseCursor +; fallthrough + +Func_615c: + ld a, [wCurrentDuelMenuItem] + ld d, a + add a + add d + add a + add 2 + ld d, a + ld e, 16 + lb bc, SYM_CURSOR_R, SYM_SPACE + jp SetCursorParametersForTextBox + +Func_616e: + ldh [hTempPlayAreaLocation_ff9d], a + call ZeroObjectPositionsAndToggleOAMCopy + call EmptyScreen + call LoadDuelCardSymbolTiles + call LoadDuelCheckPokemonScreenTiles + xor a + ld [wExcludeArenaPokemon], a + call PrintPlayAreaCardList + call EnableLCD +; fallthrough + +Func_6186: + ld hl, wCurPlayAreaSlot + ldh a, [hTempPlayAreaLocation_ff9d] + ld [hli], a + ld c, a + add a + add c + ld [hl], a + call PrintPlayAreaCardInformationAndLocation + ret + +Func_6194: + call Func_6186 + ld a, [wCurPlayAreaY] + ld e, a + ld d, 0 + call SetCursorParametersForTextBox_Default + ret + +Func_61a1: + xor a + ld [wExcludeArenaPokemon], a + ld a, [wDuelDisplayedScreen] + cp PLAY_AREA_CARD_LIST + ret z + call ZeroObjectPositionsAndToggleOAMCopy + call EmptyScreen + call LoadDuelCardSymbolTiles + call LoadDuelCheckPokemonScreenTiles + ret + +; for each turn holder's play area Pokemon card, print the name, level, +; face down stage card, color symbol, status symbol (if any), pluspower/defender +; symbols (if any), attached energies (if any), and HP bar. +; also print the play area locations (ACT/BPx indicators) for each of the six slots. +; return the value of wNumPlayAreaItems (as returned from PrintPlayAreaCardList) in a. +PrintPlayAreaCardList_EnableLCD: + ld a, PLAY_AREA_CARD_LIST + ld [wDuelDisplayedScreen], a + call PrintPlayAreaCardList + call EnableLCD + ld a, [wNumPlayAreaItems] + ret + +; for each turn holder's play area Pokemon card, print the name, level, +; face down stage card, color symbol, status symbol (if any), pluspower/defender +; symbols (if any), attached energies (if any), and HP bar. +; also print the play area locations (ACT/BPx indicators) for each of the six slots. +PrintPlayAreaCardList: + ld a, PLAY_AREA_CARD_LIST + ld [wDuelDisplayedScreen], a + ld de, wDuelTempList + call SetListPointer + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld c, a + ld b, $00 +.print_cards_info_loop + ; for each Pokemon card in play area, print its information (and location) + push hl + push bc + ld a, b + ld [wCurPlayAreaSlot], a + ld a, b + add a + add b + ld [wCurPlayAreaY], a + ld a, b + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call SetNextElementOfList + call PrintPlayAreaCardInformationAndLocation + pop bc + pop hl + inc b + dec c + jr nz, .print_cards_info_loop + push bc +.print_locations_loop + ; print all play area location indicators (even if there's no Pokemon card on it) + ld a, b + cp MAX_PLAY_AREA_POKEMON + jr z, .locations_printed + ld [wCurPlayAreaSlot], a + add a + add b + ld [wCurPlayAreaY], a + push bc + call PrintPlayAreaCardLocation + pop bc + inc b + jr .print_locations_loop +.locations_printed + pop bc + ld a, b + ld [wNumPlayAreaItems], a + ld a, [wExcludeArenaPokemon] + or a + ret z + ; if wExcludeArenaPokemon is set, decrement [wNumPlayAreaItems] and shift back wDuelTempList + dec b + ld a, b + ld [wNumPlayAreaItems], a + ld hl, wDuelTempList + 1 + ld de, wDuelTempList +.shift_back_loop + ld a, [hli] + ld [de], a + inc de + dec b + jr nz, .shift_back_loop + ret + +; print a turn holder's play area Pokemon card's name, level, face down stage card, +; color symbol, status symbol (if any), pluspower/defender symbols (if any), +; attached energies (if any), HP bar, and the play area location (ACT/BPx indicator) +; input: + ; wCurPlayAreaSlot: PLAY_AREA_* of the card to display the information of + ; wCurPlayAreaY: Y coordinate of where to print the card's information +; total space occupied is a rectangle of 20x3 tiles +PrintPlayAreaCardInformationAndLocation: + ld a, [wCurPlayAreaSlot] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + cp -1 + ret z + call PrintPlayAreaCardInformation +; fallthrough + +; print a turn holder's play area Pokemon card's location (ACT/BPx indicator) +PrintPlayAreaCardLocation: + ; print the ACT/BPx indicator + ld a, [wCurPlayAreaSlot] + add a + add a + ld e, a + ld d, $00 + ld hl, PlayAreaLocationTileNumbers + add hl, de + ldh a, [hWhoseTurn] + cp PLAYER_TURN + jr z, .write_tiles + ; move forward to the opponent's side tile numbers + ; they have black letters and white background instead of the other way around + ld d, $0a +.write_tiles + ld a, [wCurPlayAreaY] + ld b, 1 + ld c, a + ld a, [hli] + add d + call WriteByteToBGMap0 + inc c + ld a, [hli] + add d + call WriteByteToBGMap0 + inc c + ld a, [hli] + add d + call WriteByteToBGMap0 + ret + +PlayAreaLocationTileNumbers: + db $e0, $e1, $e2, $00 ; ACT + db $e3, $e4, $e5, $00 ; BP1 + db $e3, $e4, $e6, $00 ; BP2 + db $e3, $e4, $e7, $00 ; BP3 + db $e3, $e4, $e8, $00 ; BP4 + db $e3, $e4, $e9, $00 ; BP5 + +; print a turn holder's play area Pokemon card's name, level, face down stage card, +; color symbol, status symbol (if any), pluspower/defender symbols (if any), +; attached energies (if any), and HP bar. +; input: + ; wCurPlayAreaSlot: PLAY_AREA_* of the card to display the information of + ; wCurPlayAreaY: Y coordinate of where to print the card's information +; total space occupied is a rectangle of 20x3 tiles +PrintPlayAreaCardInformation: + ; print name, level, color, stage, status, pluspower/defender + call PrintPlayAreaCardHeader + ; print the symbols of the attached energies + ld a, [wCurPlayAreaSlot] + ld e, a + ld a, [wCurPlayAreaY] + inc a + ld c, a + ld b, 7 + call PrintPlayAreaCardAttachedEnergies + ld a, [wCurPlayAreaY] + inc a + ld c, a + ld b, 5 + ld a, SYM_E + call WriteByteToBGMap0 + ; print the HP bar + inc c + ld a, SYM_HP + call WriteByteToBGMap0 + ld a, [wCurPlayAreaSlot] + add DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + or a + jr z, .zero_hp + ld e, a + ld a, [wLoadedCard1HP] + ld d, a + call DrawHPBar + ld a, [wCurPlayAreaY] + inc a + inc a + ld c, a + ld b, 7 + call BCCoordToBGMap0Address + ld hl, wDefaultText + ld b, 12 + call SafeCopyDataHLtoDE + ret +.zero_hp + ; if fainted, print "Knock Out" in place of the HP bar + ld a, [wCurPlayAreaY] + inc a + inc a + ld e, a + ld d, 7 + ldtx hl, KnockOutText + call InitTextPrinting_ProcessTextFromID + ret + +; print a turn holder's play area Pokemon card's name, level, face down stage card, +; color symbol, status symbol (if any), and pluspower/defender symbols (if any). +; input: + ; wCurPlayAreaSlot: PLAY_AREA_* of the card to display the information of + ; wCurPlayAreaY: Y coordinate of where to print the card's information +PrintPlayAreaCardHeader: + ; start by printing the Pokemon's name + ld a, [wCurPlayAreaSlot] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer1_FromDeckIndex + ld a, [wCurPlayAreaY] + ld e, a + ld d, 4 + call InitTextPrinting + ; copy the name to wDefaultText (max. 10 characters) + ; then call ProcessText with hl = wDefaultText + ld hl, wLoadedCard1Name + ld a, [hli] + ld h, [hl] + ld l, a + ld de, wDefaultText + push de + ld a, 10 ; card name maximum length + call CopyTextData_FromTextID + pop hl + call ProcessText + + ; print the Pokemon's color and the level + ld a, [wCurPlayAreaY] + ld c, a + ld b, 18 + ld a, [wCurPlayAreaSlot] + call GetPlayAreaCardColor + inc a + call JPWriteByteToBGMap0 + ld b, 14 + ld a, SYM_Lv + call WriteByteToBGMap0 + ld a, [wCurPlayAreaY] + ld c, a + ld b, 15 + ld a, [wLoadedCard1Level] + call WriteTwoDigitNumberInTxSymbolFormat + + ; print the 2x2 face down card image depending on the Pokemon's evolution stage + ld a, [wCurPlayAreaSlot] + add DUELVARS_ARENA_CARD_STAGE + call GetTurnDuelistVariable + add a + ld e, a + ld d, $00 + ld hl, FaceDownCardTileNumbers + add hl, de + ld a, [hli] ; starting tile to fill the 2x2 rectangle with + push hl + push af + lb hl, 1, 2 + lb bc, 2, 2 + ld a, [wCurPlayAreaY] + ld e, a + ld d, 2 + pop af + call FillRectangle + pop hl + ld a, [wConsole] + cp CONSOLE_CGB + jr nz, .not_cgb + ; in cgb, we have to take care of coloring it too + ld a, [hl] + lb hl, 0, 0 + lb bc, 2, 2 + call BankswitchVRAM1 + call FillRectangle + call BankswitchVRAM0 + +.not_cgb + ; print the status condition symbol if any (only for the arena Pokemon card) + ld hl, wCurPlayAreaSlot + ld a, [hli] + or a + jr nz, .skip_status + ld c, [hl] + inc c + inc c + ld b, 2 + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + call CheckPrintCnfSlpPrz + inc b + call CheckPrintPoisoned + inc b + call CheckPrintDoublePoisoned + +.skip_status + ; finally check whether to print the Pluspower and/or Defender symbols + ld a, [wCurPlayAreaSlot] + add DUELVARS_ARENA_CARD_ATTACHED_PLUSPOWER + call GetTurnDuelistVariable + or a + jr z, .not_pluspower + ld a, [wCurPlayAreaY] + inc a + ld c, a + ld b, 15 + ld a, SYM_PLUSPOWER + call WriteByteToBGMap0 + inc b + ld a, [hl] + add SYM_0 + call WriteByteToBGMap0 +.not_pluspower + ld a, [wCurPlayAreaSlot] + add DUELVARS_ARENA_CARD_ATTACHED_DEFENDER + call GetTurnDuelistVariable + or a + jr z, .not_defender + ld a, [wCurPlayAreaY] + inc a + ld c, a + ld b, 17 + ld a, SYM_DEFENDER + call WriteByteToBGMap0 + inc b + ld a, [hl] + add SYM_0 + call WriteByteToBGMap0 +.not_defender + ret + +FaceDownCardTileNumbers: +; starting tile number, cgb palette (grey, yellow/red, green/blue, pink/orange) + db $d0, $02 ; basic + db $d4, $02 ; stage 1 + db $d8, $01 ; stage 2 + db $dc, $01 ; stage 2 special + +; given a card's status in a, print the Poison symbol at bc if it's poisoned +CheckPrintPoisoned: + push af + and POISONED + jr z, .print +.poison + ld a, SYM_POISONED +.print + call WriteByteToBGMap0 + pop af + ret + +; given a card's status in a, print the Poison symbol at bc if it's double poisoned +CheckPrintDoublePoisoned: + push af + and DOUBLE_POISONED & (POISONED ^ $ff) + jr nz, CheckPrintPoisoned.poison ; double poisoned (print SYM_POISONED) + jr CheckPrintPoisoned.print ; not double poisoned (print SYM_SPACE) + +; given a card's status in a, print the Confusion, Sleep, or Paralysis symbol at bc +; for each of those status that is active +CheckPrintCnfSlpPrz: + push af + push hl + push de + and CNF_SLP_PRZ + ld e, a + ld d, $00 + ld hl, .status_symbols + add hl, de + ld a, [hl] + call WriteByteToBGMap0 + pop de + pop hl + pop af + ret + +.status_symbols + ; NO_STATUS, CONFUSED, ASLEEP, PARALYZED + db SYM_SPACE, SYM_CONFUSED, SYM_ASLEEP, SYM_PARALYZED + +; print the symbols of the attached energies of a turn holder's play area card +; input: +; - e: PLAY_AREA_* +; - b, c: where to print (x, y) +; - wAttachedEnergies and wTotalAttachedEnergies +PrintPlayAreaCardAttachedEnergies: + push bc + call GetPlayAreaCardAttachedEnergies + ld hl, wDefaultText + push hl + ld c, NUM_TYPES + xor a +.empty_loop + ld [hli], a + dec c + jr nz, .empty_loop + pop hl + ld de, wAttachedEnergies + lb bc, SYM_FIRE, NUM_TYPES - 1 +.next_color + ld a, [de] ; energy count of current color + inc de + inc a + jr .check_amount +.has_energy + ld [hl], b + inc hl +.check_amount + dec a + jr nz, .has_energy + inc b + dec c + jr nz, .next_color + ld a, [wTotalAttachedEnergies] + cp 9 + jr c, .place_tiles + ld a, SYM_PLUS + ld [wDefaultText + 7], a +.place_tiles + pop bc + call BCCoordToBGMap0Address + ld hl, wDefaultText + ld b, NUM_TYPES + call SafeCopyDataHLtoDE + ret + +Func_6423: + ld hl, wDefaultText + ld e, $08 +.asm_6428 + ld a, [hli] + call JPWriteByteToBGMap0 + inc b + dec e + jr nz, .asm_6428 + ret + +Func_6431: + xor a + ld [wSelectedDuelSubMenuItem], a + +Func_6435: + call Func_64b0 + ld hl, PlayAreaScreenMenuParameters_ActivePokemonIncluded + ld a, [wSelectedDuelSubMenuItem] + call InitializeMenuParameters + ld a, [wNumPlayAreaItems] + ld [wNumMenuItems], a +.asm_6447 + call DoFrame + call HandleMenuInput + ldh [hTempPlayAreaLocation_ff9d], a + ld [wHUDEnergyAndHPBarsX], a + jr nc, .asm_6447 + cp $ff + jr z, .asm_649b + ld [wSelectedDuelSubMenuItem], a + ldh a, [hKeysPressed] + and START + jr nz, .asm_649d + ldh a, [hCurMenuItem] + add a + ld e, a + ld d, $00 + ld hl, wDuelTempList + 1 + add hl, de + ld a, [hld] + cp $04 + jr nz, .asm_6447 + ld a, [hl] + ldh [hTempCardIndex_ff98], a + ld d, a + ld e, $00 + call CopyAttackDataAndDamage_FromDeckIndex + call DisplayUsePokemonPowerScreen + ld a, EFFECTCMDTYPE_INITIAL_EFFECT_1 + call TryExecuteEffectCommandFunction + jr nc, .asm_648c + ldtx hl, PokemonPowerSelectNotRequiredText + call DrawWideTextBox_WaitForInput + jp Func_6435 +.asm_648c + ldtx hl, UseThisPokemonPowerText + call YesOrNoMenuWithText + jp c, Func_6435 + ldh a, [hTempCardIndex_ff98] + ldh [hTemp_ffa0], a + or a + ret +.asm_649b + scf + ret +.asm_649d + ldh a, [hCurMenuItem] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + call LoadCardDataToBuffer1_FromCardID + call OpenCardPage_FromCheckPlayArea + jp Func_6435 + +Func_64b0: + call ZeroObjectPositionsAndToggleOAMCopy + call EmptyScreen + call LoadDuelCardSymbolTiles + call LoadDuelCheckPokemonScreenTiles + ld de, wDuelTempList + call SetListPointer + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld c, a + ld b, $00 +.asm_64ca + push hl + push bc + ld a, b + ld [wHUDEnergyAndHPBarsX], a + ld a, b + add a + add b + ld [wCurPlayAreaY], a + ld a, b + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call SetNextElementOfList + call PrintPlayAreaCardHeader + call PrintPlayAreaCardLocation + call Func_64fc + ld a, [wLoadedCard1Atk1Category] + call SetNextElementOfList + pop bc + pop hl + inc b + dec c + jr nz, .asm_64ca + ld a, b + ld [wNumPlayAreaItems], a + call EnableLCD + ret + +Func_64fc: + ld a, [wLoadedCard1Atk1Category] + cp POKEMON_POWER + ret nz + ld a, [wCurPlayAreaY] + inc a + ld e, a + ld d, $04 + ld hl, wLoadedCard1Atk1Name + call InitTextPrinting_ProcessTextFromPointerToID + ret + +; display the screen that prompts the player to use the selected card's +; Pokemon Power. Includes the card's information above, and the Pokemon Power's +; description below. +; input: hTempPlayAreaLocation_ff9d +DisplayUsePokemonPowerScreen: + ldh a, [hTempPlayAreaLocation_ff9d] + ld [wCurPlayAreaSlot], a + xor a + ld [wCurPlayAreaY], a + call ZeroObjectPositionsAndToggleOAMCopy + call EmptyScreen + call LoadDuelCardSymbolTiles + call LoadDuelCheckPokemonScreenTiles + call PrintPlayAreaCardInformationAndLocation + lb de, 1, 4 + call InitTextPrinting + ld hl, wLoadedCard1Atk1Name + call InitTextPrinting_ProcessTextFromPointerToID + lb de, 1, 6 + ld hl, wLoadedCard1Atk1Description + call PrintAttackOrCardDescription + ret + +; print the description of an attack, a Pokemon power, or a trainer or energy card +; x,y coordinates of where to start printing the text are given at de +; don't separate lines of text +PrintAttackOrCardDescription: + call SetNoLineSeparation + ld a, [hli] + ld h, [hl] + ld l, a + call CountLinesOfTextFromID + cp 7 + jr c, .print + dec e ; move one line up to fit (assumes it will be enough) +.print + ld a, 19 + call InitTextPrintingInTextbox + call ProcessTextFromID + call SetOneLineSeparation + ret + +; moves the cards loaded by deck index at hTempRetreatCostCards to the discard pile +DiscardRetreatCostCards: + ld hl, hTempRetreatCostCards +.discard_loop + ld a, [hli] + cp $ff + ret z + call PutCardInDiscardPile + jr .discard_loop + +; moves the discard pile cards that were loaded to hTempRetreatCostCards back to the active Pokemon. +; this exists because they will be discarded again during the call to AttemptRetreat, so +; it prevents the energy cards from being discarded twice. +ReturnRetreatCostCardsToArena: + ld hl, hTempRetreatCostCards +.loop + ld a, [hli] + cp $ff + ret z + push hl + call MoveDiscardPileCardToHand + call AddCardToHand + ld e, PLAY_AREA_ARENA + call PutHandCardInPlayArea + pop hl + jr .loop + +; discard retreat cost energy cards and attempt retreat of the arena card. +; return carry if unable to retreat this turn due to unsuccessful confusion check +; if successful, the retreated card is replaced with a bench Pokemon card +AttemptRetreat: + call DiscardRetreatCostCards + ldh a, [hTemp_ffa0] + and CNF_SLP_PRZ + cp CONFUSED + jr nz, .success + ldtx de, ConfusionCheckRetreatText + call TossCoin + jr c, .success + ld a, 1 + ld [wGotHeadsFromConfusionCheckDuringRetreat], a + scf + ret +.success + ldh a, [hTempPlayAreaLocation_ffa1] + ld e, a + call SwapArenaWithBenchPokemon + xor a + ld [wGotHeadsFromConfusionCheckDuringRetreat], a + ret + +; given a number between 0-255 in a, converts it to TX_SYMBOL format, +; and writes it to wStringBuffer + 2 and to the BGMap0 address at bc. +; leading zeros replaced with SYM_SPACE. +WriteTwoByteNumberInTxSymbolFormat: + push de + push bc + ld l, a + ld h, $00 + call TwoByteNumberToTxSymbol_TrimLeadingZeros_Bank1 + pop bc + push bc + call BCCoordToBGMap0Address + ld hl, wStringBuffer + 2 + ld b, 3 + call SafeCopyDataHLtoDE + pop bc + pop de + ret + +; given a number between 0-99 in a, converts it to TX_SYMBOL format, +; and writes it to wStringBuffer + 3 and to the BGMap0 address at bc. +; if the number is between 0-9, the first digit is replaced with SYM_SPACE. +WriteTwoDigitNumberInTxSymbolFormat: + push hl + push de + push bc + ld l, a + ld h, $00 + call TwoByteNumberToTxSymbol_TrimLeadingZeros_Bank1 + pop bc + push bc + call BCCoordToBGMap0Address + ld hl, wStringBuffer + 3 + ld b, 2 + call SafeCopyDataHLtoDE + pop bc + pop de + pop hl + ret + +; convert the number at hl to TX_SYMBOL text format and write it to wStringBuffer +; replace leading zeros with SYM_SPACE +TwoByteNumberToTxSymbol_TrimLeadingZeros_Bank1: + ld de, wStringBuffer + ld bc, -10000 + call .get_digit + ld bc, -1000 + call .get_digit + ld bc, -100 + call .get_digit + ld bc, -10 + call .get_digit + ld bc, -1 + call .get_digit + xor a ; TX_END + ld [de], a + ld hl, wStringBuffer + ld b, 4 +.digit_loop + ld a, [hl] + cp SYM_0 + jr nz, .done ; jump if not zero + ld [hl], SYM_SPACE ; trim leading zero + inc hl + dec b + jr nz, .digit_loop +.done + ret + +.get_digit + ld a, SYM_0 - 1 +.subtract_loop + inc a + add hl, bc + jr c, .subtract_loop + ld [de], a + inc de + ld a, l + sub c + ld l, a + ld a, h + sbc b + ld h, a + ret + +; input d, e: max. HP, current HP +DrawHPBar: + ld a, MAX_HP + ld c, SYM_SPACE + call .fill_hp_bar ; empty bar + ld a, d + ld c, SYM_HP_OK + call .fill_hp_bar ; fill (max. HP) with HP counters + ld a, d + sub e + ld c, SYM_HP_NOK + ; fill (max. HP - current HP) with damaged HP counters +.fill_hp_bar + or a + ret z + ld hl, wDefaultText + ld b, HP_BAR_LENGTH +.tile_loop + ld [hl], c + inc hl + dec b + ret z + sub MAX_HP / HP_BAR_LENGTH + jr nz, .tile_loop + ret + +; when an opponent's Pokemon card attacks, this displays a screen +; containing the description and information of the used attack +DisplayOpponentUsedAttackScreen: + call ZeroObjectPositionsAndToggleOAMCopy + call EmptyScreen + call LoadDuelCardSymbolTiles + call LoadDuelFaceDownCardTiles + ld a, [wTempCardID_ccc2] + ld e, a + ld d, $00 + call LoadCardDataToBuffer1_FromCardID + ld a, CARDPAGE_POKEMON_OVERVIEW + ld [wCardPageNumber], a + ld hl, wLoadedCard1Atk1Name + ld a, [wSelectedAttack] + or a + jr z, .first_atk + ld hl, wLoadedCard1Atk2Name +.first_atk + ld e, 1 + call PrintAttackOrPkmnPowerInformation + lb de, 1, 4 + ld hl, wLoadedAttackDescription + call PrintAttackOrCardDescription + ret + +; display card detail when a trainer card is used, and print "Used xxx" +; hTempCardIndex_ff9f contains the card's deck index +DisplayUsedTrainerCardDetailScreen: + ldh a, [hTempCardIndex_ff9f] + ldtx hl, UsedText + call DisplayCardDetailScreen + ret + +; prints the name and description of a trainer card, along with the +; "Used xxx" text in a text box. this function is used to show the player +; the information of a trainer card being used by the opponent. +PrintUsedTrainerCardDescription: + call EmptyScreen + call SetNoLineSeparation + lb de, 1, 1 + call InitTextPrinting + ld hl, wLoadedCard1Name + call ProcessTextFromPointerToID + ld a, 19 + lb de, 1, 3 + call InitTextPrintingInTextbox + ld hl, wLoadedCard1NonPokemonDescription + call ProcessTextFromPointerToID + call SetOneLineSeparation + ldtx hl, UsedText + call DrawWideTextBox_WaitForInput + ret + +; save data of the current duel to sCurrentDuel +; byte 0 is $01, bytes 1 and 2 are the checksum, byte 3 is [wDuelType] +; next $33a bytes come from DuelDataToSave +SaveDuelData: + farcall CommentedOut_1a6cc + ld de, sCurrentDuel +; fallthrough + +; save data of the current duel to de (in SRAM) +; byte 0 is $01, bytes 1 and 2 are the checksum, byte 3 is [wDuelType] +; next $33a bytes come from DuelDataToSave +SaveDuelDataToDE: + call EnableSRAM + push de + inc de + inc de + inc de + inc de + ld hl, DuelDataToSave + push de +.save_duel_data_loop + ; start copying data to de = sCurrentDuelData + $1 + ld c, [hl] + inc hl + ld b, [hl] + inc hl + ld a, c + or b + jr z, .data_done + push hl + push bc + ld c, [hl] + inc hl + ld b, [hl] + inc hl + pop hl + call CopyDataHLtoDE + pop hl + inc hl + inc hl + jr .save_duel_data_loop +.data_done + pop hl + ; save a checksum to hl = sCurrentDuelData + $1 + lb de, $23, $45 + ld bc, $334 ; misses last 6 bytes to calculate checksum +.checksum_loop + ld a, e + sub [hl] + ld e, a + ld a, [hli] + xor d + ld d, a + dec bc + ld a, c + or b + jr nz, .checksum_loop + pop hl + ld a, $01 + ld [hli], a ; sCurrentDuel + ld [hl], e ; sCurrentDuelChecksum + inc hl + ld [hl], d ; sCurrentDuelChecksum + inc hl + ld a, [wDuelType] + ld [hl], a ; sCurrentDuelData + call DisableSRAM + ret + +; loads current Duel data from SRAM and also general save data +; if the data is not valid, returns carry +LoadAndValidateDuelSaveData: + ld hl, sCurrentDuel + call ValidateSavedDuelData + ret c + ld de, sCurrentDuel + call LoadSavedDuelData + + call ValidateGeneralSaveData + ret nc + call LoadGeneralSaveData + or a + ret + +; load the data saved in sCurrentDuelData to WRAM according to the distribution +; of DuelDataToSave. assumes saved data exists and that the checksum is valid. +LoadSavedDuelData: + call EnableSRAM + inc de + inc de + inc de + inc de + ld hl, DuelDataToSave +.next_block + ld c, [hl] + inc hl + ld b, [hl] + inc hl + ld a, c + or b + jr z, .done + push hl + push bc + ld c, [hl] + inc hl + ld b, [hl] + inc hl + pop hl +.copy_loop + ld a, [de] + inc de + ld [hli], a + dec bc + ld a, c + or b + jr nz, .copy_loop + pop hl + inc hl + inc hl + jr .next_block +.done + call DisableSRAM + ret + +DuelDataToSave: +; dw address, number of bytes to copy + dw wPlayerDuelVariables, wOpponentDuelVariables - wPlayerDuelVariables + dw wOpponentDuelVariables, wPlayerDeck - wOpponentDuelVariables + dw wPlayerDeck, wDuelTempList - wPlayerDeck + dw wWhoseTurn, wDuelTheme + $1 - wWhoseTurn + dw hWhoseTurn, $1 + dw wRNG1, wRNGCounter + $1 - wRNG1 + dw wAIDuelVars, wAIDuelVarsEnd - wAIDuelVars + dw NULL + +; return carry if there is no data saved at sCurrentDuel or if the checksum isn't correct, +; or if the value saved from wDuelType is DUELTYPE_LINK +ValidateSavedNonLinkDuelData: + call EnableSRAM + ld hl, sCurrentDuel + ld a, [sCurrentDuelData] + call DisableSRAM + cp DUELTYPE_LINK + jr nz, ValidateSavedDuelData + ; ignore any saved data of a link duel + scf + ret + +; return carry if there is no data saved at sCurrentDuel or if the checksum isn't correct +; input: hl = sCurrentDuel +ValidateSavedDuelData: + call EnableSRAM + push de + ld a, [hli] + or a + jr z, .no_saved_data + lb de, $23, $45 + ld bc, $334 + ld a, [hl] + sub e + ld e, a + inc hl + ld a, [hl] + xor d + ld d, a + inc hl + inc hl +.loop + ld a, [hl] + add e + ld e, a + ld a, [hli] + xor d + ld d, a + dec bc + ld a, c + or b + jr nz, .loop + ld a, e + or d + jr z, .ok +.no_saved_data + scf +.ok + call DisableSRAM + pop de + ret + +; discard data of a duel that was saved by SaveDuelData, by setting the first byte +; of sCurrentDuel to $00, and zeroing the checksum (next two bytes) +DiscardSavedDuelData: + call EnableSRAM + ld hl, sCurrentDuel + xor a + ld [hli], a + ld [hli], a + ld [hl], a + call DisableSRAM + ret + +; loads a player deck (sDeck*Cards) from SRAM to wPlayerDeck +; sCurrentlySelectedDeck determines which sDeck*Cards source (0-3) +LoadPlayerDeck: + call EnableSRAM + ld a, [sCurrentlySelectedDeck] + ld l, a + ld h, sDeck2Cards - sDeck1Cards + call HtimesL + ld de, sDeck1Cards + add hl, de + ld de, wPlayerDeck + ld c, DECK_SIZE +.copy_cards_loop + ld a, [hli] + ld [de], a + inc de + dec c + jr nz, .copy_cards_loop + call DisableSRAM + ret + +; returns carry if wSkipDelayAllowed is non-0 and B is being held in order to branch +; out of the caller's wait frames loop. probably only used for debugging. +CheckSkipDelayAllowed: + ld a, [wSkipDelayAllowed] + or a + ret z + ldh a, [hKeysHeld] + and B_BUTTON + ret z + scf + ret + +; related to AI taking their turn in a duel +; called multiple times during one AI turn +; each call results in the execution of an OppActionTable function +AIMakeDecision: + ldh [hOppActionTableIndex], a + ld hl, wSkipDuelistIsThinkingDelay + ld a, [hl] + ld [hl], $0 + or a + jr nz, .skip_delay +.delay_loop + call DoFrame + ld a, [wVBlankCounter] + cp 60 + jr c, .delay_loop + +.skip_delay + ldh a, [hOppActionTableIndex] + ld hl, wOpponentTurnEnded + ld [hl], 0 + ld hl, OppActionTable + call JumpToFunctionInTable + ld a, [wDuelFinished] + ld hl, wOpponentTurnEnded + or [hl] + jr nz, .turn_ended + ld a, [wSkipDuelistIsThinkingDelay] + or a + ret nz + ld [wVBlankCounter], a + ldtx hl, DuelistIsThinkingText + call DrawWideTextBox_PrintTextNoDelay + or a + ret + +.turn_ended + scf + ret + +Func_67fb: + ld a, 10 +.delay_loop + call DoFrame + dec a + jr nz, .delay_loop + ld [wCurrentDuelMenuItem], a ; 0 +.asm_6806 + ld a, PLAYER_TURN + ldh [hWhoseTurn], a + ldtx hl, WaitingHandExamineText + call DrawWideTextBox_PrintTextNoDelay + call Func_6850 +.asm_6813 + call DoFrame + call Func_683e + call RefreshMenuCursor + ldh a, [hKeysPressed] + bit 0, a + jr nz, .asm_682e + ld a, $01 + call Func_6862 + jr nc, .asm_6813 +.asm_6829 + call DrawDuelMainScene + jr .asm_6806 +.asm_682e + ld a, [wCurrentDuelMenuItem] + or a + jr z, .asm_6839 + call OpenDuelCheckMenu + jr .asm_6829 +.asm_6839 + call OpenTurnHolderHandScreen_Simple + jr .asm_6829 + +Func_683e: + ldh a, [hDPadHeld] + bit 1, a + ret nz + and D_LEFT | D_RIGHT + ret z + call EraseCursor + ld hl, wCurrentDuelMenuItem + ld a, [hl] + xor $01 + ld [hl], a +; fallthrough + +Func_6850: + ld d, 2 + ld a, [wCurrentDuelMenuItem] + or a + jr z, .set_cursor_params + ld d, 8 +.set_cursor_params + ld e, 16 + lb bc, SYM_CURSOR_R, SYM_SPACE + jp SetCursorParametersForTextBox + +Func_6862: + ld [wcbff], a + ldh a, [hKeysPressed] + bit START_F, a + jr nz, .start_pressed + bit SELECT_F, a + jr nz, .select_pressed + ldh a, [hKeysHeld] + and B_BUTTON + ret z + ldh a, [hKeysPressed] + bit D_DOWN_F, a + jr nz, .down_pressed + bit D_LEFT_F, a + jr nz, .left_pressed + bit D_UP_F, a + jr nz, .up_pressed + bit D_RIGHT_F, a + jr nz, .right_pressed + or a + ret +.start_pressed + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + cp -1 + jr z, .return_carry + call GetCardIDFromDeckIndex + call LoadCardDataToBuffer1_FromCardID + ld hl, wCurPlayAreaSlot + xor a + ld [hli], a + ld [hl], a ; wCurPlayAreaY + call OpenCardPage_FromCheckPlayArea +.return_carry + scf + ret +.select_pressed + ld a, [wcbff] + or a + jr nz, .asm_68ad + call OpenInPlayAreaScreen_FromSelectButton + jr .return_carry +.asm_68ad + call Func_4597 + jr .return_carry +.down_pressed + call OpenTurnHolderPlayAreaScreen + jr .return_carry +.left_pressed + call OpenTurnHolderDiscardPileScreen + jr .return_carry +.up_pressed + call OpenNonTurnHolderPlayAreaScreen + jr .return_carry +.right_pressed + call OpenNonTurnHolderDiscardPileScreen + jr .return_carry + +Func_68c6: + call Func_3b31 + ld hl, sp+$00 + ld a, l + ld [wcbf7], a + ld a, h + ld [wcbf7 + 1], a + ld de, Func_0f1d + ld hl, wDoFrameFunction + ld [hl], e + inc hl + ld [hl], d + ret + +ResetDoFrameFunction_Bank1: + xor a + ld hl, wDoFrameFunction + ld [hli], a + ld [hl], a + ret + +; print the AttachedEnergyToPokemonText, given the energy card to attach in hTempCardIndex_ff98, +; and the PLAY_AREA_* of the turn holder's Pokemon to attach the energy to in hTempPlayAreaLocation_ff9d +PrintAttachedEnergyToPokemon: + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardNameToTxRam2_b + ldh a, [hTempCardIndex_ff98] + call LoadCardNameToTxRam2 + ldtx hl, AttachedEnergyToPokemonText + call DrawWideTextBox_WaitForInput + ret + +; print the PokemonEvolvedIntoPokemonText, given the Pokemon card to evolve in wPreEvolutionPokemonCard, +; and the evolved Pokemon card in hTempCardIndex_ff98. also play a sound effect. +PrintPokemonEvolvedIntoPokemon: + ld a, SFX_5E + call PlaySFX + ld a, [wPreEvolutionPokemonCard] + call LoadCardNameToTxRam2 + ldh a, [hTempCardIndex_ff98] + call LoadCardNameToTxRam2_b + ldtx hl, PokemonEvolvedIntoPokemonText + call DrawWideTextBox_WaitForInput + ret + +; handle the opponent's turn in a link duel +; loop until either [wOpponentTurnEnded] or [wDuelFinished] is non-0 +DoLinkOpponentTurn: + xor a + ld [wOpponentTurnEnded], a + xor a + ld [wSkipDuelistIsThinkingDelay], a +.link_opp_turn_loop + ld a, [wSkipDuelistIsThinkingDelay] + or a + jr nz, .asm_6932 + call Func_68c6 + call Func_67fb + ld a, [wDuelDisplayedScreen] + cp CHECK_PLAY_AREA + jr nz, .asm_6932 + lb de, $38, $9f + call SetupText +.asm_6932 + call ResetDoFrameFunction_Bank1 + call SerialRecvDuelData + ld a, OPPONENT_TURN + ldh [hWhoseTurn], a + ld a, [wSerialFlags] + or a + jp nz, DuelTransmissionError + xor a + ld [wSkipDuelistIsThinkingDelay], a + ldh a, [hOppActionTableIndex] + cp $17 + jp nc, DuelTransmissionError + ld hl, OppActionTable + call JumpToFunctionInTable + ld hl, wOpponentTurnEnded + ld a, [wDuelFinished] + or [hl] + jr z, .link_opp_turn_loop + ret + +; actions for the opponent's turn +; on a link duel, this is referenced by DoLinkOpponentTurn in a loop (on each opponent's HandleTurn) +; on a non-link duel (vs AI opponent), this is referenced by AIMakeDecision +OppActionTable: + dw DuelTransmissionError + dw OppAction_PlayBasicPokemonCard + dw OppAction_EvolvePokemonCard + dw OppAction_PlayEnergyCard + dw OppAction_AttemptRetreat + dw OppAction_FinishTurnWithoutAttacking + dw OppAction_PlayTrainerCard + dw OppAction_ExecuteTrainerCardEffectCommands + dw OppAction_BeginUseAttack + dw OppAction_UseAttack + dw OppAction_PlayAttackAnimationDealAttackDamage + dw OppAction_DrawCard + dw OppAction_UsePokemonPower + dw OppAction_ExecutePokemonPowerEffect + dw OppAction_ForceSwitchActive + dw OppAction_NoAction + dw OppAction_NoAction + dw OppAction_TossCoinATimes + dw OppAction_6b30 + dw OppAction_NoAction + dw OppAction_UseMetronomeAttack + dw OppAction_6b15 + dw OppAction_DrawDuelMainScene + +OppAction_DrawCard: + call DrawCardFromDeck + call nc, AddCardToHand + ret + +OppAction_FinishTurnWithoutAttacking: + call DrawDuelMainScene + call ClearNonTurnTemporaryDuelvars + ldtx hl, FinishedTurnWithoutAttackingText + call DrawWideTextBox_WaitForInput + ld a, 1 + ld [wOpponentTurnEnded], a + ret + +; attach an energy card from hand to the arena or a benched Pokemon +OppAction_PlayEnergyCard: + ldh a, [hTempPlayAreaLocation_ffa1] + ldh [hTempPlayAreaLocation_ff9d], a + ld e, a + ldh a, [hTemp_ffa0] + ldh [hTempCardIndex_ff98], a + call PutHandCardInPlayArea + ldh a, [hTemp_ffa0] + call LoadCardDataToBuffer1_FromDeckIndex + call DrawLargePictureOfCard + call PrintAttachedEnergyToPokemon + ld a, 1 + ld [wAlreadyPlayedEnergy], a + call DrawDuelMainScene + ret + +; evolve a Pokemon card in the arena or in the bench +OppAction_EvolvePokemonCard: + ldh a, [hTempPlayAreaLocation_ffa1] + ldh [hTempPlayAreaLocation_ff9d], a + ldh a, [hTemp_ffa0] + ldh [hTempCardIndex_ff98], a + call LoadCardDataToBuffer1_FromDeckIndex + call DrawLargePictureOfCard + call EvolvePokemonCardIfPossible + call PrintPokemonEvolvedIntoPokemon + call Func_161e + call DrawDuelMainScene + ret + +; place a basic Pokemon card from hand in the bench +OppAction_PlayBasicPokemonCard: + ldh a, [hTemp_ffa0] + ldh [hTempCardIndex_ff98], a + call PutHandPokemonCardInPlayArea + ldh [hTempPlayAreaLocation_ff9d], a + add DUELVARS_ARENA_CARD_STAGE + call GetTurnDuelistVariable + ld [hl], 0 + ldh a, [hTemp_ffa0] + ldtx hl, PlacedOnTheBenchText + call DisplayCardDetailScreen + call Func_161e + call DrawDuelMainScene + ret + +; attempt the retreat of the active Pokemon card +; if successful, discard the required energy cards for retreat and +; swap the retreated card with a Pokemon card from the bench +OppAction_AttemptRetreat: + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + push af + call AttemptRetreat + ldtx hl, RetreatWasUnsuccessfulText + jr c, .failed + xor a + ld [wDuelDisplayedScreen], a + ldtx hl, RetreatedToTheBenchText +.failed + push hl + call DrawDuelMainScene + pop hl + pop af + push hl + call LoadCardNameToTxRam2 + pop hl + call DrawWideTextBox_WaitForInput_Bank1 + ret + +; play trainer card from hand +OppAction_PlayTrainerCard: + call LoadNonPokemonCardEffectCommands + call DisplayUsedTrainerCardDetailScreen + call PrintUsedTrainerCardDescription + call ExchangeRNG + ld a, $01 + ld [wSkipDuelistIsThinkingDelay], a + ret + +; execute the effect commands of the trainer card that is being played +; used only for Trainer cards, as a continuation of OppAction_PlayTrainerCard +OppAction_ExecuteTrainerCardEffectCommands: + ld a, EFFECTCMDTYPE_DISCARD_ENERGY + call TryExecuteEffectCommandFunction + ld a, EFFECTCMDTYPE_BEFORE_DAMAGE + call TryExecuteEffectCommandFunction + call DrawDuelMainScene + ldh a, [hTempCardIndex_ff9f] + call MoveHandCardToDiscardPile + call ExchangeRNG + call DrawDuelMainScene + ret + +; begin the execution of an attack and handle the attack being +; possibly unsuccessful due to Sand Attack or Smokescreen +OppAction_BeginUseAttack: + ldh a, [hTempCardIndex_ff9f] + ld d, a + ldh a, [hTemp_ffa0] + ld e, a + call CopyAttackDataAndDamage_FromDeckIndex + call Func_16f6 + ld a, $01 + ld [wSkipDuelistIsThinkingDelay], a + call CheckSandAttackOrSmokescreenSubstatus + jr c, .has_status + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + and CNF_SLP_PRZ + cp CONFUSED + jr z, .has_status + call ExchangeRNG + ret + +; we make it here is attacker is affected by +; Sand Attack, Smokescreen, or confusion +.has_status + call DrawDuelMainScene + call PrintPokemonsAttackText + call WaitForWideTextBoxInput + call ExchangeRNG + call HandleSandAttackOrSmokescreenSubstatus + ret nc ; return if attack is successful (won the coin toss) + call ClearNonTurnTemporaryDuelvars + ; end the turn if the attack fails + ld a, 1 + ld [wOpponentTurnEnded], a + ret + +; display the attack used by the opponent, and handle +; EFFECTCMDTYPE_DISCARD_ENERGY and confusion damage to self +OppAction_UseAttack: + ld a, EFFECTCMDTYPE_DISCARD_ENERGY + call TryExecuteEffectCommandFunction + call CheckSelfConfusionDamage + jr c, .confusion_damage + call DisplayOpponentUsedAttackScreen + call PrintPokemonsAttackText + call WaitForWideTextBoxInput + call ExchangeRNG + ld a, $01 + ld [wSkipDuelistIsThinkingDelay], a + ret +.confusion_damage + call HandleConfusionDamageToSelf + ; end the turn if dealing damage to self due to confusion + ld a, 1 + ld [wOpponentTurnEnded], a + ret + +OppAction_PlayAttackAnimationDealAttackDamage: + call PlayAttackAnimation_DealAttackDamage + ld a, 1 + ld [wOpponentTurnEnded], a + ret + +; force the player to switch the active Pokemon with a benched Pokemon +OppAction_ForceSwitchActive: + ldtx hl, SelectPkmnOnBenchToSwitchWithActiveText + call DrawWideTextBox_WaitForInput + call SwapTurn + call HasAlivePokemonInBench + ld a, $01 + ld [wcbd4], a +.force_selection + call OpenPlayAreaScreenForSelection + jr c, .force_selection + call SwapTurn + ldh a, [hTempPlayAreaLocation_ff9d] + call SerialSendByte + ret + +OppAction_UsePokemonPower: + ldh a, [hTempCardIndex_ff9f] + ld d, a + ld e, $00 + call CopyAttackDataAndDamage_FromDeckIndex + ldh a, [hTemp_ffa0] + ldh [hTempPlayAreaLocation_ff9d], a + call DisplayUsePokemonPowerScreen + ldh a, [hTempCardIndex_ff9f] + call LoadCardNameToTxRam2 + ld hl, wLoadedAttackName + ld a, [hli] + ld [wTxRam2_b], a + ld a, [hl] + ld [wTxRam2_b + 1], a + ldtx hl, WillUseThePokemonPowerText + call DrawWideTextBox_WaitForInput_Bank1 + call ExchangeRNG + ld a, $01 + ld [wSkipDuelistIsThinkingDelay], a + ret + +; execute the EFFECTCMDTYPE_BEFORE_DAMAGE command of the used Pokemon Power +OppAction_ExecutePokemonPowerEffect: + call Func_7415 + ld a, EFFECTCMDTYPE_BEFORE_DAMAGE + call TryExecuteEffectCommandFunction + ld a, $01 + ld [wSkipDuelistIsThinkingDelay], a + ret + +; execute the EFFECTCMDTYPE_AFTER_DAMAGE command of the used Pokemon Power +OppAction_6b15: + ld a, EFFECTCMDTYPE_AFTER_DAMAGE + call TryExecuteEffectCommandFunction + ld a, $01 + ld [wSkipDuelistIsThinkingDelay], a + ret + +OppAction_DrawDuelMainScene: + call DrawDuelMainScene + ret + +OppAction_TossCoinATimes: + call SerialRecv8Bytes + call TossCoinATimes + ld a, $01 + ld [wSkipDuelistIsThinkingDelay], a + ret + +OppAction_6b30: + ldh a, [hWhoseTurn] + push af + ldh a, [hTemp_ffa0] + ldh [hWhoseTurn], a + call Func_4f2d + pop af + ldh [hWhoseTurn], a + ret + +OppAction_UseMetronomeAttack: + call DrawDuelMainScene + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + and CNF_SLP_PRZ + cp CONFUSED + jr z, .asm_6b56 + call PrintPokemonsAttackText + call .asm_6b56 + call WaitForWideTextBoxInput + ret +.asm_6b56 + call SerialRecv8Bytes + push bc + call SwapTurn + call CopyAttackDataAndDamage_FromDeckIndex + call SwapTurn + ldh a, [hTempCardIndex_ff9f] + ld [wPlayerAttackingCardIndex], a + ld a, [wSelectedAttack] + ld [wPlayerAttackingAttackIndex], a + ld a, [wTempCardID_ccc2] + ld [wPlayerAttackingCardID], a + call Func_16f6 + pop bc + ld a, c + ld [wMetronomeEnergyCost], a + ret + +OppAction_NoAction: + ret + +; load the text ID of the card name with deck index given in a to TxRam2 +; also loads the card to wLoadedCard1 +LoadCardNameToTxRam2: + call LoadCardDataToBuffer1_FromDeckIndex + ld a, [wLoadedCard1Name] + ld [wTxRam2], a + ld a, [wLoadedCard1Name + 1] + ld [wTxRam2 + 1], a + ret + +; load the text ID of the card name with deck index given in a to TxRam2_b +; also loads the card to wLoadedCard1 +LoadCardNameToTxRam2_b: + call LoadCardDataToBuffer1_FromDeckIndex + ld a, [wLoadedCard1Name] + ld [wTxRam2_b], a + ld a, [wLoadedCard1Name + 1] + ld [wTxRam2_b + 1], a + ret + +DrawWideTextBox_WaitForInput_Bank1: + call DrawWideTextBox_WaitForInput + ret + +Func_6ba2: + call DrawWideTextBox_PrintText + ld a, [wDuelistType] + cp DUELIST_TYPE_LINK_OPP + ret z + call WaitForWideTextBoxInput + ret + +; apply and/or refresh status conditions and other events that trigger between turns +HandleBetweenTurnsEvents: + call IsArenaPokemonAsleepOrPoisoned + jr c, .something_to_handle + cp PARALYZED + jr z, .something_to_handle + call SwapTurn + call IsArenaPokemonAsleepOrPoisoned + call SwapTurn + jr c, .something_to_handle + call DiscardAttachedPluspowers + call SwapTurn + call DiscardAttachedDefenders + call SwapTurn + ret + +.something_to_handle + ; either: + ; 1. turn holder's arena Pokemon is paralyzed, asleep, poisoned or double poisoned + ; 2. non-turn holder's arena Pokemon is asleep, poisoned or double poisoned + call Func_3b21 + call ZeroObjectPositionsAndToggleOAMCopy + call EmptyScreen + ld a, BOXMSG_BETWEEN_TURNS + call DrawDuelBoxMessage + ldtx hl, BetweenTurnsText + call DrawWideTextBox_WaitForInput + + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + ld [wTempNonTurnDuelistCardID], a + ld l, DUELVARS_ARENA_CARD_STATUS + ld a, [hl] + or a + jr z, .discard_pluspower + ; has status condition + call HandlePoisonDamage + jr c, .discard_pluspower + call HandleSleepCheck + ld a, [hl] + and CNF_SLP_PRZ + cp PARALYZED + jr nz, .discard_pluspower + ; heal paralysis + ld a, DOUBLE_POISONED + and [hl] + ld [hl], a + call Func_6c7e + ldtx hl, IsCuredOfParalysisText + call Func_6ce4 + ld a, DUEL_ANIM_HEAL + call Func_6cab + call WaitForWideTextBoxInput + +.discard_pluspower + call DiscardAttachedPluspowers + call SwapTurn + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call GetCardIDFromDeckIndex + ld a, e + ld [wTempNonTurnDuelistCardID], a + ld l, DUELVARS_ARENA_CARD_STATUS + ld a, [hl] + or a + jr z, .asm_6c3a + call HandlePoisonDamage + jr c, .asm_6c3a + call HandleSleepCheck +.asm_6c3a + call DiscardAttachedDefenders + call SwapTurn + call Func_6e4c + ret + +; discard any PLUSPOWER attached to the turn holder's arena and/or bench Pokemon +DiscardAttachedPluspowers: + ld a, DUELVARS_ARENA_CARD_ATTACHED_PLUSPOWER + call GetTurnDuelistVariable + ld e, MAX_PLAY_AREA_POKEMON + xor a +.unattach_pluspower_loop + ld [hli], a + dec e + jr nz, .unattach_pluspower_loop + ld de, PLUSPOWER + jp MoveCardToDiscardPileIfInArena + +; discard any DEFENDER attached to the turn holder's arena and/or bench Pokemon +DiscardAttachedDefenders: + ld a, DUELVARS_ARENA_CARD_ATTACHED_DEFENDER + call GetTurnDuelistVariable + ld e, MAX_PLAY_AREA_POKEMON + xor a +.unattach_defender_loop + ld [hli], a + dec e + jr nz, .unattach_defender_loop + ld de, DEFENDER + jp MoveCardToDiscardPileIfInArena + +; return carry if the turn holder's arena Pokemon card is asleep, poisoned, or double poisoned. +; also, if confused, paralyzed, or asleep, return the status condition in a. +IsArenaPokemonAsleepOrPoisoned: + ld a, DUELVARS_ARENA_CARD_STATUS + call GetTurnDuelistVariable + or a + ret z + ; note that POISONED | DOUBLE_POISONED is the same as just DOUBLE_POISONED ($c0) + ; poison status masking is normally done with PSN_DBLPSN ($f0) + and POISONED | DOUBLE_POISONED + jr nz, .set_carry + ld a, [hl] + and CNF_SLP_PRZ + cp ASLEEP + jr z, .set_carry + or a + ret +.set_carry + scf + ret + +Func_6c7e: + ld a, [wDuelDisplayedScreen] + cp DUEL_MAIN_SCENE + jr z, .asm_6c98 + ld hl, wWhoseTurn + ldh a, [hWhoseTurn] + cp [hl] + jp z, DrawDuelMainScene + call SwapTurn + call DrawDuelMainScene + call SwapTurn + ret + +.asm_6c98 + ld hl, wWhoseTurn + ldh a, [hWhoseTurn] + cp [hl] + jp z, DrawDuelHUDs + call SwapTurn + call DrawDuelHUDs + call SwapTurn + ret + +; input: +; a = animation ID +Func_6cab: + push af + ld a, [wDuelType] + or a + jr nz, .store_duelist_turn + ld a, [wWhoseTurn] + cp PLAYER_TURN + jr z, .store_duelist_turn + call SwapTurn + ldh a, [hWhoseTurn] + ld [wDuelAnimDuelistSide], a + call SwapTurn + jr .asm_6ccb + +.store_duelist_turn + ldh a, [hWhoseTurn] + ld [wDuelAnimDuelistSide], a + +.asm_6ccb + xor a + ld [wDuelAnimLocationParam], a + ld a, DUEL_ANIM_SCREEN_MAIN_SCENE + ld [wDuelAnimationScreen], a + pop af + +; play animation + call PlayDuelAnimation +.loop_anim + call DoFrame + call CheckAnyAnimationPlaying + jr c, .loop_anim + call Func_6c7e.asm_6c98 + ret + +; prints the name of the card at wTempNonTurnDuelistCardID in a text box +Func_6ce4: + push hl + ld a, [wTempNonTurnDuelistCardID] + ld e, a + call LoadCardDataToBuffer1_FromCardID + ld hl, wLoadedCard1Name + ld a, [hli] + ld h, [hl] + ld l, a + call LoadTxRam2 + pop hl + call DrawWideTextBox_PrintText + ret + +; handles the sleep check for the NonTurn Duelist +; heals sleep status if coin is heads, else +; it plays sleeping animation +HandleSleepCheck: + ld a, [hl] + and CNF_SLP_PRZ + cp ASLEEP + ret nz ; quit if not asleep + + push hl + ld a, [wTempNonTurnDuelistCardID] + ld e, a + call LoadCardDataToBuffer1_FromCardID + ld a, 18 + call CopyCardNameAndLevel + ld [hl], TX_END + ld hl, wTxRam2 + xor a + ld [hli], a + ld [hl], a + ldtx de, PokemonsSleepCheckText + call TossCoin + ld a, DUEL_ANIM_SLEEP + ldtx hl, IsStillAsleepText + jr nc, .tails + +; coin toss was heads, cure sleep status + pop hl + push hl + ld a, DOUBLE_POISONED + and [hl] + ld [hl], a + ld a, DUEL_ANIM_HEAL + ldtx hl, IsCuredOfSleepText + +.tails + push af + push hl + call Func_6c7e + pop hl + call Func_6ce4 + pop af + call Func_6cab + pop hl + call WaitForWideTextBoxInput + ret + +HandlePoisonDamage: + or a + bit POISONED_F , [hl] + ret z ; quit if not poisoned + +; load damage and text according to normal/double poison + push hl + bit DOUBLE_POISONED_F, [hl] + ld a, PSN_DAMAGE + ldtx hl, Received10DamageDueToPoisonText + jr z, .not_double_poisoned + ld a, DBLPSN_DAMAGE + ldtx hl, Received20DamageDueToPoisonText + +.not_double_poisoned + push af + ld [wDuelAnimDamage], a + xor a + ld [wDuelAnimDamage + 1], a + + push hl + call Func_6c7e + pop hl + call Func_6ce4 + +; play animation + ld a, DUEL_ANIM_POISON + call Func_6cab + pop af + +; deal poison damage + ld e, a + ld d, $00 + ld a, DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + call SubtractHP + push hl + ld a, $8c + call Func_6cab + pop hl + + call PrintKnockedOutIfHLZero + push af + call WaitForWideTextBoxInput + pop af + pop hl + ret + +; given the deck index of a turn holder's card in register a, +; and a pointer in hl to the wLoadedCard* buffer where the card data is loaded, +; check if the card is Clefairy Doll or Mysterious Fossil, and, if so, convert it +; to a Pokemon card in the wLoadedCard* buffer, using .trainer_to_pkmn_data. +ConvertSpecialTrainerCardToPokemon: + ld c, a + ld a, [hl] + cp TYPE_TRAINER + ret nz ; return if the card is not TRAINER type + push hl + ldh a, [hWhoseTurn] + ld h, a + ld l, c + ld a, [hl] + and CARD_LOCATION_PLAY_AREA + pop hl + ret z ; return if the card is not in the arena or bench + ld a, e + cp MYSTERIOUS_FOSSIL + jr nz, .check_for_clefairy_doll + ld a, d + cp $00 ; MYSTERIOUS_FOSSIL >> 8 + jr z, .start_ram_data_overwrite + ret +.check_for_clefairy_doll + cp CLEFAIRY_DOLL + ret nz + ld a, d + cp $00 ; CLEFAIRY_DOLL >> 8 + ret nz +.start_ram_data_overwrite + push de + ld [hl], TYPE_PKMN_COLORLESS + ld bc, CARD_DATA_HP + add hl, bc + ld de, .trainer_to_pkmn_data + ld c, CARD_DATA_UNKNOWN2 - CARD_DATA_HP +.loop + ld a, [de] + inc de + ld [hli], a + dec c + jr nz, .loop + pop de + ret + +.trainer_to_pkmn_data + db 10 ; CARD_DATA_HP + ds $07 ; CARD_DATA_ATTACK1_NAME - (CARD_DATA_HP + 1) + tx DiscardName ; CARD_DATA_ATTACK1_NAME + tx DiscardDescription ; CARD_DATA_ATTACK1_DESCRIPTION + ds $03 ; CARD_DATA_ATTACK1_CATEGORY - (CARD_DATA_ATTACK1_DESCRIPTION + 2) + db POKEMON_POWER ; CARD_DATA_ATTACK1_CATEGORY + dw TrainerCardAsPokemonEffectCommands ; CARD_DATA_ATTACK1_EFFECT_COMMANDS + ds $18 ; CARD_DATA_RETREAT_COST - (CARD_DATA_ATTACK1_EFFECT_COMMANDS + 2) + db UNABLE_RETREAT ; CARD_DATA_RETREAT_COST + ds $0d ; PKMN_CARD_DATA_LENGTH - (CARD_DATA_RETREAT_COST + 1) + +; this function applies status conditions to the defending Pokemon, +; returned by the effect functions in wEffectFunctionsFeedback +Func_6df1: + xor a + ld [wPlayerArenaCardLastTurnStatus], a + ld [wOpponentArenaCardLastTurnStatus], a + ld hl, wEffectFunctionsFeedbackIndex + ld a, [hl] + or a + ret z + ld e, [hl] + ld d, $00 + ld hl, wEffectFunctionsFeedback + add hl, de + ld [hl], $00 + call CheckNoDamageOrEffect + jr c, .no_damage_or_effect + ld hl, wEffectFunctionsFeedback +.apply_status_loop + ld a, [hli] + or a + jr z, .done + ld d, a + call ApplyStatusConditionToArenaPokemon + jr .apply_status_loop +.done + scf + ret +.no_damage_or_effect + ld a, l + or h + call nz, DrawWideTextBox_PrintText + ld hl, wEffectFunctionsFeedback +.asm_6e23 + ld a, [hli] + or a + jr z, .asm_6e37 + ld d, a + ld a, [wWhoseTurn] + cp d + jr z, .asm_6e32 + inc hl + inc hl + jr .asm_6e23 +.asm_6e32 + call ApplyStatusConditionToArenaPokemon + jr .asm_6e23 +.asm_6e37 + ret + +; apply the status condition at hl+1 to the arena Pokemon +; discard the arena Pokemon's status conditions contained in the bitmask at hl +ApplyStatusConditionToArenaPokemon: + ld e, DUELVARS_ARENA_CARD_STATUS + ld a, [de] + and [hl] + inc hl + or [hl] + ld [de], a + dec hl + ld e, DUELVARS_ARENA_CARD_LAST_TURN_STATUS + ld a, [de] + and [hl] + inc hl + or [hl] + inc hl + ld [de], a + ret + +Func_6e49: + call HandleDestinyBondSubstatus + ; fallthrough + +Func_6e4c: + call ClearDamageReductionSubstatus2OfKnockedOutPokemon + xor a + ld [wcce8], a + call SwapTurn + call Func_6ef6 + call SwapTurn + ld a, [wcce8] + or a + jr z, .asm_6e86 + call Func_6ff7 + jr c, .asm_6e86 + call CountKnockedOutPokemon + ld c, a + call SwapTurn + call CountPrizes + call SwapTurn + dec a + cp c + jr c, .asm_6e86 + ld a, c + call SwapTurn + call TakeAPrizes + call SwapTurn + ld a, $01 + jr .asm_6ecc +.asm_6e86 + call Func_6ef6 + ld a, [wcce8] + cp $01 + jr nz, .asm_6e9f + call SwapTurn + call Func_6ff7 + call SwapTurn + jr c, .asm_6e9f + ld a, $02 + jr .asm_6ecc +.asm_6e9f + call SwapTurn + call Func_6eff + call SwapTurn + call Func_6eff + ld a, [wcce8] + or a + jr nz, .asm_6ec4 + xor a +.asm_6eb2 + push af + call MoveAllTurnHolderKnockedOutPokemonToDiscardPile + call SwapTurn + call MoveAllTurnHolderKnockedOutPokemonToDiscardPile + call SwapTurn + call ShiftAllPokemonToFirstPlayAreaSlots + pop af + ret +.asm_6ec4 + ld e, a + ld d, $00 + ld hl, Data_6ed2 + add hl, de + ld a, [hl] +.asm_6ecc + ld [wDuelFinished], a + scf + jr .asm_6eb2 + +Data_6ed2: + db DUEL_NOT_FINISHED, TURN_PLAYER_LOST, TURN_PLAYER_WON, TURN_PLAYER_TIED + db TURN_PLAYER_LOST, TURN_PLAYER_LOST, TURN_PLAYER_TIED, TURN_PLAYER_LOST + db TURN_PLAYER_WON, TURN_PLAYER_TIED, TURN_PLAYER_WON, TURN_PLAYER_WON + db TURN_PLAYER_TIED, TURN_PLAYER_LOST, TURN_PLAYER_WON, TURN_PLAYER_TIED + +; clears SUBSTATUS2_REDUCE_BY_20, SUBSTATUS2_POUNCE, SUBSTATUS2_GROWL, +; SUBSTATUS2_TAIL_WAG, and SUBSTATUS2_LEER for each arena Pokemon with 0 HP +ClearDamageReductionSubstatus2OfKnockedOutPokemon: + call SwapTurn + call .clear + call SwapTurn +.clear + ld a, DUELVARS_ARENA_CARD_HP + call GetNonTurnDuelistVariable + or a + ret nz + call ClearDamageReductionSubstatus2 + ret + +Func_6ef6: + call Func_6fa5 + ld hl, wcce8 + rl [hl] + ret + +Func_6eff: + call ReplaceKnockedOutPokemon + ld hl, wcce8 + rl [hl] + ret + +; for each Pokemon in the turn holder's play area (arena and bench), +; move that card to the discard pile if its HP is 0 +MoveAllTurnHolderKnockedOutPokemonToDiscardPile: + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld d, a + ld l, DUELVARS_ARENA_CARD_HP + ld e, PLAY_AREA_ARENA +.loop + ld a, [hl] + or a + jr nz, .next + push hl + push de + call MovePlayAreaCardToDiscardPile + pop de + pop hl +.next + inc hl + inc e + dec d + jr nz, .loop + ret + +; have the turn holder replace the arena Pokemon card when it's been knocked out. +; if there are no Pokemon cards in the turn holder's bench, return carry. +ReplaceKnockedOutPokemon: + ld a, DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + or a + ret nz + call ClearAllStatusConditions + call HasAlivePokemonInBench + jr nc, .can_replace_pokemon + +; if we made it here, the duelist can't replace the knocked out Pokemon + bank1call DrawDuelMainScene + ldtx hl, ThereAreNoPokemonInPlayAreaText + call DrawWideTextBox_WaitForInput + call ExchangeRNG + scf + ret + +.can_replace_pokemon + ld a, DUELVARS_DUELIST_TYPE + call GetTurnDuelistVariable + cp DUELIST_TYPE_PLAYER + jr nz, .opponent + +; prompt the player to replace the knocked out Pokemon with one from bench + bank1call DrawDuelMainScene + ldtx hl, SelectPokemonToPlaceInTheArenaText + call DrawWideTextBox_WaitForInput + ld a, $01 + ld [wcbd4], a + ld a, PRACTICEDUEL_PLAY_STARYU_FROM_BENCH + call DoPracticeDuelAction +.select_pokemon + call OpenPlayAreaScreenForSelection + jr c, .select_pokemon + ldh a, [hTempPlayAreaLocation_ff9d] + call SerialSend8Bytes + +; replace the arena Pokemon with the one at location [hTempPlayAreaLocation_ff9d] +.replace_pokemon + call Func_3b31 + ld a, PRACTICEDUEL_REPLACE_KNOCKED_OUT_POKEMON + call DoPracticeDuelAction + jr c, .select_pokemon + ldh a, [hTempPlayAreaLocation_ff9d] + ld d, a + ld e, PLAY_AREA_ARENA + call SwapPlayAreaPokemon + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ldtx hl, DuelistPlacedACardText + bank1call DisplayCardDetailScreen + call ExchangeRNG + or a + ret + +; the AI opponent replaces the knocked out Pokemon with one from bench +.opponent + cp DUELIST_TYPE_LINK_OPP + jr z, .link_opponent + call AIDoAction_KOSwitch + ldh a, [hTemp_ffa0] + ldh [hTempPlayAreaLocation_ff9d], a + jr .replace_pokemon + +; wait for link opponent to replace the knocked out Pokemon with one from bench +.link_opponent + bank1call DrawDuelMainScene + ldtx hl, DuelistIsSelectingPokemonToPlaceInArenaText + call DrawWideTextBox_PrintText + call SerialRecv8Bytes + ldh [hTempPlayAreaLocation_ff9d], a + jr .replace_pokemon + +Func_6fa5: + call CountKnockedOutPokemon + ret nc + ; at least one Pokemon knocked out + call SwapTurn + bank1call Func_5805 + call SwapTurn + ret nc + call SwapTurn + bank1call DrawDuelMainScene + ldtx hl, TookAllThePrizesText + call DrawWideTextBox_WaitForInput + call ExchangeRNG + call SwapTurn + scf + ret + +; return in wNumberPrizeCardsToTake the amount of Pokemon in the turn holder's +; play area that are still there despite having 0 HP. +; that is, the number of Pokemon that have just been knocked out. +; Clefairy Doll and Mysterious Fossil don't count. +CountKnockedOutPokemon: + ld a, DUELVARS_ARENA_CARD_HP + call GetTurnDuelistVariable + ld d, h + ld e, DUELVARS_ARENA_CARD + ld b, PLAY_AREA_ARENA + ld c, MAX_PLAY_AREA_POKEMON +.loop + ld a, [de] + cp -1 + jr z, .next ; jump if no Pokemon in this location + ld a, [hl] + or a + jr nz, .next ; jump if this Pokemon's HP isn't 0 + ; this Pokemon's HP has just become 0 + ld a, [de] + push de + call GetCardIDFromDeckIndex + call GetCardType + pop de + cp TYPE_TRAINER + jr z, .next ; jump if this is a trainer card (Clefairy Doll or Mysterious Fossil) + inc b +.next + inc hl + inc de + dec c + jr nz, .loop + ld a, b + ld [wNumberPrizeCardsToTake], a + or a + ret z + scf + ret + +Func_6ff7: + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld c, a + ld l, DUELVARS_ARENA_CARD_HP +.loop + ld a, [hli] + or a + jr nz, .non_zero_hp + dec c + jr nz, .loop + scf + ret +.non_zero_hp + or a + ret + +; print one of the "There was no effect from" texts depending +; on the value at wNoEffectFromWhichStatus (NO_STATUS or a status condition constant) +PrintThereWasNoEffectFromStatusText: + ld a, [wNoEffectFromWhichStatus] + or a + jr nz, .status + ld hl, wLoadedAttackName + ld a, [hli] + ld h, [hl] + ld l, a + call LoadTxRam2 + ldtx hl, ThereWasNoEffectFromTxRam2Text + ret +.status + ld c, a + ldtx hl, ThereWasNoEffectFromPoisonConfusionText + cp POISONED | CONFUSED + ret z + and PSN_DBLPSN + jr nz, .poison + ld a, c + and CNF_SLP_PRZ + ldtx hl, ThereWasNoEffectFromParalysisText + cp PARALYZED + ret z + ldtx hl, ThereWasNoEffectFromSleepText + cp ASLEEP + ret z + ldtx hl, ThereWasNoEffectFromConfusionText + ret +.poison + ldtx hl, ThereWasNoEffectFromPoisonText + cp POISONED + ret z + ldtx hl, ThereWasNoEffectFromToxicText + ret + +; returns carry if card at hTempPlayAreaLocation_ff9d +; is a basic card. +; otherwise, lists the card indices of all stages in +; that card location, and returns the card one +; stage below. +; input: +; hTempPlayAreaLocation_ff9d = play area location to check; +; output: +; a = card index in hTempPlayAreaLocation_ff9d; +; d = card index of card one stage below; +; carry set if card is a basic card. +GetCardOneStageBelow: + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Stage] + or a + jr nz, .not_basic + scf + ret + +.not_basic + ld hl, wAllStagesIndices + ld a, $ff + ld [hli], a + ld [hli], a + ld [hl], a + +; loads deck indices of the stages present in hTempPlayAreaLocation_ff9d. +; the three stages are loaded consecutively in wAllStagesIndices. + ldh a, [hTempPlayAreaLocation_ff9d] + or CARD_LOCATION_ARENA + ld c, a + ld a, DUELVARS_CARD_LOCATIONS + call GetTurnDuelistVariable +.loop + ld a, [hl] + cp c + jr nz, .next + ld a, l + call LoadCardDataToBuffer2_FromDeckIndex + ld a, [wLoadedCard2Type] + cp TYPE_ENERGY + jr nc, .next + ld b, l + push hl + ld a, [wLoadedCard2Stage] + ld e, a + ld d, $00 + ld hl, wAllStagesIndices + add hl, de + ld [hl], b + pop hl +.next + inc l + ld a, l + cp DECK_SIZE + jr c, .loop + +; if card at hTempPlayAreaLocation_ff9d is a stage 1, load d with basic card. +; otherwise if stage 2, load d with the stage 1 card. + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD_STAGE + call GetTurnDuelistVariable + ld hl, wAllStagesIndices ; pointing to basic + cp STAGE1 + jr z, .done + ; if stage1 was skipped, hl should point to Basic stage card + cp STAGE2_WITHOUT_STAGE1 + jr z, .done + inc hl ; pointing to stage 1 +.done + ld d, [hl] + ldh a, [hTempPlayAreaLocation_ff9d] + add DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ld e, a + or a + ret + +; initializes variables when a duel begins, such as zeroing wDuelFinished or wDuelTurns, +; and setting wDuelType based on wPlayerDuelistType and wOpponentDuelistType +InitVariablesToBeginDuel: + xor a + ld [wDuelFinished], a + ld [wDuelTurns], a + ld [wcce7], a + ld a, $ff + ld [wcc0f], a + ld [wPlayerAttackingCardIndex], a + ld [wPlayerAttackingAttackIndex], a + call EnableSRAM + ld a, [sSkipDelayAllowed] + ld [wSkipDelayAllowed], a + call DisableSRAM + ld a, [wPlayerDuelistType] + cp DUELIST_TYPE_LINK_OPP + jr z, .set_duel_type + bit 7, a ; DUELIST_TYPE_AI_OPP + jr nz, .set_duel_type + ld a, [wOpponentDuelistType] + cp DUELIST_TYPE_LINK_OPP + jr z, .set_duel_type + bit 7, a ; DUELIST_TYPE_AI_OPP + jr nz, .set_duel_type + xor a +.set_duel_type + ld [wDuelType], a + ret + +; init variables that last a single player's turn +InitVariablesToBeginTurn: + xor a + ld [wAlreadyPlayedEnergy], a + ld [wGotHeadsFromConfusionCheckDuringRetreat], a + ld [wGotHeadsFromSandAttackOrSmokescreenCheck], a + ldh a, [hWhoseTurn] + ld [wWhoseTurn], a + ret + +; make all Pokemon in the turn holder's play area able to evolve. called from the +; player's second turn on, in order to allow evolution of all Pokemon already played. +SetAllPlayAreaPokemonCanEvolve: + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + ld c, a + ld l, DUELVARS_ARENA_CARD_FLAGS +.next_pkmn_loop + res 5, [hl] + set CAN_EVOLVE_THIS_TURN_F, [hl] + inc l + dec c + jr nz, .next_pkmn_loop + ret + +; initializes duel variables such as cards in deck and in hand, or Pokemon in play area +; player turn: [c200, c2ff] +; opponent turn: [c300, c3ff] +InitializeDuelVariables: + ldh a, [hWhoseTurn] + ld h, a + ld l, DUELVARS_DUELIST_TYPE + ld a, [hl] + push hl + push af + xor a + ld l, a +.zero_duel_variables_loop + ld [hl], a + inc l + jr nz, .zero_duel_variables_loop + pop af + pop hl + ld [hl], a + lb bc, DUELVARS_CARD_LOCATIONS, DECK_SIZE + ld l, DUELVARS_DECK_CARDS +.init_duel_variables_loop +; zero card locations and cards in hand, and init order of cards in deck + push hl + ld [hl], b + ld l, b + ld [hl], $0 + pop hl + inc l + inc b + dec c + jr nz, .init_duel_variables_loop + ld l, DUELVARS_ARENA_CARD + ld c, 1 + MAX_BENCH_POKEMON + 1 +.init_play_area +; initialize to $ff card in arena as well as cards in bench (plus a terminator) + ld [hl], -1 + inc l + dec c + jr nz, .init_play_area + ret + +; draw [wDuelInitialPrizes] cards from the turn holder's deck and place them as prizes: +; write their deck indexes to DUELVARS_PRIZE_CARDS, set their location to +; CARD_LOCATION_PRIZE, and set [wDuelInitialPrizes] bits of DUELVARS_PRIZES. +InitTurnDuelistPrizes: + ldh a, [hWhoseTurn] + ld d, a + ld e, DUELVARS_PRIZE_CARDS + ld a, [wDuelInitialPrizes] + ld c, a + ld b, 0 +.draw_prizes_loop + call DrawCardFromDeck + ld [de], a + inc de + ld h, d + ld l, a + ld [hl], CARD_LOCATION_PRIZE + inc b + ld a, b + cp c + jr nz, .draw_prizes_loop + push hl + ld e, c + ld d, $00 + ld hl, PrizeBitmasks + add hl, de + ld a, [hl] + pop hl + ld l, DUELVARS_PRIZES + ld [hl], a + ret + +PrizeBitmasks: + db %0, %1, %11, %111, %1111, %11111, %111111 + +; update the turn holder's DUELVARS_PRIZES following that duelist +; drawing a number of prizes equal to register a +TakeAPrizes: + or a + ret z + ld c, a + call CountPrizes + sub c + jr nc, .no_underflow + xor a +.no_underflow + ld c, a + ld b, $00 + ld hl, PrizeBitmasks + add hl, bc + ld b, [hl] + ld a, DUELVARS_PRIZES + call GetTurnDuelistVariable + ld [hl], b + ret + +; clear the non-turn holder's duelvars starting at DUELVARS_ARENA_CARD_DISABLED_ATTACK_INDEX +; these duelvars only last a two-player turn at most. +ClearNonTurnTemporaryDuelvars: + ld a, DUELVARS_ARENA_CARD_DISABLED_ATTACK_INDEX + call GetNonTurnDuelistVariable + xor a + ld [hli], a + ld [hli], a + ld [hli], a + ld [hli], a + ld [hli], a + ld [hli], a + ld [hli], a + ld [hl], a + ret + +; same as ClearNonTurnTemporaryDuelvars, except the non-turn holder's arena +; Pokemon status condition is copied to wccc5 +ClearNonTurnTemporaryDuelvars_CopyStatus: + ld a, DUELVARS_ARENA_CARD_STATUS + call GetNonTurnDuelistVariable + ld [wccc5], a + call ClearNonTurnTemporaryDuelvars + ret + +; update non-turn holder's DUELVARS_ARENA_CARD_LAST_TURN_DAMAGE +; if wccef == 0: set to [wDealtDamage] +; if wceef != 0: set to 0 +Func_7195: + ld a, DUELVARS_ARENA_CARD_LAST_TURN_DAMAGE + call GetNonTurnDuelistVariable + ld a, [wccef] + or a + jr nz, .zero + ld a, [wDealtDamage] + ld [hli], a + ld a, [wDealtDamage + 1] + ld [hl], a + ret +.zero + xor a + ld [hli], a + ld [hl], a + ret + +_TossCoin: + ld [wCoinTossTotalNum], a + ld a, [wDuelDisplayedScreen] + cp COIN_TOSS + jr z, .print_text + xor a + ld [wCoinTossNumTossed], a + call EmptyScreen + call LoadDuelCoinTossResultTiles + +.print_text +; no need to print text if this is not the first coin toss + ld a, [wCoinTossNumTossed] + or a + jr nz, .clear_text_pointer + ld a, COIN_TOSS + ld [wDuelDisplayedScreen], a + lb de, 0, 12 + lb bc, 20, 6 + ld hl, $0000 + call DrawLabeledTextBox + call EnableLCD + lb de, 1, 14 + ld a, 19 + call InitTextPrintingInTextbox + ld hl, wCoinTossScreenTextID + ld a, [hli] + ld h, [hl] + ld l, a + call PrintText + +.clear_text_pointer + ld hl, wCoinTossScreenTextID + xor a + ld [hli], a + ld [hl], a + +; store duelist type and reset number of heads + call EnableLCD + ld a, DUELVARS_DUELIST_TYPE + call GetTurnDuelistVariable + ld [wCoinTossDuelistType], a + call ExchangeRNG + xor a + ld [wCoinTossNumHeads], a + +.print_coin_tally +; skip printing text if it's only one coin toss + ld a, [wCoinTossTotalNum] + cp 2 + jr c, .asm_7223 + +; write "#coin/#total coins" + lb bc, 15, 11 + ld a, [wCoinTossNumTossed] + inc a ; current coin number is wCoinTossNumTossed + 1 + call WriteTwoDigitNumberInTxSymbolFormat + ld b, 17 + ld a, SYM_SLASH + call WriteByteToBGMap0 + inc b + ld a, [wCoinTossTotalNum] + call WriteTwoDigitNumberInTxSymbolFormat + +.asm_7223 + call Func_3b21 + ld a, DUEL_ANIM_COIN_SPIN + call PlayDuelAnimation + + ld a, [wCoinTossDuelistType] + or a + jr z, .asm_7236 + call Func_7324 + jr .asm_723c + +.asm_7236 + call WaitForWideTextBoxInput + call Func_72ff + +.asm_723c + call Func_3b21 + ld d, DUEL_ANIM_COIN_TOSS2 + ld e, $0 ; heads + call UpdateRNGSources + rra + jr c, .got_result + ld d, DUEL_ANIM_COIN_TOSS1 + ld e, $1 ; tails + +.got_result +; already decided on coin toss result, +; load the correct tossing animation +; and wait for it to finish + ld a, d + call PlayDuelAnimation + ld a, [wCoinTossDuelistType] + or a + jr z, .wait_anim + ld a, e + call Func_7310 + ld e, a + jr .done_toss_anim +.wait_anim + push de + call DoFrame + call CheckAnyAnimationPlaying + pop de + jr c, .wait_anim + ld a, e + call Func_72ff + +.done_toss_anim + ld b, DUEL_ANIM_COIN_HEADS + ld c, $34 ; tile for circle + ld a, e + or a + jr z, .show_result + ld b, DUEL_ANIM_COIN_TAILS + ld c, $30 ; tile for cross + ld hl, wCoinTossNumHeads + inc [hl] + +.show_result + ld a, b + call PlayDuelAnimation + +; load correct sound effect +; the sound of the coin toss result +; is dependant on whether it was the Player +; or the Opponent to get heads/tails + ld a, [wCoinTossDuelistType] + or a + jr z, .check_sfx + ld a, $1 + xor e ; invert result in case it's not Player + ld e, a +.check_sfx + ld d, SFX_54 + ld a, e + or a + jr nz, .got_sfx + ld d, SFX_55 +.got_sfx + ld a, d + call PlaySFX + +; in case it's a multiple coin toss scenario, +; then the result needs to be registered on screen +; with a circle (o) or a cross (x) + ld a, [wCoinTossTotalNum] + dec a + jr z, .incr_num_coin_tossed ; skip if not more than 1 coin toss + ld a, c + push af + ld e, 0 + ld a, [wCoinTossNumTossed] +; calculate the offset to draw the circle/cross +.asm_72a3 + ; if < 10, then the offset is simply calculated + ; from wCoinTossNumTossed * 2... + cp 10 + jr c, .got_offset + ; ...else the y-offset is added for each multiple of 10 + inc e + inc e + sub 10 + jr .asm_72a3 + +.got_offset + add a + ld d, a + lb bc, 2, 2 + lb hl, 1, 2 + pop af + call FillRectangle + +.incr_num_coin_tossed + ld hl, wCoinTossNumTossed + inc [hl] + + ld a, [wCoinTossDuelistType] + or a + jr z, .asm_72dc + ld a, [hl] + ld hl, wCoinTossTotalNum + cp [hl] + call z, WaitForWideTextBoxInput + call Func_7324 + ld a, [wCoinTossTotalNum] + ld hl, wCoinTossNumHeads + or [hl] + jr nz, .asm_72e2 + call z, WaitForWideTextBoxInput + jr .asm_72e2 + +.asm_72dc + call WaitForWideTextBoxInput + call Func_72ff + +.asm_72e2 + call Func_3b31 + ld a, [wCoinTossNumTossed] + ld hl, wCoinTossTotalNum + cp [hl] + jp c, .print_coin_tally + call ExchangeRNG + call Func_3b31 + call Func_3b21 + +; return carry if at least 1 heads + ld a, [wCoinTossNumHeads] + or a + ret z + scf + ret + +Func_72ff: + ldh [hff96], a + ld a, [wDuelType] + cp DUELTYPE_LINK + ret nz + ldh a, [hff96] + call SerialSendByte + call Func_7344 + ret + +Func_7310: + ldh [hff96], a + ld a, [wDuelType] + cp DUELTYPE_LINK + jr z, Func_7338 +.loop_anim + call DoFrame + call CheckAnyAnimationPlaying + jr c, .loop_anim + ldh a, [hff96] + ret + +Func_7324: + ldh [hff96], a + ld a, [wDuelType] + cp DUELTYPE_LINK + jr z, Func_7338 + +; delay coin flip for AI opponent + ld a, 30 +.asm_732f + call DoFrame + dec a + jr nz, .asm_732f + ldh a, [hff96] + ret + +Func_7338: + call DoFrame + call SerialRecvByte + jr c, Func_7338 + call Func_7344 + ret + +Func_7344: + push af + ld a, [wSerialFlags] + or a + jr nz, .asm_734d + pop af + ret +.asm_734d + call Func_3b31 + call DuelTransmissionError + ret + +BuildVersion: + db "VER 12/20 09:36", TX_END + +; possibly unreferenced, used for testing +; enters computer opponent selection screen +; handles input to select/cancel/scroll through deck IDs +; loads the NPC duel configurations if one was selected +; returns carry if selection was cancelled +Func_7364: + xor a + ld [wTileMapFill], a + call ZeroObjectPositionsAndToggleOAMCopy + call EmptyScreen + call LoadSymbolsFont + lb de, $38, $9f + call SetupText + call DrawWideTextBox + call EnableLCD + + xor a + ld [wOpponentDeckID], a + call DrawOpponentSelectionScreen +.wait_input + call DoFrame + ldh a, [hDPadHeld] + or a + jr z, .wait_input + ld b, a + + ; handle selection/cancellation buttons + and A_BUTTON | START + jr nz, .select_opp + bit B_BUTTON_F, b + jr nz, .cancel + +; handle D-pad inputs +; check right + ld a, [wOpponentDeckID] + bit D_RIGHT_F, b + jr z, .check_left + inc a ; next deck ID + cp DECK_IDS_END + 1 + jr c, .check_left + xor a ; wrap around to first deck ID + +.check_left + bit D_LEFT_F, b + jr z, .check_up + or a + jr nz, .not_first_deck_id + ld a, DECK_IDS_END ; wrap around to last deck ID + jr .check_up +.not_first_deck_id + dec a ; previous deck ID + +.check_up + bit D_UP_F, b + jr z, .check_down + add 10 + cp DECK_IDS_END + 1 + jr c, .check_down + xor a ; wrap around to first deck ID + +.check_down + bit D_DOWN_F, b + jr z, .got_deck_id + sub 10 + jr nc, .got_deck_id + ld a, DECK_IDS_END ; wrap around to last deck ID + +.got_deck_id + ld [wOpponentDeckID], a + call DrawOpponentSelectionScreen + jr .wait_input + +.cancel + scf + ret +.select_opp + ld a, [wOpponentDeckID] + ld [wNPCDuelDeckID], a + call GetNPCDuelConfigurations + or a + ret + +; draws the current opponent to be selected +; (his/her portrait and name) +; and prints text box for selection +DrawOpponentSelectionScreen: + ld a, [wOpponentDeckID] + ld [wNPCDuelDeckID], a + call GetNPCDuelConfigurations + jr c, .ok + ; duel configuration not found for the NPC + ; so load a default portrait and name + xor a + ld [wOpponentPortrait], a + ld hl, wOpponentName + ld [hli], a + ld [hl], a +.ok + ld hl, SelectComputerOpponentData + call PlaceTextItems + call DrawDuelistPortraitsAndNames + ld a, [wOpponentDeckID] + lb bc, 5, 16 + call WriteTwoByteNumberInTxSymbolFormat + ld a, [wNPCDuelPrizes] + lb bc, 15, 10 + call WriteTwoByteNumberInTxSymbolFormat + ret + +SelectComputerOpponentData: + textitem 10, 0, ClearOpponentNameText + textitem 10, 10, NumberOfPrizesText + textitem 3, 14, SelectComputerOpponentText + db $ff + +Func_7415: + xor a + ld [wce7e], a + ret + +Func_741a: + ld hl, wEffectFunctionsFeedbackIndex + ld a, [hl] + or a + ret z + ld e, a + ld d, $00 + ld hl, wEffectFunctionsFeedback + add hl, de + ld [hl], $00 + ld hl, wEffectFunctionsFeedback +.loop + ld a, [hli] + or a + jr z, .done + ld d, a + inc hl + ld a, [hli] + ld e, ATK_ANIM_SLEEP + cp ASLEEP + jr z, .got_anim + ld e, ATK_ANIM_PARALYSIS + cp PARALYZED + jr z, .got_anim + ld e, ATK_ANIM_POISON + cp POISONED + jr z, .got_anim + ld e, ATK_ANIM_POISON + cp DOUBLE_POISONED + jr z, .got_anim + ld e, ATK_ANIM_CONFUSION + cp CONFUSED + jr nz, .loop + ldh a, [hWhoseTurn] + cp d + jr nz, .got_anim + ld e, ATK_ANIM_IMAKUNI_CONFUSION +.got_anim + ld a, e + ld [wLoadedAttackAnimation], a + xor a + ld [wDuelAnimLocationParam], a + push hl + farcall PlayAttackAnimationCommands + pop hl + jr .loop +.done + ret + +; this is a simple version of PlayAttackAnimation_DealAttackDamage that doesn't +; take into account status conditions, damage modifiers, etc, for damage calculation. +; used for confusion damage to self and for damage to benched Pokemon, for example +PlayAttackAnimation_DealAttackDamageSimple: + push hl + push de + call PlayAttackAnimation + call WaitAttackAnimation + pop de + pop hl + call SubtractHP + ld a, [wDuelDisplayedScreen] + cp DUEL_MAIN_SCENE + ret nz + push hl + push de + call DrawDuelHUDs + pop de + pop hl + ret + +; if [wLoadedAttackAnimation] != 0, wait until the animation is over +WaitAttackAnimation: + ld a, [wLoadedAttackAnimation] + or a + ret z + push de +.anim_loop + call DoFrame + call CheckAnyAnimationPlaying + jr c, .anim_loop + pop de + ret + +; play attack animation +; input: +; - [wLoadedAttackAnimation]: animation to play +; - de: damage dealt by the attack (to display the animation with the number) +; - c: a wDamageEffectiveness constant (to print WEAK or RESIST if necessary) +PlayAttackAnimation: + ldh a, [hWhoseTurn] + push af + push hl + push de + push bc + ld a, [wWhoseTurn] + ldh [hWhoseTurn], a + ld a, c + ld [wce81], a + ldh a, [hWhoseTurn] + cp h + jr z, .asm_74aa + set 7, b +.asm_74aa + ld a, b + ld [wce82], a + ld a, [wWhoseTurn] + ld [wce83], a + ld a, [wTempNonTurnDuelistCardID] + ld [wce84], a + ld hl, wce7f + ld [hl], e + inc hl + ld [hl], d + +; if damage >= 70, ATK_ANIM_HIT becomes ATK_ANIM_BIG_HIT + ld a, [wLoadedAttackAnimation] + cp ATK_ANIM_HIT + jr nz, .got_anim + ld a, e + cp 70 + jr c, .got_anim + ld a, ATK_ANIM_BIG_HIT + ld [wLoadedAttackAnimation], a + +.got_anim + farcall PlayAttackAnimationCommands + pop bc + pop de + pop hl + pop af + ldh [hWhoseTurn], a + ret + +Func_74dc: + call EmptyScreen + call EnableLCD + ld a, GRASS_ENERGY + ld [wPrizeCardSelectionFrameCounter], a +.wait_input + call DoFrame + ldh a, [hDPadHeld] + ld b, a + ld a, [wPrizeCardSelectionFrameCounter] +; left + bit D_LEFT_F, b + jr z, .right + dec a ; previous card +.right + bit D_RIGHT_F, b + jr z, .up + inc a ; next card +.up + bit D_UP_F, b + jr z, .down + add 10 +.down + bit D_DOWN_F, b + jr z, .got_card_id + sub 10 + +.got_card_id + ld [wPrizeCardSelectionFrameCounter], a + lb bc, 5, 5 + bank1call WriteTwoByteNumberInTxSymbolFormat + ldh a, [hKeysPressed] + and START + jr z, .wait_input + ld a, [wPrizeCardSelectionFrameCounter] + ld e, a + ld d, $0 +.card_loop + call LoadCardDataToBuffer1_FromCardID + ret c ; card not found + push de + ld a, e + call Func_758a + pop de + inc de + jr .card_loop + +; seems to communicate with other device +; for starting a duel +; outputs in hl either wPlayerDuelVariables +; or wOpponentDuelVariables depending on wSerialOp +DecideLinkDuelVariables: + call Func_0e8e + ldtx hl, PressStartWhenReadyText + call DrawWideTextBox_PrintText + call EnableLCD +.input_loop + call DoFrame + ldh a, [hKeysPressed] + bit B_BUTTON_F, a + jr nz, .link_cancel + and START + call Func_0cc5 + jr nc, .input_loop + ld hl, wPlayerDuelVariables + ld a, [wSerialOp] + cp $29 + jr z, .link_continue + ld hl, wOpponentDuelVariables + cp $12 + jr z, .link_continue +.link_cancel + call ResetSerial + scf + ret +.link_continue + or a + ret + + ret ; stray ret |
