summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/constants.asm1
-rw-r--r--src/constants/deck_ai_constants.asm61
-rw-r--r--src/constants/duel_constants.asm28
-rw-r--r--src/data/deck_ai_pointers.asm55
-rw-r--r--src/data/effect_commands.asm6
-rw-r--r--src/engine/bank01.asm19
-rw-r--r--src/engine/bank05.asm1384
-rw-r--r--src/engine/bank08.asm1342
-rw-r--r--src/engine/deck_ai/deck_ai.asm82
-rw-r--r--src/engine/deck_ai/decks/fire_charge.asm86
-rw-r--r--src/engine/deck_ai/decks/first_strike.asm82
-rw-r--r--src/engine/deck_ai/decks/flower_power.asm81
-rw-r--r--src/engine/deck_ai/decks/general.asm196
-rw-r--r--src/engine/deck_ai/decks/general_no_retreat.asm146
-rw-r--r--src/engine/deck_ai/decks/go_go_rain_dance.asm85
-rw-r--r--src/engine/deck_ai/decks/im_ronald.asm86
-rw-r--r--src/engine/deck_ai/decks/invincible_ronald.asm84
-rw-r--r--src/engine/deck_ai/decks/legendary_articuno.asm217
-rw-r--r--src/engine/deck_ai/decks/legendary_dragonite.asm173
-rw-r--r--src/engine/deck_ai/decks/legendary_moltres.asm183
-rw-r--r--src/engine/deck_ai/decks/legendary_ronald.asm210
-rw-r--r--src/engine/deck_ai/decks/legendary_zapdos.asm160
-rw-r--r--src/engine/deck_ai/decks/powerful_ronald.asm98
-rw-r--r--src/engine/deck_ai/decks/rock_crusher.asm80
-rw-r--r--src/engine/deck_ai/decks/sams_practice.asm222
-rw-r--r--src/engine/deck_ai/decks/strange_psyshock.asm87
-rw-r--r--src/engine/deck_ai/decks/wonders_of_science.asm83
-rw-r--r--src/engine/deck_ai/decks/zapping_selfdestruct.asm81
-rw-r--r--src/engine/effect_functions.asm235
-rw-r--r--src/engine/home.asm63
-rw-r--r--src/hram.asm23
-rw-r--r--src/text/text1.asm2
-rw-r--r--src/text/text_offsets.asm2
-rw-r--r--src/wram.asm117
34 files changed, 5229 insertions, 631 deletions
diff --git a/src/constants.asm b/src/constants.asm
index 2159564..5049fa9 100644
--- a/src/constants.asm
+++ b/src/constants.asm
@@ -1,6 +1,7 @@
INCLUDE "constants/booster_constants.asm"
INCLUDE "constants/card_constants.asm"
INCLUDE "constants/card_data_constants.asm"
+INCLUDE "constants/deck_ai_constants.asm"
INCLUDE "constants/deck_constants.asm"
INCLUDE "constants/duel_constants.asm"
INCLUDE "constants/duel_interface_constants.asm"
diff --git a/src/constants/deck_ai_constants.asm b/src/constants/deck_ai_constants.asm
new file mode 100644
index 0000000..ceae8ed
--- /dev/null
+++ b/src/constants/deck_ai_constants.asm
@@ -0,0 +1,61 @@
+; wPreviousAIFlags and wCurrentAIFlags constants
+AI_FLAG_USED_PLUSPOWER EQU 1 << 0
+AI_FLAG_USED_SWITCH EQU 1 << 1
+AI_FLAG_USED_PROFESSOR_OAK EQU 1 << 2
+AI_FLAG_MODIFIED_HAND EQU 1 << 3
+AI_FLAG_USED_GUST_OF_WIND EQU 1 << 4
+
+; used as input for AIProcessEnergyCards to determine what to check
+; and whether to play card after the routine is over.
+; I suspect AI_ENERGY_FLAG_DONT_PLAY to be a flag to signal the routine
+; not to actually play the energy card after it's finished,
+; but AIProcessEnergyCards checks whether ANY flag is set in order
+; to decide not to play it, so it's redundant in the presence of another flag.
+AI_ENERGY_FLAG_DONT_PLAY EQU 1 << 0 ; whether to play energy card (?)
+AI_ENERGY_FLAG_SKIP_EVOLUTION EQU 1 << 1 ; whether to check if card has evolutions
+AI_ENERGY_FLAG_SKIP_ARENA_CARD EQU 1 << 7 ; whether to include Arena card in determining which card to attach energy
+
+; used to determine which Trainer cards for AI
+; to process in AIProcessHandTrainerCards.
+; these go in chronological order, except for
+; AI_TRAINER_CARD_PHASE_14 which happens just before AI attacks.
+; AI_TRAINER_CARD_PHASE_15 is reserved for Professor Oak card.
+; if Professor Oak is played, all other Trainer card phases
+; are processed again except AI_TRAINER_CARD_PHASE_15.
+ const_def 1
+ const AI_TRAINER_CARD_PHASE_01 ; $1
+ const AI_TRAINER_CARD_PHASE_02 ; $2
+ const AI_TRAINER_CARD_PHASE_03 ; $3
+ const AI_TRAINER_CARD_PHASE_04 ; $4
+ const AI_TRAINER_CARD_PHASE_05 ; $5
+ const AI_TRAINER_CARD_PHASE_06 ; $6
+ const AI_TRAINER_CARD_PHASE_07 ; $7
+ const AI_TRAINER_CARD_PHASE_08 ; $8
+ const AI_TRAINER_CARD_PHASE_09 ; $9
+ const AI_TRAINER_CARD_PHASE_10 ; $a
+ const AI_TRAINER_CARD_PHASE_11 ; $b
+ const AI_TRAINER_CARD_PHASE_12 ; $c
+ const AI_TRAINER_CARD_PHASE_13 ; $d
+ const AI_TRAINER_CARD_PHASE_14 ; $e, just before attack
+ const AI_TRAINER_CARD_PHASE_15 ; $f, for Professor Oak
+
+; used by wAIBarrierFlagCounter to determine
+; whether Player is running Mewtwo1 mill deck.
+; flag set means true, flag not set means false.
+AI_FLAG_MEWTWO_MILL EQU 1 << 7
+
+; defines the behaviour of HandleAIEnergyTrans, for determining
+; whether to move energy cards from the Bench to the Arena or vice-versa
+; and the number of energy cards needed for achieving that.
+AI_ENERGY_TRANS_RETREAT EQU $9 ; moves energy cards needed for Retreat Cost
+AI_ENERGY_TRANS_ATTACK EQU $d ; moves energy cards needed for second attack
+AI_ENERGY_TRANS_TO_BENCH EQU $e ; moves energy cards away from Arena card
+
+; used to know which AI routine to call in
+; the AIAction pointer tables in AIDoAction
+ const_def 1
+ const AIACTION_DO_TURN ; $1
+ const AIACTION_START_DUEL ; $2
+ const AIACTION_FORCED_SWITCH ; $3
+ const AIACTION_KO_SWITCH ; $4
+ const AIACTION_TAKE_PRIZE ; $5
diff --git a/src/constants/duel_constants.asm b/src/constants/duel_constants.asm
index 5d105d7..344cff1 100644
--- a/src/constants/duel_constants.asm
+++ b/src/constants/duel_constants.asm
@@ -3,6 +3,8 @@ MAX_PLAY_AREA_POKEMON EQU 6 ; arena + bench
MAX_HP EQU 120
HP_BAR_LENGTH EQU MAX_HP / 10
+STARTING_HAND_SIZE EQU 7
+
; hWhoseTurn constants
PLAYER_TURN EQUS "HIGH(wPlayerDuelVariables)"
OPPONENT_TURN EQUS "HIGH(wOpponentDuelVariables)"
@@ -231,29 +233,3 @@ PRIZES_3 EQU $03
PRIZES_4 EQU $04
PRIZES_5 EQU $05
PRIZES_6 EQU $06
-
-; wPreviousAIFlags and wCurrentAIFlags constants
-AI_FLAG_USED_PLUSPOWER EQU 1 << 0
-AI_FLAG_USED_SWITCH EQU 1 << 1
-AI_FLAG_USED_PROFESSOR_OAK EQU 1 << 2
-AI_FLAG_MODIFIED_HAND EQU 1 << 3
-AI_FLAG_USED_GUST_OF_WIND EQU 1 << 4
-
-; used to determine which Trainer cards for AI
-; to process in AIProcessHandTrainerCards.
-; aside from a few exceptions, these go in chronological order.
-AI_TRAINER_CARD_PHASE_01 EQU $1
-AI_TRAINER_CARD_PHASE_02 EQU $2
-AI_TRAINER_CARD_PHASE_03 EQU $3
-AI_TRAINER_CARD_PHASE_04 EQU $4
-AI_TRAINER_CARD_PHASE_05 EQU $5
-AI_TRAINER_CARD_PHASE_06 EQU $6
-AI_TRAINER_CARD_PHASE_07 EQU $7
-AI_TRAINER_CARD_PHASE_08 EQU $8
-AI_TRAINER_CARD_PHASE_09 EQU $9
-AI_TRAINER_CARD_PHASE_10 EQU $a
-AI_TRAINER_CARD_PHASE_11 EQU $b
-AI_TRAINER_CARD_PHASE_12 EQU $c
-AI_TRAINER_CARD_PHASE_13 EQU $d
-AI_TRAINER_CARD_PHASE_14 EQU $e
-AI_TRAINER_CARD_PHASE_15 EQU $f
diff --git a/src/data/deck_ai_pointers.asm b/src/data/deck_ai_pointers.asm
new file mode 100644
index 0000000..4f00299
--- /dev/null
+++ b/src/data/deck_ai_pointers.asm
@@ -0,0 +1,55 @@
+DeckAIPointerTable: ; 14000 (05:4000)
+ dw AIActionTable_SamPractice ; SAMS_PRACTICE_DECK
+ dw AIActionTable_GeneralDecks ; PRACTICE_PLAYER_DECK
+ dw AIActionTable_GeneralDecks ; SAMS_NORMAL_DECK
+ dw AIActionTable_GeneralDecks ; CHARMANDER_AND_FRIENDS_DECK
+ dw AIActionTable_GeneralDecks ; CHARMANDER_EXTRA_DECK
+ dw AIActionTable_GeneralDecks ; SQUIRTLE_AND_FRIENDS_DECK
+ dw AIActionTable_GeneralDecks ; SQUIRTLE_EXTRA_DECK
+ dw AIActionTable_GeneralDecks ; BULBASAUR_AND_FRIENDS_DECK
+ dw AIActionTable_GeneralDecks ; BULBASAUR_EXTRA_DECK
+ dw AIActionTable_GeneralDecks ; LIGHTNING_AND_FIRE_DECK
+ dw AIActionTable_GeneralDecks ; WATER_AND_FIGHTING_DECK
+ dw AIActionTable_GeneralDecks ; GRASS_AND_PSYCHIC_DECK
+ dw AIActionTable_LegendaryMoltres ; LEGENDARY_MOLTRES_DECK
+ dw AIActionTable_LegendaryZapdos ; LEGENDARY_ZAPDOS_DECK
+ dw AIActionTable_LegendaryArticuno ; LEGENDARY_ARTICUNO_DECK
+ dw AIActionTable_LegendaryDragonite ; LEGENDARY_DRAGONITE_DECK
+ dw AIActionTable_FirstStrike ; FIRST_STRIKE_DECK
+ dw AIActionTable_RockCrusher ; ROCK_CRUSHER_DECK
+ dw AIActionTable_GoGoRainDance ; GO_GO_RAIN_DANCE_DECK
+ dw AIActionTable_ZappingSelfdestruct ; ZAPPING_SELFDESTRUCT_DECK
+ dw AIActionTable_FlowerPower ; FLOWER_POWER_DECK
+ dw AIActionTable_StrangePsyshock ; STRANGE_PSYSHOCK_DECK
+ dw AIActionTable_WondersOfScience ; WONDERS_OF_SCIENCE_DECK
+ dw AIActionTable_FireCharge ; FIRE_CHARGE_DECK
+ dw AIActionTable_ImRonald ; IM_RONALD_DECK
+ dw AIActionTable_PowerfulRonald ; POWERFUL_RONALD_DECK
+ dw AIActionTable_InvincibleRonald ; INVINCIBLE_RONALD_DECK
+ dw AIActionTable_LegendaryRonald ; LEGENDARY_RONALD_DECK
+ dw AIActionTable_GeneralNoRetreat ; MUSCLES_FOR_BRAINS_DECK
+ dw AIActionTable_GeneralDecks ; HEATED_BATTLE_DECK
+ dw AIActionTable_GeneralDecks ; LOVE_TO_BATTLE_DECK
+ dw AIActionTable_GeneralDecks ; EXCAVATION_DECK
+ dw AIActionTable_GeneralDecks ; BLISTERING_POKEMON_DECK
+ dw AIActionTable_GeneralDecks ; HARD_POKEMON_DECK
+ dw AIActionTable_GeneralDecks ; WATERFRONT_POKEMON_DECK
+ dw AIActionTable_GeneralDecks ; LONELY_FRIENDS_DECK
+ dw AIActionTable_GeneralDecks ; SOUND_OF_THE_WAVES_DECK
+ dw AIActionTable_GeneralDecks ; PIKACHU_DECK
+ dw AIActionTable_GeneralDecks ; BOOM_BOOM_SELFDESTRUCT_DECK
+ dw AIActionTable_GeneralDecks ; POWER_GENERATOR_DECK
+ dw AIActionTable_GeneralDecks ; ETCETERA_DECK
+ dw AIActionTable_GeneralDecks ; FLOWER_GARDEN_DECK
+ dw AIActionTable_GeneralDecks ; KALEIDOSCOPE_DECK
+ dw AIActionTable_GeneralDecks ; GHOST_DECK
+ dw AIActionTable_GeneralDecks ; NAP_TIME_DECK
+ dw AIActionTable_GeneralDecks ; STRANGE_POWER_DECK
+ dw AIActionTable_GeneralDecks ; FLYIN_POKEMON_DECK
+ dw AIActionTable_GeneralDecks ; LOVELY_NIDORAN_DECK
+ dw AIActionTable_GeneralDecks ; POISON_DECK
+ dw AIActionTable_GeneralDecks ; ANGER_DECK
+ dw AIActionTable_GeneralDecks ; FLAMETHROWER_DECK
+ dw AIActionTable_GeneralDecks ; RESHUFFLE_DECK
+ dw AIActionTable_GeneralNoRetreat ; IMAKUNI_DECK
+; 1406a
diff --git a/src/data/effect_commands.asm b/src/data/effect_commands.asm
index 0e1886e..171c241 100644
--- a/src/data/effect_commands.asm
+++ b/src/data/effect_commands.asm
@@ -42,9 +42,9 @@ EkansWrapEffectCommands:
db $00
ArbokTerrorStrikeEffectCommands:
- dbw EFFECTCMDTYPE_AFTER_DAMAGE, $4726
- dbw EFFECTCMDTYPE_REQUIRE_SELECTION, $470a
- dbw EFFECTCMDTYPE_SWITCH_DEFENDING_PKMN, $470a
+ dbw EFFECTCMDTYPE_AFTER_DAMAGE, TerrorStrike_SwitchDefendingPokemon
+ dbw EFFECTCMDTYPE_REQUIRE_SELECTION, TerrorStrike_50PercentSelectSwitchPokemon
+ dbw EFFECTCMDTYPE_SWITCH_DEFENDING_PKMN, TerrorStrike_50PercentSelectSwitchPokemon
db $00
ArbokPoisonFangEffectCommands:
diff --git a/src/engine/bank01.asm b/src/engine/bank01.asm
index 51fb0ef..090ecf0 100644
--- a/src/engine/bank01.asm
+++ b/src/engine/bank01.asm
@@ -353,7 +353,7 @@ DuelMainInterface: ; 426d (1:426d)
ld [wSkipDuelistIsThinkingDelay], a
ldtx hl, DuelistIsThinkingText
call DrawWideTextBox_PrintTextNoDelay
- call Func_2bbf
+ call AIDoAction_Turn
ld a, $ff
ld [wPlayerAttackingCardIndex], a
ld [wPlayerAttackingMoveIndex], a
@@ -1967,7 +1967,7 @@ ChooseInitialArenaAndBenchPokemon: ; 4cd5 (1:4cd5)
; AI opponent's turn
push af
push hl
- call Func_2bc3
+ call AIDoAction_StartDuel
pop hl
pop af
ld [hl], a
@@ -3720,7 +3720,7 @@ Func_57df: ; 57df (1:57df)
Func_5805: ; 5805 (1:5805)
call Func_3b31
- ld a, [wccc8]
+ ld a, [wNumberPrizeCardsToTake]
ld l, a
ld h, $00
call LoadTxRam3
@@ -3731,7 +3731,7 @@ Func_5805: ; 5805 (1:5805)
ldtx hl, WillDrawNPrizesText
call DrawWideTextBox_WaitForInput
- ld a, [wccc8]
+ ld a, [wNumberPrizeCardsToTake]
call Func_310a
ld hl, hTemp_ffa0
ld d, [hl]
@@ -3757,7 +3757,7 @@ Func_5805: ; 5805 (1:5805)
call GetTurnDuelistVariable
cp DUELIST_TYPE_LINK_OPP
jr z, .link_opponent
- call Func_2bd7
+ call AIDoAction_TakePrize
ld c, DECK_SIZE
.asm_5858
call DoFrame
@@ -3775,7 +3775,7 @@ Func_5805: ; 5805 (1:5805)
call nz, AddCardToHand
.asm_586f
ld a, [wcbfc]
- ld hl, wccc8
+ ld hl, wNumberPrizeCardsToTake
cp [hl]
jr nc, .asm_587e
ld l, a
@@ -6835,6 +6835,7 @@ OppAction_ExecutePokemonPowerEffect: ; 6b07 (1:6b07)
ret
; 0x6b15
+; execute the EFFECTCMDTYPE_AFTER_DAMAGE command of the used Pokemon Power
OppAction_6b15: ; 6b15 (1:6b15)
ld a, EFFECTCMDTYPE_AFTER_DAMAGE
call TryExecuteEffectCommandFunction
@@ -7491,7 +7492,7 @@ ReplaceKnockedOutPokemon: ; 6f23 (1:6f23)
.opponent
cp DUELIST_TYPE_LINK_OPP
jr z, .link_opponent
- call Func_2bcf
+ call AIDoAction_KOSwitch
ldh a, [hTemp_ffa0]
ldh [hTempPlayAreaLocation_ff9d], a
jr .replace_pokemon
@@ -7524,7 +7525,7 @@ Func_6fa5: ; 6fa5 (1:6fa5)
ret
; 0x6fc7
-; return in wccc8 the amount of Pokemon in the turn holder's
+; return in wNumberPrizeCardsToTake the amount of Pokemon in the turn holder's
; play area that are still there despite having 0 HP.
; that is, the number of Pokemon that have just been knocked out.
; Clefairy Doll and Mysterious Fossil don't count.
@@ -7557,7 +7558,7 @@ CountKnockedOutPokemon: ; 6fc7 (1:6fc7)
dec c
jr nz, .loop
ld a, b
- ld [wccc8], a
+ ld [wNumberPrizeCardsToTake], a
or a
ret z
scf
diff --git a/src/engine/bank05.asm b/src/engine/bank05.asm
index ee45de4..762d5c8 100644
--- a/src/engine/bank05.asm
+++ b/src/engine/bank05.asm
@@ -1,89 +1,52 @@
-PointerTable_14000: ; 14000 (05:4000)
- dw $47bd ; SAMS_PRACTICE_DECK
- dw PointerTable_14668 ; PRACTICE_PLAYER_DECK
- dw PointerTable_14668 ; SAMS_NORMAL_DECK
- dw PointerTable_14668 ; CHARMANDER_AND_FRIENDS_DECK
- dw PointerTable_14668 ; CHARMANDER_EXTRA_DECK
- dw PointerTable_14668 ; SQUIRTLE_AND_FRIENDS_DECK
- dw PointerTable_14668 ; SQUIRTLE_EXTRA_DECK
- dw PointerTable_14668 ; BULBASAUR_AND_FRIENDS_DECK
- dw PointerTable_14668 ; BULBASAUR_EXTRA_DECK
- dw PointerTable_14668 ; LIGHTNING_AND_FIRE_DECK
- dw PointerTable_14668 ; WATER_AND_FIGHTING_DECK
- dw PointerTable_14668 ; GRASS_AND_PSYCHIC_DECK
- dw $49e8 ; LEGENDARY_MOLTRES_DECK
- dw $4b0f ; LEGENDARY_ZAPDOS_DECK
- dw $4c0b ; LEGENDARY_ARTICUNO_DECK
- dw $4d60 ; LEGENDARY_DRAGONITE_DECK
- dw $4e89 ; FIRST_STRIKE_DECK
- dw $4f0e ; ROCK_CRUSHER_DECK
- dw $4f8f ; GO_GO_RAIN_DANCE_DECK
- dw $5019 ; ZAPPING_SELFDESTRUCT_DECK
- dw $509b ; FLOWER_POWER_DECK
- dw $5122 ; STRANGE_PSYSHOCK_DECK
- dw $51ad ; WONDERS_OF_SCIENCE_DECK
- dw $5232 ; FIRE_CHARGE_DECK
- dw $52bd ; IM_RONALD_DECK
- dw $534b ; POWERFUL_RONALD_DECK
- dw $53e8 ; INVINCIBLE_RONALD_DECK
- dw $546f ; LEGENDARY_RONALD_DECK
- dw $48dc ; MUSCLES_FOR_BRAINS_DECK
- dw PointerTable_14668 ; HEATED_BATTLE_DECK
- dw PointerTable_14668 ; LOVE_TO_BATTLE_DECK
- dw PointerTable_14668 ; EXCAVATION_DECK
- dw PointerTable_14668 ; BLISTERING_POKEMON_DECK
- dw PointerTable_14668 ; HARD_POKEMON_DECK
- dw PointerTable_14668 ; WATERFRONT_POKEMON_DECK
- dw PointerTable_14668 ; LONELY_FRIENDS_DECK
- dw PointerTable_14668 ; SOUND_OF_THE_WAVES_DECK
- dw PointerTable_14668 ; PIKACHU_DECK
- dw PointerTable_14668 ; BOOM_BOOM_SELFDESTRUCT_DECK
- dw PointerTable_14668 ; POWER_GENERATOR_DECK
- dw PointerTable_14668 ; ETCETERA_DECK
- dw PointerTable_14668 ; FLOWER_GARDEN_DECK
- dw PointerTable_14668 ; KALEIDOSCOPE_DECK
- dw PointerTable_14668 ; GHOST_DECK
- dw PointerTable_14668 ; NAP_TIME_DECK
- dw PointerTable_14668 ; STRANGE_POWER_DECK
- dw PointerTable_14668 ; FLYIN_POKEMON_DECK
- dw PointerTable_14668 ; LOVELY_NIDORAN_DECK
- dw PointerTable_14668 ; POISON_DECK
- dw PointerTable_14668 ; ANGER_DECK
- dw PointerTable_14668 ; FLAMETHROWER_DECK
- dw PointerTable_14668 ; RESHUFFLE_DECK
- dw $48dc ; IMAKUNI_DECK
-; 1406a
-
-PointerTable_1406a: ; 1406a (5:406a)
+INCLUDE "data/deck_ai_pointers.asm"
+
+AIActionTable_Unreferenced: ; 1406a (5:406a)
dw $406c
- dw Func_14078
- dw Func_14078
- dw Func_1409e
- dw $40a2
- dw $40a6
- dw $40aa
-
-Func_14078: ; 14078 (5:4078)
+ dw .do_turn
+ dw .do_turn
+ dw .star_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 14078 (5:4078)
call AIDecidePlayPokemonCard
call AIDecideWhetherToRetreat
- jr nc, .asm_14091
+ jr nc, .try_attack
call AIDecideBenchPokemonToSwitchTo
- call AIChooseEnergyToDiscardForRetreatCost
+ call AITryToRetreat
call AIDecideWhetherToRetreat
- jr nc, .asm_14091
+ jr nc, .try_attack
call AIDecideBenchPokemonToSwitchTo
- call AIChooseEnergyToDiscardForRetreatCost
-.asm_14091
- call AIDecidePlayEnergyCardFromHand
- call Func_169f8
+ call AITryToRetreat
+.try_attack
+ call AIProcessAndTryToPlayEnergy
+ call AIProcessAndTryToUseAttack
ret c
ld a, OPPACTION_FINISH_NO_ATTACK
bank1call AIMakeDecision
ret
; 0x1409e
-Func_1409e: ; 1409e (5:409e)
- INCROM $1409e, $140ae
+.star_duel ; 1409e (5:409e)
+ call AIPlayInitialBasicCards
+ ret
+; 0x140a2
+
+.forced_switch ; 140a2 (5:40a2)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x140a6
+
+.ko_switch ; 140a6 (5:40a6)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x140aa
+
+.take_prize ; 140aa (5:40aa)
+ call AIPickPrizeCards
+ ret
+; 0x140ae
; returns carry if damage dealt from any of
; a card's moves KOs defending Pokémon
@@ -137,7 +100,7 @@ CheckIfAnyDefendingPokemonAttackDealsSameDamageAsHP: ; 140c5 (5:40c5)
; checks AI scores for all benched Pokémon
; returns the location of the card with highest score
-; in hTempPlayAreaLocation_ff9d
+; in a and [hTempPlayAreaLocation_ff9d]
FindHighestBenchScore: ; 140df (5:40df)
ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
call GetTurnDuelistVariable
@@ -220,8 +183,44 @@ LoadDefendingPokemonColorWRAndPrizeCards: ; 1411d (5:411d)
ret
; 0x14145
-Func_14145: ; 14145 (5:4145)
- INCROM $14145, $14184
+; called when AI has chosen its attack.
+; executes all effects and damage.
+; handles AI choosing parameters for certain attacks as well.
+AITryUseAttack: ; 14145 (5:4145)
+ ld a, [wSelectedAttack]
+ ldh [hTemp_ffa0], a
+ ld e, a
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ ldh [hTempCardIndex_ff9f], a
+ ld d, a
+ call CopyMoveDataAndDamage_FromDeckIndex
+ ld a, OPPACTION_BEGIN_ATTACK
+ bank1call AIMakeDecision
+ ret c
+
+ call AISelectSpecialAttackParameters
+ jr c, .use_attack
+ ld a, OPPACTION_BEGIN_ATTACK
+ call TryExecuteEffectCommandFunction
+
+.use_attack
+ ld a, [wSelectedAttack]
+ ld e, a
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ ld d, a
+ call CopyMoveDataAndDamage_FromDeckIndex
+ ld a, OPPACTION_USE_ATTACK
+ bank1call AIMakeDecision
+ ret c
+
+ ld a, OPPACTION_ATTACK_ANIM_AND_DAMAGE
+ call TryExecuteEffectCommandFunction
+ ld a, OPPACTION_ATTACK_ANIM_AND_DAMAGE
+ bank1call AIMakeDecision
+ ret
+; 0x14184
; return carry if any of the following is satisfied:
; - deck index in a corresponds to a double colorless energy card;
@@ -296,25 +295,100 @@ CheckIfEnergyIsUseful: ; 14184 (5:4184)
ret
; 0x141da
-Func_141da: ; 141da (5:41da)
- INCROM $141da, $14226
+; pick a random Pokemon in the bench.
+; output:
+; - a = PLAY_AREA_* of Bench Pokemon picked.
+PickRandomBenchPokemon: ; 141da (5:41da)
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ dec a
+ call Random
+ inc a
+ ret
+; 0x141e5
-Func_14226: ; 14226 (5:4226)
+AIPickPrizeCards: ; 141e5 (5:41e5)
+ ld a, [wNumberPrizeCardsToTake]
+ ld b, a
+.loop
+ call .PickPrizeCard
+ ld a, DUELVARS_PRIZES
+ call GetTurnDuelistVariable
+ or a
+ jr z, .done
+ dec b
+ jr nz, .loop
+.done
+ ret
+; 0x141f8
+
+; picks a prize card at random
+; and adds it to the hand.
+.PickPrizeCard: ; 141f8 (5:41f8)
+ ld a, DUELVARS_PRIZES
+ call GetTurnDuelistVariable
+ push hl
+ ld c, a
+
+; choose a random prize card until
+; one is found that isn't taken already.
+.loop_pick_prize
+ ld a, 6
+ call Random
+ ld e, a
+ ld d, $00
+ ld hl, .prize_flags
+ add hl, de
+ ld a, [hl]
+ and c
+ jr z, .loop_pick_prize ; no prize
+
+; prize card was found
+; remove this prize from wOpponentPrizes
+ ld a, [hl]
+ pop hl
+ cpl
+ and [hl]
+ ld [hl], a
+
+; add this prize card to the hand
+ ld a, e
+ add DUELVARS_PRIZE_CARDS
+ call GetTurnDuelistVariable
+ call AddCardToHand
+ ret
+
+.prize_flags ; 1421e (5:421e)
+ db $1 << 0
+ db $1 << 1
+ db $1 << 2
+ db $1 << 3
+ db $1 << 4
+ db $1 << 5
+ db $1 << 6
+ db $1 << 7
+; 0x14226
+
+; routine for AI to play all Basic cards from its hand
+; in the beginning of the Duel.
+AIPlayInitialBasicCards: ; 14226 (5:4226)
call CreateHandCardList
ld hl, wDuelTempList
.check_for_next_card
ld a, [hli]
ldh [hTempCardIndex_ff98], a
cp $ff
- ret z
+ ret z ; return when done
call LoadCardDataToBuffer1_FromDeckIndex
ld a, [wLoadedCard1Type]
cp TYPE_ENERGY
- jr nc, .check_for_next_card
+ jr nc, .check_for_next_card ; skip if not Pokemon card
ld a, [wLoadedCard1Stage]
or a
- jr nz, .check_for_next_card
+ jr nz, .check_for_next_card ; skip if not Basic Stage
+
+; play Basic card from hand
push hl
ldh a, [hTempCardIndex_ff98]
call PutHandPokemonCardInPlayArea
@@ -521,7 +595,7 @@ ConvertColorToEnergyCardID: ; 1430f (5:430f)
db FIGHTING_ENERGY
db PSYCHIC_ENERGY
db DOUBLE_COLORLESS_ENERGY
-
+
Func_14323: ; 14323 (5:4323)
INCROM $14323, $1433d
@@ -1003,13 +1077,13 @@ EstimateDamage_FromDefendingPokemon: ; 1450b (5:450b)
; damage as the receiver
CalculateDamage_FromDefendingPokemon: ; 1458c (5:458c)
ld hl, wAIMinDamage
- call _CalculateDamage_FromDefendingPokemon
+ call .CalculateDamage
ld hl, wAIMaxDamage
- call _CalculateDamage_FromDefendingPokemon
+ call .CalculateDamage
ld hl, wDamage
-; fallthrough
+ ; fallthrough
-_CalculateDamage_FromDefendingPokemon: ; 1459b (5:459b)
+.CalculateDamage ; 1459b (5:459b)
ld e, [hl]
ld d, $00
push hl
@@ -1139,243 +1213,7 @@ AIProcessHandTrainerCards: ; 14663 (5:4663)
farcall _AIProcessHandTrainerCards
ret
-; GENERAL DECK POINTER LIST - Not sure on all of these.
-; This is an example of an AI pointer table, there's one for each AI type.
-PointerTable_14668: ; 14668 (05:4668)
- dw Func_14674 ; not used
- dw Func_14674 ; general AI for battles
- dw Func_14678 ; basic pokemon placement / cheater shuffling on better AI
- dw Func_1467f
- dw Func_14683
- dw Func_14687
-
-; when battle AI gets called
-Func_14674: ; 14674 (5:4674)
- call Func_1468b
- ret
-
-Func_14678: ; 14678 (5:4678)
- call Func_15636
- call $4226
- ret
-
-Func_1467f: ; 1467f (5:467f)
- call $5b72
- ret
-
-Func_14683: ; 14683 (5:4683)
- call $5b72
- ret
-
-Func_14687: ; 14687 (5:4687)
- call $41e5
- ret
-
-; AI for general decks i think
-Func_1468b: ; 1468b (5:468b)
- call Func_15649
- ld a, $1
- call AIProcessHandTrainerCards
- farcall $8, $67d3
- jp nc, $4776
- farcall $8, $6790
- farcall $8, $66a3
- farcall $8, $637f
- ret c
- farcall $8, $662d
- ld a, $2
- call AIProcessHandTrainerCards
- ld a, $3
- call AIProcessHandTrainerCards
- ld a, $4
- call AIProcessHandTrainerCards
- call $5eae
- ret c
- ld a, $5
- call AIProcessHandTrainerCards
- ld a, $6
- call AIProcessHandTrainerCards
- ld a, $7
- call AIProcessHandTrainerCards
- ld a, $8
- call AIProcessHandTrainerCards
- call $4786
- ld a, $a
- call AIProcessHandTrainerCards
- ld a, $b
- call AIProcessHandTrainerCards
- ld a, $c
- call AIProcessHandTrainerCards
- ld a, [wAlreadyPlayedEnergy]
- or a
- jr nz, .asm_146ed
- call $64e8
-
-.asm_146ed
- call $5eae
- farcall $8, $66a3
- farcall $8, $637f
- ret c
- farcall $8, $6790
- ld a, $d
- farcall $8, $619b
- ld a, $d
- call AIProcessHandTrainerCards
- ld a, $f
- call AIProcessHandTrainerCards
- ld a, [wPreviousAIFlags]
- and AI_FLAG_USED_PROFESSOR_OAK
- jr z, .asm_14776
- ld a, $1
- call AIProcessHandTrainerCards
- ld a, $2
- call AIProcessHandTrainerCards
- ld a, $3
- call AIProcessHandTrainerCards
- ld a, $4
- call AIProcessHandTrainerCards
- call $5eae
- ret c
- ld a, $5
- call AIProcessHandTrainerCards
- ld a, $6
- call AIProcessHandTrainerCards
- ld a, $7
- call AIProcessHandTrainerCards
- ld a, $8
- call AIProcessHandTrainerCards
- call $4786
- ld a, $a
- call AIProcessHandTrainerCards
- ld a, $b
- call AIProcessHandTrainerCards
- ld a, $c
- call AIProcessHandTrainerCards
- ld a, [wAlreadyPlayedEnergy]
- or a
- jr nz, .asm_1475b
- call $64e8
-
-.asm_1475b
- call $5eae
- farcall $8, $66a3
- farcall $8, $637f
- ret c
- farcall $8, $6790
- ld a, $d
- farcall $8, $619b
- ld a, $d
- call AIProcessHandTrainerCards
-
-.asm_14776
- ld a, $e
- farcall $8, $619b
- call $69f8
- ret c
- ld a, $5
- bank1call AIMakeDecision
- ret
-; 0x14786
-
-Func_14786: ; 14786 (5:4786)
- INCROM $14786, $14c91
-
-; this routine handles how Legendary Articuno
-; prioritises playing energy cards to each Pokémon.
-; first, it makes sure that all Lapras have at least
-; 3 energy cards before moving on to Articuno,
-; and then to Dewgong and Seel
-ScoreLegendaryArticunoCards: ; 14c91 (5:4c91)
- call SwapTurn
- call CountPrizes
- call SwapTurn
- cp 3
- ret c
-
-; player prizes >= 3
-; if Lapras has more than half HP and
-; can use second move, check next for Articuno
-; otherwise, check if Articuno or Dewgong
-; have more than half HP and can use second move
-; and if so, the next Pokémon to check is Lapras
- ld a, LAPRAS
- call CheckForBenchIDAtHalfHPAndCanUseSecondMove
- jr c, .articuno
- ld a, ARTICUNO1
- call CheckForBenchIDAtHalfHPAndCanUseSecondMove
- jr c, .lapras
- ld a, DEWGONG
- call CheckForBenchIDAtHalfHPAndCanUseSecondMove
- jr c, .lapras
- jr .articuno
-
-; the following routines check for certain card IDs in bench
-; and call RaiseAIScoreToAllMatchingIDsInBench if these are found.
-; for Lapras, an additional check is made to its
-; attached energy count, which skips calling the routine
-; if this count is >= 3
-.lapras
- ld a, LAPRAS
- ld b, PLAY_AREA_BENCH_1
- call LookForCardIDInPlayArea_Bank5
- jr nc, .articuno
- ld e, a
- call CountNumberOfEnergyCardsAttached
- cp 3
- jr nc, .articuno
- ld a, LAPRAS
- call RaiseAIScoreToAllMatchingIDsInBench
- ret
-
-.articuno
- ld a, ARTICUNO1
- ld b, PLAY_AREA_BENCH_1
- call LookForCardIDInPlayArea_Bank5
- jr nc, .dewgong
- ld a, ARTICUNO1
- call RaiseAIScoreToAllMatchingIDsInBench
- ret
-
-.dewgong
- ld a, DEWGONG
- ld b, PLAY_AREA_BENCH_1
- call LookForCardIDInPlayArea_Bank5
- jr nc, .seel
- ld a, DEWGONG
- call RaiseAIScoreToAllMatchingIDsInBench
- ret
-
-.seel
- ld a, SEEL
- ld b, PLAY_AREA_BENCH_1
- call LookForCardIDInPlayArea_Bank5
- ret nc
- ld a, SEEL
- call RaiseAIScoreToAllMatchingIDsInBench
- ret
-; 0x14cf7
-
-Func_14cf7: ; 14cf7 (5:4cf7)
- INCROM $14cf7, $1514f
-
-; these seem to be lists of card IDs
-; for the AI to look up in their hand
-Data_1514f: ; 1514f (5:514f)
- db KANGASKHAN
- db CHANSEY
- db SNORLAX
- db MR_MIME
- db ABRA
- db $00
-
- db ABRA
- db MR_MIME
- db KANGASKHAN
- db SNORLAX
- db CHANSEY
- db $00
-
- INCROM $1515b, $155d2
+INCLUDE "engine/deck_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
@@ -1384,7 +1222,7 @@ Data_1514f: ; 1514f (5:514f)
; input:
; a = card ID
; output:
-; a = card deck index, if found
+; a = card deck index, if found
; carry set if found
LookForCardIDInHandList_Bank5: ; 155d2 (5:55d2)
ld [wTempCardIDToLook], a
@@ -1444,49 +1282,105 @@ LookForCardIDInPlayArea_Bank5: ; 155ef (5:55ef)
ret
; 0x15612
-Func_15612: ; 15612 (5:5612)
- INCROM $15612, $15636
+; check if energy card ID in e is in AI hand and,
+; if so, attaches it to card ID in d in Play Area.
+; input:
+; e = Energy card ID
+; d = Pokemon card ID
+AIAttachEnergyInHandToCardInPlayArea: ; 15612 (5:5612)
+ ld a, e
+ push de
+ call LookForCardIDInHandList_Bank5
+ pop de
+ ret nc ; not in hand
+ ld b, PLAY_AREA_ARENA
+
+.attach
+ ld e, a
+ ld a, d
+ call LookForCardIDInPlayArea_Bank5
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, e
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_PLAY_ENERGY
+ bank1call AIMakeDecision
+ ret
+; 0x1562b
+
+; same as AIAttachEnergyInHandToCardInPlayArea but
+; only look for card ID in the Bench.
+AIAttachEnergyInHandToCardInBench: ; 1562b (5:562b)
+ ld a, e
+ push de
+ call LookForCardIDInHandList_Bank5
+ pop de
+ ret nc
+ ld b, PLAY_AREA_BENCH_1
+ jr AIAttachEnergyInHandToCardInPlayArea.attach
+; 0x15636
-Func_15636: ; 15636 (5:5636)
+InitAIDuelVars: ; 15636 (5:5636)
ld a, $10
ld hl, wcda5
call ClearMemory_Bank5
- ld a, $5
- ld [wcda6], a
+ ld a, 5
+ ld [wAIPokedexCounter], a
ld a, $ff
ld [wcda5], a
ret
-Func_15649: ; 15649 (5:5649)
- ld a, [wcda6]
+; initializes some variables and sets value of wAIBarrierFlagCounter.
+; if Player uses Barrier 3 times in a row, AI checks if Player's deck
+; has only Mewtwo1 Pokemon cards (running a Mewtwo1 mill deck).
+InitAITurnVars: ; 15649 (5:5649)
+; increase Pokedex counter by 1
+ ld a, [wAIPokedexCounter]
inc a
- ld [wcda6], a
+ ld [wAIPokedexCounter], a
+
xor a
ld [wPreviousAIFlags], a
ld [wcddb], a
ld [wcddc], a
- ld [wce03], a
+ ld [wAIRetreatedThisTurn], a
+
+; checks if the Player used an attack last turn
+; and if it was the second attack of their card.
ld a, [wPlayerAttackingMoveIndex]
cp $ff
- jr z, .asm_156b1
+ jr z, .check_flag
or a
- jr z, .asm_156b1
+ jr z, .check_flag
ld a, [wPlayerAttackingCardIndex]
cp $ff
- jr z, .asm_156b1
+ jr z, .check_flag
+
+; if the card is Mewtwo1, it means the Player
+; used its second attack, Barrier.
call SwapTurn
call GetCardIDFromDeckIndex
call SwapTurn
ld a, e
- cp MEWTWO1 ; I believe this is a check for Mewtwo1's Barrier move
- jr nz, .asm_156b1
- ld a, [wcda7]
+ cp MEWTWO1
+ jr nz, .check_flag
+ ; Player used Barrier last turn
+
+; check if flag was already set, if so,
+; reset wAIBarrierFlagCounter to $80.
+ ld a, [wAIBarrierFlagCounter]
bit 7, a
- jr nz, .asm_156aa
+ jr nz, .set_flag
+
+; if not, increase it by 1 and check if it exceeds 2.
inc a
- ld [wcda7], a
- cp $3
- jr c, .asm_156c2
+ ld [wAIBarrierFlagCounter], a
+ cp 3
+ jr c, .done
+
+; this means that the Player used Barrier
+; at least 3 turns in a row.
+; check if Player is running Mewtwo1-only deck,
+; if so, set wAIBarrierFlagCounter flag.
ld a, DUELVARS_ARENA_CARD
call GetNonTurnDuelistVariable
call SwapTurn
@@ -1494,33 +1388,34 @@ Func_15649: ; 15649 (5:5649)
call SwapTurn
ld a, e
cp MEWTWO1
- jr nz, .asm_156a4
- farcall $8, $67a9
- jr nc, .asm_156aa
-
-.asm_156a4
+ jr nz, .reset_1
+ farcall CheckIfPlayerHasPokemonOtherThanMewtwo1
+ jr nc, .set_flag
+.reset_1
+; reset wAIBarrierFlagCounter
xor a
- ld [wcda7], a
- jr .asm_156c2
+ ld [wAIBarrierFlagCounter], a
+ jr .done
-.asm_156aa
- ld a, $80
- ld [wcda7], a
- jr .asm_156c2
+.set_flag
+ ld a, AI_FLAG_MEWTWO_MILL + 0
+ ld [wAIBarrierFlagCounter], a
+ jr .done
-.asm_156b1
- ld a, [wcda7]
+.check_flag
+; increase counter by 1 if flag is set
+ ld a, [wAIBarrierFlagCounter]
bit 7, a
- jr z, .asm_156be
+ jr z, .reset_2
inc a
- ld [wcda7], a
- jr .asm_156c2
+ ld [wAIBarrierFlagCounter], a
+ jr .done
-.asm_156be
+.reset_2
+; reset wAIBarrierFlagCounter
xor a
- ld [wcda7], a
-
-.asm_156c2
+ ld [wAIBarrierFlagCounter], a
+.done
ret
; 0x156c3
@@ -1735,6 +1630,8 @@ CountNumberOfEnergyCardsAttached: ; 15787 (5:5787)
; input:
; a = card location to look in;
; e = card ID to look for.
+; output:
+; a = deck index of card found, if any
CheckIfAnyCardIDinLocation: ; 157a3 (5:57a3)
ld b, a
ld c, e
@@ -1856,8 +1753,82 @@ RemoveCardIDInList: ; 157f3 (5:57f3)
ret
; 0x1581b
-Func_1581b: ; 1581b (5:581b)
- INCROM $1581b, $158b2
+; play Pokemon cards from the hand to set the starting
+; Play Area of Boss decks.
+; each Boss deck has two ID lists in order of preference.
+; one list is for the Arena card is the other is for the Bench cards.
+; if Arena card could not be set (due to hand not having any card in its list)
+; or if list is null, return carry and do not play any cards.
+TrySetUpBossStartingPlayArea: ; 1581b (5:581b)
+ ld de, wAICardListArenaPriority
+ ld a, d
+ or a
+ jr z, .set_carry ; return if null
+
+; pick Arena card
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ ld de, wAICardListArenaPriority
+ call .PlayPokemonCardInOrder
+ ret c
+
+; play Pokemon cards to Bench until there are
+; a maximum of 3 cards in Play Area.
+.loop
+ ld de, wAICardListBenchPriority
+ call .PlayPokemonCardInOrder
+ jr c, .done
+ cp 3
+ jr c, .loop
+
+.done
+ or a
+ ret
+.set_carry
+ scf
+ ret
+; 0x1583f
+
+; runs through input card ID list in de.
+; plays to Play Area first card that is found in hand.
+; returns carry if none of the cards in the list are found.
+; returns number of Pokemon in Play Area in a.
+.PlayPokemonCardInOrder ; 1583f (5:583f)
+ ld a, [de]
+ ld c, a
+ inc de
+ ld a, [de]
+ ld d, a
+ ld e, c
+
+; go in order of the list in de and
+; add first card that matches ID.
+; returns carry if hand doesn't have any card in list.
+.loop_id_list
+ ld a, [de]
+ inc de
+ or a
+ jr z, .not_found
+ push de
+ ld e, a
+ call RemoveCardIDInList
+ pop de
+ jr nc, .loop_id_list
+
+ ; play this card to Play Area and return
+ push hl
+ call PutHandPokemonCardInPlayArea
+ pop hl
+ or a
+ ret
+
+.not_found
+ scf
+ ret
+; 0x1585b
+
+Func_1585b: ; 1585b (5:585b)
+ INCROM $1585b, $158b2
; determine AI score for retreating
; return carry if AI decides to retreat
@@ -2207,7 +2178,7 @@ AIDecideWhetherToRetreat: ; 158b2 (5:58b2)
.one_or_none
call CheckIfArenaCardIsAtHalfHPCanEvolveAndUseSecondMove
jr c, .check_defending_can_ko
- call CheckIfBenchCardsAreAtHalfHPCanEvolveAndUseSecondMove
+ call CountNumberOfSetUpBenchPokemon
cp 2
jr c, .check_defending_can_ko
call AddToAIScore
@@ -2323,8 +2294,9 @@ Func_15b54: ; 15b54 (5:5b54)
; 0x15b72
; calculates AI score for bench Pokémon
-; returns in hTempPlayAreaLocation_ff9d the
-; Play Area location of best card to switch to
+; returns in a and [hTempPlayAreaLocation_ff9d] the
+; Play Area location of best card to switch to.
+; returns carry if no Bench Pokemon.
AIDecideBenchPokemonToSwitchTo: ; 15b72 (5:5b72)
xor a
ldh [hTempPlayAreaLocation_ff9d], a
@@ -2376,17 +2348,17 @@ AIDecideBenchPokemonToSwitchTo: ; 15b72 (5:5b72)
xor a
ld [wSelectedAttack], a
call CheckIfSelectedMoveIsUnusable
- call nc, .calculate_damage
+ call nc, .HandleAttackDamageScore
ld a, $01
ld [wSelectedAttack], a
call CheckIfSelectedMoveIsUnusable
- call nc, .calculate_damage
+ call nc, .HandleAttackDamageScore
jr .check_energy_card
; adds to AI score depending on amount of damage
; it can inflict to the defending Pokémon
; AI score += floor(Damage / 10) + 1
-.calculate_damage
+.HandleAttackDamageScore
ld a, [wSelectedAttack]
call EstimateDamage_VersusDefendingCard
ld a, [wDamage]
@@ -2571,40 +2543,40 @@ AIDecideBenchPokemonToSwitchTo: ; 15b72 (5:5b72)
cp MYSTERIOUS_FOSSIL
jr z, .lower_score_2
cp CLEFAIRY_DOLL
- jr nz, .asm_15d0c
+ jr nz, .ai_score_bonus
.lower_score_2
ld a, 10
call SubFromAIScore
-.asm_15d0c
+.ai_score_bonus
ld b, a
- ld a, [$cdb1]
+ ld a, [wAICardListRetreatBonus + 1]
or a
jr z, .store_score
ld h, a
- ld a, [$cdb0]
+ ld a, [wAICardListRetreatBonus]
ld l, a
-.loop
+.loop_ids
ld a, [hli]
or a
- jr z, .store_score
+ jr z, .store_score ; list is over
cp b
- jr nz, .asm_15d32
+ jr nz, .next_id
ld a, [hl]
cp $80
- jr c, .asm_15d2b
+ jr c, .subtract_score
sub $80
call AddToAIScore
- jr .asm_15d32
-.asm_15d2b
+ jr .next_id
+.subtract_score
ld c, a
ld a, $80
sub c
call SubFromAIScore
-.asm_15d32
+.next_id
inc hl
- jr .loop
+ jr .loop_ids
.store_score
ldh a, [hTempPlayAreaLocation_ff9d]
@@ -2626,9 +2598,13 @@ AIDecideBenchPokemonToSwitchTo: ; 15b72 (5:5b72)
; 0x15d4f
; handles AI action of retreating Arena Pokémon
-; and chooses which energy cards to discard
-; if card can't discard, return carry
-AIChooseEnergyToDiscardForRetreatCost: ; 15d4f (5:5d4f)
+; and chooses which energy cards to discard.
+; if card can't discard, return carry.
+; in case it's Clefairy Doll or Mysterious Fossil,
+; handle its effect to discard itself instead of retreating.
+; input:
+; - a = Play Area location (PLAY_AREA_*) of card to retreat to.
+AITryToRetreat: ; 15d4f (5:5d4f)
push af
ld a, [wAIPlayEnergyCardForRetreat]
or a
@@ -2859,7 +2835,7 @@ AIChooseEnergyToDiscardForRetreatCost: ; 15d4f (5:5d4f)
ld a, OPPACTION_USE_PKMN_POWER
bank1call AIMakeDecision
pop af
- ldh [hTempPlayAreaLocation_ffa1], a
+ ldh [hAIPkmnPowerEffectParam], a
ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT
bank1call AIMakeDecision
ld a, OPPACTION_DUEL_MAIN_SCENE
@@ -3692,25 +3668,25 @@ LookForEnergyNeededForMoveInHand: ; 16311 (5:6311)
; 0x1633f
; goes through $00 terminated list pointed
-; by wcdae and compares it to each card in hand.
+; by wAICardListPlayFromHandPriority and compares it to each card in hand.
; Sorts the hand in wDuelTempList so that the found card IDs
; are in the same order as the list pointed by de.
SortTempHandByIDList: ; 1633f (5:633f)
- ld a, [wcdae+1]
+ ld a, [wAICardListPlayFromHandPriority+1]
or a
- ret z
+ ret z ; return if list is empty
; start going down the ID list
ld d, a
- ld a, [wcdae]
+ ld a, [wAICardListPlayFromHandPriority]
ld e, a
ld c, 0
-.next_list_id
+.loop_list_id
; get this item's ID
; if $00, list has ended
ld a, [de]
or a
- ret z
+ ret z ; return when list is over
inc de
ld hl, wDuelTempList
ld b, 0
@@ -3722,7 +3698,7 @@ SortTempHandByIDList: ; 1633f (5:633f)
ld a, [hl]
ldh [hTempCardIndex_ff98], a
cp -1
- jr z, .next_list_id
+ jr z, .loop_list_id
push bc
push de
call GetCardIDFromDeckIndex
@@ -4006,47 +3982,58 @@ CheckForEvolutionInDeck: ; 16451 (5:6451)
Func_16488 ; 16488 (5:6488)
INCROM $16488, $164a1
-; copies wPlayAreaAIScore to wTempPlayAreaAIScore.
-; copies AIScore to wcde3.
-; decides which card to get energy card.
-Func_164a1: ; 164a1 (5:64a1)
- ld a, $03
- ld [wcdd8], a
+; have AI choose an energy card to play, but do not play it.
+; does not consider whether the cards have evolutions to be played.
+; return carry if an energy card is chosen to use in any Play Area card,
+; and if so, return its Play Area location in hTempPlayAreaLocation_ff9d.
+AIProcessButDontPlayEnergy_SkipEvolution: ; 164a1 (5:64a1)
+ ld a, AI_ENERGY_FLAG_DONT_PLAY | AI_ENERGY_FLAG_SKIP_EVOLUTION
+ ld [wAIEnergyAttachLogicFlags], a
+
+; backup wPlayAreaAIScore in wTempPlayAreaAIScore.
ld de, wTempPlayAreaAIScore
ld hl, wPlayAreaAIScore
ld b, MAX_PLAY_AREA_POKEMON
-.loop_play_area
+.loop
ld a, [hli]
ld [de], a
inc de
dec b
- jr nz, .loop_play_area
+ jr nz, .loop
ld a, [wAIScore]
ld [de], a
- jr AIDecideWhichCardToAttachEnergy
-Func_164ba: ; 164ba (5:64ba)
- ld a, $83
- ld [wcdd8], a
+ jr AIProcessEnergyCards
+
+; have AI choose an energy card to play, but do not play it.
+; does not consider whether the cards have evolutions to be played.
+; return carry if an energy card is chosen to use in any Bench card,
+; and if so, return its Play Area location in hTempPlayAreaLocation_ff9d.
+AIProcessButDontPlayEnergy_SkipEvolutionAndArena: ; 164ba (5:64ba)
+ ld a, AI_ENERGY_FLAG_DONT_PLAY | AI_ENERGY_FLAG_SKIP_EVOLUTION | AI_ENERGY_FLAG_SKIP_ARENA_CARD
+ ld [wAIEnergyAttachLogicFlags], a
+
+; backup wPlayAreaAIScore in wTempPlayAreaAIScore.
ld de, wTempPlayAreaAIScore
ld hl, wPlayAreaAIScore
ld b, MAX_PLAY_AREA_POKEMON
-.asm_164c7
+.loop
ld a, [hli]
ld [de], a
inc de
dec b
- jr nz, .asm_164c7
+ jr nz, .loop
ld a, [wAIScore]
ld [de], a
- jr AIDecideWhichCardToAttachEnergy
+
+ jr AIProcessEnergyCards
; copies wTempPlayAreaAIScore to wPlayAreaAIScore
-; and loads wAIscore with value in wcde3.
-; identical to Func_169e3.
-Func_164d3: ; 164d3 (5:64d3)
+; and loads wAIscore with value in wTempAIScore.
+; identical to RetrievePlayAreaAIScoreFromBackup2.
+RetrievePlayAreaAIScoreFromBackup1: ; 164d3 (5:64d3)
push af
ld de, wPlayAreaAIScore
ld hl, wTempPlayAreaAIScore
@@ -4065,34 +4052,37 @@ Func_164d3: ; 164d3 (5:64d3)
; have AI decide whether to play energy card from hand
; and determine which card is best to attach it.
-AIDecidePlayEnergyCardFromHand: ; 164e8 (5:64e8)
+AIProcessAndTryToPlayEnergy: ; 164e8 (5:64e8)
xor a
- ld [wcdd8], a
+ ld [wAIEnergyAttachLogicFlags], a
call CreateEnergyCardListFromHand
- jr nc, AIDecideWhichCardToAttachEnergy
+ jr nc, AIProcessEnergyCards
; no energy
- ld a, [wcdd8]
+ ld a, [wAIEnergyAttachLogicFlags]
or a
jr z, .exit
- jp Func_164d3
+ jp RetrievePlayAreaAIScoreFromBackup1
.exit
or a
ret
; have AI decide whether to play energy card
; and determine which card is best to attach it.
-AIDecideWhichCardToAttachEnergy: ; 164fc (5:64fc)
+AIProcessEnergyCards: ; 164fc (5:64fc)
+; initialize Play Area AI score
ld a, $80
ld b, MAX_PLAY_AREA_POKEMON
- ld hl, wcde4
+ ld hl, wPlayAreaEnergyAIScore
.loop
ld [hli], a
dec b
jr nz, .loop
+; Legendary Articuno Deck has its own energy card logic
call HandleLegendaryArticunoEnergyScoring
+; start the main Play Area loop
ld b, PLAY_AREA_ARENA
ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
call GetTurnDuelistVariable
@@ -4106,8 +4096,8 @@ AIDecideWhichCardToAttachEnergy: ; 164fc (5:64fc)
ld [wAIScore], a
ld a, $ff
ld [wTempAI], a
- ld a, [wcdd8]
- and $02
+ ld a, [wAIEnergyAttachLogicFlags]
+ and AI_ENERGY_FLAG_SKIP_EVOLUTION
jr nz, .check_venusaur
; check if energy needed is found in hand
@@ -4125,7 +4115,7 @@ AIDecideWhichCardToAttachEnergy: ; 164fc (5:64fc)
ld a, [wCurCardCanAttack]
call CheckForEvolutionInList
jr nc, .no_evolution_in_hand
- ld [wTempAI], a
+ ld [wTempAI], a ; store evolution card found
ld a, 2
call AddToAIScore
jr .check_venusaur
@@ -4137,8 +4127,9 @@ AIDecideWhichCardToAttachEnergy: ; 164fc (5:64fc)
ld a, 1
call AddToAIScore
-; if there's no Muk in Play Area
-; and there's Venusaur2, add to AI score
+; if there's no Muk in any Play Area
+; and there's Venusaur2 in own Play Area,
+; add to AI score
.check_venusaur
ld a, MUK
call CountPokemonIDInBothPlayAreas
@@ -4155,29 +4146,32 @@ AIDecideWhichCardToAttachEnergy: ; 164fc (5:64fc)
jr nz, .bench
; arena
- ld a, [wcda7]
+ ld a, [wAIBarrierFlagCounter]
bit 7, a
- jr z, .check_arena_hp
+ jr z, .add_to_score
+
+; subtract from score instead
+; if Player is running Mewtwo1 mill deck.
ld a, 5
call SubFromAIScore
jr .check_defending_can_ko
-; lower AI score if poison/double poison
-; will KO Pokémon between turns
-; or if the defending Pokémon can KO
-.check_arena_hp
+.add_to_score
ld a, 4
call AddToAIScore
+; lower AI score if poison/double poison
+; will KO Pokémon between turns
+; or if the defending Pokémon can KO
ld a, DUELVARS_ARENA_CARD_HP
call GetTurnDuelistVariable
call CalculateByteTensDigit
cp 3
jr nc, .check_defending_can_ko
-; hp < 30
+ ; hp < 30
cp 2
jr z, .has_20_hp
-; hp = 10
+ ; hp = 10
ld a, DUELVARS_ARENA_CARD_STATUS
call GetTurnDuelistVariable
and POISONED
@@ -4194,7 +4188,7 @@ AIDecideWhichCardToAttachEnergy: ; 164fc (5:64fc)
jr .check_bench
.check_defending_can_ko
call CheckIfDefendingPokemonCanKnockOut
- jr nc, .asm_165e1
+ jr nc, .ai_score_bonus
ld a, 10
call SubFromAIScore
@@ -4205,10 +4199,10 @@ AIDecideWhichCardToAttachEnergy: ; 164fc (5:64fc)
ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
call GetTurnDuelistVariable
dec a
- jr nz, .asm_165e1
+ jr nz, .ai_score_bonus
ld a, 6
call AddToAIScore
- jr .asm_165e1
+ jr .ai_score_bonus
; lower AI score by 3 - (bench HP)/10
; if bench HP < 30
@@ -4217,20 +4211,20 @@ AIDecideWhichCardToAttachEnergy: ; 164fc (5:64fc)
call GetTurnDuelistVariable
call CalculateByteTensDigit
cp 3
- jr nc, .asm_165e1
+ jr nc, .ai_score_bonus
; hp < 30
ld b, a
ld a, 3
sub b
call SubFromAIScore
-; check list in wcdb2
-.asm_165e1
- ld a, [wcdb3]
+; check list in wAICardListEnergyBonus
+.ai_score_bonus
+ ld a, [wAICardListEnergyBonus + 1]
or a
- jr z, .check_boss_deck
+ jr z, .check_boss_deck ; is null
ld h, a
- ld a, [wcdb2]
+ ld a, [wAICardListEnergyBonus]
ld l, a
push hl
@@ -4258,6 +4252,7 @@ AIDecideWhichCardToAttachEnergy: ; 164fc (5:64fc)
pop de
cp d
jr c, .check_id_score
+ ; already reached target number of energy cards
ld a, 10
call SubFromAIScore
jr .store_score
@@ -4288,11 +4283,12 @@ AIDecideWhichCardToAttachEnergy: ; 164fc (5:64fc)
.check_boss_deck
call CheckIfNotABossDeckID
jr c, .skip_boss_deck
+
call Func_174f2
ldh a, [hTempPlayAreaLocation_ff9d]
ld c, a
ld b, $00
- ld hl, wcde4
+ ld hl, wPlayAreaEnergyAIScore
add hl, bc
ld a, [hl]
cp $80
@@ -4336,24 +4332,24 @@ AIDecideWhichCardToAttachEnergy: ; 164fc (5:64fc)
; for each card has been calculated.
; now to determine the highest score.
call FindPlayAreaCardWithHighestAIScore
- jp nc, .asm_1668a
+ jp nc, .not_found
- ld a, [wcdd8]
+ ld a, [wAIEnergyAttachLogicFlags]
or a
jr z, .play_card
scf
- jp Func_164d3
+ jp RetrievePlayAreaAIScoreFromBackup1
.play_card
call CreateEnergyCardListFromHand
jp AITryToPlayEnergyCard
-.asm_1668a: ; 1668a (5:668a)
- ld a, [wcdd8]
+.not_found: ; 1668a (5:668a)
+ ld a, [wAIEnergyAttachLogicFlags]
or a
- jr z, .asm_16693
- jp Func_164d3
-.asm_16693
+ jr z, .no_carry
+ jp RetrievePlayAreaAIScoreFromBackup1
+.no_carry
or a
ret
; 0x16695
@@ -4562,15 +4558,17 @@ DetermineAIScoreOfMoveEnergyRequirement: ; 16695 (5:6695)
; of the card with the highest Play Area AI score, unless
; the highest score is below $85.
; if it succeeds in return a card location, set carry.
+; if AI_ENERGY_FLAG_SKIP_ARENA_CARD is set in wAIEnergyAttachLogicFlags
+; doesn't include the Arena card and there's no minimum score.
FindPlayAreaCardWithHighestAIScore: ; 167b5 (5:67b5)
- ld a, [wcdd8]
- and $80
- jr nz, .asm_167e1
+ ld a, [wAIEnergyAttachLogicFlags]
+ and AI_ENERGY_FLAG_SKIP_ARENA_CARD
+ jr nz, .only_bench
ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
call GetTurnDuelistVariable
ld b, a
- ld c, 0 ; PLAY_AREA_ARENA
+ ld c, PLAY_AREA_ARENA
ld e, c
ld d, c
ld hl, wPlayAreaAIScore
@@ -4601,7 +4599,7 @@ FindPlayAreaCardWithHighestAIScore: ; 167b5 (5:67b5)
ret
; same as above but only check bench Pokémon scores.
-.asm_167e1
+.only_bench
ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
call GetTurnDuelistVariable
dec a
@@ -4624,6 +4622,7 @@ FindPlayAreaCardWithHighestAIScore: ; 167b5 (5:67b5)
dec b
jr nz, .loop_2
+; in this case, there is no minimum threshold AI score.
ld a, d
ldh [hTempPlayAreaLocation_ff9d], a
scf
@@ -4694,16 +4693,16 @@ GetEnergyCardForDiscardOrEnergyBoostAttack: ; 1683b (5:683b)
jr z, .first_attack
; check if second attack is Zapdos2's Thunderbolt,
-; Charizard's Fire Spin or Exeggcutor's Big Eggsplosion,
+; Charizard's Fire Spin or Exeggutor's Big Eggsplosion,
; for these to be treated differently.
; for both attacks, load its energy cost.
ld a, b
cp ZAPDOS2
jr z, .zapdos2
cp CHARIZARD
- jr z, .charizard_or_exeggcutor
+ jr z, .charizard_or_exeggutor
cp EXEGGUTOR
- jr z, .charizard_or_exeggcutor
+ jr z, .charizard_or_exeggutor
ld hl, wLoadedCard2Move2EnergyCost
jr .fire
.first_attack
@@ -4758,9 +4757,9 @@ GetEnergyCardForDiscardOrEnergyBoostAttack: ; 1683b (5:683b)
or a
ret
-; Charizard's Fire Spin and Exeggcutor's Big Eggsplosion,
+; Charizard's Fire Spin and Exeggutor's Big Eggsplosion,
; return carry.
-.charizard_or_exeggcutor
+.charizard_or_exeggutor
lb bc, $00, $01
scf
ret
@@ -5019,11 +5018,13 @@ CheckSpecificDecksToAttachDoubleColorless: ; 1696e (5:696e)
ret
; 0x169ca
-Func_169ca: ; 169ca (5:69ca)
+; have AI choose an attack to use, but do not execute it.
+; return carry if an attack is chosen.
+AIProcessButDontUseAttack: ; 169ca (5:69ca)
ld a, $01
- ld [wcdd9], a
+ ld [wAIExecuteProcessedAttack], a
-; copy wPlayAreaAIScore to wTempPlayAreaAIScore.
+; backup wPlayAreaAIScore in wTempPlayAreaAIScore.
ld de, wTempPlayAreaAIScore
ld hl, wPlayAreaAIScore
ld b, MAX_PLAY_AREA_POKEMON
@@ -5034,15 +5035,15 @@ Func_169ca: ; 169ca (5:69ca)
dec b
jr nz, .loop
-; copies wAIScore to wcde3
+; copies wAIScore to wTempAIScore
ld a, [wAIScore]
ld [de], a
- jr Func_169fc
+ jr AIProcessAttacks
; copies wTempPlayAreaAIScore to wPlayAreaAIScore
-; and loads wAIscore with value in wcde3.
-; identical to Func_164d3.
-Func_169e3: ; 169e3 (5:69e3)
+; and loads wAIscore with value in wTempAIScore.
+; identical to RetrievePlayAreaAIScoreFromBackup1.
+RetrievePlayAreaAIScoreFromBackup2: ; 169e3 (5:69e3)
push af
ld de, wPlayAreaAIScore
ld hl, wTempPlayAreaAIScore
@@ -5060,12 +5061,18 @@ Func_169e3: ; 169e3 (5:69e3)
ret
; 0x169f8
-Func_169f8: ; 169f8 (5:69f8)
+; have AI choose and execute an attack.
+; return carry if an attack was chosen and attempted.
+AIProcessAndTryToUseAttack: ; 169f8 (5:69f8)
xor a
- ld [wcdd9], a
+ ld [wAIExecuteProcessedAttack], a
; fallthrough
-Func_169fc: ; 169fc (5:69fc)
+; checks which of the Active card's attacks for AI to use.
+; If any of the attacks has enough AI score to be used,
+; AI will use it if wAIExecuteProcessedAttack is 0.
+; in either case, return carry if an attack is chosen to be used.
+AIProcessAttacks: ; 169fc (5:69fc)
; if AI used Pluspower, load its attack index
ld a, [wPreviousAIFlags]
and AI_FLAG_USED_PLUSPOWER
@@ -5075,25 +5082,27 @@ Func_169fc: ; 169fc (5:69fc)
jr .attack_chosen
.no_pluspower
- ld a, [wcda7]
- cp $80
- jp z, .asm_16a77
+; if Player is running Mewtwo1 mill deck,
+; skip attack if Barrier counter is 0.
+ ld a, [wAIBarrierFlagCounter]
+ cp AI_FLAG_MEWTWO_MILL + 0
+ jp z, .dont_attack
; determine AI score of both attacks.
xor a ; FIRST_ATTACK_OR_PKMN_POWER
call GetAIScoreOfAttack
ld a, [wAIScore]
- ld [wPlayAreaAIScore], a
+ ld [wFirstAttackAIScore], a
ld a, SECOND_ATTACK
call GetAIScoreOfAttack
; compare both attack scores
ld c, SECOND_ATTACK
- ld a, [wPlayAreaAIScore]
+ ld a, [wFirstAttackAIScore]
ld b, a
ld a, [wAIScore]
cp b
- jr nc, .asm_16a30
+ jr nc, .check_score
; first attack has higher score
dec c
ld a, b
@@ -5103,9 +5112,9 @@ Func_169fc: ; 169fc (5:69fc)
; first check if chosen attack has at least minimum score.
; then check if first attack is better than second attack
; in case the second one was chosen.
-.asm_16a30
+.check_score
cp $50 ; minimum score to use attack
- jr c, .asm_16a77
+ jr c, .dont_attack
; enough score, proceed
ld a, c
@@ -5115,14 +5124,18 @@ Func_169fc: ; 169fc (5:69fc)
call CheckWhetherToSwitchToFirstAttack
.attack_chosen
- ld a, [wcdd9]
+; check whether to execute the attack chosen
+ ld a, [wAIExecuteProcessedAttack]
or a
- jr z, .asm_16a48
+ jr z, .execute
+
+; set carry and reset Play Area AI score
+; to the previous values.
scf
- jp Func_169e3
+ jp RetrievePlayAreaAIScoreFromBackup2
-.asm_16a48
- ld a, $0e
+.execute
+ ld a, AI_TRAINER_CARD_PHASE_14
call AIProcessHandTrainerCards
; load this attack's damage output against
@@ -5134,39 +5147,52 @@ Func_169fc: ; 169fc (5:69fc)
ld a, [wDamage]
or a
- jr z, .asm_16a62
- ; if damage is 0, fallthrough
+ jr z, .check_damage_bench
+ ; if damage is not 0, fallthrough
-.cannot_damage
+.can_damage
xor a
ld [wcdb4], a
- jr .asm_16a6d
+ jr .use_attack
-.asm_16a62
+.check_damage_bench
+; check if it can otherwise damage player's bench
ld a, MOVE_FLAG1_ADDRESS | DAMAGE_TO_OPPONENT_BENCH_F
call CheckLoadedMoveFlag
- jr c, .cannot_damage
+ jr c, .can_damage
+
+; cannot damage either Defending Pokemon or Bench
ld hl, wcdb4
inc [hl]
-.asm_16a6d
+
+; return carry if attack is chosen
+; and AI tries to use it.
+.use_attack
ld a, $01
ld [wcddb], a
- call Func_14145
+ call AITryUseAttack
scf
ret
-.asm_16a77
- ld a, [wcdd9]
+
+.dont_attack
+ ld a, [wAIExecuteProcessedAttack]
or a
- jr z, .asm_16a80
- jp Func_169e3
-.asm_16a80
+ jr z, .failed_to_use
+
+; reset Play Area AI score
+; to the previous values.
+ jp RetrievePlayAreaAIScoreFromBackup2
+
+; return no carry if no viable attack.
+.failed_to_use
ld hl, wcdb4
inc [hl]
or a
ret
; 0x16a86
-; determines the AI score of attack index in a.
+; determines the AI score of attack index in a
+; of card in Play Area location hTempPlayAreaLocation_ff9d.
GetAIScoreOfAttack: ; 16a86 (5:6a86)
; initialize AI score.
ld [wSelectedAttack], a
@@ -5737,7 +5763,7 @@ HandleSpecialAIMoves: ; 16dcd (5:6dcd)
cp BELLSPROUT
jr z, HandleCallForFamily
cp EXEGGUTOR
- jp z, HandleExeggcutorTeleport
+ jp z, HandleExeggutorTeleport
cp SCYTHER
jp z, HandleSwordsDanceAndFocusEnergy
cp KRABBY
@@ -5864,7 +5890,7 @@ HandleJigglypuff2FriendshipSong: ; 16ead (5:6ead)
ret
; if AI decides to retreat, return a score of $80 + 10.
-HandleExeggcutorTeleport: ; 16ec2 (5:6ec2)
+HandleExeggutorTeleport: ; 16ec2 (5:6ec2)
call AIDecideWhetherToRetreat
jp nc, HandleSpecialAIMoves.zero
ld a, $8a
@@ -5942,14 +5968,14 @@ HandlePorygonConversion: ; 16f18 (5:6f18)
jr nz, .conversion_2
; conversion 1
- call CheckIfBenchCardsAreAtHalfHPCanEvolveAndUseSecondMove
+ call CountNumberOfSetUpBenchPokemon
cp 2
jr c, .low_score
ld a, $82
ret
.conversion_2
- call CheckIfBenchCardsAreAtHalfHPCanEvolveAndUseSecondMove
+ call CountNumberOfSetUpBenchPokemon
cp 2
jr nc, .low_score
ld a, $82
@@ -6097,7 +6123,7 @@ HandleElectrode1EnergySpike: ; 16ff2 (5:6ff2)
ld e, LIGHTNING_ENERGY
call CheckIfAnyCardIDinLocation
jp nc, HandleSpecialAIMoves.zero
- call Func_164a1
+ call AIProcessButDontPlayEnergy_SkipEvolution
jp nc, HandleSpecialAIMoves.zero
ld a, $83
ret
@@ -6125,7 +6151,7 @@ HandleHyperBeam: ; 17005 (5:7005)
CheckWhetherToSwitchToFirstAttack: ; 17019 (5:7019)
; this checks whether the first attack is also viable
; (has more than minimum score to be used)
- ld a, [wPlayAreaAIScore]
+ ld a, [wFirstAttackAIScore]
cp $50
jr c, .keep_second_attack
@@ -6209,7 +6235,7 @@ CheckIfAnyBasicPokemonInDeck: ; 17057 (5:7057)
; return carry and that card's Play Area location in a.
; output:
; a = card location of Pokémon card, if found;
-; cerry set if such a card is found.
+; carry set if such a card is found.
LookForCardThatIsKnockedOutOnDevolution: ; 17080 (5:7080)
ldh a, [hTempPlayAreaLocation_ff9d]
push af
@@ -6307,15 +6333,16 @@ CheckIfArenaCardIsAtHalfHPCanEvolveAndUseSecondMove: ; 170c9 (5:70c9)
ret
; 0x17101
-; returns carry if at least one Pokémon in bench
-; meets the following conditions:
-; - card HP >= half max HP
+; count Pokemon in the Bench that
+; meet the following conditions:
+; - card HP > half max HP
; - card Unknown2's 4 bit is not set or
; is set but there's no evolution of card in hand/deck
; - card can use second move
-; Also outputs the number of Pokémon in bench
+; Outputs the number of Pokémon in bench
; that meet these requirements in a
-CheckIfBenchCardsAreAtHalfHPCanEvolveAndUseSecondMove: ; 17101 (5:7101)
+; and returns carry if at least one is found
+CountNumberOfSetUpBenchPokemon: ; 17101 (5:7101)
ldh a, [hTempPlayAreaLocation_ff9d]
ld d, a
ld a, [wSelectedAttack]
@@ -6333,17 +6360,24 @@ CheckIfBenchCardsAreAtHalfHPCanEvolveAndUseSecondMove: ; 17101 (5:7101)
push hl
cp $ff
jr z, .done
+
ld d, a
push de
push bc
call LoadCardDataToBuffer1_FromDeckIndex
pop bc
+
+; compares card's current HP with max HP
ld a, c
add DUELVARS_ARENA_CARD_HP
call GetTurnDuelistVariable
ld d, a
ld a, [wLoadedCard1HP]
rrca
+
+; a = max HP / 2
+; d = current HP
+; jumps if (current HP) <= (max HP / 2)
cp d
pop de
jr nc, .next
@@ -6386,8 +6420,138 @@ CheckIfBenchCardsAreAtHalfHPCanEvolveAndUseSecondMove: ; 17101 (5:7101)
ret
; 0x17161
-Func_17161 ; 17161 (5:7161)
- INCROM $17161, $171fb
+; handles AI logic to determine some selections regarding certain attacks,
+; if any of these attacks were chosen to be used.
+; returns carry if selection was successful,
+; and no carry if unable to make one.
+; outputs in hTempPlayAreaLocation_ffa1 the chosen parameter.
+AISelectSpecialAttackParameters: ; 17161 (5:7161)
+ ld a, [wSelectedAttack]
+ push af
+ call .SelectAttackParameters
+ pop bc
+ ld a, b
+ ld [wSelectedAttack], a
+ ret
+; 0x1716e
+
+.SelectAttackParameters: ; 1716e (5:716e)
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ cp MEW3
+ jr z, .devolution_beam
+ cp MEWTWO3
+ jr z, .energy_absorption
+ cp MEWTWO2
+ jr z, .energy_absorption
+ cp EXEGGUTOR
+ jr z, .teleport
+ cp ELECTRODE1
+ jr z, .energy_spike
+ ; fallthrough
+
+.no_carry
+ or a
+ ret
+
+.devolution_beam
+; in case selected attack is Devolution Beam
+; store in hTempPlayAreaLocation_ffa1
+; the location of card to select to devolve
+ ld a, [wSelectedAttack]
+ or a
+ jp z, .no_carry ; can be jr
+
+ ld a, $01
+ ldh [hTemp_ffa0], a
+ call LookForCardThatIsKnockedOutOnDevolution
+ ldh [hTempPlayAreaLocation_ffa1], a
+
+.set_carry_1
+ scf
+ ret
+
+.energy_absorption
+; in case selected attack is Energy Absorption
+; make list from energy cards in Discard Pile
+ ld a, [wSelectedAttack]
+ or a
+ jp nz, .no_carry ; can be jr
+
+ ld a, $ff
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ldh [hTempRetreatCostCards], a
+
+; search for Psychic energy cards in Discard Pile
+ ld e, PSYCHIC_ENERGY
+ ld a, CARD_LOCATION_DISCARD_PILE
+ call CheckIfAnyCardIDinLocation
+ ldh [hTemp_ffa0], a
+ farcall CreateEnergyCardListFromOpponentDiscardPile
+
+; find any energy card different from
+; the one found by CheckIfAnyCardIDinLocation.
+; since using this move requires a Psychic energy card,
+; and another one is in hTemp_ffa0,
+; then any other energy card would account
+; for the Energy Cost of Psyburn.
+ ld hl, wDuelTempList
+.loop_energy_cards
+ ld a, [hli]
+ cp $ff
+ jr z, .set_carry_2
+ ld b, a
+ ldh a, [hTemp_ffa0]
+ cp b
+ jr z, .loop_energy_cards ; same card, keep looking
+
+; store the deck index of energy card found
+ ld a, b
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ; fallthrough
+
+.set_carry_2
+ scf
+ ret
+
+.teleport
+; in case selected attack is Teleport
+; decide Bench card to switch to.
+ ld a, [wSelectedAttack]
+ or a
+ jp nz, .no_carry ; can be jr
+ call AIDecideBenchPokemonToSwitchTo
+ jr c, .no_carry
+ ldh [hTemp_ffa0], a
+ scf
+ ret
+
+.energy_spike
+; in case selected attack is Energy Spike
+; decide basic energy card to fetch from Deck.
+ ld a, [wSelectedAttack]
+ or a
+ jp z, .no_carry ; can be jr
+
+ ld a, CARD_LOCATION_DECK
+ ld e, LIGHTNING_ENERGY
+
+; if none were found in Deck, return carry...
+ call CheckIfAnyCardIDinLocation
+ ldh [hTemp_ffa0], a
+ jp nc, .no_carry ; can be jr
+
+; ...else find a suitable Play Area Pokemon to
+; attach the energy card to.
+ call AIProcessButDontPlayEnergy_SkipEvolution
+ jp nc, .no_carry ; can be jr
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ scf
+ ret
+; 0x171fb
; return carry if Pokémon at play area location
; in hTempPlayAreaLocation_ff9d does not have
@@ -6547,8 +6711,175 @@ CheckCardEvolutionInHandOrDeck: ; 17274 (5:7274)
ret
; 0x172af
-Func_172af ; 172af (5:72af)
- INCROM $172af, $17383
+; sets up the inital hand of boss deck.
+; always draws at least 2 Basic Pokemon cards and 2 Energy cards.
+; also sets up so that the next cards to be drawn have
+; some minimum number of Basic Pokemon and Energy cards.
+SetUpBossStartingHandAndDeck: ; 172af (5:72af)
+; shuffle all hand cards in deck
+ ld a, DUELVARS_HAND
+ call GetTurnDuelistVariable
+ ld b, STARTING_HAND_SIZE
+.loop_hand
+ ld a, [hl]
+ call RemoveCardFromHand
+ call ReturnCardToDeck
+ dec b
+ jr nz, .loop_hand
+ jr .count_energy_basic
+
+.shuffle_deck
+ call ShuffleDeck
+
+; count number of Energy and basic Pokemon cards
+; in the first STARTING_HAND_SIZE in deck.
+.count_energy_basic
+ xor a
+ ld [wce06], a
+ ld [wce08], a
+
+ ld a, DUELVARS_DECK_CARDS
+ call GetTurnDuelistVariable
+ ld b, STARTING_HAND_SIZE
+.loop_deck_1
+ ld a, [hli]
+ push bc
+ call LoadCardDataToBuffer1_FromDeckIndex
+ pop bc
+ ld a, [wLoadedCard1Type]
+ cp TYPE_ENERGY
+ jr c, .pokemon_card_1
+ cp TYPE_TRAINER
+ jr z, .next_card_deck_1
+
+; energy card
+ ld a, [wce08]
+ inc a
+ ld [wce08], a
+ jr .next_card_deck_1
+
+.pokemon_card_1
+ ld a, [wLoadedCard1Stage]
+ or a
+ jr nz, .next_card_deck_1 ; not basic
+ ld a, [wce06]
+ inc a
+ ld [wce06], a
+
+.next_card_deck_1
+ dec b
+ jr nz, .loop_deck_1
+
+; tally the number of Energy and basic Pokemon cards
+; and if any of them is smaller than 2, re-shuffle deck.
+ ld a, [wce06]
+ cp 2
+ jr c, .shuffle_deck
+ ld a, [wce08]
+ cp 2
+ jr c, .shuffle_deck
+
+; now check the following 6 cards (prize cards).
+; re-shuffle deck if any of these cards is listed in wAICardListAvoidPrize.
+ ld b, 6
+.check_card_ids
+ ld a, [hli]
+ push bc
+ call .CheckIfIDIsInList
+ pop bc
+ jr c, .shuffle_deck
+ dec b
+ jr nz, .check_card_ids
+
+; finally, check 6 cards after that.
+; if Energy or Basic Pokemon counter is below 4
+; (counting with the ones found in the initial hand)
+; then re-shuffle deck.
+ ld b, 6
+.loop_deck_2
+ ld a, [hli]
+ push bc
+ call LoadCardDataToBuffer1_FromDeckIndex
+ pop bc
+ ld a, [wLoadedCard1Type]
+ cp TYPE_ENERGY
+ jr c, .pokemon_card_2
+ cp TYPE_TRAINER
+ jr z, .next_card_deck_2
+
+; energy card
+ ld a, [wce08]
+ inc a
+ ld [wce08], a
+ jr .next_card_deck_2
+
+.pokemon_card_2
+ ld a, [wLoadedCard1Stage]
+ or a
+ jr nz, .next_card_deck_2
+ ld a, [wce06]
+ inc a
+ ld [wce06], a
+
+.next_card_deck_2
+ dec b
+ jr nz, .loop_deck_2
+
+ ld a, [wce06]
+ cp 4
+ jp c, .shuffle_deck
+ ld a, [wce08]
+ cp 4
+ jp c, .shuffle_deck
+
+; draw new set of hand cards
+ ld a, DUELVARS_DECK_CARDS
+ call GetTurnDuelistVariable
+ ld b, STARTING_HAND_SIZE
+.draw_loop
+ ld a, [hli]
+ call SearchCardInDeckAndAddToHand
+ call AddCardToHand
+ dec b
+ jr nz, .draw_loop
+ ret
+; 0x17366
+
+; expectation: return carry if card ID corresponding
+; to the input deck index is listed in wAICardListAvoidPrize;
+; reality: always returns no carry because when checking terminating
+; byte in wAICardListAvoidPrize ($00), it wrongfully uses 'cp a' instead of 'or a',
+; so it always ends up returning in the first item in list.
+; input:
+; - a = deck index of card to check
+.CheckIfIDIsInList ; 17366 (5:7366)
+ ld b, a
+ ld a, [wAICardListAvoidPrize + 1]
+ or a
+ ret z ; null
+ push hl
+ ld h, a
+ ld a, [wAICardListAvoidPrize]
+ ld l, a
+
+ ld a, b
+ call GetCardIDFromDeckIndex
+.loop_id_list
+ ld a, [hli]
+ cp a ; bug, should be 'or a'
+ jr z, .false
+ cp e
+ jr nz, .loop_id_list
+
+; true
+ pop hl
+ scf
+ ret
+.false
+ pop hl
+ or a
+ ret
+; 0x17383
; returns carry if Pokemon at PLAY_AREA* in a
; can damage defending Pokémon with any of its moves
@@ -6591,7 +6922,7 @@ CheckIfCanDamageDefendingPokemon: ; 17383 (5:7383)
; sets carry if any on the moves knocks out
; also outputs the largest damage dealt in a
; input:
-; [hTempPlayAreaLocation_ff9d] = locaion of card to check
+; [hTempPlayAreaLocation_ff9d] = location of card to check
; output:
; a = largest damage of both moves
; carry set if can knock out
@@ -6713,8 +7044,8 @@ CheckIfNotABossDeckID: ; 17426 (5:7426)
; - 25% for all other decks;
; - 0% for boss decks.
; used for certain decks to randomly choose
-; not to play Trainer card in hand.
-ChooseRandomlyNotToPlayTrainerCard: ; 1743b (5:743b)
+; not to play Trainer card or use PKMN Power
+AIChooseRandomlyNotToDoAction: ; 1743b (5:743b)
; boss decks always use Trainer cards.
push hl
push de
@@ -6825,7 +7156,7 @@ CheckForBenchIDAtHalfHPAndCanUseSecondMove: ; 17474 (5:7474)
ret
; 0x174cd
-; add 5 to wcde4 AI score corresponding to all cards
+; add 5 to wPlayAreaEnergyAIScore AI score corresponding to all cards
; in bench that have same ID as register a
; input:
; a = card ID to look for
@@ -6848,7 +7179,7 @@ RaiseAIScoreToAllMatchingIDsInBench: ; 174cd (5:74cd)
ld c, e
ld b, $00
push hl
- ld hl, wcde4
+ ld hl, wPlayAreaEnergyAIScore
add hl, bc
ld a, 5
add [hl]
@@ -6860,8 +7191,8 @@ RaiseAIScoreToAllMatchingIDsInBench: ; 174cd (5:74cd)
; goes through each play area Pokémon, and
; for all cards of the same ID, determine which
; card has highest value calculated from Func_17583
-; the card with highest value gets increased wcde4
-; while all others get decreased wcde4
+; the card with highest value gets increased wPlayAreaEnergyAIScore
+; while all others get decreased wPlayAreaEnergyAIScore
Func_174f2: ; 174f2 (5:74f2)
ld a, MAX_PLAY_AREA_POKEMON
ld hl, wcdfa
@@ -6880,6 +7211,7 @@ Func_174f2: ; 174f2 (5:74f2)
ld a, [hli]
cp $ff
ret z
+
ld [wcdf9], a
push de
push hl
@@ -6945,11 +7277,11 @@ Func_174f2: ; 174f2 (5:74f2)
jr .loop_2
; c = play area location of highest score
-; decrease wcde4 score for all cards with same ID
+; decrease wPlayAreaEnergyAIScore score for all cards with same ID
; except for the one with highest score
-; increase wcde4 score for card with highest ID
+; increase wPlayAreaEnergyAIScore score for card with highest ID
.asm_17560
- ld hl, wcde4
+ ld hl, wPlayAreaEnergyAIScore
ld de, wcdea
ld b, PLAY_AREA_ARENA
.loop_3
diff --git a/src/engine/bank08.asm b/src/engine/bank08.asm
index 5a0f535..1bb06a3 100644
--- a/src/engine/bank08.asm
+++ b/src/engine/bank08.asm
@@ -114,7 +114,7 @@ _AIProcessHandTrainerCards: ; 200e5 (8:40e5)
jp c, .next_in_data
; AI can randomly choose not to play card.
- farcall ChooseRandomlyNotToPlayTrainerCard
+ farcall AIChooseRandomlyNotToDoAction
jr c, .next_in_data
; call routine to decide whether to play Trainer card
@@ -3589,9 +3589,9 @@ AIPlay_Pokedex: ; 212b4 (8:52b4)
; 0x212dc
AIDecide_Pokedex: ; 212dc (8:52dc)
- ld a, [wcda6]
- cp $06
- jr c, .no_carry
+ ld a, [wAIPokedexCounter]
+ cp 5 + 1
+ jr c, .no_carry ; return if counter hasn't reached 6 yet
; return no carry if number of cards in deck <= 4
ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
@@ -3624,7 +3624,7 @@ AIDecide_Pokedex: ; 212dc (8:52dc)
PickPokedexCards_Unreferenced: ; 212ff (8:52ff)
; unreferenced
xor a
- ld [wcda6], a
+ ld [wAIPokedexCounter], a ; reset counter
ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
call GetTurnDuelistVariable
@@ -3756,7 +3756,7 @@ PickPokedexCards_Unreferenced: ; 212ff (8:52ff)
; stores the resulting order in wce1a.
PickPokedexCards: ; 2138e (8:538e)
xor a
- ld [wcda6], a
+ ld [wAIPokedexCounter], a ; reset counter ; reset counter
ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
call GetTurnDuelistVariable
@@ -4698,19 +4698,22 @@ AIPlay_Gambler: ; 2182d (8:582d)
; 0x21875
; checks whether to play Gambler.
-; aside from Imakuni, all other opponents only
-; play if there's less than 4 cards in the deck.
+; aside from Imakuni?, all other opponents only
+; play this card if Player is running Mewtwo1-only deck.
AIDecide_Gambler: ; 21875 (8:5875)
; Imakuni? has his own routine
ld a, [wOpponentDeckID]
cp IMAKUNI_DECK_ID
jr z, .imakuni
- ld a, [wcda7]
- and $80
+; check if flag is set for Player using Mewtwo1 only deck
+ ld a, [wAIBarrierFlagCounter]
+ and AI_FLAG_MEWTWO_MILL
jr z, .no_carry
-; set carry if number of cards in deck <= 4
+; set carry if number of cards in deck <= 4.
+; this is done to counteract the deck out strategy
+; of Mewtwo1 deck, by replenishing the deck with hand cards.
ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
call GetTurnDuelistVariable
cp DECK_SIZE - 4
@@ -6219,8 +6222,1319 @@ AIDecide_PokemonTrader_Flamethrower: ; 22133 (8:6133)
ret
; 0x2219b
-Func_2219b: ; 2219b (8:219b)
- INCROM $2219b, $227f6
+; 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
+; 0x22246
+
+; 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
+; 0x22286
+
+; 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
+; 0x222a9
+
+; 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
+; 0x222ca
+
+; 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 CopyMoveDataAndDamage_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
+; 0x2237f
+
+; 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 CopyMoveDataAndDamage_FromDeckIndex
+ ld a, [wLoadedMoveCategory]
+ 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
+; 0x22402
+
+; 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 [hAIHealCard], a
+ ld a, OPPACTION_EXECUTE_PKMN_POWER_EFFECT
+ bank1call AIMakeDecision
+ ld a, OPPACTION_DUEL_MAIN_SCENE
+ bank1call AIMakeDecision
+ ret
+; 0x22422
+
+; 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 GetCardDamage
+ 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 GetCardDamage
+ ; 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 GetCardDamage
+ 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
+; 0x22476
+
+; 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 apropriate 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
+; 0x224c6
+
+; 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
+ call TranslateColorToWR
+ pop bc
+ and b
+ jr z, .loop_play_area
+; true
+ scf
+ ret
+.false
+ or a
+ ret
+; 0x224e6
+
+; 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_player_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, $ff
+ jr .use_peek
+
+.check_player_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, $40
+ 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 $80
+
+.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
+; 0x2255d
+
+; 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 GetCardDamage
+ 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
+; 0x225b5
+
+; 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 GetCardDamage
+ 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 GetCardDamage
+ 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
+; 0x2262d
+
+; 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
+; 0x22671
+
+; 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 GetCardDamage
+.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
+; 0x226a3
+
+; 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 GetCardDamage
+ 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
+; 0x2273c
+
+; 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
+; 0x22790
+
+; 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
+; 0x227a9
+
+; 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
+; 0x227d3
+
+; 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 7, a
+ jr z, .set_carry
+
+; else, check if there's been less than 2 turns
+; without the Player using Barrier.
+ cp AI_FLAG_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
+; 0x227f6
; lists in wDuelTempList all the basic energy cards
; in card location of a.
@@ -7128,7 +8442,7 @@ FindDuplicatePokemonCards: ; 22b6f (8:6b6f)
; return carry flag if move is not high recoil.
Func_22bad: ; 22bad (8:6bad)
- farcall Func_169ca
+ farcall AIProcessButDontUseAttack
ret nc
ld a, [wSelectedAttack]
ld e, a
diff --git a/src/engine/deck_ai/deck_ai.asm b/src/engine/deck_ai/deck_ai.asm
new file mode 100644
index 0000000..c8b73f3
--- /dev/null
+++ b/src/engine/deck_ai/deck_ai.asm
@@ -0,0 +1,82 @@
+; AI card retreat score bonus
+; when the AI retreat routine runs through the Bench to choose
+; a Pokemon to switch to, it looks up in this list and if
+; a card ID matches, applies a retreat score bonus to this card.
+; positive (negative) means more (less) likely to switch to this card.
+ai_retreat: MACRO
+ db \1 ; card ID
+ db $80 + \2 ; retreat score (ranges between -128 and 127)
+ENDM
+
+; AI card energy attach score bonus
+; when the AI energy attachment routine runs through the Play Area to choose
+; a Pokemon to attach an energy card, it looks up in this list and if
+; a card ID matches, skips this card if the maximum number of energy
+; cards attached has been reached. If it hasn't been reached, additionally
+; applies a positive (or negative) AI score to attach energy to this card.
+ai_energy: MACRO
+ db \1 ; card ID
+ db \2 ; maximum number of attached cards
+ db $80 + \3 ; energy score (ranges between -128 and 127)
+ENDM
+
+; stores in WRAM pointer to data in argument
+; e.g. store_list_pointer wSomeListPointer, SomeData
+store_list_pointer: MACRO
+ ld hl, \1
+ ld de, \2
+ ld [hl], e
+ inc hl
+ ld [hl], d
+ENDM
+
+; deck AIs are specialized to work on a given deck ID.
+; they decide what happens during a turn, what Pokemon cards
+; to pick during the start of the duel, etc.
+; the different scenarios these are used are listed in AIACTION_* constants.
+; each of these have a pointer table with the following structure:
+; dw .do_turn : never called;
+;
+; dw .do_turn : called to handle the main turn logic, from the beginning
+; of the turn up to the attack (or lack thereof);
+;
+; dw .start_duel : called at the start of the duel to initialize some
+; variables and optionally set up CPU hand and deck;
+;
+; dw .forced_switch : logic to determine what Pokemon to pick when there's
+; an effect that forces AI to switch to Bench card;
+;
+; dw .ko_switch : logic for picking which card to use after a KO;
+;
+; dw .take_prize : logic to decide which prize card to pick.
+
+; optionally, decks can also declare card lists that will add
+; more specialized logic during various generic AI routines,
+; and read during the .start_duel routines.
+; the pointers to these lists are stored in memory:
+; wAICardListAvoidPrize : list of cards to avoid being placed as prize;
+; wAICardListArenaPriority : priority list of Arena card at duel start;
+; wAICardListBenchPriority : priority list of Bench cards at duel start;
+; wAICardListPlayFromHandPriority : priority list of cards to play from hand;
+; wAICardListRetreatBonus : scores given to certain cards for retreat;
+; wAICardListEnergyBonus : max number of energy cards and card scores.
+
+INCLUDE "engine/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"
diff --git a/src/engine/deck_ai/decks/fire_charge.asm b/src/engine/deck_ai/decks/fire_charge.asm
new file mode 100644
index 0000000..454f731
--- /dev/null
+++ b/src/engine/deck_ai/decks/fire_charge.asm
@@ -0,0 +1,86 @@
+AIActionTable_FireCharge: ; 15232 (5:5232)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 1523e (5:523e)
+ call AIMainTurnLogic
+ ret
+; 0x15242
+
+.start_duel ; 15242 (5:5242)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+; 0x15253
+
+.forced_switch ; 15253 (5:5253)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x15257
+
+.ko_switch ; 15257 (5:5257)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x1525b
+
+.take_prize ; 1525b (5:525b)
+ call AIPickPrizeCards
+ ret
+; 0x1525f
+
+.list_arena ; 1525f (5:525f)
+ db JIGGLYPUFF3
+ db CHANSEY
+ db TAUROS
+ db MAGMAR1
+ db JIGGLYPUFF1
+ db GROWLITHE
+ db $00
+
+.list_bench ; 15266 (5:5266)
+ db JIGGLYPUFF3
+ db CHANSEY
+ db GROWLITHE
+ db MAGMAR1
+ db JIGGLYPUFF1
+ db TAUROS
+ db $00
+
+.list_retreat ; 1526e (5:526e)
+ ai_retreat JIGGLYPUFF1, -1
+ ai_retreat CHANSEY, -1
+ ai_retreat GROWLITHE, -1
+ db $00
+
+.list_energy ; 15274 (5:5274)
+ ai_energy GROWLITHE, 3, +0
+ ai_energy ARCANINE2, 4, +0
+ ai_energy MAGMAR1, 3, +0
+ ai_energy JIGGLYPUFF1, 3, +0
+ ai_energy JIGGLYPUFF3, 2, +0
+ ai_energy WIGGLYTUFF, 3, +0
+ ai_energy CHANSEY, 4, +0
+ ai_energy TAUROS, 3, +0
+ db $00
+
+.list_prize ; 1528d (5:528d)
+ db GAMBLER
+ db $00
+
+.store_list_pointers ; 1528f (5:528f)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
+; 0x152bd
diff --git a/src/engine/deck_ai/decks/first_strike.asm b/src/engine/deck_ai/decks/first_strike.asm
new file mode 100644
index 0000000..6d2906c
--- /dev/null
+++ b/src/engine/deck_ai/decks/first_strike.asm
@@ -0,0 +1,82 @@
+AIActionTable_FirstStrike: ; 14e89 (5:4e89)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 14e95 (5:4e95)
+ call AIMainTurnLogic
+ ret
+; 0x14e99
+
+.start_duel ; 14e99 (5:4e99)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+; 0x14eaa
+
+.forced_switch ; 14eaa (5:4eaa)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x14eae
+
+.ko_switch ; 14eae (5:4eae)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x14eb2
+
+.take_prize ; 14eb2 (5:4eb2)
+ call AIPickPrizeCards
+ ret
+; 0x14eb6
+
+.list_arena ; 14eb6 (5:1eb6)
+ db HITMONCHAN
+ db MACHOP
+ db HITMONLEE
+ db MANKEY
+ db $00
+
+.list_bench ; 14ebb (5:1ebb)
+ db MACHOP
+ db HITMONLEE
+ db HITMONCHAN
+ db MANKEY
+ db $00
+
+.list_retreat ; 14ec0 (5:1ec0)
+ ai_retreat MACHOP, -1
+ ai_retreat MACHOKE, -1
+ ai_retreat MANKEY, -2
+ db $00
+
+.list_energy ; 14ec7 (5:1ec7)
+ ai_energy MACHOP, 3, +0
+ ai_energy MACHOKE, 4, +0
+ ai_energy MACHAMP, 4, -1
+ ai_energy HITMONCHAN, 3, +0
+ ai_energy HITMONLEE, 3, +0
+ ai_energy MANKEY, 2, -1
+ ai_energy PRIMEAPE, 3, -1
+ db $00
+
+.list_prize ; 14edd (5:1edd)
+ db HITMONLEE
+ db HITMONCHAN
+ db $00
+
+.store_list_pointers ; 14ee0 (5:4ee0)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
+; 0x14f0e
diff --git a/src/engine/deck_ai/decks/flower_power.asm b/src/engine/deck_ai/decks/flower_power.asm
new file mode 100644
index 0000000..566d064
--- /dev/null
+++ b/src/engine/deck_ai/decks/flower_power.asm
@@ -0,0 +1,81 @@
+AIActionTable_FlowerPower: ; 1509b (5:509b)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 150a7 (5:50a7)
+ call AIMainTurnLogic
+ ret
+; 0x150ab
+
+.start_duel ; 150ab (5:50ab)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+; 0x150bc
+
+.forced_switch ; 150bc (5:50bc)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x150c0
+
+.ko_switch ; 150c0 (5:50c0)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x150c4
+
+.take_prize ; 150c4 (5:50c4)
+ call AIPickPrizeCards
+ ret
+; 0x150c8
+
+.list_arena ; 150c8 (5:50c8)
+ db ODDISH
+ db EXEGGCUTE
+ db BULBASAUR
+ db $00
+
+.list_bench ; 150cc (5:50cc)
+ db BULBASAUR
+ db EXEGGCUTE
+ db ODDISH
+ db $00
+
+.list_retreat ; 150cf (5:50cf)
+ ai_retreat GLOOM, -2
+ ai_retreat VILEPLUME, -2
+ ai_retreat BULBASAUR, -2
+ ai_retreat IVYSAUR, -2
+ db $00
+
+.list_energy ; 150d9 (5:50d9)
+ ai_energy BULBASAUR, 3, +0
+ ai_energy IVYSAUR, 4, +0
+ ai_energy VENUSAUR2, 4, +0
+ ai_energy ODDISH, 2, +0
+ ai_energy GLOOM, 3, -1
+ ai_energy VILEPLUME, 3, -1
+ ai_energy EXEGGCUTE, 3, +0
+ ai_energy EXEGGUTOR, 22, +0
+ db $00
+
+.list_prize ; 150f2 (5:50f2)
+ db VENUSAUR2
+ db $00
+
+.store_list_pointers ; 150f4 (5:50f4)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
+; 0x15122
diff --git a/src/engine/deck_ai/decks/general.asm b/src/engine/deck_ai/decks/general.asm
new file mode 100644
index 0000000..1bfc3e0
--- /dev/null
+++ b/src/engine/deck_ai/decks/general.asm
@@ -0,0 +1,196 @@
+; AI logic used by general decks
+AIActionTable_GeneralDecks: ; 14668 (05:4668)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 14674 (5:4674)
+ call AIMainTurnLogic
+ ret
+
+.start_duel ; 14678 (5:4678)
+ call InitAIDuelVars
+ call AIPlayInitialBasicCards
+ ret
+
+.forced_switch ; 1467f (5:467f)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.ko_switch ; 14683 (5:4683)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+
+.take_prize: ; 14687 (5:4687)
+ call AIPickPrizeCards
+ ret
+
+; handle AI routines for a whole turn
+AIMainTurnLogic: ; 1468b (5:468b)
+; initialize variables
+ call InitAITurnVars
+ ld a, AI_TRAINER_CARD_PHASE_01
+ call AIProcessHandTrainerCards
+ farcall HandleAIAntiMewtwoDeckStrategy
+ jp nc, .try_attack
+; handle Pkmn Powers
+ farcall HandleAIGoGoRainDanceEnergy
+ farcall HandleAIDamageSwap
+ farcall HandleAIPkmnPowers
+ ret c ; return if turn ended
+ farcall HandleAICowardice
+; process Trainer cards
+; phase 2 through 4.
+ ld a, AI_TRAINER_CARD_PHASE_02
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_03
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_04
+ call AIProcessHandTrainerCards
+; play Pokemon from hand
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+; process Trainer cards
+; phase 5 through 12.
+ ld a, AI_TRAINER_CARD_PHASE_05
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_06
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_07
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_08
+ call AIProcessHandTrainerCards
+ call AIProcessRetreat
+ ld a, AI_TRAINER_CARD_PHASE_10
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_11
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_12
+ call AIProcessHandTrainerCards
+; play Energy card if possible
+ ld a, [wAlreadyPlayedEnergy]
+ or a
+ jr nz, .skip_energy_attach_1
+ call AIProcessAndTryToPlayEnergy
+.skip_energy_attach_1
+; play Pokemon from hand again
+ call AIDecidePlayPokemonCard
+; handle Pkmn Powers again
+ farcall HandleAIDamageSwap
+ farcall HandleAIPkmnPowers
+ ret c ; return if turn ended
+ farcall HandleAIGoGoRainDanceEnergy
+ ld a, AI_ENERGY_TRANS_ATTACK
+ farcall HandleAIEnergyTrans
+; process Trainer cards phases 13 and 15
+ ld a, AI_TRAINER_CARD_PHASE_13
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_15
+ call AIProcessHandTrainerCards
+; if used Professor Oak, process new hand
+; if not, then proceed to attack.
+ ld a, [wPreviousAIFlags]
+ and AI_FLAG_USED_PROFESSOR_OAK
+ jr z, .try_attack
+ ld a, AI_TRAINER_CARD_PHASE_01
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_02
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_03
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_04
+ call AIProcessHandTrainerCards
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+ ld a, AI_TRAINER_CARD_PHASE_05
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_06
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_07
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_08
+ call AIProcessHandTrainerCards
+ call AIProcessRetreat
+ ld a, AI_TRAINER_CARD_PHASE_10
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_11
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_12
+ call AIProcessHandTrainerCards
+ ld a, [wAlreadyPlayedEnergy]
+ or a
+ jr nz, .skip_energy_attach_2
+ call AIProcessAndTryToPlayEnergy
+.skip_energy_attach_2
+ call AIDecidePlayPokemonCard
+ farcall HandleAIDamageSwap
+ farcall HandleAIPkmnPowers
+ ret c ; return if turn ended
+ farcall HandleAIGoGoRainDanceEnergy
+ ld a, AI_ENERGY_TRANS_ATTACK
+ farcall HandleAIEnergyTrans
+ ld a, AI_TRAINER_CARD_PHASE_13
+ call AIProcessHandTrainerCards
+ ; skip AI_TRAINER_CARD_PHASE_15
+.try_attack
+ ld a, AI_ENERGY_TRANS_TO_BENCH
+ farcall HandleAIEnergyTrans
+; attack if possible, if not,
+; finish turn without attacking.
+ call AIProcessAndTryToUseAttack
+ ret c ; return if AI attacked
+ ld a, OPPACTION_FINISH_NO_ATTACK
+ bank1call AIMakeDecision
+ ret
+; 0x14786
+
+; handles AI retreating logic
+AIProcessRetreat: ; 14786 (5:4786)
+ ld a, [wAIRetreatedThisTurn]
+ or a
+ ret nz ; return, already retreated this turn
+
+ call AIDecideWhetherToRetreat
+ ret nc ; return if not retreating
+
+ call AIDecideBenchPokemonToSwitchTo
+ ret c ; return if no Bench Pokemon
+
+; store Play Area to retreat to and
+; set wAIRetreatedThisTurn to true
+ ld [wAIPlayAreaCardToSwitch], a
+ ld a, $01
+ ld [wAIRetreatedThisTurn], a
+
+; if AI can use Switch from hand, use it instead...
+ ld a, AI_TRAINER_CARD_PHASE_09
+ call AIProcessHandTrainerCards
+ ld a, [wPreviousAIFlags]
+ and AI_FLAG_USED_SWITCH
+ jr nz, .used_switch
+; ... else try retreating normally.
+ ld a, [wAIPlayAreaCardToSwitch]
+ call AITryToRetreat
+ ret
+
+.used_switch
+; if AI used switch, unset its AI flag
+ ld a, [wPreviousAIFlags]
+ and ~AI_FLAG_USED_SWITCH ; clear Switch flag
+ ld [wPreviousAIFlags], a
+
+; bug, this doesn't make sense being here, since at this point
+; Switch Trainer card was already used to retreat the Pokemon.
+; what the routine will do is just transfer Energy cards to
+; the Arena Pokemon for the purpose of retreating, and
+; then not actually retreat, resulting in unusual behaviour.
+; this would only work placed right after the AI checks whether
+; they have Switch card in hand to use and doesn't have one.
+; (and probably that was the original intention.)
+ ld a, AI_ENERGY_TRANS_RETREAT ; retreat
+ farcall HandleAIEnergyTrans
+ ret
+; 0x147bd
diff --git a/src/engine/deck_ai/decks/general_no_retreat.asm b/src/engine/deck_ai/decks/general_no_retreat.asm
new file mode 100644
index 0000000..e735076
--- /dev/null
+++ b/src/engine/deck_ai/decks/general_no_retreat.asm
@@ -0,0 +1,146 @@
+; acts just like a general deck AI except never retreats
+AIActionTable_GeneralNoRetreat: ; 148dc (5:48dc)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 148e8 (5:48e8)
+ call AIDoTurn_GeneralNoRetreat
+ ret
+; 0x148ec
+
+.start_duel ; 148ec (5:48ec)
+ call InitAIDuelVars
+ call AIPlayInitialBasicCards
+ ret
+; 0x148f3
+
+.forced_switch ; 148f3 (5:48f3)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x148f7
+
+.ko_switch ; 148f7 (5:48f7)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x148fb
+
+.take_prize ; 148fb (5:48fb)
+ call AIPickPrizeCards
+ ret
+; 0x148ff
+
+AIDoTurn_GeneralNoRetreat: ; 148ff (5:48ff)
+; initialize variables
+ call InitAITurnVars
+ ld a, AI_TRAINER_CARD_PHASE_01
+ call AIProcessHandTrainerCards
+ farcall HandleAIAntiMewtwoDeckStrategy
+ jp nc, .try_attack
+; handle Pkmn Powers
+ farcall HandleAIGoGoRainDanceEnergy
+ farcall HandleAIDamageSwap
+ farcall HandleAIPkmnPowers
+ ret c ; return if turn ended
+ farcall HandleAICowardice
+; process Trainer cards
+; phase 2 through 4.
+ ld a, AI_TRAINER_CARD_PHASE_02
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_03
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_04
+ call AIProcessHandTrainerCards
+; play Pokemon from hand
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+; process Trainer cards
+; phase 5 through 12.
+ ld a, AI_TRAINER_CARD_PHASE_05
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_06
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_07
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_08
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_10
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_11
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_12
+ call AIProcessHandTrainerCards
+; play Energy card if possible
+ ld a, [wAlreadyPlayedEnergy]
+ or a
+ jr nz, .skip_energy_attach_1
+ call AIProcessAndTryToPlayEnergy
+.skip_energy_attach_1
+; play Pokemon from hand again
+ call AIDecidePlayPokemonCard
+; handle Pkmn Powers again
+ farcall HandleAIDamageSwap
+ farcall HandleAIPkmnPowers
+ ret c ; return if turn ended
+ farcall HandleAIGoGoRainDanceEnergy
+ ld a, AI_ENERGY_TRANS_ATTACK
+ farcall HandleAIEnergyTrans
+; process Trainer cards phases 13 and 15
+ ld a, AI_TRAINER_CARD_PHASE_13
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_15
+ call AIProcessHandTrainerCards
+; if used Professor Oak, process new hand
+; if not, then proceed to attack.
+ ld a, [wPreviousAIFlags]
+ and AI_FLAG_USED_PROFESSOR_OAK
+ jr z, .try_attack
+ ld a, AI_TRAINER_CARD_PHASE_01
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_02
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_03
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_04
+ call AIProcessHandTrainerCards
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+ ld a, AI_TRAINER_CARD_PHASE_05
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_06
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_07
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_08
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_10
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_11
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_12
+ call AIProcessHandTrainerCards
+ ld a, [wAlreadyPlayedEnergy]
+ or a
+ jr nz, .skip_energy_attach_2
+ call AIProcessAndTryToPlayEnergy
+.skip_energy_attach_2
+ call AIDecidePlayPokemonCard
+ farcall HandleAIDamageSwap
+ farcall HandleAIPkmnPowers
+ ret c ; return if turn ended
+ farcall HandleAIGoGoRainDanceEnergy
+ ld a, AI_TRAINER_CARD_PHASE_13
+ call AIProcessHandTrainerCards
+ ; skip AI_TRAINER_CARD_PHASE_15
+.try_attack
+; attack if possible, if not,
+; finish turn without attacking.
+ call AIProcessAndTryToUseAttack
+ ret c ; return if turn ended
+ ld a, OPPACTION_FINISH_NO_ATTACK
+ bank1call AIMakeDecision
+ ret
+; 0x149e8
diff --git a/src/engine/deck_ai/decks/go_go_rain_dance.asm b/src/engine/deck_ai/decks/go_go_rain_dance.asm
new file mode 100644
index 0000000..006bbdc
--- /dev/null
+++ b/src/engine/deck_ai/decks/go_go_rain_dance.asm
@@ -0,0 +1,85 @@
+AIActionTable_GoGoRainDance: ; 14f8f (5:4f8f)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 14f9b (5:4f9b)
+ call AIMainTurnLogic
+ ret
+; 0x14f9f
+
+.start_duel ; 14f9f (5:4f9f)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+; 0x14fb0
+
+.forced_switch ; 14fb0 (5:4fb0)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x14fb4
+
+.ko_switch ; 14fb4 (5:4fb4)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x14fb8
+
+.take_prize ; 14fb8 (5:4fb8)
+ call AIPickPrizeCards
+ ret
+; 0x14fbc
+
+.list_arena ; 14fbc (5:4fbc)
+ db LAPRAS
+ db HORSEA
+ db GOLDEEN
+ db SQUIRTLE
+ db $00
+
+.list_bench ; 14fc1 (5:4fc1)
+ db SQUIRTLE
+ db HORSEA
+ db GOLDEEN
+ db LAPRAS
+ db $00
+
+.list_retreat ; 14fc6 (5:4fc6)
+ ai_retreat SQUIRTLE, -3
+ ai_retreat WARTORTLE, -2
+ ai_retreat HORSEA, -1
+ db $00
+
+.list_energy ; 14fcd (5:4fcd)
+ ai_energy SQUIRTLE, 2, +0
+ ai_energy WARTORTLE, 3, +0
+ ai_energy BLASTOISE, 5, +0
+ ai_energy GOLDEEN, 1, +0
+ ai_energy SEAKING, 2, +0
+ ai_energy HORSEA, 2, +0
+ ai_energy SEADRA, 3, +0
+ ai_energy LAPRAS, 3, +0
+ db $00
+
+.list_prize ; 14fe6 (5:4fe6)
+ db GAMBLER
+ db ENERGY_RETRIEVAL
+ db SUPER_ENERGY_RETRIEVAL
+ db BLASTOISE
+ db $00
+
+.store_list_pointers ; 14feb (5:4feb)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
+; 0x15019
diff --git a/src/engine/deck_ai/decks/im_ronald.asm b/src/engine/deck_ai/decks/im_ronald.asm
new file mode 100644
index 0000000..c140f1d
--- /dev/null
+++ b/src/engine/deck_ai/decks/im_ronald.asm
@@ -0,0 +1,86 @@
+AIActionTable_ImRonald: ; 152bd (5:52bd)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 152c9 (5:52c9)
+ call AIMainTurnLogic
+ ret
+; 0x152cd
+
+.start_duel ; 152cd (5:52cd)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+; 0x152de
+
+.forced_switch ; 152de (5:52de)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x152e2
+
+.ko_switch ; 152e2 (5:52e2)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x152e6
+
+.take_prize ; 152e6 (5:52e6)
+ call AIPickPrizeCards
+ ret
+; 0x152ea
+
+.list_arena ; 152ea (5:52ea)
+ db LAPRAS
+ db SEEL
+ db CHARMANDER
+ db CUBONE
+ db SQUIRTLE
+ db GROWLITHE
+ db $00
+
+.list_bench ; 152f1 (5:52f1)
+ db CHARMANDER
+ db SQUIRTLE
+ db SEEL
+ db CUBONE
+ db GROWLITHE
+ db LAPRAS
+ db $00
+
+.list_retreat ; 152f8 (5:52f8)
+ db $00
+
+.list_energy ; 152f9 (5:52f9)
+ ai_energy CHARMANDER, 3, +0
+ ai_energy CHARMELEON, 5, +0
+ ai_energy GROWLITHE, 2, +0
+ ai_energy ARCANINE2, 4, +0
+ ai_energy SQUIRTLE, 2, +0
+ ai_energy WARTORTLE, 3, +0
+ ai_energy SEEL, 3, +0
+ ai_energy DEWGONG, 4, +0
+ ai_energy LAPRAS, 3, +0
+ ai_energy CUBONE, 3, +0
+ ai_energy MAROWAK1, 3, +0
+ db $00
+
+.list_prize ; 1531b (5:531b)
+ db LAPRAS
+ db $00
+
+.store_list_pointers ; 1531d (5:531d)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
+; 0x1534b
diff --git a/src/engine/deck_ai/decks/invincible_ronald.asm b/src/engine/deck_ai/decks/invincible_ronald.asm
new file mode 100644
index 0000000..0a55461
--- /dev/null
+++ b/src/engine/deck_ai/decks/invincible_ronald.asm
@@ -0,0 +1,84 @@
+AIActionTable_InvincibleRonald: ; 153e8 (5:53e8)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 153f4 (5:53f4)
+ call AIMainTurnLogic
+ ret
+; 0x153f8
+
+.start_duel ; 153f8 (5:53f8)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+; 0x15409
+
+.forced_switch ; 15409 (5:5409)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x1540d
+
+.ko_switch ; 1540d (5:540d)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x15411
+
+.take_prize ; 15411 (5:5411)
+ call AIPickPrizeCards
+ ret
+; 0x15415
+
+.list_arena ; 15415 (5:5415)
+ db KANGASKHAN
+ db MAGMAR2
+ db CHANSEY
+ db GEODUDE
+ db SCYTHER
+ db GRIMER
+ db $00
+
+.list_bench ; 1541c (5:541c)
+ db GRIMER
+ db SCYTHER
+ db GEODUDE
+ db CHANSEY
+ db MAGMAR2
+ db KANGASKHAN
+ db $00
+
+.list_retreat ; 15423 (5:5423)
+ ai_retreat GRIMER, -1
+ db $00
+
+.list_energy ; 15426 (5:5426)
+ ai_energy GRIMER, 1, -1
+ ai_energy MUK, 3, -1
+ ai_energy SCYTHER, 4, +1
+ ai_energy MAGMAR2, 2, +0
+ ai_energy GEODUDE, 2, +0
+ ai_energy GRAVELER, 3, +0
+ ai_energy CHANSEY, 4, +0
+ ai_energy KANGASKHAN, 4, -1
+ db $00
+
+.list_prize ; 1543f (5:543f)
+ db GAMBLER
+ db $00
+
+.store_list_pointers ; 15441 (5:5441)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
+; 0x1546f
diff --git a/src/engine/deck_ai/decks/legendary_articuno.asm b/src/engine/deck_ai/decks/legendary_articuno.asm
new file mode 100644
index 0000000..183ab62
--- /dev/null
+++ b/src/engine/deck_ai/decks/legendary_articuno.asm
@@ -0,0 +1,217 @@
+AIActionTable_LegendaryArticuno: ; 14c0b (5:4c0b)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 14c17 (5:4c17)
+ call AIDoTurn_LegendaryArticuno
+ ret
+; 0x14c1b
+
+.start_duel ; 14c1b (5:4c1b)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+; 0x14c2c
+
+.forced_switch ; 14c2c (5:4c2c)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x14c30
+
+.ko_switch ; 14c30 (5:4c30)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x14c34
+
+.take_prize ; 14c34 (5:4c34)
+ call AIPickPrizeCards
+ ret
+; 0x14c38
+
+.list_arena ; 14c38 (5:4c38)
+ db CHANSEY
+ db LAPRAS
+ db DITTO
+ db SEEL
+ db ARTICUNO1
+ db ARTICUNO2
+ db $00
+
+.list_bench ; 14c3f (5:4c3f)
+ db ARTICUNO1
+ db SEEL
+ db LAPRAS
+ db CHANSEY
+ db DITTO
+ db $00
+
+.list_retreat ; 14c45 (5:4c45)
+ ai_retreat SEEL, -3
+ ai_retreat DITTO, -3
+ db $00
+
+.list_energy ; 14c4a (5:4c4a)
+ ai_energy SEEL, 3, +1
+ ai_energy DEWGONG, 4, +0
+ ai_energy LAPRAS, 3, +0
+ ai_energy ARTICUNO1, 4, +1
+ ai_energy ARTICUNO2, 3, +0
+ ai_energy CHANSEY, 0, -8
+ ai_energy DITTO, 3, +0
+ db $00
+
+.list_prize ; 14c60 (5:4c60)
+ db GAMBLER
+ db ARTICUNO2
+ db $00
+
+.store_list_pointers ; 14c63 (5:4c63)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
+; 0x14c91
+
+; this routine handles how Legendary Articuno
+; prioritises playing energy cards to each Pokémon.
+; first, it makes sure that all Lapras have at least
+; 3 energy cards before moving on to Articuno,
+; and then to Dewgong and Seel
+ScoreLegendaryArticunoCards: ; 14c91 (5:4c91)
+ call SwapTurn
+ call CountPrizes
+ call SwapTurn
+ cp 3
+ ret c
+
+; player prizes >= 3
+; if Lapras has more than half HP and
+; can use second move, check next for Articuno
+; otherwise, check if Articuno or Dewgong
+; have more than half HP and can use second move
+; and if so, the next Pokémon to check is Lapras
+ ld a, LAPRAS
+ call CheckForBenchIDAtHalfHPAndCanUseSecondMove
+ jr c, .articuno
+ ld a, ARTICUNO1
+ call CheckForBenchIDAtHalfHPAndCanUseSecondMove
+ jr c, .lapras
+ ld a, DEWGONG
+ call CheckForBenchIDAtHalfHPAndCanUseSecondMove
+ jr c, .lapras
+ jr .articuno
+
+; the following routines check for certain card IDs in bench
+; and call RaiseAIScoreToAllMatchingIDsInBench if these are found.
+; for Lapras, an additional check is made to its
+; attached energy count, which skips calling the routine
+; if this count is >= 3
+.lapras
+ ld a, LAPRAS
+ ld b, PLAY_AREA_BENCH_1
+ call LookForCardIDInPlayArea_Bank5
+ jr nc, .articuno
+ ld e, a
+ call CountNumberOfEnergyCardsAttached
+ cp 3
+ jr nc, .articuno
+ ld a, LAPRAS
+ call RaiseAIScoreToAllMatchingIDsInBench
+ ret
+
+.articuno
+ ld a, ARTICUNO1
+ ld b, PLAY_AREA_BENCH_1
+ call LookForCardIDInPlayArea_Bank5
+ jr nc, .dewgong
+ ld a, ARTICUNO1
+ call RaiseAIScoreToAllMatchingIDsInBench
+ ret
+
+.dewgong
+ ld a, DEWGONG
+ ld b, PLAY_AREA_BENCH_1
+ call LookForCardIDInPlayArea_Bank5
+ jr nc, .seel
+ ld a, DEWGONG
+ call RaiseAIScoreToAllMatchingIDsInBench
+ ret
+
+.seel
+ ld a, SEEL
+ ld b, PLAY_AREA_BENCH_1
+ call LookForCardIDInPlayArea_Bank5
+ ret nc
+ ld a, SEEL
+ call RaiseAIScoreToAllMatchingIDsInBench
+ ret
+; 0x14cf7
+
+AIDoTurn_LegendaryArticuno: ; 14cf7 (5:4cf7)
+; initialize variables
+ call InitAITurnVars
+ ld a, AI_TRAINER_CARD_PHASE_01
+ call AIProcessHandTrainerCards
+ farcall HandleAIAntiMewtwoDeckStrategy
+ jp nc, .try_attack
+; process Trainer cards
+ ld a, AI_TRAINER_CARD_PHASE_02
+ call AIProcessHandTrainerCards
+; play Pokemon from hand
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+ call AIProcessRetreat
+ ld a, AI_TRAINER_CARD_PHASE_10
+ call AIProcessHandTrainerCards
+; play Energy card if possible
+ ld a, [wAlreadyPlayedEnergy]
+ or a
+ jr nz, .skip_energy_attach_1
+ call AIProcessAndTryToPlayEnergy
+.skip_energy_attach_1
+; play Pokemon from hand again
+ call AIDecidePlayPokemonCard
+; process Trainer cards phases 13 and 15
+ ld a, AI_TRAINER_CARD_PHASE_13
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_15
+ call AIProcessHandTrainerCards
+; if used Professor Oak, process new hand
+ ld a, [wPreviousAIFlags]
+ and AI_FLAG_USED_PROFESSOR_OAK
+ jr z, .try_attack
+ ld a, AI_TRAINER_CARD_PHASE_01
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_02
+ call AIProcessHandTrainerCards
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+ call AIProcessRetreat
+ ld a, AI_TRAINER_CARD_PHASE_10
+ call AIProcessHandTrainerCards
+ ld a, [wAlreadyPlayedEnergy]
+ or a
+ jr nz, .skip_energy_attach_2
+ call AIProcessAndTryToPlayEnergy
+.skip_energy_attach_2
+ call AIDecidePlayPokemonCard
+.try_attack
+; attack if possible, if not,
+; finish turn without attacking.
+ call AIProcessAndTryToUseAttack
+ ret c ; return if turn ended
+ ld a, OPPACTION_FINISH_NO_ATTACK
+ bank1call AIMakeDecision
+ ret
+; 0x14d60
diff --git a/src/engine/deck_ai/decks/legendary_dragonite.asm b/src/engine/deck_ai/decks/legendary_dragonite.asm
new file mode 100644
index 0000000..d320440
--- /dev/null
+++ b/src/engine/deck_ai/decks/legendary_dragonite.asm
@@ -0,0 +1,173 @@
+AIActionTable_LegendaryDragonite: ; 14d60 (05:4d60)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 14d6c (5:4d6c)
+ call AIDoTurn_LegendaryDragonite
+ ret
+; 0x14d70
+
+.start_duel ; 14d70 (5:4d70)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+; 0x14d81
+
+.forced_switch ; 14d81 (5:4d81)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x14d85
+
+.ko_switch ; 14d85 (5:4d85)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x14d89
+
+.take_prize ; 14d89 (5:4d89)
+ call AIPickPrizeCards
+ ret
+; 0x14d8d
+
+.list_arena ; 14d8d (5:4d8d)
+ db KANGASKHAN
+ db LAPRAS
+ db CHARMANDER
+ db DRATINI
+ db MAGIKARP
+ db $00
+
+.list_bench ; 14d93 (5:4d93)
+ db CHARMANDER
+ db MAGIKARP
+ db DRATINI
+ db LAPRAS
+ db KANGASKHAN
+ db $00
+
+.list_retreat ; 14d99 (5:4d99)
+ ai_retreat CHARMANDER, -1
+ ai_retreat MAGIKARP, -5
+ db $00
+
+.list_energy ; 14d9e (5:4d9e)
+ ai_energy CHARMANDER, 3, +1
+ ai_energy CHARMELEON, 4, +1
+ ai_energy CHARIZARD, 5, +0
+ ai_energy MAGIKARP, 3, +1
+ ai_energy GYARADOS, 4, -1
+ ai_energy DRATINI, 2, +0
+ ai_energy DRAGONAIR, 4, +0
+ ai_energy DRAGONITE1, 3, -1
+ ai_energy KANGASKHAN, 2, -2
+ ai_energy LAPRAS, 3, +0
+ db $00
+
+.list_prize ; 14dbd (5:4dbd)
+ db GAMBLER
+ db DRAGONITE1
+ db KANGASKHAN
+ db $00
+
+.store_list_pointers ; 14dc1 (5:4dc1)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
+; 0x14def
+
+AIDoTurn_LegendaryDragonite: ; 14def (5:4def)
+; initialize variables
+ call InitAITurnVars
+ ld a, AI_TRAINER_CARD_PHASE_01
+ call AIProcessHandTrainerCards
+ farcall HandleAIAntiMewtwoDeckStrategy
+ jp nc, .try_attack
+; process Trainer cards
+ ld a, AI_TRAINER_CARD_PHASE_02
+ call AIProcessHandTrainerCards
+; play Pokemon from hand
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+ ld a, AI_TRAINER_CARD_PHASE_07
+ call AIProcessHandTrainerCards
+ call AIProcessRetreat
+ ld a, AI_TRAINER_CARD_PHASE_10
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_11
+ call AIProcessHandTrainerCards
+; play Energy card if possible
+ ld a, [wAlreadyPlayedEnergy]
+ or a
+ jr nz, .skip_energy_attach_1
+
+; if Arena card is Kangaskhan and doens't
+; have Energy cards attached, try attaching from hand.
+; otherwise run normal AI energy attach routine.
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, KANGASKHAN
+ cp e
+ jr nz, .attach_normally
+ call CreateEnergyCardListFromHand
+ jr c, .skip_energy_attach_1
+ ld e, PLAY_AREA_ARENA
+ call CountNumberOfEnergyCardsAttached
+ or a
+ jr nz, .attach_normally
+ xor a
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call AITryToPlayEnergyCard
+ jr c, .skip_energy_attach_1
+.attach_normally
+ call AIProcessAndTryToPlayEnergy
+
+.skip_energy_attach_1
+; play Pokemon from hand again
+ call AIDecidePlayPokemonCard
+ ld a, AI_TRAINER_CARD_PHASE_15
+ call AIProcessHandTrainerCards
+; if used Professor Oak, process new hand
+; if not, then proceed to attack.
+ ld a, [wPreviousAIFlags]
+ and AI_FLAG_USED_PROFESSOR_OAK
+ jr z, .try_attack
+ ld a, AI_TRAINER_CARD_PHASE_01
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_02
+ call AIProcessHandTrainerCards
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+ ld a, AI_TRAINER_CARD_PHASE_07
+ call AIProcessHandTrainerCards
+ call AIProcessRetreat
+ ld a, AI_TRAINER_CARD_PHASE_10
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_11
+ call AIProcessHandTrainerCards
+ ld a, [wAlreadyPlayedEnergy]
+ or a
+ jr nz, .skip_energy_attach_2
+ call AIProcessAndTryToPlayEnergy
+.skip_energy_attach_2
+ call AIDecidePlayPokemonCard
+.try_attack
+; attack if possible, if not,
+; finish turn without attacking.
+ call AIProcessAndTryToUseAttack
+ ret c ; return if turn ended
+ ld a, OPPACTION_FINISH_NO_ATTACK
+ bank1call AIMakeDecision
+ ret
+; 0x14e89
diff --git a/src/engine/deck_ai/decks/legendary_moltres.asm b/src/engine/deck_ai/decks/legendary_moltres.asm
new file mode 100644
index 0000000..cb38668
--- /dev/null
+++ b/src/engine/deck_ai/decks/legendary_moltres.asm
@@ -0,0 +1,183 @@
+AIActionTable_LegendaryMoltres: ; 149e8 (05:49e8)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 149f4 (5:49f4)
+ call AIDoTurn_LegendaryMoltres
+ ret
+; 0x149f8
+
+.start_duel ; 149f8 (5:49f8)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc ; Play Area set up was successful
+ call AIPlayInitialBasicCards
+ ret
+; 0x14a09
+
+.forced_switch ; 14a09 (5:4a09)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x14a0d
+
+.ko_switch ; 14a0d (5:4a0d)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x14a11
+
+.take_prize ; 14a11 (5:4a11)
+ call AIPickPrizeCards
+ ret
+; 0x14a15
+
+.list_arena ; 14a15 (5:4a15)
+ db MAGMAR2
+ db GROWLITHE
+ db VULPIX
+ db MAGMAR1
+ db MOLTRES1
+ db MOLTRES2
+ db $00
+
+.list_bench ; 14a1c (5:4a1c)
+ db MOLTRES1
+ db VULPIX
+ db GROWLITHE
+ db MAGMAR2
+ db MAGMAR1
+ db $00
+
+.list_play_hand ; 14a22 (5:4a22)
+ db MOLTRES2
+ db MOLTRES1
+ db VULPIX
+ db GROWLITHE
+ db MAGMAR2
+ db MAGMAR1
+ db $00
+
+.list_retreat ; 14a29 (5:4a29)
+ ai_retreat GROWLITHE, -5
+ ai_retreat VULPIX, -5
+ db $00
+
+.list_energy ; 14a2e (5:4a2e)
+ ai_energy VULPIX, 3, +0
+ ai_energy NINETAILS2, 3, +1
+ ai_energy GROWLITHE, 3, +1
+ ai_energy ARCANINE2, 4, +1
+ ai_energy MAGMAR1, 4, -1
+ ai_energy MAGMAR2, 1, -1
+ ai_energy MOLTRES2, 3, +2
+ ai_energy MOLTRES1, 4, +2
+ db $00
+
+.list_prize ; 14a47 (5:4a47)
+ db ENERGY_REMOVAL
+ db MOLTRES2
+ db $00
+
+.store_list_pointers ; 14a4a (5:4a4a)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_play_hand
+ store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
+; 0x14a81
+
+AIDoTurn_LegendaryMoltres: ; 14a81 (5:4a81)
+; initialize variables
+ call InitAITurnVars
+ farcall HandleAIAntiMewtwoDeckStrategy
+ jp nc, .try_attack
+; process Trainer cards
+; phase 2 through 4.
+ ld a, AI_TRAINER_CARD_PHASE_02
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_04
+ call AIProcessHandTrainerCards
+
+; check if AI can play Moltres2
+; from hand and if so, play it.
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp MAX_PLAY_AREA_POKEMON
+ jr nc, .skip_moltres ; skip if bench is full
+ ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
+ call GetTurnDuelistVariable
+ cp DECK_SIZE - 9
+ jr nc, .skip_moltres ; skip if cards in deck <= 9
+ ld a, MUK
+ call CountPokemonIDInBothPlayAreas
+ jr c, .skip_moltres ; skip if Muk in play
+ ld a, MOLTRES2
+ call LookForCardIDInHandList_Bank5
+ jr nc, .skip_moltres ; skip if no Moltres2 in hand
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_PLAY_BASIC_PKMN
+ bank1call AIMakeDecision
+
+.skip_moltres
+; play Pokemon from hand
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+; process Trainer cards
+ ld a, AI_TRAINER_CARD_PHASE_05
+ call AIProcessHandTrainerCards
+ call AIProcessRetreat
+ ld a, AI_TRAINER_CARD_PHASE_10
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_11
+ call AIProcessHandTrainerCards
+; play Energy card if possible
+ ld a, [wAlreadyPlayedEnergy]
+ or a
+ jr nz, .skip_attach_energy
+
+; if Magmar2 is the Arena card and has no energy attached,
+; try attaching an energy card to it from the hand.
+; otherwise, run normal AI energy attach routine.
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, MAGMAR2
+ cp e
+ jr nz, .attach_normally
+ ; Magmar2 is the Arena card
+ call CreateEnergyCardListFromHand
+ jr c, .skip_attach_energy
+ ld e, PLAY_AREA_ARENA
+ call CountNumberOfEnergyCardsAttached
+ or a
+ jr nz, .attach_normally
+ xor a ; PLAY_AREA_ARENA
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call AITryToPlayEnergyCard
+ jr c, .skip_attach_energy
+
+.attach_normally
+; play Energy card if possible
+ call AIProcessAndTryToPlayEnergy
+.skip_attach_energy
+; try playing Pokemon cards from hand again
+ call AIDecidePlayPokemonCard
+ ld a, AI_TRAINER_CARD_PHASE_13
+ call AIProcessHandTrainerCards
+
+.try_attack
+; attack if possible, if not,
+; finish turn without attacking.
+ call AIProcessAndTryToUseAttack
+ ret c
+ ld a, OPPACTION_FINISH_NO_ATTACK
+ bank1call AIMakeDecision
+ ret
+; 0x14b0f
diff --git a/src/engine/deck_ai/decks/legendary_ronald.asm b/src/engine/deck_ai/decks/legendary_ronald.asm
new file mode 100644
index 0000000..2721ba9
--- /dev/null
+++ b/src/engine/deck_ai/decks/legendary_ronald.asm
@@ -0,0 +1,210 @@
+AIActionTable_LegendaryRonald: ; 1546f (5:546f)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 1547b (5:547b)
+ call AIDoTurn_LegendaryRonald
+ ret
+; 0x1547f
+
+.start_duel ; 1547f (5:547f)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+; 0x15490
+
+.forced_switch ; 15490 (5:5490)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x15494
+
+.ko_switch ; 15494 (5:5494)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x15498
+
+.take_prize ; 15498 (5:5498)
+ call AIPickPrizeCards
+ ret
+; 0x1549c
+
+.list_arena ; 1549c (5:549c)
+ db KANGASKHAN
+ db DRATINI
+ db EEVEE
+ db ZAPDOS3
+ db ARTICUNO2
+ db MOLTRES2
+ db $00
+
+.list_bench ; 154a3 (5:54a3)
+ db KANGASKHAN
+ db DRATINI
+ db EEVEE
+ db $00
+
+.list_play_hand ; 154a7 (5:54a7)
+ db MOLTRES2
+ db ZAPDOS3
+ db KANGASKHAN
+ db DRATINI
+ db EEVEE
+ db ARTICUNO2
+ db $00
+
+.list_retreat ; 154ae (5:54ae)
+ ai_retreat EEVEE, -2
+ db $00
+
+.list_energy ; 154b1 (5:54b1)
+ ai_energy FLAREON1, 3, +0
+ ai_energy MOLTRES2, 3, +0
+ ai_energy VAPOREON1, 3, +0
+ ai_energy ARTICUNO2, 0, -8
+ ai_energy JOLTEON1, 4, +0
+ ai_energy ZAPDOS3, 0, -8
+ ai_energy KANGASKHAN, 4, -1
+ ai_energy EEVEE, 3, +0
+ ai_energy DRATINI, 3, +0
+ ai_energy DRAGONAIR, 4, +0
+ ai_energy DRAGONITE1, 3, +0
+ db $00
+
+.list_prize ; 154d3 (5:54d3)
+ db MOLTRES2
+ db ARTICUNO2
+ db ZAPDOS3
+ db DRAGONITE1
+ db GAMBLER
+ db $00
+
+.store_list_pointers ; 154d9 (5:54d9)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_play_hand
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
+; 0x15507
+
+AIDoTurn_LegendaryRonald: ; 15507 (5:5507)
+; initialize variables
+ call InitAITurnVars
+; process Trainer cards
+ ld a, AI_TRAINER_CARD_PHASE_01
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_02
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_04
+ call AIProcessHandTrainerCards
+
+; check if AI can play Moltres2
+; from hand and if so, play it.
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp MAX_PLAY_AREA_POKEMON
+ jr nc, .skip_moltres_1 ; skip if bench is full
+ ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
+ call GetTurnDuelistVariable
+ cp DECK_SIZE - 9
+ jr nc, .skip_moltres_1 ; skip if cards in deck <= 9
+ ld a, MUK
+ call CountPokemonIDInBothPlayAreas
+ jr c, .skip_moltres_1 ; skip if Muk in play
+ ld a, MOLTRES2
+ call LookForCardIDInHandList_Bank5
+ jr nc, .skip_moltres_1 ; skip if no Moltres2 in hand
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_PLAY_BASIC_PKMN
+ bank1call AIMakeDecision
+
+.skip_moltres_1
+; play Pokemon from hand
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+; process Trainer cards
+ ld a, AI_TRAINER_CARD_PHASE_05
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_07
+ call AIProcessHandTrainerCards
+ call AIProcessRetreat
+ ld a, AI_TRAINER_CARD_PHASE_10
+ call AIProcessHandTrainerCards
+; play Energy card if possible
+ ld a, [wAlreadyPlayedEnergy]
+ or a
+ jr nz, .skip_attach_energy_1
+ call AIProcessAndTryToPlayEnergy
+.skip_attach_energy_1
+; try playing Pokemon cards from hand again
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+ ld a, AI_TRAINER_CARD_PHASE_15
+; if used Professor Oak, process new hand
+; if not, then proceed to attack.
+ call AIProcessHandTrainerCards
+ ld a, [wPreviousAIFlags]
+ and AI_FLAG_USED_PROFESSOR_OAK
+ jr z, .try_attack
+ ld a, AI_TRAINER_CARD_PHASE_01
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_02
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_04
+ call AIProcessHandTrainerCards
+
+; check if AI can play Moltres2
+; from hand and if so, play it.
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp MAX_PLAY_AREA_POKEMON
+ jr nc, .skip_moltres_2 ; skip if bench is full
+ ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
+ call GetTurnDuelistVariable
+ cp DECK_SIZE - 9
+ jr nc, .skip_moltres_2 ; skip if cards in deck <= 9
+ ld a, MUK
+ call CountPokemonIDInBothPlayAreas
+ jr c, .skip_moltres_2 ; skip if Muk in play
+ ld a, MOLTRES2
+ call LookForCardIDInHandList_Bank5
+ jr nc, .skip_moltres_2 ; skip if no Moltres2 in hand
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_PLAY_BASIC_PKMN
+ bank1call AIMakeDecision
+
+.skip_moltres_2
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+ ld a, AI_TRAINER_CARD_PHASE_05
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_07
+ call AIProcessHandTrainerCards
+ call AIProcessRetreat
+ ld a, AI_TRAINER_CARD_PHASE_10
+ call AIProcessHandTrainerCards
+ ld a, [wAlreadyPlayedEnergy]
+ or a
+ jr nz, .skip_attach_energy_2
+ call AIProcessAndTryToPlayEnergy
+.skip_attach_energy_2
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+.try_attack
+; attack if possible, if not,
+; finish turn without attacking.
+ call AIProcessAndTryToUseAttack
+ ret c ; return if turn ended
+ ld a, OPPACTION_FINISH_NO_ATTACK
+ bank1call AIMakeDecision
+ ret
+; 0x155d2
diff --git a/src/engine/deck_ai/decks/legendary_zapdos.asm b/src/engine/deck_ai/decks/legendary_zapdos.asm
new file mode 100644
index 0000000..cfa60be
--- /dev/null
+++ b/src/engine/deck_ai/decks/legendary_zapdos.asm
@@ -0,0 +1,160 @@
+AIActionTable_LegendaryZapdos: ; 14b0f (05:4b0f)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 14b1b (5:4b1b)
+ call AIDoTurn_LegendaryZapdos
+ ret
+; 0x14b1f
+
+.start_duel ; 14b1f (5:4b1f)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+; 0x14b30
+
+.forced_switch ; 14b30 (5:4b30)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x14b34
+
+.ko_switch ; 14b34 (5:4b34)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x14b38
+
+.take_prize ; 14b38 (5:4b38)
+ call AIPickPrizeCards
+ ret
+; 0x14b3c
+
+.list_arena ; 14b3c (5:4b3c)
+ db ELECTABUZZ2
+ db VOLTORB
+ db EEVEE
+ db ZAPDOS1
+ db ZAPDOS2
+ db ZAPDOS3
+ db $00
+
+.list_bench ; 14b43 (5:4b43)
+ db ZAPDOS2
+ db ZAPDOS1
+ db EEVEE
+ db VOLTORB
+ db ELECTABUZZ2
+ db $00
+
+.list_retreat ; 14b49 (5:4b49)
+ ai_retreat EEVEE, -5
+ ai_retreat VOLTORB, -5
+ ai_retreat ELECTABUZZ2, -5
+ db $00
+
+.list_energy ; 14b50 (5:4b50)
+ ai_energy VOLTORB, 1, -1
+ ai_energy ELECTRODE1, 3, +0
+ ai_energy ELECTABUZZ2, 2, -1
+ ai_energy JOLTEON2, 3, +1
+ ai_energy ZAPDOS1, 4, +2
+ ai_energy ZAPDOS2, 4, +2
+ ai_energy ZAPDOS3, 3, +1
+ ai_energy EEVEE, 3, +0
+ db $00
+
+.list_prize ; 14b69 (5:4b69)
+ db GAMBLER
+ db ZAPDOS3
+ db $00
+
+.store_list_pointers ; 14b6c (5:4b6c)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
+; 0x14b9a
+
+AIDoTurn_LegendaryZapdos: ; 14b9a (5:4b9a)
+; initialize variables
+ call InitAITurnVars
+ farcall HandleAIAntiMewtwoDeckStrategy
+ jp nc, .try_attack
+; process Trainer cards
+ ld a, AI_TRAINER_CARD_PHASE_01
+ call AIProcessHandTrainerCards
+ ld a, AI_TRAINER_CARD_PHASE_04
+ call AIProcessHandTrainerCards
+; play Pokemon from hand
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+ ld a, AI_TRAINER_CARD_PHASE_07
+ call AIProcessHandTrainerCards
+ call AIProcessRetreat
+ ld a, AI_TRAINER_CARD_PHASE_10
+ call AIProcessHandTrainerCards
+; play Energy card if possible.
+ ld a, [wAlreadyPlayedEnergy]
+ or a
+ jr nz, .skip_energy_attach
+
+; if Arena card is Voltorb and there's Electrode1 in hand,
+; or if it's Electabuzz, try attaching Energy card
+; to the Arena card if it doesn't have any energy attached.
+; Otherwise if Energy card is not needed,
+; go through normal AI energy attach routine.
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, VOLTORB
+ cp e
+ jr nz, .check_electabuzz
+ ld a, ELECTRODE1
+ call LookForCardIDInHandList_Bank5
+ jr nc, .attach_normally
+ jr .voltorb_or_electabuzz
+.check_electabuzz
+ ld a, ELECTABUZZ2
+ cp e
+ jr nz, .attach_normally
+
+.voltorb_or_electabuzz
+ call CreateEnergyCardListFromHand
+ jr c, .skip_energy_attach
+ ld e, PLAY_AREA_ARENA
+ call CountNumberOfEnergyCardsAttached
+ or a
+ jr nz, .attach_normally
+ xor a ; PLAY_AREA_ARENA
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call AITryToPlayEnergyCard
+ jr c, .skip_energy_attach
+
+.attach_normally
+ call AIProcessAndTryToPlayEnergy
+
+.skip_energy_attach
+; play Pokemon from hand again
+ call AIDecidePlayPokemonCard
+ ret c ; return if turn ended
+ ld a, AI_TRAINER_CARD_PHASE_13
+ call AIProcessHandTrainerCards
+.try_attack
+; attack if possible, if not,
+; finish turn without attacking.
+ call AIProcessAndTryToUseAttack
+ ret c ; return if turn ended
+ ld a, OPPACTION_FINISH_NO_ATTACK
+ bank1call AIMakeDecision
+ ret
+; 0x14c0b
diff --git a/src/engine/deck_ai/decks/powerful_ronald.asm b/src/engine/deck_ai/decks/powerful_ronald.asm
new file mode 100644
index 0000000..d99f87f
--- /dev/null
+++ b/src/engine/deck_ai/decks/powerful_ronald.asm
@@ -0,0 +1,98 @@
+AIActionTable_PowerfulRonald: ; 1534b (5:534b)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 15357 (5:5357)
+ call AIMainTurnLogic
+ ret
+; 0x1535b
+
+.start_duel ; 1535b (5:535b)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+; 0x1536c
+
+.forced_switch ; 1536c (5:536c)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x15370
+
+.ko_switch ; 15370 (5:5370)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x15374
+
+.take_prize ; 15374 (5:5374)
+ call AIPickPrizeCards
+ ret
+; 0x15378
+
+.list_arena ; 15378 (5:5378)
+ db KANGASKHAN
+ db ELECTABUZZ2
+ db HITMONCHAN
+ db MR_MIME
+ db LICKITUNG
+ db HITMONLEE
+ db TAUROS
+ db JYNX
+ db MEWTWO1
+ db DODUO
+ db $00
+
+.list_bench ; 15383 (5:5383)
+ db KANGASKHAN
+ db HITMONLEE
+ db HITMONCHAN
+ db TAUROS
+ db DODUO
+ db JYNX
+ db MEWTWO1
+ db ELECTABUZZ2
+ db MR_MIME
+ db LICKITUNG
+ db $00
+
+.list_retreat ; 1538e (5:538e)
+ ai_retreat KANGASKHAN, -1
+ ai_retreat DODUO, -1
+ ai_retreat DODRIO, -1
+ db $00
+
+.list_energy ; 15395 (5:5395)
+ ai_energy ELECTABUZZ2, 2, +1
+ ai_energy HITMONLEE, 3, +1
+ ai_energy HITMONCHAN, 3, +1
+ ai_energy MR_MIME, 2, +0
+ ai_energy JYNX, 3, +0
+ ai_energy MEWTWO1, 2, +0
+ ai_energy DODUO, 3, -1
+ ai_energy DODRIO, 3, -1
+ ai_energy LICKITUNG, 2, +0
+ ai_energy KANGASKHAN, 4, -1
+ ai_energy TAUROS, 3, +0
+ db $00
+
+.list_prize ; 153b7 (5:53b7)
+ db GAMBLER
+ db ENERGY_REMOVAL
+ db $00
+
+.store_list_pointers ; 153ba (5:53ba)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
+; 0x153e8
diff --git a/src/engine/deck_ai/decks/rock_crusher.asm b/src/engine/deck_ai/decks/rock_crusher.asm
new file mode 100644
index 0000000..f4e79d8
--- /dev/null
+++ b/src/engine/deck_ai/decks/rock_crusher.asm
@@ -0,0 +1,80 @@
+AIActionTable_RockCrusher: ; 14f0e (5:4f0e)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 14f1a (5:4f1a)
+ call AIMainTurnLogic
+ ret
+; 0x14f1e
+
+.start_duel ; 14f1e (5:4f1e)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+; 0x14f2f
+
+.forced_switch ; 14f2f (5:4f2f)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x14f33
+
+.ko_switch ; 14f33 (5:4f33)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x14f37
+
+.take_prize ; 14f37 (5:4f37)
+ call AIPickPrizeCards
+ ret
+; 0x14f3b
+
+.list_arena ; 14f3b (5:4f3b)
+ db RHYHORN
+ db ONIX
+ db GEODUDE
+ db DIGLETT
+ db $00
+
+.list_bench ; 14f40 (5:4f40)
+ db DIGLETT
+ db GEODUDE
+ db RHYHORN
+ db ONIX
+ db $00
+
+.list_retreat ; 14f45 (5:4f45)
+ ai_retreat DIGLETT, -1
+ db $00
+
+.list_energy ; 14f48 (5:4f48)
+ ai_energy DIGLETT, 3, +1
+ ai_energy DUGTRIO, 4, +0
+ ai_energy GEODUDE, 2, +1
+ ai_energy GRAVELER, 3, +0
+ ai_energy GOLEM, 4, +0
+ ai_energy ONIX, 2, -1
+ ai_energy RHYHORN, 3, +0
+ db $00
+
+.list_prize ; 14f5e (5:4f5e)
+ db ENERGY_REMOVAL
+ db RHYHORN
+ db $00
+
+.store_list_pointers ; 14f61 (5:4f61)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
+; 0x14f8f
diff --git a/src/engine/deck_ai/decks/sams_practice.asm b/src/engine/deck_ai/decks/sams_practice.asm
new file mode 100644
index 0000000..2ce0060
--- /dev/null
+++ b/src/engine/deck_ai/decks/sams_practice.asm
@@ -0,0 +1,222 @@
+; AI for Sam's practice duel, which handles his scripted actions.
+; will act as a normal duelist AI after turn 7.
+AIActionTable_SamPractice: ; 147bd (05:47bd)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 147c9 (5:47c9)
+ call IsAIPracticeScriptedTurn
+ jr nc, .scripted_1
+; not scripted, use AI main turn logic
+ call AIMainTurnLogic
+ ret
+.scripted_1 ; use scripted actions instead
+ call AIPerformSciptedTurn
+ ret
+; 0x147d6
+
+.start_duel ; 147d6 (5:47d6)
+ call SetSamsStartingPlayArea
+ ret
+; 0x147da
+
+.forced_switch ; 147da (5:47da)
+ call IsAIPracticeScriptedTurn
+ jr nc, .scripted_2
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+.scripted_2
+ call PickRandomBenchPokemon
+ ret
+; 0x147e7
+
+.ko_switch: ; 147e7 (5:47e7)
+ call IsAIPracticeScriptedTurn
+ jr nc, .scripted_3
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+.scripted_3
+ call GetPlayAreaLocationOfRaticateOrRattata
+ ret
+; 0x147f4
+
+.take_prize: ; 147f4 (5:47f4)
+ call AIPickPrizeCards
+ ret
+; 0x147f8
+
+; returns carry if number of turns
+; the AI has taken >= 7.
+; used to know whether AI Sam is still
+; doing scripted turns.
+IsAIPracticeScriptedTurn: ; 147f8 (5:47f8)
+ ld a, [wDuelTurns]
+ srl a
+ cp 7
+ ccf
+ ret
+; 0x14801
+
+; places one Machop from the hand to the Play Area
+; and sets the number of prizes to 2.
+SetSamsStartingPlayArea: ; 14801 (5:4801)
+ call CreateHandCardList
+ ld hl, wDuelTempList
+.loop_hand
+ ld a, [hli]
+ ldh [hTempCardIndex_ff98], a
+ cp $ff
+ ret z
+ call LoadCardDataToBuffer1_FromDeckIndex
+ cp MACHOP
+ jr nz, .loop_hand
+ ldh a, [hTempCardIndex_ff98]
+ call PutHandPokemonCardInPlayArea
+ ld a, 2
+ ld [wDuelInitialPrizes], a
+ ret
+; 0x1481f
+
+; outputs in a Play Area location of Raticate or Rattata
+; in the Bench. If neither is found, just output PLAY_AREA_BENCH_1.
+GetPlayAreaLocationOfRaticateOrRattata: ; 1481f (5:481f)
+ ld a, RATICATE
+ ld b, PLAY_AREA_BENCH_1
+ call LookForCardIDInPlayArea_Bank5
+ cp $ff
+ jr nz, .found
+ ld a, RATTATA
+ ld b, PLAY_AREA_BENCH_1
+ call LookForCardIDInPlayArea_Bank5
+ cp $ff
+ jr nz, .found
+ ld a, PLAY_AREA_BENCH_1
+.found
+ ldh [hTempPlayAreaLocation_ff9d], a
+ ret
+; 0x1483a
+
+; has AI execute some scripted actions depending on Duel turn.
+AIPerformSciptedTurn: ; 1483a (5:483a)
+ ld a, [wDuelTurns]
+ srl a
+ ld hl, .scripted_actions_list
+ call JumpToFunctionInTable
+
+; always attack with Arena card's first attack.
+; if it's unusable end turn without attacking.
+ xor a
+ ldh [hTempPlayAreaLocation_ff9d], a
+ ld [wSelectedAttack], a
+ call CheckIfSelectedMoveIsUnusable
+ jr c, .unusable
+ call AITryUseAttack
+ ret
+
+.unusable
+ ld a, OPPACTION_FINISH_NO_ATTACK
+ bank1call AIMakeDecision
+ ret
+; 0x1485a
+
+.scripted_actions_list ; 1485a (05:485a)
+ dw .turn_1
+ dw .turn_2
+ dw .turn_3
+ dw .turn_4
+ dw .turn_5
+ dw .turn_6
+ dw .turn_7
+; 0x14868
+
+.turn_1 ; 14868 (5:4868)
+ ld d, MACHOP
+ ld e, FIGHTING_ENERGY
+ call AIAttachEnergyInHandToCardInPlayArea
+ ret
+; 0x14870
+
+.turn_2 ; 14870 (5:4870)
+ ld a, RATTATA
+ call LookForCardIDInHandList_Bank5
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_PLAY_BASIC_PKMN
+ bank1call AIMakeDecision
+ ld d, RATTATA
+ ld e, FIGHTING_ENERGY
+ call AIAttachEnergyInHandToCardInPlayArea
+ ret
+; 0x14884
+
+.turn_3 ; 14884 (5:4884)
+ ld a, RATTATA
+ ld b, PLAY_AREA_ARENA
+ call LookForCardIDInPlayArea_Bank5
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, RATICATE
+ call LookForCardIDInHandList_Bank5
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_EVOLVE_PKMN
+ bank1call AIMakeDecision
+ ld d, RATICATE
+ ld e, LIGHTNING_ENERGY
+ call AIAttachEnergyInHandToCardInPlayArea
+ ret
+; 0x148a1
+
+.turn_4 ; 148a1 (5:48a1)
+ ld d, RATICATE
+ ld e, LIGHTNING_ENERGY
+ call AIAttachEnergyInHandToCardInPlayArea
+ ret
+; 0x148a9
+
+.turn_5 ; 148a9 (5:48a9)
+ ld a, MACHOP
+ call LookForCardIDInHandList_Bank5
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_PLAY_BASIC_PKMN
+ bank1call AIMakeDecision
+ ld d, MACHOP
+ ld e, FIGHTING_ENERGY
+ call AIAttachEnergyInHandToCardInBench
+
+; this is a bug, it's attempting to compare a card ID with a deck index.
+; the intention was to change the card to switch to depending on whether
+; the first Machop was KO'd at this point in the Duel or not.
+; because of the buggy comparison, this will always jump the
+; 'inc a' instruction and switch to PLAY_AREA_BENCH_1.
+; in a normal Practice Duel following Dr. Mason's instructions,
+; this will always lead to the AI correctly switching Raticate with Machop,
+; but in case of a "Free" Duel where the first Machop is not KO'd,
+; the intention was to switch to PLAY_AREA_BENCH_2 instead.
+; but due to 'inc a' always being skipped, it will switch to Raticate.
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ cp MACHOP ; wrong
+ ld a, PLAY_AREA_BENCH_1
+ jr nz, .retreat
+ inc a ; PLAY_AREA_BENCH_2
+
+.retreat
+ call AITryToRetreat
+ ret
+; 0x148cc
+
+.turn_6 ; 148cc (5:48cc)
+ ld d, MACHOP
+ ld e, FIGHTING_ENERGY
+ call AIAttachEnergyInHandToCardInPlayArea
+ ret
+; 0x148d4
+
+.turn_7 ; 148d4 (5:48d4)
+ ld d, MACHOP
+ ld e, FIGHTING_ENERGY
+ call AIAttachEnergyInHandToCardInPlayArea
+ ret
+; 0x148dc
diff --git a/src/engine/deck_ai/decks/strange_psyshock.asm b/src/engine/deck_ai/decks/strange_psyshock.asm
new file mode 100644
index 0000000..309ef11
--- /dev/null
+++ b/src/engine/deck_ai/decks/strange_psyshock.asm
@@ -0,0 +1,87 @@
+AIActionTable_StrangePsyshock: ; 15122 (5:5122)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 1512e (5:512e)
+ call AIMainTurnLogic
+ ret
+; 0x15132
+
+.start_duel ; 15132 (5:5132)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+; 0x15143
+
+.forced_switch ; 15143 (5:5143)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x15147
+
+.ko_switch ; 15147 (5:5147)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x1514b
+
+.take_prize ; 1514b (5:514b)
+ call AIPickPrizeCards
+ ret
+; 0x1514f
+
+.list_arena ; 1514f (5:514f)
+ db KANGASKHAN
+ db CHANSEY
+ db SNORLAX
+ db MR_MIME
+ db ABRA
+ db $00
+
+.list_bench ; 15155 (5:5155)
+ db ABRA
+ db MR_MIME
+ db KANGASKHAN
+ db SNORLAX
+ db CHANSEY
+ db $00
+
+.list_retreat ; 1515b (5:515b)
+ ai_retreat ABRA, -3
+ ai_retreat SNORLAX, -3
+ ai_retreat KANGASKHAN, -1
+ ai_retreat CHANSEY, -1
+ db $00
+
+.list_energy ; 15164 (5:5164)
+ ai_energy ABRA, 3, +1
+ ai_energy KADABRA, 3, +0
+ ai_energy ALAKAZAM, 3, +0
+ ai_energy MR_MIME, 2, +0
+ ai_energy CHANSEY, 2, -2
+ ai_energy KANGASKHAN, 4, -2
+ ai_energy SNORLAX, 0, -8
+ db $00
+
+.list_prize ; 1517a (5:517a)
+ db GAMBLER
+ db MR_MIME
+ db ALAKAZAM
+ db SWITCH
+ db $00
+
+.store_list_pointers ; 1517f (5:517f)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
+; 0x151ad
diff --git a/src/engine/deck_ai/decks/wonders_of_science.asm b/src/engine/deck_ai/decks/wonders_of_science.asm
new file mode 100644
index 0000000..e11a829
--- /dev/null
+++ b/src/engine/deck_ai/decks/wonders_of_science.asm
@@ -0,0 +1,83 @@
+AIActionTable_WondersOfScience: ; 151ad (5:51ad)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 151b9 (5:51b9)
+ call AIMainTurnLogic
+ ret
+; 0x151bd
+
+.start_duel ; 151bd (5:51bd)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+; 0x151ce
+
+.forced_switch ; 151ce (5:51ce)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x151d2
+
+.ko_switch ; 151d2 (5:51d2)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x151d6
+
+.take_prize ; 151d6 (5:51d6)
+ call AIPickPrizeCards
+ ret
+; 0x151da
+
+.list_arena ; 151da (5:51da)
+ db MEWTWO1
+ db MEWTWO3
+ db MEWTWO2
+ db GRIMER
+ db KOFFING
+ db PORYGON
+ db $00
+
+.list_bench ; 151e1 (5:51e1)
+ db GRIMER
+ db KOFFING
+ db MEWTWO3
+ db MEWTWO2
+ db MEWTWO1
+ db PORYGON
+ db $00
+
+.list_retreat ; 151e8 (5:51e8)
+ db $00
+
+.list_energy ; 151e9 (5:51e9)
+ ai_energy GRIMER, 3, +0
+ ai_energy MUK, 4, +0
+ ai_energy KOFFING, 2, +0
+ ai_energy WEEZING, 3, +0
+ ai_energy MEWTWO1, 2, -1
+ ai_energy MEWTWO3, 2, -1
+ ai_energy MEWTWO2, 2, -1
+ ai_energy PORYGON, 2, -1
+ db $00
+
+.list_prize ; 15202 (5:5202)
+ db MUK
+ db $00
+
+.store_list_pointers ; 15204 (5:5204)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
+; 0x15232
diff --git a/src/engine/deck_ai/decks/zapping_selfdestruct.asm b/src/engine/deck_ai/decks/zapping_selfdestruct.asm
new file mode 100644
index 0000000..6675c52
--- /dev/null
+++ b/src/engine/deck_ai/decks/zapping_selfdestruct.asm
@@ -0,0 +1,81 @@
+AIActionTable_ZappingSelfdestruct: ; 15019 (5:5019)
+ dw .do_turn ; unused
+ dw .do_turn
+ dw .start_duel
+ dw .forced_switch
+ dw .ko_switch
+ dw .take_prize
+
+.do_turn ; 15025 (5:5025)
+ call AIMainTurnLogic
+ ret
+; 0x15029
+
+.start_duel ; 15029 (5:5029)
+ call InitAIDuelVars
+ call .store_list_pointers
+ call SetUpBossStartingHandAndDeck
+ call TrySetUpBossStartingPlayArea
+ ret nc
+ call AIPlayInitialBasicCards
+ ret
+; 0x1503a
+
+.forced_switch ; 1503a (5:503a)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x1503e
+
+.ko_switch ; 1503e (5:503e)
+ call AIDecideBenchPokemonToSwitchTo
+ ret
+; 0x15042
+
+.take_prize ; 15042 (5:5042)
+ call AIPickPrizeCards
+ ret
+; 0x15046
+
+.list_arena ; 15046 (5:5046)
+ db KANGASKHAN
+ db ELECTABUZZ2
+ db TAUROS
+ db MAGNEMITE1
+ db VOLTORB
+ db $00
+
+.list_bench ; 1504c (5:504c)
+ db MAGNEMITE1
+ db VOLTORB
+ db ELECTABUZZ2
+ db TAUROS
+ db KANGASKHAN
+ db $00
+
+.list_retreat ; 15052 (5:5052)
+ ai_retreat VOLTORB, -1
+ db $00
+
+.list_energy ; 15055 (5:5055)
+ ai_energy MAGNEMITE1, 3, +1
+ ai_energy MAGNETON1, 4, +0
+ ai_energy VOLTORB, 3, +1
+ ai_energy ELECTRODE1, 3, +0
+ ai_energy ELECTABUZZ2, 1, +0
+ ai_energy KANGASKHAN, 2, -2
+ ai_energy TAUROS, 3, +0
+ db $00
+
+.list_prize ; 1506b (5:506b)
+ db KANGASKHAN
+ db $00
+
+.store_list_pointers ; 1506d (5:506d)
+ store_list_pointer wAICardListAvoidPrize, .list_prize
+ store_list_pointer wAICardListArenaPriority, .list_arena
+ store_list_pointer wAICardListBenchPriority, .list_bench
+ store_list_pointer wAICardListPlayFromHandPriority, .list_bench
+ ; missing store_list_pointer wAICardListRetreatBonus, .list_retreat
+ store_list_pointer wAICardListEnergyBonus, .list_energy
+ ret
+; 0x1509b
diff --git a/src/engine/effect_functions.asm b/src/engine/effect_functions.asm
index 116d545..75b0f76 100644
--- a/src/engine/effect_functions.asm
+++ b/src/engine/effect_functions.asm
@@ -292,7 +292,9 @@ ApplySubstatus2ToDefendingCard: ; 2c149 (b:4149)
ret
; 0x2c166
-Func_2c166: ; 2c166 (b:4166)
+; overwrites in wDamage, wAIMinDamage and wAIMaxDamage
+; with the value in a.
+StoreDamageInfo: ; 2c166 (b:4166)
ld [wDamage], a
ld [wAIMinDamage], a
ld [wAIMaxDamage], a
@@ -301,7 +303,181 @@ Func_2c166: ; 2c166 (b:4166)
ret
; 0x2c174
- INCROM $2c174, $2c6f0
+ INCROM $2c174, $2c1ec
+
+HandleSwitchDefendingPokemonEffect: ; 2c1ec (b:41ec)
+ ld e, a
+ cp $ff
+ ret z
+
+; check Defending Pokemon's HP
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetNonTurnDuelistVariable
+ or a
+ jr nz, .switch
+
+; if 0, handle Destiny Bond first
+ push de
+ bank1call HandleDestinyBondSubstatus
+ pop de
+
+.switch
+ call .HandleNoDamageOrEffect
+ ret c
+
+; attack was successful, switch Defending Pokemon
+ call SwapTurn
+ call SwapArenaWithBenchPokemon
+ call SwapTurn
+
+ xor a
+ ld [wccc5], a
+ ld [wDuelDisplayedScreen], a
+ inc a
+ ld [wccef], a
+ ret
+; 0x2c216
+
+; returns carry if Defending has No Damage or Effect
+; if so, print its appropriate text.
+.HandleNoDamageOrEffect: ; 2c216 (b:4216)
+ call CheckNoDamageOrEffect
+ ret nc
+ ld a, l
+ or h
+ call nz, DrawWideTextBox_PrintText
+ scf
+ ret
+; 0x2c221
+
+ INCROM $2c221, $2c2a4
+
+; makes a list in wDuelTempList with the deck indices
+; of all the energy cards found in opponent's Discard Pile.
+; if (c == 0), all energy cards are allowed;
+; if (c != 0), double colorless energy cards are not counted.
+; returns carry if no energy cards were found.
+CreateEnergyCardListFromOpponentDiscardPile: ; 2c2a4 (b:42a4)
+ ld c, $00
+
+; get number of cards in Discard Pile
+; and have hl point to the end of the
+; Discard Pile list in wOpponentDeckCards.
+ ld a, DUELVARS_NUMBER_OF_CARDS_IN_DISCARD_PILE
+ call GetTurnDuelistVariable
+ ld b, a
+ add DUELVARS_DECK_CARDS
+ ld l, a
+
+ ld de, wDuelTempList
+ inc b
+ jr .next_card
+
+.check_energy
+ ld a, [hl]
+ call LoadCardDataToBuffer2_FromDeckIndex
+ ld a, [wLoadedCard2Type]
+ and TYPE_ENERGY
+ jr z, .next_card
+
+; if (c != $00), then we dismiss Double Colorless
+; energy cards found.
+ ld a, c
+ or a
+ jr z, .copy
+ ld a, [wLoadedCard2Type]
+ cp TYPE_ENERGY_DOUBLE_COLORLESS
+ jr nc, .next_card
+
+.copy
+ ld a, [hl]
+ ld [de], a
+ inc de
+
+; goes through Discard Pile list
+; in wOpponentDeckCards in descending order.
+.next_card
+ dec l
+ dec b
+ jr nz, .check_energy
+
+; terminating byte on wDuelTempList
+ ld a, $ff
+ ld [de], a
+
+; check if any energy card was found
+; by checking whether the first byte
+; in wDuelTempList is $ff.
+; if none were found, return carry.
+ ld a, [wDuelTempList]
+ cp $ff
+ jr z, .set_carry
+ or a
+ ret
+
+.set_carry
+ scf
+ ret
+; 0x2c2e0
+
+ INCROM $2c2e0, $2c487
+
+; handles the selection of a forced switch
+; by link/AI opponent or by the player.
+; outputs the Play Area location of the chosen
+; bench card in hTempPlayAreaLocation_ff9d.
+DuelistSelectForcedSwitch: ; 2c487 (b:4487)
+ ld a, DUELVARS_DUELIST_TYPE
+ call GetNonTurnDuelistVariable
+ cp DUELIST_TYPE_LINK_OPP
+ jr z, .link_opp
+
+ cp DUELIST_TYPE_PLAYER
+ jr z, .player
+
+; AI opponent
+ call SwapTurn
+ bank1call AIDoAction_ForcedSwitch
+ call SwapTurn
+
+ ld a, [wPlayerAttackingMoveIndex]
+ ld e, a
+ ld a, [wPlayerAttackingCardIndex]
+ ld d, a
+ ld a, [wPlayerAttackingCardID]
+ call CopyMoveDataAndDamage_FromCardID
+ call Func_16f6
+ ret
+
+.player
+ ldtx hl, SelectPkmnOnBenchToSwitchWithActiveText
+ call DrawWideTextBox_WaitForInput
+ call SwapTurn
+ bank1call HasAlivePokemonInBench
+ ld a, $01
+ ld [wcbd4], a
+.asm_2c4c0
+ bank1call OpenPlayAreaScreenForSelection
+ jr c, .asm_2c4c0
+ call SwapTurn
+ ret
+
+.link_opp
+; get selection from link opponent
+ ld a, OPPACTION_FORCE_SWITCH_ACTIVE
+ call SetOppAction_SerialSendDuelData
+.loop
+ call SerialRecvByte
+ jr nc, .received
+ halt
+ nop
+ jr .loop
+.received
+ ldh [hTempPlayAreaLocation_ff9d], a
+ ret
+; 0x2c4da
+
+ INCROM $2c4da, $2c6f0
SpitPoison_AIEffect: ; 2c6f0 (b:46f0)
ld a, 5
@@ -320,7 +496,44 @@ SpitPoison_Poison50PercentEffect: ; 2c6f8 (b:46f8)
ret
; 0x2c70a
- INCROM $2c70a, $2c730
+; outputs in hTemp_ffa0 the result of the coin toss
+; (0 = tails, 1 = heads) and, in case it was heads,
+; stores in hTempPlayAreaLocation_ffa1 the location
+; of the Bench Pokemon that was selected for switch.
+TerrorStrike_50PercentSelectSwitchPokemon: ; 2c70a (b:470a)
+ xor a
+ ldh [hTemp_ffa0], a
+
+; return failure if no Pokemon to switch to
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetNonTurnDuelistVariable
+ cp 2
+ ret c
+
+; toss coin and store whether it was tails (0)
+; or heads (1) in hTemp_ffa0
+; return if it was tails.
+ ldtx de, IfHeadsChangeOpponentsActivePokemonText
+ call Func_2c08a
+ ldh [hTemp_ffa0], a
+ ret nc
+
+ call DuelistSelectForcedSwitch
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ret
+; 0x2c726
+
+; if coin toss was heads and it's possible,
+; switch Defending Pokemon
+TerrorStrike_SwitchDefendingPokemon: ; 2c726 (b:4726)
+ ldh a, [hTemp_ffa0]
+ or a
+ ret z
+ ldh a, [hTempPlayAreaLocation_ffa1]
+ call HandleSwitchDefendingPokemonEffect
+ ret
+; 0x2c730
PoisonFang_AIEffect: ; 2c730 (b:4730)
ld a, 10
@@ -417,7 +630,7 @@ Twineedle_MultiplierEffect: ; 2c7f5 (b:47f5)
add a
add e
call ATimes10
- call Func_2c166
+ call StoreDamageInfo
ret
; 0x2c80d
@@ -538,4 +751,16 @@ Toxic_DoublePoisonEffect: ; 2c994 (b:4994)
ret
; 0x2c998
- INCROM $2c998, $30000
+ INCROM $2c998, $2cbfb
+
+Func_2cbfb: ; 2cbfb (b:4bfb)
+ ldh a, [hAIEnergyTransPlayAreaLocation]
+ ld e, a
+ ldh a, [hAIEnergyTransEnergyCard]
+ call AddCardToHand
+ call PutHandCardInPlayArea
+ bank1call PrintPlayAreaCardList_EnableLCD
+ ret
+; 0x2cc0a
+
+ INCROM $2cc0a, $30000
diff --git a/src/engine/home.asm b/src/engine/home.asm
index 478aa39..78e0bf1 100644
--- a/src/engine/home.asm
+++ b/src/engine/home.asm
@@ -3419,6 +3419,7 @@ CreateArenaOrBenchEnergyCardList: ; 120a (0:120a)
; fill wDuelTempList with the turn holder's hand cards (their 0-59 deck indexes)
; return carry if the turn holder has no cards in hand
+; and outputs in a number of cards.
CreateHandCardList: ; 123b (0:123b)
call FindLastCardInHand
inc b
@@ -5218,7 +5219,7 @@ Func_1bca: ; 1bca (0:1bca)
ret
; 0x1c05
-; return in a the retreat cost of the turn holder's arena or benchx Pokemon
+; return in a the retreat cost of the turn holder's arena or bench Pokemon
; given the PLAY_AREA_* value in hTempPlayAreaLocation_ff9d
GetPlayAreaCardRetreatCost: ; 1c05 (0:1c05)
ldh a, [hTempPlayAreaLocation_ff9d]
@@ -8279,56 +8280,70 @@ LoadOpponentDeck: ; 2b78 (0:2b78)
ld [hl], a
ret
-Func_2bbf: ; 2bbf (0:2bbf)
- ld a, $1
- jr Func_2bdb
+AIDoAction_Turn: ; 2bbf (0:2bbf)
+ ld a, AIACTION_DO_TURN
+ jr AIDoAction
-Func_2bc3: ; 2bc3 (0:2bc3)
- ld a, $2
- jr Func_2bdb
+AIDoAction_StartDuel: ; 2bc3 (0:2bc3)
+ ld a, AIACTION_START_DUEL
+ jr AIDoAction
-Func_2bc7: ; 2bc7 (0:2bc7)
- ld a, $3
- call Func_2bdb
+AIDoAction_ForcedSwitch: ; 2bc7 (0:2bc7)
+ ld a, AIACTION_FORCED_SWITCH
+ call AIDoAction
ldh [hTempPlayAreaLocation_ff9d], a
ret
-Func_2bcf: ; 2bcf (0:2bcf)
- ld a, $4
- call Func_2bdb
+AIDoAction_KOSwitch: ; 2bcf (0:2bcf)
+ ld a, AIACTION_KO_SWITCH
+ call AIDoAction
ldh [hTemp_ffa0], a
ret
-Func_2bd7: ; 2bd7 (0:2bd7)
- ld a, $5
- jr Func_2bdb
+AIDoAction_TakePrize: ; 2bd7 (0:2bd7)
+ ld a, AIACTION_TAKE_PRIZE
+ jr AIDoAction ; this line is not needed
-Func_2bdb: ; 2bdb (0:2bdb)
+; calls the appropriate AI routine to handle action,
+; depending on the deck ID (see engine/deck_ai/deck_ai.asm)
+; input:
+; - a = AIACTION_* constant
+AIDoAction: ; 2bdb (0:2bdb)
ld c, a
+
+; load bank for Opponent Deck pointer table
ldh a, [hBankROM]
push af
- ld a, BANK(PointerTable_14000)
+ ld a, BANK(DeckAIPointerTable)
call BankswitchROM
+
+; load hl with the corresponding pointer
ld a, [wOpponentDeckID]
ld l, a
ld h, $0
- add hl, hl
- ld de, PointerTable_14000
+ add hl, hl ; two bytes per deck
+ ld de, DeckAIPointerTable
add hl, de
ld a, [hli]
ld h, [hl]
ld l, a
+
ld a, c
or a
- jr nz, .asm_2bfe
+ jr nz, .not_zero
+
+; if input was 0, copy deck data of turn player
ld e, [hl]
inc hl
ld d, [hl]
call CopyDeckData
- jr .asm_2c01
-.asm_2bfe
+ jr .done
+
+; jump to corresponding AI routine related to input
+.not_zero
call JumpToFunctionInTable
-.asm_2c01
+
+.done
ld c, a
pop af
call BankswitchROM
diff --git a/src/hram.asm b/src/hram.asm
index 9cdba06..f22b172 100644
--- a/src/hram.asm
+++ b/src/hram.asm
@@ -83,12 +83,35 @@ hTemp_ffa0:: ; ffa0
; a PLAY_AREA_* constant (0: arena card, 1-5: bench card)
hTempPlayAreaLocation_ffa1:: ; ffa1
+; parameter to be used by the AI's Pkmn Power effect
+hAIPkmnPowerEffectParam:: ; ffa1
ds $1
+UNION
+
; $ff-terminated list of cards to be discarded upon retreat
hTempRetreatCostCards:: ; ffa2
ds $6
+NEXTU
+
+; parameters chosen by AI in Energy Trans routine.
+; the deck index (0-59) of the energy card to transfer
+; and the Play Area location (PLAY_AREA_*) of card to receive that energy card.
+hAIEnergyTransEnergyCard:: ; ffa2
+ ds $1
+hAIEnergyTransPlayAreaLocation:: ; ffa3
+ ds $1
+
+NEXTU
+
+; Play Area location (PLAY_AREA_*) of card
+; chosen by AI to use Heal Pkmn Power on.
+hAIHealCard:: ; ffa2
+ ds $1
+
+ENDU
+
; hffa8 through hffb0 belong to the text engine
hffa8:: ; ffa8
ds $1
diff --git a/src/text/text1.asm b/src/text/text1.asm
index 69f44f6..07d7a7a 100644
--- a/src/text/text1.asm
+++ b/src/text/text1.asm
@@ -1103,7 +1103,7 @@ Text00ea: ; 37e03 (d:7e03)
line "Heads, damage to yours if Tails."
done
-Text00eb: ; 37e46 (d:7e46)
+IfHeadsChangeOpponentsActivePokemonText: ; 37e46 (d:7e46)
text "If Heads, change opponent's"
line "Active Pokémon."
done
diff --git a/src/text/text_offsets.asm b/src/text/text_offsets.asm
index 3149307..d027768 100644
--- a/src/text/text_offsets.asm
+++ b/src/text/text_offsets.asm
@@ -236,7 +236,7 @@ TextOffsets:: ; 34000 (d:40
textpointer Text00e8 ; 0x00e8
textpointer IfHeadPlus10IfTails10ToYourselfText ; 0x00e9
textpointer Text00ea ; 0x00ea
- textpointer Text00eb ; 0x00eb
+ textpointer IfHeadsChangeOpponentsActivePokemonText ; 0x00eb
textpointer Text00ec ; 0x00ec
textpointer Text00ed ; 0x00ed
textpointer Text00ee ; 0x00ee
diff --git a/src/wram.asm b/src/wram.asm
index 60a8bc5..acd9638 100644
--- a/src/wram.asm
+++ b/src/wram.asm
@@ -967,7 +967,7 @@ wNoDamageOrEffect:: ; ccc7
; used by CountKnockedOutPokemon and Func_5805 to store the amount
; of prizes to take (equal to the number of Pokemon knocked out)
-wccc8:: ; ccc8
+wNumberPrizeCardsToTake:: ; ccc8
ds $1
; set to 1 if the coin toss in the confusion check is heads (CheckSelfConfusionDamage)
@@ -1182,26 +1182,66 @@ wcd9f:: ; cd9f
wcda5:: ; cda5
ds $1
-wcda6:: ; cda6
+; this is used by AI in order to determine whether
+; it should use Pokedex Trainer card.
+; starts with 5 when Duel starts and counts up by 1 every turn.
+; only when it's higher than 5 is AI allowed to use Pokedex,
+; in which case it sets the counter to 0.
+; this stops the AI from using Pokedex right after using another one
+; while still drawing cards that were ordered.
+wAIPokedexCounter:: ; cda6
ds $1
-wcda7:: ; cda7
+; variable to keep track of Mewtwo1's Barrier usage during Player' turn.
+; AI_FLAG_MEWTWO_MILL set means Player is running Mewtwo1 mill deck.
+; - when flag is not set, this counts how many turns in a row
+; Player used Mewtwo1's Barrier attack;
+; - when flag is set, this counts how many turns in a row
+; Player has NOT used Barrier attack.
+wAIBarrierFlagCounter:: ; cda7
ds $1
- ds $6
+; pointer to $00-terminated list of card IDs
+; to avoid being placed as prize cards
+; when setting up AI duelist's cards at duel start.
+; (see SetUpBossStartingHandAndDeck)
+wAICardListAvoidPrize:: ; cda8
+ ds $2
-; pointer to a list of card IDs for sorting AI hand
-wcdae:: ; cdae
+; pointer to $00-terminated list of card IDs
+; sorted by priority of AI placing in the Arena
+; at duel start (see TrySetUpBossStartingPlayArea)
+wAICardListArenaPriority:: ; cdaa
ds $2
+; pointer to $00-terminated list of card IDs
+; sorted by priority of AI placing in the Bench
+; at duel start (see TrySetUpBossStartingPlayArea)
+wAICardListBenchPriority:: ; cdac
ds $2
-; these seem to hold pointer to some kind of
-; card ID list with attached energy and score
-wcdb2:: ; cdb2
- ds $1
-wcdb3:: ; cdb3
- ds $1
+; pointer to $00-terminated list of card IDs
+; sorted by priority of AI playing it from Hand
+; to the Bench (see AIDecidePlayPokemonCard)
+wAICardListPlayFromHandPriority:: ; cdae
+ ds $2
+
+; pointer to $00-terminated list of card IDs and AI scores.
+; these are for giving certain cards more or less
+; likelihood of being picked by AI to switch to.
+; (see AIDecideBenchPokemonToSwitchTo)
+wAICardListRetreatBonus:: ; cdb0
+ ds $2
+
+; pointer to $00-terminated list of card IDs,
+; number of energy cards and AI score.
+; these are for giving certain cards more or less
+; likelihood of being picked for AI to attach energy.
+; also has the maximum number of energy cards that
+; the AI is willing to provide for it.
+; (see AIProcessEnergyCards)
+wAICardListEnergyBonus:: ; cdb2
+ ds $2
wcdb4:: ; cdb4
ds $1
@@ -1230,12 +1270,22 @@ wTempCardType:: ; cdba
wAIScore:: ; cdbe
ds $1
+UNION
+
; used for AI decisions that involve
; each card in the Play Area.
wPlayAreaAIScore:: ; cdbf
ds MAX_PLAY_AREA_POKEMON
- ds $0a
+NEXTU
+
+; stores the score determined by AI for first attack
+wFirstAttackAIScore:: ; cdbf
+ ds $1
+
+ENDU
+
+ ds $a
; information about the defending Pokémon and
; the prize card count on both sides for AI:
@@ -1259,7 +1309,9 @@ wAIOpponentPrizeCount:: ; cdd3
wTempCardIDToLook:: ; cdd4
ds $1
-wcdd5:: ; cdd5
+; when AI decides which Bench Pokemon to switch to
+; it stores it Play Area location here.
+wAIPlayAreaCardToSwitch:: ; cdd5
ds $1
; the index of attack chosen by AI
@@ -1274,10 +1326,16 @@ wAIPluspowerAttack:: ; cdd6
wAIPlayEnergyCardForRetreat:: ; cdd7
ds $1
-wcdd8:: ; cdd8
+; flags defined by AI_ENERGY_FLAG_* constants
+; used as input for AIProcessEnergyCards
+; to determine what to check in the routine.
+wAIEnergyAttachLogicFlags:: ; cdd8
ds $1
-wcdd9:: ; cdd9
+; used as input to AIProcessAttacks.
+; if 0, execute the attack chosen by the AI.
+; if not 0, return without executing attack.
+wAIExecuteProcessedAttack:: ; cdd9
ds $1
wcdda:: ; cdda
@@ -1289,15 +1347,17 @@ wcddb:: ; cddb
wcddc:: ; cddc
ds $1
-; used to compliment wPlayAreaAIScore,
-; to temporarily do calculations and store results.
+; used to temporarily backup wPlayAreaAIScore values.
wTempPlayAreaAIScore:: ; cddd
ds MAX_PLAY_AREA_POKEMON
-wcde3:: ; cde3
+wTempAIScore:: ; cde3
ds $1
-wcde4:: ; cde4
+; used for AI decisions that involve
+; each card in the Play Area involving
+; attaching Energy cards.
+wPlayAreaEnergyAIScore:: ; cde4
ds MAX_PLAY_AREA_POKEMON
wcdea:: ; cdea
@@ -1352,12 +1412,25 @@ wce01:: ; ce01
wAIMoveIsNonDamaging:: ; ce02
ds $1
-wce03:: ; ce03
+; whether AI already retreated this turn or not.
+; - $0 has not retreated;
+; - $1 has retreated.
+wAIRetreatedThisTurn:: ; ce03
ds $1
- ds $2
+; used by AI to store information of Venusaur2
+; while handling Energy Trans logic.
+wAIVenusaur2DeckIndex:: ; ce04
+ ds $1
+wAIVenusaur2PlayAreaLocation:: ; ce05
+ ds $1
wce06:: ; ce06
+; number of cards to be transferred by AI using Energy Trans.
+wAINumberOfEnergyTransCards::
+; used for storing weakness of Player's Arena card
+; in AI routine dealing with Shift Pkmn Power.
+wAIDefendingPokemonWeakness::
ds $1
wce07:: ; ce07