Generalised AI code for scoring stat changes

This commit is contained in:
Maruno17
2022-12-18 20:51:16 +00:00
parent 7ace4c5289
commit f33184d413
14 changed files with 382 additions and 729 deletions

View File

@@ -155,6 +155,8 @@ end
# Lower multiple of user's stats. # Lower multiple of user's stats.
#=============================================================================== #===============================================================================
class Battle::Move::StatDownMove < Battle::Move class Battle::Move::StatDownMove < Battle::Move
attr_reader :statDown
def pbEffectWhenDealingDamage(user, target) def pbEffectWhenDealingDamage(user, target)
return if @battle.pbAllFainted?(target.idxOwnSide) return if @battle.pbAllFainted?(target.idxOwnSide)
showAnim = true showAnim = true

View File

@@ -907,11 +907,12 @@ class Battle::Move::RaiseTargetAtkSpAtk2 < Battle::Move
end end
def pbEffectAgainstTarget(user, target) def pbEffectAgainstTarget(user, target)
showAnim = true
if target.pbCanRaiseStatStage?(:ATTACK, user, self) if target.pbCanRaiseStatStage?(:ATTACK, user, self)
target.pbRaiseStatStage(:ATTACK, 2, user) showAnim = false if target.pbRaiseStatStage(:ATTACK, 2, user, showAnim)
end end
if target.pbCanRaiseStatStage?(:SPECIAL_ATTACK, user, self) if target.pbCanRaiseStatStage?(:SPECIAL_ATTACK, user, self)
target.pbRaiseStatStage(:SPECIAL_ATTACK, 2, user) target.pbRaiseStatStage(:SPECIAL_ATTACK, 2, user, showAnim)
end end
end end
end end

View File

@@ -65,7 +65,8 @@ class Battle::AI
scoreSum = 0 scoreSum = 0
scoreCount = 0 scoreCount = 0
battler.allOpposing.each do |b| battler.allOpposing.each do |b|
scoreSum += pbGetMoveScore(battler.moves[idxEncoredMove], [b]) set_up_move_check(battler.moves[idxEncoredMove])
scoreSum += pbGetMoveScore([b])
scoreCount += 1 scoreCount += 1
end end
if scoreCount > 0 && scoreSum / scoreCount <= 20 if scoreCount > 0 && scoreSum / scoreCount <= 20

View File

@@ -11,6 +11,11 @@ class Battle::AI
#============================================================================= #=============================================================================
# Get scores for the user's moves (done before any action is assessed). # Get scores for the user's moves (done before any action is assessed).
# NOTE: For any move with a target type that can target a foe (or which
# includes a foe(s) if it has multiple targets), the score calculated
# for a target ally will be inverted. The MoveHandlers for those moves
# should therefore treat an ally as a foe when calculating a score
# against it.
#============================================================================= #=============================================================================
def pbGetMoveScores def pbGetMoveScores
choices = [] choices = []
@@ -37,7 +42,7 @@ class Battle::AI
when 0 # No targets, affects the user or a side or the whole field when 0 # No targets, affects the user or a side or the whole field
# Includes: BothSides, FoeSide, None, User, UserSide # Includes: BothSides, FoeSide, None, User, UserSide
score = MOVE_BASE_SCORE score = MOVE_BASE_SCORE
PBDebug.logonerr { score = pbGetMoveScore(move) } PBDebug.logonerr { score = pbGetMoveScore }
add_move_to_choices(choices, idxMove, score) add_move_to_choices(choices, idxMove, score)
when 1 # One target to be chosen by the trainer when 1 # One target to be chosen by the trainer
# Includes: Foe, NearAlly, NearFoe, NearOther, Other, RandomNearFoe, UserOrNearAlly # Includes: Foe, NearAlly, NearFoe, NearOther, Other, RandomNearFoe, UserOrNearAlly
@@ -46,16 +51,11 @@ class Battle::AI
# Lightning Rod. Skip any battlers that can't be targeted. # Lightning Rod. Skip any battlers that can't be targeted.
@battle.allBattlers.each do |b| @battle.allBattlers.each do |b|
next if !@battle.pbMoveCanTarget?(@user.battler.index, b.index, target_data) next if !@battle.pbMoveCanTarget?(@user.battler.index, b.index, target_data)
# TODO: This should consider targeting an ally if possible. Scores will # TODO: Should this sometimes consider targeting an ally? See def
# need to distinguish between harmful and beneficial to target. # pbGetMoveScoreAgainstTarget for more information.
# def pbGetMoveScore uses "175 - score" if the target is an ally;
# is this okay?
# Noticeably affects a few moves like Heal Pulse, as well as moves
# that the target can be immune to by an ability (you may want to
# attack the ally anyway so it gains the effect of that ability).
next if target_data.targets_foe && !@user.battler.opposes?(b) next if target_data.targets_foe && !@user.battler.opposes?(b)
score = MOVE_BASE_SCORE score = MOVE_BASE_SCORE
PBDebug.logonerr { score = pbGetMoveScore(move, [b]) } PBDebug.logonerr { score = pbGetMoveScore([b]) }
add_move_to_choices(choices, idxMove, score, b.index) add_move_to_choices(choices, idxMove, score, b.index)
end end
else # Multiple targets at once else # Multiple targets at once
@@ -66,7 +66,7 @@ class Battle::AI
targets.push(b) targets.push(b)
end end
score = MOVE_BASE_SCORE score = MOVE_BASE_SCORE
PBDebug.logonerr { score = pbGetMoveScore(move, targets) } PBDebug.logonerr { score = pbGetMoveScore(targets) }
add_move_to_choices(choices, idxMove, score) add_move_to_choices(choices, idxMove, score)
end end
end end
@@ -93,8 +93,7 @@ class Battle::AI
end end
def set_up_move_check_target(target) def set_up_move_check_target(target)
# TODO: Set @target to nil if there isn't one? @target = (target) ? @battlers[target.index] : nil
@target = (target) ? @battlers[target.index] : nil # @user
@target&.refresh_battler @target&.refresh_battler
end end
@@ -104,11 +103,12 @@ class Battle::AI
# TODO: Add skill checks in here for particular calculations? # TODO: Add skill checks in here for particular calculations?
#============================================================================= #=============================================================================
def pbPredictMoveFailure def pbPredictMoveFailure
# TODO: Something involving user.usingMultiTurnAttack? (perhaps earlier than # TODO: Something involving user.battler.usingMultiTurnAttack? (perhaps
# this?). # earlier than this?).
# User is asleep and will not wake up # User is asleep and will not wake up
return true if @trainer.medium_skill? && @user.battler.asleep? && return true if @user.battler.asleep? && @user.statusCount > 1 && !@move.move.usableWhenAsleep?
@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 # User will be truanting
return true if @user.has_active_ability?(:TRUANT) && @user.effects[PBEffects::Truant] return true if @user.has_active_ability?(:TRUANT) && @user.effects[PBEffects::Truant]
# Primal weather # Primal weather
@@ -142,8 +142,8 @@ class Battle::AI
typeMod = @move.move.pbCalcTypeMod(calc_type, @user.battler, @target.battler) typeMod = @move.move.pbCalcTypeMod(calc_type, @user.battler, @target.battler)
return true if @move.move.pbDamagingMove? && Effectiveness.ineffective?(typeMod) return true if @move.move.pbDamagingMove? && Effectiveness.ineffective?(typeMod)
# Dark-type immunity to moves made faster by Prankster # Dark-type immunity to moves made faster by Prankster
return true if Settings::MECHANICS_GENERATION >= 7 && @user.has_active_ability?(:PRANKSTER) && return true if Settings::MECHANICS_GENERATION >= 7 && @move.statusMove? &&
@target.has_type?(:DARK) && @target.opposes?(@user) @user.has_active_ability?(:PRANKSTER) && @target.has_type?(:DARK) && @target.opposes?(@user)
# Airborne-based immunity to Ground moves # Airborne-based immunity to Ground moves
return true if @move.damagingMove? && calc_type == :GROUND && return true if @move.damagingMove? && calc_type == :GROUND &&
@target.battler.airborne? && !@move.move.hitsFlyingTargets? @target.battler.airborne? && !@move.move.hitsFlyingTargets?
@@ -157,8 +157,9 @@ class Battle::AI
#============================================================================= #=============================================================================
# Get a score for the given move being used against the given target. # Get a score for the given move being used against the given target.
# Assumes def set_up_move_check has previously been called.
#============================================================================= #=============================================================================
def pbGetMoveScore(move, targets = nil) def pbGetMoveScore(targets = nil)
# Get the base score for the move # Get the base score for the move
score = MOVE_BASE_SCORE score = MOVE_BASE_SCORE
# Scores for each target in turn # Scores for each target in turn
@@ -170,32 +171,17 @@ class Battle::AI
# Get a score for the move against each target in turn # Get a score for the move against each target in turn
targets.each do |target| targets.each do |target|
set_up_move_check_target(target) set_up_move_check_target(target)
# Predict whether the move will fail against the target t_score = pbGetMoveScoreAgainstTarget
if @trainer.has_skill_flag?("PredictMoveFailure") next if t_score < 0
next if pbPredictMoveFailureAgainstTarget score += t_score
end
affected_targets += 1 affected_targets += 1
# Score the move
t_score = MOVE_BASE_SCORE
if @trainer.has_skill_flag?("ScoreMoves")
# Modify the score according to the move's effect against the target
t_score = Battle::AI::Handlers.apply_move_effect_against_target_score(@move.function,
MOVE_BASE_SCORE, @move, @user, @target, self, @battle)
# Modify the score according to various other effects against the target
score = Battle::AI::Handlers.apply_general_move_against_target_score_modifiers(
score, @move, @user, @target, self, @battle)
end
score += (@target.opposes?(@user)) ? t_score : 175 - t_score
end end
# Check if any targets were affected # Check if any targets were affected
if affected_targets == 0 if affected_targets == 0 && @trainer.has_skill_flag?("PredictMoveFailure")
if @trainer.has_skill_flag?("PredictMoveFailure")
return MOVE_FAIL_SCORE if !@move.move.worksWithNoTargets? return MOVE_FAIL_SCORE if !@move.move.worksWithNoTargets?
score = MOVE_USELESS_SCORE score = MOVE_USELESS_SCORE
else else
score = MOVE_BASE_SCORE affected_targets = 1 if affected_targets == 0 # To avoid dividing by 0
end
else
# TODO: Can this accounting for multiple targets be improved somehow? # TODO: Can this accounting for multiple targets be improved somehow?
score /= affected_targets # Average the score against multiple targets score /= affected_targets # Average the score against multiple targets
# Bonus for affecting multiple targets # Bonus for affecting multiple targets
@@ -220,6 +206,46 @@ class Battle::AI
return score return score
end end
#=============================================================================
# Returns the score of @move being used against @target. A return value of -1
# means the move will fail or do nothing against the target.
# Assumes def set_up_move_check and def set_up_move_check_target have
# previously been called.
# TODO: Add something in here (I think) to specially score moves used against
# an ally and the ally has an ability that will benefit from being hit
# by the move.
# TODO: The above also applies if the move is Heal Pulse or a few other moves
# like that, which CAN target a foe but you'd never do so. Maybe use a
# move flag to determine such moves? The implication is that such moves
# wouldn't apply the "175 - score" bit, which would make their
# MoveHandlers do the opposite calculations to other moves with the same
# targets, but is this desirable?
#=============================================================================
def pbGetMoveScoreAgainstTarget
# Predict whether the move will fail against the target
if @trainer.has_skill_flag?("PredictMoveFailure")
return -1 if pbPredictMoveFailureAgainstTarget
end
# Score the move
score = MOVE_BASE_SCORE
if @trainer.has_skill_flag?("ScoreMoves")
# Modify the score according to the move's effect against the target
score = Battle::AI::Handlers.apply_move_effect_against_target_score(@move.function,
MOVE_BASE_SCORE, @move, @user, @target, self, @battle)
# Modify the score according to various other effects against the target
score = Battle::AI::Handlers.apply_general_move_against_target_score_modifiers(
score, @move, @user, @target, self, @battle)
end
# Add the score against the target to the overall score
target_data = @move.pbTarget(@user.battler)
if target_data.targets_foe && !@target.opposes?(@user) && @target.index != @user.index
return -1 if score == MOVE_USELESS_SCORE
# TODO: Is this reversal of the score okay?
score = 175 - score
end
return score
end
#============================================================================= #=============================================================================
# Make the final choice of which move to use depending on the calculated # Make the final choice of which move to use depending on the calculated
# scores for each move. Moves with higher scores are more likely to be chosen. # scores for each move. Moves with higher scores are more likely to be chosen.

