Added decent spacing to all scripts thanks to Rubocop

This commit is contained in:
Maruno17
2021-12-18 15:25:40 +00:00
parent f7b76ae62e
commit d17fc40a47
207 changed files with 18577 additions and 18587 deletions

View File

@@ -31,17 +31,17 @@ class Battle::AI
sum += c[1]
n += 1
end
return 0 if n<2
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
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)
return Math.sqrt(varianceTimesN / n)
end
#=============================================================================

View File

@@ -12,7 +12,7 @@ class Battle::AI
idxTarget = @battle.battlers[idxTarget].pokemonIndex # Party Pokémon
end
# Register use of item
@battle.pbRegisterItem(idxBattler,item,idxTarget)
@battle.pbRegisterItem(idxBattler, item, idxTarget)
PBDebug.log("[AI] #{user.pbThis} (#{user.index}) will use item #{GameData::Item.get(item).name}")
return true
end
@@ -22,7 +22,7 @@ class Battle::AI
def pbEnemyItemToUse(idxBattler)
return nil if !@battle.internalBattle
items = @battle.pbGetOwnerItems(idxBattler)
return nil if !items || items.length==0
return nil if !items || items.length == 0
# Determine target of item (always the Pokémon choosing the action)
idxTarget = idxBattler # Battler using the item
battler = @battle.battlers[idxTarget]
@@ -40,7 +40,7 @@ class Battle::AI
:LEMONADE => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 70 : 80,
:MOOMOOMILK => 100,
:ORANBERRY => 10,
:SITRUSBERRY => battler.totalhp/4,
:SITRUSBERRY => battler.totalhp / 4,
:ENERGYPOWDER => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 60 : 50,
:ENERGYROOT => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 120 : 200
}
@@ -103,9 +103,9 @@ class Battle::AI
usableXItems = []
items.each do |i|
next if !i
next if !@battle.pbCanUseItemOnPokemon?(i,pkmn,battler,@battle.scene,false)
next if !ItemHandlers.triggerCanUseInBattle(i,pkmn,battler,nil,
false,self,@battle.scene,false)
next if !@battle.pbCanUseItemOnPokemon?(i, pkmn, battler, @battle.scene, false)
next if !ItemHandlers.triggerCanUseInBattle(i, pkmn, battler, nil,
false, self, @battle.scene, false)
# Log HP healing items
if losthp > 0
power = hpItems[i]
@@ -140,28 +140,28 @@ class Battle::AI
end
end
# Prioritise using a HP restoration item
if usableHPItems.length>0 && (battler.hp<=battler.totalhp/4 ||
(battler.hp<=battler.totalhp/2 && pbAIRandom(100)<30))
usableHPItems.sort! { |a,b| (a[1]==b[1]) ? a[2]<=>b[2] : a[1]<=>b[1] }
if usableHPItems.length > 0 && (battler.hp <= battler.totalhp / 4 ||
(battler.hp <= battler.totalhp / 2 && pbAIRandom(100) < 30))
usableHPItems.sort! { |a, b| (a[1] == b[1]) ? a[2] <=> b[2] : a[1] <=> b[1] }
prevItem = nil
usableHPItems.each do |i|
return i[0], idxTarget if i[2]>=losthp
return i[0], idxTarget if i[2] >= losthp
prevItem = i
end
return prevItem[0], idxTarget
end
# Next prioritise using a status-curing item
if usableStatusItems.length>0 && pbAIRandom(100)<40
usableStatusItems.sort! { |a,b| a[1]<=>b[1] }
if usableStatusItems.length > 0 && pbAIRandom(100) < 40
usableStatusItems.sort! { |a, b| a[1] <=> b[1] }
return usableStatusItems[0][0], idxTarget
end
# Next try using an X item
if usableXItems.length>0 && pbAIRandom(100)<30
usableXItems.sort! { |a,b| (a[1]==b[1]) ? a[2]<=>b[2] : a[1]<=>b[1] }
if usableXItems.length > 0 && pbAIRandom(100) < 30
usableXItems.sort! { |a, b| (a[1] == b[1]) ? a[2] <=> b[2] : a[1] <=> b[1] }
prevItem = nil
usableXItems.each do |i|
break if prevItem && i[1]>prevItem[1]
return i[0], idxTarget if i[1]+i[2]>=6
break if prevItem && i[1] > prevItem[1]
return i[0], idxTarget if i[1] + i[2] >= 6
prevItem = i
end
return prevItem[0], idxTarget

View File

