mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2026-01-30 18:21:03 +00:00
Resolved all remaining TODO comments for AI (except testing), fixed effects of moves that can end the battle
This commit is contained in:
@@ -387,7 +387,7 @@ class Battle::Battler
|
|||||||
#=============================================================================
|
#=============================================================================
|
||||||
# Item effects
|
# Item effects
|
||||||
#=============================================================================
|
#=============================================================================
|
||||||
def pbConfusionBerry(item_to_use, forced, flavor, confuse_msg)
|
def pbConfusionBerry(item_to_use, forced, confuse_stat, confuse_msg)
|
||||||
return false if !forced && !canHeal?
|
return false if !forced && !canHeal?
|
||||||
return false if !forced && !canConsumePinchBerry?(Settings::MECHANICS_GENERATION >= 7)
|
return false if !forced && !canConsumePinchBerry?(Settings::MECHANICS_GENERATION >= 7)
|
||||||
used_item_name = GameData::Item.get(item_to_use).name
|
used_item_name = GameData::Item.get(item_to_use).name
|
||||||
@@ -415,12 +415,9 @@ class Battle::Battler
|
|||||||
@battle.pbDisplay(_INTL("{1} restored its health using its {2}!", pbThis, used_item_name))
|
@battle.pbDisplay(_INTL("{1} restored its health using its {2}!", pbThis, used_item_name))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
flavor_stat = [:ATTACK, :DEFENSE, :SPEED, :SPECIAL_ATTACK, :SPECIAL_DEFENSE][flavor]
|
if self.nature.stat_changes.any? { |val| val[0] == confuse_stat && val[1] < 0 }
|
||||||
self.nature.stat_changes.each do |change|
|
|
||||||
next if change[1] > 0 || change[0] != flavor_stat
|
|
||||||
@battle.pbDisplay(confuse_msg)
|
@battle.pbDisplay(confuse_msg)
|
||||||
pbConfuse if pbCanConfuseSelf?(false)
|
pbConfuse if pbCanConfuseSelf?(false)
|
||||||
break
|
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#===============================================================================
|
#===============================================================================
|
||||||
class Battle::Move::FleeFromBattle < Battle::Move
|
class Battle::Move::FleeFromBattle < Battle::Move
|
||||||
def pbMoveFailed?(user, targets)
|
def pbMoveFailed?(user, targets)
|
||||||
if !@battle.pbCanRun?(user.index)
|
if !@battle.pbCanRun?(user.index) || (user.wild? && user.allAllies.length > 0)
|
||||||
@battle.pbDisplay(_INTL("But it failed!"))
|
@battle.pbDisplay(_INTL("But it failed!"))
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@@ -23,7 +23,7 @@ end
|
|||||||
class Battle::Move::SwitchOutUserStatusMove < Battle::Move
|
class Battle::Move::SwitchOutUserStatusMove < Battle::Move
|
||||||
def pbMoveFailed?(user, targets)
|
def pbMoveFailed?(user, targets)
|
||||||
if user.wild?
|
if user.wild?
|
||||||
if !@battle.pbCanRun?(user.index)
|
if !@battle.pbCanRun?(user.index) || user.allAllies.length > 0
|
||||||
@battle.pbDisplay(_INTL("But it failed!"))
|
@battle.pbDisplay(_INTL("But it failed!"))
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@@ -145,9 +145,9 @@ class Battle::Move::SwitchOutUserPassOnEffects < Battle::Move
|
|||||||
end
|
end
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# In wild battles, makes target flee. Fails if target is a higher level than the
|
# When used against a sole wild Pokémon, makes target flee and ends the battle;
|
||||||
# user.
|
# fails if target is a higher level than the user.
|
||||||
# In trainer battles, target switches out.
|
# When used against a trainer's Pokémon, target switches out.
|
||||||
# For status moves. (Roar, Whirlwind)
|
# For status moves. (Roar, Whirlwind)
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
class Battle::Move::SwitchOutTargetStatusMove < Battle::Move
|
class Battle::Move::SwitchOutTargetStatusMove < Battle::Move
|
||||||
@@ -171,38 +171,40 @@ class Battle::Move::SwitchOutTargetStatusMove < Battle::Move
|
|||||||
@battle.pbDisplay(_INTL("{1} anchored itself with its roots!", target.pbThis)) if show_message
|
@battle.pbDisplay(_INTL("{1} anchored itself with its roots!", target.pbThis)) if show_message
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
if !@battle.canRun
|
if target.wild? && target.allAllies.length == 0 && @battle.canRun
|
||||||
@battle.pbDisplay(_INTL("But it failed!")) if show_message
|
# End the battle
|
||||||
return true
|
if target.level > user.level
|
||||||
end
|
@battle.pbDisplay(_INTL("But it failed!")) if show_message
|
||||||
if @battle.wildBattle? && target.level > user.level
|
return true
|
||||||
@battle.pbDisplay(_INTL("But it failed!")) if show_message
|
end
|
||||||
return true
|
elsif !target.wild?
|
||||||
end
|
# Switch target out
|
||||||
if @battle.trainerBattle?
|
|
||||||
canSwitch = false
|
canSwitch = false
|
||||||
@battle.eachInTeamFromBattlerIndex(target.index) do |_pkmn, i|
|
@battle.eachInTeamFromBattlerIndex(target.index) do |_pkmn, i|
|
||||||
next if !@battle.pbCanSwitchIn?(target.index, i)
|
canSwitch = @battle.pbCanSwitchIn?(target.index, i)
|
||||||
canSwitch = true
|
break if canSwitch
|
||||||
break
|
|
||||||
end
|
end
|
||||||
if !canSwitch
|
if !canSwitch
|
||||||
@battle.pbDisplay(_INTL("But it failed!")) if show_message
|
@battle.pbDisplay(_INTL("But it failed!")) if show_message
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
@battle.pbDisplay(_INTL("But it failed!")) if show_message
|
||||||
|
return true
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
def pbEffectGeneral(user)
|
def pbEffectAgainstTarget(user, target)
|
||||||
@battle.decision = 3 if @battle.wildBattle? # Escaped from battle
|
@battle.decision = 3 if target.wild? # Escaped from battle
|
||||||
end
|
end
|
||||||
|
|
||||||
def pbSwitchOutTargetEffect(user, targets, numHits, switched_battlers)
|
def pbSwitchOutTargetEffect(user, targets, numHits, switched_battlers)
|
||||||
return if @battle.wildBattle? || !switched_battlers.empty?
|
return if !switched_battlers.empty?
|
||||||
return if user.fainted? || numHits == 0
|
return if user.fainted? || numHits == 0
|
||||||
targets.each do |b|
|
targets.each do |b|
|
||||||
next if b.fainted? || b.damageState.unaffected
|
next if b.fainted? || b.damageState.unaffected
|
||||||
|
next if b.wild?
|
||||||
next if b.effects[PBEffects::Ingrain]
|
next if b.effects[PBEffects::Ingrain]
|
||||||
next if b.hasActiveAbility?(:SUCTIONCUPS) && !@battle.moldBreaker
|
next if b.hasActiveAbility?(:SUCTIONCUPS) && !@battle.moldBreaker
|
||||||
newPkmn = @battle.pbGetReplacementPokemonIndex(b.index, true) # Random
|
newPkmn = @battle.pbGetReplacementPokemonIndex(b.index, true) # Random
|
||||||
@@ -218,24 +220,26 @@ class Battle::Move::SwitchOutTargetStatusMove < Battle::Move
|
|||||||
end
|
end
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# In wild battles, makes target flee. Fails if target is a higher level than the
|
# When used against a sole wild Pokémon, makes target flee and ends the battle;
|
||||||
# user.
|
# fails if target is a higher level than the user.
|
||||||
# In trainer battles, target switches out.
|
# When used against a trainer's Pokémon, target switches out.
|
||||||
# For damaging moves. (Circle Throw, Dragon Tail)
|
# For damaging moves. (Circle Throw, Dragon Tail)
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
class Battle::Move::SwitchOutTargetDamagingMove < Battle::Move
|
class Battle::Move::SwitchOutTargetDamagingMove < Battle::Move
|
||||||
def pbEffectAgainstTarget(user, target)
|
def pbEffectAgainstTarget(user, target)
|
||||||
if @battle.wildBattle? && target.level <= user.level && @battle.canRun &&
|
if target.wild? && target.allAllies.length == 0 && @battle.canRun &&
|
||||||
|
target.level <= user.level &&
|
||||||
(target.effects[PBEffects::Substitute] == 0 || ignoresSubstitute?(user))
|
(target.effects[PBEffects::Substitute] == 0 || ignoresSubstitute?(user))
|
||||||
@battle.decision = 3
|
@battle.decision = 3 # Escaped from battle
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def pbSwitchOutTargetEffect(user, targets, numHits, switched_battlers)
|
def pbSwitchOutTargetEffect(user, targets, numHits, switched_battlers)
|
||||||
return if @battle.wildBattle? || !switched_battlers.empty?
|
return if !switched_battlers.empty?
|
||||||
return if user.fainted? || numHits == 0
|
return if user.fainted? || numHits == 0
|
||||||
targets.each do |b|
|
targets.each do |b|
|
||||||
next if b.fainted? || b.damageState.unaffected || b.damageState.substitute
|
next if b.fainted? || b.damageState.unaffected || b.damageState.substitute
|
||||||
|
next if b.wild?
|
||||||
next if b.effects[PBEffects::Ingrain]
|
next if b.effects[PBEffects::Ingrain]
|
||||||
next if b.hasActiveAbility?(:SUCTIONCUPS) && !@battle.moldBreaker
|
next if b.hasActiveAbility?(:SUCTIONCUPS) && !@battle.moldBreaker
|
||||||
newPkmn = @battle.pbGetReplacementPokemonIndex(b.index, true) # Random
|
newPkmn = @battle.pbGetReplacementPokemonIndex(b.index, true) # Random
|
||||||
|
|||||||
@@ -5,13 +5,10 @@ class Battle::AI
|
|||||||
attr_reader :battle
|
attr_reader :battle
|
||||||
attr_reader :trainer
|
attr_reader :trainer
|
||||||
attr_reader :battlers
|
attr_reader :battlers
|
||||||
attr_reader :roles
|
|
||||||
attr_reader :user, :target, :move
|
attr_reader :user, :target, :move
|
||||||
|
|
||||||
def initialize(battle)
|
def initialize(battle)
|
||||||
@battle = battle
|
@battle = battle
|
||||||
@roles = [Array.new(@battle.pbParty(0).length) { |i| determine_roles(0, i) },
|
|
||||||
Array.new(@battle.pbParty(1).length) { |i| determine_roles(1, i) }]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_ai_objects
|
def create_ai_objects
|
||||||
|
|||||||
@@ -97,9 +97,7 @@ class Battle::AI
|
|||||||
reserves.sort! { |a, b| b[1] <=> a[1] } # Sort from highest to lowest rated
|
reserves.sort! { |a, b| b[1] <=> a[1] } # Sort from highest to lowest rated
|
||||||
# Don't bother choosing to switch if all replacements are poorly rated
|
# Don't bother choosing to switch if all replacements are poorly rated
|
||||||
if @trainer.high_skill? && !mandatory
|
if @trainer.high_skill? && !mandatory
|
||||||
# TODO: Should the current battler be rated as well, to provide a
|
return -1 if reserves[0][1] < 100 # If best replacement rated at <100, don't switch
|
||||||
# threshold instead of using a threshold of 100?
|
|
||||||
return -1 if reserves[0][1] < 100 # Best replacement rated at <100, don't switch
|
|
||||||
end
|
end
|
||||||
# Return the party index of the best rated replacement Pokémon
|
# Return the party index of the best rated replacement Pokémon
|
||||||
return reserves[0][0]
|
return reserves[0][0]
|
||||||
@@ -136,9 +134,8 @@ class Battle::AI
|
|||||||
pkmn.moves.each do |m|
|
pkmn.moves.each do |m|
|
||||||
next if m.power == 0 || (m.pp == 0 && m.total_pp > 0)
|
next if m.power == 0 || (m.pp == 0 && m.total_pp > 0)
|
||||||
@battle.battlers[idxBattler].allOpposing.each do |b|
|
@battle.battlers[idxBattler].allOpposing.each do |b|
|
||||||
|
next if pokemon_can_absorb_move?(b.pokemon, m, m.type)
|
||||||
bTypes = b.pbTypes(true)
|
bTypes = b.pbTypes(true)
|
||||||
# TODO: Consider Wonder Guard, Volt Absorb et al. Consider pkmn's
|
|
||||||
# ability if it changes the user's types or powers up their moves?
|
|
||||||
score += m.power * Effectiveness.calculate(m.type, *bTypes) / 10
|
score += m.power * Effectiveness.calculate(m.type, *bTypes) / 10
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -175,7 +172,6 @@ end
|
|||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# Pokémon is about to faint because of Perish Song.
|
# Pokémon is about to faint because of Perish Song.
|
||||||
# TODO: Also switch to remove other negative effects like Disable, Yawn.
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
Battle::AI::Handlers::ShouldSwitch.add(:perish_song,
|
Battle::AI::Handlers::ShouldSwitch.add(:perish_song,
|
||||||
proc { |battler, reserves, ai, battle|
|
proc { |battler, reserves, ai, battle|
|
||||||
@@ -443,65 +439,60 @@ Battle::AI::Handlers::ShouldSwitch.add(:battler_is_useless,
|
|||||||
)
|
)
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# Pokémon can't do anything to a Wonder Guard foe.
|
# Pokémon can't do anything to any foe because its ability absorbs all damage
|
||||||
# TODO: Check other abilities that provide immunities?
|
# the Pokémon can deal out.
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
Battle::AI::Handlers::ShouldSwitch.add(:foe_has_wonder_guard,
|
Battle::AI::Handlers::ShouldSwitch.add(:foe_absorbs_all_moves_with_its_ability,
|
||||||
proc { |battler, reserves, ai, battle|
|
proc { |battler, reserves, ai, battle|
|
||||||
|
next false if battler.battler.turnCount < 2 # Don't switch out too quickly
|
||||||
next false if battler.battler.hasMoldBreaker?
|
next false if battler.battler.hasMoldBreaker?
|
||||||
non_wonder_guard_foe_exists = false
|
# Check if battler can damage any of its foes
|
||||||
has_super_effective_move = false
|
can_damage_foe = false
|
||||||
foe_types = battler.pbTypes(true)
|
|
||||||
next false if foe_types.length == 0
|
|
||||||
ai.each_foe_battler(battler.side) do |b, i|
|
ai.each_foe_battler(battler.side) do |b, i|
|
||||||
if !b.has_active_ability?(:WONDERGUARD)
|
|
||||||
non_wonder_guard_foe_exists = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
if ai.trainer.high_skill? && b.rough_end_of_round_damage > 0
|
if ai.trainer.high_skill? && b.rough_end_of_round_damage > 0
|
||||||
non_wonder_guard_foe_exists = true # Wonder Guard is being overcome already
|
can_damage_foe = true # Foe is being damaged already
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
# Check for super-effective damaging moves
|
# Check for battler's moves that can damage the foe (b)
|
||||||
battler.battler.eachMove do |move|
|
battler.battler.eachMove do |move|
|
||||||
next if move.statusMove?
|
next if move.statusMove?
|
||||||
if ["IgnoreTargetAbility",
|
if ["IgnoreTargetAbility",
|
||||||
"CategoryDependsOnHigherDamageIgnoreTargetAbility"].include?(move.function)
|
"CategoryDependsOnHigherDamageIgnoreTargetAbility"].include?(move.function)
|
||||||
has_super_effective_move = true
|
can_damage_foe = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
eff = Effectiveness.calculate(move.pbCalcType(battler.battler), *foe_types)
|
if !ai.pokemon_can_absorb_move?(b, move, move.pbCalcType(battler.battler))
|
||||||
if Effectiveness.super_effective?(eff)
|
can_damage_foe = true
|
||||||
has_super_effective_move = true
|
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
break if has_super_effective_move
|
break if can_damage_foe
|
||||||
end
|
end
|
||||||
if !non_wonder_guard_foe_exists && !has_super_effective_move
|
next false if can_damage_foe
|
||||||
# Check reserves for super-effective moves; only switch if there are any
|
# Check if a reserve could damage any foe; only switch if one could
|
||||||
reserve_has_super_effective_move = false
|
reserve_can_damage_foe = false
|
||||||
reserves.each do |pkmn|
|
reserves.each do |pkmn|
|
||||||
|
ai.each_foe_battler(battler.side) do |b, i|
|
||||||
|
# Check for reserve's moves that can damage the foe (b)
|
||||||
pkmn.moves.each do |move|
|
pkmn.moves.each do |move|
|
||||||
next if move.status_move?
|
next if move.status_move?
|
||||||
if ["IgnoreTargetAbility",
|
if ["IgnoreTargetAbility",
|
||||||
"CategoryDependsOnHigherDamageIgnoreTargetAbility"].include?(move.function_code)
|
"CategoryDependsOnHigherDamageIgnoreTargetAbility"].include?(move.function_code)
|
||||||
reserve_has_super_effective_move = true
|
reserve_can_damage_foe = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
eff = Effectiveness.calculate(move.type, *foe_types)
|
if !ai.pokemon_can_absorb_move?(b, move, move.type)
|
||||||
if Effectiveness.super_effective?(eff)
|
reserve_can_damage_foe = true
|
||||||
reserve_has_super_effective_move = true
|
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
break if reserve_has_super_effective_move
|
break if reserve_can_damage_foe
|
||||||
end
|
end
|
||||||
next false if !reserve_has_super_effective_move
|
break if reserve_can_damage_foe
|
||||||
PBDebug.log_ai("#{battler.name} wants to switch because it can't do anything against Wonder Guard")
|
|
||||||
next true
|
|
||||||
end
|
end
|
||||||
next false
|
next false if !reserve_can_damage_foe
|
||||||
|
PBDebug.log_ai("#{battler.name} wants to switch because it can't damage the foe(s)")
|
||||||
|
next true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -576,7 +567,6 @@ Battle::AI::Handlers::ShouldSwitch.add(:sudden_death,
|
|||||||
#===============================================================================
|
#===============================================================================
|
||||||
# Pokémon is within 5 levels of the foe, and foe's last move was super-effective
|
# Pokémon is within 5 levels of the foe, and foe's last move was super-effective
|
||||||
# and powerful.
|
# and powerful.
|
||||||
# TODO: Review switch deciding.
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
Battle::AI::Handlers::ShouldSwitch.add(:high_damage_from_foe,
|
Battle::AI::Handlers::ShouldSwitch.add(:high_damage_from_foe,
|
||||||
proc { |battler, reserves, ai, battle|
|
proc { |battler, reserves, ai, battle|
|
||||||
@@ -653,7 +643,7 @@ Battle::AI::Handlers::ShouldNotSwitch.add(:battler_has_super_effective_move,
|
|||||||
# NOTE: Ideally this would ignore foes that move cannot target, but that
|
# NOTE: Ideally this would ignore foes that move cannot target, but that
|
||||||
# is complicated enough to implement that I'm not bothering. It's
|
# is complicated enough to implement that I'm not bothering. It's
|
||||||
# also rare that it would matter.
|
# also rare that it would matter.
|
||||||
eff = b.effectiveness_of_type_against_battler(move_type, battler)
|
eff = b.effectiveness_of_type_against_battler(move_type, battler, move)
|
||||||
has_super_effective_move = Effectiveness.super_effective?(eff)
|
has_super_effective_move = Effectiveness.super_effective?(eff)
|
||||||
break if has_super_effective_move
|
break if has_super_effective_move
|
||||||
end
|
end
|
||||||
@@ -670,8 +660,6 @@ Battle::AI::Handlers::ShouldNotSwitch.add(:battler_has_super_effective_move,
|
|||||||
#===============================================================================
|
#===============================================================================
|
||||||
# Don't bother switching if the battler has 4 or more positive stat stages.
|
# Don't bother switching if the battler has 4 or more positive stat stages.
|
||||||
# Negative stat stages are ignored.
|
# Negative stat stages are ignored.
|
||||||
# TODO: Ignore this if deciding whether to use Baton Pass (assuming move-scoring
|
|
||||||
# uses this code).
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
Battle::AI::Handlers::ShouldNotSwitch.add(:battler_has_very_raised_stats,
|
Battle::AI::Handlers::ShouldNotSwitch.add(:battler_has_very_raised_stats,
|
||||||
proc { |battler, reserves, ai, battle|
|
proc { |battler, reserves, ai, battle|
|
||||||
|
|||||||
@@ -146,34 +146,31 @@ Battle::AI::Handlers::GeneralMoveScore.add(:any_battler_can_Snatch_move,
|
|||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# Pick a good move for the Choice items.
|
# Pick a good move for the Choice items.
|
||||||
# TODO: Review score modifier.
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
Battle::AI::Handlers::GeneralMoveScore.add(:good_move_for_choice_item,
|
Battle::AI::Handlers::GeneralMoveScore.add(:good_move_for_choice_item,
|
||||||
proc { |score, move, user, ai, battle|
|
proc { |score, move, user, ai, battle|
|
||||||
if ai.trainer.medium_skill?
|
next score if !ai.trainer.medium_skill?
|
||||||
if user.has_active_item?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF]) ||
|
next score if !user.has_active_item?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF]) &&
|
||||||
user.has_active_ability?(:GORILLATACTICS)
|
!user.has_active_ability?(:GORILLATACTICS)
|
||||||
old_score = score
|
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"
|
||||||
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
|
||||||
end
|
|
||||||
# Don't prefer moves which are 0x against at least one type
|
|
||||||
move_type = move.rough_type
|
|
||||||
GameData::Type.each do |type_data|
|
|
||||||
score -= 8 if type_data.immunities.include?(move_type)
|
|
||||||
end
|
|
||||||
# Don't prefer moves with lower accuracy
|
|
||||||
if move.accuracy > 0
|
|
||||||
score -= (0.4 * (100 - move.accuracy)).to_i # -0 (100%) to -39 (1%)
|
|
||||||
end
|
|
||||||
# Don't prefer moves with low PP
|
|
||||||
score -= 10 if move.move.pp <= 5
|
|
||||||
PBDebug.log_score_change(score - old_score, "move is less suitable to be Choiced into")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
# Don't prefer moves which are 0x against at least one type
|
||||||
|
move_type = move.rough_type
|
||||||
|
GameData::Type.each do |type_data|
|
||||||
|
score -= 8 if type_data.immunities.include?(move_type)
|
||||||
|
end
|
||||||
|
# Don't prefer moves with lower accuracy
|
||||||
|
if move.accuracy > 0
|
||||||
|
score -= (0.4 * (100 - move.accuracy)).to_i # -0 (100%) to -39 (1%)
|
||||||
|
end
|
||||||
|
# Don't prefer moves with low PP
|
||||||
|
score -= 10 if move.move.pp <= 5
|
||||||
|
PBDebug.log_score_change(score - old_score, "move is less suitable to be Choiced into")
|
||||||
next score
|
next score
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -205,22 +202,42 @@ Battle::AI::Handlers::GeneralMoveScore.add(:damaging_move_and_either_side_no_res
|
|||||||
)
|
)
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
#
|
# Don't prefer Fire-type moves if target knows Powder and is faster than the
|
||||||
|
# user.
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# TODO: Don't prefer Fire-type moves if target has previously used Powder and is
|
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:target_can_powder_fire_moves,
|
||||||
# faster than the user.
|
proc { |score, move, user, target, ai, battle|
|
||||||
|
if ai.trainer.high_skill? && move.rough_type == :FIRE &&
|
||||||
|
target.has_move_with_function?("TargetNextFireMoveDamagesTarget") &&
|
||||||
|
target.faster_than?(user)
|
||||||
|
old_score = score
|
||||||
|
score -= 5 # Only 5 because we're not sure target will use Powder
|
||||||
|
PBDebug.log_score_change(score - old_score, "target knows Powder and could negate Fire moves")
|
||||||
|
end
|
||||||
|
next score
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
#
|
# Don't prefer moves if target knows a move that can make them Electric-type,
|
||||||
|
# and if target is unaffected by Electric moves.
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# TODO: Don't prefer Normal-type moves if target has previously used Ion Deluge
|
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:target_can_make_moves_Electric_and_be_immune,
|
||||||
# and is immune to Electric moves.
|
proc { |score, move, user, target, ai, battle|
|
||||||
|
next score if !ai.trainer.high_skill?
|
||||||
#===============================================================================
|
next score if !target.has_move_with_function?("TargetMovesBecomeElectric") &&
|
||||||
#
|
!(move.rough_type == :NORMAL && target.has_move_with_function?("NormalMovesBecomeElectric"))
|
||||||
#===============================================================================
|
next score if !ai.pokemon_can_absorb_move?(target, move, :ELECTRIC) &&
|
||||||
# TODO: Don't prefer a move that is stopped by Wide Guard if any foe has
|
!Effectiveness.ineffective?(target.effectiveness_of_type_against_battler(:ELECTRIC, user))
|
||||||
# previously used Wide Guard.
|
priority = move.rough_priority(user)
|
||||||
|
if priority > 0 || (priority == 0 && target.faster_than?(user)) # Target goes first
|
||||||
|
old_score = score
|
||||||
|
score -= 5 # Only 5 because we're not sure target will use Electrify/Ion Deluge
|
||||||
|
PBDebug.log_score_change(score - old_score, "target knows Electrify/Ion Deluge and is immune to Electric moves")
|
||||||
|
end
|
||||||
|
next score
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# Don't prefer attacking the target if they'd be semi-invulnerable.
|
# Don't prefer attacking the target if they'd be semi-invulnerable.
|
||||||
@@ -293,6 +310,12 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:predicted_damage,
|
|||||||
old_score = score
|
old_score = score
|
||||||
score += 10
|
score += 10
|
||||||
PBDebug.log_score_change(score - old_score, "predicted to KO the target")
|
PBDebug.log_score_change(score - old_score, "predicted to KO the target")
|
||||||
|
if move.move.multiHitMove? && target.hp == target.totalhp &&
|
||||||
|
(target.has_active_ability?(:STURDY) || target.has_active_item?(:FOCUSSASH))
|
||||||
|
old_score = score
|
||||||
|
score += 8
|
||||||
|
PBDebug.log_score_change(score - old_score, "predicted to overcome the target's Sturdy/Focus Sash")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -313,6 +336,7 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:external_flinching_effe
|
|||||||
if battle.moldBreaker || !target.has_active_ability?([:INNERFOCUS, :SHIELDDUST])
|
if battle.moldBreaker || !target.has_active_ability?([:INNERFOCUS, :SHIELDDUST])
|
||||||
old_score = score
|
old_score = score
|
||||||
score += 8
|
score += 8
|
||||||
|
score += 5 if move.move.multiHitMove?
|
||||||
PBDebug.log_score_change(score - old_score, "added chance to cause flinching")
|
PBDebug.log_score_change(score - old_score, "added chance to cause flinching")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -338,25 +362,67 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:thawing_move_against_fr
|
|||||||
)
|
)
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
#
|
# Don't prefer a damaging move if it will trigger the target's ability or held
|
||||||
|
# item when used, e.g. Effect Spore/Rough Skin, Pickpocket, Rocky Helmet, Red
|
||||||
|
# Card.
|
||||||
|
# NOTE: These abilities/items may not be triggerable after all (e.g. they
|
||||||
|
# require the move to make contact but it doesn't), or may have a negative
|
||||||
|
# effect for the target (e.g. Air Balloon popping), but it's too much
|
||||||
|
# effort to go into detail deciding all this.
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# TODO: Check all effects that trigger upon using a move, including per-hit
|
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:trigger_target_ability_or_item_upon_hit,
|
||||||
# stuff in def pbEffectsOnMakingHit (worse if the move is a multi-hit one)
|
proc { |score, move, user, target, ai, battle|
|
||||||
# and end-of-move stuff in def pbEffectsAfterMove.
|
if ai.trainer.high_skill? && move.damagingMove? && target.effects[PBEffects::Substitute] == 0
|
||||||
|
if target.ability_active?
|
||||||
|
if Battle::AbilityEffects::OnBeingHit[target.ability] ||
|
||||||
|
(Battle::AbilityEffects::AfterMoveUseFromTarget[target.ability] &&
|
||||||
|
(!user.has_active_ability?(:SHEERFORCE) || move.move.addlEffect == 0))
|
||||||
|
old_score = score
|
||||||
|
score += 8
|
||||||
|
PBDebug.log_score_change(score - old_score, "can trigger the target's ability")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if target.battler.isSpecies?(:CRAMORANT) && target.ability == :GULPMISSILE &&
|
||||||
|
target.battler.form > 0 && !target.effects[PBEffects::Transform]
|
||||||
|
old_score = score
|
||||||
|
score += 8
|
||||||
|
PBDebug.log_score_change(score - old_score, "can trigger the target's ability")
|
||||||
|
end
|
||||||
|
if target.item_active?
|
||||||
|
if Battle::ItemEffects::OnBeingHit[target.item] ||
|
||||||
|
(Battle::ItemEffects::AfterMoveUseFromTarget[target.item] &&
|
||||||
|
(!user.has_active_ability?(:SHEERFORCE) || move.move.addlEffect == 0))
|
||||||
|
old_score = score
|
||||||
|
score += 8
|
||||||
|
PBDebug.log_score_change(score - old_score, "can trigger the target's item")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
next score
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
#
|
# Prefer a damaging move if it will trigger the user's ability when used, e.g.
|
||||||
|
# Poison Touch, Magician.
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# TODO: Prefer a contact move if making contact with the target could trigger
|
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:trigger_user_ability_upon_hit,
|
||||||
# an effect that's good for the user (Poison Touch/Pickpocket).
|
proc { |score, move, user, target, ai, battle|
|
||||||
|
if ai.trainer.high_skill? && user.ability_active? && move.damagingMove? &&
|
||||||
#===============================================================================
|
target.effects[PBEffects::Substitute] == 0
|
||||||
#
|
# NOTE: The only ability with an OnDealingHit effect also requires the
|
||||||
#===============================================================================
|
# move to make contact. The only abilities with an OnEndOfUsingMove
|
||||||
# TODO: Don't prefer contact move if making contact with the target could
|
# effect revolve around damaging moves.
|
||||||
# trigger an effect that's bad for the user (Static, etc.).
|
if (Battle::AbilityEffects::OnDealingHit[user.ability] && move.move.contactMove?) ||
|
||||||
# => Also check if target has previously used Spiky Shield/King's Shield/
|
Battle::AbilityEffects::OnEndOfUsingMove[user.ability]
|
||||||
# Baneful Bunker, and don't prefer move if so
|
old_score = score
|
||||||
|
score += 8
|
||||||
|
PBDebug.log_score_change(score - old_score, "can trigger the user's ability")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
next score
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# Don't prefer damaging moves that will knock out the target if they are using
|
# Don't prefer damaging moves that will knock out the target if they are using
|
||||||
@@ -387,10 +453,23 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:knocking_out_a_destiny_
|
|||||||
)
|
)
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
#
|
# Don't prefer damaging moves if the target is using Rage, unless the move will
|
||||||
|
# deal enough damage to KO the target within two rounds.
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# TODO: Don't prefer damaging moves if the target is using Rage and they benefit
|
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:damaging_a_raging_target,
|
||||||
# from the raised Attack.
|
proc { |score, move, user, target, ai, battle|
|
||||||
|
if ai.trainer.medium_skill? && target.effects[PBEffects::Rage] && move.damagingMove?
|
||||||
|
# Worth damaging the target if it can be knocked out within two rounds
|
||||||
|
if ai.trainer.has_skill_flag?("HPAware")
|
||||||
|
next score if (move.rough_damage + target.rough_end_of_round_damage) * 2 > target.hp * 1.1
|
||||||
|
end
|
||||||
|
old_score = score
|
||||||
|
score -= 10
|
||||||
|
PBDebug.log_score_change(score - old_score, "don't want to damage a Raging target")
|
||||||
|
end
|
||||||
|
next score
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# Don't prefer damaging moves if the target is Biding, unless the move will deal
|
# Don't prefer damaging moves if the target is Biding, unless the move will deal
|
||||||
|
|||||||
@@ -38,14 +38,16 @@ class Battle::AI
|
|||||||
|
|
||||||
# Assumes that pkmn's ability is not negated by a global effect (e.g.
|
# Assumes that pkmn's ability is not negated by a global effect (e.g.
|
||||||
# Neutralizing Gas).
|
# Neutralizing Gas).
|
||||||
# pkmn is either a Battle::AI::AIBattler or a Pokemon. move is a Battle::Move.
|
# pkmn is either a Battle::AI::AIBattler or a Pokemon.
|
||||||
|
# move is a Battle::Move or a Pokemon::Move.
|
||||||
def pokemon_can_absorb_move?(pkmn, move, move_type)
|
def pokemon_can_absorb_move?(pkmn, move, move_type)
|
||||||
return false if pkmn.is_a?(Battle::AI::AIBattler) && !pkmn.ability_active?
|
return false if pkmn.is_a?(Battle::AI::AIBattler) && !pkmn.ability_active?
|
||||||
# Check pkmn's ability
|
# Check pkmn's ability
|
||||||
# Anything with a Battle::AbilityEffects::MoveImmunity handler
|
# Anything with a Battle::AbilityEffects::MoveImmunity handler
|
||||||
case pkmn.ability_id
|
case pkmn.ability_id
|
||||||
when :BULLETPROOF
|
when :BULLETPROOF
|
||||||
return move.bombMove?
|
move_data = GameData::Move.get(move.id)
|
||||||
|
return move_data.has_flag?("Bomb")
|
||||||
when :FLASHFIRE
|
when :FLASHFIRE
|
||||||
return move_type == :FIRE
|
return move_type == :FIRE
|
||||||
when :LIGHTNINGROD, :MOTORDRIVE, :VOLTABSORB
|
when :LIGHTNINGROD, :MOTORDRIVE, :VOLTABSORB
|
||||||
@@ -53,7 +55,8 @@ class Battle::AI
|
|||||||
when :SAPSIPPER
|
when :SAPSIPPER
|
||||||
return move_type == :GRASS
|
return move_type == :GRASS
|
||||||
when :SOUNDPROOF
|
when :SOUNDPROOF
|
||||||
return move.soundMove?
|
move_data = GameData::Move.get(move.id)
|
||||||
|
return move_data.has_flag?("Sound")
|
||||||
when :STORMDRAIN, :WATERABSORB, :DRYSKIN
|
when :STORMDRAIN, :WATERABSORB, :DRYSKIN
|
||||||
return move_type == :WATER
|
return move_type == :WATER
|
||||||
when :TELEPATHY
|
when :TELEPATHY
|
||||||
@@ -62,7 +65,7 @@ class Battle::AI
|
|||||||
when :WONDERGUARD
|
when :WONDERGUARD
|
||||||
types = pkmn.types
|
types = pkmn.types
|
||||||
types = pkmn.pbTypes(true) if pkmn.is_a?(Battle::AI::AIBattler)
|
types = pkmn.pbTypes(true) if pkmn.is_a?(Battle::AI::AIBattler)
|
||||||
return Effectiveness.super_effective_type?(move_type, *types)
|
return !Effectiveness.super_effective_type?(move_type, *types)
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
@@ -157,24 +160,72 @@ class Battle::AI
|
|||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
# TODO: Add more items.
|
|
||||||
BASE_ITEM_RATINGS = {
|
BASE_ITEM_RATINGS = {
|
||||||
4 => [:CHOICEBAND, :CHOICESCARF, :CHOICESPECS, :DEEPSEATOOTH, :LEFTOVERS,
|
10 => [:EVIOLITE, :FOCUSSASH, :LIFEORB, :THICKCLUB],
|
||||||
:LIGHTBALL, :THICKCLUB],
|
9 => [:ASSAULTVEST, :BLACKSLUDGE, :CHOICEBAND, :CHOICESCARF, :CHOICESPECS,
|
||||||
3 => [:ADAMANTORB, :GRISEOUSORB, :LIFEORB, :LUSTROUSORB, :SOULDEW],
|
:DEEPSEATOOTH, :LEFTOVERS],
|
||||||
2 => [:BLACKBELT, :BLACKGLASSES, :CHARCOAL, :DRAGONFANG, :HARDSTONE,
|
8 => [:LEEK, :STICK, :THROATSPRAY, :WEAKNESSPOLICY],
|
||||||
|
7 => [:EXPERTBELT, :LIGHTBALL, :LUMBERRY, :POWERHERB, :ROCKYHELMET,
|
||||||
|
:SITRUSBERRY],
|
||||||
|
6 => [:KINGSROCK, :LIECHIBERRY, :LIGHTCLAY, :PETAYABERRY, :RAZORFANG,
|
||||||
|
:REDCARD, :SALACBERRY, :SHELLBELL, :WHITEHERB,
|
||||||
|
# Type-resisting berries
|
||||||
|
:BABIRIBERRY, :CHARTIBERRY, :CHILANBERRY, :CHOPLEBERRY, :COBABERRY,
|
||||||
|
:COLBURBERRY, :HABANBERRY, :KASIBBERRY, :KEBIABERRY, :OCCABERRY,
|
||||||
|
:PASSHOBERRY, :PAYAPABERRY, :RINDOBERRY, :ROSELIBERRY, :SHUCABERRY,
|
||||||
|
:TANGABERRY, :WACANBERRY, :YACHEBERRY,
|
||||||
|
# Gems
|
||||||
|
:BUGGEM, :DARKGEM, :DRAGONGEM, :ELECTRICGEM, :FAIRYGEM, :FIGHTINGGEM,
|
||||||
|
:FIREGEM, :FLYINGGEM, :GHOSTGEM, :GRASSGEM, :GROUNDGEM, :ICEGEM,
|
||||||
|
:NORMALGEM, :POISONGEM, :PSYCHICGEM, :ROCKGEM, :STEELGEM, :WATERGEM,
|
||||||
|
# Legendary Orbs
|
||||||
|
:ADAMANTORB, :GRISEOUSORB, :LUSTROUSORB, :SOULDEW,
|
||||||
|
# Berries that heal HP and may confuse
|
||||||
|
:AGUAVBERRY, :FIGYBERRY, :IAPAPABERRY, :MAGOBERRY, :WIKIBERRY],
|
||||||
|
5 => [:CUSTAPBERRY, :DEEPSEASCALE, :EJECTBUTTON, :FOCUSBAND, :JABOCABERRY,
|
||||||
|
:KEEBERRY, :LANSATBERRY, :MARANGABERRY, :MENTALHERB, :METRONOME,
|
||||||
|
:MUSCLEBAND, :QUICKCLAW, :RAZORCLAW, :ROWAPBERRY, :SCOPELENS,
|
||||||
|
:WISEGLASSES,
|
||||||
|
# Type power boosters
|
||||||
|
:BLACKBELT, :BLACKGLASSES, :CHARCOAL, :DRAGONFANG, :HARDSTONE,
|
||||||
:MAGNET, :METALCOAT, :MIRACLESEED, :MYSTICWATER, :NEVERMELTICE,
|
:MAGNET, :METALCOAT, :MIRACLESEED, :MYSTICWATER, :NEVERMELTICE,
|
||||||
:POISONBARB, :SHARPBEAK, :SILKSCARF, :SILVERPOWDER, :SOFTSAND,
|
:POISONBARB, :SHARPBEAK, :SILKSCARF,:SILVERPOWDER, :SOFTSAND,
|
||||||
:SPELLTAG, :TWISTEDSPOON,
|
:SPELLTAG, :TWISTEDSPOON,
|
||||||
|
:ODDINCENSE, :ROCKINCENSE, :ROSEINCENSE, :SEAINCENSE, :WAVEINCENSE,
|
||||||
|
# Plates
|
||||||
:DRACOPLATE, :DREADPLATE, :EARTHPLATE, :FISTPLATE, :FLAMEPLATE,
|
:DRACOPLATE, :DREADPLATE, :EARTHPLATE, :FISTPLATE, :FLAMEPLATE,
|
||||||
:ICICLEPLATE, :INSECTPLATE, :IRONPLATE, :MEADOWPLATE, :MINDPLATE,
|
:ICICLEPLATE, :INSECTPLATE, :IRONPLATE, :MEADOWPLATE, :MINDPLATE,
|
||||||
:PIXIEPLATE, :SKYPLATE, :SPLASHPLATE, :SPOOKYPLATE, :STONEPLATE,
|
:PIXIEPLATE, :SKYPLATE, :SPLASHPLATE, :SPOOKYPLATE, :STONEPLATE,
|
||||||
:TOXICPLATE, :ZAPPLATE,
|
:TOXICPLATE, :ZAPPLATE,
|
||||||
:ODDINCENSE, :ROCKINCENSE, :ROSEINCENSE, :SEAINCENSE, :WAVEINCENSE,
|
# Weather/terrain extenders
|
||||||
:MUSCLEBAND, :WISEGLASSES],
|
:DAMPROCK, :HEATROCK, :ICYROCK, :SMOOTHROCK, :TERRAINEXTENDER],
|
||||||
1 => [:METRONOME],
|
4 => [:ADRENALINEORB, :APICOTBERRY, :BLUNDERPOLICY, :CHESTOBERRY,
|
||||||
-2 => [:LAGGINGTAIL, :STICKYBARB],
|
:EJECTPACK, :ENIGMABERRY, :GANLONBERRY, :HEAVYDUTYBOOTS,
|
||||||
-4 => [:BLACKSLUDGE, :FLAMEORB, :IRONBALL, :TOXICORB]
|
:ROOMSERVICE, :SAFETYGOGGLES, :SHEDSHELL, :STARFBERRY],
|
||||||
|
3 => [:BIGROOT, :BRIGHTPOWDER, :LAXINCENSE, :LEPPABERRY, :PERSIMBERRY,
|
||||||
|
:PROTECTIVEPADS, :UTILITYUMBRELLA,
|
||||||
|
# Status problem-curing berries (except Chesto which is in 4)
|
||||||
|
:ASPEARBERRY, :CHERIBERRY, :PECHABERRY, :RAWSTBERRY],
|
||||||
|
2 => [:ABSORBBULB, :BERRYJUICE, :CELLBATTERY, :GRIPCLAW, :LUMINOUSMOSS,
|
||||||
|
:MICLEBERRY, :ORANBERRY, :SNOWBALL, :WIDELENS, :ZOOMLENS,
|
||||||
|
# Terrain seeds
|
||||||
|
:ELECTRICSEED, :GRASSYSEED, :MISTYSEED, :PSYCHICSEED],
|
||||||
|
1 => [:AIRBALLOON, :BINDINGBAND, :DESTINYKNOT, :FLOATSTONE, :LUCKYPUNCH,
|
||||||
|
:METALPOWDER, :QUICKPOWDER,
|
||||||
|
# Drives
|
||||||
|
:BURNDRIVE, :CHILLDRIVE, :DOUSEDRIVE, :SHOCKDRIVE,
|
||||||
|
# Memories
|
||||||
|
:BUGMEMORY, :DARKMEMORY, :DRAGONMEMORY, :ELECTRICMEMORY,
|
||||||
|
:FAIRYMEMORY, :FIGHTINGMEMORY, :FIREMEMORY, :FLYINGMEMORY,
|
||||||
|
:GHOSTMEMORY, :GRASSMEMORY, :GROUNDMEMORY, :ICEMEMORY, :POISONMEMORY,
|
||||||
|
:PSYCHICMEMORY, :ROCKMEMORY, :STEELMEMORY, :WATERMEMORY
|
||||||
|
],
|
||||||
|
0 => [:SMOKEBALL],
|
||||||
|
-5 => [:FULLINCENSE, :LAGGINGTAIL, :RINGTARGET],
|
||||||
|
-6 => [:MACHOBRACE, :POWERANKLET, :POWERBAND, :POWERBELT, :POWERBRACER,
|
||||||
|
:POWERLENS, :POWERWEIGHT],
|
||||||
|
-7 => [:FLAMEORB, :IRONBALL, :TOXICORB],
|
||||||
|
-9 => [:STICKYBARB]
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -342,9 +393,71 @@ Battle::AI::Handlers::ItemRanking.add(:ADAMANTORB,
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:AGUAVBERRY,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
if Settings::MECHANICS_GENERATION == 7 # Heals 50%
|
||||||
|
score += 2
|
||||||
|
elsif Settings::MECHANICS_GENERATION <= 6 # Heals 12.5%
|
||||||
|
score -= 3
|
||||||
|
end
|
||||||
|
if ai.trainer.high_skill?
|
||||||
|
if battler.battler.nature.stat_changes.any? { |val| val[0] == :SPECIAL_DEFENSE && val[1] < 0 }
|
||||||
|
score -= 2 # Will confuse
|
||||||
|
end
|
||||||
|
end
|
||||||
|
next score
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:ASSAULTVEST,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
if ai.trainer.high_skill?
|
||||||
|
score += 1 if !battler.check_for_move { |m| m.statusMove? && !m.is_a?(Battle::Move::UseMoveTargetIsAboutToUse) }
|
||||||
|
end
|
||||||
|
next score
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:BERRYJUICE,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
next [10 - (battler.totalhp / 15), 1].max
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:BIGROOT,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
next score if battler.check_for_move do |m|
|
||||||
|
m.is_a?(Battle::Move::HealUserByHalfOfDamageDone) ||
|
||||||
|
m.is_a?(Battle::Move::HealUserByHalfOfDamageDoneIfTargetAsleep) ||
|
||||||
|
m.is_a?(Battle::Move::HealUserByThreeQuartersOfDamageDone) ||
|
||||||
|
m.is_a?(Battle::Move::HealUserByTargetAttackLowerTargetAttack1) ||
|
||||||
|
m.is_a?(Battle::Move::StartLeechSeedTarget)
|
||||||
|
end
|
||||||
|
next 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:BINDINGBAND,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
next score if battler.check_for_move { |m| m.is_a?(Battle::Move::BindTarget) }
|
||||||
|
next 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.copy(:BINDINGBAND, :GRIPCLAW)
|
||||||
|
|
||||||
Battle::AI::Handlers::ItemRanking.add(:BLACKSLUDGE,
|
Battle::AI::Handlers::ItemRanking.add(:BLACKSLUDGE,
|
||||||
proc { |item, score, battler, ai|
|
proc { |item, score, battler, ai|
|
||||||
next 4 if battler.has_type?(:POISON)
|
next score if battler.has_type?(:POISON)
|
||||||
|
next -9
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:CHESTOBERRY,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
if ai.trainer.high_skill?
|
||||||
|
score += 1 if battler.has_move_with_function("HealUserFullyAndFallAsleep")
|
||||||
|
end
|
||||||
next score
|
next score
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -367,6 +480,20 @@ Battle::AI::Handlers::ItemRanking.add(:CHOICESPECS,
|
|||||||
|
|
||||||
Battle::AI::Handlers::ItemRanking.copy(:CHOICESPECS, :WISEGLASSES)
|
Battle::AI::Handlers::ItemRanking.copy(:CHOICESPECS, :WISEGLASSES)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:DEEPSEASCALE,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
next score if battler.battler.isSpecies?(:CLAMPERL)
|
||||||
|
next 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:DAMPROCK,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
next score if battler.check_for_move { |m| m.is_a?(Battle::Move::StartRainWeather) }
|
||||||
|
next 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
Battle::AI::Handlers::ItemRanking.add(:DEEPSEATOOTH,
|
Battle::AI::Handlers::ItemRanking.add(:DEEPSEATOOTH,
|
||||||
proc { |item, score, battler, ai|
|
proc { |item, score, battler, ai|
|
||||||
next score if battler.battler.isSpecies?(:CLAMPERL) &&
|
next score if battler.battler.isSpecies?(:CLAMPERL) &&
|
||||||
@@ -375,6 +502,62 @@ Battle::AI::Handlers::ItemRanking.add(:DEEPSEATOOTH,
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:ELECTRICSEED,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
next score if battler.check_for_move { |m| m.is_a?(Battle::Move::StartElectricTerrain) }
|
||||||
|
next 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:EVIOLITE,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
next score if battler.battler.pokemon.species_data.get_evolutions(true).length > 0
|
||||||
|
next 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:FIGYBERRY,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
if Settings::MECHANICS_GENERATION == 7 # Heals 50%
|
||||||
|
score += 2
|
||||||
|
elsif Settings::MECHANICS_GENERATION <= 6 # Heals 12.5%
|
||||||
|
score -= 3
|
||||||
|
end
|
||||||
|
if ai.trainer.high_skill?
|
||||||
|
if battler.battler.nature.stat_changes.any? { |val| val[0] == :ATTACK && val[1] < 0 }
|
||||||
|
score -= 2 # Will confuse
|
||||||
|
end
|
||||||
|
end
|
||||||
|
next score
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:FLAMEORB,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
next 0 if battler.status != :NONE
|
||||||
|
next 7 if battler.wants_status_problem?(:BURN)
|
||||||
|
next score
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:FULLINCENSE,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
if ai.trainer.high_skill?
|
||||||
|
score = 7 if battler.has_active_ability?(:ANALYTIC)
|
||||||
|
end
|
||||||
|
next score
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.copy(:FULLINCENSE, :LAGGINGTAIL)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:GRASSYSEED,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
next score if battler.check_for_move { |m| m.is_a?(Battle::Move::StartGrassyTerrain) }
|
||||||
|
next 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
Battle::AI::Handlers::ItemRanking.add(:GRISEOUSORB,
|
Battle::AI::Handlers::ItemRanking.add(:GRISEOUSORB,
|
||||||
proc { |item, score, battler, ai|
|
proc { |item, score, battler, ai|
|
||||||
next score if battler.battler.isSpecies?(:GIRATINA) &&
|
next score if battler.battler.isSpecies?(:GIRATINA) &&
|
||||||
@@ -383,6 +566,36 @@ Battle::AI::Handlers::ItemRanking.add(:GRISEOUSORB,
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:HEATROCK,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
next score if battler.check_for_move { |m| m.is_a?(Battle::Move::StartSunWeather) }
|
||||||
|
next 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:IAPAPABERRY,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
if Settings::MECHANICS_GENERATION == 7 # Heals 50%
|
||||||
|
score += 2
|
||||||
|
elsif Settings::MECHANICS_GENERATION <= 6 # Heals 12.5%
|
||||||
|
score -= 3
|
||||||
|
end
|
||||||
|
if ai.trainer.high_skill?
|
||||||
|
if battler.battler.nature.stat_changes.any? { |val| val[0] == :DEFENSE && val[1] < 0 }
|
||||||
|
score -= 2 # Will confuse
|
||||||
|
end
|
||||||
|
end
|
||||||
|
next score
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:ICYROCK,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
next score if battler.check_for_move { |m| m.is_a?(Battle::Move::StartHailWeather) }
|
||||||
|
next 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
Battle::AI::Handlers::ItemRanking.add(:IRONBALL,
|
Battle::AI::Handlers::ItemRanking.add(:IRONBALL,
|
||||||
proc { |item, score, battler, ai|
|
proc { |item, score, battler, ai|
|
||||||
next 0 if battler.has_move_with_function?("ThrowUserItemAtTarget")
|
next 0 if battler.has_move_with_function?("ThrowUserItemAtTarget")
|
||||||
@@ -390,6 +603,27 @@ Battle::AI::Handlers::ItemRanking.add(:IRONBALL,
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:KINGSROCK,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
if ai.trainer.high_skill?
|
||||||
|
score += 1 if battler.check_for_move { |m| m.multiHitMove? }
|
||||||
|
end
|
||||||
|
next score
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.copy(:KINGSROCK, :RAZORFANG)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:LEEK,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
next score if (battler.battler.isSpecies?(:FARFETCHD) || battler.battler.isSpecies?(:SIRFETCHD)) &&
|
||||||
|
battler.check_for_move { |m| m.damagingMove? }
|
||||||
|
next 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.copy(:LEEK, :STICK)
|
||||||
|
|
||||||
Battle::AI::Handlers::ItemRanking.add(:LIGHTBALL,
|
Battle::AI::Handlers::ItemRanking.add(:LIGHTBALL,
|
||||||
proc { |item, score, battler, ai|
|
proc { |item, score, battler, ai|
|
||||||
next score if battler.battler.isSpecies?(:PIKACHU) &&
|
next score if battler.battler.isSpecies?(:PIKACHU) &&
|
||||||
@@ -398,6 +632,24 @@ Battle::AI::Handlers::ItemRanking.add(:LIGHTBALL,
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:LIGHTCLAY,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
next score if battler.check_for_move do |m|
|
||||||
|
m.is_a?(Battle::Move::StartWeakenPhysicalDamageAgainstUserSide) ||
|
||||||
|
m.is_a?(Battle::Move::StartWeakenSpecialDamageAgainstUserSide) ||
|
||||||
|
m.is_a?(Battle::Move::StartWeakenDamageAgainstUserSideIfHail)
|
||||||
|
end
|
||||||
|
next 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:LUCKYPUNCH,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
next score if battler.battler.isSpecies?(:CHANSEY)
|
||||||
|
next 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
Battle::AI::Handlers::ItemRanking.add(:LUSTROUSORB,
|
Battle::AI::Handlers::ItemRanking.add(:LUSTROUSORB,
|
||||||
proc { |item, score, battler, ai|
|
proc { |item, score, battler, ai|
|
||||||
next score if battler.battler.isSpecies?(:PALKIA) &&
|
next score if battler.battler.isSpecies?(:PALKIA) &&
|
||||||
@@ -406,18 +658,106 @@ Battle::AI::Handlers::ItemRanking.add(:LUSTROUSORB,
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:MAGOBERRY,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
if Settings::MECHANICS_GENERATION == 7 # Heals 50%
|
||||||
|
score += 2
|
||||||
|
elsif Settings::MECHANICS_GENERATION <= 6 # Heals 12.5%
|
||||||
|
score -= 3
|
||||||
|
end
|
||||||
|
if ai.trainer.high_skill?
|
||||||
|
if battler.battler.nature.stat_changes.any? { |val| val[0] == :SPEED && val[1] < 0 }
|
||||||
|
score -= 2 # Will confuse
|
||||||
|
end
|
||||||
|
end
|
||||||
|
next score
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:METALPOWDER,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
next score if battler.battler.isSpecies?(:DITTO) && !battler.effects[PBEffects::Transform]
|
||||||
|
next 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.copy(:METALPOWDER, :QUICKPOWDER)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:MISTYSEED,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
next score if battler.check_for_move { |m| m.is_a?(Battle::Move::StartMistyTerrain) }
|
||||||
|
next 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:ORANBERRY,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
next [10 - (battler.totalhp / 8), 1].max
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:POWERHERB,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
next score if battler.check_for_move do |m|
|
||||||
|
m.is_a?(Battle::Move::TwoTurnMove) &&
|
||||||
|
!m.is_a?(Battle::Move::TwoTurnAttackInvulnerableInSkyTargetCannotAct)
|
||||||
|
end
|
||||||
|
next 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:PSYCHICSEED,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
next score if battler.check_for_move { |m| m.is_a?(Battle::Move::StartPsychicTerrain) }
|
||||||
|
next 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:RINGTARGET,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
has_immunity = false
|
||||||
|
battler.pbTypes(true).each do |type|
|
||||||
|
has_immunity = GameData::Type.get(type).immunities.length > 0
|
||||||
|
break if has_immunity
|
||||||
|
end
|
||||||
|
next score if has_immunity
|
||||||
|
next 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:SMOOTHROCK,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
next score if battler.check_for_move { |m| m.is_a?(Battle::Move::StartSandstormWeather) }
|
||||||
|
next 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
Battle::AI::Handlers::ItemRanking.add(:SOULDEW,
|
Battle::AI::Handlers::ItemRanking.add(:SOULDEW,
|
||||||
proc { |item, score, battler, ai|
|
proc { |item, score, battler, ai|
|
||||||
next 0 if !battler.battler.isSpecies?(:LATIAS) && !battler.battler.isSpecies?(:LATIOS)
|
next 0 if !battler.battler.isSpecies?(:LATIAS) && !battler.battler.isSpecies?(:LATIOS)
|
||||||
if Settings::SOUL_DEW_POWERS_UP_TYPES
|
if Settings::SOUL_DEW_POWERS_UP_TYPES
|
||||||
next 0 if !battler.has_damaging_move_of_type?(:PSYCHIC, :DRAGON)
|
next 0 if !battler.has_damaging_move_of_type?(:PSYCHIC, :DRAGON)
|
||||||
elsif !battler.check_for_move { |m| m.specialMove?(m.type) }
|
elsif battler.check_for_move { |m| m.specialMove?(m.type) }
|
||||||
next 1 # Also boosts SpDef
|
next 10
|
||||||
|
else
|
||||||
|
next 6 # Boosts SpDef
|
||||||
end
|
end
|
||||||
next score
|
next score
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:TERRAINEXTENDER,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
next score if battler.check_for_move do |m|
|
||||||
|
m.is_a?(Battle::Move::StartElectricTerrain) ||
|
||||||
|
m.is_a?(Battle::Move::StartGrassyTerrain) ||
|
||||||
|
m.is_a?(Battle::Move::StartMistyTerrain) ||
|
||||||
|
m.is_a?(Battle::Move::StartPsychicTerrain)
|
||||||
|
end
|
||||||
|
next 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
Battle::AI::Handlers::ItemRanking.add(:THICKCLUB,
|
Battle::AI::Handlers::ItemRanking.add(:THICKCLUB,
|
||||||
proc { |item, score, battler, ai|
|
proc { |item, score, battler, ai|
|
||||||
next score if (battler.battler.isSpecies?(:CUBONE) || battler.battler.isSpecies?(:MAROWAK)) &&
|
next score if (battler.battler.isSpecies?(:CUBONE) || battler.battler.isSpecies?(:MAROWAK)) &&
|
||||||
@@ -426,6 +766,61 @@ Battle::AI::Handlers::ItemRanking.add(:THICKCLUB,
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:THROATSPRAY,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
next score if battler.check_for_move { |m| m.soundMove? }
|
||||||
|
next 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:TOXICORB,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
next 0 if battler.status != :NONE
|
||||||
|
next 7 if battler.wants_status_problem?(:POISON)
|
||||||
|
next score
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:WHITEHERB,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
if ai.trainer.high_skill?
|
||||||
|
score += 1 if battler.has_move_with_function("LowerUserDefSpDef1RaiseUserAtkSpAtkSpd2")
|
||||||
|
end
|
||||||
|
next score
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:WIKIBERRY,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
if Settings::MECHANICS_GENERATION == 7 # Heals 50%
|
||||||
|
score += 2
|
||||||
|
elsif Settings::MECHANICS_GENERATION <= 6 # Heals 12.5%
|
||||||
|
score -= 3
|
||||||
|
end
|
||||||
|
if ai.trainer.high_skill?
|
||||||
|
if battler.battler.nature.stat_changes.any? { |val| val[0] == :SPECIAL_ATTACK && val[1] < 0 }
|
||||||
|
score -= 2 # Will confuse
|
||||||
|
end
|
||||||
|
end
|
||||||
|
next score
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.add(:ZOOMLENS,
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
if ai.trainer.high_skill?
|
||||||
|
score += 1 if battler.stages[:ACCURACY] < 0
|
||||||
|
battler.battler.eachMove do |m|
|
||||||
|
next if m.accuracy == 0 || m.is_a?(Battle::Move::OHKO)
|
||||||
|
next if m.accuracy > 70
|
||||||
|
score += 1
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
next score
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
Battle::AI::Handlers::ItemRanking.addIf(:type_boosting_items,
|
Battle::AI::Handlers::ItemRanking.addIf(:type_boosting_items,
|
||||||
proc { |item|
|
proc { |item|
|
||||||
next [:BLACKBELT, :BLACKGLASSES, :CHARCOAL, :DRAGONFANG, :HARDSTONE,
|
next [:BLACKBELT, :BLACKGLASSES, :CHARCOAL, :DRAGONFANG, :HARDSTONE,
|
||||||
@@ -469,3 +864,36 @@ Battle::AI::Handlers::ItemRanking.addIf(:type_boosting_items,
|
|||||||
next 0
|
next 0
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Battle::AI::Handlers::ItemRanking.addIf(:gems,
|
||||||
|
proc { |item|
|
||||||
|
next [:FIREGEM, :WATERGEM, :ELECTRICGEM, :GRASSGEM, :ICEGEM, :FIGHTINGGEM,
|
||||||
|
:POISONGEM, :GROUNDGEM, :FLYINGGEM, :PSYCHICGEM, :BUGGEM, :ROCKGEM,
|
||||||
|
:GHOSTGEM, :DRAGONGEM, :DARKGEM, :STEELGEM, :FAIRYGEM, :NORMALGEM].include?(item)
|
||||||
|
},
|
||||||
|
proc { |item, score, battler, ai|
|
||||||
|
score += 2 if Settings::MECHANICS_GENERATION <= 5 # 1.5x boost rather than 1.3x
|
||||||
|
boosted_type = {
|
||||||
|
:BUGGEM => :BUG,
|
||||||
|
:DARKGEM => :DARK,
|
||||||
|
:DRAGONGEM => :DRAGON,
|
||||||
|
:ELECTRICGEM => :ELECTRIC,
|
||||||
|
:FAIRYGEM => :FAIRY,
|
||||||
|
:FIGHTINGGEM => :FIGHTING,
|
||||||
|
:FIREGEM => :FIRE,
|
||||||
|
:FLYINGGEM => :FLYING,
|
||||||
|
:GHOSTGEM => :GHOST,
|
||||||
|
:GRASSGEM => :GRASS,
|
||||||
|
:GROUNDGEM => :GROUND,
|
||||||
|
:ICEGEM => :ICE,
|
||||||
|
:NORMALGEM => :NORMAL,
|
||||||
|
:POISONGEM => :POISON,
|
||||||
|
:PSYCHICGEM => :PSYCHIC,
|
||||||
|
:ROCKGEM => :ROCK,
|
||||||
|
:STEELGEM => :STEEL,
|
||||||
|
:WATERGEM => :WATER,
|
||||||
|
}[item]
|
||||||
|
next score if boosted_type && battler.has_damaging_move_of_type?(boosted_type)
|
||||||
|
next 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,157 +0,0 @@
|
|||||||
#===============================================================================
|
|
||||||
#
|
|
||||||
#===============================================================================
|
|
||||||
class Battle::AI
|
|
||||||
# Determine the roles filled by a Pokémon on a given side at a given party
|
|
||||||
# index.
|
|
||||||
# Roles are:
|
|
||||||
# :ace
|
|
||||||
# :baton_passer
|
|
||||||
# :cleric
|
|
||||||
# :field_setter
|
|
||||||
# :lead
|
|
||||||
# :phazer
|
|
||||||
# :physical_wall
|
|
||||||
# :pivot
|
|
||||||
# :screener
|
|
||||||
# :second
|
|
||||||
# :special_wall
|
|
||||||
# :spinner
|
|
||||||
# :stall_breaker
|
|
||||||
# :status_absorber
|
|
||||||
# :sweeper
|
|
||||||
# :tank
|
|
||||||
# :trapper
|
|
||||||
# :weather_setter
|
|
||||||
# NOTE: Reborn has the REVENGEKILLER role which compares mon's speed with
|
|
||||||
# opponent (only when deciding whether to switch mon in) - this
|
|
||||||
# comparison should be calculated when needed instead of being a role.
|
|
||||||
def determine_roles(side, index)
|
|
||||||
pkmn = @battle.pbParty(side)[index]
|
|
||||||
ret = []
|
|
||||||
return ret if !pkmn || pkmn.egg?
|
|
||||||
# Check for moves indicative of particular roles
|
|
||||||
hasHealMove = false
|
|
||||||
pkmn.moves.each do |m|
|
|
||||||
next if !m
|
|
||||||
move = Battle::Move.from_pokemon_move(@battle, m)
|
|
||||||
hasHealMove = true if !hasHealMove && move.healingMove?
|
|
||||||
case move.function
|
|
||||||
when "SleepTargetNextTurn", # Yawn
|
|
||||||
"StartPerishCountsForAllBattlers", # Perish Song
|
|
||||||
"SwitchOutTargetStatusMove", # Roar
|
|
||||||
"SwitchOutTargetDamagingMove" # Circle Throw
|
|
||||||
ret.push(:phazer)
|
|
||||||
when "CureUserPartyStatus" # Aromatherapy/Heal Bell
|
|
||||||
ret.push(:cleric)
|
|
||||||
when "DisableTargetStatusMoves" # Taunt
|
|
||||||
ret.push(:stall_breaker)
|
|
||||||
when "HealUserPositionNextTurn" # Wish
|
|
||||||
ret.push(:cleric) if pkmn.ev[:HP] == Pokemon::EV_STAT_LIMIT
|
|
||||||
when "HealUserFullyAndFallAsleep" # Rest
|
|
||||||
ret.push(:status_absorber)
|
|
||||||
when "SwitchOutUserPassOnEffects" # Baton Pass
|
|
||||||
ret.push(:baton_passer)
|
|
||||||
when "SwitchOutUserStatusMove", "SwitchOutUserDamagingMove" # Teleport (Gen 8+), U-turn
|
|
||||||
ret.push(:pivot) if hasHealMove
|
|
||||||
when "RemoveUserBindingAndEntryHazards" # Rapid Spin
|
|
||||||
ret.push(:spinner)
|
|
||||||
when "StartElectricTerrain", "StartGrassyTerrain",
|
|
||||||
"StartMistyTerrain", "StartPsychicTerrain" # Terrain moves
|
|
||||||
ret.push(:field_setter)
|
|
||||||
else
|
|
||||||
ret.push(:weather_setter) if move.is_a?(Battle::Move::WeatherMove)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
# Check EVs, nature and moves for combinations indicative of particular roles
|
|
||||||
if pkmn.ev[:SPEED] == Pokemon::EV_STAT_LIMIT
|
|
||||||
if [:MODEST, :ADAMANT, # SpAtk+ Atk-, Atk+ SpAtk-
|
|
||||||
:TIMID, :JOLLY].include?(pkmn.nature) # Spd+ Atk-, Spd+ SpAtk-
|
|
||||||
ret.push(:sweeper)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if hasHealMove
|
|
||||||
if pkmn.nature.stat_changes.any? { |change| change[0] == :DEFENSE && change[1] > 0 } &&
|
|
||||||
!pkmn.nature.stat_changes.any? { |change| change[0] == :DEFENSE && change[1] < 0 }
|
|
||||||
ret.push(:physical_wall) if pkmn.ev[:DEFENSE] == Pokemon::EV_STAT_LIMIT
|
|
||||||
elsif pkmn.nature.stat_changes.any? { |change| change[0] == :SPECIAL_DEFENSE && change[1] > 0 } &&
|
|
||||||
!pkmn.nature.stat_changes.any? { |change| change[0] == :SPECIAL_DEFENSE && change[1] < 0 }
|
|
||||||
ret.push(:special_wall) if pkmn.ev[:SPECIAL_DEFENSE] == Pokemon::EV_STAT_LIMIT
|
|
||||||
end
|
|
||||||
else
|
|
||||||
ret.push(:tank) if pkmn.ev[:HP] == Pokemon::EV_STAT_LIMIT
|
|
||||||
end
|
|
||||||
# Check for abilities indicative of particular roles
|
|
||||||
case pkmn.ability_id
|
|
||||||
when :REGENERATOR
|
|
||||||
ret.push(:pivot)
|
|
||||||
when :GUTS, :QUICKFEET, :FLAREBOOST, :TOXICBOOST, :NATURALCURE, :MAGICGUARD,
|
|
||||||
:MAGICBOUNCE, :HYDRATION
|
|
||||||
ret.push(:status_absorber)
|
|
||||||
when :SHADOWTAG, :ARENATRAP, :MAGNETPULL
|
|
||||||
ret.push(:trapper)
|
|
||||||
when :DROUGHT, :DRIZZLE, :SANDSTREAM, :SNOWWARNING, :PRIMORDIALSEA,
|
|
||||||
:DESOLATELAND, :DELTASTREAM
|
|
||||||
ret.push(:weather_setter)
|
|
||||||
when :GRASSYSURGE, :ELECTRICSURGE, :MISTYSURGE, :PSYCHICSURGE
|
|
||||||
ret.push(:field_setter)
|
|
||||||
end
|
|
||||||
# Check for items indicative of particular roles
|
|
||||||
case pkmn.item_id
|
|
||||||
when :LIGHTCLAY
|
|
||||||
ret.push(:screener)
|
|
||||||
when :ASSAULTVEST
|
|
||||||
ret.push(:tank)
|
|
||||||
when :CHOICEBAND, :CHOICESPECS
|
|
||||||
ret.push(:stall_breaker)
|
|
||||||
ret.push(:sweeper) if pkmn.ev[:SPEED] == Pokemon::EV_STAT_LIMIT
|
|
||||||
when :CHOICESCARF
|
|
||||||
ret.push(:sweeper) if pkmn.ev[:SPEED] == Pokemon::EV_STAT_LIMIT
|
|
||||||
when :TOXICORB, :FLAMEORB
|
|
||||||
ret.push(:status_absorber)
|
|
||||||
when :TERRAINEXTENDER
|
|
||||||
ret.push(:field_setter)
|
|
||||||
end
|
|
||||||
# Check for position in team, level relative to other levels in team
|
|
||||||
partyStarts = @battle.pbPartyStarts(side)
|
|
||||||
if partyStarts.include?(index + 1) || index == @battle.pbParty(side).length - 1
|
|
||||||
ret.push(:ace) # Last in party, assumed to be the best Pokémon
|
|
||||||
else
|
|
||||||
ret.push(:lead) if partyStarts.include?(index) # First in party
|
|
||||||
idxTrainer = @battle.pbGetOwnerIndexFromPartyIndex(side, index)
|
|
||||||
maxLevel = @battle.pbMaxLevelInTeam(side, idxTrainer)
|
|
||||||
if pkmn.level >= maxLevel
|
|
||||||
ret.push(:second)
|
|
||||||
else
|
|
||||||
secondHighest = true
|
|
||||||
seenHigherLevel = false
|
|
||||||
@battle.eachInTeam(side, @battle.pbGetOwnerIndexFromPartyIndex(side, index)) do |p|
|
|
||||||
next if p.level < pkmn.level
|
|
||||||
if seenHigherLevel
|
|
||||||
secondHighest = false
|
|
||||||
break
|
|
||||||
end
|
|
||||||
seenHigherLevel = true
|
|
||||||
end
|
|
||||||
# NOTE: There can be multiple "second"s if all their levels are equal
|
|
||||||
# and the highest in the team (and none are the ace).
|
|
||||||
ret.push(:second) if secondHighest
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return ret
|
|
||||||
end
|
|
||||||
|
|
||||||
def check_role(side, idxBattler, *roles)
|
|
||||||
role_array = @roles[side][idxBattler]
|
|
||||||
roles.each do |r|
|
|
||||||
return true if role_array.include?(r)
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
def check_battler_role(battler, *roles)
|
|
||||||
side = idxParty.idxOwnSide
|
|
||||||
idxParty = idxParty.pokemonIndex
|
|
||||||
return check_role(side, idxParty, *roles)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -16,10 +16,6 @@ class Battle::AI::AIBattler
|
|||||||
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
|
|
||||||
# TODO: Start of battle or Pokémon switched/shifted; recalculate roles,
|
|
||||||
# etc. What is etc.?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def pokemon; return battler.pokemon; end
|
def pokemon; return battler.pokemon; end
|
||||||
@@ -232,12 +228,7 @@ class Battle::AI::AIBattler
|
|||||||
end
|
end
|
||||||
alias pbHasType? has_type?
|
alias pbHasType? has_type?
|
||||||
|
|
||||||
# TODO: Also make a def effectiveness_of_move_against_battler which calls
|
def effectiveness_of_type_against_battler(type, user = nil, move = nil)
|
||||||
# pbCalcTypeModSingle instead of effectiveness_of_type_against_single_battler_type,
|
|
||||||
# for moves with custom def pbCalcTypeMod, e.g. Freeze-Dry. When would
|
|
||||||
# that be used instead? Or rather, when would THIS method be used if
|
|
||||||
# that existed?
|
|
||||||
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)
|
||||||
@@ -250,7 +241,16 @@ class Battle::AI::AIBattler
|
|||||||
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)
|
mult = effectiveness_of_type_against_single_battler_type(type, defend_type, user)
|
||||||
|
if move
|
||||||
|
case move.function
|
||||||
|
when "HitsTargetInSkyGroundsTarget"
|
||||||
|
mult = Effectiveness::NORMAL_EFFECTIVE_MULTIPLIER if type == :GROUND && defend_type == :FLYING
|
||||||
|
when "FreezeTargetSuperEffectiveAgainstWater"
|
||||||
|
mult = Effectiveness::SUPER_EFFECTIVE_MULTIPLIER if defend_type == :WATER
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ret *= mult
|
||||||
end
|
end
|
||||||
ret *= 2 if self.effects[PBEffects::TarShot] && type == :FIRE
|
ret *= 2 if self.effects[PBEffects::TarShot] && type == :FIRE
|
||||||
end
|
end
|
||||||
@@ -414,7 +414,9 @@ class Battle::AI::AIBattler
|
|||||||
break
|
break
|
||||||
end
|
end
|
||||||
# Modify the rating based on ability-specific contexts
|
# Modify the rating based on ability-specific contexts
|
||||||
ret = Battle::AI::Handlers.modify_ability_ranking(ability, ret, self, @ai)
|
if @ai.trainer.medium_skill?
|
||||||
|
ret = Battle::AI::Handlers.modify_ability_ranking(ability, ret, self, @ai)
|
||||||
|
end
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -424,13 +426,12 @@ class Battle::AI::AIBattler
|
|||||||
# battler if it is holding it.
|
# battler if it is holding it.
|
||||||
# Return values are typically between -10 and +10. 0 is indifferent, positive
|
# Return values are typically between -10 and +10. 0 is indifferent, positive
|
||||||
# values mean this battler benefits, negative values mean this battler suffers.
|
# values mean this battler benefits, negative values mean this battler suffers.
|
||||||
# TODO: This method shouldn't check for item effect negation, to work the same
|
# NOTE: This method assumes the item isn't being negated. The calculations
|
||||||
# as def wants_ability?.
|
# that call this method separately check for it being negated, because
|
||||||
|
# they need to do something special in that case.
|
||||||
def wants_item?(item)
|
def wants_item?(item)
|
||||||
item = :NONE if !item
|
item = :NONE if !item
|
||||||
item = item.id if !item.is_a?(Symbol) && item.respond_to?("id")
|
item = item.id if !item.is_a?(Symbol) && item.respond_to?("id")
|
||||||
return 0 if has_active_ability?(:KLUTZ)
|
|
||||||
# TODO: Unnerve, other item-negating effects.
|
|
||||||
# Get the base item rating
|
# Get the base item rating
|
||||||
ret = 0
|
ret = 0
|
||||||
Battle::AI::BASE_ITEM_RATINGS.each_pair do |val, items|
|
Battle::AI::BASE_ITEM_RATINGS.each_pair do |val, items|
|
||||||
@@ -439,7 +440,9 @@ class Battle::AI::AIBattler
|
|||||||
break
|
break
|
||||||
end
|
end
|
||||||
# Modify the rating based on item-specific contexts
|
# Modify the rating based on item-specific contexts
|
||||||
ret = Battle::AI::Handlers.modify_item_ranking(item, ret, self, @ai)
|
if @ai.trainer.medium_skill?
|
||||||
|
ret = Battle::AI::Handlers.modify_item_ranking(item, ret, self, @ai)
|
||||||
|
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
|
||||||
# additional (negative) effect when flung
|
# additional (negative) effect when flung
|
||||||
if item != :NONE && has_move_with_function?("ThrowUserItemAtTarget")
|
if item != :NONE && has_move_with_function?("ThrowUserItemAtTarget")
|
||||||
@@ -481,7 +484,18 @@ class Battle::AI::AIBattler
|
|||||||
end
|
end
|
||||||
ret += (hp > totalhp * (1 - (1.0 / fraction_to_heal))) ? -6 : 6
|
ret += (hp > totalhp * (1 - (1.0 / fraction_to_heal))) ? -6 : 6
|
||||||
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)
|
||||||
# TODO: Check whether the item will cause confusion?
|
if @ai.trainer.high_skill?
|
||||||
|
flavor_stat = {
|
||||||
|
:AGUAVBERRY => :SPECIAL_DEFENSE,
|
||||||
|
:FIGYBERRY => :ATTACK,
|
||||||
|
:IAPAPABERRY => :DEFENSE,
|
||||||
|
:MAGOBERRY => :SPEED,
|
||||||
|
:WIKIBERRY => :SPECIAL_ATTACK
|
||||||
|
}[item]
|
||||||
|
if @battler.nature.stat_changes.any? { |val| val[0] == flavor_stat && val[1] < 0 }
|
||||||
|
ret -= 3 if @battler.pbCanConfuseSelf?(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
when :ASPEARBERRY, :CHERIBERRY, :CHESTOBERRY, :PECHABERRY, :RAWSTBERRY
|
when :ASPEARBERRY, :CHERIBERRY, :CHESTOBERRY, :PECHABERRY, :RAWSTBERRY
|
||||||
# Status cure
|
# Status cure
|
||||||
cured_status = {
|
cured_status = {
|
||||||
|
|||||||
@@ -101,7 +101,6 @@ class Battle::AI::AIMove
|
|||||||
# 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 has 50% chance of higher of being a critical hit
|
||||||
# TODO: Make this a gradient/probability rather than all-or-nothing?
|
|
||||||
crit_stage = rough_critical_hit_stage
|
crit_stage = rough_critical_hit_stage
|
||||||
is_critical = crit_stage >= Battle::Move::CRITICAL_HIT_RATIOS.length ||
|
is_critical = crit_stage >= Battle::Move::CRITICAL_HIT_RATIOS.length ||
|
||||||
Battle::Move::CRITICAL_HIT_RATIOS[crit_stage] <= 2
|
Battle::Move::CRITICAL_HIT_RATIOS[crit_stage] <= 2
|
||||||
@@ -325,7 +324,7 @@ class Battle::AI::AIMove
|
|||||||
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, @move)
|
||||||
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) &&
|
||||||
|
|||||||
@@ -494,10 +494,6 @@ Battle::AI::Handlers::MoveEffectScore.add("RaiseUserMainStats1TrapUserInBattle",
|
|||||||
next (move.damagingMove?) ? score : Battle::AI::MOVE_USELESS_SCORE
|
next (move.damagingMove?) ? score : Battle::AI::MOVE_USELESS_SCORE
|
||||||
end
|
end
|
||||||
# Score for user becoming trapped in battle
|
# Score for user becoming trapped in battle
|
||||||
# TODO: These checks are related to desire to switch, and there can be a lot
|
|
||||||
# more things to consider, e.g. effectiveness of the target's moves
|
|
||||||
# against its foes. Also applies to other code that calls
|
|
||||||
# can_become_trapped?
|
|
||||||
if user.effects[PBEffects::PerishSong] > 0 ||
|
if user.effects[PBEffects::PerishSong] > 0 ||
|
||||||
user.effects[PBEffects::Attract] >= 0 ||
|
user.effects[PBEffects::Attract] >= 0 ||
|
||||||
eor_damage > 0
|
eor_damage > 0
|
||||||
@@ -632,7 +628,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseTargetAttack2Confu
|
|||||||
)
|
)
|
||||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetAttack2ConfuseTarget",
|
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetAttack2ConfuseTarget",
|
||||||
proc { |score, move, user, target, ai, battle|
|
proc { |score, move, user, target, ai, battle|
|
||||||
if !target.has_active_ability?(:CONTRARY) || @battle.moldBreaker
|
if !target.has_active_ability?(:CONTRARY) || battle.moldBreaker
|
||||||
next Battle::AI::MOVE_USELESS_SCORE if !target.battler.pbCanConfuse?(user.battler, false, move.move)
|
next Battle::AI::MOVE_USELESS_SCORE if !target.battler.pbCanConfuse?(user.battler, false, move.move)
|
||||||
end
|
end
|
||||||
# Score for stat raise
|
# Score for stat raise
|
||||||
@@ -654,7 +650,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseTargetSpAtk1Confus
|
|||||||
)
|
)
|
||||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetSpAtk1ConfuseTarget",
|
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetSpAtk1ConfuseTarget",
|
||||||
proc { |score, move, user, target, ai, battle|
|
proc { |score, move, user, target, ai, battle|
|
||||||
if !target.has_active_ability?(:CONTRARY) || @battle.moldBreaker
|
if !target.has_active_ability?(:CONTRARY) || battle.moldBreaker
|
||||||
next Battle::AI::MOVE_USELESS_SCORE if !target.battler.pbCanConfuse?(user.battler, false, move.move)
|
next Battle::AI::MOVE_USELESS_SCORE if !target.battler.pbCanConfuse?(user.battler, false, move.move)
|
||||||
end
|
end
|
||||||
# Score for stat raise
|
# Score for stat raise
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("ParalyzeTarget",
|
|||||||
#===============================================================================
|
#===============================================================================
|
||||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("ParalyzeTargetIfNotTypeImmune",
|
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("ParalyzeTargetIfNotTypeImmune",
|
||||||
proc { |move, user, target, ai, battle|
|
proc { |move, user, target, ai, battle|
|
||||||
eff = target.effectiveness_of_type_against_battler(move.rough_type, user)
|
eff = target.effectiveness_of_type_against_battler(move.rough_type, user, move)
|
||||||
next true if Effectiveness.ineffective?(eff)
|
next true if Effectiveness.ineffective?(eff)
|
||||||
next true if move.statusMove? && !target.battler.pbCanParalyze?(user.battler, false, move.move)
|
next true if move.statusMove? && !target.battler.pbCanParalyze?(user.battler, false, move.move)
|
||||||
next false
|
next false
|
||||||
|
|||||||
@@ -1094,8 +1094,6 @@ Battle::AI::Handlers::MoveEffectScore.add("ProtectUserSideFromPriorityMoves",
|
|||||||
useless = true
|
useless = true
|
||||||
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?
|
||||||
# TODO: There are more calculations that could be done to get a more
|
|
||||||
# accurate priority number.
|
|
||||||
next if !b.check_for_move { |m| m.pbPriority(b.battler) > 0 && m.canProtectAgainst? }
|
next if !b.check_for_move { |m| m.pbPriority(b.battler) > 0 && m.canProtectAgainst? }
|
||||||
useless = false
|
useless = false
|
||||||
# General preference
|
# General preference
|
||||||
@@ -1343,9 +1341,6 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("EnsureNextMoveAlwaysHits
|
|||||||
# Prefer if the user knows moves with low accuracy
|
# Prefer if the user knows moves with low accuracy
|
||||||
user.battler.eachMove do |m|
|
user.battler.eachMove do |m|
|
||||||
next if target.effects[PBEffects::Minimize] && m.tramplesMinimize? && Settings::MECHANICS_GENERATION >= 6
|
next if target.effects[PBEffects::Minimize] && m.tramplesMinimize? && Settings::MECHANICS_GENERATION >= 6
|
||||||
# TODO: There are other effects that make a move certain to hit. Account
|
|
||||||
# for those as well. Score this move useless if no moves would
|
|
||||||
# benefit from locking on.
|
|
||||||
acc = m.accuracy
|
acc = m.accuracy
|
||||||
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
|
||||||
@@ -1503,7 +1498,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TargetMovesBecomeElectri
|
|||||||
next if !m.damagingMove?
|
next if !m.damagingMove?
|
||||||
m_type = m.pbCalcType(target.battler)
|
m_type = m.pbCalcType(target.battler)
|
||||||
next if m_type == :ELECTRIC
|
next if m_type == :ELECTRIC
|
||||||
eff = user.effectiveness_of_type_against_battler(m_type, target)
|
eff = user.effectiveness_of_type_against_battler(m_type, target, m)
|
||||||
eff *= 1.5 if target.has_type?(m_type) # STAB
|
eff *= 1.5 if target.has_type?(m_type) # STAB
|
||||||
case m_type
|
case m_type
|
||||||
when :FIRE
|
when :FIRE
|
||||||
|
|||||||
@@ -451,6 +451,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("TwoTurnAttackInvulnerab
|
|||||||
proc { |move, user, target, ai, battle|
|
proc { |move, user, target, ai, battle|
|
||||||
next true if !target.opposes?(user)
|
next true if !target.opposes?(user)
|
||||||
next true if target.effects[PBEffects::Substitute] > 0 && !move.move.ignoresSubstitute?(user.battler)
|
next true if target.effects[PBEffects::Substitute] > 0 && !move.move.ignoresSubstitute?(user.battler)
|
||||||
|
next true if target.has_type?(:FLYING)
|
||||||
next true if Settings::MECHANICS_GENERATION >= 6 && target.battler.pbWeight >= 2000 # 200.0kg
|
next true if Settings::MECHANICS_GENERATION >= 6 && target.battler.pbWeight >= 2000 # 200.0kg
|
||||||
next true if target.battler.semiInvulnerable? || target.effects[PBEffects::SkyDrop] >= 0
|
next true if target.battler.semiInvulnerable? || target.effects[PBEffects::SkyDrop] >= 0
|
||||||
next false
|
next false
|
||||||
|
|||||||
@@ -11,10 +11,14 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTakesTargetItem",
|
|||||||
# User can steal the target's item; score it
|
# User can steal the target's item; score it
|
||||||
user_item_preference = user.wants_item?(target.item_id)
|
user_item_preference = user.wants_item?(target.item_id)
|
||||||
user_no_item_preference = user.wants_item?(:NONE)
|
user_no_item_preference = user.wants_item?(:NONE)
|
||||||
|
user_diff = user_item_preference - user_no_item_preference
|
||||||
|
user_diff = 0 if !user.item_active?
|
||||||
target_item_preference = target.wants_item?(target.item_id)
|
target_item_preference = target.wants_item?(target.item_id)
|
||||||
target_no_item_preference = target.wants_item?(:NONE)
|
target_no_item_preference = target.wants_item?(:NONE)
|
||||||
score += user_item_preference - user_no_item_preference
|
target_diff = target_no_item_preference - target_item_preference
|
||||||
score += target_item_preference - target_no_item_preference
|
target_diff = 0 if !target.item_active?
|
||||||
|
score += user_diff * 4
|
||||||
|
score -= target_diff * 4
|
||||||
next score
|
next score
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -33,10 +37,14 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TargetTakesUserItem",
|
|||||||
proc { |score, move, user, target, ai, battle|
|
proc { |score, move, user, target, ai, battle|
|
||||||
user_item_preference = user.wants_item?(user.item_id)
|
user_item_preference = user.wants_item?(user.item_id)
|
||||||
user_no_item_preference = user.wants_item?(:NONE)
|
user_no_item_preference = user.wants_item?(:NONE)
|
||||||
|
user_diff = user_no_item_preference - user_item_preference
|
||||||
|
user_diff = 0 if !user.item_active?
|
||||||
target_item_preference = target.wants_item?(user.item_id)
|
target_item_preference = target.wants_item?(user.item_id)
|
||||||
target_no_item_preference = target.wants_item?(:NONE)
|
target_no_item_preference = target.wants_item?(:NONE)
|
||||||
score += user_no_item_preference - user_item_preference
|
target_diff = target_item_preference - target_no_item_preference
|
||||||
score += target_no_item_preference - target_item_preference
|
target_diff = 0 if !target.item_active?
|
||||||
|
score += user_diff * 4
|
||||||
|
score -= target_diff * 4
|
||||||
next score
|
next score
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -58,10 +66,14 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTargetSwapItems",
|
|||||||
proc { |score, move, user, target, ai, battle|
|
proc { |score, move, user, target, ai, battle|
|
||||||
user_new_item_preference = user.wants_item?(target.item_id)
|
user_new_item_preference = user.wants_item?(target.item_id)
|
||||||
user_old_item_preference = user.wants_item?(user.item_id)
|
user_old_item_preference = user.wants_item?(user.item_id)
|
||||||
|
user_diff = user_new_item_preference - user_old_item_preference
|
||||||
|
user_diff = 0 if !user.item_active?
|
||||||
target_new_item_preference = target.wants_item?(user.item_id)
|
target_new_item_preference = target.wants_item?(user.item_id)
|
||||||
target_old_item_preference = target.wants_item?(target.item_id)
|
target_old_item_preference = target.wants_item?(target.item_id)
|
||||||
score += user_new_item_preference - user_old_item_preference
|
target_diff = target_new_item_preference - target_old_item_preference
|
||||||
score += target_old_item_preference - target_new_item_preference
|
target_diff = 0 if !target.item_active?
|
||||||
|
score += user_diff * 4
|
||||||
|
score -= target_diff * 4
|
||||||
# Don't prefer if user used this move in the last round
|
# Don't prefer if user used this move in the last round
|
||||||
score -= 15 if user.battler.lastMoveUsed &&
|
score -= 15 if user.battler.lastMoveUsed &&
|
||||||
GameData::Move.exists?(user.battler.lastMoveUsed) &&
|
GameData::Move.exists?(user.battler.lastMoveUsed) &&
|
||||||
@@ -80,9 +92,10 @@ Battle::AI::Handlers::MoveFailureCheck.add("RestoreUserConsumedItem",
|
|||||||
)
|
)
|
||||||
Battle::AI::Handlers::MoveEffectScore.add("RestoreUserConsumedItem",
|
Battle::AI::Handlers::MoveEffectScore.add("RestoreUserConsumedItem",
|
||||||
proc { |score, move, user, ai, battle|
|
proc { |score, move, user, ai, battle|
|
||||||
user_new_item_preference = user.wants_item?(user.battler.recycleItem)
|
next Battle::AI::MOVE_USELESS_SCORE if !user.item_active?
|
||||||
user_old_item_preference = user.wants_item?(:NONE)
|
item_preference = user.wants_item?(user.battler.recycleItem)
|
||||||
score += (user_new_item_preference - user_old_item_preference) * 2
|
no_item_preference = user.wants_item?(:NONE)
|
||||||
|
score += (item_preference - no_item_preference) * 4
|
||||||
next score
|
next score
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -101,10 +114,11 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RemoveTargetItem",
|
|||||||
next score if !target.item || target.battler.unlosableItem?(target.item)
|
next score if !target.item || target.battler.unlosableItem?(target.item)
|
||||||
next score if target.effects[PBEffects::Substitute] > 0
|
next score if target.effects[PBEffects::Substitute] > 0
|
||||||
next score if target.has_active_ability?(:STICKYHOLD) && !battle.moldBreaker
|
next score if target.has_active_ability?(:STICKYHOLD) && !battle.moldBreaker
|
||||||
|
next score if !target.item_active?
|
||||||
# User can knock off the target's item; score it
|
# User can knock off the target's item; score it
|
||||||
target_item_preference = target.wants_item?(target.item_id)
|
item_preference = target.wants_item?(target.item_id)
|
||||||
target_no_item_preference = target.wants_item?(:NONE)
|
no_item_preference = target.wants_item?(:NONE)
|
||||||
score += (target_item_preference - target_no_item_preference) * 2
|
score -= (no_item_preference - item_preference) * 4
|
||||||
next score
|
next score
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -119,10 +133,11 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DestroyTargetBerryOrGem"
|
|||||||
next score if user.battler.unlosableItem?(target.item)
|
next score if user.battler.unlosableItem?(target.item)
|
||||||
next score if target.effects[PBEffects::Substitute] > 0
|
next score if target.effects[PBEffects::Substitute] > 0
|
||||||
next score if target.has_active_ability?(:STICKYHOLD) && !battle.moldBreaker
|
next score if target.has_active_ability?(:STICKYHOLD) && !battle.moldBreaker
|
||||||
|
next score if !target.item_active?
|
||||||
# User can incinerate the target's item; score it
|
# User can incinerate the target's item; score it
|
||||||
target_item_preference = target.wants_item?(target.item_id)
|
item_preference = target.wants_item?(target.item_id)
|
||||||
target_no_item_preference = target.wants_item?(:NONE)
|
no_item_preference = target.wants_item?(:NONE)
|
||||||
score += (target_item_preference - target_no_item_preference) * 2
|
score -= (no_item_preference - item_preference) * 4
|
||||||
next score
|
next score
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -141,9 +156,11 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("CorrodeTargetItem",
|
|||||||
)
|
)
|
||||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("CorrodeTargetItem",
|
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("CorrodeTargetItem",
|
||||||
proc { |score, move, user, target, ai, battle|
|
proc { |score, move, user, target, ai, battle|
|
||||||
target_item_preference = target.wants_item?(target.item_id)
|
item_preference = target.wants_item?(target.item_id)
|
||||||
target_no_item_preference = target.wants_item?(:NONE)
|
no_item_preference = target.wants_item?(:NONE)
|
||||||
score += (target_item_preference - target_no_item_preference) * 2
|
target_diff = no_item_preference - item_preference
|
||||||
|
target_diff = 0 if !target.item_active?
|
||||||
|
score += target_diff * 4
|
||||||
next score
|
next score
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -159,7 +176,8 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("StartTargetCannotUseIte
|
|||||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("StartTargetCannotUseItem",
|
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("StartTargetCannotUseItem",
|
||||||
proc { |score, move, user, target, ai, battle|
|
proc { |score, move, user, target, ai, battle|
|
||||||
next Battle::AI::MOVE_USELESS_SCORE if !target.item || !target.item_active?
|
next Battle::AI::MOVE_USELESS_SCORE if !target.item || !target.item_active?
|
||||||
# TODO: Useless if target's item cannot be negated.
|
# NOTE: We won't check if the item has an effect, because if a Pokémon is
|
||||||
|
# holding an item, it probably does.
|
||||||
item_score = target.wants_item?(target.item_id)
|
item_score = target.wants_item?(target.item_id)
|
||||||
next Battle::AI::MOVE_USELESS_SCORE if item_score <= 0 # Item has no effect or is bad
|
next Battle::AI::MOVE_USELESS_SCORE if item_score <= 0 # Item has no effect or is bad
|
||||||
score += item_score * 2
|
score += item_score * 2
|
||||||
@@ -179,7 +197,8 @@ Battle::AI::Handlers::MoveEffectScore.add("StartNegateHeldItems",
|
|||||||
next if !b.item
|
next if !b.item
|
||||||
# Skip b if its item is disabled
|
# Skip b if its item is disabled
|
||||||
if ai.trainer.medium_skill?
|
if ai.trainer.medium_skill?
|
||||||
# TODO: Skip b if its item cannot be negated or it has no effect.
|
# NOTE: We won't check if the item has an effect, because if a Pokémon
|
||||||
|
# is holding an item, it probably does.
|
||||||
if battle.field.effects[PBEffects::MagicRoom] > 0
|
if battle.field.effects[PBEffects::MagicRoom] > 0
|
||||||
# NOTE: Same as b.item_active? but ignoring the Magic Room part.
|
# NOTE: Same as b.item_active? but ignoring the Magic Room part.
|
||||||
next if b.effects[PBEffects::Embargo] > 0
|
next if b.effects[PBEffects::Embargo] > 0
|
||||||
@@ -297,9 +316,9 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserConsumeTargetBerry",
|
|||||||
score -= 5 if target.has_active_ability?(:UNBURDEN)
|
score -= 5 if target.has_active_ability?(:UNBURDEN)
|
||||||
end
|
end
|
||||||
# Score the target no longer having the item
|
# Score the target no longer having the item
|
||||||
target_item_preference = target.wants_item?(target.item_id)
|
item_preference = target.wants_item?(target.item_id)
|
||||||
target_no_item_preference = target.wants_item?(:NONE)
|
no_item_preference = target.wants_item?(:NONE)
|
||||||
score += (target_item_preference - target_no_item_preference) * 2
|
score -= (no_item_preference - item_preference) * 3
|
||||||
next score
|
next score
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -349,9 +368,9 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("ThrowUserItemAtTarget",
|
|||||||
end
|
end
|
||||||
# Prefer if the user doesn't want its held item/don't prefer if it wants to
|
# Prefer if the user doesn't want its held item/don't prefer if it wants to
|
||||||
# keep its held item
|
# keep its held item
|
||||||
user_item_preference = user.wants_item?(user.item_id)
|
item_preference = user.wants_item?(user.item_id)
|
||||||
user_no_item_preference = user.wants_item?(:NONE)
|
no_item_preference = user.wants_item?(:NONE)
|
||||||
score += (user_item_preference - user_no_item_preference) * 2
|
score += (no_item_preference - item_preference) * 2
|
||||||
next score
|
next score
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#===============================================================================
|
#===============================================================================
|
||||||
Battle::AI::Handlers::MoveFailureCheck.add("FleeFromBattle",
|
Battle::AI::Handlers::MoveFailureCheck.add("FleeFromBattle",
|
||||||
proc { |move, user, ai, battle|
|
proc { |move, user, ai, battle|
|
||||||
next !battle.pbCanRun?(user.index)
|
next !battle.pbCanRun?(user.index) || (user.wild? && user.battler.allAllies.length > 0)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
Battle::AI::Handlers::MoveEffectScore.add("FleeFromBattle",
|
Battle::AI::Handlers::MoveEffectScore.add("FleeFromBattle",
|
||||||
@@ -14,35 +14,50 @@ Battle::AI::Handlers::MoveEffectScore.add("FleeFromBattle",
|
|||||||
)
|
)
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# TODO: Review score modifiers.
|
#
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
Battle::AI::Handlers::MoveFailureCheck.add("SwitchOutUserStatusMove",
|
Battle::AI::Handlers::MoveFailureCheck.add("SwitchOutUserStatusMove",
|
||||||
proc { |move, user, ai, battle|
|
proc { |move, user, ai, battle|
|
||||||
next !battle.pbCanRun?(user.index) if user.wild?
|
if user.wild?
|
||||||
|
next !battle.pbCanRun?(user.index) || user.battler.allAllies.length > 0
|
||||||
|
end
|
||||||
next !battle.pbCanChooseNonActive?(user.index)
|
next !battle.pbCanChooseNonActive?(user.index)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserStatusMove",
|
Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserStatusMove",
|
||||||
proc { |score, move, user, ai, battle|
|
proc { |score, move, user, ai, battle|
|
||||||
# Wild Pokémon run from battle
|
# Wild Pokémon run from battle - generally don't prefer (don't want to end the battle too easily)
|
||||||
next score - 20 if user.wild?
|
next score - 20 if user.wild?
|
||||||
# Trainer-owned Pokémon switch out
|
# Trainer-owned Pokémon switch out
|
||||||
if ai.trainer.has_skill_flag?("ReserveLastPokemon") && battle.pbTeamAbleNonActiveCount(user.index) == 1
|
if ai.trainer.has_skill_flag?("ReserveLastPokemon") && battle.pbTeamAbleNonActiveCount(user.index) == 1
|
||||||
next Battle::AI::MOVE_USELESS_SCORE # Don't switch in ace
|
next Battle::AI::MOVE_USELESS_SCORE # Don't switch in ace
|
||||||
end
|
end
|
||||||
# Prefer if the user switching out will lose a negative effect
|
# Prefer if the user switching out will lose a negative effect
|
||||||
|
score += 20 if user.effects[PBEffects::PerishSong] > 0
|
||||||
score += 10 if user.effects[PBEffects::Confusion] > 1
|
score += 10 if user.effects[PBEffects::Confusion] > 1
|
||||||
|
score += 10 if user.effects[PBEffects::Attract] >= 0
|
||||||
|
# Consider the user's stat stages
|
||||||
|
if user.stages.any? { |key, val| val >= 2 }
|
||||||
|
score -= 15
|
||||||
|
elsif user.stages.any? { |key, val| val < 0 }
|
||||||
|
score += 10
|
||||||
|
end
|
||||||
|
# Consider the user's end of round damage/healing
|
||||||
|
eor_damage = user.rough_end_of_round_damage
|
||||||
|
score += 15 if eor_damage > 0
|
||||||
|
score -= 15 if eor_damage < 0
|
||||||
# Prefer if the user doesn't have any damaging moves
|
# Prefer if the user doesn't have any damaging moves
|
||||||
# TODO: Check effectiveness of moves.
|
score += 10 if !user.check_for_move { |m| m.damagingMove? }
|
||||||
score += 15 if !user.check_for_move { |m| m.damagingMove? }
|
# Don't prefer if the user's side has entry hazards on it
|
||||||
# Don't prefer the more stat raises the user has
|
score -= 10 if user.pbOwnSide.effects[PBEffects::Spikes] > 0
|
||||||
GameData::Stat.each_battle { |s| score -= user.stages[s.id] * 5 }
|
score -= 10 if user.pbOwnSide.effects[PBEffects::ToxicSpikes] > 0
|
||||||
|
score -= 10 if user.pbOwnSide.effects[PBEffects::StealthRock]
|
||||||
next score
|
next score
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# TODO: Review score modifiers.
|
#
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserDamagingMove",
|
Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserDamagingMove",
|
||||||
proc { |score, move, user, ai, battle|
|
proc { |score, move, user, ai, battle|
|
||||||
@@ -51,16 +66,29 @@ Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserDamagingMove",
|
|||||||
score -= 20 if ai.trainer.has_skill_flag?("ReserveLastPokemon") &&
|
score -= 20 if ai.trainer.has_skill_flag?("ReserveLastPokemon") &&
|
||||||
battle.pbTeamAbleNonActiveCount(user.index) == 1
|
battle.pbTeamAbleNonActiveCount(user.index) == 1
|
||||||
# Prefer if the user switching out will lose a negative effect
|
# Prefer if the user switching out will lose a negative effect
|
||||||
|
score += 20 if user.effects[PBEffects::PerishSong] > 0
|
||||||
score += 10 if user.effects[PBEffects::Confusion] > 1
|
score += 10 if user.effects[PBEffects::Confusion] > 1
|
||||||
# Don't prefer the more stat raises the user has
|
score += 10 if user.effects[PBEffects::Attract] >= 0
|
||||||
GameData::Stat.each_battle { |s| score -= user.stages[s.id] * 5 }
|
# Consider the user's stat stages
|
||||||
|
if user.stages.any? { |key, val| val >= 2 }
|
||||||
|
score -= 15
|
||||||
|
elsif user.stages.any? { |key, val| val < 0 }
|
||||||
|
score += 10
|
||||||
|
end
|
||||||
|
# Consider the user's end of round damage/healing
|
||||||
|
eor_damage = user.rough_end_of_round_damage
|
||||||
|
score += 15 if eor_damage > 0
|
||||||
|
score -= 15 if eor_damage < 0
|
||||||
|
# Don't prefer if the user's side has entry hazards on it
|
||||||
|
score -= 10 if user.pbOwnSide.effects[PBEffects::Spikes] > 0
|
||||||
|
score -= 10 if user.pbOwnSide.effects[PBEffects::ToxicSpikes] > 0
|
||||||
|
score -= 10 if user.pbOwnSide.effects[PBEffects::StealthRock]
|
||||||
next score
|
next score
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# TODO: Review score modifiers.
|
#
|
||||||
# TODO: Might need both MoveEffectScore and MoveEffectAgainstTargetScore.
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("LowerTargetAtkSpAtk1SwitchOutUser",
|
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("LowerTargetAtkSpAtk1SwitchOutUser",
|
||||||
proc { |move, user, target, ai, battle|
|
proc { |move, user, target, ai, battle|
|
||||||
@@ -75,25 +103,14 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("LowerTargetAtkSpAtk1Swi
|
|||||||
)
|
)
|
||||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("LowerTargetAtkSpAtk1SwitchOutUser",
|
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("LowerTargetAtkSpAtk1SwitchOutUser",
|
||||||
proc { |score, move, user, target, ai, battle|
|
proc { |score, move, user, target, ai, battle|
|
||||||
score = ai.get_score_for_target_stat_drop(score, target, move.move.statDown, false)
|
next ai.get_score_for_target_stat_drop(score, target, move.move.statDown, false)
|
||||||
if battle.pbCanChooseNonActive?(user.index)
|
|
||||||
# Don't want to switch in ace
|
|
||||||
score -= 20 if ai.trainer.has_skill_flag?("ReserveLastPokemon") &&
|
|
||||||
battle.pbTeamAbleNonActiveCount(user.index) == 1
|
|
||||||
# Prefer if the user switching out will lose a negative effect
|
|
||||||
score += 10 if user.effects[PBEffects::Confusion] > 1
|
|
||||||
# Prefer if the user doesn't have any damaging moves
|
|
||||||
# TODO: Check effectiveness of moves.
|
|
||||||
score += 15 if !user.check_for_move { |m| m.damagingMove? }
|
|
||||||
# Don't prefer the more stat raises the user has
|
|
||||||
GameData::Stat.each_battle { |s| score -= user.stages[s.id] * 5 }
|
|
||||||
end
|
|
||||||
next score
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
Battle::AI::Handlers::MoveEffectAgainstTargetScore.copy("SwitchOutUserDamagingMove",
|
||||||
|
"LowerTargetAtkSpAtk1SwitchOutUser")
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# TODO: Review score modifiers.
|
#
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
Battle::AI::Handlers::MoveFailureCheck.add("SwitchOutUserPassOnEffects",
|
Battle::AI::Handlers::MoveFailureCheck.add("SwitchOutUserPassOnEffects",
|
||||||
proc { |move, user, ai, battle|
|
proc { |move, user, ai, battle|
|
||||||
@@ -107,56 +124,110 @@ Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserPassOnEffects",
|
|||||||
battle.pbTeamAbleNonActiveCount(user.index) == 1
|
battle.pbTeamAbleNonActiveCount(user.index) == 1
|
||||||
# Don't prefer if the user will pass on a negative effect
|
# Don't prefer if the user will pass on a negative effect
|
||||||
score -= 10 if user.effects[PBEffects::Confusion] > 1
|
score -= 10 if user.effects[PBEffects::Confusion] > 1
|
||||||
|
score -= 15 if user.effects[PBEffects::Curse]
|
||||||
|
score -= 10 if user.effects[PBEffects::Embargo] > 1
|
||||||
|
score -= 15 if user.effects[PBEffects::GastroAcid]
|
||||||
|
score -= 10 if user.effects[PBEffects::HealBlock] > 1
|
||||||
|
score -= 10 if user.effects[PBEffects::LeechSeed] >= 0
|
||||||
|
score -= 20 if user.effects[PBEffects::PerishSong] > 0
|
||||||
|
# Prefer if the user will pass on a positive effect
|
||||||
|
score += 10 if user.effects[PBEffects::AquaRing]
|
||||||
|
score += 10 if user.effects[PBEffects::FocusEnergy] > 0
|
||||||
|
score += 10 if user.effects[PBEffects::Ingrain]
|
||||||
|
score += 8 if user.effects[PBEffects::MagnetRise] > 1
|
||||||
|
score += 10 if user.effects[PBEffects::Substitute] > 0
|
||||||
|
# Consider the user's stat stages
|
||||||
|
if user.stages.any? { |key, val| val >= 4 }
|
||||||
|
score += 25
|
||||||
|
elsif user.stages.any? { |key, val| val >= 2 }
|
||||||
|
score += 15
|
||||||
|
elsif user.stages.any? { |key, val| val < 0 }
|
||||||
|
score -= 15
|
||||||
|
end
|
||||||
|
# Consider the user's end of round damage/healing
|
||||||
|
eor_damage = user.rough_end_of_round_damage
|
||||||
|
score += 15 if eor_damage > 0
|
||||||
|
score -= 15 if eor_damage < 0
|
||||||
# Prefer if the user doesn't have any damaging moves
|
# Prefer if the user doesn't have any damaging moves
|
||||||
# TODO: Check effectiveness of moves.
|
|
||||||
score += 15 if !user.check_for_move { |m| m.damagingMove? }
|
score += 15 if !user.check_for_move { |m| m.damagingMove? }
|
||||||
# Prefer if the user will pass on good stat stages
|
# Don't prefer if the user's side has entry hazards on it
|
||||||
GameData::Stat.each_battle { |s| score += user.stages[s.id] * 5 }
|
score -= 10 if user.pbOwnSide.effects[PBEffects::Spikes] > 0
|
||||||
|
score -= 10 if user.pbOwnSide.effects[PBEffects::ToxicSpikes] > 0
|
||||||
|
score -= 10 if user.pbOwnSide.effects[PBEffects::StealthRock]
|
||||||
next score
|
next score
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# TODO: Review score modifiers.
|
#
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("SwitchOutTargetStatusMove",
|
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("SwitchOutTargetStatusMove",
|
||||||
proc { |move, user, target, ai, battle|
|
proc { |move, user, target, ai, battle|
|
||||||
next true if (!battle.moldBreaker && target.has_active_ability?(:SUCTIONCUPS)) ||
|
next move.move.pbFailsAgainstTarget?(user.battler, target.battler, false)
|
||||||
target.effects[PBEffects::Ingrain]
|
|
||||||
next true if !battle.canRun
|
|
||||||
next true if battle.wildBattle? && target.level > user.level
|
|
||||||
if battle.trainerBattle?
|
|
||||||
will_fail = true
|
|
||||||
battle.eachInTeamFromBattlerIndex(target.index) do |_pkmn, i|
|
|
||||||
next if !battle.pbCanSwitchIn?(target.index, i)
|
|
||||||
will_fail = false
|
|
||||||
break
|
|
||||||
end
|
|
||||||
next will_fail
|
|
||||||
end
|
|
||||||
next false
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("SwitchOutTargetStatusMove",
|
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("SwitchOutTargetStatusMove",
|
||||||
proc { |score, move, user, target, ai, battle|
|
proc { |score, move, user, target, ai, battle|
|
||||||
score += 15 if target.pbOwnSide.effects[PBEffects::Spikes] > 0
|
# Ends the battle - generally don't prefer (don't want to end the battle too easily)
|
||||||
score += 15 if target.pbOwnSide.effects[PBEffects::ToxicSpikes] > 0
|
next score - 10 if target.wild?
|
||||||
score += 15 if target.pbOwnSide.effects[PBEffects::StealthRock]
|
# Switches the target out
|
||||||
|
next Battle::AI::MOVE_USELESS_SCORE if target.effects[PBEffects::PerishSong] > 0
|
||||||
|
# Don't prefer if target is at low HP and could be knocked out instead
|
||||||
|
if ai.trainer.has_skill_flag?("HPAware")
|
||||||
|
score -= 10 if target.hp <= target.totalhp / 3
|
||||||
|
end
|
||||||
|
# Consider the target's stat stages
|
||||||
|
if target.stages.any? { |key, val| val >= 2 }
|
||||||
|
score += 15
|
||||||
|
elsif target.stages.any? { |key, val| val < 0 }
|
||||||
|
score -= 15
|
||||||
|
end
|
||||||
|
# Consider the target's end of round damage/healing
|
||||||
|
eor_damage = target.rough_end_of_round_damage
|
||||||
|
score -= 15 if eor_damage > 0
|
||||||
|
score += 15 if eor_damage < 0
|
||||||
|
# Prefer if the target's side has entry hazards on it
|
||||||
|
score += 10 if target.pbOwnSide.effects[PBEffects::Spikes] > 0
|
||||||
|
score += 10 if target.pbOwnSide.effects[PBEffects::ToxicSpikes] > 0
|
||||||
|
score += 10 if target.pbOwnSide.effects[PBEffects::StealthRock]
|
||||||
next score
|
next score
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# TODO: Review score modifiers.
|
#
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("SwitchOutTargetDamagingMove",
|
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("SwitchOutTargetDamagingMove",
|
||||||
proc { |score, move, user, target, ai, battle|
|
proc { |score, move, user, target, ai, battle|
|
||||||
if (battle.moldBreaker || !target.has_active_ability?(:SUCTIONCUPS)) &&
|
next score if target.wild?
|
||||||
!target.effects[PBEffects::Ingrain]
|
# No score modification if the target can't be made to switch out
|
||||||
score += 15 if target.pbOwnSide.effects[PBEffects::Spikes] > 0
|
next score if !battle.moldBreaker && target.has_active_ability?(:SUCTIONCUPS)
|
||||||
score += 15 if target.pbOwnSide.effects[PBEffects::ToxicSpikes] > 0
|
next score if target.effects[PBEffects::Ingrain]
|
||||||
score += 15 if target.pbOwnSide.effects[PBEffects::StealthRock]
|
# No score modification if the target can't be replaced
|
||||||
|
can_switch = false
|
||||||
|
battle.eachInTeamFromBattlerIndex(target.index) do |_pkmn, i|
|
||||||
|
can_switch = battle.pbCanSwitchIn?(target.index, i)
|
||||||
|
break if can_switch
|
||||||
end
|
end
|
||||||
|
next score if !can_switch
|
||||||
|
# Not score modification if the target has a Substitute
|
||||||
|
next score if target.effects[PBEffects::Substitute] > 0
|
||||||
|
# Don't want to switch out the target if it will faint from Perish Song
|
||||||
|
score -= 20 if target.effects[PBEffects::PerishSong] > 0
|
||||||
|
# Consider the target's stat stages
|
||||||
|
if target.stages.any? { |key, val| val >= 2 }
|
||||||
|
score += 15
|
||||||
|
elsif target.stages.any? { |key, val| val < 0 }
|
||||||
|
score -= 15
|
||||||
|
end
|
||||||
|
# Consider the target's end of round damage/healing
|
||||||
|
eor_damage = target.rough_end_of_round_damage
|
||||||
|
score -= 15 if eor_damage > 0
|
||||||
|
score += 15 if eor_damage < 0
|
||||||
|
# Prefer if the target's side has entry hazards on it
|
||||||
|
score += 10 if target.pbOwnSide.effects[PBEffects::Spikes] > 0
|
||||||
|
score += 10 if target.pbOwnSide.effects[PBEffects::ToxicSpikes] > 0
|
||||||
|
score += 10 if target.pbOwnSide.effects[PBEffects::StealthRock]
|
||||||
next score
|
next score
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -242,12 +313,9 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TrapTargetInBattle",
|
|||||||
next score if add_effect == -999 # Additional effect will be negated
|
next score if add_effect == -999 # Additional effect will be negated
|
||||||
score += add_effect
|
score += add_effect
|
||||||
# Score for target becoming trapped in battle
|
# Score for target becoming trapped in battle
|
||||||
# TODO: These checks are related to desire to switch, and there can be a lot
|
|
||||||
# more things to consider, e.g. effectiveness of the target's moves
|
|
||||||
# against its foes. Also applies to other code that calls
|
|
||||||
# can_become_trapped?
|
|
||||||
if target.effects[PBEffects::PerishSong] > 0 ||
|
if target.effects[PBEffects::PerishSong] > 0 ||
|
||||||
target.effects[PBEffects::Attract] >= 0 ||
|
target.effects[PBEffects::Attract] >= 0 ||
|
||||||
|
target.effects[PBEffects::Confusion] > 0 ||
|
||||||
eor_damage > 0
|
eor_damage > 0
|
||||||
score += 15
|
score += 15
|
||||||
end
|
end
|
||||||
@@ -290,12 +358,9 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TrapTargetInBattleLowerT
|
|||||||
next (move.damagingMove?) ? score : Battle::AI::MOVE_USELESS_SCORE
|
next (move.damagingMove?) ? score : Battle::AI::MOVE_USELESS_SCORE
|
||||||
end
|
end
|
||||||
# Score for target becoming trapped in battle
|
# Score for target becoming trapped in battle
|
||||||
# TODO: These checks are related to desire to switch, and there can be a lot
|
|
||||||
# more things to consider, e.g. effectiveness of the target's moves
|
|
||||||
# against its foes. Also applies to other code that calls
|
|
||||||
# can_become_trapped?
|
|
||||||
if target.effects[PBEffects::PerishSong] > 0 ||
|
if target.effects[PBEffects::PerishSong] > 0 ||
|
||||||
target.effects[PBEffects::Attract] >= 0 ||
|
target.effects[PBEffects::Attract] >= 0 ||
|
||||||
|
target.effects[PBEffects::Confusion] > 0 ||
|
||||||
eor_damage > 0
|
eor_damage > 0
|
||||||
score += 15
|
score += 15
|
||||||
end
|
end
|
||||||
@@ -324,12 +389,9 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TrapUserAndTargetInBattl
|
|||||||
next (move.damagingMove?) ? score : Battle::AI::MOVE_USELESS_SCORE
|
next (move.damagingMove?) ? score : Battle::AI::MOVE_USELESS_SCORE
|
||||||
end
|
end
|
||||||
# Score for target becoming trapped in battle
|
# Score for target becoming trapped in battle
|
||||||
# TODO: These checks are related to desire to switch, and there can be a lot
|
|
||||||
# more things to consider, e.g. effectiveness of the target's moves
|
|
||||||
# against its foes. Also applies to other code that calls
|
|
||||||
# can_become_trapped?
|
|
||||||
if target.effects[PBEffects::PerishSong] > 0 ||
|
if target.effects[PBEffects::PerishSong] > 0 ||
|
||||||
target.effects[PBEffects::Attract] >= 0 ||
|
target.effects[PBEffects::Attract] >= 0 ||
|
||||||
|
target.effects[PBEffects::Confusion] > 0 ||
|
||||||
eor_damage > 0
|
eor_damage > 0
|
||||||
score += 15
|
score += 15
|
||||||
end
|
end
|
||||||
@@ -339,16 +401,22 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TrapUserAndTargetInBattl
|
|||||||
)
|
)
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# TODO: Review score modifiers.
|
#
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
Battle::AI::Handlers::MoveFailureCheck.add("TrapAllBattlersInBattleForOneTurn",
|
Battle::AI::Handlers::MoveFailureCheck.add("TrapAllBattlersInBattleForOneTurn",
|
||||||
proc { |move, user, ai, battle|
|
proc { |move, user, ai, battle|
|
||||||
next battle.field.effects[PBEffects::FairyLock] > 0
|
next battle.field.effects[PBEffects::FairyLock] > 0
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
Battle::AI::Handlers::MoveEffectScore.add("TrapAllBattlersInBattleForOneTurn",
|
||||||
|
proc { |score, move, user, ai, battle|
|
||||||
|
# Trapping for just one turn isn't so significant, so generally don't prefer
|
||||||
|
next score - 10
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# TODO: Review score modifiers.
|
#
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# PursueSwitchingFoe
|
# PursueSwitchingFoe
|
||||||
|
|
||||||
@@ -661,7 +729,6 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetUsingDiffer
|
|||||||
if move_data.status?
|
if move_data.status?
|
||||||
# Prefer encoring status moves
|
# Prefer encoring status moves
|
||||||
if [:User, :BothSides].include?(move_data.target)
|
if [:User, :BothSides].include?(move_data.target)
|
||||||
# TODO: This target distinction was in the old code. Is it appropriate?
|
|
||||||
score += 10
|
score += 10
|
||||||
else
|
else
|
||||||
score += 8
|
score += 8
|
||||||
|
|||||||
@@ -242,8 +242,7 @@ Battle::ItemEffects::SpeedCalc.copy(:MACHOBRACE, :POWERANKLET, :POWERBAND,
|
|||||||
|
|
||||||
Battle::ItemEffects::SpeedCalc.add(:QUICKPOWDER,
|
Battle::ItemEffects::SpeedCalc.add(:QUICKPOWDER,
|
||||||
proc { |item, battler, mult|
|
proc { |item, battler, mult|
|
||||||
next mult * 2 if battler.isSpecies?(:DITTO) &&
|
next mult * 2 if battler.isSpecies?(:DITTO) && !battler.effects[PBEffects::Transform]
|
||||||
!battler.effects[PBEffects::Transform]
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -263,7 +262,7 @@ Battle::ItemEffects::WeightCalc.add(:FLOATSTONE,
|
|||||||
|
|
||||||
Battle::ItemEffects::HPHeal.add(:AGUAVBERRY,
|
Battle::ItemEffects::HPHeal.add(:AGUAVBERRY,
|
||||||
proc { |item, battler, battle, forced|
|
proc { |item, battler, battle, forced|
|
||||||
next battler.pbConfusionBerry(item, forced, 4,
|
next battler.pbConfusionBerry(item, forced, :SPECIAL_DEFENSE,
|
||||||
_INTL("For {1}, the {2} was too bitter!", battler.pbThis(true), GameData::Item.get(item).name)
|
_INTL("For {1}, the {2} was too bitter!", battler.pbThis(true), GameData::Item.get(item).name)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -294,7 +293,7 @@ Battle::ItemEffects::HPHeal.add(:BERRYJUICE,
|
|||||||
|
|
||||||
Battle::ItemEffects::HPHeal.add(:FIGYBERRY,
|
Battle::ItemEffects::HPHeal.add(:FIGYBERRY,
|
||||||
proc { |item, battler, battle, forced|
|
proc { |item, battler, battle, forced|
|
||||||
next battler.pbConfusionBerry(item, forced, 0,
|
next battler.pbConfusionBerry(item, forced, :ATTACK,
|
||||||
_INTL("For {1}, the {2} was too spicy!", battler.pbThis(true), GameData::Item.get(item).name)
|
_INTL("For {1}, the {2} was too spicy!", battler.pbThis(true), GameData::Item.get(item).name)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -308,7 +307,7 @@ Battle::ItemEffects::HPHeal.add(:GANLONBERRY,
|
|||||||
|
|
||||||
Battle::ItemEffects::HPHeal.add(:IAPAPABERRY,
|
Battle::ItemEffects::HPHeal.add(:IAPAPABERRY,
|
||||||
proc { |item, battler, battle, forced|
|
proc { |item, battler, battle, forced|
|
||||||
next battler.pbConfusionBerry(item, forced, 1,
|
next battler.pbConfusionBerry(item, forced, :DEFENSE,
|
||||||
_INTL("For {1}, the {2} was too sour!", battler.pbThis(true), GameData::Item.get(item).name)
|
_INTL("For {1}, the {2} was too sour!", battler.pbThis(true), GameData::Item.get(item).name)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -338,7 +337,7 @@ Battle::ItemEffects::HPHeal.add(:LIECHIBERRY,
|
|||||||
|
|
||||||
Battle::ItemEffects::HPHeal.add(:MAGOBERRY,
|
Battle::ItemEffects::HPHeal.add(:MAGOBERRY,
|
||||||
proc { |item, battler, battle, forced|
|
proc { |item, battler, battle, forced|
|
||||||
next battler.pbConfusionBerry(item, forced, 2,
|
next battler.pbConfusionBerry(item, forced, :SPEED,
|
||||||
_INTL("For {1}, the {2} was too sweet!", battler.pbThis(true), GameData::Item.get(item).name)
|
_INTL("For {1}, the {2} was too sweet!", battler.pbThis(true), GameData::Item.get(item).name)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -436,7 +435,7 @@ Battle::ItemEffects::HPHeal.add(:STARFBERRY,
|
|||||||
|
|
||||||
Battle::ItemEffects::HPHeal.add(:WIKIBERRY,
|
Battle::ItemEffects::HPHeal.add(:WIKIBERRY,
|
||||||
proc { |item, battler, battle, forced|
|
proc { |item, battler, battle, forced|
|
||||||
next battler.pbConfusionBerry(item, forced, 3,
|
next battler.pbConfusionBerry(item, forced, :SPECIAL_ATTACK,
|
||||||
_INTL("For {1}, the {2} was too dry!", battler.pbThis(true), GameData::Item.get(item).name)
|
_INTL("For {1}, the {2} was too dry!", battler.pbThis(true), GameData::Item.get(item).name)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user