View File

@@ -1,88 +1,100 @@
class Battle::AI class Battle::AI
#============================================================================= #=============================================================================
# Main method for calculating the score for moves that raise the user's stat(s). # Main method for calculating the score for moves that raise a battler's
# stat(s).
# By default, assumes that a stat raise is a good thing. However, this score
# is inverted (by desire_mult) if the target opposes the user. If the move
# 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_user_stat_raise(score) def get_score_for_target_stat_raise(score, target, stat_changes, whole_effect = true)
# Discard status move/don't prefer damaging move if user has Contrary whole_effect = false if @move.damagingMove?
if !@battle.moldBreaker && @user.has_active_ability?(:CONTRARY) # Decide whether the target raising its stat(s) is a good thing
return (@move.statusMove?) ? MOVE_USELESS_SCORE : score - 20 desire_mult = 1
if target.opposes?(@user) ||
(@move.pbTarget(@user.battler).targets_foe && target.index != @user.index)
desire_mult = -1
end end
# Don't make score changes if user will faint from EOR damage # Discard status move/don't prefer damaging move if target has Contrary
if @user.rough_end_of_round_damage > @user.hp # TODO: Maybe this should return get_score_for_target_stat_drop if Contrary
return (@move.statusMove?) ? MOVE_USELESS_SCORE : score # applies and desire_mult < 1.
if !@battle.moldBreaker && target.has_active_ability?(:CONTRARY) && desire_mult > 1
return (whole_effect) ? MOVE_USELESS_SCORE : score - 20
end end
# Don't make score changes if foes have Unaware and user can't make use of # Don't make score changes if target will faint from EOR damage
if target.rough_end_of_round_damage > target.hp
return (whole_effect) ? MOVE_USELESS_SCORE : score
end
# Don't make score changes if foes have Unaware and target can't make use of
# extra stat stages # extra stat stages
if !@user.check_for_move { |m| m.function == "PowerHigherWithUserPositiveStatStages" } if !target.check_for_move { |m| m.function == "PowerHigherWithUserPositiveStatStages" }
foe_is_aware = false foe_is_aware = false
each_foe_battler(@user.side) do |b, i| each_foe_battler(target.side) do |b, i|
foe_is_aware = true if !b.has_active_ability?(:UNAWARE) foe_is_aware = true if !b.has_active_ability?(:UNAWARE)
end end
if !foe_is_aware if !foe_is_aware
return (@move.statusMove?) ? MOVE_USELESS_SCORE : score return (whole_effect) ? MOVE_USELESS_SCORE : score
end end
end end
# Figure out which stat raises can happen # Figure out which stat raises can happen
stat_changes = [] real_stat_changes = []
@move.move.statUp.each_with_index do |stat, idx| stat_changes.each_with_index do |stat, idx|
next if idx.odd? next if idx.odd?
next if !stat_raise_worthwhile?(stat) next if !stat_raise_worthwhile?(target, stat)
# Calculate amount that stat will be raised by # Calculate amount that stat will be raised by
increment = @move.move.statUp[idx + 1] increment = stat_changes[idx + 1]
if @move.function == "RaiseUserAtkSpAtk1Or2InSun" if @move.function == "RaiseUserAtkSpAtk1Or2InSun"
increment = 1 increment = 1
increment = 2 if [:Sun, :HarshSun].include?(@user.battler.effectiveWeather) increment = 2 if [:Sun, :HarshSun].include?(target.battler.effectiveWeather)
end end
increment *= 2 if !@battle.moldBreaker && @user.has_active_ability?(:SIMPLE) increment *= 2 if !@battle.moldBreaker && target.has_active_ability?(:SIMPLE)
increment = [increment, 6 - @user.stages[stat]].min # The actual stages gained increment = [increment, 6 - target.stages[stat]].min # The actual stages gained
# Count this as a valid stat raise # Count this as a valid stat raise
stat_changes.push([stat, increment]) if increment > 0 real_stat_changes.push([stat, increment]) if increment > 0
end end
# Discard move if it can't raise any stats # Discard move if it can't raise any stats
if stat_changes.length == 0 if real_stat_changes.length == 0
# TODO: Have a parameter that decides whether to reduce the score here return (whole_effect) ? MOVE_USELESS_SCORE : score
# (for moves where this is just part of the effect).
return (@move.statusMove?) ? MOVE_USELESS_SCORE : score
end end
# Make score changes based on the general concept of raising stats at all # Make score changes based on the general concept of raising stats at all
score = get_user_stat_raise_score_generic(score, stat_changes) 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 # Make score changes based on the specific changes to each stat that will be
# raised # raised
stat_changes.each do |change| real_stat_changes.each do |change|
score = get_user_stat_raise_score_one(score, change[0], change[1]) score = get_target_stat_raise_score_one(score, target, change[0], change[1], desire_mult)
end end
return score return score
end end
#============================================================================= #=============================================================================
# Returns whether the user raising the given stat will have any impact. # Returns whether the target raising the given stat will have any impact.
# TODO: Make sure the move's actual damage category is taken into account, # TODO: Make sure the move's actual damage category is taken into account,
# i.e. CategoryDependsOnHigherDamagePoisonTarget and # i.e. CategoryDependsOnHigherDamagePoisonTarget and
# CategoryDependsOnHigherDamageIgnoreTargetAbility. # CategoryDependsOnHigherDamageIgnoreTargetAbility.
#============================================================================= #=============================================================================
def stat_raise_worthwhile?(stat) def stat_raise_worthwhile?(target, stat)
return false if !@user.battler.pbCanRaiseStatStage?(stat, @user.battler, @move.move) return false if !target.battler.pbCanRaiseStatStage?(stat, @user.battler, @move.move)
# Check if user won't benefit from the stat being raised # Check if target won't benefit from the stat being raised
# TODO: Exception if user knows Baton Pass/Stored Power? # TODO: Exception if target knows Baton Pass/Stored Power?
case stat case stat
when :ATTACK when :ATTACK
return false if !@user.check_for_move { |m| m.physicalMove?(m.type) && return false if !target.check_for_move { |m| m.physicalMove?(m.type) &&
m.function != "UseUserDefenseInsteadOfUserAttack" && m.function != "UseUserDefenseInsteadOfUserAttack" &&
m.function != "UseTargetAttackInsteadOfUserAttack" } m.function != "UseTargetAttackInsteadOfUserAttack" }
when :DEFENSE when :DEFENSE
each_foe_battler(@user.side) do |b, i| each_foe_battler(target.side) do |b, i|
return true if b.check_for_move { |m| m.physicalMove?(m.type) || return true if b.check_for_move { |m| m.physicalMove?(m.type) ||
m.function == "UseTargetDefenseInsteadOfTargetSpDef" } m.function == "UseTargetDefenseInsteadOfTargetSpDef" }
end end
return false return false
when :SPECIAL_ATTACK when :SPECIAL_ATTACK
return false if !@user.check_for_move { |m| m.specialMove?(m.type) } return false if !target.check_for_move { |m| m.specialMove?(m.type) }
when :SPECIAL_DEFENSE when :SPECIAL_DEFENSE
each_foe_battler(@user.side) do |b, i| each_foe_battler(target.side) do |b, i|
return true if b.check_for_move { |m| m.specialMove?(m.type) && return true if b.check_for_move { |m| m.specialMove?(m.type) &&
m.function != "UseTargetDefenseInsteadOfTargetSpDef" } m.function != "UseTargetDefenseInsteadOfTargetSpDef" }
end end
@@ -92,9 +104,9 @@ class Battle::AI
"PowerHigherWithUserFasterThanTarget", "PowerHigherWithUserFasterThanTarget",
"PowerHigherWithUserPositiveStatStages" "PowerHigherWithUserPositiveStatStages"
] ]
if !@user.check_for_move { |m| moves_that_prefer_high_speed.include?(m.function) } if !target.check_for_move { |m| moves_that_prefer_high_speed.include?(m.function) }
each_foe_battler(@user.side) do |b, i| each_foe_battler(target.side) do |b, i|
return true if b.faster_than?(@user) return true if b.faster_than?(target)
end end
return false return false
end end
@@ -107,27 +119,35 @@ class Battle::AI
#============================================================================= #=============================================================================
# Make score changes based on the general concept of raising stats at all. # Make score changes based on the general concept of raising stats at all.
#============================================================================= #=============================================================================
def get_user_stat_raise_score_generic(score, stat_changes) def get_target_stat_raise_score_generic(score, target, stat_changes, desire_mult = 1)
total_increment = stat_changes.sum { |change| change[1] } total_increment = stat_changes.sum { |change| change[1] }
# TODO: Just return if foe is predicted to use a phazing move (one that # TODO: Just return if the target's foe is predicted to use a phazing move
# switches the user out). # (one that switches the target out).
# TODO: Don't prefer if foe is faster than user and is predicted to deal # TODO: Don't prefer if foe is faster than target and is predicted to deal
# lethal damage. # lethal damage.
# TODO: Don't prefer if foe is slower than user but is predicted to be able # TODO: Don't prefer if foe is slower than target but is predicted to be
# to 2HKO user. # able to 2HKO the target.
# TODO: Prefer if foe is semi-invulnerable and user is faster (can't hit # TODO: Prefer if foe is semi-invulnerable and target is faster (can't hit
# the foe anyway). # the foe anyway).
# Prefer if move is a status move and it's the user's first/second turn # Prefer if move is a status move and it's the user's first/second turn
if @user.turnCount < 2 && @move.statusMove? if @user.turnCount < 2 && @move.statusMove?
score += total_increment * 4 score += total_increment * desire_mult * 4
end end
# Prefer if user is at high HP, don't prefer if user is at low HP # 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 if @user.hp >= @user.totalhp * 0.7
score += 4 * total_increment score += total_increment * desire_mult * 3
else else
score += total_increment * ((100 * @user.hp / @user.totalhp) - 50) / 4 # +5 to -12 per stage score += total_increment * desire_mult * ((100 * @user.hp / @user.totalhp) - 50) / 6 # +3 to -8 per stage
end
end
# Prefer if target is at high HP, don't prefer if target is at low HP
if target.hp >= target.totalhp * 0.7
score += total_increment * desire_mult * 4
else
score += total_increment * desire_mult * ((100 * target.hp / target.totalhp) - 50) / 4 # +5 to -12 per stage
end end
# TODO: Look at abilities that trigger upon stat raise. There are none. # TODO: Look at abilities that trigger upon stat raise. There are none.
@@ -225,7 +245,7 @@ class Battle::AI
#============================================================================= #=============================================================================
# Make score changes based on the raising of a specific stat. # Make score changes based on the raising of a specific stat.
#============================================================================= #=============================================================================
def get_user_stat_raise_score_one(score, stat, increment) def get_target_stat_raise_score_one(score, target, stat, increment, desire_mult = 1)
# Figure out how much the stat will actually change by # Figure out how much the stat will actually change by
stage_mul = [2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8] stage_mul = [2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8]
stage_div = [8, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2] stage_div = [8, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2]
@@ -233,97 +253,95 @@ class Battle::AI
stage_mul = [3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7, 8, 9] stage_mul = [3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7, 8, 9]
stage_div = [9, 8, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3] stage_div = [9, 8, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3]
end end
old_stage = @user.stages[stat] old_stage = target.stages[stat]
new_stage = old_stage + increment new_stage = old_stage + increment
inc_mult = (stage_mul[new_stage].to_f * stage_div[old_stage]) / (stage_div[new_stage] * stage_mul[old_stage]) inc_mult = (stage_mul[new_stage].to_f * stage_div[old_stage]) / (stage_div[new_stage] * stage_mul[old_stage])
inc_mult -= 1 inc_mult -= 1
inc_mult *= desire_mult
# Stat-based score changes # Stat-based score changes
case stat case stat
when :ATTACK when :ATTACK
# Modify score depending on current stat stage # Modify score depending on current stat stage
# More strongly prefer if the user has no special moves # More strongly prefer if the target has no special moves
if old_stage >= 2 if old_stage >= 2
score -= 20 score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
else else
has_special_moves = @user.check_for_move { |m| m.specialMove?(m.type) } has_special_moves = target.check_for_move { |m| m.specialMove?(m.type) }
inc = (has_special_moves) ? 10 : 20 inc = (has_special_moves) ? 10 : 20
score += inc * inc_mult score += inc * inc_mult
score += 8 * inc_mult if @user.hp == @user.totalhp
end end
when :DEFENSE when :DEFENSE
# Modify score depending on current stat stage # Modify score depending on current stat stage
if old_stage >= 2 if old_stage >= 2
score -= 20 score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
else else
score += 10 * inc_mult score += 10 * inc_mult
score += 8 * inc_mult if @user.hp == @user.totalhp
end end
when :SPECIAL_ATTACK when :SPECIAL_ATTACK
# Modify score depending on current stat stage # Modify score depending on current stat stage
# More strongly prefer if the user has no physical moves # More strongly prefer if the target has no physical moves
if old_stage >= 2 if old_stage >= 2
score -= 20 score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
else else
has_physical_moves = @user.check_for_move { |m| m.physicalMove?(m.type) && has_physical_moves = target.check_for_move { |m| m.physicalMove?(m.type) &&
m.function != "UseUserDefenseInsteadOfUserAttack" && m.function != "UseUserDefenseInsteadOfUserAttack" &&
m.function != "UseTargetAttackInsteadOfUserAttack" } m.function != "UseTargetAttackInsteadOfUserAttack" }
inc = (has_physical_moves) ? 10 : 20 inc = (has_physical_moves) ? 10 : 20
score += inc * inc_mult score += inc * inc_mult
score += 8 * inc_mult if @user.hp == @user.totalhp
end end
when :SPECIAL_DEFENSE when :SPECIAL_DEFENSE
# Modify score depending on current stat stage # Modify score depending on current stat stage
if old_stage >= 2 if old_stage >= 2
score -= 20 score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
else else
score += 10 * inc_mult score += 10 * inc_mult
score += 8 * inc_mult if @user.hp == @user.totalhp
end end
when :SPEED when :SPEED
# Prefer if user is slower than a foe # Prefer if target is slower than a foe
# TODO: Don't prefer if the user is too much slower than any foe that it # TODO: Don't prefer if the target is too much slower than any foe that it
# can't catch up. # can't catch up.
each_foe_battler(@user.side) do |b, i| each_foe_battler(target.side) do |b, i|
next if @user.faster_than?(b) next if target.faster_than?(b)
score += 15 * inc_mult score += 15 * inc_mult
break break
end end
# TODO: Prefer if the user is able to cause flinching (moves that flinch, # TODO: Prefer if the target is able to cause flinching (moves that
# or has King's Rock/Stench). # flinch, or has King's Rock/Stench).
# Prefer if the user has Electro Ball or Power Trip/Stored Power # Prefer if the target has Electro Ball or Power Trip/Stored Power
moves_that_prefer_high_speed = [ moves_that_prefer_high_speed = [
"PowerHigherWithUserFasterThanTarget", "PowerHigherWithUserFasterThanTarget",
"PowerHigherWithUserPositiveStatStages" "PowerHigherWithUserPositiveStatStages"
] ]
if @user.check_for_move { |m| moves_that_prefer_high_speed.include?(m.function) } if target.check_for_move { |m| moves_that_prefer_high_speed.include?(m.function) }
score += 8 * inc_mult score += 8 * inc_mult
end end
# Don't prefer if any foe has Gyro Ball # Don't prefer if any foe has Gyro Ball
each_foe_battler(@user.side) do |b, i| each_foe_battler(target.side) do |b, i|
next if !b.check_for_move { |m| m.function == "PowerHigherWithTargetFasterThanUser" } next if !b.check_for_move { |m| m.function == "PowerHigherWithTargetFasterThanUser" }
score -= 8 * inc_mult score -= 8 * inc_mult
end end
# Don't prefer if user has Speed Boost (will be gaining Speed anyway) # Don't prefer if target has Speed Boost (will be gaining Speed anyway)
score -= 20 if @user.has_active_ability?(:SPEEDBOOST) if target.has_active_ability?(:SPEEDBOOST)
score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
end
when :ACCURACY when :ACCURACY
# Modify score depending on current stat stage # Modify score depending on current stat stage
if old_stage >= 2 if old_stage >= 2
score -= 20 score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
else else
min_accuracy = 100 min_accuracy = 100
@user.battler.moves.each do |m| target.battler.moves.each do |m|
next if m.accuracy == 0 || m.is_a?(Battle::Move::OHKO) next if m.accuracy == 0 || m.is_a?(Battle::Move::OHKO)
min_accuracy = m.accuracy if m.accuracy < min_accuracy min_accuracy = m.accuracy if m.accuracy < min_accuracy
end end
min_accuracy = min_accuracy * stage_mul[old_stage] / stage_div[old_stage] min_accuracy = min_accuracy * stage_mul[old_stage] / stage_div[old_stage]
if min_accuracy < 90 if min_accuracy < 90
score += 10 * inc_mult score += 10 * inc_mult
score += 8 * inc_mult if @user.hp == @user.totalhp
end end
end end
@@ -331,29 +349,28 @@ class Battle::AI
# Prefer if a foe will (probably) take damage at the end of the round # 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 # TODO: Should this take into account EOR healing, one-off damage and
# damage-causing effects that wear off naturally (like Sea of Fire)? # damage-causing effects that wear off naturally (like Sea of Fire)?
# TODO: Emerald AI also prefers if user is rooted via Ingrain. # TODO: Emerald AI also prefers if target is rooted via Ingrain.
each_foe_battler(@user.side) do |b, i| each_foe_battler(target.side) do |b, i|
eor_damage = b.rough_end_of_round_damage eor_damage = b.rough_end_of_round_damage
score += 60 * eor_damage / b.totalhp if eor_damage > 0 next if eor_damage <= 0
end end
# Modify score depending on current stat stage # Modify score depending on current stat stage
if old_stage >= 2 if old_stage >= 2
score -= 20 score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
else else
score += 10 * (2 - old_stage) * inc_mult score += 10 * inc_mult
score += 8 * inc_mult if @user.hp == @user.totalhp
end end
end end
# Prefer if user has Stored Power # Prefer if target has Stored Power
if @user.check_for_move { |m| m.function == "PowerHigherWithUserPositiveStatStages" } if target.check_for_move { |m| m.function == "PowerHigherWithUserPositiveStatStages" }
score += 5 * pos_change score += 5 * increment * desire_mult
end end
# Don't prefer if any foe has Punishment # Don't prefer if any foe has Punishment
each_foe_battler(@user.side) do |b, i| each_foe_battler(target.side) do |b, i|
next if !b.check_for_move { |m| m.function == "PowerHigherWithTargetPositiveStatStages" } next if !b.check_for_move { |m| m.function == "PowerHigherWithTargetPositiveStatStages" }
score -= 5 * pos_change score -= 5 * increment * desire_mult
end end
return score return score
@@ -363,9 +380,6 @@ class Battle::AI
mini_score = 1.0 mini_score = 1.0
case stat case stat
when :ATTACK when :ATTACK
# TODO: Don't prefer if target has previously used a move that benefits
# from user's Attack being boosted.
# mini_score *= 0.3 if @target.check_for_move { |m| m.function == "UseTargetAttackInsteadOfUserAttack" } # Foul Play
# Prefer if user can definitely survive a hit no matter how powerful, and # Prefer if user can definitely survive a hit no matter how powerful, and
# it won't be hurt by weather # it won't be hurt by weather
# if @user.hp == @user.totalhp && # if @user.hp == @user.totalhp &&
@@ -567,55 +581,67 @@ class Battle::AI
end end
#============================================================================= #=============================================================================
# Main method for calculating the score for moves that lower the target's # Main method for calculating the score for moves that lower a battler's
# stat(s). # stat(s).
# By default, assumes that a stat drop is a good thing. However, this score
# is inverted (by desire_mult) if the target is the user or an ally. This
# inversion does not happen if the move could target a foe but is targeting an
# ally, but only because it is inverted in def pbGetMoveScoreAgainstTarget
# instead.
# TODO: Revisit this method as parts may need rewriting. # TODO: Revisit this method as parts may need rewriting.
#============================================================================= #=============================================================================
def get_score_for_target_stat_drop(score) def get_score_for_target_stat_drop(score, target, stat_changes, whole_effect = true)
whole_effect = false if @move.damagingMove?
# Decide whether the target raising its stat(s) is a good thing
desire_mult = -1
if target.opposes?(@user) ||
(@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 # Discard status move/don't prefer damaging move if target has Contrary
if !@battle.moldBreaker && @target.has_active_ability?(:CONTRARY) # TODO: Maybe this should return get_score_for_target_stat_raise if Contrary
return (@move.statusMove?) ? MOVE_USELESS_SCORE : score - 20 # applies and desire_mult < 1.
if !@battle.moldBreaker && target.has_active_ability?(:CONTRARY) && desire_mult > 1
return (whole_effect) ? MOVE_USELESS_SCORE : score - 20
end end
# Don't make score changes if target will faint from EOR damage # Don't make score changes if target will faint from EOR damage
if @target.rough_end_of_round_damage > @target.hp if target.rough_end_of_round_damage > target.hp
return (@move.statusMove?) ? MOVE_USELESS_SCORE : score return (whole_effect) ? MOVE_USELESS_SCORE : score
end end
# Don't make score changes if allies have Unaware and can't make use of # Don't make score changes if foes have Unaware and target can't make use of
# target's lowered stat stages # its lowered stat stages
ally_is_aware = false foe_is_aware = false
each_foe_battler(@target.side) do |b, i| each_foe_battler(target.side) do |b, i|
ally_is_aware = true if !b.has_active_ability?(:UNAWARE) foe_is_aware = true if !b.has_active_ability?(:UNAWARE)
end end
if !ally_is_aware if !foe_is_aware
return (@move.statusMove?) ? MOVE_USELESS_SCORE : score return (whole_effect) ? MOVE_USELESS_SCORE : score
end end
# Figure out which stat raises can happen # Figure out which stat raises can happen
stat_changes = [] real_stat_changes = []
@move.move.statDown.each_with_index do |stat, idx| stat_changes.each_with_index do |stat, idx|
next if idx.odd? next if idx.odd?
next if !stat_drop_worthwhile?(stat) next if !stat_drop_worthwhile?(target, stat)
# Calculate amount that stat will be raised by # Calculate amount that stat will be raised by
decrement = @move.move.statDown[idx + 1] decrement = stat_changes[idx + 1]
decrement *= 2 if !@battle.moldBreaker && @user.has_active_ability?(:SIMPLE) decrement *= 2 if !@battle.moldBreaker && @user.has_active_ability?(:SIMPLE)
decrement = [decrement, 6 + @target.stages[stat]].min # The actual stages lost decrement = [decrement, 6 + target.stages[stat]].min # The actual stages lost
# Count this as a valid stat drop # Count this as a valid stat drop
stat_changes.push([stat, decrement]) if decrement > 0 real_stat_changes.push([stat, decrement]) if decrement > 0
end end
# Discard move if it can't lower any stats # Discard move if it can't lower any stats
if stat_changes.length == 0 if real_stat_changes.length == 0
# TODO: Have a parameter that decides whether to reduce the score here return (whole_effect) ? MOVE_USELESS_SCORE : score
# (for moves where this is just part of the effect).
return (@move.statusMove?) ? MOVE_USELESS_SCORE : score
end end
# Make score changes based on the general concept of lowering stats at all # Make score changes based on the general concept of lowering stats at all
score = get_target_stat_drop_score_generic(score, stat_changes) 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 # Make score changes based on the specific changes to each stat that will be
# lowered # lowered
stat_changes.each do |change| real_stat_changes.each do |change|
score = get_target_stat_drop_score_one(score, change[0], change[1]) score = get_target_stat_drop_score_one(score, target, change[0], change[1], desire_mult)
end end
return score return score
@@ -628,24 +654,24 @@ class Battle::AI
# CategoryDependsOnHigherDamageIgnoreTargetAbility. # CategoryDependsOnHigherDamageIgnoreTargetAbility.
# TODO: Revisit this method as parts may need rewriting. # TODO: Revisit this method as parts may need rewriting.
#============================================================================= #=============================================================================
def stat_drop_worthwhile?(stat) def stat_drop_worthwhile?(target, stat)
return false if !@target.battler.pbCanLowerStatStage?(stat, @user.battler, @move.move) return false if !target.battler.pbCanLowerStatStage?(stat, @user.battler, @move.move)
# Check if target won't benefit from the stat being lowered # Check if target won't benefit from the stat being lowered
case stat case stat
when :ATTACK when :ATTACK
return false if !@target.check_for_move { |m| m.physicalMove?(m.type) && return false if !target.check_for_move { |m| m.physicalMove?(m.type) &&
m.function != "UseUserDefenseInsteadOfUserAttack" && m.function != "UseUserDefenseInsteadOfUserAttack" &&
m.function != "UseTargetAttackInsteadOfUserAttack" } m.function != "UseTargetAttackInsteadOfUserAttack" }
when :DEFENSE when :DEFENSE
each_foe_battler(@target.side) do |b, i| each_foe_battler(target.side) do |b, i|
return true if b.check_for_move { |m| m.physicalMove?(m.type) || return true if b.check_for_move { |m| m.physicalMove?(m.type) ||
m.function == "UseTargetDefenseInsteadOfTargetSpDef" } m.function == "UseTargetDefenseInsteadOfTargetSpDef" }
end end
return false return false
when :SPECIAL_ATTACK when :SPECIAL_ATTACK
return false if !@target.check_for_move { |m| m.specialMove?(m.type) } return false if !target.check_for_move { |m| m.specialMove?(m.type) }
when :SPECIAL_DEFENSE when :SPECIAL_DEFENSE
each_foe_battler(@target.side) do |b, i| each_foe_battler(target.side) do |b, i|
return true if b.check_for_move { |m| m.specialMove?(m.type) && return true if b.check_for_move { |m| m.specialMove?(m.type) &&
m.function != "UseTargetDefenseInsteadOfTargetSpDef" } m.function != "UseTargetDefenseInsteadOfTargetSpDef" }
end end
@@ -655,9 +681,9 @@ class Battle::AI
"PowerHigherWithUserFasterThanTarget", "PowerHigherWithUserFasterThanTarget",
"PowerHigherWithUserPositiveStatStages" "PowerHigherWithUserPositiveStatStages"
] ]
if !@target.check_for_move { |m| moves_that_prefer_high_speed.include?(m.function) } if !target.check_for_move { |m| moves_that_prefer_high_speed.include?(m.function) }
each_foe_battler(@target.side) do |b, i| each_foe_battler(target.side) do |b, i|
return true if !b.faster_than?(@target) return true if !b.faster_than?(target)
end end
return false return false
end end
@@ -672,7 +698,7 @@ class Battle::AI
# TODO: Revisit this method as parts may need rewriting. # TODO: Revisit this method as parts may need rewriting.
# TODO: All comments in this method may be inaccurate. # TODO: All comments in this method may be inaccurate.
#============================================================================= #=============================================================================
def get_target_stat_drop_score_generic(score, stat_changes) def get_target_stat_drop_score_generic(score, target, stat_changes, desire_mult = 1)
total_decrement = stat_changes.sum { |change| change[1] } total_decrement = stat_changes.sum { |change| change[1] }
# TODO: Just return if target is predicted to switch out (except via Baton Pass). # TODO: Just return if target is predicted to switch out (except via Baton Pass).
# TODO: Don't prefer if target is faster than user and is predicted to deal # TODO: Don't prefer if target is faster than user and is predicted to deal
@@ -683,20 +709,22 @@ class Battle::AI
# Prefer if move is a status move and it's the user's first/second turn # Prefer if move is a status move and it's the user's first/second turn
if @user.turnCount < 2 && @move.statusMove? if @user.turnCount < 2 && @move.statusMove?
score += total_decrement * 4 score += total_decrement * desire_mult * 4
end end
# Prefer if user is at high HP, don't prefer if user is at low HP # 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 if @user.hp >= @user.totalhp * 0.7
score += 3 * total_decrement score += total_decrement * desire_mult * 3
else else
score += total_decrement * ((100 * @user.hp / @user.totalhp) - 50) / 6 # +3 to -8 per stage score += total_decrement * desire_mult * ((100 * @user.hp / @user.totalhp) - 50) / 6 # +3 to -8 per stage
end
end end
# Prefer if target is at high HP, don't prefer if target is at low HP # Prefer if target is at high HP, don't prefer if target is at low HP
if @target.hp >= @target.totalhp * 0.7 if target.hp >= target.totalhp * 0.7
score += 3 * total_decrement score += total_decrement * desire_mult * 3
else else
score += total_decrement * ((100 * @target.hp / @target.totalhp) - 50) / 6 # +3 to -8 per stage score += total_decrement * desire_mult * ((100 * target.hp / target.totalhp) - 50) / 6 # +3 to -8 per stage
end end
# TODO: Look at abilities that trigger upon stat lowering. # TODO: Look at abilities that trigger upon stat lowering.
@@ -708,7 +736,7 @@ class Battle::AI
# Make score changes based on the lowering of a specific stat. # Make score changes based on the lowering of a specific stat.
# TODO: Revisit this method as parts may need rewriting. # TODO: Revisit this method as parts may need rewriting.
#============================================================================= #=============================================================================
def get_target_stat_drop_score_one(score, stat, decrement) def get_target_stat_drop_score_one(score, target, stat, decrement, desire_mult = 1)
# Figure out how much the stat will actually change by # Figure out how much the stat will actually change by
stage_mul = [2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8] stage_mul = [2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8]
stage_div = [8, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2] stage_div = [8, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2]
@@ -716,102 +744,99 @@ class Battle::AI
stage_mul = [3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7, 8, 9] stage_mul = [3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7, 8, 9]
stage_div = [9, 8, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3] stage_div = [9, 8, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3]
end end
old_stage = @target.stages[stat] old_stage = target.stages[stat]
new_stage = old_stage - decrement new_stage = old_stage - decrement
dec_mult = (stage_mul[old_stage].to_f * stage_div[new_stage]) / (stage_div[old_stage] * stage_mul[new_stage]) dec_mult = (stage_mul[old_stage].to_f * stage_div[new_stage]) / (stage_div[old_stage] * stage_mul[new_stage])
dec_mult -= 1 dec_mult -= 1
dec_mult *= desire_mult
# Stat-based score changes # Stat-based score changes
case stat case stat
when :ATTACK when :ATTACK
# Modify score depending on current stat stage # Modify score depending on current stat stage
# More strongly prefer if the target has no special moves # More strongly prefer if the target has no special moves
if old_stage <= -2 if old_stage <= -2
score -= 20 score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
else else
has_special_moves = @target.check_for_move { |m| m.specialMove?(m.type) } has_special_moves = target.check_for_move { |m| m.specialMove?(m.type) }
dec = (has_special_moves) ? 5 : 10 dec = (has_special_moves) ? 5 : 10
score += dec * (2 + old_stage) * dec_mult score += dec * dec_mult
score += 4 * dec_mult if @user.hp == @user.totalhp
end end
when :DEFENSE when :DEFENSE
# Modify score depending on current stat stage # Modify score depending on current stat stage
if old_stage <= -2 if old_stage <= -2
score -= 20 score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
else else
score += 5 * (2 + old_stage) * dec_mult score += 5 * dec_mult
score += 4 * dec_mult if @user.hp == @user.totalhp
end end
when :SPECIAL_ATTACK when :SPECIAL_ATTACK
# Modify score depending on current stat stage # Modify score depending on current stat stage
# More strongly prefer if the target has no physical moves # More strongly prefer if the target has no physical moves
if old_stage <= -2 if old_stage <= -2
score -= 20 score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
else else
has_physical_moves = @target.check_for_move { |m| m.physicalMove?(m.type) && has_physical_moves = target.check_for_move { |m| m.physicalMove?(m.type) &&
m.function != "UseUserDefenseInsteadOfUserAttack" && m.function != "UseUserDefenseInsteadOfUserAttack" &&
m.function != "UseTargetAttackInsteadOfUserAttack" } m.function != "UseTargetAttackInsteadOfUserAttack" }
dec = (has_physical_moves) ? 5 : 10 dec = (has_physical_moves) ? 5 : 10
score += dec * (2 + old_stage) * dec_mult score += dec * dec_mult
score += 4 * dec_mult if @user.hp == @user.totalhp
end end
when :SPECIAL_DEFENSE when :SPECIAL_DEFENSE
# Modify score depending on current stat stage # Modify score depending on current stat stage
if old_stage <= -2 if old_stage <= -2
score -= 20 score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
else else
score += 5 * (2 + old_stage) * dec_mult score += 5 * dec_mult
score += 4 * dec_mult if @user.hp == @user.totalhp
end end
when :SPEED when :SPEED
# Prefer if target is faster than an ally # Prefer if target is faster than an ally
# TODO: Don't prefer if the target is too much faster than any ally and # TODO: Don't prefer if the target is too much faster than any ally and
# can't be brought slow enough. # can't be brought slow enough.
each_foe_battler(@target.side) do |b, i| each_foe_battler(target.side) do |b, i|
next if b.faster_than?(@target) next if b.faster_than?(target)
score += 15 * dec_mult score += 15 * dec_mult
break break
end end
# Prefer if any ally has Electro Ball # Prefer if any ally has Electro Ball
each_foe_battler(@target.side) do |b, i| each_foe_battler(target.side) do |b, i|
next if !b.check_for_move { |m| m.function == "PowerHigherWithUserFasterThanTarget" } next if !b.check_for_move { |m| m.function == "PowerHigherWithUserFasterThanTarget" }
score += 8 * dec_mult score += 8 * dec_mult
end end
# Don't prefer if target has Speed Boost (will be gaining Speed anyway) # Don't prefer if target has Speed Boost (will be gaining Speed anyway)
score -= 20 if @target.has_active_ability?(:SPEEDBOOST) if target.has_active_ability?(:SPEEDBOOST)
score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
end
when :ACCURACY when :ACCURACY
# Modify score depending on current stat stage # Modify score depending on current stat stage
if old_stage <= -2 if old_stage <= -2
score -= 20 score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
else else
score += 5 * (2 + old_stage) * dec_mult score += 5 * dec_mult
score += 4 * dec_mult if @user.hp == @user.totalhp
end end
# TODO: Prefer if target is poisoned/toxiced/Leech Seeded/cursed. # TODO: Prefer if target is poisoned/toxiced/Leech Seeded/cursed.
when :EVASION when :EVASION
# Modify score depending on current stat stage # Modify score depending on current stat stage
if old_stage <= -2 if old_stage <= -2
score -= 20 score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
else else
score += 5 * (2 + old_stage) * dec_mult score += 5 * dec_mult
score += 4 * dec_mult if @user.hp == @user.totalhp
end end
end end
# Prefer if target has Stored Power # Prefer if target has Stored Power
if @target.check_for_move { |m| m.function == "PowerHigherWithUserPositiveStatStages" } if target.check_for_move { |m| m.function == "PowerHigherWithUserPositiveStatStages" }
score += 5 * decrement score += 5 * decrement * desire_mult
end end
# Don't prefer if any ally has Punishment # Don't prefer if any foe has Punishment
each_foe_battler(@target.side) do |b, i| each_foe_battler(target.side) do |b, i|
next if !b.check_for_move { |m| m.function == "PowerHigherWithTargetPositiveStatStages" } next if !b.check_for_move { |m| m.function == "PowerHigherWithTargetPositiveStatStages" }
score -= 5 * decrement score -= 5 * decrement * desire_mult
end end
return score return score

