Tackling of various AI "TODO" comments, a little tidying

This commit is contained in:
Maruno17
2023-02-17 21:36:08 +00:00
parent 81d069eef1
commit 0e4053f837
14 changed files with 275 additions and 549 deletions

View File

@@ -91,13 +91,6 @@ module Battle::AI::Handlers
MoveBasePower = HandlerHash.new
GeneralMoveScore = HandlerHash.new
GeneralMoveAgainstTargetScore = HandlerHash.new
# TODO: Make HandlerHashes for these?
# Move type - uses main battle code via rough_type
# Move accuracy - uses main battle code via rough_accuracy
# Move target
# Move additional effect chance
# Move unselectable check
# Move failure check
ShouldSwitch = HandlerHash.new
def self.move_will_fail?(function_code, *args)

View File

@@ -33,32 +33,35 @@ class Battle::AI
set_up_move_check(move)
# Predict whether the move will fail (generally)
if @trainer.has_skill_flag?("PredictMoveFailure") && pbPredictMoveFailure
PBDebug.log_ai("#{@user.name} is considering using #{move.name}...")
PBDebug.log_ai("#{@user.name} is considering using #{@move.name}...")
PBDebug.log_score_change(MOVE_FAIL_SCORE - MOVE_BASE_SCORE, "move will fail")
add_move_to_choices(choices, idxMove, MOVE_FAIL_SCORE)
next
end
# Get the move's target type
target_data = @move.pbTarget(@user.battler)
# TODO: Alter target_data if user has Protean and move is Curse.
if @move.function == "CurseTargetOrLowerUserSpd1RaiseUserAtkDef1" &&
@move.rough_type == :GHOST && @user.has_active_ability?([:LIBERO, :PROTEAN])
target_data = GameData::Target.get((Settings::MECHANICS_GENERATION >= 8) ? :RandomNearFoe : :NearFoe)
end
case target_data.num_targets
when 0 # No targets, affects the user or a side or the whole field
# Includes: BothSides, FoeSide, None, User, UserSide
PBDebug.log_ai("#{@user.name} is considering using #{move.name}...")
PBDebug.log_ai("#{@user.name} is considering using #{@move.name}...")
score = MOVE_BASE_SCORE
PBDebug.logonerr { score = pbGetMoveScore }
add_move_to_choices(choices, idxMove, score)
when 1 # One target to be chosen by the trainer
# Includes: Foe, NearAlly, NearFoe, NearOther, Other, RandomNearFoe, UserOrNearAlly
# TODO: Figure out first which targets are valid. Includes the call to
# pbMoveCanTarget?, but also includes move-redirecting effects
# like Lightning Rod. Skip any battlers that can't be targeted.
redirected_target = get_redirected_target(target_data)
num_targets = 0
@battle.allBattlers.each do |b|
next if redirected_target && b.index != redirected_target
next if !@battle.pbMoveCanTarget?(@user.battler.index, b.index, target_data)
# TODO: Should this sometimes consider targeting an ally? See def
# pbGetMoveScoreAgainstTarget for more information.
next if target_data.targets_foe && !@user.battler.opposes?(b)
PBDebug.log_ai("#{@user.name} is considering using #{move.name} against #{b.name} (#{b.index})...")
PBDebug.log_ai("#{@user.name} is considering using #{@move.name} against #{b.name} (#{b.index})...")
score = MOVE_BASE_SCORE
PBDebug.logonerr { score = pbGetMoveScore([b]) }
add_move_to_choices(choices, idxMove, score, b.index)
@@ -72,7 +75,7 @@ class Battle::AI
next if !@battle.pbMoveCanTarget?(@user.battler.index, b.index, target_data)
targets.push(b)
end
PBDebug.log_ai("#{@user.name} is considering using #{move.name}...")
PBDebug.log_ai("#{@user.name} is considering using #{@move.name}...")
score = MOVE_BASE_SCORE
PBDebug.logonerr { score = pbGetMoveScore(targets) }
add_move_to_choices(choices, idxMove, score)
@@ -82,6 +85,44 @@ class Battle::AI
return choices
end
def get_redirected_target(target_data)
return nil if @move.move.cannotRedirect?
return nil if !target_data.can_target_one_foe? || target_data.num_targets != 1
return nil if @user.has_active_ability?([:PROPELLERTAIL, :STALWART])
priority = @battle.pbPriority(true)
near_only = !target_data.can_choose_distant_target?
# Spotlight, Follow Me/Rage Powder
new_target = -1
strength = 100 # Lower strength takes priority
priority.each do |b|
next if b.fainted? || b.effects[PBEffects::SkyDrop] >= 0
next if !b.opposes?(@user.battler)
next if near_only && !b.near?(@user.battler)
if b.effects[PBEffects::Spotlight] > 0 && b.effects[PBEffects::Spotlight] - 50 < strength
new_target = b.index
strength = b.effects[PBEffects::Spotlight] - 50 # Spotlight takes priority
elsif (b.effects[PBEffects::RagePowder] && @user.battler.affectedByPowder?) ||
(b.effects[PBEffects::FollowMe] > 0 && b.effects[PBEffects::FollowMe] < strength)
new_target = b.index
strength = b.effects[PBEffects::FollowMe]
end
end
return new_target if new_target
calc_type = @move.rough_type
priority.each do |b|
next if b.index == @user.index
next if near_only && !b.near?(@user.battler)
case calc_type
when :ELECTRIC
new_target = b.index if b.hasActiveAbility?(:LIGHTNINGROD)
when :WATER
new_target = b.index if b.hasActiveAbility?(:STORMDRAIN)
end
break if new_target >= 0
end
return new_target
end
def add_move_to_choices(choices, idxMove, score, idxTarget = -1)
choices.push([idxMove, score, idxTarget])
# If the user is a wild Pokémon, doubly prefer one of its moves (the choice
@@ -123,19 +164,16 @@ class Battle::AI
#=============================================================================
# Returns whether the move will definitely fail (assuming no battle conditions
# change between now and using the move).
# TODO: Add skill checks in here for particular calculations?
#=============================================================================
def pbPredictMoveFailure
# TODO: Something involving user.battler.usingMultiTurnAttack? (perhaps
# earlier than this?).
# User is asleep and will not wake up
return true if @user.battler.asleep? && @user.statusCount > 1 && !@move.move.usableWhenAsleep?
# User is awake and can't use moves that are only usable when asleep
return true if !@user.battler.asleep? && @move.move.usableWhenAsleep?
# User will be truanting
# TODO: Should Truanting treat all moves as failing? If it does, it will
# trigger switching due to terrible moves.
# return true if @user.has_active_ability?(:TRUANT) && @user.effects[PBEffects::Truant]
# NOTE: Truanting is not considered, because if it is, a Pokémon with Truant
# will want to switch due to terrible moves every other round (because
# all of its moves will fail), and this is disruptive and shouldn't be
# how such Pokémon behave.
# Primal weather
return true if @battle.pbWeather == :HeavyRain && @move.rough_type == :FIRE
return true if @battle.pbWeather == :HarshSun && @move.rough_type == :WATER
@@ -151,8 +189,7 @@ class Battle::AI
return true if @battle.field.terrain == :Psychic && @target.battler.affectedByTerrain? &&
@target.opposes?(@user) && @move.rough_priority(@user) > 0
# Immunity because of ability
# TODO: Check for target-redirecting abilities that also provide immunity.
# If an ally has such an ability, may want to just not prefer the move
# TODO: If an ally has such an ability, may want to just not prefer the move
# instead of predicting its failure, as might want to hit the ally
# after all.
return true if @move.move.pbImmunityByAbility(@user.battler, @target.battler, false)
@@ -212,7 +249,6 @@ class Battle::AI
return MOVE_FAIL_SCORE
end
else
# TODO: Can this accounting for multiple targets be improved somehow?
score /= affected_targets if affected_targets > 1 # Average the score against multiple targets
# Bonus for affecting multiple targets
if @trainer.has_skill_flag?("PreferMultiTargetMoves") && affected_targets > 1

