Merge branch 'ai' into dev

This commit is contained in:
Maruno17
2023-05-14 18:36:25 +01:00
160 changed files with 75932 additions and 5730 deletions

View File

@@ -0,0 +1,69 @@
# AI skill levels:
# 0: Wild Pokémon
# 1-31: Basic trainer (young/inexperienced)
# 32-47: Some skill
# 48-99: High skill
# 100+: Best trainers (Gym Leaders, Elite Four, Champion)
# NOTE: A trainer's skill value can range from 0-255, but by default only four
# distinct skill levels exist. The skill value is typically the same as
# the trainer's base money value.
module PBTrainerAI
# Minimum skill level to be in each AI category.
def self.minimumSkill; return 1; end
def self.mediumSkill; return 32; end
def self.highSkill; return 48; end
def self.bestSkill; return 100; end
end
class PokeBattle_AI
def initialize(battle)
@battle = battle
end
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 0 if n<2
mean = sum.to_f/n.to_f
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
#=============================================================================
# Decide whether the opponent should Mega Evolve their Pokémon
#=============================================================================
def pbEnemyShouldMegaEvolve?(idxBattler)
battler = @battle.battlers[idxBattler]
if @battle.pbCanMegaEvolve?(idxBattler) # Simple "always should if possible"
PBDebug.log("[AI] #{battler.pbThis} (#{idxBattler}) will Mega Evolve")
return true
end
return false
end
#=============================================================================
# Choose an action
#=============================================================================
def pbDefaultChooseEnemyCommand(idxBattler)
return if pbEnemyShouldUseItem?(idxBattler)
return if pbEnemyShouldWithdraw?(idxBattler)
return if @battle.pbAutoFightMenu(idxBattler)
@battle.pbRegisterMegaEvolution(idxBattler) if pbEnemyShouldMegaEvolve?(idxBattler)
pbChooseMoves(idxBattler)
end
end

View File

@@ -0,0 +1,182 @@
class PokeBattle_AI
#=============================================================================
# Decide whether the opponent should use an item on the Pokémon
#=============================================================================
def pbEnemyShouldUseItem?(idxBattler)
user = @battle.battlers[idxBattler]
item, idxTarget = pbEnemyItemToUse(idxBattler)
return false if item==0
# Determine target of item (always the Pokémon choosing the action)
useType = pbGetItemData(item,ITEM_BATTLE_USE)
if useType && (useType==1 || useType==6) # Use on Pokémon
idxTarget = @battle.battlers[idxTarget].pokemonIndex # Party Pokémon
end
# Register use of item
@battle.pbRegisterItem(idxBattler,item,idxTarget)
PBDebug.log("[AI] #{user.pbThis} (#{user.index}) will use item #{PBItems.getName(item)}")
return true
end
# NOTE: The AI will only consider using an item on the Pokémon it's currently
# choosing an action for.
def pbEnemyItemToUse(idxBattler)
return 0 if !@internalBattle
items = @battle.pbGetOwnerItems(idxBattler)
return 0 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]
pkmn = battler.pokemon
# Item categories
hpItems = {
:POTION => 20,
:SUPERPOTION => 50,
:HYPERPOTION => 200,
:MAXPOTION => 999,
:BERRYJUICE => 20,
:SWEETHEART => 20,
:FRESHWATER => 50,
:SODAPOP => 60,
:LEMONADE => 80,
:MOOMOOMILK => 100,
:ORANBERRY => 10,
:SITRUSBERRY => battler.totalhp/4,
:ENERGYPOWDER => 50,
:ENERGYROOT => 200
}
hpItems[:RAGECANDYBAR] = 20 if !NEWEST_BATTLE_MECHANICS
fullRestoreItems = [
:FULLRESTORE
]
oneStatusItems = [ # Preferred over items that heal all status problems
:AWAKENING,:CHESTOBERRY,:BLUEFLUTE,
:ANTIDOTE,:PECHABERRY,
:BURNHEAL,:RAWSTBERRY,
:PARALYZEHEAL,:PARLYZHEAL,:CHERIBERRY,
:ICEHEAL,:ASPEARBERRY
]
allStatusItems = [
:FULLHEAL,:LAVACOOKIE,:OLDGATEAU,:CASTELIACONE,:LUMIOSEGALETTE,
:SHALOURSABLE,:BIGMALASADA,:LUMBERRY,:HEALPOWDER
]
allStatusItems.push(:RAGECANDYBAR) if NEWEST_BATTLE_MECHANICS
xItems = {
:XATTACK => [PBStats::ATTACK,(NEWEST_BATTLE_MECHANICS) ? 2 : 1],
:XATTACK2 => [PBStats::ATTACK,2],
:XATTACK3 => [PBStats::ATTACK,3],
:XATTACK6 => [PBStats::ATTACK,6],
:XDEFENSE => [PBStats::DEFENSE,(NEWEST_BATTLE_MECHANICS) ? 2 : 1],
:XDEFENSE2 => [PBStats::DEFENSE,2],
:XDEFENSE3 => [PBStats::DEFENSE,3],
:XDEFENSE6 => [PBStats::DEFENSE,6],
:XDEFEND => [PBStats::DEFENSE,(NEWEST_BATTLE_MECHANICS) ? 2 : 1],
:XDEFEND2 => [PBStats::DEFENSE,2],
:XDEFEND3 => [PBStats::DEFENSE,3],
:XDEFEND6 => [PBStats::DEFENSE,6],
:XSPATK => [PBStats::SPATK,(NEWEST_BATTLE_MECHANICS) ? 2 : 1],
:XSPATK2 => [PBStats::SPATK,2],
:XSPATK3 => [PBStats::SPATK,3],
:XSPATK6 => [PBStats::SPATK,6],
:XSPECIAL => [PBStats::SPATK,(NEWEST_BATTLE_MECHANICS) ? 2 : 1],
:XSPECIAL2 => [PBStats::SPATK,2],
:XSPECIAL3 => [PBStats::SPATK,3],
:XSPECIAL6 => [PBStats::SPATK,6],
:XSPDEF => [PBStats::SPDEF,(NEWEST_BATTLE_MECHANICS) ? 2 : 1],
:XSPDEF2 => [PBStats::SPDEF,2],
:XSPDEF3 => [PBStats::SPDEF,3],
:XSPDEF6 => [PBStats::SPDEF,6],
:XSPEED => [PBStats::SPEED,(NEWEST_BATTLE_MECHANICS) ? 2 : 1],
:XSPEED2 => [PBStats::SPEED,2],
:XSPEED3 => [PBStats::SPEED,3],
:XSPEED6 => [PBStats::SPEED,6],
:XACCURACY => [PBStats::ACCURACY,(NEWEST_BATTLE_MECHANICS) ? 2 : 1],
:XACCURACY2 => [PBStats::ACCURACY,2],
:XACCURACY3 => [PBStats::ACCURACY,3],
:XACCURACY6 => [PBStats::ACCURACY,6]
}
losthp = battler.totalhp-battler.hp
preferFullRestore = (battler.hp<=battler.totalhp*2/3 &&
(battler.status!=PBStatuses::NONE || battler.effects[PBEffects::Confusion]>0))
# Find all usable items
usableHPItems = []
usableStatusItems = []
usableXItems = []
items.each do |i|
next if !i || i==0
next if !@battle.pbCanUseItemOnPokemon?(i,pkmn,battler,@battle.scene,false)
next if !ItemHandlers.triggerCanUseInBattle(i,pkmn,battler,nil,
false,self,@battle.scene,false)
checkedItem = false
# Log HP healing items
if losthp>0
hpItems.each do |item, power|
next if !isConst?(i,PBItems,item)
checkedItem = true
usableHPItems.push([i,5,power])
end
next if checkedItem
end
# Log Full Restores (HP healer and status curer)
if losthp>0 || battler.status!=PBStatuses::NONE
fullRestoreItems.each do |item|
next if !isConst?(i,PBItems,item)
checkedItem = true
usableHPItems.push([i,(preferFullRestore) ? 3 : 7,999])
usableStatusItems.push([i,(preferFullRestore) ? 3 : 9])
end
next if checkedItem
end
# Log single status-curing items
if battler.status!=PBStatuses::NONE
oneStatusItems.each do |item|
next if !isConst?(i,PBItems,item)
checkedItem = true
usableStatusItems.push([i,5])
end
next if checkedItem
# Log Full Heal-type items
allStatusItems.each do |item|
next if !isConst?(i,PBItems,item)
checkedItem = true
usableStatusItems.push([i,7])
end
next if checkedItem
end
# Log stat-raising items
xItems.each do |item, data|
next if !isConst?(i,PBItems,item)
checkedItem = true
usableXItems.push([i,battler.stages[data[0]],data[1]])
end
next if checkedItem
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] }
prevItem = nil
usableHPItems.each do |i|
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] }
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] }
prevItem = nil
usableXItems.each do |i|
break if prevItem && i[1]>prevItem[1]
return i[0],idxTarget if i[1]+i[2]>=6
prevItem = i
end
return prevItem[0],idxTarget
end
return 0
end
end

View File

@@ -0,0 +1,182 @@
class PokeBattle_AI
#=============================================================================
# Decide whether the opponent should switch Pokémon
#=============================================================================
def pbEnemyShouldWithdraw?(idxBattler)
return pbEnemyShouldWithdrawEx?(idxBattler,false)
end
def pbEnemyShouldWithdrawEx?(idxBattler,forceSwitch)
return false if @battle.wildBattle?
shouldSwitch = forceSwitch
batonPass = -1
moveType = -1
skill = @battle.pbGetOwnerFromBattlerIndex(idxBattler).skill || 0
battler = @battle.battlers[idxBattler]
# If Pokémon is within 6 levels of the foe, and foe's last move was
# super-effective and powerful
if !shouldSwitch && battler.turnCount>0 && skill>=PBTrainerAI.highSkill
target = battler.pbDirectOpposing(true)
if !target.fainted? && target.lastMoveUsed>0 &&
(target.level-battler.level).abs<=6
moveData = pbGetMoveData(target.lastMoveUsed)
moveType = moveData[MOVE_TYPE]
typeMod = pbCalcTypeMod(moveType,target,battler)
if PBTypes.superEffective?(typeMod) && moveData[MOVE_BASE_DAMAGE]>50
switchChance = (moveData[MOVE_BASE_DAMAGE]>70) ? 30 : 20
shouldSwitch = (pbAIRandom(100)<switchChance)
end
end
end
# Pokémon can't do anything (must have been in battle for at least 5 rounds)
if !@battle.pbCanChooseAnyMove?(idxBattler) &&
battler.turnCount && battler.turnCount>=5
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!="0ED" # Baton Pass
next if !@battle.pbCanChooseMove?(idxBattler,i,false)
batonPass = i
break
end
end
# Pokémon will faint because of bad poisoning at the end of this round, but
# would survive at least one more round if it were regular poisoning instead
if battler.status==PBStatuses::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
end
end
# Pokémon is Encored into an unfavourable move
if battler.effects[PBEffects::Encore]>0 && skill>=PBTrainerAI.mediumSkill
idxEncoredMove = battler.pbEncoredMoveIndex
if idxEncoredMove>=0
scoreSum = 0
scoreCount = 0
battler.eachOpposing do |b|
scoreSum += pbGetMoveScore(battler.moves[idxEncoredMove],battler,b,skill)
scoreCount += 1
end
if scoreCount>0 && scoreSum/scoreCount<=20
shouldSwitch = true if pbAIRandom(100)<80
end
end
end
# If there is a single foe and it is resting after Hyper Beam or is
# Truanting (i.e. free turn)
if @battle.pbSideSize(battler.index+1)==1 &&
!battler.pbDirectOpposing.fainted? && skill>=PBTrainerAI.highSkill
opp = battler.pbDirectOpposing
if 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"] && battler.turnCount>0
if battler.hp<=battler.totalhp/4 && pbAIRandom(100)<30
shouldSwitch = true
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
shouldSwitch = true
end
if shouldSwitch
list = []
@battle.pbParty(idxBattler).each_with_index do |pkmn,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
# 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
next if !pkmn.hasType?(:FLYING) && !pkmn.hasActiveAbility?(:LEVITATE)
end
end
end
# moveType is the type of the target's last used move
if moveType>=0 && PBTypes.ineffective?(pbCalcTypeMod(moveType,battler,battler))
weight = 65
typeMod = pbCalcTypeModPokemon(pkmn,battler.pbDirectOpposing(true))
if PBTypes.superEffective?(typeMod.to_f/PBTypeEffectivenesss::NORMAL_EFFECTIVE)
# 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>=0 && PBTypes.resistant?(pbCalcTypeMod(moveType,battler,battler))
weight = 40
typeMod = pbCalcTypeModPokemon(pkmn,battler.pbDirectOpposing(true))
if PBTypes.superEffective?(typeMod.to_f/PBTypeEffectivenesss::NORMAL_EFFECTIVE)
# 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
else
list.push(i) # put this Pokemon last
end
end
if list.length>0
if batonPass>=0 && @battle.pbRegisterMove(idxBattler,batonPass,false)
PBDebug.log("[AI] #{battler.pbThis} (#{idxBattler}) will use Baton Pass to avoid Perish Song")
return true
end
if @battle.pbRegisterSwitch(idxBattler,list[0])
PBDebug.log("[AI] #{battler.pbThis} (#{idxBattler}) will switch with " +
"#{@battle.pbParty(idxBattler)[list[0]].name}")
return
end
end
end
return false
end
#=============================================================================
# Choose a replacement Pokémon
#=============================================================================
def pbDefaultChooseNewEnemy(idxBattler,party)
enemies = []
party.each_with_index do |_p,i|
enemies.push(i) if @battle.pbCanSwitchLax?(idxBattler,i)
end
return -1 if enemies.length==0
return pbChooseBestNewEnemy(idxBattler,party,enemies)
end
def pbChooseBestNewEnemy(idxBattler,party,enemies)
return -1 if !enemies || enemies.length==0
best = -1
bestSum = 0
movesData = pbLoadMovesData
enemies.each do |i|
pkmn = party[i]
sum = 0
pkmn.moves.each do |m|
next if m.id==0
moveData = movesData[m.id]
next if moveData[MOVE_BASE_DAMAGE]==0
@battle.battlers[idxBattler].eachOpposing do |b|
bTypes = b.pbTypes(true)
sum += PBTypes.getCombinedEffectiveness(moveData[MOVE_TYPE],
bTypes[0],bTypes[1],bTypes[2])
end
end
if best==-1 || sum>bestSum
best = i
bestSum = sum
end
end
return best
end
end

View File

@@ -0,0 +1,286 @@
class PokeBattle_AI
#=============================================================================
# Main move-choosing method (moves with higher scores are more likely to be
# chosen)
#=============================================================================
def pbChooseMoves(idxBattler)
user = @battle.battlers[idxBattler]
wildBattler = (@battle.wildBattle? && @battle.opposes?(idxBattler))
skill = 0
if !wildBattler
skill = @battle.pbGetOwnerFromBattlerIndex(user.index).skill || 0
end
# Get scores and targets for each move
# NOTE: A move is only added to the choices array if it has a non-zero
# score.
choices = []
user.eachMoveWithIndex do |_m,i|
next if !@battle.pbCanChooseMove?(idxBattler,i,false)
if wildBattler
pbRegisterMoveWild(user,i,choices)
else
pbRegisterMoveTrainer(user,i,choices,skill)
end
end
# Figure out useful information about the choices
totalScore = 0
maxScore = 0
choices.each do |c|
totalScore += c[1]
maxScore = c[1] if maxScore<c[1]
end
# Log the available choices
if $INTERNAL
logMsg = "[AI] Move choices for #{user.pbThis(true)} (#{user.index}): "
choices.each_with_index do |c,i|
logMsg += "#{user.moves[c[0]].name}=#{c[1]}"
logMsg += " (target #{c[2]})" if c[2]>=0
logMsg += ", " if i<choices.length-1
end
PBDebug.log(logMsg)
end
# Find any preferred moves and just choose from them
if !wildBattler && skill>=PBTrainerAI.highSkill && maxScore>100
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(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
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?(idxBattler,true)
if $INTERNAL
PBDebug.log("[AI] #{user.pbThis} (#{user.index}) will switch due to terrible moves")
end
return
end
end
# Randomly choose a move to use
if choices.length==0
# If there are no calculated choices, use Struggle (or an Encored move)
@battle.pbAutoChooseMove(idxBattler)
else
# Randomly choose a move from the choices and register it
randNum = pbAIRandom(totalScore)
choices.each do |c|
randNum -= c[1]
next if randNum>=0
@battle.pbRegisterMove(idxBattler,c[0],false)
@battle.pbRegisterTarget(idxBattler,c[2]) if c[2]>=0
break
end
end
# Log the result
if @battle.choices[idxBattler][2]
PBDebug.log("[AI] #{user.pbThis} (#{user.index}) will use #{@battle.choices[user.index][2].name}")
end
end
#=============================================================================
# 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
end
# Trainer Pokémon calculate how much they want to use each of their moves.
def pbRegisterMoveTrainer(user,idxMove,choices,skill)
move = user.moves[idxMove]
targetType = move.pbTarget(user)
if PBTargets.multipleTargets?(targetType)
# If move affects multiple battlers and you don't choose a particular one
totalScore = 0
@battle.eachBattler do |b|
next if !@battle.pbMoveCanTarget?(user.index,b.index,targetType)
score = pbGetMoveScore(move,user,b,skill)
totalScore += ((user.opposes?(b)) ? score : -score)
end
choices.push([idxMove,totalScore,-1]) if totalScore>0
elsif PBTargets.noTargets?(targetType)
# 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
else
# If move affects one battler and you have to choose which one
scoresAndTargets = []
@battle.eachBattler do |b|
next if !@battle.pbMoveCanTarget?(user.index,b.index,targetType)
next if PBTargets.canChooseFoeTarget?(targetType) && !user.opposes?(b)
score = pbGetMoveScore(move,user,b,skill)
scoresAndTargets.push([score,b.index]) if score>0
end
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]])
end
end
end
#=============================================================================
# Get a score for the given move being used against the given target
#=============================================================================
def pbGetMoveScore(move,user,target,skill=100)
skill = PBTrainerAI.minimumSkill if skill<PBTrainerAI.minimumSkill
score = 100
score = pbGetMoveScoreFunctionCode(score,move,user,target,skill)
# A score of 0 here means it absolutely should not be used
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 move.statusMove?
score /= 1.5
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)
miss = true
miss = false if user.hasActiveAbility?(:NOGUARD) || target.hasActiveAbility?(:NOGUARD)
if miss && pbRoughStat(user,PBStats::SPEED,skill)>pbRoughStat(target,PBStats::SPEED,skill)
# Knows what can get past semi-invulnerability
if target.effects[PBEffects::SkyDrop]>=0
miss = false if move.hitsFlyingTargets?
else
if target.inTwoTurnAttack?("0C9","0CC","0CE") # Fly, Bounce, Sky Drop
miss = false if move.hitsFlyingTargets?
elsif target.inTwoTurnAttack?("0CA") # Dig
miss = false if move.hitsDiggingTargets?
elsif target.inTwoTurnAttack?("0CB") # Dive
miss = false if move.hitsDivingTargets?
end
end
end
score -= 80 if miss
end
# Pick a good move for the Choice items
if user.hasActiveItem?([:CHOICEBAND,:CHOICESPECS,:CHOICESCARF])
if move.baseDamage>=60; score += 60
elsif move.damagingMove?; score += 30
elsif move.function=="0F2"; score += 70 # Trick
else; score -= 60
end
end
# If user is asleep, prefer moves that are usable while asleep
if user.status==PBStatuses::SLEEP && !move.usableWhenAsleep?
user.eachMove do |m|
next unless m.usableWhenAsleep?
score -= 60
break
end
end
# If user is frozen, prefer a move that can thaw the user
if user.status==PBStatuses::FROZEN
if move.thawsUser?
score += 40
else
user.eachMove do |m|
next unless m.thawsUser?
score -= 60
break
end
end
end
# If target is frozen, don't prefer moves that could thaw them
if target.status==PBStatuses::FROZEN
user.eachMove do |m|
next if m.thawsUser?
score -= 60
break
end
end
end
# Adjust score based on how much damage it can deal
if move.damagingMove?
score = pbGetMoveScoreDamage(score,move,user,target,skill)
else # Status moves
# Don't prefer attacks which don't deal damage
score -= 10
# Account for accuracy of move
accuracy = pbRoughAccuracy(move,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
return score
end
#=============================================================================
# Add to a move's score based on how much damage it will deal (as a percentage
# of the target's current HP)
#=============================================================================
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)
# Calculate how much damage the move will do (roughly)
baseDmg = pbMoveBaseDamage(move,user,target,skill)
realDamage = pbRoughDamage(move,user,target,skill,baseDmg)
# Account for accuracy of move
accuracy = pbRoughAccuracy(move,user,target,skill)
realDamage *= accuracy/100.0
# Two-turn attacks waste 2 turns to deal one lot of damage
if move.chargingTurnMove? || move.function=="0C2" # 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
if !target.hasActiveAbility?(:INNERFOCUS) &&
!target.hasActiveAbility?(:SHIELDDUST) &&
target.effects[PBEffects::Substitute]==0
canFlinch = false
if move.canKingsRock? && user.hasActiveItem?([:KINGSROCK,:RAZORFANG])
canFlinch = true
end
if user.hasActiveAbility?(:STENCH) && !move.flinchingMove?
canFlinch = true
end
realDamage *= 1.3 if canFlinch
end
end
# Convert damage to percentage of target's remaining 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
# Adjust score
damagePercentage = 120 if damagePercentage>120 # Treat all lethal moves the same
damagePercentage += 40 if damagePercentage>100 # Prefer moves likely to be lethal
score += damagePercentage.to_i
return score
end
end

View File

@@ -0,0 +1,838 @@
class PokeBattle_AI
#=============================================================================
# Get a score for the given move based on its effect
#=============================================================================
def pbGetMoveScoreFunctionCode(score,move,user,target,skill=100)
case move.function
#---------------------------------------------------------------------------
when "000" # No extra effect
#---------------------------------------------------------------------------
when "001"
score -= 95
score = 0 if skill>=PBTrainerAI.highSkill
#---------------------------------------------------------------------------
when "002" # Struggle
#---------------------------------------------------------------------------
when "003"
if target.pbCanSleep?(user,false)
score += 30
if skill>=PBTrainerAI.mediumSkill
score -= 30 if target.effects[PBEffects::Yawn]>0
end
if skill>=PBTrainerAI.highSkill
score -= 30 if target.hasActiveAbility?(:MARVELSCALE)
end
if skill>=PBTrainerAI.bestSkill
if target.pbHasMoveFunction?("011","0B4") # Snore, Sleep Talk
score -= 50
end
end
else
if skill>=PBTrainerAI.mediumSkill
score -= 90 if move.statusMove?
end
end
#---------------------------------------------------------------------------
when "004"
if target.effects[PBEffects::Yawn]>0 || !target.pbCanSleep?(user,false)
score -= 90 if skill>=PBTrainerAI.mediumSkill
else
score += 30
if skill>=PBTrainerAI.highSkill
score -= 30 if target.hasActiveAbility?(:MARVELSCALE)
end
if skill>=PBTrainerAI.bestSkill
if target.pbHasMoveFunction?("011","0B4") # Snore, Sleep Talk
score -= 50
end
end
end
#---------------------------------------------------------------------------
when "005", "006", "0BE"
if target.pbCanPoison?(user,false)
score += 30
if skill>=PBTrainerAI.mediumSkill
score += 30 if target.hp<=target.totalhp/4
score += 50 if target.hp<=target.totalhp/8
score -= 40 if target.effects[PBEffects::Yawn]>0
end
if skill>=PBTrainerAI.highSkill
score += 10 if pbRoughStat(target,PBStats::DEFENSE,skill)>100
score += 10 if pbRoughStat(target,PBStats::SPDEF,skill)>100
score -= 40 if target.hasActiveAbility?([:GUTS,:MARVELSCALE,:TOXICBOOST])
end
else
if skill>=PBTrainerAI.mediumSkill
score -= 90 if move.statusMove?
end
end
#---------------------------------------------------------------------------
when "007", "008", "009", "0C5"
if target.pbCanParalyze?(user,false) &&
!(skill>=PBTrainerAI.mediumSkill &&
isConst?(move.id,PBMoves,:THUNDERWAVE) &&
PBTypes.ineffective?(pbCalcTypeMod(move.type,user,target)))
score += 30
if skill>=PBTrainerAI.mediumSkill
aspeed = pbRoughStat(user,PBStats::SPEED,skill)
ospeed = pbRoughStat(target,PBStats::SPEED,skill)
if aspeed<ospeed
score += 30
elsif aspeed>ospeed
score -= 40
end
end
if skill>=PBTrainerAI.highSkill
score -= 40 if target.hasActiveAbility?([:GUTS,:MARVELSCALE,:QUICKFEET])
end
else
if skill>=PBTrainerAI.mediumSkill
score -= 90 if move.statusMove?
end
end
#---------------------------------------------------------------------------
when "00A", "00B", "0C6"
if target.pbCanBurn?(user,false)
score += 30
if skill>=PBTrainerAI.highSkill
score -= 40 if target.hasActiveAbility?([:GUTS,:MARVELSCALE,:QUICKFEET,:FLAREBOOST])
end
else
if skill>=PBTrainerAI.mediumSkill
score -= 90 if move.statusMove?
end
end
#---------------------------------------------------------------------------
when "00C", "00D", "00E"
if target.pbCanFreeze?(user,false)
score += 30
if skill>=PBTrainerAI.highSkill
score -= 20 if target.hasActiveAbility?(:MARVELSCALE)
end
else
if skill>=PBTrainerAI.mediumSkill
score -= 90 if move.statusMove?
end
end
#---------------------------------------------------------------------------
when "00F"
score += 30
if skill>=PBTrainerAI.highSkill
score += 30 if !target.hasActiveAbility?(:INNERFOCUS) &&
target.effects[PBEffects::Substitute]==0
end
#---------------------------------------------------------------------------
when "010"
if skill>=PBTrainerAI.highSkill
score += 30 if !target.hasActiveAbility?(:INNERFOCUS) &&
target.effects[PBEffects::Substitute]==0
end
score += 30 if target.effects[PBEffects::Minimize]
#---------------------------------------------------------------------------
when "011"
if user.asleep?
score += 100 # Because it can only be used while asleep
if skill>=PBTrainerAI.highSkill
score += 30 if !target.hasActiveAbility?(:INNERFOCUS) &&
target.effects[PBEffects::Substitute]==0
end
else
score -= 90 # Because it will fail here
score = 0 if skill>=PBTrainerAI.bestSkill
end
#---------------------------------------------------------------------------
when "012"
if user.turnCount==0
if skill>=PBTrainerAI.highSkill
score += 30 if !target.hasActiveAbility?(:INNERFOCUS) &&
target.effects[PBEffects::Substitute]==0
end
else
score -= 90 # Because it will fail here
score = 0 if skill>=PBTrainerAI.bestSkill
end
#---------------------------------------------------------------------------
when "013", "014", "015"
if target.pbCanConfuse?(user,false)
score += 30
else
if skill>=PBTrainerAI.mediumSkill
score -= 90 if move.statusMove?
end
end
#---------------------------------------------------------------------------
when "016"
canattract = true
agender = user.gender
ogender = target.gender
if agender==2 || ogender==2 || agender==ogender
score -= 90; canattract = false
elsif target.effects[PBEffects::Attract]>=0
score -= 80; canattract = false
elsif skill>=PBTrainerAI.bestSkill && target.hasActiveAbility?(:OBLIVIOUS)
score -= 80; canattract = false
end
if skill>=PBTrainerAI.highSkill
if canattract && target.hasActiveItem?(:DESTINYKNOT) &&
user.pbCanAttract?(target,false)
score -= 30
end
end
#---------------------------------------------------------------------------
when "017"
score += 30 if target.status==PBStatuses::NONE
#---------------------------------------------------------------------------
when "018"
case user.status
when PBStatuses::POISON
score += 40
if skill>=PBTrainerAI.mediumSkill
if user.hp<user.totalhp/8
score += 60
elsif skill>=PBTrainerAI.highSkill &&
user.hp<(user.effects[PBEffects::Toxic]+1)*user.totalhp/16
score += 60
end
end
when PBStatuses::BURN, PBStatuses::PARALYSIS
score += 40
else
score -= 90
end
#---------------------------------------------------------------------------
when "019"
statuses = 0
@battle.pbParty(user.index).each do |pkmn|
statuses += 1 if pkmn && pkmn.status!=PBStatuses::NONE
end
if statuses==0
score -= 80
else
score += 20*statuses
end
#---------------------------------------------------------------------------
when "01A"
if user.pbOwnSide.effects[PBEffects::Safeguard]>0
score -= 80
elsif user.status!=0
score -= 40
else
score += 30
end
#---------------------------------------------------------------------------
when "01B"
if user.status==PBStatuses::NONE
score -= 90
else
score += 40
end
#---------------------------------------------------------------------------
when "01C"
if move.statusMove?
if user.statStageAtMax?(PBStats::ATTACK)
score -= 90
else
score -= user.stages[PBStats::ATTACK]*20
if skill>=PBTrainerAI.mediumSkill
hasPhysicalAttack = false
user.eachMove do |m|
next if !m.physicalMove?(m.type)
hasPhysicalAttack = true
break
end
if hasPhysicalAttack
score += 20
elsif skill>=PBTrainerAI.highSkill
score -= 90
end
end
end
else
score += 20 if user.stages[PBStats::ATTACK]<0
if skill>=PBTrainerAI.mediumSkill
hasPhysicalAttack = false
user.eachMove do |m|
next if !m.physicalMove?(m.type)
hasPhysicalAttack = true
break
end
score += 20 if hasPhysicalAttack
end
end
#---------------------------------------------------------------------------
when "01D", "01E", "0C8"
if move.statusMove?
if user.statStageAtMax?(PBStats::DEFENSE)
score -= 90
else
score -= user.stages[PBStats::DEFENSE]*20
end
else
score += 20 if user.stages[PBStats::DEFENSE]<0
end
#---------------------------------------------------------------------------
when "01F"
if move.statusMove?
if user.statStageAtMax?(PBStats::SPEED)
score -= 90
else
score -= user.stages[PBStats::SPEED]*10
if skill>=PBTrainerAI.highSkill
aspeed = pbRoughStat(user,PBStats::SPEED,skill)
ospeed = pbRoughStat(target,PBStats::SPEED,skill)
score += 30 if aspeed<ospeed && aspeed*2>ospeed
end
end
else
score += 20 if user.stages[PBStats::SPEED]<0
end
#---------------------------------------------------------------------------
when "020"
if move.statusMove?
if user.statStageAtMax?(PBStats::SPATK)
score -= 90
else
score -= user.stages[PBStats::SPATK]*20
if skill>=PBTrainerAI.mediumSkill
hasSpecicalAttack = false
user.eachMove do |m|
next if !m.specialMove?(m.type)
hasSpecicalAttack = true
break
end
if hasSpecicalAttack
score += 20
elsif skill>=PBTrainerAI.highSkill
score -= 90
end
end
end
else
score += 20 if user.stages[PBStats::SPATK]<0
if skill>=PBTrainerAI.mediumSkill
hasSpecicalAttack = false
user.eachMove do |m|
next if !m.specialMove?(m.type)
hasSpecicalAttack = true
break
end
score += 20 if hasSpecicalAttack
end
end
#---------------------------------------------------------------------------
when "021"
foundMove = false
user.eachMove do |m|
next if !isConst?(m.type,PBTypes,:ELECTRIC) || !m.damagingMove?
foundMove = true
break
end
score += 20 if foundMove
if move.statusMove?
if user.statStageAtMax?(PBStats::SPDEF)
score -= 90
else
score -= user.stages[PBStats::SPDEF]*20
end
else
score += 20 if user.stages[PBStats::SPDEF]<0
end
#---------------------------------------------------------------------------
when "022"
if move.statusMove?
if user.statStageAtMax?(PBStats::EVASION)
score -= 90
else
score -= user.stages[PBStats::EVASION]*10
end
else
score += 20 if user.stages[PBStats::EVASION]<0
end
#---------------------------------------------------------------------------
when "023"
if move.statusMove?
if user.effects[PBEffects::FocusEnergy]>=2
score -= 80
else
score += 30
end
else
score += 30 if user.effects[PBEffects::FocusEnergy]<2
end
#---------------------------------------------------------------------------
when "024"
if user.statStageAtMax?(PBStats::ATTACK) &&
user.statStageAtMax?(PBStats::DEFENSE)
score -= 90
else
score -= user.stages[PBStats::ATTACK]*10
score -= user.stages[PBStats::DEFENSE]*10
if skill>=PBTrainerAI.mediumSkill
hasPhysicalAttack = false
user.eachMove do |m|
next if !m.physicalMove?(m.type)
hasPhysicalAttack = true
break
end
if hasPhysicalAttack
score += 20
elsif skill>=PBTrainerAI.highSkill
score -= 90
end
end
end
#---------------------------------------------------------------------------
when "025"
if user.statStageAtMax?(PBStats::ATTACK) &&
user.statStageAtMax?(PBStats::DEFENSE) &&
user.statStageAtMax?(PBStats::ACCURACY)
score -= 90
else
score -= user.stages[PBStats::ATTACK]*10
score -= user.stages[PBStats::DEFENSE]*10
score -= user.stages[PBStats::ACCURACY]*10
if skill>=PBTrainerAI.mediumSkill
hasPhysicalAttack = false
user.eachMove do |m|
next if !m.physicalMove?(m.type)
hasPhysicalAttack = true
break
end
if hasPhysicalAttack
score += 20
elsif skill>=PBTrainerAI.highSkill
score -= 90
end
end
end
#---------------------------------------------------------------------------
when "026"
score += 40 if user.turnCount==0 # Dragon Dance tends to be popular
if user.statStageAtMax?(PBStats::ATTACK) &&
user.statStageAtMax?(PBStats::SPEED)
score -= 90
else
score -= user.stages[PBStats::ATTACK]*10
score -= user.stages[PBStats::SPEED]*10
if skill>=PBTrainerAI.mediumSkill
hasPhysicalAttack = false
user.eachMove do |m|
next if !m.physicalMove?(m.type)
hasPhysicalAttack = true
break
end
if hasPhysicalAttack
score += 20
elsif skill>=PBTrainerAI.highSkill
score -= 90
end
end
if skill>=PBTrainerAI.highSkill
aspeed = pbRoughStat(user,PBStats::SPEED,skill)
ospeed = pbRoughStat(target,PBStats::SPEED,skill)
score += 20 if aspeed<ospeed && aspeed*2>ospeed
end
end
#---------------------------------------------------------------------------
when "027", "028"
if user.statStageAtMax?(PBStats::ATTACK) &&
user.statStageAtMax?(PBStats::SPATK)
score -= 90
else
score -= user.stages[PBStats::ATTACK]*10
score -= user.stages[PBStats::SPATK]*10
if skill>=PBTrainerAI.mediumSkill
hasDamagingAttack = false
user.eachMove do |m|
next if !m.damagingMove?
hasDamagingAttack = true
break
end
if hasDamagingAttack
score += 20
elsif skill>=PBTrainerAI.highSkill
score -= 90
end
end
if move.function=="028" # Growth
score += 20 if @battle.pbWeather==PBWeather::Sun ||
@battle.pbWeather==PBWeather::HarshSun
end
end
#---------------------------------------------------------------------------
when "029"
if user.statStageAtMax?(PBStats::ATTACK) &&
user.statStageAtMax?(PBStats::ACCURACY)
score -= 90
else
score -= user.stages[PBStats::ATTACK]*10
score -= user.stages[PBStats::ACCURACY]*10
if skill>=PBTrainerAI.mediumSkill
hasPhysicalAttack = false
user.eachMove do |m|
next if !m.physicalMove?(m.type)
hasPhysicalAttack = true
break
end
if hasPhysicalAttack
score += 20
elsif skill>=PBTrainerAI.highSkill
score -= 90
end
end
end
#---------------------------------------------------------------------------
when "02A"
if user.statStageAtMax?(PBStats::DEFENSE) &&
user.statStageAtMax?(PBStats::SPDEF)
score -= 90
else
score -= user.stages[PBStats::DEFENSE]*10
score -= user.stages[PBStats::SPDEF]*10
end
#---------------------------------------------------------------------------
when "02B"
if user.statStageAtMax?(PBStats::SPEED) &&
user.statStageAtMax?(PBStats::SPATK) &&
user.statStageAtMax?(PBStats::SPDEF)
score -= 90
else
score -= user.stages[PBStats::SPATK]*10
score -= user.stages[PBStats::SPDEF]*10
score -= user.stages[PBStats::SPEED]*10
if skill>=PBTrainerAI.mediumSkill
hasSpecicalAttack = false
user.eachMove do |m|
next if !m.specialMove?(m.type)
hasSpecicalAttack = true
break
end
if hasSpecicalAttack
score += 20
elsif skill>=PBTrainerAI.highSkill
score -= 90
end
end
if skill>=PBTrainerAI.highSkill
aspeed = pbRoughStat(user,PBStats::SPEED,skill)
ospeed = pbRoughStat(target,PBStats::SPEED,skill)
if aspeed<ospeed && aspeed*2>ospeed
score += 20
end
end
end
#---------------------------------------------------------------------------
when "02C"
if user.statStageAtMax?(PBStats::SPATK) &&
user.statStageAtMax?(PBStats::SPDEF)
score -= 90
else
score += 40 if user.turnCount==0 # Calm Mind tends to be popular
score -= user.stages[PBStats::SPATK]*10
score -= user.stages[PBStats::SPDEF]*10
if skill>=PBTrainerAI.mediumSkill
hasSpecicalAttack = false
user.eachMove do |m|
next if !m.specialMove?(m.type)
hasSpecicalAttack = true
break
end
if hasSpecicalAttack
score += 20
elsif skill>=PBTrainerAI.highSkill
score -= 90
end
end
end
#---------------------------------------------------------------------------
when "02D"
PBStats.eachMainBattleStat { |s| score += 10 if user.stages[s]<0 }
if skill>=PBTrainerAI.mediumSkill
hasDamagingAttack = false
user.eachMove do |m|
next if !m.damagingMove?
hasDamagingAttack = true
break
end
score += 20 if hasDamagingAttack
end
#---------------------------------------------------------------------------
when "02E"
if move.statusMove?
if user.statStageAtMax?(PBStats::ATTACK)
score -= 90
else
score += 40 if user.turnCount==0
score -= user.stages[PBStats::ATTACK]*20
if skill>=PBTrainerAI.mediumSkill
hasPhysicalAttack = false
user.eachMove do |m|
next if !m.physicalMove?(m.type)
hasPhysicalAttack = true
break
end
if hasPhysicalAttack
score += 20
elsif skill>=PBTrainerAI.highSkill
score -= 90
end
end
end
else
score += 10 if user.turnCount==0
score += 20 if user.stages[PBStats::ATTACK]<0
if skill>=PBTrainerAI.mediumSkill
hasPhysicalAttack = false
user.eachMove do |m|
next if !m.physicalMove?(m.type)
hasPhysicalAttack = true
break
end
score += 20 if hasPhysicalAttack
end
end
#---------------------------------------------------------------------------
when "02F"
if move.statusMove?
if user.statStageAtMax?(PBStats::DEFENSE)
score -= 90
else
score += 40 if user.turnCount==0
score -= user.stages[PBStats::DEFENSE]*20
end
else
score += 10 if user.turnCount==0
score += 20 if user.stages[PBStats::DEFENSE]<0
end
#---------------------------------------------------------------------------
when "030", "031"
if move.statusMove?
if user.statStageAtMax?(PBStats::SPEED)
score -= 90
else
score += 20 if user.turnCount==0
score -= user.stages[PBStats::SPEED]*10
if skill>=PBTrainerAI.highSkill
aspeed = pbRoughStat(user,PBStats::SPEED,skill)
ospeed = pbRoughStat(target,PBStats::SPEED,skill)
score += 30 if aspeed<ospeed && aspeed*2>ospeed
end
end
else
score += 10 if user.turnCount==0
score += 20 if user.stages[PBStats::SPEED]<0
end
#---------------------------------------------------------------------------
when "032"
if move.statusMove?
if user.statStageAtMax?(PBStats::SPATK)
score -= 90
else
score += 40 if user.turnCount==0
score -= user.stages[PBStats::SPATK]*20
if skill>=PBTrainerAI.mediumSkill
hasSpecicalAttack = false
user.eachMove do |m|
next if !m.specialMove?(m.type)
hasSpecicalAttack = true
break
end
if hasSpecicalAttack
score += 20
elsif skill>=PBTrainerAI.highSkill
score -= 90
end
end
end
else
score += 10 if user.turnCount==0
score += 20 if user.stages[PBStats::SPATK]<0
if skill>=PBTrainerAI.mediumSkill
hasSpecicalAttack = false
user.eachMove do |m|
next if !m.specialMove?(m.type)
hasSpecicalAttack = true
break
end
score += 20 if hasSpecicalAttack
end
end
#---------------------------------------------------------------------------
when "033"
if move.statusMove?
if user.statStageAtMax?(PBStats::SPDEF)
score -= 90
else
score += 40 if user.turnCount==0
score -= user.stages[PBStats::SPDEF]*20
end
else
score += 10 if user.turnCount==0
score += 20 if user.stages[PBStats::SPDEF]<0
end
#---------------------------------------------------------------------------
when "034"
if move.statusMove?
if user.statStageAtMax?(PBStats::EVASION)
score -= 90
else
score += 40 if user.turnCount==0
score -= user.stages[PBStats::EVASION]*10
end
else
score += 10 if user.turnCount==0
score += 20 if user.stages[PBStats::EVASION]<0
end
#---------------------------------------------------------------------------
when "035"
score -= user.stages[PBStats::ATTACK]*20
score -= user.stages[PBStats::SPEED]*20
score -= user.stages[PBStats::SPATK]*20
score += user.stages[PBStats::DEFENSE]*10
score += user.stages[PBStats::SPDEF]*10
if skill>=PBTrainerAI.mediumSkill
hasDamagingAttack = false
user.eachMove do |m|
next if !m.damagingMove?
hasDamagingAttack = true
break
end
score += 20 if hasDamagingAttack
end
#---------------------------------------------------------------------------
when "036"
if user.statStageAtMax?(PBStats::ATTACK) &&
user.statStageAtMax?(PBStats::SPEED)
score -= 90
else
score -= user.stages[PBStats::ATTACK]*10
score -= user.stages[PBStats::SPEED]*10
if skill>=PBTrainerAI.mediumSkill
hasPhysicalAttack = false
user.eachMove do |m|
next if !m.physicalMove?(m.type)
hasPhysicalAttack = true
break
end
if hasPhysicalAttack
score += 20
elsif skill>=PBTrainerAI.highSkill
score -= 90
end
end
if skill>=PBTrainerAI.highSkill
aspeed = pbRoughStat(user,PBStats::SPEED,skill)
ospeed = pbRoughStat(target,PBStats::SPEED,skill)
score += 30 if aspeed<ospeed && aspeed*2>ospeed
end
end
#---------------------------------------------------------------------------
when "037"
avgStat = 0; canChangeStat = false
PBStats.eachBattleStat do |s|
next if target.statStageAtMax?(s)
avgStat -= target.stages[s]
canChangeStat = true
end
if canChangeStat
avgStat = avgStat/2 if avgStat<0 # More chance of getting even better
score += avgStat*10
else
score -= 90
end
#---------------------------------------------------------------------------
when "038"
if move.statusMove?
if user.statStageAtMax?(PBStats::DEFENSE)
score -= 90
else
score += 40 if user.turnCount==0
score -= user.stages[PBStats::DEFENSE]*30
end
else
score += 10 if user.turnCount==0
score += 30 if user.stages[PBStats::DEFENSE]<0
end
#---------------------------------------------------------------------------
when "039"
if move.statusMove?
if user.statStageAtMax?(PBStats::SPATK)
score -= 90
else
score += 40 if user.turnCount==0
score -= user.stages[PBStats::SPATK]*30
if skill>=PBTrainerAI.mediumSkill
hasSpecicalAttack = false
user.eachMove do |m|
next if !m.specialMove?(m.type)
hasSpecicalAttack = true
break
end
if hasSpecicalAttack
score += 20
elsif skill>=PBTrainerAI.highSkill
score -= 90
end
end
end
else
score += 10 if user.turnCount==0
score += 30 if user.stages[PBStats::SPATK]<0
if skill>=PBTrainerAI.mediumSkill
hasSpecicalAttack = false
user.eachMove do |m|
next if !m.specialMove?(m.type)
hasSpecicalAttack = true
break
end
score += 30 if hasSpecicalAttack
end
end
#---------------------------------------------------------------------------
when "03A"
if user.statStageAtMax?(PBStats::ATTACK) ||
user.hp<=user.totalhp/2
score -= 100
else
score += (6-user.stages[PBStats::ATTACK])*10
if skill>=PBTrainerAI.mediumSkill
hasPhysicalAttack = false
user.eachMove do |m|
next if !m.physicalMove?(m.type)
hasPhysicalAttack = true
break
end
if hasPhysicalAttack
score += 40
elsif skill>=PBTrainerAI.highSkill
score -= 90
end
end
end
#---------------------------------------------------------------------------
when "03B"
avg = user.stages[PBStats::ATTACK]*10
avg += user.stages[PBStats::DEFENSE]*10
score += avg/2
#---------------------------------------------------------------------------
when "03C"
avg = user.stages[PBStats::DEFENSE]*10
avg += user.stages[PBStats::SPDEF]*10
score += avg/2
#---------------------------------------------------------------------------
when "03D"
avg = user.stages[PBStats::DEFENSE]*10
avg += user.stages[PBStats::SPEED]*10
avg += user.stages[PBStats::SPDEF]*10
score += (avg/3).floor
#---------------------------------------------------------------------------
when "03E"
score += user.stages[PBStats::SPEED]*10
#---------------------------------------------------------------------------
when "03F"
score += user.stages[PBStats::SPATK]*10
#---------------------------------------------------------------------------
end
return score
end
end

View File

@@ -0,0 +1,797 @@
class PokeBattle_AI
alias __b__pbGetMoveScoreFunctionCode pbGetMoveScoreFunctionCode
#=============================================================================
# Get a score for the given move based on its effect
#=============================================================================
def pbGetMoveScoreFunctionCode(score,move,user,target,skill=100)
score = __b__pbGetMoveScoreFunctionCode(score,move,user,target,skill)
case move.function
#---------------------------------------------------------------------------
when "040"
if !target.pbCanConfuse?(user,false)
score -= 90
else
score += 30 if target.stages[PBStats::SPATK]<0
end
#---------------------------------------------------------------------------
when "041"
if !target.pbCanConfuse?(user,false)
score -= 90
else
score += 30 if target.stages[PBStats::ATTACK]<0
end
#---------------------------------------------------------------------------
when "042"
if move.statusMove?
if !target.pbCanLowerStatStage?(PBStats::ATTACK,user)
score -= 90
else
score += target.stages[PBStats::ATTACK]*20
if skill>=PBTrainerAI.mediumSkill
hasPhysicalAttack = false
target.eachMove do |m|
next if !m.physicalMove?(m.type)
hasPhysicalAttack = true
break
end
if hasPhysicalAttack
score += 20
elsif skill>=PBTrainerAI.highSkill
score -= 90
end
end
end
else
score += 20 if target.stages[PBStats::ATTACK]>0
if skill>=PBTrainerAI.mediumSkill
hasPhysicalAttack = false
target.eachMove do |m|
next if !m.physicalMove?(m.type)
hasPhysicalAttack = true
break
end
score += 20 if hasPhysicalAttack
end
end
#---------------------------------------------------------------------------
when "043"
if move.statusMove?
if !target.pbCanLowerStatStage?(PBStats::DEFENSE,user)
score -= 90
else
score += target.stages[PBStats::DEFENSE]*20
end
else
score += 20 if target.stages[PBStats::DEFENSE]>0
end
#---------------------------------------------------------------------------
when "044"
if move.statusMove?
if !target.pbCanLowerStatStage?(PBStats::SPEED,user)
score -= 90
else
score += target.stages[PBStats::SPEED]*10
if skill>=PBTrainerAI.highSkill
aspeed = pbRoughStat(user,PBStats::SPEED,skill)
ospeed = pbRoughStat(target,PBStats::SPEED,skill)
score += 30 if aspeed<ospeed && aspeed*2>ospeed
end
end
else
score += 20 if user.stages[PBStats::SPEED]>0
end
#---------------------------------------------------------------------------
when "045"
if move.statusMove?
if !target.pbCanLowerStatStage?(PBStats::SPATK,user)
score -= 90
else
score += user.stages[PBStats::SPATK]*20
if skill>=PBTrainerAI.mediumSkill
hasSpecicalAttack = false
target.eachMove do |m|
next if !m.specialMove?(m.type)
hasSpecicalAttack = true
break
end
if hasSpecicalAttack
score += 20
elsif skill>=PBTrainerAI.highSkill
score -= 90
end
end
end
else
score += 20 if user.stages[PBStats::SPATK]>0
if skill>=PBTrainerAI.mediumSkill
hasSpecicalAttack = false
target.eachMove do |m|
next if !m.specialMove?(m.type)
hasSpecicalAttack = true
break
end
score += 20 if hasSpecicalAttack
end
end
#---------------------------------------------------------------------------
when "046"
if move.statusMove?
if !target.pbCanLowerStatStage?(PBStats::SPDEF,user)
score -= 90
else
score += target.stages[PBStats::SPDEF]*20
end
else
score += 20 if target.stages[PBStats::SPDEF]>0
end
#---------------------------------------------------------------------------
when "047"
if move.statusMove?
if !target.pbCanLowerStatStage?(PBStats::ACCURACY,user)
score -= 90
else
score += target.stages[PBStats::ACCURACY]*10
end
else
score += 20 if target.stages[PBStats::ACCURACY]>0
end
#---------------------------------------------------------------------------
when "048"
if move.statusMove?
if !target.pbCanLowerStatStage?(PBStats::EVASION,user)
score -= 90
else
score += target.stages[PBStats::EVASION]*10
end
else
score += 20 if target.stages[PBStats::EVASION]>0
end
#---------------------------------------------------------------------------
when "049"
if move.statusMove?
if !target.pbCanLowerStatStage?(PBStats::EVASION,user)
score -= 90
else
score += target.stages[PBStats::EVASION]*10
end
else
score += 20 if target.stages[PBStats::EVASION]>0
end
score += 30 if target.pbOwnSide.effects[PBEffects::AuroraVeil]>0 ||
target.pbOwnSide.effects[PBEffects::Reflect]>0 ||
target.pbOwnSide.effects[PBEffects::LightScreen]>0 ||
target.pbOwnSide.effects[PBEffects::Mist]>0 ||
target.pbOwnSide.effects[PBEffects::Safeguard]>0
score -= 30 if target.pbOwnSide.effects[PBEffects::Spikes]>0 ||
target.pbOwnSide.effects[PBEffects::ToxicSpikes]>0 ||
target.pbOwnSide.effects[PBEffects::StealthRock]
#---------------------------------------------------------------------------
when "04A"
avg = target.stages[PBStats::ATTACK]*10
avg += target.stages[PBStats::DEFENSE]*10
score += avg/2
#---------------------------------------------------------------------------
when "04B"
if move.statusMove?
if !target.pbCanLowerStatStage?(PBStats::ATTACK,user)
score -= 90
else
score += 40 if user.turnCount==0
score += target.stages[PBStats::ATTACK]*20
if skill>=PBTrainerAI.mediumSkill
hasPhysicalAttack = false
target.eachMove do |m|
next if !m.physicalMove?(m.type)
hasPhysicalAttack = true
break
end
if hasPhysicalAttack
score += 20
elsif skill>=PBTrainerAI.highSkill
score -= 90
end
end
end
else
score += 10 if user.turnCount==0
score += 20 if target.stages[PBStats::ATTACK]>0
if skill>=PBTrainerAI.mediumSkill
hasPhysicalAttack = false
target.eachMove do |m|
next if !m.physicalMove?(m.type)
hasPhysicalAttack = true
break
end
score += 20 if hasPhysicalAttack
end
end
#---------------------------------------------------------------------------
when "04C"
if move.statusMove?
if !target.pbCanLowerStatStage?(PBStats::DEFENSE,user)
score -= 90
else
score += 40 if user.turnCount==0
score += target.stages[PBStats::DEFENSE]*20
end
else
score += 10 if user.turnCount==0
score += 20 if target.stages[PBStats::DEFENSE]>0
end
#---------------------------------------------------------------------------
when "04D"
if move.statusMove?
if !target.pbCanLowerStatStage?(PBStats::SPEED,user)
score -= 90
else
score += 20 if user.turnCount==0
score += target.stages[PBStats::SPEED]*20
if skill>=PBTrainerAI.highSkill
aspeed = pbRoughStat(user,PBStats::SPEED,skill)
ospeed = pbRoughStat(target,PBStats::SPEED,skill)
score += 30 if aspeed<ospeed && aspeed*2>ospeed
end
end
else
score += 10 if user.turnCount==0
score += 30 if target.stages[PBStats::SPEED]>0
end
#---------------------------------------------------------------------------
when "04E"
if user.gender==2 || target.gender==2 || user.gender==target.gender ||
target.hasActiveAbility?(:OBLIVIOUS)
score -= 90
elsif move.statusMove?
if !target.pbCanLowerStatStage?(PBStats::SPATK,user)
score -= 90
else
score += 40 if user.turnCount==0
score += target.stages[PBStats::SPATK]*20
if skill>=PBTrainerAI.mediumSkill
hasSpecicalAttack = false
target.eachMove do |m|
next if !m.specialMove?(m.type)
hasSpecicalAttack = true
break
end
if hasSpecicalAttack
score += 20
elsif skill>=PBTrainerAI.highSkill
score -= 90
end
end
end
else
score += 10 if user.turnCount==0
score += 20 if target.stages[PBStats::SPATK]>0
if skill>=PBTrainerAI.mediumSkill
hasSpecicalAttack = false
target.eachMove do |m|
next if !m.specialMove?(m.type)
hasSpecicalAttack = true
break
end
score += 30 if hasSpecicalAttack
end
end
#---------------------------------------------------------------------------
when "04F"
if move.statusMove?
if !target.pbCanLowerStatStage?(PBStats::SPDEF,user)
score -= 90
else
score += 40 if user.turnCount==0
score += target.stages[PBStats::SPDEF]*20
end
else
score += 10 if user.turnCount==0
score += 20 if target.stages[PBStats::SPDEF]>0
end
#---------------------------------------------------------------------------
when "050"
if target.effects[PBEffects::Substitute]>0
score -= 90
else
avg = 0; anyChange = false
PBStats.eachBattleStat do |s|
next if target.stages[s]==0
avg += target.stages[s]
anyChange = true
end
if anyChange
score += avg*10
else
score -= 90
end
end
#---------------------------------------------------------------------------
when "051"
if skill>=PBTrainerAI.mediumSkill
stages = 0
@battle.eachBattler do |b|
totalStages = 0
PBStats.eachBattleStat { |s| totalStages += b.stages[s] }
if b.opposes?(user)
stages += totalStages
else
stages -= totalStages
end
end
score += stages*10
end
#---------------------------------------------------------------------------
when "052"
if skill>=PBTrainerAI.mediumSkill
aatk = user.stages[PBStats::ATTACK]
aspa = user.stages[PBStats::SPATK]
oatk = target.stages[PBStats::ATTACK]
ospa = target.stages[PBStats::SPATK]
if aatk>=oatk && aspa>=ospa
score -= 80
else
score += (oatk-aatk)*10
score += (ospa-aspa)*10
end
else
score -= 50
end
#---------------------------------------------------------------------------
when "053"
if skill>=PBTrainerAI.mediumSkill
adef = user.stages[PBStats::DEFENSE]
aspd = user.stages[PBStats::SPDEF]
odef = target.stages[PBStats::DEFENSE]
ospd = target.stages[PBStats::SPDEF]
if adef>=odef && aspd>=ospd
score -= 80
else
score += (odef-adef)*10
score += (ospd-aspd)*10
end
else
score -= 50
end
#---------------------------------------------------------------------------
when "054"
if skill>=PBTrainerAI.mediumSkill
userStages = 0; targetStages = 0
PBStats.eachBattleStat do |s|
userStages += user.stages[s]
targetStages += target.stages[s]
end
score += (targetStages-userStages)*10
else
score -= 50
end
#---------------------------------------------------------------------------
when "055"
if skill>=PBTrainerAI.mediumSkill
equal = true
PBStats.eachBattleStat do |s|
stagediff = target.stages[s]-user.stages[s]
score += stagediff*10
equal = false if stagediff!=0
end
score -= 80 if equal
else
score -= 50
end
#---------------------------------------------------------------------------
when "056"
score -= 80 if user.pbOwnSide.effects[PBEffects::Mist]>0
#---------------------------------------------------------------------------
when "057"
if skill>=PBTrainerAI.mediumSkill
aatk = pbRoughStat(user,PBStats::ATTACK,skill)
adef = pbRoughStat(user,PBStats::DEFENSE,skill)
if aatk==adef ||
user.effects[PBEffects::PowerTrick] # No flip-flopping
score -= 90
elsif adef>aatk # Prefer a higher Attack
score += 30
else
score -= 30
end
else
score -= 30
end
#---------------------------------------------------------------------------
when "058"
if skill>=PBTrainerAI.mediumSkill
aatk = pbRoughStat(user,PBStats::ATTACK,skill)
aspatk = pbRoughStat(user,PBStats::SPATK,skill)
oatk = pbRoughStat(target,PBStats::ATTACK,skill)
ospatk = pbRoughStat(target,PBStats::SPATK,skill)
if aatk<oatk && aspatk<ospatk
score += 50
elsif aatk+aspatk<oatk+ospatk
score += 30
else
score -= 50
end
else
score -= 30
end
#---------------------------------------------------------------------------
when "059"
if skill>=PBTrainerAI.mediumSkill
adef = pbRoughStat(user,PBStats::DEFENSE,skill)
aspdef = pbRoughStat(user,PBStats::SPDEF,skill)
odef = pbRoughStat(target,PBStats::DEFENSE,skill)
ospdef = pbRoughStat(target,PBStats::SPDEF,skill)
if adef<odef && aspdef<ospdef
score += 50
elsif adef+aspdef<odef+ospdef
score += 30
else
score -= 50
end
else
score -= 30
end
#---------------------------------------------------------------------------
when "05A"
if target.effects[PBEffects::Substitute]>0
score -= 90
elsif user.hp>=(user.hp+target.hp)/2
score -= 90
else
score += 40
end
#---------------------------------------------------------------------------
when "05B"
score -= 90 if user.pbOwnSide.effects[PBEffects::Tailwind]>0
#---------------------------------------------------------------------------
when "05C"
moveBlacklist = [
"002", # Struggle
"014", # Chatter
"05C", # Mimic
"05D", # Sketch
"0B6" # Metronome
]
lastMoveData = pbGetMoveData(target.lastRegularMoveUsed)
if user.effects[PBEffects::Transform] ||
target.lastRegularMoveUsed<=0 ||
moveBlacklist.include?(lastMoveData[MOVE_FUNCTION_CODE]) ||
isConst?(lastMoveData[MOVE_TYPE],PBTypes,:SHADOW)
score -= 90
end
user.eachMove do |m|
next if m.id!=target.lastRegularMoveUsed
score -= 90
break
end
#---------------------------------------------------------------------------
when "05D"
moveBlacklist = [
"002", # Struggle
"014", # Chatter
"05D" # Sketch
]
lastMoveData = pbGetMoveData(target.lastRegularMoveUsed)
if user.effects[PBEffects::Transform] ||
target.lastRegularMoveUsed<=0 ||
moveBlacklist.include?(lastMoveData[MOVE_FUNCTION_CODE]) ||
isConst?(lastMoveData[MOVE_TYPE],PBTypes,:SHADOW)
score -= 90
end
user.eachMove do |m|
next if m.id!=target.lastRegularMoveUsed
score -= 90 # User already knows the move that will be Sketched
break
end
#---------------------------------------------------------------------------
when "05E"
if isConst?(user.ability,PBAbilities,:MULTITYPE) ||
isConst?(user.ability,PBAbilities,:RKSSYSTEM)
score -= 90
else
types = []
user.eachMove do |m|
next if m.id==@id
next if PBTypes.isPseudoType?(m.type)
next if user.pbHasType?(m.type)
types.push(m.type) if !types.include?(m.type)
end
score -= 90 if types.length==0
end
#---------------------------------------------------------------------------
when "05F"
if isConst?(user.ability,PBAbilities,:MULTITYPE) ||
isConst?(user.ability,PBAbilities,:RKSSYSTEM)
score -= 90
elsif target.lastMoveUsed<=0 ||
PBTypes.isPseudoType?(pbGetMoveData(target.lastMoveUsed,MOVE_TYPE))
score -= 90
else
aType = -1
target.eachMove do |m|
next if m.id!=target.lastMoveUsed
aType = m.pbCalcType(user)
break
end
if aType<0
score -= 90
else
types = []
for i in 0..PBTypes.maxValue
next if user.pbHasType?(i)
types.push(i) if PBTypes.resistant?(aType,i)
end
score -= 90 if types.length==0
end
end
#---------------------------------------------------------------------------
when "060"
if isConst?(user.ability,PBAbilities,:MULTITYPE) ||
isConst?(user.ability,PBAbilities,:RKSSYSTEM)
score -= 90
elsif skill>=PBTrainerAI.mediumSkill
envtypes = [
:NORMAL, # None
:GRASS, # Grass
:GRASS, # Tall grass
:WATER, # Moving water
:WATER, # Still water
:WATER, # Underwater
:ROCK, # Rock
:ROCK, # Cave
:GROUND # Sand
]
type = envtypes[@environment]
score -= 90 if user.pbHasType?(type)
end
#---------------------------------------------------------------------------
when "061"
if target.effects[PBEffects::Substitute]>0 ||
isConst?(target.ability,PBAbilities,:MULTITYPE) ||
isConst?(target.ability,PBAbilities,:RKSSYSTEM)
score -= 90
elsif target.pbHasType?(:WATER)
score -= 90
end
#---------------------------------------------------------------------------
when "062"
if isConst?(user.ability,PBAbilities,:MULTITYPE) ||
isConst?(user.ability,PBAbilities,:RKSSYSTEM)
score -= 90
elsif user.pbHasType?(target.type1) &&
user.pbHasType?(target.type2) &&
target.pbHasType?(user.type1) &&
target.pbHasType?(user.type2)
score -= 90
end
#---------------------------------------------------------------------------
when "063"
if target.effects[PBEffects::Substitute]>0
score -= 90
elsif skill>=PBTrainerAI.mediumSkill
if isConst?(target.ability,PBAbilities,:MULTITYPE) ||
isConst?(target.ability,PBAbilities,:RKSSYSTEM) ||
isConst?(target.ability,PBAbilities,:SIMPLE) ||
isConst?(target.ability,PBAbilities,:TRUANT)
score -= 90
end
end
#---------------------------------------------------------------------------
when "064"
if target.effects[PBEffects::Substitute]>0
score -= 90
elsif skill>=PBTrainerAI.mediumSkill
if isConst?(target.ability,PBAbilities,:INSOMNIA) ||
isConst?(target.ability,PBAbilities,:MULTITYPE) ||
isConst?(target.ability,PBAbilities,:RKSSYSTEM) ||
isConst?(target.ability,PBAbilities,:TRUANT)
score -= 90
end
end
#---------------------------------------------------------------------------
when "065"
score -= 40 # don't prefer this move
if skill>=PBTrainerAI.mediumSkill
if target.ability==0 || user.ability==target.ability ||
isConst?(user.ability,PBAbilities,:MULTITYPE) ||
isConst?(user.ability,PBAbilities,:RKSSYSTEM) ||
isConst?(target.ability,PBAbilities,:FLOWERGIFT) ||
isConst?(target.ability,PBAbilities,:FORECAST) ||
isConst?(target.ability,PBAbilities,:ILLUSION) ||
isConst?(target.ability,PBAbilities,:IMPOSTER) ||
isConst?(target.ability,PBAbilities,:MULTITYPE) ||
isConst?(target.ability,PBAbilities,:RKSSYSTEM) ||
isConst?(target.ability,PBAbilities,:TRACE) ||
isConst?(target.ability,PBAbilities,:WONDERGUARD) ||
isConst?(target.ability,PBAbilities,:ZENMODE)
score -= 90
end
end
if skill>=PBTrainerAI.highSkill
if isConst?(target.ability,PBAbilities,:TRUANT) &&
user.opposes?(target)
score -= 90
elsif isConst?(target.ability,PBAbilities,:SLOWSTART) &&
user.opposes?(target)
score -= 90
end
end
#---------------------------------------------------------------------------
when "066"
score -= 40 # don't prefer this move
if target.effects[PBEffects::Substitute]>0
score -= 90
elsif skill>=PBTrainerAI.mediumSkill
if user.ability==0 || user.ability==target.ability ||
isConst?(target.ability,PBAbilities,:MULTITYPE) ||
isConst?(target.ability,PBAbilities,:RKSSYSTEM) ||
isConst?(target.ability,PBAbilities,:TRUANT) ||
isConst?(user.ability,PBAbilities,:FLOWERGIFT) ||
isConst?(user.ability,PBAbilities,:FORECAST) ||
isConst?(user.ability,PBAbilities,:ILLUSION) ||
isConst?(user.ability,PBAbilities,:IMPOSTER) ||
isConst?(user.ability,PBAbilities,:MULTITYPE) ||
isConst?(user.ability,PBAbilities,:RKSSYSTEM) ||
isConst?(user.ability,PBAbilities,:TRACE) ||
isConst?(user.ability,PBAbilities,:ZENMODE)
score -= 90
end
if skill>=PBTrainerAI.highSkill
if isConst?(user.ability,PBAbilities,:TRUANT) &&
user.opposes?(target)
score += 90
elsif isConst?(user.ability,PBAbilities,:SLOWSTART) &&
user.opposes?(target)
score += 90
end
end
end
#---------------------------------------------------------------------------
when "067"
score -= 40 # don't prefer this move
if skill>=PBTrainerAI.mediumSkill
if (user.ability==0 && target.ability==0) ||
user.ability==target.ability ||
isConst?(user.ability,PBAbilities,:ILLUSION) ||
isConst?(user.ability,PBAbilities,:MULTITYPE) ||
isConst?(user.ability,PBAbilities,:RKSSYSTEM) ||
isConst?(user.ability,PBAbilities,:WONDERGUARD) ||
isConst?(target.ability,PBAbilities,:ILLUSION) ||
isConst?(target.ability,PBAbilities,:MULTITYPE) ||
isConst?(target.ability,PBAbilities,:RKSSYSTEM) ||
isConst?(target.ability,PBAbilities,:WONDERGUARD)
score -= 90
end
end
if skill>=PBTrainerAI.highSkill
if isConst?(target.ability,PBAbilities,:TRUANT) &&
user.opposes?(target)
score -= 90
elsif isConst?(target.ability,PBAbilities,:SLOWSTART) &&
user.opposes?(target)
score -= 90
end
end
#---------------------------------------------------------------------------
when "068"
if target.effects[PBEffects::Substitute]>0 ||
target.effects[PBEffects::GastroAcid]
score -= 90
elsif skill>=PBTrainerAI.highSkill
score -= 90 if isConst?(target.ability,PBAbilities,:MULTITYPE)
score -= 90 if isConst?(target.ability,PBAbilities,:RKSSYSTEM)
score -= 90 if isConst?(target.ability,PBAbilities,:SLOWSTART)
score -= 90 if isConst?(target.ability,PBAbilities,:TRUANT)
end
#---------------------------------------------------------------------------
when "069"
score -= 70
#---------------------------------------------------------------------------
when "06A"
if target.hp<=20
score += 80
elsif target.level>=25
score -= 60 # Not useful against high-level Pokemon
end
#---------------------------------------------------------------------------
when "06B"
score += 80 if target.hp<=40
#---------------------------------------------------------------------------
when "06C"
score -= 50
score += target.hp*100/target.totalhp
#---------------------------------------------------------------------------
when "06D"
score += 80 if target.hp<=user.level
#---------------------------------------------------------------------------
when "06E"
if user.hp>=target.hp
score -= 90
elsif user.hp<target.hp/2
score += 50
end
#---------------------------------------------------------------------------
when "06F"
score += 30 if target.hp<=user.level
#---------------------------------------------------------------------------
when "070"
score -= 90 if target.hasActiveAbility?(:STURDY)
score -= 90 if target.level>user.level
#---------------------------------------------------------------------------
when "071"
if target.effects[PBEffects::HyperBeam]>0
score -= 90
else
attack = pbRoughStat(user,PBStats::ATTACK,skill)
spatk = pbRoughStat(user,PBStats::SPATK,skill)
if attack*1.5<spatk
score -= 60
elsif skill>=PBTrainerAI.mediumSkill && target.lastMoveUsed>0
moveData = pbGetMoveData(target.lastMoveUsed)
if moveData[MOVE_BASE_DAMAGE]>0 &&
(MOVE_CATEGORY_PER_MOVE && moveData[MOVE_CATEGORY]==0) ||
(!MOVE_CATEGORY_PER_MOVE && PBTypes.isPhysicalType?(moveData[MOVE_TYPE]))
score -= 60
end
end
end
#---------------------------------------------------------------------------
when "072"
if target.effects[PBEffects::HyperBeam]>0
score -= 90
else
attack = pbRoughStat(user,PBStats::ATTACK,skill)
spatk = pbRoughStat(user,PBStats::SPATK,skill)
if attack>spatk*1.5
score -= 60
elsif skill>=PBTrainerAI.mediumSkill && target.lastMoveUsed>0
moveData = pbGetMoveData(target.lastMoveUsed)
if moveData[MOVE_BASE_DAMAGE]>0 &&
(MOVE_CATEGORY_PER_MOVE && moveData[MOVE_CATEGORY]==1) ||
(!MOVE_CATEGORY_PER_MOVE && !PBTypes.isSpecialType?(moveData[MOVE_TYPE]))
score -= 60
end
end
end
#---------------------------------------------------------------------------
when "073"
score -= 90 if target.effects[PBEffects::HyperBeam]>0
#---------------------------------------------------------------------------
when "074"
target.eachAlly do |b|
next if !b.near?(target)
score += 10
end
#---------------------------------------------------------------------------
when "075"
#---------------------------------------------------------------------------
when "076"
#---------------------------------------------------------------------------
when "077"
#---------------------------------------------------------------------------
when "078"
if skill>=PBTrainerAI.highSkill
score += 30 if !target.hasActiveAbility?(:INNERFOCUS) &&
target.effects[PBEffects::Substitute]==0
end
#---------------------------------------------------------------------------
when "079"
#---------------------------------------------------------------------------
when "07A"
#---------------------------------------------------------------------------
when "07B"
#---------------------------------------------------------------------------
when "07C"
score -= 20 if target.status==PBStatuses::PARALYSIS # Will cure status
#---------------------------------------------------------------------------
when "07D"
score -= 20 if target.status==PBStatuses::SLEEP && # Will cure status
target.statusCount>1
#---------------------------------------------------------------------------
when "07E"
#---------------------------------------------------------------------------
when "07F"
#---------------------------------------------------------------------------
end
return score
end
end

View File

@@ -0,0 +1,228 @@
class PokeBattle_AI
alias __c__pbGetMoveScoreFunctionCode pbGetMoveScoreFunctionCode
#=============================================================================
# Get a score for the given move based on its effect
#=============================================================================
def pbGetMoveScoreFunctionCode(score,move,user,target,skill=100)
score = __c__pbGetMoveScoreFunctionCode(score,move,user,target,skill)
case move.function
#---------------------------------------------------------------------------
when "080"
#---------------------------------------------------------------------------
when "081"
attspeed = pbRoughStat(user,PBStats::SPEED,skill)
oppspeed = pbRoughStat(target,PBStats::SPEED,skill)
score += 30 if oppspeed>attspeed
#---------------------------------------------------------------------------
when "082"
score += 20 if @battle.pbOpposingBattlerCount(user)>1
#---------------------------------------------------------------------------
when "083"
if skill>=PBTrainerAI.mediumSkill
user.eachAlly do |b|
next if !b.pbHasMove?(move.id)
score += 20
end
end
#---------------------------------------------------------------------------
when "084"
attspeed = pbRoughStat(user,PBStats::SPEED,skill)
oppspeed = pbRoughStat(target,PBStats::SPEED,skill)
score += 30 if oppspeed>attspeed
#---------------------------------------------------------------------------
when "085"
#---------------------------------------------------------------------------
when "086"
#---------------------------------------------------------------------------
when "087"
#---------------------------------------------------------------------------
when "088"
#---------------------------------------------------------------------------
when "089"
#---------------------------------------------------------------------------
when "08A"
#---------------------------------------------------------------------------
when "08B"
#---------------------------------------------------------------------------
when "08C"
#---------------------------------------------------------------------------
when "08D"
#---------------------------------------------------------------------------
when "08E"
#---------------------------------------------------------------------------
when "08F"
#---------------------------------------------------------------------------
when "090"
#---------------------------------------------------------------------------
when "091"
#---------------------------------------------------------------------------
when "092"
#---------------------------------------------------------------------------
when "093"
score += 25 if user.effects[PBEffects::Rage]
#---------------------------------------------------------------------------
when "094"
#---------------------------------------------------------------------------
when "095"
#---------------------------------------------------------------------------
when "096"
score -= 90 if !pbIsBerry?(user.item) || !user.itemActive?
#---------------------------------------------------------------------------
when "097"
#---------------------------------------------------------------------------
when "098"
#---------------------------------------------------------------------------
when "099"
#---------------------------------------------------------------------------
when "09A"
#---------------------------------------------------------------------------
when "09B"
#---------------------------------------------------------------------------
when "09C"
hasAlly = false
user.eachAlly do |b|
hasAlly = true
score += 30
break
end
score -= 90 if !hasAlly
#---------------------------------------------------------------------------
when "09D"
score -= 90 if user.effects[PBEffects::MudSport]
#---------------------------------------------------------------------------
when "09E"
score -= 90 if user.effects[PBEffects::WaterSport]
#---------------------------------------------------------------------------
when "09F"
#---------------------------------------------------------------------------
when "0A0"
#---------------------------------------------------------------------------
when "0A1"
score -= 90 if user.pbOwnSide.effects[PBEffects::LuckyChant]>0
#---------------------------------------------------------------------------
when "0A2"
score -= 90 if user.pbOwnSide.effects[PBEffects::Reflect]>0
#---------------------------------------------------------------------------
when "0A3"
score -= 90 if user.pbOwnSide.effects[PBEffects::LightScreen]>0
#---------------------------------------------------------------------------
when "0A4"
#---------------------------------------------------------------------------
when "0A5"
#---------------------------------------------------------------------------
when "0A6"
score -= 90 if target.effects[PBEffects::Substitute]>0
score -= 90 if user.effects[PBEffects::LockOn]>0
#---------------------------------------------------------------------------
when "0A7"
if target.effects[PBEffects::Foresight]
score -= 90
elsif target.pbHasType?(:GHOST)
score += 70
elsif target.stages[PBStats::EVASION]<=0
score -= 60
end
#---------------------------------------------------------------------------
when "0A8"
if target.effects[PBEffects::MiracleEye]
score -= 90
elsif target.pbHasType?(:DARK)
score += 70
elsif target.stages[PBStats::EVASION]<=0
score -= 60
end
#---------------------------------------------------------------------------
when "0A9"
#---------------------------------------------------------------------------
when "0AA"
if user.effects[PBEffects::ProtectRate]>1 ||
target.effects[PBEffects::HyperBeam]>0
score -= 90
else
if skill>=PBTrainerAI.mediumSkill
score -= user.effects[PBEffects::ProtectRate]*40
end
score += 50 if user.turnCount==0
score += 30 if target.effects[PBEffects::TwoTurnAttack]>0
end
#---------------------------------------------------------------------------
when "0AB"
#---------------------------------------------------------------------------
when "0AC"
#---------------------------------------------------------------------------
when "0AD"
#---------------------------------------------------------------------------
when "0AE"
score -= 40
if skill>=PBTrainerAI.highSkill
score -= 100 if target.lastRegularMoveUsed<=0 ||
!pbGetMoveData(target.lastRegularMoveUsed,MOVE_FLAGS)[/e/] # Not copyable by Mirror Move
end
#---------------------------------------------------------------------------
when "0AF"
#---------------------------------------------------------------------------
when "0B0"
#---------------------------------------------------------------------------
when "0B1"
#---------------------------------------------------------------------------
when "0B2"
#---------------------------------------------------------------------------
when "0B3"
#---------------------------------------------------------------------------
when "0B4"
if user.asleep?
score += 100 # Because it can only be used while asleep
else
score -= 90
end
#---------------------------------------------------------------------------
when "0B5"
#---------------------------------------------------------------------------
when "0B6"
#---------------------------------------------------------------------------
when "0B7"
score -= 90 if target.effects[PBEffects::Torment]
#---------------------------------------------------------------------------
when "0B8"
score -= 90 if user.effects[PBEffects::Imprison]
#---------------------------------------------------------------------------
when "0B9"
score -= 90 if target.effects[PBEffects::Disable]>0
#---------------------------------------------------------------------------
when "0BA"
score -= 90 if target.effects[PBEffects::Taunt]>0
#---------------------------------------------------------------------------
when "0BB"
score -= 90 if target.effects[PBEffects::HealBlock]>0
#---------------------------------------------------------------------------
when "0BC"
aspeed = pbRoughStat(user,PBStats::SPEED,skill)
ospeed = pbRoughStat(target,PBStats::SPEED,skill)
if target.effects[PBEffects::Encore]>0
score -= 90
elsif aspeed>ospeed
if target.lastMoveUsed<=0
score -= 90
else
moveData = pbGetMoveData(target.lastRegularMoveUsed)
if moveData[MOVE_CATEGORY]==2 && # Status move
(moveData[MOVE_TARGET]==PBTargets::User ||
moveData[MOVE_TARGET]==PBTargets::BothSides)
score += 60
elsif moveData[MOVE_CATEGORY]!=2 && # Damaging move
moveData[MOVE_TARGET]==PBTargets::NearOther &&
PBTypes.ineffective?(pbCalcTypeMod(moveData[MOVE_TYPE],target,user))
score += 60
end
end
end
#---------------------------------------------------------------------------
when "0BD"
#---------------------------------------------------------------------------
when "0BF"
#---------------------------------------------------------------------------
end
return score
end
end

View File

@@ -0,0 +1,375 @@
class PokeBattle_AI
alias __d__pbGetMoveScoreFunctionCode pbGetMoveScoreFunctionCode
#=============================================================================
# Get a score for the given move based on its effect
#=============================================================================
def pbGetMoveScoreFunctionCode(score,move,user,target,skill=100)
score = __d__pbGetMoveScoreFunctionCode(score,move,user,target,skill)
case move.function
#---------------------------------------------------------------------------
when "0C0"
#---------------------------------------------------------------------------
when "0C1"
#---------------------------------------------------------------------------
when "0C2"
#---------------------------------------------------------------------------
when "0C3"
#---------------------------------------------------------------------------
when "0C4"
#---------------------------------------------------------------------------
when "0C7"
score += 20 if user.effects[PBEffects::FocusEnergy]>0
if skill>=PBTrainerAI.highSkill
score += 20 if !target.hasActiveAbility?(:INNERFOCUS) &&
target.effects[PBEffects::Substitute]==0
end
#---------------------------------------------------------------------------
when "0C9"
#---------------------------------------------------------------------------
when "0CA"
#---------------------------------------------------------------------------
when "0CB"
#---------------------------------------------------------------------------
when "0CC"
#---------------------------------------------------------------------------
when "0CD"
#---------------------------------------------------------------------------
when "0CE"
#---------------------------------------------------------------------------
when "0CF"
score += 40 if target.effects[PBEffects::Trapping]==0
#---------------------------------------------------------------------------
when "0D0"
score += 40 if target.effects[PBEffects::Trapping]==0
#---------------------------------------------------------------------------
when "0D1"
#---------------------------------------------------------------------------
when "0D2"
#---------------------------------------------------------------------------
when "0D3"
#---------------------------------------------------------------------------
when "0D4"
if user.hp<=user.totalhp/4
score -= 90
elsif user.hp<=user.totalhp/2
score -= 50
end
#---------------------------------------------------------------------------
when "0D5", "0D6"
if user.hp==user.totalhp || (skill>=PBTrainerAI.mediumSkill && !user.canHeal?)
score -= 90
else
score += 50
score -= user.hp*100/user.totalhp
end
#---------------------------------------------------------------------------
when "0D7"
score -= 90 if @battle.positions[user.index].effects[PBEffects::Wish]>0
#---------------------------------------------------------------------------
when "0D8"
if user.hp==user.totalhp || (skill>=PBTrainerAI.mediumSkill && !user.canHeal?)
score -= 90
else
case @battle.pbWeather
when PBWeather::Sun, PBWeather::HarshSun
score += 30
when PBWeather::None
else
score -= 30
end
score += 50
score -= user.hp*100/user.totalhp
end
#---------------------------------------------------------------------------
when "0D9"
if user.hp==user.totalhp || !user.pbCanSleep?(user,false,nil,true)
score -= 90
else
score += 70
score -= user.hp*140/user.totalhp
score += 30 if user.status!=0
end
#---------------------------------------------------------------------------
when "0DA"
score -= 90 if user.effects[PBEffects::AquaRing]
#---------------------------------------------------------------------------
when "0DB"
score -= 90 if user.effects[PBEffects::Ingrain]
#---------------------------------------------------------------------------
when "0DC"
if target.effects[PBEffects::LeechSeed]>=0
score -= 90
elsif skill>=PBTrainerAI.mediumSkill && target.pbHasType?(:GRASS)
score -= 90
else
score += 60 if user.turnCount==0
end
#---------------------------------------------------------------------------
when "0DD"
if skill>=PBTrainerAI.highSkill && target.hasActiveAbility?(:LIQUIDOOZE)
score -= 70
else
score += 20 if user.hp<=user.totalhp/2
end
#---------------------------------------------------------------------------
when "0DE"
if !target.asleep?
score -= 100
elsif skill>=PBTrainerAI.highSkill && target.hasActiveAbility?(:LIQUIDOOZE)
score -= 70
else
score += 20 if user.hp<=user.totalhp/2
end
#---------------------------------------------------------------------------
when "0DF"
if user.opposes?(target)
score -= 100
else
score += 20 if target.hp<target.totalhp/2 &&
target.effects[PBEffects::Substitute]==0
end
#---------------------------------------------------------------------------
when "0E0"
reserves = @battle.pbAbleNonActiveCount(user.idxOwnSide)
foes = @battle.pbAbleNonActiveCount(user.idxOpposingSide)
if @battle.pbCheckGlobalAbility(:DAMP)
score -= 100
elsif skill>=PBTrainerAI.mediumSkill && reserves==0 && foes>0
score -= 100 # don't want to lose
elsif skill>=PBTrainerAI.highSkill && reserves==0 && foes==0
score += 80 # want to draw
else
score -= user.hp*100/user.totalhp
end
#---------------------------------------------------------------------------
when "0E1"
#---------------------------------------------------------------------------
when "0E2"
if !target.pbCanLowerStatStage?(PBStats::ATTACK,user) &&
!target.pbCanLowerStatStage?(PBStats::SPATK,user)
score -= 100
elsif @battle.pbAbleNonActiveCount(user.idxOwnSide)==0
score -= 100
else
score += target.stages[PBStats::ATTACK]*10
score += target.stages[PBStats::SPATK]*10
score -= user.hp*100/user.totalhp
end
#---------------------------------------------------------------------------
when "0E3", "0E4"
score -= 70
#---------------------------------------------------------------------------
when "0E5"
if @battle.pbAbleNonActiveCount(user.idxOwnSide)==0
score -= 90
else
score -= 90 if target.effects[PBEffects::PerishSong]>0
end
#---------------------------------------------------------------------------
when "0E6"
score += 50
score -= user.hp*100/user.totalhp
score += 30 if user.hp<=user.totalhp/10
#---------------------------------------------------------------------------
when "0E7"
score += 50
score -= user.hp*100/user.totalhp
score += 30 if user.hp<=user.totalhp/10
#---------------------------------------------------------------------------
when "0E8"
score -= 25 if user.hp>user.totalhp/2
if skill>=PBTrainerAI.mediumSkill
score -= 90 if user.effects[PBEffects::ProtectRate]>1
score -= 90 if target.effects[PBEffects::HyperBeam]>0
else
score -= user.effects[PBEffects::ProtectRate]*40
end
#---------------------------------------------------------------------------
when "0E9"
if target.hp==1
score -= 90
elsif target.hp<=target.totalhp/8
score -= 60
elsif target.hp<=target.totalhp/4
score -= 30
end
#---------------------------------------------------------------------------
when "0EA"
score -= 100 if @battle.trainerBattle?
#---------------------------------------------------------------------------
when "0EB"
if target.effects[PBEffects::Ingrain] ||
(skill>=PBTrainerAI.highSkill && target.hasActiveAbility?(:SUCTIONCUPS))
score -= 90
else
ch = 0
@battle.pbParty(target.index).each_with_index do |pkmn,i|
ch += 1 if @battle.pbCanSwitchLax?(target.index,i)
end
score -= 90 if ch==0
end
if score>20
score += 50 if target.pbOwnSide.effects[PBEffects::Spikes]>0
score += 50 if target.pbOwnSide.effects[PBEffects::ToxicSpikes]>0
score += 50 if target.pbOwnSide.effects[PBEffects::StealthRock]
end
#---------------------------------------------------------------------------
when "0EC"
if !target.effects[PBEffects::Ingrain] &&
!(skill>=PBTrainerAI.highSkill && target.hasActiveAbility?(:SUCTIONCUPS))
score += 40 if target.pbOwnSide.effects[PBEffects::Spikes]>0
score += 40 if target.pbOwnSide.effects[PBEffects::ToxicSpikes]>0
score += 40 if target.pbOwnSide.effects[PBEffects::StealthRock]
end
#---------------------------------------------------------------------------
when "0ED"
if !@battle.pbCanChooseNonActive?(user.index)
score -= 80
else
score -= 40 if user.effects[PBEffects::Confusion]>0
total = 0
PBStats.eachBattleStat { |s| total += user.stages[s] }
if total<=0 || user.turnCount==0
score -= 60
else
score += total*10
# special case: user has no damaging moves
hasDamagingMove = false
user.eachMove do |m|
next if !m.damagingMove?
hasDamagingMove = true
break
end
score += 75 if !hasDamagingMove
end
end
#---------------------------------------------------------------------------
when "0EE"
#---------------------------------------------------------------------------
when "0EF"
score -= 90 if target.effects[PBEffects::MeanLook]>=0
#---------------------------------------------------------------------------
when "0F0"
if skill>=PBTrainerAI.highSkill
score += 20 if target.item!=0
end
#---------------------------------------------------------------------------
when "0F1"
if skill>=PBTrainerAI.highSkill
if user.item==0 && target.item!=0
score += 40
else
score -= 90
end
else
score -= 80
end
#---------------------------------------------------------------------------
when "0F2"
if user.item==0 && target.item==0
score -= 90
elsif skill>=PBTrainerAI.highSkill && target.hasActiveAbility?(:STICKYHOLD)
score -= 90
elsif user.hasActiveItem?([:FLAMEORB,:TOXICORB,:STICKYBARB,:IRONBALL,
:CHOICEBAND,:CHOICESCARF,:CHOICESPECS])
score += 50
elsif user.item==0 && target.item!=0
score -= 30 if pbGetMoveData(user.lastMoveUsed,MOVE_FUNCTION_CODE)=="0F2" # Trick/Switcheroo
end
#---------------------------------------------------------------------------
when "0F3"
if user.item==0 || target.item!=0
score -= 90
else
if user.hasActiveItem?([:FLAMEORB,:TOXICORB,:STICKYBARB,:IRONBALL,
:CHOICEBAND,:CHOICESCARF,:CHOICESPECS])
score += 50
else
score -= 80
end
end
#---------------------------------------------------------------------------
when "0F4", "0F5"
if target.effects[PBEffects::Substitute]==0
if skill>=PBTrainerAI.highSkill && pbIsBerry?(target.item)
score += 30
end
end
#---------------------------------------------------------------------------
when "0F6"
if user.recycleItem==0 || user.item!=0
score -= 80
elsif user.recycleItem!=0
score += 30
end
#---------------------------------------------------------------------------
when "0F7"
if user.item==0 || !user.itemActive? ||
user.unlosableItem?(user.item) || pbIsPokeBall?(user.item)
score -= 90
end
#---------------------------------------------------------------------------
when "0F8"
score -= 90 if target.effects[PBEffects::Embargo]>0
#---------------------------------------------------------------------------
when "0F9"
if @battle.field.effects[PBEffects::MagicRoom]>0
score -= 90
else
score += 30 if user.item==0 && target.item!=0
end
#---------------------------------------------------------------------------
when "0FA"
score -= 25
#---------------------------------------------------------------------------
when "0FB"
score -= 30
#---------------------------------------------------------------------------
when "0FC"
score -= 40
#---------------------------------------------------------------------------
when "0FD"
score -= 30
if target.pbCanParalyze?(user,false)
score += 30
if skill>=PBTrainerAI.mediumSkill
aspeed = pbRoughStat(user,PBStats::SPEED,skill)
ospeed = pbRoughStat(target,PBStats::SPEED,skill)
if aspeed<ospeed
score += 30
elsif aspeed>ospeed
score -= 40
end
end
if skill>=PBTrainerAI.highSkill
score -= 40 if target.hasActiveAbility?([:GUTS,:MARVELSCALE,:QUICKFEET])
end
end
#---------------------------------------------------------------------------
when "0FE"
score -= 30
if target.pbCanBurn?(user,false)
score += 30
if skill>=PBTrainerAI.highSkill
score -= 40 if target.hasActiveAbility?([:GUTS,:MARVELSCALE,:QUICKFEET,:FLAREBOOST])
end
end
#---------------------------------------------------------------------------
when "0FF"
if @battle.pbCheckGlobalAbility(:AIRLOCK) ||
@battle.pbCheckGlobalAbility(:CLOUDNINE)
score -= 90
elsif @battle.pbWeather==PBWeather::Sun
score -= 90
else
user.eachMove do |m|
next if !m.damagingMove? || !isConst?(m.type,PBTypes,:FIRE)
score += 20
end
end
#---------------------------------------------------------------------------
end
return score
end
end

View File

@@ -0,0 +1,498 @@
class PokeBattle_AI
alias __e__pbGetMoveScoreFunctionCode pbGetMoveScoreFunctionCode
#=============================================================================
# Get a score for the given move based on its effect
#=============================================================================
def pbGetMoveScoreFunctionCode(score,move,user,target,skill=100)
score = __e__pbGetMoveScoreFunctionCode(score,move,user,target,skill)
case move.function
#---------------------------------------------------------------------------
when "100"
if @battle.pbCheckGlobalAbility(:AIRLOCK) ||
@battle.pbCheckGlobalAbility(:CLOUDNINE)
score -= 90
elsif @battle.pbWeather==PBWeather::Rain
score -= 90
else
user.eachMove do |m|
next if !m.damagingMove? || !isConst?(m.type,PBTypes,:WATER)
score += 20
end
end
#---------------------------------------------------------------------------
when "101"
if @battle.pbCheckGlobalAbility(:AIRLOCK) ||
@battle.pbCheckGlobalAbility(:CLOUDNINE)
score -= 90
elsif @battle.pbWeather==PBWeather::Sandstorm
score -= 90
end
#---------------------------------------------------------------------------
when "102"
if @battle.pbCheckGlobalAbility(:AIRLOCK) ||
@battle.pbCheckGlobalAbility(:CLOUDNINE)
score -= 90
elsif @battle.pbWeather==PBWeather::Hail
score -= 90
end
#---------------------------------------------------------------------------
when "103"
if user.pbOpposingSide.effects[PBEffects::Spikes]>=3
score -= 90
else
canChoose = false
user.eachOpposing do |b|
next if !@battle.pbCanChooseNonActive?(b.index)
canChoose = true
break
end
if !canChoose
# Opponent can't switch in any Pokemon
score -= 90
else
score += 10*@battle.pbAbleNonActiveCount(user.idxOpposingSide)
score += [40,26,13][user.pbOpposingSide.effects[PBEffects::Spikes]]
end
end
#---------------------------------------------------------------------------
when "104"
if user.pbOpposingSide.effects[PBEffects::ToxicSpikes]>=2
score -= 90
else
canChoose = false
user.eachOpposing do |b|
next if !@battle.pbCanChooseNonActive?(b.index)
canChoose = true
break
end
if !canChoose
# Opponent can't switch in any Pokemon
score -= 90
else
score += 8*@battle.pbAbleNonActiveCount(user.idxOpposingSide)
score += [26,13][user.pbOpposingSide.effects[PBEffects::ToxicSpikes]]
end
end
#---------------------------------------------------------------------------
when "105"
if user.pbOpposingSide.effects[PBEffects::StealthRock]
score -= 90
else
canChoose = false
user.eachOpposing do |b|
next if !@battle.pbCanChooseNonActive?(b.index)
canChoose = true
break
end
if !canChoose
# Opponent can't switch in any Pokemon
score -= 90
else
score += 10*@battle.pbAbleNonActiveCount(user.idxOpposingSide)
end
end
#---------------------------------------------------------------------------
when "106"
#---------------------------------------------------------------------------
when "107"
#---------------------------------------------------------------------------
when "108"
#---------------------------------------------------------------------------
when "109"
#---------------------------------------------------------------------------
when "10A"
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
#---------------------------------------------------------------------------
when "10B"
score += 10*(user.stages[PBStats::ACCURACY]-target.stages[PBStats::EVASION])
#---------------------------------------------------------------------------
when "10C"
if user.effects[PBEffects::Substitute]>0
score -= 90
elsif user.hp<=user.totalhp/4
score -= 90
end
#---------------------------------------------------------------------------
when "10D"
if user.pbHasType?(:GHOST)
if target.effects[PBEffects::Curse]
score -= 90
elsif user.hp<=user.totalhp/2
if @battle.pbAbleNonActiveCount(user.idxOwnSide)==0
score -= 90
else
score -= 50
score -= 30 if @battle.switchStyle
end
end
else
avg = user.stages[PBStats::SPEED]*10
avg -= user.stages[PBStats::ATTACK]*10
avg -= user.stages[PBStats::DEFENSE]*10
score += avg/3
end
#---------------------------------------------------------------------------
when "10E"
score -= 40
#---------------------------------------------------------------------------
when "10F"
if target.effects[PBEffects::Nightmare] ||
target.effects[PBEffects::Substitute]>0
score -= 90
elsif !target.asleep?
score -= 90
else
score -= 90 if target.statusCount<=1
score += 50 if target.statusCount>3
end
#---------------------------------------------------------------------------
when "110"
score += 30 if user.effects[PBEffects::Trapping]>0
score += 30 if user.effects[PBEffects::LeechSeed]>=0
if @battle.pbAbleNonActiveCount(user.idxOwnSide)>0
score += 80 if user.pbOwnSide.effects[PBEffects::Spikes]>0
score += 80 if user.pbOwnSide.effects[PBEffects::ToxicSpikes]>0
score += 80 if user.pbOwnSide.effects[PBEffects::StealthRock]
end
#---------------------------------------------------------------------------
when "111"
if @battle.positions[target.index].effects[PBEffects::FutureSightCounter]>0
score -= 100
elsif @battle.pbAbleNonActiveCount(user.idxOwnSide)==0
# Future Sight tends to be wasteful if down to last Pokemon
score -= 70
end
#---------------------------------------------------------------------------
when "112"
avg = 0
avg -= user.stages[PBStats::DEFENSE]*10
avg -= user.stages[PBStats::SPDEF]*10
score += avg/2
if user.effects[PBEffects::Stockpile]>=3
score -= 80
else
# More preferable if user also has Spit Up/Swallow
score += 20 if user.pbHasMoveFunction?("113","114") # Spit Up, Swallow
end
#---------------------------------------------------------------------------
when "113"
score -= 100 if user.effects[PBEffects::Stockpile]==0
#---------------------------------------------------------------------------
when "114"
if user.effects[PBEffects::Stockpile]==0
score -= 90
elsif user.hp==user.totalhp
score -= 90
else
mult = [0,25,50,100][user.effects[PBEffects::Stockpile]]
score += mult
score -= user.hp*mult*2/user.totalhp
end
#---------------------------------------------------------------------------
when "115"
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
#---------------------------------------------------------------------------
when "116"
#---------------------------------------------------------------------------
when "117"
hasAlly = false
user.eachAlly do |b|
hasAlly = true
break
end
score -= 90 if !hasAlly
#---------------------------------------------------------------------------
when "118"
if @battle.field.effects[PBEffects::Gravity]>0
score -= 90
elsif skill>=PBTrainerAI.mediumSkill
score -= 30
score -= 20 if user.effects[PBEffects::SkyDrop]>=0
score -= 20 if user.effects[PBEffects::MagnetRise]>0
score -= 20 if user.effects[PBEffects::Telekinesis]>0
score -= 20 if user.pbHasType?(:FLYING)
score -= 20 if user.hasActiveAbility?(:LEVITATE)
score -= 20 if user.hasActiveItem?(:AIRBALLOON)
score += 20 if target.effects[PBEffects::SkyDrop]>=0
score += 20 if target.effects[PBEffects::MagnetRise]>0
score += 20 if target.effects[PBEffects::Telekinesis]>0
score += 20 if target.inTwoTurnAttack?("0C9","0CC","0CE") # Fly, Bounce, Sky Drop
score += 20 if target.pbHasType?(:FLYING)
score += 20 if target.hasActiveAbility?(:LEVITATE)
score += 20 if target.hasActiveItem?(:AIRBALLOON)
end
#---------------------------------------------------------------------------
when "119"
if user.effects[PBEffects::MagnetRise]>0 ||
user.effects[PBEffects::Ingrain] ||
user.effects[PBEffects::SmackDown]
score -= 90
end
#---------------------------------------------------------------------------
when "11A"
if target.effects[PBEffects::Telekinesis]>0 ||
target.effects[PBEffects::Ingrain] ||
target.effects[PBEffects::SmackDown]
score -= 90
end
#---------------------------------------------------------------------------
when "11B"
#---------------------------------------------------------------------------
when "11C"
if skill>=PBTrainerAI.mediumSkill
score += 20 if target.effects[PBEffects::MagnetRise]>0
score += 20 if target.effects[PBEffects::Telekinesis]>0
score += 20 if target.inTwoTurnAttack?("0C9","0CC") # Fly, Bounce
score += 20 if target.pbHasType?(:FLYING)
score += 20 if target.hasActiveAbility?(:LEVITATE)
score += 20 if target.hasActiveItem?(:AIRBALLOON)
end
#---------------------------------------------------------------------------
when "11D"
#---------------------------------------------------------------------------
when "11E"
#---------------------------------------------------------------------------
when "11F"
#---------------------------------------------------------------------------
when "120"
#---------------------------------------------------------------------------
when "121"
#---------------------------------------------------------------------------
when "122"
#---------------------------------------------------------------------------
when "123"
if !target.pbHasType?(user.type1) &&
!target.pbHasType?(user.type2)
score -= 90
end
#---------------------------------------------------------------------------
when "124"
#---------------------------------------------------------------------------
when "125"
#---------------------------------------------------------------------------
when "126"
score += 20 # Shadow moves are more preferable
#---------------------------------------------------------------------------
when "127"
score += 20 # Shadow moves are more preferable
if target.pbCanParalyze?(user,false)
score += 30
if skill>=PBTrainerAI.mediumSkill
aspeed = pbRoughStat(user,PBStats::SPEED,skill)
ospeed = pbRoughStat(target,PBStats::SPEED,skill)
if aspeed<ospeed
score += 30
elsif aspeed>ospeed
score -= 40
end
end
if skill>=PBTrainerAI.highSkill
score -= 40 if target.hasActiveAbility?([:GUTS,:MARVELSCALE,:QUICKFEET])
end
end
#---------------------------------------------------------------------------
when "128"
score += 20 # Shadow moves are more preferable
if target.pbCanBurn?(user,false)
score += 30
if skill>=PBTrainerAI.highSkill
score -= 40 if target.hasActiveAbility?([:GUTS,:MARVELSCALE,:QUICKFEET,:FLAREBOOST])
end
end
#---------------------------------------------------------------------------
when "129"
score += 20 # Shadow moves are more preferable
if target.pbCanFreeze?(user,false)
score += 30
if skill>=PBTrainerAI.highSkill
score -= 20 if target.hasActiveAbility?(:MARVELSCALE)
end
end
#---------------------------------------------------------------------------
when "12A"
score += 20 # Shadow moves are more preferable
if target.pbCanConfuse?(user,false)
score += 30
else
if skill>=PBTrainerAI.mediumSkill
score -= 90
end
end
#---------------------------------------------------------------------------
when "12B"
score += 20 # Shadow moves are more preferable
if !target.pbCanLowerStatStage?(PBStats::DEFENSE,user)
score -= 90
else
score += 40 if user.turnCount==0
score += target.stages[PBStats::DEFENSE]*20
end
#---------------------------------------------------------------------------
when "12C"
score += 20 # Shadow moves are more preferable
if !target.pbCanLowerStatStage?(PBStats::EVASION,user)
score -= 90
else
score += target.stages[PBStats::EVASION]*15
end
#---------------------------------------------------------------------------
when "12D"
score += 20 # Shadow moves are more preferable
#---------------------------------------------------------------------------
when "12E"
score += 20 # Shadow moves are more preferable
score += 20 if target.hp>=target.totalhp/2
score -= 20 if user.hp<user.hp/2
#---------------------------------------------------------------------------
when "12F"
score += 20 # Shadow moves are more preferable
score -= 110 if target.effects[PBEffects::MeanLook]>=0
#---------------------------------------------------------------------------
when "130"
score += 20 # Shadow moves are more preferable
score -= 40
#---------------------------------------------------------------------------
when "131"
score += 20 # Shadow moves are more preferable
if @battle.pbCheckGlobalAbility(:AIRLOCK) ||
@battle.pbCheckGlobalAbility(:CLOUDNINE)
score -= 90
elsif @battle.pbWeather==PBWeather::ShadowSky
score -= 90
end
#---------------------------------------------------------------------------
when "132"
score += 20 # Shadow moves are more preferable
if target.pbOwnSide.effects[PBEffects::AuroraVeil]>0 ||
target.pbOwnSide.effects[PBEffects::Reflect]>0 ||
target.pbOwnSide.effects[PBEffects::LightScreen]>0 ||
target.pbOwnSide.effects[PBEffects::Safeguard]>0
score += 30
score -= 90 if user.pbOwnSide.effects[PBEffects::AuroraVeil]>0 ||
user.pbOwnSide.effects[PBEffects::Reflect]>0 ||
user.pbOwnSide.effects[PBEffects::LightScreen]>0 ||
user.pbOwnSide.effects[PBEffects::Safeguard]>0
else
score -= 110
end
#---------------------------------------------------------------------------
when "133", "134"
score -= 95
score = 0 if skill>=PBTrainerAI.highSkill
#---------------------------------------------------------------------------
when "135"
if target.pbCanFreeze?(user,false)
score += 30
if skill>=PBTrainerAI.highSkill
score -= 20 if target.hasActiveAbility?(:MARVELSCALE)
end
end
#---------------------------------------------------------------------------
when "136"
score += 20 if user.stages[PBStats::DEFENSE]<0
#---------------------------------------------------------------------------
when "137"
hasEffect = user.statStageAtMax?(PBStats::DEFENSE) &&
user.statStageAtMax?(PBStats::SPDEF)
user.eachAlly do |b|
next if b.statStageAtMax?(PBStats::DEFENSE) && b.statStageAtMax?(PBStats::SPDEF)
hasEffect = true
score -= b.stages[PBStats::DEFENSE]*10
score -= b.stages[PBStats::SPDEF]*10
end
if hasEffect
score -= user.stages[PBStats::DEFENSE]*10
score -= user.stages[PBStats::SPDEF]*10
else
score -= 90
end
#---------------------------------------------------------------------------
when "138"
if target.statStageAtMax?(PBStats::SPDEF)
score -= 90
else
score -= target.stages[PBStats::SPDEF]*10
end
#---------------------------------------------------------------------------
when "139"
if !target.pbCanLowerStatStage?(PBStats::ATTACK,user)
score -= 90
else
score += target.stages[PBStats::ATTACK]*20
if skill>=PBTrainerAI.mediumSkill
hasPhysicalAttack = false
target.eachMove do |m|
next if !m.physicalMove?(m.type)
hasPhysicalAttack = true
break
end
if hasPhysicalAttack
score += 20
elsif skill>=PBTrainerAI.highSkill
score -= 90
end
end
end
#---------------------------------------------------------------------------
when "13A"
avg = target.stages[PBStats::ATTACK]*10
avg += target.stages[PBStats::SPATK]*10
score += avg/2
#---------------------------------------------------------------------------
when "13B"
if !user.isSpecies?(:HOOPA) || user.form!=1
score -= 100
else
score += 20 if target.stages[PBStats::DEFENSE]>0
end
#---------------------------------------------------------------------------
when "13C"
score += 20 if target.stages[PBStats::SPATK]>0
#---------------------------------------------------------------------------
when "13D"
if !target.pbCanLowerStatStage?(PBStats::SPATK,user)
score -= 90
else
score += 40 if user.turnCount==0
score += target.stages[PBStats::SPATK]*20
end
#---------------------------------------------------------------------------
when "13E"
count = 0
@battle.eachBattler do |b|
if b.pbHasType?(:GRASS) && !b.airborne? &&
(!b.statStageAtMax?(PBStats::ATTACK) || !b.statStageAtMax?(PBStats::SPATK))
count += 1
if user.opposes?(b)
score -= 20
else
score -= user.stages[PBStats::ATTACK]*10
score -= user.stages[PBStats::SPATK]*10
end
end
end
score -= 95 if count==0
#---------------------------------------------------------------------------
when "13F"
count = 0
@battle.eachBattler do |b|
if b.pbHasType?(:GRASS) && !b.statStageAtMax?(PBStats::DEFENSE)
count += 1
if user.opposes?(b)
score -= 20
else
score -= user.stages[PBStats::DEFENSE]*10
end
end
end
score -= 95 if count==0
#---------------------------------------------------------------------------
end
return score
end
end

View File

@@ -0,0 +1,409 @@
class PokeBattle_AI
alias __f__pbGetMoveScoreFunctionCode pbGetMoveScoreFunctionCode
#=============================================================================
# Get a score for the given move based on its effect
#=============================================================================
def pbGetMoveScoreFunctionCode(score,move,user,target,skill=100)
score = __f__pbGetMoveScoreFunctionCode(score,move,user,target,skill)
case move.function
#---------------------------------------------------------------------------
when "140"
count=0
@battle.eachBattler do |b|
if b.poisoned? &&
(!b.statStageAtMin?(PBStats::ATTACK) ||
!b.statStageAtMin?(PBStats::SPATK) ||
!b.statStageAtMin?(PBStats::SPEED))
count += 1
if user.opposes?(b)
score += user.stages[PBStats::ATTACK]*10
score += user.stages[PBStats::SPATK]*10
score += user.stages[PBStats::SPEED]*10
else
score -= 20
end
end
end
score -= 95 if count==0
#---------------------------------------------------------------------------
when "141"
if target.effects[PBEffects::Substitute]>0
score -= 90
else
numpos = 0; numneg = 0
PBStats.eachBattleStat do |s|
numpos += target.stages[s] if target.stages[s]>0
numneg += target.stages[s] if target.stages[s]<0
end
if numpos!=0 || numneg!=0
score += (numpos-numneg)*10
else
score -= 95
end
end
#---------------------------------------------------------------------------
when "142"
score -= 90 if target.pbHasType?(:GHOST)
#---------------------------------------------------------------------------
when "143"
score -= 90 if target.pbHasType?(:GRASS)
#---------------------------------------------------------------------------
when "144"
#---------------------------------------------------------------------------
when "145"
aspeed = pbRoughStat(user,PBStats::SPEED,skill)
ospeed = pbRoughStat(target,PBStats::SPEED,skill)
score -= 90 if aspeed>ospeed
#---------------------------------------------------------------------------
when "146"
#---------------------------------------------------------------------------
when "147"
#---------------------------------------------------------------------------
when "148"
aspeed = pbRoughStat(user,PBStats::SPEED,skill)
ospeed = pbRoughStat(target,PBStats::SPEED,skill)
if aspeed>ospeed
score -= 90
else
score += 30 if target.pbHasMoveType?(:FIRE)
end
#---------------------------------------------------------------------------
when "149"
if user.turnCount==0
score += 30
else
score -= 90 # Because it will fail here
score = 0 if skill>=PBTrainerAI.bestSkill
end
#---------------------------------------------------------------------------
when "14A"
#---------------------------------------------------------------------------
when "14B", "14C"
if user.effects[PBEffects::ProtectRate]>1 ||
target.effects[PBEffects::HyperBeam]>0
score -= 90
else
if skill>=PBTrainerAI.mediumSkill
score -= user.effects[PBEffects::ProtectRate]*40
end
score += 50 if user.turnCount==0
score += 30 if target.effects[PBEffects::TwoTurnAttack]>0
end
#---------------------------------------------------------------------------
when "14D"
#---------------------------------------------------------------------------
when "14E"
if user.statStageAtMax?(PBStats::SPATK) &&
user.statStageAtMax?(PBStats::SPDEF) &&
user.statStageAtMax?(PBStats::SPEED)
score -= 90
else
score -= user.stages[PBStats::SPATK]*10 # Only *10 isntead of *20
score -= user.stages[PBStats::SPDEF]*10 # because two-turn attack
score -= user.stages[PBStats::SPEED]*10
if skill>=PBTrainerAI.mediumSkill
hasSpecialAttack = false
user.eachMove do |m|
next if !m.specialMove?(m.type)
hasSpecialAttack = true
break
end
if hasSpecialAttack
score += 20
elsif skill>=PBTrainerAI.highSkill
score -= 90
end
end
if skill>=PBTrainerAI.highSkill
aspeed = pbRoughStat(user,PBStats::SPEED,skill)
ospeed = pbRoughStat(target,PBStats::SPEED,skill)
score += 30 if aspeed<ospeed && aspeed*2>ospeed
end
end
#---------------------------------------------------------------------------
when "14F"
if skill>=PBTrainerAI.highSkill && target.hasActiveAbility?(:LIQUIDOOZE)
score -= 80
else
score += 40 if user.hp<=user.totalhp/2
end
#---------------------------------------------------------------------------
when "150"
score += 20 if !user.statStageAtMax?(PBStats::ATTACK) && target.hp<=target.totalhp/4
#---------------------------------------------------------------------------
when "151"
avg = target.stages[PBStats::ATTACK]*10
avg += target.stages[PBStats::SPATK]*10
score += avg/2
#---------------------------------------------------------------------------
when "152"
#---------------------------------------------------------------------------
when "153"
score -= 95 if target.pbOwnSide.effects[PBEffects::StickyWeb]
#---------------------------------------------------------------------------
when "154"
#---------------------------------------------------------------------------
when "155"
#---------------------------------------------------------------------------
when "156"
#---------------------------------------------------------------------------
when "157"
score -= 90
#---------------------------------------------------------------------------
when "158"
score -= 90 if !user.belched?
#---------------------------------------------------------------------------
when "159"
if !target.pbCanPoison?(user,false) && !target.pbCanLowerStatStage?(PBStats::SPEED,user)
score -= 90
else
if target.pbCanPoison?(user,false)
score += 30
if skill>=PBTrainerAI.mediumSkill
score += 30 if target.hp<=target.totalhp/4
score += 50 if target.hp<=target.totalhp/8
score -= 40 if target.effects[PBEffects::Yawn]>0
end
if skill>=PBTrainerAI.highSkill
score += 10 if pbRoughStat(target,PBStats::DEFENSE,skill)>100
score += 10 if pbRoughStat(target,PBStats::SPDEF,skill)>100
score -= 40 if target.hasActiveAbility?([:GUTS,:MARVELSCALE,:TOXICBOOST])
end
end
if target.pbCanLowerStatStage?(PBStats::SPEED,user)
score += target.stages[PBStats::SPEED]*10
if skill>=PBTrainerAI.highSkill
aspeed = pbRoughStat(user,PBStats::SPEED,skill)
ospeed = pbRoughStat(target,PBStats::SPEED,skill)
score += 30 if aspeed<ospeed && aspeed*2>ospeed
end
end
end
#---------------------------------------------------------------------------
when "15A"
if target.opposes?(user)
score -= 40 if target.status==PBStatuses::BURN
else
score += 40 if target.status==PBStatuses::BURN
end
#---------------------------------------------------------------------------
when "15B"
if target.status==PBStatuses::NONE
score -= 90
elsif user.hp==user.totalhp && target.opposes?(user)
score -= 90
else
score += (user.totalhp-user.hp)*50/user.totalhp
score -= 30 if target.opposes?(user)
end
#---------------------------------------------------------------------------
when "15C"
hasEffect = user.statStageAtMax?(PBStats::ATTACK) &&
user.statStageAtMax?(PBStats::SPATK)
user.eachAlly do |b|
next if b.statStageAtMax?(PBStats::ATTACK) && b.statStageAtMax?(PBStats::SPATK)
hasEffect = true
score -= b.stages[PBStats::ATTACK]*10
score -= b.stages[PBStats::SPATK]*10
end
if hasEffect
score -= user.stages[PBStats::ATTACK]*10
score -= user.stages[PBStats::SPATK]*10
else
score -= 90
end
#---------------------------------------------------------------------------
when "15D"
numStages = 0
PBStats.eachBattleStat do |s|
next if target.stages[s]<=0
numStages += target.stages[s]
end
score += numStages*20
#---------------------------------------------------------------------------
when "15E"
if user.effects[PBEffects::LaserFocus]>0
score -= 90
else
score += 40
end
#---------------------------------------------------------------------------
when "15F"
score += user.stages[PBStats::DEFENSE]*10
#---------------------------------------------------------------------------
when "160"
if target.statStageAtMin?(PBStats::ATTACK)
score -= 90
else
if target.pbCanLowerStatStage?(PBStats::ATTACK,user)
score += target.stages[PBStats::ATTACK]*20
if skill>=PBTrainerAI.mediumSkill
hasPhysicalAttack = false
target.eachMove do |m|
next if !m.physicalMove?(m.type)
hasPhysicalAttack = true
break
end
if hasPhysicalAttack
score += 20
elsif skill>=PBTrainerAI.highSkill
score -= 90
end
end
end
score += (user.totalhp-user.hp)*50/user.totalhp
end
#---------------------------------------------------------------------------
when "161"
if skill>=PBTrainerAI.mediumSkill
if user.speed>target.speed
score += 50
else
score -= 70
end
end
#---------------------------------------------------------------------------
when "162"
score -= 90 if !user.pbHasType?(:FIRE)
#---------------------------------------------------------------------------
when "163"
#---------------------------------------------------------------------------
when "164"
#---------------------------------------------------------------------------
when "165"
if skill>=PBTrainerAI.mediumSkill
userSpeed = pbRoughStat(user,PBStats::SPEED,skill)
targetSpeed = pbRoughStat(target,PBStats::SPEED,skill)
if userSpeed<targetSpeed
score += 30
end
else
score += 30
end
#---------------------------------------------------------------------------
when "166"
#---------------------------------------------------------------------------
when "167"
if user.pbOwnSide.effects[PBEffects::AuroraVeil]>0 || @battle.pbWeather!=PBWeather::Hail
score -= 90
else
score += 40
end
#---------------------------------------------------------------------------
when "168"
if user.effects[PBEffects::ProtectRate]>1 ||
target.effects[PBEffects::HyperBeam]>0
score -= 90
else
if skill>=PBTrainerAI.mediumSkill
score -= user.effects[PBEffects::ProtectRate]*40
end
score += 50 if user.turnCount==0
score += 30 if target.effects[PBEffects::TwoTurnAttack]>0
score += 20 # Because of possible poisoning
end
#---------------------------------------------------------------------------
when "169"
#---------------------------------------------------------------------------
when "16A"
hasAlly = false
target.eachAlly do |b|
hasAlly = true
break
end
score -= 90 if !hasAlly
#---------------------------------------------------------------------------
when "16B"
if skill>=PBTrainerAI.mediumSkill
if target.lastRegularMoveUsed<0 ||
!target.pbHasMove?(target.lastRegularMoveUsed) ||
target.usingMultiTurnAttack?
score -= 90
else
# Without lots of code here to determine good/bad moves and relative
# speeds, using this move is likely to just be a waste of a turn
score -= 50
end
end
#---------------------------------------------------------------------------
when "16C"
if target.effects[PBEffects::ThroatChop]==0 && skill>=PBTrainerAI.highSkill
hasSoundMove = false
user.eachMove do |m|
next if !m.soundMove?
hasSoundMove = true
break
end
score += 40 if hasSoundMove
end
#---------------------------------------------------------------------------
when "16D"
if user.hp==user.totalhp || (skill>=PBTrainerAI.mediumSkill && !user.canHeal?)
score -= 90
else
score += 50
score -= user.hp*100/user.totalhp
score += 30 if @battle.pbWeather==PBWeather::Sandstorm
end
#---------------------------------------------------------------------------
when "16E"
if user.hp==user.totalhp || (skill>=PBTrainerAI.mediumSkill && !user.canHeal?)
score -= 90
else
score += 50
score -= user.hp*100/user.totalhp
if skill>=PBTrainerAI.mediumSkill
score += 30 if @battle.field.terrain==PBBattleTerrains::Grassy
end
end
#---------------------------------------------------------------------------
when "16F"
if !target.opposes?(user)
if target.hp==target.totalhp || (skill>=PBTrainerAI.mediumSkill && !target.canHeal?)
score -= 90
else
score += 50
score -= target.hp*100/target.totalhp
end
end
#---------------------------------------------------------------------------
when "170"
reserves = @battle.pbAbleNonActiveCount(user.idxOwnSide)
foes = @battle.pbAbleNonActiveCount(user.idxOpposingSide)
if @battle.pbCheckGlobalAbility(:DAMP)
score -= 100
elsif skill>=PBTrainerAI.mediumSkill && reserves==0 && foes>0
score -= 100 # don't want to lose
elsif skill>=PBTrainerAI.highSkill && reserves==0 && foes==0
score += 80 # want to draw
else
score -= (user.total.hp-user.hp)*75/user.totalhp
end
#---------------------------------------------------------------------------
when "171"
if skill>=PBTrainerAI.mediumSkill
hasPhysicalAttack = false
target.eachMove do |m|
next if !m.physicalMove?(m.type)
hasPhysicalAttack = true
break
end
score -= 80 if !hasPhysicalAttack
end
#---------------------------------------------------------------------------
when "172"
score += 20 # Because of possible burning
#---------------------------------------------------------------------------
when "173"
#---------------------------------------------------------------------------
when "174"
score -= 90 if user.turnCount>0 || user.lastRoundMoved>=0
#---------------------------------------------------------------------------
when "175"
score += 30 if target.effects[PBEffects::Minimize]
#---------------------------------------------------------------------------
end
return score
end
end

View File

@@ -0,0 +1,661 @@
class PokeBattle_AI
#=============================================================================
#
#=============================================================================
def pbTargetsMultiple?(move,user)
numTargets = 0
case move.pbTarget(user)
when PBTargets::AllNearFoes
@battle.eachOtherSideBattler(user) { |b| numTargets += 1 if b.near?(user) }
return numTargets>1
when PBTargets::AllNearOthers
@battle.eachBattler { |b| numTargets += 1 if b.near?(user) }
return numTargets>1
when PBTargets::UserAndAllies
@battle.eachSameSideBattler(user) { |_b| numTargets += 1 }
return numTargets>1
when PBTargets::AllFoes
@battle.eachOtherSideBattler(user) { |_b| numTargets += 1 }
return numTargets>1
when PBTargets::AllBattlers
@battle.eachBattler { |_b| numTargets += 1 }
return numTargets>1
end
return false
end
#=============================================================================
# Move's type effectiveness
#=============================================================================
def pbCalcTypeModSingle(moveType,defType,user,target)
ret = PBTypes.getEffectiveness(moveType,defType)
# Ring Target
if target.hasActiveItem?(:RINGTARGET)
ret = PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE if PBTypes.ineffective?(moveType,defType)
end
# Foresight
if user.hasActiveAbility?(:SCRAPPY) || target.effects[PBEffects::Foresight]
ret = PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE if defType == :GHOST &&
PBTypes.ineffective?(moveType,defType)
end
# Miracle Eye
if target.effects[PBEffects::MiracleEye]
ret = PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE if defType == :DARK &&
PBTypes.ineffective?(moveType,defType)
end
# Delta Stream's weather
if @battle.pbWeather==PBWeather::StrongWinds
ret = PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE if defType == :FLYING &&
PBTypes.superEffective?(moveType,defType)
end
# Grounded Flying-type Pokémon become susceptible to Ground moves
if !target.airborne?
ret = PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE if defType == :FLYING && moveType == :GROUND
end
return ret
end
def pbCalcTypeMod(moveType,user,target)
return PBTypeEffectiveness::NORMAL_EFFECTIVE if moveType<0
return PBTypeEffectiveness::NORMAL_EFFECTIVE if moveType == :GROUND &&
target.pbHasType?(:FLYING) && target.hasActiveItem?(:IRONBALL)
# Determine types
tTypes = target.pbTypes(true)
# Get effectivenesses
typeMods = [PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE] * 3 # 3 types max
tTypes.each_with_index do |type,i|
typeMods[i] = pbCalcTypeModSingle(moveType,type,user,target)
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(battlerThis,_battlerOther)
mod1 = PBTypes.getCombinedEffectiveness(battlerThis.type1,target.type1,target.type2)
mod2 = PBTypeEffectiveness::NORMAL_EFFECTIVE
if battlerThis.type1!=battlerThis.type2
mod2 = PBTypes.getCombinedEffectiveness(battlerThis.type2,target.type1,target.type2)
end
return mod1*mod2 # Normal effectiveness is 64 here
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)
# Type effectiveness
return true if PBTypeEffectiveness.ineffective?(typeMod) || score<=0
# Immunity due to ability/item/other effects
if skill>=PBTrainerAI.mediumSkill
case move.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])
when :GRASS
return true if target.hasActiveAbility?(:SAPSIPPER)
when :ELECTRIC
return true if target.hasActiveAbility?([:LIGHTNINGROD,:MOTORDRIVE,:VOLTABSORB])
end
return true if PBTypeEffectiveness.notVeryEffective?(typeMod) &&
target.hasActiveAbility?(:WONDERGUARD)
return true if move.damagingMove? && user.index!=target.index && !target.opposes?(user) &&
target.hasActiveAbility?(:TELEPATHY)
return true if 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 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 NEWEST_BATTLE_MECHANICS && user.hasActiveAbility?(:PRANKSTER) &&
target.pbHasType?(:DARK) && target.opposes?(user)
return true if move.priority>0 && @battle.field.terrain==PBBattleTerrains::Psychic &&
target.affectedByTerrain? && target.opposes?(user)
end
return false
end
#=============================================================================
# Get approximate properties for a battler
#=============================================================================
def pbRoughType(move,user,skill)
ret = move.type
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==PBStats::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 PBStats::ATTACK then value = battler.attack
when PBStats::DEFENSE then value = battler.defense
when PBStats::SPATK then value = battler.spatk
when PBStats::SPDEF then value = battler.spdef
when PBStats::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,user,target,skill)
baseDmg = move.baseDamage
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 "010" # Stomp
baseDmg *= 2 if skill>=PBTrainerAI.mediumSkill && target.effects[PBEffects::Minimize]
# Sonic Boom, Dragon Rage, Super Fang, Night Shade, Endeavor
when "06A", "06B", "06C", "06D", "06E"
baseDmg = move.pbFixedDamage(user,target)
when "06F" # Psywave
baseDmg = user.level
when "070" # OHKO
baseDmg = 200
when "071", "072", "073" # Counter, Mirror Coat, Metal Burst
baseDmg = 60
when "075", "076", "0D0", "12D" # Surf, Earthquake, Whirlpool, Shadow Storm
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 "077", "078", "07B", "07C", "07D", "07E", "07F", "080", "085", "087",
"089", "08A", "08B", "08C", "08E", "08F", "090", "091", "092", "097",
"098", "099", "09A", "0F7", "113"
baseDmg = move.pbBaseDamage(baseDmg,user,target)
when "086" # Acrobatics
baseDmg *= 2 if !user.item || user.hasActiveItem?(:FLYINGGEM)
when "08D" # Gyro Ball
targetSpeed = pbRoughStat(target,PBStats::SPEED,skill)
userSpeed = pbRoughStat(user,PBStats::SPEED,skill)
baseDmg = [[(25*targetSpeed/userSpeed).floor,150].min,1].max
when "094" # Present
baseDmg = 50
when "095" # Magnitude
baseDmg = 71
baseDmg *= 2 if target.inTwoTurnAttack?("0CA") # Dig
when "096" # Natural Gift
baseDmg = move.pbNaturalGiftBaseDamage(user.item_id)
when "09B" # Heavy Slam
baseDmg = move.pbBaseDamage(baseDmg,user,target)
baseDmg *= 2 if NEWEST_BATTLE_MECHANICS && skill>=PBTrainerAI.mediumSkill &&
target.effects[PBEffects::Minimize]
when "0A0", "0BD", "0BE" # Frost Breath, Double Kick, Twineedle
baseDmg *= 2
when "0BF" # Triple Kick
baseDmg *= 6 # Hits do x1, x2, x3 baseDmg in turn, for x6 in total
when "0C0" # Fury Attack
if user.hasActiveAbility?(:SKILLLINK)
baseDmg *= 5
else
baseDmg = (baseDmg*19/6).floor # Average damage dealt
end
when "0C1" # Beat Up
mult = 0
@battle.eachInTeamFromBattlerIndex(user.index) do |pkmn,_i|
mult += 1 if pkmn && pkmn.able? && pkmn.status==PBStatuses::NONE
end
baseDmg *= mult
when "0C4" # Solar Beam
baseDmg = move.pbBaseDamageMultiplier(baseDmg,user,target)
when "0D3" # Rollout
baseDmg *= 2 if user.effects[PBEffects::DefenseCurl]
when "0D4" # Bide
baseDmg = 40
when "0E1" # Final Gambit
baseDmg = user.hp
when "144" # Flying Press
if GameData::Type.exists?(:FLYING)
if skill>=PBTrainerAI.highSkill
targetTypes = target.pbTypes(true)
mult = PBTypes.getCombinedEffectiveness(:FLYING,
targetTypes[0],targetTypes[1],targetTypes[2])
baseDmg = (baseDmg.to_f*mult/PBTypeEffectiveness::NORMAL_EFFECTIVE).round
else
mult = PBTypes.getCombinedEffectiveness(:FLYING,
target.type1,target.type2,target.effects[PBEffects::Type3])
baseDmg = (baseDmg.to_f*mult/PBTypeEffectiveness::NORMAL_EFFECTIVE).round
end
end
baseDmg *= 2 if skill>=PBTrainerAI.mediumSkill && target.effects[PBEffects::Minimize]
when "166" # Stomping Tantrum
baseDmg *= 2 if user.lastRoundMoveFailed
when "175" # Double Iron Bash
baseDmg *= 2
baseDmg *= 2 if skill>=PBTrainerAI.mediumSkill && target.effects[PBEffects::Minimize]
end
return baseDmg
end
#=============================================================================
# Damage calculation
#=============================================================================
def pbRoughDamage(move,user,target,skill,baseDmg)
# Fixed damage moves
return baseDmg if move.is_a?(PokeBattle_FixedDamageMove)
# Get the move's type
type = pbRoughType(move,user,skill)
##### Calculate user's attack stat #####
atk = pbRoughStat(user,PBStats::ATTACK,skill)
if move.function=="121" # Foul Play
atk = pbRoughStat(target,PBStats::ATTACK,skill)
elsif move.specialMove?(type)
if move.function=="121" # Foul Play
atk = pbRoughStat(target,PBStats::SPATK,skill)
else
atk = pbRoughStat(user,PBStats::SPATK,skill)
end
end
##### Calculate target's defense stat #####
defense = pbRoughStat(target,PBStats::DEFENSE,skill)
if move.specialMove?(type) && move.function!="122" # Psyshock
defense = pbRoughStat(target,PBStats::SPDEF,skill)
end
##### Calculate all multiplier effects #####
multipliers = [1.0, 1.0, 1.0, 1.0]
# Ability effects that alter damage
moldBreaker = false
if skill>=PBTrainerAI.highSkill && target.hasMoldBreaker?
moldBreaker = true
end
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]
canCheck = true
abilityBlacklist.each do |m|
next if move.id != m
canCheck = false
break
end
if canCheck
BattleHandlers.triggerDamageCalcUserAbility(user.ability,
user,target,move,multipliers,baseDmg,type)
end
end
if skill>=PBTrainerAI.mediumSkill && !moldBreaker
user.eachAlly do |b|
next if !b.abilityActive?
BattleHandlers.triggerDamageCalcUserAllyAbility(b.ability,
user,target,move,multipliers,baseDmg,type)
end
end
if skill>=PBTrainerAI.bestSkill && !moldBreaker && target.abilityActive?
# 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
canCheck = false
break
end
if canCheck
BattleHandlers.triggerDamageCalcTargetAbility(target.ability,
user,target,move,multipliers,baseDmg,type)
end
end
if skill>=PBTrainerAI.bestSkill && !moldBreaker
target.eachAlly do |b|
next if !b.abilityActive?
BattleHandlers.triggerDamageCalcTargetAllyAbility(b.ability,
user,target,move,multipliers,baseDmg,type)
end
end
# Item effects that alter damage
# NOTE: Type-boosting gems aren't suitable for checking at the start of the
# round.
if skill>=PBTrainerAI.mediumSkill && user.itemActive?
# NOTE: These items aren't suitable for checking at the start of the
# round.
itemBlacklist = [:EXPERTBELT,:LIFEORB]
if !itemBlacklist.include?(user.item_id)
BattleHandlers.triggerDamageCalcUserItem(user.item,
user,target,move,multipliers,baseDmg,type)
end
end
if skill>=PBTrainerAI.bestSkill && target.itemActive?
# NOTE: Type-weakening berries aren't suitable for checking at the start
# of the round.
if !target.item.is_berry?
BattleHandlers.triggerDamageCalcTargetItem(target.item,
user,target,move,multipliers,baseDmg,type)
end
end
# Global abilities
if skill>=PBTrainerAI.mediumSkill
if (@battle.pbCheckGlobalAbility(:DARKAURA) && type == :DARK) ||
(@battle.pbCheckGlobalAbility(:FAIRYAURA) && type == :FAIRY)
if @battle.pbCheckGlobalAbility(:AURABREAK)
multipliers[BASE_DMG_MULT] *= 2/3
else
multipliers[BASE_DMG_MULT] *= 4/3
end
end
end
# Parental Bond
if skill>=PBTrainerAI.mediumSkill && user.hasActiveAbility?(:PARENTALBOND)
multipliers[BASE_DMG_MULT] *= 1.25
end
# Me First
# TODO
# Helping Hand - n/a
# Charge
if skill>=PBTrainerAI.mediumSkill
if user.effects[PBEffects::Charge]>0 && type == :ELECTRIC
multipliers[BASE_DMG_MULT] *= 2
end
end
# Mud Sport and Water Sport
if skill>=PBTrainerAI.mediumSkill
if type == :ELECTRIC
@battle.eachBattler do |b|
next if !b.effects[PBEffects::MudSport]
multipliers[BASE_DMG_MULT] /= 3
break
end
if @battle.field.effects[PBEffects::MudSportField]>0
multipliers[BASE_DMG_MULT] /= 3
end
end
if type == :FIRE
@battle.eachBattler do |b|
next if !b.effects[PBEffects::WaterSport]
multipliers[BASE_DMG_MULT] /= 3
break
end
if @battle.field.effects[PBEffects::WaterSportField]>0
multipliers[BASE_DMG_MULT] /= 3
end
end
end
# Terrain moves
if user.affectedByTerrain? && skill>=PBTrainerAI.mediumSkill
case @battle.field.terrain
when PBBattleTerrains::Electric
multipliers[BASE_DMG_MULT] *= 1.5 if type == :ELECTRIC
when PBBattleTerrains::Grassy
multipliers[BASE_DMG_MULT] *= 1.5 if type == :GRASS
when PBBattleTerrains::Psychic
multipliers[BASE_DMG_MULT] *= 1.5 if type == :PSYCHIC
end
end
if target.affectedByTerrain? && skill>=PBTrainerAI.mediumSkill
if @battle.field.terrain==PBBattleTerrains::Misty && type == :DRAGON
multipliers[BASE_DMG_MULT] /= 2
end
end
# Badge multipliers
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.
if target.pbOwnedByPlayer?
if move.physicalMove?(type) && @battle.pbPlayer.numbadges>=NUM_BADGES_BOOST_DEFENSE
multipliers[DEF_MULT] *= 1.1
elsif move.specialMove?(type) && @battle.pbPlayer.numbadges>=NUM_BADGES_BOOST_SPDEF
multipliers[DEF_MULT] *= 1.1
end
end
end
end
# Multi-targeting attacks
if skill>=PBTrainerAI.highSkill
if pbTargetsMultiple?(move,user)
multipliers[FINAL_DMG_MULT] *= 0.75
end
end
# Weather
if skill>=PBTrainerAI.mediumSkill
case @battle.pbWeather
when PBWeather::Sun, PBWeather::HarshSun
if type == :FIRE
multipliers[FINAL_DMG_MULT] *= 1.5
elsif type == :WATER
multipliers[FINAL_DMG_MULT] /= 2
end
when PBWeather::Rain, PBWeather::HeavyRain
if type == :FIRE
multipliers[FINAL_DMG_MULT] /= 2
elsif type == :WATER
multipliers[FINAL_DMG_MULT] *= 1.5
end
when PBWeather::Sandstorm
if target.pbHasType?(:ROCK) && move.specialMove?(type) && move.function!="122" # Psyshock
multipliers[DEF_MULT] *= 1.5
end
end
end
# Critical hits - n/a
# Random variance - n/a
# STAB
if skill>=PBTrainerAI.mediumSkill
if type && user.pbHasType?(type)
if user.hasActiveAbility?(:ADAPTABILITY)
multipliers[FINAL_DMG_MULT] *= 2
else
multipliers[FINAL_DMG_MULT] *= 1.5
end
end
end
# Type effectiveness
if skill>=PBTrainerAI.mediumSkill
typemod = pbCalcTypeMod(type,user,target)
multipliers[FINAL_DMG_MULT] *= typemod.to_f/PBTypeEffectiveness::NORMAL_EFFECTIVE
end
# Burn
if skill>=PBTrainerAI.highSkill
if user.status==PBStatuses::BURN && move.physicalMove?(type) &&
!user.hasActiveAbility?(:GUTS) &&
!(NEWEST_BATTLE_MECHANICS && move.function=="07E") # Facade
multipliers[FINAL_DMG_MULT] /= 2
end
end
# Aurora Veil, Reflect, Light Screen
if skill>=PBTrainerAI.highSkill
if !move.ignoresReflect? && !user.hasActiveAbility?(:INFILTRATOR)
if target.pbOwnSide.effects[PBEffects::AuroraVeil]>0
if @battle.pbSideBattlerCount(target)>1
multipliers[FINAL_DMG_MULT] *= 2/3.0
else
multipliers[FINAL_DMG_MULT] /= 2
end
elsif target.pbOwnSide.effects[PBEffects::Reflect]>0 && move.physicalMove?(type)
if @battle.pbSideBattlerCount(target)>1
multipliers[FINAL_DMG_MULT] *= 2/3.0
else
multipliers[FINAL_DMG_MULT] /= 2
end
elsif target.pbOwnSide.effects[PBEffects::LightScreen]>0 && move.specialMove?(type)
if @battle.pbSideBattlerCount(target)>1
multipliers[FINAL_DMG_MULT] *= 2/3.0
else
multipliers[FINAL_DMG_MULT] /= 2
end
end
end
end
# Minimize
if skill>=PBTrainerAI.highSkill
if target.effects[PBEffects::Minimize] && move.tramplesMinimize?(2)
multipliers[FINAL_DMG_MULT] *= 2
end
end
# Move-specific base damage modifiers
# TODO
# Move-specific final damage modifiers
# TODO
##### Main damage calculation #####
baseDmg = [(baseDmg * multipliers[BASE_DMG_MULT]).round, 1].max
atk = [(atk * multipliers[ATK_MULT]).round, 1].max
defense = [(defense * multipliers[DEF_MULT]).round, 1].max
damage = (((2.0 * user.level / 5 + 2).floor * baseDmg * atk / defense).floor / 50).floor + 2
damage = [(damage * multipliers[FINAL_DMG_MULT]).round, 1].max
# "AI-specific calculations below"
# Increased critical hit rates
if skill>=PBTrainerAI.mediumSkill
c = 0
# Ability effects that alter critical hit rate
if c>=0 && user.abilityActive?
c = BattleHandlers.triggerCriticalCalcUserAbility(user.ability,user,target,c)
end
if skill>=PBTrainerAI.bestSkill
if c>=0 && !moldBreaker && target.abilityActive?
c = BattleHandlers.triggerCriticalCalcTargetAbility(target.ability,user,target,c)
end
end
# Item effects that alter critical hit rate
if c>=0 && user.itemActive?
c = BattleHandlers.triggerCriticalCalcUserItem(user.item,user,target,c)
end
if skill>=PBTrainerAI.bestSkill
if c>=0 && target.itemActive?
c = BattleHandlers.triggerCriticalCalcTargetItem(target.item,user,target,c)
end
end
# Other efffects
c = -1 if target.pbOwnSide.effects[PBEffects::LuckyChant]>0
if c>=0
c += 1 if move.highCriticalRate?
c += user.effects[PBEffects::FocusEnergy]
c += 1 if user.inHyperMode? && move.type == :SHADOW
end
if c>=0
c = 4 if c>4
damage += damage*0.1*c
end
end
return damage.floor
end
#=============================================================================
# Accuracy calculation
#=============================================================================
def pbRoughAccuracy(move,user,target,skill)
# "Always hit" effects and "always hit" accuracy
if skill>=PBTrainerAI.mediumSkill
return 125 if target.effects[PBEffects::Minimize] && move.tramplesMinimize?(1)
return 125 if target.effects[PBEffects::Telekinesis]>0
end
baseAcc = move.accuracy
if skill>=PBTrainerAI.highSkill
baseAcc = move.pbBaseAccuracy(user,target)
end
return 125 if baseAcc==0 && skill>=PBTrainerAI.mediumSkill
# Get the move's type
type = pbRoughType(move,user,skill)
# Calculate all modifier effects
modifiers = []
modifiers[BASE_ACC] = baseAcc
modifiers[ACC_STAGE] = user.stages[PBStats::ACCURACY]
modifiers[EVA_STAGE] = target.stages[PBStats::EVASION]
modifiers[ACC_MULT] = 1.0
modifiers[EVA_MULT] = 1.0
pbCalcAccuracyModifiers(user,target,modifiers,move,type,skill)
# Check if move can't miss
return 125 if modifiers[BASE_ACC]==0
# Calculation
accStage = [[modifiers[ACC_STAGE],-6].max,6].min + 6
evaStage = [[modifiers[EVA_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[ACC_MULT]).round
evasion = (evasion * modifiers[EVA_MULT]).round
evasion = 1 if evasion<1
return modifiers[BASE_ACC] * accuracy / evasion
end
def pbCalcAccuracyModifiers(user,target,modifiers,move,type,skill)
moldBreaker = false
if skill>=PBTrainerAI.highSkill && target.hasMoldBreaker?
moldBreaker = true
end
# Ability effects that alter accuracy calculation
if skill>=PBTrainerAI.mediumSkill
if user.abilityActive?
BattleHandlers.triggerAccuracyCalcUserAbility(user.ability,
modifiers,user,target,move,type)
end
user.eachAlly do |b|
next if !b.abilityActive?
BattleHandlers.triggerAccuracyCalcUserAllyAbility(b.ability,
modifiers,user,target,move,type)
end
end
if skill>=PBTrainerAI.bestSkill
if target.abilityActive? && !moldBreaker
BattleHandlers.triggerAccuracyCalcTargetAbility(target.ability,
modifiers,user,target,move,type)
end
end
# Item effects that alter accuracy calculation
if skill>=PBTrainerAI.mediumSkill
if user.itemActive?
BattleHandlers.triggerAccuracyCalcUserItem(user.item,
modifiers,user,target,move,type)
end
end
if skill>=PBTrainerAI.bestSkill
if target.itemActive?
BattleHandlers.triggerAccuracyCalcTargetItem(target.item,
modifiers,user,target,move,type)
end
end
# Other effects, inc. ones that set ACC_MULT or EVA_STAGE to specific values
if skill>=PBTrainerAI.mediumSkill
if @battle.field.effects[PBEffects::Gravity]>0
modifiers[ACC_MULT] *= 5/3.0
end
if user.effects[PBEffects::MicleBerry]
modifiers[ACC_MULT] *= 1.2
end
modifiers[EVA_STAGE] = 0 if target.effects[PBEffects::Foresight] && modifiers[EVA_STAGE]>0
modifiers[EVA_STAGE] = 0 if target.effects[PBEffects::MiracleEye] && modifiers[EVA_STAGE]>0
end
# "AI-specific calculations below"
if skill>=PBTrainerAI.mediumSkill
modifiers[EVA_STAGE] = 0 if move.function=="0A9" # Chip Away
modifiers[BASE_ACC] = 0 if ["0A5","139","13A","13B","13C", # "Always hit"
"147"].include?(move.function)
modifiers[BASE_ACC] = 0 if user.effects[PBEffects::LockOn]>0 &&
user.effects[PBEffects::LockOnPos]==target.index
end
if skill>=PBTrainerAI.highSkill
if move.function=="006" # Toxic
modifiers[BASE_ACC] = 0 if NEWEST_BATTLE_MECHANICS && move.statusMove? &&
user.pbHasType?(:POISON)
end
if move.function=="070" # OHKO moves
modifiers[BASE_ACC] = move.accuracy+user.level-target.level
modifiers[ACC_MULT] = 0 if target.level>user.level
if skill>=PBTrainerAI.bestSkill
modifiers[ACC_MULT] = 0 if target.hasActiveAbility?(:STURDY)
end
end
end
end
end

View File

@@ -0,0 +1,108 @@
class PBStuff
#Standardized lists of moves with similar purposes/characteristics
#(mostly just "stuff that gets called together")
UNFREEZEMOVE = [PBMoves::FLAMEWHEEL,PBMoves::SACREDFIRE,PBMoves::FLAREBLITZ,
PBMoves::FUSIONFLARE,PBMoves::SCALD,PBMoves::STEAMERUPTION,PBMoves::BURNUP]
SWITCHOUTMOVE = [PBMoves::ROAR,PBMoves::WHIRLWIND,PBMoves::CIRCLETHROW,
PBMoves::DRAGONTAIL,PBMoves::YAWN,PBMoves::PERISHSONG]
SETUPMOVE = [PBMoves::SWORDSDANCE,PBMoves::DRAGONDANCE,PBMoves::CALMMIND,
PBMoves::WORKUP,PBMoves::NASTYPLOT,PBMoves::TAILGLOW,PBMoves::BELLYDRUM,
PBMoves::BULKUP,PBMoves::COIL,PBMoves::CURSE,PBMoves::GROWTH,
PBMoves::HONECLAWS,PBMoves::QUIVERDANCE,PBMoves::SHELLSMASH]
PROTECTMOVE = [PBMoves::PROTECT,PBMoves::DETECT,PBMoves::KINGSSHIELD,
PBMoves::SPIKYSHIELD,PBMoves::BANEFULBUNKER]
PROTECTIGNORINGMOVE = [PBMoves::FEINT,PBMoves::HYPERSPACEHOLE,
PBMoves::HYPERSPACEFURY,PBMoves::SHADOWFORCE,PBMoves::PHANTOMFORCE]
SCREENBREAKERMOVE = [PBMoves::DEFOG,PBMoves::BRICKBREAK,PBMoves::PSYCHICFANGS]
CONTRARYBAITMOVE = [PBMoves::SUPERPOWER,PBMoves::OVERHEAT,PBMoves::DRACOMETEOR,
PBMoves::LEAFSTORM,PBMoves::FLEURCANNON,PBMoves::PSYCHOBOOST]
TWOTURNAIRMOVE = [PBMoves::BOUNCE,PBMoves::FLY,PBMoves::SKYDROP]
PIVOTMOVE = [PBMoves::UTURN,PBMoves::VOLTSWITCH,PBMoves::PARTINGSHOT]
DANCEMOVE = [PBMoves::QUIVERDANCE,PBMoves::DRAGONDANCE,PBMoves::FIERYDANCE,
PBMoves::FEATHERDANCE,PBMoves::PETALDANCE,PBMoves::SWORDSDANCE,
PBMoves::TEETERDANCE,PBMoves::LUNARDANCE,PBMoves::REVELATIONDANCE]
BULLETMOVE = [PBMoves::ACIDSPRAY,PBMoves::AURASPHERE,PBMoves::BARRAGE,
PBMoves::BULLETSEED,PBMoves::EGGBOMB,PBMoves::ELECTROBALL,PBMoves::ENERGYBALL,
PBMoves::FOCUSBLAST,PBMoves::GYROBALL,PBMoves::ICEBALL,PBMoves::MAGNETBOMB,
PBMoves::MISTBALL,PBMoves::MUDBOMB,PBMoves::OCTAZOOKA,PBMoves::ROCKWRECKER,
PBMoves::SEARINGSHOT,PBMoves::SEEDBOMB,PBMoves::SHADOWBALL,PBMoves::SLUDGEBOMB,
PBMoves::WEATHERBALL,PBMoves::ZAPCANNON,PBMoves::BEAKBLAST]
BURNMOVE = [PBMoves::WILLOWISP,PBMoves::SACREDFIRE,PBMoves::INFERNO]
PARAMOVE = [PBMoves::THUNDERWAVE,PBMoves::STUNSPORE,PBMoves::GLARE,
PBMoves::NUZZLE,PBMoves::ZAPCANNON]
SLEEPMOVE = [PBMoves::SPORE,PBMoves::SLEEPPOWDER,PBMoves::HYPNOSIS,PBMoves::DARKVOID,
PBMoves::GRASSWHISTLE,PBMoves::LOVELYKISS,PBMoves::SING]
POISONMOVE = [PBMoves::TOXIC,PBMoves::POISONPOWDER,PBMoves::POISONGAS,PBMoves::TOXICTHREAD]
CONFUMOVE = [PBMoves::CONFUSERAY,PBMoves::SUPERSONIC,PBMoves::FLATTER,PBMoves::SWAGGER,
PBMoves::SWEETKISS,PBMoves::TEETERDANCE,PBMoves::CHATTER,PBMoves::DYNAMICPUNCH]
HEALFUNCTIONS = [0xD5,0xD6,0xD7,0xD8,0xD9,0xDD,0xDE,0xDF,0xE3,0xE4,0x114,0x139,0x158,0x162,0x169,0x16C,0x172]
#massive arrays of stuff that no one wants to see
NATURALGIFTDAMAGE={
100 => [:WATMELBERRY,:DURINBERRY,:BELUEBERRY,:LIECHIBERRY,:GANLONBERRY,:SALACBERRY,
:PETAYABERRY,:APICOTBERRY,:LANSATBERRY,:STARFBERRY,:ENIGMABERRY,:MICLEBERRY,
:CUSTAPBERRY,:JABOCABERRY,:ROWAPBERRY],
90 => [:BLUKBERRY,:NANABBERRY,:WEPEARBERRY,:PINAPBERRY,:POMEGBERRY,:KELPSYBERRY,
:QUALOTBERRY,:HONDEWBERRY,:GREPABERRY,:TAMATOBERRY,:CORNNBERRY,:MAGOSTBERRY,
:RABUTABERRY,:NOMELBERRY,:SPELONBERRY,:PAMTREBERRY],
80 => [:CHERIBERRY,:CHESTOBERRY,:PECHABERRY,:RAWSTBERRY,:ASPEARBERRY,:LEPPABERRY,
:ORANBERRY,:PERSIMBERRY,:LUMBERRY,:SITRUSBERRY,:FIGYBERRY,:WIKIBERRY,
:MAGOBERRY,:AGUAVBERRY,:IAPAPABERRY,:RAZZBERRY,:OCCABERRY,:PASSHOBERRY,
:WACANBERRY,:RINDOBERRY,:YACHEBERRY,:CHOPLEBERRY,:KEBIABERRY,:SHUCABERRY,
:COBABERRY,:PAYAPABERRY,:TANGABERRY,:CHARTIBERRY,:KASIBBERRY,:HABANBERRY,
:COLBURBERRY,:BABIRIBERRY,:CHILANBERRY]}
FLINGDAMAGE={
300 => [:MEMEONADE],
130 => [:IRONBALL],
100 => [:ARMORFOSSIL,:CLAWFOSSIL,:COVERFOSSIL,:DOMEFOSSIL,:HARDSTONE,:HELIXFOSSIL,
:OLDAMBER,:PLUMEFOSSIL,:RAREBONE,:ROOTFOSSIL,:SKULLFOSSIL],
90 => [:DEEPSEATOOTH,:DRACOPLATE,:DREADPLATE,:EARTHPLATE,:FISTPLATE,:FLAMEPLATE,
:GRIPCLAW,:ICICLEPLATE,:INSECTPLATE,:IRONPLATE,:MEADOWPLATE,:MINDPLATE,
:SKYPLATE,:SPLASHPLATE,:SPOOKYPLATE,:STONEPLATE,:THICKCLUB,:TOXICPLATE,
:ZAPPLATE],
80 => [:DAWNSTONE,:DUSKSTONE,:ELECTIRIZER,:MAGMARIZER,:ODDKEYSTONE,:OVALSTONE,
:PROTECTOR,:QUICKCLAW,:RAZORCLAW,:SHINYSTONE,:STICKYBARB,:ASSAULTVEST],
70 => [:BURNDRIVE,:CHILLDRIVE,:DOUSEDRIVE,:DRAGONFANG,:POISONBARB,:POWERANKLET,
:POWERBAND,:POWERBELT,:POWERBRACER,:POWERLENS,:POWERWEIGHT,:SHOCKDRIVE],
60 => [:ADAMANTORB,:DAMPROCK,:HEATROCK,:LUSTROUSORB,:MACHOBRACE,:ROCKYHELMET,
:STICK,:AMPLIFIELDROCK,:ADRENALINEORB],
50 => [:DUBIOUSDISC,:SHARPBEAK],
40 => [:EVIOLITE,:ICYROCK,:LUCKYPUNCH,:PROTECTIVEPADS],
30 => [:ABILITYURGE,:ABSORBBULB,:AMULETCOIN,:ANTIDOTE,:AWAKENING,:BALMMUSHROOM,
:BERRYJUICE,:BIGMUSHROOM,:BIGNUGGET,:BIGPEARL,:BINDINGBAND,:BLACKBELT,
:BLACKFLUTE,:BLACKGLASSES,:BLACKSLUDGE,:BLUEFLUTE,:BLUESHARD,:BURNHEAL,
:CALCIUM,:CARBOS,:CASTELIACONE,:CELLBATTERY,:CHARCOAL,:CLEANSETAG,
:COMETSHARD,:DAMPMULCH,:DEEPSEASCALE,:DIREHIT,:DIREHIT2,:DIREHIT3,
:DRAGONSCALE,:EJECTBUTTON,:ELIXIR,:ENERGYPOWDER,:ENERGYROOT,:ESCAPEROPE,
:ETHER,:EVERSTONE,:EXPSHARE,:FIRESTONE,:FLAMEORB,:FLOATSTONE,:FLUFFYTAIL,
:FRESHWATER,:FULLHEAL,:FULLRESTORE,:GOOEYMULCH,:GREENSHARD,:GROWTHMULCH,
:GUARDSPEC,:HEALPOWDER,:HEARTSCALE,:HONEY,:HPUP,:HYPERPOTION,:ICEHEAL,
:IRON,:ITEMDROP,:ITEMURGE,:KINGSROCK,:LAVACOOKIE,:LEAFSTONE,:LEMONADE,
:LIFEORB,:LIGHTBALL,:LIGHTCLAY,:LUCKYEGG,:MAGNET,:MAXELIXIR,:MAXETHER,
:MAXPOTION,:MAXREPEL,:MAXREVIVE,:METALCOAT,:METRONOME,:MIRACLESEED,
:MOOMOOMILK,:MOONSTONE,:MYSTICWATER,:NEVERMELTICE,:NUGGET,:OLDGATEAU,
:PARLYZHEAL,:PEARL,:PEARLSTRING,:POKEDOLL,:POKETOY,:POTION,:PPMAX,:PPUP,
:PRISMSCALE,:PROTEIN,:RAGECANDYBAR,:RARECANDY,:RAZORFANG,:REDFLUTE,
:REDSHARD,:RELICBAND,:RELICCOPPER,:RELICCROWN,:RELICGOLD,:RELICSILVER,
:RELICSTATUE,:RELICVASE,:REPEL,:RESETURGE,:REVIVALHERB,:REVIVE,:SACREDASH,
:SCOPELENS,:SHELLBELL,:SHOALSALT,:SHOALSHELL,:SMOKEBALL,:SODAPOP,:SOULDEW,
:SPELLTAG,:STABLEMULCH,:STARDUST,:STARPIECE,:SUNSTONE,:SUPERPOTION,
:SUPERREPEL,:SWEETHEART,:THUNDERSTONE,:TINYMUSHROOM,:TOXICORB,
:TWISTEDSPOON,:UPGRADE,:WATERSTONE,:WHITEFLUTE,:XACCURACY,:XACCURACY2,
:XACCURACY3,:XACCURACY6,:XATTACK,:XATTACK2,:XATTACK3,:XATTACK6,:XDEFEND,
:XDEFEND2,:XDEFEND3,:XDEFEND6,:XSPDEF,:XSPDEF2,:XSPDEF3,:XSPDEF6,:XSPECIAL,
:XSPECIAL2,:XSPECIAL3,:XSPECIAL6,:XSPEED,:XSPEED2,:XSPEED3,:XSPEED6,
:YELLOWFLUTE,:YELLOWSHARD,:ZINC,:BIGMALASADA,:ICESTONE],
20 => [:CLEVERWING,:GENIUSWING,:HEALTHWING,:MUSCLEWING,:PRETTYWING,
:RESISTWING,:SWIFTWING],
10 => [:AIRBALLOON,:BIGROOT,:BLUESCARF,:BRIGHTPOWDER,:CHOICEBAND,:CHOICESCARF,
:CHOICESPECS,:DESTINYKNOT,:EXPERTBELT,:FOCUSBAND,:FOCUSSASH,:FULLINCENSE,
:GREENSCARF,:LAGGINGTAIL,:LAXINCENSE,:LEFTOVERS,:LUCKINCENSE,:MENTALHERB,
:METALPOWDER,:MUSCLEBAND,:ODDINCENSE,:PINKSCARF,:POWERHERB,:PUREINCENSE,
:QUICKPOWDER,:REAPERCLOTH,:REDCARD,:REDSCARF,:RINGTARGET,:ROCKINCENSE,
:ROSEINCENSE,:SEAINCENSE,:SHEDSHELL,:SILKSCARF,:SILVERPOWDER,:SMOOTHROCK,
:SOFTSAND,:SOOTHEBELL,:WAVEINCENSE,:WHITEHERB,:WIDELENS,:WISEGLASSES,
:YELLOWSCARF,:ZOOMLENS,:BLUEMIC,:VANILLAIC,:STRAWBIC,:CHOCOLATEIC]}
end

View File

@@ -0,0 +1,154 @@
class PokeBattle_Battle
# Legacy method that should stop being used.
def pbGetOwner(battlerIndex)
if opposes?(battlerIndex)
if @opponent.is_a?(Array)
return (battlerIndex==1) ? @opponent[0] : @opponent[1]
else
return @opponent
end
else
if @player.is_a?(Array)
return (battlerIndex==0) ? @player[0] : @player[1]
else
return @player
end
end
end
# Reborn method (the difference is that each element in "choices" is an array
# in Essentials but just a number in Reborn)
def pbStdDev(choices)
sum = 0
n = 0
choices.each do |c|
sum += c
n += 1
end
return 0 if n<2
mean = sum.to_f/n.to_f
varianceTimesN = 0
for i in 0...choices.length
next if choices[i]<=0
deviation = choices[i].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
##############################################################################
# Choose an action.
##############################################################################
def pbDefaultChooseEnemyCommand(idxBattler)
if !@battle.pbCanShowFightMenu?(idxBattler)
return if pbEnemyShouldUseItem?(idxBattler)
# return if pbEnemyShouldWithdraw?(idxBattler) # Old Switching Method
return if pbShouldSwitch?(idxBattler)
return if @battle.pbAutoFightMenu(idxBattler)
@battle.pbAutoChooseMove(idxBattler)
return
end
pbBuildMoveScores(idxBattler) # Grab the array of scores/targets before doing anything else
return if pbShouldSwitch?(idxBattler)
# return if pbEnemyShouldWithdraw?(idxBattler) # Old Switching Method
return if pbEnemyShouldUseItem?(idxBattler)
return if @battle.pbAutoFightMenu(idxBattler)
@battle.pbRegisterUltraBurst(idxBattler) if pbEnemyShouldUltraBurst?(idxBattler)
@battle.pbRegisterMegaEvolution(idxBattler) if pbEnemyShouldMegaEvolve?(idxBattler)
if pbEnemyShouldZMove?(idxBattler)
return pbChooseEnemyZMove(idxBattler)
end
pbChooseMoves(idxBattler)
end
def pbChooseEnemyZMove(index) #Put specific cases for trainers using status Z-Moves
chosenmove=false
chosenindex=-1
attacker = @battlers[index]
opponent=attacker.pbOppositeOpposing
otheropp=opponent.pbPartner
skill=pbGetOwner(attacker.index).skill || 0
for i in 0..3
move=@battlers[index].moves[i]
if @battlers[index].pbCompatibleZMoveFromMove?(move)
if move.id == (PBMoves::CONVERSION) || move.id == (PBMoves::SPLASH)
pbRegisterZMove(index)
pbRegisterMove(index,i,false)
pbRegisterTarget(index,opponent.index)
return
end
if !chosenmove
chosenindex = i
chosenmove=move
else
if move.basedamage>chosenmove.basedamage
chosenindex=i
chosenmove=move
end
end
end
end
#oppeff1 = chosenmove.pbTypeModifier(chosenmove.type,attacker,opponent)
oppeff1 = pbTypeModNoMessages(chosenmove.type,attacker,opponent,chosenmove,skill)
#oppeff2 = chosenmove.pbTypeModifier(chosenmove.type,attacker,otheropp)
oppeff2 = pbTypeModNoMessages(chosenmove.type,attacker,otheropp,chosenmove,skill)
oppeff1 = 0 if opponent.hp<(opponent.totalhp/2.0).round
oppeff1 = 0 if (opponent.effects[PBEffects::Substitute]>0 || opponent.effects[PBEffects::Disguise]) && attacker.item!=(PBItems::KOMMONIUMZ2)
oppeff2 = 0 if otheropp.hp<(otheropp.totalhp/2.0).round
oppeff2 = 0 if (otheropp.effects[PBEffects::Substitute]>0 || otheropp.effects[PBEffects::Disguise]) && attacker.item!=(PBItems::KOMMONIUMZ2)
oppmult=0
for i in 1..7 #iterates through all the stats
oppmult+=opponent.stages[i] if opponent.stages[i]>0
end
othermult=0
for i in 1..7
othermult+=otheropp.stages[i] if otheropp.stages[i]>0
end
if (oppeff1<4) && ((oppeff2<4) || otheropp.hp==0)
pbChooseMoves(index)
elsif oppeff1>oppeff2
pbRegisterZMove(index)
pbRegisterMove(index,chosenindex,false)
pbRegisterTarget(index,opponent.index)
elsif oppeff1<oppeff2
pbRegisterZMove(index)
pbRegisterMove(index,chosenindex,false)
pbRegisterTarget(index,otheropp.index)
elsif oppeff1==oppeff2
if oppmult > othermult
pbRegisterZMove(index)
pbRegisterMove(index,chosenindex,false)
pbRegisterTarget(index,opponent.index)
elsif oppmult < othermult
pbRegisterZMove(index)
pbRegisterMove(index,chosenindex,false)
pbRegisterTarget(index,otheropp.index)
else
if otheropp.hp > opponent.hp
pbRegisterZMove(index)
pbRegisterMove(index,chosenindex,false)
pbRegisterTarget(index,otheropp.index)
else
pbRegisterZMove(index)
pbRegisterMove(index,chosenindex,false)
pbRegisterTarget(index,opponent.index)
end
end
end
end
#=============================================================================
# Decide whether the opponent should Ultra Burst their Pokémon.
#=============================================================================
def pbEnemyShouldUltraBurst?(idxBattler)
battler = @battlers[idxBattler]
if pbCanUltraBurst?(idxBattler) # Simple "always should if possible"
PBDebug.log("[AI] #{battler.pbThis} (#{idxBattler}) will Ultra Burst")
return true
end
return false
end
end

View File

@@ -0,0 +1,495 @@
class PokeBattle_Battle
################################################################################
# Decide whether the opponent should use an item on the Pokémon.
################################################################################
def pbEnemyShouldUseItem?(index)
item=pbEnemyItemToUse(index)
if item>0 && @battlers[index].effects[PBEffects::Embargo]==0
pbRegisterItem(index,item,nil)
return true
end
return false
end
def pbEnemyItemAlreadyUsed?(index,item,items)
if @choices[1][0]==3 && @choices[1][1]==item
qty=0
for i in items
qty+=1 if i==item
end
return true if qty<=1
end
return false
end
def pbEnemyItemToUse(index)
return 0 if !@opponent
return 0 if !@internalbattle
items=pbGetOwnerItems(index)
return 0 if !items
skill=pbGetOwner(index).skill || 0
battler=@battlers[index]
party = pbParty(index)
opponent1 = battler.pbOppositeOpposing
opponent2 = opponent1.pbPartner
currentroles = pbGetMonRole(battler,opponent1,skill)
return 0 if battler.isFainted?
highscore = 0
movecount = -1
maxplaypri = -1
partynumber = 0
aimem = getAIMemory(skill,opponent1.pokemonIndex)
for i in party
next if i.nil?
next if i.hp == 0
partynumber+=1
end
itemnumber = 0
for i in items
next if pbEnemyItemAlreadyUsed?(index,i,items)
itemnumber+=1
end
#highest score
for i in battler.moves
scorearray = 0
scorearray = @scores[i] if @scores[i]
if scorearray>100 && i.priority>maxplaypri
maxplaypri = i.priority
end
end
highscore = @scores.max
highdamage = -1
maxopppri = -1
pridam = -1
bestid = -1
#expected damage
#if battler.pbSpeed<pbRoughStat(opponent1,PBStats::SPEED,skill)
if aimem.length > 0
for i in aimem
tempdam = pbRoughDamage(i,opponent1,battler,skill,i.basedamage)
if tempdam>highdamage
highdamage = tempdam
bestid = i.id
end
if i.priority > maxopppri
maxopppri = i.priority
pridam = tempdam
end
end
end
highratio = -1
#expected damage percentage
if battler.hp!=0
highratio = highdamage*(1.0/battler.hp)
end
scorearray = []
arraycount = -1
PBDebug.log(sprintf("Beginning AI Item use check.")) if $INTERNAL
PBDebug.log(sprintf(" ")) if $INTERNAL
for i in items
arraycount+=1
scorearray.push(0)
itemscore=100
ishpitem = false
isstatusitem = false
next if pbEnemyItemAlreadyUsed?(index,i,items)
if (i== PBItems::POTION) ||
(i== PBItems::ULTRAPOTION) ||
(i== PBItems::SUPERPOTION) ||
(i== PBItems::HYPERPOTION) ||
(i== PBItems::MAXPOTION) ||
(i== PBItems::FULLRESTORE) ||
(i== PBItems::FRESHWATER) ||
(i== PBItems::SODAPOP) ||
(i== PBItems::LEMONADE) ||
(i== PBItems::MOOMOOMILK) ||
(i== PBItems::MEMEONADE) ||
(i== PBItems::STRAWBIC) ||
(i== PBItems::CHOCOLATEIC) ||
(i== PBItems::BLUEMIC)
ishpitem=true
end
if (i== PBItems::FULLRESTORE) ||
(i== PBItems::FULLHEAL) ||
(i== PBItems::RAGECANDYBAR) ||
(i== PBItems::LAVACOOKIE) ||
(i== PBItems::OLDGATEAU) ||
(i== PBItems::CASTELIACONE) ||
(i== PBItems::LUMIOSEGALETTE) ||
(i== PBItems::BIGMALASADA)
isstatusitem=true
end
if ishpitem
PBDebug.log(sprintf("This is a HP-healing item.")) if $INTERNAL
restoreamount=0
if (i== PBItems::POTION)
restoreamount=20
elsif (i== PBItems::ULTRAPOTION)
restoreamount=200
elsif (i== PBItems::SUPERPOTION)
restoreamount=60
elsif (i== PBItems::HYPERPOTION)
restoreamount=120
elsif (i== PBItems::MAXPOTION) || (i== PBItems::FULLRESTORE)
restoreamount=battler.totalhp
elsif (i== PBItems::FRESHWATER)
restoreamount=30
elsif (i== PBItems::SODAPOP)
restoreamount=50
elsif (i== PBItems::LEMONADE)
restoreamount=70
elsif (i== PBItems::MOOMOOMILK)
restoreamount=110
elsif (i== PBItems::MEMEONADE)
restoreamount=103
elsif (i== PBItems::STRAWBIC)
restoreamount=90
elsif (i== PBItems::CHOCOLATEIC)
restoreamount=70
elsif (i== PBItems::BLUEMIC)
restoreamount=200
end
resratio=restoreamount*(1.0/battler.totalhp)
itemscore*= (2 - (2*(battler.hp*(1.0/battler.totalhp))))
if highdamage>=battler.hp
if highdamage > [battler.hp+restoreamount,battler.totalhp].min
itemscore*=0
else
itemscore*=1.2
end
healmove = false
for j in battler.moves
if j.isHealingMove?
healmove=true
end
end
if healmove
if battler.pbSpeed < opponent1.pbSpeed
if highdamage>=battler.hp
itemscore*=1.1
else
itemscore*=0.6
if resratio<0.55
itemscore*=0.2
end
end
end
end
else
itemscore*=0.4
end
if highdamage > restoreamount
itemscore*=0
else
if restoreamount-highdamage < 15
itemscore*=0.5
end
end
if battler.pbSpeed > opponent1.pbSpeed
itemscore*=0.8
if highscore >=110
if maxopppri > maxplaypri
itemscore*=1.3
if pridam>battler.hp
if pridam>(battler.hp/2.0)
itemscore*=0
else
itemscore*=2
end
end
elsif !(!opponent1.abilitynulled && opponent1.ability == PBAbilities::STURDY)
itemscore*=0
end
end
if currentroles.include?(PBMonRoles::SWEEPER)
itemscore*=1.1
end
else
if highdamage*2 > [battler.hp+restoreamount,battler.totalhp].min
itemscore*=0
else
itemscore*=1.5
if highscore >=110
itemscore*=1.5
end
end
end
if battler.hp == battler.totalhp
itemscore*=0
elsif battler.hp >= (battler.totalhp*0.8)
itemscore*=0.2
elsif battler.hp >= (battler.totalhp*0.6)
itemscore*=0.3
elsif battler.hp >= (battler.totalhp*0.5)
itemscore*=0.5
end
minipot = (partynumber-1)
minimini = -1
for j in items
next if pbEnemyItemAlreadyUsed?(index,j,items)
next if !((j== PBItems::POTION) || (j== PBItems::ULTRAPOTION) ||
(j== PBItems::SUPERPOTION) || (j== PBItems::HYPERPOTION) ||
(j== PBItems::MAXPOTION) || (j== PBItems::FULLRESTORE) ||
(j== PBItems::FRESHWATER) || (j== PBItems::SODAPOP) ||
(j== PBItems::LEMONADE) || (j== PBItems::MOOMOOMILK) ||
(j== PBItems::MEMEONADE) || (j== PBItems::STRAWBIC) ||
(j== PBItems::CHOCOLATEIC) || (j== PBItems::BLUEMIC))
minimini+=1
end
if minipot>minimini
itemscore*=(0.9**(minipot-minimini))
minipot=minimini
elsif minimini>minipot
itemscore*=(1.1**(minimini-minipot))
minimini=minipot
end
if currentroles.include?(PBMonRoles::LEAD) || currentroles.include?(PBMonRoles::SCREENER)
itemscore*=0.6
end
if currentroles.include?(PBMonRoles::TANK)
itemscore*=1.1
end
if currentroles.include?(PBMonRoles::SECOND)
itemscore*=1.1
end
if battler.hasWorkingItem(:LEFTOVERS) || (battler.hasWorkingItem(:BLACKSLUDGE) && battler.pbHasType?(:POISON))
itemscore*=0.9
end
if battler.status!=0 && !(i== PBItems::FULLRESTORE)
itemscore*=0.7
if battler.effects[PBEffects::Toxic]>0 && partynumber>1
itemscore*=0.2
end
end
if PBTypes.getCombinedEffectiveness(opponent1.type1,battler.type1,battler.type2)>4
itemscore*=0.7
elsif PBTypes.getCombinedEffectiveness(opponent1.type1,battler.type1,battler.type2)<4
itemscore*=1.1
if PBTypes.getCombinedEffectiveness(opponent1.type1,battler.type1,battler.type2)==0
itemscore*=1.2
end
end
if PBTypes.getCombinedEffectiveness(opponent1.type2,battler.type1,battler.type2)>4
itemscore*=0.6
elsif PBTypes.getCombinedEffectiveness(opponent1.type1,battler.type1,battler.type2)<4
itemscore*=1.1
if PBTypes.getCombinedEffectiveness(opponent1.type1,battler.type1,battler.type2)==0
itemscore*=1.2
end
end
if (!battler.abilitynulled && battler.ability == PBAbilities::REGENERATOR) && partynumber>1
itemscore*=0.7
end
end
if isstatusitem
PBDebug.log(sprintf("This is a status-curing item.")) if $INTERNAL
if !(i== PBItems::FULLRESTORE)
if battler.status==0
itemscore*=0
else
if highdamage>battler.hp
if (bestid==106 && battler.status==PBStatuses::SLEEP) || (bestid==298 && battler.status==PBStatuses::PARALYSIS) || bestid==179
if highdamage*0.5>battler.hp
itemscore*=0
else
itemscore*=1.4
end
else
itemscore*=0
end
end
end
if battler.status==PBStatuses::SLEEP
if battler.pbHasMove?((PBMoves::SLEEPTALK)) ||
battler.pbHasMove?((PBMoves::SNORE)) ||
battler.pbHasMove?((PBMoves::REST)) ||
(!battler.abilitynulled && battler.ability == PBAbilities::COMATOSE)
itemscore*=0.6
end
if checkAImoves([PBMoves::DREAMEATER,PBMoves::NIGHTMARE],aimem) || (!opponent1.abilitynulled && opponent1.ability == PBAbilities::BADDREAMS)
itemscore*=1.3
end
if highdamage*(1.0/battler.hp)>0.2
itemscore*=1.3
else
itemscore*=0.7
end
end
if battler.status==PBStatuses::PARALYSIS
if (!battler.abilitynulled && battler.ability == PBAbilities::QUICKFEET) || (!battler.abilitynulled && battler.ability == PBAbilities::GUTS)
itemscore*=0.5
end
if battler.pbSpeed>opponent1.pbSpeed && (battler.pbSpeed*0.5)<opponent1.pbSpeed
itemscore*=1.3
end
itemscore*=1.1
end
if battler.status==PBStatuses::BURN
itemscore*=1.1
if battler.attack>battler.spatk
itemscore*=1.2
else
itemscore*=0.8
end
if !battler.abilitynulled
itemscore*=0.6 if battler.ability == PBAbilities::GUTS
itemscore*=0.7 if battler.ability == PBAbilities::MAGICGUARD
itemscore*=0.8 if battler.ability == PBAbilities::FLAREBOOST
end
end
if battler.status==PBStatuses::POISON
itemscore*=1.1
if !battler.abilitynulled
itemscore*=0.5 if battler.ability == PBAbilities::GUTS
itemscore*=0.5 if battler.ability == PBAbilities::MAGICGUARD
itemscore*=0.5 if battler.ability == PBAbilities::TOXICBOOST
itemscore*=0.4 if battler.ability == PBAbilities::POISONHEAL
end
if battler.effects[PBEffects::Toxic]>0
itemscore*=1.1
if battler.effects[PBEffects::Toxic]>3
itemscore*=1.3
end
end
end
if battler.status==PBStatuses::FROZEN
itemscore*=1.3
thawmove=false
for j in battler.moves
if j.canThawUser?
thawmove=true
end
end
if thawmove
itemscore*=0.5
end
if highdamage*(1.0/battler.hp)>0.15
itemscore*=1.1
else
itemscore*=0.9
end
end
end
if battler.pbHasMove?((PBMoves::REFRESH)) ||
battler.pbHasMove?((PBMoves::REST)) ||
battler.pbHasMove?((PBMoves::PURIFY))
itemscore*=0.5
end
if (!battler.abilitynulled && battler.ability == PBAbilities::NATURALCURE) && partynumber>1
itemscore*=0.2
end
if (!battler.abilitynulled && battler.ability == PBAbilities::SHEDSKIN)
itemscore*=0.3
end
end
if partynumber==1 || currentroles.include?(PBMonRoles::ACE)
itemscore*=1.2
else
itemscore*=0.8
if battler.itemUsed2
itemscore*=0.6
end
end
if battler.effects[PBEffects::Confusion]>0
itemscore*=0.9
end
if battler.effects[PBEffects::Attract]>=0
itemscore*=0.6
end
if battler.effects[PBEffects::Substitute]>0
itemscore*=1.1
end
if battler.effects[PBEffects::LeechSeed]>=0
itemscore*=0.5
end
if battler.effects[PBEffects::Curse]
itemscore*=0.5
end
if battler.effects[PBEffects::PerishSong]>0
itemscore*=0.2
end
minipot=0
for s in [PBStats::ATTACK,PBStats::DEFENSE,PBStats::SPEED,
PBStats::SPATK,PBStats::SPDEF,PBStats::ACCURACY,PBStats::EVASION]
minipot+=battler.stages[s]
end
if currentroles.include?(PBMonRoles::PHYSICALWALL) || currentroles.include?(PBMonRoles::SPECIALWALL)
for s in [PBStats::DEFENSE,PBStats::SPDEF]
minipot+=battler.stages[s]
end
end
if currentroles.include?(PBMonRoles::SWEEPER)
for s in [PBStats::SPEED]
minipot+=battler.stages[s]
end
if battler.attack>battler.spatk
for s in [PBStats::ATTACK]
minipot+=battler.stages[s]
end
else
for s in [PBStats::SPATK]
minipot+=battler.stages[s]
end
end
end
minipot*=5
minipot+=100
minipot*=0.01
itemscore*=minipot
if opponent1.effects[PBEffects::TwoTurnAttack]>0 || opponent1.effects[PBEffects::HyperBeam]>0
itemscore*=1.2
end
if highscore>70
itemscore*=1.1
else
itemscore*=0.9
end
fielddisrupt = getFieldDisruptScore(battler,opponent1,skill)
if fielddisrupt <= 0
fielddisrupt=0.6
end
itemscore*= (1.0/fielddisrupt)
if @trickroom > 0
itemscore*=0.9
end
if battler.pbOwnSide.effects[PBEffects::Tailwind]>0
itemscore*=0.6
end
if battler.pbOwnSide.effects[PBEffects::Reflect]>0
itemscore*=0.9
end
if battler.pbOwnSide.effects[PBEffects::LightScreen]>0
itemscore*=0.9
end
if battler.pbOwnSide.effects[PBEffects::AuroraVeil]>0
itemscore*=0.8
end
if @doublebattle
itemscore*=0.8
end
itemscore-=100
PBDebug.log(sprintf("Score for %s: %d",PBItems.getName(i),itemscore)) if $INTERNAL
scorearray[arraycount] = itemscore
end
bestitem=-1
bestscore=-10000
counter=-1
for k in scorearray
counter+=1
if k>bestscore
bestscore = k
bestitem = items[counter]
end
end
PBDebug.log(sprintf("Highest item score: %d",bestscore)) if $INTERNAL
PBDebug.log(sprintf("Highest move score: %d",highscore)) if $INTERNAL
if highscore<bestscore
PBDebug.log(sprintf("Using %s",PBItems.getName(bestitem))) if $INTERNAL
return bestitem
else
PBDebug.log(sprintf("Not using an item.")) if $INTERNAL
PBDebug.log(sprintf(" ")) if $INTERNAL
return 0
end
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,832 @@
class PokeBattle_Battle
attr_accessor :scores
attr_accessor :targets
attr_accessor :myChoices
################################################################################
# Choose a move to use.
# Called before any decisions are made.
################################################################################
def pbBuildMoveScores(index) #Generates an array of movescores for decisions
# Ally targetting stuff marked with ###
attacker=@battlers[index]
@scores=[0,0,0,0]
@targets=nil
@myChoices=[]
totalscore=0
target=-1
skill=0
wildbattle=!@opponent && pbIsOpposing?(index)
if wildbattle # If wild battle
preference = attacker.personalID % 16
preference = preference % 4
for i in 0...4
if pbCanChooseMove?(index,i,false)
@scores[i]=100
if preference == i # for personality
@scores[i]+=100
end
@myChoices.push(i)
end
end
else
skill=pbGetOwner(attacker.index).skill || 0
opponent=attacker.pbOppositeOpposing
fastermon = (attacker.pbSpeed>pbRoughStat(opponent,PBStats::SPEED,skill)) ^ (@trickroom!=0)
if fastermon && opponent
PBDebug.log(sprintf("AI Pokemon #{attacker.name} is faster than #{opponent.name}.")) if $INTERNAL
elsif opponent
PBDebug.log(sprintf("Player Pokemon #{opponent.name} is faster than #{attacker.name}.")) if $INTERNAL
end
#if @doublebattle && !opponent.isFainted? && !opponent.pbPartner.isFainted?
if @doublebattle && ((!opponent.isFainted? && !opponent.pbPartner.isFainted?) || !attacker.pbPartner.isFainted?)
# Choose a target and move. Also care about partner.
otheropp=opponent.pbPartner
fastermon = (attacker.pbSpeed>pbRoughStat(opponent,PBStats::SPEED,skill)) ^ (@trickroom!=0)
if fastermon && otheropp
PBDebug.log(sprintf("AI Pokemon #{attacker.name} is faster than #{otheropp.name}.")) if $INTERNAL
elsif otheropp
PBDebug.log(sprintf("Player Pokemon #{otheropp.name} is faster than #{attacker.name}.")) if $INTERNAL
end
notopp=attacker.pbPartner ###
scoresAndTargets=[]
@targets=[-1,-1,-1,-1]
maxscore1=0
maxscore2=0
totalscore1=0
totalscore2=0
baseDamageArray=[]
baseDamageArray2=[]
baseDamageArray3=[] ###
for j in 0...4
next if attacker.moves[j].id < 1
# check attacker.moves[j].basedamage and if this is 0 instead check the status method
dmgValue = pbRoughDamage(attacker.moves[j],attacker,opponent,skill,attacker.moves[j].basedamage)
if attacker.moves[j].basedamage!=0
if opponent.hp==0
dmgPercent = 0
else
dmgPercent = (dmgValue*100)/(opponent.hp)
dmgPercent = 110 if dmgPercent > 110
end
else
dmgPercent = pbStatusDamage(attacker.moves[j])
end
baseDamageArray.push(dmgPercent)
#Second opponent
dmgValue2 = pbRoughDamage(attacker.moves[j],attacker,otheropp,skill,attacker.moves[j].basedamage)
if attacker.moves[j].basedamage!=0
if otheropp.hp==0
dmgPercent2=0
else
dmgPercent2 = (dmgValue2*100)/(otheropp.hp)
dmgPercent2 = 110 if dmgPercent2 > 110
end
else
dmgPercent2 = pbStatusDamage(attacker.moves[j])
end
baseDamageArray2.push(dmgPercent2)
#Partner ###
dmgValue3 = pbRoughDamage(attacker.moves[j],attacker,notopp,skill,attacker.moves[j].basedamage)
if attacker.moves[j].basedamage!=0
if notopp.hp==0
dmgPercent3=0
else
dmgPercent3 = (dmgValue3*100)/(notopp.hp)
dmgPercent3 = 110 if dmgPercent3 > 110
end
else
dmgPercent3 = pbStatusDamage(attacker.moves[j])
end
baseDamageArray3.push(dmgPercent3)
end
for i in 0...4
if pbCanChooseMove?(index,i,false)
score1=pbGetMoveScore(attacker.moves[i],attacker,opponent,skill,baseDamageArray[i],baseDamageArray,i)
score2=pbGetMoveScore(attacker.moves[i],attacker,otheropp,skill,baseDamageArray2[i],baseDamageArray2,i)
totalscore = score1+score2
if (attacker.moves[i].target&0x08)!=0 # Targets all users
score1=totalscore # Consider both scores as it will hit BOTH targets
score2=totalscore
if attacker.pbPartner.isFainted? || (!attacker.pbPartner.abilitynulled && attacker.pbPartner.ability == PBAbilities::TELEPATHY) # No partner
score1*=1.66
score2*=1.66
else
# If this move can also target the partner, get the partner's
# score too
v=pbRoughDamage(attacker.moves[i],attacker,attacker.pbPartner,skill,attacker.moves[i].basedamage)
p=(v*100)/(attacker.pbPartner.hp)
s=pbGetMoveScore(attacker.moves[i],attacker,attacker.pbPartner,skill,p)
s=110 if s>110
if !attacker.pbPartner.abilitynulled &&
(attacker.moves[i].type == PBTypes::FIRE && attacker.pbPartner.ability == PBAbilities::FLASHFIRE) ||
(attacker.moves[i].type == PBTypes::WATER && [PBAbilities::WATERABSORB, PBAbilities::STORMDRAIN, PBAbilities::DRYSKIN].include?(attacker.pbPartner.ability)) ||
(attacker.moves[i].type == PBTypes::GRASS && attacker.pbPartner.ability == PBAbilities::SAPSIPPER) ||
(attacker.moves[i].type == PBTypes::ELECTRIC && [PBAbilities::VOLTABSORB, PBAbilities::LIGHTNINGROD, PBAbilities::MOTORDRIVE].include?(attacker.pbPartner.ability))
score1*=2.00
score2*=2.00
else
if (attacker.pbPartner.hp.to_f)/attacker.pbPartner.totalhp>0.10 || ((attacker.pbPartner.pbSpeed<attacker.pbSpeed) ^ (@trickroom!=0))
s = 100-s
s=0 if s<0
s/=100.0
s * 0.5 # multiplier to control how much to arbitrarily care about hitting partner; lower cares more
if (attacker.pbPartner.pbSpeed<attacker.pbSpeed) ^ (@trickroom!=0)
s * 0.5 # care more if we're faster and would knock it out before it attacks
end
score1*=s
score2*=s
end
end
end
score1=score1.to_i
score2=score2.to_i
PBDebug.log(sprintf("%s: Final Score after Multi-Target Adjustment: %d",PBMoves.getName(attacker.moves[i].id),score1))
PBDebug.log(sprintf(""))
end
if attacker.moves[i].target==PBTargets::AllOpposing # Consider both scores as it will hit BOTH targets
totalscore = score1+score2
score1=totalscore
score2=totalscore
PBDebug.log(sprintf("%s: Final Score after Multi-Target Adjustment: %d",PBMoves.getName(attacker.moves[i].id),score1))
PBDebug.log(sprintf(""))
end
@myChoices.push(i)
scoresAndTargets.push([i*2,i,score1,opponent.index])
scoresAndTargets.push([i*2+1,i,score2,otheropp.index])
else
scoresAndTargets.push([i*2,i,-1,opponent.index])
scoresAndTargets.push([i*2+1,i,-1,otheropp.index])
end
end
for i in 0...4 ### This whole bit
if pbCanChooseMove?(index,i,false)
movecode = attacker.moves[i].function
if movecode == 0xDF || movecode == 0x63 || movecode == 0x67 || #Heal Pulse, Simple Beam, Skill Swap,
movecode == 0xA0 || movecode == 0xC1 || movecode == 0x142 || #Frost Breath, Beat Up, Topsy-Turvy,
movecode == 0x162 || movecode == 0x164 || movecode == 0x167 || #Floral Healing, Instruct, Pollen Puff,
movecode == 0x169 || movecode == 0x170 || movecode == 0x55 || #Purify, Spotlight, Psych Up,
movecode == 0x40 || movecode == 0x41 || movecode == 0x66 #Swagger, Flatter, Entrainment
partnerscore=pbGetMoveScore(attacker.moves[i],attacker,notopp,skill,baseDamageArray3[i],baseDamageArray3,i)
PBDebug.log(sprintf("%s: Score for using on partner: %d",PBMoves.getName(attacker.moves[i].id),partnerscore))
PBDebug.log(sprintf(""))
scoresAndTargets.push([i*10,i,partnerscore,notopp.index])
end
end
end
scoresAndTargets.sort!{|a,b|
if a[2]==b[2] # if scores are equal
a[0]<=>b[0] # sort by index (for stable comparison)
else
b[2]<=>a[2]
end
}
for i in 0...scoresAndTargets.length
idx=scoresAndTargets[i][1]
thisScore=scoresAndTargets[i][2]
if thisScore>0 || thisScore==-1
if scores[idx]==0 || ((scores[idx]==thisScore && pbAIRandom(10)<5) ||
(scores[idx] < thisScore))
# (scores[idx]!=thisScore && pbAIRandom(10)<3))
@scores[idx]=thisScore
@targets[idx]=scoresAndTargets[i][3]
end
end
end
else
# Choose a move. There is only 1 opposing Pokémon.
if @doublebattle && opponent.isFainted?
opponent=opponent.pbPartner
end
baseDamageArray=[]
baseDamageArrayAdj=[]
for j in 0...4
next if attacker.moves[j].id < 1
# check attacker.moves[j].basedamage and if this is 0 instead check the status method
dmgValue = pbRoughDamage(attacker.moves[j],attacker,opponent,skill,attacker.moves[j].basedamage)
if attacker.moves[j].basedamage!=0
dmgPercent = (dmgValue*100)/(opponent.hp)
dmgPercent = 110 if dmgPercent > 110
if attacker.moves[j].function == 0x115 || attacker.moves[j].function == 0xC3 ||
attacker.moves[j].function == 0xC4 || attacker.moves[j].function == 0xC5 ||
attacker.moves[j].function == 0xC6 || attacker.moves[j].function == 0xC7 ||
attacker.moves[j].function == 0xC8
dmgPercentAdj = (dmgPercent * 0.5)
else
dmgPercentAdj = dmgPercent
end
else
dmgPercent = pbStatusDamage(attacker.moves[j])
dmgPercentAdj = dmgPercent
end
baseDamageArray.push(dmgPercent)
baseDamageArrayAdj.push(dmgPercentAdj)
end
for i in 0...4
if pbCanChooseMove?(index,i,false)
@scores[i]=pbGetMoveScore(attacker.moves[i],attacker,opponent,skill,baseDamageArray[i],baseDamageArrayAdj,i)
@myChoices.push(i)
else
@scores[i] = -1
end
end
end
end
end
################################################################################
# Primary method for deciding which move to use.
################################################################################
def pbChooseMoves(index)
maxscore=0
totalscore=0
attacker=@battlers[index]
skill=pbGetOwner(attacker.index).skill rescue 0
wildbattle=!@opponent && pbIsOpposing?(index)
for i in 0...4
#next if scores[i] == -1
@scores[i]=0 if @scores[i]<0
maxscore=@scores[i] if @scores[i]>maxscore
totalscore+=@scores[i]
end
# Minmax choices depending on AI
if !wildbattle && skill>=PBTrainerAI.mediumSkill
threshold=(skill>=PBTrainerAI.bestSkill) ? 1.5 : (skill>=PBTrainerAI.highSkill) ? 2 : 3
newscore=(skill>=PBTrainerAI.bestSkill) ? 5 : (skill>=PBTrainerAI.highSkill) ? 10 : 15
for i in 0...@scores.length
if @scores[i]>newscore && @scores[i]*threshold<maxscore
totalscore-=(@scores[i]-newscore)
@scores[i]=newscore
end
end
end
if $INTERNAL
x="[#{attacker.pbThis}: "
j=0
for i in 0...4
if attacker.moves[i].id!=0
x+=", " if j>0
x+=PBMoves.getName(attacker.moves[i].id)+"="+@scores[i].to_s
j+=1
end
end
x+="]"
PBDebug.log(x)
end
if !wildbattle #&& maxscore>100
stdev=pbStdDev(@scores)
preferredMoves=[]
for i in 0...4
if attacker.moves[i].id!=0 && (@scores[i] >= (maxscore*0.95)) && pbCanChooseMove?(index,i,false)
preferredMoves.push(i)
preferredMoves.push(i) if @scores[i]==maxscore # Doubly prefer the best move
end
end
if preferredMoves.length>0
i=preferredMoves[pbAIRandom(preferredMoves.length)]
PBDebug.log("[Prefer "+PBMoves.getName(attacker.moves[i].id)+"]") if $INTERNAL
pbRegisterMove(index,i,false)
target=@targets[i] if @targets
if @doublebattle && target && target>=0
pbRegisterTarget(index,target)
end
return
end
end
PBDebug.log("If this battle is not wild, something has gone wrong in scoring moves (no preference chosen).") if $INTERNAL
if !wildbattle && attacker.turncount
badmoves=false
if ((maxscore<=20 && attacker.turncount>2) ||
(maxscore<=30 && attacker.turncount>5)) && pbAIRandom(10)<8
badmoves=true
end
if totalscore<100 && attacker.turncount>1
badmoves=true
movecount=0
for i in 0...4
if attacker.moves[i].id!=0
if @scores[i]>0 && attacker.moves[i].basedamage>0
badmoves=false
end
movecount+=1
end
end
badmoves=badmoves && pbAIRandom(10)!=0
end
end
if maxscore<=0
# If all scores are 0 or less, choose a move at random
if @myChoices.length>0
pbRegisterMove(index,@myChoices[pbAIRandom(@myChoices.length)],false)
else
pbAutoChooseMove(index)
end
else
randnum=pbAIRandom(totalscore)
cumtotal=0
for i in 0...4
if @scores[i]>0
cumtotal+=@scores[i]
if randnum<cumtotal
pbRegisterMove(index,i,false)
target=@targets[i] if @targets
break
end
end
end
end
if @doublebattle && target && target>=0
pbRegisterTarget(index,target)
end
end
##############################################################################
# Get a score for each move being considered (trainer-owned Pokémon only).
# Moves with higher scores are more likely to be chosen.
##############################################################################
def pbGetMoveScore(move,attacker,opponent,skill=100,roughdamage=10,initialscores=[],scoreindex=-1)
if roughdamage<1
roughdamage=1
end
PBDebug.log(sprintf("%s: initial score: %d",PBMoves.getName(move.id),roughdamage)) if $INTERNAL
skill=PBTrainerAI.minimumSkill if skill<PBTrainerAI.minimumSkill
#score=(pbRoughDamage(move,attacker,opponent,skill,move.basedamage)*100/opponent.hp) #roughdamage
score=roughdamage
#Temporarly mega-ing pokemon if it can #perry
if pbCanMegaEvolve?(attacker.index)
attacker.pokemon.makeMega
attacker.pbUpdate(true)
attacker.form=attacker.startform
megaEvolved=true
end
#Little bit of prep before getting into the case statement
oppitemworks = opponent.itemWorks?
attitemworks = attacker.itemWorks?
aimem = getAIMemory(skill,opponent.pokemonIndex)
bettertype = move.pbType(move.type,attacker,opponent)
opponent=attacker.pbOppositeOpposing if !opponent
opponent=opponent.pbPartner if opponent && opponent.isFainted?
roles = pbGetMonRole(attacker,opponent,skill)
if move.priority>0 || (move.basedamage==0 && !attacker.abilitynulled && attacker.ability == PBAbilities::PRANKSTER)
if move.basedamage>0
PBDebug.log(sprintf("Priority Check Begin")) if $INTERNAL
fastermon = (attacker.pbSpeed>pbRoughStat(opponent,PBStats::SPEED,skill)) ^ (@trickroom!=0)
if fastermon
PBDebug.log(sprintf("AI Pokemon is faster.")) if $INTERNAL
else
PBDebug.log(sprintf("Player Pokemon is faster.")) if $INTERNAL
end
if score>100
if @doublebattle
score*=1.3
else
if fastermon
score*=1.3
else
score*=2
end
end
else
if (!attacker.abilitynulled && attacker.ability == PBAbilities::STANCECHANGE)
if !fastermon
score*=0.7
end
end
end
movedamage = -1
opppri = false
pridam = -1
if (attacker.pbSpeed<pbRoughStat(opponent,PBStats::SPEED,skill)) ^ (@trickroom!=0)
if aimem.length > 0
for i in aimem
tempdam = pbRoughDamage(i,opponent,attacker,skill,i.basedamage)
if i.priority>0
opppri=true
if tempdam>pridam
pridam = tempdam
end
end
if tempdam>movedamage
movedamage = tempdam
end
end
end
end
PBDebug.log(sprintf("Expected damage taken: %d",movedamage)) if $INTERNAL
if !fastermon
if movedamage>attacker.hp
if @doublebattle
score+=75
else
score+=150
end
end
end
if opppri
score*=1.1
if pridam>attacker.hp
if fastermon
score*=3
else
score*=0.5
end
end
end
if !fastermon && opponent.effects[PBEffects::TwoTurnAttack]>0
score*=0
end
if $fefieldeffect==37
score*=0
end
if !opponent.abilitynulled && (opponent.ability == PBAbilities::DAZZLING || opponent.ability == PBAbilities::QUEENLYMAJESTY)
score*=0
end
end
score*=0.2 if checkAImoves([PBMoves::QUICKGUARD],aimem)
PBDebug.log(sprintf("Priority Check End")) if $INTERNAL
elsif move.priority<0
if fastermon
score*=0.9
if move.basedamage>0
if opponent.effects[PBEffects::TwoTurnAttack]>0
score*=2
end
end
end
end
##### Alter score depending on the move's function code ########################
score = pbGetMoveScoreFunctions(move,attacker,opponent,skill,roughdamage,initialscores,scoreindex,
score, oppitemworks, attitemworks, aimem, bettertype, roles, tempdam)
###### END FUNCTION CODES
if (!opponent.abilitynulled && opponent.ability == PBAbilities::DANCER)
if (PBStuff::DANCEMOVE).include?(move.id)
score*=0.5
score*=0.1 if $fefieldeffect==6
end
end
ioncheck = false
destinycheck = false
widecheck = false
powdercheck = false
shieldcheck = false
if skill>=PBTrainerAI.highSkill
for j in aimem
ioncheck = true if j.id==(PBMoves::IONDELUGE)
destinycheck = true if j.id==(PBMoves::DESTINYBOND)
widecheck = true if j.id==(PBMoves::WIDEGUARD)
powdercheck = true if j.id==(PBMoves::POWDER)
shieldcheck = true if j.id==(PBMoves::SPIKYSHIELD) ||
j.id==(PBMoves::KINGSSHIELD) || j.id==(PBMoves::BANEFULBUNKER)
end
if @doublebattle && @aiMoveMemory[2][opponent.pbPartner.pokemonIndex].length>0
for j in @aiMoveMemory[2][opponent.pbPartner.pokemonIndex]
widecheck = true if j.id==(PBMoves::WIDEGUARD)
powdercheck = true if j.id==(PBMoves::POWDER)
end
end
end
if ioncheck == true
if move.type == 0
if (!opponent.pbPartner.abilitynulled && opponent.pbPartner.ability == PBAbilities::LIGHTNINGROD) ||
(!opponent.abilitynulled && opponent.ability == PBAbilities::LIGHTNINGROD) ||
(!opponent.abilitynulled && opponent.ability == PBAbilities::VOLTABSORB) ||
(!opponent.abilitynulled && opponent.ability == PBAbilities::MOTORDRIVE)
score *= 0.3
end
end
end
if (move.target==PBTargets::SingleNonUser || move.target==PBTargets::RandomOpposing ||
move.target==PBTargets::AllOpposing || move.target==PBTargets::SingleOpposing ||
move.target==PBTargets::OppositeOpposing)
if move.type==13 || (ioncheck == true && move.type == 0)
if (!opponent.pbPartner.abilitynulled && opponent.pbPartner.ability == PBAbilities::LIGHTNINGROD)
score*=0
elsif (!attacker.pbPartner.abilitynulled && attacker.pbPartner.ability == PBAbilities::LIGHTNINGROD)
score*=0.3
end
elsif move.type==11
if (!opponent.pbPartner.abilitynulled && opponent.pbPartner.ability == PBAbilities::LIGHTNINGROD)
score*=0
elsif (!attacker.pbPartner.abilitynulled && attacker.pbPartner.ability == PBAbilities::LIGHTNINGROD)
score*=0.3
end
end
end
if move.isSoundBased?
if ((!opponent.abilitynulled && opponent.ability == PBAbilities::SOUNDPROOF) && !opponent.moldbroken) || attacker.effects[PBEffects::ThroatChop]!=0
score*=0
else
score *= 0.6 if checkAImoves([PBMoves::THROATCHOP],aimem)
end
end
if move.flags&0x80!=0 # Boosted crit moves
if !((!opponent.abilitynulled && opponent.ability == PBAbilities::SHELLARMOR) ||
(!opponent.abilitynulled && opponent.ability == PBAbilities::BATTLEARMOR) ||
attacker.effects[PBEffects::LaserFocus]>0)
boostercount = 0
if move.pbIsPhysical?(move.type)
boostercount += opponent.stages[PBStats::DEFENSE] if opponent.stages[PBStats::DEFENSE]>0
boostercount -= attacker.stages[PBStats::ATTACK] if attacker.stages[PBStats::ATTACK]<0
elsif move.pbIsSpecial?(move.type)
boostercount += opponent.stages[PBStats::SPDEF] if opponent.stages[PBStats::SPDEF]>0
boostercount -= attacker.stages[PBStats::SPATK] if attacker.stages[PBStats::SPATK]<0
end
score*=(1.05**boostercount)
end
end
if move.basedamage>0
if skill>=PBTrainerAI.highSkill
if opponent.effects[PBEffects::DestinyBond]
score*=0.2
else
if ((opponent.pbSpeed>attacker.pbSpeed) ^ (@trickroom!=0)) && destinycheck
score*=0.7
end
end
end
end
if widecheck && ((move.target == PBTargets::AllOpposing) || (move.target == PBTargets::AllNonUsers))
score*=0.2
end
if powdercheck && move.type==10
score*=0.2
end
if move.isContactMove? && !(attacker.item == PBItems::PROTECTIVEPADS) && !(!attacker.abilitynulled && attacker.ability == PBAbilities::LONGREACH)
if (oppitemworks && opponent.item == PBItems::ROCKYHELMET) || shieldcheck
score*=0.85
end
if !opponent.abilitynulled
if opponent.ability == PBAbilities::ROUGHSKIN || opponent.ability == PBAbilities::IRONBARBS
score*=0.85
elsif opponent.ability == PBAbilities::EFFECTSPORE
score*=0.75
elsif opponent.ability == PBAbilities::FLAMEBODY && attacker.pbCanBurn?(false)
score*=0.75
elsif opponent.ability == PBAbilities::STATIC && attacker.pbCanParalyze?(false)
score*=0.75
elsif opponent.ability == PBAbilities::POISONPOINT && attacker.pbCanPoison?(false)
score*=0.75
elsif opponent.ability == PBAbilities::CUTECHARM && attacker.effects[PBEffects::Attract]<0
if initialscores.length>0
if initialscores[scoreindex] < 102
score*=0.8
end
end
elsif opponent.ability == PBAbilities::GOOEY || opponent.ability == PBAbilities::TANGLINGHAIR
if attacker.pbCanReduceStatStage?(PBStats::SPEED)
score*=0.9
if ((pbRoughStat(opponent,PBStats::SPEED,skill)<attacker.pbSpeed) ^ (@trickroom!=0))
score*=0.8
end
end
elsif opponent.ability == PBAbilities::MUMMY
if !attacker.abilitynulled && !attacker.unstoppableAbility? &&
attacker.ability != opponent.ability && attacker.ability != PBAbilities::SHIELDDUST
mummyscore = getAbilityDisruptScore(move,opponent,attacker,skill)
if mummyscore < 2
mummyscore = 2 - mummyscore
else
mummyscore = 0
end
score*=mummyscore
end
end
end
if (!attacker.abilitynulled && attacker.ability == PBAbilities::POISONTOUCH) && opponent.pbCanPoison?(false)
score*=1.1
end
if (!attacker.abilitynulled && attacker.ability == PBAbilities::PICKPOCKET) && opponent.item!=0 && !pbIsUnlosableItem(opponent,opponent.item)
score*=1.1
end
if opponent.effects[PBEffects::KingsShield]== true ||
opponent.effects[PBEffects::BanefulBunker]== true ||
opponent.effects[PBEffects::SpikyShield]== true
score *=0.1
end
end
if move.basedamage>0 && (opponent.effects[PBEffects::SpikyShield] ||
opponent.effects[PBEffects::BanefulBunker] || opponent.effects[PBEffects::KingsShield])
score*=0.1
end
if move.basedamage==0
if hasgreatmoves(initialscores,scoreindex,skill)
maxdam=checkAIdamage(aimem,attacker,opponent,skill)
if maxdam>0 && maxdam<(attacker.hp*0.3)
score*=0.6
else
score*=0.2 ### highly controversial, revert to 0.1 if shit sucks
end
end
end
ispowder = (move.id==214 || move.id==218 || move.id==220 || move.id==445 || move.id==600 || move.id==18 || move.id==219)
if ispowder && (opponent.type==(PBTypes::GRASS) ||
(!opponent.abilitynulled && opponent.ability == PBAbilities::OVERCOAT) ||
(oppitemworks && opponent.item == PBItems::SAFETYGOGGLES))
score*=0
end
# A score of 0 here means it should absolutely not be used
if score<=0
PBDebug.log(sprintf("%s: final score: 0",PBMoves.getName(move.id))) if $INTERNAL
PBDebug.log(sprintf(" ")) if $INTERNAL
attacker.pbUpdate(true) if defined?(megaEvolved) && megaEvolved==true #perry
return score
end
##### Other score modifications ################################################
# Prefer damaging moves if AI has no more Pokémon
if attacker.pbNonActivePokemonCount==0
if skill>=PBTrainerAI.mediumSkill &&
!(skill>=PBTrainerAI.highSkill && opponent.pbNonActivePokemonCount>0)
if move.basedamage==0
PBDebug.log("[Not preferring status move]") if $INTERNAL
score*=0.9
elsif opponent.hp<=opponent.totalhp/2.0
PBDebug.log("[Preferring damaging move]") if $INTERNAL
score*=1.1
end
end
end
# Don't prefer attacking the opponent if they'd be semi-invulnerable
if opponent.effects[PBEffects::TwoTurnAttack]>0 &&
skill>=PBTrainerAI.highSkill
invulmove=$pkmn_move[opponent.effects[PBEffects::TwoTurnAttack]][0] #the function code of the current move
if move.accuracy>0 && # Checks accuracy, i.e. targets opponent
([0xC9,0xCA,0xCB,0xCC,0xCD,0xCE].include?(invulmove) ||
opponent.effects[PBEffects::SkyDrop]) &&
((attacker.pbSpeed>opponent.pbSpeed) ^ (@trickroom!=0))
if skill>=PBTrainerAI.bestSkill # Can get past semi-invulnerability
miss=false
case invulmove
when 0xC9, 0xCC # Fly, Bounce
miss=true unless move.function==0x08 || # Thunder
move.function==0x15 || # Hurricane
move.function==0x77 || # Gust
move.function==0x78 || # Twister
move.function==0x11B || # Sky Uppercut
move.function==0x11C || # Smack Down
(move.id == PBMoves::WHIRLWIND)
when 0xCA # Dig
miss=true unless move.function==0x76 || # Earthquake
move.function==0x95 # Magnitude
when 0xCB # Dive
miss=true unless move.function==0x75 || # Surf
move.function==0xD0 || # Whirlpool
move.function==0x12D # Shadow Storm
when 0xCD # Shadow Force
miss=true
when 0xCE # Sky Drop
miss=true unless move.function==0x08 || # Thunder
move.function==0x15 || # Hurricane
move.function==0x77 || # Gust
move.function==0x78 || # Twister
move.function==0x11B || # Sky Uppercut
move.function==0x11C # Smack Down
end
if opponent.effects[PBEffects::SkyDrop]
miss=true unless move.function==0x08 || # Thunder
move.function==0x15 || # Hurricane
move.function==0x77 || # Gust
move.function==0x78 || # Twister
move.function==0x11B || # Sky Uppercut
move.function==0x11C # Smack Down
end
score*=0 if miss
else
score*=0
end
end
end
# Pick a good move for the Choice items
if attitemworks && (attacker.item == PBItems::CHOICEBAND ||
attacker.item == PBItems::CHOICESPECS || attacker.item == PBItems::CHOICESCARF)
if move.basedamage==0 && move.function!=0xF2 # Trick
score*=0.1
end
if ((move.type == PBTypes::NORMAL) && $fefieldeffect!=29) ||
(move.type == PBTypes::GHOST) || (move.type == PBTypes::FIGHTING) ||
(move.type == PBTypes::DRAGON) || (move.type == PBTypes::PSYCHIC) ||
(move.type == PBTypes::GROUND) || (move.type == PBTypes::ELECTRIC) ||
(move.type == PBTypes::POISON)
score*=0.95
end
if (move.type == PBTypes::FIRE) || (move.type == PBTypes::WATER) ||
(move.type == PBTypes::GRASS) || (move.type == PBTypes::ELECTRIC)
score*=0.95
end
if move.accuracy > 0
miniacc = (move.accuracy/100.0)
score *= miniacc
end
if move.pp < 6
score *= 0.9
end
end
#If user is frozen, prefer a move that can thaw the user
if attacker.status==PBStatuses::FROZEN
if skill>=PBTrainerAI.mediumSkill
if move.canThawUser?
score+=30
else
hasFreezeMove=false
for m in attacker.moves
if m.canThawUser?
hasFreezeMove=true; break
end
end
score*=0 if hasFreezeMove
end
end
end
# If target is frozen, don't prefer moves that could thaw them
if opponent.status==PBStatuses::FROZEN
if (move.type == PBTypes::FIRE)
score *= 0.1
end
end
# Adjust score based on how much damage it can deal
if move.basedamage>0
typemod=pbTypeModNoMessages(bettertype,attacker,opponent,move,skill)
if typemod==0 || score<=0
score=0
elsif skill>=PBTrainerAI.mediumSkill && !(!attacker.abilitynulled &&
(attacker.ability == PBAbilities::MOLDBREAKER ||
attacker.ability == PBAbilities::TURBOBLAZE ||
attacker.ability == PBAbilities::TERAVOLT))
if !opponent.abilitynulled
if (typemod<=4 && opponent.ability == PBAbilities::WONDERGUARD) ||
(move.type == PBTypes::GROUND && (opponent.ability == PBAbilities::LEVITATE || (oppitemworks && opponent.item == PBItems::AIRBALLOON) || opponent.effects[PBEffects::MagnetRise]>0)) ||
(move.type == PBTypes::FIRE && opponent.ability == PBAbilities::FLASHFIRE) ||
(move.type == PBTypes::WATER && (opponent.ability == PBAbilities::WATERABSORB || opponent.ability == PBAbilities::STORMDRAIN || opponent.ability == PBAbilities::DRYSKIN)) ||
(move.type == PBTypes::GRASS && opponent.ability == PBAbilities::SAPSIPPER) ||
(move.type == PBTypes::ELECTRIC)&& (opponent.ability == PBAbilities::VOLTABSORB || opponent.ability == PBAbilities::LIGHTNINGROD || opponent.ability == PBAbilities::MOTORDRIVE)
score=0
end
end
else
if move.type == PBTypes::GROUND && (opponent.ability == PBAbilities::LEVITATE || (oppitemworks && opponent.item == PBItems::AIRBALLOON) || opponent.effects[PBEffects::MagnetRise]>0)
score=0
end
end
if score != 0
# Calculate how much damage the move will do (roughly)
realBaseDamage=move.basedamage
realBaseDamage=60 if move.basedamage==1
if skill>=PBTrainerAI.mediumSkill
realBaseDamage=pbBetterBaseDamage(move,attacker,opponent,skill,realBaseDamage)
end
end
else # non-damaging moves
if !opponent.abilitynulled
if (move.type == PBTypes::GROUND && (opponent.ability == PBAbilities::LEVITATE || (oppitemworks && opponent.item == PBItems::AIRBALLOON) || opponent.effects[PBEffects::MagnetRise]>0)) ||
(move.type == PBTypes::FIRE && opponent.ability == PBAbilities::FLASHFIRE) ||
(move.type == PBTypes::WATER && (opponent.ability == PBAbilities::WATERABSORB || opponent.ability == PBAbilities::STORMDRAIN || opponent.ability == PBAbilities::DRYSKIN)) ||
(move.type == PBTypes::GRASS && opponent.ability == PBAbilities::SAPSIPPER) ||
(move.type == PBTypes::ELECTRIC)&& (opponent.ability == PBAbilities::VOLTABSORB || opponent.ability == PBAbilities::LIGHTNINGROD || opponent.ability == PBAbilities::MOTORDRIVE)
score=0
end
end
end
accuracy=pbRoughAccuracy(move,attacker,opponent,skill)
score*=accuracy/100.0
#score=0 if score<=10 && skill>=PBTrainerAI.highSkill
if (move.basedamage==0 && !(move.id == PBMoves::NATUREPOWER)) &&
(move.target==PBTargets::SingleNonUser || move.target==PBTargets::RandomOpposing ||
move.target==PBTargets::AllOpposing || move.target==PBTargets::OpposingSide ||
move.target==PBTargets::SingleOpposing || move.target==PBTargets::OppositeOpposing) &&
((!opponent.abilitynulled && opponent.ability == PBAbilities::MAGICBOUNCE) ||
(!opponent.pbPartner.abilitynulled && opponent.pbPartner.ability == PBAbilities::MAGICBOUNCE))
score=0
end
if skill>=PBTrainerAI.mediumSkill
if (!attacker.abilitynulled && attacker.ability == PBAbilities::PRANKSTER)
if opponent.pbHasType?(:DARK)
if move.basedamage==0 && move.priority>-1
score=0
end
end
end
end
# Avoid shiny wild pokemon if you're an AI partner
if pbIsWild?
if attacker.index == 2
if opponent.pokemon.isShiny?
score *= 0.15
end
end
end
score=score.to_i
score=0 if score<0
PBDebug.log(sprintf("%s: final score: %d",PBMoves.getName(move.id),score)) if $INTERNAL
PBDebug.log(sprintf(" ")) if $INTERNAL
attacker.pbUpdate(true) if defined?(megaEvolved) && megaEvolved==true #perry
return score
end
##############################################################################
# Decide whether the opponent should use a Z-Move.
##############################################################################
def pbEnemyShouldZMove?(index)
return pbCanZMove?(index) #Conditions based on effectiveness and type handled later
end
end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,121 @@
class PokeBattle_Battle
attr_accessor :aiMoveMemory
alias __ai__initialize initialize
def initialize(battle)
__ai__initialize(battle)
@aiMoveMemory = [[],
[],
[[], [], [], [], [], [], [], [], [], [], [], []] # One array for each party index
]
end
################################################################################
# AI Memory utility functions
################################################################################
def getAIMemory(skill,index=0)
if skill>=PBTrainerAI.bestSkill
return @aiMoveMemory[2][index]
elsif skill>=PBTrainerAI.highSkill
return @aiMoveMemory[1]
elsif skill>=PBTrainerAI.mediumSkill
return @aiMoveMemory[0]
else
return []
end
end
def checkAImoves(moveID,memory)
#basic "does the other mon have x"
return false if memory.length == 0
for i in moveID
for j in memory
j = pbChangeMove(j,nil)#doesn't matter that i'm passing nil, won't get used
return true if i == j.id #i should already be an ID here
end
end
return false
end
def checkAIhealing(memory)
#less basic "can the other mon heal"
return false if memory.length == 0
for j in memory
return true if j.isHealingMove?
end
return false
end
def checkAIpriority(memory)
#"does the other mon have priority"
return false if memory.length == 0
for j in memory
return true if j.priority>0
end
return false
end
def checkAIaccuracy(memory)
#"does the other mon have moves that don't miss"
return false if memory.length == 0
for j in memory
j = pbChangeMove(j,nil)
return true if j.accuracy==0
end
return false
end
def checkAIdamage(memory,attacker,opponent,skill)
#returns how much damage the AI expects to take
return -1 if memory.length == 0
maxdam=0
for j in memory
tempdam = pbRoughDamage(j,opponent,attacker,skill,j.basedamage)
maxdam=tempdam if tempdam>maxdam
end
return maxdam
end
def checkAIbest(memory,modifier,type=[],usepower=true,attacker=nil,opponent=nil,skill=nil)
return false if memory.length == 0
#had to split this because switching ai uses power
bestmove = 0
if usepower
biggestpower = 0
for j in memory
if j.basedamage>biggestpower
biggestpower=j.basedamage
bestmove=j
end
end
else #maxdam
maxdam=0
for j in memory
tempdam = pbRoughDamage(j,opponent,attacker,skill,j.basedamage)
if tempdam>maxdam
maxdam=tempdam
bestmove=j
end
end
end
return false if bestmove==0
#i don't want to make multiple functions for rare cases
#we're doing it in one and you're gonna like it
case modifier
when 1 #type mod. checks types from a list.
return true if type.include?(bestmove.type)
when 2 #physical mod.
return true if bestmove.pbIsPhysical?(bestmove.type)
when 3 #special mod.
return true if bestmove.pbIsSpecial?(bestmove.type)
when 4 #contact mod.
return true if bestmove.isContactMove?
when 5 #sound mod.
return true if bestmove.isSoundBased?
when 6 #why.
return true if (PBStuff::BULLETMOVE).include?(bestmove.id)
end
return false #you're still here? it's over! go home.
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,308 @@
class PokeBattle_Battle
def pbGetMonRole(mon,opponent,skill,position=0,party=nil)
#PBDebug.log(sprintf("Beginning role assignment for %s",PBSpecies.getName(mon.species))) if $INTERNAL
monRoles=[]
monability = mon.ability.to_i
curemove=false
healingmove=false
wishmove=false
phasemove=false
priorityko=false
pivotmove=false
spinmove=false
batonmove=false
tauntmove=false
restmove=false
weathermove=false
fieldmove=false
if mon.class == PokeBattle_Battler
if mon.ev[3]>251 && (mon.nature==PBNatures::MODEST ||
mon.nature==PBNatures::JOLLY || mon.nature==PBNatures::TIMID ||
mon.nature==PBNatures::ADAMANT) || (mon.item==(PBItems::CHOICEBAND) ||
mon.item==(PBItems::CHOICESPECS) || mon.item==(PBItems::CHOICESCARF))
monRoles.push(PBMonRoles::SWEEPER)
end
for i in mon.moves
next if i.nil?
next if i.id == 0
if i.priority>0
dam=pbRoughDamage(i,mon,opponent,skill,i.basedamage)
if opponent.hp>0
percentage=(dam*100.0)/opponent.hp
priorityko=true if percentage>100
end
end
if i.isHealingMove?
healingmove=true
elsif (i.id == (PBMoves::HEALBELL) || i.id == (PBMoves::AROMATHERAPY))
curemove=true
elsif (i.id == (PBMoves::WISH))
wishmove=true
elsif (i.id == (PBMoves::YAWN) || i.id == (PBMoves::PERISHSONG) ||
i.id == (PBMoves::DRAGONTAIL) || i.id == (PBMoves::CIRCLETHROW) ||
i.id == (PBMoves::WHIRLWIND) || i.id == (PBMoves::ROAR))
phasemove=true
elsif (i.id == (PBMoves::UTURN) || i.id == (PBMoves::VOLTSWITCH))
pivotmove=true
elsif (i.id == (PBMoves::RAPIDSPIN))
spinmove=true
elsif (i.id == (PBMoves::BATONPASS))
batonmove=true
elsif (i.id == (PBMoves::TAUNT))
tauntmove=true
elsif (i.id == (PBMoves::REST))
restmove=true
elsif (i.id == (PBMoves::SUNNYDAY) || i.id == (PBMoves::RAINDANCE) ||
i.id == (PBMoves::HAIL) || i.id == (PBMoves::SANDSTORM))
weathermove=true
elsif (i.id == (PBMoves::GRASSYTERRAIN) || i.id == (PBMoves::ELECTRICTERRAIN) ||
i.id == (PBMoves::MISTYTERRAIN) || i.id == (PBMoves::PSYCHICTERRAIN) ||
i.id == (PBMoves::MIST) || i.id == (PBMoves::IONDELUGE) ||
i.id == (PBMoves::TOPSYTURVY))
fieldmove=true
end
end
if healingmove && (mon.ev[2]>251 && (mon.nature==PBNatures::BOLD ||
mon.nature==PBNatures::RELAXED || mon.nature==PBNatures::IMPISH ||
mon.nature==PBNatures::LAX))
monRoles.push(PBMonRoles::PHYSICALWALL)
end
if healingmove && (mon.ev[5]>251 && (mon.nature==PBNatures::CALM ||
mon.nature==PBNatures::GENTLE || mon.nature==PBNatures::SASSY ||
mon.nature==PBNatures::CAREFUL))
monRoles.push(PBMonRoles::SPECIALWALL)
end
if mon.pokemonIndex==0
monRoles.push(PBMonRoles::LEAD)
end
if curemove || (wishmove && mon.ev[0]>251)
monRoles.push(PBMonRoles::CLERIC)
end
if phasemove == true
monRoles.push(PBMonRoles::PHAZER)
end
if mon.item==(PBItems::LIGHTCLAY)
monRoles.push(PBMonRoles::SCREENER)
end
if priorityko || (mon.speed>opponent.speed)
monRoles.push(PBMonRoles::REVENGEKILLER)
end
if (pivotmove && healingmove) || (monability == PBAbilities::REGENERATOR)
monRoles.push(PBMonRoles::PIVOT)
end
if spinmove
monRoles.push(PBMonRoles::SPINNER)
end
if (mon.ev[0]>251 && !healingmove) || mon.item==(PBItems::ASSAULTVEST)
monRoles.push(PBMonRoles::TANK)
end
if batonmove
monRoles.push(PBMonRoles::BATONPASSER)
end
if tauntmove || mon.item==(PBItems::CHOICEBAND) ||
mon.item==(PBItems::CHOICESPECS)
monRoles.push(PBMonRoles::STALLBREAKER)
end
if restmove || (monability == PBAbilities::COMATOSE) ||
mon.item==(PBItems::TOXICORB) || mon.item==(PBItems::FLAMEORB) ||
(monability == PBAbilities::GUTS) ||
(monability == PBAbilities::QUICKFEET)||
(monability == PBAbilities::FLAREBOOST) ||
(monability == PBAbilities::TOXICBOOST) ||
(monability == PBAbilities::NATURALCURE) ||
(monability == PBAbilities::MAGICGUARD) ||
(monability == PBAbilities::MAGICBOUNCE) ||
((monability == PBAbilities::HYDRATION) && pbWeather==PBWeather::RAINDANCE)
monRoles.push(PBMonRoles::STATUSABSORBER)
end
if (monability == PBAbilities::SHADOWTAG) ||
(monability == PBAbilities::ARENATRAP) ||
(monability == PBAbilities::MAGNETPULL)
monRoles.push(PBMonRoles::TRAPPER)
end
if weathermove || (monability == PBAbilities::DROUGHT) ||
(monability == PBAbilities::SANDSTREAM) ||
(monability == PBAbilities::DRIZZLE) ||
(monability == PBAbilities::SNOWWARNING) ||
(monability == PBAbilities::PRIMORDIALSEA) ||
(monability == PBAbilities::DESOLATELAND) ||
(monability == PBAbilities::DELTASTREAM)
monRoles.push(PBMonRoles::WEATHERSETTER)
end
if fieldmove || (monability == PBAbilities::GRASSYSURGE) ||
(monability == PBAbilities::ELECTRICSURGE) ||
(monability == PBAbilities::MISTYSURGE) ||
(monability == PBAbilities::PSYCHICSURGE) ||
mon.item==(PBItems::AMPLIFIELDROCK)
monRoles.push(PBMonRoles::FIELDSETTER)
end
#if $game_switches[525] && mon.pokemonIndex==(pbParty(mon.index).length-1)
if mon.pokemonIndex==(pbParty(mon.index).length-1)
monRoles.push(PBMonRoles::ACE)
end
secondhighest=true
if pbParty(mon.index).length>2
for i in 0..(pbParty(mon.index).length-2)
next if pbParty(mon.index)[i].nil?
if mon.level<pbParty(mon.index)[i].level
secondhighest=false
end
end
end
#if $game_switches[525]&& secondhighest
if secondhighest
monRoles.push(PBMonRoles::SECOND)
end
#PBDebug.log(sprintf("Ending role assignment for %s",PBSpecies.getName(mon.species))) if $INTERNAL
#PBDebug.log(sprintf("")) if $INTERNAL
return monRoles
elsif mon.class == PokeBattle_Pokemon
movelist = []
for i in mon.moves
next if i.nil?
next if i.id == 0
movedummy = PokeBattle_Move.pbFromPBMove(self,i,mon)
movelist.push(movedummy)
end
if mon.ev[3]>251 && (mon.nature==PBNatures::MODEST ||
mon.nature==PBNatures::JOLLY || mon.nature==PBNatures::TIMID ||
mon.nature==PBNatures::ADAMANT) || (mon.item==(PBItems::CHOICEBAND) ||
mon.item==(PBItems::CHOICESPECS) || mon.item==(PBItems::CHOICESCARF))
monRoles.push(PBMonRoles::SWEEPER)
end
for i in movelist
next if i.nil?
if i.isHealingMove?
healingmove=true
elsif (i.id == (PBMoves::HEALBELL) || i.id == (PBMoves::AROMATHERAPY))
curemove=true
elsif (i.id == (PBMoves::WISH))
wishmove=true
elsif (i.id == (PBMoves::YAWN) || i.id == (PBMoves::PERISHSONG) ||
i.id == (PBMoves::DRAGONTAIL) || i.id == (PBMoves::CIRCLETHROW) ||
i.id == (PBMoves::WHIRLWIND) || i.id == (PBMoves::ROAR))
phasemove=true
elsif (i.id == (PBMoves::UTURN) || i.id == (PBMoves::VOLTSWITCH))
pivotmove=true
elsif (i.id == (PBMoves::RAPIDSPIN))
spinmove=true
elsif (i.id == (PBMoves::BATONPASS))
batonmove=true
elsif(i.id == (PBMoves::TAUNT))
tauntmove=true
elsif (i.id == (PBMoves::REST))
restmove=true
elsif (i.id == (PBMoves::SUNNYDAY) || i.id == (PBMoves::RAINDANCE) ||
i.id == (PBMoves::HAIL) || i.id == (PBMoves::SANDSTORM))
weathermove=true
elsif (i.id == (PBMoves::GRASSYTERRAIN) || i.id == (PBMoves::ELECTRICTERRAIN) ||
i.id == (PBMoves::MISTYTERRAIN) || i.id == (PBMoves::PSYCHICTERRAIN) ||
i.id == (PBMoves::MIST) || i.id == (PBMoves::IONDELUGE) ||
i.id == (PBMoves::TOPSYTURVY))
fieldmove=true
end
end
if healingmove && (mon.ev[2]>251 && (mon.nature==PBNatures::BOLD ||
mon.nature==PBNatures::RELAXED || mon.nature==PBNatures::IMPISH ||
mon.nature==PBNatures::LAX))
monRoles.push(PBMonRoles::PHYSICALWALL)
end
if healingmove && (mon.ev[5]>251 && (mon.nature==PBNatures::CALM ||
mon.nature==PBNatures::GENTLE || mon.nature==PBNatures::SASSY ||
mon.nature==PBNatures::CAREFUL))
monRoles.push(PBMonRoles::SPECIALWALL)
end
if position==0
monRoles.push(PBMonRoles::LEAD)
end
if (phasemove)
monRoles.push(PBMonRoles::PHAZER)
end
if mon.item==(PBItems::LIGHTCLAY)
monRoles.push(PBMonRoles::SCREENER)
end
# pbRoughDamage does not take Pokemon objects, this will cause issues
priorityko=false
for i in movelist
next if i.priority<1
next if i.basedamage<10
priorityko=true
end
if priorityko || (mon.speed>opponent.speed)
monRoles.push(PBMonRoles::REVENGEKILLER)
end
if (pivotmove && healingmove) || (monability == PBAbilities::REGENERATOR)
monRoles.push(PBMonRoles::PIVOT)
end
if spinmove
monRoles.push(PBMonRoles::SPINNER)
end
if (mon.ev[0]>251 && !healingmove) || mon.item==(PBItems::ASSAULTVEST)
monRoles.push(PBMonRoles::TANK)
end
if batonmove
monRoles.push(PBMonRoles::BATONPASSER)
end
if tauntmove || mon.item==(PBItems::CHOICEBAND) ||
mon.item==(PBItems::CHOICESPECS)
monRoles.push(PBMonRoles::STALLBREAKER)
end
if restmove || (monability == PBAbilities::COMATOSE) ||
mon.item==(PBItems::TOXICORB) || mon.item==(PBItems::FLAMEORB) ||
(monability == PBAbilities::GUTS) ||
(monability == PBAbilities::QUICKFEET) ||
(monability == PBAbilities::FLAREBOOST) ||
(monability == PBAbilities::TOXICBOOST) ||
(monability == PBAbilities::NATURALCURE) ||
(monability == PBAbilities::MAGICGUARD) ||
(monability == PBAbilities::MAGICBOUNCE) ||
((monability == PBAbilities::HYDRATION) && pbWeather==PBWeather::RAINDANCE)
monRoles.push(PBMonRoles::STATUSABSORBER)
end
if (monability == PBAbilities::SHADOWTAG) ||
(monability == PBAbilities::ARENATRAP) ||
(monability == PBAbilities::MAGNETPULL)
monRoles.push(PBMonRoles::TRAPPER)
end
if weathermove || (monability == PBAbilities::DROUGHT) ||
(monability == PBAbilities::SANDSTREAM) ||
(monability == PBAbilities::DRIZZLE) ||
(monability == PBAbilities::SNOWWARNING) ||
(monability == PBAbilities::PRIMORDIALSEA) ||
(monability == PBAbilities::DESOLATELAND) ||
(monability == PBAbilities::DELTASTREAM)
monRoles.push(PBMonRoles::WEATHERSETTER)
end
if fieldmove || (monability == PBAbilities::GRASSYSURGE) ||
(monability == PBAbilities::ELECTRICSURGE) ||
(monability == PBAbilities::MISTYSURGE) ||
(monability == PBAbilities::PSYCHICSURGE) ||
mon.item==(PBItems::AMPLIFIELDROCK)
monRoles.push(PBMonRoles::FIELDSETTER)
end
if position==(party.length-1)
#if $game_switches[525] && position==(party.length-1)
monRoles.push(PBMonRoles::ACE)
end
secondhighest=true
if party.length>2
for i in 0..(party.length-2)
next if party[i].nil?
if mon.level<party[i].level
secondhighest=false
end
end
end
#if $game_switches[525]&& secondhighest
if secondhighest
monRoles.push(PBMonRoles::SECOND)
end
#PBDebug.log(sprintf("Ending role assignment for %s",PBSpecies.getName(mon.species))) if $INTERNAL
#PBDebug.log(sprintf("")) if $INTERNAL
return monRoles
end
#PBDebug.log(sprintf("Ending role assignment for %s",PBSpecies.getName(mon.species))) if $INTERNAL
#PBDebug.log(sprintf("")) if $INTERNAL
return monRoles
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,209 @@
class PokeBattle_Battle
attr_reader :battleAI
alias mkai_initialize initialize
def initialize(*args)
mkai_initialize(*args)
@battleAI = MKAI.new(self, self.wildBattle?)
@battleAI.sides[0].set_party(@party1)
@battleAI.sides[0].set_trainers(@player)
@battleAI.sides[1].set_party(@party2)
@battleAI.sides[1].set_trainers(@opponent)
end
def pbRecallAndReplace(idxBattler, idxParty, batonPass = false)
if !@battlers[idxBattler].fainted?
@scene.pbRecall(idxBattler)
@battleAI.sides[idxBattler % 2].recall(idxBattler)
end
@battlers[idxBattler].pbAbilitiesOnSwitchOut # Inc. primordial weather check
@scene.pbShowPartyLineup(idxBattler & 1) if pbSideSize(idxBattler) == 1
pbMessagesOnReplace(idxBattler, idxParty)
pbReplace(idxBattler, idxParty, batonPass)
end
# Bug fix (used b instead of battler)
def pbMessageOnRecall(battler)
if battler.pbOwnedByPlayer?
if battler.hp<=battler.totalhp/4
pbDisplayBrief(_INTL("Good job, {1}! Come back!",battler.name))
elsif battler.hp<=battler.totalhp/2
pbDisplayBrief(_INTL("OK, {1}! Come back!",battler.name))
elsif battler.turnCount>=5
pbDisplayBrief(_INTL("{1}, thats enough! Come back!",battler.name))
elsif battler.turnCount>=2
pbDisplayBrief(_INTL("{1}, come back!",battler.name))
else
pbDisplayBrief(_INTL("{1}, switch out! Come back!",battler.name))
end
else
owner = pbGetOwnerName(battler.index)
pbDisplayBrief(_INTL("{1} withdrew {2}!",owner,battler.name))
end
end
alias mkai_pbEndOfRoundPhase pbEndOfRoundPhase
def pbEndOfRoundPhase
mkai_pbEndOfRoundPhase
@battleAI.end_of_round
end
alias mkai_pbShowAbilitySplash pbShowAbilitySplash
def pbShowAbilitySplash(battler, delay = false, logTrigger = true)
mkai_pbShowAbilitySplash(battler, delay, logTrigger)
@battleAI.reveal_ability(battler)
end
end
class PokeBattle_Move
attr_reader :statUp
attr_reader :statDown
alias mkai_pbReduceDamage pbReduceDamage
def pbReduceDamage(user, target)
mkai_pbReduceDamage(user, target)
@battle.battleAI.register_damage(self, user, target, target.damageState.hpLost)
end
def pbCouldBeCritical?(user, target)
return false if target.pbOwnSide.effects[PBEffects::LuckyChant] > 0
# Set up the critical hit ratios
ratios = (NEWEST_BATTLE_MECHANICS) ? [24,8,2,1] : [16,8,4,3,2]
c = 0
# Ability effects that alter critical hit rate
if c >= 0 && user.abilityActive?
c = BattleHandlers.triggerCriticalCalcUserAbility(user.ability, user, target, c)
end
if c >= 0 && target.abilityActive? && !@battle.moldBreaker
c = BattleHandlers.triggerCriticalCalcTargetAbility(target.ability, user, target, c)
end
# Item effects that alter critical hit rate
if c >= 0 && user.itemActive?
c = BattleHandlers.triggerCriticalCalcUserItem(user.item, user, target, c)
end
if c >= 0 && target.itemActive?
c = BattleHandlers.triggerCriticalCalcTargetItem(target.item, user, target, c)
end
return false if c < 0
# Move-specific "always/never a critical hit" effects
return false if pbCritialOverride(user,target) == -1
return true
end
end
class MKAI
def pbAIRandom(x)
return rand(x)
end
def pbDefaultChooseEnemyCommand(idxBattler)
sideIndex = idxBattler % 2
index = MKAI.battler_to_proj_index(idxBattler)
side = @sides[sideIndex]
projection = side.battlers[index]
# Choose move
data = projection.choose_move
if data.nil?
# Struggle
@battle.pbAutoChooseMove(idxBattler)
elsif data[0] == :ITEM
# [:ITEM, item_id, target&]
item = data[1]
# Determine target of item (always the Pokémon choosing the action)
useType = pbGetItemData(item, ITEM_BATTLE_USE)
if data[2]
target_index = data[2]
else
target_index = idxBattler
if useType && (useType == 1 || useType == 6) # Use on Pokémon
target_index = @battle.battlers[target_index].pokemonIndex # Party Pokémon
end
end
# Register our item
@battle.pbRegisterItem(idxBattler, item, target_index)
elsif data[0] == :SWITCH
# [:SWITCH, pokemon_index]
@battle.pbRegisterSwitch(idxBattler, data[1])
else
# [move_index, move_target]
move_index, move_target = data
# Mega evolve if we determine that we should
@battle.pbRegisterMegaEvolution(idxBattler) if projection.should_mega_evolve?(idxBattler)
# Register our move
@battle.pbRegisterMove(idxBattler, move_index, false)
# Register the move's target
@battle.pbRegisterTarget(idxBattler, move_target)
end
end
#=============================================================================
# Choose a replacement Pokémon
#=============================================================================
def pbDefaultChooseNewEnemy(idxBattler, party)
proj = self.battler_to_projection(@battle.battlers[idxBattler])
scores = proj.get_optimal_switch_choice
scores.each do |_, _, proj|
pkmn = proj.pokemon
index = @battle.pbParty(idxBattler).index(pkmn)
if @battle.pbCanSwitchLax?(idxBattler, index)
return index
end
end
return -1
end
end
class PokeBattle_Battler
alias mkai_pbInitialize pbInitialize
def pbInitialize(pkmn, idxParty, batonPass = false)
mkai_pbInitialize(pkmn, idxParty, batonPass)
ai = @battle.battleAI
sideIndex = @index % 2
ai.sides[sideIndex].send_out(@index, self)
end
alias mkai_pbFaint pbFaint
def pbFaint(*args)
mkai_pbFaint(*args)
@battle.battleAI.faint_battler(self)
end
end
class PokeBattle_PoisonMove
attr_reader :toxic
end
class Array
def sum
n = 0
self.each { |e| n += e }
n
end
end
# Overwrite Frisk to show the enemy held item
BattleHandlers::AbilityOnSwitchIn.add(:FRISK,
proc { |ability,battler,battle|
foes = []
battle.eachOtherSideBattler(battler.index) do |b|
foes.push(b) if b.item > 0
end
if foes.length > 0
battle.pbShowAbilitySplash(battler)
if NEWEST_BATTLE_MECHANICS
foes.each do |b|
battle.pbDisplay(_INTL("{1} frisked {2} and found its {3}!",
battler.pbThis, b.pbThis(true), PBItems.getName(b.item)))
battle.battleAI.reveal_item(b)
end
else
foe = foes[battle.pbRandom(foes.length)]
battle.pbDisplay(_INTL("{1} frisked the foe and found one {2}!",
battler.pbThis, PBItems.getName(foe.item)))
battle.battleAI.reveal_item(foe)
end
battle.pbHideAbilitySplash(battler)
end
}
)

View File

@@ -0,0 +1,133 @@
class MKAI
attr_reader :battle
attr_reader :sides
def initialize(battle, wild_battle)
@battle = battle
@sides = [Side.new(self, 0), Side.new(self, 1, wild_battle)]
MKAI.log("AI initialized")
end
def self.battler_to_proj_index(battlerIndex)
if battlerIndex % 2 == 0 # Player side: 0, 2, 4 -> 0, 1, 2
return battlerIndex / 2
else # Opponent side: 1, 3, 5 -> 0, 1, 2
return (battlerIndex - 1) / 2
end
end
def self.weighted_rand(weights)
num = rand(weights.sum)
for i in 0...weights.size
if num < weights[i]
return i
else
num -= weights[i]
end
end
return nil
end
def self.get_weights(factor, weights)
avg = weights.sum / weights.size.to_f
newweights = weights.map do |e|
diff = e - avg
next [0, ((e - diff * factor) * 100).round].max
end
return newweights
end
def self.weighted_factored_rand(factor, weights)
avg = weights.sum / weights.size.to_f
newweights = weights.map do |e|
diff = e - avg
next [0, ((e - diff * factor) * 100).round].max
end
return weighted_rand(newweights)
end
def self.log(msg)
echoln msg
end
def battler_to_projection(battler)
@sides.each do |side|
side.battlers.each do |projection|
if projection && projection.pokemon == battler.pokemon
return projection
end
end
side.party.each do |projection|
if projection && projection.pokemon == battler.pokemon
return projection
end
end
end
return nil
end
def pokemon_to_projection(pokemon)
@sides.each do |side|
side.battlers.each do |projection|
if projection && projection.pokemon == pokemon
return projection
end
end
side.party.each do |projection|
if projection && projection.pokemon == pokemon
return projection
end
end
end
return nil
end
def register_damage(move, user, target, damage)
user = battler_to_projection(user)
target = battler_to_projection(target)
user.register_damage_dealt(move, target, damage)
target.register_damage_taken(move, user, damage)
end
def faint_battler(battler)
# Remove the battler from the AI's list of the active battlers
@sides.each do |side|
side.battlers.each_with_index do |proj, index|
if proj && proj.battler == battler
# Decouple the projection from the battler
side.recall(battler.index)
side.battlers[index] = nil
break
end
end
end
end
def end_of_round
@sides.each { |side| side.end_of_round }
end
def reveal_ability(battler)
@sides.each do |side|
side.battlers.each do |proj|
if proj && proj.battler == battler && !proj.revealed_ability
proj.revealed_ability = true
MKAI.log("#{proj.pokemon.name}'s ability was revealed.")
break
end
end
end
end
def reveal_item(battler)
@sides.each do |side|
side.battlers.each do |proj|
if proj.battler == battler && !proj.revealed_item
proj.revealed_item = true
MKAI.log("#{proj.pokemon.name}'s item was revealed.")
break
end
end
end
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,68 @@
class MKAI
class Side
attr_reader :ai
attr_reader :index
attr_reader :battlers
attr_reader :party
attr_reader :trainers
attr_reader :flags
def initialize(ai, index, wild_pokemon = false)
@ai = ai
@index = index
@battle = @ai.battle
@wild_pokemon = wild_pokemon
@battlers = []
@party = []
@flags = {}
end
def effects
return @battle.sides[@index].effects
end
def set_party(party)
@party = party.map { |pokemon| BattlerProjection.new(self, pokemon, @wild_pokemon) }
end
def set_trainers(trainers)
@trainers = trainers
end
def opposing_side
return @ai.sides[1 - @index]
end
def recall(battlerIndex)
index = MKAI.battler_to_proj_index(battlerIndex)
proj = @battlers[index]
if proj.nil?
raise "Battler to be recalled was not found in the active battlers list."
end
if !proj.active?
raise "Battler to be recalled was not active."
end
@battlers[index] = nil
proj.battler = nil
end
def send_out(battlerIndex, battler)
proj = @party.find { |proj| proj && proj.pokemon == battler.pokemon }
if proj.nil?
raise "Battler to be sent-out was not found in the party list."
end
if proj.active?
raise "Battler to be sent-out was already sent out before."
end
index = MKAI.battler_to_proj_index(battlerIndex)
@battlers[index] = proj
proj.ai_index = index
proj.battler = battler
end
def end_of_round
@battlers.each { |proj| proj.end_of_round if proj }
@flags = {}
end
end
end

View File

@@ -0,0 +1,92 @@
class PokeBattle_AI
#=============================================================================
#
#=============================================================================
# AI skill levels:
# 0: Wild Pokémon
# 1-31: Basic trainer (young/inexperienced)
# 32-47: Some skill
# 48-99: High skill
# 100+: Best trainers (Gym Leaders, Elite Four, Champion)
# NOTE: A trainer's skill value can range from 0-255, but by default only four
# distinct skill levels exist. The skill value is typically the same as
# the trainer's base money value.
module AILevel
# Minimum skill level to be in each AI skill bracket.
def self.minimum; return 1; end
def self.medium; return 32; end
def self.high; return 48; end
def self.best; return 100; end
end
#=============================================================================
#
#=============================================================================
def initialize(battle)
@battle = battle
@skill = 0
@user = nil
@wildBattler = @battle.wildBattle? # Whether AI is choosing for a wild Pokémon
@roles = [Array.new(@battle.pbParty(0).length) { |i| determine_roles(0, i) },
Array.new(@battle.pbParty(1).length) { |i| determine_roles(1, i) }]
end
def pbAIRandom(x); return rand(x); end
def pbStdDev(choices)
sum = 0
n = 0
choices.each do |c|
sum += c[1]
n += 1
end
return 0 if n<2
mean = sum.to_f/n.to_f
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
# Decide whether the opponent should Mega Evolve their Pokémon
def pbEnemyShouldMegaEvolve?
if @battle.pbCanMegaEvolve?(@user.index) # Simple "always should if possible"
PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) will Mega Evolve")
return true
end
return false
end
# Choose an action
def pbDefaultChooseEnemyCommand(idxBattler)
set_up(idxBattler)
choices = pbGetMoveScores
return if pbEnemyShouldUseItem?
return if pbEnemyShouldWithdraw?
return if @battle.pbAutoFightMenu(idxBattler)
@battle.pbRegisterMegaEvolution(idxBattler) if pbEnemyShouldMegaEvolve?
pbChooseMove(choices)
end
# Set some class variables for the Pokémon whose action is being chosen
def set_up(idxBattler)
# TODO: Where relevant, pretend the user is Mega Evolved if it isn't but can
# be.
@user = @battle.battlers[idxBattler]
@wildBattler = (@battle.wildBattle? && @user.opposes?)
@skill = 0
if !@wildBattler
@skill = @battle.pbGetOwnerFromBattlerIndex(@user.index).skill || 0
@skill = AILevel.minimum if @skill < AILevel.minimum
end
end
def skill_check(threshold)
return @skill >= threshold
end
end

View File

@@ -0,0 +1,170 @@
class PokeBattle_AI
#=============================================================================
# Decide whether the opponent should use an item on the Pokémon
#=============================================================================
def pbEnemyShouldUseItem?
item, idxTarget = pbEnemyItemToUse
return false if !item
# Determine target of item (always the Pokémon choosing the action)
useType = GameData::Item.get(item).battle_use
if useType==1 || useType==6 # Use on Pokémon
idxTarget = @battle.battlers[idxTarget].pokemonIndex # Party Pokémon
end
# Register use of item
@battle.pbRegisterItem(@user.index,item,idxTarget)
PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) will use item #{GameData::Item.get(item).name}")
return true
end
# NOTE: The AI will only consider using an item on the Pokémon it's currently
# choosing an action for.
def pbEnemyItemToUse
return nil if !@battle.internalBattle
items = @battle.pbGetOwnerItems(@user.index)
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]
pkmn = battler.pokemon
# Item categories
hpItems = {
:POTION => 20,
:SUPERPOTION => 50,
:HYPERPOTION => 200,
:MAXPOTION => 999,
:BERRYJUICE => 20,
:SWEETHEART => 20,
:FRESHWATER => 50,
:SODAPOP => 60,
:LEMONADE => 80,
:MOOMOOMILK => 100,
:ORANBERRY => 10,
:SITRUSBERRY => battler.totalhp/4,
:ENERGYPOWDER => 50,
:ENERGYROOT => 200
}
hpItems[:RAGECANDYBAR] = 20 if !NEWEST_BATTLE_MECHANICS
fullRestoreItems = [
:FULLRESTORE
]
oneStatusItems = [ # Preferred over items that heal all status problems
:AWAKENING, :CHESTOBERRY, :BLUEFLUTE,
:ANTIDOTE, :PECHABERRY,
:BURNHEAL, :RAWSTBERRY,
:PARALYZEHEAL, :PARLYZHEAL, :CHERIBERRY,
:ICEHEAL, :ASPEARBERRY
]
allStatusItems = [
:FULLHEAL, :LAVACOOKIE, :OLDGATEAU, :CASTELIACONE, :LUMIOSEGALETTE,
:SHALOURSABLE, :BIGMALASADA, :LUMBERRY, :HEALPOWDER
]
allStatusItems.push(:RAGECANDYBAR) if NEWEST_BATTLE_MECHANICS
xItems = {
:XATTACK => [PBStats::ATTACK, (NEWEST_BATTLE_MECHANICS) ? 2 : 1],
:XATTACK2 => [PBStats::ATTACK, 2],
:XATTACK3 => [PBStats::ATTACK, 3],
:XATTACK6 => [PBStats::ATTACK, 6],
:XDEFENSE => [PBStats::DEFENSE, (NEWEST_BATTLE_MECHANICS) ? 2 : 1],
:XDEFENSE2 => [PBStats::DEFENSE, 2],
:XDEFENSE3 => [PBStats::DEFENSE, 3],
:XDEFENSE6 => [PBStats::DEFENSE, 6],
:XDEFEND => [PBStats::DEFENSE, (NEWEST_BATTLE_MECHANICS) ? 2 : 1],
:XDEFEND2 => [PBStats::DEFENSE, 2],
:XDEFEND3 => [PBStats::DEFENSE, 3],
:XDEFEND6 => [PBStats::DEFENSE, 6],
:XSPATK => [PBStats::SPATK, (NEWEST_BATTLE_MECHANICS) ? 2 : 1],
:XSPATK2 => [PBStats::SPATK, 2],
:XSPATK3 => [PBStats::SPATK, 3],
:XSPATK6 => [PBStats::SPATK, 6],
:XSPECIAL => [PBStats::SPATK, (NEWEST_BATTLE_MECHANICS) ? 2 : 1],
:XSPECIAL2 => [PBStats::SPATK, 2],
:XSPECIAL3 => [PBStats::SPATK, 3],
:XSPECIAL6 => [PBStats::SPATK, 6],
:XSPDEF => [PBStats::SPDEF, (NEWEST_BATTLE_MECHANICS) ? 2 : 1],
:XSPDEF2 => [PBStats::SPDEF, 2],
:XSPDEF3 => [PBStats::SPDEF, 3],
:XSPDEF6 => [PBStats::SPDEF, 6],
:XSPEED => [PBStats::SPEED, (NEWEST_BATTLE_MECHANICS) ? 2 : 1],
:XSPEED2 => [PBStats::SPEED, 2],
:XSPEED3 => [PBStats::SPEED, 3],
:XSPEED6 => [PBStats::SPEED, 6],
:XACCURACY => [PBStats::ACCURACY, (NEWEST_BATTLE_MECHANICS) ? 2 : 1],
:XACCURACY2 => [PBStats::ACCURACY, 2],
:XACCURACY3 => [PBStats::ACCURACY, 3],
:XACCURACY6 => [PBStats::ACCURACY, 6]
}
losthp = battler.totalhp - battler.hp
preferFullRestore = (battler.hp <= battler.totalhp * 2 / 3 &&
(battler.status != PBStatuses::NONE || battler.effects[PBEffects::Confusion] > 0))
# Find all usable items
usableHPItems = []
usableStatusItems = []
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)
# Log HP healing items
if losthp > 0
power = hpItems[i]
if power
usableHPItems.push([i, 5, power])
next
end
end
# Log Full Restores (HP healer and status curer)
if losthp > 0 || battler.status != PBStatuses::NONE
if fullRestoreItems.include?(i)
usableHPItems.push([i, (preferFullRestore) ? 3 : 7, 999])
usableStatusItems.push([i, (preferFullRestore) ? 3 : 9])
next
end
end
# Log single status-curing items
if oneStatusItems.include?(i)
usableStatusItems.push([i, 5])
next
end
# Log Full Heal-type items
if allStatusItems.include?(i)
usableStatusItems.push([i, 7])
next
end
# Log stat-raising items
if xItems[i]
data = xItems[i]
usableXItems.push([i, battler.stages[data[0]], data[1]])
next
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] }
prevItem = nil
usableHPItems.each do |i|
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] }
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] }
prevItem = nil
usableXItems.each do |i|
break if prevItem && i[1]>prevItem[1]
return i[0], idxTarget if i[1]+i[2]>=6
prevItem = i
end
return prevItem[0], idxTarget
end
return nil
end
end

View File

@@ -0,0 +1,178 @@
class PokeBattle_AI
#=============================================================================
# Decide whether the opponent should switch Pokémon
#=============================================================================
def pbEnemyShouldWithdraw?
return pbEnemyShouldWithdrawEx?(false)
end
def pbEnemyShouldWithdrawEx?(forceSwitch)
return false if @wildBattler
shouldSwitch = forceSwitch
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 !target.fainted? && target.lastMoveUsed &&
(target.level - @user.level).abs <= 6
moveData = GameData::Move.get(target.lastMoveUsed)
moveType = moveData.type
typeMod = pbCalcTypeMod(moveType, target, @user)
if PBTypeEffectiveness.superEffective?(typeMod) && moveData.base_damage > 50
switchChance = (moveData.base_damage > 70) ? 30 : 20
shouldSwitch = (pbAIRandom(100) < switchChance)
end
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
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|
next if m.function!="0ED" # Baton Pass
next if !@battle.pbCanChooseMove?(@user.index,i,false)
batonPass = i
break
end
end
# Pokémon will faint because of bad poisoning at the end of this round, but
# would survive at least one more round if it were regular poisoning instead
if @user.status==PBStatuses::POISON && @user.statusCount>0 &&
skill_check(AILevel.high)
toxicHP = @user.totalhp/16
nextToxicHP = toxicHP*(@user.effects[PBEffects::Toxic]+1)
if @user.hp<=nextToxicHP && @user.hp>toxicHP*2
shouldSwitch = true if pbAIRandom(100)<80
end
end
# Pokémon is Encored into an unfavourable move
if @user.effects[PBEffects::Encore]>0 && skill_check(AILevel.medium)
idxEncoredMove = @user.pbEncoredMoveIndex
if idxEncoredMove>=0
scoreSum = 0
scoreCount = 0
@user.eachOpposing do |b|
scoreSum += pbGetMoveScore(@user.moves[idxEncoredMove],b)
scoreCount += 1
end
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(@user.index+1)==1 &&
!@user.pbDirectOpposing.fainted? && skill_check(AILevel.high)
opp = @user.pbDirectOpposing
if opp.effects[PBEffects::HyperBeam]>0 ||
(opp.hasActiveAbility?(:TRUANT) && opp.effects[PBEffects::Truant])
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
shouldSwitch = true
elsif @user.hp<=@user.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
shouldSwitch = true
end
if shouldSwitch
list = []
@battle.pbParty(@user.index).each_with_index do |pkmn,i|
next if !@battle.pbCanSwitch?(@user.index,i)
# If perish count is 1, it may be worth it to switch
# even with Spikes, since Perish Song's effect will end
if @user.effects[PBEffects::PerishSong]!=1
# Will contain effects that recommend against switching
spikes = @user.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
next if !pkmn.hasType?(:FLYING) && !pkmn.hasActiveAbility?(:LEVITATE)
end
end
end
# moveType is the type of the target's last used move
if moveType && PBTypeEffectiveness.ineffective?(pbCalcTypeMod(moveType,@user,@user))
weight = 65
typeMod = pbCalcTypeModPokemon(pkmn,@user.pbDirectOpposing(true))
if PBTypeEffectiveness.superEffective?(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 && PBTypeEffectiveness.resistant?(pbCalcTypeMod(moveType,@user,@user))
weight = 40
typeMod = pbCalcTypeModPokemon(pkmn,@user.pbDirectOpposing(true))
if PBTypeEffectiveness.superEffective?(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
else
list.push(i) # put this Pokemon last
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")
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}")
return
end
end
end
return false
end
#=============================================================================
# Choose a replacement Pokémon (called directly from @battle, not part of
# action choosing)
#=============================================================================
def pbDefaultChooseNewEnemy(idxBattler,party)
set_up(idxBattler)
enemies = []
party.each_with_index do |_p,i|
enemies.push(i) if @battle.pbCanSwitchLax?(idxBattler,i)
end
return -1 if enemies.length==0
return pbChooseBestNewEnemy(idxBattler,party,enemies)
end
def pbChooseBestNewEnemy(idxBattler,party,enemies)
return -1 if !enemies || enemies.length==0
best = -1
bestSum = 0
enemies.each do |i|
pkmn = party[i]
sum = 0
pkmn.moves.each do |m|
next if m.base_damage == 0
@battle.battlers[idxBattler].eachOpposing do |b|
bTypes = b.pbTypes(true)
sum += PBTypes.getCombinedEffectiveness(m.type, bTypes[0], bTypes[1], bTypes[2])
end
end
if best==-1 || sum>bestSum
best = i
bestSum = sum
end
end
return best
end
end

View File

@@ -0,0 +1,497 @@
<<<class PokeBattle_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
# Get scores and targets for each move
choices = []
# TODO: Split this into two, the first part being the calculation of all
# predicted damages and the second part being the score calculations
# (which are based on the predicted damages). Note that this requires
# saving each of the scoresAndTargets entries in here rather than in
# def pbRegisterMoveTrainer, and only at the very end are they
# whittled down to one per move which are chosen from. Multi-target
# moves could be fiddly since damages should be calculated for each
# target but they're all related.
@user.eachMoveWithIndex do |_m, i|
next if !@battle.pbCanChooseMove?(@user.index, i, false)
if @wildBattler
pbRegisterMoveWild(i, choices)
else
pbRegisterMoveTrainer(i, choices)
end
end
# Log the available choices
if $INTERNAL
logMsg = "[AI] Move choices for #{@user.pbThis(true)} (#{@user.index}): "
choices.each_with_index do |c, i|
logMsg += "#{@user.moves[c[0]].name}=#{c[1]}"
logMsg += " (target #{c[2]})" if c[2] >= 0
logMsg += ", " if i < choices.length-1
end
PBDebug.log(logMsg)
end
return choices
end
#=============================================================================
# Get scores for the given move against each possible target
#=============================================================================
# Wild Pokémon choose their moves randomly.
def pbRegisterMoveWild(idxMove, choices)
score = 100
# Doubly prefer one of the user's moves (the choice is random but consistent
# and does not correlate to any other property of the user)
score *= 2 if @user.pokemon.personalID % @user.moves.length == idxMove
choices.push([idxMove, score, -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]
targetType = move.pbTarget(@user)
# TODO: Alter targetType if user has Protean and move is Curse.
if PBTargets.multipleTargets?(targetType)
# Move affects multiple battlers and you don't choose a particular one
totalScore = 0
@battle.eachBattler do |b|
next if !@battle.pbMoveCanTarget?(@user.index, b.index, targetType)
score = pbGetMoveScore(move, b)
totalScore += ((@user.opposes?(b)) ? score : -score)
end
choices.push([idxMove, totalScore, -1]) if totalScore > 0
elsif PBTargets.noTargets?(targetType)
# Move has no targets, affects the user, a side or the whole field
score = pbGetMoveScore(move)
choices.push([idxMove, score, -1]) if score > 0
else
# Move affects one battler and you have to choose which one
scoresAndTargets = []
@battle.eachBattler do |b|
next if !@battle.pbMoveCanTarget?(@user.index, b.index, targetType)
next if PBTargets.canChooseFoeTarget?(targetType) && !@user.opposes?(b)
score = pbGetMoveScore(move, b)
scoresAndTargets.push([score, b.index]) if score > 0
end
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]])
end
end
end
#=============================================================================
# Set some class variables for the move being assessed
#=============================================================================
def set_up_move_check(move, target)
@move = move
@target = target
# TODO: Calculate pbRoughType once here.
# Determine whether user or target is faster, and store that result so it
# doesn't need recalculating
if @target
user_speed = pbRoughStat(@user, PBStats::SPEED)
target_speed = pbRoughStat(@target, PBStats::SPEED)
@user_faster = (user_speed > target_speed) ^ (@battle.field.effects[PBEffects::TrickRoom] > 0)
else
@user_faster = false # Won't be used if there is no target
end
end
#=============================================================================
# Get a score for the given move being used against the given target
#=============================================================================
def pbGetMoveScore(move, target = nil)
set_up_move_check(move, target)
# Get the base score for the move
if @move.damagingMove?
# Is also the predicted damage amount as a percentage of target's current HP
score = pbGetDamagingMoveBaseScore
else # Status moves
# Depends on the move's effect
score = pbGetStatusMoveBaseScore
end
# Modify the score according to the move's effect
score = pbGetMoveScoreFunctions(score)
# A score of 0 here means it absolutely should not be used
return 0 if score <= 0
# TODO: High priority checks:
# => Prefer move if it will KO the target (moreso if user is slower than target)
# => Don't prefer damaging move if it won't KO, user has Stance Change and
# is in shield form, and user is slower than the target
# => Check memory for past damage dealt by a target's non-high priority move,
# and prefer move if user is slower than the target and another hit from
# the same amount will KO the user
# => Check memory for past damage dealt by a target's priority move, and don't
# prefer the move if user is slower than the target and can't move faster
# than it because of priority
# => Discard move if user is slower than the target and target is semi-
# invulnerable (and move won't hit it)
# => Check memory for whether target has previously used Quick Guard, and
# don't prefer move if so
# TODO: Low priority checks:
# => Don't prefer move if user is faster than the target
# => Prefer move if user is faster than the target and target is semi-
# invulnerable
# Don't prefer a dancing move if the target has the Dancer ability
# TODO: Check all battlers, not just the target.
if skill_check(AILevel.high) && @move.danceMove? && @target.hasActiveAbility?(:DANCER)
score /= 2
end
# TODO: Check memory for whether target has previously used Ion Deluge, and
# don't prefer move if it's Normal-type and target is immune because
# of its ability (Lightning Rod, etc.).
# TODO: Discard move if it can be redirected by a non-target's ability
# (Lightning Rod/Storm Drain). Include checking for a previous use of
# Ion Deluge and this move being Normal-type.
# => If non-target is a user's ally, don't prefer move (rather than discarding
# it)
# TODO: Discard move if it's sound-based and user has been Throat Chopped.
# Don't prefer move if user hasn't been Throat Chopped but target has
# previously used Throat Chop. The first part of this would probably
# go elsewhere (damage calc?).
# TODO: Prefer move if it has a high critical hit rate, critical hits are
# possible but not certain, and target has raised defences/user has
# lowered offences (Atk/Def or SpAtk/SpDef, whichever is relevant).
# TODO: Don't prefer damaging moves if target is Destiny Bonding.
# => Also don't prefer damaging moves if user is slower than the target, move
# is likely to be lethal, and target has previously used Destiny Bond
# TODO: Don't prefer a move that is stopped by Wide Guard if target has
# previously used Wide Guard.
# TODO: Don't prefer Fire-type moves if target has previously used Powder.
# TODO: Don't prefer contact move if making contact with the target could
# trigger an effect that's bad for the user (Static, etc.).
# => Also check if target has previously used Spiky Shield.King's Shield/
# Baneful Bunker, and don't prefer move if so
# TODO: Prefer a contact move if making contact with the target could trigger
# an effect that's good for the user (Poison Touch/Pickpocket).
# TODO: Don't prefer a status move if user has a damaging move that will KO
# the target.
# => If target has previously used a move that will hurt the user by 30% of
# its current HP or more, moreso don't prefer a status move.
if skill_check(AILevel.medium)
# Prefer damaging moves if AI has no more Pokémon or AI is less clever
if @battle.pbAbleNonActiveCount(@user.idxOwnSide) == 0
if !(skill_check(AILevel.high) && @battle.pbAbleNonActiveCount(@target.idxOwnSide) > 0)
if @move.statusMove?
score *= 0.9
elsif @target.hp <= @target.totalhp / 2
score *= 1.1
end
end
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.effects[PBEffects::SkyDrop] != @user.index
miss = false if @move.hitsFlyingTargets?
else
if @target.inTwoTurnAttack?("0C9", "0CC", "0CE") # Fly, Bounce, Sky Drop
miss = false if @move.hitsFlyingTargets?
elsif @target.inTwoTurnAttack?("0CA") # Dig
miss = false if @move.hitsDiggingTargets?
elsif @target.inTwoTurnAttack?("0CB") # Dive
miss = false if @move.hitsDivingTargets?
end
end
end
score = 0 if miss
end
# Pick a good move for the Choice items
if @user.hasActiveItem?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF])
# Really don't prefer status moves (except Trick)
score *= 0.1 if @move.statusMove? && @move.function != "0F2" # Trick
# Don't prefer moves of certain types
move_type = pbRoughType(@move)
# Most unpreferred types are 0x effective against another type, except
# Fire/Water/Grass
# TODO: Actually check through the types for 0x instead of hardcoding
# them.
# TODO: Reborn separately doesn't prefer Fire/Water/Grass/Electric, also
# with a 0.95x score, meaning Electric can be 0.95x twice. Why are
# these four types not preferred? Maybe because they're all not
# very effective against Dragon.
unpreferred_types = [:NORMAL, :FIGHTING, :POISON, :GROUND, :GHOST,
:FIRE, :WATER, :GRASS, :ELECTRIC, :PSYCHIC, :DRAGON]
score *= 0.95 if unpreferred_types.include?(move_type)
# Don't prefer moves with lower accuracy
score *= @move.accuracy / 100.0 if @move.accuracy > 0
# Don't prefer moves with low PP
score *= 0.9 if @move.pp < 6
end
# If user is asleep, don't prefer moves that can't be used while asleep
if skill_check(AILevel.medium) && @user.asleep? &&
@user.statusCount > 1 && !@move.usableWhenAsleep?
score *= 0.2
end
# If user is frozen, prefer a move that can thaw the user
if skill_check(AILevel.medium) && @user.status == PBStatuses::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 == PBStatuses::FROZEN
if pbRoughType(@move) == :FIRE || (NEWEST_BATTLE_MECHANICS && @move.thawsUser?)
score *= 0.1
end
end
end
# Don't prefer hitting a wild shiny Pokémon
if @battle.wildBattle? && @target.opposes? && @target.shiny?
score *= 0.15
end
# TODO: Discard a move that can be Magic Coated if either opponent has Magic
# Bounce.
# Account for accuracy of move
accuracy = pbRoughAccuracy(@move, @target)
score *= accuracy / 100.0
# Prefer flinching external effects (note that move effects which cause
# flinching are dealt with in the function code part of score calculation)
if skill_check(AILevel.medium)
if !@target.hasActiveAbility?([:INNERFOCUS, :SHIELDDUST]) &&
@target.effects[PBEffects::Substitute] == 0
can_flinch = false
if @move.canKingsRock? && @user.hasActiveItem?([:KINGSROCK, :RAZORFANG])
can_flinch = true
elsif @user.hasActiveAbility?(:STENCH) && !@move.flinchingMove?
can_flinch = true
end
calc_damage *= 1.3 if can_flinch
end
end
score = score.to_i
score = 0 if score < 0
return score
end
#=============================================================================
# Calculate how much damage a move is likely to do to a given target (as a
# percentage of the target's current HP)
#=============================================================================
def pbGetDamagingMoveBaseScore
# Don't prefer moves that are ineffective because of abilities or effects
return 0 if pbCheckMoveImmunity(@move, @target)
# Calculate how much damage the move will do (roughly)
base_damage = pbMoveBaseDamage(@move, @target)
calc_damage = pbRoughDamage(@move, @target, base_damage)
# TODO: Maybe move this check elsewhere? Note that Reborn's base score does
# not include this halving, but the predicted damage does.
# Two-turn attacks waste 2 turns to deal one lot of damage
calc_damage /= 2 if @move.chargingTurnMove?
# TODO: Maybe move this check elsewhere?
# Increased critical hit rate
if skill_check(AILevel.medium)
crit_stage = pbRoughCriticalHitStage(@move, @target)
if crit_stage >= 0
crit_fraction = (crit_stage > 50) ? 1 : PokeBattle_Move::CRITICAL_HIT_RATIOS[crit_stage]
crit_mult = (NEWEST_BATTLE_MECHANICS) ? 0.5 : 1
calc_damage *= (1 + crit_mult / crit_fraction)
end
end
# Convert damage to percentage of target's remaining HP
damage_percentage = calc_damage * 100.0 / @target.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
# Adjust score
damage_percentage = 110 if damage_percentage > 110 # Treat all lethal moves the same
damage_percentage += 40 if damage_percentage > 100 # Prefer moves likely to be lethal
score = damage_percentage.to_i
return score
end
def pbGetStatusMoveBaseScore
# TODO: Call pbCheckMoveImmunity here too, not just for damaging moves
# (only if this status move will be affected).
# TODO: Make sure all status moves are accounted for.
# TODO: Duplicates:
# 003 cause sleep - Dark Void (15), Grass Whistle (15), Hypnosis (15), Sing (15),
# Lovely Kiss (20), Sleep Powder (20), Spore (60)
# 005 poisons - Poison Powder (15), Poison Gas (20)
# 007 paralyses - Stun Spore (25), Glare (30), Thunder Wave (30)
# 013 confuses - Teeter Dance (5), Supersonic (10), Sweet Kiss (20), Confuse Ray (25)
# 01C user's Atk +1 - Howl (10), Sharpen (10), Medicate (15)
# 030 user's Spd +2 - Agility (15), Rock Polish (25)
# 042 target Atk -1 - Growl (10), Baby-Doll Eyes (15)
# 047 target acc -1 - Sand Attack (5), Flash (10), Kinesis (10), Smokescreen (10)
# 04B target Atk -2 - Charm (10), Feather Dance (15)
# 04D target Spd -2 - String Shot (10), Cotton Spore (15), Scary Face (15)
# 04F target SpDef -2 - Metal Sound (10), Fake Tears (15)
case @move.function
when "013", "047", "049", "052", "053", "057", "058", "059", "05E", "061",
"062", "066", "067", "09C", "09D", "09E", "0A6", "0A7", "0A8", "0AB",
"0AC", "0B1", "0B2", "0B8", "0BB", "0E6", "0E8", "0F6", "0F9", "10F",
"114", "118", "119", "120", "124", "138", "13E", "13F", "143", "152",
"15E", "161", "16A", "16B"
return 5
when "013", "01C", "01D", "01E", "023", "027", "028", "029", "037", "042",
"043", "047", "04B", "04D", "04F", "051", "055", "060", "0B7", "0F8",
"139", "13A", "13C", "148"
return 10
when "003", "005", "018", "01C", "021", "022", "030", "042", "04A", "04B",
"04C", "04D", "04E", "04F", "05C", "05D", "065", "0B0", "0B5", "0DB",
"0DF", "0E3", "0E4", "0FF", "100", "101", "102", "137", "13D", "140",
"142", "151", "15C", "16E"
return 15
when "003", "004", "005", "013", "040", "041", "054", "056", "05F", "063",
"064", "068", "069", "0AE", "0AF", "0B6", "0D9", "0DA", "0E5", "0EB",
"0EF", "145", "146", "159"
return 20
when "006", "007", "00A", "013", "016", "01B", "02A", "02F", "030", "031",
"033", "034", "038", "03A", "05A", "0AA", "0B9", "0BA", "0D5", "0D6",
"0D7", "0D8", "0DC", "0E7", "0F2", "10C", "112", "117", "141", "160",
"16D"
return 25
when "007", "024", "025", "02C", "0B3", "0B4", "0BC", "0ED", "103", "104",
"105", "10D", "11F", "14C", "154", "155", "156", "15B", "173"
return 30
when "019", "02E", "032", "039", "05B", "0A2", "0A3", "149", "14B", "168"
return 35
when "026", "02B", "035", "036", "14E"
return 40
when "003", "153", "167"
return 60
end
# "001", "01A", "048", "0A1", "0E2", "0EA", "0F3", "10E", "11A", "11D",
# "11E", "14A"
return 0
end
end

View File

@@ -0,0 +1,431 @@
class PokeBattle_AI
#=============================================================================
# Apply additional effect chance to a move's score
# TODO: Apply all the additional effect chance modifiers.
#=============================================================================
def apply_effect_chance_to_score(score)
if @move.damagingMove?
# TODO: Doesn't return the correct value for "014" (Chatter).
effect_chance = @move.addlEffect
if effect_chance > 0
effect_chance *= 2 if @user.hasActiveAbility?(:SERENEGRACE) ||
@user.pbOwnSide.effects[PBEffects::Rainbow] > 0
effect_multiplier = [effect_chance.to_f, 100].min / 100
score = ((score - 1) * effect_multiplier) + 1
end
end
return score
end
#=============================================================================
#
#=============================================================================
# TODO: These function codes need to have an attr_reader :statUp and for them
# to be set when the move is initialised.
# 035 Shell Smash
# 037 Acupressure
# 137 Magnetic Flux
# 15C Gear Up
def calc_user_stat_raise_mini_score
mini_score = 1.0
# Determine whether the move boosts Attack, Special Attack or Speed (Bulk Up
# is sometimes not considered a sweeping move)
sweeping_stat = false
offensive_stat = false
@move.stat_up.each_with_index do |stat, idx|
next if idx.odd?
next if ![:ATTACK, :SPATK, :SPEED].include?(stat)
sweeping_stat = true
next if @move.function == "024" # Bulk Up (+Atk +Def)
offensive_stat = true
break
end
# Prefer if user has most of its HP
if @user.hp >= @user.totalhp * 3 / 4
mini_score *= (sweeping_stat) ? 1.2 : 1.1
end
# Prefer if user hasn't been in battle for long
if @user.turnCount < 2
mini_score *= (sweeping_stat) ? 1.2 : 1.1
end
# Prefer if user has the ability Simple
mini_score *= 2 if @user.hasActiveAbility?(:SIMPLE)
# TODO: Prefer if user's moves won't do much damage.
# Prefer if user has something that will limit damage taken
mini_score *= 1.3 if @user.effects[PBEffects::Substitute] > 0 ||
(@user.form == 0 && @user.ability_id == :DISGUISE)
# Don't prefer if user doesn't have much HP left
mini_score *= 0.3 if @user.hp < @user.totalhp / 3
# Don't prefer if user is badly poisoned
mini_score *= 0.2 if @user.effects[PBEffects::Toxic] > 0 && !offensive_stat
# Don't prefer if user is confused
if @user.effects[PBEffects::Confusion] > 0
# TODO: Especially don't prefer if the move raises Atk. Even more so if
# the move raises the stat by 2+. Not quite so much if the move also
# raises Def.
mini_score *= 0.5
end
# Don't prefer if user is infatuated or Leech Seeded
if @user.effects[PBEffects::Attract] >= 0 || @user.effects[PBEffects::LeechSeed] >= 0
mini_score *= (offensive_stat) ? 0.6 : 0.3
end
# Don't prefer if user has an ability or item that will force it to switch
# out
if @user.hp < @user.totalhp * 3 / 4
mini_score *= 0.3 if @user.hasActiveAbility?([:EMERGENCYEXIT, :WIMPOUT])
mini_score *= 0.3 if @user.hasActiveItem?(:EJECTBUTTON)
end
# Prefer if target has a status problem
if @target.status != PBStatuses::NONE
mini_score *= (sweeping_stat) ? 1.2 : 1.1
case @target.status
when PBStatuses::SLEEP, PBStatuses::FROZEN
mini_score *= 1.3
when PBStatuses::BURN
# TODO: Prefer if the move boosts Sp Def.
mini_score *= 1.1 if !offensive_stat
end
end
# Prefer if target is yawning
if @target.effects[PBEffects::Yawn] > 0
mini_score *= (sweeping_stat) ? 1.7 : 1.3
end
# Prefer if target is recovering after Hyper Beam
if @target.effects[PBEffects::HyperBeam] > 0
mini_score *= (sweeping_stat) ? 1.3 : 1.2
end
# Prefer if target is Encored into a status move
if @target.effects[PBEffects::Encore] > 0 &&
GameData::Move.get(@target.effects[PBEffects::EncoreMove]).category == 2 # Status move
# TODO: Why should this check greatly prefer raising both the user's defences?
if sweeping_stat || @move.function == "02A" # +Def +SpDef
mini_score *= 1.5
else
mini_score *= 1.3
end
end
# TODO: Don't prefer if target has previously used a move that would force
# the user to switch (or Yawn/Perish Song which encourage it). Prefer
# instead if the move raises evasion. Note this comes after the
# dissociation of Bulk Up from sweeping_stat.
if skill_check(AILevel.medium)
# TODO: Prefer if the maximum damage the target has dealt wouldn't hurt
# the user much.
end
# Don't prefer if foe's side is able to use a boosted Retaliate
# TODO: I think this is what Reborn means. Reborn doesn't check for the
# existence of the move Retaliate, just whether it can be boosted.
if @user.pbOpposingSide.effects[PBEffects::LastRoundFainted] == @battle.turnCount - 1
mini_score *= 0.3
end
# Don't prefer if it's not a single battle
if !@battle.singleBattle?
mini_score *= (offensive_stat) ? 0.25 : 0.5
end
return mini_score
end
#=============================================================================
#
#=============================================================================
# TODO: This method doesn't take the increment into account but should.
def calc_user_stat_raise_one(stat, increment)
mini_score = 1.0
# Ignore if user won't benefit from the stat being raised
# TODO: Exception if user knows Baton Pass? Exception if user knows Power Trip?
case stat
when PBStats::ATTACK
has_physical_move = false
@user.eachMove do |m|
next if !m.physicalMove?(m.type) || m.function == "121" # Foul Play
has_physical_move = true
break
end
return mini_score if !has_physical_move
when PBStats::SPATK
has_special_move = false
@user.eachMove do |m|
next if !m.specialMove?(m.type)
has_special_move = true
break
end
return mini_score if !has_special_move
end
case stat
when PBStats::ATTACK
# Prefer if user can definitely survive a hit no matter how powerful, and
# it won't be hurt by weather
if @user.hp == @user.totalhp &&
(@user.hasActiveItem?(:FOCUSSASH) || @user.hasActiveAbility?(:STURDY))
if !(@battle.pbWeather == PBWeather::Sandstorm && @user.takesSandstormDamage?) &&
!(@battle.pbWeather == PBWeather::Hail && @user.takesHailDamage?) &&
!(@battle.pbWeather == PBWeather::ShadowSky && @user.takesShadowSkyDamage?)
mini_score *= 1.4
end
end
# Prefer if user has the Sweeper role
# TODO: Is 1.1x for 025 Coil (+Atk, +Def, +acc).
mini_score *= 1.3 if check_battler_role(@user, BattleRole::SWEEPER)
# Don't prefer if user is burned or paralysed
mini_score *= 0.5 if @user.status == PBStatuses::BURN || @user.status == PBStatuses::PARALYSIS
# Don't prefer if user's Speed stat is lowered
sum_stages = @user.stages[PBStats::SPEED]
mini_score *= 1 + sum_stages * 0.05 if sum_stages < 0
# TODO: Prefer if target has previously used a HP-restoring move.
# TODO: Don't prefer if some of foes' stats are raised
sum_stages = 0
[PBStats::ATTACK, PBStats::SPATK, PBStats::SPEED].each do |s|
sum_stages += @target.stages[s]
end
mini_score *= 1 - sum_stages * 0.05 if sum_stages > 0
# TODO: Don't prefer if target has Speed Boost (+Spd at end of each round).
mini_score *= 0.6 if @target.hasActiveAbility?(:SPEEDBOOST)
# TODO: Don't prefer if target has previously used a move that benefits
# from user's Attack being boosted.
mini_score *= 0.3 if check_for_move(@target) { |move| move.function == "121" } # Foul Play
# TODO: Don't prefer if the target has previously used a priority move.
when PBStats::DEFENSE
# Prefer if user has a healing item
# TODO: Is 1.1x for 025 Coil (+Atk, +Def, +acc).
mini_score *= 1.2 if @user.hasActiveItem?(:LEFTOVERS) ||
(@user.hasActiveItem?(:BLACKSLUDGE) && @user.pbHasType?(:POISON))
# Prefer if user knows any healing moves
# TODO: Is 1.2x for 025 Coil (+Atk, +Def, +acc).
mini_score *= 1.3 if check_for_move(@user) { |move| move.healingMove? }
# Prefer if user knows Pain Split or Leech Seed
# TODO: Leech Seed is 1.2x for 025 Coil (+Atk, +Def, +acc).
mini_score *= 1.2 if @user.pbHasMoveFunction?("05A") # Pain Split
mini_score *= 1.3 if @user.pbHasMoveFunction?("0DC") # Leech Seed
# Prefer if user has certain roles
# TODO: Is 1.1x for 025 Coil (+Atk, +Def, +acc).
mini_score *= 1.3 if check_battler_role(@user, BattleRole::PHYSICALWALL, BattleRole::SPECIALWALL)
# Don't prefer if user is badly poisoned
mini_score *= 0.2 if @user.effects[PBEffects::Toxic] > 0
# Don't prefer if user's Defense stat is raised
sum_stages = @user.stages[PBStats::DEFENSE]
mini_score *= 1 - sum_stages * 0.15 if sum_stages > 0
# TODO: Prefer if foes have higher Attack than Special Attack, and user
# doesn't have a wall role, user is faster and user has at least 75%
# HP. Don't prefer instead if user is slower (ignore HP).
# TODO: Don't prefer if previous damage done by foes wouldn't hurt the
# user much.
when PBStats::SPEED
# Prefer if user can definitely survive a hit no matter how powerful, and
# it won't be hurt by weather
if @user.hp == @user.totalhp &&
(@user.hasActiveItem?(:FOCUSSASH) || @user.hasActiveAbility?(:STURDY))
if !(@battle.pbWeather == PBWeather::Sandstorm && @user.takesSandstormDamage?) &&
!(@battle.pbWeather == PBWeather::Hail && @user.takesHailDamage?) &&
!(@battle.pbWeather == PBWeather::ShadowSky && @user.takesShadowSkyDamage?)
mini_score *= 1.4
end
end
# Prefer if user's Attack/SpAtk stat (whichever is higher) is lowered
# TODO: Why?
if @user.attack > @user.spatk
sum_stages = @user.stages[PBStats::ATTACK]
mini_score *= 1 - sum_stages * 0.05 if sum_stages < 0
else
sum_stages = @user.stages[PBStats::SPATK]
mini_score *= 1 - sum_stages * 0.05 if sum_stages < 0
end
# Prefer if user has lowered Speed
# TODO: Is a flat 1.3x for 026 Dragon Dance (+Atk, +Spd).
sum_stages = @user.stages[PBStats::SPEED]
mini_score *= 1 - sum_stages * 0.05 if sum_stages < 0
# Prefer if user has Moxie
mini_score *= 1.3 if @user.hasActiveAbility?(:MOXIE)
# Prefer if user has the Sweeper role
mini_score *= 1.3 if check_battler_role(@user, BattleRole::SWEEPER)
# Don't prefer if user is burned or paralysed
mini_score *= 0.2 if @user.status == PBStatuses::PARALYSIS
# Don't prefer if user has Speed Boost
mini_score *= 0.6 if @user.hasActiveAbility?(:SPEEDBOOST)
# TODO: Don't prefer if target has raised defenses.
sum_stages = 0
[PBStats::DEFENSE, PBStats::SPDEF].each { |s| sum_stages += @target.stages[s] }
mini_score *= 1 - sum_stages * 0.05 if sum_stages > 0
# TODO: Don't prefer if the target has previously used a priority move.
# TODO: Don't prefer if Trick Room applies or any foe has previously used
# Trick Room.
mini_score *= 0.2 if @battle.field.effects[PBEffects::TrickRoom] > 0
# TODO: Don't prefer if user is already faster than the target. Exception
# for moves that benefit from a raised user's Speed?
# TODO: Don't prefer if user is already faster than the target and there's
# only 1 unfainted foe (this check is done by Agility/Autotomize
# (both +2 Spd) only in Reborn.)
when PBStats::SPATK
# Prefer if user can definitely survive a hit no matter how powerful, and
# it won't be hurt by weather
if @user.hp == @user.totalhp &&
(@user.hasActiveItem?(:FOCUSSASH) || @user.hasActiveAbility?(:STURDY))
if !(@battle.pbWeather == PBWeather::Sandstorm && @user.takesSandstormDamage?) &&
!(@battle.pbWeather == PBWeather::Hail && @user.takesHailDamage?) &&
!(@battle.pbWeather == PBWeather::ShadowSky && @user.takesShadowSkyDamage?)
mini_score *= 1.4
end
end
# Prefer if user has the Sweeper role
mini_score *= 1.3 if check_battler_role(@user, BattleRole::SWEEPER)
# Don't prefer if user's Speed stat is lowered
sum_stages = @user.stages[PBStats::SPEED]
mini_score *= 1 + sum_stages * 0.05 if sum_stages < 0
# TODO: Prefer if target has previously used a HP-restoring move.
# TODO: Don't prefer if some of foes' stats are raised
sum_stages = 0
[PBStats::ATTACK, PBStats::SPATK, PBStats::SPEED].each do |s|
sum_stages += @target.stages[s]
end
mini_score *= 1 - sum_stages * 0.05 if sum_stages > 0
# TODO: Don't prefer if target has Speed Boost (+Spd at end of each round)
mini_score *= 0.6 if @target.hasActiveAbility?(:SPEEDBOOST)
# TODO: Don't prefer if the target has previously used a priority move.
when PBStats::SPDEF
# Prefer if user has a healing item
mini_score *= 1.2 if @user.hasActiveItem?(:LEFTOVERS) ||
(@user.hasActiveItem?(:BLACKSLUDGE) && @user.pbHasType?(:POISON))
# Prefer if user knows any healing moves
mini_score *= 1.3 if check_for_move(@user) { |move| move.healingMove? }
# Prefer if user knows Pain Split or Leech Seed
mini_score *= 1.2 if @user.pbHasMoveFunction?("05A") # Pain Split
mini_score *= 1.3 if @user.pbHasMoveFunction?("0DC") # Leech Seed
# Prefer if user has certain roles
mini_score *= 1.3 if check_battler_role(@user, BattleRole::PHYSICALWALL, BattleRole::SPECIALWALL)
# Don't prefer if user's Defense stat is raised
sum_stages = @user.stages[PBStats::SPDEF]
mini_score *= 1 - sum_stages * 0.15 if sum_stages > 0
# TODO: Prefer if foes have higher Special Attack than Attack.
# TODO: Don't prefer if previous damage done by foes wouldn't hurt the
# user much.
when PBStats::ACCURACY
# Prefer if user knows any weaker moves
mini_score *= 1.1 if check_for_move(@user) { |move| move.damagingMove? && move.basedamage < 95 }
# Prefer if target has a raised evasion
sum_stages = @target.stages[PBStats::EVASION]
mini_score *= 1 + sum_stages * 0.05 if sum_stages > 0
# Prefer if target has an item that lowers foes' accuracy
mini_score *= 1.1 if @target.hasActiveItem?([:BRIGHTPOWDER, :LAXINCENSE])
# Prefer if target has an ability that lowers foes' accuracy
# TODO: Tangled Feet while user is confused?
if (@battle.pbWeather == PBWeather::Sandstorm && @target.hasActiveAbility?(:SANDVEIL)) ||
(@battle.pbWeather == PBWeather::Hail && @target.hasActiveAbility?(:SNOWCLOAK))
mini_score *= 1.1
end
when PBStats::EVASION
# Prefer if user has a healing item
mini_score *= 1.2 if @user.hasActiveItem?(:LEFTOVERS) ||
(@user.hasActiveItem?(:BLACKSLUDGE) && @user.pbHasType?(:POISON))
# Prefer if user has an item that lowers foes' accuracy
mini_score *= 1.3 if @user.hasActiveItem?([:BRIGHTPOWDER, :LAXINCENSE])
# Prefer if user has an ability that lowers foes' accuracy
# TODO: Tangled Feet while user is confused?
if (@battle.pbWeather == PBWeather::Sandstorm && @user.hasActiveAbility?(:SANDVEIL)) ||
(@battle.pbWeather == PBWeather::Hail && @user.hasActiveAbility?(:SNOWCLOAK))
mini_score *= 1.3
end
# Prefer if user knows any healing moves
mini_score *= 1.3 if check_for_move(@user) { |move| move.healingMove? }
# Prefer if user knows Pain Split or Leech Seed
mini_score *= 1.2 if @user.pbHasMoveFunction?("05A") # Pain Split
mini_score *= 1.3 if @user.pbHasMoveFunction?("0DC") # Leech Seed
# Prefer if user has certain roles
mini_score *= 1.3 if check_battler_role(@user, BattleRole::PHYSICALWALL, BattleRole::SPECIALWALL)
# TODO: Don't prefer if user's evasion stat is raised
# TODO: Don't prefer if target has No Guard.
mini_score *= 0.2 if @target.hasActiveAbility?(:NOGUARD)
# TODO: Don't prefer if target has previously used any moves that never miss.
end
# Don't prefer if user has Contrary
mini_score *= 0.5 if @user.hasActiveAbility?(:CONTRARY)
# TODO: Don't prefer if target has Unaware? Reborn resets mini_score to 1.
# This check needs more consideration. Note that @target is user for
# status moves, so that part is wrong.
# TODO: Is 0x for 025, 026, 026 (all moves that raise multiple stats)
mini_score *= 0.5 if @move.statusMove? && @target.hasActiveAbility?(:UNAWARE)
# TODO: Don't prefer if any foe has previously used a stat stage-clearing
# move (050, 051 Clear Smog/Haze).
mini_score *= 0.3 if check_for_move(@target) { |move| ["050", "051"].include?(move.function) } # Clear Smog, Haze
# TODO: Prefer if user is faster than the target.
# TODO: Is 1.3x for 025 Coil (+Atk, +Def, +acc).
mini_score *= 1.5 if @user_faster
# TODO: Don't prefer if target is a higher level than the user
if @target.level > @user.level + 5
mini_score *= 0.6
if @target.level > @user.level + 10
mini_score *= 0.2
end
end
return mini_score
end
#=============================================================================
#
#=============================================================================
def get_score_for_user_stat_raise(score)
# Discard status move if user has Contrary
return 0 if @move.statusMove? && @user.hasActiveAbility?(:CONTRARY)
# Discard move if it can't raise any stats
can_change_any_stat = false
@move.stat_up.each_with_index do |stat, idx|
next if idx.odd?
next if @user.statStageAtMax?(stat)
can_change_any_stat = true
break
end
if !can_change_any_stat
return (@move.statusMove?) ? 0 : score
end
# Get the main mini-score
main_mini_score = calc_user_stat_raise_mini_score
# For each stat to be raised in turn, calculate a mini-score describing how
# beneficial that stat being raised will be
mini_score = 0
num_stats = 0
@move.stat_up.each_with_index do |stat, idx|
next if idx.odd?
next if @user.statStageAtMax?(stat)
# TODO: Use the effective increment (e.g. 1 if the stat is raised by 2 but
# the stat is already at +5).
mini_score += calc_user_stat_raise_one(stat, @move.stat_up[idx + 1])
num_stats += 1
end
# Apply the average mini-score to the actual score
score = apply_effect_chance_to_score(main_mini_score * mini_score / num_stats)
return score
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,746 @@
class PokeBattle_AI
alias __b__pbGetMoveScoreFunctions pbGetMoveScoreFunctions
#=============================================================================
# Get a score for the given move based on its effect
#=============================================================================
def pbGetMoveScoreFunctions(score)
score = __b__pbGetMoveScoreFunctions(score)
case @move.function
#---------------------------------------------------------------------------
when "040"
if !@target.pbCanConfuse?(@user,false)
score -= 90
else
score += 30 if @target.stages[PBStats::SPATK]<0
end
#---------------------------------------------------------------------------
when "041"
if !@target.pbCanConfuse?(@user,false)
score -= 90
else
score += 30 if @target.stages[PBStats::ATTACK]<0
end
#---------------------------------------------------------------------------
when "042"
if @move.statusMove?
if !@target.pbCanLowerStatStage?(PBStats::ATTACK,@user)
score -= 90
else
score += @target.stages[PBStats::ATTACK]*20
if skill_check(AILevel.medium)
hasPhysicalAttack = false
@target.eachMove do |m|
next if !m.physicalMove?(m.type)
hasPhysicalAttack = true
break
end
if hasPhysicalAttack
score += 20
elsif skill_check(AILevel.high)
score -= 90
end
end
end
else
score += 20 if @target.stages[PBStats::ATTACK]>0
if skill_check(AILevel.medium)
hasPhysicalAttack = false
@target.eachMove do |m|
next if !m.physicalMove?(m.type)
hasPhysicalAttack = true
break
end
score += 20 if hasPhysicalAttack
end
end
#---------------------------------------------------------------------------
when "043"
if @move.statusMove?
if !@target.pbCanLowerStatStage?(PBStats::DEFENSE,@user)
score -= 90
else
score += @target.stages[PBStats::DEFENSE]*20
end
else
score += 20 if @target.stages[PBStats::DEFENSE]>0
end
#---------------------------------------------------------------------------
when "044"
if @move.statusMove?
if !@target.pbCanLowerStatStage?(PBStats::SPEED,@user)
score -= 90
else
score += @target.stages[PBStats::SPEED]*10
if skill_check(AILevel.high)
aspeed = pbRoughStat(@user,PBStats::SPEED)
ospeed = pbRoughStat(@target,PBStats::SPEED)
score += 30 if aspeed<ospeed && aspeed*2>ospeed
end
end
else
score += 20 if @user.stages[PBStats::SPEED]>0
end
#---------------------------------------------------------------------------
when "045"
if @move.statusMove?
if !@target.pbCanLowerStatStage?(PBStats::SPATK,@user)
score -= 90
else
score += @user.stages[PBStats::SPATK]*20
if skill_check(AILevel.medium)
hasSpecicalAttack = false
@target.eachMove do |m|
next if !m.specialMove?(m.type)
hasSpecicalAttack = true
break
end
if hasSpecicalAttack
score += 20
elsif skill_check(AILevel.high)
score -= 90
end
end
end
else
score += 20 if @user.stages[PBStats::SPATK]>0
if skill_check(AILevel.medium)
hasSpecicalAttack = false
@target.eachMove do |m|
next if !m.specialMove?(m.type)
hasSpecicalAttack = true
break
end
score += 20 if hasSpecicalAttack
end
end
#---------------------------------------------------------------------------
when "046"
if @move.statusMove?
if !@target.pbCanLowerStatStage?(PBStats::SPDEF,@user)
score -= 90
else
score += @target.stages[PBStats::SPDEF]*20
end
else
score += 20 if @target.stages[PBStats::SPDEF]>0
end
#---------------------------------------------------------------------------
when "047"
if @move.statusMove?
if !@target.pbCanLowerStatStage?(PBStats::ACCURACY,@user)
score -= 90
else
score += @target.stages[PBStats::ACCURACY]*10
end
else
score += 20 if @target.stages[PBStats::ACCURACY]>0
end
#---------------------------------------------------------------------------
when "048"
if @move.statusMove?
if !@target.pbCanLowerStatStage?(PBStats::EVASION,@user)
score -= 90
else
score += @target.stages[PBStats::EVASION]*10
end
else
score += 20 if @target.stages[PBStats::EVASION]>0
end
#---------------------------------------------------------------------------
when "049"
if @move.statusMove?
if !@target.pbCanLowerStatStage?(PBStats::EVASION,@user)
score -= 90
else
score += @target.stages[PBStats::EVASION]*10
end
else
score += 20 if @target.stages[PBStats::EVASION]>0
end
score += 30 if @target.pbOwnSide.effects[PBEffects::AuroraVeil]>0 ||
@target.pbOwnSide.effects[PBEffects::Reflect]>0 ||
@target.pbOwnSide.effects[PBEffects::LightScreen]>0 ||
@target.pbOwnSide.effects[PBEffects::Mist]>0 ||
@target.pbOwnSide.effects[PBEffects::Safeguard]>0
score -= 30 if @target.pbOwnSide.effects[PBEffects::Spikes]>0 ||
@target.pbOwnSide.effects[PBEffects::ToxicSpikes]>0 ||
@target.pbOwnSide.effects[PBEffects::StealthRock]
#---------------------------------------------------------------------------
when "04A"
avg = @target.stages[PBStats::ATTACK]*10
avg += @target.stages[PBStats::DEFENSE]*10
score += avg/2
#---------------------------------------------------------------------------
when "04B"
if @move.statusMove?
if !@target.pbCanLowerStatStage?(PBStats::ATTACK,@user)
score -= 90
else
score += 40 if @user.turnCount==0
score += @target.stages[PBStats::ATTACK]*20
if skill_check(AILevel.medium)
hasPhysicalAttack = false
@target.eachMove do |m|
next if !m.physicalMove?(m.type)
hasPhysicalAttack = true
break
end
if hasPhysicalAttack
score += 20
elsif skill_check(AILevel.high)
score -= 90
end
end
end
else
score += 10 if @user.turnCount==0
score += 20 if @target.stages[PBStats::ATTACK]>0
if skill_check(AILevel.medium)
hasPhysicalAttack = false
@target.eachMove do |m|
next if !m.physicalMove?(m.type)
hasPhysicalAttack = true
break
end
score += 20 if hasPhysicalAttack
end
end
#---------------------------------------------------------------------------
when "04C"
if @move.statusMove?
if !@target.pbCanLowerStatStage?(PBStats::DEFENSE,@user)
score -= 90
else
score += 40 if @user.turnCount==0
score += @target.stages[PBStats::DEFENSE]*20
end
else
score += 10 if @user.turnCount==0
score += 20 if @target.stages[PBStats::DEFENSE]>0
end
#---------------------------------------------------------------------------
when "04D"
if @move.statusMove?
if !@target.pbCanLowerStatStage?(PBStats::SPEED,@user)
score -= 90
else
score += 20 if @user.turnCount==0
score += @target.stages[PBStats::SPEED]*20
if skill_check(AILevel.high)
aspeed = pbRoughStat(@user,PBStats::SPEED)
ospeed = pbRoughStat(@target,PBStats::SPEED)
score += 30 if aspeed<ospeed && aspeed*2>ospeed
end
end
else
score += 10 if @user.turnCount==0
score += 30 if @target.stages[PBStats::SPEED]>0
end
#---------------------------------------------------------------------------
when "04E"
if @user.gender==2 || @target.gender==2 || @user.gender==@target.gender ||
@target.hasActiveAbility?(:OBLIVIOUS)
score -= 90
elsif @move.statusMove?
if !@target.pbCanLowerStatStage?(PBStats::SPATK,@user)
score -= 90
else
score += 40 if @user.turnCount==0
score += @target.stages[PBStats::SPATK]*20
if skill_check(AILevel.medium)
hasSpecicalAttack = false
@target.eachMove do |m|
next if !m.specialMove?(m.type)
hasSpecicalAttack = true
break
end
if hasSpecicalAttack
score += 20
elsif skill_check(AILevel.high)
score -= 90
end
end
end
else
score += 10 if @user.turnCount==0
score += 20 if @target.stages[PBStats::SPATK]>0
if skill_check(AILevel.medium)
hasSpecicalAttack = false
@target.eachMove do |m|
next if !m.specialMove?(m.type)
hasSpecicalAttack = true
break
end
score += 30 if hasSpecicalAttack
end
end
#---------------------------------------------------------------------------
when "04F"
if @move.statusMove?
if !@target.pbCanLowerStatStage?(PBStats::SPDEF,@user)
score -= 90
else
score += 40 if @user.turnCount==0
score += @target.stages[PBStats::SPDEF]*20
end
else
score += 10 if @user.turnCount==0
score += 20 if @target.stages[PBStats::SPDEF]>0
end
#---------------------------------------------------------------------------
when "050"
if @target.effects[PBEffects::Substitute]>0
score -= 90
else
avg = 0; anyChange = false
PBStats.eachBattleStat do |s|
next if @target.stages[s]==0
avg += @target.stages[s]
anyChange = true
end
if anyChange
score += avg*10
else
score -= 90
end
end
#---------------------------------------------------------------------------
when "051"
if skill_check(AILevel.medium)
stages = 0
@battle.eachBattler do |b|
totalStages = 0
PBStats.eachBattleStat { |s| totalStages += b.stages[s] }
if b.opposes?(@user)
stages += totalStages
else
stages -= totalStages
end
end
score += stages*10
end
#---------------------------------------------------------------------------
when "052"
if skill_check(AILevel.medium)
aatk = @user.stages[PBStats::ATTACK]
aspa = @user.stages[PBStats::SPATK]
oatk = @target.stages[PBStats::ATTACK]
ospa = @target.stages[PBStats::SPATK]
if aatk>=oatk && aspa>=ospa
score -= 80
else
score += (oatk-aatk)*10
score += (ospa-aspa)*10
end
else
score -= 50
end
#---------------------------------------------------------------------------
when "053"
if skill_check(AILevel.medium)
adef = @user.stages[PBStats::DEFENSE]
aspd = @user.stages[PBStats::SPDEF]
odef = @target.stages[PBStats::DEFENSE]
ospd = @target.stages[PBStats::SPDEF]
if adef>=odef && aspd>=ospd
score -= 80
else
score += (odef-adef)*10
score += (ospd-aspd)*10
end
else
score -= 50
end
#---------------------------------------------------------------------------
when "054"
if skill_check(AILevel.medium)
userStages = 0; targetStages = 0
PBStats.eachBattleStat do |s|
userStages += @user.stages[s]
targetStages += @target.stages[s]
end
score += (targetStages-userStages)*10
else
score -= 50
end
#---------------------------------------------------------------------------
when "055"
if skill_check(AILevel.medium)
equal = true
PBStats.eachBattleStat do |s|
stagediff = @target.stages[s]-@user.stages[s]
score += stagediff*10
equal = false if stagediff!=0
end
score -= 80 if equal
else
score -= 50
end
#---------------------------------------------------------------------------
when "056"
score -= 80 if @user.pbOwnSide.effects[PBEffects::Mist]>0
#---------------------------------------------------------------------------
when "057"
if skill_check(AILevel.medium)
aatk = pbRoughStat(@user,PBStats::ATTACK)
adef = pbRoughStat(@user,PBStats::DEFENSE)
if aatk==adef ||
@user.effects[PBEffects::PowerTrick] # No flip-flopping
score -= 90
elsif adef>aatk # Prefer a higher Attack
score += 30
else
score -= 30
end
else
score -= 30
end
#---------------------------------------------------------------------------
when "058"
if skill_check(AILevel.medium)
aatk = pbRoughStat(@user,PBStats::ATTACK)
aspatk = pbRoughStat(@user,PBStats::SPATK)
oatk = pbRoughStat(@target,PBStats::ATTACK)
ospatk = pbRoughStat(@target,PBStats::SPATK)
if aatk<oatk && aspatk<ospatk
score += 50
elsif aatk+aspatk<oatk+ospatk
score += 30
else
score -= 50
end
else
score -= 30
end
#---------------------------------------------------------------------------
when "059"
if skill_check(AILevel.medium)
adef = pbRoughStat(@user,PBStats::DEFENSE)
aspdef = pbRoughStat(@user,PBStats::SPDEF)
odef = pbRoughStat(@target,PBStats::DEFENSE)
ospdef = pbRoughStat(@target,PBStats::SPDEF)
if adef<odef && aspdef<ospdef
score += 50
elsif adef+aspdef<odef+ospdef
score += 30
else
score -= 50
end
else
score -= 30
end
#---------------------------------------------------------------------------
when "05A"
if @target.effects[PBEffects::Substitute]>0
score -= 90
elsif @user.hp>=(@user.hp+@target.hp)/2
score -= 90
else
score += 40
end
#---------------------------------------------------------------------------
when "05B"
score -= 90 if @user.pbOwnSide.effects[PBEffects::Tailwind]>0
#---------------------------------------------------------------------------
when "05C"
moveBlacklist = [
"002", # Struggle
"014", # Chatter
"05C", # Mimic
"05D", # Sketch
"0B6" # Metronome
]
if @user.effects[PBEffects::Transform] || !@target.lastRegularMoveUsed
score -= 90
else
lastMoveData = GameData::Move.get(@target.lastRegularMoveUsed)
if moveBlacklist.include?(lastMoveData.function_code) ||
lastMoveData.type == :SHADOW
score -= 90
end
@user.eachMove do |m|
next if m != @target.lastRegularMoveUsed
score -= 90
break
end
end
#---------------------------------------------------------------------------
when "05D"
moveBlacklist = [
"002", # Struggle
"014", # Chatter
"05D" # Sketch
]
if @user.effects[PBEffects::Transform] || !@target.lastRegularMoveUsed
score -= 90
else
lastMoveData = GameData::Move.get(@target.lastRegularMoveUsed)
if moveBlacklist.include?(lastMoveData.function_code) ||
lastMoveData.type == :SHADOW
score -= 90
end
@user.eachMove do |m|
next if m != @target.lastRegularMoveUsed
score -= 90 # User already knows the move that will be Sketched
break
end
end
#---------------------------------------------------------------------------
when "05E"
if [:MULTITYPE, :RKSSYSTEM].include?(@user.ability_id)
score -= 90
else
types = []
@user.eachMove do |m|
next if m.id==@id
next if PBTypes.isPseudoType?(m.type)
next if @user.pbHasType?(m.type)
types.push(m.type) if !types.include?(m.type)
end
score -= 90 if types.length==0
end
#---------------------------------------------------------------------------
when "05F"
if [:MULTITYPE, :RKSSYSTEM].include?(@user.ability_id)
score -= 90
elsif !@target.lastMoveUsed ||
PBTypes.isPseudoType?(GameData::Move.get(@target.lastMoveUsed).type)
score -= 90
else
aType = nil
@target.eachMove do |m|
next if m.id!=@target.lastMoveUsed
aType = m.pbCalcType(@user)
break
end
if aType
types = []
GameData::Type.each do |t|
types.push(t.id) if !@user.pbHasType?(t.id) && PBTypes.resistant?(aType, t.id)
end
score -= 90 if types.length==0
else
score -= 90
end
end
#---------------------------------------------------------------------------
when "060"
if [:MULTITYPE, :RKSSYSTEM].include?(@user.ability_id)
score -= 90
elsif skill_check(AILevel.medium)
envtypes = [
:NORMAL, # None
:GRASS, # Grass
:GRASS, # Tall grass
:WATER, # Moving water
:WATER, # Still water
:WATER, # Underwater
:ROCK, # Rock
:ROCK, # Cave
:GROUND # Sand
]
type = envtypes[@battle.environment]
score -= 90 if @user.pbHasType?(type)
end
#---------------------------------------------------------------------------
when "061"
if @target.effects[PBEffects::Substitute]>0 ||
[:MULTITYPE, :RKSSYSTEM].include?(@target.ability_id)
score -= 90
elsif @target.pbHasType?(:WATER)
score -= 90
end
#---------------------------------------------------------------------------
when "062"
if [:MULTITYPE, :RKSSYSTEM].include?(@user.ability_id)
score -= 90
elsif @user.pbHasType?(@target.type1) &&
@user.pbHasType?(@target.type2) &&
@target.pbHasType?(@user.type1) &&
@target.pbHasType?(@user.type2)
score -= 90
end
#---------------------------------------------------------------------------
when "063"
if @target.effects[PBEffects::Substitute]>0
score -= 90
elsif skill_check(AILevel.medium)
if [:MULTITYPE, :RKSSYSTEM, :SIMPLE, :TRUANT].include?(@target.ability_id)
score -= 90
end
end
#---------------------------------------------------------------------------
when "064"
if @target.effects[PBEffects::Substitute]>0
score -= 90
elsif skill_check(AILevel.medium)
if [:INSOMNIA, :MULTITYPE, :RKSSYSTEM, :TRUANT].include?(@target.ability_id)
score -= 90
end
end
#---------------------------------------------------------------------------
when "065"
score -= 40 # don't prefer this move
if skill_check(AILevel.medium)
if !@target.ability || @user.ability_id == @target.ability_id ||
[:MULTITYPE, :RKSSYSTEM].include?(@user.ability_id) ||
[:FLOWERGIFT, :FORECAST, :ILLUSION, :IMPOSTER, :MULTITYPE, :RKSSYSTEM,
:TRACE, :WONDERGUARD, :ZENMODE].include?(@target.ability_id)
score -= 90
end
end
if skill_check(AILevel.high) && @user.opposes?(@target)
score -= 90 if [:SLOWSTART, :TRUANT].include?(@target.ability_id)
end
#---------------------------------------------------------------------------
when "066"
score -= 40 # don't prefer this move
if @target.effects[PBEffects::Substitute]>0
score -= 90
elsif skill_check(AILevel.medium)
if !@user.ability || @user.ability_id == @target.ability_id ||
[:MULTITYPE, :RKSSYSTEM, :TRUANT].include?(@target.ability_id) ||
[:FLOWERGIFT, :FORECAST, :ILLUSION, :IMPOSTER, :MULTITYPE, :RKSSYSTEM,
:TRACE, :ZENMODE].include?(@user.ability_id)
score -= 90
end
if skill_check(AILevel.high) && @user.opposes?(@target)
score += 90 if [:SLOWSTART, :TRUANT].include?(@user.ability_id)
end
end
#---------------------------------------------------------------------------
when "067"
score -= 40 # don't prefer this move
if skill_check(AILevel.medium)
if (!@user.ability && !@target.ability) ||
@user.ability_id == @target.ability_id ||
[:ILLUSION, :MULTITYPE, :RKSSYSTEM, :WONDERGUARD].include?(@user.ability_id) ||
[:ILLUSION, :MULTITYPE, :RKSSYSTEM, :WONDERGUARD].include?(@target.ability_id)
score -= 90
end
end
if skill_check(AILevel.high) && @user.opposes?(@target)
score -= 90 if [:SLOWSTART, :TRUANT].include?(@target.ability_id)
end
#---------------------------------------------------------------------------
when "068"
if @target.effects[PBEffects::Substitute]>0 ||
@target.effects[PBEffects::GastroAcid]
score -= 90
elsif skill_check(AILevel.high)
score -= 90 if [:MULTITYPE, :RKSSYSTEM, :SLOWSTART, :TRUANT].include?(@target.ability_id)
end
#---------------------------------------------------------------------------
when "069"
score -= 70
#---------------------------------------------------------------------------
when "06A"
if @target.hp<=20
score += 80
elsif @target.level>=25
score -= 60 # Not useful against high-level Pokemon
end
#---------------------------------------------------------------------------
when "06B"
score += 80 if @target.hp<=40
#---------------------------------------------------------------------------
when "06C"
score -= 50
score += @target.hp*100/@target.totalhp
#---------------------------------------------------------------------------
when "06D"
score += 80 if @target.hp<=@user.level
#---------------------------------------------------------------------------
when "06E"
if @user.hp>=@target.hp
score -= 90
elsif @user.hp<@target.hp/2
score += 50
end
#---------------------------------------------------------------------------
when "06F"
score += 30 if @target.hp<=@user.level
#---------------------------------------------------------------------------
when "070"
score -= 90 if @target.hasActiveAbility?(:STURDY)
score -= 90 if @target.level>@user.level
#---------------------------------------------------------------------------
when "071"
if @target.effects[PBEffects::HyperBeam]>0
score -= 90
else
attack = pbRoughStat(@user,PBStats::ATTACK)
spatk = pbRoughStat(@user,PBStats::SPATK)
if attack*1.5<spatk
score -= 60
elsif skill_check(AILevel.medium) && @target.lastMoveUsed
moveData = GameData::Move.get(@target.lastMoveUsed)
if moveData.base_damage > 0 &&
(MOVE_CATEGORY_PER_MOVE && moveData.category == 0) ||
(!MOVE_CATEGORY_PER_MOVE && PBTypes.isPhysicalType?(moveData.type))
score -= 60
end
end
end
#---------------------------------------------------------------------------
when "072"
if @target.effects[PBEffects::HyperBeam]>0
score -= 90
else
attack = pbRoughStat(@user,PBStats::ATTACK)
spatk = pbRoughStat(@user,PBStats::SPATK)
if attack>spatk*1.5
score -= 60
elsif skill_check(AILevel.medium) && @target.lastMoveUsed
moveData = GameData::Move.get(@target.lastMoveUsed)
if moveData.base_damage > 0 &&
(MOVE_CATEGORY_PER_MOVE && moveData.category == 1) ||
(!MOVE_CATEGORY_PER_MOVE && !PBTypes.isSpecialType?(moveData.type))
score -= 60
end
end
end
#---------------------------------------------------------------------------
when "073"
score -= 90 if @target.effects[PBEffects::HyperBeam]>0
#---------------------------------------------------------------------------
when "074"
@target.eachAlly do |b|
next if !b.near?(@target)
score += 10
end
#---------------------------------------------------------------------------
when "075"
#---------------------------------------------------------------------------
when "076"
#---------------------------------------------------------------------------
when "077"
#---------------------------------------------------------------------------
when "078"
if skill_check(AILevel.high)
score += 30 if !@target.hasActiveAbility?(:INNERFOCUS) &&
@target.effects[PBEffects::Substitute]==0
end
#---------------------------------------------------------------------------
when "079"
#---------------------------------------------------------------------------
when "07A"
#---------------------------------------------------------------------------
when "07B"
#---------------------------------------------------------------------------
when "07C"
score -= 20 if @target.status==PBStatuses::PARALYSIS # Will cure status
#---------------------------------------------------------------------------
when "07D"
score -= 20 if @target.status==PBStatuses::SLEEP && # Will cure status
@target.statusCount>1
#---------------------------------------------------------------------------
when "07E"
#---------------------------------------------------------------------------
when "07F"
#---------------------------------------------------------------------------
end
return score
end
end

View File

@@ -0,0 +1,227 @@
class PokeBattle_AI
alias __c__pbGetMoveScoreFunctions pbGetMoveScoreFunctions
#=============================================================================
# Get a score for the given move based on its effect
#=============================================================================
def pbGetMoveScoreFunctions(score)
score = __c__pbGetMoveScoreFunctions(score)
case @move.function
#---------------------------------------------------------------------------
when "080"
#---------------------------------------------------------------------------
when "081"
attspeed = pbRoughStat(@user,PBStats::SPEED)
oppspeed = pbRoughStat(@target,PBStats::SPEED)
score += 30 if oppspeed>attspeed
#---------------------------------------------------------------------------
when "082"
score += 20 if @battle.pbOpposingBattlerCount(@user)>1
#---------------------------------------------------------------------------
when "083"
if skill_check(AILevel.medium)
@user.eachAlly do |b|
next if !b.pbHasMove?(@move.id)
score += 20
end
end
#---------------------------------------------------------------------------
when "084"
attspeed = pbRoughStat(@user,PBStats::SPEED)
oppspeed = pbRoughStat(@target,PBStats::SPEED)
score += 30 if oppspeed>attspeed
#---------------------------------------------------------------------------
when "085"
#---------------------------------------------------------------------------
when "086"
#---------------------------------------------------------------------------
when "087"
#---------------------------------------------------------------------------
when "088"
#---------------------------------------------------------------------------
when "089"
#---------------------------------------------------------------------------
when "08A"
#---------------------------------------------------------------------------
when "08B"
#---------------------------------------------------------------------------
when "08C"
#---------------------------------------------------------------------------
when "08D"
#---------------------------------------------------------------------------
when "08E"
#---------------------------------------------------------------------------
when "08F"
#---------------------------------------------------------------------------
when "090"
#---------------------------------------------------------------------------
when "091"
#---------------------------------------------------------------------------
when "092"
#---------------------------------------------------------------------------
when "093"
score += 25 if @user.effects[PBEffects::Rage]
#---------------------------------------------------------------------------
when "094"
#---------------------------------------------------------------------------
when "095"
#---------------------------------------------------------------------------
when "096"
score -= 90 if !@user.item || !@user.item.is_berry? || !@user.itemActive?
#---------------------------------------------------------------------------
when "097"
#---------------------------------------------------------------------------
when "098"
#---------------------------------------------------------------------------
when "099"
#---------------------------------------------------------------------------
when "09A"
#---------------------------------------------------------------------------
when "09B"
#---------------------------------------------------------------------------
when "09C"
hasAlly = false
@user.eachAlly do |b|
hasAlly = true
score += 30
break
end
score -= 90 if !hasAlly
#---------------------------------------------------------------------------
when "09D"
score -= 90 if @user.effects[PBEffects::MudSport]
#---------------------------------------------------------------------------
when "09E"
score -= 90 if @user.effects[PBEffects::WaterSport]
#---------------------------------------------------------------------------
when "09F"
#---------------------------------------------------------------------------
when "0A0"
#---------------------------------------------------------------------------
when "0A1"
score -= 90 if @user.pbOwnSide.effects[PBEffects::LuckyChant]>0
#---------------------------------------------------------------------------
when "0A2"
score -= 90 if @user.pbOwnSide.effects[PBEffects::Reflect]>0
#---------------------------------------------------------------------------
when "0A3"
score -= 90 if @user.pbOwnSide.effects[PBEffects::LightScreen]>0
#---------------------------------------------------------------------------
when "0A4"
#---------------------------------------------------------------------------
when "0A5"
#---------------------------------------------------------------------------
when "0A6"
score -= 90 if @target.effects[PBEffects::Substitute]>0
score -= 90 if @user.effects[PBEffects::LockOn]>0
#---------------------------------------------------------------------------
when "0A7"
if @target.effects[PBEffects::Foresight]
score -= 90
elsif @target.pbHasType?(:GHOST)
score += 70
elsif @target.stages[PBStats::EVASION]<=0
score -= 60
end
#---------------------------------------------------------------------------
when "0A8"
if @target.effects[PBEffects::MiracleEye]
score -= 90
elsif @target.pbHasType?(:DARK)
score += 70
elsif @target.stages[PBStats::EVASION]<=0
score -= 60
end
#---------------------------------------------------------------------------
when "0A9"
#---------------------------------------------------------------------------
when "0AA"
if @user.effects[PBEffects::ProtectRate]>1 ||
@target.effects[PBEffects::HyperBeam]>0
score -= 90
else
if skill_check(AILevel.medium)
score -= @user.effects[PBEffects::ProtectRate]*40
end
score += 50 if @user.turnCount==0
score += 30 if @target.effects[PBEffects::TwoTurnAttack]
end
#---------------------------------------------------------------------------
when "0AB"
#---------------------------------------------------------------------------
when "0AC"
#---------------------------------------------------------------------------
when "0AD"
#---------------------------------------------------------------------------
when "0AE"
score -= 40
if skill_check(AILevel.high)
score -= 100 if !@target.lastRegularMoveUsed ||
!GameData::Move.get(@target.lastRegularMoveUsed).flags[/e/] # Not copyable by Mirror Move
end
#---------------------------------------------------------------------------
when "0AF"
#---------------------------------------------------------------------------
when "0B0"
#---------------------------------------------------------------------------
when "0B1"
#---------------------------------------------------------------------------
when "0B2"
#---------------------------------------------------------------------------
when "0B3"
#---------------------------------------------------------------------------
when "0B4"
if @user.asleep?
score += 100 # Because it can only be used while asleep
else
score -= 90
end
#---------------------------------------------------------------------------
when "0B5"
#---------------------------------------------------------------------------
when "0B6"
#---------------------------------------------------------------------------
when "0B7"
score -= 90 if @target.effects[PBEffects::Torment]
#---------------------------------------------------------------------------
when "0B8"
score -= 90 if @user.effects[PBEffects::Imprison]
#---------------------------------------------------------------------------
when "0B9"
score -= 90 if @target.effects[PBEffects::Disable]>0
#---------------------------------------------------------------------------
when "0BA"
score -= 90 if @target.effects[PBEffects::Taunt]>0
#---------------------------------------------------------------------------
when "0BB"
score -= 90 if @target.effects[PBEffects::HealBlock]>0
#---------------------------------------------------------------------------
when "0BC"
aspeed = pbRoughStat(@user,PBStats::SPEED)
ospeed = pbRoughStat(@target,PBStats::SPEED)
if @target.effects[PBEffects::Encore]>0
score -= 90
elsif aspeed>ospeed
if !@target.lastRegularMoveUsed
score -= 90
else
moveData = GameData::Move.get(@target.lastRegularMoveUsed)
if moveData.category == 2 && # Status move
[PBTargets::User, PBTargets::BothSides].include?(moveData.target)
score += 60
elsif moveData.category != 2 && # Damaging move
moveData.target == PBTargets::NearOther &&
PBTypeEffectiveness.ineffective?(pbCalcTypeMod(moveData.type, @target, @user))
score += 60
end
end
end
#---------------------------------------------------------------------------
when "0BD"
#---------------------------------------------------------------------------
when "0BF"
#---------------------------------------------------------------------------
end
return score
end
end

View File

@@ -0,0 +1,375 @@
class PokeBattle_AI
alias __d__pbGetMoveScoreFunctions pbGetMoveScoreFunctions
#=============================================================================
# Get a score for the given move based on its effect
#=============================================================================
def pbGetMoveScoreFunctions(score)
score = __d__pbGetMoveScoreFunctions(score)
case @move.function
#---------------------------------------------------------------------------
when "0C0"
#---------------------------------------------------------------------------
when "0C1"
#---------------------------------------------------------------------------
when "0C2"
#---------------------------------------------------------------------------
when "0C3"
#---------------------------------------------------------------------------
when "0C4"
#---------------------------------------------------------------------------
when "0C7"
score += 20 if @user.effects[PBEffects::FocusEnergy]>0
if skill_check(AILevel.high)
score += 20 if !@target.hasActiveAbility?(:INNERFOCUS) &&
@target.effects[PBEffects::Substitute]==0
end
#---------------------------------------------------------------------------
when "0C9"
#---------------------------------------------------------------------------
when "0CA"
#---------------------------------------------------------------------------
when "0CB"
#---------------------------------------------------------------------------
when "0CC"
#---------------------------------------------------------------------------
when "0CD"
#---------------------------------------------------------------------------
when "0CE"
#---------------------------------------------------------------------------
when "0CF"
score += 40 if @target.effects[PBEffects::Trapping]==0
#---------------------------------------------------------------------------
when "0D0"
score += 40 if @target.effects[PBEffects::Trapping]==0
#---------------------------------------------------------------------------
when "0D1"
#---------------------------------------------------------------------------
when "0D2"
#---------------------------------------------------------------------------
when "0D3"
#---------------------------------------------------------------------------
when "0D4"
if @user.hp<=@user.totalhp/4
score -= 90
elsif @user.hp<=@user.totalhp/2
score -= 50
end
#---------------------------------------------------------------------------
when "0D5", "0D6"
if @user.hp==@user.totalhp || (skill_check(AILevel.medium) && !@user.canHeal?)
score -= 90
else
score += 50
score -= @user.hp*100/@user.totalhp
end
#---------------------------------------------------------------------------
when "0D7"
score -= 90 if @battle.positions[@user.index].effects[PBEffects::Wish]>0
#---------------------------------------------------------------------------
when "0D8"
if @user.hp==@user.totalhp || (skill_check(AILevel.medium) && !@user.canHeal?)
score -= 90
else
case @battle.pbWeather
when PBWeather::Sun, PBWeather::HarshSun
score += 30
when PBWeather::None
else
score -= 30
end
score += 50
score -= @user.hp*100/@user.totalhp
end
#---------------------------------------------------------------------------
when "0D9"
if @user.hp==@user.totalhp || !@user.pbCanSleep?(@user,false,nil,true)
score -= 90
else
score += 70
score -= @user.hp*140/@user.totalhp
score += 30 if @user.status!=0
end
#---------------------------------------------------------------------------
when "0DA"
score -= 90 if @user.effects[PBEffects::AquaRing]
#---------------------------------------------------------------------------
when "0DB"
score -= 90 if @user.effects[PBEffects::Ingrain]
#---------------------------------------------------------------------------
when "0DC"
if @target.effects[PBEffects::LeechSeed]>=0
score -= 90
elsif skill_check(AILevel.medium) && @target.pbHasType?(:GRASS)
score -= 90
else
score += 60 if @user.turnCount==0
end
#---------------------------------------------------------------------------
when "0DD"
if skill_check(AILevel.high) && @target.hasActiveAbility?(:LIQUIDOOZE)
score -= 70
else
score += 20 if @user.hp<=@user.totalhp/2
end
#---------------------------------------------------------------------------
when "0DE"
if !@target.asleep?
score -= 100
elsif skill_check(AILevel.high) && @target.hasActiveAbility?(:LIQUIDOOZE)
score -= 70
else
score += 20 if @user.hp<=@user.totalhp/2
end
#---------------------------------------------------------------------------
when "0DF"
if @user.opposes?(@target)
score -= 100
else
score += 20 if @target.hp<@target.totalhp/2 &&
@target.effects[PBEffects::Substitute]==0
end
#---------------------------------------------------------------------------
when "0E0"
reserves = @battle.pbAbleNonActiveCount(@user.idxOwnSide)
foes = @battle.pbAbleNonActiveCount(@user.idxOpposingSide)
if @battle.pbCheckGlobalAbility(:DAMP)
score -= 100
elsif skill_check(AILevel.medium) && reserves==0 && foes>0
score -= 100 # don't want to lose
elsif skill_check(AILevel.high) && reserves==0 && foes==0
score += 80 # want to draw
else
score -= @user.hp*100/@user.totalhp
end
#---------------------------------------------------------------------------
when "0E1"
#---------------------------------------------------------------------------
when "0E2"
if !@target.pbCanLowerStatStage?(PBStats::ATTACK,@user) &&
!@target.pbCanLowerStatStage?(PBStats::SPATK,@user)
score -= 100
elsif @battle.pbAbleNonActiveCount(@user.idxOwnSide)==0
score -= 100
else
score += @target.stages[PBStats::ATTACK]*10
score += @target.stages[PBStats::SPATK]*10
score -= @user.hp*100/@user.totalhp
end
#---------------------------------------------------------------------------
when "0E3", "0E4"
score -= 70
#---------------------------------------------------------------------------
when "0E5"
if @battle.pbAbleNonActiveCount(@user.idxOwnSide)==0
score -= 90
else
score -= 90 if @target.effects[PBEffects::PerishSong]>0
end
#---------------------------------------------------------------------------
when "0E6"
score += 50
score -= @user.hp*100/@user.totalhp
score += 30 if @user.hp<=@user.totalhp/10
#---------------------------------------------------------------------------
when "0E7"
score += 50
score -= @user.hp*100/@user.totalhp
score += 30 if @user.hp<=@user.totalhp/10
#---------------------------------------------------------------------------
when "0E8"
score -= 25 if @user.hp>@user.totalhp/2
if skill_check(AILevel.medium)
score -= 90 if @user.effects[PBEffects::ProtectRate]>1
score -= 90 if @target.effects[PBEffects::HyperBeam]>0
else
score -= @user.effects[PBEffects::ProtectRate]*40
end
#---------------------------------------------------------------------------
when "0E9"
if @target.hp==1
score -= 90
elsif @target.hp<=@target.totalhp/8
score -= 60
elsif @target.hp<=@target.totalhp/4
score -= 30
end
#---------------------------------------------------------------------------
when "0EA"
score -= 100 if @battle.trainerBattle?
#---------------------------------------------------------------------------
when "0EB"
if @target.effects[PBEffects::Ingrain] ||
(skill_check(AILevel.high) && @target.hasActiveAbility?(:SUCTIONCUPS))
score -= 90
else
ch = 0
@battle.pbParty(@target.index).each_with_index do |pkmn,i|
ch += 1 if @battle.pbCanSwitchLax?(@target.index,i)
end
score -= 90 if ch==0
end
if score>20
score += 50 if @target.pbOwnSide.effects[PBEffects::Spikes]>0
score += 50 if @target.pbOwnSide.effects[PBEffects::ToxicSpikes]>0
score += 50 if @target.pbOwnSide.effects[PBEffects::StealthRock]
end
#---------------------------------------------------------------------------
when "0EC"
if !@target.effects[PBEffects::Ingrain] &&
!(skill_check(AILevel.high) && @target.hasActiveAbility?(:SUCTIONCUPS))
score += 40 if @target.pbOwnSide.effects[PBEffects::Spikes]>0
score += 40 if @target.pbOwnSide.effects[PBEffects::ToxicSpikes]>0
score += 40 if @target.pbOwnSide.effects[PBEffects::StealthRock]
end
#---------------------------------------------------------------------------
when "0ED"
if !@battle.pbCanChooseNonActive?(@user.index)
score -= 80
else
score -= 40 if @user.effects[PBEffects::Confusion]>0
total = 0
PBStats.eachBattleStat { |s| total += @user.stages[s] }
if total<=0 || @user.turnCount==0
score -= 60
else
score += total*10
# special case: user has no damaging moves
hasDamagingMove = false
@user.eachMove do |m|
next if !m.damagingMove?
hasDamagingMove = true
break
end
score += 75 if !hasDamagingMove
end
end
#---------------------------------------------------------------------------
when "0EE"
#---------------------------------------------------------------------------
when "0EF"
score -= 90 if @target.effects[PBEffects::MeanLook]>=0
#---------------------------------------------------------------------------
when "0F0"
if skill_check(AILevel.high)
score += 20 if @target.item
end
#---------------------------------------------------------------------------
when "0F1"
if skill_check(AILevel.high)
if !@user.item && @target.item
score += 40
else
score -= 90
end
else
score -= 80
end
#---------------------------------------------------------------------------
when "0F2"
if !@user.item && !@target.item
score -= 90
elsif skill_check(AILevel.high) && @target.hasActiveAbility?(:STICKYHOLD)
score -= 90
elsif @user.hasActiveItem?([:FLAMEORB,:TOXICORB,:STICKYBARB,:IRONBALL,
:CHOICEBAND,:CHOICESCARF,:CHOICESPECS])
score += 50
elsif !@user.item && @target.item
score -= 30 if user.lastMoveUsed &&
GameData::Move.get(@user.lastMoveUsed).function_code == "0F2" # Trick/Switcheroo
end
#---------------------------------------------------------------------------
when "0F3"
if !@user.item || @target.item
score -= 90
else
if @user.hasActiveItem?([:FLAMEORB,:TOXICORB,:STICKYBARB,:IRONBALL,
:CHOICEBAND,:CHOICESCARF,:CHOICESPECS])
score += 50
else
score -= 80
end
end
#---------------------------------------------------------------------------
when "0F4", "0F5"
if @target.effects[PBEffects::Substitute]==0
if skill_check(AILevel.high) && @target.item && @target.item.is_berry?
score += 30
end
end
#---------------------------------------------------------------------------
when "0F6"
if !@user.recycleItem || @user.item
score -= 80
elsif @user.recycleItem
score += 30
end
#---------------------------------------------------------------------------
when "0F7"
if !@user.item || !@user.itemActive? ||
@user.unlosableItem?(@user.item) || @user.item.is_poke_ball?
score -= 90
end
#---------------------------------------------------------------------------
when "0F8"
score -= 90 if @target.effects[PBEffects::Embargo]>0
#---------------------------------------------------------------------------
when "0F9"
if @battle.field.effects[PBEffects::MagicRoom]>0
score -= 90
else
score += 30 if !@user.item && @target.item
end
#---------------------------------------------------------------------------
when "0FA"
score -= 25
#---------------------------------------------------------------------------
when "0FB"
score -= 30
#---------------------------------------------------------------------------
when "0FC"
score -= 40
#---------------------------------------------------------------------------
when "0FD"
score -= 30
if @target.pbCanParalyze?(@user,false)
score += 30
if skill_check(AILevel.medium)
aspeed = pbRoughStat(@user,PBStats::SPEED)
ospeed = pbRoughStat(@target,PBStats::SPEED)
if aspeed<ospeed
score += 30
elsif aspeed>ospeed
score -= 40
end
end
if skill_check(AILevel.high)
score -= 40 if @target.hasActiveAbility?([:GUTS,:MARVELSCALE,:QUICKFEET])
end
end
#---------------------------------------------------------------------------
when "0FE"
score -= 30
if @target.pbCanBurn?(@user,false)
score += 30
if skill_check(AILevel.high)
score -= 40 if @target.hasActiveAbility?([:GUTS,:MARVELSCALE,:QUICKFEET,:FLAREBOOST])
end
end
#---------------------------------------------------------------------------
when "0FF"
if @battle.pbCheckGlobalAbility(:AIRLOCK) ||
@battle.pbCheckGlobalAbility(:CLOUDNINE)
score -= 90
elsif @battle.pbWeather==PBWeather::Sun
score -= 90
else
@user.eachMove do |m|
score += 20 if m.damagingMove? && m.type == :FIRE
end
end
#---------------------------------------------------------------------------
end
return score
end
end

View File

@@ -0,0 +1,497 @@
class PokeBattle_AI
alias __e__pbGetMoveScoreFunctions pbGetMoveScoreFunctions
#=============================================================================
# Get a score for the given move based on its effect
#=============================================================================
def pbGetMoveScoreFunctions(score)
score = __e__pbGetMoveScoreFunctions(score)
case @move.function
#---------------------------------------------------------------------------
when "100"
if @battle.pbCheckGlobalAbility(:AIRLOCK) ||
@battle.pbCheckGlobalAbility(:CLOUDNINE)
score -= 90
elsif @battle.pbWeather==PBWeather::Rain
score -= 90
else
@user.eachMove do |m|
score += 20 if m.damagingMove? && m.type == :WATER
end
end
#---------------------------------------------------------------------------
when "101"
if @battle.pbCheckGlobalAbility(:AIRLOCK) ||
@battle.pbCheckGlobalAbility(:CLOUDNINE)
score -= 90
elsif @battle.pbWeather==PBWeather::Sandstorm
score -= 90
end
#---------------------------------------------------------------------------
when "102"
if @battle.pbCheckGlobalAbility(:AIRLOCK) ||
@battle.pbCheckGlobalAbility(:CLOUDNINE)
score -= 90
elsif @battle.pbWeather==PBWeather::Hail
score -= 90
end
#---------------------------------------------------------------------------
when "103"
if @user.pbOpposingSide.effects[PBEffects::Spikes]>=3
score -= 90
else
canChoose = false
@user.eachOpposing do |b|
next if !@battle.pbCanChooseNonActive?(b.index)
canChoose = true
break
end
if !canChoose
# Opponent can't switch in any Pokemon
score -= 90
else
score += 10*@battle.pbAbleNonActiveCount(@user.idxOpposingSide)
score += [40,26,13][@user.pbOpposingSide.effects[PBEffects::Spikes]]
end
end
#---------------------------------------------------------------------------
when "104"
if @user.pbOpposingSide.effects[PBEffects::ToxicSpikes]>=2
score -= 90
else
canChoose = false
@user.eachOpposing do |b|
next if !@battle.pbCanChooseNonActive?(b.index)
canChoose = true
break
end
if !canChoose
# Opponent can't switch in any Pokemon
score -= 90
else
score += 8*@battle.pbAbleNonActiveCount(@user.idxOpposingSide)
score += [26,13][@user.pbOpposingSide.effects[PBEffects::ToxicSpikes]]
end
end
#---------------------------------------------------------------------------
when "105"
if @user.pbOpposingSide.effects[PBEffects::StealthRock]
score -= 90
else
canChoose = false
@user.eachOpposing do |b|
next if !@battle.pbCanChooseNonActive?(b.index)
canChoose = true
break
end
if !canChoose
# Opponent can't switch in any Pokemon
score -= 90
else
score += 10*@battle.pbAbleNonActiveCount(@user.idxOpposingSide)
end
end
#---------------------------------------------------------------------------
when "106"
#---------------------------------------------------------------------------
when "107"
#---------------------------------------------------------------------------
when "108"
#---------------------------------------------------------------------------
when "109"
#---------------------------------------------------------------------------
when "10A"
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
#---------------------------------------------------------------------------
when "10B"
score += 10*(@user.stages[PBStats::ACCURACY]-@target.stages[PBStats::EVASION])
#---------------------------------------------------------------------------
when "10C"
if @user.effects[PBEffects::Substitute]>0
score -= 90
elsif @user.hp<=@user.totalhp/4
score -= 90
end
#---------------------------------------------------------------------------
when "10D"
if @user.pbHasType?(:GHOST)
if @target.effects[PBEffects::Curse]
score -= 90
elsif @user.hp<=@user.totalhp/2
if @battle.pbAbleNonActiveCount(@user.idxOwnSide)==0
score -= 90
else
score -= 50
score -= 30 if @battle.switchStyle
end
end
else
avg = @user.stages[PBStats::SPEED]*10
avg -= @user.stages[PBStats::ATTACK]*10
avg -= @user.stages[PBStats::DEFENSE]*10
score += avg/3
end
#---------------------------------------------------------------------------
when "10E"
score -= 40
#---------------------------------------------------------------------------
when "10F"
if @target.effects[PBEffects::Nightmare] ||
@target.effects[PBEffects::Substitute]>0
score -= 90
elsif !@target.asleep?
score -= 90
else
score -= 90 if @target.statusCount<=1
score += 50 if @target.statusCount>3
end
#---------------------------------------------------------------------------
when "110"
score += 30 if @user.effects[PBEffects::Trapping]>0
score += 30 if @user.effects[PBEffects::LeechSeed]>=0
if @battle.pbAbleNonActiveCount(@user.idxOwnSide)>0
score += 80 if @user.pbOwnSide.effects[PBEffects::Spikes]>0
score += 80 if @user.pbOwnSide.effects[PBEffects::ToxicSpikes]>0
score += 80 if @user.pbOwnSide.effects[PBEffects::StealthRock]
end
#---------------------------------------------------------------------------
when "111"
if @battle.positions[@target.index].effects[PBEffects::FutureSightCounter]>0
score -= 100
elsif @battle.pbAbleNonActiveCount(@user.idxOwnSide)==0
# Future Sight tends to be wasteful if down to last Pokemon
score -= 70
end
#---------------------------------------------------------------------------
when "112"
avg = 0
avg -= @user.stages[PBStats::DEFENSE]*10
avg -= @user.stages[PBStats::SPDEF]*10
score += avg/2
if @user.effects[PBEffects::Stockpile]>=3
score -= 80
else
# More preferable if user also has Spit Up/Swallow
score += 20 if @user.pbHasMoveFunction?("113","114") # Spit Up, Swallow
end
#---------------------------------------------------------------------------
when "113"
score -= 100 if @user.effects[PBEffects::Stockpile]==0
#---------------------------------------------------------------------------
when "114"
if @user.effects[PBEffects::Stockpile]==0
score -= 90
elsif @user.hp==@user.totalhp
score -= 90
else
mult = [0,25,50,100][@user.effects[PBEffects::Stockpile]]
score += mult
score -= @user.hp*mult*2/@user.totalhp
end
#---------------------------------------------------------------------------
when "115"
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
#---------------------------------------------------------------------------
when "116"
#---------------------------------------------------------------------------
when "117"
hasAlly = false
@user.eachAlly do |b|
hasAlly = true
break
end
score -= 90 if !hasAlly
#---------------------------------------------------------------------------
when "118"
if @battle.field.effects[PBEffects::Gravity]>0
score -= 90
elsif skill_check(AILevel.medium)
score -= 30
score -= 20 if @user.effects[PBEffects::SkyDrop]>=0
score -= 20 if @user.effects[PBEffects::MagnetRise]>0
score -= 20 if @user.effects[PBEffects::Telekinesis]>0
score -= 20 if @user.pbHasType?(:FLYING)
score -= 20 if @user.hasActiveAbility?(:LEVITATE)
score -= 20 if @user.hasActiveItem?(:AIRBALLOON)
score += 20 if @target.effects[PBEffects::SkyDrop]>=0
score += 20 if @target.effects[PBEffects::MagnetRise]>0
score += 20 if @target.effects[PBEffects::Telekinesis]>0
score += 20 if @target.inTwoTurnAttack?("0C9","0CC","0CE") # Fly, Bounce, Sky Drop
score += 20 if @target.pbHasType?(:FLYING)
score += 20 if @target.hasActiveAbility?(:LEVITATE)
score += 20 if @target.hasActiveItem?(:AIRBALLOON)
end
#---------------------------------------------------------------------------
when "119"
if @user.effects[PBEffects::MagnetRise]>0 ||
@user.effects[PBEffects::Ingrain] ||
@user.effects[PBEffects::SmackDown]
score -= 90
end
#---------------------------------------------------------------------------
when "11A"
if @target.effects[PBEffects::Telekinesis]>0 ||
@target.effects[PBEffects::Ingrain] ||
@target.effects[PBEffects::SmackDown]
score -= 90
end
#---------------------------------------------------------------------------
when "11B"
#---------------------------------------------------------------------------
when "11C"
if skill_check(AILevel.medium)
score += 20 if @target.effects[PBEffects::MagnetRise]>0
score += 20 if @target.effects[PBEffects::Telekinesis]>0
score += 20 if @target.inTwoTurnAttack?("0C9","0CC") # Fly, Bounce
score += 20 if @target.pbHasType?(:FLYING)
score += 20 if @target.hasActiveAbility?(:LEVITATE)
score += 20 if @target.hasActiveItem?(:AIRBALLOON)
end
#---------------------------------------------------------------------------
when "11D"
#---------------------------------------------------------------------------
when "11E"
#---------------------------------------------------------------------------
when "11F"
#---------------------------------------------------------------------------
when "120"
#---------------------------------------------------------------------------
when "121"
#---------------------------------------------------------------------------
when "122"
#---------------------------------------------------------------------------
when "123"
if !@target.pbHasType?(@user.type1) &&
!@target.pbHasType?(@user.type2)
score -= 90
end
#---------------------------------------------------------------------------
when "124"
#---------------------------------------------------------------------------
when "125"
#---------------------------------------------------------------------------
when "126"
score += 20 # Shadow moves are more preferable
#---------------------------------------------------------------------------
when "127"
score += 20 # Shadow moves are more preferable
if @target.pbCanParalyze?(@user,false)
score += 30
if skill_check(AILevel.medium)
aspeed = pbRoughStat(@user,PBStats::SPEED)
ospeed = pbRoughStat(@target,PBStats::SPEED)
if aspeed<ospeed
score += 30
elsif aspeed>ospeed
score -= 40
end
end
if skill_check(AILevel.high)
score -= 40 if @target.hasActiveAbility?([:GUTS,:MARVELSCALE,:QUICKFEET])
end
end
#---------------------------------------------------------------------------
when "128"
score += 20 # Shadow moves are more preferable
if @target.pbCanBurn?(@user,false)
score += 30
if skill_check(AILevel.high)
score -= 40 if @target.hasActiveAbility?([:GUTS,:MARVELSCALE,:QUICKFEET,:FLAREBOOST])
end
end
#---------------------------------------------------------------------------
when "129"
score += 20 # Shadow moves are more preferable
if @target.pbCanFreeze?(@user,false)
score += 30
if skill_check(AILevel.high)
score -= 20 if @target.hasActiveAbility?(:MARVELSCALE)
end
end
#---------------------------------------------------------------------------
when "12A"
score += 20 # Shadow moves are more preferable
if @target.pbCanConfuse?(@user,false)
score += 30
else
if skill_check(AILevel.medium)
score -= 90
end
end
#---------------------------------------------------------------------------
when "12B"
score += 20 # Shadow moves are more preferable
if !@target.pbCanLowerStatStage?(PBStats::DEFENSE,@user)
score -= 90
else
score += 40 if @user.turnCount==0
score += @target.stages[PBStats::DEFENSE]*20
end
#---------------------------------------------------------------------------
when "12C"
score += 20 # Shadow moves are more preferable
if !@target.pbCanLowerStatStage?(PBStats::EVASION,@user)
score -= 90
else
score += @target.stages[PBStats::EVASION]*15
end
#---------------------------------------------------------------------------
when "12D"
score += 20 # Shadow moves are more preferable
#---------------------------------------------------------------------------
when "12E"
score += 20 # Shadow moves are more preferable
score += 20 if @target.hp>=@target.totalhp/2
score -= 20 if @user.hp<@user.hp/2
#---------------------------------------------------------------------------
when "12F"
score += 20 # Shadow moves are more preferable
score -= 110 if @target.effects[PBEffects::MeanLook]>=0
#---------------------------------------------------------------------------
when "130"
score += 20 # Shadow moves are more preferable
score -= 40
#---------------------------------------------------------------------------
when "131"
score += 20 # Shadow moves are more preferable
if @battle.pbCheckGlobalAbility(:AIRLOCK) ||
@battle.pbCheckGlobalAbility(:CLOUDNINE)
score -= 90
elsif @battle.pbWeather==PBWeather::ShadowSky
score -= 90
end
#---------------------------------------------------------------------------
when "132"
score += 20 # Shadow moves are more preferable
if @target.pbOwnSide.effects[PBEffects::AuroraVeil]>0 ||
@target.pbOwnSide.effects[PBEffects::Reflect]>0 ||
@target.pbOwnSide.effects[PBEffects::LightScreen]>0 ||
@target.pbOwnSide.effects[PBEffects::Safeguard]>0
score += 30
score -= 90 if @user.pbOwnSide.effects[PBEffects::AuroraVeil]>0 ||
@user.pbOwnSide.effects[PBEffects::Reflect]>0 ||
@user.pbOwnSide.effects[PBEffects::LightScreen]>0 ||
@user.pbOwnSide.effects[PBEffects::Safeguard]>0
else
score -= 110
end
#---------------------------------------------------------------------------
when "133", "134"
score -= 95
score = 0 if skill_check(AILevel.high)
#---------------------------------------------------------------------------
when "135"
if @target.pbCanFreeze?(@user,false)
score += 30
if skill_check(AILevel.high)
score -= 20 if @target.hasActiveAbility?(:MARVELSCALE)
end
end
#---------------------------------------------------------------------------
when "136"
score += 20 if @user.stages[PBStats::DEFENSE]<0
#---------------------------------------------------------------------------
when "137"
hasEffect = @user.statStageAtMax?(PBStats::DEFENSE) &&
@user.statStageAtMax?(PBStats::SPDEF)
@user.eachAlly do |b|
next if b.statStageAtMax?(PBStats::DEFENSE) && b.statStageAtMax?(PBStats::SPDEF)
hasEffect = true
score -= b.stages[PBStats::DEFENSE]*10
score -= b.stages[PBStats::SPDEF]*10
end
if hasEffect
score -= @user.stages[PBStats::DEFENSE]*10
score -= @user.stages[PBStats::SPDEF]*10
else
score -= 90
end
#---------------------------------------------------------------------------
when "138"
if @target.statStageAtMax?(PBStats::SPDEF)
score -= 90
else
score -= @target.stages[PBStats::SPDEF]*10
end
#---------------------------------------------------------------------------
when "139"
if !@target.pbCanLowerStatStage?(PBStats::ATTACK,@user)
score -= 90
else
score += @target.stages[PBStats::ATTACK]*20
if skill_check(AILevel.medium)
hasPhysicalAttack = false
@target.eachMove do |m|
next if !m.physicalMove?(m.type)
hasPhysicalAttack = true
break
end
if hasPhysicalAttack
score += 20
elsif skill_check(AILevel.high)
score -= 90
end
end
end
#---------------------------------------------------------------------------
when "13A"
avg = @target.stages[PBStats::ATTACK]*10
avg += @target.stages[PBStats::SPATK]*10
score += avg/2
#---------------------------------------------------------------------------
when "13B"
if !@user.isSpecies?(:HOOPA) || @user.form!=1
score -= 100
else
score += 20 if @target.stages[PBStats::DEFENSE]>0
end
#---------------------------------------------------------------------------
when "13C"
score += 20 if @target.stages[PBStats::SPATK]>0
#---------------------------------------------------------------------------
when "13D"
if !@target.pbCanLowerStatStage?(PBStats::SPATK,@user)
score -= 90
else
score += 40 if @user.turnCount==0
score += @target.stages[PBStats::SPATK]*20
end
#---------------------------------------------------------------------------
when "13E"
count = 0
@battle.eachBattler do |b|
if b.pbHasType?(:GRASS) && !b.airborne? &&
(!b.statStageAtMax?(PBStats::ATTACK) || !b.statStageAtMax?(PBStats::SPATK))
count += 1
if @user.opposes?(b)
score -= 20
else
score -= @user.stages[PBStats::ATTACK]*10
score -= @user.stages[PBStats::SPATK]*10
end
end
end
score -= 95 if count==0
#---------------------------------------------------------------------------
when "13F"
count = 0
@battle.eachBattler do |b|
if b.pbHasType?(:GRASS) && !b.statStageAtMax?(PBStats::DEFENSE)
count += 1
if @user.opposes?(b)
score -= 20
else
score -= @user.stages[PBStats::DEFENSE]*10
end
end
end
score -= 95 if count==0
#---------------------------------------------------------------------------
end
return score
end
end

View File

@@ -0,0 +1,409 @@
class PokeBattle_AI
alias __f__pbGetMoveScoreFunctions pbGetMoveScoreFunctions
#=============================================================================
# Get a score for the given move based on its effect
#=============================================================================
def pbGetMoveScoreFunctions(score)
score = __f__pbGetMoveScoreFunctions(score)
case @move.function
#---------------------------------------------------------------------------
when "140"
count=0
@battle.eachBattler do |b|
if b.poisoned? &&
(!b.statStageAtMin?(PBStats::ATTACK) ||
!b.statStageAtMin?(PBStats::SPATK) ||
!b.statStageAtMin?(PBStats::SPEED))
count += 1
if @user.opposes?(b)
score += @user.stages[PBStats::ATTACK]*10
score += @user.stages[PBStats::SPATK]*10
score += @user.stages[PBStats::SPEED]*10
else
score -= 20
end
end
end
score -= 95 if count==0
#---------------------------------------------------------------------------
when "141"
if @target.effects[PBEffects::Substitute]>0
score -= 90
else
numpos = 0; numneg = 0
PBStats.eachBattleStat do |s|
numpos += @target.stages[s] if @target.stages[s]>0
numneg += @target.stages[s] if @target.stages[s]<0
end
if numpos!=0 || numneg!=0
score += (numpos-numneg)*10
else
score -= 95
end
end
#---------------------------------------------------------------------------
when "142"
score -= 90 if @target.pbHasType?(:GHOST)
#---------------------------------------------------------------------------
when "143"
score -= 90 if @target.pbHasType?(:GRASS)
#---------------------------------------------------------------------------
when "144"
#---------------------------------------------------------------------------
when "145"
aspeed = pbRoughStat(@user,PBStats::SPEED)
ospeed = pbRoughStat(@target,PBStats::SPEED)
score -= 90 if aspeed>ospeed
#---------------------------------------------------------------------------
when "146"
#---------------------------------------------------------------------------
when "147"
#---------------------------------------------------------------------------
when "148"
aspeed = pbRoughStat(@user,PBStats::SPEED)
ospeed = pbRoughStat(@target,PBStats::SPEED)
if aspeed>ospeed
score -= 90
else
score += 30 if @target.pbHasMoveType?(:FIRE)
end
#---------------------------------------------------------------------------
when "149"
if @user.turnCount==0
score += 30
else
score -= 90 # Because it will fail here
score = 0 if skill_check(AILevel.best)
end
#---------------------------------------------------------------------------
when "14A"
#---------------------------------------------------------------------------
when "14B", "14C"
if @user.effects[PBEffects::ProtectRate]>1 ||
@target.effects[PBEffects::HyperBeam]>0
score -= 90
else
if skill_check(AILevel.medium)
score -= @user.effects[PBEffects::ProtectRate]*40
end
score += 50 if @user.turnCount==0
score += 30 if @target.effects[PBEffects::TwoTurnAttack]
end
#---------------------------------------------------------------------------
when "14D"
#---------------------------------------------------------------------------
when "14E"
if @user.statStageAtMax?(PBStats::SPATK) &&
@user.statStageAtMax?(PBStats::SPDEF) &&
@user.statStageAtMax?(PBStats::SPEED)
score -= 90
else
score -= @user.stages[PBStats::SPATK]*10 # Only *10 isntead of *20
score -= @user.stages[PBStats::SPDEF]*10 # because two-turn attack
score -= @user.stages[PBStats::SPEED]*10
if skill_check(AILevel.medium)
hasSpecialAttack = false
@user.eachMove do |m|
next if !m.specialMove?(m.type)
hasSpecialAttack = true
break
end
if hasSpecialAttack
score += 20
elsif skill_check(AILevel.high)
score -= 90
end
end
if skill_check(AILevel.high)
aspeed = pbRoughStat(@user,PBStats::SPEED)
ospeed = pbRoughStat(@target,PBStats::SPEED)
score += 30 if aspeed<ospeed && aspeed*2>ospeed
end
end
#---------------------------------------------------------------------------
when "14F"
if skill_check(AILevel.high) && @target.hasActiveAbility?(:LIQUIDOOZE)
score -= 80
else
score += 40 if @user.hp<=@user.totalhp/2
end
#---------------------------------------------------------------------------
when "150"
score += 20 if !@user.statStageAtMax?(PBStats::ATTACK) && @target.hp<=@target.totalhp/4
#---------------------------------------------------------------------------
when "151"
avg = @target.stages[PBStats::ATTACK]*10
avg += @target.stages[PBStats::SPATK]*10
score += avg/2
#---------------------------------------------------------------------------
when "152"
#---------------------------------------------------------------------------
when "153"
score -= 95 if @target.pbOwnSide.effects[PBEffects::StickyWeb]
#---------------------------------------------------------------------------
when "154"
#---------------------------------------------------------------------------
when "155"
#---------------------------------------------------------------------------
when "156"
#---------------------------------------------------------------------------
when "157"
score -= 90
#---------------------------------------------------------------------------
when "158"
score -= 90 if !@user.belched?
#---------------------------------------------------------------------------
when "159"
if !@target.pbCanPoison?(@user,false) && !@target.pbCanLowerStatStage?(PBStats::SPEED,@user)
score -= 90
else
if @target.pbCanPoison?(@user,false)
score += 30
if skill_check(AILevel.medium)
score += 30 if @target.hp<=@target.totalhp/4
score += 50 if @target.hp<=@target.totalhp/8
score -= 40 if @target.effects[PBEffects::Yawn]>0
end
if skill_check(AILevel.high)
score += 10 if pbRoughStat(@target,PBStats::DEFENSE)>100
score += 10 if pbRoughStat(@target,PBStats::SPDEF)>100
score -= 40 if @target.hasActiveAbility?([:GUTS,:MARVELSCALE,:TOXICBOOST])
end
end
if @target.pbCanLowerStatStage?(PBStats::SPEED,@user)
score += @target.stages[PBStats::SPEED]*10
if skill_check(AILevel.high)
aspeed = pbRoughStat(@user,PBStats::SPEED)
ospeed = pbRoughStat(@target,PBStats::SPEED)
score += 30 if aspeed<ospeed && aspeed*2>ospeed
end
end
end
#---------------------------------------------------------------------------
when "15A"
if @target.opposes?(@user)
score -= 40 if @target.status==PBStatuses::BURN
else
score += 40 if @target.status==PBStatuses::BURN
end
#---------------------------------------------------------------------------
when "15B"
if @target.status==PBStatuses::NONE
score -= 90
elsif @user.hp==@user.totalhp && @target.opposes?(@user)
score -= 90
else
score += (@user.totalhp-@user.hp)*50/@user.totalhp
score -= 30 if @target.opposes?(@user)
end
#---------------------------------------------------------------------------
when "15C"
hasEffect = @user.statStageAtMax?(PBStats::ATTACK) &&
@user.statStageAtMax?(PBStats::SPATK)
@user.eachAlly do |b|
next if b.statStageAtMax?(PBStats::ATTACK) && b.statStageAtMax?(PBStats::SPATK)
hasEffect = true
score -= b.stages[PBStats::ATTACK]*10
score -= b.stages[PBStats::SPATK]*10
end
if hasEffect
score -= @user.stages[PBStats::ATTACK]*10
score -= @user.stages[PBStats::SPATK]*10
else
score -= 90
end
#---------------------------------------------------------------------------
when "15D"
numStages = 0
PBStats.eachBattleStat do |s|
next if @target.stages[s]<=0
numStages += @target.stages[s]
end
score += numStages*20
#---------------------------------------------------------------------------
when "15E"
if @user.effects[PBEffects::LaserFocus]>0
score -= 90
else
score += 40
end
#---------------------------------------------------------------------------
when "15F"
score += @user.stages[PBStats::DEFENSE]*10
#---------------------------------------------------------------------------
when "160"
if @target.statStageAtMin?(PBStats::ATTACK)
score -= 90
else
if @target.pbCanLowerStatStage?(PBStats::ATTACK,@user)
score += @target.stages[PBStats::ATTACK]*20
if skill_check(AILevel.medium)
hasPhysicalAttack = false
@target.eachMove do |m|
next if !m.physicalMove?(m.type)
hasPhysicalAttack = true
break
end
if hasPhysicalAttack
score += 20
elsif skill_check(AILevel.high)
score -= 90
end
end
end
score += (@user.totalhp-@user.hp)*50/@user.totalhp
end
#---------------------------------------------------------------------------
when "161"
if skill_check(AILevel.medium)
if @user.speed>@target.speed
score += 50
else
score -= 70
end
end
#---------------------------------------------------------------------------
when "162"
score -= 90 if !@user.pbHasType?(:FIRE)
#---------------------------------------------------------------------------
when "163"
#---------------------------------------------------------------------------
when "164"
#---------------------------------------------------------------------------
when "165"
if skill_check(AILevel.medium)
userSpeed = pbRoughStat(@user,PBStats::SPEED)
targetSpeed = pbRoughStat(@target,PBStats::SPEED)
if userSpeed<targetSpeed
score += 30
end
else
score += 30
end
#---------------------------------------------------------------------------
when "166"
#---------------------------------------------------------------------------
when "167"
if @user.pbOwnSide.effects[PBEffects::AuroraVeil]>0 || @battle.pbWeather!=PBWeather::Hail
score -= 90
else
score += 40
end
#---------------------------------------------------------------------------
when "168"
if @user.effects[PBEffects::ProtectRate]>1 ||
@target.effects[PBEffects::HyperBeam]>0
score -= 90
else
if skill_check(AILevel.medium)
score -= @user.effects[PBEffects::ProtectRate]*40
end
score += 50 if @user.turnCount==0
score += 30 if @target.effects[PBEffects::TwoTurnAttack]
score += 20 # Because of possible poisoning
end
#---------------------------------------------------------------------------
when "169"
#---------------------------------------------------------------------------
when "16A"
hasAlly = false
@target.eachAlly do |b|
hasAlly = true
break
end
score -= 90 if !hasAlly
#---------------------------------------------------------------------------
when "16B"
if skill_check(AILevel.medium)
if !@target.lastRegularMoveUsed ||
!@target.pbHasMove?(@target.lastRegularMoveUsed) ||
@target.usingMultiTurnAttack?
score -= 90
else
# Without lots of code here to determine good/bad moves and relative
# speeds, using this move is likely to just be a waste of a turn
score -= 50
end
end
#---------------------------------------------------------------------------
when "16C"
if @target.effects[PBEffects::ThroatChop]==0 && skill_check(AILevel.high)
hasSoundMove = false
@user.eachMove do |m|
next if !m.soundMove?
hasSoundMove = true
break
end
score += 40 if hasSoundMove
end
#---------------------------------------------------------------------------
when "16D"
if @user.hp==@user.totalhp || (skill_check(AILevel.medium) && !@user.canHeal?)
score -= 90
else
score += 50
score -= @user.hp*100/@user.totalhp
score += 30 if @battle.pbWeather==PBWeather::Sandstorm
end
#---------------------------------------------------------------------------
when "16E"
if @user.hp==@user.totalhp || (skill_check(AILevel.medium) && !@user.canHeal?)
score -= 90
else
score += 50
score -= @user.hp*100/@user.totalhp
if skill_check(AILevel.medium)
score += 30 if @battle.field.terrain==PBBattleTerrains::Grassy
end
end
#---------------------------------------------------------------------------
when "16F"
if !@target.opposes?(@user)
if @target.hp==@target.totalhp || (skill_check(AILevel.medium) && !@target.canHeal?)
score -= 90
else
score += 50
score -= @target.hp*100/@target.totalhp
end
end
#---------------------------------------------------------------------------
when "170"
reserves = @battle.pbAbleNonActiveCount(@user.idxOwnSide)
foes = @battle.pbAbleNonActiveCount(@user.idxOpposingSide)
if @battle.pbCheckGlobalAbility(:DAMP)
score -= 100
elsif skill_check(AILevel.medium) && reserves==0 && foes>0
score -= 100 # don't want to lose
elsif skill_check(AILevel.high) && reserves==0 && foes==0
score += 80 # want to draw
else
score -= (@user.total.hp-@user.hp)*75/@user.totalhp
end
#---------------------------------------------------------------------------
when "171"
if skill_check(AILevel.medium)
hasPhysicalAttack = false
@target.eachMove do |m|
next if !m.physicalMove?(m.type)
hasPhysicalAttack = true
break
end
score -= 80 if !hasPhysicalAttack
end
#---------------------------------------------------------------------------
when "172"
score += 20 # Because of possible burning
#---------------------------------------------------------------------------
when "173"
#---------------------------------------------------------------------------
when "174"
score -= 90 if @user.turnCount>0 || @user.lastRoundMoved>=0
#---------------------------------------------------------------------------
when "175"
score += 30 if @target.effects[PBEffects::Minimize]
#---------------------------------------------------------------------------
end
return score
end
end

View File

@@ -0,0 +1,725 @@
class PokeBattle_AI
#=============================================================================
#
#=============================================================================
def pbTargetsMultiple?(move)
numTargets = 0
case move.pbTarget(@user)
when PBTargets::AllNearFoes
@battle.eachOtherSideBattler(@user) { |b| numTargets += 1 if b.near?(@user) }
return numTargets > 1
when PBTargets::AllNearOthers
@battle.eachBattler { |b| numTargets += 1 if b.near?(@user) }
return numTargets > 1
when PBTargets::UserAndAllies
@battle.eachSameSideBattler(@user) { |_b| numTargets += 1 }
return numTargets > 1
when PBTargets::AllFoes
@battle.eachOtherSideBattler(@user) { |_b| numTargets += 1 }
return numTargets > 1
when PBTargets::AllBattlers
@battle.eachBattler { |_b| numTargets += 1 }
return numTargets > 1
end
return false
end
#=============================================================================
# Move's type effectiveness
#=============================================================================
def pbCalcTypeModSingle(moveType,defType,user,target)
ret = PBTypes.getEffectiveness(moveType,defType)
# Ring Target
if target.hasActiveItem?(:RINGTARGET)
ret = PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE if PBTypes.ineffective?(moveType,defType)
end
# Foresight
if user.hasActiveAbility?(:SCRAPPY) || target.effects[PBEffects::Foresight]
ret = PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE if defType == :GHOST &&
PBTypes.ineffective?(moveType,defType)
end
# Miracle Eye
if target.effects[PBEffects::MiracleEye]
ret = PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE if defType == :DARK &&
PBTypes.ineffective?(moveType,defType)
end
# Delta Stream's weather
if @battle.pbWeather==PBWeather::StrongWinds
ret = PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE if defType == :FLYING &&
PBTypes.superEffective?(moveType,defType)
end
# Grounded Flying-type Pokémon become susceptible to Ground moves
if !target.airborne?
ret = PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE if defType == :FLYING && moveType == :GROUND
end
return ret
end
def pbCalcTypeMod(moveType,user,target)
return PBTypeEffectiveness::NORMAL_EFFECTIVE if !moveType
return PBTypeEffectiveness::NORMAL_EFFECTIVE if moveType == :GROUND &&
target.pbHasType?(:FLYING) && target.hasActiveItem?(:IRONBALL)
# Determine types
tTypes = target.pbTypes(true)
# Get effectivenesses
typeMods = [PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE] * 3 # 3 types max
tTypes.each_with_index do |type,i|
typeMods[i] = pbCalcTypeModSingle(moveType,type,user,target)
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(battlerThis, _battlerOther)
mod1 = PBTypes.getCombinedEffectiveness(battlerThis.type1, target.type1, target.type2)
return mod1 if battlerThis.type1 == battlerThis.type2
mod2 = PBTypes.getCombinedEffectiveness(battlerThis.type2, target.type1, target.type2)
return mod1 * mod2.to_f / PBTypeEffectivenesss::NORMAL_EFFECTIVE
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 PBTypeEffectiveness.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 PBTypeEffectiveness.notVeryEffective?(typeMod) &&
target.hasActiveAbility?(:WONDERGUARD)
return true if move.damagingMove? && @user.index != target.index && !target.opposes?(@user) &&
target.hasActiveAbility?(:TELEPATHY)
return true if 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 target.effects[PBEffects::Substitute] > 0 && move.statusMove? &&
!move.ignoresSubstitute?(@user) && @user.index != target.index
return true if NEWEST_BATTLE_MECHANICS && @user.hasActiveAbility?(:PRANKSTER) &&
target.pbHasType?(:DARK) && target.opposes?(@user)
return true if move.priority>0 && @battle.field.terrain == PBBattleTerrains::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
if skill_check(AILevel.high)
ret = move.pbCalcType(@user)
end
return ret
end
def pbRoughStat(battler,stat)
return battler.pbSpeed if skill_check(AILevel.high) && stat==PBStats::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 PBStats::ATTACK then value = battler.attack
when PBStats::DEFENSE then value = battler.defense
when PBStats::SPATK then value = battler.spatk
when PBStats::SPDEF then value = battler.spdef
when PBStats::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
when "010" # Stomp
baseDmg *= 2 if target.effects[PBEffects::Minimize]
# Sonic Boom, Dragon Rage, Super Fang, Night Shade, Endeavor
when "06A", "06B", "06C", "06D", "06E"
baseDmg = move.pbFixedDamage(@user, target)
when "06F" # Psywave
baseDmg = @user.level
when "070" # OHKO
baseDmg = target.totalhp
when "071", "072", "073" # Counter, Mirror Coat, Metal Burst
# TODO: Check memory to find the move that did the most damage, and use
# that value (if this move counters it, applying this move's
# doubling effect if appropriate).
baseDmg = 60
when "075", "076", "0D0", "12D" # Surf, Earthquake, Whirlpool, Shadow Storm
baseDmg = move.pbModifyDamage(baseDmg, @user, target)
# Bulldoze, Gust, Twister, Venoshock, Smelling Salts, Wake-Up Slap, Facade,
# Hex, Brine, Retaliate, Weather Ball, Return, Frustration, Eruption,
# Crush Grip, Stored Power, Punishment, Flail, Electro Ball, Low Kick,
# Knock Off, Spit Up, Stomping Tantrum
when "044", "077", "078", "07B", "07C", "07D", "07E", "07F", "080", "085",
"087", "089", "08A", "08B", "08C", "08E", "08F", "098", "099", "09A",
"0F0", "113", "166"
baseDmg = move.pbBaseDamage(baseDmg, @user, target)
when "086" # Acrobatics
baseDmg *= 2 if !@user.item || @user.hasActiveItem?(:FLYINGGEM)
when "08D" # Gyro Ball
target_speed = pbRoughStat(target, PBStats::SPEED)
user_speed = pbRoughStat(@user, PBStats::SPEED)
baseDmg = [[(25 * target_speed / user_speed).floor, 150].min, 1].max
when "091" # Fury Cutter
baseDmg = move.pbBaseDamage(baseDmg, @user, target)
baseDmg *= 2 if baseDmg < 160 && @user.effects[PBEffects::FuryCutter] > 0
when "092" # Echoed Voice
factor = @user.pbOwnSide.effects[PBEffects::EchoedVoiceCounter]
baseDmg *= [(factor + 1), 5].min
when "094" # Present
baseDmg = 50
when "095" # Magnitude
baseDmg = 71
baseDmg *= 2 if target.inTwoTurnAttack?("0CA") # Dig
baseDmg /= 2 if @battle.field.terrain == PBBattleTerrains::Grassy
when "096" # Natural Gift
baseDmg = 0 if !@user.item || !@user.item.is_berry? || !@user.itemActive?
baseDmg = move.pbNaturalGiftBaseDamage(@user.item_id) if baseDmg > 0
when "097" # Trump Card
dmgs = [200, 80, 60, 50, 40]
pp_left = [[move.pp - 1, dmgs.length - 1].min, 0].max
return dmgs[pp_left]
when "09B" # Heavy Slam
baseDmg = move.pbBaseDamage(baseDmg, @user, target)
baseDmg *= 2 if NEWEST_BATTLE_MECHANICS && target.effects[PBEffects::Minimize]
when "0BD", "0BE" # Double Kick, Twineedle
baseDmg *= 2
when "0BF" # Triple Kick
baseDmg *= 6 # Hits do x1, x2, x3 baseDmg in turn, for x6 in total
when "0C0" # Fury Attack
if @user.hasActiveAbility?(:SKILLLINK)
baseDmg *= 5
else
baseDmg = (baseDmg * 19 / 6).floor # Average damage dealt
end
when "0C1" # Beat Up
mult = 0
@battle.eachInTeamFromBattlerIndex(@user.index) do |pkmn, _i|
mult += 1 if pkmn && pkmn.able? && pkmn.status == PBStatuses::NONE
end
baseDmg *= mult
when "0C4" # Solar Beam
baseDmg = move.pbBaseDamageMultiplier(baseDmg, @user, target)
when "0D3" # Rollout
baseDmg *= 2 if @user.effects[PBEffects::DefenseCurl]
when "0D4" # Bide
# TODO: Maybe make this equal to the highest damage a foe has dealt?
baseDmg = 40
when "0E1" # Final Gambit
baseDmg = @user.hp
when "0F7" # Fling
if !@user.item || !@user.itemActive? || @user.unlosableItem?(@user.item) ||
(@user.item.is_berry? && @battle.pbCheckOpposingAbility(:UNNERVE, @user.index))
baseDmg = 0
else
# TODO: Currently assumes a power of 10 if item is unflingable.
baseDmg = move.pbBaseDamage(baseDmg, @user, target)
end
when "144" # Flying Press
if GameData::Type.exists?(:FLYING)
if skill_check(AILevel.high)
targetTypes = target.pbTypes(true)
mult = PBTypes.getCombinedEffectiveness(:FLYING,
targetTypes[0], targetTypes[1], targetTypes[2])
baseDmg = (baseDmg.to_f * mult / PBTypeEffectiveness::NORMAL_EFFECTIVE).round
else
mult = PBTypes.getCombinedEffectiveness(:FLYING,
target.type1, target.type2, target.effects[PBEffects::Type3])
baseDmg = (baseDmg.to_f * mult / PBTypeEffectiveness::NORMAL_EFFECTIVE).round
end
end
baseDmg *= 2 if target.effects[PBEffects::Minimize]
when "175" # Double Iron Bash
baseDmg *= 2
baseDmg *= 2 if target.effects[PBEffects::Minimize]
end
return baseDmg
end
#=============================================================================
# Damage calculation
#=============================================================================
def pbRoughDamage(move,target,baseDmg)
# Fixed damage moves
return baseDmg if move.is_a?(PokeBattle_FixedDamageMove)
# Get the move's type
type = pbRoughType(move)
##### Calculate user's attack stat #####
atk = pbRoughStat(@user,PBStats::ATTACK)
if move.function=="121" # Foul Play
atk = pbRoughStat(target,PBStats::ATTACK)
elsif move.specialMove?(type)
if move.function=="121" # Foul Play
atk = pbRoughStat(target,PBStats::SPATK)
else
atk = pbRoughStat(@user,PBStats::SPATK)
end
end
##### Calculate target's defense stat #####
defense = pbRoughStat(target,PBStats::DEFENSE)
if move.specialMove?(type) && move.function!="122" # Psyshock
defense = pbRoughStat(target,PBStats::SPDEF)
end
##### Calculate all multiplier effects #####
multipliers = [1.0, 1.0, 1.0, 1.0]
# Ability effects that alter damage
mold_breaker = false
if skill_check(AILevel.high) && @user.hasMoldBreaker?
mold_breaker = true
end
if skill_check(AILevel.medium) && @user.abilityActive?
# NOTE: These abilities aren't suitable for checking at the start of the
# round.
if ![:ANALYTIC, :SNIPER, :TINTEDLENS, :AERILATE, :PIXILATE,
:REFRIGERATE].include?(@user.ability_id)
BattleHandlers.triggerDamageCalcUserAbility(@user.ability,
@user,target,move,multipliers,baseDmg,type)
end
end
if skill_check(AILevel.medium) && !mold_breaker
@user.eachAlly do |b|
next if !b.abilityActive?
BattleHandlers.triggerDamageCalcUserAllyAbility(b.ability,
@user,target,move,multipliers,baseDmg,type)
end
end
if skill_check(AILevel.best) && !mold_breaker && target.abilityActive?
# NOTE: These abilities aren't suitable for checking at the start of the
# round.
if ![:FILTER, :SOLIDROCK].include?(target.ability_id)
BattleHandlers.triggerDamageCalcTargetAbility(target.ability,
@user,target,move,multipliers,baseDmg,type)
end
end
if skill_check(AILevel.best) && !mold_breaker
target.eachAlly do |b|
next if !b.abilityActive?
BattleHandlers.triggerDamageCalcTargetAllyAbility(b.ability,
@user,target,move,multipliers,baseDmg,type)
end
end
# Item effects that alter damage
# NOTE: Type-boosting gems aren't suitable for checking at the start of the
# round.
if skill_check(AILevel.medium) && @user.itemActive?
# NOTE: These items aren't suitable for checking at the start of the
# round.
if ![:EXPERTBELT, :LIFEORB].include?(@user.item_id)
BattleHandlers.triggerDamageCalcUserItem(@user.item,
@user,target,move,multipliers,baseDmg,type)
end
# TODO: Prefer (1.5x?) if item will be consumed and user has Unburden.
end
if skill_check(AILevel.best) && target.itemActive?
# NOTE: Type-weakening berries aren't suitable for checking at the start
# of the round.
if !target.item.is_berry?
BattleHandlers.triggerDamageCalcTargetItem(target.item,
@user,target,move,multipliers,baseDmg,type)
end
end
# Global abilities
if skill_check(AILevel.medium)
if (@battle.pbCheckGlobalAbility(:DARKAURA) && type == :DARK) ||
(@battle.pbCheckGlobalAbility(:FAIRYAURA) && type == :FAIRY)
if @battle.pbCheckGlobalAbility(:AURABREAK)
multipliers[BASE_DMG_MULT] *= 2.0 / 3
else
multipliers[BASE_DMG_MULT] *= 4.0 / 3
end
end
end
# Parental Bond
if skill_check(AILevel.medium) && @user.hasActiveAbility?(:PARENTALBOND)
multipliers[BASE_DMG_MULT] *= 1.25
end
# Me First
# TODO
# Helping Hand - n/a
# Charge
if skill_check(AILevel.medium)
if @user.effects[PBEffects::Charge]>0 && type == :ELECTRIC
multipliers[BASE_DMG_MULT] *= 2
end
end
# Mud Sport and Water Sport
if skill_check(AILevel.medium)
if type == :ELECTRIC
@battle.eachBattler do |b|
next if !b.effects[PBEffects::MudSport]
multipliers[BASE_DMG_MULT] /= 3
break
end
if @battle.field.effects[PBEffects::MudSportField]>0
multipliers[BASE_DMG_MULT] /= 3
end
end
if type == :FIRE
@battle.eachBattler do |b|
next if !b.effects[PBEffects::WaterSport]
multipliers[BASE_DMG_MULT] /= 3
break
end
if @battle.field.effects[PBEffects::WaterSportField]>0
multipliers[BASE_DMG_MULT] /= 3
end
end
end
# Terrain moves
if @user.affectedByTerrain? && skill_check(AILevel.medium)
case @battle.field.terrain
when PBBattleTerrains::Electric
multipliers[BASE_DMG_MULT] *= 1.5 if type == :ELECTRIC
when PBBattleTerrains::Grassy
multipliers[BASE_DMG_MULT] *= 1.5 if type == :GRASS
when PBBattleTerrains::Psychic
multipliers[BASE_DMG_MULT] *= 1.5 if type == :PSYCHIC
end
end
if target.affectedByTerrain? && skill_check(AILevel.medium)
if @battle.field.terrain==PBBattleTerrains::Misty && type == :DRAGON
multipliers[BASE_DMG_MULT] /= 2
end
end
# Badge multipliers
if skill_check(AILevel.high)
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.
if target.pbOwnedByPlayer?
if move.physicalMove?(type) && @battle.pbPlayer.numbadges>=NUM_BADGES_BOOST_DEFENSE
multipliers[DEF_MULT] *= 1.1
elsif move.specialMove?(type) && @battle.pbPlayer.numbadges>=NUM_BADGES_BOOST_SPDEF
multipliers[DEF_MULT] *= 1.1
end
end
end
end
# Multi-targeting attacks
if skill_check(AILevel.high)
if pbTargetsMultiple?(move)
multipliers[FINAL_DMG_MULT] *= 0.75
end
end
# Weather
if skill_check(AILevel.medium)
case @battle.pbWeather
when PBWeather::Sun, PBWeather::HarshSun
if type == :FIRE
multipliers[FINAL_DMG_MULT] *= 1.5
elsif type == :WATER
multipliers[FINAL_DMG_MULT] /= 2
end
when PBWeather::Rain, PBWeather::HeavyRain
if type == :FIRE
multipliers[FINAL_DMG_MULT] /= 2
elsif type == :WATER
multipliers[FINAL_DMG_MULT] *= 1.5
end
when PBWeather::Sandstorm
if target.pbHasType?(:ROCK) && move.specialMove?(type) && move.function!="122" # Psyshock
multipliers[DEF_MULT] *= 1.5
end
end
end
# Critical hits - n/a
# Random variance - n/a
# STAB
if skill_check(AILevel.medium)
if type && @user.pbHasType?(type)
if @user.hasActiveAbility?(:ADAPTABILITY)
multipliers[FINAL_DMG_MULT] *= 2
else
multipliers[FINAL_DMG_MULT] = (multipliers[FINAL_DMG_MULT]*1.5).round
end
end
end
# Type effectiveness
if skill_check(AILevel.medium)
typemod = pbCalcTypeMod(type,@user,target)
multipliers[FINAL_DMG_MULT] *= typemod.to_f/PBTypeEffectiveness::NORMAL_EFFECTIVE
end
# Burn
if skill_check(AILevel.high)
if @user.status==PBStatuses::BURN && move.physicalMove?(type) &&
!@user.hasActiveAbility?(:GUTS) &&
!(NEWEST_BATTLE_MECHANICS && move.function=="07E") # Facade
multipliers[FINAL_DMG_MULT] /= 2
end
end
# Aurora Veil, Reflect, Light Screen
if skill_check(AILevel.high)
if !move.ignoresReflect? && !@user.hasActiveAbility?(:INFILTRATOR)
if target.pbOwnSide.effects[PBEffects::AuroraVeil]>0
if @battle.pbSideBattlerCount(target)>1
multipliers[FINAL_DMG_MULT] *= 2.0 / 3
else
multipliers[FINAL_DMG_MULT] /= 2
end
elsif target.pbOwnSide.effects[PBEffects::Reflect]>0 && move.physicalMove?(type)
if @battle.pbSideBattlerCount(target)>1
multipliers[FINAL_DMG_MULT] *= 2.0 / 3
else
multipliers[FINAL_DMG_MULT] /= 2
end
elsif target.pbOwnSide.effects[PBEffects::LightScreen]>0 && move.specialMove?(type)
if @battle.pbSideBattlerCount(target)>1
multipliers[FINAL_DMG_MULT] *= 2.0 / 3
else
multipliers[FINAL_DMG_MULT] /= 2
end
end
end
end
# Minimize
if skill_check(AILevel.high)
if target.effects[PBEffects::Minimize] && move.tramplesMinimize?(2)
multipliers[FINAL_DMG_MULT] *= 2
end
end
# Move-specific base damage modifiers
# TODO
# Move-specific final damage modifiers
# TODO
##### Main damage calculation #####
baseDmg = [(baseDmg * multipliers[BASE_DMG_MULT]).round, 1].max
atk = [(atk * multipliers[ATK_MULT]).round, 1].max
defense = [(defense * multipliers[DEF_MULT]).round, 1].max
damage = (((2.0 * @user.level / 5 + 2).floor * baseDmg * atk / defense).floor / 50).floor + 2
damage = [(damage * multipliers[FINAL_DMG_MULT]).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, PokeBattle_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?(1)
return 100 if target.effects[PBEffects::Telekinesis] > 0
end
# Get base accuracy
baseAcc = move.accuracy
if skill_check(AILevel.medium)
baseAcc = move.pbBaseAccuracy(@user, target)
end
return 100 if baseAcc == 0 && skill_check(AILevel.medium)
# Get the move's type
type = pbRoughType(move)
# Calculate all modifier effects
modifiers = []
modifiers[BASE_ACC] = baseAcc
modifiers[ACC_STAGE] = @user.stages[PBStats::ACCURACY]
modifiers[EVA_STAGE] = target.stages[PBStats::EVASION]
modifiers[ACC_MULT] = 1.0
modifiers[EVA_MULT] = 1.0
pbCalcAccuracyModifiers(target, modifiers, move, type)
# Check if move certainly misses/can't miss
return 0 if modifiers[BASE_ACC] < 0
return 100 if modifiers[BASE_ACC] == 0
# Calculation
accStage = [[modifiers[ACC_STAGE], -6].max, 6].min + 6
evaStage = [[modifiers[EVA_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[ACC_MULT]).round
evasion = (evasion * modifiers[EVA_MULT]).round
evasion = 1 if evasion < 1
return modifiers[BASE_ACC] * accuracy / evasion
end
def pbCalcAccuracyModifiers(target,modifiers,move,type)
mold_breaker = false
if skill_check(AILevel.medium) && @user.hasMoldBreaker?
mold_breaker = true
end
# Ability effects that alter accuracy calculation
if skill_check(AILevel.medium)
if @user.abilityActive?
BattleHandlers.triggerAccuracyCalcUserAbility(@user.ability,
modifiers, @user, target, move, type)
end
end
if skill_check(AILevel.high)
@user.eachAlly do |b|
next if !b.abilityActive?
BattleHandlers.triggerAccuracyCalcUserAllyAbility(b.ability,
modifiers, @user, target, move, type)
end
end
if skill_check(AILevel.best)
if target.abilityActive? && !mold_breaker
BattleHandlers.triggerAccuracyCalcTargetAbility(target.ability,
modifiers, @user, target, move, type)
end
end
# Item effects that alter accuracy calculation
if skill_check(AILevel.medium)
if @user.itemActive?
# TODO: Zoom Lens needs to be checked differently (compare speeds of
# user and target).
BattleHandlers.triggerAccuracyCalcUserItem(@user.item,
modifiers, @user, target, move, type)
end
end
if skill_check(AILevel.high)
if target.itemActive?
BattleHandlers.triggerAccuracyCalcTargetItem(target.item,
modifiers, @user, target, move, type)
end
end
# Other effects, inc. ones that set ACC_MULT or EVA_STAGE to specific values
if @battle.field.effects[PBEffects::Gravity] > 0
modifiers[ACC_MULT] *= 5 / 3.0
end
if skill_check(AILevel.medium)
if @user.effects[PBEffects::MicleBerry]
modifiers[ACC_MULT] *= 1.2
end
modifiers[EVA_STAGE] = 0 if target.effects[PBEffects::Foresight] && modifiers[EVA_STAGE] > 0
modifiers[EVA_STAGE] = 0 if target.effects[PBEffects::MiracleEye] && modifiers[EVA_STAGE] > 0
end
# "AI-specific calculations below"
modifiers[EVA_STAGE] = 0 if move.function == "0A9" # Chip Away
modifiers[BASE_ACC] = 0 if ["0A5", "139", "13A", "13B", "13C", # "Always hit"
"147"].include?(move.function)
if skill_check(AILevel.medium)
modifiers[BASE_ACC] = 0 if @user.effects[PBEffects::LockOn] > 0 &&
@user.effects[PBEffects::LockOnPos] == target.index
end
if skill_check(AILevel.medium)
if move.function == "006" # Toxic
modifiers[BASE_ACC] = 0 if NEWEST_BATTLE_MECHANICS && move.statusMove? &&
@user.pbHasType?(:POISON)
elsif move.function == "070" # OHKO moves
modifiers[BASE_ACC] = move.accuracy + @user.level - target.level
modifiers[BASE_ACC] = -1 if modifiers[BASE_ACC] <= 0 # Certain miss
modifiers[ACC_MULT] = 0 if target.level > @user.level
if skill_check(AILevel.best)
modifiers[ACC_MULT] = 0 if target.hasActiveAbility?(:STURDY) && !mold_breaker
end
end
end
end
#=============================================================================
# Check if battler has a move that meets the criteria in the block provided
#=============================================================================
def check_for_move(battler)
ret = false
battler.eachMove do |move|
next unless yield move
ret = true
break
end
return ret
end
end

View File

@@ -0,0 +1,165 @@
class PokeBattle_AI
#=============================================================================
#
#=============================================================================
# TODO: Reborn has the REVENGEKILLER role which compares mon's speed with
# opponent (only when deciding whether to switch mon in) - this
# comparison should be calculated when needed instead of being a role.
module BattleRole
PHAZER = 0
CLERIC = 1
STALLBREAKER = 2
STATUSABSORBER = 3
BATONPASSER = 4
SPINNER = 5
FIELDSETTER = 6
WEATHERSETTER = 7
SWEEPER = 8
PIVOT = 9
PHYSICALWALL = 10
SPECIALWALL = 11
TANK = 12
TRAPPER = 13
SCREENER = 14
ACE = 15
LEAD = 16
SECOND = 17
end
#=============================================================================
# Determine the roles filled by a Pokémon on a given side at a given party
# index.
#=============================================================================
def determine_roles(side, index)
pkmn = @battle.pbParty(side)[index]
ret = []
return ret if !pkmn || pkmn.egg?
# Check for moves indicative of particular roles
hasHealMove = false
hasPivotMove = false
pkmn.moves.each do |m|
next if !m
move = PokeBattle_Move.pbFromPBMove(@battle, m)
hasHealMove = true if !hasHealMove && move.healingMove?
case move.function
when "004", "0E5", "0EB", "0EC" # Yawn, Perish Song, Roar, Circle Throw
ret.push(BattleRole::PHAZER)
when "019" # Aromatherapy/Heal Bell
ret.push(BattleRole::CLERIC)
when "0BA" # Taunt
ret.push(BattleRole::STALLBREAKER)
when "0D7" # Wish
ret.push(BattleRole::CLERIC) if pkmn.ev[PBStats::HP] == PokeBattle_Pokemon::EV_STAT_LIMIT
when "0D9" # Rest
ret.push(BattleRole::STATUSABSORBER)
when "0ED" # Baton Pass
ret.push(BattleRole::BATONPASSER)
when "0EE" # U-turn
hasPivotMove = true
when "110" # Rapid Spin
ret.push(BattleRole::SPINNER)
when "154", "155", "156", "173" # Terrain moves
ret.push(BattleRole::FIELDSETTER)
else
ret.push(BattleRole::WEATHERSETTER) if move.is_a?(PokeBattle_WeatherMove)
end
end
# Check EVs, nature and moves for combinations indicative of particular roles
if pkmn.ev[PBStats::SPEED] == PokeBattle_Pokemon::EV_STAT_LIMIT
if [PBNatures::MODEST, PBNatures::ADAMANT, # SpAtk+ Atk-, Atk+ SpAtk-
PBNatures::TIMID, PBNatures::JOLLY].include?(pkmn.nature) # Spd+ Atk-, Spd+ SpAtk-
ret.push(BattleRole::SWEEPER)
end
end
if hasHealMove
ret.push(BattleRole::PIVOT) if hasPivotMove
if PBNatures.getStatRaised(pkmn.nature) == PBStats::DEFENSE &&
PBNatures.getStatLowered(pkmn.nature) != PBStats::DEFENSE
ret.push(BattleRole::PHYSICALWALL) if pkmn.ev[PBStats::DEFENSE] == PokeBattle_Pokemon::EV_STAT_LIMIT
elsif PBNatures.getStatRaised(pkmn.nature) == PBStats::SPDEF &&
PBNatures.getStatLowered(pkmn.nature) != PBStats::SPDEF
ret.push(BattleRole::SPECIALWALL) if pkmn.ev[PBStats::SPDEF] == PokeBattle_Pokemon::EV_STAT_LIMIT
end
else
ret.push(BattleRole::TANK) if pkmn.ev[PBStats::HP] == PokeBattle_Pokemon::EV_STAT_LIMIT
end
# Check for abilities indicative of particular roles
case pkmn.ability_id
when :REGENERATOR
ret.push(BattleRole::PIVOT)
when :GUTS, :QUICKFEET, :FLAREBOOST, :TOXICBOOST, :NATURALCURE, :MAGICGUARD,
:MAGICBOUNCE, :HYDRATION
ret.push(BattleRole::STATUSABSORBER)
when :SHADOWTAG, :ARENATRAP, :MAGNETPULL
ret.push(BattleRole::TRAPPER)
when :DROUGHT, :DRIZZLE, :SANDSTREAM, :SNOWWARNING, :PRIMORDIALSEA,
:DESOLATELAND, :DELTASTREAM
ret.push(BattleRole::WEATHERSETTER)
when :GRASSYSURGE, :ELECTRICSURGE, :MISTYSURGE, :PSYCHICSURGE
ret.push(BattleRole::FIELDSETTER)
end
# Check for items indicative of particular roles
case pkmn.item_id
when :LIGHTCLAY
ret.push(BattleRole::SCREENER)
when :ASSAULTVEST
ret.push(BattleRole::TANK)
when :CHOICEBAND, :CHOICESPECS
ret.push(BattleRole::STALLBREAKER)
ret.push(BattleRole::SWEEPER) if pkmn.ev[PBStats::SPEED] == PokeBattle_Pokemon::EV_STAT_LIMIT
when :CHOICESCARF
ret.push(BattleRole::SWEEPER) if pkmn.ev[PBStats::SPEED] == PokeBattle_Pokemon::EV_STAT_LIMIT
when :TOXICORB, :FLAMEORB
ret.push(BattleRole::STATUSABSORBER)
when :TERRAINEXTENDER
ret.push(BattleRole::FIELDSETTER)
end
# Check for position in team, level relative to other levels in team
partyStarts = @battle.pbPartyStarts(side)
if partyStarts.include?(index + 1) || index == @battle.pbParty(side).length - 1
ret.push(BattleRole::ACE)
else
ret.push(BattleRole::LEAD) if partyStarts.include?(index)
idxTrainer = @battle.pbGetOwnerIndexFromPartyIndex(side, index)
maxLevel = @battle.pbMaxLevelInTeam(side, idxTrainer)
if pkmn.level >= maxLevel
ret.push(BattleRole::SECOND)
else
secondHighest = true
seenHigherLevel = false
@battle.eachInTeam(side, @battle.pbGetOwnerIndexFromPartyIndex(side, index)).each do |p|
next if p.level < pkmn.level
if seenHigherLevel
secondHighest = false
break
end
seenHigherLevel = true
end
# NOTE: There can be multiple "second"s if all their levels are equal
# and the highest in the team (and none are the ace).
ret.push(BattleRole::SECOND) if secondHighest
end
end
return ret
end
def check_role(side, idxBattler, *roles)
role_array = @roles[side][idxBattler]
roles.each do |r|
return true if role_array.include?(r)
end
return false
end
def check_battler_role(battler, *roles)
side = idxParty.idxOwnSide
idxParty = idxParty.pokemonIndex
return check_role(side, idxParty, *roles)
end
end

View File

@@ -0,0 +1,86 @@
class PokeBattle_StatUpMove < PokeBattle_Move
attr_reader :statUp
end
class PokeBattle_MultiStatUpMove < PokeBattle_Move
attr_reader :statUp
end
#===============================================================================
# Battle Palace AI.
#===============================================================================
class PokeBattle_AI
alias _battlePalace_pbEnemyShouldWithdraw? pbEnemyShouldWithdraw?
def pbEnemyShouldWithdraw?
return _battlePalace_pbEnemyShouldWithdraw? if !@battlePalace
shouldswitch = false
if @user.effects[PBEffects::PerishSong]==1
shouldswitch = true
elsif !@battle.pbCanChooseAnyMove?(@user.index) &&
@user.turnCount && @user.turnCount>5
shouldswitch = true
else
hppercent = @user.hp*100/@user.totalhp
percents = []
maxindex = -1
maxpercent = 0
factor = 0
@battle.pbParty(@user.index).each_with_index do |pkmn,i|
if @battle.pbCanSwitch?(@user.index,i)
percents[i] = 100*pkmn.hp/pkmn.totalhp
if percents[i]>maxpercent
maxindex = i
maxpercent = percents[i]
end
else
percents[i] = 0
end
end
if hppercent<50
factor = (maxpercent<hppercent) ? 20 : 40
end
if hppercent<25
factor = (maxpercent<hppercent) ? 30 : 50
end
case @user.status
when PBStatuses::SLEEP, PBStatuses::FROZEN
factor += 20
when PBStatuses::POISON, PBStatuses::BURN
factor += 10
when PBStatuses::PARALYSIS
factor += 15
end
if @justswitched[@user.index]
factor -= 60
factor = 0 if factor<0
end
shouldswitch = (pbAIRandom(100)<factor)
if shouldswitch && maxindex>=0
@battle.pbRegisterSwitch(@user.index,maxindex)
return true
end
end
@justswitched[@user.index] = shouldswitch
if shouldswitch
@battle.pbParty(@user.index).each_with_index do |_pkmn,i|
next if !@battle.pbCanSwitch?(@user.index,i)
@battle.pbRegisterSwitch(@user.index,i)
return true
end
end
return false
end
end
#===============================================================================
# Battle Arena AI.
#===============================================================================
class PokeBattle_AI
alias _battleArena_pbEnemyShouldWithdraw? pbEnemyShouldWithdraw?
def pbEnemyShouldWithdraw?
return _battleArena_pbEnemyShouldWithdraw? if !@battleArena
return false
end
end

View File

@@ -0,0 +1,110 @@
=begin
class PBStuff
#Standardized lists of moves with similar purposes/characteristics
#(mostly just "stuff that gets called together")
UNFREEZEMOVE = [PBMoves::FLAMEWHEEL,PBMoves::SACREDFIRE,PBMoves::FLAREBLITZ,
PBMoves::FUSIONFLARE,PBMoves::SCALD,PBMoves::STEAMERUPTION,PBMoves::BURNUP]
SWITCHOUTMOVE = [PBMoves::ROAR,PBMoves::WHIRLWIND,PBMoves::CIRCLETHROW,
PBMoves::DRAGONTAIL,PBMoves::YAWN,PBMoves::PERISHSONG]
SETUPMOVE = [PBMoves::SWORDSDANCE,PBMoves::DRAGONDANCE,PBMoves::CALMMIND,
PBMoves::WORKUP,PBMoves::NASTYPLOT,PBMoves::TAILGLOW,PBMoves::BELLYDRUM,
PBMoves::BULKUP,PBMoves::COIL,PBMoves::CURSE,PBMoves::GROWTH,
PBMoves::HONECLAWS,PBMoves::QUIVERDANCE,PBMoves::SHELLSMASH]
PROTECTMOVE = [PBMoves::PROTECT,PBMoves::DETECT,PBMoves::KINGSSHIELD,
PBMoves::SPIKYSHIELD,PBMoves::BANEFULBUNKER]
PROTECTIGNORINGMOVE = [PBMoves::FEINT,PBMoves::HYPERSPACEHOLE,
PBMoves::HYPERSPACEFURY,PBMoves::SHADOWFORCE,PBMoves::PHANTOMFORCE]
SCREENBREAKERMOVE = [PBMoves::DEFOG,PBMoves::BRICKBREAK,PBMoves::PSYCHICFANGS]
CONTRARYBAITMOVE = [PBMoves::SUPERPOWER,PBMoves::OVERHEAT,PBMoves::DRACOMETEOR,
PBMoves::LEAFSTORM,PBMoves::FLEURCANNON,PBMoves::PSYCHOBOOST]
TWOTURNAIRMOVE = [PBMoves::BOUNCE,PBMoves::FLY,PBMoves::SKYDROP]
PIVOTMOVE = [PBMoves::UTURN,PBMoves::VOLTSWITCH,PBMoves::PARTINGSHOT]
DANCEMOVE = [PBMoves::QUIVERDANCE,PBMoves::DRAGONDANCE,PBMoves::FIERYDANCE,
PBMoves::FEATHERDANCE,PBMoves::PETALDANCE,PBMoves::SWORDSDANCE,
PBMoves::TEETERDANCE,PBMoves::LUNARDANCE,PBMoves::REVELATIONDANCE]
BULLETMOVE = [PBMoves::ACIDSPRAY,PBMoves::AURASPHERE,PBMoves::BARRAGE,
PBMoves::BULLETSEED,PBMoves::EGGBOMB,PBMoves::ELECTROBALL,PBMoves::ENERGYBALL,
PBMoves::FOCUSBLAST,PBMoves::GYROBALL,PBMoves::ICEBALL,PBMoves::MAGNETBOMB,
PBMoves::MISTBALL,PBMoves::MUDBOMB,PBMoves::OCTAZOOKA,PBMoves::ROCKWRECKER,
PBMoves::SEARINGSHOT,PBMoves::SEEDBOMB,PBMoves::SHADOWBALL,PBMoves::SLUDGEBOMB,
PBMoves::WEATHERBALL,PBMoves::ZAPCANNON,PBMoves::BEAKBLAST]
BURNMOVE = [PBMoves::WILLOWISP,PBMoves::SACREDFIRE,PBMoves::INFERNO]
PARAMOVE = [PBMoves::THUNDERWAVE,PBMoves::STUNSPORE,PBMoves::GLARE,
PBMoves::NUZZLE,PBMoves::ZAPCANNON]
SLEEPMOVE = [PBMoves::SPORE,PBMoves::SLEEPPOWDER,PBMoves::HYPNOSIS,PBMoves::DARKVOID,
PBMoves::GRASSWHISTLE,PBMoves::LOVELYKISS,PBMoves::SING]
POISONMOVE = [PBMoves::TOXIC,PBMoves::POISONPOWDER,PBMoves::POISONGAS,PBMoves::TOXICTHREAD]
CONFUMOVE = [PBMoves::CONFUSERAY,PBMoves::SUPERSONIC,PBMoves::FLATTER,PBMoves::SWAGGER,
PBMoves::SWEETKISS,PBMoves::TEETERDANCE,PBMoves::CHATTER,PBMoves::DYNAMICPUNCH]
HEALFUNCTIONS = [0xD5,0xD6,0xD7,0xD8,0xD9,0xDD,0xDE,0xDF,0xE3,0xE4,0x114,0x139,0x158,0x162,0x169,0x16C,0x172]
#massive arrays of stuff that no one wants to see
NATURALGIFTDAMAGE={
100 => [:WATMELBERRY,:DURINBERRY,:BELUEBERRY,:LIECHIBERRY,:GANLONBERRY,:SALACBERRY,
:PETAYABERRY,:APICOTBERRY,:LANSATBERRY,:STARFBERRY,:ENIGMABERRY,:MICLEBERRY,
:CUSTAPBERRY,:JABOCABERRY,:ROWAPBERRY],
90 => [:BLUKBERRY,:NANABBERRY,:WEPEARBERRY,:PINAPBERRY,:POMEGBERRY,:KELPSYBERRY,
:QUALOTBERRY,:HONDEWBERRY,:GREPABERRY,:TAMATOBERRY,:CORNNBERRY,:MAGOSTBERRY,
:RABUTABERRY,:NOMELBERRY,:SPELONBERRY,:PAMTREBERRY],
80 => [:CHERIBERRY,:CHESTOBERRY,:PECHABERRY,:RAWSTBERRY,:ASPEARBERRY,:LEPPABERRY,
:ORANBERRY,:PERSIMBERRY,:LUMBERRY,:SITRUSBERRY,:FIGYBERRY,:WIKIBERRY,
:MAGOBERRY,:AGUAVBERRY,:IAPAPABERRY,:RAZZBERRY,:OCCABERRY,:PASSHOBERRY,
:WACANBERRY,:RINDOBERRY,:YACHEBERRY,:CHOPLEBERRY,:KEBIABERRY,:SHUCABERRY,
:COBABERRY,:PAYAPABERRY,:TANGABERRY,:CHARTIBERRY,:KASIBBERRY,:HABANBERRY,
:COLBURBERRY,:BABIRIBERRY,:CHILANBERRY]}
FLINGDAMAGE={
300 => [:MEMEONADE],
130 => [:IRONBALL],
100 => [:ARMORFOSSIL,:CLAWFOSSIL,:COVERFOSSIL,:DOMEFOSSIL,:HARDSTONE,:HELIXFOSSIL,
:OLDAMBER,:PLUMEFOSSIL,:RAREBONE,:ROOTFOSSIL,:SKULLFOSSIL],
90 => [:DEEPSEATOOTH,:DRACOPLATE,:DREADPLATE,:EARTHPLATE,:FISTPLATE,:FLAMEPLATE,
:GRIPCLAW,:ICICLEPLATE,:INSECTPLATE,:IRONPLATE,:MEADOWPLATE,:MINDPLATE,
:SKYPLATE,:SPLASHPLATE,:SPOOKYPLATE,:STONEPLATE,:THICKCLUB,:TOXICPLATE,
:ZAPPLATE],
80 => [:DAWNSTONE,:DUSKSTONE,:ELECTIRIZER,:MAGMARIZER,:ODDKEYSTONE,:OVALSTONE,
:PROTECTOR,:QUICKCLAW,:RAZORCLAW,:SHINYSTONE,:STICKYBARB,:ASSAULTVEST],
70 => [:BURNDRIVE,:CHILLDRIVE,:DOUSEDRIVE,:DRAGONFANG,:POISONBARB,:POWERANKLET,
:POWERBAND,:POWERBELT,:POWERBRACER,:POWERLENS,:POWERWEIGHT,:SHOCKDRIVE],
60 => [:ADAMANTORB,:DAMPROCK,:HEATROCK,:LUSTROUSORB,:MACHOBRACE,:ROCKYHELMET,
:STICK,:AMPLIFIELDROCK,:ADRENALINEORB],
50 => [:DUBIOUSDISC,:SHARPBEAK],
40 => [:EVIOLITE,:ICYROCK,:LUCKYPUNCH,:PROTECTIVEPADS],
30 => [:ABILITYURGE,:ABSORBBULB,:AMULETCOIN,:ANTIDOTE,:AWAKENING,:BALMMUSHROOM,
:BERRYJUICE,:BIGMUSHROOM,:BIGNUGGET,:BIGPEARL,:BINDINGBAND,:BLACKBELT,
:BLACKFLUTE,:BLACKGLASSES,:BLACKSLUDGE,:BLUEFLUTE,:BLUESHARD,:BURNHEAL,
:CALCIUM,:CARBOS,:CASTELIACONE,:CELLBATTERY,:CHARCOAL,:CLEANSETAG,
:COMETSHARD,:DAMPMULCH,:DEEPSEASCALE,:DIREHIT,:DIREHIT2,:DIREHIT3,
:DRAGONSCALE,:EJECTBUTTON,:ELIXIR,:ENERGYPOWDER,:ENERGYROOT,:ESCAPEROPE,
:ETHER,:EVERSTONE,:EXPSHARE,:FIRESTONE,:FLAMEORB,:FLOATSTONE,:FLUFFYTAIL,
:FRESHWATER,:FULLHEAL,:FULLRESTORE,:GOOEYMULCH,:GREENSHARD,:GROWTHMULCH,
:GUARDSPEC,:HEALPOWDER,:HEARTSCALE,:HONEY,:HPUP,:HYPERPOTION,:ICEHEAL,
:IRON,:ITEMDROP,:ITEMURGE,:KINGSROCK,:LAVACOOKIE,:LEAFSTONE,:LEMONADE,
:LIFEORB,:LIGHTBALL,:LIGHTCLAY,:LUCKYEGG,:MAGNET,:MAXELIXIR,:MAXETHER,
:MAXPOTION,:MAXREPEL,:MAXREVIVE,:METALCOAT,:METRONOME,:MIRACLESEED,
:MOOMOOMILK,:MOONSTONE,:MYSTICWATER,:NEVERMELTICE,:NUGGET,:OLDGATEAU,
:PARLYZHEAL,:PEARL,:PEARLSTRING,:POKEDOLL,:POKETOY,:POTION,:PPMAX,:PPUP,
:PRISMSCALE,:PROTEIN,:RAGECANDYBAR,:RARECANDY,:RAZORFANG,:REDFLUTE,
:REDSHARD,:RELICBAND,:RELICCOPPER,:RELICCROWN,:RELICGOLD,:RELICSILVER,
:RELICSTATUE,:RELICVASE,:REPEL,:RESETURGE,:REVIVALHERB,:REVIVE,:SACREDASH,
:SCOPELENS,:SHELLBELL,:SHOALSALT,:SHOALSHELL,:SMOKEBALL,:SODAPOP,:SOULDEW,
:SPELLTAG,:STABLEMULCH,:STARDUST,:STARPIECE,:SUNSTONE,:SUPERPOTION,
:SUPERREPEL,:SWEETHEART,:THUNDERSTONE,:TINYMUSHROOM,:TOXICORB,
:TWISTEDSPOON,:UPGRADE,:WATERSTONE,:WHITEFLUTE,:XACCURACY,:XACCURACY2,
:XACCURACY3,:XACCURACY6,:XATTACK,:XATTACK2,:XATTACK3,:XATTACK6,:XDEFEND,
:XDEFEND2,:XDEFEND3,:XDEFEND6,:XSPDEF,:XSPDEF2,:XSPDEF3,:XSPDEF6,:XSPECIAL,
:XSPECIAL2,:XSPECIAL3,:XSPECIAL6,:XSPEED,:XSPEED2,:XSPEED3,:XSPEED6,
:YELLOWFLUTE,:YELLOWSHARD,:ZINC,:BIGMALASADA,:ICESTONE],
20 => [:CLEVERWING,:GENIUSWING,:HEALTHWING,:MUSCLEWING,:PRETTYWING,
:RESISTWING,:SWIFTWING],
10 => [:AIRBALLOON,:BIGROOT,:BLUESCARF,:BRIGHTPOWDER,:CHOICEBAND,:CHOICESCARF,
:CHOICESPECS,:DESTINYKNOT,:EXPERTBELT,:FOCUSBAND,:FOCUSSASH,:FULLINCENSE,
:GREENSCARF,:LAGGINGTAIL,:LAXINCENSE,:LEFTOVERS,:LUCKINCENSE,:MENTALHERB,
:METALPOWDER,:MUSCLEBAND,:ODDINCENSE,:PINKSCARF,:POWERHERB,:PUREINCENSE,
:QUICKPOWDER,:REAPERCLOTH,:REDCARD,:REDSCARF,:RINGTARGET,:ROCKINCENSE,
:ROSEINCENSE,:SEAINCENSE,:SHEDSHELL,:SILKSCARF,:SILVERPOWDER,:SMOOTHROCK,
:SOFTSAND,:SOOTHEBELL,:WAVEINCENSE,:WHITEHERB,:WIDELENS,:WISEGLASSES,
:YELLOWSCARF,:ZOOMLENS,:BLUEMIC,:VANILLAIC,:STRAWBIC,:CHOCOLATEIC]}
end
=end

View File

@@ -0,0 +1,165 @@
class PokeBattle_Battle
# Legacy method that should stop being used.
def pbGetOwner(battlerIndex)
if opposes?(battlerIndex)
if @opponent.is_a?(Array)
return (battlerIndex==1) ? @opponent[0] : @opponent[1]
else
return @opponent
end
else
if @player.is_a?(Array)
return (battlerIndex==0) ? @player[0] : @player[1]
else
return @player
end
end
end
# Reborn method (the difference is that each element in "choices" is an array
# in Essentials but just a number in Reborn)
def pbStdDev(choices)
sum = 0
n = 0
choices.each do |c|
sum += c
n += 1
end
return 0 if n<2
mean = sum.to_f/n.to_f
varianceTimesN = 0
for i in 0...choices.length
next if choices[i]<=0
deviation = choices[i].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
##############################################################################
# Choose an action.
##############################################################################
def pbDefaultChooseEnemyCommand(idxBattler)
if !@battle.pbCanShowFightMenu?(idxBattler)
return if pbEnemyShouldUseItem?(idxBattler)
# return if pbEnemyShouldWithdraw?(idxBattler) # Old Switching Method
return if pbShouldSwitch?(idxBattler)
return if @battle.pbAutoFightMenu(idxBattler)
@battle.pbAutoChooseMove(idxBattler)
return
end
pbBuildMoveScores(idxBattler) # Grab the array of scores/targets before doing anything else
return if pbShouldSwitch?(idxBattler)
# return if pbEnemyShouldWithdraw?(idxBattler) # Old Switching Method
return if pbEnemyShouldUseItem?(idxBattler)
return if @battle.pbAutoFightMenu(idxBattler)
@battle.pbRegisterUltraBurst(idxBattler) if pbEnemyShouldUltraBurst?(idxBattler)
@battle.pbRegisterMegaEvolution(idxBattler) if pbEnemyShouldMegaEvolve?(idxBattler)
if pbEnemyShouldZMove?(idxBattler)
return pbChooseEnemyZMove(idxBattler)
end
pbChooseMoves(idxBattler)
end
##############################################################################
#
##############################################################################
def pbChooseEnemyZMove(index) #Put specific cases for trainers using status Z-Moves
chosenmove=false
chosenindex=-1
attacker = @battlers[index]
opponent=attacker.pbOppositeOpposing
otheropp=opponent.pbPartner
skill=pbGetOwner(attacker.index).skill || 0
for i in 0..3
move=@battlers[index].moves[i]
if @battlers[index].pbCompatibleZMoveFromMove?(move)
if move.id == (PBMoves::CONVERSION) || move.id == (PBMoves::SPLASH)
pbRegisterZMove(index)
pbRegisterMove(index,i,false)
pbRegisterTarget(index,opponent.index)
return
end
if !chosenmove
chosenindex = i
chosenmove=move
else
if move.basedamage>chosenmove.basedamage
chosenindex=i
chosenmove=move
end
end
end
end
#oppeff1 = chosenmove.pbTypeModifier(chosenmove.type,attacker,opponent)
oppeff1 = pbTypeModNoMessages(chosenmove.type,attacker,opponent,chosenmove,skill)
#oppeff2 = chosenmove.pbTypeModifier(chosenmove.type,attacker,otheropp)
oppeff2 = pbTypeModNoMessages(chosenmove.type,attacker,otheropp,chosenmove,skill)
oppeff1 = 0 if opponent.hp<(opponent.totalhp/2.0).round
oppeff1 = 0 if (opponent.effects[PBEffects::Substitute]>0 || opponent.effects[PBEffects::Disguise]) && attacker.item!=(PBItems::KOMMONIUMZ2)
oppeff2 = 0 if otheropp.hp<(otheropp.totalhp/2.0).round
oppeff2 = 0 if (otheropp.effects[PBEffects::Substitute]>0 || otheropp.effects[PBEffects::Disguise]) && attacker.item!=(PBItems::KOMMONIUMZ2)
oppmult=0
for i in 1..7 #iterates through all the stats
oppmult+=opponent.stages[i] if opponent.stages[i]>0
end
othermult=0
for i in 1..7
othermult+=otheropp.stages[i] if otheropp.stages[i]>0
end
if (oppeff1<4) && ((oppeff2<4) || otheropp.hp==0)
pbChooseMoves(index)
elsif oppeff1>oppeff2
pbRegisterZMove(index)
pbRegisterMove(index,chosenindex,false)
pbRegisterTarget(index,opponent.index)
elsif oppeff1<oppeff2
pbRegisterZMove(index)
pbRegisterMove(index,chosenindex,false)
pbRegisterTarget(index,otheropp.index)
elsif oppeff1==oppeff2
if oppmult > othermult
pbRegisterZMove(index)
pbRegisterMove(index,chosenindex,false)
pbRegisterTarget(index,opponent.index)
elsif oppmult < othermult
pbRegisterZMove(index)
pbRegisterMove(index,chosenindex,false)
pbRegisterTarget(index,otheropp.index)
else
if otheropp.hp > opponent.hp
pbRegisterZMove(index)
pbRegisterMove(index,chosenindex,false)
pbRegisterTarget(index,otheropp.index)
else
pbRegisterZMove(index)
pbRegisterMove(index,chosenindex,false)
pbRegisterTarget(index,opponent.index)
end
end
end
end
##############################################################################
# Decide whether the opponent should use a Z-Move.
##############################################################################
def pbEnemyShouldZMove?(index)
return pbCanZMove?(index) #Conditions based on effectiveness and type handled later
end
#=============================================================================
# Decide whether the opponent should Ultra Burst their Pokémon.
#=============================================================================
def pbEnemyShouldUltraBurst?(idxBattler)
battler = @battlers[idxBattler]
if pbCanUltraBurst?(idxBattler) # Simple "always should if possible"
PBDebug.log("[AI] #{battler.pbThis} (#{idxBattler}) will Ultra Burst")
return true
end
return false
end
end

View File

@@ -0,0 +1,495 @@
class PokeBattle_Battle
################################################################################
# Decide whether the opponent should use an item on the Pokémon.
################################################################################
def pbEnemyShouldUseItem?(index)
item=pbEnemyItemToUse(index)
if item>0 && @battlers[index].effects[PBEffects::Embargo]==0
pbRegisterItem(index,item,nil)
return true
end
return false
end
def pbEnemyItemAlreadyUsed?(index,item,items)
if @choices[1][0]==3 && @choices[1][1]==item
qty=0
for i in items
qty+=1 if i==item
end
return true if qty<=1
end
return false
end
def pbEnemyItemToUse(index)
return 0 if !@opponent
return 0 if !@internalbattle
items=pbGetOwnerItems(index)
return 0 if !items
skill=pbGetOwner(index).skill || 0
battler=@battlers[index]
party = pbParty(index)
opponent1 = battler.pbOppositeOpposing
opponent2 = opponent1.pbPartner
currentroles = pbGetMonRole(battler,opponent1,skill)
return 0 if battler.isFainted?
highscore = 0
movecount = -1
maxplaypri = -1
partynumber = 0
aimem = getAIMemory(skill,opponent1.pokemonIndex)
for i in party
next if i.nil?
next if i.hp == 0
partynumber+=1
end
itemnumber = 0
for i in items
next if pbEnemyItemAlreadyUsed?(index,i,items)
itemnumber+=1
end
#highest score
for i in battler.moves
scorearray = 0
scorearray = @scores[i] if @scores[i]
if scorearray>100 && i.priority>maxplaypri
maxplaypri = i.priority
end
end
highscore = @scores.max
highdamage = -1
maxopppri = -1
pridam = -1
bestid = -1
#expected damage
#if battler.pbSpeed<pbRoughStat(opponent1,PBStats::SPEED,skill)
if aimem.length > 0
for i in aimem
tempdam = pbRoughDamage(i,opponent1,battler,skill,i.basedamage)
if tempdam>highdamage
highdamage = tempdam
bestid = i.id
end
if i.priority > maxopppri
maxopppri = i.priority
pridam = tempdam
end
end
end
highratio = -1
#expected damage percentage
if battler.hp!=0
highratio = highdamage*(1.0/battler.hp)
end
scorearray = []
arraycount = -1
PBDebug.log(sprintf("Beginning AI Item use check.")) if $INTERNAL
PBDebug.log(sprintf(" ")) if $INTERNAL
for i in items
arraycount+=1
scorearray.push(0)
itemscore=100
ishpitem = false
isstatusitem = false
next if pbEnemyItemAlreadyUsed?(index,i,items)
if (i== PBItems::POTION) ||
(i== PBItems::ULTRAPOTION) ||
(i== PBItems::SUPERPOTION) ||
(i== PBItems::HYPERPOTION) ||
(i== PBItems::MAXPOTION) ||
(i== PBItems::FULLRESTORE) ||
(i== PBItems::FRESHWATER) ||
(i== PBItems::SODAPOP) ||
(i== PBItems::LEMONADE) ||
(i== PBItems::MOOMOOMILK) ||
(i== PBItems::MEMEONADE) ||
(i== PBItems::STRAWBIC) ||
(i== PBItems::CHOCOLATEIC) ||
(i== PBItems::BLUEMIC)
ishpitem=true
end
if (i== PBItems::FULLRESTORE) ||
(i== PBItems::FULLHEAL) ||
(i== PBItems::RAGECANDYBAR) ||
(i== PBItems::LAVACOOKIE) ||
(i== PBItems::OLDGATEAU) ||
(i== PBItems::CASTELIACONE) ||
(i== PBItems::LUMIOSEGALETTE) ||
(i== PBItems::BIGMALASADA)
isstatusitem=true
end
if ishpitem
PBDebug.log(sprintf("This is a HP-healing item.")) if $INTERNAL
restoreamount=0
if (i== PBItems::POTION)
restoreamount=20
elsif (i== PBItems::ULTRAPOTION)
restoreamount=200
elsif (i== PBItems::SUPERPOTION)
restoreamount=60
elsif (i== PBItems::HYPERPOTION)
restoreamount=120
elsif (i== PBItems::MAXPOTION) || (i== PBItems::FULLRESTORE)
restoreamount=battler.totalhp
elsif (i== PBItems::FRESHWATER)
restoreamount=30
elsif (i== PBItems::SODAPOP)
restoreamount=50
elsif (i== PBItems::LEMONADE)
restoreamount=70
elsif (i== PBItems::MOOMOOMILK)
restoreamount=110
elsif (i== PBItems::MEMEONADE)
restoreamount=103
elsif (i== PBItems::STRAWBIC)
restoreamount=90
elsif (i== PBItems::CHOCOLATEIC)
restoreamount=70
elsif (i== PBItems::BLUEMIC)
restoreamount=200
end
resratio=restoreamount*(1.0/battler.totalhp)
itemscore*= (2 - (2*(battler.hp*(1.0/battler.totalhp))))
if highdamage>=battler.hp
if highdamage > [battler.hp+restoreamount,battler.totalhp].min
itemscore*=0
else
itemscore*=1.2
end
healmove = false
for j in battler.moves
if j.isHealingMove?
healmove=true
end
end
if healmove
if battler.pbSpeed < opponent1.pbSpeed
if highdamage>=battler.hp
itemscore*=1.1
else
itemscore*=0.6
if resratio<0.55
itemscore*=0.2
end
end
end
end
else
itemscore*=0.4
end
if highdamage > restoreamount
itemscore*=0
else
if restoreamount-highdamage < 15
itemscore*=0.5
end
end
if battler.pbSpeed > opponent1.pbSpeed
itemscore*=0.8
if highscore >=110
if maxopppri > maxplaypri
itemscore*=1.3
if pridam>battler.hp
if pridam>(battler.hp/2.0)
itemscore*=0
else
itemscore*=2
end
end
elsif !(!opponent1.abilitynulled && opponent1.ability == PBAbilities::STURDY)
itemscore*=0
end
end
if currentroles.include?(PBMonRoles::SWEEPER)
itemscore*=1.1
end
else
if highdamage*2 > [battler.hp+restoreamount,battler.totalhp].min
itemscore*=0
else
itemscore*=1.5
if highscore >=110
itemscore*=1.5
end
end
end
if battler.hp == battler.totalhp
itemscore*=0
elsif battler.hp >= (battler.totalhp*0.8)
itemscore*=0.2
elsif battler.hp >= (battler.totalhp*0.6)
itemscore*=0.3
elsif battler.hp >= (battler.totalhp*0.5)
itemscore*=0.5
end
minipot = (partynumber-1)
minimini = -1
for j in items
next if pbEnemyItemAlreadyUsed?(index,j,items)
next if !((j== PBItems::POTION) || (j== PBItems::ULTRAPOTION) ||
(j== PBItems::SUPERPOTION) || (j== PBItems::HYPERPOTION) ||
(j== PBItems::MAXPOTION) || (j== PBItems::FULLRESTORE) ||
(j== PBItems::FRESHWATER) || (j== PBItems::SODAPOP) ||
(j== PBItems::LEMONADE) || (j== PBItems::MOOMOOMILK) ||
(j== PBItems::MEMEONADE) || (j== PBItems::STRAWBIC) ||
(j== PBItems::CHOCOLATEIC) || (j== PBItems::BLUEMIC))
minimini+=1
end
if minipot>minimini
itemscore*=(0.9**(minipot-minimini))
minipot=minimini
elsif minimini>minipot
itemscore*=(1.1**(minimini-minipot))
minimini=minipot
end
if currentroles.include?(PBMonRoles::LEAD) || currentroles.include?(PBMonRoles::SCREENER)
itemscore*=0.6
end
if currentroles.include?(PBMonRoles::TANK)
itemscore*=1.1
end
if currentroles.include?(PBMonRoles::SECOND)
itemscore*=1.1
end
if battler.hasWorkingItem(:LEFTOVERS) || (battler.hasWorkingItem(:BLACKSLUDGE) && battler.pbHasType?(:POISON))
itemscore*=0.9
end
if battler.status!=0 && !(i== PBItems::FULLRESTORE)
itemscore*=0.7
if battler.effects[PBEffects::Toxic]>0 && partynumber>1
itemscore*=0.2
end
end
if PBTypes.getCombinedEffectiveness(opponent1.type1,battler.type1,battler.type2)>4
itemscore*=0.7
elsif PBTypes.getCombinedEffectiveness(opponent1.type1,battler.type1,battler.type2)<4
itemscore*=1.1
if PBTypes.getCombinedEffectiveness(opponent1.type1,battler.type1,battler.type2)==0
itemscore*=1.2
end
end
if PBTypes.getCombinedEffectiveness(opponent1.type2,battler.type1,battler.type2)>4
itemscore*=0.6
elsif PBTypes.getCombinedEffectiveness(opponent1.type1,battler.type1,battler.type2)<4
itemscore*=1.1
if PBTypes.getCombinedEffectiveness(opponent1.type1,battler.type1,battler.type2)==0
itemscore*=1.2
end
end
if (!battler.abilitynulled && battler.ability == PBAbilities::REGENERATOR) && partynumber>1
itemscore*=0.7
end
end
if isstatusitem
PBDebug.log(sprintf("This is a status-curing item.")) if $INTERNAL
if !(i== PBItems::FULLRESTORE)
if battler.status==0
itemscore*=0
else
if highdamage>battler.hp
if (bestid==106 && battler.status==PBStatuses::SLEEP) || (bestid==298 && battler.status==PBStatuses::PARALYSIS) || bestid==179
if highdamage*0.5>battler.hp
itemscore*=0
else
itemscore*=1.4
end
else
itemscore*=0
end
end
end
if battler.status==PBStatuses::SLEEP
if battler.pbHasMove?((PBMoves::SLEEPTALK)) ||
battler.pbHasMove?((PBMoves::SNORE)) ||
battler.pbHasMove?((PBMoves::REST)) ||
(!battler.abilitynulled && battler.ability == PBAbilities::COMATOSE)
itemscore*=0.6
end
if checkAImoves([PBMoves::DREAMEATER,PBMoves::NIGHTMARE],aimem) || (!opponent1.abilitynulled && opponent1.ability == PBAbilities::BADDREAMS)
itemscore*=1.3
end
if highdamage*(1.0/battler.hp)>0.2
itemscore*=1.3
else
itemscore*=0.7
end
end
if battler.status==PBStatuses::PARALYSIS
if (!battler.abilitynulled && battler.ability == PBAbilities::QUICKFEET) || (!battler.abilitynulled && battler.ability == PBAbilities::GUTS)
itemscore*=0.5
end
if battler.pbSpeed>opponent1.pbSpeed && (battler.pbSpeed*0.5)<opponent1.pbSpeed
itemscore*=1.3
end
itemscore*=1.1
end
if battler.status==PBStatuses::BURN
itemscore*=1.1
if battler.attack>battler.spatk
itemscore*=1.2
else
itemscore*=0.8
end
if !battler.abilitynulled
itemscore*=0.6 if battler.ability == PBAbilities::GUTS
itemscore*=0.7 if battler.ability == PBAbilities::MAGICGUARD
itemscore*=0.8 if battler.ability == PBAbilities::FLAREBOOST
end
end
if battler.status==PBStatuses::POISON
itemscore*=1.1
if !battler.abilitynulled
itemscore*=0.5 if battler.ability == PBAbilities::GUTS
itemscore*=0.5 if battler.ability == PBAbilities::MAGICGUARD
itemscore*=0.5 if battler.ability == PBAbilities::TOXICBOOST
itemscore*=0.4 if battler.ability == PBAbilities::POISONHEAL
end
if battler.effects[PBEffects::Toxic]>0
itemscore*=1.1
if battler.effects[PBEffects::Toxic]>3
itemscore*=1.3
end
end
end
if battler.status==PBStatuses::FROZEN
itemscore*=1.3
thawmove=false
for j in battler.moves
if j.canThawUser?
thawmove=true
end
end
if thawmove
itemscore*=0.5
end
if highdamage*(1.0/battler.hp)>0.15
itemscore*=1.1
else
itemscore*=0.9
end
end
end
if battler.pbHasMove?((PBMoves::REFRESH)) ||
battler.pbHasMove?((PBMoves::REST)) ||
battler.pbHasMove?((PBMoves::PURIFY))
itemscore*=0.5
end
if (!battler.abilitynulled && battler.ability == PBAbilities::NATURALCURE) && partynumber>1
itemscore*=0.2
end
if (!battler.abilitynulled && battler.ability == PBAbilities::SHEDSKIN)
itemscore*=0.3
end
end
if partynumber==1 || currentroles.include?(PBMonRoles::ACE)
itemscore*=1.2
else
itemscore*=0.8
if battler.itemUsed2
itemscore*=0.6
end
end
if battler.effects[PBEffects::Confusion]>0
itemscore*=0.9
end
if battler.effects[PBEffects::Attract]>=0
itemscore*=0.6
end
if battler.effects[PBEffects::Substitute]>0
itemscore*=1.1
end
if battler.effects[PBEffects::LeechSeed]>=0
itemscore*=0.5
end
if battler.effects[PBEffects::Curse]
itemscore*=0.5
end
if battler.effects[PBEffects::PerishSong]>0
itemscore*=0.2
end
minipot=0
for s in [PBStats::ATTACK,PBStats::DEFENSE,PBStats::SPEED,
PBStats::SPATK,PBStats::SPDEF,PBStats::ACCURACY,PBStats::EVASION]
minipot+=battler.stages[s]
end
if currentroles.include?(PBMonRoles::PHYSICALWALL) || currentroles.include?(PBMonRoles::SPECIALWALL)
for s in [PBStats::DEFENSE,PBStats::SPDEF]
minipot+=battler.stages[s]
end
end
if currentroles.include?(PBMonRoles::SWEEPER)
for s in [PBStats::SPEED]
minipot+=battler.stages[s]
end
if battler.attack>battler.spatk
for s in [PBStats::ATTACK]
minipot+=battler.stages[s]
end
else
for s in [PBStats::SPATK]
minipot+=battler.stages[s]
end
end
end
minipot*=5
minipot+=100
minipot*=0.01
itemscore*=minipot
if opponent1.effects[PBEffects::TwoTurnAttack]>0 || opponent1.effects[PBEffects::HyperBeam]>0
itemscore*=1.2
end
if highscore>70
itemscore*=1.1
else
itemscore*=0.9
end
fielddisrupt = getFieldDisruptScore(battler,opponent1,skill)
if fielddisrupt <= 0
fielddisrupt=0.6
end
itemscore*= (1.0/fielddisrupt)
if @trickroom > 0
itemscore*=0.9
end
if battler.pbOwnSide.effects[PBEffects::Tailwind]>0
itemscore*=0.6
end
if battler.pbOwnSide.effects[PBEffects::Reflect]>0
itemscore*=0.9
end
if battler.pbOwnSide.effects[PBEffects::LightScreen]>0
itemscore*=0.9
end
if battler.pbOwnSide.effects[PBEffects::AuroraVeil]>0
itemscore*=0.8
end
if @doublebattle
itemscore*=0.8
end
itemscore-=100
PBDebug.log(sprintf("Score for %s: %d",PBItems.getName(i),itemscore)) if $INTERNAL
scorearray[arraycount] = itemscore
end
bestitem=-1
bestscore=-10000
counter=-1
for k in scorearray
counter+=1
if k>bestscore
bestscore = k
bestitem = items[counter]
end
end
PBDebug.log(sprintf("Highest item score: %d",bestscore)) if $INTERNAL
PBDebug.log(sprintf("Highest move score: %d",highscore)) if $INTERNAL
if highscore<bestscore
PBDebug.log(sprintf("Using %s",PBItems.getName(bestitem))) if $INTERNAL
return bestitem
else
PBDebug.log(sprintf("Not using an item.")) if $INTERNAL
PBDebug.log(sprintf(" ")) if $INTERNAL
return 0
end
end
end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,121 @@
class PokeBattle_Battle
attr_accessor :aiMoveMemory
alias __ai__initialize initialize
def initialize(scene,p1,p2,player,opponent)
__ai__initialize(scene,p1,p2,player,opponent)
@aiMoveMemory = [[],
[],
[[], [], [], [], [], [], [], [], [], [], [], []] # One array for each party index
]
end
################################################################################
# AI Memory utility functions
################################################################################
def getAIMemory(skill,index=0)
if skill>=PBTrainerAI.bestSkill
return @aiMoveMemory[2][index]
elsif skill>=PBTrainerAI.highSkill
return @aiMoveMemory[1]
elsif skill>=PBTrainerAI.mediumSkill
return @aiMoveMemory[0]
else
return []
end
end
def checkAImoves(moveID,memory)
#basic "does the other mon have x"
return false if memory.length == 0
for i in moveID
for j in memory
j = pbChangeMove(j,nil)#doesn't matter that i'm passing nil, won't get used
return true if i == j.id #i should already be an ID here
end
end
return false
end
def checkAIhealing(memory)
#less basic "can the other mon heal"
return false if memory.length == 0
for j in memory
return true if j.isHealingMove?
end
return false
end
def checkAIpriority(memory)
#"does the other mon have priority"
return false if memory.length == 0
for j in memory
return true if j.priority>0
end
return false
end
def checkAIaccuracy(memory)
#"does the other mon have moves that don't miss"
return false if memory.length == 0
for j in memory
j = pbChangeMove(j,nil)
return true if j.accuracy==0
end
return false
end
def checkAIdamage(memory,attacker,opponent,skill)
#returns how much damage the AI expects to take
return -1 if memory.length == 0
maxdam=0
for j in memory
tempdam = pbRoughDamage(j,opponent,attacker,skill,j.basedamage)
maxdam=tempdam if tempdam>maxdam
end
return maxdam
end
def checkAIbest(memory,modifier,type=[],usepower=true,attacker=nil,opponent=nil,skill=nil)
return false if memory.length == 0
#had to split this because switching ai uses power
bestmove = 0
if usepower
biggestpower = 0
for j in memory
if j.basedamage>biggestpower
biggestpower=j.basedamage
bestmove=j
end
end
else #maxdam
maxdam=0
for j in memory
tempdam = pbRoughDamage(j,opponent,attacker,skill,j.basedamage)
if tempdam>maxdam
maxdam=tempdam
bestmove=j
end
end
end
return false if bestmove==0
#i don't want to make multiple functions for rare cases
#we're doing it in one and you're gonna like it
case modifier
when 1 #type mod. checks types from a list.
return true if type.include?(bestmove.type)
when 2 #physical mod.
return true if bestmove.pbIsPhysical?(bestmove.type)
when 3 #special mod.
return true if bestmove.pbIsSpecial?(bestmove.type)
when 4 #contact mod.
return true if bestmove.isContactMove?
when 5 #sound mod.
return true if bestmove.isSoundBased?
when 6 #why.
return true if (PBStuff::BULLETMOVE).include?(bestmove.id)
end
return false #you're still here? it's over! go home.
end
end

View File

@@ -0,0 +1,974 @@
class PokeBattle_Battle
=begin
# Added to Essentials
def pbStatusDamage(move)
if (move.id == PBMoves::AFTERYOU || move.id == PBMoves::BESTOW || # 11D, 0F3
move.id == PBMoves::CRAFTYSHIELD || move.id == PBMoves::LUCKYCHANT || # 14A, 0A1
move.id == PBMoves::MEMENTO || move.id == PBMoves::QUASH || # 0E2, 11E
move.id == PBMoves::SAFEGUARD || move.id == PBMoves::SPITE || # 01A, 10E
move.id == PBMoves::SPLASH || move.id == PBMoves::SWEETSCENT || # 001, 048
move.id == PBMoves::TELEKINESIS || move.id == PBMoves::TELEPORT) # 11A, 0EA
# "001", "01A", "048", "0A1", "0E2", "0EA", "0F3", "10E", "11A", "11D", "11E", "14A"
return 0
elsif (move.id == PBMoves::ALLYSWITCH || move.id == PBMoves::AROMATICMIST ||
move.id == PBMoves::CONVERSION || move.id == PBMoves::ENDURE ||
move.id == PBMoves::ENTRAINMENT || move.id == PBMoves::FLOWERSHIELD ||
move.id == PBMoves::FORESIGHT || move.id == PBMoves::FORESTSCURSE ||
move.id == PBMoves::GRAVITY || move.id == PBMoves::DEFOG ||
move.id == PBMoves::GUARDSWAP || move.id == PBMoves::HEALBLOCK ||
move.id == PBMoves::IMPRISON || move.id == PBMoves::INSTRUCT ||
move.id == PBMoves::FAIRYLOCK || move.id == PBMoves::LASERFOCUS ||
move.id == PBMoves::HELPINGHAND || move.id == PBMoves::MAGICROOM ||
move.id == PBMoves::MAGNETRISE || move.id == PBMoves::SOAK ||
move.id == PBMoves::LOCKON || move.id == PBMoves::MINDREADER ||
move.id == PBMoves::MIRACLEEYE || move.id == PBMoves::MUDSPORT ||
move.id == PBMoves::NIGHTMARE || move.id == PBMoves::ODORSLEUTH ||
move.id == PBMoves::POWERSPLIT || move.id == PBMoves::POWERSWAP ||
move.id == PBMoves::GRUDGE || move.id == PBMoves::GUARDSPLIT ||
move.id == PBMoves::POWERTRICK || move.id == PBMoves::QUICKGUARD ||
move.id == PBMoves::RECYCLE || move.id == PBMoves::REFLECTTYPE ||
move.id == PBMoves::ROTOTILLER || move.id == PBMoves::SANDATTACK ||
move.id == PBMoves::SKILLSWAP || move.id == PBMoves::SNATCH ||
move.id == PBMoves::MAGICCOAT || move.id == PBMoves::SPEEDSWAP ||
move.id == PBMoves::SPOTLIGHT || move.id == PBMoves::SWALLOW ||
move.id == PBMoves::TEETERDANCE || move.id == PBMoves::WATERSPORT ||
move.id == PBMoves::WIDEGUARD || move.id == PBMoves::WONDERROOM)
# "013", "047", "049", "052", "053", "057", "058", "059", "05E", "061",
# "062", "066", "067", "09C", "09D", "09E", "0A6", "0A6", "0A7", "0A7",
# "0A8", "0AB", "0AC", "0B1", "0B2", "0B8", "0BB", "0E6", "0E8", "0F6",
# "0F9", "10F", "114", "118", "119", "120", "124", "138", "13E", "13F",
# "143", "152", "15E", "161", "16A", "16B"
return 5
elsif (move.id == PBMoves::ACUPRESSURE || move.id == PBMoves::CAMOUFLAGE ||
move.id == PBMoves::CHARM || move.id == PBMoves::CONFIDE ||
move.id == PBMoves::DEFENSECURL || move.id == PBMoves::GROWTH ||
move.id == PBMoves::EMBARGO || move.id == PBMoves::FLASH ||
move.id == PBMoves::FOCUSENERGY || move.id == PBMoves::GROWL ||
move.id == PBMoves::HARDEN || move.id == PBMoves::HAZE ||
move.id == PBMoves::HONECLAWS || move.id == PBMoves::HOWL ||
move.id == PBMoves::KINESIS || move.id == PBMoves::LEER ||
move.id == PBMoves::METALSOUND || move.id == PBMoves::NOBLEROAR ||
move.id == PBMoves::PLAYNICE || move.id == PBMoves::POWDER ||
move.id == PBMoves::PSYCHUP || move.id == PBMoves::SHARPEN ||
move.id == PBMoves::SMOKESCREEN || move.id == PBMoves::STRINGSHOT ||
move.id == PBMoves::SUPERSONIC || move.id == PBMoves::TAILWHIP ||
move.id == PBMoves::TEARFULLOOK || move.id == PBMoves::TORMENT ||
move.id == PBMoves::WITHDRAW || move.id == PBMoves::WORKUP)
# "013", "01C", "01C", "01D", "01D", "01E", "023", "027", "028", "029",
# "037", "042", "043", "043", "047", "047", "047", "04B", "04D", "04F",
# "051", "055", "060", "0B7", "0F8", "139", "13A", "13A", "13C", "148"
return 10
elsif (move.id == PBMoves::ASSIST || move.id == PBMoves::BABYDOLLEYES ||
move.id == PBMoves::CAPTIVATE || move.id == PBMoves::COTTONSPORE ||
move.id == PBMoves::DARKVOID || move.id == PBMoves::AGILITY ||
move.id == PBMoves::DOUBLETEAM || move.id == PBMoves::EERIEIMPULSE ||
move.id == PBMoves::FAKETEARS || move.id == PBMoves::FEATHERDANCE ||
move.id == PBMoves::FLORALHEALING || move.id == PBMoves::GRASSWHISTLE ||
move.id == PBMoves::HEALPULSE || move.id == PBMoves::HEALINGWISH ||
move.id == PBMoves::HYPNOSIS || move.id == PBMoves::INGRAIN ||
move.id == PBMoves::LUNARDANCE || move.id == PBMoves::MEFIRST ||
move.id == PBMoves::MEDITATE || move.id == PBMoves::MIMIC ||
move.id == PBMoves::PARTINGSHOT || move.id == PBMoves::POISONPOWDER ||
move.id == PBMoves::REFRESH || move.id == PBMoves::ROLEPLAY ||
move.id == PBMoves::SCARYFACE || move.id == PBMoves::SCREECH ||
move.id == PBMoves::SING || move.id == PBMoves::SKETCH ||
move.id == PBMoves::TICKLE || move.id == PBMoves::CHARGE ||
move.id == PBMoves::TRICKORTREAT || move.id == PBMoves::VENOMDRENCH ||
move.id == PBMoves::GEARUP || move.id == PBMoves::MAGNETICFLUX ||
move.id == PBMoves::SANDSTORM || move.id == PBMoves::HAIL ||
move.id == PBMoves::SUNNYDAY || move.id == PBMoves::RAINDANCE)
# "003", "003", "003", "003", "005", "018", "01C", "021", "022", "030",
# "042", "04A", "04B", "04C", "04D", "04D", "04E", "04F", "05C", "05D",
# "065", "0B0", "0B5", "0DB", "0DF", "0E3", "0E4", "0FF", "100", "101",
# "102", "137", "13D", "140", "142", "151", "15C", "16E"
return 15
elsif (move.id == PBMoves::AQUARING || move.id == PBMoves::BLOCK ||
move.id == PBMoves::CONVERSION2 || move.id == PBMoves::ELECTRIFY ||
move.id == PBMoves::FLATTER || move.id == PBMoves::GASTROACID ||
move.id == PBMoves::HEARTSWAP || move.id == PBMoves::IONDELUGE ||
move.id == PBMoves::MEANLOOK || move.id == PBMoves::LOVELYKISS ||
move.id == PBMoves::METRONOME || move.id == PBMoves::COPYCAT ||
move.id == PBMoves::MIRRORMOVE || move.id == PBMoves::MIST ||
move.id == PBMoves::PERISHSONG || move.id == PBMoves::REST ||
move.id == PBMoves::ROAR || move.id == PBMoves::SIMPLEBEAM ||
move.id == PBMoves::SLEEPPOWDER || move.id == PBMoves::SPIDERWEB ||
move.id == PBMoves::SWAGGER || move.id == PBMoves::SWEETKISS ||
move.id == PBMoves::POISONGAS || move.id == PBMoves::TOXICTHREAD ||
move.id == PBMoves::TRANSFORM || move.id == PBMoves::WHIRLWIND ||
move.id == PBMoves::WORRYSEED || move.id == PBMoves::YAWN)
# "003", "003", "004", "005", "013", "040", "041", "054", "056", "05F",
# "063", "064", "068", "069", "0AE", "0AF", "0B6", "0D9", "0DA", "0E5",
# "0EB", "0EB", "0EF", "0EF", "0EF", "145", "146", "159"
return 20
elsif (move.id == PBMoves::AMNESIA || move.id == PBMoves::ATTRACT ||
move.id == PBMoves::BARRIER || move.id == PBMoves::BELLYDRUM ||
move.id == PBMoves::CONFUSERAY || move.id == PBMoves::DESTINYBOND ||
move.id == PBMoves::DETECT || move.id == PBMoves::DISABLE ||
move.id == PBMoves::ACIDARMOR || move.id == PBMoves::COSMICPOWER ||
move.id == PBMoves::COTTONGUARD || move.id == PBMoves::DEFENDORDER ||
move.id == PBMoves::FOLLOWME || move.id == PBMoves::AUTOTOMIZE ||
move.id == PBMoves::HEALORDER || move.id == PBMoves::IRONDEFENSE ||
move.id == PBMoves::LEECHSEED || move.id == PBMoves::MILKDRINK ||
move.id == PBMoves::MINIMIZE || move.id == PBMoves::MOONLIGHT ||
move.id == PBMoves::MORNINGSUN || move.id == PBMoves::PAINSPLIT ||
move.id == PBMoves::PROTECT || move.id == PBMoves::PSYCHOSHIFT ||
move.id == PBMoves::RAGEPOWDER || move.id == PBMoves::ROOST ||
move.id == PBMoves::RECOVER || move.id == PBMoves::ROCKPOLISH ||
move.id == PBMoves::SHOREUP || move.id == PBMoves::SLACKOFF ||
move.id == PBMoves::SOFTBOILED || move.id == PBMoves::STRENGTHSAP ||
move.id == PBMoves::STOCKPILE || move.id == PBMoves::STUNSPORE ||
move.id == PBMoves::SUBSTITUTE ||
move.id == PBMoves::SWITCHEROO || move.id == PBMoves::SYNTHESIS ||
move.id == PBMoves::TAUNT || move.id == PBMoves::TOPSYTURVY ||
move.id == PBMoves::TOXIC || move.id == PBMoves::TRICK ||
move.id == PBMoves::WILLOWISP || move.id == PBMoves::WISH)
# "006", "007", "00A", "013", "016", "01B", "02A", "02A", "02F", "02F",
# "02F", "030", "031", "033", "034", "038", "03A", "05A", "0AA", "0AA",
# "0B9", "0BA", "0D5", "0D5", "0D5", "0D5", "0D5", "0D6", "0D7", "0D8",
# "0D8", "0D8", "0DC", "0E7", "0F2", "0F2", "10C", "112", "117", "117",
# "141", "160", "16D"
return 25
elsif (move.id == PBMoves::BATONPASS || move.id == PBMoves::BULKUP ||
move.id == PBMoves::CALMMIND || move.id == PBMoves::COIL ||
move.id == PBMoves::CURSE || move.id == PBMoves::ELECTRICTERRAIN ||
move.id == PBMoves::ENCORE || move.id == PBMoves::GLARE ||
move.id == PBMoves::GRASSYTERRAIN || move.id == PBMoves::MISTYTERRAIN ||
move.id == PBMoves::NATUREPOWER || move.id == PBMoves::PSYCHICTERRAIN ||
move.id == PBMoves::PURIFY || move.id == PBMoves::SLEEPTALK ||
move.id == PBMoves::SPIKES || move.id == PBMoves::STEALTHROCK ||
move.id == PBMoves::SPIKYSHIELD || move.id == PBMoves::THUNDERWAVE ||
move.id == PBMoves::TOXICSPIKES || move.id == PBMoves::TRICKROOM)
# "007", "007", "024", "025", "02C", "0B3", "0B4", "0BC", "0ED", "103",
# "104", "105", "10D", "11F", "14C", "154", "155", "156", "15B", "173"
return 30
elsif (move.id == PBMoves::AROMATHERAPY || move.id == PBMoves::BANEFULBUNKER ||
move.id == PBMoves::HEALBELL || move.id == PBMoves::KINGSSHIELD ||
move.id == PBMoves::LIGHTSCREEN || move.id == PBMoves::MATBLOCK ||
move.id == PBMoves::NASTYPLOT || move.id == PBMoves::REFLECT ||
move.id == PBMoves::SWORDSDANCE || move.id == PBMoves::TAILGLOW ||
move.id == PBMoves::TAILWIND)
# "019", "019", "02E", "032", "039", "05B", "0A2", "0A3", "149", "14B", "168"
return 35
elsif (move.id == PBMoves::DRAGONDANCE || move.id == PBMoves::GEOMANCY ||
move.id == PBMoves::QUIVERDANCE || move.id == PBMoves::SHELLSMASH ||
move.id == PBMoves::SHIFTGEAR)
# "026", "02B", "035", "036", "14E"
return 40
elsif (move.id == PBMoves::AURORAVEIL || move.id == PBMoves::STICKYWEB ||
move.id == PBMoves::SPORE)
# "003", "153", "167"
return 60
end
end
=end
def pbAegislashStats(aegi)
if aegi.form==1
return aegi
else
bladecheck = aegi.clone
bladecheck.form = 1
return bladecheck
end
end
def pbMegaStats(mon)
if mon.isMega?
return mon
else
megacheck = mon.clone
megacheck.stages = mon.stages.clone
megacheck.form = mon.getMegaForm
return megacheck
end
end
def pbChangeMove(move,attacker)
move = PokeBattle_Move.pbFromPBMove(self,PBMove.new(move.id),attacker)
case move.id
when PBMoves::WEATHERBALL
weather=pbWeather
move.type=(PBTypes::NORMAL)
move.type=PBTypes::FIRE if (weather==PBWeather::SUNNYDAY && !attacker.hasWorkingItem(:UTILITYUMBRELLA))
move.type=PBTypes::WATER if (weather==PBWeather::RAINDANCE && !attacker.hasWorkingItem(:UTILITYUMBRELLA))
move.type=PBTypes::ROCK if weather==PBWeather::SANDSTORM
move.type=PBTypes::ICE if weather==PBWeather::HAIL
if pbWeather !=0
move.basedamage*=2 if move.basedamage == 50
end
when PBMoves::HIDDENPOWER
if attacker
move.type = move.pbType(type,attacker,nil)
end
when PBMoves::NATUREPOWER
move=0
if $fefieldeffect > 0
naturemoves = FieldEffects::NATUREMOVES
move= naturemoves[$fefieldeffect] # Combination of environment and Terrain
else
move=PBMoves::TRIATTACK
end
move = PokeBattle_Move.pbFromPBMove(self,PBMove.new(move),attacker)
end
return move
end
def getAbilityDisruptScore(move,attacker,opponent,skill)
abilityscore=100.0
return abilityscore if !opponent.abilitynulled == false #if the ability doesn't work, then nothing here matters
if opponent.ability == PBAbilities::SPEEDBOOST
PBDebug.log(sprintf("Speedboost Disrupt")) if $INTERNAL
abilityscore*=1.1
if opponent.stages[PBStats::SPEED]<2
abilityscore*=1.3
end
elsif opponent.ability == PBAbilities::SANDVEIL
PBDebug.log(sprintf("Sand veil Disrupt")) if $INTERNAL
if @weather==PBWeather::SANDSTORM
abilityscore*=1.3
end
elsif opponent.ability == PBAbilities::VOLTABSORB ||
opponent.ability == PBAbilities::LIGHTNINGROD ||
opponent.ability == PBAbilities::MOTORDRIVE
PBDebug.log(sprintf("Volt Absorb Disrupt")) if $INTERNAL
elecvar = false
totalelec=true
elecmove=nil
for i in attacker.moves
if !(i.type == PBTypes::ELECTRIC)
totalelec=false
end
if (i.type == PBTypes::ELECTRIC)
elecvar=true
elecmove=i
end
end
if elecvar
if totalelec
abilityscore*=3
end
if pbTypeModNoMessages(elecmove.type,attacker,opponent,elecmove,skill)>4
abilityscore*=2
end
end
elsif opponent.ability == PBAbilities::WATERABSORB ||
opponent.ability == PBAbilities::STORMDRAIN ||
opponent.ability == PBAbilities::DRYSKIN
PBDebug.log(sprintf("Water Absorb Disrupt")) if $INTERNAL
watervar = false
totalwater=true
watermove=nil
firevar=false
for i in attacker.moves
if !(i.type == PBTypes::WATER)
totalwater=false
end
if (i.type == PBTypes::WATER)
watervar=true
watermove=i
end
if (i.type == PBTypes::FIRE)
firevar=true
end
end
if watervar
if totalwater
abilityscore*=3
end
if pbTypeModNoMessages(watermove.type,attacker,opponent,watermove,skill)>4
abilityscore*=2
end
end
if opponent.ability == PBAbilities::DRYSKIN
if firevar
abilityscore*=0.5
end
end
elsif opponent.ability == PBAbilities::FLASHFIRE
PBDebug.log(sprintf("Flash Fire Disrupt")) if $INTERNAL
firevar = false
totalfire=true
firemove=nil
for i in attacker.moves
if !(i.type == PBTypes::FIRE)
totalfire=false
end
if (i.type == PBTypes::FIRE)
firevar=true
firemove=i
end
end
if firevar
if totalfire
abilityscore*=3
end
if pbTypeModNoMessages(firemove.type,attacker,opponent,firemove,skill)>4
abilityscore*=2
end
end
elsif opponent.ability == PBAbilities::LEVITATE
PBDebug.log(sprintf("Levitate Disrupt")) if $INTERNAL
groundvar = false
totalground=true
groundmove=nil
for i in attacker.moves
if !(i.type == PBTypes::GROUND)
totalground=false
end
if (i.type == PBTypes::GROUND)
groundvar=true
groundmove=i
end
end
if groundvar
if totalground
abilityscore*=3
end
if pbTypeModNoMessages(groundmove.type,attacker,opponent,groundmove,skill)>4
abilityscore*=2
end
end
elsif opponent.ability == PBAbilities::SHADOWTAG
PBDebug.log(sprintf("Shadow Tag Disrupt")) if $INTERNAL
if !attacker.hasType?(PBTypes::GHOST)
abilityscore*=1.5
end
elsif opponent.ability == PBAbilities::ARENATRAP
PBDebug.log(sprintf("Arena Trap Disrupt")) if $INTERNAL
if attacker.isAirborne?
abilityscore*=1.5
end
elsif opponent.ability == PBAbilities::WONDERGUARD
PBDebug.log(sprintf("Wonder Guard Disrupt")) if $INTERNAL
wondervar=false
for i in attacker.moves
if pbTypeModNoMessages(i.type,attacker,opponent,i,skill)>4
wondervar=true
end
end
if !wondervar
abilityscore*=5
end
elsif opponent.ability == PBAbilities::SERENEGRACE
PBDebug.log(sprintf("Serene Grace Disrupt")) if $INTERNAL
abilityscore*=1.3
elsif opponent.ability == PBAbilities::PUREPOWER || opponent.ability == PBAbilities::HUGEPOWER
PBDebug.log(sprintf("Pure Power Disrupt")) if $INTERNAL
abilityscore*=2
elsif opponent.ability == PBAbilities::SOUNDPROOF
PBDebug.log(sprintf("Soundproof Disrupt")) if $INTERNAL
soundvar=false
for i in attacker.moves
if i.isSoundBased?
soundvar=true
end
end
if !soundvar
abilityscore*=3
end
elsif opponent.ability == PBAbilities::THICKFAT
PBDebug.log(sprintf("Thick Fat Disrupt")) if $INTERNAL
totalguard=true
for i in attacker.moves
if !(i.type == PBTypes::FIRE) && !(i.type == PBTypes::ICE)
totalguard=false
end
end
if totalguard
abilityscore*=1.5
end
elsif opponent.ability == PBAbilities::TRUANT
PBDebug.log(sprintf("Truant Disrupt")) if $INTERNAL
abilityscore*=0.1
elsif opponent.ability == PBAbilities::GUTS ||
opponent.ability == PBAbilities::QUICKFEET ||
opponent.ability == PBAbilities::MARVELSCALE
PBDebug.log(sprintf("Guts Disrupt")) if $INTERNAL
if opponent.status!=0
abilityscore*=1.5
end
elsif opponent.ability == PBAbilities::LIQUIDOOZE
PBDebug.log(sprintf("Liquid Ooze Disrupt")) if $INTERNAL
if opponent.effects[PBEffects::LeechSeed]>=0 || attacker.pbHasMove?((PBMoves::LEECHSEED))
abilityscore*=2
end
elsif opponent.ability == PBAbilities::AIRLOCK || opponent.ability == PBAbilities::CLOUDNINE
PBDebug.log(sprintf("Airlock Disrupt")) if $INTERNAL
abilityscore*=1.1
elsif opponent.ability == PBAbilities::HYDRATION
PBDebug.log(sprintf("Hydration Disrupt")) if $INTERNAL
if @weather==PBWeather::RAINDANCE
abilityscore*=1.3
end
elsif opponent.ability == PBAbilities::ADAPTABILITY
PBDebug.log(sprintf("Adaptability Disrupt")) if $INTERNAL
abilityscore*=1.3
elsif opponent.ability == PBAbilities::SKILLLINK
PBDebug.log(sprintf("Skill Link Disrupt")) if $INTERNAL
abilityscore*=1.5
elsif opponent.ability == PBAbilities::POISONHEAL
PBDebug.log(sprintf("Poison Heal Disrupt")) if $INTERNAL
if opponent.status==PBStatuses::POISON
abilityscore*=2
end
elsif opponent.ability == PBAbilities::NORMALIZE
PBDebug.log(sprintf("Normalize Disrupt")) if $INTERNAL
abilityscore*=0.6
elsif opponent.ability == PBAbilities::MAGICGUARD
PBDebug.log(sprintf("Magic Guard Disrupt")) if $INTERNAL
abilityscore*=1.4
elsif opponent.ability == PBAbilities::STALL
PBDebug.log(sprintf("Stall Disrupt")) if $INTERNAL
abilityscore*=0.5
elsif opponent.ability == PBAbilities::TECHNICIAN
PBDebug.log(sprintf("Technician Disrupt")) if $INTERNAL
abilityscore*=1.3
elsif opponent.ability == PBAbilities::MOLDBREAKER
PBDebug.log(sprintf("Mold Breaker Disrupt")) if $INTERNAL
abilityscore*=1.1
elsif opponent.ability == PBAbilities::UNAWARE
PBDebug.log(sprintf("Unaware Disrupt")) if $INTERNAL
abilityscore*=1.7
elsif opponent.ability == PBAbilities::SLOWSTART
PBDebug.log(sprintf("Slow Start Disrupt")) if $INTERNAL
abilityscore*=0.3
elsif opponent.ability == PBAbilities::MULTITYPE || opponent.ability == PBAbilities::STANCECHANGE ||
opponent.ability == PBAbilities::SCHOOLING || opponent.ability == PBAbilities::SHIELDSDOWN ||
opponent.ability == PBAbilities::DISGUISE || opponent.ability == PBAbilities::RKSSYSTEM ||
opponent.ability == PBAbilities::POWERCONSTRUCT
PBDebug.log(sprintf("Multitype Disrupt")) if $INTERNAL
abilityscore*=0
elsif opponent.ability == PBAbilities::SHEERFORCE
PBDebug.log(sprintf("Sheer Force Disrupt")) if $INTERNAL
abilityscore*=1.2
elsif opponent.ability == PBAbilities::CONTRARY
PBDebug.log(sprintf("Contrary Disrupt")) if $INTERNAL
abilityscore*=1.4
if opponent.stages[PBStats::ATTACK]>0 || opponent.stages[PBStats::SPATK]>0 ||
opponent.stages[PBStats::DEFENSE]>0 || opponent.stages[PBStats::SPDEF]>0 ||
opponent.stages[PBStats::SPEED]>0
abilityscore*=2
end
elsif opponent.ability == PBAbilities::DEFEATIST
PBDebug.log(sprintf("Defeatist Disrupt")) if $INTERNAL
abilityscore*=0.5
elsif opponent.ability == PBAbilities::MULTISCALE
PBDebug.log(sprintf("Multiscale Disrupt")) if $INTERNAL
if opponent.hp==opponent.totalhp
abilityscore*=1.5
end
elsif opponent.ability == PBAbilities::HARVEST
PBDebug.log(sprintf("Harvest Disrupt")) if $INTERNAL
abilityscore*=1.2
elsif opponent.ability == PBAbilities::MOODY
PBDebug.log(sprintf("Moody Disrupt")) if $INTERNAL
abilityscore*=1.8
elsif opponent.ability == PBAbilities::SAPSIPPER
PBDebug.log(sprintf("Sap Sipper Disrupt")) if $INTERNAL
grassvar = false
totalgrass=true
grassmove=nil
for i in attacker.moves
if !(i.type == PBTypes::GRASS)
totalgrass=false
end
if (i.type == PBTypes::GRASS)
grassvar=true
grassmove=i
end
end
if grassvar
if totalgrass
abilityscore*=3
end
if pbTypeModNoMessages(grassmove.type,attacker,opponent,grassmove,skill)>4
abilityscore*=2
end
end
elsif opponent.ability == PBAbilities::PRANKSTER
PBDebug.log(sprintf("Prankster Disrupt")) if $INTERNAL
if attacker.speed>opponent.speed
abilityscore*=1.5
end
elsif opponent.ability == PBAbilities::SNOWCLOAK
PBDebug.log(sprintf("Snow Cloak Disrupt")) if $INTERNAL
if @weather==PBWeather::HAIL
abilityscore*=1.1
end
elsif opponent.ability == PBAbilities::FURCOAT
PBDebug.log(sprintf("Fur Coat Disrupt")) if $INTERNAL
if attacker.attack>attacker.spatk
abilityscore*=1.5
end
elsif opponent.ability == PBAbilities::PARENTALBOND
PBDebug.log(sprintf("Parental Bond Disrupt")) if $INTERNAL
abilityscore*=3
elsif opponent.ability == PBAbilities::PROTEAN
PBDebug.log(sprintf("Protean Disrupt")) if $INTERNAL
abilityscore*=3
elsif opponent.ability == PBAbilities::TOUGHCLAWS
PBDebug.log(sprintf("Tough Claws Disrupt")) if $INTERNAL
abilityscore*=1.2
elsif opponent.ability == PBAbilities::BEASTBOOST
PBDebug.log(sprintf("Beast Boost Disrupt")) if $INTERNAL
abilityscore*=1.1
elsif opponent.ability == PBAbilities::COMATOSE
PBDebug.log(sprintf("Comatose Disrupt")) if $INTERNAL
abilityscore*=1.3
elsif opponent.ability == PBAbilities::FLUFFY
PBDebug.log(sprintf("Fluffy Disrupt")) if $INTERNAL
abilityscore*=1.5
firevar = false
for i in attacker.moves
if (i.type == PBTypes::FIRE)
firevar=true
end
end
if firevar
abilityscore*=0.5
end
elsif opponent.ability == PBAbilities::MERCILESS
PBDebug.log(sprintf("Merciless Disrupt")) if $INTERNAL
abilityscore*=1.3
elsif opponent.ability == PBAbilities::WATERBUBBLE
PBDebug.log(sprintf("Water Bubble Disrupt")) if $INTERNAL
abilityscore*=1.5
firevar = false
for i in attacker.moves
if (i.type == PBTypes::FIRE)
firevar=true
end
end
if firevar
abilityscore*=1.3
end
elsif attacker.pbPartner==opponent
if abilityscore!=0
if abilityscore>200
abilityscore=200
end
tempscore = abilityscore
abilityscore = 200 - tempscore
end
end
abilityscore*=0.01
return abilityscore
end
def getFieldDisruptScore(attacker,opponent,skill)
fieldscore=100.0
aroles = pbGetMonRole(attacker,opponent,skill)
oroles = pbGetMonRole(opponent,attacker,skill)
aimem = getAIMemory(skill,opponent.pokemonIndex)
if $fefieldeffect==1 # Electric Terrain
PBDebug.log(sprintf("Electric Terrain Disrupt")) if $INTERNAL
if opponent.pbHasType?(:ELECTRIC) || opponent.pbPartner.pbHasType?(:ELECTRIC)
fieldscore*=1.5
end
if attacker.pbHasType?(:ELECTRIC)
fieldscore*=0.5
end
partyelec=false
for k in pbParty(attacker.index)
next if k.nil?
if k.hasType?(:ELECTRIC)
partyelec=true
end
end
if partyelec
fieldscore*=0.5
end
if (!opponent.abilitynulled && opponent.ability == PBAbilities::SURGESURFER)
fieldscore*=1.3
end
if (!attacker.abilitynulled && attacker.ability == PBAbilities::SURGESURFER)
fieldscore*=0.7
end
end
if $fefieldeffect==2 # Grassy Terrain
PBDebug.log(sprintf("Grassy Terrain Disrupt")) if $INTERNAL
if opponent.pbHasType?(:GRASS) || opponent.pbPartner.pbHasType?(:GRASS)
fieldscore*=1.5
end
if attacker.pbHasType?(:GRASS)
fieldscore*=0.5
end
if opponent.pbHasType?(:FIRE) || opponent.pbPartner.pbHasType?(:FIRE)
fieldscore*=1.8
end
if attacker.pbHasType?(:FIRE)
fieldscore*=0.2
end
partygrass=false
for k in pbParty(attacker.index)
next if k.nil?
if k.hasType?(:GRASS)
partygrass=true
end
end
if partygrass
fieldscore*=0.5
end
partyfire=false
for k in pbParty(attacker.index)
next if k.nil?
if k.hasType?(:FIRE)
partyfire=true
end
end
if partyfire
fieldscore*=0.2
end
if aroles.include?(PBMonRoles::SPECIALWALL) || aroles.include?(PBMonRoles::PHYSICALWALL)
fieldscore*=0.8
end
if oroles.include?(PBMonRoles::SPECIALWALL) || oroles.include?(PBMonRoles::PHYSICALWALL)
fieldscore*=1.2
end
end
if $fefieldeffect==3 # Misty Terrain
PBDebug.log(sprintf("Misty Terrain Disrupt")) if $INTERNAL
if attacker.spatk>attacker.attack
if opponent.pbHasType?(:FAIRY) || opponent.pbPartner.pbHasType?(:FAIRY)
fieldscore*=1.3
end
end
if opponent.spatk>opponent.attack
if attacker.pbHasType?(:FAIRY)
fieldscore*=0.7
end
end
if opponent.pbHasType?(:DRAGON) || opponent.pbPartner.pbHasType?(:DRAGON)
fieldscore*=0.5
end
if attacker.pbHasType?(:DRAGON)
fieldscore*=1.5
end
partyfairy=false
for k in pbParty(attacker.index)
next if k.nil?
if k.hasType?(:FAIRY)
partyfairy=true
end
end
if partyfairy
fieldscore*=0.7
end
partydragon=false
for k in pbParty(attacker.index)
next if k.nil?
if k.hasType?(:DRAGON)
partydragon=true
end
end
if partydragon
fieldscore*=1.5
end
if !(attacker.pbHasType?(:POISON) || attacker.pbHasType?(:STEEL))
if $fecounter==1
fieldscore*=1.8
end
end
end
if $fefieldeffect==37 # Psychic Terrain
PBDebug.log(sprintf("Psychic Terrain Disrupt")) if $INTERNAL
if opponent.pbHasType?(:PSYCHIC) || opponent.pbPartner.pbHasType?(:PSYCHIC)
fieldscore*=1.7
end
if attacker.pbHasType?(:PSYCHIC)
fieldscore*=0.3
end
partypsy=false
for k in pbParty(attacker.index)
next if k.nil?
if k.hasType?(:PSYCHIC)
partypsy=true
end
end
if partypsy
fieldscore*=0.3
end
if (!opponent.abilitynulled && opponent.ability == PBAbilities::TELEPATHY)
fieldscore*=1.3
end
if (!attacker.abilitynulled && attacker.ability == PBAbilities::TELEPATHY)
fieldscore*=0.7
end
end
fieldscore*=0.01
return fieldscore
end
# Used by all moves that raise the user's stat(s)
def setupminiscore(attacker,opponent,skill,move,sweep,code,double,initialscores,scoreindex)
aimem = getAIMemory(skill,opponent.pokemonIndex)
miniscore=100
#hi we are going in the comments for this one because it is in dire need of explanation
#up until this point, most of the key differences between different set up moves has
#been whether they are good for setting up to sweep or not.
#this is not the case past here.
#there are some really obnoxious differences between moves, and the way i'm dealing
#with it is through binary strings.
#this string is passed as a single number and is then processed by the function as such:
# 00001 = attack 00010 = defense 00100 = sp.attack 01000 = sp.defense 10000 = speed
#cosmic power would be 01010 in binary or 10 in normal, bulk up would be 00011 or 3, etc
#evasion has a code of 0
#this way new moves can be added and still use this function without any loss in
#the overall scoring precision of the AI
if initialscores.length>0
miniscore*=1.3 if hasbadmoves(initialscores,scoreindex,20)
end
if skill>=PBTrainerAI.mediumSkill
if aimem.length > 0
maxdam = checkAIdamage(aimem,attacker,opponent,skill)
if maxdam<(attacker.hp/4.0) && sweep
miniscore*=1.2
elsif maxdam<(attacker.hp/3.0) && !sweep
miniscore*=1.1
elsif maxdam<(attacker.hp/4.0) && code == 10
miniscore*=1.5
else
if move.basedamage==0
miniscore*=0.8
if maxdam>attacker.hp
miniscore*=0.1
end
end
end
else
if move.basedamage==0
effcheck = PBTypes.getCombinedEffectiveness(opponent.type1,attacker.type1,attacker.type2)
if effcheck > 4
miniscore*=0.5
end
effcheck2 = PBTypes.getCombinedEffectiveness(opponent.type2,attacker.type1,attacker.type2)
if effcheck2 > 4
miniscore*=0.5
end
end
end
end
if attacker.effects[PBEffects::Confusion]>0
if code & 0b1 == 0b1 #if move boosts attack
miniscore*=0.2
miniscore*=0.5 if double #using swords dance or shell smash while confused is Extra Bad
miniscore*=1.5 if code & 0b11 == 0b11 #adds a correction for moves that boost attack and defense
else
miniscore*=0.5
end
end
if !sweep
miniscore*=1.1 if opponent.status==PBStatuses::BURN && code & 0b1000 == 0b1000 #sp.def boosting
end
if checkAImoves(PBStuff::SWITCHOUTMOVE,aimem)
miniscore*=0.5 if sweep
miniscore*=0.2 if !sweep
miniscore*=1.5 if code == 0 #correction for evasion moves
end
=begin
if attacker.effects[PBEffects::Substitute]>0 || attacker.effects[PBEffects::Disguise]
miniscore*=1.3
end
if (attacker.hp.to_f)/attacker.totalhp<0.33
miniscore*=0.3
end
if (attacker.hp.to_f)/attacker.totalhp<0.75 &&
(!attacker.abilitynulled && (attacker.ability == PBAbilities::EMERGENCYEXIT || attacker.ability == PBAbilities::WIMPOUT) ||
(attacker.itemWorks? && attacker.item == PBItems::EJECTBUTTON))
miniscore*=0.3
end
if attacker.pbOpposingSide.effects[PBEffects::Retaliate]
miniscore*=0.3
end
if opponent.status==PBStatuses::SLEEP || opponent.status==PBStatuses::FROZEN
miniscore*=1.3
end
if (!attacker.abilitynulled && attacker.ability == PBAbilities::SIMPLE)
miniscore*=2
end
if (attacker.hp.to_f)/attacker.totalhp>0.75
miniscore*=1.2 if sweep
miniscore*=1.1 if !sweep
end
if opponent.effects[PBEffects::HyperBeam]>0
miniscore*=1.3 if sweep
miniscore*=1.2 if !sweep
end
if opponent.effects[PBEffects::Yawn]>0
miniscore*=1.7 if sweep
miniscore*=1.3 if !sweep
end
if attacker.turncount<2
miniscore*=1.2 if sweep
miniscore*=1.1 if !sweep
end
if opponent.status!=0
miniscore*=1.2 if sweep
miniscore*=1.1 if !sweep
end
if opponent.effects[PBEffects::Encore]>0
if opponent.moves[(opponent.effects[PBEffects::EncoreIndex])].basedamage==0
if sweep || code == 10 #cosmic power
miniscore*=1.5
else
miniscore*=1.3
end
end
end
sweep = false if code == 3 #from here on out, bulk up is not a sweep move
if attacker.effects[PBEffects::LeechSeed]>=0 || attacker.effects[PBEffects::Attract]>=0
miniscore*=0.6 if sweep
miniscore*=0.3 if !sweep
end
miniscore*=0.2 if attacker.effects[PBEffects::Toxic]>0 && !sweep
if @doublebattle
miniscore*=0.5
miniscore*=0.5 if !sweep #drop is doubled
end
=end
return miniscore
end
# General processing for stat-dropping moves
def unsetupminiscore(attacker,opponent,skill,move,roles,type,physical,greatmoves=false)
#attack stat = type 1 defense stat = type 2 speed = 3 evasion = no
miniscore = 100
aimem = getAIMemory(skill,opponent.pokemonIndex)
if type == 3 #speed stuff
if (pbRoughStat(opponent,PBStats::SPEED,skill)*0.66)<attacker.pbSpeed
if greatmoves
miniscore*=1.5 if greatmoves
else
miniscore*=1.1
end
end
else #non-speed stuff
count=-1
party=pbParty(attacker.index)
sweepvar=false
for i in 0...party.length
count+=1
next if (count==attacker.pokemonIndex || party[i].nil?)
temproles = pbGetMonRole(party[i],opponent,skill,count,party)
if temproles.include?(PBMonRoles::SWEEPER)
sweepvar=true
end
end
if sweepvar
miniscore*=1.1
end
end
if type == 2 #defense stuff
miniscore*=1.5 if checkAIhealing(aimem)
miniscore*=1.5 if move.function == 0x4C
else
if roles.include?(PBMonRoles::PHYSICALWALL) || roles.include?(PBMonRoles::SPECIALWALL)
miniscore*=1.3 if type == 1
miniscore*=1.1 if type == 3
end
end
livecount1=0
for i in pbParty(attacker.index)
next if i.nil?
livecount1+=1 if i.hp!=0
end
livecount2=0
for i in pbParty(opponent.index)
next if i.nil?
livecount2+=1 if i.hp!=0
end
if livecount2==1 || (!attacker.abilitynulled && attacker.ability == PBAbilities::SHADOWTAG) || opponent.effects[PBEffects::MeanLook]>0
miniscore*=1.4
end
#status section
if type == 2 || !physical
miniscore*=1.2 if opponent.status==PBStatuses::POISON || opponent.status==PBStatuses::BURN
elsif type == 1
miniscore*=1.2 if opponent.status==PBStatuses::POISON
miniscore*=0.5 if opponent.status==PBStatuses::BURN
end
#move checks
if type == 1 && physical
miniscore*=0.5 if attacker.pbHasMove?(PBMoves::FOULPLAY)
elsif type == 3
miniscore*=0.5 if attacker.pbHasMove?(PBMoves::GYROBALL)
miniscore*=1.5 if attacker.pbHasMove?(PBMoves::ELECTROBALL)
miniscore*=1.3 if checkAImoves([PBMoves::ELECTROBALL],aimem)
miniscore*=0.5 if checkAImoves([PBMoves::GYROBALL],aimem)
miniscore*=0.1 if @trickroom!=0 || checkAImoves([PBMoves::TRICKROOM],aimem)
end
#final things
if type == 3
miniscore*=0.1 if opponent.itemWorks? && (opponent.item == PBItems::LAGGINGTAIL || opponent.item == PBItems::IRONBALL)
miniscore*=0.2 if !opponent.abilitynulled && [PBAbilities::COMPETITIVE, PBAbilities::DEFIANT, PBAbilities::CONTRARY].include?(opponent.ability)
else
miniscore*=0.1 if !opponent.abilitynulled && [PBAbilities::UNAWARE, PBAbilities::COMPETITIVE, PBAbilities::DEFIANT, PBAbilities::CONTRARY].include?(opponent.ability)
end
if move.basedamage>0
miniscore-=100
if move.addlEffect.to_f != 100
miniscore*=(move.addlEffect.to_f/100)
if !attacker.abilitynulled && attacker.ability == PBAbilities::SERENEGRACE
miniscore*=2
end
end
miniscore+=100
else
if livecount1==1
miniscore*=0.5
end
if attacker.status!=0
miniscore*=0.7
end
end
miniscore /= 100
return miniscore
end
def hasgreatmoves(initialscores,scoreindex,skill)
#slight variance in precision based on trainer skill
threshold = 100
threshold = 105 if skill>=PBTrainerAI.highSkill
threshold = 110 if skill>=PBTrainerAI.bestSkill
for i in 0...initialscores.length
next if i==scoreindex
if initialscores[i]>=threshold
return true
end
end
return false
end
def hasbadmoves(initialscores,scoreindex,threshold)
for i in 0...initialscores.length
next if i==scoreindex
if initialscores[i]>threshold
return false
end
end
return false
end
def statchangecounter(mon,initial,final,limiter=0)
count = 0
case limiter
when 0 #all stats
for i in initial..final
count += mon.stages[i]
end
when 1 #increases only
for i in initial..final
count += mon.stages[i] if mon.stages[i]>0
end
when -1 #decreases only
for i in initial..final
count += mon.stages[i] if mon.stages[i]<0
end
end
return count
end
end

View File

@@ -0,0 +1,327 @@
=begin
class PokeBattle_Battle
def pbGetMonRole(mon,opponent,skill,position=0,party=nil)
#PBDebug.log(sprintf("Beginning role assignment for %s",PBSpecies.getName(mon.species))) if $INTERNAL
monRoles=[]
monability = mon.ability.to_i
curemove=false
healingmove=false
wishmove=false
phasemove=false
priorityko=false
pivotmove=false
spinmove=false
batonmove=false
tauntmove=false
restmove=false
weathermove=false
fieldmove=false
if mon.class == PokeBattle_Battler
if mon.ev[3]>251 && (mon.nature==PBNatures::MODEST ||
mon.nature==PBNatures::JOLLY || mon.nature==PBNatures::TIMID ||
mon.nature==PBNatures::ADAMANT) || (mon.item==(PBItems::CHOICEBAND) ||
mon.item==(PBItems::CHOICESPECS) || mon.item==(PBItems::CHOICESCARF))
monRoles.push(PBMonRoles::SWEEPER)
end
for i in mon.moves
next if i.nil?
next if i.id == 0
# Unused as only counts for REVENGEKILLER, which is only used if mon is
# a Pokémon and not a battler (used in switching calculation)
if i.priority>0
dam=pbRoughDamage(i,mon,opponent,skill,i.basedamage)
if opponent.hp>0
percentage=(dam*100.0)/opponent.hp
priorityko=true if percentage>100
end
end
if i.isHealingMove?
healingmove=true
elsif (i.id == (PBMoves::HEALBELL) || i.id == (PBMoves::AROMATHERAPY))
curemove=true
elsif (i.id == (PBMoves::WISH))
wishmove=true
elsif (i.id == (PBMoves::YAWN) || i.id == (PBMoves::PERISHSONG) ||
i.id == (PBMoves::DRAGONTAIL) || i.id == (PBMoves::CIRCLETHROW) ||
i.id == (PBMoves::WHIRLWIND) || i.id == (PBMoves::ROAR))
phasemove=true
elsif (i.id == (PBMoves::UTURN) || i.id == (PBMoves::VOLTSWITCH))
pivotmove=true
elsif (i.id == (PBMoves::RAPIDSPIN))
spinmove=true
elsif (i.id == (PBMoves::BATONPASS))
batonmove=true
elsif (i.id == (PBMoves::TAUNT))
tauntmove=true
elsif (i.id == (PBMoves::REST))
restmove=true
elsif (i.id == (PBMoves::SUNNYDAY) || i.id == (PBMoves::RAINDANCE) ||
i.id == (PBMoves::HAIL) || i.id == (PBMoves::SANDSTORM))
weathermove=true
elsif (i.id == (PBMoves::GRASSYTERRAIN) || i.id == (PBMoves::ELECTRICTERRAIN) ||
i.id == (PBMoves::MISTYTERRAIN) || i.id == (PBMoves::PSYCHICTERRAIN) ||
i.id == (PBMoves::MIST) || i.id == (PBMoves::IONDELUGE) ||
i.id == (PBMoves::TOPSYTURVY))
fieldmove=true
end
end
if healingmove && (mon.ev[2]>251 && (mon.nature==PBNatures::BOLD ||
mon.nature==PBNatures::RELAXED || mon.nature==PBNatures::IMPISH ||
mon.nature==PBNatures::LAX))
monRoles.push(PBMonRoles::PHYSICALWALL)
end
if healingmove && (mon.ev[5]>251 && (mon.nature==PBNatures::CALM ||
mon.nature==PBNatures::GENTLE || mon.nature==PBNatures::SASSY ||
mon.nature==PBNatures::CAREFUL))
monRoles.push(PBMonRoles::SPECIALWALL)
end
if mon.pokemonIndex==0
monRoles.push(PBMonRoles::LEAD)
end
if curemove || (wishmove && mon.ev[0]>251)
monRoles.push(PBMonRoles::CLERIC)
end
if phasemove == true
monRoles.push(PBMonRoles::PHAZER)
end
if mon.item==(PBItems::LIGHTCLAY)
monRoles.push(PBMonRoles::SCREENER)
end
# Unused
if priorityko || (mon.speed>opponent.speed)
monRoles.push(PBMonRoles::REVENGEKILLER)
end
if (pivotmove && healingmove) || (monability == PBAbilities::REGENERATOR)
monRoles.push(PBMonRoles::PIVOT)
end
if spinmove
monRoles.push(PBMonRoles::SPINNER)
end
if (mon.ev[0]>251 && !healingmove) || mon.item==(PBItems::ASSAULTVEST)
monRoles.push(PBMonRoles::TANK)
end
if batonmove
monRoles.push(PBMonRoles::BATONPASSER)
end
if tauntmove || mon.item==(PBItems::CHOICEBAND) ||
mon.item==(PBItems::CHOICESPECS)
monRoles.push(PBMonRoles::STALLBREAKER)
end
if restmove || (monability == PBAbilities::COMATOSE) ||
mon.item==(PBItems::TOXICORB) || mon.item==(PBItems::FLAMEORB) ||
(monability == PBAbilities::GUTS) ||
(monability == PBAbilities::QUICKFEET)||
(monability == PBAbilities::FLAREBOOST) ||
(monability == PBAbilities::TOXICBOOST) ||
(monability == PBAbilities::NATURALCURE) ||
(monability == PBAbilities::MAGICGUARD) ||
(monability == PBAbilities::MAGICBOUNCE) ||
((monability == PBAbilities::HYDRATION) && pbWeather==PBWeather::RAINDANCE)
monRoles.push(PBMonRoles::STATUSABSORBER)
end
if (monability == PBAbilities::SHADOWTAG) ||
(monability == PBAbilities::ARENATRAP) ||
(monability == PBAbilities::MAGNETPULL)
monRoles.push(PBMonRoles::TRAPPER)
end
if weathermove || (monability == PBAbilities::DROUGHT) ||
(monability == PBAbilities::SANDSTREAM) ||
(monability == PBAbilities::DRIZZLE) ||
(monability == PBAbilities::SNOWWARNING) ||
(monability == PBAbilities::PRIMORDIALSEA) ||
(monability == PBAbilities::DESOLATELAND) ||
(monability == PBAbilities::DELTASTREAM)
monRoles.push(PBMonRoles::WEATHERSETTER)
end
if fieldmove || (monability == PBAbilities::GRASSYSURGE) ||
(monability == PBAbilities::ELECTRICSURGE) ||
(monability == PBAbilities::MISTYSURGE) ||
(monability == PBAbilities::PSYCHICSURGE) ||
mon.item==(PBItems::AMPLIFIELDROCK)
monRoles.push(PBMonRoles::FIELDSETTER)
end
#if $game_switches[525] && mon.pokemonIndex==(pbParty(mon.index).length-1)
if mon.pokemonIndex==(pbParty(mon.index).length-1)
monRoles.push(PBMonRoles::ACE)
end
secondhighest=true
if pbParty(mon.index).length>2
for i in 0..(pbParty(mon.index).length-2)
next if pbParty(mon.index)[i].nil?
if mon.level<pbParty(mon.index)[i].level
secondhighest=false
end
end
end
#if $game_switches[525]&& secondhighest
if secondhighest
monRoles.push(PBMonRoles::SECOND)
end
#PBDebug.log(sprintf("Ending role assignment for %s",PBSpecies.getName(mon.species))) if $INTERNAL
#PBDebug.log(sprintf("")) if $INTERNAL
return monRoles
elsif mon.class == PokeBattle_Pokemon
movelist = []
for i in mon.moves
next if i.nil?
next if i.id == 0
movedummy = PokeBattle_Move.pbFromPBMove(self,i,mon)
movelist.push(movedummy)
end
if mon.ev[3]>251 && (mon.nature==PBNatures::MODEST ||
mon.nature==PBNatures::JOLLY || mon.nature==PBNatures::TIMID ||
mon.nature==PBNatures::ADAMANT) || (mon.item==(PBItems::CHOICEBAND) ||
mon.item==(PBItems::CHOICESPECS) || mon.item==(PBItems::CHOICESCARF))
monRoles.push(PBMonRoles::SWEEPER)
end
for i in movelist
next if i.nil?
if i.isHealingMove?
healingmove=true
elsif (i.id == (PBMoves::HEALBELL) || i.id == (PBMoves::AROMATHERAPY))
curemove=true
elsif (i.id == (PBMoves::WISH))
wishmove=true
elsif (i.id == (PBMoves::YAWN) || i.id == (PBMoves::PERISHSONG) ||
i.id == (PBMoves::DRAGONTAIL) || i.id == (PBMoves::CIRCLETHROW) ||
i.id == (PBMoves::WHIRLWIND) || i.id == (PBMoves::ROAR))
phasemove=true
elsif (i.id == (PBMoves::UTURN) || i.id == (PBMoves::VOLTSWITCH))
pivotmove=true
elsif (i.id == (PBMoves::RAPIDSPIN))
spinmove=true
elsif (i.id == (PBMoves::BATONPASS))
batonmove=true
elsif(i.id == (PBMoves::TAUNT))
tauntmove=true
elsif (i.id == (PBMoves::REST))
restmove=true
elsif (i.id == (PBMoves::SUNNYDAY) || i.id == (PBMoves::RAINDANCE) ||
i.id == (PBMoves::HAIL) || i.id == (PBMoves::SANDSTORM))
weathermove=true
elsif (i.id == (PBMoves::GRASSYTERRAIN) || i.id == (PBMoves::ELECTRICTERRAIN) ||
i.id == (PBMoves::MISTYTERRAIN) || i.id == (PBMoves::PSYCHICTERRAIN) ||
i.id == (PBMoves::MIST) || i.id == (PBMoves::IONDELUGE) ||
i.id == (PBMoves::TOPSYTURVY))
fieldmove=true
end
end
if healingmove && (mon.ev[2]>251 && (mon.nature==PBNatures::BOLD ||
mon.nature==PBNatures::RELAXED || mon.nature==PBNatures::IMPISH ||
mon.nature==PBNatures::LAX))
monRoles.push(PBMonRoles::PHYSICALWALL)
end
if healingmove && (mon.ev[5]>251 && (mon.nature==PBNatures::CALM ||
mon.nature==PBNatures::GENTLE || mon.nature==PBNatures::SASSY ||
mon.nature==PBNatures::CAREFUL))
monRoles.push(PBMonRoles::SPECIALWALL)
end
if position==0
monRoles.push(PBMonRoles::LEAD)
end
if curemove || (wishmove && mon.ev[0]>251)
monRoles.push(PBMonRoles::CLERIC)
end
if (phasemove)
monRoles.push(PBMonRoles::PHAZER)
end
if mon.item==(PBItems::LIGHTCLAY)
monRoles.push(PBMonRoles::SCREENER)
end
# pbRoughDamage does not take Pokemon objects, this will cause issues
priorityko=false
for i in movelist
next if i.priority<1
next if i.basedamage<10
priorityko=true
end
if priorityko || (mon.speed>opponent.speed)
monRoles.push(PBMonRoles::REVENGEKILLER)
end
if (pivotmove && healingmove) || (monability == PBAbilities::REGENERATOR)
monRoles.push(PBMonRoles::PIVOT)
end
if spinmove
monRoles.push(PBMonRoles::SPINNER)
end
if (mon.ev[0]>251 && !healingmove) || mon.item==(PBItems::ASSAULTVEST)
monRoles.push(PBMonRoles::TANK)
end
if batonmove
monRoles.push(PBMonRoles::BATONPASSER)
end
if tauntmove || mon.item==(PBItems::CHOICEBAND) ||
mon.item==(PBItems::CHOICESPECS)
monRoles.push(PBMonRoles::STALLBREAKER)
end
if restmove || (monability == PBAbilities::COMATOSE) ||
mon.item==(PBItems::TOXICORB) || mon.item==(PBItems::FLAMEORB) ||
(monability == PBAbilities::GUTS) ||
(monability == PBAbilities::QUICKFEET) ||
(monability == PBAbilities::FLAREBOOST) ||
(monability == PBAbilities::TOXICBOOST) ||
(monability == PBAbilities::NATURALCURE) ||
(monability == PBAbilities::MAGICGUARD) ||
(monability == PBAbilities::MAGICBOUNCE) ||
# TODO: Reference to the weather here.
((monability == PBAbilities::HYDRATION) && pbWeather==PBWeather::RAINDANCE)
monRoles.push(PBMonRoles::STATUSABSORBER)
end
if (monability == PBAbilities::SHADOWTAG) ||
(monability == PBAbilities::ARENATRAP) ||
(monability == PBAbilities::MAGNETPULL)
monRoles.push(PBMonRoles::TRAPPER)
end
if weathermove || (monability == PBAbilities::DROUGHT) ||
(monability == PBAbilities::SANDSTREAM) ||
(monability == PBAbilities::DRIZZLE) ||
(monability == PBAbilities::SNOWWARNING) ||
(monability == PBAbilities::PRIMORDIALSEA) ||
(monability == PBAbilities::DESOLATELAND) ||
(monability == PBAbilities::DELTASTREAM)
monRoles.push(PBMonRoles::WEATHERSETTER)
end
if fieldmove || (monability == PBAbilities::GRASSYSURGE) ||
(monability == PBAbilities::ELECTRICSURGE) ||
(monability == PBAbilities::MISTYSURGE) ||
(monability == PBAbilities::PSYCHICSURGE) ||
mon.item==(PBItems::AMPLIFIELDROCK)
monRoles.push(PBMonRoles::FIELDSETTER)
end
if position==(party.length-1)
#if $game_switches[525] && position==(party.length-1)
monRoles.push(PBMonRoles::ACE)
end
secondhighest=true
if party.length>2
for i in 0...(party.length-1)
next if party[i].nil?
if mon.level<party[i].level
secondhighest=false
end
end
end
#if $game_switches[525]&& secondhighest
if secondhighest
monRoles.push(PBMonRoles::SECOND)
end
#PBDebug.log(sprintf("Ending role assignment for %s",PBSpecies.getName(mon.species))) if $INTERNAL
#PBDebug.log(sprintf("")) if $INTERNAL
return monRoles
end
#PBDebug.log(sprintf("Ending role assignment for %s",PBSpecies.getName(mon.species))) if $INTERNAL
#PBDebug.log(sprintf("")) if $INTERNAL
return monRoles
end
end
=end

View File

@@ -7,7 +7,9 @@ module PBDebug
rescue
PBDebug.log("")
PBDebug.log("**Exception: #{$!.message}")
PBDebug.log($!.backtrace.inspect.to_s)
backtrace = ""
$!.backtrace.each { |line| backtrace += line + "\r\n" }
PBDebug.log(backtrace)
PBDebug.log("")
pbPrintException($!) # if $INTERNAL
PBDebug.flush
@@ -16,14 +18,53 @@ 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.gsub("%", "%%")
@@log.push(msg + "\r\n")
PBDebug.flush # if @@log.length > 1024
end
end
def self.log_header(msg)
if $DEBUG && $INTERNAL
echoln Console.markup_style(msg.gsub("%", "%%"), text: :light_purple)
@@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.gsub("%", "%%"), text: :dark_gray)
@@log.push(msg + "\r\n")
PBDebug.flush # if @@log.length > 1024
end
end
def self.log_ai(msg)
if $DEBUG && $INTERNAL
msg = "[AI] " + msg
echoln msg.gsub("%", "%%")
@@log.push(msg + "\r\n")
PBDebug.flush # if @@log.length > 1024
end
end
def self.log_score_change(amt, msg)
return if amt == 0
if $DEBUG && $INTERNAL
sign = (amt > 0) ? "+" : "-"
amt_text = sprintf("%3d", amt.abs)
msg = " #{sign}#{amt_text}: #{msg}"
echoln msg.gsub("%", "%%")
@@log.push(msg + "\r\n")
PBDebug.flush # if @@log.length > 1024
end
end

View File

@@ -104,4 +104,11 @@ module Settings
CHECK_EVOLUTION_AFTER_ALL_BATTLES = (MECHANICS_GENERATION >= 6)
# Whether fainted Pokémon can try to evolve after a battle.
CHECK_EVOLUTION_FOR_FAINTED_POKEMON = true
#=============================================================================
# Whether wild Pokémon with the "Legendary", "Mythical" or "UltraBeast" flag
# (as defined in pokemon.txt) have a smarter AI. Their skill level is set to
# 32, which is a medium skill level.
SMARTER_WILD_LEGENDARY_POKEMON = true
end

View File

@@ -91,94 +91,61 @@ class NamedEvent
end
#===============================================================================
# Unused.
# A class that stores code that can be triggered. Each piece of code has an
# associated ID, which can be anything that can be used as a key in a hash.
#===============================================================================
class HandlerHash
def initialize(mod)
@mod = mod
@hash = {}
@addIfs = []
@symbolCache = {}
def initialize
@hash = {}
end
def fromSymbol(sym)
return sym unless sym.is_a?(Symbol) || sym.is_a?(String)
mod = Object.const_get(@mod) rescue nil
return nil if !mod
return mod.const_get(sym.to_sym) rescue nil
def [](id)
return @hash[id] if id && @hash[id]
return nil
end
def toSymbol(sym)
return sym.to_sym if sym.is_a?(Symbol) || sym.is_a?(String)
ret = @symbolCache[sym]
return ret if ret
mod = Object.const_get(@mod) rescue nil
return nil if !mod
mod.constants.each do |key|
next if mod.const_get(key) != sym
ret = key.to_sym
@symbolCache[sym] = ret
break
end
return ret
end
def addIf(conditionProc, handler = nil, &handlerBlock)
def add(id, handler = nil, &handlerBlock)
if ![Proc, Hash].include?(handler.class) && !block_given?
raise ArgumentError, "addIf call for #{self.class.name} has no valid handler (#{handler.inspect} was given)"
raise ArgumentError, "#{self.class.name} for #{id.inspect} has no valid handler (#{handler.inspect} was given)"
end
@addIfs.push([conditionProc, handler || handlerBlock])
end
def add(sym, handler = nil, &handlerBlock) # 'sym' can be an ID or symbol
if ![Proc, Hash].include?(handler.class) && !block_given?
raise ArgumentError, "#{self.class.name} for #{sym.inspect} has no valid handler (#{handler.inspect} was given)"
end
id = fromSymbol(sym)
@hash[id] = handler || handlerBlock if id
symbol = toSymbol(sym)
@hash[symbol] = handler || handlerBlock if symbol
@hash[id] = handler || handlerBlock if id && !id.empty?
end
def copy(src, *dests)
handler = self[src]
if handler
dests.each do |dest|
self.add(dest, handler)
end
end
return if !handler
dests.each { |dest| add(dest, handler) }
end
def [](sym) # 'sym' can be an ID or symbol
id = fromSymbol(sym)
ret = nil
ret = @hash[id] if id && @hash[id] # Real ID from the item
symbol = toSymbol(sym)
ret = @hash[symbol] if symbol && @hash[symbol] # Symbol or string
unless ret
@addIfs.each do |addif|
return addif[1] if addif[0].call(id)
end
end
return ret
end
def trigger(sym, *args)
handler = self[sym]
return (handler) ? handler.call(fromSymbol(sym), *args) : nil
def remove(key)
@hash.delete(key)
end
def clear
@hash.clear
end
def each
@hash.each_pair { |key, value| yield key, value }
end
def keys
return @hash.keys.clone
end
# NOTE: The call does not pass id as a parameter to the proc/block.
def trigger(id, *args)
handler = self[id]
return handler&.call(*args)
end
end
#===============================================================================
# A stripped-down version of class HandlerHash which only deals with symbols and
# doesn't care about whether those symbols are defined as constants in a class
# or module.
# A stripped-down version of class HandlerHash which only deals with IDs that
# are symbols. Also contains an add_ifs hash for code that applies to multiple
# IDs (determined by its condition proc).
#===============================================================================
class HandlerHash2
class HandlerHashSymbol
def initialize
@hash = {}
@add_ifs = {}
@@ -219,6 +186,7 @@ class HandlerHash2
def clear
@hash.clear
@add_ifs.clear
end
def trigger(sym, *args)
@@ -229,66 +197,100 @@ class HandlerHash2
end
#===============================================================================
# An even more stripped down version of class HandlerHash which just takes
# hashes with keys, no matter what the keys are.
# A specialised version of class HandlerHash which only deals with IDs that are
# constants in a particular class or module. That class or module must be
# defined when creating an instance of this class.
# Unused.
#===============================================================================
class HandlerHashBasic
def initialize
@hash = {}
class HandlerHashEnum
def initialize(mod)
@mod = mod
@hash = {}
@addIfs = []
@symbolCache = {}
end
def [](entry)
return @hash[entry] if entry && @hash[entry]
return nil
end
def add(entry, handler = nil, &handlerBlock)
if ![Proc, Hash].include?(handler.class) && !block_given?
raise ArgumentError, "#{self.class.name} for #{entry.inspect} has no valid handler (#{handler.inspect} was given)"
def [](sym) # 'sym' can be an ID or symbol
id = fromSymbol(sym)
ret = nil
ret = @hash[id] if id && @hash[id] # Real ID from the item
symbol = toSymbol(sym)
ret = @hash[symbol] if symbol && @hash[symbol] # Symbol or string
unless ret
@addIfs.each do |addif|
return addif[1] if addif[0].call(id)
end
end
return if !entry || entry.empty?
@hash[entry] = handler || handlerBlock
return ret
end
def fromSymbol(sym)
return sym unless sym.is_a?(Symbol) || sym.is_a?(String)
mod = Object.const_get(@mod) rescue nil
return nil if !mod
return mod.const_get(sym.to_sym) rescue nil
end
def toSymbol(sym)
return sym.to_sym if sym.is_a?(Symbol) || sym.is_a?(String)
ret = @symbolCache[sym]
return ret if ret
mod = Object.const_get(@mod) rescue nil
return nil if !mod
mod.constants.each do |key|
next if mod.const_get(key) != sym
ret = key.to_sym
@symbolCache[sym] = ret
break
end
return ret
end
def add(sym, handler = nil, &handlerBlock) # 'sym' can be an ID or symbol
if ![Proc, Hash].include?(handler.class) && !block_given?
raise ArgumentError, "#{self.class.name} for #{sym.inspect} has no valid handler (#{handler.inspect} was given)"
end
id = fromSymbol(sym)
@hash[id] = handler || handlerBlock if id
symbol = toSymbol(sym)
@hash[symbol] = handler || handlerBlock if symbol
end
def addIf(conditionProc, handler = nil, &handlerBlock)
if ![Proc, Hash].include?(handler.class) && !block_given?
raise ArgumentError, "addIf call for #{self.class.name} has no valid handler (#{handler.inspect} was given)"
end
@addIfs.push([conditionProc, handler || handlerBlock])
end
def copy(src, *dests)
handler = self[src]
return if !handler
dests.each { |dest| add(dest, handler) }
end
def remove(key)
@hash.delete(key)
dests.each { |dest| self.add(dest, handler) }
end
def clear
@hash.clear
@addIfs.clear
end
def each
@hash.each_pair { |key, value| yield key, value }
end
def keys
return @hash.keys.clone
end
def trigger(entry, *args)
handler = self[entry]
return handler&.call(*args)
def trigger(sym, *args)
handler = self[sym]
return (handler) ? handler.call(fromSymbol(sym), *args) : nil
end
end
#===============================================================================
#
#===============================================================================
class SpeciesHandlerHash < HandlerHash2
class SpeciesHandlerHash < HandlerHashSymbol
end
class AbilityHandlerHash < HandlerHash2
class AbilityHandlerHash < HandlerHashSymbol
end
class ItemHandlerHash < HandlerHash2
class ItemHandlerHash < HandlerHashSymbol
end
class MoveHandlerHash < HandlerHash2
class MoveHandlerHash < HandlerHashSymbol
end

View File

@@ -81,7 +81,7 @@ module MenuHandlers
@@handlers = {}
def self.add(menu, option, hash)
@@handlers[menu] = HandlerHashBasic.new if !@@handlers.has_key?(menu)
@@handlers[menu] = HandlerHash.new if !@@handlers.has_key?(menu)
@@handlers[menu].add(option, hash)
end

View File

@@ -88,6 +88,14 @@ module GameData
return GameData::Type.get(@type).special?
end
def damaging?
return @category != 2
end
def status?
return @category == 2
end
def hidden_move?
GameData::Item.each do |i|
return true if i.is_HM? && i.move == @id

View File

@@ -84,6 +84,7 @@ class Battle
attr_accessor :poke_ball_failed # Set after first_poke_ball to prevent it being set again
attr_reader :switching # True if during the switching phase of the round
attr_reader :futureSight # True if Future Sight is hitting
attr_reader :command_phase
attr_reader :endOfRound # True during the end of round
attr_accessor :moldBreaker # True if Mold Breaker applies
attr_reader :struggle # The Struggle move
@@ -159,15 +160,12 @@ class Battle
@lastMoveUser = -1
@switching = false
@futureSight = false
@command_phase = false
@endOfRound = false
@moldBreaker = false
@runCommand = 0
@nextPickupUse = 0
if GameData::Move.exists?(:STRUGGLE)
@struggle = Move.from_pokemon_move(self, Pokemon::Move.new(:STRUGGLE))
else
@struggle = Move::Struggle.new(self, nil)
end
@struggle = Move::Struggle.new(self, nil)
@mega_rings = []
GameData::Item.each { |item| @mega_rings.push(item.id) if item.has_flag?("MegaRing") }
@battleAI = AI.new(self)

View File

@@ -246,7 +246,8 @@ class Battle
#=============================================================================
def pbStartBattle
PBDebug.log("")
PBDebug.log("******************************************")
PBDebug.log("================================================================")
PBDebug.log("")
logMsg = "[Started battle] "
if @sideSizes[0] == 1 && @sideSizes[1] == 1
logMsg += "Single "
@@ -278,6 +279,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
@@ -321,7 +323,7 @@ class Battle
@turnCount = 0
loop do # Now begin the battle loop
PBDebug.log("")
PBDebug.log("***Round #{@turnCount + 1}***")
PBDebug.log_header("===== Round #{@turnCount + 1} =====")
if @debug && @turnCount >= 100
@decision = pbDecisionOnTime
PBDebug.log("")
@@ -407,7 +409,8 @@ class Battle
##### WIN #####
when 1
PBDebug.log("")
PBDebug.log("***Player won***")
PBDebug.log_header("===== Player won =====")
PBDebug.log("")
if trainerBattle?
@scene.pbTrainerBattleSuccess
case @opponent.length
@@ -426,6 +429,7 @@ class Battle
msg = "..." if !msg || msg.empty?
pbDisplayPaused(msg.gsub(/\\[Pp][Nn]/, pbPlayer.name))
end
PBDebug.log("")
end
# Gain money from winning a trainer battle, and from Pay Day
pbGainMoney if @decision != 4
@@ -434,8 +438,9 @@ class Battle
##### LOSE, DRAW #####
when 2, 5
PBDebug.log("")
PBDebug.log("***Player lost***") if @decision == 2
PBDebug.log("***Player drew with opponent***") if @decision == 5
PBDebug.log_header("===== Player lost =====") if @decision == 2
PBDebug.log_header("===== Player drew with opponent =====") if @decision == 5
PBDebug.log("")
if @internalBattle
pbDisplayPaused(_INTL("You have no more Pokémon that can fight!"))
if trainerBattle?
@@ -461,10 +466,14 @@ class Battle
msg = "..." if !msg || msg.empty?
pbDisplayPaused(msg.gsub(/\\[Pp][Nn]/, pbPlayer.name))
end
PBDebug.log("")
end
end
##### CAUGHT WILD POKÉMON #####
when 4
PBDebug.log("")
PBDebug.log_header("===== Pokémon caught =====")
PBDebug.log("")
@scene.pbWildBattleSuccess if !Settings::GAIN_EXP_FOR_CAPTURE
end
# Register captured Pokémon in the Pokédex, and store them

View File

@@ -243,14 +243,14 @@ class Battle
end
end
# Write the priority order to the debug log
logMsg = (fullCalc) ? "[Round order] " : "[Round order recalculated] "
comma = false
@priority.each do |entry|
logMsg += ", " if comma
logMsg += "#{entry[0].pbThis(comma)} (#{entry[0].index})"
comma = true
if fullCalc && $INTERNAL
logMsg = "[Round order] "
@priority.each_with_index do |entry, i|
logMsg += ", " if i > 0
logMsg += "#{entry[0].pbThis(i > 0)} (#{entry[0].index})"
end
PBDebug.log(logMsg)
end
PBDebug.log(logMsg)
end
end

View File

@@ -6,7 +6,7 @@ class Battle
# battle.
# NOTE: Messages are only shown while in the party screen when choosing a
# command for the next round.
def pbCanSwitchLax?(idxBattler, idxParty, partyScene = nil)
def pbCanSwitchIn?(idxBattler, idxParty, partyScene = nil)
return true if idxParty < 0
party = pbParty(idxBattler)
return false if idxParty >= party.length
@@ -18,8 +18,7 @@ class Battle
if !pbIsOwner?(idxBattler, idxParty)
if partyScene
owner = pbGetOwnerFromPartyIndex(idxBattler, idxParty)
partyScene.pbDisplay(_INTL("You can't switch {1}'s Pokémon with one of yours!",
owner.name))
partyScene.pbDisplay(_INTL("You can't switch {1}'s Pokémon with one of yours!", owner.name))
end
return false
end
@@ -35,30 +34,15 @@ class Battle
end
# Check whether the currently active Pokémon (at battler index idxBattler) can
# switch out (and that its replacement at party index idxParty can switch in).
# NOTE: Messages are only shown while in the party screen when choosing a
# command for the next round.
def pbCanSwitch?(idxBattler, idxParty = -1, partyScene = nil)
# Check whether party Pokémon can switch in
return false if !pbCanSwitchLax?(idxBattler, idxParty, partyScene)
# Make sure another battler isn't already choosing to switch to the party
# Pokémon
allSameSideBattlers(idxBattler).each do |b|
next if choices[b.index][0] != :SwitchOut || choices[b.index][1] != idxParty
partyScene&.pbDisplay(_INTL("{1} has already been selected.",
pbParty(idxBattler)[idxParty].name))
return false
end
# Check whether battler can switch out
# switch out.
def pbCanSwitchOut?(idxBattler, partyScene = nil)
battler = @battlers[idxBattler]
return true if battler.fainted?
# Ability/item effects that allow switching no matter what
if battler.abilityActive? &&
Battle::AbilityEffects.triggerCertainSwitching(battler.ability, battler, self)
if battler.abilityActive? && Battle::AbilityEffects.triggerCertainSwitching(battler.ability, battler, self)
return true
end
if battler.itemActive? &&
Battle::ItemEffects.triggerCertainSwitching(battler.item, battler, self)
if battler.itemActive? && Battle::ItemEffects.triggerCertainSwitching(battler.item, battler, self)
return true
end
# Other certain switching effects
@@ -72,25 +56,42 @@ class Battle
allOtherSideBattlers(idxBattler).each do |b|
next if !b.abilityActive?
if Battle::AbilityEffects.triggerTrappingByTarget(b.ability, battler, b, self)
partyScene&.pbDisplay(_INTL("{1}'s {2} prevents switching!",
b.pbThis, b.abilityName))
partyScene&.pbDisplay(_INTL("{1}'s {2} prevents switching!", b.pbThis, b.abilityName))
return false
end
end
allOtherSideBattlers(idxBattler).each do |b|
next if !b.itemActive?
if Battle::ItemEffects.triggerTrappingByTarget(b.item, battler, b, self)
partyScene&.pbDisplay(_INTL("{1}'s {2} prevents switching!",
b.pbThis, b.itemName))
partyScene&.pbDisplay(_INTL("{1}'s {2} prevents switching!", b.pbThis, b.itemName))
return false
end
end
return true
end
# Check whether the currently active Pokémon (at battler index idxBattler) can
# switch out (and that its replacement at party index idxParty can switch in).
# NOTE: Messages are only shown while in the party screen when choosing a
# command for the next round.
def pbCanSwitch?(idxBattler, idxParty = -1, partyScene = nil)
# Check whether party Pokémon can switch in
return false if !pbCanSwitchIn?(idxBattler, idxParty, partyScene)
# Make sure another battler isn't already choosing to switch to the party
# Pokémon
allSameSideBattlers(idxBattler).each do |b|
next if choices[b.index][0] != :SwitchOut || choices[b.index][1] != idxParty
partyScene&.pbDisplay(_INTL("{1} has already been selected.",
pbParty(idxBattler)[idxParty].name))
return false
end
# Check whether battler can switch out
return pbCanSwitchOut?(idxBattler, partyScene)
end
def pbCanChooseNonActive?(idxBattler)
pbParty(idxBattler).each_with_index do |_pkmn, i|
return true if pbCanSwitchLax?(idxBattler, i)
return true if pbCanSwitchIn?(idxBattler, i)
end
return false
end
@@ -113,7 +114,7 @@ class Battle
ret = -1
@scene.pbPartyScreen(idxBattler, canCancel) do |idxParty, partyScene|
if checkLaxOnly
next false if !pbCanSwitchLax?(idxBattler, idxParty, partyScene)
next false if !pbCanSwitchIn?(idxBattler, idxParty, partyScene)
elsif !pbCanSwitch?(idxBattler, idxParty, partyScene)
next false
end
@@ -129,8 +130,8 @@ class Battle
# For choosing a replacement Pokémon when prompted in the middle of other
# things happening (U-turn, Baton Pass, in def pbEORSwitch).
def pbSwitchInBetween(idxBattler, checkLaxOnly = false, canCancel = false)
return pbPartyScreen(idxBattler, checkLaxOnly, canCancel) if pbOwnedByPlayer?(idxBattler)
return @battleAI.pbDefaultChooseNewEnemy(idxBattler, pbParty(idxBattler))
return pbPartyScreen(idxBattler, checkLaxOnly, canCancel) if !@controlPlayer && pbOwnedByPlayer?(idxBattler)
return @battleAI.pbDefaultChooseNewEnemy(idxBattler)
end
#=============================================================================
@@ -206,7 +207,7 @@ class Battle
if random
choices = [] # Find all Pokémon that can switch in
eachInTeamFromBattlerIndex(idxBattler) do |_pkmn, i|
choices.push(i) if pbCanSwitchLax?(idxBattler, i)
choices.push(i) if pbCanSwitchIn?(idxBattler, i)
end
return -1 if choices.length == 0
return choices[pbRandom(choices.length)]

View File

@@ -140,7 +140,7 @@ class Battle
def pbPartyMenu(idxBattler)
ret = -1
if @debug
ret = @battleAI.pbDefaultChooseNewEnemy(idxBattler, pbParty(idxBattler))
ret = @battleAI.pbDefaultChooseNewEnemy(idxBattler)
else
ret = pbPartyScreen(idxBattler, false, true, true)
end
@@ -172,6 +172,7 @@ class Battle
# Command phase
#=============================================================================
def pbCommandPhase
@command_phase = true
@scene.pbBeginCommandPhase
# Reset choices if commands can be shown
@battlers.each_with_index do |b, i|
@@ -186,8 +187,12 @@ class Battle
end
# Choose actions for the round (player first, then AI)
pbCommandPhaseLoop(true) # Player chooses their actions
return if @decision != 0 # Battle ended, stop choosing actions
if @decision != 0 # Battle ended, stop choosing actions
@command_phase = false
return
end
pbCommandPhaseLoop(false) # AI chooses their actions
@command_phase = false
end
def pbCommandPhaseLoop(isPlayer)
@@ -200,8 +205,11 @@ class Battle
idxBattler += 1
break if idxBattler >= @battlers.length
next if !@battlers[idxBattler] || pbOwnedByPlayer?(idxBattler) != isPlayer
next if @choices[idxBattler][0] != :None # Action is forced, can't choose one
next if !pbCanShowCommands?(idxBattler) # Action is forced, can't choose one
if @choices[idxBattler][0] != :None || !pbCanShowCommands?(idxBattler)
# Action is forced, can't choose one
PBDebug.log_ai("#{@battlers[idxBattler].pbThis} (#{idxBattler}) is forced to use a multi-turn move")
next
end
# AI controls this battler
if @controlPlayer || !pbOwnedByPlayer?(idxBattler)
@battleAI.pbDefaultChooseEnemyCommand(idxBattler)

View File

@@ -186,9 +186,9 @@ class Battle
end
b.effects[PBEffects::Rage] = false if !pbChoseMoveFunctionCode?(i, "StartRaiseUserAtk1WhenDamaged")
end
PBDebug.log("")
# Calculate move order for this round
pbCalculatePriority(true)
PBDebug.log("")
# Perform actions
pbAttackPhasePriorityChangeMessages
pbAttackPhaseCall

View File

@@ -134,6 +134,8 @@ class Battle
#=============================================================================
def pbEORSeaOfFireDamage(priority)
2.times do |side|
next if sides[side].effects[PBEffects::SeaOfFire] == 0
sides[side].effects[PBEffects::SeaOfFire] -= 1
next if sides[side].effects[PBEffects::SeaOfFire] == 0
pbCommonAnimation("SeaOfFire") if side == 0
pbCommonAnimation("SeaOfFireOpp") if side == 1
@@ -594,7 +596,7 @@ class Battle
#=============================================================================
def pbEndOfRoundPhase
PBDebug.log("")
PBDebug.log("[End of round]")
PBDebug.log("[End of round #{@turnCount + 1}]")
@endOfRound = true
@scene.pbBeginEndOfRoundPhase
pbCalculatePriority # recalculate speeds
@@ -747,6 +749,7 @@ class Battle
battler.lastHPLostFromFoe = 0
battler.droppedBelowHalfHP = false
battler.statsDropped = false
battler.tookMoveDamageThisRound = false
battler.tookDamageThisRound = false
battler.tookPhysicalHit = false
battler.statsRaisedThisRound = false

View File

@@ -37,6 +37,7 @@ class Battle::Battler
attr_accessor :currentMove # ID of multi-turn move currently being used
attr_accessor :droppedBelowHalfHP # Used for Emergency Exit/Wimp Out
attr_accessor :statsDropped # Used for Eject Pack
attr_accessor :tookMoveDamageThisRound # Boolean for Focus Punch
attr_accessor :tookDamageThisRound # Boolean for whether self took damage this round
attr_accessor :tookPhysicalHit
attr_accessor :statsRaisedThisRound # Boolean for whether self's stat(s) raised this round
@@ -44,6 +45,13 @@ class Battle::Battler
attr_accessor :canRestoreIceFace # Whether Hail started in the round
attr_accessor :damageState
# These arrays should all have the same number of values in them
STAT_STAGE_MULTIPLIERS = [2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8]
STAT_STAGE_DIVISORS = [8, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2]
ACC_EVA_STAGE_MULTIPLIERS = [3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7, 8, 9]
ACC_EVA_STAGE_DIVISORS = [9, 8, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3]
STAT_STAGE_MAXIMUM = 6 # Is also the minimum (-6)
#=============================================================================
# Complex accessors
#=============================================================================
@@ -239,10 +247,8 @@ class Battle::Battler
#=============================================================================
def pbSpeed
return 1 if fainted?
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 = @stages[:SPEED] + 6
speed = @speed * stageMul[stage] / stageDiv[stage]
stage = @stages[:SPEED] + STAT_STAGE_MAXIMUM
speed = @speed * STAT_STAGE_MULTIPLIERS[stage] / STAT_STAGE_DIVISORS[stage]
speedMult = 1.0
# Ability effects that alter calculated Speed
if abilityActive?

View File

@@ -125,27 +125,28 @@ class Battle::Battler
@effects[PBEffects::Substitute] = 0
@effects[PBEffects::Telekinesis] = 0
end
@fainted = (@hp == 0)
@lastAttacker = []
@lastFoeAttacker = []
@lastHPLost = 0
@lastHPLostFromFoe = 0
@droppedBelowHalfHP = false
@statsDropped = false
@tookDamageThisRound = false
@tookPhysicalHit = false
@statsRaisedThisRound = false
@statsLoweredThisRound = false
@canRestoreIceFace = false
@lastMoveUsed = nil
@lastMoveUsedType = nil
@lastRegularMoveUsed = nil
@lastRegularMoveTarget = -1
@lastRoundMoved = -1
@lastMoveFailed = false
@lastRoundMoveFailed = false
@movesUsed = []
@turnCount = 0
@fainted = (@hp == 0)
@lastAttacker = []
@lastFoeAttacker = []
@lastHPLost = 0
@lastHPLostFromFoe = 0
@droppedBelowHalfHP = false
@statsDropped = false
@tookMoveDamageThisRound = false
@tookDamageThisRound = false
@tookPhysicalHit = false
@statsRaisedThisRound = false
@statsLoweredThisRound = false
@canRestoreIceFace = false
@lastMoveUsed = nil
@lastMoveUsedType = nil
@lastRegularMoveUsed = nil
@lastRegularMoveTarget = -1
@lastRoundMoved = -1
@lastMoveFailed = false
@lastRoundMoveFailed = false
@movesUsed = []
@turnCount = 0
@effects[PBEffects::Attract] = -1
@battle.allBattlers.each do |b| # Other battlers no longer attracted to self
b.effects[PBEffects::Attract] = -1 if b.effects[PBEffects::Attract] == @index

View File

@@ -8,13 +8,14 @@ class Battle::Battler
amt = 1 if amt < 1 && !fainted?
oldHP = @hp
self.hp -= amt
PBDebug.log("[HP change] #{pbThis} lost #{amt} HP (#{oldHP}=>#{@hp})") if amt > 0
PBDebug.log("[HP change] #{pbThis} lost #{amt} HP (#{oldHP} -> #{@hp})") if amt > 0
raise _INTL("HP less than 0") if @hp < 0
raise _INTL("HP greater than total HP") if @hp > @totalhp
@battle.scene.pbHPChanged(self, oldHP, anim) if anyAnim && amt > 0
if amt > 0 && registerDamage
@droppedBelowHalfHP = true if @hp < @totalhp / 2 && @hp + amt >= @totalhp / 2
@tookDamageThisRound = true
@tookMoveDamageThisRound = true
end
return amt
end
@@ -25,7 +26,7 @@ class Battle::Battler
amt = 1 if amt < 1 && @hp < @totalhp
oldHP = @hp
self.hp += amt
PBDebug.log("[HP change] #{pbThis} gained #{amt} HP (#{oldHP}=>#{@hp})") if amt > 0
PBDebug.log("[HP change] #{pbThis} gained #{amt} HP (#{oldHP} -> #{@hp})") if amt > 0
raise _INTL("HP less than 0") if @hp < 0
raise _INTL("HP greater than total HP") if @hp > @totalhp
@battle.scene.pbHPChanged(self, oldHP, anim) if anyAnim && amt > 0

View File

@@ -171,7 +171,7 @@ class Battle::Battler
return true
end
def pbCanSynchronizeStatus?(newStatus, target)
def pbCanSynchronizeStatus?(newStatus, user)
return false if fainted?
# Trying to replace a status problem with another one
return false if self.status != :NONE
@@ -181,8 +181,8 @@ class Battle::Battler
hasImmuneType = false
case newStatus
when :POISON
# NOTE: target will have Synchronize, so it can't have Corrosion.
if !(target && target.hasActiveAbility?(:CORROSION))
# NOTE: user will have Synchronize, so it can't have Corrosion.
if !(user && user.hasActiveAbility?(:CORROSION))
hasImmuneType |= pbHasType?(:POISON)
hasImmuneType |= pbHasType?(:STEEL)
end
@@ -205,6 +205,7 @@ class Battle::Battler
return false
end
# Safeguard immunity
# NOTE: user will have Synchronize, so it can't have Infiltrator.
if pbOwnSide.effects[PBEffects::Safeguard] > 0 &&
!(user && user.hasActiveAbility?(:INFILTRATOR))
return false

View File

@@ -3,7 +3,7 @@ class Battle::Battler
# Increase stat stages
#=============================================================================
def statStageAtMax?(stat)
return @stages[stat] >= 6
return @stages[stat] >= STAT_STAGE_MAXIMUM
end
def pbCanRaiseStatStage?(stat, user = nil, move = nil, showFailMsg = false, ignoreContrary = false)
@@ -33,11 +33,11 @@ class Battle::Battler
increment *= 2 if hasActiveAbility?(:SIMPLE)
end
# Change the stat stage
increment = [increment, 6 - @stages[stat]].min
increment = [increment, STAT_STAGE_MAXIMUM - @stages[stat]].min
if increment > 0
stat_name = GameData::Stat.get(stat).name
new = @stages[stat] + increment
PBDebug.log("[Stat change] #{pbThis}'s #{stat_name}: #{@stages[stat]} -> #{new} (+#{increment})")
PBDebug.log("[Stat change] #{pbThis}'s #{stat_name} changed by +#{increment} (#{@stages[stat]} -> #{new})")
@stages[stat] += increment
@statsRaisedThisRound = true
end
@@ -117,7 +117,7 @@ class Battle::Battler
# Decrease stat stages
#=============================================================================
def statStageAtMin?(stat)
return @stages[stat] <= -6
return @stages[stat] <= -STAT_STAGE_MAXIMUM
end
def pbCanLowerStatStage?(stat, user = nil, move = nil, showFailMsg = false,
@@ -183,11 +183,11 @@ class Battle::Battler
increment *= 2 if hasActiveAbility?(:SIMPLE)
end
# Change the stat stage
increment = [increment, 6 + @stages[stat]].min
increment = [increment, STAT_STAGE_MAXIMUM + @stages[stat]].min
if increment > 0
stat_name = GameData::Stat.get(stat).name
new = @stages[stat] - increment
PBDebug.log("[Stat change] #{pbThis}'s #{stat_name}: #{@stages[stat]} -> #{new} (-#{increment})")
PBDebug.log("[Stat change] #{pbThis}'s #{stat_name} changed by -#{increment} (#{@stages[stat]} -> #{new})")
@stages[stat] -= increment
@statsLoweredThisRound = true
@statsDropped = true

View File

@@ -282,7 +282,8 @@ class Battle::Battler
# NOTE: A Pokémon using Bug Bite/Pluck, and a Pokémon having an item thrown at
# it via Fling, will gain the effect of the item even if the Pokémon is
# affected by item-negating effects.
# item_to_use is an item ID for Bug Bite/Pluck and Fling, and nil otherwise.
# item_to_use is an item ID for Stuff Cheeks, Teatime, Bug Bite/Pluck and
# Fling, and nil otherwise.
# fling is for Fling only.
def pbHeldItemTriggerCheck(item_to_use = nil, fling = false)
return if fainted?
@@ -386,7 +387,7 @@ class Battle::Battler
#=============================================================================
# Item effects
#=============================================================================
def pbConfusionBerry(item_to_use, forced, flavor, confuse_msg)
def pbConfusionBerry(item_to_use, forced, confuse_stat, confuse_msg)
return false if !forced && !canHeal?
return false if !forced && !canConsumePinchBerry?(Settings::MECHANICS_GENERATION >= 7)
used_item_name = GameData::Item.get(item_to_use).name
@@ -414,12 +415,9 @@ class Battle::Battler
@battle.pbDisplay(_INTL("{1} restored its health using its {2}!", pbThis, used_item_name))
end
end
flavor_stat = [:ATTACK, :DEFENSE, :SPEED, :SPECIAL_ATTACK, :SPECIAL_DEFENSE][flavor]
self.nature.stat_changes.each do |change|
next if change[1] > 0 || change[0] != flavor_stat
if self.nature.stat_changes.any? { |val| val[0] == confuse_stat && val[1] < 0 }
@battle.pbDisplay(confuse_msg)
pbConfuse if pbCanConfuseSelf?(false)
break
end
return true
end

View File

@@ -47,7 +47,7 @@ class Battle::Battler
return false
end
# Use the move
PBDebug.log("[Move usage] #{pbThis} started using #{choice[2].name}")
PBDebug.log("[Use move] #{pbThis} (#{@index}) used #{choice[2].name}")
PBDebug.logonerr { pbUseMove(choice, choice[2] == @battle.struggle) }
@battle.pbJudge
# Update priority order
@@ -144,7 +144,7 @@ class Battle::Battler
choice[2].pp = -1
end
choice[3] = target # Target (-1 means no target yet)
PBDebug.log("[Move usage] #{pbThis} started using the called/simple move #{choice[2].name}")
PBDebug.log("[Use move] #{pbThis} used the called/simple move #{choice[2].name}")
pbUseMove(choice, specialUsage)
end

View File

@@ -43,19 +43,21 @@ class Battle::Battler
# Choice Band/Gorilla Tactics
@effects[PBEffects::ChoiceBand] = nil if !pbHasMove?(@effects[PBEffects::ChoiceBand])
if @effects[PBEffects::ChoiceBand] && move.id != @effects[PBEffects::ChoiceBand]
choiced_move_name = GameData::Move.get(@effects[PBEffects::ChoiceBand]).name
if hasActiveItem?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF])
if showMessages
msg = _INTL("The {1} only allows the use of {2}!", itemName, choiced_move_name)
(commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg)
choiced_move = GameData::Move.try_get(@effects[PBEffects::ChoiceBand])
if choiced_move
if hasActiveItem?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF])
if showMessages
msg = _INTL("The {1} only allows the use of {2}!", itemName, choiced_move.name)
(commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg)
end
return false
elsif hasActiveAbility?(:GORILLATACTICS)
if showMessages
msg = _INTL("{1} can only use {2}!", pbThis, choiced_move.name)
(commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg)
end
return false
end
return false
elsif hasActiveAbility?(:GORILLATACTICS)
if showMessages
msg = _INTL("{1} can only use {2}!", pbThis, choiced_move_name)
(commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg)
end
return false
end
end
# Taunt
@@ -85,7 +87,7 @@ class Battle::Battler
end
# Assault Vest (prevents choosing status moves but doesn't prevent
# executing them)
if hasActiveItem?(:ASSAULTVEST) && move.statusMove? && move.id != :MEFIRST && commandPhase
if hasActiveItem?(:ASSAULTVEST) && move.statusMove? && move.function != "UseMoveTargetIsAboutToUse" && commandPhase
if showMessages
msg = _INTL("The effects of the {1} prevent status moves from being used!", itemName)
(commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg)
@@ -192,10 +194,13 @@ class Battle::Battler
return false
end
if @effects[PBEffects::HyperBeam] > 0 # Intentionally before Truant
PBDebug.log("[Move failed] #{pbThis} is recharging after using #{move.name}")
@battle.pbDisplay(_INTL("{1} must recharge!", pbThis))
@effects[PBEffects::Truant] = !@effects[PBEffects::Truant] if hasActiveAbility?(:TRUANT)
return false
end
if choice[1] == -2 # Battle Palace
PBDebug.log("[Move failed] #{pbThis} can't act in the Battle Palace somehow")
@battle.pbDisplay(_INTL("{1} appears incapable of using its power!", pbThis))
return false
end
@@ -210,6 +215,7 @@ class Battle::Battler
else
pbContinueStatus
if !move.usableWhenAsleep? # Snore/Sleep Talk
PBDebug.log("[Move failed] #{pbThis} is asleep")
@lastMoveFailed = true
return false
end
@@ -220,6 +226,7 @@ class Battle::Battler
pbCureStatus
else
pbContinueStatus
PBDebug.log("[Move failed] #{pbThis} is frozen")
@lastMoveFailed = true
return false
end
@@ -235,12 +242,14 @@ class Battle::Battler
@battle.pbDisplay(_INTL("{1} is loafing around!", pbThis))
@lastMoveFailed = true
@battle.pbHideAbilitySplash(self)
PBDebug.log("[Move failed] #{pbThis} can't act because of #{abilityName}")
return false
end
end
# Flinching
if @effects[PBEffects::Flinch]
@battle.pbDisplay(_INTL("{1} flinched and couldn't move!", pbThis))
PBDebug.log("[Move failed] #{pbThis} flinched")
if abilityActive?
Battle::AbilityEffects.triggerOnFlinch(self.ability, self, @battle)
end
@@ -259,6 +268,7 @@ class Battle::Battler
threshold = (Settings::MECHANICS_GENERATION >= 7) ? 33 : 50 # % chance
if @battle.pbRandom(100) < threshold
pbConfusionDamage(_INTL("It hurt itself in its confusion!"))
PBDebug.log("[Move failed] #{pbThis} hurt itself in its confusion")
@lastMoveFailed = true
return false
end
@@ -267,6 +277,7 @@ class Battle::Battler
# Paralysis
if @status == :PARALYSIS && @battle.pbRandom(100) < 25
pbContinueStatus
PBDebug.log("[Move failed] #{pbThis} is paralyzed")
@lastMoveFailed = true
return false
end
@@ -277,6 +288,7 @@ class Battle::Battler
@battle.battlers[@effects[PBEffects::Attract]].pbThis(true)))
if @battle.pbRandom(100) < 50
@battle.pbDisplay(_INTL("{1} is immobilized by love!", pbThis))
PBDebug.log("[Move failed] #{pbThis} is immobilized by love")
@lastMoveFailed = true
return false
end
@@ -295,7 +307,10 @@ class Battle::Battler
# Two-turn attacks can't fail here in the charging turn
return true if user.effects[PBEffects::TwoTurnAttack]
# Move-specific failures
return false if move.pbFailsAgainstTarget?(user, target, show_message)
if move.pbFailsAgainstTarget?(user, target, show_message)
PBDebug.log(sprintf("[Move failed] In function code %s's def pbFailsAgainstTarget?", move.function))
return false
end
# Immunity to priority moves because of Psychic Terrain
if @battle.field.terrain == :Psychic && target.affectedByTerrain? && target.opposes?(user) &&
@battle.choices[user.index][4] > 0 # Move priority saved from pbCalculatePriority
@@ -551,15 +566,17 @@ class Battle::Battler
miss = true
end
end
target.damageState.invulnerable = true if miss
if !miss
if miss
target.damageState.invulnerable = true
PBDebug.log("[Move failed] Target is semi-invulnerable")
else
# Called by another move
return true if skipAccuracyCheck
# Accuracy check
return true if move.pbAccuracyCheck(user, target) # Includes Counter/Mirror Coat
PBDebug.log("[Move failed] Failed pbAccuracyCheck")
end
# Missed
PBDebug.log("[Move failed] Failed pbAccuracyCheck or target is semi-invulnerable")
return false
end

View File

@@ -43,11 +43,10 @@ class Battle::Move
@category = move.category
@accuracy = move.accuracy
@pp = move.pp # Can be changed with Mimic/Transform
@addlEffect = move.effect_chance
@target = move.target
@priority = move.priority
@flags = move.flags.clone
@calcType = nil
@addlEffect = move.effect_chance
@powerBoost = false # For Aerilate, Pixilate, Refrigerate, Galvanize
@snatched = false
end

View File

@@ -251,10 +251,9 @@ class Battle::Move
oldHP = b.hp
if b.damageState.substitute
old_sub_hp = b.effects[PBEffects::Substitute] + b.damageState.hpLost
PBDebug.log("[Move damage] #{b.pbThis}'s substitute lost #{b.damageState.hpLost} HP (#{old_sub_hp}=>#{b.effects[PBEffects::Substitute]})")
PBDebug.log("[Substitute HP change] #{b.pbThis}'s substitute lost #{b.damageState.hpLost} HP (#{old_sub_hp} -> #{b.effects[PBEffects::Substitute]})")
else
oldHP += b.damageState.hpLost
PBDebug.log("[Move damage] #{b.pbThis} lost #{b.damageState.hpLost} HP (#{oldHP}=>#{b.hp})")
end
effectiveness = 0
if Effectiveness.resistant?(b.damageState.typeMod)
@@ -375,19 +374,22 @@ class Battle::Move
# code.
moveType = nil
moveType = :NORMAL if @function == "TypeDependsOnUserIVs" # Hidden Power
if physicalMove?(moveType)
target.effects[PBEffects::Counter] = damage
target.effects[PBEffects::CounterTarget] = user.index
elsif specialMove?(moveType)
target.effects[PBEffects::MirrorCoat] = damage
target.effects[PBEffects::MirrorCoatTarget] = user.index
if !target.damageState.substitute
if physicalMove?(moveType)
target.effects[PBEffects::Counter] = damage
target.effects[PBEffects::CounterTarget] = user.index
elsif specialMove?(moveType)
target.effects[PBEffects::MirrorCoat] = damage
target.effects[PBEffects::MirrorCoatTarget] = user.index
end
end
if target.effects[PBEffects::Bide] > 0
target.effects[PBEffects::BideDamage] += damage
target.effects[PBEffects::BideTarget] = user.index
end
target.damageState.fainted = true if target.fainted?
target.lastHPLost = damage # For Focus Punch
target.lastHPLost = damage
target.tookMoveDamageThisRound = true if damage > 0 && !target.damageState.substitute # For Focus Punch
target.tookDamageThisRound = true if damage > 0 # For Assurance
target.lastAttacker.push(user.index) # For Revenge
if target.opposes?(user)

View File

@@ -101,10 +101,11 @@ class Battle::Move
# Check if move can't miss
return true 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]
max_stage = Battle::Battler::STAT_STAGE_MAXIMUM
accStage = [[modifiers[:accuracy_stage], -max_stage].max, max_stage].min + max_stage
evaStage = [[modifiers[:evasion_stage], -max_stage].max, max_stage].min + max_stage
stageMul = Battle::Battler::ACC_EVA_STAGE_MULTIPLIERS
stageDiv = Battle::Battler::ACC_EVA_STAGE_DIVISORS
accuracy = 100.0 * stageMul[accStage] / stageDiv[accStage]
evasion = 100.0 * stageMul[evaStage] / stageDiv[evaStage]
accuracy = (accuracy * modifiers[:accuracy_multiplier]).round
@@ -226,13 +227,13 @@ class Battle::Move
def pbModifyDamage(damageMult, user, target); return damageMult; end
def pbGetAttackStats(user, target)
return user.spatk, user.stages[:SPECIAL_ATTACK] + 6 if specialMove?
return user.attack, user.stages[:ATTACK] + 6
return user.spatk, user.stages[:SPECIAL_ATTACK] + Battle::Battler::STAT_STAGE_MAXIMUM if specialMove?
return user.attack, user.stages[:ATTACK] + Battle::Battler::STAT_STAGE_MAXIMUM
end
def pbGetDefenseStats(user, target)
return target.spdef, target.stages[:SPECIAL_DEFENSE] + 6 if specialMove?
return target.defense, target.stages[:DEFENSE] + 6
return target.spdef, target.stages[:SPECIAL_DEFENSE] + Battle::Battler::STAT_STAGE_MAXIMUM if specialMove?
return target.defense, target.stages[:DEFENSE] + Battle::Battler::STAT_STAGE_MAXIMUM
end
def pbCalcDamage(user, target, numTargets = 1)
@@ -241,8 +242,9 @@ class Battle::Move
target.damageState.calcDamage = 1
return
end
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]
max_stage = Battle::Battler::STAT_STAGE_MAXIMUM
stageMul = Battle::Battler::STAT_STAGE_MULTIPLIERS
stageDiv = Battle::Battler::STAT_STAGE_DIVISORS
# Get the move's type
type = @calcType # nil is treated as physical
# Calculate whether this hit deals critical damage
@@ -252,13 +254,13 @@ class Battle::Move
# Calculate user's attack stat
atk, atkStage = pbGetAttackStats(user, target)
if !target.hasActiveAbility?(:UNAWARE) || @battle.moldBreaker
atkStage = 6 if target.damageState.critical && atkStage < 6
atkStage = max_stage if target.damageState.critical && atkStage < max_stage
atk = (atk.to_f * stageMul[atkStage] / stageDiv[atkStage]).floor
end
# Calculate target's defense stat
defense, defStage = pbGetDefenseStats(user, target)
if !user.hasActiveAbility?(:UNAWARE)
defStage = 6 if target.damageState.critical && defStage > 6
defStage = max_stage if target.damageState.critical && defStage > max_stage
defense = (defense.to_f * stageMul[defStage] / stageDiv[defStage]).floor
end
# Calculate all multiplier effects
@@ -283,7 +285,7 @@ class Battle::Move
if (@battle.pbCheckGlobalAbility(:DARKAURA) && type == :DARK) ||
(@battle.pbCheckGlobalAbility(:FAIRYAURA) && type == :FAIRY)
if @battle.pbCheckGlobalAbility(:AURABREAK)
multipliers[:power_multiplier] *= 2 / 3.0
multipliers[:power_multiplier] *= 3 / 4.0
else
multipliers[:power_multiplier] *= 4 / 3.0
end
@@ -308,10 +310,14 @@ class Battle::Move
Battle::AbilityEffects.triggerDamageCalcFromTarget(
target.ability, user, target, self, multipliers, baseDmg, type
)
Battle::AbilityEffects.triggerDamageCalcFromTargetNonIgnorable(
target.ability, user, target, self, multipliers, baseDmg, type
)
end
end
if target.abilityActive?
Battle::AbilityEffects.triggerDamageCalcFromTargetNonIgnorable(
target.ability, user, target, self, multipliers, baseDmg, type
)
end
if !@battle.moldBreaker
target.allAllies.each do |b|
next if !b.abilityActive?
Battle::AbilityEffects.triggerDamageCalcFromTargetAlly(
@@ -413,6 +419,8 @@ class Battle::Move
if target.pbHasType?(:ROCK) && specialMove? && @function != "UseTargetDefenseInsteadOfTargetSpDef"
multipliers[:defense_multiplier] *= 1.5
end
when :ShadowSky
multipliers[:final_damage_multiplier] *= 1.5 if type == :SHADOW
end
# Critical hits
if target.damageState.critical

View File

@@ -36,7 +36,6 @@ class Battle::Move::Confusion < Battle::Move
@priority = 0
@flags = []
@addlEffect = 0
@calcType = nil
@powerBoost = false
@snatched = false
end
@@ -53,19 +52,18 @@ class Battle::Move::Struggle < Battle::Move
def initialize(battle, move)
@battle = battle
@realMove = nil # Not associated with a move
@id = (move) ? move.id : :STRUGGLE
@name = (move) ? move.name : _INTL("Struggle")
@id = :STRUGGLE
@name = _INTL("Struggle")
@function = "Struggle"
@power = 50
@type = nil
@category = 0
@accuracy = 0
@pp = -1
@target = :NearOther
@target = :RandomNearFoe
@priority = 0
@flags = ["Contact", "CanProtect"]
@addlEffect = 0
@calcType = nil
@powerBoost = false
@snatched = false
end
@@ -85,6 +83,8 @@ end
# Raise one of user's stats.
#===============================================================================
class Battle::Move::StatUpMove < Battle::Move
attr_reader :statUp
def canSnatch?; return true; end
def pbMoveFailed?(user, targets)
@@ -108,6 +108,8 @@ end
# Raise multiple of user's stats.
#===============================================================================
class Battle::Move::MultiStatUpMove < Battle::Move
attr_reader :statUp
def canSnatch?; return true; end
def pbMoveFailed?(user, targets)
@@ -151,6 +153,8 @@ end
# Lower multiple of user's stats.
#===============================================================================
class Battle::Move::StatDownMove < Battle::Move
attr_reader :statDown
def pbEffectWhenDealingDamage(user, target)
return if @battle.pbAllFainted?(target.idxOwnSide)
showAnim = true
@@ -167,6 +171,8 @@ end
# Lower one of target's stats.
#===============================================================================
class Battle::Move::TargetStatDownMove < Battle::Move
attr_reader :statDown
def canMagicCoat?; return true; end
def pbFailsAgainstTarget?(user, target, show_message)
@@ -190,6 +196,8 @@ end
# Lower multiple of target's stats.
#===============================================================================
class Battle::Move::TargetMultiStatDownMove < Battle::Move
attr_reader :statDown
def canMagicCoat?; return true; end
def pbFailsAgainstTarget?(user, target, show_message)
@@ -485,6 +493,8 @@ end
# Weather-inducing move.
#===============================================================================
class Battle::Move::WeatherMove < Battle::Move
attr_reader :weatherType
def initialize(battle, move)
super
@weatherType = :None

View File

@@ -174,11 +174,11 @@ class Battle::Move::FailsIfUserDamagedThisTurn < Battle::Move
end
def pbDisplayUseMessage(user)
super if !user.effects[PBEffects::FocusPunch] || user.lastHPLost == 0
super if !user.effects[PBEffects::FocusPunch] || !user.tookMoveDamageThisRound
end
def pbMoveFailed?(user, targets)
if user.effects[PBEffects::FocusPunch] && user.lastHPLost > 0
if user.effects[PBEffects::FocusPunch] && user.tookMoveDamageThisRound
@battle.pbDisplay(_INTL("{1} lost its focus and couldn't move!", user.pbThis))
return true
end
@@ -187,7 +187,7 @@ class Battle::Move::FailsIfUserDamagedThisTurn < Battle::Move
end
#===============================================================================
# Fails if the target didn't chose a damaging move to use this round, or has
# Fails if the target didn't choose a damaging move to use this round, or has
# already moved. (Sucker Punch)
#===============================================================================
class Battle::Move::FailsIfTargetActed < Battle::Move
@@ -459,6 +459,8 @@ end
# side. (Court Change)
#===============================================================================
class Battle::Move::SwapSideEffects < Battle::Move
attr_reader :number_effects, :boolean_effects
def initialize(battle, move)
super
@number_effects = [

View File

@@ -23,10 +23,17 @@ end
# (Fell Stinger (Gen 6-))
#===============================================================================
class Battle::Move::RaiseUserAttack2IfTargetFaints < Battle::Move
attr_reader :statUp
def initialize(battle, move)
super
@statUp = [:ATTACK, 2]
end
def pbEffectAfterAllHits(user, target)
return if !target.damageState.fainted
return if !user.pbCanRaiseStatStage?(:ATTACK, user, self)
user.pbRaiseStatStage(:ATTACK, 2, user)
return if !user.pbCanRaiseStatStage?(@statUp[0], user, self)
user.pbRaiseStatStage(@statUp[0], @statUp[1], user)
end
end
@@ -45,10 +52,17 @@ end
# (Fell Stinger (Gen 7+))
#===============================================================================
class Battle::Move::RaiseUserAttack3IfTargetFaints < Battle::Move
attr_reader :statUp
def initialize(battle, move)
super
@statUp = [:ATTACK, 3]
end
def pbEffectAfterAllHits(user, target)
return if !target.damageState.fainted
return if !user.pbCanRaiseStatStage?(:ATTACK, user, self)
user.pbRaiseStatStage(:ATTACK, 3, user)
return if !user.pbCanRaiseStatStage?(@statUp[0], user, self)
user.pbRaiseStatStage(@statUp[0], @statUp[1], user)
end
end
@@ -57,15 +71,22 @@ end
# (Belly Drum)
#===============================================================================
class Battle::Move::MaxUserAttackLoseHalfOfTotalHP < Battle::Move
attr_reader :statUp
def canSnatch?; return true; end
def initialize(battle, move)
super
@statUp = [:ATTACK, 12]
end
def pbMoveFailed?(user, targets)
hpLoss = [user.totalhp / 2, 1].max
if user.hp <= hpLoss
@battle.pbDisplay(_INTL("But it failed!"))
return true
end
return true if !user.pbCanRaiseStatStage?(:ATTACK, user, self, true)
return true if !user.pbCanRaiseStatStage?(@statUp[0], user, self, true)
return false
end
@@ -73,16 +94,18 @@ class Battle::Move::MaxUserAttackLoseHalfOfTotalHP < Battle::Move
hpLoss = [user.totalhp / 2, 1].max
user.pbReduceHP(hpLoss, false, false)
if user.hasActiveAbility?(:CONTRARY)
user.stages[:ATTACK] = -6
user.stages[@statUp[0]] = -Battle::Battler::STAT_STAGE_MAXIMUM
user.statsLoweredThisRound = true
user.statsDropped = true
@battle.pbCommonAnimation("StatDown", user)
@battle.pbDisplay(_INTL("{1} cut its own HP and minimized its Attack!", user.pbThis))
@battle.pbDisplay(_INTL("{1} cut its own HP and minimized its {2}!",
user.pbThis, GameData::Stat.get(@statUp[0]).name))
else
user.stages[:ATTACK] = 6
user.stages[@statUp[0]] = Battle::Battler::STAT_STAGE_MAXIMUM
user.statsRaisedThisRound = true
@battle.pbCommonAnimation("StatUp", user)
@battle.pbDisplay(_INTL("{1} cut its own HP and maximized its Attack!", user.pbThis))
@battle.pbDisplay(_INTL("{1} cut its own HP and maximized its {2}!",
user.pbThis, GameData::Stat.get(@statUp[0]).name))
end
user.pbItemHPHealCheck
end
@@ -407,6 +430,8 @@ end
# (Shell Smash)
#===============================================================================
class Battle::Move::LowerUserDefSpDef1RaiseUserAtkSpAtkSpd2 < Battle::Move
attr_reader :statUp, :statDown
def canSnatch?; return true; end
def initialize(battle, move)
@@ -882,11 +907,12 @@ class Battle::Move::RaiseTargetAtkSpAtk2 < Battle::Move
end
def pbEffectAgainstTarget(user, target)
showAnim = true
if target.pbCanRaiseStatStage?(:ATTACK, user, self)
target.pbRaiseStatStage(:ATTACK, 2, user)
showAnim = false if target.pbRaiseStatStage(:ATTACK, 2, user, showAnim)
end
if target.pbCanRaiseStatStage?(:SPECIAL_ATTACK, user, self)
target.pbRaiseStatStage(:SPECIAL_ATTACK, 2, user)
target.pbRaiseStatStage(:SPECIAL_ATTACK, 2, user, showAnim)
end
end
end
@@ -1330,6 +1356,8 @@ end
# stage each. (Venom Drench)
#===============================================================================
class Battle::Move::LowerPoisonedTargetAtkSpAtkSpd1 < Battle::Move
attr_reader :statDown
def canMagicCoat?; return true; end
def initialize(battle, move)
@@ -1342,10 +1370,13 @@ class Battle::Move::LowerPoisonedTargetAtkSpAtkSpd1 < Battle::Move
targets.each do |b|
next if !b || b.fainted?
next if !b.poisoned?
next if !b.pbCanLowerStatStage?(:ATTACK, user, self) &&
!b.pbCanLowerStatStage?(:SPECIAL_ATTACK, user, self) &&
!b.pbCanLowerStatStage?(:SPEED, user, self)
@validTargets.push(b.index)
failed = true
(@statDown.length / 2).times do |i|
next if !b.pbCanLowerStatStage?(@statDown[i * 2], user, self)
failed = false
break
end
@validTargets.push(b.index) if !failed
end
if @validTargets.length == 0
@battle.pbDisplay(_INTL("But it failed!"))
@@ -1397,7 +1428,7 @@ end
# Raises the Attack and Defense of all user's allies by 1 stage each. Bypasses
# protections, including Crafty Shield. Fails if there is no ally. (Coaching)
#===============================================================================
class Battle::Move::RaiseUserAndAlliesAtkDef1 < Battle::Move
class Battle::Move::RaiseAlliesAtkDef1 < Battle::Move
def ignoresSubstitute?(user); return true; end
def canSnatch?; return true; end

View File

@@ -100,11 +100,18 @@ end
# Poisons the target and decreases its Speed by 1 stage. (Toxic Thread)
#===============================================================================
class Battle::Move::PoisonTargetLowerTargetSpeed1 < Battle::Move
attr_reader :statDown
def initialize(battle, move)
super
@statDown = [:SPEED, 1]
end
def canMagicCoat?; return true; end
def pbFailsAgainstTarget?(user, target, show_message)
if !target.pbCanPoison?(user, false, self) &&
!target.pbCanLowerStatStage?(:SPEED, user, self)
!target.pbCanLowerStatStage?(@statDown[0], user, self)
@battle.pbDisplay(_INTL("But it failed!")) if show_message
return true
end
@@ -113,8 +120,8 @@ class Battle::Move::PoisonTargetLowerTargetSpeed1 < Battle::Move
def pbEffectAgainstTarget(user, target)
target.pbPoison(user) if target.pbCanPoison?(user, false, self)
if target.pbCanLowerStatStage?(:SPEED, user, self)
target.pbLowerStatStage(:SPEED, 1, user)
if target.pbCanLowerStatStage?(@statDown[0], user, self)
target.pbLowerStatStage(@statDown[0], @statDown[1], user)
end
end
end
@@ -651,6 +658,34 @@ end
# Changes user's type depending on the environment. (Camouflage)
#===============================================================================
class Battle::Move::SetUserTypesBasedOnEnvironment < Battle::Move
TERRAIN_TYPES = {
:Electric => :ELECTRIC,
:Grassy => :GRASS,
:Misty => :FAIRY,
:Psychic => :PSYCHIC
}
ENVIRONMENT_TYPES = {
:None => :NORMAL,
:Grass => :GRASS,
:TallGrass => :GRASS,
:MovingWater => :WATER,
:StillWater => :WATER,
:Puddle => :WATER,
:Underwater => :WATER,
:Cave => :ROCK,
:Rock => :GROUND,
:Sand => :GROUND,
:Forest => :BUG,
:ForestGrass => :BUG,
:Snow => :ICE,
:Ice => :ICE,
:Volcano => :FIRE,
:Graveyard => :GHOST,
:Sky => :FLYING,
:Space => :DRAGON,
:UltraSpace => :PSYCHIC
}
def canSnatch?; return true; end
def pbMoveFailed?(user, targets)
@@ -659,56 +694,13 @@ class Battle::Move::SetUserTypesBasedOnEnvironment < Battle::Move
return true
end
@newType = :NORMAL
checkedTerrain = false
case @battle.field.terrain
when :Electric
if GameData::Type.exists?(:ELECTRIC)
@newType = :ELECTRIC
checkedTerrain = true
end
when :Grassy
if GameData::Type.exists?(:GRASS)
@newType = :GRASS
checkedTerrain = true
end
when :Misty
if GameData::Type.exists?(:FAIRY)
@newType = :FAIRY
checkedTerrain = true
end
when :Psychic
if GameData::Type.exists?(:PSYCHIC)
@newType = :PSYCHIC
checkedTerrain = true
end
terr_type = TERRAIN_TYPES[@battle.field.terrain]
if terr_type && GameData::Type.exists?(terr_type)
@newType = terr_type
else
@newType = ENVIRONMENT_TYPES[@battle.environment] || :NORMAL
@newType = :NORMAL if !GameData::Type.exists?(@newType)
end
if !checkedTerrain
case @battle.environment
when :Grass, :TallGrass
@newType = :GRASS
when :MovingWater, :StillWater, :Puddle, :Underwater
@newType = :WATER
when :Cave
@newType = :ROCK
when :Rock, :Sand
@newType = :GROUND
when :Forest, :ForestGrass
@newType = :BUG
when :Snow, :Ice
@newType = :ICE
when :Volcano
@newType = :FIRE
when :Graveyard
@newType = :GHOST
when :Sky
@newType = :FLYING
when :Space
@newType = :DRAGON
when :UltraSpace
@newType = :PSYCHIC
end
end
@newType = :NORMAL if !GameData::Type.exists?(@newType)
if !GameData::Type.exists?(@newType) || !user.pbHasOtherType?(@newType)
@battle.pbDisplay(_INTL("But it failed!"))
return true
@@ -887,7 +879,7 @@ class Battle::Move::AddGhostTypeToTarget < Battle::Move
def canMagicCoat?; return true; end
def pbFailsAgainstTarget?(user, target, show_message)
if !GameData::Type.exists?(:GHOST) || target.pbHasType?(:GHOST) || !target.canChangeType?
if !target.canChangeType? || !GameData::Type.exists?(:GHOST) || target.pbHasType?(:GHOST)
@battle.pbDisplay(_INTL("But it failed!")) if show_message
return true
end
@@ -908,7 +900,7 @@ class Battle::Move::AddGrassTypeToTarget < Battle::Move
def canMagicCoat?; return true; end
def pbFailsAgainstTarget?(user, target, show_message)
if !GameData::Type.exists?(:GRASS) || target.pbHasType?(:GRASS) || !target.canChangeType?
if !target.canChangeType? || !GameData::Type.exists?(:GRASS) || target.pbHasType?(:GRASS)
@battle.pbDisplay(_INTL("But it failed!")) if show_message
return true
end
@@ -957,7 +949,7 @@ class Battle::Move::SetTargetAbilityToSimple < Battle::Move
end
def pbFailsAgainstTarget?(user, target, show_message)
if target.unstoppableAbility? || [:TRUANT, :SIMPLE].include?(target.ability)
if target.unstoppableAbility? || [:TRUANT, :SIMPLE].include?(target.ability_id)
@battle.pbDisplay(_INTL("But it failed!")) if show_message
return true
end
@@ -1165,7 +1157,7 @@ class Battle::Move::NegateTargetAbility < Battle::Move
def canMagicCoat?; return true; end
def pbFailsAgainstTarget?(user, target, show_message)
if target.unstoppableAbility?
if target.unstoppableAbility? || target.effects[PBEffects::GastroAcid]
@battle.pbDisplay(_INTL("But it failed!")) if show_message
return true
end

View File

@@ -837,9 +837,9 @@ class Battle::Move::RemoveScreens < Battle::Move
end
def pbShowAnimation(id, user, targets, hitNum = 0, showAnimation = true)
if user.pbOpposingSide.effects[PBEffects::LightScreen] > 0 ||
user.pbOpposingSide.effects[PBEffects::Reflect] > 0 ||
user.pbOpposingSide.effects[PBEffects::AuroraVeil] > 0
if user.pbOpposingSide.effects[PBEffects::AuroraVeil] > 0 ||
user.pbOpposingSide.effects[PBEffects::LightScreen] > 0 ||
user.pbOpposingSide.effects[PBEffects::Reflect] > 0
hitNum = 1 # Wall-breaking anim
end
super
@@ -991,20 +991,8 @@ end
#===============================================================================
# Ends target's protections immediately. (Hyperspace Hole)
#===============================================================================
class Battle::Move::RemoveProtectionsBypassSubstitute < Battle::Move
class Battle::Move::RemoveProtectionsBypassSubstitute < Battle::Move::RemoveProtections
def ignoresSubstitute?(user); return true; end
def pbEffectAgainstTarget(user, target)
target.effects[PBEffects::BanefulBunker] = false
target.effects[PBEffects::KingsShield] = false
target.effects[PBEffects::Obstruct] = false
target.effects[PBEffects::Protect] = false
target.effects[PBEffects::SpikyShield] = false
target.pbOwnSide.effects[PBEffects::CraftyShield] = false
target.pbOwnSide.effects[PBEffects::MatBlock] = false
target.pbOwnSide.effects[PBEffects::QuickGuard] = false
target.pbOwnSide.effects[PBEffects::WideGuard] = false
end
end
#===============================================================================
@@ -1133,24 +1121,25 @@ class Battle::Move::CategoryDependsOnHigherDamagePoisonTarget < Battle::Move::Po
def pbOnStartUse(user, targets)
target = targets[0]
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]
max_stage = Battle::Battler::STAT_STAGE_MAXIMUM
stageMul = Battle::Battler::STAT_STAGE_MULTIPLIERS
stageDiv = Battle::Battler::STAT_STAGE_DIVISORS
# Calculate user's effective attacking values
attack_stage = user.stages[:ATTACK] + 6
attack_stage = user.stages[:ATTACK] + max_stage
real_attack = (user.attack.to_f * stageMul[attack_stage] / stageDiv[attack_stage]).floor
special_attack_stage = user.stages[:SPECIAL_ATTACK] + 6
special_attack_stage = user.stages[:SPECIAL_ATTACK] + max_stage
real_special_attack = (user.spatk.to_f * stageMul[special_attack_stage] / stageDiv[special_attack_stage]).floor
# Calculate target's effective defending values
defense_stage = target.stages[:DEFENSE] + 6
defense_stage = target.stages[:DEFENSE] + max_stage
real_defense = (target.defense.to_f * stageMul[defense_stage] / stageDiv[defense_stage]).floor
special_defense_stage = target.stages[:SPECIAL_DEFENSE] + 6
special_defense_stage = target.stages[:SPECIAL_DEFENSE] + max_stage
real_special_defense = (target.spdef.to_f * stageMul[special_defense_stage] / stageDiv[special_defense_stage]).floor
# Perform simple damage calculation
physical_damage = real_attack.to_f / real_defense
special_damage = real_special_attack.to_f / real_special_defense
# Determine move's category
if physical_damage == special_damage
@calcCategry = @battle.pbRandom(2)
@calcCategory = (@battle.command_phase) ? rand(2) : @battle.pbRandom(2)
else
@calcCategory = (physical_damage > special_damage) ? 0 : 1
end
@@ -1178,13 +1167,14 @@ class Battle::Move::CategoryDependsOnHigherDamageIgnoreTargetAbility < Battle::M
def pbOnStartUse(user, targets)
# Calculate user's effective attacking value
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]
max_stage = Battle::Battler::STAT_STAGE_MAXIMUM
stageMul = Battle::Battler::STAT_STAGE_MULTIPLIERS
stageDiv = Battle::Battler::STAT_STAGE_DIVISORS
atk = user.attack
atkStage = user.stages[:ATTACK] + 6
atkStage = user.stages[:ATTACK] + max_stage
realAtk = (atk.to_f * stageMul[atkStage] / stageDiv[atkStage]).floor
spAtk = user.spatk
spAtkStage = user.stages[:SPECIAL_ATTACK] + 6
spAtkStage = user.stages[:SPECIAL_ATTACK] + max_stage
realSpAtk = (spAtk.to_f * stageMul[spAtkStage] / stageDiv[spAtkStage]).floor
# Determine move's category
@calcCategory = (realAtk > realSpAtk) ? 0 : 1
@@ -1197,9 +1187,9 @@ end
# are applied normally, applying the user's Attack modifiers and not the user's
# Defence modifiers. (Body Press)
#===============================================================================
class Battle::Move::UseUserBaseDefenseInsteadOfUserBaseAttack < Battle::Move
class Battle::Move::UseUserDefenseInsteadOfUserAttack < Battle::Move
def pbGetAttackStats(user, target)
return user.defense, user.stages[:DEFENSE] + 6
return user.defense, user.stages[:DEFENSE] + Battle::Battler::STAT_STAGE_MAXIMUM
end
end
@@ -1209,8 +1199,8 @@ end
#===============================================================================
class Battle::Move::UseTargetAttackInsteadOfUserAttack < Battle::Move
def pbGetAttackStats(user, target)
return target.spatk, target.stages[:SPECIAL_ATTACK] + 6 if specialMove?
return target.attack, target.stages[:ATTACK] + 6
return target.spatk, target.stages[:SPECIAL_ATTACK] + Battle::Battler::STAT_STAGE_MAXIMUM if specialMove?
return target.attack, target.stages[:ATTACK] + Battle::Battler::STAT_STAGE_MAXIMUM
end
end
@@ -1220,7 +1210,7 @@ end
#===============================================================================
class Battle::Move::UseTargetDefenseInsteadOfTargetSpDef < Battle::Move
def pbGetDefenseStats(user, target)
return target.defense, target.stages[:DEFENSE] + 6
return target.defense, target.stages[:DEFENSE] + Battle::Battler::STAT_STAGE_MAXIMUM
end
end
@@ -1269,14 +1259,14 @@ end
# (Chip Away, Darkest Lariat, Sacred Sword)
#===============================================================================
class Battle::Move::IgnoreTargetDefSpDefEvaStatStages < Battle::Move
def pbCalcAccuracyMultipliers(user, target, multipliers)
def pbCalcAccuracyModifiers(user, target, modifiers)
super
modifiers[:evasion_stage] = 0
end
def pbGetDefenseStats(user, target)
ret1, _ret2 = super
return ret1, 6 # Def/SpDef stat stage
return ret1, Battle::Battler::STAT_STAGE_MAXIMUM # Def/SpDef stat stage
end
end
@@ -1356,13 +1346,9 @@ class Battle::Move::TypeAndPowerDependOnUserBerry < Battle::Move
return false
end
# NOTE: The AI calls this method via pbCalcType, and this method returns a
# type assuming user has an item even though it might not. Since the AI
# won't want to use this move if the user has no item, though, perhaps
# this is good enough.
def pbBaseType(user)
item = user.item
ret = :NORMAL
item = user.item
if item
item.flags.each do |flag|
next if !flag[/^NaturalGift_(\w+)_(?:\d+)$/i]
@@ -1374,20 +1360,15 @@ class Battle::Move::TypeAndPowerDependOnUserBerry < Battle::Move
return ret
end
# This is a separate method so that the AI can use it as well
def pbNaturalGiftBaseDamage(heldItem)
if heldItem
GameData::Item.get(heldItem).flags.each do |flag|
def pbBaseDamage(baseDmg, user, target)
if user.item.id
GameData::Item.get(user.item.id).flags.each do |flag|
return [$~[1].to_i, 10].max if flag[/^NaturalGift_(?:\w+)_(\d+)$/i]
end
end
return 1
end
def pbBaseDamage(baseDmg, user, target)
return pbNaturalGiftBaseDamage(user.item.id)
end
def pbEndOfMoveUsageEffect(user, targets, numHits, switchedBattlers)
# NOTE: The item is consumed even if this move was Protected against or it
# missed. The item is not consumed if the target was switched out by
@@ -1426,12 +1407,9 @@ class Battle::Move::TypeDependsOnUserPlate < Battle::Move
def pbBaseType(user)
ret = :NORMAL
if user.itemActive?
@itemTypes.each do |item, itemType|
next if user.item != item
ret = itemType if GameData::Type.exists?(itemType)
break
end
if user.item_id && user.itemActive?
typ = @itemTypes[user.item_id]
ret = typ if typ && GameData::Type.exists?(typ)
end
return ret
end
@@ -1466,12 +1444,9 @@ class Battle::Move::TypeDependsOnUserMemory < Battle::Move
def pbBaseType(user)
ret = :NORMAL
if user.itemActive?
@itemTypes.each do |item, itemType|
next if user.item != item
ret = itemType if GameData::Type.exists?(itemType)
break
end
if user.item_id && user.itemActive?
typ = @itemTypes[user.item_id]
ret = typ if typ && GameData::Type.exists?(typ)
end
return ret
end
@@ -1493,12 +1468,9 @@ class Battle::Move::TypeDependsOnUserDrive < Battle::Move
def pbBaseType(user)
ret = :NORMAL
if user.itemActive?
@itemTypes.each do |item, itemType|
next if user.item != item
ret = itemType if GameData::Type.exists?(itemType)
break
end
if user.item_id && user.itemActive?
typ = @itemTypes[user.item_id]
ret = typ if typ && GameData::Type.exists?(typ)
end
return ret
end
@@ -1554,6 +1526,8 @@ class Battle::Move::TypeAndPowerDependOnWeather < Battle::Move
ret = :ROCK if GameData::Type.exists?(:ROCK)
when :Hail
ret = :ICE if GameData::Type.exists?(:ICE)
when :ShadowSky
ret = :NONE
end
return ret
end

View File

@@ -134,21 +134,7 @@ end
# Hits 2-5 times in a row. If the move does not fail, increases the user's Speed
# by 1 stage and decreases the user's Defense by 1 stage. (Scale Shot)
#===============================================================================
class Battle::Move::HitTwoToFiveTimesRaiseUserSpd1LowerUserDef1 < Battle::Move
def multiHitMove?; return true; end
def pbNumHits(user, targets)
hitChances = [
2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3,
4, 4, 4,
5, 5, 5
]
r = @battle.pbRandom(hitChances.length)
r = hitChances.length - 1 if user.hasActiveAbility?(:SKILLLINK)
return hitChances[r]
end
class Battle::Move::HitTwoToFiveTimesRaiseUserSpd1LowerUserDef1 < Battle::Move::HitTwoToFiveTimes
def pbEffectAfterAllHits(user, target)
return if target.damageState.unaffected
if user.pbCanLowerStatStage?(:DEFENSE, user, self)
@@ -291,11 +277,22 @@ end
# Special Defense and Speed by 2 stages each in the second turn. (Geomancy)
#===============================================================================
class Battle::Move::TwoTurnAttackRaiseUserSpAtkSpDefSpd2 < Battle::Move::TwoTurnMove
attr_reader :statUp
def initialize(battle, move)
super
@statUp = [:SPECIAL_ATTACK, 2, :SPECIAL_DEFENSE, 2, :SPEED, 2]
end
def pbMoveFailed?(user, targets)
return false if user.effects[PBEffects::TwoTurnAttack] # Charging turn
if !user.pbCanRaiseStatStage?(:SPECIAL_ATTACK, user, self) &&
!user.pbCanRaiseStatStage?(:SPECIAL_DEFENSE, user, self) &&
!user.pbCanRaiseStatStage?(:SPEED, user, self)
failed = true
(@statUp.length / 2).times do |i|
next if !user.pbCanRaiseStatStage?(@statUp[i * 2], user, self)
failed = false
break
end
if failed
@battle.pbDisplay(_INTL("{1}'s stats won't go any higher!", user.pbThis))
return true
end
@@ -309,9 +306,9 @@ class Battle::Move::TwoTurnAttackRaiseUserSpAtkSpDefSpd2 < Battle::Move::TwoTurn
def pbEffectGeneral(user)
return if !@damagingTurn
showAnim = true
[:SPECIAL_ATTACK, :SPECIAL_DEFENSE, :SPEED].each do |s|
next if !user.pbCanRaiseStatStage?(s, user, self)
if user.pbRaiseStatStage(s, 2, user, showAnim)
(@statUp.length / 2).times do |i|
next if !user.pbCanRaiseStatStage?(@statUp[i * 2], user, self)
if user.pbRaiseStatStage(@statUp[i * 2], @statUp[(i * 2) + 1], user, showAnim)
showAnim = false
end
end
@@ -323,13 +320,20 @@ end
# (Skull Bash)
#===============================================================================
class Battle::Move::TwoTurnAttackChargeRaiseUserDefense1 < Battle::Move::TwoTurnMove
attr_reader :statUp
def initialize(battle, move)
super
@statUp = [:DEFENSE, 1]
end
def pbChargingTurnMessage(user, targets)
@battle.pbDisplay(_INTL("{1} tucked in its head!", user.pbThis))
end
def pbChargingTurnEffect(user, target)
if user.pbCanRaiseStatStage?(:DEFENSE, user, self)
user.pbRaiseStatStage(:DEFENSE, 1, user)
if user.pbCanRaiseStatStage?(@statUp[0], user, self)
user.pbRaiseStatStage(@statUp[0], @statUp[1], user)
end
end
end

View File

@@ -109,6 +109,13 @@ end
# it). (Strength Sap)
#===============================================================================
class Battle::Move::HealUserByTargetAttackLowerTargetAttack1 < Battle::Move
attr_reader :statDown
def initialize(battle, move)
super
@statDown = [:ATTACK, 1]
end
def healingMove?; return true; end
def canMagicCoat?; return true; end
@@ -118,11 +125,12 @@ class Battle::Move::HealUserByTargetAttackLowerTargetAttack1 < Battle::Move
# has Contrary and is at +6" check too for symmetry. This move still
# works even if the stat stage cannot be changed due to an ability or
# other effect.
if !@battle.moldBreaker && target.hasActiveAbility?(:CONTRARY) &&
target.statStageAtMax?(:ATTACK)
@battle.pbDisplay(_INTL("But it failed!")) if show_message
return true
elsif target.statStageAtMin?(:ATTACK)
if !@battle.moldBreaker && target.hasActiveAbility?(:CONTRARY)
if target.statStageAtMax?(@statDown[0])
@battle.pbDisplay(_INTL("But it failed!")) if show_message
return true
end
elsif target.statStageAtMin?(@statDown[0])
@battle.pbDisplay(_INTL("But it failed!")) if show_message
return true
end
@@ -131,14 +139,15 @@ class Battle::Move::HealUserByTargetAttackLowerTargetAttack1 < Battle::Move
def pbEffectAgainstTarget(user, target)
# Calculate target's effective attack value
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]
max_stage = Battle::Battler::STAT_STAGE_MAXIMUM
stageMul = Battle::Battler::STAT_STAGE_MULTIPLIERS
stageDiv = Battle::Battler::STAT_STAGE_DIVISORS
atk = target.attack
atkStage = target.stages[:ATTACK] + 6
atkStage = target.stages[@statDown[0]] + max_stage
healAmt = (atk.to_f * stageMul[atkStage] / stageDiv[atkStage]).floor
# Reduce target's Attack stat
if target.pbCanLowerStatStage?(:ATTACK, user, self)
target.pbLowerStatStage(:ATTACK, 1, user)
if target.pbCanLowerStatStage?(@statDown[0], user, self)
target.pbLowerStatStage(@statDown[0], @statDown[1], user)
end
# Heal user
if target.hasActiveAbility?(:LIQUIDOOZE, true)

View File

@@ -137,7 +137,7 @@ class Battle::Move::RestoreUserConsumedItem < Battle::Move
def canSnatch?; return true; end
def pbMoveFailed?(user, targets)
if !user.recycleItem
if !user.recycleItem || user.item
@battle.pbDisplay(_INTL("But it failed!"))
return true
end
@@ -196,6 +196,8 @@ class Battle::Move::DestroyTargetBerryOrGem < Battle::Move
return if target.damageState.substitute || target.damageState.berryWeakened
return if !target.item || (!target.item.is_berry? &&
!(Settings::MECHANICS_GENERATION >= 6 && target.item.is_gem?))
return if target.unlosableItem?(target.item)
return if target.hasActiveAbility?(:STICKYHOLD) && !@battle.moldBreaker
item_name = target.itemName
target.pbRemoveItem
@battle.pbDisplay(_INTL("{1}'s {2} was incinerated!", target.pbThis, item_name))
@@ -317,6 +319,7 @@ class Battle::Move::UserConsumeBerryRaiseDefense2 < Battle::Move::StatUpMove
@battle.pbDisplay(_INTL("But it failed!"))
return true
end
# TODO: Should this return super? It can consume the berry at this point.
return super
end
@@ -383,7 +386,7 @@ class Battle::Move::UserConsumeTargetBerry < Battle::Move
def pbEffectAfterAllHits(user, target)
return if user.fainted? || target.fainted?
return if target.damageState.unaffected || target.damageState.substitute
return if !target.item || !target.item.is_berry?
return if !target.item || !target.item.is_berry? || target.unlosableItem?(target.item)
return if target.hasActiveAbility?(:STICKYHOLD) && !@battle.moldBreaker
item = target.item
itemName = target.itemName

View File

@@ -138,8 +138,16 @@ end
# user's Attack and Defense by 1 stage each. (Curse)
#===============================================================================
class Battle::Move::CurseTargetOrLowerUserSpd1RaiseUserAtkDef1 < Battle::Move
attr_reader :statUp, :statDown
def ignoresSubstitute?(user); return true; end
def initialize(battle, move)
super
@statUp = [:ATTACK, 1, :DEFENSE, 1]
@statDown = [:SPEED, 1]
end
def pbTarget(user)
if user.pbHasType?(:GHOST)
ghost_target = (Settings::MECHANICS_GENERATION >= 8) ? :RandomNearFoe : :NearFoe
@@ -150,9 +158,18 @@ class Battle::Move::CurseTargetOrLowerUserSpd1RaiseUserAtkDef1 < Battle::Move
def pbMoveFailed?(user, targets)
return false if user.pbHasType?(:GHOST)
if !user.pbCanLowerStatStage?(:SPEED, user, self) &&
!user.pbCanRaiseStatStage?(:ATTACK, user, self) &&
!user.pbCanRaiseStatStage?(:DEFENSE, user, self)
failed = true
(@statUp.length / 2).times do |i|
next if !user.pbCanRaiseStatStage?(@statUp[i * 2], user, self)
failed = false
break
end
(@statDown.length / 2).times do |i|
next if !user.pbCanLowerStatStage?(@statDown[i * 2], user, self)
failed = false
break
end
if failed
@battle.pbDisplay(_INTL("But it failed!"))
return true
end
@@ -170,15 +187,19 @@ class Battle::Move::CurseTargetOrLowerUserSpd1RaiseUserAtkDef1 < Battle::Move
def pbEffectGeneral(user)
return if user.pbHasType?(:GHOST)
# Non-Ghost effect
if user.pbCanLowerStatStage?(:SPEED, user, self)
user.pbLowerStatStage(:SPEED, 1, user)
showAnim = true
(@statDown.length / 2).times do |i|
next if !user.pbCanLowerStatStage?(@statDown[i * 2], user, self)
if user.pbLowerStatStage(@statDown[i * 2], @statDown[(i * 2) + 1], user, showAnim)
showAnim = false
end
end
showAnim = true
if user.pbCanRaiseStatStage?(:ATTACK, user, self)
showAnim = false if user.pbRaiseStatStage(:ATTACK, 1, user, showAnim)
end
if user.pbCanRaiseStatStage?(:DEFENSE, user, self)
user.pbRaiseStatStage(:DEFENSE, 1, user, showAnim)
(@statUp.length / 2).times do |i|
next if !user.pbCanRaiseStatStage?(@statUp[i * 2], user, self)
if user.pbRaiseStatStage(@statUp[i * 2], @statUp[(i * 2) + 1], user, showAnim)
showAnim = false
end
end
end
@@ -201,6 +222,8 @@ end
# Effect depends on the environment. (Secret Power)
#===============================================================================
class Battle::Move::EffectDependsOnEnvironment < Battle::Move
attr_reader :secretPower
def flinchingMove?; return [6, 10, 12].include?(@secretPower); end
def pbOnStartUse(user, targets)
@@ -250,6 +273,7 @@ class Battle::Move::EffectDependsOnEnvironment < Battle::Move
def pbEffectAfterAllHits(user, target)
return if target.fainted?
return if target.damageState.unaffected || target.damageState.substitute
return if user.hasActiveAbility?(:SHEERFORCE)
chance = pbAdditionalEffectChance(user, target)
return if @battle.pbRandom(100) >= chance
case @secretPower
@@ -668,6 +692,8 @@ end
# Uses the last move that was used. (Copycat)
#===============================================================================
class Battle::Move::UseLastMoveUsed < Battle::Move
attr_reader :moveBlacklist
def callsAnotherMove?; return true; end
def initialize(battle, move)
@@ -691,7 +717,7 @@ class Battle::Move::UseLastMoveUsed < Battle::Move
"ProtectUser", # Detect, Protect
"ProtectUserSideFromPriorityMoves", # Quick Guard # Not listed on Bulbapedia
"ProtectUserSideFromMultiTargetDamagingMoves", # Wide Guard # Not listed on Bulbapedia
"UserEnduresFaintingThisTurn", # Endure
"UserEnduresFaintingThisTurn", # Endure
"ProtectUserSideFromDamagingMovesIfUserFirstTurn", # Mat Block
"ProtectUserSideFromStatusMoves", # Crafty Shield # Not listed on Bulbapedia
"ProtectUserFromDamagingMovesKingsShield", # King's Shield
@@ -742,6 +768,7 @@ class Battle::Move::UseLastMoveUsed < Battle::Move
def pbMoveFailed?(user, targets)
if !@copied_move ||
!GameData::Move.exists?(@copied_move) ||
@moveBlacklist.include?(GameData::Move.get(@copied_move).function_code)
@battle.pbDisplay(_INTL("But it failed!"))
return true
@@ -763,7 +790,8 @@ class Battle::Move::UseLastMoveUsedByTarget < Battle::Move
def pbFailsAgainstTarget?(user, target, show_message)
if !target.lastRegularMoveUsed ||
GameData::Move.get(target.lastRegularMoveUsed).flags.none? { |f| f[/^CanMirrorMove$/i] }
!GameData::Move.exists?(target.lastRegularMoveUsed) ||
!GameData::Move.get(target.lastRegularMoveUsed).has_flag?("CanMirrorMove")
@battle.pbDisplay(_INTL("The mirror move failed!")) if show_message
return true
end
@@ -829,6 +857,8 @@ end
# Pokémon.
#===============================================================================
class Battle::Move::UseMoveDependingOnEnvironment < Battle::Move
attr_reader :npMove
def callsAnotherMove?; return true; end
def pbOnStartUse(user, targets)
@@ -983,6 +1013,8 @@ end
# Uses a random move known by any non-user Pokémon in the user's party. (Assist)
#===============================================================================
class Battle::Move::UseRandomMoveFromUserParty < Battle::Move
attr_reader :moveBlacklist
def callsAnotherMove?; return true; end
def initialize(battle, move)
@@ -1098,6 +1130,8 @@ end
# Uses a random move the user knows. Fails if user is not asleep. (Sleep Talk)
#===============================================================================
class Battle::Move::UseRandomUserMoveIfAsleep < Battle::Move
attr_reader :moveBlacklist
def usableWhenAsleep?; return true; end
def callsAnotherMove?; return true; end
@@ -1163,8 +1197,8 @@ class Battle::Move::UseRandomUserMoveIfAsleep < Battle::Move
end
#===============================================================================
# This round, reflects all moves with the "C" flag targeting the user back at
# their origin. (Magic Coat)
# This round, reflects all moves that can be Magic Coated which target the user
# or which have no target back at their origin. (Magic Coat)
#===============================================================================
class Battle::Move::BounceBackProblemCausingStatusMoves < Battle::Move
def pbEffectGeneral(user)
@@ -1174,7 +1208,7 @@ class Battle::Move::BounceBackProblemCausingStatusMoves < Battle::Move
end
#===============================================================================
# This round, snatches all used moves with the "D" flag. (Snatch)
# This round, snatches all used moves that can be Snatched. (Snatch)
#===============================================================================
class Battle::Move::StealAndUseBeneficialStatusMove < Battle::Move
def pbEffectGeneral(user)
@@ -1192,6 +1226,8 @@ end
# out. (Mimic)
#===============================================================================
class Battle::Move::ReplaceMoveThisBattleWithTargetLastMoveUsed < Battle::Move
attr_reader :moveBlacklist
def ignoresSubstitute?(user); return true; end
def initialize(battle, move)
@@ -1243,6 +1279,8 @@ end
# This move permanently turns into the last move used by the target. (Sketch)
#===============================================================================
class Battle::Move::ReplaceMoveWithTargetLastMoveUsed < Battle::Move
attr_reader :moveBlacklist
def ignoresSubstitute?(user); return true; end
def initialize(battle, move)

View File

@@ -3,7 +3,7 @@
#===============================================================================
class Battle::Move::FleeFromBattle < Battle::Move
def pbMoveFailed?(user, targets)
if !@battle.pbCanRun?(user.index)
if !@battle.pbCanRun?(user.index) || (user.wild? && user.allAllies.length > 0)
@battle.pbDisplay(_INTL("But it failed!"))
return true
end
@@ -23,7 +23,7 @@ end
class Battle::Move::SwitchOutUserStatusMove < Battle::Move
def pbMoveFailed?(user, targets)
if user.wild?
if !@battle.pbCanRun?(user.index)
if !@battle.pbCanRun?(user.index) || user.allAllies.length > 0
@battle.pbDisplay(_INTL("But it failed!"))
return true
end
@@ -59,7 +59,7 @@ end
#===============================================================================
# After inflicting damage, user switches out. Ignores trapping moves.
# (U-turn, Volt Switch)
# (Flip Turn, U-turn, Volt Switch)
#===============================================================================
class Battle::Move::SwitchOutUserDamagingMove < Battle::Move
def pbEndOfMoveUsageEffect(user, targets, numHits, switchedBattlers)
@@ -145,9 +145,9 @@ class Battle::Move::SwitchOutUserPassOnEffects < Battle::Move
end
#===============================================================================
# In wild battles, makes target flee. Fails if target is a higher level than the
# user.
# In trainer battles, target switches out.
# When used against a sole wild Pokémon, makes target flee and ends the battle;
# fails if target is a higher level than the user.
# When used against a trainer's Pokémon, target switches out.
# For status moves. (Roar, Whirlwind)
#===============================================================================
class Battle::Move::SwitchOutTargetStatusMove < Battle::Move
@@ -171,38 +171,40 @@ class Battle::Move::SwitchOutTargetStatusMove < Battle::Move
@battle.pbDisplay(_INTL("{1} anchored itself with its roots!", target.pbThis)) if show_message
return true
end
if !@battle.canRun
@battle.pbDisplay(_INTL("But it failed!")) if show_message
return true
end
if @battle.wildBattle? && target.level > user.level
@battle.pbDisplay(_INTL("But it failed!")) if show_message
return true
end
if @battle.trainerBattle?
if target.wild? && target.allAllies.length == 0 && @battle.canRun
# End the battle
if target.level > user.level
@battle.pbDisplay(_INTL("But it failed!")) if show_message
return true
end
elsif !target.wild?
# Switch target out
canSwitch = false
@battle.eachInTeamFromBattlerIndex(target.index) do |_pkmn, i|
next if !@battle.pbCanSwitchLax?(target.index, i)
canSwitch = true
break
canSwitch = @battle.pbCanSwitchIn?(target.index, i)
break if canSwitch
end
if !canSwitch
@battle.pbDisplay(_INTL("But it failed!")) if show_message
return true
end
else
@battle.pbDisplay(_INTL("But it failed!")) if show_message
return true
end
return false
end
def pbEffectGeneral(user)
@battle.decision = 3 if @battle.wildBattle? # Escaped from battle
def pbEffectAgainstTarget(user, target)
@battle.decision = 3 if target.wild? # Escaped from battle
end
def pbSwitchOutTargetEffect(user, targets, numHits, switched_battlers)
return if @battle.wildBattle? || !switched_battlers.empty?
return if !switched_battlers.empty?
return if user.fainted? || numHits == 0
targets.each do |b|
next if b.fainted? || b.damageState.unaffected
next if b.wild?
next if b.effects[PBEffects::Ingrain]
next if b.hasActiveAbility?(:SUCTIONCUPS) && !@battle.moldBreaker
newPkmn = @battle.pbGetReplacementPokemonIndex(b.index, true) # Random
@@ -218,24 +220,26 @@ class Battle::Move::SwitchOutTargetStatusMove < Battle::Move
end
#===============================================================================
# In wild battles, makes target flee. Fails if target is a higher level than the
# user.
# In trainer battles, target switches out.
# When used against a sole wild Pokémon, makes target flee and ends the battle;
# fails if target is a higher level than the user.
# When used against a trainer's Pokémon, target switches out.
# For damaging moves. (Circle Throw, Dragon Tail)
#===============================================================================
class Battle::Move::SwitchOutTargetDamagingMove < Battle::Move
def pbEffectAgainstTarget(user, target)
if @battle.wildBattle? && target.level <= user.level && @battle.canRun &&
if target.wild? && target.allAllies.length == 0 && @battle.canRun &&
target.level <= user.level &&
(target.effects[PBEffects::Substitute] == 0 || ignoresSubstitute?(user))
@battle.decision = 3
@battle.decision = 3 # Escaped from battle
end
end
def pbSwitchOutTargetEffect(user, targets, numHits, switched_battlers)
return if @battle.wildBattle? || !switched_battlers.empty?
return if !switched_battlers.empty?
return if user.fainted? || numHits == 0
targets.each do |b|
next if b.fainted? || b.damageState.unaffected || b.damageState.substitute
next if b.wild?
next if b.effects[PBEffects::Ingrain]
next if b.hasActiveAbility?(:SUCTIONCUPS) && !@battle.moldBreaker
newPkmn = @battle.pbGetReplacementPokemonIndex(b.index, true) # Random
@@ -306,7 +310,8 @@ end
#===============================================================================
# Target can no longer switch out or flee, as long as the user remains active.
# (Anchor Shot, Block, Mean Look, Spider Web, Spirit Shackle, Thousand Waves)
# Trapping is considered an additional effect for damaging moves.
# (Anchor Shot, Block, Mean Look, Spider Web, Spirit Shackle)
#===============================================================================
class Battle::Move::TrapTargetInBattle < Battle::Move
def canMagicCoat?; return true; end
@@ -339,6 +344,22 @@ class Battle::Move::TrapTargetInBattle < Battle::Move
end
end
#===============================================================================
# Target can no longer switch out or flee, as long as the user remains active.
# Trapping is not considered an additional effect. (Thousand Waves)
#===============================================================================
class Battle::Move::TrapTargetInBattleMainEffect < Battle::Move
def canMagicCoat?; return true; end
def pbEffectAgainstTarget(user, target)
return if target.fainted? || target.damageState.substitute
return if target.effects[PBEffects::MeanLook] >= 0
return if Settings::MORE_TYPE_EFFECTS && target.pbHasType?(:GHOST)
target.effects[PBEffects::MeanLook] = user.index
@battle.pbDisplay(_INTL("{1} can no longer escape!", target.pbThis))
end
end
#===============================================================================
# The target can no longer switch out or flee, while the user remains in battle.
# At the end of each round, the target's Defense and Special Defense are lowered
@@ -370,7 +391,7 @@ end
# fleeing. (Jaw Lock)
#===============================================================================
class Battle::Move::TrapUserAndTargetInBattle < Battle::Move
def pbAdditionalEffect(user, target)
def pbEffectAgainstTarget(user, target)
return if user.fainted? || target.fainted? || target.damageState.substitute
return if Settings::MORE_TYPE_EFFECTS && target.pbHasType?(:GHOST)
return if user.trappedInBattle? || target.trappedInBattle?
@@ -540,6 +561,8 @@ end
# The target uses its most recent move again. (Instruct)
#===============================================================================
class Battle::Move::TargetUsesItsLastUsedMoveAgain < Battle::Move
attr_reader :moveBlacklist
def ignoresSubstitute?(user); return true; end
def initialize(battle, move)
@@ -587,7 +610,8 @@ class Battle::Move::TargetUsesItsLastUsedMoveAgain < Battle::Move
end
def pbFailsAgainstTarget?(user, target, show_message)
if !target.lastRegularMoveUsed || !target.pbHasMove?(target.lastRegularMoveUsed)
if !target.lastRegularMoveUsed || !target.pbHasMove?(target.lastRegularMoveUsed) ||
!GameData::Move.exists?(target.lastRegularMoveUsed)
@battle.pbDisplay(_INTL("But it failed!")) if show_message
return true
end
@@ -658,8 +682,8 @@ end
# Target's last move used loses 3 PP. Damaging move. (Eerie Spell)
#===============================================================================
class Battle::Move::LowerPPOfTargetLastMoveBy3 < Battle::Move
def pbEffectAgainstTarget(user, target)
return if target.fainted?
def pbAdditionalEffect(user, target)
return if target.fainted? || target.damageState.substitute
last_move = target.pbGetMoveWithID(target.lastRegularMoveUsed)
return if !last_move || last_move.pp == 0 || last_move.total_pp <= 0
reduction = [3, last_move.pp].min
@@ -757,32 +781,34 @@ end
# For 4 rounds, the target must use the same move each round. (Encore)
#===============================================================================
class Battle::Move::DisableTargetUsingDifferentMove < Battle::Move
attr_reader :moveBlacklist
def ignoresSubstitute?(user); return true; end
def canMagicCoat?; return true; end
def initialize(battle, move)
super
@moveBlacklist = [
"DisableTargetUsingDifferentMove", # Encore
"DisableTargetUsingDifferentMove", # Encore
# Struggle
"Struggle", # Struggle
"Struggle", # Struggle
# Moves that affect the moveset
"ReplaceMoveThisBattleWithTargetLastMoveUsed", # Mimic
"ReplaceMoveWithTargetLastMoveUsed", # Sketch
"TransformUserIntoTarget", # Transform
"ReplaceMoveWithTargetLastMoveUsed", # Sketch
"TransformUserIntoTarget", # Transform
# Moves that call other moves (see also below)
"UseLastMoveUsedByTarget" # Mirror Move
"UseLastMoveUsedByTarget" # Mirror Move
]
if Settings::MECHANICS_GENERATION >= 7
@moveBlacklist += [
# Moves that call other moves
# "UseLastMoveUsedByTarget", # Mirror Move # See above
"UseLastMoveUsed", # Copycat
"UseMoveTargetIsAboutToUse", # Me First
"UseMoveDependingOnEnvironment", # Nature Power
"UseRandomUserMoveIfAsleep", # Sleep Talk
"UseRandomMoveFromUserParty", # Assist
"UseRandomMove" # Metronome
# "UseLastMoveUsedByTarget", # Mirror Move # See above
"UseLastMoveUsed", # Copycat
"UseMoveTargetIsAboutToUse", # Me First
"UseMoveDependingOnEnvironment", # Nature Power
"UseRandomUserMoveIfAsleep", # Sleep Talk
"UseRandomMoveFromUserParty", # Assist
"UseRandomMove" # Metronome
]
end
end
@@ -793,6 +819,7 @@ class Battle::Move::DisableTargetUsingDifferentMove < Battle::Move
return true
end
if !target.lastRegularMoveUsed ||
!GameData::Move.exists?(target.lastRegularMoveUsed) ||
@moveBlacklist.include?(GameData::Move.get(target.lastRegularMoveUsed).function_code)
@battle.pbDisplay(_INTL("But it failed!")) if show_message
return true

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 = msg + "\1"
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

@@ -410,7 +410,7 @@ class Battle::Scene
# Returns the animation ID to use for a given move/user. Returns nil if that
# move has no animations defined for it.
def pbFindMoveAnimDetails(move2anim, moveID, idxUser, hitNum = 0)
real_move_id = GameData::Move.get(moveID).id
real_move_id = GameData::Move.try_get(moveID)&.id || moveID
noFlip = false
if (idxUser & 1) == 0 # On player's side
anim = move2anim[0][real_move_id]
@@ -440,7 +440,7 @@ class Battle::Scene
moveType = moveData.type
moveKind = moveData.category
moveKind += 3 if target_data.num_targets > 1 || target_data.affects_foe_side
moveKind += 3 if moveKind == 2 && target_data.num_targets > 0
moveKind += 3 if moveData.status? && target_data.num_targets > 0
# [one target physical, one target special, user status,
# multiple targets physical, multiple targets special, non-user status]
typeDefaultAnim = {

View File

@@ -1,11 +1,12 @@
#===============================================================================
# Used when generating new trainers for battle challenges
#===============================================================================
class Battle::DebugSceneNoLogging
def initialize
@battle = nil
@lastCmd = [0, 0, 0, 0]
@lastMove = [0, 0, 0, 0]
class Battle::DebugSceneNoVisuals
def initialize(log_messages = false)
@battle = nil
@lastCmd = [0, 0, 0, 0]
@lastMove = [0, 0, 0, 0]
@log_messages = log_messages
end
# Called whenever the battle begins.
@@ -42,11 +43,24 @@ class Battle::DebugSceneNoLogging
def pbRefresh; end
def pbRefreshOne(idxBattler); end
def pbDisplayMessage(msg, brief = false); end
def pbDisplayMessage(msg, brief = false)
PBDebug.log_message(msg) if @log_messages
end
alias pbDisplay pbDisplayMessage
def pbDisplayPausedMessage(msg); end
def pbDisplayConfirmMessage(msg); return true; end
def pbShowCommands(msg, commands, defaultValue); return 0; end
def pbDisplayPausedMessage(msg)
PBDebug.log_message(msg) if @log_messages
end
def pbDisplayConfirmMessage(msg)
PBDebug.log_message(msg) if @log_messages
return true
end
def pbShowCommands(msg, commands, defaultValue)
PBDebug.log_message(msg) if @log_messages
return 0
end
def pbSendOutBattlers(sendOuts, startBattle = false); end
def pbRecall(idxBattler); end

View File

@@ -1,69 +1,163 @@
# AI skill levels:
# 0: Wild Pokémon
# 1-31: Basic trainer (young/inexperienced)
# 32-47: Some skill
# 48-99: High skill
# 100+: Best trainers (Gym Leaders, Elite Four, Champion)
# NOTE: A trainer's skill value can range from 0-255, but by default only four
# distinct skill levels exist. The skill value is typically the same as
# the trainer's base money value.
module PBTrainerAI
# Minimum skill level to be in each AI category.
def self.minimumSkill; return 1; end
def self.mediumSkill; return 32; end
def self.highSkill; return 48; end
def self.bestSkill; return 100; end
end
#===============================================================================
#
#===============================================================================
class Battle::AI
attr_reader :battle
attr_reader :trainer
attr_reader :battlers
attr_reader :user, :target, :move
def initialize(battle)
@battle = battle
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?(idxBattler)
battler = @battle.battlers[idxBattler]
if @battle.pbCanMegaEvolve?(idxBattler) # Simple "always should if possible"
PBDebug.log("[AI] #{battler.pbThis} (#{idxBattler}) 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]
@battlers.each { |b| b.refresh_battler if b }
end
#=============================================================================
# Choose an action
#=============================================================================
# Choose an action.
def pbDefaultChooseEnemyCommand(idxBattler)
return if pbEnemyShouldUseItem?(idxBattler)
return if pbEnemyShouldWithdraw?(idxBattler)
return if @battle.pbAutoFightMenu(idxBattler)
@battle.pbRegisterMegaEvolution(idxBattler) if pbEnemyShouldMegaEvolve?(idxBattler)
pbChooseMoves(idxBattler)
set_up(idxBattler)
ret = false
PBDebug.logonerr { ret = pbChooseToSwitchOut }
if ret
PBDebug.log("")
return
end
ret = false
PBDebug.logonerr { ret = pbChooseToUseItem }
if ret
PBDebug.log("")
return
end
if @battle.pbAutoFightMenu(idxBattler)
PBDebug.log("")
return
end
@battle.pbRegisterMegaEvolution(idxBattler) if pbEnemyShouldMegaEvolve?
choices = pbGetMoveScores
pbChooseMove(choices)
PBDebug.log("")
end
# Choose a replacement Pokémon (called directly from @battle, not part of
# action choosing). Must return the party index of a replacement Pokémon if
# possible.
def pbDefaultChooseNewEnemy(idxBattler)
set_up(idxBattler)
return choose_best_replacement_pokemon(idxBattler, true)
end
end
#===============================================================================
#
#===============================================================================
module Battle::AI::Handlers
MoveFailureCheck = HandlerHash.new
MoveFailureAgainstTargetCheck = HandlerHash.new
MoveEffectScore = HandlerHash.new
MoveEffectAgainstTargetScore = HandlerHash.new
MoveBasePower = HandlerHash.new
GeneralMoveScore = HandlerHash.new
GeneralMoveAgainstTargetScore = HandlerHash.new
ShouldSwitch = HandlerHash.new
ShouldNotSwitch = HandlerHash.new
AbilityRanking = AbilityHandlerHash.new
ItemRanking = ItemHandlerHash.new
def self.move_will_fail?(function_code, *args)
return MoveFailureCheck.trigger(function_code, *args) || false
end
def self.move_will_fail_against_target?(function_code, *args)
return MoveFailureAgainstTargetCheck.trigger(function_code, *args) || false
end
def self.apply_move_effect_score(function_code, score, *args)
ret = MoveEffectScore.trigger(function_code, score, *args)
return (ret.nil?) ? score : ret
end
def self.apply_move_effect_against_target_score(function_code, score, *args)
ret = MoveEffectAgainstTargetScore.trigger(function_code, score, *args)
return (ret.nil?) ? score : ret
end
def self.get_base_power(function_code, power, *args)
ret = MoveBasePower.trigger(function_code, power, *args)
return (ret.nil?) ? power : ret
end
def self.apply_general_move_score_modifiers(score, *args)
GeneralMoveScore.each do |id, score_proc|
new_score = score_proc.call(score, *args)
score = new_score if new_score
end
return score
end
def self.apply_general_move_against_target_score_modifiers(score, *args)
GeneralMoveAgainstTargetScore.each do |id, score_proc|
new_score = score_proc.call(score, *args)
score = new_score if new_score
end
return score
end
def self.should_switch?(*args)
ret = false
ShouldSwitch.each do |id, switch_proc|
ret ||= switch_proc.call(*args)
break if ret
end
return ret
end
def self.should_not_switch?(*args)
ret = false
ShouldNotSwitch.each do |id, switch_proc|
ret ||= switch_proc.call(*args)
break if ret
end
return ret
end
def self.modify_ability_ranking(ability, score, *args)
ret = AbilityRanking.trigger(ability, score, *args)
return (ret.nil?) ? score : ret
end
def self.modify_item_ranking(item, score, *args)
ret = ItemRanking.trigger(item, score, *args)
return (ret.nil?) ? score : ret
end
end

View File

@@ -1,170 +0,0 @@
class Battle::AI
#=============================================================================
# Decide whether the opponent should use an item on the Pokémon
#=============================================================================
def pbEnemyShouldUseItem?(idxBattler)
user = @battle.battlers[idxBattler]
item, idxTarget = pbEnemyItemToUse(idxBattler)
return false if !item
# Determine target of item (always the Pokémon choosing the action)
useType = GameData::Item.get(item).battle_use
if [1, 2, 3].include?(useType) # Use on Pokémon
idxTarget = @battle.battlers[idxTarget].pokemonIndex # Party Pokémon
end
# Register use of item
@battle.pbRegisterItem(idxBattler, item, idxTarget)
PBDebug.log("[AI] #{user.pbThis} (#{user.index}) will use item #{GameData::Item.get(item).name}")
return true
end
# NOTE: The AI will only consider using an item on the Pokémon it's currently
# choosing an action for.
def pbEnemyItemToUse(idxBattler)
return nil if !@battle.internalBattle
items = @battle.pbGetOwnerItems(idxBattler)
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]
pkmn = battler.pokemon
# Item categories
hpItems = {
:POTION => 20,
:SUPERPOTION => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 60 : 50,
:HYPERPOTION => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 120 : 200,
:MAXPOTION => 999,
:BERRYJUICE => 20,
:SWEETHEART => 20,
:FRESHWATER => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 30 : 50,
:SODAPOP => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 50 : 60,
:LEMONADE => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 70 : 80,
:MOOMOOMILK => 100,
:ORANBERRY => 10,
:SITRUSBERRY => battler.totalhp / 4,
:ENERGYPOWDER => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 60 : 50,
:ENERGYROOT => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 120 : 200
}
hpItems[:RAGECANDYBAR] = 20 if !Settings::RAGE_CANDY_BAR_CURES_STATUS_PROBLEMS
fullRestoreItems = [
:FULLRESTORE
]
oneStatusItems = [ # Preferred over items that heal all status problems
:AWAKENING, :CHESTOBERRY, :BLUEFLUTE,
:ANTIDOTE, :PECHABERRY,
:BURNHEAL, :RAWSTBERRY,
:PARALYZEHEAL, :PARLYZHEAL, :CHERIBERRY,
:ICEHEAL, :ASPEARBERRY
]
allStatusItems = [
:FULLHEAL, :LAVACOOKIE, :OLDGATEAU, :CASTELIACONE, :LUMIOSEGALETTE,
:SHALOURSABLE, :BIGMALASADA, :PEWTERCRUNCHIES, :LUMBERRY, :HEALPOWDER
]
allStatusItems.push(:RAGECANDYBAR) if Settings::RAGE_CANDY_BAR_CURES_STATUS_PROBLEMS
xItems = {
:XATTACK => [:ATTACK, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
:XATTACK2 => [:ATTACK, 2],
:XATTACK3 => [:ATTACK, 3],
:XATTACK6 => [:ATTACK, 6],
:XDEFENSE => [:DEFENSE, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
:XDEFENSE2 => [:DEFENSE, 2],
:XDEFENSE3 => [:DEFENSE, 3],
:XDEFENSE6 => [:DEFENSE, 6],
:XDEFEND => [:DEFENSE, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
:XDEFEND2 => [:DEFENSE, 2],
:XDEFEND3 => [:DEFENSE, 3],
:XDEFEND6 => [:DEFENSE, 6],
:XSPATK => [:SPECIAL_ATTACK, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
:XSPATK2 => [:SPECIAL_ATTACK, 2],
:XSPATK3 => [:SPECIAL_ATTACK, 3],
:XSPATK6 => [:SPECIAL_ATTACK, 6],
:XSPECIAL => [:SPECIAL_ATTACK, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
:XSPECIAL2 => [:SPECIAL_ATTACK, 2],
:XSPECIAL3 => [:SPECIAL_ATTACK, 3],
:XSPECIAL6 => [:SPECIAL_ATTACK, 6],
:XSPDEF => [:SPECIAL_DEFENSE, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
:XSPDEF2 => [:SPECIAL_DEFENSE, 2],
:XSPDEF3 => [:SPECIAL_DEFENSE, 3],
:XSPDEF6 => [:SPECIAL_DEFENSE, 6],
:XSPEED => [:SPEED, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
:XSPEED2 => [:SPEED, 2],
:XSPEED3 => [:SPEED, 3],
:XSPEED6 => [:SPEED, 6],
:XACCURACY => [:ACCURACY, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
:XACCURACY2 => [:ACCURACY, 2],
:XACCURACY3 => [:ACCURACY, 3],
:XACCURACY6 => [:ACCURACY, 6]
}
losthp = battler.totalhp - battler.hp
preferFullRestore = (battler.hp <= battler.totalhp * 2 / 3 &&
(battler.status != :NONE || battler.effects[PBEffects::Confusion] > 0))
# Find all usable items
usableHPItems = []
usableStatusItems = []
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)
# Log HP healing items
if losthp > 0
power = hpItems[i]
if power
usableHPItems.push([i, 5, power])
next
end
end
# Log Full Restores (HP healer and status curer)
if fullRestoreItems.include?(i)
usableHPItems.push([i, (preferFullRestore) ? 3 : 7, 999]) if losthp > 0
usableStatusItems.push([i, (preferFullRestore) ? 3 : 9]) if battler.status != :NONE ||
battler.effects[PBEffects::Confusion] > 0
next
end
# Log single status-curing items
if oneStatusItems.include?(i)
usableStatusItems.push([i, 5])
next
end
# Log Full Heal-type items
if allStatusItems.include?(i)
usableStatusItems.push([i, 7])
next
end
# Log stat-raising items
if xItems[i]
data = xItems[i]
usableXItems.push([i, battler.stages[data[0]], data[1]])
next
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] }
prevItem = nil
usableHPItems.each do |i|
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] }
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] }
prevItem = nil
usableXItems.each do |i|
break if prevItem && i[1] > prevItem[1]
return i[0], idxTarget if i[1] + i[2] >= 6
prevItem = i
end
return prevItem[0], idxTarget
end
return nil
end
end

View File

@@ -0,0 +1,675 @@
#===============================================================================
#
#===============================================================================
class Battle::AI
# Called by the AI's def pbDefaultChooseEnemyCommand, and by def pbChooseMove
# if the only moves known are bad ones (the latter forces a switch). Also
# aliased by the Battle Palace and Battle Arena.
def pbChooseToSwitchOut(force_switch = false)
return false if @user.wild?
return false if !@battle.pbCanSwitchOut?(@user.index)
# Don't switch if all foes are unable to do anything, e.g. resting after
# Hyper Beam, will Truant (i.e. free turn)
if @trainer.high_skill?
foe_can_act = false
each_foe_battler(@user.side) do |b, i|
next if !b.can_attack?
foe_can_act = true
break
end
return false if !foe_can_act
end
# Various calculations to decide whether to switch
if force_switch
PBDebug.log_ai("#{@user.name} is being forced to switch out")
else
return false if !@trainer.has_skill_flag?("ConsiderSwitching")
reserves = get_non_active_party_pokemon(@user.index)
return false if reserves.empty?
should_switch = Battle::AI::Handlers.should_switch?(@user, reserves, self, @battle)
if should_switch && @trainer.medium_skill?
should_switch = false if Battle::AI::Handlers.should_not_switch?(@user, reserves, self, @battle)
end
return false if !should_switch
end
# Want to switch; find the best replacement Pokémon
idxParty = choose_best_replacement_pokemon(@user.index, force_switch)
if idxParty < 0 # No good replacement Pokémon found
PBDebug.log(" => no good replacement Pokémon, will not switch after all")
return false
end
# Prefer using Baton Pass instead of switching
baton_pass = -1
@user.battler.eachMoveWithIndex do |m, i|
next if m.function != "SwitchOutUserPassOnEffects" # Baton Pass
next if !@battle.pbCanChooseMove?(@user.index, i, false)
baton_pass = i
break
end
if baton_pass >= 0 && @battle.pbRegisterMove(@user.index, baton_pass, false)
PBDebug.log(" => will use Baton Pass to switch out")
return true
elsif @battle.pbRegisterSwitch(@user.index, idxParty)
PBDebug.log(" => will switch with #{@battle.pbParty(@user.index)[idxParty].name}")
return true
end
return false
end
def get_non_active_party_pokemon(idxBattler)
ret = []
@battle.pbParty(idxBattler).each_with_index do |pkmn, i|
ret.push(pkmn) if @battle.pbCanSwitchIn?(idxBattler, i)
end
return ret
end
#-----------------------------------------------------------------------------
def choose_best_replacement_pokemon(idxBattler, mandatory = false)
# Get all possible replacement Pokémon
party = @battle.pbParty(idxBattler)
idxPartyStart, idxPartyEnd = @battle.pbTeamIndexRangeFromBattlerIndex(idxBattler)
reserves = []
party.each_with_index do |_pkmn, i|
next if !@battle.pbCanSwitchIn?(idxBattler, i)
if !mandatory # Not mandatory means choosing an action for the round
ally_will_switch_with_i = false
@battle.allSameSideBattlers(idxBattler).each do |b|
next if @battle.choices[b.index][0] != :SwitchOut || @battle.choices[b.index][1] != i
ally_will_switch_with_i = true
break
end
next if ally_will_switch_with_i
end
# Ignore ace if possible
if @trainer.has_skill_flag?("ReserveLastPokemon") && i == idxPartyEnd - 1
next if !mandatory || reserves.length > 0
end
reserves.push([i, 100])
break if @trainer.has_skill_flag?("UsePokemonInOrder") && reserves.length > 0
end
return -1 if reserves.length == 0
# Rate each possible replacement Pokémon
reserves.each_with_index do |reserve, i|
reserves[i][1] = rate_replacement_pokemon(idxBattler, party[reserve[0]], reserve[1])
end
reserves.sort! { |a, b| b[1] <=> a[1] } # Sort from highest to lowest rated
# Don't bother choosing to switch if all replacements are poorly rated
if @trainer.high_skill? && !mandatory
return -1 if reserves[0][1] < 100 # If best replacement rated at <100, don't switch
end
# Return the party index of the best rated replacement Pokémon
return reserves[0][0]
end
def rate_replacement_pokemon(idxBattler, pkmn, score)
pkmn_types = pkmn.types
entry_hazard_damage = calculate_entry_hazard_damage(pkmn, idxBattler & 1)
if entry_hazard_damage >= pkmn.hp
score -= 50 # pkmn will just faint
elsif entry_hazard_damage > 0
score -= 50 * entry_hazard_damage / pkmn.hp
end
if !pkmn.hasItem?(:HEAVYDUTYBOOTS) && !pokemon_airborne?(pkmn)
# Toxic Spikes
if @user.pbOwnSide.effects[PBEffects::ToxicSpikes] > 0
score -= 20 if pokemon_can_be_poisoned?(pkmn)
end
# Sticky Web
if @user.pbOwnSide.effects[PBEffects::ToxicSpikes]
score -= 15
end
end
# Predict effectiveness of foe's last used move against pkmn
each_foe_battler(@user.side) do |b, i|
next if !b.battler.lastMoveUsed
move_data = GameData::Move.try_get(b.battler.lastMoveUsed)
next if !move_data || move_data.status?
move_type = move_data.type
eff = Effectiveness.calculate(move_type, *pkmn_types)
score -= move_data.power * eff / 5
end
# Add power * effectiveness / 10 of all pkmn's moves to score
pkmn.moves.each do |m|
next if m.power == 0 || (m.pp == 0 && m.total_pp > 0)
@battle.battlers[idxBattler].allOpposing.each do |b|
next if pokemon_can_absorb_move?(b.pokemon, m, m.type)
bTypes = b.pbTypes(true)
score += m.power * Effectiveness.calculate(m.type, *bTypes) / 10
end
end
# Prefer if pkmn has lower HP and its position will be healed by Wish
position = @battle.positions[idxBattler]
if position.effects[PBEffects::Wish] > 0
amt = position.effects[PBEffects::WishAmount]
if pkmn.totalhp - pkmn.hp > amt * 2 / 3
score += 20 * [pkmn.totalhp - pkmn.hp, amt].min / pkmn.totalhp
end
end
# Prefer if user is about to faint from Perish Song
score += 20 if @user.effects[PBEffects::PerishSong] == 1
return score
end
def calculate_entry_hazard_damage(pkmn, side)
return 0 if pkmn.hasAbility?(:MAGICGUARD) || pkmn.hasItem?(:HEAVYDUTYBOOTS)
ret = 0
# Stealth Rock
if @battle.sides[side].effects[PBEffects::StealthRock] && GameData::Type.exists?(:ROCK)
pkmn_types = pkmn.types
eff = Effectiveness.calculate(:ROCK, *pkmn_types)
ret += pkmn.totalhp * eff / 8 if !Effectiveness.ineffective?(eff)
end
# Spikes
if @battle.sides[side].effects[PBEffects::Spikes] > 0 && !pokemon_airborne?(pkmn)
spikes_div = [8, 6, 4][@battle.sides[side].effects[PBEffects::Spikes] - 1]
ret += pkmn.totalhp / spikes_div
end
return ret
end
end
#===============================================================================
# Pokémon is about to faint because of Perish Song.
#===============================================================================
Battle::AI::Handlers::ShouldSwitch.add(:perish_song,
proc { |battler, reserves, ai, battle|
if battler.effects[PBEffects::PerishSong] == 1
PBDebug.log_ai("#{battler.name} wants to switch because it is about to faint from Perish Song")
next true
end
next false
}
)
#===============================================================================
# Pokémon will take a significant amount of damage at the end of this round, or
# it has an effect that causes it damage at the end of this round which it can
# remove by switching.
#===============================================================================
Battle::AI::Handlers::ShouldSwitch.add(:significant_eor_damage,
proc { |battler, reserves, ai, battle|
eor_damage = battler.rough_end_of_round_damage
# Switch if battler will take significant EOR damage
if eor_damage >= battler.hp / 2 || eor_damage >= battler.totalhp / 4
PBDebug.log_ai("#{battler.name} wants to switch because it will take a lot of EOR damage")
next true
end
# Switch to remove certain effects that cause the battler EOR damage
if ai.trainer.high_skill? && eor_damage > 0
if battler.effects[PBEffects::LeechSeed] >= 0 && ai.pbAIRandom(100) < 50
PBDebug.log_ai("#{battler.name} wants to switch to get rid of its Leech Seed")
next true
end
if battler.effects[PBEffects::Nightmare]
PBDebug.log_ai("#{battler.name} wants to switch to get rid of its Nightmare")
next true
end
if battler.effects[PBEffects::Curse]
PBDebug.log_ai("#{battler.name} wants to switch to get rid of its Curse")
next true
end
if battler.status == :POISON && battler.statusCount > 0 && !battler.has_active_ability?(:POISONHEAL)
poison_damage = battler.totalhp / 8
next_toxic_damage = battler.totalhp * (battler.effects[PBEffects::Toxic] + 1) / 16
if (battler.hp <= next_toxic_damage && battler.hp > poison_damage) ||
next_toxic_damage > poison_damage * 2
PBDebug.log_ai("#{battler.name} wants to switch to reduce toxic to regular poisoning")
next true
end
end
end
next false
}
)
#===============================================================================
# Pokémon can cure its status problem or heal some HP with its ability by
# switching out. Covers all abilities with an OnSwitchOut AbilityEffects
# handler.
#===============================================================================
Battle::AI::Handlers::ShouldSwitch.add(:cure_status_problem_by_switching_out,
proc { |battler, reserves, ai, battle|
next false if !battler.ability_active?
# Don't try to cure a status problem/heal a bit of HP if entry hazards will
# KO the battler if it switches back in
entry_hazard_damage = ai.calculate_entry_hazard_damage(battler.pokemon, battler.side)
next false if entry_hazard_damage >= battler.hp
# Check specific abilities
single_status_cure = {
:IMMUNITY => :POISON,
:INSOMNIA => :SLEEP,
:LIMBER => :PARALYSIS,
:MAGMAARMOR => :FROZEN,
:VITALSPIRIT => :SLEEP,
:WATERBUBBLE => :BURN,
:WATERVEIL => :BURN
}[battler.ability_id]
if battler.ability == :NATURALCURE || (single_status_cure && single_status_cure == battler.status)
# Cures status problem
next false if battler.wants_status_problem?(battler.status)
next false if battler.status == :SLEEP && battler.statusCount == 1 # Will wake up this round anyway
next false if entry_hazard_damage >= battler.totalhp / 4
# Don't bother curing a poisoning if Toxic Spikes will just re-poison the
# battler when it switches back in
if battler.status == :POISON && reserves.none? { |pkmn| pkmn.hasType?(:POISON) }
next false if battle.field.effects[PBEffects::ToxicSpikes] == 2
next false if battle.field.effects[PBEffects::ToxicSpikes] == 1 && battler.statusCount == 0
end
# Not worth curing status problems that still allow actions if at high HP
next false if battler.hp >= battler.totalhp / 2 && ![:SLEEP, :FROZEN].include?(battler.status)
if ai.pbAIRandom(100) < 70
PBDebug.log_ai("#{battler.name} wants to switch to cure its status problem with #{battler.ability.name}")
next true
end
elsif battler.ability == :REGENERATOR
# Not worth healing if battler would lose more HP from switching back in later
next false if entry_hazard_damage >= battler.totalhp / 3
# Not worth healing HP if already at high HP
next false if battler.hp >= battler.totalhp / 2
# Don't bother if a foe is at low HP and could be knocked out instead
if battler.check_for_move { |m| m.damagingMove? }
weak_foe = false
ai.each_foe_battler(battler.side) do |b, i|
weak_foe = true if b.hp < b.totalhp / 3
break if weak_foe
end
next false if weak_foe
end
if ai.pbAIRandom(100) < 70
PBDebug.log_ai("#{battler.name} wants to switch to heal with #{battler.ability.name}")
next true
end
end
next false
}
)
#===============================================================================
# Pokémon's position is about to be healed by Wish, and a reserve can benefit
# more from that healing than the Pokémon can.
#===============================================================================
Battle::AI::Handlers::ShouldSwitch.add(:wish_healing,
proc { |battler, reserves, ai, battle|
position = battle.positions[battler.index]
next false if position.effects[PBEffects::Wish] == 0
amt = position.effects[PBEffects::WishAmount]
next false if battler.totalhp - battler.hp >= amt * 2 / 3 # Want to heal itself instead
reserve_wants_healing_more = false
reserves.each do |pkmn|
entry_hazard_damage = ai.calculate_entry_hazard_damage(pkmn, battler.index & 1)
next if entry_hazard_damage >= pkmn.hp
reserve_wants_healing_more = (pkmn.totalhp - pkmn.hp - entry_hazard_damage >= amt * 2 / 3)
break if reserve_wants_healing_more
end
if reserve_wants_healing_more
PBDebug.log_ai("#{battler.name} wants to switch because Wish can heal a reserve more")
next true
end
next false
}
)
#===============================================================================
# Pokémon is yawning and can't do anything while asleep.
#===============================================================================
Battle::AI::Handlers::ShouldSwitch.add(:yawning,
proc { |battler, reserves, ai, battle|
# Yawning and can fall asleep because of it
next false if battler.effects[PBEffects::Yawn] == 0 || !battler.battler.pbCanSleepYawn?
# Doesn't want to be asleep (includes checking for moves usable while asleep)
next false if battler.wants_status_problem?(:SLEEP)
# Can't cure itself of sleep
if battler.ability_active?
next false if [:INSOMNIA, :NATURALCURE, :REGENERATOR, :SHEDSKIN].include?(battler.ability_id)
next false if battler.ability_id == :HYDRATION && [:Rain, :HeavyRain].include?(battler.battler.effectiveWeather)
end
next false if battler.has_active_item?([:CHESTOBERRY, :LUMBERRY]) && battler.battler.canConsumeBerry?
# Ally can't cure sleep
ally_can_heal = false
ai.each_ally(battler.index) do |b, i|
ally_can_heal = b.has_active_ability?(:HEALER)
break if ally_can_heal
end
next false if ally_can_heal
# Doesn't benefit from being asleep/isn't less affected by sleep
next false if battler.has_active_ability?([:EARLYBIRD, :MARVELSCALE])
# Not trapping another battler in battle
if ai.trainer.high_skill?
next false if ai.battlers.any? do |b|
b.effects[PBEffects::JawLock] == battler.index ||
b.effects[PBEffects::MeanLook] == battler.index ||
b.effects[PBEffects::Octolock] == battler.index ||
b.effects[PBEffects::TrappingUser] == battler.index
end
trapping = false
ai.each_foe_battler(battler.side) do |b, i|
next if b.ability_active? && Battle::AbilityEffects.triggerCertainSwitching(b.ability, b, battle)
next if b.item_active? && Battle::ItemEffects.triggerCertainSwitching(b.item, b, battle)
next if Settings::MORE_TYPE_EFFECTS && b.has_type?(:GHOST)
next if b.battler.trappedInBattle? # Relevant trapping effects are checked above
if battler.ability_active?
trapping = Battle::AbilityEffects.triggerTrappingByTarget(battler.ability, b, battler.battler, battle)
break if trapping
end
if battler.item_active?
trapping = Battle::ItemEffects.triggerTrappingByTarget(battler.item, b, battler.battler, battle)
break if trapping
end
end
next false if trapping
end
# Doesn't have sufficiently raised stats that would be lost by switching
next false if battler.stages.any? { |key, val| val >= 2 }
PBDebug.log_ai("#{battler.name} wants to switch because it is yawning and can't do anything while asleep")
next true
}
)
#===============================================================================
# Pokémon is asleep, won't wake up soon and can't do anything while asleep.
#===============================================================================
Battle::AI::Handlers::ShouldSwitch.add(:asleep,
proc { |battler, reserves, ai, battle|
# Asleep and won't wake up this round or next round
next false if battler.status != :SLEEP || battler.statusCount <= 2
# Doesn't want to be asleep (includes checking for moves usable while asleep)
next false if battler.wants_status_problem?(:SLEEP)
# Doesn't benefit from being asleep
next false if battler.has_active_ability?(:MARVELSCALE)
# Doesn't know Rest (if it does, sleep is expected, so don't apply this check)
next false if battler.check_for_move { |m| m.function == "HealUserFullyAndFallAsleep" }
# Not trapping another battler in battle
if ai.trainer.high_skill?
next false if ai.battlers.any? do |b|
b.effects[PBEffects::JawLock] == battler.index ||
b.effects[PBEffects::MeanLook] == battler.index ||
b.effects[PBEffects::Octolock] == battler.index ||
b.effects[PBEffects::TrappingUser] == battler.index
end
trapping = false
ai.each_foe_battler(battler.side) do |b, i|
next if b.ability_active? && Battle::AbilityEffects.triggerCertainSwitching(b.ability, b, battle)
next if b.item_active? && Battle::ItemEffects.triggerCertainSwitching(b.item, b, battle)
next if Settings::MORE_TYPE_EFFECTS && b.has_type?(:GHOST)
next if b.battler.trappedInBattle? # Relevant trapping effects are checked above
if battler.ability_active?
trapping = Battle::AbilityEffects.triggerTrappingByTarget(battler.ability, b, battler.battler, battle)
break if trapping
end
if battler.item_active?
trapping = Battle::ItemEffects.triggerTrappingByTarget(battler.item, b, battler.battler, battle)
break if trapping
end
end
next false if trapping
end
# Doesn't have sufficiently raised stats that would be lost by switching
next false if battler.stages.any? { |key, val| val >= 2 }
# 50% chance to not bother
next false if ai.pbAIRandom(100) < 50
PBDebug.log_ai("#{battler.name} wants to switch because it is asleep and can't do anything")
next true
}
)
#===============================================================================
# Pokémon can't use any moves and isn't Destiny Bonding/Grudging/hiding behind a
# Substitute.
#===============================================================================
Battle::AI::Handlers::ShouldSwitch.add(:battler_is_useless,
proc { |battler, reserves, ai, battle|
next false if !ai.trainer.medium_skill?
next false if battler.turnCount < 2 # Just switched in, give it a chance
next false if battle.pbCanChooseAnyMove?(battler.index)
next false if battler.effects[PBEffects::DestinyBond] || battler.effects[PBEffects::Grudge]
if battler.effects[PBEffects::Substitute]
hidden_behind_substitute = true
ai.each_foe_battler(battler.side) do |b, i|
next if !b.check_for_move { |m| m.ignoresSubstitute?(b.battler) }
hidden_behind_substitute = false
break
end
next false if hidden_behind_substitute
end
PBDebug.log_ai("#{battler.name} wants to switch because it can't do anything")
next true
}
)
#===============================================================================
# Pokémon can't do anything to any foe because its ability absorbs all damage
# the Pokémon can deal out.
#===============================================================================
Battle::AI::Handlers::ShouldSwitch.add(:foe_absorbs_all_moves_with_its_ability,
proc { |battler, reserves, ai, battle|
next false if battler.battler.turnCount < 2 # Don't switch out too quickly
next false if battler.battler.hasMoldBreaker?
# Check if battler can damage any of its foes
can_damage_foe = false
ai.each_foe_battler(battler.side) do |b, i|
if ai.trainer.high_skill? && b.rough_end_of_round_damage > 0
can_damage_foe = true # Foe is being damaged already
break
end
# Check for battler's moves that can damage the foe (b)
battler.battler.eachMove do |move|
next if move.statusMove?
if ["IgnoreTargetAbility",
"CategoryDependsOnHigherDamageIgnoreTargetAbility"].include?(move.function)
can_damage_foe = true
break
end
if !ai.pokemon_can_absorb_move?(b, move, move.pbCalcType(battler.battler))
can_damage_foe = true
break
end
end
break if can_damage_foe
end
next false if can_damage_foe
# Check if a reserve could damage any foe; only switch if one could
reserve_can_damage_foe = false
reserves.each do |pkmn|
ai.each_foe_battler(battler.side) do |b, i|
# Check for reserve's moves that can damage the foe (b)
pkmn.moves.each do |move|
next if move.status_move?
if ["IgnoreTargetAbility",
"CategoryDependsOnHigherDamageIgnoreTargetAbility"].include?(move.function_code)
reserve_can_damage_foe = true
break
end
if !ai.pokemon_can_absorb_move?(b, move, move.type)
reserve_can_damage_foe = true
break
end
end
break if reserve_can_damage_foe
end
break if reserve_can_damage_foe
end
next false if !reserve_can_damage_foe
PBDebug.log_ai("#{battler.name} wants to switch because it can't damage the foe(s)")
next true
}
)
#===============================================================================
# Pokémon doesn't have an ability that makes it immune to a foe's move, but a
# reserve does (see def pokemon_can_absorb_move?). The foe's move is chosen
# randomly, or is their most powerful move if the trainer's skill level is good
# enough.
#===============================================================================
Battle::AI::Handlers::ShouldSwitch.add(:absorb_foe_move,
proc { |battler, reserves, ai, battle|
next false if !ai.trainer.medium_skill?
# Not worth it if the battler is evasive enough
next false if battler.stages[:EVASION] >= 3
# Not worth it if abilities are being negated
next false if battle.pbCheckGlobalAbility(:NEUTRALIZINGGAS)
# Get the foe move with the highest power (or a random damaging move)
foe_moves = []
ai.each_foe_battler(battler.side) do |b, i|
b.moves.each do |move|
next if move.statusMove?
m_power = move.power
m_power = 100 if move.is_a?(Battle::Move::OHKO)
m_type = move.pbCalcType(b.battler)
foe_moves.push([m_power, m_type, move])
end
end
next false if foe_moves.empty?
if ai.trainer.high_skill?
foe_moves.sort! { |a, b| a[0] <=> b[0] } # Highest power move
chosen_move = foe_moves.last
else
chosen_move = foe_moves[ai.pbAIRandom(foe_moves.length)] # Random move
end
# Get the chosen move's information
move_power = chosen_move[0]
move_type = chosen_move[1]
move = chosen_move[2]
# Don't bother if the foe's best move isn't too strong
next false if move_power < 70
# Check battler for absorbing ability
next false if ai.pokemon_can_absorb_move?(battler, move, move_type)
# battler can't absorb move; find a party Pokémon that can
if reserves.any? { |pkmn| ai.pokemon_can_absorb_move?(pkmn, move, move_type) }
next false if ai.pbAIRandom(100) < 70
PBDebug.log_ai("#{battler.name} wants to switch because it can't absorb a foe's move but a reserve can")
next true
end
next false
}
)
#===============================================================================
# Sudden Death rule (at the end of each round, if one side has more able Pokémon
# than the other side, that side wins). Avoid fainting at all costs.
# NOTE: This rule isn't used anywhere.
#===============================================================================
Battle::AI::Handlers::ShouldSwitch.add(:sudden_death,
proc { |battler, reserves, ai, battle|
next false if !battle.rules["suddendeath"] || battler.turnCount == 0
if battler.hp <= battler.totalhp / 2
threshold = 100 * (battler.totalhp - battler.hp) / battler.totalhp
if ai.pbAIRandom(100) < threshold
PBDebug.log_ai("#{battler.name} wants to switch to avoid being KO'd and losing because of the sudden death rule")
next true
end
end
next false
}
)
#===============================================================================
# Pokémon is within 5 levels of the foe, and foe's last move was super-effective
# and powerful.
#===============================================================================
Battle::AI::Handlers::ShouldSwitch.add(:high_damage_from_foe,
proc { |battler, reserves, ai, battle|
next false if !ai.trainer.high_skill?
next false if battler.hp >= battler.totalhp / 2
big_threat = false
ai.each_foe_battler(battler.side) do |b, i|
next if (b.level - battler.level).abs > 5
next if !b.battler.lastMoveUsed || !GameData::Move.exists?(b.battler.lastMoveUsed)
move_data = GameData::Move.get(b.battler.lastMoveUsed)
next if move_data.status?
eff = battler.effectiveness_of_type_against_battler(move_data.type, b)
next if !Effectiveness.super_effective?(eff) || move_data.power < 70
switch_chance = (move_data.power > 90) ? 50 : 25
big_threat = (ai.pbAIRandom(100) < switch_chance)
break if big_threat
end
if big_threat
PBDebug.log_ai("#{battler.name} wants to switch because a foe has a powerful super-effective move")
next true
end
next false
}
)
#===============================================================================
#===============================================================================
#===============================================================================
#===============================================================================
# Don't bother switching if the battler will just faint from entry hazard damage
# upon switching back in, and if no reserve can remove the entry hazard(s).
# Switching out in this case means the battler becomes unusable, so it might as
# well stick around instead and do as much as it can.
#===============================================================================
Battle::AI::Handlers::ShouldNotSwitch.add(:lethal_entry_hazards,
proc { |battler, reserves, ai, battle|
next false if battle.rules["suddendeath"]
# Check whether battler will faint from entry hazard(s)
entry_hazard_damage = ai.calculate_entry_hazard_damage(battler.pokemon, battler.side)
next false if entry_hazard_damage < battler.hp
# Check for Rapid Spin
reserve_can_remove_hazards = false
reserves.each do |pkmn|
pkmn.moves.each do |move|
reserve_can_remove_hazards = (move.function_code == "RemoveUserBindingAndEntryHazards")
break if reserve_can_remove_hazards
end
break if reserve_can_remove_hazards
end
next false if reserve_can_remove_hazards
PBDebug.log_ai("#{battler.name} won't switch after all because it will faint from entry hazards if it switches back in")
next true
}
)
#===============================================================================
# Don't bother switching (50% chance) if the battler knows a super-effective
# move.
#===============================================================================
Battle::AI::Handlers::ShouldNotSwitch.add(:battler_has_super_effective_move,
proc { |battler, reserves, ai, battle|
next false if battler.effects[PBEffects::PerishSong] == 1
next false if battle.rules["suddendeath"]
has_super_effective_move = false
battler.battler.eachMove do |move|
next if move.pp == 0 && move.total_pp > 0
next if move.statusMove?
# NOTE: Ideally this would ignore moves that are unusable, but that would
# be too complicated to implement.
move_type = move.type
move_type = move.pbCalcType(battler.battler) if ai.trainer.medium_skill?
ai.each_foe_battler(battler.side) do |b|
# NOTE: Ideally this would ignore foes that move cannot target, but that
# is complicated enough to implement that I'm not bothering. It's
# also rare that it would matter.
eff = b.effectiveness_of_type_against_battler(move_type, battler, move)
has_super_effective_move = Effectiveness.super_effective?(eff)
break if has_super_effective_move
end
break if has_super_effective_move
end
if has_super_effective_move && ai.pbAIRandom(100) < 50
PBDebug.log_ai("#{battler.name} won't switch after all because it has a super-effective move")
next true
end
next false
}
)
#===============================================================================
# Don't bother switching if the battler has 4 or more positive stat stages.
# Negative stat stages are ignored.
#===============================================================================
Battle::AI::Handlers::ShouldNotSwitch.add(:battler_has_very_raised_stats,
proc { |battler, reserves, ai, battle|
next false if battle.rules["suddendeath"]
stat_raises = 0
battler.stages.each_value { |val| stat_raises += val if val > 0 }
if stat_raises >= 4
PBDebug.log_ai("#{battler.name} won't switch after all because it has a lot of raised stats")
next true
end
next false
}
)

View File

@@ -1,181 +0,0 @@
class Battle::AI
#=============================================================================
# Decide whether the opponent should switch Pokémon
#=============================================================================
def pbEnemyShouldWithdraw?(idxBattler)
return pbEnemyShouldWithdrawEx?(idxBattler, false)
end
def pbEnemyShouldWithdrawEx?(idxBattler, forceSwitch)
return false if @battle.wildBattle?
shouldSwitch = forceSwitch
batonPass = -1
moveType = nil
skill = @battle.pbGetOwnerFromBattlerIndex(idxBattler).skill_level || 0
battler = @battle.battlers[idxBattler]
# If Pokémon is within 6 levels of the foe, and foe's last move was
# super-effective and powerful
if !shouldSwitch && battler.turnCount > 0 && skill >= PBTrainerAI.highSkill
target = battler.pbDirectOpposing(true)
if !target.fainted? && target.lastMoveUsed &&
(target.level - battler.level).abs <= 6
moveData = GameData::Move.get(target.lastMoveUsed)
moveType = moveData.type
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)
end
end
end
# Pokémon can't do anything (must have been in battle for at least 5 rounds)
if !@battle.pbCanChooseAnyMove?(idxBattler) &&
battler.turnCount && battler.turnCount >= 5
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)
batonPass = i
break
end
end
# Pokémon will faint because of bad poisoning at the end of this round, but
# would survive at least one more round if it were regular poisoning instead
if battler.status == :POISON && battler.statusCount > 0 &&
skill >= PBTrainerAI.highSkill
toxicHP = battler.totalhp / 16
nextToxicHP = toxicHP * (battler.effects[PBEffects::Toxic] + 1)
if battler.hp <= nextToxicHP && battler.hp > toxicHP * 2 && pbAIRandom(100) < 80
shouldSwitch = true
end
end
# Pokémon is Encored into an unfavourable move
if battler.effects[PBEffects::Encore] > 0 && skill >= PBTrainerAI.mediumSkill
idxEncoredMove = battler.pbEncoredMoveIndex
if idxEncoredMove >= 0
scoreSum = 0
scoreCount = 0
battler.allOpposing.each do |b|
scoreSum += pbGetMoveScore(battler.moves[idxEncoredMove], battler, b, skill)
scoreCount += 1
end
if scoreCount > 0 && scoreSum / scoreCount <= 20 && pbAIRandom(100) < 80
shouldSwitch = true
end
end
end
# If there is a single foe and it is resting after Hyper Beam or is
# Truanting (i.e. free turn)
if @battle.pbSideSize(battler.index + 1) == 1 &&
!battler.pbDirectOpposing.fainted? && skill >= PBTrainerAI.highSkill
opp = battler.pbDirectOpposing
if (opp.effects[PBEffects::HyperBeam] > 0 ||
(opp.hasActiveAbility?(:TRUANT) && opp.effects[PBEffects::Truant])) && pbAIRandom(100) < 80
shouldSwitch = false
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
shouldSwitch = true
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
shouldSwitch = true
end
if shouldSwitch
list = []
idxPartyStart, idxPartyEnd = @battle.pbTeamIndexRangeFromBattlerIndex(idxBattler)
@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)
# 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
# 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]
next if pkmn.hp <= pkmn.totalhp / spikesDmg &&
!pkmn.hasType?(:FLYING) && !pkmn.hasActiveAbility?(:LEVITATE)
end
end
# moveType is the type of the target's last used move
if moveType && Effectiveness.ineffective?(pbCalcTypeMod(moveType, battler, battler))
weight = 65
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))
weight = 40
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
else
list.push(i) # put this Pokemon last
end
end
if list.length > 0
if batonPass >= 0 && @battle.pbRegisterMove(idxBattler, batonPass, false)
PBDebug.log("[AI] #{battler.pbThis} (#{idxBattler}) will use Baton Pass to avoid Perish Song")
return true
end
if @battle.pbRegisterSwitch(idxBattler, list[0])
PBDebug.log("[AI] #{battler.pbThis} (#{idxBattler}) will switch with " +
@battle.pbParty(idxBattler)[list[0]].name)
return true
end
end
end
return false
end
#=============================================================================
# Choose a replacement Pokémon
#=============================================================================
def pbDefaultChooseNewEnemy(idxBattler, party)
enemies = []
idxPartyStart, idxPartyEnd = @battle.pbTeamIndexRangeFromBattlerIndex(idxBattler)
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)
end
return -1 if enemies.length == 0
return pbChooseBestNewEnemy(idxBattler, party, enemies)
end
def pbChooseBestNewEnemy(idxBattler, party, enemies)
return -1 if !enemies || enemies.length == 0
best = -1
bestSum = 0
enemies.each do |i|
pkmn = party[i]
sum = 0
pkmn.moves.each do |m|
next if m.base_damage == 0
@battle.battlers[idxBattler].allOpposing.each do |b|
bTypes = b.pbTypes(true)
sum += Effectiveness.calculate(m.type, bTypes[0], bTypes[1], bTypes[2])
end
end
if best == -1 || sum > bestSum
best = i
bestSum = sum
end
end
return best
end
end

Some files were not shown because too many files have changed in this diff Show More