mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-06 06:01:46 +00:00
Generalised AI code for scoring stat changes
This commit is contained in:
@@ -155,6 +155,8 @@ end
|
||||
# Lower multiple of user's stats.
|
||||
#===============================================================================
|
||||
class Battle::Move::StatDownMove < Battle::Move
|
||||
attr_reader :statDown
|
||||
|
||||
def pbEffectWhenDealingDamage(user, target)
|
||||
return if @battle.pbAllFainted?(target.idxOwnSide)
|
||||
showAnim = true
|
||||
|
||||
@@ -907,11 +907,12 @@ class Battle::Move::RaiseTargetAtkSpAtk2 < Battle::Move
|
||||
end
|
||||
|
||||
def pbEffectAgainstTarget(user, target)
|
||||
showAnim = true
|
||||
if target.pbCanRaiseStatStage?(:ATTACK, user, self)
|
||||
target.pbRaiseStatStage(:ATTACK, 2, user)
|
||||
showAnim = false if target.pbRaiseStatStage(:ATTACK, 2, user, showAnim)
|
||||
end
|
||||
if target.pbCanRaiseStatStage?(:SPECIAL_ATTACK, user, self)
|
||||
target.pbRaiseStatStage(:SPECIAL_ATTACK, 2, user)
|
||||
target.pbRaiseStatStage(:SPECIAL_ATTACK, 2, user, showAnim)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -65,7 +65,8 @@ class Battle::AI
|
||||
scoreSum = 0
|
||||
scoreCount = 0
|
||||
battler.allOpposing.each do |b|
|
||||
scoreSum += pbGetMoveScore(battler.moves[idxEncoredMove], [b])
|
||||
set_up_move_check(battler.moves[idxEncoredMove])
|
||||
scoreSum += pbGetMoveScore([b])
|
||||
scoreCount += 1
|
||||
end
|
||||
if scoreCount > 0 && scoreSum / scoreCount <= 20
|
||||
|
||||
@@ -11,6 +11,11 @@ class Battle::AI
|
||||
|
||||
#=============================================================================
|
||||
# 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
|
||||
choices = []
|
||||
@@ -37,7 +42,7 @@ class Battle::AI
|
||||
when 0 # No targets, affects the user or a side or the whole field
|
||||
# Includes: BothSides, FoeSide, None, User, UserSide
|
||||
score = MOVE_BASE_SCORE
|
||||
PBDebug.logonerr { score = pbGetMoveScore(move) }
|
||||
PBDebug.logonerr { score = pbGetMoveScore }
|
||||
add_move_to_choices(choices, idxMove, score)
|
||||
when 1 # One target to be chosen by the trainer
|
||||
# Includes: Foe, NearAlly, NearFoe, NearOther, Other, RandomNearFoe, UserOrNearAlly
|
||||
@@ -46,16 +51,11 @@ class Battle::AI
|
||||
# Lightning Rod. Skip any battlers that can't be targeted.
|
||||
@battle.allBattlers.each do |b|
|
||||
next if !@battle.pbMoveCanTarget?(@user.battler.index, b.index, target_data)
|
||||
# TODO: This should consider targeting an ally if possible. Scores will
|
||||
# need to distinguish between harmful and beneficial to target.
|
||||
# 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).
|
||||
# TODO: Should this sometimes consider targeting an ally? See def
|
||||
# pbGetMoveScoreAgainstTarget for more information.
|
||||
next if target_data.targets_foe && !@user.battler.opposes?(b)
|
||||
score = MOVE_BASE_SCORE
|
||||
PBDebug.logonerr { score = pbGetMoveScore(move, [b]) }
|
||||
PBDebug.logonerr { score = pbGetMoveScore([b]) }
|
||||
add_move_to_choices(choices, idxMove, score, b.index)
|
||||
end
|
||||
else # Multiple targets at once
|
||||
@@ -66,7 +66,7 @@ class Battle::AI
|
||||
targets.push(b)
|
||||
end
|
||||
score = MOVE_BASE_SCORE
|
||||
PBDebug.logonerr { score = pbGetMoveScore(move, targets) }
|
||||
PBDebug.logonerr { score = pbGetMoveScore(targets) }
|
||||
add_move_to_choices(choices, idxMove, score)
|
||||
end
|
||||
end
|
||||
@@ -93,8 +93,7 @@ class Battle::AI
|
||||
end
|
||||
|
||||
def set_up_move_check_target(target)
|
||||
# TODO: Set @target to nil if there isn't one?
|
||||
@target = (target) ? @battlers[target.index] : nil # @user
|
||||
@target = (target) ? @battlers[target.index] : nil
|
||||
@target&.refresh_battler
|
||||
end
|
||||
|
||||
@@ -104,11 +103,12 @@ class Battle::AI
|
||||
# TODO: Add skill checks in here for particular calculations?
|
||||
#=============================================================================
|
||||
def pbPredictMoveFailure
|
||||
# TODO: Something involving user.usingMultiTurnAttack? (perhaps earlier than
|
||||
# this?).
|
||||
# TODO: Something involving user.battler.usingMultiTurnAttack? (perhaps
|
||||
# earlier than this?).
|
||||
# User is asleep and will not wake up
|
||||
return true if @trainer.medium_skill? && @user.battler.asleep? &&
|
||||
@user.statusCount > 1 && !@move.move.usableWhenAsleep?
|
||||
return true if @user.battler.asleep? && @user.statusCount > 1 && !@move.move.usableWhenAsleep?
|
||||
# User is awake and can't use moves that are only usable when asleep
|
||||
return true if !@user.battler.asleep? && @move.move.usableWhenAsleep?
|
||||
# User will be truanting
|
||||
return true if @user.has_active_ability?(:TRUANT) && @user.effects[PBEffects::Truant]
|
||||
# Primal weather
|
||||
@@ -142,8 +142,8 @@ class Battle::AI
|
||||
typeMod = @move.move.pbCalcTypeMod(calc_type, @user.battler, @target.battler)
|
||||
return true if @move.move.pbDamagingMove? && Effectiveness.ineffective?(typeMod)
|
||||
# Dark-type immunity to moves made faster by Prankster
|
||||
return true if Settings::MECHANICS_GENERATION >= 7 && @user.has_active_ability?(:PRANKSTER) &&
|
||||
@target.has_type?(:DARK) && @target.opposes?(@user)
|
||||
return true if Settings::MECHANICS_GENERATION >= 7 && @move.statusMove? &&
|
||||
@user.has_active_ability?(:PRANKSTER) && @target.has_type?(:DARK) && @target.opposes?(@user)
|
||||
# Airborne-based immunity to Ground moves
|
||||
return true if @move.damagingMove? && calc_type == :GROUND &&
|
||||
@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.
|
||||
# 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
|
||||
score = MOVE_BASE_SCORE
|
||||
# Scores for each target in turn
|
||||
@@ -170,32 +171,17 @@ class Battle::AI
|
||||
# Get a score for the move against each target in turn
|
||||
targets.each do |target|
|
||||
set_up_move_check_target(target)
|
||||
# Predict whether the move will fail against the target
|
||||
if @trainer.has_skill_flag?("PredictMoveFailure")
|
||||
next if pbPredictMoveFailureAgainstTarget
|
||||
end
|
||||
t_score = pbGetMoveScoreAgainstTarget
|
||||
next if t_score < 0
|
||||
score += t_score
|
||||
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
|
||||
# Check if any targets were affected
|
||||
if affected_targets == 0
|
||||
if @trainer.has_skill_flag?("PredictMoveFailure")
|
||||
return MOVE_FAIL_SCORE if !@move.move.worksWithNoTargets?
|
||||
score = MOVE_USELESS_SCORE
|
||||
else
|
||||
score = MOVE_BASE_SCORE
|
||||
end
|
||||
if affected_targets == 0 && @trainer.has_skill_flag?("PredictMoveFailure")
|
||||
return MOVE_FAIL_SCORE if !@move.move.worksWithNoTargets?
|
||||
score = MOVE_USELESS_SCORE
|
||||
else
|
||||
affected_targets = 1 if affected_targets == 0 # To avoid dividing by 0
|
||||
# TODO: Can this accounting for multiple targets be improved somehow?
|
||||
score /= affected_targets # Average the score against multiple targets
|
||||
# Bonus for affecting multiple targets
|
||||
@@ -220,6 +206,46 @@ class Battle::AI
|
||||
return score
|
||||
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
|
||||
# scores for each move. Moves with higher scores are more likely to be chosen.
|
||||
|
||||
@@ -1,88 +1,100 @@
|
||||
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)
|
||||
# Discard status move/don't prefer damaging move if user has Contrary
|
||||
if !@battle.moldBreaker && @user.has_active_ability?(:CONTRARY)
|
||||
return (@move.statusMove?) ? MOVE_USELESS_SCORE : score - 20
|
||||
def get_score_for_target_stat_raise(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
|
||||
# Don't make score changes if user will faint from EOR damage
|
||||
if @user.rough_end_of_round_damage > @user.hp
|
||||
return (@move.statusMove?) ? MOVE_USELESS_SCORE : score
|
||||
# Discard status move/don't prefer damaging move if target has Contrary
|
||||
# TODO: Maybe this should return get_score_for_target_stat_drop if Contrary
|
||||
# applies and desire_mult < 1.
|
||||
if !@battle.moldBreaker && target.has_active_ability?(:CONTRARY) && desire_mult > 1
|
||||
return (whole_effect) ? MOVE_USELESS_SCORE : score - 20
|
||||
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
|
||||
if !@user.check_for_move { |m| m.function == "PowerHigherWithUserPositiveStatStages" }
|
||||
if !target.check_for_move { |m| m.function == "PowerHigherWithUserPositiveStatStages" }
|
||||
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)
|
||||
end
|
||||
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
|
||||
stat_changes = []
|
||||
@move.move.statUp.each_with_index do |stat, idx|
|
||||
real_stat_changes = []
|
||||
stat_changes.each_with_index do |stat, idx|
|
||||
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
|
||||
increment = @move.move.statUp[idx + 1]
|
||||
increment = stat_changes[idx + 1]
|
||||
if @move.function == "RaiseUserAtkSpAtk1Or2InSun"
|
||||
increment = 1
|
||||
increment = 2 if [:Sun, :HarshSun].include?(@user.battler.effectiveWeather)
|
||||
increment = 2 if [:Sun, :HarshSun].include?(target.battler.effectiveWeather)
|
||||
end
|
||||
increment *= 2 if !@battle.moldBreaker && @user.has_active_ability?(:SIMPLE)
|
||||
increment = [increment, 6 - @user.stages[stat]].min # The actual stages gained
|
||||
increment *= 2 if !@battle.moldBreaker && target.has_active_ability?(:SIMPLE)
|
||||
increment = [increment, 6 - target.stages[stat]].min # The actual stages gained
|
||||
# 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
|
||||
# Discard move if it can't raise any stats
|
||||
if stat_changes.length == 0
|
||||
# TODO: Have a parameter that decides whether to reduce the score here
|
||||
# (for moves where this is just part of the effect).
|
||||
return (@move.statusMove?) ? MOVE_USELESS_SCORE : score
|
||||
if real_stat_changes.length == 0
|
||||
return (whole_effect) ? MOVE_USELESS_SCORE : score
|
||||
end
|
||||
|
||||
# Make score changes based on the general concept of raising stats at all
|
||||
score = get_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
|
||||
# raised
|
||||
stat_changes.each do |change|
|
||||
score = get_user_stat_raise_score_one(score, change[0], change[1])
|
||||
real_stat_changes.each do |change|
|
||||
score = get_target_stat_raise_score_one(score, target, change[0], change[1], desire_mult)
|
||||
end
|
||||
|
||||
return score
|
||||
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,
|
||||
# i.e. CategoryDependsOnHigherDamagePoisonTarget and
|
||||
# CategoryDependsOnHigherDamageIgnoreTargetAbility.
|
||||
#=============================================================================
|
||||
def stat_raise_worthwhile?(stat)
|
||||
return false if !@user.battler.pbCanRaiseStatStage?(stat, @user.battler, @move.move)
|
||||
# Check if user won't benefit from the stat being raised
|
||||
# TODO: Exception if user knows Baton Pass/Stored Power?
|
||||
def stat_raise_worthwhile?(target, stat)
|
||||
return false if !target.battler.pbCanRaiseStatStage?(stat, @user.battler, @move.move)
|
||||
# Check if target won't benefit from the stat being raised
|
||||
# TODO: Exception if target knows Baton Pass/Stored Power?
|
||||
case stat
|
||||
when :ATTACK
|
||||
return false if !@user.check_for_move { |m| m.physicalMove?(m.type) &&
|
||||
m.function != "UseUserDefenseInsteadOfUserAttack" &&
|
||||
m.function != "UseTargetAttackInsteadOfUserAttack" }
|
||||
return false if !target.check_for_move { |m| m.physicalMove?(m.type) &&
|
||||
m.function != "UseUserDefenseInsteadOfUserAttack" &&
|
||||
m.function != "UseTargetAttackInsteadOfUserAttack" }
|
||||
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) ||
|
||||
m.function == "UseTargetDefenseInsteadOfTargetSpDef" }
|
||||
end
|
||||
return false
|
||||
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
|
||||
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) &&
|
||||
m.function != "UseTargetDefenseInsteadOfTargetSpDef" }
|
||||
end
|
||||
@@ -92,9 +104,9 @@ class Battle::AI
|
||||
"PowerHigherWithUserFasterThanTarget",
|
||||
"PowerHigherWithUserPositiveStatStages"
|
||||
]
|
||||
if !@user.check_for_move { |m| moves_that_prefer_high_speed.include?(m.function) }
|
||||
each_foe_battler(@user.side) do |b, i|
|
||||
return true if b.faster_than?(@user)
|
||||
if !target.check_for_move { |m| moves_that_prefer_high_speed.include?(m.function) }
|
||||
each_foe_battler(target.side) do |b, i|
|
||||
return true if b.faster_than?(target)
|
||||
end
|
||||
return false
|
||||
end
|
||||
@@ -107,27 +119,35 @@ class Battle::AI
|
||||
#=============================================================================
|
||||
# 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] }
|
||||
# TODO: Just return if foe is predicted to use a phazing move (one that
|
||||
# switches the user out).
|
||||
# TODO: Don't prefer if foe is faster than user and is predicted to deal
|
||||
# TODO: Just return if the target's foe is predicted to use a phazing move
|
||||
# (one that switches the target out).
|
||||
# TODO: Don't prefer if foe is faster than target and is predicted to deal
|
||||
# lethal damage.
|
||||
# TODO: Don't prefer if foe is slower than user but is predicted to be able
|
||||
# to 2HKO user.
|
||||
# TODO: Prefer if foe is semi-invulnerable and user is faster (can't hit
|
||||
# TODO: Don't prefer if foe is slower than target but is predicted to be
|
||||
# able to 2HKO the target.
|
||||
# TODO: Prefer if foe is semi-invulnerable and target is faster (can't hit
|
||||
# the foe anyway).
|
||||
|
||||
# Prefer if move is a status move and it's the user's first/second turn
|
||||
if @user.turnCount < 2 && @move.statusMove?
|
||||
score += total_increment * 4
|
||||
score += total_increment * desire_mult * 4
|
||||
end
|
||||
|
||||
# Prefer if user is at high HP, don't prefer if user is at low HP
|
||||
if @user.hp >= @user.totalhp * 0.7
|
||||
score += 4 * total_increment
|
||||
if target.index != @user.index
|
||||
if @user.hp >= @user.totalhp * 0.7
|
||||
score += total_increment * desire_mult * 3
|
||||
else
|
||||
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 * ((100 * @user.hp / @user.totalhp) - 50) / 4 # +5 to -12 per stage
|
||||
score += total_increment * desire_mult * ((100 * target.hp / target.totalhp) - 50) / 4 # +5 to -12 per stage
|
||||
end
|
||||
|
||||
# TODO: Look at abilities that trigger upon stat raise. There are none.
|
||||
@@ -225,7 +245,7 @@ class Battle::AI
|
||||
#=============================================================================
|
||||
# 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
|
||||
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]
|
||||
@@ -233,97 +253,95 @@ class Battle::AI
|
||||
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]
|
||||
end
|
||||
old_stage = @user.stages[stat]
|
||||
old_stage = target.stages[stat]
|
||||
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 -= 1
|
||||
inc_mult *= desire_mult
|
||||
# Stat-based score changes
|
||||
case stat
|
||||
when :ATTACK
|
||||
# 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
|
||||
score -= 20
|
||||
score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
|
||||
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
|
||||
score += inc * inc_mult
|
||||
score += 8 * inc_mult if @user.hp == @user.totalhp
|
||||
end
|
||||
|
||||
when :DEFENSE
|
||||
# Modify score depending on current stat stage
|
||||
if old_stage >= 2
|
||||
score -= 20
|
||||
score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
|
||||
else
|
||||
score += 10 * inc_mult
|
||||
score += 8 * inc_mult if @user.hp == @user.totalhp
|
||||
end
|
||||
|
||||
when :SPECIAL_ATTACK
|
||||
# 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
|
||||
score -= 20
|
||||
score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
|
||||
else
|
||||
has_physical_moves = @user.check_for_move { |m| m.physicalMove?(m.type) &&
|
||||
m.function != "UseUserDefenseInsteadOfUserAttack" &&
|
||||
m.function != "UseTargetAttackInsteadOfUserAttack" }
|
||||
has_physical_moves = target.check_for_move { |m| m.physicalMove?(m.type) &&
|
||||
m.function != "UseUserDefenseInsteadOfUserAttack" &&
|
||||
m.function != "UseTargetAttackInsteadOfUserAttack" }
|
||||
inc = (has_physical_moves) ? 10 : 20
|
||||
score += inc * inc_mult
|
||||
score += 8 * inc_mult if @user.hp == @user.totalhp
|
||||
end
|
||||
|
||||
when :SPECIAL_DEFENSE
|
||||
# Modify score depending on current stat stage
|
||||
if old_stage >= 2
|
||||
score -= 20
|
||||
score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
|
||||
else
|
||||
score += 10 * inc_mult
|
||||
score += 8 * inc_mult if @user.hp == @user.totalhp
|
||||
end
|
||||
|
||||
when :SPEED
|
||||
# Prefer if user is slower than a foe
|
||||
# TODO: Don't prefer if the user is too much slower than any foe that it
|
||||
# Prefer if target is slower than a foe
|
||||
# TODO: Don't prefer if the target is too much slower than any foe that it
|
||||
# can't catch up.
|
||||
each_foe_battler(@user.side) do |b, i|
|
||||
next if @user.faster_than?(b)
|
||||
each_foe_battler(target.side) do |b, i|
|
||||
next if target.faster_than?(b)
|
||||
score += 15 * inc_mult
|
||||
break
|
||||
end
|
||||
# TODO: Prefer if the user is able to cause flinching (moves that flinch,
|
||||
# or has King's Rock/Stench).
|
||||
# Prefer if the user has Electro Ball or Power Trip/Stored Power
|
||||
# TODO: Prefer if the target is able to cause flinching (moves that
|
||||
# flinch, or has King's Rock/Stench).
|
||||
# Prefer if the target has Electro Ball or Power Trip/Stored Power
|
||||
moves_that_prefer_high_speed = [
|
||||
"PowerHigherWithUserFasterThanTarget",
|
||||
"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
|
||||
end
|
||||
# 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" }
|
||||
score -= 8 * inc_mult
|
||||
end
|
||||
# Don't prefer if user has Speed Boost (will be gaining Speed anyway)
|
||||
score -= 20 if @user.has_active_ability?(:SPEEDBOOST)
|
||||
# Don't prefer if target has Speed Boost (will be gaining Speed anyway)
|
||||
if target.has_active_ability?(:SPEEDBOOST)
|
||||
score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
|
||||
end
|
||||
|
||||
when :ACCURACY
|
||||
# Modify score depending on current stat stage
|
||||
if old_stage >= 2
|
||||
score -= 20
|
||||
score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
|
||||
else
|
||||
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)
|
||||
min_accuracy = m.accuracy if m.accuracy < min_accuracy
|
||||
end
|
||||
min_accuracy = min_accuracy * stage_mul[old_stage] / stage_div[old_stage]
|
||||
if min_accuracy < 90
|
||||
score += 10 * inc_mult
|
||||
score += 8 * inc_mult if @user.hp == @user.totalhp
|
||||
end
|
||||
end
|
||||
|
||||
@@ -331,29 +349,28 @@ class Battle::AI
|
||||
# Prefer if a foe will (probably) take damage at the end of the round
|
||||
# TODO: Should this take into account EOR healing, one-off damage and
|
||||
# damage-causing effects that wear off naturally (like Sea of Fire)?
|
||||
# TODO: Emerald AI also prefers if user is rooted via Ingrain.
|
||||
each_foe_battler(@user.side) do |b, i|
|
||||
# TODO: Emerald AI also prefers if target is rooted via Ingrain.
|
||||
each_foe_battler(target.side) do |b, i|
|
||||
eor_damage = b.rough_end_of_round_damage
|
||||
score += 60 * eor_damage / b.totalhp if eor_damage > 0
|
||||
next if eor_damage <= 0
|
||||
end
|
||||
# Modify score depending on current stat stage
|
||||
if old_stage >= 2
|
||||
score -= 20
|
||||
score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
|
||||
else
|
||||
score += 10 * (2 - old_stage) * inc_mult
|
||||
score += 8 * inc_mult if @user.hp == @user.totalhp
|
||||
score += 10 * inc_mult
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Prefer if user has Stored Power
|
||||
if @user.check_for_move { |m| m.function == "PowerHigherWithUserPositiveStatStages" }
|
||||
score += 5 * pos_change
|
||||
# Prefer if target has Stored Power
|
||||
if target.check_for_move { |m| m.function == "PowerHigherWithUserPositiveStatStages" }
|
||||
score += 5 * increment * desire_mult
|
||||
end
|
||||
# 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" }
|
||||
score -= 5 * pos_change
|
||||
score -= 5 * increment * desire_mult
|
||||
end
|
||||
|
||||
return score
|
||||
@@ -363,9 +380,6 @@ class Battle::AI
|
||||
mini_score = 1.0
|
||||
case stat
|
||||
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
|
||||
# it won't be hurt by weather
|
||||
# if @user.hp == @user.totalhp &&
|
||||
@@ -567,55 +581,67 @@ class Battle::AI
|
||||
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).
|
||||
# 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.
|
||||
#=============================================================================
|
||||
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
|
||||
if !@battle.moldBreaker && @target.has_active_ability?(:CONTRARY)
|
||||
return (@move.statusMove?) ? MOVE_USELESS_SCORE : score - 20
|
||||
# TODO: Maybe this should return get_score_for_target_stat_raise if Contrary
|
||||
# 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
|
||||
# Don't make score changes if target will faint from EOR damage
|
||||
if @target.rough_end_of_round_damage > @target.hp
|
||||
return (@move.statusMove?) ? MOVE_USELESS_SCORE : score
|
||||
if target.rough_end_of_round_damage > target.hp
|
||||
return (whole_effect) ? MOVE_USELESS_SCORE : score
|
||||
end
|
||||
# Don't make score changes if allies have Unaware and can't make use of
|
||||
# target's lowered stat stages
|
||||
ally_is_aware = false
|
||||
each_foe_battler(@target.side) do |b, i|
|
||||
ally_is_aware = true if !b.has_active_ability?(:UNAWARE)
|
||||
# Don't make score changes if foes have Unaware and target can't make use of
|
||||
# its lowered stat stages
|
||||
foe_is_aware = false
|
||||
each_foe_battler(target.side) do |b, i|
|
||||
foe_is_aware = true if !b.has_active_ability?(:UNAWARE)
|
||||
end
|
||||
if !ally_is_aware
|
||||
return (@move.statusMove?) ? MOVE_USELESS_SCORE : score
|
||||
if !foe_is_aware
|
||||
return (whole_effect) ? MOVE_USELESS_SCORE : score
|
||||
end
|
||||
|
||||
# Figure out which stat raises can happen
|
||||
stat_changes = []
|
||||
@move.move.statDown.each_with_index do |stat, idx|
|
||||
real_stat_changes = []
|
||||
stat_changes.each_with_index do |stat, idx|
|
||||
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
|
||||
decrement = @move.move.statDown[idx + 1]
|
||||
decrement = stat_changes[idx + 1]
|
||||
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
|
||||
stat_changes.push([stat, decrement]) if decrement > 0
|
||||
real_stat_changes.push([stat, decrement]) if decrement > 0
|
||||
end
|
||||
# Discard move if it can't lower any stats
|
||||
if stat_changes.length == 0
|
||||
# TODO: Have a parameter that decides whether to reduce the score here
|
||||
# (for moves where this is just part of the effect).
|
||||
return (@move.statusMove?) ? MOVE_USELESS_SCORE : score
|
||||
if real_stat_changes.length == 0
|
||||
return (whole_effect) ? MOVE_USELESS_SCORE : score
|
||||
end
|
||||
|
||||
# Make score changes based on the general concept of lowering stats at all
|
||||
score = get_target_stat_drop_score_generic(score, 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
|
||||
# lowered
|
||||
stat_changes.each do |change|
|
||||
score = get_target_stat_drop_score_one(score, change[0], change[1])
|
||||
real_stat_changes.each do |change|
|
||||
score = get_target_stat_drop_score_one(score, target, change[0], change[1], desire_mult)
|
||||
end
|
||||
|
||||
return score
|
||||
@@ -628,24 +654,24 @@ class Battle::AI
|
||||
# CategoryDependsOnHigherDamageIgnoreTargetAbility.
|
||||
# TODO: Revisit this method as parts may need rewriting.
|
||||
#=============================================================================
|
||||
def stat_drop_worthwhile?(stat)
|
||||
return false if !@target.battler.pbCanLowerStatStage?(stat, @user.battler, @move.move)
|
||||
def stat_drop_worthwhile?(target, stat)
|
||||
return false if !target.battler.pbCanLowerStatStage?(stat, @user.battler, @move.move)
|
||||
# Check if target won't benefit from the stat being lowered
|
||||
case stat
|
||||
when :ATTACK
|
||||
return false if !@target.check_for_move { |m| m.physicalMove?(m.type) &&
|
||||
m.function != "UseUserDefenseInsteadOfUserAttack" &&
|
||||
m.function != "UseTargetAttackInsteadOfUserAttack" }
|
||||
return false if !target.check_for_move { |m| m.physicalMove?(m.type) &&
|
||||
m.function != "UseUserDefenseInsteadOfUserAttack" &&
|
||||
m.function != "UseTargetAttackInsteadOfUserAttack" }
|
||||
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) ||
|
||||
m.function == "UseTargetDefenseInsteadOfTargetSpDef" }
|
||||
end
|
||||
return false
|
||||
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
|
||||
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) &&
|
||||
m.function != "UseTargetDefenseInsteadOfTargetSpDef" }
|
||||
end
|
||||
@@ -655,9 +681,9 @@ class Battle::AI
|
||||
"PowerHigherWithUserFasterThanTarget",
|
||||
"PowerHigherWithUserPositiveStatStages"
|
||||
]
|
||||
if !@target.check_for_move { |m| moves_that_prefer_high_speed.include?(m.function) }
|
||||
each_foe_battler(@target.side) do |b, i|
|
||||
return true if !b.faster_than?(@target)
|
||||
if !target.check_for_move { |m| moves_that_prefer_high_speed.include?(m.function) }
|
||||
each_foe_battler(target.side) do |b, i|
|
||||
return true if !b.faster_than?(target)
|
||||
end
|
||||
return false
|
||||
end
|
||||
@@ -672,7 +698,7 @@ class Battle::AI
|
||||
# TODO: Revisit this method as parts may need rewriting.
|
||||
# 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] }
|
||||
# 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
|
||||
@@ -683,20 +709,22 @@ class Battle::AI
|
||||
|
||||
# Prefer if move is a status move and it's the user's first/second turn
|
||||
if @user.turnCount < 2 && @move.statusMove?
|
||||
score += total_decrement * 4
|
||||
score += total_decrement * desire_mult * 4
|
||||
end
|
||||
|
||||
# Prefer if user is at high HP, don't prefer if user is at low HP
|
||||
if @user.hp >= @user.totalhp * 0.7
|
||||
score += 3 * total_decrement
|
||||
else
|
||||
score += total_decrement * ((100 * @user.hp / @user.totalhp) - 50) / 6 # +3 to -8 per stage
|
||||
if target.index != @user.index
|
||||
if @user.hp >= @user.totalhp * 0.7
|
||||
score += total_decrement * desire_mult * 3
|
||||
else
|
||||
score += total_decrement * 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 += 3 * total_decrement
|
||||
if target.hp >= target.totalhp * 0.7
|
||||
score += total_decrement * desire_mult * 3
|
||||
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
|
||||
|
||||
# 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.
|
||||
# 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
|
||||
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]
|
||||
@@ -716,102 +744,99 @@ class Battle::AI
|
||||
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]
|
||||
end
|
||||
old_stage = @target.stages[stat]
|
||||
old_stage = target.stages[stat]
|
||||
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 -= 1
|
||||
dec_mult *= desire_mult
|
||||
# Stat-based score changes
|
||||
case stat
|
||||
when :ATTACK
|
||||
# Modify score depending on current stat stage
|
||||
# More strongly prefer if the target has no special moves
|
||||
if old_stage <= -2
|
||||
score -= 20
|
||||
score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
|
||||
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
|
||||
score += dec * (2 + old_stage) * dec_mult
|
||||
score += 4 * dec_mult if @user.hp == @user.totalhp
|
||||
score += dec * dec_mult
|
||||
end
|
||||
|
||||
when :DEFENSE
|
||||
# Modify score depending on current stat stage
|
||||
if old_stage <= -2
|
||||
score -= 20
|
||||
score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
|
||||
else
|
||||
score += 5 * (2 + old_stage) * dec_mult
|
||||
score += 4 * dec_mult if @user.hp == @user.totalhp
|
||||
score += 5 * dec_mult
|
||||
end
|
||||
|
||||
when :SPECIAL_ATTACK
|
||||
# Modify score depending on current stat stage
|
||||
# More strongly prefer if the target has no physical moves
|
||||
if old_stage <= -2
|
||||
score -= 20
|
||||
score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
|
||||
else
|
||||
has_physical_moves = @target.check_for_move { |m| m.physicalMove?(m.type) &&
|
||||
m.function != "UseUserDefenseInsteadOfUserAttack" &&
|
||||
m.function != "UseTargetAttackInsteadOfUserAttack" }
|
||||
has_physical_moves = target.check_for_move { |m| m.physicalMove?(m.type) &&
|
||||
m.function != "UseUserDefenseInsteadOfUserAttack" &&
|
||||
m.function != "UseTargetAttackInsteadOfUserAttack" }
|
||||
dec = (has_physical_moves) ? 5 : 10
|
||||
score += dec * (2 + old_stage) * dec_mult
|
||||
score += 4 * dec_mult if @user.hp == @user.totalhp
|
||||
score += dec * dec_mult
|
||||
end
|
||||
|
||||
when :SPECIAL_DEFENSE
|
||||
# Modify score depending on current stat stage
|
||||
if old_stage <= -2
|
||||
score -= 20
|
||||
score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
|
||||
else
|
||||
score += 5 * (2 + old_stage) * dec_mult
|
||||
score += 4 * dec_mult if @user.hp == @user.totalhp
|
||||
score += 5 * dec_mult
|
||||
end
|
||||
|
||||
when :SPEED
|
||||
# Prefer if target is faster than an ally
|
||||
# TODO: Don't prefer if the target is too much faster than any ally and
|
||||
# can't be brought slow enough.
|
||||
each_foe_battler(@target.side) do |b, i|
|
||||
next if b.faster_than?(@target)
|
||||
each_foe_battler(target.side) do |b, i|
|
||||
next if b.faster_than?(target)
|
||||
score += 15 * dec_mult
|
||||
break
|
||||
end
|
||||
# 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" }
|
||||
score += 8 * dec_mult
|
||||
end
|
||||
# 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
|
||||
# Modify score depending on current stat stage
|
||||
if old_stage <= -2
|
||||
score -= 20
|
||||
score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
|
||||
else
|
||||
score += 5 * (2 + old_stage) * dec_mult
|
||||
score += 4 * dec_mult if @user.hp == @user.totalhp
|
||||
score += 5 * dec_mult
|
||||
end
|
||||
# TODO: Prefer if target is poisoned/toxiced/Leech Seeded/cursed.
|
||||
|
||||
when :EVASION
|
||||
# Modify score depending on current stat stage
|
||||
if old_stage <= -2
|
||||
score -= 20
|
||||
score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
|
||||
else
|
||||
score += 5 * (2 + old_stage) * dec_mult
|
||||
score += 4 * dec_mult if @user.hp == @user.totalhp
|
||||
score += 5 * dec_mult
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Prefer if target has Stored Power
|
||||
if @target.check_for_move { |m| m.function == "PowerHigherWithUserPositiveStatStages" }
|
||||
score += 5 * decrement
|
||||
if target.check_for_move { |m| m.function == "PowerHigherWithUserPositiveStatStages" }
|
||||
score += 5 * decrement * desire_mult
|
||||
end
|
||||
# Don't prefer if any ally has Punishment
|
||||
each_foe_battler(@target.side) do |b, i|
|
||||
# Don't prefer if any foe has Punishment
|
||||
each_foe_battler(target.side) do |b, i|
|
||||
next if !b.check_for_move { |m| m.function == "PowerHigherWithTargetPositiveStatStages" }
|
||||
score -= 5 * decrement
|
||||
score -= 5 * decrement * desire_mult
|
||||
end
|
||||
|
||||
return score
|
||||
@@ -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
|
||||
@@ -10,7 +10,7 @@ Battle::AI::Handlers::MoveFailureCheck.add("RaiseUserAttack1",
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("RaiseUserAttack1",
|
||||
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",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
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
|
||||
}
|
||||
)
|
||||
@@ -58,7 +58,7 @@ Battle::AI::Handlers::MoveFailureCheck.add("MaxUserAttackLoseHalfOfTotalHP",
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("MaxUserAttackLoseHalfOfTotalHP",
|
||||
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
|
||||
score -= 80 * (1 - (user.hp.to_f / user.totalhp)) # 0 to -40
|
||||
next score
|
||||
@@ -81,7 +81,7 @@ Battle::AI::Handlers::MoveFailureCheck.copy("RaiseUserDefense1",
|
||||
"RaiseUserDefense1CurlUpUser")
|
||||
Battle::AI::Handlers::MoveEffectScore.add("RaiseUserDefense1CurlUpUser",
|
||||
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] &&
|
||||
user.check_for_move { |m| m.function == "MultiTurnAttackPowersUpEachTurn" }
|
||||
score += 10
|
||||
@@ -146,7 +146,7 @@ Battle::AI::Handlers::MoveFailureCheck.copy("RaiseUserSpDef1",
|
||||
"RaiseUserSpDef1PowerUpElectricMove")
|
||||
Battle::AI::Handlers::MoveEffectScore.add("RaiseUserSpDef1PowerUpElectricMove",
|
||||
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 }
|
||||
score += 10
|
||||
end
|
||||
@@ -193,7 +193,7 @@ Battle::AI::Handlers::MoveFailureCheck.copy("RaiseUserSpeed2",
|
||||
"RaiseUserSpeed2LowerUserWeight")
|
||||
Battle::AI::Handlers::MoveEffectScore.add("RaiseUserSpeed2LowerUserWeight",
|
||||
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?
|
||||
# TODO: Take into account weight-modifying items/abilities? This "> 1"
|
||||
# line can probably ignore them, but these moves' powers will change
|
||||
@@ -274,7 +274,7 @@ Battle::AI::Handlers::MoveFailureCheck.copy("RaiseUserEvasion2",
|
||||
"RaiseUserEvasion2MinimizeUser")
|
||||
Battle::AI::Handlers::MoveEffectScore.add("RaiseUserEvasion2MinimizeUser",
|
||||
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]
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
# Moves that do double damage and (in Gen 6+) have perfect accuracy
|
||||
@@ -368,7 +368,7 @@ Battle::AI::Handlers::MoveEffectScore.copy("RaiseUserAtkSpAtk1",
|
||||
"RaiseUserAtkSpAtk1Or2InSun")
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("LowerUserDefSpDef1RaiseUserAtkSpAtkSpd2",
|
||||
proc { |move, user, ai, battle|
|
||||
@@ -388,21 +388,9 @@ Battle::AI::Handlers::MoveFailureCheck.add("LowerUserDefSpDef1RaiseUserAtkSpAtkS
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("LowerUserDefSpDef1RaiseUserAtkSpAtkSpd2",
|
||||
proc { |score, move, user, ai, battle|
|
||||
score -= user.stages[:ATTACK] * 20
|
||||
score -= user.stages[:SPEED] * 20
|
||||
score -= user.stages[:SPECIAL_ATTACK] * 20
|
||||
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
|
||||
score = ai.get_score_for_target_stat_raise(score, user, move.move.statUp)
|
||||
next score if score == Battle::AI::MOVE_USELESS_SCORE
|
||||
next ai.get_score_for_target_stat_drop(score, user, move.move.statDown, false)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -463,7 +451,7 @@ Battle::AI::Handlers::MoveEffectScore.copy("RaiseUserAtkSpAtk1",
|
||||
"RaiseUserMainStats1")
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("RaiseUserMainStats1LoseThirdOfTotalHP",
|
||||
proc { |move, user, ai, battle|
|
||||
@@ -479,12 +467,12 @@ Battle::AI::Handlers::MoveFailureCheck.add("RaiseUserMainStats1LoseThirdOfTotalH
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("RaiseUserMainStats1LoseThirdOfTotalHP",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next 0 if !battle.moldBreaker && user.has_active_ability?(:CONTRARY)
|
||||
score += 30 if ai.trainer.high_skill? && user.hp >= user.totalhp * 0.75
|
||||
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
|
||||
# Score for stat increase
|
||||
score = ai.get_score_for_target_stat_raise(score, user, move.move.statUp)
|
||||
next score if score == Battle::AI::MOVE_USELESS_SCORE
|
||||
# Score for losing HP
|
||||
if user.hp <= user.totalhp * 0.75
|
||||
score -= 30 * (user.totalhp - user.hp) / user.totalhp
|
||||
end
|
||||
next score
|
||||
}
|
||||
@@ -507,15 +495,11 @@ Battle::AI::Handlers::MoveFailureCheck.add("RaiseUserMainStats1TrapUserInBattle"
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("RaiseUserMainStats1TrapUserInBattle",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next 0 if !battle.moldBreaker && user.has_active_ability?(:CONTRARY)
|
||||
if ai.trainer.high_skill?
|
||||
score -= 50 if user.hp <= user.totalhp / 2
|
||||
score += 30 if user.battler.trappedInBattle?
|
||||
end
|
||||
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
|
||||
# Score for stat increase
|
||||
score = ai.get_score_for_target_stat_raise(score, user, move.move.statUp)
|
||||
# Score for trapping user in battle
|
||||
if ai.trainer.medium_skill? && !user.battler.trappedInBattle?
|
||||
score -= 10 if user.hp <= user.totalhp / 2
|
||||
end
|
||||
next score
|
||||
}
|
||||
@@ -531,113 +515,85 @@ Battle::AI::Handlers::MoveEffectScore.add("StartRaiseUserAtk1WhenDamaged",
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("LowerUserAttack1",
|
||||
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",
|
||||
"LowerUserAttack2")
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("LowerUserDefense1",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next score + user.stages[:DEFENSE] * 10
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.copy("LowerUserAttack1",
|
||||
"LowerUserDefense1")
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.copy("LowerUserDefense1",
|
||||
"LowerUserDefense2")
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("LowerUserSpAtk1",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next score + user.stages[:SPECIAL_ATTACK] * 10
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.copy("LowerUserAttack1",
|
||||
"LowerUserSpAtk1")
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.copy("LowerUserSpAtk1",
|
||||
"LowerUserSpAtk2")
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("LowerUserSpDef1",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next score + user.stages[:SPECIAL_DEFENSE] * 10
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.copy("LowerUserDefense1",
|
||||
"LowerUserSpDef1")
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.copy("LowerUserSpDef1",
|
||||
"LowerUserSpDef2")
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("LowerUserSpeed1",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next score + user.stages[:SPECIAL_DEFENSE] * 10
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.copy("LowerUserAttack1",
|
||||
"LowerUserSpeed1")
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.copy("LowerUserSpeed1",
|
||||
"LowerUserSpeed2")
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("LowerUserAtkDef1",
|
||||
proc { |score, move, user, ai, battle|
|
||||
avg = user.stages[:ATTACK] * 10
|
||||
avg += user.stages[:DEFENSE] * 10
|
||||
next score + avg / 2
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.copy("LowerUserAttack1",
|
||||
"LowerUserAtkDef1")
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("LowerUserDefSpDef1",
|
||||
proc { |score, move, user, ai, battle|
|
||||
avg = user.stages[:DEFENSE] * 10
|
||||
avg += user.stages[:SPECIAL_DEFENSE] * 10
|
||||
next score + avg / 2
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.copy("LowerUserAttack1",
|
||||
"LowerUserDefSpDef1")
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("LowerUserDefSpDefSpd1",
|
||||
proc { |score, move, user, ai, battle|
|
||||
avg = user.stages[:DEFENSE] * 10
|
||||
avg += user.stages[:SPEED] * 10
|
||||
avg += user.stages[:SPECIAL_DEFENSE] * 10
|
||||
next score + (avg / 3).floor
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.copy("LowerUserAttack1",
|
||||
"LowerUserDefSpDefSpd1")
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
@@ -648,6 +604,11 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseTargetAttack1",
|
||||
!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.
|
||||
@@ -660,8 +621,12 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseTargetAttack2Confu
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetAttack2ConfuseTarget",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next Battle::AI::MOVE_USELESS_SCORE if !target.battler.pbCanConfuse?(user.battler, false)
|
||||
next score + 30 if target.stages[:ATTACK] < 0
|
||||
next Battle::AI::MOVE_USELESS_SCORE if !target.battler.pbCanConfuse?(user.battler, false, move.move)
|
||||
# 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",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next Battle::AI::MOVE_USELESS_SCORE if !target.battler.pbCanConfuse?(user.battler, false)
|
||||
next score + 30 if target.stages[:SPECIAL_ATTACK] < 0
|
||||
next Battle::AI::MOVE_USELESS_SCORE if !target.battler.pbCanConfuse?(user.battler, false, move.move)
|
||||
# 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",
|
||||
proc { |move, user, target, ai, battle|
|
||||
@@ -691,7 +660,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseTargetSpDef1",
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetSpDef1",
|
||||
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",
|
||||
proc { |move, user, target, ai, battle|
|
||||
@@ -731,10 +700,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseTargetAtkSpAtk2",
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetAtkSpAtk2",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next Battle::AI::MOVE_USELESS_SCORE if target.opposes?(user)
|
||||
next Battle::AI::MOVE_USELESS_SCORE if !battle.moldBreaker && target.has_active_ability?(:CONTRARY)
|
||||
score -= target.stages[:ATTACK] * 10
|
||||
score -= target.stages[:SPECIAL_ATTACK] * 10
|
||||
next score
|
||||
next ai.get_score_for_target_stat_raise(score, target, [:ATTACK, 2, :SPECIAL_ATTACK, 2])
|
||||
}
|
||||
)
|
||||
|
||||
@@ -749,7 +715,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("LowerTargetAttack1",
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("LowerTargetAttack1",
|
||||
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",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# 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
|
||||
if !target.effects[PBEffects::TarShot]
|
||||
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",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# 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 += 30 if target.pbOwnSide.effects[PBEffects::AuroraVeil] > 0 ||
|
||||
target.pbOwnSide.effects[PBEffects::Reflect] > 0 ||
|
||||
@@ -1089,13 +1055,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseAlliesAtkDef1",
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseAlliesAtkDef1",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
user.battler.allAllies.each do |b|
|
||||
if !battle.moldBreaker && b.hasActiveAbility?(:CONTRARY)
|
||||
score -= 40
|
||||
else
|
||||
score += 10
|
||||
score -= b.stages[:ATTACK] * 10
|
||||
score -= b.stages[:SPECIAL_ATTACK] * 10
|
||||
end
|
||||
score = ai.get_score_for_target_stat_raise(score, b, [:ATTACK, 1, :DEFENSE, 1])
|
||||
end
|
||||
next score
|
||||
}
|
||||
@@ -1125,13 +1085,10 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaisePlusMinusUserAndAl
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaisePlusMinusUserAndAlliesAtkSpAtk1",
|
||||
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|
|
||||
next if b.statStageAtMax?(:ATTACK) && b.statStageAtMax?(:SPECIAL_ATTACK)
|
||||
score -= b.stages[:ATTACK] * 10
|
||||
score -= b.stages[:SPECIAL_ATTACK] * 10
|
||||
score = ai.get_score_for_target_stat_raise(score, b, [:ATTACK, 1, :SPECIAL_ATTACK, 1], false)
|
||||
end
|
||||
score -= user.stages[:ATTACK] * 10
|
||||
score -= user.stages[:SPECIAL_ATTACK] * 10
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -1250,9 +1207,11 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTargetSwapAtkSpAtkSt
|
||||
target_attack = target.stages[:ATTACK]
|
||||
target_spatk = target.stages[:SPECIAL_ATTACK]
|
||||
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
|
||||
score += (target_attack - user_attack) * 10
|
||||
score += (target_spatk - user_spatk) * 10
|
||||
next score - 20 if user_attack + user_spatk >= target_attack + target_spatk
|
||||
# TODO: Check whether the user has physical/special moves that will be
|
||||
# stronger after the swap, and vice versa for the target?
|
||||
score += (target_attack - user_attack) * 5
|
||||
score += (target_spatk - user_spatk) * 5
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -1270,9 +1229,11 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTargetSwapDefSpDefSt
|
||||
target_def = target.stages[:DEFENSE]
|
||||
target_spdef = target.stages[:SPECIAL_DEFENSE]
|
||||
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
|
||||
score += (target_def - user_def) * 10
|
||||
score += (target_spdef - user_spdef) * 10
|
||||
next score - 20 if user_def + user_spdef >= target_def + target_spdef
|
||||
# TODO: Check whether the target has physical/special moves that will be
|
||||
# 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
|
||||
}
|
||||
)
|
||||
@@ -1493,15 +1454,13 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTargetAverageBaseDef
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTargetAverageHP",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if user.hp >= (user.hp + target.hp) / 2
|
||||
score -= 25
|
||||
else
|
||||
score += 25
|
||||
end
|
||||
next Battle::AI::MOVE_USELESS_SCORE if user.hp >= (user.hp + target.hp) / 2
|
||||
mult = (user.hp + target.hp) / (2.0 * user.hp)
|
||||
score += 10 * mult if mult >= 1.2
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
@@ -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",
|
||||
"FlinchTargetFailsIfUserNotAsleep")
|
||||
|
||||
|
||||
@@ -321,7 +321,7 @@ Battle::AI::Handlers::MoveFailureCheck.copy("RaiseUserAtkDef1",
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackRaiseUserSpAtkSpDefSpd2",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# 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 = Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttack",
|
||||
score, move, user, target, ai, battle)
|
||||
|
||||
@@ -440,11 +440,10 @@ Battle::AI::Handlers::MoveFailureCheck.add("UseRandomMoveFromUserParty",
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("UseRandomUserMoveIfAsleep",
|
||||
proc { |move, user, ai, battle|
|
||||
next true if !user.battler.asleep?
|
||||
will_fail = true
|
||||
user.battler.eachMoveWithIndex do |m, i|
|
||||
next if move.move.moveBlacklist.include?(m.function)
|
||||
|
||||
@@ -322,7 +322,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("TargetUsesItsLastUsedMo
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if !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)
|
||||
idxMove = -1
|
||||
target.battler.eachMoveWithIndex do |m, i|
|
||||
@@ -522,11 +522,6 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("AllBattlersLoseHalfHPUs
|
||||
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",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
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",
|
||||
proc { |score, move, user, ai, battle|
|
||||
score += 20 # Shadow moves are more preferable
|
||||
score -= 40
|
||||
next score
|
||||
next score - 40
|
||||
}
|
||||
)
|
||||
|
||||
@@ -551,7 +544,6 @@ Battle::AI::Handlers::MoveFailureCheck.copy("StartSunWeather",
|
||||
"StartShadowSkyWeather")
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartShadowSkyWeather",
|
||||
proc { |score, move, user, ai, battle|
|
||||
score += 20 # Shadow moves are more preferable
|
||||
next Battle::AI::MOVE_USELESS_SCORE if battle.pbCheckGlobalAbility(:AIRLOCK) ||
|
||||
battle.pbCheckGlobalAbility(:CLOUDNINE)
|
||||
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",
|
||||
proc { |score, move, user, ai, battle|
|
||||
score += 20 # Shadow moves are more preferable
|
||||
if user.pbOpposingSide.effects[PBEffects::AuroraVeil] > 0 ||
|
||||
user.pbOpposingSide.effects[PBEffects::Reflect] > 0 ||
|
||||
user.pbOpposingSide.effects[PBEffects::LightScreen] > 0 ||
|
||||
|
||||
@@ -46,8 +46,8 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:add_predicted_damage,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if move.damagingMove?
|
||||
dmg = move.rough_damage
|
||||
score += [15.0 * dmg / target.hp, 20].min
|
||||
score += 15 if dmg > target.hp * 1.1 # Predicted to KO the target
|
||||
score += [20.0 * dmg / target.hp, 25].min
|
||||
score += 10 if dmg > target.hp * 1.1 # Predicted to KO the target
|
||||
next score.to_i
|
||||
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.
|
||||
# TODO: Review score modifier.
|
||||
|
||||
@@ -267,57 +267,6 @@ class Battle::AI::AIBattler
|
||||
return ret
|
||||
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?
|
||||
|
||||
@@ -40,6 +40,10 @@ class Battle::AI::AIMove
|
||||
|
||||
#=============================================================================
|
||||
|
||||
def pbTarget(_user)
|
||||
return @move.pbTarget(_user)
|
||||
end
|
||||
|
||||
# Returns whether this move targets multiple battlers.
|
||||
def targets_multiple_battlers?
|
||||
user_battler = @ai_battler.battler
|
||||
|
||||
Reference in New Issue
Block a user