Files
infinitefusion-e18/AI references/Old AI rewrite attempt/003_AI_Switch.rb

179 lines
6.9 KiB
Ruby

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