mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-07 13:15:01 +00:00
Merge branch 'ai' into dev
This commit is contained in:
69
AI references/AI backup/001_PokeBattle_AI.rb
Normal file
69
AI references/AI backup/001_PokeBattle_AI.rb
Normal 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
|
||||
182
AI references/AI backup/002_AI_Item.rb
Normal file
182
AI references/AI backup/002_AI_Item.rb
Normal 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
|
||||
182
AI references/AI backup/003_AI_Switch.rb
Normal file
182
AI references/AI backup/003_AI_Switch.rb
Normal 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
|
||||
286
AI references/AI backup/004_AI_Move.rb
Normal file
286
AI references/AI backup/004_AI_Move.rb
Normal 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
|
||||
838
AI references/AI backup/005_AI_Move_EffectScores_000-03F.rb
Normal file
838
AI references/AI backup/005_AI_Move_EffectScores_000-03F.rb
Normal 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
|
||||
797
AI references/AI backup/006_AI_Move_EffectScores_040-07F.rb
Normal file
797
AI references/AI backup/006_AI_Move_EffectScores_040-07F.rb
Normal 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
|
||||
228
AI references/AI backup/007_AI_Move_EffectScores_080-0BF.rb
Normal file
228
AI references/AI backup/007_AI_Move_EffectScores_080-0BF.rb
Normal 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
|
||||
375
AI references/AI backup/008_AI_Move_EffectScores_0C0-0FF.rb
Normal file
375
AI references/AI backup/008_AI_Move_EffectScores_0C0-0FF.rb
Normal 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
|
||||
498
AI references/AI backup/009_AI_Move_EffectScores_100-13F.rb
Normal file
498
AI references/AI backup/009_AI_Move_EffectScores_100-13F.rb
Normal 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
|
||||
409
AI references/AI backup/010_AI_Move_EffectScores_140-17F.rb
Normal file
409
AI references/AI backup/010_AI_Move_EffectScores_140-17F.rb
Normal 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
|
||||
661
AI references/AI backup/011_AI_Move_Utilities.rb
Normal file
661
AI references/AI backup/011_AI_Move_Utilities.rb
Normal 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
|
||||
108
AI references/AI backup/100_PBStuff.rb
Normal file
108
AI references/AI backup/100_PBStuff.rb
Normal 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
|
||||
154
AI references/AI backup/101_Reborn AI main.rb
Normal file
154
AI references/AI backup/101_Reborn AI main.rb
Normal 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
|
||||
495
AI references/AI backup/102_Reborn item.rb
Normal file
495
AI references/AI backup/102_Reborn item.rb
Normal 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
|
||||
2314
AI references/AI backup/103_Reborn switch.rb
Normal file
2314
AI references/AI backup/103_Reborn switch.rb
Normal file
File diff suppressed because it is too large
Load Diff
832
AI references/AI backup/104_Reborn move.rb
Normal file
832
AI references/AI backup/104_Reborn move.rb
Normal 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
|
||||
4690
AI references/AI backup/105_Reborn effect scores 000-03F.rb
Normal file
4690
AI references/AI backup/105_Reborn effect scores 000-03F.rb
Normal file
File diff suppressed because it is too large
Load Diff
2077
AI references/AI backup/106_Reborn effect scores 040-07F.rb
Normal file
2077
AI references/AI backup/106_Reborn effect scores 040-07F.rb
Normal file
File diff suppressed because it is too large
Load Diff
1524
AI references/AI backup/107_Reborn effect scores 080-0BF.rb
Normal file
1524
AI references/AI backup/107_Reborn effect scores 080-0BF.rb
Normal file
File diff suppressed because it is too large
Load Diff
3836
AI references/AI backup/108_Reborn effect scores 0C0-0FF.rb
Normal file
3836
AI references/AI backup/108_Reborn effect scores 0C0-0FF.rb
Normal file
File diff suppressed because it is too large
Load Diff
2284
AI references/AI backup/109_Reborn effect scores 100-13F.rb
Normal file
2284
AI references/AI backup/109_Reborn effect scores 100-13F.rb
Normal file
File diff suppressed because it is too large
Load Diff
2715
AI references/AI backup/110_Reborn effect scores 140-17F.rb
Normal file
2715
AI references/AI backup/110_Reborn effect scores 140-17F.rb
Normal file
File diff suppressed because it is too large
Load Diff
2563
AI references/AI backup/111_Reborn utilities.rb
Normal file
2563
AI references/AI backup/111_Reborn utilities.rb
Normal file
File diff suppressed because it is too large
Load Diff
121
AI references/AI backup/112_Reborn memory.rb
Normal file
121
AI references/AI backup/112_Reborn memory.rb
Normal 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
|
||||
2038
AI references/AI backup/113_Reborn unique.rb
Normal file
2038
AI references/AI backup/113_Reborn unique.rb
Normal file
File diff suppressed because it is too large
Load Diff
308
AI references/AI backup/114_Reborn roles.rb
Normal file
308
AI references/AI backup/114_Reborn roles.rb
Normal 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
|
||||
1316
AI references/Marin's AI/AI_BattlerProjection.rb
Normal file
1316
AI references/Marin's AI/AI_BattlerProjection.rb
Normal file
File diff suppressed because it is too large
Load Diff
209
AI references/Marin's AI/AI_Compatibility.rb
Normal file
209
AI references/Marin's AI/AI_Compatibility.rb
Normal 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}, that’s 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
|
||||
}
|
||||
)
|
||||
133
AI references/Marin's AI/AI_Main.rb
Normal file
133
AI references/Marin's AI/AI_Main.rb
Normal 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
|
||||
1491
AI references/Marin's AI/AI_ScoreHandler.rb
Normal file
1491
AI references/Marin's AI/AI_ScoreHandler.rb
Normal file
File diff suppressed because it is too large
Load Diff
68
AI references/Marin's AI/AI_Side.rb
Normal file
68
AI references/Marin's AI/AI_Side.rb
Normal 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
|
||||
92
AI references/Old AI rewrite attempt/001_PokeBattle_AI.rb
Normal file
92
AI references/Old AI rewrite attempt/001_PokeBattle_AI.rb
Normal 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
|
||||
170
AI references/Old AI rewrite attempt/002_AI_Item.rb
Normal file
170
AI references/Old AI rewrite attempt/002_AI_Item.rb
Normal 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
|
||||
178
AI references/Old AI rewrite attempt/003_AI_Switch.rb
Normal file
178
AI references/Old AI rewrite attempt/003_AI_Switch.rb
Normal 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
|
||||
497
AI references/Old AI rewrite attempt/004_AI_Move.rb
Normal file
497
AI references/Old AI rewrite attempt/004_AI_Move.rb
Normal 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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
725
AI references/Old AI rewrite attempt/012_AI_Move_Utilities.rb
Normal file
725
AI references/Old AI rewrite attempt/012_AI_Move_Utilities.rb
Normal 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
|
||||
165
AI references/Old AI rewrite attempt/013_AI_roles.rb
Normal file
165
AI references/Old AI rewrite attempt/013_AI_roles.rb
Normal 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
|
||||
@@ -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
|
||||
110
AI references/Old AI rewrite attempt/100_PBStuff.rb
Normal file
110
AI references/Old AI rewrite attempt/100_PBStuff.rb
Normal 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
|
||||
165
AI references/Old AI rewrite attempt/101_Reborn AI main.rb
Normal file
165
AI references/Old AI rewrite attempt/101_Reborn AI main.rb
Normal 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
|
||||
495
AI references/Old AI rewrite attempt/102_Reborn item.rb
Normal file
495
AI references/Old AI rewrite attempt/102_Reborn item.rb
Normal 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
|
||||
2071
AI references/Old AI rewrite attempt/103_Reborn switch.rb
Normal file
2071
AI references/Old AI rewrite attempt/103_Reborn switch.rb
Normal file
File diff suppressed because it is too large
Load Diff
1017
AI references/Old AI rewrite attempt/104_Reborn move.rb
Normal file
1017
AI references/Old AI rewrite attempt/104_Reborn move.rb
Normal file
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
1319
AI references/Old AI rewrite attempt/111_Reborn utilities.rb
Normal file
1319
AI references/Old AI rewrite attempt/111_Reborn utilities.rb
Normal file
File diff suppressed because it is too large
Load Diff
121
AI references/Old AI rewrite attempt/112_Reborn memory.rb
Normal file
121
AI references/Old AI rewrite attempt/112_Reborn memory.rb
Normal 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
|
||||
974
AI references/Old AI rewrite attempt/113_Reborn unique.rb
Normal file
974
AI references/Old AI rewrite attempt/113_Reborn unique.rb
Normal 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
|
||||
327
AI references/Old AI rewrite attempt/114_Reborn roles.rb
Normal file
327
AI references/Old AI rewrite attempt/114_Reborn roles.rb
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
675
Data/Scripts/011_Battle/005_AI/002_AI_Switch.rb
Normal file
675
Data/Scripts/011_Battle/005_AI/002_AI_Switch.rb
Normal 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
|
||||
}
|
||||
)
|
||||
@@ -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
Reference in New Issue
Block a user