summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorElectroDeoxys <ElectroDeoxys@gmail.com>2020-04-28 20:20:20 +0100
committerElectroDeoxys <ElectroDeoxys@gmail.com>2020-04-28 20:20:20 +0100
commit7d9ea8a30d19fae6a39bb36981e27db6edbea5e7 (patch)
tree632526b60f8642fa2362e6a5582799e906b28718 /src
parente38d9f1de3abe1cdb54e7292ead677151348bd4e (diff)
AI routines and Energy Trans logic
Diffstat (limited to 'src')
-rw-r--r--src/constants/duel_constants.asm41
-rw-r--r--src/engine/bank01.asm1
-rw-r--r--src/engine/bank05.asm589
-rw-r--r--src/engine/bank08.asm447
-rw-r--r--src/engine/effect_functions.asm92
-rw-r--r--src/engine/home.asm2
-rw-r--r--src/hram.asm14
-rw-r--r--src/wram.asm39
8 files changed, 1013 insertions, 212 deletions
diff --git a/src/constants/duel_constants.asm b/src/constants/duel_constants.asm
index 65f58e3..b667c50 100644
--- a/src/constants/duel_constants.asm
+++ b/src/constants/duel_constants.asm
@@ -232,21 +232,32 @@ 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.
; 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
+ 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
+ const AI_TRAINER_CARD_PHASE_15 ; $f
diff --git a/src/engine/bank01.asm b/src/engine/bank01.asm
index b73b47c..7668903 100644
--- a/src/engine/bank01.asm
+++ b/src/engine/bank01.asm
@@ -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
diff --git a/src/engine/bank05.asm b/src/engine/bank05.asm
index ee45de4..ba0a565 100644
--- a/src/engine/bank05.asm
+++ b/src/engine/bank05.asm
@@ -74,8 +74,8 @@ Func_14078: ; 14078 (5:4078)
call AIDecideBenchPokemonToSwitchTo
call AIChooseEnergyToDiscardForRetreatCost
.asm_14091
- call AIDecidePlayEnergyCardFromHand
- call Func_169f8
+ call AIProcessAndTryToPlayEnergy
+ call AIProcessAndTryToUseAttack
ret c
ld a, OPPACTION_FINISH_NO_ATTACK
bank1call AIMakeDecision
@@ -220,8 +220,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;
@@ -1145,7 +1181,7 @@ 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_1467f ; deciding which Bench Pokemon to switch to
dw Func_14683
dw Func_14687
@@ -1160,11 +1196,11 @@ Func_14678: ; 14678 (5:4678)
ret
Func_1467f: ; 1467f (5:467f)
- call $5b72
+ call AIDecideBenchPokemonToSwitchTo
ret
Func_14683: ; 14683 (5:4683)
- call $5b72
+ call AIDecideBenchPokemonToSwitchTo
ret
Func_14687: ; 14687 (5:4687)
@@ -1174,105 +1210,108 @@ Func_14687: ; 14687 (5:4687)
; AI for general decks i think
Func_1468b: ; 1468b (5:468b)
call Func_15649
- ld a, $1
+
+ ld a, AI_TRAINER_CARD_PHASE_01
call AIProcessHandTrainerCards
- farcall $8, $67d3
- jp nc, $4776
- farcall $8, $6790
- farcall $8, $66a3
- farcall $8, $637f
+ farcall Func_227d3
+ jp nc, .asm_14776
+
+ farcall Func_22790
+ farcall Func_226a3
+ farcall Func_2237f
ret c
- farcall $8, $662d
- ld a, $2
+
+ farcall Func_2262d
+
+ ld a, AI_TRAINER_CARD_PHASE_02
call AIProcessHandTrainerCards
- ld a, $3
+ ld a, AI_TRAINER_CARD_PHASE_03
call AIProcessHandTrainerCards
- ld a, $4
+ ld a, AI_TRAINER_CARD_PHASE_04
call AIProcessHandTrainerCards
- call $5eae
+ call AIDecidePlayPokemonCard
ret c
- ld a, $5
+
+ ld a, AI_TRAINER_CARD_PHASE_05
call AIProcessHandTrainerCards
- ld a, $6
+ ld a, AI_TRAINER_CARD_PHASE_06
call AIProcessHandTrainerCards
- ld a, $7
+ ld a, AI_TRAINER_CARD_PHASE_07
call AIProcessHandTrainerCards
- ld a, $8
+ ld a, AI_TRAINER_CARD_PHASE_08
call AIProcessHandTrainerCards
- call $4786
- ld a, $a
+ call Func_14786
+ ld a, AI_TRAINER_CARD_PHASE_10
call AIProcessHandTrainerCards
- ld a, $b
+ ld a, AI_TRAINER_CARD_PHASE_11
call AIProcessHandTrainerCards
- ld a, $c
+ ld a, AI_TRAINER_CARD_PHASE_12
call AIProcessHandTrainerCards
ld a, [wAlreadyPlayedEnergy]
or a
jr nz, .asm_146ed
- call $64e8
-
+ call AIProcessAndTryToPlayEnergy
.asm_146ed
- call $5eae
- farcall $8, $66a3
- farcall $8, $637f
+ call AIDecidePlayPokemonCard
+ farcall Func_226a3
+ farcall Func_2237f
ret c
- farcall $8, $6790
- ld a, $d
- farcall $8, $619b
- ld a, $d
+ farcall Func_22790
+ ld a, $0d
+ farcall HandleAIEnergyTrans
+ ld a, AI_TRAINER_CARD_PHASE_13
call AIProcessHandTrainerCards
- ld a, $f
+ ld a, AI_TRAINER_CARD_PHASE_15
call AIProcessHandTrainerCards
ld a, [wPreviousAIFlags]
- and AI_FLAG_USED_PROFESSOR_OAK
+ and $04
jr z, .asm_14776
- ld a, $1
+ ld a, AI_TRAINER_CARD_PHASE_01
call AIProcessHandTrainerCards
- ld a, $2
+ ld a, AI_TRAINER_CARD_PHASE_02
call AIProcessHandTrainerCards
- ld a, $3
+ ld a, AI_TRAINER_CARD_PHASE_03
call AIProcessHandTrainerCards
- ld a, $4
+ ld a, AI_TRAINER_CARD_PHASE_04
call AIProcessHandTrainerCards
- call $5eae
+ call AIDecidePlayPokemonCard
ret c
- ld a, $5
+ ld a, AI_TRAINER_CARD_PHASE_05
call AIProcessHandTrainerCards
- ld a, $6
+ ld a, AI_TRAINER_CARD_PHASE_06
call AIProcessHandTrainerCards
- ld a, $7
+ ld a, AI_TRAINER_CARD_PHASE_07
call AIProcessHandTrainerCards
- ld a, $8
+ ld a, AI_TRAINER_CARD_PHASE_08
call AIProcessHandTrainerCards
- call $4786
- ld a, $a
+ call Func_14786
+ ld a, AI_TRAINER_CARD_PHASE_10
call AIProcessHandTrainerCards
- ld a, $b
+ ld a, AI_TRAINER_CARD_PHASE_11
call AIProcessHandTrainerCards
- ld a, $c
+ ld a, AI_TRAINER_CARD_PHASE_12
call AIProcessHandTrainerCards
ld a, [wAlreadyPlayedEnergy]
or a
jr nz, .asm_1475b
- call $64e8
-
+ call AIProcessAndTryToPlayEnergy
.asm_1475b
- call $5eae
- farcall $8, $66a3
- farcall $8, $637f
+ call AIDecidePlayPokemonCard
+ farcall Func_226a3
+ farcall Func_2237f
ret c
- farcall $8, $6790
- ld a, $d
- farcall $8, $619b
- ld a, $d
+ farcall Func_22790
+ ld a, $0d
+ farcall HandleAIEnergyTrans
+ ld a, AI_TRAINER_CARD_PHASE_13
call AIProcessHandTrainerCards
.asm_14776
- ld a, $e
- farcall $8, $619b
- call $69f8
+ ld a, $0e
+ farcall HandleAIEnergyTrans
+ call AIProcessAndTryToUseAttack
ret c
- ld a, $5
+ ld a, OPPACTION_FINISH_NO_ATTACK
bank1call AIMakeDecision
ret
; 0x14786
@@ -1735,6 +1774,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
@@ -2207,7 +2248,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
@@ -2324,7 +2365,8 @@ Func_15b54: ; 15b54 (5:5b54)
; calculates AI score for bench Pokémon
; returns in hTempPlayAreaLocation_ff9d the
-; Play Area location of best card to switch to
+; 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 +2418,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]
@@ -4006,47 +4048,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 +4118,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 +4162,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 +4181,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 +4193,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
@@ -4157,27 +4214,27 @@ AIDecideWhichCardToAttachEnergy: ; 164fc (5:64fc)
; arena
ld a, [wcda7]
bit 7, a
- jr z, .check_arena_hp
+ jr z, .skip_subtracting_score
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
+.skip_subtracting_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
@@ -4288,11 +4345,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 +4394,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 +4620,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 +4661,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 +4684,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 +4755,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 +4819,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 +5080,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 +5097,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 +5123,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
@@ -5077,23 +5146,23 @@ Func_169fc: ; 169fc (5:69fc)
.no_pluspower
ld a, [wcda7]
cp $80
- jp z, .asm_16a77
+ 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 +5172,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 +5184,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 +5207,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 +5823,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 +5950,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 +6028,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 +6183,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 +6211,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 +6295,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 +6393,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 +6420,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 +6480,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
@@ -6591,7 +6815,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
@@ -6825,7 +7049,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 +7072,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 +7084,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 +7104,7 @@ Func_174f2: ; 174f2 (5:74f2)
ld a, [hli]
cp $ff
ret z
+
ld [wcdf9], a
push de
push hl
@@ -6945,11 +7170,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..6850b82 100644
--- a/src/engine/bank08.asm
+++ b/src/engine/bank08.asm
@@ -6219,8 +6219,449 @@ AIDecide_PokemonTrader_Flamethrower: ; 22133 (8:6133)
ret
; 0x2219b
-Func_2219b: ; 2219b (8:219b)
- INCROM $2219b, $227f6
+; handle AI routines for Energy Trans.
+; depending on input, AI can use Energy Trans to
+; give Arena or Bench cards some Grass energy cards,
+; depending whether it's for attack, retreat, etc.
+HandleAIEnergyTrans: ; 2219b (8:619b)
+ ld [wce06], a
+
+; choose to randomly return
+ farcall ChooseRandomlyNotToPlayTrainerCard
+ 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 $09
+ jr z, .check_retreat
+
+ cp $0e
+ jp z, .TransferEnergyToBench
+
+ 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_1
+ 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_1
+
+ ld a, b
+ or a
+ ret z ; return when finished Play Area loop
+
+ dec b
+ jr .loop_play_area_1
+
+; use Energy Trans Pkmn Power
+.use_pkmn_power_1
+ 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_1
+ ld a, DUELVARS_CARD_LOCATIONS
+ add e
+ call GetTurnDuelistVariable
+ and %00011111
+ cp CARD_LOCATION_BENCH_1
+ jr c, .next_card_1
+
+ 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_1
+
+ ; store the deck index of energy card
+ ld a, e
+ ldh [hAIEnergyTransEnergyCard], a
+
+ push de
+ ld d, 30
+.small_delay_1
+ call DoFrame
+ dec d
+ jr nz, .small_delay_1
+
+ ld a, OPPACTION_6B15
+ bank1call AIMakeDecision
+ pop de
+ dec d
+ jr z, .done_transfer_1
+
+.next_card_1
+ inc e
+ ld a, DECK_SIZE
+ cp e
+ jr nz, .loop_deck_locations_1
+
+; transfer is done, perform delay
+; and return to main scene.
+.done_transfer_1
+ ld d, 60
+.big_delay_1
+ call DoFrame
+ dec d
+ jr nz, .big_delay_1
+ ld a, OPPACTION_DUEL_MAIN_SCENE
+ bank1call AIMakeDecision
+ ret
+; 0x22246
+
+; checks if the Arena card has not enough energy for second attack,
+; and if not, return carry if transferring Grass energy from Bench
+; would be enough to use it and 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.
+.TransferEnergyToBench ; 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_2
+ 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_2
+
+ ld a, b
+ or a
+ ret z ; return when Play Area loop is ended
+
+ dec b
+ jr .loop_play_area_2
+
+; use Energy Trans Pkmn Power
+.use_pkmn_power_2
+ 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_2
+
+; look for Grass energy cards that
+; are currently attached to Arena card.
+ ld e, 0
+.loop_deck_locations_2
+ ld a, DUELVARS_CARD_LOCATIONS
+ add e
+ call GetTurnDuelistVariable
+ cp CARD_LOCATION_ARENA
+ jr nz, .next_card_2
+
+ ld a, e
+ push de
+ call GetCardIDFromDeckIndex
+ ld a, e
+ pop de
+ cp GRASS_ENERGY
+ jr nz, .next_card_2
+
+ ; store the deck index of energy card
+ ld a, e
+ ldh [hAIEnergyTransEnergyCard], a
+ jr .transfer
+
+.next_card_2
+ inc e
+ ld a, DECK_SIZE
+ cp e
+ jr nz, .loop_deck_locations_2
+ jr .done_transfer_2
+
+.transfer
+; get the Bench card location to transfer Grass energy card to.
+ farcall AIProcessButDontPlayEnergy_SkipEvolutionAndArena
+ jr nc, .done_transfer_2
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ ldh [hAIEnergyTransPlayAreaLocation], a
+
+ ld d, 30
+.small_delay_2
+ call DoFrame
+ dec d
+ jr nz, .small_delay_2
+
+ 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_2
+ ld d, 60
+.big_delay_2
+ call DoFrame
+ dec d
+ jr nz, .big_delay_2
+ ld a, OPPACTION_DUEL_MAIN_SCENE
+ bank1call AIMakeDecision
+ ret
+; 0x2237f
+
+Func_2237f: ; 2237f (8:237f)
+ INCROM $2237f, $2262d
+
+Func_2262d: ; 2262d (8:262d)
+ INCROM $2262d, $226a3
+
+Func_226a3: ; 226a3 (8:26a3)
+ INCROM $226a3, $22790
+
+Func_22790: ; 22790 (8:2790)
+ INCROM $22790, $227d3
+
+; checks wcda7 and Pokemon in Play Area that are set up.
+; if there's at least 4, goes to AI_TRAINER_CARD_PHASE_05.
+; else, returns carry.
+Func_227d3: ; 227d3 (8:67d3)
+ ld a, [wcda7]
+ bit 7, a
+ jr z, .set_carry
+ cp %10000010
+ jr c, .asm_227e4
+
+ xor a
+ ld [wcda7], a
+ jr .set_carry
+
+.asm_227e4
+ farcall CountNumberOfSetUpBenchPokemon
+ cp 4
+ jr c, .set_carry
+
+ 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 +7569,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/effect_functions.asm b/src/engine/effect_functions.asm
index 8832134..dbda9ad 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
@@ -348,7 +350,77 @@ HandleSwitchDefendingPokemonEffect: ; 2c1ec (b:41ec)
ret
; 0x2c221
- INCROM $2c221, $2c487
+ 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.
@@ -558,7 +630,7 @@ Twineedle_MultiplierEffect: ; 2c7f5 (b:47f5)
add a
add e
call ATimes10
- call Func_2c166
+ call StoreDamageInfo
ret
; 0x2c80d
@@ -679,4 +751,16 @@ Toxic_DoublePoisonEffect: ; 2c994 (b:4994)
ret
; 0x2c998
- INCROM $2c998, $30000 \ No newline at end of file
+ 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 \ No newline at end of file
diff --git a/src/engine/home.asm b/src/engine/home.asm
index 4f9fc31..338dbb0 100644
--- a/src/engine/home.asm
+++ b/src/engine/home.asm
@@ -5218,7 +5218,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]
diff --git a/src/hram.asm b/src/hram.asm
index 9cdba06..2b4fbc9 100644
--- a/src/hram.asm
+++ b/src/hram.asm
@@ -85,10 +85,24 @@ hTemp_ffa0:: ; ffa0
hTempPlayAreaLocation_ffa1:: ; 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
+
+ENDU
+
; hffa8 through hffb0 belong to the text engine
hffa8:: ; ffa8
ds $1
diff --git a/src/wram.asm b/src/wram.asm
index e5fc9c6..2a10fe2 100644
--- a/src/wram.asm
+++ b/src/wram.asm
@@ -1230,11 +1230,21 @@ 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
+NEXTU
+
+; stores the score determined by AI for first attack
+wFirstAttackAIScore:: ; cdbf
+ ds $1
+
+ENDU
+
ds $0a
; information about the defending Pokémon and
@@ -1274,10 +1284,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 +1305,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
@@ -1355,8 +1373,15 @@ wAIMoveIsNonDamaging:: ; ce02
wce03:: ; 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
+; number of cards to be transferred by AI using Energy Trans.
+wAINumberOfEnergyTransCards::
wce06:: ; ce06
ds $1