@@ -3,10 +3,10 @@ class Battle::AI
# Decide whether the opponent should switch Pokémon
#=============================================================================
def pbEnemyShouldWithdraw?(idxBattler)
return pbEnemyShouldWithdrawEx?(idxBattler,false)
return pbEnemyShouldWithdrawEx?(idxBattler, false)
end
def pbEnemyShouldWithdrawEx?(idxBattler,forceSwitch)
def pbEnemyShouldWithdrawEx?(idxBattler, forceSwitch)
return false if @battle.wildBattle?
shouldSwitch = forceSwitch
batonPass = -1
@@ -15,13 +15,13 @@ class Battle::AI
battler = @battle.battlers[idxBattler]
# If Pokémon is within 6 levels of the foe, and foe's last move was
# super-effective and powerful
if !shouldSwitch && battler.turnCount>0 && skill>=PBTrainerAI.highSkill
if !shouldSwitch && battler.turnCount > 0 && skill >= PBTrainerAI.highSkill
target = battler.pbDirectOpposing(true)
if !target.fainted? && target.lastMoveUsed &&
(target.level-battler.level).abs<=6
(target.level - battler.level).abs <= 6
moveData = GameData::Move.get(target.lastMoveUsed)
moveType = moveData.type
typeMod = pbCalcTypeMod(moveType,target,battler)
typeMod = pbCalcTypeMod(moveType, target, battler)
if Effectiveness.super_effective?(typeMod) && moveData.base_damage > 50
switchChance = (moveData.base_damage > 70) ? 30 : 20
shouldSwitch = (pbAIRandom(100) < switchChance)
@@ -30,14 +30,14 @@ class Battle::AI
end
# Pokémon can't do anything (must have been in battle for at least 5 rounds)
if !@battle.pbCanChooseAnyMove?(idxBattler) &&
battler.turnCount && battler.turnCount>=5
battler.turnCount && battler.turnCount >= 5
shouldSwitch = true
end
# Pokémon is Perish Songed and has Baton Pass
if skill>=PBTrainerAI.highSkill && battler.effects[PBEffects::PerishSong]==1
battler.eachMoveWithIndex do |m,i|
next if m.function!="SwitchOutUserPassOnEffects" # Baton Pass
next if !@battle.pbCanChooseMove?(idxBattler,i,false)
if skill >= PBTrainerAI.highSkill && battler.effects[PBEffects::PerishSong] == 1
battler.eachMoveWithIndex do |m, i|
next if m.function != "SwitchOutUserPassOnEffects" # Baton Pass
next if !@battle.pbCanChooseMove?(idxBattler, i, false)
batonPass = i
break
end
@@ -45,96 +45,96 @@ class Battle::AI
# Pokémon will faint because of bad poisoning at the end of this round, but
# would survive at least one more round if it were regular poisoning instead
if battler.status == :POISON && battler.statusCount > 0 &&
skill>=PBTrainerAI.highSkill
toxicHP = battler.totalhp/16
nextToxicHP = toxicHP*(battler.effects[PBEffects::Toxic]+1)
if battler.hp<=nextToxicHP && battler.hp>toxicHP*2
shouldSwitch = true if pbAIRandom(100)<80
skill >= PBTrainerAI.highSkill
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 battler.effects[PBEffects::Encore]>0 && skill>=PBTrainerAI.mediumSkill
if battler.effects[PBEffects::Encore] > 0 && skill >= PBTrainerAI.mediumSkill
idxEncoredMove = battler.pbEncoredMoveIndex
if idxEncoredMove>=0
if idxEncoredMove >= 0
scoreSum = 0
scoreCount = 0
battler.allOpposing.each do |b|
scoreSum += pbGetMoveScore(battler.moves[idxEncoredMove],battler,b,skill)
scoreSum += pbGetMoveScore(battler.moves[idxEncoredMove], battler, b, skill)
scoreCount += 1
end
if scoreCount>0 && scoreSum/scoreCount<=20
shouldSwitch = true if pbAIRandom(100)<80
if scoreCount > 0 && scoreSum / scoreCount <= 20
shouldSwitch = true if pbAIRandom(100) < 80
end
end
end
# If there is a single foe and it is resting after Hyper Beam or is
# Truanting (i.e. free turn)
if @battle.pbSideSize(battler.index+1)==1 &&
!battler.pbDirectOpposing.fainted? && skill>=PBTrainerAI.highSkill
if @battle.pbSideSize(battler.index + 1) == 1 &&
!battler.pbDirectOpposing.fainted? && skill >= PBTrainerAI.highSkill
opp = battler.pbDirectOpposing
if opp.effects[PBEffects::HyperBeam]>0 ||
if opp.effects[PBEffects::HyperBeam] > 0 ||
(opp.hasActiveAbility?(:TRUANT) && opp.effects[PBEffects::Truant])
shouldSwitch = false if pbAIRandom(100)<80
shouldSwitch = false if pbAIRandom(100) < 80
end
end
# Sudden Death rule - I'm not sure what this means
if @battle.rules["suddendeath"] && battler.turnCount>0
if battler.hp<=battler.totalhp/4 && pbAIRandom(100)<30
if @battle.rules["suddendeath"] && battler.turnCount > 0
if battler.hp <= battler.totalhp / 4 && pbAIRandom(100) < 30
shouldSwitch = true
elsif battler.hp<=battler.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 battler.effects[PBEffects::PerishSong]==1
if battler.effects[PBEffects::PerishSong] == 1
shouldSwitch = true
end
if shouldSwitch
list = []
idxPartyStart, idxPartyEnd = @battle.pbTeamIndexRangeFromBattlerIndex(idxBattler)
@battle.pbParty(idxBattler).each_with_index do |pkmn,i|
@battle.pbParty(idxBattler).each_with_index do |pkmn, i|
next if i == idxPartyEnd - 1 # Don't choose to switch in ace
next if !@battle.pbCanSwitch?(idxBattler,i)
next if !@battle.pbCanSwitch?(idxBattler, i)
# If perish count is 1, it may be worth it to switch
# even with Spikes, since Perish Song's effect will end
if battler.effects[PBEffects::PerishSong]!=1
if battler.effects[PBEffects::PerishSong] != 1
# Will contain effects that recommend against switching
spikes = battler.pbOwnSide.effects[PBEffects::Spikes]
# Don't switch to this if too little HP
if spikes>0
spikesDmg = [8,6,4][spikes-1]
if pkmn.hp<=pkmn.totalhp/spikesDmg
if spikes > 0
spikesDmg = [8, 6, 4][spikes - 1]
if pkmn.hp <= pkmn.totalhp / spikesDmg
next if !pkmn.hasType?(:FLYING) && !pkmn.hasActiveAbility?(:LEVITATE)
end
end
end
# moveType is the type of the target's last used move
if moveType && Effectiveness.ineffective?(pbCalcTypeMod(moveType,battler,battler))
if moveType && Effectiveness.ineffective?(pbCalcTypeMod(moveType, battler, battler))
weight = 65
typeMod = pbCalcTypeModPokemon(pkmn,battler.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,battler,battler))
list.unshift(i) if pbAIRandom(100) < weight # Put this Pokemon first
elsif moveType && Effectiveness.resistant?(pbCalcTypeMod(moveType, battler, battler))
weight = 40
typeMod = pbCalcTypeModPokemon(pkmn,battler.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
end
list.unshift(i) if pbAIRandom(100)<weight # Put this Pokemon first
list.unshift(i) if pbAIRandom(100) < weight # Put this Pokemon first
else
list.push(i) # put this Pokemon last
end
end
if list.length>0
if batonPass>=0 && @battle.pbRegisterMove(idxBattler,batonPass,false)
if list.length > 0
if batonPass >= 0 && @battle.pbRegisterMove(idxBattler, batonPass, false)
PBDebug.log("[AI] #{battler.pbThis} (#{idxBattler}) will use Baton Pass to avoid Perish Song")
return true
end
if @battle.pbRegisterSwitch(idxBattler,list[0])
if @battle.pbRegisterSwitch(idxBattler, list[0])
PBDebug.log("[AI] #{battler.pbThis} (#{idxBattler}) will switch with " +
"#{@battle.pbParty(idxBattler)[list[0]].name}")
return true
@@ -147,19 +147,19 @@ class Battle::AI
#=============================================================================
# Choose a replacement Pokémon
#=============================================================================
def pbDefaultChooseNewEnemy(idxBattler,party)
def pbDefaultChooseNewEnemy(idxBattler, party)
enemies = []
idxPartyStart, idxPartyEnd = @battle.pbTeamIndexRangeFromBattlerIndex(idxBattler)
party.each_with_index do |_p,i|
party.each_with_index do |_p, i|
next if i == idxPartyEnd - 1 && enemies.length > 0 # Ignore ace if possible
enemies.push(i) if @battle.pbCanSwitchLax?(idxBattler,i)
enemies.push(i) if @battle.pbCanSwitchLax?(idxBattler, i)
end
return -1 if enemies.length==0
return pbChooseBestNewEnemy(idxBattler,party,enemies)
return -1 if enemies.length == 0
return pbChooseBestNewEnemy(idxBattler, party, enemies)
end
def pbChooseBestNewEnemy(idxBattler,party,enemies)
return -1 if !enemies || enemies.length==0
def pbChooseBestNewEnemy(idxBattler, party, enemies)
return -1 if !enemies || enemies.length == 0
best = -1
bestSum = 0
enemies.each do |i|
@@ -172,7 +172,7 @@ class Battle::AI
sum += Effectiveness.calculate(m.type, bTypes[0], bTypes[1], bTypes[2])
end
end
if best==-1 || sum>bestSum
if best == -1 || sum > bestSum
best = i
bestSum = sum
end

View File

@@ -14,12 +14,12 @@ class Battle::AI
# NOTE: A move is only added to the choices array if it has a non-zero
# score.
choices = []
user.eachMoveWithIndex do |_m,i|
next if !@battle.pbCanChooseMove?(idxBattler,i,false)
user.eachMoveWithIndex do |_m, i|
next if !@battle.pbCanChooseMove?(idxBattler, i, false)
if wildBattler
pbRegisterMoveWild(user,i,choices)
pbRegisterMoveWild(user, i, choices)
else
pbRegisterMoveTrainer(user,i,choices,skill)
pbRegisterMoveTrainer(user, i, choices, skill)
end
end
# Figure out useful information about the choices
@@ -27,54 +27,54 @@ class Battle::AI
maxScore = 0
choices.each do |c|
totalScore += c[1]
maxScore = c[1] if maxScore<c[1]
maxScore = c[1] if maxScore < c[1]
end
# Log the available choices
if $INTERNAL
logMsg = "[AI] Move choices for #{user.pbThis(true)} (#{user.index}): "
choices.each_with_index do |c,i|
choices.each_with_index do |c, i|
logMsg += "#{user.moves[c[0]].name}=#{c[1]}"
logMsg += " (target #{c[2]})" if c[2]>=0
logMsg += ", " if i<choices.length-1
logMsg += " (target #{c[2]})" if c[2] >= 0
logMsg += ", " if i < choices.length - 1
end
PBDebug.log(logMsg)
end
# Find any preferred moves and just choose from them
if !wildBattler && skill>=PBTrainerAI.highSkill && maxScore>100
if !wildBattler && skill >= PBTrainerAI.highSkill && maxScore > 100
stDev = pbStdDev(choices)
if stDev>=40 && pbAIRandom(100)<90
if stDev >= 40 && pbAIRandom(100) < 90
preferredMoves = []
choices.each do |c|
next if c[1]<200 && c[1]<maxScore*0.8
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
preferredMoves.push(c) if c[1] == maxScore # Doubly prefer the best move
end
if preferredMoves.length>0
if preferredMoves.length > 0
m = preferredMoves[pbAIRandom(preferredMoves.length)]
PBDebug.log("[AI] #{user.pbThis} (#{user.index}) prefers #{user.moves[m[0]].name}")
@battle.pbRegisterMove(idxBattler,m[0],false)
@battle.pbRegisterTarget(idxBattler,m[2]) if m[2]>=0
@battle.pbRegisterMove(idxBattler, m[0], false)
@battle.pbRegisterTarget(idxBattler, m[2]) if m[2] >= 0
return
end
end
end
# Decide whether all choices are bad, and if so, try switching instead
if !wildBattler && skill>=PBTrainerAI.highSkill
if !wildBattler && skill >= PBTrainerAI.highSkill
badMoves = false
if (maxScore<=20 && user.turnCount>2) ||
(maxScore<=40 && user.turnCount>5)
badMoves = true if pbAIRandom(100)<80
if (maxScore <= 20 && user.turnCount > 2) ||
(maxScore <= 40 && user.turnCount > 5)
badMoves = true if pbAIRandom(100) < 80
end
if !badMoves && totalScore<100 && user.turnCount>1
if !badMoves && totalScore < 100 && user.turnCount > 1
badMoves = true
choices.each do |c|
next if !user.moves[c[0]].damagingMove?
badMoves = false
break
end
badMoves = false if badMoves && pbAIRandom(100)<10
badMoves = false if badMoves && pbAIRandom(100) < 10
end
if badMoves && pbEnemyShouldWithdrawEx?(idxBattler,true)
if badMoves && pbEnemyShouldWithdrawEx?(idxBattler, true)
if $INTERNAL
PBDebug.log("[AI] #{user.pbThis} (#{user.index}) will switch due to terrible moves")
end
@@ -82,13 +82,13 @@ class Battle::AI
end
end
# If there are no calculated choices, pick one at random
if choices.length==0
if choices.length == 0
PBDebug.log("[AI] #{user.pbThis} (#{user.index}) doesn't want to use any moves; picking one at random")
user.eachMoveWithIndex do |_m,i|
next if !@battle.pbCanChooseMove?(idxBattler,i,false)
choices.push([i,100,-1]) # Move index, score, target
user.eachMoveWithIndex do |_m, i|
next if !@battle.pbCanChooseMove?(idxBattler, 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
if choices.length == 0 # No moves are physically possible to use; use Struggle
@battle.pbAutoChooseMove(user.index)
end
end
@@ -96,9 +96,9 @@ class Battle::AI
randNum = pbAIRandom(totalScore)
choices.each do |c|
randNum -= c[1]
next if randNum>=0
@battle.pbRegisterMove(idxBattler,c[0],false)
@battle.pbRegisterTarget(idxBattler,c[2]) if c[2]>=0
next if randNum >= 0
@battle.pbRegisterMove(idxBattler, c[0], false)
@battle.pbRegisterTarget(idxBattler, c[2]) if c[2] >= 0
break
end
# Log the result
@@ -111,40 +111,40 @@ class Battle::AI
# Get scores for the given move against each possible target
#=============================================================================
# Wild Pokémon choose their moves randomly.
def pbRegisterMoveWild(_user,idxMove,choices)
choices.push([idxMove,100,-1]) # Move index, score, target
def pbRegisterMoveWild(_user, idxMove, choices)
choices.push([idxMove, 100, -1]) # Move index, score, target
end
# Trainer Pokémon calculate how much they want to use each of their moves.
def pbRegisterMoveTrainer(user,idxMove,choices,skill)
def pbRegisterMoveTrainer(user, idxMove, choices, skill)
move = user.moves[idxMove]
target_data = move.pbTarget(user)
if target_data.num_targets > 1
# If move affects multiple battlers and you don't choose a particular one
totalScore = 0
@battle.allBattlers.each do |b|
next if !@battle.pbMoveCanTarget?(user.index,b.index,target_data)
score = pbGetMoveScore(move,user,b,skill)
next if !@battle.pbMoveCanTarget?(user.index, b.index, target_data)
score = pbGetMoveScore(move, user, b, skill)
totalScore += ((user.opposes?(b)) ? score : -score)
end
choices.push([idxMove,totalScore,-1]) if totalScore>0
choices.push([idxMove, totalScore, -1]) if totalScore > 0
elsif target_data.num_targets == 0
# If move has no targets, affects the user, a side or the whole field
score = pbGetMoveScore(move,user,user,skill)
choices.push([idxMove,score,-1]) if score>0
score = pbGetMoveScore(move, user, user, skill)
choices.push([idxMove, score, -1]) if score > 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 !@battle.pbMoveCanTarget?(user.index, b.index, target_data)
next if target_data.targets_foe && !user.opposes?(b)
score = pbGetMoveScore(move,user,b,skill)
scoresAndTargets.push([score,b.index]) if score>0
score = pbGetMoveScore(move, user, b, skill)
scoresAndTargets.push([score, b.index]) if score > 0
end
if scoresAndTargets.length>0
if scoresAndTargets.length > 0
# Get the one best target for the move
scoresAndTargets.sort! { |a,b| b[0]<=>a[0] }
choices.push([idxMove,scoresAndTargets[0][0],scoresAndTargets[0][1]])
scoresAndTargets.sort! { |a, b| b[0] <=> a[0] }
choices.push([idxMove, scoresAndTargets[0][0], scoresAndTargets[0][1]])
end
end
end
@@ -152,31 +152,31 @@ class Battle::AI
#=============================================================================
# Get a score for the given move being used against the given target
#=============================================================================
def pbGetMoveScore(move,user,target,skill = 100)
skill = PBTrainerAI.minimumSkill if skill<PBTrainerAI.minimumSkill
def pbGetMoveScore(move, user, target, skill = 100)
skill = PBTrainerAI.minimumSkill if skill < PBTrainerAI.minimumSkill
score = 100
score = pbGetMoveScoreFunctionCode(score,move,user,target,skill)
score = pbGetMoveScoreFunctionCode(score, move, user, target, skill)
# A score of 0 here means it absolutely should not be used
return 0 if score<=0
if skill>=PBTrainerAI.mediumSkill
return 0 if score <= 0
if skill >= PBTrainerAI.mediumSkill
# Prefer damaging moves if AI has no more Pokémon or AI is less clever
if @battle.pbAbleNonActiveCount(user.idxOwnSide)==0
if !(skill>=PBTrainerAI.highSkill && @battle.pbAbleNonActiveCount(target.idxOwnSide)>0)
if @battle.pbAbleNonActiveCount(user.idxOwnSide) == 0
if !(skill >= PBTrainerAI.highSkill && @battle.pbAbleNonActiveCount(target.idxOwnSide) > 0)
if move.statusMove?
score /= 1.5
elsif target.hp<=target.totalhp/2
elsif target.hp <= target.totalhp / 2
score *= 1.5
end
end
end
# Don't prefer attacking the target if they'd be semi-invulnerable
if skill>=PBTrainerAI.highSkill && move.accuracy>0 &&
(target.semiInvulnerable? || target.effects[PBEffects::SkyDrop]>=0)
if skill >= PBTrainerAI.highSkill && move.accuracy > 0 &&
(target.semiInvulnerable? || target.effects[PBEffects::SkyDrop] >= 0)
miss = true
miss = false if user.hasActiveAbility?(:NOGUARD) || target.hasActiveAbility?(:NOGUARD)
if miss && pbRoughStat(user,:SPEED,skill)>pbRoughStat(target,:SPEED,skill)
if miss && pbRoughStat(user, :SPEED, skill) > pbRoughStat(target, :SPEED, skill)
# Knows what can get past semi-invulnerability
if target.effects[PBEffects::SkyDrop]>=0
if target.effects[PBEffects::SkyDrop] >= 0
miss = false if move.hitsFlyingTargets?
else
if target.inTwoTurnAttack?("TwoTurnAttackInvulnerableInSky",
@@ -193,13 +193,13 @@ class Battle::AI
score -= 80 if miss
end
# Pick a good move for the Choice items
if user.hasActiveItem?([:CHOICEBAND,:CHOICESPECS,:CHOICESCARF]) ||
if user.hasActiveItem?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF]) ||
user.hasActiveAbility?(:GORILLATACTICS)
if move.baseDamage>=60
if move.baseDamage >= 60
score += 60
elsif move.damagingMove?
score += 30
elsif move.function=="UserTargetSwapItems"
elsif move.function == "UserTargetSwapItems"
score += 70 # Trick
else
score -= 60
@@ -236,17 +236,17 @@ class Battle::AI
end
# 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, skill)
else # Status moves
# Don't prefer attacks which don't deal damage
score -= 10
# Account for accuracy of move
accuracy = pbRoughAccuracy(move,user,target,skill)
score *= accuracy/100.0
score = 0 if score<=10 && skill>=PBTrainerAI.highSkill
accuracy = pbRoughAccuracy(move, user, target, skill)
score *= accuracy / 100.0
score = 0 if score <= 10 && skill >= PBTrainerAI.highSkill
end
score = score.to_i
score = 0 if score<0
score = 0 if score < 0
return score
end
@@ -254,25 +254,25 @@ class Battle::AI
# Add to a move's score based on how much damage it will deal (as a percentage
# of the target's current HP)
#=============================================================================
def pbGetMoveScoreDamage(score,move,user,target,skill)
def pbGetMoveScoreDamage(score, move, user, target, skill)
# Don't prefer moves that are ineffective because of abilities or effects
return 0 if score<=0 || pbCheckMoveImmunity(score,move,user,target,skill)
return 0 if score <= 0 || pbCheckMoveImmunity(score, move, user, target, skill)
# Calculate how much damage the move will do (roughly)
baseDmg = pbMoveBaseDamage(move,user,target,skill)
realDamage = pbRoughDamage(move,user,target,skill,baseDmg)
baseDmg = pbMoveBaseDamage(move, user, target, skill)
realDamage = pbRoughDamage(move, user, target, skill, baseDmg)
# Account for accuracy of move
accuracy = pbRoughAccuracy(move,user,target,skill)
realDamage *= accuracy/100.0
accuracy = pbRoughAccuracy(move, user, target, skill)
realDamage *= accuracy / 100.0
# Two-turn attacks waste 2 turns to deal one lot of damage
if move.chargingTurnMove? || move.function=="AttackAndSkipNextTurn" # Hyper Beam
realDamage *= 2/3 # Not halved because semi-invulnerable during use or hits first turn
if move.chargingTurnMove? || move.function == "AttackAndSkipNextTurn" # Hyper Beam
realDamage *= 2 / 3 # Not halved because semi-invulnerable during use or hits first turn
end
# Prefer flinching external effects (note that move effects which cause
# flinching are dealt with in the function code part of score calculation)
if skill>=PBTrainerAI.mediumSkill && !move.flinchingMove?
if skill >= PBTrainerAI.mediumSkill && !move.flinchingMove?
if !target.hasActiveAbility?(:INNERFOCUS) &&
!target.hasActiveAbility?(:SHIELDDUST) &&
target.effects[PBEffects::Substitute]==0
target.effects[PBEffects::Substitute] == 0
canFlinch = false
if user.hasActiveItem?([:KINGSROCK, :RAZORFANG])
canFlinch = true
@@ -283,14 +283,14 @@ class Battle::AI
end
end
# Convert damage to percentage of target's remaining HP
damagePercentage = realDamage*100.0/target.hp
damagePercentage = realDamage * 100.0 / target.hp
# Don't prefer weak attacks
# damagePercentage /= 2 if damagePercentage<20
# Prefer damaging attack if level difference is significantly high
damagePercentage *= 1.2 if user.level-10>target.level
damagePercentage *= 1.2 if user.level - 10 > target.level
# Adjust score
damagePercentage = 120 if damagePercentage>120 # Treat all lethal moves the same
damagePercentage += 40 if damagePercentage>100 # Prefer moves likely to be lethal
damagePercentage = 120 if damagePercentage > 120 # Treat all lethal moves the same
damagePercentage += 40 if damagePercentage > 100 # Prefer moves likely to be lethal
score += damagePercentage.to_i
return score
end

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@ class Battle::AI
#=============================================================================
#
#=============================================================================
def pbTargetsMultiple?(move,user)
def pbTargetsMultiple?(move, user)
target_data = move.pbTarget(user)
return false if target_data.num_targets <= 1
num_targets = 0
@@ -26,8 +26,8 @@ class Battle::AI
#=============================================================================
# Move's type effectiveness
#=============================================================================
def pbCalcTypeModSingle(moveType,defType,user,target)
ret = Effectiveness.calculate_one(moveType,defType)
def pbCalcTypeModSingle(moveType, defType, user, target)
ret = Effectiveness.calculate_one(moveType, defType)
# Ring Target
if target.hasActiveItem?(:RINGTARGET)
ret = Effectiveness::NORMAL_EFFECTIVE_ONE if Effectiveness.ineffective_type?(moveType, defType)
@@ -54,7 +54,7 @@ class Battle::AI
return ret
end
def pbCalcTypeMod(moveType,user,target)
def pbCalcTypeMod(moveType, user, target)
return Effectiveness::NORMAL_EFFECTIVE if !moveType
return Effectiveness::NORMAL_EFFECTIVE if moveType == :GROUND &&
target.pbHasType?(:FLYING) &&
@@ -70,8 +70,8 @@ class Battle::AI
typeMods[0] = Effectiveness::SUPER_EFFECTIVE_ONE
end
else
tTypes.each_with_index do |type,i|
typeMods[i] = pbCalcTypeModSingle(moveType,type,user,target)
tTypes.each_with_index do |type, i|
typeMods[i] = pbCalcTypeModSingle(moveType, type, user, target)
end
end
# Multiply all effectivenesses together
@@ -82,41 +82,41 @@ class Battle::AI
# For switching. Determines the effectiveness of a potential switch-in against
# an opposing battler.
def pbCalcTypeModPokemon(battlerThis,_battlerOther)
def pbCalcTypeModPokemon(battlerThis, _battlerOther)
mod1 = Effectiveness.calculate(battlerThis.types[0], target.types[0], target.types[1])
mod2 = Effectiveness::NORMAL_EFFECTIVE
if battlerThis.types.length > 1
mod2 = Effectiveness.calculate(battlerThis.types[1], target.types[0], target.types[1])
mod2 = mod2.to_f / Effectivenesss::NORMAL_EFFECTIVE
end
return mod1*mod2
return mod1 * mod2
end
#=============================================================================
# Immunity to a move because of the target's ability, item or other effects
#=============================================================================
def pbCheckMoveImmunity(score,move,user,target,skill)
type = pbRoughType(move,user,skill)
typeMod = pbCalcTypeMod(type,user,target)
def pbCheckMoveImmunity(score, move, user, target, skill)
type = pbRoughType(move, user, skill)
typeMod = pbCalcTypeMod(type, user, target)
# Type effectiveness
return true if Effectiveness.ineffective?(typeMod) || score<=0
return true if Effectiveness.ineffective?(typeMod) || score <= 0
# Immunity due to ability/item/other effects
if skill>=PBTrainerAI.mediumSkill
if skill >= PBTrainerAI.mediumSkill
case type
when :GROUND
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])
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])
return true if target.hasActiveAbility?([:LIGHTNINGROD, :MOTORDRIVE, :VOLTABSORB])
end
return true if Effectiveness.not_very_effective?(typeMod) &&
target.hasActiveAbility?(:WONDERGUARD)
return true if move.damagingMove? && user.index!=target.index && !target.opposes?(user) &&
return true if move.damagingMove? && user.index != target.index && !target.opposes?(user) &&
target.hasActiveAbility?(:TELEPATHY)
return true if move.statusMove? && move.canMagicCoat? && target.hasActiveAbility?(:MAGICBOUNCE) &&
target.opposes?(user)
@@ -127,11 +127,11 @@ class Battle::AI
return true if target.hasActiveAbility?(:OVERCOAT)
return true if target.hasActiveItem?(:SAFETYGOGGLES)
end
return true if target.effects[PBEffects::Substitute]>0 && move.statusMove? &&
!move.ignoresSubstitute?(user) && user.index!=target.index
return true if target.effects[PBEffects::Substitute] > 0 && move.statusMove? &&
!move.ignoresSubstitute?(user) && user.index != target.index
return true if Settings::MECHANICS_GENERATION >= 7 && user.hasActiveAbility?(:PRANKSTER) &&
target.pbHasType?(:DARK) && target.opposes?(user)
return true if move.priority>0 && @battle.field.terrain == :Psychic &&
return true if move.priority > 0 && @battle.field.terrain == :Psychic &&
target.affectedByTerrain? && target.opposes?(user)
end
return false
@@ -140,19 +140,19 @@ class Battle::AI
#=============================================================================
# Get approximate properties for a battler
#=============================================================================
def pbRoughType(move,user,skill)
def pbRoughType(move, user, skill)
ret = move.type
if skill>=PBTrainerAI.highSkill
if skill >= PBTrainerAI.highSkill
ret = move.pbCalcType(user)
end
return ret
end
def pbRoughStat(battler,stat,skill)
return battler.pbSpeed if skill>=PBTrainerAI.highSkill && 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
def pbRoughStat(battler, stat, skill)
return battler.pbSpeed if skill >= PBTrainerAI.highSkill && 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
@@ -161,24 +161,24 @@ class Battle::AI
when :SPECIAL_DEFENSE then value = battler.spdef
when :SPEED then value = battler.speed
end
return (value.to_f*stageMul[stage]/stageDiv[stage]).floor
return (value.to_f * stageMul[stage] / stageDiv[stage]).floor
end
#=============================================================================
# Get a better move's base damage value
#=============================================================================
def pbMoveBaseDamage(move,user,target,skill)
def pbMoveBaseDamage(move, user, target, skill)
baseDmg = move.baseDamage
baseDmg = 60 if baseDmg==1
return baseDmg if skill<PBTrainerAI.mediumSkill
baseDmg = 60 if baseDmg == 1
return baseDmg if skill < PBTrainerAI.mediumSkill
# Covers all function codes which have their own def pbBaseDamage
case move.function
when "FlinchTargetTrampleMinimize" # Stomp
baseDmg *= 2 if skill>=PBTrainerAI.mediumSkill && target.effects[PBEffects::Minimize]
baseDmg *= 2 if skill >= PBTrainerAI.mediumSkill && target.effects[PBEffects::Minimize]
# Sonic Boom, Dragon Rage, Super Fang, Night Shade, Endeavor
when "FixedDamage20", "FixedDamage40", "FixedDamageHalfTargetHP",
"FixedDamageUserLevel", "LowerTargetHPToUserHP"
baseDmg = move.pbFixedDamage(user,target)
baseDmg = move.pbFixedDamage(user, target)
when "FixedDamageUserLevelRandom" # Psywave
baseDmg = user.level
when "OHKO", "OHKOIce", "OHKOHitsUndergroundTarget"
@@ -187,7 +187,7 @@ class Battle::AI
baseDmg = 60
when "DoublePowerIfTargetUnderwater", "DoublePowerIfTargetUnderground",
"BindTargetDoublePowerIfTargetUnderwater"
baseDmg = move.pbModifyDamage(baseDmg,user,target)
baseDmg = move.pbModifyDamage(baseDmg, user, target)
# Gust, Twister, Venoshock, Smelling Salts, Wake-Up Slap, Facade, Hex, Brine,
# Retaliate, Weather Ball, Return, Frustration, Eruption, Crush Grip,
# Stored Power, Punishment, Hidden Power, Fury Cutter, Echoed Voice,
@@ -217,13 +217,13 @@ class Battle::AI
"PowerHigherWithTargetWeight",
"ThrowUserItemAtTarget",
"PowerDependsOnUserStockpile"
baseDmg = move.pbBaseDamage(baseDmg,user,target)
baseDmg = move.pbBaseDamage(baseDmg, user, target)
when "DoublePowerIfUserHasNoItem" # Acrobatics
baseDmg *= 2 if !user.item || user.hasActiveItem?(:FLYINGGEM)
when "PowerHigherWithTargetFasterThanUser" # Gyro Ball
targetSpeed = pbRoughStat(target,:SPEED,skill)
userSpeed = pbRoughStat(user,:SPEED,skill)
baseDmg = [[(25*targetSpeed/userSpeed).floor,150].min,1].max
targetSpeed = pbRoughStat(target, :SPEED, skill)
userSpeed = pbRoughStat(user, :SPEED, skill)
baseDmg = [[(25 * targetSpeed / userSpeed).floor, 150].min, 1].max
when "RandomlyDamageOrHealTarget" # Present
baseDmg = 50
when "RandomPowerDoublePowerIfTargetUnderground" # Magnitude
@@ -232,8 +232,8 @@ class Battle::AI
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>=PBTrainerAI.mediumSkill &&
baseDmg = move.pbBaseDamage(baseDmg, user, target)
baseDmg *= 2 if Settings::MECHANICS_GENERATION >= 7 && skill >= PBTrainerAI.mediumSkill &&
target.effects[PBEffects::Minimize]
when "AlwaysCriticalHit", "HitTwoTimes", "HitTwoTimesPoisonTarget" # Frost Breath, Double Kick, Twineedle
baseDmg *= 2
@@ -255,12 +255,12 @@ class Battle::AI
end
when "HitOncePerUserTeamMember" # Beat Up
mult = 0
@battle.eachInTeamFromBattlerIndex(user.index) do |pkmn,_i|
@battle.eachInTeamFromBattlerIndex(user.index) do |pkmn, _i|
mult += 1 if pkmn && pkmn.able? && pkmn.status == :NONE
end
baseDmg *= mult
when "TwoTurnAttackOneTurnInSun" # Solar Beam
baseDmg = move.pbBaseDamageMultiplier(baseDmg,user,target)
baseDmg = move.pbBaseDamageMultiplier(baseDmg, user, target)
when "MultiTurnAttackPowersUpEachTurn" # Rollout
baseDmg *= 2 if user.effects[PBEffects::DefenseCurl]
when "MultiTurnAttackBideThenReturnDoubleDamage" # Bide
@@ -269,23 +269,23 @@ class Battle::AI
baseDmg = user.hp
when "EffectivenessIncludesFlyingType" # Flying Press
if GameData::Type.exists?(:FLYING)
if skill>=PBTrainerAI.highSkill
if skill >= PBTrainerAI.highSkill
targetTypes = target.pbTypes(true)
mult = Effectiveness.calculate(:FLYING,
targetTypes[0],targetTypes[1],targetTypes[2])
baseDmg = (baseDmg.to_f*mult/Effectiveness::NORMAL_EFFECTIVE).round
targetTypes[0], targetTypes[1], targetTypes[2])
baseDmg = (baseDmg.to_f * mult / Effectiveness::NORMAL_EFFECTIVE).round
else
mult = Effectiveness.calculate(:FLYING,
target.types[0], target.types[1], target.effects[PBEffects::Type3])
baseDmg = (baseDmg.to_f*mult/Effectiveness::NORMAL_EFFECTIVE).round
baseDmg = (baseDmg.to_f * mult / Effectiveness::NORMAL_EFFECTIVE).round
end
end
baseDmg *= 2 if skill>=PBTrainerAI.mediumSkill && target.effects[PBEffects::Minimize]
baseDmg *= 2 if skill >= PBTrainerAI.mediumSkill && target.effects[PBEffects::Minimize]
when "DoublePowerIfUserLastMoveFailed" # Stomping Tantrum
baseDmg *= 2 if user.lastRoundMoveFailed
when "HitTwoTimesFlinchTarget" # Double Iron Bash
baseDmg *= 2
baseDmg *= 2 if skill>=PBTrainerAI.mediumSkill && target.effects[PBEffects::Minimize]
baseDmg *= 2 if skill >= PBTrainerAI.mediumSkill && target.effects[PBEffects::Minimize]
end
return baseDmg
end
@@ -293,28 +293,28 @@ class Battle::AI
#=============================================================================
# Damage calculation
#=============================================================================
def pbRoughDamage(move,user,target,skill,baseDmg)
def pbRoughDamage(move, user, target, skill, baseDmg)
# Fixed damage moves
return baseDmg if move.is_a?(Battle::Move::FixedDamageMove)
# Get the move's type
type = pbRoughType(move,user,skill)
type = pbRoughType(move, user, skill)
##### Calculate user's attack stat #####
atk = pbRoughStat(user,:ATTACK,skill)
if move.function=="UseTargetAttackInsteadOfUserAttack" # Foul Play
atk = pbRoughStat(target,:ATTACK,skill)
elsif move.function=="UseUserBaseDefenseInsteadOfUserBaseAttack" # Body Press
atk = pbRoughStat(user,:DEFENSE,skill)
atk = pbRoughStat(user, :ATTACK, skill)
if move.function == "UseTargetAttackInsteadOfUserAttack" # Foul Play
atk = pbRoughStat(target, :ATTACK, skill)
elsif move.function == "UseUserBaseDefenseInsteadOfUserBaseAttack" # Body Press
atk = pbRoughStat(user, :DEFENSE, skill)
elsif move.specialMove?(type)
if move.function=="UseTargetAttackInsteadOfUserAttack" # Foul Play
atk = pbRoughStat(target,:SPECIAL_ATTACK,skill)
if move.function == "UseTargetAttackInsteadOfUserAttack" # Foul Play
atk = pbRoughStat(target, :SPECIAL_ATTACK, skill)
else
atk = pbRoughStat(user,:SPECIAL_ATTACK,skill)
atk = pbRoughStat(user, :SPECIAL_ATTACK, skill)
end
end
##### Calculate target's defense stat #####
defense = pbRoughStat(target,:DEFENSE,skill)
if move.specialMove?(type) && move.function!="UseTargetDefenseInsteadOfTargetSpDef" # Psyshock
defense = pbRoughStat(target,:SPECIAL_DEFENSE,skill)
defense = pbRoughStat(target, :DEFENSE, skill)
if move.specialMove?(type) && move.function != "UseTargetDefenseInsteadOfTargetSpDef" # Psyshock
defense = pbRoughStat(target, :SPECIAL_DEFENSE, skill)
end
##### Calculate all multiplier effects #####
multipliers = {
@@ -325,13 +325,13 @@ class Battle::AI
}
# Ability effects that alter damage
moldBreaker = false
if skill>=PBTrainerAI.highSkill && target.hasMoldBreaker?
if skill >= PBTrainerAI.highSkill && target.hasMoldBreaker?
moldBreaker = true
end
if skill>=PBTrainerAI.mediumSkill && user.abilityActive?
if skill >= PBTrainerAI.mediumSkill && user.abilityActive?
# NOTE: These abilities aren't suitable for checking at the start of the
# round.
abilityBlacklist = [:ANALYTIC,:SNIPER,:TINTEDLENS,:AERILATE,:PIXILATE,:REFRIGERATE]
abilityBlacklist = [:ANALYTIC, :SNIPER, :TINTEDLENS, :AERILATE, :PIXILATE, :REFRIGERATE]
canCheck = true
abilityBlacklist.each do |m|
next if move.id != m
@@ -340,20 +340,20 @@ class Battle::AI
end
if canCheck
Battle::AbilityEffects.triggerDamageCalcFromUser(user.ability,
user,target,move,multipliers,baseDmg,type)
user, target, move, multipliers, baseDmg, type)
end
end
if skill>=PBTrainerAI.mediumSkill && !moldBreaker
if skill >= PBTrainerAI.mediumSkill && !moldBreaker
user.allAllies.each do |b|
next if !b.abilityActive?
Battle::AbilityEffects.triggerDamageCalcFromAlly(b.ability,
user,target,move,multipliers,baseDmg,type)
user, target, move, multipliers, baseDmg, type)
end
end
if skill>=PBTrainerAI.bestSkill && !moldBreaker && target.abilityActive?
if skill >= PBTrainerAI.bestSkill && !moldBreaker && target.abilityActive?
# NOTE: These abilities aren't suitable for checking at the start of the
# round.
abilityBlacklist = [:FILTER,:SOLIDROCK]
abilityBlacklist = [:FILTER, :SOLIDROCK]
canCheck = true
abilityBlacklist.each do |m|
next if move.id != m
@@ -362,38 +362,38 @@ class Battle::AI
end
if canCheck
Battle::AbilityEffects.triggerDamageCalcFromTarget(target.ability,
user,target,move,multipliers,baseDmg,type)
user, target, move, multipliers, baseDmg, type)
end
end
if skill>=PBTrainerAI.bestSkill && !moldBreaker
if skill >= PBTrainerAI.bestSkill && !moldBreaker
target.allAllies.each do |b|
next if !b.abilityActive?
Battle::AbilityEffects.triggerDamageCalcFromTargetAlly(b.ability,
user,target,move,multipliers,baseDmg,type)
user, target, move, multipliers, baseDmg, type)
end
end
# Item effects that alter damage
# NOTE: Type-boosting gems aren't suitable for checking at the start of the
# round.
if skill>=PBTrainerAI.mediumSkill && user.itemActive?
if skill >= PBTrainerAI.mediumSkill && user.itemActive?
# NOTE: These items aren't suitable for checking at the start of the
# round.
itemBlacklist = [:EXPERTBELT,:LIFEORB]
itemBlacklist = [:EXPERTBELT, :LIFEORB]
if !itemBlacklist.include?(user.item_id)
Battle::ItemEffects.triggerDamageCalcFromUser(user.item,
user,target,move,multipliers,baseDmg,type)
user, target, move, multipliers, baseDmg, type)
end
end
if skill>=PBTrainerAI.bestSkill && target.itemActive?
if skill >= PBTrainerAI.bestSkill && target.itemActive?
# NOTE: Type-weakening berries aren't suitable for checking at the start
# of the round.
if target.item && !target.item.is_berry?
Battle::ItemEffects.triggerDamageCalcFromTarget(target.item,
user,target,move,multipliers,baseDmg,type)
user, target, move, multipliers, baseDmg, type)
end
end
# Global abilities
if skill>=PBTrainerAI.mediumSkill
if skill >= PBTrainerAI.mediumSkill
if (@battle.pbCheckGlobalAbility(:DARKAURA) && type == :DARK) ||
(@battle.pbCheckGlobalAbility(:FAIRYAURA) && type == :FAIRY)
if @battle.pbCheckGlobalAbility(:AURABREAK)
@@ -404,25 +404,25 @@ class Battle::AI
end
end
# Parental Bond
if skill>=PBTrainerAI.mediumSkill && user.hasActiveAbility?(:PARENTALBOND)
if skill >= PBTrainerAI.mediumSkill && user.hasActiveAbility?(:PARENTALBOND)
multipliers[:base_damage_multiplier] *= 1.25
end
# Me First
# TODO
# Helping Hand - n/a
# Charge
if skill>=PBTrainerAI.mediumSkill
if user.effects[PBEffects::Charge]>0 && type == :ELECTRIC
if skill >= PBTrainerAI.mediumSkill
if user.effects[PBEffects::Charge] > 0 && type == :ELECTRIC
multipliers[:base_damage_multiplier] *= 2
end
end
# Mud Sport and Water Sport
if skill>=PBTrainerAI.mediumSkill
if skill >= PBTrainerAI.mediumSkill
if type == :ELECTRIC
if @battle.allBattlers.any? { |b| b.effects[PBEffects::MudSport] }
multipliers[:base_damage_multiplier] /= 3
end
if @battle.field.effects[PBEffects::MudSportField]>0
if @battle.field.effects[PBEffects::MudSportField] > 0
multipliers[:base_damage_multiplier] /= 3
end
end
@@ -430,13 +430,13 @@ class Battle::AI
if @battle.allBattlers.any? { |b| b.effects[PBEffects::WaterSport] }
multipliers[:base_damage_multiplier] /= 3
end
if @battle.field.effects[PBEffects::WaterSportField]>0
if @battle.field.effects[PBEffects::WaterSportField] > 0
multipliers[:base_damage_multiplier] /= 3
end
end
end
# Terrain moves
if skill>=PBTrainerAI.mediumSkill
if skill >= PBTrainerAI.mediumSkill
case @battle.field.terrain
when :Electric
multipliers[:base_damage_multiplier] *= 1.5 if type == :ELECTRIC && user.affectedByTerrain?
@@ -449,7 +449,7 @@ class Battle::AI
end
end
# Badge multipliers
if skill>=PBTrainerAI.highSkill
if skill >= PBTrainerAI.highSkill
if @battle.internalBattle
# Don't need to check the Atk/Sp Atk-boosting badges because the AI
# won't control the player's Pokémon.
@@ -463,13 +463,13 @@ class Battle::AI
end
end
# Multi-targeting attacks
if skill>=PBTrainerAI.highSkill
if pbTargetsMultiple?(move,user)
if skill >= PBTrainerAI.highSkill
if pbTargetsMultiple?(move, user)
multipliers[:final_damage_multiplier] *= 0.75
end
end
# Weather
if skill>=PBTrainerAI.mediumSkill
if skill >= PBTrainerAI.mediumSkill
case user.effectiveWeather
when :Sun, :HarshSun
if type == :FIRE
@@ -493,7 +493,7 @@ class Battle::AI
# Critical hits - n/a
# Random variance - n/a
# STAB
if skill>=PBTrainerAI.mediumSkill
if skill >= PBTrainerAI.mediumSkill
if type && user.pbHasType?(type)
if user.hasActiveAbility?(:ADAPTABILITY)
multipliers[:final_damage_multiplier] *= 2
@@ -503,12 +503,12 @@ class Battle::AI
end
end
# Type effectiveness
if skill>=PBTrainerAI.mediumSkill
typemod = pbCalcTypeMod(type,user,target)
if skill >= PBTrainerAI.mediumSkill
typemod = pbCalcTypeMod(type, user, target)
multipliers[:final_damage_multiplier] *= typemod.to_f / Effectiveness::NORMAL_EFFECTIVE
end
# Burn
if skill>=PBTrainerAI.highSkill
if skill >= PBTrainerAI.highSkill
if user.status == :BURN && move.physicalMove?(type) &&
!user.hasActiveAbility?(:GUTS) &&
!(Settings::MECHANICS_GENERATION >= 6 && move.function == "DoublePowerIfUserPoisonedBurnedParalyzed") # Facade
@@ -516,7 +516,7 @@ class Battle::AI
end
end
# Aurora Veil, Reflect, Light Screen
if skill>=PBTrainerAI.highSkill
if skill >= PBTrainerAI.highSkill
if !move.ignoresReflect? && !user.hasActiveAbility?(:INFILTRATOR)
if target.pbOwnSide.effects[PBEffects::AuroraVeil] > 0
if @battle.pbSideBattlerCount(target) > 1
@@ -540,7 +540,7 @@ class Battle::AI
end
end
# Minimize
if skill>=PBTrainerAI.highSkill
if skill >= PBTrainerAI.highSkill
if target.effects[PBEffects::Minimize] && move.tramplesMinimize?(2)
multipliers[:final_damage_multiplier] *= 2
end
@@ -554,39 +554,39 @@ class Battle::AI
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 = [(damage * multipliers[:final_damage_multiplier]).round, 1].max
damage = [(damage * multipliers[:final_damage_multiplier]).round, 1].max
# "AI-specific calculations below"
# Increased critical hit rates
if skill>=PBTrainerAI.mediumSkill
if skill >= PBTrainerAI.mediumSkill
c = 0
# Ability effects that alter critical hit rate
if c>=0 && user.abilityActive?
if c >= 0 && user.abilityActive?
c = Battle::AbilityEffects.triggerCriticalCalcFromUser(user.ability, user, target, c)
end
if skill>=PBTrainerAI.bestSkill
if c>=0 && !moldBreaker && target.abilityActive?
if skill >= PBTrainerAI.bestSkill
if c >= 0 && !moldBreaker && target.abilityActive?
c = Battle::AbilityEffects.triggerCriticalCalcFromTarget(target.ability, user, target, c)
end
end
# Item effects that alter critical hit rate
if c>=0 && user.itemActive?
if c >= 0 && user.itemActive?
c = Battle::ItemEffects.triggerCriticalCalcFromUser(user.item, user, target, c)
end
if skill>=PBTrainerAI.bestSkill
if c>=0 && target.itemActive?
if skill >= PBTrainerAI.bestSkill
if c >= 0 && target.itemActive?
c = Battle::ItemEffects.triggerCriticalCalcFromTarget(target.item, user, target, c)
end
end
# Other efffects
c = -1 if target.pbOwnSide.effects[PBEffects::LuckyChant]>0
if c>=0
c = -1 if target.pbOwnSide.effects[PBEffects::LuckyChant] > 0
if c >= 0
c += 1 if move.highCriticalRate?
c += user.effects[PBEffects::FocusEnergy]
c += 1 if user.inHyperMode? && move.type == :SHADOW
end
if c>=0
c = 4 if c>4
damage += damage*0.1*c
if c >= 0
c = 4 if c > 4
damage += damage * 0.1 * c
end
end
return damage.floor
@@ -595,19 +595,19 @@ class Battle::AI
#=============================================================================
# Accuracy calculation
#=============================================================================
def pbRoughAccuracy(move,user,target,skill)
def pbRoughAccuracy(move, user, target, skill)
# "Always hit" effects and "always hit" accuracy
if skill>=PBTrainerAI.mediumSkill
if skill >= PBTrainerAI.mediumSkill
return 125 if target.effects[PBEffects::Minimize] && move.tramplesMinimize?(1)
return 125 if target.effects[PBEffects::Telekinesis]>0
return 125 if target.effects[PBEffects::Telekinesis] > 0
end
baseAcc = move.accuracy
if skill>=PBTrainerAI.highSkill
baseAcc = move.pbBaseAccuracy(user,target)
if skill >= PBTrainerAI.highSkill
baseAcc = move.pbBaseAccuracy(user, target)
end
return 125 if baseAcc==0 && skill>=PBTrainerAI.mediumSkill
return 125 if baseAcc == 0 && skill >= PBTrainerAI.mediumSkill
# Get the move's type
type = pbRoughType(move,user,skill)
type = pbRoughType(move, user, skill)
# Calculate all modifier effects
modifiers = {}
modifiers[:base_accuracy] = baseAcc
@@ -615,62 +615,62 @@ class Battle::AI
modifiers[:evasion_stage] = target.stages[:EVASION]
modifiers[:accuracy_multiplier] = 1.0
modifiers[:evasion_multiplier] = 1.0
pbCalcAccuracyModifiers(user,target,modifiers,move,type,skill)
pbCalcAccuracyModifiers(user, target, modifiers, move, type, skill)
# Check if move can't miss
return 125 if modifiers[:base_accuracy]==0
return 125 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]
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
evasion = 1 if evasion < 1
return modifiers[:base_accuracy] * accuracy / evasion
end
def pbCalcAccuracyModifiers(user,target,modifiers,move,type,skill)
def pbCalcAccuracyModifiers(user, target, modifiers, move, type, skill)
moldBreaker = false
if skill>=PBTrainerAI.highSkill && target.hasMoldBreaker?
if skill >= PBTrainerAI.highSkill && target.hasMoldBreaker?
moldBreaker = true
end
# Ability effects that alter accuracy calculation
if skill>=PBTrainerAI.mediumSkill
if skill >= PBTrainerAI.mediumSkill
if user.abilityActive?
Battle::AbilityEffects.triggerAccuracyCalcFromUser(user.ability,
modifiers,user,target,move,type)
modifiers, user, target, move, type)
end
user.allAllies.each do |b|
next if !b.abilityActive?
Battle::AbilityEffects.triggerAccuracyCalcFromAlly(b.ability,
modifiers,user,target,move,type)
modifiers, user, target, move, type)
end
end
if skill>=PBTrainerAI.bestSkill
if skill >= PBTrainerAI.bestSkill
if target.abilityActive? && !moldBreaker
Battle::AbilityEffects.triggerAccuracyCalcFromTarget(target.ability,
modifiers,user,target,move,type)
modifiers, user, target, move, type)
end
end
# Item effects that alter accuracy calculation
if skill>=PBTrainerAI.mediumSkill
if skill >= PBTrainerAI.mediumSkill
if user.itemActive?
Battle::ItemEffects.triggerAccuracyCalcFromUser(user.item,
modifiers,user,target,move,type)
modifiers, user, target, move, type)
end
end
if skill>=PBTrainerAI.bestSkill
if skill >= PBTrainerAI.bestSkill
if target.itemActive?
Battle::ItemEffects.triggerAccuracyCalcFromTarget(target.item,
modifiers,user,target,move,type)
modifiers, user, target, move, type)
end
end
# Other effects, inc. ones that set accuracy_multiplier or evasion_stage to specific values
if skill>=PBTrainerAI.mediumSkill
if skill >= PBTrainerAI.mediumSkill
if @battle.field.effects[PBEffects::Gravity] > 0
modifiers[:accuracy_multiplier] *= 5/3.0
modifiers[:accuracy_multiplier] *= 5 / 3.0
end
if user.effects[PBEffects::MicleBerry]
modifiers[:accuracy_multiplier] *= 1.2
@@ -679,20 +679,20 @@ class Battle::AI
modifiers[:evasion_stage] = 0 if target.effects[PBEffects::MiracleEye] && modifiers[:evasion_stage] > 0
end
# "AI-specific calculations below"
if skill>=PBTrainerAI.mediumSkill
if skill >= PBTrainerAI.mediumSkill
modifiers[:evasion_stage] = 0 if move.function == "IgnoreTargetDefSpDefEvaStatStages" # Chip Away
modifiers[:base_accuracy] = 0 if user.effects[PBEffects::LockOn]>0 &&
user.effects[PBEffects::LockOnPos]==target.index
modifiers[:base_accuracy] = 0 if user.effects[PBEffects::LockOn] > 0 &&
user.effects[PBEffects::LockOnPos] == target.index
end
if skill>=PBTrainerAI.highSkill
if move.function=="BadPoisonTarget" # Toxic
if skill >= PBTrainerAI.highSkill
if move.function == "BadPoisonTarget" # Toxic
modifiers[:base_accuracy] = 0 if Settings::MORE_TYPE_EFFECTS && move.statusMove? &&
user.pbHasType?(:POISON)
end
if ["OHKO", "OHKOIce", "OHKOHitsUndergroundTarget"].include?(move.function)
modifiers[:base_accuracy] = move.accuracy + user.level - target.level
modifiers[:accuracy_multiplier] = 0 if target.level > user.level
if skill>=PBTrainerAI.bestSkill
if skill >= PBTrainerAI.bestSkill
modifiers[:accuracy_multiplier] = 0 if target.hasActiveAbility?(:STURDY)
end
end