View File

@@ -7,7 +7,8 @@ class Battle::AI
# could target a foe but is targeting an ally, the score is also inverted, but
# only because it is inverted again in def pbGetMoveScoreAgainstTarget.
#=============================================================================
def get_score_for_target_stat_raise(score, target, stat_changes, whole_effect = true, fixed_change = false)
def get_score_for_target_stat_raise(score, target, stat_changes, whole_effect = true,
fixed_change = false, ignore_contrary = false)
whole_effect = false if @move.damagingMove?
# Decide whether the target raising its stat(s) is a good thing
desire_mult = 1
@@ -15,13 +16,13 @@ class Battle::AI
(@move.pbTarget(@user.battler).targets_foe && target.index != @user.index)
desire_mult = -1
end
# Discard status move/don't prefer damaging move if target has Contrary
# TODO: Maybe this should return get_score_for_target_stat_drop if Contrary
# applies and desire_mult < 1.
if !fixed_change && !@battle.moldBreaker && target.has_active_ability?(:CONTRARY) && desire_mult > 0
ret = (whole_effect) ? MOVE_USELESS_SCORE : score - 20
PBDebug.log_score_change(ret - score, "don't prefer raising target's stats (it has Contrary)")
return ret
# If target has Contrary, use different calculations to score the stat change
if !ignore_contrary && !fixed_change && !@battle.moldBreaker && target.has_active_ability?(:CONTRARY)
if desire_mult > 0 && whole_effect
PBDebug.log_score_change(MOVE_USELESS_SCORE - score, "don't prefer raising target's stats (it has Contrary)")
return MOVE_USELESS_SCORE
end
return get_score_for_target_stat_drop(score, target, stat_changes, whole_effect, fixed_change, true)
end
# Don't make score changes if target will faint from EOR damage
if target.rough_end_of_round_damage >= target.hp
@@ -42,7 +43,6 @@ class Battle::AI
return ret
end
end
# Figure out which stat raises can happen
real_stat_changes = []
stat_changes.each_with_index do |stat, idx|
@@ -66,10 +66,8 @@ class Battle::AI
if real_stat_changes.length == 0
return (whole_effect) ? MOVE_USELESS_SCORE : score
end
# Make score changes based on the general concept of raising stats at all
score = get_target_stat_raise_score_generic(score, target, real_stat_changes, desire_mult)
# Make score changes based on the specific changes to each stat that will be
# raised
real_stat_changes.each do |change|
@@ -81,7 +79,6 @@ class Battle::AI
PBDebug.log_score_change(score - old_score, "raising the target's #{GameData::Stat.get(change[0]).name} by #{change[1]}")
end
end
return score
end
@@ -146,12 +143,10 @@ class Battle::AI
# able to 2HKO the target.
# TODO: Prefer if foe is semi-invulnerable and target is faster (can't hit
# the foe anyway).
# Prefer if move is a status move and it's the user's first/second turn
if @user.turnCount < 2 && @move.statusMove?
score += total_increment * desire_mult * 4
end
# Prefer if user is at high HP, don't prefer if user is at low HP
if target.index != @user.index
if @user.hp >= @user.totalhp * 0.7
@@ -166,97 +161,8 @@ class Battle::AI
else
score += total_increment * desire_mult * ((100 * target.hp / target.totalhp) - 50) / 4 # +5 to -12 per stage
end
# TODO: Look at abilities that trigger upon stat raise. There are none.
return score
=begin
mini_score = 1.0
# Determine whether the move boosts Attack, Special Attack or Speed (Bulk Up
# is sometimes not considered a sweeping move)
sweeping_stat = false
offensive_stat = false
stat_changes.each do |change|
next if ![:ATTACK, :SPECIAL_ATTACK, :SPEED].include?(change[0])
sweeping_stat = true
next if @move.function == "RaiseUserAtkDef1" # Bulk Up (+Atk +Def)
offensive_stat = true
break
end
# TODO: Prefer if user's moves won't do much damage.
# Prefer if user has something that will limit damage taken
mini_score *= 1.3 if @user.effects[PBEffects::Substitute] > 0 ||
(@user.form == 0 && @user.ability_id == :DISGUISE)
# Don't prefer if user is badly poisoned
mini_score *= 0.2 if @user.effects[PBEffects::Toxic] > 0 && !offensive_stat
# Don't prefer if user is confused
if @user.effects[PBEffects::Confusion] > 0
# TODO: Especially don't prefer if the move raises Atk. Even more so if
# the move raises the stat by 2+. Not quite so much if the move also
# raises Def.
mini_score *= 0.5
end
# Don't prefer if user is infatuated or Leech Seeded
if @user.effects[PBEffects::Attract] >= 0 || @user.effects[PBEffects::LeechSeed] >= 0
mini_score *= (offensive_stat) ? 0.6 : 0.3
end
# Don't prefer if user has an ability or item that will force it to switch
# out
if @user.hp < @user.totalhp * 3 / 4
mini_score *= 0.3 if @user.hasActiveAbility?([:EMERGENCYEXIT, :WIMPOUT])
mini_score *= 0.3 if @user.hasActiveItem?(:EJECTBUTTON)
end
# Prefer if target has a status problem
if @target.status != :NONE
mini_score *= (sweeping_stat) ? 1.2 : 1.1
case @target.status
when :SLEEP, :FROZEN
mini_score *= 1.3
when :BURN
# TODO: Prefer if the move boosts Sp Def.
mini_score *= 1.1 if !offensive_stat
end
end
# Prefer if target is yawning
if @target.effects[PBEffects::Yawn] > 0
mini_score *= (sweeping_stat) ? 1.7 : 1.3
end
# Prefer if target is recovering after Hyper Beam
if @target.effects[PBEffects::HyperBeam] > 0
mini_score *= (sweeping_stat) ? 1.3 : 1.2
end
# Prefer if target is Encored into a status move
if @target.effects[PBEffects::Encore] > 0 &&
GameData::Move.get(@target.effects[PBEffects::EncoreMove]).category == 2 # Status move
# TODO: Why should this check greatly prefer raising both the user's defences?
if sweeping_stat || @move.function == "RaiseUserDefSpDef1" # +Def +SpDef
mini_score *= 1.5
else
mini_score *= 1.3
end
end
# TODO: Don't prefer if target has previously used a move that would force
# the user to switch (or Yawn/Perish Song which encourage it). Prefer
# instead if the move raises evasion. Note this comes after the
# dissociation of Bulk Up from sweeping_stat.
if @trainer.medium_skill?
# TODO: Prefer if the maximum damage the target has dealt wouldn't hurt
# the user much.
end
# Don't prefer if it's not a single battle
if !@battle.singleBattle?
mini_score *= (offensive_stat) ? 0.25 : 0.5
end
return mini_score
=end
end
#=============================================================================
@@ -287,7 +193,6 @@ class Battle::AI
inc = (has_special_moves) ? 10 : 20
score += inc * inc_mult
end
when :DEFENSE
# Modify score depending on current stat stage
if old_stage >= 2 && increment == 1
@@ -295,7 +200,6 @@ class Battle::AI
else
score += 10 * inc_mult
end
when :SPECIAL_ATTACK
# Modify score depending on current stat stage
# More strongly prefer if the target has no physical moves
@@ -308,7 +212,6 @@ class Battle::AI
inc = (has_physical_moves) ? 10 : 20
score += inc * inc_mult
end
when :SPECIAL_DEFENSE
# Modify score depending on current stat stage
if old_stage >= 2 && increment == 1
@@ -316,7 +219,6 @@ class Battle::AI
else
score += 10 * inc_mult
end
when :SPEED
# Prefer if target is slower than a foe
# TODO: Don't prefer if the target is too much slower than any foe that it
@@ -345,7 +247,6 @@ class Battle::AI
if target.has_active_ability?(:SPEEDBOOST)
score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
end
when :ACCURACY
# Modify score depending on current stat stage
if old_stage >= 2 && increment == 1
@@ -361,15 +262,11 @@ class Battle::AI
score += 10 * inc_mult
end
end
when :EVASION
# Prefer if a foe will (probably) take damage at the end of the round
# TODO: Should this take into account EOR healing, one-off damage and
# damage-causing effects that wear off naturally (like Sea of Fire)?
# TODO: Emerald AI also prefers if target is rooted via Ingrain.
# Prefer if a foe of the target will take damage at the end of the round
each_foe_battler(target.side) do |b, i|
eor_damage = b.rough_end_of_round_damage
next if eor_damage <= 0
score += 4 * inc_mult if eor_damage > 0
end
# Modify score depending on current stat stage
if old_stage >= 2 && increment == 1
@@ -377,9 +274,7 @@ class Battle::AI
else
score += 10 * inc_mult
end
end
# Prefer if target has Stored Power
if target.has_move_with_function?("PowerHigherWithUserPositiveStatStages")
score += 5 * increment * desire_mult
@@ -389,212 +284,7 @@ class Battle::AI
next if !b.has_move_with_function?("PowerHigherWithTargetPositiveStatStages")
score -= 5 * increment * desire_mult
end
return score
=begin
mini_score = 1.0
case stat
when :ATTACK
# Prefer if user can definitely survive a hit no matter how powerful, and
# it won't be hurt by weather
# if @user.hp == @user.totalhp &&
# (@user.hasActiveItem?(:FOCUSSASH) || (!@battle.moldBreaker && @user.hasActiveAbility?(:STURDY)))
# if !(@battle.pbWeather == :Sandstorm && @user.takesSandstormDamage?) &&
# !(@battle.pbWeather == :Hail && @user.takesHailDamage?) &&
# !(@battle.pbWeather == :ShadowSky && @user.takesShadowSkyDamage?)
# mini_score *= 1.4
# end
# end
# Prefer if user has the Sweeper role
# TODO: Is 1.1x for RaiseUserAtkDefAcc1 Coil (+Atk, +Def, +acc).
# mini_score *= 1.3 if check_battler_role(@user, BattleRole::SWEEPER)
# # Don't prefer if user is burned or paralysed
# mini_score *= 0.5 if @user.status == :BURN || @user.status == :PARALYSIS
# # Don't prefer if user's Speed stat is lowered
# sum_stages = @user.stages[:SPEED]
# mini_score *= 1 + sum_stages * 0.05 if sum_stages < 0
# # TODO: Prefer if target has previously used a HP-restoring move.
# # TODO: Don't prefer if some of foes' stats are raised.
# sum_stages = 0
# [:ATTACK, :SPECIAL_ATTACK, :SPEED].each do |s|
# sum_stages += @target.stages[s]
# end
# mini_score *= 1 - sum_stages * 0.05 if sum_stages > 0
# # TODO: Don't prefer if target has Speed Boost (+Spd at end of each round).
# mini_score *= 0.6 if @target.hasActiveAbility?(:SPEEDBOOST)
# # TODO: Don't prefer if the target has previously used a priority move.
when :DEFENSE
# Prefer if user has a healing item
# TODO: Is 1.1x for RaiseUserAtkDefAcc1 Coil (+Atk, +Def, +acc).
# mini_score *= 1.2 if @user.hasActiveItem?(:LEFTOVERS) ||
# (@user.hasActiveItem?(:BLACKSLUDGE) && @user.pbHasType?(:POISON))
# Prefer if user knows any healing moves
# # TODO: Is 1.2x for RaiseUserAtkDefAcc1 Coil (+Atk, +Def, +acc).
# mini_score *= 1.3 if check_for_move(@user) { |m| m.healingMove? }
# Prefer if user knows Pain Split or Leech Seed
# # TODO: Leech Seed is 1.2x for RaiseUserAtkDefAcc1 Coil (+Atk, +Def, +acc).
# mini_score *= 1.2 if @user.pbHasMoveFunction?("UserTargetAverageHP") # Pain Split
# mini_score *= 1.3 if @user.pbHasMoveFunction?("StartLeechSeedTarget") # Leech Seed
# Prefer if user has certain roles
# # TODO: Is 1.1x for RaiseUserAtkDefAcc1 Coil (+Atk, +Def, +acc).
# mini_score *= 1.3 if check_battler_role(@user, BattleRole::PHYSICALWALL, BattleRole::SPECIALWALL)
# Don't prefer if user is badly poisoned
mini_score *= 0.2 if @user.effects[PBEffects::Toxic] > 0
# # TODO: Prefer if foes have higher Attack than Special Attack, and user
# # doesn't have a wall role, user is faster and user has at least 75%
# # HP. Don't prefer instead if user is slower (ignore HP).
# TODO: Don't prefer if previous damage done by foes wouldn't hurt the
# user much.
when :SPEED
# Don't prefer if user has Speed Boost
# mini_score *= 0.6 if @user.hasActiveAbility?(:SPEEDBOOST)
# Prefer if user can definitely survive a hit no matter how powerful, and
# it won't be hurt by weather
# if @user.hp == @user.totalhp &&
# (@user.hasActiveItem?(:FOCUSSASH) || (!@battle.moldBreaker && @user.hasActiveAbility?(:STURDY)))
# if !(@battle.pbWeather == :Sandstorm && @user.takesSandstormDamage?) &&
# !(@battle.pbWeather == :Hail && @user.takesHailDamage?) &&
# !(@battle.pbWeather == :ShadowSky && @user.takesShadowSkyDamage?)
# mini_score *= 1.4
# end
# end
# Prefer if user has the Sweeper role
# mini_score *= 1.3 if check_battler_role(@user, BattleRole::SWEEPER)
# TODO: Don't prefer if Trick Room applies or any foe has previously used
# Trick Room.
# mini_score *= 0.2 if @battle.field.effects[PBEffects::TrickRoom] > 0
# # Prefer if user's Attack/SpAtk stat (whichever is higher) is lowered
# # TODO: Why?
# if @user.attack > @user.spatk
# sum_stages = @user.stages[:ATTACK]
# mini_score *= 1 - sum_stages * 0.05 if sum_stages < 0
# else
# sum_stages = @user.stages[:SPATK]
# mini_score *= 1 - sum_stages * 0.05 if sum_stages < 0
# end
# # Prefer if user has Moxie
# mini_score *= 1.3 if @user.hasActiveAbility?(:MOXIE)
# # Don't prefer if user is burned or paralysed
# mini_score *= 0.2 if @user.status == :PARALYSIS
# # TODO: Don't prefer if target has raised defenses.
# sum_stages = 0
# [:DEFENSE, :SPECIAL_DEFENSE].each { |s| sum_stages += @target.stages[s] }
# mini_score *= 1 - sum_stages * 0.05 if sum_stages > 0
# # TODO: Don't prefer if the target has previously used a priority move.
# # TODO: Don't prefer if user is already faster than the target and there's
# # only 1 unfainted foe (this check is done by Agility/Autotomize
# # (both +2 Spd) only in Reborn.)
when :SPECIAL_ATTACK
# Prefer if user can definitely survive a hit no matter how powerful, and
# it won't be hurt by weather
# if @user.hp == @user.totalhp &&
# (@user.hasActiveItem?(:FOCUSSASH) || (!@battle.moldBreaker && @user.hasActiveAbility?(:STURDY)))
# if !(@battle.pbWeather == :Sandstorm && @user.takesSandstormDamage?) &&
# !(@battle.pbWeather == :Hail && @user.takesHailDamage?) &&
# !(@battle.pbWeather == :ShadowSky && @user.takesShadowSkyDamage?)
# mini_score *= 1.4
# end
# end
# Prefer if user has the Sweeper role
# mini_score *= 1.3 if check_battler_role(@user, BattleRole::SWEEPER)
# # Don't prefer if user's Speed stat is lowered
# sum_stages = @user.stages[:SPEED]
# mini_score *= 1 + sum_stages * 0.05 if sum_stages < 0
# # TODO: Prefer if target has previously used a HP-restoring move.
# # TODO: Don't prefer if some of foes' stats are raised
# sum_stages = 0
# [:ATTACK, :SPECIAL_ATTACK, :SPEED].each do |s|
# sum_stages += @target.stages[s]
# end
# mini_score *= 1 - sum_stages * 0.05 if sum_stages > 0
# # TODO: Don't prefer if target has Speed Boost (+Spd at end of each round)
# mini_score *= 0.6 if @target.hasActiveAbility?(:SPEEDBOOST)
# # TODO: Don't prefer if the target has previously used a priority move.
when :SPECIAL_DEFENSE
# Prefer if user has a healing item
# mini_score *= 1.2 if @user.hasActiveItem?(:LEFTOVERS) ||
# (@user.hasActiveItem?(:BLACKSLUDGE) && @user.pbHasType?(:POISON))
# Prefer if user knows any healing moves
# mini_score *= 1.3 if check_for_move(@user) { |m| m.healingMove? }
# Prefer if user knows Pain Split or Leech Seed
# mini_score *= 1.2 if @user.pbHasMoveFunction?("UserTargetAverageHP") # Pain Split
# mini_score *= 1.3 if @user.pbHasMoveFunction?("StartLeechSeedTarget") # Leech Seed
# Prefer if user has certain roles
# mini_score *= 1.3 if check_battler_role(@user, BattleRole::PHYSICALWALL, BattleRole::SPECIALWALL)
# # TODO: Prefer if foes have higher Special Attack than Attack.
# TODO: Don't prefer if previous damage done by foes wouldn't hurt the
# user much.
when :ACCURACY
# Prefer if user knows any weaker moves
mini_score *= 1.1 if check_for_move(@user) { |m| m.damagingMove? && m.power < 95 }
# Prefer if target has a raised evasion
sum_stages = @target.stages[:EVASION]
mini_score *= 1 + sum_stages * 0.05 if sum_stages > 0
# Prefer if target has an item that lowers foes' accuracy
mini_score *= 1.1 if @target.hasActiveItem?([:BRIGHTPOWDER, :LAXINCENSE])
# Prefer if target has an ability that lowers foes' accuracy
# TODO: Tangled Feet while user is confused?
if (@battle.pbWeather == :Sandstorm && @target.hasActiveAbility?(:SANDVEIL)) ||
(@battle.pbWeather == :Hail && @target.hasActiveAbility?(:SNOWCLOAK))
mini_score *= 1.1
end
when :EVASION
# Prefer if user has a healing item
mini_score *= 1.2 if @user.hasActiveItem?(:LEFTOVERS) ||
(@user.hasActiveItem?(:BLACKSLUDGE) && @user.pbHasType?(:POISON))
# Prefer if user has an item that lowers foes' accuracy
mini_score *= 1.3 if @user.hasActiveItem?([:BRIGHTPOWDER, :LAXINCENSE])
# Prefer if user has an ability that lowers foes' accuracy
# TODO: Tangled Feet while user is confused?
if (@battle.pbWeather == :Sandstorm && @user.hasActiveAbility?(:SANDVEIL)) ||
(@battle.pbWeather == :Hail && @user.hasActiveAbility?(:SNOWCLOAK))
mini_score *= 1.3
end
# Prefer if user knows any healing moves
mini_score *= 1.3 if check_for_move(@user) { |m| move.healingMove? }
# Prefer if user knows Pain Split or Leech Seed
mini_score *= 1.2 if @user.pbHasMoveFunction?("UserTargetAverageHP") # Pain Split
mini_score *= 1.3 if @user.pbHasMoveFunction?("StartLeechSeedTarget") # Leech Seed
# Prefer if user has certain roles
mini_score *= 1.3 if check_battler_role(@user, BattleRole::PHYSICALWALL, BattleRole::SPECIALWALL)
# TODO: Don't prefer if user's evasion stat is raised
# TODO: Don't prefer if target has No Guard.
mini_score *= 0.2 if @target.hasActiveAbility?(:NOGUARD)
# TODO: Don't prefer if target has previously used any moves that never miss.
end
# TODO: Don't prefer if any foe has previously used a stat stage-clearing
# move (Clear Smog/Haze).
mini_score *= 0.3 if check_for_move(@target) { |m|
["ResetTargetStatStages", "ResetAllBattlersStatStages"].include?(m.function)
} # Clear Smog, Haze
# TODO: Prefer if user is faster than the target.
# TODO: Is 1.3x for RaiseUserAtkDefAcc1 Coil (+Atk, +Def, +acc).
mini_score *= 1.5 if @user.faster_than?(@target)
# TODO: Don't prefer if target is a higher level than the user
if @target.level > @user.level + 5
mini_score *= 0.6
if @target.level > @user.level + 10
mini_score *= 0.2
end
end
return mini_score
=end
end
#=============================================================================
@@ -606,10 +296,9 @@ class Battle::AI
# ally, but only because it is inverted in def pbGetMoveScoreAgainstTarget
# instead.
# TODO: Revisit this method as parts may need rewriting.
# TODO: fixed_change should make this ignore Mist/Clear Body/other effects
# that prevent increments/decrements to stat stages.
#=============================================================================
def get_score_for_target_stat_drop(score, target, stat_changes, whole_effect = true, fixed_change = false)
def get_score_for_target_stat_drop(score, target, stat_changes, whole_effect = true,
fixed_change = false, ignore_contrary = false)
whole_effect = false if @move.damagingMove?
# Decide whether the target lowering its stat(s) is a good thing
desire_mult = -1
@@ -617,13 +306,13 @@ class Battle::AI
(@move.pbTarget(@user.battler).targets_foe && target.index != @user.index)
desire_mult = 1
end
# Discard status move/don't prefer damaging move if target has Contrary
# TODO: Maybe this should return get_score_for_target_stat_raise if Contrary
# applies and desire_mult < 0.
if !fixed_change && !@battle.moldBreaker && target.has_active_ability?(:CONTRARY) && desire_mult > 0
ret = (whole_effect) ? MOVE_USELESS_SCORE : score - 20
PBDebug.log_score_change(ret - score, "don't prefer lowering target's stats (it has Contrary)")
return ret
# If target has Contrary, use different calculations to score the stat change
if !ignore_contrary && !fixed_change && !@battle.moldBreaker && target.has_active_ability?(:CONTRARY)
if desire_mult > 0 && whole_effect
PBDebug.log_score_change(MOVE_USELESS_SCORE - score, "don't prefer lowering target's stats (it has Contrary)")
return MOVE_USELESS_SCORE
end
return get_score_for_target_stat_raise(score, target, stat_changes, whole_effect, fixed_change, true)
end
# Don't make score changes if target will faint from EOR damage
if target.rough_end_of_round_damage >= target.hp
@@ -642,7 +331,6 @@ class Battle::AI
PBDebug.log(" ignore stat change (target's foes have Unaware)")
return ret
end
# Figure out which stat drops can happen
real_stat_changes = []
stat_changes.each_with_index do |stat, idx|
@@ -666,10 +354,8 @@ class Battle::AI
if real_stat_changes.length == 0
return (whole_effect) ? MOVE_USELESS_SCORE : score
end
# Make score changes based on the general concept of lowering stats at all
score = get_target_stat_drop_score_generic(score, target, real_stat_changes, desire_mult)
# Make score changes based on the specific changes to each stat that will be
# lowered
real_stat_changes.each do |change|
@@ -681,7 +367,6 @@ class Battle::AI
PBDebug.log_score_change(score - old_score, "lowering the target's #{GameData::Stat.get(change[0]).name} by #{change[1]}")
end
end
return score
end
@@ -746,12 +431,10 @@ class Battle::AI
# TODO: Don't prefer if target is slower than user but is predicted to be able
# to 2HKO user.
# TODO: Don't prefer if target is semi-invulnerable and user is faster.
# Prefer if move is a status move and it's the user's first/second turn
if @user.turnCount < 2 && @move.statusMove?
score += total_decrement * desire_mult * 4
end
# Prefer if user is at high HP, don't prefer if user is at low HP
if target.index != @user.index
if @user.hp >= @user.totalhp * 0.7
@@ -766,9 +449,7 @@ class Battle::AI
else
score += total_decrement * desire_mult * ((100 * target.hp / target.totalhp) - 50) / 6 # +3 to -8 per stage
end
# TODO: Look at abilities that trigger upon stat lowering.
return score
end
@@ -801,7 +482,6 @@ class Battle::AI
dec = (has_special_moves) ? 5 : 10
score += dec * dec_mult
end
when :DEFENSE
# Modify score depending on current stat stage
if old_stage <= -2 && decrement == 1
@@ -809,7 +489,6 @@ class Battle::AI
else
score += 5 * dec_mult
end
when :SPECIAL_ATTACK
# Modify score depending on current stat stage
# More strongly prefer if the target has no physical moves
@@ -822,7 +501,6 @@ class Battle::AI
dec = (has_physical_moves) ? 5 : 10
score += dec * dec_mult
end
when :SPECIAL_DEFENSE
# Modify score depending on current stat stage
if old_stage <= -2 && decrement == 1
@@ -830,7 +508,6 @@ class Battle::AI
else
score += 5 * dec_mult
end
when :SPEED
# Prefer if target is faster than an ally
# TODO: Don't prefer if the target is too much faster than any ally and
@@ -849,7 +526,6 @@ class Battle::AI
if target.has_active_ability?(:SPEEDBOOST)
score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
end
when :ACCURACY
# Modify score depending on current stat stage
if old_stage <= -2 && decrement == 1
@@ -858,7 +534,6 @@ class Battle::AI
score += 5 * dec_mult
end
# TODO: Prefer if target is poisoned/toxiced/Leech Seeded/cursed.
when :EVASION
# Modify score depending on current stat stage
if old_stage <= -2 && decrement == 1
@@ -866,9 +541,7 @@ class Battle::AI
else
score += 5 * dec_mult
end
end
# Prefer if target has Stored Power
if target.has_move_with_function?("PowerHigherWithUserPositiveStatStages")
score += 5 * decrement * desire_mult
@@ -878,7 +551,6 @@ class Battle::AI
next if !b.has_move_with_function?("PowerHigherWithTargetPositiveStatStages")
score -= 5 * decrement * desire_mult
end
return score
end

