mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-06 06:01:46 +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
|
||||
|
||||
#===============================================================================
|
||||
# Unused.
|
||||
# A class that stores code that can be triggered. Each piece of code has an
|
||||
# associated ID, which can be anything that can be used as a key in a hash.
|
||||
#===============================================================================
|
||||
class HandlerHash
|
||||
def initialize(mod)
|
||||
@mod = mod
|
||||
@hash = {}
|
||||
@addIfs = []
|
||||
@symbolCache = {}
|
||||
def initialize
|
||||
@hash = {}
|
||||
@add_ifs = []
|
||||
end
|
||||
|
||||
def fromSymbol(sym)
|
||||
return sym unless sym.is_a?(Symbol) || sym.is_a?(String)
|
||||
mod = Object.const_get(@mod) rescue nil
|
||||
return nil if !mod
|
||||
return mod.const_get(sym.to_sym) rescue nil
|
||||
end
|
||||
|
||||
def toSymbol(sym)
|
||||
return sym.to_sym if sym.is_a?(Symbol) || sym.is_a?(String)
|
||||
ret = @symbolCache[sym]
|
||||
return ret if ret
|
||||
mod = Object.const_get(@mod) rescue nil
|
||||
return nil if !mod
|
||||
mod.constants.each do |key|
|
||||
next if mod.const_get(key) != sym
|
||||
ret = key.to_sym
|
||||
@symbolCache[sym] = ret
|
||||
break
|
||||
def [](id)
|
||||
return @hash[id] if id && @hash[id]
|
||||
@add_ifs.each do |add_if|
|
||||
return add_if[2] if add_if[1].call(id)
|
||||
end
|
||||
return ret
|
||||
return nil
|
||||
end
|
||||
|
||||
def addIf(conditionProc, handler = nil, &handlerBlock)
|
||||
def add(id, handler = nil, &handlerBlock)
|
||||
if ![Proc, Hash].include?(handler.class) && !block_given?
|
||||
raise ArgumentError, "addIf call for #{self.class.name} has no valid handler (#{handler.inspect} was given)"
|
||||
raise ArgumentError, "#{self.class.name} for #{id.inspect} has no valid handler (#{handler.inspect} was given)"
|
||||
end
|
||||
@addIfs.push([conditionProc, handler || handlerBlock])
|
||||
@hash[id] = handler || handlerBlock if id && !id.empty?
|
||||
end
|
||||
|
||||
def add(sym, handler = nil, &handlerBlock) # 'sym' can be an ID or symbol
|
||||
def add_if(id, conditionProc, handler = nil, &handlerBlock)
|
||||
if ![Proc, Hash].include?(handler.class) && !block_given?
|
||||
raise ArgumentError, "#{self.class.name} for #{sym.inspect} has no valid handler (#{handler.inspect} was given)"
|
||||
raise ArgumentError, "add_if call for #{self.class.name} has no valid handler (#{handler.inspect} was given)"
|
||||
end
|
||||
id = fromSymbol(sym)
|
||||
@hash[id] = handler || handlerBlock if id
|
||||
symbol = toSymbol(sym)
|
||||
@hash[symbol] = handler || handlerBlock if symbol
|
||||
@add_ifs.push([id, conditionProc, handler || handlerBlock])
|
||||
end
|
||||
|
||||
def copy(src, *dests)
|
||||
handler = self[src]
|
||||
if handler
|
||||
dests.each do |dest|
|
||||
self.add(dest, handler)
|
||||
end
|
||||
end
|
||||
return if !handler
|
||||
dests.each { |dest| add(dest, handler) }
|
||||
end
|
||||
|
||||
def [](sym) # 'sym' can be an ID or symbol
|
||||
id = fromSymbol(sym)
|
||||
ret = nil
|
||||
ret = @hash[id] if id && @hash[id] # Real ID from the item
|
||||
symbol = toSymbol(sym)
|
||||
ret = @hash[symbol] if symbol && @hash[symbol] # Symbol or string
|
||||
unless ret
|
||||
@addIfs.each do |addif|
|
||||
return addif[1] if addif[0].call(id)
|
||||
end
|
||||
def remove(key)
|
||||
if @hash.keys.include?(key)
|
||||
@hash.delete(key)
|
||||
else
|
||||
@add_ifs.delete_if { |add_if| add_if[0] == key }
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def trigger(sym, *args)
|
||||
handler = self[sym]
|
||||
return (handler) ? handler.call(fromSymbol(sym), *args) : nil
|
||||
end
|
||||
|
||||
def clear
|
||||
@hash.clear
|
||||
@add_ifs.clear
|
||||
end
|
||||
|
||||
def each
|
||||
@hash.each_pair { |key, value| yield key, value }
|
||||
end
|
||||
|
||||
def keys
|
||||
return @hash.keys.clone
|
||||
end
|
||||
|
||||
# NOTE: The call does not pass id as a parameter to the proc/block.
|
||||
def trigger(id, *args)
|
||||
handler = self[id]
|
||||
return handler&.call(*args)
|
||||
end
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
# A stripped-down version of class HandlerHash which only deals with symbols and
|
||||
# doesn't care about whether those symbols are defined as constants in a class
|
||||
# or module.
|
||||
# A stripped-down version of class HandlerHash which only deals with IDs that
|
||||
# are symbols.
|
||||
#===============================================================================
|
||||
class HandlerHash2
|
||||
class HandlerHashSymbol
|
||||
def initialize
|
||||
@hash = {}
|
||||
@add_ifs = []
|
||||
@@ -219,6 +201,7 @@ class HandlerHash2
|
||||
|
||||
def clear
|
||||
@hash.clear
|
||||
@add_ifs.clear
|
||||
end
|
||||
|
||||
def trigger(sym, *args)
|
||||
@@ -229,32 +212,63 @@ class HandlerHash2
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
# An even more stripped down version of class HandlerHash which just takes
|
||||
# hashes with keys, no matter what the keys are.
|
||||
# A specialised version of class HandlerHash which only deals with IDs that are
|
||||
# constants in a particular class or module. That class or module must be
|
||||
# defined when creating an instance of this class.
|
||||
# Unused.
|
||||
#===============================================================================
|
||||
class HandlerHashBasic
|
||||
def initialize
|
||||
@hash = {}
|
||||
@addIfs = []
|
||||
class HandlerHashEnum
|
||||
def initialize(mod)
|
||||
@mod = mod
|
||||
@hash = {}
|
||||
@addIfs = []
|
||||
@symbolCache = {}
|
||||
end
|
||||
|
||||
def [](entry)
|
||||
def [](sym) # 'sym' can be an ID or symbol
|
||||
id = fromSymbol(sym)
|
||||
ret = nil
|
||||
ret = @hash[entry] if entry && @hash[entry]
|
||||
ret = @hash[id] if id && @hash[id] # Real ID from the item
|
||||
symbol = toSymbol(sym)
|
||||
ret = @hash[symbol] if symbol && @hash[symbol] # Symbol or string
|
||||
unless ret
|
||||
@addIfs.each do |addif|
|
||||
return addif[1] if addif[0].call(entry)
|
||||
return addif[1] if addif[0].call(id)
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def add(entry, handler = nil, &handlerBlock)
|
||||
if ![Proc, Hash].include?(handler.class) && !block_given?
|
||||
raise ArgumentError, "#{self.class.name} for #{entry.inspect} has no valid handler (#{handler.inspect} was given)"
|
||||
def fromSymbol(sym)
|
||||
return sym unless sym.is_a?(Symbol) || sym.is_a?(String)
|
||||
mod = Object.const_get(@mod) rescue nil
|
||||
return nil if !mod
|
||||
return mod.const_get(sym.to_sym) rescue nil
|
||||
end
|
||||
|
||||
def toSymbol(sym)
|
||||
return sym.to_sym if sym.is_a?(Symbol) || sym.is_a?(String)
|
||||
ret = @symbolCache[sym]
|
||||
return ret if ret
|
||||
mod = Object.const_get(@mod) rescue nil
|
||||
return nil if !mod
|
||||
mod.constants.each do |key|
|
||||
next if mod.const_get(key) != sym
|
||||
ret = key.to_sym
|
||||
@symbolCache[sym] = ret
|
||||
break
|
||||
end
|
||||
return if !entry || entry.empty?
|
||||
@hash[entry] = handler || handlerBlock
|
||||
return ret
|
||||
end
|
||||
|
||||
def add(sym, handler = nil, &handlerBlock) # 'sym' can be an ID or symbol
|
||||
if ![Proc, Hash].include?(handler.class) && !block_given?
|
||||
raise ArgumentError, "#{self.class.name} for #{sym.inspect} has no valid handler (#{handler.inspect} was given)"
|
||||
end
|
||||
id = fromSymbol(sym)
|
||||
@hash[id] = handler || handlerBlock if id
|
||||
symbol = toSymbol(sym)
|
||||
@hash[symbol] = handler || handlerBlock if symbol
|
||||
end
|
||||
|
||||
def addIf(conditionProc, handler = nil, &handlerBlock)
|
||||
@@ -267,42 +281,31 @@ class HandlerHashBasic
|
||||
def copy(src, *dests)
|
||||
handler = self[src]
|
||||
return if !handler
|
||||
dests.each { |dest| add(dest, handler) }
|
||||
end
|
||||
|
||||
def remove(key)
|
||||
@hash.delete(key)
|
||||
dests.each { |dest| self.add(dest, handler) }
|
||||
end
|
||||
|
||||
def clear
|
||||
@hash.clear
|
||||
@addIfs.clear
|
||||
end
|
||||
|
||||
def each
|
||||
@hash.each_pair { |key, value| yield key, value }
|
||||
end
|
||||
|
||||
def keys
|
||||
return @hash.keys.clone
|
||||
end
|
||||
|
||||
def trigger(entry, *args)
|
||||
handler = self[entry]
|
||||
return handler&.call(*args)
|
||||
def trigger(sym, *args)
|
||||
handler = self[sym]
|
||||
return (handler) ? handler.call(fromSymbol(sym), *args) : nil
|
||||
end
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
class SpeciesHandlerHash < HandlerHash2
|
||||
class SpeciesHandlerHash < HandlerHashSymbol
|
||||
end
|
||||
|
||||
class AbilityHandlerHash < HandlerHash2
|
||||
class AbilityHandlerHash < HandlerHashSymbol
|
||||
end
|
||||
|
||||
class ItemHandlerHash < HandlerHash2
|
||||
class ItemHandlerHash < HandlerHashSymbol
|
||||
end
|
||||
|
||||
class MoveHandlerHash < HandlerHash2
|
||||
class MoveHandlerHash < HandlerHashSymbol
|
||||
end
|
||||
|
||||
@@ -81,7 +81,7 @@ module MenuHandlers
|
||||
@@handlers = {}
|
||||
|
||||
def self.add(menu, option, hash)
|
||||
@@handlers[menu] = HandlerHashBasic.new if !@@handlers.has_key?(menu)
|
||||
@@handlers[menu] = HandlerHash.new if !@@handlers.has_key?(menu)
|
||||
@@handlers[menu].add(option, hash)
|
||||
end
|
||||
|
||||
|
||||
@@ -1,25 +1,34 @@
|
||||
# AI skill levels:
|
||||
# 0: Wild Pokémon
|
||||
# 1-31: Basic trainer (young/inexperienced)
|
||||
# 32-47: Some skill
|
||||
# 48-99: High skill
|
||||
# 100+: Best trainers (Gym Leaders, Elite Four, Champion)
|
||||
# NOTE: A trainer's skill value can range from 0-255, but by default only four
|
||||
# distinct skill levels exist. The skill value is typically the same as
|
||||
# the trainer's base money value.
|
||||
module PBTrainerAI
|
||||
# Minimum skill level to be in each AI category.
|
||||
def self.minimumSkill; return 1; end
|
||||
def self.mediumSkill; return 32; end
|
||||
def self.highSkill; return 48; end
|
||||
def self.bestSkill; return 100; end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
class Battle::AI
|
||||
# AI skill levels:
|
||||
# 0: Wild Pokémon
|
||||
# 1-31: Basic trainer (young/inexperienced)
|
||||
# 32-47: Some skill
|
||||
# 48-99: High skill
|
||||
# 100+: Best trainers (Gym Leaders, Elite Four, Champion)
|
||||
# NOTE: A trainer's skill value can range from 0-255, but by default only four
|
||||
# distinct skill levels exist. The skill value is typically the same as
|
||||
# the trainer's base money value.
|
||||
module AILevel
|
||||
# Minimum skill level to be in each AI skill bracket.
|
||||
def self.minimum; return 1; end
|
||||
def self.medium; return 32; end
|
||||
def self.high; return 48; end
|
||||
def self.best; return 100; end
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
#
|
||||
#=============================================================================
|
||||
def initialize(battle)
|
||||
@battle = battle
|
||||
@battle = battle
|
||||
@skill = 0
|
||||
@user = nil
|
||||
@wildBattler = @battle.wildBattle? # Whether AI is choosing for a wild Pokémon
|
||||
@roles = [Array.new(@battle.pbParty(0).length) { |i| determine_roles(0, i) },
|
||||
Array.new(@battle.pbParty(1).length) { |i| determine_roles(1, i) }]
|
||||
end
|
||||
|
||||
def pbAIRandom(x); return rand(x); end
|
||||
@@ -44,26 +53,66 @@ class Battle::AI
|
||||
return Math.sqrt(varianceTimesN / n)
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Decide whether the opponent should Mega Evolve their Pokémon
|
||||
#=============================================================================
|
||||
def pbEnemyShouldMegaEvolve?(idxBattler)
|
||||
battler = @battle.battlers[idxBattler]
|
||||
if @battle.pbCanMegaEvolve?(idxBattler) # Simple "always should if possible"
|
||||
PBDebug.log("[AI] #{battler.pbThis} (#{idxBattler}) will Mega Evolve")
|
||||
# Decide whether the opponent should Mega Evolve their Pokémon.
|
||||
def pbEnemyShouldMegaEvolve?
|
||||
if @battle.pbCanMegaEvolve?(@user.index) # Simple "always should if possible"
|
||||
PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) will Mega Evolve")
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Choose an action
|
||||
#=============================================================================
|
||||
# Choose an action.
|
||||
def pbDefaultChooseEnemyCommand(idxBattler)
|
||||
return if pbEnemyShouldUseItem?(idxBattler)
|
||||
return if pbEnemyShouldWithdraw?(idxBattler)
|
||||
set_up(idxBattler)
|
||||
choices = pbGetMoveScores
|
||||
return if pbEnemyShouldUseItem?
|
||||
return if pbEnemyShouldWithdraw?
|
||||
return if @battle.pbAutoFightMenu(idxBattler)
|
||||
@battle.pbRegisterMegaEvolution(idxBattler) if pbEnemyShouldMegaEvolve?(idxBattler)
|
||||
pbChooseMoves(idxBattler)
|
||||
@battle.pbRegisterMegaEvolution(idxBattler) if pbEnemyShouldMegaEvolve?
|
||||
pbChooseMove(choices)
|
||||
end
|
||||
|
||||
# Set some class variables for the Pokémon whose action is being chosen
|
||||
def set_up(idxBattler)
|
||||
# TODO: Where relevant, pretend the user is Mega Evolved if it isn't but can
|
||||
# be.
|
||||
@user = @battle.battlers[idxBattler]
|
||||
@wildBattler = (@battle.wildBattle? && @user.opposes?)
|
||||
@skill = 0
|
||||
if !@wildBattler
|
||||
@skill = @battle.pbGetOwnerFromBattlerIndex(@user.index).skill_level || 0
|
||||
@skill = AILevel.minimum if @skill < AILevel.minimum
|
||||
end
|
||||
end
|
||||
|
||||
def skill_check(threshold)
|
||||
return @skill >= threshold
|
||||
end
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
module Battle::AI::Handlers
|
||||
MoveEffectScore = HandlerHash.new
|
||||
MoveBasePower = HandlerHash.new
|
||||
# Move type
|
||||
# Move accuracy
|
||||
# Move target
|
||||
# Move additional effect chance
|
||||
# Move unselectable check
|
||||
# Move failure check
|
||||
|
||||
def self.apply_move_effect_score(function_code, score, *args)
|
||||
function_code = function_code.to_sym
|
||||
ret = MoveEffectScore.trigger(function_code, score, *args)
|
||||
return (ret.nil?) ? score : ret
|
||||
end
|
||||
|
||||
def self.get_base_power(function_code, power, *args)
|
||||
function_code = function_code.to_sym
|
||||
ret = MoveBasePower.trigger(function_code, *args)
|
||||
return (ret.nil?) ? power : ret
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,9 +2,8 @@ class Battle::AI
|
||||
#=============================================================================
|
||||
# Decide whether the opponent should use an item on the Pokémon
|
||||
#=============================================================================
|
||||
def pbEnemyShouldUseItem?(idxBattler)
|
||||
user = @battle.battlers[idxBattler]
|
||||
item, idxTarget = pbEnemyItemToUse(idxBattler)
|
||||
def pbEnemyShouldUseItem?
|
||||
item, idxTarget = pbEnemyItemToUse
|
||||
return false if !item
|
||||
# Determine target of item (always the Pokémon choosing the action)
|
||||
useType = GameData::Item.get(item).battle_use
|
||||
@@ -12,19 +11,19 @@ class Battle::AI
|
||||
idxTarget = @battle.battlers[idxTarget].pokemonIndex # Party Pokémon
|
||||
end
|
||||
# Register use of item
|
||||
@battle.pbRegisterItem(idxBattler, item, idxTarget)
|
||||
PBDebug.log("[AI] #{user.pbThis} (#{user.index}) will use item #{GameData::Item.get(item).name}")
|
||||
@battle.pbRegisterItem(@user.index, item, idxTarget)
|
||||
PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) will use item #{GameData::Item.get(item).name}")
|
||||
return true
|
||||
end
|
||||
|
||||
# NOTE: The AI will only consider using an item on the Pokémon it's currently
|
||||
# choosing an action for.
|
||||
def pbEnemyItemToUse(idxBattler)
|
||||
def pbEnemyItemToUse
|
||||
return nil if !@battle.internalBattle
|
||||
items = @battle.pbGetOwnerItems(idxBattler)
|
||||
items = @battle.pbGetOwnerItems(@user.index)
|
||||
return nil if !items || items.length == 0
|
||||
# Determine target of item (always the Pokémon choosing the action)
|
||||
idxTarget = idxBattler # Battler using the item
|
||||
idxTarget = @user.index # Battler using the item
|
||||
battler = @battle.battlers[idxTarget]
|
||||
pkmn = battler.pokemon
|
||||
# Item categories
|
||||
|
||||
@@ -2,26 +2,24 @@ class Battle::AI
|
||||
#=============================================================================
|
||||
# Decide whether the opponent should switch Pokémon
|
||||
#=============================================================================
|
||||
def pbEnemyShouldWithdraw?(idxBattler)
|
||||
return pbEnemyShouldWithdrawEx?(idxBattler, false)
|
||||
def pbEnemyShouldWithdraw?
|
||||
return pbEnemyShouldWithdrawEx?(false)
|
||||
end
|
||||
|
||||
def pbEnemyShouldWithdrawEx?(idxBattler, forceSwitch)
|
||||
return false if @battle.wildBattle?
|
||||
def pbEnemyShouldWithdrawEx?(forceSwitch)
|
||||
return false if @wildBattler
|
||||
shouldSwitch = forceSwitch
|
||||
batonPass = -1
|
||||
moveType = nil
|
||||
skill = @battle.pbGetOwnerFromBattlerIndex(idxBattler).skill_level || 0
|
||||
battler = @battle.battlers[idxBattler]
|
||||
# If Pokémon is within 6 levels of the foe, and foe's last move was
|
||||
# super-effective and powerful
|
||||
if !shouldSwitch && battler.turnCount > 0 && skill >= PBTrainerAI.highSkill
|
||||
target = battler.pbDirectOpposing(true)
|
||||
if !shouldSwitch && @user.turnCount > 0 && skill_check(AILevel.high)
|
||||
target = @user.pbDirectOpposing(true)
|
||||
if !target.fainted? && target.lastMoveUsed &&
|
||||
(target.level - battler.level).abs <= 6
|
||||
(target.level - @user.level).abs <= 6
|
||||
moveData = GameData::Move.get(target.lastMoveUsed)
|
||||
moveType = moveData.type
|
||||
typeMod = pbCalcTypeMod(moveType, target, battler)
|
||||
typeMod = pbCalcTypeMod(moveType, target, @user)
|
||||
if Effectiveness.super_effective?(typeMod) && moveData.base_damage > 50
|
||||
switchChance = (moveData.base_damage > 70) ? 30 : 20
|
||||
shouldSwitch = (pbAIRandom(100) < switchChance)
|
||||
@@ -29,77 +27,76 @@ class Battle::AI
|
||||
end
|
||||
end
|
||||
# Pokémon can't do anything (must have been in battle for at least 5 rounds)
|
||||
if !@battle.pbCanChooseAnyMove?(idxBattler) &&
|
||||
battler.turnCount && battler.turnCount >= 5
|
||||
if !@battle.pbCanChooseAnyMove?(@user.index) &&
|
||||
@user.turnCount && @user.turnCount >= 5
|
||||
shouldSwitch = true
|
||||
end
|
||||
# Pokémon is Perish Songed and has Baton Pass
|
||||
if skill >= PBTrainerAI.highSkill && battler.effects[PBEffects::PerishSong] == 1
|
||||
battler.eachMoveWithIndex do |m, i|
|
||||
if skill_check(AILevel.high) && @user.effects[PBEffects::PerishSong] == 1
|
||||
@user.eachMoveWithIndex do |m, i|
|
||||
next if m.function != "SwitchOutUserPassOnEffects" # Baton Pass
|
||||
next if !@battle.pbCanChooseMove?(idxBattler, i, false)
|
||||
next if !@battle.pbCanChooseMove?(@user.index, i, false)
|
||||
batonPass = i
|
||||
break
|
||||
end
|
||||
end
|
||||
# Pokémon will faint because of bad poisoning at the end of this round, but
|
||||
# would survive at least one more round if it were regular poisoning instead
|
||||
if battler.status == :POISON && battler.statusCount > 0 &&
|
||||
skill >= PBTrainerAI.highSkill
|
||||
toxicHP = battler.totalhp / 16
|
||||
nextToxicHP = toxicHP * (battler.effects[PBEffects::Toxic] + 1)
|
||||
if battler.hp <= nextToxicHP && battler.hp > toxicHP * 2 && pbAIRandom(100) < 80
|
||||
shouldSwitch = true
|
||||
if @user.status == :POISON && @user.statusCount > 0 && skill_check(AILevel.high)
|
||||
toxicHP = @user.totalhp / 16
|
||||
nextToxicHP = toxicHP * (@user.effects[PBEffects::Toxic] + 1)
|
||||
if @user.hp <= nextToxicHP && @user.hp > toxicHP * 2
|
||||
shouldSwitch = true if pbAIRandom(100) < 80
|
||||
end
|
||||
end
|
||||
# Pokémon is Encored into an unfavourable move
|
||||
if battler.effects[PBEffects::Encore] > 0 && skill >= PBTrainerAI.mediumSkill
|
||||
idxEncoredMove = battler.pbEncoredMoveIndex
|
||||
if @user.effects[PBEffects::Encore] > 0 && skill_check(AILevel.medium)
|
||||
idxEncoredMove = @user.pbEncoredMoveIndex
|
||||
if idxEncoredMove >= 0
|
||||
scoreSum = 0
|
||||
scoreCount = 0
|
||||
battler.allOpposing.each do |b|
|
||||
scoreSum += pbGetMoveScore(battler.moves[idxEncoredMove], battler, b, skill)
|
||||
@user.allOpposing.each do |b|
|
||||
scoreSum += pbGetMoveScore(@user.moves[idxEncoredMove], b)
|
||||
scoreCount += 1
|
||||
end
|
||||
if scoreCount > 0 && scoreSum / scoreCount <= 20 && pbAIRandom(100) < 80
|
||||
shouldSwitch = true
|
||||
if scoreCount > 0 && scoreSum / scoreCount <= 20
|
||||
shouldSwitch = true if pbAIRandom(100) < 80
|
||||
end
|
||||
end
|
||||
end
|
||||
# If there is a single foe and it is resting after Hyper Beam or is
|
||||
# Truanting (i.e. free turn)
|
||||
if @battle.pbSideSize(battler.index + 1) == 1 &&
|
||||
!battler.pbDirectOpposing.fainted? && skill >= PBTrainerAI.highSkill
|
||||
opp = battler.pbDirectOpposing
|
||||
if @battle.pbSideSize(@user.index + 1) == 1 &&
|
||||
!@user.pbDirectOpposing.fainted? && skill_check(AILevel.high)
|
||||
opp = @user.pbDirectOpposing
|
||||
if (opp.effects[PBEffects::HyperBeam] > 0 ||
|
||||
(opp.hasActiveAbility?(:TRUANT) && opp.effects[PBEffects::Truant])) && pbAIRandom(100) < 80
|
||||
shouldSwitch = false
|
||||
(opp.hasActiveAbility?(:TRUANT) && opp.effects[PBEffects::Truant]))
|
||||
shouldSwitch = false if pbAIRandom(100) < 80
|
||||
end
|
||||
end
|
||||
# Sudden Death rule - I'm not sure what this means
|
||||
if @battle.rules["suddendeath"] && battler.turnCount > 0
|
||||
if battler.hp <= battler.totalhp / 4 && pbAIRandom(100) < 30
|
||||
if @battle.rules["suddendeath"] && @user.turnCount > 0
|
||||
if @user.hp <= @user.totalhp / 4 && pbAIRandom(100) < 30
|
||||
shouldSwitch = true
|
||||
elsif battler.hp <= battler.totalhp / 2 && pbAIRandom(100) < 80
|
||||
elsif @user.hp <= @user.totalhp / 2 && pbAIRandom(100) < 80
|
||||
shouldSwitch = true
|
||||
end
|
||||
end
|
||||
# Pokémon is about to faint because of Perish Song
|
||||
if battler.effects[PBEffects::PerishSong] == 1
|
||||
if @user.effects[PBEffects::PerishSong] == 1
|
||||
shouldSwitch = true
|
||||
end
|
||||
if shouldSwitch
|
||||
list = []
|
||||
idxPartyStart, idxPartyEnd = @battle.pbTeamIndexRangeFromBattlerIndex(idxBattler)
|
||||
@battle.pbParty(idxBattler).each_with_index do |pkmn, i|
|
||||
idxPartyStart, idxPartyEnd = @battle.pbTeamIndexRangeFromBattlerIndex(@user.index)
|
||||
@battle.pbParty(@user.index).each_with_index do |pkmn, i|
|
||||
next if i == idxPartyEnd - 1 # Don't choose to switch in ace
|
||||
next if !@battle.pbCanSwitch?(idxBattler, i)
|
||||
next if !@battle.pbCanSwitch?(@user.index, i)
|
||||
# If perish count is 1, it may be worth it to switch
|
||||
# even with Spikes, since Perish Song's effect will end
|
||||
if battler.effects[PBEffects::PerishSong] != 1
|
||||
if @user.effects[PBEffects::PerishSong] != 1
|
||||
# Will contain effects that recommend against switching
|
||||
spikes = battler.pbOwnSide.effects[PBEffects::Spikes]
|
||||
spikes = @user.pbOwnSide.effects[PBEffects::Spikes]
|
||||
# Don't switch to this if too little HP
|
||||
if spikes > 0
|
||||
spikesDmg = [8, 6, 4][spikes - 1]
|
||||
@@ -108,17 +105,17 @@ class Battle::AI
|
||||
end
|
||||
end
|
||||
# moveType is the type of the target's last used move
|
||||
if moveType && Effectiveness.ineffective?(pbCalcTypeMod(moveType, battler, battler))
|
||||
if moveType && Effectiveness.ineffective?(pbCalcTypeMod(moveType, @user, @user))
|
||||
weight = 65
|
||||
typeMod = pbCalcTypeModPokemon(pkmn, battler.pbDirectOpposing(true))
|
||||
typeMod = pbCalcTypeModPokemon(pkmn, @user.pbDirectOpposing(true))
|
||||
if Effectiveness.super_effective?(typeMod)
|
||||
# Greater weight if new Pokemon's type is effective against target
|
||||
weight = 85
|
||||
end
|
||||
list.unshift(i) if pbAIRandom(100) < weight # Put this Pokemon first
|
||||
elsif moveType && Effectiveness.resistant?(pbCalcTypeMod(moveType, battler, battler))
|
||||
elsif moveType && Effectiveness.resistant?(pbCalcTypeMod(moveType, @user, @user))
|
||||
weight = 40
|
||||
typeMod = pbCalcTypeModPokemon(pkmn, battler.pbDirectOpposing(true))
|
||||
typeMod = pbCalcTypeModPokemon(pkmn, @user.pbDirectOpposing(true))
|
||||
if Effectiveness.super_effective?(typeMod)
|
||||
# Greater weight if new Pokemon's type is effective against target
|
||||
weight = 60
|
||||
@@ -129,13 +126,13 @@ class Battle::AI
|
||||
end
|
||||
end
|
||||
if list.length > 0
|
||||
if batonPass >= 0 && @battle.pbRegisterMove(idxBattler, batonPass, false)
|
||||
PBDebug.log("[AI] #{battler.pbThis} (#{idxBattler}) will use Baton Pass to avoid Perish Song")
|
||||
if batonPass >= 0 && @battle.pbRegisterMove(@user.index, batonPass, false)
|
||||
PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) will use Baton Pass to avoid Perish Song")
|
||||
return true
|
||||
end
|
||||
if @battle.pbRegisterSwitch(idxBattler, list[0])
|
||||
PBDebug.log("[AI] #{battler.pbThis} (#{idxBattler}) will switch with " +
|
||||
@battle.pbParty(idxBattler)[list[0]].name)
|
||||
if @battle.pbRegisterSwitch(@user.index, list[0])
|
||||
PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) will switch with " +
|
||||
@battle.pbParty(@user.index)[list[0]].name)
|
||||
return true
|
||||
end
|
||||
end
|
||||
@@ -143,10 +140,10 @@ class Battle::AI
|
||||
return false
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Choose a replacement Pokémon
|
||||
#=============================================================================
|
||||
# Choose a replacement Pokémon (called directly from @battle, not part of
|
||||
# action choosing).
|
||||
def pbDefaultChooseNewEnemy(idxBattler, party)
|
||||
set_up(idxBattler)
|
||||
enemies = []
|
||||
idxPartyStart, idxPartyEnd = @battle.pbTeamIndexRangeFromBattlerIndex(idxBattler)
|
||||
party.each_with_index do |_p, i|
|
||||
|
||||
@@ -3,25 +3,7 @@ class Battle::AI
|
||||
# Main move-choosing method (moves with higher scores are more likely to be
|
||||
# chosen)
|
||||
#=============================================================================
|
||||
def pbChooseMoves(idxBattler)
|
||||
user = @battle.battlers[idxBattler]
|
||||
wildBattler = user.wild?
|
||||
skill = 0
|
||||
if !wildBattler
|
||||
skill = @battle.pbGetOwnerFromBattlerIndex(user.index).skill_level || 0
|
||||
end
|
||||
# Get scores and targets for each move
|
||||
# NOTE: A move is only added to the choices array if it has a non-zero
|
||||
# score.
|
||||
choices = []
|
||||
user.eachMoveWithIndex do |_m, i|
|
||||
next if !@battle.pbCanChooseMove?(idxBattler, i, false)
|
||||
if wildBattler
|
||||
pbRegisterMoveWild(user, i, choices)
|
||||
else
|
||||
pbRegisterMoveTrainer(user, i, choices, skill)
|
||||
end
|
||||
end
|
||||
def pbChooseMove(choices)
|
||||
# Figure out useful information about the choices
|
||||
totalScore = 0
|
||||
maxScore = 0
|
||||
@@ -29,18 +11,9 @@ class Battle::AI
|
||||
totalScore += c[1]
|
||||
maxScore = c[1] if maxScore < c[1]
|
||||
end
|
||||
# Log the available choices
|
||||
if $INTERNAL
|
||||
logMsg = "[AI] Move choices for #{user.pbThis(true)} (#{user.index}): "
|
||||
choices.each_with_index do |c, i|
|
||||
logMsg += "#{user.moves[c[0]].name}=#{c[1]}"
|
||||
logMsg += " (target #{c[2]})" if c[2] >= 0
|
||||
logMsg += ", " if i < choices.length - 1
|
||||
end
|
||||
PBDebug.log(logMsg)
|
||||
end
|
||||
|
||||
# Find any preferred moves and just choose from them
|
||||
if !wildBattler && skill >= PBTrainerAI.highSkill && maxScore > 100
|
||||
if skill_check(AILevel.high) && maxScore > 100
|
||||
stDev = pbStdDev(choices)
|
||||
if stDev >= 40 && pbAIRandom(100) < 90
|
||||
preferredMoves = []
|
||||
@@ -51,97 +24,141 @@ class Battle::AI
|
||||
end
|
||||
if preferredMoves.length > 0
|
||||
m = preferredMoves[pbAIRandom(preferredMoves.length)]
|
||||
PBDebug.log("[AI] #{user.pbThis} (#{user.index}) prefers #{user.moves[m[0]].name}")
|
||||
@battle.pbRegisterMove(idxBattler, m[0], false)
|
||||
@battle.pbRegisterTarget(idxBattler, m[2]) if m[2] >= 0
|
||||
PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) prefers #{@user.moves[m[0]].name}")
|
||||
@battle.pbRegisterMove(@user.index, m[0], false)
|
||||
@battle.pbRegisterTarget(@user.index, m[2]) if m[2] >= 0
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Decide whether all choices are bad, and if so, try switching instead
|
||||
if !wildBattler && skill >= PBTrainerAI.highSkill
|
||||
if !@wildBattler && skill_check(AILevel.high)
|
||||
badMoves = false
|
||||
if ((maxScore <= 20 && user.turnCount > 2) ||
|
||||
(maxScore <= 40 && user.turnCount > 5)) && pbAIRandom(100) < 80
|
||||
badMoves = true
|
||||
if (maxScore <= 20 && @user.turnCount > 2) ||
|
||||
(maxScore <= 40 && @user.turnCount > 5)
|
||||
badMoves = true if pbAIRandom(100) < 80
|
||||
end
|
||||
if !badMoves && totalScore < 100 && user.turnCount > 1
|
||||
if !badMoves && totalScore < 100 && @user.turnCount > 1
|
||||
badMoves = true
|
||||
choices.each do |c|
|
||||
next if !user.moves[c[0]].damagingMove?
|
||||
next if !@user.moves[c[0]].damagingMove?
|
||||
badMoves = false
|
||||
break
|
||||
end
|
||||
badMoves = false if badMoves && pbAIRandom(100) < 10
|
||||
end
|
||||
if badMoves && pbEnemyShouldWithdrawEx?(idxBattler, true)
|
||||
if badMoves && pbEnemyShouldWithdrawEx?(true)
|
||||
if $INTERNAL
|
||||
PBDebug.log("[AI] #{user.pbThis} (#{user.index}) will switch due to terrible moves")
|
||||
PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) will switch due to terrible moves")
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
# If there are no calculated choices, pick one at random
|
||||
if choices.length == 0
|
||||
PBDebug.log("[AI] #{user.pbThis} (#{user.index}) doesn't want to use any moves; picking one at random")
|
||||
user.eachMoveWithIndex do |_m, i|
|
||||
next if !@battle.pbCanChooseMove?(idxBattler, i, false)
|
||||
PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) doesn't want to use any moves; picking one at random")
|
||||
@user.eachMoveWithIndex do |_m, i|
|
||||
next if !@battle.pbCanChooseMove?(@user.index, i, false)
|
||||
choices.push([i, 100, -1]) # Move index, score, target
|
||||
end
|
||||
if choices.length == 0 # No moves are physically possible to use; use Struggle
|
||||
@battle.pbAutoChooseMove(user.index)
|
||||
@battle.pbAutoChooseMove(@user.index)
|
||||
end
|
||||
end
|
||||
|
||||
# Randomly choose a move from the choices and register it
|
||||
randNum = pbAIRandom(totalScore)
|
||||
choices.each do |c|
|
||||
randNum -= c[1]
|
||||
next if randNum >= 0
|
||||
@battle.pbRegisterMove(idxBattler, c[0], false)
|
||||
@battle.pbRegisterTarget(idxBattler, c[2]) if c[2] >= 0
|
||||
@battle.pbRegisterMove(@user.index, c[0], false)
|
||||
@battle.pbRegisterTarget(@user.index, c[2]) if c[2] >= 0
|
||||
break
|
||||
end
|
||||
# Log the result
|
||||
if @battle.choices[idxBattler][2]
|
||||
PBDebug.log("[AI] #{user.pbThis} (#{user.index}) will use #{@battle.choices[idxBattler][2].name}")
|
||||
if @battle.choices[@user.index][2]
|
||||
PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) will use #{@battle.choices[@user.index][2].name}")
|
||||
end
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Get scores for the user's moves (done before any action is assessed)
|
||||
# NOTE: A move is only added to the choices array if it has a non-zero score.
|
||||
#=============================================================================
|
||||
def pbGetMoveScores
|
||||
# Get scores and targets for each move
|
||||
choices = []
|
||||
# TODO: Split this into two, the first part being the calculation of all
|
||||
# predicted damages and the second part being the score calculations
|
||||
# (which are based on the predicted damages). Note that this requires
|
||||
# saving each of the scoresAndTargets entries in here rather than in
|
||||
# def pbRegisterMoveTrainer, and only at the very end are they
|
||||
# whittled down to one per move which are chosen from. Multi-target
|
||||
# moves could be fiddly since damages should be calculated for each
|
||||
# target but they're all related.
|
||||
@user.eachMoveWithIndex do |_m, i|
|
||||
next if !@battle.pbCanChooseMove?(@user.index, i, false)
|
||||
if @wildBattler
|
||||
pbRegisterMoveWild(i, choices)
|
||||
else
|
||||
pbRegisterMoveTrainer(i, choices)
|
||||
end
|
||||
end
|
||||
# Log the available choices
|
||||
if $INTERNAL
|
||||
logMsg = "[AI] Move choices for #{@user.pbThis(true)} (#{@user.index}): "
|
||||
choices.each_with_index do |c, i|
|
||||
logMsg += "#{@user.moves[c[0]].name}=#{c[1]}"
|
||||
logMsg += " (target #{c[2]})" if c[2] >= 0
|
||||
logMsg += ", " if i < choices.length-1
|
||||
end
|
||||
PBDebug.log(logMsg)
|
||||
end
|
||||
return choices
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Get scores for the given move against each possible target
|
||||
#=============================================================================
|
||||
# Wild Pokémon choose their moves randomly.
|
||||
def pbRegisterMoveWild(_user, idxMove, choices)
|
||||
def pbRegisterMoveWild(idxMove, choices)
|
||||
score = 100
|
||||
# Doubly prefer one of the user's moves (the choice is random but consistent
|
||||
# and does not correlate to any other property of the user)
|
||||
score *= 2 if @user.pokemon.personalID % @user.moves.length == idxMove
|
||||
choices.push([idxMove, 100, -1]) # Move index, score, target
|
||||
end
|
||||
|
||||
# Trainer Pokémon calculate how much they want to use each of their moves.
|
||||
def pbRegisterMoveTrainer(user, idxMove, choices, skill)
|
||||
move = user.moves[idxMove]
|
||||
target_data = move.pbTarget(user)
|
||||
def pbRegisterMoveTrainer(idxMove, choices)
|
||||
move = @user.moves[idxMove]
|
||||
target_data = move.pbTarget(@user)
|
||||
# TODO: Alter target_data if user has Protean and move is Curse.
|
||||
if [:UserAndAllies, :AllAllies, :AllBattlers].include?(target_data.id) ||
|
||||
target_data.num_targets == 0
|
||||
# If move has no targets, affects the user, a side or the whole field, or
|
||||
# specially affects multiple Pokémon and the AI calculates an overall
|
||||
# score at once instead of per target
|
||||
score = pbGetMoveScore(move, user, user, skill)
|
||||
score = pbGetMoveScore(move, @user)
|
||||
choices.push([idxMove, score, -1]) if score > 0
|
||||
elsif target_data.num_targets > 1
|
||||
# If move affects multiple battlers and you don't choose a particular one
|
||||
totalScore = 0
|
||||
@battle.allBattlers.each do |b|
|
||||
next if !@battle.pbMoveCanTarget?(user.index, b.index, target_data)
|
||||
score = pbGetMoveScore(move, user, b, skill)
|
||||
totalScore += ((user.opposes?(b)) ? score : -score)
|
||||
next if !@battle.pbMoveCanTarget?(@user.index, b.index, target_data)
|
||||
score = pbGetMoveScore(move, b)
|
||||
totalScore += ((@user.opposes?(b)) ? score : -score)
|
||||
end
|
||||
choices.push([idxMove, totalScore, -1]) if totalScore > 0
|
||||
else
|
||||
# If move affects one battler and you have to choose which one
|
||||
scoresAndTargets = []
|
||||
@battle.allBattlers.each do |b|
|
||||
next if !@battle.pbMoveCanTarget?(user.index, b.index, target_data)
|
||||
next if target_data.targets_foe && !user.opposes?(b)
|
||||
score = pbGetMoveScore(move, user, b, skill)
|
||||
next if !@battle.pbMoveCanTarget?(@user.index, b.index, target_data)
|
||||
next if target_data.targets_foe && !@user.opposes?(b)
|
||||
score = pbGetMoveScore(move, b)
|
||||
scoresAndTargets.push([score, b.index]) if score > 0
|
||||
end
|
||||
if scoresAndTargets.length > 0
|
||||
@@ -152,34 +169,134 @@ class Battle::AI
|
||||
end
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Set some class variables for the move being assessed
|
||||
#=============================================================================
|
||||
def set_up_move_check(move, target)
|
||||
@move = move
|
||||
@target = target
|
||||
# TODO: Calculate pbRoughType once here.
|
||||
# Determine whether user or target is faster, and store that result so it
|
||||
# doesn't need recalculating
|
||||
if @target
|
||||
user_speed = pbRoughStat(@user, :SPEED)
|
||||
target_speed = pbRoughStat(@target, :SPEED)
|
||||
@user_faster = (user_speed > target_speed) ^ (@battle.field.effects[PBEffects::TrickRoom] > 0)
|
||||
else
|
||||
@user_faster = false # Won't be used if there is no target
|
||||
end
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Get a score for the given move being used against the given target
|
||||
#=============================================================================
|
||||
def pbGetMoveScore(move, user, target, skill = 100)
|
||||
skill = PBTrainerAI.minimumSkill if skill < PBTrainerAI.minimumSkill
|
||||
score = 100
|
||||
score = pbGetMoveScoreFunctionCode(score, move, user, target, skill)
|
||||
def pbGetMoveScore(move, target = nil)
|
||||
set_up_move_check(move, target)
|
||||
|
||||
# Get the base score for the move
|
||||
if @move.damagingMove?
|
||||
# Is also the predicted damage amount as a percentage of target's current HP
|
||||
score = pbGetDamagingMoveBaseScore
|
||||
else # Status moves
|
||||
# Depends on the move's effect
|
||||
score = pbGetStatusMoveBaseScore
|
||||
end
|
||||
# Modify the score according to the move's effect
|
||||
score = Battle::AI::Handlers.apply_move_effect_score(move.function,
|
||||
score, move, @user, target, @skill, self, @battle)
|
||||
|
||||
# A score of 0 here means it absolutely should not be used
|
||||
return 0 if score <= 0
|
||||
if skill >= PBTrainerAI.mediumSkill
|
||||
|
||||
# TODO: High priority checks:
|
||||
# => Prefer move if it will KO the target (moreso if user is slower than target)
|
||||
# => Don't prefer damaging move if it won't KO, user has Stance Change and
|
||||
# is in shield form, and user is slower than the target
|
||||
# => Check memory for past damage dealt by a target's non-high priority move,
|
||||
# and prefer move if user is slower than the target and another hit from
|
||||
# the same amount will KO the user
|
||||
# => Check memory for past damage dealt by a target's priority move, and don't
|
||||
# prefer the move if user is slower than the target and can't move faster
|
||||
# than it because of priority
|
||||
# => Discard move if user is slower than the target and target is semi-
|
||||
# invulnerable (and move won't hit it)
|
||||
# => Check memory for whether target has previously used Quick Guard, and
|
||||
# don't prefer move if so
|
||||
|
||||
# TODO: Low priority checks:
|
||||
# => Don't prefer move if user is faster than the target
|
||||
# => Prefer move if user is faster than the target and target is semi-
|
||||
# invulnerable
|
||||
|
||||
# Don't prefer a dancing move if the target has the Dancer ability
|
||||
# TODO: Check all battlers, not just the target.
|
||||
if skill_check(AILevel.high) && @move.danceMove? && @target.hasActiveAbility?(:DANCER)
|
||||
score /= 2
|
||||
end
|
||||
|
||||
# TODO: Check memory for whether target has previously used Ion Deluge, and
|
||||
# don't prefer move if it's Normal-type and target is immune because
|
||||
# of its ability (Lightning Rod, etc.).
|
||||
|
||||
# TODO: Discard move if it can be redirected by a non-target's ability
|
||||
# (Lightning Rod/Storm Drain). Include checking for a previous use of
|
||||
# Ion Deluge and this move being Normal-type.
|
||||
# => If non-target is a user's ally, don't prefer move (rather than discarding
|
||||
# it)
|
||||
|
||||
# TODO: Discard move if it's sound-based and user has been Throat Chopped.
|
||||
# Don't prefer move if user hasn't been Throat Chopped but target has
|
||||
# previously used Throat Chop. The first part of this would probably
|
||||
# go elsewhere (damage calc?).
|
||||
|
||||
# TODO: Prefer move if it has a high critical hit rate, critical hits are
|
||||
# possible but not certain, and target has raised defences/user has
|
||||
# lowered offences (Atk/Def or SpAtk/SpDef, whichever is relevant).
|
||||
|
||||
# TODO: Don't prefer damaging moves if target is Destiny Bonding.
|
||||
# => Also don't prefer damaging moves if user is slower than the target, move
|
||||
# is likely to be lethal, and target has previously used Destiny Bond
|
||||
|
||||
# TODO: Don't prefer a move that is stopped by Wide Guard if target has
|
||||
# previously used Wide Guard.
|
||||
|
||||
# TODO: Don't prefer Fire-type moves if target has previously used Powder.
|
||||
|
||||
# TODO: Don't prefer contact move if making contact with the target could
|
||||
# trigger an effect that's bad for the user (Static, etc.).
|
||||
# => Also check if target has previously used Spiky Shield.King's Shield/
|
||||
# Baneful Bunker, and don't prefer move if so
|
||||
|
||||
# TODO: Prefer a contact move if making contact with the target could trigger
|
||||
# an effect that's good for the user (Poison Touch/Pickpocket).
|
||||
|
||||
# TODO: Don't prefer a status move if user has a damaging move that will KO
|
||||
# the target.
|
||||
# => If target has previously used a move that will hurt the user by 30% of
|
||||
# its current HP or more, moreso don't prefer a status move.
|
||||
|
||||
if skill_check(AILevel.medium)
|
||||
|
||||
# Prefer damaging moves if AI has no more Pokémon or AI is less clever
|
||||
if @battle.pbAbleNonActiveCount(user.idxOwnSide) == 0 &&
|
||||
!(skill >= PBTrainerAI.highSkill && @battle.pbAbleNonActiveCount(target.idxOwnSide) > 0)
|
||||
if move.statusMove?
|
||||
score /= 1.5
|
||||
elsif target.hp <= target.totalhp / 2
|
||||
score *= 1.5
|
||||
if @battle.pbAbleNonActiveCount(@user.idxOwnSide) == 0 &&
|
||||
!(skill_check(AILevel.high) && @battle.pbAbleNonActiveCount(@target.idxOwnSide) > 0)
|
||||
if @move.statusMove?
|
||||
score *= 0.9
|
||||
elsif @target.hp <= @target.totalhp / 2
|
||||
score *= 1.1
|
||||
end
|
||||
end
|
||||
|
||||
# Don't prefer attacking the target if they'd be semi-invulnerable
|
||||
if skill >= PBTrainerAI.highSkill && move.accuracy > 0 &&
|
||||
(target.semiInvulnerable? || target.effects[PBEffects::SkyDrop] >= 0)
|
||||
if skill_check(AILevel.high) && @move.accuracy > 0 && @user_faster &&
|
||||
(@target.semiInvulnerable? || @target.effects[PBEffects::SkyDrop] >= 0)
|
||||
miss = true
|
||||
miss = false if user.hasActiveAbility?(:NOGUARD) || target.hasActiveAbility?(:NOGUARD)
|
||||
if miss && pbRoughStat(user, :SPEED, skill) > pbRoughStat(target, :SPEED, skill)
|
||||
miss = false if @user.hasActiveAbility?(:NOGUARD)
|
||||
miss = false if skill_check(AILevel.best) && @target.hasActiveAbility?(:NOGUARD)
|
||||
if skill_check(AILevel.best) && miss
|
||||
# Knows what can get past semi-invulnerability
|
||||
if target.effects[PBEffects::SkyDrop] >= 0 ||
|
||||
target.inTwoTurnAttack?("TwoTurnAttackInvulnerableInSky",
|
||||
if @target.effects[PBEffects::SkyDrop] >= 0 ||
|
||||
@target.inTwoTurnAttack?("TwoTurnAttackInvulnerableInSky",
|
||||
"TwoTurnAttackInvulnerableInSkyParalyzeTarget",
|
||||
"TwoTurnAttackInvulnerableInSkyTargetCannotAct")
|
||||
miss = false if move.hitsFlyingTargets?
|
||||
@@ -189,107 +306,388 @@ class Battle::AI
|
||||
miss = false if move.hitsDivingTargets?
|
||||
end
|
||||
end
|
||||
score -= 80 if miss
|
||||
score = 0 if miss
|
||||
end
|
||||
|
||||
# Pick a good move for the Choice items
|
||||
if user.hasActiveItem?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF]) ||
|
||||
user.hasActiveAbility?(:GORILLATACTICS)
|
||||
if move.baseDamage >= 60
|
||||
score += 60
|
||||
elsif move.damagingMove?
|
||||
score += 30
|
||||
elsif move.function == "UserTargetSwapItems"
|
||||
score += 70 # Trick
|
||||
else
|
||||
score -= 60
|
||||
end
|
||||
if @user.hasActiveItem?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF]) ||
|
||||
@user.hasActiveAbility?(:GORILLATACTICS)
|
||||
# Really don't prefer status moves (except Trick)
|
||||
score *= 0.1 if @move.statusMove? && @move.function != "UserTargetSwapItems"
|
||||
# Don't prefer moves of certain types
|
||||
move_type = pbRoughType(@move)
|
||||
# Most unpreferred types are 0x effective against another type, except
|
||||
# Fire/Water/Grass
|
||||
# TODO: Actually check through the types for 0x instead of hardcoding
|
||||
# them.
|
||||
# TODO: Reborn separately doesn't prefer Fire/Water/Grass/Electric, also
|
||||
# with a 0.95x score, meaning Electric can be 0.95x twice. Why are
|
||||
# these four types not preferred? Maybe because they're all not
|
||||
# very effective against Dragon.
|
||||
unpreferred_types = [:NORMAL, :FIGHTING, :POISON, :GROUND, :GHOST,
|
||||
:FIRE, :WATER, :GRASS, :ELECTRIC, :PSYCHIC, :DRAGON]
|
||||
score *= 0.95 if unpreferred_types.include?(move_type)
|
||||
# Don't prefer moves with lower accuracy
|
||||
score *= @move.accuracy / 100.0 if @move.accuracy > 0
|
||||
# Don't prefer moves with low PP
|
||||
score *= 0.9 if @move.pp < 6
|
||||
end
|
||||
# If user is asleep, prefer moves that are usable while asleep
|
||||
if user.status == :SLEEP && !move.usableWhenAsleep?
|
||||
user.eachMove do |m|
|
||||
next unless m.usableWhenAsleep?
|
||||
score -= 60
|
||||
break
|
||||
end
|
||||
|
||||
# If user is asleep, don't prefer moves that can't be used while asleep
|
||||
if skill_check(AILevel.medium) && @user.asleep? && @user.statusCount > 1 &&
|
||||
!@move.usableWhenAsleep?
|
||||
score *= 0.2
|
||||
end
|
||||
|
||||
# If user is frozen, prefer a move that can thaw the user
|
||||
if user.status == :FROZEN
|
||||
if move.thawsUser?
|
||||
score += 40
|
||||
if skill_check(AILevel.medium) && @user.status == :FROZEN
|
||||
if @move.thawsUser?
|
||||
score += 30
|
||||
else
|
||||
user.eachMove do |m|
|
||||
@user.eachMove do |m|
|
||||
next unless m.thawsUser?
|
||||
score -= 60
|
||||
score = 0 # Discard this move if user knows another move that thaws
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# If target is frozen, don't prefer moves that could thaw them
|
||||
if target.status == :FROZEN
|
||||
user.eachMove do |m|
|
||||
next if m.thawsUser?
|
||||
score -= 60
|
||||
break
|
||||
if @target.status == :FROZEN
|
||||
if pbRoughType(@move) == :FIRE || (Settings::MECHANICS_GENERATION >= 6 && @move.thawsUser?)
|
||||
score *= 0.1
|
||||
end
|
||||
end
|
||||
end
|
||||
# Don't prefer moves that are ineffective because of abilities or effects
|
||||
return 0 if pbCheckMoveImmunity(score, move, user, target, skill)
|
||||
# Adjust score based on how much damage it can deal
|
||||
if move.damagingMove?
|
||||
score = pbGetMoveScoreDamage(score, move, user, target, skill)
|
||||
else # Status moves
|
||||
# Don't prefer attacks which don't deal damage
|
||||
score -= 10
|
||||
# Account for accuracy of move
|
||||
accuracy = pbRoughAccuracy(move, user, target, skill)
|
||||
score *= accuracy / 100.0
|
||||
score = 0 if score <= 10 && skill >= PBTrainerAI.highSkill
|
||||
|
||||
# Don't prefer hitting a wild shiny Pokémon
|
||||
if @battle.wildBattle? && @target.opposes? && @target.shiny?
|
||||
score *= 0.15
|
||||
end
|
||||
|
||||
# TODO: Discard a move that can be Magic Coated if either opponent has Magic
|
||||
# Bounce.
|
||||
|
||||
# Account for accuracy of move
|
||||
accuracy = pbRoughAccuracy(@move, @target)
|
||||
score *= accuracy / 100.0
|
||||
|
||||
# Prefer flinching external effects (note that move effects which cause
|
||||
# flinching are dealt with in the function code part of score calculation)
|
||||
if skill_check(AILevel.medium)
|
||||
if !@target.hasActiveAbility?([:INNERFOCUS, :SHIELDDUST]) &&
|
||||
@target.effects[PBEffects::Substitute] == 0
|
||||
if @move.flinchingMove? ||
|
||||
(@move.damagingMove? &&
|
||||
(@user.hasActiveItem?([:KINGSROCK, :RAZORFANG]) ||
|
||||
@user.hasActiveAbility?(:STENCH)))
|
||||
score *= 1.3
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# # Adjust score based on how much damage it can deal
|
||||
# if move.damagingMove?
|
||||
# score = pbGetMoveScoreDamage(score, move, @user, @target, @skill)
|
||||
# else # Status moves
|
||||
# # Don't prefer attacks which don't deal damage
|
||||
# score -= 10
|
||||
# # Account for accuracy of move
|
||||
# accuracy = pbRoughAccuracy(move, target)
|
||||
# score *= accuracy / 100.0
|
||||
# score = 0 if score <= 10 && skill_check(AILevel.high)
|
||||
# end
|
||||
score = score.to_i
|
||||
score = 0 if score < 0
|
||||
return score
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Add to a move's score based on how much damage it will deal (as a percentage
|
||||
# of the target's current HP)
|
||||
# Calculate how much damage a move is likely to do to a given target (as a
|
||||
# percentage of the target's current HP)
|
||||
#=============================================================================
|
||||
def pbGetMoveScoreDamage(score, move, user, target, skill)
|
||||
return 0 if score <= 0
|
||||
def pbGetDamagingMoveBaseScore
|
||||
# Don't prefer moves that are ineffective because of abilities or effects
|
||||
return 0 if pbCheckMoveImmunity(@move, @target)
|
||||
|
||||
# Calculate how much damage the move will do (roughly)
|
||||
baseDmg = pbMoveBaseDamage(move, user, target, skill)
|
||||
realDamage = pbRoughDamage(move, user, target, skill, baseDmg)
|
||||
# Account for accuracy of move
|
||||
accuracy = pbRoughAccuracy(move, user, target, skill)
|
||||
realDamage *= accuracy / 100.0
|
||||
base_damage = pbMoveBaseDamage(@move, @target)
|
||||
calc_damage = pbRoughDamage(@move, @target, base_damage)
|
||||
|
||||
# TODO: Maybe move this check elsewhere? Note that Reborn's base score does
|
||||
# not include this halving, but the predicted damage does.
|
||||
# Two-turn attacks waste 2 turns to deal one lot of damage
|
||||
if move.chargingTurnMove? || move.function == "AttackAndSkipNextTurn" # Hyper Beam
|
||||
realDamage *= 2 / 3 # Not halved because semi-invulnerable during use or hits first turn
|
||||
end
|
||||
# Prefer flinching external effects (note that move effects which cause
|
||||
# flinching are dealt with in the function code part of score calculation)
|
||||
if skill >= PBTrainerAI.mediumSkill && !move.flinchingMove? &&
|
||||
!target.hasActiveAbility?(:INNERFOCUS) &&
|
||||
!target.hasActiveAbility?(:SHIELDDUST) &&
|
||||
target.effects[PBEffects::Substitute] == 0
|
||||
canFlinch = false
|
||||
if user.hasActiveItem?([:KINGSROCK, :RAZORFANG]) ||
|
||||
user.hasActiveAbility?(:STENCH)
|
||||
canFlinch = true
|
||||
calc_damage /= 2 if @move.chargingTurnMove?
|
||||
|
||||
# TODO: Maybe move this check elsewhere?
|
||||
# Increased critical hit rate
|
||||
if skill_check(AILevel.medium)
|
||||
crit_stage = pbRoughCriticalHitStage(@move, @target)
|
||||
if crit_stage >= 0
|
||||
crit_fraction = (crit_stage > 50) ? 1 : Battle::Move::CRITICAL_HIT_RATIOS[crit_stage]
|
||||
crit_mult = (Settings::NEW_CRITICAL_HIT_RATE_MECHANICS) ? 0.5 : 1
|
||||
calc_damage *= (1 + crit_mult / crit_fraction)
|
||||
end
|
||||
realDamage *= 1.3 if canFlinch
|
||||
end
|
||||
|
||||
# Convert damage to percentage of target's remaining HP
|
||||
damagePercentage = realDamage * 100.0 / target.hp
|
||||
damage_percentage = calc_damage * 100.0 / @target.hp
|
||||
|
||||
# Don't prefer weak attacks
|
||||
# damagePercentage /= 2 if damagePercentage<20
|
||||
# damage_percentage /= 2 if damage_percentage < 20
|
||||
|
||||
# Prefer damaging attack if level difference is significantly high
|
||||
damagePercentage *= 1.2 if user.level - 10 > target.level
|
||||
# damage_percentage *= 1.2 if @user.level - 10 > @target.level
|
||||
|
||||
# Adjust score
|
||||
damagePercentage = 120 if damagePercentage > 120 # Treat all lethal moves the same
|
||||
damagePercentage += 40 if damagePercentage > 100 # Prefer moves likely to be lethal
|
||||
score += damagePercentage.to_i
|
||||
return score
|
||||
damage_percentage = 110 if damage_percentage > 110 # Treat all lethal moves the same
|
||||
damage_percentage += 40 if damage_percentage > 100 # Prefer moves likely to be lethal
|
||||
|
||||
return damage_percentage.to_i
|
||||
end
|
||||
|
||||
def pbGetStatusMoveBaseScore
|
||||
# TODO: Call pbCheckMoveImmunity here too, not just for damaging moves
|
||||
# (only if this status move will be affected).
|
||||
|
||||
# TODO: Make sure all status moves are accounted for.
|
||||
# TODO: Duplicates in Reborn's AI:
|
||||
# "SleepTarget" Grass Whistle (15), Hypnosis (15), Sing (15),
|
||||
# Lovely Kiss (20), Sleep Powder (20), Spore (60)
|
||||
# "PoisonTarget" - Poison Powder (15), Poison Gas (20)
|
||||
# "ParalyzeTarget" - Stun Spore (25), Glare (30)
|
||||
# "ConfuseTarget" - Teeter Dance (5), Supersonic (10),
|
||||
# Sweet Kiss (20), Confuse Ray (25)
|
||||
# "RaiseUserAttack1" - Howl (10), Sharpen (10), Medicate (15)
|
||||
# "RaiseUserSpeed2" - Agility (15), Rock Polish (25)
|
||||
# "LowerTargetAttack1" - Growl (10), Baby-Doll Eyes (15)
|
||||
# "LowerTargetAccuracy1" - Sand Attack (5), Flash (10), Kinesis (10), Smokescreen (10)
|
||||
# "LowerTargetAttack2" - Charm (10), Feather Dance (15)
|
||||
# "LowerTargetSpeed2" - String Shot (10), Cotton Spore (15), Scary Face (15)
|
||||
# "LowerTargetSpDef2" - Metal Sound (10), Fake Tears (15)
|
||||
case @move.function
|
||||
when "ConfuseTarget",
|
||||
"LowerTargetAccuracy1",
|
||||
"LowerTargetEvasion1RemoveSideEffects",
|
||||
"UserTargetSwapAtkSpAtkStages",
|
||||
"UserTargetSwapDefSpDefStages",
|
||||
"UserSwapBaseAtkDef",
|
||||
"UserTargetAverageBaseAtkSpAtk",
|
||||
"UserTargetAverageBaseDefSpDef",
|
||||
"SetUserTypesToUserMoveType",
|
||||
"SetTargetTypesToWater",
|
||||
"SetUserTypesToTargetTypes",
|
||||
"SetTargetAbilityToUserAbility",
|
||||
"UserTargetSwapAbilities",
|
||||
"PowerUpAllyMove",
|
||||
"StartWeakenElectricMoves",
|
||||
"StartWeakenFireMoves",
|
||||
"EnsureNextMoveAlwaysHits",
|
||||
"StartNegateTargetEvasionStatStageAndGhostImmunity",
|
||||
"StartNegateTargetEvasionStatStageAndDarkImmunity",
|
||||
"ProtectUserSideFromPriorityMoves",
|
||||
"ProtectUserSideFromMultiTargetDamagingMoves",
|
||||
"BounceBackProblemCausingStatusMoves",
|
||||
"StealAndUseBeneficialStatusMove",
|
||||
"DisableTargetMovesKnownByUser",
|
||||
"DisableTargetHealingMoves",
|
||||
"SetAttackerMovePPTo0IfUserFaints",
|
||||
"UserEnduresFaintingThisTurn",
|
||||
"RestoreUserConsumedItem",
|
||||
"StartNegateHeldItems",
|
||||
"StartDamageTargetEachTurnIfTargetAsleep",
|
||||
"HealUserDependingOnUserStockpile",
|
||||
"StartGravity",
|
||||
"StartUserAirborne",
|
||||
"UserSwapsPositionsWithAlly",
|
||||
"StartSwapAllBattlersBaseDefensiveStats",
|
||||
"RaiseTargetSpDef1",
|
||||
"RaiseGroundedGrassBattlersAtkSpAtk1",
|
||||
"RaiseGrassBattlersDef1",
|
||||
"AddGrassTypeToTarget",
|
||||
"TrapAllBattlersInBattleForOneTurn",
|
||||
"EnsureNextCriticalHit",
|
||||
"UserTargetSwapBaseSpeed",
|
||||
"RedirectAllMovesToTarget",
|
||||
"TargetUsesItsLastUsedMoveAgain"
|
||||
return 5
|
||||
when "RaiseUserAttack1",
|
||||
"RaiseUserDefense1",
|
||||
"RaiseUserDefense1CurlUpUser",
|
||||
"RaiseUserCriticalHitRate2",
|
||||
"RaiseUserAtkSpAtk1",
|
||||
"RaiseUserAtkSpAtk1Or2InSun",
|
||||
"RaiseUserAtkAcc1",
|
||||
"RaiseTargetRandomStat2",
|
||||
"LowerTargetAttack1",
|
||||
"LowerTargetDefense1",
|
||||
"LowerTargetAccuracy1",
|
||||
"LowerTargetAttack2",
|
||||
"LowerTargetSpeed2",
|
||||
"LowerTargetSpDef2",
|
||||
"ResetAllBattlersStatStages",
|
||||
"UserCopyTargetStatStages",
|
||||
"SetUserTypesBasedOnEnvironment",
|
||||
"DisableTargetUsingSameMoveConsecutively",
|
||||
"StartTargetCannotUseItem",
|
||||
"LowerTargetAttack1BypassSubstitute",
|
||||
"LowerTargetAtkSpAtk1",
|
||||
"LowerTargetSpAtk1",
|
||||
"TargetNextFireMoveDamagesTarget"
|
||||
return 10
|
||||
when "SleepTarget",
|
||||
"SleepTargetIfUserDarkrai",
|
||||
"SleepTargetChangeUserMeloettaForm",
|
||||
"PoisonTarget",
|
||||
"CureUserBurnPoisonParalysis",
|
||||
"RaiseUserAttack1",
|
||||
"RaiseUserSpDef1PowerUpElectricMove",
|
||||
"RaiseUserEvasion1",
|
||||
"RaiseUserSpeed2",
|
||||
"LowerTargetAttack1",
|
||||
"LowerTargetAtkDef1",
|
||||
"LowerTargetAttack2",
|
||||
"LowerTargetDefense2",
|
||||
"LowerTargetSpeed2",
|
||||
"LowerTargetSpAtk2IfCanAttract",
|
||||
"LowerTargetSpDef2",
|
||||
"ReplaceMoveThisBattleWithTargetLastMoveUsed",
|
||||
"ReplaceMoveWithTargetLastMoveUsed",
|
||||
"SetUserAbilityToTargetAbility",
|
||||
"UseMoveTargetIsAboutToUse",
|
||||
"UseRandomMoveFromUserParty",
|
||||
"StartHealUserEachTurnTrapUserInBattle",
|
||||
"HealTargetHalfOfTotalHP",
|
||||
"UserFaintsHealAndCureReplacement",
|
||||
"UserFaintsHealAndCureReplacementRestorePP",
|
||||
"StartSunWeather",
|
||||
"StartRainWeather",
|
||||
"StartSandstormWeather",
|
||||
"StartHailWeather",
|
||||
"RaisePlusMinusUserAndAlliesDefSpDef1",
|
||||
"LowerTargetSpAtk2",
|
||||
"LowerPoisonedTargetAtkSpAtkSpd1",
|
||||
"AddGhostTypeToTarget",
|
||||
"LowerTargetAtkSpAtk1SwitchOutUser",
|
||||
"RaisePlusMinusUserAndAlliesAtkSpAtk1",
|
||||
"HealTargetDependingOnGrassyTerrain"
|
||||
return 15
|
||||
when "SleepTarget",
|
||||
"SleepTargetChangeUserMeloettaForm",
|
||||
"SleepTargetNextTurn",
|
||||
"PoisonTarget",
|
||||
"ConfuseTarget",
|
||||
"RaiseTargetSpAtk1ConfuseTarget",
|
||||
"RaiseTargetAttack2ConfuseTarget",
|
||||
"UserTargetSwapStatStages",
|
||||
"StartUserSideImmunityToStatStageLowering",
|
||||
"SetUserTypesToResistLastAttack",
|
||||
"SetTargetAbilityToSimple",
|
||||
"SetTargetAbilityToInsomnia",
|
||||
"NegateTargetAbility",
|
||||
"TransformUserIntoTarget",
|
||||
"UseLastMoveUsedByTarget",
|
||||
"UseLastMoveUsed",
|
||||
"UseRandomMove",
|
||||
"HealUserFullyAndFallAsleep",
|
||||
"StartHealUserEachTurn",
|
||||
"StartPerishCountsForAllBattlers",
|
||||
"SwitchOutTargetStatusMove",
|
||||
"TrapTargetInBattle",
|
||||
"TargetMovesBecomeElectric",
|
||||
"NormalMovesBecomeElectric",
|
||||
"PoisonTargetLowerTargetSpeed1"
|
||||
return 20
|
||||
when "BadPoisonTarget",
|
||||
"ParalyzeTarget",
|
||||
"BurnTarget",
|
||||
"ConfuseTarget",
|
||||
"AttractTarget",
|
||||
"GiveUserStatusToTarget",
|
||||
"RaiseUserDefSpDef1",
|
||||
"RaiseUserDefense2",
|
||||
"RaiseUserSpeed2",
|
||||
"RaiseUserSpeed2LowerUserWeight",
|
||||
"RaiseUserSpDef2",
|
||||
"RaiseUserEvasion2MinimizeUser",
|
||||
"RaiseUserDefense3",
|
||||
"MaxUserAttackLoseHalfOfTotalHP",
|
||||
"UserTargetAverageHP",
|
||||
"ProtectUser",
|
||||
"DisableTargetLastMoveUsed",
|
||||
"DisableTargetStatusMoves",
|
||||
"HealUserHalfOfTotalHP",
|
||||
"HealUserHalfOfTotalHPLoseFlyingTypeThisTurn",
|
||||
"HealUserPositionNextTurn",
|
||||
"HealUserDependingOnWeather",
|
||||
"StartLeechSeedTarget",
|
||||
"AttackerFaintsIfUserFaints",
|
||||
"UserTargetSwapItems",
|
||||
"UserMakeSubstitute",
|
||||
"UserAddStockpileRaiseDefSpDef1",
|
||||
"RedirectAllMovesToUser",
|
||||
"InvertTargetStatStages",
|
||||
"HealUserByTargetAttackLowerTargetAttack1",
|
||||
"HealUserDependingOnSandstorm"
|
||||
return 25
|
||||
when "ParalyzeTarget",
|
||||
"ParalyzeTargetIfNotTypeImmune",
|
||||
"RaiseUserAtkDef1",
|
||||
"RaiseUserAtkDefAcc1",
|
||||
"RaiseUserSpAtkSpDef1",
|
||||
"UseMoveDependingOnEnvironment",
|
||||
"UseRandomUserMoveIfAsleep",
|
||||
"DisableTargetUsingDifferentMove",
|
||||
"SwitchOutUserPassOnEffects",
|
||||
"AddSpikesToFoeSide",
|
||||
"AddToxicSpikesToFoeSide",
|
||||
"AddStealthRocksToFoeSide",
|
||||
"CurseTargetOrLowerUserSpd1RaiseUserAtkDef1",
|
||||
"StartSlowerBattlersActFirst",
|
||||
"ProtectUserFromTargetingMovesSpikyShield",
|
||||
"StartElectricTerrain",
|
||||
"StartGrassyTerrain",
|
||||
"StartMistyTerrain",
|
||||
"StartPsychicTerrain",
|
||||
"CureTargetStatusHealUserHalfOfTotalHP"
|
||||
return 30
|
||||
when "CureUserPartyStatus",
|
||||
"RaiseUserAttack2",
|
||||
"RaiseUserSpAtk2",
|
||||
"RaiseUserSpAtk3",
|
||||
"StartUserSideDoubleSpeed",
|
||||
"StartWeakenPhysicalDamageAgainstUserSide",
|
||||
"StartWeakenSpecialDamageAgainstUserSide",
|
||||
"ProtectUserSideFromDamagingMovesIfUserFirstTurn",
|
||||
"ProtectUserFromDamagingMovesKingsShield",
|
||||
"ProtectUserBanefulBunker"
|
||||
return 35
|
||||
when "RaiseUserAtkSpd1",
|
||||
"RaiseUserSpAtkSpDefSpd1",
|
||||
"LowerUserDefSpDef1RaiseUserAtkSpAtkSpd2",
|
||||
"RaiseUserAtk1Spd2",
|
||||
"TwoTurnAttackRaiseUserSpAtkSpDefSpd2"
|
||||
return 40
|
||||
when "SleepTarget",
|
||||
"SleepTargetChangeUserMeloettaForm",
|
||||
"AddStickyWebToFoeSide",
|
||||
"StartWeakenDamageAgainstUserSideIfHail"
|
||||
return 60
|
||||
end
|
||||
# "DoesNothingUnusableInGravity",
|
||||
# "StartUserSideImmunityToInflictedStatus",
|
||||
# "LowerTargetEvasion1",
|
||||
# "LowerTargetEvasion2",
|
||||
# "StartPreventCriticalHitsAgainstUserSide",
|
||||
# "UserFaintsLowerTargetAtkSpAtk2",
|
||||
# "FleeFromBattle",
|
||||
# "SwitchOutUserStatusMove"
|
||||
# "TargetTakesUserItem",
|
||||
# "LowerPPOfTargetLastMoveBy4",
|
||||
# "StartTargetAirborneAndAlwaysHitByMoves",
|
||||
# "TargetActsNext",
|
||||
# "TargetActsLast",
|
||||
# "ProtectUserSideFromStatusMoves"
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
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)
|
||||
target_data = move.pbTarget(user)
|
||||
def pbTargetsMultiple?(move)
|
||||
target_data = move.pbTarget(@user)
|
||||
return false if target_data.num_targets <= 1
|
||||
num_targets = 0
|
||||
case target_data.id
|
||||
when :AllAllies
|
||||
@battle.allSameSideBattlers(user).each { |b| num_targets += 1 if b.index != user.index }
|
||||
@battle.allSameSideBattlers(@user).each { |b| num_targets += 1 if b.index != @user.index }
|
||||
when :UserAndAllies
|
||||
@battle.allSameSideBattlers(user).each { |_b| num_targets += 1 }
|
||||
@battle.allSameSideBattlers(@user).each { |_b| num_targets += 1 }
|
||||
when :AllNearFoes
|
||||
@battle.allOtherSideBattlers(user).each { |b| num_targets += 1 if b.near?(user) }
|
||||
@battle.allOtherSideBattlers(@user).each { |b| num_targets += 1 if b.near?(@user) }
|
||||
when :AllFoes
|
||||
@battle.allOtherSideBattlers(user).each { |_b| num_targets += 1 }
|
||||
@battle.allOtherSideBattlers(@user).each { |_b| num_targets += 1 }
|
||||
when :AllNearOthers
|
||||
@battle.allBattlers.each { |b| num_targets += 1 if b.near?(user) }
|
||||
@battle.allBattlers.each { |b| num_targets += 1 if b.near?(@user) }
|
||||
when :AllBattlers
|
||||
@battle.allBattlers.each { |_b| num_targets += 1 }
|
||||
end
|
||||
@@ -83,11 +83,11 @@ class Battle::AI
|
||||
|
||||
# For switching. Determines the effectiveness of a potential switch-in against
|
||||
# an opposing battler.
|
||||
def pbCalcTypeModPokemon(battlerThis, _battlerOther)
|
||||
mod1 = Effectiveness.calculate(battlerThis.types[0], target.types[0], target.types[1])
|
||||
def pbCalcTypeModPokemon(pkmn, target)
|
||||
mod1 = Effectiveness.calculate(pkmn.types[0], target.types[0], target.types[1])
|
||||
mod2 = Effectiveness::NORMAL_EFFECTIVE
|
||||
if battlerThis.types.length > 1
|
||||
mod2 = Effectiveness.calculate(battlerThis.types[1], target.types[0], target.types[1])
|
||||
if pkmn.types.length > 1
|
||||
mod2 = Effectiveness.calculate(pkmn.types[1], target.types[0], target.types[1])
|
||||
mod2 = mod2.to_f / Effectivenesss::NORMAL_EFFECTIVE
|
||||
end
|
||||
return mod1 * mod2
|
||||
@@ -96,15 +96,18 @@ class Battle::AI
|
||||
#=============================================================================
|
||||
# Immunity to a move because of the target's ability, item or other effects
|
||||
#=============================================================================
|
||||
def pbCheckMoveImmunity(score, move, user, target, skill)
|
||||
type = pbRoughType(move, user, skill)
|
||||
typeMod = pbCalcTypeMod(type, user, target)
|
||||
def pbCheckMoveImmunity(move, target)
|
||||
# TODO: Add consideration of user's Mold Breaker.
|
||||
move_type = pbRoughType(move)
|
||||
typeMod = pbCalcTypeMod(move_type, @user, target)
|
||||
# Type effectiveness
|
||||
return true if (move.damagingMove? && Effectiveness.ineffective?(typeMod)) || score <= 0
|
||||
return true if move.damagingMove? && Effectiveness.ineffective?(typeMod)
|
||||
# Immunity due to ability/item/other effects
|
||||
if skill >= PBTrainerAI.mediumSkill
|
||||
case type
|
||||
if skill_check(AILevel.medium)
|
||||
case move_type
|
||||
when :GROUND
|
||||
# TODO: Split target.airborne? into separate parts to allow different
|
||||
# skill levels to apply to each part.
|
||||
return true if target.airborne? && !move.hitsFlyingTargets?
|
||||
when :FIRE
|
||||
return true if target.hasActiveAbility?(:FLASHFIRE)
|
||||
@@ -117,24 +120,25 @@ class Battle::AI
|
||||
end
|
||||
return true if move.damagingMove? && Effectiveness.not_very_effective?(typeMod) &&
|
||||
target.hasActiveAbility?(:WONDERGUARD)
|
||||
return true if move.damagingMove? && user.index != target.index && !target.opposes?(user) &&
|
||||
return true if move.damagingMove? && @user.index != target.index && !target.opposes?(@user) &&
|
||||
target.hasActiveAbility?(:TELEPATHY)
|
||||
return true if move.statusMove? && move.canMagicCoat? && target.hasActiveAbility?(:MAGICBOUNCE) &&
|
||||
target.opposes?(user)
|
||||
target.opposes?(@user)
|
||||
return true if move.soundMove? && target.hasActiveAbility?(:SOUNDPROOF)
|
||||
return true if move.bombMove? && target.hasActiveAbility?(:BULLETPROOF)
|
||||
if move.powderMove?
|
||||
return true if target.pbHasType?(:GRASS)
|
||||
return true if target.hasActiveAbility?(:OVERCOAT)
|
||||
return true if target.hasActiveItem?(:SAFETYGOGGLES)
|
||||
return true if skill_check(AILevel.best) && target.hasActiveAbility?(:OVERCOAT)
|
||||
return true if skill_check(AILevel.high) && target.hasActiveItem?(:SAFETYGOGGLES)
|
||||
end
|
||||
return true if move.statusMove? && target.effects[PBEffects::Substitute] > 0 &&
|
||||
!move.ignoresSubstitute?(user) && user.index != target.index
|
||||
!move.ignoresSubstitute?(@user) && @user.index != target.index
|
||||
return true if move.statusMove? && Settings::MECHANICS_GENERATION >= 7 &&
|
||||
user.hasActiveAbility?(:PRANKSTER) && target.pbHasType?(:DARK) &&
|
||||
target.opposes?(user)
|
||||
@user.hasActiveAbility?(:PRANKSTER) && target.pbHasType?(:DARK) &&
|
||||
target.opposes?(@user)
|
||||
return true if move.priority > 0 && @battle.field.terrain == :Psychic &&
|
||||
target.affectedByTerrain? && target.opposes?(user)
|
||||
target.affectedByTerrain? && target.opposes?(@user)
|
||||
# TODO: Dazzling/Queenly Majesty go here.
|
||||
end
|
||||
return false
|
||||
end
|
||||
@@ -142,16 +146,14 @@ class Battle::AI
|
||||
#=============================================================================
|
||||
# Get approximate properties for a battler
|
||||
#=============================================================================
|
||||
def pbRoughType(move, user, skill)
|
||||
def pbRoughType(move)
|
||||
ret = move.type
|
||||
if skill >= PBTrainerAI.highSkill
|
||||
ret = move.pbCalcType(user)
|
||||
end
|
||||
ret = move.pbCalcType(@user) if skill_check(AILevel.high)
|
||||
return ret
|
||||
end
|
||||
|
||||
def pbRoughStat(battler, stat, skill)
|
||||
return battler.pbSpeed if skill >= PBTrainerAI.highSkill && stat == :SPEED
|
||||
def pbRoughStat(battler, stat)
|
||||
return battler.pbSpeed if skill_check(AILevel.high) && stat == :SPEED
|
||||
stageMul = [2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8]
|
||||
stageDiv = [8, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2]
|
||||
stage = battler.stages[stat] + 6
|
||||
@@ -169,25 +171,25 @@ class Battle::AI
|
||||
#=============================================================================
|
||||
# Get a better move's base damage value
|
||||
#=============================================================================
|
||||
def pbMoveBaseDamage(move, user, target, skill)
|
||||
def pbMoveBaseDamage(move, target)
|
||||
baseDmg = move.baseDamage
|
||||
baseDmg = 60 if baseDmg == 1
|
||||
return baseDmg if skill < PBTrainerAI.mediumSkill
|
||||
return baseDmg if !skill_check(AILevel.medium)
|
||||
# Covers all function codes which have their own def pbBaseDamage
|
||||
case move.function
|
||||
# Sonic Boom, Dragon Rage, Super Fang, Night Shade, Endeavor
|
||||
when "FixedDamage20", "FixedDamage40", "FixedDamageHalfTargetHP",
|
||||
"FixedDamageUserLevel", "LowerTargetHPToUserHP"
|
||||
baseDmg = move.pbFixedDamage(user, target)
|
||||
baseDmg = move.pbFixedDamage(@user, target)
|
||||
when "FixedDamageUserLevelRandom" # Psywave
|
||||
baseDmg = user.level
|
||||
baseDmg = @user.level
|
||||
when "OHKO", "OHKOIce", "OHKOHitsUndergroundTarget"
|
||||
baseDmg = 200
|
||||
when "CounterPhysicalDamage", "CounterSpecialDamage", "CounterDamagePlusHalf"
|
||||
baseDmg = 60
|
||||
when "DoublePowerIfTargetUnderwater", "DoublePowerIfTargetUnderground",
|
||||
"BindTargetDoublePowerIfTargetUnderwater"
|
||||
baseDmg = move.pbModifyDamage(baseDmg, user, target)
|
||||
baseDmg = move.pbModifyDamage(baseDmg, @user, target)
|
||||
# Gust, Twister, Venoshock, Smelling Salts, Wake-Up Slap, Facade, Hex, Brine,
|
||||
# Retaliate, Weather Ball, Return, Frustration, Eruption, Crush Grip,
|
||||
# Stored Power, Punishment, Hidden Power, Fury Cutter, Echoed Voice,
|
||||
@@ -217,12 +219,12 @@ class Battle::AI
|
||||
"PowerHigherWithTargetWeight",
|
||||
"ThrowUserItemAtTarget",
|
||||
"PowerDependsOnUserStockpile"
|
||||
baseDmg = move.pbBaseDamage(baseDmg, user, target)
|
||||
baseDmg = move.pbBaseDamage(baseDmg, @user, target)
|
||||
when "DoublePowerIfUserHasNoItem" # Acrobatics
|
||||
baseDmg *= 2 if !user.item || user.hasActiveItem?(:FLYINGGEM)
|
||||
baseDmg *= 2 if !@user.item || @user.hasActiveItem?(:FLYINGGEM)
|
||||
when "PowerHigherWithTargetFasterThanUser" # Gyro Ball
|
||||
targetSpeed = pbRoughStat(target, :SPEED, skill)
|
||||
userSpeed = pbRoughStat(user, :SPEED, skill)
|
||||
targetSpeed = pbRoughStat(target, :SPEED)
|
||||
userSpeed = pbRoughStat(@user, :SPEED)
|
||||
baseDmg = [[(25 * targetSpeed / userSpeed).floor, 150].min, 1].max
|
||||
when "RandomlyDamageOrHealTarget" # Present
|
||||
baseDmg = 50
|
||||
@@ -230,46 +232,46 @@ class Battle::AI
|
||||
baseDmg = 71
|
||||
baseDmg *= 2 if target.inTwoTurnAttack?("TwoTurnAttackInvulnerableUnderground") # Dig
|
||||
when "TypeAndPowerDependOnUserBerry" # Natural Gift
|
||||
baseDmg = move.pbNaturalGiftBaseDamage(user.item_id)
|
||||
baseDmg = move.pbNaturalGiftBaseDamage(@user.item_id)
|
||||
when "PowerHigherWithUserHeavierThanTarget" # Heavy Slam
|
||||
baseDmg = move.pbBaseDamage(baseDmg, user, target)
|
||||
baseDmg *= 2 if Settings::MECHANICS_GENERATION >= 7 && skill >= PBTrainerAI.mediumSkill &&
|
||||
baseDmg = move.pbBaseDamage(baseDmg, @user, target)
|
||||
baseDmg *= 2 if Settings::MECHANICS_GENERATION >= 7 && skill_check(AILevel.medium) &&
|
||||
target.effects[PBEffects::Minimize]
|
||||
when "AlwaysCriticalHit", "HitTwoTimes", "HitTwoTimesPoisonTarget" # Frost Breath, Double Kick, Twineedle
|
||||
baseDmg *= 2
|
||||
when "HitThreeTimesPowersUpWithEachHit" # Triple Kick
|
||||
baseDmg *= 6 # Hits do x1, x2, x3 baseDmg in turn, for x6 in total
|
||||
when "HitTwoToFiveTimes" # Fury Attack
|
||||
if user.hasActiveAbility?(:SKILLLINK)
|
||||
if @user.hasActiveAbility?(:SKILLLINK)
|
||||
baseDmg *= 5
|
||||
else
|
||||
baseDmg = (baseDmg * 31 / 10).floor # Average damage dealt
|
||||
end
|
||||
when "HitTwoToFiveTimesOrThreeForAshGreninja"
|
||||
if user.isSpecies?(:GRENINJA) && user.form == 2
|
||||
if @user.isSpecies?(:GRENINJA) && @user.form == 2
|
||||
baseDmg *= 4 # 3 hits at 20 power = 4 hits at 15 power
|
||||
elsif user.hasActiveAbility?(:SKILLLINK)
|
||||
elsif @user.hasActiveAbility?(:SKILLLINK)
|
||||
baseDmg *= 5
|
||||
else
|
||||
baseDmg = (baseDmg * 31 / 10).floor # Average damage dealt
|
||||
end
|
||||
when "HitOncePerUserTeamMember" # Beat Up
|
||||
mult = 0
|
||||
@battle.eachInTeamFromBattlerIndex(user.index) do |pkmn, _i|
|
||||
@battle.eachInTeamFromBattlerIndex(@user.index) do |pkmn, _i|
|
||||
mult += 1 if pkmn&.able? && pkmn.status == :NONE
|
||||
end
|
||||
baseDmg *= mult
|
||||
when "TwoTurnAttackOneTurnInSun" # Solar Beam
|
||||
baseDmg = move.pbBaseDamageMultiplier(baseDmg, user, target)
|
||||
baseDmg = move.pbBaseDamageMultiplier(baseDmg, @user, target)
|
||||
when "MultiTurnAttackPowersUpEachTurn" # Rollout
|
||||
baseDmg *= 2 if user.effects[PBEffects::DefenseCurl]
|
||||
baseDmg *= 2 if @user.effects[PBEffects::DefenseCurl]
|
||||
when "MultiTurnAttackBideThenReturnDoubleDamage" # Bide
|
||||
baseDmg = 40
|
||||
when "UserFaintsFixedDamageUserHP" # Final Gambit
|
||||
baseDmg = user.hp
|
||||
baseDmg = @user.hp
|
||||
when "EffectivenessIncludesFlyingType" # Flying Press
|
||||
if GameData::Type.exists?(:FLYING)
|
||||
if skill >= PBTrainerAI.highSkill
|
||||
if skill_check(AILevel.high)
|
||||
targetTypes = target.pbTypes(true)
|
||||
mult = Effectiveness.calculate(
|
||||
:FLYING, targetTypes[0], targetTypes[1], targetTypes[2]
|
||||
@@ -281,12 +283,12 @@ class Battle::AI
|
||||
end
|
||||
baseDmg = (baseDmg.to_f * mult / Effectiveness::NORMAL_EFFECTIVE).round
|
||||
end
|
||||
baseDmg *= 2 if skill >= PBTrainerAI.mediumSkill && target.effects[PBEffects::Minimize]
|
||||
baseDmg *= 2 if skill_check(AILevel.medium) && target.effects[PBEffects::Minimize]
|
||||
when "DoublePowerIfUserLastMoveFailed" # Stomping Tantrum
|
||||
baseDmg *= 2 if user.lastRoundMoveFailed
|
||||
baseDmg *= 2 if @user.lastRoundMoveFailed
|
||||
when "HitTwoTimesFlinchTarget" # Double Iron Bash
|
||||
baseDmg *= 2
|
||||
baseDmg *= 2 if skill >= PBTrainerAI.mediumSkill && target.effects[PBEffects::Minimize]
|
||||
baseDmg *= 2 if skill_check(AILevel.medium) && target.effects[PBEffects::Minimize]
|
||||
end
|
||||
return baseDmg
|
||||
end
|
||||
@@ -294,29 +296,33 @@ class Battle::AI
|
||||
#=============================================================================
|
||||
# Damage calculation
|
||||
#=============================================================================
|
||||
def pbRoughDamage(move, user, target, skill, baseDmg)
|
||||
def pbRoughDamage(move, target, baseDmg)
|
||||
# Fixed damage moves
|
||||
return baseDmg if move.is_a?(Battle::Move::FixedDamageMove)
|
||||
|
||||
# Get the move's type
|
||||
type = pbRoughType(move, user, skill)
|
||||
type = pbRoughType(move)
|
||||
|
||||
##### Calculate user's attack stat #####
|
||||
atk = pbRoughStat(user, :ATTACK, skill)
|
||||
atk = pbRoughStat(@user, :ATTACK)
|
||||
if move.function == "UseTargetAttackInsteadOfUserAttack" # Foul Play
|
||||
atk = pbRoughStat(target, :ATTACK, skill)
|
||||
atk = pbRoughStat(target, :ATTACK)
|
||||
elsif move.function == "UseUserBaseDefenseInsteadOfUserBaseAttack" # Body Press
|
||||
atk = pbRoughStat(user, :DEFENSE, skill)
|
||||
atk = pbRoughStat(@user, :DEFENSE)
|
||||
elsif move.specialMove?(type)
|
||||
if move.function == "UseTargetAttackInsteadOfUserAttack" # Foul Play
|
||||
atk = pbRoughStat(target, :SPECIAL_ATTACK, skill)
|
||||
atk = pbRoughStat(target, :SPECIAL_ATTACK)
|
||||
else
|
||||
atk = pbRoughStat(user, :SPECIAL_ATTACK, skill)
|
||||
atk = pbRoughStat(@user, :SPECIAL_ATTACK)
|
||||
end
|
||||
end
|
||||
|
||||
##### Calculate target's defense stat #####
|
||||
defense = pbRoughStat(target, :DEFENSE, skill)
|
||||
defense = pbRoughStat(target, :DEFENSE)
|
||||
if move.specialMove?(type) && move.function != "UseTargetDefenseInsteadOfTargetSpDef" # Psyshock
|
||||
defense = pbRoughStat(target, :SPECIAL_DEFENSE, skill)
|
||||
defense = pbRoughStat(target, :SPECIAL_DEFENSE)
|
||||
end
|
||||
|
||||
##### Calculate all multiplier effects #####
|
||||
multipliers = {
|
||||
:base_damage_multiplier => 1.0,
|
||||
@@ -325,11 +331,9 @@ class Battle::AI
|
||||
:final_damage_multiplier => 1.0
|
||||
}
|
||||
# Ability effects that alter damage
|
||||
moldBreaker = false
|
||||
if skill >= PBTrainerAI.highSkill && target.hasMoldBreaker?
|
||||
moldBreaker = true
|
||||
end
|
||||
if skill >= PBTrainerAI.mediumSkill && user.abilityActive?
|
||||
moldBreaker = skill_check(AILevel.high) && target.hasMoldBreaker?
|
||||
|
||||
if skill_check(AILevel.medium) && @user.abilityActive?
|
||||
# NOTE: These abilities aren't suitable for checking at the start of the
|
||||
# round.
|
||||
abilityBlacklist = [:ANALYTIC, :SNIPER, :TINTEDLENS, :AERILATE, :PIXILATE, :REFRIGERATE]
|
||||
@@ -341,19 +345,21 @@ class Battle::AI
|
||||
end
|
||||
if canCheck
|
||||
Battle::AbilityEffects.triggerDamageCalcFromUser(
|
||||
user.ability, user, target, move, multipliers, baseDmg, type
|
||||
@user.ability, @user, target, move, multipliers, baseDmg, type
|
||||
)
|
||||
end
|
||||
end
|
||||
if skill >= PBTrainerAI.mediumSkill && !moldBreaker
|
||||
user.allAllies.each do |b|
|
||||
|
||||
if skill_check(AILevel.medium) && !moldBreaker
|
||||
@user.allAllies.each do |b|
|
||||
next if !b.abilityActive?
|
||||
Battle::AbilityEffects.triggerDamageCalcFromAlly(
|
||||
b.ability, user, target, move, multipliers, baseDmg, type
|
||||
b.ability, @user, target, move, multipliers, baseDmg, type
|
||||
)
|
||||
end
|
||||
end
|
||||
if skill >= PBTrainerAI.bestSkill && !moldBreaker && target.abilityActive?
|
||||
|
||||
if skill_check(AILevel.best) && !moldBreaker && target.abilityActive?
|
||||
# NOTE: These abilities aren't suitable for checking at the start of the
|
||||
# round.
|
||||
abilityBlacklist = [:FILTER, :SOLIDROCK]
|
||||
@@ -365,40 +371,45 @@ class Battle::AI
|
||||
end
|
||||
if canCheck
|
||||
Battle::AbilityEffects.triggerDamageCalcFromTarget(
|
||||
target.ability, user, target, move, multipliers, baseDmg, type
|
||||
target.ability, @user, target, move, multipliers, baseDmg, type
|
||||
)
|
||||
end
|
||||
end
|
||||
if skill >= PBTrainerAI.bestSkill && !moldBreaker
|
||||
|
||||
if skill_check(AILevel.best) && !moldBreaker
|
||||
target.allAllies.each do |b|
|
||||
next if !b.abilityActive?
|
||||
Battle::AbilityEffects.triggerDamageCalcFromTargetAlly(
|
||||
b.ability, user, target, move, multipliers, baseDmg, type
|
||||
b.ability, @user, target, move, multipliers, baseDmg, type
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# Item effects that alter damage
|
||||
# NOTE: Type-boosting gems aren't suitable for checking at the start of the
|
||||
# round.
|
||||
if skill >= PBTrainerAI.mediumSkill && user.itemActive?
|
||||
if skill_check(AILevel.medium) && @user.itemActive?
|
||||
# NOTE: These items aren't suitable for checking at the start of the
|
||||
# round.
|
||||
itemBlacklist = [:EXPERTBELT, :LIFEORB]
|
||||
if !itemBlacklist.include?(user.item_id)
|
||||
if !itemBlacklist.include?(@user.item_id)
|
||||
Battle::ItemEffects.triggerDamageCalcFromUser(
|
||||
user.item, user, target, move, multipliers, baseDmg, type
|
||||
@user.item, @user, target, move, multipliers, baseDmg, type
|
||||
)
|
||||
user.effects[PBEffects::GemConsumed] = nil # Untrigger consuming of Gems
|
||||
@user.effects[PBEffects::GemConsumed] = nil # Untrigger consuming of Gems
|
||||
end
|
||||
# TODO: Prefer (1.5x?) if item will be consumed and user has Unburden.
|
||||
end
|
||||
if skill >= PBTrainerAI.bestSkill &&
|
||||
|
||||
if skill_check(AILevel.best) &&
|
||||
target.itemActive? && target.item && !target.item.is_berry?
|
||||
Battle::ItemEffects.triggerDamageCalcFromTarget(
|
||||
target.item, user, target, move, multipliers, baseDmg, type
|
||||
target.item, @user, target, move, multipliers, baseDmg, type
|
||||
)
|
||||
end
|
||||
|
||||
# Global abilities
|
||||
if skill >= PBTrainerAI.mediumSkill &&
|
||||
if skill_check(AILevel.medium) &&
|
||||
((@battle.pbCheckGlobalAbility(:DARKAURA) && type == :DARK) ||
|
||||
(@battle.pbCheckGlobalAbility(:FAIRYAURA) && type == :FAIRY))
|
||||
if @battle.pbCheckGlobalAbility(:AURABREAK)
|
||||
@@ -407,20 +418,25 @@ class Battle::AI
|
||||
multipliers[:base_damage_multiplier] *= 4 / 3.0
|
||||
end
|
||||
end
|
||||
|
||||
# Parental Bond
|
||||
if skill >= PBTrainerAI.mediumSkill && user.hasActiveAbility?(:PARENTALBOND)
|
||||
if skill_check(AILevel.medium) && @user.hasActiveAbility?(:PARENTALBOND)
|
||||
multipliers[:base_damage_multiplier] *= 1.25
|
||||
end
|
||||
|
||||
# Me First
|
||||
# TODO
|
||||
|
||||
# Helping Hand - n/a
|
||||
|
||||
# Charge
|
||||
if skill >= PBTrainerAI.mediumSkill &&
|
||||
user.effects[PBEffects::Charge] > 0 && type == :ELECTRIC
|
||||
if skill_check(AILevel.medium) &&
|
||||
@user.effects[PBEffects::Charge] > 0 && type == :ELECTRIC
|
||||
multipliers[:base_damage_multiplier] *= 2
|
||||
end
|
||||
|
||||
# Mud Sport and Water Sport
|
||||
if skill >= PBTrainerAI.mediumSkill
|
||||
if skill_check(AILevel.medium)
|
||||
if type == :ELECTRIC
|
||||
if @battle.allBattlers.any? { |b| b.effects[PBEffects::MudSport] }
|
||||
multipliers[:base_damage_multiplier] /= 3
|
||||
@@ -438,34 +454,40 @@ class Battle::AI
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Terrain moves
|
||||
if skill >= PBTrainerAI.mediumSkill
|
||||
if skill_check(AILevel.medium)
|
||||
case @battle.field.terrain
|
||||
when :Electric
|
||||
multipliers[:base_damage_multiplier] *= 1.5 if type == :ELECTRIC && user.affectedByTerrain?
|
||||
multipliers[:base_damage_multiplier] *= 1.5 if type == :ELECTRIC && @user.affectedByTerrain?
|
||||
when :Grassy
|
||||
multipliers[:base_damage_multiplier] *= 1.5 if type == :GRASS && user.affectedByTerrain?
|
||||
multipliers[:base_damage_multiplier] *= 1.5 if type == :GRASS && @user.affectedByTerrain?
|
||||
when :Psychic
|
||||
multipliers[:base_damage_multiplier] *= 1.5 if type == :PSYCHIC && user.affectedByTerrain?
|
||||
multipliers[:base_damage_multiplier] *= 1.5 if type == :PSYCHIC && @user.affectedByTerrain?
|
||||
when :Misty
|
||||
multipliers[:base_damage_multiplier] /= 2 if type == :DRAGON && target.affectedByTerrain?
|
||||
end
|
||||
end
|
||||
|
||||
# Badge multipliers
|
||||
if skill >= PBTrainerAI.highSkill && @battle.internalBattle && target.pbOwnedByPlayer?
|
||||
if skill_check(AILevel.high) && @battle.internalBattle && target.pbOwnedByPlayer?
|
||||
# Don't need to check the Atk/Sp Atk-boosting badges because the AI
|
||||
# won't control the player's Pokémon.
|
||||
if move.physicalMove?(type) && @battle.pbPlayer.badge_count >= Settings::NUM_BADGES_BOOST_DEFENSE
|
||||
multipliers[:defense_multiplier] *= 1.1
|
||||
elsif move.specialMove?(type) && @battle.pbPlayer.badge_count >= Settings::NUM_BADGES_BOOST_SPDEF
|
||||
multipliers[:defense_multiplier] *= 1.1
|
||||
end
|
||||
end
|
||||
|
||||
# Multi-targeting attacks
|
||||
if skill >= PBTrainerAI.highSkill && pbTargetsMultiple?(move, user)
|
||||
if skill_check(AILevel.high) && pbTargetsMultiple?(move)
|
||||
multipliers[:final_damage_multiplier] *= 0.75
|
||||
end
|
||||
|
||||
# Weather
|
||||
if skill >= PBTrainerAI.mediumSkill
|
||||
case user.effectiveWeather
|
||||
if skill_check(AILevel.medium)
|
||||
case @user.effectiveWeather
|
||||
when :Sun, :HarshSun
|
||||
case type
|
||||
when :FIRE
|
||||
@@ -487,30 +509,36 @@ class Battle::AI
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Critical hits - n/a
|
||||
|
||||
# Random variance - n/a
|
||||
|
||||
# STAB
|
||||
if skill >= PBTrainerAI.mediumSkill && type && user.pbHasType?(type)
|
||||
if user.hasActiveAbility?(:ADAPTABILITY)
|
||||
if skill_check(AILevel.medium) && type && @user.pbHasType?(type)
|
||||
if @user.hasActiveAbility?(:ADAPTABILITY)
|
||||
multipliers[:final_damage_multiplier] *= 2
|
||||
else
|
||||
multipliers[:final_damage_multiplier] *= 1.5
|
||||
end
|
||||
end
|
||||
|
||||
# Type effectiveness
|
||||
if skill >= PBTrainerAI.mediumSkill
|
||||
typemod = pbCalcTypeMod(type, user, target)
|
||||
if skill_check(AILevel.medium)
|
||||
typemod = pbCalcTypeMod(type, @user, target)
|
||||
multipliers[:final_damage_multiplier] *= typemod.to_f / Effectiveness::NORMAL_EFFECTIVE
|
||||
end
|
||||
|
||||
# Burn
|
||||
if skill >= PBTrainerAI.highSkill && move.physicalMove?(type) &&
|
||||
user.status == :BURN && !user.hasActiveAbility?(:GUTS) &&
|
||||
if skill_check(AILevel.high) && move.physicalMove?(type) &&
|
||||
@user.status == :BURN && !@user.hasActiveAbility?(:GUTS) &&
|
||||
!(Settings::MECHANICS_GENERATION >= 6 &&
|
||||
move.function == "DoublePowerIfUserPoisonedBurnedParalyzed") # Facade
|
||||
multipliers[:final_damage_multiplier] /= 2
|
||||
end
|
||||
|
||||
# Aurora Veil, Reflect, Light Screen
|
||||
if skill >= PBTrainerAI.highSkill && !move.ignoresReflect? && !user.hasActiveAbility?(:INFILTRATOR)
|
||||
if skill_check(AILevel.high) && !move.ignoresReflect? && !@user.hasActiveAbility?(:INFILTRATOR)
|
||||
if target.pbOwnSide.effects[PBEffects::AuroraVeil] > 0
|
||||
if @battle.pbSideBattlerCount(target) > 1
|
||||
multipliers[:final_damage_multiplier] *= 2 / 3.0
|
||||
@@ -531,80 +559,93 @@ class Battle::AI
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Minimize
|
||||
if skill >= PBTrainerAI.highSkill && target.effects[PBEffects::Minimize] && move.tramplesMinimize?
|
||||
if skill_check(AILevel.high) && target.effects[PBEffects::Minimize] && move.tramplesMinimize?
|
||||
multipliers[:final_damage_multiplier] *= 2
|
||||
end
|
||||
|
||||
# Move-specific base damage modifiers
|
||||
# TODO
|
||||
|
||||
# Move-specific final damage modifiers
|
||||
# TODO
|
||||
|
||||
##### Main damage calculation #####
|
||||
baseDmg = [(baseDmg * multipliers[:base_damage_multiplier]).round, 1].max
|
||||
atk = [(atk * multipliers[:attack_multiplier]).round, 1].max
|
||||
defense = [(defense * multipliers[:defense_multiplier]).round, 1].max
|
||||
damage = ((((2.0 * user.level / 5) + 2).floor * baseDmg * atk / defense).floor / 50).floor + 2
|
||||
damage = ((((2.0 * @user.level / 5) + 2).floor * baseDmg * atk / defense).floor / 50).floor + 2
|
||||
damage = [(damage * multipliers[:final_damage_multiplier]).round, 1].max
|
||||
# "AI-specific calculations below"
|
||||
# Increased critical hit rates
|
||||
if skill >= PBTrainerAI.mediumSkill
|
||||
c = 0
|
||||
# Ability effects that alter critical hit rate
|
||||
if c >= 0 && user.abilityActive?
|
||||
c = Battle::AbilityEffects.triggerCriticalCalcFromUser(user.ability, user, target, c)
|
||||
end
|
||||
if skill >= PBTrainerAI.bestSkill && c >= 0 && !moldBreaker && target.abilityActive?
|
||||
c = Battle::AbilityEffects.triggerCriticalCalcFromTarget(target.ability, user, target, c)
|
||||
end
|
||||
# Item effects that alter critical hit rate
|
||||
if c >= 0 && user.itemActive?
|
||||
c = Battle::ItemEffects.triggerCriticalCalcFromUser(user.item, user, target, c)
|
||||
end
|
||||
if skill >= PBTrainerAI.bestSkill && c >= 0 && target.itemActive?
|
||||
c = Battle::ItemEffects.triggerCriticalCalcFromTarget(target.item, user, target, c)
|
||||
end
|
||||
# Other efffects
|
||||
c = -1 if target.pbOwnSide.effects[PBEffects::LuckyChant] > 0
|
||||
if c >= 0
|
||||
c += 1 if move.highCriticalRate?
|
||||
c += user.effects[PBEffects::FocusEnergy]
|
||||
c += 1 if user.inHyperMode? && move.type == :SHADOW
|
||||
end
|
||||
if c >= 0
|
||||
c = 4 if c > 4
|
||||
damage += damage * 0.1 * c
|
||||
end
|
||||
end
|
||||
return damage.floor
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Critical hit rate calculation
|
||||
#=============================================================================
|
||||
def pbRoughCriticalHitStage(move, target)
|
||||
return -1 if target.pbOwnSide.effects[PBEffects::LuckyChant] > 0
|
||||
mold_breaker = (skill_check(AILevel.medium) && @user.hasMoldBreaker?)
|
||||
crit_stage = 0
|
||||
# Ability effects that alter critical hit rate
|
||||
if skill_check(AILevel.medium) && @user.abilityActive?
|
||||
crit_stage = BattleHandlers.triggerCriticalCalcUserAbility(@user.ability, @user, target, crit_stage)
|
||||
return -1 if crit_stage < 0
|
||||
end
|
||||
if skill_check(AILevel.best) && !mold_breaker && target.abilityActive?
|
||||
crit_stage = BattleHandlers.triggerCriticalCalcTargetAbility(target.ability, @user, target, crit_stage)
|
||||
return -1 if crit_stage < 0
|
||||
end
|
||||
# Item effects that alter critical hit rate
|
||||
if skill_check(AILevel.medium) && @user.itemActive?
|
||||
crit_stage = BattleHandlers.triggerCriticalCalcUserItem(@user.item, @user, target, crit_stage)
|
||||
return -1 if crit_stage < 0
|
||||
end
|
||||
if skill_check(AILevel.high) && target.itemActive?
|
||||
crit_stage = BattleHandlers.triggerCriticalCalcTargetItem(target.item, @user, target, crit_stage)
|
||||
return -1 if crit_stage < 0
|
||||
end
|
||||
# Other effects
|
||||
case move.pbCritialOverride(@user, target)
|
||||
when 1 then return 99
|
||||
when -1 then return -1
|
||||
end
|
||||
return 99 if crit_stage > 50 # Merciless
|
||||
return 99 if @user.effects[PBEffects::LaserFocus] > 0
|
||||
crit_stage += 1 if move.highCriticalRate?
|
||||
crit_stage += @user.effects[PBEffects::FocusEnergy]
|
||||
crit_stage += 1 if @user.inHyperMode? && move.type == :SHADOW
|
||||
crit_stage = [crit_stage, Battle::Move::CRITICAL_HIT_RATIOS.length - 1].min
|
||||
return crit_stage
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Accuracy calculation
|
||||
#=============================================================================
|
||||
def pbRoughAccuracy(move, user, target, skill)
|
||||
def pbRoughAccuracy(move, target)
|
||||
# "Always hit" effects and "always hit" accuracy
|
||||
if skill >= PBTrainerAI.mediumSkill
|
||||
return 125 if target.effects[PBEffects::Minimize] && move.tramplesMinimize? &&
|
||||
if skill_check(AILevel.medium)
|
||||
return 100 if target.effects[PBEffects::Minimize] && move.tramplesMinimize? &&
|
||||
Settings::MECHANICS_GENERATION >= 6
|
||||
return 125 if target.effects[PBEffects::Telekinesis] > 0
|
||||
return 100 if target.effects[PBEffects::Telekinesis] > 0
|
||||
end
|
||||
# Get base accuracy
|
||||
baseAcc = move.accuracy
|
||||
if skill >= PBTrainerAI.highSkill
|
||||
baseAcc = move.pbBaseAccuracy(user, target)
|
||||
end
|
||||
return 125 if baseAcc == 0 && skill >= PBTrainerAI.mediumSkill
|
||||
baseAcc = move.pbBaseAccuracy(@user, target) if skill_check(AILevel.medium)
|
||||
return 100 if baseAcc == 0 && skill_check(AILevel.medium)
|
||||
# Get the move's type
|
||||
type = pbRoughType(move, user, skill)
|
||||
type = pbRoughType(move)
|
||||
# Calculate all modifier effects
|
||||
modifiers = {}
|
||||
modifiers[:base_accuracy] = baseAcc
|
||||
modifiers[:accuracy_stage] = user.stages[:ACCURACY]
|
||||
modifiers[:accuracy_stage] = @user.stages[:ACCURACY]
|
||||
modifiers[:evasion_stage] = target.stages[:EVASION]
|
||||
modifiers[:accuracy_multiplier] = 1.0
|
||||
modifiers[:evasion_multiplier] = 1.0
|
||||
pbCalcAccuracyModifiers(user, target, modifiers, move, type, skill)
|
||||
# Check if move can't miss
|
||||
return 125 if modifiers[:base_accuracy] == 0
|
||||
pbCalcAccuracyModifiers(target, modifiers, move, type)
|
||||
# Check if move certainly misses/can't miss
|
||||
return 0 if modifiers[:base_accuracy] < 0
|
||||
return 100 if modifiers[:base_accuracy] == 0
|
||||
# Calculation
|
||||
accStage = [[modifiers[:accuracy_stage], -6].max, 6].min + 6
|
||||
evaStage = [[modifiers[:evasion_stage], -6].max, 6].min + 6
|
||||
@@ -618,70 +659,82 @@ class Battle::AI
|
||||
return modifiers[:base_accuracy] * accuracy / evasion
|
||||
end
|
||||
|
||||
def pbCalcAccuracyModifiers(user, target, modifiers, move, type, skill)
|
||||
moldBreaker = false
|
||||
if skill >= PBTrainerAI.highSkill && target.hasMoldBreaker?
|
||||
moldBreaker = true
|
||||
end
|
||||
def pbCalcAccuracyModifiers(target, modifiers, move, type)
|
||||
moldBreaker = (skill_check(AILevel.medium) && target.hasMoldBreaker?)
|
||||
# Ability effects that alter accuracy calculation
|
||||
if skill >= PBTrainerAI.mediumSkill
|
||||
if user.abilityActive?
|
||||
Battle::AbilityEffects.triggerAccuracyCalcFromUser(
|
||||
user.ability, modifiers, user, target, move, type
|
||||
)
|
||||
end
|
||||
user.allAllies.each do |b|
|
||||
if skill_check(AILevel.medium) && @user.abilityActive?
|
||||
Battle::AbilityEffects.triggerAccuracyCalcFromUser(
|
||||
@user.ability, modifiers, @user, target, move, type
|
||||
)
|
||||
end
|
||||
if skill_check(AILevel.high)
|
||||
@user.allAllies.each do |b|
|
||||
next if !b.abilityActive?
|
||||
Battle::AbilityEffects.triggerAccuracyCalcFromAlly(
|
||||
b.ability, modifiers, user, target, move, type
|
||||
b.ability, modifiers, @user, target, move, type
|
||||
)
|
||||
end
|
||||
end
|
||||
if skill >= PBTrainerAI.bestSkill && target.abilityActive? && !moldBreaker
|
||||
if skill_check(AILevel.best) && target.abilityActive? && !moldBreaker
|
||||
Battle::AbilityEffects.triggerAccuracyCalcFromTarget(
|
||||
target.ability, modifiers, user, target, move, type
|
||||
target.ability, modifiers, @user, target, move, type
|
||||
)
|
||||
end
|
||||
# Item effects that alter accuracy calculation
|
||||
if skill >= PBTrainerAI.mediumSkill && user.itemActive?
|
||||
if skill_check(AILevel.medium) && @user.itemActive?
|
||||
# TODO: Zoom Lens needs to be checked differently (compare speeds of
|
||||
# user and target).
|
||||
Battle::ItemEffects.triggerAccuracyCalcFromUser(
|
||||
user.item, modifiers, user, target, move, type
|
||||
@user.item, modifiers, @user, target, move, type
|
||||
)
|
||||
end
|
||||
if skill >= PBTrainerAI.bestSkill && target.itemActive?
|
||||
if skill_check(AILevel.high) && target.itemActive?
|
||||
Battle::ItemEffects.triggerAccuracyCalcFromTarget(
|
||||
target.item, modifiers, user, target, move, type
|
||||
target.item, modifiers, @user, target, move, type
|
||||
)
|
||||
end
|
||||
# Other effects, inc. ones that set accuracy_multiplier or evasion_stage to specific values
|
||||
if skill >= PBTrainerAI.mediumSkill
|
||||
if @battle.field.effects[PBEffects::Gravity] > 0
|
||||
modifiers[:accuracy_multiplier] *= 5 / 3.0
|
||||
end
|
||||
if user.effects[PBEffects::MicleBerry]
|
||||
if @battle.field.effects[PBEffects::Gravity] > 0
|
||||
modifiers[:accuracy_multiplier] *= 5 / 3.0
|
||||
end
|
||||
if skill_check(AILevel.medium)
|
||||
if @user.effects[PBEffects::MicleBerry]
|
||||
modifiers[:accuracy_multiplier] *= 1.2
|
||||
end
|
||||
modifiers[:evasion_stage] = 0 if target.effects[PBEffects::Foresight] && modifiers[:evasion_stage] > 0
|
||||
modifiers[:evasion_stage] = 0 if target.effects[PBEffects::MiracleEye] && modifiers[:evasion_stage] > 0
|
||||
end
|
||||
# "AI-specific calculations below"
|
||||
if skill >= PBTrainerAI.mediumSkill
|
||||
modifiers[:evasion_stage] = 0 if move.function == "IgnoreTargetDefSpDefEvaStatStages" # Chip Away
|
||||
modifiers[:base_accuracy] = 0 if user.effects[PBEffects::LockOn] > 0 &&
|
||||
user.effects[PBEffects::LockOnPos] == target.index
|
||||
modifiers[:evasion_stage] = 0 if move.function == "IgnoreTargetDefSpDefEvaStatStages" # Chip Away
|
||||
if skill_check(AILevel.medium)
|
||||
modifiers[:base_accuracy] = 0 if @user.effects[PBEffects::LockOn] > 0 &&
|
||||
@user.effects[PBEffects::LockOnPos] == target.index
|
||||
end
|
||||
if skill >= PBTrainerAI.highSkill
|
||||
if skill_check(AILevel.medium)
|
||||
if move.function == "BadPoisonTarget" && # Toxic
|
||||
Settings::MORE_TYPE_EFFECTS && move.statusMove? && user.pbHasType?(:POISON)
|
||||
Settings::MORE_TYPE_EFFECTS && move.statusMove? && @user.pbHasType?(:POISON)
|
||||
modifiers[:base_accuracy] = 0
|
||||
end
|
||||
if ["OHKO", "OHKOIce", "OHKOHitsUndergroundTarget"].include?(move.function)
|
||||
modifiers[:base_accuracy] = move.accuracy + user.level - target.level
|
||||
modifiers[:accuracy_multiplier] = 0 if target.level > user.level
|
||||
if skill >= PBTrainerAI.bestSkill && target.hasActiveAbility?(:STURDY)
|
||||
modifiers[:base_accuracy] = move.accuracy + @user.level - target.level
|
||||
modifiers[:accuracy_multiplier] = 0 if target.level > @user.level
|
||||
if skill_check(AILevel.best) && target.hasActiveAbility?(:STURDY)
|
||||
modifiers[:accuracy_multiplier] = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Check if battler has a move that meets the criteria in the block provided
|
||||
#=============================================================================
|
||||
def check_for_move(battler)
|
||||
ret = false
|
||||
battler.eachMove do |move|
|
||||
next unless yield move
|
||||
ret = true
|
||||
break
|
||||
end
|
||||
return ret
|
||||
end
|
||||
end
|
||||
|
||||
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?
|
||||
end
|
||||
|
||||
def pbEnemyShouldWithdraw?(idxBattler)
|
||||
return _battlePalace_pbEnemyShouldWithdraw?(idxBattler) if !@battlePalace
|
||||
thispkmn = @battle.battlers[idxBattler]
|
||||
def pbEnemyShouldWithdraw?
|
||||
return _battlePalace_pbEnemyShouldWithdraw? if !@battlePalace
|
||||
thispkmn = @user
|
||||
idxBattler = @user.index
|
||||
shouldswitch = false
|
||||
if thispkmn.effects[PBEffects::PerishSong] == 1
|
||||
shouldswitch = true
|
||||
|
||||
@@ -215,8 +215,8 @@ class Battle::AI
|
||||
alias _battleArena_pbEnemyShouldWithdraw? pbEnemyShouldWithdraw?
|
||||
end
|
||||
|
||||
def pbEnemyShouldWithdraw?(idxBattler)
|
||||
return _battleArena_pbEnemyShouldWithdraw?(idxBattler) if !@battleArena
|
||||
def pbEnemyShouldWithdraw?
|
||||
return _battleArena_pbEnemyShouldWithdraw? if !@battleArena
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
@@ -19,6 +19,10 @@ class Trainer
|
||||
return _INTL("{1} {2}", trainer_type_name, @name)
|
||||
end
|
||||
|
||||
def skill_level
|
||||
return GameData::TrainerType.try_get(:trainer_type)&.skill_level || 0
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
|
||||
# Portion of the ID which is visible on the Trainer Card
|
||||
|
||||
Reference in New Issue
Block a user