summaryrefslogtreecommitdiff
path: root/src/engine
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine')
-rw-r--r--src/engine/bank01.asm91
-rw-r--r--src/engine/bank02.asm18
-rw-r--r--src/engine/bank05.asm2655
-rw-r--r--src/engine/bank06.asm261
-rw-r--r--src/engine/bank07.asm298
-rw-r--r--src/engine/bank08.asm7094
-rw-r--r--src/engine/home.asm20
7 files changed, 9889 insertions, 548 deletions
diff --git a/src/engine/bank01.asm b/src/engine/bank01.asm
index d5926df..51fb0ef 100644
--- a/src/engine/bank01.asm
+++ b/src/engine/bank01.asm
@@ -3055,7 +3055,7 @@ PracticeDuelVerify_Turn2: ; 5438 (1:5438)
ld a, [wTempCardID_ccc2]
cp SEAKING
jp nz, ReturnWrongAction
- ld a, [wSelectedMoveIndex]
+ ld a, [wSelectedAttack]
cp 1
jp nz, ReturnWrongAction
ld e, PLAY_AREA_ARENA
@@ -3090,7 +3090,7 @@ PracticeDuelVerify_Turn4: ; 5467 (1:5467)
ld a, [wTempCardID_ccc2]
cp SEAKING
jr nz, ReturnWrongAction
- ld a, [wSelectedMoveIndex]
+ ld a, [wSelectedAttack]
cp 1
jr nz, ReturnWrongAction
ret
@@ -3127,7 +3127,7 @@ PracticeDuelVerify_Turn7Or8: ; 54b7 (1:54b7)
ld a, [wTempCardID_ccc2]
cp STARMIE
jr nz, ReturnWrongAction
- ld a, [wSelectedMoveIndex]
+ ld a, [wSelectedAttack]
cp 1
jr nz, ReturnWrongAction
ret
@@ -6041,7 +6041,7 @@ DisplayOpponentUsedMoveScreen: ; 6635 (1:6635)
ld a, CARDPAGE_POKEMON_OVERVIEW
ld [wCardPageNumber], a
ld hl, wLoadedCard1Move1Name
- ld a, [wSelectedMoveIndex]
+ ld a, [wSelectedAttack]
or a
jr z, .first_move
ld hl, wLoadedCard1Move2Name
@@ -6886,7 +6886,7 @@ OppAction_6b3e: ; 6b3e (1:6b3e)
call SwapTurn
ldh a, [hTempCardIndex_ff9f]
ld [wPlayerAttackingCardIndex], a
- ld a, [wSelectedMoveIndex]
+ ld a, [wSelectedAttack]
ld [wPlayerAttackingMoveIndex], a
ld a, [wTempCardID_ccc2]
ld [wPlayerAttackingCardID], a
@@ -7620,7 +7620,86 @@ PrintThereWasNoEffectFromStatusText: ; 700a (1:700a)
ret
; 0x7045
- INCROM $7045, $70aa
+; returns carry if card at hTempPlayAreaLocation_ff9d
+; is a basic card.
+; otherwise, lists the card indices of all stages in
+; that card location, and returns the card one
+; stage below.
+; input:
+; hTempPlayAreaLocation_ff9d = play area location to check;
+; output:
+; a = card index in hTempPlayAreaLocation_ff9d;
+; d = card index of card one stage below;
+; carry set if card is a basic card.
+GetCardOneStageBelow: ; 7045 (1:7045)
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ add DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call LoadCardDataToBuffer2_FromDeckIndex
+ ld a, [wLoadedCard2Stage]
+ or a
+ jr nz, .not_basic
+ scf
+ ret
+
+.not_basic
+ ld hl, wAllStagesIndices
+ ld a, $ff
+ ld [hli], a
+ ld [hli], a
+ ld [hl], a
+
+; loads deck indices of the stages present in hTempPlayAreaLocation_ff9d.
+; the three stages are loaded consecutively in wAllStagesIndices.
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ or CARD_LOCATION_ARENA
+ ld c, a
+ ld a, DUELVARS_CARD_LOCATIONS
+ call GetTurnDuelistVariable
+.loop
+ ld a, [hl]
+ cp c
+ jr nz, .next
+ ld a, l
+ call LoadCardDataToBuffer2_FromDeckIndex
+ ld a, [wLoadedCard2Type]
+ cp TYPE_ENERGY
+ jr nc, .next
+ ld b, l
+ push hl
+ ld a, [wLoadedCard2Stage]
+ ld e, a
+ ld d, $00
+ ld hl, wAllStagesIndices
+ add hl, de
+ ld [hl], b
+ pop hl
+.next
+ inc l
+ ld a, l
+ cp DECK_SIZE
+ jr c, .loop
+
+; if card at hTempPlayAreaLocation_ff9d is a stage 1, load d with basic card.
+; otherwise if stage 2, load d with the stage 1 card.
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ add DUELVARS_ARENA_CARD_STAGE
+ call GetTurnDuelistVariable
+ ld hl, wAllStagesIndices ; pointing to basic
+ cp STAGE1
+ jr z, .done
+ cp STAGE2 + 1 ; unnecessary check?
+ jr z, .done
+ inc hl ; pointing to stage 1
+.done
+ ld d, [hl]
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ add DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ ld e, a
+ or a
+ ret
+; 0x70aa
; initializes variables when a duel begins, such as zeroing wDuelFinished or wDuelTurns,
; and setting wDuelType based on wPlayerDuelistType and wOpponentDuelistType
diff --git a/src/engine/bank02.asm b/src/engine/bank02.asm
index 108a34d..5a7761e 100644
--- a/src/engine/bank02.asm
+++ b/src/engine/bank02.asm
@@ -1215,7 +1215,7 @@ DrawPlayArea_HandText: ; 8676 (2:4676)
; returns a = $ff if B pressed
HandleCheckMenuInput_YourOrOppPlayArea: ; 86ac (2:46ac)
xor a
- ld [wcfe3], a
+ ld [wPlaysSfx], a
ld a, [wCheckMenuCursorXPosition]
ld d, a
ld a, [wCheckMenuCursorYPosition]
@@ -1283,7 +1283,7 @@ HandleCheckMenuInput_YourOrOppPlayArea: ; 86ac (2:46ac)
.erase
ld a, $01
- ld [wcfe3], a
+ ld [wPlaysSfx], a
push de
call EraseCheckMenuCursor_YourOrOppPlayArea
pop de
@@ -1319,7 +1319,7 @@ HandleCheckMenuInput_YourOrOppPlayArea: ; 86ac (2:46ac)
ret
.sfx
- ld a, [wcfe3]
+ ld a, [wPlaysSfx]
or a
jr z, .draw_cursor
call PlaySFX
@@ -1638,7 +1638,7 @@ LoadCursorTile: ; 8992 (2:4992)
; similar to OpenInPlayAreaScreen_HandleInput
Func_89ae: ; 89ae (2:49ae)
xor a
- ld [wcfe3], a
+ ld [wPlaysSfx], a
ld hl, wce53
ld e, [hl]
@@ -1757,7 +1757,7 @@ Func_89ae: ; 89ae (2:49ae)
.next
ld a, $01
- ld [wcfe3], a
+ ld [wPlaysSfx], a
; reset cursor blink
xor a
@@ -1784,7 +1784,7 @@ Func_89ae: ; 89ae (2:49ae)
ret
.return
- ld a, [wcfe3]
+ ld a, [wPlaysSfx]
or a
jr z, .skip_sfx
call PlaySFX
@@ -2267,7 +2267,7 @@ ResetCheckMenuCursorPositionAndBlink: ; 905a (2:505a)
; returns a = $ff if B pressed
HandleCheckMenuInput: ; 9065 (2:5065)
xor a
- ld [wcfe3], a
+ ld [wPlaysSfx], a
ld a, [wCheckMenuCursorXPosition]
ld d, a
ld a, [wCheckMenuCursorYPosition]
@@ -2304,7 +2304,7 @@ HandleCheckMenuInput: ; 9065 (2:5065)
.okay
ld a, $01
- ld [wcfe3], a
+ ld [wPlaysSfx], a
push de
call EraseCheckMenuCursor
pop de
@@ -2337,7 +2337,7 @@ HandleCheckMenuInput: ; 9065 (2:5065)
ret
.no_input
- ld a, [wcfe3]
+ ld a, [wPlaysSfx]
or a
jr z, .check_blink
call PlaySFX
diff --git a/src/engine/bank05.asm b/src/engine/bank05.asm
index 7e8854f..ee45de4 100644
--- a/src/engine/bank05.asm
+++ b/src/engine/bank05.asm
@@ -58,30 +58,31 @@ PointerTable_1406a: ; 1406a (5:406a)
dw $406c
dw Func_14078
dw Func_14078
- dw $409e
+ dw Func_1409e
dw $40a2
dw $40a6
dw $40aa
Func_14078: ; 14078 (5:4078)
- call Func_15eae
- call Func_158b2
+ call AIDecidePlayPokemonCard
+ call AIDecideWhetherToRetreat
jr nc, .asm_14091
- call Func_15b72
- call Func_15d4f
- call Func_158b2
+ call AIDecideBenchPokemonToSwitchTo
+ call AIChooseEnergyToDiscardForRetreatCost
+ call AIDecideWhetherToRetreat
jr nc, .asm_14091
- call Func_15b72
- call Func_15d4f
+ call AIDecideBenchPokemonToSwitchTo
+ call AIChooseEnergyToDiscardForRetreatCost
.asm_14091
- call Func_164e8
+ call AIDecidePlayEnergyCardFromHand
call Func_169f8
ret c
- ld a, $05
+ ld a, OPPACTION_FINISH_NO_ATTACK
bank1call AIMakeDecision
ret
; 0x1409e
+Func_1409e: ; 1409e (5:409e)
INCROM $1409e, $140ae
; returns carry if damage dealt from any of
@@ -90,7 +91,7 @@ Func_14078: ; 14078 (5:4078)
; input:
; [hTempPlayAreaLocation_ff9d] = location of attacking card to consider
; output:
-; [wSelectedMoveIndex] = move index that KOs
+; [wSelectedAttack] = move index that KOs
CheckIfAnyMoveKnocksOutDefendingCard: ; 140ae (5:40ae)
xor a ; first move
call CheckIfMoveKnocksOutDefendingCard
@@ -110,7 +111,29 @@ CheckIfMoveKnocksOutDefendingCard: ; 140b5 (5:40b5)
ret
; 0x140c5
- INCROM $140c5, $140df
+; returns carry if any of the defending Pokémon's attacks
+; brings card at hTempPlayAreaLocation_ff9d down
+; to exactly 0 HP.
+; outputs that attack index in wSelectedMove.
+CheckIfAnyDefendingPokemonAttackDealsSameDamageAsHP: ; 140c5 (5:40c5)
+ xor a ; FIRST_ATTACK_OR_PKMN_POWER
+ call .check_damage
+ ret c
+ ld a, SECOND_ATTACK
+
+.check_damage
+ call EstimateDamage_FromDefendingPokemon
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ add DUELVARS_ARENA_CARD_HP
+ call GetTurnDuelistVariable
+ ld hl, wDamage
+ sub [hl]
+ jr z, .true
+ ret
+.true
+ scf
+ ret
+; 0x140df
; checks AI scores for all benched Pokémon
; returns the location of the card with highest score
@@ -122,7 +145,7 @@ FindHighestBenchScore: ; 140df (5:40df)
ld c, 0
ld e, c
ld d, c
- ld hl, wBenchAIScore + 1
+ ld hl, wPlayAreaAIScore + 1
jp .next
.loop
@@ -202,7 +225,7 @@ Func_14145: ; 14145 (5:4145)
; return carry if any of the following is satisfied:
; - deck index in a corresponds to a double colorless energy card;
-; - card type in wTempCardType is double colorless energy;
+; - card type in wTempCardType is colorless;
; - card ID in wTempCardID is a Pokémon card that has
; moves that require energy other than its color and
; the deck index in a corresponds to that energy type;
@@ -303,8 +326,8 @@ Func_14226: ; 14226 (5:4226)
; can't use a move or if that selected move doesn't have enough energy
; input:
; [hTempPlayAreaLocation_ff9d] = location of Pokémon card
-; [wSelectedMoveIndex] = selected move to examine
-CheckIfCardCanUseSelectedMove: ; 1424b (5:424b)
+; [wSelectedAttack] = selected move to examine
+CheckIfSelectedMoveIsUnusable: ; 1424b (5:424b)
ldh a, [hTempPlayAreaLocation_ff9d]
or a
jr nz, .bench
@@ -317,7 +340,7 @@ CheckIfCardCanUseSelectedMove: ; 1424b (5:424b)
ld a, DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
ld d, a
- ld a, [wSelectedMoveIndex]
+ ld a, [wSelectedAttack]
ld e, a
call CopyMoveDataAndDamage_FromDeckIndex
call HandleAmnesiaSubstatus
@@ -329,7 +352,7 @@ CheckIfCardCanUseSelectedMove: ; 1424b (5:424b)
.bench
call CheckEnergyNeededForAttack
ret c ; can't be used
- ld a, $0d ; $00001101
+ ld a, MOVE_FLAG2_ADDRESS | FLAG_2_BIT_5_F
call CheckLoadedMoveFlag
ret
; 0x14279
@@ -338,10 +361,10 @@ CheckIfCardCanUseSelectedMove: ; 1424b (5:424b)
; and checks if there is enough energy to execute the selected move
; input:
; [hTempPlayAreaLocation_ff9d] = location of Pokémon card
-; [wSelectedMoveIndex] = selected move to examine
+; [wSelectedAttack] = selected move to examine
; output:
-; b = colorless energy still needed
-; c = basic energy still needed
+; b = basic energy still needed
+; c = colorless energy still needed
; e = output of ConvertColorToEnergyCardID, or $0 if not a move
; carry set if no move
; OR if it's a Pokémon Power
@@ -351,23 +374,23 @@ CheckEnergyNeededForAttack: ; 14279 (5:4279)
add DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
ld d, a
- ld a, [wSelectedMoveIndex]
+ ld a, [wSelectedAttack]
ld e, a
call CopyMoveDataAndDamage_FromDeckIndex
ld hl, wLoadedMoveName
ld a, [hli]
or [hl]
- jr z, .no_move
+ jr z, .no_attack
ld a, [wLoadedMoveCategory]
cp POKEMON_POWER
- jr nz, .is_move
-.no_move
+ jr nz, .is_attack
+.no_attack
lb bc, 0, 0
ld e, c
scf
ret
-.is_move
+.is_attack
ldh a, [hTempPlayAreaLocation_ff9d]
ld e, a
call GetPlayAreaCardAttachedEnergies
@@ -611,7 +634,7 @@ CreateEnergyCardListFromHand: ; 1438c (5:438c)
; looks for card ID in hand and
; sets carry if a card wasn't found
-; as opposed to LookForCardIDInHandList
+; as opposed to LookForCardIDInHandList_Bank5
; this function doesn't create a list
; and preserves hl, de and bc
; input:
@@ -663,7 +686,7 @@ LookForCardIDInHand: ; 143bf (5:43bf)
; a = move index to take into account
; [hTempPlayAreaLocation_ff9d] = location of attacking card to consider
EstimateDamage_VersusDefendingCard: ; 143e5 (5:43e5)
- ld [wSelectedMoveIndex], a
+ ld [wSelectedAttack], a
ld e, a
ldh a, [hTempPlayAreaLocation_ff9d]
add DUELVARS_ARENA_CARD
@@ -672,7 +695,7 @@ EstimateDamage_VersusDefendingCard: ; 143e5 (5:43e5)
call CopyMoveDataAndDamage_FromDeckIndex
ld a, [wLoadedMoveCategory]
cp POKEMON_POWER
- jr nz, .is_move
+ jr nz, .is_attack
; is a Pokémon Power
; set wDamage, wAIMinDamage and wAIMaxDamage to zero
@@ -686,7 +709,7 @@ EstimateDamage_VersusDefendingCard: ; 143e5 (5:43e5)
ld d, a
ret
-.is_move
+.is_attack
; set wAIMinDamage and wAIMaxDamage to damage of move
; these values take into account the range of damage
; that the move can span (e.g. min and max number of hits)
@@ -880,7 +903,7 @@ _CalculateDamage_VersusDefendingPokemon: ; 14462 (5:4462)
; damage as the receiver
EstimateDamage_FromDefendingPokemon: ; 1450b (5:450b)
call SwapTurn
- ld [wSelectedMoveIndex], a
+ ld [wSelectedAttack], a
ld e, a
ld a, DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
@@ -889,7 +912,7 @@ EstimateDamage_FromDefendingPokemon: ; 1450b (5:450b)
call SwapTurn
ld a, [wLoadedMoveCategory]
cp POKEMON_POWER
- jr nz, .is_move
+ jr nz, .is_attack
; is a Pokémon Power
; set wDamage, wAIMinDamage and wAIMaxDamage to zero
@@ -903,7 +926,7 @@ EstimateDamage_FromDefendingPokemon: ; 1450b (5:450b)
ld d, a
ret
-.is_move
+.is_attack
; set wAIMinDamage and wAIMaxDamage to damage of move
; these values take into account the range of damage
; that the move can span (e.g. min and max number of hits)
@@ -1112,8 +1135,8 @@ _CalculateDamage_FromDefendingPokemon: ; 1459b (5:459b)
ret
; 0x14663
-Func_14663: ; 14663 (5:4663)
- farcall Func_200e5
+AIProcessHandTrainerCards: ; 14663 (5:4663)
+ farcall _AIProcessHandTrainerCards
ret
; GENERAL DECK POINTER LIST - Not sure on all of these.
@@ -1152,7 +1175,7 @@ Func_14687: ; 14687 (5:4687)
Func_1468b: ; 1468b (5:468b)
call Func_15649
ld a, $1
- call Func_14663
+ call AIProcessHandTrainerCards
farcall $8, $67d3
jp nc, $4776
farcall $8, $6790
@@ -1161,28 +1184,28 @@ Func_1468b: ; 1468b (5:468b)
ret c
farcall $8, $662d
ld a, $2
- call Func_14663
+ call AIProcessHandTrainerCards
ld a, $3
- call Func_14663
+ call AIProcessHandTrainerCards
ld a, $4
- call Func_14663
+ call AIProcessHandTrainerCards
call $5eae
ret c
ld a, $5
- call Func_14663
+ call AIProcessHandTrainerCards
ld a, $6
- call Func_14663
+ call AIProcessHandTrainerCards
ld a, $7
- call Func_14663
+ call AIProcessHandTrainerCards
ld a, $8
- call Func_14663
+ call AIProcessHandTrainerCards
call $4786
ld a, $a
- call Func_14663
+ call AIProcessHandTrainerCards
ld a, $b
- call Func_14663
+ call AIProcessHandTrainerCards
ld a, $c
- call Func_14663
+ call AIProcessHandTrainerCards
ld a, [wAlreadyPlayedEnergy]
or a
jr nz, .asm_146ed
@@ -1197,37 +1220,37 @@ Func_1468b: ; 1468b (5:468b)
ld a, $d
farcall $8, $619b
ld a, $d
- call Func_14663
+ call AIProcessHandTrainerCards
ld a, $f
- call Func_14663
- ld a, [wce20]
- and $4
+ call AIProcessHandTrainerCards
+ ld a, [wPreviousAIFlags]
+ and AI_FLAG_USED_PROFESSOR_OAK
jr z, .asm_14776
ld a, $1
- call Func_14663
+ call AIProcessHandTrainerCards
ld a, $2
- call Func_14663
+ call AIProcessHandTrainerCards
ld a, $3
- call Func_14663
+ call AIProcessHandTrainerCards
ld a, $4
- call Func_14663
+ call AIProcessHandTrainerCards
call $5eae
ret c
ld a, $5
- call Func_14663
+ call AIProcessHandTrainerCards
ld a, $6
- call Func_14663
+ call AIProcessHandTrainerCards
ld a, $7
- call Func_14663
+ call AIProcessHandTrainerCards
ld a, $8
- call Func_14663
+ call AIProcessHandTrainerCards
call $4786
ld a, $a
- call Func_14663
+ call AIProcessHandTrainerCards
ld a, $b
- call Func_14663
+ call AIProcessHandTrainerCards
ld a, $c
- call Func_14663
+ call AIProcessHandTrainerCards
ld a, [wAlreadyPlayedEnergy]
or a
jr nz, .asm_1475b
@@ -1242,7 +1265,7 @@ Func_1468b: ; 1468b (5:468b)
ld a, $d
farcall $8, $619b
ld a, $d
- call Func_14663
+ call AIProcessHandTrainerCards
.asm_14776
ld a, $e
@@ -1257,10 +1280,12 @@ Func_1468b: ; 1468b (5:468b)
Func_14786: ; 14786 (5:4786)
INCROM $14786, $14c91
-; if the player has more than 3 prize cards
-; check for certain card IDs in bench,
-; as well as their HP and energy cards attached
-Func_14c91: ; 14c91 (5:4c91)
+; 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
@@ -1285,48 +1310,48 @@ Func_14c91: ; 14c91 (5:4c91)
jr .articuno
; the following routines check for certain card IDs in bench
-; and call Func_174cd if these are found
+; and call RaiseAIScoreToAllMatchingIDsInBench if these are found.
; for Lapras, an additional check is made to its
-; attached energy count, which skips Func_174cd
+; attached energy count, which skips calling the routine
; if this count is >= 3
.lapras
ld a, LAPRAS
ld b, PLAY_AREA_BENCH_1
- call LookForCardIDInBench
+ call LookForCardIDInPlayArea_Bank5
jr nc, .articuno
ld e, a
call CountNumberOfEnergyCardsAttached
cp 3
jr nc, .articuno
ld a, LAPRAS
- call Func_174cd
+ call RaiseAIScoreToAllMatchingIDsInBench
ret
.articuno
ld a, ARTICUNO1
ld b, PLAY_AREA_BENCH_1
- call LookForCardIDInBench
+ call LookForCardIDInPlayArea_Bank5
jr nc, .dewgong
ld a, ARTICUNO1
- call Func_174cd
+ call RaiseAIScoreToAllMatchingIDsInBench
ret
.dewgong
ld a, DEWGONG
ld b, PLAY_AREA_BENCH_1
- call LookForCardIDInBench
+ call LookForCardIDInPlayArea_Bank5
jr nc, .seel
ld a, DEWGONG
- call Func_174cd
+ call RaiseAIScoreToAllMatchingIDsInBench
ret
.seel
ld a, SEEL
ld b, PLAY_AREA_BENCH_1
- call LookForCardIDInBench
+ call LookForCardIDInPlayArea_Bank5
ret nc
ld a, SEEL
- call Func_174cd
+ call RaiseAIScoreToAllMatchingIDsInBench
ret
; 0x14cf7
@@ -1361,7 +1386,7 @@ Data_1514f: ; 1514f (5:514f)
; output:
; a = card deck index, if found
; carry set if found
-LookForCardIDInHandList: ; 155d2 (5:55d2)
+LookForCardIDInHandList_Bank5: ; 155d2 (5:55d2)
ld [wTempCardIDToLook], a
call CreateHandCardList
ld hl, wDuelTempList
@@ -1383,7 +1408,7 @@ LookForCardIDInHandList: ; 155d2 (5:55d2)
; 0x155ef
; returns carry if card ID in a
-; is found in bench, starting with
+; is found in Play Area, starting with
; location in b
; input:
; a = card ID
@@ -1391,7 +1416,7 @@ LookForCardIDInHandList: ; 155d2 (5:55d2)
; ouput:
; a = PLAY_AREA_* of found card
; carry set if found
-LookForCardIDInBench: ; 155ef (5:55ef)
+LookForCardIDInPlayArea_Bank5: ; 155ef (5:55ef)
ld [wTempCardIDToLook], a
.loop
@@ -1425,7 +1450,7 @@ Func_15612: ; 15612 (5:5612)
Func_15636: ; 15636 (5:5636)
ld a, $10
ld hl, wcda5
- call ZeroData
+ call ClearMemory_Bank5
ld a, $5
ld [wcda6], a
ld a, $ff
@@ -1437,7 +1462,7 @@ Func_15649: ; 15649 (5:5649)
inc a
ld [wcda6], a
xor a
- ld [wce20], a
+ ld [wPreviousAIFlags], a
ld [wcddb], a
ld [wcddc], a
ld [wce03], a
@@ -1499,10 +1524,125 @@ Func_15649: ; 15649 (5:5649)
ret
; 0x156c3
- INCROM $156c3, $1575e
+; load selected move from Pokémon in hTempPlayAreaLocation_ff9d,
+; gets an energy card to discard and subsequently
+; check if there is enough energy to execute the selected move
+; after removing that attached energy card.
+; input:
+; [hTempPlayAreaLocation_ff9d] = location of Pokémon card
+; [wSelectedAttack] = selected move to examine
+; output:
+; b = basic energy still needed
+; c = colorless energy still needed
+; e = output of ConvertColorToEnergyCardID, or $0 if not a move
+; carry set if no move
+; OR if it's a Pokémon Power
+; OR if not enough energy for move
+CheckEnergyNeededForAttackAfterDiscard: ; 156c3 (5:56c3)
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ add DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ ld d, a
+ ld a, [wSelectedAttack]
+ ld e, a
+ call CopyMoveDataAndDamage_FromDeckIndex
+ ld hl, wLoadedMoveName
+ ld a, [hli]
+ or [hl]
+ jr z, .no_attack
+ ld a, [wLoadedMoveCategory]
+ cp POKEMON_POWER
+ jr nz, .is_attack
+.no_attack
+ lb bc, 0, 0
+ ld e, c
+ scf
+ ret
+
+.is_attack
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ farcall AIPickEnergyCardToDiscard
+ call LoadCardDataToBuffer1_FromDeckIndex
+ cp DOUBLE_COLORLESS_ENERGY
+ jr z, .colorless
+
+; color energy
+; decrease respective attached energy by 1.
+ ld hl, wAttachedEnergies
+ dec a
+ ld c, a
+ ld b, $00
+ add hl, bc
+ dec [hl]
+ ld hl, wTotalAttachedEnergies
+ dec [hl]
+ jr .asm_1570c
+; decrease attached colorless by 2.
+.colorless
+ ld hl, wAttachedEnergies + COLORLESS
+ dec [hl]
+ dec [hl]
+ ld hl, wTotalAttachedEnergies
+ dec [hl]
+ dec [hl]
+
+.asm_1570c
+ bank1call HandleEnergyBurn
+ xor a
+ ld [wTempLoadedMoveEnergyCost], a
+ ld [wTempLoadedMoveEnergyNeededAmount], a
+ ld [wTempLoadedMoveEnergyNeededType], a
+ ld hl, wAttachedEnergies
+ ld de, wLoadedMoveEnergyCost
+ ld b, 0
+ ld c, (NUM_TYPES / 2) - 1
+.loop
+ ; check all basic energy cards except colorless
+ ld a, [de]
+ swap a
+ call CheckIfEnoughParticularAttachedEnergy
+ ld a, [de]
+ call CheckIfEnoughParticularAttachedEnergy
+ inc de
+ dec c
+ jr nz, .loop
+
+ ld a, [de]
+ swap a
+ and $0f
+ ld b, a ; colorless energy still needed
+ ld a, [wTempLoadedMoveEnergyCost]
+ ld hl, wTempLoadedMoveEnergyNeededAmount
+ sub [hl]
+ ld c, a ; basic energy still needed
+ ld a, [wTotalAttachedEnergies]
+ sub c
+ sub b
+ jr c, .not_enough_energy
+
+ ld a, [wTempLoadedMoveEnergyNeededAmount]
+ or a
+ ret z
+
+; being here means the energy cost isn't satisfied,
+; including with colorless energy
+ xor a
+.not_enough_energy
+ cpl
+ inc a
+ ld c, a ; colorless energy still needed
+ ld a, [wTempLoadedMoveEnergyNeededAmount]
+ ld b, a ; basic energy still needed
+ ld a, [wTempLoadedMoveEnergyNeededType]
+ call ConvertColorToEnergyCardID
+ ld e, a
+ ld d, 0
+ scf
+ ret
+; 0x1575e
; zeroes a bytes starting at hl
-ZeroData: ; 1575e (5:575e)
+ClearMemory_Bank5: ; 1575e (5:575e)
push af
push bc
push hl
@@ -1519,7 +1659,7 @@ ZeroData: ; 1575e (5:575e)
; 0x1576b
; returns in a the tens digit of value in a
-CalculateTensDigit: ; 1576b (5:576b)
+CalculateByteTensDigit: ; 1576b (5:576b)
push bc
ld c, 0
.loop
@@ -1538,7 +1678,7 @@ CalculateTensDigit: ; 1576b (5:576b)
; input:
; a = divisor
; b = dividend
-CalculateBDividedByA: ; 15778 (5:5778)
+CalculateBDividedByA_Bank5: ; 15778 (5:5778)
push bc
ld c, a
ld a, b
@@ -1590,11 +1730,138 @@ CountNumberOfEnergyCardsAttached: ; 15787 (5:5787)
ret
; 0x157a3
-Func_157a3: ; 157a3 (5:57a3)
- INCROM $157a3, $158b2
+; returns carry if any card with ID in e is found
+; in card location in a
+; input:
+; a = card location to look in;
+; e = card ID to look for.
+CheckIfAnyCardIDinLocation: ; 157a3 (5:57a3)
+ ld b, a
+ ld c, e
+ lb de, 0, 0
+.loop
+ ld a, DUELVARS_CARD_LOCATIONS
+ add e
+ call GetTurnDuelistVariable
+ cp b
+ jr nz, .next
+ ld a, e
+ push de
+ call GetCardIDFromDeckIndex
+ ld a, e
+ pop de
+ cp c
+ jr z, .set_carry
+.next
+ inc e
+ ld a, DECK_SIZE
+ cp e
+ jr nz, .loop
+ or a
+ ret
+.set_carry
+ ld a, e
+ scf
+ ret
+; 0x157c6
+
+; counts total number of energy cards in opponent's hand
+; plus all the cards attached in Turn Duelist's Play Area.
+; output:
+; a and wTempAI = total number of energy cards.
+CountOppEnergyCardsInHandAndAttached: ; 157c6 (5:57c6)
+ xor a
+ ld [wTempAI], a
+ call CreateEnergyCardListFromHand
+ jr c, .attached
+
+; counts number of energy cards in hand
+ ld b, -1
+ ld hl, wDuelTempList
+.loop_hand
+ inc b
+ ld a, [hli]
+ cp $ff
+ jr nz, .loop_hand
+ ld a, b
+ ld [wTempAI], a
+
+; counts number of energy cards
+; that are attached in Play Area
+.attached
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld d, a
+ ld e, PLAY_AREA_ARENA
+.loop_play_area
+ call CountNumberOfEnergyCardsAttached
+ ld hl, wTempAI
+ add [hl]
+ ld [hl], a
+ inc e
+ dec d
+ jr nz, .loop_play_area
+ ret
+; 0x157f3
+
+; returns carry if any card with ID in e is found
+; in the list that is pointed by hl.
+; if one is found, it is removed from the list.
+; input:
+; e = card ID to look for.
+; hl = list to look in
+RemoveCardIDInList: ; 157f3 (5:57f3)
+ push hl
+ push de
+ push bc
+ ld c, e
+
+.loop_1
+ ld a, [hli]
+ cp $ff
+ jr z, .no_carry
+
+ ldh [hTempCardIndex_ff98], a
+ call GetCardIDFromDeckIndex
+ ld a, c
+ cp e
+ jr nz, .loop_1
+
+; found
+ ld d, h
+ ld e, l
+ dec hl
+
+; remove this index from the list
+; and reposition the rest of the list ahead.
+.loop_2
+ ld a, [de]
+ inc de
+ ld [hli], a
+ cp $ff
+ jr nz, .loop_2
+
+ ldh a, [hTempCardIndex_ff98]
+ pop bc
+ pop de
+ pop hl
+ scf
+ ret
+
+.no_carry
+ pop bc
+ pop de
+ pop hl
+ or a
+ ret
+; 0x1581b
+
+Func_1581b: ; 1581b (5:581b)
+ INCROM $1581b, $158b2
; determine AI score for retreating
-Func_158b2: ; 158b2 (5:58b2)
+; return carry if AI decides to retreat
+AIDecideWhetherToRetreat: ; 158b2 (5:58b2)
ld a, [wGotHeadsFromConfusionCheckDuringRetreat]
or a
jp nz, .no_carry
@@ -1603,7 +1870,7 @@ Func_158b2: ; 158b2 (5:58b2)
call LoadDefendingPokemonColorWRAndPrizeCards
ld a, 128 ; initial retreat score
ld [wAIScore], a
- ld a, [$cdb4]
+ ld a, [wcdb4]
or a
jr z, .check_status
srl a
@@ -1633,7 +1900,7 @@ Func_158b2: ; 158b2 (5:58b2)
ldh [hTempPlayAreaLocation_ff9d], a
call CheckIfAnyMoveKnocksOutDefendingCard
jr nc, .active_cant_ko_1
- call CheckIfCardCanUseSelectedMove
+ call CheckIfSelectedMoveIsUnusable
jp nc, .active_cant_use_move
call LookForEnergyNeededForMoveInHand
jr nc, .active_cant_ko_1
@@ -1834,7 +2101,7 @@ Func_158b2: ; 158b2 (5:58b2)
push bc
call CheckIfAnyMoveKnocksOutDefendingCard
jr nc, .no_ko
- call CheckIfCardCanUseSelectedMove
+ call CheckIfSelectedMoveIsUnusable
jr nc, .success
call LookForEnergyNeededForMoveInHand
jr c, .success
@@ -1863,7 +2130,7 @@ Func_158b2: ; 158b2 (5:58b2)
ldh [hTempPlayAreaLocation_ff9d], a
call CheckIfAnyMoveKnocksOutDefendingCard
jr nc, .active_cant_ko_2
- call CheckIfCardCanUseSelectedMove
+ call CheckIfSelectedMoveIsUnusable
jp nc, .check_defending_id
.active_cant_ko_2
ld a, 40
@@ -2056,7 +2323,9 @@ Func_15b54: ; 15b54 (5:5b54)
; 0x15b72
; calculates AI score for bench Pokémon
-Func_15b72: ; 15b72 (5:5b72)
+; returns in hTempPlayAreaLocation_ff9d the
+; Play Area location of best card to switch to
+AIDecideBenchPokemonToSwitchTo: ; 15b72 (5:5b72)
xor a
ldh [hTempPlayAreaLocation_ff9d], a
ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
@@ -2088,7 +2357,7 @@ Func_15b72: ; 15b72 (5:5b72)
; if on last prize card, raise AI score again
call CheckIfAnyMoveKnocksOutDefendingCard
jr nc, .check_can_use_moves
- call CheckIfCardCanUseSelectedMove
+ call CheckIfSelectedMoveIsUnusable
jr c, .check_can_use_moves
ld a, 10
call AddToAIScore
@@ -2105,12 +2374,12 @@ Func_15b72: ; 15b72 (5:5b72)
; to raise AI score accordingly
.check_can_use_moves
xor a
- ld [wSelectedMoveIndex], a
- call CheckIfCardCanUseSelectedMove
+ ld [wSelectedAttack], a
+ call CheckIfSelectedMoveIsUnusable
call nc, .calculate_damage
ld a, $01
- ld [wSelectedMoveIndex], a
- call CheckIfCardCanUseSelectedMove
+ ld [wSelectedAttack], a
+ call CheckIfSelectedMoveIsUnusable
call nc, .calculate_damage
jr .check_energy_card
@@ -2118,10 +2387,10 @@ Func_15b72: ; 15b72 (5:5b72)
; it can inflict to the defending Pokémon
; AI score += floor(Damage / 10) + 1
.calculate_damage
- ld a, [wSelectedMoveIndex]
+ ld a, [wSelectedAttack]
call EstimateDamage_VersusDefendingCard
ld a, [wDamage]
- call CalculateTensDigit
+ call CalculateByteTensDigit
inc a
call AddToAIScore
ret
@@ -2132,10 +2401,10 @@ Func_15b72: ; 15b72 (5:5b72)
.check_energy_card
call LookForEnergyNeededInHand
jr nc, .check_attached_energy
- ld a, [wSelectedMoveIndex]
+ ld a, [wSelectedAttack]
call EstimateDamage_VersusDefendingCard
ld a, [wDamage]
- call CalculateTensDigit
+ call CalculateByteTensDigit
srl a
call AddToAIScore
@@ -2262,8 +2531,8 @@ Func_15b72: ; 15b72 (5:5b72)
.add_hp_score
ld b, a
ld a, 4
- call CalculateBDividedByA
- call CalculateTensDigit
+ call CalculateBDividedByA_Bank5
+ call CalculateByteTensDigit
call AddToAIScore
; raise AI score if
@@ -2341,7 +2610,7 @@ Func_15b72: ; 15b72 (5:5b72)
ldh a, [hTempPlayAreaLocation_ff9d]
ld c, a
ld b, $00
- ld hl, wBenchAIScore
+ ld hl, wPlayAreaAIScore
add hl, bc
ld a, [wAIScore]
ld [hl], a
@@ -2352,14 +2621,14 @@ Func_15b72: ; 15b72 (5:5b72)
; done
xor a
- ld [$cdb4], a
+ ld [wcdb4], a
jp FindHighestBenchScore
; 0x15d4f
; handles AI action of retreating Arena Pokémon
; and chooses which energy cards to discard
; if card can't discard, return carry
-Func_15d4f: ; 15d4f (5:5d4f)
+AIChooseEnergyToDiscardForRetreatCost: ; 15d4f (5:5d4f)
push af
ld a, [wAIPlayEnergyCardForRetreat]
or a
@@ -2609,8 +2878,8 @@ CopyHandCardList: ; 15ea6 (5:5ea6)
jr CopyHandCardList
; determine whether AI plays
-; basic card from hand
-Func_15eae: ; 15eae (5:5eae)
+; basic cards from hand
+AIDecidePlayPokemonCard: ; 15eae (5:5eae)
call CreateHandCardList
call SortTempHandByIDList
ld hl, wDuelTempList
@@ -2621,7 +2890,7 @@ Func_15eae: ; 15eae (5:5eae)
.next_hand_card
ld a, [hli]
cp $ff
- jp z, Func_15f4c
+ jp z, AIDecideEvolution
ld [wTempAIPokemonCard], a
push hl
@@ -2711,7 +2980,7 @@ Func_15eae: ; 15eae (5:5eae)
; determine whether AI evolves
; Pokémon in the Play Area
-Func_15f4c: ; 15f4c (5:5f4c)
+AIDecideEvolution: ; 15f4c (5:5f4c)
call CreateHandCardList
ld hl, wDuelTempList
ld de, wHandTempList
@@ -2758,10 +3027,10 @@ Func_15f4c: ; 15f4c (5:5f4c)
push bc
jp c, .done_bench_pokemon
-; store this Play Area location in wCurCardPlayAreaLocation
+; store this Play Area location in wTempAI
; and initialize the AI score
ld a, b
- ld [wCurCardPlayAreaLocation], a
+ ld [wTempAI], a
ldh [hTempPlayAreaLocation_ff9d], a
ld a, 128
ld [wAIScore], a
@@ -2770,19 +3039,19 @@ Func_15f4c: ; 15f4c (5:5f4c)
; check if the card can use any moves
; and if any of those moves can KO
xor a
- ld [wSelectedMoveIndex], a
- call CheckIfCardCanUseSelectedMove
+ ld [wSelectedAttack], a
+ call CheckIfSelectedMoveIsUnusable
jr nc, .can_attack
ld a, $01
- ld [wSelectedMoveIndex], a
- call CheckIfCardCanUseSelectedMove
+ ld [wSelectedAttack], a
+ call CheckIfSelectedMoveIsUnusable
jr c, .cant_attack_or_ko
.can_attack
ld a, $01
ld [wCurCardCanAttack], a
call CheckIfAnyMoveKnocksOutDefendingCard
jr nc, .check_evolution_attacks
- call CheckIfCardCanUseSelectedMove
+ call CheckIfSelectedMoveIsUnusable
jr c, .check_evolution_attacks
ld a, $01
ld [wCurCardCanKO], a
@@ -2804,12 +3073,12 @@ Func_15f4c: ; 15f4c (5:5f4c)
ld a, [wTempAIPokemonCard]
ld [hl], a
xor a
- ld [wSelectedMoveIndex], a
- call CheckIfCardCanUseSelectedMove
+ ld [wSelectedAttack], a
+ call CheckIfSelectedMoveIsUnusable
jr nc, .evolution_can_attack
ld a, $01
- ld [wSelectedMoveIndex], a
- call CheckIfCardCanUseSelectedMove
+ ld [wSelectedAttack], a
+ call CheckIfSelectedMoveIsUnusable
jr c, .evolution_cant_attack
.evolution_can_attack
ld a, 5
@@ -2836,12 +3105,12 @@ Func_15f4c: ; 15f4c (5:5f4c)
ld a, [wCurCardCanAttack]
or a
jr z, .check_defending_can_ko_evolution
- ld a, [wCurCardPlayAreaLocation]
+ ld a, [wTempAI]
or a
jr nz, .check_defending_can_ko_evolution
call CheckIfAnyMoveKnocksOutDefendingCard
jr nc, .evolution_cant_ko
- call CheckIfCardCanUseSelectedMove
+ call CheckIfSelectedMoveIsUnusable
jr c, .evolution_cant_ko
ld a, 5
call AddToAIScore
@@ -2855,7 +3124,7 @@ Func_15f4c: ; 15f4c (5:5f4c)
; if defending Pokémon can KO evolution, lower AI score
.check_defending_can_ko_evolution
- ld a, [wCurCardPlayAreaLocation]
+ ld a, [wTempAI]
or a
jr nz, .check_mr_mime
xor a
@@ -2867,7 +3136,7 @@ Func_15f4c: ; 15f4c (5:5f4c)
; if evolution can't damage player's Mr Mime, lower AI score
.check_mr_mime
- ld a, [wCurCardPlayAreaLocation]
+ ld a, [wTempAI]
call CheckDamageToMrMime
jr c, .check_defending_can_ko
ld a, 20
@@ -2875,12 +3144,12 @@ Func_15f4c: ; 15f4c (5:5f4c)
; if defending Pokémon can KO current card, raise AI score
.check_defending_can_ko
- ld a, [wCurCardPlayAreaLocation]
+ ld a, [wTempAI]
add DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
pop af
ld [hl], a
- ld a, [wCurCardPlayAreaLocation]
+ ld a, [wTempAI]
or a
jr nz, .check_2nd_stage_hand
xor a
@@ -2919,21 +3188,21 @@ Func_15f4c: ; 15f4c (5:5f4c)
; decrease AI score proportional to damage
; AI score -= floor(Damage / 40)
.check_damage
- ld a, [wCurCardPlayAreaLocation]
+ ld a, [wTempAI]
ld e, a
call GetCardDamage
or a
jr z, .check_mysterious_fossil
srl a
srl a
- call CalculateTensDigit
+ call CalculateByteTensDigit
call SubFromAIScore
; if is Mysterious Fossil or
; wLoadedCard1Unknown2 is set to $02,
; raise AI score
.check_mysterious_fossil
- ld a, [wCurCardPlayAreaLocation]
+ ld a, [wTempAI]
add DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
call LoadCardDataToBuffer1_FromDeckIndex
@@ -2974,7 +3243,7 @@ Func_15f4c: ; 15f4c (5:5f4c)
ld a, [wAIScore]
cp 133
jr c, .done_bench_pokemon
- ld a, [wCurCardPlayAreaLocation]
+ ld a, [wTempAI]
ldh [hTempPlayAreaLocation_ffa1], a
ld a, [wTempAIPokemonCard]
ldh [hTemp_ffa0], a
@@ -3028,7 +3297,7 @@ Func_16120: ; 16120 (5:6120)
cp 3
jr c, .not_enough_energy
push af
- farcall CountEnergyCardsInHand
+ farcall CountOppEnergyCardsInHand
pop bc
add b
cp 6
@@ -3169,7 +3438,7 @@ Func_161d5: ; 161d5 (5:61d5)
jr c, .subtract
call CheckIfActivePokemonCanUseAnyNonResidualMove
jr nc, .subtract
- call Func_158b2
+ call AIDecideWhetherToRetreat
jr c, .subtract
; checks for player's active card status
@@ -3272,7 +3541,7 @@ CheckIfActiveCardCanKnockOut: ; 1628f (5:628f)
ldh [hTempPlayAreaLocation_ff9d], a
call CheckIfAnyMoveKnocksOutDefendingCard
jr nc, .fail
- call CheckIfCardCanUseSelectedMove
+ call CheckIfSelectedMoveIsUnusable
jp c, .fail
scf
ret
@@ -3288,8 +3557,8 @@ CheckIfActivePokemonCanUseAnyNonResidualMove: ; 162a1 (5:62a1)
xor a ; active card
ldh [hTempPlayAreaLocation_ff9d], a
; first move
- ld [wSelectedMoveIndex], a
- call CheckIfCardCanUseSelectedMove
+ ld [wSelectedAttack], a
+ call CheckIfSelectedMoveIsUnusable
jr c, .next_move
ld a, [wLoadedMoveCategory]
and RESIDUAL
@@ -3298,8 +3567,8 @@ CheckIfActivePokemonCanUseAnyNonResidualMove: ; 162a1 (5:62a1)
.next_move
; second move
ld a, $01
- ld [wSelectedMoveIndex], a
- call CheckIfCardCanUseSelectedMove
+ ld [wSelectedAttack], a
+ call CheckIfSelectedMoveIsUnusable
jr c, .fail
ld a, [wLoadedMoveCategory]
and RESIDUAL
@@ -3324,21 +3593,21 @@ CheckIfActivePokemonCanUseAnyNonResidualMove: ; 162a1 (5:62a1)
; [hTempPlayAreaLocation_ff9d] = location of Pokémon card
LookForEnergyNeededInHand: ; 162c8 (5:62c8)
xor a ; first move
- ld [wSelectedMoveIndex], a
+ ld [wSelectedAttack], a
call CheckEnergyNeededForAttack
ld a, b
add c
cp 1
jr z, .one_energy
cp 2
- jr nz, .second_move
+ jr nz, .second_attack
ld a, c
cp 2
jr z, .two_colorless
-.second_move
+.second_attack
ld a, $01 ; second move
- ld [wSelectedMoveIndex], a
+ ld [wSelectedAttack], a
call CheckEnergyNeededForAttack
ld a, b
add c
@@ -3358,7 +3627,7 @@ LookForEnergyNeededInHand: ; 162c8 (5:62c8)
or a
jr z, .one_colorless
ld a, e
- call LookForCardIDInHandList
+ call LookForCardIDInHandList_Bank5
ret c
jr .no_carry
@@ -3370,7 +3639,7 @@ LookForEnergyNeededInHand: ; 162c8 (5:62c8)
.two_colorless
ld a, DOUBLE_COLORLESS_ENERGY
- call LookForCardIDInHandList
+ call LookForCardIDInHandList_Bank5
ret c
jr .no_carry
; 0x16311
@@ -3384,7 +3653,7 @@ LookForEnergyNeededInHand: ; 162c8 (5:62c8)
; return carry if successful in finding card
; input:
; [hTempPlayAreaLocation_ff9d] = location of Pokémon card
-; [wSelectedMoveIndex] = selected move to examine
+; [wSelectedAttack] = selected move to examine
LookForEnergyNeededForMoveInHand: ; 16311 (5:6311)
call CheckEnergyNeededForAttack
ld a, b
@@ -3405,7 +3674,7 @@ LookForEnergyNeededForMoveInHand: ; 16311 (5:6311)
or a
jr z, .one_colorless
ld a, e
- call LookForCardIDInHandList
+ call LookForCardIDInHandList_Bank5
ret c
jr .done
@@ -3417,7 +3686,7 @@ LookForEnergyNeededForMoveInHand: ; 16311 (5:6311)
.two_colorless
ld a, DOUBLE_COLORLESS_ENERGY
- call LookForCardIDInHandList
+ call LookForCardIDInHandList_Bank5
ret c
jr .done
; 0x1633f
@@ -3735,14 +4004,52 @@ CheckForEvolutionInDeck: ; 16451 (5:6451)
; 0x16488
Func_16488 ; 16488 (5:6488)
- INCROM $16488, $164d3
+ 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
+ ld de, wTempPlayAreaAIScore
+ ld hl, wPlayAreaAIScore
+ ld b, MAX_PLAY_AREA_POKEMON
+.loop_play_area
+ ld a, [hli]
+ ld [de], a
+ inc de
+ dec b
+ jr nz, .loop_play_area
-; copies bench AI score to wcddd
-; and loads in wAIscore value in wcde3
+ ld a, [wAIScore]
+ ld [de], a
+ jr AIDecideWhichCardToAttachEnergy
+
+Func_164ba: ; 164ba (5:64ba)
+ ld a, $83
+ ld [wcdd8], a
+ ld de, wTempPlayAreaAIScore
+ ld hl, wPlayAreaAIScore
+ ld b, MAX_PLAY_AREA_POKEMON
+.asm_164c7
+ ld a, [hli]
+ ld [de], a
+ inc de
+ dec b
+ jr nz, .asm_164c7
+
+ ld a, [wAIScore]
+ ld [de], a
+ jr AIDecideWhichCardToAttachEnergy
+
+; copies wTempPlayAreaAIScore to wPlayAreaAIScore
+; and loads wAIscore with value in wcde3.
+; identical to Func_169e3.
Func_164d3: ; 164d3 (5:64d3)
push af
- ld de, wBenchAIScore
- ld hl, wcddd
+ ld de, wPlayAreaAIScore
+ ld hl, wTempPlayAreaAIScore
ld b, MAX_PLAY_AREA_POKEMON
.loop
ld a, [hli]
@@ -3756,24 +4063,26 @@ Func_164d3: ; 164d3 (5:64d3)
ret
; 0x164e8
-Func_164e8: ; 164e8 (5:64e8)
+; have AI decide whether to play energy card from hand
+; and determine which card is best to attach it.
+AIDecidePlayEnergyCardFromHand: ; 164e8 (5:64e8)
xor a
ld [wcdd8], a
call CreateEnergyCardListFromHand
- jr nc, .has_energy
+ jr nc, AIDecideWhichCardToAttachEnergy
; no energy
ld a, [wcdd8]
or a
jr z, .exit
- ; can this even be reached?
jp Func_164d3
.exit
or a
ret
-; initialize wcde4 to $80
-.has_energy
+; have AI decide whether to play energy card
+; and determine which card is best to attach it.
+AIDecideWhichCardToAttachEnergy: ; 164fc (5:64fc)
ld a, $80
ld b, MAX_PLAY_AREA_POKEMON
ld hl, wcde4
@@ -3782,7 +4091,8 @@ Func_164e8: ; 164e8 (5:64e8)
dec b
jr nz, .loop
- call Func_175bd
+ call HandleLegendaryArticunoEnergyScoring
+
ld b, PLAY_AREA_ARENA
ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
call GetTurnDuelistVariable
@@ -3795,7 +4105,7 @@ Func_164e8: ; 164e8 (5:64e8)
ld a, $80
ld [wAIScore], a
ld a, $ff
- ld [wCurCardPlayAreaLocation], a
+ ld [wTempAI], a
ld a, [wcdd8]
and $02
jr nz, .check_venusaur
@@ -3811,11 +4121,11 @@ Func_164e8: ; 164e8 (5:64e8)
call GetMovesEnergyCostBits
ld hl, wDuelTempList
call CheckEnergyFlagsNeededInList
- jp nc, .asm_16661
+ jp nc, .store_score
ld a, [wCurCardCanAttack]
call CheckForEvolutionInList
jr nc, .no_evolution_in_hand
- ld [wCurCardPlayAreaLocation], a
+ ld [wTempAI], a
ld a, 2
call AddToAIScore
jr .check_venusaur
@@ -3861,7 +4171,7 @@ Func_164e8: ; 164e8 (5:64e8)
ld a, DUELVARS_ARENA_CARD_HP
call GetTurnDuelistVariable
- call CalculateTensDigit
+ call CalculateByteTensDigit
cp 3
jr nc, .check_defending_can_ko
; hp < 30
@@ -3905,7 +4215,7 @@ Func_164e8: ; 164e8 (5:64e8)
.bench
add DUELVARS_ARENA_CARD_HP
call GetTurnDuelistVariable
- call CalculateTensDigit
+ call CalculateByteTensDigit
cp 3
jr nc, .asm_165e1
; hp < 30
@@ -3950,16 +4260,17 @@ Func_164e8: ; 164e8 (5:64e8)
jr c, .check_id_score
ld a, 10
call SubFromAIScore
- jr .asm_16661
+ jr .store_score
.check_id_score
ld a, [hli]
cp $80
- jr c, .asm_16622
+ jr c, .decrease_score_1
sub $80
call AddToAIScore
jr .check_boss_deck
-.asm_16622
+
+.decrease_score_1
ld d, a
ld a, $80
sub d
@@ -3985,29 +4296,34 @@ Func_164e8: ; 164e8 (5:64e8)
add hl, bc
ld a, [hl]
cp $80
- jr c, .asm_1664c
+ jr c, .decrease_score_2
sub $80
call AddToAIScore
jr .skip_boss_deck
-.asm_1664c
+
+.decrease_score_2
ld b, a
ld a, $80
sub b
call SubFromAIScore
+
.skip_boss_deck
ld a, 1
call AddToAIScore
-
+
+; add AI score for both moves,
+; according to their energy requirements.
xor a ; first move
- call Func_16695
+ call DetermineAIScoreOfMoveEnergyRequirement
ld a, $01 ; second move
- call Func_16695
+ call DetermineAIScoreOfMoveEnergyRequirement
-.asm_16661
+; store bench score for this card.
+.store_score
ldh a, [hTempPlayAreaLocation_ff9d]
ld c, a
ld b, $00
- ld hl, wBenchAIScore
+ ld hl, wPlayAreaAIScore
add hl, bc
ld a, [wAIScore]
ld [hl], a
@@ -4015,60 +4331,83 @@ Func_164e8: ; 164e8 (5:64e8)
inc b
dec c
jp nz, .loop_play_area
- call Func_167b5
- jp nc, Func_1668a
+
+; the Play Area loop is over and the score
+; for each card has been calculated.
+; now to determine the highest score.
+ call FindPlayAreaCardWithHighestAIScore
+ jp nc, .asm_1668a
+
ld a, [wcdd8]
or a
- jr z, .asm_16684
+ jr z, .play_card
scf
jp Func_164d3
-.asm_16684
+
+.play_card
call CreateEnergyCardListFromHand
- jp Func_1689f
-; 0x1668a
+ jp AITryToPlayEnergyCard
-Func_1668a ; 1668a (5:668a)
- INCROM $1668a, $16695
+.asm_1668a: ; 1668a (5:668a)
+ ld a, [wcdd8]
+ or a
+ jr z, .asm_16693
+ jp Func_164d3
+.asm_16693
+ or a
+ ret
+; 0x16695
-Func_16695: ; 16695 (5:6695)
- ld [wSelectedMoveIndex], a
+; checks score related to selected move,
+; in order to determine whether to play energy card.
+; the AI score is increased/decreased accordingly.
+; input:
+; [wSelectedAttack] = move to check.
+DetermineAIScoreOfMoveEnergyRequirement: ; 16695 (5:6695)
+ ld [wSelectedAttack], a
call CheckEnergyNeededForAttack
- jp c, .asm_1671e
- ld a, $0c ; ATTACHED_ENERGY_BOOST
+ jp c, .not_enough_energy
+ ld a, MOVE_FLAG2_ADDRESS | ATTACHED_ENERGY_BOOST_F
call CheckLoadedMoveFlag
- jr c, .asm_166af
- ld a, $0b ; FLAG_2_BIT_5
+ jr c, .attached_energy_boost
+ ld a, MOVE_FLAG2_ADDRESS | DISCARD_ENERGY_F
call CheckLoadedMoveFlag
- jr c, .asm_16710
+ jr c, .discard_energy
jp .check_evolution
-.asm_166af
- ld a, [wLoadedMoveUnknown1]
- cp $02
- jr z, .asm_166bc
+.attached_energy_boost
+ ld a, [wLoadedMoveEffectParam]
+ cp MAX_ENERGY_BOOST_IS_LIMITED
+ jr z, .check_surplus_energy
+
+ ; is MAX_ENERGY_BOOST_IS_NOT_LIMITED,
+ ; which is equal to 3, add to score.
call AddToAIScore
jp .check_evolution
-.asm_166bc
- call Func_171fb
+.check_surplus_energy
+ call CheckIfNoSurplusEnergyForMove
jr c, .asm_166cd
- cp 3
+ cp 3 ; check how much surplus energy
jr c, .asm_166cd
+
.asm_166c5
ld a, 5
call SubFromAIScore
jp .check_evolution
+
.asm_166cd
ld a, 2
call AddToAIScore
; check whether move has ATTACHED_ENERGY_BOOST flag
; and add to AI score if attaching another energy
-; will KO defending Pokémon
- ld a, $0c
+; will KO defending Pokémon.
+; add more to score if this is currently active Pokémon.
+ ld a, MOVE_FLAG2_ADDRESS | ATTACHED_ENERGY_BOOST_F
call CheckLoadedMoveFlag
jp nc, .check_evolution
- ld a, [wSelectedMoveIndex]
+ ld a, [wSelectedAttack]
call EstimateDamage_VersusDefendingCard
ld a, DUELVARS_ARENA_CARD_HP
call GetNonTurnDuelistVariable
@@ -4082,9 +4421,10 @@ Func_16695: ; 16695 (5:6695)
ld a, DUELVARS_ARENA_CARD_HP
call GetNonTurnDuelistVariable
sub b
- jr c, .asm_166ff
+ jr c, .attaching_kos_player
jr nz, .check_evolution
-.asm_166ff
+
+.attaching_kos_player
ld a, 20
call AddToAIScore
ldh a, [hTempPlayAreaLocation_ff9d]
@@ -4094,41 +4434,47 @@ Func_16695: ; 16695 (5:6695)
call AddToAIScore
jr .check_evolution
-; FLAG_2_BIT_5 is set only for Pokémon Powers,
-; except for Magnemite2's Magnetic Storm attack
-.asm_16710
+; checks if there is surplus energy for move
+; that discards attached energy card.
+; if current card is Zapdos2, don't add to score.
+; if there is no surplus energy, encourage playing energy.
+.discard_energy
ld a, [wLoadedCard1ID]
cp ZAPDOS2
jr z, .check_evolution
- call Func_171fb
+ call CheckIfNoSurplusEnergyForMove
jr c, .asm_166cd
jr .asm_166c5
-.asm_1671e
- ld a, $0d ; FLAG_2_BIT_5
+.not_enough_energy
+ ld a, MOVE_FLAG2_ADDRESS | FLAG_2_BIT_5_F
call CheckLoadedMoveFlag
- jr nc, .asm_1672a
+ jr nc, .check_color_needed
ld a, 5
call SubFromAIScore
-.asm_1672a
+; if the energy card color needed is in hand, increase AI score.
+; if a colorless card is needed, increase AI score.
+.check_color_needed
ld a, b
or a
- jr z, .asm_1673b
+ jr z, .check_colorless_needed
ld a, e
call LookForCardIDInHand
- jr c, .asm_1673b
+ jr c, .check_colorless_needed
ld a, 4
call AddToAIScore
- jr .asm_16744
-.asm_1673b
+ jr .check_total_needed
+.check_colorless_needed
ld a, c
or a
jr z, .check_evolution
ld a, 3
call AddToAIScore
-.asm_16744
+; if only one energy card is needed for move,
+; encourage playing energy card.
+.check_total_needed
ld a, b
add c
dec a
@@ -4136,20 +4482,28 @@ Func_16695: ; 16695 (5:6695)
ld a, 3
call AddToAIScore
+; if the move KOs player and this is the active card, add to AI score.
ldh a, [hTempPlayAreaLocation_ff9d]
or a
jr nz, .check_evolution
- ld a, [wSelectedMoveIndex]
+ ld a, [wSelectedAttack]
call EstimateDamage_VersusDefendingCard
ld a, DUELVARS_ARENA_CARD_HP
call GetNonTurnDuelistVariable
ld hl, wDamage
sub [hl]
- jr z, .asm_16766
+ jr z, .move_kos_defending
jr nc, .check_evolution
-.asm_16766
+.move_kos_defending
ld a, 20
call AddToAIScore
+
+; this is possibly a bug.
+; this is an identical check as above to test whether this card is active.
+; in case it is active, the score gets added 10 more points,
+; in addition to the 20 points already added above.
+; what was probably intended was to add 20 points in case this card
+; is active and only 10 in case it is benched.
ldh a, [hTempPlayAreaLocation_ff9d]
or a
jr nz, .check_evolution
@@ -4157,36 +4511,44 @@ Func_16695: ; 16695 (5:6695)
call AddToAIScore
.check_evolution
- ld a, [wCurCardPlayAreaLocation]
+ ld a, [wTempAI] ; evolution in hand
cp $ff
ret z
+
+; temporarily replace this card with evolution in hand.
ld b, a
ldh a, [hTempPlayAreaLocation_ff9d]
add DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
push af
ld [hl], b
+
+; check for energy still needed for evolution to attack.
+; if FLAG_2_BIT_5 is not set, check what color is needed.
+; if the energy card color needed is in hand, increase AI score.
+; if a colorless card is needed, increase AI score.
call CheckEnergyNeededForAttack
jr nc, .done
- ld a, $0d ; FLAG_2_BIT_5
+ ld a, MOVE_FLAG2_ADDRESS | FLAG_2_BIT_5_F
call CheckLoadedMoveFlag
jr c, .done
ld a, b
or a
- jr z, .asm_167a2
+ jr z, .check_colorless_needed_evo
ld a, e
call LookForCardIDInHand
- jr c, .asm_167a2
+ jr c, .check_colorless_needed_evo
ld a, 2
call AddToAIScore
jr .done
-.asm_167a2
+.check_colorless_needed_evo
ld a, c
or a
jr z, .done
ld a, 1
call AddToAIScore
+; recover the original card in the Play Area location.
.done
ldh a, [hTempPlayAreaLocation_ff9d]
add DUELVARS_ARENA_CARD
@@ -4196,14 +4558,1711 @@ Func_16695: ; 16695 (5:6695)
ret
; 0x167b5
-Func_167b5 ; 167b5 (5:67b5)
- INCROM $167b5, $1689f
+; returns in hTempPlayAreaLocation_ff9d the Play Area location
+; 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.
+FindPlayAreaCardWithHighestAIScore: ; 167b5 (5:67b5)
+ ld a, [wcdd8]
+ and $80
+ jr nz, .asm_167e1
+
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld b, a
+ ld c, 0 ; PLAY_AREA_ARENA
+ ld e, c
+ ld d, c
+ ld hl, wPlayAreaAIScore
+; find highest Play Area AI score.
+.loop_1
+ ld a, [hli]
+ cp e
+ jr c, .next_1
+ jr z, .next_1
+ ld e, a ; overwrite highest score found
+ ld d, c ; overwrite Play Area of highest score
+.next_1
+ inc c
+ dec b
+ jr nz, .loop_1
+
+; if highest AI score is below $85, return no carry.
+; else, store Play Area location and return carry.
+ ld a, e
+ cp $85
+ jr c, .not_enough_score
+ ld a, d
+ ldh [hTempPlayAreaLocation_ff9d], a
+ scf
+ ret
+.not_enough_score
+ or a
+ ret
+
+; same as above but only check bench Pokémon scores.
+.asm_167e1
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ dec a
+ jr z, .no_carry
+
+ ld b, a
+ ld e, 0
+ ld c, PLAY_AREA_BENCH_1
+ ld d, c
+ ld hl, wPlayAreaAIScore + 1
+.loop_2
+ ld a, [hli]
+ cp e
+ jr c, .next_2
+ jr z, .next_2
+ ld e, a ; overwrite highest score found
+ ld d, c ; overwrite Play Area of highest score
+.next_2
+ inc c
+ dec b
+ jr nz, .loop_2
+
+ ld a, d
+ ldh [hTempPlayAreaLocation_ff9d], a
+ scf
+ ret
+.no_carry
+ or a
+ ret
+; 0x16805
+
+; returns carry if there's an evolution card
+; that can evolve card in hTempPlayAreaLocation_ff9d,
+; and that card needs energy to use wSelectedMove.
+CheckIfEvolutionNeedsEnergyForMove: ; 16805 (5:6805)
+ call CreateHandCardList
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ add DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call CheckCardEvolutionInHandOrDeck
+ jr c, .has_evolution
+ or a
+ ret
+
+.has_evolution
+ ld b, a
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ add DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ push af
+ ld [hl], b
+ call CheckEnergyNeededForAttack
+ jr c, .not_enough_energy
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ add DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ pop af
+ ld [hl], a
+ or a
+ ret
+
+.not_enough_energy
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ add DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ pop af
+ ld [hl], a
+ scf
+ ret
+; 0x1683b
+
+; returns in e the card ID of the energy required for
+; the Discard/Energy Boost attack loaded in wSelectedAttack.
+; if it's Zapdos2's Thunderbolt attack, return no carry.
+; if it's Charizard's Fire Spin or Exeggutor's Big Eggplosion
+; attack, don't return energy card ID, but set carry.
+; output:
+; b = 1 if needs color energy, 0 otherwise;
+; c = 1 if only needs colorless energy, 0 otherwise;
+; carry set if not Zapdos2's Thunderbolt attack.
+GetEnergyCardForDiscardOrEnergyBoostAttack: ; 1683b (5:683b)
+; load card ID and check selected move index.
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ add DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call LoadCardDataToBuffer2_FromDeckIndex
+ ld b, a
+ ld a, [wSelectedAttack]
+ or a
+ jr z, .first_attack
+
+; check if second attack is Zapdos2's Thunderbolt,
+; Charizard's Fire Spin or Exeggcutor'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
+ cp EXEGGUTOR
+ jr z, .charizard_or_exeggcutor
+ ld hl, wLoadedCard2Move2EnergyCost
+ jr .fire
+.first_attack
+ ld hl, wLoadedCard2Move1EnergyCost
+
+; check which energy color the move requires,
+; and load in e the card ID of corresponding energy card,
+; then return carry flag set.
+.fire
+ ld a, [hli]
+ ld b, a
+ and $f0
+ jr z, .grass
+ ld e, FIRE_ENERGY
+ jr .set_carry
+.grass
+ ld a, b
+ and $0f
+ jr z, .lightning
+ ld e, GRASS_ENERGY
+ jr .set_carry
+.lightning
+ ld a, [hli]
+ ld b, a
+ and $f0
+ jr z, .water
+ ld e, LIGHTNING_ENERGY
+ jr .set_carry
+.water
+ ld a, b
+ and $0f
+ jr z, .fighting
+ ld e, WATER_ENERGY
+ jr .set_carry
+.fighting
+ ld a, [hli]
+ ld b, a
+ and $f0
+ jr z, .psychic
+ ld e, FIGHTING_ENERGY
+ jr .set_carry
+.psychic
+ ld e, PSYCHIC_ENERGY
+
+.set_carry
+ lb bc, $01, $00
+ scf
+ ret
+
+; for Zapdos2's Thunderbolt attack, return with no carry.
+.zapdos2
+ or a
+ ret
+
+; Charizard's Fire Spin and Exeggcutor's Big Eggsplosion,
+; return carry.
+.charizard_or_exeggcutor
+ lb bc, $00, $01
+ scf
+ ret
+; 0x1689f
+
+; called after the AI has decided which card to attach
+; energy from hand. AI does checks to determine whether
+; this card needs more energy or not, and chooses the
+; right energy card to play. If the card is played,
+; return with carry flag set.
+AITryToPlayEnergyCard: ; 1689f (5:689f)
+; check if energy cards are still needed for attacks.
+; if first attack doesn't need, test for the second attack.
+ xor a
+ ld [wTempAI], a
+ ld [wSelectedAttack], a
+ call CheckEnergyNeededForAttack
+ jr nc, .second_attack
+ ld a, b
+ or a
+ jr nz, .check_deck
+ ld a, c
+ or a
+ jr nz, .check_deck
+
+.second_attack
+ ld a, SECOND_ATTACK
+ ld [wSelectedAttack], a
+ call CheckEnergyNeededForAttack
+ jr nc, .check_discard_or_energy_boost
+ ld a, b
+ or a
+ jr nz, .check_deck
+ ld a, c
+ or a
+ jr nz, .check_deck
+
+; neither attack needs energy cards to be used.
+; check whether these attacks can be given
+; extra energy cards for their effects.
+.check_discard_or_energy_boost
+ ld a, $01
+ ld [wTempAI], a
+
+; for both attacks, check if it has the effect of
+; discarding energy cards or attached energy boost.
+ xor a ; FIRST_ATTACK_OR_PKMN_POWER
+ ld [wSelectedAttack], a
+ call CheckEnergyNeededForAttack
+ ld a, MOVE_FLAG2_ADDRESS | ATTACHED_ENERGY_BOOST_F
+ call CheckLoadedMoveFlag
+ jr c, .energy_boost_or_discard_energy
+ ld a, MOVE_FLAG2_ADDRESS | DISCARD_ENERGY_F
+ call CheckLoadedMoveFlag
+ jr c, .energy_boost_or_discard_energy
+
+ ld a, SECOND_ATTACK
+ ld [wSelectedAttack], a
+ call CheckEnergyNeededForAttack
+ ld a, MOVE_FLAG2_ADDRESS | ATTACHED_ENERGY_BOOST_F
+ call CheckLoadedMoveFlag
+ jr c, .energy_boost_or_discard_energy
+ ld a, MOVE_FLAG2_ADDRESS | DISCARD_ENERGY_F
+ call CheckLoadedMoveFlag
+ jr c, .energy_boost_or_discard_energy
+
+; if none of the attacks have those flags, do an additional
+; check to ascertain whether evolution card needs energy
+; to use second attack. Return if all these checks fail.
+ call CheckIfEvolutionNeedsEnergyForMove
+ ret nc
+ call CreateEnergyCardListFromHand
+ jr .check_deck
+
+; for attacks that discard energy or get boost for
+; additional energy cards, get the energy card ID required by move.
+; if it's Zapdos2's Thunderbolt move, return.
+.energy_boost_or_discard_energy
+ call GetEnergyCardForDiscardOrEnergyBoostAttack
+ ret nc
+
+; some decks allow basic Pokémon to be given double colorless
+; in anticipation for evolution, so play card if that is the case.
+.check_deck
+ call CheckSpecificDecksToAttachDoubleColorless
+ jr c, .play_energy_card
-Func_1689f ; 1689f (5:689f)
- INCROM $1689f, $169f8
+ ld a, b
+ or a
+ jr z, .colorless_energy
+
+; in this case, Pokémon needs a specific basic energy card.
+; look for basic energy card needed in hand and play it.
+ ld a, e
+ call LookForCardIDInHand
+ ldh [hTemp_ffa0], a
+ jr nc, .play_energy_card
+
+; in this case Pokémon just needs colorless (any basic energy card).
+; if active card, check if it needs 2 colorless.
+; if it does (and also doesn't additionally need a color energy),
+; look for double colorless card in hand and play it if found.
+.colorless_energy
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ or a
+ jr nz, .look_for_any_energy
+ ld a, c
+ or a
+ jr z, .check_if_done
+ cp 2
+ jr nz, .look_for_any_energy
-Func_169f8 ; 169f8 (5:69f8)
- INCROM $169f8, $170c9
+ ; needs two colorless
+ ld hl, wDuelTempList
+.loop_1
+ ld a, [hli]
+ cp $ff
+ jr z, .look_for_any_energy
+ ldh [hTemp_ffa0], a
+ call GetCardIDFromDeckIndex
+ ld a, e
+ cp DOUBLE_COLORLESS_ENERGY
+ jr nz, .loop_1
+ jr .play_energy_card
+
+; otherwise, look for any card and play it.
+; if it's a boss deck, only play double colorless in this situation.
+.look_for_any_energy
+ ld hl, wDuelTempList
+ call CountCardsInDuelTempList
+ call ShuffleCards
+.loop_2
+ ld a, [hli]
+ cp $ff
+ jr z, .check_if_done
+ call CheckIfOpponentHasBossDeckID
+ jr nc, .load_card
+ push af
+ call GetCardIDFromDeckIndex
+ ld a, e
+ cp DOUBLE_COLORLESS_ENERGY
+ pop bc
+ jr z, .loop_2
+ ld a, b
+.load_card
+ ldh [hTemp_ffa0], a
+
+; plays energy card loaded in hTemp_ffa0 and sets carry flag.
+.play_energy_card
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, OPPACTION_PLAY_ENERGY
+ bank1call AIMakeDecision
+ scf
+ ret
+
+; wTempAI is 1 if the attack had a Discard/Energy Boost effect,
+; and 0 otherwise. If 1, then return. If not one, check if
+; there is still a second attack to check.
+.check_if_done
+ ld a, [wTempAI]
+ or a
+ jr z, .check_first_attack
+ ret
+.check_first_attack
+ ld a, [wSelectedAttack]
+ or a
+ jp z, .second_attack
+ ret
+; 0x1696e
+
+; check if playing certain decks so that AI can decide whether to play
+; double colorless to some specific cards.
+; these are cards that do not need double colorless to any of their moves
+; but are required by their evolutions.
+; return carry if there's a double colorless in hand to attach
+; and it's one of the card IDs from these decks.
+; output:
+; [hTemp_ffa0] = card index of double colorless in hand;
+; carry set if can play energy card.
+CheckSpecificDecksToAttachDoubleColorless: ; 1696e (5:696e)
+ push bc
+ push de
+ push hl
+
+; check if AI is playing any of the aplicable decks.
+ ld a, [wOpponentDeckID]
+ cp LEGENDARY_DRAGONITE_DECK_ID
+ jr z, .legendary_dragonite_deck
+ cp FIRE_CHARGE_DECK_ID
+ jr z, .fire_charge_deck
+ cp LEGENDARY_RONALD_DECK_ID
+ jr z, .legendary_ronald_deck
+
+.no_carry
+ pop hl
+ pop de
+ pop bc
+ or a
+ ret
+
+; if playing Legendary Dragonite deck,
+; check for Charmander and Dratini.
+.legendary_dragonite_deck
+ call .get_id
+ cp CHARMANDER
+ jr z, .check_colorless_attached
+ cp DRATINI
+ jr z, .check_colorless_attached
+ jr .no_carry
+
+; if playing Fire Charge deck,
+; check for Growlithe.
+.fire_charge_deck
+ call .get_id
+ cp GROWLITHE
+ jr z, .check_colorless_attached
+ jr .no_carry
+
+; if playing Legendary Ronald deck,
+; check for Dratini.
+.legendary_ronald_deck
+ call .get_id
+ cp DRATINI
+ jr z, .check_colorless_attached
+ jr .no_carry
+
+; check if card has any colorless energy cards attached,
+; and if there are any, return no carry.
+.check_colorless_attached
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ ld e, a
+ call GetPlayAreaCardAttachedEnergies
+ ld a, [wAttachedEnergies + COLORLESS]
+ or a
+ jr nz, .no_carry
+
+; card has no colorless energy, so look for double colorless
+; in hand and if found, return carry and its card index.
+ ld a, DOUBLE_COLORLESS_ENERGY
+ call LookForCardIDInHand
+ jr c, .no_carry
+ ldh [hTemp_ffa0], a
+ pop hl
+ pop de
+ pop bc
+ scf
+ ret
+
+.get_id:
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ add DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ ret
+; 0x169ca
+
+Func_169ca: ; 169ca (5:69ca)
+ ld a, $01
+ ld [wcdd9], a
+
+; copy wPlayAreaAIScore to wTempPlayAreaAIScore.
+ ld de, wTempPlayAreaAIScore
+ ld hl, wPlayAreaAIScore
+ ld b, MAX_PLAY_AREA_POKEMON
+.loop
+ ld a, [hli]
+ ld [de], a
+ inc de
+ dec b
+ jr nz, .loop
+
+; copies wAIScore to wcde3
+ ld a, [wAIScore]
+ ld [de], a
+ jr Func_169fc
+
+; copies wTempPlayAreaAIScore to wPlayAreaAIScore
+; and loads wAIscore with value in wcde3.
+; identical to Func_164d3.
+Func_169e3: ; 169e3 (5:69e3)
+ push af
+ ld de, wPlayAreaAIScore
+ ld hl, wTempPlayAreaAIScore
+ ld b, MAX_PLAY_AREA_POKEMON
+.loop
+ ld a, [hli]
+ ld [de], a
+ inc de
+ dec b
+ jr nz, .loop
+
+ ld a, [hl]
+ ld [wAIScore], a
+ pop af
+ ret
+; 0x169f8
+
+Func_169f8: ; 169f8 (5:69f8)
+ xor a
+ ld [wcdd9], a
+ ; fallthrough
+
+Func_169fc: ; 169fc (5:69fc)
+; if AI used Pluspower, load its attack index
+ ld a, [wPreviousAIFlags]
+ and AI_FLAG_USED_PLUSPOWER
+ jr z, .no_pluspower
+ ld a, [wAIPluspowerAttack]
+ ld [wSelectedAttack], a
+ jr .attack_chosen
+
+.no_pluspower
+ ld a, [wcda7]
+ cp $80
+ jp z, .asm_16a77
+
+; determine AI score of both attacks.
+ xor a ; FIRST_ATTACK_OR_PKMN_POWER
+ call GetAIScoreOfAttack
+ ld a, [wAIScore]
+ ld [wPlayAreaAIScore], a
+ ld a, SECOND_ATTACK
+ call GetAIScoreOfAttack
+
+; compare both attack scores
+ ld c, SECOND_ATTACK
+ ld a, [wPlayAreaAIScore]
+ ld b, a
+ ld a, [wAIScore]
+ cp b
+ jr nc, .asm_16a30
+ ; first attack has higher score
+ dec c
+ ld a, b
+
+; c holds the attack index chosen by AI,
+; and a holds its AI score.
+; 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
+ cp $50 ; minimum score to use attack
+ jr c, .asm_16a77
+ ; enough score, proceed
+
+ ld a, c
+ ld [wSelectedAttack], a
+ or a
+ jr z, .attack_chosen
+ call CheckWhetherToSwitchToFirstAttack
+
+.attack_chosen
+ ld a, [wcdd9]
+ or a
+ jr z, .asm_16a48
+ scf
+ jp Func_169e3
+
+.asm_16a48
+ ld a, $0e
+ call AIProcessHandTrainerCards
+
+; load this attack's damage output against
+; the current Defending Pokemon.
+ xor a ; PLAY_AREA_ARENA
+ ldh [hTempPlayAreaLocation_ff9d], a
+ ld a, [wSelectedAttack]
+ call EstimateDamage_VersusDefendingCard
+ ld a, [wDamage]
+
+ or a
+ jr z, .asm_16a62
+ ; if damage is 0, fallthrough
+
+.cannot_damage
+ xor a
+ ld [wcdb4], a
+ jr .asm_16a6d
+
+.asm_16a62
+ ld a, MOVE_FLAG1_ADDRESS | DAMAGE_TO_OPPONENT_BENCH_F
+ call CheckLoadedMoveFlag
+ jr c, .cannot_damage
+ ld hl, wcdb4
+ inc [hl]
+.asm_16a6d
+ ld a, $01
+ ld [wcddb], a
+ call Func_14145
+ scf
+ ret
+.asm_16a77
+ ld a, [wcdd9]
+ or a
+ jr z, .asm_16a80
+ jp Func_169e3
+.asm_16a80
+ ld hl, wcdb4
+ inc [hl]
+ or a
+ ret
+; 0x16a86
+
+; determines the AI score of attack index in a.
+GetAIScoreOfAttack: ; 16a86 (5:6a86)
+; initialize AI score.
+ ld [wSelectedAttack], a
+ ld a, $50
+ ld [wAIScore], a
+
+ xor a
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call CheckIfSelectedMoveIsUnusable
+ jr nc, .usable
+
+; return zero AI score.
+.unusable
+ xor a
+ ld [wAIScore], a
+ jp .done
+
+; load arena card IDs
+.usable
+ xor a
+ ld [wAICannotDamage], a
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ ld [wTempTurnDuelistCardID], a
+ call SwapTurn
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ ld [wTempNonTurnDuelistCardID], a
+
+; handle the case where the player has No Damage substatus.
+; in the case the player does, check if this move
+; has a residual effect, or if it can damage the opposing bench.
+; If none of those are true, render the move unusable.
+; also if it's a PKMN power, consider it unusable as well.
+ bank1call HandleNoDamageOrEffectSubstatus
+ call SwapTurn
+ jr nc, .check_if_can_ko
+
+ ; player is under No Damage substatus
+ ld a, $01
+ ld [wAICannotDamage], a
+ ld a, [wSelectedAttack]
+ call EstimateDamage_VersusDefendingCard
+ ld a, [wLoadedMoveCategory]
+ cp POKEMON_POWER
+ jr z, .unusable
+ and RESIDUAL
+ jr nz, .check_if_can_ko
+ ld a, MOVE_FLAG1_ADDRESS | DAMAGE_TO_OPPONENT_BENCH_F
+ call CheckLoadedMoveFlag
+ jr nc, .unusable
+
+; calculate damage to player to check if move can KO.
+; encourage move if it's able to KO.
+.check_if_can_ko
+ ld a, [wSelectedAttack]
+ call EstimateDamage_VersusDefendingCard
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetNonTurnDuelistVariable
+ ld hl, wDamage
+ sub [hl]
+ jr c, .can_ko
+ jr z, .can_ko
+ jr .check_damage
+.can_ko
+ ld a, 20
+ call AddToAIScore
+
+; raise AI score by the number of damage counters that this move deals.
+; if no damage is dealt, subtract AI score. in case wDamage is zero
+; but wMaxDamage is not, then encourage move afterwards.
+; otherwise, if wMaxDamage is also zero, check for damage against
+; player's bench, and encourage move in case there is.
+.check_damage
+ xor a
+ ld [wAIMoveIsNonDamaging], a
+ ld a, [wDamage]
+ ld [wTempAI], a
+ or a
+ jr z, .no_damage
+ call CalculateByteTensDigit
+ call AddToAIScore
+ jr .check_recoil
+.no_damage
+ ld a, $01
+ ld [wAIMoveIsNonDamaging], a
+ call SubFromAIScore
+ ld a, [wAIMaxDamage]
+ or a
+ jr z, .no_max_damage
+ ld a, 2
+ call AddToAIScore
+ xor a
+ ld [wAIMoveIsNonDamaging], a
+.no_max_damage
+ ld a, MOVE_FLAG1_ADDRESS | DAMAGE_TO_OPPONENT_BENCH_F
+ call CheckLoadedMoveFlag
+ jr nc, .check_recoil
+ ld a, 2
+ call AddToAIScore
+
+; handle recoil moves (low and high recoil).
+.check_recoil
+ ld a, MOVE_FLAG1_ADDRESS | LOW_RECOIL_F
+ call CheckLoadedMoveFlag
+ jr c, .is_recoil
+ ld a, MOVE_FLAG1_ADDRESS | HIGH_RECOIL_F
+ call CheckLoadedMoveFlag
+ jp nc, .check_defending_can_ko
+.is_recoil
+ ; sub from AI score number of damage counters
+ ; that move deals to itself.
+ ld a, [wLoadedMoveEffectParam]
+ or a
+ jp z, .check_defending_can_ko
+ ld [wDamage], a
+ call ApplyDamageModifiers_DamageToSelf
+ ld a, e
+ call CalculateByteTensDigit
+ call SubFromAIScore
+
+ push de
+ ld a, MOVE_FLAG1_ADDRESS | HIGH_RECOIL_F
+ call CheckLoadedMoveFlag
+ pop de
+ jr c, .high_recoil
+
+ ; if LOW_RECOIL KOs self, decrease AI score
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetTurnDuelistVariable
+ cp e
+ jr c, .kos_self
+ jp nz, .check_defending_can_ko
+.kos_self
+ ld a, 10
+ call SubFromAIScore
+
+.high_recoil
+ ; dismiss this move if no benched Pokémon
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp 2
+ jr c, .dismiss_high_recoil_move
+ ; has benched Pokémon
+
+; here the AI handles high recoil moves differently
+; depending on what deck it's playing.
+ ld a, [wOpponentDeckID]
+ cp ROCK_CRUSHER_DECK_ID
+ jr z, .rock_crusher_deck
+ cp ZAPPING_SELFDESTRUCT_DECK_ID
+ jr z, .zapping_selfdestruct_deck
+ cp BOOM_BOOM_SELFDESTRUCT_DECK_ID
+ jr z, .encourage_high_recoil_move
+ ; Boom Boom Selfdestruct deck always encourages
+ cp POWER_GENERATOR_DECK_ID
+ jr nz, .high_recoil_generic_checks
+ ; Power Generator deck always dismisses
+
+.dismiss_high_recoil_move
+ xor a
+ ld [wAIScore], a
+ jp .done
+
+.encourage_high_recoil_move
+ ld a, 20
+ call AddToAIScore
+ jp .done
+
+; Zapping Selfdestruct deck only uses this move
+; if number of cards in deck >= 30 and
+; HP of active card is < half max HP.
+.zapping_selfdestruct_deck
+ ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
+ call GetTurnDuelistVariable
+ cp 31
+ jr nc, .high_recoil_generic_checks
+ ld e, PLAY_AREA_ARENA
+ call GetCardDamage
+ sla a
+ cp c
+ jr c, .high_recoil_generic_checks
+ ld b, 0
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ cp MAGNEMITE1
+ jr z, .magnemite1
+ ld b, 10 ; bench damage
+.magnemite1
+ ld a, 10
+ add b
+ ld b, a ; 20 bench damage if not Magnemite1
+
+; if this move causes player to win the duel by
+; knocking out own Pokémon, dismiss move.
+ ld a, 1 ; count active Pokémon as KO'd
+ call .check_if_kos_bench
+ jr c, .dismiss_high_recoil_move
+ jr .encourage_high_recoil_move
+
+; Rock Crusher Deck only uses this move if
+; prize count is below 4 and move wins (or potentially draws) the duel,
+; (i.e. at least gets KOs equal to prize cards left).
+.rock_crusher_deck
+ call CountPrizes
+ cp 4
+ jr nc, .dismiss_high_recoil_move
+ ; prize count < 4
+ ld b, 20 ; damage dealt to bench
+ call SwapTurn
+ xor a
+ call .check_if_kos_bench
+ call SwapTurn
+ jr c, .encourage_high_recoil_move
+
+; generic checks for all other deck IDs.
+; encourage move if it wins (or potentially draws) the duel,
+; (i.e. at least gets KOs equal to prize cards left).
+; dismiss it if it causes the player to win.
+.high_recoil_generic_checks
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ cp CHANSEY
+ jr z, .chansey
+ cp MAGNEMITE1
+ jr z, .magnemite1_or_weezing
+ cp WEEZING
+ jr z, .magnemite1_or_weezing
+ ld b, 20 ; bench damage
+ jr .check_bench_kos
+.magnemite1_or_weezing
+ ld b, 10 ; bench damage
+ jr .check_bench_kos
+.chansey
+ ld b, 0 ; no bench damage
+
+.check_bench_kos
+ push bc
+ call SwapTurn
+ xor a
+ call .check_if_kos_bench
+ call SwapTurn
+ pop bc
+ jr c, .wins_the_duel
+ push de
+ ld a, 1
+ call .check_if_kos_bench
+ pop bc
+ jr nc, .count_own_ko_bench
+
+; move causes player to draw all prize cards
+ xor a
+ ld [wAIScore], a
+ jp .done
+
+; move causes CPU to draw all prize cards
+.wins_the_duel
+ ld a, 20
+ call AddToAIScore
+ jp .done
+
+; subtract from AI score number of own benched Pokémon KO'd
+.count_own_ko_bench
+ push bc
+ ld a, d
+ or a
+ jr z, .count_player_ko_bench
+ dec a
+ call SubFromAIScore
+
+; add to AI score number of player benched Pokémon KO'd
+.count_player_ko_bench
+ pop bc
+ ld a, b
+ call AddToAIScore
+ jr .check_defending_can_ko
+
+; local function that gets called to determine damage to
+; benched Pokémon caused by a HIGH_RECOIL move.
+; return carry if using move causes number of benched Pokémon KOs
+; equal to or larger than remaining prize cards.
+; this function is independent on duelist turn, so whatever
+; turn it is when this is called, it's that duelist's
+; bench/prize cards that get checked.
+; input:
+; a = initial number of KO's beside benched Pokémon,
+; so that if the active Pokémon is KO'd by the move,
+; this counts towards the prize cards collected
+; b = damage dealt to bench Pokémon
+.check_if_kos_bench
+ ld d, a
+ ld a, DUELVARS_BENCH
+ call GetTurnDuelistVariable
+ ld e, PLAY_AREA_ARENA
+.loop
+ inc e
+ ld a, [hli]
+ cp $ff
+ jr z, .exit_loop
+ ld a, e
+ add DUELVARS_ARENA_CARD_HP
+ push hl
+ call GetTurnDuelistVariable
+ pop hl
+ cp b
+ jr z, .increase_count
+ jr nc, .loop
+.increase_count
+ ; increase d if damage dealt KOs
+ inc d
+ jr .loop
+.exit_loop
+ push de
+ call SwapTurn
+ call CountPrizes
+ call SwapTurn
+ pop de
+ cp d
+ jp c, .set_carry
+ jp z, .set_carry
+ or a
+ ret
+.set_carry
+ scf
+ ret
+
+; if defending card can KO, encourage move
+; unless move is non-damaging.
+.check_defending_can_ko
+ ld a, [wSelectedAttack]
+ push af
+ call CheckIfDefendingPokemonCanKnockOut
+ pop bc
+ ld a, b
+ ld [wSelectedAttack], a
+ jr nc, .check_discard
+ ld a, 5
+ call AddToAIScore
+ ld a, [wAIMoveIsNonDamaging]
+ or a
+ jr z, .check_discard
+ ld a, 5
+ call SubFromAIScore
+
+; subtract from AI score if this move requires
+; discarding any energy cards.
+.check_discard
+ ld a, [wSelectedAttack]
+ ld e, a
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ ld d, a
+ call CopyMoveDataAndDamage_FromDeckIndex
+ ld a, MOVE_FLAG2_ADDRESS | DISCARD_ENERGY_F
+ call CheckLoadedMoveFlag
+ jr nc, .asm_16ca6
+ ld a, 1
+ call SubFromAIScore
+ ld a, [wLoadedMoveEffectParam]
+ call SubFromAIScore
+
+.asm_16ca6
+ ld a, MOVE_FLAG2_ADDRESS | FLAG_2_BIT_6_F
+ call CheckLoadedMoveFlag
+ jr nc, .check_nullify_flag
+ ld a, [wLoadedMoveEffectParam]
+ call AddToAIScore
+
+; encourage move if it has a nullify or weaken attack effect.
+.check_nullify_flag
+ ld a, MOVE_FLAG2_ADDRESS | NULLIFY_OR_WEAKEN_ATTACK_F
+ call CheckLoadedMoveFlag
+ jr nc, .check_draw_flag
+ ld a, 1
+ call AddToAIScore
+
+; encourage move if it has an effect to draw a card.
+.check_draw_flag
+ ld a, MOVE_FLAG1_ADDRESS | DRAW_CARD_F
+ call CheckLoadedMoveFlag
+ jr nc, .check_heal_flag
+ ld a, 1
+ call AddToAIScore
+
+.check_heal_flag
+ ld a, MOVE_FLAG2_ADDRESS | HEAL_USER_F
+ call CheckLoadedMoveFlag
+ jr nc, .check_status_effect
+ ld a, [wLoadedMoveEffectParam]
+ cp 1
+ jr z, .tally_heal_score
+ ld a, [wTempAI]
+ call CalculateByteTensDigit
+ ld b, a
+ ld a, [wLoadedMoveEffectParam]
+ cp 3
+ jr z, .asm_16cec
+ srl b
+ jr nc, .asm_16cec
+ inc b
+.asm_16cec
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetTurnDuelistVariable
+ call CalculateByteTensDigit
+ cp b
+ jr c, .tally_heal_score
+ ld a, b
+.tally_heal_score
+ push af
+ ld e, PLAY_AREA_ARENA
+ call GetCardDamage
+ call CalculateByteTensDigit
+ pop bc
+ cp b ; wLoadedMoveEffectParam
+ jr c, .add_heal_score
+ ld a, b
+.add_heal_score
+ call AddToAIScore
+
+.check_status_effect
+ ld a, DUELVARS_ARENA_CARD
+ call GetNonTurnDuelistVariable
+ call SwapTurn
+ call GetCardIDFromDeckIndex
+ call SwapTurn
+ ld a, e
+ ; skip if player has Snorlax
+ cp SNORLAX
+ jp z, .handle_flag3_bit1
+
+ ld a, DUELVARS_ARENA_CARD_STATUS
+ call GetNonTurnDuelistVariable
+ ld [wTempAI], a
+
+; encourage a poison inflicting move if opposing Pokémon
+; isn't (doubly) poisoned already.
+; if opposing Pokémon is only poisoned and not double poisoned,
+; and this move has FLAG_2_BIT_6 set, discourage it
+; (possibly to make Nidoking's Toxic attack less likely to be chosen
+; if the other Pokémon is poisoned.)
+ ld a, MOVE_FLAG1_ADDRESS | INFLICT_POISON_F
+ call CheckLoadedMoveFlag
+ jr nc, .check_sleep
+ ld a, [wTempAI]
+ and DOUBLE_POISONED
+ jr z, .add_poison_score
+ and $40 ; only double poisoned?
+ jr z, .check_sleep
+ ld a, MOVE_FLAG2_ADDRESS | FLAG_2_BIT_6_F
+ call CheckLoadedMoveFlag
+ jr nc, .check_sleep
+ ld a, 2
+ call SubFromAIScore
+ jr .check_sleep
+.add_poison_score
+ ld a, 2
+ call AddToAIScore
+
+; encourage sleep-inducing move if other Pokémon isn't asleep.
+.check_sleep
+ ld a, MOVE_FLAG1_ADDRESS | INFLICT_SLEEP_F
+ call CheckLoadedMoveFlag
+ jr nc, .check_paralysis
+ ld a, [wTempAI]
+ and CNF_SLP_PRZ
+ cp ASLEEP
+ jr z, .check_paralysis
+ ld a, 1
+ call AddToAIScore
+
+; encourage paralysis-inducing move if other Pokémon isn't asleep.
+; otherwise, if other Pokémon is asleep, discourage move.
+.check_paralysis
+ ld a, MOVE_FLAG1_ADDRESS | INFLICT_PARALYSIS_F
+ call CheckLoadedMoveFlag
+ jr nc, .check_confusion
+ ld a, [wTempAI]
+ and CNF_SLP_PRZ
+ cp ASLEEP
+ jr z, .sub_prz_score
+ ld a, 1
+ call AddToAIScore
+ jr .check_confusion
+.sub_prz_score
+ ld a, 1
+ call SubFromAIScore
+
+; encourage confuse-inducing move if other Pokémon isn't asleep
+; or confused already.
+; otherwise, if other Pokémon is asleep or confused,
+; discourage move instead.
+.check_confusion
+ ld a, MOVE_FLAG1_ADDRESS | INFLICT_CONFUSION_F
+ call CheckLoadedMoveFlag
+ jr nc, .check_if_confused
+ ld a, [wTempAI]
+ and CNF_SLP_PRZ
+ cp ASLEEP
+ jr z, .sub_cnf_score
+ ld a, [wTempAI]
+ and CNF_SLP_PRZ
+ cp CONFUSED
+ jr z, .check_if_confused
+ ld a, 1
+ call AddToAIScore
+ jr .check_if_confused
+.sub_cnf_score
+ ld a, 1
+ call SubFromAIScore
+
+; if this Pokémon is confused, subtract from score.
+.check_if_confused
+ ld a, DUELVARS_ARENA_CARD_STATUS
+ call GetTurnDuelistVariable
+ and CNF_SLP_PRZ
+ cp CONFUSED
+ jr nz, .handle_flag3_bit1
+ ld a, 1
+ call SubFromAIScore
+
+; flag3_bit1 marks moves that the AI handles individually.
+; each move has its own checks and modifies AI score accordingly.
+.handle_flag3_bit1
+ ld a, MOVE_FLAG3_ADDRESS | FLAG_3_BIT_1_F
+ call CheckLoadedMoveFlag
+ jr nc, .done
+ call HandleSpecialAIMoves
+ cp $80
+ jr c, .negative_score
+ sub $80
+ call AddToAIScore
+ jr .done
+.negative_score
+ ld b, a
+ ld a, $80
+ sub b
+ call SubFromAIScore
+
+.done
+ ret
+; 0x16dcd
+
+; this function handles moves with the FLAG_3_BIT_1 set,
+; and makes specific checks in each of these moves
+; to either return a positive score (value above $80)
+; or a negative score (value below $80).
+; input:
+; hTempPlayAreaLocation_ff9d = location of card with move.
+HandleSpecialAIMoves: ; 16dcd (5:6dcd)
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ add DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+
+ cp NIDORANF
+ jr z, HandleNidoranFCallForFamily
+ cp ODDISH
+ jr z, HandleCallForFamily
+ cp BELLSPROUT
+ jr z, HandleCallForFamily
+ cp EXEGGUTOR
+ jp z, HandleExeggcutorTeleport
+ cp SCYTHER
+ jp z, HandleSwordsDanceAndFocusEnergy
+ cp KRABBY
+ jr z, HandleCallForFamily
+ cp VAPOREON1
+ jp z, HandleSwordsDanceAndFocusEnergy
+ cp ELECTRODE2
+ jp z, HandleElectrode2ChainLightning
+ cp MAROWAK1
+ jr z, HandleMarowak1CallForFriend
+ cp MEW3
+ jp z, HandleMew3DevolutionBeam
+ cp JIGGLYPUFF2
+ jp z, HandleJigglypuff2FriendshipSong
+ cp PORYGON
+ jp z, HandlePorygonConversion
+ cp MEWTWO3
+ jp z, HandleEnergyAbsorption
+ cp MEWTWO2
+ jp z, HandleEnergyAbsorption
+ cp NINETAILS2
+ jp z, HandleNinetalesMixUp
+ cp ZAPDOS3
+ jp z, HandleZapdos3BigThunder
+ cp KANGASKHAN
+ jp z, HandleKangaskhanFetch
+ cp DUGTRIO
+ jp z, HandleDugtrioEarthquake
+ cp ELECTRODE1
+ jp z, HandleElectrode1EnergySpike
+ cp GOLDUCK
+ jp z, HandleHyperBeam
+ cp DRAGONAIR
+ jp z, HandleHyperBeam
+
+; return zero score.
+.zero
+ xor a
+ ret
+
+; if any of card ID in a is found in deck,
+; return a score of $80 + slots available in bench.
+HandleCallForFamily:
+ ld a, CARD_LOCATION_DECK
+ call CheckIfAnyCardIDinLocation
+ jr nc, HandleSpecialAIMoves.zero
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp MAX_BENCH_POKEMON
+ jr nc, HandleSpecialAIMoves.zero
+ ld b, a
+ ld a, MAX_BENCH_POKEMON
+ sub b
+ add $80
+ ret
+
+; if any of NidoranM or NidoranF is found in deck,
+; return a score of $80 + slots available in bench.
+HandleNidoranFCallForFamily:
+ ld e, NIDORANM
+ ld a, CARD_LOCATION_DECK
+ call CheckIfAnyCardIDinLocation
+ jr c, .found
+ ld e, NIDORANF
+ ld a, CARD_LOCATION_DECK
+ call CheckIfAnyCardIDinLocation
+ jr nc, HandleSpecialAIMoves.zero
+.found
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp MAX_PLAY_AREA_POKEMON
+ jr nc, HandleSpecialAIMoves.zero
+ ld b, a
+ ld a, MAX_PLAY_AREA_POKEMON
+ sub b
+ add $80
+ ret
+
+; checks for certain card IDs of Fighting color in deck.
+; if any of them are found, return a score of
+; $80 + slots available in bench.
+HandleMarowak1CallForFriend
+ ld e, GEODUDE
+ ld a, CARD_LOCATION_DECK
+ call CheckIfAnyCardIDinLocation
+ jr c, .found
+ ld e, ONIX
+ ld a, CARD_LOCATION_DECK
+ call CheckIfAnyCardIDinLocation
+ jr c, .found
+ ld e, CUBONE
+ ld a, CARD_LOCATION_DECK
+ call CheckIfAnyCardIDinLocation
+ jr c, .found
+ ld e, RHYHORN
+ ld a, CARD_LOCATION_DECK
+ call CheckIfAnyCardIDinLocation
+ jr c, .found
+ jr HandleSpecialAIMoves.zero
+.found
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp MAX_BENCH_POKEMON
+ jr nc, HandleSpecialAIMoves.zero
+ ld b, a
+ ld a, MAX_BENCH_POKEMON
+ sub b
+ add $80
+ ret
+
+; if any basic cards are found in deck,
+; return a score of $80 + slots available in bench.
+HandleJigglypuff2FriendshipSong: ; 16ead (5:6ead)
+ call CheckIfAnyBasicPokemonInDeck
+ jr nc, HandleSpecialAIMoves.zero
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp MAX_PLAY_AREA_POKEMON
+ jr nc, HandleSpecialAIMoves.zero
+ ld b, a
+ ld a, MAX_PLAY_AREA_POKEMON
+ sub b
+ add $80
+ ret
+
+; if AI decides to retreat, return a score of $80 + 10.
+HandleExeggcutorTeleport: ; 16ec2 (5:6ec2)
+ call AIDecideWhetherToRetreat
+ jp nc, HandleSpecialAIMoves.zero
+ ld a, $8a
+ ret
+
+; tests for the following conditions:
+; - player is under No Damage substatus;
+; - second move is unusable;
+; - second move deals no damage;
+; if any are true, returns score of $80 + 5.
+HandleSwordsDanceAndFocusEnergy: ; 16ecb (5:6ecb)
+ ld a, [wAICannotDamage]
+ or a
+ jr nz, .success
+ ld a, $01 ; second move
+ ld [wSelectedAttack], a
+ call CheckIfSelectedMoveIsUnusable
+ jr c, .success
+ ld a, $01 ; second move
+ call EstimateDamage_VersusDefendingCard
+ ld a, [wDamage]
+ or a
+ jp nz, HandleSpecialAIMoves.zero
+.success
+ ld a, $85
+ ret
+
+; checks player's active card color, then
+; loops through bench looking for a Pokémon
+; with that same color.
+; if none are found, returns score of $80 + 2.
+HandleElectrode2ChainLightning: ; 16eea (5:6eea)
+ call SwapTurn
+ call GetArenaCardColor
+ call SwapTurn
+ ld b, a
+ ld a, DUELVARS_BENCH
+ call GetTurnDuelistVariable
+.loop
+ ld a, [hli]
+ cp $ff
+ jr z, .success
+ push bc
+ call GetCardIDFromDeckIndex
+ call GetCardType
+ pop bc
+ cp b
+ jr nz, .loop
+ jp HandleSpecialAIMoves.zero
+.success
+ ld a, $82
+ ret
+
+HandleMew3DevolutionBeam: ; 16f0f (5:6f0f)
+ call LookForCardThatIsKnockedOutOnDevolution
+ jp nc, HandleSpecialAIMoves.zero
+ ld a, $85
+ ret
+
+; first checks if card is confused, and if so return 0.
+; then checks number of Pokémon in bench that are viable to use:
+; - if that number is < 2 and this move is Conversion 1 OR
+; - if that number is >= 2 and this move is Conversion 2
+; then return score of $80 + 2.
+; otherwise return score of $80 + 1.
+HandlePorygonConversion: ; 16f18 (5:6f18)
+ ld a, DUELVARS_ARENA_CARD_STATUS
+ call GetTurnDuelistVariable
+ and CNF_SLP_PRZ
+ cp CONFUSED
+ jp z, HandleSpecialAIMoves.zero
+
+ ld a, [wSelectedAttack]
+ or a
+ jr nz, .conversion_2
+
+; conversion 1
+ call CheckIfBenchCardsAreAtHalfHPCanEvolveAndUseSecondMove
+ cp 2
+ jr c, .low_score
+ ld a, $82
+ ret
+
+.conversion_2
+ call CheckIfBenchCardsAreAtHalfHPCanEvolveAndUseSecondMove
+ cp 2
+ jr nc, .low_score
+ ld a, $82
+ ret
+
+.low_score
+ ld a, $81
+ ret
+
+; if any Psychic Energy is found in the Discard Pile,
+; return a score of $80 + 2.
+HandleEnergyAbsorption: ; 16f41 (5:6f41)
+ ld e, PSYCHIC_ENERGY
+ ld a, CARD_LOCATION_DISCARD_PILE
+ call CheckIfAnyCardIDinLocation
+ jp nc, HandleSpecialAIMoves.zero
+ ld a, $82
+ ret
+
+; if player has cards in hand, AI calls Random:
+; - 1/3 chance to encourage move regardless;
+; - 1/3 chance to dismiss move regardless;
+; - 1/3 change to make some checks to player's hand.
+; AI tallies number of basic cards in hand, and if this
+; number is >= 2, encourage move.
+; otherwise, if it finds an evolution card in hand that
+; can evolve a card in player's deck, encourage.
+; if encouraged, returns a score of $80 + 3.
+HandleNinetalesMixUp: ; 16f4e (5:6f4e)
+ ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND
+ call GetNonTurnDuelistVariable
+ or a
+ ret z
+
+ ld a, 3
+ call Random
+ or a
+ jr z, .encourage
+ dec a
+ ret z
+ call SwapTurn
+ call CreateHandCardList
+ call SwapTurn
+ or a
+ ret z ; return if no hand cards (again)
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetNonTurnDuelistVariable
+ cp 3
+ jr nc, .check_play_area
+
+ ld hl, wDuelTempList
+ ld b, 0
+.loop_hand
+ ld a, [hli]
+ cp $ff
+ jr z, .tally_basic_cards
+ push bc
+ call SwapTurn
+ call LoadCardDataToBuffer2_FromDeckIndex
+ call SwapTurn
+ pop bc
+ ld a, [wLoadedCard2Type]
+ cp TYPE_ENERGY
+ jr nc, .loop_hand
+ ld a, [wLoadedCard2Stage]
+ or a
+ jr nz, .loop_hand
+ ; is a basic Pokémon card
+ inc b
+ jr .loop_hand
+.tally_basic_cards
+ ld a, b
+ cp 2
+ jr nc, .encourage
+
+; less than 2 basic cards in hand
+.check_play_area
+ ld a, DUELVARS_ARENA_CARD
+ call GetNonTurnDuelistVariable
+.loop_play_area
+ ld a, [hli]
+ cp $ff
+ jp z, HandleSpecialAIMoves.zero
+ push hl
+ call SwapTurn
+ call CheckForEvolutionInList
+ call SwapTurn
+ pop hl
+ jr nc, .loop_play_area
+
+.encourage
+ ld a, $83
+ ret
+
+; return score of $80 + 3.
+HandleZapdos3BigThunder: ; 16fb8 (5:6fb8)
+ ld a, $83
+ ret
+
+; dismiss move if cards in deck <= 20.
+; otherwise return a score of $80 + 0.
+HandleKangaskhanFetch: ; 16fbb (5:6fbb)
+ ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
+ call GetTurnDuelistVariable
+ cp 41
+ jp nc, HandleSpecialAIMoves.zero
+ ld a, $80
+ ret
+
+; dismiss move if number of own benched cards which would
+; be KOd is greater than or equal to the number
+; of prize cards left for player.
+HandleDugtrioEarthquake: ; 16fc8 (5:6fc8)
+ ld a, DUELVARS_BENCH
+ call GetTurnDuelistVariable
+
+ lb de, 0, 0
+.loop
+ inc e
+ ld a, [hli]
+ cp $ff
+ jr z, .count_prizes
+ ld a, e
+ add DUELVARS_ARENA_CARD_HP
+ call GetTurnDuelistVariable
+ cp 20
+ jr nc, .loop
+ inc d
+ jr .loop
+
+.count_prizes
+ push de
+ call CountPrizes
+ pop de
+ cp d
+ jp c, HandleSpecialAIMoves.zero
+ jp z, HandleSpecialAIMoves.zero
+ ld a, $80
+ ret
+
+; if there's any lightning energy cards in deck,
+; return a score of $80 + 3.
+HandleElectrode1EnergySpike: ; 16ff2 (5:6ff2)
+ ld a, CARD_LOCATION_DECK
+ ld e, LIGHTNING_ENERGY
+ call CheckIfAnyCardIDinLocation
+ jp nc, HandleSpecialAIMoves.zero
+ call Func_164a1
+ jp nc, HandleSpecialAIMoves.zero
+ ld a, $83
+ ret
+
+; only incentivize move if player's active card,
+; has any energy cards attached, and if so,
+; return a score of $80 + 3.
+HandleHyperBeam: ; 17005 (5:7005)
+ call SwapTurn
+ ld e, PLAY_AREA_ARENA
+ call CountNumberOfEnergyCardsAttached
+ call SwapTurn
+ or a
+ jr z, .keep_score
+ ld a, $83
+ ret
+.keep_score
+ ld a, $80
+ ret
+; 0x17019
+
+; called when second attack is determined by AI to have
+; more AI score than the first attack, so that it checks
+; whether the first attack is a better alternative.
+CheckWhetherToSwitchToFirstAttack: ; 17019 (5:7019)
+; this checks whether the first attack is also viable
+; (has more than minimum score to be used)
+ ld a, [wPlayAreaAIScore]
+ cp $50
+ jr c, .keep_second_attack
+
+; first attack has more than minimum score to be used.
+; check if second attack can KO.
+; in case it can't, the AI keeps it as the attack to be used.
+; (possibly due to the assumption that if the
+; second attack cannot KO, the first attack can't KO as well.)
+ xor a
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call EstimateDamage_VersusDefendingCard
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetNonTurnDuelistVariable
+ ld hl, wDamage
+ sub [hl]
+ jr z, .check_flag
+ jr nc, .keep_second_attack
+
+; second attack can ko, check its flag.
+; in case its effect is to heal user or nullify/weaken damage
+; next turn, keep second move as the option.
+; otherwise switch to the first attack.
+.check_flag
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ ld d, a
+ ld e, $01 ; second attack
+ call CopyMoveDataAndDamage_FromDeckIndex
+ ld a, MOVE_FLAG2_ADDRESS | HEAL_USER_F
+ call CheckLoadedMoveFlag
+ jr c, .keep_second_attack
+ ld a, MOVE_FLAG2_ADDRESS | NULLIFY_OR_WEAKEN_ATTACK_F
+ call CheckLoadedMoveFlag
+ jr c, .keep_second_attack
+; switch to first attack
+ xor a
+ ld [wSelectedAttack], a
+ ret
+.keep_second_attack
+ ld a, $01
+ ld [wSelectedAttack], a
+ ret
+; 0x17057
+
+; returns carry if there are
+; any basic Pokémon cards in deck.
+CheckIfAnyBasicPokemonInDeck: ; 17057 (5:7057)
+ ld e, 0
+.loop
+ ld a, DUELVARS_CARD_LOCATIONS
+ add e
+ call GetTurnDuelistVariable
+ cp CARD_LOCATION_DECK
+ jr nz, .next
+ push de
+ ld a, e
+ call LoadCardDataToBuffer2_FromDeckIndex
+ pop de
+ ld a, [wLoadedCard2Type]
+ cp TYPE_ENERGY
+ jr nc, .next
+ ld a, [wLoadedCard2Stage]
+ or a
+ jr z, .set_carry
+.next
+ inc e
+ ld a, DECK_SIZE
+ cp e
+ jr nz, .loop
+ or a
+ ret
+.set_carry
+ scf
+ ret
+; 0x17080
+
+; checks in other Play Area for non-basic cards.
+; afterwards, that card is checked for damage,
+; and if the damage counters it has is greater than or equal
+; to the max HP of the card stage below it,
+; 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.
+LookForCardThatIsKnockedOutOnDevolution: ; 17080 (5:7080)
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ push af
+ call SwapTurn
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld b, a
+ ld c, PLAY_AREA_ARENA
+
+.loop
+ ld a, c
+ ldh [hTempPlayAreaLocation_ff9d], a
+ push bc
+ bank1call GetCardOneStageBelow
+ pop bc
+ jr c, .next
+ ; is not a basic card
+ ; compare its HP with current damage
+ ld a, d
+ push bc
+ call LoadCardDataToBuffer2_FromDeckIndex
+ pop bc
+ ld a, [wLoadedCard2HP]
+ ld [wTempAI], a
+ ld e, c
+ push bc
+ call GetCardDamage
+ pop bc
+ ld e, a
+ ld a, [wTempAI]
+ cp e
+ jr c, .set_carry
+ jr z, .set_carry
+.next
+ inc c
+ ld a, c
+ cp b
+ jr nz, .loop
+
+ call SwapTurn
+ pop af
+ ldh [hTempPlayAreaLocation_ff9d], a
+ or a
+ ret
+
+.set_carry
+ call SwapTurn
+ pop af
+ ldh [hTempPlayAreaLocation_ff9d], a
+ ld a, c
+ scf
+ ret
+; 0x170c9
; returns carry if the following conditions are met:
; - arena card HP >= half max HP
@@ -4227,18 +6286,18 @@ CheckIfArenaCardIsAtHalfHPCanEvolveAndUseSecondMove: ; 170c9 (5:70c9)
ld a, [wLoadedCard1Unknown2]
and %00010000
- jr z, .check_second_move
+ jr z, .check_second_attack
ld a, d
call CheckCardEvolutionInHandOrDeck
jr c, .no_carry
-.check_second_move
+.check_second_attack
xor a ; active card
ldh [hTempPlayAreaLocation_ff9d], a
ld a, $01 ; second move
- ld [wSelectedMoveIndex], a
+ ld [wSelectedAttack], a
push hl
- call CheckIfCardCanUseSelectedMove
+ call CheckIfSelectedMoveIsUnusable
pop hl
jr c, .no_carry
scf
@@ -4255,11 +6314,11 @@ CheckIfArenaCardIsAtHalfHPCanEvolveAndUseSecondMove: ; 170c9 (5:70c9)
; 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
-; that meet these requirements in b
+; that meet these requirements in a
CheckIfBenchCardsAreAtHalfHPCanEvolveAndUseSecondMove: ; 17101 (5:7101)
ldh a, [hTempPlayAreaLocation_ff9d]
ld d, a
- ld a, [wSelectedMoveIndex]
+ ld a, [wSelectedAttack]
ld e, a
push de
ld a, DUELVARS_BENCH
@@ -4291,7 +6350,7 @@ CheckIfBenchCardsAreAtHalfHPCanEvolveAndUseSecondMove: ; 17101 (5:7101)
ld a, [wLoadedCard1Unknown2]
and $10
- jr z, .check_second_move
+ jr z, .check_second_attack
ld a, d
push bc
@@ -4299,14 +6358,14 @@ CheckIfBenchCardsAreAtHalfHPCanEvolveAndUseSecondMove: ; 17101 (5:7101)
pop bc
jr c, .next
-.check_second_move
+.check_second_attack
ld a, c
ldh [hTempPlayAreaLocation_ff9d], a
ld a, $01 ; second move
- ld [wSelectedMoveIndex], a
+ ld [wSelectedAttack], a
push bc
push hl
- call CheckIfCardCanUseSelectedMove
+ call CheckIfSelectedMoveIsUnusable
pop hl
pop bc
jr c, .next
@@ -4317,7 +6376,7 @@ CheckIfBenchCardsAreAtHalfHPCanEvolveAndUseSecondMove: ; 17101 (5:7101)
pop hl
pop de
ld a, e
- ld [wSelectedMoveIndex], a
+ ld [wSelectedAttack], a
ld a, d
ldh [hTempPlayAreaLocation_ff9d], a
ld a, b
@@ -4332,17 +6391,19 @@ Func_17161 ; 17161 (5:7161)
; return carry if Pokémon at play area location
; in hTempPlayAreaLocation_ff9d does not have
-; energy required for the move index in wSelectedMoveIndex
+; energy required for the move index in wSelectedAttack
; or has exactly the same amount of energy needed
; input:
; [hTempPlayAreaLocation_ff9d] = play area location
-; [wSelectedMoveIndex] = move index to check
-Func_171fb: ; 171fb (5:71fb)
+; [wSelectedAttack] = move index to check
+; output:
+; a = number of extra energy cards attached
+CheckIfNoSurplusEnergyForMove: ; 171fb (5:71fb)
ldh a, [hTempPlayAreaLocation_ff9d]
add DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
ld d, a
- ld a, [wSelectedMoveIndex]
+ ld a, [wSelectedAttack]
ld e, a
call CopyMoveDataAndDamage_FromDeckIndex
ld hl, wLoadedMoveName
@@ -4394,7 +6455,7 @@ Func_171fb: ; 171fb (5:71fb)
or a
ret nz ; return if surplus energy
-; exactly the amount of energy needed
+ ; exactly the amount of energy needed
scf
ret
; 0x17258
@@ -4434,16 +6495,19 @@ CalculateParticularAttachedEnergyNeeded: ; 17258 (5:7258)
; 0x17274
; return carry if there is a card that
-; can evolve a Pokémon in hand or deck
+; can evolve a Pokémon in hand or deck.
; input:
-; a = deck index of card to check
+; a = deck index of card to check;
+; output:
+; a = deck index of evolution in hand, if found;
+; carry set if there's a card in hand that can evolve.
CheckCardEvolutionInHandOrDeck: ; 17274 (5:7274)
ld b, a
ld a, DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
push af
ld [hl], b
- ld e, $00
+ ld e, 0
.loop
ld a, DUELVARS_CARD_LOCATIONS
@@ -4493,19 +6557,19 @@ Func_172af ; 172af (5:72af)
CheckIfCanDamageDefendingPokemon: ; 17383 (5:7383)
ldh [hTempPlayAreaLocation_ff9d], a
xor a ; first move
- ld [wSelectedMoveIndex], a
- call CheckIfCardCanUseSelectedMove
- jr c, .second_move
+ ld [wSelectedAttack], a
+ call CheckIfSelectedMoveIsUnusable
+ jr c, .second_attack
xor a
call EstimateDamage_VersusDefendingCard
ld a, [wDamage]
or a
jr nz, .set_carry
-.second_move
+.second_attack
ld a, $01 ; second move
- ld [wSelectedMoveIndex], a
- call CheckIfCardCanUseSelectedMove
+ ld [wSelectedAttack], a
+ call CheckIfSelectedMoveIsUnusable
jr c, .no_carry
ld a, $01
call EstimateDamage_VersusDefendingCard
@@ -4536,11 +6600,11 @@ CheckIfDefendingPokemonCanKnockOut: ; 173b1 (5:73b1)
ld [wce00], a
ld [wce01], a
call CheckIfDefendingPokemonCanKnockOutWithMove
- jr nc, .second_move
+ jr nc, .second_attack
ld a, [wDamage]
ld [wce00], a
-.second_move
+.second_attack
ld a, $01 ; second move
call CheckIfDefendingPokemonCanKnockOutWithMove
jr nc, .return_if_neither_kos
@@ -4571,13 +6635,13 @@ CheckIfDefendingPokemonCanKnockOut: ; 173b1 (5:73b1)
; a = move index
; [hTempPlayAreaLocation_ff9d] = location of card to check
CheckIfDefendingPokemonCanKnockOutWithMove: ; 173e4 (5:73e4)
- ld [wSelectedMoveIndex], a
+ ld [wSelectedAttack], a
ldh a, [hTempPlayAreaLocation_ff9d]
push af
xor a
ldh [hTempPlayAreaLocation_ff9d], a
call SwapTurn
- call CheckIfCardCanUseSelectedMove
+ call CheckIfSelectedMoveIsUnusable
call SwapTurn
pop bc
ld a, b
@@ -4585,7 +6649,7 @@ CheckIfDefendingPokemonCanKnockOutWithMove: ; 173e4 (5:73e4)
jr c, .done
; player's active Pokémon can use move
- ld a, [wSelectedMoveIndex]
+ ld a, [wSelectedAttack]
call EstimateDamage_FromDefendingPokemon
ldh a, [hTempPlayAreaLocation_ff9d]
add DUELVARS_ARENA_CARD_HP
@@ -4644,8 +6708,53 @@ CheckIfNotABossDeckID: ; 17426 (5:7426)
ret
; 0x1743b
-Func_1743b ; 1743b (5:743b)
- INCROM $1743b, $17474
+; probability to return carry:
+; - 50% if deck AI is playing is on the list;
+; - 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)
+; boss decks always use Trainer cards.
+ push hl
+ push de
+ call CheckIfNotABossDeckID
+ jr c, .check_deck
+ pop de
+ pop hl
+ ret
+
+.check_deck
+ ld a, [wOpponentDeckID]
+ cp MUSCLES_FOR_BRAINS_DECK_ID
+ jr z, .carry_50_percent
+ cp BLISTERING_POKEMON_DECK_ID
+ jr z, .carry_50_percent
+ cp WATERFRONT_POKEMON_DECK_ID
+ jr z, .carry_50_percent
+ cp BOOM_BOOM_SELFDESTRUCT_DECK_ID
+ jr z, .carry_50_percent
+ cp KALEIDOSCOPE_DECK_ID
+ jr z, .carry_50_percent
+ cp RESHUFFLE_DECK_ID
+ jr z, .carry_50_percent
+
+; carry 25 percent
+ ld a, 4
+ call Random
+ cp 1
+ pop de
+ pop hl
+ ret
+
+.carry_50_percent
+ ld a, 4
+ call Random
+ cp 2
+ pop de
+ pop hl
+ ret
+; 0x17474
; checks if any bench Pokémon has same ID
; as input, and sets carry if it has more than
@@ -4658,7 +6767,7 @@ CheckForBenchIDAtHalfHPAndCanUseSecondMove: ; 17474 (5:7474)
ld [wcdf9], a
ldh a, [hTempPlayAreaLocation_ff9d]
ld d, a
- ld a, [wSelectedMoveIndex]
+ ld a, [wSelectedAttack]
ld e, a
push de
ld a, DUELVARS_ARENA_CARD
@@ -4696,9 +6805,9 @@ CheckForBenchIDAtHalfHPAndCanUseSecondMove: ; 17474 (5:7474)
ld a, c
ldh [hTempPlayAreaLocation_ff9d], a
ld a, $01 ; second move
- ld [wSelectedMoveIndex], a
+ ld [wSelectedAttack], a
push bc
- call CheckIfCardCanUseSelectedMove
+ call CheckIfSelectedMoveIsUnusable
pop bc
jr c, .loop
inc b
@@ -4706,7 +6815,7 @@ CheckForBenchIDAtHalfHPAndCanUseSecondMove: ; 17474 (5:7474)
pop hl
pop de
ld a, e
- ld [wSelectedMoveIndex], a
+ ld [wSelectedAttack], a
ld a, d
ldh [hTempPlayAreaLocation_ff9d], a
ld a, b
@@ -4720,7 +6829,7 @@ CheckForBenchIDAtHalfHPAndCanUseSecondMove: ; 17474 (5:7474)
; in bench that have same ID as register a
; input:
; a = card ID to look for
-Func_174cd: ; 174cd (5:74cd)
+RaiseAIScoreToAllMatchingIDsInBench: ; 174cd (5:74cd)
ld d, a
ld a, DUELVARS_BENCH
call GetTurnDuelistVariable
@@ -4756,7 +6865,7 @@ Func_174cd: ; 174cd (5:74cd)
Func_174f2: ; 174f2 (5:74f2)
ld a, MAX_PLAY_AREA_POKEMON
ld hl, wcdfa
- call ZeroData
+ call ClearMemory_Bank5
ld a, DUELVARS_BENCH
call GetTurnDuelistVariable
ld e, 0
@@ -4765,7 +6874,7 @@ Func_174f2: ; 174f2 (5:74f2)
push hl
ld a, MAX_PLAY_AREA_POKEMON
ld hl, wcdea
- call ZeroData
+ call ClearMemory_Bank5
pop hl
inc e
ld a, [hli]
@@ -4879,7 +6988,7 @@ Func_17583: ; 17583 (5:7583)
push hl
push de
call GetCardDamage
- call CalculateTensDigit
+ call CalculateByteTensDigit
ld b, a
push bc
call CountNumberOfEnergyCardsAttached
@@ -4922,17 +7031,17 @@ Func_175a8: ; 175a8 (5:75a8)
ret
; 0x175bd
-; handle Legendary Articuno deck
-; card IDs in bench
-Func_175bd: ; 175bd (5:75bd)
+; handle how AI scores giving out Energy Cards
+; when using Legendary Articuno deck
+HandleLegendaryArticunoEnergyScoring: ; 175bd (5:75bd)
ld a, [wOpponentDeckID]
cp LEGENDARY_ARTICUNO_DECK_ID
jr z, .articuno_deck
ret
.articuno_deck
- call Func_14c91
+ call ScoreLegendaryArticunoCards
ret
; 0x175c9
Func_175c9 ; 175c9 (5:75c9)
- INCROM $175c9, $18000 \ No newline at end of file
+ INCROM $175c9, $18000
diff --git a/src/engine/bank06.asm b/src/engine/bank06.asm
index e3ee426..19f5e1f 100644
--- a/src/engine/bank06.asm
+++ b/src/engine/bank06.asm
@@ -523,7 +523,7 @@ OpenInPlayAreaScreen_TransitionTable2:
OpenInPlayAreaScreen_HandleInput: ; 183bb (6:43bb)
xor a
- ld [wcfe3], a
+ ld [wPlaysSfx], a
ld hl, wInPlayAreaInputTablePointer
ld e, [hl]
inc hl
@@ -655,7 +655,7 @@ OpenInPlayAreaScreen_HandleInput: ; 183bb (6:43bb)
ld [wInPlayAreaCurPosition], a
.next
ld a, $01
- ld [wcfe3], a
+ ld [wPlaysSfx], a
xor a
ld [wCheckMenuCursorBlinkCounter], a
.check_button
@@ -681,7 +681,7 @@ OpenInPlayAreaScreen_HandleInput: ; 183bb (6:43bb)
ret
.return
- ld a, [wcfe3]
+ ld a, [wPlaysSfx]
or a
jr z, .skip_sfx
call PlaySFX
@@ -947,37 +947,40 @@ GlossaryData2:
Func_18661: ; 18661 (6:4661)
xor a
- ld [wcfe3], a
+ ld [wPlaysSfx], a
ld a, [wCheckMenuCursorXPosition]
ld d, a
ld a, [wCheckMenuCursorYPosition]
ld e, a
ldh a, [hDPadHeld]
or a
- jr z, .asm_46a2
+ jr z, .check_button
+; check input from dpad
bit D_LEFT_F, a
- jr nz, .asm_467a
+ jr nz, .left_or_right
bit D_RIGHT_F, a
- jr z, .asm_4680
-.asm_467a
+ jr z, .check_up_and_down
+.left_or_right
+; swap the lsb of x position value.
ld a, d
- xor $01
+ xor $1
ld d, a
- jr .asm_468c
-.asm_4680
+ jr .cursor_moved
+
+.check_up_and_down
bit D_UP_F, a
- jr nz, .asm_4688
+ jr nz, .up_or_down
bit D_DOWN_F, a
- jr z, .asm_46a2
-.asm_4688
+ jr z, .check_button
+.up_or_down
ld a, e
- xor $01
+ xor $1
ld e, a
-.asm_468c
- ld a, $01
- ld [wcfe3], a
+.cursor_moved
+ ld a, $1
+ ld [wPlaysSfx], a
push de
- call .asm_46d4
+ call .draw_blank_cursor
pop de
ld a, d
ld [wCheckMenuCursorXPosition], a
@@ -985,59 +988,66 @@ Func_18661: ; 18661 (6:4661)
ld [wCheckMenuCursorYPosition], a
xor a
ld [wCheckMenuCursorBlinkCounter], a
-.asm_46a2
+.check_button
ldh a, [hKeysPressed]
and A_BUTTON | B_BUTTON
- jr z, .asm_46bd
+ jr z, .check_cursor_moved
and A_BUTTON
- jr nz, .asm_46b3
- ld a, $ff
+ jr nz, .a_button
+
+; b button
+ ld a, -1
call Func_190fb
scf
ret
-.asm_46b3
- call .asm_46f3
- ld a, $01
+
+; a button
+.a_button
+ call .draw_cursor
+ ld a, 1
call Func_190fb
scf
ret
-.asm_46bd
- ld a, [wcfe3]
+
+.check_cursor_moved
+ ld a, [wPlaysSfx]
or a
- jr z, .asm_46c6
+ jr z, .check_cursor_blink
call PlaySFX
-.asm_46c6
+.check_cursor_blink
ld hl, wCheckMenuCursorBlinkCounter
ld a, [hl]
inc [hl]
- and $0f
+ and %00001111
ret nz
- ld a, $0f
+ ld a, SYM_CURSOR_R
bit D_RIGHT_F, [hl]
- jr z, .asm_46d6
-.asm_46d4 ; 186d4 (6:46d4)
- ld a, $00
-.asm_46d6
+ jr z, .draw_tile
+.draw_blank_cursor ; 186d4 (6:46d4)
+ ld a, SYM_SPACE
+.draw_tile
ld e, a
- ld a, $0a
+ ld a, 10
ld l, a
ld a, [wCheckMenuCursorXPosition]
ld h, a
call HtimesL
ld a, l
- add $01
+ add 1
ld b, a
ld a, [wCheckMenuCursorYPosition]
sla a
- add $0e
+ add 14
ld c, a
ld a, e
+ ; b = 11, c = y_pos * 2 + 14
+ ; h = x_pos * 10, l = 10
call WriteByteToBGMap0
or a
ret
-.asm_46f3: ; 186f3 (6:46f3)
- ld a, $0f
- jr .asm_46d6
+.draw_cursor ; 186f3 (6:46f3)
+ ld a, SYM_CURSOR_R
+ jr .draw_tile
; (6:46f7)
INCLUDE "data/effect_commands.asm"
@@ -1049,7 +1059,7 @@ Func_18f9c: ; 18f9c (6:4f9c)
ld l, a
ld h, 0
add hl, hl
- ld de, Data_006_51a4
+ ld de, PointerTable_MoveAnimation
.asm_4fa8
add hl, de
ld e, [hl]
@@ -1357,154 +1367,9 @@ Func_19168: ; 19168 (6:5168)
ret
-Data_006_51a4:
- dw $0000
- dw $52c6
- dw $52cf
- dw $52c6
- dw $52c6
- dw $52c6
- dw $52d8
- dw $52d8
- dw $52e3
- dw $52d8
- dw $52f0
- dw $52f0
- dw $52f0
- dw $52f0
- dw $52fd
- dw $5308
- dw $5313
- dw $531e
- dw $5329
- dw $5334
- dw $533f
- dw $534a
- dw $5357
- dw $5362
- dw $5362
- dw $536d
- dw $536d
- dw $536d
- dw $5378
- dw $5383
- dw $538e
- dw $5383
- dw $5399
- dw $53a4
- dw $53af
- dw $53ba
- dw $53c5
- dw $53d0
- dw $53d5
- dw $53e0
- dw $53eb
- dw $53f6
- dw $53f6
- dw $53f6
- dw $5401
- dw $540c
- dw $5417
- dw $5422
- dw $542d
- dw $542d
- dw $5438
- dw $5438
- dw $5438
- dw $5438
- dw $5438
- dw $5443
- dw $5443
- dw $544e
- dw $5443
- dw $5443
- dw $5443
- dw $5453
- dw $5453
- dw $5460
- dw $5453
- dw $5467
- dw $5467
- dw $5472
- dw $5472
- dw $547d
- dw $5488
- dw $548f
- dw $549c
- dw $549c
- dw $54a9
- dw $54a9
- dw $54ae
- dw $54ae
- dw $54b3
- dw $54be
- dw $54c3
- dw $54c8
- dw $54d3
- dw $54e0
- dw $54eb
- dw $54f2
- dw $54f9
- dw $5504
- dw $5513
- dw $5516
- dw $5521
- dw $552e
- dw $5533
- dw $553a
- dw $5543
- dw $554a
- dw $5555
- dw $555e
- dw $556d
- dw $5574
- dw $557b
- dw $557e
- dw $5583
- dw $5583
- dw $5583
- dw $558c
- dw $5597
- dw $559c
- dw $55a1
- dw $55a4
- dw $55a9
- dw $55b4
- dw $55b4
- dw $55bf
- dw $55c4
- dw $55c9
- dw $55ce
- dw $55d5
- dw $55e0
- dw $55e5
- dw $55e6
- dw $55ed
- dw $55f2
- dw $55fb
- dw $55fe
- dw $5601
- dw $5604
- dw $5607
- dw $560a
- dw $560f
- dw $5612
- dw $561d
- dw $5628
- dw $562d
- dw $5632
- dw $5637
- dw $5644
- dw $564f
- dw $5654
- dw $5659
- dw $565e
- dw $5665
- dw $5668
- dw $5673
- dw $5673
-
- INCROM $192c6, $1991f
+INCLUDE "data/move_animations.asm"
+
+ INCROM $19674, $1991f
Func_1991f: ; 1991f (6:591f)
add a
@@ -2116,7 +1981,7 @@ endr
; if pressed, set the carry bit on.
NamingScreen_CheckButtonState:
xor a
- ld [wcfe3], a
+ ld [wPlaysSfx], a
ldh a, [hDPadHeld]
or a
jp z, .no_press
@@ -2265,7 +2130,7 @@ NamingScreen_CheckButtonState:
cp d
jp z, NamingScreen_CheckButtonState
ld a, $01
- ld [wcfe3], a
+ ld [wPlaysSfx], a
.no_press
ldh a, [hKeysPressed]
and A_BUTTON | B_BUTTON
@@ -2281,7 +2146,7 @@ NamingScreen_CheckButtonState:
scf
ret
.asm_69ef
- ld a, [wcfe3]
+ ld a, [wPlaysSfx]
or a
jr z, .asm_69f8
call PlaySFX
@@ -2989,7 +2854,7 @@ Func_1aec3: ; 1aec3 (6:6ec3)
Func_1aefb: ; 1aefb (6:6efb)
xor a
- ld [wcfe3], a
+ ld [wPlaysSfx], a
ldh a, [hDPadHeld]
or a
jp z, .asm_6f73
@@ -3063,7 +2928,7 @@ Func_1aefb: ; 1aefb (6:6efb)
cp d
jp z, Func_1aefb
ld a, $01
- ld [wcfe3], a
+ ld [wPlaysSfx], a
.asm_6f73
ldh a, [hKeysPressed]
and $03
@@ -3079,7 +2944,7 @@ Func_1aefb: ; 1aefb (6:6efb)
scf
ret
.asm_6f89
- ld a, [wcfe3]
+ ld a, [wPlaysSfx]
or a
jr z, .asm_6f92
call PlaySFX
@@ -3436,4 +3301,4 @@ Func_1bae4: ; 1bae4 (6:7ae4)
rept $508
db $ff
-endr \ No newline at end of file
+endr
diff --git a/src/engine/bank07.asm b/src/engine/bank07.asm
index 54e5c74..48d2565 100644
--- a/src/engine/bank07.asm
+++ b/src/engine/bank07.asm
@@ -554,7 +554,303 @@ Func_1c83d: ; 1c83d (7:483d)
ret
; 0x1c858
- INCROM $1c858, $1cb18
+ INCROM $1c858, $1c8ef
+
+Func_1c8ef: ; 1c8ef (7:48ef)
+ ld a, [wDoFrameFunction + 0]
+ cp LOW(Func_3ba2)
+ jr nz, .error
+ ld a, [wDoFrameFunction + 1]
+ cp HIGH(Func_3ba2)
+ jr z, .okay
+.error
+ debug_ret
+ ret
+
+.okay
+ ld a, [wTempAnimation]
+ ld [wd4bf], a
+ cp $61
+ jp nc, $4b5e ; asm_007_4b5e
+ push hl
+ push bc
+ push de
+ call Func_1cab3
+; hl: pointer
+ ld a, [wd421]
+ or a
+ jr z, .check_to_play_sfx
+
+ push hl
+ ld bc, $0003
+ add hl, bc
+ ld a, [hl]
+ and %10000000
+ pop hl
+
+ jr z, .return
+.check_to_play_sfx
+ push hl
+ ld bc, $0004
+ add hl, bc
+ ld a, [hl]
+ pop hl
+
+ or a
+ jr z, .calc_addr
+ call PlaySFX
+.calc_addr
+ push hl
+ ld bc, $0005
+ add hl, bc
+ ld a, [hl]
+ rlca
+ add $48
+ ld l, a ; LO
+ ld a, HIGH(.address) ; $49
+ adc 0
+ ld h, a ; HI
+; hl: pointer
+ ld a, [hli]
+ ld b, [hl]
+ ld c, a
+ pop hl
+
+ call CallBC
+.return
+ pop de
+ pop bc
+ pop hl
+ ret
+
+.address
+ dw Func_1c94a
+
+Func_1c94a:
+ ld e, l
+ ld d, h
+ ld c, 3
+.loop
+ ld a, [de]
+ or a
+ jr z, .return_with_carry
+ inc de
+ dec c
+ jr nz, .loop
+ ld a, [hli]
+ farcall CreateSpriteAndAnimBufferEntry
+ ld a, [wWhichSprite]
+ ld [wAnimationQueue], a ; push an animation to the queue
+ xor a
+ ld [wd4ca], a
+ ld [wd4cb], a
+ ld a, [hli]
+ farcall $20, $4418
+ ld a, [hli]
+
+ push af
+ ld a, [hli]
+ ld [wd42b], a
+ call Func_1c980
+ pop af
+
+ farcall Func_12ab5
+ or a
+ jr .return
+
+.return_with_carry
+ scf
+.return
+ ret
+
+Func_1c980: ; 1c980 (7:4980)
+ push hl
+ push bc
+ ld a, [wAnimationQueue]
+ ld c, SPRITE_ANIM_FIELD_01
+ call GetSpriteAnimBufferProperty_SpriteInA
+ call Func_1c9a2
+
+ push af
+ and %01100000
+ or [hl]
+ ld [hli], a
+ ld a, b
+ ld [hli], a
+ ld [hl], c
+ pop af
+
+ ld bc, $000c
+ add hl, bc
+ ld c, a
+ and %00000011
+ or [hl]
+ ld [hl], a
+ pop bc
+ pop hl
+ ret
+
+Func_1c9a2: ; 1c9a2 (7:49a2)
+ push hl
+ ld c, 0
+ ld a, [wd42b]
+ and %00000100
+ jr nz, .calc_addr
+
+ ld a, [wd4ae]
+ add a
+ ld c, a
+ add a
+ add c
+ add a
+ ld c, a
+ ld a, [wd4af]
+ cp PLAYER_TURN
+ jr z, .player_turn
+
+ ld a, $06
+ add c
+ ld c, a
+.player_turn
+ ld a, [wd4b0]
+ add c ; a = [wd4b0] + c
+ ld c, a
+ ld b, 0
+ ld hl, Data_1c9e0
+ add hl, bc
+ ld c, [hl]
+.calc_addr
+ ld a, c
+ add a ; a = c * 2
+ add c ; a = c * 3
+ ld c, a
+ ld b, 0
+ ld hl, Data_1ca04
+ add hl, bc
+ ld b, [hl]
+ inc hl
+ ld c, [hl]
+ inc hl
+ ld a, [wd42b]
+ and [hl]
+ pop hl
+ ret
+
+Data_1c9e0:
+ db $01
+ db $01
+ db $01
+ db $01
+ db $01
+ db $01
+ db $02
+ db $02
+ db $02
+ db $02
+ db $02
+ db $02
+ db $03
+ db $04
+ db $05
+ db $06
+ db $07
+ db $08
+ db $03
+ db $04
+ db $05
+ db $06
+ db $07
+ db $08
+ db $09
+ db $0a
+ db $0b
+ db $0c
+ db $0d
+ db $0e
+ db $09
+ db $0a
+ db $0b
+ db $0c
+ db $0d
+ db $0e
+
+macro_1ca04: MACRO
+ dw \1
+ db \2
+ENDM
+Data_1ca04:
+; value(2), flag(1)
+ macro_1ca04 $5858, $08
+ macro_1ca04 $5028, $00
+ macro_1ca04 $3088, $63
+ macro_1ca04 $4858, $00
+ macro_1ca04 $6018, $00
+ macro_1ca04 $6038, $00
+ macro_1ca04 $6058, $00
+ macro_1ca04 $6078, $00
+ macro_1ca04 $6098, $00
+ macro_1ca04 $5058, $00
+ macro_1ca04 $2898, $00
+ macro_1ca04 $2878, $00
+ macro_1ca04 $2858, $00
+ macro_1ca04 $2838, $00
+ macro_1ca04 $2818, $00
+
+Func_1ca31:
+ push hl
+ push bc
+ ld a, [wd4ac]
+ ld b, a
+ ld hl, wd4ad
+ ld a, [hl]
+ ld c, a
+ add %00001000
+ and %01111111
+ cp b
+ jp z, .asm_007_4a6b
+ ld [hl], a
+ ld b, 0
+ ld hl, $d42c
+ add hl, bc
+ ld a, [wTempAnimation]
+ ld [hli], a
+ ld a, [wd4ae]
+ ld [hli], a
+ ld a, [wd4af]
+ ld [hli], a
+ ld a, [wd4b0]
+ ld [hli], a
+ ld a, [$d4b1]
+ ld [hli], a
+ ld a, [$d4b2]
+ ld [hli], a
+ ld a, [$d4b3]
+ ld [hli], a
+ ld a, [wd4be]
+ ld [hl], a
+.asm_007_4a6b
+ pop bc
+ pop hl
+ ret
+
+ INCROM $1ca6e, $1cab3
+
+Func_1cab3: ; 1cab3 (7:4ab3)
+ push bc
+ ld a, [wTempAnimation]
+ ld l, a
+ ld h, 0
+ add hl, hl ; hl = anim * 2
+ ld b, h
+ ld c, l
+ add hl, hl ; hl = anim * 4
+ add hl, bc ; hl = anim * 6
+ ld bc, $4e32
+ add hl, bc
+ pop bc
+ ret
+
+ INCROM $1cac5, $1cb18
Func_1cb18: ; 1cb18 (7:4b18)
push hl
diff --git a/src/engine/bank08.asm b/src/engine/bank08.asm
index 15ff62a..5a0f535 100644
--- a/src/engine/bank08.asm
+++ b/src/engine/bank08.asm
@@ -1,123 +1,6550 @@
- INCROM $20000, $200e5
+; unknown byte / card ID / function pointer 1 / function pointer 2
+unknown_data_20000: MACRO
+ db \1, \2
+ dw \3
+ dw \4
+ENDM
-; 0 - e4 is a big set of data, seems to be one entry for each card
+Data_20000: ; 20000 (8:4000)
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_07, POTION, AIDecide_Potion1, AIPlay_Potion
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_10, POTION, AIDecide_Potion2, AIPlay_Potion
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_08, SUPER_POTION, AIDecide_SuperPotion1, AIPlay_SuperPotion
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_11, SUPER_POTION, AIDecide_SuperPotion2, AIPlay_SuperPotion
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_13, DEFENDER, AIDecide_Defender1, AIPlay_Defender
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_14, DEFENDER, AIDecide_Defender2, AIPlay_Defender
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_13, PLUSPOWER, AIDecide_Pluspower1, AIPlay_Pluspower
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_14, PLUSPOWER, AIDecide_Pluspower2, AIPlay_Pluspower
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_09, SWITCH, AIDecide_Switch, AIPlay_Switch
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_07, GUST_OF_WIND, AIDecide_GustOfWind, AIPlay_GustOfWind
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_10, GUST_OF_WIND, AIDecide_GustOfWind, AIPlay_GustOfWind
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_04, BILL, AIDecide_Bill, AIPlay_Bill
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_05, ENERGY_REMOVAL, AIDecide_EnergyRemoval, AIPlay_EnergyRemoval
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_05, SUPER_ENERGY_REMOVAL, AIDecide_SuperEnergyRemoval, AIPlay_SuperEnergyRemoval
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_07, POKEMON_BREEDER, AIDecide_PokemonBreeder, AIPlay_PokemonBreeder
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_15, PROFESSOR_OAK, AIDecide_ProfessorOak, AIPlay_ProfessorOak
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_10, ENERGY_RETRIEVAL, AIDecide_EnergyRetrieval, AIPlay_EnergyRetrieval
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_11, SUPER_ENERGY_RETRIEVAL, AIDecide_SuperEnergyRetrieval, AIPlay_SuperEnergyRetrieval
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_06, POKEMON_CENTER, AIDecide_PokemonCenter, AIPlay_PokemonCenter
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_07, IMPOSTER_PROFESSOR_OAK, AIDecide_ImposterProfessorOak, AIPlay_ImposterProfessorOak
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_12, ENERGY_SEARCH, AIDecide_EnergySearch, AIPlay_EnergySearch
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_03, POKEDEX, AIDecide_Pokedex, AIPlay_Pokedex
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_07, FULL_HEAL, AIDecide_FullHeal, AIPlay_FullHeal
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_10, MR_FUJI, AIDecide_MrFuji, AIPlay_MrFuji
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_10, SCOOP_UP, AIDecide_ScoopUp, AIPlay_ScoopUp
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_02, MAINTENANCE, AIDecide_Maintenance, AIPlay_Maintenance
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_03, RECYCLE, AIDecide_Recycle, AIPlay_Recycle
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_13, LASS, AIDecide_Lass, AIPlay_Lass
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_04, ITEM_FINDER, AIDecide_ItemFinder, AIPlay_ItemFinder
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_01, IMAKUNI_CARD, AIDecide_Imakuni, AIPlay_Imakuni
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_01, GAMBLER, AIDecide_Gambler, AIPlay_Gambler
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_05, REVIVE, AIDecide_Revive, AIPlay_Revive
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_13, POKEMON_FLUTE, AIDecide_PokemonFlute, AIPlay_PokemonFlute
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_05, CLEFAIRY_DOLL, AIDecide_ClefairyDollOrMysteriousFossil, AIPlay_ClefairyDollOrMysteriousFossil
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_05, MYSTERIOUS_FOSSIL, AIDecide_ClefairyDollOrMysteriousFossil, AIPlay_ClefairyDollOrMysteriousFossil
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_02, POKE_BALL, AIDecide_Pokeball, AIPlay_Pokeball
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_02, COMPUTER_SEARCH, AIDecide_ComputerSearch, AIPlay_ComputerSearch
+ unknown_data_20000 AI_TRAINER_CARD_PHASE_02, POKEMON_TRADER, AIDecide_PokemonTrader, AIPlay_PokemonTrader
+ db $ff
-Func_200e5: ; 200e5 (8:40e5)
+_AIProcessHandTrainerCards: ; 200e5 (8:40e5)
ld [wce18], a
+; create hand list in wDuelTempList and wTempHandCardList.
call CreateHandCardList
ld hl, wDuelTempList
ld de, wTempHandCardList
call CopyBuffer
ld hl, wTempHandCardList
+
+.loop_hand
ld a, [hli]
- ld [wce16], a
+ ld [wAITrainerCardToPlay], a
cp $ff
ret z
+
push hl
ld a, [wce18]
ld d, a
- ld hl, $4000
-.asm_4106
+ ld hl, Data_20000
+.loop_data
xor a
- ld [wce21], a
+ ld [wCurrentAIFlags], a
ld a, [hli]
cp $ff
- jp z, $41b1
+ jp z, .pop_hl
+
+; compare input to first byte in data and continue if equal.
cp d
- jp nz, .incHL5
+ jp nz, .inc_hl_by_5
+
ld a, [hli]
ld [wce17], a
- ld a, [wce16]
+ ld a, [wAITrainerCardToPlay]
call LoadCardDataToBuffer1_FromDeckIndex
- cp $d2
- jr nz, .asm_2012b
+
+ cp SWITCH
+ jr nz, .skip_switch_check
+
ld b, a
- ld a, [wce20]
- and $2
- jr nz, .incHL4
+ ld a, [wPreviousAIFlags]
+ and AI_FLAG_USED_SWITCH
+ jr nz, .inc_hl_by_4
ld a, b
-.asm_2012b
+.skip_switch_check
+; compare hand card to second byte in data and continue if equal.
ld b, a
ld a, [wce17]
cp b
- jr nz, .incHL4
+ jr nz, .inc_hl_by_4
+
+; found Trainer card
push hl
push de
- ld a, [wce16]
+ ld a, [wAITrainerCardToPlay]
ldh [hTempCardIndex_ff9f], a
+
+; if Headache effects prevent playing card
+; move on to the next item in list.
bank1call CheckCantUseTrainerDueToHeadache
- jp c, $41a8
+ jp c, .next_in_data
+
call LoadNonPokemonCardEffectCommands
ld a, EFFECTCMDTYPE_INITIAL_EFFECT_1
call TryExecuteEffectCommandFunction
- jp c, $41a8
- farcall $5, $743b
- jr c, .asm_201a8
+ jp c, .next_in_data
+
+; AI can randomly choose not to play card.
+ farcall ChooseRandomlyNotToPlayTrainerCard
+ jr c, .next_in_data
+
+; call routine to decide whether to play Trainer card
pop de
pop hl
push hl
call CallIndirect
pop hl
- jr nc, .incHL4
+ jr nc, .inc_hl_by_4
+
+; routine returned carry, which means
+; this card should be played.
inc hl
inc hl
- ld [wce19], a
+ ld [wAITrainerCardParameter], a
+
+; show Play Trainer Card screen
push de
push hl
- ld a, [wce16]
+ ld a, [wAITrainerCardToPlay]
ldh [hTempCardIndex_ff9f], a
- ld a, $6
- bank1call $67be
+ ld a, OPPACTION_PLAY_TRAINER
+ bank1call AIMakeDecision
pop hl
pop de
- jr c, .incHL2
+ jr c, .inc_hl_by_2
+
+; execute the effects of the Trainer card
push hl
call CallIndirect
pop hl
+
inc hl
inc hl
- ld a, [wce20]
+ ld a, [wPreviousAIFlags]
ld b, a
- ld a, [wce21]
+ ld a, [wCurrentAIFlags]
or b
- ld [wce20], a
+ ld [wPreviousAIFlags], a
pop hl
- and $8
- jp z, $40f7
+ and AI_FLAG_MODIFIED_HAND
+ jp z, .loop_hand
+
+; the hand was modified during the Trainer effect
+; so it needs to be re-listed again and
+; looped from the top.
call CreateHandCardList
ld hl, wDuelTempList
ld de, wTempHandCardList
- call $697b
+ call CopyBuffer
ld hl, wTempHandCardList
- ld a, [wce20]
- and $f7
- ld [wce20], a
- jp $40f7
+; clear the AI_FLAG_MODIFIED_HAND flag
+ ld a, [wPreviousAIFlags]
+ and ~AI_FLAG_MODIFIED_HAND
+ ld [wPreviousAIFlags], a
+ jp .loop_hand
-.incHL5
+.inc_hl_by_5
inc hl
-
-.incHL4
+.inc_hl_by_4
inc hl
inc hl
+.inc_hl_by_2
+ inc hl
+ inc hl
+ jp .loop_data
-.incHL2
+.next_in_data
+ pop de
+ pop hl
+ inc hl
+ inc hl
+ inc hl
inc hl
+ jp .loop_data
+
+.pop_hl
+ pop hl
+ jp .loop_hand
+; 0x201b5
+
+; makes AI use Potion card.
+AIPlay_Potion: ; 201b5 (8:41b5)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld e, a
+ call GetCardDamage
+ cp 20
+ jr c, .play_card
+ ld a, 20
+.play_card
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x201d1
+
+; if AI doesn't decide to retreat this card,
+; check if defending Pokémon can KO active card
+; next turn after using Potion.
+; if it cannot, return carry.
+; also take into account whether move is high recoil.
+AIDecide_Potion1: ; 201d1 (8:41d1)
+ farcall AIDecideWhetherToRetreat
+ jr c, .no_carry
+ call Func_22bad
+ jr c, .no_carry
+ xor a ; active card
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckIfDefendingPokemonCanKnockOut
+ jr nc, .no_carry
+ ld d, a
+
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetTurnDuelistVariable
+ ld h, a
+ ld e, PLAY_AREA_ARENA
+ call GetCardDamage
+ cp 20 + 1 ; if damage <= 20
+ jr c, .calculate_hp
+ ld a, 20 ; amount of Potion HP healing
+
+; if damage done by defending Pokémon next turn will still
+; KO this card after healing, return no carry.
+.calculate_hp
+ ld l, a
+ ld a, h
+ add l
+ sub d
+ jr c, .no_carry
+ jr z, .no_carry
+
+; return carry.
+ xor a
+ scf
+ ret
+.no_carry
+ or a
+ ret
+; 0x20204
+
+; finds a card in Play Area to use Potion on.
+; output:
+; a = card to use Potion on;
+; carry set if Potion should be used.
+AIDecide_Potion2: ; 20204 (8:4204)
+ xor a
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckIfDefendingPokemonCanKnockOut
+ jr nc, .start_from_active
+; can KO
+ ld d, a
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetTurnDuelistVariable
+ ld h, a
+ ld e, PLAY_AREA_ARENA
+ call GetCardDamage
+ cp 20 + 1 ; if damage <= 20
+ jr c, .calculate_hp
+ ld a, 20
+; return if using healing prevents KO.
+.calculate_hp
+ ld l, a
+ ld a, h
+ add l
+ sub d
+ jr c, .count_prizes
+ jr z, .count_prizes
+ or a
+ ret
+
+; using Potion on active card does not prevent a KO.
+; if player is at last prize, start loop with active card.
+; otherwise start loop at first bench Pokémon.
+.count_prizes
+ call SwapTurn
+ call CountPrizes
+ call SwapTurn
+ dec a
+ jr z, .start_from_active
+ ld e, PLAY_AREA_BENCH_1
+ jr .loop
+
+; find Play Area Pokémon with more than 10 damage.
+; skip Pokémon if it has a BOOST_IF_TAKEN_DAMAGE attack.
+.start_from_active
+ ld e, PLAY_AREA_ARENA
+.loop
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ call GetTurnDuelistVariable
+ cp $ff
+ ret z
+ call .check_boost_if_taken_damage
+ jr c, .has_boost_damage
+ call GetCardDamage
+ cp 20 ; if damage >= 20
+ jr nc, .found
+.has_boost_damage
+ inc e
+ jr .loop
+
+; a card was found, now to check if it's active or benched.
+.found
+ ld a, e
+ or a
+ jr z, .active_card
+
+; bench card
+ push de
+ call SwapTurn
+ call CountPrizes
+ call SwapTurn
+ dec a
+ or a
+ jr z, .check_random
+ ld a, 10
+ call Random
+ cp 3
+; 7/10 chance of returning carry.
+.check_random
+ pop de
+ jr c, .no_carry
+ ld a, e
+ scf
+ ret
+
+; return carry for active card if not Hgh Recoil.
+.active_card
+ push de
+ call Func_22bad
+ pop de
+ jr c, .no_carry
+ ld a, e
+ scf
+ ret
+.no_carry
+ or a
+ ret
+; 0x2027e
+
+; return carry if either of the attacks are usable
+; and have the BOOST_IF_TAKEN_DAMAGE effect.
+.check_boost_if_taken_damage ; 2027e (8:427e)
+ push de
+ xor a ; FIRST_ATTACK_OR_PKMN_POWER
+ ld [wSelectedAttack], a
+ farcall CheckIfSelectedMoveIsUnusable
+ jr c, .second_attack
+ ld a, MOVE_FLAG3_ADDRESS | BOOST_IF_TAKEN_DAMAGE_F
+ call CheckLoadedMoveFlag
+ jr c, .set_carry
+.second_attack
+ ld a, SECOND_ATTACK
+ ld [wSelectedAttack], a
+ farcall CheckIfSelectedMoveIsUnusable
+ jr c, .false
+ ld a, MOVE_FLAG3_ADDRESS | BOOST_IF_TAKEN_DAMAGE_F
+ call CheckLoadedMoveFlag
+ jr c, .set_carry
+.false
+ pop de
+ or a
+ ret
+.set_carry
+ pop de
+ scf
+ ret
+; 0x202a8
+
+; makes AI use Super Potion card.
+AIPlay_SuperPotion: ; 202a8 (8:42a8)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ call AIPickEnergyCardToDiscard
+ ldh [hTemp_ffa0], a
+ ld a, [wAITrainerCardParameter]
+ ld e, a
+ call GetCardDamage
+ cp 40
+ jr c, .play_card
+ ld a, 40
+.play_card
+ ldh [hTempRetreatCostCards], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x202cc
+
+; if AI doesn't decide to retreat this card and card has
+; any energy cards attached, check if defending Pokémon can KO
+; active card next turn after using Super Potion.
+; if it cannot, return carry.
+; also take into account whether move is high recoil.
+AIDecide_SuperPotion1: ; 202cc (8:42cc)
+ farcall AIDecideWhetherToRetreat
+ jr c, .no_carry
+ call Func_22bad
+ jr c, .no_carry
+ xor a
+ ldh [hTempPlayAreaLocation_ff9d], a
+ ld e, a
+ call .check_attached_energy
+ ret nc
+ farcall CheckIfDefendingPokemonCanKnockOut
+ jr nc, .no_carry
+
+ ld d, a
+ ld d, a
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetTurnDuelistVariable
+ ld h, a
+ ld e, PLAY_AREA_ARENA
+ call GetCardDamage
+ cp 40 + 1 ; if damage < 40
+ jr c, .calculate_hp
+ ld a, 40
+.calculate_hp
+ ld l, a
+ ld a, h
+ add l
+ sub d
+ jr c, .no_carry
+ jr z, .no_carry
+
+; return carry
+ ld a, e
+ scf
+ ret
+.no_carry
+ or a
+ ret
+; 0x20305
+
+; returns carry if card has energies attached.
+.check_attached_energy ; 20305 (8:4305)
+ call GetPlayAreaCardAttachedEnergies
+ ld a, [wTotalAttachedEnergies]
+ or a
+ ret z
+ scf
+ ret
+; 0x2030f
+
+; finds a card in Play Area to use Super Potion on.
+; output:
+; a = card to use Super Potion on;
+; carry set if Super Potion should be used.
+AIDecide_SuperPotion2: ; 2030f (8:430f)
+ xor a
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckIfDefendingPokemonCanKnockOut
+ jr nc, .start_from_active
+; can KO
+ ld d, a
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetTurnDuelistVariable
+ ld h, a
+ ld e, PLAY_AREA_ARENA
+ call GetCardDamage
+ cp 40 + 1 ; if damage < 40
+ jr c, .calculate_hp
+ ld a, 40
+; return if using healing prevents KO.
+.calculate_hp
+ ld l, a
+ ld a, h
+ add l
+ sub d
+ jr c, .count_prizes
+ jr z, .count_prizes
+ or a
+ ret
+
+; using Super Potion on active card does not prevent a KO.
+; if player is at last prize, start loop with active card.
+; otherwise start loop at first bench Pokémon.
+.count_prizes
+ call SwapTurn
+ call CountPrizes
+ call SwapTurn
+ dec a
+ jr z, .start_from_active
+ ld e, PLAY_AREA_BENCH_1
+ jr .loop
+
+; find Play Area Pokémon with more than 30 damage.
+; skip Pokémon if it doesn't have any energy attached,
+; has a BOOST_IF_TAKEN_DAMAGE attack,
+; or if discarding makes any attack of its attacks unusable.
+.start_from_active
+ ld e, PLAY_AREA_ARENA
+.loop
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ call GetTurnDuelistVariable
+ cp $ff
+ ret z
+ ld d, a
+ call .check_attached_energy
+ jr nc, .next
+ call .check_boost_if_taken_damage
+ jr c, .next
+ call .check_energy_cost
+ jr c, .next
+ call GetCardDamage
+ cp 40 ; if damage >= 40
+ jr nc, .found
+.next
+ inc e
+ jr .loop
+
+; a card was found, now to check if it's active or benched.
+.found
+ ld a, e
+ or a
+ jr z, .active_card
+
+; bench card
+ push de
+ call SwapTurn
+ call CountPrizes
+ call SwapTurn
+ dec a
+ or a
+ jr z, .check_random
+ ld a, 10
+ call Random
+ cp 3
+; 7/10 chance of returning carry.
+.check_random
+ pop de
+ jr c, .no_carry
+ ld a, e
+ scf
+ ret
+
+; return carry for active card if not Hgh Recoil.
+.active_card
+ push de
+ call Func_22bad
+ pop de
+ jr c, .no_carry
+ ld a, e
+ scf
+ ret
+.no_carry
+ or a
+ ret
+; 0x20394
+
+; returns carry if card has energies attached.
+.check_attached_energy ; 20394 (8:4394)
+ call GetPlayAreaCardAttachedEnergies
+ ld a, [wTotalAttachedEnergies]
+ or a
+ ret z
+ scf
+ ret
+; 0x2039e
+
+; return carry if either of the attacks are usable
+; and have the BOOST_IF_TAKEN_DAMAGE effect.
+.check_boost_if_taken_damage ; 2039e (8:439e)
+ push de
+ xor a ; FIRST_ATTACK_OR_PKMN_POWER
+ ld [wSelectedAttack], a
+ farcall CheckIfSelectedMoveIsUnusable
+ jr c, .second_attack_1
+ ld a, MOVE_FLAG3_ADDRESS | BOOST_IF_TAKEN_DAMAGE_F
+ call CheckLoadedMoveFlag
+ jr c, .true_1
+.second_attack_1
+ ld a, SECOND_ATTACK
+ ld [wSelectedAttack], a
+ farcall CheckIfSelectedMoveIsUnusable
+ jr c, .false_1
+ ld a, MOVE_FLAG3_ADDRESS | BOOST_IF_TAKEN_DAMAGE_F
+ call CheckLoadedMoveFlag
+ jr c, .true_1
+.false_1
+ pop de
+ or a
+ ret
+.true_1
+ pop de
+ scf
+ ret
+; 0x203c8
+
+; returns carry if discarding energy card renders any attack unusable,
+; given that they have enough energy to be used before discarding.
+.check_energy_cost ; 203c8 (8:43c8)
+ push de
+ xor a ; FIRST_ATTACK_OR_PKMN_POWER
+ ld [wSelectedAttack], a
+ ld a, e
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckEnergyNeededForAttack
+ jr c, .second_attack_2
+ farcall CheckEnergyNeededForAttackAfterDiscard
+ jr c, .true_2
+
+.second_attack_2
+ pop de
+ push de
+ ld a, SECOND_ATTACK
+ ld [wSelectedAttack], a
+ ld a, e
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckEnergyNeededForAttack
+ jr c, .false_2
+ farcall CheckEnergyNeededForAttackAfterDiscard
+ jr c, .true_2
+
+.false_2
+ pop de
+ or a
+ ret
+.true_2
+ pop de
+ scf
+ ret
+; 0x203f8
+
+AIPlay_Defender: ; 203f8 (8:43f8)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ xor a
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x20406
+
+; returns carry if using Defender can prevent a KO
+; by the defending Pokémon.
+; this takes into account both attacks and whether they're useable.
+AIDecide_Defender1: ; 20406 (8:4406)
+ xor a ; PLAY_AREA_ARENA
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckIfAnyMoveKnocksOutDefendingCard
+ jr nc, .cannot_ko
+ farcall CheckIfSelectedMoveIsUnusable
+ jr nc, .no_carry
+ farcall LookForEnergyNeededForMoveInHand
+ jr c, .no_carry
+
+.cannot_ko
+; check if any of the defending Pokémon's attacks deal
+; damage exactly equal to current HP, and if so,
+; only continue if that move is useable.
+ farcall CheckIfAnyDefendingPokemonAttackDealsSameDamageAsHP
+ jr nc, .no_carry
+ call SwapTurn
+ farcall CheckIfSelectedMoveIsUnusable
+ call SwapTurn
+ jr c, .no_carry
+
+ ld a, [wSelectedAttack]
+ farcall EstimateDamage_FromDefendingPokemon
+ ld a, [wDamage]
+ ld [wce06], a
+ ld d, a
+
+; load in a the attack that was not selected,
+; and check if it is useable.
+ ld a, [wSelectedAttack]
+ ld b, a
+ ld a, $01
+ sub b
+ ld [wSelectedAttack], a
+ push de
+ call SwapTurn
+ farcall CheckIfSelectedMoveIsUnusable
+ call SwapTurn
+ pop de
+ jr c, .switch_back
+
+; the other attack is useable.
+; compare its damage to the selected move.
+ ld a, [wSelectedAttack]
+ push de
+ farcall EstimateDamage_FromDefendingPokemon
+ pop de
+ ld a, [wDamage]
+ cp d
+ jr nc, .subtract
+
+; in case the non-selected move is useable
+; and deals less damage than the selected move,
+; switch back to the other attack.
+.switch_back
+ ld a, [wSelectedAttack]
+ ld b, a
+ ld a, $01
+ sub b
+ ld [wSelectedAttack], a
+ ld a, [wce06]
+ ld [wDamage], a
+
+; now the selected attack is the one that deals
+; the most damage of the two (and is useable).
+; if subtracting damage by using Defender
+; still prevents a KO, return carry.
+.subtract
+ ld a, [wDamage]
+ sub 20
+ ld d, a
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetTurnDuelistVariable
+ sub d
+ jr c, .no_carry
+ jr z, .no_carry
+ scf
+ ret
+.no_carry
+ or a
+ ret
+; 0x20486
+
+; return carry if using Defender prevents Pokémon
+; from being knocked out by an attack with recoil.
+AIDecide_Defender2: ; 20486 (8:4486)
+ ld a, MOVE_FLAG1_ADDRESS | HIGH_RECOIL_F
+ call CheckLoadedMoveFlag
+ jr c, .recoil
+ ld a, MOVE_FLAG1_ADDRESS | LOW_RECOIL_F
+ call CheckLoadedMoveFlag
+ jr c, .recoil
+ or a
+ ret
+
+.recoil
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call LoadCardDataToBuffer2_FromDeckIndex
+ ld a, [wSelectedAttack]
+ or a
+ jr nz, .second_attack
+; first attack
+ ld a, [wLoadedCard2Move1EffectParam]
+ jr .check_weak
+.second_attack
+ ld a, [wLoadedCard2Move2EffectParam]
+
+; double recoil damage if card is weak to its own color.
+.check_weak
+ ld d, a
+ push de
+ call GetArenaCardColor
+ call TranslateColorToWR
+ ld b, a
+ call GetArenaCardWeakness
+ and b
+ pop de
+ jr z, .check_resist
+ sla d
+
+; subtract 30 from recoil damage if card resists its own color.
+; if this yields a negative number, return no carry.
+.check_resist
+ push de
+ call GetArenaCardColor
+ call TranslateColorToWR
+ ld b, a
+ call GetArenaCardResistance
+ and b
+ pop de
+ jr z, .subtract
+ ld a, d
+ sub 30
+ jr c, .no_carry
+ ld d, a
+
+; subtract damage prevented by Defender.
+; if damage still knocks out card, return no carry.
+; if damage does not knock out, return carry.
+.subtract
+ ld a, d
+ or a
+ jr z, .no_carry
+ sub 20
+ ld d, a
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetTurnDuelistVariable
+ sub d
+ jr c, .no_carry
+ jr z, .no_carry
+ scf
+ ret
+.no_carry
+ or a
+ ret
+; 0x204e8
+
+AIPlay_Pluspower: ; 204e8 (8:44e8)
+ ld a, [wCurrentAIFlags]
+ or AI_FLAG_USED_PLUSPOWER
+ ld [wCurrentAIFlags], a
+ ld a, [wAITrainerCardParameter]
+ ld [wAIPluspowerAttack], a
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x20501
+
+; returns carry if using a Pluspower can KO defending Pokémon
+; if active card cannot KO without the boost.
+; outputs in a the attack to use.
+AIDecide_Pluspower1: ; 20501 (8:4501)
+; this is mistakenly duplicated
+ xor a
+ ldh [hTempPlayAreaLocation_ff9d], a
+ xor a
+ ldh [hTempPlayAreaLocation_ff9d], a
+
+; continue if no attack can knock out.
+; if there's an attack that can, only continue
+; if it's unusable and there's no card in hand
+; to fulfill its energy cost.
+ farcall CheckIfAnyMoveKnocksOutDefendingCard
+ jr nc, .cannot_ko
+ farcall CheckIfSelectedMoveIsUnusable
+ jr nc, .no_carry
+ farcall LookForEnergyNeededForMoveInHand
+ jr c, .no_carry
+
+; cannot use an attack that knocks out.
+.cannot_ko
+; get active Pokémon's info.
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ ld [wTempTurnDuelistCardID], a
+
+; get defending Pokémon's info and check
+; its No Damage or Effect substatus.
+; if substatus is active, return.
+ call SwapTurn
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ ld [wTempNonTurnDuelistCardID], a
+ bank1call HandleNoDamageOrEffectSubstatus
+ call SwapTurn
+ jr c, .no_carry
+
+; check both attacks and decide which one
+; can KO with Pluspower boost.
+; if neither can KO, return no carry.
+ xor a ; FIRST_ATTACK_OR_PKMN_POWER
+ ld [wSelectedAttack], a
+ call .check_ko_with_pluspower
+ jr c, .kos_with_pluspower_1
+ ld a, SECOND_ATTACK
+ ld [wSelectedAttack], a
+ call .check_ko_with_pluspower
+ jr c, .kos_with_pluspower_2
+
+.no_carry
+ or a
+ ret
+
+; first attack can KO with Pluspower.
+.kos_with_pluspower_1
+ call .check_mr_mime
+ jr nc, .no_carry
+ xor a ; FIRST_ATTACK_OR_PKMN_POWER
+ scf
+ ret
+; second attack can KO with Pluspower.
+.kos_with_pluspower_2
+ call .check_mr_mime
+ jr nc, .no_carry
+ ld a, SECOND_ATTACK
+ scf
+ ret
+; 0x20562
+
+; return carry if move is useable and KOs
+; defending Pokémon with Pluspower boost.
+.check_ko_with_pluspower ; 20562 (8:4562)
+ farcall CheckIfSelectedMoveIsUnusable
+ jr c, .unusable
+ ld a, [wSelectedAttack]
+ farcall EstimateDamage_VersusDefendingCard
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetNonTurnDuelistVariable
+ ld b, a
+ ld hl, wDamage
+ sub [hl]
+ jr c, .no_carry
+ jr z, .no_carry
+ ld a, [hl]
+ add 10 ; add Pluspower boost
+ ld c, a
+ ld a, b
+ sub c
+ ret c ; return carry if damage > HP left
+ ret nz ; does not KO
+ scf
+ ret ; KOs with Pluspower boost
+.unusable
+ or a
+ ret
+; 0x20589
+
+; returns carry if Pluspower boost does
+; not exceed 30 damage when facing Mr. Mime.
+.check_mr_mime ; 20589 (8:4589)
+ ld a, [wDamage]
+ add 10 ; add Pluspower boost
+ cp 30 ; no danger in preventing damage
+ ret c
+ call SwapTurn
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ call SwapTurn
+ ld a, e
+ cp MR_MIME
+ ret z
+; damage is >= 30 but not Mr. Mime
+ scf
+ ret
+; 0x205a5
+
+; returns carry 7/10 of the time
+; if selected move is useable, can't KO without Pluspower boost
+; can damage Mr. Mime even with Pluspower boost
+; and has a minimum damage > 0.
+; outputs in a the attack to use.
+AIDecide_Pluspower2: ; 205a5 (8:45a5)
+ xor a
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call .check_can_ko
+ jr nc, .no_carry
+ call .check_random
+ jr nc, .no_carry
+ call .check_mr_mime
+ jr nc, .no_carry
+ scf
+ ret
+.no_carry
+ or a
+ ret
+; 0x205bb
+
+; returns carry if Pluspower boost does
+; not exceed 30 damage when facing Mr. Mime.
+.check_mr_mime ; 205bb (8:45bb)
+ ld a, [wDamage]
+ add 10 ; add Pluspower boost
+ cp 30 ; no danger in preventing damage
+ ret c
+ call SwapTurn
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ call SwapTurn
+ ld a, e
+ cp MR_MIME
+ ret z
+; damage is >= 30 but not Mr. Mime
+ scf
+ ret
+; 0x205d7
+
+; return carry if move is useable but cannot KO.
+.check_can_ko ; 205d7 (8:45d7)
+ farcall CheckIfSelectedMoveIsUnusable
+ jr c, .unuseable
+ ld a, [wSelectedAttack]
+ farcall EstimateDamage_VersusDefendingCard
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetNonTurnDuelistVariable
+ ld b, a
+ ld hl, wDamage
+ sub [hl]
+ jr c, .no_carry
+ jr z, .no_carry
+; can't KO.
+ scf
+ ret
+.unuseable
+ or a
+ ret
+; 0x205f6
+
+; return carry 7/10 of the time if
+; move is useable and minimum damage > 0.
+.check_random ; 205f6 (8:45f6)
+ farcall CheckIfSelectedMoveIsUnusable
+ jr c, .unuseable
+ ld a, [wSelectedAttack]
+ farcall EstimateDamage_VersusDefendingCard
+ ld a, [wAIMinDamage]
+ cp 10
+ jr c, .unuseable
+ ld a, 10
+ call Random
+ cp 3
+ ret
+; 0x20612
+
+AIPlay_Switch: ; 20612 (8:4612)
+ ld a, [wCurrentAIFlags]
+ or AI_FLAG_USED_SWITCH
+ ld [wCurrentAIFlags], a
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ xor a
+ ld [wcdb4], a
+ ret
+; 0x2062e
+
+; returns carry if the active card has less energy cards
+; than the retreat cost and if AI can't play an energy
+; card from the hand to fulfill the cost
+AIDecide_Switch: ; 2062e (8:462e)
+; check if AI can already play an energy card from hand to retreat
+ ld a, [wAIPlayEnergyCardForRetreat]
+ or a
+ jr z, .check_cost_amount
+
+; can't play energy card from hand to retreat
+; compare number of energy cards attached to retreat cost
+ xor a ; PLAY_AREA_ARENA
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call GetPlayAreaCardRetreatCost
+ push af
+ ld e, PLAY_AREA_ARENA
+ farcall CountNumberOfEnergyCardsAttached
+ ld b, a
+ pop af
+ sub b
+ ; jump if cards attached > retreat cost
+ jr c, .check_cost_amount
+ cp 2
+ ; jump if retreat cost is 2 more energy cards
+ ; than the number of cards attached
+ jr nc, .switch
+
+.check_cost_amount
+ xor a ; PLAY_AREA_ARENA
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call GetPlayAreaCardRetreatCost
+ cp 3
+ ; jump if retreat cost >= 3
+ jr nc, .switch
+
+ push af
+ ld e, PLAY_AREA_ARENA
+ farcall CountNumberOfEnergyCardsAttached
+ pop bc
+ cp b
+ ; jump if energy cards attached < retreat cost
+ jr c, .switch
+ ret
+
+.switch
+ farcall AIDecideBenchPokemonToSwitchTo
+ ccf
+ ret
+; 0x20666
+
+AIPlay_GustOfWind: ; 20666 (8:4666)
+ ld a, [wCurrentAIFlags]
+ or AI_FLAG_USED_GUST_OF_WIND
+ ld [wCurrentAIFlags], a
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x2067e
+
+AIDecide_GustOfWind: ; 2067e (8:467e)
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetNonTurnDuelistVariable
+ dec a
+ or a
+ ret z ; no bench cards
+
+; if used Gust Of Wind already,
+; do not use it again.
+ ld a, [wPreviousAIFlags]
+ and AI_FLAG_USED_GUST_OF_WIND
+ ret nz
+
+ farcall CheckIfActivePokemonCanUseAnyNonResidualMove
+ ret nc ; no non-residual move can be used
+
+ xor a ; PLAY_AREA_ARENA
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckIfAnyMoveKnocksOutDefendingCard
+ jr nc, .check_id ; if can't KO
+ farcall CheckIfSelectedMoveIsUnusable
+ jr nc, .no_carry ; if KO move is useable
+ farcall LookForEnergyNeededForMoveInHand
+ jr c, .no_carry ; if energy card is in hand
+
+.check_id
+ ; skip if current active card is MEW3 or MEWTWO1
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ cp MEW3
+ jr z, .no_carry
+ cp MEWTWO1
+ jr z, .no_carry
+
+ call .FindBenchCardToKnockOut
+ ret c
+
+ xor a ; PLAY_AREA_ARENA
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call .CheckIfNoAttackDealsDamage
+ jr c, .check_bench_energy
+
+ ; skip if current arena card's color is
+ ; the defending card's weakness
+ call GetArenaCardColor
+ call TranslateColorToWR
+ ld b, a
+ call SwapTurn
+ call GetArenaCardWeakness
+ call SwapTurn
+ and b
+ jr nz, .no_carry
+
+; check weakness
+ call .FindBenchCardWithWeakness
+ ret nc ; no bench card weak to arena card
+ scf
+ ret ; found bench card weak to arena card
+
+.no_carry
+ or a
+ ret
+
+; being here means AI's arena card cannot damage player's arena card
+
+; first check if there is a card in player's bench that
+; has no attached energy cards and that the AI can damage
+.check_bench_energy
+ ; return carry if there's a bench card with weakness
+ call .FindBenchCardWithWeakness
+ ret c
+
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetNonTurnDuelistVariable
+ ld d, a
+ ld e, PLAY_AREA_ARENA
+; loop through bench and check attached energy cards
+.loop_1
+ inc e
+ dec d
+ jr z, .check_bench_hp
+ call SwapTurn
+ call GetPlayAreaCardAttachedEnergies
+ call SwapTurn
+ ld a, [wTotalAttachedEnergies]
+ or a
+ jr nz, .loop_1 ; skip if has energy attached
+ call .CheckIfCanDamageBenchedCard
+ jr nc, .loop_1
+ ld a, e
+ scf
+ ret
+
+.check_bench_hp
+ ld a, $ff
+ ld [wce06], a
+ xor a
+ ld [wce08], a
+ ld e, a
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetNonTurnDuelistVariable
+ ld d, a
+
+; find bench card with least amount of available HP
+.loop_2
+ inc e
+ dec d
+ jr z, .check_found
+ ld a, e
+ add DUELVARS_ARENA_CARD_HP
+ call GetNonTurnDuelistVariable
+ ld b, a
+ ld a, [wce06]
+ inc b
+ cp b
+ jr c, .loop_2
+ call .CheckIfCanDamageBenchedCard
+ jr nc, .loop_2
+ dec b
+ ld a, b
+ ld [wce06], a
+ ld a, e
+ ld [wce08], a
+ jr .loop_2
+
+.check_found
+ ld a, [wce08]
+ or a
+ jr z, .no_carry
+; a card was found
+
+.set_carry
+ scf
+ ret
+
+.check_can_damage
+ push bc
+ push hl
+ xor a ; PLAY_AREA_ARENA
+ farcall CheckIfCanDamageDefendingPokemon
+ pop hl
+ pop bc
+ jr nc, .loop_3
+ ld a, c
+ scf
+ ret
+
+; returns carry if any of the player's
+; benched cards is weak to color in b
+; and has a way to damage it
+.FindBenchCardWithWeakness ; 2074d (8:474d)
+ ld a, DUELVARS_BENCH
+ call GetNonTurnDuelistVariable
+ ld c, PLAY_AREA_ARENA
+.loop_3
+ inc c
+ ld a, [hli]
+ cp $ff
+ jr z, .no_carry
+ call SwapTurn
+ call LoadCardDataToBuffer1_FromDeckIndex
+ call SwapTurn
+ ld a, [wLoadedCard1Weakness]
+ and b
+ jr nz, .check_can_damage
+ jr .loop_3
+
+; returns carry if neither attack can deal damage
+.CheckIfNoAttackDealsDamage ; 2076b (8:476b)
+ xor a ; FIRST_ATTACK_OR_PKMN_POWER
+ ld [wSelectedAttack], a
+ call .CheckIfAttackDealsNoDamage
+ jr c, .second_attack
+ ret
+.second_attack
+ ld a, SECOND_ATTACK
+ ld [wSelectedAttack], a
+ call .CheckIfAttackDealsNoDamage
+ jr c, .true
+ ret
+.true
+ scf
+ ret
+
+; returns carry if attack is Pokemon Power
+; or otherwise doesn't deal any damage
+.CheckIfAttackDealsNoDamage ; 20782 (8:4782)
+ ld a, [wSelectedAttack]
+ ld e, a
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ ld d, a
+ call CopyMoveDataAndDamage_FromDeckIndex
+ ld a, [wLoadedMoveCategory]
+
+ ; skip if move is a Power or has 0 damage
+ cp POKEMON_POWER
+ jr z, .no_damage
+ ld a, [wDamage]
+ or a
+ ret z
+
+ ; check damage against defending card
+ ld a, [wSelectedAttack]
+ farcall EstimateDamage_VersusDefendingCard
+ ld a, [wAIMaxDamage]
+ or a
+ ret nz
+
+.no_damage
+ scf
+ ret
+
+; returns carry if there is a player's bench card that
+; the opponent's current active card can KO
+.FindBenchCardToKnockOut ; 207a9 (8:47a9)
+ ld a, DUELVARS_BENCH
+ call GetNonTurnDuelistVariable
+ ld e, PLAY_AREA_BENCH_1
+
+.loop_4
+ ld a, [hli]
+ cp $ff
+ ret z
+
+; overwrite the player's active card and its HP
+; with the current bench card that is being checked
+ push hl
+ push de
+ ld b, a
+ ld a, DUELVARS_ARENA_CARD
+ call GetNonTurnDuelistVariable
+ push af
+ ld [hl], b
+ ld a, e
+ add DUELVARS_ARENA_CARD_HP
+ call GetNonTurnDuelistVariable
+ ld b, a
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetNonTurnDuelistVariable
+ push af
+ ld [hl], b
+
+ xor a ; PLAY_AREA_ARENA
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call .CheckIfAnyAttackKnocksOut
+ jr nc, .next
+ farcall CheckIfSelectedMoveIsUnusable
+ jr nc, .found
+ farcall LookForEnergyNeededForMoveInHand
+ jr c, .found
+
+; the following two local routines can be condensed into one
+; since they both revert the player's arena card
+.next
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetNonTurnDuelistVariable
+ pop af
+ ld [hl], a
+ ld a, DUELVARS_ARENA_CARD
+ call GetNonTurnDuelistVariable
+ pop af
+ ld [hl], a
+ pop de
+ inc e
+ pop hl
+ jr .loop_4
+
+; revert player's arena card and set carry
+.found
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetNonTurnDuelistVariable
+ pop af
+ ld [hl], a
+ ld a, DUELVARS_ARENA_CARD
+ call GetNonTurnDuelistVariable
+ pop af
+ ld [hl], a
+ pop de
+ ld a, e
+ pop hl
+ scf
+ ret
+
+; returns carry if any of arena card's attacks
+; KOs player card in location stored in e
+.CheckIfAnyAttackKnocksOut ; 20806 (8:4806)
+ xor a ; FIRST_ATTACK_OR_PKMN_POWER
+ call .CheckIfAttackKnocksOut
+ ret c
+ ld a, SECOND_ATTACK
+
+; returns carry if attack KOs player card
+; in location stored in e
+.CheckIfAttackKnocksOut
+ push de
+ farcall EstimateDamage_VersusDefendingCard
+ pop de
+ ld a, DUELVARS_ARENA_CARD_HP
+ add e
+ call GetNonTurnDuelistVariable
+ ld hl, wDamage
+ sub [hl]
+ ret c
+ ret nz
+ scf
+ ret
+
+; returns carry if opponent's arena card can damage
+; this benched card if it were switched with
+; the player's arena card
+.CheckIfCanDamageBenchedCard ; 20821 (8:4821)
+ push bc
+ push de
+ push hl
+
+ ; overwrite arena card data
+ ld a, e
+ add DUELVARS_ARENA_CARD
+ call GetNonTurnDuelistVariable
+ ld b, a
+ ld a, DUELVARS_ARENA_CARD
+ call GetNonTurnDuelistVariable
+ push af
+ ld [hl], b
+
+ ; overwrite arena card HP
+ ld a, e
+ add DUELVARS_ARENA_CARD_HP
+ call GetNonTurnDuelistVariable
+ ld b, a
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetNonTurnDuelistVariable
+ push af
+ ld [hl], b
+
+ xor a ; PLAY_AREA_ARENA
+ farcall CheckIfCanDamageDefendingPokemon
+ jr c, .can_damage
+
+; the following two local routines can be condensed into one
+; since they both revert the player's arena card
+
+; can't damage
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetNonTurnDuelistVariable
+ pop af
+ ld [hl], a
+ ld a, DUELVARS_ARENA_CARD
+ call GetNonTurnDuelistVariable
+ pop af
+ ld [hl], a
+ pop hl
+ pop de
+ pop bc
+ or a
+ ret
+
+.can_damage
+ ld a, DUELVARS_ARENA_CARD_HP
+ call GetNonTurnDuelistVariable
+ pop af
+ ld [hl], a
+ ld a, DUELVARS_ARENA_CARD
+ call GetNonTurnDuelistVariable
+ pop af
+ ld [hl], a
+ pop hl
+ pop de
+ pop bc
+ scf
+ ret
+; 0x2086d
+
+AIPlay_Bill: ; 2086d (8:486d)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x20878
+
+; return carry if cards in deck > 9
+AIDecide_Bill: ; 20878 (8:4878)
+ ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
+ call GetTurnDuelistVariable
+ cp DECK_SIZE - 9
+ ret
+; 0x20880
+
+AIPlay_EnergyRemoval: ; 20880 (8:4880)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld a, [wce1a]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x20895
+
+; picks an energy card in the player's Play Area to remove
+AIDecide_EnergyRemoval: ; 20895 (8:4895)
+; check if the current active card can KO player's card
+; if it's possible to KO, then do not consider the player's
+; active card to remove its attached energy
+ xor a ; PLAY_AREA_ARENA
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckIfAnyMoveKnocksOutDefendingCard
+ jr nc, .cannot_ko
+ farcall CheckIfSelectedMoveIsUnusable
+ jr nc, .can_ko
+ farcall LookForEnergyNeededForMoveInHand
+ jr nc, .cannot_ko
+
+.can_ko
+ ; start checking from the bench
+ ld a, PLAY_AREA_BENCH_1
+ ld [wce0f], a
+ jr .check_bench_energy
+.cannot_ko
+ ; start checking from the arena card
+ xor a ; PLAY_AREA_ARENA
+ ld [wce0f], a
+
+; loop each card and check if it has enough energy to use any attack
+; if it does, then proceed to pick an energy card to remove
+.check_bench_energy
+ call SwapTurn
+ ld a, [wce0f]
+ ld e, a
+.loop_1
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ call GetTurnDuelistVariable
+ cp $ff
+ jr z, .default
+
+ ld d, a
+ call .CheckIfCardHasEnergyAttached
+ jr nc, .next_1
+ call .CheckIfNotEnoughEnergyToAttack
+ jr nc, .pick_energy ; jump if enough energy to attack
+.next_1
+ inc e
+ jr .loop_1
+
+.pick_energy
+; a play area card was picked to remove energy
+; store the picked energy card to remove in wce1a
+; and set carry
+ ld a, e
+ push af
+ call PickAttachedEnergyCardToRemove
+ ld [wce1a], a
+ pop af
+ call SwapTurn
+ scf
+ ret
+
+; if no card in player's Play Area was found with enough energy
+; to attack, just pick an energy card from player's active card
+; (in case the AI cannot KO it this turn)
+.default
+ ld a, [wce0f]
+ or a
+ jr nz, .check_bench_damage ; not active card
+ call .CheckIfCardHasEnergyAttached
+ jr c, .pick_energy
+
+; lastly, check what attack on player's Play Area is highest damaging
+; and pick an energy card attached to that Pokemon to remove
+.check_bench_damage
+ xor a
+ ld [wce06], a
+ ld [wce08], a
+
+ ld e, PLAY_AREA_BENCH_1
+.loop_2
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ call GetTurnDuelistVariable
+ cp $ff
+ jr z, .found_damage
+
+ ld d, a
+ call .CheckIfCardHasEnergyAttached
+ jr nc, .next_2
+ call .FindHighestDamagingAttack
+.next_2
+ inc e
+ jr .loop_2
+
+.found_damage
+ ld a, [wce08]
+ or a
+ jr z, .no_carry ; skip if none found
+ ld e, a
+ jr .pick_energy
+.no_carry
+ call SwapTurn
+ or a
+ ret
+
+; returns carry if this card has any energy cards attached
+.CheckIfCardHasEnergyAttached ; 2091a (8:491a)
+ call GetPlayAreaCardAttachedEnergies
+ ld a, [wTotalAttachedEnergies]
+ or a
+ ret z
+ scf
+ ret
+
+; returns carry if this card does not
+; have enough energy for either of its attacks
+.CheckIfNotEnoughEnergyToAttack ; 20924 (8:4924)
+ push de
+ xor a ; FIRST_ATTACK_OR_PKMN_POWER
+ ld [wSelectedAttack], a
+ ld a, e
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckEnergyNeededForAttack
+ jr nc, .enough_energy
+ pop de
+
+ push de
+ ld a, SECOND_ATTACK
+ ld [wSelectedAttack], a
+ ld a, e
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckEnergyNeededForAttack
+ jr nc, .check_surplus
+ pop de
+
+; neither attack has enough energy
+ scf
+ ret
+
+.enough_energy
+ pop de
+ or a
+ ret
+
+; first attack doesn't have enough energy (or is just a Pokemon Power)
+; but second attack has enough energy to be used
+; check if there's surplus energy for attack and, if so, return carry
+.check_surplus
+ farcall CheckIfNoSurplusEnergyForMove
+ pop de
+ ccf
+ ret
+
+; stores in wce06 the highest damaging attack
+; for the card in play area location in e
+; and stores this card's location in wce08
+.FindHighestDamagingAttack ; 2094f (8:494f)
+ push de
+ ld a, e
+ ldh [hTempPlayAreaLocation_ff9d], a
+
+ xor a ; FIRST_ATTACK_OR_PKMN_POWER
+ farcall EstimateDamage_VersusDefendingCard
+ ld a, [wDamage]
+ or a
+ jr z, .skip_1
+ ld e, a
+ ld a, [wce06]
+ cp e
+ jr nc, .skip_1
+ ld a, e
+ ld [wce06], a ; store this damage value
+ pop de
+ ld a, e
+ ld [wce08], a ; store this location
+ jr .second_attack
+
+.skip_1
+ pop de
+
+.second_attack
+ push de
+ ld a, e
+ ldh [hTempPlayAreaLocation_ff9d], a
+
+ ld a, SECOND_ATTACK
+ farcall EstimateDamage_VersusDefendingCard
+ ld a, [wDamage]
+ or a
+ jr z, .skip_2
+ ld e, a
+ ld a, [wce06]
+ cp e
+ jr nc, .skip_2
+ ld a, e
+ ld [wce06], a ; store this damage value
+ pop de
+ ld a, e
+ ld [wce08], a ; store this location
+ ret
+.skip_2
+ pop de
+ ret
+; 0x20994
+
+AIPlay_SuperEnergyRemoval: ; 20994 (8:4994)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld a, [wce1a]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, [wce1b]
+ ldh [hTempRetreatCostCards], a
+ ld a, [wce1c]
+ ldh [hTempRetreatCostCards + 1], a
+ ld a, [wce1d]
+ ldh [hTempRetreatCostCards + 2], a
+ ld a, $ff
+ ldh [hTempRetreatCostCards + 3], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x209bc
+
+; picks two energy cards in the player's Play Area to remove
+AIDecide_SuperEnergyRemoval: ; 209bc (8:49bc)
+ ld e, PLAY_AREA_BENCH_1
+.loop_1
+; first find an Arena card with a color energy card
+; to discard for card effect
+; return immediately if no Arena cards
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ call GetTurnDuelistVariable
+ cp $ff
+ jr z, .exit
+
+ ld d, a
+ push de
+ call .LookForNonDoubleColorless
+ pop de
+ jr c, .not_double_colorless
+ inc e
+ jr .loop_1
+
+; returns carry if an energy card other than double colorless
+; is found attached to the card in play area location e
+.LookForNonDoubleColorless
+ ld a, e
+ call CreateArenaOrBenchEnergyCardList
+ ld hl, wDuelTempList
+.loop_2
+ ld a, [hli]
+ cp $ff
+ ret z
+ call LoadCardDataToBuffer1_FromDeckIndex
+ cp DOUBLE_COLORLESS_ENERGY
+ ; any basic energy card
+ ; will set carry flag here
+ jr nc, .loop_2
+ ret
+
+.exit
+ or a
+ ret
+
+; card in Play Area location e was found with
+; a basic energy card
+.not_double_colorless
+ ld a, e
+ ld [wce0f], a
+
+; check if the current active card can KO player's card
+; if it's possible to KO, then do not consider the player's
+; active card to remove its attached energy
+ xor a ; PLAY_AREA_ARENA
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckIfAnyMoveKnocksOutDefendingCard
+ jr nc, .cannot_ko
+ farcall CheckIfSelectedMoveIsUnusable
+ jr nc, .can_ko
+ farcall LookForEnergyNeededForMoveInHand
+ jr nc, .cannot_ko
+
+.can_ko
+ ; start checking from the bench
+ call SwapTurn
+ ld e, PLAY_AREA_BENCH_1
+ jr .loop_3
+.cannot_ko
+ ; start checking from the arena card
+ call SwapTurn
+ ld e, PLAY_AREA_ARENA
+
+; loop each card and check if it has enough energy to use any attack
+; if it does, then proceed to pick energy cards to remove
+.loop_3
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ call GetTurnDuelistVariable
+ cp $ff
+ jr z, .no_carry
+
+ ld d, a
+ call .CheckIfFewerThanTwoEnergyCards
+ jr c, .next_1
+ call .CheckIfNotEnoughEnergyToAttack
+ jr nc, .found_card ; jump if enough energy to attack
+.next_1
+ inc e
+ jr .loop_3
+
+.found_card
+; a play area card was picked to remove energy
+; if this is not the Arena Card, then check
+; entire bench to pick the highest damage
+ ld a, e
+ or a
+ jr nz, .check_bench_damage
+
+; store the picked energy card to remove in wce1a
+; and set carry
+.pick_energy
+ ld [wce1b], a
+ call PickTwoAttachedEnergyCards
+ ld [wce1c], a
+ ld a, b
+ ld [wce1d], a
+ call SwapTurn
+ ld a, [wce0f]
+ push af
+ call AIPickEnergyCardToDiscard
+ ld [wce1a], a
+ pop af
+ scf
+ ret
+
+; check what attack on player's Play Area is highest damaging
+; and pick an energy card attached to that Pokemon to remove
+.check_bench_damage
+ xor a
+ ld [wce06], a
+ ld [wce08], a
+
+ ld e, PLAY_AREA_BENCH_1
+.loop_4
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ call GetTurnDuelistVariable
+ cp $ff
+ jr z, .found_damage
+
+ ld d, a
+ call .CheckIfFewerThanTwoEnergyCards
+ jr c, .next_2
+ call .CheckIfNotEnoughEnergyToAttack
+ jr c, .next_2
+ call .FindHighestDamagingAttack
+.next_2
+ inc e
+ jr .loop_4
+
+.found_damage
+ ld a, [wce08]
+ or a
+ jr z, .no_carry
+ jr .pick_energy
+.no_carry
+ call SwapTurn
+ or a
+ ret
+
+; returns carry if the number of energy cards attached
+; is fewer than 2, or if all energy combined yields
+; fewer than 2 energy
+.CheckIfFewerThanTwoEnergyCards ; 20a77 (8:4a77)
+ call GetPlayAreaCardAttachedEnergies
+ ld a, [wTotalAttachedEnergies]
+ cp 2
+ ret c ; return if fewer than 2 attached cards
+
+; count all energy attached
+; i.e. colored energy card = 1
+; and double colorless energy card = 2
+ xor a
+ ld b, NUM_COLORED_TYPES
+ ld hl, wAttachedEnergies
+.loop_5
+ add [hl]
inc hl
- jp .asm_4106
+ dec b
+ jr nz, .loop_5
+ ld b, [hl]
+ srl b
+ add b
+ cp 2
+ ret
+
+; returns carry if this card does not
+; have enough energy for either of its attacks
+.CheckIfNotEnoughEnergyToAttack ; 20a92 (8:4a92)
+ push de
+ xor a ; FIRST_ATTACK_OR_PKMN_POWER
+ ld [wSelectedAttack], a
+ ld a, e
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckEnergyNeededForAttack
+ jr nc, .enough_energy
+ pop de
+
+ push de
+ ld a, SECOND_ATTACK
+ ld [wSelectedAttack], a
+ ld a, e
+ ldh [hTempPlayAreaLocation_ff9d], a
+ farcall CheckEnergyNeededForAttack
+ jr nc, .check_surplus
+ pop de
+
+; neither attack has enough energy
+ scf
+ ret
+
+.enough_energy
+ pop de
+ or a
+ ret
+
+; first attack doesn't have enough energy (or is just a Pokemon Power)
+; but second attack has enough energy to be used
+; check if there's surplus energy for attack and, if so,
+; return carry if this surplus energy is at least 2
+.check_surplus
+ farcall CheckIfNoSurplusEnergyForMove
+ cp 2
+ jr c, .enough_energy
+ pop de
+ scf
+ ret
+; 0x20ac1
+
+; stores in wce06 the highest damaging attack
+; for the card in play area location in e
+; and stores this card's location in wce08
+.FindHighestDamagingAttack ; 20ac1 (8:4ac1)
+ push de
+ ld a, e
+ ldh [hTempPlayAreaLocation_ff9d], a
+
+ xor a ; FIRST_ATTACK_OR_PKMN_POWER
+ farcall EstimateDamage_VersusDefendingCard
+ ld a, [wDamage]
+ or a
+ jr z, .skip_1
+ ld e, a
+ ld a, [wce06]
+ cp e
+ jr nc, .skip_1
+ ld a, e
+ ld [wce06], a ; store this damage value
+ pop de
+ ld a, e
+ ld [wce08], a ; store this location
+ jr .second_attack
+
+.skip_1
+ pop de
+
+.second_attack
+ push de
+ ld a, e
+ ldh [hTempPlayAreaLocation_ff9d], a
+
+ ld a, SECOND_ATTACK
+ farcall EstimateDamage_VersusDefendingCard
+ ld a, [wDamage]
+ or a
+ jr z, .skip_2
+ ld e, a
+ ld a, [wce06]
+ cp e
+ jr nc, .skip_2
+ ld a, e
+ ld [wce06], a ; store this damage value
+ pop de
+ ld a, e
+ ld [wce08], a ; store this location
+ ret
+.skip_2
+ pop de
+ ret
+; 0x20b06
+
+AIPlay_PokemonBreeder: ; 20b06 (8:4b06)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, [wce1a]
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x20b1b
+
+AIDecide_PokemonBreeder: ; 20b1b (8:4b1b)
+ call IsPrehistoricPowerActive
+ jp c, .done
+
+ ld a, 7
+ ld hl, wce08
+ call ClearMemory_Bank8
+
+ xor a
+ ld [wce06], a
+ call CreateHandCardList
+ ld hl, wDuelTempList
+
+.loop_hand_1
+ ld a, [hli]
+ cp $ff
+ jr z, .not_found_in_hand
+
+; check if card in hand is any of the following
+; stage 2 Pokemon cards
+ ld d, a
+ call LoadCardDataToBuffer1_FromDeckIndex
+ cp VENUSAUR1
+ jr z, .found
+ cp VENUSAUR2
+ jr z, .found
+ cp BLASTOISE
+ jr z, .found
+ cp VILEPLUME
+ jr z, .found
+ cp ALAKAZAM
+ jr z, .found
+ cp GENGAR
+ jr nz, .loop_hand_1
+
+.found
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ push hl
+ call GetTurnDuelistVariable
+ pop hl
+ ld c, a
+ ld e, PLAY_AREA_ARENA
+
+; check Play Area for card that can evolve into
+; the picked stage 2 Pokemon
+.loop_play_area_1
+ push hl
+ push bc
+ push de
+ call CheckIfCanEvolveInto_BasicToStage2
+ pop de
+ call nc, .can_evolve
+ pop bc
+ pop hl
+ inc e
+ dec c
+ jr nz, .loop_play_area_1
+ jr .loop_hand_1
+
+.can_evolve
+ ld a, DUELVARS_ARENA_CARD_HP
+ add e
+ call GetTurnDuelistVariable
+ call ConvertHPToCounters
+ swap a
+ ld b, a
+
+; count number of energy cards attached and keep
+; the lowest 4 bits (capped at $0f)
+ call GetPlayAreaCardAttachedEnergies
+ ld a, [wTotalAttachedEnergies]
+ cp $10
+ jr c, .not_maxed_out
+ ld a, %00001111
+.not_maxed_out
+ or b
+
+; 4 high bits of a = HP counters Pokemon still has
+; 4 low bits of a = number of energy cards attached
+
+; store this score in wce08 + PLAY_AREA*
+ ld hl, wce08
+ ld c, e
+ ld b, $00
+ add hl, bc
+ ld [hl], a
+
+; store the deck index of stage 2 Pokemon in wce0f + PLAY_AREA*
+ ld hl, wce0f
+ add hl, bc
+ ld [hl], d
+
+; increase wce06 by one
+ ld hl, wce06
+ inc [hl]
+ ret
+
+.not_found_in_hand
+ ld a, [wce06]
+ or a
+ jr z, .check_evolution_and_dragonite
+
+; an evolution has been found before
+ xor a
+ ld [wce06], a
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld c, a
+ ld e, $00
+ ld d, $00
+
+; find highest score in wce08
+.loop_score_1
+ ld hl, wce08
+ add hl, de
+ ld a, [wce06]
+ cp [hl]
+ jr nc, .not_higher
+
+; store this score to wce06
+ ld a, [hl]
+ ld [wce06], a
+; store this PLay Area location to wce07
+ ld a, e
+ ld [wce07], a
+
+.not_higher
+ inc e
+ dec c
+ jr nz, .loop_score_1
+
+; store the deck index of the stage 2 card
+; that has been decided in wce1a,
+; return the Play Area location of card
+; to evolve in a and return carry
+ ld a, [wce07]
+ ld e, a
+ ld hl, wce0f
+ add hl, de
+ ld a, [hl]
+ ld [wce1a], a
+ ld a, [wce07]
+ scf
+ ret
+
+.check_evolution_and_dragonite
+ ld a, 7
+ ld hl, wce08
+ call ClearMemory_Bank8
+
+ xor a
+ ld [wce06], a
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ push hl
+
+.loop_hand_2
+ pop hl
+ ld a, [hli]
+ cp $ff
+ jr z, .check_evolution_found
+
+ push hl
+ ld d, a
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld c, a
+ ld e, PLAY_AREA_ARENA
+
+.loop_play_area_2
+; check if evolution is possible
+ push bc
+ push de
+ call CheckIfCanEvolveInto_BasicToStage2
+ pop de
+ call nc, .HandleDragonite1Evolution
+ call nc, .can_evolve
+
+; not possible to evolve or returned carry
+; when handling Dragonite1 evolution
+ pop bc
+ inc e
+ dec c
+ jr nz, .loop_play_area_2
+ jr .loop_hand_2
+
+.check_evolution_found
+ ld a, [wce06]
+ or a
+ jr nz, .evolution_was_found
+; no evolution was found before
+ or a
+ ret
+
+.evolution_was_found
+ xor a
+ ld [wce06], a
+ ld a, $ff
+ ld [wce07], a
+
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld c, a
+ ld e, $00
+ ld d, $00
+
+; find highest score in wce08 with at least
+; 2 energy cards attached
+.loop_score_2
+ ld hl, wce08
+ add hl, de
+ ld a, [wce06]
+ cp [hl]
+ jr nc, .next_score
+
+; take the lower 4 bits (total energy cards)
+; and skip if less than 2
+ ld a, [hl]
+ ld b, a
+ and %00001111
+ cp 2
+ jr c, .next_score
+
+; has at least 2 energy cards
+; store the score in wce06
+ ld a, b
+ ld [wce06], a
+; store this PLay Area location to wce07
+ ld a, e
+ ld [wce07], a
+
+.next_score
+ inc e
+ dec c
+ jr nz, .loop_score_2
+
+ ld a, [wce07]
+ cp $ff
+ jr z, .done
+
+; a card to evolve was found
+; store the deck index of the stage 2 card
+; that has been decided in wce1a,
+; return the Play Area location of card
+; to evolve in a and return carry
+ ld e, a
+ ld hl, wce0f
+ add hl, de
+ ld a, [hl]
+ ld [wce1a], a
+ ld a, [wce07]
+ scf
+ ret
+
+.done
+ or a
+ ret
+
+; return carry if card is evolving to Dragonite1 and if
+; - the card that is evolving is not Arena card and
+; number of damage counters in Play Area is under 8;
+; - the card that is evolving is Arena card and has under 5
+; damage counters or has less than 3 energy cards attached.
+.HandleDragonite1Evolution ; 20c5c (8:4c5c)
+ push af
+ push bc
+ push de
+ push hl
+ push de
+
+; check card ID
+ ld a, d
+ call GetCardIDFromDeckIndex
+ ld a, e
+ pop de
+ cp DRAGONITE1
+ jr nz, .no_carry
+
+; check card Play Area location
+ ld a, e
+ or a
+ jr z, .active_card_dragonite
+
+; the card that is evolving is not active card
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld b, a
+ ld c, 0
+
+; count damage counters in Play Area
+.loop_play_area_damage
+ dec b
+ ld e, b
+ push bc
+ call GetCardDamage
+ pop bc
+ call ConvertHPToCounters
+ add c
+ ld c, a
+
+ ld a, b
+ or a
+ jr nz, .loop_play_area_damage
+
+; compare number of total damage counters
+; with 7, if less or equal to that, set carry
+ ld a, 7
+ cp c
+ jr c, .no_carry
+ jr .set_carry
+
+.active_card_dragonite
+; the card that is evolving is active card
+; compare number of this card's damage counters
+; with 5, if less than that, set carry
+ ld e, PLAY_AREA_ARENA
+ call GetCardDamage
+ cp 5
+ jr c, .set_carry
+
+; compare number of this card's attached energy cards
+; with 3, if less than that, set carry
+ ld e, PLAY_AREA_ARENA
+ farcall CountNumberOfEnergyCardsAttached
+ cp 3
+ jr c, .set_carry
+ jr .no_carry
+
+.no_carry
+ pop hl
+ pop de
+ pop bc
+ pop af
+ ret
+
+.set_carry
+ pop hl
+ pop de
+ pop bc
+ pop af
+ scf
+ ret
+; 0x20cae
+
+AIPlay_ProfessorOak: ; 20cae (8:4cae)
+ ld a, [wCurrentAIFlags]
+ or AI_FLAG_USED_PROFESSOR_OAK | AI_FLAG_MODIFIED_HAND
+ ld [wCurrentAIFlags], a
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x20cc1
+
+; sets carry if AI determines a score of playing
+; Professor Oak is over a certain threshold.
+AIDecide_ProfessorOak: ; 20cc1 (8:4cc1)
+; return if cards in deck <= 6
+ ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
+ call GetTurnDuelistVariable
+ cp DECK_SIZE - 6
+ ret nc
+
+ ld a, [wOpponentDeckID]
+ cp LEGENDARY_ARTICUNO_DECK_ID
+ jp z, .HandleLegendaryArticunoDeck
+ cp EXCAVATION_DECK_ID
+ jp z, .HandleExcavationDeck
+ cp WONDERS_OF_SCIENCE_DECK_ID
+ jp z, .HandleWondersOfScienceDeck
+
+; return if cards in deck <= 14
+.check_cards_deck
+ ld a, [hl]
+ cp DECK_SIZE - 14
+ ret nc
+
+; initialize score
+ ld a, $1e
+ ld [wce06], a
+
+; check number of cards in hand
+.check_cards_hand
+ ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND
+ call GetTurnDuelistVariable
+ cp 4
+ jr nc, .more_than_3_cards
+
+; less than 4 cards in hand
+ ld a, [wce06]
+ add $32
+ ld [wce06], a
+ jr .check_energy_cards
+
+.more_than_3_cards
+ cp 9
+ jr c, .check_energy_cards
+
+; more than 8 cards
+ ld a, [wce06]
+ sub $1e
+ ld [wce06], a
+
+.check_energy_cards
+ farcall CreateEnergyCardListFromHand
+ jr nc, .handle_blastoise
+
+; no energy cards in hand
+ ld a, [wce06]
+ add $28
+ ld [wce06], a
+
+.handle_blastoise
+ ld a, MUK
+ call CountPokemonIDInBothPlayAreas
+ jr c, .check_hand
+
+; no Muk in Play Area
+ ld a, BLASTOISE
+ call CountPokemonIDInPlayArea
+ jr nc, .check_hand
+
+; at least one Blastoise in AI Play Area
+ ld a, WATER_ENERGY
+ farcall LookForCardIDInHand
+ jr nc, .check_hand
+
+; no Water energy in hand
+ ld a, [wce06]
+ add $0a
+ ld [wce06], a
+
+; this part seems buggy
+; the AI loops through all the cards in hand and checks
+; if any of them is not a Pokemon card and has Basic stage.
+; it seems like the intention was that if there was
+; any Basic Pokemon still in hand, the AI would add to the score.
+.check_hand
+ call CreateHandCardList
+ ld hl, wDuelTempList
+.loop_hand
+ ld a, [hli]
+ cp $ff
+ jr z, .check_evolution
+
+ call LoadCardDataToBuffer1_FromDeckIndex
+ ld a, [wLoadedCard1Type]
+ cp TYPE_ENERGY
+ jr c, .loop_hand ; bug, should be jr nc
+
+ ld a, [wLoadedCard1Stage]
+ or a
+ jr nz, .loop_hand
+
+ ld a, [wce06]
+ add $0a
+ ld [wce06], a
+
+.check_evolution
+ xor a
+ ld [wce0f], a
+ ld [wce0f + 1], a
+
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld d, a
+ ld e, PLAY_AREA_ARENA
+
+.loop_play_area
+ push de
+ call .LookForEvolution
+ pop de
+ jr nc, .not_in_hand
+
+; there's a card in hand that can evolve
+ ld a, $01
+ ld [wce0f], a
+
+.not_in_hand
+; check if a card that can evolve was found at all
+; if not, go to the next card in the Play Area
+ ld a, [wce08]
+ cp $01
+ jr nz, .next_play_area
+
+; if it was found, set wce0f + 1 to $01
+ ld a, $01
+ ld [wce0f + 1], a
+
+.next_play_area
+ inc e
+ dec d
+ jr nz, .loop_play_area
+
+; if a card was found that evolves...
+ ld a, [wce0f + 1]
+ or a
+ jr z, .check_score
+
+; ...but that card is not in the hand...
+ ld a, [wce0f]
+ or a
+ jr nz, .check_score
+
+; ...add to the score
+ ld a, [wce06]
+ add $0a
+ ld [wce06], a
+
+; only return carry if score > $3c
+.check_score
+ ld a, [wce06]
+ ld b, $3c
+ cp b
+ jr nc, .set_carry
+ or a
+ ret
+
+.set_carry
+ scf
+ ret
+; 0x20d9d
+
+; return carry if there's a card in the hand that
+; can evolve the card in Play Area location in e.
+; sets wce08 to $01 if any card is found that can
+; evolve regardless of card location.
+.LookForEvolution ; 20d9d (8:4d9d)
+ xor a
+ ld [wce08], a
+ ld d, 0
+
+; loop through the whole deck to check if there's
+; a card that can evolve this Pokemon.
+.loop_deck_evolution
+ push de
+ call CheckIfCanEvolveInto
+ pop de
+ jr nc, .can_evolve
+.evolution_not_in_hand
+ inc d
+ ld a, DECK_SIZE
+ cp d
+ jr nz, .loop_deck_evolution
+
+ or a
+ ret
+
+; a card was found that can evolve, set wce08 to $01
+; and if the card is in the hand, return carry.
+; otherwise resume looping through deck.
+.can_evolve
+ ld a, $01
+ ld [wce08], a
+ ld a, DUELVARS_CARD_LOCATIONS
+ add d
+ call GetTurnDuelistVariable
+ cp CARD_LOCATION_HAND
+ jr nz, .evolution_not_in_hand
+
+ scf
+ ret
+; 0x20dc3
+
+; handles Legendary Articuno Deck AI logic.
+.HandleLegendaryArticunoDeck ; 20dc3 (8:4dc3)
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp 3
+ jr nc, .check_playable_cards
+
+; has less than 3 Pokemon in Play Area.
+ push af
+ call CreateHandCardList
+ pop af
+ ld d, a
+ ld e, PLAY_AREA_ARENA
+
+; if no cards in hand evolve cards in Play Area,
+; returns carry.
+.loop_play_area_articuno
+ ld a, DUELVARS_ARENA_CARD
+ add e
+
+ push de
+ call GetTurnDuelistVariable
+ farcall CheckForEvolutionInList
+ pop de
+ jr c, .check_playable_cards
+
+ inc e
+ ld a, d
+ cp e
+ jr nz, .loop_play_area_articuno
+
+.set_carry_articuno
+ scf
+ ret
+
+; if there are more than 3 energy cards in hand,
+; return no carry, otherwise check for playable cards.
+.check_playable_cards
+ call CountOppEnergyCardsInHand
+ cp 4
+ jr nc, .no_carry_articuno
+
+; remove both Professor Oak cards from list
+; before checking for playable cards
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ ld e, PROFESSOR_OAK
+ farcall RemoveCardIDInList
+ ld e, PROFESSOR_OAK
+ farcall RemoveCardIDInList
+
+; look in hand for cards that can be played.
+; if a card that cannot be played is found, return no carry.
+; otherwise return carry.
+.loop_hand_articuno
+ ld a, [hli]
+ cp $ff
+ jr z, .set_carry_articuno
+ push hl
+ farcall CheckIfCardCanBePlayed
+ pop hl
+ jr c, .loop_hand_articuno
+
+.no_carry_articuno
+ or a
+ ret
+; 0x20e11
+
+; handles Excavation deck AI logic.
+; sets score depending on whether there's no
+; Mysterious Fossil in play and in hand.
+.HandleExcavationDeck ; 20e11 (8:4e11)
+; return no carry if cards in deck < 15
+ ld a, [hl]
+ cp 46
+ ret nc
+
+; look for Mysterious Fossil
+ ld a, MYSTERIOUS_FOSSIL
+ call LookForCardIDInHandAndPlayArea
+ jr c, .found_mysterious_fossil
+ ld a, $50
+ ld [wce06], a
+ jp .check_cards_hand
+.found_mysterious_fossil
+ ld a, $1e
+ ld [wce06], a
+ jp .check_cards_hand
+; 0x20e2c
+
+; handles Wonders of Science AI logic.
+; if there's either Grimer or Muk in hand,
+; do not play Professor Oak.
+.HandleWondersOfScienceDeck ; 20e2c (8:4e2c)
+ ld a, GRIMER
+ call LookForCardIDInHandList_Bank8
+ jr c, .found_grimer_or_muk
+ ld a, MUK
+ call LookForCardIDInHandList_Bank8
+ jr c, .found_grimer_or_muk
+
+ ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
+ call GetTurnDuelistVariable
+ jp .check_cards_deck
+
+.found_grimer_or_muk
+ or a
+ ret
+; 0x20e44
+
+AIPlay_EnergyRetrieval: ; 20e44 (8:4e44)
+ ld a, [wCurrentAIFlags]
+ or AI_FLAG_MODIFIED_HAND
+ ld [wCurrentAIFlags], a
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld a, [wce1a]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, [wce1b]
+ ldh [hTempRetreatCostCards], a
+ cp $ff
+ jr z, .asm_20e68
+ ld a, $ff
+ ldh [$ffa3], a
+.asm_20e68
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x20e6e
+
+; checks whether AI can play Energy Retrieval and
+; picks the energy cards from the discard pile,
+; and duplicate cards in hand to discard.
+AIDecide_EnergyRetrieval: ; 20e6e (8:4e6e)
+; return no carry if no cards in hand
+ farcall CreateEnergyCardListFromHand
+ jp nc, .no_carry
+
+; handle Go Go Rain Dance deck
+; return no carry if there's no Muk card in play and
+; if there's no Blastoise card in Play Area
+; if there's a Muk in play, continue as normal
+ ld a, [wOpponentDeckID]
+ cp GO_GO_RAIN_DANCE_DECK_ID
+ jr nz, .start
+ ld a, MUK
+ call CountPokemonIDInBothPlayAreas
+ jr c, .start
+ ld a, BLASTOISE
+ call CountPokemonIDInPlayArea
+ jp nc, .no_carry
+
+.start
+; find duplicate cards in hand
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ call FindDuplicateCards
+ jp c, .no_carry
+
+ ld [wce06], a
+ ld a, CARD_LOCATION_DISCARD_PILE
+ call FindBasicEnergyCardsInLocation
+ jp c, .no_carry
+
+; some basic energy cards were found in Discard Pile
+ ld a, $ff
+ ld [wce1a], a
+ ld [wce1b], a
+ ld [wce1c], a
+
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld d, a
+ ld e, PLAY_AREA_ARENA
+
+; first check if there are useful energy cards in the list
+; and choose them for retrieval first
+.loop_play_area
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ push de
+
+; load this card's ID in wTempCardID
+; and this card's Type in wTempCardType
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ ld [wTempCardID], a
+ call LoadCardDataToBuffer1_FromCardID
+ pop de
+ ld a, [wLoadedCard1Type]
+ or TYPE_ENERGY
+ ld [wTempCardType], a
+
+; loop the energy cards in the Discard Pile
+; and check if they are useful for this Pokemon
+ ld hl, wDuelTempList
+.loop_energy_cards_1
+ ld a, [hli]
+ cp $ff
+ jr z, .next_play_area
+
+ ld b, a
+ push hl
+ farcall CheckIfEnergyIsUseful
+ pop hl
+ jr nc, .loop_energy_cards_1
+
+ ld a, [wce1a]
+ cp $ff
+ jr nz, .second_energy_1
+
+; check if there were already chosen cards,
+; if this is the second chosen card, return carry
+
+; first energy card found
+ ld a, b
+ ld [wce1a], a
+ call RemoveCardFromList
+ jr .next_play_area
+.second_energy_1
+ ld a, b
+ ld [wce1b], a
+ jr .set_carry
+
+.next_play_area
+ inc e
+ dec d
+ jr nz, .loop_play_area
+
+; next, if there are still energy cards left to choose,
+; loop through the energy cards again and select
+; them in order.
+ ld hl, wDuelTempList
+.loop_energy_cards_2
+ ld a, [hli]
+ cp $ff
+ jr z, .check_chosen
+ ld b, a
+ ld a, [wce1a]
+ cp $ff
+ jr nz, .second_energy_2
+ ld a, b
+ ld [wce1a], a
+ call RemoveCardFromList
+ jr .loop_energy_cards_2
+
+.second_energy_2
+ ld a, b
+ ld [wce1b], a
+ jr .set_carry
+
+; will set carry if at least one has been chosen
+.check_chosen
+ ld a, [wce1a]
+ cp $ff
+ jr nz, .set_carry
+.no_carry
+ or a
+ ret
+
+.set_carry
+ ld a, [wce06]
+ scf
+ ret
+; 0x20f27
+
+; remove an element from the list
+; and shortens it accordingly
+; input:
+; hl = pointer to element after the one to remove
+RemoveCardFromList: ; 20f27 (8:4f27)
+ push de
+ ld d, h
+ ld e, l
+ dec hl
+ push hl
+.loop_remove
+ ld a, [de]
+ ld [hli], a
+ cp $ff
+ jr z, .done_remove
+ inc de
+ jr .loop_remove
+.done_remove
+ pop hl
+ pop de
+ ret
+; 0x20f38
+
+; finds duplicates in card list in hl.
+; if a duplicate of Pokemon cards are found, return in
+; a the deck index of the second one.
+; otherwise, if a duplicate of non-Pokemon cards are found
+; return in a the deck index of the second one.
+; if no duplicates found, return carry.
+; input:
+; hl = list to look in
+; output:
+; a = deck index of duplicate card
+FindDuplicateCards: ; 20f38 (8:4f38)
+ ld a, $ff
+ ld [wce0f], a
+ ld [wce0f + 1], a
+ push hl
+
+.loop_outer
+; get ID of current card
+ pop hl
+ ld a, [hli]
+ cp $ff
+ jr z, .check_found
+ call GetCardIDFromDeckIndex
+ ld b, e
+ push hl
+
+; loop the rest of the list to find
+; another card with the same ID
+.loop_inner
+ ld a, [hli]
+ cp $ff
+ jr z, .loop_outer
+ ld c, a
+ call GetCardIDFromDeckIndex
+ ld a, e
+ cp b
+ jr nz, .loop_inner
+
+; found two cards with same ID
+ push bc
+ call GetCardType
+ pop bc
+ cp TYPE_ENERGY
+ jr c, .not_energy
+
+; they are energy or trainer cards
+; loads wce0f+1 with this card deck index
+ ld a, c
+ ld [wce0f + 1], a
+ jr .loop_outer
+
+.not_energy
+; they are Pokemon cards
+; loads wce0f with this card deck index
+ ld a, c
+ ld [wce0f], a
+ jr .loop_outer
+
+.check_found
+ ld a, [wce0f]
+ cp $ff
+ jr nz, .no_carry
+ ld a, [wce0f + 1]
+ cp $ff
+ jr nz, .no_carry
+
+; only set carry if duplicate cards were not found
+ scf
+ ret
+
+.no_carry
+; two cards with the same ID were found
+; of either Pokemon or Non-Pokemon cards
+ or a
+ ret
+; 0x20f80
+
+AIPlay_SuperEnergyRetrieval: ; 20f80 (8:4f80)
+ ld a, [wCurrentAIFlags]
+ or AI_FLAG_MODIFIED_HAND
+ ld [wCurrentAIFlags], a
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld a, [wce1a]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, [wce1b]
+ ldh [hTempRetreatCostCards], a
+ ld a, [wce1c]
+ ldh [$ffa3], a
+ cp $ff
+ jr z, .asm_20fbb
+ ld a, [wce1d]
+ ldh [$ffa4], a
+ cp $ff
+ jr z, .asm_20fbb
+ ld a, [wce1e]
+ ldh [$ffa5], a
+ cp $ff
+ jr z, .asm_20fbb
+ ld a, $ff
+ ldh [$ffa6], a
+.asm_20fbb
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x20fc1
+
+AIDecide_SuperEnergyRetrieval: ; 20fc1 (8:4fc1)
+; return no carry if no cards in hand
+ farcall CreateEnergyCardListFromHand
+ jp nc, .no_carry
+
+; handle Go Go Rain Dance deck
+; return no carry if there's no Muk card in play and
+; if there's no Blastoise card in Play Area
+; if there's a Muk in play, continue as normal
+ ld a, [wOpponentDeckID]
+ cp GO_GO_RAIN_DANCE_DECK_ID
+ jr nz, .start
+ ld a, MUK
+ call CountPokemonIDInBothPlayAreas
+ jr c, .start
+ ld a, BLASTOISE
+ call CountPokemonIDInPlayArea
+ jp nc, .no_carry
+
+.start
+; find duplicate cards in hand
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ call FindDuplicateCards
+ jp c, .no_carry
+
+; remove the duplicate card in hand
+; and run the hand check again
+ ld [wce06], a
+ ld hl, wDuelTempList
+ call FindAndRemoveCardFromList
+ call FindDuplicateCards
+ jp c, .no_carry
+
+ ld [wce08], a
+ ld a, CARD_LOCATION_DISCARD_PILE
+ call FindBasicEnergyCardsInLocation
+ jp c, .no_carry
+
+; some basic energy cards were found in Discard Pile
+ ld a, $ff
+ ld [wce1b], a
+ ld [wce1c], a
+ ld [wce1d], a
+ ld [wce1e], a
+ ld [wce1f], a
+
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld d, a
+ ld e, PLAY_AREA_ARENA
+
+; first check if there are useful energy cards in the list
+; and choose them for retrieval first
+.loop_play_area
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ push de
+
+; load this card's ID in wTempCardID
+; and this card's Type in wTempCardType
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ ld [wTempCardID], a
+ call LoadCardDataToBuffer1_FromCardID
+ pop de
+ ld a, [wLoadedCard1Type]
+ or TYPE_ENERGY
+ ld [wTempCardType], a
+
+; loop the energy cards in the Discard Pile
+; and check if they are useful for this Pokemon
+ ld hl, wDuelTempList
+.loop_energy_cards_1
+ ld a, [hli]
+ cp $ff
+ jr z, .next_play_area
+
+ ld b, a
+ push hl
+ farcall CheckIfEnergyIsUseful
+ pop hl
+ jr nc, .loop_energy_cards_1
+
+; first energy
+ ld a, [wce1b]
+ cp $ff
+ jr nz, .second_energy_1
+ ld a, b
+ ld [wce1b], a
+ call RemoveCardFromList
+ jr .next_play_area
+
+.second_energy_1
+ ld a, [wce1c]
+ cp $ff
+ jr nz, .third_energy_1
+ ld a, b
+ ld [wce1c], a
+ call RemoveCardFromList
+ jr .next_play_area
+
+.third_energy_1
+ ld a, [wce1d]
+ cp $ff
+ jr nz, .fourth_energy_1
+ ld a, b
+ ld [wce1d], a
+ call RemoveCardFromList
+ jr .next_play_area
+
+.fourth_energy_1
+ ld a, b
+ ld [wce1e], a
+ jr .set_carry
+
+.next_play_area
+ inc e
+ dec d
+ jr nz, .loop_play_area
+
+; next, if there are still energy cards left to choose,
+; loop through the energy cards again and select
+; them in order.
+ ld hl, wDuelTempList
+.loop_energy_cards_2
+ ld a, [hli]
+ cp $ff
+ jr z, .check_chosen
+ ld b, a
+ ld a, [wce1b]
+ cp $ff
+ jr nz, .second_energy_2
+ ld a, b
+
+; first energy
+ ld [wce1b], a
+ call RemoveCardFromList
+ jr .loop_energy_cards_2
+
+.second_energy_2
+ ld a, [wce1c]
+ cp $ff
+ jr nz, .third_energy_2
+ ld a, b
+ ld [wce1c], a
+ call RemoveCardFromList
+ jr .loop_energy_cards_2
+
+.third_energy_2
+ ld a, [wce1d]
+ cp $ff
+ jr nz, .fourth_energy
+ ld a, b
+ ld [wce1d], a
+ call RemoveCardFromList
+ jr .loop_energy_cards_2
+
+.fourth_energy
+ ld a, b
+ ld [wce1e], a
+ jr .set_carry
+
+; will set carry if at least one has been chosen
+.check_chosen
+ ld a, [wce1b]
+ cp $ff
+ jr nz, .set_carry
+
+.no_carry
+ or a
+ ret
+.set_carry
+ ld a, [wce08]
+ ld [wce1a], a
+ ld a, [wce06]
+ scf
+ ret
+; 0x210d5
+
+; finds the card with deck index a in list hl,
+; and removes it from the list.
+; the card HAS to exist in the list, since this
+; routine does not check for the terminating byte $ff!
+; input:
+; a = card deck index to look
+; hl = pointer to list of cards
+FindAndRemoveCardFromList: ; 210d5 (8:50d5)
+ push hl
+ ld b, a
+.loop_duplicate
+ ld a, [hli]
+ cp b
+ jr nz, .loop_duplicate
+ call RemoveCardFromList
+ pop hl
+ ret
+; 0x210e0
+
+AIPlay_PokemonCenter: ; 210e0 (8:50e0)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x210eb
+
+AIDecide_PokemonCenter: ; 210eb (8:50eb)
+ xor a
+ ldh [hTempPlayAreaLocation_ff9d], a
+
+; return if active Pokemon can KO player's card.
+ farcall CheckIfAnyMoveKnocksOutDefendingCard
+ jr nc, .start
+ farcall CheckIfSelectedMoveIsUnusable
+ jr nc, .no_carry
+ farcall LookForEnergyNeededForMoveInHand
+ jr c, .no_carry
+
+.start
+ xor a
+ ld [wce06], a
+ ld [wce08], a
+ ld [wce0f], a
+
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld d, a
+ ld e, PLAY_AREA_ARENA
+
+.loop_play_area
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ push de
+ call GetTurnDuelistVariable
+ call LoadCardDataToBuffer1_FromDeckIndex
+ ld a, e ; useless instruction
+ pop de
+
+; get this Pokemon's current HP in number of counters
+; and add it to the total.
+ ld a, [wLoadedCard1HP]
+ call ConvertHPToCounters
+ ld b, a
+ ld a, [wce06]
+ add b
+ ld [wce06], a
+
+; get this Pokemon's current damage counters
+; and add it to the total.
+ call GetCardDamage
+ call ConvertHPToCounters
+ ld b, a
+ ld a, [wce08]
+ add b
+ ld [wce08], a
+
+; get this Pokemon's number of attached energy cards
+; and add it to the total.
+; if there's overflow, return no carry.
+ call GetPlayAreaCardAttachedEnergies
+ ld a, [wTotalAttachedEnergies]
+ ld b, a
+ ld a, [wce0f]
+ add b
+ jr c, .no_carry
+ ld [wce0f], a
+
+ inc e
+ dec d
+ jr nz, .loop_play_area
+
+; if (number of damage counters / 2) < (total energy cards attached)
+; return no carry.
+ ld a, [wce08]
+ srl a
+ ld hl, wce0f
+ cp [hl]
+ jr c, .no_carry
+
+; if (number of HP counters * 6 / 10) >= (number of damage counters)
+; return no carry.
+ ld a, [wce06]
+ ld l, a
+ ld h, 6
+ call HtimesL
+ call CalculateWordTensDigit
+ ld a, l
+ ld hl, wce08
+ cp [hl]
+ jr nc, .no_carry
+
+ scf
+ ret
+
+.no_carry
+ or a
+ ret
+; 0x21170
+
+AIPlay_ImposterProfessorOak: ; 21170 (8:5170)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x2117b
+
+; sets carry depending on player's number of cards
+; in deck in in hand.
+AIDecide_ImposterProfessorOak: ; 2117b (8:517b)
+ ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
+ call GetNonTurnDuelistVariable
+ cp DECK_SIZE - 14
+ jr c, .more_than_14_cards
+
+; if player has less than 14 cards in deck, only
+; set carry if number of cards in their hands < 6
+ ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND
+ call GetNonTurnDuelistVariable
+ cp 6
+ jr c, .set_carry
+.no_carry
+ or a
+ ret
+
+; if player has more than 14 cards in deck, only
+; set carry if number of cards in their hands >= 9
+.more_than_14_cards
+ ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND
+ call GetNonTurnDuelistVariable
+ cp 9
+ jr c, .no_carry
+.set_carry
+ scf
+ ret
+; 0x2119a
+
+AIPlay_EnergySearch: ; 2119a (8:519a)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x211aa
+
+; AI checks for playing Energy Search
+AIDecide_EnergySearch: ; 211aa (8:51aa)
+ farcall CreateEnergyCardListFromHand
+ jr c, .start
+ call .CheckForUsefulEnergyCards
+ jr c, .start
+
+; there are energy cards in hand and at least
+; one of them is useful to a card in Play Area
+.no_carry
+ or a
+ ret
+
+.start
+ ld a, [wOpponentDeckID]
+ cp HEATED_BATTLE_DECK_ID
+ jr z, .heated_battle
+ cp WONDERS_OF_SCIENCE_DECK_ID
+ jr z, .wonders_of_science
+
+; if no energy cards in deck, return no carry
+ ld a, CARD_LOCATION_DECK
+ call FindBasicEnergyCardsInLocation
+ jr c, .no_carry
+
+; if any of the energy cards in deck is useful
+; return carry right away...
+ call .CheckForUsefulEnergyCards
+ jr c, .no_useful
+ scf
+ ret
+
+; ...otherwise save the list in a before return carry.
+.no_useful
+ ld a, [wDuelTempList]
+ scf
+ ret
+
+; Heated Battle deck only searches for Fire and Lightning
+; if they are found to be useful to some card in Play Area
+.heated_battle
+ ld a, CARD_LOCATION_DECK
+ call FindBasicEnergyCardsInLocation
+ jr c, .no_carry
+ call .CheckUsefulFireOrLightningEnergy
+ jr c, .no_carry
+ scf
+ ret
+
+; this subroutine has a bug.
+; it was supposed to use the .CheckUsefulGrassEnergy subroutine
+; but uses .CheckUsefulFireOrLightningEnergy instead.
+.wonders_of_science
+ ld a, CARD_LOCATION_DECK
+ call FindBasicEnergyCardsInLocation
+ jr c, .no_carry
+ call .CheckUsefulFireOrLightningEnergy
+ jr c, .no_carry
+ scf
+ ret
+; 0x211f1
+
+; return carry if cards in wDuelTempList are not
+; useful to any of the Play Area Pokemon
+.CheckForUsefulEnergyCards ; 211f1 (8:51f1)
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld d, a
+ ld e, PLAY_AREA_ARENA
+
+.loop_play_area_1
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ push de
+ call GetTurnDuelistVariable
+
+; store ID and type of card
+ call GetCardIDFromDeckIndex
+ ld a, e
+ ld [wTempCardID], a
+ call LoadCardDataToBuffer1_FromCardID
+ pop de
+ ld a, [wLoadedCard1Type]
+ or TYPE_ENERGY
+ ld [wTempCardType], a
+
+; look in list for a useful energy,
+; is any is found return no carry.
+ ld hl, wDuelTempList
+.loop_energy_1
+ ld a, [hli]
+ cp $ff
+ jr z, .none_found
+ ld b, a
+ push hl
+ farcall CheckIfEnergyIsUseful
+ pop hl
+ jr nc, .loop_energy_1
+
+ ld a, b
+ or a
+ ret
+
+.none_found
+ inc e
+ ld a, e
+ cp d
+ jr nz, .loop_play_area_1
+
+ scf
+ ret
+; 0x2122e
+
+; checks whether there are useful energies
+; only for Fire and Lightning type Pokemon cards
+; in Play Area. If none found, return carry.
+.CheckUsefulFireOrLightningEnergy ; 2122e (8:522e)
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld d, a
+ ld e, PLAY_AREA_ARENA
+
+.loop_play_area_2
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ push de
+ call GetTurnDuelistVariable
+
+; get card's ID and Type
+ call GetCardIDFromDeckIndex
+ ld a, e
+ ld [wTempCardID], a
+ call LoadCardDataToBuffer1_FromCardID
+ pop de
+ ld a, [wLoadedCard1Type]
+ or TYPE_ENERGY
+
+; only do check if the Pokemon's type
+; is either Fire or Lightning
+ cp TYPE_ENERGY_FIRE
+ jr z, .fire_or_lightning
+ cp TYPE_ENERGY_LIGHTNING
+ jr nz, .next_play_area
+
+; loop each energy card in list
+.fire_or_lightning
+ ld [wTempCardType], a
+ ld hl, wDuelTempList
+.loop_energy_2
+ ld a, [hli]
+ cp $ff
+ jr z, .next_play_area
+
+; if this energy card is useful,
+; return no carry.
+ ld b, a
+ push hl
+ farcall CheckIfEnergyIsUseful
+ pop hl
+ jr nc, .loop_energy_2
+
+ ld a, b
+ or a
+ ret
+
+.next_play_area
+ inc e
+ ld a, e
+ cp d
+ jr nz, .loop_play_area_2
-.asm_201a8
+; no card was found to be useful
+; for Fire/Lightning type Pokemon card.
+ scf
+ ret
+; 0x21273
+
+; checks whether there are useful energies
+; only for Grass type Pokemon cards
+; in Play Area. If none found, return carry.
+.CheckUsefulGrassEnergy ; 21273 (8:5273)
+; unreferenced
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld d, a
+ ld e, PLAY_AREA_ARENA
+
+.loop_play_area_3
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ push de
+ call GetTurnDuelistVariable
+
+; get card's ID and Type
+ call GetCardIDFromDeckIndex
+ ld a, e
+ ld [wTempCardID], a
+ call LoadCardDataToBuffer1_FromCardID
pop de
+ ld a, [wLoadedCard1Type]
+ or TYPE_ENERGY
+
+; only do check if the Pokemon's type is Grass
+ cp TYPE_ENERGY_GRASS
+ jr nz, .next_play_area_3
+
+; loop each energy card in list
+ ld [wTempCardType], a
+ ld hl, wDuelTempList
+.loop_energy_3
+ ld a, [hli]
+ cp $ff
+ jr z, .next_play_area_3
+
+; if this energy card is useful,
+; return no carry.
+ ld b, a
+ push hl
+ farcall CheckIfEnergyIsUseful
+ pop hl
+ jr nc, .loop_energy_3
+
+ ld a, b
+ or a
+ ret
+
+.next_play_area_3
+ inc e
+ ld a, e
+ cp d
+ jr nz, .loop_play_area_3
+
+; no card was found to be useful
+; for Grass type Pokemon card.
+ scf
+ ret
+; 0x212b4
+
+AIPlay_Pokedex: ; 212b4 (8:52b4)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wce1a]
+ ldh [hTemp_ffa0], a
+ ld a, [wce1b]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, [wce1c]
+ ldh [hTempRetreatCostCards], a
+ ld a, [wce1d]
+ ldh [$ffa3], a
+ ld a, [wce1e]
+ ldh [$ffa4], a
+ ld a, $ff
+ ldh [$ffa5], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x212dc
+
+AIDecide_Pokedex: ; 212dc (8:52dc)
+ ld a, [wcda6]
+ cp $06
+ jr c, .no_carry
+
+; return no carry if number of cards in deck <= 4
+ ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
+ call GetTurnDuelistVariable
+ cp DECK_SIZE - 4
+ jr nc, .no_carry
+
+; has a 3 in 10 chance of actually playing card
+ ld a, 10
+ call Random
+ cp 3
+ jr c, .pick_cards
+
+.no_carry
+ or a
+ ret
+
+.pick_cards
+; the following comparison is disregarded
+; the Wonders of Science deck was probably intended
+; to use PickPokedexCards_Unreferenced instead
+ ld a, [wOpponentDeckID]
+ cp WONDERS_OF_SCIENCE_DECK_ID
+ jp PickPokedexCards ; bug, should be jp nz
+; 0x212ff
+
+; picks order of the cards in deck from the effects of Pokedex.
+; prioritises Pokemon cards, then Trainer cards, then energy cards.
+; stores the resulting order in wce1a.
+PickPokedexCards_Unreferenced: ; 212ff (8:52ff)
+; unreferenced
+ xor a
+ ld [wcda6], a
+
+ ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
+ call GetTurnDuelistVariable
+ add DUELVARS_DECK_CARDS
+ ld l, a
+ lb de, $00, $00
+ ld b, 5
+
+; run through 5 of the remaining cards in deck
+.next_card
+ ld a, [hli]
+ ld c, a
+ call .GetCardType
+
+; load this card's deck index and type in memory
+; wce08 = card types
+; wce0f = card deck indices
+ push hl
+ ld hl, wce08
+ add hl, de
+ ld [hl], a
+ ld hl, wce0f
+ add hl, de
+ ld [hl], c
+ pop hl
+
+ inc e
+ dec b
+ jr nz, .next_card
+
+; terminate the wce08 list
+ ld a, $ff
+ ld [wce08 + 5], a
+
+ ld de, wce1a
+
+; find Pokemon
+ ld hl, wce08
+ ld c, -1
+ ld b, $00
+
+; run through the stored cards
+; and look for any Pokemon cards.
+.loop_pokemon
+ inc c
+ ld a, [hli]
+ cp $ff
+ jr z, .find_trainers
+ cp TYPE_ENERGY
+ jr nc, .loop_pokemon
+; found a Pokemon card
+; store it in wce1a list
+ push hl
+ ld hl, wce0f
+ add hl, bc
+ ld a, [hl]
+ pop hl
+ ld [de], a
+ inc de
+ jr .loop_pokemon
+
+; run through the stored cards
+; and look for any Trainer cards.
+.find_trainers
+ ld hl, wce08
+ ld c, -1
+ ld b, $00
+
+.loop_trainers
+ inc c
+ ld a, [hli]
+ cp $ff
+ jr z, .find_energy
+ cp TYPE_TRAINER
+ jr nz, .loop_trainers
+; found a Trainer card
+; store it in wce1a list
+ push hl
+ ld hl, wce0f
+ add hl, bc
+ ld a, [hl]
+ pop hl
+ ld [de], a
+ inc de
+ jr .loop_trainers
+
+.find_energy
+ ld hl, wce08
+ ld c, -1
+ ld b, $00
+
+; run through the stored cards
+; and look for any energy cards.
+.loop_energy
+ inc c
+ ld a, [hli]
+ cp $ff
+ jr z, .done
+ and TYPE_ENERGY
+ jr z, .loop_energy
+; found an energy card
+; store it in wce1a list
+ push hl
+ ld hl, wce0f
+ add hl, bc
+ ld a, [hl]
+ pop hl
+ ld [de], a
+ inc de
+ jr .loop_energy
+
+.done
+ scf
+ ret
+; 0x21383
+
+.GetCardType ; 21383 (8:5383)
+ push bc
+ push de
+ call GetCardIDFromDeckIndex
+ call GetCardType
+ pop de
+ pop bc
+ ret
+; 0x2138e
+
+; picks order of the cards in deck from the effects of Pokedex.
+; prioritises energy cards, then Pokemon cards, then Trainer cards.
+; stores the resulting order in wce1a.
+PickPokedexCards: ; 2138e (8:538e)
+ xor a
+ ld [wcda6], a
+
+ ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
+ call GetTurnDuelistVariable
+ add DUELVARS_DECK_CARDS
+ ld l, a
+ lb de, $00, $00
+ ld b, 5
+
+; run through 5 of the remaining cards in deck
+.next_card
+ ld a, [hli]
+ ld c, a
+ call .GetCardType
+
+; load this card's deck index and type in memory
+; wce08 = card types
+; wce0f = card deck indices
+ push hl
+ ld hl, wce08
+ add hl, de
+ ld [hl], a
+ ld hl, wce0f
+ add hl, de
+ ld [hl], c
+ pop hl
+
+ inc e
+ dec b
+ jr nz, .next_card
+
+; terminate the wce08 list
+ ld a, $ff
+ ld [wce08 + 5], a
+
+ ld de, wce1a
+
+; find energy
+ ld hl, wce08
+ ld c, -1
+ ld b, $00
+
+; run through the stored cards
+; and look for any energy cards.
+.loop_energy
+ inc c
+ ld a, [hli]
+ cp $ff
+ jr z, .find_pokemon
+ and TYPE_ENERGY
+ jr z, .loop_energy
+; found an energy card
+; store it in wce1a list
+ push hl
+ ld hl, wce0f
+ add hl, bc
+ ld a, [hl]
+ pop hl
+ ld [de], a
+ inc de
+ jr .loop_energy
+
+.find_pokemon
+ ld hl, wce08
+ ld c, -1
+ ld b, $00
+
+; run through the stored cards
+; and look for any Pokemon cards.
+.loop_pokemon
+ inc c
+ ld a, [hli]
+ cp $ff
+ jr z, .find_trainers
+ cp TYPE_ENERGY
+ jr nc, .loop_pokemon
+; found a Pokemon card
+; store it in wce1a list
+ push hl
+ ld hl, wce0f
+ add hl, bc
+ ld a, [hl]
+ pop hl
+ ld [de], a
+ inc de
+ jr .loop_pokemon
+
+; run through the stored cards
+; and look for any Trainer cards.
+.find_trainers
+ ld hl, wce08
+ ld c, -1
+ ld b, $00
+
+.loop_trainers
+ inc c
+ ld a, [hli]
+ cp $ff
+ jr z, .done
+ cp TYPE_TRAINER
+ jr nz, .loop_trainers
+; found a Trainer card
+; store it in wce1a list
+ push hl
+ ld hl, wce0f
+ add hl, bc
+ ld a, [hl]
+ pop hl
+ ld [de], a
+ inc de
+ jr .loop_trainers
+
+.done
+ scf
+ ret
+; 0x21412
+
+.GetCardType ; 21412 (8:5412)
+ push bc
+ push de
+ call GetCardIDFromDeckIndex
+ call GetCardType
+ pop de
+ pop bc
+ ret
+; 0x2141d
+
+AIPlay_FullHeal: ; 2141d (8:541d)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x21428
+
+AIDecide_FullHeal: ; 21428 (8:5428)
+ ld a, DUELVARS_ARENA_CARD_STATUS
+ call GetTurnDuelistVariable
+
+; skip if no status on arena card
+ or a ; NO_STATUS
+ jr z, .no_carry
+
+ and CNF_SLP_PRZ
+ cp PARALYZED
+ jr z, .paralyzed
+ cp ASLEEP
+ jr z, .asleep
+ cp CONFUSED
+ jr z, .confused
+ ; if either PSN or DBLPSN, fallthrough
+
+.set_carry
+ scf
+ ret
+
+.asleep
+; set carry if any of the following
+; cards are in the Play Area.
+ ld a, GASTLY1
+ ld b, PLAY_AREA_ARENA
+ call LookForCardIDInPlayArea_Bank8
+ jr c, .set_carry
+ ld a, GASTLY2
+ ld b, PLAY_AREA_ARENA
+ call LookForCardIDInPlayArea_Bank8
+ jr c, .set_carry
+ ld a, HAUNTER2
+ ld b, PLAY_AREA_ARENA
+ call LookForCardIDInPlayArea_Bank8
+ jr c, .set_carry
+
+; otherwise fallthrough
+
+.paralyzed
+; if Scoop Up is in hand and decided to be played, skip.
+ ld a, SCOOP_UP
+ call LookForCardIDInHandList_Bank8
+ jr nc, .no_scoop_up_prz
+ call AIDecide_ScoopUp
+ jr c, .no_carry
+
+.no_scoop_up_prz
+; if card can damage defending Pokemon...
+ xor a ; PLAY_AREA_ARENA
+ farcall CheckIfCanDamageDefendingPokemon
+ jr nc, .no_carry
+; ...and can play an energy card to retreat, set carry.
+ ld a, [wAIPlayEnergyCardForRetreat]
+ or a
+ jr nz, .set_carry
+
+; if not, check whether it's a card it would rather retreat,
+; and if it isn't, set carry.
+ farcall AIDecideWhetherToRetreat
+ jr nc, .set_carry
+
+.no_carry
+ or a
+ ret
+
+.confused
+; if Scoop Up is in hand and decided to be played, skip.
+ ld a, SCOOP_UP
+ call LookForCardIDInHandList_Bank8
+ jr nc, .no_scoop_up_cnf
+ call AIDecide_ScoopUp
+ jr c, .no_carry
+
+.no_scoop_up_cnf
+; if card can damage defending Pokemon...
+ xor a ; PLAY_AREA_ARENA
+ farcall CheckIfCanDamageDefendingPokemon
+ jr nc, .no_carry
+; ...and can play an energy card to retreat, set carry.
+ ld a, [wAIPlayEnergyCardForRetreat]
+ or a
+ jr nz, .set_carry
+; if not, return no carry.
+ jr .no_carry
+; 0x21497
+
+AIPlay_MrFuji: ; 21497 (8:5497)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x214a7
+
+; AI logic for playing Mr Fuji
+AIDecide_MrFuji: ; 214a7 (8:54a7)
+ ld a, $ff
+ ld [wce06], a
+ ld [wce08], a
+
+; if just one Pokemon in Play Area, skip.
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp 1
+ ret z
+
+ dec a
+ ld d, a
+ ld e, PLAY_AREA_BENCH_1
+
+; find a Pokemon in the bench that has damage counters.
+.loop_bench
+ ld a, DUELVARS_ARENA_CARD
+ add e
+ push de
+ call GetTurnDuelistVariable
+ call LoadCardDataToBuffer1_FromDeckIndex
+ pop de
+
+ ld a, [wLoadedCard1HP]
+ ld b, a
+
+ ; skip if zero damage counters
+ call GetCardDamage
+ call ConvertHPToCounters
+ or a
+ jr z, .next
+
+; a = damage counters
+; b = hp left
+ call CalculateBDividedByA_Bank8
+ cp 20
+ jr nc, .next
+
+; here, HP left in counters is less than twice
+; the number of damage counters, that is:
+; HP < 1/3 max HP
+
+; if value is less than the one found before, store this one.
+ ld hl, wce08
+ cp [hl]
+ jr nc, .next
+ ld [hl], a
+ ld a, e
+ ld [wce06], a
+.next
+ inc e
+ dec d
+ jr nz, .loop_bench
+
+ ld a, [wce06]
+ cp $ff
+ ret z
+
+ scf
+ ret
+; 0x214f1
+
+AIPlay_ScoopUp: ; 214f1 (8:54f1)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld a, [wce1a]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x21506
+
+AIDecide_ScoopUp: ; 21506 (8:5506)
+ xor a
+ ldh [hTempPlayAreaLocation_ff9d], a
+
+; if only one Pokemon in Play Area, skip.
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp 2
+ jr c, .no_carry
+
+; handle some decks differently
+ ld a, [wOpponentDeckID]
+ cp LEGENDARY_ARTICUNO_DECK_ID
+ jr z, .HandleLegendaryArticuno
+ cp LEGENDARY_RONALD_DECK_ID
+ jp z, .HandleLegendaryRonald
+
+; if can't KO defending Pokemon, check if defending Pokemon
+; can KO this card. If so, then continue.
+; If not, return no carry.
+
+; if it can KO the defending Pokemon this turn,
+; return no carry.
+ farcall CheckIfAnyMoveKnocksOutDefendingCard
+ jr nc, .cannot_ko
+ farcall CheckIfSelectedMoveIsUnusable
+ jr nc, .no_carry
+ farcall LookForEnergyNeededForMoveInHand
+ jr c, .no_carry
+
+.cannot_ko
+ ld a, DUELVARS_ARENA_CARD_STATUS
+ call GetTurnDuelistVariable
+ and CNF_SLP_PRZ
+ cp PARALYZED
+ jr z, .cannot_retreat
+ cp ASLEEP
+ jr z, .cannot_retreat
+
+; doesn't have a status that prevents retreat.
+; so check if it has enough energy to retreat.
+; if not, return no carry.
+ xor a
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call GetPlayAreaCardRetreatCost
+ ld b, a
+ ld e, PLAY_AREA_ARENA
+ farcall CountNumberOfEnergyCardsAttached
+ cp b
+ jr c, .cannot_retreat
+
+.no_carry
+ or a
+ ret
+
+.cannot_retreat
+; store damage and total HP left
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call LoadCardDataToBuffer1_FromDeckIndex
+ ld a, [wLoadedCard1HP]
+ call ConvertHPToCounters
+ ld d, a
+
+; skip if card has no damage counters.
+ ld e, PLAY_AREA_ARENA
+ call GetCardDamage
+ or a
+ jr z, .no_carry
+
+; if (total damage / total HP counters) < 7
+; return carry.
+; (this corresponds to damage counters
+; being under 70% of the max HP)
+ ld b, a
+ ld a, d
+ call CalculateBDividedByA_Bank8
+ cp 7
+ jr c, .no_carry
+
+; store Pokemon to switch to in wce1a and set carry.
+.decide_switch
+ farcall AIDecideBenchPokemonToSwitchTo
+ jr c, .no_carry
+ ld [wce1a], a
+ xor a
+ scf
+ ret
+
+; this deck will use Scoop Up on a benched Articuno2.
+; it checks if the defending Pokemon is a Snorlax,
+; but interestingly does not check for Muk in both Play Areas.
+; will also use Scoop Up on
+.HandleLegendaryArticuno
+; if less than 3 Play Area Pokemon cards, skip.
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp 3
+ jr c, .no_carry
+
+; look for Articuno2 in bench
+ ld a, ARTICUNO2
+ ld b, PLAY_AREA_BENCH_1
+ call LookForCardIDInPlayArea_Bank8
+ jr c, .articuno_bench
+
+; check Arena card
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ cp ARTICUNO2
+ jr z, .articuno_or_chansey
+ cp CHANSEY
+ jr nz, .no_carry
+
+; here either Articuno2 or Chansey
+; is the Arena Card.
+.articuno_or_chansey
+; if can't KO defending Pokemon, check if defending Pokemon
+; can KO this card. If so, then continue.
+; If not, return no carry.
+
+; if it can KO the defending Pokemon this turn,
+; return no carry.
+ farcall CheckIfAnyMoveKnocksOutDefendingCard
+ jr nc, .check_ko
+ farcall CheckIfSelectedMoveIsUnusable
+ jr nc, .no_carry
+ farcall LookForEnergyNeededForMoveInHand
+ jr c, .no_carry
+.check_ko
+ farcall CheckIfDefendingPokemonCanKnockOut
+ jr nc, .no_carry
+ jr .decide_switch
+
+.articuno_bench
+; skip if the defending card is Snorlax
+ push af
+ ld a, DUELVARS_ARENA_CARD
+ call GetNonTurnDuelistVariable
+ call SwapTurn
+ call GetCardIDFromDeckIndex
+ call SwapTurn
+ ld a, e
+ cp SNORLAX
+ pop bc
+ jr z, .no_carry
+
+; check attached energy cards.
+; if it has any, return no carry.
+ ld a, b
+.check_attached_energy
+ ld e, a
+ push af
+ farcall CountNumberOfEnergyCardsAttached
+ or a
+ pop bc
+ ld a, b
+ jr z, .no_energy
+ jp .no_carry
+
+.no_energy
+; has decided to Scoop Up benched card,
+; store $ff as the Pokemon card to switch to
+; because there's no need to switch.
+ push af
+ ld a, $ff
+ ld [wce1a], a
+ pop af
+ scf
+ ret
+; 0x215e7
+
+; this deck will use Scoop Up on a benched Articuno2, Zapdos3 or Molres2.
+; interestingly, does not check for Muk in both Play Areas.
+.HandleLegendaryRonald ; 215e7 (8:55e7)
+; if less than 3 Play Area Pokemon cards, skip.
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp 3
+ jp c, .no_carry
+
+ ld a, ARTICUNO2
+ ld b, PLAY_AREA_BENCH_1
+ call LookForCardIDInPlayArea_Bank8
+ jr c, .articuno_bench
+ ld a, ZAPDOS3
+ ld b, PLAY_AREA_BENCH_1
+ call LookForCardIDInPlayArea_Bank8
+ jr c, .check_attached_energy
+ ld a, MOLTRES2
+ ld b, PLAY_AREA_BENCH_1
+ call LookForCardIDInPlayArea_Bank8
+ jr c, .check_attached_energy
+ jp .no_carry
+; 0x2160f
+
+AIPlay_Maintenance: ; 2160f (8:560f)
+ ld a, [wCurrentAIFlags]
+ or AI_FLAG_MODIFIED_HAND
+ ld [wCurrentAIFlags], a
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wce1a]
+ ldh [hTemp_ffa0], a
+ ld a, [wce1b]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x2162c
+
+; AI logic for playing Maintenance
+AIDecide_Maintenance: ; 2162c (8:562c)
+; Imakuni? has his own thing
+ ld a, [wOpponentDeckID]
+ cp IMAKUNI_DECK_ID
+ jr z, .imakuni
+
+; skip if number of cars in hand < 4.
+ ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND
+ call GetTurnDuelistVariable
+ cp 4
+ jr c, .no_carry
+
+; list out all the hand cards and remove
+; wAITrainerCardToPlay from list.Then find any duplicate cards.
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ ld a, [wAITrainerCardToPlay]
+ call FindAndRemoveCardFromList
+; if duplicates are not found, return no carry.
+ call FindDuplicateCards
+ jp c, .no_carry
+
+; store the first duplicate card and remove it from the list.
+; run duplicate check again.
+ ld [wce1a], a
+ ld hl, wDuelTempList
+ call FindAndRemoveCardFromList
+; if duplicates are not found, return no carry.
+ call FindDuplicateCards
+ jp c, .no_carry
+
+; store the second duplicate card and return carry.
+ ld [wce1b], a
+ scf
+ ret
+
+.no_carry
+ or a
+ ret
+
+.imakuni
+; has a 2 in 10 chance of not skipping.
+ ld a, 10
+ call Random
+ cp 2
+ jr nc, .no_carry
+
+; skip if number of cards in hand < 3.
+ ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND
+ call GetTurnDuelistVariable
+ cp 3
+ jr c, .no_carry
+
+; shuffle hand cards
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ call CountCardsInDuelTempList
+ call ShuffleCards
+
+; go through each card and find
+; cards that are different from wAITrainerCardToPlay.
+; if found, add those cards to wce1a and wce1a+1.
+ ld a, [wAITrainerCardToPlay]
+ ld b, a
+ ld c, 2
+ ld de, wce1a
+
+.loop
+ ld a, [hli]
+ cp $ff
+ jr z, .no_carry
+ cp b
+ jr z, .loop
+ ld [de], a
+ inc de
+ dec c
+ jr nz, .loop
+
+; two cards were found, return carry.
+ scf
+ ret
+; 0x2169a
+
+AIPlay_Recycle: ; 2169a (8:569a)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ldtx de, TrainerCardSuccessCheckText
+ bank1call TossCoin
+ jr nc, .asm_216ae
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ jr .asm_216b2
+.asm_216ae
+ ld a, $ff
+ ldh [hTemp_ffa0], a
+.asm_216b2
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x216b8
+
+; lists cards to look for in the Discard Pile.
+; has priorities for Ghost Deck, and a "default" priority list
+; (which is the Fire Charge deck, since it's the only other
+; deck that runs a Recycle card in it.)
+AIDecide_Recycle: ; 216b8 (8:56b8)
+; no use checking if no cards in Discard Pile
+ call CreateDiscardPileCardList
+ jr c, .no_carry
+
+ ld a, $ff
+ ld [wce08], a
+ ld [wce08 + 1], a
+ ld [wce08 + 2], a
+ ld [wce08 + 3], a
+ ld [wce08 + 4], a
+
+; handle Ghost deck differently
+ ld hl, wDuelTempList
+ ld a, [wOpponentDeckID]
+ cp GHOST_DECK_ID
+ jr z, .loop_2
+
+; priority list for Fire Charge deck
+.loop_1
+ ld a, [hli]
+ cp $ff
+ jr z, .done
+
+ ld b, a
+ call LoadCardDataToBuffer1_FromDeckIndex
+
+; double colorless
+ cp DOUBLE_COLORLESS_ENERGY
+ jr nz, .chansey
+ ld a, b
+ ld [wce08], a
+ jr .loop_1
+
+.chansey
+ cp CHANSEY
+ jr nz, .tauros
+ ld a, b
+ ld [wce08 + 1], a
+ jr .loop_1
+
+.tauros
+ cp TAUROS
+ jr nz, .jigglypuff
+ ld a, b
+ ld [wce08 + 2], a
+ jr .loop_1
+
+.jigglypuff
+ cp JIGGLYPUFF1
+ jr nz, .loop_1
+ ld a, b
+ ld [wce08 + 3], a
+ jr .loop_1
+
+; loop through wce08 and set carry
+; on the first that was found in Discard Pile.
+; if none were found, return no carry.
+.done
+ ld hl, wce08
+ ld b, 5
+.loop_found
+ ld a, [hli]
+ cp $ff
+ jr nz, .set_carry
+ dec b
+ jr nz, .loop_found
+.no_carry
+ or a
+ ret
+.set_carry
+ scf
+ ret
+
+; priority list for Ghost deck
+.loop_2
+ ld a, [hli]
+ cp $ff
+ jr z, .done
+
+ ld b, a
+ call LoadCardDataToBuffer1_FromDeckIndex
+
+; gastly2
+ cp GASTLY2
+ jr nz, .gastly1
+ ld a, b
+ ld [wce08], a
+ jr .loop_2
+
+.gastly1
+ cp GASTLY1
+ jr nz, .zubat
+ ld a, b
+ ld [wce08 + 1], a
+ jr .loop_2
+
+.zubat
+ cp ZUBAT
+ jr nz, .ditto
+ ld a, b
+ ld [wce08 + 2], a
+ jr .loop_2
+
+.ditto
+ cp DITTO
+ jr nz, .meowth
+ ld a, b
+ ld [wce08 + 3], a
+ jr .loop_2
+
+.meowth
+ cp MEOWTH2
+ jr nz, .loop_2
+ ld a, b
+ ld [wce08 + 4], a
+ jr .loop_2
+; 0x21755
+
+AIPlay_Lass: ; 21755 (8:5755)
+ ld a, [wCurrentAIFlags]
+ or AI_FLAG_MODIFIED_HAND
+ ld [wCurrentAIFlags], a
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x21768
+
+AIDecide_Lass: ; 21768 (8:5768)
+; skip if player has less than 7 cards in hand
+ ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND
+ call GetNonTurnDuelistVariable
+ cp 7
+ jr c, .no_carry
+
+; look for Trainer cards in hand (except for Lass)
+; if any is found, return no carry.
+; otherwise, return carry.
+ call CreateHandCardList
+ ld hl, wDuelTempList
+.loop
+ ld a, [hli]
+ cp $ff
+ jr z, .set_carry
+ ld b, a
+ call LoadCardDataToBuffer1_FromDeckIndex
+ cp LASS
+ jr z, .loop
+ ld a, [wLoadedCard1Type]
+ cp TYPE_TRAINER
+ jr nz, .loop
+.no_carry
+ or a
+ ret
+.set_carry
+ scf
+ ret
+; 0x2178f
+
+AIPlay_ItemFinder: ; 2178f (8:578f)
+ ld a, [wCurrentAIFlags]
+ or AI_FLAG_MODIFIED_HAND
+ ld [wCurrentAIFlags], a
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wce1a]
+ ldh [hTemp_ffa0], a
+ ld a, [wce1b]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTempRetreatCostCards], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x217b1
+
+; checks whether there's Energy Removal in Discard Pile.
+; if so, find duplicate cards in hand to discard
+; that are not Mr Mime and Pokemon Trader cards.
+; this logic is suitable only for Strange Psyshock deck.
+AIDecide_ItemFinder: ; 217b1 (8:57b1)
+; skip if no Discard Pile.
+ call CreateDiscardPileCardList
+ jr c, .no_carry
+
+; look for Energy Removal in Discard Pile
+ ld hl, wDuelTempList
+.loop_discard_pile
+ ld a, [hli]
+ cp $ff
+ jr z, .no_carry
+ ld b, a
+ call LoadCardDataToBuffer1_FromDeckIndex
+ cp ENERGY_REMOVAL
+ jr nz, .loop_discard_pile
+; found, store this deck index
+ ld a, b
+ ld [wce06], a
+
+; before looking for cards to discard in hand,
+; remove any Mr Mime and Pokemon Trader cards.
+; this way these are guaranteed to not be discarded.
+ call CreateHandCardList
+ ld hl, wDuelTempList
+.loop_hand
+ ld a, [hli]
+ cp $ff
+ jr z, .choose_discard
+ ld b, a
+ call LoadCardDataToBuffer1_FromDeckIndex
+ cp MR_MIME
+ jr nz, .pkmn_trader
+ call RemoveCardFromList
+ jr .loop_hand
+.pkmn_trader
+ cp POKEMON_TRADER
+ jr nz, .loop_hand
+ call RemoveCardFromList
+ jr .loop_hand
+
+; choose cards to discard from hand.
+.choose_discard
+ ld hl, wDuelTempList
+
+; do not discard wAITrainerCardToPlay
+ ld a, [wAITrainerCardToPlay]
+ call FindAndRemoveCardFromList
+; find any duplicates, if not found, return no carry.
+ call FindDuplicateCards
+ jp c, .no_carry
+
+; store the duplicate found in wce1a and
+; remove it from the hand list.
+ ld [wce1a], a
+ ld hl, wDuelTempList
+ call FindAndRemoveCardFromList
+; find duplicates again, if not found, return no carry.
+ call FindDuplicateCards
+ jp c, .no_carry
+
+; store the duplicate found in wce1b.
+; output the card to be recovered from the Discard Pile.
+ ld [wce1b], a
+ ld a, [wce06]
+ scf
+ ret
+
+.no_carry
+ or a
+ ret
+; 0x21813
+
+AIPlay_Imakuni: ; 21813 (8:5813)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x2181e
+
+; only sets carry if Active card is not confused.
+AIDecide_Imakuni: ; 2181e (8:581e)
+ ld a, DUELVARS_ARENA_CARD_STATUS
+ call GetTurnDuelistVariable
+ and CNF_SLP_PRZ
+ cp CONFUSED
+ jr z, .confused
+ scf
+ ret
+.confused
+ or a
+ ret
+; 0x2182d
+
+AIPlay_Gambler: ; 2182d (8:582d)
+ ld a, [wCurrentAIFlags]
+ or AI_FLAG_MODIFIED_HAND
+ ld [wCurrentAIFlags], a
+ ld a, [wOpponentDeckID]
+ cp IMAKUNI_DECK_ID
+ jr z, .asm_2186a
+ ld hl, wRNG1
+ ld a, [hli]
+ ld [wce06], a
+ ld a, [hli]
+ ld [wce08], a
+ ld a, [hl]
+ ld [wce0f], a
+ ld a, $50
+ ld [hld], a
+ ld [hld], a
+ ld [hl], a
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ld hl, wRNG1
+ ld a, [wce06]
+ ld [hli], a
+ ld a, [wce08]
+ ld [hli], a
+ ld a, [wce0f]
+ ld [hl], a
+ ret
+.asm_2186a
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x21875
+
+; checks whether to play Gambler.
+; aside from Imakuni, all other opponents only
+; play if there's less than 4 cards in the 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
+ jr z, .no_carry
+
+; set carry if number of cards in deck <= 4
+ ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
+ call GetTurnDuelistVariable
+ cp DECK_SIZE - 4
+ jr nc, .set_carry
+.no_carry
+ or a
+ ret
+
+.imakuni
+; has a 2 in 10 chance of returning carry
+ ld a, 10
+ call Random
+ cp 2
+ jr nc, .no_carry
+.set_carry
+ scf
+ ret
+; 0x21899
+
+AIPlay_Revive: ; 21899 (8:5899)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x218a9
+
+; checks certain cards in Discard Pile to use Revive on.
+; suitable for Muscle For Brains deck only.
+AIDecide_Revive: ; 218a9 (8:58a9)
+; skip if no cards in Discard Pile
+ call CreateDiscardPileCardList
+ jr c, .no_carry
+
+; skip if number of Pokemon cards in Play Area >= 4
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp 4
+ jr nc, .no_carry
+
+; look in Discard Pile for specific cards.
+ ld hl, wDuelTempList
+.loop_discard_pile
+ ld a, [hli]
+ cp $ff
+ jr z, .no_carry
+ ld b, a
+ call LoadCardDataToBuffer1_FromDeckIndex
+
+; these checks have a bug.
+; it works fine for Hitmonchan and Hitmonlee,
+; but in case it's a Tauros card, the routine will fallthrough
+; into the Kangaskhan check. since it will never be equal to Kangaskhan,
+; it will fallthrough into the set carry branch.
+; in case it's a Kangaskhan card, the check will fail in the Tauros check
+; and jump back into the loop. so just by accident the Tauros check works,
+; but Kangaskhan will never be correctly checked because of this.
+ cp HITMONCHAN
+ jr z, .set_carry
+ cp HITMONLEE
+ jr z, .set_carry
+ cp TAUROS
+ jr nz, .loop_discard_pile ; bug, these two lines should be swapped
+ cp KANGASKHAN
+ jr z, .set_carry ; bug, these two lines should be swapped
+
+.set_carry
+ ld a, b
+ scf
+ ret
+.no_carry
+ or a
+ ret
+; 0x218d8
+
+AIPlay_PokemonFlute: ; 218d8 (8:58d8)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x218e8
+
+AIDecide_PokemonFlute: ; 218e8 (8:58e8)
+; if player has no Discard Pile, skip.
+ call SwapTurn
+ call CreateDiscardPileCardList
+ call SwapTurn
+ jr c, .no_carry
+
+; if player's Play Area is already full, skip.
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetNonTurnDuelistVariable
+ cp MAX_PLAY_AREA_POKEMON
+ jr nc, .no_carry
+
+ ld a, [wOpponentDeckID]
+ cp IMAKUNI_DECK_ID
+ jr z, .imakuni
+
+ ld a, $ff
+ ld [wce06], a
+ ld [wce08], a
+
+; find Basic stage Pokemon with lowest HP in Discard Pile
+ ld hl, wDuelTempList
+.loop_1
+ ld a, [hli]
+ cp $ff
+ jr z, .done
+
+ ld b, a
+ call SwapTurn
+ call LoadCardDataToBuffer1_FromDeckIndex
+ call SwapTurn
+; skip this card if it's not Pokemon card
+ ld a, [wLoadedCard1Type]
+ cp TYPE_ENERGY
+ jr nc, .loop_1
+; skip this card if it's not Basic Stage
+ ld a, [wLoadedCard1Stage]
+ or a ; BASIC
+ jr nz, .loop_1
+
+; compare this HP with one stored
+ ld a, [wLoadedCard1HP]
+ push hl
+ ld hl, wce06
+ cp [hl]
pop hl
+ jr nc, .loop_1
+; if lower, store this one
+ ld [wce06], a
+ ld a, b
+ ld [wce08], a
+ jr .loop_1
+
+.done
+; if lowest HP found >= 50, return no carry
+ ld a, [wce06]
+ cp 50
+ jr nc, .no_carry
+; otherwise output its deck index in a and set carry.
+ ld a, [wce08]
+ scf
+ ret
+.no_carry
+ or a
+ ret
+
+.imakuni
+; has 2 in 10 chance of not skipping
+ ld a, 10
+ call Random
+ cp 2
+ jr nc, .no_carry
+
+; look for any Basic Pokemon card
+ ld hl, wDuelTempList
+.loop_2
+ ld a, [hli]
+ cp $ff
+ jr z, .no_carry
+ ld b, a
+ call SwapTurn
+ call LoadCardDataToBuffer1_FromDeckIndex
+ call SwapTurn
+ ld a, [wLoadedCard1Type]
+ cp TYPE_ENERGY
+ jr nc, .loop_2
+ ld a, [wLoadedCard1Stage]
+ or a ; BASIC
+ jr nz, .loop_2
+
+; a Basic stage Pokemon was found, return carry
+ ld a, b
+ scf
+ ret
+; 0x21977
+
+AIPlay_ClefairyDollOrMysteriousFossil: ; 21977 (8:5977)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x21982
+
+; AI logic for playing Clefairy Doll
+AIDecide_ClefairyDollOrMysteriousFossil: ; 21982 (8:5982)
+; if has max number of Play Area Pokemon, skip
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ cp MAX_PLAY_AREA_POKEMON
+ jr nc, .no_carry
+
+; store number of Play Area Pokemon cards
+ ld [wce06], a
+
+; if the Arena card is Wigglytuff, return carry
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ cp WIGGLYTUFF
+ jr z, .set_carry
+
+; if number of Play Area Pokemon >= 4, return no carry
+ ld a, [wce06]
+ cp 4
+ jr nc, .no_carry
+
+.set_carry
+ scf
+ ret
+.no_carry
+ or a
+ ret
+; 0x219a6
+
+AIPlay_Pokeball: ; 219a6 (8:59a6)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ldtx de, TrainerCardSuccessCheckText
+ bank1call TossCoin
+ ldh [hTemp_ffa0], a
+ jr nc, .asm_219bc
+ ld a, [wAITrainerCardParameter]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ jr .asm_219c0
+.asm_219bc
+ ld a, $ff
+ ldh [hTempPlayAreaLocation_ffa1], a
+.asm_219c0
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x219c6
+
+AIDecide_Pokeball: ; 219c6 (8:59c6)
+; go to the routines associated with deck ID
+ ld a, [wOpponentDeckID]
+ cp FIRE_CHARGE_DECK_ID
+ jr z, .fire_charge
+ cp HARD_POKEMON_DECK_ID
+ jr z, .hard_pokemon
+ cp PIKACHU_DECK_ID
+ jr z, .pikachu
+ cp ETCETERA_DECK_ID
+ jr z, .etcetera
+ cp LOVELY_NIDORAN_DECK_ID
+ jp z, .lovely_nidoran
+ or a
+ ret
+
+; this deck runs a deck check for specific
+; card IDs in order of decreasing priority
+.fire_charge
+ ld e, CHANSEY
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ld e, TAUROS
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ld e, JIGGLYPUFF1
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ret
+
+; this deck runs a deck check for specific
+; card IDs in order of decreasing priority
+.hard_pokemon
+ ld e, RHYHORN
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ld e, RHYDON
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ld e, ONIX
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ret
+
+; this deck runs a deck check for specific
+; card IDs in order of decreasing priority
+.pikachu
+ ld e, PIKACHU2
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ld e, PIKACHU3
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ld e, PIKACHU4
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ld e, PIKACHU1
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ld e, FLYING_PIKACHU
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ret
+
+; this deck runs a deck check for specific
+; card IDs in order of decreasing priority
+; given a specific energy card in hand.
+; also it avoids redundancy, so if it already
+; has that card ID in the hand, it is skipped.
+.etcetera
+; fire
+ ld a, FIRE_ENERGY
+ call LookForCardIDInHandList_Bank8
+ jr nc, .lightning
+ ld a, CHARMANDER
+ call LookForCardIDInHandList_Bank8
+ jr c, .lightning
+ ld a, MAGMAR2
+ call LookForCardIDInHandList_Bank8
+ jr c, .lightning
+ ld e, CHARMANDER
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ld e, MAGMAR2
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+
+.lightning
+ ld a, LIGHTNING_ENERGY
+ call LookForCardIDInHandList_Bank8
+ jr nc, .fighting
+ ld a, PIKACHU1
+ call LookForCardIDInHandList_Bank8
+ jr c, .fighting
+ ld a, MAGNEMITE1
+ call LookForCardIDInHandList_Bank8
+ jr c, .fighting
+ ld e, PIKACHU1
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ld e, MAGNEMITE1
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+
+.fighting
+ ld a, FIGHTING_ENERGY
+ call LookForCardIDInHandList_Bank8
+ jr nc, .psychic
+ ld a, DIGLETT
+ call LookForCardIDInHandList_Bank8
+ jr c, .psychic
+ ld a, MACHOP
+ call LookForCardIDInHandList_Bank8
+ jr c, .psychic
+ ld e, DIGLETT
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ld e, MACHOP
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+
+.psychic
+ ld a, PSYCHIC_ENERGY
+ call LookForCardIDInHandList_Bank8
+ jr nc, .done_etcetera
+ ld a, GASTLY1
+ call LookForCardIDInHandList_Bank8
+ jr c, .done_etcetera
+ ld a, JYNX
+ call LookForCardIDInHandList_Bank8
+ jr c, .done_etcetera
+ ld e, GASTLY1
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+ ld e, JYNX
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret c
+.done_etcetera
+ or a
+ ret
+
+; this deck looks for card evolutions if
+; its pre-evolution is in hand or in Play Area.
+; if none of these are found, it looks for pre-evolutions
+; of cards it has in hand.
+; it does this for both the NidoranM (first)
+; and NidoranF (second) families.
+.lovely_nidoran
+ ld b, NIDORANM
+ ld a, NIDORINO
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ ret c
+ ld b, NIDORINO
+ ld a, NIDOKING
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ ret c
+ ld a, NIDORANM
+ ld b, NIDORINO
+ call LookForCardIDInDeck_GivenCardIDInHand
+ ret c
+ ld a, NIDORINO
+ ld b, NIDOKING
+ call LookForCardIDInDeck_GivenCardIDInHand
+ ret c
+ ld b, NIDORANF
+ ld a, NIDORINA
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ ret c
+ ld b, NIDORINA
+ ld a, NIDOQUEEN
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ ret c
+ ld a, NIDORANF
+ ld b, NIDORINA
+ call LookForCardIDInDeck_GivenCardIDInHand
+ ret c
+ ld a, NIDORINA
+ ld b, NIDOQUEEN
+ call LookForCardIDInDeck_GivenCardIDInHand
+ ret c
+ ret
+; 0x21b12
+
+AIPlay_ComputerSearch: ; 21b12 (8:5b12)
+ ld a, [wCurrentAIFlags]
+ or AI_FLAG_MODIFIED_HAND
+ ld [wCurrentAIFlags], a
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTempRetreatCostCards], a
+ ld a, [wce1a]
+ ldh [hTemp_ffa0], a
+ ld a, [wce1b]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x21b34
+
+; checks what Deck ID AI is playing and handle
+; them in their own routine.
+AIDecide_ComputerSearch: ; 21b34 (8:5b34)
+; skip if number of cards in hand < 3
+ ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND
+ call GetTurnDuelistVariable
+ cp 3
+ jr c, .no_carry
+
+ ld a, [wOpponentDeckID]
+ cp ROCK_CRUSHER_DECK_ID
+ jr z, AIDecide_ComputerSearch_RockCrusher
+ cp WONDERS_OF_SCIENCE_DECK_ID
+ jp z, AIDecide_ComputerSearch_WondersOfScience
+ cp FIRE_CHARGE_DECK_ID
+ jp z, AIDecide_ComputerSearch_FireCharge
+ cp ANGER_DECK_ID
+ jp z, AIDecide_ComputerSearch_Anger
+
+.no_carry
+ or a
+ ret
+
+AIDecide_ComputerSearch_RockCrusher: ; 21b55 (8:5b55)
+; if number of cards in hand is equal to 3,
+; target Professor Oak in deck
+ ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND
+ call GetTurnDuelistVariable
+ cp 3
+ jr nz, .graveler
+
+ ld e, PROFESSOR_OAK
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jr c, .find_discard_cards_1
+ ; no Professor Oak in deck, fallthrough
+
+.no_carry
+ or a
+ ret
+
+.find_discard_cards_1
+ ld [wce06], a
+ ld a, $ff
+ ld [wce1a], a
+ ld [wce1b], a
+
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ ld de, wce1a
+.loop_hand_1
+ ld a, [hli]
+ cp $ff
+ jr z, .check_discard_cards
+
+ ld c, a
+ call LoadCardDataToBuffer1_FromDeckIndex
+
+; if any of the following cards are in the hand,
+; return no carry.
+ cp PROFESSOR_OAK
+ jr z, .no_carry
+ cp FIGHTING_ENERGY
+ jr z, .no_carry
+ cp DOUBLE_COLORLESS_ENERGY
+ jr z, .no_carry
+ cp DIGLETT
+ jr z, .no_carry
+ cp GEODUDE
+ jr z, .no_carry
+ cp ONIX
+ jr z, .no_carry
+ cp RHYHORN
+ jr z, .no_carry
+
+; if it's same as wAITrainerCardToPlay, skip this card.
+ ld a, [wAITrainerCardToPlay]
+ ld b, a
+ ld a, c
+ cp b
+ jr z, .loop_hand_1
+
+; store this card index in memory
+ ld [de], a
+ inc de
+ jr .loop_hand_1
+
+.check_discard_cards
+; check if two cards were found
+; if so, output in a the deck index
+; of Professor Oak card found in deck and set carry.
+ ld a, [wce1b]
+ cp $ff
+ jr z, .no_carry
+ ld a, [wce06]
+ scf
+ ret
+
+; more than 3 cards in hand, so look for
+; specific evolution cards.
+
+; checks if there is a Graveler card in the deck to target.
+; if so, check if there's Geodude in hand or Play Area,
+; and if there's no Graveler card in hand, proceed.
+; also removes Geodude from hand list so that it is not discarded.
+.graveler
+ ld e, GRAVELER
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jr nc, .golem
+ ld [wce06], a
+ ld a, GEODUDE
+ call LookForCardIDInHandAndPlayArea
+ jr nc, .golem
+ ld a, GRAVELER
+ call LookForCardIDInHandList_Bank8
+ jr c, .golem
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ ld e, GEODUDE
+ farcall RemoveCardIDInList
+ jr .find_discard_cards_2
+
+; checks if there is a Golem card in the deck to target.
+; if so, check if there's Graveler in Play Area,
+; and if there's no Golem card in hand, proceed.
+.golem
+ ld e, GOLEM
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jr nc, .dugtrio
+ ld [wce06], a
+ ld a, GRAVELER
+ call LookForCardIDInPlayArea_Bank8
+ jr nc, .dugtrio
+ ld a, GOLEM
+ call LookForCardIDInHandList_Bank8
+ jr c, .dugtrio
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ jr .find_discard_cards_2
+
+; checks if there is a Dugtrio card in the deck to target.
+; if so, check if there's Diglett in Play Area,
+; and if there's no Dugtrio card in hand, proceed.
+.dugtrio
+ ld e, DUGTRIO
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jp nc, .no_carry
+ ld [wce06], a
+ ld a, DIGLETT
+ call LookForCardIDInPlayArea_Bank8
+ jp nc, .no_carry
+ ld a, DUGTRIO
+ call LookForCardIDInHandList_Bank8
+ jp c, .no_carry
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ jr .find_discard_cards_2
+
+.find_discard_cards_2
+ ld a, $ff
+ ld [wce1a], a
+ ld [wce1b], a
+
+ ld bc, wce1a
+ ld d, $00 ; start considering Trainer cards only
+
+; stores wAITrainerCardToPlay in e so that
+; all routines ignore it for the discard effects.
+ ld a, [wAITrainerCardToPlay]
+ ld e, a
+
+; this loop will store in wce1a cards to discard from hand.
+; at the start it will only consider Trainer cards,
+; then if there are still needed to discard,
+; move on to Pokemon cards, and finally to Energy cards.
+.loop_hand_2
+ call RemoveFromListDifferentCardOfGivenType
+ jr c, .found
+ inc d ; move on to next type (Pokemon, then Energy)
+ ld a, $03
+ cp d
+ jp z, .no_carry ; no more types to look
+ jr .loop_hand_2
+.found
+; store this card in memory,
+; and if there's still one more card to search for,
+; jump back into the loop.
+ ld [bc], a
+ inc bc
+ ld a, [wce1b]
+ cp $ff
+ jr z, .loop_hand_2
+
+; output in a Computer Search target and set carry.
+ ld a, [wce06]
+ scf
+ ret
+
+AIDecide_ComputerSearch_WondersOfScience: ; 21c56 (8:5c56)
+; if number of cards in hand < 5, target Professor Oak in deck
+ ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND
+ call GetTurnDuelistVariable
+ cp 5
+ jr nc, .look_in_hand
+
+; target Professor Oak for Computer Search
+ ld e, PROFESSOR_OAK
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jp nc, .look_in_hand ; can be a jr
+ ld [wce06], a
+ jr .find_discard_cards
+
+; Professor Oak not in deck, move on to
+; look for other cards instead.
+; if Grimer or Muk are not in hand,
+; check whether to use Computer Search on them.
+.look_in_hand
+ ld a, GRIMER
+ call LookForCardIDInHandList_Bank8
+ jr nc, .target_grimer
+ ld a, MUK
+ call LookForCardIDInHandList_Bank8
+ jr nc, .target_muk
+
+.no_carry
+ or a
+ ret
+
+; first check Grimer
+; if in deck, check cards to discard.
+.target_grimer
+ ld e, GRIMER
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jp nc, .no_carry ; can be a jr
+ ld [wce06], a
+ jr .find_discard_cards
+
+; first check Muk
+; if in deck, check cards to discard.
+.target_muk
+ ld e, MUK
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jp nc, .no_carry ; can be a jr
+ ld [wce06], a
+
+; only discard Trainer cards from hand.
+; if there are less than 2 Trainer cards to discard,
+; then return with no carry.
+; else, store the cards to discard and the
+; target card deck index, and return carry.
+.find_discard_cards
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ ld d, $00 ; first consider Trainer cards
+
+; ignore wAITrainerCardToPlay for the discard effects.
+ ld a, [wAITrainerCardToPlay]
+ ld e, a
+ call RemoveFromListDifferentCardOfGivenType
+ jr nc, .no_carry
+ ld [wce1a], a
+ call RemoveFromListDifferentCardOfGivenType
+ jr nc, .no_carry
+ ld [wce1b], a
+ ld a, [wce06]
+ scf
+ ret
+
+AIDecide_ComputerSearch_FireCharge: ; 21cbb (8:5cbb)
+; pick target card in deck from highest to lowest priority.
+; if not found in hand, go to corresponding branch.
+ ld a, CHANSEY
+ call LookForCardIDInHandList_Bank8
+ jr nc, .chansey
+ ld a, TAUROS
+ call LookForCardIDInHandList_Bank8
+ jr nc, .tauros
+ ld a, JIGGLYPUFF1
+ call LookForCardIDInHandList_Bank8
+ jr nc, .jigglypuff
+ ; fallthrough
+
+.no_carry
+ or a
+ ret
+
+; for each card targeted, check if it's in deck and,
+; if not, then return no carry.
+; else, look for cards to discard.
+.chansey
+ ld e, CHANSEY
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jp nc, .no_carry
+ ld [wce06], a
+ jr .find_discard_cards
+.tauros
+ ld e, TAUROS
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jp nc, .no_carry
+ ld [wce06], a
+ jr .find_discard_cards
+.jigglypuff
+ ld e, JIGGLYPUFF1
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jp nc, .no_carry
+ ld [wce06], a
+
+; only discard Trainer cards from hand.
+; if there are less than 2 Trainer cards to discard,
+; then return with no carry.
+; else, store the cards to discard and the
+; target card deck index, and return carry.
+.find_discard_cards
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ ld d, $00 ; first consider Trainer cards
+
+; ignore wAITrainerCardToPlay for the discard effects.
+ ld a, [wAITrainerCardToPlay]
+ ld e, a
+ call RemoveFromListDifferentCardOfGivenType
+ jr nc, .no_carry
+ ld [wce1a], a
+ call RemoveFromListDifferentCardOfGivenType
+ jr nc, .no_carry
+ ld [wce1b], a
+ ld a, [wce06]
+ scf
+ ret
+; 0x21d1e
+
+AIDecide_ComputerSearch_Anger: ; 21d1e (8:5d1e)
+; for each of the following cards,
+; first run a check if there's a pre-evolution in
+; Play Area or in the hand. If there is, choose it as target.
+; otherwise, check if the evolution card is in
+; hand and if so, choose it as target instead.
+ ld b, RATTATA
+ ld a, RATICATE
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_discard_cards
+ ld a, RATTATA
+ ld b, RATICATE
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_discard_cards
+ ld b, GROWLITHE
+ ld a, ARCANINE1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_discard_cards
+ ld a, GROWLITHE
+ ld b, ARCANINE1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_discard_cards
+ ld b, DODUO
+ ld a, DODRIO
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_discard_cards
+ ld a, DODUO
+ ld b, DODRIO
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_discard_cards
+ ; fallthrough
+
+.no_carry
+ or a
+ ret
+
+; only discard Trainer cards from hand.
+; if there are less than 2 Trainer cards to discard,
+; then return with no carry.
+; else, store the cards to discard and the
+; target card deck index, and return carry.
+.find_discard_cards
+ ld [wce06], a
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ ld d, $00 ; first consider Trainer cards
+
+; ignore wAITrainerCardToPlay for the discard effects.
+ ld a, [wAITrainerCardToPlay]
+ ld e, a
+ call RemoveFromListDifferentCardOfGivenType
+ jr nc, .no_carry
+ ld [wce1a], a
+ call RemoveFromListDifferentCardOfGivenType
+ jr nc, .no_carry
+ ld [wce1b], a
+ ld a, [wce06]
+ scf
+ ret
+; 0x21d7a
+
+AIPlay_PokemonTrader: ; 21d7a (8:5d7a)
+ ld a, [wAITrainerCardToPlay]
+ ldh [hTempCardIndex_ff9f], a
+ ld a, [wAITrainerCardParameter]
+ ldh [hTemp_ffa0], a
+ ld a, [wce1a]
+ ldh [hTempPlayAreaLocation_ffa1], a
+ ld a, OPPACTION_EXECUTE_TRAINER_EFFECTS
+ bank1call AIMakeDecision
+ ret
+; 0x21d8f
+
+AIDecide_PokemonTrader: ; 21d8f (8:5d8f)
+; each deck has their own routine for picking
+; what Pokemon to look for.
+ ld a, [wOpponentDeckID]
+ cp LEGENDARY_MOLTRES_DECK_ID
+ jr z, AIDecide_PokemonTrader_LegendaryMoltres
+ cp LEGENDARY_ARTICUNO_DECK_ID
+ jr z, AIDecide_PokemonTrader_LegendaryArticuno
+ cp LEGENDARY_DRAGONITE_DECK_ID
+ jp z, AIDecide_PokemonTrader_LegendaryDragonite
+ cp LEGENDARY_RONALD_DECK_ID
+ jp z, AIDecide_PokemonTrader_LegendaryRonald
+ cp BLISTERING_POKEMON_DECK_ID
+ jp z, AIDecide_PokemonTrader_BlisteringPokemon
+ cp SOUND_OF_THE_WAVES_DECK_ID
+ jp z, AIDecide_PokemonTrader_SoundOfTheWaves
+ cp POWER_GENERATOR_DECK_ID
+ jp z, AIDecide_PokemonTrader_PowerGenerator
+ cp FLOWER_GARDEN_DECK_ID
+ jp z, AIDecide_PokemonTrader_FlowerGarden
+ cp STRANGE_POWER_DECK_ID
+ jp z, AIDecide_PokemonTrader_StrangePower
+ cp FLAMETHROWER_DECK_ID
+ jp z, AIDecide_PokemonTrader_Flamethrower
+ or a
+ ret
+
+AIDecide_PokemonTrader_LegendaryMoltres: ; 21dc4 (8:5dc4)
+; look for Moltres2 card in deck to trade with a
+; card in hand different from Moltres1.
+ ld a, MOLTRES2
+ ld e, MOLTRES1
+ call LookForCardIDToTradeWithDifferentHandCard
+ jr nc, .no_carry
+; success
+ ld [wce1a], a
+ ld a, e
+ scf
+ ret
+.no_carry
+ or a
+ ret
+
+AIDecide_PokemonTrader_LegendaryArticuno: ; 21dd5 (8:5dd5)
+; if has none of these cards in Hand or Play Area, proceed
+ ld a, ARTICUNO1
+ call LookForCardIDInHandAndPlayArea
+ jr c, .no_carry
+ ld a, LAPRAS
+ call LookForCardIDInHandAndPlayArea
+ jr c, .no_carry
+
+; if doesn't have Seel in Hand or Play Area,
+; look for it in the deck.
+; otherwise, look for Dewgong instead.
+ ld a, SEEL
+ call LookForCardIDInHandAndPlayArea
+ jr c, .dewgong
+
+ ld e, SEEL
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jr nc, .dewgong
+ ld [wce1a], a
+ jr .check_hand
+
+.dewgong
+ ld a, DEWGONG
+ call LookForCardIDInHandAndPlayArea
+ jr c, .no_carry
+ ld e, DEWGONG
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jr nc, .no_carry
+ ld [wce1a], a
+
+; a Seel or Dewgong was found in deck,
+; check hand for card to trade for
+.check_hand
+ ld a, CHANSEY
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ld a, DITTO
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ld a, ARTICUNO2
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ; doesn't have any of the cards in hand
+
+.no_carry
+ or a
+ ret
+
+.set_carry
+ scf
+ ret
+; 0x21e24
+
+AIDecide_PokemonTrader_LegendaryDragonite: ; 21e24 (8:5e24)
+; if has less than 5 cards of energy
+; and of Pokemon in hand/Play Area,
+; target a Kangaskhan in deck.
+ farcall CountOppEnergyCardsInHandAndAttached
+ cp 5
+ jr c, .kangaskhan
+ call CountPokemonCardsInHandAndInPlayArea
+ cp 5
+ jr c, .kangaskhan
+ ; total number of energy cards >= 5
+ ; total number of Pokemon cards >= 5
+
+; for each of the following cards,
+; first run a check if there's a pre-evolution in
+; Play Area or in the hand. If there is, choose it as target.
+; otherwise, check if the evolution card is in
+; hand and if so, choose it as target instead.
+ ld b, MAGIKARP
+ ld a, GYARADOS
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld a, MAGIKARP
+ ld b, GYARADOS
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ ld b, DRATINI
+ ld a, DRAGONAIR
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld b, DRAGONAIR
+ ld a, DRAGONITE1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld a, DRATINI
+ ld b, DRAGONAIR
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ ld a, DRAGONAIR
+ ld b, DRAGONITE1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ ld b, CHARMANDER
+ ld a, CHARMELEON
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld b, CHARMELEON
+ ld a, CHARIZARD
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld a, CHARMANDER
+ ld b, CHARMELEON
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ ld a, CHARMELEON
+ ld b, CHARIZARD
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ jr .no_carry
+
+.kangaskhan
+ ld e, KANGASKHAN
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jr nc, .no_carry
+
+; card was found as target in deck,
+; look for card in hand to trade with
+.choose_hand
+ ld [wce1a], a
+ ld a, DRAGONAIR
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ld a, CHARMELEON
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ld a, GYARADOS
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ld a, MAGIKARP
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ld a, CHARMANDER
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ld a, DRATINI
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ; non found
+
+.no_carry
+ or a
+ ret
+.set_carry
+ scf
+ ret
+; 0x21ec9
+
+AIDecide_PokemonTrader_LegendaryRonald: ; 21ec9 (8:5ec9)
+; for each of the following cards,
+; first run a check if there's a pre-evolution in
+; Play Area or in the hand. If there is, choose it as target.
+; otherwise, check if the evolution card is in
+; hand and if so, choose it as target instead.
+ ld b, EEVEE
+ ld a, FLAREON1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld b, EEVEE
+ ld a, VAPOREON1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld b, EEVEE
+ ld a, JOLTEON1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld a, EEVEE
+ ld b, FLAREON1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ ld a, EEVEE
+ ld b, VAPOREON1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ ld a, EEVEE
+ ld b, JOLTEON1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ ld b, DRATINI
+ ld a, DRAGONAIR
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld b, DRAGONAIR
+ ld a, DRAGONITE1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld a, DRATINI
+ ld b, DRAGONAIR
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ ld a, DRAGONAIR
+ ld b, DRAGONITE1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ jr .no_carry
+
+; card was found as target in deck,
+; look for card in hand to trade with
+.choose_hand
+ ld [wce1a], a
+ ld a, ZAPDOS3
+ call LookForCardIDInHandList_Bank8
+ jr c, .set_carry
+ ld a, ARTICUNO2
+ call LookForCardIDInHandList_Bank8
+ jr c, .set_carry
+ ld a, MOLTRES2
+ call LookForCardIDInHandList_Bank8
+ jr c, .set_carry
+ ; none found
+
+.no_carry
+ or a
+ ret
+.set_carry
+ scf
+ ret
+; 0x21f41
+
+AIDecide_PokemonTrader_BlisteringPokemon: ; 21f41 (8:5f41)
+; for each of the following cards,
+; first run a check if there's a pre-evolution in
+; Play Area or in the hand. If there is, choose it as target.
+; otherwise, check if the evolution card is in
+; hand and if so, choose it as target instead.
+ ld b, RHYHORN
+ ld a, RHYDON
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, RHYHORN
+ ld b, RHYDON
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld b, CUBONE
+ ld a, MAROWAK1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, CUBONE
+ ld b, MAROWAK1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld b, PONYTA
+ ld a, RAPIDASH
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, PONYTA
+ ld b, RAPIDASH
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ jr .no_carry
+
+; a card in deck was found to look for,
+; check if there are duplicates in hand to trade with.
+.find_duplicates
+ ld [wce1a], a
+ call FindDuplicatePokemonCards
+ jr c, .set_carry
+.no_carry
+ or a
+ ret
+.set_carry
+ scf
+ ret
+; 0x21f85
+
+AIDecide_PokemonTrader_SoundOfTheWaves: ; 21f85 (8:5f85)
+; for each of the following cards,
+; first run a check if there's a pre-evolution in
+; Play Area or in the hand. If there is, choose it as target.
+; otherwise, check if the evolution card is in
+; hand and if so, choose it as target instead.
+ ld b, SEEL
+ ld a, DEWGONG
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld a, SEEL
+ ld b, DEWGONG
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ ld b, KRABBY
+ ld a, KINGLER
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld a, KRABBY
+ ld b, KINGLER
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ ld b, SHELLDER
+ ld a, CLOYSTER
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld a, SHELLDER
+ ld b, CLOYSTER
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ ld b, HORSEA
+ ld a, SEADRA
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld a, HORSEA
+ ld b, SEADRA
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ ld b, TENTACOOL
+ ld a, TENTACRUEL
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .choose_hand
+ ld a, TENTACOOL
+ ld b, TENTACRUEL
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .choose_hand
+ jr .no_carry
+
+; card was found as target in deck,
+; look for card in hand to trade with
+.choose_hand
+ ld [wce1a], a
+ ld a, SEEL
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ld a, KRABBY
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ld a, HORSEA
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ld a, SHELLDER
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ld a, TENTACOOL
+ call CheckIfHasCardIDInHand
+ jr c, .set_carry
+ ; none found
+
+.no_carry
+ or a
+ ret
+.set_carry
+ scf
+ ret
+; 0x2200b
+
+AIDecide_PokemonTrader_PowerGenerator: ; 2200b (8:600b)
+; for each of the following cards,
+; first run a check if there's a pre-evolution in
+; Play Area or in the hand. If there is, choose it as target.
+; otherwise, check if the evolution card is in
+; hand and if so, choose it as target instead.
+ ld b, PIKACHU2
+ ld a, RAICHU1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jp c, .find_duplicates
+ ld b, PIKACHU1
+ ld a, RAICHU1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, PIKACHU2
+ ld b, RAICHU1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld a, PIKACHU1
+ ld b, RAICHU1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld b, VOLTORB
+ ld a, ELECTRODE2
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld b, VOLTORB
+ ld a, ELECTRODE1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, VOLTORB
+ ld b, ELECTRODE2
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld a, VOLTORB
+ ld b, ELECTRODE1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld b, MAGNEMITE1
+ ld a, MAGNETON2
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld b, MAGNEMITE2
+ ld a, MAGNETON2
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld b, MAGNEMITE1
+ ld a, MAGNETON1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld b, MAGNEMITE2
+ ld a, MAGNETON1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, MAGNEMITE2
+ ld b, MAGNETON2
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld a, MAGNEMITE1
+ ld b, MAGNETON2
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld a, MAGNEMITE2
+ ld b, MAGNETON1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld a, MAGNEMITE1
+ ld b, MAGNETON1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ; bug, missing jr .no_carry
+
+; since this last check falls through regardless of result,
+; register a might hold an invalid deck index,
+; which might lead to hilarious results like Brandon
+; trading a Pikachu with a Grass Energy from the deck.
+; however, since it's deep in a tower of conditionals,
+; reaching here is extremely unlikely.
+
+; a card in deck was found to look for,
+; check if there are duplicates in hand to trade with.
+.find_duplicates
+ ld [wce1a], a
+ call FindDuplicatePokemonCards
+ jr c, .set_carry
+ or a
+ ret
+.set_carry
+ scf
+ ret
+; 0x220a8
+
+AIDecide_PokemonTrader_FlowerGarden: ; 220a8 (8:60a8)
+; for each of the following cards,
+; first run a check if there's a pre-evolution in
+; Play Area or in the hand. If there is, choose it as target.
+; otherwise, check if the evolution card is in
+; hand and if so, choose it as target instead.
+ ld b, BULBASAUR
+ ld a, IVYSAUR
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld b, IVYSAUR
+ ld a, VENUSAUR2
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, BULBASAUR
+ ld b, IVYSAUR
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld a, IVYSAUR
+ ld b, VENUSAUR2
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld b, BELLSPROUT
+ ld a, WEEPINBELL
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld b, WEEPINBELL
+ ld a, VICTREEBEL
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, BELLSPROUT
+ ld b, WEEPINBELL
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld a, WEEPINBELL
+ ld b, VICTREEBEL
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld b, ODDISH
+ ld a, GLOOM
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld b, GLOOM
+ ld a, VILEPLUME
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, ODDISH
+ ld b, GLOOM
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld a, GLOOM
+ ld b, VILEPLUME
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ jr .no_carry
+
+; a card in deck was found to look for,
+; check if there are duplicates in hand to trade with.
+.find_duplicates
+ ld [wce1a], a
+ call FindDuplicatePokemonCards
+ jr c, .found
+.no_carry
+ or a
+ ret
+.found
+ scf
+ ret
+; 0x22122
+
+AIDecide_PokemonTrader_StrangePower: ; 22122 (8:6122)
+; looks for a Pokemon in hand to trade with Mr Mime in deck.
+; inputing Mr Mime in register e for the function is redundant
+; since it already checks whether a Mr Mime exists in the hand.
+ ld a, MR_MIME
+ ld e, MR_MIME
+ call LookForCardIDToTradeWithDifferentHandCard
+ jr nc, .no_carry
+; found
+ ld [wce1a], a
+ ld a, e
+ scf
+ ret
+.no_carry
+ or a
+ ret
+; 0x22133
+
+AIDecide_PokemonTrader_Flamethrower: ; 22133 (8:6133)
+; for each of the following cards,
+; first run a check if there's a pre-evolution in
+; Play Area or in the hand. If there is, choose it as target.
+; otherwise, check if the evolution card is in
+; hand and if so, choose it as target instead.
+ ld b, CHARMANDER
+ ld a, CHARMELEON
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld b, CHARMELEON
+ ld a, CHARIZARD
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, CHARMANDER
+ ld b, CHARMELEON
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld a, CHARMELEON
+ ld b, CHARIZARD
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld b, VULPIX
+ ld a, NINETAILS1
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, VULPIX
+ ld b, NINETAILS1
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld b, GROWLITHE
+ ld a, ARCANINE2
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, GROWLITHE
+ ld b, ARCANINE2
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ ld b, EEVEE
+ ld a, FLAREON2
+ call LookForCardIDInDeck_GivenCardIDInHandAndPlayArea
+ jr c, .find_duplicates
+ ld a, EEVEE
+ ld b, FLAREON2
+ call LookForCardIDInDeck_GivenCardIDInHand
+ jr c, .find_duplicates
+ jr .no_carry
+
+; a card in deck was found to look for,
+; check if there are duplicates in hand to trade with.
+.find_duplicates
+ ld [wce1a], a
+ call FindDuplicatePokemonCards
+ jr c, .set_carry
+.no_carry
+ or a
+ ret
+.set_carry
+ scf
+ ret
+; 0x2219b
+
+Func_2219b: ; 2219b (8:219b)
+ INCROM $2219b, $227f6
+
+; lists in wDuelTempList all the basic energy cards
+; in card location of a.
+; outputs in a number of cards found.
+; returns carry if none were found.
+; input:
+; a = CARD_LOCATION_* to look
+; output:
+; a = number of cards found
+FindBasicEnergyCardsInLocation: ; 227f6 (8:67f6)
+ ld [wTempAI], a
+ lb de, 0, 0
+ ld hl, wDuelTempList
+
+; d = number of basic energy cards found
+; e = current card in deck
+; loop entire deck
+.loop
+ ld a, DUELVARS_CARD_LOCATIONS
+ add e
+ push hl
+ call GetTurnDuelistVariable
+ ld hl, wTempAI
+ cp [hl]
+ pop hl
+ jr nz, .next_card
+
+; is in the card location we're looking for
+ ld a, e
+ push de
+ push hl
+ call GetCardIDFromDeckIndex
+ pop hl
+ ld a, e
+ pop de
+ cp DOUBLE_COLORLESS_ENERGY
+ ; only basic energy cards
+ ; will set carry here
+ jr nc, .next_card
+
+; is a basic energy card
+; add this card to the TempList
+ ld a, e
+ ld [hli], a
+ inc d
+.next_card
+ inc e
+ ld a, DECK_SIZE
+ cp e
+ jr nz, .loop
+
+; check if any were found
+ ld a, d
+ or a
+ jr z, .set_carry
+
+; some were found, add the termination byte on TempList
+ ld a, $ff
+ ld [hl], a
+ ld a, d
+ ret
+
+.set_carry
+ scf
+ ret
+; 0x2282e
+
+; returns in a the card index of energy card
+; attached to Pokémon in Play Area location a,
+; that is to be discarded by the AI for an effect.
+; outputs $ff is none was found.
+; input:
+; a = PLAY_AREA_* constant of card
+; output:
+; a = deck index of attached energy card chosen
+AIPickEnergyCardToDiscard: ; 2282e (8:682e)
+; load Pokémon's attached energy cards.
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call CreateArenaOrBenchEnergyCardList
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ ld e, a
+ call GetPlayAreaCardAttachedEnergies
+ ld a, [wTotalAttachedEnergies]
+ or a
+ jr z, .no_energy
+
+; load card's ID and type.
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ ld b, a
+ ld a, DUELVARS_ARENA_CARD
+ add b
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ ld [wTempCardID], a
+ call LoadCardDataToBuffer1_FromCardID
+ ld a, [wLoadedCard1Type]
+ or TYPE_ENERGY
+ ld [wTempCardType], a
+
+; find a card that is not useful.
+; if none is found, just return the first energy card attached.
+ ld hl, wDuelTempList
+.loop
+ ld a, [hl]
+ cp $ff
+ jr z, .not_found
+ farcall CheckIfEnergyIsUseful
+ jr nc, .found
inc hl
+ jr .loop
+
+.found
+ ld a, [hl]
+ ret
+.not_found
+ ld hl, wDuelTempList
+ ld a, [hl]
+ ret
+.no_energy
+ ld a, $ff
+ ret
+; 0x22875
+
+; returns in a the deck index of an energy card attached to card
+; in player's Play Area location a to remove.
+; prioritises double colorless energy, then any useful energy,
+; then defaults to the first energy card attached if neither
+; of those are found.
+; returns $ff in a if there are no energy cards attached.
+; input:
+; a = Play Area location to check
+; output:
+; a = deck index of attached energy card
+PickAttachedEnergyCardToRemove: ; 22875 (8:6875)
+; construct energy list and check if there are any energy cards attached
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call CreateArenaOrBenchEnergyCardList
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ ld e, a
+ call GetPlayAreaCardAttachedEnergies
+ ld a, [wTotalAttachedEnergies]
+ or a
+ jr z, .no_energy
+
+; load card data and store its type
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ ld b, a
+ ld a, DUELVARS_ARENA_CARD
+ add b
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ ld [wTempCardID], a
+ call LoadCardDataToBuffer1_FromCardID
+ ld a, [wLoadedCard1Type]
+ or TYPE_ENERGY
+ ld [wTempCardType], a
+
+; first look for any double colorless energy
+ ld hl, wDuelTempList
+.loop_1
+ ld a, [hl]
+ cp $ff
+ jr z, .check_useful
+ push hl
+ call GetCardIDFromDeckIndex
+ ld a, e
+ cp DOUBLE_COLORLESS_ENERGY
+ pop hl
+ jr z, .found
inc hl
+ jr .loop_1
+
+; then look for any energy cards that are useful
+.check_useful
+ ld hl, wDuelTempList
+.loop_2
+ ld a, [hl]
+ cp $ff
+ jr z, .default
+ farcall CheckIfEnergyIsUseful
+ jr c, .found
inc hl
+ jr .loop_2
+
+; return the energy card that was found
+.found
+ ld a, [hl]
+ ret
+
+; if none were found with the above criteria,
+; just return the first option
+.default
+ ld hl, wDuelTempList
+ ld a, [hl]
+ ret
+
+; return $ff if no energy cards attached
+.no_energy
+ ld a, $ff
+ ret
+; 0x228d1
+
+; stores in wTempAI and wCurCardCanAttack the deck indices
+; of energy cards attached to card in Play Area location a.
+; prioritises double colorless energy, then any useful energy,
+; then defaults to the first two energy cards attached if neither
+; of those are found.
+; returns $ff in a if there are no energy cards attached.
+; input:
+; a = Play Area location to check
+; output:
+; [wTempAI] = deck index of attached energy card
+; [wCurCardCanAttack] = deck index of attached energy card
+PickTwoAttachedEnergyCards: ; 228d1 (8:68d1)
+ ldh [hTempPlayAreaLocation_ff9d], a
+ call CreateArenaOrBenchEnergyCardList
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ ld e, a
+ farcall CountNumberOfEnergyCardsAttached
+ cp 2
+ jp c, .not_enough
+
+; load card data and store its type
+ ldh a, [hTempPlayAreaLocation_ff9d]
+ ld b, a
+ ld a, DUELVARS_ARENA_CARD
+ add b
+ call GetTurnDuelistVariable
+ call GetCardIDFromDeckIndex
+ ld a, e
+ ld [wTempCardID], a
+ call LoadCardDataToBuffer1_FromCardID
+ ld a, [wLoadedCard1Type]
+ or TYPE_ENERGY
+ ld [wTempCardType], a
+ ld a, $ff
+ ld [wTempAI], a
+ ld [wCurCardCanAttack], a
+
+; first look for any double colorless energy
+ ld hl, wDuelTempList
+.loop_1
+ ld a, [hl]
+ cp $ff
+ jr z, .check_useful
+ push hl
+ call GetCardIDFromDeckIndex
+ ld a, e
+ cp DOUBLE_COLORLESS_ENERGY
+ pop hl
+ jr z, .found_double_colorless
inc hl
- jp .asm_4106
-; 0x201b1
+ jr .loop_1
+.found_double_colorless
+ ld a, [wTempAI]
+ cp $ff
+ jr nz, .already_chosen_1
+ ld a, [hli]
+ ld [wTempAI], a
+ jr .loop_1
+.already_chosen_1
+ ld a, [hl]
+ ld [wCurCardCanAttack], a
+ jr .done
- INCROM $201b1, $2297b
+; then look for any energy cards that are useful
+.check_useful
+ ld hl, wDuelTempList
+.loop_2
+ ld a, [hl]
+ cp $ff
+ jr z, .default
+ farcall CheckIfEnergyIsUseful
+ jr c, .found_useful
+ inc hl
+ jr .loop_2
+.found_useful
+ ld a, [wTempAI]
+ cp $ff
+ jr nz, .already_chosen_2
+ ld a, [hli]
+ ld [wTempAI], a
+ jr .loop_2
+.already_chosen_2
+ ld a, [hl]
+ ld [wCurCardCanAttack], a
+ jr .done
+
+; if none were found with the above criteria,
+; just return the first 2 options
+.default
+ ld hl, wDuelTempList
+ ld a, [wTempAI]
+ cp $ff
+ jr nz, .pick_one_card
+
+; pick 2 cards
+ ld a, [hli]
+ ld [wTempAI], a
+ ld a, [hl]
+ ld [wCurCardCanAttack], a
+ jr .done
+.pick_one_card
+ ld a, [wTempAI]
+ ld b, a
+.loop_3
+ ld a, [hli]
+ cp b
+ jr z, .loop_3 ; already picked
+ ld [wCurCardCanAttack], a
+
+.done
+ ld a, [wCurCardCanAttack]
+ ld b, a
+ ld a, [wTempAI]
+ ret
+
+; return $ff if no energy cards attached
+.not_enough
+ ld a, $ff
+ ret
+; 0x2297b
; copies $ff terminated buffer from hl to de
CopyBuffer: ; 2297b (8:697b)
@@ -129,14 +6556,29 @@ CopyBuffer: ; 2297b (8:697b)
jr CopyBuffer
; 0x22983
- INCROM $22983, $22990
+; zeroes a bytes starting at hl
+ClearMemory_Bank8: ; 22983 (8:6983)
+ push af
+ push bc
+ push hl
+ ld b, a
+ xor a
+.loop
+ ld [hli], a
+ dec b
+ jr nz, .loop
+ pop hl
+ pop bc
+ pop af
+ ret
+; 0x22990
; counts number of energy cards found in hand
; and outputs result in a
; sets carry if none are found
; output:
; a = number of energy cards found
-CountEnergyCardsInHand: ; 22990 (8:6990)
+CountOppEnergyCardsInHand: ; 22990 (8:6990)
farcall CreateEnergyCardListFromHand
ret c
ld b, -1
@@ -151,5 +6593,555 @@ CountEnergyCardsInHand: ; 22990 (8:6990)
ret
; 0x229a3
-Func_229a3 ; 229a3 (8:69a3)
- INCROM $229a3, $24000
+; converts HP in a to number of equivalent damage counters
+; input:
+; a = HP
+; output:
+; a = number of damage counters
+ConvertHPToCounters: ; 229a3 (8:69a3)
+ push bc
+ ld c, 0
+.loop
+ sub 10
+ jr c, .carry
+ inc c
+ jr .loop
+.carry
+ ld a, c
+ pop bc
+ ret
+; 0x229b0
+
+; calculates floor(hl / 10)
+CalculateWordTensDigit: ; 229b0 (8:69b0)
+ push bc
+ push de
+ lb bc, $ff, -10
+ lb de, $ff, -1
+.asm_229b8
+ inc de
+ add hl, bc
+ jr c, .asm_229b8
+ ld h, d
+ ld l, e
+ pop de
+ pop bc
+ ret
+; 0x229c1
+
+; returns in a division of b by a
+CalculateBDividedByA_Bank8: ; 229c1 (8:69c1)
+ push bc
+ ld c, a
+ ld a, b
+ ld b, c
+ ld c, 0
+.loop
+ sub b
+ jr c, .done
+ inc c
+ jr .loop
+.done
+ ld a, c
+ pop bc
+ ret
+; 0x229d0
+
+; returns in a the deck index of the first
+; instance of card with ID equal to the ID in e
+; in card location a.
+; returns carry if found.
+; input:
+; a = CARD_LOCATION_*
+; e = card ID to look for
+LookForCardIDInLocation: ; 229d0 (8:69d0)
+ ld b, a
+ ld c, e
+ lb de, $00, 0 ; d is never used
+.loop
+ ld a, DUELVARS_CARD_LOCATIONS
+ add e
+ call GetTurnDuelistVariable
+ cp b
+ jr nz, .next
+ ld a, e
+ push de
+ call GetCardIDFromDeckIndex
+ ld a, e
+ pop de
+ cp c
+ jr z, .found
+.next
+ inc e
+ ld a, DECK_SIZE
+ cp e
+ jr nz, .loop
+
+; not found
+ or a
+ ret
+.found
+ ld a, e
+ scf
+ ret
+; 0x229f3
+
+; return carry if card ID loaded in a is found in hand
+; and outputs in a the deck index of that card
+; input:
+; a = card ID
+; output:
+; a = card deck index, if found
+; carry set if found
+LookForCardIDInHandList_Bank8: ; 229f3 (8:69f3)
+ ld [wTempCardIDToLook], a
+ call CreateHandCardList
+ ld hl, wDuelTempList
+
+.loop
+ ld a, [hli]
+ cp $ff
+ ret z
+
+ ldh [hTempCardIndex_ff98], a
+ call LoadCardDataToBuffer1_FromDeckIndex
+ ld b, a
+ ld a, [wTempCardIDToLook]
+ cp b
+ jr nz, .loop
+
+ ldh a, [hTempCardIndex_ff98]
+ scf
+ ret
+; 0x22a10
+
+; searches in deck for card ID 1 in a, and
+; if found, searches in Hand/Play Area for card ID 2 in b, and
+; if found, searches for card ID 1 in Hand/Play Area, and
+; if none found, return carry and output deck index
+; of the card ID 1 in deck.
+; input:
+; a = card ID 1
+; b = card ID 2
+; output:
+; a = index of card ID 1 in deck
+LookForCardIDInDeck_GivenCardIDInHandAndPlayArea: ; 22a10 (8:6a10)
+; store a in wCurCardCanAttack
+; and b in wTempAI
+ ld c, a
+ ld a, b
+ ld [wTempAI], a
+ ld a, c
+ ld [wCurCardCanAttack], a
+
+; look for the card ID 1 in deck
+ ld e, a
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret nc
+
+; was found, store its deck index in memory
+ ld [wTempAIPokemonCard], a
+
+; look for the card ID 2
+; in Hand and Play Area, return if not found.
+ ld a, [wTempAI]
+ call LookForCardIDInHandAndPlayArea
+ ret nc
+
+; look for the card ID 1 in the Hand and Play Area
+; if any card is found, return no carry.
+ ld a, [wCurCardCanAttack]
+ call LookForCardIDInHandAndPlayArea
+ jr c, .no_carry
+; none found
+
+ ld a, [wTempAIPokemonCard]
+ scf
+ ret
+
+.no_carry
+ or a
+ ret
+; 0x22a39
+
+; returns carry if card ID in a
+; is found in Play Area or in hand
+; input:
+; a = card ID
+LookForCardIDInHandAndPlayArea: ; 22a39 (8:6a39)
+ ld b, a
+ push bc
+ call LookForCardIDInHandList_Bank8
+ pop bc
+ ret c
+
+ ld a, b
+ ld b, PLAY_AREA_ARENA
+ call LookForCardIDInPlayArea_Bank8
+ ret c
+ or a
+ ret
+; 0x22a49
+
+; searches in deck for card ID 1 in a, and
+; if found, searches in Hand Area for card ID 2 in b, and
+; if found, searches for card ID 1 in Hand/Play Area, and
+; if none found, return carry and output deck index
+; of the card ID 1 in deck.
+; input:
+; a = card ID 1
+; b = card ID 2
+; output:
+; a = index of card ID 1 in deck
+LookForCardIDInDeck_GivenCardIDInHand: ; 22a49 (8:6a49)
+; store a in wCurCardCanAttack
+; and b in wTempAI
+ ld c, a
+ ld a, b
+ ld [wTempAI], a
+ ld a, c
+ ld [wCurCardCanAttack], a
+
+; look for the card ID 1 in deck
+ ld e, a
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ ret nc
+
+; was found, store its deck index in memory
+ ld [wTempAIPokemonCard], a
+
+; look for the card ID 2 in hand, return if not found.
+ ld a, [wTempAI]
+ call LookForCardIDInHandList_Bank8
+ ret nc
+
+; look for the card ID 1 in the Hand and Play Area
+; if any card is found, return no carry.
+ ld a, [wCurCardCanAttack]
+ call LookForCardIDInHandAndPlayArea
+ jr c, .no_carry
+; none found
+
+ ld a, [wTempAIPokemonCard]
+ scf
+ ret
+
+.no_carry
+ or a
+ ret
+; 0x22a72
+
+; returns carry if card ID in a
+; is found in Play Area, starting with
+; location in b
+; input:
+; a = card ID
+; b = PLAY_AREA_* to start with
+; ouput:
+; a = PLAY_AREA_* of found card
+; carry set if found
+LookForCardIDInPlayArea_Bank8: ; 22a72 (8:6a72)
+ ld [wTempCardIDToLook], a
+.loop
+ ld a, DUELVARS_ARENA_CARD
+ add b
+ call GetTurnDuelistVariable
+ cp $ff
+ ret z
+
+ call LoadCardDataToBuffer1_FromDeckIndex
+ ld c, a
+ ld a, [wTempCardIDToLook]
+ cp c
+ jr z, .is_same
+
+ inc b
+ ld a, MAX_PLAY_AREA_POKEMON
+ cp b
+ jr nz, .loop
+ ld b, $ff
+ or a
+ ret
+
+.is_same
+ ld a, b
+ scf
+ ret
+; 0x22a95
+
+; runs through list avoiding card in e.
+; removes first card in list not equal to e
+; and that has a type allowed to be removed, in d.
+; returns carry if successful in finding a card.
+; input:
+; d = type of card allowed to be removed
+; ($00 = Trainer, $01 = Pokemon, $02 = Energy)
+; e = card deck index to avoid removing
+; output:
+; a = card index of removed card
+RemoveFromListDifferentCardOfGivenType: ; 22a95 (8:6a95)
+ push hl
+ push de
+ push bc
+ call CountCardsInDuelTempList
+ call ShuffleCards
+
+; loop list until a card with
+; deck index different from e is found.
+.loop_list
+ ld a, [hli]
+ cp $ff
+ jr z, .no_carry
+ cp e
+ jr z, .loop_list
+
+; get this card's type
+ ldh [hTempCardIndex_ff98], a
+ push de
+ call GetCardIDFromDeckIndex
+ call GetCardType
+ pop de
+ cp TYPE_ENERGY
+ jr c, .pkmn_card
+ cp TYPE_TRAINER
+ jr nz, .energy
+
+; only remove from list specific type.
+
+; trainer
+ ld a, d
+ or a
+ jr nz, .loop_list
+ jr .remove_card
+.energy
+ ld a, d
+ cp $02
+ jr nz, .loop_list
+ jr .remove_card
+.pkmn_card
+ ld a, d
+ cp $01
+ jr nz, .loop_list
+ ; fallthrough
+
+.remove_card
+ ld d, h
+ ld e, l
+ dec hl
+.loop_remove
+ ld a, [de]
+ inc de
+ ld [hli], a
+ cp $ff
+ jr nz, .loop_remove
+
+; success
+ ldh a, [hTempCardIndex_ff98]
+ pop bc
+ pop de
+ pop hl
+ scf
+ ret
+.no_carry
+ pop bc
+ pop de
+ pop hl
+ or a
+ ret
+; 0x22ae0
+
+; used in Pokemon Trader checks to look for a specific
+; card in the deck to trade with a card in hand that
+; has a card ID different from e.
+; returns carry if successful.
+; input:
+; a = card ID 1
+; e = card ID 2
+; output:
+; a = deck index of card ID 1 found in deck
+; e = deck index of Pokemon card in hand dfferent than card ID 2
+LookForCardIDToTradeWithDifferentHandCard: ; 22ae0 (8:6ae0)
+ ld hl, wCurCardCanAttack
+ ld [hl], e
+ ld [wTempAI], a
+
+; if card ID 1 is in hand, return no carry.
+ call LookForCardIDInHandList_Bank8
+ jr c, .no_carry
+
+; if card ID 1 is not in deck, return no carry.
+ ld a, [wTempAI]
+ ld e, a
+ ld a, CARD_LOCATION_DECK
+ call LookForCardIDInLocation
+ jr nc, .no_carry
+
+; store its deck index
+ ld [wTempAI], a
+
+; look in hand for Pokemon card ID that
+; is different from card ID 2.
+ ld a, [wCurCardCanAttack]
+ ld c, a
+ call CreateHandCardList
+ ld hl, wDuelTempList
+
+.loop_hand
+ ld a, [hli]
+ cp $ff
+ jr z, .no_carry
+ ld b, a
+ call LoadCardDataToBuffer1_FromDeckIndex
+ cp c
+ jr z, .loop_hand
+ ld a, [wLoadedCard1Type]
+ cp TYPE_ENERGY
+ jr nc, .loop_hand
+
+; found, output deck index of card ID 1 in deck
+; and deck index of card found in hand, and set carry
+ ld e, b
+ ld a, [wTempAI]
+ scf
+ ret
+
+.no_carry
+ or a
+ ret
+; 0x22b1f
+
+; returns carry if at least one card in the hand
+; has the card ID of input. Outputs its index.
+; input:
+; a = card ID to look for
+; output:
+; a = deck index of card in hand found
+CheckIfHasCardIDInHand: ; 22b1f (8:6b1f)
+ ld [wTempCardIDToLook], a
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ ld c, 0
+
+.loop_hand
+ ld a, [hli]
+ cp $ff
+ ret z
+ ldh [hTempCardIndex_ff98], a
+ call LoadCardDataToBuffer1_FromDeckIndex
+ ld b, a
+ ld a, [wTempCardIDToLook]
+ cp b
+ jr nz, .loop_hand
+ ld a, c
+ or a
+ jr nz, .set_carry
+ inc c
+ jr nz, .loop_hand
+
+.set_carry
+ ldh a, [hTempCardIndex_ff98]
+ scf
+ ret
+; 0x22b45
+
+; outputs in a total number of Pokemon cards in hand
+; plus Pokemon in Turn Duelist's Play Area.
+CountPokemonCardsInHandAndInPlayArea: ; 22b45 (8:6b45)
+ ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
+ call GetTurnDuelistVariable
+ ld [wTempAI], a
+ call CreateHandCardList
+ ld hl, wDuelTempList
+.loop_hand
+ ld a, [hli]
+ cp $ff
+ jr z, .done
+ call GetCardIDFromDeckIndex
+ call GetCardType
+ cp TYPE_ENERGY
+ jr nc, .loop_hand
+ ld a, [wTempAI]
+ inc a
+ ld [wTempAI], a
+ jr .loop_hand
+.done
+ ld a, [wTempAI]
+ ret
+; 0x22b6f
+
+; returns carry if a duplicate Pokemon card is found in hand.
+; outputs in a the deck index of one of them.
+FindDuplicatePokemonCards: ; 22b6f (8:6b6f)
+ ld a, $ff
+ ld [wTempAI], a
+ call CreateHandCardList
+ ld hl, wDuelTempList
+ push hl
+
+.loop_hand_outer
+ pop hl
+ ld a, [hli]
+ cp $ff
+ jr z, .done
+ call GetCardIDFromDeckIndex
+ ld b, e
+ push hl
+
+.loop_hand_inner
+ ld a, [hli]
+ cp $ff
+ jr z, .loop_hand_outer
+ ld c, a
+ call GetCardIDFromDeckIndex
+ ld a, e
+ cp b
+ jr nz, .loop_hand_inner
+
+; found two cards with same ID,
+; if they are Pokemon cards, store its deck index.
+ push bc
+ call GetCardType
+ pop bc
+ cp TYPE_ENERGY
+ jr nc, .loop_hand_outer
+ ld a, c
+ ld [wTempAI], a
+ ; for some reason loop still continues
+ ; even though if some other duplicate
+ ; cards are found, it overwrites the result.
+ jr .loop_hand_outer
+
+.done
+ ld a, [wTempAI]
+ cp $ff
+ jr z, .no_carry
+
+; found
+ scf
+ ret
+.no_carry
+ or a
+ ret
+; 0x22bad
+
+; return carry flag if move is not high recoil.
+Func_22bad: ; 22bad (8:6bad)
+ farcall Func_169ca
+ ret nc
+ ld a, [wSelectedAttack]
+ ld e, a
+ ld a, DUELVARS_ARENA_CARD
+ call GetTurnDuelistVariable
+ ld d, a
+ call CopyMoveDataAndDamage_FromDeckIndex
+ ld a, MOVE_FLAG1_ADDRESS | HIGH_RECOIL_F
+ call CheckLoadedMoveFlag
+ ccf
+ ret
+; 0x22bc6
+
+rept $143a
+ db $ff
+endr
diff --git a/src/engine/home.asm b/src/engine/home.asm
index ca0eb19..1ec17fb 100644
--- a/src/engine/home.asm
+++ b/src/engine/home.asm
@@ -4376,7 +4376,7 @@ Func_161e: ; 161e (0:161e)
; 0x16ad
; copies, given a card identified by register a (card ID):
-; - e into wSelectedMoveIndex and d into hTempCardIndex_ff9f
+; - e into wSelectedAttack and d into hTempCardIndex_ff9f
; - Move1 (if e == 0) or Move2 (if e == 1) data into wLoadedMove
; - Also from that move, its Damage field into wDamage
; finally, clears wNoDamageOrEffect and wDealtDamage
@@ -4384,7 +4384,7 @@ CopyMoveDataAndDamage_FromCardID: ; 16ad (0:16ad)
push de
push af
ld a, e
- ld [wSelectedMoveIndex], a
+ ld [wSelectedAttack], a
ld a, d
ldh [hTempCardIndex_ff9f], a
pop af
@@ -4395,13 +4395,13 @@ CopyMoveDataAndDamage_FromCardID: ; 16ad (0:16ad)
jr CopyMoveDataAndDamage
; copies, given a card identified by register d (0-59 deck index):
-; - e into wSelectedMoveIndex and d into hTempCardIndex_ff9f
+; - e into wSelectedAttack and d into hTempCardIndex_ff9f
; - Move1 (if e == 0) or Move2 (if e == 1) data into wLoadedMove
; - Also from that move, its Damage field into wDamage
; finally, clears wNoDamageOrEffect and wDealtDamage
CopyMoveDataAndDamage_FromDeckIndex: ; 16c0 (0:16c0)
ld a, e
- ld [wSelectedMoveIndex], a
+ ld [wSelectedAttack], a
ld a, d
ldh [hTempCardIndex_ff9f], a
call LoadCardDataToBuffer1_FromDeckIndex
@@ -4465,7 +4465,7 @@ Func_16f6: ; 16f6 (0:16f6)
; Use an attack (from DuelMenu_Attack) or a Pokemon Power (from DuelMenu_PkmnPower)
UseAttackOrPokemonPower: ; 1730 (0:1730)
- ld a, [wSelectedMoveIndex]
+ ld a, [wSelectedAttack]
ld [wPlayerAttackingMoveIndex], a
ldh a, [hTempCardIndex_ff9f]
ld [wPlayerAttackingCardIndex], a
@@ -5259,7 +5259,7 @@ MoveCardToDiscardPileIfInArena: ; 1c13 (0:1c13)
ret
; 0x1c35
-; calculate damage of card at CARD_LOCATION_PLAY_AREA + e
+; calculate damage of card at CARD_LOCATION_* in e
; return the result in a
GetCardDamage: ; 1c35 (0:1c35)
push hl
@@ -9858,7 +9858,7 @@ HandleCantAttackSubstatus: ; 33c1 (0:33c1)
ret
; return carry if the turn holder's arena Pokemon cannot use
-; selected move at wSelectedMoveIndex due to amnesia
+; selected move at wSelectedAttack due to amnesia
HandleAmnesiaSubstatus: ; 33e1 (0:33e1)
ld a, DUELVARS_ARENA_CARD_SUBSTATUS2
call GetTurnDuelistVariable
@@ -9874,7 +9874,7 @@ HandleAmnesiaSubstatus: ; 33e1 (0:33e1)
.affected_by_amnesia
ld a, DUELVARS_ARENA_CARD_DISABLED_MOVE_INDEX
call GetTurnDuelistVariable
- ld a, [wSelectedMoveIndex]
+ ld a, [wSelectedAttack]
cp [hl]
jr nz, .not_the_disabled_move
ldtx hl, UnableToUseAttackDueToAmnesiaText
@@ -11248,7 +11248,7 @@ CheckAnyAnimationPlaying: ; 3b52 (0:3b52)
ret
Func_3b6a: ; 3b6a (0:3b6a)
- ld [wd422], a
+ ld [wTempAnimation], a ; hold an animation temporarily
ldh a, [hBankROM]
push af
ld [wd4be], a
@@ -11257,7 +11257,7 @@ Func_3b6a: ; 3b6a (0:3b6a)
push de
ld a, $07
call BankswitchROM
- ld a, [wd422]
+ ld a, [wTempAnimation]
cp $61
jr nc, .asm_3b90
ld hl, wd4ad