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.
|
# 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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
|
||||||
score = MOVE_BASE_SCORE
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
|
affected_targets = 1 if affected_targets == 0 # To avoid dividing by 0
|
||||||
# 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.
|
||||||
|
|||||||
@@ -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 @user.hp >= @user.totalhp * 0.7
|
if target.index != @user.index
|
||||||
score += 4 * total_increment
|
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
|
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
|
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 @user.hp >= @user.totalhp * 0.7
|
if target.index != @user.index
|
||||||
score += 3 * total_decrement
|
if @user.hp >= @user.totalhp * 0.7
|
||||||
else
|
score += total_decrement * desire_mult * 3
|
||||||
score += total_decrement * ((100 * @user.hp / @user.totalhp) - 50) / 6 # +3 to -8 per stage
|
else
|
||||||
|
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
|
||||||
@@ -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",
|
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
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 ||
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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?
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user