mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-06 06:01:46 +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
|
||||
#=============================================================================
|
||||
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 && !canConsumePinchBerry?(Settings::MECHANICS_GENERATION >= 7)
|
||||
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))
|
||||
end
|
||||
end
|
||||
flavor_stat = [:ATTACK, :DEFENSE, :SPEED, :SPECIAL_ATTACK, :SPECIAL_DEFENSE][flavor]
|
||||
self.nature.stat_changes.each do |change|
|
||||
next if change[1] > 0 || change[0] != flavor_stat
|
||||
if self.nature.stat_changes.any? { |val| val[0] == confuse_stat && val[1] < 0 }
|
||||
@battle.pbDisplay(confuse_msg)
|
||||
pbConfuse if pbCanConfuseSelf?(false)
|
||||
break
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#===============================================================================
|
||||
class Battle::Move::FleeFromBattle < Battle::Move
|
||||
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!"))
|
||||
return true
|
||||
end
|
||||
@@ -23,7 +23,7 @@ end
|
||||
class Battle::Move::SwitchOutUserStatusMove < Battle::Move
|
||||
def pbMoveFailed?(user, targets)
|
||||
if user.wild?
|
||||
if !@battle.pbCanRun?(user.index)
|
||||
if !@battle.pbCanRun?(user.index) || user.allAllies.length > 0
|
||||
@battle.pbDisplay(_INTL("But it failed!"))
|
||||
return true
|
||||
end
|
||||
@@ -145,9 +145,9 @@ class Battle::Move::SwitchOutUserPassOnEffects < Battle::Move
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
# In wild battles, makes target flee. Fails if target is a higher level than the
|
||||
# user.
|
||||
# In trainer battles, target switches out.
|
||||
# When used against a sole wild Pokémon, makes target flee and ends the battle;
|
||||
# fails if target is a higher level than the user.
|
||||
# When used against a trainer's Pokémon, target switches out.
|
||||
# For status moves. (Roar, Whirlwind)
|
||||
#===============================================================================
|
||||
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
|
||||
return true
|
||||
end
|
||||
if !@battle.canRun
|
||||
@battle.pbDisplay(_INTL("But it failed!")) if show_message
|
||||
return true
|
||||
end
|
||||
if @battle.wildBattle? && target.level > user.level
|
||||
@battle.pbDisplay(_INTL("But it failed!")) if show_message
|
||||
return true
|
||||
end
|
||||
if @battle.trainerBattle?
|
||||
if target.wild? && target.allAllies.length == 0 && @battle.canRun
|
||||
# End the battle
|
||||
if target.level > user.level
|
||||
@battle.pbDisplay(_INTL("But it failed!")) if show_message
|
||||
return true
|
||||
end
|
||||
elsif !target.wild?
|
||||
# Switch target out
|
||||
canSwitch = false
|
||||
@battle.eachInTeamFromBattlerIndex(target.index) do |_pkmn, i|
|
||||
next if !@battle.pbCanSwitchIn?(target.index, i)
|
||||
canSwitch = true
|
||||
break
|
||||
canSwitch = @battle.pbCanSwitchIn?(target.index, i)
|
||||
break if canSwitch
|
||||
end
|
||||
if !canSwitch
|
||||
@battle.pbDisplay(_INTL("But it failed!")) if show_message
|
||||
return true
|
||||
end
|
||||
else
|
||||
@battle.pbDisplay(_INTL("But it failed!")) if show_message
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
def pbEffectGeneral(user)
|
||||
@battle.decision = 3 if @battle.wildBattle? # Escaped from battle
|
||||
def pbEffectAgainstTarget(user, target)
|
||||
@battle.decision = 3 if target.wild? # Escaped from battle
|
||||
end
|
||||
|
||||
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
|
||||
targets.each do |b|
|
||||
next if b.fainted? || b.damageState.unaffected
|
||||
next if b.wild?
|
||||
next if b.effects[PBEffects::Ingrain]
|
||||
next if b.hasActiveAbility?(:SUCTIONCUPS) && !@battle.moldBreaker
|
||||
newPkmn = @battle.pbGetReplacementPokemonIndex(b.index, true) # Random
|
||||
@@ -218,24 +220,26 @@ class Battle::Move::SwitchOutTargetStatusMove < Battle::Move
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
# In wild battles, makes target flee. Fails if target is a higher level than the
|
||||
# user.
|
||||
# In trainer battles, target switches out.
|
||||
# When used against a sole wild Pokémon, makes target flee and ends the battle;
|
||||
# fails if target is a higher level than the user.
|
||||
# When used against a trainer's Pokémon, target switches out.
|
||||
# For damaging moves. (Circle Throw, Dragon Tail)
|
||||
#===============================================================================
|
||||
class Battle::Move::SwitchOutTargetDamagingMove < Battle::Move
|
||||
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))
|
||||
@battle.decision = 3
|
||||
@battle.decision = 3 # Escaped from battle
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
targets.each do |b|
|
||||
next if b.fainted? || b.damageState.unaffected || b.damageState.substitute
|
||||
next if b.wild?
|
||||
next if b.effects[PBEffects::Ingrain]
|
||||
next if b.hasActiveAbility?(:SUCTIONCUPS) && !@battle.moldBreaker
|
||||
newPkmn = @battle.pbGetReplacementPokemonIndex(b.index, true) # Random
|
||||
|
||||
@@ -5,13 +5,10 @@ class Battle::AI
|
||||
attr_reader :battle
|
||||
attr_reader :trainer
|
||||
attr_reader :battlers
|
||||
attr_reader :roles
|
||||
attr_reader :user, :target, :move
|
||||
|
||||
def initialize(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
|
||||
|
||||
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
|
||||
# Don't bother choosing to switch if all replacements are poorly rated
|
||||
if @trainer.high_skill? && !mandatory
|
||||
# TODO: Should the current battler be rated as well, to provide a
|
||||
# threshold instead of using a threshold of 100?
|
||||
return -1 if reserves[0][1] < 100 # Best replacement rated at <100, don't switch
|
||||
return -1 if reserves[0][1] < 100 # If best replacement rated at <100, don't switch
|
||||
end
|
||||
# Return the party index of the best rated replacement Pokémon
|
||||
return reserves[0][0]
|
||||
@@ -136,9 +134,8 @@ class Battle::AI
|
||||
pkmn.moves.each do |m|
|
||||
next if m.power == 0 || (m.pp == 0 && m.total_pp > 0)
|
||||
@battle.battlers[idxBattler].allOpposing.each do |b|
|
||||
next if pokemon_can_absorb_move?(b.pokemon, m, m.type)
|
||||
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
|
||||
end
|
||||
end
|
||||
@@ -175,7 +172,6 @@ end
|
||||
|
||||
#===============================================================================
|
||||
# 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,
|
||||
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.
|
||||
# TODO: Check other abilities that provide immunities?
|
||||
# Pokémon can't do anything to any foe because its ability absorbs all damage
|
||||
# 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|
|
||||
next false if battler.battler.turnCount < 2 # Don't switch out too quickly
|
||||
next false if battler.battler.hasMoldBreaker?
|
||||
non_wonder_guard_foe_exists = false
|
||||
has_super_effective_move = false
|
||||
foe_types = battler.pbTypes(true)
|
||||
next false if foe_types.length == 0
|
||||
# Check if battler can damage any of its foes
|
||||
can_damage_foe = false
|
||||
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
|
||||
non_wonder_guard_foe_exists = true # Wonder Guard is being overcome already
|
||||
can_damage_foe = true # Foe is being damaged already
|
||||
break
|
||||
end
|
||||
# Check for super-effective damaging moves
|
||||
# Check for battler's moves that can damage the foe (b)
|
||||
battler.battler.eachMove do |move|
|
||||
next if move.statusMove?
|
||||
if ["IgnoreTargetAbility",
|
||||
"CategoryDependsOnHigherDamageIgnoreTargetAbility"].include?(move.function)
|
||||
has_super_effective_move = true
|
||||
can_damage_foe = true
|
||||
break
|
||||
end
|
||||
eff = Effectiveness.calculate(move.pbCalcType(battler.battler), *foe_types)
|
||||
if Effectiveness.super_effective?(eff)
|
||||
has_super_effective_move = true
|
||||
if !ai.pokemon_can_absorb_move?(b, move, move.pbCalcType(battler.battler))
|
||||
can_damage_foe = true
|
||||
break
|
||||
end
|
||||
end
|
||||
break if has_super_effective_move
|
||||
break if can_damage_foe
|
||||
end
|
||||
if !non_wonder_guard_foe_exists && !has_super_effective_move
|
||||
# Check reserves for super-effective moves; only switch if there are any
|
||||
reserve_has_super_effective_move = false
|
||||
reserves.each do |pkmn|
|
||||
next false if can_damage_foe
|
||||
# Check if a reserve could damage any foe; only switch if one could
|
||||
reserve_can_damage_foe = false
|
||||
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|
|
||||
next if move.status_move?
|
||||
if ["IgnoreTargetAbility",
|
||||
"CategoryDependsOnHigherDamageIgnoreTargetAbility"].include?(move.function_code)
|
||||
reserve_has_super_effective_move = true
|
||||
reserve_can_damage_foe = true
|
||||
break
|
||||
end
|
||||
eff = Effectiveness.calculate(move.type, *foe_types)
|
||||
if Effectiveness.super_effective?(eff)
|
||||
reserve_has_super_effective_move = true
|
||||
if !ai.pokemon_can_absorb_move?(b, move, move.type)
|
||||
reserve_can_damage_foe = true
|
||||
break
|
||||
end
|
||||
end
|
||||
break if reserve_has_super_effective_move
|
||||
break if reserve_can_damage_foe
|
||||
end
|
||||
next false if !reserve_has_super_effective_move
|
||||
PBDebug.log_ai("#{battler.name} wants to switch because it can't do anything against Wonder Guard")
|
||||
next true
|
||||
break if reserve_can_damage_foe
|
||||
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
|
||||
# and powerful.
|
||||
# TODO: Review switch deciding.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::ShouldSwitch.add(:high_damage_from_foe,
|
||||
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
|
||||
# is complicated enough to implement that I'm not bothering. It's
|
||||
# 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)
|
||||
break if has_super_effective_move
|
||||
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.
|
||||
# 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,
|
||||
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.
|
||||
# TODO: Review score modifier.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveScore.add(:good_move_for_choice_item,
|
||||
proc { |score, move, user, ai, battle|
|
||||
if ai.trainer.medium_skill?
|
||||
if user.has_active_item?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF]) ||
|
||||
user.has_active_ability?(:GORILLATACTICS)
|
||||
old_score = score
|
||||
# Really don't prefer status moves (except Trick)
|
||||
if move.statusMove? && move.function != "UserTargetSwapItems"
|
||||
score -= 25
|
||||
PBDebug.log_score_change(score - old_score, "don't want to be Choiced into a status move")
|
||||
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
|
||||
next score if !ai.trainer.medium_skill?
|
||||
next score if !user.has_active_item?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF]) &&
|
||||
!user.has_active_ability?(:GORILLATACTICS)
|
||||
old_score = score
|
||||
# Really don't prefer status moves (except Trick)
|
||||
if move.statusMove? && move.function != "UserTargetSwapItems"
|
||||
score -= 25
|
||||
PBDebug.log_score_change(score - old_score, "don't want to be Choiced into a status move")
|
||||
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")
|
||||
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
|
||||
# faster than the user.
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:target_can_powder_fire_moves,
|
||||
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
|
||||
# and is immune to Electric moves.
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Don't prefer a move that is stopped by Wide Guard if any foe has
|
||||
# previously used Wide Guard.
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:target_can_make_moves_Electric_and_be_immune,
|
||||
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) &&
|
||||
!Effectiveness.ineffective?(target.effectiveness_of_type_against_battler(:ELECTRIC, user))
|
||||
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.
|
||||
@@ -293,6 +310,12 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:predicted_damage,
|
||||
old_score = score
|
||||
score += 10
|
||||
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
|
||||
@@ -313,6 +336,7 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:external_flinching_effe
|
||||
if battle.moldBreaker || !target.has_active_ability?([:INNERFOCUS, :SHIELDDUST])
|
||||
old_score = score
|
||||
score += 8
|
||||
score += 5 if move.move.multiHitMove?
|
||||
PBDebug.log_score_change(score - old_score, "added chance to cause flinching")
|
||||
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
|
||||
# stuff in def pbEffectsOnMakingHit (worse if the move is a multi-hit one)
|
||||
# and end-of-move stuff in def pbEffectsAfterMove.
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:trigger_target_ability_or_item_upon_hit,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
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
|
||||
# an effect that's good for the user (Poison Touch/Pickpocket).
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Don't prefer contact move if making contact with the target could
|
||||
# trigger an effect that's bad for the user (Static, etc.).
|
||||
# => Also check if target has previously used Spiky Shield/King's Shield/
|
||||
# Baneful Bunker, and don't prefer move if so
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:trigger_user_ability_upon_hit,
|
||||
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
|
||||
# effect revolve around damaging moves.
|
||||
if (Battle::AbilityEffects::OnDealingHit[user.ability] && move.move.contactMove?) ||
|
||||
Battle::AbilityEffects::OnEndOfUsingMove[user.ability]
|
||||
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
|
||||
@@ -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
|
||||
# from the raised Attack.
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:damaging_a_raging_target,
|
||||
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
|
||||
|
||||
@@ -38,14 +38,16 @@ class Battle::AI
|
||||
|
||||
# Assumes that pkmn's ability is not negated by a global effect (e.g.
|
||||
# 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)
|
||||
return false if pkmn.is_a?(Battle::AI::AIBattler) && !pkmn.ability_active?
|
||||
# Check pkmn's ability
|
||||
# Anything with a Battle::AbilityEffects::MoveImmunity handler
|
||||
case pkmn.ability_id
|
||||
when :BULLETPROOF
|
||||
return move.bombMove?
|
||||
move_data = GameData::Move.get(move.id)
|
||||
return move_data.has_flag?("Bomb")
|
||||
when :FLASHFIRE
|
||||
return move_type == :FIRE
|
||||
when :LIGHTNINGROD, :MOTORDRIVE, :VOLTABSORB
|
||||
@@ -53,7 +55,8 @@ class Battle::AI
|
||||
when :SAPSIPPER
|
||||
return move_type == :GRASS
|
||||
when :SOUNDPROOF
|
||||
return move.soundMove?
|
||||
move_data = GameData::Move.get(move.id)
|
||||
return move_data.has_flag?("Sound")
|
||||
when :STORMDRAIN, :WATERABSORB, :DRYSKIN
|
||||
return move_type == :WATER
|
||||
when :TELEPATHY
|
||||
@@ -62,7 +65,7 @@ class Battle::AI
|
||||
when :WONDERGUARD
|
||||
types = pkmn.types
|
||||
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
|
||||
return false
|
||||
end
|
||||
@@ -157,24 +160,72 @@ class Battle::AI
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
# TODO: Add more items.
|
||||
BASE_ITEM_RATINGS = {
|
||||
4 => [:CHOICEBAND, :CHOICESCARF, :CHOICESPECS, :DEEPSEATOOTH, :LEFTOVERS,
|
||||
:LIGHTBALL, :THICKCLUB],
|
||||
3 => [:ADAMANTORB, :GRISEOUSORB, :LIFEORB, :LUSTROUSORB, :SOULDEW],
|
||||
2 => [:BLACKBELT, :BLACKGLASSES, :CHARCOAL, :DRAGONFANG, :HARDSTONE,
|
||||
10 => [:EVIOLITE, :FOCUSSASH, :LIFEORB, :THICKCLUB],
|
||||
9 => [:ASSAULTVEST, :BLACKSLUDGE, :CHOICEBAND, :CHOICESCARF, :CHOICESPECS,
|
||||
:DEEPSEATOOTH, :LEFTOVERS],
|
||||
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,
|
||||
:POISONBARB, :SHARPBEAK, :SILKSCARF, :SILVERPOWDER, :SOFTSAND,
|
||||
:POISONBARB, :SHARPBEAK, :SILKSCARF,:SILVERPOWDER, :SOFTSAND,
|
||||
:SPELLTAG, :TWISTEDSPOON,
|
||||
:ODDINCENSE, :ROCKINCENSE, :ROSEINCENSE, :SEAINCENSE, :WAVEINCENSE,
|
||||
# Plates
|
||||
:DRACOPLATE, :DREADPLATE, :EARTHPLATE, :FISTPLATE, :FLAMEPLATE,
|
||||
:ICICLEPLATE, :INSECTPLATE, :IRONPLATE, :MEADOWPLATE, :MINDPLATE,
|
||||
:PIXIEPLATE, :SKYPLATE, :SPLASHPLATE, :SPOOKYPLATE, :STONEPLATE,
|
||||
:TOXICPLATE, :ZAPPLATE,
|
||||
:ODDINCENSE, :ROCKINCENSE, :ROSEINCENSE, :SEAINCENSE, :WAVEINCENSE,
|
||||
:MUSCLEBAND, :WISEGLASSES],
|
||||
1 => [:METRONOME],
|
||||
-2 => [:LAGGINGTAIL, :STICKYBARB],
|
||||
-4 => [:BLACKSLUDGE, :FLAMEORB, :IRONBALL, :TOXICORB]
|
||||
# Weather/terrain extenders
|
||||
:DAMPROCK, :HEATROCK, :ICYROCK, :SMOOTHROCK, :TERRAINEXTENDER],
|
||||
4 => [:ADRENALINEORB, :APICOTBERRY, :BLUNDERPOLICY, :CHESTOBERRY,
|
||||
:EJECTPACK, :ENIGMABERRY, :GANLONBERRY, :HEAVYDUTYBOOTS,
|
||||
: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
|
||||
|
||||
@@ -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,
|
||||
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
|
||||
}
|
||||
)
|
||||
@@ -367,6 +480,20 @@ Battle::AI::Handlers::ItemRanking.add(:CHOICESPECS,
|
||||
|
||||
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,
|
||||
proc { |item, score, battler, ai|
|
||||
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,
|
||||
proc { |item, score, battler, ai|
|
||||
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,
|
||||
proc { |item, score, battler, ai|
|
||||
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,
|
||||
proc { |item, score, battler, ai|
|
||||
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,
|
||||
proc { |item, score, battler, ai|
|
||||
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,
|
||||
proc { |item, score, battler, ai|
|
||||
next 0 if !battler.battler.isSpecies?(:LATIAS) && !battler.battler.isSpecies?(:LATIOS)
|
||||
if Settings::SOUL_DEW_POWERS_UP_TYPES
|
||||
next 0 if !battler.has_damaging_move_of_type?(:PSYCHIC, :DRAGON)
|
||||
elsif !battler.check_for_move { |m| m.specialMove?(m.type) }
|
||||
next 1 # Also boosts SpDef
|
||||
elsif battler.check_for_move { |m| m.specialMove?(m.type) }
|
||||
next 10
|
||||
else
|
||||
next 6 # Boosts SpDef
|
||||
end
|
||||
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,
|
||||
proc { |item, score, battler, ai|
|
||||
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,
|
||||
proc { |item|
|
||||
next [:BLACKBELT, :BLACKGLASSES, :CHARCOAL, :DRAGONFANG, :HARDSTONE,
|
||||
@@ -469,3 +864,36 @@ Battle::AI::Handlers::ItemRanking.addIf(:type_boosting_items,
|
||||
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
|
||||
@battler = @ai.battle.battlers[@index]
|
||||
@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
|
||||
|
||||
def pokemon; return battler.pokemon; end
|
||||
@@ -232,12 +228,7 @@ class Battle::AI::AIBattler
|
||||
end
|
||||
alias pbHasType? has_type?
|
||||
|
||||
# TODO: Also make a def effectiveness_of_move_against_battler which calls
|
||||
# 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)
|
||||
def effectiveness_of_type_against_battler(type, user = nil, move = nil)
|
||||
ret = Effectiveness::NORMAL_EFFECTIVE_MULTIPLIER
|
||||
return ret if !type
|
||||
return ret if type == :GROUND && has_type?(:FLYING) && has_active_item?(:IRONBALL)
|
||||
@@ -250,7 +241,16 @@ class Battle::AI::AIBattler
|
||||
end
|
||||
else
|
||||
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
|
||||
ret *= 2 if self.effects[PBEffects::TarShot] && type == :FIRE
|
||||
end
|
||||
@@ -414,7 +414,9 @@ class Battle::AI::AIBattler
|
||||
break
|
||||
end
|
||||
# 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
|
||||
end
|
||||
|
||||
@@ -424,13 +426,12 @@ class Battle::AI::AIBattler
|
||||
# battler if it is holding it.
|
||||
# Return values are typically between -10 and +10. 0 is indifferent, positive
|
||||
# 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
|
||||
# as def wants_ability?.
|
||||
# NOTE: This method assumes the item isn't being negated. The calculations
|
||||
# that call this method separately check for it being negated, because
|
||||
# they need to do something special in that case.
|
||||
def wants_item?(item)
|
||||
item = :NONE if !item
|
||||
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
|
||||
ret = 0
|
||||
Battle::AI::BASE_ITEM_RATINGS.each_pair do |val, items|
|
||||
@@ -439,7 +440,9 @@ class Battle::AI::AIBattler
|
||||
break
|
||||
end
|
||||
# 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
|
||||
# additional (negative) effect when flung
|
||||
if item != :NONE && has_move_with_function?("ThrowUserItemAtTarget")
|
||||
@@ -481,7 +484,18 @@ class Battle::AI::AIBattler
|
||||
end
|
||||
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)
|
||||
# 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
|
||||
# Status cure
|
||||
cured_status = {
|
||||
|
||||
@@ -101,7 +101,6 @@ class Battle::AI::AIMove
|
||||
# Get the move's type
|
||||
calc_type = rough_type
|
||||
# Decide whether the move has 50% chance of higher of being a critical hit
|
||||
# TODO: Make this a gradient/probability rather than all-or-nothing?
|
||||
crit_stage = rough_critical_hit_stage
|
||||
is_critical = crit_stage >= Battle::Move::CRITICAL_HIT_RATIOS.length ||
|
||||
Battle::Move::CRITICAL_HIT_RATIOS[crit_stage] <= 2
|
||||
@@ -325,7 +324,7 @@ class Battle::AI::AIMove
|
||||
end
|
||||
end
|
||||
# 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
|
||||
# Burn
|
||||
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
|
||||
end
|
||||
# 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 ||
|
||||
user.effects[PBEffects::Attract] >= 0 ||
|
||||
eor_damage > 0
|
||||
@@ -632,7 +628,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseTargetAttack2Confu
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetAttack2ConfuseTarget",
|
||||
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)
|
||||
end
|
||||
# Score for stat raise
|
||||
@@ -654,7 +650,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseTargetSpAtk1Confus
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetSpAtk1ConfuseTarget",
|
||||
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)
|
||||
end
|
||||
# Score for stat raise
|
||||
|
||||
@@ -240,7 +240,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("ParalyzeTarget",
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("ParalyzeTargetIfNotTypeImmune",
|
||||
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 move.statusMove? && !target.battler.pbCanParalyze?(user.battler, false, move.move)
|
||||
next false
|
||||
|
||||
@@ -1094,8 +1094,6 @@ Battle::AI::Handlers::MoveEffectScore.add("ProtectUserSideFromPriorityMoves",
|
||||
useless = true
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
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? }
|
||||
useless = false
|
||||
# General preference
|
||||
@@ -1343,9 +1341,6 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("EnsureNextMoveAlwaysHits
|
||||
# Prefer if the user knows moves with low accuracy
|
||||
user.battler.eachMove do |m|
|
||||
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.pbBaseAccuracy(user.battler, target.battler) if ai.trainer.medium_skill?
|
||||
score += 5 if acc < 90 && acc != 0
|
||||
@@ -1503,7 +1498,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TargetMovesBecomeElectri
|
||||
next if !m.damagingMove?
|
||||
m_type = m.pbCalcType(target.battler)
|
||||
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
|
||||
case m_type
|
||||
when :FIRE
|
||||
|
||||
@@ -451,6 +451,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("TwoTurnAttackInvulnerab
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if !target.opposes?(user)
|
||||
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 target.battler.semiInvulnerable? || target.effects[PBEffects::SkyDrop] >= 0
|
||||
next false
|
||||
|
||||
@@ -11,10 +11,14 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTakesTargetItem",
|
||||
# User can steal the target's item; score it
|
||||
user_item_preference = user.wants_item?(target.item_id)
|
||||
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_no_item_preference = target.wants_item?(:NONE)
|
||||
score += user_item_preference - user_no_item_preference
|
||||
score += target_item_preference - target_no_item_preference
|
||||
target_diff = target_no_item_preference - target_item_preference
|
||||
target_diff = 0 if !target.item_active?
|
||||
score += user_diff * 4
|
||||
score -= target_diff * 4
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -33,10 +37,14 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TargetTakesUserItem",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
user_item_preference = user.wants_item?(user.item_id)
|
||||
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_no_item_preference = target.wants_item?(:NONE)
|
||||
score += user_no_item_preference - user_item_preference
|
||||
score += target_no_item_preference - target_item_preference
|
||||
target_diff = target_item_preference - target_no_item_preference
|
||||
target_diff = 0 if !target.item_active?
|
||||
score += user_diff * 4
|
||||
score -= target_diff * 4
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -58,10 +66,14 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTargetSwapItems",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
user_new_item_preference = user.wants_item?(target.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_old_item_preference = target.wants_item?(target.item_id)
|
||||
score += user_new_item_preference - user_old_item_preference
|
||||
score += target_old_item_preference - target_new_item_preference
|
||||
target_diff = target_new_item_preference - target_old_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
|
||||
score -= 15 if 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",
|
||||
proc { |score, move, user, ai, battle|
|
||||
user_new_item_preference = user.wants_item?(user.battler.recycleItem)
|
||||
user_old_item_preference = user.wants_item?(:NONE)
|
||||
score += (user_new_item_preference - user_old_item_preference) * 2
|
||||
next Battle::AI::MOVE_USELESS_SCORE if !user.item_active?
|
||||
item_preference = user.wants_item?(user.battler.recycleItem)
|
||||
no_item_preference = user.wants_item?(:NONE)
|
||||
score += (item_preference - no_item_preference) * 4
|
||||
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.effects[PBEffects::Substitute] > 0
|
||||
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
|
||||
target_item_preference = target.wants_item?(target.item_id)
|
||||
target_no_item_preference = target.wants_item?(:NONE)
|
||||
score += (target_item_preference - target_no_item_preference) * 2
|
||||
item_preference = target.wants_item?(target.item_id)
|
||||
no_item_preference = target.wants_item?(:NONE)
|
||||
score -= (no_item_preference - item_preference) * 4
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -119,10 +133,11 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DestroyTargetBerryOrGem"
|
||||
next score if user.battler.unlosableItem?(target.item)
|
||||
next score if target.effects[PBEffects::Substitute] > 0
|
||||
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
|
||||
target_item_preference = target.wants_item?(target.item_id)
|
||||
target_no_item_preference = target.wants_item?(:NONE)
|
||||
score += (target_item_preference - target_no_item_preference) * 2
|
||||
item_preference = target.wants_item?(target.item_id)
|
||||
no_item_preference = target.wants_item?(:NONE)
|
||||
score -= (no_item_preference - item_preference) * 4
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -141,9 +156,11 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("CorrodeTargetItem",
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("CorrodeTargetItem",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
target_item_preference = target.wants_item?(target.item_id)
|
||||
target_no_item_preference = target.wants_item?(:NONE)
|
||||
score += (target_item_preference - target_no_item_preference) * 2
|
||||
item_preference = target.wants_item?(target.item_id)
|
||||
no_item_preference = target.wants_item?(:NONE)
|
||||
target_diff = no_item_preference - item_preference
|
||||
target_diff = 0 if !target.item_active?
|
||||
score += target_diff * 4
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -159,7 +176,8 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("StartTargetCannotUseIte
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("StartTargetCannotUseItem",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
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)
|
||||
next Battle::AI::MOVE_USELESS_SCORE if item_score <= 0 # Item has no effect or is bad
|
||||
score += item_score * 2
|
||||
@@ -179,7 +197,8 @@ Battle::AI::Handlers::MoveEffectScore.add("StartNegateHeldItems",
|
||||
next if !b.item
|
||||
# Skip b if its item is disabled
|
||||
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
|
||||
# NOTE: Same as b.item_active? but ignoring the Magic Room part.
|
||||
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)
|
||||
end
|
||||
# Score the target no longer having the item
|
||||
target_item_preference = target.wants_item?(target.item_id)
|
||||
target_no_item_preference = target.wants_item?(:NONE)
|
||||
score += (target_item_preference - target_no_item_preference) * 2
|
||||
item_preference = target.wants_item?(target.item_id)
|
||||
no_item_preference = target.wants_item?(:NONE)
|
||||
score -= (no_item_preference - item_preference) * 3
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -349,9 +368,9 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("ThrowUserItemAtTarget",
|
||||
end
|
||||
# Prefer if the user doesn't want its held item/don't prefer if it wants to
|
||||
# keep its held item
|
||||
user_item_preference = user.wants_item?(user.item_id)
|
||||
user_no_item_preference = user.wants_item?(:NONE)
|
||||
score += (user_item_preference - user_no_item_preference) * 2
|
||||
item_preference = user.wants_item?(user.item_id)
|
||||
no_item_preference = user.wants_item?(:NONE)
|
||||
score += (no_item_preference - item_preference) * 2
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("FleeFromBattle",
|
||||
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",
|
||||
@@ -14,35 +14,50 @@ Battle::AI::Handlers::MoveEffectScore.add("FleeFromBattle",
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("SwitchOutUserStatusMove",
|
||||
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)
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserStatusMove",
|
||||
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?
|
||||
# Trainer-owned Pokémon switch out
|
||||
if ai.trainer.has_skill_flag?("ReserveLastPokemon") && battle.pbTeamAbleNonActiveCount(user.index) == 1
|
||||
next Battle::AI::MOVE_USELESS_SCORE # Don't switch in ace
|
||||
end
|
||||
# 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::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
|
||||
# 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 }
|
||||
score += 10 if !user.check_for_move { |m| m.damagingMove? }
|
||||
# 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
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserDamagingMove",
|
||||
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") &&
|
||||
battle.pbTeamAbleNonActiveCount(user.index) == 1
|
||||
# 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
|
||||
# Don't prefer the more stat raises the user has
|
||||
GameData::Stat.each_battle { |s| score -= user.stages[s.id] * 5 }
|
||||
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
|
||||
# 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
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
# TODO: Might need both MoveEffectScore and MoveEffectAgainstTargetScore.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("LowerTargetAtkSpAtk1SwitchOutUser",
|
||||
proc { |move, user, target, ai, battle|
|
||||
@@ -75,25 +103,14 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("LowerTargetAtkSpAtk1Swi
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("LowerTargetAtkSpAtk1SwitchOutUser",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
score = 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
|
||||
next ai.get_score_for_target_stat_drop(score, target, move.move.statDown, false)
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.copy("SwitchOutUserDamagingMove",
|
||||
"LowerTargetAtkSpAtk1SwitchOutUser")
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("SwitchOutUserPassOnEffects",
|
||||
proc { |move, user, ai, battle|
|
||||
@@ -107,56 +124,110 @@ Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserPassOnEffects",
|
||||
battle.pbTeamAbleNonActiveCount(user.index) == 1
|
||||
# Don't prefer if the user will pass on a negative effect
|
||||
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
|
||||
# TODO: Check effectiveness of moves.
|
||||
score += 15 if !user.check_for_move { |m| m.damagingMove? }
|
||||
# Prefer if the user will pass on good stat stages
|
||||
GameData::Stat.each_battle { |s| score += user.stages[s.id] * 5 }
|
||||
# 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
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("SwitchOutTargetStatusMove",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if (!battle.moldBreaker && target.has_active_ability?(:SUCTIONCUPS)) ||
|
||||
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
|
||||
next move.move.pbFailsAgainstTarget?(user.battler, target.battler, false)
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("SwitchOutTargetStatusMove",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
score += 15 if target.pbOwnSide.effects[PBEffects::Spikes] > 0
|
||||
score += 15 if target.pbOwnSide.effects[PBEffects::ToxicSpikes] > 0
|
||||
score += 15 if target.pbOwnSide.effects[PBEffects::StealthRock]
|
||||
# Ends the battle - generally don't prefer (don't want to end the battle too easily)
|
||||
next score - 10 if target.wild?
|
||||
# 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
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("SwitchOutTargetDamagingMove",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if (battle.moldBreaker || !target.has_active_ability?(:SUCTIONCUPS)) &&
|
||||
!target.effects[PBEffects::Ingrain]
|
||||
score += 15 if target.pbOwnSide.effects[PBEffects::Spikes] > 0
|
||||
score += 15 if target.pbOwnSide.effects[PBEffects::ToxicSpikes] > 0
|
||||
score += 15 if target.pbOwnSide.effects[PBEffects::StealthRock]
|
||||
next score if target.wild?
|
||||
# No score modification if the target can't be made to switch out
|
||||
next score if !battle.moldBreaker && target.has_active_ability?(:SUCTIONCUPS)
|
||||
next score if target.effects[PBEffects::Ingrain]
|
||||
# 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
|
||||
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
|
||||
}
|
||||
)
|
||||
@@ -242,12 +313,9 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TrapTargetInBattle",
|
||||
next score if add_effect == -999 # Additional effect will be negated
|
||||
score += add_effect
|
||||
# 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 ||
|
||||
target.effects[PBEffects::Attract] >= 0 ||
|
||||
target.effects[PBEffects::Confusion] > 0 ||
|
||||
eor_damage > 0
|
||||
score += 15
|
||||
end
|
||||
@@ -290,12 +358,9 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TrapTargetInBattleLowerT
|
||||
next (move.damagingMove?) ? score : Battle::AI::MOVE_USELESS_SCORE
|
||||
end
|
||||
# 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 ||
|
||||
target.effects[PBEffects::Attract] >= 0 ||
|
||||
target.effects[PBEffects::Confusion] > 0 ||
|
||||
eor_damage > 0
|
||||
score += 15
|
||||
end
|
||||
@@ -324,12 +389,9 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TrapUserAndTargetInBattl
|
||||
next (move.damagingMove?) ? score : Battle::AI::MOVE_USELESS_SCORE
|
||||
end
|
||||
# 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 ||
|
||||
target.effects[PBEffects::Attract] >= 0 ||
|
||||
target.effects[PBEffects::Confusion] > 0 ||
|
||||
eor_damage > 0
|
||||
score += 15
|
||||
end
|
||||
@@ -339,16 +401,22 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TrapUserAndTargetInBattl
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("TrapAllBattlersInBattleForOneTurn",
|
||||
proc { |move, user, ai, battle|
|
||||
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
|
||||
|
||||
@@ -661,7 +729,6 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetUsingDiffer
|
||||
if move_data.status?
|
||||
# Prefer encoring status moves
|
||||
if [:User, :BothSides].include?(move_data.target)
|
||||
# TODO: This target distinction was in the old code. Is it appropriate?
|
||||
score += 10
|
||||
else
|
||||
score += 8
|
||||
|
||||
@@ -242,8 +242,7 @@ Battle::ItemEffects::SpeedCalc.copy(:MACHOBRACE, :POWERANKLET, :POWERBAND,
|
||||
|
||||
Battle::ItemEffects::SpeedCalc.add(:QUICKPOWDER,
|
||||
proc { |item, battler, mult|
|
||||
next mult * 2 if battler.isSpecies?(:DITTO) &&
|
||||
!battler.effects[PBEffects::Transform]
|
||||
next mult * 2 if battler.isSpecies?(:DITTO) && !battler.effects[PBEffects::Transform]
|
||||
}
|
||||
)
|
||||
|
||||
@@ -263,7 +262,7 @@ Battle::ItemEffects::WeightCalc.add(:FLOATSTONE,
|
||||
|
||||
Battle::ItemEffects::HPHeal.add(:AGUAVBERRY,
|
||||
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)
|
||||
)
|
||||
}
|
||||
@@ -294,7 +293,7 @@ Battle::ItemEffects::HPHeal.add(:BERRYJUICE,
|
||||
|
||||
Battle::ItemEffects::HPHeal.add(:FIGYBERRY,
|
||||
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)
|
||||
)
|
||||
}
|
||||
@@ -308,7 +307,7 @@ Battle::ItemEffects::HPHeal.add(:GANLONBERRY,
|
||||
|
||||
Battle::ItemEffects::HPHeal.add(:IAPAPABERRY,
|
||||
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)
|
||||
)
|
||||
}
|
||||
@@ -338,7 +337,7 @@ Battle::ItemEffects::HPHeal.add(:LIECHIBERRY,
|
||||
|
||||
Battle::ItemEffects::HPHeal.add(:MAGOBERRY,
|
||||
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)
|
||||
)
|
||||
}
|
||||
@@ -436,7 +435,7 @@ Battle::ItemEffects::HPHeal.add(:STARFBERRY,
|
||||
|
||||
Battle::ItemEffects::HPHeal.add(:WIKIBERRY,
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user