View File

@@ -1,305 +0,0 @@
class Battle::AI
#=============================================================================
# Calculate how much damage a move is likely to do to a given target (as a
# percentage of the target's current HP)
# TODO: How much is this going to be used? Should the predicted percentage of
# damage be used as the initial score for damaging moves?
#=============================================================================
def pbGetDamagingMoveBaseScore
return 100
=begin
# Don't prefer moves that are ineffective because of abilities or effects
return 0 if @target.immune_to_move?
user_battler = @user.battler
target_battler = @target.battler
# Calculate how much damage the move will do (roughly)
calc_damage = @move.rough_damage
# TODO: Maybe move this check elsewhere? Note that Reborn's base score does
# not include this halving, but the predicted damage does.
# Two-turn attacks waste 2 turns to deal one lot of damage
calc_damage /= 2 if @move.move.chargingTurnMove?
# TODO: Maybe move this check elsewhere?
# Increased critical hit rate
if @trainer.medium_skill?
crit_stage = @move.rough_critical_hit_stage
if crit_stage >= 0
crit_fraction = (crit_stage > 50) ? 1 : Battle::Move::CRITICAL_HIT_RATIOS[crit_stage]
crit_mult = (Settings::NEW_CRITICAL_HIT_RATE_MECHANICS) ? 0.5 : 1
calc_damage *= (1 + crit_mult / crit_fraction)
end
end
# Convert damage to percentage of target's remaining HP
damage_percentage = calc_damage * 100.0 / target_battler.hp
# Don't prefer weak attacks
# damage_percentage /= 2 if damage_percentage < 20
# Prefer damaging attack if level difference is significantly high
# damage_percentage *= 1.2 if user_battler.level - 10 > target_battler.level
# Adjust score
damage_percentage = 110 if damage_percentage > 110 # Treat all lethal moves the same
damage_percentage += 40 if damage_percentage > 100 # Prefer moves likely to be lethal
return damage_percentage.to_i
=end
end
#=============================================================================
# TODO: Remove this method. If we're keeping any score changes inherent to a
# move's effect, they will go in MoveEffectScore handlers instead.
#=============================================================================
def pbGetStatusMoveBaseScore
return 100
=begin
# TODO: Call @target.immune_to_move? here too, not just for damaging moves
# (only if this status move will be affected).
# TODO: Make sure all status moves are accounted for.
# TODO: Duplicates in Reborn's AI:
# "SleepTarget" Grass Whistle (15), Hypnosis (15), Sing (15),
# Lovely Kiss (20), Sleep Powder (20), Spore (60)
# "PoisonTarget" - Poison Powder (15), Poison Gas (20)
# "ParalyzeTarget" - Stun Spore (25), Glare (30)
# "ConfuseTarget" - Teeter Dance (5), Supersonic (10),
# Sweet Kiss (20), Confuse Ray (25)
# "RaiseUserAttack1" - Howl (10), Sharpen (10), Medicate (15)
# "RaiseUserSpeed2" - Agility (15), Rock Polish (25)
# "LowerTargetAttack1" - Growl (10), Baby-Doll Eyes (15)
# "LowerTargetAccuracy1" - Sand Attack (5), Flash (10), Kinesis (10), Smokescreen (10)
# "LowerTargetAttack2" - Charm (10), Feather Dance (15)
# "LowerTargetSpeed2" - String Shot (10), Cotton Spore (15), Scary Face (15)
# "LowerTargetSpDef2" - Metal Sound (10), Fake Tears (15)
case @move.move.function
when "ConfuseTarget",
"LowerTargetAccuracy1",
"LowerTargetEvasion1RemoveSideEffects",
"UserTargetSwapAtkSpAtkStages",
"UserTargetSwapDefSpDefStages",
"UserSwapBaseAtkDef",
"UserTargetAverageBaseAtkSpAtk",
"UserTargetAverageBaseDefSpDef",
"SetUserTypesToUserMoveType",
"SetTargetTypesToWater",
"SetUserTypesToTargetTypes",
"SetTargetAbilityToUserAbility",
"UserTargetSwapAbilities",
"PowerUpAllyMove",
"StartWeakenElectricMoves",
"StartWeakenFireMoves",
"EnsureNextMoveAlwaysHits",
"StartNegateTargetEvasionStatStageAndGhostImmunity",
"StartNegateTargetEvasionStatStageAndDarkImmunity",
"ProtectUserSideFromPriorityMoves",
"ProtectUserSideFromMultiTargetDamagingMoves",
"BounceBackProblemCausingStatusMoves",
"StealAndUseBeneficialStatusMove",
"DisableTargetMovesKnownByUser",
"DisableTargetHealingMoves",
"SetAttackerMovePPTo0IfUserFaints",
"UserEnduresFaintingThisTurn",
"RestoreUserConsumedItem",
"StartNegateHeldItems",
"StartDamageTargetEachTurnIfTargetAsleep",
"HealUserDependingOnUserStockpile",
"StartGravity",
"StartUserAirborne",
"UserSwapsPositionsWithAlly",
"StartSwapAllBattlersBaseDefensiveStats",
"RaiseTargetSpDef1",
"RaiseGroundedGrassBattlersAtkSpAtk1",
"RaiseGrassBattlersDef1",
"AddGrassTypeToTarget",
"TrapAllBattlersInBattleForOneTurn",
"EnsureNextCriticalHit",
"UserTargetSwapBaseSpeed",
"RedirectAllMovesToTarget",
"TargetUsesItsLastUsedMoveAgain"
return 55
when "RaiseUserAttack1",
"RaiseUserDefense1",
"RaiseUserDefense1CurlUpUser",
"RaiseUserCriticalHitRate2",
"RaiseUserAtkSpAtk1",
"RaiseUserAtkSpAtk1Or2InSun",
"RaiseUserAtkAcc1",
"RaiseTargetRandomStat2",
"LowerTargetAttack1",
"LowerTargetDefense1",
"LowerTargetAccuracy1",
"LowerTargetAttack2",
"LowerTargetSpeed2",
"LowerTargetSpDef2",
"ResetAllBattlersStatStages",
"UserCopyTargetStatStages",
"SetUserTypesBasedOnEnvironment",
"DisableTargetUsingSameMoveConsecutively",
"StartTargetCannotUseItem",
"LowerTargetAttack1BypassSubstitute",
"LowerTargetAtkSpAtk1",
"LowerTargetSpAtk1",
"TargetNextFireMoveDamagesTarget"
return 60
when "SleepTarget",
"SleepTargetIfUserDarkrai",
"SleepTargetChangeUserMeloettaForm",
"PoisonTarget",
"CureUserBurnPoisonParalysis",
"RaiseUserAttack1",
"RaiseUserSpDef1PowerUpElectricMove",
"RaiseUserEvasion1",
"RaiseUserSpeed2",
"LowerTargetAttack1",
"LowerTargetAtkDef1",
"LowerTargetAttack2",
"LowerTargetDefense2",
"LowerTargetSpeed2",
"LowerTargetSpAtk2IfCanAttract",
"LowerTargetSpDef2",
"ReplaceMoveThisBattleWithTargetLastMoveUsed",
"ReplaceMoveWithTargetLastMoveUsed",
"SetUserAbilityToTargetAbility",
"UseMoveTargetIsAboutToUse",
"UseRandomMoveFromUserParty",
"StartHealUserEachTurnTrapUserInBattle",
"HealTargetHalfOfTotalHP",
"UserFaintsHealAndCureReplacement",
"UserFaintsHealAndCureReplacementRestorePP",
"StartSunWeather",
"StartRainWeather",
"StartSandstormWeather",
"StartHailWeather",
"RaisePlusMinusUserAndAlliesDefSpDef1",
"LowerTargetSpAtk2",
"LowerPoisonedTargetAtkSpAtkSpd1",
"AddGhostTypeToTarget",
"LowerTargetAtkSpAtk1SwitchOutUser",
"RaisePlusMinusUserAndAlliesAtkSpAtk1",
"HealTargetDependingOnGrassyTerrain"
return 65
when "SleepTarget",
"SleepTargetChangeUserMeloettaForm",
"SleepTargetNextTurn",
"PoisonTarget",
"ConfuseTarget",
"RaiseTargetSpAtk1ConfuseTarget",
"RaiseTargetAttack2ConfuseTarget",
"UserTargetSwapStatStages",
"StartUserSideImmunityToStatStageLowering",
"SetUserTypesToResistLastAttack",
"SetTargetAbilityToSimple",
"SetTargetAbilityToInsomnia",
"NegateTargetAbility",
"TransformUserIntoTarget",
"UseLastMoveUsedByTarget",
"UseLastMoveUsed",
"UseRandomMove",
"HealUserFullyAndFallAsleep",
"StartHealUserEachTurn",
"StartPerishCountsForAllBattlers",
"SwitchOutTargetStatusMove",
"TrapTargetInBattle",
"TargetMovesBecomeElectric",
"NormalMovesBecomeElectric",
"PoisonTargetLowerTargetSpeed1"
return 70
when "BadPoisonTarget",
"ParalyzeTarget",
"BurnTarget",
"ConfuseTarget",
"AttractTarget",
"GiveUserStatusToTarget",
"RaiseUserDefSpDef1",
"RaiseUserDefense2",
"RaiseUserSpeed2",
"RaiseUserSpeed2LowerUserWeight",
"RaiseUserSpDef2",
"RaiseUserEvasion2MinimizeUser",
"RaiseUserDefense3",
"MaxUserAttackLoseHalfOfTotalHP",
"UserTargetAverageHP",
"ProtectUser",
"DisableTargetLastMoveUsed",
"DisableTargetStatusMoves",
"HealUserHalfOfTotalHP",
"HealUserHalfOfTotalHPLoseFlyingTypeThisTurn",
"HealUserPositionNextTurn",
"HealUserDependingOnWeather",
"StartLeechSeedTarget",
"AttackerFaintsIfUserFaints",
"UserTargetSwapItems",
"UserMakeSubstitute",
"UserAddStockpileRaiseDefSpDef1",
"RedirectAllMovesToUser",
"InvertTargetStatStages",
"HealUserByTargetAttackLowerTargetAttack1",
"HealUserDependingOnSandstorm"
return 75
when "ParalyzeTarget",
"ParalyzeTargetIfNotTypeImmune",
"RaiseUserAtkDef1",
"RaiseUserAtkDefAcc1",
"RaiseUserSpAtkSpDef1",
"UseMoveDependingOnEnvironment",
"UseRandomUserMoveIfAsleep",
"DisableTargetUsingDifferentMove",
"SwitchOutUserPassOnEffects",
"AddSpikesToFoeSide",
"AddToxicSpikesToFoeSide",
"AddStealthRocksToFoeSide",
"CurseTargetOrLowerUserSpd1RaiseUserAtkDef1",
"StartSlowerBattlersActFirst",
"ProtectUserFromTargetingMovesSpikyShield",
"StartElectricTerrain",
"StartGrassyTerrain",
"StartMistyTerrain",
"StartPsychicTerrain",
"CureTargetStatusHealUserHalfOfTotalHP"
return 80
when "CureUserPartyStatus",
"RaiseUserAttack2",
"RaiseUserSpAtk2",
"RaiseUserSpAtk3",
"StartUserSideDoubleSpeed",
"StartWeakenPhysicalDamageAgainstUserSide",
"StartWeakenSpecialDamageAgainstUserSide",
"ProtectUserSideFromDamagingMovesIfUserFirstTurn",
"ProtectUserFromDamagingMovesKingsShield",
"ProtectUserBanefulBunker"
return 85
when "RaiseUserAtkSpd1",
"RaiseUserSpAtkSpDefSpd1",
"LowerUserDefSpDef1RaiseUserAtkSpAtkSpd2",
"RaiseUserAtk1Spd2",
"TwoTurnAttackRaiseUserSpAtkSpDefSpd2"
return 90
when "SleepTarget",
"SleepTargetChangeUserMeloettaForm",
"AddStickyWebToFoeSide",
"StartWeakenDamageAgainstUserSideIfHail"
return 100
end
# "DoesNothingUnusableInGravity",
# "StartUserSideImmunityToInflictedStatus",
# "LowerTargetEvasion1",
# "LowerTargetEvasion2",
# "StartPreventCriticalHitsAgainstUserSide",
# "UserFaintsLowerTargetAtkSpAtk2",
# "FleeFromBattle",
# "SwitchOutUserStatusMove"
# "TargetTakesUserItem",
# "LowerPPOfTargetLastMoveBy4",
# "StartTargetAirborneAndAlwaysHitByMoves",
# "TargetActsNext",
# "TargetActsLast",
# "ProtectUserSideFromStatusMoves"
return 100
=end
end
end

