diff options
author | dannye <33dannye@gmail.com> | 2021-06-11 18:45:36 -0500 |
---|---|---|
committer | dannye <33dannye@gmail.com> | 2021-06-11 18:45:36 -0500 |
commit | a39a68356236af3850310a64233c3a16edbb92bf (patch) | |
tree | 79c96aa5efa7361496bd43f6a7ca4b2fe0f3e09e /src/engine/ai/attacks.asm | |
parent | 3e36b5fd9d1aa5a5fbe9132bae98b127b82541aa (diff) |
Normalize line endings
Diffstat (limited to 'src/engine/ai/attacks.asm')
-rw-r--r-- | src/engine/ai/attacks.asm | 1442 |
1 files changed, 721 insertions, 721 deletions
diff --git a/src/engine/ai/attacks.asm b/src/engine/ai/attacks.asm index 9f93c33..3b331a6 100644 --- a/src/engine/ai/attacks.asm +++ b/src/engine/ai/attacks.asm @@ -1,721 +1,721 @@ -; have AI choose an attack to use, but do not execute it.
-; return carry if an attack is chosen.
-AIProcessButDontUseAttack: ; 169ca (5:69ca)
- ld a, $01
- ld [wAIExecuteProcessedAttack], a
-
-; backup wPlayAreaAIScore in 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 wTempAIScore
- ld a, [wAIScore]
- ld [de], a
- jr AIProcessAttacks
-
-; copies wTempPlayAreaAIScore to wPlayAreaAIScore
-; and loads wAIScore with value in wTempAIScore.
-; identical to RetrievePlayAreaAIScoreFromBackup1.
-RetrievePlayAreaAIScoreFromBackup2: ; 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
-
-; have AI choose and execute an attack.
-; return carry if an attack was chosen and attempted.
-AIProcessAndTryToUseAttack: ; 169f8 (5:69f8)
- xor a
- ld [wAIExecuteProcessedAttack], a
- ; fallthrough
-
-; checks which of the Active card's attacks for AI to use.
-; If any of the attacks has enough AI score to be used,
-; AI will use it if wAIExecuteProcessedAttack is 0.
-; in either case, return carry if an attack is chosen to be used.
-AIProcessAttacks: ; 169fc (5:69fc)
-; if AI used Pluspower, load its attack index
- ld a, [wPreviousAIFlags]
- and AI_FLAG_USED_PLUSPOWER
- jr z, .no_pluspower
- ld a, [wAIPluspowerAttack]
- ld [wSelectedAttack], a
- jr .attack_chosen
-
-.no_pluspower
-; if Player is running Mewtwo1 mill deck,
-; skip attack if Barrier counter is 0.
- ld a, [wAIBarrierFlagCounter]
- cp AI_MEWTWO_MILL + 0
- jp z, .dont_attack
-
-; determine AI score of both attacks.
- xor a ; FIRST_ATTACK_OR_PKMN_POWER
- call GetAIScoreOfAttack
- ld a, [wAIScore]
- ld [wFirstAttackAIScore], a
- ld a, SECOND_ATTACK
- call GetAIScoreOfAttack
-
-; compare both attack scores
- ld c, SECOND_ATTACK
- ld a, [wFirstAttackAIScore]
- ld b, a
- ld a, [wAIScore]
- cp b
- jr nc, .check_score
- ; 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.
-.check_score
- cp $50 ; minimum score to use attack
- jr c, .dont_attack
- ; enough score, proceed
-
- ld a, c
- ld [wSelectedAttack], a
- or a
- jr z, .attack_chosen
- call CheckWhetherToSwitchToFirstAttack
-
-.attack_chosen
-; check whether to execute the attack chosen
- ld a, [wAIExecuteProcessedAttack]
- or a
- jr z, .execute
-
-; set carry and reset Play Area AI score
-; to the previous values.
- scf
- jp RetrievePlayAreaAIScoreFromBackup2
-
-.execute
- ld a, AI_TRAINER_CARD_PHASE_14
- 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, .check_damage_bench
- ; if damage is not 0, fallthrough
-
-.can_damage
- xor a
- ld [wcdb4], a
- jr .use_attack
-
-.check_damage_bench
-; check if it can otherwise damage player's bench
- ld a, ATTACK_FLAG1_ADDRESS | DAMAGE_TO_OPPONENT_BENCH_F
- call CheckLoadedAttackFlag
- jr c, .can_damage
-
-; cannot damage either Defending Pokemon or Bench
- ld hl, wcdb4
- inc [hl]
-
-; return carry if attack is chosen
-; and AI tries to use it.
-.use_attack
- ld a, $01
- ld [wcddb], a
- call AITryUseAttack
- scf
- ret
-
-.dont_attack
- ld a, [wAIExecuteProcessedAttack]
- or a
- jr z, .failed_to_use
-
-; reset Play Area AI score
-; to the previous values.
- jp RetrievePlayAreaAIScoreFromBackup2
-
-; return no carry if no viable attack.
-.failed_to_use
- ld hl, wcdb4
- inc [hl]
- or a
- ret
-
-; determines the AI score of attack index in a
-; of card in Play Area location hTempPlayAreaLocation_ff9d.
-GetAIScoreOfAttack: ; 16a86 (5:6a86)
-; initialize AI score.
- ld [wSelectedAttack], a
- ld a, $50
- ld [wAIScore], a
-
- xor a
- ldh [hTempPlayAreaLocation_ff9d], a
- call CheckIfSelectedAttackIsUnusable
- 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 attack
-; has a residual effect, or if it can damage the opposing bench.
-; If none of those are true, render the attack 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, [wLoadedAttackCategory]
- cp POKEMON_POWER
- jr z, .unusable
- and RESIDUAL
- jr nz, .check_if_can_ko
- ld a, ATTACK_FLAG1_ADDRESS | DAMAGE_TO_OPPONENT_BENCH_F
- call CheckLoadedAttackFlag
- jr nc, .unusable
-
-; calculate damage to player to check if attack can KO.
-; encourage attack 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 attack deals.
-; if no damage is dealt, subtract AI score. in case wDamage is zero
-; but wMaxDamage is not, then encourage attack afterwards.
-; otherwise, if wMaxDamage is also zero, check for damage against
-; player's bench, and encourage attack in case there is.
-.check_damage
- xor a
- ld [wAIAttackIsNonDamaging], 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 [wAIAttackIsNonDamaging], a
- call SubFromAIScore
- ld a, [wAIMaxDamage]
- or a
- jr z, .no_max_damage
- ld a, 2
- call AddToAIScore
- xor a
- ld [wAIAttackIsNonDamaging], a
-.no_max_damage
- ld a, ATTACK_FLAG1_ADDRESS | DAMAGE_TO_OPPONENT_BENCH_F
- call CheckLoadedAttackFlag
- jr nc, .check_recoil
- ld a, 2
- call AddToAIScore
-
-; handle recoil attacks (low and high recoil).
-.check_recoil
- ld a, ATTACK_FLAG1_ADDRESS | LOW_RECOIL_F
- call CheckLoadedAttackFlag
- jr c, .is_recoil
- ld a, ATTACK_FLAG1_ADDRESS | HIGH_RECOIL_F
- call CheckLoadedAttackFlag
- jp nc, .check_defending_can_ko
-.is_recoil
- ; sub from AI score number of damage counters
- ; that attack deals to itself.
- ld a, [wLoadedAttackEffectParam]
- 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, ATTACK_FLAG1_ADDRESS | HIGH_RECOIL_F
- call CheckLoadedAttackFlag
- 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 attack if no benched Pokémon
- ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
- call GetTurnDuelistVariable
- cp 2
- jr c, .dismiss_high_recoil_atk
- ; has benched Pokémon
-
-; here the AI handles high recoil attacks 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_atk
- ; 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_atk
- xor a
- ld [wAIScore], a
- jp .done
-
-.encourage_high_recoil_atk
- ld a, 20
- call AddToAIScore
- jp .done
-
-; Zapping Selfdestruct deck only uses this attack
-; 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 GetCardDamageAndMaxHP
- 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 attack causes player to win the duel by
-; knocking out own Pokémon, dismiss attack.
- ld a, 1 ; count active Pokémon as KO'd
- call .check_if_kos_bench
- jr c, .dismiss_high_recoil_atk
- jr .encourage_high_recoil_atk
-
-; Rock Crusher Deck only uses this attack if
-; prize count is below 4 and attack 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_atk
- ; 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_atk
-
-; generic checks for all other deck IDs.
-; encourage attack 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
-
-; attack causes player to draw all prize cards
- xor a
- ld [wAIScore], a
- jp .done
-
-; attack 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 attack.
-; return carry if using attack 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 attack,
-; 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 attack
-; unless attack 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, [wAIAttackIsNonDamaging]
- or a
- jr z, .check_discard
- ld a, 5
- call SubFromAIScore
-
-; subtract from AI score if this attack requires
-; discarding any energy cards.
-.check_discard
- ld a, [wSelectedAttack]
- ld e, a
- ld a, DUELVARS_ARENA_CARD
- call GetTurnDuelistVariable
- ld d, a
- call CopyAttackDataAndDamage_FromDeckIndex
- ld a, ATTACK_FLAG2_ADDRESS | DISCARD_ENERGY_F
- call CheckLoadedAttackFlag
- jr nc, .asm_16ca6
- ld a, 1
- call SubFromAIScore
- ld a, [wLoadedAttackEffectParam]
- call SubFromAIScore
-
-.asm_16ca6
- ld a, ATTACK_FLAG2_ADDRESS | FLAG_2_BIT_6_F
- call CheckLoadedAttackFlag
- jr nc, .check_nullify_flag
- ld a, [wLoadedAttackEffectParam]
- call AddToAIScore
-
-; encourage attack if it has a nullify or weaken attack effect.
-.check_nullify_flag
- ld a, ATTACK_FLAG2_ADDRESS | NULLIFY_OR_WEAKEN_ATTACK_F
- call CheckLoadedAttackFlag
- jr nc, .check_draw_flag
- ld a, 1
- call AddToAIScore
-
-; encourage attack if it has an effect to draw a card.
-.check_draw_flag
- ld a, ATTACK_FLAG1_ADDRESS | DRAW_CARD_F
- call CheckLoadedAttackFlag
- jr nc, .check_heal_flag
- ld a, 1
- call AddToAIScore
-
-.check_heal_flag
- ld a, ATTACK_FLAG2_ADDRESS | HEAL_USER_F
- call CheckLoadedAttackFlag
- jr nc, .check_status_effect
- ld a, [wLoadedAttackEffectParam]
- cp 1
- jr z, .tally_heal_score
- ld a, [wTempAI]
- call CalculateByteTensDigit
- ld b, a
- ld a, [wLoadedAttackEffectParam]
- 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 GetCardDamageAndMaxHP
- call CalculateByteTensDigit
- pop bc
- cp b ; wLoadedAttackEffectParam
- 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_special_atks
-
- ld a, DUELVARS_ARENA_CARD_STATUS
- call GetNonTurnDuelistVariable
- ld [wTempAI], a
-
-; encourage a poison inflicting attack if opposing Pokémon
-; isn't (doubly) poisoned already.
-; if opposing Pokémon is only poisoned and not double poisoned,
-; and this attack 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, ATTACK_FLAG1_ADDRESS | INFLICT_POISON_F
- call CheckLoadedAttackFlag
- 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, ATTACK_FLAG2_ADDRESS | FLAG_2_BIT_6_F
- call CheckLoadedAttackFlag
- jr nc, .check_sleep
- ld a, 2
- call SubFromAIScore
- jr .check_sleep
-.add_poison_score
- ld a, 2
- call AddToAIScore
-
-; encourage sleep-inducing attack if other Pokémon isn't asleep.
-.check_sleep
- ld a, ATTACK_FLAG1_ADDRESS | INFLICT_SLEEP_F
- call CheckLoadedAttackFlag
- 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 attack if other Pokémon isn't asleep.
-; otherwise, if other Pokémon is asleep, discourage attack.
-.check_paralysis
- ld a, ATTACK_FLAG1_ADDRESS | INFLICT_PARALYSIS_F
- call CheckLoadedAttackFlag
- 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 attack if other Pokémon isn't asleep
-; or confused already.
-; otherwise, if other Pokémon is asleep or confused,
-; discourage attack instead.
-.check_confusion
- ld a, ATTACK_FLAG1_ADDRESS | INFLICT_CONFUSION_F
- call CheckLoadedAttackFlag
- 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_special_atks
- ld a, 1
- call SubFromAIScore
-
-; SPECIAL_AI_HANDLING marks attacks that the AI handles individually.
-; each attack has its own checks and modifies AI score accordingly.
-.handle_special_atks
- ld a, ATTACK_FLAG3_ADDRESS | SPECIAL_AI_HANDLING_F
- call CheckLoadedAttackFlag
- jr nc, .done
- call HandleSpecialAIAttacks
- 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
+; have AI choose an attack to use, but do not execute it. +; return carry if an attack is chosen. +AIProcessButDontUseAttack: ; 169ca (5:69ca) + ld a, $01 + ld [wAIExecuteProcessedAttack], a + +; backup wPlayAreaAIScore in 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 wTempAIScore + ld a, [wAIScore] + ld [de], a + jr AIProcessAttacks + +; copies wTempPlayAreaAIScore to wPlayAreaAIScore +; and loads wAIScore with value in wTempAIScore. +; identical to RetrievePlayAreaAIScoreFromBackup1. +RetrievePlayAreaAIScoreFromBackup2: ; 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 + +; have AI choose and execute an attack. +; return carry if an attack was chosen and attempted. +AIProcessAndTryToUseAttack: ; 169f8 (5:69f8) + xor a + ld [wAIExecuteProcessedAttack], a + ; fallthrough + +; checks which of the Active card's attacks for AI to use. +; If any of the attacks has enough AI score to be used, +; AI will use it if wAIExecuteProcessedAttack is 0. +; in either case, return carry if an attack is chosen to be used. +AIProcessAttacks: ; 169fc (5:69fc) +; if AI used Pluspower, load its attack index + ld a, [wPreviousAIFlags] + and AI_FLAG_USED_PLUSPOWER + jr z, .no_pluspower + ld a, [wAIPluspowerAttack] + ld [wSelectedAttack], a + jr .attack_chosen + +.no_pluspower +; if Player is running Mewtwo1 mill deck, +; skip attack if Barrier counter is 0. + ld a, [wAIBarrierFlagCounter] + cp AI_MEWTWO_MILL + 0 + jp z, .dont_attack + +; determine AI score of both attacks. + xor a ; FIRST_ATTACK_OR_PKMN_POWER + call GetAIScoreOfAttack + ld a, [wAIScore] + ld [wFirstAttackAIScore], a + ld a, SECOND_ATTACK + call GetAIScoreOfAttack + +; compare both attack scores + ld c, SECOND_ATTACK + ld a, [wFirstAttackAIScore] + ld b, a + ld a, [wAIScore] + cp b + jr nc, .check_score + ; 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. +.check_score + cp $50 ; minimum score to use attack + jr c, .dont_attack + ; enough score, proceed + + ld a, c + ld [wSelectedAttack], a + or a + jr z, .attack_chosen + call CheckWhetherToSwitchToFirstAttack + +.attack_chosen +; check whether to execute the attack chosen + ld a, [wAIExecuteProcessedAttack] + or a + jr z, .execute + +; set carry and reset Play Area AI score +; to the previous values. + scf + jp RetrievePlayAreaAIScoreFromBackup2 + +.execute + ld a, AI_TRAINER_CARD_PHASE_14 + 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, .check_damage_bench + ; if damage is not 0, fallthrough + +.can_damage + xor a + ld [wcdb4], a + jr .use_attack + +.check_damage_bench +; check if it can otherwise damage player's bench + ld a, ATTACK_FLAG1_ADDRESS | DAMAGE_TO_OPPONENT_BENCH_F + call CheckLoadedAttackFlag + jr c, .can_damage + +; cannot damage either Defending Pokemon or Bench + ld hl, wcdb4 + inc [hl] + +; return carry if attack is chosen +; and AI tries to use it. +.use_attack + ld a, $01 + ld [wcddb], a + call AITryUseAttack + scf + ret + +.dont_attack + ld a, [wAIExecuteProcessedAttack] + or a + jr z, .failed_to_use + +; reset Play Area AI score +; to the previous values. + jp RetrievePlayAreaAIScoreFromBackup2 + +; return no carry if no viable attack. +.failed_to_use + ld hl, wcdb4 + inc [hl] + or a + ret + +; determines the AI score of attack index in a +; of card in Play Area location hTempPlayAreaLocation_ff9d. +GetAIScoreOfAttack: ; 16a86 (5:6a86) +; initialize AI score. + ld [wSelectedAttack], a + ld a, $50 + ld [wAIScore], a + + xor a + ldh [hTempPlayAreaLocation_ff9d], a + call CheckIfSelectedAttackIsUnusable + 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 attack +; has a residual effect, or if it can damage the opposing bench. +; If none of those are true, render the attack 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, [wLoadedAttackCategory] + cp POKEMON_POWER + jr z, .unusable + and RESIDUAL + jr nz, .check_if_can_ko + ld a, ATTACK_FLAG1_ADDRESS | DAMAGE_TO_OPPONENT_BENCH_F + call CheckLoadedAttackFlag + jr nc, .unusable + +; calculate damage to player to check if attack can KO. +; encourage attack 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 attack deals. +; if no damage is dealt, subtract AI score. in case wDamage is zero +; but wMaxDamage is not, then encourage attack afterwards. +; otherwise, if wMaxDamage is also zero, check for damage against +; player's bench, and encourage attack in case there is. +.check_damage + xor a + ld [wAIAttackIsNonDamaging], 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 [wAIAttackIsNonDamaging], a + call SubFromAIScore + ld a, [wAIMaxDamage] + or a + jr z, .no_max_damage + ld a, 2 + call AddToAIScore + xor a + ld [wAIAttackIsNonDamaging], a +.no_max_damage + ld a, ATTACK_FLAG1_ADDRESS | DAMAGE_TO_OPPONENT_BENCH_F + call CheckLoadedAttackFlag + jr nc, .check_recoil + ld a, 2 + call AddToAIScore + +; handle recoil attacks (low and high recoil). +.check_recoil + ld a, ATTACK_FLAG1_ADDRESS | LOW_RECOIL_F + call CheckLoadedAttackFlag + jr c, .is_recoil + ld a, ATTACK_FLAG1_ADDRESS | HIGH_RECOIL_F + call CheckLoadedAttackFlag + jp nc, .check_defending_can_ko +.is_recoil + ; sub from AI score number of damage counters + ; that attack deals to itself. + ld a, [wLoadedAttackEffectParam] + 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, ATTACK_FLAG1_ADDRESS | HIGH_RECOIL_F + call CheckLoadedAttackFlag + 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 attack if no benched Pokémon + ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA + call GetTurnDuelistVariable + cp 2 + jr c, .dismiss_high_recoil_atk + ; has benched Pokémon + +; here the AI handles high recoil attacks 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_atk + ; 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_atk + xor a + ld [wAIScore], a + jp .done + +.encourage_high_recoil_atk + ld a, 20 + call AddToAIScore + jp .done + +; Zapping Selfdestruct deck only uses this attack +; 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 GetCardDamageAndMaxHP + 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 attack causes player to win the duel by +; knocking out own Pokémon, dismiss attack. + ld a, 1 ; count active Pokémon as KO'd + call .check_if_kos_bench + jr c, .dismiss_high_recoil_atk + jr .encourage_high_recoil_atk + +; Rock Crusher Deck only uses this attack if +; prize count is below 4 and attack 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_atk + ; 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_atk + +; generic checks for all other deck IDs. +; encourage attack 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 + +; attack causes player to draw all prize cards + xor a + ld [wAIScore], a + jp .done + +; attack 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 attack. +; return carry if using attack 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 attack, +; 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 attack +; unless attack 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, [wAIAttackIsNonDamaging] + or a + jr z, .check_discard + ld a, 5 + call SubFromAIScore + +; subtract from AI score if this attack requires +; discarding any energy cards. +.check_discard + ld a, [wSelectedAttack] + ld e, a + ld a, DUELVARS_ARENA_CARD + call GetTurnDuelistVariable + ld d, a + call CopyAttackDataAndDamage_FromDeckIndex + ld a, ATTACK_FLAG2_ADDRESS | DISCARD_ENERGY_F + call CheckLoadedAttackFlag + jr nc, .asm_16ca6 + ld a, 1 + call SubFromAIScore + ld a, [wLoadedAttackEffectParam] + call SubFromAIScore + +.asm_16ca6 + ld a, ATTACK_FLAG2_ADDRESS | FLAG_2_BIT_6_F + call CheckLoadedAttackFlag + jr nc, .check_nullify_flag + ld a, [wLoadedAttackEffectParam] + call AddToAIScore + +; encourage attack if it has a nullify or weaken attack effect. +.check_nullify_flag + ld a, ATTACK_FLAG2_ADDRESS | NULLIFY_OR_WEAKEN_ATTACK_F + call CheckLoadedAttackFlag + jr nc, .check_draw_flag + ld a, 1 + call AddToAIScore + +; encourage attack if it has an effect to draw a card. +.check_draw_flag + ld a, ATTACK_FLAG1_ADDRESS | DRAW_CARD_F + call CheckLoadedAttackFlag + jr nc, .check_heal_flag + ld a, 1 + call AddToAIScore + +.check_heal_flag + ld a, ATTACK_FLAG2_ADDRESS | HEAL_USER_F + call CheckLoadedAttackFlag + jr nc, .check_status_effect + ld a, [wLoadedAttackEffectParam] + cp 1 + jr z, .tally_heal_score + ld a, [wTempAI] + call CalculateByteTensDigit + ld b, a + ld a, [wLoadedAttackEffectParam] + 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 GetCardDamageAndMaxHP + call CalculateByteTensDigit + pop bc + cp b ; wLoadedAttackEffectParam + 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_special_atks + + ld a, DUELVARS_ARENA_CARD_STATUS + call GetNonTurnDuelistVariable + ld [wTempAI], a + +; encourage a poison inflicting attack if opposing Pokémon +; isn't (doubly) poisoned already. +; if opposing Pokémon is only poisoned and not double poisoned, +; and this attack 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, ATTACK_FLAG1_ADDRESS | INFLICT_POISON_F + call CheckLoadedAttackFlag + 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, ATTACK_FLAG2_ADDRESS | FLAG_2_BIT_6_F + call CheckLoadedAttackFlag + jr nc, .check_sleep + ld a, 2 + call SubFromAIScore + jr .check_sleep +.add_poison_score + ld a, 2 + call AddToAIScore + +; encourage sleep-inducing attack if other Pokémon isn't asleep. +.check_sleep + ld a, ATTACK_FLAG1_ADDRESS | INFLICT_SLEEP_F + call CheckLoadedAttackFlag + 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 attack if other Pokémon isn't asleep. +; otherwise, if other Pokémon is asleep, discourage attack. +.check_paralysis + ld a, ATTACK_FLAG1_ADDRESS | INFLICT_PARALYSIS_F + call CheckLoadedAttackFlag + 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 attack if other Pokémon isn't asleep +; or confused already. +; otherwise, if other Pokémon is asleep or confused, +; discourage attack instead. +.check_confusion + ld a, ATTACK_FLAG1_ADDRESS | INFLICT_CONFUSION_F + call CheckLoadedAttackFlag + 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_special_atks + ld a, 1 + call SubFromAIScore + +; SPECIAL_AI_HANDLING marks attacks that the AI handles individually. +; each attack has its own checks and modifies AI score accordingly. +.handle_special_atks + ld a, ATTACK_FLAG3_ADDRESS | SPECIAL_AI_HANDLING_F + call CheckLoadedAttackFlag + jr nc, .done + call HandleSpecialAIAttacks + 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 |