View File

@@ -2,7 +2,7 @@
# because of them.
#===============================================================================
# TODO: Review score modifier.
#
#===============================================================================
# TODO:
# => Don't prefer damaging move if it won't KO, user has Stance Change and
@@ -24,13 +24,13 @@
#===============================================================================
# Don't prefer hitting a wild shiny Pokémon.
# TODO: Review score modifier.
#===============================================================================
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:shiny_target,
proc { |score, move, user, target, ai, battle|
if target.wild? && target.battler.shiny?
PBDebug.log_score_change(-40, "avoid attacking a shiny wild Pokémon")
score -= 40
old_score = score
score -= 15
PBDebug.log_score_change(score - old_score, "avoid attacking a shiny wild Pokémon")
end
next score
}
@@ -51,7 +51,7 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:add_predicted_damage,
if move.damagingMove?
dmg = move.rough_damage
old_score = score
score += ([30.0 * dmg / target.hp, 35].min).to_i
score += ([25.0 * dmg / target.hp, 30].min).to_i
PBDebug.log_score_change(score - old_score, "damaging move (predicted damage #{dmg} = #{100 * dmg / target.hp}% of target's HP)")
if dmg > target.hp * 1.1 # Predicted to KO the target
old_score = score
@@ -115,14 +115,13 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:target_semi_invulnerabl
)
#===============================================================================
# TODO: Review score modifier.
#
#===============================================================================
# TODO: Less prefer two-turn moves, as the foe can see it coming and prepare for
# it.
#===============================================================================
# If target is frozen, don't prefer moves that could thaw them.
# TODO: Review score modifier.
#===============================================================================
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:thawing_move_against_frozen_target,
proc { |score, move, user, target, ai, battle|
@@ -138,7 +137,7 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:thawing_move_against_fr
)
#===============================================================================
# TODO: Review score modifier.
#
#===============================================================================
# TODO: Prefer move if it has a high critical hit rate, critical hits are
# possible but not certain, and target has raised defences/user has
@@ -169,7 +168,7 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:flinching_effects,
)
#===============================================================================
# TODO: Review score modifier.
#
#===============================================================================
# TODO: Don't prefer contact move if making contact with the target could
# trigger an effect that's bad for the user (Static, etc.).
@@ -177,29 +176,13 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:flinching_effects,
# Baneful Bunker, and don't prefer move if so
#===============================================================================
# TODO: Review score modifier.
#
#===============================================================================
# TODO: Prefer a contact move if making contact with the target could trigger
# an effect that's good for the user (Poison Touch/Pickpocket).
#===============================================================================
# Don't prefer a dancing move if the target has the Dancer ability.
# TODO: Review score modifier.
# TODO: Check all battlers, not just the target.
#===============================================================================
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:dance_move_against_dancer,
proc { |score, move, user, target, ai, battle|
if move.move.danceMove? && target.has_active_ability?(:DANCER)
old_score = score
score -= 12
PBDebug.log_score_change(score - old_score, "don't want to use a dance move on a target with Dancer")
end
next score
}
)
#===============================================================================
# TODO: Review score modifier.
#
#===============================================================================
# TODO: Prefer a higher priority move if the user is slower than the foe(s) and
# the user is at risk of being knocked out. Consider whether the foe(s)
@@ -207,12 +190,32 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:dance_move_against_danc
# moves?
#===============================================================================
# TODO: Review score modifier.
# Don't prefer damaging moves if the target is Biding, unless the move will deal
# enough damage to KO the target before it retaliates (assuming the move is used
# repeatedly until the target retaliates). Doesn't do a score change if the user
# will be immune to Bide's damage.
#===============================================================================
# TODO: Don't prefer damaging moves if the target is Biding, unless the move
# will deal enough damage to KO the target before it retaliates (assuming
# the move is used repeatedly until the target retaliates). Don't worry
# about the target's Bide if the user will be immune to it.
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:avoid_damaging_a_biding_target,
proc { |score, move, user, target, ai, battle|
if ai.trainer.medium_skill? && target.effects[PBEffects::Bide] > 0 && move.damagingMove?
eff = user.effectiveness_of_type_against_battler(:NORMAL, target) # Bide is Normal type
if !Effectiveness.ineffective?(eff)
dmg = move.rough_damage
eor_dmg = target.rough_end_of_round_damage
hits_possible = target.effects[PBEffects::Bide] - 1
eor_dmg *= hits_possible
hits_possible += 1 if user.faster_than?(target)
if dmg * hits_possible + eor_dmg < target.hp * 1.05
old_score = score
score -= 15
PBDebug.log_score_change(score - old_score, "don't want to damage the Biding target")
end
end
end
next score
}
)
#===============================================================================
# Don't prefer damaging moves that will knock out the target if they are using
@@ -237,22 +240,53 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:avoid_knocking_out_dest
)
#===============================================================================
# TODO: Review score modifier.
#
#===============================================================================
# TODO: Don't prefer Fire-type moves if target has previously used Powder.
# TODO: Don't prefer Fire-type moves if target has previously used Powder and is
# faster than the user.
#===============================================================================
# TODO: Review score modifier.
#
#===============================================================================
# TODO: Check memory for whether target has previously used Ion Deluge, and
# don't prefer move if it's Normal-type and target is immune because
# of its ability (Lightning Rod, etc.).
#===============================================================================
# TODO: Review score modifier.
# Don't prefer a move that can be Magic Coated if the target (or any foe if the
# move doesn't have a target) has Magic Bounce.
#===============================================================================
# TODO: Don't prefer a move that can be Magic Coated if the target (or any foe
# if the move doesn't have a target) has Magic Bounce.
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:avoid_targeting_bouncable_move_against_Magic_Bouncer,
proc { |score, move, user, target, ai, battle|
if move.statusMove? && move.canMagicCoat? &&
!battle.moldBreaker && target.has_active_ability?(:MAGICBOUNCE)
old_score = score
score = Battle::AI::MOVE_USELESS_SCORE
PBDebug.log_score_change(score - old_score, "useless because target will Magic Bounce it")
end
next score
}
)
Battle::AI::Handlers::GeneralMoveScore.add(:avoid_bouncable_move_with_foe_Magic_Bouncer,
proc { |score, move, user, ai, battle|
if move.statusMove? && move.canMagicCoat? &&
move.pbTarget(user.battler).num_targets == 0 && !battle.moldBreaker
has_magic_bounce = false
ai.each_foe_battler(user.side) do |b, i|
next if !b.has_active_ability?(:MAGICBOUNCE)
has_magic_bounce = true
break
end
if has_magic_bounce
old_score = score
score = Battle::AI::MOVE_USELESS_SCORE
PBDebug.log_score_change(score - old_score, "useless because a foe will Magic Bounce it")
end
end
next score
}
)
#===============================================================================
#===============================================================================
@@ -261,13 +295,12 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:avoid_knocking_out_dest
#===============================================================================
#===============================================================================
# TODO: Review score modifier.
#
#===============================================================================
# TODO: Prefer Shadow moves.
# TODO: Prefer Shadow moves (for flavour).
#===============================================================================
# If user is frozen, prefer a move that can thaw the user.
# TODO: Review score modifier.
#===============================================================================
Battle::AI::Handlers::GeneralMoveScore.add(:thawing_move_when_frozen,
proc { |score, move, user, ai, battle|
@@ -285,6 +318,22 @@ Battle::AI::Handlers::GeneralMoveScore.add(:thawing_move_when_frozen,
}
)
#===============================================================================
# Don't prefer a dancing move if the target has the Dancer ability.
#===============================================================================
Battle::AI::Handlers::GeneralMoveScore.add(:dance_move_against_dancer,
proc { |score, move, user, ai, battle|
if move.move.danceMove?
old_score = score
ai.each_foe_battler(user.side) do |b, i|
score -= 12 if b.has_active_ability?(:DANCER)
end
PBDebug.log_score_change(score - old_score, "don't want to use a dance move because a foe has Dancer")
end
next score
}
)
#===============================================================================
# Pick a good move for the Choice items.
# TODO: Review score modifier.
@@ -298,26 +347,18 @@ Battle::AI::Handlers::GeneralMoveScore.add(:good_move_for_choice_item,
if move.statusMove? && move.function != "UserTargetSwapItems"
old_score = score
score -= 25
PBDebug.log_score_change(score - old_score, "move is not suitable to be Choiced into")
PBDebug.log_score_change(score - old_score, "don't want to be Choiced into a status move")
next score
end
# Don't prefer moves of certain types
# Don't prefer moves which are 0x against at least one type
move_type = move.rough_type
# Most unpreferred types are 0x effective against another type, except
# Fire/Water/Grass
# TODO: Actually check through the types for 0x instead of hardcoding
# them.
# TODO: Reborn separately doesn't prefer Fire/Water/Grass/Electric, also
# with a 0.95x score, meaning Electric can be 0.95x twice. Why are
# these four types not preferred? Maybe because they're all not
# very effective against Dragon.
unpreferred_types = [:NORMAL, :FIGHTING, :POISON, :GROUND, :GHOST,
:FIRE, :WATER, :GRASS, :ELECTRIC, :PSYCHIC, :DRAGON]
old_score = score
score -= 5 if unpreferred_types.include?(move_type)
GameData::Type.each do |type_data|
score -= 5 if type_data.immunities.include?(move_type)
end
# Don't prefer moves with lower accuracy
score = score * move.accuracy / 100 if move.accuracy > 0
# Don't prefer moves with low PP
score -= 10 if move.move.pp < 6
score -= 10 if move.move.pp <= 5
PBDebug.log_score_change(score - old_score, "move is less suitable to be Choiced into")
end
end
@@ -353,13 +394,13 @@ Battle::AI::Handlers::GeneralMoveScore.add(:prefer_damaging_moves_if_last_pokemo
)
#===============================================================================
# TODO: Review score modifier.
#
#===============================================================================
# TODO: Don't prefer a move that is stopped by Wide Guard if any foe has
# previously used Wide Guard.
#===============================================================================
# TODO: Review score modifier.
#
#===============================================================================
# TODO: Don't prefer sound move if user hasn't been Throat Chopped but a foe has
# previously used Throat Chop.

View File

@@ -181,7 +181,6 @@ class Battle::AI::AIBattler
return ret
end
# TODO: Cache calculated rough stats? Forget them in def refresh_battler.
def rough_stat(stat)
return @battler.pbSpeed if stat == :SPEED && @ai.trainer.high_skill?
stageMul = [2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8]
@@ -215,7 +214,8 @@ class Battle::AI::AIBattler
end
else
@battler.pbTypes(true).each do |defend_type|
# TODO: Need to check the move's pbCalcTypeModSingle.
# TODO: Need to check the move's pbCalcTypeModSingle because particular
# moves can modify that method to give different effectivenesses.
ret *= effectiveness_of_type_against_single_battler_type(type, defend_type, user)
end
ret *= 2 if self.effects[PBEffects::TarShot] && type == :FIRE
@@ -229,14 +229,10 @@ class Battle::AI::AIBattler
def ability; return @battler.ability; end
def ability_active?
# Only a high skill AI knows what an opponent's ability is
# return false if @ai.trainer.side != @side && !@ai.trainer.high_skill?
return @battler.abilityActive?
end
def has_active_ability?(ability, ignore_fainted = false)
# Only a high skill AI knows what an opponent's ability is
# return false if @ai.trainer.side != @side && !@ai.trainer.high_skill?
return @battler.hasActiveAbility?(ability, ignore_fainted)
end
@@ -250,14 +246,10 @@ class Battle::AI::AIBattler
def item; return @battler.item; end
def item_active?
# Only a high skill AI knows what an opponent's held item is
# return false if @ai.trainer.side != @side && !@ai.trainer.high_skill?
return @battler.itemActive?
end
def has_active_item?(item)
# Only a high skill AI knows what an opponent's held item is
# return false if @ai.trainer.side != @side && !@ai.trainer.high_skill?
return @battler.hasActiveItem?(item)
end
@@ -266,6 +258,7 @@ class Battle::AI::AIBattler
def check_for_move
ret = false
@battler.eachMove do |move|
next if move.pp == 0 && move.total_pp > 0
next unless yield move
ret = true
break
@@ -708,10 +701,9 @@ class Battle::AI::AIBattler
# battler if it has it.
# Return values are typically between -10 and +10. 0 is indifferent, positive
# values mean this battler benefits, negative values mean this battler suffers.
# TODO: This method assumes the ability isn't being negated. Should it return
# 0 if it is? The calculations that call this method separately check
# for it being negated, because they need to do something special in
# that case, so I think it's okay for this method to ignore negation.
# NOTE: This method assumes the ability isn't being negated. The calculations
# that call this method separately check for it being negated, because
# they need to do something special in that case.
def wants_ability?(ability = :NONE)
ability = ability.id if !ability.is_a?(Symbol) && ability.respond_to?("id")
# TODO: Ideally replace the above list of ratings with context-sensitive
@@ -729,9 +721,7 @@ class Battle::AI::AIBattler
when :GALEWINGS
return 0 if !check_for_move { |m| m.type == :FLYING }
when :HUGEPOWER, :PUREPOWER
return 0 if !check_for_move { |m| m.physicalMove?(m.type) &&
m.function != "UseUserDefenseInsteadOfUserAttack" &&
m.function != "UseTargetAttackInsteadOfUserAttack" }
return 0 if !ai.stat_raise_worthwhile?(self, :ATTACK, true)
when :IRONFIST
return 0 if !check_for_move { |m| m.punchingMove? }
when :LIQUIDVOICE

View File

@@ -40,14 +40,14 @@ class Battle::AI::AIMove
#=============================================================================
def pbTarget(_user)
return @move.pbTarget(_user)
def pbTarget(user)
return @move.pbTarget((user.is_a?(Battle::AI::AIBattler)) ? user.battler : user)
end
# Returns whether this move targets multiple battlers.
def targets_multiple_battlers?
user_battler = @ai.user.battler
target_data = @move.pbTarget(user_battler)
target_data = pbTarget(user_battler)
return false if target_data.num_targets <= 1
num_targets = 0
case target_data.id
@@ -70,8 +70,12 @@ class Battle::AI::AIMove
#=============================================================================
def rough_priority(user)
# TODO: More calculations here.
return @move.priority
ret = @move.pbPriority(user.battler)
if user.ability_active?
ret = Battle::AbilityEffects.triggerPriorityChange(user.ability, user.battler, @move, ret)
user.battler.effects[PBEffects::Prankster] = false # Untrigger this
end
return ret
end
#=============================================================================
@@ -205,7 +209,6 @@ class Battle::AI::AIMove
)
user.effects[PBEffects::GemConsumed] = nil # Untrigger consuming of Gems
end
# TODO: Prefer (1.5x?) if item will be consumed and user has Unburden.
end
if target.item_active? && target.item && !target.item.is_berry?
Battle::ItemEffects.triggerDamageCalcFromTarget(
@@ -456,11 +459,13 @@ class Battle::AI::AIMove
end
# Item effects that alter accuracy calculation
if user.item_active?
# TODO: Zoom Lens needs to be checked differently (compare speeds of
# user and target).
Battle::ItemEffects.triggerAccuracyCalcFromUser(
user.item, modifiers, user_battler, target_battler, @move, calc_type
)
if user.item == :ZOOMLENS
mods[:accuracy_multiplier] *= 1.2 if target.faster_than?(user)
else
Battle::ItemEffects.triggerAccuracyCalcFromUser(
user.item, modifiers, user_battler, target_battler, @move, calc_type
)
end
end
if target.item_active?
Battle::ItemEffects.triggerAccuracyCalcFromTarget(
@@ -553,10 +558,4 @@ class Battle::AI::AIMove
(user.has_active_ability?(:SERENEGRACE) || user.pbOwnSide.effects[PBEffects::Rainbow] > 0)
return 2
end
#=============================================================================
# TODO:
# pbBaseAccuracy(@ai.user.battler, @ai.target.battler) if @ai.trainer.medium_skill?
# pbCriticalOverride(@ai.user.battler, @ai.target.battler)
end