diff options
-rw-r--r-- | constants/battle_constants.asm | 35 | ||||
-rw-r--r-- | data/battle/ai/constant_damage_effects.asm | 10 | ||||
-rw-r--r-- | data/battle/ai/encore_moves.asm | 34 | ||||
-rw-r--r-- | data/battle/ai/rain_dance_moves.asm | 15 | ||||
-rw-r--r-- | data/battle/ai/reckless_moves.asm | 9 | ||||
-rw-r--r-- | data/battle/ai/residual_moves.asm | 16 | ||||
-rw-r--r-- | data/battle/ai/risky_effects.asm | 7 | ||||
-rw-r--r-- | data/battle/ai/stall_moves.asm | 37 | ||||
-rw-r--r-- | data/battle/ai/status_only_effects.asm | 9 | ||||
-rw-r--r-- | data/battle/ai/sunny_day_moves.asm | 12 | ||||
-rw-r--r-- | data/battle/ai/useful_moves.asm | 23 | ||||
-rw-r--r-- | engine/battle/ai/scoring.asm | 3207 | ||||
-rwxr-xr-x | home/battle_vars.asm | 2 | ||||
-rw-r--r-- | main.asm | 40 | ||||
-rw-r--r-- | wram.asm | 68 |
15 files changed, 3472 insertions, 52 deletions
diff --git a/constants/battle_constants.asm b/constants/battle_constants.asm index 99d86e56..a53a7100 100644 --- a/constants/battle_constants.asm +++ b/constants/battle_constants.asm @@ -1,12 +1,30 @@ +; significant level values MAX_LEVEL EQU 100 MIN_LEVEL EQU 2 EGG_LEVEL EQU 5 + +; maximum moves known per mon NUM_MOVES EQU 4 -REST_TURNS EQU 2 -MAX_STAT_LEVEL EQU 13 +; significant stat values BASE_STAT_LEVEL EQU 7 +MAX_STAT_LEVEL EQU 13 + +; minimum damage before type effectiveness +MIN_NEUTRAL_DAMAGE EQU 2 +; turns that sleep lasts +REST_SLEEP_TURNS EQU 2 + +; type effectiveness factors, scaled by 10 +SUPER_EFFECTIVE EQU 20 +MORE_EFFECTIVE EQU 15 +EFFECTIVE EQU 10 +NOT_VERY_EFFECTIVE EQU 05 +NO_EFFECT EQU 00 + +; wPlayerStatLevels and wEnemyStatLevels indexes (see wram.asm) +; GetStatName arguments (see data/battle/stat_names.asm) const_def const ATTACK const DEFENSE @@ -18,7 +36,7 @@ BASE_STAT_LEVEL EQU 7 const ABILITY NUM_LEVEL_STATS EQU const_value -; move struct +; move struct members (see data/moves/moves.asm) const_def const MOVE_ANIM const MOVE_EFFECT @@ -30,14 +48,21 @@ NUM_LEVEL_STATS EQU const_value const MOVE_LENGTH ; stat constants -const_value SET 1 +; indexes for: +; - wPlayerStats and wEnemyStats (see wram.asm) +; - party_struct and battle_struct members (see macros/wram.asm) + const_def 1 const STAT_HP const STAT_ATK const STAT_DEF const STAT_SPD const STAT_SATK +NUM_EXP_STATS EQU const_value - 1 const STAT_SDEF -NUM_STATS EQU const_value +NUM_STATS EQU const_value - 1 +NUM_BATTLE_STATS EQU NUM_STATS - 1 ; don't count HP + +; stat formula constants STAT_MIN_NORMAL EQU 5 STAT_MIN_HP EQU 10 diff --git a/data/battle/ai/constant_damage_effects.asm b/data/battle/ai/constant_damage_effects.asm new file mode 100644 index 00000000..cd812d41 --- /dev/null +++ b/data/battle/ai/constant_damage_effects.asm @@ -0,0 +1,10 @@ +; AIDamageCalc uses BattleCommand_ConstantDamage +; to calculate damage for these instead of +; BattleCommand_DamageCalc and BattleCommand_Stab. + +ConstantDamageEffects: + db EFFECT_SUPER_FANG + db EFFECT_STATIC_DAMAGE + db EFFECT_LEVEL_DAMAGE + db EFFECT_PSYWAVE + db -1 ; end diff --git a/data/battle/ai/encore_moves.asm b/data/battle/ai/encore_moves.asm new file mode 100644 index 00000000..03e028bd --- /dev/null +++ b/data/battle/ai/encore_moves.asm @@ -0,0 +1,34 @@ +; AI_SMART encourages these moves with Encore. + +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 -1 ; end diff --git a/data/battle/ai/rain_dance_moves.asm b/data/battle/ai/rain_dance_moves.asm new file mode 100644 index 00000000..c00e0b60 --- /dev/null +++ b/data/battle/ai/rain_dance_moves.asm @@ -0,0 +1,15 @@ +; AI_SMART prefers these moves during rain. + +RainDanceMoves: + 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 -1 ; end diff --git a/data/battle/ai/reckless_moves.asm b/data/battle/ai/reckless_moves.asm new file mode 100644 index 00000000..67252979 --- /dev/null +++ b/data/battle/ai/reckless_moves.asm @@ -0,0 +1,9 @@ +; AI_AGGRESSIVE does not discourage these moves +; even if a stronger one is available. + +RecklessMoves: + db EFFECT_SELFDESTRUCT + db EFFECT_RAMPAGE + db EFFECT_MULTI_HIT + db EFFECT_DOUBLE_HIT + db -1 ; end diff --git a/data/battle/ai/residual_moves.asm b/data/battle/ai/residual_moves.asm new file mode 100644 index 00000000..c99ac0f5 --- /dev/null +++ b/data/battle/ai/residual_moves.asm @@ -0,0 +1,16 @@ +; AI_CAUTIOUS discourages these moves after the first turn. + +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 -1 ; end diff --git a/data/battle/ai/risky_effects.asm b/data/battle/ai/risky_effects.asm new file mode 100644 index 00000000..529436c4 --- /dev/null +++ b/data/battle/ai/risky_effects.asm @@ -0,0 +1,7 @@ +; AI_RISKY will not use these effects at max HP +; even if they would KO the player. + +RiskyEffects: + db EFFECT_SELFDESTRUCT + db EFFECT_OHKO + db -1 ; end diff --git a/data/battle/ai/stall_moves.asm b/data/battle/ai/stall_moves.asm new file mode 100644 index 00000000..65838e0f --- /dev/null +++ b/data/battle/ai/stall_moves.asm @@ -0,0 +1,37 @@ +; AI_OPPORTUNIST discourages these moves +; when the player's HP is low. + +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 -1 ; end diff --git a/data/battle/ai/status_only_effects.asm b/data/battle/ai/status_only_effects.asm new file mode 100644 index 00000000..8748d93b --- /dev/null +++ b/data/battle/ai/status_only_effects.asm @@ -0,0 +1,9 @@ +; AI_BASIC discourages these effects if the player +; already has a status condition. + +StatusOnlyEffects: + db EFFECT_SLEEP + db EFFECT_TOXIC + db EFFECT_POISON + db EFFECT_PARALYZE + db -1 ; end diff --git a/data/battle/ai/sunny_day_moves.asm b/data/battle/ai/sunny_day_moves.asm new file mode 100644 index 00000000..38d6a8ee --- /dev/null +++ b/data/battle/ai/sunny_day_moves.asm @@ -0,0 +1,12 @@ +; AI_SMART prefers these moves during harsh sunlight. + +SunnyDayMoves: + db FIRE_PUNCH + db EMBER + db FLAMETHROWER + db FIRE_SPIN + db FIRE_BLAST + db SACRED_FIRE + db MORNING_SUN + db SYNTHESIS + db -1 ; end diff --git a/data/battle/ai/useful_moves.asm b/data/battle/ai/useful_moves.asm new file mode 100644 index 00000000..456bda2d --- /dev/null +++ b/data/battle/ai/useful_moves.asm @@ -0,0 +1,23 @@ +; AI_SMART knows these moves are usable all-around. + +UsefulMoves: + 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 -1 ; end diff --git a/engine/battle/ai/scoring.asm b/engine/battle/ai/scoring.asm new file mode 100644 index 00000000..41d4841c --- /dev/null +++ b/engine/battle/ai/scoring.asm @@ -0,0 +1,3207 @@ +AIScoring: ; used only for BANK(AIScoring) + +AI_Basic: +; 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, wBuffer1 - 1 + ld de, wEnemyMonMoves + ld b, wEnemyMonMovesEnd - wEnemyMonMoves + 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, [wBattleMonStatus] + and a + jr nz, .discourage + +; Dismiss Safeguard if it's already active. + ld a, [wPlayerScreens] + bit SCREENS_SAFEGUARD, a + jr z, .checkmove + +.discourage + call AIDiscourageMove + jr .checkmove + +INCLUDE "data/battle/ai/status_only_effects.asm" + + +AI_Setup: +; 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, wBuffer1 - 1 + ld de, wEnemyMonMoves + ld b, wEnemyMonMovesEnd - wEnemyMonMoves + 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, [wEnemyTurnsTaken] + and a + jr nz, .discourage + + jr .encourage + +.statdown + ld a, [wPlayerTurnsTaken] + 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 + + +AI_Types: +; 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, wBuffer1 - 1 + ld de, wEnemyMonMoves + ld b, wEnemyMonMovesEnd - wEnemyMonMoves + 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 + ldh [hBattleTurn], a + callfar BattleCheckTypeMatchup + pop de + pop bc + pop hl + + ld a, [wTypeMatchup] + and a + jr z, .immune + cp EFFECTIVE + 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, wEnemyMonMoves + ld b, wEnemyMonMovesEnd - wEnemyMonMoves + 1 + ld c, 0 +.checkmove2 + dec b + jr z, .asm_38685 + + ld a, [hli] + and a + jr z, .asm_38685 + + call AIGetEnemyMove + ld a, [wEnemyMoveStruct + MOVE_TYPE] + cp d + jr z, .checkmove2 + ld a, [wEnemyMoveStruct + MOVE_POWER] + and a + jr nz, .asm_38684 + jr .checkmove2 + +.asm_38684 + ld c, a +.asm_38685 + ld a, c + pop bc + pop de + pop hl + and a + jr z, .checkmove + inc [hl] + jr .checkmove + +.immune + call AIDiscourageMove + jr .checkmove + + +AI_Offensive: +; Greatly discourage non-damaging moves. + + ld hl, wBuffer1 - 1 + ld de, wEnemyMonMoves + ld b, wEnemyMonMovesEnd - wEnemyMonMoves + 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 + + +AI_Smart: +; Context-specific scoring. + + ld hl, wBuffer1 + ld de, wEnemyMonMoves + ld b, wEnemyMonMovesEnd - wEnemyMonMoves + 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 -1 ; end + +AI_Smart_Sleep: +; 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_387e2 + + ld b, EFFECT_NIGHTMARE + call AIHasMoveEffect + ret nc + +.asm_387e2 + call AI_50_50 + ret c + dec [hl] + dec [hl] + ret + +AI_Smart_LeechHit: + push hl + ld a, 1 + ldh [hBattleTurn], a + callfar BattleCheckTypeMatchup + pop hl + +; 60% chance to discourage this move if not very effective. + ld a, [wTypeMatchup] + cp EFFECTIVE + jr c, .asm_38807 + +; 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_38807 + call Random + cp 39 percent + 1 + ret c + + inc [hl] + ret + +AI_Smart_LockOn: + ld a, [wPlayerSubStatus5] + bit SUBSTATUS_LOCK_ON, a + jr nz, .asm_38874 + + push hl + call AICheckEnemyQuarterHP + jr nc, .asm_38869 + + call AICheckEnemyHalfHP + jr c, .asm_38826 + + call AICompareSpeed + jr nc, .asm_38869 + +.asm_38826 + ld a, [wPlayerEvaLevel] + cp $a + jr nc, .asm_3886c + cp $8 + jr nc, .asm_38867 + + ld a, [wEnemyAccLevel] + cp $5 + jr c, .asm_3886c + cp $7 + jr c, .asm_38867 + + ld hl, wEnemyMonMoves + ld c, wEnemyMonMovesEnd - wEnemyMonMoves + 1 +.asm_38841 + dec c + jr z, .asm_38869 + + ld a, [hli] + and a + jr z, .asm_38869 + + call AIGetEnemyMove + + ld a, [wEnemyMoveStruct + MOVE_ACC] + cp 180 + jr nc, .asm_38841 + + ld a, $1 + ldh [hBattleTurn], a + + push hl + push bc + farcall BattleCheckTypeMatchup + ld a, [wTypeMatchup] + cp EFFECTIVE + pop bc + pop hl + jr c, .asm_38841 + +.asm_38867 + pop hl + ret + +.asm_38869 + pop hl + inc [hl] + ret + +.asm_3886c + pop hl + call AI_50_50 + ret c + + dec [hl] + dec [hl] + ret + +.asm_38874 + push hl + ld hl, wBuffer1 - 1 + ld de, wEnemyMonMoves + ld c, wEnemyMonMovesEnd - wEnemyMonMoves + 1 + +.asm_3887d + inc hl + dec c + jr z, .asm_38894 + + ld a, [de] + and a + jr z, .asm_38894 + + inc de + call AIGetEnemyMove + + ld a, [wEnemyMoveStruct + MOVE_ACC] + cp 180 + jr nc, .asm_3887d + + dec [hl] + dec [hl] + jr .asm_3887d + +.asm_38894 + pop hl + jp AIDiscourageMove + +AI_Smart_Selfdestruct: +; Selfdestruct, Explosion + +; Greatly discourage this move if enemy's HP is above 50%. + call AICheckEnemyHalfHP + jr c, .asm_388a7 + +; 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_388a7 + inc [hl] + inc [hl] + inc [hl] + ret + +AI_Smart_DreamEater: +; 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 + +AI_Smart_EvasionUp: +; Dismiss this move if enemy's evasion can't raise anymore. + ld a, [wEnemyEvaLevel] + cp $d + jp nc, AIDiscourageMove + +; If enemy's HP is full... + call AICheckEnemyMaxHP + jr nc, .asm_388d3 + +; ...greatly encourage this move if player is badly poisoned. + ld a, [wPlayerSubStatus5] + bit SUBSTATUS_TOXIC, a + jr nz, .asm_388d0 + +; ...70% chance to greatly encourage this move if player is not badly poisoned. + call Random + cp 70 percent + jr nc, .asm_388f2 + +.asm_388d0 + dec [hl] + dec [hl] + ret + +.asm_388d3 + +; Greatly discourage this move if enemy's HP is below 25%. + call AICheckEnemyQuarterHP + jr nc, .asm_388f0 + +; 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_388d0 + +; If enemy's HP is between 25% and 50%,... + call AICheckEnemyHalfHP + jr nc, .asm_388eb + +; If enemy's HP is above 50% but not full, 20% chance to greatly encourage this move. + call AI_80_20 + jr c, .asm_388d0 + jr .asm_388f2 + +.asm_388eb +; ...50% chance to greatly discourage this move. + call AI_50_50 + jr c, .asm_388f2 + +.asm_388f0 + 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_388f2 + ld a, [wPlayerSubStatus5] + bit SUBSTATUS_TOXIC, a + jr nz, .asm_38919 + + ld a, [wPlayerSubStatus4] + bit SUBSTATUS_LEECH_SEED, a + jr nz, .asm_38922 + +; Discourage this move if enemy's evasion level is higher than player's accuracy level. + ld a, [wEnemyEvaLevel] + ld b, a + ld a, [wPlayerAccLevel] + cp b + jr c, .asm_38917 + +; Greatly encourage this move if the player is in the middle of Fury Cutter or Rollout. + ld a, [wPlayerFuryCutterCount] + and a + jr nz, .asm_388d0 + + ld a, [wPlayerSubStatus1] + bit SUBSTATUS_ROLLOUT, a + jr nz, .asm_388d0 + +.asm_38917 + inc [hl] + ret + +; Player is badly poisoned. +; 70% chance to greatly encourage this move. +; This would counter any previous discouragement. +.asm_38919 + 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_38922 + call AI_50_50 + ret c + + dec [hl] + ret + +AI_Smart_AlwaysHit: +; 80% chance to greatly encourage this move if either... + +; ...enemy's accuracy level has been lowered three or more stages + ld a, [wEnemyAccLevel] + cp $5 + jr c, .asm_38935 + +; ...or player's evasion level has been raised three or more stages. + ld a, [wPlayerEvaLevel] + cp $a + ret c + +.asm_38935 + call AI_80_20 + ret c + + dec [hl] + dec [hl] + ret + +AI_Smart_MirrorMove: +; If the player did not use any move last turn... + ld a, [wLastPlayerCounterMove] + and a + jr nz, .asm_38949 + +; ...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_38949 + 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 + +AI_Smart_AccuracyDown: +; If player's HP is full... + call AICheckPlayerMaxHP + jr nc, .asm_38981 + +; ...and enemy's HP is above 50%... + call AICheckEnemyHalfHP + jr nc, .asm_38981 + +; ...greatly encourage this move if player is badly poisoned. + ld a, [wPlayerSubStatus5] + bit SUBSTATUS_TOXIC, a + jr nz, .asm_3897e + +; ...70% chance to greatly encourage this move if player is not badly poisoned. + call Random + cp 70 percent + jr nc, .asm_389a0 + +.asm_3897e + dec [hl] + dec [hl] + ret + +.asm_38981 + +; Greatly discourage this move if player's HP is below 25%. + call AICheckPlayerQuarterHP + jr nc, .asm_3899e + +; 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_3897e + +; If player's HP is between 25% and 50%,... + call AICheckPlayerHalfHP + jr nc, .asm_38999 + +; If player's HP is above 50% but not full, 20% chance to greatly encourage this move. + call AI_80_20 + jr c, .asm_3897e + jr .asm_389a0 + +; ...50% chance to greatly discourage this move. +.asm_38999 + call AI_50_50 + jr c, .asm_389a0 + +.asm_3899e + inc [hl] + inc [hl] + +; We only end up here if the move has not been already encouraged. +.asm_389a0 + ld a, [wPlayerSubStatus5] + bit SUBSTATUS_TOXIC, a + jr nz, .asm_389c7 + + ld a, [wPlayerSubStatus4] + bit SUBSTATUS_LEECH_SEED, a + jr nz, .asm_389d0 + +; Discourage this move if enemy's evasion level is higher than player's accuracy level. + ld a, [wEnemyEvaLevel] + ld b, a + ld a, [wPlayerAccLevel] + cp b + jr c, .asm_389c5 + +; Greatly encourage this move if the player is in the middle of Fury Cutter or Rollout. + ld a, [wPlayerFuryCutterCount] + and a + jr nz, .asm_3897e + + ld a, [wPlayerSubStatus1] + bit SUBSTATUS_ROLLOUT, a + jr nz, .asm_3897e + +.asm_389c5 + inc [hl] + ret + +; Player is badly poisoned. +; 70% chance to greatly encourage this move. +; This would counter any previous discouragement. +.asm_389c7 + 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_389d0 + call AI_50_50 + ret c + + dec [hl] + ret + +AI_Smart_ResetStats: +; 85% chance to encourage this move if any of enemy's stat levels is lower than -2. + push hl + ld hl, wEnemyAtkLevel + ld c, $8 +.asm_389dc + dec c + jr z, .asm_389e6 + ld a, [hli] + cp $5 + jr c, .asm_389f3 + jr .asm_389dc + +; 85% chance to encourage this move if any of player's stat levels is higher than +2. +.asm_389e6 + ld hl, wPlayerAtkLevel + ld c, $8 +.asm_389eb + dec c + jr z, .asm_389fc + ld a, [hli] + cp $a + jr c, .asm_389eb + +.asm_389f3 + 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_389fc + pop hl + inc [hl] + ret + +AI_Smart_Bide: +; 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 + +AI_Smart_ForceSwitch: +; 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 + +AI_Smart_Heal: +AI_Smart_MorningSun: +AI_Smart_Synthesis: +AI_Smart_Moonlight: +; 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_38a26 + call AICheckEnemyHalfHP + ret nc + inc [hl] + ret + +.asm_38a26 + call Random + cp 10 percent + ret c + dec [hl] + dec [hl] + ret + +AI_Smart_Toxic: +AI_Smart_LeechSeed: +; Discourage this move if player's HP is below 50%. + + call AICheckPlayerHalfHP + ret c + inc [hl] + ret + +AI_Smart_LightScreen: +AI_Smart_Reflect: +; 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 + +AI_Smart_Ohko: +; 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, [wBattleMonLevel] + ld b, a + ld a, [wEnemyMonLevel] + cp b + jp c, AIDiscourageMove + call AICheckPlayerHalfHP + ret c + inc [hl] + ret + +AI_Smart_TrapTarget: +; 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_38a6c + +; 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, [wPlayerSubStatus5] + bit SUBSTATUS_TOXIC, a + jr nz, .asm_38a72 + + ld a, [wPlayerSubStatus1] + and 1 << SUBSTATUS_IN_LOVE | 1 << SUBSTATUS_ROLLOUT | 1 << SUBSTATUS_IDENTIFIED | 1 << SUBSTATUS_NIGHTMARE + jr nz, .asm_38a72 + +; Else, 50% chance to greatly encourage this move if it's the player's Pokemon first turn. + ld a, [wPlayerTurnsTaken] + and a + jr z, .asm_38a72 + +; 50% chance to discourage this move otherwise. +.asm_38a6c + call AI_50_50 + ret c + inc [hl] + ret + +.asm_38a72 + call AICheckEnemyQuarterHP + ret nc + call AI_50_50 + ret c + dec [hl] + dec [hl] + ret + +AI_Smart_RazorWind: +AI_Smart_Unused2B: + ld a, [wEnemySubStatus1] + bit SUBSTATUS_PERISH, a + jr z, .asm_38a8b + + ld a, [wEnemyPerishCount] + cp 3 + jr c, .asm_38ab4 + +.asm_38a8b + push hl + ld hl, wPlayerUsedMoves + ld c, 4 + +.asm_38a91 + ld a, [hli] + and a + jr z, .asm_38aa2 + + call AIGetEnemyMove + + ld a, [wEnemyMoveStruct + MOVE_EFFECT] + cp EFFECT_PROTECT + jr z, .asm_38ab6 + dec c + jr nz, .asm_38a91 + +.asm_38aa2 + pop hl + ld a, [wEnemySubStatus3] + bit SUBSTATUS_CONFUSED, a + jr nz, .asm_38aae + + call AICheckEnemyHalfHP + ret c + +.asm_38aae + call Random + cp 79 percent - 1 + ret c + +.asm_38ab4 + inc [hl] + ret + +.asm_38ab6 + pop hl + ld a, [hl] + add 6 + ld [hl], a + ret + +AI_Smart_Confuse: +; 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_38ac8 + inc [hl] + +.asm_38ac8 +; Discourage again if player's HP is below 25%. + call AICheckPlayerQuarterHP + ret c + inc [hl] + ret + +AI_Smart_SpDefenseUp2: +; Discourage this move if enemy's HP is lower than 50%. + call AICheckEnemyHalfHP + jr nc, .asm_38af1 + +; Discourage this move if enemy's special defense level is higher than +3. + ld a, [wEnemySDefLevel] + cp $b + jr nc, .asm_38af1 + +; 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, [wBattleMonType1] + cp SPECIAL + jr nc, .asm_38aea + ld a, [wBattleMonType2] + cp SPECIAL + ret c + +.asm_38aea + call AI_80_20 + ret c + dec [hl] + dec [hl] + ret + +.asm_38af1 + inc [hl] + ret + +AI_Smart_Fly: +; Fly, Dig + +; Greatly encourage this move if the player is +; flying or underground, and slower than the enemy. + + ld a, [wPlayerSubStatus3] + and 1 << SUBSTATUS_FLYING | 1 << SUBSTATUS_UNDERGROUND + ret z + + call AICompareSpeed + ret nc + + dec [hl] + dec [hl] + dec [hl] + ret + +AI_Smart_SuperFang: +; Discourage this move if player's HP is below 25%. + + call AICheckPlayerQuarterHP + ret c + inc [hl] + ret + +AI_Smart_Paralyze: +; 50% chance to discourage this move if player's HP is below 25%. + call AICheckPlayerQuarterHP + jr nc, .asm_38b1b + +; 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_38b1b + call AI_50_50 + ret c + inc [hl] + ret + +AI_Smart_SpeedDownHit: +; 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, [wPlayerTurnsTaken] + and a + ret nz + call AICompareSpeed + ret c + call Random + cp 12 percent + ret c + dec [hl] + dec [hl] + ret + +AI_Smart_Substitute: +; Dismiss this move if enemy's HP is below 50%. + + call AICheckEnemyHalfHP + ret c + jp AIDiscourageMove + +AI_Smart_HyperBeam: + call AICheckEnemyHalfHP + jr c, .asm_38b53 + +; 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_38b53 +; If enemy's HP is above 50%, discourage this move at random + call Random + cp 35 percent + 1 + ret c + inc [hl] + call AI_50_50 + ret c + inc [hl] + ret + +AI_Smart_Rage: + ld a, [wEnemySubStatus4] + bit SUBSTATUS_RAGE, a + jr z, .asm_38b7c + +; If enemy's Rage is building, 50% chance to encourage this move. + call AI_50_50 + jr c, .asm_38b6d + + dec [hl] + +; Encourage this move based on Rage's counter. +.asm_38b6d + ld a, [wEnemyRageCounter] + cp $2 + ret c + dec [hl] + ld a, [wEnemyRageCounter] + cp $3 + ret c + dec [hl] + ret + +.asm_38b7c +; If enemy's Rage is not building, discourage this move if enemy's HP is below 50%. + call AICheckEnemyHalfHP + jr nc, .asm_38b87 + +; 50% chance to encourage this move otherwise. + call AI_80_20 + ret nc + dec [hl] + ret + +.asm_38b87 + inc [hl] + ret + +AI_Smart_Mimic: + ld a, [wLastPlayerCounterMove] + and a + jr z, .asm_38bca + + call AICheckEnemyHalfHP + jr nc, .asm_38bd0 + + push hl + ld a, [wLastPlayerCounterMove] + call AIGetEnemyMove + + ld a, $1 + ldh [hBattleTurn], a + callfar BattleCheckTypeMatchup + + ld a, [wTypeMatchup] + cp EFFECTIVE + pop hl + jr c, .asm_38bd0 + jr z, .asm_38bb5 + + call AI_50_50 + jr c, .asm_38bb5 + + dec [hl] + +.asm_38bb5 + ld a, [wLastPlayerCounterMove] + push hl + ld hl, UsefulMoves + ld de, 1 + call IsInArray + + pop hl + ret nc + call AI_50_50 + ret c + dec [hl] + ret + +.asm_38bca + call AICompareSpeed + jp c, AIDiscourageMove + +.asm_38bd0 + inc [hl] + ret + +AI_Smart_Counter: + push hl + ld hl, wPlayerUsedMoves + ld c, 4 + ld b, 0 + +.asm_38bda + 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_38bda + + pop hl + ld a, b + and a + jr z, .asm_38c1a + + cp $3 + jr nc, .asm_38c11 + + ld a, [wLastPlayerCounterMove] + and a + jr z, .asm_38c19 + + call AIGetEnemyMove + + ld a, [wEnemyMoveStruct + MOVE_POWER] + and a + jr z, .asm_38c19 + + ld a, [wEnemyMoveStruct + MOVE_TYPE] + cp SPECIAL + jr nc, .asm_38c19 + +.asm_38c11 + call Random + cp 39 percent + 1 + jr c, .asm_38c19 + + dec [hl] + +.asm_38c19 + ret + +.asm_38c1a + inc [hl] + ret + +AI_Smart_Encore: + call AICompareSpeed + jr nc, .asm_38c62 + + ld a, [wLastPlayerMove] + and a + jp z, AIDiscourageMove + + call AIGetEnemyMove + + ld a, [wEnemyMoveStruct + MOVE_POWER] + and a + jr z, .asm_38c49 + + push hl + ld a, [wEnemyMoveStruct + MOVE_TYPE] + ld hl, wEnemyMonType1 + predef CheckTypeMatchup + + pop hl + ld a, [wTypeMatchup] + cp EFFECTIVE + jr nc, .asm_38c49 + + and a + ret nz + jr .asm_38c59 + +.asm_38c49 + push hl + ld a, [wLastPlayerCounterMove] + ld hl, EncoreMoves + ld de, 1 + call IsInArray + pop hl + jr nc, .asm_38c62 + +.asm_38c59 + call Random + cp 28 percent - 1 + ret c + dec [hl] + dec [hl] + ret + +.asm_38c62 + inc [hl] + inc [hl] + inc [hl] + ret + +INCLUDE "data/battle/ai/encore_moves.asm" + +AI_Smart_PainSplit: +; Discourage this move if [enemy's current HP * 2 > player's current HP]. + + push hl + ld hl, wEnemyMonHP + ld b, [hl] + inc hl + ld c, [hl] + sla c + rl b + ld hl, wBattleMonHP + 1 + ld a, [hld] + cp c + ld a, [hl] + sbc b + pop hl + ret nc + inc [hl] + ret + +AI_Smart_Snore: +AI_Smart_SleepTalk: +; Greatly encourage this move if enemy is fast asleep. +; Greatly discourage this move otherwise. + + ld a, [wEnemyMonStatus] + and $7 + cp $1 + jr z, .asm_38ca8 + + dec [hl] + dec [hl] + dec [hl] + ret + +.asm_38ca8 + inc [hl] + inc [hl] + inc [hl] + ret + +AI_Smart_DefrostOpponent: +; Greatly encourage this move if enemy is frozen. +; No move has EFFECT_DEFROST_OPPONENT, so this layer is unused. + + ld a, [wEnemyMonStatus] + and 1 << FRZ + ret z + dec [hl] + dec [hl] + dec [hl] + ret + +AI_Smart_Spite: + ld a, [wLastPlayerCounterMove] + and a + jr nz, .asm_38cc8 + + call AICompareSpeed + jp c, AIDiscourageMove + + call AI_50_50 + ret c + inc [hl] + ret + +.asm_38cc8 + push hl + ld b, a + ld c, 4 + ld hl, wBattleMonMoves + ld de, wBattleMonPP + +.asm_38cd2 + ld a, [hli] + cp b + jr z, .asm_38cdc + + inc de + dec c + jr nz, .asm_38cd2 + + pop hl + ret + +.asm_38cdc + pop hl + ld a, [de] + cp $6 + jr c, .asm_38cee + cp $f + jr nc, .asm_38cec + + call Random + cp 39 percent + 1 + ret nc + +.asm_38cec + inc [hl] + ret + +.asm_38cee + call Random + cp 39 percent + 1 + ret c + dec [hl] + dec [hl] + ret + +Function_0x38d16: + jp AIDiscourageMove + +AI_Smart_DestinyBond: +AI_Smart_Reversal: +AI_Smart_SkullBash: +; Discourage this move if enemy's HP is above 25%. + + call AICheckEnemyQuarterHP + ret nc + inc [hl] + ret + +AI_Smart_HealBell: +; 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, [wOTPartyCount] + ld b, a + ld c, 0 + ld hl, wOTPartyMon1HP + 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, [wEnemyMonStatus] + 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, [wEnemyMonStatus] + and a + ret nz + jp AIDiscourageMove + + +AI_Smart_PriorityHit: + call AICompareSpeed + ret c + +; Dismiss this move if the player is flying or underground. + ld a, [wPlayerSubStatus3] + and 1 << SUBSTATUS_FLYING | 1 << SUBSTATUS_UNDERGROUND + jp nz, AIDiscourageMove + +; Greatly encourage this move if it will KO the player. + ld a, $1 + ldh [hBattleTurn], a + push hl + callfar EnemyAttackDamage + callfar BattleCommand_DamageCalc + callfar BattleCommand_Stab + pop hl + ld a, [wCurDamage + 1] + ld c, a + ld a, [wCurDamage] + ld b, a + ld a, [wBattleMonHP + 1] + cp c + ld a, [wBattleMonHP] + sbc b + ret nc + dec [hl] + dec [hl] + dec [hl] + ret + +AI_Smart_Thief: +; Don't use Thief unless it's the only move available. + + ld a, [hl] + add $1e + ld [hl], a + ret + +AI_Smart_Conversion2: + ld a, [wLastPlayerMove] + and a + jr nz, .asm_38daa + + 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 + ldh [hBattleTurn], a + + callfar BattleCheckTypeMatchup + + ld a, [wTypeMatchup] + cp EFFECTIVE + pop hl + jr c, .asm_38daa + ret z + + call AI_50_50 + ret c + + dec [hl] + ret + +.asm_38daa + call Random + cp 10 percent + ret c + inc [hl] + ret + +AI_Smart_Disable: + call AICompareSpeed + jr nc, .asm_38dd4 + + push hl + ld a, [wLastPlayerCounterMove] + ld hl, UsefulMoves + ld de, 1 + call IsInArray + + pop hl + jr nc, .asm_38dcf + + call Random + cp 39 percent + 1 + ret c + dec [hl] + ret + +.asm_38dcf + ld a, [wEnemyMoveStruct + MOVE_POWER] + and a + ret nz + +.asm_38dd4 + call Random + cp 8 percent + ret c + inc [hl] + ret + +AI_Smart_MeanLook: + call AICheckEnemyHalfHP + jr nc, .asm_38e05 + + 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 wPlayerSubStatus5 instead. + ld a, [wEnemySubStatus5] + bit SUBSTATUS_TOXIC, a + jr nz, .asm_38e07 + +; 80% chance to greatly encourage this move if the player is either +; in love, identified, stuck in Rollout, or has a Nightmare. + ld a, [wPlayerSubStatus1] + and 1 << SUBSTATUS_IN_LOVE | 1 << SUBSTATUS_ROLLOUT | 1 << SUBSTATUS_IDENTIFIED | 1 << SUBSTATUS_NIGHTMARE + jr nz, .asm_38e07 + +; 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_38e05 + inc [hl] + ret + +.asm_38e07 + call AI_80_20 + ret c + dec [hl] + dec [hl] + dec [hl] + ret + +AICheckLastPlayerMon: + ld a, [wPartyCount] + ld b, a + ld c, 0 + ld hl, wPartyMon1HP + ld de, PARTYMON_STRUCT_LENGTH + +.loop + ld a, [wCurBattleMon] + cp c + jr z, .asm_38e25 + + ld a, [hli] + or [hl] + ret nz + dec hl + +.asm_38e25 + add hl, de + inc c + dec b + jr nz, .loop + + ret + +AI_Smart_Nightmare: +; 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 + +AI_Smart_FlameWheel: +; Use this move if the enemy is frozen. + + ld a, [wEnemyMonStatus] + bit FRZ, a + ret z +rept 5 + dec [hl] +endr + ret + +AI_Smart_Curse: + ld a, [wEnemyMonType1] + cp GHOST + jr z, .ghostcurse + ld a, [wEnemyMonType2] + cp GHOST + jr z, .ghostcurse + + call AICheckEnemyHalfHP + jr nc, .asm_38e72 + + ld a, [wEnemyAtkLevel] + cp $b + jr nc, .asm_38e72 + cp $9 + ret nc + + ld a, [wBattleMonType1] + cp GHOST + jr z, .asm_38e71 + cp SPECIAL + ret nc + ld a, [wBattleMonType2] + cp SPECIAL + ret nc + call AI_80_20 + ret c + dec [hl] + dec [hl] + ret + +.asm_38e71 + inc [hl] +.asm_38e72 + inc [hl] + ret + +.ghostcurse + call AICheckEnemyQuarterHP + jp nc, AIDiscourageMove + + call AICheckEnemyHalfHP + jr nc, .asm_38e72 + + ld a, [wPlayerSubStatus1] + bit SUBSTATUS_CURSE, a + jp nz, AIDiscourageMove + + ld a, [wPlayerTurnsTaken] + and a + ret nz + + call AI_50_50 + ret c + + dec [hl] + dec [hl] + ret + +AI_Smart_Protect: + ld a, [wEnemyProtectCount] + and a + jr nz, .asm_38ed4 + + ld a, [wPlayerSubStatus5] + bit SUBSTATUS_LOCK_ON, a + jr nz, .asm_38ed5 + + ld a, [wPlayerFuryCutterCount] + cp 3 + jr nc, .asm_38ece + + ld a, [wPlayerSubStatus3] + bit SUBSTATUS_CHARGED, a + jr nz, .asm_38ece + + ld a, [wPlayerSubStatus5] + bit SUBSTATUS_TOXIC, a + jr nz, .asm_38ece + ld a, [wPlayerSubStatus4] + bit SUBSTATUS_LEECH_SEED, a + jr nz, .asm_38ece + ld a, [wPlayerSubStatus1] + bit SUBSTATUS_CURSE, a + jr nz, .asm_38ece + + bit SUBSTATUS_ROLLOUT, a + jr z, .asm_38ed5 + + ld a, [wPlayerRolloutCount] + cp 3 + jr c, .asm_38ed5 + +.asm_38ece + call AI_80_20 + ret c + dec [hl] + ret + +.asm_38ed4 + inc [hl] + +.asm_38ed5 + call Random + cp 8 percent + ret c + inc [hl] + inc [hl] + ret + +AI_Smart_Foresight: + ld a, [wEnemyAccLevel] + cp $5 + jr c, .asm_38f02 + ld a, [wPlayerEvaLevel] + cp $a + jr nc, .asm_38f02 + + ld a, [wBattleMonType1] + cp GHOST + jr z, .asm_38f02 + ld a, [wBattleMonType2] + cp GHOST + jr z, .asm_38f02 + + call Random + cp 8 percent + ret c + inc [hl] + ret + +.asm_38f02 + call Random + cp 39 percent + 1 + ret c + dec [hl] + dec [hl] + ret + +AI_Smart_PerishSong: + push hl + callfar FindAliveEnemyMons + pop hl + jr c, .no + + ld a, [wPlayerSubStatus5] + 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 + +AI_Smart_Sandstorm: +; Greatly discourage this move if the player is immune to Sandstorm damage. + ld a, [wBattleMonType1] + push hl + ld hl, .SandstormImmuneTypes + ld de, 1 + call IsInArray + pop hl + jr c, .asm_38f66 + + ld a, [wBattleMonType2] + push hl + ld hl, .SandstormImmuneTypes + ld de, 1 + call IsInArray + pop hl + jr c, .asm_38f66 + +; Discourage this move if player's HP is below 50%. + call AICheckPlayerHalfHP + jr nc, .asm_38f67 + +; 50% chance to encourage this move otherwise. + call AI_50_50 + ret c + + dec [hl] + ret + +.asm_38f66 + inc [hl] + +.asm_38f67 + inc [hl] + ret + +.SandstormImmuneTypes: + db ROCK + db GROUND + db STEEL + db -1 ; end + +AI_Smart_Endure: + ld a, [wEnemyProtectCount] + and a + jr nz, .asm_38f99 + + call AICheckEnemyMaxHP + jr c, .asm_38f99 + + call AICheckEnemyQuarterHP + jr c, .asm_38f9a + + ld b, EFFECT_REVERSAL + call AIHasMoveEffect + jr nc, .asm_38f8c + + call AI_80_20 + ret c + + dec [hl] + dec [hl] + dec [hl] + ret + +.asm_38f8c + ld a, [wEnemySubStatus5] + bit SUBSTATUS_LOCK_ON, a + ret z + + call AI_50_50 + ret c + + dec [hl] + dec [hl] + ret + +.asm_38f99 + inc [hl] + +.asm_38f9a + inc [hl] + ret + +AI_Smart_FuryCutter: +; Encourage this move based on Fury Cutter's count. + + ld a, [wEnemyFuryCutterCount] + 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 + +AI_Smart_Rollout: +; Rollout, Fury Cutter + +; 80% chance to discourage this move if the enemy is in love, confused, or paralyzed. + ld a, [wEnemySubStatus1] + bit SUBSTATUS_IN_LOVE, a + jr nz, .asm_38fe1 + + ld a, [wEnemySubStatus3] + bit SUBSTATUS_CONFUSED, a + jr nz, .asm_38fe1 + + ld a, [wEnemyMonStatus] + bit PAR, a + jr nz, .asm_38fe1 + +; 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_38fe1 + + ld a, [wEnemyAccLevel] + cp 7 + jr c, .asm_38fe1 + ld a, [wPlayerEvaLevel] + cp 8 + jr nc, .asm_38fe1 + +; Otherwise, 80% chance to greatly encourage this move. + call Random + cp 79 percent - 1 + ret nc + dec [hl] + dec [hl] + ret + +.asm_38fe1 + call AI_80_20 + ret c + inc [hl] + ret + +AI_Smart_Swagger: +AI_Smart_Attract: +; 80% chance to encourage this move during the first turn of player's Pokemon. +; 80% chance to discourage this move otherwise. + + ld a, [wPlayerTurnsTaken] + 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 + +AI_Smart_Safeguard: +; 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 + +AI_Smart_Magnitude: +AI_Smart_Earthquake: +; Greatly encourage this move if the player is underground and the enemy is faster. + ld a, [wLastPlayerCounterMove] + cp DIG + ret nz + + ld a, [wPlayerSubStatus3] + 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 + +AI_Smart_BatonPass: +; 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 + +AI_Smart_Pursuit: +; 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_3903e + call AI_80_20 + ret c + inc [hl] + ret + +.asm_3903e + call AI_50_50 + ret c + dec [hl] + dec [hl] + ret + +AI_Smart_RapidSpin: +; 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_39058 + + ld a, [wEnemySubStatus4] + bit SUBSTATUS_LEECH_SEED, a + jr nz, .asm_39058 + + ld a, [wEnemyScreens] + bit SCREENS_SPIKES, a + ret z + +.asm_39058 + call AI_80_20 + ret c + + dec [hl] + dec [hl] + ret + +AI_Smart_HiddenPower: + push hl + ld a, 1 + ldh [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, [wTypeMatchup] + cp EFFECTIVE + 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, [wTypeMatchup] + cp EFFECTIVE + 1 + 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 + +AI_Smart_RainDance: +; Greatly discourage this move if it would favour the player type-wise. +; Particularly, if the player is a Water-type. + ld a, [wBattleMonType1] + cp WATER + jr z, AIBadWeatherType + cp FIRE + jr z, AIGoodWeatherType + + ld a, [wBattleMonType2] + cp WATER + jr z, AIBadWeatherType + cp FIRE + jr z, AIGoodWeatherType + + push hl + ld hl, RainDanceMoves + jr AI_Smart_WeatherMove + +INCLUDE "data/battle/ai/rain_dance_moves.asm" + +AI_Smart_SunnyDay: +; Greatly discourage this move if it would favour the player type-wise. +; Particularly, if the player is a Fire-type. + ld a, [wBattleMonType1] + cp FIRE + jr z, AIBadWeatherType + cp WATER + jr z, AIGoodWeatherType + + ld a, [wBattleMonType2] + cp FIRE + jr z, AIBadWeatherType + cp WATER + jr z, AIGoodWeatherType + + push hl + ld hl, SunnyDayMoves + + ; fallthrough + +AI_Smart_WeatherMove: +; 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 + +AIBadWeatherType: + inc [hl] + inc [hl] + inc [hl] + ret + +AIGoodWeatherType: +; 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, [wPlayerTurnsTaken] + and a + jr z, .good + +; Or it's the first turn of the enemy's Pokemon. + ld a, [wEnemyTurnsTaken] + and a + ret nz + +.good + dec [hl] + dec [hl] + ret + +INCLUDE "data/battle/ai/sunny_day_moves.asm" + +AI_Smart_BellyDrum: +; 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, [wEnemyAtkLevel] + cp $a + jr nc, .asm_3910e + + call AICheckEnemyMaxHP + ret c + + inc [hl] + + call AICheckEnemyHalfHP + ret c + +.asm_3910e + ld a, [hl] + add $5 + ld [hl], a + ret + +AI_Smart_PsychUp: + push hl + ld hl, wEnemyAtkLevel + 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_3911b + ld a, [hli] + sub $7 + add c + ld c, a + dec b + jr nz, .asm_3911b + +; 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, wPlayerAtkLevel + ld b, $8 + ld d, 100 + +.asm_3912a + ld a, [hli] + sub $7 + add d + ld d, a + dec b + jr nz, .asm_3912a + +; 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_3914f + +; This block will always ret, since the comparisons to wPlayerEvaLevel capture every possible value + ld a, [wPlayerAccLevel] + cp $6 + ret c + + ld a, [wPlayerEvaLevel] + cp $9 + ret c + + ld a, [wPlayerEvaLevel] + cp $8 + ret nc + +; unused + call AI_80_20 + ret c + + dec [hl] + ret + +.asm_3914f + inc [hl] + ret + +AI_Smart_MirrorCoat: + push hl + ld hl, wPlayerUsedMoves + ld c, $4 + ld b, $0 + +.asm_39159 + ld a, [hli] + and a + jr z, .asm_3916e + + call AIGetEnemyMove + + ld a, [wEnemyMoveStruct + MOVE_POWER] + and a + jr z, .asm_3916e + + ld a, [wEnemyMoveStruct + MOVE_TYPE] + cp SPECIAL + jr c, .asm_3916e + + inc b + +.asm_3916e + dec c + jr nz, .asm_39159 + + pop hl + ld a, b + and a + jr z, .asm_39199 + + cp $3 + jr nc, .asm_39190 + + ld a, [wLastPlayerCounterMove] + and a + jr z, .asm_39198 + + call AIGetEnemyMove + + ld a, [wEnemyMoveStruct + MOVE_POWER] + and a + jr z, .asm_39198 + + ld a, [wEnemyMoveStruct + MOVE_TYPE] + cp SPECIAL + jr c, .asm_39198 + +.asm_39190 + call Random + cp 100 + jr c, .asm_39198 + dec [hl] + +.asm_39198 + ret + +.asm_39199 + inc [hl] + ret + +AI_Smart_Twister: +AI_Smart_Gust: +; Greatly encourage this move if the player is flying and the enemy is faster. + ld a, [wLastPlayerCounterMove] + cp FLY + ret nz + + ld a, [wPlayerSubStatus3] + 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 + +AI_Smart_FutureSight: +; Greatly encourage this move if the player is +; flying or underground, and slower than the enemy. + + call AICompareSpeed + ret nc + + ld a, [wPlayerSubStatus3] + and 1 << SUBSTATUS_FLYING | 1 << SUBSTATUS_UNDERGROUND + ret z + + dec [hl] + dec [hl] + ret + +AI_Smart_Stomp: +; 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 + +AI_Smart_Solarbeam: +; 80% chance to encourage this move when it's sunny. +; 90% chance to discourage this move when it's raining. + + ld a, [wBattleWeather] + cp WEATHER_SUN + jr z, .asm_391e4 + + cp WEATHER_RAIN + ret nz + + call Random + cp 10 percent + ret c + + inc [hl] + inc [hl] + ret + +.asm_391e4 + call AI_80_20 + ret c + + dec [hl] + dec [hl] + ret + +AI_Smart_Thunder: +; 90% chance to discourage this move when it's sunny. + + ld a, [wBattleWeather] + cp WEATHER_SUN + ret nz + + call Random + cp 10 percent + ret c + + inc [hl] + ret + +AICompareSpeed: +; Return carry if enemy is faster than player. + + push bc + ld a, [wEnemyMonSpeed + 1] + ld b, a + ld a, [wBattleMonSpeed + 1] + cp b + ld a, [wEnemyMonSpeed] + ld b, a + ld a, [wBattleMonSpeed] + sbc b + pop bc + ret + +AICheckPlayerMaxHP: + push hl + push de + push bc + ld de, wBattleMonHP + ld hl, wBattleMonMaxHP + jr AICheckMaxHP + +AICheckEnemyMaxHP: + push hl + push de + push bc + ld de, wEnemyMonHP + ld hl, wEnemyMonMaxHP + ; fallthrough + +AICheckMaxHP: +; Return carry if hp at de matches max hp at hl. + + ld a, [de] + inc de + cp [hl] + jr nz, .asm_3922f + + inc hl + ld a, [de] + cp [hl] + jr nz, .asm_3922f + + pop bc + pop de + pop hl + scf + ret + +.asm_3922f + pop bc + pop de + pop hl + and a + ret + +AICheckPlayerHalfHP: + push hl + ld hl, wBattleMonHP + 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 + +AICheckEnemyHalfHP: + push hl + push de + push bc + ld hl, wEnemyMonHP + 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 + +AICheckEnemyQuarterHP: + push hl + push de + push bc + ld hl, wEnemyMonHP + 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 + +AICheckPlayerQuarterHP: + push hl + ld hl, wBattleMonHP + 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 + +AIHasMoveEffect: +; Return carry if the enemy has move b. + + push hl + ld hl, wEnemyMonMoves + ld c, wEnemyMonMovesEnd - wEnemyMonMoves + +.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 + +AIHasMoveInArray: +; 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, wEnemyMonMovesEnd - wEnemyMonMoves + 1 + ld de, wEnemyMonMoves + +.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 + +INCLUDE "data/battle/ai/useful_moves.asm" + +AI_Opportunist: +; 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_392e8 + +; 50% chance to discourage stall moves if enemy's HP is between 25% and 50%. + call AI_50_50 + ret c + +.asm_392e8 + ld hl, wBuffer1 - 1 + ld de, wEnemyMonMoves + ld c, wEnemyMonMovesEnd - wEnemyMonMoves + 1 +.checkmove + inc hl + dec c + jr z, .asm_3930d + + ld a, [de] + inc de + and a + jr z, .asm_3930d + + 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_3930d + ret + +INCLUDE "data/battle/ai/stall_moves.asm" + + +AI_Aggressive: +; 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, wEnemyMonMoves + ld bc, 0 + ld de, 0 +.checkmove + inc b + ld a, b + cp wEnemyMonMovesEnd - wEnemyMonMoves + 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, [wCurDamage + 1] + cp e + ld a, [wCurDamage] + sbc d + jr c, .checkmove + + ld a, [wCurDamage + 1] + ld e, a + ld a, [wCurDamage] + 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, wBuffer1 - 1 + ld de, wEnemyMonMoves + ld b, 0 +.checkmove2 + inc b + ld a, b + cp wEnemyMonMovesEnd - wEnemyMonMoves + 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 + +INCLUDE "data/battle/ai/reckless_moves.asm" + +AIDamageCalc: + ld a, 1 + ldh [hBattleTurn], a + ld a, [wEnemyMoveStruct + MOVE_EFFECT] + ld de, 1 + ld hl, ConstantDamageEffects + call IsInArray + jr nc, .asm_393c6 + callfar BattleCommand_ConstantDamage + ret + +.asm_393c6 + callfar EnemyAttackDamage + callfar BattleCommand_DamageCalc + callfar BattleCommand_Stab + ret + +INCLUDE "data/battle/ai/constant_damage_effects.asm" + +AI_Cautious: +; 90% chance to discourage moves with residual effects after the first turn. + + ld a, [wEnemyTurnsTaken] + and a + ret z + + ld hl, wBuffer1 - 1 + ld de, wEnemyMonMoves + ld c, wEnemyMonMovesEnd - wEnemyMonMoves + 1 +.asm_393eb + 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_393eb + + call Random + cp 90 percent + 1 + ret nc + + inc [hl] + jr .asm_393eb + +INCLUDE "data/battle/ai/residual_moves.asm" + + +AI_Status: +; Dismiss status moves that don't affect the player. + + ld hl, wBuffer1 - 1 + ld de, wEnemyMonMoves + ld b, wEnemyMonMovesEnd - wEnemyMonMoves + 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, [wBattleMonType1] + cp POISON + jr z, .immune + ld a, [wBattleMonType2] + cp POISON + jr z, .immune + +.typeimmunity + push hl + push bc + push de + ld a, 1 + ldh [hBattleTurn], a + callfar BattleCheckTypeMatchup + pop de + pop bc + pop hl + + ld a, [wTypeMatchup] + and a + jr nz, .checkmove + +.immune + call AIDiscourageMove + jr .checkmove + + +AI_Risky: +; Use any move that will KO the target. +; Risky moves will often be an exception (see below). + + ld hl, wBuffer1 - 1 + ld de, wEnemyMonMoves + ld c, wEnemyMonMovesEnd - wEnemyMonMoves + 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, RiskyEffects + 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, [wCurDamage + 1] + ld e, a + ld a, [wCurDamage] + ld d, a + ld a, [wBattleMonHP + 1] + cp e + ld a, [wBattleMonHP] + sbc d + jr nc, .nextmove + + pop hl +rept 5 + dec [hl] +endr + push hl + +.nextmove + pop hl + pop bc + pop de + jr .checkmove + +INCLUDE "data/battle/ai/risky_effects.asm" + + +AI_None: + ret + +AIDiscourageMove: + ld a, [hl] + add 10 + ld [hl], a + ret + +AIGetEnemyMove: +; 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 + +AI_80_20: + call Random + cp 20 percent - 1 + ret + +AI_50_50: + call Random + cp 50 percent + 1 + ret diff --git a/home/battle_vars.asm b/home/battle_vars.asm index 29acb040..6fc94409 100755 --- a/home/battle_vars.asm +++ b/home/battle_vars.asm @@ -107,5 +107,5 @@ BattleVarLocations: dw wPlayerMoveStructPower, wEnemyMoveStructPower dw wPlayerMoveStructType, wEnemyMoveStructType dw wCurPlayerMove, wCurEnemyMove - dw wLastEnemyCounterMove, wLastPlayerCounterMove + dw wLastPlayerCounterMove, wLastEnemyCounterMove dw wLastPlayerMove, wLastEnemyMove @@ -277,7 +277,9 @@ JohtoGrassWildMons: dr $2ab35, $2c000 SECTION "bankb", ROMX, BANK[$b] - dr $2c000, $2c352 + dr $2c000, $2c225 +AI_Redundant: + dr $2c225, $2c352 MoveDeletion: dr $2c352, $2c57a @@ -300,11 +302,26 @@ SECTION "bankc", ROMX, BANK[$c] dr $30000, $34000 SECTION "bankd", ROMX - dr $34000, $34923 + dr $34000, $34822 +BattleCommand_Stab: + dr $34822, $34918 +BattleCheckTypeMatchup:: + dr $34918, $34923 CheckTypeMatchup:: - dr $34923, $34a91 + dr $34923, $3499e +CheckPlayerMoveTypeMatchups:: + dr $3499e, $34a91 CheckAbleToSwitch:: - dr $34a91, $36313 + dr $34a91, $34b44 +FindAliveEnemyMons:: + dr $34b44, $3553d + +EnemyAttackDamage:: + dr $3553d, $35753 +BattleCommand_DamageCalc:: + dr $35753, $35868 +BattleCommand_ConstantDamage:: + dr $35868, $36313 RaiseStat: dr $36313, $364d7 @@ -321,15 +338,8 @@ GetItemHeldEffect: SECTION "banke", ROMX INCLUDE "engine/battle/ai/items.asm" -;INCLUDE "engine/battle/ai/scoring.asm" - dr $38583, $39217 - -AICheckEnemyMaxHP:: - dr $39217, $39247 -AICheckEnemyHalfHP:: - dr $39247, $3925e -AICheckEnemyQuarterHP:: - dr $3925e, $394f3 +INCLUDE "engine/battle/ai/scoring.asm" + GetTrainerClassName:: dr $394f3, $39562 TrainerClassAttributes:: @@ -904,7 +914,9 @@ UpdateUnownDex: CheckMagikarpLength: dr $fbc3c, $fbdd6 MagikarpHouseSign: - dr $fbdd6, $fc000 + dr $fbdd6, $fbdf1 +HiddenPowerDamage: + dr $fbdf1, $fc000 SECTION "bank3f", ROMX, BANK[$3f] nop @@ -1667,20 +1667,20 @@ wEnemySubStatus2:: ds 1 ; cb4c wEnemySubStatus3:: ds 1 ; cb4d wEnemySubStatus4:: ds 1 ; cb4e wEnemySubStatus5:: ds 1 ; cb4f -wcb50:: ds 1 ; cb50 +wPlayerRolloutCount:: db ; cb50 wcb51:: ds 1 ; cb51 wcb52:: ds 1 ; cb52 wcb53:: ds 1 ; cb53 wcb54:: ds 1 ; cb54 wcb55:: ds 1 ; cb55 -wcb56:: ds 1 ; cb56 +wPlayerFuryCutterCount:: db ; cb56 wcb57:: ds 1 ; cb57 wcb58:: ds 1 ; cb58 wEnemyConfuseCount:: db ; cb59 wEnemyToxicCount:: db ; cb5a wcb5b:: ds 1 ; cb5b wcb5c:: ds 1 ; cb5c -wcb5d:: ds 1 ; cb5d +wEnemyPerishCount:: db ; cb5d wEnemyFuryCutterCount:: db ; cb5e wEnemyProtectCount:: db ; cb5f wPlayerDamageTaken:: @@ -1760,24 +1760,24 @@ wcba6:: ds 1 ; cba6 wcba7:: ds 1 ; cba7 wcba8:: ds 1 ; cba8 wcba9:: ds 1 ; cba9 -wcbaa:: ds 1 ; cbaa -wcbab:: ds 1 ; cbab -wcbac:: ds 1 ; cbac -wcbad:: ds 1 ; cbad -wcbae:: ds 1 ; cbae -wcbaf:: ds 1 ; cbaf -wcbb0:: ds 1 ; cbb0 -wcbb1:: ds 1 ; cbb1 -wcbb2:: ds 1 ; cbb2 -wcbb3:: ds 1 ; cbb3 -wcbb4:: ds 1 ; cbb4 -wcbb5:: ds 1 ; cbb5 -wcbb6:: ds 1 ; cbb6 -wcbb7:: ds 1 ; cbb7 -wcbb8:: ds 1 ; cbb8 -wcbb9:: ds 1 ; cbb9 +wPlayerAtkLevel:: db ; cbaa +wPlayerDefLevel:: db ; cbab +wPlayerSpdLevel:: db ; cbac +wPlayerSAtkLevel:: db ; cbad +wPlayerSDefLevel:: db ; cbae +wPlayerAccLevel:: db ; cbaf +wPlayerEvaLevel:: db ; cbb0 + ds 1 +wEnemyAtkLevel:: db ; cbb2 +wEnemyDefLevel:: db ; cbb3 +wEnemySpdLevel:: db ; cbb4 +wEnemySAtkLevel:: db ; cbb5 +wEnemySDefLevel:: db ; cbb6 +wEnemyAccLevel:: db ; cbb7 +wEnemyEvaLevel:: db ; cbb8 + ds 1 wEnemyTurnsTaken:: db ; cbba -wcbbb:: ds 1 ; cbbb +wPlayerTurnsTaken:: db ; cbbb wcbbc:: ds 1 ; cbbc wcbbd:: ds 1 ; cbbd wcbbe:: ds 1 ; cbbe @@ -1803,15 +1803,18 @@ wcbd2:: ds 1 ; cbd2 wcbd3:: ds 1 ; cbd3 wcbd4:: ds 1 ; cbd4 wcbd5:: ds 1 ; cbd5 -wLastEnemyCounterMove:: ds 1 ; cbd6 -wLastPlayerCounterMove:: ds 1 ; cbd7 + +; exists so you can't counter on switch +wLastPlayerCounterMove:: db ; cbd6 +wLastEnemyCounterMove:: db ; cbd7 + wcbd8:: ds 1 ; cbd8 wcbd9:: ds 1 ; cbd9 wcbda:: ds 1 ; cbda wcbdb:: ds 1 ; cbdb -wcbdc:: ds 1 ; cbdc -wcbdd:: ds 1 ; cbdd -wcbde:: ds 1 ; cbde +wPlayerMinimized:: db ; cbdc +wPlayerScreens:: db ; cbdd +wEnemyScreens:: db ; cbde wcbdf:: ds 1 ; cbdf wcbe0:: ds 1 ; cbe0 wcbe1:: ds 1 ; cbe1 @@ -1821,7 +1824,7 @@ wcbe4:: ds 1 ; cbe4 wcbe5:: ds 1 ; cbe5 wcbe6:: ds 1 ; cbe6 wcbe7:: ds 1 ; cbe7 -wcbe8:: ds 1 ; cbe8 +wBattleWeather:: db ; cbe8 wcbe9:: ds 1 ; cbe9 wcbea:: ds 1 ; cbea wcbeb:: ds 1 ; cbeb @@ -1829,11 +1832,12 @@ wcbec:: ds 1 ; cbec wEnemyGoesFirst:: db ; cbed wcbee:: ds 1 ; cbee wEnemyIsSwitching:: db ; cbef -wcbf0:: ds 1 ; cbf0 -wcbf1:: ds 1 ; cbf1 -wcbf2:: ds 1 ; cbf2 -wcbf3:: ds 1 ; cbf3 -wcbf4:: ds 1 ; cbf4 +wPlayerUsedMoves:: ; cbf0 +; add a move that has been used once by the player +; added in order of use + ds NUM_MOVES + +wEnemyAISwitchScore:: db ; cbf4 wEnemySwitchMonParam:: db ; cbf5 wEnemySwitchMonIndex:: db ; cbf6 wcbf7:: ds 1 ; cbf7 @@ -1859,7 +1863,7 @@ wEnemyRageCounter:: db ; cc0a wcc0b:: ds 1 ; cc0b wcc0c:: ds 1 ; cc0c wcc0d:: ds 1 ; cc0d -wcc0e:: ds 1 ; cc0e +wPlayerWrapCount:: db ; cc0e wEnemyWrapCount:: db ; cc0f wcc10:: ds 1 ; cc10 wcc11:: ds 1 ; cc11 |