diff options
author | Remy Oukaour <remy.oukaour@gmail.com> | 2017-12-26 17:47:05 -0500 |
---|---|---|
committer | Remy Oukaour <remy.oukaour@gmail.com> | 2017-12-26 17:47:05 -0500 |
commit | b5417fafec7dd37cb4be391f3bd3d4541a2a381e (patch) | |
tree | a4e7d08afb2e862186a138e82c8ef4785d82786d /engine/battle/ai | |
parent | 2f98c2032fd47ada3484bfc37d590992f286d3d4 (diff) |
Split battle/ into data/ and engine/ components
Diffstat (limited to 'engine/battle/ai')
-rw-r--r-- | engine/battle/ai/items.asm | 882 | ||||
-rwxr-xr-x | engine/battle/ai/move.asm | 221 | ||||
-rwxr-xr-x | engine/battle/ai/redundant.asm | 198 | ||||
-rw-r--r-- | engine/battle/ai/scoring.asm | 3598 | ||||
-rwxr-xr-x | engine/battle/ai/switch.asm | 672 |
5 files changed, 5571 insertions, 0 deletions
diff --git a/engine/battle/ai/items.asm b/engine/battle/ai/items.asm new file mode 100644 index 000000000..09595077a --- /dev/null +++ b/engine/battle/ai/items.asm @@ -0,0 +1,882 @@ +AI_SwitchOrTryItem: ; 38000 + and a + + ld a, [wBattleMode] + dec a + ret z + + ld a, [wLinkMode] + and a + ret nz + + farcall CheckEnemyLockedIn + ret nz + + ld a, [PlayerSubStatus5] + bit SUBSTATUS_CANT_RUN, a + jr nz, DontSwitch + + ld a, [wEnemyWrapCount] + and a + jr nz, DontSwitch + + ld hl, TrainerClassAttributes + TRNATTR_AI_ITEM_SWITCH + ld a, [InBattleTowerBattle] ; Load always the first TrainerClass for BattleTower-Trainers + and a + jr nz, .ok + + ld a, [TrainerClass] + dec a + ld bc, NUM_TRAINER_ATTRIBUTES + call AddNTimes +.ok + bit SWITCH_OFTEN_F, [hl] + jp nz, SwitchOften + bit SWITCH_RARELY_F, [hl] + jp nz, SwitchRarely + bit SWITCH_SOMETIMES_F, [hl] + jp nz, SwitchSometimes + ; fallthrough + +DontSwitch: ; 38041 + call AI_TryItem + ret +; 38045 + +SwitchOften: ; 38045 + callfar CheckAbleToSwitch + ld a, [wEnemySwitchMonParam] + and $f0 + jp z, DontSwitch + + cp $10 + jr nz, .not_10 + call Random + cp 1 + 50 percent + jr c, .switch + jp DontSwitch +.not_10 + + cp $20 + jr nz, .not_20 + call Random + cp -1 + 79 percent + jr c, .switch + jp DontSwitch +.not_20 + + ; $30 + call Random + cp 4 percent + jp c, DontSwitch + +.switch + ld a, [wEnemySwitchMonParam] + and $f + inc a + ; In register 'a' is the number (1-6) of the Pkmn to switch to + ld [wEnemySwitchMonIndex], a + jp AI_TrySwitch +; 38083 + +SwitchRarely: ; 38083 + callfar CheckAbleToSwitch + ld a, [wEnemySwitchMonParam] + and $f0 + jp z, DontSwitch + + cp $10 + jr nz, .not_10 + call Random + cp 8 percent + jr c, .switch + jp DontSwitch +.not_10 + + cp $20 + jr nz, .not_20 + call Random + cp 12 percent + jr c, .switch + jp DontSwitch +.not_20 + + ; $30 + call Random + cp -1 + 79 percent + jp c, DontSwitch + +.switch + ld a, [wEnemySwitchMonParam] + and $f + inc a + ld [wEnemySwitchMonIndex], a + jp AI_TrySwitch +; 380c1 + +SwitchSometimes: ; 380c1 + callfar CheckAbleToSwitch + ld a, [wEnemySwitchMonParam] + and $f0 + jp z, DontSwitch + + cp $10 + jr nz, .not_10 + call Random + cp -1 + 20 percent + jr c, .switch + jp DontSwitch +.not_10 + + cp $20 + jr nz, .not_20 + call Random + cp 1 + 50 percent + jr c, .switch + jp DontSwitch +.not_20 + + ; $30 + call Random + cp -1 + 20 percent + jp c, DontSwitch + +.switch + ld a, [wEnemySwitchMonParam] + and $f + inc a + ld [wEnemySwitchMonIndex], a + jp AI_TrySwitch +; 380ff + + +CheckSubstatusCantRun: ; 380ff + ld a, [EnemySubStatus5] + bit SUBSTATUS_CANT_RUN, a + ret +; 38105 + + +AI_TryItem: ; 38105 + ; items are not allowed in the BattleTower + ld a, [InBattleTowerBattle] + and a + ret nz + + ld a, [wEnemyTrainerItem1] + ld b, a + ld a, [wEnemyTrainerItem2] + or b + ret z + + call .IsHighestLevel + ret nc + + ld a, [TrainerClass] + dec a + ld hl, TrainerClassAttributes + TRNATTR_AI_ITEM_SWITCH + ld bc, NUM_TRAINER_ATTRIBUTES + call AddNTimes + ld b, h + ld c, l + ld hl, AI_Items + ld de, wEnemyTrainerItem1 +.loop + ld a, [hl] + and a + inc a + ret z + + ld a, [de] + cp [hl] + jr z, .has_item + inc de + ld a, [de] + cp [hl] + jr z, .has_item + + dec de + inc hl + inc hl + inc hl + jr .loop + +.has_item + inc hl + + push hl + push de + ld de, .callback + push de + ld a, [hli] + ld h, [hl] + ld l, a + jp hl +.callback + pop de + pop hl + + inc hl + inc hl + jr c, .loop + +.used_item + xor a + ld [de], a + inc a + ld [wEnemyGoesFirst], a + + ld hl, EnemySubStatus3 + res SUBSTATUS_BIDE, [hl] + + xor a + ld [EnemyFuryCutterCount], a + ld [EnemyProtectCount], a + ld [wEnemyRageCounter], a + + ld hl, EnemySubStatus4 + res SUBSTATUS_RAGE, [hl] + + xor a + ld [LastEnemyCounterMove], a + + scf + ret + + +.IsHighestLevel: ; 38170 + ld a, [OTPartyCount] + ld d, a + ld e, 0 + ld hl, OTPartyMon1Level + ld bc, PARTYMON_STRUCT_LENGTH +.next + ld a, [hl] + cp e + jr c, .ok + ld e, a +.ok + add hl, bc + dec d + jr nz, .next + + ld a, [CurOTMon] + ld hl, OTPartyMon1Level + call AddNTimes + ld a, [hl] + cp e + jr nc, .yes + +.no + and a + ret + +.yes + scf + ret +; 38196 + + +AI_Items: ; 39196 + dbw FULL_RESTORE, .FullRestore + dbw MAX_POTION, .MaxPotion + dbw HYPER_POTION, .HyperPotion + dbw SUPER_POTION, .SuperPotion + dbw POTION, .Potion + dbw X_ACCURACY, .XAccuracy + dbw FULL_HEAL, .FullHeal + dbw GUARD_SPEC, .GuardSpec + dbw DIRE_HIT, .DireHit + dbw X_ATTACK, .XAttack + dbw X_DEFEND, .XDefend + dbw X_SPEED, .XSpeed + dbw X_SPECIAL, .XSpecial + db $ff +; 381be + +.FullHeal: ; 381be + call .Status + jp c, .DontUse + call EnemyUsedFullHeal + jp .Use +; 381ca + +.Status: ; 381ca (e:41ca) + ld a, [EnemyMonStatus] + and a + jp z, .DontUse + + ld a, [bc] + bit CONTEXT_USE_F, a + jr nz, .StatusCheckContext + ld a, [bc] + bit ALWAYS_USE_F, a + jp nz, .Use + call Random + cp -1 + 20 percent + jp c, .Use + jp .DontUse + +.StatusCheckContext: + ld a, [EnemySubStatus5] + bit SUBSTATUS_TOXIC, a + jr z, .FailToxicCheck + ld a, [EnemyToxicCount] + cp 4 + jr c, .FailToxicCheck + call Random + cp 1 + 50 percent + jp c, .Use +.FailToxicCheck: + ld a, [EnemyMonStatus] + and 1 << FRZ | SLP + jp z, .DontUse + jp .Use +; 38208 + +.FullRestore: ; 38208 + call .HealItem + jp nc, .UseFullRestore + ld a, [bc] + bit CONTEXT_USE_F, a + jp z, .DontUse + call .Status + jp c, .DontUse + +.UseFullRestore: + call EnemyUsedFullRestore + jp .Use +; 38220 + +.MaxPotion: ; 38220 + call .HealItem + jp c, .DontUse + call EnemyUsedMaxPotion + jp .Use + +.HealItem: ; 3822c (e:422c) + ld a, [bc] + bit CONTEXT_USE_F, a + jr nz, .CheckHalfOrQuarterHP + callfar AICheckEnemyHalfHP + jp c, .DontUse + ld a, [bc] + bit UNKNOWN_USE_F, a + jp nz, .CheckQuarterHP + callfar AICheckEnemyQuarterHP + jp nc, .UseHealItem + call Random + cp 1 + 50 percent + jp c, .UseHealItem + jp .DontUse + +.CheckQuarterHP: ; 38254 (e:4254) + callfar AICheckEnemyQuarterHP + jp c, .DontUse + call Random + cp -1 + 20 percent + jp c, .DontUse + jr .UseHealItem + +.CheckHalfOrQuarterHP: ; 38267 (e:4267) + callfar AICheckEnemyHalfHP + jp c, .DontUse + callfar AICheckEnemyQuarterHP + jp nc, .UseHealItem + call Random + cp -1 + 20 percent + jp nc, .DontUse + +.UseHealItem: ; 38281 (e:4281) + jp .Use +; 38284 + +.HyperPotion: ; 38284 + call .HealItem + jp c, .DontUse + ld b, 200 + call EnemyUsedHyperPotion + jp .Use +; 38292 (e:4292) + +.SuperPotion: ; 38292 + call .HealItem + jp c, .DontUse + ld b, 50 + call EnemyUsedSuperPotion + jp .Use +; 382a0 + +.Potion: ; 382a0 + call .HealItem + jp c, .DontUse + ld b, 20 + call EnemyUsedPotion + jp .Use +; 382ae + +.asm_382ae ; This appears to be unused + callfar AICheckEnemyMaxHP + jr c, .dont_use + push bc + ld de, EnemyMonMaxHP + 1 + ld hl, EnemyMonHP + 1 + ld a, [de] + sub [hl] + jr z, .check_40_percent + dec hl + dec de + ld c, a + sbc [hl] + and a + jr nz, .check_40_percent + ld a, c + cp b + jp c, .check_50_percent + callfar AICheckEnemyQuarterHP + jr c, .check_40_percent + +.check_50_percent + pop bc + ld a, [bc] + bit UNKNOWN_USE_F, a + jp z, .Use + call Random + cp 1 + 50 percent + jp c, .Use + +.dont_use + jp .DontUse + +.check_40_percent + pop bc + ld a, [bc] + bit UNKNOWN_USE_F, a + jp z, .DontUse + call Random + cp 1 + 39 percent + jp c, .Use + jp .DontUse +; 382f9 + +.XAccuracy: ; 382f9 + call .XItem + jp c, .DontUse + call EnemyUsedXAccuracy + jp .Use +; 38305 + +.GuardSpec: ; 38305 + call .XItem + jp c, .DontUse + call EnemyUsedGuardSpec + jp .Use +; 38311 + +.DireHit: ; 38311 + call .XItem + jp c, .DontUse + call EnemyUsedDireHit + jp .Use +; 3831d (e:431d) + +.XAttack: ; 3831d + call .XItem + jp c, .DontUse + call EnemyUsedXAttack + jp .Use +; 38329 + +.XDefend: ; 38329 + call .XItem + jp c, .DontUse + call EnemyUsedXDefend + jp .Use +; 38335 + +.XSpeed: ; 38335 + call .XItem + jp c, .DontUse + call EnemyUsedXSpeed + jp .Use +; 38341 + +.XSpecial: ; 38341 + call .XItem + jp c, .DontUse + call EnemyUsedXSpecial + jp .Use +; 3834d + +.XItem: ; 3834d (e:434d) + ld a, [EnemyTurnsTaken] + and a + jr nz, .notfirstturnout + ld a, [bc] + bit ALWAYS_USE_F, a + jp nz, .Use + call Random + cp 1 + 50 percent + jp c, .DontUse + ld a, [bc] + bit CONTEXT_USE_F, a + jp nz, .Use + call Random + cp 1 + 50 percent + jp c, .DontUse + jp .Use +.notfirstturnout + ld a, [bc] + bit ALWAYS_USE_F, a + jp z, .DontUse + call Random + cp -1 + 20 percent + jp nc, .DontUse + jp .Use + +.DontUse: + scf + ret + +.Use: + and a + ret + + +AIUpdateHUD: ; 38387 + call UpdateEnemyMonInParty + farcall UpdateEnemyHUD + ld a, $1 + ld [hBGMapMode], a + ld hl, wEnemyItemState + dec [hl] + scf + ret +; 3839a + +AIUsedItemSound: ; 3839a + push de + ld de, SFX_FULL_HEAL + call PlaySFX + pop de + ret +; 383a3 + + +EnemyUsedFullHeal: ; 383a3 (e:43a3) + call AIUsedItemSound + call AI_HealStatus + ld a, FULL_HEAL + jp PrintText_UsedItemOn_AND_AIUpdateHUD + +EnemyUsedMaxPotion: ; 383ae (e:43ae) + ld a, MAX_POTION + ld [CurEnemyItem], a + jr FullRestoreContinue + +EnemyUsedFullRestore: ; 383b5 (e:43b5) + call AI_HealStatus + ld a, FULL_RESTORE + ld [CurEnemyItem], a + ld hl, EnemySubStatus3 + res SUBSTATUS_CONFUSED, [hl] + xor a + ld [EnemyConfuseCount], a + +FullRestoreContinue: ; 383c6 + ld de, wCurHPAnimOldHP + ld hl, EnemyMonHP + 1 + ld a, [hld] + ld [de], a + inc de + ld a, [hl] + ld [de], a + inc de + ld hl, EnemyMonMaxHP + 1 + ld a, [hld] + ld [de], a + inc de + ld [wCurHPAnimMaxHP], a + ld [EnemyMonHP + 1], a + ld a, [hl] + ld [de], a + ld [wCurHPAnimMaxHP + 1], a + ld [EnemyMonHP], a + jr EnemyPotionFinish +; 383e8 (e:43e8) + +EnemyUsedPotion: ; 383e8 + ld a, POTION + ld b, 20 + jr EnemyPotionContinue + +EnemyUsedSuperPotion: ; 383ee + ld a, SUPER_POTION + ld b, 50 + jr EnemyPotionContinue + +EnemyUsedHyperPotion: ; 383f4 (e:43f4) + ld a, HYPER_POTION + ld b, 200 + +EnemyPotionContinue: ; 383f8 + ld [CurEnemyItem], a + ld hl, EnemyMonHP + 1 + ld a, [hl] + ld [wCurHPAnimOldHP], a + add b + ld [hld], a + ld [wCurHPAnimNewHP], a + ld a, [hl] + ld [wCurHPAnimOldHP + 1], a + ld [wCurHPAnimNewHP + 1], a + jr nc, .ok + inc a + ld [hl], a + ld [wCurHPAnimNewHP + 1], a +.ok + inc hl + ld a, [hld] + ld b, a + ld de, EnemyMonMaxHP + 1 + ld a, [de] + dec de + ld [wCurHPAnimMaxHP], a + sub b + ld a, [hli] + ld b, a + ld a, [de] + ld [wCurHPAnimMaxHP + 1], a + sbc b + jr nc, EnemyPotionFinish + inc de + ld a, [de] + dec de + ld [hld], a + ld [wCurHPAnimNewHP], a + ld a, [de] + ld [hl], a + ld [wCurHPAnimNewHP + 1], a + +EnemyPotionFinish: ; 38436 + call PrintText_UsedItemOn + hlcoord 2, 2 + xor a + ld [wWhichHPBar], a + call AIUsedItemSound + predef AnimateHPBar + jp AIUpdateHUD + + +AI_TrySwitch: ; 3844b +; Determine whether the AI can switch based on how many Pokemon are still alive. +; If it can switch, it will. + ld a, [OTPartyCount] + ld c, a + ld hl, OTPartyMon1HP + ld d, 0 +.SwitchLoop: + ld a, [hli] + ld b, a + ld a, [hld] + or b + jr z, .fainted + inc d +.fainted + push bc + ld bc, PARTYMON_STRUCT_LENGTH + add hl, bc + pop bc + dec c + jr nz, .SwitchLoop + + ld a, d + cp 2 + jp nc, AI_Switch + and a + ret +; 3846c + +AI_Switch: ; 3846c + ld a, $1 + ld [wEnemyIsSwitching], a + ld [wEnemyGoesFirst], a + ld hl, EnemySubStatus4 + res SUBSTATUS_RAGE, [hl] + xor a + ld [hBattleTurn], a + callfar PursuitSwitch + + push af + ld a, [CurOTMon] + ld hl, OTPartyMon1Status + ld bc, PARTYMON_STRUCT_LENGTH + call AddNTimes + ld d, h + ld e, l + ld hl, EnemyMonStatus + ld bc, MON_MAXHP - MON_STATUS + call CopyBytes + pop af + + jr c, .skiptext + ld hl, TextJump_EnemyWithdrew + call PrintText + +.skiptext + ld a, 1 + ld [wBattleHasJustStarted], a + callfar NewEnemyMonStatus + callfar ResetEnemyStatLevels + ld hl, PlayerSubStatus1 + res SUBSTATUS_IN_LOVE, [hl] + farcall EnemySwitch + farcall ResetBattleParticipants + xor a + ld [wBattleHasJustStarted], a + ld a, [wLinkMode] + and a + ret nz + scf + ret +; 384d0 + +TextJump_EnemyWithdrew: ; 384d0 + text_jump Text_EnemyWithdrew + db "@" +; 384d5 + +Function384d5: ; This appears to be unused + call AIUsedItemSound + call AI_HealStatus + ld a, FULL_HEAL_RED ; X_SPEED + jp PrintText_UsedItemOn_AND_AIUpdateHUD +; 384e0 + +AI_HealStatus: ; 384e0 + ld a, [CurOTMon] + ld hl, OTPartyMon1Status + ld bc, PARTYMON_STRUCT_LENGTH + call AddNTimes + xor a + ld [hl], a + ld [EnemyMonStatus], a + ; Bug: this should reset SUBSTATUS_NIGHTMARE too + ; Uncomment the lines below to fix + ; ld hl, EnemySubStatus1 + ; res SUBSTATUS_NIGHTMARE, [hl] + ld hl, EnemySubStatus5 + res SUBSTATUS_TOXIC, [hl] + ret +; 384f7 + +EnemyUsedXAccuracy: ; 384f7 + call AIUsedItemSound + ld hl, EnemySubStatus4 + set SUBSTATUS_X_ACCURACY, [hl] + ld a, X_ACCURACY + jp PrintText_UsedItemOn_AND_AIUpdateHUD +; 38504 + +EnemyUsedGuardSpec: ; 38504 + call AIUsedItemSound + ld hl, EnemySubStatus4 + set SUBSTATUS_MIST, [hl] + ld a, GUARD_SPEC + jp PrintText_UsedItemOn_AND_AIUpdateHUD +; 38511 + +EnemyUsedDireHit: ; 38511 + call AIUsedItemSound + ld hl, EnemySubStatus4 + set SUBSTATUS_FOCUS_ENERGY, [hl] + ld a, DIRE_HIT + jp PrintText_UsedItemOn_AND_AIUpdateHUD +; 3851e + +Function3851e: ; This appears to be unused + ld [hDivisor], a + ld hl, EnemyMonMaxHP + ld a, [hli] + ld [hDividend], a + ld a, [hl] + ld [hDividend + 1], a + ld b, 2 + call Divide + ld a, [hQuotient + 2] + ld c, a + ld a, [hQuotient + 1] + ld b, a + ld hl, EnemyMonHP + 1 + ld a, [hld] + ld e, a + ld a, [hl] + ld d, a + ld a, d + sub b + ret nz + ld a, e + sub c + ret +; 38541 + +EnemyUsedXAttack: ; 38541 + ld b, ATTACK + ld a, X_ATTACK + jr EnemyUsedXItem +; 38547 + +EnemyUsedXDefend: ; 38547 + ld b, DEFENSE + ld a, X_DEFEND + jr EnemyUsedXItem +; 3854d + +EnemyUsedXSpeed: ; 3854d + ld b, SPEED + ld a, X_SPEED + jr EnemyUsedXItem +; 38553 + +EnemyUsedXSpecial: ; 38553 + ld b, SP_ATTACK + ld a, X_SPECIAL + + +; Parameter +; a = ITEM_CONSTANT +; b = BATTLE_CONSTANT (ATTACK, DEFENSE, SPEED, SP_ATTACK, SP_DEFENSE, ACCURACY, EVASION) +EnemyUsedXItem: + ld [CurEnemyItem], a + push bc + call PrintText_UsedItemOn + pop bc + farcall CheckIfStatCanBeRaised + jp AIUpdateHUD +; 38568 + + +; Parameter +; a = ITEM_CONSTANT +PrintText_UsedItemOn_AND_AIUpdateHUD: ; 38568 + ld [CurEnemyItem], a + call PrintText_UsedItemOn + jp AIUpdateHUD +; 38571 + +PrintText_UsedItemOn: ; 38571 + ld a, [CurEnemyItem] + ld [wd265], a + call GetItemName + ld hl, StringBuffer1 + ld de, wMonOrItemNameBuffer + ld bc, ITEM_NAME_LENGTH + call CopyBytes + ld hl, TextJump_EnemyUsedOn + jp PrintText +; 3858c + +TextJump_EnemyUsedOn: ; 3858c + text_jump Text_EnemyUsedOn + db "@" +; 38591 diff --git a/engine/battle/ai/move.asm b/engine/battle/ai/move.asm new file mode 100755 index 000000000..11586c0da --- /dev/null +++ b/engine/battle/ai/move.asm @@ -0,0 +1,221 @@ +AIChooseMove: ; 440ce +; Score each move in EnemyMonMoves starting from Buffer1. Lower is better. +; Pick the move with the lowest score. + +; Wildmons attack at random. + ld a, [wBattleMode] + dec a + ret z + + ld a, [wLinkMode] + and a + ret nz + +; No use picking a move if there's no choice. + farcall CheckEnemyLockedIn + ret nz + + +; The default score is 20. Unusable moves are given a score of 80. + ld a, 20 + ld hl, Buffer1 + ld [hli], a + ld [hli], a + ld [hli], a + ld [hl], a + +; Don't pick disabled moves. + ld a, [EnemyDisabledMove] + and a + jr z, .CheckPP + + ld hl, EnemyMonMoves + ld c, 0 +.CheckDisabledMove: + cp [hl] + jr z, .ScoreDisabledMove + inc c + inc hl + jr .CheckDisabledMove +.ScoreDisabledMove: + ld hl, Buffer1 + ld b, 0 + add hl, bc + ld [hl], 80 + +; Don't pick moves with 0 PP. +.CheckPP: + ld hl, Buffer1 - 1 + ld de, EnemyMonPP + ld b, 0 +.CheckMovePP: + inc b + ld a, b + cp EnemyMonMovesEnd - EnemyMonMoves + 1 + jr z, .ApplyLayers + inc hl + ld a, [de] + inc de + and $3f + jr nz, .CheckMovePP + ld [hl], 80 + jr .CheckMovePP + + +; Apply AI scoring layers depending on the trainer class. +.ApplyLayers: + ld hl, TrainerClassAttributes + TRNATTR_AI_MOVE_WEIGHTS + + ; If we have a battle in BattleTower just load the Attributes of the first TrainerClass (Falkner) + ; so we have always the same AI, regardless of the loaded class of trainer + ld a, [InBattleTowerBattle] + bit 0, a + jr nz, .battle_tower_skip + + ld a, [TrainerClass] + dec a + ld bc, 7 ; Trainer2AI - Trainer1AI + call AddNTimes + +.battle_tower_skip + lb bc, CHECK_FLAG, 0 + push bc + push hl + +.CheckLayer: + pop hl + pop bc + + ld a, c + cp 16 ; up to 16 scoring layers + jr z, .DecrementScores + + push bc + ld d, BANK(TrainerClassAttributes) + predef FlagPredef + ld d, c + pop bc + + inc c + push bc + push hl + + ld a, d + and a + jr z, .CheckLayer + + ld hl, AIScoringPointers + dec c + ld b, 0 + add hl, bc + add hl, bc + ld a, [hli] + ld h, [hl] + ld l, a + ld a, BANK(AIScoring) + call FarCall_hl + + jr .CheckLayer + +; Decrement the scores of all moves one by one until one reaches 0. +.DecrementScores: + ld hl, Buffer1 + ld de, EnemyMonMoves + ld c, EnemyMonMovesEnd - EnemyMonMoves + +.DecrementNextScore: + ; If the enemy has no moves, this will infinite. + ld a, [de] + inc de + and a + jr z, .DecrementScores + + ; We are done whenever a score reaches 0 + dec [hl] + jr z, .PickLowestScoreMoves + + ; If we just decremented the fourth move's score, go back to the first move + inc hl + dec c + jr z, .DecrementScores + + jr .DecrementNextScore + +; In order to avoid bias towards the moves located first in memory, increment the scores +; that were decremented one more time than the rest (in case there was a tie). +; This means that the minimum score will be 1. +.PickLowestScoreMoves: + ld a, c + +.move_loop + inc [hl] + dec hl + inc a + cp NUM_MOVES + 1 + jr nz, .move_loop + + ld hl, Buffer1 + ld de, EnemyMonMoves + ld c, NUM_MOVES + +; Give a score of 0 to a blank move +.loop2 + ld a, [de] + and a + jr nz, .skip_load + ld [hl], a + +; Disregard the move if its score is not 1 +.skip_load + ld a, [hl] + dec a + jr z, .keep + xor a + ld [hli], a + jr .after_toss + +.keep + ld a, [de] + ld [hli], a +.after_toss + inc de + dec c + jr nz, .loop2 + +; Randomly choose one of the moves with a score of 1 +.ChooseMove: + ld hl, Buffer1 + call Random + and 3 + ld c, a + ld b, 0 + add hl, bc + ld a, [hl] + and a + jr z, .ChooseMove + + ld [CurEnemyMove], a + ld a, c + ld [CurEnemyMoveNum], a + ret +; 441af + + +AIScoringPointers: ; 441af + dw AI_Basic + dw AI_Setup + dw AI_Types + dw AI_Offensive + dw AI_Smart + dw AI_Opportunist + dw AI_Aggressive + dw AI_Cautious + dw AI_Status + dw AI_Risky + dw AI_None + dw AI_None + dw AI_None + dw AI_None + dw AI_None + dw AI_None +; 441cf diff --git a/engine/battle/ai/redundant.asm b/engine/battle/ai/redundant.asm new file mode 100755 index 000000000..2e8f7c6df --- /dev/null +++ b/engine/battle/ai/redundant.asm @@ -0,0 +1,198 @@ +AI_Redundant: ; 2c41a +; Check if move effect c will fail because it's already been used. +; Return z if the move is a good choice. +; Return nz if the move is a bad choice. + ld a, c + ld de, 3 + ld hl, .Moves + call IsInArray + jp nc, .NotRedundant + inc hl + ld a, [hli] + ld h, [hl] + ld l, a + jp hl + +.Moves: ; 2c42c + dbw EFFECT_DREAM_EATER, .DreamEater + dbw EFFECT_HEAL, .Heal + dbw EFFECT_LIGHT_SCREEN, .LightScreen + dbw EFFECT_MIST, .Mist + dbw EFFECT_FOCUS_ENERGY, .FocusEnergy + dbw EFFECT_CONFUSE, .Confuse + dbw EFFECT_TRANSFORM, .Transform + dbw EFFECT_REFLECT, .Reflect + dbw EFFECT_SUBSTITUTE, .Substitute + dbw EFFECT_LEECH_SEED, .LeechSeed + dbw EFFECT_DISABLE, .Disable + dbw EFFECT_ENCORE, .Encore + dbw EFFECT_SNORE, .Snore + dbw EFFECT_SLEEP_TALK, .SleepTalk + dbw EFFECT_MEAN_LOOK, .MeanLook + dbw EFFECT_NIGHTMARE, .Nightmare + dbw EFFECT_SPIKES, .Spikes + dbw EFFECT_FORESIGHT, .Foresight + dbw EFFECT_PERISH_SONG, .PerishSong + dbw EFFECT_SANDSTORM, .Sandstorm + dbw EFFECT_ATTRACT, .Attract + dbw EFFECT_SAFEGUARD, .Safeguard + dbw EFFECT_RAIN_DANCE, .RainDance + dbw EFFECT_SUNNY_DAY, .SunnyDay + dbw EFFECT_TELEPORT, .Teleport + dbw EFFECT_MORNING_SUN, .MorningSun + dbw EFFECT_SYNTHESIS, .Synthesis + dbw EFFECT_MOONLIGHT, .Moonlight + dbw EFFECT_SWAGGER, .Swagger + dbw EFFECT_FUTURE_SIGHT, .FutureSight + db -1 + +.LightScreen: ; 2c487 + ld a, [EnemyScreens] + bit SCREENS_LIGHT_SCREEN, a + ret + +.Mist: ; 2c48d + ld a, [EnemySubStatus4] + bit SUBSTATUS_MIST, a + ret + +.FocusEnergy: ; 2c493 + ld a, [EnemySubStatus4] + bit SUBSTATUS_FOCUS_ENERGY, a + ret + +.Confuse: ; 2c499 + ld a, [PlayerSubStatus3] + bit SUBSTATUS_CONFUSED, a + ret nz + ld a, [PlayerScreens] + bit SCREENS_SAFEGUARD, a + ret + +.Transform: ; 2c4a5 + ld a, [EnemySubStatus5] + bit SUBSTATUS_TRANSFORMED, a + ret + +.Reflect: ; 2c4ab + ld a, [EnemyScreens] + bit SCREENS_REFLECT, a + ret + +.Substitute: ; 2c4b1 + ld a, [EnemySubStatus4] + bit SUBSTATUS_SUBSTITUTE, a + ret + +.LeechSeed: ; 2c4b7 + ld a, [PlayerSubStatus4] + bit SUBSTATUS_LEECH_SEED, a + ret + +.Disable: ; 2c4bd + ld a, [PlayerDisableCount] + and a + ret + +.Encore: ; 2c4c2 + ld a, [PlayerSubStatus5] + bit SUBSTATUS_ENCORED, a + ret + +.Snore: +.SleepTalk: ; 2c4c8 + ld a, [EnemyMonStatus] + and SLP + jr z, .Redundant + jr .NotRedundant + +.MeanLook: ; 2c4d1 + ld a, [EnemySubStatus5] + bit SUBSTATUS_CANT_RUN, a + ret + +.Nightmare: ; 2c4d7 + ld a, [BattleMonStatus] + and a + jr z, .Redundant + ld a, [PlayerSubStatus1] + bit SUBSTATUS_NIGHTMARE, a + ret + +.Spikes: ; 2c4e3 + ld a, [PlayerScreens] + bit SCREENS_SPIKES, a + ret + +.Foresight: ; 2c4e9 + ld a, [PlayerSubStatus1] + bit SUBSTATUS_IDENTIFIED, a + ret + +.PerishSong: ; 2c4ef + ld a, [PlayerSubStatus1] + bit SUBSTATUS_PERISH, a + ret + +.Sandstorm: ; 2c4f5 + ld a, [Weather] + cp WEATHER_SANDSTORM + jr z, .Redundant + jr .NotRedundant + +.Attract: ; 2c4fe + farcall CheckOppositeGender + jr c, .Redundant + ld a, [PlayerSubStatus1] + bit SUBSTATUS_IN_LOVE, a + ret + +.Safeguard: ; 2c50c + ld a, [EnemyScreens] + bit SCREENS_SAFEGUARD, a + ret + +.RainDance: ; 2c512 + ld a, [Weather] + cp WEATHER_RAIN + jr z, .Redundant + jr .NotRedundant + +.SunnyDay: ; 2c51b + ld a, [Weather] + cp WEATHER_SUN + jr z, .Redundant + jr .NotRedundant + +.DreamEater: ; 2c524 + ld a, [BattleMonStatus] + and SLP + jr z, .Redundant + jr .NotRedundant + +.Swagger: ; 2c52d + ld a, [PlayerSubStatus3] + bit SUBSTATUS_CONFUSED, a + ret + +.FutureSight: ; 2c533 + ld a, [EnemyScreens] + bit 5, a + ret + +.Heal: +.MorningSun: +.Synthesis: +.Moonlight: ; 2c539 + farcall AICheckEnemyMaxHP + jr nc, .NotRedundant + +.Teleport: +.Redundant: ; 2c541 + ld a, 1 + and a + ret + +.NotRedundant: ; 2c545 + xor a + ret diff --git a/engine/battle/ai/scoring.asm b/engine/battle/ai/scoring.asm new file mode 100644 index 000000000..44194d6f7 --- /dev/null +++ b/engine/battle/ai/scoring.asm @@ -0,0 +1,3598 @@ +AIScoring: ; 38591 + +AI_Basic: ; 38591 +; Don't do anything redundant: +; -Using status-only moves if the player can't be statused +; -Using moves that fail if they've already been used + + ld hl, Buffer1 - 1 + ld de, EnemyMonMoves + ld b, EnemyMonMovesEnd - EnemyMonMoves + 1 +.checkmove + dec b + ret z + + inc hl + ld a, [de] + and a + ret z + + inc de + call AIGetEnemyMove + + ld a, [wEnemyMoveStruct + MOVE_EFFECT] + ld c, a + +; Dismiss moves with special effects if they are +; useless or not a good choice right now. +; For example, healing moves, weather moves, Dream Eater... + push hl + push de + push bc + farcall AI_Redundant + pop bc + pop de + pop hl + jr nz, .discourage + +; Dismiss status-only moves if the player can't be statused. + ld a, [wEnemyMoveStruct + MOVE_EFFECT] + push hl + push de + push bc + ld hl, .statusonlyeffects + ld de, 1 + call IsInArray + + pop bc + pop de + pop hl + jr nc, .checkmove + + ld a, [BattleMonStatus] + and a + jr nz, .discourage + +; Dismiss Safeguard if it's already active. + ld a, [PlayerScreens] + bit SCREENS_SAFEGUARD, a + jr z, .checkmove + +.discourage + call AIDiscourageMove + jr .checkmove +; 385db + +.statusonlyeffects + db EFFECT_SLEEP + db EFFECT_TOXIC + db EFFECT_POISON + db EFFECT_PARALYZE + db $ff +; 385e0 + + + +AI_Setup: ; 385e0 +; Use stat-modifying moves on turn 1. + +; 50% chance to greatly encourage stat-up moves during the first turn of enemy's Pokemon. +; 50% chance to greatly encourage stat-down moves during the first turn of player's Pokemon. +; Almost 90% chance to greatly discourage stat-modifying moves otherwise. + + ld hl, Buffer1 - 1 + ld de, EnemyMonMoves + ld b, EnemyMonMovesEnd - EnemyMonMoves + 1 +.checkmove + dec b + ret z + + inc hl + ld a, [de] + and a + ret z + + inc de + call AIGetEnemyMove + + ld a, [wEnemyMoveStruct + MOVE_EFFECT] + + cp EFFECT_ATTACK_UP + jr c, .checkmove + cp EFFECT_EVASION_UP + 1 + jr c, .statup + +; cp EFFECT_ATTACK_DOWN - 1 + jr z, .checkmove + cp EFFECT_EVASION_DOWN + 1 + jr c, .statdown + + cp EFFECT_ATTACK_UP_2 + jr c, .checkmove + cp EFFECT_EVASION_UP_2 + 1 + jr c, .statup + +; cp EFFECT_ATTACK_DOWN_2 - 1 + jr z, .checkmove + cp EFFECT_EVASION_DOWN_2 + 1 + jr c, .statdown + + jr .checkmove + +.statup + ld a, [EnemyTurnsTaken] + and a + jr nz, .discourage + + jr .encourage + +.statdown + ld a, [PlayerTurnsTaken] + and a + jr nz, .discourage + +.encourage + call AI_50_50 + jr c, .checkmove + + dec [hl] + dec [hl] + jr .checkmove + +.discourage + call Random + cp 12 percent + jr c, .checkmove + inc [hl] + inc [hl] + jr .checkmove +; 38635 + + + +AI_Types: ; 38635 +; Dismiss any move that the player is immune to. +; Encourage super-effective moves. +; Discourage not very effective moves unless +; all damaging moves are of the same type. + + ld hl, Buffer1 - 1 + ld de, EnemyMonMoves + ld b, EnemyMonMovesEnd - EnemyMonMoves + 1 +.checkmove + dec b + ret z + + inc hl + ld a, [de] + and a + ret z + + inc de + call AIGetEnemyMove + + push hl + push bc + push de + ld a, 1 + ld [hBattleTurn], a + callfar BattleCheckTypeMatchup + pop de + pop bc + pop hl + + ld a, [wd265] + and a + jr z, .immune + cp 10 ; 1.0 + jr z, .checkmove + jr c, .noteffective + +; effective + ld a, [wEnemyMoveStruct + MOVE_POWER] + and a + jr z, .checkmove + dec [hl] + jr .checkmove + +.noteffective +; Discourage this move if there are any moves +; that do damage of a different type. + push hl + push de + push bc + ld a, [wEnemyMoveStruct + MOVE_TYPE] + ld d, a + ld hl, EnemyMonMoves + ld b, EnemyMonMovesEnd - EnemyMonMoves + 1 + ld c, 0 +.checkmove2 + dec b + jr z, .asm_38693 + + ld a, [hli] + and a + jr z, .asm_38693 + + call AIGetEnemyMove + ld a, [wEnemyMoveStruct + MOVE_TYPE] + cp d + jr z, .checkmove2 + ld a, [wEnemyMoveStruct + MOVE_POWER] + and a + jr nz, .asm_38692 + jr .checkmove2 + +.asm_38692 + ld c, a +.asm_38693 + ld a, c + pop bc + pop de + pop hl + and a + jr z, .checkmove + inc [hl] + jr .checkmove + +.immune + call AIDiscourageMove + jr .checkmove +; 386a2 + + + +AI_Offensive: ; 386a2 +; Greatly discourage non-damaging moves. + + ld hl, Buffer1 - 1 + ld de, EnemyMonMoves + ld b, EnemyMonMovesEnd - EnemyMonMoves + 1 +.checkmove + dec b + ret z + + inc hl + ld a, [de] + and a + ret z + + inc de + call AIGetEnemyMove + + ld a, [wEnemyMoveStruct + MOVE_POWER] + and a + jr nz, .checkmove + + inc [hl] + inc [hl] + jr .checkmove +; 386be + + + +AI_Smart: ; 386be +; Context-specific scoring. + + ld hl, Buffer1 + ld de, EnemyMonMoves + ld b, EnemyMonMovesEnd - EnemyMonMoves + 1 +.checkmove + dec b + ret z + + ld a, [de] + inc de + and a + ret z + + push de + push bc + push hl + call AIGetEnemyMove + + ld a, [wEnemyMoveStruct + MOVE_EFFECT] + ld hl, .table_386f2 + ld de, 3 + call IsInArray + + inc hl + jr nc, .nextmove + + ld a, [hli] + ld e, a + ld d, [hl] + + pop hl + push hl + + ld bc, .nextmove + push bc + + push de + ret + +.nextmove + pop hl + pop bc + pop de + inc hl + jr .checkmove + +.table_386f2 + dbw EFFECT_SLEEP, AI_Smart_Sleep + dbw EFFECT_LEECH_HIT, AI_Smart_LeechHit + dbw EFFECT_SELFDESTRUCT, AI_Smart_Selfdestruct + dbw EFFECT_DREAM_EATER, AI_Smart_DreamEater + dbw EFFECT_MIRROR_MOVE, AI_Smart_MirrorMove + dbw EFFECT_EVASION_UP, AI_Smart_EvasionUp + dbw EFFECT_ALWAYS_HIT, AI_Smart_AlwaysHit + dbw EFFECT_ACCURACY_DOWN, AI_Smart_AccuracyDown + dbw EFFECT_RESET_STATS, AI_Smart_ResetStats + dbw EFFECT_BIDE, AI_Smart_Bide + dbw EFFECT_FORCE_SWITCH, AI_Smart_ForceSwitch + dbw EFFECT_HEAL, AI_Smart_Heal + dbw EFFECT_TOXIC, AI_Smart_Toxic + dbw EFFECT_LIGHT_SCREEN, AI_Smart_LightScreen + dbw EFFECT_OHKO, AI_Smart_Ohko + dbw EFFECT_RAZOR_WIND, AI_Smart_RazorWind + dbw EFFECT_SUPER_FANG, AI_Smart_SuperFang + dbw EFFECT_TRAP_TARGET, AI_Smart_TrapTarget + dbw EFFECT_UNUSED_2B, AI_Smart_Unused2B + dbw EFFECT_CONFUSE, AI_Smart_Confuse + dbw EFFECT_SP_DEF_UP_2, AI_Smart_SpDefenseUp2 + dbw EFFECT_REFLECT, AI_Smart_Reflect + dbw EFFECT_PARALYZE, AI_Smart_Paralyze + dbw EFFECT_SPEED_DOWN_HIT, AI_Smart_SpeedDownHit + dbw EFFECT_SUBSTITUTE, AI_Smart_Substitute + dbw EFFECT_HYPER_BEAM, AI_Smart_HyperBeam + dbw EFFECT_RAGE, AI_Smart_Rage + dbw EFFECT_MIMIC, AI_Smart_Mimic + dbw EFFECT_LEECH_SEED, AI_Smart_LeechSeed + dbw EFFECT_DISABLE, AI_Smart_Disable + dbw EFFECT_COUNTER, AI_Smart_Counter + dbw EFFECT_ENCORE, AI_Smart_Encore + dbw EFFECT_PAIN_SPLIT, AI_Smart_PainSplit + dbw EFFECT_SNORE, AI_Smart_Snore + dbw EFFECT_CONVERSION2, AI_Smart_Conversion2 + dbw EFFECT_LOCK_ON, AI_Smart_LockOn + dbw EFFECT_DEFROST_OPPONENT, AI_Smart_DefrostOpponent + dbw EFFECT_SLEEP_TALK, AI_Smart_SleepTalk + dbw EFFECT_DESTINY_BOND, AI_Smart_DestinyBond + dbw EFFECT_REVERSAL, AI_Smart_Reversal + dbw EFFECT_SPITE, AI_Smart_Spite + dbw EFFECT_HEAL_BELL, AI_Smart_HealBell + dbw EFFECT_PRIORITY_HIT, AI_Smart_PriorityHit + dbw EFFECT_THIEF, AI_Smart_Thief + dbw EFFECT_MEAN_LOOK, AI_Smart_MeanLook + dbw EFFECT_NIGHTMARE, AI_Smart_Nightmare + dbw EFFECT_FLAME_WHEEL, AI_Smart_FlameWheel + dbw EFFECT_CURSE, AI_Smart_Curse + dbw EFFECT_PROTECT, AI_Smart_Protect + dbw EFFECT_FORESIGHT, AI_Smart_Foresight + dbw EFFECT_PERISH_SONG, AI_Smart_PerishSong + dbw EFFECT_SANDSTORM, AI_Smart_Sandstorm + dbw EFFECT_ENDURE, AI_Smart_Endure + dbw EFFECT_ROLLOUT, AI_Smart_Rollout + dbw EFFECT_SWAGGER, AI_Smart_Swagger + dbw EFFECT_FURY_CUTTER, AI_Smart_FuryCutter + dbw EFFECT_ATTRACT, AI_Smart_Attract + dbw EFFECT_SAFEGUARD, AI_Smart_Safeguard + dbw EFFECT_MAGNITUDE, AI_Smart_Magnitude + dbw EFFECT_BATON_PASS, AI_Smart_BatonPass + dbw EFFECT_PURSUIT, AI_Smart_Pursuit + dbw EFFECT_RAPID_SPIN, AI_Smart_RapidSpin + dbw EFFECT_MORNING_SUN, AI_Smart_MorningSun + dbw EFFECT_SYNTHESIS, AI_Smart_Synthesis + dbw EFFECT_MOONLIGHT, AI_Smart_Moonlight + dbw EFFECT_HIDDEN_POWER, AI_Smart_HiddenPower + dbw EFFECT_RAIN_DANCE, AI_Smart_RainDance + dbw EFFECT_SUNNY_DAY, AI_Smart_SunnyDay + dbw EFFECT_BELLY_DRUM, AI_Smart_BellyDrum + dbw EFFECT_PSYCH_UP, AI_Smart_PsychUp + dbw EFFECT_MIRROR_COAT, AI_Smart_MirrorCoat + dbw EFFECT_SKULL_BASH, AI_Smart_SkullBash + dbw EFFECT_TWISTER, AI_Smart_Twister + dbw EFFECT_EARTHQUAKE, AI_Smart_Earthquake + dbw EFFECT_FUTURE_SIGHT, AI_Smart_FutureSight + dbw EFFECT_GUST, AI_Smart_Gust + dbw EFFECT_STOMP, AI_Smart_Stomp + dbw EFFECT_SOLARBEAM, AI_Smart_Solarbeam + dbw EFFECT_THUNDER, AI_Smart_Thunder + dbw EFFECT_FLY, AI_Smart_Fly + db $ff +; 387e3 + + +AI_Smart_Sleep: ; 387e3 +; Greatly encourage sleep inducing moves if the enemy has either Dream Eater or Nightmare. +; 50% chance to greatly encourage sleep inducing moves otherwise. + + ld b, EFFECT_DREAM_EATER + call AIHasMoveEffect + jr c, .asm_387f0 + + ld b, EFFECT_NIGHTMARE + call AIHasMoveEffect + ret nc + +.asm_387f0 + call AI_50_50 + ret c + dec [hl] + dec [hl] + ret +; 387f7 + + +AI_Smart_LeechHit: ; 387f7 + push hl + ld a, 1 + ld [hBattleTurn], a + callfar BattleCheckTypeMatchup + pop hl + +; 60% chance to discourage this move if not very effective. + ld a, [wd265] + cp 10 ; 1.0 + jr c, .asm_38815 + +; Do nothing if effectiveness is neutral. + ret z + +; Do nothing if enemy's HP is full. + call AICheckEnemyMaxHP + ret c + +; 80% chance to encourage this move otherwise. + call AI_80_20 + ret c + + dec [hl] + ret + +.asm_38815 + call Random + cp 39 percent + 1 + ret c + + inc [hl] + ret +; 3881d + + +AI_Smart_LockOn: ; 3881d + ld a, [PlayerSubStatus5] + bit SUBSTATUS_LOCK_ON, a + jr nz, .asm_38882 + + push hl + call AICheckEnemyQuarterHP + jr nc, .asm_38877 + + call AICheckEnemyHalfHP + jr c, .asm_38834 + + call AICompareSpeed + jr nc, .asm_38877 + +.asm_38834 + ld a, [PlayerEvaLevel] + cp $a + jr nc, .asm_3887a + cp $8 + jr nc, .asm_38875 + + ld a, [EnemyAccLevel] + cp $5 + jr c, .asm_3887a + cp $7 + jr c, .asm_38875 + + ld hl, EnemyMonMoves + ld c, EnemyMonMovesEnd - EnemyMonMoves + 1 +.asm_3884f + dec c + jr z, .asm_38877 + + ld a, [hli] + and a + jr z, .asm_38877 + + call AIGetEnemyMove + + ld a, [wEnemyMoveStruct + MOVE_ACC] + cp 180 + jr nc, .asm_3884f + + ld a, $1 + ld [hBattleTurn], a + + push hl + push bc + farcall BattleCheckTypeMatchup + ld a, [wd265] + cp $a + pop bc + pop hl + jr c, .asm_3884f + +.asm_38875 + pop hl + ret + +.asm_38877 + pop hl + inc [hl] + ret + +.asm_3887a + pop hl + call AI_50_50 + ret c + + dec [hl] + dec [hl] + ret + +.asm_38882 + push hl + ld hl, Buffer1 - 1 + ld de, EnemyMonMoves + ld c, EnemyMonMovesEnd - EnemyMonMoves + 1 + +.asm_3888b + inc hl + dec c + jr z, .asm_388a2 + + ld a, [de] + and a + jr z, .asm_388a2 + + inc de + call AIGetEnemyMove + + ld a, [wEnemyMoveStruct + MOVE_ACC] + cp 180 + jr nc, .asm_3888b + + dec [hl] + dec [hl] + jr .asm_3888b + +.asm_388a2 + pop hl + jp AIDiscourageMove +; 388a6 + + +AI_Smart_Selfdestruct: ; 388a6 +; Selfdestruct, Explosion + +; Unless this is the enemy's last Pokemon... + push hl + farcall FindAliveEnemyMons + pop hl + jr nc, .asm_388b7 + +; ...greatly discourage this move unless this is the player's last Pokemon too. + push hl + call AICheckLastPlayerMon + pop hl + jr nz, .asm_388c6 + +.asm_388b7 +; Greatly discourage this move if enemy's HP is above 50%. + call AICheckEnemyHalfHP + jr c, .asm_388c6 + +; Do nothing if enemy's HP is below 25%. + call AICheckEnemyQuarterHP + ret nc + +; If enemy's HP is between 25% and 50%, +; over 90% chance to greatly discourage this move. + call Random + cp 9 percent - 2 + ret c + +.asm_388c6 + inc [hl] + inc [hl] + inc [hl] + ret +; 388ca + + +AI_Smart_DreamEater: ; 388ca +; 90% chance to greatly encourage this move. +; The AI_Basic layer will make sure that +; Dream Eater is only used against sleeping targets. + call Random + cp 10 percent + ret c + dec [hl] + dec [hl] + dec [hl] + ret +; 388d4 + + +AI_Smart_EvasionUp: ; 388d4 + +; Dismiss this move if enemy's evasion can't raise anymore. + ld a, [EnemyEvaLevel] + cp $d + jp nc, AIDiscourageMove + +; If enemy's HP is full... + call AICheckEnemyMaxHP + jr nc, .asm_388f2 + +; ...greatly encourage this move if player is badly poisoned. + ld a, [PlayerSubStatus5] + bit SUBSTATUS_TOXIC, a + jr nz, .asm_388ef + +; ...70% chance to greatly encourage this move if player is not badly poisoned. + call Random + cp 70 percent + jr nc, .asm_38911 + +.asm_388ef + dec [hl] + dec [hl] + ret + +.asm_388f2 + +; Greatly discourage this move if enemy's HP is below 25%. + call AICheckEnemyQuarterHP + jr nc, .asm_3890f + +; If enemy's HP is above 25% but not full, 4% chance to greatly encourage this move. + call Random + cp 4 percent + jr c, .asm_388ef + +; If enemy's HP is between 25% and 50%,... + call AICheckEnemyHalfHP + jr nc, .asm_3890a + +; If enemy's HP is above 50% but not full, 20% chance to greatly encourage this move. + call AI_80_20 + jr c, .asm_388ef + jr .asm_38911 + +.asm_3890a +; ...50% chance to greatly discourage this move. + call AI_50_50 + jr c, .asm_38911 + +.asm_3890f + inc [hl] + inc [hl] + +; 30% chance to end up here if enemy's HP is full and player is not badly poisoned. +; 77% chance to end up here if enemy's HP is above 50% but not full. +; 96% chance to end up here if enemy's HP is between 25% and 50%. +; 100% chance to end up here if enemy's HP is below 25%. +; In other words, we only end up here if the move has not been encouraged or dismissed. +.asm_38911 + ld a, [PlayerSubStatus5] + bit SUBSTATUS_TOXIC, a + jr nz, .asm_38938 + + ld a, [PlayerSubStatus4] + bit SUBSTATUS_LEECH_SEED, a + jr nz, .asm_38941 + +; Discourage this move if enemy's evasion level is higher than player's accuracy level. + ld a, [EnemyEvaLevel] + ld b, a + ld a, [PlayerAccLevel] + cp b + jr c, .asm_38936 + +; Greatly encourage this move if the player is in the middle of Fury Cutter or Rollout. + ld a, [PlayerFuryCutterCount] + and a + jr nz, .asm_388ef + + ld a, [PlayerSubStatus1] + bit SUBSTATUS_ROLLOUT, a + jr nz, .asm_388ef + + +.asm_38936 + inc [hl] + ret + +; Player is badly poisoned. +; 70% chance to greatly encourage this move. +; This would counter any previous discouragement. +.asm_38938 + call Random + cp 31 percent + 1 + ret c + dec [hl] + dec [hl] + ret + +; Player is seeded. +; 50% chance to encourage this move. +; This would partly counter any previous discouragement. +.asm_38941 + call AI_50_50 + ret c + + dec [hl] + ret +; 38947 + + +AI_Smart_AlwaysHit: ; 38947 +; 80% chance to greatly encourage this move if either... + +; ...enemy's accuracy level has been lowered three or more stages + ld a, [EnemyAccLevel] + cp $5 + jr c, .asm_38954 + +; ...or player's evasion level has been raised three or more stages. + ld a, [PlayerEvaLevel] + cp $a + ret c + +.asm_38954 + call AI_80_20 + ret c + + dec [hl] + dec [hl] + ret +; 3895b + + +AI_Smart_MirrorMove: ; 3895b + +; If the player did not use any move last turn... + ld a, [LastPlayerCounterMove] + and a + jr nz, .asm_38968 + +; ...do nothing if enemy is slower than player + call AICompareSpeed + ret nc + +; ...or dismiss this move if enemy is faster than player. + jp AIDiscourageMove + +; If the player did use a move last turn... +.asm_38968 + push hl + ld hl, UsefulMoves + ld de, 1 + call IsInArray + pop hl + +; ...do nothing if he didn't use a useful move. + ret nc + +; If he did, 50% chance to encourage this move... + call AI_50_50 + ret c + + dec [hl] + +; ...and 90% chance to encourage this move again if the enemy is faster. + call AICompareSpeed + ret nc + + call Random + cp 10 percent + ret c + + dec [hl] + ret +; 38985 + + +AI_Smart_AccuracyDown: ; 38985 + +; If player's HP is full... + call AICheckPlayerMaxHP + jr nc, .asm_389a0 + +; ...and enemy's HP is above 50%... + call AICheckEnemyHalfHP + jr nc, .asm_389a0 + +; ...greatly encourage this move if player is badly poisoned. + ld a, [PlayerSubStatus5] + bit SUBSTATUS_TOXIC, a + jr nz, .asm_3899d + +; ...70% chance to greatly encourage this move if player is not badly poisoned. + call Random + cp 70 percent + jr nc, .asm_389bf + +.asm_3899d + dec [hl] + dec [hl] + ret + +.asm_389a0 + +; Greatly discourage this move if player's HP is below 25%. + call AICheckPlayerQuarterHP + jr nc, .asm_389bd + +; If player's HP is above 25% but not full, 4% chance to greatly encourage this move. + call Random + cp 4 percent + jr c, .asm_3899d + +; If player's HP is between 25% and 50%,... + call AICheckPlayerHalfHP + jr nc, .asm_389b8 + +; If player's HP is above 50% but not full, 20% chance to greatly encourage this move. + call AI_80_20 + jr c, .asm_3899d + jr .asm_389bf + +; ...50% chance to greatly discourage this move. +.asm_389b8 + call AI_50_50 + jr c, .asm_389bf + +.asm_389bd + inc [hl] + inc [hl] + +; We only end up here if the move has not been already encouraged. +.asm_389bf + ld a, [PlayerSubStatus5] + bit SUBSTATUS_TOXIC, a + jr nz, .asm_389e6 + + ld a, [PlayerSubStatus4] + bit SUBSTATUS_LEECH_SEED, a + jr nz, .asm_389ef + +; Discourage this move if enemy's evasion level is higher than player's accuracy level. + ld a, [EnemyEvaLevel] + ld b, a + ld a, [PlayerAccLevel] + cp b + jr c, .asm_389e4 + +; Greatly encourage this move if the player is in the middle of Fury Cutter or Rollout. + ld a, [PlayerFuryCutterCount] + and a + jr nz, .asm_3899d + + ld a, [PlayerSubStatus1] + bit SUBSTATUS_ROLLOUT, a + jr nz, .asm_3899d + +.asm_389e4 + inc [hl] + ret + +; Player is badly poisoned. +; 70% chance to greatly encourage this move. +; This would counter any previous discouragement. +.asm_389e6 + call Random + cp 31 percent + 1 + ret c + dec [hl] + dec [hl] + ret + +; Player is seeded. +; 50% chance to encourage this move. +; This would partly counter any previous discouragement. +.asm_389ef + call AI_50_50 + ret c + + dec [hl] + ret +; 389f5 + + +AI_Smart_ResetStats: ; 389f5 + +; 85% chance to encourage this move if any of enemy's stat levels is lower than -2. + push hl + ld hl, EnemyAtkLevel + ld c, $8 +.asm_389fb + dec c + jr z, .asm_38a05 + ld a, [hli] + cp $5 + jr c, .asm_38a12 + jr .asm_389fb + +; 85% chance to encourage this move if any of player's stat levels is higher than +2. +.asm_38a05 + ld hl, PlayerAtkLevel + ld c, $8 +.asm_38a0a + dec c + jr z, .asm_38a1b + ld a, [hli] + cp $a + jr c, .asm_38a0a + +.asm_38a12 + pop hl + call Random + cp 16 percent + ret c + dec [hl] + ret + +; Discourage this move if neither: +; Any of enemy's stat levels is lower than -2. +; Any of player's stat levels is higher than +2. +.asm_38a1b + pop hl + inc [hl] + ret +; 38a1e + + +AI_Smart_Bide: ; 38a1e +; 90% chance to discourage this move unless enemy's HP is full. + + call AICheckEnemyMaxHP + ret c + call Random + cp 10 percent + ret c + inc [hl] + ret +; 38a2a + + +AI_Smart_ForceSwitch: ; 38a2a +; Whirlwind, Roar. + +; Discourage this move if the player has not shown +; a super-effective move against the enemy. +; Consider player's type(s) if its moves are unknown. + + push hl + callfar CheckPlayerMoveTypeMatchups + ld a, [wEnemyAISwitchScore] + cp 10 ; neutral + pop hl + ret c + inc [hl] + ret +; 38a3a + + +AI_Smart_Heal: +AI_Smart_MorningSun: +AI_Smart_Synthesis: +AI_Smart_Moonlight: ; 38a3a +; 90% chance to greatly encourage this move if enemy's HP is below 25%. +; Discourage this move if enemy's HP is higher than 50%. +; Do nothing otherwise. + + call AICheckEnemyQuarterHP + jr nc, .asm_38a45 + call AICheckEnemyHalfHP + ret nc + inc [hl] + ret + +.asm_38a45 + call Random + cp 10 percent + ret c + dec [hl] + dec [hl] + ret +; 38a4e + + +AI_Smart_Toxic: +AI_Smart_LeechSeed: ; 38a4e +; Discourage this move if player's HP is below 50%. + + call AICheckPlayerHalfHP + ret c + inc [hl] + ret +; 38a54 + + +AI_Smart_LightScreen: +AI_Smart_Reflect: ; 38a54 +; Over 90% chance to discourage this move unless enemy's HP is full. + + call AICheckEnemyMaxHP + ret c + call Random + cp 8 percent + ret c + inc [hl] + ret +; 38a60 + + +AI_Smart_Ohko: ; 38a60 +; Dismiss this move if player's level is higher than enemy's level. +; Else, discourage this move is player's HP is below 50%. + + ld a, [BattleMonLevel] + ld b, a + ld a, [EnemyMonLevel] + cp b + jp c, AIDiscourageMove + call AICheckPlayerHalfHP + ret c + inc [hl] + ret +; 38a71 + + +AI_Smart_TrapTarget: ; 38a71 +; Bind, Wrap, Fire Spin, Clamp + +; 50% chance to discourage this move if the player is already trapped. + ld a, [wPlayerWrapCount] + and a + jr nz, .asm_38a8b + +; 50% chance to greatly encourage this move if player is either +; badly poisoned, in love, identified, stuck in Rollout, or has a Nightmare. + ld a, [PlayerSubStatus5] + bit SUBSTATUS_TOXIC, a + jr nz, .asm_38a91 + + ld a, [PlayerSubStatus1] + and 1<<SUBSTATUS_IN_LOVE | 1<<SUBSTATUS_ROLLOUT | 1<<SUBSTATUS_IDENTIFIED | 1<<SUBSTATUS_NIGHTMARE + jr nz, .asm_38a91 + +; Else, 50% chance to greatly encourage this move if it's the player's Pokemon first turn. + ld a, [PlayerTurnsTaken] + and a + jr z, .asm_38a91 + +; 50% chance to discourage this move otherwise. +.asm_38a8b + call AI_50_50 + ret c + inc [hl] + ret + +.asm_38a91 + call AICheckEnemyQuarterHP + ret nc + call AI_50_50 + ret c + dec [hl] + dec [hl] + ret +; 38a9c + + +AI_Smart_RazorWind: +AI_Smart_Unused2B: ; 38a9c + ld a, [EnemySubStatus1] + bit SUBSTATUS_PERISH, a + jr z, .asm_38aaa + + ld a, [EnemyPerishCount] + cp 3 + jr c, .asm_38ad3 + +.asm_38aaa + push hl + ld hl, PlayerUsedMoves + ld c, 4 + +.asm_38ab0 + ld a, [hli] + and a + jr z, .asm_38ac1 + + call AIGetEnemyMove + + ld a, [wEnemyMoveStruct + MOVE_EFFECT] + cp EFFECT_PROTECT + jr z, .asm_38ad5 + dec c + jr nz, .asm_38ab0 + +.asm_38ac1 + pop hl + ld a, [EnemySubStatus3] + bit SUBSTATUS_CONFUSED, a + jr nz, .asm_38acd + + call AICheckEnemyHalfHP + ret c + +.asm_38acd + call Random + cp 79 percent - 1 + ret c + +.asm_38ad3 + inc [hl] + ret + +.asm_38ad5 + pop hl + ld a, [hl] + add 6 + ld [hl], a + ret +; 38adb + + +AI_Smart_Confuse: ; 38adb + +; 90% chance to discourage this move if player's HP is between 25% and 50%. + call AICheckPlayerHalfHP + ret c + call Random + cp 10 percent + jr c, .asm_38ae7 + inc [hl] + +.asm_38ae7 +; Discourage again if player's HP is below 25%. + call AICheckPlayerQuarterHP + ret c + inc [hl] + ret +; 38aed + + +AI_Smart_SpDefenseUp2: ; 38aed + +; Discourage this move if enemy's HP is lower than 50%. + call AICheckEnemyHalfHP + jr nc, .asm_38b10 + +; Discourage this move if enemy's special defense level is higher than +3. + ld a, [EnemySDefLevel] + cp $b + jr nc, .asm_38b10 + +; 80% chance to greatly encourage this move if +; enemy's Special Defense level is lower than +2, and the player is of a special type. + cp $9 + ret nc + + ld a, [BattleMonType1] + cp SPECIAL + jr nc, .asm_38b09 + ld a, [BattleMonType2] + cp SPECIAL + ret c + +.asm_38b09 + call AI_80_20 + ret c + dec [hl] + dec [hl] + ret + +.asm_38b10 + inc [hl] + ret +; 38b12 + + +AI_Smart_Fly: ; 38b12 +; Fly, Dig + +; Greatly encourage this move if the player is +; flying or underground, and slower than the enemy. + + ld a, [PlayerSubStatus3] + and 1 << SUBSTATUS_FLYING | 1 << SUBSTATUS_UNDERGROUND + ret z + + call AICompareSpeed + ret nc + + dec [hl] + dec [hl] + dec [hl] + ret +; 38b20 + + +AI_Smart_SuperFang: ; 38b20 +; Discourage this move if player's HP is below 25%. + + call AICheckPlayerQuarterHP + ret c + inc [hl] + ret +; 38b26 + + +AI_Smart_Paralyze: ; 38b26 + +; 50% chance to discourage this move if player's HP is below 25%. + call AICheckPlayerQuarterHP + jr nc, .asm_38b3a + +; 80% chance to greatly encourage this move +; if enemy is slower than player and its HP is above 25%. + call AICompareSpeed + ret c + call AICheckEnemyQuarterHP + ret nc + call AI_80_20 + ret c + dec [hl] + dec [hl] + ret + +.asm_38b3a + call AI_50_50 + ret c + inc [hl] + ret +; 38b40 + + +AI_Smart_SpeedDownHit: ; 38b40 +; Icy Wind + +; Almost 90% chance to greatly encourage this move if the following conditions all meet: +; Enemy's HP is higher than 25%. +; It's the first turn of player's Pokemon. +; Player is faster than enemy. + + ld a, [wEnemyMoveStruct + MOVE_ANIM] + cp ICY_WIND + ret nz + call AICheckEnemyQuarterHP + ret nc + ld a, [PlayerTurnsTaken] + and a + ret nz + call AICompareSpeed + ret c + call Random + cp 12 percent + ret c + dec [hl] + dec [hl] + ret +; 38b5c + + +AI_Smart_Substitute: ; 38b5c +; Dismiss this move if enemy's HP is below 50%. + + call AICheckEnemyHalfHP + ret c + jp AIDiscourageMove +; 38b63 + + +AI_Smart_HyperBeam: ; 38b63 + call AICheckEnemyHalfHP + jr c, .asm_38b72 + +; 50% chance to encourage this move if enemy's HP is below 25%. + call AICheckEnemyQuarterHP + ret c + call AI_50_50 + ret c + dec [hl] + ret + +.asm_38b72 +; If enemy's HP is above 50%, discourage this move at random + call Random + cp 16 percent + ret c + inc [hl] + call AI_50_50 + ret c + inc [hl] + ret +; 38b7f + + +AI_Smart_Rage: ; 38b7f + ld a, [EnemySubStatus4] + bit SUBSTATUS_RAGE, a + jr z, .asm_38b9b + +; If enemy's Rage is building, 50% chance to encourage this move. + call AI_50_50 + jr c, .asm_38b8c + + dec [hl] + +; Encourage this move based on Rage's counter. +.asm_38b8c + ld a, [wEnemyRageCounter] + cp $2 + ret c + dec [hl] + ld a, [wEnemyRageCounter] + cp $3 + ret c + dec [hl] + ret + +.asm_38b9b +; If enemy's Rage is not building, discourage this move if enemy's HP is below 50%. + call AICheckEnemyHalfHP + jr nc, .asm_38ba6 + +; 50% chance to encourage this move otherwise. + call AI_80_20 + ret nc + dec [hl] + ret + +.asm_38ba6 + inc [hl] + ret +; 38ba8 + + +AI_Smart_Mimic: ; 38ba8 + ld a, [LastPlayerCounterMove] + and a + jr z, .asm_38be9 + + call AICheckEnemyHalfHP + jr nc, .asm_38bef + + push hl + ld a, [LastPlayerCounterMove] + call AIGetEnemyMove + + ld a, $1 + ld [hBattleTurn], a + callfar BattleCheckTypeMatchup + + ld a, [wd265] + cp $a + pop hl + jr c, .asm_38bef + jr z, .asm_38bd4 + + call AI_50_50 + jr c, .asm_38bd4 + + dec [hl] + +.asm_38bd4 + ld a, [LastPlayerCounterMove] + push hl + ld hl, UsefulMoves + ld de, 1 + call IsInArray + + pop hl + ret nc + call AI_50_50 + ret c + dec [hl] + ret + +.asm_38be9 + call AICompareSpeed + jp c, AIDiscourageMove + +.asm_38bef + inc [hl] + ret +; 38bf1 + + +AI_Smart_Counter: ; 38bf1 + push hl + ld hl, PlayerUsedMoves + ld c, 4 + ld b, 0 + +.asm_38bf9 + ld a, [hli] + and a + jr z, .asm_38c0e + + call AIGetEnemyMove + + ld a, [wEnemyMoveStruct + MOVE_POWER] + and a + jr z, .asm_38c0e + + ld a, [wEnemyMoveStruct + MOVE_TYPE] + cp SPECIAL + jr nc, .asm_38c0e + + inc b + +.asm_38c0e + dec c + jr nz, .asm_38bf9 + + pop hl + ld a, b + and a + jr z, .asm_38c39 + + cp $3 + jr nc, .asm_38c30 + + ld a, [LastPlayerCounterMove] + and a + jr z, .asm_38c38 + + call AIGetEnemyMove + + ld a, [wEnemyMoveStruct + MOVE_POWER] + and a + jr z, .asm_38c38 + + ld a, [wEnemyMoveStruct + MOVE_TYPE] + cp SPECIAL + jr nc, .asm_38c38 + + +.asm_38c30 + call Random + cp 39 percent + 1 + jr c, .asm_38c38 + + dec [hl] + +.asm_38c38 + ret + +.asm_38c39 + inc [hl] + ret +; 38c3b + + +AI_Smart_Encore: ; 38c3b + call AICompareSpeed + jr nc, .asm_38c81 + + ld a, [LastPlayerMove] + and a + jp z, AIDiscourageMove + + call AIGetEnemyMove + + ld a, [wEnemyMoveStruct + MOVE_POWER] + and a + jr z, .asm_38c68 + + push hl + ld a, [wEnemyMoveStruct + MOVE_TYPE] + ld hl, EnemyMonType1 + predef CheckTypeMatchup + + pop hl + ld a, [wd265] + cp $a + jr nc, .asm_38c68 + + and a + ret nz + jr .asm_38c78 + +.asm_38c68 + push hl + ld a, [LastPlayerCounterMove] + ld hl, .EncoreMoves + ld de, 1 + call IsInArray + pop hl + jr nc, .asm_38c81 + +.asm_38c78 + call Random + cp 28 percent - 1 + ret c + dec [hl] + dec [hl] + ret + +.asm_38c81 + inc [hl] + inc [hl] + inc [hl] + ret + +.EncoreMoves: + db SWORDS_DANCE + db WHIRLWIND + db LEER + db ROAR + db DISABLE + db MIST + db LEECH_SEED + db GROWTH + db POISONPOWDER + db STRING_SHOT + db MEDITATE + db AGILITY + db TELEPORT + db SCREECH + db HAZE + db FOCUS_ENERGY + db DREAM_EATER + db POISON_GAS + db SPLASH + db SHARPEN + db CONVERSION + db SUPER_FANG + db SUBSTITUTE + db TRIPLE_KICK + db SPIDER_WEB + db MIND_READER + db FLAME_WHEEL + db AEROBLAST + db COTTON_SPORE + db POWDER_SNOW + db $ff +; 38ca4 + + +AI_Smart_PainSplit: ; 38ca4 +; Discourage this move if [enemy's current HP * 2 > player's current HP]. + + push hl + ld hl, EnemyMonHP + ld b, [hl] + inc hl + ld c, [hl] + sla c + rl b + ld hl, BattleMonHP + 1 + ld a, [hld] + cp c + ld a, [hl] + sbc b + pop hl + ret nc + inc [hl] + ret +; 38cba + + +AI_Smart_Snore: +AI_Smart_SleepTalk: ; 38cba +; Greatly encourage this move if enemy is fast asleep. +; Greatly discourage this move otherwise. + + ld a, [EnemyMonStatus] + and $7 + cp $1 + jr z, .asm_38cc7 + + dec [hl] + dec [hl] + dec [hl] + ret + +.asm_38cc7 + inc [hl] + inc [hl] + inc [hl] + ret +; 38ccb + + +AI_Smart_DefrostOpponent: ; 38ccb +; Greatly encourage this move if enemy is frozen. +; No move has EFFECT_DEFROST_OPPONENT, so this layer is unused. + + ld a, [EnemyMonStatus] + and $20 + ret z + dec [hl] + dec [hl] + dec [hl] + ret +; 38cd5 + + +AI_Smart_Spite: ; 38cd5 + ld a, [LastPlayerCounterMove] + and a + jr nz, .asm_38ce7 + + call AICompareSpeed + jp c, AIDiscourageMove + + call AI_50_50 + ret c + inc [hl] + ret + +.asm_38ce7 + push hl + ld b, a + ld c, 4 + ld hl, BattleMonMoves + ld de, BattleMonPP + +.asm_38cf1 + ld a, [hli] + cp b + jr z, .asm_38cfb + + inc de + dec c + jr nz, .asm_38cf1 + + pop hl + ret + +.asm_38cfb + pop hl + ld a, [de] + cp $6 + jr c, .asm_38d0d + cp $f + jr nc, .asm_38d0b + + call Random + cp 39 percent + 1 + ret nc + +.asm_38d0b + inc [hl] + ret + +.asm_38d0d + call Random + cp 39 percent + 1 + ret c + dec [hl] + dec [hl] + ret +; 38d16 + + +Function_0x38d16; 38d16 + jp AIDiscourageMove +; 38d19 + + +AI_Smart_DestinyBond: +AI_Smart_Reversal: +AI_Smart_SkullBash: ; 38d19 +; Discourage this move if enemy's HP is above 25%. + + call AICheckEnemyQuarterHP + ret nc + inc [hl] + ret +; 38d1f + + +AI_Smart_HealBell: ; 38d1f +; Dismiss this move if none of the opponent's Pokemon is statused. +; Encourage this move if the enemy is statused. +; 50% chance to greatly encourage this move if the enemy is fast asleep or frozen. + + push hl + ld a, [OTPartyCount] + ld b, a + ld c, 0 + ld hl, OTPartyMon1HP + ld de, PARTYMON_STRUCT_LENGTH + +.loop + push hl + ld a, [hli] + or [hl] + jr z, .next + + ; status + dec hl + dec hl + dec hl + ld a, [hl] + or c + ld c, a + +.next + pop hl + add hl, de + dec b + jr nz, .loop + + pop hl + ld a, c + and a + jr z, .no_status + + ld a, [EnemyMonStatus] + and a + jr z, .ok + dec [hl] +.ok + and 1 << FRZ | SLP + ret z + call AI_50_50 + ret c + dec [hl] + dec [hl] + ret + +.no_status + ld a, [EnemyMonStatus] + and a + ret nz + jp AIDiscourageMove + +; 38d5a + + +AI_Smart_PriorityHit: ; 38d5a + call AICompareSpeed + ret c + +; Dismiss this move if the player is flying or underground. + ld a, [PlayerSubStatus3] + and 1 << SUBSTATUS_FLYING | 1 << SUBSTATUS_UNDERGROUND + jp nz, AIDiscourageMove + +; Greatly encourage this move if it will KO the player. + ld a, $1 + ld [hBattleTurn], a + push hl + callfar EnemyAttackDamage + callfar BattleCommand_DamageCalc + callfar BattleCommand_Stab + pop hl + ld a, [CurDamage + 1] + ld c, a + ld a, [CurDamage] + ld b, a + ld a, [BattleMonHP + 1] + cp c + ld a, [BattleMonHP] + sbc b + ret nc + dec [hl] + dec [hl] + dec [hl] + ret +; 38d93 + + +AI_Smart_Thief: ; 38d93 +; Don't use Thief unless it's the only move available. + + ld a, [hl] + add $1e + ld [hl], a + ret +; 38d98 + + +AI_Smart_Conversion2: ; 38d98 + ld a, [LastPlayerMove] + and a + jr nz, .asm_38dc9 + + push hl + dec a + ld hl, Moves + MOVE_TYPE + ld bc, MOVE_LENGTH + call AddNTimes + + ld a, BANK(Moves) + call GetFarByte + ld [wPlayerMoveStruct + MOVE_TYPE], a + + xor a + ld [hBattleTurn], a + + callfar BattleCheckTypeMatchup + + ld a, [wd265] + cp $a + pop hl + jr c, .asm_38dc9 + ret z + + call AI_50_50 + ret c + + dec [hl] + ret + +.asm_38dc9 + call Random + cp 10 percent + ret c + inc [hl] + ret +; 38dd1 + + +AI_Smart_Disable: ; 38dd1 + call AICompareSpeed + jr nc, .asm_38df3 + + push hl + ld a, [LastPlayerCounterMove] + ld hl, UsefulMoves + ld de, 1 + call IsInArray + + pop hl + jr nc, .asm_38dee + + call Random + cp 39 percent + 1 + ret c + dec [hl] + ret + +.asm_38dee + ld a, [wEnemyMoveStruct + MOVE_POWER] + and a + ret nz + +.asm_38df3 + call Random + cp 8 percent + ret c + inc [hl] + ret +; 38dfb + + +AI_Smart_MeanLook: ; 38dfb + call AICheckEnemyHalfHP + jr nc, .asm_38e24 + + push hl + call AICheckLastPlayerMon + pop hl + jp z, AIDiscourageMove + +; 80% chance to greatly encourage this move if the enemy is badly poisoned (buggy). +; Should check PlayerSubStatus5 instead. + ld a, [EnemySubStatus5] + bit SUBSTATUS_TOXIC, a + jr nz, .asm_38e26 + +; 80% chance to greatly encourage this move if the player is either +; in love, identified, stuck in Rollout, or has a Nightmare. + ld a, [PlayerSubStatus1] + and 1<<SUBSTATUS_IN_LOVE | 1<<SUBSTATUS_ROLLOUT | 1<<SUBSTATUS_IDENTIFIED | 1<<SUBSTATUS_NIGHTMARE + jr nz, .asm_38e26 + +; Otherwise, discourage this move unless the player only has not very effective moves against the enemy. + push hl + callfar CheckPlayerMoveTypeMatchups + ld a, [wEnemyAISwitchScore] + cp $b ; not very effective + pop hl + ret nc + +.asm_38e24 + inc [hl] + ret + +.asm_38e26 + call AI_80_20 + ret c + dec [hl] + dec [hl] + dec [hl] + ret +; 38e2e + + +AICheckLastPlayerMon: ; 38e2e + ld a, [PartyCount] + ld b, a + ld c, 0 + ld hl, PartyMon1HP + ld de, PARTYMON_STRUCT_LENGTH + +.loop + ld a, [CurBattleMon] + cp c + jr z, .asm_38e44 + + ld a, [hli] + or [hl] + ret nz + dec hl + +.asm_38e44 + add hl, de + inc c + dec b + jr nz, .loop + + ret +; 38e4a + + +AI_Smart_Nightmare: ; 38e4a +; 50% chance to encourage this move. +; The AI_Basic layer will make sure that +; Dream Eater is only used against sleeping targets. + + call AI_50_50 + ret c + dec [hl] + ret +; 38e50 + + +AI_Smart_FlameWheel: ; 38e50 +; Use this move if the enemy is frozen. + + ld a, [EnemyMonStatus] + bit FRZ, a + ret z +rept 5 + dec [hl] +endr + ret +; 38e5c + + +AI_Smart_Curse: ; 38e5c + ld a, [EnemyMonType1] + cp GHOST + jr z, .ghostcurse + ld a, [EnemyMonType2] + cp GHOST + jr z, .ghostcurse + + call AICheckEnemyHalfHP + jr nc, .asm_38e93 + + ld a, [EnemyAtkLevel] + cp $b + jr nc, .asm_38e93 + cp $9 + ret nc + + ld a, [BattleMonType1] + cp GHOST + jr z, .asm_38e92 + cp SPECIAL + ret nc + ld a, [BattleMonType2] + cp SPECIAL + ret nc + call AI_80_20 + ret c + dec [hl] + dec [hl] + ret + +.asm_38e90 + inc [hl] + inc [hl] +.asm_38e92 + inc [hl] +.asm_38e93 + inc [hl] + ret + +.ghostcurse + ld a, [PlayerSubStatus1] + bit SUBSTATUS_CURSE, a + jp nz, AIDiscourageMove + + push hl + farcall FindAliveEnemyMons + pop hl + jr nc, .asm_38eb0 + + push hl + call AICheckLastPlayerMon + pop hl + jr nz, .asm_38e90 + + jr .asm_38eb7 + + +.asm_38eb0 + push hl + call AICheckLastPlayerMon + pop hl + jr z, .asm_38ecb + + +.asm_38eb7 + call AICheckEnemyQuarterHP + jp nc, .asm_38e90 + + call AICheckEnemyHalfHP + jr nc, .asm_38e92 + + call AICheckEnemyMaxHP + ret nc + + ld a, [PlayerTurnsTaken] + and a + ret nz + +.asm_38ecb + call AI_50_50 + ret c + + dec [hl] + dec [hl] + ret +; 38ed2 + + +AI_Smart_Protect: ; 38ed2 + ld a, [EnemyProtectCount] + and a + jr nz, .asm_38f13 + + ld a, [PlayerSubStatus5] + bit SUBSTATUS_LOCK_ON, a + jr nz, .asm_38f14 + + ld a, [PlayerFuryCutterCount] + cp 3 + jr nc, .asm_38f0d + + ld a, [PlayerSubStatus3] + bit SUBSTATUS_CHARGED, a + jr nz, .asm_38f0d + + ld a, [PlayerSubStatus5] + bit SUBSTATUS_TOXIC, a + jr nz, .asm_38f0d + ld a, [PlayerSubStatus4] + bit SUBSTATUS_LEECH_SEED, a + jr nz, .asm_38f0d + ld a, [PlayerSubStatus1] + bit SUBSTATUS_CURSE, a + jr nz, .asm_38f0d + + bit SUBSTATUS_ROLLOUT, a + jr z, .asm_38f14 + + ld a, [PlayerRolloutCount] + cp 3 + jr c, .asm_38f14 + +.asm_38f0d + call AI_80_20 + ret c + dec [hl] + ret + +.asm_38f13 + inc [hl] + +.asm_38f14 + call Random + cp 8 percent + ret c + inc [hl] + inc [hl] + ret +; 38f1d + + +AI_Smart_Foresight: ; 38f1d + ld a, [EnemyAccLevel] + cp $5 + jr c, .asm_38f41 + ld a, [PlayerEvaLevel] + cp $a + jr nc, .asm_38f41 + + ld a, [BattleMonType1] + cp GHOST + jr z, .asm_38f41 + ld a, [BattleMonType2] + cp GHOST + jr z, .asm_38f41 + + call Random + cp 8 percent + ret c + inc [hl] + ret + +.asm_38f41 + call Random + cp 39 percent + 1 + ret c + dec [hl] + dec [hl] + ret +; 38f4a + + +AI_Smart_PerishSong: ; 38f4a + push hl + callfar FindAliveEnemyMons + pop hl + jr c, .no + + ld a, [PlayerSubStatus5] + bit SUBSTATUS_CANT_RUN, a + jr nz, .yes + + push hl + callfar CheckPlayerMoveTypeMatchups + ld a, [wEnemyAISwitchScore] + cp 10 ; 1.0 + pop hl + ret c + + call AI_50_50 + ret c + + inc [hl] + ret + +.yes + call AI_50_50 + ret c + + dec [hl] + ret + +.no + ld a, [hl] + add 5 + ld [hl], a + ret +; 38f7a + + +AI_Smart_Sandstorm: ; 38f7a + +; Greatly discourage this move if the player is immune to Sandstorm damage. + ld a, [BattleMonType1] + push hl + ld hl, .SandstormImmuneTypes + ld de, 1 + call IsInArray + pop hl + jr c, .asm_38fa5 + + ld a, [BattleMonType2] + push hl + ld hl, .SandstormImmuneTypes + ld de, 1 + call IsInArray + pop hl + jr c, .asm_38fa5 + +; Discourage this move if player's HP is below 50%. + call AICheckPlayerHalfHP + jr nc, .asm_38fa6 + +; 50% chance to encourage this move otherwise. + call AI_50_50 + ret c + + dec [hl] + ret + +.asm_38fa5 + inc [hl] + +.asm_38fa6 + inc [hl] + ret + +.SandstormImmuneTypes: + db ROCK + db GROUND + db STEEL + db $ff +; 38fac + + +AI_Smart_Endure: ; 38fac + ld a, [EnemyProtectCount] + and a + jr nz, .asm_38fd8 + + call AICheckEnemyMaxHP + jr c, .asm_38fd8 + + call AICheckEnemyQuarterHP + jr c, .asm_38fd9 + + ld b, EFFECT_REVERSAL + call AIHasMoveEffect + jr nc, .asm_38fcb + + call AI_80_20 + ret c + + dec [hl] + dec [hl] + dec [hl] + ret + +.asm_38fcb + ld a, [EnemySubStatus5] + bit SUBSTATUS_LOCK_ON, a + ret z + + call AI_50_50 + ret c + + dec [hl] + dec [hl] + ret + +.asm_38fd8 + inc [hl] + +.asm_38fd9 + inc [hl] + ret +; 38fdb + + +AI_Smart_FuryCutter: ; 38fdb +; Encourage this move based on Fury Cutter's count. + + ld a, [EnemyFuryCutterCount] + and a + jr z, .end + dec [hl] + + cp 2 + jr c, .end + dec [hl] + dec [hl] + + cp 3 + jr c, .end + dec [hl] + dec [hl] + dec [hl] + +.end + + ; fallthrough +; 38fef + + +AI_Smart_Rollout: ; 38fef +; Rollout, Fury Cutter + +; 80% chance to discourage this move if the enemy is in love, confused, or paralyzed. + ld a, [EnemySubStatus1] + bit SUBSTATUS_IN_LOVE, a + jr nz, .asm_39020 + + ld a, [EnemySubStatus3] + bit SUBSTATUS_CONFUSED, a + jr nz, .asm_39020 + + ld a, [EnemyMonStatus] + bit PAR, a + jr nz, .asm_39020 + +; 80% chance to discourage this move if the enemy's HP is below 25%, +; or if accuracy or evasion modifiers favour the player. + call AICheckEnemyQuarterHP + jr nc, .asm_39020 + + ld a, [EnemyAccLevel] + cp 7 + jr c, .asm_39020 + ld a, [PlayerEvaLevel] + cp 8 + jr nc, .asm_39020 + +; Otherwise, 80% chance to greatly encourage this move. + call Random + cp 79 percent - 1 + ret nc + dec [hl] + dec [hl] + ret + +.asm_39020 + call AI_80_20 + ret c + inc [hl] + ret +; 39026 + + +AI_Smart_Swagger: +AI_Smart_Attract: ; 39026 +; 80% chance to encourage this move during the first turn of player's Pokemon. +; 80% chance to discourage this move otherwise. + + ld a, [PlayerTurnsTaken] + and a + jr z, .first_turn + + call AI_80_20 + ret c + inc [hl] + ret + +.first_turn + call Random + cp 79 percent - 1 + ret nc + dec [hl] + ret +; 3903a + + +AI_Smart_Safeguard: ; 3903a +; 80% chance to discourage this move if player's HP is below 50%. + + call AICheckPlayerHalfHP + ret c + call AI_80_20 + ret c + inc [hl] + ret +; 39044 + + +AI_Smart_Magnitude: +AI_Smart_Earthquake: ; 39044 + +; Greatly encourage this move if the player is underground and the enemy is faster. + ld a, [LastPlayerCounterMove] + cp DIG + ret nz + + ld a, [PlayerSubStatus3] + bit SUBSTATUS_UNDERGROUND, a + jr z, .could_dig + + call AICompareSpeed + ret nc + dec [hl] + dec [hl] + ret + +.could_dig + ; Try to predict if the player will use Dig this turn. + + ; 50% chance to encourage this move if the enemy is slower than the player. + call AICompareSpeed + ret c + + call AI_50_50 + ret c + + dec [hl] + ret +; 39062 + + +AI_Smart_BatonPass: ; 39062 +; Discourage this move if the player hasn't shown super-effective moves against the enemy. +; Consider player's type(s) if its moves are unknown. + + push hl + callfar CheckPlayerMoveTypeMatchups + ld a, [wEnemyAISwitchScore] + cp 10 ; neutral + pop hl + ret c + inc [hl] + ret +; 39072 + + +AI_Smart_Pursuit: ; 39072 +; 50% chance to greatly encourage this move if player's HP is below 25%. +; 80% chance to discourage this move otherwise. + + call AICheckPlayerQuarterHP + jr nc, .asm_3907d + call AI_80_20 + ret c + inc [hl] + ret + +.asm_3907d + call AI_50_50 + ret c + dec [hl] + dec [hl] + ret +; 39084 + + +AI_Smart_RapidSpin: ; 39084 +; 80% chance to greatly encourage this move if the enemy is +; trapped (Bind effect), seeded, or scattered with spikes. + + ld a, [wEnemyWrapCount] + and a + jr nz, .asm_39097 + + ld a, [EnemySubStatus4] + bit SUBSTATUS_LEECH_SEED, a + jr nz, .asm_39097 + + ld a, [EnemyScreens] + bit SCREENS_SPIKES, a + ret z + +.asm_39097 + call AI_80_20 + ret c + + dec [hl] + dec [hl] + ret +; 3909e + + +AI_Smart_HiddenPower: ; 3909e + push hl + ld a, 1 + ld [hBattleTurn], a + +; Calculate Hidden Power's type and base power based on enemy's DVs. + callfar HiddenPowerDamage + callfar BattleCheckTypeMatchup + pop hl + +; Discourage Hidden Power if not very effective. + ld a, [wd265] + cp 10 + jr c, .bad + +; Discourage Hidden Power if its base power is lower than 50. + ld a, d + cp 50 + jr c, .bad + +; Encourage Hidden Power if super-effective. + ld a, [wd265] + cp 11 + jr nc, .good + +; Encourage Hidden Power if its base power is 70. + ld a, d + cp 70 + ret c + +.good + dec [hl] + ret + +.bad + inc [hl] + ret +; 390cb + + +AI_Smart_RainDance: ; 390cb + +; Greatly discourage this move if it would favour the player type-wise. +; Particularly, if the player is a Water-type. + ld a, [BattleMonType1] + cp WATER + jr z, AIBadWeatherType + cp FIRE + jr z, AIGoodWeatherType + + ld a, [BattleMonType2] + cp WATER + jr z, AIBadWeatherType + cp FIRE + jr z, AIGoodWeatherType + + push hl + ld hl, RainDanceMoves + jr AI_Smart_WeatherMove +; 390e7 + +RainDanceMoves: ; 390e7 + db WATER_GUN + db HYDRO_PUMP + db SURF + db BUBBLEBEAM + db THUNDER + db WATERFALL + db CLAMP + db BUBBLE + db CRABHAMMER + db OCTAZOOKA + db WHIRLPOOL + db $ff +; 390f3 + + +AI_Smart_SunnyDay: ; 390f3 + +; Greatly discourage this move if it would favour the player type-wise. +; Particularly, if the player is a Fire-type. + ld a, [BattleMonType1] + cp FIRE + jr z, AIBadWeatherType + cp WATER + jr z, AIGoodWeatherType + + ld a, [BattleMonType2] + cp FIRE + jr z, AIBadWeatherType + cp WATER + jr z, AIGoodWeatherType + + push hl + ld hl, SunnyDayMoves + + ; fallthrough +; 3910d + + +AI_Smart_WeatherMove: ; 3910d +; Rain Dance, Sunny Day + +; Greatly discourage this move if the enemy doesn't have +; one of the useful Rain Dance or Sunny Day moves. + call AIHasMoveInArray + pop hl + jr nc, AIBadWeatherType + +; Greatly discourage this move if player's HP is below 50%. + call AICheckPlayerHalfHP + jr nc, AIBadWeatherType + +; 50% chance to encourage this move otherwise. + call AI_50_50 + ret c + + dec [hl] + ret +; 3911e + +AIBadWeatherType: ; 3911e + inc [hl] + inc [hl] + inc [hl] + ret +; 39122 + +AIGoodWeatherType: ; 39122 +; Rain Dance, Sunny Day + +; Greatly encourage this move if it would disfavour the player type-wise and player's HP is above 50%... + call AICheckPlayerHalfHP + ret nc + +; ...as long as one of the following conditions meet: +; It's the first turn of the player's Pokemon. + ld a, [PlayerTurnsTaken] + and a + jr z, .good + +; Or it's the first turn of the enemy's Pokemon. + ld a, [EnemyTurnsTaken] + and a + ret nz + +.good + dec [hl] + dec [hl] + ret +; 39134 + + +SunnyDayMoves: ; 39134 + db FIRE_PUNCH + db EMBER + db FLAMETHROWER + db FIRE_SPIN + db FIRE_BLAST + db SACRED_FIRE + db MORNING_SUN + db SYNTHESIS + db $ff +; 3913d + + +AI_Smart_BellyDrum: ; 3913d +; Dismiss this move if enemy's attack is higher than +2 or if enemy's HP is below 50%. +; Else, discourage this move if enemy's HP is not full. + + ld a, [EnemyAtkLevel] + cp $a + jr nc, .asm_3914d + + call AICheckEnemyMaxHP + ret c + + inc [hl] + + call AICheckEnemyHalfHP + ret c + +.asm_3914d + ld a, [hl] + add $5 + ld [hl], a + ret +; 39152 + + +AI_Smart_PsychUp: ; 39152 + push hl + ld hl, EnemyAtkLevel + ld b, $8 + ld c, 100 + +; Calculate the sum of all enemy's stat level modifiers. Add 100 first to prevent underflow. +; Put the result in c. c will range between 58 and 142. +.asm_3915a + ld a, [hli] + sub $7 + add c + ld c, a + dec b + jr nz, .asm_3915a + +; Calculate the sum of all player's stat level modifiers. Add 100 first to prevent underflow. +; Put the result in d. d will range between 58 and 142. + ld hl, PlayerAtkLevel + ld b, $8 + ld d, 100 + +.asm_39169 + ld a, [hli] + sub $7 + add d + ld d, a + dec b + jr nz, .asm_39169 + +; Greatly discourage this move if enemy's stat levels are higher than player's (if c>=d). + ld a, c + sub d + pop hl + jr nc, .asm_39188 + +; Else, 80% chance to encourage this move unless player's accuracy level is lower than -1... + ld a, [PlayerAccLevel] + cp $6 + ret c + +; ...or enemy's evasion level is higher than +0. + ld a, [EnemyEvaLevel] + cp $8 + ret nc + + call AI_80_20 + ret c + + dec [hl] + ret + +.asm_39188 + inc [hl] + inc [hl] + ret +; 3918b + + +AI_Smart_MirrorCoat: ; 3918b + push hl + ld hl, PlayerUsedMoves + ld c, $4 + ld b, $0 + +.asm_39193 + ld a, [hli] + and a + jr z, .asm_391a8 + + call AIGetEnemyMove + + ld a, [wEnemyMoveStruct + MOVE_POWER] + and a + jr z, .asm_391a8 + + ld a, [wEnemyMoveStruct + MOVE_TYPE] + cp SPECIAL + jr c, .asm_391a8 + + inc b + +.asm_391a8 + dec c + jr nz, .asm_39193 + + pop hl + ld a, b + and a + jr z, .asm_391d3 + + cp $3 + jr nc, .asm_391ca + + ld a, [LastPlayerCounterMove] + and a + jr z, .asm_391d2 + + call AIGetEnemyMove + + ld a, [wEnemyMoveStruct + MOVE_POWER] + and a + jr z, .asm_391d2 + + ld a, [wEnemyMoveStruct + MOVE_TYPE] + cp SPECIAL + jr c, .asm_391d2 + + +.asm_391ca + call Random + cp 100 + jr c, .asm_391d2 + dec [hl] + +.asm_391d2 + ret + +.asm_391d3 + inc [hl] + ret +; 391d5 + + +AI_Smart_Twister: +AI_Smart_Gust: ; 391d5 + +; Greatly encourage this move if the player is flying and the enemy is faster. + ld a, [LastPlayerCounterMove] + cp FLY + ret nz + + ld a, [PlayerSubStatus3] + bit SUBSTATUS_FLYING, a + jr z, .couldFly + + call AICompareSpeed + ret nc + + dec [hl] + dec [hl] + ret + +; Try to predict if the player will use Fly this turn. +.couldFly + +; 50% chance to encourage this move if the enemy is slower than the player. + call AICompareSpeed + ret c + call AI_50_50 + ret c + dec [hl] + ret +; 391f3 + + +AI_Smart_FutureSight: ; 391f3 +; Greatly encourage this move if the player is +; flying or underground, and slower than the enemy. + + call AICompareSpeed + ret nc + + ld a, [PlayerSubStatus3] + and 1 << SUBSTATUS_FLYING | 1 << SUBSTATUS_UNDERGROUND + ret z + + dec [hl] + dec [hl] + ret +; 39200 + + +AI_Smart_Stomp: ; 39200 +; 80% chance to encourage this move if the player has used Minimize. + + ld a, [wPlayerMinimized] + and a + ret z + + call AI_80_20 + ret c + + dec [hl] + ret +; 3920b + + +AI_Smart_Solarbeam: ; 3920b +; 80% chance to encourage this move when it's sunny. +; 90% chance to discourage this move when it's raining. + + ld a, [Weather] + cp WEATHER_SUN + jr z, .asm_3921e + + cp WEATHER_RAIN + ret nz + + call Random + cp 10 percent + ret c + + inc [hl] + inc [hl] + ret + +.asm_3921e + call AI_80_20 + ret c + + dec [hl] + dec [hl] + ret +; 39225 + + +AI_Smart_Thunder: ; 39225 +; 90% chance to discourage this move when it's sunny. + + ld a, [Weather] + cp WEATHER_SUN + ret nz + + call Random + cp 10 percent + ret c + + inc [hl] + ret +; 39233 + + +AICompareSpeed: ; 39233 +; Return carry if enemy is faster than player. + + push bc + ld a, [EnemyMonSpeed + 1] + ld b, a + ld a, [BattleMonSpeed + 1] + cp b + ld a, [EnemyMonSpeed] + ld b, a + ld a, [BattleMonSpeed] + sbc b + pop bc + ret +; 39246 + + +AICheckPlayerMaxHP: ; 39246 + push hl + push de + push bc + ld de, BattleMonHP + ld hl, BattleMonMaxHP + jr AICheckMaxHP +; 39251 + + +AICheckEnemyMaxHP: ; 39251 + push hl + push de + push bc + ld de, EnemyMonHP + ld hl, EnemyMonMaxHP + ; fallthrough +; 3925a + + +AICheckMaxHP: ; 3925a +; Return carry if hp at de matches max hp at hl. + + ld a, [de] + inc de + cp [hl] + jr nz, .asm_39269 + + inc hl + ld a, [de] + cp [hl] + jr nz, .asm_39269 + + pop bc + pop de + pop hl + scf + ret + +.asm_39269 + pop bc + pop de + pop hl + and a + ret +; 3926e + + +AICheckPlayerHalfHP: ; 3926e + push hl + ld hl, BattleMonHP + ld b, [hl] + inc hl + ld c, [hl] + sla c + rl b + inc hl + inc hl + ld a, [hld] + cp c + ld a, [hl] + sbc b + pop hl + ret +; 39281 + + +AICheckEnemyHalfHP: ; 39281 + push hl + push de + push bc + ld hl, EnemyMonHP + ld b, [hl] + inc hl + ld c, [hl] + sla c + rl b + inc hl + inc hl + ld a, [hld] + cp c + ld a, [hl] + sbc b + pop bc + pop de + pop hl + ret +; 39298 + + +AICheckEnemyQuarterHP: ; 39298 + push hl + push de + push bc + ld hl, EnemyMonHP + ld b, [hl] + inc hl + ld c, [hl] + sla c + rl b + sla c + rl b + inc hl + inc hl + ld a, [hld] + cp c + ld a, [hl] + sbc b + pop bc + pop de + pop hl + ret +; 392b3 + + +AICheckPlayerQuarterHP: ; 392b3 + push hl + ld hl, BattleMonHP + ld b, [hl] + inc hl + ld c, [hl] + sla c + rl b + sla c + rl b + inc hl + inc hl + ld a, [hld] + cp c + ld a, [hl] + sbc b + pop hl + ret +; 392ca + + +AIHasMoveEffect: ; 392ca +; Return carry if the enemy has move b. + + push hl + ld hl, EnemyMonMoves + ld c, EnemyMonMovesEnd - EnemyMonMoves + +.checkmove + ld a, [hli] + and a + jr z, .no + + call AIGetEnemyMove + + ld a, [wEnemyMoveStruct + MOVE_EFFECT] + cp b + jr z, .yes + + dec c + jr nz, .checkmove + +.no + pop hl + and a + ret + +.yes + pop hl + scf + ret +; 392e6 + + +AIHasMoveInArray: ; 392e6 +; Return carry if the enemy has a move in array hl. + + push hl + push de + push bc + +.next + ld a, [hli] + cp $ff + jr z, .done + + ld b, a + ld c, EnemyMonMovesEnd - EnemyMonMoves + 1 + ld de, EnemyMonMoves + +.check + dec c + jr z, .next + + ld a, [de] + inc de + cp b + jr nz, .check + + scf + +.done + pop bc + pop de + pop hl + ret +; 39301 + + +UsefulMoves: ; 39301 +; Moves that are usable all-around. + db DOUBLE_EDGE + db SING + db FLAMETHROWER + db HYDRO_PUMP + db SURF + db ICE_BEAM + db BLIZZARD + db HYPER_BEAM + db SLEEP_POWDER + db THUNDERBOLT + db THUNDER + db EARTHQUAKE + db TOXIC + db PSYCHIC_M + db HYPNOSIS + db RECOVER + db FIRE_BLAST + db SOFTBOILED + db SUPER_FANG + db $ff +; 39315 + + +AI_Opportunist: ; 39315 +; Discourage stall moves when the enemy's HP is low. + +; Do nothing if enemy's HP is above 50%. + call AICheckEnemyHalfHP + ret c + +; Discourage stall moves if enemy's HP is below 25%. + call AICheckEnemyQuarterHP + jr nc, .asm_39322 + +; 50% chance to discourage stall moves if enemy's HP is between 25% and 50%. + call AI_50_50 + ret c + +.asm_39322 + ld hl, Buffer1 - 1 + ld de, EnemyMonMoves + ld c, EnemyMonMovesEnd - EnemyMonMoves + 1 +.checkmove + inc hl + dec c + jr z, .asm_39347 + + ld a, [de] + inc de + and a + jr z, .asm_39347 + + push hl + push de + push bc + ld hl, .stallmoves + ld de, 1 + call IsInArray + + pop bc + pop de + pop hl + jr nc, .checkmove + + inc [hl] + jr .checkmove + +.asm_39347 + ret + +.stallmoves + db SWORDS_DANCE + db TAIL_WHIP + db LEER + db GROWL + db DISABLE + db MIST + db COUNTER + db LEECH_SEED + db GROWTH + db STRING_SHOT + db MEDITATE + db AGILITY + db RAGE + db MIMIC + db SCREECH + db HARDEN + db WITHDRAW + db DEFENSE_CURL + db BARRIER + db LIGHT_SCREEN + db HAZE + db REFLECT + db FOCUS_ENERGY + db BIDE + db AMNESIA + db TRANSFORM + db SPLASH + db ACID_ARMOR + db SHARPEN + db CONVERSION + db SUBSTITUTE + db FLAME_WHEEL + db $ff +; 39369 + + + +AI_Aggressive: ; 39369 +; Use whatever does the most damage. + +; Discourage all damaging moves but the one that does the most damage. +; If no damaging move deals damage to the player (immune), +; no move will be discouraged + +; Figure out which attack does the most damage and put it in c. + ld hl, EnemyMonMoves + ld bc, 0 + ld de, 0 +.checkmove + inc b + ld a, b + cp EnemyMonMovesEnd - EnemyMonMoves + 1 + jr z, .gotstrongestmove + + ld a, [hli] + and a + jr z, .gotstrongestmove + + push hl + push de + push bc + call AIGetEnemyMove + ld a, [wEnemyMoveStruct + MOVE_POWER] + and a + jr z, .nodamage + call AIDamageCalc + pop bc + pop de + pop hl + +; Update current move if damage is highest so far + ld a, [CurDamage + 1] + cp e + ld a, [CurDamage] + sbc d + jr c, .checkmove + + ld a, [CurDamage + 1] + ld e, a + ld a, [CurDamage] + ld d, a + ld c, b + jr .checkmove + +.nodamage + pop bc + pop de + pop hl + jr .checkmove + +.gotstrongestmove +; Nothing we can do if no attacks did damage. + ld a, c + and a + jr z, .done + +; Discourage moves that do less damage unless they're reckless too. + ld hl, Buffer1 - 1 + ld de, EnemyMonMoves + ld b, 0 +.checkmove2 + inc b + ld a, b + cp EnemyMonMovesEnd - EnemyMonMoves + 1 + jr z, .done + +; Ignore this move if it is the highest damaging one. + cp c + ld a, [de] + inc de + inc hl + jr z, .checkmove2 + + call AIGetEnemyMove + +; Ignore this move if its power is 0 or 1. +; Moves such as Seismic Toss, Hidden Power, +; Counter and Fissure have a base power of 1. + ld a, [wEnemyMoveStruct + MOVE_POWER] + cp 2 + jr c, .checkmove2 + +; Ignore this move if it is reckless. + push hl + push de + push bc + ld a, [wEnemyMoveStruct + MOVE_EFFECT] + ld hl, .RecklessMoves + ld de, 1 + call IsInArray + pop bc + pop de + pop hl + jr c, .checkmove2 + +; If we made it this far, discourage this move. + inc [hl] + jr .checkmove2 + +.done + ret + +.RecklessMoves: + db EFFECT_SELFDESTRUCT + db EFFECT_RAMPAGE + db EFFECT_MULTI_HIT + db EFFECT_DOUBLE_HIT + db $ff +; 393e7 + + +AIDamageCalc: ; 393e7 + ld a, 1 + ld [hBattleTurn], a + ld a, [wEnemyMoveStruct + MOVE_EFFECT] + ld de, 1 + ld hl, .ConstantDamageEffects + call IsInArray + jr nc, .asm_39400 + callfar BattleCommand_ConstantDamage + ret + +.asm_39400 + callfar EnemyAttackDamage + callfar BattleCommand_DamageCalc + callfar BattleCommand_Stab + ret + +.ConstantDamageEffects: + db EFFECT_SUPER_FANG + db EFFECT_STATIC_DAMAGE + db EFFECT_LEVEL_DAMAGE + db EFFECT_PSYWAVE + db $ff +; 39418 + + +AI_Cautious: ; 39418 +; 90% chance to discourage moves with residual effects after the first turn. + + ld a, [EnemyTurnsTaken] + and a + ret z + + ld hl, Buffer1 - 1 + ld de, EnemyMonMoves + ld c, EnemyMonMovesEnd - EnemyMonMoves + 1 +.asm_39425 + inc hl + dec c + ret z + + ld a, [de] + inc de + and a + ret z + + push hl + push de + push bc + ld hl, .residualmoves + ld de, 1 + call IsInArray + + pop bc + pop de + pop hl + jr nc, .asm_39425 + + call Random + cp 90 percent + 1 + ret nc + + inc [hl] + jr .asm_39425 + +.residualmoves + db MIST + db LEECH_SEED + db POISONPOWDER + db STUN_SPORE + db THUNDER_WAVE + db FOCUS_ENERGY + db BIDE + db POISON_GAS + db TRANSFORM + db CONVERSION + db SUBSTITUTE + db SPIKES + db $ff +; 39453 + + + +AI_Status: ; 39453 +; Dismiss status moves that don't affect the player. + + ld hl, Buffer1 - 1 + ld de, EnemyMonMoves + ld b, EnemyMonMovesEnd - EnemyMonMoves + 1 +.checkmove + dec b + ret z + + inc hl + ld a, [de] + and a + ret z + + inc de + call AIGetEnemyMove + + ld a, [wEnemyMoveStruct + MOVE_EFFECT] + cp EFFECT_TOXIC + jr z, .poisonimmunity + cp EFFECT_POISON + jr z, .poisonimmunity + cp EFFECT_SLEEP + jr z, .typeimmunity + cp EFFECT_PARALYZE + jr z, .typeimmunity + + ld a, [wEnemyMoveStruct + MOVE_POWER] + and a + jr z, .checkmove + + jr .typeimmunity + +.poisonimmunity + ld a, [BattleMonType1] + cp POISON + jr z, .immune + ld a, [BattleMonType2] + cp POISON + jr z, .immune + +.typeimmunity + push hl + push bc + push de + ld a, 1 + ld [hBattleTurn], a + callfar BattleCheckTypeMatchup + pop de + pop bc + pop hl + + ld a, [wd265] + and a + jr nz, .checkmove + +.immune + call AIDiscourageMove + jr .checkmove +; 394a9 + + + +AI_Risky: ; 394a9 +; Use any move that will KO the target. +; Risky moves will often be an exception (see below). + + ld hl, Buffer1 - 1 + ld de, EnemyMonMoves + ld c, EnemyMonMovesEnd - EnemyMonMoves + 1 +.checkmove + inc hl + dec c + ret z + + ld a, [de] + inc de + and a + ret z + + push de + push bc + push hl + call AIGetEnemyMove + + ld a, [wEnemyMoveStruct + MOVE_POWER] + and a + jr z, .nextmove + +; Don't use risky moves at max hp. + ld a, [wEnemyMoveStruct + MOVE_EFFECT] + ld de, 1 + ld hl, .RiskyMoves + call IsInArray + jr nc, .checkko + + call AICheckEnemyMaxHP + jr c, .nextmove + +; Else, 80% chance to exclude them. + call Random + cp 79 percent - 1 + jr c, .nextmove + +.checkko + call AIDamageCalc + + ld a, [CurDamage + 1] + ld e, a + ld a, [CurDamage] + ld d, a + ld a, [BattleMonHP + 1] + cp e + ld a, [BattleMonHP] + sbc d + jr nc, .nextmove + + pop hl +rept 5 + dec [hl] +endr + push hl + +.nextmove + pop hl + pop bc + pop de + jr .checkmove + +.RiskyMoves: + db EFFECT_SELFDESTRUCT + db EFFECT_OHKO + db $ff +; 39502 + + + +AI_None: ; 39502 + ret +; 39503 + + +AIDiscourageMove: ; 39503 + ld a, [hl] + add 10 + ld [hl], a + ret +; 39508 + + +AIGetEnemyMove: ; 39508 +; Load attributes of move a into ram + + push hl + push de + push bc + dec a + ld hl, Moves + ld bc, MOVE_LENGTH + call AddNTimes + + ld de, wEnemyMoveStruct + ld a, BANK(Moves) + call FarCopyBytes + + pop bc + pop de + pop hl + ret +; 39521 + + +AI_80_20: ; 39521 + call Random + cp 20 percent - 1 + ret +; 39527 + + +AI_50_50: ; 39527 + call Random + cp 50 percent + 1 + ret +; 3952d diff --git a/engine/battle/ai/switch.asm b/engine/battle/ai/switch.asm new file mode 100755 index 000000000..c2f83fa1f --- /dev/null +++ b/engine/battle/ai/switch.asm @@ -0,0 +1,672 @@ +CheckPlayerMoveTypeMatchups: ; 3484e +; Check how well the moves you've already used +; fare against the enemy's Pokemon. Used to +; score a potential switch. + push hl + push de + push bc + ld a, 10 + ld [wEnemyAISwitchScore], a + ld hl, PlayerUsedMoves + ld a, [hl] + and a + jr z, .unknown_moves + + ld d, NUM_MOVES + ld e, 0 +.loop + ld a, [hli] + and a + jr z, .exit + push hl + dec a + ld hl, Moves + MOVE_POWER + call GetMoveAttr + and a + jr z, .next + + inc hl + call GetMoveByte + ld hl, EnemyMonType + call CheckTypeMatchup + ld a, [wTypeMatchup] + cp 10 + 1 ; 1.0 + 0.1 + jr nc, .super_effective + and a + jr z, .next + cp 10 ; 1.0 + jr nc, .neutral + +.not_very_effective + ld a, e + cp 1 ; 0.1 + jr nc, .next + ld e, 1 + jr .next + +.neutral + ld e, 2 + jr .next + +.super_effective + call .DecreaseScore + pop hl + jr .done + +.next + pop hl + dec d + jr nz, .loop + +.exit + ld a, e + cp 2 + jr z, .done + call .IncreaseScore + ld a, e + and a + jr nz, .done + call .IncreaseScore + jr .done + +.unknown_moves + ld a, [BattleMonType1] + ld b, a + ld hl, EnemyMonType1 + call CheckTypeMatchup + ld a, [wTypeMatchup] + cp 10 + 1 ; 1.0 + 0.1 + jr c, .ok + call .DecreaseScore +.ok + ld a, [BattleMonType2] + cp b + jr z, .ok2 + call CheckTypeMatchup + ld a, [wTypeMatchup] + cp 10 + 1 ; 1.0 + 0.1 + jr c, .ok2 + call .DecreaseScore +.ok2 + +.done + call .CheckEnemyMoveMatchups + pop bc + pop de + pop hl + ret +; 348de + + +.CheckEnemyMoveMatchups: ; 348de + ld de, EnemyMonMoves + ld b, NUM_MOVES + 1 + ld c, 0 + + ld a, [wTypeMatchup] + push af +.loop2 + dec b + jr z, .exit2 + + ld a, [de] + and a + jr z, .exit2 + + inc de + dec a + ld hl, Moves + MOVE_POWER + call GetMoveAttr + and a + jr z, .loop2 + + inc hl + call GetMoveByte + ld hl, BattleMonType1 + call CheckTypeMatchup + + ld a, [wTypeMatchup] + ; immune + and a + jr z, .loop2 + + ; not very effective + inc c + cp 10 + jr c, .loop2 + + ; neutral + inc c + inc c + inc c + inc c + inc c + cp 10 + jr z, .loop2 + + ; super effective + ld c, 100 + jr .loop2 + +.exit2 + pop af + ld [wTypeMatchup], a + + ld a, c + and a + jr z, .doubledown ; double down + cp 5 + jr c, .DecreaseScore ; down + cp 100 + ret c + jr .IncreaseScore ; up + +.doubledown + call .DecreaseScore +.DecreaseScore: ; 34931 + ld a, [wEnemyAISwitchScore] + dec a + ld [wEnemyAISwitchScore], a + ret +; 34939 + +.IncreaseScore: ; 34939 + ld a, [wEnemyAISwitchScore] + inc a + ld [wEnemyAISwitchScore], a + ret +; 34941 + +CheckAbleToSwitch: ; 34941 + xor a + ld [wEnemySwitchMonParam], a + call FindAliveEnemyMons + ret c + + ld a, [EnemySubStatus1] + bit SUBSTATUS_PERISH, a + jr z, .no_perish + + ld a, [EnemyPerishCount] + cp 1 + jr nz, .no_perish + + ; Perish count is 1 + + call FindAliveEnemyMons + call FindEnemyMonsWithAtLeastQuarterMaxHP + call FindEnemyMonsThatResistPlayer + call FindAliveEnemyMonsWithASuperEffectiveMove + + ld a, e + cp 2 + jr nz, .not_2 + + ld a, [wEnemyAISwitchScore] + add $30 ; maximum chance + ld [wEnemySwitchMonParam], a + ret + +.not_2 + call FindAliveEnemyMons + sla c + sla c + ld b, $ff + +.loop1 + inc b + sla c + jr nc, .loop1 + + ld a, b + add $30 ; maximum chance + ld [wEnemySwitchMonParam], a + ret + +.no_perish + call CheckPlayerMoveTypeMatchups + ld a, [wEnemyAISwitchScore] + cp 11 + ret nc + + ld a, [LastPlayerCounterMove] + and a + jr z, .no_last_counter_move + + call FindEnemyMonsImmuneToLastCounterMove + ld a, [wEnemyAISwitchScore] + and a + jr z, .no_last_counter_move + + ld c, a + call FindEnemyMonsWithASuperEffectiveMove + ld a, [wEnemyAISwitchScore] + cp $ff + ret z + + ld b, a + ld a, e + cp 2 + jr z, .not_2_again + + call CheckPlayerMoveTypeMatchups + ld a, [wEnemyAISwitchScore] + cp 10 + ret nc + + ld a, b + add $10 + ld [wEnemySwitchMonParam], a + ret + +.not_2_again + ld c, $10 + call CheckPlayerMoveTypeMatchups + ld a, [wEnemyAISwitchScore] + cp 10 + jr nc, .okay + ld c, $20 + +.okay + ld a, b + add c + ld [wEnemySwitchMonParam], a + ret + +.no_last_counter_move + call CheckPlayerMoveTypeMatchups + ld a, [wEnemyAISwitchScore] + cp 10 + ret nc + + call FindAliveEnemyMons + call FindEnemyMonsWithAtLeastQuarterMaxHP + call FindEnemyMonsThatResistPlayer + call FindAliveEnemyMonsWithASuperEffectiveMove + + ld a, e + cp $2 + ret nz + + ld a, [wEnemyAISwitchScore] + add $10 + ld [wEnemySwitchMonParam], a + ret +; 349f4 + + +FindAliveEnemyMons: ; 349f4 + ld a, [OTPartyCount] + cp 2 + jr c, .only_one + + ld d, a + ld e, 0 + ld b, 1 << (PARTY_LENGTH - 1) + ld c, 0 + ld hl, OTPartyMon1HP + +.loop + ld a, [CurOTMon] + cp e + jr z, .next + + push bc + ld b, [hl] + inc hl + ld a, [hld] + or b + pop bc + jr z, .next + + ld a, c + or b + ld c, a + +.next + srl b + push bc + ld bc, PARTYMON_STRUCT_LENGTH + add hl, bc + pop bc + inc e + dec d + jr nz, .loop + + ld a, c + and a + jr nz, .more_than_one + +.only_one + scf + ret + +.more_than_one + and a + ret +; 34a2a + + +FindEnemyMonsImmuneToLastCounterMove: ; 34a2a + ld hl, OTPartyMon1 + ld a, [OTPartyCount] + ld b, a + ld c, 1 << (PARTY_LENGTH - 1) + ld d, 0 + xor a + ld [wEnemyAISwitchScore], a + +.loop + ld a, [CurOTMon] + cp d + push hl + jr z, .next + + push hl + push bc + + ; If the Pokemon has at least 1 HP... + ld bc, MON_HP + add hl, bc + pop bc + ld a, [hli] + or [hl] + pop hl + jr z, .next + + ld a, [hl] + ld [CurSpecies], a + call GetBaseData + + ; the player's last move is damaging... + ld a, [LastPlayerCounterMove] + dec a + ld hl, Moves + MOVE_POWER + call GetMoveAttr + and a + jr z, .next + + ; and the Pokemon is immune to it... + inc hl + call GetMoveByte + ld hl, BaseType + call CheckTypeMatchup + ld a, [wTypeMatchup] + and a + jr nz, .next + + ; ... encourage that Pokemon. + ld a, [wEnemyAISwitchScore] + or c + ld [wEnemyAISwitchScore], a +.next + pop hl + dec b + ret z + + push bc + ld bc, PARTYMON_STRUCT_LENGTH + add hl, bc + pop bc + + inc d + srl c + jr .loop +; 34a85 + + +FindAliveEnemyMonsWithASuperEffectiveMove: ; 34a85 + push bc + ld a, [OTPartyCount] + ld e, a + ld hl, OTPartyMon1HP + ld b, 1 << (PARTY_LENGTH - 1) + ld c, 0 +.loop + ld a, [hli] + or [hl] + jr z, .next + + ld a, b + or c + ld c, a + +.next + srl b + push bc + ld bc, PartyMon2HP - (PartyMon1HP + 1) + add hl, bc + pop bc + dec e + jr nz, .loop + + ld a, c + pop bc + + and c + ld c, a +FindEnemyMonsWithASuperEffectiveMove: ; 34aa7 + + ld a, -1 + ld [wEnemyAISwitchScore], a + ld hl, OTPartyMon1Moves + ld b, 1 << (PARTY_LENGTH - 1) + ld d, 0 + ld e, 0 +.loop + ld a, b + and c + jr z, .next + + push hl + push bc + ; for move on mon: + ld b, NUM_MOVES + ld c, 0 +.loop3 + ; if move is None: break + ld a, [hli] + and a + push hl + jr z, .break3 + + ; if move has no power: continue + dec a + ld hl, Moves + MOVE_POWER + call GetMoveAttr + and a + jr z, .nope + + ; check type matchups + inc hl + call GetMoveByte + ld hl, BattleMonType1 + call CheckTypeMatchup + + ; if immune or not very effective: continue + ld a, [wTypeMatchup] + cp 10 + jr c, .nope + + ; if neutral: load 1 and continue + ld e, 1 + cp 10 + 1 + jr c, .nope + + ; if super-effective: load 2 and break + ld e, 2 + jr .break3 + +.nope + pop hl + dec b + jr nz, .loop3 + + jr .done + +.break3 + pop hl +.done + ld a, e + pop bc + pop hl + cp 2 + jr z, .done2 ; at least one move is super-effective + cp 1 + jr nz, .next ; no move does more than half damage + + ; encourage this pokemon + ld a, d + or b + ld d, a + jr .next ; such a long jump + +.next + ; next pokemon? + push bc + ld bc, PARTYMON_STRUCT_LENGTH + add hl, bc + pop bc + srl b + jr nc, .loop + + ; if no pokemon has a super-effective move: return + ld a, d + ld b, a + and a + ret z + +.done2 + ; convert the bit flag to an int and return + push bc + sla b + sla b + ld c, $ff +.loop2 + inc c + sla b + jr nc, .loop2 + + ld a, c + ld [wEnemyAISwitchScore], a + pop bc + ret +; 34b20 + + +FindEnemyMonsThatResistPlayer: ; 34b20 + push bc + ld hl, OTPartySpecies + ld b, 1 << (PARTY_LENGTH - 1) + ld c, 0 + +.loop + ld a, [hli] + cp $ff + jr z, .done + + push hl + ld [CurSpecies], a + call GetBaseData + ld a, [LastPlayerCounterMove] + and a + jr z, .skip_move + + dec a + ld hl, Moves + MOVE_POWER + call GetMoveAttr + and a + jr z, .skip_move + + inc hl + call GetMoveByte + jr .check_type + +.skip_move + ld a, [BattleMonType1] + ld hl, BaseType + call CheckTypeMatchup + ld a, [wTypeMatchup] + cp 10 + 1 + jr nc, .dont_choose_mon + ld a, [BattleMonType2] + +.check_type + ld hl, BaseType + call CheckTypeMatchup + ld a, [wTypeMatchup] + cp 10 + 1 + jr nc, .dont_choose_mon + + ld a, b + or c + ld c, a + +.dont_choose_mon + srl b + pop hl + jr .loop + +.done + ld a, c + pop bc + and c + ld c, a + ret +; 34b77 + + +FindEnemyMonsWithAtLeastQuarterMaxHP: ; 34b77 + push bc + ld de, OTPartySpecies + ld b, 1 << (PARTY_LENGTH - 1) + ld c, 0 + ld hl, OTPartyMon1HP + +.loop + ld a, [de] + inc de + cp $ff + jr z, .done + + push hl + push bc + ld b, [hl] + inc hl + ld c, [hl] + inc hl + inc hl +; hl = MaxHP + 1 +; bc = [CurHP] * 4 + srl c + rl b + srl c + rl b +; if bc >= [hl], encourage + ld a, [hld] + cp c + ld a, [hl] + sbc b + pop bc + jr nc, .next + + ld a, b + or c + ld c, a + +.next + srl b + pop hl + push bc + ld bc, PARTYMON_STRUCT_LENGTH + add hl, bc + pop bc + jr .loop + +.done + ld a, c + pop bc + and c + ld c, a + ret +; 34bb1 |