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:
Maruno17
2022-08-22 21:37:33 +01:00
parent b094a2fd8e
commit cfb870c944
24 changed files with 1856 additions and 1666 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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
#===============================================================================

View File

@@ -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 = {

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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
#=============================================================================

View File

@@ -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

View File

@@ -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
}
)

View File

@@ -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
}
)

View File

@@ -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)
}
)

View File

@@ -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

View File

@@ -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

View File

@@ -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?
}
)

View File

@@ -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

View File

@@ -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 ||

View 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

View 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

View 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

View File

@@ -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

View File

@@ -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