summaryrefslogtreecommitdiff
path: root/src/engine/ai
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/ai')
-rw-r--r--src/engine/ai/common.asm970
-rw-r--r--src/engine/ai/deck_ai.asm82
-rw-r--r--src/engine/ai/decks/fire_charge.asm80
-rw-r--r--src/engine/ai/decks/first_strike.asm76
-rw-r--r--src/engine/ai/decks/flower_power.asm75
-rw-r--r--src/engine/ai/decks/general.asm194
-rw-r--r--src/engine/ai/decks/general_no_retreat.asm140
-rw-r--r--src/engine/ai/decks/go_go_rain_dance.asm79
-rw-r--r--src/engine/ai/decks/im_ronald.asm80
-rw-r--r--src/engine/ai/decks/invincible_ronald.asm78
-rw-r--r--src/engine/ai/decks/legendary_articuno.asm209
-rw-r--r--src/engine/ai/decks/legendary_dragonite.asm166
-rw-r--r--src/engine/ai/decks/legendary_moltres.asm176
-rw-r--r--src/engine/ai/decks/legendary_ronald.asm203
-rw-r--r--src/engine/ai/decks/legendary_zapdos.asm153
-rw-r--r--src/engine/ai/decks/powerful_ronald.asm92
-rw-r--r--src/engine/ai/decks/rock_crusher.asm74
-rw-r--r--src/engine/ai/decks/sams_practice.asm205
-rw-r--r--src/engine/ai/decks/strange_psyshock.asm81
-rw-r--r--src/engine/ai/decks/wonders_of_science.asm77
-rw-r--r--src/engine/ai/decks/zapping_selfdestruct.asm75
-rw-r--r--src/engine/ai/pkmn_powers.asm1228
-rw-r--r--src/engine/ai/trainer_cards.asm6073
23 files changed, 10666 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
diff --git a/src/engine/ai/deck_ai.asm b/src/engine/ai/deck_ai.asm
new file mode 100644
index 0000000..c330418
--- /dev/null
+++ b/src/engine/ai/deck_ai.asm
@@ -0,0 +1,82 @@
+; AI card retreat score bonus
+; when the AI retreat routine runs through the Bench to choose
+; a Pokemon to switch to, it looks up in this list and if
+; a card ID matches, applies a retreat score bonus to this card.
+; positive (negative) means more (less) likely to switch to this card.
+ai_retreat: MACRO
+ db \1 ; card ID
+ db $80 + \2 ; retreat score (ranges between -128 and 127)
+ENDM
+
+; AI card energy attach score bonus
+; when the AI energy attachment routine runs through the Play Area to choose
+; a Pokemon to attach an energy card, it looks up in this list and if
+; a card ID matches, skips this card if the maximum number of energy
+; cards attached has been reached. If it hasn't been reached, additionally
+; applies a positive (or negative) AI score to attach energy to this card.
+ai_energy: MACRO
+ db \1 ; card ID
+ db \2 ; maximum number of attached cards
+ db $80 + \3 ; energy score (ranges between -128 and 127)
+ENDM
+
+; stores in WRAM pointer to data in argument
+; e.g. store_list_pointer wSomeListPointer, SomeData
+store_list_pointer: MACRO
+ ld hl, \1
+ ld de, \2
+ ld [hl], e
+ inc hl
+ ld [hl], d
+ENDM
+
+; deck AIs are specialized to work on a given deck ID.
+; they decide what happens during a turn, what Pokemon cards
+; to pick during the start of the duel, etc.
+; the different scenarios these are used are listed in AIACTION_* constants.
+; each of these have a pointer table with the following structure:
+; dw .do_turn : never called;
+;
+; dw .do_turn : called to handle the main turn logic, from the beginning
+; of the turn up to the attack (or lack thereof);
+;
+; dw .start_duel : called at the start of the duel to initialize some
+; variables and optionally set up CPU hand and deck;
+;
+; dw .forced_switch : logic to determine what Pokemon to pick when there's
+; an effect that forces AI to switch to Bench card;
+;
+; dw .ko_switch : logic for picking which card to use after a KO;
+;
+; dw .take_prize : logic to decide which prize card to pick.
+
+; optionally, decks can also declare card lists that will add
+; more specialized logic during various generic AI routines,
+; and read during the .start_duel routines.
+; the pointers to these lists are stored in memory:
+; wAICardListAvoidPrize : list of cards to avoid being placed as prize;
+; wAICardListArenaPriority : priority list of Arena card at duel start;
+; wAICardListBenchPriority : priority list of Bench cards at duel start;
+; wAICardListPlayFromHandPriority : priority list of cards to play from hand;
+; wAICardListRetreatBonus : scores given to certain cards for retreat;
+; wAICardListEnergyBonus : max number of energy cards and card scores.
+
+INCLUDE "engine/ai/decks/general.asm"
+INCLUDE "engine/ai/decks/sams_practice.asm"
+INCLUDE "engine/ai/decks/general_no_retreat.asm"
+INCLUDE "engine/ai/decks/legendary_moltres.asm"
+INCLUDE "engine/ai/decks/legendary_zapdos.asm"
+INCLUDE "engine/ai/decks/legendary_articuno.asm"
+INCLUDE "engine/ai/decks/legendary_dragonite.asm"
+INCLUDE "engine/ai/decks/first_strike.asm"
+INCLUDE "engine/ai/decks/rock_crusher.asm"
+INCLUDE "engine/ai/decks/go_go_rain_dance.asm"
+INCLUDE "engine/ai/decks/zapping_selfdestruct.asm"
+INCLUDE "engine/ai/decks/flower_power.asm"
+INCLUDE "engine/ai/decks/strange_psyshock.asm"
+INCLUDE "engine/ai/decks/wonders_of_science.asm"
+INCLUDE "engine/ai/decks/fire_charge.asm"
+INCLUDE "engine/ai/decks/im_ronald.asm"
+INCLUDE "engine/ai/decks/powerful_ronald.asm"
+INCLUDE "engine/ai/decks/invincible_ronald.asm"
+INCLUDE "engine/ai/decks/legendary_ronald.asm"
diff --git a/src/engine/ai/decks/fire_charge.asm b/src/engine/ai/decks/fire_charge.asm
new file mode 100644
index 0000000..f5b347b
--- /dev/null
+++ b/src/engine/ai/decks/fire_charge.asm
@@ -0,0 +1,80 @@
+AIActionTable_FireCharge: ; 15232 (5:5232)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 1523e (5:523e)
+ call AIMainTurnLogic
+ ret
+
+.start_duel ; 15242 (5:5242)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+
+.forced_switch ; 15253 (5:5253)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.ko_switch ; 15257 (5:5257)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.take_prize ; 1525b (5:525b)
+ call AIPickPrizeCards
+ ret
+
+.list_arena ; 1525f (5:525f)
+ db JIGGLYPUFF3
+ db CHANSEY
+ db TAUROS
+ db MAGMAR1
+ db JIGGLYPUFF1
+ db GROWLITHE
+ db $00
+
+.list_bench ; 15266 (5:5266)
+ db JIGGLYPUFF3
+ db CHANSEY
+ db GROWLITHE
+ db MAGMAR1
+ db JIGGLYPUFF1
+ db TAUROS
+ db $00
+
+.list_retreat ; 1526e (5:526e)
+ ai_retreat JIGGLYPUFF1, -1
+ ai_retreat CHANSEY, -1
+ ai_retreat GROWLITHE, -1
+ db $00
+
+.list_energy ; 15274 (5:5274)
+ ai_energy GROWLITHE, 3, +0
+ ai_energy ARCANINE2, 4, +0
+ ai_energy MAGMAR1, 3, +0
+ ai_energy JIGGLYPUFF1, 3, +0
+ ai_energy JIGGLYPUFF3, 2, +0
+ ai_energy WIGGLYTUFF, 3, +0
+ ai_energy CHANSEY, 4, +0
+ ai_energy TAUROS, 3, +0
+ db $00
+
+.list_prize ; 1528d (5:528d)
+ db GAMBLER
+ db $00
+
+.store_list_pointers ; 1528f (5:528f)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
diff --git a/src/engine/ai/decks/first_strike.asm b/src/engine/ai/decks/first_strike.asm
new file mode 100644
index 0000000..2e636e1
--- /dev/null
+++ b/src/engine/ai/decks/first_strike.asm
@@ -0,0 +1,76 @@
+AIActionTable_FirstStrike: ; 14e89 (5:4e89)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 14e95 (5:4e95)
+ call AIMainTurnLogic
+ ret
+
+.start_duel ; 14e99 (5:4e99)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+
+.forced_switch ; 14eaa (5:4eaa)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.ko_switch ; 14eae (5:4eae)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.take_prize ; 14eb2 (5:4eb2)
+ call AIPickPrizeCards
+ ret
+
+.list_arena ; 14eb6 (5:1eb6)
+ db HITMONCHAN
+ db MACHOP
+ db HITMONLEE
+ db MANKEY
+ db $00
+
+.list_bench ; 14ebb (5:1ebb)
+ db MACHOP
+ db HITMONLEE
+ db HITMONCHAN
+ db MANKEY
+ db $00
+
+.list_retreat ; 14ec0 (5:1ec0)
+ ai_retreat MACHOP, -1
+ ai_retreat MACHOKE, -1
+ ai_retreat MANKEY, -2
+ db $00
+
+.list_energy ; 14ec7 (5:1ec7)
+ ai_energy MACHOP, 3, +0
+ ai_energy MACHOKE, 4, +0
+ ai_energy MACHAMP, 4, -1
+ ai_energy HITMONCHAN, 3, +0
+ ai_energy HITMONLEE, 3, +0
+ ai_energy MANKEY, 2, -1
+ ai_energy PRIMEAPE, 3, -1
+ db $00
+
+.list_prize ; 14edd (5:1edd)
+ db HITMONLEE
+ db HITMONCHAN
+ db $00
+
+.store_list_pointers ; 14ee0 (5:4ee0)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
diff --git a/src/engine/ai/decks/flower_power.asm b/src/engine/ai/decks/flower_power.asm
new file mode 100644
index 0000000..4d423a3
--- /dev/null
+++ b/src/engine/ai/decks/flower_power.asm
@@ -0,0 +1,75 @@
+AIActionTable_FlowerPower: ; 1509b (5:509b)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 150a7 (5:50a7)
+ call AIMainTurnLogic
+ ret
+
+.start_duel ; 150ab (5:50ab)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+
+.forced_switch ; 150bc (5:50bc)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.ko_switch ; 150c0 (5:50c0)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.take_prize ; 150c4 (5:50c4)
+ call AIPickPrizeCards
+ ret
+
+.list_arena ; 150c8 (5:50c8)
+ db ODDISH
+ db EXEGGCUTE
+ db BULBASAUR
+ db $00
+
+.list_bench ; 150cc (5:50cc)
+ db BULBASAUR
+ db EXEGGCUTE
+ db ODDISH
+ db $00
+
+.list_retreat ; 150cf (5:50cf)
+ ai_retreat GLOOM, -2
+ ai_retreat VILEPLUME, -2
+ ai_retreat BULBASAUR, -2
+ ai_retreat IVYSAUR, -2
+ db $00
+
+.list_energy ; 150d9 (5:50d9)
+ ai_energy BULBASAUR, 3, +0
+ ai_energy IVYSAUR, 4, +0
+ ai_energy VENUSAUR2, 4, +0
+ ai_energy ODDISH, 2, +0
+ ai_energy GLOOM, 3, -1
+ ai_energy VILEPLUME, 3, -1
+ ai_energy EXEGGCUTE, 3, +0
+ ai_energy EXEGGUTOR, 22, +0
+ db $00
+
+.list_prize ; 150f2 (5:50f2)
+ db VENUSAUR2
+ db $00
+
+.store_list_pointers ; 150f4 (5:50f4)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
diff --git a/src/engine/ai/decks/general.asm b/src/engine/ai/decks/general.asm
new file mode 100644
index 0000000..039e101
--- /dev/null
+++ b/src/engine/ai/decks/general.asm
@@ -0,0 +1,194 @@
+; AI logic used by general decks
+AIActionTable_GeneralDecks: ; 14668 (05:4668)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 14674 (5:4674)
+ call AIMainTurnLogic
+ ret
+
+.start_duel ; 14678 (5:4678)
+ call InitAIDuelVars
+ call AIPlayInitialBasicCards
+ ret
+
+.forced_switch ; 1467f (5:467f)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.ko_switch ; 14683 (5:4683)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.take_prize: ; 14687 (5:4687)
+ call AIPickPrizeCards
+ ret
+
+; handle AI routines for a whole turn
+AIMainTurnLogic: ; 1468b (5:468b)
+; initialize variables
+ call InitAITurnVars
+ ld a, AI_TRAINER_CARD_PHASE_01
+ call AIProcessHandTrainerCards
+ farcall HandleAIAntiMewtwoDeckStrategy
+ jp nc, .try_attack
+; handle Pkmn Powers
+ farcall HandleAIGoGoRainDanceEnergy
+ farcall HandleAIDamageSwap
+ farcall HandleAIPkmnPowers
+ ret c ; return if turn ended
+ farcall HandleAICowardice
+; process Trainer cards
+; phase 2 through 4.
+ ld a, AI_TRAINER_CARD_PHASE_02
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_03
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_04
+ call AIProcessHandTrainerCards
+; play Pokemon from hand
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+; process Trainer cards
+; phase 5 through 12.
+ ld a, AI_TRAINER_CARD_PHASE_05
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_06
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_07
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_08
+ call AIProcessHandTrainerCards
+ call AIProcessRetreat
+ ld a, AI_TRAINER_CARD_PHASE_10
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_11
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_12
+ call AIProcessHandTrainerCards
+; play Energy card if possible
+ ld a, [wAlreadyPlayedEnergy]
+ or a
+ jr nz, .skip_energy_attach_1
+ call AIProcessAndTryToPlayEnergy
+.skip_energy_attach_1
+; play Pokemon from hand again
+ call AIDecidePlayPokemonCard
+; handle Pkmn Powers again
+ farcall HandleAIDamageSwap
+ farcall HandleAIPkmnPowers
+ ret c ; return if turn ended
+ farcall HandleAIGoGoRainDanceEnergy
+ ld a, AI_ENERGY_TRANS_ATTACK
+ farcall HandleAIEnergyTrans
+; process Trainer cards phases 13 and 15
+ ld a, AI_TRAINER_CARD_PHASE_13
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_15
+ call AIProcessHandTrainerCards
+; if used Professor Oak, process new hand
+; if not, then proceed to attack.
+ ld a, [wPreviousAIFlags]
+ and AI_FLAG_USED_PROFESSOR_OAK
+ jr z, .try_attack
+ ld a, AI_TRAINER_CARD_PHASE_01
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_02
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_03
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_04
+ call AIProcessHandTrainerCards
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+ ld a, AI_TRAINER_CARD_PHASE_05
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_06
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_07
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_08
+ call AIProcessHandTrainerCards
+ call AIProcessRetreat
+ ld a, AI_TRAINER_CARD_PHASE_10
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_11
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_12
+ call AIProcessHandTrainerCards
+ ld a, [wAlreadyPlayedEnergy]
+ or a
+ jr nz, .skip_energy_attach_2
+ call AIProcessAndTryToPlayEnergy
+.skip_energy_attach_2
+ call AIDecidePlayPokemonCard
+ farcall HandleAIDamageSwap
+ farcall HandleAIPkmnPowers
+ ret c ; return if turn ended
+ farcall HandleAIGoGoRainDanceEnergy
+ ld a, AI_ENERGY_TRANS_ATTACK
+ farcall HandleAIEnergyTrans
+ ld a, AI_TRAINER_CARD_PHASE_13
+ call AIProcessHandTrainerCards
+ ; skip AI_TRAINER_CARD_PHASE_15
+.try_attack
+ ld a, AI_ENERGY_TRANS_TO_BENCH
+ farcall HandleAIEnergyTrans
+; attack if possible, if not,
+; finish turn without attacking.
+ call AIProcessAndTryToUseAttack
+ ret c ; return if AI attacked
+ ld a, OPPACTION_FINISH_NO_ATTACK
+ bank1call AIMakeDecision
+ ret
+
+; handles AI retreating logic
+AIProcessRetreat: ; 14786 (5:4786)
+ ld a, [wAIRetreatedThisTurn]
+ or a
+ ret nz ; return, already retreated this turn
+
+ call AIDecideWhetherToRetreat
+ ret nc ; return if not retreating
+
+ call AIDecideBenchPokemonToSwitchTo
+ ret c ; return if no Bench Pokemon
+
+; store Play Area to retreat to and
+; set wAIRetreatedThisTurn to true
+ ld [wAIPlayAreaCardToSwitch], a
+ ld a, $01
+ ld [wAIRetreatedThisTurn], a
+
+; if AI can use Switch from hand, use it instead...
+ ld a, AI_TRAINER_CARD_PHASE_09
+ call AIProcessHandTrainerCards
+ ld a, [wPreviousAIFlags]
+ and AI_FLAG_USED_SWITCH
+ jr nz, .used_switch
+; ... else try retreating normally.
+ ld a, [wAIPlayAreaCardToSwitch]
+ call AITryToRetreat
+ ret
+
+.used_switch
+; if AI used switch, unset its AI flag
+ ld a, [wPreviousAIFlags]
+ and ~AI_FLAG_USED_SWITCH ; clear Switch flag
+ ld [wPreviousAIFlags], a
+
+; bug, this doesn't make sense being here, since at this point
+; Switch Trainer card was already used to retreat the Pokemon.
+; what the routine will do is just transfer Energy cards to
+; the Arena Pokemon for the purpose of retreating, and
+; then not actually retreat, resulting in unusual behaviour.
+; this would only work placed right after the AI checks whether
+; they have Switch card in hand to use and doesn't have one.
+; (and probably that was the original intention.)
+ ld a, AI_ENERGY_TRANS_RETREAT ; retreat
+ farcall HandleAIEnergyTrans
+ ret
diff --git a/src/engine/ai/decks/general_no_retreat.asm b/src/engine/ai/decks/general_no_retreat.asm
new file mode 100644
index 0000000..20d84e3
--- /dev/null
+++ b/src/engine/ai/decks/general_no_retreat.asm
@@ -0,0 +1,140 @@
+; acts just like a general deck AI except never retreats
+AIActionTable_GeneralNoRetreat: ; 148dc (5:48dc)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 148e8 (5:48e8)
+ call AIDoTurn_GeneralNoRetreat
+ ret
+
+.start_duel ; 148ec (5:48ec)
+ call InitAIDuelVars
+ call AIPlayInitialBasicCards
+ ret
+
+.forced_switch ; 148f3 (5:48f3)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.ko_switch ; 148f7 (5:48f7)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.take_prize ; 148fb (5:48fb)
+ call AIPickPrizeCards
+ ret
+
+AIDoTurn_GeneralNoRetreat: ; 148ff (5:48ff)
+; initialize variables
+ call InitAITurnVars
+ ld a, AI_TRAINER_CARD_PHASE_01
+ call AIProcessHandTrainerCards
+ farcall HandleAIAntiMewtwoDeckStrategy
+ jp nc, .try_attack
+; handle Pkmn Powers
+ farcall HandleAIGoGoRainDanceEnergy
+ farcall HandleAIDamageSwap
+ farcall HandleAIPkmnPowers
+ ret c ; return if turn ended
+ farcall HandleAICowardice
+; process Trainer cards
+; phase 2 through 4.
+ ld a, AI_TRAINER_CARD_PHASE_02
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_03
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_04
+ call AIProcessHandTrainerCards
+; play Pokemon from hand
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+; process Trainer cards
+; phase 5 through 12.
+ ld a, AI_TRAINER_CARD_PHASE_05
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_06
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_07
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_08
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_10
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_11
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_12
+ call AIProcessHandTrainerCards
+; play Energy card if possible
+ ld a, [wAlreadyPlayedEnergy]
+ or a
+ jr nz, .skip_energy_attach_1
+ call AIProcessAndTryToPlayEnergy
+.skip_energy_attach_1
+; play Pokemon from hand again
+ call AIDecidePlayPokemonCard
+; handle Pkmn Powers again
+ farcall HandleAIDamageSwap
+ farcall HandleAIPkmnPowers
+ ret c ; return if turn ended
+ farcall HandleAIGoGoRainDanceEnergy
+ ld a, AI_ENERGY_TRANS_ATTACK
+ farcall HandleAIEnergyTrans
+; process Trainer cards phases 13 and 15
+ ld a, AI_TRAINER_CARD_PHASE_13
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_15
+ call AIProcessHandTrainerCards
+; if used Professor Oak, process new hand
+; if not, then proceed to attack.
+ ld a, [wPreviousAIFlags]
+ and AI_FLAG_USED_PROFESSOR_OAK
+ jr z, .try_attack
+ ld a, AI_TRAINER_CARD_PHASE_01
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_02
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_03
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_04
+ call AIProcessHandTrainerCards
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+ ld a, AI_TRAINER_CARD_PHASE_05
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_06
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_07
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_08
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_10
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_11
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_12
+ call AIProcessHandTrainerCards
+ ld a, [wAlreadyPlayedEnergy]
+ or a
+ jr nz, .skip_energy_attach_2
+ call AIProcessAndTryToPlayEnergy
+.skip_energy_attach_2
+ call AIDecidePlayPokemonCard
+ farcall HandleAIDamageSwap
+ farcall HandleAIPkmnPowers
+ ret c ; return if turn ended
+ farcall HandleAIGoGoRainDanceEnergy
+ ld a, AI_TRAINER_CARD_PHASE_13
+ call AIProcessHandTrainerCards
+ ; skip AI_TRAINER_CARD_PHASE_15
+.try_attack
+; attack if possible, if not,
+; finish turn without attacking.
+ call AIProcessAndTryToUseAttack
+ ret c ; return if turn ended
+ ld a, OPPACTION_FINISH_NO_ATTACK
+ bank1call AIMakeDecision
+ ret
diff --git a/src/engine/ai/decks/go_go_rain_dance.asm b/src/engine/ai/decks/go_go_rain_dance.asm
new file mode 100644
index 0000000..23547e2
--- /dev/null
+++ b/src/engine/ai/decks/go_go_rain_dance.asm
@@ -0,0 +1,79 @@
+AIActionTable_GoGoRainDance: ; 14f8f (5:4f8f)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 14f9b (5:4f9b)
+ call AIMainTurnLogic
+ ret
+
+.start_duel ; 14f9f (5:4f9f)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+
+.forced_switch ; 14fb0 (5:4fb0)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.ko_switch ; 14fb4 (5:4fb4)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.take_prize ; 14fb8 (5:4fb8)
+ call AIPickPrizeCards
+ ret
+
+.list_arena ; 14fbc (5:4fbc)
+ db LAPRAS
+ db HORSEA
+ db GOLDEEN
+ db SQUIRTLE
+ db $00
+
+.list_bench ; 14fc1 (5:4fc1)
+ db SQUIRTLE
+ db HORSEA
+ db GOLDEEN
+ db LAPRAS
+ db $00
+
+.list_retreat ; 14fc6 (5:4fc6)
+ ai_retreat SQUIRTLE, -3
+ ai_retreat WARTORTLE, -2
+ ai_retreat HORSEA, -1
+ db $00
+
+.list_energy ; 14fcd (5:4fcd)
+ ai_energy SQUIRTLE, 2, +0
+ ai_energy WARTORTLE, 3, +0
+ ai_energy BLASTOISE, 5, +0
+ ai_energy GOLDEEN, 1, +0
+ ai_energy SEAKING, 2, +0
+ ai_energy HORSEA, 2, +0
+ ai_energy SEADRA, 3, +0
+ ai_energy LAPRAS, 3, +0
+ db $00
+
+.list_prize ; 14fe6 (5:4fe6)
+ db GAMBLER
+ db ENERGY_RETRIEVAL
+ db SUPER_ENERGY_RETRIEVAL
+ db BLASTOISE
+ db $00
+
+.store_list_pointers ; 14feb (5:4feb)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
diff --git a/src/engine/ai/decks/im_ronald.asm b/src/engine/ai/decks/im_ronald.asm
new file mode 100644
index 0000000..b002d83
--- /dev/null
+++ b/src/engine/ai/decks/im_ronald.asm
@@ -0,0 +1,80 @@
+AIActionTable_ImRonald: ; 152bd (5:52bd)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 152c9 (5:52c9)
+ call AIMainTurnLogic
+ ret
+
+.start_duel ; 152cd (5:52cd)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+
+.forced_switch ; 152de (5:52de)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.ko_switch ; 152e2 (5:52e2)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.take_prize ; 152e6 (5:52e6)
+ call AIPickPrizeCards
+ ret
+
+.list_arena ; 152ea (5:52ea)
+ db LAPRAS
+ db SEEL
+ db CHARMANDER
+ db CUBONE
+ db SQUIRTLE
+ db GROWLITHE
+ db $00
+
+.list_bench ; 152f1 (5:52f1)
+ db CHARMANDER
+ db SQUIRTLE
+ db SEEL
+ db CUBONE
+ db GROWLITHE
+ db LAPRAS
+ db $00
+
+.list_retreat ; 152f8 (5:52f8)
+ db $00
+
+.list_energy ; 152f9 (5:52f9)
+ ai_energy CHARMANDER, 3, +0
+ ai_energy CHARMELEON, 5, +0
+ ai_energy GROWLITHE, 2, +0
+ ai_energy ARCANINE2, 4, +0
+ ai_energy SQUIRTLE, 2, +0
+ ai_energy WARTORTLE, 3, +0
+ ai_energy SEEL, 3, +0
+ ai_energy DEWGONG, 4, +0
+ ai_energy LAPRAS, 3, +0
+ ai_energy CUBONE, 3, +0
+ ai_energy MAROWAK1, 3, +0
+ db $00
+
+.list_prize ; 1531b (5:531b)
+ db LAPRAS
+ db $00
+
+.store_list_pointers ; 1531d (5:531d)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
diff --git a/src/engine/ai/decks/invincible_ronald.asm b/src/engine/ai/decks/invincible_ronald.asm
new file mode 100644
index 0000000..463560b
--- /dev/null
+++ b/src/engine/ai/decks/invincible_ronald.asm
@@ -0,0 +1,78 @@
+AIActionTable_InvincibleRonald: ; 153e8 (5:53e8)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 153f4 (5:53f4)
+ call AIMainTurnLogic
+ ret
+
+.start_duel ; 153f8 (5:53f8)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+
+.forced_switch ; 15409 (5:5409)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.ko_switch ; 1540d (5:540d)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.take_prize ; 15411 (5:5411)
+ call AIPickPrizeCards
+ ret
+
+.list_arena ; 15415 (5:5415)
+ db KANGASKHAN
+ db MAGMAR2
+ db CHANSEY
+ db GEODUDE
+ db SCYTHER
+ db GRIMER
+ db $00
+
+.list_bench ; 1541c (5:541c)
+ db GRIMER
+ db SCYTHER
+ db GEODUDE
+ db CHANSEY
+ db MAGMAR2
+ db KANGASKHAN
+ db $00
+
+.list_retreat ; 15423 (5:5423)
+ ai_retreat GRIMER, -1
+ db $00
+
+.list_energy ; 15426 (5:5426)
+ ai_energy GRIMER, 1, -1
+ ai_energy MUK, 3, -1
+ ai_energy SCYTHER, 4, +1
+ ai_energy MAGMAR2, 2, +0
+ ai_energy GEODUDE, 2, +0
+ ai_energy GRAVELER, 3, +0
+ ai_energy CHANSEY, 4, +0
+ ai_energy KANGASKHAN, 4, -1
+ db $00
+
+.list_prize ; 1543f (5:543f)
+ db GAMBLER
+ db $00
+
+.store_list_pointers ; 15441 (5:5441)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
diff --git a/src/engine/ai/decks/legendary_articuno.asm b/src/engine/ai/decks/legendary_articuno.asm
new file mode 100644
index 0000000..6409330
--- /dev/null
+++ b/src/engine/ai/decks/legendary_articuno.asm
@@ -0,0 +1,209 @@
+AIActionTable_LegendaryArticuno: ; 14c0b (5:4c0b)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 14c17 (5:4c17)
+ call AIDoTurn_LegendaryArticuno
+ ret
+
+.start_duel ; 14c1b (5:4c1b)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+
+.forced_switch ; 14c2c (5:4c2c)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.ko_switch ; 14c30 (5:4c30)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.take_prize ; 14c34 (5:4c34)
+ call AIPickPrizeCards
+ ret
+
+.list_arena ; 14c38 (5:4c38)
+ db CHANSEY
+ db LAPRAS
+ db DITTO
+ db SEEL
+ db ARTICUNO1
+ db ARTICUNO2
+ db $00
+
+.list_bench ; 14c3f (5:4c3f)
+ db ARTICUNO1
+ db SEEL
+ db LAPRAS
+ db CHANSEY
+ db DITTO
+ db $00
+
+.list_retreat ; 14c45 (5:4c45)
+ ai_retreat SEEL, -3
+ ai_retreat DITTO, -3
+ db $00
+
+.list_energy ; 14c4a (5:4c4a)
+ ai_energy SEEL, 3, +1
+ ai_energy DEWGONG, 4, +0
+ ai_energy LAPRAS, 3, +0
+ ai_energy ARTICUNO1, 4, +1
+ ai_energy ARTICUNO2, 3, +0
+ ai_energy CHANSEY, 0, -8
+ ai_energy DITTO, 3, +0
+ db $00
+
+.list_prize ; 14c60 (5:4c60)
+ db GAMBLER
+ db ARTICUNO2
+ db $00
+
+.store_list_pointers ; 14c63 (5:4c63)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
+
+; this routine handles how Legendary Articuno
+; prioritizes playing energy cards to each Pokémon.
+; first, it makes sure that all Lapras have at least
+; 3 energy cards before moving on to Articuno,
+; and then to Dewgong and Seel
+ScoreLegendaryArticunoCards: ; 14c91 (5:4c91)
+ call SwapTurn
+ call CountPrizes
+ call SwapTurn
+ cp 3
+ ret c
+
+; player prizes >= 3
+; if Lapras has more than half HP and
+; can use second attack, check next for Articuno
+; otherwise, check if Articuno or Dewgong
+; have more than half HP and can use second attack
+; and if so, the next Pokémon to check is Lapras
+ ld a, LAPRAS
+ call CheckForBenchIDAtHalfHPAndCanUseSecondAttack
+ jr c, .articuno
+ ld a, ARTICUNO1
+ call CheckForBenchIDAtHalfHPAndCanUseSecondAttack
+ jr c, .lapras
+ ld a, DEWGONG
+ call CheckForBenchIDAtHalfHPAndCanUseSecondAttack
+ jr c, .lapras
+ jr .articuno
+
+; the following routines check for certain card IDs in bench
+; and call RaiseAIScoreToAllMatchingIDsInBench if these are found.
+; for Lapras, an additional check is made to its
+; attached energy count, which skips calling the routine
+; if this count is >= 3
+.lapras
+ ld a, LAPRAS
+ ld b, PLAY_AREA_BENCH_1
+ call LookForCardIDInPlayArea_Bank5
+ jr nc, .articuno
+ ld e, a
+ call CountNumberOfEnergyCardsAttached
+ cp 3
+ jr nc, .articuno
+ ld a, LAPRAS
+ call RaiseAIScoreToAllMatchingIDsInBench
+ ret
+
+.articuno
+ ld a, ARTICUNO1
+ ld b, PLAY_AREA_BENCH_1
+ call LookForCardIDInPlayArea_Bank5
+ jr nc, .dewgong
+ ld a, ARTICUNO1
+ call RaiseAIScoreToAllMatchingIDsInBench
+ ret
+
+.dewgong
+ ld a, DEWGONG
+ ld b, PLAY_AREA_BENCH_1
+ call LookForCardIDInPlayArea_Bank5
+ jr nc, .seel
+ ld a, DEWGONG
+ call RaiseAIScoreToAllMatchingIDsInBench
+ ret
+
+.seel
+ ld a, SEEL
+ ld b, PLAY_AREA_BENCH_1
+ call LookForCardIDInPlayArea_Bank5
+ ret nc
+ ld a, SEEL
+ call RaiseAIScoreToAllMatchingIDsInBench
+ ret
+
+AIDoTurn_LegendaryArticuno: ; 14cf7 (5:4cf7)
+; initialize variables
+ call InitAITurnVars
+ ld a, AI_TRAINER_CARD_PHASE_01
+ call AIProcessHandTrainerCards
+ farcall HandleAIAntiMewtwoDeckStrategy
+ jp nc, .try_attack
+; process Trainer cards
+ ld a, AI_TRAINER_CARD_PHASE_02
+ call AIProcessHandTrainerCards
+; play Pokemon from hand
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+ call AIProcessRetreat
+ ld a, AI_TRAINER_CARD_PHASE_10
+ call AIProcessHandTrainerCards
+; play Energy card if possible
+ ld a, [wAlreadyPlayedEnergy]
+ or a
+ jr nz, .skip_energy_attach_1
+ call AIProcessAndTryToPlayEnergy
+.skip_energy_attach_1
+; play Pokemon from hand again
+ call AIDecidePlayPokemonCard
+; process Trainer cards phases 13 and 15
+ ld a, AI_TRAINER_CARD_PHASE_13
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_15
+ call AIProcessHandTrainerCards
+; if used Professor Oak, process new hand
+ ld a, [wPreviousAIFlags]
+ and AI_FLAG_USED_PROFESSOR_OAK
+ jr z, .try_attack
+ ld a, AI_TRAINER_CARD_PHASE_01
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_02
+ call AIProcessHandTrainerCards
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+ call AIProcessRetreat
+ ld a, AI_TRAINER_CARD_PHASE_10
+ call AIProcessHandTrainerCards
+ ld a, [wAlreadyPlayedEnergy]
+ or a
+ jr nz, .skip_energy_attach_2
+ call AIProcessAndTryToPlayEnergy
+.skip_energy_attach_2
+ call AIDecidePlayPokemonCard
+.try_attack
+; attack if possible, if not,
+; finish turn without attacking.
+ call AIProcessAndTryToUseAttack
+ ret c ; return if turn ended
+ ld a, OPPACTION_FINISH_NO_ATTACK
+ bank1call AIMakeDecision
+ ret
diff --git a/src/engine/ai/decks/legendary_dragonite.asm b/src/engine/ai/decks/legendary_dragonite.asm
new file mode 100644
index 0000000..597f72c
--- /dev/null
+++ b/src/engine/ai/decks/legendary_dragonite.asm
@@ -0,0 +1,166 @@
+AIActionTable_LegendaryDragonite: ; 14d60 (05:4d60)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 14d6c (5:4d6c)
+ call AIDoTurn_LegendaryDragonite
+ ret
+
+.start_duel ; 14d70 (5:4d70)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+
+.forced_switch ; 14d81 (5:4d81)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.ko_switch ; 14d85 (5:4d85)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.take_prize ; 14d89 (5:4d89)
+ call AIPickPrizeCards
+ ret
+
+.list_arena ; 14d8d (5:4d8d)
+ db KANGASKHAN
+ db LAPRAS
+ db CHARMANDER
+ db DRATINI
+ db MAGIKARP
+ db $00
+
+.list_bench ; 14d93 (5:4d93)
+ db CHARMANDER
+ db MAGIKARP
+ db DRATINI
+ db LAPRAS
+ db KANGASKHAN
+ db $00
+
+.list_retreat ; 14d99 (5:4d99)
+ ai_retreat CHARMANDER, -1
+ ai_retreat MAGIKARP, -5
+ db $00
+
+.list_energy ; 14d9e (5:4d9e)
+ ai_energy CHARMANDER, 3, +1
+ ai_energy CHARMELEON, 4, +1
+ ai_energy CHARIZARD, 5, +0
+ ai_energy MAGIKARP, 3, +1
+ ai_energy GYARADOS, 4, -1
+ ai_energy DRATINI, 2, +0
+ ai_energy DRAGONAIR, 4, +0
+ ai_energy DRAGONITE1, 3, -1
+ ai_energy KANGASKHAN, 2, -2
+ ai_energy LAPRAS, 3, +0
+ db $00
+
+.list_prize ; 14dbd (5:4dbd)
+ db GAMBLER
+ db DRAGONITE1
+ db KANGASKHAN
+ db $00
+
+.store_list_pointers ; 14dc1 (5:4dc1)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
+
+AIDoTurn_LegendaryDragonite: ; 14def (5:4def)
+; initialize variables
+ call InitAITurnVars
+ ld a, AI_TRAINER_CARD_PHASE_01
+ call AIProcessHandTrainerCards
+ farcall HandleAIAntiMewtwoDeckStrategy
+ jp nc, .try_attack
+; process Trainer cards
+ ld a, AI_TRAINER_CARD_PHASE_02
+ call AIProcessHandTrainerCards
+; play Pokemon from hand
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+ ld a, AI_TRAINER_CARD_PHASE_07
+ call AIProcessHandTrainerCards
+ call AIProcessRetreat
+ ld a, AI_TRAINER_CARD_PHASE_10
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_11
+ call AIProcessHandTrainerCards
+; play Energy card if possible
+ ld a, [wAlreadyPlayedEnergy]
+ or a
+ jr nz, .skip_energy_attach_1
+
+; if Arena card is Kangaskhan and doesn't
+; have Energy cards attached, try attaching from hand.
+; otherwise run normal AI energy attach routine.
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, KANGASKHAN
+ cp e
+ jr nz, .attach_normally
+ call CreateEnergyCardListFromHand
+ jr c, .skip_energy_attach_1
+ ld e, PLAY_AREA_ARENA
+ call CountNumberOfEnergyCardsAttached
+ or a
+ jr nz, .attach_normally
+ xor a
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call AITryToPlayEnergyCard
+ jr c, .skip_energy_attach_1
+.attach_normally
+ call AIProcessAndTryToPlayEnergy
+
+.skip_energy_attach_1
+; play Pokemon from hand again
+ call AIDecidePlayPokemonCard
+ ld a, AI_TRAINER_CARD_PHASE_15
+ call AIProcessHandTrainerCards
+; if used Professor Oak, process new hand
+; if not, then proceed to attack.
+ ld a, [wPreviousAIFlags]
+ and AI_FLAG_USED_PROFESSOR_OAK
+ jr z, .try_attack
+ ld a, AI_TRAINER_CARD_PHASE_01
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_02
+ call AIProcessHandTrainerCards
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+ ld a, AI_TRAINER_CARD_PHASE_07
+ call AIProcessHandTrainerCards
+ call AIProcessRetreat
+ ld a, AI_TRAINER_CARD_PHASE_10
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_11
+ call AIProcessHandTrainerCards
+ ld a, [wAlreadyPlayedEnergy]
+ or a
+ jr nz, .skip_energy_attach_2
+ call AIProcessAndTryToPlayEnergy
+.skip_energy_attach_2
+ call AIDecidePlayPokemonCard
+.try_attack
+; attack if possible, if not,
+; finish turn without attacking.
+ call AIProcessAndTryToUseAttack
+ ret c ; return if turn ended
+ ld a, OPPACTION_FINISH_NO_ATTACK
+ bank1call AIMakeDecision
+ ret
diff --git a/src/engine/ai/decks/legendary_moltres.asm b/src/engine/ai/decks/legendary_moltres.asm
new file mode 100644
index 0000000..d07afb9
--- /dev/null
+++ b/src/engine/ai/decks/legendary_moltres.asm
@@ -0,0 +1,176 @@
+AIActionTable_LegendaryMoltres: ; 149e8 (05:49e8)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 149f4 (5:49f4)
+ call AIDoTurn_LegendaryMoltres
+ ret
+
+.start_duel ; 149f8 (5:49f8)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc ; Play Area set up was successful
+ call AIPlayInitialBasicCards
+ ret
+
+.forced_switch ; 14a09 (5:4a09)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.ko_switch ; 14a0d (5:4a0d)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.take_prize ; 14a11 (5:4a11)
+ call AIPickPrizeCards
+ ret
+
+.list_arena ; 14a15 (5:4a15)
+ db MAGMAR2
+ db GROWLITHE
+ db VULPIX
+ db MAGMAR1
+ db MOLTRES1
+ db MOLTRES2
+ db $00
+
+.list_bench ; 14a1c (5:4a1c)
+ db MOLTRES1
+ db VULPIX
+ db GROWLITHE
+ db MAGMAR2
+ db MAGMAR1
+ db $00
+
+.list_play_hand ; 14a22 (5:4a22)
+ db MOLTRES2
+ db MOLTRES1
+ db VULPIX
+ db GROWLITHE
+ db MAGMAR2
+ db MAGMAR1
+ db $00
+
+.list_retreat ; 14a29 (5:4a29)
+ ai_retreat GROWLITHE, -5
+ ai_retreat VULPIX, -5
+ db $00
+
+.list_energy ; 14a2e (5:4a2e)
+ ai_energy VULPIX, 3, +0
+ ai_energy NINETAILS2, 3, +1
+ ai_energy GROWLITHE, 3, +1
+ ai_energy ARCANINE2, 4, +1
+ ai_energy MAGMAR1, 4, -1
+ ai_energy MAGMAR2, 1, -1
+ ai_energy MOLTRES2, 3, +2
+ ai_energy MOLTRES1, 4, +2
+ db $00
+
+.list_prize ; 14a47 (5:4a47)
+ db ENERGY_REMOVAL
+ db MOLTRES2
+ db $00
+
+.store_list_pointers ; 14a4a (5:4a4a)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_play_hand
+ store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
+
+AIDoTurn_LegendaryMoltres: ; 14a81 (5:4a81)
+; initialize variables
+ call InitAITurnVars
+ farcall HandleAIAntiMewtwoDeckStrategy
+ jp nc, .try_attack
+; process Trainer cards
+; phase 2 through 4.
+ ld a, AI_TRAINER_CARD_PHASE_02
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_04
+ call AIProcessHandTrainerCards
+
+; check if AI can play Moltres2
+; from hand and if so, play it.
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp MAX_PLAY_AREA_POKEMON
+ jr nc, .skip_moltres ; skip if bench is full
+ ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
+ call GetTurnDuelistVariable
+ cp DECK_SIZE - 9
+ jr nc, .skip_moltres ; skip if cards in deck <= 9
+ ld a, MUK
+ call CountPokemonIDInBothPlayAreas
+ jr c, .skip_moltres ; skip if Muk in play
+ ld a, MOLTRES2
+ call LookForCardIDInHandList_Bank5
+ jr nc, .skip_moltres ; skip if no Moltres2 in hand
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_PLAY_BASIC_PKMN
+ bank1call AIMakeDecision
+
+.skip_moltres
+; play Pokemon from hand
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+; process Trainer cards
+ ld a, AI_TRAINER_CARD_PHASE_05
+ call AIProcessHandTrainerCards
+ call AIProcessRetreat
+ ld a, AI_TRAINER_CARD_PHASE_10
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_11
+ call AIProcessHandTrainerCards
+; play Energy card if possible
+ ld a, [wAlreadyPlayedEnergy]
+ or a
+ jr nz, .skip_attach_energy
+
+; if Magmar2 is the Arena card and has no energy attached,
+; try attaching an energy card to it from the hand.
+; otherwise, run normal AI energy attach routine.
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, MAGMAR2
+ cp e
+ jr nz, .attach_normally
+ ; Magmar2 is the Arena card
+ call CreateEnergyCardListFromHand
+ jr c, .skip_attach_energy
+ ld e, PLAY_AREA_ARENA
+ call CountNumberOfEnergyCardsAttached
+ or a
+ jr nz, .attach_normally
+ xor a ; PLAY_AREA_ARENA
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call AITryToPlayEnergyCard
+ jr c, .skip_attach_energy
+
+.attach_normally
+; play Energy card if possible
+ call AIProcessAndTryToPlayEnergy
+.skip_attach_energy
+; try playing Pokemon cards from hand again
+ call AIDecidePlayPokemonCard
+ ld a, AI_TRAINER_CARD_PHASE_13
+ call AIProcessHandTrainerCards
+
+.try_attack
+; attack if possible, if not,
+; finish turn without attacking.
+ call AIProcessAndTryToUseAttack
+ ret c
+ ld a, OPPACTION_FINISH_NO_ATTACK
+ bank1call AIMakeDecision
+ ret
diff --git a/src/engine/ai/decks/legendary_ronald.asm b/src/engine/ai/decks/legendary_ronald.asm
new file mode 100644
index 0000000..3356838
--- /dev/null
+++ b/src/engine/ai/decks/legendary_ronald.asm
@@ -0,0 +1,203 @@
+AIActionTable_LegendaryRonald: ; 1546f (5:546f)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 1547b (5:547b)
+ call AIDoTurn_LegendaryRonald
+ ret
+
+.start_duel ; 1547f (5:547f)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+
+.forced_switch ; 15490 (5:5490)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.ko_switch ; 15494 (5:5494)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.take_prize ; 15498 (5:5498)
+ call AIPickPrizeCards
+ ret
+
+.list_arena ; 1549c (5:549c)
+ db KANGASKHAN
+ db DRATINI
+ db EEVEE
+ db ZAPDOS3
+ db ARTICUNO2
+ db MOLTRES2
+ db $00
+
+.list_bench ; 154a3 (5:54a3)
+ db KANGASKHAN
+ db DRATINI
+ db EEVEE
+ db $00
+
+.list_play_hand ; 154a7 (5:54a7)
+ db MOLTRES2
+ db ZAPDOS3
+ db KANGASKHAN
+ db DRATINI
+ db EEVEE
+ db ARTICUNO2
+ db $00
+
+.list_retreat ; 154ae (5:54ae)
+ ai_retreat EEVEE, -2
+ db $00
+
+.list_energy ; 154b1 (5:54b1)
+ ai_energy FLAREON1, 3, +0
+ ai_energy MOLTRES2, 3, +0
+ ai_energy VAPOREON1, 3, +0
+ ai_energy ARTICUNO2, 0, -8
+ ai_energy JOLTEON1, 4, +0
+ ai_energy ZAPDOS3, 0, -8
+ ai_energy KANGASKHAN, 4, -1
+ ai_energy EEVEE, 3, +0
+ ai_energy DRATINI, 3, +0
+ ai_energy DRAGONAIR, 4, +0
+ ai_energy DRAGONITE1, 3, +0
+ db $00
+
+.list_prize ; 154d3 (5:54d3)
+ db MOLTRES2
+ db ARTICUNO2
+ db ZAPDOS3
+ db DRAGONITE1
+ db GAMBLER
+ db $00
+
+.store_list_pointers ; 154d9 (5:54d9)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_play_hand
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
+
+AIDoTurn_LegendaryRonald: ; 15507 (5:5507)
+; initialize variables
+ call InitAITurnVars
+; process Trainer cards
+ ld a, AI_TRAINER_CARD_PHASE_01
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_02
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_04
+ call AIProcessHandTrainerCards
+
+; check if AI can play Moltres2
+; from hand and if so, play it.
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp MAX_PLAY_AREA_POKEMON
+ jr nc, .skip_moltres_1 ; skip if bench is full
+ ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
+ call GetTurnDuelistVariable
+ cp DECK_SIZE - 9
+ jr nc, .skip_moltres_1 ; skip if cards in deck <= 9
+ ld a, MUK
+ call CountPokemonIDInBothPlayAreas
+ jr c, .skip_moltres_1 ; skip if Muk in play
+ ld a, MOLTRES2
+ call LookForCardIDInHandList_Bank5
+ jr nc, .skip_moltres_1 ; skip if no Moltres2 in hand
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_PLAY_BASIC_PKMN
+ bank1call AIMakeDecision
+
+.skip_moltres_1
+; play Pokemon from hand
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+; process Trainer cards
+ ld a, AI_TRAINER_CARD_PHASE_05
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_07
+ call AIProcessHandTrainerCards
+ call AIProcessRetreat
+ ld a, AI_TRAINER_CARD_PHASE_10
+ call AIProcessHandTrainerCards
+; play Energy card if possible
+ ld a, [wAlreadyPlayedEnergy]
+ or a
+ jr nz, .skip_attach_energy_1
+ call AIProcessAndTryToPlayEnergy
+.skip_attach_energy_1
+; try playing Pokemon cards from hand again
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+ ld a, AI_TRAINER_CARD_PHASE_15
+; if used Professor Oak, process new hand
+; if not, then proceed to attack.
+ call AIProcessHandTrainerCards
+ ld a, [wPreviousAIFlags]
+ and AI_FLAG_USED_PROFESSOR_OAK
+ jr z, .try_attack
+ ld a, AI_TRAINER_CARD_PHASE_01
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_02
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_04
+ call AIProcessHandTrainerCards
+
+; check if AI can play Moltres2
+; from hand and if so, play it.
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp MAX_PLAY_AREA_POKEMON
+ jr nc, .skip_moltres_2 ; skip if bench is full
+ ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
+ call GetTurnDuelistVariable
+ cp DECK_SIZE - 9
+ jr nc, .skip_moltres_2 ; skip if cards in deck <= 9
+ ld a, MUK
+ call CountPokemonIDInBothPlayAreas
+ jr c, .skip_moltres_2 ; skip if Muk in play
+ ld a, MOLTRES2
+ call LookForCardIDInHandList_Bank5
+ jr nc, .skip_moltres_2 ; skip if no Moltres2 in hand
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_PLAY_BASIC_PKMN
+ bank1call AIMakeDecision
+
+.skip_moltres_2
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+ ld a, AI_TRAINER_CARD_PHASE_05
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_07
+ call AIProcessHandTrainerCards
+ call AIProcessRetreat
+ ld a, AI_TRAINER_CARD_PHASE_10
+ call AIProcessHandTrainerCards
+ ld a, [wAlreadyPlayedEnergy]
+ or a
+ jr nz, .skip_attach_energy_2
+ call AIProcessAndTryToPlayEnergy
+.skip_attach_energy_2
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+.try_attack
+; attack if possible, if not,
+; finish turn without attacking.
+ call AIProcessAndTryToUseAttack
+ ret c ; return if turn ended
+ ld a, OPPACTION_FINISH_NO_ATTACK
+ bank1call AIMakeDecision
+ ret
diff --git a/src/engine/ai/decks/legendary_zapdos.asm b/src/engine/ai/decks/legendary_zapdos.asm
new file mode 100644
index 0000000..cc99f0c
--- /dev/null
+++ b/src/engine/ai/decks/legendary_zapdos.asm
@@ -0,0 +1,153 @@
+AIActionTable_LegendaryZapdos: ; 14b0f (05:4b0f)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 14b1b (5:4b1b)
+ call AIDoTurn_LegendaryZapdos
+ ret
+
+.start_duel ; 14b1f (5:4b1f)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+
+.forced_switch ; 14b30 (5:4b30)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.ko_switch ; 14b34 (5:4b34)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.take_prize ; 14b38 (5:4b38)
+ call AIPickPrizeCards
+ ret
+
+.list_arena ; 14b3c (5:4b3c)
+ db ELECTABUZZ2
+ db VOLTORB
+ db EEVEE
+ db ZAPDOS1
+ db ZAPDOS2
+ db ZAPDOS3
+ db $00
+
+.list_bench ; 14b43 (5:4b43)
+ db ZAPDOS2
+ db ZAPDOS1
+ db EEVEE
+ db VOLTORB
+ db ELECTABUZZ2
+ db $00
+
+.list_retreat ; 14b49 (5:4b49)
+ ai_retreat EEVEE, -5
+ ai_retreat VOLTORB, -5
+ ai_retreat ELECTABUZZ2, -5
+ db $00
+
+.list_energy ; 14b50 (5:4b50)
+ ai_energy VOLTORB, 1, -1
+ ai_energy ELECTRODE1, 3, +0
+ ai_energy ELECTABUZZ2, 2, -1
+ ai_energy JOLTEON2, 3, +1
+ ai_energy ZAPDOS1, 4, +2
+ ai_energy ZAPDOS2, 4, +2
+ ai_energy ZAPDOS3, 3, +1
+ ai_energy EEVEE, 3, +0
+ db $00
+
+.list_prize ; 14b69 (5:4b69)
+ db GAMBLER
+ db ZAPDOS3
+ db $00
+
+.store_list_pointers ; 14b6c (5:4b6c)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
+
+AIDoTurn_LegendaryZapdos: ; 14b9a (5:4b9a)
+; initialize variables
+ call InitAITurnVars
+ farcall HandleAIAntiMewtwoDeckStrategy
+ jp nc, .try_attack
+; process Trainer cards
+ ld a, AI_TRAINER_CARD_PHASE_01
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_04
+ call AIProcessHandTrainerCards
+; play Pokemon from hand
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+ ld a, AI_TRAINER_CARD_PHASE_07
+ call AIProcessHandTrainerCards
+ call AIProcessRetreat
+ ld a, AI_TRAINER_CARD_PHASE_10
+ call AIProcessHandTrainerCards
+; play Energy card if possible.
+ ld a, [wAlreadyPlayedEnergy]
+ or a
+ jr nz, .skip_energy_attach
+
+; if Arena card is Voltorb and there's Electrode1 in hand,
+; or if it's Electabuzz, try attaching Energy card
+; to the Arena card if it doesn't have any energy attached.
+; Otherwise if Energy card is not needed,
+; go through normal AI energy attach routine.
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, VOLTORB
+ cp e
+ jr nz, .check_electabuzz
+ ld a, ELECTRODE1
+ call LookForCardIDInHandList_Bank5
+ jr nc, .attach_normally
+ jr .voltorb_or_electabuzz
+.check_electabuzz
+ ld a, ELECTABUZZ2
+ cp e
+ jr nz, .attach_normally
+
+.voltorb_or_electabuzz
+ call CreateEnergyCardListFromHand
+ jr c, .skip_energy_attach
+ ld e, PLAY_AREA_ARENA
+ call CountNumberOfEnergyCardsAttached
+ or a
+ jr nz, .attach_normally
+ xor a ; PLAY_AREA_ARENA
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call AITryToPlayEnergyCard
+ jr c, .skip_energy_attach
+
+.attach_normally
+ call AIProcessAndTryToPlayEnergy
+
+.skip_energy_attach
+; play Pokemon from hand again
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+ ld a, AI_TRAINER_CARD_PHASE_13
+ call AIProcessHandTrainerCards
+.try_attack
+; attack if possible, if not,
+; finish turn without attacking.
+ call AIProcessAndTryToUseAttack
+ ret c ; return if turn ended
+ ld a, OPPACTION_FINISH_NO_ATTACK
+ bank1call AIMakeDecision
+ ret
diff --git a/src/engine/ai/decks/powerful_ronald.asm b/src/engine/ai/decks/powerful_ronald.asm
new file mode 100644
index 0000000..096fbea
--- /dev/null
+++ b/src/engine/ai/decks/powerful_ronald.asm
@@ -0,0 +1,92 @@
+AIActionTable_PowerfulRonald: ; 1534b (5:534b)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 15357 (5:5357)
+ call AIMainTurnLogic
+ ret
+
+.start_duel ; 1535b (5:535b)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+
+.forced_switch ; 1536c (5:536c)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.ko_switch ; 15370 (5:5370)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.take_prize ; 15374 (5:5374)
+ call AIPickPrizeCards
+ ret
+
+.list_arena ; 15378 (5:5378)
+ db KANGASKHAN
+ db ELECTABUZZ2
+ db HITMONCHAN
+ db MR_MIME
+ db LICKITUNG
+ db HITMONLEE
+ db TAUROS
+ db JYNX
+ db MEWTWO1
+ db DODUO
+ db $00
+
+.list_bench ; 15383 (5:5383)
+ db KANGASKHAN
+ db HITMONLEE
+ db HITMONCHAN
+ db TAUROS
+ db DODUO
+ db JYNX
+ db MEWTWO1
+ db ELECTABUZZ2
+ db MR_MIME
+ db LICKITUNG
+ db $00
+
+.list_retreat ; 1538e (5:538e)
+ ai_retreat KANGASKHAN, -1
+ ai_retreat DODUO, -1
+ ai_retreat DODRIO, -1
+ db $00
+
+.list_energy ; 15395 (5:5395)
+ ai_energy ELECTABUZZ2, 2, +1
+ ai_energy HITMONLEE, 3, +1
+ ai_energy HITMONCHAN, 3, +1
+ ai_energy MR_MIME, 2, +0
+ ai_energy JYNX, 3, +0
+ ai_energy MEWTWO1, 2, +0
+ ai_energy DODUO, 3, -1
+ ai_energy DODRIO, 3, -1
+ ai_energy LICKITUNG, 2, +0
+ ai_energy KANGASKHAN, 4, -1
+ ai_energy TAUROS, 3, +0
+ db $00
+
+.list_prize ; 153b7 (5:53b7)
+ db GAMBLER
+ db ENERGY_REMOVAL
+ db $00
+
+.store_list_pointers ; 153ba (5:53ba)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
diff --git a/src/engine/ai/decks/rock_crusher.asm b/src/engine/ai/decks/rock_crusher.asm
new file mode 100644
index 0000000..41a50fa
--- /dev/null
+++ b/src/engine/ai/decks/rock_crusher.asm
@@ -0,0 +1,74 @@
+AIActionTable_RockCrusher: ; 14f0e (5:4f0e)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 14f1a (5:4f1a)
+ call AIMainTurnLogic
+ ret
+
+.start_duel ; 14f1e (5:4f1e)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+
+.forced_switch ; 14f2f (5:4f2f)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.ko_switch ; 14f33 (5:4f33)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.take_prize ; 14f37 (5:4f37)
+ call AIPickPrizeCards
+ ret
+
+.list_arena ; 14f3b (5:4f3b)
+ db RHYHORN
+ db ONIX
+ db GEODUDE
+ db DIGLETT
+ db $00
+
+.list_bench ; 14f40 (5:4f40)
+ db DIGLETT
+ db GEODUDE
+ db RHYHORN
+ db ONIX
+ db $00
+
+.list_retreat ; 14f45 (5:4f45)
+ ai_retreat DIGLETT, -1
+ db $00
+
+.list_energy ; 14f48 (5:4f48)
+ ai_energy DIGLETT, 3, +1
+ ai_energy DUGTRIO, 4, +0
+ ai_energy GEODUDE, 2, +1
+ ai_energy GRAVELER, 3, +0
+ ai_energy GOLEM, 4, +0
+ ai_energy ONIX, 2, -1
+ ai_energy RHYHORN, 3, +0
+ db $00
+
+.list_prize ; 14f5e (5:4f5e)
+ db ENERGY_REMOVAL
+ db RHYHORN
+ db $00
+
+.store_list_pointers ; 14f61 (5:4f61)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
diff --git a/src/engine/ai/decks/sams_practice.asm b/src/engine/ai/decks/sams_practice.asm
new file mode 100644
index 0000000..dddce61
--- /dev/null
+++ b/src/engine/ai/decks/sams_practice.asm
@@ -0,0 +1,205 @@
+; AI for Sam's practice duel, which handles his scripted actions.
+; will act as a normal duelist AI after turn 7.
+AIActionTable_SamPractice: ; 147bd (05:47bd)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 147c9 (5:47c9)
+ call IsAIPracticeScriptedTurn
+ jr nc, .scripted_1
+; not scripted, use AI main turn logic
+ call AIMainTurnLogic
+ ret
+.scripted_1 ; use scripted actions instead
+ call AIPerformScriptedTurn
+ ret
+
+.start_duel ; 147d6 (5:47d6)
+ call SetSamsStartingPlayArea
+ ret
+
+.forced_switch ; 147da (5:47da)
+ call IsAIPracticeScriptedTurn
+ jr nc, .scripted_2
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+.scripted_2
+ call PickRandomBenchPokemon
+ ret
+
+.ko_switch: ; 147e7 (5:47e7)
+ call IsAIPracticeScriptedTurn
+ jr nc, .scripted_3
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+.scripted_3
+ call GetPlayAreaLocationOfRaticateOrRattata
+ ret
+
+.take_prize: ; 147f4 (5:47f4)
+ call AIPickPrizeCards
+ ret
+
+; returns carry if number of turns
+; the AI has taken >= 7.
+; used to know whether AI Sam is still
+; doing scripted turns.
+IsAIPracticeScriptedTurn: ; 147f8 (5:47f8)
+ ld a, [wDuelTurns]
+ srl a
+ cp 7
+ ccf
+ ret
+
+; places one Machop from the hand to the Play Area
+; and sets the number of prizes to 2.
+SetSamsStartingPlayArea: ; 14801 (5:4801)
+ call CreateHandCardList
+ ld hl, wDuelTempList
+.loop_hand
+ ld a, [hli]
+ ldh [hTempCardIndex_ff98], a
+ cp $ff
+ ret z
+ call LoadCardDataToBuffer1_FromDeckIndex
+ cp MACHOP
+ jr nz, .loop_hand
+ ldh a, [hTempCardIndex_ff98]
+ call PutHandPokemonCardInPlayArea
+ ld a, 2
+ ld [wDuelInitialPrizes], a
+ ret
+
+; outputs in a Play Area location of Raticate or Rattata
+; in the Bench. If neither is found, just output PLAY_AREA_BENCH_1.
+GetPlayAreaLocationOfRaticateOrRattata: ; 1481f (5:481f)
+ ld a, RATICATE
+ ld b, PLAY_AREA_BENCH_1
+ call LookForCardIDInPlayArea_Bank5
+ cp $ff
+ jr nz, .found
+ ld a, RATTATA
+ ld b, PLAY_AREA_BENCH_1
+ call LookForCardIDInPlayArea_Bank5
+ cp $ff
+ jr nz, .found
+ ld a, PLAY_AREA_BENCH_1
+.found
+ ldh [hTempPlayAreaLocation_ff9d], a
+ ret
+
+; has AI execute some scripted actions depending on Duel turn.
+AIPerformScriptedTurn: ; 1483a (5:483a)
+ ld a, [wDuelTurns]
+ srl a
+ ld hl, .scripted_actions_list
+ call JumpToFunctionInTable
+
+; always attack with Arena card's first attack.
+; if it's unusable end turn without attacking.
+ xor a
+ ldh [hTempPlayAreaLocation_ff9d], a
+ ld [wSelectedAttack], a
+ call CheckIfSelectedAttackIsUnusable
+ jr c, .unusable
+ call AITryUseAttack
+ ret
+
+.unusable
+ ld a, OPPACTION_FINISH_NO_ATTACK
+ bank1call AIMakeDecision
+ ret
+
+.scripted_actions_list ; 1485a (05:485a)
+ dw .turn_1
+ dw .turn_2
+ dw .turn_3
+ dw .turn_4
+ dw .turn_5
+ dw .turn_6
+ dw .turn_7
+
+.turn_1 ; 14868 (5:4868)
+ ld d, MACHOP
+ ld e, FIGHTING_ENERGY
+ call AIAttachEnergyInHandToCardInPlayArea
+ ret
+
+.turn_2 ; 14870 (5:4870)
+ ld a, RATTATA
+ call LookForCardIDInHandList_Bank5
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_PLAY_BASIC_PKMN
+ bank1call AIMakeDecision
+ ld d, RATTATA
+ ld e, FIGHTING_ENERGY
+ call AIAttachEnergyInHandToCardInPlayArea
+ ret
+
+.turn_3 ; 14884 (5:4884)
+ ld a, RATTATA
+ ld b, PLAY_AREA_ARENA
+ call LookForCardIDInPlayArea_Bank5
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, RATICATE
+ call LookForCardIDInHandList_Bank5
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_EVOLVE_PKMN
+ bank1call AIMakeDecision
+ ld d, RATICATE
+ ld e, LIGHTNING_ENERGY
+ call AIAttachEnergyInHandToCardInPlayArea
+ ret
+
+.turn_4 ; 148a1 (5:48a1)
+ ld d, RATICATE
+ ld e, LIGHTNING_ENERGY
+ call AIAttachEnergyInHandToCardInPlayArea
+ ret
+
+.turn_5 ; 148a9 (5:48a9)
+ ld a, MACHOP
+ call LookForCardIDInHandList_Bank5
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_PLAY_BASIC_PKMN
+ bank1call AIMakeDecision
+ ld d, MACHOP
+ ld e, FIGHTING_ENERGY
+ call AIAttachEnergyInHandToCardInBench
+
+; this is a bug, it's attempting to compare a card ID with a deck index.
+; the intention was to change the card to switch to depending on whether
+; the first Machop was KO'd at this point in the Duel or not.
+; because of the buggy comparison, this will always jump the
+; 'inc a' instruction and switch to PLAY_AREA_BENCH_1.
+; in a normal Practice Duel following Dr. Mason's instructions,
+; this will always lead to the AI correctly switching Raticate with Machop,
+; but in case of a "Free" Duel where the first Machop is not KO'd,
+; the intention was to switch to PLAY_AREA_BENCH_2 instead.
+; but due to 'inc a' always being skipped, it will switch to Raticate.
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ cp MACHOP ; wrong
+ ld a, PLAY_AREA_BENCH_1
+ jr nz, .retreat
+ inc a ; PLAY_AREA_BENCH_2
+
+.retreat
+ call AITryToRetreat
+ ret
+
+.turn_6 ; 148cc (5:48cc)
+ ld d, MACHOP
+ ld e, FIGHTING_ENERGY
+ call AIAttachEnergyInHandToCardInPlayArea
+ ret
+
+.turn_7 ; 148d4 (5:48d4)
+ ld d, MACHOP
+ ld e, FIGHTING_ENERGY
+ call AIAttachEnergyInHandToCardInPlayArea
+ ret
diff --git a/src/engine/ai/decks/strange_psyshock.asm b/src/engine/ai/decks/strange_psyshock.asm
new file mode 100644
index 0000000..ef378b0
--- /dev/null
+++ b/src/engine/ai/decks/strange_psyshock.asm
@@ -0,0 +1,81 @@
+AIActionTable_StrangePsyshock: ; 15122 (5:5122)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 1512e (5:512e)
+ call AIMainTurnLogic
+ ret
+
+.start_duel ; 15132 (5:5132)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+
+.forced_switch ; 15143 (5:5143)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.ko_switch ; 15147 (5:5147)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.take_prize ; 1514b (5:514b)
+ call AIPickPrizeCards
+ ret
+
+.list_arena ; 1514f (5:514f)
+ db KANGASKHAN
+ db CHANSEY
+ db SNORLAX
+ db MR_MIME
+ db ABRA
+ db $00
+
+.list_bench ; 15155 (5:5155)
+ db ABRA
+ db MR_MIME
+ db KANGASKHAN
+ db SNORLAX
+ db CHANSEY
+ db $00
+
+.list_retreat ; 1515b (5:515b)
+ ai_retreat ABRA, -3
+ ai_retreat SNORLAX, -3
+ ai_retreat KANGASKHAN, -1
+ ai_retreat CHANSEY, -1
+ db $00
+
+.list_energy ; 15164 (5:5164)
+ ai_energy ABRA, 3, +1
+ ai_energy KADABRA, 3, +0
+ ai_energy ALAKAZAM, 3, +0
+ ai_energy MR_MIME, 2, +0
+ ai_energy CHANSEY, 2, -2
+ ai_energy KANGASKHAN, 4, -2
+ ai_energy SNORLAX, 0, -8
+ db $00
+
+.list_prize ; 1517a (5:517a)
+ db GAMBLER
+ db MR_MIME
+ db ALAKAZAM
+ db SWITCH
+ db $00
+
+.store_list_pointers ; 1517f (5:517f)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
diff --git a/src/engine/ai/decks/wonders_of_science.asm b/src/engine/ai/decks/wonders_of_science.asm
new file mode 100644
index 0000000..706a7e6
--- /dev/null
+++ b/src/engine/ai/decks/wonders_of_science.asm
@@ -0,0 +1,77 @@
+AIActionTable_WondersOfScience: ; 151ad (5:51ad)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 151b9 (5:51b9)
+ call AIMainTurnLogic
+ ret
+
+.start_duel ; 151bd (5:51bd)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+
+.forced_switch ; 151ce (5:51ce)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.ko_switch ; 151d2 (5:51d2)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.take_prize ; 151d6 (5:51d6)
+ call AIPickPrizeCards
+ ret
+
+.list_arena ; 151da (5:51da)
+ db MEWTWO1
+ db MEWTWO3
+ db MEWTWO2
+ db GRIMER
+ db KOFFING
+ db PORYGON
+ db $00
+
+.list_bench ; 151e1 (5:51e1)
+ db GRIMER
+ db KOFFING
+ db MEWTWO3
+ db MEWTWO2
+ db MEWTWO1
+ db PORYGON
+ db $00
+
+.list_retreat ; 151e8 (5:51e8)
+ db $00
+
+.list_energy ; 151e9 (5:51e9)
+ ai_energy GRIMER, 3, +0
+ ai_energy MUK, 4, +0
+ ai_energy KOFFING, 2, +0
+ ai_energy WEEZING, 3, +0
+ ai_energy MEWTWO1, 2, -1
+ ai_energy MEWTWO3, 2, -1
+ ai_energy MEWTWO2, 2, -1
+ ai_energy PORYGON, 2, -1
+ db $00
+
+.list_prize ; 15202 (5:5202)
+ db MUK
+ db $00
+
+.store_list_pointers ; 15204 (5:5204)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
diff --git a/src/engine/ai/decks/zapping_selfdestruct.asm b/src/engine/ai/decks/zapping_selfdestruct.asm
new file mode 100644
index 0000000..da5e7c6
--- /dev/null
+++ b/src/engine/ai/decks/zapping_selfdestruct.asm
@@ -0,0 +1,75 @@
+AIActionTable_ZappingSelfdestruct: ; 15019 (5:5019)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 15025 (5:5025)
+ call AIMainTurnLogic
+ ret
+
+.start_duel ; 15029 (5:5029)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+
+.forced_switch ; 1503a (5:503a)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.ko_switch ; 1503e (5:503e)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.take_prize ; 15042 (5:5042)
+ call AIPickPrizeCards
+ ret
+
+.list_arena ; 15046 (5:5046)
+ db KANGASKHAN
+ db ELECTABUZZ2
+ db TAUROS
+ db MAGNEMITE1
+ db VOLTORB
+ db $00
+
+.list_bench ; 1504c (5:504c)
+ db MAGNEMITE1
+ db VOLTORB
+ db ELECTABUZZ2
+ db TAUROS
+ db KANGASKHAN
+ db $00
+
+.list_retreat ; 15052 (5:5052)
+ ai_retreat VOLTORB, -1
+ db $00
+
+.list_energy ; 15055 (5:5055)
+ ai_energy MAGNEMITE1, 3, +1
+ ai_energy MAGNETON1, 4, +0
+ ai_energy VOLTORB, 3, +1
+ ai_energy ELECTRODE1, 3, +0
+ ai_energy ELECTABUZZ2, 1, +0
+ ai_energy KANGASKHAN, 2, -2
+ ai_energy TAUROS, 3, +0
+ db $00
+
+.list_prize ; 1506b (5:506b)
+ db KANGASKHAN
+ db $00
+
+.store_list_pointers ; 1506d (5:506d)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
diff --git a/src/engine/ai/pkmn_powers.asm b/src/engine/ai/pkmn_powers.asm
new file mode 100644
index 0000000..52a8036
--- /dev/null
+++ b/src/engine/ai/pkmn_powers.asm
@@ -0,0 +1,1228 @@
+; handle AI routines for Energy Trans.
+; uses AI_ENERGY_TRANS_* constants as input:
+; - AI_ENERGY_TRANS_RETREAT: transfers enough Grass Energy cards to
+; Arena Pokemon for it to be able to pay the Retreat Cost;
+; - AI_ENERGY_TRANS_ATTACK: transfers enough Grass Energy cards to
+; Arena Pokemon for it to be able to use its second attack;
+; - AI_ENERGY_TRANS_TO_BENCH: transfers all Grass Energy cards from
+; Arena Pokemon to Bench in case Arena card will be KO'd.
+HandleAIEnergyTrans: ; 2219b (8:619b)
+ ld [wce06], a
+
+; choose to randomly return
+ farcall AIChooseRandomlyNotToDoAction
+ ret c
+
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ dec a
+ ret z ; return if no Bench cards
+
+ ld a, VENUSAUR2
+ call CountPokemonIDInPlayArea
+ ret nc ; return if no Venusaur2 found in own Play Area
+
+ ld a, MUK
+ call CountPokemonIDInBothPlayAreas
+ ret c ; return if Muk found in any Play Area
+
+ ld a, [wce06]
+ cp AI_ENERGY_TRANS_RETREAT
+ jr z, .check_retreat
+
+ cp AI_ENERGY_TRANS_TO_BENCH
+ jp z, AIEnergyTransTransferEnergyToBench
+
+ ; AI_ENERGY_TRANS_ATTACK
+ call .CheckEnoughGrassEnergyCardsForAttack
+ ret nc
+ jr .TransferEnergyToArena
+
+.check_retreat
+ call .CheckEnoughGrassEnergyCardsForRetreatCost
+ ret nc
+
+; use Energy Trans to transfer number of Grass energy cards
+; equal to input a from the Bench to the Arena card.
+.TransferEnergyToArena
+ ld [wAINumberOfEnergyTransCards], a
+
+; look for Venusaur2 in Play Area
+; so that its PKMN Power can be used.
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ dec a
+ ld b, a
+.loop_play_area
+ ld a, DUELVARS_ARENA_CARD
+ add b
+ call GetTurnDuelistVariable
+ ldh [hTempCardIndex_ff9f], a
+ call GetCardIDFromDeckIndex
+ ld a, e
+ cp VENUSAUR2
+ jr z, .use_pkmn_power
+
+ ld a, b
+ or a
+ ret z ; return when finished Play Area loop
+
+ dec b
+ jr .loop_play_area
+
+; use Energy Trans Pkmn Power
+.use_pkmn_power
+ ld a, b
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_USE_PKMN_POWER
+ bank1call AIMakeDecision
+ ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT
+ bank1call AIMakeDecision
+
+ xor a ; PLAY_AREA_ARENA
+ ldh [hAIEnergyTransPlayAreaLocation], a
+ ld a, [wAINumberOfEnergyTransCards]
+ ld d, a
+
+; look for Grass energy cards that
+; are currently attached to a Bench card.
+ ld e, 0
+.loop_deck_locations
+ ld a, DUELVARS_CARD_LOCATIONS
+ add e
+ call GetTurnDuelistVariable
+ and %00011111
+ cp CARD_LOCATION_BENCH_1
+ jr c, .next_card
+
+ and %00001111
+ ldh [hTempPlayAreaLocation_ffa1], a
+
+ ld a, e
+ push de
+ call GetCardIDFromDeckIndex
+ ld a, e
+ pop de
+ cp GRASS_ENERGY
+ jr nz, .next_card
+
+ ; store the deck index of energy card
+ ld a, e
+ ldh [hAIEnergyTransEnergyCard], a
+
+ push de
+ ld d, 30
+.small_delay_loop
+ call DoFrame
+ dec d
+ jr nz, .small_delay_loop
+
+ ld a, OPPACTION_6B15
+ bank1call AIMakeDecision
+ pop de
+ dec d
+ jr z, .done_transfer
+
+.next_card
+ inc e
+ ld a, DECK_SIZE
+ cp e
+ jr nz, .loop_deck_locations
+
+; transfer is done, perform delay
+; and return to main scene.
+.done_transfer
+ ld d, 60
+.big_delay_loop
+ call DoFrame
+ dec d
+ jr nz, .big_delay_loop
+ ld a, OPPACTION_DUEL_MAIN_SCENE
+ bank1call AIMakeDecision
+ ret
+
+; checks if the Arena card needs energy for its second attack,
+; and if it does, return carry if transferring Grass energy from Bench
+; would be enough to use it. Outputs number of energy cards needed in a.
+.CheckEnoughGrassEnergyCardsForAttack ; 22246 (8:6246)
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ cp EXEGGUTOR
+ jr z, .is_exeggutor
+
+ xor a ; PLAY_AREA_ARENA
+ ldh [hTempPlayAreaLocation_ff9d], a
+ ld a, SECOND_ATTACK
+ ld [wSelectedAttack], a
+ farcall CheckEnergyNeededForAttack
+ jr nc, .attack_false ; return if no energy needed
+
+; check if colorless energy is needed...
+ ld a, c
+ or a
+ jr nz, .count_if_enough
+
+; ...otherwise check if basic energy card is needed
+; and it's grass energy.
+ ld a, b
+ or a
+ jr z, .attack_false
+ ld a, e
+ cp GRASS_ENERGY
+ jr nz, .attack_false
+ ld c, b
+ jr .count_if_enough
+
+.attack_false
+ or a
+ ret
+
+.count_if_enough
+; if there's enough Grass energy cards in Bench
+; to satisfy the attack energy cost, return carry.
+ push bc
+ call .CountGrassEnergyInBench
+ pop bc
+ cp c
+ jr c, .attack_false
+ ld a, c
+ scf
+ ret
+
+.is_exeggutor
+; in case it's Exeggutor in Arena, return carry
+; if there are any Grass energy cards in Bench.
+ call .CountGrassEnergyInBench
+ or a
+ jr z, .attack_false
+
+ scf
+ ret
+
+; outputs in a the number of Grass energy cards
+; currently attached to Bench cards.
+.CountGrassEnergyInBench ; 22286 (8:6286)
+ lb de, 0, 0
+.count_loop
+ ld a, DUELVARS_CARD_LOCATIONS
+ add e
+ call GetTurnDuelistVariable
+ and %00011111
+ cp CARD_LOCATION_BENCH_1
+ jr c, .count_next
+
+; is in bench
+ ld a, e
+ push de
+ call GetCardIDFromDeckIndex
+ ld a, e
+ pop de
+ cp GRASS_ENERGY
+ jr nz, .count_next
+ inc d
+.count_next
+ inc e
+ ld a, DECK_SIZE
+ cp e
+ jr nz, .count_loop
+ ld a, d
+ ret
+
+; returns carry if there are enough Grass energy cards in Bench
+; to satisfy the retreat cost of the Arena card.
+; if so, output the number of energy cards still needed in a.
+.CheckEnoughGrassEnergyCardsForRetreatCost ; 222a9 (8:62a9)
+ xor a ; PLAY_AREA_ARENA
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call GetPlayAreaCardRetreatCost
+ ld b, a
+ ld e, PLAY_AREA_ARENA
+ farcall CountNumberOfEnergyCardsAttached
+ cp b
+ jr nc, .retreat_false ; return if enough to retreat
+
+; see if there's enough Grass energy cards
+; in the Bench to satisfy retreat cost
+ ld c, a
+ ld a, b
+ sub c
+ ld c, a
+ push bc
+ call .CountGrassEnergyInBench
+ pop bc
+ cp c
+ jr c, .retreat_false ; return if less cards than needed
+
+; output number of cards needed to retreat
+ ld a, c
+ scf
+ ret
+.retreat_false
+ or a
+ ret
+
+; AI logic to determine whether to use Energy Trans Pkmn Power
+; to transfer energy cards attached from the Arena Pokemon to
+; some card in the Bench.
+AIEnergyTransTransferEnergyToBench: ; 222ca (8:62ca)
+ xor a ; PLAY_AREA_ARENA
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckIfDefendingPokemonCanKnockOut
+ ret nc ; return if Defending can't KO
+
+; processes attacks and see if any attack would be used by AI.
+; if so, return.
+ farcall AIProcessButDontUseAttack
+ ret c
+
+; return if Arena card has no Grass energy cards attached.
+ ld e, PLAY_AREA_ARENA
+ call GetPlayAreaCardAttachedEnergies
+ ld a, [wAttachedEnergies + GRASS]
+ or a
+ ret z
+
+; if no energy card attachment is needed, return.
+ farcall AIProcessButDontPlayEnergy_SkipEvolutionAndArena
+ ret nc
+
+; AI decided that an energy card is needed
+; so look for Venusaur2 in Play Area
+; so that its PKMN Power can be used.
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ dec a
+ ld b, a
+.loop_play_area
+ ld a, DUELVARS_ARENA_CARD
+ add b
+ call GetTurnDuelistVariable
+ ldh [hTempCardIndex_ff9f], a
+ ld [wAIVenusaur2DeckIndex], a
+ call GetCardIDFromDeckIndex
+ ld a, e
+ cp VENUSAUR2
+ jr z, .use_pkmn_power
+
+ ld a, b
+ or a
+ ret z ; return when Play Area loop is ended
+
+ dec b
+ jr .loop_play_area
+
+; use Energy Trans Pkmn Power
+.use_pkmn_power
+ ld a, b
+ ldh [hTemp_ffa0], a
+ ld [wAIVenusaur2PlayAreaLocation], a
+ ld a, OPPACTION_USE_PKMN_POWER
+ bank1call AIMakeDecision
+ ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT
+ bank1call AIMakeDecision
+
+; loop for each energy cards that are going to be transferred.
+.loop_energy
+ xor a
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, [wAIVenusaur2PlayAreaLocation]
+ ldh [hTemp_ffa0], a
+
+ ; returns when Arena card has no Grass energy cards attached.
+ ld e, PLAY_AREA_ARENA
+ call GetPlayAreaCardAttachedEnergies
+ ld a, [wAttachedEnergies + GRASS]
+ or a
+ jr z, .done_transfer
+
+; look for Grass energy cards that
+; are currently attached to Arena card.
+ ld e, 0
+.loop_deck_locations
+ ld a, DUELVARS_CARD_LOCATIONS
+ add e
+ call GetTurnDuelistVariable
+ cp CARD_LOCATION_ARENA
+ jr nz, .next_card
+
+ ld a, e
+ push de
+ call GetCardIDFromDeckIndex
+ ld a, e
+ pop de
+ cp GRASS_ENERGY
+ jr nz, .next_card
+
+ ; store the deck index of energy card
+ ld a, e
+ ldh [hAIEnergyTransEnergyCard], a
+ jr .transfer
+
+.next_card
+ inc e
+ ld a, DECK_SIZE
+ cp e
+ jr nz, .loop_deck_locations
+ jr .done_transfer
+
+.transfer
+; get the Bench card location to transfer Grass energy card to.
+ farcall AIProcessButDontPlayEnergy_SkipEvolutionAndArena
+ jr nc, .done_transfer
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ ldh [hAIEnergyTransPlayAreaLocation], a
+
+ ld d, 30
+.small_delay_loop
+ call DoFrame
+ dec d
+ jr nz, .small_delay_loop
+
+ ld a, [wAIVenusaur2DeckIndex]
+ ldh [hTempCardIndex_ff9f], a
+ ld d, a
+ ld e, FIRST_ATTACK_OR_PKMN_POWER
+ call CopyAttackDataAndDamage_FromDeckIndex
+ ld a, OPPACTION_6B15
+ bank1call AIMakeDecision
+ jr .loop_energy
+
+; transfer is done, perform delay
+; and return to main scene.
+.done_transfer
+ ld d, 60
+.big_delay_loop
+ call DoFrame
+ dec d
+ jr nz, .big_delay_loop
+ ld a, OPPACTION_DUEL_MAIN_SCENE
+ bank1call AIMakeDecision
+ ret
+
+; handles AI logic for using some Pkmn Powers.
+; Pkmn Powers handled here are:
+; - Heal;
+; - Shift;
+; - Peek;
+; - Strange Behavior;
+; - Curse.
+; returns carry if turn ended.
+HandleAIPkmnPowers: ; 2237f (8:637f)
+ ld a, MUK
+ call CountPokemonIDInBothPlayAreas
+ ccf
+ ret nc ; return no carry if Muk is in play
+
+ farcall AIChooseRandomlyNotToDoAction
+ ccf
+ ret nc ; return no carry if AI randomly decides to
+
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld b, a
+ ld c, PLAY_AREA_ARENA
+ ld a, DUELVARS_ARENA_CARD_STATUS
+ call GetTurnDuelistVariable
+ and CNF_SLP_PRZ
+ jr nz, .next_2
+
+.loop_play_area
+ ld a, DUELVARS_ARENA_CARD
+ add c
+ call GetTurnDuelistVariable
+ ld [wce08], a
+
+ push af
+ push bc
+ ld d, a
+ ld a, c
+ ldh [hTempPlayAreaLocation_ff9d], a
+ ld e, FIRST_ATTACK_OR_PKMN_POWER
+ call CopyAttackDataAndDamage_FromDeckIndex
+ ld a, [wLoadedAttackCategory]
+ cp POKEMON_POWER
+ jr z, .execute_effect
+ pop bc
+ jr .next_3
+
+.execute_effect
+ ld a, EFFECTCMDTYPE_INITIAL_EFFECT_2
+ bank1call TryExecuteEffectCommandFunction
+ pop bc
+ jr c, .next_3
+
+; TryExecuteEffectCommandFunction was successful,
+; so check what Pkmn Power this is through card's ID.
+ pop af
+ call GetCardIDFromDeckIndex
+ ld a, e
+ push bc
+
+; check heal
+ cp VILEPLUME
+ jr nz, .check_shift
+ call HandleAIHeal
+ jr .next_1
+.check_shift
+ cp VENOMOTH
+ jr nz, .check_peek
+ call HandleAIShift
+ jr .next_1
+.check_peek
+ cp MANKEY
+ jr nz, .check_strange_behavior
+ call HandleAIPeek
+ jr .next_1
+.check_strange_behavior
+ cp SLOWBRO
+ jr nz, .check_curse
+ call HandleAIStrangeBehavior
+ jr .next_1
+.check_curse
+ cp GENGAR
+ jr nz, .next_1
+ call z, HandleAICurse
+ jr c, .done
+
+.next_1
+ pop bc
+.next_2
+ inc c
+ ld a, c
+ cp b
+ jr nz, .loop_play_area
+ ret
+
+.next_3
+ pop af
+ jr .next_2
+
+.done
+ pop bc
+ ret
+
+; checks whether AI uses Heal on Pokemon in Play Area.
+; input:
+; c = Play Area location (PLAY_AREA_*) of Vileplume.
+HandleAIHeal: ; 22402 (8:6402)
+ ld a, c
+ ldh [hTemp_ffa0], a
+ call .CheckHealTarget
+ ret nc ; return if no target to heal
+ push af
+ ld a, [wce08]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_USE_PKMN_POWER
+ bank1call AIMakeDecision
+ pop af
+ ldh [hPlayAreaEffectTarget], a
+ ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT
+ bank1call AIMakeDecision
+ ld a, OPPACTION_DUEL_MAIN_SCENE
+ bank1call AIMakeDecision
+ ret
+
+; finds a target suitable for AI to use Heal on.
+; only heals Arena card if the Defending Pokemon
+; cannot KO it after Heal is used.
+; returns carry if target was found and outputs
+; in a the Play Area location of that card.
+.CheckHealTarget ; 22422 (8:6422)
+; check if Arena card has any damage counters,
+; if not, check Bench instead.
+ ld e, PLAY_AREA_ARENA
+ call GetCardDamageAndMaxHP
+ or a
+ jr z, .check_bench
+
+ xor a ; PLAY_AREA_ARENA
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckIfDefendingPokemonCanKnockOut
+ jr nc, .set_carry ; return carry if can't KO
+ ld d, a
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetTurnDuelistVariable
+ ld h, a
+ ld e, PLAY_AREA_ARENA
+ call GetCardDamageAndMaxHP
+ ; this seems useless since it was already
+ ; checked that Arena card has damage,
+ ; so card damage is at least 10.
+ cp 10 + 1
+ jr c, .check_remaining
+ ld a, 10
+ ; a = min(10, CardDamage)
+
+; checks if Defending Pokemon can still KO
+; if Heal is used on this card.
+; if Heal prevents KO, return carry.
+.check_remaining
+ ld l, a
+ ld a, h ; load remaining HP
+ add l ; add 1 counter to account for heal
+ sub d ; subtract damage of strongest opponent attack
+ jr c, .check_bench
+ jr z, .check_bench
+
+.set_carry
+ xor a ; PLAY_AREA_ARENA
+ scf
+ ret
+
+; check Bench for Pokemon with damage counters
+; and find the one with the most damage.
+.check_bench
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld d, a
+ lb bc, 0, 0
+ ld e, PLAY_AREA_BENCH_1
+.loop_bench
+ ld a, e
+ cp d
+ jr z, .done
+ push bc
+ call GetCardDamageAndMaxHP
+ pop bc
+ cp b
+ jr c, .next_bench
+ jr z, .next_bench
+ ld b, a ; store this damage
+ ld c, e ; store this Play Area location
+.next_bench
+ inc e
+ jr .loop_bench
+
+; check if a Pokemon with damage counters was found
+; in the Bench and, if so, return carry.
+.done
+ ld a, c
+ or a
+ jr z, .not_found
+; found
+ scf
+ ret
+.not_found
+ or a
+ ret
+
+; checks whether AI uses Shift.
+; input:
+; c = Play Area location (PLAY_AREA_*) of Venomoth
+HandleAIShift: ; 22476 (8:6476)
+ ld a, c
+ or a
+ ret nz ; return if Venomoth is not Arena card
+
+ ldh [hTemp_ffa0], a
+ call GetArenaCardColor
+ call TranslateColorToWR
+ ld b, a
+ call SwapTurn
+ call GetArenaCardWeakness
+ ld [wAIDefendingPokemonWeakness], a
+ call SwapTurn
+ or a
+ ret z ; return if Defending Pokemon has no weakness
+ and b
+ ret nz ; return if Venomoth is already Defending card's weakness type
+
+; check whether there's a card in play with
+; the same color as the Player's card weakness
+ call .CheckWhetherTurnDuelistHasColor
+ jr c, .found
+ call SwapTurn
+ call .CheckWhetherTurnDuelistHasColor
+ call SwapTurn
+ ret nc ; return if no color found
+
+.found
+ ld a, [wce08]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_USE_PKMN_POWER
+ bank1call AIMakeDecision
+
+; converts WR_* to appropriate color
+ ld a, [wAIDefendingPokemonWeakness]
+ ld b, 0
+.loop_color
+ bit 7, a
+ jr nz, .done
+ inc b
+ rlca
+ jr .loop_color
+
+; use Pkmn Power effect
+.done
+ ld a, b
+ ldh [hAIPkmnPowerEffectParam], a
+ ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT
+ bank1call AIMakeDecision
+ ld a, OPPACTION_DUEL_MAIN_SCENE
+ bank1call AIMakeDecision
+ ret
+
+; returns carry if turn Duelist has a Pokemon
+; with same color as wAIDefendingPokemonWeakness.
+.CheckWhetherTurnDuelistHasColor ; 224c6 (8:64c6)
+ ld a, [wAIDefendingPokemonWeakness]
+ ld b, a
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+.loop_play_area
+ ld a, [hli]
+ cp $ff
+ jr z, .false
+ push bc
+ call GetCardIDFromDeckIndex
+ call GetCardType
+ ; in case this is a Mysterious Fossil or Clefairy Doll card,
+ ; AI might read the type of the card incorrectly here.
+ ; uncomment the following lines to account for this
+ ; cp TYPE_TRAINER
+ ; jr nz, .not_trainer
+ ; pop bc
+ ; jr .loop_play_area
+; .not_trainer
+ call TranslateColorToWR
+ pop bc
+ and b
+ jr z, .loop_play_area
+; true
+ scf
+ ret
+.false
+ or a
+ ret
+
+; checks whether AI uses Peek.
+; input:
+; c = Play Area location (PLAY_AREA_*) of Mankey.
+HandleAIPeek: ; 224e6 (8:64e6)
+ ld a, c
+ ldh [hTemp_ffa0], a
+ ld a, 50
+ call Random
+ cp 3
+ ret nc ; return 47 out of 50 times
+
+; choose what to use Peek on at random
+ ld a, 3
+ call Random
+ or a
+ jr z, .check_ai_prizes
+ cp 2
+ jr c, .check_player_hand
+
+; check Player's Deck
+ ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
+ call GetNonTurnDuelistVariable
+ cp DECK_SIZE - 1
+ ret nc ; return if Player has one or no cards in Deck
+ ld a, AI_PEEK_TARGET_DECK
+ jr .use_peek
+
+.check_ai_prizes
+ ld a, DUELVARS_PRIZES
+ call GetTurnDuelistVariable
+ ld hl, wcda5
+ and [hl]
+ ld [hl], a
+ or a
+ ret z ; return if no prizes
+
+ ld c, a
+ ld b, $1
+ ld d, 0
+.loop_prizes
+ ld a, c
+ and b
+ jr nz, .found_prize
+ sla b
+ inc d
+ jr .loop_prizes
+.found_prize
+; remove this prize's flag from the prize list
+; and use Peek on first one in list (lowest bit set)
+ ld a, c
+ sub b
+ ld [hl], a
+ ld a, AI_PEEK_TARGET_PRIZE
+ add d
+ jr .use_peek
+
+.check_player_hand
+ call SwapTurn
+ call CreateHandCardList
+ call SwapTurn
+ or a
+ ret z ; return if no cards in Hand
+; shuffle list and pick the first entry to Peek
+ ld hl, wDuelTempList
+ call CountCardsInDuelTempList
+ call ShuffleCards
+ ld a, [wDuelTempList]
+ or AI_PEEK_TARGET_HAND
+
+.use_peek
+ push af
+ ld a, [wce08]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_USE_PKMN_POWER
+ bank1call AIMakeDecision
+ pop af
+ ldh [hAIPkmnPowerEffectParam], a
+ ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT
+ bank1call AIMakeDecision
+ ld a, OPPACTION_DUEL_MAIN_SCENE
+ bank1call AIMakeDecision
+ ret
+
+; checks whether AI uses Strange Behavior.
+; input:
+; c = Play Area location (PLAY_AREA_*) of Slowbro.
+HandleAIStrangeBehavior: ; 2255d (8:655d)
+ ld a, c
+ or a
+ ret z ; return if Slowbro is Arena card
+
+ ldh [hTemp_ffa0], a
+ ld e, PLAY_AREA_ARENA
+ call GetCardDamageAndMaxHP
+ or a
+ ret z ; return if Arena card has no damage counters
+
+ ld [wce06], a
+ ldh a, [hTemp_ffa0]
+ add DUELVARS_ARENA_CARD_HP
+ call GetTurnDuelistVariable
+ sub 10
+ ret z ; return if Slowbro has only 10 HP remaining
+
+; if Slowbro can't receive all damage counters,
+; only transfer remaining HP - 10 damage
+ ld hl, wce06
+ cp [hl]
+ jr c, .use_strange_behavior
+ ld a, [hl] ; can receive all damage counters
+
+.use_strange_behavior
+ push af
+ ld a, [wce08]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_USE_PKMN_POWER
+ bank1call AIMakeDecision
+ xor a
+ ldh [hAIPkmnPowerEffectParam], a
+ ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT
+ bank1call AIMakeDecision
+ pop af
+
+; loop counters chosen to transfer and use Pkmn Power
+ call ConvertHPToCounters
+ ld e, a
+.loop_counters
+ ld d, 30
+.small_delay_loop
+ call DoFrame
+ dec d
+ jr nz, .small_delay_loop
+ push de
+ ld a, OPPACTION_6B15
+ bank1call AIMakeDecision
+ pop de
+ dec e
+ jr nz, .loop_counters
+
+; return to main scene
+ ld d, 60
+.big_delay_loop
+ call DoFrame
+ dec d
+ jr nz, .big_delay_loop
+ ld a, OPPACTION_DUEL_MAIN_SCENE
+ bank1call AIMakeDecision
+ ret
+
+; checks whether AI uses Curse.
+; input:
+; c = Play Area location (PLAY_AREA_*) of Gengar.
+HandleAICurse: ; 225b5 (8:65b5)
+ ld a, c
+ ldh [hTemp_ffa0], a
+
+; loop Player's Play Area and checks their damage.
+; finds the card with lowest remaining HP and
+; stores its HP and its Play Area location
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetNonTurnDuelistVariable
+ ld d, a
+ ld e, PLAY_AREA_ARENA
+ lb bc, 0, $ff
+ ld h, PLAY_AREA_ARENA
+ call SwapTurn
+.loop_play_area_1
+ push bc
+ call GetCardDamageAndMaxHP
+ pop bc
+ or a
+ jr z, .next_1
+
+ inc b
+ ld a, e
+ add DUELVARS_ARENA_CARD_HP
+ push hl
+ call GetTurnDuelistVariable
+ pop hl
+ cp c
+ jr nc, .next_1
+ ; lower HP than one stored
+ ld c, a ; store this HP
+ ld h, e ; store this Play Area location
+
+.next_1
+ inc e
+ ld a, e
+ cp d
+ jr nz, .loop_play_area_1 ; reached end of Play Area
+
+ ld a, 1
+ cp b
+ jr nc, .failed ; return if less than 2 cards with damage
+
+; card in Play Area with lowest HP remaining was found.
+; look for another card to take damage counter from.
+ ld a, h
+ ldh [hTempRetreatCostCards], a
+ ld b, a
+ ld a, 10
+ cp c
+ jr z, .hp_10_remaining
+ ; if has more than 10 HP remaining,
+ ; skip Arena card in choosing which
+ ; card to take damage counter from.
+ ld e, PLAY_AREA_BENCH_1
+ jr .second_card
+
+.hp_10_remaining
+ ; if Curse can KO, then include
+ ; Player's Arena card to take
+ ; damage counter from.
+ ld e, PLAY_AREA_ARENA
+
+.second_card
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld d, a
+.loop_play_area_2
+ ld a, e
+ cp b
+ jr z, .next_2 ; skip same Pokemon card
+ push bc
+ call GetCardDamageAndMaxHP
+ pop bc
+ jr nz, .use_curse ; has damage counters, choose this card
+.next_2
+ inc e
+ ld a, e
+ cp d
+ jr nz, .loop_play_area_2
+
+.failed
+ call SwapTurn
+ or a
+ ret
+
+.use_curse
+ ld a, e
+ ldh [hAIPkmnPowerEffectParam], a
+ call SwapTurn
+ ld a, [wce08]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_USE_PKMN_POWER
+ bank1call AIMakeDecision
+ ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT
+ bank1call AIMakeDecision
+ ld a, OPPACTION_DUEL_MAIN_SCENE
+ bank1call AIMakeDecision
+ ret
+
+; handles AI logic for Cowardice
+HandleAICowardice: ; 2262d (8:662d)
+ ld a, MUK
+ call CountPokemonIDInBothPlayAreas
+ ret c ; return if there's Muk in play
+
+ farcall AIChooseRandomlyNotToDoAction
+ ret c ; randomly return
+
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp 1
+ ret z ; return if only one Pokemon in Play Area
+
+ ld b, a
+ ld c, PLAY_AREA_ARENA
+ ld a, DUELVARS_ARENA_CARD_STATUS
+ call GetTurnDuelistVariable
+ and CNF_SLP_PRZ
+ jr nz, .next
+.loop
+ ld a, DUELVARS_ARENA_CARD
+ add c
+ call GetTurnDuelistVariable
+ ld [wce08], a
+ call GetCardIDFromDeckIndex
+ ld a, e
+ push bc
+ cp TENTACOOL
+ call z, .CheckWhetherToUseCowardice
+ pop bc
+ jr nc, .next
+
+ dec b ; subtract 1 from number of Pokemon in Play Area
+ ld a, 1
+ cp b
+ ret z ; return if no longer has Bench Pokemon
+ ld c, PLAY_AREA_ARENA ; reset back to Arena
+ jr .loop
+
+.next
+ inc c
+ ld a, c
+ cp b
+ jr nz, .loop
+ ret
+
+; checks whether AI uses Cowardice.
+; return carry if Pkmn Power was used.
+; input:
+; c = Play Area location (PLAY_AREA_*) of Tentacool.
+.CheckWhetherToUseCowardice ; 22671 (8:6671)
+ ld a, c
+ ldh [hTemp_ffa0], a
+ ld e, a
+ call GetCardDamageAndMaxHP
+.asm_22678
+ or a
+ ret z ; return if has no damage counters
+
+ ldh a, [hTemp_ffa0]
+ or a
+ jr nz, .is_benched
+
+ ; this part is buggy if AIDecideBenchPokemonToSwitchTo returns carry
+ ; but since this was already checked beforehand, this never happens.
+ ; so jr c, .asm_22678 can be safely removed.
+ farcall AIDecideBenchPokemonToSwitchTo
+ jr c, .asm_22678 ; bug, this jumps in the middle of damage checking
+ jr .use_cowardice
+.is_benched
+ ld a, $ff
+.use_cowardice
+ push af
+ ld a, [wce08]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_USE_PKMN_POWER
+ bank1call AIMakeDecision
+ pop af
+ ldh [hAIPkmnPowerEffectParam], a
+ ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT
+ bank1call AIMakeDecision
+ ld a, OPPACTION_DUEL_MAIN_SCENE
+ bank1call AIMakeDecision
+ scf
+ ret
+
+; AI logic for Damage Swap to transfer damage from Arena card
+; to a card in Bench with more than 10 HP remaining
+; and with no energy cards attached.
+HandleAIDamageSwap: ; 226a3 (8:66a3)
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ dec a
+ ret z ; return if no Bench Pokemon
+
+ farcall AIChooseRandomlyNotToDoAction
+ ret c
+
+ ld a, ALAKAZAM
+ call CountPokemonIDInPlayArea
+ ret nc ; return if no Alakazam
+ ld a, MUK
+ call CountPokemonIDInBothPlayAreas
+ ret c ; return if there's Muk in play
+
+; only take damage off certain cards in Arena
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ cp ALAKAZAM
+ jr z, .ok
+ cp KADABRA
+ jr z, .ok
+ cp ABRA
+ jr z, .ok
+ cp MR_MIME
+ ret nz
+
+.ok
+ ld e, PLAY_AREA_ARENA
+ call GetCardDamageAndMaxHP
+ or a
+ ret z ; return if no damage
+
+ call ConvertHPToCounters
+ ld [wce06], a
+ ld a, ALAKAZAM
+ ld b, PLAY_AREA_BENCH_1
+ farcall LookForCardIDInPlayArea_Bank5
+ jr c, .is_in_bench
+
+; Alakazam is Arena card
+ xor a
+.is_in_bench
+ ld [wce08], a
+ call .CheckForDamageSwapTargetInBench
+ ret c ; return if not found
+
+; use Damage Swap
+ ld a, [wce08]
+ add DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wce08]
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_USE_PKMN_POWER
+ bank1call AIMakeDecision
+ ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT
+ bank1call AIMakeDecision
+
+ ld a, [wce06]
+ ld e, a
+.loop_damage
+ ld d, 30
+.small_delay_loop
+ call DoFrame
+ dec d
+ jr nz, .small_delay_loop
+
+ push de
+ call .CheckForDamageSwapTargetInBench
+ jr c, .no_more_target
+
+ ldh [hTempRetreatCostCards], a
+ xor a ; PLAY_AREA_ARENA
+ ldh [hAIPkmnPowerEffectParam], a
+ ld a, OPPACTION_6B15
+ bank1call AIMakeDecision
+ pop de
+ dec e
+ jr nz, .loop_damage
+
+.done
+; return to main scene
+ ld d, 60
+.big_delay_loop
+ call DoFrame
+ dec d
+ jr nz, .big_delay_loop
+ ld a, OPPACTION_DUEL_MAIN_SCENE
+ bank1call AIMakeDecision
+ ret
+
+.no_more_target
+ pop de
+ jr .done
+
+; looks for a target in the bench to receive damage counters.
+; returns carry if one is found, and outputs remaining HP in a.
+.CheckForDamageSwapTargetInBench ; 2273c (8:673c)
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld b, a
+ ld c, PLAY_AREA_BENCH_1
+ lb de, $ff, $ff
+
+; look for candidates in bench to get the damage counters
+; only target specific card IDs.
+.loop_bench
+ ld a, c
+ add DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ push de
+ call GetCardIDFromDeckIndex
+ ld a, e
+ pop de
+ cp CHANSEY
+ jr z, .found_candidate
+ cp KANGASKHAN
+ jr z, .found_candidate
+ cp SNORLAX
+ jr z, .found_candidate
+ cp MR_MIME
+ jr z, .found_candidate
+
+.next_play_area
+ inc c
+ ld a, c
+ cp b
+ jr nz, .loop_bench
+
+; done
+ ld a, e
+ cp $ff
+ jr nz, .no_carry
+ ld a, d
+ cp $ff
+ jr z, .set_carry
+.no_carry
+ or a
+ ret
+
+.found_candidate
+; found a potential candidate to receive damage counters
+ ld a, DUELVARS_ARENA_CARD_HP
+ add c
+ call GetTurnDuelistVariable
+ cp 20
+ jr c, .next_play_area ; ignore cards with only 10 HP left
+
+ ld d, c ; store damage
+ push de
+ push bc
+ ld e, c
+ farcall CountNumberOfEnergyCardsAttached
+ pop bc
+ pop de
+ or a
+ jr nz, .next_play_area ; ignore cards with attached energy
+ ld e, c ; store deck index
+ jr .next_play_area
+
+.set_carry
+ scf
+ ret
+
+; handles AI logic for attaching energy cards
+; in Go Go Rain Dance deck.
+HandleAIGoGoRainDanceEnergy: ; 22790 (8:6790)
+ ld a, [wOpponentDeckID]
+ cp GO_GO_RAIN_DANCE_DECK_ID
+ ret nz ; return if not Go Go Rain Dance deck
+
+ ld a, BLASTOISE
+ call CountPokemonIDInPlayArea
+ ret nc ; return if no Blastoise
+ ld a, MUK
+ call CountPokemonIDInBothPlayAreas
+ ret c ; return if there's Muk in play
+
+; play all the energy cards that is needed.
+.loop
+ farcall AIProcessAndTryToPlayEnergy
+ jr c, .loop
+ ret
diff --git a/src/engine/ai/trainer_cards.asm b/src/engine/ai/trainer_cards.asm
new file mode 100644
index 0000000..cd87a4b
--- /dev/null
+++ b/src/engine/ai/trainer_cards.asm
@@ -0,0 +1,6073 @@
+INCLUDE "data/ai_trainer_card_logic.asm"
+
+_AIProcessHandTrainerCards: ; 200e5 (8:40e5)
+ ld [wce18], a
+; create hand list in wDuelTempList and wTempHandCardList.
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ ld de, wTempHandCardList
+ call CopyBuffer
+ ld hl, wTempHandCardList
+
+.loop_hand
+ ld a, [hli]
+ ld [wAITrainerCardToPlay], a
+ cp $ff
+ ret z
+
+ push hl
+ ld a, [wce18]
+ ld d, a
+ ld hl, AITrainerCardLogic
+.loop_data
+ xor a
+ ld [wCurrentAIFlags], a
+ ld a, [hli]
+ cp $ff
+ jp z, .pop_hl
+
+; compare input to first byte in data and continue if equal.
+ cp d
+ jp nz, .inc_hl_by_5
+
+ ld a, [hli]
+ ld [wce17], a
+ ld a, [wAITrainerCardToPlay]
+ call LoadCardDataToBuffer1_FromDeckIndex
+
+ cp SWITCH
+ jr nz, .skip_switch_check
+
+ ld b, a
+ ld a, [wPreviousAIFlags]
+ and AI_FLAG_USED_SWITCH
+ jr nz, .inc_hl_by_4
+ ld a, b
+
+.skip_switch_check
+; compare hand card to second byte in data and continue if equal.
+ ld b, a
+ ld a, [wce17]
+ cp b
+ jr nz, .inc_hl_by_4
+
+; found Trainer card
+ push hl
+ push de
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+
+; if Headache effects prevent playing card
+; move on to the next item in list.
+ bank1call CheckCantUseTrainerDueToHeadache
+ jp c, .next_in_data
+
+ call LoadNonPokemonCardEffectCommands
+ ld a, EFFECTCMDTYPE_INITIAL_EFFECT_1
+ call TryExecuteEffectCommandFunction
+ jp c, .next_in_data
+
+; AI can randomly choose not to play card.
+ farcall AIChooseRandomlyNotToDoAction
+ jr c, .next_in_data
+
+; call routine to decide whether to play Trainer card
+ pop de
+ pop hl
+ push hl
+ call CallIndirect
+ pop hl
+ jr nc, .inc_hl_by_4
+
+; routine returned carry, which means
+; this card should be played.
+ inc hl
+ inc hl
+ ld [wAITrainerCardParameter], a
+
+; show Play Trainer Card screen
+ push de
+ push hl
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_PLAY_TRAINER
+ bank1call AIMakeDecision
+ pop hl
+ pop de
+ jr c, .inc_hl_by_2
+
+; execute the effects of the Trainer card
+ push hl
+ call CallIndirect
+ pop hl
+
+ inc hl
+ inc hl
+ ld a, [wPreviousAIFlags]
+ ld b, a
+ ld a, [wCurrentAIFlags]
+ or b
+ ld [wPreviousAIFlags], a
+ pop hl
+ and AI_FLAG_MODIFIED_HAND
+ jp z, .loop_hand
+
+; the hand was modified during the Trainer effect
+; so it needs to be re-listed again and
+; looped from the top.
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ ld de, wTempHandCardList
+ call CopyBuffer
+ ld hl, wTempHandCardList
+; clear the AI_FLAG_MODIFIED_HAND flag
+ ld a, [wPreviousAIFlags]
+ and ~AI_FLAG_MODIFIED_HAND
+ ld [wPreviousAIFlags], a
+ jp .loop_hand
+
+.inc_hl_by_5
+ inc hl
+.inc_hl_by_4
+ inc hl
+ inc hl
+.inc_hl_by_2
+ inc hl
+ inc hl
+ jp .loop_data
+
+.next_in_data
+ pop de
+ pop hl
+ inc hl
+ inc hl
+ inc hl
+ inc hl
+ jp .loop_data
+
+.pop_hl
+ pop hl
+ jp .loop_hand
+
+; makes AI use Potion card.
+AIPlay_Potion: ; 201b5 (8:41b5)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld e, a
+ call GetCardDamageAndMaxHP
+ cp 20
+ jr c, .play_card
+ ld a, 20
+.play_card
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+; if AI doesn't decide to retreat this card,
+; check if defending Pokémon can KO active card
+; next turn after using Potion.
+; if it cannot, return carry.
+; also take into account whether attack is high recoil.
+AIDecide_Potion1: ; 201d1 (8:41d1)
+ farcall AIDecideWhetherToRetreat
+ jr c, .no_carry
+ call AICheckIfAttackIsHighRecoil
+ jr c, .no_carry
+ xor a ; active card
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckIfDefendingPokemonCanKnockOut
+ jr nc, .no_carry
+ ld d, a
+
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetTurnDuelistVariable
+ ld h, a
+ ld e, PLAY_AREA_ARENA
+ call GetCardDamageAndMaxHP
+ cp 20 + 1 ; if damage <= 20
+ jr c, .calculate_hp
+ ld a, 20 ; amount of Potion HP healing
+
+; if damage done by defending Pokémon next turn will still
+; KO this card after healing, return no carry.
+.calculate_hp
+ ld l, a
+ ld a, h
+ add l
+ sub d
+ jr c, .no_carry
+ jr z, .no_carry
+
+; return carry.
+ xor a
+ scf
+ ret
+.no_carry
+ or a
+ ret
+
+; finds a card in Play Area to use Potion on.
+; output:
+; a = card to use Potion on;
+; carry set if Potion should be used.
+AIDecide_Potion2: ; 20204 (8:4204)
+ xor a
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckIfDefendingPokemonCanKnockOut
+ jr nc, .start_from_active
+; can KO
+ ld d, a
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetTurnDuelistVariable
+ ld h, a
+ ld e, PLAY_AREA_ARENA
+ call GetCardDamageAndMaxHP
+ cp 20 + 1 ; if damage <= 20
+ jr c, .calculate_hp
+ ld a, 20
+; return if using healing prevents KO.
+.calculate_hp
+ ld l, a
+ ld a, h
+ add l
+ sub d
+ jr c, .count_prizes
+ jr z, .count_prizes
+ or a
+ ret
+
+; using Potion on active card does not prevent a KO.
+; if player is at last prize, start loop with active card.
+; otherwise start loop at first bench Pokémon.
+.count_prizes
+ call SwapTurn
+ call CountPrizes
+ call SwapTurn
+ dec a
+ jr z, .start_from_active
+ ld e, PLAY_AREA_BENCH_1
+ jr .loop
+
+; find Play Area Pokémon with more than 10 damage.
+; skip Pokémon if it has a BOOST_IF_TAKEN_DAMAGE attack.
+.start_from_active
+ ld e, PLAY_AREA_ARENA
+.loop
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ call GetTurnDuelistVariable
+ cp $ff
+ ret z
+ call .check_boost_if_taken_damage
+ jr c, .has_boost_damage
+ call GetCardDamageAndMaxHP
+ cp 20 ; if damage >= 20
+ jr nc, .found
+.has_boost_damage
+ inc e
+ jr .loop
+
+; a card was found, now to check if it's active or benched.
+.found
+ ld a, e
+ or a
+ jr z, .active_card
+
+; bench card
+ push de
+ call SwapTurn
+ call CountPrizes
+ call SwapTurn
+ dec a
+ or a
+ jr z, .check_random
+ ld a, 10
+ call Random
+ cp 3
+; 7/10 chance of returning carry.
+.check_random
+ pop de
+ jr c, .no_carry
+ ld a, e
+ scf
+ ret
+
+; return carry for active card if not High Recoil.
+.active_card
+ push de
+ call AICheckIfAttackIsHighRecoil
+ pop de
+ jr c, .no_carry
+ ld a, e
+ scf
+ ret
+.no_carry
+ or a
+ ret
+
+; return carry if either of the attacks are usable
+; and have the BOOST_IF_TAKEN_DAMAGE effect.
+.check_boost_if_taken_damage ; 2027e (8:427e)
+ push de
+ xor a ; FIRST_ATTACK_OR_PKMN_POWER
+ ld [wSelectedAttack], a
+ farcall CheckIfSelectedAttackIsUnusable
+ jr c, .second_attack
+ ld a, ATTACK_FLAG3_ADDRESS | BOOST_IF_TAKEN_DAMAGE_F
+ call CheckLoadedAttackFlag
+ jr c, .set_carry
+.second_attack
+ ld a, SECOND_ATTACK
+ ld [wSelectedAttack], a
+ farcall CheckIfSelectedAttackIsUnusable
+ jr c, .false
+ ld a, ATTACK_FLAG3_ADDRESS | BOOST_IF_TAKEN_DAMAGE_F
+ call CheckLoadedAttackFlag
+ jr c, .set_carry
+.false
+ pop de
+ or a
+ ret
+.set_carry
+ pop de
+ scf
+ ret
+
+; makes AI use Super Potion card.
+AIPlay_SuperPotion: ; 202a8 (8:42a8)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ call AIPickEnergyCardToDiscard
+ ldh [hTemp_ffa0], a
+ ld a, [wAITrainerCardParameter]
+ ld e, a
+ call GetCardDamageAndMaxHP
+ cp 40
+ jr c, .play_card
+ ld a, 40
+.play_card
+ ldh [hTempRetreatCostCards], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+; if AI doesn't decide to retreat this card and card has
+; any energy cards attached, check if defending Pokémon can KO
+; active card next turn after using Super Potion.
+; if it cannot, return carry.
+; also take into account whether attack is high recoil.
+AIDecide_SuperPotion1: ; 202cc (8:42cc)
+ farcall AIDecideWhetherToRetreat
+ jr c, .no_carry
+ call AICheckIfAttackIsHighRecoil
+ jr c, .no_carry
+ xor a
+ ldh [hTempPlayAreaLocation_ff9d], a
+ ld e, a
+ call .check_attached_energy
+ ret nc
+ farcall CheckIfDefendingPokemonCanKnockOut
+ jr nc, .no_carry
+
+ ld d, a
+ ld d, a
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetTurnDuelistVariable
+ ld h, a
+ ld e, PLAY_AREA_ARENA
+ call GetCardDamageAndMaxHP
+ cp 40 + 1 ; if damage < 40
+ jr c, .calculate_hp
+ ld a, 40
+.calculate_hp
+ ld l, a
+ ld a, h
+ add l
+ sub d
+ jr c, .no_carry
+ jr z, .no_carry
+
+; return carry
+ ld a, e
+ scf
+ ret
+.no_carry
+ or a
+ ret
+
+; returns carry if card has energies attached.
+.check_attached_energy ; 20305 (8:4305)
+ call GetPlayAreaCardAttachedEnergies
+ ld a, [wTotalAttachedEnergies]
+ or a
+ ret z
+ scf
+ ret
+
+; finds a card in Play Area to use Super Potion on.
+; output:
+; a = card to use Super Potion on;
+; carry set if Super Potion should be used.
+AIDecide_SuperPotion2: ; 2030f (8:430f)
+ xor a
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckIfDefendingPokemonCanKnockOut
+ jr nc, .start_from_active
+; can KO
+ ld d, a
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetTurnDuelistVariable
+ ld h, a
+ ld e, PLAY_AREA_ARENA
+ call GetCardDamageAndMaxHP
+ cp 40 + 1 ; if damage < 40
+ jr c, .calculate_hp
+ ld a, 40
+; return if using healing prevents KO.
+.calculate_hp
+ ld l, a
+ ld a, h
+ add l
+ sub d
+ jr c, .count_prizes
+ jr z, .count_prizes
+ or a
+ ret
+
+; using Super Potion on active card does not prevent a KO.
+; if player is at last prize, start loop with active card.
+; otherwise start loop at first bench Pokémon.
+.count_prizes
+ call SwapTurn
+ call CountPrizes
+ call SwapTurn
+ dec a
+ jr z, .start_from_active
+ ld e, PLAY_AREA_BENCH_1
+ jr .loop
+
+; find Play Area Pokémon with more than 30 damage.
+; skip Pokémon if it doesn't have any energy attached,
+; has a BOOST_IF_TAKEN_DAMAGE attack,
+; or if discarding makes any attack of its attacks unusable.
+.start_from_active
+ ld e, PLAY_AREA_ARENA
+.loop
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ call GetTurnDuelistVariable
+ cp $ff
+ ret z
+ ld d, a
+ call .check_attached_energy
+ jr nc, .next
+ call .check_boost_if_taken_damage
+ jr c, .next
+ call .check_energy_cost
+ jr c, .next
+ call GetCardDamageAndMaxHP
+ cp 40 ; if damage >= 40
+ jr nc, .found
+.next
+ inc e
+ jr .loop
+
+; a card was found, now to check if it's active or benched.
+.found
+ ld a, e
+ or a
+ jr z, .active_card
+
+; bench card
+ push de
+ call SwapTurn
+ call CountPrizes
+ call SwapTurn
+ dec a
+ or a
+ jr z, .check_random
+ ld a, 10
+ call Random
+ cp 3
+; 7/10 chance of returning carry.
+.check_random
+ pop de
+ jr c, .no_carry
+ ld a, e
+ scf
+ ret
+
+; return carry for active card if not Hgh Recoil.
+.active_card
+ push de
+ call AICheckIfAttackIsHighRecoil
+ pop de
+ jr c, .no_carry
+ ld a, e
+ scf
+ ret
+.no_carry
+ or a
+ ret
+
+; returns carry if card has energies attached.
+.check_attached_energy ; 20394 (8:4394)
+ call GetPlayAreaCardAttachedEnergies
+ ld a, [wTotalAttachedEnergies]
+ or a
+ ret z
+ scf
+ ret
+
+; return carry if either of the attacks are usable
+; and have the BOOST_IF_TAKEN_DAMAGE effect.
+.check_boost_if_taken_damage ; 2039e (8:439e)
+ push de
+ xor a ; FIRST_ATTACK_OR_PKMN_POWER
+ ld [wSelectedAttack], a
+ farcall CheckIfSelectedAttackIsUnusable
+ jr c, .second_attack_1
+ ld a, ATTACK_FLAG3_ADDRESS | BOOST_IF_TAKEN_DAMAGE_F
+ call CheckLoadedAttackFlag
+ jr c, .true_1
+.second_attack_1
+ ld a, SECOND_ATTACK
+ ld [wSelectedAttack], a
+ farcall CheckIfSelectedAttackIsUnusable
+ jr c, .false_1
+ ld a, ATTACK_FLAG3_ADDRESS | BOOST_IF_TAKEN_DAMAGE_F
+ call CheckLoadedAttackFlag
+ jr c, .true_1
+.false_1
+ pop de
+ or a
+ ret
+.true_1
+ pop de
+ scf
+ ret
+
+; returns carry if discarding energy card renders any attack unusable,
+; given that they have enough energy to be used before discarding.
+.check_energy_cost ; 203c8 (8:43c8)
+ push de
+ xor a ; FIRST_ATTACK_OR_PKMN_POWER
+ ld [wSelectedAttack], a
+ ld a, e
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckEnergyNeededForAttack
+ jr c, .second_attack_2
+ farcall CheckEnergyNeededForAttackAfterDiscard
+ jr c, .true_2
+
+.second_attack_2
+ pop de
+ push de
+ ld a, SECOND_ATTACK
+ ld [wSelectedAttack], a
+ ld a, e
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckEnergyNeededForAttack
+ jr c, .false_2
+ farcall CheckEnergyNeededForAttackAfterDiscard
+ jr c, .true_2
+
+.false_2
+ pop de
+ or a
+ ret
+.true_2
+ pop de
+ scf
+ ret
+
+AIPlay_Defender: ; 203f8 (8:43f8)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ xor a
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+; returns carry if using Defender can prevent a KO
+; by the defending Pokémon.
+; this takes into account both attacks and whether they're useable.
+AIDecide_Defender1: ; 20406 (8:4406)
+ xor a ; PLAY_AREA_ARENA
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckIfAnyAttackKnocksOutDefendingCard
+ jr nc, .cannot_ko
+ farcall CheckIfSelectedAttackIsUnusable
+ jr nc, .no_carry
+ farcall LookForEnergyNeededForAttackInHand
+ jr c, .no_carry
+
+.cannot_ko
+; check if any of the defending Pokémon's attacks deal
+; damage exactly equal to current HP, and if so,
+; only continue if that attack is useable.
+ farcall CheckIfAnyDefendingPokemonAttackDealsSameDamageAsHP
+ jr nc, .no_carry
+ call SwapTurn
+ farcall CheckIfSelectedAttackIsUnusable
+ call SwapTurn
+ jr c, .no_carry
+
+ ld a, [wSelectedAttack]
+ farcall EstimateDamage_FromDefendingPokemon
+ ld a, [wDamage]
+ ld [wce06], a
+ ld d, a
+
+; load in a the attack that was not selected,
+; and check if it is useable.
+ ld a, [wSelectedAttack]
+ ld b, a
+ ld a, $01
+ sub b
+ ld [wSelectedAttack], a
+ push de
+ call SwapTurn
+ farcall CheckIfSelectedAttackIsUnusable
+ call SwapTurn
+ pop de
+ jr c, .switch_back
+
+; the other attack is useable.
+; compare its damage to the selected attack.
+ ld a, [wSelectedAttack]
+ push de
+ farcall EstimateDamage_FromDefendingPokemon
+ pop de
+ ld a, [wDamage]
+ cp d
+ jr nc, .subtract
+
+; in case the non-selected attack is useable
+; and deals less damage than the selected attack,
+; switch back to the other attack.
+.switch_back
+ ld a, [wSelectedAttack]
+ ld b, a
+ ld a, $01
+ sub b
+ ld [wSelectedAttack], a
+ ld a, [wce06]
+ ld [wDamage], a
+
+; now the selected attack is the one that deals
+; the most damage of the two (and is useable).
+; if subtracting damage by using Defender
+; still prevents a KO, return carry.
+.subtract
+ ld a, [wDamage]
+ sub 20
+ ld d, a
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetTurnDuelistVariable
+ sub d
+ jr c, .no_carry
+ jr z, .no_carry
+ scf
+ ret
+.no_carry
+ or a
+ ret
+
+; return carry if using Defender prevents Pokémon
+; from being knocked out by an attack with recoil.
+AIDecide_Defender2: ; 20486 (8:4486)
+ ld a, ATTACK_FLAG1_ADDRESS | HIGH_RECOIL_F
+ call CheckLoadedAttackFlag
+ jr c, .recoil
+ ld a, ATTACK_FLAG1_ADDRESS | LOW_RECOIL_F
+ call CheckLoadedAttackFlag
+ jr c, .recoil
+ or a
+ ret
+
+.recoil
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call LoadCardDataToBuffer2_FromDeckIndex
+ ld a, [wSelectedAttack]
+ or a
+ jr nz, .second_attack
+; first attack
+ ld a, [wLoadedCard2Atk1EffectParam]
+ jr .check_weak
+.second_attack
+ ld a, [wLoadedCard2Atk2EffectParam]
+
+; double recoil damage if card is weak to its own color.
+.check_weak
+ ld d, a
+ push de
+ call GetArenaCardColor
+ call TranslateColorToWR
+ ld b, a
+ call GetArenaCardWeakness
+ and b
+ pop de
+ jr z, .check_resist
+ sla d
+
+; subtract 30 from recoil damage if card resists its own color.
+; if this yields a negative number, return no carry.
+.check_resist
+ push de
+ call GetArenaCardColor
+ call TranslateColorToWR
+ ld b, a
+ call GetArenaCardResistance
+ and b
+ pop de
+ jr z, .subtract
+ ld a, d
+ sub 30
+ jr c, .no_carry
+ ld d, a
+
+; subtract damage prevented by Defender.
+; if damage still knocks out card, return no carry.
+; if damage does not knock out, return carry.
+.subtract
+ ld a, d
+ or a
+ jr z, .no_carry
+ sub 20
+ ld d, a
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetTurnDuelistVariable
+ sub d
+ jr c, .no_carry
+ jr z, .no_carry
+ scf
+ ret
+.no_carry
+ or a
+ ret
+
+AIPlay_Pluspower: ; 204e8 (8:44e8)
+ ld a, [wCurrentAIFlags]
+ or AI_FLAG_USED_PLUSPOWER
+ ld [wCurrentAIFlags], a
+ ld a, [wAITrainerCardParameter]
+ ld [wAIPluspowerAttack], a
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+; returns carry if using a Pluspower can KO defending Pokémon
+; if active card cannot KO without the boost.
+; outputs in a the attack to use.
+AIDecide_Pluspower1: ; 20501 (8:4501)
+; this is mistakenly duplicated
+ xor a
+ ldh [hTempPlayAreaLocation_ff9d], a
+ xor a
+ ldh [hTempPlayAreaLocation_ff9d], a
+
+; continue if no attack can knock out.
+; if there's an attack that can, only continue
+; if it's unusable and there's no card in hand
+; to fulfill its energy cost.
+ farcall CheckIfAnyAttackKnocksOutDefendingCard
+ jr nc, .cannot_ko
+ farcall CheckIfSelectedAttackIsUnusable
+ jr nc, .no_carry
+ farcall LookForEnergyNeededForAttackInHand
+ jr c, .no_carry
+
+; cannot use an attack that knocks out.
+.cannot_ko
+; get active Pokémon's info.
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ ld [wTempTurnDuelistCardID], a
+
+; get defending Pokémon's info and check
+; its No Damage or Effect substatus.
+; if substatus is active, return.
+ call SwapTurn
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ ld [wTempNonTurnDuelistCardID], a
+ bank1call HandleNoDamageOrEffectSubstatus
+ call SwapTurn
+ jr c, .no_carry
+
+; check both attacks and decide which one
+; can KO with Pluspower boost.
+; if neither can KO, return no carry.
+ xor a ; FIRST_ATTACK_OR_PKMN_POWER
+ ld [wSelectedAttack], a
+ call .check_ko_with_pluspower
+ jr c, .kos_with_pluspower_1
+ ld a, SECOND_ATTACK
+ ld [wSelectedAttack], a
+ call .check_ko_with_pluspower
+ jr c, .kos_with_pluspower_2
+
+.no_carry
+ or a
+ ret
+
+; first attack can KO with Pluspower.
+.kos_with_pluspower_1
+ call .check_mr_mime
+ jr nc, .no_carry
+ xor a ; FIRST_ATTACK_OR_PKMN_POWER
+ scf
+ ret
+; second attack can KO with Pluspower.
+.kos_with_pluspower_2
+ call .check_mr_mime
+ jr nc, .no_carry
+ ld a, SECOND_ATTACK
+ scf
+ ret
+
+; return carry if attack is useable and KOs
+; defending Pokémon with Pluspower boost.
+.check_ko_with_pluspower ; 20562 (8:4562)
+ farcall CheckIfSelectedAttackIsUnusable
+ jr c, .unusable
+ ld a, [wSelectedAttack]
+ farcall EstimateDamage_VersusDefendingCard
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetNonTurnDuelistVariable
+ ld b, a
+ ld hl, wDamage
+ sub [hl]
+ jr c, .no_carry
+ jr z, .no_carry
+ ld a, [hl]
+ add 10 ; add Pluspower boost
+ ld c, a
+ ld a, b
+ sub c
+ ret c ; return carry if damage > HP left
+ ret nz ; does not KO
+ scf
+ ret ; KOs with Pluspower boost
+.unusable
+ or a
+ ret
+
+; returns carry if Pluspower boost does
+; not exceed 30 damage when facing Mr. Mime.
+.check_mr_mime ; 20589 (8:4589)
+ ld a, [wDamage]
+ add 10 ; add Pluspower boost
+ cp 30 ; no danger in preventing damage
+ ret c
+ call SwapTurn
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ call SwapTurn
+ ld a, e
+ cp MR_MIME
+ ret z
+; damage is >= 30 but not Mr. Mime
+ scf
+ ret
+
+; returns carry 7/10 of the time
+; if selected attack is useable, can't KO without Pluspower boost
+; can damage Mr. Mime even with Pluspower boost
+; and has a minimum damage > 0.
+; outputs in a the attack to use.
+AIDecide_Pluspower2: ; 205a5 (8:45a5)
+ xor a
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call .check_can_ko
+ jr nc, .no_carry
+ call .check_random
+ jr nc, .no_carry
+ call .check_mr_mime
+ jr nc, .no_carry
+ scf
+ ret
+.no_carry
+ or a
+ ret
+
+; returns carry if Pluspower boost does
+; not exceed 30 damage when facing Mr. Mime.
+.check_mr_mime ; 205bb (8:45bb)
+ ld a, [wDamage]
+ add 10 ; add Pluspower boost
+ cp 30 ; no danger in preventing damage
+ ret c
+ call SwapTurn
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ call SwapTurn
+ ld a, e
+ cp MR_MIME
+ ret z
+; damage is >= 30 but not Mr. Mime
+ scf
+ ret
+
+; return carry if attack is useable but cannot KO.
+.check_can_ko ; 205d7 (8:45d7)
+ farcall CheckIfSelectedAttackIsUnusable
+ jr c, .unusable
+ ld a, [wSelectedAttack]
+ farcall EstimateDamage_VersusDefendingCard
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetNonTurnDuelistVariable
+ ld b, a
+ ld hl, wDamage
+ sub [hl]
+ jr c, .no_carry
+ jr z, .no_carry
+; can't KO.
+ scf
+ ret
+.unusable
+ or a
+ ret
+
+; return carry 7/10 of the time if
+; attack is useable and minimum damage > 0.
+.check_random ; 205f6 (8:45f6)
+ farcall CheckIfSelectedAttackIsUnusable
+ jr c, .unusable
+ ld a, [wSelectedAttack]
+ farcall EstimateDamage_VersusDefendingCard
+ ld a, [wAIMinDamage]
+ cp 10
+ jr c, .unusable
+ ld a, 10
+ call Random
+ cp 3
+ ret
+
+AIPlay_Switch: ; 20612 (8:4612)
+ ld a, [wCurrentAIFlags]
+ or AI_FLAG_USED_SWITCH
+ ld [wCurrentAIFlags], a
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ xor a
+ ld [wcdb4], a
+ ret
+
+; returns carry if the active card has less energy cards
+; than the retreat cost and if AI can't play an energy
+; card from the hand to fulfill the cost
+AIDecide_Switch: ; 2062e (8:462e)
+; check if AI can already play an energy card from hand to retreat
+ ld a, [wAIPlayEnergyCardForRetreat]
+ or a
+ jr z, .check_cost_amount
+
+; can't play energy card from hand to retreat
+; compare number of energy cards attached to retreat cost
+ xor a ; PLAY_AREA_ARENA
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call GetPlayAreaCardRetreatCost
+ push af
+ ld e, PLAY_AREA_ARENA
+ farcall CountNumberOfEnergyCardsAttached
+ ld b, a
+ pop af
+ sub b
+ ; jump if cards attached > retreat cost
+ jr c, .check_cost_amount
+ cp 2
+ ; jump if retreat cost is 2 more energy cards
+ ; than the number of cards attached
+ jr nc, .switch
+
+.check_cost_amount
+ xor a ; PLAY_AREA_ARENA
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call GetPlayAreaCardRetreatCost
+ cp 3
+ ; jump if retreat cost >= 3
+ jr nc, .switch
+
+ push af
+ ld e, PLAY_AREA_ARENA
+ farcall CountNumberOfEnergyCardsAttached
+ pop bc
+ cp b
+ ; jump if energy cards attached < retreat cost
+ jr c, .switch
+ ret
+
+.switch
+ farcall AIDecideBenchPokemonToSwitchTo
+ ccf
+ ret
+
+AIPlay_GustOfWind: ; 20666 (8:4666)
+ ld a, [wCurrentAIFlags]
+ or AI_FLAG_USED_GUST_OF_WIND
+ ld [wCurrentAIFlags], a
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+AIDecide_GustOfWind: ; 2067e (8:467e)
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetNonTurnDuelistVariable
+ dec a
+ or a
+ ret z ; no bench cards
+
+; if used Gust Of Wind already,
+; do not use it again.
+ ld a, [wPreviousAIFlags]
+ and AI_FLAG_USED_GUST_OF_WIND
+ ret nz
+
+ farcall CheckIfActivePokemonCanUseAnyNonResidualAttack
+ ret nc ; no non-residual attack can be used
+
+ xor a ; PLAY_AREA_ARENA
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckIfAnyAttackKnocksOutDefendingCard
+ jr nc, .check_id ; if can't KO
+ farcall CheckIfSelectedAttackIsUnusable
+ jr nc, .no_carry ; if KO attack is useable
+ farcall LookForEnergyNeededForAttackInHand
+ jr c, .no_carry ; if energy card is in hand
+
+.check_id
+ ; skip if current active card is MEW3 or MEWTWO1
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ cp MEW3
+ jr z, .no_carry
+ cp MEWTWO1
+ jr z, .no_carry
+
+ call .FindBenchCardToKnockOut
+ ret c
+
+ xor a ; PLAY_AREA_ARENA
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call .CheckIfNoAttackDealsDamage
+ jr c, .check_bench_energy
+
+ ; skip if current arena card's color is
+ ; the defending card's weakness
+ call GetArenaCardColor
+ call TranslateColorToWR
+ ld b, a
+ call SwapTurn
+ call GetArenaCardWeakness
+ call SwapTurn
+ and b
+ jr nz, .no_carry
+
+; check weakness
+ call .FindBenchCardWithWeakness
+ ret nc ; no bench card weak to arena card
+ scf
+ ret ; found bench card weak to arena card
+
+.no_carry
+ or a
+ ret
+
+; being here means AI's arena card cannot damage player's arena card
+
+; first check if there is a card in player's bench that
+; has no attached energy cards and that the AI can damage
+.check_bench_energy
+ ; return carry if there's a bench card with weakness
+ call .FindBenchCardWithWeakness
+ ret c
+
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetNonTurnDuelistVariable
+ ld d, a
+ ld e, PLAY_AREA_ARENA
+; loop through bench and check attached energy cards
+.loop_1
+ inc e
+ dec d
+ jr z, .check_bench_hp
+ call SwapTurn
+ call GetPlayAreaCardAttachedEnergies
+ call SwapTurn
+ ld a, [wTotalAttachedEnergies]
+ or a
+ jr nz, .loop_1 ; skip if has energy attached
+ call .CheckIfCanDamageBenchedCard
+ jr nc, .loop_1
+ ld a, e
+ scf
+ ret
+
+.check_bench_hp
+ ld a, $ff
+ ld [wce06], a
+ xor a
+ ld [wce08], a
+ ld e, a
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetNonTurnDuelistVariable
+ ld d, a
+
+; find bench card with least amount of available HP
+.loop_2
+ inc e
+ dec d
+ jr z, .check_found
+ ld a, e
+ add DUELVARS_ARENA_CARD_HP
+ call GetNonTurnDuelistVariable
+ ld b, a
+ ld a, [wce06]
+ inc b
+ cp b
+ jr c, .loop_2
+ call .CheckIfCanDamageBenchedCard
+ jr nc, .loop_2
+ dec b
+ ld a, b
+ ld [wce06], a
+ ld a, e
+ ld [wce08], a
+ jr .loop_2
+
+.check_found
+ ld a, [wce08]
+ or a
+ jr z, .no_carry
+; a card was found
+
+.set_carry
+ scf
+ ret
+
+.check_can_damage
+ push bc
+ push hl
+ xor a ; PLAY_AREA_ARENA
+ farcall CheckIfCanDamageDefendingPokemon
+ pop hl
+ pop bc
+ jr nc, .loop_3
+ ld a, c
+ scf
+ ret
+
+; returns carry if any of the player's
+; benched cards is weak to color in b
+; and has a way to damage it
+.FindBenchCardWithWeakness ; 2074d (8:474d)
+ ld a, DUELVARS_BENCH
+ call GetNonTurnDuelistVariable
+ ld c, PLAY_AREA_ARENA
+.loop_3
+ inc c
+ ld a, [hli]
+ cp $ff
+ jr z, .no_carry
+ call SwapTurn
+ call LoadCardDataToBuffer1_FromDeckIndex
+ call SwapTurn
+ ld a, [wLoadedCard1Weakness]
+ and b
+ jr nz, .check_can_damage
+ jr .loop_3
+
+; returns carry if neither attack can deal damage
+.CheckIfNoAttackDealsDamage ; 2076b (8:476b)
+ xor a ; FIRST_ATTACK_OR_PKMN_POWER
+ ld [wSelectedAttack], a
+ call .CheckIfAttackDealsNoDamage
+ jr c, .second_attack
+ ret
+.second_attack
+ ld a, SECOND_ATTACK
+ ld [wSelectedAttack], a
+ call .CheckIfAttackDealsNoDamage
+ jr c, .true
+ ret
+.true
+ scf
+ ret
+
+; returns carry if attack is Pokemon Power
+; or otherwise doesn't deal any damage
+.CheckIfAttackDealsNoDamage ; 20782 (8:4782)
+ ld a, [wSelectedAttack]
+ ld e, a
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ ld d, a
+ call CopyAttackDataAndDamage_FromDeckIndex
+ ld a, [wLoadedAttackCategory]
+
+ ; skip if attack is a Power or has 0 damage
+ cp POKEMON_POWER
+ jr z, .no_damage
+ ld a, [wDamage]
+ or a
+ ret z
+
+ ; check damage against defending card
+ ld a, [wSelectedAttack]
+ farcall EstimateDamage_VersusDefendingCard
+ ld a, [wAIMaxDamage]
+ or a
+ ret nz
+
+.no_damage
+ scf
+ ret
+
+; returns carry if there is a player's bench card that
+; the opponent's current active card can KO
+.FindBenchCardToKnockOut ; 207a9 (8:47a9)
+ ld a, DUELVARS_BENCH
+ call GetNonTurnDuelistVariable
+ ld e, PLAY_AREA_BENCH_1
+
+.loop_4
+ ld a, [hli]
+ cp $ff
+ ret z
+
+; overwrite the player's active card and its HP
+; with the current bench card that is being checked
+ push hl
+ push de
+ ld b, a
+ ld a, DUELVARS_ARENA_CARD
+ call GetNonTurnDuelistVariable
+ push af
+ ld [hl], b
+ ld a, e
+ add DUELVARS_ARENA_CARD_HP
+ call GetNonTurnDuelistVariable
+ ld b, a
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetNonTurnDuelistVariable
+ push af
+ ld [hl], b
+
+ xor a ; PLAY_AREA_ARENA
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call .CheckIfAnyAttackKnocksOut
+ jr nc, .next
+ farcall CheckIfSelectedAttackIsUnusable
+ jr nc, .found
+ farcall LookForEnergyNeededForAttackInHand
+ jr c, .found
+
+; the following two local routines can be condensed into one
+; since they both revert the player's arena card
+.next
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetNonTurnDuelistVariable
+ pop af
+ ld [hl], a
+ ld a, DUELVARS_ARENA_CARD
+ call GetNonTurnDuelistVariable
+ pop af
+ ld [hl], a
+ pop de
+ inc e
+ pop hl
+ jr .loop_4
+
+; revert player's arena card and set carry
+.found
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetNonTurnDuelistVariable
+ pop af
+ ld [hl], a
+ ld a, DUELVARS_ARENA_CARD
+ call GetNonTurnDuelistVariable
+ pop af
+ ld [hl], a
+ pop de
+ ld a, e
+ pop hl
+ scf
+ ret
+
+; returns carry if any of arena card's attacks
+; KOs player card in location stored in e
+.CheckIfAnyAttackKnocksOut ; 20806 (8:4806)
+ xor a ; FIRST_ATTACK_OR_PKMN_POWER
+ call .CheckIfAttackKnocksOut
+ ret c
+ ld a, SECOND_ATTACK
+
+; returns carry if attack KOs player card
+; in location stored in e
+.CheckIfAttackKnocksOut
+ push de
+ farcall EstimateDamage_VersusDefendingCard
+ pop de
+ ld a, DUELVARS_ARENA_CARD_HP
+ add e
+ call GetNonTurnDuelistVariable
+ ld hl, wDamage
+ sub [hl]
+ ret c
+ ret nz
+ scf
+ ret
+
+; returns carry if opponent's arena card can damage
+; this benched card if it were switched with
+; the player's arena card
+.CheckIfCanDamageBenchedCard ; 20821 (8:4821)
+ push bc
+ push de
+ push hl
+
+ ; overwrite arena card data
+ ld a, e
+ add DUELVARS_ARENA_CARD
+ call GetNonTurnDuelistVariable
+ ld b, a
+ ld a, DUELVARS_ARENA_CARD
+ call GetNonTurnDuelistVariable
+ push af
+ ld [hl], b
+
+ ; overwrite arena card HP
+ ld a, e
+ add DUELVARS_ARENA_CARD_HP
+ call GetNonTurnDuelistVariable
+ ld b, a
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetNonTurnDuelistVariable
+ push af
+ ld [hl], b
+
+ xor a ; PLAY_AREA_ARENA
+ farcall CheckIfCanDamageDefendingPokemon
+ jr c, .can_damage
+
+; the following two local routines can be condensed into one
+; since they both revert the player's arena card
+
+; can't damage
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetNonTurnDuelistVariable
+ pop af
+ ld [hl], a
+ ld a, DUELVARS_ARENA_CARD
+ call GetNonTurnDuelistVariable
+ pop af
+ ld [hl], a
+ pop hl
+ pop de
+ pop bc
+ or a
+ ret
+
+.can_damage
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetNonTurnDuelistVariable
+ pop af
+ ld [hl], a
+ ld a, DUELVARS_ARENA_CARD
+ call GetNonTurnDuelistVariable
+ pop af
+ ld [hl], a
+ pop hl
+ pop de
+ pop bc
+ scf
+ ret
+
+AIPlay_Bill: ; 2086d (8:486d)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+; return carry if cards in deck > 9
+AIDecide_Bill: ; 20878 (8:4878)
+ ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
+ call GetTurnDuelistVariable
+ cp DECK_SIZE - 9
+ ret
+
+AIPlay_EnergyRemoval: ; 20880 (8:4880)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld a, [wce1a]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+; picks an energy card in the player's Play Area to remove
+AIDecide_EnergyRemoval: ; 20895 (8:4895)
+; check if the current active card can KO player's card
+; if it's possible to KO, then do not consider the player's
+; active card to remove its attached energy
+ xor a ; PLAY_AREA_ARENA
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckIfAnyAttackKnocksOutDefendingCard
+ jr nc, .cannot_ko
+ farcall CheckIfSelectedAttackIsUnusable
+ jr nc, .can_ko
+ farcall LookForEnergyNeededForAttackInHand
+ jr nc, .cannot_ko
+
+.can_ko
+ ; start checking from the bench
+ ld a, PLAY_AREA_BENCH_1
+ ld [wce0f], a
+ jr .check_bench_energy
+.cannot_ko
+ ; start checking from the arena card
+ xor a ; PLAY_AREA_ARENA
+ ld [wce0f], a
+
+; loop each card and check if it has enough energy to use any attack
+; if it does, then proceed to pick an energy card to remove
+.check_bench_energy
+ call SwapTurn
+ ld a, [wce0f]
+ ld e, a
+.loop_1
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ call GetTurnDuelistVariable
+ cp $ff
+ jr z, .default
+
+ ld d, a
+ call .CheckIfCardHasEnergyAttached
+ jr nc, .next_1
+ call .CheckIfNotEnoughEnergyToAttack
+ jr nc, .pick_energy ; jump if enough energy to attack
+.next_1
+ inc e
+ jr .loop_1
+
+.pick_energy
+; a play area card was picked to remove energy
+; store the picked energy card to remove in wce1a
+; and set carry
+ ld a, e
+ push af
+ call PickAttachedEnergyCardToRemove
+ ld [wce1a], a
+ pop af
+ call SwapTurn
+ scf
+ ret
+
+; if no card in player's Play Area was found with enough energy
+; to attack, just pick an energy card from player's active card
+; (in case the AI cannot KO it this turn)
+.default
+ ld a, [wce0f]
+ or a
+ jr nz, .check_bench_damage ; not active card
+ call .CheckIfCardHasEnergyAttached
+ jr c, .pick_energy
+
+; lastly, check what attack on player's Play Area is highest damaging
+; and pick an energy card attached to that Pokemon to remove
+.check_bench_damage
+ xor a
+ ld [wce06], a
+ ld [wce08], a
+
+ ld e, PLAY_AREA_BENCH_1
+.loop_2
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ call GetTurnDuelistVariable
+ cp $ff
+ jr z, .found_damage
+
+ ld d, a
+ call .CheckIfCardHasEnergyAttached
+ jr nc, .next_2
+ call .FindHighestDamagingAttack
+.next_2
+ inc e
+ jr .loop_2
+
+.found_damage
+ ld a, [wce08]
+ or a
+ jr z, .no_carry ; skip if none found
+ ld e, a
+ jr .pick_energy
+.no_carry
+ call SwapTurn
+ or a
+ ret
+
+; returns carry if this card has any energy cards attached
+.CheckIfCardHasEnergyAttached ; 2091a (8:491a)
+ call GetPlayAreaCardAttachedEnergies
+ ld a, [wTotalAttachedEnergies]
+ or a
+ ret z
+ scf
+ ret
+
+; returns carry if this card does not
+; have enough energy for either of its attacks
+.CheckIfNotEnoughEnergyToAttack ; 20924 (8:4924)
+ push de
+ xor a ; FIRST_ATTACK_OR_PKMN_POWER
+ ld [wSelectedAttack], a
+ ld a, e
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckEnergyNeededForAttack
+ jr nc, .enough_energy
+ pop de
+
+ push de
+ ld a, SECOND_ATTACK
+ ld [wSelectedAttack], a
+ ld a, e
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckEnergyNeededForAttack
+ jr nc, .check_surplus
+ pop de
+
+; neither attack has enough energy
+ scf
+ ret
+
+.enough_energy
+ pop de
+ or a
+ ret
+
+; first attack doesn't have enough energy (or is just a Pokemon Power)
+; but second attack has enough energy to be used
+; check if there's surplus energy for attack and, if so, return carry
+.check_surplus
+ farcall CheckIfNoSurplusEnergyForAttack
+ pop de
+ ccf
+ ret
+
+; stores in wce06 the highest damaging attack
+; for the card in play area location in e
+; and stores this card's location in wce08
+.FindHighestDamagingAttack ; 2094f (8:494f)
+ push de
+ ld a, e
+ ldh [hTempPlayAreaLocation_ff9d], a
+
+ xor a ; FIRST_ATTACK_OR_PKMN_POWER
+ farcall EstimateDamage_VersusDefendingCard
+ ld a, [wDamage]
+ or a
+ jr z, .skip_1
+ ld e, a
+ ld a, [wce06]
+ cp e
+ jr nc, .skip_1
+ ld a, e
+ ld [wce06], a ; store this damage value
+ pop de
+ ld a, e
+ ld [wce08], a ; store this location
+ jr .second_attack
+
+.skip_1
+ pop de
+
+.second_attack
+ push de
+ ld a, e
+ ldh [hTempPlayAreaLocation_ff9d], a
+
+ ld a, SECOND_ATTACK
+ farcall EstimateDamage_VersusDefendingCard
+ ld a, [wDamage]
+ or a
+ jr z, .skip_2
+ ld e, a
+ ld a, [wce06]
+ cp e
+ jr nc, .skip_2
+ ld a, e
+ ld [wce06], a ; store this damage value
+ pop de
+ ld a, e
+ ld [wce08], a ; store this location
+ ret
+.skip_2
+ pop de
+ ret
+
+AIPlay_SuperEnergyRemoval: ; 20994 (8:4994)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld a, [wce1a]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, [wce1b]
+ ldh [hTempRetreatCostCards], a
+ ld a, [wce1c]
+ ldh [hTempRetreatCostCards + 1], a
+ ld a, [wce1d]
+ ldh [hTempRetreatCostCards + 2], a
+ ld a, $ff
+ ldh [hTempRetreatCostCards + 3], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+; picks two energy cards in the player's Play Area to remove
+AIDecide_SuperEnergyRemoval: ; 209bc (8:49bc)
+ ld e, PLAY_AREA_BENCH_1
+.loop_1
+; first find an Arena card with a color energy card
+; to discard for card effect
+; return immediately if no Arena cards
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ call GetTurnDuelistVariable
+ cp $ff
+ jr z, .exit
+
+ ld d, a
+ push de
+ call .LookForNonDoubleColorless
+ pop de
+ jr c, .not_double_colorless
+ inc e
+ jr .loop_1
+
+; returns carry if an energy card other than double colorless
+; is found attached to the card in play area location e
+.LookForNonDoubleColorless
+ ld a, e
+ call CreateArenaOrBenchEnergyCardList
+ ld hl, wDuelTempList
+.loop_2
+ ld a, [hli]
+ cp $ff
+ ret z
+ call LoadCardDataToBuffer1_FromDeckIndex
+ cp DOUBLE_COLORLESS_ENERGY
+ ; any basic energy card
+ ; will set carry flag here
+ jr nc, .loop_2
+ ret
+
+.exit
+ or a
+ ret
+
+; card in Play Area location e was found with
+; a basic energy card
+.not_double_colorless
+ ld a, e
+ ld [wce0f], a
+
+; check if the current active card can KO player's card
+; if it's possible to KO, then do not consider the player's
+; active card to remove its attached energy
+ xor a ; PLAY_AREA_ARENA
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckIfAnyAttackKnocksOutDefendingCard
+ jr nc, .cannot_ko
+ farcall CheckIfSelectedAttackIsUnusable
+ jr nc, .can_ko
+ farcall LookForEnergyNeededForAttackInHand
+ jr nc, .cannot_ko
+
+.can_ko
+ ; start checking from the bench
+ call SwapTurn
+ ld e, PLAY_AREA_BENCH_1
+ jr .loop_3
+.cannot_ko
+ ; start checking from the arena card
+ call SwapTurn
+ ld e, PLAY_AREA_ARENA
+
+; loop each card and check if it has enough energy to use any attack
+; if it does, then proceed to pick energy cards to remove
+.loop_3
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ call GetTurnDuelistVariable
+ cp $ff
+ jr z, .no_carry
+
+ ld d, a
+ call .CheckIfFewerThanTwoEnergyCards
+ jr c, .next_1
+ call .CheckIfNotEnoughEnergyToAttack
+ jr nc, .found_card ; jump if enough energy to attack
+.next_1
+ inc e
+ jr .loop_3
+
+.found_card
+; a play area card was picked to remove energy
+; if this is not the Arena Card, then check
+; entire bench to pick the highest damage
+ ld a, e
+ or a
+ jr nz, .check_bench_damage
+
+; store the picked energy card to remove in wce1a
+; and set carry
+.pick_energy
+ ld [wce1b], a
+ call PickTwoAttachedEnergyCards
+ ld [wce1c], a
+ ld a, b
+ ld [wce1d], a
+ call SwapTurn
+ ld a, [wce0f]
+ push af
+ call AIPickEnergyCardToDiscard
+ ld [wce1a], a
+ pop af
+ scf
+ ret
+
+; check what attack on player's Play Area is highest damaging
+; and pick an energy card attached to that Pokemon to remove
+.check_bench_damage
+ xor a
+ ld [wce06], a
+ ld [wce08], a
+
+ ld e, PLAY_AREA_BENCH_1
+.loop_4
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ call GetTurnDuelistVariable
+ cp $ff
+ jr z, .found_damage
+
+ ld d, a
+ call .CheckIfFewerThanTwoEnergyCards
+ jr c, .next_2
+ call .CheckIfNotEnoughEnergyToAttack
+ jr c, .next_2
+ call .FindHighestDamagingAttack
+.next_2
+ inc e
+ jr .loop_4
+
+.found_damage
+ ld a, [wce08]
+ or a
+ jr z, .no_carry
+ jr .pick_energy
+.no_carry
+ call SwapTurn
+ or a
+ ret
+
+; returns carry if the number of energy cards attached
+; is fewer than 2, or if all energy combined yields
+; fewer than 2 energy
+.CheckIfFewerThanTwoEnergyCards ; 20a77 (8:4a77)
+ call GetPlayAreaCardAttachedEnergies
+ ld a, [wTotalAttachedEnergies]
+ cp 2
+ ret c ; return if fewer than 2 attached cards
+
+; count all energy attached
+; i.e. colored energy card = 1
+; and double colorless energy card = 2
+ xor a
+ ld b, NUM_COLORED_TYPES
+ ld hl, wAttachedEnergies
+.loop_5
+ add [hl]
+ inc hl
+ dec b
+ jr nz, .loop_5
+ ld b, [hl]
+ srl b
+ add b
+ cp 2
+ ret
+
+; returns carry if this card does not
+; have enough energy for either of its attacks
+.CheckIfNotEnoughEnergyToAttack ; 20a92 (8:4a92)
+ push de
+ xor a ; FIRST_ATTACK_OR_PKMN_POWER
+ ld [wSelectedAttack], a
+ ld a, e
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckEnergyNeededForAttack
+ jr nc, .enough_energy
+ pop de
+
+ push de
+ ld a, SECOND_ATTACK
+ ld [wSelectedAttack], a
+ ld a, e
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckEnergyNeededForAttack
+ jr nc, .check_surplus
+ pop de
+
+; neither attack has enough energy
+ scf
+ ret
+
+.enough_energy
+ pop de
+ or a
+ ret
+
+; first attack doesn't have enough energy (or is just a Pokemon Power)
+; but second attack has enough energy to be used
+; check if there's surplus energy for attack and, if so,
+; return carry if this surplus energy is at least 2
+.check_surplus
+ farcall CheckIfNoSurplusEnergyForAttack
+ cp 2
+ jr c, .enough_energy
+ pop de
+ scf
+ ret
+
+; stores in wce06 the highest damaging attack
+; for the card in play area location in e
+; and stores this card's location in wce08
+.FindHighestDamagingAttack ; 20ac1 (8:4ac1)
+ push de
+ ld a, e
+ ldh [hTempPlayAreaLocation_ff9d], a
+
+ xor a ; FIRST_ATTACK_OR_PKMN_POWER
+ farcall EstimateDamage_VersusDefendingCard
+ ld a, [wDamage]
+ or a
+ jr z, .skip_1
+ ld e, a
+ ld a, [wce06]
+ cp e
+ jr nc, .skip_1
+ ld a, e
+ ld [wce06], a ; store this damage value
+ pop de
+ ld a, e
+ ld [wce08], a ; store this location
+ jr .second_attack
+
+.skip_1
+ pop de
+
+.second_attack
+ push de
+ ld a, e
+ ldh [hTempPlayAreaLocation_ff9d], a
+
+ ld a, SECOND_ATTACK
+ farcall EstimateDamage_VersusDefendingCard
+ ld a, [wDamage]
+ or a
+ jr z, .skip_2
+ ld e, a
+ ld a, [wce06]
+ cp e
+ jr nc, .skip_2
+ ld a, e
+ ld [wce06], a ; store this damage value
+ pop de
+ ld a, e
+ ld [wce08], a ; store this location
+ ret
+.skip_2
+ pop de
+ ret
+
+AIPlay_PokemonBreeder: ; 20b06 (8:4b06)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, [wce1a]
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+AIDecide_PokemonBreeder: ; 20b1b (8:4b1b)
+ call IsPrehistoricPowerActive
+ jp c, .done
+
+ ld a, 7
+ ld hl, wce08
+ call ClearMemory_Bank8
+
+ xor a
+ ld [wce06], a
+ call CreateHandCardList
+ ld hl, wDuelTempList
+
+.loop_hand_1
+ ld a, [hli]
+ cp $ff
+ jr z, .not_found_in_hand
+
+; check if card in hand is any of the following
+; stage 2 Pokemon cards
+ ld d, a
+ call LoadCardDataToBuffer1_FromDeckIndex
+ cp VENUSAUR1
+ jr z, .found
+ cp VENUSAUR2
+ jr z, .found
+ cp BLASTOISE
+ jr z, .found
+ cp VILEPLUME
+ jr z, .found
+ cp ALAKAZAM
+ jr z, .found
+ cp GENGAR
+ jr nz, .loop_hand_1
+
+.found
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ push hl
+ call GetTurnDuelistVariable
+ pop hl
+ ld c, a
+ ld e, PLAY_AREA_ARENA
+
+; check Play Area for card that can evolve into
+; the picked stage 2 Pokemon
+.loop_play_area_1
+ push hl
+ push bc
+ push de
+ call CheckIfCanEvolveInto_BasicToStage2
+ pop de
+ call nc, .can_evolve
+ pop bc
+ pop hl
+ inc e
+ dec c
+ jr nz, .loop_play_area_1
+ jr .loop_hand_1
+
+.can_evolve
+ ld a, DUELVARS_ARENA_CARD_HP
+ add e
+ call GetTurnDuelistVariable
+ call ConvertHPToCounters
+ swap a
+ ld b, a
+
+; count number of energy cards attached and keep
+; the lowest 4 bits (capped at $0f)
+ call GetPlayAreaCardAttachedEnergies
+ ld a, [wTotalAttachedEnergies]
+ cp $10
+ jr c, .not_maxed_out
+ ld a, %00001111
+.not_maxed_out
+ or b
+
+; 4 high bits of a = HP counters Pokemon still has
+; 4 low bits of a = number of energy cards attached
+
+; store this score in wce08 + PLAY_AREA*
+ ld hl, wce08
+ ld c, e
+ ld b, $00
+ add hl, bc
+ ld [hl], a
+
+; store the deck index of stage 2 Pokemon in wce0f + PLAY_AREA*
+ ld hl, wce0f
+ add hl, bc
+ ld [hl], d
+
+; increase wce06 by one
+ ld hl, wce06
+ inc [hl]
+ ret
+
+.not_found_in_hand
+ ld a, [wce06]
+ or a
+ jr z, .check_evolution_and_dragonite
+
+; an evolution has been found before
+ xor a
+ ld [wce06], a
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld c, a
+ ld e, $00
+ ld d, $00
+
+; find highest score in wce08
+.loop_score_1
+ ld hl, wce08
+ add hl, de
+ ld a, [wce06]
+ cp [hl]
+ jr nc, .not_higher
+
+; store this score to wce06
+ ld a, [hl]
+ ld [wce06], a
+; store this PLay Area location to wce07
+ ld a, e
+ ld [wce07], a
+
+.not_higher
+ inc e
+ dec c
+ jr nz, .loop_score_1
+
+; store the deck index of the stage 2 card
+; that has been decided in wce1a,
+; return the Play Area location of card
+; to evolve in a and return carry
+ ld a, [wce07]
+ ld e, a
+ ld hl, wce0f
+ add hl, de
+ ld a, [hl]
+ ld [wce1a], a
+ ld a, [wce07]
+ scf
+ ret
+
+.check_evolution_and_dragonite
+ ld a, 7
+ ld hl, wce08
+ call ClearMemory_Bank8
+
+ xor a
+ ld [wce06], a
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ push hl
+
+.loop_hand_2
+ pop hl
+ ld a, [hli]
+ cp $ff
+ jr z, .check_evolution_found
+
+ push hl
+ ld d, a
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld c, a
+ ld e, PLAY_AREA_ARENA
+
+.loop_play_area_2
+; check if evolution is possible
+ push bc
+ push de
+ call CheckIfCanEvolveInto_BasicToStage2
+ pop de
+ call nc, .HandleDragonite1Evolution
+ call nc, .can_evolve
+
+; not possible to evolve or returned carry
+; when handling Dragonite1 evolution
+ pop bc
+ inc e
+ dec c
+ jr nz, .loop_play_area_2
+ jr .loop_hand_2
+
+.check_evolution_found
+ ld a, [wce06]
+ or a
+ jr nz, .evolution_was_found
+; no evolution was found before
+ or a
+ ret
+
+.evolution_was_found
+ xor a
+ ld [wce06], a
+ ld a, $ff
+ ld [wce07], a
+
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld c, a
+ ld e, $00
+ ld d, $00
+
+; find highest score in wce08 with at least
+; 2 energy cards attached
+.loop_score_2
+ ld hl, wce08
+ add hl, de
+ ld a, [wce06]
+ cp [hl]
+ jr nc, .next_score
+
+; take the lower 4 bits (total energy cards)
+; and skip if less than 2
+ ld a, [hl]
+ ld b, a
+ and %00001111
+ cp 2
+ jr c, .next_score
+
+; has at least 2 energy cards
+; store the score in wce06
+ ld a, b
+ ld [wce06], a
+; store this PLay Area location to wce07
+ ld a, e
+ ld [wce07], a
+
+.next_score
+ inc e
+ dec c
+ jr nz, .loop_score_2
+
+ ld a, [wce07]
+ cp $ff
+ jr z, .done
+
+; a card to evolve was found
+; store the deck index of the stage 2 card
+; that has been decided in wce1a,
+; return the Play Area location of card
+; to evolve in a and return carry
+ ld e, a
+ ld hl, wce0f
+ add hl, de
+ ld a, [hl]
+ ld [wce1a], a
+ ld a, [wce07]
+ scf
+ ret
+
+.done
+ or a
+ ret
+
+; return carry if card is evolving to Dragonite1 and if
+; - the card that is evolving is not Arena card and
+; number of damage counters in Play Area is under 8;
+; - the card that is evolving is Arena card and has under 5
+; damage counters or has less than 3 energy cards attached.
+.HandleDragonite1Evolution ; 20c5c (8:4c5c)
+ push af
+ push bc
+ push de
+ push hl
+ push de
+
+; check card ID
+ ld a, d
+ call GetCardIDFromDeckIndex
+ ld a, e
+ pop de
+ cp DRAGONITE1
+ jr nz, .no_carry
+
+; check card Play Area location
+ ld a, e
+ or a
+ jr z, .active_card_dragonite
+
+; the card that is evolving is not active card
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld b, a
+ ld c, 0
+
+; count damage counters in Play Area
+.loop_play_area_damage
+ dec b
+ ld e, b
+ push bc
+ call GetCardDamageAndMaxHP
+ pop bc
+ call ConvertHPToCounters
+ add c
+ ld c, a
+
+ ld a, b
+ or a
+ jr nz, .loop_play_area_damage
+
+; compare number of total damage counters
+; with 7, if less or equal to that, set carry
+ ld a, 7
+ cp c
+ jr c, .no_carry
+ jr .set_carry
+
+.active_card_dragonite
+; the card that is evolving is active card
+; compare number of this card's damage counters
+; with 5, if less than that, set carry
+ ld e, PLAY_AREA_ARENA
+ call GetCardDamageAndMaxHP
+ cp 5
+ jr c, .set_carry
+
+; compare number of this card's attached energy cards
+; with 3, if less than that, set carry
+ ld e, PLAY_AREA_ARENA
+ farcall CountNumberOfEnergyCardsAttached
+ cp 3
+ jr c, .set_carry
+ jr .no_carry
+
+.no_carry
+ pop hl
+ pop de
+ pop bc
+ pop af
+ ret
+
+.set_carry
+ pop hl
+ pop de
+ pop bc
+ pop af
+ scf
+ ret
+
+AIPlay_ProfessorOak: ; 20cae (8:4cae)
+ ld a, [wCurrentAIFlags]
+ or AI_FLAG_USED_PROFESSOR_OAK | AI_FLAG_MODIFIED_HAND
+ ld [wCurrentAIFlags], a
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+; sets carry if AI determines a score of playing
+; Professor Oak is over a certain threshold.
+AIDecide_ProfessorOak: ; 20cc1 (8:4cc1)
+; return if cards in deck <= 6
+ ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
+ call GetTurnDuelistVariable
+ cp DECK_SIZE - 6
+ ret nc
+
+ ld a, [wOpponentDeckID]
+ cp LEGENDARY_ARTICUNO_DECK_ID
+ jp z, .HandleLegendaryArticunoDeck
+ cp EXCAVATION_DECK_ID
+ jp z, .HandleExcavationDeck
+ cp WONDERS_OF_SCIENCE_DECK_ID
+ jp z, .HandleWondersOfScienceDeck
+
+; return if cards in deck <= 14
+.check_cards_deck
+ ld a, [hl]
+ cp DECK_SIZE - 14
+ ret nc
+
+; initialize score
+ ld a, $1e
+ ld [wce06], a
+
+; check number of cards in hand
+.check_cards_hand
+ ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND
+ call GetTurnDuelistVariable
+ cp 4
+ jr nc, .more_than_3_cards
+
+; less than 4 cards in hand
+ ld a, [wce06]
+ add $32
+ ld [wce06], a
+ jr .check_energy_cards
+
+.more_than_3_cards
+ cp 9
+ jr c, .check_energy_cards
+
+; more than 8 cards
+ ld a, [wce06]
+ sub $1e
+ ld [wce06], a
+
+.check_energy_cards
+ farcall CreateEnergyCardListFromHand
+ jr nc, .handle_blastoise
+
+; no energy cards in hand
+ ld a, [wce06]
+ add $28
+ ld [wce06], a
+
+.handle_blastoise
+ ld a, MUK
+ call CountPokemonIDInBothPlayAreas
+ jr c, .check_hand
+
+; no Muk in Play Area
+ ld a, BLASTOISE
+ call CountPokemonIDInPlayArea
+ jr nc, .check_hand
+
+; at least one Blastoise in AI Play Area
+ ld a, WATER_ENERGY
+ farcall LookForCardIDInHand
+ jr nc, .check_hand
+
+; no Water energy in hand
+ ld a, [wce06]
+ add $0a
+ ld [wce06], a
+
+; this part seems buggy
+; the AI loops through all the cards in hand and checks
+; if any of them is not a Pokemon card and has Basic stage.
+; it seems like the intention was that if there was
+; any Basic Pokemon still in hand, the AI would add to the score.
+.check_hand
+ call CreateHandCardList
+ ld hl, wDuelTempList
+.loop_hand
+ ld a, [hli]
+ cp $ff
+ jr z, .check_evolution
+
+ call LoadCardDataToBuffer1_FromDeckIndex
+ ld a, [wLoadedCard1Type]
+ cp TYPE_ENERGY
+ jr c, .loop_hand ; bug, should be jr nc
+
+ ld a, [wLoadedCard1Stage]
+ or a
+ jr nz, .loop_hand
+
+ ld a, [wce06]
+ add $0a
+ ld [wce06], a
+
+.check_evolution
+ xor a
+ ld [wce0f], a
+ ld [wce0f + 1], a
+
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld d, a
+ ld e, PLAY_AREA_ARENA
+
+.loop_play_area
+ push de
+ call .LookForEvolution
+ pop de
+ jr nc, .not_in_hand
+
+; there's a card in hand that can evolve
+ ld a, $01
+ ld [wce0f], a
+
+.not_in_hand
+; check if a card that can evolve was found at all
+; if not, go to the next card in the Play Area
+ ld a, [wce08]
+ cp $01
+ jr nz, .next_play_area
+
+; if it was found, set wce0f + 1 to $01
+ ld a, $01
+ ld [wce0f + 1], a
+
+.next_play_area
+ inc e
+ dec d
+ jr nz, .loop_play_area
+
+; if a card was found that evolves...
+ ld a, [wce0f + 1]
+ or a
+ jr z, .check_score
+
+; ...but that card is not in the hand...
+ ld a, [wce0f]
+ or a
+ jr nz, .check_score
+
+; ...add to the score
+ ld a, [wce06]
+ add $0a
+ ld [wce06], a
+
+; only return carry if score > $3c
+.check_score
+ ld a, [wce06]
+ ld b, $3c
+ cp b
+ jr nc, .set_carry
+ or a
+ ret
+
+.set_carry
+ scf
+ ret
+
+; return carry if there's a card in the hand that
+; can evolve the card in Play Area location in e.
+; sets wce08 to $01 if any card is found that can
+; evolve regardless of card location.
+.LookForEvolution ; 20d9d (8:4d9d)
+ xor a
+ ld [wce08], a
+ ld d, 0
+
+; loop through the whole deck to check if there's
+; a card that can evolve this Pokemon.
+.loop_deck_evolution
+ push de
+ call CheckIfCanEvolveInto
+ pop de
+ jr nc, .can_evolve
+.evolution_not_in_hand
+ inc d
+ ld a, DECK_SIZE
+ cp d
+ jr nz, .loop_deck_evolution
+
+ or a
+ ret
+
+; a card was found that can evolve, set wce08 to $01
+; and if the card is in the hand, return carry.
+; otherwise resume looping through deck.
+.can_evolve
+ ld a, $01
+ ld [wce08], a
+ ld a, DUELVARS_CARD_LOCATIONS
+ add d
+ call GetTurnDuelistVariable
+ cp CARD_LOCATION_HAND
+ jr nz, .evolution_not_in_hand
+
+ scf
+ ret
+
+; handles Legendary Articuno Deck AI logic.
+.HandleLegendaryArticunoDeck ; 20dc3 (8:4dc3)
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp 3
+ jr nc, .check_playable_cards
+
+; has less than 3 Pokemon in Play Area.
+ push af
+ call CreateHandCardList
+ pop af
+ ld d, a
+ ld e, PLAY_AREA_ARENA
+
+; if no cards in hand evolve cards in Play Area,
+; returns carry.
+.loop_play_area_articuno
+ ld a, DUELVARS_ARENA_CARD
+ add e
+
+ push de
+ call GetTurnDuelistVariable
+ farcall CheckForEvolutionInList
+ pop de
+ jr c, .check_playable_cards
+
+ inc e
+ ld a, d
+ cp e
+ jr nz, .loop_play_area_articuno
+
+.set_carry_articuno
+ scf
+ ret
+
+; if there are more than 3 energy cards in hand,
+; return no carry, otherwise check for playable cards.
+.check_playable_cards
+ call CountOppEnergyCardsInHand
+ cp 4
+ jr nc, .no_carry_articuno
+
+; remove both Professor Oak cards from list
+; before checking for playable cards
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ ld e, PROFESSOR_OAK
+ farcall RemoveCardIDInList
+ ld e, PROFESSOR_OAK
+ farcall RemoveCardIDInList
+
+; look in hand for cards that can be played.
+; if a card that cannot be played is found, return no carry.
+; otherwise return carry.
+.loop_hand_articuno
+ ld a, [hli]
+ cp $ff
+ jr z, .set_carry_articuno
+ push hl
+ farcall CheckIfCardCanBePlayed
+ pop hl
+ jr c, .loop_hand_articuno
+
+.no_carry_articuno
+ or a
+ ret
+
+; handles Excavation deck AI logic.
+; sets score depending on whether there's no
+; Mysterious Fossil in play and in hand.
+.HandleExcavationDeck ; 20e11 (8:4e11)
+; return no carry if cards in deck < 15
+ ld a, [hl]
+ cp 46
+ ret nc
+
+; look for Mysterious Fossil
+ ld a, MYSTERIOUS_FOSSIL
+ call LookForCardIDInHandAndPlayArea
+ jr c, .found_mysterious_fossil
+ ld a, $50
+ ld [wce06], a
+ jp .check_cards_hand
+.found_mysterious_fossil
+ ld a, $1e
+ ld [wce06], a
+ jp .check_cards_hand
+
+; handles Wonders of Science AI logic.
+; if there's either Grimer or Muk in hand,
+; do not play Professor Oak.
+.HandleWondersOfScienceDeck ; 20e2c (8:4e2c)
+ ld a, GRIMER
+ call LookForCardIDInHandList_Bank8
+ jr c, .found_grimer_or_muk
+ ld a, MUK
+ call LookForCardIDInHandList_Bank8
+ jr c, .found_grimer_or_muk
+
+ ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
+ call GetTurnDuelistVariable
+ jp .check_cards_deck
+
+.found_grimer_or_muk
+ or a
+ ret
+
+AIPlay_EnergyRetrieval: ; 20e44 (8:4e44)
+ ld a, [wCurrentAIFlags]
+ or AI_FLAG_MODIFIED_HAND
+ ld [wCurrentAIFlags], a
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld a, [wce1a]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, [wce1b]
+ ldh [hTempRetreatCostCards], a
+ cp $ff
+ jr z, .asm_20e68
+ ld a, $ff
+ ldh [$ffa3], a
+.asm_20e68
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+; checks whether AI can play Energy Retrieval and
+; picks the energy cards from the discard pile,
+; and duplicate cards in hand to discard.
+AIDecide_EnergyRetrieval: ; 20e6e (8:4e6e)
+; return no carry if no cards in hand
+ farcall CreateEnergyCardListFromHand
+ jp nc, .no_carry
+
+; handle Go Go Rain Dance deck
+; return no carry if there's no Muk card in play and
+; if there's no Blastoise card in Play Area
+; if there's a Muk in play, continue as normal
+ ld a, [wOpponentDeckID]
+ cp GO_GO_RAIN_DANCE_DECK_ID
+ jr nz, .start
+ ld a, MUK
+ call CountPokemonIDInBothPlayAreas
+ jr c, .start
+ ld a, BLASTOISE
+ call CountPokemonIDInPlayArea
+ jp nc, .no_carry
+
+.start
+; find duplicate cards in hand
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ call FindDuplicateCards
+ jp c, .no_carry
+
+ ld [wce06], a
+ ld a, CARD_LOCATION_DISCARD_PILE
+ call FindBasicEnergyCardsInLocation
+ jp c, .no_carry
+
+; some basic energy cards were found in Discard Pile
+ ld a, $ff
+ ld [wce1a], a
+ ld [wce1b], a
+ ld [wce1c], a
+
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld d, a
+ ld e, PLAY_AREA_ARENA
+
+; first check if there are useful energy cards in the list
+; and choose them for retrieval first
+.loop_play_area
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ push de
+
+; load this card's ID in wTempCardID
+; and this card's Type in wTempCardType
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ ld [wTempCardID], a
+ call LoadCardDataToBuffer1_FromCardID
+ pop de
+ ld a, [wLoadedCard1Type]
+ or TYPE_ENERGY
+ ld [wTempCardType], a
+
+; loop the energy cards in the Discard Pile
+; and check if they are useful for this Pokemon
+ ld hl, wDuelTempList
+.loop_energy_cards_1
+ ld a, [hli]
+ cp $ff
+ jr z, .next_play_area
+
+ ld b, a
+ push hl
+ farcall CheckIfEnergyIsUseful
+ pop hl
+ jr nc, .loop_energy_cards_1
+
+ ld a, [wce1a]
+ cp $ff
+ jr nz, .second_energy_1
+
+; check if there were already chosen cards,
+; if this is the second chosen card, return carry
+
+; first energy card found
+ ld a, b
+ ld [wce1a], a
+ call RemoveCardFromList
+ jr .next_play_area
+.second_energy_1
+ ld a, b
+ ld [wce1b], a
+ jr .set_carry
+
+.next_play_area
+ inc e
+ dec d
+ jr nz, .loop_play_area
+
+; next, if there are still energy cards left to choose,
+; loop through the energy cards again and select
+; them in order.
+ ld hl, wDuelTempList
+.loop_energy_cards_2
+ ld a, [hli]
+ cp $ff
+ jr z, .check_chosen
+ ld b, a
+ ld a, [wce1a]
+ cp $ff
+ jr nz, .second_energy_2
+ ld a, b
+ ld [wce1a], a
+ call RemoveCardFromList
+ jr .loop_energy_cards_2
+
+.second_energy_2
+ ld a, b
+ ld [wce1b], a
+ jr .set_carry
+
+; will set carry if at least one has been chosen
+.check_chosen
+ ld a, [wce1a]
+ cp $ff
+ jr nz, .set_carry
+.no_carry
+ or a
+ ret
+
+.set_carry
+ ld a, [wce06]
+ scf
+ ret
+
+; remove an element from the list
+; and shortens it accordingly
+; input:
+; hl = pointer to element after the one to remove
+RemoveCardFromList: ; 20f27 (8:4f27)
+ push de
+ ld d, h
+ ld e, l
+ dec hl
+ push hl
+.loop_remove
+ ld a, [de]
+ ld [hli], a
+ cp $ff
+ jr z, .done_remove
+ inc de
+ jr .loop_remove
+.done_remove
+ pop hl
+ pop de
+ ret
+
+; finds duplicates in card list in hl.
+; if a duplicate of Pokemon cards are found, return in
+; a the deck index of the second one.
+; otherwise, if a duplicate of non-Pokemon cards are found
+; return in a the deck index of the second one.
+; if no duplicates found, return carry.
+; input:
+; hl = list to look in
+; output:
+; a = deck index of duplicate card
+FindDuplicateCards: ; 20f38 (8:4f38)
+ ld a, $ff
+ ld [wce0f], a
+ ld [wce0f + 1], a
+ push hl
+
+.loop_outer
+; get ID of current card
+ pop hl
+ ld a, [hli]
+ cp $ff
+ jr z, .check_found
+ call GetCardIDFromDeckIndex
+ ld b, e
+ push hl
+
+; loop the rest of the list to find
+; another card with the same ID
+.loop_inner
+ ld a, [hli]
+ cp $ff
+ jr z, .loop_outer
+ ld c, a
+ call GetCardIDFromDeckIndex
+ ld a, e
+ cp b
+ jr nz, .loop_inner
+
+; found two cards with same ID
+ push bc
+ call GetCardType
+ pop bc
+ cp TYPE_ENERGY
+ jr c, .not_energy
+
+; they are energy or trainer cards
+; loads wce0f+1 with this card deck index
+ ld a, c
+ ld [wce0f + 1], a
+ jr .loop_outer
+
+.not_energy
+; they are Pokemon cards
+; loads wce0f with this card deck index
+ ld a, c
+ ld [wce0f], a
+ jr .loop_outer
+
+.check_found
+ ld a, [wce0f]
+ cp $ff
+ jr nz, .no_carry
+ ld a, [wce0f + 1]
+ cp $ff
+ jr nz, .no_carry
+
+; only set carry if duplicate cards were not found
+ scf
+ ret
+
+.no_carry
+; two cards with the same ID were found
+; of either Pokemon or Non-Pokemon cards
+ or a
+ ret
+
+AIPlay_SuperEnergyRetrieval: ; 20f80 (8:4f80)
+ ld a, [wCurrentAIFlags]
+ or AI_FLAG_MODIFIED_HAND
+ ld [wCurrentAIFlags], a
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld a, [wce1a]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, [wce1b]
+ ldh [hTempRetreatCostCards], a
+ ld a, [wce1c]
+ ldh [$ffa3], a
+ cp $ff
+ jr z, .asm_20fbb
+ ld a, [wce1d]
+ ldh [$ffa4], a
+ cp $ff
+ jr z, .asm_20fbb
+ ld a, [wce1e]
+ ldh [$ffa5], a
+ cp $ff
+ jr z, .asm_20fbb
+ ld a, $ff
+ ldh [$ffa6], a
+.asm_20fbb
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+AIDecide_SuperEnergyRetrieval: ; 20fc1 (8:4fc1)
+; return no carry if no cards in hand
+ farcall CreateEnergyCardListFromHand
+ jp nc, .no_carry
+
+; handle Go Go Rain Dance deck
+; return no carry if there's no Muk card in play and
+; if there's no Blastoise card in Play Area
+; if there's a Muk in play, continue as normal
+ ld a, [wOpponentDeckID]
+ cp GO_GO_RAIN_DANCE_DECK_ID
+ jr nz, .start
+ ld a, MUK
+ call CountPokemonIDInBothPlayAreas
+ jr c, .start
+ ld a, BLASTOISE
+ call CountPokemonIDInPlayArea
+ jp nc, .no_carry
+
+.start
+; find duplicate cards in hand
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ call FindDuplicateCards
+ jp c, .no_carry
+
+; remove the duplicate card in hand
+; and run the hand check again
+ ld [wce06], a
+ ld hl, wDuelTempList
+ call FindAndRemoveCardFromList
+ call FindDuplicateCards
+ jp c, .no_carry
+
+ ld [wce08], a
+ ld a, CARD_LOCATION_DISCARD_PILE
+ call FindBasicEnergyCardsInLocation
+ jp c, .no_carry
+
+; some basic energy cards were found in Discard Pile
+ ld a, $ff
+ ld [wce1b], a
+ ld [wce1c], a
+ ld [wce1d], a
+ ld [wce1e], a
+ ld [wce1f], a
+
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld d, a
+ ld e, PLAY_AREA_ARENA
+
+; first check if there are useful energy cards in the list
+; and choose them for retrieval first
+.loop_play_area
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ push de
+
+; load this card's ID in wTempCardID
+; and this card's Type in wTempCardType
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ ld [wTempCardID], a
+ call LoadCardDataToBuffer1_FromCardID
+ pop de
+ ld a, [wLoadedCard1Type]
+ or TYPE_ENERGY
+ ld [wTempCardType], a
+
+; loop the energy cards in the Discard Pile
+; and check if they are useful for this Pokemon
+ ld hl, wDuelTempList
+.loop_energy_cards_1
+ ld a, [hli]
+ cp $ff
+ jr z, .next_play_area
+
+ ld b, a
+ push hl
+ farcall CheckIfEnergyIsUseful
+ pop hl
+ jr nc, .loop_energy_cards_1
+
+; first energy
+ ld a, [wce1b]
+ cp $ff
+ jr nz, .second_energy_1
+ ld a, b
+ ld [wce1b], a
+ call RemoveCardFromList
+ jr .next_play_area
+
+.second_energy_1
+ ld a, [wce1c]
+ cp $ff
+ jr nz, .third_energy_1
+ ld a, b
+ ld [wce1c], a
+ call RemoveCardFromList
+ jr .next_play_area
+
+.third_energy_1
+ ld a, [wce1d]
+ cp $ff
+ jr nz, .fourth_energy_1
+ ld a, b
+ ld [wce1d], a
+ call RemoveCardFromList
+ jr .next_play_area
+
+.fourth_energy_1
+ ld a, b
+ ld [wce1e], a
+ jr .set_carry
+
+.next_play_area
+ inc e
+ dec d
+ jr nz, .loop_play_area
+
+; next, if there are still energy cards left to choose,
+; loop through the energy cards again and select
+; them in order.
+ ld hl, wDuelTempList
+.loop_energy_cards_2
+ ld a, [hli]
+ cp $ff
+ jr z, .check_chosen
+ ld b, a
+ ld a, [wce1b]
+ cp $ff
+ jr nz, .second_energy_2
+ ld a, b
+
+; first energy
+ ld [wce1b], a
+ call RemoveCardFromList
+ jr .loop_energy_cards_2
+
+.second_energy_2
+ ld a, [wce1c]
+ cp $ff
+ jr nz, .third_energy_2
+ ld a, b
+ ld [wce1c], a
+ call RemoveCardFromList
+ jr .loop_energy_cards_2
+
+.third_energy_2
+ ld a, [wce1d]
+ cp $ff
+ jr nz, .fourth_energy
+ ld a, b
+ ld [wce1d], a
+ call RemoveCardFromList
+ jr .loop_energy_cards_2
+
+.fourth_energy
+ ld a, b
+ ld [wce1e], a
+ jr .set_carry
+
+; will set carry if at least one has been chosen
+.check_chosen
+ ld a, [wce1b]
+ cp $ff
+ jr nz, .set_carry
+
+.no_carry
+ or a
+ ret
+.set_carry
+ ld a, [wce08]
+ ld [wce1a], a
+ ld a, [wce06]
+ scf
+ ret
+
+; finds the card with deck index a in list hl,
+; and removes it from the list.
+; the card HAS to exist in the list, since this
+; routine does not check for the terminating byte $ff!
+; input:
+; a = card deck index to look
+; hl = pointer to list of cards
+FindAndRemoveCardFromList: ; 210d5 (8:50d5)
+ push hl
+ ld b, a
+.loop_duplicate
+ ld a, [hli]
+ cp b
+ jr nz, .loop_duplicate
+ call RemoveCardFromList
+ pop hl
+ ret
+
+AIPlay_PokemonCenter: ; 210e0 (8:50e0)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+AIDecide_PokemonCenter: ; 210eb (8:50eb)
+ xor a
+ ldh [hTempPlayAreaLocation_ff9d], a
+
+; return if active Pokemon can KO player's card.
+ farcall CheckIfAnyAttackKnocksOutDefendingCard
+ jr nc, .start
+ farcall CheckIfSelectedAttackIsUnusable
+ jr nc, .no_carry
+ farcall LookForEnergyNeededForAttackInHand
+ jr c, .no_carry
+
+.start
+ xor a
+ ld [wce06], a
+ ld [wce08], a
+ ld [wce0f], a
+
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld d, a
+ ld e, PLAY_AREA_ARENA
+
+.loop_play_area
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ push de
+ call GetTurnDuelistVariable
+ call LoadCardDataToBuffer1_FromDeckIndex
+ ld a, e ; useless instruction
+ pop de
+
+; get this Pokemon's current HP in number of counters
+; and add it to the total.
+ ld a, [wLoadedCard1HP]
+ call ConvertHPToCounters
+ ld b, a
+ ld a, [wce06]
+ add b
+ ld [wce06], a
+
+; get this Pokemon's current damage counters
+; and add it to the total.
+ call GetCardDamageAndMaxHP
+ call ConvertHPToCounters
+ ld b, a
+ ld a, [wce08]
+ add b
+ ld [wce08], a
+
+; get this Pokemon's number of attached energy cards
+; and add it to the total.
+; if there's overflow, return no carry.
+ call GetPlayAreaCardAttachedEnergies
+ ld a, [wTotalAttachedEnergies]
+ ld b, a
+ ld a, [wce0f]
+ add b
+ jr c, .no_carry
+ ld [wce0f], a
+
+ inc e
+ dec d
+ jr nz, .loop_play_area
+
+; if (number of damage counters / 2) < (total energy cards attached)
+; return no carry.
+ ld a, [wce08]
+ srl a
+ ld hl, wce0f
+ cp [hl]
+ jr c, .no_carry
+
+; if (number of HP counters * 6 / 10) >= (number of damage counters)
+; return no carry.
+ ld a, [wce06]
+ ld l, a
+ ld h, 6
+ call HtimesL
+ call CalculateWordTensDigit
+ ld a, l
+ ld hl, wce08
+ cp [hl]
+ jr nc, .no_carry
+
+ scf
+ ret
+
+.no_carry
+ or a
+ ret
+
+AIPlay_ImposterProfessorOak: ; 21170 (8:5170)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+; sets carry depending on player's number of cards
+; in deck in in hand.
+AIDecide_ImposterProfessorOak: ; 2117b (8:517b)
+ ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
+ call GetNonTurnDuelistVariable
+ cp DECK_SIZE - 14
+ jr c, .more_than_14_cards
+
+; if player has less than 14 cards in deck, only
+; set carry if number of cards in their hands < 6
+ ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND
+ call GetNonTurnDuelistVariable
+ cp 6
+ jr c, .set_carry
+.no_carry
+ or a
+ ret
+
+; if player has more than 14 cards in deck, only
+; set carry if number of cards in their hands >= 9
+.more_than_14_cards
+ ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND
+ call GetNonTurnDuelistVariable
+ cp 9
+ jr c, .no_carry
+.set_carry
+ scf
+ ret
+
+AIPlay_EnergySearch: ; 2119a (8:519a)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+; AI checks for playing Energy Search
+AIDecide_EnergySearch: ; 211aa (8:51aa)
+ farcall CreateEnergyCardListFromHand
+ jr c, .start
+ call .CheckForUsefulEnergyCards
+ jr c, .start
+
+; there are energy cards in hand and at least
+; one of them is useful to a card in Play Area
+.no_carry
+ or a
+ ret
+
+.start
+ ld a, [wOpponentDeckID]
+ cp HEATED_BATTLE_DECK_ID
+ jr z, .heated_battle
+ cp WONDERS_OF_SCIENCE_DECK_ID
+ jr z, .wonders_of_science
+
+; if no energy cards in deck, return no carry
+ ld a, CARD_LOCATION_DECK
+ call FindBasicEnergyCardsInLocation
+ jr c, .no_carry
+
+; if any of the energy cards in deck is useful
+; return carry right away...
+ call .CheckForUsefulEnergyCards
+ jr c, .no_useful
+ scf
+ ret
+
+; ...otherwise save the list in a before return carry.
+.no_useful
+ ld a, [wDuelTempList]
+ scf
+ ret
+
+; Heated Battle deck only searches for Fire and Lightning
+; if they are found to be useful to some card in Play Area
+.heated_battle
+ ld a, CARD_LOCATION_DECK
+ call FindBasicEnergyCardsInLocation
+ jr c, .no_carry
+ call .CheckUsefulFireOrLightningEnergy
+ jr c, .no_carry
+ scf
+ ret
+
+; this subroutine has a bug.
+; it was supposed to use the .CheckUsefulGrassEnergy subroutine
+; but uses .CheckUsefulFireOrLightningEnergy instead.
+.wonders_of_science
+ ld a, CARD_LOCATION_DECK
+ call FindBasicEnergyCardsInLocation
+ jr c, .no_carry
+ call .CheckUsefulFireOrLightningEnergy
+ jr c, .no_carry
+ scf
+ ret
+
+; return carry if cards in wDuelTempList are not
+; useful to any of the Play Area Pokemon
+.CheckForUsefulEnergyCards ; 211f1 (8:51f1)
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld d, a
+ ld e, PLAY_AREA_ARENA
+
+.loop_play_area_1
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ push de
+ call GetTurnDuelistVariable
+
+; store ID and type of card
+ call GetCardIDFromDeckIndex
+ ld a, e
+ ld [wTempCardID], a
+ call LoadCardDataToBuffer1_FromCardID
+ pop de
+ ld a, [wLoadedCard1Type]
+ or TYPE_ENERGY
+ ld [wTempCardType], a
+
+; look in list for a useful energy,
+; is any is found return no carry.
+ ld hl, wDuelTempList
+.loop_energy_1
+ ld a, [hli]
+ cp $ff
+ jr z, .none_found
+ ld b, a
+ push hl
+ farcall CheckIfEnergyIsUseful
+ pop hl
+ jr nc, .loop_energy_1
+
+ ld a, b
+ or a
+ ret
+
+.none_found
+ inc e
+ ld a, e
+ cp d
+ jr nz, .loop_play_area_1
+
+ scf
+ ret
+
+; checks whether there are useful energies
+; only for Fire and Lightning type Pokemon cards
+; in Play Area. If none found, return carry.
+.CheckUsefulFireOrLightningEnergy ; 2122e (8:522e)
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld d, a
+ ld e, PLAY_AREA_ARENA
+
+.loop_play_area_2
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ push de
+ call GetTurnDuelistVariable
+
+; get card's ID and Type
+ call GetCardIDFromDeckIndex
+ ld a, e
+ ld [wTempCardID], a
+ call LoadCardDataToBuffer1_FromCardID
+ pop de
+ ld a, [wLoadedCard1Type]
+ or TYPE_ENERGY
+
+; only do check if the Pokemon's type
+; is either Fire or Lightning
+ cp TYPE_ENERGY_FIRE
+ jr z, .fire_or_lightning
+ cp TYPE_ENERGY_LIGHTNING
+ jr nz, .next_play_area
+
+; loop each energy card in list
+.fire_or_lightning
+ ld [wTempCardType], a
+ ld hl, wDuelTempList
+.loop_energy_2
+ ld a, [hli]
+ cp $ff
+ jr z, .next_play_area
+
+; if this energy card is useful,
+; return no carry.
+ ld b, a
+ push hl
+ farcall CheckIfEnergyIsUseful
+ pop hl
+ jr nc, .loop_energy_2
+
+ ld a, b
+ or a
+ ret
+
+.next_play_area
+ inc e
+ ld a, e
+ cp d
+ jr nz, .loop_play_area_2
+
+; no card was found to be useful
+; for Fire/Lightning type Pokemon card.
+ scf
+ ret
+
+; checks whether there are useful energies
+; only for Grass type Pokemon cards
+; in Play Area. If none found, return carry.
+.CheckUsefulGrassEnergy ; 21273 (8:5273)
+; unreferenced
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld d, a
+ ld e, PLAY_AREA_ARENA
+
+.loop_play_area_3
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ push de
+ call GetTurnDuelistVariable
+
+; get card's ID and Type
+ call GetCardIDFromDeckIndex
+ ld a, e
+ ld [wTempCardID], a
+ call LoadCardDataToBuffer1_FromCardID
+ pop de
+ ld a, [wLoadedCard1Type]
+ or TYPE_ENERGY
+
+; only do check if the Pokemon's type is Grass
+ cp TYPE_ENERGY_GRASS
+ jr nz, .next_play_area_3
+
+; loop each energy card in list
+ ld [wTempCardType], a
+ ld hl, wDuelTempList
+.loop_energy_3
+ ld a, [hli]
+ cp $ff
+ jr z, .next_play_area_3
+
+; if this energy card is useful,
+; return no carry.
+ ld b, a
+ push hl
+ farcall CheckIfEnergyIsUseful
+ pop hl
+ jr nc, .loop_energy_3
+
+ ld a, b
+ or a
+ ret
+
+.next_play_area_3
+ inc e
+ ld a, e
+ cp d
+ jr nz, .loop_play_area_3
+
+; no card was found to be useful
+; for Grass type Pokemon card.
+ scf
+ ret
+
+AIPlay_Pokedex: ; 212b4 (8:52b4)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wce1a]
+ ldh [hTemp_ffa0], a
+ ld a, [wce1b]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, [wce1c]
+ ldh [hTempRetreatCostCards], a
+ ld a, [wce1d]
+ ldh [$ffa3], a
+ ld a, [wce1e]
+ ldh [$ffa4], a
+ ld a, $ff
+ ldh [$ffa5], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+AIDecide_Pokedex: ; 212dc (8:52dc)
+ ld a, [wAIPokedexCounter]
+ cp 5 + 1
+ jr c, .no_carry ; return if counter hasn't reached 6 yet
+
+; return no carry if number of cards in deck <= 4
+ ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
+ call GetTurnDuelistVariable
+ cp DECK_SIZE - 4
+ jr nc, .no_carry
+
+; has a 3 in 10 chance of actually playing card
+ ld a, 10
+ call Random
+ cp 3
+ jr c, .pick_cards
+
+.no_carry
+ or a
+ ret
+
+.pick_cards
+; the following comparison is disregarded
+; the Wonders of Science deck was probably intended
+; to use PickPokedexCards_Unreferenced instead
+ ld a, [wOpponentDeckID]
+ cp WONDERS_OF_SCIENCE_DECK_ID
+ jp PickPokedexCards ; bug, should be jp nz
+
+; picks order of the cards in deck from the effects of Pokedex.
+; prioritizes Pokemon cards, then Trainer cards, then energy cards.
+; stores the resulting order in wce1a.
+PickPokedexCards_Unreferenced: ; 212ff (8:52ff)
+; unreferenced
+ xor a
+ ld [wAIPokedexCounter], a ; reset counter
+
+ ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
+ call GetTurnDuelistVariable
+ add DUELVARS_DECK_CARDS
+ ld l, a
+ lb de, $00, $00
+ ld b, 5
+
+; run through 5 of the remaining cards in deck
+.next_card
+ ld a, [hli]
+ ld c, a
+ call .GetCardType
+
+; load this card's deck index and type in memory
+; wce08 = card types
+; wce0f = card deck indices
+ push hl
+ ld hl, wce08
+ add hl, de
+ ld [hl], a
+ ld hl, wce0f
+ add hl, de
+ ld [hl], c
+ pop hl
+
+ inc e
+ dec b
+ jr nz, .next_card
+
+; terminate the wce08 list
+ ld a, $ff
+ ld [wce08 + 5], a
+
+ ld de, wce1a
+
+; find Pokemon
+ ld hl, wce08
+ ld c, -1
+ ld b, $00
+
+; run through the stored cards
+; and look for any Pokemon cards.
+.loop_pokemon
+ inc c
+ ld a, [hli]
+ cp $ff
+ jr z, .find_trainers
+ cp TYPE_ENERGY
+ jr nc, .loop_pokemon
+; found a Pokemon card
+; store it in wce1a list
+ push hl
+ ld hl, wce0f
+ add hl, bc
+ ld a, [hl]
+ pop hl
+ ld [de], a
+ inc de
+ jr .loop_pokemon
+
+; run through the stored cards
+; and look for any Trainer cards.
+.find_trainers
+ ld hl, wce08
+ ld c, -1
+ ld b, $00
+
+.loop_trainers
+ inc c
+ ld a, [hli]
+ cp $ff
+ jr z, .find_energy
+ cp TYPE_TRAINER
+ jr nz, .loop_trainers
+; found a Trainer card
+; store it in wce1a list
+ push hl
+ ld hl, wce0f
+ add hl, bc
+ ld a, [hl]
+ pop hl
+ ld [de], a
+ inc de
+ jr .loop_trainers
+
+.find_energy
+ ld hl, wce08
+ ld c, -1
+ ld b, $00
+
+; run through the stored cards
+; and look for any energy cards.
+.loop_energy
+ inc c
+ ld a, [hli]
+ cp $ff
+ jr z, .done
+ and TYPE_ENERGY
+ jr z, .loop_energy
+; found an energy card
+; store it in wce1a list
+ push hl
+ ld hl, wce0f
+ add hl, bc
+ ld a, [hl]
+ pop hl
+ ld [de], a
+ inc de
+ jr .loop_energy
+
+.done
+ scf
+ ret
+
+.GetCardType ; 21383 (8:5383)
+ push bc
+ push de
+ call GetCardIDFromDeckIndex
+ call GetCardType
+ pop de
+ pop bc
+ ret
+
+; picks order of the cards in deck from the effects of Pokedex.
+; prioritizes energy cards, then Pokemon cards, then Trainer cards.
+; stores the resulting order in wce1a.
+PickPokedexCards: ; 2138e (8:538e)
+ xor a
+ ld [wAIPokedexCounter], a ; reset counter ; reset counter
+
+ ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
+ call GetTurnDuelistVariable
+ add DUELVARS_DECK_CARDS
+ ld l, a
+ lb de, $00, $00
+ ld b, 5
+
+; run through 5 of the remaining cards in deck
+.next_card
+ ld a, [hli]
+ ld c, a
+ call .GetCardType
+
+; load this card's deck index and type in memory
+; wce08 = card types
+; wce0f = card deck indices
+ push hl
+ ld hl, wce08
+ add hl, de
+ ld [hl], a
+ ld hl, wce0f
+ add hl, de
+ ld [hl], c
+ pop hl
+
+ inc e
+ dec b
+ jr nz, .next_card
+
+; terminate the wce08 list
+ ld a, $ff
+ ld [wce08 + 5], a
+
+ ld de, wce1a
+
+; find energy
+ ld hl, wce08
+ ld c, -1
+ ld b, $00
+
+; run through the stored cards
+; and look for any energy cards.
+.loop_energy
+ inc c
+ ld a, [hli]
+ cp $ff
+ jr z, .find_pokemon
+ and TYPE_ENERGY
+ jr z, .loop_energy
+; found an energy card
+; store it in wce1a list
+ push hl
+ ld hl, wce0f
+ add hl, bc
+ ld a, [hl]
+ pop hl
+ ld [de], a
+ inc de
+ jr .loop_energy
+
+.find_pokemon
+ ld hl, wce08
+ ld c, -1
+ ld b, $00
+
+; run through the stored cards
+; and look for any Pokemon cards.
+.loop_pokemon
+ inc c
+ ld a, [hli]
+ cp $ff
+ jr z, .find_trainers
+ cp TYPE_ENERGY
+ jr nc, .loop_pokemon
+; found a Pokemon card
+; store it in wce1a list
+ push hl
+ ld hl, wce0f
+ add hl, bc
+ ld a, [hl]
+ pop hl
+ ld [de], a
+ inc de
+ jr .loop_pokemon
+
+; run through the stored cards
+; and look for any Trainer cards.
+.find_trainers
+ ld hl, wce08
+ ld c, -1
+ ld b, $00
+
+.loop_trainers
+ inc c
+ ld a, [hli]
+ cp $ff
+ jr z, .done
+ cp TYPE_TRAINER
+ jr nz, .loop_trainers
+; found a Trainer card
+; store it in wce1a list
+ push hl
+ ld hl, wce0f
+ add hl, bc
+ ld a, [hl]
+ pop hl
+ ld [de], a
+ inc de
+ jr .loop_trainers
+
+.done
+ scf
+ ret
+
+.GetCardType ; 21412 (8:5412)
+ push bc
+ push de
+ call GetCardIDFromDeckIndex
+ call GetCardType
+ pop de
+ pop bc
+ ret
+
+AIPlay_FullHeal: ; 2141d (8:541d)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+AIDecide_FullHeal: ; 21428 (8:5428)
+ ld a, DUELVARS_ARENA_CARD_STATUS
+ call GetTurnDuelistVariable
+
+; skip if no status on arena card
+ or a ; NO_STATUS
+ jr z, .no_carry
+
+ and CNF_SLP_PRZ
+ cp PARALYZED
+ jr z, .paralyzed
+ cp ASLEEP
+ jr z, .asleep
+ cp CONFUSED
+ jr z, .confused
+ ; if either PSN or DBLPSN, fallthrough
+
+.set_carry
+ scf
+ ret
+
+.asleep
+; set carry if any of the following
+; cards are in the Play Area.
+ ld a, GASTLY1
+ ld b, PLAY_AREA_ARENA
+ call LookForCardIDInPlayArea_Bank8
+ jr c, .set_carry
+ ld a, GASTLY2
+ ld b, PLAY_AREA_ARENA
+ call LookForCardIDInPlayArea_Bank8
+ jr c, .set_carry
+ ld a, HAUNTER2
+ ld b, PLAY_AREA_ARENA
+ call LookForCardIDInPlayArea_Bank8
+ jr c, .set_carry
+
+; otherwise fallthrough
+
+.paralyzed
+; if Scoop Up is in hand and decided to be played, skip.
+ ld a, SCOOP_UP
+ call LookForCardIDInHandList_Bank8
+ jr nc, .no_scoop_up_prz
+ call AIDecide_ScoopUp
+ jr c, .no_carry
+
+.no_scoop_up_prz
+; if card can damage defending Pokemon...
+ xor a ; PLAY_AREA_ARENA
+ farcall CheckIfCanDamageDefendingPokemon
+ jr nc, .no_carry
+; ...and can play an energy card to retreat, set carry.
+ ld a, [wAIPlayEnergyCardForRetreat]
+ or a
+ jr nz, .set_carry
+
+; if not, check whether it's a card it would rather retreat,
+; and if it isn't, set carry.
+ farcall AIDecideWhetherToRetreat
+ jr nc, .set_carry
+
+.no_carry
+ or a
+ ret
+
+.confused
+; if Scoop Up is in hand and decided to be played, skip.
+ ld a, SCOOP_UP
+ call LookForCardIDInHandList_Bank8
+ jr nc, .no_scoop_up_cnf
+ call AIDecide_ScoopUp
+ jr c, .no_carry
+
+.no_scoop_up_cnf
+; if card can damage defending Pokemon...
+ xor a ; PLAY_AREA_ARENA
+ farcall CheckIfCanDamageDefendingPokemon
+ jr nc, .no_carry
+; ...and can play an energy card to retreat, set carry.
+ ld a, [wAIPlayEnergyCardForRetreat]
+ or a
+ jr nz, .set_carry
+; if not, return no carry.
+ jr .no_carry
+
+AIPlay_MrFuji: ; 21497 (8:5497)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+; AI logic for playing Mr Fuji
+AIDecide_MrFuji: ; 214a7 (8:54a7)
+ ld a, $ff
+ ld [wce06], a
+ ld [wce08], a
+
+; if just one Pokemon in Play Area, skip.
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp 1
+ ret z
+
+ dec a
+ ld d, a
+ ld e, PLAY_AREA_BENCH_1
+
+; find a Pokemon in the bench that has damage counters.
+.loop_bench
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ push de
+ call GetTurnDuelistVariable
+ call LoadCardDataToBuffer1_FromDeckIndex
+ pop de
+
+ ld a, [wLoadedCard1HP]
+ ld b, a
+
+ ; skip if zero damage counters
+ call GetCardDamageAndMaxHP
+ call ConvertHPToCounters
+ or a
+ jr z, .next
+
+; a = damage counters
+; b = hp left
+ call CalculateBDividedByA_Bank8
+ cp 20
+ jr nc, .next
+
+; here, HP left in counters is less than twice
+; the number of damage counters, that is:
+; HP < 1/3 max HP
+
+; if value is less than the one found before, store this one.
+ ld hl, wce08
+ cp [hl]
+ jr nc, .next
+ ld [hl], a
+ ld a, e
+ ld [wce06], a
+.next
+ inc e
+ dec d
+ jr nz, .loop_bench
+
+ ld a, [wce06]
+ cp $ff
+ ret z
+
+ scf
+ ret
+
+AIPlay_ScoopUp: ; 214f1 (8:54f1)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld a, [wce1a]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+AIDecide_ScoopUp: ; 21506 (8:5506)
+ xor a
+ ldh [hTempPlayAreaLocation_ff9d], a
+
+; if only one Pokemon in Play Area, skip.
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp 2
+ jr c, .no_carry
+
+; handle some decks differently
+ ld a, [wOpponentDeckID]
+ cp LEGENDARY_ARTICUNO_DECK_ID
+ jr z, .HandleLegendaryArticuno
+ cp LEGENDARY_RONALD_DECK_ID
+ jp z, .HandleLegendaryRonald
+
+; if can't KO defending Pokemon, check if defending Pokemon
+; can KO this card. If so, then continue.
+; If not, return no carry.
+
+; if it can KO the defending Pokemon this turn,
+; return no carry.
+ farcall CheckIfAnyAttackKnocksOutDefendingCard
+ jr nc, .cannot_ko
+ farcall CheckIfSelectedAttackIsUnusable
+ jr nc, .no_carry
+ farcall LookForEnergyNeededForAttackInHand
+ jr c, .no_carry
+
+.cannot_ko
+ ld a, DUELVARS_ARENA_CARD_STATUS
+ call GetTurnDuelistVariable
+ and CNF_SLP_PRZ
+ cp PARALYZED
+ jr z, .cannot_retreat
+ cp ASLEEP
+ jr z, .cannot_retreat
+
+; doesn't have a status that prevents retreat.
+; so check if it has enough energy to retreat.
+; if not, return no carry.
+ xor a
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call GetPlayAreaCardRetreatCost
+ ld b, a
+ ld e, PLAY_AREA_ARENA
+ farcall CountNumberOfEnergyCardsAttached
+ cp b
+ jr c, .cannot_retreat
+
+.no_carry
+ or a
+ ret
+
+.cannot_retreat
+; store damage and total HP left
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call LoadCardDataToBuffer1_FromDeckIndex
+ ld a, [wLoadedCard1HP]
+ call ConvertHPToCounters
+ ld d, a
+
+; skip if card has no damage counters.
+ ld e, PLAY_AREA_ARENA
+ call GetCardDamageAndMaxHP
+ or a
+ jr z, .no_carry
+
+; if (total damage / total HP counters) < 7
+; return carry.
+; (this corresponds to damage counters
+; being under 70% of the max HP)
+ ld b, a
+ ld a, d
+ call CalculateBDividedByA_Bank8
+ cp 7
+ jr c, .no_carry
+
+; store Pokemon to switch to in wce1a and set carry.
+.decide_switch
+ farcall AIDecideBenchPokemonToSwitchTo
+ jr c, .no_carry
+ ld [wce1a], a
+ xor a
+ scf
+ ret
+
+; this deck will use Scoop Up on a benched Articuno2.
+; it checks if the defending Pokemon is a Snorlax,
+; but interestingly does not check for Muk in both Play Areas.
+; will also use Scoop Up on
+.HandleLegendaryArticuno
+; if less than 3 Play Area Pokemon cards, skip.
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp 3
+ jr c, .no_carry
+
+; look for Articuno2 in bench
+ ld a, ARTICUNO2
+ ld b, PLAY_AREA_BENCH_1
+ call LookForCardIDInPlayArea_Bank8
+ jr c, .articuno_bench
+
+; check Arena card
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ cp ARTICUNO2
+ jr z, .articuno_or_chansey
+ cp CHANSEY
+ jr nz, .no_carry
+
+; here either Articuno2 or Chansey
+; is the Arena Card.
+.articuno_or_chansey
+; if can't KO defending Pokemon, check if defending Pokemon
+; can KO this card. If so, then continue.
+; If not, return no carry.
+
+; if it can KO the defending Pokemon this turn,
+; return no carry.
+ farcall CheckIfAnyAttackKnocksOutDefendingCard
+ jr nc, .check_ko
+ farcall CheckIfSelectedAttackIsUnusable
+ jr nc, .no_carry
+ farcall LookForEnergyNeededForAttackInHand
+ jr c, .no_carry
+.check_ko
+ farcall CheckIfDefendingPokemonCanKnockOut
+ jr nc, .no_carry
+ jr .decide_switch
+
+.articuno_bench
+; skip if the defending card is Snorlax
+ push af
+ ld a, DUELVARS_ARENA_CARD
+ call GetNonTurnDuelistVariable
+ call SwapTurn
+ call GetCardIDFromDeckIndex
+ call SwapTurn
+ ld a, e
+ cp SNORLAX
+ pop bc
+ jr z, .no_carry
+
+; check attached energy cards.
+; if it has any, return no carry.
+ ld a, b
+.check_attached_energy
+ ld e, a
+ push af
+ farcall CountNumberOfEnergyCardsAttached
+ or a
+ pop bc
+ ld a, b
+ jr z, .no_energy
+ jp .no_carry
+
+.no_energy
+; has decided to Scoop Up benched card,
+; store $ff as the Pokemon card to switch to
+; because there's no need to switch.
+ push af
+ ld a, $ff
+ ld [wce1a], a
+ pop af
+ scf
+ ret
+
+; this deck will use Scoop Up on a benched Articuno2, Zapdos3 or Moltres2.
+; interestingly, does not check for Muk in both Play Areas.
+.HandleLegendaryRonald ; 215e7 (8:55e7)
+; if less than 3 Play Area Pokemon cards, skip.
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp 3
+ jp c, .no_carry
+
+ ld a, ARTICUNO2
+ ld b, PLAY_AREA_BENCH_1
+ call LookForCardIDInPlayArea_Bank8
+ jr c, .articuno_bench
+ ld a, ZAPDOS3
+ ld b, PLAY_AREA_BENCH_1
+ call LookForCardIDInPlayArea_Bank8
+ jr c, .check_attached_energy
+ ld a, MOLTRES2
+ ld b, PLAY_AREA_BENCH_1
+ call LookForCardIDInPlayArea_Bank8
+ jr c, .check_attached_energy
+ jp .no_carry
+
+AIPlay_Maintenance: ; 2160f (8:560f)
+ ld a, [wCurrentAIFlags]
+ or AI_FLAG_MODIFIED_HAND
+ ld [wCurrentAIFlags], a
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wce1a]
+ ldh [hTemp_ffa0], a
+ ld a, [wce1b]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+; AI logic for playing Maintenance
+AIDecide_Maintenance: ; 2162c (8:562c)
+; Imakuni? has his own thing
+ ld a, [wOpponentDeckID]
+ cp IMAKUNI_DECK_ID
+ jr z, .imakuni
+
+; skip if number of cars in hand < 4.
+ ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND
+ call GetTurnDuelistVariable
+ cp 4
+ jr c, .no_carry
+
+; list out all the hand cards and remove
+; wAITrainerCardToPlay from list.Then find any duplicate cards.
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ ld a, [wAITrainerCardToPlay]
+ call FindAndRemoveCardFromList
+; if duplicates are not found, return no carry.
+ call FindDuplicateCards
+ jp c, .no_carry
+
+; store the first duplicate card and remove it from the list.
+; run duplicate check again.
+ ld [wce1a], a
+ ld hl, wDuelTempList
+ call FindAndRemoveCardFromList
+; if duplicates are not found, return no carry.
+ call FindDuplicateCards
+ jp c, .no_carry
+
+; store the second duplicate card and return carry.
+ ld [wce1b], a
+ scf
+ ret
+
+.no_carry
+ or a
+ ret
+
+.imakuni
+; has a 2 in 10 chance of not skipping.
+ ld a, 10
+ call Random
+ cp 2
+ jr nc, .no_carry
+
+; skip if number of cards in hand < 3.
+ ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND
+ call GetTurnDuelistVariable
+ cp 3
+ jr c, .no_carry
+
+; shuffle hand cards
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ call CountCardsInDuelTempList
+ call ShuffleCards
+
+; go through each card and find
+; cards that are different from wAITrainerCardToPlay.
+; if found, add those cards to wce1a and wce1a+1.
+ ld a, [wAITrainerCardToPlay]
+ ld b, a
+ ld c, 2
+ ld de, wce1a
+
+.loop
+ ld a, [hli]
+ cp $ff
+ jr z, .no_carry
+ cp b
+ jr z, .loop
+ ld [de], a
+ inc de
+ dec c
+ jr nz, .loop
+
+; two cards were found, return carry.
+ scf
+ ret
+
+AIPlay_Recycle: ; 2169a (8:569a)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ldtx de, TrainerCardSuccessCheckText
+ bank1call TossCoin
+ jr nc, .asm_216ae
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ jr .asm_216b2
+.asm_216ae
+ ld a, $ff
+ ldh [hTemp_ffa0], a
+.asm_216b2
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+; lists cards to look for in the Discard Pile.
+; has priorities for Ghost Deck, and a "default" priority list
+; (which is the Fire Charge deck, since it's the only other
+; deck that runs a Recycle card in it.)
+AIDecide_Recycle: ; 216b8 (8:56b8)
+; no use checking if no cards in Discard Pile
+ call CreateDiscardPileCardList
+ jr c, .no_carry
+
+ ld a, $ff
+ ld [wce08], a
+ ld [wce08 + 1], a
+ ld [wce08 + 2], a
+ ld [wce08 + 3], a
+ ld [wce08 + 4], a
+
+; handle Ghost deck differently
+ ld hl, wDuelTempList
+ ld a, [wOpponentDeckID]
+ cp GHOST_DECK_ID
+ jr z, .loop_2
+
+; priority list for Fire Charge deck
+.loop_1
+ ld a, [hli]
+ cp $ff
+ jr z, .done
+
+ ld b, a
+ call LoadCardDataToBuffer1_FromDeckIndex
+
+; double colorless
+ cp DOUBLE_COLORLESS_ENERGY
+ jr nz, .chansey
+ ld a, b
+ ld [wce08], a
+ jr .loop_1
+
+.chansey
+ cp CHANSEY
+ jr nz, .tauros
+ ld a, b
+ ld [wce08 + 1], a
+ jr .loop_1
+
+.tauros
+ cp TAUROS
+ jr nz, .jigglypuff
+ ld a, b
+ ld [wce08 + 2], a
+ jr .loop_1
+
+.jigglypuff
+ cp JIGGLYPUFF1
+ jr nz, .loop_1
+ ld a, b
+ ld [wce08 + 3], a
+ jr .loop_1
+
+; loop through wce08 and set carry
+; on the first that was found in Discard Pile.
+; if none were found, return no carry.
+.done
+ ld hl, wce08
+ ld b, 5
+.loop_found
+ ld a, [hli]
+ cp $ff
+ jr nz, .set_carry
+ dec b
+ jr nz, .loop_found
+.no_carry
+ or a
+ ret
+.set_carry
+ scf
+ ret
+
+; priority list for Ghost deck
+.loop_2
+ ld a, [hli]
+ cp $ff
+ jr z, .done
+
+ ld b, a
+ call LoadCardDataToBuffer1_FromDeckIndex
+
+; gastly2
+ cp GASTLY2
+ jr nz, .gastly1
+ ld a, b
+ ld [wce08], a
+ jr .loop_2
+
+.gastly1
+ cp GASTLY1
+ jr nz, .zubat
+ ld a, b
+ ld [wce08 + 1], a
+ jr .loop_2
+
+.zubat
+ cp ZUBAT
+ jr nz, .ditto
+ ld a, b
+ ld [wce08 + 2], a
+ jr .loop_2
+
+.ditto
+ cp DITTO
+ jr nz, .meowth
+ ld a, b
+ ld [wce08 + 3], a
+ jr .loop_2
+
+.meowth
+ cp MEOWTH2
+ jr nz, .loop_2
+ ld a, b
+ ld [wce08 + 4], a
+ jr .loop_2
+
+AIPlay_Lass: ; 21755 (8:5755)
+ ld a, [wCurrentAIFlags]
+ or AI_FLAG_MODIFIED_HAND
+ ld [wCurrentAIFlags], a
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+AIDecide_Lass: ; 21768 (8:5768)
+; skip if player has less than 7 cards in hand
+ ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND
+ call GetNonTurnDuelistVariable
+ cp 7
+ jr c, .no_carry
+
+; look for Trainer cards in hand (except for Lass)
+; if any is found, return no carry.
+; otherwise, return carry.
+ call CreateHandCardList
+ ld hl, wDuelTempList
+.loop
+ ld a, [hli]
+ cp $ff
+ jr z, .set_carry
+ ld b, a
+ call LoadCardDataToBuffer1_FromDeckIndex
+ cp LASS
+ jr z, .loop
+ ld a, [wLoadedCard1Type]
+ cp TYPE_TRAINER
+ jr nz, .loop
+.no_carry
+ or a
+ ret
+.set_carry
+ scf
+ ret
+
+AIPlay_ItemFinder: ; 2178f (8:578f)
+ ld a, [wCurrentAIFlags]
+ or AI_FLAG_MODIFIED_HAND
+ ld [wCurrentAIFlags], a
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wce1a]
+ ldh [hTemp_ffa0], a
+ ld a, [wce1b]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTempRetreatCostCards], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+; checks whether there's Energy Removal in Discard Pile.
+; if so, find duplicate cards in hand to discard
+; that are not Mr Mime and Pokemon Trader cards.
+; this logic is suitable only for Strange Psyshock deck.
+AIDecide_ItemFinder: ; 217b1 (8:57b1)
+; skip if no Discard Pile.
+ call CreateDiscardPileCardList
+ jr c, .no_carry
+
+; look for Energy Removal in Discard Pile
+ ld hl, wDuelTempList
+.loop_discard_pile
+ ld a, [hli]
+ cp $ff
+ jr z, .no_carry
+ ld b, a
+ call LoadCardDataToBuffer1_FromDeckIndex
+ cp ENERGY_REMOVAL
+ jr nz, .loop_discard_pile
+; found, store this deck index
+ ld a, b
+ ld [wce06], a
+
+; before looking for cards to discard in hand,
+; remove any Mr Mime and Pokemon Trader cards.
+; this way these are guaranteed to not be discarded.
+ call CreateHandCardList
+ ld hl, wDuelTempList
+.loop_hand
+ ld a, [hli]
+ cp $ff
+ jr z, .choose_discard
+ ld b, a
+ call LoadCardDataToBuffer1_FromDeckIndex
+ cp MR_MIME
+ jr nz, .pkmn_trader
+ call RemoveCardFromList
+ jr .loop_hand
+.pkmn_trader
+ cp POKEMON_TRADER
+ jr nz, .loop_hand
+ call RemoveCardFromList
+ jr .loop_hand
+
+; choose cards to discard from hand.
+.choose_discard
+ ld hl, wDuelTempList
+
+; do not discard wAITrainerCardToPlay
+ ld a, [wAITrainerCardToPlay]
+ call FindAndRemoveCardFromList
+; find any duplicates, if not found, return no carry.
+ call FindDuplicateCards
+ jp c, .no_carry
+
+; store the duplicate found in wce1a and
+; remove it from the hand list.
+ ld [wce1a], a
+ ld hl, wDuelTempList
+ call FindAndRemoveCardFromList
+; find duplicates again, if not found, return no carry.
+ call FindDuplicateCards
+ jp c, .no_carry
+
+; store the duplicate found in wce1b.
+; output the card to be recovered from the Discard Pile.
+ ld [wce1b], a
+ ld a, [wce06]
+ scf
+ ret
+
+.no_carry
+ or a
+ ret
+
+AIPlay_Imakuni: ; 21813 (8:5813)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+; only sets carry if Active card is not confused.
+AIDecide_Imakuni: ; 2181e (8:581e)
+ ld a, DUELVARS_ARENA_CARD_STATUS
+ call GetTurnDuelistVariable
+ and CNF_SLP_PRZ
+ cp CONFUSED
+ jr z, .confused
+ scf
+ ret
+.confused
+ or a
+ ret
+
+AIPlay_Gambler: ; 2182d (8:582d)
+ ld a, [wCurrentAIFlags]
+ or AI_FLAG_MODIFIED_HAND
+ ld [wCurrentAIFlags], a
+ ld a, [wOpponentDeckID]
+ cp IMAKUNI_DECK_ID
+ jr z, .asm_2186a
+ ld hl, wRNG1
+ ld a, [hli]
+ ld [wce06], a
+ ld a, [hli]
+ ld [wce08], a
+ ld a, [hl]
+ ld [wce0f], a
+ ld a, $50
+ ld [hld], a
+ ld [hld], a
+ ld [hl], a
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ld hl, wRNG1
+ ld a, [wce06]
+ ld [hli], a
+ ld a, [wce08]
+ ld [hli], a
+ ld a, [wce0f]
+ ld [hl], a
+ ret
+.asm_2186a
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+; checks whether to play Gambler.
+; aside from Imakuni?, all other opponents only
+; play this card if Player is running Mewtwo1-only deck.
+AIDecide_Gambler: ; 21875 (8:5875)
+; Imakuni? has his own routine
+ ld a, [wOpponentDeckID]
+ cp IMAKUNI_DECK_ID
+ jr z, .imakuni
+
+; check if flag is set for Player using Mewtwo1 only deck
+ ld a, [wAIBarrierFlagCounter]
+ and AI_MEWTWO_MILL
+ jr z, .no_carry
+
+; set carry if number of cards in deck <= 4.
+; this is done to counteract the deck out strategy
+; of Mewtwo1 deck, by replenishing the deck with hand cards.
+ ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
+ call GetTurnDuelistVariable
+ cp DECK_SIZE - 4
+ jr nc, .set_carry
+.no_carry
+ or a
+ ret
+
+.imakuni
+; has a 2 in 10 chance of returning carry
+ ld a, 10
+ call Random
+ cp 2
+ jr nc, .no_carry
+.set_carry
+ scf
+ ret
+
+AIPlay_Revive: ; 21899 (8:5899)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+; checks certain cards in Discard Pile to use Revive on.
+; suitable for Muscle For Brains deck only.
+AIDecide_Revive: ; 218a9 (8:58a9)
+; skip if no cards in Discard Pile
+ call CreateDiscardPileCardList
+ jr c, .no_carry
+
+; skip if number of Pokemon cards in Play Area >= 4
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp 4
+ jr nc, .no_carry
+
+; look in Discard Pile for specific cards.
+ ld hl, wDuelTempList
+.loop_discard_pile
+ ld a, [hli]
+ cp $ff
+ jr z, .no_carry
+ ld b, a
+ call LoadCardDataToBuffer1_FromDeckIndex
+
+; these checks have a bug.
+; it works fine for Hitmonchan and Hitmonlee,
+; but in case it's a Tauros card, the routine will fallthrough
+; into the Kangaskhan check. since it will never be equal to Kangaskhan,
+; it will fallthrough into the set carry branch.
+; in case it's a Kangaskhan card, the check will fail in the Tauros check
+; and jump back into the loop. so just by accident the Tauros check works,
+; but Kangaskhan will never be correctly checked because of this.
+ cp HITMONCHAN
+ jr z, .set_carry
+ cp HITMONLEE
+ jr z, .set_carry
+ cp TAUROS
+ jr nz, .loop_discard_pile ; bug, these two lines should be swapped
+ cp KANGASKHAN
+ jr z, .set_carry ; bug, these two lines should be swapped
+
+.set_carry
+ ld a, b
+ scf
+ ret
+.no_carry
+ or a
+ ret
+
+AIPlay_PokemonFlute: ; 218d8 (8:58d8)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+AIDecide_PokemonFlute: ; 218e8 (8:58e8)
+; if player has no Discard Pile, skip.
+ call SwapTurn
+ call CreateDiscardPileCardList
+ call SwapTurn
+ jr c, .no_carry
+
+; if player's Play Area is already full, skip.
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetNonTurnDuelistVariable
+ cp MAX_PLAY_AREA_POKEMON
+ jr nc, .no_carry
+
+ ld a, [wOpponentDeckID]
+ cp IMAKUNI_DECK_ID
+ jr z, .imakuni
+
+ ld a, $ff
+ ld [wce06], a
+ ld [wce08], a
+
+; find Basic stage Pokemon with lowest HP in Discard Pile
+ ld hl, wDuelTempList
+.loop_1
+ ld a, [hli]
+ cp $ff
+ jr z, .done
+
+ ld b, a
+ call SwapTurn
+ call LoadCardDataToBuffer1_FromDeckIndex
+ call SwapTurn
+; skip this card if it's not Pokemon card
+ ld a, [wLoadedCard1Type]
+ cp TYPE_ENERGY
+ jr nc, .loop_1
+; skip this card if it's not Basic Stage
+ ld a, [wLoadedCard1Stage]
+ or a ; BASIC
+ jr nz, .loop_1
+
+; compare this HP with one stored
+ ld a, [wLoadedCard1HP]
+ push hl
+ ld hl, wce06
+ cp [hl]
+ pop hl
+ jr nc, .loop_1
+; if lower, store this one
+ ld [wce06], a
+ ld a, b
+ ld [wce08], a
+ jr .loop_1
+
+.done
+; if lowest HP found >= 50, return no carry
+ ld a, [wce06]
+ cp 50
+ jr nc, .no_carry
+; otherwise output its deck index in a and set carry.
+ ld a, [wce08]
+ scf
+ ret
+.no_carry
+ or a
+ ret
+
+.imakuni
+; has 2 in 10 chance of not skipping
+ ld a, 10
+ call Random
+ cp 2
+ jr nc, .no_carry
+
+; look for any Basic Pokemon card
+ ld hl, wDuelTempList
+.loop_2
+ ld a, [hli]
+ cp $ff
+ jr z, .no_carry
+ ld b, a
+ call SwapTurn
+ call LoadCardDataToBuffer1_FromDeckIndex
+ call SwapTurn
+ ld a, [wLoadedCard1Type]
+ cp TYPE_ENERGY
+ jr nc, .loop_2
+ ld a, [wLoadedCard1Stage]
+ or a ; BASIC
+ jr nz, .loop_2
+
+; a Basic stage Pokemon was found, return carry
+ ld a, b
+ scf
+ ret
+
+AIPlay_ClefairyDollOrMysteriousFossil: ; 21977 (8:5977)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+; AI logic for playing Clefairy Doll
+AIDecide_ClefairyDollOrMysteriousFossil: ; 21982 (8:5982)
+; if has max number of Play Area Pokemon, skip
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp MAX_PLAY_AREA_POKEMON
+ jr nc, .no_carry
+
+; store number of Play Area Pokemon cards
+ ld [wce06], a
+
+; if the Arena card is Wigglytuff, return carry
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ cp WIGGLYTUFF
+ jr z, .set_carry
+
+; if number of Play Area Pokemon >= 4, return no carry
+ ld a, [wce06]
+ cp 4
+ jr nc, .no_carry
+
+.set_carry
+ scf
+ ret
+.no_carry
+ or a
+ ret
+
+AIPlay_Pokeball: ; 219a6 (8:59a6)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ldtx de, TrainerCardSuccessCheckText
+ bank1call TossCoin
+ ldh [hTemp_ffa0], a
+ jr nc, .asm_219bc
+ ld a, [wAITrainerCardParameter]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ jr .asm_219c0
+.asm_219bc
+ ld a, $ff
+ ldh [hTempPlayAreaLocation_ffa1], a
+.asm_219c0
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+AIDecide_Pokeball: ; 219c6 (8:59c6)
+; go to the routines associated with deck ID
+ ld a, [wOpponentDeckID]
+ cp FIRE_CHARGE_DECK_ID
+ jr z, .fire_charge
+ cp HARD_POKEMON_DECK_ID
+ jr z, .hard_pokemon
+ cp PIKACHU_DECK_ID
+ jr z, .pikachu
+ cp ETCETERA_DECK_ID
+ jr z, .etcetera
+ cp LOVELY_NIDORAN_DECK_ID
+ jp z, .lovely_nidoran
+ or a
+ ret
+
+; this deck runs a deck check for specific
+; card IDs in order of decreasing priority
+.fire_charge
+ ld e, CHANSEY
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ld e, TAUROS
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ld e, JIGGLYPUFF1
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ret
+
+; this deck runs a deck check for specific
+; card IDs in order of decreasing priority
+.hard_pokemon
+ ld e, RHYHORN
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ld e, RHYDON
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ld e, ONIX
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ret
+
+; this deck runs a deck check for specific
+; card IDs in order of decreasing priority
+.pikachu
+ ld e, PIKACHU2
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ld e, PIKACHU3
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ld e, PIKACHU4
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ld e, PIKACHU1
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ld e, FLYING_PIKACHU
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ret
+
+; this deck runs a deck check for specific
+; card IDs in order of decreasing priority
+; given a specific energy card in hand.
+; also it avoids redundancy, so if it already
+; has that card ID in the hand, it is skipped.
+.etcetera
+; fire
+ ld a, FIRE_ENERGY
+ call LookForCardIDInHandList_Bank8
+ jr nc, .lightning
+ ld a, CHARMANDER
+ call LookForCardIDInHandList_Bank8
+ jr c, .lightning
+ ld a, MAGMAR2
+ call LookForCardIDInHandList_Bank8
+ jr c, .lightning
+ ld e, CHARMANDER
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ld e, MAGMAR2
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+
+.lightning
+ ld a, LIGHTNING_ENERGY
+ call LookForCardIDInHandList_Bank8
+ jr nc, .fighting
+ ld a, PIKACHU1
+ call LookForCardIDInHandList_Bank8
+ jr c, .fighting
+ ld a, MAGNEMITE1
+ call LookForCardIDInHandList_Bank8
+ jr c, .fighting
+ ld e, PIKACHU1
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ld e, MAGNEMITE1
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+
+.fighting
+ ld a, FIGHTING_ENERGY
+ call LookForCardIDInHandList_Bank8
+ jr nc, .psychic
+ ld a, DIGLETT
+ call LookForCardIDInHandList_Bank8
+ jr c, .psychic
+ ld a, MACHOP
+ call LookForCardIDInHandList_Bank8
+ jr c, .psychic
+ ld e, DIGLETT
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ld e, MACHOP
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+
+.psychic
+ ld a, PSYCHIC_ENERGY
+ call LookForCardIDInHandList_Bank8
+ jr nc, .done_etcetera
+ ld a, GASTLY1
+ call LookForCardIDInHandList_Bank8
+ jr c, .done_etcetera
+ ld a, JYNX
+ call LookForCardIDInHandList_Bank8
+ jr c, .done_etcetera
+ ld e, GASTLY1
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ld e, JYNX
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+.done_etcetera
+ or a
+ ret
+
+; this deck looks for card evolutions if
+; its pre-evolution is in hand or in Play Area.
+; if none of these are found, it looks for pre-evolutions
+; of cards it has in hand.
+; it does this for both the NidoranM (first)
+; and NidoranF (second) families.
+.lovely_nidoran
+ ld b, NIDORANM
+ ld a, NIDORINO
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ ret c
+ ld b, NIDORINO
+ ld a, NIDOKING
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ ret c
+ ld a, NIDORANM
+ ld b, NIDORINO
+ call LookForCardIDInDeck_GivenCardIDInHand
+ ret c
+ ld a, NIDORINO
+ ld b, NIDOKING
+ call LookForCardIDInDeck_GivenCardIDInHand
+ ret c
+ ld b, NIDORANF
+ ld a, NIDORINA
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ ret c
+ ld b, NIDORINA
+ ld a, NIDOQUEEN
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ ret c
+ ld a, NIDORANF
+ ld b, NIDORINA
+ call LookForCardIDInDeck_GivenCardIDInHand
+ ret c
+ ld a, NIDORINA
+ ld b, NIDOQUEEN
+ call LookForCardIDInDeck_GivenCardIDInHand
+ ret c
+ ret
+
+AIPlay_ComputerSearch: ; 21b12 (8:5b12)
+ ld a, [wCurrentAIFlags]
+ or AI_FLAG_MODIFIED_HAND
+ ld [wCurrentAIFlags], a
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTempRetreatCostCards], a
+ ld a, [wce1a]
+ ldh [hTemp_ffa0], a
+ ld a, [wce1b]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+; checks what Deck ID AI is playing and handle
+; them in their own routine.
+AIDecide_ComputerSearch: ; 21b34 (8:5b34)
+; skip if number of cards in hand < 3
+ ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND
+ call GetTurnDuelistVariable
+ cp 3
+ jr c, .no_carry
+
+ ld a, [wOpponentDeckID]
+ cp ROCK_CRUSHER_DECK_ID
+ jr z, AIDecide_ComputerSearch_RockCrusher
+ cp WONDERS_OF_SCIENCE_DECK_ID
+ jp z, AIDecide_ComputerSearch_WondersOfScience
+ cp FIRE_CHARGE_DECK_ID
+ jp z, AIDecide_ComputerSearch_FireCharge
+ cp ANGER_DECK_ID
+ jp z, AIDecide_ComputerSearch_Anger
+
+.no_carry
+ or a
+ ret
+
+AIDecide_ComputerSearch_RockCrusher: ; 21b55 (8:5b55)
+; if number of cards in hand is equal to 3,
+; target Professor Oak in deck
+ ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND
+ call GetTurnDuelistVariable
+ cp 3
+ jr nz, .graveler
+
+ ld e, PROFESSOR_OAK
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jr c, .find_discard_cards_1
+ ; no Professor Oak in deck, fallthrough
+
+.no_carry
+ or a
+ ret
+
+.find_discard_cards_1
+ ld [wce06], a
+ ld a, $ff
+ ld [wce1a], a
+ ld [wce1b], a
+
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ ld de, wce1a
+.loop_hand_1
+ ld a, [hli]
+ cp $ff
+ jr z, .check_discard_cards
+
+ ld c, a
+ call LoadCardDataToBuffer1_FromDeckIndex
+
+; if any of the following cards are in the hand,
+; return no carry.
+ cp PROFESSOR_OAK
+ jr z, .no_carry
+ cp FIGHTING_ENERGY
+ jr z, .no_carry
+ cp DOUBLE_COLORLESS_ENERGY
+ jr z, .no_carry
+ cp DIGLETT
+ jr z, .no_carry
+ cp GEODUDE
+ jr z, .no_carry
+ cp ONIX
+ jr z, .no_carry
+ cp RHYHORN
+ jr z, .no_carry
+
+; if it's same as wAITrainerCardToPlay, skip this card.
+ ld a, [wAITrainerCardToPlay]
+ ld b, a
+ ld a, c
+ cp b
+ jr z, .loop_hand_1
+
+; store this card index in memory
+ ld [de], a
+ inc de
+ jr .loop_hand_1
+
+.check_discard_cards
+; check if two cards were found
+; if so, output in a the deck index
+; of Professor Oak card found in deck and set carry.
+ ld a, [wce1b]
+ cp $ff
+ jr z, .no_carry
+ ld a, [wce06]
+ scf
+ ret
+
+; more than 3 cards in hand, so look for
+; specific evolution cards.
+
+; checks if there is a Graveler card in the deck to target.
+; if so, check if there's Geodude in hand or Play Area,
+; and if there's no Graveler card in hand, proceed.
+; also removes Geodude from hand list so that it is not discarded.
+.graveler
+ ld e, GRAVELER
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jr nc, .golem
+ ld [wce06], a
+ ld a, GEODUDE
+ call LookForCardIDInHandAndPlayArea
+ jr nc, .golem
+ ld a, GRAVELER
+ call LookForCardIDInHandList_Bank8
+ jr c, .golem
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ ld e, GEODUDE
+ farcall RemoveCardIDInList
+ jr .find_discard_cards_2
+
+; checks if there is a Golem card in the deck to target.
+; if so, check if there's Graveler in Play Area,
+; and if there's no Golem card in hand, proceed.
+.golem
+ ld e, GOLEM
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jr nc, .dugtrio
+ ld [wce06], a
+ ld a, GRAVELER
+ call LookForCardIDInPlayArea_Bank8
+ jr nc, .dugtrio
+ ld a, GOLEM
+ call LookForCardIDInHandList_Bank8
+ jr c, .dugtrio
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ jr .find_discard_cards_2
+
+; checks if there is a Dugtrio card in the deck to target.
+; if so, check if there's Diglett in Play Area,
+; and if there's no Dugtrio card in hand, proceed.
+.dugtrio
+ ld e, DUGTRIO
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jp nc, .no_carry
+ ld [wce06], a
+ ld a, DIGLETT
+ call LookForCardIDInPlayArea_Bank8
+ jp nc, .no_carry
+ ld a, DUGTRIO
+ call LookForCardIDInHandList_Bank8
+ jp c, .no_carry
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ jr .find_discard_cards_2
+
+.find_discard_cards_2
+ ld a, $ff
+ ld [wce1a], a
+ ld [wce1b], a
+
+ ld bc, wce1a
+ ld d, $00 ; start considering Trainer cards only
+
+; stores wAITrainerCardToPlay in e so that
+; all routines ignore it for the discard effects.
+ ld a, [wAITrainerCardToPlay]
+ ld e, a
+
+; this loop will store in wce1a cards to discard from hand.
+; at the start it will only consider Trainer cards,
+; then if there are still needed to discard,
+; move on to Pokemon cards, and finally to Energy cards.
+.loop_hand_2
+ call RemoveFromListDifferentCardOfGivenType
+ jr c, .found
+ inc d ; move on to next type (Pokemon, then Energy)
+ ld a, $03
+ cp d
+ jp z, .no_carry ; no more types to look
+ jr .loop_hand_2
+.found
+; store this card in memory,
+; and if there's still one more card to search for,
+; jump back into the loop.
+ ld [bc], a
+ inc bc
+ ld a, [wce1b]
+ cp $ff
+ jr z, .loop_hand_2
+
+; output in a Computer Search target and set carry.
+ ld a, [wce06]
+ scf
+ ret
+
+AIDecide_ComputerSearch_WondersOfScience: ; 21c56 (8:5c56)
+; if number of cards in hand < 5, target Professor Oak in deck
+ ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND
+ call GetTurnDuelistVariable
+ cp 5
+ jr nc, .look_in_hand
+
+; target Professor Oak for Computer Search
+ ld e, PROFESSOR_OAK
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jp nc, .look_in_hand ; can be a jr
+ ld [wce06], a
+ jr .find_discard_cards
+
+; Professor Oak not in deck, move on to
+; look for other cards instead.
+; if Grimer or Muk are not in hand,
+; check whether to use Computer Search on them.
+.look_in_hand
+ ld a, GRIMER
+ call LookForCardIDInHandList_Bank8
+ jr nc, .target_grimer
+ ld a, MUK
+ call LookForCardIDInHandList_Bank8
+ jr nc, .target_muk
+
+.no_carry
+ or a
+ ret
+
+; first check Grimer
+; if in deck, check cards to discard.
+.target_grimer
+ ld e, GRIMER
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jp nc, .no_carry ; can be a jr
+ ld [wce06], a
+ jr .find_discard_cards
+
+; first check Muk
+; if in deck, check cards to discard.
+.target_muk
+ ld e, MUK
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jp nc, .no_carry ; can be a jr
+ ld [wce06], a
+
+; only discard Trainer cards from hand.
+; if there are less than 2 Trainer cards to discard,
+; then return with no carry.
+; else, store the cards to discard and the
+; target card deck index, and return carry.
+.find_discard_cards
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ ld d, $00 ; first consider Trainer cards
+
+; ignore wAITrainerCardToPlay for the discard effects.
+ ld a, [wAITrainerCardToPlay]
+ ld e, a
+ call RemoveFromListDifferentCardOfGivenType
+ jr nc, .no_carry
+ ld [wce1a], a
+ call RemoveFromListDifferentCardOfGivenType
+ jr nc, .no_carry
+ ld [wce1b], a
+ ld a, [wce06]
+ scf
+ ret
+
+AIDecide_ComputerSearch_FireCharge: ; 21cbb (8:5cbb)
+; pick target card in deck from highest to lowest priority.
+; if not found in hand, go to corresponding branch.
+ ld a, CHANSEY
+ call LookForCardIDInHandList_Bank8
+ jr nc, .chansey
+ ld a, TAUROS
+ call LookForCardIDInHandList_Bank8
+ jr nc, .tauros
+ ld a, JIGGLYPUFF1
+ call LookForCardIDInHandList_Bank8
+ jr nc, .jigglypuff
+ ; fallthrough
+
+.no_carry
+ or a
+ ret
+
+; for each card targeted, check if it's in deck and,
+; if not, then return no carry.
+; else, look for cards to discard.
+.chansey
+ ld e, CHANSEY
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jp nc, .no_carry
+ ld [wce06], a
+ jr .find_discard_cards
+.tauros
+ ld e, TAUROS
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jp nc, .no_carry
+ ld [wce06], a
+ jr .find_discard_cards
+.jigglypuff
+ ld e, JIGGLYPUFF1
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jp nc, .no_carry
+ ld [wce06], a
+
+; only discard Trainer cards from hand.
+; if there are less than 2 Trainer cards to discard,
+; then return with no carry.
+; else, store the cards to discard and the
+; target card deck index, and return carry.
+.find_discard_cards
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ ld d, $00 ; first consider Trainer cards
+
+; ignore wAITrainerCardToPlay for the discard effects.
+ ld a, [wAITrainerCardToPlay]
+ ld e, a
+ call RemoveFromListDifferentCardOfGivenType
+ jr nc, .no_carry
+ ld [wce1a], a
+ call RemoveFromListDifferentCardOfGivenType
+ jr nc, .no_carry
+ ld [wce1b], a
+ ld a, [wce06]
+ scf
+ ret
+
+AIDecide_ComputerSearch_Anger: ; 21d1e (8:5d1e)
+; for each of the following cards,
+; first run a check if there's a pre-evolution in
+; Play Area or in the hand. If there is, choose it as target.
+; otherwise, check if the evolution card is in
+; hand and if so, choose it as target instead.
+ ld b, RATTATA
+ ld a, RATICATE
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_discard_cards
+ ld a, RATTATA
+ ld b, RATICATE
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_discard_cards
+ ld b, GROWLITHE
+ ld a, ARCANINE1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_discard_cards
+ ld a, GROWLITHE
+ ld b, ARCANINE1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_discard_cards
+ ld b, DODUO
+ ld a, DODRIO
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_discard_cards
+ ld a, DODUO
+ ld b, DODRIO
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_discard_cards
+ ; fallthrough
+
+.no_carry
+ or a
+ ret
+
+; only discard Trainer cards from hand.
+; if there are less than 2 Trainer cards to discard,
+; then return with no carry.
+; else, store the cards to discard and the
+; target card deck index, and return carry.
+.find_discard_cards
+ ld [wce06], a
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ ld d, $00 ; first consider Trainer cards
+
+; ignore wAITrainerCardToPlay for the discard effects.
+ ld a, [wAITrainerCardToPlay]
+ ld e, a
+ call RemoveFromListDifferentCardOfGivenType
+ jr nc, .no_carry
+ ld [wce1a], a
+ call RemoveFromListDifferentCardOfGivenType
+ jr nc, .no_carry
+ ld [wce1b], a
+ ld a, [wce06]
+ scf
+ ret
+
+AIPlay_PokemonTrader: ; 21d7a (8:5d7a)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld a, [wce1a]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+
+AIDecide_PokemonTrader: ; 21d8f (8:5d8f)
+; each deck has their own routine for picking
+; what Pokemon to look for.
+ ld a, [wOpponentDeckID]
+ cp LEGENDARY_MOLTRES_DECK_ID
+ jr z, AIDecide_PokemonTrader_LegendaryMoltres
+ cp LEGENDARY_ARTICUNO_DECK_ID
+ jr z, AIDecide_PokemonTrader_LegendaryArticuno
+ cp LEGENDARY_DRAGONITE_DECK_ID
+ jp z, AIDecide_PokemonTrader_LegendaryDragonite
+ cp LEGENDARY_RONALD_DECK_ID
+ jp z, AIDecide_PokemonTrader_LegendaryRonald
+ cp BLISTERING_POKEMON_DECK_ID
+ jp z, AIDecide_PokemonTrader_BlisteringPokemon
+ cp SOUND_OF_THE_WAVES_DECK_ID
+ jp z, AIDecide_PokemonTrader_SoundOfTheWaves
+ cp POWER_GENERATOR_DECK_ID
+ jp z, AIDecide_PokemonTrader_PowerGenerator
+ cp FLOWER_GARDEN_DECK_ID
+ jp z, AIDecide_PokemonTrader_FlowerGarden
+ cp STRANGE_POWER_DECK_ID
+ jp z, AIDecide_PokemonTrader_StrangePower
+ cp FLAMETHROWER_DECK_ID
+ jp z, AIDecide_PokemonTrader_Flamethrower
+ or a
+ ret
+
+AIDecide_PokemonTrader_LegendaryMoltres: ; 21dc4 (8:5dc4)
+; look for Moltres2 card in deck to trade with a
+; card in hand different from Moltres1.
+ ld a, MOLTRES2
+ ld e, MOLTRES1
+ call LookForCardIDToTradeWithDifferentHandCard
+ jr nc, .no_carry
+; success
+ ld [wce1a], a
+ ld a, e
+ scf
+ ret
+.no_carry
+ or a
+ ret
+
+AIDecide_PokemonTrader_LegendaryArticuno: ; 21dd5 (8:5dd5)
+; if has none of these cards in Hand or Play Area, proceed
+ ld a, ARTICUNO1
+ call LookForCardIDInHandAndPlayArea
+ jr c, .no_carry
+ ld a, LAPRAS
+ call LookForCardIDInHandAndPlayArea
+ jr c, .no_carry
+
+; if doesn't have Seel in Hand or Play Area,
+; look for it in the deck.
+; otherwise, look for Dewgong instead.
+ ld a, SEEL
+ call LookForCardIDInHandAndPlayArea
+ jr c, .dewgong
+
+ ld e, SEEL
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jr nc, .dewgong
+ ld [wce1a], a
+ jr .check_hand
+
+.dewgong
+ ld a, DEWGONG
+ call LookForCardIDInHandAndPlayArea
+ jr c, .no_carry
+ ld e, DEWGONG
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jr nc, .no_carry
+ ld [wce1a], a
+
+; a Seel or Dewgong was found in deck,
+; check hand for card to trade for
+.check_hand
+ ld a, CHANSEY
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ld a, DITTO
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ld a, ARTICUNO2
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ; doesn't have any of the cards in hand
+
+.no_carry
+ or a
+ ret
+
+.set_carry
+ scf
+ ret
+
+AIDecide_PokemonTrader_LegendaryDragonite: ; 21e24 (8:5e24)
+; if has less than 5 cards of energy
+; and of Pokemon in hand/Play Area,
+; target a Kangaskhan in deck.
+ farcall CountOppEnergyCardsInHandAndAttached
+ cp 5
+ jr c, .kangaskhan
+ call CountPokemonCardsInHandAndInPlayArea
+ cp 5
+ jr c, .kangaskhan
+ ; total number of energy cards >= 5
+ ; total number of Pokemon cards >= 5
+
+; for each of the following cards,
+; first run a check if there's a pre-evolution in
+; Play Area or in the hand. If there is, choose it as target.
+; otherwise, check if the evolution card is in
+; hand and if so, choose it as target instead.
+ ld b, MAGIKARP
+ ld a, GYARADOS
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld a, MAGIKARP
+ ld b, GYARADOS
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ ld b, DRATINI
+ ld a, DRAGONAIR
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld b, DRAGONAIR
+ ld a, DRAGONITE1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld a, DRATINI
+ ld b, DRAGONAIR
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ ld a, DRAGONAIR
+ ld b, DRAGONITE1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ ld b, CHARMANDER
+ ld a, CHARMELEON
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld b, CHARMELEON
+ ld a, CHARIZARD
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld a, CHARMANDER
+ ld b, CHARMELEON
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ ld a, CHARMELEON
+ ld b, CHARIZARD
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ jr .no_carry
+
+.kangaskhan
+ ld e, KANGASKHAN
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jr nc, .no_carry
+
+; card was found as target in deck,
+; look for card in hand to trade with
+.choose_hand
+ ld [wce1a], a
+ ld a, DRAGONAIR
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ld a, CHARMELEON
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ld a, GYARADOS
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ld a, MAGIKARP
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ld a, CHARMANDER
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ld a, DRATINI
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ; non found
+
+.no_carry
+ or a
+ ret
+.set_carry
+ scf
+ ret
+
+AIDecide_PokemonTrader_LegendaryRonald: ; 21ec9 (8:5ec9)
+; for each of the following cards,
+; first run a check if there's a pre-evolution in
+; Play Area or in the hand. If there is, choose it as target.
+; otherwise, check if the evolution card is in
+; hand and if so, choose it as target instead.
+ ld b, EEVEE
+ ld a, FLAREON1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld b, EEVEE
+ ld a, VAPOREON1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld b, EEVEE
+ ld a, JOLTEON1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld a, EEVEE
+ ld b, FLAREON1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ ld a, EEVEE
+ ld b, VAPOREON1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ ld a, EEVEE
+ ld b, JOLTEON1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ ld b, DRATINI
+ ld a, DRAGONAIR
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld b, DRAGONAIR
+ ld a, DRAGONITE1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld a, DRATINI
+ ld b, DRAGONAIR
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ ld a, DRAGONAIR
+ ld b, DRAGONITE1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ jr .no_carry
+
+; card was found as target in deck,
+; look for card in hand to trade with
+.choose_hand
+ ld [wce1a], a
+ ld a, ZAPDOS3
+ call LookForCardIDInHandList_Bank8
+ jr c, .set_carry
+ ld a, ARTICUNO2
+ call LookForCardIDInHandList_Bank8
+ jr c, .set_carry
+ ld a, MOLTRES2
+ call LookForCardIDInHandList_Bank8
+ jr c, .set_carry
+ ; none found
+
+.no_carry
+ or a
+ ret
+.set_carry
+ scf
+ ret
+
+AIDecide_PokemonTrader_BlisteringPokemon: ; 21f41 (8:5f41)
+; for each of the following cards,
+; first run a check if there's a pre-evolution in
+; Play Area or in the hand. If there is, choose it as target.
+; otherwise, check if the evolution card is in
+; hand and if so, choose it as target instead.
+ ld b, RHYHORN
+ ld a, RHYDON
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, RHYHORN
+ ld b, RHYDON
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld b, CUBONE
+ ld a, MAROWAK1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, CUBONE
+ ld b, MAROWAK1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld b, PONYTA
+ ld a, RAPIDASH
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, PONYTA
+ ld b, RAPIDASH
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ jr .no_carry
+
+; a card in deck was found to look for,
+; check if there are duplicates in hand to trade with.
+.find_duplicates
+ ld [wce1a], a
+ call FindDuplicatePokemonCards
+ jr c, .set_carry
+.no_carry
+ or a
+ ret
+.set_carry
+ scf
+ ret
+
+AIDecide_PokemonTrader_SoundOfTheWaves: ; 21f85 (8:5f85)
+; for each of the following cards,
+; first run a check if there's a pre-evolution in
+; Play Area or in the hand. If there is, choose it as target.
+; otherwise, check if the evolution card is in
+; hand and if so, choose it as target instead.
+ ld b, SEEL
+ ld a, DEWGONG
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld a, SEEL
+ ld b, DEWGONG
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ ld b, KRABBY
+ ld a, KINGLER
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld a, KRABBY
+ ld b, KINGLER
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ ld b, SHELLDER
+ ld a, CLOYSTER
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld a, SHELLDER
+ ld b, CLOYSTER
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ ld b, HORSEA
+ ld a, SEADRA
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld a, HORSEA
+ ld b, SEADRA
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ ld b, TENTACOOL
+ ld a, TENTACRUEL
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld a, TENTACOOL
+ ld b, TENTACRUEL
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ jr .no_carry
+
+; card was found as target in deck,
+; look for card in hand to trade with
+.choose_hand
+ ld [wce1a], a
+ ld a, SEEL
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ld a, KRABBY
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ld a, HORSEA
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ld a, SHELLDER
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ld a, TENTACOOL
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ; none found
+
+.no_carry
+ or a
+ ret
+.set_carry
+ scf
+ ret
+
+AIDecide_PokemonTrader_PowerGenerator: ; 2200b (8:600b)
+; for each of the following cards,
+; first run a check if there's a pre-evolution in
+; Play Area or in the hand. If there is, choose it as target.
+; otherwise, check if the evolution card is in
+; hand and if so, choose it as target instead.
+ ld b, PIKACHU2
+ ld a, RAICHU1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jp c, .find_duplicates
+ ld b, PIKACHU1
+ ld a, RAICHU1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, PIKACHU2
+ ld b, RAICHU1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld a, PIKACHU1
+ ld b, RAICHU1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld b, VOLTORB
+ ld a, ELECTRODE2
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld b, VOLTORB
+ ld a, ELECTRODE1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, VOLTORB
+ ld b, ELECTRODE2
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld a, VOLTORB
+ ld b, ELECTRODE1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld b, MAGNEMITE1
+ ld a, MAGNETON2
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld b, MAGNEMITE2
+ ld a, MAGNETON2
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld b, MAGNEMITE1
+ ld a, MAGNETON1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld b, MAGNEMITE2
+ ld a, MAGNETON1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, MAGNEMITE2
+ ld b, MAGNETON2
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld a, MAGNEMITE1
+ ld b, MAGNETON2
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld a, MAGNEMITE2
+ ld b, MAGNETON1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld a, MAGNEMITE1
+ ld b, MAGNETON1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ; bug, missing jr .no_carry
+
+; since this last check falls through regardless of result,
+; register a might hold an invalid deck index,
+; which might lead to hilarious results like Brandon
+; trading a Pikachu with a Grass Energy from the deck.
+; however, since it's deep in a tower of conditionals,
+; reaching here is extremely unlikely.
+
+; a card in deck was found to look for,
+; check if there are duplicates in hand to trade with.
+.find_duplicates
+ ld [wce1a], a
+ call FindDuplicatePokemonCards
+ jr c, .set_carry
+ or a
+ ret
+.set_carry
+ scf
+ ret
+
+AIDecide_PokemonTrader_FlowerGarden: ; 220a8 (8:60a8)
+; for each of the following cards,
+; first run a check if there's a pre-evolution in
+; Play Area or in the hand. If there is, choose it as target.
+; otherwise, check if the evolution card is in
+; hand and if so, choose it as target instead.
+ ld b, BULBASAUR
+ ld a, IVYSAUR
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld b, IVYSAUR
+ ld a, VENUSAUR2
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, BULBASAUR
+ ld b, IVYSAUR
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld a, IVYSAUR
+ ld b, VENUSAUR2
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld b, BELLSPROUT
+ ld a, WEEPINBELL
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld b, WEEPINBELL
+ ld a, VICTREEBEL
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, BELLSPROUT
+ ld b, WEEPINBELL
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld a, WEEPINBELL
+ ld b, VICTREEBEL
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld b, ODDISH
+ ld a, GLOOM
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld b, GLOOM
+ ld a, VILEPLUME
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, ODDISH
+ ld b, GLOOM
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld a, GLOOM
+ ld b, VILEPLUME
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ jr .no_carry
+
+; a card in deck was found to look for,
+; check if there are duplicates in hand to trade with.
+.find_duplicates
+ ld [wce1a], a
+ call FindDuplicatePokemonCards
+ jr c, .found
+.no_carry
+ or a
+ ret
+.found
+ scf
+ ret
+
+AIDecide_PokemonTrader_StrangePower: ; 22122 (8:6122)
+; looks for a Pokemon in hand to trade with Mr Mime in deck.
+; inputting Mr Mime in register e for the function is redundant
+; since it already checks whether a Mr Mime exists in the hand.
+ ld a, MR_MIME
+ ld e, MR_MIME
+ call LookForCardIDToTradeWithDifferentHandCard
+ jr nc, .no_carry
+; found
+ ld [wce1a], a
+ ld a, e
+ scf
+ ret
+.no_carry
+ or a
+ ret
+
+AIDecide_PokemonTrader_Flamethrower: ; 22133 (8:6133)
+; for each of the following cards,
+; first run a check if there's a pre-evolution in
+; Play Area or in the hand. If there is, choose it as target.
+; otherwise, check if the evolution card is in
+; hand and if so, choose it as target instead.
+ ld b, CHARMANDER
+ ld a, CHARMELEON
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld b, CHARMELEON
+ ld a, CHARIZARD
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, CHARMANDER
+ ld b, CHARMELEON
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld a, CHARMELEON
+ ld b, CHARIZARD
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld b, VULPIX
+ ld a, NINETAILS1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, VULPIX
+ ld b, NINETAILS1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld b, GROWLITHE
+ ld a, ARCANINE2
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, GROWLITHE
+ ld b, ARCANINE2
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld b, EEVEE
+ ld a, FLAREON2
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, EEVEE
+ ld b, FLAREON2
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ jr .no_carry
+
+; a card in deck was found to look for,
+; check if there are duplicates in hand to trade with.
+.find_duplicates
+ ld [wce1a], a
+ call FindDuplicatePokemonCards
+ jr c, .set_carry
+.no_carry
+ or a
+ ret
+.set_carry
+ scf
+ ret