mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-06 06:01:46 +00:00
Added AI objects for trainers, battlers and the move being assessed, logging battle messages now also echoes them to the console
This commit is contained in:
@@ -16,14 +16,24 @@ module PBDebug
|
||||
|
||||
def self.flush
|
||||
if $DEBUG && $INTERNAL && @@log.length > 0
|
||||
File.open("Data/debuglog.txt", "a+b") { |f| f.write(@@log.to_s) }
|
||||
File.open("Data/debuglog.txt", "a+b") { |f| f.write(@@log.join) }
|
||||
end
|
||||
@@log.clear
|
||||
end
|
||||
|
||||
def self.log(msg)
|
||||
if $DEBUG && $INTERNAL
|
||||
@@log.push("#{msg}\r\n")
|
||||
echoln msg
|
||||
@@log.push(msg + "\r\n")
|
||||
PBDebug.flush # if @@log.length > 1024
|
||||
end
|
||||
end
|
||||
|
||||
def self.log_message(msg)
|
||||
if $DEBUG && $INTERNAL
|
||||
msg = "\"" + msg + "\""
|
||||
echoln Console.markup_style(msg, text: :dark_gray)
|
||||
@@log.push(msg + "\r\n")
|
||||
PBDebug.flush # if @@log.length > 1024
|
||||
end
|
||||
end
|
||||
|
||||
@@ -278,6 +278,7 @@ class Battle
|
||||
def pbStartBattleCore
|
||||
# Set up the battlers on each side
|
||||
sendOuts = pbSetUpSides
|
||||
@battleAI.create_ai_objects
|
||||
# Create all the sprites and play the battle intro animation
|
||||
@scene.pbStartBattle(self)
|
||||
# Show trainers on both sides sending out Pokémon
|
||||
|
||||
@@ -189,7 +189,7 @@ class Battle::Scene
|
||||
pbShowWindow(MESSAGE_BOX)
|
||||
cw = @sprites["messageWindow"]
|
||||
cw.setText(msg)
|
||||
PBDebug.log(msg)
|
||||
PBDebug.log_message(msg)
|
||||
yielded = false
|
||||
timer = 0.0
|
||||
loop do
|
||||
@@ -235,7 +235,7 @@ class Battle::Scene
|
||||
pbShowWindow(MESSAGE_BOX)
|
||||
cw = @sprites["messageWindow"]
|
||||
cw.text = _INTL("{1}\1", msg)
|
||||
PBDebug.log(msg)
|
||||
PBDebug.log_message(msg)
|
||||
yielded = false
|
||||
timer = 0.0
|
||||
loop do
|
||||
@@ -283,7 +283,7 @@ class Battle::Scene
|
||||
cw.z = dw.z + 1
|
||||
cw.index = 0
|
||||
cw.viewport = @viewport
|
||||
PBDebug.log(msg)
|
||||
PBDebug.log_message(msg)
|
||||
loop do
|
||||
cw.visible = (!dw.busy?)
|
||||
pbUpdate(cw)
|
||||
|
||||
@@ -2,93 +2,61 @@
|
||||
#
|
||||
#===============================================================================
|
||||
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
|
||||
attr_reader :battle
|
||||
attr_reader :trainer
|
||||
attr_reader :user, :target, :move
|
||||
|
||||
#=============================================================================
|
||||
#
|
||||
#=============================================================================
|
||||
def initialize(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) }]
|
||||
@battle = battle
|
||||
|
||||
# TODO: Move this elsewhere?
|
||||
@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
|
||||
|
||||
def pbStdDev(choices)
|
||||
sum = 0
|
||||
n = 0
|
||||
choices.each do |c|
|
||||
sum += c[1]
|
||||
n += 1
|
||||
def create_ai_objects
|
||||
# Initialize AI trainers
|
||||
@trainers = [[], []]
|
||||
@battle.player.each_with_index do |trainer, i|
|
||||
@trainers[0][i] = AITrainer.new(self, 0, i, trainer)
|
||||
end
|
||||
return 0 if n < 2
|
||||
mean = sum.to_f / n
|
||||
varianceTimesN = 0
|
||||
choices.each do |c|
|
||||
next if c[1] <= 0
|
||||
deviation = c[1].to_f - mean
|
||||
varianceTimesN += deviation * deviation
|
||||
if @battle.wildBattle?
|
||||
@trainers[1][0] = AITrainer.new(self, 1, 0, nil)
|
||||
else
|
||||
@battle.opponent.each_with_index do |trainer, i|
|
||||
@trainers[1][i] = AITrainer.new(self, 1, i, trainer)
|
||||
end
|
||||
end
|
||||
# Using population standard deviation
|
||||
# [(n-1) makes it a sample std dev, would be 0 with only 1 sample]
|
||||
return Math.sqrt(varianceTimesN / n)
|
||||
# Initialize AI battlers
|
||||
@battlers = []
|
||||
@battle.battlers.each_with_index do |battler, i|
|
||||
@battlers[i] = AIBattler.new(self, i) if battler
|
||||
end
|
||||
# Initialize AI move object
|
||||
@move = AIMove.new(self)
|
||||
end
|
||||
|
||||
# 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
|
||||
# Set some class variables for the Pokémon whose action is being chosen
|
||||
def set_up(idxBattler)
|
||||
# Find the AI trainer choosing the action
|
||||
opposes = @battle.opposes?(idxBattler)
|
||||
trainer_index = @battle.pbGetOwnerIndexFromBattlerIndex(idxBattler)
|
||||
@trainer = @trainers[(opposes) ? 1 : 0][trainer_index]
|
||||
# Find the AI battler for which the action is being chosen
|
||||
@user = @battlers[idxBattler]
|
||||
@user.refresh_battler
|
||||
end
|
||||
|
||||
# Choose an action.
|
||||
def pbDefaultChooseEnemyCommand(idxBattler)
|
||||
set_up(idxBattler)
|
||||
choices = pbGetMoveScores
|
||||
return if pbEnemyShouldUseItem?
|
||||
return if pbEnemyShouldWithdraw?
|
||||
return if pbEnemyShouldUseItem?
|
||||
return if @battle.pbAutoFightMenu(idxBattler)
|
||||
@battle.pbRegisterMegaEvolution(idxBattler) if pbEnemyShouldMegaEvolve?
|
||||
choices = pbGetMoveScores
|
||||
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
|
||||
|
||||
#===============================================================================
|
||||
|
||||
@@ -24,7 +24,7 @@ class Battle::AI
|
||||
return nil if !items || items.length == 0
|
||||
# Determine target of item (always the Pokémon choosing the action)
|
||||
idxTarget = @user.index # Battler using the item
|
||||
battler = @battle.battlers[idxTarget]
|
||||
battler = @user.battler
|
||||
pkmn = battler.pokemon
|
||||
# Item categories
|
||||
hpItems = {
|
||||
|
||||
@@ -7,19 +7,20 @@ class Battle::AI
|
||||
end
|
||||
|
||||
def pbEnemyShouldWithdrawEx?(forceSwitch)
|
||||
return false if @wildBattler
|
||||
return false if @user.wild?
|
||||
shouldSwitch = forceSwitch
|
||||
battler = @user.battler
|
||||
batonPass = -1
|
||||
moveType = nil
|
||||
# If Pokémon is within 6 levels of the foe, and foe's last move was
|
||||
# super-effective and powerful
|
||||
if !shouldSwitch && @user.turnCount > 0 && skill_check(AILevel.high)
|
||||
target = @user.pbDirectOpposing(true)
|
||||
if !shouldSwitch && battler.turnCount > 0 && @trainer.high_skill?
|
||||
target = battler.pbDirectOpposing(true)
|
||||
if !target.fainted? && target.lastMoveUsed &&
|
||||
(target.level - @user.level).abs <= 6
|
||||
(target.level - battler.level).abs <= 6
|
||||
moveData = GameData::Move.get(target.lastMoveUsed)
|
||||
moveType = moveData.type
|
||||
typeMod = pbCalcTypeMod(moveType, target, @user)
|
||||
typeMod = battler.effectiveness_of_type_against_battler(moveType, target)
|
||||
if Effectiveness.super_effective?(typeMod) && moveData.base_damage > 50
|
||||
switchChance = (moveData.base_damage > 70) ? 30 : 20
|
||||
shouldSwitch = (pbAIRandom(100) < switchChance)
|
||||
@@ -27,36 +28,36 @@ class Battle::AI
|
||||
end
|
||||
end
|
||||
# Pokémon can't do anything (must have been in battle for at least 5 rounds)
|
||||
if !@battle.pbCanChooseAnyMove?(@user.index) &&
|
||||
@user.turnCount && @user.turnCount >= 5
|
||||
if !shouldSwitch && !@battle.pbCanChooseAnyMove?(battler.index) &&
|
||||
battler.turnCount && battler.turnCount >= 5
|
||||
shouldSwitch = true
|
||||
end
|
||||
# Pokémon is Perish Songed and has Baton Pass
|
||||
if skill_check(AILevel.high) && @user.effects[PBEffects::PerishSong] == 1
|
||||
@user.eachMoveWithIndex do |m, i|
|
||||
if @trainer.high_skill? && battler.effects[PBEffects::PerishSong] == 1
|
||||
battler.eachMoveWithIndex do |m, i|
|
||||
next if m.function != "SwitchOutUserPassOnEffects" # Baton Pass
|
||||
next if !@battle.pbCanChooseMove?(@user.index, i, false)
|
||||
next if !@battle.pbCanChooseMove?(battler.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 @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
|
||||
if !shouldSwitch && battler.status == :POISON && battler.statusCount > 0 && @trainer.high_skill?
|
||||
toxicHP = battler.totalhp / 16
|
||||
nextToxicHP = toxicHP * (battler.effects[PBEffects::Toxic] + 1)
|
||||
if battler.hp <= nextToxicHP && battler.hp > toxicHP * 2
|
||||
shouldSwitch = true if pbAIRandom(100) < 80
|
||||
end
|
||||
end
|
||||
# Pokémon is Encored into an unfavourable move
|
||||
if @user.effects[PBEffects::Encore] > 0 && skill_check(AILevel.medium)
|
||||
idxEncoredMove = @user.pbEncoredMoveIndex
|
||||
if !shouldSwitch && battler.effects[PBEffects::Encore] > 0 && @trainer.medium_skill?
|
||||
idxEncoredMove = battler.pbEncoredMoveIndex
|
||||
if idxEncoredMove >= 0
|
||||
scoreSum = 0
|
||||
scoreCount = 0
|
||||
@user.allOpposing.each do |b|
|
||||
scoreSum += pbGetMoveScore(@user.moves[idxEncoredMove], b)
|
||||
battler.allOpposing.each do |b|
|
||||
scoreSum += pbGetMoveScore(battler.moves[idxEncoredMove], b)
|
||||
scoreCount += 1
|
||||
end
|
||||
if scoreCount > 0 && scoreSum / scoreCount <= 20
|
||||
@@ -66,37 +67,37 @@ class Battle::AI
|
||||
end
|
||||
# If there is a single foe and it is resting after Hyper Beam or is
|
||||
# Truanting (i.e. free turn)
|
||||
if @battle.pbSideSize(@user.index + 1) == 1 &&
|
||||
!@user.pbDirectOpposing.fainted? && skill_check(AILevel.high)
|
||||
opp = @user.pbDirectOpposing
|
||||
if shouldSwitch && @battle.pbSideSize(battler.index + 1) == 1 &&
|
||||
!battler.pbDirectOpposing.fainted? && @trainer.high_skill?
|
||||
opp = battler.pbDirectOpposing
|
||||
if (opp.effects[PBEffects::HyperBeam] > 0 ||
|
||||
(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"] && @user.turnCount > 0
|
||||
if @user.hp <= @user.totalhp / 4 && pbAIRandom(100) < 30
|
||||
if !shouldSwitch && @battle.rules["suddendeath"] && battler.turnCount > 0
|
||||
if battler.hp <= battler.totalhp / 4 && pbAIRandom(100) < 30
|
||||
shouldSwitch = true
|
||||
elsif @user.hp <= @user.totalhp / 2 && pbAIRandom(100) < 80
|
||||
elsif battler.hp <= battler.totalhp / 2 && pbAIRandom(100) < 80
|
||||
shouldSwitch = true
|
||||
end
|
||||
end
|
||||
# Pokémon is about to faint because of Perish Song
|
||||
if @user.effects[PBEffects::PerishSong] == 1
|
||||
if !shouldSwitch && battler.effects[PBEffects::PerishSong] == 1
|
||||
shouldSwitch = true
|
||||
end
|
||||
if shouldSwitch
|
||||
list = []
|
||||
idxPartyStart, idxPartyEnd = @battle.pbTeamIndexRangeFromBattlerIndex(@user.index)
|
||||
@battle.pbParty(@user.index).each_with_index do |pkmn, i|
|
||||
idxPartyStart, idxPartyEnd = @battle.pbTeamIndexRangeFromBattlerIndex(battler.index)
|
||||
@battle.pbParty(battler.index).each_with_index do |pkmn, i|
|
||||
next if i == idxPartyEnd - 1 # Don't choose to switch in ace
|
||||
next if !@battle.pbCanSwitch?(@user.index, i)
|
||||
next if !@battle.pbCanSwitch?(battler.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 @user.effects[PBEffects::PerishSong] != 1
|
||||
if battler.effects[PBEffects::PerishSong] != 1
|
||||
# Will contain effects that recommend against switching
|
||||
spikes = @user.pbOwnSide.effects[PBEffects::Spikes]
|
||||
spikes = battler.pbOwnSide.effects[PBEffects::Spikes]
|
||||
# Don't switch to this if too little HP
|
||||
if spikes > 0
|
||||
spikesDmg = [8, 6, 4][spikes - 1]
|
||||
@@ -105,17 +106,17 @@ class Battle::AI
|
||||
end
|
||||
end
|
||||
# moveType is the type of the target's last used move
|
||||
if moveType && Effectiveness.ineffective?(pbCalcTypeMod(moveType, @user, @user))
|
||||
if moveType && Effectiveness.ineffective?(battler.effectiveness_of_type_against_battler(moveType))
|
||||
weight = 65
|
||||
typeMod = pbCalcTypeModPokemon(pkmn, @user.pbDirectOpposing(true))
|
||||
typeMod = pbCalcTypeModPokemon(pkmn, battler.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, @user, @user))
|
||||
elsif moveType && Effectiveness.resistant?(battler.effectiveness_of_type_against_battler(moveType))
|
||||
weight = 40
|
||||
typeMod = pbCalcTypeModPokemon(pkmn, @user.pbDirectOpposing(true))
|
||||
typeMod = pbCalcTypeModPokemon(pkmn, battler.pbDirectOpposing(true))
|
||||
if Effectiveness.super_effective?(typeMod)
|
||||
# Greater weight if new Pokemon's type is effective against target
|
||||
weight = 60
|
||||
@@ -126,13 +127,13 @@ class Battle::AI
|
||||
end
|
||||
end
|
||||
if list.length > 0
|
||||
if batonPass >= 0 && @battle.pbRegisterMove(@user.index, batonPass, false)
|
||||
PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) will use Baton Pass to avoid Perish Song")
|
||||
if batonPass >= 0 && @battle.pbRegisterMove(battler.index, batonPass, false)
|
||||
PBDebug.log("[AI] #{battler.pbThis} (#{battler.index}) will use Baton Pass to avoid Perish Song")
|
||||
return true
|
||||
end
|
||||
if @battle.pbRegisterSwitch(@user.index, list[0])
|
||||
PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) will switch with " +
|
||||
@battle.pbParty(@user.index)[list[0]].name)
|
||||
if @battle.pbRegisterSwitch(battler.index, list[0])
|
||||
PBDebug.log("[AI] #{battler.pbThis} (#{battler.index}) will switch with " +
|
||||
@battle.pbParty(battler.index)[list[0]].name)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,93 +1,10 @@
|
||||
class Battle::AI
|
||||
#=============================================================================
|
||||
# Main move-choosing method (moves with higher scores are more likely to be
|
||||
# chosen)
|
||||
#=============================================================================
|
||||
def pbChooseMove(choices)
|
||||
# Figure out useful information about the choices
|
||||
totalScore = 0
|
||||
maxScore = 0
|
||||
choices.each do |c|
|
||||
totalScore += c[1]
|
||||
maxScore = c[1] if maxScore < c[1]
|
||||
end
|
||||
|
||||
# Find any preferred moves and just choose from them
|
||||
if skill_check(AILevel.high) && maxScore > 100
|
||||
stDev = pbStdDev(choices)
|
||||
if stDev >= 40 && pbAIRandom(100) < 90
|
||||
preferredMoves = []
|
||||
choices.each do |c|
|
||||
next if c[1] < 200 && c[1] < maxScore * 0.8
|
||||
preferredMoves.push(c)
|
||||
preferredMoves.push(c) if c[1] == maxScore # Doubly prefer the best move
|
||||
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(@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_check(AILevel.high)
|
||||
badMoves = false
|
||||
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
|
||||
badMoves = true
|
||||
choices.each do |c|
|
||||
next if !@user.moves[c[0]].damagingMove?
|
||||
badMoves = false
|
||||
break
|
||||
end
|
||||
badMoves = false if badMoves && pbAIRandom(100) < 10
|
||||
end
|
||||
if badMoves && pbEnemyShouldWithdrawEx?(true)
|
||||
if $INTERNAL
|
||||
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?(@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)
|
||||
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(@user.index, c[0], false)
|
||||
@battle.pbRegisterTarget(@user.index, c[2]) if c[2] >= 0
|
||||
break
|
||||
end
|
||||
# Log the result
|
||||
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
|
||||
battler = @user.battler
|
||||
# Get scores and targets for each move
|
||||
choices = []
|
||||
# TODO: Split this into two, the first part being the calculation of all
|
||||
@@ -98,9 +15,9 @@ class Battle::AI
|
||||
# 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
|
||||
battler.eachMoveWithIndex do |_m, i|
|
||||
next if !@battle.pbCanChooseMove?(battler.index, i, false)
|
||||
if @user.wild?
|
||||
pbRegisterMoveWild(i, choices)
|
||||
else
|
||||
pbRegisterMoveTrainer(i, choices)
|
||||
@@ -108,9 +25,9 @@ class Battle::AI
|
||||
end
|
||||
# Log the available choices
|
||||
if $INTERNAL
|
||||
logMsg = "[AI] Move choices for #{@user.pbThis(true)} (#{@user.index}): "
|
||||
logMsg = "[AI] Move choices for #{battler.pbThis(true)} (#{battler.index}): "
|
||||
choices.each_with_index do |c, i|
|
||||
logMsg += "#{@user.moves[c[0]].name}=#{c[1]}"
|
||||
logMsg += "#{battler.moves[c[0]].name}=#{c[1]}"
|
||||
logMsg += " (target #{c[2]})" if c[2] >= 0
|
||||
logMsg += ", " if i < choices.length-1
|
||||
end
|
||||
@@ -124,40 +41,42 @@ class Battle::AI
|
||||
#=============================================================================
|
||||
# Wild Pokémon choose their moves randomly.
|
||||
def pbRegisterMoveWild(idxMove, choices)
|
||||
battler = @user.battler
|
||||
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
|
||||
score *= 2 if battler.pokemon.personalID % battler.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(idxMove, choices)
|
||||
move = @user.moves[idxMove]
|
||||
target_data = move.pbTarget(@user)
|
||||
battler = @user.battler
|
||||
move = battler.moves[idxMove]
|
||||
target_data = move.pbTarget(battler)
|
||||
# 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)
|
||||
score = pbGetMoveScore(move)
|
||||
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)
|
||||
next if !@battle.pbMoveCanTarget?(battler.index, b.index, target_data)
|
||||
score = pbGetMoveScore(move, b)
|
||||
totalScore += ((@user.opposes?(b)) ? score : -score)
|
||||
totalScore += ((battler.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)
|
||||
next if !@battle.pbMoveCanTarget?(battler.index, b.index, target_data)
|
||||
next if target_data.targets_foe && !battler.opposes?(b)
|
||||
score = pbGetMoveScore(move, b)
|
||||
scoresAndTargets.push([score, b.index]) if score > 0
|
||||
end
|
||||
@@ -170,21 +89,15 @@ class Battle::AI
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Set some class variables for the move being assessed
|
||||
# Set some extra class variables for the move/target combo being assessed
|
||||
#=============================================================================
|
||||
def set_up_move_check(move, target)
|
||||
@move = move
|
||||
@target = target
|
||||
# TODO: Calculate pbRoughType once here.
|
||||
@move.set_up(move, @user)
|
||||
@target = (target) ? @battlers[target.index] : @user
|
||||
@target&.refresh_battler
|
||||
# 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
|
||||
@user_faster = @user.faster_than?(@target)
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
@@ -192,9 +105,11 @@ class Battle::AI
|
||||
#=============================================================================
|
||||
def pbGetMoveScore(move, target = nil)
|
||||
set_up_move_check(move, target)
|
||||
user_battler = @user.battler
|
||||
target_battler = @target.battler
|
||||
|
||||
# Get the base score for the move
|
||||
if @move.damagingMove?
|
||||
if @move.move.damagingMove?
|
||||
# Is also the predicted damage amount as a percentage of target's current HP
|
||||
score = pbGetDamagingMoveBaseScore
|
||||
else # Status moves
|
||||
@@ -202,8 +117,8 @@ class Battle::AI
|
||||
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)
|
||||
score = Battle::AI::Handlers.apply_move_effect_score(@move.move.function,
|
||||
score, @move.move, user_battler, target_battler, self, @battle)
|
||||
|
||||
# A score of 0 here means it absolutely should not be used
|
||||
return 0 if score <= 0
|
||||
@@ -230,7 +145,7 @@ class Battle::AI
|
||||
|
||||
# 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)
|
||||
if @move.move.danceMove? && @target.has_active_ability?(:DANCER)
|
||||
score /= 2
|
||||
end
|
||||
|
||||
@@ -275,47 +190,46 @@ class Battle::AI
|
||||
# => 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 @trainer.medium_skill? && @battle.pbAbleNonActiveCount(user_battler.idxOwnSide) == 0 &&
|
||||
!(@trainer.high_skill? && @battle.pbAbleNonActiveCount(target_battler.idxOwnSide) > 0)
|
||||
if @move.move.statusMove?
|
||||
score *= 0.9
|
||||
elsif target_battler.hp <= target_battler.totalhp / 2
|
||||
score *= 1.1
|
||||
end
|
||||
end
|
||||
|
||||
# Prefer damaging moves if AI has no more Pokémon or AI is less clever
|
||||
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
|
||||
# Don't prefer attacking the target if they'd be semi-invulnerable
|
||||
if @move.accuracy > 0 && @user_faster &&
|
||||
(target_battler.semiInvulnerable? || target_battler.effects[PBEffects::SkyDrop] >= 0)
|
||||
miss = true
|
||||
miss = false if @user.has_active_ability?(:NOGUARD)
|
||||
miss = false if @trainer.best_skill? && @target.has_active_ability?(:NOGUARD)
|
||||
if @trainer.best_skill? && miss
|
||||
# Knows what can get past semi-invulnerability
|
||||
if target_battler.effects[PBEffects::SkyDrop] >= 0 ||
|
||||
target_battler.inTwoTurnAttack?("TwoTurnAttackInvulnerableInSky",
|
||||
"TwoTurnAttackInvulnerableInSkyParalyzeTarget",
|
||||
"TwoTurnAttackInvulnerableInSkyTargetCannotAct")
|
||||
miss = false if move.hitsFlyingTargets?
|
||||
elsif target.inTwoTurnAttack?("TwoTurnAttackInvulnerableUnderground")
|
||||
miss = false if move.hitsDiggingTargets?
|
||||
elsif target.inTwoTurnAttack?("TwoTurnAttackInvulnerableUnderwater")
|
||||
miss = false if move.hitsDivingTargets?
|
||||
end
|
||||
end
|
||||
score = 10 if miss
|
||||
end
|
||||
|
||||
# Don't prefer attacking the target if they'd be semi-invulnerable
|
||||
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)
|
||||
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",
|
||||
"TwoTurnAttackInvulnerableInSkyParalyzeTarget",
|
||||
"TwoTurnAttackInvulnerableInSkyTargetCannotAct")
|
||||
miss = false if move.hitsFlyingTargets?
|
||||
elsif target.inTwoTurnAttack?("TwoTurnAttackInvulnerableUnderground")
|
||||
miss = false if move.hitsDiggingTargets?
|
||||
elsif target.inTwoTurnAttack?("TwoTurnAttackInvulnerableUnderwater")
|
||||
miss = false if move.hitsDivingTargets?
|
||||
end
|
||||
end
|
||||
score = 0 if miss
|
||||
end
|
||||
|
||||
# Pick a good move for the Choice items
|
||||
if @user.hasActiveItem?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF]) ||
|
||||
@user.hasActiveAbility?(:GORILLATACTICS)
|
||||
# Pick a good move for the Choice items
|
||||
if @trainer.medium_skill?
|
||||
if @user.has_active_item?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF]) ||
|
||||
@user.has_active_ability?(:GORILLATACTICS)
|
||||
# Really don't prefer status moves (except Trick)
|
||||
score *= 0.1 if @move.statusMove? && @move.function != "UserTargetSwapItems"
|
||||
score *= 0.1 if @move.move.statusMove? && @move.move.function != "UserTargetSwapItems"
|
||||
# Don't prefer moves of certain types
|
||||
move_type = pbRoughType(@move)
|
||||
move_type = @move.rough_type
|
||||
# Most unpreferred types are 0x effective against another type, except
|
||||
# Fire/Water/Grass
|
||||
# TODO: Actually check through the types for 0x instead of hardcoding
|
||||
@@ -330,38 +244,38 @@ class Battle::AI
|
||||
# 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
|
||||
score *= 0.9 if @move.move.pp < 6
|
||||
end
|
||||
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 asleep, don't prefer moves that can't be used while asleep
|
||||
if @trainer.medium_skill? && user_battler.asleep? && user_battler.statusCount > 1 &&
|
||||
!@move.move.usableWhenAsleep?
|
||||
score *= 0.2
|
||||
end
|
||||
|
||||
# If user is frozen, prefer a move that can thaw the user
|
||||
if skill_check(AILevel.medium) && @user.status == :FROZEN
|
||||
if @move.thawsUser?
|
||||
score += 30
|
||||
else
|
||||
@user.eachMove do |m|
|
||||
next unless m.thawsUser?
|
||||
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
|
||||
if pbRoughType(@move) == :FIRE || (Settings::MECHANICS_GENERATION >= 6 && @move.thawsUser?)
|
||||
score *= 0.1
|
||||
# If user is frozen, prefer a move that can thaw the user
|
||||
if @trainer.medium_skill? && user_battler.status == :FROZEN
|
||||
if @move.move.thawsUser?
|
||||
score += 30
|
||||
else
|
||||
user_battler.eachMove do |m|
|
||||
next unless m.thawsUser?
|
||||
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 @trainer.medium_skill? && target_battler.status == :FROZEN
|
||||
if @move.rough_type == :FIRE || (Settings::MECHANICS_GENERATION >= 6 && @move.move.thawsUser?)
|
||||
score *= 0.1
|
||||
end
|
||||
end
|
||||
|
||||
# Don't prefer hitting a wild shiny Pokémon
|
||||
if @battle.wildBattle? && @target.opposes? && @target.shiny?
|
||||
if @target.wild? && target_battler.shiny?
|
||||
score *= 0.15
|
||||
end
|
||||
|
||||
@@ -369,18 +283,18 @@ class Battle::AI
|
||||
# Bounce.
|
||||
|
||||
# Account for accuracy of move
|
||||
accuracy = pbRoughAccuracy(@move, @target)
|
||||
accuracy = @move.rough_accuracy
|
||||
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)))
|
||||
if @trainer.medium_skill?
|
||||
if !@target.has_active_ability?([:INNERFOCUS, :SHIELDDUST]) &&
|
||||
target_battler.effects[PBEffects::Substitute] == 0
|
||||
if @move.move.flinchingMove? ||
|
||||
(@move.move.damagingMove? &&
|
||||
(@user.has_active_item?([:KINGSROCK, :RAZORFANG]) ||
|
||||
@user.has_active_ability?(:STENCH)))
|
||||
score *= 1.3
|
||||
end
|
||||
end
|
||||
@@ -388,14 +302,14 @@ class Battle::AI
|
||||
|
||||
# # Adjust score based on how much damage it can deal
|
||||
# if move.damagingMove?
|
||||
# score = pbGetMoveScoreDamage(score, move, @user, @target, @skill)
|
||||
# score = pbGetMoveScoreDamage(score, move, @user, @target, @trainer.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)
|
||||
# score = 0 if score <= 10 && @trainer.high_skill?
|
||||
# end
|
||||
score = score.to_i
|
||||
score = 0 if score < 0
|
||||
@@ -408,21 +322,23 @@ class Battle::AI
|
||||
#=============================================================================
|
||||
def pbGetDamagingMoveBaseScore
|
||||
# Don't prefer moves that are ineffective because of abilities or effects
|
||||
return 0 if pbCheckMoveImmunity(@move, @target)
|
||||
return 0 if @target.immune_to_move?
|
||||
user_battler = @user.battler
|
||||
target_battler = @target.battler
|
||||
|
||||
# Calculate how much damage the move will do (roughly)
|
||||
base_damage = pbMoveBaseDamage(@move, @target)
|
||||
base_damage = @move.base_power
|
||||
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
|
||||
calc_damage /= 2 if @move.chargingTurnMove?
|
||||
calc_damage /= 2 if @move.move.chargingTurnMove?
|
||||
|
||||
# TODO: Maybe move this check elsewhere?
|
||||
# Increased critical hit rate
|
||||
if skill_check(AILevel.medium)
|
||||
crit_stage = pbRoughCriticalHitStage(@move, @target)
|
||||
if @trainer.medium_skill?
|
||||
crit_stage = @move.rough_critical_hit_stage
|
||||
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
|
||||
@@ -431,13 +347,13 @@ class Battle::AI
|
||||
end
|
||||
|
||||
# Convert damage to percentage of target's remaining HP
|
||||
damage_percentage = calc_damage * 100.0 / @target.hp
|
||||
damage_percentage = calc_damage * 100.0 / target_battler.hp
|
||||
|
||||
# Don't prefer weak attacks
|
||||
# damage_percentage /= 2 if damage_percentage < 20
|
||||
|
||||
# Prefer damaging attack if level difference is significantly high
|
||||
# damage_percentage *= 1.2 if @user.level - 10 > @target.level
|
||||
# damage_percentage *= 1.2 if user_battler.level - 10 > target_battler.level
|
||||
|
||||
# Adjust score
|
||||
damage_percentage = 110 if damage_percentage > 110 # Treat all lethal moves the same
|
||||
@@ -447,7 +363,7 @@ class Battle::AI
|
||||
end
|
||||
|
||||
def pbGetStatusMoveBaseScore
|
||||
# TODO: Call pbCheckMoveImmunity here too, not just for damaging moves
|
||||
# TODO: Call @target.immune_to_move? here too, not just for damaging moves
|
||||
# (only if this status move will be affected).
|
||||
|
||||
# TODO: Make sure all status moves are accounted for.
|
||||
@@ -465,7 +381,7 @@ class Battle::AI
|
||||
# "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
|
||||
case @move.move.function
|
||||
when "ConfuseTarget",
|
||||
"LowerTargetAccuracy1",
|
||||
"LowerTargetEvasion1RemoveSideEffects",
|
||||
@@ -690,4 +606,90 @@ class Battle::AI
|
||||
# "ProtectUserSideFromStatusMoves"
|
||||
return 0
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Make the final choice of which move to use depending on the calculated
|
||||
# scores for each move. Moves with higher scores are more likely to be chosen.
|
||||
#=============================================================================
|
||||
def pbChooseMove(choices)
|
||||
user_battler = @user.battler
|
||||
|
||||
# Figure out useful information about the choices
|
||||
totalScore = 0
|
||||
maxScore = 0
|
||||
choices.each do |c|
|
||||
totalScore += c[1]
|
||||
maxScore = c[1] if maxScore < c[1]
|
||||
end
|
||||
|
||||
# Find any preferred moves and just choose from them
|
||||
if @trainer.high_skill? && maxScore > 100
|
||||
stDev = pbStdDev(choices)
|
||||
if stDev >= 40 && pbAIRandom(100) < 90
|
||||
preferredMoves = []
|
||||
choices.each do |c|
|
||||
next if c[1] < 200 && c[1] < maxScore * 0.8
|
||||
preferredMoves.push(c)
|
||||
preferredMoves.push(c) if c[1] == maxScore # Doubly prefer the best move
|
||||
end
|
||||
if preferredMoves.length > 0
|
||||
m = preferredMoves[pbAIRandom(preferredMoves.length)]
|
||||
PBDebug.log("[AI] #{user_battler.pbThis} (#{user_battler.index}) prefers #{user_battler.moves[m[0]].name}")
|
||||
@battle.pbRegisterMove(user_battler.index, m[0], false)
|
||||
@battle.pbRegisterTarget(user_battler.index, m[2]) if m[2] >= 0
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Decide whether all choices are bad, and if so, try switching instead
|
||||
if @trainer.high_skill? && @user.can_switch_lax?
|
||||
badMoves = false
|
||||
if (maxScore <= 20 && user_battler.turnCount > 2) ||
|
||||
(maxScore <= 40 && user_battler.turnCount > 5)
|
||||
badMoves = true if pbAIRandom(100) < 80
|
||||
end
|
||||
if !badMoves && totalScore < 100 && user_battler.turnCount > 1
|
||||
badMoves = true
|
||||
choices.each do |c|
|
||||
next if !user_battler.moves[c[0]].damagingMove?
|
||||
badMoves = false
|
||||
break
|
||||
end
|
||||
badMoves = false if badMoves && pbAIRandom(100) < 10
|
||||
end
|
||||
if badMoves && pbEnemyShouldWithdrawEx?(true)
|
||||
if $INTERNAL
|
||||
PBDebug.log("[AI] #{user_battler.pbThis} (#{user_battler.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_battler.pbThis} (#{user_battler.index}) doesn't want to use any moves; picking one at random")
|
||||
user_battler.eachMoveWithIndex do |_m, i|
|
||||
next if !@battle.pbCanChooseMove?(user_battler.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_battler.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(user_battler.index, c[0], false)
|
||||
@battle.pbRegisterTarget(user_battler.index, c[2]) if c[2] >= 0
|
||||
break
|
||||
end
|
||||
# Log the result
|
||||
if @battle.choices[user_battler.index][2]
|
||||
PBDebug.log("[AI] #{user_battler.pbThis} (#{user_battler.index}) will use #{@battle.choices[user_battler.index][2].name}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
14
Data/Scripts/011_Battle/005_AI/005_AI_MegaEvolve.rb
Normal file
14
Data/Scripts/011_Battle/005_AI/005_AI_MegaEvolve.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
class Battle::AI
|
||||
#=============================================================================
|
||||
# Decide whether the opponent should Mega Evolve.
|
||||
#=============================================================================
|
||||
# TODO: Where relevant, pretend the user is Mega Evolved if it isn't but can
|
||||
# be.
|
||||
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
|
||||
end
|
||||
@@ -1,326 +1,72 @@
|
||||
class Battle::AI
|
||||
#=============================================================================
|
||||
#
|
||||
#=============================================================================
|
||||
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 }
|
||||
when :UserAndAllies
|
||||
@battle.allSameSideBattlers(@user).each { |_b| num_targets += 1 }
|
||||
when :AllNearFoes
|
||||
@battle.allOtherSideBattlers(@user).each { |b| num_targets += 1 if b.near?(@user) }
|
||||
when :AllFoes
|
||||
@battle.allOtherSideBattlers(@user).each { |_b| num_targets += 1 }
|
||||
when :AllNearOthers
|
||||
@battle.allBattlers.each { |b| num_targets += 1 if b.near?(@user) }
|
||||
when :AllBattlers
|
||||
@battle.allBattlers.each { |_b| num_targets += 1 }
|
||||
def pbAIRandom(x); return rand(x); end
|
||||
|
||||
def pbStdDev(choices)
|
||||
sum = 0
|
||||
n = 0
|
||||
choices.each do |c|
|
||||
sum += c[1]
|
||||
n += 1
|
||||
end
|
||||
return num_targets > 1
|
||||
return 0 if n < 2
|
||||
mean = sum.to_f / n
|
||||
varianceTimesN = 0
|
||||
choices.each do |c|
|
||||
next if c[1] <= 0
|
||||
deviation = c[1].to_f - mean
|
||||
varianceTimesN += deviation * deviation
|
||||
end
|
||||
# Using population standard deviation
|
||||
# [(n-1) makes it a sample std dev, would be 0 with only 1 sample]
|
||||
return Math.sqrt(varianceTimesN / n)
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Move's type effectiveness
|
||||
#=============================================================================
|
||||
def pbCalcTypeModSingle(moveType, defType, user, target)
|
||||
ret = Effectiveness.calculate_one(moveType, defType)
|
||||
if Effectiveness.ineffective_type?(moveType, defType)
|
||||
# Ring Target
|
||||
if target.hasActiveItem?(:RINGTARGET)
|
||||
ret = Effectiveness::NORMAL_EFFECTIVE_ONE
|
||||
end
|
||||
# Foresight
|
||||
if (user.hasActiveAbility?(:SCRAPPY) || target.effects[PBEffects::Foresight]) &&
|
||||
defType == :GHOST
|
||||
ret = Effectiveness::NORMAL_EFFECTIVE_ONE
|
||||
end
|
||||
# Miracle Eye
|
||||
if target.effects[PBEffects::MiracleEye] && defType == :DARK
|
||||
ret = Effectiveness::NORMAL_EFFECTIVE_ONE
|
||||
end
|
||||
elsif Effectiveness.super_effective_type?(moveType, defType)
|
||||
# Delta Stream's weather
|
||||
if target.effectiveWeather == :StrongWinds && defType == :FLYING
|
||||
ret = Effectiveness::NORMAL_EFFECTIVE_ONE
|
||||
end
|
||||
end
|
||||
# Grounded Flying-type Pokémon become susceptible to Ground moves
|
||||
if !target.airborne? && defType == :FLYING && moveType == :GROUND
|
||||
ret = Effectiveness::NORMAL_EFFECTIVE_ONE
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def pbCalcTypeMod(moveType, user, target)
|
||||
return Effectiveness::NORMAL_EFFECTIVE if !moveType
|
||||
return Effectiveness::NORMAL_EFFECTIVE if moveType == :GROUND &&
|
||||
target.pbHasType?(:FLYING) &&
|
||||
target.hasActiveItem?(:IRONBALL)
|
||||
# Determine types
|
||||
tTypes = target.pbTypes(true)
|
||||
# Get effectivenesses
|
||||
typeMods = [Effectiveness::NORMAL_EFFECTIVE_ONE] * 3 # 3 types max
|
||||
if moveType == :SHADOW
|
||||
if target.shadowPokemon?
|
||||
typeMods[0] = Effectiveness::NOT_VERY_EFFECTIVE_ONE
|
||||
else
|
||||
typeMods[0] = Effectiveness::SUPER_EFFECTIVE_ONE
|
||||
end
|
||||
else
|
||||
tTypes.each_with_index do |type, i|
|
||||
typeMods[i] = pbCalcTypeModSingle(moveType, type, user, target)
|
||||
end
|
||||
end
|
||||
# Multiply all effectivenesses together
|
||||
ret = 1
|
||||
typeMods.each { |m| ret *= m }
|
||||
return ret
|
||||
end
|
||||
|
||||
# For switching. Determines the effectiveness of a potential switch-in against
|
||||
# an opposing battler.
|
||||
def pbCalcTypeModPokemon(pkmn, target)
|
||||
mod1 = Effectiveness.calculate(pkmn.types[0], target.types[0], target.types[1])
|
||||
def pbCalcTypeModPokemon(pkmn, target_battler)
|
||||
mod1 = Effectiveness.calculate(pkmn.types[0], target_battler.types[0], target_battler.types[1])
|
||||
mod2 = Effectiveness::NORMAL_EFFECTIVE
|
||||
if pkmn.types.length > 1
|
||||
mod2 = Effectiveness.calculate(pkmn.types[1], target.types[0], target.types[1])
|
||||
mod2 = Effectiveness.calculate(pkmn.types[1], target_battler.types[0], target_battler.types[1])
|
||||
mod2 = mod2.to_f / Effectivenesss::NORMAL_EFFECTIVE
|
||||
end
|
||||
return mod1 * mod2
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Immunity to a move because of the target's ability, item or other effects
|
||||
#=============================================================================
|
||||
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)
|
||||
# Immunity due to ability/item/other effects
|
||||
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)
|
||||
when :WATER
|
||||
return true if target.hasActiveAbility?([:DRYSKIN, :STORMDRAIN, :WATERABSORB])
|
||||
when :GRASS
|
||||
return true if target.hasActiveAbility?(:SAPSIPPER)
|
||||
when :ELECTRIC
|
||||
return true if target.hasActiveAbility?([:LIGHTNINGROD, :MOTORDRIVE, :VOLTABSORB])
|
||||
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) &&
|
||||
target.hasActiveAbility?(:TELEPATHY)
|
||||
return true if move.statusMove? && move.canMagicCoat? && target.hasActiveAbility?(:MAGICBOUNCE) &&
|
||||
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 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
|
||||
return true if move.statusMove? && Settings::MECHANICS_GENERATION >= 7 &&
|
||||
@user.hasActiveAbility?(:PRANKSTER) && target.pbHasType?(:DARK) &&
|
||||
target.opposes?(@user)
|
||||
return true if move.priority > 0 && @battle.field.terrain == :Psychic &&
|
||||
target.affectedByTerrain? && target.opposes?(@user)
|
||||
# TODO: Dazzling/Queenly Majesty go here.
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Get approximate properties for a battler
|
||||
#=============================================================================
|
||||
def pbRoughType(move)
|
||||
ret = move.type
|
||||
ret = move.pbCalcType(@user) if skill_check(AILevel.high)
|
||||
return ret
|
||||
end
|
||||
|
||||
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
|
||||
value = 0
|
||||
case stat
|
||||
when :ATTACK then value = battler.attack
|
||||
when :DEFENSE then value = battler.defense
|
||||
when :SPECIAL_ATTACK then value = battler.spatk
|
||||
when :SPECIAL_DEFENSE then value = battler.spdef
|
||||
when :SPEED then value = battler.speed
|
||||
end
|
||||
return (value.to_f * stageMul[stage] / stageDiv[stage]).floor
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Get a better move's base damage value
|
||||
#=============================================================================
|
||||
def pbMoveBaseDamage(move, target)
|
||||
baseDmg = move.baseDamage
|
||||
baseDmg = 60 if baseDmg == 1
|
||||
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)
|
||||
when "FixedDamageUserLevelRandom" # Psywave
|
||||
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)
|
||||
# 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,
|
||||
# Trump Card, Flail, Electro Ball, Low Kick, Fling, Spit Up
|
||||
when "DoublePowerIfTargetInSky",
|
||||
"FlinchTargetDoublePowerIfTargetInSky",
|
||||
"DoublePowerIfTargetPoisoned",
|
||||
"DoublePowerIfTargetParalyzedCureTarget",
|
||||
"DoublePowerIfTargetAsleepCureTarget",
|
||||
"DoublePowerIfUserPoisonedBurnedParalyzed",
|
||||
"DoublePowerIfTargetStatusProblem",
|
||||
"DoublePowerIfTargetHPLessThanHalf",
|
||||
"DoublePowerIfAllyFaintedLastTurn",
|
||||
"TypeAndPowerDependOnWeather",
|
||||
"PowerHigherWithUserHappiness",
|
||||
"PowerLowerWithUserHappiness",
|
||||
"PowerHigherWithUserHP",
|
||||
"PowerHigherWithTargetHP",
|
||||
"PowerHigherWithUserPositiveStatStages",
|
||||
"PowerHigherWithTargetPositiveStatStages",
|
||||
"TypeDependsOnUserIVs",
|
||||
"PowerHigherWithConsecutiveUse",
|
||||
"PowerHigherWithConsecutiveUseOnUserSide",
|
||||
"PowerHigherWithLessPP",
|
||||
"PowerLowerWithUserHP",
|
||||
"PowerHigherWithUserFasterThanTarget",
|
||||
"PowerHigherWithTargetWeight",
|
||||
"ThrowUserItemAtTarget",
|
||||
"PowerDependsOnUserStockpile"
|
||||
baseDmg = move.pbBaseDamage(baseDmg, @user, target)
|
||||
when "DoublePowerIfUserHasNoItem" # Acrobatics
|
||||
baseDmg *= 2 if !@user.item || @user.hasActiveItem?(:FLYINGGEM)
|
||||
when "PowerHigherWithTargetFasterThanUser" # Gyro Ball
|
||||
targetSpeed = pbRoughStat(target, :SPEED)
|
||||
userSpeed = pbRoughStat(@user, :SPEED)
|
||||
baseDmg = [[(25 * targetSpeed / userSpeed).floor, 150].min, 1].max
|
||||
when "RandomlyDamageOrHealTarget" # Present
|
||||
baseDmg = 50
|
||||
when "RandomPowerDoublePowerIfTargetUnderground" # Magnitude
|
||||
baseDmg = 71
|
||||
baseDmg *= 2 if target.inTwoTurnAttack?("TwoTurnAttackInvulnerableUnderground") # Dig
|
||||
when "TypeAndPowerDependOnUserBerry" # Natural Gift
|
||||
baseDmg = move.pbNaturalGiftBaseDamage(@user.item_id)
|
||||
when "PowerHigherWithUserHeavierThanTarget" # Heavy Slam
|
||||
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)
|
||||
baseDmg *= 5
|
||||
else
|
||||
baseDmg = (baseDmg * 31 / 10).floor # Average damage dealt
|
||||
end
|
||||
when "HitTwoToFiveTimesOrThreeForAshGreninja"
|
||||
if @user.isSpecies?(:GRENINJA) && @user.form == 2
|
||||
baseDmg *= 4 # 3 hits at 20 power = 4 hits at 15 power
|
||||
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|
|
||||
mult += 1 if pkmn&.able? && pkmn.status == :NONE
|
||||
end
|
||||
baseDmg *= mult
|
||||
when "TwoTurnAttackOneTurnInSun" # Solar Beam
|
||||
baseDmg = move.pbBaseDamageMultiplier(baseDmg, @user, target)
|
||||
when "MultiTurnAttackPowersUpEachTurn" # Rollout
|
||||
baseDmg *= 2 if @user.effects[PBEffects::DefenseCurl]
|
||||
when "MultiTurnAttackBideThenReturnDoubleDamage" # Bide
|
||||
baseDmg = 40
|
||||
when "UserFaintsFixedDamageUserHP" # Final Gambit
|
||||
baseDmg = @user.hp
|
||||
when "EffectivenessIncludesFlyingType" # Flying Press
|
||||
if GameData::Type.exists?(:FLYING)
|
||||
if skill_check(AILevel.high)
|
||||
targetTypes = target.pbTypes(true)
|
||||
mult = Effectiveness.calculate(
|
||||
:FLYING, targetTypes[0], targetTypes[1], targetTypes[2]
|
||||
)
|
||||
else
|
||||
mult = Effectiveness.calculate(
|
||||
:FLYING, target.types[0], target.types[1], target.effects[PBEffects::Type3]
|
||||
)
|
||||
end
|
||||
baseDmg = (baseDmg.to_f * mult / Effectiveness::NORMAL_EFFECTIVE).round
|
||||
end
|
||||
baseDmg *= 2 if skill_check(AILevel.medium) && target.effects[PBEffects::Minimize]
|
||||
when "DoublePowerIfUserLastMoveFailed" # Stomping Tantrum
|
||||
baseDmg *= 2 if @user.lastRoundMoveFailed
|
||||
when "HitTwoTimesFlinchTarget" # Double Iron Bash
|
||||
baseDmg *= 2
|
||||
baseDmg *= 2 if skill_check(AILevel.medium) && target.effects[PBEffects::Minimize]
|
||||
end
|
||||
return baseDmg
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Damage calculation
|
||||
#=============================================================================
|
||||
def pbRoughDamage(move, target, baseDmg)
|
||||
# Fixed damage moves
|
||||
return baseDmg if move.is_a?(Battle::Move::FixedDamageMove)
|
||||
return baseDmg if move.move.is_a?(Battle::Move::FixedDamageMove)
|
||||
|
||||
user_battler = @user.battler
|
||||
target_battler = target.battler
|
||||
|
||||
# Get the move's type
|
||||
type = pbRoughType(move)
|
||||
type = move.rough_type
|
||||
|
||||
##### Calculate user's attack stat #####
|
||||
atk = pbRoughStat(@user, :ATTACK)
|
||||
if move.function == "UseTargetAttackInsteadOfUserAttack" # Foul Play
|
||||
atk = pbRoughStat(target, :ATTACK)
|
||||
elsif move.function == "UseUserBaseDefenseInsteadOfUserBaseAttack" # Body Press
|
||||
atk = pbRoughStat(@user, :DEFENSE)
|
||||
elsif move.specialMove?(type)
|
||||
if move.function == "UseTargetAttackInsteadOfUserAttack" # Foul Play
|
||||
atk = pbRoughStat(target, :SPECIAL_ATTACK)
|
||||
atk = @user.rough_stat(:ATTACK)
|
||||
if move.move.function == "UseTargetAttackInsteadOfUserAttack" # Foul Play
|
||||
atk = target.rough_stat(:ATTACK)
|
||||
elsif move.move.function == "UseUserBaseDefenseInsteadOfUserBaseAttack" # Body Press
|
||||
atk = @user.rough_stat(:DEFENSE)
|
||||
elsif move.move.specialMove?(type)
|
||||
if move.move.function == "UseTargetAttackInsteadOfUserAttack" # Foul Play
|
||||
atk = target.rough_stat(:SPECIAL_ATTACK)
|
||||
else
|
||||
atk = pbRoughStat(@user, :SPECIAL_ATTACK)
|
||||
atk = @user.rough_stat(:SPECIAL_ATTACK)
|
||||
end
|
||||
end
|
||||
|
||||
##### Calculate target's defense stat #####
|
||||
defense = pbRoughStat(target, :DEFENSE)
|
||||
if move.specialMove?(type) && move.function != "UseTargetDefenseInsteadOfTargetSpDef" # Psyshock
|
||||
defense = pbRoughStat(target, :SPECIAL_DEFENSE)
|
||||
defense = target.rough_stat(:DEFENSE)
|
||||
if move.move.specialMove?(type) && move.move.function != "UseTargetDefenseInsteadOfTargetSpDef" # Psyshock
|
||||
defense = target.rough_stat(:SPECIAL_DEFENSE)
|
||||
end
|
||||
|
||||
##### Calculate all multiplier effects #####
|
||||
@@ -331,56 +77,56 @@ class Battle::AI
|
||||
:final_damage_multiplier => 1.0
|
||||
}
|
||||
# Ability effects that alter damage
|
||||
moldBreaker = skill_check(AILevel.high) && target.hasMoldBreaker?
|
||||
moldBreaker = @trainer.high_skill? && target_battler.hasMoldBreaker?
|
||||
|
||||
if skill_check(AILevel.medium) && @user.abilityActive?
|
||||
if @user.ability_active?
|
||||
# NOTE: These abilities aren't suitable for checking at the start of the
|
||||
# round.
|
||||
abilityBlacklist = [:ANALYTIC, :SNIPER, :TINTEDLENS, :AERILATE, :PIXILATE, :REFRIGERATE]
|
||||
canCheck = true
|
||||
abilityBlacklist.each do |m|
|
||||
next if move.id != m
|
||||
next if move.move.id != m
|
||||
canCheck = false
|
||||
break
|
||||
end
|
||||
if canCheck
|
||||
Battle::AbilityEffects.triggerDamageCalcFromUser(
|
||||
@user.ability, @user, target, move, multipliers, baseDmg, type
|
||||
user_battler.ability, user_battler, target_battler, move.move, multipliers, baseDmg, type
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
if skill_check(AILevel.medium) && !moldBreaker
|
||||
@user.allAllies.each do |b|
|
||||
if @trainer.medium_skill? && !moldBreaker
|
||||
user_battler.allAllies.each do |b|
|
||||
next if !b.abilityActive?
|
||||
Battle::AbilityEffects.triggerDamageCalcFromAlly(
|
||||
b.ability, @user, target, move, multipliers, baseDmg, type
|
||||
b.ability, user_battler, target_battler, move.move, multipliers, baseDmg, type
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
if skill_check(AILevel.best) && !moldBreaker && target.abilityActive?
|
||||
if !moldBreaker && target.ability_active?
|
||||
# NOTE: These abilities aren't suitable for checking at the start of the
|
||||
# round.
|
||||
abilityBlacklist = [:FILTER, :SOLIDROCK]
|
||||
canCheck = true
|
||||
abilityBlacklist.each do |m|
|
||||
next if move.id != m
|
||||
next if move.move.id != m
|
||||
canCheck = false
|
||||
break
|
||||
end
|
||||
if canCheck
|
||||
Battle::AbilityEffects.triggerDamageCalcFromTarget(
|
||||
target.ability, @user, target, move, multipliers, baseDmg, type
|
||||
target_battler.ability, user_battler, target_battler, move.move, multipliers, baseDmg, type
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
if skill_check(AILevel.best) && !moldBreaker
|
||||
target.allAllies.each do |b|
|
||||
if @trainer.high_skill? && !moldBreaker
|
||||
target_battler.allAllies.each do |b|
|
||||
next if !b.abilityActive?
|
||||
Battle::AbilityEffects.triggerDamageCalcFromTargetAlly(
|
||||
b.ability, @user, target, move, multipliers, baseDmg, type
|
||||
b.ability, user_battler, target_battler, move.move, multipliers, baseDmg, type
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -388,28 +134,27 @@ class Battle::AI
|
||||
# Item effects that alter damage
|
||||
# NOTE: Type-boosting gems aren't suitable for checking at the start of the
|
||||
# round.
|
||||
if skill_check(AILevel.medium) && @user.itemActive?
|
||||
if @user.item_active?
|
||||
# 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_battler.item_id)
|
||||
Battle::ItemEffects.triggerDamageCalcFromUser(
|
||||
@user.item, @user, target, move, multipliers, baseDmg, type
|
||||
user_battler.item, user_battler, target_battler, move.move, multipliers, baseDmg, type
|
||||
)
|
||||
@user.effects[PBEffects::GemConsumed] = nil # Untrigger consuming of Gems
|
||||
user_battler.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_check(AILevel.best) &&
|
||||
target.itemActive? && target.item && !target.item.is_berry?
|
||||
if target.item_active? && target_battler.item && !target_battler.item.is_berry?
|
||||
Battle::ItemEffects.triggerDamageCalcFromTarget(
|
||||
target.item, @user, target, move, multipliers, baseDmg, type
|
||||
target_battler.item, user_battler, target_battler, move.move, multipliers, baseDmg, type
|
||||
)
|
||||
end
|
||||
|
||||
# Global abilities
|
||||
if skill_check(AILevel.medium) &&
|
||||
if @trainer.medium_skill? &&
|
||||
((@battle.pbCheckGlobalAbility(:DARKAURA) && type == :DARK) ||
|
||||
(@battle.pbCheckGlobalAbility(:FAIRYAURA) && type == :FAIRY))
|
||||
if @battle.pbCheckGlobalAbility(:AURABREAK)
|
||||
@@ -420,7 +165,7 @@ class Battle::AI
|
||||
end
|
||||
|
||||
# Parental Bond
|
||||
if skill_check(AILevel.medium) && @user.hasActiveAbility?(:PARENTALBOND)
|
||||
if @user.has_active_ability?(:PARENTALBOND)
|
||||
multipliers[:base_damage_multiplier] *= 1.25
|
||||
end
|
||||
|
||||
@@ -430,13 +175,13 @@ class Battle::AI
|
||||
# Helping Hand - n/a
|
||||
|
||||
# Charge
|
||||
if skill_check(AILevel.medium) &&
|
||||
@user.effects[PBEffects::Charge] > 0 && type == :ELECTRIC
|
||||
if @trainer.medium_skill? &&
|
||||
user_battler.effects[PBEffects::Charge] > 0 && type == :ELECTRIC
|
||||
multipliers[:base_damage_multiplier] *= 2
|
||||
end
|
||||
|
||||
# Mud Sport and Water Sport
|
||||
if skill_check(AILevel.medium)
|
||||
if @trainer.medium_skill?
|
||||
if type == :ELECTRIC
|
||||
if @battle.allBattlers.any? { |b| b.effects[PBEffects::MudSport] }
|
||||
multipliers[:base_damage_multiplier] /= 3
|
||||
@@ -456,38 +201,38 @@ class Battle::AI
|
||||
end
|
||||
|
||||
# Terrain moves
|
||||
if skill_check(AILevel.medium)
|
||||
if @trainer.medium_skill?
|
||||
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_battler.affectedByTerrain?
|
||||
when :Grassy
|
||||
multipliers[:base_damage_multiplier] *= 1.5 if type == :GRASS && @user.affectedByTerrain?
|
||||
multipliers[:base_damage_multiplier] *= 1.5 if type == :GRASS && user_battler.affectedByTerrain?
|
||||
when :Psychic
|
||||
multipliers[:base_damage_multiplier] *= 1.5 if type == :PSYCHIC && @user.affectedByTerrain?
|
||||
multipliers[:base_damage_multiplier] *= 1.5 if type == :PSYCHIC && user_battler.affectedByTerrain?
|
||||
when :Misty
|
||||
multipliers[:base_damage_multiplier] /= 2 if type == :DRAGON && target.affectedByTerrain?
|
||||
multipliers[:base_damage_multiplier] /= 2 if type == :DRAGON && target_battler.affectedByTerrain?
|
||||
end
|
||||
end
|
||||
|
||||
# Badge multipliers
|
||||
if skill_check(AILevel.high) && @battle.internalBattle && target.pbOwnedByPlayer?
|
||||
if @trainer.high_skill? && @battle.internalBattle && target_battler.pbOwnedByPlayer?
|
||||
# Don't need to check the Atk/Sp Atk-boosting badges because the AI
|
||||
# won't control the player's Pokémon.
|
||||
if move.physicalMove?(type) && @battle.pbPlayer.badge_count >= Settings::NUM_BADGES_BOOST_DEFENSE
|
||||
if move.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
|
||||
elsif move.move.specialMove?(type) && @battle.pbPlayer.badge_count >= Settings::NUM_BADGES_BOOST_SPDEF
|
||||
multipliers[:defense_multiplier] *= 1.1
|
||||
end
|
||||
end
|
||||
|
||||
# Multi-targeting attacks
|
||||
if skill_check(AILevel.high) && pbTargetsMultiple?(move)
|
||||
if @trainer.high_skill? && move.targets_multiple_battlers?
|
||||
multipliers[:final_damage_multiplier] *= 0.75
|
||||
end
|
||||
|
||||
# Weather
|
||||
if skill_check(AILevel.medium)
|
||||
case @user.effectiveWeather
|
||||
if @trainer.medium_skill?
|
||||
case user_battler.effectiveWeather
|
||||
when :Sun, :HarshSun
|
||||
case type
|
||||
when :FIRE
|
||||
@@ -503,8 +248,8 @@ class Battle::AI
|
||||
multipliers[:final_damage_multiplier] *= 1.5
|
||||
end
|
||||
when :Sandstorm
|
||||
if target.pbHasType?(:ROCK) && move.specialMove?(type) &&
|
||||
move.function != "UseTargetDefenseInsteadOfTargetSpDef" # Psyshock
|
||||
if target.has_type?(:ROCK) && move.move.specialMove?(type) &&
|
||||
move.move.function != "UseTargetDefenseInsteadOfTargetSpDef" # Psyshock
|
||||
multipliers[:defense_multiplier] *= 1.5
|
||||
end
|
||||
end
|
||||
@@ -515,8 +260,8 @@ class Battle::AI
|
||||
# Random variance - n/a
|
||||
|
||||
# STAB
|
||||
if skill_check(AILevel.medium) && type && @user.pbHasType?(type)
|
||||
if @user.hasActiveAbility?(:ADAPTABILITY)
|
||||
if type && @user.has_type?(type)
|
||||
if @user.has_active_ability?(:ADAPTABILITY)
|
||||
multipliers[:final_damage_multiplier] *= 2
|
||||
else
|
||||
multipliers[:final_damage_multiplier] *= 1.5
|
||||
@@ -524,35 +269,33 @@ class Battle::AI
|
||||
end
|
||||
|
||||
# Type effectiveness
|
||||
if skill_check(AILevel.medium)
|
||||
typemod = pbCalcTypeMod(type, @user, target)
|
||||
multipliers[:final_damage_multiplier] *= typemod.to_f / Effectiveness::NORMAL_EFFECTIVE
|
||||
end
|
||||
typemod = target.effectiveness_of_type_against_battler(type, @user)
|
||||
multipliers[:final_damage_multiplier] *= typemod.to_f / Effectiveness::NORMAL_EFFECTIVE
|
||||
|
||||
# Burn
|
||||
if skill_check(AILevel.high) && move.physicalMove?(type) &&
|
||||
@user.status == :BURN && !@user.hasActiveAbility?(:GUTS) &&
|
||||
if @trainer.high_skill? && move.move.physicalMove?(type) &&
|
||||
user_battler.status == :BURN && !@user.has_active_ability?(:GUTS) &&
|
||||
!(Settings::MECHANICS_GENERATION >= 6 &&
|
||||
move.function == "DoublePowerIfUserPoisonedBurnedParalyzed") # Facade
|
||||
move.move.function == "DoublePowerIfUserPoisonedBurnedParalyzed") # Facade
|
||||
multipliers[:final_damage_multiplier] /= 2
|
||||
end
|
||||
|
||||
# Aurora Veil, Reflect, Light Screen
|
||||
if skill_check(AILevel.high) && !move.ignoresReflect? && !@user.hasActiveAbility?(:INFILTRATOR)
|
||||
if target.pbOwnSide.effects[PBEffects::AuroraVeil] > 0
|
||||
if @battle.pbSideBattlerCount(target) > 1
|
||||
if @trainer.medium_skill? && !move.move.ignoresReflect? && !@user.has_active_ability?(:INFILTRATOR)
|
||||
if target_battler.pbOwnSide.effects[PBEffects::AuroraVeil] > 0
|
||||
if @battle.pbSideBattlerCount(target_battler) > 1
|
||||
multipliers[:final_damage_multiplier] *= 2 / 3.0
|
||||
else
|
||||
multipliers[:final_damage_multiplier] /= 2
|
||||
end
|
||||
elsif target.pbOwnSide.effects[PBEffects::Reflect] > 0 && move.physicalMove?(type)
|
||||
if @battle.pbSideBattlerCount(target) > 1
|
||||
elsif target_battler.pbOwnSide.effects[PBEffects::Reflect] > 0 && move.move.physicalMove?(type)
|
||||
if @battle.pbSideBattlerCount(target_battler) > 1
|
||||
multipliers[:final_damage_multiplier] *= 2 / 3.0
|
||||
else
|
||||
multipliers[:final_damage_multiplier] /= 2
|
||||
end
|
||||
elsif target.pbOwnSide.effects[PBEffects::LightScreen] > 0 && move.specialMove?(type)
|
||||
if @battle.pbSideBattlerCount(target) > 1
|
||||
elsif target_battler.pbOwnSide.effects[PBEffects::LightScreen] > 0 && move.move.specialMove?(type)
|
||||
if @battle.pbSideBattlerCount(target_battler) > 1
|
||||
multipliers[:final_damage_multiplier] *= 2 / 3.0
|
||||
else
|
||||
multipliers[:final_damage_multiplier] /= 2
|
||||
@@ -561,7 +304,7 @@ class Battle::AI
|
||||
end
|
||||
|
||||
# Minimize
|
||||
if skill_check(AILevel.high) && target.effects[PBEffects::Minimize] && move.tramplesMinimize?
|
||||
if @trainer.medium_skill? && target_battler.effects[PBEffects::Minimize] && move.move.tramplesMinimize?
|
||||
multipliers[:final_damage_multiplier] *= 2
|
||||
end
|
||||
|
||||
@@ -575,156 +318,11 @@ class Battle::AI
|
||||
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_battler.level / 5) + 2).floor * baseDmg * atk / defense).floor / 50).floor + 2
|
||||
damage = [(damage * multipliers[:final_damage_multiplier]).round, 1].max
|
||||
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, target)
|
||||
# "Always hit" effects and "always hit" accuracy
|
||||
if skill_check(AILevel.medium)
|
||||
return 100 if target.effects[PBEffects::Minimize] && move.tramplesMinimize? &&
|
||||
Settings::MECHANICS_GENERATION >= 6
|
||||
return 100 if target.effects[PBEffects::Telekinesis] > 0
|
||||
end
|
||||
# Get base accuracy
|
||||
baseAcc = move.accuracy
|
||||
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)
|
||||
# Calculate all modifier effects
|
||||
modifiers = {}
|
||||
modifiers[:base_accuracy] = baseAcc
|
||||
modifiers[:accuracy_stage] = @user.stages[:ACCURACY]
|
||||
modifiers[:evasion_stage] = target.stages[:EVASION]
|
||||
modifiers[:accuracy_multiplier] = 1.0
|
||||
modifiers[:evasion_multiplier] = 1.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
|
||||
stageMul = [3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7, 8, 9]
|
||||
stageDiv = [9, 8, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3]
|
||||
accuracy = 100.0 * stageMul[accStage] / stageDiv[accStage]
|
||||
evasion = 100.0 * stageMul[evaStage] / stageDiv[evaStage]
|
||||
accuracy = (accuracy * modifiers[:accuracy_multiplier]).round
|
||||
evasion = (evasion * modifiers[:evasion_multiplier]).round
|
||||
evasion = 1 if evasion < 1
|
||||
return modifiers[:base_accuracy] * accuracy / evasion
|
||||
end
|
||||
|
||||
def pbCalcAccuracyModifiers(target, modifiers, move, type)
|
||||
moldBreaker = (skill_check(AILevel.medium) && target.hasMoldBreaker?)
|
||||
# Ability effects that alter accuracy calculation
|
||||
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
|
||||
)
|
||||
end
|
||||
end
|
||||
if skill_check(AILevel.best) && target.abilityActive? && !moldBreaker
|
||||
Battle::AbilityEffects.triggerAccuracyCalcFromTarget(
|
||||
target.ability, modifiers, @user, target, move, type
|
||||
)
|
||||
end
|
||||
# Item effects that alter accuracy calculation
|
||||
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
|
||||
)
|
||||
end
|
||||
if skill_check(AILevel.high) && target.itemActive?
|
||||
Battle::ItemEffects.triggerAccuracyCalcFromTarget(
|
||||
target.item, modifiers, @user, target, move, type
|
||||
)
|
||||
end
|
||||
# Other effects, inc. ones that set accuracy_multiplier or evasion_stage to specific values
|
||||
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"
|
||||
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_check(AILevel.medium)
|
||||
if move.function == "BadPoisonTarget" && # Toxic
|
||||
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_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
|
||||
#=============================================================================
|
||||
|
||||
@@ -112,7 +112,7 @@ class Battle::AI
|
||||
# instead if the move raises evasion. Note this comes after the
|
||||
# dissociation of Bulk Up from sweeping_stat.
|
||||
|
||||
if skill_check(AILevel.medium)
|
||||
if @trainer.medium_skill?
|
||||
# TODO: Prefer if the maximum damage the target has dealt wouldn't hurt
|
||||
# the user much.
|
||||
end
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
# 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)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if ai.trainer.high_skill?
|
||||
next score - 95
|
||||
}
|
||||
)
|
||||
@@ -20,7 +20,7 @@ Battle::AI::Handlers::MoveEffectScore.copy("DoesNothingCongratulations",
|
||||
# AddMoneyGainedFromBattle
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("FailsIfNotUserFirstTurn",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score - 90 if user.turnCount > 0
|
||||
}
|
||||
)
|
||||
@@ -28,31 +28,31 @@ Battle::AI::Handlers::MoveEffectScore.add("FailsIfNotUserFirstTurn",
|
||||
# FailsIfUserHasUnusedMove
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("FailsIfUserNotConsumedBerry",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
next score - 90 if !user.belched?
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score - 90 if !user.battler.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?
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.medium_skill?
|
||||
next score - 90 if !target.item || !target.item_active?
|
||||
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]))
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if !(user.types[0] && target.has_type?(user.types[0])) &&
|
||||
!(user.types[1] && target.has_type?(user.types[1]))
|
||||
next score - 90
|
||||
end
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("FailsIfUserDamagedThisTurn",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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
|
||||
@@ -63,20 +63,20 @@ Battle::AI::Handlers::MoveEffectScore.add("FailsIfUserDamagedThisTurn",
|
||||
# FailsIfTargetActed
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("CrashDamageIfFailsUnusableInGravity",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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|
|
||||
proc { |score, move, user, target, 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|
|
||||
user.battler.eachMove do |m|
|
||||
next if !m.damagingMove? || m.type != :FIRE
|
||||
score += 20
|
||||
end
|
||||
@@ -86,14 +86,14 @@ Battle::AI::Handlers::MoveEffectScore.add("StartSunWeather",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartRainWeather",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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|
|
||||
user.battler.eachMove do |m|
|
||||
next if !m.damagingMove? || m.type != :WATER
|
||||
score += 20
|
||||
end
|
||||
@@ -103,7 +103,7 @@ Battle::AI::Handlers::MoveEffectScore.add("StartRainWeather",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartSandstormWeather",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if battle.pbCheckGlobalAbility(:AIRLOCK) ||
|
||||
battle.pbCheckGlobalAbility(:CLOUDNINE)
|
||||
next score - 90
|
||||
@@ -114,7 +114,7 @@ Battle::AI::Handlers::MoveEffectScore.add("StartSandstormWeather",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartHailWeather",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if battle.pbCheckGlobalAbility(:AIRLOCK) ||
|
||||
battle.pbCheckGlobalAbility(:CLOUDNINE)
|
||||
next score - 90
|
||||
@@ -133,16 +133,16 @@ Battle::AI::Handlers::MoveEffectScore.add("StartHailWeather",
|
||||
# StartPsychicTerrain
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("RemoveTerrain",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if battle.field.terrain == :None
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("AddSpikesToFoeSide",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if user.pbOpposingSide.effects[PBEffects::Spikes] >= 3
|
||||
next score - 90
|
||||
elsif user.allOpposing.none? { |b| battle.pbCanChooseNonActive?(b.index) }
|
||||
elsif user.battler.allOpposing.none? { |b| battle.pbCanChooseNonActive?(b.index) }
|
||||
next score - 90 # Opponent can't switch in any Pokemon
|
||||
else
|
||||
score += 10 * battle.pbAbleNonActiveCount(user.idxOpposingSide)
|
||||
@@ -153,10 +153,10 @@ Battle::AI::Handlers::MoveEffectScore.add("AddSpikesToFoeSide",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("AddToxicSpikesToFoeSide",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if user.pbOpposingSide.effects[PBEffects::ToxicSpikes] >= 2
|
||||
next score - 90
|
||||
elsif user.allOpposing.none? { |b| battle.pbCanChooseNonActive?(b.index) }
|
||||
elsif user.battler.allOpposing.none? { |b| battle.pbCanChooseNonActive?(b.index) }
|
||||
next score - 90 # Opponent can't switch in any Pokemon
|
||||
else
|
||||
score += 8 * battle.pbAbleNonActiveCount(user.idxOpposingSide)
|
||||
@@ -167,10 +167,10 @@ Battle::AI::Handlers::MoveEffectScore.add("AddToxicSpikesToFoeSide",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("AddStealthRocksToFoeSide",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if user.pbOpposingSide.effects[PBEffects::StealthRock]
|
||||
next score - 90
|
||||
elsif user.allOpposing.none? { |b| battle.pbCanChooseNonActive?(b.index) }
|
||||
elsif user.battler.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)
|
||||
@@ -179,14 +179,14 @@ Battle::AI::Handlers::MoveEffectScore.add("AddStealthRocksToFoeSide",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("AddStickyWebToFoeSide",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.medium_skill?
|
||||
good_effects = [:Reflect, :LightScreen, :AuroraVeil, :SeaOfFire,
|
||||
:Swamp, :Rainbow, :Mist, :Safeguard,
|
||||
:Tailwind].map! { |e| PBEffects.const_get(e) }
|
||||
@@ -195,7 +195,7 @@ Battle::AI::Handlers::MoveEffectScore.add("SwapSideEffects",
|
||||
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)
|
||||
if ai.trainer.high_skill?
|
||||
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])
|
||||
@@ -207,7 +207,7 @@ Battle::AI::Handlers::MoveEffectScore.add("SwapSideEffects",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UserMakeSubstitute",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if user.effects[PBEffects::Substitute] > 0
|
||||
next score - 90
|
||||
elsif user.hp <= user.totalhp / 4
|
||||
@@ -217,7 +217,7 @@ Battle::AI::Handlers::MoveEffectScore.add("UserMakeSubstitute",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("RemoveUserBindingAndEntryHazards",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
score += 30 if user.effects[PBEffects::Trapping] > 0
|
||||
score += 30 if user.effects[PBEffects::LeechSeed] >= 0
|
||||
if battle.pbAbleNonActiveCount(user.idxOwnSide) > 0
|
||||
@@ -230,7 +230,7 @@ Battle::AI::Handlers::MoveEffectScore.add("RemoveUserBindingAndEntryHazards",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("AttackTwoTurnsLater",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if battle.positions[target.index].effects[PBEffects::FutureSightCounter] > 0
|
||||
next 0
|
||||
elsif battle.pbAbleNonActiveCount(user.idxOwnSide) == 0
|
||||
@@ -243,7 +243,7 @@ Battle::AI::Handlers::MoveEffectScore.add("AttackTwoTurnsLater",
|
||||
# UserSwapsPositionsWithAlly
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("BurnAttackerBeforeUserActs",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score + 20 # Because of possible burning
|
||||
}
|
||||
)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,18 +2,16 @@
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("SleepTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
if target.pbCanSleep?(user, false)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.battler.pbCanSleep?(user.battler, false)
|
||||
score += 30
|
||||
if ai.skill_check(Battle::AI::AILevel.medium)
|
||||
if ai.trainer.medium_skill?
|
||||
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 -= 30 if target.has_active_ability?(:MARVELSCALE)
|
||||
if ai.trainer.best_skill?
|
||||
if target.battler.pbHasMoveFunction?("FlinchTargetFailsIfUserNotAsleep",
|
||||
"UseRandomUserMoveIfAsleep") # Snore, Sleep Talk
|
||||
score -= 50
|
||||
end
|
||||
end
|
||||
@@ -29,15 +27,14 @@ Battle::AI::Handlers::MoveEffectScore.copy("SleepTarget",
|
||||
"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)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if target.effects[PBEffects::Yawn] > 0 ||
|
||||
!target.battler.pbCanSleep?(user.battler, 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 -= 30 if target.has_active_ability?(:MARVELSCALE)
|
||||
if ai.trainer.best_skill?
|
||||
if target.battler.pbHasMoveFunction?("FlinchTargetFailsIfUserNotAsleep",
|
||||
"UseRandomUserMoveIfAsleep") # Snore, Sleep Talk
|
||||
score -= 50
|
||||
end
|
||||
end
|
||||
@@ -46,19 +43,19 @@ Battle::AI::Handlers::MoveEffectScore.add("SleepTargetNextTurn",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("PoisonTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
if target.pbCanPoison?(user, false)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.battler.pbCanPoison?(user.battler, false)
|
||||
score += 30
|
||||
if ai.skill_check(Battle::AI::AILevel.medium)
|
||||
if ai.trainer.medium_skill?
|
||||
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])
|
||||
if ai.trainer.high_skill?
|
||||
score += 10 if target.rough_stat(:DEFENSE) > 100
|
||||
score += 10 if target.rough_stat(:SPECIAL_DEFENSE) > 100
|
||||
end
|
||||
score -= 40 if target.has_active_ability?([:GUTS, :MARVELSCALE, :TOXICBOOST])
|
||||
else
|
||||
next 0 if move.statusMove?
|
||||
end
|
||||
@@ -67,26 +64,27 @@ Battle::AI::Handlers::MoveEffectScore.add("PoisonTarget",
|
||||
)
|
||||
|
||||
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)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if !target.battler.pbCanPoison?(user.battler, false) &&
|
||||
!target.battler.pbCanLowerStatStage?(:SPEED, user.battler)
|
||||
if target.battler.pbCanPoison?(user.battler, false)
|
||||
score += 30
|
||||
if ai.skill_check(Battle::AI::AILevel.medium)
|
||||
if ai.trainer.medium_skill?
|
||||
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])
|
||||
if ai.trainer.high_skill?
|
||||
score += 10 if target.rough_stat(:DEFENSE) > 100
|
||||
score += 10 if target.rough_stat(:SPECIAL_DEFENSE) > 100
|
||||
end
|
||||
score -= 40 if target.has_active_ability?([:GUTS, :MARVELSCALE, :TOXICBOOST])
|
||||
end
|
||||
if target.pbCanLowerStatStage?(:SPEED, user)
|
||||
if target.battler.pbCanLowerStatStage?(:SPEED, user.battler)
|
||||
score += target.stages[:SPEED] * 10
|
||||
if ai.skill_check(Battle::AI::AILevel.high)
|
||||
aspeed = pbRoughStat(user, :SPEED)
|
||||
ospeed = pbRoughStat(target, :SPEED)
|
||||
if ai.trainer.high_skill?
|
||||
aspeed = user.rough_stat(:SPEED)
|
||||
ospeed = target.rough_stat(:SPEED)
|
||||
score += 30 if aspeed < ospeed && aspeed * 2 > ospeed
|
||||
end
|
||||
end
|
||||
@@ -95,19 +93,19 @@ Battle::AI::Handlers::MoveEffectScore.add("PoisonTargetLowerTargetSpeed1",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("BadPoisonTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
if target.pbCanPoison?(user, false)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.battler.pbCanPoison?(user.battler, false)
|
||||
score += 30
|
||||
if ai.skill_check(Battle::AI::AILevel.medium)
|
||||
if ai.trainer.medium_skill?
|
||||
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])
|
||||
if ai.trainer.high_skill?
|
||||
score += 10 if target.rough_stat(:DEFENSE) > 100
|
||||
score += 10 if target.rough_stat(:SPECIAL_DEFENSE) > 100
|
||||
end
|
||||
score -= 40 if target.has_active_ability?([:GUTS, :MARVELSCALE, :TOXICBOOST])
|
||||
else
|
||||
score -= 90 if move.statusMove?
|
||||
end
|
||||
@@ -116,24 +114,22 @@ Battle::AI::Handlers::MoveEffectScore.add("BadPoisonTarget",
|
||||
)
|
||||
|
||||
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) &&
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.battler.pbCanParalyze?(user.battler, false) &&
|
||||
!(ai.trainer.medium_skill? &&
|
||||
move.id == :THUNDERWAVE &&
|
||||
Effectiveness.ineffective?(pbCalcTypeMod(move.type, user, target)))
|
||||
Effectiveness.ineffective?(target.effectiveness_of_type_against_battler(move.type, user)))
|
||||
score += 30
|
||||
if ai.skill_check(Battle::AI::AILevel.medium)
|
||||
aspeed = pbRoughStat(user, :SPEED)
|
||||
ospeed = pbRoughStat(target, :SPEED)
|
||||
if ai.trainer.medium_skill?
|
||||
aspeed = user.rough_stat(:SPEED)
|
||||
ospeed = target.rough_stat(: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
|
||||
score -= 40 if target.has_active_ability?([:GUTS, :MARVELSCALE, :QUICKFEET])
|
||||
else
|
||||
score -= 90 if move.statusMove?
|
||||
end
|
||||
@@ -147,12 +143,10 @@ Battle::AI::Handlers::MoveEffectScore.copy("ParalyzeTarget",
|
||||
"ParalyzeFlinchTarget")
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("BurnTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
if target.pbCanBurn?(user, false)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.battler.pbCanBurn?(user.battler, false)
|
||||
score += 30
|
||||
if ai.skill_check(Battle::AI::AILevel.high)
|
||||
score -= 40 if target.hasActiveAbility?([:GUTS, :MARVELSCALE, :QUICKFEET, :FLAREBOOST])
|
||||
end
|
||||
score -= 40 if target.has_active_ability?([:GUTS, :MARVELSCALE, :QUICKFEET, :FLAREBOOST])
|
||||
else
|
||||
score -= 90 if move.statusMove?
|
||||
end
|
||||
@@ -165,12 +159,10 @@ Battle::AI::Handlers::MoveEffectScore.copy("BurnTarget",
|
||||
"BurnFlinchTarget")
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("FreezeTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
if target.pbCanFreeze?(user, false)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.battler.pbCanFreeze?(user.battler, false)
|
||||
score += 30
|
||||
if ai.skill_check(Battle::AI::AILevel.high)
|
||||
score -= 20 if target.hasActiveAbility?(:MARVELSCALE)
|
||||
end
|
||||
score -= 20 if target.has_active_ability?(:MARVELSCALE)
|
||||
else
|
||||
score -= 90 if move.statusMove?
|
||||
end
|
||||
@@ -179,24 +171,20 @@ Battle::AI::Handlers::MoveEffectScore.add("FreezeTarget",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("FreezeTargetSuperEffectiveAgainstWater",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
if target.pbCanFreeze?(user, false)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.battler.pbCanFreeze?(user.battler, false)
|
||||
score += 30
|
||||
if ai.skill_check(Battle::AI::AILevel.high)
|
||||
score -= 20 if target.hasActiveAbility?(:MARVELSCALE)
|
||||
end
|
||||
score -= 20 if target.has_active_ability?(:MARVELSCALE)
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("FreezeTargetAlwaysHitsInHail",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
if target.pbCanFreeze?(user, false)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.battler.pbCanFreeze?(user.battler, false)
|
||||
score += 30
|
||||
if ai.skill_check(Battle::AI::AILevel.high)
|
||||
score -= 20 if target.hasActiveAbility?(:MARVELSCALE)
|
||||
end
|
||||
score -= 20 if target.has_active_ability?(:MARVELSCALE)
|
||||
else
|
||||
score -= 90 if move.statusMove?
|
||||
end
|
||||
@@ -208,27 +196,27 @@ Battle::AI::Handlers::MoveEffectScore.copy("FreezeTargetAlwaysHitsInHail",
|
||||
"FreezeFlinchTarget")
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("ParalyzeBurnOrFreezeTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score + 30 if target.status == :NONE
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("GiveUserStatusToTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
case user.status
|
||||
when :POISON
|
||||
score += 40
|
||||
if ai.skill_check(Battle::AI::AILevel.medium)
|
||||
if ai.trainer.medium_skill?
|
||||
if user.hp < user.totalhp / 8
|
||||
score += 60
|
||||
elsif ai.skill_check(Battle::AI::AILevel.high) &&
|
||||
elsif ai.trainer.high_skill? &&
|
||||
user.hp < (user.effects[PBEffects::Toxic] + 1) * user.totalhp / 16
|
||||
score += 60
|
||||
end
|
||||
@@ -243,7 +231,7 @@ Battle::AI::Handlers::MoveEffectScore.add("CureUserBurnPoisonParalysis",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("CureUserPartyStatus",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
statuses = 0
|
||||
battle.pbParty(user.index).each do |pkmn|
|
||||
statuses += 1 if pkmn && pkmn.status != :NONE
|
||||
@@ -258,7 +246,7 @@ Battle::AI::Handlers::MoveEffectScore.add("CureUserPartyStatus",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("CureTargetBurn",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.opposes?(user)
|
||||
score -= 40 if target.status == :BURN
|
||||
elsif target.status == :BURN
|
||||
@@ -269,7 +257,7 @@ Battle::AI::Handlers::MoveEffectScore.add("CureTargetBurn",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartUserSideImmunityToInflictedStatus",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if user.pbOwnSide.effects[PBEffects::Safeguard] > 0
|
||||
score -= 80
|
||||
elsif user.status != :NONE
|
||||
@@ -282,52 +270,44 @@ Battle::AI::Handlers::MoveEffectScore.add("StartUserSideImmunityToInflictedStatu
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("FlinchTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
score += 30
|
||||
if ai.skill_check(Battle::AI::AILevel.high)
|
||||
score += 30 if !target.hasActiveAbility?(:INNERFOCUS) &&
|
||||
target.effects[PBEffects::Substitute] == 0
|
||||
end
|
||||
score += 30 if !target.has_active_ability?(:INNERFOCUS) &&
|
||||
target.effects[PBEffects::Substitute] == 0
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("FlinchTargetFailsIfUserNotAsleep",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
next 0 if !user.asleep?
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if !user.battler.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
|
||||
score += 30 if !target.has_active_ability?(:INNERFOCUS) &&
|
||||
target.effects[PBEffects::Substitute] == 0
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("FlinchTargetFailsIfNotUserFirstTurn",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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
|
||||
score += 30 if !target.has_active_ability?(:INNERFOCUS) &&
|
||||
target.effects[PBEffects::Substitute] == 0
|
||||
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
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
score += 30 if !target.has_active_ability?(:INNERFOCUS) &&
|
||||
target.effects[PBEffects::Substitute] == 0
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("ConfuseTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
next 0 if !target.pbCanConfuse?(user, false)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if !target.battler.pbCanConfuse?(user.battler, false)
|
||||
next score + 30
|
||||
}
|
||||
)
|
||||
@@ -336,7 +316,7 @@ Battle::AI::Handlers::MoveEffectScore.copy("ConfuseTarget",
|
||||
"ConfuseTargetAlwaysHitsInRainHitsTargetInSky")
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("AttractTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
canattract = true
|
||||
agender = user.gender
|
||||
ogender = target.gender
|
||||
@@ -346,25 +326,23 @@ Battle::AI::Handlers::MoveEffectScore.add("AttractTarget",
|
||||
elsif target.effects[PBEffects::Attract] >= 0
|
||||
score -= 80
|
||||
canattract = false
|
||||
elsif ai.skill_check(Battle::AI::AILevel.best) && target.hasActiveAbility?(:OBLIVIOUS)
|
||||
elsif target.has_active_ability?(: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
|
||||
if canattract && target.has_active_item?(:DESTINYKNOT) &&
|
||||
user.battler.pbCanAttract?(target.battler, false)
|
||||
score -= 30
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("SetUserTypesBasedOnEnvironment",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
if !user.canChangeType?
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if !user.battler.canChangeType?
|
||||
score -= 90
|
||||
elsif ai.skill_check(Battle::AI::AILevel.medium)
|
||||
elsif ai.trainer.medium_skill?
|
||||
new_type = nil
|
||||
case battle.field.terrain
|
||||
when :Electric
|
||||
@@ -402,28 +380,28 @@ Battle::AI::Handlers::MoveEffectScore.add("SetUserTypesBasedOnEnvironment",
|
||||
new_type = nil if !GameData::Type.exists?(new_type)
|
||||
new_type ||= :NORMAL
|
||||
end
|
||||
score -= 90 if !user.pbHasOtherType?(new_type)
|
||||
score -= 90 if !user.battler.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
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if !user.battler.canChangeType?
|
||||
next 0 if !target.battler.lastMoveUsed || !target.battler.lastMoveUsedType ||
|
||||
GameData::Type.get(target.battler.lastMoveUsedType).pseudo_type
|
||||
aType = nil
|
||||
target.eachMove do |m|
|
||||
next if m.id != target.lastMoveUsed
|
||||
aType = m.pbCalcType(user)
|
||||
target.battler.eachMove do |m|
|
||||
next if m.id != target.battler.lastMoveUsed
|
||||
aType = m.pbCalcType(user.battler)
|
||||
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)
|
||||
next if t.pseudo_type || user.has_type?(t.id) ||
|
||||
!Effectiveness.resistant_type?(target.battler.lastMoveUsedType, t.id)
|
||||
has_possible_type = true
|
||||
break
|
||||
end
|
||||
@@ -432,21 +410,21 @@ Battle::AI::Handlers::MoveEffectScore.add("SetUserTypesToResistLastAttack",
|
||||
)
|
||||
|
||||
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 &&
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if !user.battler.canChangeType? || target.battler.pbTypes(true).length == 0
|
||||
next 0 if user.battler.pbTypes == target.battler.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?
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if !user.battler.canChangeType?
|
||||
has_possible_type = false
|
||||
user.eachMoveWithIndex do |m, i|
|
||||
user.battler.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)
|
||||
next if user.has_type?(m.type)
|
||||
has_possible_type = true
|
||||
break
|
||||
end
|
||||
@@ -455,10 +433,10 @@ Battle::AI::Handlers::MoveEffectScore.add("SetUserTypesToUserMoveType",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("SetTargetTypesToPsychic",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
if target.pbHasOtherType?(:PSYCHIC)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if !target.battler.canChangeType?
|
||||
score -= 90
|
||||
elsif !target.canChangeType?
|
||||
elsif !target.battler.pbHasOtherType?(:PSYCHIC)
|
||||
score -= 90
|
||||
end
|
||||
next score
|
||||
@@ -466,10 +444,10 @@ Battle::AI::Handlers::MoveEffectScore.add("SetTargetTypesToPsychic",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("SetTargetTypesToWater",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
if target.effects[PBEffects::Substitute] > 0 || !target.canChangeType?
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if !target.battler.canChangeType? || target.effects[PBEffects::Substitute] > 0
|
||||
score -= 90
|
||||
elsif !target.pbHasOtherType?(:WATER)
|
||||
elsif !target.battler.pbHasOtherType?(:WATER)
|
||||
score -= 90
|
||||
end
|
||||
next score
|
||||
@@ -477,47 +455,47 @@ Battle::AI::Handlers::MoveEffectScore.add("SetTargetTypesToWater",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("AddGhostTypeToTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
next 0 if target.pbHasType?(:GHOST)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if target.has_type?(:GHOST)
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("AddGrassTypeToTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
next 0 if target.pbHasType?(:GRASS)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if target.has_type?(:GRASS)
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UserLosesFireType",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
next 0 if !user.pbHasType?(:FIRE)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if !user.has_type?(:FIRE)
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("SetTargetAbilityToSimple",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if target.effects[PBEffects::Substitute] > 0
|
||||
if ai.skill_check(Battle::AI::AILevel.medium)
|
||||
next 0 if target.unstoppableAbility? ||
|
||||
if ai.trainer.medium_skill?
|
||||
next 0 if target.battler.unstoppableAbility? ||
|
||||
[:TRUANT, :SIMPLE].include?(target.ability)
|
||||
end
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("SetTargetAbilityToInsomnia",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if target.effects[PBEffects::Substitute] > 0
|
||||
if ai.skill_check(Battle::AI::AILevel.medium)
|
||||
next 0 if target.unstoppableAbility? ||
|
||||
if ai.trainer.medium_skill?
|
||||
next 0 if target.battler.unstoppableAbility? ||
|
||||
[:TRUANT, :INSOMNIA].include?(target.ability)
|
||||
end
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("SetUserAbilityToTargetAbility",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
score -= 40 # don't prefer this move
|
||||
if ai.skill_check(Battle::AI::AILevel.medium)
|
||||
if ai.trainer.medium_skill?
|
||||
if !target.ability || user.ability == target.ability ||
|
||||
[:MULTITYPE, :RKSSYSTEM].include?(user.ability_id) ||
|
||||
[:FLOWERGIFT, :FORECAST, :ILLUSION, :IMPOSTER, :MULTITYPE, :RKSSYSTEM,
|
||||
@@ -525,7 +503,7 @@ Battle::AI::Handlers::MoveEffectScore.add("SetUserAbilityToTargetAbility",
|
||||
score -= 90
|
||||
end
|
||||
end
|
||||
if ai.skill_check(Battle::AI::AILevel.high)
|
||||
if ai.trainer.high_skill?
|
||||
if target.ability == :TRUANT && user.opposes?(target)
|
||||
score -= 90
|
||||
elsif target.ability == :SLOWSTART && user.opposes?(target)
|
||||
@@ -537,18 +515,18 @@ Battle::AI::Handlers::MoveEffectScore.add("SetUserAbilityToTargetAbility",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("SetTargetAbilityToUserAbility",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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)
|
||||
elsif ai.trainer.medium_skill?
|
||||
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 ai.trainer.high_skill?
|
||||
if user.ability == :TRUANT && user.opposes?(target)
|
||||
score += 90
|
||||
elsif user.ability == :SLOWSTART && user.opposes?(target)
|
||||
@@ -561,9 +539,9 @@ Battle::AI::Handlers::MoveEffectScore.add("SetTargetAbilityToUserAbility",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UserTargetSwapAbilities",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
score -= 40 # don't prefer this move
|
||||
if ai.skill_check(Battle::AI::AILevel.medium)
|
||||
if ai.trainer.medium_skill?
|
||||
if (!user.ability && !target.ability) ||
|
||||
user.ability == target.ability ||
|
||||
[:ILLUSION, :MULTITYPE, :RKSSYSTEM, :WONDERGUARD].include?(user.ability_id) ||
|
||||
@@ -571,7 +549,7 @@ Battle::AI::Handlers::MoveEffectScore.add("UserTargetSwapAbilities",
|
||||
score -= 90
|
||||
end
|
||||
end
|
||||
if ai.skill_check(Battle::AI::AILevel.high)
|
||||
if ai.trainer.high_skill?
|
||||
if target.ability == :TRUANT && user.opposes?(target)
|
||||
score -= 90
|
||||
elsif target.ability == :SLOWSTART && user.opposes?(target)
|
||||
@@ -583,11 +561,11 @@ Battle::AI::Handlers::MoveEffectScore.add("UserTargetSwapAbilities",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("NegateTargetAbility",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.effects[PBEffects::Substitute] > 0 ||
|
||||
target.effects[PBEffects::GastroAcid]
|
||||
score -= 90
|
||||
elsif ai.skill_check(Battle::AI::AILevel.high)
|
||||
elsif ai.trainer.high_skill?
|
||||
score -= 90 if [:MULTITYPE, :RKSSYSTEM, :SLOWSTART, :TRUANT].include?(target.ability_id)
|
||||
end
|
||||
next score
|
||||
@@ -595,13 +573,9 @@ Battle::AI::Handlers::MoveEffectScore.add("NegateTargetAbility",
|
||||
)
|
||||
|
||||
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
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.medium_skill?
|
||||
score += 30 if target.faster_than?(user)
|
||||
else
|
||||
score += 30
|
||||
end
|
||||
@@ -612,7 +586,7 @@ Battle::AI::Handlers::MoveEffectScore.add("NegateTargetAbilityIfTargetActed",
|
||||
# IgnoreTargetAbility
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartUserAirborne",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if user.effects[PBEffects::MagnetRise] > 0 ||
|
||||
user.effects[PBEffects::Ingrain] ||
|
||||
user.effects[PBEffects::SmackDown]
|
||||
@@ -623,7 +597,7 @@ Battle::AI::Handlers::MoveEffectScore.add("StartUserAirborne",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartTargetAirborneAndAlwaysHitByMoves",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.effects[PBEffects::Telekinesis] > 0 ||
|
||||
target.effects[PBEffects::Ingrain] ||
|
||||
target.effects[PBEffects::SmackDown]
|
||||
@@ -636,48 +610,48 @@ Battle::AI::Handlers::MoveEffectScore.add("StartTargetAirborneAndAlwaysHitByMove
|
||||
# HitsTargetInSky
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("HitsTargetInSkyGroundsTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
if ai.skill_check(Battle::AI::AILevel.medium)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.medium_skill?
|
||||
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)
|
||||
score += 20 if target.battler.inTwoTurnAttack?("TwoTurnAttackInvulnerableInSky",
|
||||
"TwoTurnAttackInvulnerableInSkyParalyzeTarget")
|
||||
score += 20 if target.has_type?(:FLYING)
|
||||
score += 20 if target.has_active_ability?(:LEVITATE)
|
||||
score += 20 if target.has_active_item?(:AIRBALLOON)
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartGravity",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if battle.field.effects[PBEffects::Gravity] > 0
|
||||
score -= 90
|
||||
elsif ai.skill_check(Battle::AI::AILevel.medium)
|
||||
elsif ai.trainer.medium_skill?
|
||||
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 user.has_type?(:FLYING)
|
||||
score -= 20 if user.has_active_ability?(:LEVITATE)
|
||||
score -= 20 if user.has_active_item?(: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)
|
||||
score += 20 if target.battler.inTwoTurnAttack?("TwoTurnAttackInvulnerableInSky",
|
||||
"TwoTurnAttackInvulnerableInSkyParalyzeTarget",
|
||||
"TwoTurnAttackInvulnerableInSkyTargetCannotAct")
|
||||
score += 20 if target.has_type?(:FLYING)
|
||||
score += 20 if target.has_active_ability?(:LEVITATE)
|
||||
score += 20 if target.has_active_item?(:AIRBALLOON)
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("TransformUserIntoTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score - 70
|
||||
}
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("FixedDamage20",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.hp <= 20
|
||||
score += 80
|
||||
elsif target.level >= 25
|
||||
@@ -13,32 +13,32 @@ Battle::AI::Handlers::MoveEffectScore.add("FixedDamage20",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("FixedDamage40",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score + 80 if target.hp <= 40
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("FixedDamageHalfTargetHP",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score + 80 if target.hp <= user.level
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("FixedDamageUserLevelRandom",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score + 30 if target.hp <= user.level
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("LowerTargetHPToUserHP",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if user.hp >= target.hp
|
||||
score -= 90
|
||||
elsif user.hp < target.hp / 2
|
||||
@@ -49,8 +49,8 @@ Battle::AI::Handlers::MoveEffectScore.add("LowerTargetHPToUserHP",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("OHKO",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
next 0 if target.hasActiveAbility?(:STURDY)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if target.has_active_ability?(:STURDY)
|
||||
next 0 if target.level > user.level
|
||||
}
|
||||
)
|
||||
@@ -60,9 +60,9 @@ Battle::AI::Handlers::MoveEffectScore.copy("OHKO",
|
||||
"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)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
target.battler.allAllies.each do |b|
|
||||
next if !b.near?(target.battler)
|
||||
score += 10
|
||||
end
|
||||
next score
|
||||
@@ -104,7 +104,7 @@ Battle::AI::Handlers::MoveEffectScore.add("DamageTargetAlly",
|
||||
# DoublePowerIfUserPoisonedBurnedParalyzed
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("DoublePowerIfTargetAsleepCureTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score - 20 if target.status == :SLEEP && # Will cure status
|
||||
target.statusCount > 1
|
||||
}
|
||||
@@ -113,7 +113,7 @@ Battle::AI::Handlers::MoveEffectScore.add("DoublePowerIfTargetAsleepCureTarget",
|
||||
# DoublePowerIfTargetPoisoned
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("DoublePowerIfTargetParalyzedCureTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score - 20 if target.status == :PARALYSIS # Will cure status
|
||||
}
|
||||
)
|
||||
@@ -129,8 +129,8 @@ Battle::AI::Handlers::MoveEffectScore.add("DoublePowerIfTargetParalyzedCureTarge
|
||||
# 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?
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score + 40 if battle.field.terrain == :Electric && target.battler.affectedByTerrain?
|
||||
}
|
||||
)
|
||||
|
||||
@@ -139,26 +139,22 @@ Battle::AI::Handlers::MoveEffectScore.add("DoublePowerInElectricTerrain",
|
||||
# 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
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score + 30 if target.faster_than?(user)
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("DoublePowerIfTargetLostHPThisTurn",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
next score + 20 if battle.pbOpposingBattlerCount(user) > 1
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score + 20 if battle.pbOpposingBattlerCount(user.battler) > 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
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score + 30 if target.faster_than?(user)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -167,7 +163,7 @@ Battle::AI::Handlers::MoveEffectScore.add("DoublePowerIfTargetActed",
|
||||
# AlwaysCriticalHit
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("EnsureNextCriticalHit",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if user.effects[PBEffects::LaserFocus] > 0
|
||||
score -= 90
|
||||
else
|
||||
@@ -178,13 +174,13 @@ Battle::AI::Handlers::MoveEffectScore.add("EnsureNextCriticalHit",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("EnsureNextCriticalHit",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if target.hp == 1
|
||||
if target.hp <= target.totalhp / 8
|
||||
score -= 60
|
||||
@@ -196,9 +192,9 @@ Battle::AI::Handlers::MoveEffectScore.add("CannotMakeTargetFaint",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UserEnduresFaintingThisTurn",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
score -= 25 if user.hp > user.totalhp / 2
|
||||
if ai.skill_check(Battle::AI::AILevel.medium)
|
||||
if ai.trainer.medium_skill?
|
||||
score -= 90 if user.effects[PBEffects::ProtectRate] > 1
|
||||
score -= 90 if target.effects[PBEffects::HyperBeam] > 0
|
||||
else
|
||||
@@ -209,38 +205,39 @@ Battle::AI::Handlers::MoveEffectScore.add("UserEnduresFaintingThisTurn",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartWeakenElectricMoves",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if user.effects[PBEffects::MudSport]
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartWeakenFireMoves",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if user.effects[PBEffects::WaterSport]
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartWeakenPhysicalDamageAgainstUserSide",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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|
|
||||
proc { |score, move, user, target, 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
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if user.pbOwnSide.effects[PBEffects::AuroraVeil] > 0 ||
|
||||
user.battler.effectiveWeather != :Hail
|
||||
next score + 40
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("RemoveScreens",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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
|
||||
@@ -249,12 +246,12 @@ Battle::AI::Handlers::MoveEffectScore.add("RemoveScreens",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("ProtectUser",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if user.effects[PBEffects::ProtectRate] > 1 ||
|
||||
target.effects[PBEffects::HyperBeam] > 0
|
||||
score -= 90
|
||||
else
|
||||
if ai.skill_check(Battle::AI::AILevel.medium)
|
||||
if ai.trainer.medium_skill?
|
||||
score -= user.effects[PBEffects::ProtectRate] * 40
|
||||
end
|
||||
score += 50 if user.turnCount == 0
|
||||
@@ -265,12 +262,12 @@ Battle::AI::Handlers::MoveEffectScore.add("ProtectUser",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("ProtectUserBanefulBunker",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if user.effects[PBEffects::ProtectRate] > 1 ||
|
||||
target.effects[PBEffects::HyperBeam] > 0
|
||||
score -= 90
|
||||
else
|
||||
if ai.skill_check(Battle::AI::AILevel.medium)
|
||||
if ai.trainer.medium_skill?
|
||||
score -= user.effects[PBEffects::ProtectRate] * 40
|
||||
end
|
||||
score += 50 if user.turnCount == 0
|
||||
@@ -282,12 +279,12 @@ Battle::AI::Handlers::MoveEffectScore.add("ProtectUserBanefulBunker",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("ProtectUserFromDamagingMovesKingsShield",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if user.effects[PBEffects::ProtectRate] > 1 ||
|
||||
target.effects[PBEffects::HyperBeam] > 0
|
||||
score -= 90
|
||||
else
|
||||
if ai.skill_check(Battle::AI::AILevel.medium)
|
||||
if ai.trainer.medium_skill?
|
||||
score -= user.effects[PBEffects::ProtectRate] * 40
|
||||
end
|
||||
score += 50 if user.turnCount == 0
|
||||
@@ -298,12 +295,12 @@ Battle::AI::Handlers::MoveEffectScore.add("ProtectUserFromDamagingMovesKingsShie
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("ProtectUserFromDamagingMovesObstruct",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if user.effects[PBEffects::ProtectRate] > 1 ||
|
||||
target.effects[PBEffects::HyperBeam] > 0
|
||||
score -= 90
|
||||
else
|
||||
if ai.skill_check(Battle::AI::AILevel.medium)
|
||||
if ai.trainer.medium_skill?
|
||||
score -= user.effects[PBEffects::ProtectRate] * 40
|
||||
end
|
||||
score += 50 if user.turnCount == 0
|
||||
@@ -314,12 +311,12 @@ Battle::AI::Handlers::MoveEffectScore.add("ProtectUserFromDamagingMovesObstruct"
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("ProtectUserFromTargetingMovesSpikyShield",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if user.effects[PBEffects::ProtectRate] > 1 ||
|
||||
target.effects[PBEffects::HyperBeam] > 0
|
||||
score -= 90
|
||||
else
|
||||
if ai.skill_check(Battle::AI::AILevel.medium)
|
||||
if ai.trainer.medium_skill?
|
||||
score -= user.effects[PBEffects::ProtectRate] * 40
|
||||
end
|
||||
score += 50 if user.turnCount == 0
|
||||
@@ -330,7 +327,7 @@ Battle::AI::Handlers::MoveEffectScore.add("ProtectUserFromTargetingMovesSpikyShi
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("ProtectUserSideFromDamagingMovesIfUserFirstTurn",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if user.turnCount != 0
|
||||
next score + 30
|
||||
}
|
||||
@@ -347,55 +344,51 @@ Battle::AI::Handlers::MoveEffectScore.add("ProtectUserSideFromDamagingMovesIfUse
|
||||
# RemoveProtectionsBypassSubstitute
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("HoopaRemoveProtectionsBypassSubstituteLowerUserDef1",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
next 0 if !user.isSpecies?(:HOOPA) || user.form != 1
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if !user.battler.isSpecies?(:HOOPA) || user.battler.form != 1
|
||||
next score + 20 if target.stages[:DEFENSE] > 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("RecoilQuarterOfDamageDealt",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score - 25
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("RecoilThirdOfDamageDealtParalyzeTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
score -= 30
|
||||
if target.pbCanParalyze?(user, false)
|
||||
if target.battler.pbCanParalyze?(user.battler, false)
|
||||
score += 30
|
||||
if ai.skill_check(Battle::AI::AILevel.medium)
|
||||
aspeed = pbRoughStat(user, :SPEED)
|
||||
ospeed = pbRoughStat(target, :SPEED)
|
||||
if ai.trainer.medium_skill?
|
||||
aspeed = user.rough_stat(:SPEED)
|
||||
ospeed = target.rough_stat(: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
|
||||
score -= 40 if target.has_active_ability?([:GUTS, :MARVELSCALE, :QUICKFEET])
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("RecoilThirdOfDamageDealtBurnTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
score -= 30
|
||||
if target.pbCanBurn?(user, false)
|
||||
if target.battler.pbCanBurn?(user.battler, false)
|
||||
score += 30
|
||||
if ai.skill_check(Battle::AI::AILevel.high)
|
||||
score -= 40 if target.hasActiveAbility?([:GUTS, :MARVELSCALE, :QUICKFEET, :FLAREBOOST])
|
||||
end
|
||||
score -= 40 if target.has_active_ability?([:GUTS, :MARVELSCALE, :QUICKFEET, :FLAREBOOST])
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("RecoilHalfOfDamageDealt",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score - 40
|
||||
}
|
||||
)
|
||||
@@ -403,8 +396,8 @@ Battle::AI::Handlers::MoveEffectScore.add("RecoilHalfOfDamageDealt",
|
||||
# EffectivenessIncludesFlyingType
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("CategoryDependsOnHigherDamagePoisonTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
next score + 5 if target.pbCanPoison?(user, false)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score + 5 if target.battler.pbCanPoison?(user.battler, false)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -417,17 +410,17 @@ Battle::AI::Handlers::MoveEffectScore.add("CategoryDependsOnHigherDamagePoisonTa
|
||||
# UseTargetDefenseInsteadOfTargetSpDef
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("EnsureNextMoveAlwaysHits",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.effects[PBEffects::Foresight]
|
||||
score -= 90
|
||||
elsif target.pbHasType?(:GHOST)
|
||||
elsif target.has_type?(:GHOST)
|
||||
score += 70
|
||||
elsif target.stages[:EVASION] <= 0
|
||||
score -= 60
|
||||
@@ -437,10 +430,10 @@ Battle::AI::Handlers::MoveEffectScore.add("StartNegateTargetEvasionStatStageAndG
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartNegateTargetEvasionStatStageAndDarkImmunity",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.effects[PBEffects::MiracleEye]
|
||||
score -= 90
|
||||
elsif target.pbHasType?(:DARK)
|
||||
elsif target.has_type?(:DARK)
|
||||
score += 70
|
||||
elsif target.stages[:EVASION] <= 0
|
||||
score -= 60
|
||||
@@ -456,8 +449,8 @@ Battle::AI::Handlers::MoveEffectScore.add("StartNegateTargetEvasionStatStageAndD
|
||||
# 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?
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if !user.item || !user.item.is_berry? || !user.item_active?
|
||||
}
|
||||
)
|
||||
|
||||
@@ -468,7 +461,7 @@ Battle::AI::Handlers::MoveEffectScore.add("TypeAndPowerDependOnUserBerry",
|
||||
# TypeDependsOnUserDrive
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("TypeDependsOnUserMorpekoFormRaiseUserSpeed1",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score + 20 if user.stages[:SPEED] <= 0
|
||||
}
|
||||
)
|
||||
@@ -476,16 +469,14 @@ Battle::AI::Handlers::MoveEffectScore.add("TypeDependsOnUserMorpekoFormRaiseUser
|
||||
# TypeAndPowerDependOnWeather
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("TypeAndPowerDependOnTerrain",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if user.faster_than?(target)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -5,25 +5,25 @@
|
||||
# HitTwoTimes
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("HitTwoTimesPoisonTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
next 0 if !target.pbCanPoison?(user, false)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if !target.battler.pbCanPoison?(user.battler, false)
|
||||
score += 30
|
||||
if ai.skill_check(Battle::AI::AILevel.medium)
|
||||
if ai.trainer.medium_skill?
|
||||
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])
|
||||
if ai.trainer.high_skill?
|
||||
score += 10 if target.rough_stat(:DEFENSE) > 100
|
||||
score += 10 if target.rough_stat(:SPECIAL_DEFENSE) > 100
|
||||
end
|
||||
score -= 40 if target.has_active_ability?([:GUTS, :MARVELSCALE, :TOXICBOOST])
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("HitTwoTimesFlinchTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score + 30 if target.effects[PBEffects::Minimize]
|
||||
}
|
||||
)
|
||||
@@ -33,10 +33,10 @@ Battle::AI::Handlers::MoveEffectScore.add("HitTwoTimesFlinchTarget",
|
||||
# HitThreeTimesPowersUpWithEachHit
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("HitThreeTimesAlwaysCriticalHit",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
if ai.skill_check(Battle::AI::AILevel.high)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.high_skill?
|
||||
stat = (move.physicalMove?) ? :DEFENSE : :SPECIAL_DEFENSE
|
||||
next score + 50 if targets.stages[stat] > 1
|
||||
next score + 50 if target.stages[stat] > 1
|
||||
end
|
||||
}
|
||||
)
|
||||
@@ -46,9 +46,9 @@ Battle::AI::Handlers::MoveEffectScore.add("HitThreeTimesAlwaysCriticalHit",
|
||||
# HitTwoToFiveTimesOrThreeForAshGreninja
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("HitTwoToFiveTimesRaiseUserSpd1LowerUserDef1",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
aspeed = pbRoughStat(user, :SPEED)
|
||||
ospeed = pbRoughStat(target, :SPEED)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
aspeed = user.rough_stat(:SPEED)
|
||||
ospeed = target.rough_stat(:SPEED)
|
||||
if aspeed > ospeed && aspeed * 2 / 3 < ospeed
|
||||
score -= 50
|
||||
elsif aspeed < ospeed && aspeed * 1.5 > ospeed
|
||||
@@ -68,25 +68,20 @@ Battle::AI::Handlers::MoveEffectScore.add("HitTwoToFiveTimesRaiseUserSpd1LowerUs
|
||||
# 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)))
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.battler.pbCanParalyze?(user.battler, false)
|
||||
score += 30
|
||||
if ai.skill_check(Battle::AI::AILevel.medium)
|
||||
aspeed = pbRoughStat(user, :SPEED)
|
||||
ospeed = pbRoughStat(target, :SPEED)
|
||||
if ai.trainer.medium_skill?
|
||||
aspeed = user.rough_stat(:SPEED)
|
||||
ospeed = target.rough_stat(: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 -= 40 if target.has_active_ability?([:GUTS, :MARVELSCALE, :QUICKFEET])
|
||||
elsif ai.trainer.medium_skill?
|
||||
score -= 90 if move.statusMove?
|
||||
end
|
||||
next score
|
||||
@@ -94,29 +89,25 @@ Battle::AI::Handlers::MoveEffectScore.add("TwoTurnAttackParalyzeTarget",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("TwoTurnAttackBurnTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
next 0 if !target.pbCanBurn?(user, false)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if !target.battler.pbCanBurn?(user.battler, false)
|
||||
score += 30
|
||||
if ai.skill_check(Battle::AI::AILevel.high)
|
||||
score -= 40 if target.hasActiveAbility?([:GUTS, :MARVELSCALE, :QUICKFEET, :FLAREBOOST])
|
||||
end
|
||||
score -= 40 if target.has_active_ability?([:GUTS, :MARVELSCALE, :QUICKFEET, :FLAREBOOST])
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("TwoTurnAttackFlinchTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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
|
||||
score += 20 if !target.has_active_ability?(:INNERFOCUS) &&
|
||||
target.effects[PBEffects::Substitute] == 0
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("TwoTurnAttackRaiseUserSpAtkSpDefSpd2",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if user.statStageAtMax?(:SPECIAL_ATTACK) &&
|
||||
user.statStageAtMax?(:SPECIAL_DEFENSE) &&
|
||||
user.statStageAtMax?(:SPEED)
|
||||
@@ -125,22 +116,22 @@ Battle::AI::Handlers::MoveEffectScore.add("TwoTurnAttackRaiseUserSpAtkSpDefSpd2"
|
||||
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)
|
||||
if ai.trainer.medium_skill?
|
||||
hasSpecialAttack = false
|
||||
user.eachMove do |m|
|
||||
user.battler.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)
|
||||
elsif ai.trainer.high_skill?
|
||||
score -= 90
|
||||
end
|
||||
end
|
||||
if ai.skill_check(Battle::AI::AILevel.high)
|
||||
aspeed = pbRoughStat(user, :SPEED)
|
||||
ospeed = pbRoughStat(target, :SPEED)
|
||||
if ai.trainer.high_skill?
|
||||
aspeed = user.rough_stat(:SPEED)
|
||||
ospeed = target.rough_stat(:SPEED)
|
||||
score += 30 if aspeed < ospeed && aspeed * 2 > ospeed
|
||||
end
|
||||
end
|
||||
@@ -149,7 +140,7 @@ Battle::AI::Handlers::MoveEffectScore.add("TwoTurnAttackRaiseUserSpAtkSpDefSpd2"
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("TwoTurnAttackChargeRaiseUserDefense1",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if move.statusMove?
|
||||
if user.statStageAtMax?(:DEFENSE)
|
||||
score -= 90
|
||||
@@ -164,9 +155,9 @@ Battle::AI::Handlers::MoveEffectScore.add("TwoTurnAttackChargeRaiseUserDefense1"
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("TwoTurnAttackChargeRaiseUserSpAtk1",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
aspeed = pbRoughStat(user, :SPEED)
|
||||
ospeed = pbRoughStat(target, :SPEED)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
aspeed = user.rough_stat(:SPEED)
|
||||
ospeed = target.rough_stat(:SPEED)
|
||||
if (aspeed > ospeed && user.hp > user.totalhp / 3) || user.hp > user.totalhp / 2
|
||||
score += 60
|
||||
else
|
||||
@@ -196,7 +187,7 @@ Battle::AI::Handlers::MoveEffectScore.add("TwoTurnAttackChargeRaiseUserSpAtk1",
|
||||
# MultiTurnAttackPowersUpEachTurn
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("MultiTurnAttackBideThenReturnDoubleDamage",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if user.hp <= user.totalhp / 4
|
||||
score -= 90
|
||||
elsif user.hp <= user.totalhp / 2
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
#
|
||||
#===============================================================================
|
||||
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)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if user.hp == user.totalhp ||
|
||||
!user.battler.pbCanSleep?(user.battler, false, nil, true)
|
||||
score += 70
|
||||
score -= user.hp * 140 / user.totalhp
|
||||
score += 30 if user.status != :NONE
|
||||
@@ -12,8 +13,9 @@ Battle::AI::Handlers::MoveEffectScore.add("HealUserFullyAndFallAsleep",
|
||||
)
|
||||
|
||||
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?)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if user.hp == user.totalhp ||
|
||||
(ai.trainer.medium_skill? && !user.battler.canHeal?)
|
||||
score += 50
|
||||
score -= user.hp * 100 / user.totalhp
|
||||
next score
|
||||
@@ -21,9 +23,10 @@ Battle::AI::Handlers::MoveEffectScore.add("HealUserHalfOfTotalHP",
|
||||
)
|
||||
|
||||
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
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if user.hp == user.totalhp ||
|
||||
(ai.trainer.medium_skill? && !user.battler.canHeal?)
|
||||
case user.battler.effectiveWeather
|
||||
when :Sun, :HarshSun
|
||||
score += 30
|
||||
when :None
|
||||
@@ -37,18 +40,20 @@ Battle::AI::Handlers::MoveEffectScore.add("HealUserDependingOnWeather",
|
||||
)
|
||||
|
||||
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?)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if user.hp == user.totalhp ||
|
||||
(ai.trainer.medium_skill? && !user.battler.canHeal?)
|
||||
score += 50
|
||||
score -= user.hp * 100 / user.totalhp
|
||||
score += 30 if user.effectiveWeather == :Sandstorm
|
||||
score += 30 if user.battler.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?)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if user.hp == user.totalhp ||
|
||||
(ai.trainer.medium_skill? && !user.battler.canHeal?)
|
||||
score += 50
|
||||
score -= user.hp * 100 / user.totalhp
|
||||
next score
|
||||
@@ -56,7 +61,7 @@ Battle::AI::Handlers::MoveEffectScore.add("HealUserHalfOfTotalHPLoseFlyingTypeTh
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("CureTargetStatusHealUserHalfOfTotalHP",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.status == :NONE
|
||||
score -= 90
|
||||
elsif user.hp == user.totalhp && target.opposes?(user)
|
||||
@@ -70,22 +75,22 @@ Battle::AI::Handlers::MoveEffectScore.add("CureTargetStatusHealUserHalfOfTotalHP
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("HealUserByTargetAttackLowerTargetAttack1",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.statStageAtMin?(:ATTACK)
|
||||
score -= 90
|
||||
else
|
||||
if target.pbCanLowerStatStage?(:ATTACK, user)
|
||||
if target.battler.pbCanLowerStatStage?(:ATTACK, user.battler)
|
||||
score += target.stages[:ATTACK] * 20
|
||||
if ai.skill_check(Battle::AI::AILevel.medium)
|
||||
if ai.trainer.medium_skill?
|
||||
hasPhysicalAttack = false
|
||||
target.eachMove do |m|
|
||||
target.battler.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)
|
||||
elsif ai.trainer.high_skill?
|
||||
score -= 90
|
||||
end
|
||||
end
|
||||
@@ -97,8 +102,8 @@ Battle::AI::Handlers::MoveEffectScore.add("HealUserByTargetAttackLowerTargetAtta
|
||||
)
|
||||
|
||||
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)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.has_active_ability?(:LIQUIDOOZE)
|
||||
score -= 70
|
||||
elsif user.hp <= user.totalhp / 2
|
||||
score += 20
|
||||
@@ -108,9 +113,9 @@ Battle::AI::Handlers::MoveEffectScore.add("HealUserByHalfOfDamageDone",
|
||||
)
|
||||
|
||||
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)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if !target.battler.asleep?
|
||||
if target.has_active_ability?(:LIQUIDOOZE)
|
||||
score -= 70
|
||||
elsif user.hp <= user.totalhp / 2
|
||||
score += 20
|
||||
@@ -120,8 +125,8 @@ Battle::AI::Handlers::MoveEffectScore.add("HealUserByHalfOfDamageDoneIfTargetAsl
|
||||
)
|
||||
|
||||
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)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.has_active_ability?(:LIQUIDOOZE)
|
||||
score -= 80
|
||||
elsif user.hp <= user.totalhp / 2
|
||||
score += 40
|
||||
@@ -131,10 +136,10 @@ Battle::AI::Handlers::MoveEffectScore.add("HealUserByThreeQuartersOfDamageDone",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("HealUserAndAlliesQuarterOfTotalHP",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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?)
|
||||
if b.hp == b.totalhp || (ai.trainer.medium_skill? && !b.canHeal?)
|
||||
score -= ally_amt / 2
|
||||
elsif b.hp < b.totalhp * 3 / 4
|
||||
score += ally_amt
|
||||
@@ -145,10 +150,10 @@ Battle::AI::Handlers::MoveEffectScore.add("HealUserAndAlliesQuarterOfTotalHP",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("HealUserAndAlliesQuarterOfTotalHPCureStatus",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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?)
|
||||
if b.hp == b.totalhp || (ai.trainer.medium_skill? && !b.canHeal?)
|
||||
score -= ally_amt
|
||||
elsif b.hp < b.totalhp * 3 / 4
|
||||
score += ally_amt
|
||||
@@ -160,7 +165,7 @@ Battle::AI::Handlers::MoveEffectScore.add("HealUserAndAlliesQuarterOfTotalHPCure
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("HealTargetHalfOfTotalHP",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if user.opposes?(target)
|
||||
if target.hp < target.totalhp / 2 && target.effects[PBEffects::Substitute] == 0
|
||||
score += 20
|
||||
@@ -170,11 +175,12 @@ Battle::AI::Handlers::MoveEffectScore.add("HealTargetHalfOfTotalHP",
|
||||
)
|
||||
|
||||
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?)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if user.hp == user.totalhp ||
|
||||
(ai.trainer.medium_skill? && !user.battler.canHeal?)
|
||||
score += 50
|
||||
score -= user.hp * 100 / user.totalhp
|
||||
if ai.skill_check(Battle::AI::AILevel.medium)
|
||||
if ai.trainer.medium_skill?
|
||||
score += 30 if battle.field.terrain == :Grassy
|
||||
end
|
||||
next score
|
||||
@@ -182,29 +188,29 @@ Battle::AI::Handlers::MoveEffectScore.add("HealTargetDependingOnGrassyTerrain",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("HealUserPositionNextTurn",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if user.effects[PBEffects::AquaRing]
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartHealUserEachTurnTrapUserInBattle",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if user.effects[PBEffects::Ingrain]
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartDamageTargetEachTurnIfTargetAsleep",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.effects[PBEffects::Nightmare] ||
|
||||
target.effects[PBEffects::Substitute] > 0
|
||||
score -= 90
|
||||
elsif !target.asleep?
|
||||
elsif !target.battler.asleep?
|
||||
score -= 90
|
||||
else
|
||||
score -= 90 if target.statusCount <= 1
|
||||
@@ -215,10 +221,10 @@ Battle::AI::Handlers::MoveEffectScore.add("StartDamageTargetEachTurnIfTargetAsle
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartLeechSeedTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.effects[PBEffects::LeechSeed] >= 0
|
||||
score -= 90
|
||||
elsif ai.skill_check(Battle::AI::AILevel.medium) && target.pbHasType?(:GRASS)
|
||||
elsif target.has_type?(:GRASS)
|
||||
score -= 90
|
||||
elsif user.turnCount == 0
|
||||
score += 60
|
||||
@@ -228,20 +234,20 @@ Battle::AI::Handlers::MoveEffectScore.add("StartLeechSeedTarget",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UserLosesHalfOfTotalHP",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if user.hp <= user.totalhp / 2
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UserLosesHalfOfTotalHPExplosive",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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
|
||||
elsif ai.trainer.medium_skill? && reserves == 0 && foes > 0
|
||||
score -= 100 # don't want to lose
|
||||
elsif ai.skill_check(Battle::AI::AILevel.high) && reserves == 0 && foes == 0
|
||||
elsif ai.trainer.high_skill? && reserves == 0 && foes == 0
|
||||
score += 80 # want to draw
|
||||
else
|
||||
score -= (user.totalhp - user.hp) * 75 / user.totalhp
|
||||
@@ -251,14 +257,14 @@ Battle::AI::Handlers::MoveEffectScore.add("UserLosesHalfOfTotalHPExplosive",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UserFaintsExplosive",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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
|
||||
elsif ai.trainer.medium_skill? && reserves == 0 && foes > 0
|
||||
score -= 100 # don't want to lose
|
||||
elsif ai.skill_check(Battle::AI::AILevel.high) && reserves == 0 && foes == 0
|
||||
elsif ai.trainer.high_skill? && reserves == 0 && foes == 0
|
||||
score += 80 # want to draw
|
||||
else
|
||||
score -= user.hp * 100 / user.totalhp
|
||||
@@ -268,14 +274,14 @@ Battle::AI::Handlers::MoveEffectScore.add("UserFaintsExplosive",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UserFaintsPowersUpInMistyTerrainExplosive",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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
|
||||
elsif ai.trainer.medium_skill? && reserves == 0 && foes > 0
|
||||
score -= 100 # don't want to lose
|
||||
elsif ai.skill_check(Battle::AI::AILevel.high) && reserves == 0 && foes == 0
|
||||
elsif ai.trainer.high_skill? && reserves == 0 && foes == 0
|
||||
score += 40 # want to draw
|
||||
score += 40 if battle.field.terrain == :Misty
|
||||
else
|
||||
@@ -289,9 +295,9 @@ Battle::AI::Handlers::MoveEffectScore.add("UserFaintsPowersUpInMistyTerrainExplo
|
||||
# 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)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if !target.battler.pbCanLowerStatStage?(:ATTACK, user.battler) &&
|
||||
!target.battler.pbCanLowerStatStage?(:SPECIAL_ATTACK, user.battler)
|
||||
score -= 100
|
||||
elsif battle.pbAbleNonActiveCount(user.idxOwnSide) == 0
|
||||
score -= 100
|
||||
@@ -305,7 +311,7 @@ Battle::AI::Handlers::MoveEffectScore.add("UserFaintsLowerTargetAtkSpAtk2",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UserFaintsHealAndCureReplacement",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score - 70
|
||||
}
|
||||
)
|
||||
@@ -314,7 +320,7 @@ Battle::AI::Handlers::MoveEffectScore.copy("UserFaintsHealAndCureReplacement",
|
||||
"UserFaintsHealAndCureReplacementRestorePP")
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartPerishCountsForAllBattlers",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if battle.pbAbleNonActiveCount(user.idxOwnSide) == 0
|
||||
score -= 90
|
||||
elsif target.effects[PBEffects::PerishSong] > 0
|
||||
@@ -325,7 +331,7 @@ Battle::AI::Handlers::MoveEffectScore.add("StartPerishCountsForAllBattlers",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("AttackerFaintsIfUserFaints",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
score += 50
|
||||
score -= user.hp * 100 / user.totalhp
|
||||
score += 30 if user.hp <= user.totalhp / 10
|
||||
@@ -334,7 +340,7 @@ Battle::AI::Handlers::MoveEffectScore.add("AttackerFaintsIfUserFaints",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("SetAttackerMovePPTo0IfUserFaints",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
score += 50
|
||||
score -= user.hp * 100 / user.totalhp
|
||||
score += 30 if user.hp <= user.totalhp / 10
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UserTakesTargetItem",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
if ai.skill_check(Battle::AI::AILevel.high)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.high_skill?
|
||||
if !user.item && target.item
|
||||
score += 40
|
||||
else
|
||||
@@ -17,11 +17,11 @@ Battle::AI::Handlers::MoveEffectScore.add("UserTakesTargetItem",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("TargetTakesUserItem",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if !user.item || target.item
|
||||
score -= 90
|
||||
elsif user.hasActiveItem?([:FLAMEORB, :TOXICORB, :STICKYBARB, :IRONBALL,
|
||||
:CHOICEBAND, :CHOICESCARF, :CHOICESPECS])
|
||||
elsif user.has_active_item?([:FLAMEORB, :TOXICORB, :STICKYBARB, :IRONBALL,
|
||||
:CHOICEBAND, :CHOICESCARF, :CHOICESPECS])
|
||||
score += 50
|
||||
else
|
||||
score -= 80
|
||||
@@ -31,27 +31,27 @@ Battle::AI::Handlers::MoveEffectScore.add("TargetTakesUserItem",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UserTargetSwapItems",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if !user.item && !target.item
|
||||
score -= 90
|
||||
elsif ai.skill_check(Battle::AI::AILevel.high) && target.hasActiveAbility?(:STICKYHOLD)
|
||||
elsif target.has_active_ability?(:STICKYHOLD)
|
||||
score -= 90
|
||||
elsif user.hasActiveItem?([:FLAMEORB, :TOXICORB, :STICKYBARB, :IRONBALL,
|
||||
:CHOICEBAND, :CHOICESCARF, :CHOICESPECS])
|
||||
elsif user.has_active_item?([: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"
|
||||
score -= 30 if user.battler.lastMoveUsed &&
|
||||
GameData::Move.get(user.battler.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
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if !user.battler.recycleItem || user.item
|
||||
score -= 80
|
||||
elsif user.recycleItem
|
||||
elsif user.battler.recycleItem
|
||||
score += 30
|
||||
end
|
||||
next score
|
||||
@@ -59,8 +59,8 @@ Battle::AI::Handlers::MoveEffectScore.add("RestoreUserConsumedItem",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("RemoveTargetItem",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
if ai.skill_check(Battle::AI::AILevel.high)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.high_skill?
|
||||
score += 20 if target.item
|
||||
end
|
||||
next score
|
||||
@@ -68,9 +68,9 @@ Battle::AI::Handlers::MoveEffectScore.add("RemoveTargetItem",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("DestroyTargetBerryOrGem",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.effects[PBEffects::Substitute] == 0
|
||||
if ai.skill_check(Battle::AI::AILevel.high) && target.item && target.item.is_berry?
|
||||
if ai.trainer.high_skill? && target.item && target.item.is_berry?
|
||||
score += 30
|
||||
end
|
||||
end
|
||||
@@ -79,11 +79,11 @@ Battle::AI::Handlers::MoveEffectScore.add("DestroyTargetBerryOrGem",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("CorrodeTargetItem",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
if battle.corrosiveGas[target.index % 2][target.pokemonIndex]
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if battle.corrosiveGas[target.side][target.party_index]
|
||||
score -= 100
|
||||
elsif !target.item || !target.itemActive? || target.unlosableItem?(target.item) ||
|
||||
target.hasActiveAbility?(:STICKYHOLD)
|
||||
elsif !target.item || !target.item_active? || target.battler.unlosableItem?(target.item) ||
|
||||
target.has_active_ability?(:STICKYHOLD)
|
||||
score -= 90
|
||||
elsif target.effects[PBEffects::Substitute] > 0
|
||||
score -= 90
|
||||
@@ -95,24 +95,24 @@ Battle::AI::Handlers::MoveEffectScore.add("CorrodeTargetItem",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartTargetCannotUseItem",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if target.effects[PBEffects::Embargo] > 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartNegateHeldItems",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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?
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if !user.item || !user.item.is_berry? || !user.item_active?
|
||||
score -= 100
|
||||
else
|
||||
if ai.skill_check(Battle::AI::AILevel.high)
|
||||
if ai.trainer.high_skill?
|
||||
useful_berries = [
|
||||
:ORANBERRY, :SITRUSBERRY, :AGUAVBERRY, :APICOTBERRY, :CHERIBERRY,
|
||||
:CHESTOBERRY, :FIGYBERRY, :GANLONBERRY, :IAPAPABERRY, :KEEBERRY,
|
||||
@@ -122,11 +122,12 @@ Battle::AI::Handlers::MoveEffectScore.add("UserConsumeBerryRaiseDefense2",
|
||||
]
|
||||
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?
|
||||
if ai.trainer.medium_skill?
|
||||
score += 20 if user.battler.canHeal? && user.hp < user.totalhp / 3 &&
|
||||
user.has_active_ability?(:CHEEKPOUCH)
|
||||
score += 20 if user.has_active_ability?([:HARVEST, :RIPEN]) ||
|
||||
user.battler.pbHasMoveFunction?("RestoreUserConsumedItem") # Recycle
|
||||
score += 20 if !user.battler.canConsumeBerry?
|
||||
end
|
||||
score -= user.stages[:DEFENSE] * 20
|
||||
end
|
||||
@@ -135,7 +136,7 @@ Battle::AI::Handlers::MoveEffectScore.add("UserConsumeBerryRaiseDefense2",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("AllBattlersConsumeBerry",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
useful_berries = [
|
||||
:ORANBERRY, :SITRUSBERRY, :AGUAVBERRY, :APICOTBERRY, :CHERIBERRY,
|
||||
:CHESTOBERRY, :FIGYBERRY, :GANLONBERRY, :IAPAPABERRY, :KEEBERRY,
|
||||
@@ -147,11 +148,11 @@ Battle::AI::Handlers::MoveEffectScore.add("AllBattlersConsumeBerry",
|
||||
if !b.item || !b.item.is_berry? || !b.itemActive?
|
||||
score -= 100 / battle.pbSideSize(user.index)
|
||||
else
|
||||
if ai.skill_check(Battle::AI::AILevel.high)
|
||||
if ai.trainer.high_skill?
|
||||
amt = 30 / battle.pbSideSize(user.index)
|
||||
score += amt if useful_berries.include?(b.item_id)
|
||||
end
|
||||
if ai.skill_check(Battle::AI::AILevel.medium)
|
||||
if ai.trainer.medium_skill?
|
||||
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]) ||
|
||||
@@ -160,7 +161,7 @@ Battle::AI::Handlers::MoveEffectScore.add("AllBattlersConsumeBerry",
|
||||
end
|
||||
end
|
||||
end
|
||||
if ai.skill_check(Battle::AI::AILevel.high)
|
||||
if ai.trainer.high_skill?
|
||||
battle.allOtherSideBattlers(user.index).each do |b|
|
||||
amt = 10 / battle.pbSideSize(target.index)
|
||||
score -= amt if b.hasActiveItem?(useful_berries)
|
||||
@@ -175,9 +176,9 @@ Battle::AI::Handlers::MoveEffectScore.add("AllBattlersConsumeBerry",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UserConsumeTargetBerry",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.effects[PBEffects::Substitute] == 0
|
||||
if ai.skill_check(Battle::AI::AILevel.high) && target.item && target.item.is_berry?
|
||||
if ai.trainer.high_skill? && target.item && target.item.is_berry?
|
||||
score += 30
|
||||
end
|
||||
end
|
||||
@@ -186,8 +187,8 @@ Battle::AI::Handlers::MoveEffectScore.add("UserConsumeTargetBerry",
|
||||
)
|
||||
|
||||
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?
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if !user.item || !user.item_active? ||
|
||||
user.battler.unlosableItem?(user.item) || user.item.is_poke_ball?
|
||||
}
|
||||
)
|
||||
|
||||
@@ -2,21 +2,21 @@
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("RedirectAllMovesToUser",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
next 0 if user.allAllies.length == 0
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if user.battler.allAllies.length == 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("RedirectAllMovesToTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
next 0 if user.allAllies.length == 0
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if user.battler.allAllies.length == 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("CannotBeRedirected",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
redirection = false
|
||||
user.allOpposing.each do |b|
|
||||
user.battler.allOpposing.each do |b|
|
||||
next if b.index == target.index
|
||||
if b.effects[PBEffects::RagePowder] ||
|
||||
b.effects[PBEffects::Spotlight] > 0 ||
|
||||
@@ -27,7 +27,7 @@ Battle::AI::Handlers::MoveEffectScore.add("CannotBeRedirected",
|
||||
break
|
||||
end
|
||||
end
|
||||
score += 50 if redirection && ai.skill_check(Battle::AI::AILevel.medium)
|
||||
score += 50 if redirection && ai.trainer.medium_skill?
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -35,9 +35,9 @@ Battle::AI::Handlers::MoveEffectScore.add("CannotBeRedirected",
|
||||
# RandomlyDamageOrHealTarget
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("HealAllyOrDamageFoe",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if !target.opposes?(user)
|
||||
if target.hp == target.totalhp || (ai.skill_check(Battle::AI::AILevel.medium) && !target.canHeal?)
|
||||
if target.hp == target.totalhp || (ai.trainer.medium_skill? && !target.battler.canHeal?)
|
||||
score -= 90
|
||||
else
|
||||
score += 50
|
||||
@@ -49,8 +49,8 @@ Battle::AI::Handlers::MoveEffectScore.add("HealAllyOrDamageFoe",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("CurseTargetOrLowerUserSpd1RaiseUserAtkDef1",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
if user.pbHasType?(:GHOST)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if user.has_type?(:GHOST)
|
||||
if target.effects[PBEffects::Curse]
|
||||
score -= 90
|
||||
elsif user.hp <= user.totalhp / 2
|
||||
@@ -74,18 +74,18 @@ Battle::AI::Handlers::MoveEffectScore.add("CurseTargetOrLowerUserSpd1RaiseUserAt
|
||||
# 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?
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score + 40 if battle.field.terrain == :Psychic && user.battler.affectedByTerrain?
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("TargetNextFireMoveDamagesTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
aspeed = pbRoughStat(user, :SPEED)
|
||||
ospeed = pbRoughStat(target, :SPEED)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
aspeed = user.rough_stat(:SPEED)
|
||||
ospeed = target.rough_stat(:SPEED)
|
||||
if aspeed > ospeed
|
||||
score -= 90
|
||||
elsif target.pbHasMoveType?(:FIRE)
|
||||
elsif target.battler.pbHasMoveType?(:FIRE)
|
||||
score += 30
|
||||
end
|
||||
next score
|
||||
@@ -97,23 +97,23 @@ Battle::AI::Handlers::MoveEffectScore.add("TargetNextFireMoveDamagesTarget",
|
||||
# DoublePowerAfterFusionBolt
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("PowerUpAllyMove",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
next 0 if user.allAllies.empty?
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if user.battler.allAllies.empty?
|
||||
next score + 30
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("CounterPhysicalDamage",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.effects[PBEffects::HyperBeam] > 0
|
||||
score -= 90
|
||||
else
|
||||
attack = pbRoughStat(user, :ATTACK)
|
||||
spatk = pbRoughStat(user, :SPECIAL_ATTACK)
|
||||
attack = user.rough_stat(:ATTACK)
|
||||
spatk = user.rough_stat(: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)
|
||||
elsif ai.trainer.medium_skill? && target.battler.lastMoveUsed
|
||||
moveData = GameData::Move.get(target.battler.lastMoveUsed)
|
||||
score += 60 if moveData.physical?
|
||||
end
|
||||
end
|
||||
@@ -122,16 +122,16 @@ Battle::AI::Handlers::MoveEffectScore.add("CounterPhysicalDamage",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("CounterSpecialDamage",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.effects[PBEffects::HyperBeam] > 0
|
||||
score -= 90
|
||||
else
|
||||
attack = pbRoughStat(user, :ATTACK)
|
||||
spatk = pbRoughStat(user, :SPECIAL_ATTACK)
|
||||
attack = user.rough_stat(:ATTACK)
|
||||
spatk = user.rough_stat(: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)
|
||||
elsif ai.trainer.medium_skill? && target.battler.lastMoveUsed
|
||||
moveData = GameData::Move.get(target.battler.lastMoveUsed)
|
||||
score += 60 if moveData.special?
|
||||
end
|
||||
end
|
||||
@@ -140,21 +140,21 @@ Battle::AI::Handlers::MoveEffectScore.add("CounterSpecialDamage",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("CounterDamagePlusHalf",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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|
|
||||
proc { |score, move, user, target, 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
|
||||
elsif user.battler.pbHasMoveFunction?("PowerDependsOnUserStockpile",
|
||||
"HealUserDependingOnUserStockpile") # Spit Up, Swallow
|
||||
score += 20 # More preferable if user also has Spit Up/Swallow
|
||||
end
|
||||
next score
|
||||
@@ -162,13 +162,13 @@ Battle::AI::Handlers::MoveEffectScore.add("UserAddStockpileRaiseDefSpDef1",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("PowerDependsOnUserStockpile",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if user.effects[PBEffects::Stockpile] == 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("HealUserDependingOnUserStockpile",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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]]
|
||||
@@ -187,11 +187,11 @@ Battle::AI::Handlers::MoveEffectScore.add("HealUserDependingOnUserStockpile",
|
||||
# UseLastMoveUsed
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UseLastMoveUsedByTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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] }
|
||||
if ai.trainer.high_skill?
|
||||
score -= 100 if !target.battler.lastRegularMoveUsed ||
|
||||
GameData::Move.get(target.battler.lastRegularMoveUsed).flags.none? { |f| f[/^CanMirrorMove$/i] }
|
||||
end
|
||||
next score
|
||||
}
|
||||
@@ -206,8 +206,8 @@ Battle::AI::Handlers::MoveEffectScore.add("UseLastMoveUsedByTarget",
|
||||
# UseRandomMoveFromUserParty
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UseRandomUserMoveIfAsleep",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
if user.asleep?
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if user.battler.asleep?
|
||||
score += 100 # Because it can only be used while asleep
|
||||
else
|
||||
score -= 90
|
||||
@@ -221,23 +221,23 @@ Battle::AI::Handlers::MoveEffectScore.add("UseRandomUserMoveIfAsleep",
|
||||
# StealAndUseBeneficialStatusMove
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("ReplaceMoveThisBattleWithTargetLastMoveUsed",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
moveBlacklist = [
|
||||
"Struggle", # Struggle
|
||||
"ReplaceMoveThisBattleWithTargetLastMoveUsed", # Mimic
|
||||
"ReplaceMoveWithTargetLastMoveUsed", # Sketch
|
||||
"UseRandomMove" # Metronome
|
||||
]
|
||||
if user.effects[PBEffects::Transform] || !target.lastRegularMoveUsed
|
||||
if user.effects[PBEffects::Transform] || !target.battler.lastRegularMoveUsed
|
||||
score -= 90
|
||||
else
|
||||
lastMoveData = GameData::Move.get(target.lastRegularMoveUsed)
|
||||
lastMoveData = GameData::Move.get(target.battler.lastRegularMoveUsed)
|
||||
if moveBlacklist.include?(lastMoveData.function_code) ||
|
||||
lastMoveData.type == :SHADOW
|
||||
score -= 90
|
||||
end
|
||||
user.eachMove do |m|
|
||||
next if m != target.lastRegularMoveUsed
|
||||
user.battler.eachMove do |m|
|
||||
next if m != target.battler.lastRegularMoveUsed
|
||||
score -= 90
|
||||
break
|
||||
end
|
||||
@@ -247,21 +247,21 @@ Battle::AI::Handlers::MoveEffectScore.add("ReplaceMoveThisBattleWithTargetLastMo
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("ReplaceMoveWithTargetLastMoveUsed",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
moveBlacklist = [
|
||||
"Struggle", # Struggle
|
||||
"ReplaceMoveWithTargetLastMoveUsed" # Sketch
|
||||
]
|
||||
if user.effects[PBEffects::Transform] || !target.lastRegularMoveUsed
|
||||
if user.effects[PBEffects::Transform] || !target.battler.lastRegularMoveUsed
|
||||
score -= 90
|
||||
else
|
||||
lastMoveData = GameData::Move.get(target.lastRegularMoveUsed)
|
||||
lastMoveData = GameData::Move.get(target.battler.lastRegularMoveUsed)
|
||||
if moveBlacklist.include?(lastMoveData.function_code) ||
|
||||
lastMoveData.type == :SHADOW
|
||||
score -= 90
|
||||
end
|
||||
user.eachMove do |m|
|
||||
next if m != target.lastRegularMoveUsed
|
||||
user.battler.eachMove do |m|
|
||||
next if m != target.battler.lastRegularMoveUsed
|
||||
score -= 90 # User already knows the move that will be Sketched
|
||||
break
|
||||
end
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("FleeFromBattle",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if battle.trainerBattle?
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserStatusMove",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if !battle.pbCanChooseNonActive?(user.index) ||
|
||||
battle.pbTeamAbleNonActiveCount(user.index) > 1 # Don't switch in ace
|
||||
score -= 100
|
||||
@@ -22,7 +22,7 @@ Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserStatusMove",
|
||||
score -= total * 10
|
||||
# special case: user has no damaging moves
|
||||
hasDamagingMove = false
|
||||
user.eachMove do |m|
|
||||
user.battler.eachMove do |m|
|
||||
next if !m.damagingMove?
|
||||
hasDamagingMove = true
|
||||
break
|
||||
@@ -35,14 +35,14 @@ Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserStatusMove",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserDamagingMove",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
avg = target.stages[:ATTACK] * 10
|
||||
avg += target.stages[:SPECIAL_ATTACK] * 10
|
||||
score += avg / 2
|
||||
@@ -51,7 +51,7 @@ Battle::AI::Handlers::MoveEffectScore.add("LowerTargetAtkSpAtk1SwitchOutUser",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserPassOnEffects",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if battle.pbCanChooseNonActive?(user.index)
|
||||
score -= 40 if user.effects[PBEffects::Confusion] > 0
|
||||
total = 0
|
||||
@@ -62,7 +62,7 @@ Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserPassOnEffects",
|
||||
score += total * 10
|
||||
# special case: user has no damaging moves
|
||||
hasDamagingMove = false
|
||||
user.eachMove do |m|
|
||||
user.battler.eachMove do |m|
|
||||
next if !m.damagingMove?
|
||||
hasDamagingMove = true
|
||||
break
|
||||
@@ -77,9 +77,9 @@ Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserPassOnEffects",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("SwitchOutTargetStatusMove",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.effects[PBEffects::Ingrain] ||
|
||||
(ai.skill_check(Battle::AI::AILevel.high) && target.hasActiveAbility?(:SUCTIONCUPS))
|
||||
target.has_active_ability?(:SUCTIONCUPS)
|
||||
score -= 90
|
||||
else
|
||||
ch = 0
|
||||
@@ -98,9 +98,9 @@ Battle::AI::Handlers::MoveEffectScore.add("SwitchOutTargetStatusMove",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("SwitchOutTargetDamagingMove",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if !target.effects[PBEffects::Ingrain] &&
|
||||
!(ai.skill_check(Battle::AI::AILevel.high) && target.hasActiveAbility?(:SUCTIONCUPS))
|
||||
!target.has_active_ability?(: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]
|
||||
@@ -110,37 +110,37 @@ Battle::AI::Handlers::MoveEffectScore.add("SwitchOutTargetDamagingMove",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("BindTarget",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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|
|
||||
proc { |score, move, user, target, 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|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if target.effects[PBEffects::MeanLook] >= 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("TrapTargetInBattleLowerTargetDefSpDef1EachTurn",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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)
|
||||
score += 30 if !target.battler.trappedInBattle?
|
||||
score -= 100 if !target.battler.pbCanLowerStatStage?(:DEFENSE, user.battler, move.move) &&
|
||||
!target.battler.pbCanLowerStatStage?(:SPECIAL_DEFENSE, user.battler, move.move)
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("TrapUserAndTargetInBattle",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.effects[PBEffects::JawLock] < 0
|
||||
score += 40 if !user.trappedInBattle? && !target.trappedInBattle?
|
||||
score += 40 if !user.battler.trappedInBattle? && !target.battler.trappedInBattle?
|
||||
end
|
||||
next score
|
||||
}
|
||||
@@ -151,10 +151,10 @@ Battle::AI::Handlers::MoveEffectScore.add("TrapUserAndTargetInBattle",
|
||||
# PursueSwitchingFoe
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UsedAfterUserTakesPhysicalDamage",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
if ai.skill_check(Battle::AI::AILevel.medium)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.medium_skill?
|
||||
hasPhysicalAttack = false
|
||||
target.eachMove do |m|
|
||||
target.battler.eachMove do |m|
|
||||
next if !m.physicalMove?(m.type)
|
||||
hasPhysicalAttack = true
|
||||
break
|
||||
@@ -166,9 +166,9 @@ Battle::AI::Handlers::MoveEffectScore.add("UsedAfterUserTakesPhysicalDamage",
|
||||
)
|
||||
|
||||
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|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.medium_skill?
|
||||
user.battler.allAllies.each do |b|
|
||||
next if !b.pbHasMove?(move.id)
|
||||
score += 20
|
||||
end
|
||||
@@ -182,11 +182,11 @@ Battle::AI::Handlers::MoveEffectScore.add("UsedAfterAllyRoundWithDoublePower",
|
||||
# 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?
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.medium_skill?
|
||||
if !target.battler.lastRegularMoveUsed ||
|
||||
!target.battler.pbHasMove?(target.battler.lastRegularMoveUsed) ||
|
||||
target.battler.usingMultiTurnAttack?
|
||||
score -= 90
|
||||
else
|
||||
# Without lots of code here to determine good/bad moves and relative
|
||||
@@ -201,19 +201,17 @@ Battle::AI::Handlers::MoveEffectScore.add("TargetUsesItsLastUsedMoveAgain",
|
||||
# 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
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.medium_skill? && @battle.field.terrain == :Grassy
|
||||
score += 40 if target.faster_than?(user)
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("LowerPPOfTargetLastMoveBy3",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
last_move = target.pbGetMoveWithID(target.lastRegularMoveUsed)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
last_move = target.battler.pbGetMoveWithID(target.battler.lastRegularMoveUsed)
|
||||
if last_move && last_move.total_pp > 0 && last_move.pp <= 3
|
||||
score += 50
|
||||
end
|
||||
@@ -222,38 +220,36 @@ Battle::AI::Handlers::MoveEffectScore.add("LowerPPOfTargetLastMoveBy3",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("LowerPPOfTargetLastMoveBy4",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score - 40
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("DisableTargetLastMoveUsed",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if target.effects[PBEffects::Disable] > 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("DisableTargetUsingSameMoveConsecutively",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.effects[PBEffects::Encore] > 0
|
||||
score -= 90
|
||||
elsif aspeed > ospeed
|
||||
if target.lastRegularMoveUsed
|
||||
moveData = GameData::Move.get(target.lastRegularMoveUsed)
|
||||
elsif user.faster_than?(target)
|
||||
if target.battler.lastRegularMoveUsed
|
||||
moveData = GameData::Move.get(target.battler.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))
|
||||
Effectiveness.ineffective?(user.effectiveness_of_type_against_battler(moveData.type, target))
|
||||
score += 60
|
||||
end
|
||||
else
|
||||
@@ -265,22 +261,22 @@ Battle::AI::Handlers::MoveEffectScore.add("DisableTargetUsingDifferentMove",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("DisableTargetStatusMoves",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if target.effects[PBEffects::Taunt] > 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("DisableTargetHealingMoves",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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)
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.effects[PBEffects::ThroatChop] == 0 && ai.trainer.high_skill?
|
||||
hasSoundMove = false
|
||||
user.eachMove do |m|
|
||||
user.battler.eachMove do |m|
|
||||
next if !m.soundMove?
|
||||
hasSoundMove = true
|
||||
break
|
||||
@@ -292,13 +288,13 @@ Battle::AI::Handlers::MoveEffectScore.add("DisableTargetSoundMoves",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("DisableTargetMovesKnownByUser",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next 0 if target.effects[PBEffects::Imprison]
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("AllBattlersLoseHalfHPUserSkipsNextTurn",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, 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
|
||||
@@ -307,7 +303,7 @@ Battle::AI::Handlers::MoveEffectScore.add("AllBattlersLoseHalfHPUserSkipsNextTur
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("AllBattlersLoseHalfHPUserSkipsNextTurn",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
score += 20 # Shadow moves are more preferable
|
||||
score -= 40
|
||||
next score
|
||||
@@ -315,7 +311,7 @@ Battle::AI::Handlers::MoveEffectScore.add("AllBattlersLoseHalfHPUserSkipsNextTur
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartShadowSkyWeather",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
score += 20 # Shadow moves are more preferable
|
||||
if battle.pbCheckGlobalAbility(:AIRLOCK) ||
|
||||
battle.pbCheckGlobalAbility(:CLOUDNINE)
|
||||
@@ -328,7 +324,7 @@ Battle::AI::Handlers::MoveEffectScore.add("StartShadowSkyWeather",
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::MoveEffectScore.add("RemoveAllScreens",
|
||||
proc { |score, move, user, target, skill, ai, battle|
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
score += 20 # Shadow moves are more preferable
|
||||
if target.pbOwnSide.effects[PBEffects::AuroraVeil] > 0 ||
|
||||
target.pbOwnSide.effects[PBEffects::Reflect] > 0 ||
|
||||
|
||||
53
Data/Scripts/011_Battle/005_AI/101_AITrainer.rb
Normal file
53
Data/Scripts/011_Battle/005_AI/101_AITrainer.rb
Normal file
@@ -0,0 +1,53 @@
|
||||
#===============================================================================
|
||||
# AI skill levels:
|
||||
# 0: Wild Pokémon
|
||||
# 1-31: Basic trainer (young/inexperienced)
|
||||
# 32-47: Medium skill
|
||||
# 48-99: High skill
|
||||
# 100+: Best skill (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.
|
||||
#
|
||||
# Skill flags:
|
||||
#===============================================================================
|
||||
class Battle::AI::AITrainer
|
||||
attr_reader :side, :trainer_index
|
||||
attr_reader :skill
|
||||
|
||||
def initialize(ai, side, index, trainer)
|
||||
@ai = ai
|
||||
@side = side
|
||||
@trainer_index = index
|
||||
@trainer = trainer
|
||||
|
||||
@skill = 0
|
||||
@skill_flags = []
|
||||
set_up_skill
|
||||
end
|
||||
|
||||
def set_up_skill
|
||||
return if !@trainer
|
||||
@skill = @trainer.skill_level
|
||||
# TODO: Add skill flags depending on @skill.
|
||||
end
|
||||
|
||||
def has_skill_flag?(flag)
|
||||
return @skill_flags.include?(flag)
|
||||
end
|
||||
|
||||
# TODO: This will eventually be replaced by something else, maybe skill flags.
|
||||
def medium_skill?
|
||||
return @skill >= 32
|
||||
end
|
||||
|
||||
# TODO: This will eventually be replaced by something else, maybe skill flags.
|
||||
def high_skill?
|
||||
return @skill >= 48
|
||||
end
|
||||
|
||||
# TODO: This will eventually be replaced by something else, maybe skill flags.
|
||||
def best_skill?
|
||||
return @skill >= 100
|
||||
end
|
||||
end
|
||||
239
Data/Scripts/011_Battle/005_AI/102_AIBattler.rb
Normal file
239
Data/Scripts/011_Battle/005_AI/102_AIBattler.rb
Normal file
@@ -0,0 +1,239 @@
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
class Battle::AI::AIBattler
|
||||
attr_reader :index, :side, :party_index
|
||||
attr_reader :battler
|
||||
|
||||
def initialize(ai, index)
|
||||
@ai = ai
|
||||
@index = index
|
||||
@side = (@ai.battle.opposes?(@index)) ? 1 : 0
|
||||
refresh_battler
|
||||
end
|
||||
|
||||
def refresh_battler
|
||||
old_party_index = @party_index
|
||||
@battler = @ai.battle.battlers[@index]
|
||||
@party_index = @battler.pokemonIndex
|
||||
if @party_index != old_party_index
|
||||
# TODO: Start of battle or Pokémon switched/shifted; recalculate roles,
|
||||
# etc.
|
||||
end
|
||||
end
|
||||
|
||||
def level; return @battler.level; end
|
||||
def hp; return @battler.hp; end
|
||||
def status; return @Battler.status; end
|
||||
def statusCount; return @battler.statusCount; end
|
||||
def totalhp; return @battler.totalhp; end
|
||||
def gender; return @battler.gender; end
|
||||
def turnCount; return @battler.turnCount; end
|
||||
def effects; return @battler.effects; end
|
||||
def stages; return @battler.stages; end
|
||||
def statStageAtMax?(stat); return @battler.statStageAtMax?(stat); end
|
||||
def statStageAtMin?(stat); return @battler.statStageAtMin?(stat); end
|
||||
|
||||
def wild?
|
||||
return @ai.battle.wildBattle? && opposes?
|
||||
end
|
||||
|
||||
def opposes?(other = nil)
|
||||
return @side == 1 if other.nil?
|
||||
return other.side != @side
|
||||
end
|
||||
|
||||
def idxOwnSide; return @battler.idxOwnSide; end
|
||||
def pbOwnSide; return @battler.pbOwnSide; end
|
||||
def idxOpposingSide; return @battler.idxOpposingSide; end
|
||||
def pbOpposingSide; return @battler.pbOpposingSide; end
|
||||
|
||||
def faster_than?(other)
|
||||
return false if other.nil?
|
||||
this_speed = rough_stat(:SPEED)
|
||||
other_speed = other.rough_stat(:SPEED)
|
||||
return (this_speed > other_speed) ^ (@ai.battle.field.effects[PBEffects::TrickRoom] > 0)
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
|
||||
def speed; return @battler.speed; end
|
||||
|
||||
# TODO: Cache calculated rough stats? Forget them in def refresh_battler.
|
||||
def rough_stat(stat)
|
||||
return @battler.pbSpeed if stat == :SPEED && @ai.trainer.high_skill?
|
||||
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
|
||||
value = 0
|
||||
case stat
|
||||
when :ATTACK then value = @battler.attack
|
||||
when :DEFENSE then value = @battler.defense
|
||||
when :SPECIAL_ATTACK then value = @battler.spatk
|
||||
when :SPECIAL_DEFENSE then value = @battler.spdef
|
||||
when :SPEED then value = @battler.speed
|
||||
end
|
||||
return (value.to_f * stageMul[stage] / stageDiv[stage]).floor
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
|
||||
def types; return @battler.types; end
|
||||
|
||||
def has_type?(type)
|
||||
return @battler.pbHasType?(type)
|
||||
end
|
||||
|
||||
def effectiveness_of_type_against_battler(type, user = nil)
|
||||
return Effectiveness::NORMAL_EFFECTIVE if !type
|
||||
return Effectiveness::NORMAL_EFFECTIVE if type == :GROUND &&
|
||||
has_type?(:FLYING) &&
|
||||
has_active_item?(:IRONBALL)
|
||||
# Get effectivenesses
|
||||
type_mults = [Effectiveness::NORMAL_EFFECTIVE_ONE] * 3 # 3 types max
|
||||
if type == :SHADOW
|
||||
if @battler.shadowPokemon?
|
||||
type_mults[0] = Effectiveness::NOT_VERY_EFFECTIVE_ONE
|
||||
else
|
||||
type_mults[0] = Effectiveness::SUPER_EFFECTIVE_ONE
|
||||
end
|
||||
else
|
||||
@battler.pbTypes(true).each_with_index do |defend_type, i|
|
||||
type_mults[i] = effectiveness_of_type_against_single_battler_type(type, defend_type, user)
|
||||
end
|
||||
end
|
||||
# Multiply all effectivenesses together
|
||||
ret = 1
|
||||
type_mults.each { |m| ret *= m }
|
||||
return ret
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
|
||||
def ability_id; return @battler.ability_id; end
|
||||
def ability; return @battler.ability; end
|
||||
|
||||
def ability_active?
|
||||
# Only a high skill AI knows what an opponent's ability is
|
||||
return false if @ai.trainer.side != @side && !@ai.trainer.high_skill?
|
||||
return @battler.abilityActive?
|
||||
end
|
||||
|
||||
def has_active_ability?(ability)
|
||||
# Only a high skill AI knows what an opponent's ability is
|
||||
return false if @ai.trainer.side != @side && !@ai.trainer.high_skill?
|
||||
return @battler.hasActiveAbility?(ability)
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
|
||||
def item_id; return @battler.item_id; end
|
||||
def item; return @battler.item; end
|
||||
|
||||
def item_active?
|
||||
# Only a high skill AI knows what an opponent's held item is
|
||||
return false if @ai.trainer.side != @side && !@ai.trainer.high_skill?
|
||||
return @battler.itemActive?
|
||||
end
|
||||
|
||||
def has_active_item?(item)
|
||||
# Only a high skill AI knows what an opponent's held item is
|
||||
return false if @ai.trainer.side != @side && !@ai.trainer.high_skill?
|
||||
return @battler.hasActiveItem?(item)
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
|
||||
def can_switch_lax?
|
||||
return false if wild?
|
||||
@ai.battle.eachInTeamFromBattlerIndex(@index) do |pkmn, i|
|
||||
return true if @ai.battle.pbCanSwitchLax?(@index, i)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
|
||||
def immune_to_move?
|
||||
user = @ai.user
|
||||
user_battler = user.battler
|
||||
move = @ai.move
|
||||
# TODO: Add consideration of user's Mold Breaker.
|
||||
move_type = move.rough_type
|
||||
typeMod = effectiveness_of_type_against_battler(move_type, user)
|
||||
# Type effectiveness
|
||||
return true if move.damagingMove? && Effectiveness.ineffective?(typeMod)
|
||||
# Immunity due to ability/item/other effects
|
||||
if @ai.trainer.medium_skill?
|
||||
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 @battler.airborne? && !move.move.hitsFlyingTargets?
|
||||
when :FIRE
|
||||
return true if has_active_ability?(:FLASHFIRE)
|
||||
when :WATER
|
||||
return true if has_active_ability?([:DRYSKIN, :STORMDRAIN, :WATERABSORB])
|
||||
when :GRASS
|
||||
return true if has_active_ability?(:SAPSIPPER)
|
||||
when :ELECTRIC
|
||||
return true if has_active_ability?([:LIGHTNINGROD, :MOTORDRIVE, :VOLTABSORB])
|
||||
end
|
||||
return true if move.damagingMove? && Effectiveness.not_very_effective?(typeMod) &&
|
||||
has_active_ability?(:WONDERGUARD)
|
||||
return true if move.damagingMove? && user.index != @index && !opposes?(user) &&
|
||||
has_active_ability?(:TELEPATHY)
|
||||
return true if move.statusMove? && move.move.canMagicCoat? && has_active_ability?(:MAGICBOUNCE) &&
|
||||
opposes?(user)
|
||||
return true if move.move.soundMove? && has_active_ability?(:SOUNDPROOF)
|
||||
return true if move.move.bombMove? && has_active_ability?(:BULLETPROOF)
|
||||
if move.move.powderMove?
|
||||
return true if has_type?(:GRASS)
|
||||
return true if has_active_ability?(:OVERCOAT)
|
||||
return true if has_active_ability?(:SAFETYGOGGLES)
|
||||
end
|
||||
return true if move.move.statusMove? && @battler.effects[PBEffects::Substitute] > 0 &&
|
||||
!move.move.ignoresSubstitute?(user) && user.index != @index
|
||||
return true if move.move.statusMove? && Settings::MECHANICS_GENERATION >= 7 &&
|
||||
user.has_active_ability?(:PRANKSTER) && has_type?(:DARK) &&
|
||||
opposes?(user)
|
||||
return true if move.move.priority > 0 && @ai.battle.field.terrain == :Psychic &&
|
||||
@battler.affectedByTerrain? && opposes?(user)
|
||||
# TODO: Dazzling/Queenly Majesty go here.
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
|
||||
private
|
||||
|
||||
def effectiveness_of_type_against_single_battler_type(type, defend_type, user = nil)
|
||||
ret = Effectiveness.calculate_one(type, defend_type)
|
||||
if Effectiveness.ineffective_type?(type, defend_type)
|
||||
# Ring Target
|
||||
if has_active_item?(:RINGTARGET)
|
||||
ret = Effectiveness::NORMAL_EFFECTIVE_ONE
|
||||
end
|
||||
# Foresight
|
||||
if (user&.has_active_ability?(:SCRAPPY) || @battler.effects[PBEffects::Foresight]) &&
|
||||
defend_type == :GHOST
|
||||
ret = Effectiveness::NORMAL_EFFECTIVE_ONE
|
||||
end
|
||||
# Miracle Eye
|
||||
if @battler.effects[PBEffects::MiracleEye] && defend_type == :DARK
|
||||
ret = Effectiveness::NORMAL_EFFECTIVE_ONE
|
||||
end
|
||||
elsif Effectiveness.super_effective_type?(type, defend_type)
|
||||
# Delta Stream's weather
|
||||
if @battler.effectiveWeather == :StrongWinds && defend_type == :FLYING
|
||||
ret = Effectiveness::NORMAL_EFFECTIVE_ONE
|
||||
end
|
||||
end
|
||||
# Grounded Flying-type Pokémon become susceptible to Ground moves
|
||||
if !@battler.airborne? && type == :GROUND && defend_type == :FLYING
|
||||
ret = Effectiveness::NORMAL_EFFECTIVE_ONE
|
||||
end
|
||||
return ret
|
||||
end
|
||||
end
|
||||
366
Data/Scripts/011_Battle/005_AI/103_AIMove.rb
Normal file
366
Data/Scripts/011_Battle/005_AI/103_AIMove.rb
Normal file
@@ -0,0 +1,366 @@
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
class Battle::AI::AIMove
|
||||
attr_reader :move
|
||||
|
||||
def initialize(ai)
|
||||
@ai = ai
|
||||
end
|
||||
|
||||
def set_up(move, ai_battler)
|
||||
@move = move
|
||||
@ai_battler = ai_battler
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
|
||||
# pp
|
||||
# priority
|
||||
# usableWhenAsleep?
|
||||
# thawsUser?
|
||||
# flinchingMove?
|
||||
# tramplesMinimize?
|
||||
# hitsFlyingTargets?
|
||||
# canMagicCoat?
|
||||
# soundMove?
|
||||
# bombMove?
|
||||
# powderMove?
|
||||
# ignoresSubstitute?
|
||||
# highCriticalRate?
|
||||
# ignoresReflect?
|
||||
|
||||
def id; return @move.id; end
|
||||
def physicalMove?(thisType = nil); return @move.physicalMove?(thisType); end
|
||||
def specialMove?(thisType = nil); return @move.specialMove?(thisType); end
|
||||
def damagingMove?; return @move.damagingMove?; end
|
||||
def statusMove?; return @move.statusMove?; end
|
||||
def function; return @move.function; end
|
||||
|
||||
#=============================================================================
|
||||
|
||||
# Returns whether this move targets multiple battlers.
|
||||
def targets_multiple_battlers?
|
||||
user_battler = @ai_battler.battler
|
||||
target_data = @move.pbTarget(user_battler)
|
||||
return false if target_data.num_targets <= 1
|
||||
num_targets = 0
|
||||
case target_data.id
|
||||
when :AllAllies
|
||||
@ai.battle.allSameSideBattlers(user_battler).each { |b| num_targets += 1 if b.index != user_battler.index }
|
||||
when :UserAndAllies
|
||||
@ai.battle.allSameSideBattlers(user_battler).each { |_b| num_targets += 1 }
|
||||
when :AllNearFoes
|
||||
@ai.battle.allOtherSideBattlers(user_battler).each { |b| num_targets += 1 if b.near?(user_battler) }
|
||||
when :AllFoes
|
||||
@ai.battle.allOtherSideBattlers(user_battler).each { |_b| num_targets += 1 }
|
||||
when :AllNearOthers
|
||||
@ai.battle.allBattlers.each { |b| num_targets += 1 if b.near?(user_battler) }
|
||||
when :AllBattlers
|
||||
@ai.battle.allBattlers.each { |_b| num_targets += 1 }
|
||||
end
|
||||
return num_targets > 1
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
|
||||
def type; return @move.type; end
|
||||
|
||||
def rough_type
|
||||
return @move.pbCalcType(@ai.user.battler) if @ai.trainer.high_skill?
|
||||
return @move.type
|
||||
end
|
||||
|
||||
def pbCalcType(user); return @move.pbCalcType(user); end
|
||||
|
||||
#=============================================================================
|
||||
|
||||
# Returns this move's base power, taking into account various effects that
|
||||
# modify it.
|
||||
def base_power
|
||||
ret = @move.baseDamage
|
||||
ret = 60 if ret == 1
|
||||
return ret if !@ai.trainer.medium_skill?
|
||||
user = @ai.user
|
||||
user_battler = user.battler
|
||||
target = @ai.target
|
||||
target_battler = target.battler
|
||||
# Covers all function codes which have their own def pbBaseDamage
|
||||
case @move.function
|
||||
when "FixedDamage20", "FixedDamage40", "FixedDamageHalfTargetHP",
|
||||
"FixedDamageUserLevel", "LowerTargetHPToUserHP"
|
||||
ret = @move.pbFixedDamage(user_battler, target_battler)
|
||||
when "FixedDamageUserLevelRandom"
|
||||
ret = user_battler.level
|
||||
when "OHKO", "OHKOIce", "OHKOHitsUndergroundTarget"
|
||||
ret = 200
|
||||
when "CounterPhysicalDamage", "CounterSpecialDamage", "CounterDamagePlusHalf"
|
||||
ret = 60
|
||||
when "DoublePowerIfTargetUnderwater", "DoublePowerIfTargetUnderground",
|
||||
"BindTargetDoublePowerIfTargetUnderwater"
|
||||
ret = @move.pbModifyDamage(ret, user_battler, target_battler)
|
||||
when "DoublePowerIfTargetInSky",
|
||||
"FlinchTargetDoublePowerIfTargetInSky",
|
||||
"DoublePowerIfTargetPoisoned",
|
||||
"DoublePowerIfTargetParalyzedCureTarget",
|
||||
"DoublePowerIfTargetAsleepCureTarget",
|
||||
"DoublePowerIfUserPoisonedBurnedParalyzed",
|
||||
"DoublePowerIfTargetStatusProblem",
|
||||
"DoublePowerIfTargetHPLessThanHalf",
|
||||
"DoublePowerIfAllyFaintedLastTurn",
|
||||
"TypeAndPowerDependOnWeather",
|
||||
"PowerHigherWithUserHappiness",
|
||||
"PowerLowerWithUserHappiness",
|
||||
"PowerHigherWithUserHP",
|
||||
"PowerHigherWithTargetHP",
|
||||
"PowerHigherWithUserPositiveStatStages",
|
||||
"PowerHigherWithTargetPositiveStatStages",
|
||||
"TypeDependsOnUserIVs",
|
||||
"PowerHigherWithConsecutiveUse",
|
||||
"PowerHigherWithConsecutiveUseOnUserSide",
|
||||
"PowerHigherWithLessPP",
|
||||
"PowerLowerWithUserHP",
|
||||
"PowerHigherWithUserFasterThanTarget",
|
||||
"PowerHigherWithTargetWeight",
|
||||
"ThrowUserItemAtTarget",
|
||||
"PowerDependsOnUserStockpile"
|
||||
ret = @move.pbBaseDamage(ret, user_battler, target_battler)
|
||||
when "DoublePowerIfUserHasNoItem"
|
||||
ret *= 2 if !user_battler.item || user.has_active_item?(:FLYINGGEM)
|
||||
when "PowerHigherWithTargetFasterThanUser"
|
||||
targetSpeed = target.rough_stat(:SPEED)
|
||||
userSpeed = user.rough_stat(:SPEED)
|
||||
ret = [[(25 * targetSpeed / userSpeed).floor, 150].min, 1].max
|
||||
when "RandomlyDamageOrHealTarget"
|
||||
ret = 50
|
||||
when "RandomPowerDoublePowerIfTargetUnderground"
|
||||
ret = 71
|
||||
ret *= 2 if target_battler.inTwoTurnAttack?("TwoTurnAttackInvulnerableUnderground") # Dig
|
||||
when "TypeAndPowerDependOnUserBerry"
|
||||
ret = @move.pbNaturalGiftBaseDamage(user_battler.item_id)
|
||||
when "PowerHigherWithUserHeavierThanTarget"
|
||||
ret = @move.pbBaseDamage(ret, user_battler, target_battler)
|
||||
ret *= 2 if Settings::MECHANICS_GENERATION >= 7 && @trainer.medium_skill? &&
|
||||
target_battler.effects[PBEffects::Minimize]
|
||||
when "AlwaysCriticalHit", "HitTwoTimes", "HitTwoTimesPoisonTarget"
|
||||
ret *= 2
|
||||
when "HitThreeTimesPowersUpWithEachHit"
|
||||
ret *= 6 # Hits do x1, x2, x3 ret in turn, for x6 in total
|
||||
when "HitTwoToFiveTimes"
|
||||
if user.has_active_ability?(:SKILLLINK)
|
||||
ret *= 5
|
||||
else
|
||||
ret = (ret * 31 / 10).floor # Average damage dealt
|
||||
end
|
||||
when "HitTwoToFiveTimesOrThreeForAshGreninja"
|
||||
if user_battler.isSpecies?(:GRENINJA) && user_battler.form == 2
|
||||
ret *= 4 # 3 hits at 20 power = 4 hits at 15 power
|
||||
elsif user.has_active_ability?(:SKILLLINK)
|
||||
ret *= 5
|
||||
else
|
||||
ret = (ret * 31 / 10).floor # Average damage dealt
|
||||
end
|
||||
when "HitOncePerUserTeamMember"
|
||||
mult = 0
|
||||
@ai.battle.eachInTeamFromBattlerIndex(user.index) do |pkmn, _i|
|
||||
mult += 1 if pkmn&.able? && pkmn.status == :NONE
|
||||
end
|
||||
ret *= mult
|
||||
when "TwoTurnAttackOneTurnInSun"
|
||||
ret = @move.pbBaseDamageMultiplier(ret, user_battler, target_battler)
|
||||
when "MultiTurnAttackPowersUpEachTurn"
|
||||
ret *= 2 if user_battler.effects[PBEffects::DefenseCurl]
|
||||
when "MultiTurnAttackBideThenReturnDoubleDamage"
|
||||
ret = 40
|
||||
when "UserFaintsFixedDamageUserHP"
|
||||
ret = user_battler.hp
|
||||
when "EffectivenessIncludesFlyingType"
|
||||
if GameData::Type.exists?(:FLYING)
|
||||
if @trainer.high_skill?
|
||||
targetTypes = target_battler.pbTypes(true)
|
||||
mult = Effectiveness.calculate(
|
||||
:FLYING, targetTypes[0], targetTypes[1], targetTypes[2]
|
||||
)
|
||||
else
|
||||
mult = Effectiveness.calculate(
|
||||
:FLYING, target.types[0], target.types[1], target.effects[PBEffects::Type3]
|
||||
)
|
||||
end
|
||||
ret = (ret.to_f * mult / Effectiveness::NORMAL_EFFECTIVE).round
|
||||
end
|
||||
ret *= 2 if @trainer.medium_skill? && target_battler.effects[PBEffects::Minimize]
|
||||
when "DoublePowerIfUserLastMoveFailed"
|
||||
ret *= 2 if user_battler.lastRoundMoveFailed
|
||||
when "HitTwoTimesFlinchTarget"
|
||||
ret *= 2
|
||||
ret *= 2 if @trainer.medium_skill? && target_battler.effects[PBEffects::Minimize]
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
|
||||
def accuracy
|
||||
return @move.accuracy
|
||||
end
|
||||
|
||||
def rough_accuracy
|
||||
baseAcc = self.accuracy
|
||||
return 100 if baseAcc == 0
|
||||
# Determine user and target
|
||||
user = @ai.user
|
||||
user_battler = user.battler
|
||||
target = @ai.target
|
||||
target_battler = target.battler
|
||||
# Get better base accuracy
|
||||
if @ai.trainer.medium_skill?
|
||||
baseAcc = @move.pbBaseAccuracy(user_battler, target_battler)
|
||||
return 100 if baseAcc == 0
|
||||
end
|
||||
# "Always hit" effects and "always hit" accuracy
|
||||
if @ai.trainer.medium_skill?
|
||||
return 100 if target_battler.effects[PBEffects::Minimize] && @move.tramplesMinimize? &&
|
||||
Settings::MECHANICS_GENERATION >= 6
|
||||
return 100 if target_battler.effects[PBEffects::Telekinesis] > 0
|
||||
end
|
||||
# Get the move's type
|
||||
type = rough_type
|
||||
# Calculate all modifier effects
|
||||
modifiers = {}
|
||||
modifiers[:base_accuracy] = baseAcc
|
||||
modifiers[:accuracy_stage] = user_battler.stages[:ACCURACY]
|
||||
modifiers[:evasion_stage] = target_battler.stages[:EVASION]
|
||||
modifiers[:accuracy_multiplier] = 1.0
|
||||
modifiers[:evasion_multiplier] = 1.0
|
||||
apply_rough_accuracy_modifiers(user, target, type, modifiers)
|
||||
# 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
|
||||
stageMul = [3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7, 8, 9]
|
||||
stageDiv = [9, 8, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3]
|
||||
accuracy = 100.0 * stageMul[accStage] / stageDiv[accStage]
|
||||
evasion = 100.0 * stageMul[evaStage] / stageDiv[evaStage]
|
||||
accuracy = (accuracy * modifiers[:accuracy_multiplier]).round
|
||||
evasion = (evasion * modifiers[:evasion_multiplier]).round
|
||||
evasion = 1 if evasion < 1
|
||||
return modifiers[:base_accuracy] * accuracy / evasion
|
||||
end
|
||||
|
||||
def apply_rough_accuracy_modifiers(user, target, type, modifiers)
|
||||
user_battler = user.battler
|
||||
target_battler = target.battler
|
||||
mold_breaker = (@ai.trainer.medium_skill? && target_battler.hasMoldBreaker?)
|
||||
# Ability effects that alter accuracy calculation
|
||||
if user.ability_active?
|
||||
Battle::AbilityEffects.triggerAccuracyCalcFromUser(
|
||||
user_battler.ability, modifiers, user_battler, target_battler, @move, type
|
||||
)
|
||||
end
|
||||
user_battler.allAllies.each do |b|
|
||||
next if !b.abilityActive?
|
||||
Battle::AbilityEffects.triggerAccuracyCalcFromAlly(
|
||||
b.ability, modifiers, user_battler, target_battler, @move, type
|
||||
)
|
||||
end
|
||||
if !mold_breaker && target.ability_active?
|
||||
Battle::AbilityEffects.triggerAccuracyCalcFromTarget(
|
||||
target_battler.ability, modifiers, user_battler, target_battler, @move, type
|
||||
)
|
||||
end
|
||||
# Item effects that alter accuracy calculation
|
||||
if user.item_active?
|
||||
# TODO: Zoom Lens needs to be checked differently (compare speeds of
|
||||
# user and target).
|
||||
Battle::ItemEffects.triggerAccuracyCalcFromUser(
|
||||
user_battler.item, modifiers, user_battler, target_battler, @move, type
|
||||
)
|
||||
end
|
||||
if target.item_active?
|
||||
Battle::ItemEffects.triggerAccuracyCalcFromTarget(
|
||||
target_battler.item, modifiers, user_battler, target_battler, @move, type
|
||||
)
|
||||
end
|
||||
# Other effects, inc. ones that set accuracy_multiplier or evasion_stage to specific values
|
||||
if @ai.battle.field.effects[PBEffects::Gravity] > 0
|
||||
modifiers[:accuracy_multiplier] *= 5 / 3.0
|
||||
end
|
||||
if @ai.trainer.medium_skill?
|
||||
if user_battler.effects[PBEffects::MicleBerry]
|
||||
modifiers[:accuracy_multiplier] *= 1.2
|
||||
end
|
||||
modifiers[:evasion_stage] = 0 if target_battler.effects[PBEffects::Foresight] && modifiers[:evasion_stage] > 0
|
||||
modifiers[:evasion_stage] = 0 if target_battler.effects[PBEffects::MiracleEye] && modifiers[:evasion_stage] > 0
|
||||
end
|
||||
# "AI-specific calculations below"
|
||||
modifiers[:evasion_stage] = 0 if @move.function == "IgnoreTargetDefSpDefEvaStatStages" # Chip Away
|
||||
if @ai.trainer.medium_skill?
|
||||
modifiers[:base_accuracy] = 0 if user_battler.effects[PBEffects::LockOn] > 0 &&
|
||||
user_battler.effects[PBEffects::LockOnPos] == target_battler.index
|
||||
end
|
||||
if @ai.trainer.medium_skill?
|
||||
case @move.function
|
||||
when "BadPoisonTarget"
|
||||
modifiers[:base_accuracy] = 0 if Settings::MORE_TYPE_EFFECTS &&
|
||||
@move.statusMove? && @user.has_type?(:POISON)
|
||||
when "OHKO", "OHKOIce", "OHKOHitsUndergroundTarget"
|
||||
modifiers[:base_accuracy] = self.accuracy + user_battler.level - target_battler.level
|
||||
modifiers[:accuracy_multiplier] = 0 if target_battler.level > user_battler.level
|
||||
modifiers[:accuracy_multiplier] = 0 if target.has_active_ability?(:STURDY)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
|
||||
def rough_critical_hit_stage
|
||||
user = @ai.user
|
||||
user_battler = user.battler
|
||||
target = @ai.target
|
||||
target_battler = target.battler
|
||||
return -1 if target_battler.pbOwnSide.effects[PBEffects::LuckyChant] > 0
|
||||
mold_breaker = (@ai.trainer.medium_skill? && user_battler.hasMoldBreaker?)
|
||||
crit_stage = 0
|
||||
# Ability effects that alter critical hit rate
|
||||
if user.ability_active?
|
||||
crit_stage = BattleHandlers.triggerCriticalCalcUserAbility(user_battler.ability,
|
||||
user_battler, target_battler, crit_stage)
|
||||
return -1 if crit_stage < 0
|
||||
end
|
||||
if !mold_breaker && target.ability_active?
|
||||
crit_stage = BattleHandlers.triggerCriticalCalcTargetAbility(target_battler.ability,
|
||||
user_battler, target_battler, crit_stage)
|
||||
return -1 if crit_stage < 0
|
||||
end
|
||||
# Item effects that alter critical hit rate
|
||||
if user.item_active?
|
||||
crit_stage = BattleHandlers.triggerCriticalCalcUserItem(user_battler.item,
|
||||
user_battler, target_battler, crit_stage)
|
||||
return -1 if crit_stage < 0
|
||||
end
|
||||
if target.item_active?
|
||||
crit_stage = BattleHandlers.triggerCriticalCalcTargetItem(user_battler.item,
|
||||
user_battler, target_battler, crit_stage)
|
||||
return -1 if crit_stage < 0
|
||||
end
|
||||
# Other effects
|
||||
case @move.pbCritialOverride(user_battler, target_battler)
|
||||
when 1 then return 99
|
||||
when -1 then return -1
|
||||
end
|
||||
return 99 if crit_stage > 50 # Merciless
|
||||
return 99 if user_battler.effects[PBEffects::LaserFocus] > 0
|
||||
crit_stage += 1 if @move.highCriticalRate?
|
||||
crit_stage += user_battler.effects[PBEffects::FocusEnergy]
|
||||
crit_stage += 1 if user_battler.inHyperMode? && @move.type == :SHADOW
|
||||
crit_stage = [crit_stage, Battle::Move::CRITICAL_HIT_RATIOS.length - 1].min
|
||||
return crit_stage
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
|
||||
# pbBaseAccuracy(@ai.user.battler, @ai.target.battler) if @ai.trainer.medium_skill?
|
||||
# pbCriticalOverride(@ai.user.battler, @ai.target.battler)
|
||||
end
|
||||
@@ -757,7 +757,7 @@ Description = The user stimulates its brain by thinking bad thoughts. It sharply
|
||||
Name = Obstruct
|
||||
Type = DARK
|
||||
Category = Status
|
||||
Accuracy = 100
|
||||
Accuracy = 0
|
||||
TotalPP = 10
|
||||
Target = User
|
||||
Priority = 4
|
||||
@@ -1102,7 +1102,7 @@ Description = This attack hits the target with a shock wave of pure rage. This a
|
||||
Name = Clangorous Soul
|
||||
Type = DRAGON
|
||||
Category = Status
|
||||
Accuracy = 100
|
||||
Accuracy = 0
|
||||
TotalPP = 5
|
||||
Target = User
|
||||
FunctionCode = RaiseUserMainStats1LoseThirdOfTotalHP
|
||||
@@ -5846,7 +5846,7 @@ Description = The user mimics the move used immediately before it. The move fail
|
||||
Name = Court Change
|
||||
Type = NORMAL
|
||||
Category = Status
|
||||
Accuracy = 100
|
||||
Accuracy = 0
|
||||
TotalPP = 10
|
||||
Target = BothSides
|
||||
FunctionCode = SwapSideEffects
|
||||
|
||||
@@ -757,7 +757,7 @@ Description = The user stimulates its brain by thinking bad thoughts. It sharply
|
||||
Name = Obstruct
|
||||
Type = DARK
|
||||
Category = Status
|
||||
Accuracy = 100
|
||||
Accuracy = 0
|
||||
TotalPP = 10
|
||||
Target = User
|
||||
Priority = 4
|
||||
@@ -1102,7 +1102,7 @@ Description = This attack hits the target with a shock wave of pure rage. This a
|
||||
Name = Clangorous Soul
|
||||
Type = DRAGON
|
||||
Category = Status
|
||||
Accuracy = 100
|
||||
Accuracy = 0
|
||||
TotalPP = 5
|
||||
Target = User
|
||||
FunctionCode = RaiseUserMainStats1LoseThirdOfTotalHP
|
||||
@@ -5846,7 +5846,7 @@ Description = The user mimics the move used immediately before it. The move fail
|
||||
Name = Court Change
|
||||
Type = NORMAL
|
||||
Category = Status
|
||||
Accuracy = 100
|
||||
Accuracy = 0
|
||||
TotalPP = 10
|
||||
Target = BothSides
|
||||
FunctionCode = SwapSideEffects
|
||||
|
||||
Reference in New Issue
Block a user