From 0bb0fb4a26ea5c1402af510035c746502c582186 Mon Sep 17 00:00:00 2001 From: Maruno17 Date: Sat, 25 Mar 2023 23:26:34 +0000 Subject: [PATCH] Rewrote various AI switching checks --- .../011_Battle/005_AI/001_Battle_AI.rb | 10 + .../011_Battle/005_AI/003_AI_Switch.rb | 385 +++++++++++++----- .../011_Battle/005_AI/101_AITrainer.rb | 2 + .../053_AI_MoveHandlers_BattlerOther.rb | 6 +- .../054_AI_MoveHandlers_MoveAttributes.rb | 2 +- Data/Scripts/014_Pokemon/004_Pokemon_Move.rb | 27 +- 6 files changed, 316 insertions(+), 116 deletions(-) diff --git a/Data/Scripts/011_Battle/005_AI/001_Battle_AI.rb b/Data/Scripts/011_Battle/005_AI/001_Battle_AI.rb index 46e97e64d..01fbfe7d7 100644 --- a/Data/Scripts/011_Battle/005_AI/001_Battle_AI.rb +++ b/Data/Scripts/011_Battle/005_AI/001_Battle_AI.rb @@ -92,6 +92,7 @@ module Battle::AI::Handlers GeneralMoveScore = HandlerHash.new GeneralMoveAgainstTargetScore = HandlerHash.new ShouldSwitch = HandlerHash.new + ShouldNotSwitch = HandlerHash.new def self.move_will_fail?(function_code, *args) return MoveFailureCheck.trigger(function_code, *args) || false @@ -140,4 +141,13 @@ module Battle::AI::Handlers end return ret 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 diff --git a/Data/Scripts/011_Battle/005_AI/003_AI_Switch.rb b/Data/Scripts/011_Battle/005_AI/003_AI_Switch.rb index 6f57494a2..8f5ccc161 100644 --- a/Data/Scripts/011_Battle/005_AI/003_AI_Switch.rb +++ b/Data/Scripts/011_Battle/005_AI/003_AI_Switch.rb @@ -8,11 +8,11 @@ class Battle::AI def pbChooseToSwitchOut(force_switch = false) return false if @user.wild? return false if !@battle.pbCanSwitchOut?(@user.index) - # Don't switch if there is a single foe and it is resting after Hyper Beam - # or will Truant (i.e. free turn) + # Don't switch if all foes are unable to do anything, e.g. resting after + # Hyper Beam, will Truant (i.e. free turn) if @trainer.high_skill? foe_can_act = false - each_foe_battler(@user.side) do |b| + each_foe_battler(@user.side) do |b, i| next if !b.can_attack? foe_can_act = true break @@ -23,7 +23,12 @@ class Battle::AI if force_switch PBDebug.log_ai("#{@user.name} is being forced to switch out") 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 end # Want to switch; find the best replacement Pokémon @@ -50,6 +55,14 @@ class Battle::AI return false 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) @@ -59,7 +72,7 @@ class Battle::AI reserves = [] party.each_with_index do |_pkmn, 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 @battle.allSameSideBattlers(idxBattler).each do |b| next if @battle.choices[b.index][0] != :SwitchOut || @battle.choices[b.index][1] != i @@ -82,36 +95,18 @@ class Battle::AI end 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 !mandatory + 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 end - # Return the best rated replacement Pokémon + # Return the party index of the best rated replacement Pokémon return reserves[0][0] end def rate_replacement_pokemon(idxBattler, pkmn, score) - battler_side = @battle.sides[idxBattler & 1] pkmn_types = pkmn.types - entry_hazard_damage = 0 - # 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 + entry_hazard_damage = calculate_entry_hazard_damage(pkmn, idxBattler & 1) if entry_hazard_damage >= pkmn.hp score -= 50 # pkmn will just faint elsif entry_hazard_damage > 0 @@ -128,11 +123,13 @@ class Battle::AI eff = Effectiveness.calculate(move_type, *pkmn_types) score -= move_data.power * eff / 5 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| next if m.power == 0 || (m.pp == 0 && m.total_pp > 0) @battle.battlers[idxBattler].allOpposing.each do |b| 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 @@ -140,54 +137,34 @@ class Battle::AI score += 10 if @user.effects[PBEffects::PerishSong] == 1 return score 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 #=============================================================================== -# 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, 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. +# Pokémon is about to faint because of Perish Song. +# TODO: Also switch to remove other negative effects like Disable, Yawn. # TODO: Review switch deciding. #=============================================================================== Battle::AI::Handlers::ShouldSwitch.add(:perish_song, - proc { |battler, ai, battle| + proc { |battler, reserves, ai, battle| if battler.effects[PBEffects::PerishSong] == 1 PBDebug.log_ai("#{battler.name} wants to switch because it is about to faint from Perish Song") 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 -# would survive at least one more round if it were regular poisoning instead. +# Pokémon will take a significant amount of damage at the end of this round. +# 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. #=============================================================================== -Battle::AI::Handlers::ShouldSwitch.add(:reduce_toxic_to_regular_poisoning, - proc { |battler, ai, battle| - if battler.status == :POISON && battler.statusCount > 0 && ai.trainer.high_skill? - next false if battler.has_active_ability?(:POISONHEAL) - next false if !battler.battler.takesIndirectDamage? - poison_damage = battler.totalhp / 8 - next_toxic_damage = battler.totalhp * (battler.effects[PBEffects::Toxic] + 1) / 16 - if battler.hp <= next_toxic_damage && battler.hp > poison_damage - if ai.pbAIRandom(100) < 80 +Battle::AI::Handlers::ShouldSwitch.add(:significant_eor_damage, + proc { |battler, reserves, ai, battle| + eor_damage = battler.rough_end_of_round_damage + # Switch if battler will take significant EOR damage + 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 + next_toxic_damage = battler.totalhp * (battler.effects[PBEffects::Toxic] + 1) / 16 + if (battler.hp <= next_toxic_damage && battler.hp > poison_damage) || + next_toxic_damage > poison_damage * 2 PBDebug.log_ai("#{battler.name} wants to switch to reduce toxic to regular poisoning") next true end @@ -219,30 +215,207 @@ 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. # TODO: Review switch deciding. #=============================================================================== Battle::AI::Handlers::ShouldSwitch.add(:bad_encored_move, - proc { |battler, ai, battle| - if battler.effects[PBEffects::Encore] > 0 && ai.trainer.medium_skill? - idxEncoredMove = battler.battler.pbEncoredMoveIndex - if idxEncoredMove >= 0 - ai.set_up_move_check(battler.moves[idxEncoredMove]) - scoreSum = 0 - scoreCount = 0 - battler.battler.allOpposing.each do |b| - scoreSum += ai.pbGetMoveScore([b]) - scoreCount += 1 - end - if scoreCount > 0 && scoreSum / scoreCount <= 20 - if ai.pbAIRandom(100) < 80 - PBDebug.log_ai("#{battler.name} wants to switch because of encoring a bad move") - next true - else - next false - end - end + proc { |battler, reserves, ai, battle| + next false if battler.effects[PBEffects::Encore] == 0 + idxEncoredMove = battler.battler.pbEncoredMoveIndex + next false if idxEncoredMove < 0 + ai.set_up_move_check(battler.moves[idxEncoredMove]) + scoreSum = 0 + scoreCount = 0 + battler.battler.allOpposing.each do |b| + scoreSum += ai.pbGetMoveScore([b]) + scoreCount += 1 + end + if scoreCount > 0 && scoreSum / scoreCount <= 20 + if ai.pbAIRandom(100) < 80 + PBDebug.log_ai("#{battler.name} wants to switch because of encoring a bad move") + next true + else + next false end end next false @@ -254,7 +427,7 @@ Battle::AI::Handlers::ShouldSwitch.add(:bad_encored_move, # TODO: Review switch deciding. #=============================================================================== Battle::AI::Handlers::ShouldSwitch.add(:sudden_death, - proc { |battler, ai, battle| + proc { |battler, reserves, ai, battle| if battle.rules["suddendeath"] && battler.turnCount > 0 if battler.hp <= battler.totalhp / 4 && ai.pbAIRandom(100) < 30 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 } ) + +#=============================================================================== +# 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 +# } +# ) diff --git a/Data/Scripts/011_Battle/005_AI/101_AITrainer.rb b/Data/Scripts/011_Battle/005_AI/101_AITrainer.rb index 196cfd72e..bea900e9e 100644 --- a/Data/Scripts/011_Battle/005_AI/101_AITrainer.rb +++ b/Data/Scripts/011_Battle/005_AI/101_AITrainer.rb @@ -13,6 +13,7 @@ # PredictMoveFailure # ScoreMoves # PreferMultiTargetMoves +# ConsiderSwitching (can choose to switch out Pokémon) # ReserveLastPokemon (don't switch it in if possible) # UsePokemonInOrder (uses earliest-listed Pokémon possible) #=============================================================================== @@ -57,6 +58,7 @@ class Battle::AI::AITrainer @skill_flags.push("ScoreMoves") @skill_flags.push("PreferMultiTargetMoves") end + @skill_flags.push("ConsiderSwitching") if medium_skill? if !medium_skill? @skill_flags.push("UsePokemonInOrder") elsif best_skill? diff --git a/Data/Scripts/011_Battle/005b_AI move function codes/053_AI_MoveHandlers_BattlerOther.rb b/Data/Scripts/011_Battle/005b_AI move function codes/053_AI_MoveHandlers_BattlerOther.rb index 59dd520af..bbd7e8524 100644 --- a/Data/Scripts/011_Battle/005b_AI move function codes/053_AI_MoveHandlers_BattlerOther.rb +++ b/Data/Scripts/011_Battle/005b_AI move function codes/053_AI_MoveHandlers_BattlerOther.rb @@ -697,7 +697,7 @@ Battle::AI::Handlers::MoveEffectScore.add("SetUserTypesBasedOnEnvironment", # 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) # 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 score -= 8 if user.has_damaging_move_of_type?(type) end @@ -750,8 +750,8 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("SetUserTypesToResistLast Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("SetUserTypesToTargetTypes", proc { |move, user, target, ai, battle| next true if !user.battler.canChangeType? - next true if target.battler.pbTypes(true).empty? - next true if user.battler.pbTypes == target.battler.pbTypes && + next true if target.pbTypes(true).empty? + next true if user.pbTypes == target.pbTypes && user.effects[PBEffects::ExtraType] == target.effects[PBEffects::ExtraType] next false } diff --git a/Data/Scripts/011_Battle/005b_AI move function codes/054_AI_MoveHandlers_MoveAttributes.rb b/Data/Scripts/011_Battle/005b_AI move function codes/054_AI_MoveHandlers_MoveAttributes.rb index 1233c1518..cf2fddc3c 100644 --- a/Data/Scripts/011_Battle/005b_AI move function codes/054_AI_MoveHandlers_MoveAttributes.rb +++ b/Data/Scripts/011_Battle/005b_AI move function codes/054_AI_MoveHandlers_MoveAttributes.rb @@ -1275,7 +1275,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RecoilHalfOfDamageDealt" Battle::AI::Handlers::MoveBasePower.add("EffectivenessIncludesFlyingType", proc { |power, move, user, target, ai, battle| if GameData::Type.exists?(:FLYING) - targetTypes = target.battler.pbTypes(true) + targetTypes = target.pbTypes(true) mult = Effectiveness.calculate(:FLYING, *targetTypes) power = (power * mult).round end diff --git a/Data/Scripts/014_Pokemon/004_Pokemon_Move.rb b/Data/Scripts/014_Pokemon/004_Pokemon_Move.rb index 312c53ac2..e5fc59404 100644 --- a/Data/Scripts/014_Pokemon/004_Pokemon_Move.rb +++ b/Data/Scripts/014_Pokemon/004_Pokemon_Move.rb @@ -47,18 +47,21 @@ class Pokemon end alias totalpp total_pp - def function_code; return GameData::Move.get(@id).function_code; end - def power; return GameData::Move.get(@id).power; end - def type; return GameData::Move.get(@id).type; end - def category; return GameData::Move.get(@id).category; end - def accuracy; return GameData::Move.get(@id).accuracy; end - def effect_chance; return GameData::Move.get(@id).effect_chance; end - def target; return GameData::Move.get(@id).target; end - def priority; return GameData::Move.get(@id).priority; end - def flags; return GameData::Move.get(@id).flags; end - def name; return GameData::Move.get(@id).name; end - def description; return GameData::Move.get(@id).description; end - def hidden_move?; return GameData::Move.get(@id).hidden_move?; end + def function_code; return GameData::Move.get(@id).function_code; end + def power; return GameData::Move.get(@id).power; end + def type; return GameData::Move.get(@id).type; 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 effect_chance; return GameData::Move.get(@id).effect_chance; end + def target; return GameData::Move.get(@id).target; end + def priority; return GameData::Move.get(@id).priority; end + def flags; return GameData::Move.get(@id).flags; end + def name; return GameData::Move.get(@id).name; end + def description; return GameData::Move.get(@id).description; end + def hidden_move?; return GameData::Move.get(@id).hidden_move?; end # @deprecated This method is slated to be removed in v22. def base_damage