mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-07 21:24:59 +00:00
Tackling of various AI "TODO" comments, a little tidying
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -102,7 +102,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("FailsUnlessTargetShares
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Split some of this into a MoveEffectScore?
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("FailsIfUserDamagedThisTurn",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
@@ -475,10 +475,16 @@ Battle::AI::Handlers::MoveEffectScore.add("AddToxicSpikesToFoeSide",
|
||||
if ai.trainer.medium_skill?
|
||||
# Check affected by entry hazard
|
||||
next if pkmn.hasItem?(:HEAVYDUTYBOOTS)
|
||||
# TODO: Check pkmn's immunity to being poisoned.
|
||||
# Check pkmn's immunity to being poisoned
|
||||
next if battle.field.terrain == :Misty
|
||||
next if pkmn.hasType?(:POISON)
|
||||
next if pkmn.hasType?(:STEEL)
|
||||
next if pkmn.hasAbility?(:IMMUNITY)
|
||||
next if pkmn.hasAbility?(:PASTELVEIL)
|
||||
next if pkmn.hasAbility?(:FLOWERVEIL) && pkmn.hasType?(:GRASS)
|
||||
next if pkmn.hasAbility?(:LEAFGUARD) && [:Sun, :HarshSun].include?(battle.pbWeather)
|
||||
next if pkmn.hasAbility?(:COMATOSE) && pkmn.isSpecies?(:KOMALA)
|
||||
next if pkmn.hasAbility?(:SHIELDSDOWN) && pkmn.isSpecies?(:MINIOR) && pkmn.form < 7
|
||||
# Check airborne
|
||||
if !pkmn.hasItem?(:IRONBALL) &&
|
||||
battle.field.effects[PBEffects::Gravity] == 0
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#===============================================================================
|
||||
# TODO: This code can be called with a single target and with no targets. Make
|
||||
# sure it doesn't assume that there is a target.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("RaiseUserAttack1",
|
||||
proc { |move, user, ai, battle|
|
||||
@@ -67,8 +66,7 @@ Battle::AI::Handlers::MoveEffectScore.add("MaxUserAttackLoseHalfOfTotalHP",
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: This code can be called with a single target and with no targets. Make
|
||||
# sure it doesn't assume that there is a target.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.copy("RaiseUserAttack1",
|
||||
"RaiseUserDefense1")
|
||||
@@ -92,8 +90,7 @@ Battle::AI::Handlers::MoveEffectScore.add("RaiseUserDefense1CurlUpUser",
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: This code can be called with multiple targets and with no targets. Make
|
||||
# sure it doesn't assume that there is a target.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.copy("RaiseUserDefense1",
|
||||
"RaiseUserDefense2")
|
||||
@@ -196,23 +193,17 @@ Battle::AI::Handlers::MoveEffectScore.add("RaiseUserSpeed2LowerUserWeight",
|
||||
proc { |score, move, user, ai, battle|
|
||||
score = ai.get_score_for_target_stat_raise(score, user, move.move.statUp)
|
||||
if ai.trainer.medium_skill?
|
||||
# TODO: Take into account weight-modifying items/abilities? This "> 1"
|
||||
# line can probably ignore them, but these moves' powers will change
|
||||
# because of those modifiers, and the score changes may need to be
|
||||
# different accordingly.
|
||||
if user.battler.pokemon.weight - user.effects[PBEffects::WeightChange] > 1
|
||||
if user.has_move_with_function?("PowerHigherWithUserHeavierThanTarget")
|
||||
score -= 10
|
||||
end
|
||||
current_weight = user.battler.pbWeight
|
||||
if current_weight > 1
|
||||
score -= 5 if user.has_move_with_function?("PowerHigherWithUserHeavierThanTarget")
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
if b.has_move_with_function?("PowerHigherWithUserHeavierThanTarget")
|
||||
score -= 10
|
||||
score -= 5 if b.has_move_with_function?("PowerHigherWithUserHeavierThanTarget")
|
||||
score += 5 if b.has_move_with_function?("PowerHigherWithTargetWeight")
|
||||
# User will become susceptible to Sky Drop
|
||||
if b.has_move_with_function?("TwoTurnAttackInvulnerableInSkyTargetCannotAct") &&
|
||||
Settings::MECHANICS_GENERATION >= 6
|
||||
score -= 7 if current_weight >= 2000 && current_weight < 3000
|
||||
end
|
||||
if b.has_move_with_function?("PowerHigherWithTargetWeight")
|
||||
score += 10
|
||||
end
|
||||
# TODO: Check foes for Sky Drop and whether the user is too heavy for it
|
||||
# but the weight reduction will make it susceptible.
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1323,9 +1314,10 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserCopyTargetStatStages
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Account for stat theft before damage calculation. This would be complex,
|
||||
# involving pbCanRaiseStatStage? and Contrary and Simple; do I want to
|
||||
# account for all that or simplify things?
|
||||
# NOTE: Accounting for the stat theft before damage calculation, to calculate a
|
||||
# more accurate predicted damage, would be complex, involving
|
||||
# pbCanRaiseStatStage? and Contrary and Simple; I'm not bothering with
|
||||
# that.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserStealTargetPositiveStatStages",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
|
||||
@@ -530,9 +530,9 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("CureTargetBurn",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.status == :BURN
|
||||
if target.opposes?(user)
|
||||
score -= 40
|
||||
score -= 10
|
||||
else
|
||||
score += 40
|
||||
score += 10
|
||||
end
|
||||
end
|
||||
next score
|
||||
@@ -1228,9 +1228,6 @@ Battle::AI::Handlers::MoveFailureCheck.add("StartGravity",
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartGravity",
|
||||
proc { |score, move, user, ai, battle|
|
||||
# TODO: Gravity increases accuracy of all moves. Prefer if user/ally has low
|
||||
# accuracy moves, don't prefer if foes have them. Should "low
|
||||
# accuracy" mean anything below 85%?
|
||||
ai.each_battler do |b, i|
|
||||
# Prefer grounding airborne foes, don't prefer grounding airborne allies
|
||||
# Prefer making allies affected by terrain, don't prefer making foes
|
||||
@@ -1252,6 +1249,11 @@ Battle::AI::Handlers::MoveEffectScore.add("StartGravity",
|
||||
if b.effects[PBEffects::SkyDrop] >= 0
|
||||
score += (user.opposes?(b)) ? -5 : 5
|
||||
end
|
||||
# Gravity raises accuracy of all moves; prefer if the user/ally has low
|
||||
# accuracy moves, don't prefer if foes have any
|
||||
if b.check_for_move { |m| m.accuracy < 85 }
|
||||
score += (user.opposes?(b)) ? -5 : 5
|
||||
end
|
||||
# Prefer stopping foes' sky-based attacks, don't prefer stopping allies'
|
||||
# sky-based attacks
|
||||
if user.faster_than?(b) &&
|
||||
|
||||
@@ -431,9 +431,20 @@ Battle::AI::Handlers::MoveEffectScore.add("EnsureNextCriticalHit",
|
||||
next Battle::AI::MOVE_USELESS_SCORE if user.effects[PBEffects::LaserFocus] > 0
|
||||
# Useless if the user's critical hit stage ensures critical hits already, or
|
||||
# critical hits are impossible (e.g. via Lucky Chant)
|
||||
# TODO: Critical hit rate is calculated using a target, but this move
|
||||
# doesn't have a target. An error is shown when trying it.
|
||||
crit_stage = move.rough_critical_hit_stage
|
||||
crit_stage = 0
|
||||
crit_stage = -1 if user.battler.pbOwnSide.effects[PBEffects::LuckyChant] > 0
|
||||
if crit_stage >= 0 && user.ability_active? && ![:MERCILESS].include?(user.ability)
|
||||
crit_stage = Battle::AbilityEffects.triggerCriticalCalcFromUser(user.battler.ability,
|
||||
user.battler, user.battler, crit_stage)
|
||||
end
|
||||
if crit_stage >= 0 && user.item_active?
|
||||
crit_stage = Battle::ItemEffects.triggerCriticalCalcFromUser(user.battler.item,
|
||||
user.battler, user.battler, crit_stage)
|
||||
end
|
||||
if crit_stage >= 0 && crit_stage < 50
|
||||
crit_stage += user.effects[PBEffects::FocusEnergy]
|
||||
crit_stage = [crit_stage, Battle::Move::CRITICAL_HIT_RATIOS.length - 1].min
|
||||
end
|
||||
if crit_stage < 0 ||
|
||||
crit_stage >= Battle::Move::CRITICAL_HIT_RATIOS.length ||
|
||||
Battle::Move::CRITICAL_HIT_RATIOS[crit_stage] == 1
|
||||
@@ -482,20 +493,22 @@ Battle::AI::Handlers::MoveEffectScore.add("StartPreventCriticalHitsAgainstUserSi
|
||||
# make critical hits more likely
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
crit_stage = 0
|
||||
if b.ability_active?
|
||||
if crit_stage >= 0 && b.ability_active?
|
||||
crit_stage = Battle::AbilityEffects.triggerCriticalCalcFromUser(b.battler.ability,
|
||||
b.battler, user.battler, crit_stage)
|
||||
next if crit_stage < 0
|
||||
end
|
||||
if b.item_active?
|
||||
if crit_stage >= 0 && b.item_active?
|
||||
crit_stage = Battle::ItemEffects.triggerCriticalCalcFromUser(b.battler.item,
|
||||
b.battler, user.battler, crit_stage)
|
||||
next if crit_stage < 0
|
||||
end
|
||||
crit_stage += b.effects[PBEffects::FocusEnergy]
|
||||
crit_stage += 1 if b.check_for_move { |m| m.highCriticalRate? }
|
||||
crit_stage = [crit_stage, Battle::Move::CRITICAL_HIT_RATIOS.length - 1].min
|
||||
crit_stage = 3 if crit_stage < 3 && m.check_for_move { |m| m.pbCritialOverride(b.battler, user.battler) > 0 }
|
||||
if crit_stage >= 0 && crit_stage < 50
|
||||
crit_stage += b.effects[PBEffects::FocusEnergy]
|
||||
crit_stage += 1 if b.check_for_move { |m| m.highCriticalRate? }
|
||||
crit_stage = 99 if m.check_for_move { |m| m.pbCritialOverride(b.battler, user.battler) > 0 }
|
||||
crit_stage = [crit_stage, Battle::Move::CRITICAL_HIT_RATIOS.length - 1].min
|
||||
end
|
||||
# TODO: Change the score depending on how much of an effect a critical hit
|
||||
# will have? Critical hits ignore the user's offensive stat drops
|
||||
# and the target's defensive stat raises, and multiply the damage.
|
||||
@@ -1064,7 +1077,7 @@ Battle::AI::Handlers::MoveEffectScore.add("ProtectUserSideFromPriorityMoves",
|
||||
next if !b.can_attack?
|
||||
# TODO: There are more calculations that could be done to get a more
|
||||
# accurate priority number.
|
||||
next if !b.check_for_move { |m| m.priority > 0 && m.canProtectAgainst? }
|
||||
next if !b.check_for_move { |m| m.pbPriority(b.battler) > 0 && m.canProtectAgainst? }
|
||||
useless = false
|
||||
# General preference
|
||||
score += 4
|
||||
|
||||
@@ -192,7 +192,6 @@ Battle::AI::Handlers::MoveEffectScore.add("AttackAndSkipNextTurn",
|
||||
score -= 10 if !user.has_active_ability?(:TRUANT)
|
||||
# Don't prefer if user is at a high HP (treat this move as a last resort)
|
||||
score -= 10 if user.hp >= user.totalhp / 2
|
||||
# TODO: Don't prefer if another of the user's moves could KO the target.
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -215,7 +214,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttack",
|
||||
# Don't prefer if target has a protecting move
|
||||
if ai.trainer.high_skill? && !(user.has_active_ability?(:UNSEENFIST) && move.move.contactMove?)
|
||||
has_protect_move = false
|
||||
if move.move.pbTarget(user).num_targets > 1 &&
|
||||
if move.pbTarget(user).num_targets > 1 &&
|
||||
(Settings::MECHANICS_GENERATION >= 7 || move.damagingMove?)
|
||||
if target.has_move_with_function?("ProtectUserSideFromMultiTargetDamagingMoves")
|
||||
has_protect_move = true
|
||||
|
||||
@@ -116,7 +116,7 @@ Battle::AI::Handlers::MoveEffectScore.add("HealUserHalfOfTotalHPLoseFlyingTypeTh
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
# TODO: Review score modifiers.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("CureTargetStatusHealUserHalfOfTotalHP",
|
||||
proc { |move, user, target, ai, battle|
|
||||
@@ -585,8 +585,7 @@ Battle::AI::Handlers::MoveEffectScore.copy("UserFaintsHealAndCureReplacement",
|
||||
"UserFaintsHealAndCureReplacementRestorePP")
|
||||
|
||||
#===============================================================================
|
||||
# TODO: This code should be for a single battler (each is checked in turn).
|
||||
# Should have a MoveEffectAgainstTargetScore instead.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("StartPerishCountsForAllBattlers",
|
||||
proc { |move, user, target, ai, battle|
|
||||
@@ -604,11 +603,10 @@ Battle::AI::Handlers::MoveEffectScore.add("StartPerishCountsForAllBattlers",
|
||||
allies_affected = 0
|
||||
foes_affected = 0
|
||||
foes_with_high_hp = 0
|
||||
battle.allBattlers.each do |b|
|
||||
next if b.effects[PBEffects::PerishSong] > 0
|
||||
next if Battle::AbilityEffects.triggerMoveImmunity(b.ability, user.battler, b,
|
||||
move.move, move.rough_type, battle, false)
|
||||
if b.opposes?(user.index)
|
||||
ai.each_battler do |b|
|
||||
next if Battle::AI::Handlers.move_will_fail_against_target?("StartPerishCountsForAllBattlers",
|
||||
move, user, b, ai, battle)
|
||||
if b.opposes?(user)
|
||||
foes_affected += 1
|
||||
foes_with_high_hp += 1 if b.hp >= b.totalhp * 0.75
|
||||
else
|
||||
|
||||
@@ -78,7 +78,8 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HealAllyOrDamageFoe",
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("CurseTargetOrLowerUserSpd1RaiseUserAtkDef1",
|
||||
proc { |move, user, ai, battle|
|
||||
next false if user.has_type?(:GHOST)
|
||||
next false if user.has_type?(:GHOST) ||
|
||||
(move.rough_type == :GHOST && user.has_active_ability?([:LIBERO, :PROTEAN]))
|
||||
will_fail = true
|
||||
(move.move.statUp.length / 2).times do |i|
|
||||
next if !user.battler.pbCanRaiseStatStage?(move.move.statUp[i * 2], user.battler, move.move)
|
||||
@@ -95,14 +96,16 @@ Battle::AI::Handlers::MoveFailureCheck.add("CurseTargetOrLowerUserSpd1RaiseUserA
|
||||
)
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("CurseTargetOrLowerUserSpd1RaiseUserAtkDef1",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next false if !user.has_type?(:GHOST)
|
||||
next false if !user.has_type?(:GHOST) &&
|
||||
!(move.rough_type == :GHOST && user.has_active_ability?([:LIBERO, :PROTEAN]))
|
||||
next true if target.effects[PBEffects::Curse] || !target.battler.takesIndirectDamage?
|
||||
next false
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("CurseTargetOrLowerUserSpd1RaiseUserAtkDef1",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next score if user.has_type?(:GHOST)
|
||||
next score if user.has_type?(:GHOST) ||
|
||||
(move.rough_type == :GHOST && user.has_active_ability?([:LIBERO, :PROTEAN]))
|
||||
score = ai.get_score_for_target_stat_raise(score, user, move.move.statUp)
|
||||
next score if score == Battle::AI::MOVE_USELESS_SCORE
|
||||
next ai.get_score_for_target_stat_drop(score, user, move.move.statDown, false)
|
||||
@@ -110,7 +113,8 @@ Battle::AI::Handlers::MoveEffectScore.add("CurseTargetOrLowerUserSpd1RaiseUserAt
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("CurseTargetOrLowerUserSpd1RaiseUserAtkDef1",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score if !user.has_type?(:GHOST)
|
||||
next score if !user.has_type?(:GHOST) &&
|
||||
!(move.rough_type == :GHOST && user.has_active_ability?([:LIBERO, :PROTEAN]))
|
||||
# Don't prefer if user will faint because of using this move
|
||||
next Battle::AI::MOVE_USELESS_SCORE if user.hp <= user.totalhp / 2
|
||||
# Prefer early on
|
||||
|
||||
@@ -477,8 +477,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("LowerPPOfTargetLastMoveB
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("LowerPPOfTargetLastMoveBy4",
|
||||
proc { |move, user, target, ai, battle|
|
||||
last_move = target.battler.pbGetMoveWithID(target.battler.lastRegularMoveUsed)
|
||||
next !last_move || last_move.pp == 0 || last_move.total_pp <= 0
|
||||
next !target.check_for_move { |m| m.id == target.battler.lastRegularMoveUsed }
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("LowerPPOfTargetLastMoveBy4",
|
||||
@@ -500,14 +499,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("DisableTargetLastMoveUs
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if target.effects[PBEffects::Disable] > 0 || !target.battler.lastRegularMoveUsed
|
||||
next true if move.move.pbMoveFailedAromaVeil?(user.battler, target.battler, false)
|
||||
will_fail = true
|
||||
target.battler.eachMove do |m|
|
||||
next if m.id != target.battler.lastRegularMoveUsed
|
||||
next if m.pp == 0 && m.total_pp > 0
|
||||
will_fail = false
|
||||
break
|
||||
end
|
||||
next will_fail
|
||||
next !target.check_for_move { |m| m.id == target.battler.lastRegularMoveUsed }
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetUsingSameMoveConsecutively",
|
||||
@@ -563,13 +555,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("DisableTargetUsingDiffe
|
||||
next true if target.effects[PBEffects::ShellTrap]
|
||||
next true if move.move.pbMoveFailedAromaVeil?(user.battler, target.battler, false)
|
||||
will_fail = true
|
||||
target.battler.eachMove do |m|
|
||||
next if m.id != target.battler.lastRegularMoveUsed
|
||||
next if m.pp == 0 && m.total_pp > 0
|
||||
will_fail = false
|
||||
break
|
||||
end
|
||||
next will_fail
|
||||
next !target.check_for_move { |m| m.id == target.battler.lastRegularMoveUsed }
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetUsingDifferentMove",
|
||||
@@ -627,7 +613,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetStatusMoves
|
||||
next Battle::AI::MOVE_USELESS_SCORE if !target.check_for_move { |m| m.statusMove? }
|
||||
# Not worth using on a sleeping target that won't imminently wake up
|
||||
if target.status == :SLEEP && target.statusCount > ((target.faster_than?(user)) ? 2 : 1)
|
||||
if !target.check_for_move { |m| m.statusMove? && m.usableWhenAsleep? && (m.pp > 0 || m.total_pp == 0) }
|
||||
if !target.check_for_move { |m| m.statusMove? && m.usableWhenAsleep? }
|
||||
next Battle::AI::MOVE_USELESS_SCORE
|
||||
end
|
||||
end
|
||||
@@ -652,8 +638,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetStatusMoves
|
||||
"ProtectUserFromTargetingMovesSpikyShield", # Spiky Shield
|
||||
"ProtectUserBanefulBunker" # Baneful Bunker
|
||||
]
|
||||
if target.check_for_move { |m| m.statusMove? && protection_moves.include?(m.function) &&
|
||||
(m.pp > 0 || m.total_pp == 0) }
|
||||
if target.check_for_move { |m| m.statusMove? && protection_moves.include?(m.function) }
|
||||
score += 6
|
||||
end
|
||||
# Inherent preference
|
||||
@@ -675,7 +660,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("DisableTargetHealingMov
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetHealingMoves",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Useless if the foe can't heal themselves with a move or some held items
|
||||
if !target.check_for_move { |m| m.healingMove? && (m.pp > 0 || m.total_pp == 0) }
|
||||
if !target.check_for_move { |m| m.healingMove? }
|
||||
if !target.has_active_item?(:LEFTOVERS) &&
|
||||
!(target.has_active_item?(:BLACKSLUDGE) && target.has_type?(:POISON))
|
||||
next Battle::AI::MOVE_USELESS_SCORE
|
||||
@@ -693,7 +678,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetHealingMove
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetSoundMoves",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score if target.effects[PBEffects::ThroatChop] > 1
|
||||
next score if !target.check_for_move { |m| m.soundMove? && (m.pp > 0 || m.total_pp == 0) }
|
||||
next score if !target.check_for_move { |m| m.soundMove? }
|
||||
# Inherent preference
|
||||
score += 8
|
||||
next score
|
||||
@@ -714,13 +699,9 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetMovesKnownB
|
||||
shared_move = false
|
||||
user_moves = user.battler.moves.map { |m| m.id }
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
b.battler.eachMove do |m|
|
||||
next if !user_moves.include?(m.id)
|
||||
next if m.pp == 0 && m.total_pp > 0
|
||||
shared_move = true
|
||||
break
|
||||
end
|
||||
break if shared_move
|
||||
next if !b.check_for_move { |m| user_moves.include?(m.id) }
|
||||
shared_move = true
|
||||
break
|
||||
end
|
||||
next Battle::AI::MOVE_USELESS_SCORE if !shared_move
|
||||
# Inherent preference
|
||||
|
||||
Reference in New Issue
Block a user