summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/data/ai_trainer_card_logic.asm47
-rw-r--r--src/engine/ai/common.asm970
-rw-r--r--src/engine/ai/deck_ai.asm (renamed from src/engine/deck_ai/deck_ai.asm)38
-rw-r--r--src/engine/ai/decks/fire_charge.asm (renamed from src/engine/deck_ai/decks/fire_charge.asm)0
-rw-r--r--src/engine/ai/decks/first_strike.asm (renamed from src/engine/deck_ai/decks/first_strike.asm)0
-rw-r--r--src/engine/ai/decks/flower_power.asm (renamed from src/engine/deck_ai/decks/flower_power.asm)0
-rw-r--r--src/engine/ai/decks/general.asm (renamed from src/engine/deck_ai/decks/general.asm)0
-rw-r--r--src/engine/ai/decks/general_no_retreat.asm (renamed from src/engine/deck_ai/decks/general_no_retreat.asm)0
-rw-r--r--src/engine/ai/decks/go_go_rain_dance.asm (renamed from src/engine/deck_ai/decks/go_go_rain_dance.asm)0
-rw-r--r--src/engine/ai/decks/im_ronald.asm (renamed from src/engine/deck_ai/decks/im_ronald.asm)0
-rw-r--r--src/engine/ai/decks/invincible_ronald.asm (renamed from src/engine/deck_ai/decks/invincible_ronald.asm)0
-rw-r--r--src/engine/ai/decks/legendary_articuno.asm (renamed from src/engine/deck_ai/decks/legendary_articuno.asm)0
-rw-r--r--src/engine/ai/decks/legendary_dragonite.asm (renamed from src/engine/deck_ai/decks/legendary_dragonite.asm)0
-rw-r--r--src/engine/ai/decks/legendary_moltres.asm (renamed from src/engine/deck_ai/decks/legendary_moltres.asm)0
-rw-r--r--src/engine/ai/decks/legendary_ronald.asm (renamed from src/engine/deck_ai/decks/legendary_ronald.asm)0
-rw-r--r--src/engine/ai/decks/legendary_zapdos.asm (renamed from src/engine/deck_ai/decks/legendary_zapdos.asm)0
-rw-r--r--src/engine/ai/decks/powerful_ronald.asm (renamed from src/engine/deck_ai/decks/powerful_ronald.asm)0
-rw-r--r--src/engine/ai/decks/rock_crusher.asm (renamed from src/engine/deck_ai/decks/rock_crusher.asm)0
-rw-r--r--src/engine/ai/decks/sams_practice.asm (renamed from src/engine/deck_ai/decks/sams_practice.asm)0
-rw-r--r--src/engine/ai/decks/strange_psyshock.asm (renamed from src/engine/deck_ai/decks/strange_psyshock.asm)0
-rw-r--r--src/engine/ai/decks/wonders_of_science.asm (renamed from src/engine/deck_ai/decks/wonders_of_science.asm)0
-rw-r--r--src/engine/ai/decks/zapping_selfdestruct.asm (renamed from src/engine/deck_ai/decks/zapping_selfdestruct.asm)0
-rw-r--r--src/engine/ai/pkmn_powers.asm1228
-rw-r--r--src/engine/ai/trainer_cards.asm (renamed from src/engine/bank08.asm)2260
-rw-r--r--src/engine/bank05.asm2
-rw-r--r--src/engine/home.asm2
-rw-r--r--src/layout.link2
-rw-r--r--src/main.asm6
28 files changed, 2278 insertions, 2277 deletions
diff --git a/src/data/ai_trainer_card_logic.asm b/src/data/ai_trainer_card_logic.asm
new file mode 100644
index 0000000..57bf90f
--- /dev/null
+++ b/src/data/ai_trainer_card_logic.asm
@@ -0,0 +1,47 @@
+ai_trainer_card_logic: MACRO
+ db \1 ; AI_TRAINER_CARD_PHASE_* constant
+ db \2 ; ID of trainer card
+ dw \3 ; function for AI decision to play card
+ dw \4 ; function for AI playing the card
+ENDM
+
+AITrainerCardLogic: ; 20000 (8:4000)
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_07, POTION, AIDecide_Potion1, AIPlay_Potion
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_10, POTION, AIDecide_Potion2, AIPlay_Potion
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_08, SUPER_POTION, AIDecide_SuperPotion1, AIPlay_SuperPotion
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_11, SUPER_POTION, AIDecide_SuperPotion2, AIPlay_SuperPotion
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_13, DEFENDER, AIDecide_Defender1, AIPlay_Defender
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_14, DEFENDER, AIDecide_Defender2, AIPlay_Defender
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_13, PLUSPOWER, AIDecide_Pluspower1, AIPlay_Pluspower
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_14, PLUSPOWER, AIDecide_Pluspower2, AIPlay_Pluspower
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_09, SWITCH, AIDecide_Switch, AIPlay_Switch
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_07, GUST_OF_WIND, AIDecide_GustOfWind, AIPlay_GustOfWind
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_10, GUST_OF_WIND, AIDecide_GustOfWind, AIPlay_GustOfWind
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_04, BILL, AIDecide_Bill, AIPlay_Bill
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_05, ENERGY_REMOVAL, AIDecide_EnergyRemoval, AIPlay_EnergyRemoval
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_05, SUPER_ENERGY_REMOVAL, AIDecide_SuperEnergyRemoval, AIPlay_SuperEnergyRemoval
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_07, POKEMON_BREEDER, AIDecide_PokemonBreeder, AIPlay_PokemonBreeder
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_15, PROFESSOR_OAK, AIDecide_ProfessorOak, AIPlay_ProfessorOak
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_10, ENERGY_RETRIEVAL, AIDecide_EnergyRetrieval, AIPlay_EnergyRetrieval
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_11, SUPER_ENERGY_RETRIEVAL, AIDecide_SuperEnergyRetrieval, AIPlay_SuperEnergyRetrieval
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_06, POKEMON_CENTER, AIDecide_PokemonCenter, AIPlay_PokemonCenter
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_07, IMPOSTER_PROFESSOR_OAK, AIDecide_ImposterProfessorOak, AIPlay_ImposterProfessorOak
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_12, ENERGY_SEARCH, AIDecide_EnergySearch, AIPlay_EnergySearch
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_03, POKEDEX, AIDecide_Pokedex, AIPlay_Pokedex
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_07, FULL_HEAL, AIDecide_FullHeal, AIPlay_FullHeal
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_10, MR_FUJI, AIDecide_MrFuji, AIPlay_MrFuji
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_10, SCOOP_UP, AIDecide_ScoopUp, AIPlay_ScoopUp
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_02, MAINTENANCE, AIDecide_Maintenance, AIPlay_Maintenance
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_03, RECYCLE, AIDecide_Recycle, AIPlay_Recycle
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_13, LASS, AIDecide_Lass, AIPlay_Lass
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_04, ITEM_FINDER, AIDecide_ItemFinder, AIPlay_ItemFinder
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_01, IMAKUNI_CARD, AIDecide_Imakuni, AIPlay_Imakuni
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_01, GAMBLER, AIDecide_Gambler, AIPlay_Gambler
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_05, REVIVE, AIDecide_Revive, AIPlay_Revive
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_13, POKEMON_FLUTE, AIDecide_PokemonFlute, AIPlay_PokemonFlute
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_05, CLEFAIRY_DOLL, AIDecide_ClefairyDollOrMysteriousFossil, AIPlay_ClefairyDollOrMysteriousFossil
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_05, MYSTERIOUS_FOSSIL, AIDecide_ClefairyDollOrMysteriousFossil, AIPlay_ClefairyDollOrMysteriousFossil
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_02, POKE_BALL, AIDecide_Pokeball, AIPlay_Pokeball
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_02, COMPUTER_SEARCH, AIDecide_ComputerSearch, AIPlay_ComputerSearch
+ ai_trainer_card_logic AI_TRAINER_CARD_PHASE_02, POKEMON_TRADER, AIDecide_PokemonTrader, AIPlay_PokemonTrader
+ db $ff
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/deck_ai/deck_ai.asm b/src/engine/ai/deck_ai.asm
index 97093c7..c330418 100644
--- a/src/engine/deck_ai/deck_ai.asm
+++ b/src/engine/ai/deck_ai.asm
@@ -61,22 +61,22 @@ ENDM
; wAICardListRetreatBonus : scores given to certain cards for retreat;
; wAICardListEnergyBonus : max number of energy cards and card scores.
-INCLUDE "engine/deck_ai/decks/general.asm"
-INCLUDE "engine/deck_ai/decks/sams_practice.asm"
-INCLUDE "engine/deck_ai/decks/general_no_retreat.asm"
-INCLUDE "engine/deck_ai/decks/legendary_moltres.asm"
-INCLUDE "engine/deck_ai/decks/legendary_zapdos.asm"
-INCLUDE "engine/deck_ai/decks/legendary_articuno.asm"
-INCLUDE "engine/deck_ai/decks/legendary_dragonite.asm"
-INCLUDE "engine/deck_ai/decks/first_strike.asm"
-INCLUDE "engine/deck_ai/decks/rock_crusher.asm"
-INCLUDE "engine/deck_ai/decks/go_go_rain_dance.asm"
-INCLUDE "engine/deck_ai/decks/zapping_selfdestruct.asm"
-INCLUDE "engine/deck_ai/decks/flower_power.asm"
-INCLUDE "engine/deck_ai/decks/strange_psyshock.asm"
-INCLUDE "engine/deck_ai/decks/wonders_of_science.asm"
-INCLUDE "engine/deck_ai/decks/fire_charge.asm"
-INCLUDE "engine/deck_ai/decks/im_ronald.asm"
-INCLUDE "engine/deck_ai/decks/powerful_ronald.asm"
-INCLUDE "engine/deck_ai/decks/invincible_ronald.asm"
-INCLUDE "engine/deck_ai/decks/legendary_ronald.asm"
+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/deck_ai/decks/fire_charge.asm b/src/engine/ai/decks/fire_charge.asm
index f5b347b..f5b347b 100644
--- a/src/engine/deck_ai/decks/fire_charge.asm
+++ b/src/engine/ai/decks/fire_charge.asm
diff --git a/src/engine/deck_ai/decks/first_strike.asm b/src/engine/ai/decks/first_strike.asm
index 2e636e1..2e636e1 100644
--- a/src/engine/deck_ai/decks/first_strike.asm
+++ b/src/engine/ai/decks/first_strike.asm
diff --git a/src/engine/deck_ai/decks/flower_power.asm b/src/engine/ai/decks/flower_power.asm
index 4d423a3..4d423a3 100644
--- a/src/engine/deck_ai/decks/flower_power.asm
+++ b/src/engine/ai/decks/flower_power.asm
diff --git a/src/engine/deck_ai/decks/general.asm b/src/engine/ai/decks/general.asm
index 039e101..039e101 100644
--- a/src/engine/deck_ai/decks/general.asm
+++ b/src/engine/ai/decks/general.asm
diff --git a/src/engine/deck_ai/decks/general_no_retreat.asm b/src/engine/ai/decks/general_no_retreat.asm
index 20d84e3..20d84e3 100644
--- a/src/engine/deck_ai/decks/general_no_retreat.asm
+++ b/src/engine/ai/decks/general_no_retreat.asm
diff --git a/src/engine/deck_ai/decks/go_go_rain_dance.asm b/src/engine/ai/decks/go_go_rain_dance.asm
index 23547e2..23547e2 100644
--- a/src/engine/deck_ai/decks/go_go_rain_dance.asm
+++ b/src/engine/ai/decks/go_go_rain_dance.asm
diff --git a/src/engine/deck_ai/decks/im_ronald.asm b/src/engine/ai/decks/im_ronald.asm
index b002d83..b002d83 100644
--- a/src/engine/deck_ai/decks/im_ronald.asm
+++ b/src/engine/ai/decks/im_ronald.asm
diff --git a/src/engine/deck_ai/decks/invincible_ronald.asm b/src/engine/ai/decks/invincible_ronald.asm
index 463560b..463560b 100644
--- a/src/engine/deck_ai/decks/invincible_ronald.asm
+++ b/src/engine/ai/decks/invincible_ronald.asm
diff --git a/src/engine/deck_ai/decks/legendary_articuno.asm b/src/engine/ai/decks/legendary_articuno.asm
index 6409330..6409330 100644
--- a/src/engine/deck_ai/decks/legendary_articuno.asm
+++ b/src/engine/ai/decks/legendary_articuno.asm
diff --git a/src/engine/deck_ai/decks/legendary_dragonite.asm b/src/engine/ai/decks/legendary_dragonite.asm
index 597f72c..597f72c 100644
--- a/src/engine/deck_ai/decks/legendary_dragonite.asm
+++ b/src/engine/ai/decks/legendary_dragonite.asm
diff --git a/src/engine/deck_ai/decks/legendary_moltres.asm b/src/engine/ai/decks/legendary_moltres.asm
index d07afb9..d07afb9 100644
--- a/src/engine/deck_ai/decks/legendary_moltres.asm
+++ b/src/engine/ai/decks/legendary_moltres.asm
diff --git a/src/engine/deck_ai/decks/legendary_ronald.asm b/src/engine/ai/decks/legendary_ronald.asm
index 3356838..3356838 100644
--- a/src/engine/deck_ai/decks/legendary_ronald.asm
+++ b/src/engine/ai/decks/legendary_ronald.asm
diff --git a/src/engine/deck_ai/decks/legendary_zapdos.asm b/src/engine/ai/decks/legendary_zapdos.asm
index cc99f0c..cc99f0c 100644
--- a/src/engine/deck_ai/decks/legendary_zapdos.asm
+++ b/src/engine/ai/decks/legendary_zapdos.asm
diff --git a/src/engine/deck_ai/decks/powerful_ronald.asm b/src/engine/ai/decks/powerful_ronald.asm
index 096fbea..096fbea 100644
--- a/src/engine/deck_ai/decks/powerful_ronald.asm
+++ b/src/engine/ai/decks/powerful_ronald.asm
diff --git a/src/engine/deck_ai/decks/rock_crusher.asm b/src/engine/ai/decks/rock_crusher.asm
index 41a50fa..41a50fa 100644
--- a/src/engine/deck_ai/decks/rock_crusher.asm
+++ b/src/engine/ai/decks/rock_crusher.asm
diff --git a/src/engine/deck_ai/decks/sams_practice.asm b/src/engine/ai/decks/sams_practice.asm
index dddce61..dddce61 100644
--- a/src/engine/deck_ai/decks/sams_practice.asm
+++ b/src/engine/ai/decks/sams_practice.asm
diff --git a/src/engine/deck_ai/decks/strange_psyshock.asm b/src/engine/ai/decks/strange_psyshock.asm
index ef378b0..ef378b0 100644
--- a/src/engine/deck_ai/decks/strange_psyshock.asm
+++ b/src/engine/ai/decks/strange_psyshock.asm
diff --git a/src/engine/deck_ai/decks/wonders_of_science.asm b/src/engine/ai/decks/wonders_of_science.asm
index 706a7e6..706a7e6 100644
--- a/src/engine/deck_ai/decks/wonders_of_science.asm
+++ b/src/engine/ai/decks/wonders_of_science.asm
diff --git a/src/engine/deck_ai/decks/zapping_selfdestruct.asm b/src/engine/ai/decks/zapping_selfdestruct.asm
index da5e7c6..da5e7c6 100644
--- a/src/engine/deck_ai/decks/zapping_selfdestruct.asm
+++ b/src/engine/ai/decks/zapping_selfdestruct.asm
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/bank08.asm b/src/engine/ai/trainer_cards.asm
index 4b4444c..cd87a4b 100644
--- a/src/engine/bank08.asm
+++ b/src/engine/ai/trainer_cards.asm
@@ -1,50 +1,4 @@
-; unknown byte / card ID / function pointer 1 / function pointer 2
-unknown_data_20000: MACRO
- db \1, \2
- dw \3
- dw \4
-ENDM
-
-Data_20000: ; 20000 (8:4000)
- unknown_data_20000 AI_TRAINER_CARD_PHASE_07, POTION, AIDecide_Potion1, AIPlay_Potion
- unknown_data_20000 AI_TRAINER_CARD_PHASE_10, POTION, AIDecide_Potion2, AIPlay_Potion
- unknown_data_20000 AI_TRAINER_CARD_PHASE_08, SUPER_POTION, AIDecide_SuperPotion1, AIPlay_SuperPotion
- unknown_data_20000 AI_TRAINER_CARD_PHASE_11, SUPER_POTION, AIDecide_SuperPotion2, AIPlay_SuperPotion
- unknown_data_20000 AI_TRAINER_CARD_PHASE_13, DEFENDER, AIDecide_Defender1, AIPlay_Defender
- unknown_data_20000 AI_TRAINER_CARD_PHASE_14, DEFENDER, AIDecide_Defender2, AIPlay_Defender
- unknown_data_20000 AI_TRAINER_CARD_PHASE_13, PLUSPOWER, AIDecide_Pluspower1, AIPlay_Pluspower
- unknown_data_20000 AI_TRAINER_CARD_PHASE_14, PLUSPOWER, AIDecide_Pluspower2, AIPlay_Pluspower
- unknown_data_20000 AI_TRAINER_CARD_PHASE_09, SWITCH, AIDecide_Switch, AIPlay_Switch
- unknown_data_20000 AI_TRAINER_CARD_PHASE_07, GUST_OF_WIND, AIDecide_GustOfWind, AIPlay_GustOfWind
- unknown_data_20000 AI_TRAINER_CARD_PHASE_10, GUST_OF_WIND, AIDecide_GustOfWind, AIPlay_GustOfWind
- unknown_data_20000 AI_TRAINER_CARD_PHASE_04, BILL, AIDecide_Bill, AIPlay_Bill
- unknown_data_20000 AI_TRAINER_CARD_PHASE_05, ENERGY_REMOVAL, AIDecide_EnergyRemoval, AIPlay_EnergyRemoval
- unknown_data_20000 AI_TRAINER_CARD_PHASE_05, SUPER_ENERGY_REMOVAL, AIDecide_SuperEnergyRemoval, AIPlay_SuperEnergyRemoval
- unknown_data_20000 AI_TRAINER_CARD_PHASE_07, POKEMON_BREEDER, AIDecide_PokemonBreeder, AIPlay_PokemonBreeder
- unknown_data_20000 AI_TRAINER_CARD_PHASE_15, PROFESSOR_OAK, AIDecide_ProfessorOak, AIPlay_ProfessorOak
- unknown_data_20000 AI_TRAINER_CARD_PHASE_10, ENERGY_RETRIEVAL, AIDecide_EnergyRetrieval, AIPlay_EnergyRetrieval
- unknown_data_20000 AI_TRAINER_CARD_PHASE_11, SUPER_ENERGY_RETRIEVAL, AIDecide_SuperEnergyRetrieval, AIPlay_SuperEnergyRetrieval
- unknown_data_20000 AI_TRAINER_CARD_PHASE_06, POKEMON_CENTER, AIDecide_PokemonCenter, AIPlay_PokemonCenter
- unknown_data_20000 AI_TRAINER_CARD_PHASE_07, IMPOSTER_PROFESSOR_OAK, AIDecide_ImposterProfessorOak, AIPlay_ImposterProfessorOak
- unknown_data_20000 AI_TRAINER_CARD_PHASE_12, ENERGY_SEARCH, AIDecide_EnergySearch, AIPlay_EnergySearch
- unknown_data_20000 AI_TRAINER_CARD_PHASE_03, POKEDEX, AIDecide_Pokedex, AIPlay_Pokedex
- unknown_data_20000 AI_TRAINER_CARD_PHASE_07, FULL_HEAL, AIDecide_FullHeal, AIPlay_FullHeal
- unknown_data_20000 AI_TRAINER_CARD_PHASE_10, MR_FUJI, AIDecide_MrFuji, AIPlay_MrFuji
- unknown_data_20000 AI_TRAINER_CARD_PHASE_10, SCOOP_UP, AIDecide_ScoopUp, AIPlay_ScoopUp
- unknown_data_20000 AI_TRAINER_CARD_PHASE_02, MAINTENANCE, AIDecide_Maintenance, AIPlay_Maintenance
- unknown_data_20000 AI_TRAINER_CARD_PHASE_03, RECYCLE, AIDecide_Recycle, AIPlay_Recycle
- unknown_data_20000 AI_TRAINER_CARD_PHASE_13, LASS, AIDecide_Lass, AIPlay_Lass
- unknown_data_20000 AI_TRAINER_CARD_PHASE_04, ITEM_FINDER, AIDecide_ItemFinder, AIPlay_ItemFinder
- unknown_data_20000 AI_TRAINER_CARD_PHASE_01, IMAKUNI_CARD, AIDecide_Imakuni, AIPlay_Imakuni
- unknown_data_20000 AI_TRAINER_CARD_PHASE_01, GAMBLER, AIDecide_Gambler, AIPlay_Gambler
- unknown_data_20000 AI_TRAINER_CARD_PHASE_05, REVIVE, AIDecide_Revive, AIPlay_Revive
- unknown_data_20000 AI_TRAINER_CARD_PHASE_13, POKEMON_FLUTE, AIDecide_PokemonFlute, AIPlay_PokemonFlute
- unknown_data_20000 AI_TRAINER_CARD_PHASE_05, CLEFAIRY_DOLL, AIDecide_ClefairyDollOrMysteriousFossil, AIPlay_ClefairyDollOrMysteriousFossil
- unknown_data_20000 AI_TRAINER_CARD_PHASE_05, MYSTERIOUS_FOSSIL, AIDecide_ClefairyDollOrMysteriousFossil, AIPlay_ClefairyDollOrMysteriousFossil
- unknown_data_20000 AI_TRAINER_CARD_PHASE_02, POKE_BALL, AIDecide_Pokeball, AIPlay_Pokeball
- unknown_data_20000 AI_TRAINER_CARD_PHASE_02, COMPUTER_SEARCH, AIDecide_ComputerSearch, AIPlay_ComputerSearch
- unknown_data_20000 AI_TRAINER_CARD_PHASE_02, POKEMON_TRADER, AIDecide_PokemonTrader, AIPlay_PokemonTrader
- db $ff
+INCLUDE "data/ai_trainer_card_logic.asm"
_AIProcessHandTrainerCards: ; 200e5 (8:40e5)
ld [wce18], a
@@ -64,7 +18,7 @@ _AIProcessHandTrainerCards: ; 200e5 (8:40e5)
push hl
ld a, [wce18]
ld d, a
- ld hl, Data_20000
+ ld hl, AITrainerCardLogic
.loop_data
xor a
ld [wCurrentAIFlags], a
@@ -220,7 +174,7 @@ AIPlay_Potion: ; 201b5 (8:41b5)
AIDecide_Potion1: ; 201d1 (8:41d1)
farcall AIDecideWhetherToRetreat
jr c, .no_carry
- call Func_22bad
+ call AICheckIfAttackIsHighRecoil
jr c, .no_carry
xor a ; active card
ldh [hTempPlayAreaLocation_ff9d], a
@@ -341,10 +295,10 @@ AIDecide_Potion2: ; 20204 (8:4204)
scf
ret
-; return carry for active card if not Hgh Recoil.
+; return carry for active card if not High Recoil.
.active_card
push de
- call Func_22bad
+ call AICheckIfAttackIsHighRecoil
pop de
jr c, .no_carry
ld a, e
@@ -410,7 +364,7 @@ AIPlay_SuperPotion: ; 202a8 (8:42a8)
AIDecide_SuperPotion1: ; 202cc (8:42cc)
farcall AIDecideWhetherToRetreat
jr c, .no_carry
- call Func_22bad
+ call AICheckIfAttackIsHighRecoil
jr c, .no_carry
xor a
ldh [hTempPlayAreaLocation_ff9d], a
@@ -551,7 +505,7 @@ AIDecide_SuperPotion2: ; 2030f (8:430f)
; return carry for active card if not Hgh Recoil.
.active_card
push de
- call Func_22bad
+ call AICheckIfAttackIsHighRecoil
pop de
jr c, .no_carry
ld a, e
@@ -6117,2203 +6071,3 @@ AIDecide_PokemonTrader_Flamethrower: ; 22133 (8:6133)
.set_carry
scf
ret
-
-; 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
-
-; 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.
-Func_22bad: ; 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/bank05.asm b/src/engine/bank05.asm
index 75ca6b9..4845d40 100644
--- a/src/engine/bank05.asm
+++ b/src/engine/bank05.asm
@@ -1205,7 +1205,7 @@ AIProcessHandTrainerCards: ; 14663 (5:4663)
farcall _AIProcessHandTrainerCards
ret
-INCLUDE "engine/deck_ai/deck_ai.asm"
+INCLUDE "engine/ai/deck_ai.asm"
; return carry if card ID loaded in a is found in hand
; and outputs in a the deck index of that card
diff --git a/src/engine/home.asm b/src/engine/home.asm
index 87deaaa..670557f 100644
--- a/src/engine/home.asm
+++ b/src/engine/home.asm
@@ -8258,7 +8258,7 @@ AIDoAction_TakePrize: ; 2bd7 (0:2bd7)
jr AIDoAction ; this line is not needed
; calls the appropriate AI routine to handle action,
-; depending on the deck ID (see engine/deck_ai/deck_ai.asm)
+; depending on the deck ID (see engine/ai/deck_ai.asm)
; input:
; - a = AIACTION_* constant
AIDoAction: ; 2bdb (0:2bdb)
diff --git a/src/layout.link b/src/layout.link
index 413f9b8..7a0a42a 100644
--- a/src/layout.link
+++ b/src/layout.link
@@ -46,7 +46,7 @@ ROMX $07
"Credits Sequence"
"Booster Packs"
ROMX $08
- "Bank 8"
+ "AI Logic"
ROMX $0b
"Effect Functions"
ROMX $0c
diff --git a/src/main.asm b/src/main.asm
index b331dc8..e16c9ab 100644
--- a/src/main.asm
+++ b/src/main.asm
@@ -31,8 +31,10 @@ INCLUDE "data/sequences/credits_sequence.asm"
SECTION "Booster Packs", ROMX
INCLUDE "engine/booster_packs.asm"
-SECTION "Bank 8", ROMX
-INCLUDE "engine/bank08.asm"
+SECTION "AI Logic", ROMX
+INCLUDE "engine/ai/trainer_cards.asm"
+INCLUDE "engine/ai/pkmn_powers.asm"
+INCLUDE "engine/ai/common.asm"
SECTION "Effect Functions", ROMX
INCLUDE "engine/effect_functions.asm"