mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-06 06:01:46 +00:00
More work on the AI, refactored stat stage multipliers
This commit is contained in:
@@ -45,6 +45,13 @@ class Battle::Battler
|
||||
attr_accessor :canRestoreIceFace # Whether Hail started in the round
|
||||
attr_accessor :damageState
|
||||
|
||||
# These arrays should all have the same number of values in them
|
||||
STAT_STAGE_MULTIPLIERS = [2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8]
|
||||
STAT_STAGE_DIVISORS = [8, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2]
|
||||
ACC_EVA_STAGE_MULTIPLIERS = [3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7, 8, 9]
|
||||
ACC_EVA_STAGE_DIVISORS = [9, 8, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3]
|
||||
STAT_STAGE_MAXIMUM = 6 # Is also the minimum (-6)
|
||||
|
||||
#=============================================================================
|
||||
# Complex accessors
|
||||
#=============================================================================
|
||||
@@ -240,10 +247,8 @@ class Battle::Battler
|
||||
#=============================================================================
|
||||
def pbSpeed
|
||||
return 1 if fainted?
|
||||
stageMul = [2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8]
|
||||
stageDiv = [8, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2]
|
||||
stage = @stages[:SPEED] + 6
|
||||
speed = @speed * stageMul[stage] / stageDiv[stage]
|
||||
stage = @stages[:SPEED] + STAT_STAGE_MAXIMUM
|
||||
speed = @speed * STAT_STAGE_MULTIPLIERS[stage] / STAT_STAGE_DIVISORS[stage]
|
||||
speedMult = 1.0
|
||||
# Ability effects that alter calculated Speed
|
||||
if abilityActive?
|
||||
|
||||
@@ -3,7 +3,7 @@ class Battle::Battler
|
||||
# Increase stat stages
|
||||
#=============================================================================
|
||||
def statStageAtMax?(stat)
|
||||
return @stages[stat] >= 6
|
||||
return @stages[stat] >= STAT_STAGE_MAXIMUM
|
||||
end
|
||||
|
||||
def pbCanRaiseStatStage?(stat, user = nil, move = nil, showFailMsg = false, ignoreContrary = false)
|
||||
@@ -33,7 +33,7 @@ class Battle::Battler
|
||||
increment *= 2 if hasActiveAbility?(:SIMPLE)
|
||||
end
|
||||
# Change the stat stage
|
||||
increment = [increment, 6 - @stages[stat]].min
|
||||
increment = [increment, STAT_STAGE_MAXIMUM - @stages[stat]].min
|
||||
if increment > 0
|
||||
stat_name = GameData::Stat.get(stat).name
|
||||
new = @stages[stat] + increment
|
||||
@@ -117,7 +117,7 @@ class Battle::Battler
|
||||
# Decrease stat stages
|
||||
#=============================================================================
|
||||
def statStageAtMin?(stat)
|
||||
return @stages[stat] <= -6
|
||||
return @stages[stat] <= -STAT_STAGE_MAXIMUM
|
||||
end
|
||||
|
||||
def pbCanLowerStatStage?(stat, user = nil, move = nil, showFailMsg = false,
|
||||
@@ -183,7 +183,7 @@ class Battle::Battler
|
||||
increment *= 2 if hasActiveAbility?(:SIMPLE)
|
||||
end
|
||||
# Change the stat stage
|
||||
increment = [increment, 6 + @stages[stat]].min
|
||||
increment = [increment, STAT_STAGE_MAXIMUM + @stages[stat]].min
|
||||
if increment > 0
|
||||
stat_name = GameData::Stat.get(stat).name
|
||||
new = @stages[stat] - increment
|
||||
|
||||
@@ -101,10 +101,11 @@ class Battle::Move
|
||||
# Check if move can't miss
|
||||
return true if modifiers[:base_accuracy] == 0
|
||||
# Calculation
|
||||
accStage = [[modifiers[:accuracy_stage], -6].max, 6].min + 6
|
||||
evaStage = [[modifiers[:evasion_stage], -6].max, 6].min + 6
|
||||
stageMul = [3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7, 8, 9]
|
||||
stageDiv = [9, 8, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3]
|
||||
max_stage = Battle::Battler::STAT_STAGE_MAXIMUM
|
||||
accStage = [[modifiers[:accuracy_stage], -max_stage].max, max_stage].min + max_stage
|
||||
evaStage = [[modifiers[:evasion_stage], -max_stage].max, max_stage].min + max_stage
|
||||
stageMul = Battle::Battler::ACC_EVA_STAGE_MULTIPLIERS
|
||||
stageDiv = Battle::Battler::ACC_EVA_STAGE_DIVISORS
|
||||
accuracy = 100.0 * stageMul[accStage] / stageDiv[accStage]
|
||||
evasion = 100.0 * stageMul[evaStage] / stageDiv[evaStage]
|
||||
accuracy = (accuracy * modifiers[:accuracy_multiplier]).round
|
||||
@@ -226,13 +227,13 @@ class Battle::Move
|
||||
def pbModifyDamage(damageMult, user, target); return damageMult; end
|
||||
|
||||
def pbGetAttackStats(user, target)
|
||||
return user.spatk, user.stages[:SPECIAL_ATTACK] + 6 if specialMove?
|
||||
return user.attack, user.stages[:ATTACK] + 6
|
||||
return user.spatk, user.stages[:SPECIAL_ATTACK] + Battle::Battler::STAT_STAGE_MAXIMUM if specialMove?
|
||||
return user.attack, user.stages[:ATTACK] + Battle::Battler::STAT_STAGE_MAXIMUM
|
||||
end
|
||||
|
||||
def pbGetDefenseStats(user, target)
|
||||
return target.spdef, target.stages[:SPECIAL_DEFENSE] + 6 if specialMove?
|
||||
return target.defense, target.stages[:DEFENSE] + 6
|
||||
return target.spdef, target.stages[:SPECIAL_DEFENSE] + Battle::Battler::STAT_STAGE_MAXIMUM if specialMove?
|
||||
return target.defense, target.stages[:DEFENSE] + Battle::Battler::STAT_STAGE_MAXIMUM
|
||||
end
|
||||
|
||||
def pbCalcDamage(user, target, numTargets = 1)
|
||||
@@ -241,8 +242,9 @@ class Battle::Move
|
||||
target.damageState.calcDamage = 1
|
||||
return
|
||||
end
|
||||
stageMul = [2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8]
|
||||
stageDiv = [8, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2]
|
||||
max_stage = Battle::Battler::STAT_STAGE_MAXIMUM
|
||||
stageMul = Battle::Battler::STAT_STAGE_MULTIPLIERS
|
||||
stageDiv = Battle::Battler::STAT_STAGE_DIVISORS
|
||||
# Get the move's type
|
||||
type = @calcType # nil is treated as physical
|
||||
# Calculate whether this hit deals critical damage
|
||||
@@ -252,13 +254,13 @@ class Battle::Move
|
||||
# Calculate user's attack stat
|
||||
atk, atkStage = pbGetAttackStats(user, target)
|
||||
if !target.hasActiveAbility?(:UNAWARE) || @battle.moldBreaker
|
||||
atkStage = 6 if target.damageState.critical && atkStage < 6
|
||||
atkStage = max_stage if target.damageState.critical && atkStage < max_stage
|
||||
atk = (atk.to_f * stageMul[atkStage] / stageDiv[atkStage]).floor
|
||||
end
|
||||
# Calculate target's defense stat
|
||||
defense, defStage = pbGetDefenseStats(user, target)
|
||||
if !user.hasActiveAbility?(:UNAWARE)
|
||||
defStage = 6 if target.damageState.critical && defStage > 6
|
||||
defStage = max_stage if target.damageState.critical && defStage > max_stage
|
||||
defense = (defense.to_f * stageMul[defStage] / stageDiv[defStage]).floor
|
||||
end
|
||||
# Calculate all multiplier effects
|
||||
|
||||
@@ -94,14 +94,14 @@ class Battle::Move::MaxUserAttackLoseHalfOfTotalHP < Battle::Move
|
||||
hpLoss = [user.totalhp / 2, 1].max
|
||||
user.pbReduceHP(hpLoss, false, false)
|
||||
if user.hasActiveAbility?(:CONTRARY)
|
||||
user.stages[@statUp[0]] = -6
|
||||
user.stages[@statUp[0]] = -Battle::Battler::STAT_STAGE_MAXIMUM
|
||||
user.statsLoweredThisRound = true
|
||||
user.statsDropped = true
|
||||
@battle.pbCommonAnimation("StatDown", user)
|
||||
@battle.pbDisplay(_INTL("{1} cut its own HP and minimized its {2}!",
|
||||
user.pbThis, GameData::Stat.get(@statUp[0]).name))
|
||||
else
|
||||
user.stages[@statUp[0]] = 6
|
||||
user.stages[@statUp[0]] = Battle::Battler::STAT_STAGE_MAXIMUM
|
||||
user.statsRaisedThisRound = true
|
||||
@battle.pbCommonAnimation("StatUp", user)
|
||||
@battle.pbDisplay(_INTL("{1} cut its own HP and maximized its {2}!",
|
||||
|
||||
@@ -1121,17 +1121,18 @@ class Battle::Move::CategoryDependsOnHigherDamagePoisonTarget < Battle::Move::Po
|
||||
|
||||
def pbOnStartUse(user, targets)
|
||||
target = targets[0]
|
||||
stageMul = [2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8]
|
||||
stageDiv = [8, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2]
|
||||
max_stage = Battle::Battler::STAT_STAGE_MAXIMUM
|
||||
stageMul = Battle::Battler::STAT_STAGE_MULTIPLIERS
|
||||
stageDiv = Battle::Battler::STAT_STAGE_DIVISORS
|
||||
# Calculate user's effective attacking values
|
||||
attack_stage = user.stages[:ATTACK] + 6
|
||||
attack_stage = user.stages[:ATTACK] + max_stage
|
||||
real_attack = (user.attack.to_f * stageMul[attack_stage] / stageDiv[attack_stage]).floor
|
||||
special_attack_stage = user.stages[:SPECIAL_ATTACK] + 6
|
||||
special_attack_stage = user.stages[:SPECIAL_ATTACK] + max_stage
|
||||
real_special_attack = (user.spatk.to_f * stageMul[special_attack_stage] / stageDiv[special_attack_stage]).floor
|
||||
# Calculate target's effective defending values
|
||||
defense_stage = target.stages[:DEFENSE] + 6
|
||||
defense_stage = target.stages[:DEFENSE] + max_stage
|
||||
real_defense = (target.defense.to_f * stageMul[defense_stage] / stageDiv[defense_stage]).floor
|
||||
special_defense_stage = target.stages[:SPECIAL_DEFENSE] + 6
|
||||
special_defense_stage = target.stages[:SPECIAL_DEFENSE] + max_stage
|
||||
real_special_defense = (target.spdef.to_f * stageMul[special_defense_stage] / stageDiv[special_defense_stage]).floor
|
||||
# Perform simple damage calculation
|
||||
physical_damage = real_attack.to_f / real_defense
|
||||
@@ -1166,13 +1167,14 @@ class Battle::Move::CategoryDependsOnHigherDamageIgnoreTargetAbility < Battle::M
|
||||
|
||||
def pbOnStartUse(user, targets)
|
||||
# Calculate user's effective attacking value
|
||||
stageMul = [2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8]
|
||||
stageDiv = [8, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2]
|
||||
max_stage = Battle::Battler::STAT_STAGE_MAXIMUM
|
||||
stageMul = Battle::Battler::STAT_STAGE_MULTIPLIERS
|
||||
stageDiv = Battle::Battler::STAT_STAGE_DIVISORS
|
||||
atk = user.attack
|
||||
atkStage = user.stages[:ATTACK] + 6
|
||||
atkStage = user.stages[:ATTACK] + max_stage
|
||||
realAtk = (atk.to_f * stageMul[atkStage] / stageDiv[atkStage]).floor
|
||||
spAtk = user.spatk
|
||||
spAtkStage = user.stages[:SPECIAL_ATTACK] + 6
|
||||
spAtkStage = user.stages[:SPECIAL_ATTACK] + max_stage
|
||||
realSpAtk = (spAtk.to_f * stageMul[spAtkStage] / stageDiv[spAtkStage]).floor
|
||||
# Determine move's category
|
||||
@calcCategory = (realAtk > realSpAtk) ? 0 : 1
|
||||
@@ -1187,7 +1189,7 @@ end
|
||||
#===============================================================================
|
||||
class Battle::Move::UseUserDefenseInsteadOfUserAttack < Battle::Move
|
||||
def pbGetAttackStats(user, target)
|
||||
return user.defense, user.stages[:DEFENSE] + 6
|
||||
return user.defense, user.stages[:DEFENSE] + Battle::Battler::STAT_STAGE_MAXIMUM
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1197,8 +1199,8 @@ end
|
||||
#===============================================================================
|
||||
class Battle::Move::UseTargetAttackInsteadOfUserAttack < Battle::Move
|
||||
def pbGetAttackStats(user, target)
|
||||
return target.spatk, target.stages[:SPECIAL_ATTACK] + 6 if specialMove?
|
||||
return target.attack, target.stages[:ATTACK] + 6
|
||||
return target.spatk, target.stages[:SPECIAL_ATTACK] + Battle::Battler::STAT_STAGE_MAXIMUM if specialMove?
|
||||
return target.attack, target.stages[:ATTACK] + Battle::Battler::STAT_STAGE_MAXIMUM
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1208,7 +1210,7 @@ end
|
||||
#===============================================================================
|
||||
class Battle::Move::UseTargetDefenseInsteadOfTargetSpDef < Battle::Move
|
||||
def pbGetDefenseStats(user, target)
|
||||
return target.defense, target.stages[:DEFENSE] + 6
|
||||
return target.defense, target.stages[:DEFENSE] + Battle::Battler::STAT_STAGE_MAXIMUM
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1264,7 +1266,7 @@ class Battle::Move::IgnoreTargetDefSpDefEvaStatStages < Battle::Move
|
||||
|
||||
def pbGetDefenseStats(user, target)
|
||||
ret1, _ret2 = super
|
||||
return ret1, 6 # Def/SpDef stat stage
|
||||
return ret1, Battle::Battler::STAT_STAGE_MAXIMUM # Def/SpDef stat stage
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -139,10 +139,11 @@ class Battle::Move::HealUserByTargetAttackLowerTargetAttack1 < Battle::Move
|
||||
|
||||
def pbEffectAgainstTarget(user, target)
|
||||
# Calculate target's effective attack value
|
||||
stageMul = [2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8]
|
||||
stageDiv = [8, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2]
|
||||
max_stage = Battle::Battler::STAT_STAGE_MAXIMUM
|
||||
stageMul = Battle::Battler::STAT_STAGE_MULTIPLIERS
|
||||
stageDiv = Battle::Battler::STAT_STAGE_DIVISORS
|
||||
atk = target.attack
|
||||
atkStage = target.stages[@statDown[0]] + 6
|
||||
atkStage = target.stages[@statDown[0]] + max_stage
|
||||
healAmt = (atk.to_f * stageMul[atkStage] / stageDiv[atkStage]).floor
|
||||
# Reduce target's Attack stat
|
||||
if target.pbCanLowerStatStage?(@statDown[0], user, self)
|
||||
|
||||
@@ -231,7 +231,6 @@ class Battle::AI
|
||||
if targets
|
||||
# Reset the base score for the move (each target will add its own score)
|
||||
score = 0
|
||||
# TODO: Distinguish between affected foes and affected allies?
|
||||
affected_targets = 0
|
||||
# Get a score for the move against each target in turn
|
||||
orig_move = @move.move # In case move is Mirror Move and changes depending on the target
|
||||
|
||||
@@ -62,7 +62,7 @@ class Battle::AI
|
||||
# Calculate amount that stat will be raised by
|
||||
increment = stat_changes[idx + 1]
|
||||
increment *= 2 if !fixed_change && !@battle.moldBreaker && target.has_active_ability?(:SIMPLE)
|
||||
increment = [increment, 6 - target.stages[stat]].min # The actual stages gained
|
||||
increment = [increment, Battle::Battler::STAT_STAGE_MAXIMUM - target.stages[stat]].min # The actual stages gained
|
||||
# Count this as a valid stat raise
|
||||
real_stat_changes.push([stat, increment]) if increment > 0
|
||||
end
|
||||
@@ -98,8 +98,10 @@ class Battle::AI
|
||||
if !fixed_change
|
||||
return false if !target.battler.pbCanRaiseStatStage?(stat, @user.battler, @move.move)
|
||||
end
|
||||
# TODO: Not worth it if target is predicted to switch out (except via Baton Pass).
|
||||
# Check if target won't benefit from the stat being raised
|
||||
# TODO: Exception if target knows Baton Pass/Stored Power?
|
||||
return true if target.has_move_with_function?("SwitchOutUserPassOnEffects",
|
||||
"PowerHigherWithUserPositiveStatStages")
|
||||
case stat
|
||||
when :ATTACK
|
||||
return false if !target.check_for_move { |m| m.physicalMove?(m.type) &&
|
||||
@@ -146,24 +148,18 @@ class Battle::AI
|
||||
#=============================================================================
|
||||
def get_target_stat_raise_score_generic(score, target, stat_changes, desire_mult = 1)
|
||||
total_increment = stat_changes.sum { |change| change[1] }
|
||||
# TODO: Just return if the target's foe is predicted to use a phazing move
|
||||
# (one that switches the target out).
|
||||
# TODO: Don't prefer if foe is faster than target and is predicted to deal
|
||||
# lethal damage.
|
||||
# TODO: Don't prefer if foe is slower than target but is predicted to be
|
||||
# able to 2HKO the target.
|
||||
# TODO: Prefer if foe is semi-invulnerable and target is faster (can't hit
|
||||
# the foe anyway).
|
||||
# Prefer if move is a status move and it's the user's first/second turn
|
||||
if @user.turnCount < 2 && @move.statusMove?
|
||||
score += total_increment * desire_mult * 5
|
||||
end
|
||||
# Prefer if user is at high HP, don't prefer if user is at low HP
|
||||
if target.index != @user.index
|
||||
score += total_increment * desire_mult * ((100 * @user.hp / @user.totalhp) - 50) / 8 # +6 to -6 per stage
|
||||
if @trainer.has_skill_flag?("HPAware")
|
||||
# Prefer if user is at high HP, don't prefer if user is at low HP
|
||||
if target.index != @user.index
|
||||
score += total_increment * desire_mult * ((100 * @user.hp / @user.totalhp) - 50) / 8 # +6 to -6 per stage
|
||||
end
|
||||
# Prefer if target is at high HP, don't prefer if target is at low HP
|
||||
score += total_increment * desire_mult * ((100 * target.hp / target.totalhp) - 50) / 8 # +6 to -6 per stage
|
||||
end
|
||||
# Prefer if target is at high HP, don't prefer if target is at low HP
|
||||
score += total_increment * desire_mult * ((100 * target.hp / target.totalhp) - 50) / 8 # +6 to -6 per stage
|
||||
# TODO: Look at abilities that trigger upon stat raise. There are none.
|
||||
return score
|
||||
end
|
||||
@@ -173,15 +169,16 @@ class Battle::AI
|
||||
#=============================================================================
|
||||
def get_target_stat_raise_score_one(score, target, stat, increment, desire_mult = 1)
|
||||
# Figure out how much the stat will actually change by
|
||||
stage_mul = [2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8]
|
||||
stage_div = [8, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2]
|
||||
max_stage = Battle::Battler::STAT_STAGE_MAXIMUM
|
||||
stage_mul = Battle::Battler::STAT_STAGE_MULTIPLIERS
|
||||
stage_div = Battle::Battler::STAT_STAGE_DIVISORS
|
||||
if [:ACCURACY, :EVASION].include?(stat)
|
||||
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_mul = Battle::Battler::ACC_EVA_STAGE_MULTIPLIERS
|
||||
stage_div = Battle::Battler::ACC_EVA_STAGE_DIVISORS
|
||||
end
|
||||
old_stage = target.stages[stat]
|
||||
new_stage = old_stage + increment
|
||||
inc_mult = (stage_mul[new_stage + 6].to_f * stage_div[old_stage + 6]) / (stage_div[new_stage + 6] * stage_mul[old_stage + 6])
|
||||
inc_mult = (stage_mul[new_stage + max_stage].to_f * stage_div[old_stage + max_stage]) / (stage_div[new_stage + max_stage] * stage_mul[old_stage + max_stage])
|
||||
inc_mult -= 1
|
||||
inc_mult *= desire_mult
|
||||
# Stat-based score changes
|
||||
@@ -304,7 +301,6 @@ class Battle::AI
|
||||
# inversion does not happen if the move could target a foe but is targeting an
|
||||
# ally, but only because it is inverted in def pbGetMoveScoreAgainstTarget
|
||||
# instead.
|
||||
# TODO: Revisit this method as parts may need rewriting.
|
||||
#=============================================================================
|
||||
def get_score_for_target_stat_drop(score, target, stat_changes, whole_effect = true,
|
||||
fixed_change = false, ignore_contrary = false)
|
||||
@@ -358,8 +354,8 @@ class Battle::AI
|
||||
end
|
||||
# Calculate amount that stat will be lowered by
|
||||
decrement = stat_changes[idx + 1]
|
||||
decrement *= 2 if !fixed_change && !@battle.moldBreaker && @user.has_active_ability?(:SIMPLE)
|
||||
decrement = [decrement, 6 + target.stages[stat]].min # The actual stages lost
|
||||
decrement *= 2 if !fixed_change && !@battle.moldBreaker && target.has_active_ability?(:SIMPLE)
|
||||
decrement = [decrement, Battle::Battler::STAT_STAGE_MAXIMUM + target.stages[stat]].min # The actual stages lost
|
||||
# Count this as a valid stat drop
|
||||
real_stat_changes.push([stat, decrement]) if decrement > 0
|
||||
end
|
||||
@@ -390,12 +386,12 @@ class Battle::AI
|
||||
# TODO: Make sure the move's actual damage category is taken into account,
|
||||
# i.e. CategoryDependsOnHigherDamagePoisonTarget and
|
||||
# CategoryDependsOnHigherDamageIgnoreTargetAbility.
|
||||
# TODO: Revisit this method as parts may need rewriting.
|
||||
#=============================================================================
|
||||
def stat_drop_worthwhile?(target, stat, fixed_change = false)
|
||||
if !fixed_change
|
||||
return false if !target.battler.pbCanLowerStatStage?(stat, @user.battler, @move.move)
|
||||
end
|
||||
# TODO: Not worth it if target is predicted to switch out (except via Baton Pass).
|
||||
# Check if target won't benefit from the stat being lowered
|
||||
case stat
|
||||
when :ATTACK
|
||||
@@ -437,46 +433,40 @@ class Battle::AI
|
||||
|
||||
#=============================================================================
|
||||
# Make score changes based on the general concept of lowering stats at all.
|
||||
# TODO: Revisit this method as parts may need rewriting.
|
||||
# TODO: All comments in this method may be inaccurate.
|
||||
#=============================================================================
|
||||
def get_target_stat_drop_score_generic(score, target, stat_changes, desire_mult = 1)
|
||||
total_decrement = stat_changes.sum { |change| change[1] }
|
||||
# TODO: Just return if target is predicted to switch out (except via Baton Pass).
|
||||
# TODO: Don't prefer if target is faster than user and is predicted to deal
|
||||
# lethal damage.
|
||||
# TODO: Don't prefer if target is slower than user but is predicted to be able
|
||||
# to 2HKO user.
|
||||
# TODO: Don't prefer if target is semi-invulnerable and user is faster.
|
||||
# Prefer if move is a status move and it's the user's first/second turn
|
||||
if @user.turnCount < 2 && @move.statusMove?
|
||||
score += total_decrement * desire_mult * 5
|
||||
end
|
||||
# Prefer if user is at high HP, don't prefer if user is at low HP
|
||||
if target.index != @user.index
|
||||
score += total_decrement * desire_mult * ((100 * @user.hp / @user.totalhp) - 50) / 8 # +6 to -6 per stage
|
||||
if @trainer.has_skill_flag?("HPAware")
|
||||
# Prefer if user is at high HP, don't prefer if user is at low HP
|
||||
if target.index != @user.index
|
||||
score += total_decrement * desire_mult * ((100 * @user.hp / @user.totalhp) - 50) / 8 # +6 to -6 per stage
|
||||
end
|
||||
# Prefer if target is at high HP, don't prefer if target is at low HP
|
||||
score += total_decrement * desire_mult * ((100 * target.hp / target.totalhp) - 50) / 8 # +6 to -6 per stage
|
||||
end
|
||||
# Prefer if target is at high HP, don't prefer if target is at low HP
|
||||
score += total_decrement * desire_mult * ((100 * target.hp / target.totalhp) - 50) / 8 # +6 to -6 per stage
|
||||
# TODO: Look at abilities that trigger upon stat lowering.
|
||||
return score
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Make score changes based on the lowering of a specific stat.
|
||||
# TODO: Revisit this method as parts may need rewriting.
|
||||
#=============================================================================
|
||||
def get_target_stat_drop_score_one(score, target, stat, decrement, desire_mult = 1)
|
||||
# Figure out how much the stat will actually change by
|
||||
stage_mul = [2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8]
|
||||
stage_div = [8, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2]
|
||||
max_stage = Battle::Battler::STAT_STAGE_MAXIMUM
|
||||
stage_mul = Battle::Battler::STAT_STAGE_MULTIPLIERS
|
||||
stage_div = Battle::Battler::STAT_STAGE_DIVISORS
|
||||
if [:ACCURACY, :EVASION].include?(stat)
|
||||
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_mul = Battle::Battler::ACC_EVA_STAGE_MULTIPLIERS
|
||||
stage_div = Battle::Battler::ACC_EVA_STAGE_DIVISORS
|
||||
end
|
||||
old_stage = target.stages[stat]
|
||||
new_stage = old_stage - decrement
|
||||
dec_mult = (stage_mul[old_stage + 6].to_f * stage_div[new_stage + 6]) / (stage_div[old_stage + 6] * stage_mul[new_stage + 6])
|
||||
dec_mult = (stage_mul[old_stage + max_stage].to_f * stage_div[new_stage + max_stage]) / (stage_div[old_stage + max_stage] * stage_mul[new_stage + max_stage])
|
||||
dec_mult -= 1
|
||||
dec_mult *= desire_mult
|
||||
# Stat-based score changes
|
||||
|
||||
@@ -1,27 +1,3 @@
|
||||
# TODO: Check all lingering effects to see if the AI needs to adjust itself
|
||||
# because of them.
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO:
|
||||
# => Don't prefer damaging move if it won't KO, user has Stance Change and
|
||||
# is in shield form, and user is slower than the target
|
||||
# => Check memory for past damage dealt by a target's non-high priority move,
|
||||
# and prefer move if user is slower than the target and another hit from
|
||||
# the same amount will KO the user
|
||||
# => Check memory for past damage dealt by a target's priority move, and don't
|
||||
# prefer the move if user is slower than the target and can't move faster
|
||||
# than it because of priority
|
||||
# => Check memory for whether target has previously used Quick Guard, and
|
||||
# don't prefer move if so
|
||||
|
||||
#===============================================================================
|
||||
#===============================================================================
|
||||
#===============================================================================
|
||||
#===============================================================================
|
||||
#===============================================================================
|
||||
|
||||
#===============================================================================
|
||||
# Don't prefer hitting a wild shiny Pokémon.
|
||||
#===============================================================================
|
||||
@@ -37,233 +13,78 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:shiny_target,
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# Adjust score based on how much damage it can deal.
|
||||
# Prefer the move even more if it's predicted to do enough damage to KO the
|
||||
# target.
|
||||
# TODO: Review score modifier.
|
||||
# => If target has previously used a move that will hurt the user by 30% of
|
||||
# its current HP or more, moreso don't prefer a status move.
|
||||
# => Include EOR damage in this?
|
||||
# => Prefer move if it will KO the target (moreso if user is slower than target)
|
||||
# Prefer Shadow moves (for flavour).
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:predicted_damage,
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:shadow_moves,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if move.damagingMove?
|
||||
dmg = move.rough_damage
|
||||
if move.rough_type == :SHADOW
|
||||
old_score = score
|
||||
score += ([25.0 * dmg / target.hp, 30].min).to_i
|
||||
PBDebug.log_score_change(score - old_score, "damaging move (predicted damage #{dmg} = #{100 * dmg / target.hp}% of target's HP)")
|
||||
if dmg > target.hp * 1.1 # Predicted to KO the target
|
||||
old_score = score
|
||||
score += 10
|
||||
PBDebug.log_score_change(score - old_score, "predicted to KO the target")
|
||||
end
|
||||
score += 10
|
||||
PBDebug.log_score_change(score - old_score, "prefer using a Shadow move")
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# Account for accuracy of move.
|
||||
# TODO: Review score modifier.
|
||||
# If user is frozen, prefer a move that can thaw the user.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:move_accuracy,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
acc = move.rough_accuracy.to_i
|
||||
if acc < 90
|
||||
Battle::AI::Handlers::GeneralMoveScore.add(:thawing_move_when_frozen,
|
||||
proc { |score, move, user, ai, battle|
|
||||
if ai.trainer.medium_skill? && user.status == :FROZEN
|
||||
old_score = score
|
||||
score -= (0.2 * (100 - acc)).to_i # -2 (89%) to -19 (1%)
|
||||
PBDebug.log_score_change(score - old_score, "accuracy (predicted #{acc}%)")
|
||||
if move.move.thawsUser?
|
||||
score += 20
|
||||
PBDebug.log_score_change(score - old_score, "move will thaw the user")
|
||||
elsif user.check_for_move { |m| m.thawsUser? }
|
||||
score -= 20 # Don't prefer this move if user knows another move that thaws
|
||||
PBDebug.log_score_change(score - old_score, "user knows another move will thaw it")
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# Don't prefer attacking the target if they'd be semi-invulnerable.
|
||||
# TODO: Review score modifier.
|
||||
# Prefer using a priority move if the user is slower than the target and...
|
||||
# - the user is at low HP, or
|
||||
# - the target is predicted to be knocked out by the move.
|
||||
# TODO: Less prefer a priority move if any foe knows Quick Guard?
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:target_semi_invulnerable,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# TODO: Also consider the move's priority compared to that of the move the
|
||||
# target is using.
|
||||
if move.rough_accuracy > 0 && user.faster_than?(target) &&
|
||||
(target.battler.semiInvulnerable? || target.effects[PBEffects::SkyDrop] >= 0)
|
||||
miss = true
|
||||
miss = false if user.has_active_ability?(:NOGUARD) || target.has_active_ability?(:NOGUARD)
|
||||
if ai.trainer.high_skill? && miss
|
||||
# Knows what can get past semi-invulnerability
|
||||
if target.effects[PBEffects::SkyDrop] >= 0 ||
|
||||
target.battler.inTwoTurnAttack?("TwoTurnAttackInvulnerableInSky",
|
||||
"TwoTurnAttackInvulnerableInSkyParalyzeTarget",
|
||||
"TwoTurnAttackInvulnerableInSkyTargetCannotAct")
|
||||
miss = false if move.move.hitsFlyingTargets?
|
||||
elsif target.battler.inTwoTurnAttack?("TwoTurnAttackInvulnerableUnderground")
|
||||
miss = false if move.move.hitsDiggingTargets?
|
||||
elsif target.battler.inTwoTurnAttack?("TwoTurnAttackInvulnerableUnderwater")
|
||||
miss = false if move.move.hitsDivingTargets?
|
||||
end
|
||||
end
|
||||
if miss
|
||||
Battle::AI::Handlers::GeneralMoveScore.add(:priority_move_against_faster_target,
|
||||
proc { |score, move, user, ai, battle|
|
||||
if ai.trainer.high_skill? && target.faster_than?(user) && move.rough_priority(user) > 0
|
||||
# User is at risk of being knocked out
|
||||
if ai.trainer.has_skill_flag?("HPAware") && user.hp < user.totalhp / 3
|
||||
old_score = score
|
||||
score = Battle::AI::MOVE_USELESS_SCORE
|
||||
PBDebug.log_score_change(score - old_score, "target is semi-invulnerable")
|
||||
score += 8
|
||||
PBDebug.log_score_change(score - old_score, "user at low HP and move has priority over faster target")
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# 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.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:thawing_move_against_frozen_target,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.medium_skill? && target.status == :FROZEN
|
||||
if move.rough_type == :FIRE || (Settings::MECHANICS_GENERATION >= 6 && move.move.thawsUser?)
|
||||
# Target is predicted to be knocked out by the move
|
||||
if move.damaging_move? && move.rough_damage >= target.hp
|
||||
old_score = score
|
||||
score -= 20
|
||||
PBDebug.log_score_change(score - old_score, "thaws the target")
|
||||
score += 8
|
||||
PBDebug.log_score_change(score - old_score, "target at low HP and move has priority over faster target")
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Prefer move if it has a high critical hit rate, critical hits are
|
||||
# possible but not certain, and target has raised defences/user has
|
||||
# lowered offences (Atk/Def or SpAtk/SpDef, whichever is relevant).
|
||||
|
||||
#===============================================================================
|
||||
# Prefer flinching external effects (note that move effects which cause
|
||||
# flinching are dealt with in the function code part of score calculation).
|
||||
# TODO: Review score modifier.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:external_flinching_effects,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.medium_skill? && move.damagingMove? && !move.move.flinchingMove?
|
||||
if (battle.moldBreaker || !target.has_active_ability?([:INNERFOCUS, :SHIELDDUST])) &&
|
||||
target.effects[PBEffects::Substitute] == 0
|
||||
if user.has_active_item?([:KINGSROCK, :RAZORFANG]) ||
|
||||
user.has_active_ability?(:STENCH)
|
||||
old_score = score
|
||||
score += 8
|
||||
PBDebug.log_score_change(score - old_score, "flinching")
|
||||
end
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Don't prefer contact move if making contact with the target could
|
||||
# trigger an effect that's bad for the user (Static, etc.).
|
||||
# => Also check if target has previously used Spiky Shield/King's Shield/
|
||||
# Baneful Bunker, and don't prefer move if so
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Prefer a contact move if making contact with the target could trigger
|
||||
# an effect that's good for the user (Poison Touch/Pickpocket).
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Prefer a higher priority move if the user is slower than the foe(s) and
|
||||
# the user is at risk of being knocked out. Consider whether the foe(s)
|
||||
# have priority moves of their own? Limit this to prefer priority damaging
|
||||
# moves?
|
||||
|
||||
#===============================================================================
|
||||
# Don't prefer damaging moves if the target is Biding, unless the move will deal
|
||||
# enough damage to KO the target before it retaliates (assuming the move is used
|
||||
# repeatedly until the target retaliates). Doesn't do a score change if the user
|
||||
# will be immune to Bide's damage.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:damaging_a_biding_target,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.medium_skill? && target.effects[PBEffects::Bide] > 0 && move.damagingMove?
|
||||
eff = user.effectiveness_of_type_against_battler(:NORMAL, target) # Bide is Normal type
|
||||
if !Effectiveness.ineffective?(eff)
|
||||
dmg = move.rough_damage
|
||||
eor_dmg = target.rough_end_of_round_damage
|
||||
hits_possible = target.effects[PBEffects::Bide] - 1
|
||||
eor_dmg *= hits_possible
|
||||
hits_possible += 1 if user.faster_than?(target)
|
||||
if dmg * hits_possible + eor_dmg < target.hp * 1.05
|
||||
old_score = score
|
||||
score -= 20
|
||||
PBDebug.log_score_change(score - old_score, "don't want to damage the Biding target")
|
||||
end
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# Don't prefer damaging moves that will knock out the target if they are using
|
||||
# Destiny Bond.
|
||||
# TODO: Review score modifier.
|
||||
# => Also don't prefer damaging moves if user is slower than the target, move
|
||||
# is likely to be lethal, and target has previously used Destiny Bond
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:knocking_out_a_destiny_bonder,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.medium_skill? && move.damagingMove? && target.effects[PBEffects::DestinyBond]
|
||||
dmg = move.rough_damage
|
||||
if dmg > target.hp * 1.05 # Predicted to KO the target
|
||||
old_score = score
|
||||
score -= 20
|
||||
score -= 10 if battle.pbAbleNonActiveCount(user.idxOwnSide) == 0
|
||||
PBDebug.log_score_change(score - old_score, "don't want to KO the Destiny Bonding target")
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Don't prefer Fire-type moves if target has previously used Powder and is
|
||||
# faster than the user.
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Check memory for whether target has previously used Ion Deluge, and
|
||||
# don't prefer move if it's Normal-type and target is immune because
|
||||
# of its ability (Lightning Rod, etc.).
|
||||
|
||||
#===============================================================================
|
||||
# Don't prefer a move that can be Magic Coated if the target (or any foe if the
|
||||
# move doesn't have a target) knows Magic Coat/has Magic Bounce.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:target_can_Magic_Coat_or_Bounce_move,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# TODO: Modify the semiInvulnerable? check to only apply if the target will
|
||||
# still be invulnerable when the user acts, i.e. compare speeds?
|
||||
if move.statusMove? && move.move.canMagicCoat? &&
|
||||
target.opposes?(user) && !target.battler.semiInvulnerable?
|
||||
if move.statusMove? && move.move.canMagicCoat? && target.opposes?(user) &&
|
||||
(target.faster_than?(user) || !target.battler.semiInvulnerable?)
|
||||
old_score = score
|
||||
if !battle.moldBreaker && target.has_active_ability?(:MAGICBOUNCE)
|
||||
score = Battle::AI::MOVE_USELESS_SCORE
|
||||
PBDebug.log_score_change(score - old_score, "useless because target will Magic Bounce it")
|
||||
elsif target.has_move_with_function?("BounceBackProblemCausingStatusMoves")
|
||||
elsif target.has_move_with_function?("BounceBackProblemCausingStatusMoves") &&
|
||||
target.can_attack? && !target.battler.semiInvulnerable?
|
||||
score -= 7
|
||||
PBDebug.log_score_change(score - old_score, "target knows Magic Coat and could bounce it")
|
||||
end
|
||||
@@ -277,15 +98,13 @@ Battle::AI::Handlers::GeneralMoveScore.add(:any_foe_can_Magic_Coat_or_Bounce_mov
|
||||
if move.statusMove? && move.move.canMagicCoat? && move.pbTarget(user.battler).num_targets == 0
|
||||
old_score = score
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
# TODO: Modify the semiInvulnerable? check to only apply if the target
|
||||
# will still be invulnerable when the user acts, i.e. compare
|
||||
# speeds?
|
||||
next if b.battler.semiInvulnerable?
|
||||
next if user.faster_than?(b) && b.battler.semiInvulnerable?
|
||||
if b.has_active_ability?(:MAGICBOUNCE) && !battle.moldBreaker
|
||||
score = Battle::AI::MOVE_USELESS_SCORE
|
||||
PBDebug.log_score_change(score - old_score, "useless because a foe will Magic Bounce it")
|
||||
break
|
||||
elsif b.has_move_with_function?("BounceBackProblemCausingStatusMoves")
|
||||
elsif b.has_move_with_function?("BounceBackProblemCausingStatusMoves") &&
|
||||
b.can_attack? && !b.battler.semiInvulnerable?
|
||||
score -= 7
|
||||
PBDebug.log_score_change(score - old_score, "a foe knows Magic Coat and could bounce it")
|
||||
break
|
||||
@@ -316,52 +135,6 @@ Battle::AI::Handlers::GeneralMoveScore.add(:any_battler_can_Snatch_move,
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#===============================================================================
|
||||
#===============================================================================
|
||||
#===============================================================================
|
||||
#===============================================================================
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Prefer Shadow moves (for flavour).
|
||||
|
||||
#===============================================================================
|
||||
# If user is frozen, prefer a move that can thaw the user.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveScore.add(:thawing_move_when_frozen,
|
||||
proc { |score, move, user, ai, battle|
|
||||
if ai.trainer.medium_skill? && user.status == :FROZEN
|
||||
old_score = score
|
||||
if move.move.thawsUser?
|
||||
score += 20
|
||||
PBDebug.log_score_change(score - old_score, "move will thaw the user")
|
||||
elsif user.check_for_move { |m| m.thawsUser? }
|
||||
score -= 20 # Don't prefer this move if user knows another move that thaws
|
||||
PBDebug.log_score_change(score - old_score, "user knows another move will thaw it")
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# Don't prefer a dancing move if the target has the Dancer ability.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveScore.add(:dance_move_against_dancer,
|
||||
proc { |score, move, user, ai, battle|
|
||||
if move.move.danceMove?
|
||||
old_score = score
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
score -= 10 if b.has_active_ability?(:DANCER)
|
||||
end
|
||||
PBDebug.log_score_change(score - old_score, "don't want to use a dance move because a foe has Dancer")
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# Pick a good move for the Choice items.
|
||||
# TODO: Review score modifier.
|
||||
@@ -371,9 +144,9 @@ Battle::AI::Handlers::GeneralMoveScore.add(:good_move_for_choice_item,
|
||||
if ai.trainer.medium_skill?
|
||||
if user.has_active_item?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF]) ||
|
||||
user.has_active_ability?(:GORILLATACTICS)
|
||||
old_score = score
|
||||
# Really don't prefer status moves (except Trick)
|
||||
if move.statusMove? && move.function != "UserTargetSwapItems"
|
||||
old_score = score
|
||||
score -= 25
|
||||
PBDebug.log_score_change(score - old_score, "don't want to be Choiced into a status move")
|
||||
next score
|
||||
@@ -400,7 +173,6 @@ Battle::AI::Handlers::GeneralMoveScore.add(:good_move_for_choice_item,
|
||||
# Prefer damaging moves if the foe is down to their last Pokémon (opportunistic).
|
||||
# Prefer damaging moves if the AI is down to its last Pokémon but the foe has
|
||||
# more (desperate).
|
||||
# TODO: Review score modifier.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveScore.add(:damaging_move_and_either_side_no_reserves,
|
||||
proc { |score, move, user, ai, battle|
|
||||
@@ -426,11 +198,225 @@ Battle::AI::Handlers::GeneralMoveScore.add(:damaging_move_and_either_side_no_res
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Don't prefer a move that is stopped by Wide Guard if any foe has
|
||||
# previously used Wide Guard.
|
||||
# TODO: Don't prefer Fire-type moves if target has previously used Powder and is
|
||||
# faster than the user.
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Don't prefer sound move if user hasn't been Throat Chopped but a foe has
|
||||
# previously used Throat Chop.
|
||||
# TODO: Don't prefer Normal-type moves if target has previously used Ion Deluge
|
||||
# and is immune to Electric moves.
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Don't prefer a move that is stopped by Wide Guard if any foe has
|
||||
# previously used Wide Guard.
|
||||
|
||||
#===============================================================================
|
||||
# Don't prefer attacking the target if they'd be semi-invulnerable.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:target_semi_invulnerable,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.medium_skill? && move.rough_accuracy > 0 &&
|
||||
(target.battler.semiInvulnerable? || target.effects[PBEffects::SkyDrop] >= 0)
|
||||
next score if user.has_active_ability?(:NOGUARD) || target.has_active_ability?(:NOGUARD)
|
||||
priority = move.rough_priority
|
||||
if priority > 0 || (priority == 0 && user.faster_than?(target)) # User goes first
|
||||
miss = true
|
||||
if ai.trainer.high_skill?
|
||||
# Knows what can get past semi-invulnerability
|
||||
if target.effects[PBEffects::SkyDrop] >= 0 ||
|
||||
target.battler.inTwoTurnAttack?("TwoTurnAttackInvulnerableInSky",
|
||||
"TwoTurnAttackInvulnerableInSkyParalyzeTarget",
|
||||
"TwoTurnAttackInvulnerableInSkyTargetCannotAct")
|
||||
miss = false if move.move.hitsFlyingTargets?
|
||||
elsif target.battler.inTwoTurnAttack?("TwoTurnAttackInvulnerableUnderground")
|
||||
miss = false if move.move.hitsDiggingTargets?
|
||||
elsif target.battler.inTwoTurnAttack?("TwoTurnAttackInvulnerableUnderwater")
|
||||
miss = false if move.move.hitsDivingTargets?
|
||||
end
|
||||
end
|
||||
if miss
|
||||
old_score = score
|
||||
score = Battle::AI::MOVE_USELESS_SCORE
|
||||
PBDebug.log_score_change(score - old_score, "target is semi-invulnerable")
|
||||
end
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# Account for accuracy of move.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:predicted_accuracy,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
acc = move.rough_accuracy.to_i
|
||||
if acc < 90
|
||||
old_score = score
|
||||
score -= (0.25 * (100 - acc)).to_i # -2 (89%) to -24 (1%)
|
||||
PBDebug.log_score_change(score - old_score, "accuracy (predicted #{acc}%)")
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# Adjust score based on how much damage it can deal.
|
||||
# Prefer the move even more if it's predicted to do enough damage to KO the
|
||||
# target.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:predicted_damage,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if move.damagingMove?
|
||||
dmg = move.rough_damage
|
||||
old_score = score
|
||||
if target.effects[PBEffects::Substitute] > 0
|
||||
target_hp = target.effects[PBEffects::Substitute]
|
||||
score += ([15.0 * dmg / target.effects[PBEffects::Substitute], 20].min).to_i
|
||||
PBDebug.log_score_change(score - old_score, "damaging move (predicted damage #{dmg} = #{100 * dmg / target.hp}% of target's Substitute)")
|
||||
else
|
||||
score += ([25.0 * dmg / target.hp, 30].min).to_i
|
||||
PBDebug.log_score_change(score - old_score, "damaging move (predicted damage #{dmg} = #{100 * dmg / target.hp}% of target's HP)")
|
||||
if ai.trainer.has_skill_flag?("HPAware") && dmg > target.hp * 1.1 # Predicted to KO the target
|
||||
old_score = score
|
||||
score += 10
|
||||
PBDebug.log_score_change(score - old_score, "predicted to KO the target")
|
||||
end
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# Prefer flinching external effects (note that move effects which cause
|
||||
# flinching are dealt with in the function code part of score calculation).
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:external_flinching_effects,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.medium_skill? && move.damagingMove? && !move.move.flinchingMove? &&
|
||||
user.faster_than?(target) && target.effects[PBEffects::Substitute] == 0
|
||||
if user.has_active_item?([:KINGSROCK, :RAZORFANG]) ||
|
||||
user.has_active_ability?(:STENCH)
|
||||
if battle.moldBreaker || !target.has_active_ability?([:INNERFOCUS, :SHIELDDUST])
|
||||
old_score = score
|
||||
score += 8
|
||||
PBDebug.log_score_change(score - old_score, "added chance to cause flinching")
|
||||
end
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# If target is frozen, don't prefer moves that could thaw them.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:thawing_move_against_frozen_target,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.medium_skill? && target.status == :FROZEN
|
||||
if move.rough_type == :FIRE || (Settings::MECHANICS_GENERATION >= 6 && move.move.thawsUser?)
|
||||
old_score = score
|
||||
score -= 20
|
||||
PBDebug.log_score_change(score - old_score, "thaws the target")
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Prefer a contact move if making contact with the target could trigger
|
||||
# an effect that's good for the user (Poison Touch/Pickpocket).
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Don't prefer contact move if making contact with the target could
|
||||
# trigger an effect that's bad for the user (Static, etc.).
|
||||
# => Also check if target has previously used Spiky Shield/King's Shield/
|
||||
# Baneful Bunker, and don't prefer move if so
|
||||
|
||||
#===============================================================================
|
||||
# Don't prefer damaging moves that will knock out the target if they are using
|
||||
# Destiny Bond or Grudge.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:knocking_out_a_destiny_bonder_or_grudger,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if (ai.trainer.has_skill_flag?("HPAware") || ai.trainer.high_skill?) && move.damagingMove? &&
|
||||
(target.effects[PBEffects::DestinyBond] || target.effects[PBEffects::Grudge])
|
||||
priority = move.rough_priority
|
||||
if priority > 0 || (priority == 0 && user.faster_than?(target)) # User goes first
|
||||
if move.rough_damage > target.hp * 1.1 # Predicted to KO the target
|
||||
old_score = score
|
||||
if target.effects[PBEffects::DestinyBond]
|
||||
score -= 20
|
||||
score -= 10 if battle.pbAbleNonActiveCount(user.idxOwnSide) == 0
|
||||
PBDebug.log_score_change(score - old_score, "don't want to KO the Destiny Bonding target")
|
||||
elsif target.effects[PBEffects::Grudge]
|
||||
score -= 15
|
||||
score -= 7 if battle.pbAbleNonActiveCount(user.idxOwnSide) == 0
|
||||
PBDebug.log_score_change(score - old_score, "don't want to KO the Grudge-using target")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Don't prefer damaging moves if the target is using Rage and they benefit
|
||||
# from the raised Attack.
|
||||
|
||||
#===============================================================================
|
||||
# Don't prefer damaging moves if the target is Biding, unless the move will deal
|
||||
# enough damage to KO the target before it retaliates (assuming the move is used
|
||||
# repeatedly until the target retaliates). Doesn't do a score change if the user
|
||||
# will be immune to Bide's damage.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:damaging_a_biding_target,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.medium_skill? && target.effects[PBEffects::Bide] > 0 && move.damagingMove?
|
||||
eff = user.effectiveness_of_type_against_battler(:NORMAL, target) # Bide is Normal type
|
||||
if !Effectiveness.ineffective?(eff)
|
||||
# Worth damaging the target if it can be knocked out before Bide ends
|
||||
if ai.trainer.has_skill_flag?("HPAware")
|
||||
dmg = move.rough_damage
|
||||
eor_dmg = target.rough_end_of_round_damage
|
||||
hits_possible = target.effects[PBEffects::Bide] - 1
|
||||
eor_dmg *= hits_possible
|
||||
hits_possible += 1 if user.faster_than?(target)
|
||||
next score if dmg * hits_possible + eor_dmg > target.hp * 1.1
|
||||
end
|
||||
old_score = score
|
||||
score -= 20
|
||||
PBDebug.log_score_change(score - old_score, "don't want to damage the Biding target")
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# Don't prefer a dancing move if the target has the Dancer ability.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveScore.add(:dance_move_against_dancer,
|
||||
proc { |score, move, user, ai, battle|
|
||||
if move.move.danceMove?
|
||||
old_score = score
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
score -= 10 if b.has_active_ability?(:DANCER)
|
||||
end
|
||||
PBDebug.log_score_change(score - old_score, "don't want to use a dance move because a foe has Dancer")
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
# ConsiderSwitching (can choose to switch out Pokémon)
|
||||
# ReserveLastPokemon (don't switch it in if possible)
|
||||
# UsePokemonInOrder (uses earliest-listed Pokémon possible)
|
||||
#
|
||||
# TODO: Add more skill flags.
|
||||
#===============================================================================
|
||||
class Battle::AI::AITrainer
|
||||
attr_reader :side, :trainer_index
|
||||
@@ -53,7 +55,6 @@ class Battle::AI::AITrainer
|
||||
if @trainer
|
||||
@trainer.flags.each { |flag| @skill_flags.push(flag) }
|
||||
end
|
||||
# TODO: Add skill flags depending on @skill.
|
||||
if @skill > 0
|
||||
@skill_flags.push("PredictMoveFailure")
|
||||
@skill_flags.push("ScoreMoves")
|
||||
@@ -74,7 +75,6 @@ class Battle::AI::AITrainer
|
||||
# NOTE: Any skill flag which is shorthand for multiple other skill flags
|
||||
# should be "unpacked" here.
|
||||
# TODO: Have a bunch of "AntiX" flags that negate the corresponding "X" flags.
|
||||
# TODO: Have flag "DontReserveLastPokemon" which negates "ReserveLastPokemon".
|
||||
end
|
||||
|
||||
def has_skill_flag?(flag)
|
||||
|
||||
@@ -15,34 +15,34 @@ class Battle::AI::AIBattler
|
||||
def refresh_battler
|
||||
old_party_index = @party_index
|
||||
@battler = @ai.battle.battlers[@index]
|
||||
@party_index = @battler.pokemonIndex
|
||||
@party_index = battler.pokemonIndex
|
||||
if @party_index != old_party_index
|
||||
# TODO: Start of battle or Pokémon switched/shifted; recalculate roles,
|
||||
# etc.
|
||||
end
|
||||
end
|
||||
|
||||
def pokemon; return @battler.pokemon; end
|
||||
def level; return @battler.level; end
|
||||
def hp; return @battler.hp; end
|
||||
def totalhp; return @battler.totalhp; end
|
||||
def fainted?; return @battler.fainted?; end
|
||||
def status; return @battler.status; end
|
||||
def statusCount; return @battler.statusCount; end
|
||||
def gender; return @battler.gender; end
|
||||
def turnCount; return @battler.turnCount; end
|
||||
def effects; return @battler.effects; end
|
||||
def stages; return @battler.stages; end
|
||||
def statStageAtMax?(stat); return @battler.statStageAtMax?(stat); end
|
||||
def statStageAtMin?(stat); return @battler.statStageAtMin?(stat); end
|
||||
def moves; return @battler.moves; end
|
||||
def pokemon; return battler.pokemon; end
|
||||
def level; return battler.level; end
|
||||
def hp; return battler.hp; end
|
||||
def totalhp; return battler.totalhp; end
|
||||
def fainted?; return battler.fainted?; end
|
||||
def status; return battler.status; end
|
||||
def statusCount; return battler.statusCount; end
|
||||
def gender; return battler.gender; end
|
||||
def turnCount; return battler.turnCount; end
|
||||
def effects; return battler.effects; end
|
||||
def stages; return battler.stages; end
|
||||
def statStageAtMax?(stat); return battler.statStageAtMax?(stat); end
|
||||
def statStageAtMin?(stat); return battler.statStageAtMin?(stat); end
|
||||
def moves; return battler.moves; end
|
||||
|
||||
def wild?
|
||||
return @ai.battle.wildBattle? && opposes?
|
||||
end
|
||||
|
||||
def name
|
||||
return sprintf("%s (%d)", @battler.name, @index)
|
||||
return sprintf("%s (%d)", battler.name, @index)
|
||||
end
|
||||
|
||||
def opposes?(other = nil)
|
||||
@@ -50,10 +50,10 @@ class Battle::AI::AIBattler
|
||||
return other.side != @side
|
||||
end
|
||||
|
||||
def idxOwnSide; return @battler.idxOwnSide; end
|
||||
def pbOwnSide; return @battler.pbOwnSide; end
|
||||
def idxOpposingSide; return @battler.idxOpposingSide; end
|
||||
def pbOpposingSide; return @battler.pbOpposingSide; end
|
||||
def idxOwnSide; return battler.idxOwnSide; end
|
||||
def pbOwnSide; return battler.pbOwnSide; end
|
||||
def idxOpposingSide; return battler.idxOpposingSide; end
|
||||
def pbOpposingSide; return battler.pbOpposingSide; end
|
||||
|
||||
#=============================================================================
|
||||
|
||||
@@ -63,44 +63,44 @@ class Battle::AI::AIBattler
|
||||
# Future Sight/Doom Desire
|
||||
# TODO
|
||||
# Wish
|
||||
if @ai.battle.positions[@index].effects[PBEffects::Wish] == 1 && @battler.canHeal?
|
||||
if @ai.battle.positions[@index].effects[PBEffects::Wish] == 1 && battler.canHeal?
|
||||
ret -= @ai.battle.positions[@index].effects[PBEffects::WishAmount]
|
||||
end
|
||||
# Sea of Fire
|
||||
if @ai.battle.sides[@side].effects[PBEffects::SeaOfFire] > 1 &&
|
||||
@battler.takesIndirectDamage? && !has_type?(:FIRE)
|
||||
battler.takesIndirectDamage? && !has_type?(:FIRE)
|
||||
ret += self.totalhp / 8
|
||||
end
|
||||
# Grassy Terrain (healing)
|
||||
if @ai.battle.field.terrain == :Grassy && @battler.affectedByTerrain? && @battler.canHeal?
|
||||
if @ai.battle.field.terrain == :Grassy && battler.affectedByTerrain? && battler.canHeal?
|
||||
ret -= [battler.totalhp / 16, 1].max
|
||||
end
|
||||
# Leftovers/Black Sludge
|
||||
if has_active_item?(:BLACKSLUDGE)
|
||||
if has_type?(:POISON)
|
||||
ret -= [battler.totalhp / 16, 1].max if @battler.canHeal?
|
||||
ret -= [battler.totalhp / 16, 1].max if battler.canHeal?
|
||||
else
|
||||
ret += [battler.totalhp / 8, 1].max if @battler.takesIndirectDamage?
|
||||
ret += [battler.totalhp / 8, 1].max if battler.takesIndirectDamage?
|
||||
end
|
||||
elsif has_active_item?(:LEFTOVERS)
|
||||
ret -= [battler.totalhp / 16, 1].max if @battler.canHeal?
|
||||
ret -= [battler.totalhp / 16, 1].max if battler.canHeal?
|
||||
end
|
||||
# Aqua Ring
|
||||
if self.effects[PBEffects::AquaRing] && @battler.canHeal?
|
||||
if self.effects[PBEffects::AquaRing] && battler.canHeal?
|
||||
amt = battler.totalhp / 16
|
||||
amt = (amt * 1.3).floor if has_active_item?(:BIGROOT)
|
||||
ret -= [amt, 1].max
|
||||
end
|
||||
# Ingrain
|
||||
if self.effects[PBEffects::Ingrain] && @battler.canHeal?
|
||||
if self.effects[PBEffects::Ingrain] && battler.canHeal?
|
||||
amt = battler.totalhp / 16
|
||||
amt = (amt * 1.3).floor if has_active_item?(:BIGROOT)
|
||||
ret -= [amt, 1].max
|
||||
end
|
||||
# Leech Seed
|
||||
if self.effects[PBEffects::LeechSeed] >= 0
|
||||
if @battler.takesIndirectDamage?
|
||||
ret += [battler.totalhp / 8, 1].max if @battler.takesIndirectDamage?
|
||||
if battler.takesIndirectDamage?
|
||||
ret += [battler.totalhp / 8, 1].max if battler.takesIndirectDamage?
|
||||
end
|
||||
else
|
||||
@ai.each_battler do |b, i|
|
||||
@@ -111,31 +111,33 @@ class Battle::AI::AIBattler
|
||||
end
|
||||
end
|
||||
# Hyper Mode (Shadow Pokémon)
|
||||
# TODO
|
||||
if battler.inHyperMode?
|
||||
ret += [battler.totalhp / 24, 1].max
|
||||
end
|
||||
# Poison/burn/Nightmare
|
||||
if self.status == :POISON
|
||||
if has_active_ability?(:POISONHEAL)
|
||||
ret -= [battler.totalhp / 8, 1].max if @battler.canHeal?
|
||||
elsif @battler.takesIndirectDamage?
|
||||
ret -= [battler.totalhp / 8, 1].max if battler.canHeal?
|
||||
elsif battler.takesIndirectDamage?
|
||||
mult = 2
|
||||
mult = [self.effects[PBEffects::Toxic] + 1, 16].min if self.statusCount > 0 # Toxic
|
||||
ret += [mult * battler.totalhp / 16, 1].max
|
||||
end
|
||||
elsif self.status == :BURN
|
||||
if @battler.takesIndirectDamage?
|
||||
if battler.takesIndirectDamage?
|
||||
amt = (Settings::MECHANICS_GENERATION >= 7) ? self.totalhp / 16 : self.totalhp / 8
|
||||
amt = (amt / 2.0).round if has_active_ability?(:HEATPROOF)
|
||||
ret += [amt, 1].max
|
||||
end
|
||||
elsif @battler.asleep? && self.statusCount > 1 && self.effects[PBEffects::Nightmare]
|
||||
ret += [battler.totalhp / 4, 1].max if @battler.takesIndirectDamage?
|
||||
elsif battler.asleep? && self.statusCount > 1 && self.effects[PBEffects::Nightmare]
|
||||
ret += [battler.totalhp / 4, 1].max if battler.takesIndirectDamage?
|
||||
end
|
||||
# Curse
|
||||
if self.effects[PBEffects::Curse]
|
||||
ret += [battler.totalhp / 4, 1].max if @battler.takesIndirectDamage?
|
||||
ret += [battler.totalhp / 4, 1].max if battler.takesIndirectDamage?
|
||||
end
|
||||
# Trapping damage
|
||||
if self.effects[PBEffects::Trapping] > 1 && @battler.takesIndirectDamage?
|
||||
if self.effects[PBEffects::Trapping] > 1 && battler.takesIndirectDamage?
|
||||
amt = (Settings::MECHANICS_GENERATION >= 6) ? self.totalhp / 8 : self.totalhp / 16
|
||||
if @ai.battlers[self.effects[PBEffects::TrappingUser]].has_active_item?(:BINDINGBAND)
|
||||
amt = (Settings::MECHANICS_GENERATION >= 6) ? self.totalhp / 6 : self.totalhp / 8
|
||||
@@ -143,16 +145,16 @@ class Battle::AI::AIBattler
|
||||
ret += [amt, 1].max
|
||||
end
|
||||
# Perish Song
|
||||
# TODO
|
||||
return 999_999 if self.effects[PBEffects::PerishSong] == 1
|
||||
# Bad Dreams
|
||||
if @battler.asleep? && self.statusCount > 1 && @battler.takesIndirectDamage?
|
||||
if battler.asleep? && self.statusCount > 1 && battler.takesIndirectDamage?
|
||||
@ai.each_battler do |b, i|
|
||||
next if i == @index || !b.battler.near?(@battler) || !b.has_active_ability?(:BADDREAMS)
|
||||
next if i == @index || !b.battler.near?(battler) || !b.has_active_ability?(:BADDREAMS)
|
||||
ret += [battler.totalhp / 8, 1].max
|
||||
end
|
||||
end
|
||||
# Sticky Barb
|
||||
if has_active_item?(:STICKYBARB) && @battler.takesIndirectDamage?
|
||||
if has_active_item?(:STICKYBARB) && battler.takesIndirectDamage?
|
||||
ret += [battler.totalhp / 8, 1].max
|
||||
end
|
||||
return ret
|
||||
@@ -163,22 +165,26 @@ class Battle::AI::AIBattler
|
||||
def base_stat(stat)
|
||||
ret = 0
|
||||
case stat
|
||||
when :ATTACK then ret = @battler.attack
|
||||
when :DEFENSE then ret = @battler.defense
|
||||
when :SPECIAL_ATTACK then ret = @battler.spatk
|
||||
when :SPECIAL_DEFENSE then ret = @battler.spdef
|
||||
when :SPEED then ret = @battler.speed
|
||||
when :ATTACK then ret = battler.attack
|
||||
when :DEFENSE then ret = battler.defense
|
||||
when :SPECIAL_ATTACK then ret = battler.spatk
|
||||
when :SPECIAL_DEFENSE then ret = battler.spdef
|
||||
when :SPEED then ret = battler.speed
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def rough_stat(stat)
|
||||
return @battler.pbSpeed if stat == :SPEED && @ai.trainer.high_skill?
|
||||
stageMul = [2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8]
|
||||
stageDiv = [8, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2]
|
||||
stage = @battler.stages[stat] + 6
|
||||
return battler.pbSpeed if stat == :SPEED && @ai.trainer.high_skill?
|
||||
stage_mul = Battle::Battler::STAT_STAGE_MULTIPLIERS
|
||||
stage_div = Battle::Battler::STAT_STAGE_DIVISORS
|
||||
if [:ACCURACY, :EVASION].include?(stat)
|
||||
stage_mul = Battle::Battler::ACC_EVA_STAGE_MULTIPLIERS
|
||||
stage_div = Battle::Battler::ACC_EVA_STAGE_DIVISORS
|
||||
end
|
||||
stage = battler.stages[stat] + Battle::Battler::STAT_STAGE_MAXIMUM
|
||||
value = base_stat(stat)
|
||||
return (value.to_f * stageMul[stage] / stageDiv[stage]).floor
|
||||
return (value.to_f * stage_mul[stage] / stage_div[stage]).floor
|
||||
end
|
||||
|
||||
def faster_than?(other)
|
||||
@@ -190,8 +196,8 @@ class Battle::AI::AIBattler
|
||||
|
||||
#=============================================================================
|
||||
|
||||
def types; return @battler.types; end
|
||||
def pbTypes(withExtraType = false); return @battler.pbTypes(withExtraType); end
|
||||
def types; return battler.types; end
|
||||
def pbTypes(withExtraType = false); return battler.pbTypes(withExtraType); end
|
||||
|
||||
def has_type?(type)
|
||||
return false if !type
|
||||
@@ -201,19 +207,20 @@ class Battle::AI::AIBattler
|
||||
|
||||
# TODO: Also make a def effectiveness_of_move_against_battler which calls
|
||||
# pbCalcTypeModSingle instead of effectiveness_of_type_against_single_battler_type.
|
||||
# Why?
|
||||
def effectiveness_of_type_against_battler(type, user = nil)
|
||||
ret = Effectiveness::NORMAL_EFFECTIVE_MULTIPLIER
|
||||
return ret if !type
|
||||
return ret if type == :GROUND && has_type?(:FLYING) && has_active_item?(:IRONBALL)
|
||||
# Get effectivenesses
|
||||
if type == :SHADOW
|
||||
if @battler.shadowPokemon?
|
||||
if battler.shadowPokemon?
|
||||
ret = Effectiveness::NOT_VERY_EFFECTIVE_MULTIPLIER
|
||||
else
|
||||
ret = Effectiveness::SUPER_EFFECTIVE_MULTIPLIER
|
||||
end
|
||||
else
|
||||
@battler.pbTypes(true).each do |defend_type|
|
||||
battler.pbTypes(true).each do |defend_type|
|
||||
ret *= effectiveness_of_type_against_single_battler_type(type, defend_type, user)
|
||||
end
|
||||
ret *= 2 if self.effects[PBEffects::TarShot] && type == :FIRE
|
||||
@@ -223,39 +230,39 @@ class Battle::AI::AIBattler
|
||||
|
||||
#=============================================================================
|
||||
|
||||
def ability_id; return @battler.ability_id; end
|
||||
def ability; return @battler.ability; end
|
||||
def ability_id; return battler.ability_id; end
|
||||
def ability; return battler.ability; end
|
||||
|
||||
def ability_active?
|
||||
return @battler.abilityActive?
|
||||
return battler.abilityActive?
|
||||
end
|
||||
|
||||
def has_active_ability?(ability, ignore_fainted = false)
|
||||
return @battler.hasActiveAbility?(ability, ignore_fainted)
|
||||
return battler.hasActiveAbility?(ability, ignore_fainted)
|
||||
end
|
||||
|
||||
def has_mold_breaker?
|
||||
return @ai.move.function == "IgnoreTargetAbility" || @battler.hasMoldBreaker?
|
||||
return @ai.move.function == "IgnoreTargetAbility" || battler.hasMoldBreaker?
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
|
||||
def item_id; return @battler.item_id; end
|
||||
def item; return @battler.item; end
|
||||
def item_id; return battler.item_id; end
|
||||
def item; return battler.item; end
|
||||
|
||||
def item_active?
|
||||
return @battler.itemActive?
|
||||
return battler.itemActive?
|
||||
end
|
||||
|
||||
def has_active_item?(item)
|
||||
return @battler.hasActiveItem?(item)
|
||||
return battler.hasActiveItem?(item)
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
|
||||
def check_for_move
|
||||
ret = false
|
||||
@battler.eachMove do |move|
|
||||
battler.eachMove do |move|
|
||||
next if move.pp == 0 && move.total_pp > 0
|
||||
next unless yield move
|
||||
ret = true
|
||||
@@ -266,7 +273,7 @@ class Battle::AI::AIBattler
|
||||
|
||||
def has_damaging_move_of_type?(*types)
|
||||
check_for_move do |m|
|
||||
return true if m.damagingMove? && types.include?(m.pbCalcType(@battler))
|
||||
return true if m.damagingMove? && types.include?(m.pbCalcType(battler))
|
||||
end
|
||||
return false
|
||||
end
|
||||
@@ -303,24 +310,24 @@ class Battle::AI::AIBattler
|
||||
def can_become_trapped?
|
||||
return false if fainted?
|
||||
# Ability/item effects that allow switching no matter what
|
||||
if ability_active? && Battle::AbilityEffects.triggerCertainSwitching(ability, @battler, @ai.battle)
|
||||
if ability_active? && Battle::AbilityEffects.triggerCertainSwitching(ability, battler, @ai.battle)
|
||||
return false
|
||||
end
|
||||
if item_active? && Battle::ItemEffects.triggerCertainSwitching(item, @battler, @ai.battle)
|
||||
if item_active? && Battle::ItemEffects.triggerCertainSwitching(item, battler, @ai.battle)
|
||||
return false
|
||||
end
|
||||
# Other certain switching effects
|
||||
return false if Settings::MORE_TYPE_EFFECTS && has_type?(:GHOST)
|
||||
# Other certain trapping effects
|
||||
return false if @battler.trappedInBattle?
|
||||
return false if battler.trappedInBattle?
|
||||
# Trapping abilities/items
|
||||
ai.each_foe_battler(side) do |b, i|
|
||||
if b.ability_active? &&
|
||||
Battle::AbilityEffects.triggerTrappingByTarget(b.ability, @battler, b.battler, @ai.battle)
|
||||
Battle::AbilityEffects.triggerTrappingByTarget(b.ability, battler, b.battler, @ai.battle)
|
||||
return false
|
||||
end
|
||||
if b.item_active? &&
|
||||
Battle::ItemEffects.triggerTrappingByTarget(b.item, @battler, b.battler, @ai.battle)
|
||||
Battle::ItemEffects.triggerTrappingByTarget(b.item, battler, b.battler, @ai.battle)
|
||||
return false
|
||||
end
|
||||
end
|
||||
@@ -432,9 +439,11 @@ class Battle::AI::AIBattler
|
||||
return 0 if has_active_ability?(:KLUTZ)
|
||||
# TODO: Unnerve, other item-negating effects.
|
||||
ret = BASE_ITEM_RATINGS[item] || 0
|
||||
# TODO: Add more context-sensitive modifications to the ratings from above.
|
||||
# Should they be moved into a handler?
|
||||
case item
|
||||
when :ADAMANTORB
|
||||
ret = 0 if !@battler.isSpecies?(:DIALGA) || !has_damaging_move_of_type?(:DRAGON, :STEEL)
|
||||
ret = 0 if !battler.isSpecies?(:DIALGA) || !has_damaging_move_of_type?(:DRAGON, :STEEL)
|
||||
when :BLACKBELT, :BLACKGLASSES, :CHARCOAL, :DRAGONFANG, :HARDSTONE, :MAGNET,
|
||||
:METALCOAT, :MIRACLESEED, :MYSTICWATER, :NEVERMELTICE, :POISONBARB,
|
||||
:SHARPBEAK, :SILKSCARF, :SILVERPOWDER, :SOFTSAND, :SPELLTAG,
|
||||
@@ -493,17 +502,17 @@ class Battle::AI::AIBattler
|
||||
when :CHOICESPECS, :WISEGLASSES
|
||||
ret = 0 if !check_for_move { |m| m.specialMove?(m.type) }
|
||||
when :DEEPSEATOOTH
|
||||
ret = 0 if !@battler.isSpecies?(:CLAMPERL) || !check_for_move { |m| m.specialMove?(m.type) }
|
||||
ret = 0 if !battler.isSpecies?(:CLAMPERL) || !check_for_move { |m| m.specialMove?(m.type) }
|
||||
when :GRISEOUSORB
|
||||
ret = 0 if !@battler.isSpecies?(:GIRATINA) || !has_damaging_move_of_type?(:DRAGON, :GHOST)
|
||||
ret = 0 if !battler.isSpecies?(:GIRATINA) || !has_damaging_move_of_type?(:DRAGON, :GHOST)
|
||||
when :IRONBALL
|
||||
ret = 0 if has_move_with_function?("ThrowUserItemAtTarget")
|
||||
when :LIGHTBALL
|
||||
ret = 0 if !@battler.isSpecies?(:PIKACHU) || !check_for_move { |m| m.damagingMove? }
|
||||
ret = 0 if !battler.isSpecies?(:PIKACHU) || !check_for_move { |m| m.damagingMove? }
|
||||
when :LUSTROUSORB
|
||||
ret = 0 if !@battler.isSpecies?(:PALKIA) || !has_damaging_move_of_type?(:DRAGON, :WATER)
|
||||
ret = 0 if !battler.isSpecies?(:PALKIA) || !has_damaging_move_of_type?(:DRAGON, :WATER)
|
||||
when :SOULDEW
|
||||
if !@battler.isSpecies?(:LATIAS) && !@battler.isSpecies?(:LATIOS)
|
||||
if !battler.isSpecies?(:LATIAS) && !battler.isSpecies?(:LATIOS)
|
||||
ret = 0
|
||||
elsif Settings::SOUL_DEW_POWERS_UP_TYPES
|
||||
ret = 0 if !has_damaging_move_of_type?(:PSYCHIC, :DRAGON)
|
||||
@@ -511,7 +520,7 @@ class Battle::AI::AIBattler
|
||||
ret -= 2 if !check_for_move { |m| m.specialMove?(m.type) } # Also boosts SpDef
|
||||
end
|
||||
when :THICKCLUB
|
||||
ret = 0 if (!@battler.isSpecies?(:CUBONE) && !@battler.isSpecies?(:MAROWAK)) ||
|
||||
ret = 0 if (!battler.isSpecies?(:CUBONE) && !battler.isSpecies?(:MAROWAK)) ||
|
||||
!check_for_move { |m| m.physicalMove?(m.type) }
|
||||
end
|
||||
# Prefer if this battler knows Fling and it will do a lot of damage/have an
|
||||
@@ -568,18 +577,18 @@ class Battle::AI::AIBattler
|
||||
ret += (cured_status && status == cured_status) ? 6 : -6
|
||||
when :PERSIMBERRY
|
||||
# Confusion cure
|
||||
ret += (effects[PBEffects::Confusion] > 1) ? 6 : -6
|
||||
ret += (self.effects[PBEffects::Confusion] > 1) ? 6 : -6
|
||||
when :LUMBERRY
|
||||
# Any status/confusion cure
|
||||
ret += (status != :NONE || effects[PBEffects::Confusion] > 1) ? 6 : -6
|
||||
ret += (status != :NONE || self.effects[PBEffects::Confusion] > 1) ? 6 : -6
|
||||
when :MENTALHERB
|
||||
# Cure mental effects
|
||||
if effects[PBEffects::Attract] >= 0 ||
|
||||
effects[PBEffects::Taunt] > 1 ||
|
||||
effects[PBEffects::Encore] > 1 ||
|
||||
effects[PBEffects::Torment] ||
|
||||
effects[PBEffects::Disable] > 1 ||
|
||||
effects[PBEffects::HealBlock] > 1
|
||||
if self.effects[PBEffects::Attract] >= 0 ||
|
||||
self.effects[PBEffects::Taunt] > 1 ||
|
||||
self.effects[PBEffects::Encore] > 1 ||
|
||||
self.effects[PBEffects::Torment] ||
|
||||
self.effects[PBEffects::Disable] > 1 ||
|
||||
self.effects[PBEffects::HealBlock] > 1
|
||||
ret += 6
|
||||
else
|
||||
ret -= 6
|
||||
@@ -604,13 +613,13 @@ class Battle::AI::AIBattler
|
||||
ret = ret * 3 / 2 if GameData::Item.get(item).is_berry? && has_active_ability?(:RIPEN)
|
||||
when :WHITEHERB
|
||||
# Resets lowered stats
|
||||
ret += (@battler.hasLoweredStatStages?) ? 8 : -8
|
||||
ret += (battler.hasLoweredStatStages?) ? 8 : -8
|
||||
when :MICLEBERRY
|
||||
# Raises accuracy of next move
|
||||
ret += (@ai.stat_raise_worthwhile?(self, :ACCURACY, true)) ? 6 : -6
|
||||
when :LANSATBERRY
|
||||
# Focus energy
|
||||
ret += (effects[PBEffects::FocusEnergy] < 2) ? 6 : -6
|
||||
ret += (self.effects[PBEffects::FocusEnergy] < 2) ? 6 : -6
|
||||
when :LEPPABERRY
|
||||
# Restore PP
|
||||
ret += 6
|
||||
@@ -903,54 +912,53 @@ class Battle::AI::AIBattler
|
||||
# they need to do something special in that case.
|
||||
def wants_ability?(ability = :NONE)
|
||||
ability = ability.id if !ability.is_a?(Symbol) && ability.respond_to?("id")
|
||||
# TODO: Ideally replace the above list of ratings with context-sensitive
|
||||
# calculations. Should they all go in this method, or should there be
|
||||
# more handlers for each ability?
|
||||
ret = BASE_ABILITY_RATINGS[ability] || 0
|
||||
# TODO: Add more context-sensitive modifications to the ratings from above.
|
||||
# Should they be moved into a handler?
|
||||
case ability
|
||||
when :BLAZE
|
||||
return 0 if !has_damaging_move_of_type?(:FIRE)
|
||||
ret = 0 if !has_damaging_move_of_type?(:FIRE)
|
||||
when :CUTECHARM, :RIVALRY
|
||||
return 0 if gender == 2
|
||||
ret = 0 if gender == 2
|
||||
when :FRIENDGUARD, :HEALER, :SYMBOISIS, :TELEPATHY
|
||||
has_ally = false
|
||||
each_ally(@side) { |b, i| has_ally = true }
|
||||
return 0 if !has_ally
|
||||
ret = 0 if !has_ally
|
||||
when :GALEWINGS
|
||||
return 0 if !check_for_move { |m| m.type == :FLYING }
|
||||
ret = 0 if !check_for_move { |m| m.type == :FLYING }
|
||||
when :HUGEPOWER, :PUREPOWER
|
||||
return 0 if !ai.stat_raise_worthwhile?(self, :ATTACK, true)
|
||||
ret = 0 if !ai.stat_raise_worthwhile?(self, :ATTACK, true)
|
||||
when :IRONFIST
|
||||
return 0 if !check_for_move { |m| m.punchingMove? }
|
||||
ret = 0 if !check_for_move { |m| m.punchingMove? }
|
||||
when :LIQUIDVOICE
|
||||
return 0 if !check_for_move { |m| m.soundMove? }
|
||||
ret = 0 if !check_for_move { |m| m.soundMove? }
|
||||
when :MEGALAUNCHER
|
||||
return 0 if !check_for_move { |m| m.pulseMove? }
|
||||
ret = 0 if !check_for_move { |m| m.pulseMove? }
|
||||
when :OVERGROW
|
||||
return 0 if !has_damaging_move_of_type?(:GRASS)
|
||||
ret = 0 if !has_damaging_move_of_type?(:GRASS)
|
||||
when :PRANKSTER
|
||||
return 0 if !check_for_move { |m| m.statusMove? }
|
||||
ret = 0 if !check_for_move { |m| m.statusMove? }
|
||||
when :PUNKROCK
|
||||
return 1 if !check_for_move { |m| m.damagingMove? && m.soundMove? }
|
||||
ret = 1 if !check_for_move { |m| m.damagingMove? && m.soundMove? }
|
||||
when :RECKLESS
|
||||
return 0 if !check_for_move { |m| m.recoilMove? }
|
||||
ret = 0 if !check_for_move { |m| m.recoilMove? }
|
||||
when :ROCKHEAD
|
||||
return 0 if !check_for_move { |m| m.recoilMove? && !m.is_a?(Battle::Move::CrashDamageIfFailsUnusableInGravity) }
|
||||
ret = 0 if !check_for_move { |m| m.recoilMove? && !m.is_a?(Battle::Move::CrashDamageIfFailsUnusableInGravity) }
|
||||
when :RUNAWAY
|
||||
return 0 if wild?
|
||||
ret = 0 if wild?
|
||||
when :SANDFORCE
|
||||
return 2 if !has_damaging_move_of_type?(:GROUND, :ROCK, :STEEL)
|
||||
ret = 2 if !has_damaging_move_of_type?(:GROUND, :ROCK, :STEEL)
|
||||
when :SKILLLINK
|
||||
return 0 if !check_for_move { |m| m.is_a?(Battle::Move::HitTwoToFiveTimes) }
|
||||
ret = 0 if !check_for_move { |m| m.is_a?(Battle::Move::HitTwoToFiveTimes) }
|
||||
when :STEELWORKER
|
||||
return 0 if !has_damaging_move_of_type?(:GRASS)
|
||||
ret = 0 if !has_damaging_move_of_type?(:GRASS)
|
||||
when :SWARM
|
||||
return 0 if !has_damaging_move_of_type?(:BUG)
|
||||
ret = 0 if !has_damaging_move_of_type?(:BUG)
|
||||
when :TORRENT
|
||||
return 0 if !has_damaging_move_of_type?(:WATER)
|
||||
ret = 0 if !has_damaging_move_of_type?(:WATER)
|
||||
when :TRIAGE
|
||||
return 0 if !check_for_move { |m| m.healingMove? }
|
||||
ret = 0 if !check_for_move { |m| m.healingMove? }
|
||||
end
|
||||
ret = BASE_ABILITY_RATINGS[ability] || 0
|
||||
return ret
|
||||
end
|
||||
|
||||
@@ -966,22 +974,22 @@ class Battle::AI::AIBattler
|
||||
ret = Effectiveness::NORMAL_EFFECTIVE_MULTIPLIER
|
||||
end
|
||||
# Foresight
|
||||
if (user&.has_active_ability?(:SCRAPPY) || @battler.effects[PBEffects::Foresight]) &&
|
||||
if (user&.has_active_ability?(:SCRAPPY) || self.effects[PBEffects::Foresight]) &&
|
||||
defend_type == :GHOST
|
||||
ret = Effectiveness::NORMAL_EFFECTIVE_MULTIPLIER
|
||||
end
|
||||
# Miracle Eye
|
||||
if @battler.effects[PBEffects::MiracleEye] && defend_type == :DARK
|
||||
if self.effects[PBEffects::MiracleEye] && defend_type == :DARK
|
||||
ret = Effectiveness::NORMAL_EFFECTIVE_MULTIPLIER
|
||||
end
|
||||
elsif Effectiveness.super_effective_type?(type, defend_type)
|
||||
# Delta Stream's weather
|
||||
if @battler.effectiveWeather == :StrongWinds && defend_type == :FLYING
|
||||
if battler.effectiveWeather == :StrongWinds && defend_type == :FLYING
|
||||
ret = Effectiveness::NORMAL_EFFECTIVE_MULTIPLIER
|
||||
end
|
||||
end
|
||||
# Grounded Flying-type Pokémon become susceptible to Ground moves
|
||||
if !@battler.airborne? && defend_type == :FLYING && type == :GROUND
|
||||
if !battler.airborne? && defend_type == :FLYING && type == :GROUND
|
||||
ret = Effectiveness::NORMAL_EFFECTIVE_MULTIPLIER
|
||||
end
|
||||
return ret
|
||||
|
||||
@@ -105,20 +105,21 @@ class Battle::AI::AIMove
|
||||
def rough_damage
|
||||
base_dmg = base_power
|
||||
return base_dmg if @move.is_a?(Battle::Move::FixedDamageMove)
|
||||
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]
|
||||
max_stage = Battle::Battler::STAT_STAGE_MAXIMUM
|
||||
stage_mul = Battle::Battler::STAT_STAGE_MULTIPLIERS
|
||||
stage_div = Battle::Battler::STAT_STAGE_DIVISORS
|
||||
# Get the user and target of this move
|
||||
user = @ai.user
|
||||
user_battler = user.battler
|
||||
target = @ai.target
|
||||
target_battler = target.battler
|
||||
|
||||
# Get the move's type
|
||||
calc_type = rough_type
|
||||
|
||||
# Decide whether the move will definitely be a critical hit
|
||||
is_critical = rough_critical_hit_stage >= Battle::Move::CRITICAL_HIT_RATIOS.length
|
||||
|
||||
# Decide whether the move has 50% chance of higher of being a critical hit
|
||||
# TODO: Make this a gradient/probability rather than all-or-nothing?
|
||||
crit_stage = rough_critical_hit_stage
|
||||
is_critical = crit_stage >= Battle::Move::CRITICAL_HIT_RATIOS.length ||
|
||||
Battle::Move::CRITICAL_HIT_RATIOS[crit_stage] <= 2
|
||||
##### Calculate user's attack stat #####
|
||||
if ["CategoryDependsOnHigherDamagePoisonTarget",
|
||||
"CategoryDependsOnHigherDamageIgnoreTargetAbility"].include?(function)
|
||||
@@ -126,17 +127,15 @@ class Battle::AI::AIMove
|
||||
end
|
||||
atk, atk_stage = @move.pbGetAttackStats(user.battler, target.battler)
|
||||
if !target.has_active_ability?(:UNAWARE) || @ai.battle.moldBreaker
|
||||
atk_stage = 6 if is_critical && atk_stage < 6
|
||||
atk_stage = max_stage if is_critical && atk_stage < max_stage
|
||||
atk = (atk.to_f * stage_mul[atk_stage] / stage_div[atk_stage]).floor
|
||||
end
|
||||
|
||||
##### Calculate target's defense stat #####
|
||||
defense, def_stage = @move.pbGetDefenseStats(user.battler, target.battler)
|
||||
if !user.has_active_ability?(:UNAWARE) || @ai.battle.moldBreaker
|
||||
def_stage = 6 if is_critical && def_stage > 6
|
||||
def_stage = max_stage if is_critical && def_stage > max_stage
|
||||
defense = (defense.to_f * stage_mul[def_stage] / stage_div[def_stage]).floor
|
||||
end
|
||||
|
||||
##### Calculate all multiplier effects #####
|
||||
multipliers = {
|
||||
:power_multiplier => 1.0,
|
||||
@@ -154,7 +153,6 @@ class Battle::AI::AIMove
|
||||
multipliers[:power_multiplier] *= 4 / 3.0
|
||||
end
|
||||
end
|
||||
|
||||
# Ability effects that alter damage
|
||||
if user.ability_active?
|
||||
# NOTE: These abilities aren't suitable for checking at the start of the
|
||||
@@ -166,7 +164,6 @@ class Battle::AI::AIMove
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
if !@ai.battle.moldBreaker
|
||||
user_battler.allAllies.each do |b|
|
||||
next if !b.abilityActive?
|
||||
@@ -198,7 +195,6 @@ class Battle::AI::AIMove
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# Item effects that alter damage
|
||||
# NOTE: Type-boosting gems aren't suitable for checking at the start of the
|
||||
# round.
|
||||
@@ -218,23 +214,18 @@ class Battle::AI::AIMove
|
||||
target.item, user_battler, target_battler, @move, multipliers, base_dmg, calc_type
|
||||
)
|
||||
end
|
||||
|
||||
# Parental Bond
|
||||
if user.has_active_ability?(:PARENTALBOND)
|
||||
multipliers[:power_multiplier] *= (Settings::MECHANICS_GENERATION >= 7) ? 1.25 : 1.5
|
||||
end
|
||||
|
||||
# Me First
|
||||
# TODO
|
||||
|
||||
# Helping Hand - n/a
|
||||
|
||||
# Charge
|
||||
if @ai.trainer.medium_skill? &&
|
||||
user.effects[PBEffects::Charge] > 0 && calc_type == :ELECTRIC
|
||||
multipliers[:power_multiplier] *= 2
|
||||
end
|
||||
|
||||
# Mud Sport and Water Sport
|
||||
if @ai.trainer.medium_skill?
|
||||
if calc_type == :ELECTRIC
|
||||
@@ -253,7 +244,6 @@ class Battle::AI::AIMove
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Terrain moves
|
||||
if @ai.trainer.medium_skill?
|
||||
terrain_multiplier = (Settings::MECHANICS_GENERATION >= 8) ? 1.3 : 1.5
|
||||
@@ -268,7 +258,6 @@ class Battle::AI::AIMove
|
||||
multipliers[:power_multiplier] /= 2 if calc_type == :DRAGON && target_battler.affectedByTerrain?
|
||||
end
|
||||
end
|
||||
|
||||
# Badge multipliers
|
||||
if @ai.trainer.high_skill? && @ai.battle.internalBattle && target_battler.pbOwnedByPlayer?
|
||||
# Don't need to check the Atk/Sp Atk-boosting badges because the AI
|
||||
@@ -279,12 +268,10 @@ class Battle::AI::AIMove
|
||||
multipliers[:defense_multiplier] *= 1.1
|
||||
end
|
||||
end
|
||||
|
||||
# Multi-targeting attacks
|
||||
if @ai.trainer.high_skill? && targets_multiple_battlers?
|
||||
multipliers[:final_damage_multiplier] *= 0.75
|
||||
end
|
||||
|
||||
# Weather
|
||||
if @ai.trainer.medium_skill?
|
||||
case user_battler.effectiveWeather
|
||||
@@ -309,7 +296,6 @@ class Battle::AI::AIMove
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Critical hits
|
||||
if is_critical
|
||||
if Settings::NEW_CRITICAL_HIT_RATE_MECHANICS
|
||||
@@ -318,9 +304,7 @@ class Battle::AI::AIMove
|
||||
multipliers[:final_damage_multiplier] *= 2
|
||||
end
|
||||
end
|
||||
|
||||
# Random variance - n/a
|
||||
|
||||
# STAB
|
||||
if calc_type && user.has_type?(calc_type)
|
||||
if user.has_active_ability?(:ADAPTABILITY)
|
||||
@@ -329,17 +313,14 @@ class Battle::AI::AIMove
|
||||
multipliers[:final_damage_multiplier] *= 1.5
|
||||
end
|
||||
end
|
||||
|
||||
# Type effectiveness
|
||||
typemod = target.effectiveness_of_type_against_battler(calc_type, user)
|
||||
multipliers[:final_damage_multiplier] *= typemod
|
||||
|
||||
# Burn
|
||||
if @ai.trainer.high_skill? && user.status == :BURN && physicalMove?(calc_type) &&
|
||||
@move.damageReducedByBurn? && !user.has_active_ability?(:GUTS)
|
||||
multipliers[:final_damage_multiplier] /= 2
|
||||
end
|
||||
|
||||
# Aurora Veil, Reflect, Light Screen
|
||||
if @ai.trainer.medium_skill? && !@move.ignoresReflect? && !is_critical &&
|
||||
!user.has_active_ability?(:INFILTRATOR)
|
||||
@@ -363,18 +344,14 @@ class Battle::AI::AIMove
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Minimize
|
||||
if @ai.trainer.medium_skill? && target.effects[PBEffects::Minimize] && @move.tramplesMinimize?
|
||||
multipliers[:final_damage_multiplier] *= 2
|
||||
end
|
||||
|
||||
# Move-specific base damage modifiers
|
||||
# TODO
|
||||
|
||||
# Move-specific final damage modifiers
|
||||
# TODO
|
||||
|
||||
##### Main damage calculation #####
|
||||
base_dmg = [(base_dmg * multipliers[:power_multiplier]).round, 1].max
|
||||
atk = [(atk * multipliers[:attack_multiplier]).round, 1].max
|
||||
@@ -428,10 +405,11 @@ class Battle::AI::AIMove
|
||||
return 0 if modifiers[:base_accuracy] < 0
|
||||
return 100 if modifiers[:base_accuracy] == 0
|
||||
# Calculation
|
||||
accStage = [[modifiers[:accuracy_stage], -6].max, 6].min + 6
|
||||
evaStage = [[modifiers[:evasion_stage], -6].max, 6].min + 6
|
||||
stageMul = [3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7, 8, 9]
|
||||
stageDiv = [9, 8, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3]
|
||||
max_stage = Battle::Battler::STAT_STAGE_MAXIMUM
|
||||
accStage = [[modifiers[:accuracy_stage], -max_stage].max, max_stage].min + max_stage
|
||||
evaStage = [[modifiers[:evasion_stage], -max_stage].max, max_stage].min + max_stage
|
||||
stageMul = Battle::Battler::ACC_EVA_STAGE_MULTIPLIERS
|
||||
stageDiv = Battle::Battler::ACC_EVA_STAGE_DIVISORS
|
||||
accuracy = 100.0 * stageMul[accStage] / stageDiv[accStage]
|
||||
evasion = 100.0 * stageMul[evaStage] / stageDiv[evaStage]
|
||||
accuracy = (accuracy * modifiers[:accuracy_multiplier]).round
|
||||
|
||||
@@ -562,8 +562,9 @@ Battle::AI::Handlers::MoveEffectScore.add("UserMakeSubstitute",
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
score += 5 if !b.check_for_move { |m| m.ignoresSubstitute?(b.battler) }
|
||||
end
|
||||
# TODO: Predict incoming damage, and prefer if it's greater than
|
||||
# user.totalhp / 4?
|
||||
# Prefer if the user lost more than a Substitute's worth of HP from the last
|
||||
# attack against it
|
||||
score += 7 if user.battler.lastHPLost >= user.totalhp / 4
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
@@ -518,8 +518,6 @@ Battle::AI::Handlers::MoveEffectScore.add("StartRaiseUserAtk1WhenDamaged",
|
||||
if ai.trainer.has_skill_flag?("HPAware")
|
||||
next score if user.hp <= user.totalhp / 3
|
||||
end
|
||||
# TODO: Check whether any foe has damaging moves that will trigger the stat
|
||||
# raise?
|
||||
# Prefer if user benefits from a raised Attack stat
|
||||
score += 10 if ai.stat_raise_worthwhile?(user, :ATTACK)
|
||||
score += 7 if user.has_move_with_function?("PowerHigherWithUserPositiveStatStages")
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#===============================================================================
|
||||
# TODO: Should there be all the "next score" for status moves? Remember that
|
||||
# other function codes can call this code as part of their scoring.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("SleepTarget",
|
||||
proc { |move, user, target, ai, battle|
|
||||
@@ -9,15 +8,16 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("SleepTarget",
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("SleepTarget",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score if target.effects[PBEffects::Yawn] > 0 # Target is going to fall asleep anyway
|
||||
useless_score = (move.statusMove?) ? Battle::AI::MOVE_USELESS_SCORE : score
|
||||
next useless_score if target.effects[PBEffects::Yawn] > 0 # Target is going to fall asleep anyway
|
||||
# No score modifier if the sleep will be removed immediately
|
||||
next score if target.has_active_item?([:CHESTOBERRY, :LUMBERRY])
|
||||
next score if target.faster_than?(user) &&
|
||||
target.has_active_ability?(:HYDRATION) &&
|
||||
[:Rain, :HeavyRain].include?(target.battler.effectiveWeather)
|
||||
next useless_score if target.has_active_item?([:CHESTOBERRY, :LUMBERRY])
|
||||
next useless_score if target.faster_than?(user) &&
|
||||
target.has_active_ability?(:HYDRATION) &&
|
||||
[:Rain, :HeavyRain].include?(target.battler.effectiveWeather)
|
||||
if target.battler.pbCanSleep?(user.battler, false, move.move)
|
||||
add_effect = move.get_score_change_for_additional_effect(user, target)
|
||||
next score if add_effect == -999 # Additional effect will be negated
|
||||
next useless_score if add_effect == -999 # Additional effect will be negated
|
||||
score += add_effect
|
||||
# Inherent preference
|
||||
score += 15
|
||||
@@ -88,8 +88,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.copy("SleepTarget",
|
||||
"SleepTargetNextTurn")
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Should there be all the "next score" for status moves? Remember that
|
||||
# other function codes can call this code as part of their scoring.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("PoisonTarget",
|
||||
proc { |move, user, target, ai, battle|
|
||||
@@ -98,16 +97,16 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("PoisonTarget",
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("PoisonTarget",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score if target.effects[PBEffects::Yawn] > 0 # Target is going to fall asleep
|
||||
next Battle::AI::MOVE_USELESS_SCORE if move.statusMove? && target.has_active_ability?(:POISONHEAL)
|
||||
useless_score = (move.statusMove?) ? Battle::AI::MOVE_USELESS_SCORE : score
|
||||
next useless_score if target.has_active_ability?(:POISONHEAL)
|
||||
# No score modifier if the poisoning will be removed immediately
|
||||
next score if target.has_active_item?([:PECHABERRY, :LUMBERRY])
|
||||
next score if target.faster_than?(user) &&
|
||||
target.has_active_ability?(:HYDRATION) &&
|
||||
[:Rain, :HeavyRain].include?(target.battler.effectiveWeather)
|
||||
next useless_score if target.has_active_item?([:PECHABERRY, :LUMBERRY])
|
||||
next useless_score if target.faster_than?(user) &&
|
||||
target.has_active_ability?(:HYDRATION) &&
|
||||
[:Rain, :HeavyRain].include?(target.battler.effectiveWeather)
|
||||
if target.battler.pbCanPoison?(user.battler, false, move.move)
|
||||
add_effect = move.get_score_change_for_additional_effect(user, target)
|
||||
next score if add_effect == -999 # Additional effect will be negated
|
||||
next useless_score if add_effect == -999 # Additional effect will be negated
|
||||
score += add_effect
|
||||
# Inherent preference
|
||||
score += 15
|
||||
@@ -159,10 +158,10 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("PoisonTargetLowerTarget
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("PoisonTargetLowerTargetSpeed1",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("PoisonTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("LowerTargetSpeed1",
|
||||
score, move, user, target, ai, battle)
|
||||
poison_score = Battle::AI::Handlers.apply_move_effect_against_target_score("PoisonTarget",
|
||||
0, move, user, target, ai, battle)
|
||||
score += poison_score if poison_score != Battle::AI::MOVE_USELESS_SCORE
|
||||
score = ai.get_score_for_target_stat_drop(score, target, move.move.statDown, false)
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -176,8 +175,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.copy("PoisonTarget",
|
||||
"BadPoisonTarget")
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Should there be all the "next score" for status moves? Remember that
|
||||
# other function codes can call this code as part of their scoring.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("ParalyzeTarget",
|
||||
proc { |move, user, target, ai, battle|
|
||||
@@ -186,15 +184,15 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("ParalyzeTarget",
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("ParalyzeTarget",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score if target.effects[PBEffects::Yawn] > 0 # Target is going to fall asleep
|
||||
useless_score = (move.statusMove?) ? Battle::AI::MOVE_USELESS_SCORE : score
|
||||
# No score modifier if the paralysis will be removed immediately
|
||||
next score if target.has_active_item?([:CHERIBERRY, :LUMBERRY])
|
||||
next score if target.faster_than?(user) &&
|
||||
target.has_active_ability?(:HYDRATION) &&
|
||||
[:Rain, :HeavyRain].include?(target.battler.effectiveWeather)
|
||||
next useless_score if target.has_active_item?([:CHERIBERRY, :LUMBERRY])
|
||||
next useless_score if target.faster_than?(user) &&
|
||||
target.has_active_ability?(:HYDRATION) &&
|
||||
[:Rain, :HeavyRain].include?(target.battler.effectiveWeather)
|
||||
if target.battler.pbCanParalyze?(user.battler, false, move.move)
|
||||
add_effect = move.get_score_change_for_additional_effect(user, target)
|
||||
next score if add_effect == -999 # Additional effect will be negated
|
||||
next useless_score if add_effect == -999 # Additional effect will be negated
|
||||
score += add_effect
|
||||
# Inherent preference (because of the chance of full paralysis)
|
||||
score += 10
|
||||
@@ -262,17 +260,22 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.copy("ParalyzeTarget",
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("ParalyzeFlinchTarget",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("ParalyzeTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("FlinchTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
paralyze_score = Battle::AI::Handlers.apply_move_effect_against_target_score("ParalyzeTarget",
|
||||
0, move, user, target, ai, battle)
|
||||
flinch_score = Battle::AI::Handlers.apply_move_effect_against_target_score("FlinchTarget",
|
||||
0, move, user, target, ai, battle)
|
||||
if paralyze_score == Battle::AI::MOVE_USELESS_SCORE &&
|
||||
flinch_score == Battle::AI::MOVE_USELESS_SCORE
|
||||
next Battle::AI::MOVE_USELESS_SCORE
|
||||
end
|
||||
score += paralyze_score if paralyze_score != Battle::AI::MOVE_USELESS_SCORE
|
||||
score += flinch_score if flinch_score != Battle::AI::MOVE_USELESS_SCORE
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Should there be all the "next score" for status moves? Remember that
|
||||
# other function codes can call this code as part of their scoring.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("BurnTarget",
|
||||
proc { |move, user, target, ai, battle|
|
||||
@@ -281,15 +284,15 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("BurnTarget",
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("BurnTarget",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score if target.effects[PBEffects::Yawn] > 0 # Target is going to fall asleep
|
||||
useless_score = (move.statusMove?) ? Battle::AI::MOVE_USELESS_SCORE : score
|
||||
# No score modifier if the burn will be removed immediately
|
||||
next score if target.has_active_item?([:RAWSTBERRY, :LUMBERRY])
|
||||
next score if target.faster_than?(user) &&
|
||||
target.has_active_ability?(:HYDRATION) &&
|
||||
[:Rain, :HeavyRain].include?(target.battler.effectiveWeather)
|
||||
next useless_score if target.has_active_item?([:RAWSTBERRY, :LUMBERRY])
|
||||
next useless_score if target.faster_than?(user) &&
|
||||
target.has_active_ability?(:HYDRATION) &&
|
||||
[:Rain, :HeavyRain].include?(target.battler.effectiveWeather)
|
||||
if target.battler.pbCanBurn?(user.battler, false, move.move)
|
||||
add_effect = move.get_score_change_for_additional_effect(user, target)
|
||||
next score if add_effect == -999 # Additional effect will be negated
|
||||
next useless_score if add_effect == -999 # Additional effect will be negated
|
||||
score += add_effect
|
||||
# Inherent preference
|
||||
score += 15
|
||||
@@ -339,17 +342,22 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("BurnTarget",
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("BurnFlinchTarget",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("BurnTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("FlinchTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
burn_score = Battle::AI::Handlers.apply_move_effect_against_target_score("BurnTarget",
|
||||
0, move, user, target, ai, battle)
|
||||
flinch_score = Battle::AI::Handlers.apply_move_effect_against_target_score("FlinchTarget",
|
||||
0, move, user, target, ai, battle)
|
||||
if burn_score == Battle::AI::MOVE_USELESS_SCORE &&
|
||||
flinch_score == Battle::AI::MOVE_USELESS_SCORE
|
||||
next Battle::AI::MOVE_USELESS_SCORE
|
||||
end
|
||||
score += burn_score if burn_score != Battle::AI::MOVE_USELESS_SCORE
|
||||
score += flinch_score if flinch_score != Battle::AI::MOVE_USELESS_SCORE
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Should there be all the "next score" for status moves? Remember that
|
||||
# other function codes can call this code as part of their scoring.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("FreezeTarget",
|
||||
proc { |move, user, target, ai, battle|
|
||||
@@ -358,15 +366,15 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("FreezeTarget",
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("FreezeTarget",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score if target.effects[PBEffects::Yawn] > 0 # Target is going to fall asleep
|
||||
useless_score = (move.statusMove?) ? Battle::AI::MOVE_USELESS_SCORE : score
|
||||
# No score modifier if the freeze will be removed immediately
|
||||
next score if target.has_active_item?([:ASPEARBERRY, :LUMBERRY])
|
||||
next score if target.faster_than?(user) &&
|
||||
target.has_active_ability?(:HYDRATION) &&
|
||||
[:Rain, :HeavyRain].include?(target.battler.effectiveWeather)
|
||||
next useless_score if target.has_active_item?([:ASPEARBERRY, :LUMBERRY])
|
||||
next useless_score if target.faster_than?(user) &&
|
||||
target.has_active_ability?(:HYDRATION) &&
|
||||
[:Rain, :HeavyRain].include?(target.battler.effectiveWeather)
|
||||
if target.battler.pbCanFreeze?(user.battler, false, move.move)
|
||||
add_effect = move.get_score_change_for_additional_effect(user, target)
|
||||
next score if add_effect == -999 # Additional effect will be negated
|
||||
next useless_score if add_effect == -999 # Additional effect will be negated
|
||||
score += add_effect
|
||||
# Inherent preference
|
||||
score += 15
|
||||
@@ -413,10 +421,16 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.copy("FreezeTarget",
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("FreezeFlinchTarget",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("FreezeTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("FlinchTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
freeze_score = Battle::AI::Handlers.apply_move_effect_against_target_score("FreezeTarget",
|
||||
0, move, user, target, ai, battle)
|
||||
flinch_score = Battle::AI::Handlers.apply_move_effect_against_target_score("FlinchTarget",
|
||||
0, move, user, target, ai, battle)
|
||||
if freeze_score == Battle::AI::MOVE_USELESS_SCORE &&
|
||||
flinch_score == Battle::AI::MOVE_USELESS_SCORE
|
||||
next Battle::AI::MOVE_USELESS_SCORE
|
||||
end
|
||||
score += freeze_score if freeze_score != Battle::AI::MOVE_USELESS_SCORE
|
||||
score += flinch_score if flinch_score != Battle::AI::MOVE_USELESS_SCORE
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -426,19 +440,17 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("FreezeFlinchTarget",
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("ParalyzeBurnOrFreezeTarget",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score if target.effects[PBEffects::Yawn] > 0 # Target is going to fall asleep
|
||||
# No score modifier if the status problem will be removed immediately
|
||||
next score if target.has_active_item?(:LUMBERRY)
|
||||
next score if target.faster_than?(user) &&
|
||||
target.has_active_ability?(:HYDRATION) &&
|
||||
[:Rain, :HeavyRain].include?(target.battler.effectiveWeather)
|
||||
# Scores for the possible effects
|
||||
score += (Battle::AI::Handlers.apply_move_effect_against_target_score("ParalyzeTarget",
|
||||
Battle::AI::MOVE_BASE_SCORE, move, user, target, ai, battle) - Battle::AI::MOVE_BASE_SCORE) / 3
|
||||
score += (Battle::AI::Handlers.apply_move_effect_against_target_score("BurnTarget",
|
||||
Battle::AI::MOVE_BASE_SCORE, move, user, target, ai, battle) - Battle::AI::MOVE_BASE_SCORE) / 3
|
||||
score += (Battle::AI::Handlers.apply_move_effect_against_target_score("FreezeTarget",
|
||||
Battle::AI::MOVE_BASE_SCORE, move, user, target, ai, battle) - Battle::AI::MOVE_BASE_SCORE) / 3
|
||||
["ParalyzeTarget", "BurnTarget", "FreezeTarget"].each do |function_code|
|
||||
effect_score = Battle::AI::Handlers.apply_move_effect_against_target_score(function_code,
|
||||
0, move, user, target, ai, battle)
|
||||
score += effect_score / 3 if effect_score != Battle::AI::MOVE_USELESS_SCORE
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -461,22 +473,17 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("GiveUserStatusToTarget",
|
||||
# Curing the user's status problem
|
||||
score += 15 if !user.wants_status_problem?(user.status)
|
||||
# Giving the target a status problem
|
||||
case user.status
|
||||
when :SLEEP
|
||||
next Battle::AI::Handlers.apply_move_effect_against_target_score("SleepTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
when :PARALYSIS
|
||||
next Battle::AI::Handlers.apply_move_effect_against_target_score("ParalyzeTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
when :POISON
|
||||
next Battle::AI::Handlers.apply_move_effect_against_target_score("PoisonTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
when :BURN
|
||||
next Battle::AI::Handlers.apply_move_effect_against_target_score("BurnTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
when :FROZEN
|
||||
next Battle::AI::Handlers.apply_move_effect_against_target_score("FreezeTarget",
|
||||
function_code = {
|
||||
:SLEEP => "SleepTarget",
|
||||
:PARALYSIS => "ParalyzeTarget",
|
||||
:POISON => "PoisonTarget",
|
||||
:BURN => "BurnTarget",
|
||||
:FROZEN => "FreezeTarget"
|
||||
}[user.status]
|
||||
if function_code
|
||||
new_score = Battle::AI::Handlers.apply_move_effect_against_target_score(function_code,
|
||||
score, move, user, target, ai, battle)
|
||||
next new_score if new_score != Battle::AI::MOVE_USELESS_SCORE
|
||||
end
|
||||
next score
|
||||
}
|
||||
@@ -633,7 +640,6 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("ConfuseTarget",
|
||||
score += 20 * target.hp / target.totalhp
|
||||
end
|
||||
# Prefer if the target is paralysed or infatuated, to compound the turn skipping
|
||||
# TODO: Also prefer if the target is trapped in battle or can't switch out?
|
||||
score += 8 if target.status == :PARALYSIS || target.effects[PBEffects::Attract] >= 0
|
||||
# Don't prefer if target benefits from being confused
|
||||
score -= 15 if target.has_active_ability?(:TANGLEDFEET)
|
||||
@@ -665,7 +671,6 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("AttractTarget",
|
||||
# Inherent preference
|
||||
score += 15
|
||||
# Prefer if the target is paralysed or confused, to compound the turn skipping
|
||||
# TODO: Also prefer if the target is trapped in battle or can't switch out?
|
||||
score += 8 if target.status == :PARALYSIS || target.effects[PBEffects::Confusion] > 1
|
||||
# Don't prefer if the target can infatuate the user because of this move
|
||||
score -= 15 if target.has_active_item?(:DESTINYKNOT) &&
|
||||
|
||||
@@ -464,9 +464,6 @@ Battle::AI::Handlers::MoveEffectScore.add("EnsureNextCriticalHit",
|
||||
end
|
||||
# Prefer if user knows a damaging move which won't definitely critical hit
|
||||
if user.check_for_move { |m| m.damagingMove? && m.function != "AlwaysCriticalHit"}
|
||||
# TODO: Change the score depending on how much of an effect a critical hit
|
||||
# will have? Critical hits ignore the user's offensive stat drops
|
||||
# and the target's defensive stat raises, and multiply the damage.
|
||||
score += 15
|
||||
end
|
||||
next score
|
||||
@@ -521,9 +518,6 @@ Battle::AI::Handlers::MoveEffectScore.add("StartPreventCriticalHitsAgainstUserSi
|
||||
crit_stage = 99 if b.check_for_move { |m| m.pbCritialOverride(b.battler, user.battler) > 0 }
|
||||
crit_stage = [crit_stage, Battle::Move::CRITICAL_HIT_RATIOS.length - 1].min
|
||||
end
|
||||
# TODO: Change the score depending on how much of an effect a critical hit
|
||||
# will have? Critical hits ignore the user's offensive stat drops
|
||||
# and the target's defensive stat raises, and multiply the damage.
|
||||
score += 8 * crit_stage if crit_stage > 0
|
||||
score += 10 if b.effects[PBEffects::LaserFocus] > 0
|
||||
end
|
||||
@@ -655,10 +649,7 @@ Battle::AI::Handlers::MoveEffectScore.add("StartWeakenPhysicalDamageAgainstUserS
|
||||
# Doesn't stack with Aurora Veil
|
||||
next Battle::AI::MOVE_USELESS_SCORE if user.pbOwnSide.effects[PBEffects::AuroraVeil] > 0
|
||||
# Don't prefer the lower the user's HP is
|
||||
# TODO: Should this HP check exist? The effect can still be set up for
|
||||
# allies. Maybe just don't prefer if there are no replacement mons
|
||||
# left.
|
||||
if ai.trainer.has_skill_flag?("HPAware")
|
||||
if ai.trainer.has_skill_flag?("HPAware") && battle.pbAbleNonActiveCount(user.idxOwnSide) == 0
|
||||
if user.hp <= user.totalhp / 2
|
||||
score -= (20 * (0.75 - (user.hp.to_f / user.totalhp))).to_i # -5 to -15
|
||||
end
|
||||
@@ -686,10 +677,7 @@ Battle::AI::Handlers::MoveEffectScore.add("StartWeakenSpecialDamageAgainstUserSi
|
||||
# Doesn't stack with Aurora Veil
|
||||
next Battle::AI::MOVE_USELESS_SCORE if user.pbOwnSide.effects[PBEffects::AuroraVeil] > 0
|
||||
# Don't prefer the lower the user's HP is
|
||||
# TODO: Should this HP check exist? The effect can still be set up for
|
||||
# allies. Maybe just don't prefer if there are no replacement mons
|
||||
# left.
|
||||
if ai.trainer.has_skill_flag?("HPAware")
|
||||
if ai.trainer.has_skill_flag?("HPAware") && battle.pbAbleNonActiveCount(user.idxOwnSide) == 0
|
||||
if user.hp <= user.totalhp / 2
|
||||
score -= (20 * (0.75 - (user.hp.to_f / user.totalhp))).to_i # -5 to -15
|
||||
end
|
||||
@@ -720,10 +708,7 @@ Battle::AI::Handlers::MoveEffectScore.add("StartWeakenDamageAgainstUserSideIfHai
|
||||
next Battle::AI::MOVE_USELESS_SCORE if user.pbOwnSide.effects[PBEffects::Reflect] > 0 &&
|
||||
user.pbOwnSide.effects[PBEffects::LightScreen] > 0
|
||||
# Don't prefer the lower the user's HP is
|
||||
# TODO: Should this HP check exist? The effect can still be set up for
|
||||
# allies. Maybe just don't prefer if there are no replacement mons
|
||||
# left.
|
||||
if ai.trainer.has_skill_flag?("HPAware")
|
||||
if ai.trainer.has_skill_flag?("HPAware") && battle.pbAbleNonActiveCount(user.idxOwnSide) == 0
|
||||
if user.hp <= user.totalhp / 2
|
||||
score -= (20 * (0.75 - (user.hp.to_f / user.totalhp))).to_i # -5 to -15
|
||||
end
|
||||
@@ -767,8 +752,7 @@ Battle::AI::Handlers::MoveEffectScore.add("ProtectUser",
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
next if !b.can_attack?
|
||||
next if !b.check_for_move { |m| m.canProtectAgainst? }
|
||||
# TODO: Include b's Unseen Fist somehow? We don't know which move b will
|
||||
# be using, so we don't know if Unseen Fist will apply.
|
||||
next if b.has_active_ability?(:UNSEENFIST) && b.check_for_move { |m| m.contactMove? }
|
||||
useless = false
|
||||
# General preference
|
||||
score += 7
|
||||
@@ -812,8 +796,7 @@ Battle::AI::Handlers::MoveEffectScore.add("ProtectUserBanefulBunker",
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
next if !b.can_attack?
|
||||
next if !b.check_for_move { |m| m.canProtectAgainst? }
|
||||
# TODO: Include b's Unseen Fist somehow? We don't know which move b will
|
||||
# be using, so we don't know if Unseen Fist will apply.
|
||||
next if b.has_active_ability?(:UNSEENFIST) && b.check_for_move { |m| m.contactMove? }
|
||||
useless = false
|
||||
# General preference
|
||||
score += 7
|
||||
@@ -821,7 +804,9 @@ Battle::AI::Handlers::MoveEffectScore.add("ProtectUserBanefulBunker",
|
||||
if b.check_for_move { |m| m.contactMove? }
|
||||
poison_score = Battle::AI::Handlers.apply_move_effect_against_target_score("PoisonTarget",
|
||||
0, move, user, b, ai, battle)
|
||||
score += poison_score / 2 # Halved because we don't know what move b will use
|
||||
if poison_score != Battle::AI::MOVE_USELESS_SCORE
|
||||
score += poison_score / 2 # Halved because we don't know what move b will use
|
||||
end
|
||||
end
|
||||
# Prefer if the foe is in the middle of using a two turn attack
|
||||
score += 15 if b.effects[PBEffects::TwoTurnAttack] &&
|
||||
@@ -852,7 +837,7 @@ Battle::AI::Handlers::MoveEffectScore.add("ProtectUserBanefulBunker",
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Special scoring for Aegislash.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("ProtectUserFromDamagingMovesKingsShield",
|
||||
proc { |score, move, user, ai, battle|
|
||||
@@ -863,8 +848,7 @@ Battle::AI::Handlers::MoveEffectScore.add("ProtectUserFromDamagingMovesKingsShie
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
next if !b.can_attack?
|
||||
next if !b.check_for_move { |m| m.damagingMove? && m.canProtectAgainst? }
|
||||
# TODO: Include b's Unseen Fist somehow? We don't know which move b will
|
||||
# be using, so we don't know if Unseen Fist will apply.
|
||||
next if b.has_active_ability?(:UNSEENFIST) && b.check_for_move { |m| m.contactMove? }
|
||||
useless = false
|
||||
# General preference
|
||||
score += 7
|
||||
@@ -898,6 +882,9 @@ Battle::AI::Handlers::MoveEffectScore.add("ProtectUserFromDamagingMovesKingsShie
|
||||
# Don't prefer if the user used a protection move last turn, making this one
|
||||
# less likely to work
|
||||
score -= (user.effects[PBEffects::ProtectRate] - 1) * ((Settings::MECHANICS_GENERATION >= 6) ? 15 : 10)
|
||||
# Aegislash
|
||||
score += 10 if user.battler.isSpecies?(:AEGISLASH) && user.form == 1 &&
|
||||
user.ability == :STANCECHANGE
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -914,8 +901,7 @@ Battle::AI::Handlers::MoveEffectScore.add("ProtectUserFromDamagingMovesObstruct"
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
next if !b.can_attack?
|
||||
next if !b.check_for_move { |m| m.damagingMove? && m.canProtectAgainst? }
|
||||
# TODO: Include b's Unseen Fist somehow? We don't know which move b will
|
||||
# be using, so we don't know if Unseen Fist will apply.
|
||||
next if b.has_active_ability?(:UNSEENFIST) && b.check_for_move { |m| m.contactMove? }
|
||||
useless = false
|
||||
# General preference
|
||||
score += 7
|
||||
@@ -964,8 +950,7 @@ Battle::AI::Handlers::MoveEffectScore.add("ProtectUserFromTargetingMovesSpikyShi
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
next if !b.can_attack?
|
||||
next if !b.check_for_move { |m| m.canProtectAgainst? }
|
||||
# TODO: Include b's Unseen Fist somehow? We don't know which move b will
|
||||
# be using, so we don't know if Unseen Fist will apply.
|
||||
next if b.has_active_ability?(:UNSEENFIST) && b.check_for_move { |m| m.contactMove? }
|
||||
useless = false
|
||||
# General preference
|
||||
score += 7
|
||||
@@ -1016,8 +1001,7 @@ Battle::AI::Handlers::MoveEffectScore.add("ProtectUserSideFromDamagingMovesIfUse
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
next if !b.can_attack?
|
||||
next if !b.check_for_move { |m| m.damagingMove? && m.canProtectAgainst? }
|
||||
# TODO: Include b's Unseen Fist somehow? We don't know which move b will
|
||||
# be using, so we don't know if Unseen Fist will apply.
|
||||
next if b.has_active_ability?(:UNSEENFIST) && b.check_for_move { |m| m.contactMove? }
|
||||
useless = false
|
||||
# General preference
|
||||
score += 7
|
||||
@@ -1252,8 +1236,9 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RecoilThirdOfDamageDealt
|
||||
score -= 25 * [dmg, user.hp].min / user.hp
|
||||
end
|
||||
# Score for paralysing
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("ParalyzeTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
paralyze_score = Battle::AI::Handlers.apply_move_effect_against_target_score("ParalyzeTarget",
|
||||
0, move, user, target, ai, battle)
|
||||
score += paralyze_score if paralyze_score != Battle::AI::MOVE_USELESS_SCORE
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -1274,8 +1259,9 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RecoilThirdOfDamageDealt
|
||||
score -= 25 * [dmg, user.hp].min / user.hp
|
||||
end
|
||||
# Score for burning
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("BurnTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
burn_score = Battle::AI::Handlers.apply_move_effect_against_target_score("BurnTarget",
|
||||
0, move, user, target, ai, battle)
|
||||
score += burn_score if burn_score != Battle::AI::MOVE_USELESS_SCORE
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -1359,7 +1345,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("EnsureNextMoveAlwaysHits
|
||||
acc = m.pbBaseAccuracy(user.battler, target.battler) if ai.trainer.medium_skill?
|
||||
score += 5 if acc < 90 && acc != 0
|
||||
score += 8 if acc <= 50 && acc != 0
|
||||
# TODO: Prefer more if m is a OHKO move.
|
||||
score += 8 if m.is_a?(Battle::Move::OHKO)
|
||||
end
|
||||
# TODO: Prefer if target has increased evasion.
|
||||
# Not worth it if the user or the target is at low HP
|
||||
@@ -1535,10 +1521,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TargetMovesBecomeElectri
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: This could check all other battlers, not just foes. It could check the
|
||||
# effectivenesses of their Normal and Electric moves on all their foes,
|
||||
# not just on the user. I think this is overkill, particularly as the
|
||||
# effect only lasts for one round.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("NormalMovesBecomeElectric",
|
||||
proc { |score, move, user, ai, battle|
|
||||
|
||||
@@ -31,8 +31,9 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HitTwoTimesPoisonTarget"
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("HitTwoTimes",
|
||||
score, move, user, target, ai, battle)
|
||||
# Score for poisoning
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("PoisonTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
poison_score = Battle::AI::Handlers.apply_move_effect_against_target_score("PoisonTarget",
|
||||
0, move, user, target, ai, battle)
|
||||
score += poison_score if poison_score != Battle::AI::MOVE_USELESS_SCORE
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -279,6 +280,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackParalyzeTar
|
||||
# Score for being a two turn attack
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttack",
|
||||
score, move, user, target, ai, battle)
|
||||
next score if score == Battle::AI::MOVE_USELESS_SCORE
|
||||
# Score for paralysing
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("ParalyzeTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
@@ -294,6 +296,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackBurnTarget"
|
||||
# Score for being a two turn attack
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttack",
|
||||
score, move, user, target, ai, battle)
|
||||
next score if score == Battle::AI::MOVE_USELESS_SCORE
|
||||
# Score for burning
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("BurnTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
@@ -309,6 +312,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackFlinchTarge
|
||||
# Score for being a two turn attack
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttack",
|
||||
score, move, user, target, ai, battle)
|
||||
next score if score == Battle::AI::MOVE_USELESS_SCORE
|
||||
# Score for flinching
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("FlinchTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
@@ -323,11 +327,12 @@ Battle::AI::Handlers::MoveFailureCheck.copy("RaiseUserAtkDef1",
|
||||
"TwoTurnAttackRaiseUserSpAtkSpDefSpd2")
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackRaiseUserSpAtkSpDefSpd2",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Score for raising user's stats
|
||||
score = ai.get_score_for_target_stat_raise(score, user, move.move.statUp)
|
||||
# Score for being a two turn attack
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttack",
|
||||
score, move, user, target, ai, battle)
|
||||
next score if score == Battle::AI::MOVE_USELESS_SCORE
|
||||
# Score for raising user's stats
|
||||
score = ai.get_score_for_target_stat_raise(score, user, move.move.statUp)
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -340,6 +345,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackChargeRaise
|
||||
# Score for being a two turn attack
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttack",
|
||||
score, move, user, target, ai, battle)
|
||||
next score if score == Battle::AI::MOVE_USELESS_SCORE
|
||||
# Score for raising the user's stat
|
||||
score = Battle::AI::Handlers.apply_move_effect_score("RaiseUserDefense1",
|
||||
score, move, user, ai, battle)
|
||||
@@ -355,6 +361,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackChargeRaise
|
||||
# Score for being a two turn attack
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttack",
|
||||
score, move, user, target, ai, battle)
|
||||
next score if score == Battle::AI::MOVE_USELESS_SCORE
|
||||
# Score for raising the user's stat
|
||||
score = Battle::AI::Handlers.apply_move_effect_score("RaiseUserSpAtk1",
|
||||
score, move, user, ai, battle)
|
||||
@@ -370,6 +377,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackInvulnerabl
|
||||
# Score for being a two turn attack
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttack",
|
||||
score, move, user, target, ai, battle)
|
||||
next score if score == Battle::AI::MOVE_USELESS_SCORE
|
||||
# Score for being semi-invulnerable underground
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
if b.check_for_move { |m| m.hitsDiggingTargets? }
|
||||
@@ -390,6 +398,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackInvulnerabl
|
||||
# Score for being a two turn attack
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttack",
|
||||
score, move, user, target, ai, battle)
|
||||
next score if score == Battle::AI::MOVE_USELESS_SCORE
|
||||
# Score for being semi-invulnerable underwater
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
if b.check_for_move { |m| m.hitsDivingTargets? }
|
||||
@@ -410,6 +419,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackInvulnerabl
|
||||
# Score for being a two turn attack
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttack",
|
||||
score, move, user, target, ai, battle)
|
||||
next score if score == Battle::AI::MOVE_USELESS_SCORE
|
||||
# Score for being semi-invulnerable in the sky
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
if b.check_for_move { |m| m.hitsFlyingTargets? }
|
||||
@@ -430,6 +440,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackInvulnerabl
|
||||
# Score for being a two turn attack and semi-invulnerable in the sky
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttackInvulnerableInSky",
|
||||
score, move, user, target, ai, battle)
|
||||
next score if score == Battle::AI::MOVE_USELESS_SCORE
|
||||
# Score for paralyzing the target
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("ParalyzeTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
@@ -460,6 +471,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackInvulnerabl
|
||||
# Score for being a two turn attack
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttack",
|
||||
score, move, user, target, ai, battle)
|
||||
next score if score == Battle::AI::MOVE_USELESS_SCORE
|
||||
# Score for being invulnerable
|
||||
score += 8
|
||||
# Score for removing protections
|
||||
|
||||
@@ -97,11 +97,7 @@ Battle::AI::Handlers::MoveEffectScore.add("HealUserHalfOfTotalHPLoseFlyingTypeTh
|
||||
score = Battle::AI::Handlers.apply_move_effect_score("HealUserHalfOfTotalHP",
|
||||
score, move, user, ai, battle)
|
||||
# User loses the Flying type this round
|
||||
if user.has_type?(:FLYING)
|
||||
# TODO: Decide whether losing the Flying type is good or bad. Look at
|
||||
# type effectiveness changes against the user, and for foes' Ground
|
||||
# moves (foe foes slower than the user). Anything else?
|
||||
end
|
||||
# NOTE: Not worth considering and scoring for.
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -118,7 +114,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("CureTargetStatusHealUser
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Consider how much HP will be restored
|
||||
score = Battle::AI::Handlers.apply_move_effect_score("HealUserHalfOfTotalHP",
|
||||
score, move, user, ai, battle)
|
||||
score, move, user, ai, battle)
|
||||
# Will cure target's status
|
||||
score += (target.wants_status_problem?(target.status)) ? 10 : -8
|
||||
next score
|
||||
|
||||
@@ -217,8 +217,9 @@ Battle::AI::Handlers::MoveFailureCheck.add("UserConsumeBerryRaiseDefense2",
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UserConsumeBerryRaiseDefense2",
|
||||
proc { |score, move, user, ai, battle|
|
||||
# Score for raising the user's stat
|
||||
score = Battle::AI::Handlers.apply_move_effect_score("RaiseUserDefense2",
|
||||
score, move, user, ai, battle)
|
||||
stat_raise_score = Battle::AI::Handlers.apply_move_effect_score("RaiseUserDefense2",
|
||||
0, move, user, ai, battle)
|
||||
score += stat_raise_score if stat_raise_score != Battle::AI::MOVE_USELESS_SCORE
|
||||
# Score for the consumed berry's effect
|
||||
score += user.get_score_change_for_consuming_item(user.item_id, true)
|
||||
# Score for other results of consuming the berry
|
||||
|
||||
@@ -1693,7 +1693,7 @@ Battle::AbilityEffects::OnBeingHit.add(:ANGERPOINT,
|
||||
next if !target.damageState.critical
|
||||
next if !target.pbCanRaiseStatStage?(:ATTACK, target)
|
||||
battle.pbShowAbilitySplash(target)
|
||||
target.stages[:ATTACK] = 6
|
||||
target.stages[:ATTACK] = Battle::Battler::STAT_STAGE_MAXIMUM
|
||||
target.statsRaisedThisRound = true
|
||||
battle.pbCommonAnimation("StatUp", target)
|
||||
if Battle::Scene::USE_ABILITY_SPLASH
|
||||
|
||||
@@ -162,7 +162,7 @@ MenuHandlers.add(:battle_pokemon_debug_menu, :set_stat_stages, {
|
||||
break if cmd < 0
|
||||
if cmd < stat_ids.length # Set a stat
|
||||
params = ChooseNumberParams.new
|
||||
params.setRange(-6, 6)
|
||||
params.setRange(-Battle::Battler::STAT_STAGE_MAXIMUM, Battle::Battler::STAT_STAGE_MAXIMUM)
|
||||
params.setNegativesAllowed(true)
|
||||
params.setDefaultValue(battler.stages[stat_ids[cmd]])
|
||||
value = pbMessageChooseNumber(
|
||||
|
||||
Reference in New Issue
Block a user