From b094a2fd8e6b0068f4bd9df24ec7c00d965c8916 Mon Sep 17 00:00:00 2001 From: Maruno17 Date: Sun, 21 Aug 2022 15:59:49 +0100 Subject: [PATCH] Reapplied various AI changes from previous atempt at improving the AI, split function code move score changes into handlers --- .../003_Game processing/005_Event_Handlers.rb | 189 +- .../006_Event_HandlerCollections.rb | 2 +- .../011_Battle/005_AI/001_Battle_AI.rb | 117 +- Data/Scripts/011_Battle/005_AI/002_AI_Item.rb | 15 +- .../011_Battle/005_AI/003_AI_Switch.rb | 103 +- Data/Scripts/011_Battle/005_AI/004_AI_Move.rb | 694 ++++++-- .../005_AI/005_AI_Move_EffectScores_1.rb | 1537 ---------------- .../005_AI/006_AI_Move_EffectScores_2.rb | 575 ------ .../005_AI/007_AI_Move_EffectScores_3.rb | 1383 --------------- .../005_AI/008_AI_Move_Utilities.rb | 413 +++-- .../Scripts/011_Battle/005_AI/009_AI_Roles.rb | 169 ++ .../005_AI/020_AI_MoveEffectScores_Generic.rb | 431 +++++ .../005_AI/051_AI_MoveHandlers_Misc.rb | 249 +++ .../052_AI_MoveHandlers_BattlerStats.rb | 1576 +++++++++++++++++ .../053_AI_MoveHandlers_BattlerOther.rb | 683 +++++++ .../054_AI_MoveHandlers_MoveAttributes.rb | 492 +++++ .../005_AI/055_AI_MoveHandlers_MultiHit.rb | 207 +++ .../005_AI/056_AI_MoveHandlers_Healing.rb | 343 ++++ .../005_AI/057_AI_MoveHandlers_Items.rb | 193 ++ .../058_AI_MoveHandlers_ChangeMoveEffect.rb | 271 +++ .../059_AI_MoveHandlers_SwitchingActing.rb | 347 ++++ .../003_BattlePalaceBattle.rb | 7 +- .../004_BattleArenaBattle.rb | 4 +- .../015_Trainers and player/001_Trainer.rb | 4 + 24 files changed, 5987 insertions(+), 4017 deletions(-) delete mode 100644 Data/Scripts/011_Battle/005_AI/005_AI_Move_EffectScores_1.rb delete mode 100644 Data/Scripts/011_Battle/005_AI/006_AI_Move_EffectScores_2.rb delete mode 100644 Data/Scripts/011_Battle/005_AI/007_AI_Move_EffectScores_3.rb create mode 100644 Data/Scripts/011_Battle/005_AI/009_AI_Roles.rb create mode 100644 Data/Scripts/011_Battle/005_AI/020_AI_MoveEffectScores_Generic.rb create mode 100644 Data/Scripts/011_Battle/005_AI/051_AI_MoveHandlers_Misc.rb create mode 100644 Data/Scripts/011_Battle/005_AI/052_AI_MoveHandlers_BattlerStats.rb create mode 100644 Data/Scripts/011_Battle/005_AI/053_AI_MoveHandlers_BattlerOther.rb create mode 100644 Data/Scripts/011_Battle/005_AI/054_AI_MoveHandlers_MoveAttributes.rb create mode 100644 Data/Scripts/011_Battle/005_AI/055_AI_MoveHandlers_MultiHit.rb create mode 100644 Data/Scripts/011_Battle/005_AI/056_AI_MoveHandlers_Healing.rb create mode 100644 Data/Scripts/011_Battle/005_AI/057_AI_MoveHandlers_Items.rb create mode 100644 Data/Scripts/011_Battle/005_AI/058_AI_MoveHandlers_ChangeMoveEffect.rb create mode 100644 Data/Scripts/011_Battle/005_AI/059_AI_MoveHandlers_SwitchingActing.rb diff --git a/Data/Scripts/003_Game processing/005_Event_Handlers.rb b/Data/Scripts/003_Game processing/005_Event_Handlers.rb index fb679a4c4..7e9417b94 100644 --- a/Data/Scripts/003_Game processing/005_Event_Handlers.rb +++ b/Data/Scripts/003_Game processing/005_Event_Handlers.rb @@ -91,94 +91,76 @@ class NamedEvent end #=============================================================================== -# Unused. +# A class that stores code that can be triggered. Each piece of code has an +# associated ID, which can be anything that can be used as a key in a hash. #=============================================================================== class HandlerHash - def initialize(mod) - @mod = mod - @hash = {} - @addIfs = [] - @symbolCache = {} + def initialize + @hash = {} + @add_ifs = [] end - def fromSymbol(sym) - return sym unless sym.is_a?(Symbol) || sym.is_a?(String) - mod = Object.const_get(@mod) rescue nil - return nil if !mod - return mod.const_get(sym.to_sym) rescue nil - end - - def toSymbol(sym) - return sym.to_sym if sym.is_a?(Symbol) || sym.is_a?(String) - ret = @symbolCache[sym] - return ret if ret - mod = Object.const_get(@mod) rescue nil - return nil if !mod - mod.constants.each do |key| - next if mod.const_get(key) != sym - ret = key.to_sym - @symbolCache[sym] = ret - break + def [](id) + return @hash[id] if id && @hash[id] + @add_ifs.each do |add_if| + return add_if[2] if add_if[1].call(id) end - return ret + return nil end - def addIf(conditionProc, handler = nil, &handlerBlock) + def add(id, handler = nil, &handlerBlock) if ![Proc, Hash].include?(handler.class) && !block_given? - raise ArgumentError, "addIf call for #{self.class.name} has no valid handler (#{handler.inspect} was given)" + raise ArgumentError, "#{self.class.name} for #{id.inspect} has no valid handler (#{handler.inspect} was given)" end - @addIfs.push([conditionProc, handler || handlerBlock]) + @hash[id] = handler || handlerBlock if id && !id.empty? end - def add(sym, handler = nil, &handlerBlock) # 'sym' can be an ID or symbol + def add_if(id, conditionProc, handler = nil, &handlerBlock) if ![Proc, Hash].include?(handler.class) && !block_given? - raise ArgumentError, "#{self.class.name} for #{sym.inspect} has no valid handler (#{handler.inspect} was given)" + raise ArgumentError, "add_if call for #{self.class.name} has no valid handler (#{handler.inspect} was given)" end - id = fromSymbol(sym) - @hash[id] = handler || handlerBlock if id - symbol = toSymbol(sym) - @hash[symbol] = handler || handlerBlock if symbol + @add_ifs.push([id, conditionProc, handler || handlerBlock]) end def copy(src, *dests) handler = self[src] - if handler - dests.each do |dest| - self.add(dest, handler) - end - end + return if !handler + dests.each { |dest| add(dest, handler) } end - def [](sym) # 'sym' can be an ID or symbol - id = fromSymbol(sym) - ret = nil - ret = @hash[id] if id && @hash[id] # Real ID from the item - symbol = toSymbol(sym) - ret = @hash[symbol] if symbol && @hash[symbol] # Symbol or string - unless ret - @addIfs.each do |addif| - return addif[1] if addif[0].call(id) - end + def remove(key) + if @hash.keys.include?(key) + @hash.delete(key) + else + @add_ifs.delete_if { |add_if| add_if[0] == key } end - return ret - end - - def trigger(sym, *args) - handler = self[sym] - return (handler) ? handler.call(fromSymbol(sym), *args) : nil end def clear @hash.clear + @add_ifs.clear + end + + def each + @hash.each_pair { |key, value| yield key, value } + end + + def keys + return @hash.keys.clone + end + + # NOTE: The call does not pass id as a parameter to the proc/block. + def trigger(id, *args) + handler = self[id] + return handler&.call(*args) end end #=============================================================================== -# A stripped-down version of class HandlerHash which only deals with symbols and -# doesn't care about whether those symbols are defined as constants in a class -# or module. +# A stripped-down version of class HandlerHash which only deals with IDs that +# are symbols. #=============================================================================== -class HandlerHash2 +class HandlerHashSymbol def initialize @hash = {} @add_ifs = [] @@ -219,6 +201,7 @@ class HandlerHash2 def clear @hash.clear + @add_ifs.clear end def trigger(sym, *args) @@ -229,32 +212,63 @@ class HandlerHash2 end #=============================================================================== -# An even more stripped down version of class HandlerHash which just takes -# hashes with keys, no matter what the keys are. +# A specialised version of class HandlerHash which only deals with IDs that are +# constants in a particular class or module. That class or module must be +# defined when creating an instance of this class. +# Unused. #=============================================================================== -class HandlerHashBasic - def initialize - @hash = {} - @addIfs = [] +class HandlerHashEnum + def initialize(mod) + @mod = mod + @hash = {} + @addIfs = [] + @symbolCache = {} end - def [](entry) + def [](sym) # 'sym' can be an ID or symbol + id = fromSymbol(sym) ret = nil - ret = @hash[entry] if entry && @hash[entry] + ret = @hash[id] if id && @hash[id] # Real ID from the item + symbol = toSymbol(sym) + ret = @hash[symbol] if symbol && @hash[symbol] # Symbol or string unless ret @addIfs.each do |addif| - return addif[1] if addif[0].call(entry) + return addif[1] if addif[0].call(id) end end return ret end - def add(entry, handler = nil, &handlerBlock) - if ![Proc, Hash].include?(handler.class) && !block_given? - raise ArgumentError, "#{self.class.name} for #{entry.inspect} has no valid handler (#{handler.inspect} was given)" + def fromSymbol(sym) + return sym unless sym.is_a?(Symbol) || sym.is_a?(String) + mod = Object.const_get(@mod) rescue nil + return nil if !mod + return mod.const_get(sym.to_sym) rescue nil + end + + def toSymbol(sym) + return sym.to_sym if sym.is_a?(Symbol) || sym.is_a?(String) + ret = @symbolCache[sym] + return ret if ret + mod = Object.const_get(@mod) rescue nil + return nil if !mod + mod.constants.each do |key| + next if mod.const_get(key) != sym + ret = key.to_sym + @symbolCache[sym] = ret + break end - return if !entry || entry.empty? - @hash[entry] = handler || handlerBlock + return ret + end + + def add(sym, handler = nil, &handlerBlock) # 'sym' can be an ID or symbol + if ![Proc, Hash].include?(handler.class) && !block_given? + raise ArgumentError, "#{self.class.name} for #{sym.inspect} has no valid handler (#{handler.inspect} was given)" + end + id = fromSymbol(sym) + @hash[id] = handler || handlerBlock if id + symbol = toSymbol(sym) + @hash[symbol] = handler || handlerBlock if symbol end def addIf(conditionProc, handler = nil, &handlerBlock) @@ -267,42 +281,31 @@ class HandlerHashBasic def copy(src, *dests) handler = self[src] return if !handler - dests.each { |dest| add(dest, handler) } - end - - def remove(key) - @hash.delete(key) + dests.each { |dest| self.add(dest, handler) } end def clear @hash.clear + @addIfs.clear end - def each - @hash.each_pair { |key, value| yield key, value } - end - - def keys - return @hash.keys.clone - end - - def trigger(entry, *args) - handler = self[entry] - return handler&.call(*args) + def trigger(sym, *args) + handler = self[sym] + return (handler) ? handler.call(fromSymbol(sym), *args) : nil end end #=============================================================================== # #=============================================================================== -class SpeciesHandlerHash < HandlerHash2 +class SpeciesHandlerHash < HandlerHashSymbol end -class AbilityHandlerHash < HandlerHash2 +class AbilityHandlerHash < HandlerHashSymbol end -class ItemHandlerHash < HandlerHash2 +class ItemHandlerHash < HandlerHashSymbol end -class MoveHandlerHash < HandlerHash2 +class MoveHandlerHash < HandlerHashSymbol end diff --git a/Data/Scripts/003_Game processing/006_Event_HandlerCollections.rb b/Data/Scripts/003_Game processing/006_Event_HandlerCollections.rb index daf628d39..807311c0e 100644 --- a/Data/Scripts/003_Game processing/006_Event_HandlerCollections.rb +++ b/Data/Scripts/003_Game processing/006_Event_HandlerCollections.rb @@ -81,7 +81,7 @@ module MenuHandlers @@handlers = {} def self.add(menu, option, hash) - @@handlers[menu] = HandlerHashBasic.new if !@@handlers.has_key?(menu) + @@handlers[menu] = HandlerHash.new if !@@handlers.has_key?(menu) @@handlers[menu].add(option, hash) end 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 4a2c395ab..25edfb07c 100644 --- a/Data/Scripts/011_Battle/005_AI/001_Battle_AI.rb +++ b/Data/Scripts/011_Battle/005_AI/001_Battle_AI.rb @@ -1,25 +1,34 @@ -# AI skill levels: -# 0: Wild Pokémon -# 1-31: Basic trainer (young/inexperienced) -# 32-47: Some skill -# 48-99: High skill -# 100+: Best trainers (Gym Leaders, Elite Four, Champion) -# NOTE: A trainer's skill value can range from 0-255, but by default only four -# distinct skill levels exist. The skill value is typically the same as -# the trainer's base money value. -module PBTrainerAI - # Minimum skill level to be in each AI category. - def self.minimumSkill; return 1; end - def self.mediumSkill; return 32; end - def self.highSkill; return 48; end - def self.bestSkill; return 100; end -end - - - +#=============================================================================== +# +#=============================================================================== class Battle::AI + # AI skill levels: + # 0: Wild Pokémon + # 1-31: Basic trainer (young/inexperienced) + # 32-47: Some skill + # 48-99: High skill + # 100+: Best trainers (Gym Leaders, Elite Four, Champion) + # NOTE: A trainer's skill value can range from 0-255, but by default only four + # distinct skill levels exist. The skill value is typically the same as + # the trainer's base money value. + module AILevel + # Minimum skill level to be in each AI skill bracket. + def self.minimum; return 1; end + def self.medium; return 32; end + def self.high; return 48; end + def self.best; return 100; end + end + + #============================================================================= + # + #============================================================================= def initialize(battle) - @battle = battle + @battle = battle + @skill = 0 + @user = nil + @wildBattler = @battle.wildBattle? # Whether AI is choosing for a wild Pokémon + @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 pbAIRandom(x); return rand(x); end @@ -44,26 +53,66 @@ class Battle::AI return Math.sqrt(varianceTimesN / n) end - #============================================================================= - # Decide whether the opponent should Mega Evolve their Pokémon - #============================================================================= - def pbEnemyShouldMegaEvolve?(idxBattler) - battler = @battle.battlers[idxBattler] - if @battle.pbCanMegaEvolve?(idxBattler) # Simple "always should if possible" - PBDebug.log("[AI] #{battler.pbThis} (#{idxBattler}) will Mega Evolve") + # Decide whether the opponent should Mega Evolve their Pokémon. + def pbEnemyShouldMegaEvolve? + if @battle.pbCanMegaEvolve?(@user.index) # Simple "always should if possible" + PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) will Mega Evolve") return true end return false end - #============================================================================= - # Choose an action - #============================================================================= + # Choose an action. def pbDefaultChooseEnemyCommand(idxBattler) - return if pbEnemyShouldUseItem?(idxBattler) - return if pbEnemyShouldWithdraw?(idxBattler) + set_up(idxBattler) + choices = pbGetMoveScores + return if pbEnemyShouldUseItem? + return if pbEnemyShouldWithdraw? return if @battle.pbAutoFightMenu(idxBattler) - @battle.pbRegisterMegaEvolution(idxBattler) if pbEnemyShouldMegaEvolve?(idxBattler) - pbChooseMoves(idxBattler) + @battle.pbRegisterMegaEvolution(idxBattler) if pbEnemyShouldMegaEvolve? + pbChooseMove(choices) + end + + # Set some class variables for the Pokémon whose action is being chosen + def set_up(idxBattler) + # TODO: Where relevant, pretend the user is Mega Evolved if it isn't but can + # be. + @user = @battle.battlers[idxBattler] + @wildBattler = (@battle.wildBattle? && @user.opposes?) + @skill = 0 + if !@wildBattler + @skill = @battle.pbGetOwnerFromBattlerIndex(@user.index).skill_level || 0 + @skill = AILevel.minimum if @skill < AILevel.minimum + end + end + + def skill_check(threshold) + return @skill >= threshold + end +end + +#=============================================================================== +# +#=============================================================================== +module Battle::AI::Handlers + MoveEffectScore = HandlerHash.new + MoveBasePower = HandlerHash.new + # Move type + # Move accuracy + # Move target + # Move additional effect chance + # Move unselectable check + # Move failure check + + def self.apply_move_effect_score(function_code, score, *args) + function_code = function_code.to_sym + ret = MoveEffectScore.trigger(function_code, score, *args) + return (ret.nil?) ? score : ret + end + + def self.get_base_power(function_code, power, *args) + function_code = function_code.to_sym + ret = MoveBasePower.trigger(function_code, *args) + return (ret.nil?) ? power : ret end end diff --git a/Data/Scripts/011_Battle/005_AI/002_AI_Item.rb b/Data/Scripts/011_Battle/005_AI/002_AI_Item.rb index 2a1f469aa..11ad1c9df 100644 --- a/Data/Scripts/011_Battle/005_AI/002_AI_Item.rb +++ b/Data/Scripts/011_Battle/005_AI/002_AI_Item.rb @@ -2,9 +2,8 @@ class Battle::AI #============================================================================= # Decide whether the opponent should use an item on the Pokémon #============================================================================= - def pbEnemyShouldUseItem?(idxBattler) - user = @battle.battlers[idxBattler] - item, idxTarget = pbEnemyItemToUse(idxBattler) + def pbEnemyShouldUseItem? + item, idxTarget = pbEnemyItemToUse return false if !item # Determine target of item (always the Pokémon choosing the action) useType = GameData::Item.get(item).battle_use @@ -12,19 +11,19 @@ class Battle::AI idxTarget = @battle.battlers[idxTarget].pokemonIndex # Party Pokémon end # Register use of item - @battle.pbRegisterItem(idxBattler, item, idxTarget) - PBDebug.log("[AI] #{user.pbThis} (#{user.index}) will use item #{GameData::Item.get(item).name}") + @battle.pbRegisterItem(@user.index, item, idxTarget) + PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) will use item #{GameData::Item.get(item).name}") return true end # NOTE: The AI will only consider using an item on the Pokémon it's currently # choosing an action for. - def pbEnemyItemToUse(idxBattler) + def pbEnemyItemToUse return nil if !@battle.internalBattle - items = @battle.pbGetOwnerItems(idxBattler) + items = @battle.pbGetOwnerItems(@user.index) return nil if !items || items.length == 0 # Determine target of item (always the Pokémon choosing the action) - idxTarget = idxBattler # Battler using the item + idxTarget = @user.index # Battler using the item battler = @battle.battlers[idxTarget] pkmn = battler.pokemon # Item categories 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 49fd61c81..bb8be850a 100644 --- a/Data/Scripts/011_Battle/005_AI/003_AI_Switch.rb +++ b/Data/Scripts/011_Battle/005_AI/003_AI_Switch.rb @@ -2,26 +2,24 @@ class Battle::AI #============================================================================= # Decide whether the opponent should switch Pokémon #============================================================================= - def pbEnemyShouldWithdraw?(idxBattler) - return pbEnemyShouldWithdrawEx?(idxBattler, false) + def pbEnemyShouldWithdraw? + return pbEnemyShouldWithdrawEx?(false) end - def pbEnemyShouldWithdrawEx?(idxBattler, forceSwitch) - return false if @battle.wildBattle? + def pbEnemyShouldWithdrawEx?(forceSwitch) + return false if @wildBattler shouldSwitch = forceSwitch batonPass = -1 moveType = nil - skill = @battle.pbGetOwnerFromBattlerIndex(idxBattler).skill_level || 0 - battler = @battle.battlers[idxBattler] # If Pokémon is within 6 levels of the foe, and foe's last move was # super-effective and powerful - if !shouldSwitch && battler.turnCount > 0 && skill >= PBTrainerAI.highSkill - target = battler.pbDirectOpposing(true) + if !shouldSwitch && @user.turnCount > 0 && skill_check(AILevel.high) + target = @user.pbDirectOpposing(true) if !target.fainted? && target.lastMoveUsed && - (target.level - battler.level).abs <= 6 + (target.level - @user.level).abs <= 6 moveData = GameData::Move.get(target.lastMoveUsed) moveType = moveData.type - typeMod = pbCalcTypeMod(moveType, target, battler) + typeMod = pbCalcTypeMod(moveType, target, @user) if Effectiveness.super_effective?(typeMod) && moveData.base_damage > 50 switchChance = (moveData.base_damage > 70) ? 30 : 20 shouldSwitch = (pbAIRandom(100) < switchChance) @@ -29,77 +27,76 @@ class Battle::AI end end # Pokémon can't do anything (must have been in battle for at least 5 rounds) - if !@battle.pbCanChooseAnyMove?(idxBattler) && - battler.turnCount && battler.turnCount >= 5 + if !@battle.pbCanChooseAnyMove?(@user.index) && + @user.turnCount && @user.turnCount >= 5 shouldSwitch = true end # Pokémon is Perish Songed and has Baton Pass - if skill >= PBTrainerAI.highSkill && battler.effects[PBEffects::PerishSong] == 1 - battler.eachMoveWithIndex do |m, i| + if skill_check(AILevel.high) && @user.effects[PBEffects::PerishSong] == 1 + @user.eachMoveWithIndex do |m, i| next if m.function != "SwitchOutUserPassOnEffects" # Baton Pass - next if !@battle.pbCanChooseMove?(idxBattler, i, false) + next if !@battle.pbCanChooseMove?(@user.index, i, false) batonPass = i break end end # 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 - if battler.status == :POISON && battler.statusCount > 0 && - skill >= PBTrainerAI.highSkill - toxicHP = battler.totalhp / 16 - nextToxicHP = toxicHP * (battler.effects[PBEffects::Toxic] + 1) - if battler.hp <= nextToxicHP && battler.hp > toxicHP * 2 && pbAIRandom(100) < 80 - shouldSwitch = true + if @user.status == :POISON && @user.statusCount > 0 && skill_check(AILevel.high) + toxicHP = @user.totalhp / 16 + nextToxicHP = toxicHP * (@user.effects[PBEffects::Toxic] + 1) + if @user.hp <= nextToxicHP && @user.hp > toxicHP * 2 + shouldSwitch = true if pbAIRandom(100) < 80 end end # Pokémon is Encored into an unfavourable move - if battler.effects[PBEffects::Encore] > 0 && skill >= PBTrainerAI.mediumSkill - idxEncoredMove = battler.pbEncoredMoveIndex + if @user.effects[PBEffects::Encore] > 0 && skill_check(AILevel.medium) + idxEncoredMove = @user.pbEncoredMoveIndex if idxEncoredMove >= 0 scoreSum = 0 scoreCount = 0 - battler.allOpposing.each do |b| - scoreSum += pbGetMoveScore(battler.moves[idxEncoredMove], battler, b, skill) + @user.allOpposing.each do |b| + scoreSum += pbGetMoveScore(@user.moves[idxEncoredMove], b) scoreCount += 1 end - if scoreCount > 0 && scoreSum / scoreCount <= 20 && pbAIRandom(100) < 80 - shouldSwitch = true + if scoreCount > 0 && scoreSum / scoreCount <= 20 + shouldSwitch = true if pbAIRandom(100) < 80 end end end # If there is a single foe and it is resting after Hyper Beam or is # Truanting (i.e. free turn) - if @battle.pbSideSize(battler.index + 1) == 1 && - !battler.pbDirectOpposing.fainted? && skill >= PBTrainerAI.highSkill - opp = battler.pbDirectOpposing + if @battle.pbSideSize(@user.index + 1) == 1 && + !@user.pbDirectOpposing.fainted? && skill_check(AILevel.high) + opp = @user.pbDirectOpposing if (opp.effects[PBEffects::HyperBeam] > 0 || - (opp.hasActiveAbility?(:TRUANT) && opp.effects[PBEffects::Truant])) && pbAIRandom(100) < 80 - shouldSwitch = false + (opp.hasActiveAbility?(:TRUANT) && opp.effects[PBEffects::Truant])) + shouldSwitch = false if pbAIRandom(100) < 80 end end # Sudden Death rule - I'm not sure what this means - if @battle.rules["suddendeath"] && battler.turnCount > 0 - if battler.hp <= battler.totalhp / 4 && pbAIRandom(100) < 30 + if @battle.rules["suddendeath"] && @user.turnCount > 0 + if @user.hp <= @user.totalhp / 4 && pbAIRandom(100) < 30 shouldSwitch = true - elsif battler.hp <= battler.totalhp / 2 && pbAIRandom(100) < 80 + elsif @user.hp <= @user.totalhp / 2 && pbAIRandom(100) < 80 shouldSwitch = true end end # Pokémon is about to faint because of Perish Song - if battler.effects[PBEffects::PerishSong] == 1 + if @user.effects[PBEffects::PerishSong] == 1 shouldSwitch = true end if shouldSwitch list = [] - idxPartyStart, idxPartyEnd = @battle.pbTeamIndexRangeFromBattlerIndex(idxBattler) - @battle.pbParty(idxBattler).each_with_index do |pkmn, i| + idxPartyStart, idxPartyEnd = @battle.pbTeamIndexRangeFromBattlerIndex(@user.index) + @battle.pbParty(@user.index).each_with_index do |pkmn, i| next if i == idxPartyEnd - 1 # Don't choose to switch in ace - next if !@battle.pbCanSwitch?(idxBattler, i) + next if !@battle.pbCanSwitch?(@user.index, i) # If perish count is 1, it may be worth it to switch # even with Spikes, since Perish Song's effect will end - if battler.effects[PBEffects::PerishSong] != 1 + if @user.effects[PBEffects::PerishSong] != 1 # Will contain effects that recommend against switching - spikes = battler.pbOwnSide.effects[PBEffects::Spikes] + spikes = @user.pbOwnSide.effects[PBEffects::Spikes] # Don't switch to this if too little HP if spikes > 0 spikesDmg = [8, 6, 4][spikes - 1] @@ -108,17 +105,17 @@ class Battle::AI end end # moveType is the type of the target's last used move - if moveType && Effectiveness.ineffective?(pbCalcTypeMod(moveType, battler, battler)) + if moveType && Effectiveness.ineffective?(pbCalcTypeMod(moveType, @user, @user)) weight = 65 - typeMod = pbCalcTypeModPokemon(pkmn, battler.pbDirectOpposing(true)) + typeMod = pbCalcTypeModPokemon(pkmn, @user.pbDirectOpposing(true)) if Effectiveness.super_effective?(typeMod) # Greater weight if new Pokemon's type is effective against target weight = 85 end list.unshift(i) if pbAIRandom(100) < weight # Put this Pokemon first - elsif moveType && Effectiveness.resistant?(pbCalcTypeMod(moveType, battler, battler)) + elsif moveType && Effectiveness.resistant?(pbCalcTypeMod(moveType, @user, @user)) weight = 40 - typeMod = pbCalcTypeModPokemon(pkmn, battler.pbDirectOpposing(true)) + typeMod = pbCalcTypeModPokemon(pkmn, @user.pbDirectOpposing(true)) if Effectiveness.super_effective?(typeMod) # Greater weight if new Pokemon's type is effective against target weight = 60 @@ -129,13 +126,13 @@ class Battle::AI end end if list.length > 0 - if batonPass >= 0 && @battle.pbRegisterMove(idxBattler, batonPass, false) - PBDebug.log("[AI] #{battler.pbThis} (#{idxBattler}) will use Baton Pass to avoid Perish Song") + if batonPass >= 0 && @battle.pbRegisterMove(@user.index, batonPass, false) + PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) will use Baton Pass to avoid Perish Song") return true end - if @battle.pbRegisterSwitch(idxBattler, list[0]) - PBDebug.log("[AI] #{battler.pbThis} (#{idxBattler}) will switch with " + - @battle.pbParty(idxBattler)[list[0]].name) + if @battle.pbRegisterSwitch(@user.index, list[0]) + PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) will switch with " + + @battle.pbParty(@user.index)[list[0]].name) return true end end @@ -143,10 +140,10 @@ class Battle::AI return false end - #============================================================================= - # Choose a replacement Pokémon - #============================================================================= + # Choose a replacement Pokémon (called directly from @battle, not part of + # action choosing). def pbDefaultChooseNewEnemy(idxBattler, party) + set_up(idxBattler) enemies = [] idxPartyStart, idxPartyEnd = @battle.pbTeamIndexRangeFromBattlerIndex(idxBattler) party.each_with_index do |_p, i| diff --git a/Data/Scripts/011_Battle/005_AI/004_AI_Move.rb b/Data/Scripts/011_Battle/005_AI/004_AI_Move.rb index 2f31f5f97..03f760295 100644 --- a/Data/Scripts/011_Battle/005_AI/004_AI_Move.rb +++ b/Data/Scripts/011_Battle/005_AI/004_AI_Move.rb @@ -3,25 +3,7 @@ class Battle::AI # Main move-choosing method (moves with higher scores are more likely to be # chosen) #============================================================================= - def pbChooseMoves(idxBattler) - user = @battle.battlers[idxBattler] - wildBattler = user.wild? - skill = 0 - if !wildBattler - skill = @battle.pbGetOwnerFromBattlerIndex(user.index).skill_level || 0 - end - # Get scores and targets for each move - # NOTE: A move is only added to the choices array if it has a non-zero - # score. - choices = [] - user.eachMoveWithIndex do |_m, i| - next if !@battle.pbCanChooseMove?(idxBattler, i, false) - if wildBattler - pbRegisterMoveWild(user, i, choices) - else - pbRegisterMoveTrainer(user, i, choices, skill) - end - end + def pbChooseMove(choices) # Figure out useful information about the choices totalScore = 0 maxScore = 0 @@ -29,18 +11,9 @@ class Battle::AI totalScore += c[1] maxScore = c[1] if maxScore < c[1] end - # Log the available choices - if $INTERNAL - logMsg = "[AI] Move choices for #{user.pbThis(true)} (#{user.index}): " - choices.each_with_index do |c, i| - logMsg += "#{user.moves[c[0]].name}=#{c[1]}" - logMsg += " (target #{c[2]})" if c[2] >= 0 - logMsg += ", " if i < choices.length - 1 - end - PBDebug.log(logMsg) - end + # Find any preferred moves and just choose from them - if !wildBattler && skill >= PBTrainerAI.highSkill && maxScore > 100 + if skill_check(AILevel.high) && maxScore > 100 stDev = pbStdDev(choices) if stDev >= 40 && pbAIRandom(100) < 90 preferredMoves = [] @@ -51,97 +24,141 @@ class Battle::AI end if preferredMoves.length > 0 m = preferredMoves[pbAIRandom(preferredMoves.length)] - PBDebug.log("[AI] #{user.pbThis} (#{user.index}) prefers #{user.moves[m[0]].name}") - @battle.pbRegisterMove(idxBattler, m[0], false) - @battle.pbRegisterTarget(idxBattler, m[2]) if m[2] >= 0 + PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) prefers #{@user.moves[m[0]].name}") + @battle.pbRegisterMove(@user.index, m[0], false) + @battle.pbRegisterTarget(@user.index, m[2]) if m[2] >= 0 return end end end + # Decide whether all choices are bad, and if so, try switching instead - if !wildBattler && skill >= PBTrainerAI.highSkill + if !@wildBattler && skill_check(AILevel.high) badMoves = false - if ((maxScore <= 20 && user.turnCount > 2) || - (maxScore <= 40 && user.turnCount > 5)) && pbAIRandom(100) < 80 - badMoves = true + if (maxScore <= 20 && @user.turnCount > 2) || + (maxScore <= 40 && @user.turnCount > 5) + badMoves = true if pbAIRandom(100) < 80 end - if !badMoves && totalScore < 100 && user.turnCount > 1 + if !badMoves && totalScore < 100 && @user.turnCount > 1 badMoves = true choices.each do |c| - next if !user.moves[c[0]].damagingMove? + next if !@user.moves[c[0]].damagingMove? badMoves = false break end badMoves = false if badMoves && pbAIRandom(100) < 10 end - if badMoves && pbEnemyShouldWithdrawEx?(idxBattler, true) + if badMoves && pbEnemyShouldWithdrawEx?(true) if $INTERNAL - PBDebug.log("[AI] #{user.pbThis} (#{user.index}) will switch due to terrible moves") + PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) will switch due to terrible moves") end return end end + # If there are no calculated choices, pick one at random if choices.length == 0 - PBDebug.log("[AI] #{user.pbThis} (#{user.index}) doesn't want to use any moves; picking one at random") - user.eachMoveWithIndex do |_m, i| - next if !@battle.pbCanChooseMove?(idxBattler, i, false) + PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) doesn't want to use any moves; picking one at random") + @user.eachMoveWithIndex do |_m, i| + next if !@battle.pbCanChooseMove?(@user.index, i, false) choices.push([i, 100, -1]) # Move index, score, target end if choices.length == 0 # No moves are physically possible to use; use Struggle - @battle.pbAutoChooseMove(user.index) + @battle.pbAutoChooseMove(@user.index) end end + # Randomly choose a move from the choices and register it randNum = pbAIRandom(totalScore) choices.each do |c| randNum -= c[1] next if randNum >= 0 - @battle.pbRegisterMove(idxBattler, c[0], false) - @battle.pbRegisterTarget(idxBattler, c[2]) if c[2] >= 0 + @battle.pbRegisterMove(@user.index, c[0], false) + @battle.pbRegisterTarget(@user.index, c[2]) if c[2] >= 0 break end # Log the result - if @battle.choices[idxBattler][2] - PBDebug.log("[AI] #{user.pbThis} (#{user.index}) will use #{@battle.choices[idxBattler][2].name}") + if @battle.choices[@user.index][2] + PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) will use #{@battle.choices[@user.index][2].name}") end end + #============================================================================= + # Get scores for the user's moves (done before any action is assessed) + # NOTE: A move is only added to the choices array if it has a non-zero score. + #============================================================================= + def pbGetMoveScores + # Get scores and targets for each move + choices = [] + # TODO: Split this into two, the first part being the calculation of all + # predicted damages and the second part being the score calculations + # (which are based on the predicted damages). Note that this requires + # saving each of the scoresAndTargets entries in here rather than in + # def pbRegisterMoveTrainer, and only at the very end are they + # whittled down to one per move which are chosen from. Multi-target + # moves could be fiddly since damages should be calculated for each + # target but they're all related. + @user.eachMoveWithIndex do |_m, i| + next if !@battle.pbCanChooseMove?(@user.index, i, false) + if @wildBattler + pbRegisterMoveWild(i, choices) + else + pbRegisterMoveTrainer(i, choices) + end + end + # Log the available choices + if $INTERNAL + logMsg = "[AI] Move choices for #{@user.pbThis(true)} (#{@user.index}): " + choices.each_with_index do |c, i| + logMsg += "#{@user.moves[c[0]].name}=#{c[1]}" + logMsg += " (target #{c[2]})" if c[2] >= 0 + logMsg += ", " if i < choices.length-1 + end + PBDebug.log(logMsg) + end + return choices + end + #============================================================================= # Get scores for the given move against each possible target #============================================================================= # Wild Pokémon choose their moves randomly. - def pbRegisterMoveWild(_user, idxMove, choices) + def pbRegisterMoveWild(idxMove, choices) + score = 100 + # Doubly prefer one of the user's moves (the choice is random but consistent + # and does not correlate to any other property of the user) + score *= 2 if @user.pokemon.personalID % @user.moves.length == idxMove choices.push([idxMove, 100, -1]) # Move index, score, target end # Trainer Pokémon calculate how much they want to use each of their moves. - def pbRegisterMoveTrainer(user, idxMove, choices, skill) - move = user.moves[idxMove] - target_data = move.pbTarget(user) + def pbRegisterMoveTrainer(idxMove, choices) + move = @user.moves[idxMove] + target_data = move.pbTarget(@user) + # TODO: Alter target_data if user has Protean and move is Curse. if [:UserAndAllies, :AllAllies, :AllBattlers].include?(target_data.id) || target_data.num_targets == 0 # If move has no targets, affects the user, a side or the whole field, or # specially affects multiple Pokémon and the AI calculates an overall # score at once instead of per target - score = pbGetMoveScore(move, user, user, skill) + score = pbGetMoveScore(move, @user) choices.push([idxMove, score, -1]) if score > 0 elsif target_data.num_targets > 1 # If move affects multiple battlers and you don't choose a particular one totalScore = 0 @battle.allBattlers.each do |b| - next if !@battle.pbMoveCanTarget?(user.index, b.index, target_data) - score = pbGetMoveScore(move, user, b, skill) - totalScore += ((user.opposes?(b)) ? score : -score) + next if !@battle.pbMoveCanTarget?(@user.index, b.index, target_data) + score = pbGetMoveScore(move, b) + totalScore += ((@user.opposes?(b)) ? score : -score) end choices.push([idxMove, totalScore, -1]) if totalScore > 0 else # If move affects one battler and you have to choose which one scoresAndTargets = [] @battle.allBattlers.each do |b| - next if !@battle.pbMoveCanTarget?(user.index, b.index, target_data) - next if target_data.targets_foe && !user.opposes?(b) - score = pbGetMoveScore(move, user, b, skill) + next if !@battle.pbMoveCanTarget?(@user.index, b.index, target_data) + next if target_data.targets_foe && !@user.opposes?(b) + score = pbGetMoveScore(move, b) scoresAndTargets.push([score, b.index]) if score > 0 end if scoresAndTargets.length > 0 @@ -152,34 +169,134 @@ class Battle::AI end end + #============================================================================= + # Set some class variables for the move being assessed + #============================================================================= + def set_up_move_check(move, target) + @move = move + @target = target + # TODO: Calculate pbRoughType once here. + # Determine whether user or target is faster, and store that result so it + # doesn't need recalculating + if @target + user_speed = pbRoughStat(@user, :SPEED) + target_speed = pbRoughStat(@target, :SPEED) + @user_faster = (user_speed > target_speed) ^ (@battle.field.effects[PBEffects::TrickRoom] > 0) + else + @user_faster = false # Won't be used if there is no target + end + end + #============================================================================= # Get a score for the given move being used against the given target #============================================================================= - def pbGetMoveScore(move, user, target, skill = 100) - skill = PBTrainerAI.minimumSkill if skill < PBTrainerAI.minimumSkill - score = 100 - score = pbGetMoveScoreFunctionCode(score, move, user, target, skill) + def pbGetMoveScore(move, target = nil) + set_up_move_check(move, target) + + # Get the base score for the move + if @move.damagingMove? + # Is also the predicted damage amount as a percentage of target's current HP + score = pbGetDamagingMoveBaseScore + else # Status moves + # Depends on the move's effect + score = pbGetStatusMoveBaseScore + end + # Modify the score according to the move's effect + score = Battle::AI::Handlers.apply_move_effect_score(move.function, + score, move, @user, target, @skill, self, @battle) + # A score of 0 here means it absolutely should not be used return 0 if score <= 0 - if skill >= PBTrainerAI.mediumSkill + + # TODO: High priority checks: + # => Prefer move if it will KO the target (moreso if user is slower than target) + # => Don't prefer damaging move if it won't KO, user has Stance Change and + # is in shield form, and user is slower than the target + # => Check memory for past damage dealt by a target's non-high priority move, + # and prefer move if user is slower than the target and another hit from + # the same amount will KO the user + # => Check memory for past damage dealt by a target's priority move, and don't + # prefer the move if user is slower than the target and can't move faster + # than it because of priority + # => Discard move if user is slower than the target and target is semi- + # invulnerable (and move won't hit it) + # => Check memory for whether target has previously used Quick Guard, and + # don't prefer move if so + + # TODO: Low priority checks: + # => Don't prefer move if user is faster than the target + # => Prefer move if user is faster than the target and target is semi- + # invulnerable + + # Don't prefer a dancing move if the target has the Dancer ability + # TODO: Check all battlers, not just the target. + if skill_check(AILevel.high) && @move.danceMove? && @target.hasActiveAbility?(:DANCER) + score /= 2 + end + + # TODO: Check memory for whether target has previously used Ion Deluge, and + # don't prefer move if it's Normal-type and target is immune because + # of its ability (Lightning Rod, etc.). + + # TODO: Discard move if it can be redirected by a non-target's ability + # (Lightning Rod/Storm Drain). Include checking for a previous use of + # Ion Deluge and this move being Normal-type. + # => If non-target is a user's ally, don't prefer move (rather than discarding + # it) + + # TODO: Discard move if it's sound-based and user has been Throat Chopped. + # Don't prefer move if user hasn't been Throat Chopped but target has + # previously used Throat Chop. The first part of this would probably + # go elsewhere (damage calc?). + + # TODO: Prefer move if it has a high critical hit rate, critical hits are + # possible but not certain, and target has raised defences/user has + # lowered offences (Atk/Def or SpAtk/SpDef, whichever is relevant). + + # TODO: Don't prefer damaging moves if target is Destiny Bonding. + # => Also don't prefer damaging moves if user is slower than the target, move + # is likely to be lethal, and target has previously used Destiny Bond + + # TODO: Don't prefer a move that is stopped by Wide Guard if target has + # previously used Wide Guard. + + # TODO: Don't prefer Fire-type moves if target has previously used Powder. + + # TODO: Don't prefer contact move if making contact with the target could + # trigger an effect that's bad for the user (Static, etc.). + # => Also check if target has previously used Spiky Shield.King's Shield/ + # Baneful Bunker, and don't prefer move if so + + # TODO: Prefer a contact move if making contact with the target could trigger + # an effect that's good for the user (Poison Touch/Pickpocket). + + # TODO: Don't prefer a status move if user has a damaging move that will KO + # the target. + # => If target has previously used a move that will hurt the user by 30% of + # its current HP or more, moreso don't prefer a status move. + + if skill_check(AILevel.medium) + # Prefer damaging moves if AI has no more Pokémon or AI is less clever - if @battle.pbAbleNonActiveCount(user.idxOwnSide) == 0 && - !(skill >= PBTrainerAI.highSkill && @battle.pbAbleNonActiveCount(target.idxOwnSide) > 0) - if move.statusMove? - score /= 1.5 - elsif target.hp <= target.totalhp / 2 - score *= 1.5 + if @battle.pbAbleNonActiveCount(@user.idxOwnSide) == 0 && + !(skill_check(AILevel.high) && @battle.pbAbleNonActiveCount(@target.idxOwnSide) > 0) + if @move.statusMove? + score *= 0.9 + elsif @target.hp <= @target.totalhp / 2 + score *= 1.1 end end + # Don't prefer attacking the target if they'd be semi-invulnerable - if skill >= PBTrainerAI.highSkill && move.accuracy > 0 && - (target.semiInvulnerable? || target.effects[PBEffects::SkyDrop] >= 0) + if skill_check(AILevel.high) && @move.accuracy > 0 && @user_faster && + (@target.semiInvulnerable? || @target.effects[PBEffects::SkyDrop] >= 0) miss = true - miss = false if user.hasActiveAbility?(:NOGUARD) || target.hasActiveAbility?(:NOGUARD) - if miss && pbRoughStat(user, :SPEED, skill) > pbRoughStat(target, :SPEED, skill) + miss = false if @user.hasActiveAbility?(:NOGUARD) + miss = false if skill_check(AILevel.best) && @target.hasActiveAbility?(:NOGUARD) + if skill_check(AILevel.best) && miss # Knows what can get past semi-invulnerability - if target.effects[PBEffects::SkyDrop] >= 0 || - target.inTwoTurnAttack?("TwoTurnAttackInvulnerableInSky", + if @target.effects[PBEffects::SkyDrop] >= 0 || + @target.inTwoTurnAttack?("TwoTurnAttackInvulnerableInSky", "TwoTurnAttackInvulnerableInSkyParalyzeTarget", "TwoTurnAttackInvulnerableInSkyTargetCannotAct") miss = false if move.hitsFlyingTargets? @@ -189,107 +306,388 @@ class Battle::AI miss = false if move.hitsDivingTargets? end end - score -= 80 if miss + score = 0 if miss end + # Pick a good move for the Choice items - if user.hasActiveItem?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF]) || - user.hasActiveAbility?(:GORILLATACTICS) - if move.baseDamage >= 60 - score += 60 - elsif move.damagingMove? - score += 30 - elsif move.function == "UserTargetSwapItems" - score += 70 # Trick - else - score -= 60 - end + if @user.hasActiveItem?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF]) || + @user.hasActiveAbility?(:GORILLATACTICS) + # Really don't prefer status moves (except Trick) + score *= 0.1 if @move.statusMove? && @move.function != "UserTargetSwapItems" + # Don't prefer moves of certain types + move_type = pbRoughType(@move) + # Most unpreferred types are 0x effective against another type, except + # Fire/Water/Grass + # TODO: Actually check through the types for 0x instead of hardcoding + # them. + # TODO: Reborn separately doesn't prefer Fire/Water/Grass/Electric, also + # with a 0.95x score, meaning Electric can be 0.95x twice. Why are + # these four types not preferred? Maybe because they're all not + # very effective against Dragon. + unpreferred_types = [:NORMAL, :FIGHTING, :POISON, :GROUND, :GHOST, + :FIRE, :WATER, :GRASS, :ELECTRIC, :PSYCHIC, :DRAGON] + score *= 0.95 if unpreferred_types.include?(move_type) + # Don't prefer moves with lower accuracy + score *= @move.accuracy / 100.0 if @move.accuracy > 0 + # Don't prefer moves with low PP + score *= 0.9 if @move.pp < 6 end - # If user is asleep, prefer moves that are usable while asleep - if user.status == :SLEEP && !move.usableWhenAsleep? - user.eachMove do |m| - next unless m.usableWhenAsleep? - score -= 60 - break - end + + # If user is asleep, don't prefer moves that can't be used while asleep + if skill_check(AILevel.medium) && @user.asleep? && @user.statusCount > 1 && + !@move.usableWhenAsleep? + score *= 0.2 end + # If user is frozen, prefer a move that can thaw the user - if user.status == :FROZEN - if move.thawsUser? - score += 40 + if skill_check(AILevel.medium) && @user.status == :FROZEN + if @move.thawsUser? + score += 30 else - user.eachMove do |m| + @user.eachMove do |m| next unless m.thawsUser? - score -= 60 + score = 0 # Discard this move if user knows another move that thaws break end end end + # If target is frozen, don't prefer moves that could thaw them - if target.status == :FROZEN - user.eachMove do |m| - next if m.thawsUser? - score -= 60 - break + if @target.status == :FROZEN + if pbRoughType(@move) == :FIRE || (Settings::MECHANICS_GENERATION >= 6 && @move.thawsUser?) + score *= 0.1 end end end - # Don't prefer moves that are ineffective because of abilities or effects - return 0 if pbCheckMoveImmunity(score, move, user, target, skill) - # Adjust score based on how much damage it can deal - if move.damagingMove? - score = pbGetMoveScoreDamage(score, move, user, target, skill) - else # Status moves - # Don't prefer attacks which don't deal damage - score -= 10 - # Account for accuracy of move - accuracy = pbRoughAccuracy(move, user, target, skill) - score *= accuracy / 100.0 - score = 0 if score <= 10 && skill >= PBTrainerAI.highSkill + + # Don't prefer hitting a wild shiny Pokémon + if @battle.wildBattle? && @target.opposes? && @target.shiny? + score *= 0.15 end + + # TODO: Discard a move that can be Magic Coated if either opponent has Magic + # Bounce. + + # Account for accuracy of move + accuracy = pbRoughAccuracy(@move, @target) + score *= accuracy / 100.0 + + # Prefer flinching external effects (note that move effects which cause + # flinching are dealt with in the function code part of score calculation) + if skill_check(AILevel.medium) + if !@target.hasActiveAbility?([:INNERFOCUS, :SHIELDDUST]) && + @target.effects[PBEffects::Substitute] == 0 + if @move.flinchingMove? || + (@move.damagingMove? && + (@user.hasActiveItem?([:KINGSROCK, :RAZORFANG]) || + @user.hasActiveAbility?(:STENCH))) + score *= 1.3 + end + end + end + + # # Adjust score based on how much damage it can deal + # if move.damagingMove? + # score = pbGetMoveScoreDamage(score, move, @user, @target, @skill) + # else # Status moves + # # Don't prefer attacks which don't deal damage + # score -= 10 + # # Account for accuracy of move + # accuracy = pbRoughAccuracy(move, target) + # score *= accuracy / 100.0 + # score = 0 if score <= 10 && skill_check(AILevel.high) + # end score = score.to_i score = 0 if score < 0 return score end #============================================================================= - # Add to a move's score based on how much damage it will deal (as a percentage - # of the target's current HP) + # Calculate how much damage a move is likely to do to a given target (as a + # percentage of the target's current HP) #============================================================================= - def pbGetMoveScoreDamage(score, move, user, target, skill) - return 0 if score <= 0 + def pbGetDamagingMoveBaseScore + # Don't prefer moves that are ineffective because of abilities or effects + return 0 if pbCheckMoveImmunity(@move, @target) + # Calculate how much damage the move will do (roughly) - baseDmg = pbMoveBaseDamage(move, user, target, skill) - realDamage = pbRoughDamage(move, user, target, skill, baseDmg) - # Account for accuracy of move - accuracy = pbRoughAccuracy(move, user, target, skill) - realDamage *= accuracy / 100.0 + base_damage = pbMoveBaseDamage(@move, @target) + calc_damage = pbRoughDamage(@move, @target, base_damage) + + # TODO: Maybe move this check elsewhere? Note that Reborn's base score does + # not include this halving, but the predicted damage does. # Two-turn attacks waste 2 turns to deal one lot of damage - if move.chargingTurnMove? || move.function == "AttackAndSkipNextTurn" # Hyper Beam - realDamage *= 2 / 3 # Not halved because semi-invulnerable during use or hits first turn - end - # Prefer flinching external effects (note that move effects which cause - # flinching are dealt with in the function code part of score calculation) - if skill >= PBTrainerAI.mediumSkill && !move.flinchingMove? && - !target.hasActiveAbility?(:INNERFOCUS) && - !target.hasActiveAbility?(:SHIELDDUST) && - target.effects[PBEffects::Substitute] == 0 - canFlinch = false - if user.hasActiveItem?([:KINGSROCK, :RAZORFANG]) || - user.hasActiveAbility?(:STENCH) - canFlinch = true + calc_damage /= 2 if @move.chargingTurnMove? + + # TODO: Maybe move this check elsewhere? + # Increased critical hit rate + if skill_check(AILevel.medium) + crit_stage = pbRoughCriticalHitStage(@move, @target) + if crit_stage >= 0 + crit_fraction = (crit_stage > 50) ? 1 : Battle::Move::CRITICAL_HIT_RATIOS[crit_stage] + crit_mult = (Settings::NEW_CRITICAL_HIT_RATE_MECHANICS) ? 0.5 : 1 + calc_damage *= (1 + crit_mult / crit_fraction) end - realDamage *= 1.3 if canFlinch end + # Convert damage to percentage of target's remaining HP - damagePercentage = realDamage * 100.0 / target.hp + damage_percentage = calc_damage * 100.0 / @target.hp + # Don't prefer weak attacks -# damagePercentage /= 2 if damagePercentage<20 +# damage_percentage /= 2 if damage_percentage < 20 + # Prefer damaging attack if level difference is significantly high - damagePercentage *= 1.2 if user.level - 10 > target.level +# damage_percentage *= 1.2 if @user.level - 10 > @target.level + # Adjust score - damagePercentage = 120 if damagePercentage > 120 # Treat all lethal moves the same - damagePercentage += 40 if damagePercentage > 100 # Prefer moves likely to be lethal - score += damagePercentage.to_i - return score + damage_percentage = 110 if damage_percentage > 110 # Treat all lethal moves the same + damage_percentage += 40 if damage_percentage > 100 # Prefer moves likely to be lethal + + return damage_percentage.to_i + end + + def pbGetStatusMoveBaseScore + # TODO: Call pbCheckMoveImmunity here too, not just for damaging moves + # (only if this status move will be affected). + + # TODO: Make sure all status moves are accounted for. + # TODO: Duplicates in Reborn's AI: + # "SleepTarget" Grass Whistle (15), Hypnosis (15), Sing (15), + # Lovely Kiss (20), Sleep Powder (20), Spore (60) + # "PoisonTarget" - Poison Powder (15), Poison Gas (20) + # "ParalyzeTarget" - Stun Spore (25), Glare (30) + # "ConfuseTarget" - Teeter Dance (5), Supersonic (10), + # Sweet Kiss (20), Confuse Ray (25) + # "RaiseUserAttack1" - Howl (10), Sharpen (10), Medicate (15) + # "RaiseUserSpeed2" - Agility (15), Rock Polish (25) + # "LowerTargetAttack1" - Growl (10), Baby-Doll Eyes (15) + # "LowerTargetAccuracy1" - Sand Attack (5), Flash (10), Kinesis (10), Smokescreen (10) + # "LowerTargetAttack2" - Charm (10), Feather Dance (15) + # "LowerTargetSpeed2" - String Shot (10), Cotton Spore (15), Scary Face (15) + # "LowerTargetSpDef2" - Metal Sound (10), Fake Tears (15) + case @move.function + when "ConfuseTarget", + "LowerTargetAccuracy1", + "LowerTargetEvasion1RemoveSideEffects", + "UserTargetSwapAtkSpAtkStages", + "UserTargetSwapDefSpDefStages", + "UserSwapBaseAtkDef", + "UserTargetAverageBaseAtkSpAtk", + "UserTargetAverageBaseDefSpDef", + "SetUserTypesToUserMoveType", + "SetTargetTypesToWater", + "SetUserTypesToTargetTypes", + "SetTargetAbilityToUserAbility", + "UserTargetSwapAbilities", + "PowerUpAllyMove", + "StartWeakenElectricMoves", + "StartWeakenFireMoves", + "EnsureNextMoveAlwaysHits", + "StartNegateTargetEvasionStatStageAndGhostImmunity", + "StartNegateTargetEvasionStatStageAndDarkImmunity", + "ProtectUserSideFromPriorityMoves", + "ProtectUserSideFromMultiTargetDamagingMoves", + "BounceBackProblemCausingStatusMoves", + "StealAndUseBeneficialStatusMove", + "DisableTargetMovesKnownByUser", + "DisableTargetHealingMoves", + "SetAttackerMovePPTo0IfUserFaints", + "UserEnduresFaintingThisTurn", + "RestoreUserConsumedItem", + "StartNegateHeldItems", + "StartDamageTargetEachTurnIfTargetAsleep", + "HealUserDependingOnUserStockpile", + "StartGravity", + "StartUserAirborne", + "UserSwapsPositionsWithAlly", + "StartSwapAllBattlersBaseDefensiveStats", + "RaiseTargetSpDef1", + "RaiseGroundedGrassBattlersAtkSpAtk1", + "RaiseGrassBattlersDef1", + "AddGrassTypeToTarget", + "TrapAllBattlersInBattleForOneTurn", + "EnsureNextCriticalHit", + "UserTargetSwapBaseSpeed", + "RedirectAllMovesToTarget", + "TargetUsesItsLastUsedMoveAgain" + return 5 + when "RaiseUserAttack1", + "RaiseUserDefense1", + "RaiseUserDefense1CurlUpUser", + "RaiseUserCriticalHitRate2", + "RaiseUserAtkSpAtk1", + "RaiseUserAtkSpAtk1Or2InSun", + "RaiseUserAtkAcc1", + "RaiseTargetRandomStat2", + "LowerTargetAttack1", + "LowerTargetDefense1", + "LowerTargetAccuracy1", + "LowerTargetAttack2", + "LowerTargetSpeed2", + "LowerTargetSpDef2", + "ResetAllBattlersStatStages", + "UserCopyTargetStatStages", + "SetUserTypesBasedOnEnvironment", + "DisableTargetUsingSameMoveConsecutively", + "StartTargetCannotUseItem", + "LowerTargetAttack1BypassSubstitute", + "LowerTargetAtkSpAtk1", + "LowerTargetSpAtk1", + "TargetNextFireMoveDamagesTarget" + return 10 + when "SleepTarget", + "SleepTargetIfUserDarkrai", + "SleepTargetChangeUserMeloettaForm", + "PoisonTarget", + "CureUserBurnPoisonParalysis", + "RaiseUserAttack1", + "RaiseUserSpDef1PowerUpElectricMove", + "RaiseUserEvasion1", + "RaiseUserSpeed2", + "LowerTargetAttack1", + "LowerTargetAtkDef1", + "LowerTargetAttack2", + "LowerTargetDefense2", + "LowerTargetSpeed2", + "LowerTargetSpAtk2IfCanAttract", + "LowerTargetSpDef2", + "ReplaceMoveThisBattleWithTargetLastMoveUsed", + "ReplaceMoveWithTargetLastMoveUsed", + "SetUserAbilityToTargetAbility", + "UseMoveTargetIsAboutToUse", + "UseRandomMoveFromUserParty", + "StartHealUserEachTurnTrapUserInBattle", + "HealTargetHalfOfTotalHP", + "UserFaintsHealAndCureReplacement", + "UserFaintsHealAndCureReplacementRestorePP", + "StartSunWeather", + "StartRainWeather", + "StartSandstormWeather", + "StartHailWeather", + "RaisePlusMinusUserAndAlliesDefSpDef1", + "LowerTargetSpAtk2", + "LowerPoisonedTargetAtkSpAtkSpd1", + "AddGhostTypeToTarget", + "LowerTargetAtkSpAtk1SwitchOutUser", + "RaisePlusMinusUserAndAlliesAtkSpAtk1", + "HealTargetDependingOnGrassyTerrain" + return 15 + when "SleepTarget", + "SleepTargetChangeUserMeloettaForm", + "SleepTargetNextTurn", + "PoisonTarget", + "ConfuseTarget", + "RaiseTargetSpAtk1ConfuseTarget", + "RaiseTargetAttack2ConfuseTarget", + "UserTargetSwapStatStages", + "StartUserSideImmunityToStatStageLowering", + "SetUserTypesToResistLastAttack", + "SetTargetAbilityToSimple", + "SetTargetAbilityToInsomnia", + "NegateTargetAbility", + "TransformUserIntoTarget", + "UseLastMoveUsedByTarget", + "UseLastMoveUsed", + "UseRandomMove", + "HealUserFullyAndFallAsleep", + "StartHealUserEachTurn", + "StartPerishCountsForAllBattlers", + "SwitchOutTargetStatusMove", + "TrapTargetInBattle", + "TargetMovesBecomeElectric", + "NormalMovesBecomeElectric", + "PoisonTargetLowerTargetSpeed1" + return 20 + when "BadPoisonTarget", + "ParalyzeTarget", + "BurnTarget", + "ConfuseTarget", + "AttractTarget", + "GiveUserStatusToTarget", + "RaiseUserDefSpDef1", + "RaiseUserDefense2", + "RaiseUserSpeed2", + "RaiseUserSpeed2LowerUserWeight", + "RaiseUserSpDef2", + "RaiseUserEvasion2MinimizeUser", + "RaiseUserDefense3", + "MaxUserAttackLoseHalfOfTotalHP", + "UserTargetAverageHP", + "ProtectUser", + "DisableTargetLastMoveUsed", + "DisableTargetStatusMoves", + "HealUserHalfOfTotalHP", + "HealUserHalfOfTotalHPLoseFlyingTypeThisTurn", + "HealUserPositionNextTurn", + "HealUserDependingOnWeather", + "StartLeechSeedTarget", + "AttackerFaintsIfUserFaints", + "UserTargetSwapItems", + "UserMakeSubstitute", + "UserAddStockpileRaiseDefSpDef1", + "RedirectAllMovesToUser", + "InvertTargetStatStages", + "HealUserByTargetAttackLowerTargetAttack1", + "HealUserDependingOnSandstorm" + return 25 + when "ParalyzeTarget", + "ParalyzeTargetIfNotTypeImmune", + "RaiseUserAtkDef1", + "RaiseUserAtkDefAcc1", + "RaiseUserSpAtkSpDef1", + "UseMoveDependingOnEnvironment", + "UseRandomUserMoveIfAsleep", + "DisableTargetUsingDifferentMove", + "SwitchOutUserPassOnEffects", + "AddSpikesToFoeSide", + "AddToxicSpikesToFoeSide", + "AddStealthRocksToFoeSide", + "CurseTargetOrLowerUserSpd1RaiseUserAtkDef1", + "StartSlowerBattlersActFirst", + "ProtectUserFromTargetingMovesSpikyShield", + "StartElectricTerrain", + "StartGrassyTerrain", + "StartMistyTerrain", + "StartPsychicTerrain", + "CureTargetStatusHealUserHalfOfTotalHP" + return 30 + when "CureUserPartyStatus", + "RaiseUserAttack2", + "RaiseUserSpAtk2", + "RaiseUserSpAtk3", + "StartUserSideDoubleSpeed", + "StartWeakenPhysicalDamageAgainstUserSide", + "StartWeakenSpecialDamageAgainstUserSide", + "ProtectUserSideFromDamagingMovesIfUserFirstTurn", + "ProtectUserFromDamagingMovesKingsShield", + "ProtectUserBanefulBunker" + return 35 + when "RaiseUserAtkSpd1", + "RaiseUserSpAtkSpDefSpd1", + "LowerUserDefSpDef1RaiseUserAtkSpAtkSpd2", + "RaiseUserAtk1Spd2", + "TwoTurnAttackRaiseUserSpAtkSpDefSpd2" + return 40 + when "SleepTarget", + "SleepTargetChangeUserMeloettaForm", + "AddStickyWebToFoeSide", + "StartWeakenDamageAgainstUserSideIfHail" + return 60 + end + # "DoesNothingUnusableInGravity", + # "StartUserSideImmunityToInflictedStatus", + # "LowerTargetEvasion1", + # "LowerTargetEvasion2", + # "StartPreventCriticalHitsAgainstUserSide", + # "UserFaintsLowerTargetAtkSpAtk2", + # "FleeFromBattle", + # "SwitchOutUserStatusMove" + # "TargetTakesUserItem", + # "LowerPPOfTargetLastMoveBy4", + # "StartTargetAirborneAndAlwaysHitByMoves", + # "TargetActsNext", + # "TargetActsLast", + # "ProtectUserSideFromStatusMoves" + return 0 end end diff --git a/Data/Scripts/011_Battle/005_AI/005_AI_Move_EffectScores_1.rb b/Data/Scripts/011_Battle/005_AI/005_AI_Move_EffectScores_1.rb deleted file mode 100644 index 269b3df6a..000000000 --- a/Data/Scripts/011_Battle/005_AI/005_AI_Move_EffectScores_1.rb +++ /dev/null @@ -1,1537 +0,0 @@ -class Battle::AI - #============================================================================= - # Get a score for the given move based on its effect - #============================================================================= - def pbGetMoveScoreFunctionCode(score, move, user, target, skill = 100) - case move.function - #--------------------------------------------------------------------------- - when "Struggle" - #--------------------------------------------------------------------------- - when "None" # No extra effect - #--------------------------------------------------------------------------- - when "DoesNothingCongratulations", "DoesNothingFailsIfNoAlly", - "DoesNothingUnusableInGravity" - score -= 95 - score = 0 if skill >= PBTrainerAI.highSkill - #--------------------------------------------------------------------------- - when "AddMoneyGainedFromBattle" - #--------------------------------------------------------------------------- - when "DoubleMoneyGainedFromBattle" - score -= 90 - #--------------------------------------------------------------------------- - when "FailsIfNotUserFirstTurn" - score -= 90 if user.turnCount > 0 - #--------------------------------------------------------------------------- - when "FailsIfUserHasUnusedMove" - #--------------------------------------------------------------------------- - when "FailsIfUserNotConsumedBerry" - score -= 90 if !user.belched? - #--------------------------------------------------------------------------- - when "FailsIfTargetHasNoItem" - if skill >= PBTrainerAI.mediumSkill - if !target.item || !target.itemActive? - score -= 90 - else - score += 50 - end - end - #--------------------------------------------------------------------------- - when "FailsUnlessTargetSharesTypeWithUser" - if !(user.types[0] && target.pbHasType?(user.types[0])) && - !(user.types[1] && target.pbHasType?(user.types[1])) - score -= 90 - end - #--------------------------------------------------------------------------- - when "FailsIfUserDamagedThisTurn" - score += 50 if target.effects[PBEffects::HyperBeam] > 0 - score -= 35 if target.hp <= target.totalhp / 2 # If target is weak, no - score -= 70 if target.hp <= target.totalhp / 4 # need to risk this move - #--------------------------------------------------------------------------- - when "FailsIfTargetActed" - #--------------------------------------------------------------------------- - when "CrashDamageIfFailsUnusableInGravity" - score += 10 * (user.stages[:ACCURACY] - target.stages[:EVASION]) - #--------------------------------------------------------------------------- - when "StartSunWeather" - if @battle.pbCheckGlobalAbility(:AIRLOCK) || - @battle.pbCheckGlobalAbility(:CLOUDNINE) - score -= 90 - elsif @battle.field.weather == :Sun - score -= 90 - else - user.eachMove do |m| - next if !m.damagingMove? || m.type != :FIRE - score += 20 - end - end - #--------------------------------------------------------------------------- - when "StartRainWeather" - if @battle.pbCheckGlobalAbility(:AIRLOCK) || - @battle.pbCheckGlobalAbility(:CLOUDNINE) - score -= 90 - elsif @battle.field.weather == :Rain - score -= 90 - else - user.eachMove do |m| - next if !m.damagingMove? || m.type != :WATER - score += 20 - end - end - #--------------------------------------------------------------------------- - when "StartSandstormWeather" - if @battle.pbCheckGlobalAbility(:AIRLOCK) || - @battle.pbCheckGlobalAbility(:CLOUDNINE) - score -= 90 - elsif @battle.field.weather == :Sandstorm - score -= 90 - end - #--------------------------------------------------------------------------- - when "StartHailWeather" - if @battle.pbCheckGlobalAbility(:AIRLOCK) || - @battle.pbCheckGlobalAbility(:CLOUDNINE) - score -= 90 - elsif @battle.field.weather == :Hail - score -= 90 - end - #--------------------------------------------------------------------------- - when "StartElectricTerrain" - #--------------------------------------------------------------------------- - when "StartGrassyTerrain" - #--------------------------------------------------------------------------- - when "StartMistyTerrain" - #--------------------------------------------------------------------------- - when "StartPsychicTerrain" - #--------------------------------------------------------------------------- - when "RemoveTerrain" - score -= 100 if @battle.field.terrain == :None - #--------------------------------------------------------------------------- - when "AddSpikesToFoeSide" - if user.pbOpposingSide.effects[PBEffects::Spikes] >= 3 - score -= 90 - elsif user.allOpposing.none? { |b| @battle.pbCanChooseNonActive?(b.index) } - score -= 90 # Opponent can't switch in any Pokemon - else - score += 10 * @battle.pbAbleNonActiveCount(user.idxOpposingSide) - score += [40, 26, 13][user.pbOpposingSide.effects[PBEffects::Spikes]] - end - #--------------------------------------------------------------------------- - when "AddToxicSpikesToFoeSide" - if user.pbOpposingSide.effects[PBEffects::ToxicSpikes] >= 2 - score -= 90 - elsif user.allOpposing.none? { |b| @battle.pbCanChooseNonActive?(b.index) } - score -= 90 # Opponent can't switch in any Pokemon - else - score += 8 * @battle.pbAbleNonActiveCount(user.idxOpposingSide) - score += [26, 13][user.pbOpposingSide.effects[PBEffects::ToxicSpikes]] - end - #--------------------------------------------------------------------------- - when "AddStealthRocksToFoeSide" - if user.pbOpposingSide.effects[PBEffects::StealthRock] - score -= 90 - elsif user.allOpposing.none? { |b| @battle.pbCanChooseNonActive?(b.index) } - score -= 90 # Opponent can't switch in any Pokemon - else - score += 10 * @battle.pbAbleNonActiveCount(user.idxOpposingSide) - end - #--------------------------------------------------------------------------- - when "AddStickyWebToFoeSide" - score -= 95 if user.pbOpposingSide.effects[PBEffects::StickyWeb] - #--------------------------------------------------------------------------- - when "SwapSideEffects" - if skill >= PBTrainerAI.mediumSkill - good_effects = [:Reflect, :LightScreen, :AuroraVeil, :SeaOfFire, - :Swamp, :Rainbow, :Mist, :Safeguard, - :Tailwind].map! { |e| PBEffects.const_get(e) } - bad_effects = [:Spikes, :StickyWeb, :ToxicSpikes, :StealthRock].map! { |e| PBEffects.const_get(e) } - bad_effects.each do |e| - score += 10 if ![0, false, nil].include?(user.pbOwnSide.effects[e]) - score -= 10 if ![0, 1, false, nil].include?(user.pbOpposingSide.effects[e]) - end - if skill >= PBTrainerAI.highSkill - good_effects.each do |e| - score += 10 if ![0, 1, false, nil].include?(user.pbOpposingSide.effects[e]) - score -= 10 if ![0, false, nil].include?(user.pbOwnSide.effects[e]) - end - end - end - #--------------------------------------------------------------------------- - when "UserMakeSubstitute" - if user.effects[PBEffects::Substitute] > 0 - score -= 90 - elsif user.hp <= user.totalhp / 4 - score -= 90 - end - #--------------------------------------------------------------------------- - when "RemoveUserBindingAndEntryHazards" - score += 30 if user.effects[PBEffects::Trapping] > 0 - score += 30 if user.effects[PBEffects::LeechSeed] >= 0 - if @battle.pbAbleNonActiveCount(user.idxOwnSide) > 0 - score += 80 if user.pbOwnSide.effects[PBEffects::Spikes] > 0 - score += 80 if user.pbOwnSide.effects[PBEffects::ToxicSpikes] > 0 - score += 80 if user.pbOwnSide.effects[PBEffects::StealthRock] - end - #--------------------------------------------------------------------------- - when "AttackTwoTurnsLater" - if @battle.positions[target.index].effects[PBEffects::FutureSightCounter] > 0 - score -= 100 - elsif @battle.pbAbleNonActiveCount(user.idxOwnSide) == 0 - # Future Sight tends to be wasteful if down to last Pokemon - score -= 70 - end - #--------------------------------------------------------------------------- - when "UserSwapsPositionsWithAlly" - #--------------------------------------------------------------------------- - when "BurnAttackerBeforeUserActs" - score += 20 # Because of possible burning - #--------------------------------------------------------------------------- - when "RaiseUserAttack1" - if move.statusMove? - if user.statStageAtMax?(:ATTACK) - score -= 90 - else - score -= user.stages[:ATTACK] * 20 - if skill >= PBTrainerAI.mediumSkill - hasPhysicalAttack = false - user.eachMove do |m| - next if !m.physicalMove?(m.type) - hasPhysicalAttack = true - break - end - if hasPhysicalAttack - score += 20 - elsif skill >= PBTrainerAI.highSkill - score -= 90 - end - end - end - else - score += 20 if user.stages[:ATTACK] < 0 - if skill >= PBTrainerAI.mediumSkill - hasPhysicalAttack = false - user.eachMove do |m| - next if !m.physicalMove?(m.type) - hasPhysicalAttack = true - break - end - score += 20 if hasPhysicalAttack - end - end - #--------------------------------------------------------------------------- - when "RaiseUserAttack2", "RaiseUserAttack2IfTargetFaints", - "RaiseUserAttack3", "RaiseUserAttack3IfTargetFaints" - if move.statusMove? - if user.statStageAtMax?(:ATTACK) - score -= 90 - else - score += 40 if user.turnCount == 0 - score -= user.stages[:ATTACK] * 20 - if skill >= PBTrainerAI.mediumSkill - hasPhysicalAttack = false - user.eachMove do |m| - next if !m.physicalMove?(m.type) - hasPhysicalAttack = true - break - end - if hasPhysicalAttack - score += 20 - elsif skill >= PBTrainerAI.highSkill - score -= 90 - end - end - end - else - score += 10 if user.turnCount == 0 - score += 20 if user.stages[:ATTACK] < 0 - if skill >= PBTrainerAI.mediumSkill - hasPhysicalAttack = false - user.eachMove do |m| - next if !m.physicalMove?(m.type) - hasPhysicalAttack = true - break - end - score += 20 if hasPhysicalAttack - end - end - #--------------------------------------------------------------------------- - when "MaxUserAttackLoseHalfOfTotalHP" - if user.statStageAtMax?(:ATTACK) || - user.hp <= user.totalhp / 2 - score -= 100 - else - score += (6 - user.stages[:ATTACK]) * 10 - if skill >= PBTrainerAI.mediumSkill - hasPhysicalAttack = false - user.eachMove do |m| - next if !m.physicalMove?(m.type) - hasPhysicalAttack = true - break - end - if hasPhysicalAttack - score += 40 - elsif skill >= PBTrainerAI.highSkill - score -= 90 - end - end - end - #--------------------------------------------------------------------------- - when "RaiseUserDefense1", "RaiseUserDefense1CurlUpUser" - if move.statusMove? - if user.statStageAtMax?(:DEFENSE) - score -= 90 - else - score -= user.stages[:DEFENSE] * 20 - end - elsif user.stages[:DEFENSE] < 0 - score += 20 - end - #--------------------------------------------------------------------------- - when "RaiseUserDefense2" - if move.statusMove? - if user.statStageAtMax?(:DEFENSE) - score -= 90 - else - score += 40 if user.turnCount == 0 - score -= user.stages[:DEFENSE] * 20 - end - else - score += 10 if user.turnCount == 0 - score += 20 if user.stages[:DEFENSE] < 0 - end - #--------------------------------------------------------------------------- - when "RaiseUserDefense3" - if move.statusMove? - if user.statStageAtMax?(:DEFENSE) - score -= 90 - else - score += 40 if user.turnCount == 0 - score -= user.stages[:DEFENSE] * 30 - end - else - score += 10 if user.turnCount == 0 - score += 30 if user.stages[:DEFENSE] < 0 - end - #--------------------------------------------------------------------------- - when "RaiseUserSpAtk1" - if move.statusMove? - if user.statStageAtMax?(:SPECIAL_ATTACK) - score -= 90 - else - score -= user.stages[:SPECIAL_ATTACK] * 20 - if skill >= PBTrainerAI.mediumSkill - hasSpecicalAttack = false - user.eachMove do |m| - next if !m.specialMove?(m.type) - hasSpecicalAttack = true - break - end - if hasSpecicalAttack - score += 20 - elsif skill >= PBTrainerAI.highSkill - score -= 90 - end - end - end - else - score += 20 if user.stages[:SPECIAL_ATTACK] < 0 - if skill >= PBTrainerAI.mediumSkill - hasSpecicalAttack = false - user.eachMove do |m| - next if !m.specialMove?(m.type) - hasSpecicalAttack = true - break - end - score += 20 if hasSpecicalAttack - end - end - #--------------------------------------------------------------------------- - when "RaiseUserSpAtk2" - if move.statusMove? - if user.statStageAtMax?(:SPECIAL_ATTACK) - score -= 90 - else - score += 40 if user.turnCount == 0 - score -= user.stages[:SPECIAL_ATTACK] * 20 - if skill >= PBTrainerAI.mediumSkill - hasSpecicalAttack = false - user.eachMove do |m| - next if !m.specialMove?(m.type) - hasSpecicalAttack = true - break - end - if hasSpecicalAttack - score += 20 - elsif skill >= PBTrainerAI.highSkill - score -= 90 - end - end - end - else - score += 10 if user.turnCount == 0 - score += 20 if user.stages[:SPECIAL_ATTACK] < 0 - if skill >= PBTrainerAI.mediumSkill - hasSpecicalAttack = false - user.eachMove do |m| - next if !m.specialMove?(m.type) - hasSpecicalAttack = true - break - end - score += 20 if hasSpecicalAttack - end - end - #--------------------------------------------------------------------------- - when "RaiseUserSpAtk3" - if move.statusMove? - if user.statStageAtMax?(:SPECIAL_ATTACK) - score -= 90 - else - score += 40 if user.turnCount == 0 - score -= user.stages[:SPECIAL_ATTACK] * 30 - if skill >= PBTrainerAI.mediumSkill - hasSpecicalAttack = false - user.eachMove do |m| - next if !m.specialMove?(m.type) - hasSpecicalAttack = true - break - end - if hasSpecicalAttack - score += 20 - elsif skill >= PBTrainerAI.highSkill - score -= 90 - end - end - end - else - score += 10 if user.turnCount == 0 - score += 30 if user.stages[:SPECIAL_ATTACK] < 0 - if skill >= PBTrainerAI.mediumSkill - hasSpecicalAttack = false - user.eachMove do |m| - next if !m.specialMove?(m.type) - hasSpecicalAttack = true - break - end - score += 30 if hasSpecicalAttack - end - end - #--------------------------------------------------------------------------- - when "RaiseUserSpDef1", "RaiseUserSpDef2", "RaiseUserSpDef3" - if move.statusMove? - if user.statStageAtMax?(:SPECIAL_DEFENSE) - score -= 90 - else - score += 40 if user.turnCount == 0 - score -= user.stages[:SPECIAL_DEFENSE] * 20 - end - else - score += 10 if user.turnCount == 0 - score += 20 if user.stages[:SPECIAL_DEFENSE] < 0 - end - #--------------------------------------------------------------------------- - when "RaiseUserSpDef1PowerUpElectricMove" - foundMove = false - user.eachMove do |m| - next if m.type != :ELECTRIC || !m.damagingMove? - foundMove = true - break - end - score += 20 if foundMove - if move.statusMove? - if user.statStageAtMax?(:SPECIAL_DEFENSE) - score -= 90 - else - score -= user.stages[:SPECIAL_DEFENSE] * 20 - end - elsif user.stages[:SPECIAL_DEFENSE] < 0 - score += 20 - end - #--------------------------------------------------------------------------- - when "RaiseUserSpeed1" - if move.statusMove? - if user.statStageAtMax?(:SPEED) - score -= 90 - else - score -= user.stages[:SPEED] * 10 - if skill >= PBTrainerAI.highSkill - aspeed = pbRoughStat(user, :SPEED, skill) - ospeed = pbRoughStat(target, :SPEED, skill) - score += 30 if aspeed < ospeed && aspeed * 2 > ospeed - end - end - elsif user.stages[:SPEED] < 0 - score += 20 - end - #--------------------------------------------------------------------------- - when "RaiseUserSpeed2", "RaiseUserSpeed2LowerUserWeight", "RaiseUserSpeed3" - if move.statusMove? - if user.statStageAtMax?(:SPEED) - score -= 90 - else - score += 20 if user.turnCount == 0 - score -= user.stages[:SPEED] * 10 - if skill >= PBTrainerAI.highSkill - aspeed = pbRoughStat(user, :SPEED, skill) - ospeed = pbRoughStat(target, :SPEED, skill) - score += 30 if aspeed < ospeed && aspeed * 2 > ospeed - end - end - else - score += 10 if user.turnCount == 0 - score += 20 if user.stages[:SPEED] < 0 - end - #--------------------------------------------------------------------------- - when "RaiseUserAccuracy1", "RaiseUserAccuracy2", "RaiseUserAccuracy3" - if move.statusMove? - if user.statStageAtMax?(:ACCURACY) - score -= 90 - else - score += 40 if user.turnCount == 0 - score -= user.stages[:ACCURACY] * 20 - end - else - score += 10 if user.turnCount == 0 - score += 20 if user.stages[:ACCURACY] < 0 - end - #--------------------------------------------------------------------------- - when "RaiseUserEvasion1" - if move.statusMove? - if user.statStageAtMax?(:EVASION) - score -= 90 - else - score -= user.stages[:EVASION] * 10 - end - elsif user.stages[:EVASION] < 0 - score += 20 - end - #--------------------------------------------------------------------------- - when "RaiseUserEvasion2", "RaiseUserEvasion2MinimizeUser", "RaiseUserEvasion3" - if move.statusMove? - if user.statStageAtMax?(:EVASION) - score -= 90 - else - score += 40 if user.turnCount == 0 - score -= user.stages[:EVASION] * 10 - end - else - score += 10 if user.turnCount == 0 - score += 20 if user.stages[:EVASION] < 0 - end - #--------------------------------------------------------------------------- - when "RaiseUserCriticalHitRate2" - if move.statusMove? - if user.effects[PBEffects::FocusEnergy] >= 2 - score -= 80 - else - score += 30 - end - elsif user.effects[PBEffects::FocusEnergy] < 2 - score += 30 - end - #--------------------------------------------------------------------------- - when "RaiseUserAtkDef1" - if user.statStageAtMax?(:ATTACK) && - user.statStageAtMax?(:DEFENSE) - score -= 90 - else - score -= user.stages[:ATTACK] * 10 - score -= user.stages[:DEFENSE] * 10 - if skill >= PBTrainerAI.mediumSkill - hasPhysicalAttack = false - user.eachMove do |m| - next if !m.physicalMove?(m.type) - hasPhysicalAttack = true - break - end - if hasPhysicalAttack - score += 20 - elsif skill >= PBTrainerAI.highSkill - score -= 90 - end - end - end - #--------------------------------------------------------------------------- - when "RaiseUserAtkDefAcc1" - if user.statStageAtMax?(:ATTACK) && - user.statStageAtMax?(:DEFENSE) && - user.statStageAtMax?(:ACCURACY) - score -= 90 - else - score -= user.stages[:ATTACK] * 10 - score -= user.stages[:DEFENSE] * 10 - score -= user.stages[:ACCURACY] * 10 - if skill >= PBTrainerAI.mediumSkill - hasPhysicalAttack = false - user.eachMove do |m| - next if !m.physicalMove?(m.type) - hasPhysicalAttack = true - break - end - if hasPhysicalAttack - score += 20 - elsif skill >= PBTrainerAI.highSkill - score -= 90 - end - end - end - #--------------------------------------------------------------------------- - when "RaiseUserAtkSpAtk1", "RaiseUserAtkSpAtk1Or2InSun" - if user.statStageAtMax?(:ATTACK) && - user.statStageAtMax?(:SPECIAL_ATTACK) - score -= 90 - else - score -= user.stages[:ATTACK] * 10 - score -= user.stages[:SPECIAL_ATTACK] * 10 - if skill >= PBTrainerAI.mediumSkill - hasDamagingAttack = false - user.eachMove do |m| - next if !m.damagingMove? - hasDamagingAttack = true - break - end - if hasDamagingAttack - score += 20 - elsif skill >= PBTrainerAI.highSkill - score -= 90 - end - end - if move.function == "RaiseUserAtkSpAtk1Or2InSun" # Growth - score += 20 if [:Sun, :HarshSun].include?(user.effectiveWeather) - end - end - #--------------------------------------------------------------------------- - when "LowerUserDefSpDef1RaiseUserAtkSpAtkSpd2" - score -= user.stages[:ATTACK] * 20 - score -= user.stages[:SPEED] * 20 - score -= user.stages[:SPECIAL_ATTACK] * 20 - score += user.stages[:DEFENSE] * 10 - score += user.stages[:SPECIAL_DEFENSE] * 10 - if skill >= PBTrainerAI.mediumSkill - hasDamagingAttack = false - user.eachMove do |m| - next if !m.damagingMove? - hasDamagingAttack = true - break - end - score += 20 if hasDamagingAttack - end - #--------------------------------------------------------------------------- - when "RaiseUserAtkSpd1" - score += 40 if user.turnCount == 0 # Dragon Dance tends to be popular - if user.statStageAtMax?(:ATTACK) && - user.statStageAtMax?(:SPEED) - score -= 90 - else - score -= user.stages[:ATTACK] * 10 - score -= user.stages[:SPEED] * 10 - if skill >= PBTrainerAI.mediumSkill - hasPhysicalAttack = false - user.eachMove do |m| - next if !m.physicalMove?(m.type) - hasPhysicalAttack = true - break - end - if hasPhysicalAttack - score += 20 - elsif skill >= PBTrainerAI.highSkill - score -= 90 - end - end - if skill >= PBTrainerAI.highSkill - aspeed = pbRoughStat(user, :SPEED, skill) - ospeed = pbRoughStat(target, :SPEED, skill) - score += 20 if aspeed < ospeed && aspeed * 2 > ospeed - end - end - #--------------------------------------------------------------------------- - when "RaiseUserAtk1Spd2" - if user.statStageAtMax?(:ATTACK) && - user.statStageAtMax?(:SPEED) - score -= 90 - else - score -= user.stages[:ATTACK] * 10 - score -= user.stages[:SPEED] * 10 - if skill >= PBTrainerAI.mediumSkill - hasPhysicalAttack = false - user.eachMove do |m| - next if !m.physicalMove?(m.type) - hasPhysicalAttack = true - break - end - if hasPhysicalAttack - score += 20 - elsif skill >= PBTrainerAI.highSkill - score -= 90 - end - end - if skill >= PBTrainerAI.highSkill - aspeed = pbRoughStat(user, :SPEED, skill) - ospeed = pbRoughStat(target, :SPEED, skill) - score += 30 if aspeed < ospeed && aspeed * 2 > ospeed - end - end - #--------------------------------------------------------------------------- - when "RaiseUserAtkAcc1" - if user.statStageAtMax?(:ATTACK) && - user.statStageAtMax?(:ACCURACY) - score -= 90 - else - score -= user.stages[:ATTACK] * 10 - score -= user.stages[:ACCURACY] * 10 - if skill >= PBTrainerAI.mediumSkill - hasPhysicalAttack = false - user.eachMove do |m| - next if !m.physicalMove?(m.type) - hasPhysicalAttack = true - break - end - if hasPhysicalAttack - score += 20 - elsif skill >= PBTrainerAI.highSkill - score -= 90 - end - end - end - #--------------------------------------------------------------------------- - when "RaiseUserDefSpDef1" - if user.statStageAtMax?(:DEFENSE) && - user.statStageAtMax?(:SPECIAL_DEFENSE) - score -= 90 - else - score -= user.stages[:DEFENSE] * 10 - score -= user.stages[:SPECIAL_DEFENSE] * 10 - end - #--------------------------------------------------------------------------- - when "RaiseUserSpAtkSpDef1" - if user.statStageAtMax?(:SPECIAL_ATTACK) && - user.statStageAtMax?(:SPECIAL_DEFENSE) - score -= 90 - else - score += 40 if user.turnCount == 0 # Calm Mind tends to be popular - score -= user.stages[:SPECIAL_ATTACK] * 10 - score -= user.stages[:SPECIAL_DEFENSE] * 10 - if skill >= PBTrainerAI.mediumSkill - hasSpecicalAttack = false - user.eachMove do |m| - next if !m.specialMove?(m.type) - hasSpecicalAttack = true - break - end - if hasSpecicalAttack - score += 20 - elsif skill >= PBTrainerAI.highSkill - score -= 90 - end - end - end - #--------------------------------------------------------------------------- - when "RaiseUserSpAtkSpDefSpd1" - if user.statStageAtMax?(:SPEED) && - user.statStageAtMax?(:SPECIAL_ATTACK) && - user.statStageAtMax?(:SPECIAL_DEFENSE) - score -= 90 - else - score -= user.stages[:SPECIAL_ATTACK] * 10 - score -= user.stages[:SPECIAL_DEFENSE] * 10 - score -= user.stages[:SPEED] * 10 - if skill >= PBTrainerAI.mediumSkill - hasSpecicalAttack = false - user.eachMove do |m| - next if !m.specialMove?(m.type) - hasSpecicalAttack = true - break - end - if hasSpecicalAttack - score += 20 - elsif skill >= PBTrainerAI.highSkill - score -= 90 - end - end - if skill >= PBTrainerAI.highSkill - aspeed = pbRoughStat(user, :SPEED, skill) - ospeed = pbRoughStat(target, :SPEED, skill) - if aspeed < ospeed && aspeed * 2 > ospeed - score += 20 - end - end - end - #--------------------------------------------------------------------------- - when "RaiseUserMainStats1" - GameData::Stat.each_main_battle { |s| score += 10 if user.stages[s.id] < 0 } - if skill >= PBTrainerAI.mediumSkill - hasDamagingAttack = false - user.eachMove do |m| - next if !m.damagingMove? - hasDamagingAttack = true - break - end - score += 20 if hasDamagingAttack - end - #--------------------------------------------------------------------------- - when "RaiseUserMainStats1LoseThirdOfTotalHP" - if user.hp <= user.totalhp / 2 - score -= 100 - elsif user.hasActiveAbility?(:CONTRARY) - score -= 100 - else - stats_maxed = true - GameData::Stat.each_main_battle do |s| - next if user.statStageAtMax?(s.id) - stats_maxed = false - break - end - if stats_maxed - score -= 100 - else - if skill >= PBTrainerAI.highSkill && user.hp >= user.totalhp * 0.75 - score += 30 - end - GameData::Stat.each_main_battle { |s| score += 10 if user.stages[s.id] <= 0 } - if skill >= PBTrainerAI.mediumSkill - hasDamagingAttack = user.moves.any? { |m| next m&.damagingMove? } - score += 20 if hasDamagingAttack - end - end - end - #--------------------------------------------------------------------------- - when "RaiseUserMainStats1TrapUserInBattle" - if user.effects[PBEffects::NoRetreat] - score -= 100 - elsif user.hasActiveAbility?(:CONTRARY) - score -= 100 - else - stats_maxed = true - GameData::Stat.each_main_battle do |s| - next if user.statStageAtMax?(s.id) - stats_maxed = false - break - end - if stats_maxed - score -= 100 - else - if skill >= PBTrainerAI.highSkill - score -= 50 if user.hp <= user.totalhp / 2 - score += 30 if user.trappedInBattle? - end - GameData::Stat.each_main_battle { |s| score += 10 if user.stages[s.id] <= 0 } - if skill >= PBTrainerAI.mediumSkill - hasDamagingAttack = user.moves.any? { |m| next m&.damagingMove? } - score += 20 if hasDamagingAttack - end - end - end - #--------------------------------------------------------------------------- - when "StartRaiseUserAtk1WhenDamaged" - score += 25 if user.effects[PBEffects::Rage] - #--------------------------------------------------------------------------- - when "LowerUserAttack1", "LowerUserAttack2" - score += user.stages[:ATTACK] * 10 - #--------------------------------------------------------------------------- - when "LowerUserDefense1", "LowerUserDefense2" - score += user.stages[:DEFENSE] * 10 - #--------------------------------------------------------------------------- - when "LowerUserSpAtk1", "LowerUserSpAtk2" - score += user.stages[:SPECIAL_ATTACK] * 10 - #--------------------------------------------------------------------------- - when "LowerUserSpDef1", "LowerUserSpDef2" - score += user.stages[:SPECIAL_DEFENSE] * 10 - #--------------------------------------------------------------------------- - when "LowerUserSpeed1", "LowerUserSpeed2" - score += user.stages[:SPEED] * 10 - #--------------------------------------------------------------------------- - when "LowerUserAtkDef1" - avg = user.stages[:ATTACK] * 10 - avg += user.stages[:DEFENSE] * 10 - score += avg / 2 - #--------------------------------------------------------------------------- - when "LowerUserDefSpDef1" - avg = user.stages[:DEFENSE] * 10 - avg += user.stages[:SPECIAL_DEFENSE] * 10 - score += avg / 2 - #--------------------------------------------------------------------------- - when "LowerUserDefSpDefSpd1" - avg = user.stages[:DEFENSE] * 10 - avg += user.stages[:SPEED] * 10 - avg += user.stages[:SPECIAL_DEFENSE] * 10 - score += (avg / 3).floor - #--------------------------------------------------------------------------- - when "RaiseTargetAttack1" - #--------------------------------------------------------------------------- - when "RaiseTargetAttack2ConfuseTarget" - if !target.pbCanConfuse?(user, false) - score -= 90 - elsif target.stages[:ATTACK] < 0 - score += 30 - end - #--------------------------------------------------------------------------- - when "RaiseTargetSpAtk1ConfuseTarget" - if !target.pbCanConfuse?(user, false) - score -= 90 - elsif target.stages[:SPECIAL_ATTACK] < 0 - score += 30 - end - #--------------------------------------------------------------------------- - when "RaiseTargetSpDef1" - if target.statStageAtMax?(:SPECIAL_DEFENSE) - score -= 90 - else - score -= target.stages[:SPECIAL_DEFENSE] * 10 - end - #--------------------------------------------------------------------------- - when "RaiseTargetRandomStat2" - avgStat = 0 - canChangeStat = false - GameData::Stat.each_battle do |s| - next if target.statStageAtMax?(s.id) - avgStat -= target.stages[s.id] - canChangeStat = true - end - if canChangeStat - avgStat = avgStat / 2 if avgStat < 0 # More chance of getting even better - score += avgStat * 10 - else - score -= 90 - end - #--------------------------------------------------------------------------- - when "RaiseTargetAtkSpAtk2" - if target.opposes?(user) - score -= 100 - elsif skill >= PBTrainerAI.mediumSkill && target.hasActiveAbility?(:CONTRARY) - score -= 90 - else - score -= target.stages[:ATTACK] * 20 - score -= target.stages[:SPECIAL_ATTACK] * 20 - end - #--------------------------------------------------------------------------- - when "LowerTargetAttack1" - if move.statusMove? - if target.pbCanLowerStatStage?(:ATTACK, user) - score += target.stages[:ATTACK] * 20 - if skill >= PBTrainerAI.mediumSkill - hasPhysicalAttack = false - target.eachMove do |m| - next if !m.physicalMove?(m.type) - hasPhysicalAttack = true - break - end - if hasPhysicalAttack - score += 20 - elsif skill >= PBTrainerAI.highSkill - score -= 90 - end - end - else - score -= 90 - end - else - score += 20 if target.stages[:ATTACK] > 0 - if skill >= PBTrainerAI.mediumSkill - hasPhysicalAttack = false - target.eachMove do |m| - next if !m.physicalMove?(m.type) - hasPhysicalAttack = true - break - end - score += 20 if hasPhysicalAttack - end - end - #--------------------------------------------------------------------------- - when "LowerTargetAttack1BypassSubstitute" - if target.pbCanLowerStatStage?(:ATTACK, user) - score += target.stages[:ATTACK] * 20 - if skill >= PBTrainerAI.mediumSkill - hasPhysicalAttack = false - target.eachMove do |m| - next if !m.physicalMove?(m.type) - hasPhysicalAttack = true - break - end - if hasPhysicalAttack - score += 20 - elsif skill >= PBTrainerAI.highSkill - score -= 90 - end - end - else - score -= 90 - end - #--------------------------------------------------------------------------- - when "LowerTargetAttack2", "LowerTargetAttack3" - if move.statusMove? - if target.pbCanLowerStatStage?(:ATTACK, user) - score += 40 if user.turnCount == 0 - score += target.stages[:ATTACK] * 20 - if skill >= PBTrainerAI.mediumSkill - hasPhysicalAttack = false - target.eachMove do |m| - next if !m.physicalMove?(m.type) - hasPhysicalAttack = true - break - end - if hasPhysicalAttack - score += 20 - elsif skill >= PBTrainerAI.highSkill - score -= 90 - end - end - else - score -= 90 - end - else - score += 10 if user.turnCount == 0 - score += 20 if target.stages[:ATTACK] > 0 - if skill >= PBTrainerAI.mediumSkill - hasPhysicalAttack = false - target.eachMove do |m| - next if !m.physicalMove?(m.type) - hasPhysicalAttack = true - break - end - score += 20 if hasPhysicalAttack - end - end - #--------------------------------------------------------------------------- - when "LowerTargetDefense1" - if move.statusMove? - if target.pbCanLowerStatStage?(:DEFENSE, user) - score += target.stages[:DEFENSE] * 20 - else - score -= 90 - end - elsif target.stages[:DEFENSE] > 0 - score += 20 - end - #--------------------------------------------------------------------------- - when "LowerTargetDefense1PowersUpInGravity" - if target.pbCanLowerStatStage?(:DEFENSE, user) - score += 20 - score += target.stages[:DEFENSE] * 20 - else - score -= 90 - end - score += 30 if @battle.field.effects[PBEffects::Gravity] > 0 - #--------------------------------------------------------------------------- - when "LowerTargetDefense2", "LowerTargetDefense3" - if move.statusMove? - if target.pbCanLowerStatStage?(:DEFENSE, user) - score += 40 if user.turnCount == 0 - score += target.stages[:DEFENSE] * 20 - else - score -= 90 - end - else - score += 10 if user.turnCount == 0 - score += 20 if target.stages[:DEFENSE] > 0 - end - #--------------------------------------------------------------------------- - when "LowerTargetSpAtk1" - if move.statusMove? - if target.pbCanLowerStatStage?(:SPECIAL_ATTACK, user) - score += user.stages[:SPECIAL_ATTACK] * 20 - if skill >= PBTrainerAI.mediumSkill - hasSpecicalAttack = false - target.eachMove do |m| - next if !m.specialMove?(m.type) - hasSpecicalAttack = true - break - end - if hasSpecicalAttack - score += 20 - elsif skill >= PBTrainerAI.highSkill - score -= 90 - end - end - else - score -= 90 - end - else - score += 20 if user.stages[:SPECIAL_ATTACK] > 0 - if skill >= PBTrainerAI.mediumSkill - hasSpecicalAttack = false - target.eachMove do |m| - next if !m.specialMove?(m.type) - hasSpecicalAttack = true - break - end - score += 20 if hasSpecicalAttack - end - end - #--------------------------------------------------------------------------- - when "LowerTargetSpAtk2" - if target.pbCanLowerStatStage?(:SPECIAL_ATTACK, user) - score += 40 if user.turnCount == 0 - score += target.stages[:SPECIAL_ATTACK] * 20 - else - score -= 90 - end - #--------------------------------------------------------------------------- - when "LowerTargetSpAtk2IfCanAttract" - if user.gender == 2 || target.gender == 2 || user.gender == target.gender || - target.hasActiveAbility?(:OBLIVIOUS) - score -= 90 - elsif move.statusMove? - if target.pbCanLowerStatStage?(:SPECIAL_ATTACK, user) - score += 40 if user.turnCount == 0 - score += target.stages[:SPECIAL_ATTACK] * 20 - if skill >= PBTrainerAI.mediumSkill - hasSpecicalAttack = false - target.eachMove do |m| - next if !m.specialMove?(m.type) - hasSpecicalAttack = true - break - end - if hasSpecicalAttack - score += 20 - elsif skill >= PBTrainerAI.highSkill - score -= 90 - end - end - else - score -= 90 - end - else - score += 10 if user.turnCount == 0 - score += 20 if target.stages[:SPECIAL_ATTACK] > 0 - if skill >= PBTrainerAI.mediumSkill - hasSpecicalAttack = false - target.eachMove do |m| - next if !m.specialMove?(m.type) - hasSpecicalAttack = true - break - end - score += 30 if hasSpecicalAttack - end - end - #--------------------------------------------------------------------------- - when "LowerTargetSpAtk3" - if target.pbCanLowerStatStage?(:SPECIAL_ATTACK, user) - score += 40 if user.turnCount == 0 - score += target.stages[:SPECIAL_ATTACK] * 20 - else - score -= 90 - end - #--------------------------------------------------------------------------- - when "LowerTargetSpDef1" - if move.statusMove? - if target.pbCanLowerStatStage?(:SPECIAL_DEFENSE, user) - score += target.stages[:SPECIAL_DEFENSE] * 20 - else - score -= 90 - end - elsif target.stages[:SPECIAL_DEFENSE] > 0 - score += 20 - end - #--------------------------------------------------------------------------- - when "LowerTargetSpDef2", "LowerTargetSpDef3" - if move.statusMove? - if target.pbCanLowerStatStage?(:SPECIAL_DEFENSE, user) - score += 40 if user.turnCount == 0 - score += target.stages[:SPECIAL_DEFENSE] * 20 - else - score -= 90 - end - else - score += 10 if user.turnCount == 0 - score += 20 if target.stages[:SPECIAL_DEFENSE] > 0 - end - #--------------------------------------------------------------------------- - when "LowerTargetSpeed1", "LowerTargetSpeed1WeakerInGrassyTerrain" - if move.statusMove? - if target.pbCanLowerStatStage?(:SPEED, user) - score += target.stages[:SPEED] * 10 - if skill >= PBTrainerAI.highSkill - aspeed = pbRoughStat(user, :SPEED, skill) - ospeed = pbRoughStat(target, :SPEED, skill) - score += 30 if aspeed < ospeed && aspeed * 2 > ospeed - end - else - score -= 90 - end - elsif user.stages[:SPEED] > 0 - score += 20 - end - #--------------------------------------------------------------------------- - when "LowerTargetSpeed1MakeTargetWeakerToFire" - if !target.pbCanLowerStatStage?(:SPEED, user) && target.effects[PBEffects::TarShot] - score -= 100 - else - score += target.stages[:SPEED] * 10 - if skill >= PBTrainerAI.highSkill - aspeed = pbRoughStat(user, :SPEED, skill) - ospeed = pbRoughStat(target, :SPEED, skill) - score += 50 if aspeed < ospeed && aspeed * 2 > ospeed - end - end - score += 20 if user.moves.any? { |m| m.damagingMove? && m.pbCalcType(user) == :FIRE } - #--------------------------------------------------------------------------- - when "LowerTargetSpeed2", "LowerTargetSpeed3" - if move.statusMove? - if target.pbCanLowerStatStage?(:SPEED, user) - score += 20 if user.turnCount == 0 - score += target.stages[:SPEED] * 20 - if skill >= PBTrainerAI.highSkill - aspeed = pbRoughStat(user, :SPEED, skill) - ospeed = pbRoughStat(target, :SPEED, skill) - score += 30 if aspeed < ospeed && aspeed * 2 > ospeed - end - else - score -= 90 - end - else - score += 10 if user.turnCount == 0 - score += 30 if target.stages[:SPEED] > 0 - end - #--------------------------------------------------------------------------- - when "LowerTargetAccuracy1", "LowerTargetAccuracy2", "LowerTargetAccuracy3" - if move.statusMove? - if target.pbCanLowerStatStage?(:ACCURACY, user) - score += target.stages[:ACCURACY] * 10 - else - score -= 90 - end - elsif target.stages[:ACCURACY] > 0 - score += 20 - end - #--------------------------------------------------------------------------- - when "LowerTargetEvasion1" - if move.statusMove? - if target.pbCanLowerStatStage?(:EVASION, user) - score += target.stages[:EVASION] * 10 - else - score -= 90 - end - elsif target.stages[:EVASION] > 0 - score += 20 - end - #--------------------------------------------------------------------------- - when "LowerTargetEvasion1RemoveSideEffects" - if move.statusMove? - if target.pbCanLowerStatStage?(:EVASION, user) - score += target.stages[:EVASION] * 10 - else - score -= 90 - end - elsif target.stages[:EVASION] > 0 - score += 20 - end - score += 30 if target.pbOwnSide.effects[PBEffects::AuroraVeil] > 0 || - target.pbOwnSide.effects[PBEffects::Reflect] > 0 || - target.pbOwnSide.effects[PBEffects::LightScreen] > 0 || - target.pbOwnSide.effects[PBEffects::Mist] > 0 || - target.pbOwnSide.effects[PBEffects::Safeguard] > 0 - score -= 30 if target.pbOwnSide.effects[PBEffects::Spikes] > 0 || - target.pbOwnSide.effects[PBEffects::ToxicSpikes] > 0 || - target.pbOwnSide.effects[PBEffects::StealthRock] - #--------------------------------------------------------------------------- - when "LowerTargetEvasion2", "LowerTargetEvasion3" - if move.statusMove? - if target.pbCanLowerStatStage?(:EVASION, user) - score += target.stages[:EVASION] * 10 - else - score -= 90 - end - elsif target.stages[:EVASION] > 0 - score += 20 - end - #--------------------------------------------------------------------------- - when "LowerTargetAtkDef1" - avg = target.stages[:ATTACK] * 10 - avg += target.stages[:DEFENSE] * 10 - score += avg / 2 - #--------------------------------------------------------------------------- - when "LowerTargetAtkSpAtk1" - avg = target.stages[:ATTACK] * 10 - avg += target.stages[:SPECIAL_ATTACK] * 10 - score += avg / 2 - #--------------------------------------------------------------------------- - when "LowerPoisonedTargetAtkSpAtkSpd1" - count = 0 - @battle.allBattlers.each do |b| - if b.poisoned? && - (!b.statStageAtMin?(:ATTACK) || - !b.statStageAtMin?(:SPECIAL_ATTACK) || - !b.statStageAtMin?(:SPEED)) - count += 1 - if user.opposes?(b) - score += user.stages[:ATTACK] * 10 - score += user.stages[:SPECIAL_ATTACK] * 10 - score += user.stages[:SPEED] * 10 - else - score -= 20 - end - end - end - score -= 95 if count == 0 - #--------------------------------------------------------------------------- - when "RaiseUserAndAlliesAtkDef1" - has_ally = false - user.allAllies.each do |b| - next if !b.pbCanLowerStatStage?(:ATTACK, user) && - !b.pbCanLowerStatStage?(:SPECIAL_ATTACK, user) - has_ally = true - if skill >= PBTrainerAI.mediumSkill && b.hasActiveAbility?(:CONTRARY) - score -= 90 - else - score += 40 - score -= b.stages[:ATTACK] * 20 - score -= b.stages[:SPECIAL_ATTACK] * 20 - end - end - score = 0 if !has_ally - #--------------------------------------------------------------------------- - when "RaisePlusMinusUserAndAlliesAtkSpAtk1" - hasEffect = user.statStageAtMax?(:ATTACK) && - user.statStageAtMax?(:SPECIAL_ATTACK) - user.allAllies.each do |b| - next if b.statStageAtMax?(:ATTACK) && b.statStageAtMax?(:SPECIAL_ATTACK) - hasEffect = true - score -= b.stages[:ATTACK] * 10 - score -= b.stages[:SPECIAL_ATTACK] * 10 - end - if hasEffect - score -= user.stages[:ATTACK] * 10 - score -= user.stages[:SPECIAL_ATTACK] * 10 - else - score -= 90 - end - #--------------------------------------------------------------------------- - when "RaisePlusMinusUserAndAlliesDefSpDef1" - hasEffect = user.statStageAtMax?(:DEFENSE) && - user.statStageAtMax?(:SPECIAL_DEFENSE) - user.allAllies.each do |b| - next if b.statStageAtMax?(:DEFENSE) && b.statStageAtMax?(:SPECIAL_DEFENSE) - hasEffect = true - score -= b.stages[:DEFENSE] * 10 - score -= b.stages[:SPECIAL_DEFENSE] * 10 - end - if hasEffect - score -= user.stages[:DEFENSE] * 10 - score -= user.stages[:SPECIAL_DEFENSE] * 10 - else - score -= 90 - end - #--------------------------------------------------------------------------- - when "RaiseGroundedGrassBattlersAtkSpAtk1" - count = 0 - @battle.allBattlers.each do |b| - if b.pbHasType?(:GRASS) && !b.airborne? && - (!b.statStageAtMax?(:ATTACK) || !b.statStageAtMax?(:SPECIAL_ATTACK)) - count += 1 - if user.opposes?(b) - score -= 20 - else - score -= user.stages[:ATTACK] * 10 - score -= user.stages[:SPECIAL_ATTACK] * 10 - end - end - end - score -= 95 if count == 0 - #--------------------------------------------------------------------------- - when "RaiseGrassBattlersDef1" - count = 0 - @battle.allBattlers.each do |b| - if b.pbHasType?(:GRASS) && !b.statStageAtMax?(:DEFENSE) - count += 1 - if user.opposes?(b) - score -= 20 - else - score -= user.stages[:DEFENSE] * 10 - end - end - end - score -= 95 if count == 0 - #--------------------------------------------------------------------------- - when "UserTargetSwapAtkSpAtkStages" - if skill >= PBTrainerAI.mediumSkill - aatk = user.stages[:ATTACK] - aspa = user.stages[:SPECIAL_ATTACK] - oatk = target.stages[:ATTACK] - ospa = target.stages[:SPECIAL_ATTACK] - if aatk >= oatk && aspa >= ospa - score -= 80 - else - score += (oatk - aatk) * 10 - score += (ospa - aspa) * 10 - end - else - score -= 50 - end - #--------------------------------------------------------------------------- - when "UserTargetSwapDefSpDefStages" - if skill >= PBTrainerAI.mediumSkill - adef = user.stages[:DEFENSE] - aspd = user.stages[:SPECIAL_DEFENSE] - odef = target.stages[:DEFENSE] - ospd = target.stages[:SPECIAL_DEFENSE] - if adef >= odef && aspd >= ospd - score -= 80 - else - score += (odef - adef) * 10 - score += (ospd - aspd) * 10 - end - else - score -= 50 - end - #--------------------------------------------------------------------------- - when "UserTargetSwapStatStages" - if skill >= PBTrainerAI.mediumSkill - userStages = 0 - targetStages = 0 - GameData::Stat.each_battle do |s| - userStages += user.stages[s.id] - targetStages += target.stages[s.id] - end - score += (targetStages - userStages) * 10 - else - score -= 50 - end - #--------------------------------------------------------------------------- - when "UserCopyTargetStatStages" - if skill >= PBTrainerAI.mediumSkill - equal = true - GameData::Stat.each_battle do |s| - stagediff = target.stages[s.id] - user.stages[s.id] - score += stagediff * 10 - equal = false if stagediff != 0 - end - score -= 80 if equal - else - score -= 50 - end - #--------------------------------------------------------------------------- - when "UserStealTargetPositiveStatStages" - numStages = 0 - GameData::Stat.each_battle do |s| - next if target.stages[s.id] <= 0 - numStages += target.stages[s.id] - end - score += numStages * 20 - #--------------------------------------------------------------------------- - when "InvertTargetStatStages" - if target.effects[PBEffects::Substitute] > 0 - score -= 90 - else - numpos = 0 - numneg = 0 - GameData::Stat.each_battle do |s| - numpos += target.stages[s.id] if target.stages[s.id] > 0 - numneg += target.stages[s.id] if target.stages[s.id] < 0 - end - if numpos != 0 || numneg != 0 - score += (numpos - numneg) * 10 - else - score -= 95 - end - end - #--------------------------------------------------------------------------- - when "ResetTargetStatStages" - if target.effects[PBEffects::Substitute] > 0 - score -= 90 - else - avg = 0 - anyChange = false - GameData::Stat.each_battle do |s| - next if target.stages[s.id] == 0 - avg += target.stages[s.id] - anyChange = true - end - if anyChange - score += avg * 10 - else - score -= 90 - end - end - #--------------------------------------------------------------------------- - when "ResetAllBattlersStatStages" - if skill >= PBTrainerAI.mediumSkill - stages = 0 - @battle.allBattlers.each do |b| - totalStages = 0 - GameData::Stat.each_battle { |s| totalStages += b.stages[s.id] } - if b.opposes?(user) - stages += totalStages - else - stages -= totalStages - end - end - score += stages * 10 - end - #--------------------------------------------------------------------------- - when "StartUserSideImmunityToStatStageLowering" - score -= 80 if user.pbOwnSide.effects[PBEffects::Mist] > 0 - #--------------------------------------------------------------------------- - when "UserSwapBaseAtkDef" - if skill >= PBTrainerAI.mediumSkill - aatk = pbRoughStat(user, :ATTACK, skill) - adef = pbRoughStat(user, :DEFENSE, skill) - if aatk == adef || - user.effects[PBEffects::PowerTrick] # No flip-flopping - score -= 90 - elsif adef > aatk # Prefer a higher Attack - score += 30 - else - score -= 30 - end - else - score -= 30 - end - #--------------------------------------------------------------------------- - when "UserTargetSwapBaseSpeed" - if skill >= PBTrainerAI.mediumSkill - if user.speed > target.speed - score += 50 - else - score -= 70 - end - end - #--------------------------------------------------------------------------- - when "UserTargetAverageBaseAtkSpAtk" - if skill >= PBTrainerAI.mediumSkill - aatk = pbRoughStat(user, :ATTACK, skill) - aspatk = pbRoughStat(user, :SPECIAL_ATTACK, skill) - oatk = pbRoughStat(target, :ATTACK, skill) - ospatk = pbRoughStat(target, :SPECIAL_ATTACK, skill) - if aatk < oatk && aspatk < ospatk - score += 50 - elsif aatk + aspatk < oatk + ospatk - score += 30 - else - score -= 50 - end - else - score -= 30 - end - #--------------------------------------------------------------------------- - when "UserTargetAverageBaseDefSpDef" - if skill >= PBTrainerAI.mediumSkill - adef = pbRoughStat(user, :DEFENSE, skill) - aspdef = pbRoughStat(user, :SPECIAL_DEFENSE, skill) - odef = pbRoughStat(target, :DEFENSE, skill) - ospdef = pbRoughStat(target, :SPECIAL_DEFENSE, skill) - if adef < odef && aspdef < ospdef - score += 50 - elsif adef + aspdef < odef + ospdef - score += 30 - else - score -= 50 - end - else - score -= 30 - end - #--------------------------------------------------------------------------- - when "UserTargetAverageHP" - if target.effects[PBEffects::Substitute] > 0 - score -= 90 - elsif user.hp >= (user.hp + target.hp) / 2 - score -= 90 - else - score += 40 - end - #--------------------------------------------------------------------------- - when "StartUserSideDoubleSpeed" - score -= 90 if user.pbOwnSide.effects[PBEffects::Tailwind] > 0 - #--------------------------------------------------------------------------- - when "StartSwapAllBattlersBaseDefensiveStats" - #--------------------------------------------------------------------------- - end - return score - end -end diff --git a/Data/Scripts/011_Battle/005_AI/006_AI_Move_EffectScores_2.rb b/Data/Scripts/011_Battle/005_AI/006_AI_Move_EffectScores_2.rb deleted file mode 100644 index 1b1670db2..000000000 --- a/Data/Scripts/011_Battle/005_AI/006_AI_Move_EffectScores_2.rb +++ /dev/null @@ -1,575 +0,0 @@ -class Battle::AI - #============================================================================= - # Get a score for the given move based on its effect - #============================================================================= - alias aiEffectScorePart1_pbGetMoveScoreFunctionCode pbGetMoveScoreFunctionCode - - def pbGetMoveScoreFunctionCode(score, move, user, target, skill = 100) - case move.function - #--------------------------------------------------------------------------- - when "SleepTarget", "SleepTargetIfUserDarkrai", "SleepTargetChangeUserMeloettaForm" - if target.pbCanSleep?(user, false) - score += 30 - if skill >= PBTrainerAI.mediumSkill - score -= 30 if target.effects[PBEffects::Yawn] > 0 - end - if skill >= PBTrainerAI.highSkill - score -= 30 if target.hasActiveAbility?(:MARVELSCALE) - end - if skill >= PBTrainerAI.bestSkill - if target.pbHasMoveFunction?("FlinchTargetFailsIfUserNotAsleep", - "UseRandomUserMoveIfAsleep") # Snore, Sleep Talk - score -= 50 - end - end - elsif skill >= PBTrainerAI.mediumSkill - score -= 90 if move.statusMove? - end - #--------------------------------------------------------------------------- - when "SleepTargetNextTurn" - if target.effects[PBEffects::Yawn] > 0 || !target.pbCanSleep?(user, false) - score -= 90 if skill >= PBTrainerAI.mediumSkill - else - score += 30 - if skill >= PBTrainerAI.highSkill - score -= 30 if target.hasActiveAbility?(:MARVELSCALE) - end - if skill >= PBTrainerAI.bestSkill - if target.pbHasMoveFunction?("FlinchTargetFailsIfUserNotAsleep", - "UseRandomUserMoveIfAsleep") # Snore, Sleep Talk - score -= 50 - end - end - end - #--------------------------------------------------------------------------- - when "PoisonTarget" - if target.pbCanPoison?(user, false) - score += 30 - if skill >= PBTrainerAI.mediumSkill - score += 30 if target.hp <= target.totalhp / 4 - score += 50 if target.hp <= target.totalhp / 8 - score -= 40 if target.effects[PBEffects::Yawn] > 0 - end - if skill >= PBTrainerAI.highSkill - score += 10 if pbRoughStat(target, :DEFENSE, skill) > 100 - score += 10 if pbRoughStat(target, :SPECIAL_DEFENSE, skill) > 100 - score -= 40 if target.hasActiveAbility?([:GUTS, :MARVELSCALE, :TOXICBOOST]) - end - elsif skill >= PBTrainerAI.mediumSkill - score -= 90 if move.statusMove? - end - #--------------------------------------------------------------------------- - when "PoisonTargetLowerTargetSpeed1" - if !target.pbCanPoison?(user, false) && !target.pbCanLowerStatStage?(:SPEED, user) - score -= 90 - else - if target.pbCanPoison?(user, false) - score += 30 - if skill >= PBTrainerAI.mediumSkill - score += 30 if target.hp <= target.totalhp / 4 - score += 50 if target.hp <= target.totalhp / 8 - score -= 40 if target.effects[PBEffects::Yawn] > 0 - end - if skill >= PBTrainerAI.highSkill - score += 10 if pbRoughStat(target, :DEFENSE, skill) > 100 - score += 10 if pbRoughStat(target, :SPECIAL_DEFENSE, skill) > 100 - score -= 40 if target.hasActiveAbility?([:GUTS, :MARVELSCALE, :TOXICBOOST]) - end - end - if target.pbCanLowerStatStage?(:SPEED, user) - score += target.stages[:SPEED] * 10 - if skill >= PBTrainerAI.highSkill - aspeed = pbRoughStat(user, :SPEED, skill) - ospeed = pbRoughStat(target, :SPEED, skill) - score += 30 if aspeed < ospeed && aspeed * 2 > ospeed - end - end - end - #--------------------------------------------------------------------------- - when "BadPoisonTarget" - if target.pbCanPoison?(user, false) - score += 30 - if skill >= PBTrainerAI.mediumSkill - score += 30 if target.hp <= target.totalhp / 4 - score += 50 if target.hp <= target.totalhp / 8 - score -= 40 if target.effects[PBEffects::Yawn] > 0 - end - if skill >= PBTrainerAI.highSkill - score += 10 if pbRoughStat(target, :DEFENSE, skill) > 100 - score += 10 if pbRoughStat(target, :SPECIAL_DEFENSE, skill) > 100 - score -= 40 if target.hasActiveAbility?([:GUTS, :MARVELSCALE, :TOXICBOOST]) - end - elsif skill >= PBTrainerAI.mediumSkill - score -= 90 if move.statusMove? - end - #--------------------------------------------------------------------------- - when "ParalyzeTarget", "ParalyzeTargetIfNotTypeImmune", - "ParalyzeTargetAlwaysHitsInRainHitsTargetInSky", "ParalyzeFlinchTarget" - if target.pbCanParalyze?(user, false) && - !(skill >= PBTrainerAI.mediumSkill && - move.id == :THUNDERWAVE && - Effectiveness.ineffective?(pbCalcTypeMod(move.type, user, target))) - score += 30 - if skill >= PBTrainerAI.mediumSkill - aspeed = pbRoughStat(user, :SPEED, skill) - ospeed = pbRoughStat(target, :SPEED, skill) - if aspeed < ospeed - score += 30 - elsif aspeed > ospeed - score -= 40 - end - end - if skill >= PBTrainerAI.highSkill - score -= 40 if target.hasActiveAbility?([:GUTS, :MARVELSCALE, :QUICKFEET]) - end - elsif skill >= PBTrainerAI.mediumSkill - score -= 90 if move.statusMove? - end - #--------------------------------------------------------------------------- - when "BurnTarget" - if target.pbCanBurn?(user, false) - score += 30 - if skill >= PBTrainerAI.highSkill - score -= 40 if target.hasActiveAbility?([:GUTS, :MARVELSCALE, :QUICKFEET, :FLAREBOOST]) - end - elsif skill >= PBTrainerAI.mediumSkill - score -= 90 if move.statusMove? - end - #--------------------------------------------------------------------------- - when "BurnTargetIfTargetStatsRaisedThisTurn" - if target.pbCanBurn?(user, false) - score += 40 - if skill >= PBTrainerAI.highSkill - score -= 40 if target.hasActiveAbility?([:GUTS, :MARVELSCALE, :QUICKFEET, :FLAREBOOST]) - end - else - score -= 90 - end - #--------------------------------------------------------------------------- - when "BurnFlinchTarget" - if target.pbCanBurn?(user, false) - score += 30 - if skill >= PBTrainerAI.highSkill - score -= 40 if target.hasActiveAbility?([:GUTS, :MARVELSCALE, :QUICKFEET, :FLAREBOOST]) - end - elsif skill >= PBTrainerAI.mediumSkill - score -= 90 if move.statusMove? - end - #--------------------------------------------------------------------------- - when "FreezeTarget" - if target.pbCanFreeze?(user, false) - score += 30 - if skill >= PBTrainerAI.highSkill - score -= 20 if target.hasActiveAbility?(:MARVELSCALE) - end - elsif skill >= PBTrainerAI.mediumSkill - score -= 90 if move.statusMove? - end - #--------------------------------------------------------------------------- - when "FreezeTargetSuperEffectiveAgainstWater" - if target.pbCanFreeze?(user, false) - score += 30 - if skill >= PBTrainerAI.highSkill - score -= 20 if target.hasActiveAbility?(:MARVELSCALE) - end - end - #--------------------------------------------------------------------------- - when "FreezeTargetAlwaysHitsInHail", "FreezeFlinchTarget" - if target.pbCanFreeze?(user, false) - score += 30 - if skill >= PBTrainerAI.highSkill - score -= 20 if target.hasActiveAbility?(:MARVELSCALE) - end - elsif skill >= PBTrainerAI.mediumSkill - score -= 90 if move.statusMove? - end - #--------------------------------------------------------------------------- - when "ParalyzeBurnOrFreezeTarget" - score += 30 if target.status == :NONE - #--------------------------------------------------------------------------- - when "GiveUserStatusToTarget" - if user.status == :NONE - score -= 90 - else - score += 40 - end - #--------------------------------------------------------------------------- - when "CureUserBurnPoisonParalysis" - case user.status - when :POISON - score += 40 - if skill >= PBTrainerAI.mediumSkill - if user.hp < user.totalhp / 8 - score += 60 - elsif skill >= PBTrainerAI.highSkill && - user.hp < (user.effects[PBEffects::Toxic] + 1) * user.totalhp / 16 - score += 60 - end - end - when :BURN, :PARALYSIS - score += 40 - else - score -= 90 - end - #--------------------------------------------------------------------------- - when "CureUserPartyStatus" - statuses = 0 - @battle.pbParty(user.index).each do |pkmn| - statuses += 1 if pkmn && pkmn.status != :NONE - end - if statuses == 0 - score -= 80 - else - score += 20 * statuses - end - #--------------------------------------------------------------------------- - when "CureTargetBurn" - if target.opposes?(user) - score -= 40 if target.status == :BURN - elsif target.status == :BURN - score += 40 - end - #--------------------------------------------------------------------------- - when "StartUserSideImmunityToInflictedStatus" - if user.pbOwnSide.effects[PBEffects::Safeguard] > 0 - score -= 80 - elsif user.status != :NONE - score -= 40 - else - score += 30 - end - #--------------------------------------------------------------------------- - when "FlinchTarget" - score += 30 - if skill >= PBTrainerAI.highSkill - score += 30 if !target.hasActiveAbility?(:INNERFOCUS) && - target.effects[PBEffects::Substitute] == 0 - end - #--------------------------------------------------------------------------- - when "FlinchTargetFailsIfUserNotAsleep" - if user.asleep? - score += 100 # Because it can only be used while asleep - if skill >= PBTrainerAI.highSkill - score += 30 if !target.hasActiveAbility?(:INNERFOCUS) && - target.effects[PBEffects::Substitute] == 0 - end - else - score -= 90 # Because it will fail here - score = 0 if skill >= PBTrainerAI.bestSkill - end - #--------------------------------------------------------------------------- - when "FlinchTargetFailsIfNotUserFirstTurn" - if user.turnCount == 0 - if skill >= PBTrainerAI.highSkill - score += 30 if !target.hasActiveAbility?(:INNERFOCUS) && - target.effects[PBEffects::Substitute] == 0 - end - else - score -= 90 # Because it will fail here - score = 0 if skill >= PBTrainerAI.bestSkill - end - #--------------------------------------------------------------------------- - when "FlinchTargetDoublePowerIfTargetInSky" - if skill >= PBTrainerAI.highSkill - score += 30 if !target.hasActiveAbility?(:INNERFOCUS) && - target.effects[PBEffects::Substitute] == 0 - end - #--------------------------------------------------------------------------- - when "ConfuseTarget", "ConfuseTargetAlwaysHitsInRainHitsTargetInSky" - if target.pbCanConfuse?(user, false) - score += 30 - elsif skill >= PBTrainerAI.mediumSkill - score -= 90 if move.statusMove? - end - #--------------------------------------------------------------------------- - when "AttractTarget" - canattract = true - agender = user.gender - ogender = target.gender - if agender == 2 || ogender == 2 || agender == ogender - score -= 90 - canattract = false - elsif target.effects[PBEffects::Attract] >= 0 - score -= 80 - canattract = false - elsif skill >= PBTrainerAI.bestSkill && target.hasActiveAbility?(:OBLIVIOUS) - score -= 80 - canattract = false - end - if skill >= PBTrainerAI.highSkill - if canattract && target.hasActiveItem?(:DESTINYKNOT) && - user.pbCanAttract?(target, false) - score -= 30 - end - end - #--------------------------------------------------------------------------- - when "SetUserTypesBasedOnEnvironment" - if !user.canChangeType? - score -= 90 - elsif skill >= PBTrainerAI.mediumSkill - new_type = nil - case @battle.field.terrain - when :Electric - new_type = :ELECTRIC if GameData::Type.exists?(:ELECTRIC) - when :Grassy - new_type = :GRASS if GameData::Type.exists?(:GRASS) - when :Misty - new_type = :FAIRY if GameData::Type.exists?(:FAIRY) - when :Psychic - new_type = :PSYCHIC if GameData::Type.exists?(:PSYCHIC) - end - if !new_type - envtypes = { - :None => :NORMAL, - :Grass => :GRASS, - :TallGrass => :GRASS, - :MovingWater => :WATER, - :StillWater => :WATER, - :Puddle => :WATER, - :Underwater => :WATER, - :Cave => :ROCK, - :Rock => :GROUND, - :Sand => :GROUND, - :Forest => :BUG, - :ForestGrass => :BUG, - :Snow => :ICE, - :Ice => :ICE, - :Volcano => :FIRE, - :Graveyard => :GHOST, - :Sky => :FLYING, - :Space => :DRAGON, - :UltraSpace => :PSYCHIC - } - new_type = envtypes[@battle.environment] - new_type = nil if !GameData::Type.exists?(new_type) - new_type ||= :NORMAL - end - score -= 90 if !user.pbHasOtherType?(new_type) - end - #--------------------------------------------------------------------------- - when "SetUserTypesToResistLastAttack" - if !user.canChangeType? - score -= 90 - elsif !target.lastMoveUsed || !target.lastMoveUsedType || - GameData::Type.get(target.lastMoveUsedType).pseudo_type - score -= 90 - else - aType = nil - target.eachMove do |m| - next if m.id != target.lastMoveUsed - aType = m.pbCalcType(user) - break - end - if aType - has_possible_type = false - GameData::Type.each do |t| - next if t.pseudo_type || user.pbHasType?(t.id) || - !Effectiveness.resistant_type?(target.lastMoveUsedType, t.id) - has_possible_type = true - break - end - score -= 90 if !has_possible_type - else - score -= 90 - end - end - #--------------------------------------------------------------------------- - when "SetUserTypesToTargetTypes" - if !user.canChangeType? || target.pbTypes(true).length == 0 - score -= 90 - elsif user.pbTypes == target.pbTypes && - user.effects[PBEffects::Type3] == target.effects[PBEffects::Type3] - score -= 90 - end - #--------------------------------------------------------------------------- - when "SetUserTypesToUserMoveType" - if user.canChangeType? - has_possible_type = false - user.eachMoveWithIndex do |m, i| - break if Settings::MECHANICS_GENERATION >= 6 && i > 0 - next if GameData::Type.get(m.type).pseudo_type - next if user.pbHasType?(m.type) - has_possible_type = true - break - end - score -= 90 if !has_possible_type - else - score -= 90 - end - #--------------------------------------------------------------------------- - when "SetTargetTypesToPsychic" - if target.pbHasOtherType?(:PSYCHIC) - score -= 90 - elsif !target.canChangeType? - score -= 90 - end - #--------------------------------------------------------------------------- - when "SetTargetTypesToWater" - if target.effects[PBEffects::Substitute] > 0 || !target.canChangeType? - score -= 90 - elsif !target.pbHasOtherType?(:WATER) - score -= 90 - end - #--------------------------------------------------------------------------- - when "AddGhostTypeToTarget" - score -= 90 if target.pbHasType?(:GHOST) - #--------------------------------------------------------------------------- - when "AddGrassTypeToTarget" - score -= 90 if target.pbHasType?(:GRASS) - #--------------------------------------------------------------------------- - when "UserLosesFireType" - score -= 90 if !user.pbHasType?(:FIRE) - #--------------------------------------------------------------------------- - when "SetTargetAbilityToSimple" - if target.effects[PBEffects::Substitute] > 0 - score -= 90 - elsif skill >= PBTrainerAI.mediumSkill - if target.unstoppableAbility? || [:TRUANT, :SIMPLE].include?(target.ability) - score -= 90 - end - end - #--------------------------------------------------------------------------- - when "SetTargetAbilityToInsomnia" - if target.effects[PBEffects::Substitute] > 0 - score -= 90 - elsif skill >= PBTrainerAI.mediumSkill - if target.unstoppableAbility? || [:TRUANT, :INSOMNIA].include?(target.ability_id) - score -= 90 - end - end - #--------------------------------------------------------------------------- - when "SetUserAbilityToTargetAbility" - score -= 40 # don't prefer this move - if skill >= PBTrainerAI.mediumSkill - if !target.ability || user.ability == target.ability || - [:MULTITYPE, :RKSSYSTEM].include?(user.ability_id) || - [:FLOWERGIFT, :FORECAST, :ILLUSION, :IMPOSTER, :MULTITYPE, :RKSSYSTEM, - :TRACE, :WONDERGUARD, :ZENMODE].include?(target.ability_id) - score -= 90 - end - end - if skill >= PBTrainerAI.highSkill - if target.ability == :TRUANT && user.opposes?(target) - score -= 90 - elsif target.ability == :SLOWSTART && user.opposes?(target) - score -= 90 - end - end - #--------------------------------------------------------------------------- - when "SetTargetAbilityToUserAbility" - score -= 40 # don't prefer this move - if target.effects[PBEffects::Substitute] > 0 - score -= 90 - elsif skill >= PBTrainerAI.mediumSkill - if !user.ability || user.ability == target.ability || - [:MULTITYPE, :RKSSYSTEM, :TRUANT].include?(target.ability_id) || - [:FLOWERGIFT, :FORECAST, :ILLUSION, :IMPOSTER, :MULTITYPE, :RKSSYSTEM, - :TRACE, :ZENMODE].include?(user.ability_id) - score -= 90 - end - if skill >= PBTrainerAI.highSkill - if user.ability == :TRUANT && user.opposes?(target) - score += 90 - elsif user.ability == :SLOWSTART && user.opposes?(target) - score += 90 - end - end - end - #--------------------------------------------------------------------------- - when "UserTargetSwapAbilities" - score -= 40 # don't prefer this move - if skill >= PBTrainerAI.mediumSkill - if (!user.ability && !target.ability) || - user.ability == target.ability || - [:ILLUSION, :MULTITYPE, :RKSSYSTEM, :WONDERGUARD].include?(user.ability_id) || - [:ILLUSION, :MULTITYPE, :RKSSYSTEM, :WONDERGUARD].include?(target.ability_id) - score -= 90 - end - end - if skill >= PBTrainerAI.highSkill - if target.ability == :TRUANT && user.opposes?(target) - score -= 90 - elsif target.ability == :SLOWSTART && user.opposes?(target) - score -= 90 - end - end - #--------------------------------------------------------------------------- - when "NegateTargetAbility" - if target.effects[PBEffects::Substitute] > 0 || - target.effects[PBEffects::GastroAcid] - score -= 90 - elsif skill >= PBTrainerAI.highSkill - score -= 90 if [:MULTITYPE, :RKSSYSTEM, :SLOWSTART, :TRUANT].include?(target.ability_id) - end - #--------------------------------------------------------------------------- - when "NegateTargetAbilityIfTargetActed" - if skill >= PBTrainerAI.mediumSkill - userSpeed = pbRoughStat(user, :SPEED, skill) - targetSpeed = pbRoughStat(target, :SPEED, skill) - if userSpeed < targetSpeed - score += 30 - end - else - score += 30 - end - #--------------------------------------------------------------------------- - when "IgnoreTargetAbility" - #--------------------------------------------------------------------------- - when "StartUserAirborne" - if user.effects[PBEffects::MagnetRise] > 0 || - user.effects[PBEffects::Ingrain] || - user.effects[PBEffects::SmackDown] - score -= 90 - end - #--------------------------------------------------------------------------- - when "StartTargetAirborneAndAlwaysHitByMoves" - if target.effects[PBEffects::Telekinesis] > 0 || - target.effects[PBEffects::Ingrain] || - target.effects[PBEffects::SmackDown] - score -= 90 - end - #--------------------------------------------------------------------------- - when "HitsTargetInSky" - #--------------------------------------------------------------------------- - when "HitsTargetInSkyGroundsTarget" - if skill >= PBTrainerAI.mediumSkill - score += 20 if target.effects[PBEffects::MagnetRise] > 0 - score += 20 if target.effects[PBEffects::Telekinesis] > 0 - score += 20 if target.inTwoTurnAttack?("TwoTurnAttackInvulnerableInSky", - "TwoTurnAttackInvulnerableInSkyParalyzeTarget") - score += 20 if target.pbHasType?(:FLYING) - score += 20 if target.hasActiveAbility?(:LEVITATE) - score += 20 if target.hasActiveItem?(:AIRBALLOON) - end - #--------------------------------------------------------------------------- - when "StartGravity" - if @battle.field.effects[PBEffects::Gravity] > 0 - score -= 90 - elsif skill >= PBTrainerAI.mediumSkill - score -= 30 - score -= 20 if user.effects[PBEffects::SkyDrop] >= 0 - score -= 20 if user.effects[PBEffects::MagnetRise] > 0 - score -= 20 if user.effects[PBEffects::Telekinesis] > 0 - score -= 20 if user.pbHasType?(:FLYING) - score -= 20 if user.hasActiveAbility?(:LEVITATE) - score -= 20 if user.hasActiveItem?(:AIRBALLOON) - score += 20 if target.effects[PBEffects::SkyDrop] >= 0 - score += 20 if target.effects[PBEffects::MagnetRise] > 0 - score += 20 if target.effects[PBEffects::Telekinesis] > 0 - score += 20 if target.inTwoTurnAttack?("TwoTurnAttackInvulnerableInSky", - "TwoTurnAttackInvulnerableInSkyParalyzeTarget", - "TwoTurnAttackInvulnerableInSkyTargetCannotAct") - score += 20 if target.pbHasType?(:FLYING) - score += 20 if target.hasActiveAbility?(:LEVITATE) - score += 20 if target.hasActiveItem?(:AIRBALLOON) - end - #--------------------------------------------------------------------------- - when "TransformUserIntoTarget" - score -= 70 - #--------------------------------------------------------------------------- - else - return aiEffectScorePart1_pbGetMoveScoreFunctionCode(score, move, user, target, skill) - end - return score - end -end diff --git a/Data/Scripts/011_Battle/005_AI/007_AI_Move_EffectScores_3.rb b/Data/Scripts/011_Battle/005_AI/007_AI_Move_EffectScores_3.rb deleted file mode 100644 index 3666f809f..000000000 --- a/Data/Scripts/011_Battle/005_AI/007_AI_Move_EffectScores_3.rb +++ /dev/null @@ -1,1383 +0,0 @@ -class Battle::AI - #============================================================================= - # Get a score for the given move based on its effect - #============================================================================= - alias aiEffectScorePart2_pbGetMoveScoreFunctionCode pbGetMoveScoreFunctionCode - - def pbGetMoveScoreFunctionCode(score, move, user, target, skill = 100) - case move.function - #--------------------------------------------------------------------------- - when "FixedDamage20" - if target.hp <= 20 - score += 80 - elsif target.level >= 25 - score -= 60 # Not useful against high-level Pokemon - end - #--------------------------------------------------------------------------- - when "FixedDamage40" - score += 80 if target.hp <= 40 - #--------------------------------------------------------------------------- - when "FixedDamageHalfTargetHP" - score -= 50 - score += target.hp * 100 / target.totalhp - #--------------------------------------------------------------------------- - when "FixedDamageUserLevel" - score += 80 if target.hp <= user.level - #--------------------------------------------------------------------------- - when "FixedDamageUserLevelRandom" - score += 30 if target.hp <= user.level - #--------------------------------------------------------------------------- - when "LowerTargetHPToUserHP" - if user.hp >= target.hp - score -= 90 - elsif user.hp < target.hp / 2 - score += 50 - end - #--------------------------------------------------------------------------- - when "OHKO", "OHKOIce", "OHKOHitsUndergroundTarget" - score -= 90 if target.hasActiveAbility?(:STURDY) - score -= 90 if target.level > user.level - #--------------------------------------------------------------------------- - when "DamageTargetAlly" - target.allAllies.each do |b| - next if !b.near?(target) - score += 10 - end - #--------------------------------------------------------------------------- - when "PowerHigherWithUserHP" - #--------------------------------------------------------------------------- - when "PowerLowerWithUserHP" - #--------------------------------------------------------------------------- - when "PowerHigherWithTargetHP" - #--------------------------------------------------------------------------- - when "PowerHigherWithUserHappiness" - #--------------------------------------------------------------------------- - when "PowerLowerWithUserHappiness" - #--------------------------------------------------------------------------- - when "PowerHigherWithUserPositiveStatStages" - #--------------------------------------------------------------------------- - when "PowerHigherWithTargetPositiveStatStages" - #--------------------------------------------------------------------------- - when "PowerHigherWithUserFasterThanTarget" - #--------------------------------------------------------------------------- - when "PowerHigherWithTargetFasterThanUser" - #--------------------------------------------------------------------------- - when "PowerHigherWithLessPP" - #--------------------------------------------------------------------------- - when "PowerHigherWithTargetWeight" - #--------------------------------------------------------------------------- - when "PowerHigherWithUserHeavierThanTarget" - #--------------------------------------------------------------------------- - when "PowerHigherWithConsecutiveUse" - #--------------------------------------------------------------------------- - when "PowerHigherWithConsecutiveUseOnUserSide" - #--------------------------------------------------------------------------- - when "RandomPowerDoublePowerIfTargetUnderground" - #--------------------------------------------------------------------------- - when "DoublePowerIfTargetHPLessThanHalf" - #--------------------------------------------------------------------------- - when "DoublePowerIfUserPoisonedBurnedParalyzed" - #--------------------------------------------------------------------------- - when "DoublePowerIfTargetAsleepCureTarget" - score -= 20 if target.status == :SLEEP && # Will cure status - target.statusCount > 1 - #--------------------------------------------------------------------------- - when "DoublePowerIfTargetPoisoned" - #--------------------------------------------------------------------------- - when "DoublePowerIfTargetParalyzedCureTarget" - score -= 20 if target.status == :PARALYSIS # Will cure status - #--------------------------------------------------------------------------- - when "DoublePowerIfTargetStatusProblem" - #--------------------------------------------------------------------------- - when "DoublePowerIfUserHasNoItem" - #--------------------------------------------------------------------------- - when "DoublePowerIfTargetUnderwater" - #--------------------------------------------------------------------------- - when "DoublePowerIfTargetUnderground" - #--------------------------------------------------------------------------- - when "DoublePowerIfTargetInSky" - #--------------------------------------------------------------------------- - when "DoublePowerInElectricTerrain" - score += 40 if @battle.field.terrain == :Electric && target.affectedByTerrain? - #--------------------------------------------------------------------------- - when "DoublePowerIfUserLastMoveFailed" - #--------------------------------------------------------------------------- - when "DoublePowerIfAllyFaintedLastTurn" - #--------------------------------------------------------------------------- - when "DoublePowerIfUserLostHPThisTurn" - attspeed = pbRoughStat(user, :SPEED, skill) - oppspeed = pbRoughStat(target, :SPEED, skill) - score += 30 if oppspeed > attspeed - #--------------------------------------------------------------------------- - when "DoublePowerIfTargetLostHPThisTurn" - score += 20 if @battle.pbOpposingBattlerCount(user) > 1 - #--------------------------------------------------------------------------- - when "DoublePowerIfUserStatsLoweredThisTurn" - #--------------------------------------------------------------------------- - when "DoublePowerIfTargetActed" - attspeed = pbRoughStat(user, :SPEED, skill) - oppspeed = pbRoughStat(target, :SPEED, skill) - score += 30 if oppspeed > attspeed - #--------------------------------------------------------------------------- - when "DoublePowerIfTargetNotActed" - #--------------------------------------------------------------------------- - when "AlwaysCriticalHit" - #--------------------------------------------------------------------------- - when "EnsureNextCriticalHit" - if user.effects[PBEffects::LaserFocus] > 0 - score -= 90 - else - score += 40 - end - #--------------------------------------------------------------------------- - when "StartPreventCriticalHitsAgainstUserSide" - score -= 90 if user.pbOwnSide.effects[PBEffects::LuckyChant] > 0 - #--------------------------------------------------------------------------- - when "CannotMakeTargetFaint" - if target.hp == 1 - score -= 90 - elsif target.hp <= target.totalhp / 8 - score -= 60 - elsif target.hp <= target.totalhp / 4 - score -= 30 - end - #--------------------------------------------------------------------------- - when "UserEnduresFaintingThisTurn" - score -= 25 if user.hp > user.totalhp / 2 - if skill >= PBTrainerAI.mediumSkill - score -= 90 if user.effects[PBEffects::ProtectRate] > 1 - score -= 90 if target.effects[PBEffects::HyperBeam] > 0 - else - score -= user.effects[PBEffects::ProtectRate] * 40 - end - #--------------------------------------------------------------------------- - when "StartWeakenElectricMoves" - score -= 90 if user.effects[PBEffects::MudSport] - #--------------------------------------------------------------------------- - when "StartWeakenFireMoves" - score -= 90 if user.effects[PBEffects::WaterSport] - #--------------------------------------------------------------------------- - when "StartWeakenPhysicalDamageAgainstUserSide" - score -= 90 if user.pbOwnSide.effects[PBEffects::Reflect] > 0 - #--------------------------------------------------------------------------- - when "StartWeakenSpecialDamageAgainstUserSide" - score -= 90 if user.pbOwnSide.effects[PBEffects::LightScreen] > 0 - #--------------------------------------------------------------------------- - when "StartWeakenDamageAgainstUserSideIfHail" - if user.pbOwnSide.effects[PBEffects::AuroraVeil] > 0 || user.effectiveWeather != :Hail - score -= 90 - else - score += 40 - end - #--------------------------------------------------------------------------- - when "RemoveScreens" - score += 20 if user.pbOpposingSide.effects[PBEffects::AuroraVeil] > 0 - score += 20 if user.pbOpposingSide.effects[PBEffects::Reflect] > 0 - score += 20 if user.pbOpposingSide.effects[PBEffects::LightScreen] > 0 - #--------------------------------------------------------------------------- - when "ProtectUser" - if user.effects[PBEffects::ProtectRate] > 1 || - target.effects[PBEffects::HyperBeam] > 0 - score -= 90 - else - if skill >= PBTrainerAI.mediumSkill - score -= user.effects[PBEffects::ProtectRate] * 40 - end - score += 50 if user.turnCount == 0 - score += 30 if target.effects[PBEffects::TwoTurnAttack] - end - #--------------------------------------------------------------------------- - when "ProtectUserBanefulBunker" - if user.effects[PBEffects::ProtectRate] > 1 || - target.effects[PBEffects::HyperBeam] > 0 - score -= 90 - else - if skill >= PBTrainerAI.mediumSkill - score -= user.effects[PBEffects::ProtectRate] * 40 - end - score += 50 if user.turnCount == 0 - score += 30 if target.effects[PBEffects::TwoTurnAttack] - score += 20 # Because of possible poisoning - end - #--------------------------------------------------------------------------- - when "ProtectUserFromDamagingMovesKingsShield" - if user.effects[PBEffects::ProtectRate] > 1 || - target.effects[PBEffects::HyperBeam] > 0 - score -= 90 - else - if skill >= PBTrainerAI.mediumSkill - score -= user.effects[PBEffects::ProtectRate] * 40 - end - score += 50 if user.turnCount == 0 - score += 30 if target.effects[PBEffects::TwoTurnAttack] - end - #--------------------------------------------------------------------------- - when "ProtectUserFromDamagingMovesObstruct" - if user.effects[PBEffects::ProtectRate] > 1 || - target.effects[PBEffects::HyperBeam] > 0 - score -= 90 - else - if skill >= PBTrainerAI.mediumSkill - score -= user.effects[PBEffects::ProtectRate] * 40 - end - score += 50 if user.turnCount == 0 - score += 30 if target.effects[PBEffects::TwoTurnAttack] - end - #--------------------------------------------------------------------------- - when "ProtectUserFromTargetingMovesSpikyShield" - if user.effects[PBEffects::ProtectRate] > 1 || - target.effects[PBEffects::HyperBeam] > 0 - score -= 90 - else - if skill >= PBTrainerAI.mediumSkill - score -= user.effects[PBEffects::ProtectRate] * 40 - end - score += 50 if user.turnCount == 0 - score += 30 if target.effects[PBEffects::TwoTurnAttack] - end - #--------------------------------------------------------------------------- - when "ProtectUserSideFromDamagingMovesIfUserFirstTurn" - if user.turnCount == 0 - score += 30 - else - score -= 90 # Because it will fail here - score = 0 if skill >= PBTrainerAI.bestSkill - end - #--------------------------------------------------------------------------- - when "ProtectUserSideFromStatusMoves" - #--------------------------------------------------------------------------- - when "ProtectUserSideFromPriorityMoves" - #--------------------------------------------------------------------------- - when "ProtectUserSideFromMultiTargetDamagingMoves" - #--------------------------------------------------------------------------- - when "RemoveProtections" - #--------------------------------------------------------------------------- - when "RemoveProtectionsBypassSubstitute" - #--------------------------------------------------------------------------- - when "HoopaRemoveProtectionsBypassSubstituteLowerUserDef1" - if !user.isSpecies?(:HOOPA) || user.form != 1 - score -= 100 - elsif target.stages[:DEFENSE] > 0 - score += 20 - end - #--------------------------------------------------------------------------- - when "RecoilQuarterOfDamageDealt" - score -= 25 - #--------------------------------------------------------------------------- - when "RecoilThirdOfDamageDealt" - score -= 30 - #--------------------------------------------------------------------------- - when "RecoilThirdOfDamageDealtParalyzeTarget" - score -= 30 - if target.pbCanParalyze?(user, false) - score += 30 - if skill >= PBTrainerAI.mediumSkill - aspeed = pbRoughStat(user, :SPEED, skill) - ospeed = pbRoughStat(target, :SPEED, skill) - if aspeed < ospeed - score += 30 - elsif aspeed > ospeed - score -= 40 - end - end - if skill >= PBTrainerAI.highSkill - score -= 40 if target.hasActiveAbility?([:GUTS, :MARVELSCALE, :QUICKFEET]) - end - end - #--------------------------------------------------------------------------- - when "RecoilThirdOfDamageDealtBurnTarget" - score -= 30 - if target.pbCanBurn?(user, false) - score += 30 - if skill >= PBTrainerAI.highSkill - score -= 40 if target.hasActiveAbility?([:GUTS, :MARVELSCALE, :QUICKFEET, :FLAREBOOST]) - end - end - #--------------------------------------------------------------------------- - when "RecoilHalfOfDamageDealt" - score -= 40 - #--------------------------------------------------------------------------- - when "EffectivenessIncludesFlyingType" - #--------------------------------------------------------------------------- - when "CategoryDependsOnHigherDamagePoisonTarget" - score += 5 if target.pbCanPoison?(user, false) - #--------------------------------------------------------------------------- - when "CategoryDependsOnHigherDamageIgnoreTargetAbility" - #--------------------------------------------------------------------------- - when "UseUserBaseDefenseInsteadOfUserBaseAttack" - #--------------------------------------------------------------------------- - when "UseTargetAttackInsteadOfUserAttack" - #--------------------------------------------------------------------------- - when "UseTargetDefenseInsteadOfTargetSpDef" - #--------------------------------------------------------------------------- - when "EnsureNextMoveAlwaysHits" - score -= 90 if target.effects[PBEffects::Substitute] > 0 - score -= 90 if user.effects[PBEffects::LockOn] > 0 - #--------------------------------------------------------------------------- - when "StartNegateTargetEvasionStatStageAndGhostImmunity" - if target.effects[PBEffects::Foresight] - score -= 90 - elsif target.pbHasType?(:GHOST) - score += 70 - elsif target.stages[:EVASION] <= 0 - score -= 60 - end - #--------------------------------------------------------------------------- - when "StartNegateTargetEvasionStatStageAndDarkImmunity" - if target.effects[PBEffects::MiracleEye] - score -= 90 - elsif target.pbHasType?(:DARK) - score += 70 - elsif target.stages[:EVASION] <= 0 - score -= 60 - end - #--------------------------------------------------------------------------- - when "IgnoreTargetDefSpDefEvaStatStages" - #--------------------------------------------------------------------------- - when "TypeIsUserFirstType" - #--------------------------------------------------------------------------- - when "TypeDependsOnUserIVs" - #--------------------------------------------------------------------------- - when "TypeAndPowerDependOnUserBerry" - score -= 90 if !user.item || !user.item.is_berry? || !user.itemActive? - #--------------------------------------------------------------------------- - when "TypeDependsOnUserPlate", "TypeDependsOnUserMemory", "TypeDependsOnUserDrive" - #--------------------------------------------------------------------------- - when "TypeDependsOnUserMorpekoFormRaiseUserSpeed1" - score += 20 if user.stages[:SPEED] <= 0 - #--------------------------------------------------------------------------- - when "TypeAndPowerDependOnWeather" - #--------------------------------------------------------------------------- - when "TypeAndPowerDependOnTerrain" - score += 40 if @battle.field.terrain != :None - #--------------------------------------------------------------------------- - when "TargetMovesBecomeElectric" - aspeed = pbRoughStat(user, :SPEED, skill) - ospeed = pbRoughStat(target, :SPEED, skill) - score -= 90 if aspeed > ospeed - #--------------------------------------------------------------------------- - when "NormalMovesBecomeElectric" - #--------------------------------------------------------------------------- - when "HitTwoTimes" - #--------------------------------------------------------------------------- - when "HitTwoTimesPoisonTarget" - if target.pbCanPoison?(user, false) - score += 30 - if skill >= PBTrainerAI.mediumSkill - score += 30 if target.hp <= target.totalhp / 4 - score += 50 if target.hp <= target.totalhp / 8 - score -= 40 if target.effects[PBEffects::Yawn] > 0 - end - if skill >= PBTrainerAI.highSkill - score += 10 if pbRoughStat(target, :DEFENSE, skill) > 100 - score += 10 if pbRoughStat(target, :SPECIAL_DEFENSE, skill) > 100 - score -= 40 if target.hasActiveAbility?([:GUTS, :MARVELSCALE, :TOXICBOOST]) - end - elsif skill >= PBTrainerAI.mediumSkill - score -= 90 if move.statusMove? - end - #--------------------------------------------------------------------------- - when "HitTwoTimesFlinchTarget" - score += 30 if target.effects[PBEffects::Minimize] - #--------------------------------------------------------------------------- - when "HitTwoTimesTargetThenTargetAlly" - #--------------------------------------------------------------------------- - when "HitThreeTimesPowersUpWithEachHit" - #--------------------------------------------------------------------------- - when "HitThreeTimesAlwaysCriticalHit" - if skill >= PBTrainerAI.highSkill - stat = (move.physicalMove?) ? :DEFENSE : :SPECIAL_DEFENSE - score += 50 if targets.stages[stat] > 1 - end - #--------------------------------------------------------------------------- - when "HitTwoToFiveTimes" - #--------------------------------------------------------------------------- - when "HitTwoToFiveTimesOrThreeForAshGreninja" - #--------------------------------------------------------------------------- - when "HitTwoToFiveTimesRaiseUserSpd1LowerUserDef1" - aspeed = pbRoughStat(user, :SPEED, skill) - ospeed = pbRoughStat(target, :SPEED, skill) - if aspeed > ospeed && aspeed * 2 / 3 < ospeed - score -= 50 - elsif aspeed < ospeed && aspeed * 1.5 > ospeed - score += 50 - end - score += user.stages[:DEFENSE] * 30 - #--------------------------------------------------------------------------- - when "HitOncePerUserTeamMember" - #--------------------------------------------------------------------------- - when "AttackAndSkipNextTurn" - #--------------------------------------------------------------------------- - when "TwoTurnAttack", "TwoTurnAttackOneTurnInSun" - #--------------------------------------------------------------------------- - when "TwoTurnAttackParalyzeTarget" - if target.pbCanParalyze?(user, false) && - !(skill >= PBTrainerAI.mediumSkill && - move.id == :THUNDERWAVE && - Effectiveness.ineffective?(pbCalcTypeMod(move.type, user, target))) - score += 30 - if skill >= PBTrainerAI.mediumSkill - aspeed = pbRoughStat(user, :SPEED, skill) - ospeed = pbRoughStat(target, :SPEED, skill) - if aspeed < ospeed - score += 30 - elsif aspeed > ospeed - score -= 40 - end - end - if skill >= PBTrainerAI.highSkill - score -= 40 if target.hasActiveAbility?([:GUTS, :MARVELSCALE, :QUICKFEET]) - end - elsif skill >= PBTrainerAI.mediumSkill - score -= 90 if move.statusMove? - end - #--------------------------------------------------------------------------- - when "TwoTurnAttackBurnTarget" - if target.pbCanBurn?(user, false) - score += 30 - if skill >= PBTrainerAI.highSkill - score -= 40 if target.hasActiveAbility?([:GUTS, :MARVELSCALE, :QUICKFEET, :FLAREBOOST]) - end - elsif skill >= PBTrainerAI.mediumSkill - score -= 90 if move.statusMove? - end - #--------------------------------------------------------------------------- - when "TwoTurnAttackFlinchTarget" - score += 20 if user.effects[PBEffects::FocusEnergy] > 0 - if skill >= PBTrainerAI.highSkill - score += 20 if !target.hasActiveAbility?(:INNERFOCUS) && - target.effects[PBEffects::Substitute] == 0 - end - #--------------------------------------------------------------------------- - when "TwoTurnAttackRaiseUserSpAtkSpDefSpd2" - if user.statStageAtMax?(:SPECIAL_ATTACK) && - user.statStageAtMax?(:SPECIAL_DEFENSE) && - user.statStageAtMax?(:SPEED) - score -= 90 - else - score -= user.stages[:SPECIAL_ATTACK] * 10 # Only *10 instead of *20 - score -= user.stages[:SPECIAL_DEFENSE] * 10 # because two-turn attack - score -= user.stages[:SPEED] * 10 - if skill >= PBTrainerAI.mediumSkill - hasSpecialAttack = false - user.eachMove do |m| - next if !m.specialMove?(m.type) - hasSpecialAttack = true - break - end - if hasSpecialAttack - score += 20 - elsif skill >= PBTrainerAI.highSkill - score -= 90 - end - end - if skill >= PBTrainerAI.highSkill - aspeed = pbRoughStat(user, :SPEED, skill) - ospeed = pbRoughStat(target, :SPEED, skill) - score += 30 if aspeed < ospeed && aspeed * 2 > ospeed - end - end - #--------------------------------------------------------------------------- - when "TwoTurnAttackChargeRaiseUserDefense1" - if move.statusMove? - if user.statStageAtMax?(:DEFENSE) - score -= 90 - else - score -= user.stages[:DEFENSE] * 20 - end - elsif user.stages[:DEFENSE] < 0 - score += 20 - end - #--------------------------------------------------------------------------- - when "TwoTurnAttackChargeRaiseUserSpAtk1" - aspeed = pbRoughStat(user, :SPEED, skill) - ospeed = pbRoughStat(target, :SPEED, skill) - if (aspeed > ospeed && user.hp > user.totalhp / 3) || user.hp > user.totalhp / 2 - score += 60 - else - score -= 90 - end - score += user.stages[:SPECIAL_ATTACK] * 20 - #--------------------------------------------------------------------------- - when "TwoTurnAttackInvulnerableUnderground" - #--------------------------------------------------------------------------- - when "TwoTurnAttackInvulnerableUnderwater" - #--------------------------------------------------------------------------- - when "TwoTurnAttackInvulnerableInSky" - #--------------------------------------------------------------------------- - when "TwoTurnAttackInvulnerableInSkyParalyzeTarget" - #--------------------------------------------------------------------------- - when "TwoTurnAttackInvulnerableInSkyTargetCannotAct" - #--------------------------------------------------------------------------- - when "TwoTurnAttackInvulnerableRemoveProtections" - #--------------------------------------------------------------------------- - when "MultiTurnAttackPreventSleeping" - #--------------------------------------------------------------------------- - when "MultiTurnAttackConfuseUserAtEnd" - #--------------------------------------------------------------------------- - when "MultiTurnAttackPowersUpEachTurn" - #--------------------------------------------------------------------------- - when "MultiTurnAttackBideThenReturnDoubleDamage" - if user.hp <= user.totalhp / 4 - score -= 90 - elsif user.hp <= user.totalhp / 2 - score -= 50 - end - #--------------------------------------------------------------------------- - when "HealUserFullyAndFallAsleep" - if user.hp == user.totalhp || !user.pbCanSleep?(user, false, nil, true) - score -= 90 - else - score += 70 - score -= user.hp * 140 / user.totalhp - score += 30 if user.status != :NONE - end - #--------------------------------------------------------------------------- - when "HealUserHalfOfTotalHP" - if user.hp == user.totalhp || (skill >= PBTrainerAI.mediumSkill && !user.canHeal?) - score -= 90 - else - score += 50 - score -= user.hp * 100 / user.totalhp - end - #--------------------------------------------------------------------------- - when "HealUserDependingOnWeather" - if user.hp == user.totalhp || (skill >= PBTrainerAI.mediumSkill && !user.canHeal?) - score -= 90 - else - case user.effectiveWeather - when :Sun, :HarshSun - score += 30 - when :None - else - score -= 30 - end - score += 50 - score -= user.hp * 100 / user.totalhp - end - #--------------------------------------------------------------------------- - when "HealUserDependingOnSandstorm" - if user.hp == user.totalhp || (skill >= PBTrainerAI.mediumSkill && !user.canHeal?) - score -= 90 - else - score += 50 - score -= user.hp * 100 / user.totalhp - score += 30 if user.effectiveWeather == :Sandstorm - end - #--------------------------------------------------------------------------- - when "HealUserHalfOfTotalHPLoseFlyingTypeThisTurn" - if user.hp == user.totalhp || (skill >= PBTrainerAI.mediumSkill && !user.canHeal?) - score -= 90 - else - score += 50 - score -= user.hp * 100 / user.totalhp - end - #--------------------------------------------------------------------------- - when "CureTargetStatusHealUserHalfOfTotalHP" - if target.status == :NONE - score -= 90 - elsif user.hp == user.totalhp && target.opposes?(user) - score -= 90 - else - score += (user.totalhp - user.hp) * 50 / user.totalhp - score -= 30 if target.opposes?(user) - end - #--------------------------------------------------------------------------- - when "HealUserByTargetAttackLowerTargetAttack1" - if target.statStageAtMin?(:ATTACK) - score -= 90 - else - if target.pbCanLowerStatStage?(:ATTACK, user) - score += target.stages[:ATTACK] * 20 - if skill >= PBTrainerAI.mediumSkill - hasPhysicalAttack = false - target.eachMove do |m| - next if !m.physicalMove?(m.type) - hasPhysicalAttack = true - break - end - if hasPhysicalAttack - score += 20 - elsif skill >= PBTrainerAI.highSkill - score -= 90 - end - end - end - score += (user.totalhp - user.hp) * 50 / user.totalhp - end - #--------------------------------------------------------------------------- - when "HealUserByHalfOfDamageDone" - if skill >= PBTrainerAI.highSkill && target.hasActiveAbility?(:LIQUIDOOZE) - score -= 70 - elsif user.hp <= user.totalhp / 2 - score += 20 - end - #--------------------------------------------------------------------------- - when "HealUserByHalfOfDamageDoneIfTargetAsleep" - if !target.asleep? - score -= 100 - elsif skill >= PBTrainerAI.highSkill && target.hasActiveAbility?(:LIQUIDOOZE) - score -= 70 - elsif user.hp <= user.totalhp / 2 - score += 20 - end - #--------------------------------------------------------------------------- - when "HealUserByThreeQuartersOfDamageDone" - if skill >= PBTrainerAI.highSkill && target.hasActiveAbility?(:LIQUIDOOZE) - score -= 80 - elsif user.hp <= user.totalhp / 2 - score += 40 - end - #--------------------------------------------------------------------------- - when "HealUserAndAlliesQuarterOfTotalHP" - ally_amt = 30 - @battle.allSameSideBattlers(user.index).each do |b| - if b.hp == b.totalhp || (skill >= PBTrainerAI.mediumSkill && !b.canHeal?) - score -= ally_amt / 2 - elsif b.hp < b.totalhp * 3 / 4 - score += ally_amt - end - end - #--------------------------------------------------------------------------- - when "HealUserAndAlliesQuarterOfTotalHPCureStatus" - ally_amt = 80 / @battle.pbSideSize(user.index) - @battle.allSameSideBattlers(user.index).each do |b| - if b.hp == b.totalhp || (skill >= PBTrainerAI.mediumSkill && !b.canHeal?) - score -= ally_amt - elsif b.hp < b.totalhp * 3 / 4 - score += ally_amt - end - score += ally_amt / 2 if b.pbHasAnyStatus? - end - #--------------------------------------------------------------------------- - when "HealTargetHalfOfTotalHP" - if user.opposes?(target) - score -= 100 - elsif target.hp < target.totalhp / 2 && target.effects[PBEffects::Substitute] == 0 - score += 20 - end - #--------------------------------------------------------------------------- - when "HealTargetDependingOnGrassyTerrain" - if user.hp == user.totalhp || (skill >= PBTrainerAI.mediumSkill && !user.canHeal?) - score -= 90 - else - score += 50 - score -= user.hp * 100 / user.totalhp - if skill >= PBTrainerAI.mediumSkill - score += 30 if @battle.field.terrain == :Grassy - end - end - #--------------------------------------------------------------------------- - when "HealUserPositionNextTurn" - score -= 90 if @battle.positions[user.index].effects[PBEffects::Wish] > 0 - #--------------------------------------------------------------------------- - when "StartHealUserEachTurn" - score -= 90 if user.effects[PBEffects::AquaRing] - #--------------------------------------------------------------------------- - when "StartHealUserEachTurnTrapUserInBattle" - score -= 90 if user.effects[PBEffects::Ingrain] - #--------------------------------------------------------------------------- - when "StartDamageTargetEachTurnIfTargetAsleep" - if target.effects[PBEffects::Nightmare] || - target.effects[PBEffects::Substitute] > 0 - score -= 90 - elsif !target.asleep? - score -= 90 - else - score -= 90 if target.statusCount <= 1 - score += 50 if target.statusCount > 3 - end - #--------------------------------------------------------------------------- - when "StartLeechSeedTarget" - if target.effects[PBEffects::LeechSeed] >= 0 - score -= 90 - elsif skill >= PBTrainerAI.mediumSkill && target.pbHasType?(:GRASS) - score -= 90 - elsif user.turnCount == 0 - score += 60 - end - #--------------------------------------------------------------------------- - when "UserLosesHalfOfTotalHP" - score -= 100 if user.hp <= user.totalhp / 2 - #--------------------------------------------------------------------------- - when "UserLosesHalfOfTotalHPExplosive" - reserves = @battle.pbAbleNonActiveCount(user.idxOwnSide) - foes = @battle.pbAbleNonActiveCount(user.idxOpposingSide) - if @battle.pbCheckGlobalAbility(:DAMP) - score -= 100 - elsif skill >= PBTrainerAI.mediumSkill && reserves == 0 && foes > 0 - score -= 100 # don't want to lose - elsif skill >= PBTrainerAI.highSkill && reserves == 0 && foes == 0 - score += 80 # want to draw - else - score -= (user.totalhp - user.hp) * 75 / user.totalhp - end - #--------------------------------------------------------------------------- - when "UserFaintsExplosive" - reserves = @battle.pbAbleNonActiveCount(user.idxOwnSide) - foes = @battle.pbAbleNonActiveCount(user.idxOpposingSide) - if @battle.pbCheckGlobalAbility(:DAMP) - score -= 100 - elsif skill >= PBTrainerAI.mediumSkill && reserves == 0 && foes > 0 - score -= 100 # don't want to lose - elsif skill >= PBTrainerAI.highSkill && reserves == 0 && foes == 0 - score += 80 # want to draw - else - score -= user.hp * 100 / user.totalhp - end - #--------------------------------------------------------------------------- - when "UserFaintsPowersUpInMistyTerrainExplosive" - reserves = @battle.pbAbleNonActiveCount(user.idxOwnSide) - foes = @battle.pbAbleNonActiveCount(user.idxOpposingSide) - if @battle.pbCheckGlobalAbility(:DAMP) - score -= 100 - elsif skill >= PBTrainerAI.mediumSkill && reserves == 0 && foes > 0 - score -= 100 # don't want to lose - elsif skill >= PBTrainerAI.highSkill && reserves == 0 && foes == 0 - score += 40 # want to draw - score += 40 if @battle.field.terrain == :Misty - else - score -= user.hp * 100 / user.totalhp - score += 20 if @battle.field.terrain == :Misty - end - #--------------------------------------------------------------------------- - when "UserFaintsFixedDamageUserHP" - #--------------------------------------------------------------------------- - when "UserFaintsLowerTargetAtkSpAtk2" - if !target.pbCanLowerStatStage?(:ATTACK, user) && - !target.pbCanLowerStatStage?(:SPECIAL_ATTACK, user) - score -= 100 - elsif @battle.pbAbleNonActiveCount(user.idxOwnSide) == 0 - score -= 100 - else - score += target.stages[:ATTACK] * 10 - score += target.stages[:SPECIAL_ATTACK] * 10 - score -= user.hp * 100 / user.totalhp - end - #--------------------------------------------------------------------------- - when "UserFaintsHealAndCureReplacement", "UserFaintsHealAndCureReplacementRestorePP" - score -= 70 - #--------------------------------------------------------------------------- - when "StartPerishCountsForAllBattlers" - if @battle.pbAbleNonActiveCount(user.idxOwnSide) == 0 - score -= 90 - elsif target.effects[PBEffects::PerishSong] > 0 - score -= 90 - end - #--------------------------------------------------------------------------- - when "AttackerFaintsIfUserFaints" - score += 50 - score -= user.hp * 100 / user.totalhp - score += 30 if user.hp <= user.totalhp / 10 - #--------------------------------------------------------------------------- - when "SetAttackerMovePPTo0IfUserFaints" - score += 50 - score -= user.hp * 100 / user.totalhp - score += 30 if user.hp <= user.totalhp / 10 - #--------------------------------------------------------------------------- - when "UserTakesTargetItem" - if skill >= PBTrainerAI.highSkill - if !user.item && target.item - score += 40 - else - score -= 90 - end - else - score -= 80 - end - #--------------------------------------------------------------------------- - when "TargetTakesUserItem" - if !user.item || target.item - score -= 90 - elsif user.hasActiveItem?([:FLAMEORB, :TOXICORB, :STICKYBARB, :IRONBALL, - :CHOICEBAND, :CHOICESCARF, :CHOICESPECS]) - score += 50 - else - score -= 80 - end - #--------------------------------------------------------------------------- - when "UserTargetSwapItems" - if !user.item && !target.item - score -= 90 - elsif skill >= PBTrainerAI.highSkill && target.hasActiveAbility?(:STICKYHOLD) - score -= 90 - elsif user.hasActiveItem?([:FLAMEORB, :TOXICORB, :STICKYBARB, :IRONBALL, - :CHOICEBAND, :CHOICESCARF, :CHOICESPECS]) - score += 50 - elsif !user.item && target.item - score -= 30 if user.lastMoveUsed && - GameData::Move.get(user.lastMoveUsed).function_code == "UserTargetSwapItems" - end - #--------------------------------------------------------------------------- - when "RestoreUserConsumedItem" - if !user.recycleItem || user.item - score -= 80 - elsif user.recycleItem - score += 30 - end - #--------------------------------------------------------------------------- - when "RemoveTargetItem" - if skill >= PBTrainerAI.highSkill - score += 20 if target.item - end - #--------------------------------------------------------------------------- - when "DestroyTargetBerryOrGem" - if target.effects[PBEffects::Substitute] == 0 - if skill >= PBTrainerAI.highSkill && target.item && target.item.is_berry? - score += 30 - end - end - #--------------------------------------------------------------------------- - when "CorrodeTargetItem" - if @battle.corrosiveGas[target.index % 2][target.pokemonIndex] - score -= 100 - elsif !target.item || !target.itemActive? || target.unlosableItem?(target.item) || - target.hasActiveAbility?(:STICKYHOLD) - score -= 90 - elsif target.effects[PBEffects::Substitute] > 0 - score -= 90 - else - score += 50 - end - #--------------------------------------------------------------------------- - when "StartTargetCannotUseItem" - score -= 90 if target.effects[PBEffects::Embargo] > 0 - #--------------------------------------------------------------------------- - when "StartNegateHeldItems" - if @battle.field.effects[PBEffects::MagicRoom] > 0 - score -= 90 - elsif !user.item && target.item - score += 30 - end - #--------------------------------------------------------------------------- - when "UserConsumeBerryRaiseDefense2" - if !user.item || !user.item.is_berry? || !user.itemActive? - score -= 100 - else - if skill >= PBTrainerAI.highSkill - useful_berries = [ - :ORANBERRY, :SITRUSBERRY, :AGUAVBERRY, :APICOTBERRY, :CHERIBERRY, - :CHESTOBERRY, :FIGYBERRY, :GANLONBERRY, :IAPAPABERRY, :KEEBERRY, - :LANSATBERRY, :LEPPABERRY, :LIECHIBERRY, :LUMBERRY, :MAGOBERRY, - :MARANGABERRY, :PECHABERRY, :PERSIMBERRY, :PETAYABERRY, :RAWSTBERRY, - :SALACBERRY, :STARFBERRY, :WIKIBERRY - ] - score += 30 if useful_berries.include?(user.item_id) - end - if skill >= PBTrainerAI.mediumSkill - score += 20 if user.canHeal? && user.hp < user.totalhp / 3 && user.hasActiveAbility?(:CHEEKPOUCH) - score += 20 if user.hasActiveAbility?([:HARVEST, :RIPEN]) || - user.pbHasMoveFunction?("RestoreUserConsumedItem") # Recycle - score += 20 if !user.canConsumeBerry? - end - score -= user.stages[:DEFENSE] * 20 - end - #--------------------------------------------------------------------------- - when "AllBattlersConsumeBerry" - useful_berries = [ - :ORANBERRY, :SITRUSBERRY, :AGUAVBERRY, :APICOTBERRY, :CHERIBERRY, - :CHESTOBERRY, :FIGYBERRY, :GANLONBERRY, :IAPAPABERRY, :KEEBERRY, - :LANSATBERRY, :LEPPABERRY, :LIECHIBERRY, :LUMBERRY, :MAGOBERRY, - :MARANGABERRY, :PECHABERRY, :PERSIMBERRY, :PETAYABERRY, - :RAWSTBERRY, :SALACBERRY, :STARFBERRY, :WIKIBERRY - ] - @battle.allSameSideBattlers(user.index).each do |b| - if !b.item || !b.item.is_berry? || !b.itemActive? - score -= 100 / @battle.pbSideSize(user.index) - else - if skill >= PBTrainerAI.highSkill - amt = 30 / @battle.pbSideSize(user.index) - score += amt if useful_berries.include?(b.item_id) - end - if skill >= PBTrainerAI.mediumSkill - amt = 20 / @battle.pbSideSize(user.index) - score += amt if b.canHeal? && b.hp < b.totalhp / 3 && b.hasActiveAbility?(:CHEEKPOUCH) - score += amt if b.hasActiveAbility?([:HARVEST, :RIPEN]) || - b.pbHasMoveFunction?("RestoreUserConsumedItem") # Recycle - score += amt if !b.canConsumeBerry? - end - end - end - if skill >= PBTrainerAI.highSkill - @battle.allOtherSideBattlers(user.index).each do |b| - amt = 10 / @battle.pbSideSize(target.index) - score -= amt if b.hasActiveItem?(useful_berries) - score -= amt if b.canHeal? && b.hp < b.totalhp / 3 && b.hasActiveAbility?(:CHEEKPOUCH) - score -= amt if b.hasActiveAbility?([:HARVEST, :RIPEN]) || - b.pbHasMoveFunction?("RestoreUserConsumedItem") # Recycle - score -= amt if !b.canConsumeBerry? - end - end - #--------------------------------------------------------------------------- - when "UserConsumeTargetBerry" - if target.effects[PBEffects::Substitute] == 0 - if skill >= PBTrainerAI.highSkill && target.item && target.item.is_berry? - score += 30 - end - end - #--------------------------------------------------------------------------- - when "ThrowUserItemAtTarget" - if !user.item || !user.itemActive? || - user.unlosableItem?(user.item) || user.item.is_poke_ball? - score -= 90 - end - #--------------------------------------------------------------------------- - when "RedirectAllMovesToUser" - score -= 90 if user.allAllies.length == 0 - #--------------------------------------------------------------------------- - when "RedirectAllMovesToTarget" - score -= 90 if user.allAllies.length == 0 - #--------------------------------------------------------------------------- - when "CannotBeRedirected" - redirection = false - user.allOpposing.each do |b| - next if b.index == target.index - if b.effects[PBEffects::RagePowder] || - b.effects[PBEffects::Spotlight] > 0 || - b.effects[PBEffects::FollowMe] > 0 || - (b.hasActiveAbility?(:LIGHTNINGROD) && move.pbCalcType == :ELECTRIC) || - (b.hasActiveAbility?(:STORMDRAIN) && move.pbCalcType == :WATER) - redirection = true - break - end - end - score += 50 if redirection && skill >= PBTrainerAI.mediumSkill - #--------------------------------------------------------------------------- - when "RandomlyDamageOrHealTarget" - #--------------------------------------------------------------------------- - when "HealAllyOrDamageFoe" - if !target.opposes?(user) - if target.hp == target.totalhp || (skill >= PBTrainerAI.mediumSkill && !target.canHeal?) - score -= 90 - else - score += 50 - score -= target.hp * 100 / target.totalhp - end - end - #--------------------------------------------------------------------------- - when "CurseTargetOrLowerUserSpd1RaiseUserAtkDef1" - if user.pbHasType?(:GHOST) - if target.effects[PBEffects::Curse] - score -= 90 - elsif user.hp <= user.totalhp / 2 - if @battle.pbAbleNonActiveCount(user.idxOwnSide) == 0 - score -= 90 - else - score -= 50 - score -= 30 if @battle.switchStyle - end - end - else - avg = user.stages[:SPEED] * 10 - avg -= user.stages[:ATTACK] * 10 - avg -= user.stages[:DEFENSE] * 10 - score += avg / 3 - end - #--------------------------------------------------------------------------- - when "EffectDependsOnEnvironment" - #--------------------------------------------------------------------------- - when "HitsAllFoesAndPowersUpInPsychicTerrain" - score += 40 if @battle.field.terrain == :Psychic && user.affectedByTerrain? - #--------------------------------------------------------------------------- - when "TargetNextFireMoveDamagesTarget" - aspeed = pbRoughStat(user, :SPEED, skill) - ospeed = pbRoughStat(target, :SPEED, skill) - if aspeed > ospeed - score -= 90 - elsif target.pbHasMoveType?(:FIRE) - score += 30 - end - #--------------------------------------------------------------------------- - when "DoublePowerAfterFusionFlare" - #--------------------------------------------------------------------------- - when "DoublePowerAfterFusionBolt" - #--------------------------------------------------------------------------- - when "PowerUpAllyMove" - hasAlly = !user.allAllies.empty? - score += 30 if hasAlly - score -= 90 if !hasAlly - #--------------------------------------------------------------------------- - when "CounterPhysicalDamage" - if target.effects[PBEffects::HyperBeam] > 0 - score -= 90 - else - attack = pbRoughStat(user, :ATTACK, skill) - spatk = pbRoughStat(user, :SPECIAL_ATTACK, skill) - if attack * 1.5 < spatk - score -= 60 - elsif skill >= PBTrainerAI.mediumSkill && target.lastMoveUsed - moveData = GameData::Move.get(target.lastMoveUsed) - score += 60 if moveData.physical? - end - end - #--------------------------------------------------------------------------- - when "CounterSpecialDamage" - if target.effects[PBEffects::HyperBeam] > 0 - score -= 90 - else - attack = pbRoughStat(user, :ATTACK, skill) - spatk = pbRoughStat(user, :SPECIAL_ATTACK, skill) - if attack > spatk * 1.5 - score -= 60 - elsif skill >= PBTrainerAI.mediumSkill && target.lastMoveUsed - moveData = GameData::Move.get(target.lastMoveUsed) - score += 60 if moveData.special? - end - end - #--------------------------------------------------------------------------- - when "CounterDamagePlusHalf" - score -= 90 if target.effects[PBEffects::HyperBeam] > 0 - #--------------------------------------------------------------------------- - when "UserAddStockpileRaiseDefSpDef1" - avg = 0 - avg -= user.stages[:DEFENSE] * 10 - avg -= user.stages[:SPECIAL_DEFENSE] * 10 - score += avg / 2 - if user.effects[PBEffects::Stockpile] >= 3 - score -= 80 - elsif user.pbHasMoveFunction?("PowerDependsOnUserStockpile", - "HealUserDependingOnUserStockpile") # Spit Up, Swallow - score += 20 # More preferable if user also has Spit Up/Swallow - end - #--------------------------------------------------------------------------- - when "PowerDependsOnUserStockpile" - score -= 100 if user.effects[PBEffects::Stockpile] == 0 - #--------------------------------------------------------------------------- - when "HealUserDependingOnUserStockpile" - if user.effects[PBEffects::Stockpile] == 0 - score -= 90 - elsif user.hp == user.totalhp - score -= 90 - else - mult = [0, 25, 50, 100][user.effects[PBEffects::Stockpile]] - score += mult - score -= user.hp * mult * 2 / user.totalhp - end - #--------------------------------------------------------------------------- - when "GrassPledge" - #--------------------------------------------------------------------------- - when "FirePledge" - #--------------------------------------------------------------------------- - when "WaterPledge" - #--------------------------------------------------------------------------- - when "UseLastMoveUsed" - #--------------------------------------------------------------------------- - when "UseLastMoveUsedByTarget" - score -= 40 - if skill >= PBTrainerAI.highSkill - score -= 100 if !target.lastRegularMoveUsed || - GameData::Move.get(target.lastRegularMoveUsed).flags.none? { |f| f[/^CanMirrorMove$/i] } - end - #--------------------------------------------------------------------------- - when "UseMoveTargetIsAboutToUse" - #--------------------------------------------------------------------------- - when "UseMoveDependingOnEnvironment" - #--------------------------------------------------------------------------- - when "UseRandomMove" - #--------------------------------------------------------------------------- - when "UseRandomMoveFromUserParty" - #--------------------------------------------------------------------------- - when "UseRandomUserMoveIfAsleep" - if user.asleep? - score += 100 # Because it can only be used while asleep - else - score -= 90 - end - #--------------------------------------------------------------------------- - when "BounceBackProblemCausingStatusMoves" - #--------------------------------------------------------------------------- - when "StealAndUseBeneficialStatusMove" - #--------------------------------------------------------------------------- - when "ReplaceMoveThisBattleWithTargetLastMoveUsed" - moveBlacklist = [ - "Struggle", # Struggle - "ReplaceMoveThisBattleWithTargetLastMoveUsed", # Mimic - "ReplaceMoveWithTargetLastMoveUsed", # Sketch - "UseRandomMove" # Metronome - ] - if user.effects[PBEffects::Transform] || !target.lastRegularMoveUsed - score -= 90 - else - lastMoveData = GameData::Move.get(target.lastRegularMoveUsed) - if moveBlacklist.include?(lastMoveData.function_code) || - lastMoveData.type == :SHADOW - score -= 90 - end - user.eachMove do |m| - next if m != target.lastRegularMoveUsed - score -= 90 - break - end - end - #--------------------------------------------------------------------------- - when "ReplaceMoveWithTargetLastMoveUsed" - moveBlacklist = [ - "Struggle", # Struggle - "ReplaceMoveWithTargetLastMoveUsed" # Sketch - ] - if user.effects[PBEffects::Transform] || !target.lastRegularMoveUsed - score -= 90 - else - lastMoveData = GameData::Move.get(target.lastRegularMoveUsed) - if moveBlacklist.include?(lastMoveData.function_code) || - lastMoveData.type == :SHADOW - score -= 90 - end - user.eachMove do |m| - next if m != target.lastRegularMoveUsed - score -= 90 # User already knows the move that will be Sketched - break - end - end - #--------------------------------------------------------------------------- - when "FleeFromBattle" - score -= 100 if @battle.trainerBattle? - #--------------------------------------------------------------------------- - when "SwitchOutUserStatusMove" - if !@battle.pbCanChooseNonActive?(user.index) || - @battle.pbTeamAbleNonActiveCount(user.index) > 1 # Don't switch in ace - score -= 100 - else - score += 40 if user.effects[PBEffects::Confusion] > 0 - total = 0 - GameData::Stat.each_battle { |s| total += user.stages[s.id] } - if total <= 0 || user.turnCount == 0 - score += 60 - else - score -= total * 10 - # special case: user has no damaging moves - hasDamagingMove = false - user.eachMove do |m| - next if !m.damagingMove? - hasDamagingMove = true - break - end - score += 75 if !hasDamagingMove - end - end - #--------------------------------------------------------------------------- - when "SwitchOutUserDamagingMove" - if !@battle.pbCanChooseNonActive?(user.index) || - @battle.pbTeamAbleNonActiveCount(user.index) > 1 # Don't switch in ace - score -= 100 - end - #--------------------------------------------------------------------------- - when "LowerTargetAtkSpAtk1SwitchOutUser" - avg = target.stages[:ATTACK] * 10 - avg += target.stages[:SPECIAL_ATTACK] * 10 - score += avg / 2 - #--------------------------------------------------------------------------- - when "SwitchOutUserPassOnEffects" - if @battle.pbCanChooseNonActive?(user.index) - score -= 40 if user.effects[PBEffects::Confusion] > 0 - total = 0 - GameData::Stat.each_battle { |s| total += user.stages[s.id] } - if total <= 0 || user.turnCount == 0 - score -= 60 - else - score += total * 10 - # special case: user has no damaging moves - hasDamagingMove = false - user.eachMove do |m| - next if !m.damagingMove? - hasDamagingMove = true - break - end - score += 75 if !hasDamagingMove - end - else - score -= 100 - end - #--------------------------------------------------------------------------- - when "SwitchOutTargetStatusMove" - if target.effects[PBEffects::Ingrain] || - (skill >= PBTrainerAI.highSkill && target.hasActiveAbility?(:SUCTIONCUPS)) - score -= 90 - else - ch = 0 - @battle.pbParty(target.index).each_with_index do |pkmn, i| - ch += 1 if @battle.pbCanSwitchLax?(target.index, i) - end - score -= 90 if ch == 0 - end - if score > 20 - score += 50 if target.pbOwnSide.effects[PBEffects::Spikes] > 0 - score += 50 if target.pbOwnSide.effects[PBEffects::ToxicSpikes] > 0 - score += 50 if target.pbOwnSide.effects[PBEffects::StealthRock] - end - #--------------------------------------------------------------------------- - when "SwitchOutTargetDamagingMove" - if !target.effects[PBEffects::Ingrain] && - !(skill >= PBTrainerAI.highSkill && target.hasActiveAbility?(:SUCTIONCUPS)) - score += 40 if target.pbOwnSide.effects[PBEffects::Spikes] > 0 - score += 40 if target.pbOwnSide.effects[PBEffects::ToxicSpikes] > 0 - score += 40 if target.pbOwnSide.effects[PBEffects::StealthRock] - end - #--------------------------------------------------------------------------- - when "BindTarget" - score += 40 if target.effects[PBEffects::Trapping] == 0 - #--------------------------------------------------------------------------- - when "BindTargetDoublePowerIfTargetUnderwater" - score += 40 if target.effects[PBEffects::Trapping] == 0 - #--------------------------------------------------------------------------- - when "TrapTargetInBattle" - score -= 90 if target.effects[PBEffects::MeanLook] >= 0 - #--------------------------------------------------------------------------- - when "TrapTargetInBattleLowerTargetDefSpDef1EachTurn" - if target.effects[PBEffects::Octolock] >= 0 - score -= 100 - else - score += 30 if !target.trappedInBattle? - score -= 100 if !target.pbCanLowerStatStage?(:DEFENSE, user, move) && - !target.pbCanLowerStatStage?(:SPECIAL_DEFENSE, user, move) - end - #--------------------------------------------------------------------------- - when "TrapUserAndTargetInBattle" - if target.effects[PBEffects::JawLock] < 0 - score += 40 if !user.trappedInBattle? && !target.trappedInBattle? - end - #--------------------------------------------------------------------------- - when "TrapAllBattlersInBattleForOneTurn" - #--------------------------------------------------------------------------- - when "PursueSwitchingFoe" - #--------------------------------------------------------------------------- - when "UsedAfterUserTakesPhysicalDamage" - if skill >= PBTrainerAI.mediumSkill - hasPhysicalAttack = false - target.eachMove do |m| - next if !m.physicalMove?(m.type) - hasPhysicalAttack = true - break - end - score -= 80 if !hasPhysicalAttack - end - #--------------------------------------------------------------------------- - when "UsedAfterAllyRoundWithDoublePower" - if skill >= PBTrainerAI.mediumSkill - user.allAllies.each do |b| - next if !b.pbHasMove?(move.id) - score += 20 - end - end - #--------------------------------------------------------------------------- - when "TargetActsNext" - #--------------------------------------------------------------------------- - when "TargetActsLast" - #--------------------------------------------------------------------------- - when "TargetUsesItsLastUsedMoveAgain" - if skill >= PBTrainerAI.mediumSkill - if !target.lastRegularMoveUsed || - !target.pbHasMove?(target.lastRegularMoveUsed) || - target.usingMultiTurnAttack? - score -= 90 - else - # Without lots of code here to determine good/bad moves and relative - # speeds, using this move is likely to just be a waste of a turn - score -= 50 - end - end - #--------------------------------------------------------------------------- - when "StartSlowerBattlersActFirst" - #--------------------------------------------------------------------------- - when "HigherPriorityInGrassyTerrain" - if skill >= PBTrainerAI.mediumSkill && @battle.field.terrain == :Grassy - aspeed = pbRoughStat(user, :SPEED, skill) - ospeed = pbRoughStat(target, :SPEED, skill) - score += 40 if aspeed < ospeed - end - #--------------------------------------------------------------------------- - when "LowerPPOfTargetLastMoveBy3" - last_move = target.pbGetMoveWithID(target.lastRegularMoveUsed) - if last_move && last_move.total_pp > 0 && last_move.pp <= 3 - score += 50 - end - #--------------------------------------------------------------------------- - when "LowerPPOfTargetLastMoveBy4" - score -= 40 - #--------------------------------------------------------------------------- - when "DisableTargetLastMoveUsed" - score -= 90 if target.effects[PBEffects::Disable] > 0 - #--------------------------------------------------------------------------- - when "DisableTargetUsingSameMoveConsecutively" - score -= 90 if target.effects[PBEffects::Torment] - #--------------------------------------------------------------------------- - when "DisableTargetUsingDifferentMove" - aspeed = pbRoughStat(user, :SPEED, skill) - ospeed = pbRoughStat(target, :SPEED, skill) - if target.effects[PBEffects::Encore] > 0 - score -= 90 - elsif aspeed > ospeed - if target.lastRegularMoveUsed - moveData = GameData::Move.get(target.lastRegularMoveUsed) - if moveData.category == 2 && # Status move - [:User, :BothSides].include?(moveData.target) - score += 60 - elsif moveData.category != 2 && # Damaging move - moveData.target == :NearOther && - Effectiveness.ineffective?(pbCalcTypeMod(moveData.type, target, user)) - score += 60 - end - else - score -= 90 - end - end - #--------------------------------------------------------------------------- - when "DisableTargetStatusMoves" - score -= 90 if target.effects[PBEffects::Taunt] > 0 - #--------------------------------------------------------------------------- - when "DisableTargetHealingMoves" - score -= 90 if target.effects[PBEffects::HealBlock] > 0 - #--------------------------------------------------------------------------- - when "DisableTargetSoundMoves" - if target.effects[PBEffects::ThroatChop] == 0 && skill >= PBTrainerAI.highSkill - hasSoundMove = false - user.eachMove do |m| - next if !m.soundMove? - hasSoundMove = true - break - end - score += 40 if hasSoundMove - end - #--------------------------------------------------------------------------- - when "DisableTargetMovesKnownByUser" - score -= 90 if user.effects[PBEffects::Imprison] - #--------------------------------------------------------------------------- - when "AllBattlersLoseHalfHPUserSkipsNextTurn" - score += 20 # Shadow moves are more preferable - score += 20 if target.hp >= target.totalhp / 2 - score -= 20 if user.hp < user.hp / 2 - #--------------------------------------------------------------------------- - when "UserLosesHalfHP" - score += 20 # Shadow moves are more preferable - score -= 40 - #--------------------------------------------------------------------------- - when "StartShadowSkyWeather" - score += 20 # Shadow moves are more preferable - if @battle.pbCheckGlobalAbility(:AIRLOCK) || - @battle.pbCheckGlobalAbility(:CLOUDNINE) - score -= 90 - elsif @battle.field.weather == :ShadowSky - score -= 90 - end - #--------------------------------------------------------------------------- - when "RemoveAllScreens" - score += 20 # Shadow moves are more preferable - if target.pbOwnSide.effects[PBEffects::AuroraVeil] > 0 || - target.pbOwnSide.effects[PBEffects::Reflect] > 0 || - target.pbOwnSide.effects[PBEffects::LightScreen] > 0 || - target.pbOwnSide.effects[PBEffects::Safeguard] > 0 - score += 30 - score -= 90 if user.pbOwnSide.effects[PBEffects::AuroraVeil] > 0 || - user.pbOwnSide.effects[PBEffects::Reflect] > 0 || - user.pbOwnSide.effects[PBEffects::LightScreen] > 0 || - user.pbOwnSide.effects[PBEffects::Safeguard] > 0 - else - score -= 110 - end - #--------------------------------------------------------------------------- - else - return aiEffectScorePart2_pbGetMoveScoreFunctionCode(score, move, user, target, skill) - end - return score - end -end diff --git a/Data/Scripts/011_Battle/005_AI/008_AI_Move_Utilities.rb b/Data/Scripts/011_Battle/005_AI/008_AI_Move_Utilities.rb index a126bcfe3..f9ecceebe 100644 --- a/Data/Scripts/011_Battle/005_AI/008_AI_Move_Utilities.rb +++ b/Data/Scripts/011_Battle/005_AI/008_AI_Move_Utilities.rb @@ -2,21 +2,21 @@ class Battle::AI #============================================================================= # #============================================================================= - def pbTargetsMultiple?(move, user) - target_data = move.pbTarget(user) + def pbTargetsMultiple?(move) + target_data = move.pbTarget(@user) return false if target_data.num_targets <= 1 num_targets = 0 case target_data.id when :AllAllies - @battle.allSameSideBattlers(user).each { |b| num_targets += 1 if b.index != user.index } + @battle.allSameSideBattlers(@user).each { |b| num_targets += 1 if b.index != @user.index } when :UserAndAllies - @battle.allSameSideBattlers(user).each { |_b| num_targets += 1 } + @battle.allSameSideBattlers(@user).each { |_b| num_targets += 1 } when :AllNearFoes - @battle.allOtherSideBattlers(user).each { |b| num_targets += 1 if b.near?(user) } + @battle.allOtherSideBattlers(@user).each { |b| num_targets += 1 if b.near?(@user) } when :AllFoes - @battle.allOtherSideBattlers(user).each { |_b| num_targets += 1 } + @battle.allOtherSideBattlers(@user).each { |_b| num_targets += 1 } when :AllNearOthers - @battle.allBattlers.each { |b| num_targets += 1 if b.near?(user) } + @battle.allBattlers.each { |b| num_targets += 1 if b.near?(@user) } when :AllBattlers @battle.allBattlers.each { |_b| num_targets += 1 } end @@ -83,11 +83,11 @@ class Battle::AI # For switching. Determines the effectiveness of a potential switch-in against # an opposing battler. - def pbCalcTypeModPokemon(battlerThis, _battlerOther) - mod1 = Effectiveness.calculate(battlerThis.types[0], target.types[0], target.types[1]) + def pbCalcTypeModPokemon(pkmn, target) + mod1 = Effectiveness.calculate(pkmn.types[0], target.types[0], target.types[1]) mod2 = Effectiveness::NORMAL_EFFECTIVE - if battlerThis.types.length > 1 - mod2 = Effectiveness.calculate(battlerThis.types[1], target.types[0], target.types[1]) + if pkmn.types.length > 1 + mod2 = Effectiveness.calculate(pkmn.types[1], target.types[0], target.types[1]) mod2 = mod2.to_f / Effectivenesss::NORMAL_EFFECTIVE end return mod1 * mod2 @@ -96,15 +96,18 @@ class Battle::AI #============================================================================= # Immunity to a move because of the target's ability, item or other effects #============================================================================= - def pbCheckMoveImmunity(score, move, user, target, skill) - type = pbRoughType(move, user, skill) - typeMod = pbCalcTypeMod(type, user, target) + def pbCheckMoveImmunity(move, target) + # TODO: Add consideration of user's Mold Breaker. + move_type = pbRoughType(move) + typeMod = pbCalcTypeMod(move_type, @user, target) # Type effectiveness - return true if (move.damagingMove? && Effectiveness.ineffective?(typeMod)) || score <= 0 + return true if move.damagingMove? && Effectiveness.ineffective?(typeMod) # Immunity due to ability/item/other effects - if skill >= PBTrainerAI.mediumSkill - case type + if skill_check(AILevel.medium) + case move_type when :GROUND + # TODO: Split target.airborne? into separate parts to allow different + # skill levels to apply to each part. return true if target.airborne? && !move.hitsFlyingTargets? when :FIRE return true if target.hasActiveAbility?(:FLASHFIRE) @@ -117,24 +120,25 @@ class Battle::AI end return true if move.damagingMove? && Effectiveness.not_very_effective?(typeMod) && target.hasActiveAbility?(:WONDERGUARD) - return true if move.damagingMove? && user.index != target.index && !target.opposes?(user) && + return true if move.damagingMove? && @user.index != target.index && !target.opposes?(@user) && target.hasActiveAbility?(:TELEPATHY) return true if move.statusMove? && move.canMagicCoat? && target.hasActiveAbility?(:MAGICBOUNCE) && - target.opposes?(user) + target.opposes?(@user) return true if move.soundMove? && target.hasActiveAbility?(:SOUNDPROOF) return true if move.bombMove? && target.hasActiveAbility?(:BULLETPROOF) if move.powderMove? return true if target.pbHasType?(:GRASS) - return true if target.hasActiveAbility?(:OVERCOAT) - return true if target.hasActiveItem?(:SAFETYGOGGLES) + return true if skill_check(AILevel.best) && target.hasActiveAbility?(:OVERCOAT) + return true if skill_check(AILevel.high) && target.hasActiveItem?(:SAFETYGOGGLES) end return true if move.statusMove? && target.effects[PBEffects::Substitute] > 0 && - !move.ignoresSubstitute?(user) && user.index != target.index + !move.ignoresSubstitute?(@user) && @user.index != target.index return true if move.statusMove? && Settings::MECHANICS_GENERATION >= 7 && - user.hasActiveAbility?(:PRANKSTER) && target.pbHasType?(:DARK) && - target.opposes?(user) + @user.hasActiveAbility?(:PRANKSTER) && target.pbHasType?(:DARK) && + target.opposes?(@user) return true if move.priority > 0 && @battle.field.terrain == :Psychic && - target.affectedByTerrain? && target.opposes?(user) + target.affectedByTerrain? && target.opposes?(@user) + # TODO: Dazzling/Queenly Majesty go here. end return false end @@ -142,16 +146,14 @@ class Battle::AI #============================================================================= # Get approximate properties for a battler #============================================================================= - def pbRoughType(move, user, skill) + def pbRoughType(move) ret = move.type - if skill >= PBTrainerAI.highSkill - ret = move.pbCalcType(user) - end + ret = move.pbCalcType(@user) if skill_check(AILevel.high) return ret end - def pbRoughStat(battler, stat, skill) - return battler.pbSpeed if skill >= PBTrainerAI.highSkill && stat == :SPEED + def pbRoughStat(battler, stat) + return battler.pbSpeed if skill_check(AILevel.high) && stat == :SPEED stageMul = [2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8] stageDiv = [8, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2] stage = battler.stages[stat] + 6 @@ -169,25 +171,25 @@ class Battle::AI #============================================================================= # Get a better move's base damage value #============================================================================= - def pbMoveBaseDamage(move, user, target, skill) + def pbMoveBaseDamage(move, target) baseDmg = move.baseDamage baseDmg = 60 if baseDmg == 1 - return baseDmg if skill < PBTrainerAI.mediumSkill + return baseDmg if !skill_check(AILevel.medium) # Covers all function codes which have their own def pbBaseDamage case move.function # Sonic Boom, Dragon Rage, Super Fang, Night Shade, Endeavor when "FixedDamage20", "FixedDamage40", "FixedDamageHalfTargetHP", "FixedDamageUserLevel", "LowerTargetHPToUserHP" - baseDmg = move.pbFixedDamage(user, target) + baseDmg = move.pbFixedDamage(@user, target) when "FixedDamageUserLevelRandom" # Psywave - baseDmg = user.level + baseDmg = @user.level when "OHKO", "OHKOIce", "OHKOHitsUndergroundTarget" baseDmg = 200 when "CounterPhysicalDamage", "CounterSpecialDamage", "CounterDamagePlusHalf" baseDmg = 60 when "DoublePowerIfTargetUnderwater", "DoublePowerIfTargetUnderground", "BindTargetDoublePowerIfTargetUnderwater" - baseDmg = move.pbModifyDamage(baseDmg, user, target) + baseDmg = move.pbModifyDamage(baseDmg, @user, target) # Gust, Twister, Venoshock, Smelling Salts, Wake-Up Slap, Facade, Hex, Brine, # Retaliate, Weather Ball, Return, Frustration, Eruption, Crush Grip, # Stored Power, Punishment, Hidden Power, Fury Cutter, Echoed Voice, @@ -217,12 +219,12 @@ class Battle::AI "PowerHigherWithTargetWeight", "ThrowUserItemAtTarget", "PowerDependsOnUserStockpile" - baseDmg = move.pbBaseDamage(baseDmg, user, target) + baseDmg = move.pbBaseDamage(baseDmg, @user, target) when "DoublePowerIfUserHasNoItem" # Acrobatics - baseDmg *= 2 if !user.item || user.hasActiveItem?(:FLYINGGEM) + baseDmg *= 2 if !@user.item || @user.hasActiveItem?(:FLYINGGEM) when "PowerHigherWithTargetFasterThanUser" # Gyro Ball - targetSpeed = pbRoughStat(target, :SPEED, skill) - userSpeed = pbRoughStat(user, :SPEED, skill) + targetSpeed = pbRoughStat(target, :SPEED) + userSpeed = pbRoughStat(@user, :SPEED) baseDmg = [[(25 * targetSpeed / userSpeed).floor, 150].min, 1].max when "RandomlyDamageOrHealTarget" # Present baseDmg = 50 @@ -230,46 +232,46 @@ class Battle::AI baseDmg = 71 baseDmg *= 2 if target.inTwoTurnAttack?("TwoTurnAttackInvulnerableUnderground") # Dig when "TypeAndPowerDependOnUserBerry" # Natural Gift - baseDmg = move.pbNaturalGiftBaseDamage(user.item_id) + baseDmg = move.pbNaturalGiftBaseDamage(@user.item_id) when "PowerHigherWithUserHeavierThanTarget" # Heavy Slam - baseDmg = move.pbBaseDamage(baseDmg, user, target) - baseDmg *= 2 if Settings::MECHANICS_GENERATION >= 7 && skill >= PBTrainerAI.mediumSkill && + baseDmg = move.pbBaseDamage(baseDmg, @user, target) + baseDmg *= 2 if Settings::MECHANICS_GENERATION >= 7 && skill_check(AILevel.medium) && target.effects[PBEffects::Minimize] when "AlwaysCriticalHit", "HitTwoTimes", "HitTwoTimesPoisonTarget" # Frost Breath, Double Kick, Twineedle baseDmg *= 2 when "HitThreeTimesPowersUpWithEachHit" # Triple Kick baseDmg *= 6 # Hits do x1, x2, x3 baseDmg in turn, for x6 in total when "HitTwoToFiveTimes" # Fury Attack - if user.hasActiveAbility?(:SKILLLINK) + if @user.hasActiveAbility?(:SKILLLINK) baseDmg *= 5 else baseDmg = (baseDmg * 31 / 10).floor # Average damage dealt end when "HitTwoToFiveTimesOrThreeForAshGreninja" - if user.isSpecies?(:GRENINJA) && user.form == 2 + if @user.isSpecies?(:GRENINJA) && @user.form == 2 baseDmg *= 4 # 3 hits at 20 power = 4 hits at 15 power - elsif user.hasActiveAbility?(:SKILLLINK) + elsif @user.hasActiveAbility?(:SKILLLINK) baseDmg *= 5 else baseDmg = (baseDmg * 31 / 10).floor # Average damage dealt end when "HitOncePerUserTeamMember" # Beat Up mult = 0 - @battle.eachInTeamFromBattlerIndex(user.index) do |pkmn, _i| + @battle.eachInTeamFromBattlerIndex(@user.index) do |pkmn, _i| mult += 1 if pkmn&.able? && pkmn.status == :NONE end baseDmg *= mult when "TwoTurnAttackOneTurnInSun" # Solar Beam - baseDmg = move.pbBaseDamageMultiplier(baseDmg, user, target) + baseDmg = move.pbBaseDamageMultiplier(baseDmg, @user, target) when "MultiTurnAttackPowersUpEachTurn" # Rollout - baseDmg *= 2 if user.effects[PBEffects::DefenseCurl] + baseDmg *= 2 if @user.effects[PBEffects::DefenseCurl] when "MultiTurnAttackBideThenReturnDoubleDamage" # Bide baseDmg = 40 when "UserFaintsFixedDamageUserHP" # Final Gambit - baseDmg = user.hp + baseDmg = @user.hp when "EffectivenessIncludesFlyingType" # Flying Press if GameData::Type.exists?(:FLYING) - if skill >= PBTrainerAI.highSkill + if skill_check(AILevel.high) targetTypes = target.pbTypes(true) mult = Effectiveness.calculate( :FLYING, targetTypes[0], targetTypes[1], targetTypes[2] @@ -281,12 +283,12 @@ class Battle::AI end baseDmg = (baseDmg.to_f * mult / Effectiveness::NORMAL_EFFECTIVE).round end - baseDmg *= 2 if skill >= PBTrainerAI.mediumSkill && target.effects[PBEffects::Minimize] + baseDmg *= 2 if skill_check(AILevel.medium) && target.effects[PBEffects::Minimize] when "DoublePowerIfUserLastMoveFailed" # Stomping Tantrum - baseDmg *= 2 if user.lastRoundMoveFailed + baseDmg *= 2 if @user.lastRoundMoveFailed when "HitTwoTimesFlinchTarget" # Double Iron Bash baseDmg *= 2 - baseDmg *= 2 if skill >= PBTrainerAI.mediumSkill && target.effects[PBEffects::Minimize] + baseDmg *= 2 if skill_check(AILevel.medium) && target.effects[PBEffects::Minimize] end return baseDmg end @@ -294,29 +296,33 @@ class Battle::AI #============================================================================= # Damage calculation #============================================================================= - def pbRoughDamage(move, user, target, skill, baseDmg) + def pbRoughDamage(move, target, baseDmg) # Fixed damage moves return baseDmg if move.is_a?(Battle::Move::FixedDamageMove) + # Get the move's type - type = pbRoughType(move, user, skill) + type = pbRoughType(move) + ##### Calculate user's attack stat ##### - atk = pbRoughStat(user, :ATTACK, skill) + atk = pbRoughStat(@user, :ATTACK) if move.function == "UseTargetAttackInsteadOfUserAttack" # Foul Play - atk = pbRoughStat(target, :ATTACK, skill) + atk = pbRoughStat(target, :ATTACK) elsif move.function == "UseUserBaseDefenseInsteadOfUserBaseAttack" # Body Press - atk = pbRoughStat(user, :DEFENSE, skill) + atk = pbRoughStat(@user, :DEFENSE) elsif move.specialMove?(type) if move.function == "UseTargetAttackInsteadOfUserAttack" # Foul Play - atk = pbRoughStat(target, :SPECIAL_ATTACK, skill) + atk = pbRoughStat(target, :SPECIAL_ATTACK) else - atk = pbRoughStat(user, :SPECIAL_ATTACK, skill) + atk = pbRoughStat(@user, :SPECIAL_ATTACK) end end + ##### Calculate target's defense stat ##### - defense = pbRoughStat(target, :DEFENSE, skill) + defense = pbRoughStat(target, :DEFENSE) if move.specialMove?(type) && move.function != "UseTargetDefenseInsteadOfTargetSpDef" # Psyshock - defense = pbRoughStat(target, :SPECIAL_DEFENSE, skill) + defense = pbRoughStat(target, :SPECIAL_DEFENSE) end + ##### Calculate all multiplier effects ##### multipliers = { :base_damage_multiplier => 1.0, @@ -325,11 +331,9 @@ class Battle::AI :final_damage_multiplier => 1.0 } # Ability effects that alter damage - moldBreaker = false - if skill >= PBTrainerAI.highSkill && target.hasMoldBreaker? - moldBreaker = true - end - if skill >= PBTrainerAI.mediumSkill && user.abilityActive? + moldBreaker = skill_check(AILevel.high) && target.hasMoldBreaker? + + if skill_check(AILevel.medium) && @user.abilityActive? # NOTE: These abilities aren't suitable for checking at the start of the # round. abilityBlacklist = [:ANALYTIC, :SNIPER, :TINTEDLENS, :AERILATE, :PIXILATE, :REFRIGERATE] @@ -341,19 +345,21 @@ class Battle::AI end if canCheck Battle::AbilityEffects.triggerDamageCalcFromUser( - user.ability, user, target, move, multipliers, baseDmg, type + @user.ability, @user, target, move, multipliers, baseDmg, type ) end end - if skill >= PBTrainerAI.mediumSkill && !moldBreaker - user.allAllies.each do |b| + + if skill_check(AILevel.medium) && !moldBreaker + @user.allAllies.each do |b| next if !b.abilityActive? Battle::AbilityEffects.triggerDamageCalcFromAlly( - b.ability, user, target, move, multipliers, baseDmg, type + b.ability, @user, target, move, multipliers, baseDmg, type ) end end - if skill >= PBTrainerAI.bestSkill && !moldBreaker && target.abilityActive? + + if skill_check(AILevel.best) && !moldBreaker && target.abilityActive? # NOTE: These abilities aren't suitable for checking at the start of the # round. abilityBlacklist = [:FILTER, :SOLIDROCK] @@ -365,40 +371,45 @@ class Battle::AI end if canCheck Battle::AbilityEffects.triggerDamageCalcFromTarget( - target.ability, user, target, move, multipliers, baseDmg, type + target.ability, @user, target, move, multipliers, baseDmg, type ) end end - if skill >= PBTrainerAI.bestSkill && !moldBreaker + + if skill_check(AILevel.best) && !moldBreaker target.allAllies.each do |b| next if !b.abilityActive? Battle::AbilityEffects.triggerDamageCalcFromTargetAlly( - b.ability, user, target, move, multipliers, baseDmg, type + b.ability, @user, target, move, multipliers, baseDmg, type ) end end + # Item effects that alter damage # NOTE: Type-boosting gems aren't suitable for checking at the start of the # round. - if skill >= PBTrainerAI.mediumSkill && user.itemActive? + if skill_check(AILevel.medium) && @user.itemActive? # NOTE: These items aren't suitable for checking at the start of the # round. itemBlacklist = [:EXPERTBELT, :LIFEORB] - if !itemBlacklist.include?(user.item_id) + if !itemBlacklist.include?(@user.item_id) Battle::ItemEffects.triggerDamageCalcFromUser( - user.item, user, target, move, multipliers, baseDmg, type + @user.item, @user, target, move, multipliers, baseDmg, type ) - user.effects[PBEffects::GemConsumed] = nil # Untrigger consuming of Gems + @user.effects[PBEffects::GemConsumed] = nil # Untrigger consuming of Gems end + # TODO: Prefer (1.5x?) if item will be consumed and user has Unburden. end - if skill >= PBTrainerAI.bestSkill && + + if skill_check(AILevel.best) && target.itemActive? && target.item && !target.item.is_berry? Battle::ItemEffects.triggerDamageCalcFromTarget( - target.item, user, target, move, multipliers, baseDmg, type + target.item, @user, target, move, multipliers, baseDmg, type ) end + # Global abilities - if skill >= PBTrainerAI.mediumSkill && + if skill_check(AILevel.medium) && ((@battle.pbCheckGlobalAbility(:DARKAURA) && type == :DARK) || (@battle.pbCheckGlobalAbility(:FAIRYAURA) && type == :FAIRY)) if @battle.pbCheckGlobalAbility(:AURABREAK) @@ -407,20 +418,25 @@ class Battle::AI multipliers[:base_damage_multiplier] *= 4 / 3.0 end end + # Parental Bond - if skill >= PBTrainerAI.mediumSkill && user.hasActiveAbility?(:PARENTALBOND) + if skill_check(AILevel.medium) && @user.hasActiveAbility?(:PARENTALBOND) multipliers[:base_damage_multiplier] *= 1.25 end + # Me First # TODO + # Helping Hand - n/a + # Charge - if skill >= PBTrainerAI.mediumSkill && - user.effects[PBEffects::Charge] > 0 && type == :ELECTRIC + if skill_check(AILevel.medium) && + @user.effects[PBEffects::Charge] > 0 && type == :ELECTRIC multipliers[:base_damage_multiplier] *= 2 end + # Mud Sport and Water Sport - if skill >= PBTrainerAI.mediumSkill + if skill_check(AILevel.medium) if type == :ELECTRIC if @battle.allBattlers.any? { |b| b.effects[PBEffects::MudSport] } multipliers[:base_damage_multiplier] /= 3 @@ -438,34 +454,40 @@ class Battle::AI end end end + # Terrain moves - if skill >= PBTrainerAI.mediumSkill + if skill_check(AILevel.medium) case @battle.field.terrain when :Electric - multipliers[:base_damage_multiplier] *= 1.5 if type == :ELECTRIC && user.affectedByTerrain? + multipliers[:base_damage_multiplier] *= 1.5 if type == :ELECTRIC && @user.affectedByTerrain? when :Grassy - multipliers[:base_damage_multiplier] *= 1.5 if type == :GRASS && user.affectedByTerrain? + multipliers[:base_damage_multiplier] *= 1.5 if type == :GRASS && @user.affectedByTerrain? when :Psychic - multipliers[:base_damage_multiplier] *= 1.5 if type == :PSYCHIC && user.affectedByTerrain? + multipliers[:base_damage_multiplier] *= 1.5 if type == :PSYCHIC && @user.affectedByTerrain? when :Misty multipliers[:base_damage_multiplier] /= 2 if type == :DRAGON && target.affectedByTerrain? end end + # Badge multipliers - if skill >= PBTrainerAI.highSkill && @battle.internalBattle && target.pbOwnedByPlayer? + if skill_check(AILevel.high) && @battle.internalBattle && target.pbOwnedByPlayer? + # Don't need to check the Atk/Sp Atk-boosting badges because the AI + # won't control the player's Pokémon. if move.physicalMove?(type) && @battle.pbPlayer.badge_count >= Settings::NUM_BADGES_BOOST_DEFENSE multipliers[:defense_multiplier] *= 1.1 elsif move.specialMove?(type) && @battle.pbPlayer.badge_count >= Settings::NUM_BADGES_BOOST_SPDEF multipliers[:defense_multiplier] *= 1.1 end end + # Multi-targeting attacks - if skill >= PBTrainerAI.highSkill && pbTargetsMultiple?(move, user) + if skill_check(AILevel.high) && pbTargetsMultiple?(move) multipliers[:final_damage_multiplier] *= 0.75 end + # Weather - if skill >= PBTrainerAI.mediumSkill - case user.effectiveWeather + if skill_check(AILevel.medium) + case @user.effectiveWeather when :Sun, :HarshSun case type when :FIRE @@ -487,30 +509,36 @@ class Battle::AI end end end + # Critical hits - n/a + # Random variance - n/a + # STAB - if skill >= PBTrainerAI.mediumSkill && type && user.pbHasType?(type) - if user.hasActiveAbility?(:ADAPTABILITY) + if skill_check(AILevel.medium) && type && @user.pbHasType?(type) + if @user.hasActiveAbility?(:ADAPTABILITY) multipliers[:final_damage_multiplier] *= 2 else multipliers[:final_damage_multiplier] *= 1.5 end end + # Type effectiveness - if skill >= PBTrainerAI.mediumSkill - typemod = pbCalcTypeMod(type, user, target) + if skill_check(AILevel.medium) + typemod = pbCalcTypeMod(type, @user, target) multipliers[:final_damage_multiplier] *= typemod.to_f / Effectiveness::NORMAL_EFFECTIVE end + # Burn - if skill >= PBTrainerAI.highSkill && move.physicalMove?(type) && - user.status == :BURN && !user.hasActiveAbility?(:GUTS) && + if skill_check(AILevel.high) && move.physicalMove?(type) && + @user.status == :BURN && !@user.hasActiveAbility?(:GUTS) && !(Settings::MECHANICS_GENERATION >= 6 && move.function == "DoublePowerIfUserPoisonedBurnedParalyzed") # Facade multipliers[:final_damage_multiplier] /= 2 end + # Aurora Veil, Reflect, Light Screen - if skill >= PBTrainerAI.highSkill && !move.ignoresReflect? && !user.hasActiveAbility?(:INFILTRATOR) + if skill_check(AILevel.high) && !move.ignoresReflect? && !@user.hasActiveAbility?(:INFILTRATOR) if target.pbOwnSide.effects[PBEffects::AuroraVeil] > 0 if @battle.pbSideBattlerCount(target) > 1 multipliers[:final_damage_multiplier] *= 2 / 3.0 @@ -531,80 +559,93 @@ class Battle::AI end end end + # Minimize - if skill >= PBTrainerAI.highSkill && target.effects[PBEffects::Minimize] && move.tramplesMinimize? + if skill_check(AILevel.high) && target.effects[PBEffects::Minimize] && move.tramplesMinimize? multipliers[:final_damage_multiplier] *= 2 end + # Move-specific base damage modifiers # TODO + # Move-specific final damage modifiers # TODO + ##### Main damage calculation ##### baseDmg = [(baseDmg * multipliers[:base_damage_multiplier]).round, 1].max atk = [(atk * multipliers[:attack_multiplier]).round, 1].max defense = [(defense * multipliers[:defense_multiplier]).round, 1].max - damage = ((((2.0 * user.level / 5) + 2).floor * baseDmg * atk / defense).floor / 50).floor + 2 + damage = ((((2.0 * @user.level / 5) + 2).floor * baseDmg * atk / defense).floor / 50).floor + 2 damage = [(damage * multipliers[:final_damage_multiplier]).round, 1].max - # "AI-specific calculations below" - # Increased critical hit rates - if skill >= PBTrainerAI.mediumSkill - c = 0 - # Ability effects that alter critical hit rate - if c >= 0 && user.abilityActive? - c = Battle::AbilityEffects.triggerCriticalCalcFromUser(user.ability, user, target, c) - end - if skill >= PBTrainerAI.bestSkill && c >= 0 && !moldBreaker && target.abilityActive? - c = Battle::AbilityEffects.triggerCriticalCalcFromTarget(target.ability, user, target, c) - end - # Item effects that alter critical hit rate - if c >= 0 && user.itemActive? - c = Battle::ItemEffects.triggerCriticalCalcFromUser(user.item, user, target, c) - end - if skill >= PBTrainerAI.bestSkill && c >= 0 && target.itemActive? - c = Battle::ItemEffects.triggerCriticalCalcFromTarget(target.item, user, target, c) - end - # Other efffects - c = -1 if target.pbOwnSide.effects[PBEffects::LuckyChant] > 0 - if c >= 0 - c += 1 if move.highCriticalRate? - c += user.effects[PBEffects::FocusEnergy] - c += 1 if user.inHyperMode? && move.type == :SHADOW - end - if c >= 0 - c = 4 if c > 4 - damage += damage * 0.1 * c - end - end return damage.floor end + #============================================================================= + # Critical hit rate calculation + #============================================================================= + def pbRoughCriticalHitStage(move, target) + return -1 if target.pbOwnSide.effects[PBEffects::LuckyChant] > 0 + mold_breaker = (skill_check(AILevel.medium) && @user.hasMoldBreaker?) + crit_stage = 0 + # Ability effects that alter critical hit rate + if skill_check(AILevel.medium) && @user.abilityActive? + crit_stage = BattleHandlers.triggerCriticalCalcUserAbility(@user.ability, @user, target, crit_stage) + return -1 if crit_stage < 0 + end + if skill_check(AILevel.best) && !mold_breaker && target.abilityActive? + crit_stage = BattleHandlers.triggerCriticalCalcTargetAbility(target.ability, @user, target, crit_stage) + return -1 if crit_stage < 0 + end + # Item effects that alter critical hit rate + if skill_check(AILevel.medium) && @user.itemActive? + crit_stage = BattleHandlers.triggerCriticalCalcUserItem(@user.item, @user, target, crit_stage) + return -1 if crit_stage < 0 + end + if skill_check(AILevel.high) && target.itemActive? + crit_stage = BattleHandlers.triggerCriticalCalcTargetItem(target.item, @user, target, crit_stage) + return -1 if crit_stage < 0 + end + # Other effects + case move.pbCritialOverride(@user, target) + when 1 then return 99 + when -1 then return -1 + end + return 99 if crit_stage > 50 # Merciless + return 99 if @user.effects[PBEffects::LaserFocus] > 0 + crit_stage += 1 if move.highCriticalRate? + crit_stage += @user.effects[PBEffects::FocusEnergy] + crit_stage += 1 if @user.inHyperMode? && move.type == :SHADOW + crit_stage = [crit_stage, Battle::Move::CRITICAL_HIT_RATIOS.length - 1].min + return crit_stage + end + #============================================================================= # Accuracy calculation #============================================================================= - def pbRoughAccuracy(move, user, target, skill) + def pbRoughAccuracy(move, target) # "Always hit" effects and "always hit" accuracy - if skill >= PBTrainerAI.mediumSkill - return 125 if target.effects[PBEffects::Minimize] && move.tramplesMinimize? && + if skill_check(AILevel.medium) + return 100 if target.effects[PBEffects::Minimize] && move.tramplesMinimize? && Settings::MECHANICS_GENERATION >= 6 - return 125 if target.effects[PBEffects::Telekinesis] > 0 + return 100 if target.effects[PBEffects::Telekinesis] > 0 end + # Get base accuracy baseAcc = move.accuracy - if skill >= PBTrainerAI.highSkill - baseAcc = move.pbBaseAccuracy(user, target) - end - return 125 if baseAcc == 0 && skill >= PBTrainerAI.mediumSkill + baseAcc = move.pbBaseAccuracy(@user, target) if skill_check(AILevel.medium) + return 100 if baseAcc == 0 && skill_check(AILevel.medium) # Get the move's type - type = pbRoughType(move, user, skill) + type = pbRoughType(move) # Calculate all modifier effects modifiers = {} modifiers[:base_accuracy] = baseAcc - modifiers[:accuracy_stage] = user.stages[:ACCURACY] + modifiers[:accuracy_stage] = @user.stages[:ACCURACY] modifiers[:evasion_stage] = target.stages[:EVASION] modifiers[:accuracy_multiplier] = 1.0 modifiers[:evasion_multiplier] = 1.0 - pbCalcAccuracyModifiers(user, target, modifiers, move, type, skill) - # Check if move can't miss - return 125 if modifiers[:base_accuracy] == 0 + pbCalcAccuracyModifiers(target, modifiers, move, type) + # Check if move certainly misses/can't miss + return 0 if modifiers[:base_accuracy] < 0 + return 100 if modifiers[:base_accuracy] == 0 # Calculation accStage = [[modifiers[:accuracy_stage], -6].max, 6].min + 6 evaStage = [[modifiers[:evasion_stage], -6].max, 6].min + 6 @@ -618,70 +659,82 @@ class Battle::AI return modifiers[:base_accuracy] * accuracy / evasion end - def pbCalcAccuracyModifiers(user, target, modifiers, move, type, skill) - moldBreaker = false - if skill >= PBTrainerAI.highSkill && target.hasMoldBreaker? - moldBreaker = true - end + def pbCalcAccuracyModifiers(target, modifiers, move, type) + moldBreaker = (skill_check(AILevel.medium) && target.hasMoldBreaker?) # Ability effects that alter accuracy calculation - if skill >= PBTrainerAI.mediumSkill - if user.abilityActive? - Battle::AbilityEffects.triggerAccuracyCalcFromUser( - user.ability, modifiers, user, target, move, type - ) - end - user.allAllies.each do |b| + if skill_check(AILevel.medium) && @user.abilityActive? + Battle::AbilityEffects.triggerAccuracyCalcFromUser( + @user.ability, modifiers, @user, target, move, type + ) + end + if skill_check(AILevel.high) + @user.allAllies.each do |b| next if !b.abilityActive? Battle::AbilityEffects.triggerAccuracyCalcFromAlly( - b.ability, modifiers, user, target, move, type + b.ability, modifiers, @user, target, move, type ) end end - if skill >= PBTrainerAI.bestSkill && target.abilityActive? && !moldBreaker + if skill_check(AILevel.best) && target.abilityActive? && !moldBreaker Battle::AbilityEffects.triggerAccuracyCalcFromTarget( - target.ability, modifiers, user, target, move, type + target.ability, modifiers, @user, target, move, type ) end # Item effects that alter accuracy calculation - if skill >= PBTrainerAI.mediumSkill && user.itemActive? + if skill_check(AILevel.medium) && @user.itemActive? + # TODO: Zoom Lens needs to be checked differently (compare speeds of + # user and target). Battle::ItemEffects.triggerAccuracyCalcFromUser( - user.item, modifiers, user, target, move, type + @user.item, modifiers, @user, target, move, type ) end - if skill >= PBTrainerAI.bestSkill && target.itemActive? + if skill_check(AILevel.high) && target.itemActive? Battle::ItemEffects.triggerAccuracyCalcFromTarget( - target.item, modifiers, user, target, move, type + target.item, modifiers, @user, target, move, type ) end # Other effects, inc. ones that set accuracy_multiplier or evasion_stage to specific values - if skill >= PBTrainerAI.mediumSkill - if @battle.field.effects[PBEffects::Gravity] > 0 - modifiers[:accuracy_multiplier] *= 5 / 3.0 - end - if user.effects[PBEffects::MicleBerry] + if @battle.field.effects[PBEffects::Gravity] > 0 + modifiers[:accuracy_multiplier] *= 5 / 3.0 + end + if skill_check(AILevel.medium) + if @user.effects[PBEffects::MicleBerry] modifiers[:accuracy_multiplier] *= 1.2 end modifiers[:evasion_stage] = 0 if target.effects[PBEffects::Foresight] && modifiers[:evasion_stage] > 0 modifiers[:evasion_stage] = 0 if target.effects[PBEffects::MiracleEye] && modifiers[:evasion_stage] > 0 end # "AI-specific calculations below" - if skill >= PBTrainerAI.mediumSkill - modifiers[:evasion_stage] = 0 if move.function == "IgnoreTargetDefSpDefEvaStatStages" # Chip Away - modifiers[:base_accuracy] = 0 if user.effects[PBEffects::LockOn] > 0 && - user.effects[PBEffects::LockOnPos] == target.index + modifiers[:evasion_stage] = 0 if move.function == "IgnoreTargetDefSpDefEvaStatStages" # Chip Away + if skill_check(AILevel.medium) + modifiers[:base_accuracy] = 0 if @user.effects[PBEffects::LockOn] > 0 && + @user.effects[PBEffects::LockOnPos] == target.index end - if skill >= PBTrainerAI.highSkill + if skill_check(AILevel.medium) if move.function == "BadPoisonTarget" && # Toxic - Settings::MORE_TYPE_EFFECTS && move.statusMove? && user.pbHasType?(:POISON) + Settings::MORE_TYPE_EFFECTS && move.statusMove? && @user.pbHasType?(:POISON) modifiers[:base_accuracy] = 0 end if ["OHKO", "OHKOIce", "OHKOHitsUndergroundTarget"].include?(move.function) - modifiers[:base_accuracy] = move.accuracy + user.level - target.level - modifiers[:accuracy_multiplier] = 0 if target.level > user.level - if skill >= PBTrainerAI.bestSkill && target.hasActiveAbility?(:STURDY) + modifiers[:base_accuracy] = move.accuracy + @user.level - target.level + modifiers[:accuracy_multiplier] = 0 if target.level > @user.level + if skill_check(AILevel.best) && target.hasActiveAbility?(:STURDY) modifiers[:accuracy_multiplier] = 0 end end end end + + #============================================================================= + # Check if battler has a move that meets the criteria in the block provided + #============================================================================= + def check_for_move(battler) + ret = false + battler.eachMove do |move| + next unless yield move + ret = true + break + end + return ret + end end diff --git a/Data/Scripts/011_Battle/005_AI/009_AI_Roles.rb b/Data/Scripts/011_Battle/005_AI/009_AI_Roles.rb new file mode 100644 index 000000000..13a813726 --- /dev/null +++ b/Data/Scripts/011_Battle/005_AI/009_AI_Roles.rb @@ -0,0 +1,169 @@ +class Battle::AI + #============================================================================= + # + #============================================================================= + # TODO: 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. + module BattleRole + PHAZER = 0 + CLERIC = 1 + STALLBREAKER = 2 + STATUSABSORBER = 3 + BATONPASSER = 4 + SPINNER = 5 + FIELDSETTER = 6 + WEATHERSETTER = 7 + SWEEPER = 8 + PIVOT = 9 + PHYSICALWALL = 10 + SPECIALWALL = 11 + TANK = 12 + TRAPPER = 13 + SCREENER = 14 + ACE = 15 + LEAD = 16 + SECOND = 17 + end + + #============================================================================= + # Determine the roles filled by a Pokémon on a given side at a given party + # index. + #============================================================================= + 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 + hasPivotMove = 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(BattleRole::PHAZER) + when "CureUserPartyStatus" # Aromatherapy/Heal Bell + ret.push(BattleRole::CLERIC) + when "DisableTargetStatusMoves" # Taunt + ret.push(BattleRole::STALLBREAKER) + when "HealUserPositionNextTurn" # Wish + ret.push(BattleRole::CLERIC) if pkmn.ev[:HP] == Pokemon::EV_STAT_LIMIT + when "HealUserFullyAndFallAsleep" # Rest + ret.push(BattleRole::STATUSABSORBER) + when "SwitchOutUserPassOnEffects" # Baton Pass + ret.push(BattleRole::BATONPASSER) + when "SwitchOutUserStatusMove", "SwitchOutUserDamagingMove" # Teleport (Gen 8+), U-turn + hasPivotMove = true + when "RemoveUserBindingAndEntryHazards" # Rapid Spin + ret.push(BattleRole::SPINNER) + when "StartElectricTerrain", "StartGrassyTerrain", + "StartMistyTerrain", "StartPsychicTerrain" # Terrain moves + ret.push(BattleRole::FIELDSETTER) + else + ret.push(BattleRole::WEATHERSETTER) 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(BattleRole::SWEEPER) + end + end + if hasHealMove + ret.push(BattleRole::PIVOT) if hasPivotMove + 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(BattleRole::PHYSICALWALL) 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(BattleRole::SPECIALWALL) if pkmn.ev[:SPECIAL_DEFENSE] == Pokemon::EV_STAT_LIMIT + end + else + ret.push(BattleRole::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(BattleRole::PIVOT) + when :GUTS, :QUICKFEET, :FLAREBOOST, :TOXICBOOST, :NATURALCURE, :MAGICGUARD, + :MAGICBOUNCE, :HYDRATION + ret.push(BattleRole::STATUSABSORBER) + when :SHADOWTAG, :ARENATRAP, :MAGNETPULL + ret.push(BattleRole::TRAPPER) + when :DROUGHT, :DRIZZLE, :SANDSTREAM, :SNOWWARNING, :PRIMORDIALSEA, + :DESOLATELAND, :DELTASTREAM + ret.push(BattleRole::WEATHERSETTER) + when :GRASSYSURGE, :ELECTRICSURGE, :MISTYSURGE, :PSYCHICSURGE + ret.push(BattleRole::FIELDSETTER) + end + + # Check for items indicative of particular roles + case pkmn.item_id + when :LIGHTCLAY + ret.push(BattleRole::SCREENER) + when :ASSAULTVEST + ret.push(BattleRole::TANK) + when :CHOICEBAND, :CHOICESPECS + ret.push(BattleRole::STALLBREAKER) + ret.push(BattleRole::SWEEPER) if pkmn.ev[:SPEED] == Pokemon::EV_STAT_LIMIT + when :CHOICESCARF + ret.push(BattleRole::SWEEPER) if pkmn.ev[:SPEED] == Pokemon::EV_STAT_LIMIT + when :TOXICORB, :FLAMEORB + ret.push(BattleRole::STATUSABSORBER) + when :TERRAINEXTENDER + ret.push(BattleRole::FIELDSETTER) + 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(BattleRole::ACE) + else + ret.push(BattleRole::LEAD) if partyStarts.include?(index) + idxTrainer = @battle.pbGetOwnerIndexFromPartyIndex(side, index) + maxLevel = @battle.pbMaxLevelInTeam(side, idxTrainer) + if pkmn.level >= maxLevel + ret.push(BattleRole::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(BattleRole::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 diff --git a/Data/Scripts/011_Battle/005_AI/020_AI_MoveEffectScores_Generic.rb b/Data/Scripts/011_Battle/005_AI/020_AI_MoveEffectScores_Generic.rb new file mode 100644 index 000000000..2f7532e5d --- /dev/null +++ b/Data/Scripts/011_Battle/005_AI/020_AI_MoveEffectScores_Generic.rb @@ -0,0 +1,431 @@ +class Battle::AI + #============================================================================= + # Apply additional effect chance to a move's score + # TODO: Apply all the additional effect chance modifiers. + #============================================================================= + def apply_effect_chance_to_score(score) + if @move.damagingMove? + # TODO: Doesn't return the correct value for "014" (Chatter). + effect_chance = @move.addlEffect + if effect_chance > 0 + effect_chance *= 2 if @user.hasActiveAbility?(:SERENEGRACE) || + @user.pbOwnSide.effects[PBEffects::Rainbow] > 0 + effect_multiplier = [effect_chance.to_f, 100].min / 100 + score = ((score - 1) * effect_multiplier) + 1 + end + end + return score + end + + #============================================================================= + # + #============================================================================= + # TODO: These function codes need to have an attr_reader :statUp and for them + # to be set when the move is initialised. + # 035 Shell Smash + # 037 Acupressure + # 137 Magnetic Flux + # 15C Gear Up + def calc_user_stat_raise_mini_score + mini_score = 1.0 + # Determine whether the move boosts Attack, Special Attack or Speed (Bulk Up + # is sometimes not considered a sweeping move) + sweeping_stat = false + offensive_stat = false + @move.stat_up.each_with_index do |stat, idx| + next if idx.odd? + next if ![:ATTACK, :SPATK, :SPEED].include?(stat) + sweeping_stat = true + next if @move.function == "024" # Bulk Up (+Atk +Def) + offensive_stat = true + break + end + + # Prefer if user has most of its HP + if @user.hp >= @user.totalhp * 3 / 4 + mini_score *= (sweeping_stat) ? 1.2 : 1.1 + end + # Prefer if user hasn't been in battle for long + if @user.turnCount < 2 + mini_score *= (sweeping_stat) ? 1.2 : 1.1 + end + # Prefer if user has the ability Simple + mini_score *= 2 if @user.hasActiveAbility?(:SIMPLE) + # TODO: Prefer if user's moves won't do much damage. + # Prefer if user has something that will limit damage taken + mini_score *= 1.3 if @user.effects[PBEffects::Substitute] > 0 || + (@user.form == 0 && @user.ability_id == :DISGUISE) + + # Don't prefer if user doesn't have much HP left + mini_score *= 0.3 if @user.hp < @user.totalhp / 3 + # Don't prefer if user is badly poisoned + mini_score *= 0.2 if @user.effects[PBEffects::Toxic] > 0 && !offensive_stat + # Don't prefer if user is confused + if @user.effects[PBEffects::Confusion] > 0 + # TODO: Especially don't prefer if the move raises Atk. Even more so if + # the move raises the stat by 2+. Not quite so much if the move also + # raises Def. + mini_score *= 0.5 + end + # Don't prefer if user is infatuated or Leech Seeded + if @user.effects[PBEffects::Attract] >= 0 || @user.effects[PBEffects::LeechSeed] >= 0 + mini_score *= (offensive_stat) ? 0.6 : 0.3 + end + # Don't prefer if user has an ability or item that will force it to switch + # out + if @user.hp < @user.totalhp * 3 / 4 + mini_score *= 0.3 if @user.hasActiveAbility?([:EMERGENCYEXIT, :WIMPOUT]) + mini_score *= 0.3 if @user.hasActiveItem?(:EJECTBUTTON) + end + + # Prefer if target has a status problem + if @target.status != PBStatuses::NONE + mini_score *= (sweeping_stat) ? 1.2 : 1.1 + case @target.status + when PBStatuses::SLEEP, PBStatuses::FROZEN + mini_score *= 1.3 + when PBStatuses::BURN + # TODO: Prefer if the move boosts Sp Def. + mini_score *= 1.1 if !offensive_stat + end + end + # Prefer if target is yawning + if @target.effects[PBEffects::Yawn] > 0 + mini_score *= (sweeping_stat) ? 1.7 : 1.3 + end + # Prefer if target is recovering after Hyper Beam + if @target.effects[PBEffects::HyperBeam] > 0 + mini_score *= (sweeping_stat) ? 1.3 : 1.2 + end + # Prefer if target is Encored into a status move + if @target.effects[PBEffects::Encore] > 0 && + GameData::Move.get(@target.effects[PBEffects::EncoreMove]).category == 2 # Status move + # TODO: Why should this check greatly prefer raising both the user's defences? + if sweeping_stat || @move.function == "02A" # +Def +SpDef + mini_score *= 1.5 + else + mini_score *= 1.3 + end + end + # TODO: Don't prefer if target has previously used a move that would force + # the user to switch (or Yawn/Perish Song which encourage it). Prefer + # instead if the move raises evasion. Note this comes after the + # dissociation of Bulk Up from sweeping_stat. + + if skill_check(AILevel.medium) + # TODO: Prefer if the maximum damage the target has dealt wouldn't hurt + # the user much. + end + # Don't prefer if foe's side is able to use a boosted Retaliate + # TODO: I think this is what Reborn means. Reborn doesn't check for the + # existence of the move Retaliate, just whether it can be boosted. + if @user.pbOpposingSide.effects[PBEffects::LastRoundFainted] == @battle.turnCount - 1 + mini_score *= 0.3 + end + + # Don't prefer if it's not a single battle + if !@battle.singleBattle? + mini_score *= (offensive_stat) ? 0.25 : 0.5 + end + + return mini_score + end + + #============================================================================= + # + #============================================================================= + # TODO: This method doesn't take the increment into account but should. + def calc_user_stat_raise_one(stat, increment) + mini_score = 1.0 + + # Ignore if user won't benefit from the stat being raised + # TODO: Exception if user knows Baton Pass? Exception if user knows Power Trip? + case stat + when :ATTACK + has_physical_move = false + @user.eachMove do |m| + next if !m.physicalMove?(m.type) || m.function == "121" # Foul Play + has_physical_move = true + break + end + return mini_score if !has_physical_move + when :SPECIAL_ATTACK + has_special_move = false + @user.eachMove do |m| + next if !m.specialMove?(m.type) + has_special_move = true + break + end + return mini_score if !has_special_move + end + + case stat + when :ATTACK + # Prefer if user can definitely survive a hit no matter how powerful, and + # it won't be hurt by weather + if @user.hp == @user.totalhp && + (@user.hasActiveItem?(:FOCUSSASH) || @user.hasActiveAbility?(:STURDY)) + if !(@battle.pbWeather == PBWeather::Sandstorm && @user.takesSandstormDamage?) && + !(@battle.pbWeather == PBWeather::Hail && @user.takesHailDamage?) && + !(@battle.pbWeather == PBWeather::ShadowSky && @user.takesShadowSkyDamage?) + mini_score *= 1.4 + end + end + # Prefer if user has the Sweeper role + # TODO: Is 1.1x for 025 Coil (+Atk, +Def, +acc). + mini_score *= 1.3 if check_battler_role(@user, BattleRole::SWEEPER) + # Don't prefer if user is burned or paralysed + mini_score *= 0.5 if @user.status == PBStatuses::BURN || @user.status == PBStatuses::PARALYSIS + # Don't prefer if user's Speed stat is lowered + sum_stages = @user.stages[:SPEED] + mini_score *= 1 + sum_stages * 0.05 if sum_stages < 0 + + # TODO: Prefer if target has previously used a HP-restoring move. + # TODO: Don't prefer if some of foes' stats are raised + sum_stages = 0 + [:ATTACK, :SPECIAL_ATTACK, :SPEED].each do |s| + sum_stages += @target.stages[s] + end + mini_score *= 1 - sum_stages * 0.05 if sum_stages > 0 + # TODO: Don't prefer if target has Speed Boost (+Spd at end of each round). + mini_score *= 0.6 if @target.hasActiveAbility?(:SPEEDBOOST) + # TODO: Don't prefer if target has previously used a move that benefits + # from user's Attack being boosted. + mini_score *= 0.3 if check_for_move(@target) { |move| move.function == "121" } # Foul Play + # TODO: Don't prefer if the target has previously used a priority move. + + when :DEFENSE + # Prefer if user has a healing item + # TODO: Is 1.1x for 025 Coil (+Atk, +Def, +acc). + mini_score *= 1.2 if @user.hasActiveItem?(:LEFTOVERS) || + (@user.hasActiveItem?(:BLACKSLUDGE) && @user.pbHasType?(:POISON)) + # Prefer if user knows any healing moves + # TODO: Is 1.2x for 025 Coil (+Atk, +Def, +acc). + mini_score *= 1.3 if check_for_move(@user) { |move| move.healingMove? } + # Prefer if user knows Pain Split or Leech Seed + # TODO: Leech Seed is 1.2x for 025 Coil (+Atk, +Def, +acc). + mini_score *= 1.2 if @user.pbHasMoveFunction?("05A") # Pain Split + mini_score *= 1.3 if @user.pbHasMoveFunction?("0DC") # Leech Seed + # Prefer if user has certain roles + # TODO: Is 1.1x for 025 Coil (+Atk, +Def, +acc). + mini_score *= 1.3 if check_battler_role(@user, BattleRole::PHYSICALWALL, BattleRole::SPECIALWALL) + # Don't prefer if user is badly poisoned + mini_score *= 0.2 if @user.effects[PBEffects::Toxic] > 0 + # Don't prefer if user's Defense stat is raised + sum_stages = @user.stages[:DEFENSE] + mini_score *= 1 - sum_stages * 0.15 if sum_stages > 0 + + # TODO: Prefer if foes have higher Attack than Special Attack, and user + # doesn't have a wall role, user is faster and user has at least 75% + # HP. Don't prefer instead if user is slower (ignore HP). + + # TODO: Don't prefer if previous damage done by foes wouldn't hurt the + # user much. + + when :SPEED + # Prefer if user can definitely survive a hit no matter how powerful, and + # it won't be hurt by weather + if @user.hp == @user.totalhp && + (@user.hasActiveItem?(:FOCUSSASH) || @user.hasActiveAbility?(:STURDY)) + if !(@battle.pbWeather == PBWeather::Sandstorm && @user.takesSandstormDamage?) && + !(@battle.pbWeather == PBWeather::Hail && @user.takesHailDamage?) && + !(@battle.pbWeather == PBWeather::ShadowSky && @user.takesShadowSkyDamage?) + mini_score *= 1.4 + end + end + # Prefer if user's Attack/SpAtk stat (whichever is higher) is lowered + # TODO: Why? + if @user.attack > @user.spatk + sum_stages = @user.stages[:ATTACK] + mini_score *= 1 - sum_stages * 0.05 if sum_stages < 0 + else + sum_stages = @user.stages[:SPATK] + mini_score *= 1 - sum_stages * 0.05 if sum_stages < 0 + end + # Prefer if user has lowered Speed + # TODO: Is a flat 1.3x for 026 Dragon Dance (+Atk, +Spd). + sum_stages = @user.stages[:SPEED] + mini_score *= 1 - sum_stages * 0.05 if sum_stages < 0 + # Prefer if user has Moxie + mini_score *= 1.3 if @user.hasActiveAbility?(:MOXIE) + # Prefer if user has the Sweeper role + mini_score *= 1.3 if check_battler_role(@user, BattleRole::SWEEPER) + # Don't prefer if user is burned or paralysed + mini_score *= 0.2 if @user.status == PBStatuses::PARALYSIS + # Don't prefer if user has Speed Boost + mini_score *= 0.6 if @user.hasActiveAbility?(:SPEEDBOOST) + + # TODO: Don't prefer if target has raised defenses. + sum_stages = 0 + [:DEFENSE, :SPECIAL_DEFENSE].each { |s| sum_stages += @target.stages[s] } + mini_score *= 1 - sum_stages * 0.05 if sum_stages > 0 + # TODO: Don't prefer if the target has previously used a priority move. + # TODO: Don't prefer if Trick Room applies or any foe has previously used + # Trick Room. + mini_score *= 0.2 if @battle.field.effects[PBEffects::TrickRoom] > 0 + + # TODO: Don't prefer if user is already faster than the target. Exception + # for moves that benefit from a raised user's Speed? + # TODO: Don't prefer if user is already faster than the target and there's + # only 1 unfainted foe (this check is done by Agility/Autotomize + # (both +2 Spd) only in Reborn.) + + when :SPECIAL_ATTACK + # Prefer if user can definitely survive a hit no matter how powerful, and + # it won't be hurt by weather + if @user.hp == @user.totalhp && + (@user.hasActiveItem?(:FOCUSSASH) || @user.hasActiveAbility?(:STURDY)) + if !(@battle.pbWeather == PBWeather::Sandstorm && @user.takesSandstormDamage?) && + !(@battle.pbWeather == PBWeather::Hail && @user.takesHailDamage?) && + !(@battle.pbWeather == PBWeather::ShadowSky && @user.takesShadowSkyDamage?) + mini_score *= 1.4 + end + end + # Prefer if user has the Sweeper role + mini_score *= 1.3 if check_battler_role(@user, BattleRole::SWEEPER) + # Don't prefer if user's Speed stat is lowered + sum_stages = @user.stages[:SPEED] + mini_score *= 1 + sum_stages * 0.05 if sum_stages < 0 + + # TODO: Prefer if target has previously used a HP-restoring move. + # TODO: Don't prefer if some of foes' stats are raised + sum_stages = 0 + [:ATTACK, :SPECIAL_ATTACK, :SPEED].each do |s| + sum_stages += @target.stages[s] + end + mini_score *= 1 - sum_stages * 0.05 if sum_stages > 0 + # TODO: Don't prefer if target has Speed Boost (+Spd at end of each round) + mini_score *= 0.6 if @target.hasActiveAbility?(:SPEEDBOOST) + # TODO: Don't prefer if the target has previously used a priority move. + + when :SPECIAL_DEFENSE + # Prefer if user has a healing item + mini_score *= 1.2 if @user.hasActiveItem?(:LEFTOVERS) || + (@user.hasActiveItem?(:BLACKSLUDGE) && @user.pbHasType?(:POISON)) + # Prefer if user knows any healing moves + mini_score *= 1.3 if check_for_move(@user) { |move| move.healingMove? } + # Prefer if user knows Pain Split or Leech Seed + mini_score *= 1.2 if @user.pbHasMoveFunction?("05A") # Pain Split + mini_score *= 1.3 if @user.pbHasMoveFunction?("0DC") # Leech Seed + # Prefer if user has certain roles + mini_score *= 1.3 if check_battler_role(@user, BattleRole::PHYSICALWALL, BattleRole::SPECIALWALL) + # Don't prefer if user's Defense stat is raised + sum_stages = @user.stages[:SPECIAL_DEFENSE] + mini_score *= 1 - sum_stages * 0.15 if sum_stages > 0 + + # TODO: Prefer if foes have higher Special Attack than Attack. + + # TODO: Don't prefer if previous damage done by foes wouldn't hurt the + # user much. + + when :ACCURACY + + # Prefer if user knows any weaker moves + mini_score *= 1.1 if check_for_move(@user) { |move| move.damagingMove? && move.basedamage < 95 } + + # Prefer if target has a raised evasion + sum_stages = @target.stages[:EVASION] + mini_score *= 1 + sum_stages * 0.05 if sum_stages > 0 + # Prefer if target has an item that lowers foes' accuracy + mini_score *= 1.1 if @target.hasActiveItem?([:BRIGHTPOWDER, :LAXINCENSE]) + # Prefer if target has an ability that lowers foes' accuracy + # TODO: Tangled Feet while user is confused? + if (@battle.pbWeather == PBWeather::Sandstorm && @target.hasActiveAbility?(:SANDVEIL)) || + (@battle.pbWeather == PBWeather::Hail && @target.hasActiveAbility?(:SNOWCLOAK)) + mini_score *= 1.1 + end + + when :EVASION + # Prefer if user has a healing item + mini_score *= 1.2 if @user.hasActiveItem?(:LEFTOVERS) || + (@user.hasActiveItem?(:BLACKSLUDGE) && @user.pbHasType?(:POISON)) + # Prefer if user has an item that lowers foes' accuracy + mini_score *= 1.3 if @user.hasActiveItem?([:BRIGHTPOWDER, :LAXINCENSE]) + # Prefer if user has an ability that lowers foes' accuracy + # TODO: Tangled Feet while user is confused? + if (@battle.pbWeather == PBWeather::Sandstorm && @user.hasActiveAbility?(:SANDVEIL)) || + (@battle.pbWeather == PBWeather::Hail && @user.hasActiveAbility?(:SNOWCLOAK)) + mini_score *= 1.3 + end + # Prefer if user knows any healing moves + mini_score *= 1.3 if check_for_move(@user) { |move| move.healingMove? } + # Prefer if user knows Pain Split or Leech Seed + mini_score *= 1.2 if @user.pbHasMoveFunction?("05A") # Pain Split + mini_score *= 1.3 if @user.pbHasMoveFunction?("0DC") # Leech Seed + # Prefer if user has certain roles + mini_score *= 1.3 if check_battler_role(@user, BattleRole::PHYSICALWALL, BattleRole::SPECIALWALL) + # TODO: Don't prefer if user's evasion stat is raised + + # TODO: Don't prefer if target has No Guard. + mini_score *= 0.2 if @target.hasActiveAbility?(:NOGUARD) + # TODO: Don't prefer if target has previously used any moves that never miss. + + end + + # Don't prefer if user has Contrary + mini_score *= 0.5 if @user.hasActiveAbility?(:CONTRARY) + # TODO: Don't prefer if target has Unaware? Reborn resets mini_score to 1. + # This check needs more consideration. Note that @target is user for + # status moves, so that part is wrong. + # TODO: Is 0x for 025, 026, 026 (all moves that raise multiple stats) + mini_score *= 0.5 if @move.statusMove? && @target.hasActiveAbility?(:UNAWARE) + + # TODO: Don't prefer if any foe has previously used a stat stage-clearing + # move (050, 051 Clear Smog/Haze). + mini_score *= 0.3 if check_for_move(@target) { |move| ["050", "051"].include?(move.function) } # Clear Smog, Haze + + # TODO: Prefer if user is faster than the target. + # TODO: Is 1.3x for 025 Coil (+Atk, +Def, +acc). + mini_score *= 1.5 if @user_faster + # TODO: Don't prefer if target is a higher level than the user + if @target.level > @user.level + 5 + mini_score *= 0.6 + if @target.level > @user.level + 10 + mini_score *= 0.2 + end + end + + return mini_score + end + + #============================================================================= + # + #============================================================================= + def get_score_for_user_stat_raise(score) + # Discard status move if user has Contrary + return 0 if @move.statusMove? && @user.hasActiveAbility?(:CONTRARY) + + # Discard move if it can't raise any stats + can_change_any_stat = false + @move.stat_up.each_with_index do |stat, idx| + next if idx.odd? + next if @user.statStageAtMax?(stat) + can_change_any_stat = true + break + end + if !can_change_any_stat + return (@move.statusMove?) ? 0 : score + end + + # Get the main mini-score + main_mini_score = calc_user_stat_raise_mini_score + + # For each stat to be raised in turn, calculate a mini-score describing how + # beneficial that stat being raised will be + mini_score = 0 + num_stats = 0 + @move.stat_up.each_with_index do |stat, idx| + next if idx.odd? + next if @user.statStageAtMax?(stat) + # TODO: Use the effective increment (e.g. 1 if the stat is raised by 2 but + # the stat is already at +5). + mini_score += calc_user_stat_raise_one(stat, @move.stat_up[idx + 1]) + num_stats += 1 + end + + # Apply the average mini-score to the actual score + score = apply_effect_chance_to_score(main_mini_score * mini_score / num_stats) + + return score + end +end diff --git a/Data/Scripts/011_Battle/005_AI/051_AI_MoveHandlers_Misc.rb b/Data/Scripts/011_Battle/005_AI/051_AI_MoveHandlers_Misc.rb new file mode 100644 index 000000000..b7a064393 --- /dev/null +++ b/Data/Scripts/011_Battle/005_AI/051_AI_MoveHandlers_Misc.rb @@ -0,0 +1,249 @@ +#=============================================================================== +# +#=============================================================================== +# Struggle + +# None + +Battle::AI::Handlers::MoveEffectScore.add("DoesNothingCongratulations", + proc { |score, move, user, target, skill, ai, battle| + next 0 if ai.skill_check(Battle::AI::AILevel.high) + next score - 95 + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("DoesNothingCongratulations", + "DoesNothingFailsIfNoAlly", + "DoesNothingUnusableInGravity", + "DoubleMoneyGainedFromBattle") + +# AddMoneyGainedFromBattle + +Battle::AI::Handlers::MoveEffectScore.add("FailsIfNotUserFirstTurn", + proc { |score, move, user, target, skill, ai, battle| + next score - 90 if user.turnCount > 0 + } +) + +# FailsIfUserHasUnusedMove + +Battle::AI::Handlers::MoveEffectScore.add("FailsIfUserNotConsumedBerry", + proc { |score, move, user, target, skill, ai, battle| + next score - 90 if !user.belched? + } +) + +Battle::AI::Handlers::MoveEffectScore.add("FailsIfTargetHasNoItem", + proc { |score, move, user, target, skill, ai, battle| + if ai.skill_check(Battle::AI::AILevel.medium) + next score - 90 if !target.item || !target.itemActive? + next score + 50 + end + } +) + +Battle::AI::Handlers::MoveEffectScore.add("FailsUnlessTargetSharesTypeWithUser", + proc { |score, move, user, target, skill, ai, battle| + if !(user.types[0] && target.pbHasType?(user.types[0])) && + !(user.types[1] && target.pbHasType?(user.types[1])) + next score - 90 + end + } +) + +Battle::AI::Handlers::MoveEffectScore.add("FailsIfUserDamagedThisTurn", + proc { |score, move, user, target, skill, ai, battle| + score += 50 if target.effects[PBEffects::HyperBeam] > 0 + score -= 35 if target.hp <= target.totalhp / 2 # If target is weak, no + score -= 70 if target.hp <= target.totalhp / 4 # need to risk this move + next score + } +) + +# FailsIfTargetActed + +Battle::AI::Handlers::MoveEffectScore.add("CrashDamageIfFailsUnusableInGravity", + proc { |score, move, user, target, skill, ai, battle| + next score + 10 * (user.stages[:ACCURACY] - target.stages[:EVASION]) + } +) + +Battle::AI::Handlers::MoveEffectScore.add("StartSunWeather", + proc { |score, move, user, target, skill, ai, battle| + if battle.pbCheckGlobalAbility(:AIRLOCK) || + battle.pbCheckGlobalAbility(:CLOUDNINE) + next score - 90 + elsif battle.field.weather == :Sun + next score - 90 + else + user.eachMove do |m| + next if !m.damagingMove? || m.type != :FIRE + score += 20 + end + next score + end + } +) + +Battle::AI::Handlers::MoveEffectScore.add("StartRainWeather", + proc { |score, move, user, target, skill, ai, battle| + if battle.pbCheckGlobalAbility(:AIRLOCK) || + battle.pbCheckGlobalAbility(:CLOUDNINE) + next score - 90 + elsif battle.field.weather == :Rain + next score - 90 + else + user.eachMove do |m| + next if !m.damagingMove? || m.type != :WATER + score += 20 + end + next score + end + } +) + +Battle::AI::Handlers::MoveEffectScore.add("StartSandstormWeather", + proc { |score, move, user, target, skill, ai, battle| + if battle.pbCheckGlobalAbility(:AIRLOCK) || + battle.pbCheckGlobalAbility(:CLOUDNINE) + next score - 90 + elsif battle.field.weather == :Rain + next score - 90 + end + } +) + +Battle::AI::Handlers::MoveEffectScore.add("StartHailWeather", + proc { |score, move, user, target, skill, ai, battle| + if battle.pbCheckGlobalAbility(:AIRLOCK) || + battle.pbCheckGlobalAbility(:CLOUDNINE) + next score - 90 + elsif battle.field.weather == :Hail + next score - 90 + end + } +) + +# StartElectricTerrain + +# StartGrassyTerrain + +# StartMistyTerrain + +# StartPsychicTerrain + +Battle::AI::Handlers::MoveEffectScore.add("RemoveTerrain", + proc { |score, move, user, target, skill, ai, battle| + next 0 if battle.field.terrain == :None + } +) + +Battle::AI::Handlers::MoveEffectScore.add("AddSpikesToFoeSide", + proc { |score, move, user, target, skill, ai, battle| + if user.pbOpposingSide.effects[PBEffects::Spikes] >= 3 + next score - 90 + elsif user.allOpposing.none? { |b| battle.pbCanChooseNonActive?(b.index) } + next score - 90 # Opponent can't switch in any Pokemon + else + score += 10 * battle.pbAbleNonActiveCount(user.idxOpposingSide) + score += [40, 26, 13][user.pbOpposingSide.effects[PBEffects::Spikes]] + next score + end + } +) + +Battle::AI::Handlers::MoveEffectScore.add("AddToxicSpikesToFoeSide", + proc { |score, move, user, target, skill, ai, battle| + if user.pbOpposingSide.effects[PBEffects::ToxicSpikes] >= 2 + next score - 90 + elsif user.allOpposing.none? { |b| battle.pbCanChooseNonActive?(b.index) } + next score - 90 # Opponent can't switch in any Pokemon + else + score += 8 * battle.pbAbleNonActiveCount(user.idxOpposingSide) + score += [26, 13][user.pbOpposingSide.effects[PBEffects::ToxicSpikes]] + next score + end + } +) + +Battle::AI::Handlers::MoveEffectScore.add("AddStealthRocksToFoeSide", + proc { |score, move, user, target, skill, ai, battle| + if user.pbOpposingSide.effects[PBEffects::StealthRock] + next score - 90 + elsif user.allOpposing.none? { |b| battle.pbCanChooseNonActive?(b.index) } + next score - 90 # Opponent can't switch in any Pokemon + else + next score + 10 * battle.pbAbleNonActiveCount(user.idxOpposingSide) + end + } +) + +Battle::AI::Handlers::MoveEffectScore.add("AddStickyWebToFoeSide", + proc { |score, move, user, target, skill, ai, battle| + next score - 95 if user.pbOpposingSide.effects[PBEffects::StickyWeb] + } +) + +Battle::AI::Handlers::MoveEffectScore.add("SwapSideEffects", + proc { |score, move, user, target, skill, ai, battle| + if ai.skill_check(Battle::AI::AILevel.medium) + good_effects = [:Reflect, :LightScreen, :AuroraVeil, :SeaOfFire, + :Swamp, :Rainbow, :Mist, :Safeguard, + :Tailwind].map! { |e| PBEffects.const_get(e) } + bad_effects = [:Spikes, :StickyWeb, :ToxicSpikes, :StealthRock].map! { |e| PBEffects.const_get(e) } + bad_effects.each do |e| + score += 10 if ![0, false, nil].include?(user.pbOwnSide.effects[e]) + score -= 10 if ![0, 1, false, nil].include?(user.pbOpposingSide.effects[e]) + end + if ai.skill_check(Battle::AI::AILevel.high) + good_effects.each do |e| + score += 10 if ![0, 1, false, nil].include?(user.pbOpposingSide.effects[e]) + score -= 10 if ![0, false, nil].include?(user.pbOwnSide.effects[e]) + end + end + next score + end + } +) + +Battle::AI::Handlers::MoveEffectScore.add("UserMakeSubstitute", + proc { |score, move, user, target, skill, ai, battle| + if user.effects[PBEffects::Substitute] > 0 + next score - 90 + elsif user.hp <= user.totalhp / 4 + next score - 90 + end + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RemoveUserBindingAndEntryHazards", + proc { |score, move, user, target, skill, ai, battle| + score += 30 if user.effects[PBEffects::Trapping] > 0 + score += 30 if user.effects[PBEffects::LeechSeed] >= 0 + if battle.pbAbleNonActiveCount(user.idxOwnSide) > 0 + score += 80 if user.pbOwnSide.effects[PBEffects::Spikes] > 0 + score += 80 if user.pbOwnSide.effects[PBEffects::ToxicSpikes] > 0 + score += 80 if user.pbOwnSide.effects[PBEffects::StealthRock] + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("AttackTwoTurnsLater", + proc { |score, move, user, target, skill, ai, battle| + if battle.positions[target.index].effects[PBEffects::FutureSightCounter] > 0 + next 0 + elsif battle.pbAbleNonActiveCount(user.idxOwnSide) == 0 + # Future Sight tends to be wasteful if down to last Pokemon + next score - 70 + end + } +) + +# UserSwapsPositionsWithAlly + +Battle::AI::Handlers::MoveEffectScore.add("BurnAttackerBeforeUserActs", + proc { |score, move, user, target, skill, ai, battle| + next score + 20 # Because of possible burning + } +) diff --git a/Data/Scripts/011_Battle/005_AI/052_AI_MoveHandlers_BattlerStats.rb b/Data/Scripts/011_Battle/005_AI/052_AI_MoveHandlers_BattlerStats.rb new file mode 100644 index 000000000..689b9d28e --- /dev/null +++ b/Data/Scripts/011_Battle/005_AI/052_AI_MoveHandlers_BattlerStats.rb @@ -0,0 +1,1576 @@ +#=============================================================================== +# +#=============================================================================== +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserAttack1", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next score - 90 if user.statStageAtMax?(:ATTACK) + score -= user.stages[:ATTACK] * 20 + if ai.skill_check(Battle::AI::AILevel.medium) + hasPhysicalAttack = false + user.eachMove do |m| + next if !m.physicalMove?(m.type) + hasPhysicalAttack = true + break + end + if hasPhysicalAttack + score += 20 + elsif ai.skill_check(Battle::AI::AILevel.high) + score -= 90 + end + end + else + score += 20 if user.stages[:ATTACK] < 0 + if ai.skill_check(Battle::AI::AILevel.medium) + hasPhysicalAttack = false + user.eachMove do |m| + next if !m.physicalMove?(m.type) + hasPhysicalAttack = true + break + end + score += 20 if hasPhysicalAttack + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserAttack2", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next score - 90 if user.statStageAtMax?(:ATTACK) + score += 40 if user.turnCount == 0 + score -= user.stages[:ATTACK] * 20 + if ai.skill_check(Battle::AI::AILevel.medium) + hasPhysicalAttack = false + user.eachMove do |m| + next if !m.physicalMove?(m.type) + hasPhysicalAttack = true + break + end + if hasPhysicalAttack + score += 20 + elsif ai.skill_check(Battle::AI::AILevel.high) + score -= 90 + end + end + else + score += 10 if user.turnCount == 0 + score += 20 if user.stages[:ATTACK] < 0 + if ai.skill_check(Battle::AI::AILevel.medium) + hasPhysicalAttack = false + user.eachMove do |m| + next if !m.physicalMove?(m.type) + hasPhysicalAttack = true + break + end + score += 20 if hasPhysicalAttack + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("RaiseUserAttack2", + "RaiseUserAttack2IfTargetFaints", + "RaiseUserAttack3", + "RaiseUserAttack3IfTargetFaints") + +Battle::AI::Handlers::MoveEffectScore.add("MaxUserAttackLoseHalfOfTotalHP", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.statStageAtMax?(:ATTACK) || user.hp <= user.totalhp / 2 + score += (6 - user.stages[:ATTACK]) * 10 + if ai.skill_check(Battle::AI::AILevel.medium) + hasPhysicalAttack = false + user.eachMove do |m| + next if !m.physicalMove?(m.type) + hasPhysicalAttack = true + break + end + if hasPhysicalAttack + score += 40 + elsif ai.skill_check(Battle::AI::AILevel.high) + score -= 90 + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserDefense1", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next 0 if user.statStageAtMax?(:DEFENSE) + next score - user.stages[:DEFENSE] * 20 + elsif user.stages[:DEFENSE] < 0 + next score + 20 + end + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("RaiseUserDefense1", + "RaiseUserDefense1CurlUpUser") + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserDefense2", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next 0 if user.statStageAtMax?(:DEFENSE) + score += 40 if user.turnCount == 0 + score -= user.stages[:DEFENSE] * 20 + else + score += 10 if user.turnCount == 0 + score += 20 if user.stages[:DEFENSE] < 0 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserDefense3", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next 0 if user.statStageAtMax?(:DEFENSE) + score += 40 if user.turnCount == 0 + score -= user.stages[:DEFENSE] * 30 + else + score += 10 if user.turnCount == 0 + score += 30 if user.stages[:DEFENSE] < 0 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserSpAtk1", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next 0 if user.statStageAtMax?(:SPECIAL_ATTACK) + score -= user.stages[:SPECIAL_ATTACK] * 20 + if ai.skill_check(Battle::AI::AILevel.medium) + hasSpecicalAttack = false + user.eachMove do |m| + next if !m.specialMove?(m.type) + hasSpecicalAttack = true + break + end + if hasSpecicalAttack + score += 20 + elsif ai.skill_check(Battle::AI::AILevel.high) + score -= 90 + end + end + else + score += 20 if user.stages[:SPECIAL_ATTACK] < 0 + if ai.skill_check(Battle::AI::AILevel.medium) + hasSpecicalAttack = false + user.eachMove do |m| + next if !m.specialMove?(m.type) + hasSpecicalAttack = true + break + end + score += 20 if hasSpecicalAttack + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserSpAtk2", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next 0 if user.statStageAtMax?(:SPECIAL_ATTACK) + score += 40 if user.turnCount == 0 + score -= user.stages[:SPECIAL_ATTACK] * 20 + if ai.skill_check(Battle::AI::AILevel.medium) + hasSpecicalAttack = false + user.eachMove do |m| + next if !m.specialMove?(m.type) + hasSpecicalAttack = true + break + end + if hasSpecicalAttack + score += 20 + elsif ai.skill_check(Battle::AI::AILevel.high) + score -= 90 + end + end + else + score += 10 if user.turnCount == 0 + score += 20 if user.stages[:SPECIAL_ATTACK] < 0 + if ai.skill_check(Battle::AI::AILevel.medium) + hasSpecicalAttack = false + user.eachMove do |m| + next if !m.specialMove?(m.type) + hasSpecicalAttack = true + break + end + score += 20 if hasSpecicalAttack + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserSpAtk3", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next 0 if user.statStageAtMax?(:SPECIAL_ATTACK) + score += 40 if user.turnCount == 0 + score -= user.stages[:SPECIAL_ATTACK] * 30 + if ai.skill_check(Battle::AI::AILevel.medium) + hasSpecicalAttack = false + user.eachMove do |m| + next if !m.specialMove?(m.type) + hasSpecicalAttack = true + break + end + if hasSpecicalAttack + score += 30 + elsif ai.skill_check(Battle::AI::AILevel.high) + score -= 90 + end + end + else + score += 10 if user.turnCount == 0 + score += 30 if user.stages[:SPECIAL_ATTACK] < 0 + if ai.skill_check(Battle::AI::AILevel.medium) + hasSpecicalAttack = false + user.eachMove do |m| + next if !m.specialMove?(m.type) + hasSpecicalAttack = true + break + end + score += 30 if hasSpecicalAttack + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserSpDef1", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next 0 if user.statStageAtMax?(:SPECIAL_DEFENSE) + score += 40 if user.turnCount == 0 + score -= user.stages[:SPECIAL_DEFENSE] * 20 + else + score += 10 if user.turnCount == 0 + score += 20 if user.stages[:SPECIAL_DEFENSE] < 0 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("RaiseUserSpDef1", + "RaiseUserSpDef2", + "RaiseUserSpDef3") + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserSpDef1PowerUpElectricMove", + proc { |score, move, user, target, skill, ai, battle| + foundMove = false + user.eachMove do |m| + next if m.type != :ELECTRIC || !m.damagingMove? + foundMove = true + break + end + score += 20 if foundMove + if move.statusMove? + if user.statStageAtMax?(:SPECIAL_DEFENSE) + score -= 90 + else + score -= user.stages[:SPECIAL_DEFENSE] * 20 + end + elsif user.stages[:SPECIAL_DEFENSE] < 0 + score += 20 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserSpeed1", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next 0 if user.statStageAtMax?(:SPEED) + score -= user.stages[:SPEED] * 10 + if ai.skill_check(Battle::AI::AILevel.high) + aspeed = pbRoughStat(user, :SPEED) + ospeed = pbRoughStat(target, :SPEED) + score += 30 if aspeed < ospeed && aspeed * 2 > ospeed + end + elsif user.stages[:SPEED] < 0 + score += 20 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserSpeed2", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next 0 if user.statStageAtMax?(:SPEED) + score += 20 if user.turnCount == 0 + score -= user.stages[:SPEED] * 10 + if ai.skill_check(Battle::AI::AILevel.high) + aspeed = pbRoughStat(user, :SPEED) + ospeed = pbRoughStat(target, :SPEED) + score += 30 if aspeed < ospeed && aspeed * 2 > ospeed + end + else + score += 10 if user.turnCount == 0 + score += 20 if user.stages[:SPEED] < 0 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("RaiseUserSpeed2", + "RaiseUserSpeed2LowerUserWeight", + "RaiseUserSpeed3") + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserAccuracy1", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next 0 if user.statStageAtMax?(:ACCURACY) + score += 40 if user.turnCount == 0 + score -= user.stages[:ACCURACY] * 20 + else + score += 10 if user.turnCount == 0 + score += 20 if user.stages[:ACCURACY] < 0 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("RaiseUserAccuracy1", + "RaiseUserAccuracy2", + "RaiseUserAccuracy3") + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserEvasion1", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next 0 if user.statStageAtMax?(:EVASION) + score -= user.stages[:EVASION] * 10 + elsif user.stages[:EVASION] < 0 + score += 20 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserEvasion2", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next 0 if user.statStageAtMax?(:EVASION) + score += 40 if user.turnCount == 0 + score -= user.stages[:EVASION] * 10 + else + score += 10 if user.turnCount == 0 + score += 20 if user.stages[:EVASION] < 0 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("RaiseUserEvasion2", + "RaiseUserEvasion2MinimizeUser", + "RaiseUserEvasion3") + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserCriticalHitRate2", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next 0 if user.effects[PBEffects::FocusEnergy] >= 2 + next score + 30 + elsif user.effects[PBEffects::FocusEnergy] < 2 + next score + 30 + end + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserAtkDef1", + proc { |score, move, user, target, skill, ai, battle| + if user.statStageAtMax?(:ATTACK) && + user.statStageAtMax?(:DEFENSE) + next score - 90 + end + score -= user.stages[:ATTACK] * 10 + score -= user.stages[:DEFENSE] * 10 + if ai.skill_check(Battle::AI::AILevel.medium) + hasPhysicalAttack = false + user.eachMove do |m| + next if !m.physicalMove?(m.type) + hasPhysicalAttack = true + break + end + if hasPhysicalAttack + score += 20 + elsif ai.skill_check(Battle::AI::AILevel.high) + score -= 90 + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserAtkDefAcc1", + proc { |score, move, user, target, skill, ai, battle| + if user.statStageAtMax?(:ATTACK) && + user.statStageAtMax?(:DEFENSE) && + user.statStageAtMax?(:ACCURACY) + next score - 90 + end + score -= user.stages[:ATTACK] * 10 + score -= user.stages[:DEFENSE] * 10 + score -= user.stages[:ACCURACY] * 10 + if ai.skill_check(Battle::AI::AILevel.medium) + hasPhysicalAttack = false + user.eachMove do |m| + next if !m.physicalMove?(m.type) + hasPhysicalAttack = true + break + end + if hasPhysicalAttack + score += 20 + elsif ai.skill_check(Battle::AI::AILevel.high) + score -= 90 + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserAtkDefAcc1", + proc { |score, move, user, target, skill, ai, battle| + if user.statStageAtMax?(:ATTACK) && + user.statStageAtMax?(:SPECIAL_ATTACK) + next score - 90 + end + score -= user.stages[:ATTACK] * 10 + score -= user.stages[:SPECIAL_ATTACK] * 10 + if ai.skill_check(Battle::AI::AILevel.medium) + hasDamagingAttack = false + user.eachMove do |m| + next if !m.damagingMove? + hasDamagingAttack = true + break + end + if hasDamagingAttack + score += 20 + elsif ai.skill_check(Battle::AI::AILevel.high) + score -= 90 + end + end + if move.function == "RaiseUserAtkSpAtk1Or2InSun" # Growth + score += 20 if [:Sun, :HarshSun].include?(user.effectiveWeather) + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("RaiseUserAtkSpAtk1", + "RaiseUserAtkSpAtk1Or2InSun") + +Battle::AI::Handlers::MoveEffectScore.add("LowerUserDefSpDef1RaiseUserAtkSpAtkSpd2", + proc { |score, move, user, target, skill, ai, battle| + score -= user.stages[:ATTACK] * 20 + score -= user.stages[:SPEED] * 20 + score -= user.stages[:SPECIAL_ATTACK] * 20 + score += user.stages[:DEFENSE] * 10 + score += user.stages[:SPECIAL_DEFENSE] * 10 + if ai.skill_check(Battle::AI::AILevel.medium) + hasDamagingAttack = false + user.eachMove do |m| + next if !m.damagingMove? + hasDamagingAttack = true + break + end + score += 20 if hasDamagingAttack + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserAtkSpd1", + proc { |score, move, user, target, skill, ai, battle| + if user.statStageAtMax?(:ATTACK) && + user.statStageAtMax?(:SPEED) + next score - 90 + end + score += 40 if user.turnCount == 0 # Dragon Dance tends to be popular + score -= user.stages[:ATTACK] * 10 + score -= user.stages[:SPEED] * 10 + if ai.skill_check(Battle::AI::AILevel.medium) + hasPhysicalAttack = false + user.eachMove do |m| + next if !m.physicalMove?(m.type) + hasPhysicalAttack = true + break + end + if hasPhysicalAttack + score += 20 + elsif ai.skill_check(Battle::AI::AILevel.high) + score -= 90 + end + end + if ai.skill_check(Battle::AI::AILevel.high) + aspeed = pbRoughStat(user, :SPEED) + ospeed = pbRoughStat(target, :SPEED) + score += 20 if aspeed < ospeed && aspeed * 2 > ospeed + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserAtk1Spd2", + proc { |score, move, user, target, skill, ai, battle| + if user.statStageAtMax?(:ATTACK) && + user.statStageAtMax?(:SPEED) + next score - 90 + end + score -= user.stages[:ATTACK] * 10 + score -= user.stages[:SPEED] * 10 + if ai.skill_check(Battle::AI::AILevel.medium) + hasPhysicalAttack = false + user.eachMove do |m| + next if !m.physicalMove?(m.type) + hasPhysicalAttack = true + break + end + if hasPhysicalAttack + score += 20 + elsif ai.skill_check(Battle::AI::AILevel.high) + score -= 90 + end + end + if ai.skill_check(Battle::AI::AILevel.high) + aspeed = pbRoughStat(user, :SPEED) + ospeed = pbRoughStat(target, :SPEED) + score += 30 if aspeed < ospeed && aspeed * 2 > ospeed + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserAtkAcc1", + proc { |score, move, user, target, skill, ai, battle| + if user.statStageAtMax?(:ATTACK) && + user.statStageAtMax?(:ACCURACY) + next score - 90 + end + score -= user.stages[:ATTACK] * 10 + score -= user.stages[:ACCURACY] * 10 + if ai.skill_check(Battle::AI::AILevel.medium) + hasPhysicalAttack = false + user.eachMove do |m| + next if !m.physicalMove?(m.type) + hasPhysicalAttack = true + break + end + if hasPhysicalAttack + score += 20 + elsif ai.skill_check(Battle::AI::AILevel.high) + score -= 90 + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserDefSpDef1", + proc { |score, move, user, target, skill, ai, battle| + if user.statStageAtMax?(:DEFENSE) && + user.statStageAtMax?(:SPECIAL_DEFENSE) + next score - 90 + end + score -= user.stages[:DEFENSE] * 10 + score -= user.stages[:SPECIAL_DEFENSE] * 10 + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserSpAtkSpDef1", + proc { |score, move, user, target, skill, ai, battle| + if user.statStageAtMax?(:SPECIAL_ATTACK) && + user.statStageAtMax?(:SPECIAL_DEFENSE) + next score - 90 + end + score += 40 if user.turnCount == 0 # Calm Mind tends to be popular + score -= user.stages[:SPECIAL_ATTACK] * 10 + score -= user.stages[:SPECIAL_DEFENSE] * 10 + if ai.skill_check(Battle::AI::AILevel.medium) + hasSpecicalAttack = false + user.eachMove do |m| + next if !m.specialMove?(m.type) + hasSpecicalAttack = true + break + end + if hasSpecicalAttack + score += 20 + elsif ai.skill_check(Battle::AI::AILevel.high) + score -= 90 + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserSpAtkSpDefSpd1", + proc { |score, move, user, target, skill, ai, battle| + if user.statStageAtMax?(:SPECIAL_ATTACK) && + user.statStageAtMax?(:SPECIAL_DEFENSE) && + user.statStageAtMax?(:SPEED) + next score - 90 + end + score += 40 if user.turnCount == 0 # Calm Mind tends to be popular + score -= user.stages[:SPECIAL_ATTACK] * 10 + score -= user.stages[:SPECIAL_DEFENSE] * 10 + score -= user.stages[:SPEED] * 10 + if ai.skill_check(Battle::AI::AILevel.medium) + hasSpecicalAttack = false + user.eachMove do |m| + next if !m.specialMove?(m.type) + hasSpecicalAttack = true + break + end + if hasSpecicalAttack + score += 20 + elsif ai.skill_check(Battle::AI::AILevel.high) + score -= 90 + end + end + if ai.skill_check(Battle::AI::AILevel.high) + aspeed = pbRoughStat(user, :SPEED) + ospeed = pbRoughStat(target, :SPEED) + if aspeed < ospeed && aspeed * 2 > ospeed + score += 20 + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserMainStats1", + proc { |score, move, user, target, skill, ai, battle| + GameData::Stat.each_main_battle { |s| score += 10 if user.stages[s.id] < 0 } + if ai.skill_check(Battle::AI::AILevel.medium) + hasDamagingAttack = false + user.eachMove do |m| + next if !m.damagingMove? + hasDamagingAttack = true + break + end + score += 20 if hasDamagingAttack + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserMainStats1LoseThirdOfTotalHP", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.hp <= user.totalhp / 2 + next 0 if user.hasActiveAbility?(:CONTRARY) + stats_maxed = true + GameData::Stat.each_main_battle do |s| + next if user.statStageAtMax?(s.id) + stats_maxed = false + break + end + next 0 if stats_maxed + if ai.skill_check(Battle::AI::AILevel.high) && user.hp >= user.totalhp * 0.75 + score += 30 + end + GameData::Stat.each_main_battle { |s| score += 10 if user.stages[s.id] <= 0 } + if ai.skill_check(Battle::AI::AILevel.medium) + hasDamagingAttack = user.moves.any? { |m| next m&.damagingMove? } + score += 20 if hasDamagingAttack + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserMainStats1TrapUserInBattle", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.effects[PBEffects::NoRetreat] + next 0 if user.hasActiveAbility?(:CONTRARY) + stats_maxed = true + GameData::Stat.each_main_battle do |s| + next if user.statStageAtMax?(s.id) + stats_maxed = false + break + end + next 0 if stats_maxed + if ai.skill_check(Battle::AI::AILevel.high) + score -= 50 if user.hp <= user.totalhp / 2 + score += 30 if user.trappedInBattle? + end + GameData::Stat.each_main_battle { |s| score += 10 if user.stages[s.id] <= 0 } + if ai.skill_check(Battle::AI::AILevel.medium) + hasDamagingAttack = user.moves.any? { |m| next m&.damagingMove? } + score += 20 if hasDamagingAttack + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("StartRaiseUserAtk1WhenDamaged", + proc { |score, move, user, target, skill, ai, battle| + next score + 25 if user.effects[PBEffects::Rage] + } +) + +Battle::AI::Handlers::MoveEffectScore.add("LowerUserAttack1", + proc { |score, move, user, target, skill, ai, battle| + next score + user.stages[:ATTACK] * 10 + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("LowerUserAttack1", + "LowerUserAttack2") + +Battle::AI::Handlers::MoveEffectScore.add("LowerUserDefense1", + proc { |score, move, user, target, skill, ai, battle| + next score + user.stages[:DEFENSE] * 10 + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("LowerUserDefense1", + "LowerUserDefense2") + +Battle::AI::Handlers::MoveEffectScore.add("LowerUserSpAtk1", + proc { |score, move, user, target, skill, ai, battle| + next score + user.stages[:SPECIAL_ATTACK] * 10 + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("LowerUserSpAtk1", + "LowerUserSpAtk2") + +Battle::AI::Handlers::MoveEffectScore.add("LowerUserSpDef1", + proc { |score, move, user, target, skill, ai, battle| + next score + user.stages[:SPECIAL_DEFENSE] * 10 + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("LowerUserSpDef1", + "LowerUserSpDef2") + +Battle::AI::Handlers::MoveEffectScore.add("LowerUserSpeed1", + proc { |score, move, user, target, skill, ai, battle| + next score + user.stages[:SPECIAL_DEFENSE] * 10 + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("LowerUserSpeed1", + "LowerUserSpeed2") + +Battle::AI::Handlers::MoveEffectScore.add("LowerUserAtkDef1", + proc { |score, move, user, target, skill, ai, battle| + avg = user.stages[:ATTACK] * 10 + avg += user.stages[:DEFENSE] * 10 + next score + avg / 2 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("LowerUserDefSpDef1", + proc { |score, move, user, target, skill, ai, battle| + avg = user.stages[:DEFENSE] * 10 + avg += user.stages[:SPECIAL_DEFENSE] * 10 + next score + avg / 2 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("LowerUserDefSpDefSpd1", + proc { |score, move, user, target, skill, ai, battle| + avg = user.stages[:DEFENSE] * 10 + avg += user.stages[:SPEED] * 10 + avg += user.stages[:SPECIAL_DEFENSE] * 10 + next score + (avg / 3).floor + } +) + +# RaiseTargetAttack1 + +Battle::AI::Handlers::MoveEffectScore.add("RaiseTargetAttack2ConfuseTarget", + proc { |score, move, user, target, skill, ai, battle| + next score - 90 if !target.pbCanConfuse?(user, false) + next score + 30 if target.stages[:ATTACK] < 0 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseTargetSpAtk1ConfuseTarget", + proc { |score, move, user, target, skill, ai, battle| + next score - 90 if !target.pbCanConfuse?(user, false) + next score + 30 if target.stages[:SPECIAL_ATTACK] < 0 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseTargetSpDef1", + proc { |score, move, user, target, skill, ai, battle| + next score + 30 if target.statStageAtMax?(:SPECIAL_DEFENSE) + next score - target.stages[:SPECIAL_DEFENSE] * 10 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseTargetRandomStat2", + proc { |score, move, user, target, skill, ai, battle| + avgStat = 0 + canChangeStat = false + GameData::Stat.each_battle do |s| + next if target.statStageAtMax?(s.id) + avgStat -= target.stages[s.id] + canChangeStat = true + end + if canChangeStat + avgStat = avgStat / 2 if avgStat < 0 # More chance of getting even better + next + avgStat * 10 + else + next score - 90 + end + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseTargetAtkSpAtk2", + proc { |score, move, user, target, skill, ai, battle| + next 0 if target.opposes?(user) + next score - 90 if ai.skill_check(Battle::AI::AILevel.medium) && target.hasActiveAbility?(:CONTRARY) + score -= target.stages[:ATTACK] * 20 + score -= target.stages[:SPECIAL_ATTACK] * 20 + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("LowerTargetAttack1", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next 0 if !target.pbCanLowerStatStage?(:ATTACK, user) + score += target.stages[:ATTACK] * 20 + if ai.skill_check(Battle::AI::AILevel.medium) + hasPhysicalAttack = false + target.eachMove do |m| + next if !m.physicalMove?(m.type) + hasPhysicalAttack = true + break + end + if hasPhysicalAttack + score += 20 + elsif ai.skill_check(Battle::AI::AILevel.high) + score -= 90 + end + end + else + score += 20 if target.stages[:ATTACK] > 0 + if ai.skill_check(Battle::AI::AILevel.medium) + hasPhysicalAttack = false + target.eachMove do |m| + next if !m.physicalMove?(m.type) + hasPhysicalAttack = true + break + end + score += 20 if hasPhysicalAttack + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("LowerTargetAttack1BypassSubstitute", + proc { |score, move, user, target, skill, ai, battle| + next 0 if !target.pbCanLowerStatStage?(:ATTACK, user) + score += target.stages[:ATTACK] * 20 + if ai.skill_check(Battle::AI::AILevel.medium) + hasPhysicalAttack = false + target.eachMove do |m| + next if !m.physicalMove?(m.type) + hasPhysicalAttack = true + break + end + if hasPhysicalAttack + score += 20 + elsif ai.skill_check(Battle::AI::AILevel.high) + score -= 90 + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("LowerTargetAttack2", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next 0 if !target.pbCanLowerStatStage?(:ATTACK, user) + score += 40 if user.turnCount == 0 + score += target.stages[:ATTACK] * 20 + if ai.skill_check(Battle::AI::AILevel.medium) + hasPhysicalAttack = false + target.eachMove do |m| + next if !m.physicalMove?(m.type) + hasPhysicalAttack = true + break + end + if hasPhysicalAttack + score += 20 + elsif ai.skill_check(Battle::AI::AILevel.high) + score -= 90 + end + end + else + score += 10 if user.turnCount == 0 + score += 20 if target.stages[:ATTACK] > 0 + if ai.skill_check(Battle::AI::AILevel.medium) + hasPhysicalAttack = false + target.eachMove do |m| + next if !m.physicalMove?(m.type) + hasPhysicalAttack = true + break + end + score += 20 if hasPhysicalAttack + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("LowerTargetAttack2", + "LowerTargetAttack3") + +Battle::AI::Handlers::MoveEffectScore.add("LowerTargetDefense1", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next 0 if !target.pbCanLowerStatStage?(:DEFENSE, user) + score += target.stages[:DEFENSE] * 20 + elsif target.stages[:DEFENSE] > 0 + score += 20 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("LowerTargetDefense1PowersUpInGravity", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next 0 if !target.pbCanLowerStatStage?(:DEFENSE, user) + score += target.stages[:DEFENSE] * 20 + elsif target.stages[:DEFENSE] > 0 + score += 20 + end + score += 30 if battle.field.effects[PBEffects::Gravity] > 0 + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("LowerTargetDefense2", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next 0 if !target.pbCanLowerStatStage?(:DEFENSE, user) + score += 40 if user.turnCount == 0 + score += target.stages[:DEFENSE] * 20 + else + score += 10 if user.turnCount == 0 + score += 20 if target.stages[:DEFENSE] > 0 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("LowerTargetDefense2", + "LowerTargetDefense3") + +Battle::AI::Handlers::MoveEffectScore.add("LowerTargetSpAtk1", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next 0 if !target.pbCanLowerStatStage?(:SPECIAL_ATTACK, user) + score += user.stages[:SPECIAL_ATTACK] * 20 + if ai.skill_check(Battle::AI::AILevel.medium) + hasSpecicalAttack = false + target.eachMove do |m| + next if !m.specialMove?(m.type) + hasSpecicalAttack = true + break + end + if hasSpecicalAttack + score += 20 + elsif ai.skill_check(Battle::AI::AILevel.high) + score -= 90 + end + end + else + score += 20 if user.stages[:SPECIAL_ATTACK] > 0 + if ai.skill_check(Battle::AI::AILevel.medium) + hasSpecicalAttack = false + target.eachMove do |m| + next if !m.specialMove?(m.type) + hasSpecicalAttack = true + break + end + score += 20 if hasSpecicalAttack + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("LowerTargetSpAtk2", + proc { |score, move, user, target, skill, ai, battle| + next 0 if !target.pbCanLowerStatStage?(:SPECIAL_ATTACK, user) + score += 40 if user.turnCount == 0 + score += target.stages[:SPECIAL_ATTACK] * 20 + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("LowerTargetSpAtk2IfCanAttract", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.gender == 2 || target.gender == 2 || + user.gender == target.gender || target.hasActiveAbility?(:OBLIVIOUS) + if move.statusMove? + next 0 if !target.pbCanLowerStatStage?(:SPECIAL_ATTACK, user) + score += 40 if user.turnCount == 0 + score += target.stages[:SPECIAL_ATTACK] * 20 + if ai.skill_check(Battle::AI::AILevel.medium) + hasSpecicalAttack = false + target.eachMove do |m| + next if !m.specialMove?(m.type) + hasSpecicalAttack = true + break + end + if hasSpecicalAttack + score += 20 + elsif ai.skill_check(Battle::AI::AILevel.high) + score -= 90 + end + end + else + score += 10 if user.turnCount == 0 + score += 20 if target.stages[:SPECIAL_ATTACK] > 0 + if ai.skill_check(Battle::AI::AILevel.medium) + hasSpecicalAttack = false + target.eachMove do |m| + next if !m.specialMove?(m.type) + hasSpecicalAttack = true + break + end + score += 30 if hasSpecicalAttack + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("LowerTargetSpAtk2", + proc { |score, move, user, target, skill, ai, battle| + next 0 if !target.pbCanLowerStatStage?(:SPECIAL_ATTACK, user) + score += 40 if user.turnCount == 0 + score += target.stages[:SPECIAL_ATTACK] * 20 + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("LowerTargetSpDef1", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next 0 if !target.pbCanLowerStatStage?(:SPECIAL_DEFENSE, user) + score += target.stages[:SPECIAL_DEFENSE] * 20 + elsif target.stages[:SPECIAL_DEFENSE] > 0 + score += 20 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("LowerTargetSpDef2", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next 0 if !target.pbCanLowerStatStage?(:SPECIAL_DEFENSE, user) + score += 40 if user.turnCount == 0 + score += target.stages[:SPECIAL_DEFENSE] * 20 + else + score += 10 if user.turnCount == 0 + score += 20 if target.stages[:SPECIAL_DEFENSE] > 0 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("LowerTargetSpDef2", + "LowerTargetSpDef3") + +Battle::AI::Handlers::MoveEffectScore.add("LowerTargetSpeed1", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next 0 if !target.pbCanLowerStatStage?(:SPEED, user) + score += target.stages[:SPEED] * 10 + if ai.skill_check(Battle::AI::AILevel.high) + aspeed = pbRoughStat(user, :SPEED) + ospeed = pbRoughStat(target, :SPEED) + score += 30 if aspeed < ospeed && aspeed * 2 > ospeed + end + elsif user.stages[:SPEED] > 0 + score += 20 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("LowerTargetSpeed1", + "LowerTargetSpeed1WeakerInGrassyTerrain") + +Battle::AI::Handlers::MoveEffectScore.add("LowerTargetSpeed1MakeTargetWeakerToFire", + proc { |score, move, user, target, skill, ai, battle| + next 0 if !target.pbCanLowerStatStage?(:SPEED, user) && target.effects[PBEffects::TarShot] + score += target.stages[:SPEED] * 10 + if ai.skill_check(Battle::AI::AILevel.high) + aspeed = pbRoughStat(user, :SPEED) + ospeed = pbRoughStat(target, :SPEED) + score += 50 if aspeed < ospeed && aspeed * 2 > ospeed + end + score += 20 if user.moves.any? { |m| m.damagingMove? && m.pbCalcType(user) == :FIRE } + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("LowerTargetSpeed2", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next 0 if !target.pbCanLowerStatStage?(:SPEED, user) + score += 20 if user.turnCount == 0 + score += target.stages[:SPEED] * 20 + if ai.skill_check(Battle::AI::AILevel.high) + aspeed = pbRoughStat(user, :SPEED) + ospeed = pbRoughStat(target, :SPEED) + score += 30 if aspeed < ospeed && aspeed * 2 > ospeed + end + else + score += 10 if user.turnCount == 0 + score += 30 if target.stages[:SPEED] > 0 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("LowerTargetSpeed2", + "LowerTargetSpeed3") + +Battle::AI::Handlers::MoveEffectScore.add("LowerTargetAccuracy1", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + return 0 if !target.pbCanLowerStatStage?(:ACCURACY, user) + score += target.stages[:ACCURACY] * 10 + elsif target.stages[:ACCURACY] > 0 + score += 20 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("LowerTargetAccuracy1", + "LowerTargetAccuracy2", + "LowerTargetAccuracy3") + +Battle::AI::Handlers::MoveEffectScore.add("LowerTargetEvasion1", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + return 0 if !target.pbCanLowerStatStage?(:EVASION, user) + score += target.stages[:EVASION] * 10 + elsif target.stages[:EVASION] > 0 + score += 20 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("LowerTargetEvasion1RemoveSideEffects", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + next 0 if !target.pbCanLowerStatStage?(:EVASION, user) + score += target.stages[:EVASION] * 10 + elsif target.stages[:EVASION] > 0 + score += 20 + end + score += 30 if target.pbOwnSide.effects[PBEffects::AuroraVeil] > 0 || + target.pbOwnSide.effects[PBEffects::Reflect] > 0 || + target.pbOwnSide.effects[PBEffects::LightScreen] > 0 || + target.pbOwnSide.effects[PBEffects::Mist] > 0 || + target.pbOwnSide.effects[PBEffects::Safeguard] > 0 + score -= 30 if target.pbOwnSide.effects[PBEffects::Spikes] > 0 || + target.pbOwnSide.effects[PBEffects::ToxicSpikes] > 0 || + target.pbOwnSide.effects[PBEffects::StealthRock] + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("LowerTargetEvasion2", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + return 0 if !target.pbCanLowerStatStage?(:EVASION, user) + score += target.stages[:EVASION] * 10 + elsif target.stages[:EVASION] > 0 + score += 20 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("LowerTargetEvasion2", + "LowerTargetEvasion3") + +Battle::AI::Handlers::MoveEffectScore.add("LowerTargetAtkDef1", + proc { |score, move, user, target, skill, ai, battle| + avg = target.stages[:ATTACK] * 10 + avg += target.stages[:DEFENSE] * 10 + next score + avg / 2 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("LowerTargetAtkSpAtk1", + proc { |score, move, user, target, skill, ai, battle| + avg = target.stages[:ATTACK] * 10 + avg += target.stages[:SPECIAL_ATTACK] * 10 + next score + avg / 2 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("LowerPoisonedTargetAtkSpAtkSpd1", + proc { |score, move, user, target, skill, ai, battle| + count = 0 + battle.allBattlers.each do |b| + if b.poisoned? && + (!b.statStageAtMin?(:ATTACK) || + !b.statStageAtMin?(:SPECIAL_ATTACK) || + !b.statStageAtMin?(:SPEED)) + count += 1 + if user.opposes?(b) + score += user.stages[:ATTACK] * 10 + score += user.stages[:SPECIAL_ATTACK] * 10 + score += user.stages[:SPEED] * 10 + else + score -= 20 + end + end + end + next 0 if count == 0 + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseUserAndAlliesAtkDef1", + proc { |score, move, user, target, skill, ai, battle| + has_ally = false + user.allAllies.each do |b| + next if !b.pbCanLowerStatStage?(:ATTACK, user) && + !b.pbCanLowerStatStage?(:SPECIAL_ATTACK, user) + has_ally = true + if ai.skill_check(Battle::AI::AILevel.medium) && b.hasActiveAbility?(:CONTRARY) + score -= 90 + else + score += 40 + score -= b.stages[:ATTACK] * 20 + score -= b.stages[:SPECIAL_ATTACK] * 20 + end + end + next 0 if !has_ally + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaisePlusMinusUserAndAlliesAtkSpAtk1", + proc { |score, move, user, target, skill, ai, battle| + hasEffect = user.statStageAtMax?(:ATTACK) && + user.statStageAtMax?(:SPECIAL_ATTACK) + user.allAllies.each do |b| + next if b.statStageAtMax?(:ATTACK) && b.statStageAtMax?(:SPECIAL_ATTACK) + hasEffect = true + score -= b.stages[:ATTACK] * 10 + score -= b.stages[:SPECIAL_ATTACK] * 10 + end + next 0 if !hasEffect + score -= user.stages[:ATTACK] * 10 + score -= user.stages[:SPECIAL_ATTACK] * 10 + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaisePlusMinusUserAndAlliesDefSpDef1", + proc { |score, move, user, target, skill, ai, battle| + hasEffect = user.statStageAtMax?(:DEFENSE) && + user.statStageAtMax?(:SPECIAL_DEFENSE) + user.allAllies.each do |b| + next if b.statStageAtMax?(:DEFENSE) && b.statStageAtMax?(:SPECIAL_DEFENSE) + hasEffect = true + score -= b.stages[:DEFENSE] * 10 + score -= b.stages[:SPECIAL_DEFENSE] * 10 + end + next 0 if !hasEffect + score -= user.stages[:DEFENSE] * 10 + score -= user.stages[:SPECIAL_DEFENSE] * 10 + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseGroundedGrassBattlersAtkSpAtk1", + proc { |score, move, user, target, skill, ai, battle| + count = 0 + battle.allBattlers.each do |b| + if b.pbHasType?(:GRASS) && !b.airborne? && + (!b.statStageAtMax?(:ATTACK) || !b.statStageAtMax?(:SPECIAL_ATTACK)) + count += 1 + if user.opposes?(b) + score -= 20 + else + score -= user.stages[:ATTACK] * 10 + score -= user.stages[:SPECIAL_ATTACK] * 10 + end + end + end + next 0 if count == 0 + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RaiseGrassBattlersDef1", + proc { |score, move, user, target, skill, ai, battle| + count = 0 + battle.allBattlers.each do |b| + if b.pbHasType?(:GRASS) && !b.statStageAtMax?(:DEFENSE) + count += 1 + if user.opposes?(b) + score -= 20 + else + score -= user.stages[:DEFENSE] * 10 + end + end + end + next 0 if count == 0 + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("UserTargetSwapAtkSpAtkStages", + proc { |score, move, user, target, skill, ai, battle| + if ai.skill_check(Battle::AI::AILevel.medium) + aatk = user.stages[:ATTACK] + aspa = user.stages[:SPECIAL_ATTACK] + oatk = target.stages[:ATTACK] + ospa = target.stages[:SPECIAL_ATTACK] + if aatk >= oatk && aspa >= ospa + score -= 80 + else + score += (oatk - aatk) * 10 + score += (ospa - aspa) * 10 + end + else + score -= 50 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("UserTargetSwapDefSpDefStages", + proc { |score, move, user, target, skill, ai, battle| + if ai.skill_check(Battle::AI::AILevel.medium) + adef = user.stages[:DEFENSE] + aspd = user.stages[:SPECIAL_DEFENSE] + odef = target.stages[:DEFENSE] + ospd = target.stages[:SPECIAL_DEFENSE] + if adef >= odef && aspd >= ospd + score -= 80 + else + score += (odef - adef) * 10 + score += (ospd - aspd) * 10 + end + else + score -= 50 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("UserTargetSwapStatStages", + proc { |score, move, user, target, skill, ai, battle| + if ai.skill_check(Battle::AI::AILevel.medium) + userStages = 0 + targetStages = 0 + GameData::Stat.each_battle do |s| + userStages += user.stages[s.id] + targetStages += target.stages[s.id] + end + score += (targetStages - userStages) * 10 + else + score -= 50 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("UserCopyTargetStatStages", + proc { |score, move, user, target, skill, ai, battle| + if ai.skill_check(Battle::AI::AILevel.medium) + equal = true + GameData::Stat.each_battle do |s| + stagediff = target.stages[s.id] - user.stages[s.id] + score += stagediff * 10 + equal = false if stagediff != 0 + end + score -= 80 if equal + else + score -= 50 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("UserStealTargetPositiveStatStages", + proc { |score, move, user, target, skill, ai, battle| + numStages = 0 + GameData::Stat.each_battle do |s| + next if target.stages[s.id] <= 0 + numStages += target.stages[s.id] + end + next score + numStages * 20 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("InvertTargetStatStages", + proc { |score, move, user, target, skill, ai, battle| + next 0 if target.effects[PBEffects::Substitute] > 0 + numpos = 0 + numneg = 0 + GameData::Stat.each_battle do |s| + numpos += target.stages[s.id] if target.stages[s.id] > 0 + numneg += target.stages[s.id] if target.stages[s.id] < 0 + end + next 0 if numpos == 0 && numneg == 0 + next score + (numpos - numneg) * 10 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("ResetTargetStatStages", + proc { |score, move, user, target, skill, ai, battle| + next 0 if target.effects[PBEffects::Substitute] > 0 + avg = 0 + anyChange = false + GameData::Stat.each_battle do |s| + next if target.stages[s.id] == 0 + avg += target.stages[s.id] + anyChange = true + end + next 0 if !anyChange + next score + avg * 10 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("ResetAllBattlersStatStages", + proc { |score, move, user, target, skill, ai, battle| + if ai.skill_check(Battle::AI::AILevel.medium) + stages = 0 + battle.allBattlers.each do |b| + totalStages = 0 + GameData::Stat.each_battle { |s| totalStages += b.stages[s.id] } + if b.opposes?(user) + stages += totalStages + else + stages -= totalStages + end + end + next score + stages * 10 + end + } +) + +Battle::AI::Handlers::MoveEffectScore.add("StartUserSideImmunityToStatStageLowering", + proc { |score, move, user, target, skill, ai, battle| + next score - 80 if user.pbOwnSide.effects[PBEffects::Mist] > 0 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("UserSwapBaseAtkDef", + proc { |score, move, user, target, skill, ai, battle| + if ai.skill_check(Battle::AI::AILevel.medium) + aatk = pbRoughStat(user, :ATTACK) + adef = pbRoughStat(user, :DEFENSE) + if aatk == adef || + user.effects[PBEffects::PowerTrick] # No flip-flopping + score -= 90 + elsif adef > aatk # Prefer a higher Attack + score += 30 + else + score -= 30 + end + else + score -= 30 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("UserTargetSwapBaseSpeed", + proc { |score, move, user, target, skill, ai, battle| + if ai.skill_check(Battle::AI::AILevel.medium) + if user.speed > target.speed + score += 50 + else + score -= 70 + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("UserTargetAverageBaseAtkSpAtk", + proc { |score, move, user, target, skill, ai, battle| + if ai.skill_check(Battle::AI::AILevel.medium) + aatk = pbRoughStat(user, :ATTACK) + aspatk = pbRoughStat(user, :SPECIAL_ATTACK) + oatk = pbRoughStat(target, :ATTACK) + ospatk = pbRoughStat(target, :SPECIAL_ATTACK) + if aatk < oatk && aspatk < ospatk + score += 50 + elsif aatk + aspatk < oatk + ospatk + score += 30 + else + score -= 50 + end + else + score -= 30 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("UserTargetAverageBaseDefSpDef", + proc { |score, move, user, target, skill, ai, battle| + if ai.skill_check(Battle::AI::AILevel.medium) + adef = pbRoughStat(user, :DEFENSE) + aspdef = pbRoughStat(user, :SPECIAL_DEFENSE) + odef = pbRoughStat(target, :DEFENSE) + ospdef = pbRoughStat(target, :SPECIAL_DEFENSE) + if adef < odef && aspdef < ospdef + score += 50 + elsif adef + aspdef < odef + ospdef + score += 30 + else + score -= 50 + end + else + score -= 30 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("UserTargetAverageHP", + proc { |score, move, user, target, skill, ai, battle| + if target.effects[PBEffects::Substitute] > 0 + score -= 90 + elsif user.hp >= (user.hp + target.hp) / 2 + score -= 90 + else + score += 40 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("StartUserSideDoubleSpeed", + proc { |score, move, user, target, skill, ai, battle| + next score - 90 if user.pbOwnSide.effects[PBEffects::Tailwind] > 0 + } +) + +# StartSwapAllBattlersBaseDefensiveStats diff --git a/Data/Scripts/011_Battle/005_AI/053_AI_MoveHandlers_BattlerOther.rb b/Data/Scripts/011_Battle/005_AI/053_AI_MoveHandlers_BattlerOther.rb new file mode 100644 index 000000000..1c4ee1184 --- /dev/null +++ b/Data/Scripts/011_Battle/005_AI/053_AI_MoveHandlers_BattlerOther.rb @@ -0,0 +1,683 @@ +#=============================================================================== +# +#=============================================================================== +Battle::AI::Handlers::MoveEffectScore.add("SleepTarget", + proc { |score, move, user, target, skill, ai, battle| + if target.pbCanSleep?(user, false) + score += 30 + if ai.skill_check(Battle::AI::AILevel.medium) + score -= 30 if target.effects[PBEffects::Yawn] > 0 + end + if ai.skill_check(Battle::AI::AILevel.high) + score -= 30 if target.hasActiveAbility?(:MARVELSCALE) + end + if ai.skill_check(Battle::AI::AILevel.best) + if target.pbHasMoveFunction?("FlinchTargetFailsIfUserNotAsleep", + "UseRandomUserMoveIfAsleep") # Snore, Sleep Talk + score -= 50 + end + end + else + next 0 if move.statusMove? + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("SleepTarget", + "SleepTargetIfUserDarkrai", + "SleepTargetChangeUserMeloettaForm") + +Battle::AI::Handlers::MoveEffectScore.add("SleepTargetNextTurn", + proc { |score, move, user, target, skill, ai, battle| + next 0 if target.effects[PBEffects::Yawn] > 0 || !target.pbCanSleep?(user, false) + score += 30 + if ai.skill_check(Battle::AI::AILevel.high) + score -= 30 if target.hasActiveAbility?(:MARVELSCALE) + end + if ai.skill_check(Battle::AI::AILevel.best) + if target.pbHasMoveFunction?("FlinchTargetFailsIfUserNotAsleep", + "UseRandomUserMoveIfAsleep") # Snore, Sleep Talk + score -= 50 + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("PoisonTarget", + proc { |score, move, user, target, skill, ai, battle| + if target.pbCanPoison?(user, false) + score += 30 + if ai.skill_check(Battle::AI::AILevel.medium) + score += 30 if target.hp <= target.totalhp / 4 + score += 50 if target.hp <= target.totalhp / 8 + score -= 40 if target.effects[PBEffects::Yawn] > 0 + end + if ai.skill_check(Battle::AI::AILevel.high) + score += 10 if pbRoughStat(target, :DEFENSE) > 100 + score += 10 if pbRoughStat(target, :SPECIAL_DEFENSE) > 100 + score -= 40 if target.hasActiveAbility?([:GUTS, :MARVELSCALE, :TOXICBOOST]) + end + else + next 0 if move.statusMove? + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("PoisonTargetLowerTargetSpeed1", + proc { |score, move, user, target, skill, ai, battle| + next 0 if !target.pbCanPoison?(user, false) && !target.pbCanLowerStatStage?(:SPEED, user) + if target.pbCanPoison?(user, false) + score += 30 + if ai.skill_check(Battle::AI::AILevel.medium) + score += 30 if target.hp <= target.totalhp / 4 + score += 50 if target.hp <= target.totalhp / 8 + score -= 40 if target.effects[PBEffects::Yawn] > 0 + end + if ai.skill_check(Battle::AI::AILevel.high) + score += 10 if pbRoughStat(target, :DEFENSE) > 100 + score += 10 if pbRoughStat(target, :SPECIAL_DEFENSE) > 100 + score -= 40 if target.hasActiveAbility?([:GUTS, :MARVELSCALE, :TOXICBOOST]) + end + end + if target.pbCanLowerStatStage?(:SPEED, user) + score += target.stages[:SPEED] * 10 + if ai.skill_check(Battle::AI::AILevel.high) + aspeed = pbRoughStat(user, :SPEED) + ospeed = pbRoughStat(target, :SPEED) + score += 30 if aspeed < ospeed && aspeed * 2 > ospeed + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("BadPoisonTarget", + proc { |score, move, user, target, skill, ai, battle| + if target.pbCanPoison?(user, false) + score += 30 + if ai.skill_check(Battle::AI::AILevel.medium) + score += 30 if target.hp <= target.totalhp / 4 + score += 50 if target.hp <= target.totalhp / 8 + score -= 40 if target.effects[PBEffects::Yawn] > 0 + end + if ai.skill_check(Battle::AI::AILevel.high) + score += 10 if pbRoughStat(target, :DEFENSE) > 100 + score += 10 if pbRoughStat(target, :SPECIAL_DEFENSE) > 100 + score -= 40 if target.hasActiveAbility?([:GUTS, :MARVELSCALE, :TOXICBOOST]) + end + else + score -= 90 if move.statusMove? + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("ParalyzeTarget", + proc { |score, move, user, target, skill, ai, battle| + if target.pbCanParalyze?(user, false) && + !(ai.skill_check(Battle::AI::AILevel.medium) && + move.id == :THUNDERWAVE && + Effectiveness.ineffective?(pbCalcTypeMod(move.type, user, target))) + score += 30 + if ai.skill_check(Battle::AI::AILevel.medium) + aspeed = pbRoughStat(user, :SPEED) + ospeed = pbRoughStat(target, :SPEED) + if aspeed < ospeed + score += 30 + elsif aspeed > ospeed + score -= 40 + end + end + if ai.skill_check(Battle::AI::AILevel.high) + score -= 40 if target.hasActiveAbility?([:GUTS, :MARVELSCALE, :QUICKFEET]) + end + else + score -= 90 if move.statusMove? + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("ParalyzeTarget", + "ParalyzeTargetIfNotTypeImmune", + "ParalyzeTargetAlwaysHitsInRainHitsTargetInSky", + "ParalyzeFlinchTarget") + +Battle::AI::Handlers::MoveEffectScore.add("BurnTarget", + proc { |score, move, user, target, skill, ai, battle| + if target.pbCanBurn?(user, false) + score += 30 + if ai.skill_check(Battle::AI::AILevel.high) + score -= 40 if target.hasActiveAbility?([:GUTS, :MARVELSCALE, :QUICKFEET, :FLAREBOOST]) + end + else + score -= 90 if move.statusMove? + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("BurnTarget", + "BurnTargetIfTargetStatsRaisedThisTurn", + "BurnFlinchTarget") + +Battle::AI::Handlers::MoveEffectScore.add("FreezeTarget", + proc { |score, move, user, target, skill, ai, battle| + if target.pbCanFreeze?(user, false) + score += 30 + if ai.skill_check(Battle::AI::AILevel.high) + score -= 20 if target.hasActiveAbility?(:MARVELSCALE) + end + else + score -= 90 if move.statusMove? + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("FreezeTargetSuperEffectiveAgainstWater", + proc { |score, move, user, target, skill, ai, battle| + if target.pbCanFreeze?(user, false) + score += 30 + if ai.skill_check(Battle::AI::AILevel.high) + score -= 20 if target.hasActiveAbility?(:MARVELSCALE) + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("FreezeTargetAlwaysHitsInHail", + proc { |score, move, user, target, skill, ai, battle| + if target.pbCanFreeze?(user, false) + score += 30 + if ai.skill_check(Battle::AI::AILevel.high) + score -= 20 if target.hasActiveAbility?(:MARVELSCALE) + end + else + score -= 90 if move.statusMove? + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("FreezeTargetAlwaysHitsInHail", + "FreezeFlinchTarget") + +Battle::AI::Handlers::MoveEffectScore.add("ParalyzeBurnOrFreezeTarget", + proc { |score, move, user, target, skill, ai, battle| + next score + 30 if target.status == :NONE + } +) + +Battle::AI::Handlers::MoveEffectScore.add("GiveUserStatusToTarget", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.status == :NONE + next score + 40 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("CureUserBurnPoisonParalysis", + proc { |score, move, user, target, skill, ai, battle| + case user.status + when :POISON + score += 40 + if ai.skill_check(Battle::AI::AILevel.medium) + if user.hp < user.totalhp / 8 + score += 60 + elsif ai.skill_check(Battle::AI::AILevel.high) && + user.hp < (user.effects[PBEffects::Toxic] + 1) * user.totalhp / 16 + score += 60 + end + end + when :BURN, :PARALYSIS + score += 40 + else + score -= 90 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("CureUserPartyStatus", + proc { |score, move, user, target, skill, ai, battle| + statuses = 0 + battle.pbParty(user.index).each do |pkmn| + statuses += 1 if pkmn && pkmn.status != :NONE + end + if statuses == 0 + score -= 80 + else + score += 20 * statuses + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("CureTargetBurn", + proc { |score, move, user, target, skill, ai, battle| + if target.opposes?(user) + score -= 40 if target.status == :BURN + elsif target.status == :BURN + score += 40 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("StartUserSideImmunityToInflictedStatus", + proc { |score, move, user, target, skill, ai, battle| + if user.pbOwnSide.effects[PBEffects::Safeguard] > 0 + score -= 80 + elsif user.status != :NONE + score -= 40 + else + score += 30 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("FlinchTarget", + proc { |score, move, user, target, skill, ai, battle| + score += 30 + if ai.skill_check(Battle::AI::AILevel.high) + score += 30 if !target.hasActiveAbility?(:INNERFOCUS) && + target.effects[PBEffects::Substitute] == 0 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("FlinchTargetFailsIfUserNotAsleep", + proc { |score, move, user, target, skill, ai, battle| + next 0 if !user.asleep? + score += 100 # Because it can only be used while asleep + if ai.skill_check(Battle::AI::AILevel.high) + score += 30 if !target.hasActiveAbility?(:INNERFOCUS) && + target.effects[PBEffects::Substitute] == 0 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("FlinchTargetFailsIfNotUserFirstTurn", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.turnCount != 0 + if ai.skill_check(Battle::AI::AILevel.high) + score += 30 if !target.hasActiveAbility?(:INNERFOCUS) && + target.effects[PBEffects::Substitute] == 0 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("FlinchTargetDoublePowerIfTargetInSky", + proc { |score, move, user, target, skill, ai, battle| + if ai.skill_check(Battle::AI::AILevel.high) + score += 30 if !target.hasActiveAbility?(:INNERFOCUS) && + target.effects[PBEffects::Substitute] == 0 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("ConfuseTarget", + proc { |score, move, user, target, skill, ai, battle| + next 0 if !target.pbCanConfuse?(user, false) + next score + 30 + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("ConfuseTarget", + "ConfuseTargetAlwaysHitsInRainHitsTargetInSky") + +Battle::AI::Handlers::MoveEffectScore.add("AttractTarget", + proc { |score, move, user, target, skill, ai, battle| + canattract = true + agender = user.gender + ogender = target.gender + if agender == 2 || ogender == 2 || agender == ogender + score -= 90 + canattract = false + elsif target.effects[PBEffects::Attract] >= 0 + score -= 80 + canattract = false + elsif ai.skill_check(Battle::AI::AILevel.best) && target.hasActiveAbility?(:OBLIVIOUS) + score -= 80 + canattract = false + end + if ai.skill_check(Battle::AI::AILevel.high) + if canattract && target.hasActiveItem?(:DESTINYKNOT) && + user.pbCanAttract?(target, false) + score -= 30 + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("SetUserTypesBasedOnEnvironment", + proc { |score, move, user, target, skill, ai, battle| + if !user.canChangeType? + score -= 90 + elsif ai.skill_check(Battle::AI::AILevel.medium) + new_type = nil + case battle.field.terrain + when :Electric + new_type = :ELECTRIC if GameData::Type.exists?(:ELECTRIC) + when :Grassy + new_type = :GRASS if GameData::Type.exists?(:GRASS) + when :Misty + new_type = :FAIRY if GameData::Type.exists?(:FAIRY) + when :Psychic + new_type = :PSYCHIC if GameData::Type.exists?(:PSYCHIC) + end + if !new_type + envtypes = { + :None => :NORMAL, + :Grass => :GRASS, + :TallGrass => :GRASS, + :MovingWater => :WATER, + :StillWater => :WATER, + :Puddle => :WATER, + :Underwater => :WATER, + :Cave => :ROCK, + :Rock => :GROUND, + :Sand => :GROUND, + :Forest => :BUG, + :ForestGrass => :BUG, + :Snow => :ICE, + :Ice => :ICE, + :Volcano => :FIRE, + :Graveyard => :GHOST, + :Sky => :FLYING, + :Space => :DRAGON, + :UltraSpace => :PSYCHIC + } + new_type = envtypes[battle.environment] + new_type = nil if !GameData::Type.exists?(new_type) + new_type ||= :NORMAL + end + score -= 90 if !user.pbHasOtherType?(new_type) + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("SetUserTypesToResistLastAttack", + proc { |score, move, user, target, skill, ai, battle| + next 0 if !user.canChangeType? + next 0 if !target.lastMoveUsed || !target.lastMoveUsedType || + GameData::Type.get(target.lastMoveUsedType).pseudo_type + aType = nil + target.eachMove do |m| + next if m.id != target.lastMoveUsed + aType = m.pbCalcType(user) + break + end + next 0 if !aType + has_possible_type = false + GameData::Type.each do |t| + next if t.pseudo_type || user.pbHasType?(t.id) || + !Effectiveness.resistant_type?(target.lastMoveUsedType, t.id) + has_possible_type = true + break + end + next 0 if !has_possible_type + } +) + +Battle::AI::Handlers::MoveEffectScore.add("SetUserTypesToTargetTypes", + proc { |score, move, user, target, skill, ai, battle| + next 0 if !user.canChangeType? || target.pbTypes(true).length == 0 + next 0 if user.pbTypes == target.pbTypes && + user.effects[PBEffects::Type3] == target.effects[PBEffects::Type3] + } +) + +Battle::AI::Handlers::MoveEffectScore.add("SetUserTypesToUserMoveType", + proc { |score, move, user, target, skill, ai, battle| + next 0 if !user.canChangeType? + has_possible_type = false + user.eachMoveWithIndex do |m, i| + break if Settings::MECHANICS_GENERATION >= 6 && i > 0 + next if GameData::Type.get(m.type).pseudo_type + next if user.pbHasType?(m.type) + has_possible_type = true + break + end + next 0 if !has_possible_type + } +) + +Battle::AI::Handlers::MoveEffectScore.add("SetTargetTypesToPsychic", + proc { |score, move, user, target, skill, ai, battle| + if target.pbHasOtherType?(:PSYCHIC) + score -= 90 + elsif !target.canChangeType? + score -= 90 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("SetTargetTypesToWater", + proc { |score, move, user, target, skill, ai, battle| + if target.effects[PBEffects::Substitute] > 0 || !target.canChangeType? + score -= 90 + elsif !target.pbHasOtherType?(:WATER) + score -= 90 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("AddGhostTypeToTarget", + proc { |score, move, user, target, skill, ai, battle| + next 0 if target.pbHasType?(:GHOST) + } +) + +Battle::AI::Handlers::MoveEffectScore.add("AddGrassTypeToTarget", + proc { |score, move, user, target, skill, ai, battle| + next 0 if target.pbHasType?(:GRASS) + } +) + +Battle::AI::Handlers::MoveEffectScore.add("UserLosesFireType", + proc { |score, move, user, target, skill, ai, battle| + next 0 if !user.pbHasType?(:FIRE) + } +) + +Battle::AI::Handlers::MoveEffectScore.add("SetTargetAbilityToSimple", + proc { |score, move, user, target, skill, ai, battle| + next 0 if target.effects[PBEffects::Substitute] > 0 + if ai.skill_check(Battle::AI::AILevel.medium) + next 0 if target.unstoppableAbility? || + [:TRUANT, :SIMPLE].include?(target.ability) + end + } +) + +Battle::AI::Handlers::MoveEffectScore.add("SetTargetAbilityToInsomnia", + proc { |score, move, user, target, skill, ai, battle| + next 0 if target.effects[PBEffects::Substitute] > 0 + if ai.skill_check(Battle::AI::AILevel.medium) + next 0 if target.unstoppableAbility? || + [:TRUANT, :INSOMNIA].include?(target.ability) + end + } +) + +Battle::AI::Handlers::MoveEffectScore.add("SetUserAbilityToTargetAbility", + proc { |score, move, user, target, skill, ai, battle| + score -= 40 # don't prefer this move + if ai.skill_check(Battle::AI::AILevel.medium) + if !target.ability || user.ability == target.ability || + [:MULTITYPE, :RKSSYSTEM].include?(user.ability_id) || + [:FLOWERGIFT, :FORECAST, :ILLUSION, :IMPOSTER, :MULTITYPE, :RKSSYSTEM, + :TRACE, :WONDERGUARD, :ZENMODE].include?(target.ability_id) + score -= 90 + end + end + if ai.skill_check(Battle::AI::AILevel.high) + if target.ability == :TRUANT && user.opposes?(target) + score -= 90 + elsif target.ability == :SLOWSTART && user.opposes?(target) + score -= 90 + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("SetTargetAbilityToUserAbility", + proc { |score, move, user, target, skill, ai, battle| + score -= 40 # don't prefer this move + if target.effects[PBEffects::Substitute] > 0 + score -= 90 + elsif ai.skill_check(Battle::AI::AILevel.medium) + if !user.ability || user.ability == target.ability || + [:MULTITYPE, :RKSSYSTEM, :TRUANT].include?(target.ability_id) || + [:FLOWERGIFT, :FORECAST, :ILLUSION, :IMPOSTER, :MULTITYPE, :RKSSYSTEM, + :TRACE, :ZENMODE].include?(user.ability_id) + score -= 90 + end + if ai.skill_check(Battle::AI::AILevel.high) + if user.ability == :TRUANT && user.opposes?(target) + score += 90 + elsif user.ability == :SLOWSTART && user.opposes?(target) + score += 90 + end + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("UserTargetSwapAbilities", + proc { |score, move, user, target, skill, ai, battle| + score -= 40 # don't prefer this move + if ai.skill_check(Battle::AI::AILevel.medium) + if (!user.ability && !target.ability) || + user.ability == target.ability || + [:ILLUSION, :MULTITYPE, :RKSSYSTEM, :WONDERGUARD].include?(user.ability_id) || + [:ILLUSION, :MULTITYPE, :RKSSYSTEM, :WONDERGUARD].include?(target.ability_id) + score -= 90 + end + end + if ai.skill_check(Battle::AI::AILevel.high) + if target.ability == :TRUANT && user.opposes?(target) + score -= 90 + elsif target.ability == :SLOWSTART && user.opposes?(target) + score -= 90 + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("NegateTargetAbility", + proc { |score, move, user, target, skill, ai, battle| + if target.effects[PBEffects::Substitute] > 0 || + target.effects[PBEffects::GastroAcid] + score -= 90 + elsif ai.skill_check(Battle::AI::AILevel.high) + score -= 90 if [:MULTITYPE, :RKSSYSTEM, :SLOWSTART, :TRUANT].include?(target.ability_id) + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("NegateTargetAbilityIfTargetActed", + proc { |score, move, user, target, skill, ai, battle| + if ai.skill_check(Battle::AI::AILevel.medium) + userSpeed = pbRoughStat(user, :SPEED) + targetSpeed = pbRoughStat(target, :SPEED) + if userSpeed < targetSpeed + score += 30 + end + else + score += 30 + end + next score + } +) + +# IgnoreTargetAbility + +Battle::AI::Handlers::MoveEffectScore.add("StartUserAirborne", + proc { |score, move, user, target, skill, ai, battle| + if user.effects[PBEffects::MagnetRise] > 0 || + user.effects[PBEffects::Ingrain] || + user.effects[PBEffects::SmackDown] + score -= 90 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("StartTargetAirborneAndAlwaysHitByMoves", + proc { |score, move, user, target, skill, ai, battle| + if target.effects[PBEffects::Telekinesis] > 0 || + target.effects[PBEffects::Ingrain] || + target.effects[PBEffects::SmackDown] + score -= 90 + end + next score + } +) + +# HitsTargetInSky + +Battle::AI::Handlers::MoveEffectScore.add("HitsTargetInSkyGroundsTarget", + proc { |score, move, user, target, skill, ai, battle| + if ai.skill_check(Battle::AI::AILevel.medium) + score += 20 if target.effects[PBEffects::MagnetRise] > 0 + score += 20 if target.effects[PBEffects::Telekinesis] > 0 + score += 20 if target.inTwoTurnAttack?("TwoTurnAttackInvulnerableInSky", + "TwoTurnAttackInvulnerableInSkyParalyzeTarget") + score += 20 if target.pbHasType?(:FLYING) + score += 20 if target.hasActiveAbility?(:LEVITATE) + score += 20 if target.hasActiveItem?(:AIRBALLOON) + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("StartGravity", + proc { |score, move, user, target, skill, ai, battle| + if battle.field.effects[PBEffects::Gravity] > 0 + score -= 90 + elsif ai.skill_check(Battle::AI::AILevel.medium) + score -= 30 + score -= 20 if user.effects[PBEffects::SkyDrop] >= 0 + score -= 20 if user.effects[PBEffects::MagnetRise] > 0 + score -= 20 if user.effects[PBEffects::Telekinesis] > 0 + score -= 20 if user.pbHasType?(:FLYING) + score -= 20 if user.hasActiveAbility?(:LEVITATE) + score -= 20 if user.hasActiveItem?(:AIRBALLOON) + score += 20 if target.effects[PBEffects::SkyDrop] >= 0 + score += 20 if target.effects[PBEffects::MagnetRise] > 0 + score += 20 if target.effects[PBEffects::Telekinesis] > 0 + score += 20 if target.inTwoTurnAttack?("TwoTurnAttackInvulnerableInSky", + "TwoTurnAttackInvulnerableInSkyParalyzeTarget", + "TwoTurnAttackInvulnerableInSkyTargetCannotAct") + score += 20 if target.pbHasType?(:FLYING) + score += 20 if target.hasActiveAbility?(:LEVITATE) + score += 20 if target.hasActiveItem?(:AIRBALLOON) + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("TransformUserIntoTarget", + proc { |score, move, user, target, skill, ai, battle| + next score - 70 + } +) diff --git a/Data/Scripts/011_Battle/005_AI/054_AI_MoveHandlers_MoveAttributes.rb b/Data/Scripts/011_Battle/005_AI/054_AI_MoveHandlers_MoveAttributes.rb new file mode 100644 index 000000000..0ebf64d73 --- /dev/null +++ b/Data/Scripts/011_Battle/005_AI/054_AI_MoveHandlers_MoveAttributes.rb @@ -0,0 +1,492 @@ +#=============================================================================== +# +#=============================================================================== +Battle::AI::Handlers::MoveEffectScore.add("FixedDamage20", + proc { |score, move, user, target, skill, ai, battle| + if target.hp <= 20 + score += 80 + elsif target.level >= 25 + score -= 60 # Not useful against high-level Pokemon + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("FixedDamage40", + proc { |score, move, user, target, skill, ai, battle| + next score + 80 if target.hp <= 40 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("FixedDamageHalfTargetHP", + proc { |score, move, user, target, skill, ai, battle| + score -= 50 + next score + target.hp * 100 / target.totalhp + } +) + +Battle::AI::Handlers::MoveEffectScore.add("FixedDamageUserLevel", + proc { |score, move, user, target, skill, ai, battle| + next score + 80 if target.hp <= user.level + } +) + +Battle::AI::Handlers::MoveEffectScore.add("FixedDamageUserLevelRandom", + proc { |score, move, user, target, skill, ai, battle| + next score + 30 if target.hp <= user.level + } +) + +Battle::AI::Handlers::MoveEffectScore.add("LowerTargetHPToUserHP", + proc { |score, move, user, target, skill, ai, battle| + if user.hp >= target.hp + score -= 90 + elsif user.hp < target.hp / 2 + score += 50 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("OHKO", + proc { |score, move, user, target, skill, ai, battle| + next 0 if target.hasActiveAbility?(:STURDY) + next 0 if target.level > user.level + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("OHKO", + "OHKOIce", + "OHKOHitsUndergroundTarget") + +Battle::AI::Handlers::MoveEffectScore.add("DamageTargetAlly", + proc { |score, move, user, target, skill, ai, battle| + target.allAllies.each do |b| + next if !b.near?(target) + score += 10 + end + next score + } +) + +# PowerHigherWithUserHP + +# PowerLowerWithUserHP + +# PowerHigherWithTargetHP + +# PowerHigherWithUserHappiness + +# PowerLowerWithUserHappiness + +# PowerHigherWithUserPositiveStatStages + +# PowerHigherWithTargetPositiveStatStages + +# PowerHigherWithUserFasterThanTarget + +# PowerHigherWithTargetFasterThanUser + +# PowerHigherWithLessPP + +# PowerHigherWithTargetWeight + +# PowerHigherWithUserHeavierThanTarget + +# PowerHigherWithConsecutiveUse + +# PowerHigherWithConsecutiveUseOnUserSide + +# RandomPowerDoublePowerIfTargetUnderground + +# DoublePowerIfTargetHPLessThanHalf + +# DoublePowerIfUserPoisonedBurnedParalyzed + +Battle::AI::Handlers::MoveEffectScore.add("DoublePowerIfTargetAsleepCureTarget", + proc { |score, move, user, target, skill, ai, battle| + next score - 20 if target.status == :SLEEP && # Will cure status + target.statusCount > 1 + } +) + +# DoublePowerIfTargetPoisoned + +Battle::AI::Handlers::MoveEffectScore.add("DoublePowerIfTargetParalyzedCureTarget", + proc { |score, move, user, target, skill, ai, battle| + next score - 20 if target.status == :PARALYSIS # Will cure status + } +) + +# DoublePowerIfTargetStatusProblem + +# DoublePowerIfUserHasNoItem + +# DoublePowerIfTargetUnderwater + +# DoublePowerIfTargetUnderground + +# DoublePowerIfTargetInSky + +Battle::AI::Handlers::MoveEffectScore.add("DoublePowerInElectricTerrain", + proc { |score, move, user, target, skill, ai, battle| + next score + 40 if battle.field.terrain == :Electric && target.affectedByTerrain? + } +) + +# DoublePowerIfUserLastMoveFailed + +# DoublePowerIfAllyFaintedLastTurn + +Battle::AI::Handlers::MoveEffectScore.add("DoublePowerIfUserLostHPThisTurn", + proc { |score, move, user, target, skill, ai, battle| + attspeed = pbRoughStat(user, :SPEED) + oppspeed = pbRoughStat(target, :SPEED) + next score + 30 if oppspeed > attspeed + } +) + +Battle::AI::Handlers::MoveEffectScore.add("DoublePowerIfTargetLostHPThisTurn", + proc { |score, move, user, target, skill, ai, battle| + next score + 20 if battle.pbOpposingBattlerCount(user) > 1 + } +) + +# DoublePowerIfUserStatsLoweredThisTurn + +Battle::AI::Handlers::MoveEffectScore.add("DoublePowerIfTargetActed", + proc { |score, move, user, target, skill, ai, battle| + attspeed = pbRoughStat(user, :SPEED) + oppspeed = pbRoughStat(target, :SPEED) + next score + 30 if oppspeed > attspeed + } +) + +# DoublePowerIfTargetNotActed + +# AlwaysCriticalHit + +Battle::AI::Handlers::MoveEffectScore.add("EnsureNextCriticalHit", + proc { |score, move, user, target, skill, ai, battle| + if user.effects[PBEffects::LaserFocus] > 0 + score -= 90 + else + score += 40 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("EnsureNextCriticalHit", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.pbOwnSide.effects[PBEffects::LuckyChant] > 0 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("CannotMakeTargetFaint", + proc { |score, move, user, target, skill, ai, battle| + next 0 if target.hp == 1 + if target.hp <= target.totalhp / 8 + score -= 60 + elsif target.hp <= target.totalhp / 4 + score -= 30 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("UserEnduresFaintingThisTurn", + proc { |score, move, user, target, skill, ai, battle| + score -= 25 if user.hp > user.totalhp / 2 + if ai.skill_check(Battle::AI::AILevel.medium) + score -= 90 if user.effects[PBEffects::ProtectRate] > 1 + score -= 90 if target.effects[PBEffects::HyperBeam] > 0 + else + score -= user.effects[PBEffects::ProtectRate] * 40 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("StartWeakenElectricMoves", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.effects[PBEffects::MudSport] + } +) + +Battle::AI::Handlers::MoveEffectScore.add("StartWeakenFireMoves", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.effects[PBEffects::WaterSport] + } +) + +Battle::AI::Handlers::MoveEffectScore.add("StartWeakenPhysicalDamageAgainstUserSide", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.pbOwnSide.effects[PBEffects::Reflect] > 0 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("StartWeakenSpecialDamageAgainstUserSide", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.pbOwnSide.effects[PBEffects::LightScreen] > 0 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("StartWeakenDamageAgainstUserSideIfHail", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.pbOwnSide.effects[PBEffects::AuroraVeil] > 0 || user.effectiveWeather != :Hail + next score + 40 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RemoveScreens", + proc { |score, move, user, target, skill, ai, battle| + score += 20 if user.pbOpposingSide.effects[PBEffects::AuroraVeil] > 0 + score += 20 if user.pbOpposingSide.effects[PBEffects::Reflect] > 0 + score += 20 if user.pbOpposingSide.effects[PBEffects::LightScreen] > 0 + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("ProtectUser", + proc { |score, move, user, target, skill, ai, battle| + if user.effects[PBEffects::ProtectRate] > 1 || + target.effects[PBEffects::HyperBeam] > 0 + score -= 90 + else + if ai.skill_check(Battle::AI::AILevel.medium) + score -= user.effects[PBEffects::ProtectRate] * 40 + end + score += 50 if user.turnCount == 0 + score += 30 if target.effects[PBEffects::TwoTurnAttack] + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("ProtectUserBanefulBunker", + proc { |score, move, user, target, skill, ai, battle| + if user.effects[PBEffects::ProtectRate] > 1 || + target.effects[PBEffects::HyperBeam] > 0 + score -= 90 + else + if ai.skill_check(Battle::AI::AILevel.medium) + score -= user.effects[PBEffects::ProtectRate] * 40 + end + score += 50 if user.turnCount == 0 + score += 30 if target.effects[PBEffects::TwoTurnAttack] + score += 20 # Because of possible poisoning + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("ProtectUserFromDamagingMovesKingsShield", + proc { |score, move, user, target, skill, ai, battle| + if user.effects[PBEffects::ProtectRate] > 1 || + target.effects[PBEffects::HyperBeam] > 0 + score -= 90 + else + if ai.skill_check(Battle::AI::AILevel.medium) + score -= user.effects[PBEffects::ProtectRate] * 40 + end + score += 50 if user.turnCount == 0 + score += 30 if target.effects[PBEffects::TwoTurnAttack] + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("ProtectUserFromDamagingMovesObstruct", + proc { |score, move, user, target, skill, ai, battle| + if user.effects[PBEffects::ProtectRate] > 1 || + target.effects[PBEffects::HyperBeam] > 0 + score -= 90 + else + if ai.skill_check(Battle::AI::AILevel.medium) + score -= user.effects[PBEffects::ProtectRate] * 40 + end + score += 50 if user.turnCount == 0 + score += 30 if target.effects[PBEffects::TwoTurnAttack] + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("ProtectUserFromTargetingMovesSpikyShield", + proc { |score, move, user, target, skill, ai, battle| + if user.effects[PBEffects::ProtectRate] > 1 || + target.effects[PBEffects::HyperBeam] > 0 + score -= 90 + else + if ai.skill_check(Battle::AI::AILevel.medium) + score -= user.effects[PBEffects::ProtectRate] * 40 + end + score += 50 if user.turnCount == 0 + score += 30 if target.effects[PBEffects::TwoTurnAttack] + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("ProtectUserSideFromDamagingMovesIfUserFirstTurn", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.turnCount != 0 + next score + 30 + } +) + +# ProtectUserSideFromStatusMoves + +# ProtectUserSideFromPriorityMoves + +# ProtectUserSideFromMultiTargetDamagingMoves + +# RemoveProtections + +# RemoveProtectionsBypassSubstitute + +Battle::AI::Handlers::MoveEffectScore.add("HoopaRemoveProtectionsBypassSubstituteLowerUserDef1", + proc { |score, move, user, target, skill, ai, battle| + next 0 if !user.isSpecies?(:HOOPA) || user.form != 1 + next score + 20 if target.stages[:DEFENSE] > 0 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RecoilQuarterOfDamageDealt", + proc { |score, move, user, target, skill, ai, battle| + next score - 25 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RecoilThirdOfDamageDealtParalyzeTarget", + proc { |score, move, user, target, skill, ai, battle| + score -= 30 + if target.pbCanParalyze?(user, false) + score += 30 + if ai.skill_check(Battle::AI::AILevel.medium) + aspeed = pbRoughStat(user, :SPEED) + ospeed = pbRoughStat(target, :SPEED) + if aspeed < ospeed + score += 30 + elsif aspeed > ospeed + score -= 40 + end + end + if ai.skill_check(Battle::AI::AILevel.high) + score -= 40 if target.hasActiveAbility?([:GUTS, :MARVELSCALE, :QUICKFEET]) + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RecoilThirdOfDamageDealtBurnTarget", + proc { |score, move, user, target, skill, ai, battle| + score -= 30 + if target.pbCanBurn?(user, false) + score += 30 + if ai.skill_check(Battle::AI::AILevel.high) + score -= 40 if target.hasActiveAbility?([:GUTS, :MARVELSCALE, :QUICKFEET, :FLAREBOOST]) + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RecoilHalfOfDamageDealt", + proc { |score, move, user, target, skill, ai, battle| + next score - 40 + } +) + +# EffectivenessIncludesFlyingType + +Battle::AI::Handlers::MoveEffectScore.add("CategoryDependsOnHigherDamagePoisonTarget", + proc { |score, move, user, target, skill, ai, battle| + next score + 5 if target.pbCanPoison?(user, false) + } +) + +# CategoryDependsOnHigherDamageIgnoreTargetAbility + +# UseUserBaseDefenseInsteadOfUserBaseAttack + +# UseTargetAttackInsteadOfUserAttack + +# UseTargetDefenseInsteadOfTargetSpDef + +Battle::AI::Handlers::MoveEffectScore.add("EnsureNextMoveAlwaysHits", + proc { |score, move, user, target, skill, ai, battle| + next 0 if target.effects[PBEffects::Substitute] > 0 + next 0 if user.effects[PBEffects::LockOn] > 0 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("StartNegateTargetEvasionStatStageAndGhostImmunity", + proc { |score, move, user, target, skill, ai, battle| + if target.effects[PBEffects::Foresight] + score -= 90 + elsif target.pbHasType?(:GHOST) + score += 70 + elsif target.stages[:EVASION] <= 0 + score -= 60 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("StartNegateTargetEvasionStatStageAndDarkImmunity", + proc { |score, move, user, target, skill, ai, battle| + if target.effects[PBEffects::MiracleEye] + score -= 90 + elsif target.pbHasType?(:DARK) + score += 70 + elsif target.stages[:EVASION] <= 0 + score -= 60 + end + next score + } +) + +# IgnoreTargetDefSpDefEvaStatStages + +# TypeIsUserFirstType + +# TypeDependsOnUserIVs + +Battle::AI::Handlers::MoveEffectScore.add("TypeAndPowerDependOnUserBerry", + proc { |score, move, user, target, skill, ai, battle| + next 0 if !user.item || !user.item.is_berry? || !user.itemActive? + } +) + +# TypeDependsOnUserPlate + +# TypeDependsOnUserMemory + +# TypeDependsOnUserDrive + +Battle::AI::Handlers::MoveEffectScore.add("TypeDependsOnUserMorpekoFormRaiseUserSpeed1", + proc { |score, move, user, target, skill, ai, battle| + next score + 20 if user.stages[:SPEED] <= 0 + } +) + +# TypeAndPowerDependOnWeather + +Battle::AI::Handlers::MoveEffectScore.add("TypeAndPowerDependOnTerrain", + proc { |score, move, user, target, skill, ai, battle| + next score + 40 if battle.field.terrain != :None + } +) + +Battle::AI::Handlers::MoveEffectScore.add("TargetMovesBecomeElectric", + proc { |score, move, user, target, skill, ai, battle| + aspeed = pbRoughStat(user, :SPEED) + ospeed = pbRoughStat(target, :SPEED) + next 0 if aspeed > ospeed + } +) + +# NormalMovesBecomeElectric diff --git a/Data/Scripts/011_Battle/005_AI/055_AI_MoveHandlers_MultiHit.rb b/Data/Scripts/011_Battle/005_AI/055_AI_MoveHandlers_MultiHit.rb new file mode 100644 index 000000000..6105c4d11 --- /dev/null +++ b/Data/Scripts/011_Battle/005_AI/055_AI_MoveHandlers_MultiHit.rb @@ -0,0 +1,207 @@ +#=============================================================================== +# +#=============================================================================== + +# HitTwoTimes + +Battle::AI::Handlers::MoveEffectScore.add("HitTwoTimesPoisonTarget", + proc { |score, move, user, target, skill, ai, battle| + next 0 if !target.pbCanPoison?(user, false) + score += 30 + if ai.skill_check(Battle::AI::AILevel.medium) + score += 30 if target.hp <= target.totalhp / 4 + score += 50 if target.hp <= target.totalhp / 8 + score -= 40 if target.effects[PBEffects::Yawn] > 0 + end + if ai.skill_check(Battle::AI::AILevel.high) + score += 10 if pbRoughStat(target, :DEFENSE) > 100 + score += 10 if pbRoughStat(target, :SPECIAL_DEFENSE) > 100 + score -= 40 if target.hasActiveAbility?([:GUTS, :MARVELSCALE, :TOXICBOOST]) + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("HitTwoTimesFlinchTarget", + proc { |score, move, user, target, skill, ai, battle| + next score + 30 if target.effects[PBEffects::Minimize] + } +) + +# HitTwoTimesTargetThenTargetAlly + +# HitThreeTimesPowersUpWithEachHit + +Battle::AI::Handlers::MoveEffectScore.add("HitThreeTimesAlwaysCriticalHit", + proc { |score, move, user, target, skill, ai, battle| + if ai.skill_check(Battle::AI::AILevel.high) + stat = (move.physicalMove?) ? :DEFENSE : :SPECIAL_DEFENSE + next score + 50 if targets.stages[stat] > 1 + end + } +) + +# HitTwoToFiveTimes + +# HitTwoToFiveTimesOrThreeForAshGreninja + +Battle::AI::Handlers::MoveEffectScore.add("HitTwoToFiveTimesRaiseUserSpd1LowerUserDef1", + proc { |score, move, user, target, skill, ai, battle| + aspeed = pbRoughStat(user, :SPEED) + ospeed = pbRoughStat(target, :SPEED) + if aspeed > ospeed && aspeed * 2 / 3 < ospeed + score -= 50 + elsif aspeed < ospeed && aspeed * 1.5 > ospeed + score += 50 + end + score += user.stages[:DEFENSE] * 30 + next score + } +) + +# HitOncePerUserTeamMember + +# AttackAndSkipNextTurn + +# TwoTurnAttack + +# TwoTurnAttackOneTurnInSun + +Battle::AI::Handlers::MoveEffectScore.add("TwoTurnAttackParalyzeTarget", + proc { |score, move, user, target, skill, ai, battle| + if target.pbCanParalyze?(user, false) && + !(ai.skill_check(Battle::AI::AILevel.medium) && + move.id == :THUNDERWAVE && + Effectiveness.ineffective?(pbCalcTypeMod(move.type, user, target))) + score += 30 + if ai.skill_check(Battle::AI::AILevel.medium) + aspeed = pbRoughStat(user, :SPEED) + ospeed = pbRoughStat(target, :SPEED) + if aspeed < ospeed + score += 30 + elsif aspeed > ospeed + score -= 40 + end + end + if ai.skill_check(Battle::AI::AILevel.high) + score -= 40 if target.hasActiveAbility?([:GUTS, :MARVELSCALE, :QUICKFEET]) + end + elsif ai.skill_check(Battle::AI::AILevel.medium) + score -= 90 if move.statusMove? + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("TwoTurnAttackBurnTarget", + proc { |score, move, user, target, skill, ai, battle| + next 0 if !target.pbCanBurn?(user, false) + score += 30 + if ai.skill_check(Battle::AI::AILevel.high) + score -= 40 if target.hasActiveAbility?([:GUTS, :MARVELSCALE, :QUICKFEET, :FLAREBOOST]) + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("TwoTurnAttackFlinchTarget", + proc { |score, move, user, target, skill, ai, battle| + score += 20 if user.effects[PBEffects::FocusEnergy] > 0 + if ai.skill_check(Battle::AI::AILevel.high) + score += 20 if !target.hasActiveAbility?(:INNERFOCUS) && + target.effects[PBEffects::Substitute] == 0 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("TwoTurnAttackRaiseUserSpAtkSpDefSpd2", + proc { |score, move, user, target, skill, ai, battle| + if user.statStageAtMax?(:SPECIAL_ATTACK) && + user.statStageAtMax?(:SPECIAL_DEFENSE) && + user.statStageAtMax?(:SPEED) + score -= 90 + else + score -= user.stages[:SPECIAL_ATTACK] * 10 # Only *10 instead of *20 + score -= user.stages[:SPECIAL_DEFENSE] * 10 # because two-turn attack + score -= user.stages[:SPEED] * 10 + if ai.skill_check(Battle::AI::AILevel.medium) + hasSpecialAttack = false + user.eachMove do |m| + next if !m.specialMove?(m.type) + hasSpecialAttack = true + break + end + if hasSpecialAttack + score += 20 + elsif ai.skill_check(Battle::AI::AILevel.high) + score -= 90 + end + end + if ai.skill_check(Battle::AI::AILevel.high) + aspeed = pbRoughStat(user, :SPEED) + ospeed = pbRoughStat(target, :SPEED) + score += 30 if aspeed < ospeed && aspeed * 2 > ospeed + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("TwoTurnAttackChargeRaiseUserDefense1", + proc { |score, move, user, target, skill, ai, battle| + if move.statusMove? + if user.statStageAtMax?(:DEFENSE) + score -= 90 + else + score -= user.stages[:DEFENSE] * 20 + end + elsif user.stages[:DEFENSE] < 0 + score += 20 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("TwoTurnAttackChargeRaiseUserSpAtk1", + proc { |score, move, user, target, skill, ai, battle| + aspeed = pbRoughStat(user, :SPEED) + ospeed = pbRoughStat(target, :SPEED) + if (aspeed > ospeed && user.hp > user.totalhp / 3) || user.hp > user.totalhp / 2 + score += 60 + else + score -= 90 + end + score += user.stages[:SPECIAL_ATTACK] * 20 + next score + } +) + +# TwoTurnAttackInvulnerableUnderground + +# TwoTurnAttackInvulnerableUnderwater + +# TwoTurnAttackInvulnerableInSky + +# TwoTurnAttackInvulnerableInSkyParalyzeTarget + +# TwoTurnAttackInvulnerableInSkyTargetCannotAct + +# TwoTurnAttackInvulnerableRemoveProtections + +# MultiTurnAttackPreventSleeping + +# MultiTurnAttackConfuseUserAtEnd + +# MultiTurnAttackPowersUpEachTurn + +Battle::AI::Handlers::MoveEffectScore.add("MultiTurnAttackBideThenReturnDoubleDamage", + proc { |score, move, user, target, skill, ai, battle| + if user.hp <= user.totalhp / 4 + score -= 90 + elsif user.hp <= user.totalhp / 2 + score -= 50 + end + next score + } +) diff --git a/Data/Scripts/011_Battle/005_AI/056_AI_MoveHandlers_Healing.rb b/Data/Scripts/011_Battle/005_AI/056_AI_MoveHandlers_Healing.rb new file mode 100644 index 000000000..6b0a3a6d6 --- /dev/null +++ b/Data/Scripts/011_Battle/005_AI/056_AI_MoveHandlers_Healing.rb @@ -0,0 +1,343 @@ +#=============================================================================== +# +#=============================================================================== +Battle::AI::Handlers::MoveEffectScore.add("HealUserFullyAndFallAsleep", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.hp == user.totalhp || !user.pbCanSleep?(user, false, nil, true) + score += 70 + score -= user.hp * 140 / user.totalhp + score += 30 if user.status != :NONE + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("HealUserHalfOfTotalHP", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.hp == user.totalhp || (ai.skill_check(Battle::AI::AILevel.medium) && !user.canHeal?) + score += 50 + score -= user.hp * 100 / user.totalhp + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("HealUserDependingOnWeather", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.hp == user.totalhp || (ai.skill_check(Battle::AI::AILevel.medium) && !user.canHeal?) + case user.effectiveWeather + when :Sun, :HarshSun + score += 30 + when :None + else + score -= 30 + end + score += 50 + score -= user.hp * 100 / user.totalhp + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("HealUserDependingOnSandstorm", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.hp == user.totalhp || (ai.skill_check(Battle::AI::AILevel.medium) && !user.canHeal?) + score += 50 + score -= user.hp * 100 / user.totalhp + score += 30 if user.effectiveWeather == :Sandstorm + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("HealUserHalfOfTotalHPLoseFlyingTypeThisTurn", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.hp == user.totalhp || (ai.skill_check(Battle::AI::AILevel.medium) && !user.canHeal?) + score += 50 + score -= user.hp * 100 / user.totalhp + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("CureTargetStatusHealUserHalfOfTotalHP", + proc { |score, move, user, target, skill, ai, battle| + if target.status == :NONE + score -= 90 + elsif user.hp == user.totalhp && target.opposes?(user) + score -= 90 + else + score += (user.totalhp - user.hp) * 50 / user.totalhp + score -= 30 if target.opposes?(user) + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("HealUserByTargetAttackLowerTargetAttack1", + proc { |score, move, user, target, skill, ai, battle| + if target.statStageAtMin?(:ATTACK) + score -= 90 + else + if target.pbCanLowerStatStage?(:ATTACK, user) + score += target.stages[:ATTACK] * 20 + if ai.skill_check(Battle::AI::AILevel.medium) + hasPhysicalAttack = false + target.eachMove do |m| + next if !m.physicalMove?(m.type) + hasPhysicalAttack = true + break + end + if hasPhysicalAttack + score += 20 + elsif ai.skill_check(Battle::AI::AILevel.high) + score -= 90 + end + end + end + score += (user.totalhp - user.hp) * 50 / user.totalhp + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("HealUserByHalfOfDamageDone", + proc { |score, move, user, target, skill, ai, battle| + if ai.skill_check(Battle::AI::AILevel.high) && target.hasActiveAbility?(:LIQUIDOOZE) + score -= 70 + elsif user.hp <= user.totalhp / 2 + score += 20 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("HealUserByHalfOfDamageDoneIfTargetAsleep", + proc { |score, move, user, target, skill, ai, battle| + next 0 if !target.asleep? + if ai.skill_check(Battle::AI::AILevel.high) && target.hasActiveAbility?(:LIQUIDOOZE) + score -= 70 + elsif user.hp <= user.totalhp / 2 + score += 20 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("HealUserByThreeQuartersOfDamageDone", + proc { |score, move, user, target, skill, ai, battle| + if ai.skill_check(Battle::AI::AILevel.high) && target.hasActiveAbility?(:LIQUIDOOZE) + score -= 80 + elsif user.hp <= user.totalhp / 2 + score += 40 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("HealUserAndAlliesQuarterOfTotalHP", + proc { |score, move, user, target, skill, ai, battle| + ally_amt = 30 + battle.allSameSideBattlers(user.index).each do |b| + if b.hp == b.totalhp || (ai.skill_check(Battle::AI::AILevel.medium) && !b.canHeal?) + score -= ally_amt / 2 + elsif b.hp < b.totalhp * 3 / 4 + score += ally_amt + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("HealUserAndAlliesQuarterOfTotalHPCureStatus", + proc { |score, move, user, target, skill, ai, battle| + ally_amt = 80 / battle.pbSideSize(user.index) + battle.allSameSideBattlers(user.index).each do |b| + if b.hp == b.totalhp || (ai.skill_check(Battle::AI::AILevel.medium) && !b.canHeal?) + score -= ally_amt + elsif b.hp < b.totalhp * 3 / 4 + score += ally_amt + end + score += ally_amt / 2 if b.pbHasAnyStatus? + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("HealTargetHalfOfTotalHP", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.opposes?(target) + if target.hp < target.totalhp / 2 && target.effects[PBEffects::Substitute] == 0 + score += 20 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("HealTargetDependingOnGrassyTerrain", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.hp == user.totalhp || (ai.skill_check(Battle::AI::AILevel.medium) && !user.canHeal?) + score += 50 + score -= user.hp * 100 / user.totalhp + if ai.skill_check(Battle::AI::AILevel.medium) + score += 30 if battle.field.terrain == :Grassy + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("HealUserPositionNextTurn", + proc { |score, move, user, target, skill, ai, battle| + next 0 if battle.positions[user.index].effects[PBEffects::Wish] > 0 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("StartHealUserEachTurn", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.effects[PBEffects::AquaRing] + } +) + +Battle::AI::Handlers::MoveEffectScore.add("StartHealUserEachTurnTrapUserInBattle", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.effects[PBEffects::Ingrain] + } +) + +Battle::AI::Handlers::MoveEffectScore.add("StartDamageTargetEachTurnIfTargetAsleep", + proc { |score, move, user, target, skill, ai, battle| + if target.effects[PBEffects::Nightmare] || + target.effects[PBEffects::Substitute] > 0 + score -= 90 + elsif !target.asleep? + score -= 90 + else + score -= 90 if target.statusCount <= 1 + score += 50 if target.statusCount > 3 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("StartLeechSeedTarget", + proc { |score, move, user, target, skill, ai, battle| + if target.effects[PBEffects::LeechSeed] >= 0 + score -= 90 + elsif ai.skill_check(Battle::AI::AILevel.medium) && target.pbHasType?(:GRASS) + score -= 90 + elsif user.turnCount == 0 + score += 60 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("UserLosesHalfOfTotalHP", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.hp <= user.totalhp / 2 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("UserLosesHalfOfTotalHPExplosive", + proc { |score, move, user, target, skill, ai, battle| + reserves = battle.pbAbleNonActiveCount(user.idxOwnSide) + foes = battle.pbAbleNonActiveCount(user.idxOpposingSide) + if battle.pbCheckGlobalAbility(:DAMP) + score -= 100 + elsif ai.skill_check(Battle::AI::AILevel.medium) && reserves == 0 && foes > 0 + score -= 100 # don't want to lose + elsif ai.skill_check(Battle::AI::AILevel.high) && reserves == 0 && foes == 0 + score += 80 # want to draw + else + score -= (user.totalhp - user.hp) * 75 / user.totalhp + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("UserFaintsExplosive", + proc { |score, move, user, target, skill, ai, battle| + reserves = battle.pbAbleNonActiveCount(user.idxOwnSide) + foes = battle.pbAbleNonActiveCount(user.idxOpposingSide) + if battle.pbCheckGlobalAbility(:DAMP) + score -= 100 + elsif ai.skill_check(Battle::AI::AILevel.medium) && reserves == 0 && foes > 0 + score -= 100 # don't want to lose + elsif ai.skill_check(Battle::AI::AILevel.high) && reserves == 0 && foes == 0 + score += 80 # want to draw + else + score -= user.hp * 100 / user.totalhp + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("UserFaintsPowersUpInMistyTerrainExplosive", + proc { |score, move, user, target, skill, ai, battle| + reserves = battle.pbAbleNonActiveCount(user.idxOwnSide) + foes = battle.pbAbleNonActiveCount(user.idxOpposingSide) + if battle.pbCheckGlobalAbility(:DAMP) + score -= 100 + elsif ai.skill_check(Battle::AI::AILevel.medium) && reserves == 0 && foes > 0 + score -= 100 # don't want to lose + elsif ai.skill_check(Battle::AI::AILevel.high) && reserves == 0 && foes == 0 + score += 40 # want to draw + score += 40 if battle.field.terrain == :Misty + else + score -= user.hp * 100 / user.totalhp + score += 20 if battle.field.terrain == :Misty + end + next score + } +) + +# UserFaintsFixedDamageUserHP + +Battle::AI::Handlers::MoveEffectScore.add("UserFaintsLowerTargetAtkSpAtk2", + proc { |score, move, user, target, skill, ai, battle| + if !target.pbCanLowerStatStage?(:ATTACK, user) && + !target.pbCanLowerStatStage?(:SPECIAL_ATTACK, user) + score -= 100 + elsif battle.pbAbleNonActiveCount(user.idxOwnSide) == 0 + score -= 100 + else + score += target.stages[:ATTACK] * 10 + score += target.stages[:SPECIAL_ATTACK] * 10 + score -= user.hp * 100 / user.totalhp + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("UserFaintsHealAndCureReplacement", + proc { |score, move, user, target, skill, ai, battle| + next score - 70 + } +) + +Battle::AI::Handlers::MoveEffectScore.copy("UserFaintsHealAndCureReplacement", + "UserFaintsHealAndCureReplacementRestorePP") + +Battle::AI::Handlers::MoveEffectScore.add("StartPerishCountsForAllBattlers", + proc { |score, move, user, target, skill, ai, battle| + if battle.pbAbleNonActiveCount(user.idxOwnSide) == 0 + score -= 90 + elsif target.effects[PBEffects::PerishSong] > 0 + score -= 90 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("AttackerFaintsIfUserFaints", + proc { |score, move, user, target, skill, ai, battle| + score += 50 + score -= user.hp * 100 / user.totalhp + score += 30 if user.hp <= user.totalhp / 10 + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("SetAttackerMovePPTo0IfUserFaints", + proc { |score, move, user, target, skill, ai, battle| + score += 50 + score -= user.hp * 100 / user.totalhp + score += 30 if user.hp <= user.totalhp / 10 + next score + } +) diff --git a/Data/Scripts/011_Battle/005_AI/057_AI_MoveHandlers_Items.rb b/Data/Scripts/011_Battle/005_AI/057_AI_MoveHandlers_Items.rb new file mode 100644 index 000000000..12bc23ee9 --- /dev/null +++ b/Data/Scripts/011_Battle/005_AI/057_AI_MoveHandlers_Items.rb @@ -0,0 +1,193 @@ +#=============================================================================== +# +#=============================================================================== +Battle::AI::Handlers::MoveEffectScore.add("UserTakesTargetItem", + proc { |score, move, user, target, skill, ai, battle| + if ai.skill_check(Battle::AI::AILevel.high) + if !user.item && target.item + score += 40 + else + score -= 90 + end + else + score -= 80 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("TargetTakesUserItem", + proc { |score, move, user, target, skill, ai, battle| + if !user.item || target.item + score -= 90 + elsif user.hasActiveItem?([:FLAMEORB, :TOXICORB, :STICKYBARB, :IRONBALL, + :CHOICEBAND, :CHOICESCARF, :CHOICESPECS]) + score += 50 + else + score -= 80 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("UserTargetSwapItems", + proc { |score, move, user, target, skill, ai, battle| + if !user.item && !target.item + score -= 90 + elsif ai.skill_check(Battle::AI::AILevel.high) && target.hasActiveAbility?(:STICKYHOLD) + score -= 90 + elsif user.hasActiveItem?([:FLAMEORB, :TOXICORB, :STICKYBARB, :IRONBALL, + :CHOICEBAND, :CHOICESCARF, :CHOICESPECS]) + score += 50 + elsif !user.item && target.item + score -= 30 if user.lastMoveUsed && + GameData::Move.get(user.lastMoveUsed).function_code == "UserTargetSwapItems" + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RestoreUserConsumedItem", + proc { |score, move, user, target, skill, ai, battle| + if !user.recycleItem || user.item + score -= 80 + elsif user.recycleItem + score += 30 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RemoveTargetItem", + proc { |score, move, user, target, skill, ai, battle| + if ai.skill_check(Battle::AI::AILevel.high) + score += 20 if target.item + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("DestroyTargetBerryOrGem", + proc { |score, move, user, target, skill, ai, battle| + if target.effects[PBEffects::Substitute] == 0 + if ai.skill_check(Battle::AI::AILevel.high) && target.item && target.item.is_berry? + score += 30 + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("CorrodeTargetItem", + proc { |score, move, user, target, skill, ai, battle| + if battle.corrosiveGas[target.index % 2][target.pokemonIndex] + score -= 100 + elsif !target.item || !target.itemActive? || target.unlosableItem?(target.item) || + target.hasActiveAbility?(:STICKYHOLD) + score -= 90 + elsif target.effects[PBEffects::Substitute] > 0 + score -= 90 + else + score += 50 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("StartTargetCannotUseItem", + proc { |score, move, user, target, skill, ai, battle| + next 0 if target.effects[PBEffects::Embargo] > 0 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("StartNegateHeldItems", + proc { |score, move, user, target, skill, ai, battle| + next 0 if battle.field.effects[PBEffects::MagicRoom] > 0 + next score + 30 if !user.item && target.item + } +) + +Battle::AI::Handlers::MoveEffectScore.add("UserConsumeBerryRaiseDefense2", + proc { |score, move, user, target, skill, ai, battle| + if !user.item || !user.item.is_berry? || !user.itemActive? + score -= 100 + else + if ai.skill_check(Battle::AI::AILevel.high) + useful_berries = [ + :ORANBERRY, :SITRUSBERRY, :AGUAVBERRY, :APICOTBERRY, :CHERIBERRY, + :CHESTOBERRY, :FIGYBERRY, :GANLONBERRY, :IAPAPABERRY, :KEEBERRY, + :LANSATBERRY, :LEPPABERRY, :LIECHIBERRY, :LUMBERRY, :MAGOBERRY, + :MARANGABERRY, :PECHABERRY, :PERSIMBERRY, :PETAYABERRY, :RAWSTBERRY, + :SALACBERRY, :STARFBERRY, :WIKIBERRY + ] + score += 30 if useful_berries.include?(user.item_id) + end + if ai.skill_check(Battle::AI::AILevel.medium) + score += 20 if user.canHeal? && user.hp < user.totalhp / 3 && user.hasActiveAbility?(:CHEEKPOUCH) + score += 20 if user.hasActiveAbility?([:HARVEST, :RIPEN]) || + user.pbHasMoveFunction?("RestoreUserConsumedItem") # Recycle + score += 20 if !user.canConsumeBerry? + end + score -= user.stages[:DEFENSE] * 20 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("AllBattlersConsumeBerry", + proc { |score, move, user, target, skill, ai, battle| + useful_berries = [ + :ORANBERRY, :SITRUSBERRY, :AGUAVBERRY, :APICOTBERRY, :CHERIBERRY, + :CHESTOBERRY, :FIGYBERRY, :GANLONBERRY, :IAPAPABERRY, :KEEBERRY, + :LANSATBERRY, :LEPPABERRY, :LIECHIBERRY, :LUMBERRY, :MAGOBERRY, + :MARANGABERRY, :PECHABERRY, :PERSIMBERRY, :PETAYABERRY, + :RAWSTBERRY, :SALACBERRY, :STARFBERRY, :WIKIBERRY + ] + battle.allSameSideBattlers(user.index).each do |b| + if !b.item || !b.item.is_berry? || !b.itemActive? + score -= 100 / battle.pbSideSize(user.index) + else + if ai.skill_check(Battle::AI::AILevel.high) + amt = 30 / battle.pbSideSize(user.index) + score += amt if useful_berries.include?(b.item_id) + end + if ai.skill_check(Battle::AI::AILevel.medium) + amt = 20 / battle.pbSideSize(user.index) + score += amt if b.canHeal? && b.hp < b.totalhp / 3 && b.hasActiveAbility?(:CHEEKPOUCH) + score += amt if b.hasActiveAbility?([:HARVEST, :RIPEN]) || + b.pbHasMoveFunction?("RestoreUserConsumedItem") # Recycle + score += amt if !b.canConsumeBerry? + end + end + end + if ai.skill_check(Battle::AI::AILevel.high) + battle.allOtherSideBattlers(user.index).each do |b| + amt = 10 / battle.pbSideSize(target.index) + score -= amt if b.hasActiveItem?(useful_berries) + score -= amt if b.canHeal? && b.hp < b.totalhp / 3 && b.hasActiveAbility?(:CHEEKPOUCH) + score -= amt if b.hasActiveAbility?([:HARVEST, :RIPEN]) || + b.pbHasMoveFunction?("RestoreUserConsumedItem") # Recycle + score -= amt if !b.canConsumeBerry? + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("UserConsumeTargetBerry", + proc { |score, move, user, target, skill, ai, battle| + if target.effects[PBEffects::Substitute] == 0 + if ai.skill_check(Battle::AI::AILevel.high) && target.item && target.item.is_berry? + score += 30 + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("ThrowUserItemAtTarget", + proc { |score, move, user, target, skill, ai, battle| + next 0 if !user.item || !user.itemActive? || + user.unlosableItem?(user.item) || user.item.is_poke_ball? + } +) diff --git a/Data/Scripts/011_Battle/005_AI/058_AI_MoveHandlers_ChangeMoveEffect.rb b/Data/Scripts/011_Battle/005_AI/058_AI_MoveHandlers_ChangeMoveEffect.rb new file mode 100644 index 000000000..399b34488 --- /dev/null +++ b/Data/Scripts/011_Battle/005_AI/058_AI_MoveHandlers_ChangeMoveEffect.rb @@ -0,0 +1,271 @@ +#=============================================================================== +# +#=============================================================================== +Battle::AI::Handlers::MoveEffectScore.add("RedirectAllMovesToUser", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.allAllies.length == 0 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RedirectAllMovesToTarget", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.allAllies.length == 0 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("CannotBeRedirected", + proc { |score, move, user, target, skill, ai, battle| + redirection = false + user.allOpposing.each do |b| + next if b.index == target.index + if b.effects[PBEffects::RagePowder] || + b.effects[PBEffects::Spotlight] > 0 || + b.effects[PBEffects::FollowMe] > 0 || + (b.hasActiveAbility?(:LIGHTNINGROD) && move.pbCalcType == :ELECTRIC) || + (b.hasActiveAbility?(:STORMDRAIN) && move.pbCalcType == :WATER) + redirection = true + break + end + end + score += 50 if redirection && ai.skill_check(Battle::AI::AILevel.medium) + next score + } +) + +# RandomlyDamageOrHealTarget + +Battle::AI::Handlers::MoveEffectScore.add("HealAllyOrDamageFoe", + proc { |score, move, user, target, skill, ai, battle| + if !target.opposes?(user) + if target.hp == target.totalhp || (ai.skill_check(Battle::AI::AILevel.medium) && !target.canHeal?) + score -= 90 + else + score += 50 + score -= target.hp * 100 / target.totalhp + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("CurseTargetOrLowerUserSpd1RaiseUserAtkDef1", + proc { |score, move, user, target, skill, ai, battle| + if user.pbHasType?(:GHOST) + if target.effects[PBEffects::Curse] + score -= 90 + elsif user.hp <= user.totalhp / 2 + if battle.pbAbleNonActiveCount(user.idxOwnSide) == 0 + score -= 90 + else + score -= 50 + score -= 30 if battle.switchStyle + end + end + else + avg = user.stages[:SPEED] * 10 + avg -= user.stages[:ATTACK] * 10 + avg -= user.stages[:DEFENSE] * 10 + score += avg / 3 + end + next score + } +) + +# EffectDependsOnEnvironment + +Battle::AI::Handlers::MoveEffectScore.add("HitsAllFoesAndPowersUpInPsychicTerrain", + proc { |score, move, user, target, skill, ai, battle| + next score + 40 if battle.field.terrain == :Psychic && user.affectedByTerrain? + } +) + +Battle::AI::Handlers::MoveEffectScore.add("TargetNextFireMoveDamagesTarget", + proc { |score, move, user, target, skill, ai, battle| + aspeed = pbRoughStat(user, :SPEED) + ospeed = pbRoughStat(target, :SPEED) + if aspeed > ospeed + score -= 90 + elsif target.pbHasMoveType?(:FIRE) + score += 30 + end + next score + } +) + +# DoublePowerAfterFusionFlare + +# DoublePowerAfterFusionBolt + +Battle::AI::Handlers::MoveEffectScore.add("PowerUpAllyMove", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.allAllies.empty? + next score + 30 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("CounterPhysicalDamage", + proc { |score, move, user, target, skill, ai, battle| + if target.effects[PBEffects::HyperBeam] > 0 + score -= 90 + else + attack = pbRoughStat(user, :ATTACK) + spatk = pbRoughStat(user, :SPECIAL_ATTACK) + if attack * 1.5 < spatk + score -= 60 + elsif ai.skill_check(Battle::AI::AILevel.medium) && target.lastMoveUsed + moveData = GameData::Move.get(target.lastMoveUsed) + score += 60 if moveData.physical? + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("CounterSpecialDamage", + proc { |score, move, user, target, skill, ai, battle| + if target.effects[PBEffects::HyperBeam] > 0 + score -= 90 + else + attack = pbRoughStat(user, :ATTACK) + spatk = pbRoughStat(user, :SPECIAL_ATTACK) + if attack > spatk * 1.5 + score -= 60 + elsif ai.skill_check(Battle::AI::AILevel.medium) && target.lastMoveUsed + moveData = GameData::Move.get(target.lastMoveUsed) + score += 60 if moveData.special? + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("CounterDamagePlusHalf", + proc { |score, move, user, target, skill, ai, battle| + next score - 90 if target.effects[PBEffects::HyperBeam] > 0 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("UserAddStockpileRaiseDefSpDef1", + proc { |score, move, user, target, skill, ai, battle| + avg = 0 + avg -= user.stages[:DEFENSE] * 10 + avg -= user.stages[:SPECIAL_DEFENSE] * 10 + score += avg / 2 + if user.effects[PBEffects::Stockpile] >= 3 + score -= 80 + elsif user.pbHasMoveFunction?("PowerDependsOnUserStockpile", + "HealUserDependingOnUserStockpile") # Spit Up, Swallow + score += 20 # More preferable if user also has Spit Up/Swallow + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("PowerDependsOnUserStockpile", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.effects[PBEffects::Stockpile] == 0 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("HealUserDependingOnUserStockpile", + proc { |score, move, user, target, skill, ai, battle| + next 0 if user.effects[PBEffects::Stockpile] == 0 + next 0 if user.hp == user.totalhp + mult = [0, 25, 50, 100][user.effects[PBEffects::Stockpile]] + score += mult + score -= user.hp * mult * 2 / user.totalhp + next score + } +) + +# GrassPledge + +# FirePledge + +# WaterPledge + +# UseLastMoveUsed + +Battle::AI::Handlers::MoveEffectScore.add("UseLastMoveUsedByTarget", + proc { |score, move, user, target, skill, ai, battle| + score -= 40 + if ai.skill_check(Battle::AI::AILevel.high) + score -= 100 if !target.lastRegularMoveUsed || + GameData::Move.get(target.lastRegularMoveUsed).flags.none? { |f| f[/^CanMirrorMove$/i] } + end + next score + } +) + +# UseMoveTargetIsAboutToUse + +# UseMoveDependingOnEnvironment + +# UseRandomMove + +# UseRandomMoveFromUserParty + +Battle::AI::Handlers::MoveEffectScore.add("UseRandomUserMoveIfAsleep", + proc { |score, move, user, target, skill, ai, battle| + if user.asleep? + score += 100 # Because it can only be used while asleep + else + score -= 90 + end + next score + } +) + +# BounceBackProblemCausingStatusMoves + +# StealAndUseBeneficialStatusMove + +Battle::AI::Handlers::MoveEffectScore.add("ReplaceMoveThisBattleWithTargetLastMoveUsed", + proc { |score, move, user, target, skill, ai, battle| + moveBlacklist = [ + "Struggle", # Struggle + "ReplaceMoveThisBattleWithTargetLastMoveUsed", # Mimic + "ReplaceMoveWithTargetLastMoveUsed", # Sketch + "UseRandomMove" # Metronome + ] + if user.effects[PBEffects::Transform] || !target.lastRegularMoveUsed + score -= 90 + else + lastMoveData = GameData::Move.get(target.lastRegularMoveUsed) + if moveBlacklist.include?(lastMoveData.function_code) || + lastMoveData.type == :SHADOW + score -= 90 + end + user.eachMove do |m| + next if m != target.lastRegularMoveUsed + score -= 90 + break + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("ReplaceMoveWithTargetLastMoveUsed", + proc { |score, move, user, target, skill, ai, battle| + moveBlacklist = [ + "Struggle", # Struggle + "ReplaceMoveWithTargetLastMoveUsed" # Sketch + ] + if user.effects[PBEffects::Transform] || !target.lastRegularMoveUsed + score -= 90 + else + lastMoveData = GameData::Move.get(target.lastRegularMoveUsed) + if moveBlacklist.include?(lastMoveData.function_code) || + lastMoveData.type == :SHADOW + score -= 90 + end + user.eachMove do |m| + next if m != target.lastRegularMoveUsed + score -= 90 # User already knows the move that will be Sketched + break + end + end + next score + } +) diff --git a/Data/Scripts/011_Battle/005_AI/059_AI_MoveHandlers_SwitchingActing.rb b/Data/Scripts/011_Battle/005_AI/059_AI_MoveHandlers_SwitchingActing.rb new file mode 100644 index 000000000..499df268d --- /dev/null +++ b/Data/Scripts/011_Battle/005_AI/059_AI_MoveHandlers_SwitchingActing.rb @@ -0,0 +1,347 @@ +#=============================================================================== +# +#=============================================================================== +Battle::AI::Handlers::MoveEffectScore.add("FleeFromBattle", + proc { |score, move, user, target, skill, ai, battle| + next 0 if battle.trainerBattle? + } +) + +Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserStatusMove", + proc { |score, move, user, target, skill, ai, battle| + if !battle.pbCanChooseNonActive?(user.index) || + battle.pbTeamAbleNonActiveCount(user.index) > 1 # Don't switch in ace + score -= 100 + else + score += 40 if user.effects[PBEffects::Confusion] > 0 + total = 0 + GameData::Stat.each_battle { |s| total += user.stages[s.id] } + if total <= 0 || user.turnCount == 0 + score += 60 + else + score -= total * 10 + # special case: user has no damaging moves + hasDamagingMove = false + user.eachMove do |m| + next if !m.damagingMove? + hasDamagingMove = true + break + end + score += 75 if !hasDamagingMove + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserDamagingMove", + proc { |score, move, user, target, skill, ai, battle| + next 0 if !battle.pbCanChooseNonActive?(user.index) || + battle.pbTeamAbleNonActiveCount(user.index) > 1 # Don't switch in ace + } +) + +Battle::AI::Handlers::MoveEffectScore.add("LowerTargetAtkSpAtk1SwitchOutUser", + proc { |score, move, user, target, skill, ai, battle| + avg = target.stages[:ATTACK] * 10 + avg += target.stages[:SPECIAL_ATTACK] * 10 + score += avg / 2 + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserPassOnEffects", + proc { |score, move, user, target, skill, ai, battle| + if battle.pbCanChooseNonActive?(user.index) + score -= 40 if user.effects[PBEffects::Confusion] > 0 + total = 0 + GameData::Stat.each_battle { |s| total += user.stages[s.id] } + if total <= 0 || user.turnCount == 0 + score -= 60 + else + score += total * 10 + # special case: user has no damaging moves + hasDamagingMove = false + user.eachMove do |m| + next if !m.damagingMove? + hasDamagingMove = true + break + end + score += 75 if !hasDamagingMove + end + else + score -= 100 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("SwitchOutTargetStatusMove", + proc { |score, move, user, target, skill, ai, battle| + if target.effects[PBEffects::Ingrain] || + (ai.skill_check(Battle::AI::AILevel.high) && target.hasActiveAbility?(:SUCTIONCUPS)) + score -= 90 + else + ch = 0 + battle.pbParty(target.index).each_with_index do |pkmn, i| + ch += 1 if battle.pbCanSwitchLax?(target.index, i) + end + score -= 90 if ch == 0 + end + if score > 20 + score += 50 if target.pbOwnSide.effects[PBEffects::Spikes] > 0 + score += 50 if target.pbOwnSide.effects[PBEffects::ToxicSpikes] > 0 + score += 50 if target.pbOwnSide.effects[PBEffects::StealthRock] + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("SwitchOutTargetDamagingMove", + proc { |score, move, user, target, skill, ai, battle| + if !target.effects[PBEffects::Ingrain] && + !(ai.skill_check(Battle::AI::AILevel.high) && target.hasActiveAbility?(:SUCTIONCUPS)) + score += 40 if target.pbOwnSide.effects[PBEffects::Spikes] > 0 + score += 40 if target.pbOwnSide.effects[PBEffects::ToxicSpikes] > 0 + score += 40 if target.pbOwnSide.effects[PBEffects::StealthRock] + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("BindTarget", + proc { |score, move, user, target, skill, ai, battle| + next score + 40 if target.effects[PBEffects::Trapping] == 0 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("BindTargetDoublePowerIfTargetUnderwater", + proc { |score, move, user, target, skill, ai, battle| + next score + 40 if target.effects[PBEffects::Trapping] == 0 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("TrapTargetInBattle", + proc { |score, move, user, target, skill, ai, battle| + next 0 if target.effects[PBEffects::MeanLook] >= 0 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("TrapTargetInBattleLowerTargetDefSpDef1EachTurn", + proc { |score, move, user, target, skill, ai, battle| + next 0 if target.effects[PBEffects::Octolock] >= 0 + score += 30 if !target.trappedInBattle? + score -= 100 if !target.pbCanLowerStatStage?(:DEFENSE, user, move) && + !target.pbCanLowerStatStage?(:SPECIAL_DEFENSE, user, move) + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("TrapUserAndTargetInBattle", + proc { |score, move, user, target, skill, ai, battle| + if target.effects[PBEffects::JawLock] < 0 + score += 40 if !user.trappedInBattle? && !target.trappedInBattle? + end + next score + } +) + +# TrapAllBattlersInBattleForOneTurn + +# PursueSwitchingFoe + +Battle::AI::Handlers::MoveEffectScore.add("UsedAfterUserTakesPhysicalDamage", + proc { |score, move, user, target, skill, ai, battle| + if ai.skill_check(Battle::AI::AILevel.medium) + hasPhysicalAttack = false + target.eachMove do |m| + next if !m.physicalMove?(m.type) + hasPhysicalAttack = true + break + end + score -= 80 if !hasPhysicalAttack + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("UsedAfterAllyRoundWithDoublePower", + proc { |score, move, user, target, skill, ai, battle| + if ai.skill_check(Battle::AI::AILevel.medium) + user.allAllies.each do |b| + next if !b.pbHasMove?(move.id) + score += 20 + end + end + next score + } +) + +# TargetActsNext + +# TargetActsLast + +Battle::AI::Handlers::MoveEffectScore.add("TargetUsesItsLastUsedMoveAgain", + proc { |score, move, user, target, skill, ai, battle| + if ai.skill_check(Battle::AI::AILevel.medium) + if !target.lastRegularMoveUsed || + !target.pbHasMove?(target.lastRegularMoveUsed) || + target.usingMultiTurnAttack? + score -= 90 + else + # Without lots of code here to determine good/bad moves and relative + # speeds, using this move is likely to just be a waste of a turn + score -= 50 + end + end + next score + } +) + +# StartSlowerBattlersActFirst + +Battle::AI::Handlers::MoveEffectScore.add("HigherPriorityInGrassyTerrain", + proc { |score, move, user, target, skill, ai, battle| + if ai.skill_check(Battle::AI::AILevel.medium) && @battle.field.terrain == :Grassy + aspeed = pbRoughStat(user, :SPEED) + ospeed = pbRoughStat(target, :SPEED) + score += 40 if aspeed < ospeed + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("LowerPPOfTargetLastMoveBy3", + proc { |score, move, user, target, skill, ai, battle| + last_move = target.pbGetMoveWithID(target.lastRegularMoveUsed) + if last_move && last_move.total_pp > 0 && last_move.pp <= 3 + score += 50 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("LowerPPOfTargetLastMoveBy4", + proc { |score, move, user, target, skill, ai, battle| + next score - 40 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("DisableTargetLastMoveUsed", + proc { |score, move, user, target, skill, ai, battle| + next 0 if target.effects[PBEffects::Disable] > 0 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("DisableTargetUsingSameMoveConsecutively", + proc { |score, move, user, target, skill, ai, battle| + next 0 if target.effects[PBEffects::Torment] + } +) + +Battle::AI::Handlers::MoveEffectScore.add("DisableTargetUsingDifferentMove", + proc { |score, move, user, target, skill, ai, battle| + aspeed = pbRoughStat(user, :SPEED) + ospeed = pbRoughStat(target, :SPEED) + if target.effects[PBEffects::Encore] > 0 + score -= 90 + elsif aspeed > ospeed + if target.lastRegularMoveUsed + moveData = GameData::Move.get(target.lastRegularMoveUsed) + if moveData.category == 2 && # Status move + [:User, :BothSides].include?(moveData.target) + score += 60 + elsif moveData.category != 2 && # Damaging move + moveData.target == :NearOther && + Effectiveness.ineffective?(pbCalcTypeMod(moveData.type, target, user)) + score += 60 + end + else + score -= 90 + end + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("DisableTargetStatusMoves", + proc { |score, move, user, target, skill, ai, battle| + next 0 if target.effects[PBEffects::Taunt] > 0 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("DisableTargetHealingMoves", + proc { |score, move, user, target, skill, ai, battle| + next 0 if target.effects[PBEffects::HealBlock] > 0 + } +) + +Battle::AI::Handlers::MoveEffectScore.add("DisableTargetSoundMoves", + proc { |score, move, user, target, skill, ai, battle| + if target.effects[PBEffects::ThroatChop] == 0 && ai.skill_check(Battle::AI::AILevel.high) + hasSoundMove = false + user.eachMove do |m| + next if !m.soundMove? + hasSoundMove = true + break + end + score += 40 if hasSoundMove + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("DisableTargetMovesKnownByUser", + proc { |score, move, user, target, skill, ai, battle| + next 0 if target.effects[PBEffects::Imprison] + } +) + +Battle::AI::Handlers::MoveEffectScore.add("AllBattlersLoseHalfHPUserSkipsNextTurn", + proc { |score, move, user, target, skill, ai, battle| + score += 20 # Shadow moves are more preferable + score += 20 if target.hp >= target.totalhp / 2 + score -= 20 if user.hp < user.hp / 2 + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("AllBattlersLoseHalfHPUserSkipsNextTurn", + proc { |score, move, user, target, skill, ai, battle| + score += 20 # Shadow moves are more preferable + score -= 40 + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("StartShadowSkyWeather", + proc { |score, move, user, target, skill, ai, battle| + score += 20 # Shadow moves are more preferable + if battle.pbCheckGlobalAbility(:AIRLOCK) || + battle.pbCheckGlobalAbility(:CLOUDNINE) + score -= 90 + elsif battle.field.weather == :ShadowSky + score -= 90 + end + next score + } +) + +Battle::AI::Handlers::MoveEffectScore.add("RemoveAllScreens", + proc { |score, move, user, target, skill, ai, battle| + score += 20 # Shadow moves are more preferable + if target.pbOwnSide.effects[PBEffects::AuroraVeil] > 0 || + target.pbOwnSide.effects[PBEffects::Reflect] > 0 || + target.pbOwnSide.effects[PBEffects::LightScreen] > 0 || + target.pbOwnSide.effects[PBEffects::Safeguard] > 0 + score += 30 + score -= 90 if user.pbOwnSide.effects[PBEffects::AuroraVeil] > 0 || + user.pbOwnSide.effects[PBEffects::Reflect] > 0 || + user.pbOwnSide.effects[PBEffects::LightScreen] > 0 || + user.pbOwnSide.effects[PBEffects::Safeguard] > 0 + else + next 0 + end + next score + } +) diff --git a/Data/Scripts/011_Battle/007_Other battle types/003_BattlePalaceBattle.rb b/Data/Scripts/011_Battle/007_Other battle types/003_BattlePalaceBattle.rb index ce78e38b5..21f1e4fca 100644 --- a/Data/Scripts/011_Battle/007_Other battle types/003_BattlePalaceBattle.rb +++ b/Data/Scripts/011_Battle/007_Other battle types/003_BattlePalaceBattle.rb @@ -183,9 +183,10 @@ class Battle::AI alias _battlePalace_pbEnemyShouldWithdraw? pbEnemyShouldWithdraw? end - def pbEnemyShouldWithdraw?(idxBattler) - return _battlePalace_pbEnemyShouldWithdraw?(idxBattler) if !@battlePalace - thispkmn = @battle.battlers[idxBattler] + def pbEnemyShouldWithdraw? + return _battlePalace_pbEnemyShouldWithdraw? if !@battlePalace + thispkmn = @user + idxBattler = @user.index shouldswitch = false if thispkmn.effects[PBEffects::PerishSong] == 1 shouldswitch = true diff --git a/Data/Scripts/011_Battle/007_Other battle types/004_BattleArenaBattle.rb b/Data/Scripts/011_Battle/007_Other battle types/004_BattleArenaBattle.rb index 6334b98e3..1bdaaf94c 100644 --- a/Data/Scripts/011_Battle/007_Other battle types/004_BattleArenaBattle.rb +++ b/Data/Scripts/011_Battle/007_Other battle types/004_BattleArenaBattle.rb @@ -215,8 +215,8 @@ class Battle::AI alias _battleArena_pbEnemyShouldWithdraw? pbEnemyShouldWithdraw? end - def pbEnemyShouldWithdraw?(idxBattler) - return _battleArena_pbEnemyShouldWithdraw?(idxBattler) if !@battleArena + def pbEnemyShouldWithdraw? + return _battleArena_pbEnemyShouldWithdraw? if !@battleArena return false end end diff --git a/Data/Scripts/015_Trainers and player/001_Trainer.rb b/Data/Scripts/015_Trainers and player/001_Trainer.rb index f49ceb0dd..2ee52fbd4 100644 --- a/Data/Scripts/015_Trainers and player/001_Trainer.rb +++ b/Data/Scripts/015_Trainers and player/001_Trainer.rb @@ -19,6 +19,10 @@ class Trainer return _INTL("{1} {2}", trainer_type_name, @name) end + def skill_level + return GameData::TrainerType.try_get(:trainer_type)&.skill_level || 0 + end + #============================================================================= # Portion of the ID which is visible on the Trainer Card