View File

@@ -10,7 +10,7 @@ Battle::AI::Handlers::MoveFailureCheck.add("RaiseUserAttack1",
) )
Battle::AI::Handlers::MoveEffectScore.add("RaiseUserAttack1", Battle::AI::Handlers::MoveEffectScore.add("RaiseUserAttack1",
proc { |score, move, user, ai, battle| proc { |score, move, user, ai, battle|
next ai.get_score_for_user_stat_raise(score) next ai.get_score_for_target_stat_raise(score, user, move.move.statUp)
} }
) )
@@ -28,7 +28,7 @@ Battle::AI::Handlers::MoveEffectScore.copy("RaiseUserAttack1",
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseUserAttack2IfTargetFaints", Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseUserAttack2IfTargetFaints",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
if move.rough_damage >= target.hp * 0.9 if move.rough_damage >= target.hp * 0.9
next ai.get_score_for_user_stat_raise(score) next ai.get_score_for_target_stat_raise(score, user, move.move.statUp)
end end
} }
) )
@@ -58,7 +58,7 @@ Battle::AI::Handlers::MoveFailureCheck.add("MaxUserAttackLoseHalfOfTotalHP",
) )
Battle::AI::Handlers::MoveEffectScore.add("MaxUserAttackLoseHalfOfTotalHP", Battle::AI::Handlers::MoveEffectScore.add("MaxUserAttackLoseHalfOfTotalHP",
proc { |score, move, user, ai, battle| proc { |score, move, user, ai, battle|
score = ai.get_score_for_user_stat_raise(score) score = ai.get_score_for_target_stat_raise(score, user, move.move.statUp)
# Don't prefer the lower the user's HP is # Don't prefer the lower the user's HP is
score -= 80 * (1 - (user.hp.to_f / user.totalhp)) # 0 to -40 score -= 80 * (1 - (user.hp.to_f / user.totalhp)) # 0 to -40
next score next score
@@ -81,7 +81,7 @@ Battle::AI::Handlers::MoveFailureCheck.copy("RaiseUserDefense1",
"RaiseUserDefense1CurlUpUser") "RaiseUserDefense1CurlUpUser")
Battle::AI::Handlers::MoveEffectScore.add("RaiseUserDefense1CurlUpUser", Battle::AI::Handlers::MoveEffectScore.add("RaiseUserDefense1CurlUpUser",
proc { |score, move, user, ai, battle| proc { |score, move, user, ai, battle|
score = ai.get_score_for_user_stat_raise(score) score = ai.get_score_for_target_stat_raise(score, user, move.move.statUp)
if !user.effects[PBEffects::DefenseCurl] && if !user.effects[PBEffects::DefenseCurl] &&
user.check_for_move { |m| m.function == "MultiTurnAttackPowersUpEachTurn" } user.check_for_move { |m| m.function == "MultiTurnAttackPowersUpEachTurn" }
score += 10 score += 10
@@ -146,7 +146,7 @@ Battle::AI::Handlers::MoveFailureCheck.copy("RaiseUserSpDef1",
"RaiseUserSpDef1PowerUpElectricMove") "RaiseUserSpDef1PowerUpElectricMove")
Battle::AI::Handlers::MoveEffectScore.add("RaiseUserSpDef1PowerUpElectricMove", Battle::AI::Handlers::MoveEffectScore.add("RaiseUserSpDef1PowerUpElectricMove",
proc { |score, move, user, ai, battle| proc { |score, move, user, ai, battle|
score = ai.get_score_for_user_stat_raise(score) score = ai.get_score_for_target_stat_raise(score, user, move.move.statUp)
if user.check_for_move { |m| m.damagingMove? && m.type == :ELECTRIC } if user.check_for_move { |m| m.damagingMove? && m.type == :ELECTRIC }
score += 10 score += 10
end end
@@ -193,7 +193,7 @@ Battle::AI::Handlers::MoveFailureCheck.copy("RaiseUserSpeed2",
"RaiseUserSpeed2LowerUserWeight") "RaiseUserSpeed2LowerUserWeight")
Battle::AI::Handlers::MoveEffectScore.add("RaiseUserSpeed2LowerUserWeight", Battle::AI::Handlers::MoveEffectScore.add("RaiseUserSpeed2LowerUserWeight",
proc { |score, move, user, ai, battle| proc { |score, move, user, ai, battle|
score = ai.get_score_for_user_stat_raise(score) score = ai.get_score_for_target_stat_raise(score, user, move.move.statUp)
if ai.trainer.medium_skill? if ai.trainer.medium_skill?
# TODO: Take into account weight-modifying items/abilities? This "> 1" # TODO: Take into account weight-modifying items/abilities? This "> 1"
# line can probably ignore them, but these moves' powers will change # line can probably ignore them, but these moves' powers will change
@@ -274,7 +274,7 @@ Battle::AI::Handlers::MoveFailureCheck.copy("RaiseUserEvasion2",
"RaiseUserEvasion2MinimizeUser") "RaiseUserEvasion2MinimizeUser")
Battle::AI::Handlers::MoveEffectScore.add("RaiseUserEvasion2MinimizeUser", Battle::AI::Handlers::MoveEffectScore.add("RaiseUserEvasion2MinimizeUser",
proc { |score, move, user, ai, battle| proc { |score, move, user, ai, battle|
score = ai.get_score_for_user_stat_raise(score) score = ai.get_score_for_target_stat_raise(score, user, move.move.statUp)
if ai.trainer.medium_skill? && !user.effects[PBEffects::Minimize] if ai.trainer.medium_skill? && !user.effects[PBEffects::Minimize]
ai.each_foe_battler(user.side) do |b, i| ai.each_foe_battler(user.side) do |b, i|
# Moves that do double damage and (in Gen 6+) have perfect accuracy # Moves that do double damage and (in Gen 6+) have perfect accuracy
@@ -368,7 +368,7 @@ Battle::AI::Handlers::MoveEffectScore.copy("RaiseUserAtkSpAtk1",
"RaiseUserAtkSpAtk1Or2InSun") "RaiseUserAtkSpAtk1Or2InSun")
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveFailureCheck.add("LowerUserDefSpDef1RaiseUserAtkSpAtkSpd2", Battle::AI::Handlers::MoveFailureCheck.add("LowerUserDefSpDef1RaiseUserAtkSpAtkSpd2",
proc { |move, user, ai, battle| proc { |move, user, ai, battle|
@@ -388,21 +388,9 @@ Battle::AI::Handlers::MoveFailureCheck.add("LowerUserDefSpDef1RaiseUserAtkSpAtkS
) )
Battle::AI::Handlers::MoveEffectScore.add("LowerUserDefSpDef1RaiseUserAtkSpAtkSpd2", Battle::AI::Handlers::MoveEffectScore.add("LowerUserDefSpDef1RaiseUserAtkSpAtkSpd2",
proc { |score, move, user, ai, battle| proc { |score, move, user, ai, battle|
score -= user.stages[:ATTACK] * 20 score = ai.get_score_for_target_stat_raise(score, user, move.move.statUp)
score -= user.stages[:SPEED] * 20 next score if score == Battle::AI::MOVE_USELESS_SCORE
score -= user.stages[:SPECIAL_ATTACK] * 20 next ai.get_score_for_target_stat_drop(score, user, move.move.statDown, false)
score += user.stages[:DEFENSE] * 10
score += user.stages[:SPECIAL_DEFENSE] * 10
if ai.trainer.medium_skill?
hasDamagingAttack = false
user.battler.eachMove do |m|
next if !m.damagingMove?
hasDamagingAttack = true
break
end
score += 20 if hasDamagingAttack
end
next score
} }
) )
@@ -463,7 +451,7 @@ Battle::AI::Handlers::MoveEffectScore.copy("RaiseUserAtkSpAtk1",
"RaiseUserMainStats1") "RaiseUserMainStats1")
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveFailureCheck.add("RaiseUserMainStats1LoseThirdOfTotalHP", Battle::AI::Handlers::MoveFailureCheck.add("RaiseUserMainStats1LoseThirdOfTotalHP",
proc { |move, user, ai, battle| proc { |move, user, ai, battle|
@@ -479,12 +467,12 @@ Battle::AI::Handlers::MoveFailureCheck.add("RaiseUserMainStats1LoseThirdOfTotalH
) )
Battle::AI::Handlers::MoveEffectScore.add("RaiseUserMainStats1LoseThirdOfTotalHP", Battle::AI::Handlers::MoveEffectScore.add("RaiseUserMainStats1LoseThirdOfTotalHP",
proc { |score, move, user, ai, battle| proc { |score, move, user, ai, battle|
next 0 if !battle.moldBreaker && user.has_active_ability?(:CONTRARY) # Score for stat increase
score += 30 if ai.trainer.high_skill? && user.hp >= user.totalhp * 0.75 score = ai.get_score_for_target_stat_raise(score, user, move.move.statUp)
GameData::Stat.each_main_battle { |s| score += 10 if user.stages[s.id] <= 0 } next score if score == Battle::AI::MOVE_USELESS_SCORE
if ai.trainer.medium_skill? # Score for losing HP
hasDamagingAttack = user.battler.moves.any? { |m| next m&.damagingMove? } if user.hp <= user.totalhp * 0.75
score += 20 if hasDamagingAttack score -= 30 * (user.totalhp - user.hp) / user.totalhp
end end
next score next score
} }
@@ -507,15 +495,11 @@ Battle::AI::Handlers::MoveFailureCheck.add("RaiseUserMainStats1TrapUserInBattle"
) )
Battle::AI::Handlers::MoveEffectScore.add("RaiseUserMainStats1TrapUserInBattle", Battle::AI::Handlers::MoveEffectScore.add("RaiseUserMainStats1TrapUserInBattle",
proc { |score, move, user, ai, battle| proc { |score, move, user, ai, battle|
next 0 if !battle.moldBreaker && user.has_active_ability?(:CONTRARY) # Score for stat increase
if ai.trainer.high_skill? score = ai.get_score_for_target_stat_raise(score, user, move.move.statUp)
score -= 50 if user.hp <= user.totalhp / 2 # Score for trapping user in battle
score += 30 if user.battler.trappedInBattle? if ai.trainer.medium_skill? && !user.battler.trappedInBattle?
end score -= 10 if user.hp <= user.totalhp / 2
GameData::Stat.each_main_battle { |s| score += 10 if user.stages[s.id] <= 0 }
if ai.trainer.medium_skill?
hasDamagingAttack = user.battler.moves.any? { |m| next m&.damagingMove? }
score += 20 if hasDamagingAttack
end end
next score next score
} }
@@ -531,113 +515,85 @@ Battle::AI::Handlers::MoveEffectScore.add("StartRaiseUserAtk1WhenDamaged",
) )
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.add("LowerUserAttack1", Battle::AI::Handlers::MoveEffectScore.add("LowerUserAttack1",
proc { |score, move, user, ai, battle| proc { |score, move, user, ai, battle|
next score + user.stages[:ATTACK] * 10 next ai.get_score_for_target_stat_drop(score, user, move.move.statDown)
} }
) )
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.copy("LowerUserAttack1", Battle::AI::Handlers::MoveEffectScore.copy("LowerUserAttack1",
"LowerUserAttack2") "LowerUserAttack2")
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.add("LowerUserDefense1", Battle::AI::Handlers::MoveEffectScore.copy("LowerUserAttack1",
proc { |score, move, user, ai, battle| "LowerUserDefense1")
next score + user.stages[:DEFENSE] * 10
}
)
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.copy("LowerUserDefense1", Battle::AI::Handlers::MoveEffectScore.copy("LowerUserDefense1",
"LowerUserDefense2") "LowerUserDefense2")
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.add("LowerUserSpAtk1", Battle::AI::Handlers::MoveEffectScore.copy("LowerUserAttack1",
proc { |score, move, user, ai, battle| "LowerUserSpAtk1")
next score + user.stages[:SPECIAL_ATTACK] * 10
}
)
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.copy("LowerUserSpAtk1", Battle::AI::Handlers::MoveEffectScore.copy("LowerUserSpAtk1",
"LowerUserSpAtk2") "LowerUserSpAtk2")
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.add("LowerUserSpDef1", Battle::AI::Handlers::MoveEffectScore.copy("LowerUserDefense1",
proc { |score, move, user, ai, battle| "LowerUserSpDef1")
next score + user.stages[:SPECIAL_DEFENSE] * 10
}
)
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.copy("LowerUserSpDef1", Battle::AI::Handlers::MoveEffectScore.copy("LowerUserSpDef1",
"LowerUserSpDef2") "LowerUserSpDef2")
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.add("LowerUserSpeed1", Battle::AI::Handlers::MoveEffectScore.copy("LowerUserAttack1",
proc { |score, move, user, ai, battle| "LowerUserSpeed1")
next score + user.stages[:SPECIAL_DEFENSE] * 10
}
)
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.copy("LowerUserSpeed1", Battle::AI::Handlers::MoveEffectScore.copy("LowerUserSpeed1",
"LowerUserSpeed2") "LowerUserSpeed2")
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.add("LowerUserAtkDef1", Battle::AI::Handlers::MoveEffectScore.copy("LowerUserAttack1",
proc { |score, move, user, ai, battle| "LowerUserAtkDef1")
avg = user.stages[:ATTACK] * 10
avg += user.stages[:DEFENSE] * 10
next score + avg / 2
}
)
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.add("LowerUserDefSpDef1", Battle::AI::Handlers::MoveEffectScore.copy("LowerUserAttack1",
proc { |score, move, user, ai, battle| "LowerUserDefSpDef1")
avg = user.stages[:DEFENSE] * 10
avg += user.stages[:SPECIAL_DEFENSE] * 10
next score + avg / 2
}
)
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.add("LowerUserDefSpDefSpd1", Battle::AI::Handlers::MoveEffectScore.copy("LowerUserAttack1",
proc { |score, move, user, ai, battle| "LowerUserDefSpDefSpd1")
avg = user.stages[:DEFENSE] * 10
avg += user.stages[:SPEED] * 10
avg += user.stages[:SPECIAL_DEFENSE] * 10
next score + (avg / 3).floor
}
)
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. # TODO: Review score modifiers.
@@ -648,6 +604,11 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseTargetAttack1",
!target.battler.pbCanRaiseStatStage?(:ATTACK, user.battler, move.move) !target.battler.pbCanRaiseStatStage?(:ATTACK, user.battler, move.move)
} }
) )
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetAttack1",
proc { |score, move, user, target, ai, battle|
next ai.get_score_for_target_stat_raise(score, target, [:ATTACK, 1])
}
)
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. # TODO: Review score modifiers.
@@ -660,8 +621,12 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseTargetAttack2Confu
) )
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetAttack2ConfuseTarget", Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetAttack2ConfuseTarget",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
next Battle::AI::MOVE_USELESS_SCORE if !target.battler.pbCanConfuse?(user.battler, false) next Battle::AI::MOVE_USELESS_SCORE if !target.battler.pbCanConfuse?(user.battler, false, move.move)
next score + 30 if target.stages[:ATTACK] < 0 # Score for stat raise
stat_score = ai.get_score_for_target_stat_raise(score, target, [:ATTACK, 2], false)
# Score for confusing the target
next Battle::AI::Handlers.apply_move_effect_against_target_score(
"ConfuseTarget", score, move, user, target, ai, battle)
} }
) )
@@ -676,13 +641,17 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseTargetSpAtk1Confus
) )
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetSpAtk1ConfuseTarget", Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetSpAtk1ConfuseTarget",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
next Battle::AI::MOVE_USELESS_SCORE if !target.battler.pbCanConfuse?(user.battler, false) next Battle::AI::MOVE_USELESS_SCORE if !target.battler.pbCanConfuse?(user.battler, false, move.move)
next score + 30 if target.stages[:SPECIAL_ATTACK] < 0 # Score for stat raise
stat_score = ai.get_score_for_target_stat_raise(score, target, [:SPECIAL_ATTACK, 1], false)
# Score for confusing the target
next Battle::AI::Handlers.apply_move_effect_against_target_score(
"ConfuseTarget", score, move, user, target, ai, battle)
} }
) )
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseTargetSpDef1", Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseTargetSpDef1",
proc { |move, user, target, ai, battle| proc { |move, user, target, ai, battle|
@@ -691,7 +660,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseTargetSpDef1",
) )
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetSpDef1", Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetSpDef1",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
next score - target.stages[:SPECIAL_DEFENSE] * 10 next ai.get_score_for_target_stat_raise(score, target, [:SPECIAL_DEFENSE, 1])
} }
) )
@@ -720,7 +689,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetRandomStat2",
) )
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseTargetAtkSpAtk2", Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseTargetAtkSpAtk2",
proc { |move, user, target, ai, battle| proc { |move, user, target, ai, battle|
@@ -731,10 +700,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseTargetAtkSpAtk2",
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetAtkSpAtk2", Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetAtkSpAtk2",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
next Battle::AI::MOVE_USELESS_SCORE if target.opposes?(user) next Battle::AI::MOVE_USELESS_SCORE if target.opposes?(user)
next Battle::AI::MOVE_USELESS_SCORE if !battle.moldBreaker && target.has_active_ability?(:CONTRARY) next ai.get_score_for_target_stat_raise(score, target, [:ATTACK, 2, :SPECIAL_ATTACK, 2])
score -= target.stages[:ATTACK] * 10
score -= target.stages[:SPECIAL_ATTACK] * 10
next score
} }
) )
@@ -749,7 +715,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("LowerTargetAttack1",
) )
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("LowerTargetAttack1", Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("LowerTargetAttack1",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
next ai.get_score_for_target_stat_drop(score) next ai.get_score_for_target_stat_drop(score, target, move.move.statDown)
} }
) )
@@ -911,7 +877,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("LowerTargetSpeed1MakeTa
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("LowerTargetSpeed1MakeTargetWeakerToFire", Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("LowerTargetSpeed1MakeTargetWeakerToFire",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
# Score for stat drop # Score for stat drop
score = ai.get_score_for_target_stat_drop(score) score = ai.get_score_for_target_stat_drop(score, target, move.move.statDown)
# Score for adding weakness to Fire # Score for adding weakness to Fire
if !target.effects[PBEffects::TarShot] if !target.effects[PBEffects::TarShot]
score += 20 if user.battler.moves.any? { |m| m.damagingMove? && m.pbCalcType(user.battler) == :FIRE } score += 20 if user.battler.moves.any? { |m| m.damagingMove? && m.pbCalcType(user.battler) == :FIRE }
@@ -997,7 +963,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("LowerTargetEvasion1Remo
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("LowerTargetEvasion1RemoveSideEffects", Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("LowerTargetEvasion1RemoveSideEffects",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
# Score for stat drop # Score for stat drop
score = ai.get_score_for_target_stat_drop(score) score = ai.get_score_for_target_stat_drop(score, target, move.move.statDown)
# Score for removing side effects/terrain # Score for removing side effects/terrain
score += 30 if target.pbOwnSide.effects[PBEffects::AuroraVeil] > 0 || score += 30 if target.pbOwnSide.effects[PBEffects::AuroraVeil] > 0 ||
target.pbOwnSide.effects[PBEffects::Reflect] > 0 || target.pbOwnSide.effects[PBEffects::Reflect] > 0 ||
@@ -1089,13 +1055,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseAlliesAtkDef1",
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseAlliesAtkDef1", Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseAlliesAtkDef1",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
user.battler.allAllies.each do |b| user.battler.allAllies.each do |b|
if !battle.moldBreaker && b.hasActiveAbility?(:CONTRARY) score = ai.get_score_for_target_stat_raise(score, b, [:ATTACK, 1, :DEFENSE, 1])
score -= 40
else
score += 10
score -= b.stages[:ATTACK] * 10
score -= b.stages[:SPECIAL_ATTACK] * 10
end
end end
next score next score
} }
@@ -1125,13 +1085,10 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaisePlusMinusUserAndAl
) )
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaisePlusMinusUserAndAlliesAtkSpAtk1", Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaisePlusMinusUserAndAlliesAtkSpAtk1",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
# score = ai.get_score_for_target_stat_raise(score, user, [:ATTACK, 1, :SPECIAL_ATTACK, 1], false)
user.battler.allAllies.each do |b| user.battler.allAllies.each do |b|
next if b.statStageAtMax?(:ATTACK) && b.statStageAtMax?(:SPECIAL_ATTACK) score = ai.get_score_for_target_stat_raise(score, b, [:ATTACK, 1, :SPECIAL_ATTACK, 1], false)
score -= b.stages[:ATTACK] * 10
score -= b.stages[:SPECIAL_ATTACK] * 10
end end
score -= user.stages[:ATTACK] * 10
score -= user.stages[:SPECIAL_ATTACK] * 10
next score next score
} }
) )
@@ -1250,9 +1207,11 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTargetSwapAtkSpAtkSt
target_attack = target.stages[:ATTACK] target_attack = target.stages[:ATTACK]
target_spatk = target.stages[:SPECIAL_ATTACK] target_spatk = target.stages[:SPECIAL_ATTACK]
next Battle::AI::MOVE_USELESS_SCORE if user_attack >= target_attack && user_spatk >= target_spatk next Battle::AI::MOVE_USELESS_SCORE if user_attack >= target_attack && user_spatk >= target_spatk
next score - 20 if user_attack + user_spatk <= target_attack + target_spatk next score - 20 if user_attack + user_spatk >= target_attack + target_spatk
score += (target_attack - user_attack) * 10 # TODO: Check whether the user has physical/special moves that will be
score += (target_spatk - user_spatk) * 10 # stronger after the swap, and vice versa for the target?
score += (target_attack - user_attack) * 5
score += (target_spatk - user_spatk) * 5
next score next score
} }
) )
@@ -1270,9 +1229,11 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTargetSwapDefSpDefSt
target_def = target.stages[:DEFENSE] target_def = target.stages[:DEFENSE]
target_spdef = target.stages[:SPECIAL_DEFENSE] target_spdef = target.stages[:SPECIAL_DEFENSE]
next Battle::AI::MOVE_USELESS_SCORE if user_def >= target_def && user_spdef >= target_spdef next Battle::AI::MOVE_USELESS_SCORE if user_def >= target_def && user_spdef >= target_spdef
next score - 20 if user_def + user_spdef <= target_def + target_spdef next score - 20 if user_def + user_spdef >= target_def + target_spdef
score += (target_def - user_def) * 10 # TODO: Check whether the target has physical/special moves that will be
score += (target_spdef - user_spdef) * 10 # more resisted after the swap, and vice versa for the user?
score += (target_def - user_def) * 5
score += (target_spdef - user_spdef) * 5
next score next score
} }
) )
@@ -1493,15 +1454,13 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTargetAverageBaseDef
) )
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTargetAverageHP", Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTargetAverageHP",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
if user.hp >= (user.hp + target.hp) / 2 next Battle::AI::MOVE_USELESS_SCORE if user.hp >= (user.hp + target.hp) / 2
score -= 25 mult = (user.hp + target.hp) / (2.0 * user.hp)
else score += 10 * mult if mult >= 1.2
score += 25
end
next score next score
} }
) )

