summaryrefslogtreecommitdiff
path: root/src/engine/ai/common.asm
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/ai/common.asm')
-rw-r--r--src/engine/ai/common.asm970
1 files changed, 970 insertions, 0 deletions
diff --git a/src/engine/ai/common.asm b/src/engine/ai/common.asm
new file mode 100644
index 0000000..d4f1da4
--- /dev/null
+++ b/src/engine/ai/common.asm
@@ -0,0 +1,970 @@
+; runs through Player's whole deck and
+; sets carry if there's any Pokemon other
+; than Mewtwo1.
+CheckIfPlayerHasPokemonOtherThanMewtwo1: ; 227a9 (8:67a9)
+ call SwapTurn
+ ld e, 0
+.loop_deck
+ ld a, e
+ push de
+ call LoadCardDataToBuffer2_FromDeckIndex
+ pop de
+ ld a, [wLoadedCard2Type]
+ cp TYPE_ENERGY
+ jp nc, .next ; can be a jr
+ ld a, [wLoadedCard2ID]
+ cp MEWTWO1
+ jr nz, .not_mewtwo1
+.next
+ inc e
+ ld a, DECK_SIZE
+ cp e
+ jr nz, .loop_deck
+
+; no carry
+ call SwapTurn
+ or a
+ ret
+
+.not_mewtwo1
+ call SwapTurn
+ scf
+ ret
+
+; returns no carry if, given the Player is using a Mewtwo1 mill deck,
+; the AI already has a Bench fully set up, in which case it
+; will process some Trainer cards in hand (namely Energy Removals).
+; this is used to check whether to skip some normal AI routines
+; this turn and jump right to the attacking phase.
+HandleAIAntiMewtwoDeckStrategy: ; 227d3 (8:67d3)
+; return carry if Player is not playing Mewtwo1 mill deck
+ ld a, [wAIBarrierFlagCounter]
+ bit AI_MEWTWO_MILL_F, a
+ jr z, .set_carry
+
+; else, check if there's been less than 2 turns
+; without the Player using Barrier.
+ cp AI_MEWTWO_MILL + 2
+ jr c, .count_bench
+
+; if there has been, reset wAIBarrierFlagCounter
+; and return carry.
+ xor a
+ ld [wAIBarrierFlagCounter], a
+ jr .set_carry
+
+; else, check number of Pokemon that are set up in Bench
+; if less than 4, return carry.
+.count_bench
+ farcall CountNumberOfSetUpBenchPokemon
+ cp 4
+ jr c, .set_carry
+
+; if there's at least 4 Pokemon in the Bench set up,
+; process Trainer hand cards of AI_TRAINER_CARD_PHASE_05
+ ld a, AI_TRAINER_CARD_PHASE_05
+ farcall AIProcessHandTrainerCards
+ or a
+ ret
+
+.set_carry
+ scf
+ ret
+
+; lists in wDuelTempList all the basic energy cards
+; in card location of a.
+; outputs in a number of cards found.
+; returns carry if none were found.
+; input:
+; a = CARD_LOCATION_* to look
+; output:
+; a = number of cards found
+FindBasicEnergyCardsInLocation: ; 227f6 (8:67f6)
+ ld [wTempAI], a
+ lb de, 0, 0
+ ld hl, wDuelTempList
+
+; d = number of basic energy cards found
+; e = current card in deck
+; loop entire deck
+.loop
+ ld a, DUELVARS_CARD_LOCATIONS
+ add e
+ push hl
+ call GetTurnDuelistVariable
+ ld hl, wTempAI
+ cp [hl]
+ pop hl
+ jr nz, .next_card
+
+; is in the card location we're looking for
+ ld a, e
+ push de
+ push hl
+ call GetCardIDFromDeckIndex
+ pop hl
+ ld a, e
+ pop de
+ cp DOUBLE_COLORLESS_ENERGY
+ ; only basic energy cards
+ ; will set carry here
+ jr nc, .next_card
+
+; is a basic energy card
+; add this card to the TempList
+ ld a, e
+ ld [hli], a
+ inc d
+.next_card
+ inc e
+ ld a, DECK_SIZE
+ cp e
+ jr nz, .loop
+
+; check if any were found
+ ld a, d
+ or a
+ jr z, .set_carry
+
+; some were found, add the termination byte on TempList
+ ld a, $ff
+ ld [hl], a
+ ld a, d
+ ret
+
+.set_carry
+ scf
+ ret
+
+; returns in a the card index of energy card
+; attached to Pokémon in Play Area location a,
+; that is to be discarded by the AI for an effect.
+; outputs $ff is none was found.
+; input:
+; a = PLAY_AREA_* constant of card
+; output:
+; a = deck index of attached energy card chosen
+AIPickEnergyCardToDiscard: ; 2282e (8:682e)
+; load Pokémon's attached energy cards.
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call CreateArenaOrBenchEnergyCardList
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ ld e, a
+ call GetPlayAreaCardAttachedEnergies
+ ld a, [wTotalAttachedEnergies]
+ or a
+ jr z, .no_energy
+
+; load card's ID and type.
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ ld b, a
+ ld a, DUELVARS_ARENA_CARD
+ add b
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ ld [wTempCardID], a
+ call LoadCardDataToBuffer1_FromCardID
+ ld a, [wLoadedCard1Type]
+ or TYPE_ENERGY
+ ld [wTempCardType], a
+
+; find a card that is not useful.
+; if none is found, just return the first energy card attached.
+ ld hl, wDuelTempList
+.loop
+ ld a, [hl]
+ cp $ff
+ jr z, .not_found
+ farcall CheckIfEnergyIsUseful
+ jr nc, .found
+ inc hl
+ jr .loop
+
+.found
+ ld a, [hl]
+ ret
+.not_found
+ ld hl, wDuelTempList
+ ld a, [hl]
+ ret
+.no_energy
+ ld a, $ff
+ ret
+
+; returns in a the deck index of an energy card attached to card
+; in player's Play Area location a to remove.
+; prioritizes double colorless energy, then any useful energy,
+; then defaults to the first energy card attached if neither
+; of those are found.
+; returns $ff in a if there are no energy cards attached.
+; input:
+; a = Play Area location to check
+; output:
+; a = deck index of attached energy card
+PickAttachedEnergyCardToRemove: ; 22875 (8:6875)
+; construct energy list and check if there are any energy cards attached
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call CreateArenaOrBenchEnergyCardList
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ ld e, a
+ call GetPlayAreaCardAttachedEnergies
+ ld a, [wTotalAttachedEnergies]
+ or a
+ jr z, .no_energy
+
+; load card data and store its type
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ ld b, a
+ ld a, DUELVARS_ARENA_CARD
+ add b
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ ld [wTempCardID], a
+ call LoadCardDataToBuffer1_FromCardID
+ ld a, [wLoadedCard1Type]
+ or TYPE_ENERGY
+ ld [wTempCardType], a
+
+; first look for any double colorless energy
+ ld hl, wDuelTempList
+.loop_1
+ ld a, [hl]
+ cp $ff
+ jr z, .check_useful
+ push hl
+ call GetCardIDFromDeckIndex
+ ld a, e
+ cp DOUBLE_COLORLESS_ENERGY
+ pop hl
+ jr z, .found
+ inc hl
+ jr .loop_1
+
+; then look for any energy cards that are useful
+.check_useful
+ ld hl, wDuelTempList
+.loop_2
+ ld a, [hl]
+ cp $ff
+ jr z, .default
+ farcall CheckIfEnergyIsUseful
+ jr c, .found
+ inc hl
+ jr .loop_2
+
+; return the energy card that was found
+.found
+ ld a, [hl]
+ ret
+
+; if none were found with the above criteria,
+; just return the first option
+.default
+ ld hl, wDuelTempList
+ ld a, [hl]
+ ret
+
+; return $ff if no energy cards attached
+.no_energy
+ ld a, $ff
+ ret
+
+; stores in wTempAI and wCurCardCanAttack the deck indices
+; of energy cards attached to card in Play Area location a.
+; prioritizes double colorless energy, then any useful energy,
+; then defaults to the first two energy cards attached if neither
+; of those are found.
+; returns $ff in a if there are no energy cards attached.
+; input:
+; a = Play Area location to check
+; output:
+; [wTempAI] = deck index of attached energy card
+; [wCurCardCanAttack] = deck index of attached energy card
+PickTwoAttachedEnergyCards: ; 228d1 (8:68d1)
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call CreateArenaOrBenchEnergyCardList
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ ld e, a
+ farcall CountNumberOfEnergyCardsAttached
+ cp 2
+ jp c, .not_enough
+
+; load card data and store its type
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ ld b, a
+ ld a, DUELVARS_ARENA_CARD
+ add b
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ ld [wTempCardID], a
+ call LoadCardDataToBuffer1_FromCardID
+ ld a, [wLoadedCard1Type]
+ or TYPE_ENERGY
+ ld [wTempCardType], a
+ ld a, $ff
+ ld [wTempAI], a
+ ld [wCurCardCanAttack], a
+
+; first look for any double colorless energy
+ ld hl, wDuelTempList
+.loop_1
+ ld a, [hl]
+ cp $ff
+ jr z, .check_useful
+ push hl
+ call GetCardIDFromDeckIndex
+ ld a, e
+ cp DOUBLE_COLORLESS_ENERGY
+ pop hl
+ jr z, .found_double_colorless
+ inc hl
+ jr .loop_1
+.found_double_colorless
+ ld a, [wTempAI]
+ cp $ff
+ jr nz, .already_chosen_1
+ ld a, [hli]
+ ld [wTempAI], a
+ jr .loop_1
+.already_chosen_1
+ ld a, [hl]
+ ld [wCurCardCanAttack], a
+ jr .done
+
+; then look for any energy cards that are useful
+.check_useful
+ ld hl, wDuelTempList
+.loop_2
+ ld a, [hl]
+ cp $ff
+ jr z, .default
+ farcall CheckIfEnergyIsUseful
+ jr c, .found_useful
+ inc hl
+ jr .loop_2
+.found_useful
+ ld a, [wTempAI]
+ cp $ff
+ jr nz, .already_chosen_2
+ ld a, [hli]
+ ld [wTempAI], a
+ jr .loop_2
+.already_chosen_2
+ ld a, [hl]
+ ld [wCurCardCanAttack], a
+ jr .done
+
+; if none were found with the above criteria,
+; just return the first 2 options
+.default
+ ld hl, wDuelTempList
+ ld a, [wTempAI]
+ cp $ff
+ jr nz, .pick_one_card
+
+; pick 2 cards
+ ld a, [hli]
+ ld [wTempAI], a
+ ld a, [hl]
+ ld [wCurCardCanAttack], a
+ jr .done
+.pick_one_card
+ ld a, [wTempAI]
+ ld b, a
+.loop_3
+ ld a, [hli]
+ cp b
+ jr z, .loop_3 ; already picked
+ ld [wCurCardCanAttack], a
+
+.done
+ ld a, [wCurCardCanAttack]
+ ld b, a
+ ld a, [wTempAI]
+ ret
+
+; return $ff if no energy cards attached
+.not_enough
+ ld a, $ff
+ ret
+
+; copies $ff terminated buffer from hl to de
+CopyBuffer: ; 2297b (8:697b)
+ ld a, [hli]
+ ld [de], a
+ cp $ff
+ ret z
+ inc de
+ jr CopyBuffer
+
+; zeroes a bytes starting at hl
+ClearMemory_Bank8: ; 22983 (8:6983)
+ push af
+ push bc
+ push hl
+ ld b, a
+ xor a
+.loop
+ ld [hli], a
+ dec b
+ jr nz, .loop
+ pop hl
+ pop bc
+ pop af
+ ret
+
+; counts number of energy cards found in hand
+; and outputs result in a
+; sets carry if none are found
+; output:
+; a = number of energy cards found
+CountOppEnergyCardsInHand: ; 22990 (8:6990)
+ farcall CreateEnergyCardListFromHand
+ ret c
+ ld b, -1
+ ld hl, wDuelTempList
+.loop
+ inc b
+ ld a, [hli]
+ cp $ff
+ jr nz, .loop
+ ld a, b
+ or a
+ ret
+
+; converts HP in a to number of equivalent damage counters
+; input:
+; a = HP
+; output:
+; a = number of damage counters
+ConvertHPToCounters: ; 229a3 (8:69a3)
+ push bc
+ ld c, 0
+.loop
+ sub 10
+ jr c, .carry
+ inc c
+ jr .loop
+.carry
+ ld a, c
+ pop bc
+ ret
+
+; calculates floor(hl / 10)
+CalculateWordTensDigit: ; 229b0 (8:69b0)
+ push bc
+ push de
+ lb bc, $ff, -10
+ lb de, $ff, -1
+.asm_229b8
+ inc de
+ add hl, bc
+ jr c, .asm_229b8
+ ld h, d
+ ld l, e
+ pop de
+ pop bc
+ ret
+
+; returns in a division of b by a
+CalculateBDividedByA_Bank8: ; 229c1 (8:69c1)
+ push bc
+ ld c, a
+ ld a, b
+ ld b, c
+ ld c, 0
+.loop
+ sub b
+ jr c, .done
+ inc c
+ jr .loop
+.done
+ ld a, c
+ pop bc
+ ret
+
+; returns in a the deck index of the first
+; instance of card with ID equal to the ID in e
+; in card location a.
+; returns carry if found.
+; input:
+; a = CARD_LOCATION_*
+; e = card ID to look for
+LookForCardIDInLocation: ; 229d0 (8:69d0)
+ ld b, a
+ ld c, e
+ lb de, $00, 0 ; d is never used
+.loop
+ ld a, DUELVARS_CARD_LOCATIONS
+ add e
+ call GetTurnDuelistVariable
+ cp b
+ jr nz, .next
+ ld a, e
+ push de
+ call GetCardIDFromDeckIndex
+ ld a, e
+ pop de
+ cp c
+ jr z, .found
+.next
+ inc e
+ ld a, DECK_SIZE
+ cp e
+ jr nz, .loop
+
+; not found
+ or a
+ ret
+.found
+ ld a, e
+ scf
+ ret
+
+; return carry if card ID loaded in a is found in hand
+; and outputs in a the deck index of that card
+; input:
+; a = card ID
+; output:
+; a = card deck index, if found
+; carry set if found
+LookForCardIDInHandList_Bank8: ; 229f3 (8:69f3)
+ ld [wTempCardIDToLook], a
+ call CreateHandCardList
+ ld hl, wDuelTempList
+
+.loop
+ ld a, [hli]
+ cp $ff
+ ret z
+
+ ldh [hTempCardIndex_ff98], a
+ call LoadCardDataToBuffer1_FromDeckIndex
+ ld b, a
+ ld a, [wTempCardIDToLook]
+ cp b
+ jr nz, .loop
+
+ ldh a, [hTempCardIndex_ff98]
+ scf
+ ret
+
+; searches in deck for card ID 1 in a, and
+; if found, searches in Hand/Play Area for card ID 2 in b, and
+; if found, searches for card ID 1 in Hand/Play Area, and
+; if none found, return carry and output deck index
+; of the card ID 1 in deck.
+; input:
+; a = card ID 1
+; b = card ID 2
+; output:
+; a = index of card ID 1 in deck
+LookForCardIDInDeck_GivenCardIDInHandAndPlayArea: ; 22a10 (8:6a10)
+; store a in wCurCardCanAttack
+; and b in wTempAI
+ ld c, a
+ ld a, b
+ ld [wTempAI], a
+ ld a, c
+ ld [wCurCardCanAttack], a
+
+; look for the card ID 1 in deck
+ ld e, a
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret nc
+
+; was found, store its deck index in memory
+ ld [wTempAIPokemonCard], a
+
+; look for the card ID 2
+; in Hand and Play Area, return if not found.
+ ld a, [wTempAI]
+ call LookForCardIDInHandAndPlayArea
+ ret nc
+
+; look for the card ID 1 in the Hand and Play Area
+; if any card is found, return no carry.
+ ld a, [wCurCardCanAttack]
+ call LookForCardIDInHandAndPlayArea
+ jr c, .no_carry
+; none found
+
+ ld a, [wTempAIPokemonCard]
+ scf
+ ret
+
+.no_carry
+ or a
+ ret
+
+; returns carry if card ID in a
+; is found in Play Area or in hand
+; input:
+; a = card ID
+LookForCardIDInHandAndPlayArea: ; 22a39 (8:6a39)
+ ld b, a
+ push bc
+ call LookForCardIDInHandList_Bank8
+ pop bc
+ ret c
+
+ ld a, b
+ ld b, PLAY_AREA_ARENA
+ call LookForCardIDInPlayArea_Bank8
+ ret c
+ or a
+ ret
+
+; searches in deck for card ID 1 in a, and
+; if found, searches in Hand Area for card ID 2 in b, and
+; if found, searches for card ID 1 in Hand/Play Area, and
+; if none found, return carry and output deck index
+; of the card ID 1 in deck.
+; input:
+; a = card ID 1
+; b = card ID 2
+; output:
+; a = index of card ID 1 in deck
+LookForCardIDInDeck_GivenCardIDInHand: ; 22a49 (8:6a49)
+; store a in wCurCardCanAttack
+; and b in wTempAI
+ ld c, a
+ ld a, b
+ ld [wTempAI], a
+ ld a, c
+ ld [wCurCardCanAttack], a
+
+; look for the card ID 1 in deck
+ ld e, a
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret nc
+
+; was found, store its deck index in memory
+ ld [wTempAIPokemonCard], a
+
+; look for the card ID 2 in hand, return if not found.
+ ld a, [wTempAI]
+ call LookForCardIDInHandList_Bank8
+ ret nc
+
+; look for the card ID 1 in the Hand and Play Area
+; if any card is found, return no carry.
+ ld a, [wCurCardCanAttack]
+ call LookForCardIDInHandAndPlayArea
+ jr c, .no_carry
+; none found
+
+ ld a, [wTempAIPokemonCard]
+ scf
+ ret
+
+.no_carry
+ or a
+ ret
+
+; returns carry if card ID in a
+; is found in Play Area, starting with
+; location in b
+; input:
+; a = card ID
+; b = PLAY_AREA_* to start with
+; output:
+; a = PLAY_AREA_* of found card
+; carry set if found
+LookForCardIDInPlayArea_Bank8: ; 22a72 (8:6a72)
+ ld [wTempCardIDToLook], a
+.loop
+ ld a, DUELVARS_ARENA_CARD
+ add b
+ call GetTurnDuelistVariable
+ cp $ff
+ ret z
+
+ call LoadCardDataToBuffer1_FromDeckIndex
+ ld c, a
+ ld a, [wTempCardIDToLook]
+ cp c
+ jr z, .is_same
+
+ inc b
+ ld a, MAX_PLAY_AREA_POKEMON
+ cp b
+ jr nz, .loop
+ ld b, $ff
+ or a
+ ret
+
+.is_same
+ ld a, b
+ scf
+ ret
+
+; runs through list avoiding card in e.
+; removes first card in list not equal to e
+; and that has a type allowed to be removed, in d.
+; returns carry if successful in finding a card.
+; input:
+; d = type of card allowed to be removed
+; ($00 = Trainer, $01 = Pokemon, $02 = Energy)
+; e = card deck index to avoid removing
+; output:
+; a = card index of removed card
+RemoveFromListDifferentCardOfGivenType: ; 22a95 (8:6a95)
+ push hl
+ push de
+ push bc
+ call CountCardsInDuelTempList
+ call ShuffleCards
+
+; loop list until a card with
+; deck index different from e is found.
+.loop_list
+ ld a, [hli]
+ cp $ff
+ jr z, .no_carry
+ cp e
+ jr z, .loop_list
+
+; get this card's type
+ ldh [hTempCardIndex_ff98], a
+ push de
+ call GetCardIDFromDeckIndex
+ call GetCardType
+ pop de
+ cp TYPE_ENERGY
+ jr c, .pkmn_card
+ cp TYPE_TRAINER
+ jr nz, .energy
+
+; only remove from list specific type.
+
+; trainer
+ ld a, d
+ or a
+ jr nz, .loop_list
+ jr .remove_card
+.energy
+ ld a, d
+ cp $02
+ jr nz, .loop_list
+ jr .remove_card
+.pkmn_card
+ ld a, d
+ cp $01
+ jr nz, .loop_list
+ ; fallthrough
+
+.remove_card
+ ld d, h
+ ld e, l
+ dec hl
+.loop_remove
+ ld a, [de]
+ inc de
+ ld [hli], a
+ cp $ff
+ jr nz, .loop_remove
+
+; success
+ ldh a, [hTempCardIndex_ff98]
+ pop bc
+ pop de
+ pop hl
+ scf
+ ret
+.no_carry
+ pop bc
+ pop de
+ pop hl
+ or a
+ ret
+
+; used in Pokemon Trader checks to look for a specific
+; card in the deck to trade with a card in hand that
+; has a card ID different from e.
+; returns carry if successful.
+; input:
+; a = card ID 1
+; e = card ID 2
+; output:
+; a = deck index of card ID 1 found in deck
+; e = deck index of Pokemon card in hand different than card ID 2
+LookForCardIDToTradeWithDifferentHandCard: ; 22ae0 (8:6ae0)
+ ld hl, wCurCardCanAttack
+ ld [hl], e
+ ld [wTempAI], a
+
+; if card ID 1 is in hand, return no carry.
+ call LookForCardIDInHandList_Bank8
+ jr c, .no_carry
+
+; if card ID 1 is not in deck, return no carry.
+ ld a, [wTempAI]
+ ld e, a
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jr nc, .no_carry
+
+; store its deck index
+ ld [wTempAI], a
+
+; look in hand for Pokemon card ID that
+; is different from card ID 2.
+ ld a, [wCurCardCanAttack]
+ ld c, a
+ call CreateHandCardList
+ ld hl, wDuelTempList
+
+.loop_hand
+ ld a, [hli]
+ cp $ff
+ jr z, .no_carry
+ ld b, a
+ call LoadCardDataToBuffer1_FromDeckIndex
+ cp c
+ jr z, .loop_hand
+ ld a, [wLoadedCard1Type]
+ cp TYPE_ENERGY
+ jr nc, .loop_hand
+
+; found, output deck index of card ID 1 in deck
+; and deck index of card found in hand, and set carry
+ ld e, b
+ ld a, [wTempAI]
+ scf
+ ret
+
+.no_carry
+ or a
+ ret
+
+; returns carry if at least one card in the hand
+; has the card ID of input. Outputs its index.
+; input:
+; a = card ID to look for
+; output:
+; a = deck index of card in hand found
+CheckIfHasCardIDInHand: ; 22b1f (8:6b1f)
+ ld [wTempCardIDToLook], a
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ ld c, 0
+
+.loop_hand
+ ld a, [hli]
+ cp $ff
+ ret z
+ ldh [hTempCardIndex_ff98], a
+ call LoadCardDataToBuffer1_FromDeckIndex
+ ld b, a
+ ld a, [wTempCardIDToLook]
+ cp b
+ jr nz, .loop_hand
+ ld a, c
+ or a
+ jr nz, .set_carry
+ inc c
+ jr nz, .loop_hand
+
+.set_carry
+ ldh a, [hTempCardIndex_ff98]
+ scf
+ ret
+
+; outputs in a total number of Pokemon cards in hand
+; plus Pokemon in Turn Duelist's Play Area.
+CountPokemonCardsInHandAndInPlayArea: ; 22b45 (8:6b45)
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld [wTempAI], a
+ call CreateHandCardList
+ ld hl, wDuelTempList
+.loop_hand
+ ld a, [hli]
+ cp $ff
+ jr z, .done
+ call GetCardIDFromDeckIndex
+ call GetCardType
+ cp TYPE_ENERGY
+ jr nc, .loop_hand
+ ld a, [wTempAI]
+ inc a
+ ld [wTempAI], a
+ jr .loop_hand
+.done
+ ld a, [wTempAI]
+ ret
+
+; returns carry if a duplicate Pokemon card is found in hand.
+; outputs in a the deck index of one of them.
+FindDuplicatePokemonCards: ; 22b6f (8:6b6f)
+ ld a, $ff
+ ld [wTempAI], a
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ push hl
+
+.loop_hand_outer
+ pop hl
+ ld a, [hli]
+ cp $ff
+ jr z, .done
+ call GetCardIDFromDeckIndex
+ ld b, e
+ push hl
+
+.loop_hand_inner
+ ld a, [hli]
+ cp $ff
+ jr z, .loop_hand_outer
+ ld c, a
+ call GetCardIDFromDeckIndex
+ ld a, e
+ cp b
+ jr nz, .loop_hand_inner
+
+; found two cards with same ID,
+; if they are Pokemon cards, store its deck index.
+ push bc
+ call GetCardType
+ pop bc
+ cp TYPE_ENERGY
+ jr nc, .loop_hand_outer
+ ld a, c
+ ld [wTempAI], a
+ ; for some reason loop still continues
+ ; even though if some other duplicate
+ ; cards are found, it overwrites the result.
+ jr .loop_hand_outer
+
+.done
+ ld a, [wTempAI]
+ cp $ff
+ jr z, .no_carry
+
+; found
+ scf
+ ret
+.no_carry
+ or a
+ ret
+
+; return carry flag if attack is not high recoil.
+AICheckIfAttackIsHighRecoil: ; 22bad (8:6bad)
+ farcall AIProcessButDontUseAttack
+ ret nc
+ ld a, [wSelectedAttack]
+ ld e, a
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ ld d, a
+ call CopyAttackDataAndDamage_FromDeckIndex
+ ld a, ATTACK_FLAG1_ADDRESS | HIGH_RECOIL_F
+ call CheckLoadedAttackFlag
+ ccf
+ ret