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