View File

@@ -591,11 +591,6 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("FlinchTarget",
#=============================================================================== #===============================================================================
# #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveFailureCheck.add("FlinchTargetFailsIfUserNotAsleep",
proc { |move, user, ai, battle|
next true if !user.battler.asleep?
}
)
Battle::AI::Handlers::MoveEffectAgainstTargetScore.copy("FlinchTarget", Battle::AI::Handlers::MoveEffectAgainstTargetScore.copy("FlinchTarget",
"FlinchTargetFailsIfUserNotAsleep") "FlinchTargetFailsIfUserNotAsleep")

View File

@@ -321,7 +321,7 @@ Battle::AI::Handlers::MoveFailureCheck.copy("RaiseUserAtkDef1",
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackRaiseUserSpAtkSpDefSpd2", Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackRaiseUserSpAtkSpDefSpd2",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
# Score for raising user's stats # Score for raising user's stats
score = ai.get_score_for_user_stat_raise(score) score = ai.get_score_for_target_stat_raise(score, user, move.move.statUp)
# Score for being a two turn attack # Score for being a two turn attack
score = Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttack", score = Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttack",
score, move, user, target, ai, battle) score, move, user, target, ai, battle)

View File

@@ -440,11 +440,10 @@ Battle::AI::Handlers::MoveFailureCheck.add("UseRandomMoveFromUserParty",
) )
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveFailureCheck.add("UseRandomUserMoveIfAsleep", Battle::AI::Handlers::MoveFailureCheck.add("UseRandomUserMoveIfAsleep",
proc { |move, user, ai, battle| proc { |move, user, ai, battle|
next true if !user.battler.asleep?
will_fail = true will_fail = true
user.battler.eachMoveWithIndex do |m, i| user.battler.eachMoveWithIndex do |m, i|
next if move.move.moveBlacklist.include?(m.function) next if move.move.moveBlacklist.include?(m.function)

View File

@@ -322,7 +322,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("TargetUsesItsLastUsedMo
proc { |move, user, target, ai, battle| proc { |move, user, target, ai, battle|
next true if !target.battler.lastRegularMoveUsed || next true if !target.battler.lastRegularMoveUsed ||
!target.battler.pbHasMove?(target.battler.lastRegularMoveUsed) !target.battler.pbHasMove?(target.battler.lastRegularMoveUsed)
next true if target.usingMultiTurnAttack? next true if target.battler.usingMultiTurnAttack?
next true if move.move.moveBlacklist.include?(GameData::Move.get(target.battler.lastRegularMoveUsed).function_code) next true if move.move.moveBlacklist.include?(GameData::Move.get(target.battler.lastRegularMoveUsed).function_code)
idxMove = -1 idxMove = -1
target.battler.eachMoveWithIndex do |m, i| target.battler.eachMoveWithIndex do |m, i|
@@ -522,11 +522,6 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("AllBattlersLoseHalfHPUs
next true if target.hp <= 1 next true if target.hp <= 1
} }
) )
Battle::AI::Handlers::MoveEffectScore.add("AllBattlersLoseHalfHPUserSkipsNextTurn",
proc { |score, move, user, ai, battle|
next score + 10 # Shadow moves are more preferable
}
)
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("AllBattlersLoseHalfHPUserSkipsNextTurn", Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("AllBattlersLoseHalfHPUserSkipsNextTurn",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
next score + 20 if target.hp >= target.totalhp / 2 next score + 20 if target.hp >= target.totalhp / 2
@@ -538,9 +533,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("AllBattlersLoseHalfHPUse
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.add("UserLosesHalfHP", Battle::AI::Handlers::MoveEffectScore.add("UserLosesHalfHP",
proc { |score, move, user, ai, battle| proc { |score, move, user, ai, battle|
score += 20 # Shadow moves are more preferable next score - 40
score -= 40
next score
} }
) )
@@ -551,7 +544,6 @@ Battle::AI::Handlers::MoveFailureCheck.copy("StartSunWeather",
"StartShadowSkyWeather") "StartShadowSkyWeather")
Battle::AI::Handlers::MoveEffectScore.add("StartShadowSkyWeather", Battle::AI::Handlers::MoveEffectScore.add("StartShadowSkyWeather",
proc { |score, move, user, ai, battle| proc { |score, move, user, ai, battle|
score += 20 # Shadow moves are more preferable
next Battle::AI::MOVE_USELESS_SCORE if battle.pbCheckGlobalAbility(:AIRLOCK) || next Battle::AI::MOVE_USELESS_SCORE if battle.pbCheckGlobalAbility(:AIRLOCK) ||
battle.pbCheckGlobalAbility(:CLOUDNINE) battle.pbCheckGlobalAbility(:CLOUDNINE)
score += 10 if battle.field.weather != :None # Prefer replacing another weather score += 10 if battle.field.weather != :None # Prefer replacing another weather
@@ -577,7 +569,6 @@ Battle::AI::Handlers::MoveFailureCheck.add("RemoveAllScreens",
) )
Battle::AI::Handlers::MoveEffectScore.add("RemoveAllScreens", Battle::AI::Handlers::MoveEffectScore.add("RemoveAllScreens",
proc { |score, move, user, ai, battle| proc { |score, move, user, ai, battle|
score += 20 # Shadow moves are more preferable
if user.pbOpposingSide.effects[PBEffects::AuroraVeil] > 0 || if user.pbOpposingSide.effects[PBEffects::AuroraVeil] > 0 ||
user.pbOpposingSide.effects[PBEffects::Reflect] > 0 || user.pbOpposingSide.effects[PBEffects::Reflect] > 0 ||
user.pbOpposingSide.effects[PBEffects::LightScreen] > 0 || user.pbOpposingSide.effects[PBEffects::LightScreen] > 0 ||

View File

@@ -46,8 +46,8 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:add_predicted_damage,
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
if move.damagingMove? if move.damagingMove?
dmg = move.rough_damage dmg = move.rough_damage
score += [15.0 * dmg / target.hp, 20].min score += [20.0 * dmg / target.hp, 25].min
score += 15 if dmg > target.hp * 1.1 # Predicted to KO the target score += 10 if dmg > target.hp * 1.1 # Predicted to KO the target
next score.to_i next score.to_i
end end
} }
@@ -93,6 +93,12 @@ 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. # If target is frozen, don't prefer moves that could thaw them.
# TODO: Review score modifier. # TODO: Review score modifier.

View File

@@ -267,57 +267,6 @@ class Battle::AI::AIBattler
return ret return ret
end end
def immune_to_move?
user = @ai.user
user_battler = user.battler
move = @ai.move
# TODO: Add consideration of user's Mold Breaker.
move_type = move.rough_type
typeMod = effectiveness_of_type_against_battler(move_type, user)
# Type effectiveness
return true if move.damagingMove? && Effectiveness.ineffective?(typeMod)
# Immunity due to ability/item/other effects
if @ai.trainer.medium_skill?
case move_type
when :GROUND
# TODO: Split target.airborne? into separate parts to allow different
# skill levels to apply to each part.
return true if @battler.airborne? && !move.move.hitsFlyingTargets?
when :FIRE
return true if has_active_ability?(:FLASHFIRE)
when :WATER
return true if has_active_ability?([:DRYSKIN, :STORMDRAIN, :WATERABSORB])
when :GRASS
return true if has_active_ability?(:SAPSIPPER)
when :ELECTRIC
return true if has_active_ability?([:LIGHTNINGROD, :MOTORDRIVE, :VOLTABSORB])
end
return true if move.damagingMove? && Effectiveness.not_very_effective?(typeMod) &&
has_active_ability?(:WONDERGUARD)
return true if move.damagingMove? && user.index != @index && !opposes?(user) &&
has_active_ability?(:TELEPATHY)
return true if move.statusMove? && move.move.canMagicCoat? &&
!@ai.battle.moldBreaker && has_active_ability?(:MAGICBOUNCE) &&
opposes?(user)
return true if move.move.soundMove? && !@ai.battle.moldBreaker && has_active_ability?(:SOUNDPROOF)
return true if move.move.bombMove? && has_active_ability?(:BULLETPROOF)
if move.move.powderMove?
return true if has_type?(:GRASS)
return true if !@ai.battle.moldBreaker && has_active_ability?(:OVERCOAT)
return true if has_active_ability?(:SAFETYGOGGLES)
end
return true if move.move.statusMove? && @battler.effects[PBEffects::Substitute] > 0 &&
!move.move.ignoresSubstitute?(user) && user.index != @index
return true if move.move.statusMove? && Settings::MECHANICS_GENERATION >= 7 &&
user.has_active_ability?(:PRANKSTER) && has_type?(:DARK) &&
opposes?(user)
return true if move.move.priority > 0 && @ai.battle.field.terrain == :Psychic &&
@battler.affectedByTerrain? && opposes?(user)
# TODO: Dazzling/Queenly Majesty go here.
end
return false
end
#============================================================================= #=============================================================================
def can_switch_lax? def can_switch_lax?

View File

@@ -40,6 +40,10 @@ class Battle::AI::AIMove
#============================================================================= #=============================================================================
def pbTarget(_user)
return @move.pbTarget(_user)
end
# Returns whether this move targets multiple battlers. # Returns whether this move targets multiple battlers.
def targets_multiple_battlers? def targets_multiple_battlers?
user_battler = @ai_battler.battler user_battler = @ai_battler.battler