mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2026-06-15 00:04:12 +00:00
Rewrote various AI switching checks
This commit is contained in:
@@ -92,6 +92,7 @@ module Battle::AI::Handlers
|
|||||||
GeneralMoveScore = HandlerHash.new
|
GeneralMoveScore = HandlerHash.new
|
||||||
GeneralMoveAgainstTargetScore = HandlerHash.new
|
GeneralMoveAgainstTargetScore = HandlerHash.new
|
||||||
ShouldSwitch = HandlerHash.new
|
ShouldSwitch = HandlerHash.new
|
||||||
|
ShouldNotSwitch = HandlerHash.new
|
||||||
|
|
||||||
def self.move_will_fail?(function_code, *args)
|
def self.move_will_fail?(function_code, *args)
|
||||||
return MoveFailureCheck.trigger(function_code, *args) || false
|
return MoveFailureCheck.trigger(function_code, *args) || false
|
||||||
@@ -140,4 +141,13 @@ module Battle::AI::Handlers
|
|||||||
end
|
end
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.should_not_switch?(*args)
|
||||||
|
ret = false
|
||||||
|
ShouldNotSwitch.each do |id, switch_proc|
|
||||||
|
ret ||= switch_proc.call(*args)
|
||||||
|
break if ret
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ class Battle::AI
|
|||||||
def pbChooseToSwitchOut(force_switch = false)
|
def pbChooseToSwitchOut(force_switch = false)
|
||||||
return false if @user.wild?
|
return false if @user.wild?
|
||||||
return false if !@battle.pbCanSwitchOut?(@user.index)
|
return false if !@battle.pbCanSwitchOut?(@user.index)
|
||||||
# Don't switch if there is a single foe and it is resting after Hyper Beam
|
# Don't switch if all foes are unable to do anything, e.g. resting after
|
||||||
# or will Truant (i.e. free turn)
|
# Hyper Beam, will Truant (i.e. free turn)
|
||||||
if @trainer.high_skill?
|
if @trainer.high_skill?
|
||||||
foe_can_act = false
|
foe_can_act = false
|
||||||
each_foe_battler(@user.side) do |b|
|
each_foe_battler(@user.side) do |b, i|
|
||||||
next if !b.can_attack?
|
next if !b.can_attack?
|
||||||
foe_can_act = true
|
foe_can_act = true
|
||||||
break
|
break
|
||||||
@@ -23,7 +23,12 @@ class Battle::AI
|
|||||||
if force_switch
|
if force_switch
|
||||||
PBDebug.log_ai("#{@user.name} is being forced to switch out")
|
PBDebug.log_ai("#{@user.name} is being forced to switch out")
|
||||||
else
|
else
|
||||||
should_switch = Battle::AI::Handlers.should_switch?(@user, self, @battle)
|
return false if !@trainer.has_skill_flag?("ConsiderSwitching")
|
||||||
|
reserves = get_non_active_party_pokemon(@user.index)
|
||||||
|
should_switch = Battle::AI::Handlers.should_switch?(@user, reserves, self, @battle)
|
||||||
|
if should_switch && @trainer.medium_skill?
|
||||||
|
should_switch = false if Battle::AI::Handlers.should_not_switch?(@user, reserves, self, @battle)
|
||||||
|
end
|
||||||
return false if !should_switch
|
return false if !should_switch
|
||||||
end
|
end
|
||||||
# Want to switch; find the best replacement Pokémon
|
# Want to switch; find the best replacement Pokémon
|
||||||
@@ -50,6 +55,14 @@ class Battle::AI
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_non_active_party_pokemon(idxBattler)
|
||||||
|
ret = []
|
||||||
|
@battle.pbParty(idxBattler).each_with_index do |pkmn, i|
|
||||||
|
ret.push(pkmn) if @battle.pbCanSwitchIn?(idxBattler, i)
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
#=============================================================================
|
#=============================================================================
|
||||||
|
|
||||||
def choose_best_replacement_pokemon(idxBattler, mandatory = false)
|
def choose_best_replacement_pokemon(idxBattler, mandatory = false)
|
||||||
@@ -59,7 +72,7 @@ class Battle::AI
|
|||||||
reserves = []
|
reserves = []
|
||||||
party.each_with_index do |_pkmn, i|
|
party.each_with_index do |_pkmn, i|
|
||||||
next if !@battle.pbCanSwitchIn?(idxBattler, i)
|
next if !@battle.pbCanSwitchIn?(idxBattler, i)
|
||||||
if !mandatory
|
if !mandatory # Not mandatory means choosing an action for the round
|
||||||
ally_will_switch_with_i = false
|
ally_will_switch_with_i = false
|
||||||
@battle.allSameSideBattlers(idxBattler).each do |b|
|
@battle.allSameSideBattlers(idxBattler).each do |b|
|
||||||
next if @battle.choices[b.index][0] != :SwitchOut || @battle.choices[b.index][1] != i
|
next if @battle.choices[b.index][0] != :SwitchOut || @battle.choices[b.index][1] != i
|
||||||
@@ -82,36 +95,18 @@ class Battle::AI
|
|||||||
end
|
end
|
||||||
reserves.sort! { |a, b| b[1] <=> a[1] } # Sort from highest to lowest rated
|
reserves.sort! { |a, b| b[1] <=> a[1] } # Sort from highest to lowest rated
|
||||||
# Don't bother choosing to switch if all replacements are poorly rated
|
# Don't bother choosing to switch if all replacements are poorly rated
|
||||||
if !mandatory
|
if @trainer.high_skill? && !mandatory
|
||||||
# TODO: Should the current battler be rated as well, to provide a
|
# TODO: Should the current battler be rated as well, to provide a
|
||||||
# threshold instead of using a threshold of 100?
|
# 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 # Best replacement rated at <100, don't switch
|
||||||
end
|
end
|
||||||
# Return the best rated replacement Pokémon
|
# Return the party index of the best rated replacement Pokémon
|
||||||
return reserves[0][0]
|
return reserves[0][0]
|
||||||
end
|
end
|
||||||
|
|
||||||
def rate_replacement_pokemon(idxBattler, pkmn, score)
|
def rate_replacement_pokemon(idxBattler, pkmn, score)
|
||||||
battler_side = @battle.sides[idxBattler & 1]
|
|
||||||
pkmn_types = pkmn.types
|
pkmn_types = pkmn.types
|
||||||
entry_hazard_damage = 0
|
entry_hazard_damage = calculate_entry_hazard_damage(pkmn, idxBattler & 1)
|
||||||
# Stealth Rock
|
|
||||||
if battler_side.effects[PBEffects::StealthRock] && !pkmn.hasAbility?(:MAGICGUARD) &&
|
|
||||||
GameData::Type.exists?(:ROCK) && !pkmn.hasItem?(:HEAVYDUTYBOOTS)
|
|
||||||
eff = Effectiveness.calculate(:ROCK, *pkmn_types)
|
|
||||||
if !Effectiveness.ineffective?(eff)
|
|
||||||
entry_hazard_damage += pkmn.totalhp * eff / 8
|
|
||||||
end
|
|
||||||
end
|
|
||||||
# Spikes
|
|
||||||
if battler_side.effects[PBEffects::Spikes] > 0 && !pkmn.hasAbility?(:MAGICGUARD) &&
|
|
||||||
!pkmn.hasItem?(:HEAVYDUTYBOOTS)
|
|
||||||
if @battle.field.effects[PBEffects::Gravity] > 0 || pkmn.hasItem?(:IRONBALL) ||
|
|
||||||
!(pkmn.hasType?(:FLYING) || pkmn.hasItem?(:LEVITATE) || pkmn.hasItem?(:AIRBALLOON))
|
|
||||||
spikes_div = [8, 6, 4][battler_side.effects[PBEffects::Spikes] - 1]
|
|
||||||
entry_hazard_damage += pkmn.totalhp / spikes_div
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if entry_hazard_damage >= pkmn.hp
|
if entry_hazard_damage >= pkmn.hp
|
||||||
score -= 50 # pkmn will just faint
|
score -= 50 # pkmn will just faint
|
||||||
elsif entry_hazard_damage > 0
|
elsif entry_hazard_damage > 0
|
||||||
@@ -128,11 +123,13 @@ class Battle::AI
|
|||||||
eff = Effectiveness.calculate(move_type, *pkmn_types)
|
eff = Effectiveness.calculate(move_type, *pkmn_types)
|
||||||
score -= move_data.power * eff / 5
|
score -= move_data.power * eff / 5
|
||||||
end
|
end
|
||||||
# Add power * effectiveness / 10 of all moves to score
|
# Add power * effectiveness / 10 of all pkmn's moves to score
|
||||||
pkmn.moves.each do |m|
|
pkmn.moves.each do |m|
|
||||||
next if m.power == 0 || (m.pp == 0 && m.total_pp > 0)
|
next if m.power == 0 || (m.pp == 0 && m.total_pp > 0)
|
||||||
@battle.battlers[idxBattler].allOpposing.each do |b|
|
@battle.battlers[idxBattler].allOpposing.each do |b|
|
||||||
bTypes = b.pbTypes(true)
|
bTypes = b.pbTypes(true)
|
||||||
|
# TODO: Consider Wonder Guard, Volt Absorb et al. Consider pkmn's
|
||||||
|
# ability if it changes the user's types or powers up their moves?
|
||||||
score += m.power * Effectiveness.calculate(m.type, *bTypes) / 10
|
score += m.power * Effectiveness.calculate(m.type, *bTypes) / 10
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -140,54 +137,34 @@ class Battle::AI
|
|||||||
score += 10 if @user.effects[PBEffects::PerishSong] == 1
|
score += 10 if @user.effects[PBEffects::PerishSong] == 1
|
||||||
return score
|
return score
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def calculate_entry_hazard_damage(pkmn, side)
|
||||||
|
return 0 if pkmn.hasAbility?(:MAGICGUARD) || pkmn.hasItem?(:HEAVYDUTYBOOTS)
|
||||||
|
ret = 0
|
||||||
|
# Stealth Rock
|
||||||
|
if @battle.sides[side].effects[PBEffects::StealthRock] && GameData::Type.exists?(:ROCK)
|
||||||
|
eff = Effectiveness.calculate(:ROCK, *pkmn_types)
|
||||||
|
ret += pkmn.totalhp * eff / 8 if !Effectiveness.ineffective?(eff)
|
||||||
|
end
|
||||||
|
# Spikes
|
||||||
|
if @battle.sides[side].effects[PBEffects::Spikes] > 0
|
||||||
|
if @battle.field.effects[PBEffects::Gravity] > 0 || pkmn.hasItem?(:IRONBALL) ||
|
||||||
|
!(pkmn.hasType?(:FLYING) || pkmn.hasItem?(:LEVITATE) || pkmn.hasItem?(:AIRBALLOON))
|
||||||
|
spikes_div = [8, 6, 4][@battle.sides[side].effects[PBEffects::Spikes] - 1]
|
||||||
|
ret += pkmn.totalhp / spikes_div
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# If Pokémon is within 5 levels of the foe, and foe's last move was
|
# Pokémon is about to faint because of Perish Song.
|
||||||
# super-effective and powerful.
|
# TODO: Also switch to remove other negative effects like Disable, Yawn.
|
||||||
# TODO: Review switch deciding.
|
|
||||||
#===============================================================================
|
|
||||||
Battle::AI::Handlers::ShouldSwitch.add(:high_damage_from_foe,
|
|
||||||
proc { |battler, ai, battle|
|
|
||||||
if battler.turnCount > 0 && battler.hp < battler.totalhp / 2 && ai.trainer.high_skill?
|
|
||||||
target_battler = battler.battler.pbDirectOpposing(true)
|
|
||||||
foe = ai.battlers[target_battler.index]
|
|
||||||
if !foe.fainted? && foe.battler.lastMoveUsed && (foe.level - battler.level).abs <= 5
|
|
||||||
move_data = GameData::Move.get(foe.battler.lastMoveUsed)
|
|
||||||
eff = battler.effectiveness_of_type_against_battler(move_data.type, foe)
|
|
||||||
if Effectiveness.super_effective?(eff) && move_data.power > 70
|
|
||||||
switch_chance = (move_data.power > 95) ? 40 : 20
|
|
||||||
if ai.pbAIRandom(100) < switch_chance
|
|
||||||
PBDebug.log_ai("#{battler.name} wants to switch because a foe can do a lot of damage to it")
|
|
||||||
next true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
next false
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
#===============================================================================
|
|
||||||
# Pokémon can't do anything (must have been in battle for at least 5 rounds).
|
|
||||||
# TODO: Review switch deciding.
|
|
||||||
#===============================================================================
|
|
||||||
Battle::AI::Handlers::ShouldSwitch.add(:battler_is_useless,
|
|
||||||
proc { |battler, ai, battle|
|
|
||||||
if !battle.pbCanChooseAnyMove?(battler.index) && battler.turnCount >= 5
|
|
||||||
PBDebug.log_ai("#{battler.name} wants to switch because it can't do anything")
|
|
||||||
next true
|
|
||||||
end
|
|
||||||
next false
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
#===============================================================================
|
|
||||||
# Pokémon is Perish Songed and has Baton Pass.
|
|
||||||
# TODO: Review switch deciding.
|
# TODO: Review switch deciding.
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
Battle::AI::Handlers::ShouldSwitch.add(:perish_song,
|
Battle::AI::Handlers::ShouldSwitch.add(:perish_song,
|
||||||
proc { |battler, ai, battle|
|
proc { |battler, reserves, ai, battle|
|
||||||
if battler.effects[PBEffects::PerishSong] == 1
|
if battler.effects[PBEffects::PerishSong] == 1
|
||||||
PBDebug.log_ai("#{battler.name} wants to switch because it is about to faint from Perish Song")
|
PBDebug.log_ai("#{battler.name} wants to switch because it is about to faint from Perish Song")
|
||||||
next true
|
next true
|
||||||
@@ -197,19 +174,38 @@ Battle::AI::Handlers::ShouldSwitch.add(:perish_song,
|
|||||||
)
|
)
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# Pokémon will faint because of bad poisoning at the end of this round, but
|
# Pokémon will take a significant amount of damage at the end of this round.
|
||||||
# would survive at least one more round if it were regular poisoning instead.
|
# Also, Pokémon has an effect that causes it damage at the end of this round,
|
||||||
|
# which it can remove by switching.
|
||||||
# TODO: Review switch deciding.
|
# TODO: Review switch deciding.
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
Battle::AI::Handlers::ShouldSwitch.add(:reduce_toxic_to_regular_poisoning,
|
Battle::AI::Handlers::ShouldSwitch.add(:significant_eor_damage,
|
||||||
proc { |battler, ai, battle|
|
proc { |battler, reserves, ai, battle|
|
||||||
if battler.status == :POISON && battler.statusCount > 0 && ai.trainer.high_skill?
|
eor_damage = battler.rough_end_of_round_damage
|
||||||
next false if battler.has_active_ability?(:POISONHEAL)
|
# Switch if battler will take significant EOR damage
|
||||||
next false if !battler.battler.takesIndirectDamage?
|
if eor_damage >= battler.hp / 2 || eor_damage >= battler.totalhp / 4
|
||||||
|
PBDebug.log_ai("#{battler.name} wants to switch because it will take a lot of EOR damage")
|
||||||
|
next true
|
||||||
|
end
|
||||||
|
# Switch to remove certain effects that cause the battler EOR damage
|
||||||
|
if ai.trainer.high_skill? && eor_damage > 0
|
||||||
|
if battler.effects[PBEffects::LeechSeed] >= 0 && ai.pbAIRandom(100) < 50
|
||||||
|
PBDebug.log_ai("#{battler.name} wants to switch to get rid of its Leech Seed")
|
||||||
|
next true
|
||||||
|
end
|
||||||
|
if battler.effects[PBEffects::Nightmare]
|
||||||
|
PBDebug.log_ai("#{battler.name} wants to switch to get rid of its Nightmare")
|
||||||
|
next true
|
||||||
|
end
|
||||||
|
if battler.effects[PBEffects::Curse]
|
||||||
|
PBDebug.log_ai("#{battler.name} wants to switch to get rid of its Curse")
|
||||||
|
next true
|
||||||
|
end
|
||||||
|
if battler.status == :POISON && battler.statusCount > 0 && !battler.has_active_ability?(:POISONHEAL)
|
||||||
poison_damage = battler.totalhp / 8
|
poison_damage = battler.totalhp / 8
|
||||||
next_toxic_damage = battler.totalhp * (battler.effects[PBEffects::Toxic] + 1) / 16
|
next_toxic_damage = battler.totalhp * (battler.effects[PBEffects::Toxic] + 1) / 16
|
||||||
if battler.hp <= next_toxic_damage && battler.hp > poison_damage
|
if (battler.hp <= next_toxic_damage && battler.hp > poison_damage) ||
|
||||||
if ai.pbAIRandom(100) < 80
|
next_toxic_damage > poison_damage * 2
|
||||||
PBDebug.log_ai("#{battler.name} wants to switch to reduce toxic to regular poisoning")
|
PBDebug.log_ai("#{battler.name} wants to switch to reduce toxic to regular poisoning")
|
||||||
next true
|
next true
|
||||||
end
|
end
|
||||||
@@ -219,15 +215,194 @@ Battle::AI::Handlers::ShouldSwitch.add(:reduce_toxic_to_regular_poisoning,
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
#===============================================================================
|
||||||
|
# Pokémon can cure its status problem or heal some HP with its ability by
|
||||||
|
# switching out. Covers all abilities with an OnSwitchOut AbilityEffects handler.
|
||||||
|
# TODO: Review switch deciding. Add randomness?
|
||||||
|
#===============================================================================
|
||||||
|
Battle::AI::Handlers::ShouldSwitch.add(:cure_status_problem_by_switching_out,
|
||||||
|
proc { |battler, reserves, ai, battle|
|
||||||
|
next false if !battler.ability_active?
|
||||||
|
# Don't try to cure a status problem/heal a bit of HP if entry hazards will
|
||||||
|
# KO the battler if it switches back in
|
||||||
|
entry_hazard_damage = ai.calculate_entry_hazard_damage(battler.pkmn, battler.side)
|
||||||
|
next false if entry_hazard_damage >= battler.hp
|
||||||
|
# Check specific abilities
|
||||||
|
single_status_cure = {
|
||||||
|
:IMMUNITY => :POISON,
|
||||||
|
:INSOMNIA => :SLEEP,
|
||||||
|
:LIMBER => :PARALYSIS,
|
||||||
|
:MAGMAARMOR => :FROZEN,
|
||||||
|
:VITALSPIRIT => :SLEEP,
|
||||||
|
:WATERBUBBLE => :BURN,
|
||||||
|
:WATERVEIL => :BURN
|
||||||
|
}[battler.ability_id]
|
||||||
|
if battler.ability_id == :NATURALCURE || (single_status_cure && single_status_cure == battler.status)
|
||||||
|
# Cures status problem
|
||||||
|
next false if battler.wants_status_problem?(battler.status)
|
||||||
|
next false if battler.status == :SLEEP && battler.statusCount == 1 # Will wake up this round anyway
|
||||||
|
next false if entry_hazard_damage >= battler.totalhp / 4
|
||||||
|
# Don't bother curing a poisoning if Toxic Spikes will just re-poison the
|
||||||
|
# battler when it switches back in
|
||||||
|
if battler.status == :POISON && reserves.none? { |pkmn| pkmn.hasType?(:POISON) }
|
||||||
|
next false if battle.field.effects[PBEffectS::ToxicSpikes] == 2
|
||||||
|
next false if battle.field.effects[PBEffectS::ToxicSpikes] == 1 && battler.statusCount == 0
|
||||||
|
end
|
||||||
|
# Not worth curing status problems that still allow actions if at high HP
|
||||||
|
next false if battler.hp >= battler.totalhp / 2 && ![:SLEEP, :FROZEN].include?(battler.status)
|
||||||
|
PBDebug.log_ai("#{battler.name} wants to switch to cure its status problem with #{battler.ability.name}")
|
||||||
|
next true
|
||||||
|
elsif battler.ability_id == :REGENERATOR
|
||||||
|
# Heals 33% HP
|
||||||
|
next false if entry_hazard_damage >= battler.totalhp / 3
|
||||||
|
# Not worth healing HP if already at high HP
|
||||||
|
next false if battler.hp >= battler.totalhp / 2
|
||||||
|
# TODO: Don't bother if user can do decent damage.
|
||||||
|
if ai.pbAIRandom(100) < 50
|
||||||
|
PBDebug.log_ai("#{battler.name} wants to switch to heal with #{battler.ability.name}")
|
||||||
|
next true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
next false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
#===============================================================================
|
||||||
|
# Pokémon can't do anything to a Wonder Guard foe.
|
||||||
|
# TODO: Check other abilities that provide immunities?
|
||||||
|
# TODO: Review switch deciding.
|
||||||
|
#===============================================================================
|
||||||
|
Battle::AI::Handlers::ShouldSwitch.add(:foe_has_wonder_guard,
|
||||||
|
proc { |battler, reserves, ai, battle|
|
||||||
|
next false if battler.battler.hasMoldBreaker?
|
||||||
|
non_wonder_guard_foe_exists = false
|
||||||
|
has_super_effective_move = false
|
||||||
|
foe_types = b.pbTypes(true)
|
||||||
|
next false if foe_types.length == 0
|
||||||
|
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
|
||||||
|
break
|
||||||
|
end
|
||||||
|
# Check for super-effective damaging moves
|
||||||
|
battler.battler.eachMove do |move|
|
||||||
|
next if move.statusMove?
|
||||||
|
if ["IgnoreTargetAbility",
|
||||||
|
"CategoryDependsOnHigherDamageIgnoreTargetAbility"].include?(move.function)
|
||||||
|
has_super_effective_move = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
eff = Effectiveness.calculate(move.pbCalcType(battler.battler), *foe_types)
|
||||||
|
if Effectiveness.super_effective?(eff)
|
||||||
|
has_super_effective_move = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
# TODO: Check if battler has other useful status moves. CFRU considers
|
||||||
|
# these (and makes sure they're usable; also ensure they're not
|
||||||
|
# stopped by a substitute):
|
||||||
|
# - Inflict sleep/poison/burn/paralysis (not freeze)
|
||||||
|
# - Inflict confusion (inc. Swagger/Flatter)
|
||||||
|
# - Start damaging weather (sandstorm/hail)
|
||||||
|
# - Trick (to give foe an item with EOR damage/status infliction)
|
||||||
|
# - Other EOR damage moves (Leech Seed, Nightmare, Curse)
|
||||||
|
# - Perish Song
|
||||||
|
# - Add third type to target (Trick-or-Treat, Forest's Curse)
|
||||||
|
# - Worry Seed, Gastro Acid, Entrainment, Simple Beam, Core Enforcer
|
||||||
|
# - Roar
|
||||||
|
# - Baton Pass, Teleport
|
||||||
|
# - Memento (why?)
|
||||||
|
# - Entry hazards (not sure why; just to stack them up?)
|
||||||
|
# - Wish (just to set it up?)
|
||||||
|
# - Tailwind (just to set it up?)
|
||||||
|
# - Lucky Chant (just to set it up?)
|
||||||
|
break if has_super_effective_move
|
||||||
|
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|
|
||||||
|
pkmn.moves.each do |m|
|
||||||
|
next if m.status_move?
|
||||||
|
if ["IgnoreTargetAbility",
|
||||||
|
"CategoryDependsOnHigherDamageIgnoreTargetAbility"].include?(move.function_code)
|
||||||
|
reserve_has_super_effective_move = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
eff = Effectiveness.calculate(m.type, *foe_types)
|
||||||
|
if Effectiveness.super_effective?(eff)
|
||||||
|
reserve_has_super_effective_move = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
break if reserve_has_super_effective_move
|
||||||
|
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
|
||||||
|
end
|
||||||
|
next false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
#===============================================================================
|
||||||
|
# If 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|
|
||||||
|
next false if battler.hp >= battler.totalhp / 2
|
||||||
|
next false if !ai.trainer.high_skill?
|
||||||
|
big_threat = false
|
||||||
|
ai.each_foe_battler(battler.side) do |b, i|
|
||||||
|
next if (foe.level - battler.level).abs > 5
|
||||||
|
next if !b.battler.lastMoveUsed
|
||||||
|
move_data = GameData::Move.get(b.battler.lastMoveUsed)
|
||||||
|
next if move_data.status?
|
||||||
|
eff = battler.effectiveness_of_type_against_battler(move_data.type, b)
|
||||||
|
next if !Effectiveness.super_effective?(eff) || move_data.power < 60
|
||||||
|
switch_chance = (move_data.power > 90) ? 50 : 25
|
||||||
|
if ai.pbAIRandom(100) < switch_chance
|
||||||
|
big_threat = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if big_threat
|
||||||
|
PBDebug.log_ai("#{battler.name} wants to switch because a foe can do a lot of damage to it")
|
||||||
|
next true
|
||||||
|
end
|
||||||
|
next false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
#===============================================================================
|
||||||
|
# Pokémon can't do anything (must have been in battle for at least 3 rounds).
|
||||||
|
# TODO: Review switch deciding.
|
||||||
|
#===============================================================================
|
||||||
|
Battle::AI::Handlers::ShouldSwitch.add(:battler_is_useless,
|
||||||
|
proc { |battler, reserves, ai, battle|
|
||||||
|
if !battle.pbCanChooseAnyMove?(battler.index) && battler.turnCount >= 3
|
||||||
|
PBDebug.log_ai("#{battler.name} wants to switch because it can't do anything")
|
||||||
|
next true
|
||||||
|
end
|
||||||
|
next false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# Pokémon is Encored into an unfavourable move.
|
# Pokémon is Encored into an unfavourable move.
|
||||||
# TODO: Review switch deciding.
|
# TODO: Review switch deciding.
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
Battle::AI::Handlers::ShouldSwitch.add(:bad_encored_move,
|
Battle::AI::Handlers::ShouldSwitch.add(:bad_encored_move,
|
||||||
proc { |battler, ai, battle|
|
proc { |battler, reserves, ai, battle|
|
||||||
if battler.effects[PBEffects::Encore] > 0 && ai.trainer.medium_skill?
|
next false if battler.effects[PBEffects::Encore] == 0
|
||||||
idxEncoredMove = battler.battler.pbEncoredMoveIndex
|
idxEncoredMove = battler.battler.pbEncoredMoveIndex
|
||||||
if idxEncoredMove >= 0
|
next false if idxEncoredMove < 0
|
||||||
ai.set_up_move_check(battler.moves[idxEncoredMove])
|
ai.set_up_move_check(battler.moves[idxEncoredMove])
|
||||||
scoreSum = 0
|
scoreSum = 0
|
||||||
scoreCount = 0
|
scoreCount = 0
|
||||||
@@ -243,8 +418,6 @@ Battle::AI::Handlers::ShouldSwitch.add(:bad_encored_move,
|
|||||||
next false
|
next false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
next false
|
next false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -254,7 +427,7 @@ Battle::AI::Handlers::ShouldSwitch.add(:bad_encored_move,
|
|||||||
# TODO: Review switch deciding.
|
# TODO: Review switch deciding.
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
Battle::AI::Handlers::ShouldSwitch.add(:sudden_death,
|
Battle::AI::Handlers::ShouldSwitch.add(:sudden_death,
|
||||||
proc { |battler, ai, battle|
|
proc { |battler, reserves, ai, battle|
|
||||||
if battle.rules["suddendeath"] && battler.turnCount > 0
|
if battle.rules["suddendeath"] && battler.turnCount > 0
|
||||||
if battler.hp <= battler.totalhp / 4 && ai.pbAIRandom(100) < 30
|
if battler.hp <= battler.totalhp / 4 && ai.pbAIRandom(100) < 30
|
||||||
PBDebug.log_ai("#{battler.name} wants to switch because of the sudden death rule")
|
PBDebug.log_ai("#{battler.name} wants to switch because of the sudden death rule")
|
||||||
@@ -267,3 +440,15 @@ Battle::AI::Handlers::ShouldSwitch.add(:sudden_death,
|
|||||||
next false
|
next false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
#===============================================================================
|
||||||
|
# Don't bother switching if the battler will just faint from entry hazard damage
|
||||||
|
# upon switching back in.
|
||||||
|
# TODO: Allow it if the replacement will be able to get rid of entry hazards?
|
||||||
|
#===============================================================================
|
||||||
|
# Battle::AI::Handlers::ShouldNotSwitch.add(:lethal_entry_hazards,
|
||||||
|
# proc { |battler, reserves, ai, battle|
|
||||||
|
# entry_hazard_damage = ai.calculate_entry_hazard_damage(battler.pkmn, battler.side)
|
||||||
|
# next entry_hazard_damage >= battler.hp
|
||||||
|
# }
|
||||||
|
# )
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
# PredictMoveFailure
|
# PredictMoveFailure
|
||||||
# ScoreMoves
|
# ScoreMoves
|
||||||
# PreferMultiTargetMoves
|
# PreferMultiTargetMoves
|
||||||
|
# ConsiderSwitching (can choose to switch out Pokémon)
|
||||||
# ReserveLastPokemon (don't switch it in if possible)
|
# ReserveLastPokemon (don't switch it in if possible)
|
||||||
# UsePokemonInOrder (uses earliest-listed Pokémon possible)
|
# UsePokemonInOrder (uses earliest-listed Pokémon possible)
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
@@ -57,6 +58,7 @@ class Battle::AI::AITrainer
|
|||||||
@skill_flags.push("ScoreMoves")
|
@skill_flags.push("ScoreMoves")
|
||||||
@skill_flags.push("PreferMultiTargetMoves")
|
@skill_flags.push("PreferMultiTargetMoves")
|
||||||
end
|
end
|
||||||
|
@skill_flags.push("ConsiderSwitching") if medium_skill?
|
||||||
if !medium_skill?
|
if !medium_skill?
|
||||||
@skill_flags.push("UsePokemonInOrder")
|
@skill_flags.push("UsePokemonInOrder")
|
||||||
elsif best_skill?
|
elsif best_skill?
|
||||||
|
|||||||
+3
-3
@@ -697,7 +697,7 @@ Battle::AI::Handlers::MoveEffectScore.add("SetUserTypesBasedOnEnvironment",
|
|||||||
# Check if any user's moves will get STAB because of the type change
|
# Check if any user's moves will get STAB because of the type change
|
||||||
score += 8 if user.has_damaging_move_of_type?(new_type)
|
score += 8 if user.has_damaging_move_of_type?(new_type)
|
||||||
# Check if any user's moves will lose STAB because of the type change
|
# Check if any user's moves will lose STAB because of the type change
|
||||||
user.battler.pbTypes(true).each do |type|
|
user.pbTypes(true).each do |type|
|
||||||
next if type == new_type
|
next if type == new_type
|
||||||
score -= 8 if user.has_damaging_move_of_type?(type)
|
score -= 8 if user.has_damaging_move_of_type?(type)
|
||||||
end
|
end
|
||||||
@@ -750,8 +750,8 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("SetUserTypesToResistLast
|
|||||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("SetUserTypesToTargetTypes",
|
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("SetUserTypesToTargetTypes",
|
||||||
proc { |move, user, target, ai, battle|
|
proc { |move, user, target, ai, battle|
|
||||||
next true if !user.battler.canChangeType?
|
next true if !user.battler.canChangeType?
|
||||||
next true if target.battler.pbTypes(true).empty?
|
next true if target.pbTypes(true).empty?
|
||||||
next true if user.battler.pbTypes == target.battler.pbTypes &&
|
next true if user.pbTypes == target.pbTypes &&
|
||||||
user.effects[PBEffects::ExtraType] == target.effects[PBEffects::ExtraType]
|
user.effects[PBEffects::ExtraType] == target.effects[PBEffects::ExtraType]
|
||||||
next false
|
next false
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1275,7 +1275,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RecoilHalfOfDamageDealt"
|
|||||||
Battle::AI::Handlers::MoveBasePower.add("EffectivenessIncludesFlyingType",
|
Battle::AI::Handlers::MoveBasePower.add("EffectivenessIncludesFlyingType",
|
||||||
proc { |power, move, user, target, ai, battle|
|
proc { |power, move, user, target, ai, battle|
|
||||||
if GameData::Type.exists?(:FLYING)
|
if GameData::Type.exists?(:FLYING)
|
||||||
targetTypes = target.battler.pbTypes(true)
|
targetTypes = target.pbTypes(true)
|
||||||
mult = Effectiveness.calculate(:FLYING, *targetTypes)
|
mult = Effectiveness.calculate(:FLYING, *targetTypes)
|
||||||
power = (power * mult).round
|
power = (power * mult).round
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -51,6 +51,9 @@ class Pokemon
|
|||||||
def power; return GameData::Move.get(@id).power; end
|
def power; return GameData::Move.get(@id).power; end
|
||||||
def type; return GameData::Move.get(@id).type; end
|
def type; return GameData::Move.get(@id).type; end
|
||||||
def category; return GameData::Move.get(@id).category; end
|
def category; return GameData::Move.get(@id).category; end
|
||||||
|
def physical_move?; return GameData::Move.get(@id).physical?; end
|
||||||
|
def special_move?; return GameData::Move.get(@id).special?; end
|
||||||
|
def status_move?; return GameData::Move.get(@id).status?; end
|
||||||
def accuracy; return GameData::Move.get(@id).accuracy; end
|
def accuracy; return GameData::Move.get(@id).accuracy; end
|
||||||
def effect_chance; return GameData::Move.get(@id).effect_chance; end
|
def effect_chance; return GameData::Move.get(@id).effect_chance; end
|
||||||
def target; return GameData::Move.get(@id).target; end
|
def target; return GameData::Move.get(@id).target; end
|
||||||
|
|||||||
Reference in New Issue
Block a user