mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-08 13:44:59 +00:00
Reapplied various AI changes from previous atempt at improving the AI, split function code move score changes into handlers
This commit is contained in:
@@ -91,94 +91,76 @@ class NamedEvent
|
|||||||
end
|
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
|
class HandlerHash
|
||||||
def initialize(mod)
|
def initialize
|
||||||
@mod = mod
|
@hash = {}
|
||||||
@hash = {}
|
@add_ifs = []
|
||||||
@addIfs = []
|
|
||||||
@symbolCache = {}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def fromSymbol(sym)
|
def [](id)
|
||||||
return sym unless sym.is_a?(Symbol) || sym.is_a?(String)
|
return @hash[id] if id && @hash[id]
|
||||||
mod = Object.const_get(@mod) rescue nil
|
@add_ifs.each do |add_if|
|
||||||
return nil if !mod
|
return add_if[2] if add_if[1].call(id)
|
||||||
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
|
end
|
||||||
return ret
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def addIf(conditionProc, handler = nil, &handlerBlock)
|
def add(id, handler = nil, &handlerBlock)
|
||||||
if ![Proc, Hash].include?(handler.class) && !block_given?
|
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
|
end
|
||||||
@addIfs.push([conditionProc, handler || handlerBlock])
|
@hash[id] = handler || handlerBlock if id && !id.empty?
|
||||||
end
|
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?
|
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
|
end
|
||||||
id = fromSymbol(sym)
|
@add_ifs.push([id, conditionProc, handler || handlerBlock])
|
||||||
@hash[id] = handler || handlerBlock if id
|
|
||||||
symbol = toSymbol(sym)
|
|
||||||
@hash[symbol] = handler || handlerBlock if symbol
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def copy(src, *dests)
|
def copy(src, *dests)
|
||||||
handler = self[src]
|
handler = self[src]
|
||||||
if handler
|
return if !handler
|
||||||
dests.each do |dest|
|
dests.each { |dest| add(dest, handler) }
|
||||||
self.add(dest, handler)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def [](sym) # 'sym' can be an ID or symbol
|
def remove(key)
|
||||||
id = fromSymbol(sym)
|
if @hash.keys.include?(key)
|
||||||
ret = nil
|
@hash.delete(key)
|
||||||
ret = @hash[id] if id && @hash[id] # Real ID from the item
|
else
|
||||||
symbol = toSymbol(sym)
|
@add_ifs.delete_if { |add_if| add_if[0] == key }
|
||||||
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
|
|
||||||
end
|
end
|
||||||
return ret
|
|
||||||
end
|
|
||||||
|
|
||||||
def trigger(sym, *args)
|
|
||||||
handler = self[sym]
|
|
||||||
return (handler) ? handler.call(fromSymbol(sym), *args) : nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def clear
|
def clear
|
||||||
@hash.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
|
||||||
end
|
end
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# A stripped-down version of class HandlerHash which only deals with symbols and
|
# A stripped-down version of class HandlerHash which only deals with IDs that
|
||||||
# doesn't care about whether those symbols are defined as constants in a class
|
# are symbols.
|
||||||
# or module.
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
class HandlerHash2
|
class HandlerHashSymbol
|
||||||
def initialize
|
def initialize
|
||||||
@hash = {}
|
@hash = {}
|
||||||
@add_ifs = []
|
@add_ifs = []
|
||||||
@@ -219,6 +201,7 @@ class HandlerHash2
|
|||||||
|
|
||||||
def clear
|
def clear
|
||||||
@hash.clear
|
@hash.clear
|
||||||
|
@add_ifs.clear
|
||||||
end
|
end
|
||||||
|
|
||||||
def trigger(sym, *args)
|
def trigger(sym, *args)
|
||||||
@@ -229,32 +212,63 @@ class HandlerHash2
|
|||||||
end
|
end
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# An even more stripped down version of class HandlerHash which just takes
|
# A specialised version of class HandlerHash which only deals with IDs that are
|
||||||
# hashes with keys, no matter what the keys 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
|
class HandlerHashEnum
|
||||||
def initialize
|
def initialize(mod)
|
||||||
@hash = {}
|
@mod = mod
|
||||||
@addIfs = []
|
@hash = {}
|
||||||
|
@addIfs = []
|
||||||
|
@symbolCache = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
def [](entry)
|
def [](sym) # 'sym' can be an ID or symbol
|
||||||
|
id = fromSymbol(sym)
|
||||||
ret = nil
|
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
|
unless ret
|
||||||
@addIfs.each do |addif|
|
@addIfs.each do |addif|
|
||||||
return addif[1] if addif[0].call(entry)
|
return addif[1] if addif[0].call(id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
def add(entry, handler = nil, &handlerBlock)
|
def fromSymbol(sym)
|
||||||
if ![Proc, Hash].include?(handler.class) && !block_given?
|
return sym unless sym.is_a?(Symbol) || sym.is_a?(String)
|
||||||
raise ArgumentError, "#{self.class.name} for #{entry.inspect} has no valid handler (#{handler.inspect} was given)"
|
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
|
end
|
||||||
return if !entry || entry.empty?
|
return ret
|
||||||
@hash[entry] = handler || handlerBlock
|
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
|
end
|
||||||
|
|
||||||
def addIf(conditionProc, handler = nil, &handlerBlock)
|
def addIf(conditionProc, handler = nil, &handlerBlock)
|
||||||
@@ -267,42 +281,31 @@ class HandlerHashBasic
|
|||||||
def copy(src, *dests)
|
def copy(src, *dests)
|
||||||
handler = self[src]
|
handler = self[src]
|
||||||
return if !handler
|
return if !handler
|
||||||
dests.each { |dest| add(dest, handler) }
|
dests.each { |dest| self.add(dest, handler) }
|
||||||
end
|
|
||||||
|
|
||||||
def remove(key)
|
|
||||||
@hash.delete(key)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def clear
|
def clear
|
||||||
@hash.clear
|
@hash.clear
|
||||||
|
@addIfs.clear
|
||||||
end
|
end
|
||||||
|
|
||||||
def each
|
def trigger(sym, *args)
|
||||||
@hash.each_pair { |key, value| yield key, value }
|
handler = self[sym]
|
||||||
end
|
return (handler) ? handler.call(fromSymbol(sym), *args) : nil
|
||||||
|
|
||||||
def keys
|
|
||||||
return @hash.keys.clone
|
|
||||||
end
|
|
||||||
|
|
||||||
def trigger(entry, *args)
|
|
||||||
handler = self[entry]
|
|
||||||
return handler&.call(*args)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
#
|
#
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
class SpeciesHandlerHash < HandlerHash2
|
class SpeciesHandlerHash < HandlerHashSymbol
|
||||||
end
|
end
|
||||||
|
|
||||||
class AbilityHandlerHash < HandlerHash2
|
class AbilityHandlerHash < HandlerHashSymbol
|
||||||
end
|
end
|
||||||
|
|
||||||
class ItemHandlerHash < HandlerHash2
|
class ItemHandlerHash < HandlerHashSymbol
|
||||||
end
|
end
|
||||||
|
|
||||||
class MoveHandlerHash < HandlerHash2
|
class MoveHandlerHash < HandlerHashSymbol
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ module MenuHandlers
|
|||||||
@@handlers = {}
|
@@handlers = {}
|
||||||
|
|
||||||
def self.add(menu, option, hash)
|
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)
|
@@handlers[menu].add(option, hash)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
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)
|
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
|
end
|
||||||
|
|
||||||
def pbAIRandom(x); return rand(x); end
|
def pbAIRandom(x); return rand(x); end
|
||||||
@@ -44,26 +53,66 @@ class Battle::AI
|
|||||||
return Math.sqrt(varianceTimesN / n)
|
return Math.sqrt(varianceTimesN / n)
|
||||||
end
|
end
|
||||||
|
|
||||||
#=============================================================================
|
# Decide whether the opponent should Mega Evolve their Pokémon.
|
||||||
# Decide whether the opponent should Mega Evolve their Pokémon
|
def pbEnemyShouldMegaEvolve?
|
||||||
#=============================================================================
|
if @battle.pbCanMegaEvolve?(@user.index) # Simple "always should if possible"
|
||||||
def pbEnemyShouldMegaEvolve?(idxBattler)
|
PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) will Mega Evolve")
|
||||||
battler = @battle.battlers[idxBattler]
|
|
||||||
if @battle.pbCanMegaEvolve?(idxBattler) # Simple "always should if possible"
|
|
||||||
PBDebug.log("[AI] #{battler.pbThis} (#{idxBattler}) will Mega Evolve")
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
#=============================================================================
|
# Choose an action.
|
||||||
# Choose an action
|
|
||||||
#=============================================================================
|
|
||||||
def pbDefaultChooseEnemyCommand(idxBattler)
|
def pbDefaultChooseEnemyCommand(idxBattler)
|
||||||
return if pbEnemyShouldUseItem?(idxBattler)
|
set_up(idxBattler)
|
||||||
return if pbEnemyShouldWithdraw?(idxBattler)
|
choices = pbGetMoveScores
|
||||||
|
return if pbEnemyShouldUseItem?
|
||||||
|
return if pbEnemyShouldWithdraw?
|
||||||
return if @battle.pbAutoFightMenu(idxBattler)
|
return if @battle.pbAutoFightMenu(idxBattler)
|
||||||
@battle.pbRegisterMegaEvolution(idxBattler) if pbEnemyShouldMegaEvolve?(idxBattler)
|
@battle.pbRegisterMegaEvolution(idxBattler) if pbEnemyShouldMegaEvolve?
|
||||||
pbChooseMoves(idxBattler)
|
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
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ class Battle::AI
|
|||||||
#=============================================================================
|
#=============================================================================
|
||||||
# Decide whether the opponent should use an item on the Pokémon
|
# Decide whether the opponent should use an item on the Pokémon
|
||||||
#=============================================================================
|
#=============================================================================
|
||||||
def pbEnemyShouldUseItem?(idxBattler)
|
def pbEnemyShouldUseItem?
|
||||||
user = @battle.battlers[idxBattler]
|
item, idxTarget = pbEnemyItemToUse
|
||||||
item, idxTarget = pbEnemyItemToUse(idxBattler)
|
|
||||||
return false if !item
|
return false if !item
|
||||||
# Determine target of item (always the Pokémon choosing the action)
|
# Determine target of item (always the Pokémon choosing the action)
|
||||||
useType = GameData::Item.get(item).battle_use
|
useType = GameData::Item.get(item).battle_use
|
||||||
@@ -12,19 +11,19 @@ class Battle::AI
|
|||||||
idxTarget = @battle.battlers[idxTarget].pokemonIndex # Party Pokémon
|
idxTarget = @battle.battlers[idxTarget].pokemonIndex # Party Pokémon
|
||||||
end
|
end
|
||||||
# Register use of item
|
# Register use of item
|
||||||
@battle.pbRegisterItem(idxBattler, item, idxTarget)
|
@battle.pbRegisterItem(@user.index, item, idxTarget)
|
||||||
PBDebug.log("[AI] #{user.pbThis} (#{user.index}) will use item #{GameData::Item.get(item).name}")
|
PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) will use item #{GameData::Item.get(item).name}")
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
# NOTE: The AI will only consider using an item on the Pokémon it's currently
|
# NOTE: The AI will only consider using an item on the Pokémon it's currently
|
||||||
# choosing an action for.
|
# choosing an action for.
|
||||||
def pbEnemyItemToUse(idxBattler)
|
def pbEnemyItemToUse
|
||||||
return nil if !@battle.internalBattle
|
return nil if !@battle.internalBattle
|
||||||
items = @battle.pbGetOwnerItems(idxBattler)
|
items = @battle.pbGetOwnerItems(@user.index)
|
||||||
return nil if !items || items.length == 0
|
return nil if !items || items.length == 0
|
||||||
# Determine target of item (always the Pokémon choosing the action)
|
# 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]
|
battler = @battle.battlers[idxTarget]
|
||||||
pkmn = battler.pokemon
|
pkmn = battler.pokemon
|
||||||
# Item categories
|
# Item categories
|
||||||
|
|||||||
@@ -2,26 +2,24 @@ class Battle::AI
|
|||||||
#=============================================================================
|
#=============================================================================
|
||||||
# Decide whether the opponent should switch Pokémon
|
# Decide whether the opponent should switch Pokémon
|
||||||
#=============================================================================
|
#=============================================================================
|
||||||
def pbEnemyShouldWithdraw?(idxBattler)
|
def pbEnemyShouldWithdraw?
|
||||||
return pbEnemyShouldWithdrawEx?(idxBattler, false)
|
return pbEnemyShouldWithdrawEx?(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pbEnemyShouldWithdrawEx?(idxBattler, forceSwitch)
|
def pbEnemyShouldWithdrawEx?(forceSwitch)
|
||||||
return false if @battle.wildBattle?
|
return false if @wildBattler
|
||||||
shouldSwitch = forceSwitch
|
shouldSwitch = forceSwitch
|
||||||
batonPass = -1
|
batonPass = -1
|
||||||
moveType = nil
|
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
|
# If Pokémon is within 6 levels of the foe, and foe's last move was
|
||||||
# super-effective and powerful
|
# super-effective and powerful
|
||||||
if !shouldSwitch && battler.turnCount > 0 && skill >= PBTrainerAI.highSkill
|
if !shouldSwitch && @user.turnCount > 0 && skill_check(AILevel.high)
|
||||||
target = battler.pbDirectOpposing(true)
|
target = @user.pbDirectOpposing(true)
|
||||||
if !target.fainted? && target.lastMoveUsed &&
|
if !target.fainted? && target.lastMoveUsed &&
|
||||||
(target.level - battler.level).abs <= 6
|
(target.level - @user.level).abs <= 6
|
||||||
moveData = GameData::Move.get(target.lastMoveUsed)
|
moveData = GameData::Move.get(target.lastMoveUsed)
|
||||||
moveType = moveData.type
|
moveType = moveData.type
|
||||||
typeMod = pbCalcTypeMod(moveType, target, battler)
|
typeMod = pbCalcTypeMod(moveType, target, @user)
|
||||||
if Effectiveness.super_effective?(typeMod) && moveData.base_damage > 50
|
if Effectiveness.super_effective?(typeMod) && moveData.base_damage > 50
|
||||||
switchChance = (moveData.base_damage > 70) ? 30 : 20
|
switchChance = (moveData.base_damage > 70) ? 30 : 20
|
||||||
shouldSwitch = (pbAIRandom(100) < switchChance)
|
shouldSwitch = (pbAIRandom(100) < switchChance)
|
||||||
@@ -29,77 +27,76 @@ class Battle::AI
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
# Pokémon can't do anything (must have been in battle for at least 5 rounds)
|
# Pokémon can't do anything (must have been in battle for at least 5 rounds)
|
||||||
if !@battle.pbCanChooseAnyMove?(idxBattler) &&
|
if !@battle.pbCanChooseAnyMove?(@user.index) &&
|
||||||
battler.turnCount && battler.turnCount >= 5
|
@user.turnCount && @user.turnCount >= 5
|
||||||
shouldSwitch = true
|
shouldSwitch = true
|
||||||
end
|
end
|
||||||
# Pokémon is Perish Songed and has Baton Pass
|
# Pokémon is Perish Songed and has Baton Pass
|
||||||
if skill >= PBTrainerAI.highSkill && battler.effects[PBEffects::PerishSong] == 1
|
if skill_check(AILevel.high) && @user.effects[PBEffects::PerishSong] == 1
|
||||||
battler.eachMoveWithIndex do |m, i|
|
@user.eachMoveWithIndex do |m, i|
|
||||||
next if m.function != "SwitchOutUserPassOnEffects" # Baton Pass
|
next if m.function != "SwitchOutUserPassOnEffects" # Baton Pass
|
||||||
next if !@battle.pbCanChooseMove?(idxBattler, i, false)
|
next if !@battle.pbCanChooseMove?(@user.index, i, false)
|
||||||
batonPass = i
|
batonPass = i
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
# Pokémon will faint because of bad poisoning at the end of this round, but
|
# 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
|
# would survive at least one more round if it were regular poisoning instead
|
||||||
if battler.status == :POISON && battler.statusCount > 0 &&
|
if @user.status == :POISON && @user.statusCount > 0 && skill_check(AILevel.high)
|
||||||
skill >= PBTrainerAI.highSkill
|
toxicHP = @user.totalhp / 16
|
||||||
toxicHP = battler.totalhp / 16
|
nextToxicHP = toxicHP * (@user.effects[PBEffects::Toxic] + 1)
|
||||||
nextToxicHP = toxicHP * (battler.effects[PBEffects::Toxic] + 1)
|
if @user.hp <= nextToxicHP && @user.hp > toxicHP * 2
|
||||||
if battler.hp <= nextToxicHP && battler.hp > toxicHP * 2 && pbAIRandom(100) < 80
|
shouldSwitch = true if pbAIRandom(100) < 80
|
||||||
shouldSwitch = true
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
# Pokémon is Encored into an unfavourable move
|
# Pokémon is Encored into an unfavourable move
|
||||||
if battler.effects[PBEffects::Encore] > 0 && skill >= PBTrainerAI.mediumSkill
|
if @user.effects[PBEffects::Encore] > 0 && skill_check(AILevel.medium)
|
||||||
idxEncoredMove = battler.pbEncoredMoveIndex
|
idxEncoredMove = @user.pbEncoredMoveIndex
|
||||||
if idxEncoredMove >= 0
|
if idxEncoredMove >= 0
|
||||||
scoreSum = 0
|
scoreSum = 0
|
||||||
scoreCount = 0
|
scoreCount = 0
|
||||||
battler.allOpposing.each do |b|
|
@user.allOpposing.each do |b|
|
||||||
scoreSum += pbGetMoveScore(battler.moves[idxEncoredMove], battler, b, skill)
|
scoreSum += pbGetMoveScore(@user.moves[idxEncoredMove], b)
|
||||||
scoreCount += 1
|
scoreCount += 1
|
||||||
end
|
end
|
||||||
if scoreCount > 0 && scoreSum / scoreCount <= 20 && pbAIRandom(100) < 80
|
if scoreCount > 0 && scoreSum / scoreCount <= 20
|
||||||
shouldSwitch = true
|
shouldSwitch = true if pbAIRandom(100) < 80
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
# If there is a single foe and it is resting after Hyper Beam or is
|
# If there is a single foe and it is resting after Hyper Beam or is
|
||||||
# Truanting (i.e. free turn)
|
# Truanting (i.e. free turn)
|
||||||
if @battle.pbSideSize(battler.index + 1) == 1 &&
|
if @battle.pbSideSize(@user.index + 1) == 1 &&
|
||||||
!battler.pbDirectOpposing.fainted? && skill >= PBTrainerAI.highSkill
|
!@user.pbDirectOpposing.fainted? && skill_check(AILevel.high)
|
||||||
opp = battler.pbDirectOpposing
|
opp = @user.pbDirectOpposing
|
||||||
if (opp.effects[PBEffects::HyperBeam] > 0 ||
|
if (opp.effects[PBEffects::HyperBeam] > 0 ||
|
||||||
(opp.hasActiveAbility?(:TRUANT) && opp.effects[PBEffects::Truant])) && pbAIRandom(100) < 80
|
(opp.hasActiveAbility?(:TRUANT) && opp.effects[PBEffects::Truant]))
|
||||||
shouldSwitch = false
|
shouldSwitch = false if pbAIRandom(100) < 80
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
# Sudden Death rule - I'm not sure what this means
|
# Sudden Death rule - I'm not sure what this means
|
||||||
if @battle.rules["suddendeath"] && battler.turnCount > 0
|
if @battle.rules["suddendeath"] && @user.turnCount > 0
|
||||||
if battler.hp <= battler.totalhp / 4 && pbAIRandom(100) < 30
|
if @user.hp <= @user.totalhp / 4 && pbAIRandom(100) < 30
|
||||||
shouldSwitch = true
|
shouldSwitch = true
|
||||||
elsif battler.hp <= battler.totalhp / 2 && pbAIRandom(100) < 80
|
elsif @user.hp <= @user.totalhp / 2 && pbAIRandom(100) < 80
|
||||||
shouldSwitch = true
|
shouldSwitch = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
# Pokémon is about to faint because of Perish Song
|
# Pokémon is about to faint because of Perish Song
|
||||||
if battler.effects[PBEffects::PerishSong] == 1
|
if @user.effects[PBEffects::PerishSong] == 1
|
||||||
shouldSwitch = true
|
shouldSwitch = true
|
||||||
end
|
end
|
||||||
if shouldSwitch
|
if shouldSwitch
|
||||||
list = []
|
list = []
|
||||||
idxPartyStart, idxPartyEnd = @battle.pbTeamIndexRangeFromBattlerIndex(idxBattler)
|
idxPartyStart, idxPartyEnd = @battle.pbTeamIndexRangeFromBattlerIndex(@user.index)
|
||||||
@battle.pbParty(idxBattler).each_with_index do |pkmn, i|
|
@battle.pbParty(@user.index).each_with_index do |pkmn, i|
|
||||||
next if i == idxPartyEnd - 1 # Don't choose to switch in ace
|
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
|
# If perish count is 1, it may be worth it to switch
|
||||||
# even with Spikes, since Perish Song's effect will end
|
# 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
|
# 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
|
# Don't switch to this if too little HP
|
||||||
if spikes > 0
|
if spikes > 0
|
||||||
spikesDmg = [8, 6, 4][spikes - 1]
|
spikesDmg = [8, 6, 4][spikes - 1]
|
||||||
@@ -108,17 +105,17 @@ class Battle::AI
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
# moveType is the type of the target's last used move
|
# 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
|
weight = 65
|
||||||
typeMod = pbCalcTypeModPokemon(pkmn, battler.pbDirectOpposing(true))
|
typeMod = pbCalcTypeModPokemon(pkmn, @user.pbDirectOpposing(true))
|
||||||
if Effectiveness.super_effective?(typeMod)
|
if Effectiveness.super_effective?(typeMod)
|
||||||
# Greater weight if new Pokemon's type is effective against target
|
# Greater weight if new Pokemon's type is effective against target
|
||||||
weight = 85
|
weight = 85
|
||||||
end
|
end
|
||||||
list.unshift(i) if pbAIRandom(100) < weight # Put this Pokemon first
|
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
|
weight = 40
|
||||||
typeMod = pbCalcTypeModPokemon(pkmn, battler.pbDirectOpposing(true))
|
typeMod = pbCalcTypeModPokemon(pkmn, @user.pbDirectOpposing(true))
|
||||||
if Effectiveness.super_effective?(typeMod)
|
if Effectiveness.super_effective?(typeMod)
|
||||||
# Greater weight if new Pokemon's type is effective against target
|
# Greater weight if new Pokemon's type is effective against target
|
||||||
weight = 60
|
weight = 60
|
||||||
@@ -129,13 +126,13 @@ class Battle::AI
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
if list.length > 0
|
if list.length > 0
|
||||||
if batonPass >= 0 && @battle.pbRegisterMove(idxBattler, batonPass, false)
|
if batonPass >= 0 && @battle.pbRegisterMove(@user.index, batonPass, false)
|
||||||
PBDebug.log("[AI] #{battler.pbThis} (#{idxBattler}) will use Baton Pass to avoid Perish Song")
|
PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) will use Baton Pass to avoid Perish Song")
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
if @battle.pbRegisterSwitch(idxBattler, list[0])
|
if @battle.pbRegisterSwitch(@user.index, list[0])
|
||||||
PBDebug.log("[AI] #{battler.pbThis} (#{idxBattler}) will switch with " +
|
PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) will switch with " +
|
||||||
@battle.pbParty(idxBattler)[list[0]].name)
|
@battle.pbParty(@user.index)[list[0]].name)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -143,10 +140,10 @@ class Battle::AI
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
#=============================================================================
|
# Choose a replacement Pokémon (called directly from @battle, not part of
|
||||||
# Choose a replacement Pokémon
|
# action choosing).
|
||||||
#=============================================================================
|
|
||||||
def pbDefaultChooseNewEnemy(idxBattler, party)
|
def pbDefaultChooseNewEnemy(idxBattler, party)
|
||||||
|
set_up(idxBattler)
|
||||||
enemies = []
|
enemies = []
|
||||||
idxPartyStart, idxPartyEnd = @battle.pbTeamIndexRangeFromBattlerIndex(idxBattler)
|
idxPartyStart, idxPartyEnd = @battle.pbTeamIndexRangeFromBattlerIndex(idxBattler)
|
||||||
party.each_with_index do |_p, i|
|
party.each_with_index do |_p, i|
|
||||||
|
|||||||
@@ -3,25 +3,7 @@ class Battle::AI
|
|||||||
# Main move-choosing method (moves with higher scores are more likely to be
|
# Main move-choosing method (moves with higher scores are more likely to be
|
||||||
# chosen)
|
# chosen)
|
||||||
#=============================================================================
|
#=============================================================================
|
||||||
def pbChooseMoves(idxBattler)
|
def pbChooseMove(choices)
|
||||||
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
|
|
||||||
# Figure out useful information about the choices
|
# Figure out useful information about the choices
|
||||||
totalScore = 0
|
totalScore = 0
|
||||||
maxScore = 0
|
maxScore = 0
|
||||||
@@ -29,18 +11,9 @@ class Battle::AI
|
|||||||
totalScore += c[1]
|
totalScore += c[1]
|
||||||
maxScore = c[1] if maxScore < c[1]
|
maxScore = c[1] if maxScore < c[1]
|
||||||
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
|
|
||||||
# Find any preferred moves and just choose from them
|
# 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)
|
stDev = pbStdDev(choices)
|
||||||
if stDev >= 40 && pbAIRandom(100) < 90
|
if stDev >= 40 && pbAIRandom(100) < 90
|
||||||
preferredMoves = []
|
preferredMoves = []
|
||||||
@@ -51,97 +24,141 @@ class Battle::AI
|
|||||||
end
|
end
|
||||||
if preferredMoves.length > 0
|
if preferredMoves.length > 0
|
||||||
m = preferredMoves[pbAIRandom(preferredMoves.length)]
|
m = preferredMoves[pbAIRandom(preferredMoves.length)]
|
||||||
PBDebug.log("[AI] #{user.pbThis} (#{user.index}) prefers #{user.moves[m[0]].name}")
|
PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) prefers #{@user.moves[m[0]].name}")
|
||||||
@battle.pbRegisterMove(idxBattler, m[0], false)
|
@battle.pbRegisterMove(@user.index, m[0], false)
|
||||||
@battle.pbRegisterTarget(idxBattler, m[2]) if m[2] >= 0
|
@battle.pbRegisterTarget(@user.index, m[2]) if m[2] >= 0
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Decide whether all choices are bad, and if so, try switching instead
|
# 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
|
badMoves = false
|
||||||
if ((maxScore <= 20 && user.turnCount > 2) ||
|
if (maxScore <= 20 && @user.turnCount > 2) ||
|
||||||
(maxScore <= 40 && user.turnCount > 5)) && pbAIRandom(100) < 80
|
(maxScore <= 40 && @user.turnCount > 5)
|
||||||
badMoves = true
|
badMoves = true if pbAIRandom(100) < 80
|
||||||
end
|
end
|
||||||
if !badMoves && totalScore < 100 && user.turnCount > 1
|
if !badMoves && totalScore < 100 && @user.turnCount > 1
|
||||||
badMoves = true
|
badMoves = true
|
||||||
choices.each do |c|
|
choices.each do |c|
|
||||||
next if !user.moves[c[0]].damagingMove?
|
next if !@user.moves[c[0]].damagingMove?
|
||||||
badMoves = false
|
badMoves = false
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
badMoves = false if badMoves && pbAIRandom(100) < 10
|
badMoves = false if badMoves && pbAIRandom(100) < 10
|
||||||
end
|
end
|
||||||
if badMoves && pbEnemyShouldWithdrawEx?(idxBattler, true)
|
if badMoves && pbEnemyShouldWithdrawEx?(true)
|
||||||
if $INTERNAL
|
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
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# If there are no calculated choices, pick one at random
|
# If there are no calculated choices, pick one at random
|
||||||
if choices.length == 0
|
if choices.length == 0
|
||||||
PBDebug.log("[AI] #{user.pbThis} (#{user.index}) doesn't want to use any moves; picking one at random")
|
PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) doesn't want to use any moves; picking one at random")
|
||||||
user.eachMoveWithIndex do |_m, i|
|
@user.eachMoveWithIndex do |_m, i|
|
||||||
next if !@battle.pbCanChooseMove?(idxBattler, i, false)
|
next if !@battle.pbCanChooseMove?(@user.index, i, false)
|
||||||
choices.push([i, 100, -1]) # Move index, score, target
|
choices.push([i, 100, -1]) # Move index, score, target
|
||||||
end
|
end
|
||||||
if choices.length == 0 # No moves are physically possible to use; use Struggle
|
if choices.length == 0 # No moves are physically possible to use; use Struggle
|
||||||
@battle.pbAutoChooseMove(user.index)
|
@battle.pbAutoChooseMove(@user.index)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Randomly choose a move from the choices and register it
|
# Randomly choose a move from the choices and register it
|
||||||
randNum = pbAIRandom(totalScore)
|
randNum = pbAIRandom(totalScore)
|
||||||
choices.each do |c|
|
choices.each do |c|
|
||||||
randNum -= c[1]
|
randNum -= c[1]
|
||||||
next if randNum >= 0
|
next if randNum >= 0
|
||||||
@battle.pbRegisterMove(idxBattler, c[0], false)
|
@battle.pbRegisterMove(@user.index, c[0], false)
|
||||||
@battle.pbRegisterTarget(idxBattler, c[2]) if c[2] >= 0
|
@battle.pbRegisterTarget(@user.index, c[2]) if c[2] >= 0
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
# Log the result
|
# Log the result
|
||||||
if @battle.choices[idxBattler][2]
|
if @battle.choices[@user.index][2]
|
||||||
PBDebug.log("[AI] #{user.pbThis} (#{user.index}) will use #{@battle.choices[idxBattler][2].name}")
|
PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) will use #{@battle.choices[@user.index][2].name}")
|
||||||
end
|
end
|
||||||
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
|
# Get scores for the given move against each possible target
|
||||||
#=============================================================================
|
#=============================================================================
|
||||||
# Wild Pokémon choose their moves randomly.
|
# 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
|
choices.push([idxMove, 100, -1]) # Move index, score, target
|
||||||
end
|
end
|
||||||
|
|
||||||
# Trainer Pokémon calculate how much they want to use each of their moves.
|
# Trainer Pokémon calculate how much they want to use each of their moves.
|
||||||
def pbRegisterMoveTrainer(user, idxMove, choices, skill)
|
def pbRegisterMoveTrainer(idxMove, choices)
|
||||||
move = user.moves[idxMove]
|
move = @user.moves[idxMove]
|
||||||
target_data = move.pbTarget(user)
|
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) ||
|
if [:UserAndAllies, :AllAllies, :AllBattlers].include?(target_data.id) ||
|
||||||
target_data.num_targets == 0
|
target_data.num_targets == 0
|
||||||
# If move has no targets, affects the user, a side or the whole field, or
|
# 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
|
# specially affects multiple Pokémon and the AI calculates an overall
|
||||||
# score at once instead of per target
|
# 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
|
choices.push([idxMove, score, -1]) if score > 0
|
||||||
elsif target_data.num_targets > 1
|
elsif target_data.num_targets > 1
|
||||||
# If move affects multiple battlers and you don't choose a particular one
|
# If move affects multiple battlers and you don't choose a particular one
|
||||||
totalScore = 0
|
totalScore = 0
|
||||||
@battle.allBattlers.each do |b|
|
@battle.allBattlers.each do |b|
|
||||||
next if !@battle.pbMoveCanTarget?(user.index, b.index, target_data)
|
next if !@battle.pbMoveCanTarget?(@user.index, b.index, target_data)
|
||||||
score = pbGetMoveScore(move, user, b, skill)
|
score = pbGetMoveScore(move, b)
|
||||||
totalScore += ((user.opposes?(b)) ? score : -score)
|
totalScore += ((@user.opposes?(b)) ? score : -score)
|
||||||
end
|
end
|
||||||
choices.push([idxMove, totalScore, -1]) if totalScore > 0
|
choices.push([idxMove, totalScore, -1]) if totalScore > 0
|
||||||
else
|
else
|
||||||
# If move affects one battler and you have to choose which one
|
# If move affects one battler and you have to choose which one
|
||||||
scoresAndTargets = []
|
scoresAndTargets = []
|
||||||
@battle.allBattlers.each do |b|
|
@battle.allBattlers.each do |b|
|
||||||
next if !@battle.pbMoveCanTarget?(user.index, b.index, target_data)
|
next if !@battle.pbMoveCanTarget?(@user.index, b.index, target_data)
|
||||||
next if target_data.targets_foe && !user.opposes?(b)
|
next if target_data.targets_foe && !@user.opposes?(b)
|
||||||
score = pbGetMoveScore(move, user, b, skill)
|
score = pbGetMoveScore(move, b)
|
||||||
scoresAndTargets.push([score, b.index]) if score > 0
|
scoresAndTargets.push([score, b.index]) if score > 0
|
||||||
end
|
end
|
||||||
if scoresAndTargets.length > 0
|
if scoresAndTargets.length > 0
|
||||||
@@ -152,34 +169,134 @@ class Battle::AI
|
|||||||
end
|
end
|
||||||
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
|
# Get a score for the given move being used against the given target
|
||||||
#=============================================================================
|
#=============================================================================
|
||||||
def pbGetMoveScore(move, user, target, skill = 100)
|
def pbGetMoveScore(move, target = nil)
|
||||||
skill = PBTrainerAI.minimumSkill if skill < PBTrainerAI.minimumSkill
|
set_up_move_check(move, target)
|
||||||
score = 100
|
|
||||||
score = pbGetMoveScoreFunctionCode(score, move, user, target, skill)
|
# 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
|
# A score of 0 here means it absolutely should not be used
|
||||||
return 0 if score <= 0
|
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
|
# Prefer damaging moves if AI has no more Pokémon or AI is less clever
|
||||||
if @battle.pbAbleNonActiveCount(user.idxOwnSide) == 0 &&
|
if @battle.pbAbleNonActiveCount(@user.idxOwnSide) == 0 &&
|
||||||
!(skill >= PBTrainerAI.highSkill && @battle.pbAbleNonActiveCount(target.idxOwnSide) > 0)
|
!(skill_check(AILevel.high) && @battle.pbAbleNonActiveCount(@target.idxOwnSide) > 0)
|
||||||
if move.statusMove?
|
if @move.statusMove?
|
||||||
score /= 1.5
|
score *= 0.9
|
||||||
elsif target.hp <= target.totalhp / 2
|
elsif @target.hp <= @target.totalhp / 2
|
||||||
score *= 1.5
|
score *= 1.1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Don't prefer attacking the target if they'd be semi-invulnerable
|
# Don't prefer attacking the target if they'd be semi-invulnerable
|
||||||
if skill >= PBTrainerAI.highSkill && move.accuracy > 0 &&
|
if skill_check(AILevel.high) && @move.accuracy > 0 && @user_faster &&
|
||||||
(target.semiInvulnerable? || target.effects[PBEffects::SkyDrop] >= 0)
|
(@target.semiInvulnerable? || @target.effects[PBEffects::SkyDrop] >= 0)
|
||||||
miss = true
|
miss = true
|
||||||
miss = false if user.hasActiveAbility?(:NOGUARD) || target.hasActiveAbility?(:NOGUARD)
|
miss = false if @user.hasActiveAbility?(:NOGUARD)
|
||||||
if miss && pbRoughStat(user, :SPEED, skill) > pbRoughStat(target, :SPEED, skill)
|
miss = false if skill_check(AILevel.best) && @target.hasActiveAbility?(:NOGUARD)
|
||||||
|
if skill_check(AILevel.best) && miss
|
||||||
# Knows what can get past semi-invulnerability
|
# Knows what can get past semi-invulnerability
|
||||||
if target.effects[PBEffects::SkyDrop] >= 0 ||
|
if @target.effects[PBEffects::SkyDrop] >= 0 ||
|
||||||
target.inTwoTurnAttack?("TwoTurnAttackInvulnerableInSky",
|
@target.inTwoTurnAttack?("TwoTurnAttackInvulnerableInSky",
|
||||||
"TwoTurnAttackInvulnerableInSkyParalyzeTarget",
|
"TwoTurnAttackInvulnerableInSkyParalyzeTarget",
|
||||||
"TwoTurnAttackInvulnerableInSkyTargetCannotAct")
|
"TwoTurnAttackInvulnerableInSkyTargetCannotAct")
|
||||||
miss = false if move.hitsFlyingTargets?
|
miss = false if move.hitsFlyingTargets?
|
||||||
@@ -189,107 +306,388 @@ class Battle::AI
|
|||||||
miss = false if move.hitsDivingTargets?
|
miss = false if move.hitsDivingTargets?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
score -= 80 if miss
|
score = 0 if miss
|
||||||
end
|
end
|
||||||
|
|
||||||
# Pick a good move for the Choice items
|
# Pick a good move for the Choice items
|
||||||
if user.hasActiveItem?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF]) ||
|
if @user.hasActiveItem?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF]) ||
|
||||||
user.hasActiveAbility?(:GORILLATACTICS)
|
@user.hasActiveAbility?(:GORILLATACTICS)
|
||||||
if move.baseDamage >= 60
|
# Really don't prefer status moves (except Trick)
|
||||||
score += 60
|
score *= 0.1 if @move.statusMove? && @move.function != "UserTargetSwapItems"
|
||||||
elsif move.damagingMove?
|
# Don't prefer moves of certain types
|
||||||
score += 30
|
move_type = pbRoughType(@move)
|
||||||
elsif move.function == "UserTargetSwapItems"
|
# Most unpreferred types are 0x effective against another type, except
|
||||||
score += 70 # Trick
|
# Fire/Water/Grass
|
||||||
else
|
# TODO: Actually check through the types for 0x instead of hardcoding
|
||||||
score -= 60
|
# them.
|
||||||
end
|
# 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
|
end
|
||||||
# If user is asleep, prefer moves that are usable while asleep
|
|
||||||
if user.status == :SLEEP && !move.usableWhenAsleep?
|
# If user is asleep, don't prefer moves that can't be used while asleep
|
||||||
user.eachMove do |m|
|
if skill_check(AILevel.medium) && @user.asleep? && @user.statusCount > 1 &&
|
||||||
next unless m.usableWhenAsleep?
|
!@move.usableWhenAsleep?
|
||||||
score -= 60
|
score *= 0.2
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# If user is frozen, prefer a move that can thaw the user
|
# If user is frozen, prefer a move that can thaw the user
|
||||||
if user.status == :FROZEN
|
if skill_check(AILevel.medium) && @user.status == :FROZEN
|
||||||
if move.thawsUser?
|
if @move.thawsUser?
|
||||||
score += 40
|
score += 30
|
||||||
else
|
else
|
||||||
user.eachMove do |m|
|
@user.eachMove do |m|
|
||||||
next unless m.thawsUser?
|
next unless m.thawsUser?
|
||||||
score -= 60
|
score = 0 # Discard this move if user knows another move that thaws
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# If target is frozen, don't prefer moves that could thaw them
|
# If target is frozen, don't prefer moves that could thaw them
|
||||||
if target.status == :FROZEN
|
if @target.status == :FROZEN
|
||||||
user.eachMove do |m|
|
if pbRoughType(@move) == :FIRE || (Settings::MECHANICS_GENERATION >= 6 && @move.thawsUser?)
|
||||||
next if m.thawsUser?
|
score *= 0.1
|
||||||
score -= 60
|
|
||||||
break
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
# Don't prefer moves that are ineffective because of abilities or effects
|
|
||||||
return 0 if pbCheckMoveImmunity(score, move, user, target, skill)
|
# Don't prefer hitting a wild shiny Pokémon
|
||||||
# Adjust score based on how much damage it can deal
|
if @battle.wildBattle? && @target.opposes? && @target.shiny?
|
||||||
if move.damagingMove?
|
score *= 0.15
|
||||||
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
|
|
||||||
end
|
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 = score.to_i
|
||||||
score = 0 if score < 0
|
score = 0 if score < 0
|
||||||
return score
|
return score
|
||||||
end
|
end
|
||||||
|
|
||||||
#=============================================================================
|
#=============================================================================
|
||||||
# Add to a move's score based on how much damage it will deal (as a percentage
|
# Calculate how much damage a move is likely to do to a given target (as a
|
||||||
# of the target's current HP)
|
# percentage of the target's current HP)
|
||||||
#=============================================================================
|
#=============================================================================
|
||||||
def pbGetMoveScoreDamage(score, move, user, target, skill)
|
def pbGetDamagingMoveBaseScore
|
||||||
return 0 if score <= 0
|
# 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)
|
# Calculate how much damage the move will do (roughly)
|
||||||
baseDmg = pbMoveBaseDamage(move, user, target, skill)
|
base_damage = pbMoveBaseDamage(@move, @target)
|
||||||
realDamage = pbRoughDamage(move, user, target, skill, baseDmg)
|
calc_damage = pbRoughDamage(@move, @target, base_damage)
|
||||||
# Account for accuracy of move
|
|
||||||
accuracy = pbRoughAccuracy(move, user, target, skill)
|
# TODO: Maybe move this check elsewhere? Note that Reborn's base score does
|
||||||
realDamage *= accuracy / 100.0
|
# not include this halving, but the predicted damage does.
|
||||||
# Two-turn attacks waste 2 turns to deal one lot of damage
|
# Two-turn attacks waste 2 turns to deal one lot of damage
|
||||||
if move.chargingTurnMove? || move.function == "AttackAndSkipNextTurn" # Hyper Beam
|
calc_damage /= 2 if @move.chargingTurnMove?
|
||||||
realDamage *= 2 / 3 # Not halved because semi-invulnerable during use or hits first turn
|
|
||||||
end
|
# TODO: Maybe move this check elsewhere?
|
||||||
# Prefer flinching external effects (note that move effects which cause
|
# Increased critical hit rate
|
||||||
# flinching are dealt with in the function code part of score calculation)
|
if skill_check(AILevel.medium)
|
||||||
if skill >= PBTrainerAI.mediumSkill && !move.flinchingMove? &&
|
crit_stage = pbRoughCriticalHitStage(@move, @target)
|
||||||
!target.hasActiveAbility?(:INNERFOCUS) &&
|
if crit_stage >= 0
|
||||||
!target.hasActiveAbility?(:SHIELDDUST) &&
|
crit_fraction = (crit_stage > 50) ? 1 : Battle::Move::CRITICAL_HIT_RATIOS[crit_stage]
|
||||||
target.effects[PBEffects::Substitute] == 0
|
crit_mult = (Settings::NEW_CRITICAL_HIT_RATE_MECHANICS) ? 0.5 : 1
|
||||||
canFlinch = false
|
calc_damage *= (1 + crit_mult / crit_fraction)
|
||||||
if user.hasActiveItem?([:KINGSROCK, :RAZORFANG]) ||
|
|
||||||
user.hasActiveAbility?(:STENCH)
|
|
||||||
canFlinch = true
|
|
||||||
end
|
end
|
||||||
realDamage *= 1.3 if canFlinch
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Convert damage to percentage of target's remaining HP
|
# 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
|
# 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
|
# 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
|
# Adjust score
|
||||||
damagePercentage = 120 if damagePercentage > 120 # Treat all lethal moves the same
|
damage_percentage = 110 if damage_percentage > 110 # Treat all lethal moves the same
|
||||||
damagePercentage += 40 if damagePercentage > 100 # Prefer moves likely to be lethal
|
damage_percentage += 40 if damage_percentage > 100 # Prefer moves likely to be lethal
|
||||||
score += damagePercentage.to_i
|
|
||||||
return score
|
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
|
||||||
end
|
end
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -2,21 +2,21 @@ class Battle::AI
|
|||||||
#=============================================================================
|
#=============================================================================
|
||||||
#
|
#
|
||||||
#=============================================================================
|
#=============================================================================
|
||||||
def pbTargetsMultiple?(move, user)
|
def pbTargetsMultiple?(move)
|
||||||
target_data = move.pbTarget(user)
|
target_data = move.pbTarget(@user)
|
||||||
return false if target_data.num_targets <= 1
|
return false if target_data.num_targets <= 1
|
||||||
num_targets = 0
|
num_targets = 0
|
||||||
case target_data.id
|
case target_data.id
|
||||||
when :AllAllies
|
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
|
when :UserAndAllies
|
||||||
@battle.allSameSideBattlers(user).each { |_b| num_targets += 1 }
|
@battle.allSameSideBattlers(@user).each { |_b| num_targets += 1 }
|
||||||
when :AllNearFoes
|
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
|
when :AllFoes
|
||||||
@battle.allOtherSideBattlers(user).each { |_b| num_targets += 1 }
|
@battle.allOtherSideBattlers(@user).each { |_b| num_targets += 1 }
|
||||||
when :AllNearOthers
|
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
|
when :AllBattlers
|
||||||
@battle.allBattlers.each { |_b| num_targets += 1 }
|
@battle.allBattlers.each { |_b| num_targets += 1 }
|
||||||
end
|
end
|
||||||
@@ -83,11 +83,11 @@ class Battle::AI
|
|||||||
|
|
||||||
# For switching. Determines the effectiveness of a potential switch-in against
|
# For switching. Determines the effectiveness of a potential switch-in against
|
||||||
# an opposing battler.
|
# an opposing battler.
|
||||||
def pbCalcTypeModPokemon(battlerThis, _battlerOther)
|
def pbCalcTypeModPokemon(pkmn, target)
|
||||||
mod1 = Effectiveness.calculate(battlerThis.types[0], target.types[0], target.types[1])
|
mod1 = Effectiveness.calculate(pkmn.types[0], target.types[0], target.types[1])
|
||||||
mod2 = Effectiveness::NORMAL_EFFECTIVE
|
mod2 = Effectiveness::NORMAL_EFFECTIVE
|
||||||
if battlerThis.types.length > 1
|
if pkmn.types.length > 1
|
||||||
mod2 = Effectiveness.calculate(battlerThis.types[1], target.types[0], target.types[1])
|
mod2 = Effectiveness.calculate(pkmn.types[1], target.types[0], target.types[1])
|
||||||
mod2 = mod2.to_f / Effectivenesss::NORMAL_EFFECTIVE
|
mod2 = mod2.to_f / Effectivenesss::NORMAL_EFFECTIVE
|
||||||
end
|
end
|
||||||
return mod1 * mod2
|
return mod1 * mod2
|
||||||
@@ -96,15 +96,18 @@ class Battle::AI
|
|||||||
#=============================================================================
|
#=============================================================================
|
||||||
# Immunity to a move because of the target's ability, item or other effects
|
# Immunity to a move because of the target's ability, item or other effects
|
||||||
#=============================================================================
|
#=============================================================================
|
||||||
def pbCheckMoveImmunity(score, move, user, target, skill)
|
def pbCheckMoveImmunity(move, target)
|
||||||
type = pbRoughType(move, user, skill)
|
# TODO: Add consideration of user's Mold Breaker.
|
||||||
typeMod = pbCalcTypeMod(type, user, target)
|
move_type = pbRoughType(move)
|
||||||
|
typeMod = pbCalcTypeMod(move_type, @user, target)
|
||||||
# Type effectiveness
|
# 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
|
# Immunity due to ability/item/other effects
|
||||||
if skill >= PBTrainerAI.mediumSkill
|
if skill_check(AILevel.medium)
|
||||||
case type
|
case move_type
|
||||||
when :GROUND
|
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?
|
return true if target.airborne? && !move.hitsFlyingTargets?
|
||||||
when :FIRE
|
when :FIRE
|
||||||
return true if target.hasActiveAbility?(:FLASHFIRE)
|
return true if target.hasActiveAbility?(:FLASHFIRE)
|
||||||
@@ -117,24 +120,25 @@ class Battle::AI
|
|||||||
end
|
end
|
||||||
return true if move.damagingMove? && Effectiveness.not_very_effective?(typeMod) &&
|
return true if move.damagingMove? && Effectiveness.not_very_effective?(typeMod) &&
|
||||||
target.hasActiveAbility?(:WONDERGUARD)
|
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)
|
target.hasActiveAbility?(:TELEPATHY)
|
||||||
return true if move.statusMove? && move.canMagicCoat? && target.hasActiveAbility?(:MAGICBOUNCE) &&
|
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.soundMove? && target.hasActiveAbility?(:SOUNDPROOF)
|
||||||
return true if move.bombMove? && target.hasActiveAbility?(:BULLETPROOF)
|
return true if move.bombMove? && target.hasActiveAbility?(:BULLETPROOF)
|
||||||
if move.powderMove?
|
if move.powderMove?
|
||||||
return true if target.pbHasType?(:GRASS)
|
return true if target.pbHasType?(:GRASS)
|
||||||
return true if target.hasActiveAbility?(:OVERCOAT)
|
return true if skill_check(AILevel.best) && target.hasActiveAbility?(:OVERCOAT)
|
||||||
return true if target.hasActiveItem?(:SAFETYGOGGLES)
|
return true if skill_check(AILevel.high) && target.hasActiveItem?(:SAFETYGOGGLES)
|
||||||
end
|
end
|
||||||
return true if move.statusMove? && target.effects[PBEffects::Substitute] > 0 &&
|
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 &&
|
return true if move.statusMove? && Settings::MECHANICS_GENERATION >= 7 &&
|
||||||
user.hasActiveAbility?(:PRANKSTER) && target.pbHasType?(:DARK) &&
|
@user.hasActiveAbility?(:PRANKSTER) && target.pbHasType?(:DARK) &&
|
||||||
target.opposes?(user)
|
target.opposes?(@user)
|
||||||
return true if move.priority > 0 && @battle.field.terrain == :Psychic &&
|
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
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
@@ -142,16 +146,14 @@ class Battle::AI
|
|||||||
#=============================================================================
|
#=============================================================================
|
||||||
# Get approximate properties for a battler
|
# Get approximate properties for a battler
|
||||||
#=============================================================================
|
#=============================================================================
|
||||||
def pbRoughType(move, user, skill)
|
def pbRoughType(move)
|
||||||
ret = move.type
|
ret = move.type
|
||||||
if skill >= PBTrainerAI.highSkill
|
ret = move.pbCalcType(@user) if skill_check(AILevel.high)
|
||||||
ret = move.pbCalcType(user)
|
|
||||||
end
|
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
def pbRoughStat(battler, stat, skill)
|
def pbRoughStat(battler, stat)
|
||||||
return battler.pbSpeed if skill >= PBTrainerAI.highSkill && stat == :SPEED
|
return battler.pbSpeed if skill_check(AILevel.high) && stat == :SPEED
|
||||||
stageMul = [2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8]
|
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]
|
stageDiv = [8, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2]
|
||||||
stage = battler.stages[stat] + 6
|
stage = battler.stages[stat] + 6
|
||||||
@@ -169,25 +171,25 @@ class Battle::AI
|
|||||||
#=============================================================================
|
#=============================================================================
|
||||||
# Get a better move's base damage value
|
# Get a better move's base damage value
|
||||||
#=============================================================================
|
#=============================================================================
|
||||||
def pbMoveBaseDamage(move, user, target, skill)
|
def pbMoveBaseDamage(move, target)
|
||||||
baseDmg = move.baseDamage
|
baseDmg = move.baseDamage
|
||||||
baseDmg = 60 if baseDmg == 1
|
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
|
# Covers all function codes which have their own def pbBaseDamage
|
||||||
case move.function
|
case move.function
|
||||||
# Sonic Boom, Dragon Rage, Super Fang, Night Shade, Endeavor
|
# Sonic Boom, Dragon Rage, Super Fang, Night Shade, Endeavor
|
||||||
when "FixedDamage20", "FixedDamage40", "FixedDamageHalfTargetHP",
|
when "FixedDamage20", "FixedDamage40", "FixedDamageHalfTargetHP",
|
||||||
"FixedDamageUserLevel", "LowerTargetHPToUserHP"
|
"FixedDamageUserLevel", "LowerTargetHPToUserHP"
|
||||||
baseDmg = move.pbFixedDamage(user, target)
|
baseDmg = move.pbFixedDamage(@user, target)
|
||||||
when "FixedDamageUserLevelRandom" # Psywave
|
when "FixedDamageUserLevelRandom" # Psywave
|
||||||
baseDmg = user.level
|
baseDmg = @user.level
|
||||||
when "OHKO", "OHKOIce", "OHKOHitsUndergroundTarget"
|
when "OHKO", "OHKOIce", "OHKOHitsUndergroundTarget"
|
||||||
baseDmg = 200
|
baseDmg = 200
|
||||||
when "CounterPhysicalDamage", "CounterSpecialDamage", "CounterDamagePlusHalf"
|
when "CounterPhysicalDamage", "CounterSpecialDamage", "CounterDamagePlusHalf"
|
||||||
baseDmg = 60
|
baseDmg = 60
|
||||||
when "DoublePowerIfTargetUnderwater", "DoublePowerIfTargetUnderground",
|
when "DoublePowerIfTargetUnderwater", "DoublePowerIfTargetUnderground",
|
||||||
"BindTargetDoublePowerIfTargetUnderwater"
|
"BindTargetDoublePowerIfTargetUnderwater"
|
||||||
baseDmg = move.pbModifyDamage(baseDmg, user, target)
|
baseDmg = move.pbModifyDamage(baseDmg, @user, target)
|
||||||
# Gust, Twister, Venoshock, Smelling Salts, Wake-Up Slap, Facade, Hex, Brine,
|
# Gust, Twister, Venoshock, Smelling Salts, Wake-Up Slap, Facade, Hex, Brine,
|
||||||
# Retaliate, Weather Ball, Return, Frustration, Eruption, Crush Grip,
|
# Retaliate, Weather Ball, Return, Frustration, Eruption, Crush Grip,
|
||||||
# Stored Power, Punishment, Hidden Power, Fury Cutter, Echoed Voice,
|
# Stored Power, Punishment, Hidden Power, Fury Cutter, Echoed Voice,
|
||||||
@@ -217,12 +219,12 @@ class Battle::AI
|
|||||||
"PowerHigherWithTargetWeight",
|
"PowerHigherWithTargetWeight",
|
||||||
"ThrowUserItemAtTarget",
|
"ThrowUserItemAtTarget",
|
||||||
"PowerDependsOnUserStockpile"
|
"PowerDependsOnUserStockpile"
|
||||||
baseDmg = move.pbBaseDamage(baseDmg, user, target)
|
baseDmg = move.pbBaseDamage(baseDmg, @user, target)
|
||||||
when "DoublePowerIfUserHasNoItem" # Acrobatics
|
when "DoublePowerIfUserHasNoItem" # Acrobatics
|
||||||
baseDmg *= 2 if !user.item || user.hasActiveItem?(:FLYINGGEM)
|
baseDmg *= 2 if !@user.item || @user.hasActiveItem?(:FLYINGGEM)
|
||||||
when "PowerHigherWithTargetFasterThanUser" # Gyro Ball
|
when "PowerHigherWithTargetFasterThanUser" # Gyro Ball
|
||||||
targetSpeed = pbRoughStat(target, :SPEED, skill)
|
targetSpeed = pbRoughStat(target, :SPEED)
|
||||||
userSpeed = pbRoughStat(user, :SPEED, skill)
|
userSpeed = pbRoughStat(@user, :SPEED)
|
||||||
baseDmg = [[(25 * targetSpeed / userSpeed).floor, 150].min, 1].max
|
baseDmg = [[(25 * targetSpeed / userSpeed).floor, 150].min, 1].max
|
||||||
when "RandomlyDamageOrHealTarget" # Present
|
when "RandomlyDamageOrHealTarget" # Present
|
||||||
baseDmg = 50
|
baseDmg = 50
|
||||||
@@ -230,46 +232,46 @@ class Battle::AI
|
|||||||
baseDmg = 71
|
baseDmg = 71
|
||||||
baseDmg *= 2 if target.inTwoTurnAttack?("TwoTurnAttackInvulnerableUnderground") # Dig
|
baseDmg *= 2 if target.inTwoTurnAttack?("TwoTurnAttackInvulnerableUnderground") # Dig
|
||||||
when "TypeAndPowerDependOnUserBerry" # Natural Gift
|
when "TypeAndPowerDependOnUserBerry" # Natural Gift
|
||||||
baseDmg = move.pbNaturalGiftBaseDamage(user.item_id)
|
baseDmg = move.pbNaturalGiftBaseDamage(@user.item_id)
|
||||||
when "PowerHigherWithUserHeavierThanTarget" # Heavy Slam
|
when "PowerHigherWithUserHeavierThanTarget" # Heavy Slam
|
||||||
baseDmg = move.pbBaseDamage(baseDmg, user, target)
|
baseDmg = move.pbBaseDamage(baseDmg, @user, target)
|
||||||
baseDmg *= 2 if Settings::MECHANICS_GENERATION >= 7 && skill >= PBTrainerAI.mediumSkill &&
|
baseDmg *= 2 if Settings::MECHANICS_GENERATION >= 7 && skill_check(AILevel.medium) &&
|
||||||
target.effects[PBEffects::Minimize]
|
target.effects[PBEffects::Minimize]
|
||||||
when "AlwaysCriticalHit", "HitTwoTimes", "HitTwoTimesPoisonTarget" # Frost Breath, Double Kick, Twineedle
|
when "AlwaysCriticalHit", "HitTwoTimes", "HitTwoTimesPoisonTarget" # Frost Breath, Double Kick, Twineedle
|
||||||
baseDmg *= 2
|
baseDmg *= 2
|
||||||
when "HitThreeTimesPowersUpWithEachHit" # Triple Kick
|
when "HitThreeTimesPowersUpWithEachHit" # Triple Kick
|
||||||
baseDmg *= 6 # Hits do x1, x2, x3 baseDmg in turn, for x6 in total
|
baseDmg *= 6 # Hits do x1, x2, x3 baseDmg in turn, for x6 in total
|
||||||
when "HitTwoToFiveTimes" # Fury Attack
|
when "HitTwoToFiveTimes" # Fury Attack
|
||||||
if user.hasActiveAbility?(:SKILLLINK)
|
if @user.hasActiveAbility?(:SKILLLINK)
|
||||||
baseDmg *= 5
|
baseDmg *= 5
|
||||||
else
|
else
|
||||||
baseDmg = (baseDmg * 31 / 10).floor # Average damage dealt
|
baseDmg = (baseDmg * 31 / 10).floor # Average damage dealt
|
||||||
end
|
end
|
||||||
when "HitTwoToFiveTimesOrThreeForAshGreninja"
|
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
|
baseDmg *= 4 # 3 hits at 20 power = 4 hits at 15 power
|
||||||
elsif user.hasActiveAbility?(:SKILLLINK)
|
elsif @user.hasActiveAbility?(:SKILLLINK)
|
||||||
baseDmg *= 5
|
baseDmg *= 5
|
||||||
else
|
else
|
||||||
baseDmg = (baseDmg * 31 / 10).floor # Average damage dealt
|
baseDmg = (baseDmg * 31 / 10).floor # Average damage dealt
|
||||||
end
|
end
|
||||||
when "HitOncePerUserTeamMember" # Beat Up
|
when "HitOncePerUserTeamMember" # Beat Up
|
||||||
mult = 0
|
mult = 0
|
||||||
@battle.eachInTeamFromBattlerIndex(user.index) do |pkmn, _i|
|
@battle.eachInTeamFromBattlerIndex(@user.index) do |pkmn, _i|
|
||||||
mult += 1 if pkmn&.able? && pkmn.status == :NONE
|
mult += 1 if pkmn&.able? && pkmn.status == :NONE
|
||||||
end
|
end
|
||||||
baseDmg *= mult
|
baseDmg *= mult
|
||||||
when "TwoTurnAttackOneTurnInSun" # Solar Beam
|
when "TwoTurnAttackOneTurnInSun" # Solar Beam
|
||||||
baseDmg = move.pbBaseDamageMultiplier(baseDmg, user, target)
|
baseDmg = move.pbBaseDamageMultiplier(baseDmg, @user, target)
|
||||||
when "MultiTurnAttackPowersUpEachTurn" # Rollout
|
when "MultiTurnAttackPowersUpEachTurn" # Rollout
|
||||||
baseDmg *= 2 if user.effects[PBEffects::DefenseCurl]
|
baseDmg *= 2 if @user.effects[PBEffects::DefenseCurl]
|
||||||
when "MultiTurnAttackBideThenReturnDoubleDamage" # Bide
|
when "MultiTurnAttackBideThenReturnDoubleDamage" # Bide
|
||||||
baseDmg = 40
|
baseDmg = 40
|
||||||
when "UserFaintsFixedDamageUserHP" # Final Gambit
|
when "UserFaintsFixedDamageUserHP" # Final Gambit
|
||||||
baseDmg = user.hp
|
baseDmg = @user.hp
|
||||||
when "EffectivenessIncludesFlyingType" # Flying Press
|
when "EffectivenessIncludesFlyingType" # Flying Press
|
||||||
if GameData::Type.exists?(:FLYING)
|
if GameData::Type.exists?(:FLYING)
|
||||||
if skill >= PBTrainerAI.highSkill
|
if skill_check(AILevel.high)
|
||||||
targetTypes = target.pbTypes(true)
|
targetTypes = target.pbTypes(true)
|
||||||
mult = Effectiveness.calculate(
|
mult = Effectiveness.calculate(
|
||||||
:FLYING, targetTypes[0], targetTypes[1], targetTypes[2]
|
:FLYING, targetTypes[0], targetTypes[1], targetTypes[2]
|
||||||
@@ -281,12 +283,12 @@ class Battle::AI
|
|||||||
end
|
end
|
||||||
baseDmg = (baseDmg.to_f * mult / Effectiveness::NORMAL_EFFECTIVE).round
|
baseDmg = (baseDmg.to_f * mult / Effectiveness::NORMAL_EFFECTIVE).round
|
||||||
end
|
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
|
when "DoublePowerIfUserLastMoveFailed" # Stomping Tantrum
|
||||||
baseDmg *= 2 if user.lastRoundMoveFailed
|
baseDmg *= 2 if @user.lastRoundMoveFailed
|
||||||
when "HitTwoTimesFlinchTarget" # Double Iron Bash
|
when "HitTwoTimesFlinchTarget" # Double Iron Bash
|
||||||
baseDmg *= 2
|
baseDmg *= 2
|
||||||
baseDmg *= 2 if skill >= PBTrainerAI.mediumSkill && target.effects[PBEffects::Minimize]
|
baseDmg *= 2 if skill_check(AILevel.medium) && target.effects[PBEffects::Minimize]
|
||||||
end
|
end
|
||||||
return baseDmg
|
return baseDmg
|
||||||
end
|
end
|
||||||
@@ -294,29 +296,33 @@ class Battle::AI
|
|||||||
#=============================================================================
|
#=============================================================================
|
||||||
# Damage calculation
|
# Damage calculation
|
||||||
#=============================================================================
|
#=============================================================================
|
||||||
def pbRoughDamage(move, user, target, skill, baseDmg)
|
def pbRoughDamage(move, target, baseDmg)
|
||||||
# Fixed damage moves
|
# Fixed damage moves
|
||||||
return baseDmg if move.is_a?(Battle::Move::FixedDamageMove)
|
return baseDmg if move.is_a?(Battle::Move::FixedDamageMove)
|
||||||
|
|
||||||
# Get the move's type
|
# Get the move's type
|
||||||
type = pbRoughType(move, user, skill)
|
type = pbRoughType(move)
|
||||||
|
|
||||||
##### Calculate user's attack stat #####
|
##### Calculate user's attack stat #####
|
||||||
atk = pbRoughStat(user, :ATTACK, skill)
|
atk = pbRoughStat(@user, :ATTACK)
|
||||||
if move.function == "UseTargetAttackInsteadOfUserAttack" # Foul Play
|
if move.function == "UseTargetAttackInsteadOfUserAttack" # Foul Play
|
||||||
atk = pbRoughStat(target, :ATTACK, skill)
|
atk = pbRoughStat(target, :ATTACK)
|
||||||
elsif move.function == "UseUserBaseDefenseInsteadOfUserBaseAttack" # Body Press
|
elsif move.function == "UseUserBaseDefenseInsteadOfUserBaseAttack" # Body Press
|
||||||
atk = pbRoughStat(user, :DEFENSE, skill)
|
atk = pbRoughStat(@user, :DEFENSE)
|
||||||
elsif move.specialMove?(type)
|
elsif move.specialMove?(type)
|
||||||
if move.function == "UseTargetAttackInsteadOfUserAttack" # Foul Play
|
if move.function == "UseTargetAttackInsteadOfUserAttack" # Foul Play
|
||||||
atk = pbRoughStat(target, :SPECIAL_ATTACK, skill)
|
atk = pbRoughStat(target, :SPECIAL_ATTACK)
|
||||||
else
|
else
|
||||||
atk = pbRoughStat(user, :SPECIAL_ATTACK, skill)
|
atk = pbRoughStat(@user, :SPECIAL_ATTACK)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
##### Calculate target's defense stat #####
|
##### Calculate target's defense stat #####
|
||||||
defense = pbRoughStat(target, :DEFENSE, skill)
|
defense = pbRoughStat(target, :DEFENSE)
|
||||||
if move.specialMove?(type) && move.function != "UseTargetDefenseInsteadOfTargetSpDef" # Psyshock
|
if move.specialMove?(type) && move.function != "UseTargetDefenseInsteadOfTargetSpDef" # Psyshock
|
||||||
defense = pbRoughStat(target, :SPECIAL_DEFENSE, skill)
|
defense = pbRoughStat(target, :SPECIAL_DEFENSE)
|
||||||
end
|
end
|
||||||
|
|
||||||
##### Calculate all multiplier effects #####
|
##### Calculate all multiplier effects #####
|
||||||
multipliers = {
|
multipliers = {
|
||||||
:base_damage_multiplier => 1.0,
|
:base_damage_multiplier => 1.0,
|
||||||
@@ -325,11 +331,9 @@ class Battle::AI
|
|||||||
:final_damage_multiplier => 1.0
|
:final_damage_multiplier => 1.0
|
||||||
}
|
}
|
||||||
# Ability effects that alter damage
|
# Ability effects that alter damage
|
||||||
moldBreaker = false
|
moldBreaker = skill_check(AILevel.high) && target.hasMoldBreaker?
|
||||||
if skill >= PBTrainerAI.highSkill && target.hasMoldBreaker?
|
|
||||||
moldBreaker = true
|
if skill_check(AILevel.medium) && @user.abilityActive?
|
||||||
end
|
|
||||||
if skill >= PBTrainerAI.mediumSkill && user.abilityActive?
|
|
||||||
# NOTE: These abilities aren't suitable for checking at the start of the
|
# NOTE: These abilities aren't suitable for checking at the start of the
|
||||||
# round.
|
# round.
|
||||||
abilityBlacklist = [:ANALYTIC, :SNIPER, :TINTEDLENS, :AERILATE, :PIXILATE, :REFRIGERATE]
|
abilityBlacklist = [:ANALYTIC, :SNIPER, :TINTEDLENS, :AERILATE, :PIXILATE, :REFRIGERATE]
|
||||||
@@ -341,19 +345,21 @@ class Battle::AI
|
|||||||
end
|
end
|
||||||
if canCheck
|
if canCheck
|
||||||
Battle::AbilityEffects.triggerDamageCalcFromUser(
|
Battle::AbilityEffects.triggerDamageCalcFromUser(
|
||||||
user.ability, user, target, move, multipliers, baseDmg, type
|
@user.ability, @user, target, move, multipliers, baseDmg, type
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
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?
|
next if !b.abilityActive?
|
||||||
Battle::AbilityEffects.triggerDamageCalcFromAlly(
|
Battle::AbilityEffects.triggerDamageCalcFromAlly(
|
||||||
b.ability, user, target, move, multipliers, baseDmg, type
|
b.ability, @user, target, move, multipliers, baseDmg, type
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
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
|
# NOTE: These abilities aren't suitable for checking at the start of the
|
||||||
# round.
|
# round.
|
||||||
abilityBlacklist = [:FILTER, :SOLIDROCK]
|
abilityBlacklist = [:FILTER, :SOLIDROCK]
|
||||||
@@ -365,40 +371,45 @@ class Battle::AI
|
|||||||
end
|
end
|
||||||
if canCheck
|
if canCheck
|
||||||
Battle::AbilityEffects.triggerDamageCalcFromTarget(
|
Battle::AbilityEffects.triggerDamageCalcFromTarget(
|
||||||
target.ability, user, target, move, multipliers, baseDmg, type
|
target.ability, @user, target, move, multipliers, baseDmg, type
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if skill >= PBTrainerAI.bestSkill && !moldBreaker
|
|
||||||
|
if skill_check(AILevel.best) && !moldBreaker
|
||||||
target.allAllies.each do |b|
|
target.allAllies.each do |b|
|
||||||
next if !b.abilityActive?
|
next if !b.abilityActive?
|
||||||
Battle::AbilityEffects.triggerDamageCalcFromTargetAlly(
|
Battle::AbilityEffects.triggerDamageCalcFromTargetAlly(
|
||||||
b.ability, user, target, move, multipliers, baseDmg, type
|
b.ability, @user, target, move, multipliers, baseDmg, type
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Item effects that alter damage
|
# Item effects that alter damage
|
||||||
# NOTE: Type-boosting gems aren't suitable for checking at the start of the
|
# NOTE: Type-boosting gems aren't suitable for checking at the start of the
|
||||||
# round.
|
# 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
|
# NOTE: These items aren't suitable for checking at the start of the
|
||||||
# round.
|
# round.
|
||||||
itemBlacklist = [:EXPERTBELT, :LIFEORB]
|
itemBlacklist = [:EXPERTBELT, :LIFEORB]
|
||||||
if !itemBlacklist.include?(user.item_id)
|
if !itemBlacklist.include?(@user.item_id)
|
||||||
Battle::ItemEffects.triggerDamageCalcFromUser(
|
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
|
end
|
||||||
|
# TODO: Prefer (1.5x?) if item will be consumed and user has Unburden.
|
||||||
end
|
end
|
||||||
if skill >= PBTrainerAI.bestSkill &&
|
|
||||||
|
if skill_check(AILevel.best) &&
|
||||||
target.itemActive? && target.item && !target.item.is_berry?
|
target.itemActive? && target.item && !target.item.is_berry?
|
||||||
Battle::ItemEffects.triggerDamageCalcFromTarget(
|
Battle::ItemEffects.triggerDamageCalcFromTarget(
|
||||||
target.item, user, target, move, multipliers, baseDmg, type
|
target.item, @user, target, move, multipliers, baseDmg, type
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Global abilities
|
# Global abilities
|
||||||
if skill >= PBTrainerAI.mediumSkill &&
|
if skill_check(AILevel.medium) &&
|
||||||
((@battle.pbCheckGlobalAbility(:DARKAURA) && type == :DARK) ||
|
((@battle.pbCheckGlobalAbility(:DARKAURA) && type == :DARK) ||
|
||||||
(@battle.pbCheckGlobalAbility(:FAIRYAURA) && type == :FAIRY))
|
(@battle.pbCheckGlobalAbility(:FAIRYAURA) && type == :FAIRY))
|
||||||
if @battle.pbCheckGlobalAbility(:AURABREAK)
|
if @battle.pbCheckGlobalAbility(:AURABREAK)
|
||||||
@@ -407,20 +418,25 @@ class Battle::AI
|
|||||||
multipliers[:base_damage_multiplier] *= 4 / 3.0
|
multipliers[:base_damage_multiplier] *= 4 / 3.0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Parental Bond
|
# Parental Bond
|
||||||
if skill >= PBTrainerAI.mediumSkill && user.hasActiveAbility?(:PARENTALBOND)
|
if skill_check(AILevel.medium) && @user.hasActiveAbility?(:PARENTALBOND)
|
||||||
multipliers[:base_damage_multiplier] *= 1.25
|
multipliers[:base_damage_multiplier] *= 1.25
|
||||||
end
|
end
|
||||||
|
|
||||||
# Me First
|
# Me First
|
||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
# Helping Hand - n/a
|
# Helping Hand - n/a
|
||||||
|
|
||||||
# Charge
|
# Charge
|
||||||
if skill >= PBTrainerAI.mediumSkill &&
|
if skill_check(AILevel.medium) &&
|
||||||
user.effects[PBEffects::Charge] > 0 && type == :ELECTRIC
|
@user.effects[PBEffects::Charge] > 0 && type == :ELECTRIC
|
||||||
multipliers[:base_damage_multiplier] *= 2
|
multipliers[:base_damage_multiplier] *= 2
|
||||||
end
|
end
|
||||||
|
|
||||||
# Mud Sport and Water Sport
|
# Mud Sport and Water Sport
|
||||||
if skill >= PBTrainerAI.mediumSkill
|
if skill_check(AILevel.medium)
|
||||||
if type == :ELECTRIC
|
if type == :ELECTRIC
|
||||||
if @battle.allBattlers.any? { |b| b.effects[PBEffects::MudSport] }
|
if @battle.allBattlers.any? { |b| b.effects[PBEffects::MudSport] }
|
||||||
multipliers[:base_damage_multiplier] /= 3
|
multipliers[:base_damage_multiplier] /= 3
|
||||||
@@ -438,34 +454,40 @@ class Battle::AI
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Terrain moves
|
# Terrain moves
|
||||||
if skill >= PBTrainerAI.mediumSkill
|
if skill_check(AILevel.medium)
|
||||||
case @battle.field.terrain
|
case @battle.field.terrain
|
||||||
when :Electric
|
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
|
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
|
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
|
when :Misty
|
||||||
multipliers[:base_damage_multiplier] /= 2 if type == :DRAGON && target.affectedByTerrain?
|
multipliers[:base_damage_multiplier] /= 2 if type == :DRAGON && target.affectedByTerrain?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Badge multipliers
|
# 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
|
if move.physicalMove?(type) && @battle.pbPlayer.badge_count >= Settings::NUM_BADGES_BOOST_DEFENSE
|
||||||
multipliers[:defense_multiplier] *= 1.1
|
multipliers[:defense_multiplier] *= 1.1
|
||||||
elsif move.specialMove?(type) && @battle.pbPlayer.badge_count >= Settings::NUM_BADGES_BOOST_SPDEF
|
elsif move.specialMove?(type) && @battle.pbPlayer.badge_count >= Settings::NUM_BADGES_BOOST_SPDEF
|
||||||
multipliers[:defense_multiplier] *= 1.1
|
multipliers[:defense_multiplier] *= 1.1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Multi-targeting attacks
|
# Multi-targeting attacks
|
||||||
if skill >= PBTrainerAI.highSkill && pbTargetsMultiple?(move, user)
|
if skill_check(AILevel.high) && pbTargetsMultiple?(move)
|
||||||
multipliers[:final_damage_multiplier] *= 0.75
|
multipliers[:final_damage_multiplier] *= 0.75
|
||||||
end
|
end
|
||||||
|
|
||||||
# Weather
|
# Weather
|
||||||
if skill >= PBTrainerAI.mediumSkill
|
if skill_check(AILevel.medium)
|
||||||
case user.effectiveWeather
|
case @user.effectiveWeather
|
||||||
when :Sun, :HarshSun
|
when :Sun, :HarshSun
|
||||||
case type
|
case type
|
||||||
when :FIRE
|
when :FIRE
|
||||||
@@ -487,30 +509,36 @@ class Battle::AI
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Critical hits - n/a
|
# Critical hits - n/a
|
||||||
|
|
||||||
# Random variance - n/a
|
# Random variance - n/a
|
||||||
|
|
||||||
# STAB
|
# STAB
|
||||||
if skill >= PBTrainerAI.mediumSkill && type && user.pbHasType?(type)
|
if skill_check(AILevel.medium) && type && @user.pbHasType?(type)
|
||||||
if user.hasActiveAbility?(:ADAPTABILITY)
|
if @user.hasActiveAbility?(:ADAPTABILITY)
|
||||||
multipliers[:final_damage_multiplier] *= 2
|
multipliers[:final_damage_multiplier] *= 2
|
||||||
else
|
else
|
||||||
multipliers[:final_damage_multiplier] *= 1.5
|
multipliers[:final_damage_multiplier] *= 1.5
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Type effectiveness
|
# Type effectiveness
|
||||||
if skill >= PBTrainerAI.mediumSkill
|
if skill_check(AILevel.medium)
|
||||||
typemod = pbCalcTypeMod(type, user, target)
|
typemod = pbCalcTypeMod(type, @user, target)
|
||||||
multipliers[:final_damage_multiplier] *= typemod.to_f / Effectiveness::NORMAL_EFFECTIVE
|
multipliers[:final_damage_multiplier] *= typemod.to_f / Effectiveness::NORMAL_EFFECTIVE
|
||||||
end
|
end
|
||||||
|
|
||||||
# Burn
|
# Burn
|
||||||
if skill >= PBTrainerAI.highSkill && move.physicalMove?(type) &&
|
if skill_check(AILevel.high) && move.physicalMove?(type) &&
|
||||||
user.status == :BURN && !user.hasActiveAbility?(:GUTS) &&
|
@user.status == :BURN && !@user.hasActiveAbility?(:GUTS) &&
|
||||||
!(Settings::MECHANICS_GENERATION >= 6 &&
|
!(Settings::MECHANICS_GENERATION >= 6 &&
|
||||||
move.function == "DoublePowerIfUserPoisonedBurnedParalyzed") # Facade
|
move.function == "DoublePowerIfUserPoisonedBurnedParalyzed") # Facade
|
||||||
multipliers[:final_damage_multiplier] /= 2
|
multipliers[:final_damage_multiplier] /= 2
|
||||||
end
|
end
|
||||||
|
|
||||||
# Aurora Veil, Reflect, Light Screen
|
# 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 target.pbOwnSide.effects[PBEffects::AuroraVeil] > 0
|
||||||
if @battle.pbSideBattlerCount(target) > 1
|
if @battle.pbSideBattlerCount(target) > 1
|
||||||
multipliers[:final_damage_multiplier] *= 2 / 3.0
|
multipliers[:final_damage_multiplier] *= 2 / 3.0
|
||||||
@@ -531,80 +559,93 @@ class Battle::AI
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Minimize
|
# 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
|
multipliers[:final_damage_multiplier] *= 2
|
||||||
end
|
end
|
||||||
|
|
||||||
# Move-specific base damage modifiers
|
# Move-specific base damage modifiers
|
||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
# Move-specific final damage modifiers
|
# Move-specific final damage modifiers
|
||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
##### Main damage calculation #####
|
##### Main damage calculation #####
|
||||||
baseDmg = [(baseDmg * multipliers[:base_damage_multiplier]).round, 1].max
|
baseDmg = [(baseDmg * multipliers[:base_damage_multiplier]).round, 1].max
|
||||||
atk = [(atk * multipliers[:attack_multiplier]).round, 1].max
|
atk = [(atk * multipliers[:attack_multiplier]).round, 1].max
|
||||||
defense = [(defense * multipliers[:defense_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
|
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
|
return damage.floor
|
||||||
end
|
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
|
# Accuracy calculation
|
||||||
#=============================================================================
|
#=============================================================================
|
||||||
def pbRoughAccuracy(move, user, target, skill)
|
def pbRoughAccuracy(move, target)
|
||||||
# "Always hit" effects and "always hit" accuracy
|
# "Always hit" effects and "always hit" accuracy
|
||||||
if skill >= PBTrainerAI.mediumSkill
|
if skill_check(AILevel.medium)
|
||||||
return 125 if target.effects[PBEffects::Minimize] && move.tramplesMinimize? &&
|
return 100 if target.effects[PBEffects::Minimize] && move.tramplesMinimize? &&
|
||||||
Settings::MECHANICS_GENERATION >= 6
|
Settings::MECHANICS_GENERATION >= 6
|
||||||
return 125 if target.effects[PBEffects::Telekinesis] > 0
|
return 100 if target.effects[PBEffects::Telekinesis] > 0
|
||||||
end
|
end
|
||||||
|
# Get base accuracy
|
||||||
baseAcc = move.accuracy
|
baseAcc = move.accuracy
|
||||||
if skill >= PBTrainerAI.highSkill
|
baseAcc = move.pbBaseAccuracy(@user, target) if skill_check(AILevel.medium)
|
||||||
baseAcc = move.pbBaseAccuracy(user, target)
|
return 100 if baseAcc == 0 && skill_check(AILevel.medium)
|
||||||
end
|
|
||||||
return 125 if baseAcc == 0 && skill >= PBTrainerAI.mediumSkill
|
|
||||||
# Get the move's type
|
# Get the move's type
|
||||||
type = pbRoughType(move, user, skill)
|
type = pbRoughType(move)
|
||||||
# Calculate all modifier effects
|
# Calculate all modifier effects
|
||||||
modifiers = {}
|
modifiers = {}
|
||||||
modifiers[:base_accuracy] = baseAcc
|
modifiers[:base_accuracy] = baseAcc
|
||||||
modifiers[:accuracy_stage] = user.stages[:ACCURACY]
|
modifiers[:accuracy_stage] = @user.stages[:ACCURACY]
|
||||||
modifiers[:evasion_stage] = target.stages[:EVASION]
|
modifiers[:evasion_stage] = target.stages[:EVASION]
|
||||||
modifiers[:accuracy_multiplier] = 1.0
|
modifiers[:accuracy_multiplier] = 1.0
|
||||||
modifiers[:evasion_multiplier] = 1.0
|
modifiers[:evasion_multiplier] = 1.0
|
||||||
pbCalcAccuracyModifiers(user, target, modifiers, move, type, skill)
|
pbCalcAccuracyModifiers(target, modifiers, move, type)
|
||||||
# Check if move can't miss
|
# Check if move certainly misses/can't miss
|
||||||
return 125 if modifiers[:base_accuracy] == 0
|
return 0 if modifiers[:base_accuracy] < 0
|
||||||
|
return 100 if modifiers[:base_accuracy] == 0
|
||||||
# Calculation
|
# Calculation
|
||||||
accStage = [[modifiers[:accuracy_stage], -6].max, 6].min + 6
|
accStage = [[modifiers[:accuracy_stage], -6].max, 6].min + 6
|
||||||
evaStage = [[modifiers[:evasion_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
|
return modifiers[:base_accuracy] * accuracy / evasion
|
||||||
end
|
end
|
||||||
|
|
||||||
def pbCalcAccuracyModifiers(user, target, modifiers, move, type, skill)
|
def pbCalcAccuracyModifiers(target, modifiers, move, type)
|
||||||
moldBreaker = false
|
moldBreaker = (skill_check(AILevel.medium) && target.hasMoldBreaker?)
|
||||||
if skill >= PBTrainerAI.highSkill && target.hasMoldBreaker?
|
|
||||||
moldBreaker = true
|
|
||||||
end
|
|
||||||
# Ability effects that alter accuracy calculation
|
# Ability effects that alter accuracy calculation
|
||||||
if skill >= PBTrainerAI.mediumSkill
|
if skill_check(AILevel.medium) && @user.abilityActive?
|
||||||
if user.abilityActive?
|
Battle::AbilityEffects.triggerAccuracyCalcFromUser(
|
||||||
Battle::AbilityEffects.triggerAccuracyCalcFromUser(
|
@user.ability, modifiers, @user, target, move, type
|
||||||
user.ability, modifiers, user, target, move, type
|
)
|
||||||
)
|
end
|
||||||
end
|
if skill_check(AILevel.high)
|
||||||
user.allAllies.each do |b|
|
@user.allAllies.each do |b|
|
||||||
next if !b.abilityActive?
|
next if !b.abilityActive?
|
||||||
Battle::AbilityEffects.triggerAccuracyCalcFromAlly(
|
Battle::AbilityEffects.triggerAccuracyCalcFromAlly(
|
||||||
b.ability, modifiers, user, target, move, type
|
b.ability, modifiers, @user, target, move, type
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if skill >= PBTrainerAI.bestSkill && target.abilityActive? && !moldBreaker
|
if skill_check(AILevel.best) && target.abilityActive? && !moldBreaker
|
||||||
Battle::AbilityEffects.triggerAccuracyCalcFromTarget(
|
Battle::AbilityEffects.triggerAccuracyCalcFromTarget(
|
||||||
target.ability, modifiers, user, target, move, type
|
target.ability, modifiers, @user, target, move, type
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
# Item effects that alter accuracy calculation
|
# 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(
|
Battle::ItemEffects.triggerAccuracyCalcFromUser(
|
||||||
user.item, modifiers, user, target, move, type
|
@user.item, modifiers, @user, target, move, type
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
if skill >= PBTrainerAI.bestSkill && target.itemActive?
|
if skill_check(AILevel.high) && target.itemActive?
|
||||||
Battle::ItemEffects.triggerAccuracyCalcFromTarget(
|
Battle::ItemEffects.triggerAccuracyCalcFromTarget(
|
||||||
target.item, modifiers, user, target, move, type
|
target.item, modifiers, @user, target, move, type
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
# Other effects, inc. ones that set accuracy_multiplier or evasion_stage to specific values
|
# 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
|
||||||
if @battle.field.effects[PBEffects::Gravity] > 0
|
modifiers[:accuracy_multiplier] *= 5 / 3.0
|
||||||
modifiers[:accuracy_multiplier] *= 5 / 3.0
|
end
|
||||||
end
|
if skill_check(AILevel.medium)
|
||||||
if user.effects[PBEffects::MicleBerry]
|
if @user.effects[PBEffects::MicleBerry]
|
||||||
modifiers[:accuracy_multiplier] *= 1.2
|
modifiers[:accuracy_multiplier] *= 1.2
|
||||||
end
|
end
|
||||||
modifiers[:evasion_stage] = 0 if target.effects[PBEffects::Foresight] && modifiers[:evasion_stage] > 0
|
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
|
modifiers[:evasion_stage] = 0 if target.effects[PBEffects::MiracleEye] && modifiers[:evasion_stage] > 0
|
||||||
end
|
end
|
||||||
# "AI-specific calculations below"
|
# "AI-specific calculations below"
|
||||||
if skill >= PBTrainerAI.mediumSkill
|
modifiers[:evasion_stage] = 0 if move.function == "IgnoreTargetDefSpDefEvaStatStages" # Chip Away
|
||||||
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 &&
|
modifiers[:base_accuracy] = 0 if @user.effects[PBEffects::LockOn] > 0 &&
|
||||||
user.effects[PBEffects::LockOnPos] == target.index
|
@user.effects[PBEffects::LockOnPos] == target.index
|
||||||
end
|
end
|
||||||
if skill >= PBTrainerAI.highSkill
|
if skill_check(AILevel.medium)
|
||||||
if move.function == "BadPoisonTarget" && # Toxic
|
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
|
modifiers[:base_accuracy] = 0
|
||||||
end
|
end
|
||||||
if ["OHKO", "OHKOIce", "OHKOHitsUndergroundTarget"].include?(move.function)
|
if ["OHKO", "OHKOIce", "OHKOHitsUndergroundTarget"].include?(move.function)
|
||||||
modifiers[:base_accuracy] = move.accuracy + user.level - target.level
|
modifiers[:base_accuracy] = move.accuracy + @user.level - target.level
|
||||||
modifiers[:accuracy_multiplier] = 0 if target.level > user.level
|
modifiers[:accuracy_multiplier] = 0 if target.level > @user.level
|
||||||
if skill >= PBTrainerAI.bestSkill && target.hasActiveAbility?(:STURDY)
|
if skill_check(AILevel.best) && target.hasActiveAbility?(:STURDY)
|
||||||
modifiers[:accuracy_multiplier] = 0
|
modifiers[:accuracy_multiplier] = 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|||||||
169
Data/Scripts/011_Battle/005_AI/009_AI_Roles.rb
Normal file
169
Data/Scripts/011_Battle/005_AI/009_AI_Roles.rb
Normal file
@@ -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
|
||||||
@@ -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
|
||||||
249
Data/Scripts/011_Battle/005_AI/051_AI_MoveHandlers_Misc.rb
Normal file
249
Data/Scripts/011_Battle/005_AI/051_AI_MoveHandlers_Misc.rb
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
)
|
||||||
1576
Data/Scripts/011_Battle/005_AI/052_AI_MoveHandlers_BattlerStats.rb
Normal file
1576
Data/Scripts/011_Battle/005_AI/052_AI_MoveHandlers_BattlerStats.rb
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -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
|
||||||
207
Data/Scripts/011_Battle/005_AI/055_AI_MoveHandlers_MultiHit.rb
Normal file
207
Data/Scripts/011_Battle/005_AI/055_AI_MoveHandlers_MultiHit.rb
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
)
|
||||||
343
Data/Scripts/011_Battle/005_AI/056_AI_MoveHandlers_Healing.rb
Normal file
343
Data/Scripts/011_Battle/005_AI/056_AI_MoveHandlers_Healing.rb
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
)
|
||||||
193
Data/Scripts/011_Battle/005_AI/057_AI_MoveHandlers_Items.rb
Normal file
193
Data/Scripts/011_Battle/005_AI/057_AI_MoveHandlers_Items.rb
Normal file
@@ -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?
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -183,9 +183,10 @@ class Battle::AI
|
|||||||
alias _battlePalace_pbEnemyShouldWithdraw? pbEnemyShouldWithdraw?
|
alias _battlePalace_pbEnemyShouldWithdraw? pbEnemyShouldWithdraw?
|
||||||
end
|
end
|
||||||
|
|
||||||
def pbEnemyShouldWithdraw?(idxBattler)
|
def pbEnemyShouldWithdraw?
|
||||||
return _battlePalace_pbEnemyShouldWithdraw?(idxBattler) if !@battlePalace
|
return _battlePalace_pbEnemyShouldWithdraw? if !@battlePalace
|
||||||
thispkmn = @battle.battlers[idxBattler]
|
thispkmn = @user
|
||||||
|
idxBattler = @user.index
|
||||||
shouldswitch = false
|
shouldswitch = false
|
||||||
if thispkmn.effects[PBEffects::PerishSong] == 1
|
if thispkmn.effects[PBEffects::PerishSong] == 1
|
||||||
shouldswitch = true
|
shouldswitch = true
|
||||||
|
|||||||
@@ -215,8 +215,8 @@ class Battle::AI
|
|||||||
alias _battleArena_pbEnemyShouldWithdraw? pbEnemyShouldWithdraw?
|
alias _battleArena_pbEnemyShouldWithdraw? pbEnemyShouldWithdraw?
|
||||||
end
|
end
|
||||||
|
|
||||||
def pbEnemyShouldWithdraw?(idxBattler)
|
def pbEnemyShouldWithdraw?
|
||||||
return _battleArena_pbEnemyShouldWithdraw?(idxBattler) if !@battleArena
|
return _battleArena_pbEnemyShouldWithdraw? if !@battleArena
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ class Trainer
|
|||||||
return _INTL("{1} {2}", trainer_type_name, @name)
|
return _INTL("{1} {2}", trainer_type_name, @name)
|
||||||
end
|
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
|
# Portion of the ID which is visible on the Trainer Card
|
||||||
|
|||||||
Reference in New Issue
Block a user