mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-06 06:01:46 +00:00
Added script to make two random AI trainers battle, tweaked battle messages when logged, fixed typos
This commit is contained in:
@@ -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
|
||||
@@ -23,7 +25,15 @@ module PBDebug
|
||||
|
||||
def self.log(msg)
|
||||
if $DEBUG && $INTERNAL
|
||||
echoln msg
|
||||
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
|
||||
@@ -32,7 +42,7 @@ module PBDebug
|
||||
def self.log_message(msg)
|
||||
if $DEBUG && $INTERNAL
|
||||
msg = "\"" + msg + "\""
|
||||
echoln Console.markup_style(msg, text: :dark_gray)
|
||||
echoln Console.markup_style(msg.gsub("%", "%%"), text: :dark_gray)
|
||||
@@log.push(msg + "\r\n")
|
||||
PBDebug.flush # if @@log.length > 1024
|
||||
end
|
||||
|
||||
@@ -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 "
|
||||
@@ -322,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("")
|
||||
@@ -408,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
|
||||
@@ -427,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
|
||||
@@ -435,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?
|
||||
@@ -462,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
|
||||
}
|
||||
# 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
|
||||
|
||||
|
||||
@@ -200,8 +200,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 into using a multi-turn move")
|
||||
next
|
||||
end
|
||||
# AI controls this battler
|
||||
if @controlPlayer || !pbOwnedByPlayer?(idxBattler)
|
||||
@battleAI.pbDefaultChooseEnemyCommand(idxBattler)
|
||||
|
||||
@@ -186,9 +186,10 @@ class Battle
|
||||
end
|
||||
b.effects[PBEffects::Rage] = false if !pbChoseMoveFunctionCode?(i, "StartRaiseUserAtk1WhenDamaged")
|
||||
end
|
||||
PBDebug.log("")
|
||||
# Calculate move order for this round
|
||||
PBDebug.log("")
|
||||
pbCalculatePriority(true)
|
||||
PBDebug.log("")
|
||||
# Perform actions
|
||||
pbAttackPhasePriorityChangeMessages
|
||||
pbAttackPhaseCall
|
||||
|
||||
@@ -596,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
|
||||
|
||||
@@ -8,7 +8,7 @@ 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
|
||||
@@ -26,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
|
||||
|
||||
@@ -37,7 +37,7 @@ class Battle::Battler
|
||||
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
|
||||
@@ -187,7 +187,7 @@ class Battle::Battler
|
||||
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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -146,7 +146,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
|
||||
|
||||
|
||||
@@ -192,10 +192,12 @@ 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))
|
||||
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 +212,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 +223,7 @@ class Battle::Battler
|
||||
pbCureStatus
|
||||
else
|
||||
pbContinueStatus
|
||||
PBDebug.log("[Move failed] #{pbThis} is frozen")
|
||||
@lastMoveFailed = true
|
||||
return false
|
||||
end
|
||||
@@ -235,12 +239,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 +265,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 +274,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 +285,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 +304,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 +563,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
|
||||
|
||||
|
||||
@@ -248,10 +248,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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -45,7 +45,7 @@ class Battle::AI
|
||||
@trainer = @trainers[(opposes) ? 1 : 0][trainer_index]
|
||||
# Find the AI battler for which the action is being chosen
|
||||
@user = @battlers[idxBattler]
|
||||
@user.refresh_battler
|
||||
@battlers.each { |b| b.refresh_battler if b }
|
||||
end
|
||||
|
||||
# Choose an action.
|
||||
|
||||
@@ -11,16 +11,18 @@ class Battle::AI
|
||||
shouldSwitch = forceSwitch
|
||||
battler = @user.battler
|
||||
batonPass = -1
|
||||
foe = nil
|
||||
moveType = nil
|
||||
# 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 && @trainer.high_skill?
|
||||
target = battler.pbDirectOpposing(true)
|
||||
foe = @battlers[target.index]
|
||||
if !target.fainted? && target.lastMoveUsed &&
|
||||
(target.level - battler.level).abs <= 6
|
||||
moveData = GameData::Move.get(target.lastMoveUsed)
|
||||
moveType = moveData.type
|
||||
typeMod = battler.effectiveness_of_type_against_battler(moveType, target)
|
||||
typeMod = @user.effectiveness_of_type_against_battler(moveType, foe)
|
||||
if Effectiveness.super_effective?(typeMod) && moveData.base_damage > 50
|
||||
switchChance = (moveData.base_damage > 70) ? 30 : 20
|
||||
shouldSwitch = (pbAIRandom(100) < switchChance)
|
||||
@@ -106,7 +108,7 @@ class Battle::AI
|
||||
end
|
||||
end
|
||||
# moveType is the type of the target's last used move
|
||||
if moveType && Effectiveness.ineffective?(battler.effectiveness_of_type_against_battler(moveType))
|
||||
if moveType && Effectiveness.ineffective?(@user.effectiveness_of_type_against_battler(moveType, foe))
|
||||
weight = 65
|
||||
typeMod = pbCalcTypeModPokemon(pkmn, battler.pbDirectOpposing(true))
|
||||
if Effectiveness.super_effective?(typeMod)
|
||||
@@ -114,7 +116,7 @@ class Battle::AI
|
||||
weight = 85
|
||||
end
|
||||
list.unshift(i) if pbAIRandom(100) < weight # Put this Pokemon first
|
||||
elsif moveType && Effectiveness.resistant?(battler.effectiveness_of_type_against_battler(moveType))
|
||||
elsif moveType && Effectiveness.resistant?(@user.effectiveness_of_type_against_battler(moveType, foe))
|
||||
weight = 40
|
||||
typeMod = pbCalcTypeModPokemon(pkmn, battler.pbDirectOpposing(true))
|
||||
if Effectiveness.super_effective?(typeMod)
|
||||
|
||||
@@ -11,19 +11,16 @@ class Battle::AI
|
||||
# (which are based on the predicted damages). Multi-target moves could
|
||||
# be fiddly since damages should be calculated for each target but
|
||||
# they're all related.
|
||||
battler.eachMoveWithIndex do |_m, i|
|
||||
next if !@battle.pbCanChooseMove?(battler.index, i, false) # Unchoosable moves aren't considered
|
||||
pbAddMoveWithScoreToChoices(i, choices)
|
||||
end
|
||||
# Log the available choices
|
||||
if $INTERNAL
|
||||
logMsg = "[AI] Move choices for #{battler.pbThis(true)} (#{battler.index}): "
|
||||
choices.each_with_index do |c, i|
|
||||
logMsg += "#{battler.moves[c[0]].name}=#{c[1]}"
|
||||
logMsg += " (target #{c[2]})" if c[2] >= 0
|
||||
logMsg += ", " if i < choices.length - 1
|
||||
battler.eachMoveWithIndex do |m, i|
|
||||
if !@battle.pbCanChooseMove?(battler.index, i, false) # Unchoosable moves aren't considered
|
||||
if m.pp == 0 && m.total_pp > 0
|
||||
PBDebug.log("[AI] #{battler.pbThis} (#{battler.index}) cannot use move #{m.name} as it has no PP left")
|
||||
else
|
||||
PBDebug.log("[AI] #{battler.pbThis} (#{battler.index}) cannot choose to use #{m.name}")
|
||||
end
|
||||
next
|
||||
end
|
||||
PBDebug.log(logMsg)
|
||||
pbAddMoveWithScoreToChoices(i, choices)
|
||||
end
|
||||
@battle.moldBreaker = false
|
||||
return choices
|
||||
@@ -159,7 +156,7 @@ class Battle::AI
|
||||
target_battler = @target.battler
|
||||
|
||||
# Predict whether the move will fail
|
||||
return 50 if pbPredictMoveFailure
|
||||
return 25 if pbPredictMoveFailure
|
||||
|
||||
# Get the base score for the move
|
||||
if @move.damagingMove?
|
||||
@@ -188,20 +185,11 @@ class Battle::AI
|
||||
def pbChooseMove(choices)
|
||||
user_battler = @user.battler
|
||||
|
||||
# If there are no calculated choices, pick one at random
|
||||
# If no moves can be chosen, auto-choose a move or Struggle
|
||||
if choices.length == 0
|
||||
# NOTE: Can only get here if no moves can be chosen, i.e. will auto-use a
|
||||
# move or struggle.
|
||||
user_battler.eachMoveWithIndex do |_m, i|
|
||||
next if !@battle.pbCanChooseMove?(user_battler.index, i, false)
|
||||
choices.push([i, 100, -1]) # Move index, score, target
|
||||
end
|
||||
if choices.length == 0 # No moves are physically possible to use; use Struggle
|
||||
@battle.pbAutoChooseMove(user_battler.index)
|
||||
PBDebug.log("[AI] #{user_battler.pbThis} (#{user_battler.index}) will auto-use a move or Struggle")
|
||||
return
|
||||
end
|
||||
PBDebug.log("[AI] #{user_battler.pbThis} (#{user_battler.index}) doesn't want to use any moves; picking one at random")
|
||||
@battle.pbAutoChooseMove(user_battler.index)
|
||||
PBDebug.log("[AI] #{user_battler.pbThis} (#{user_battler.index}) will auto-use a move or Struggle")
|
||||
return
|
||||
end
|
||||
|
||||
# Figure out useful information about the choices
|
||||
@@ -211,31 +199,45 @@ class Battle::AI
|
||||
# Decide whether all choices are bad, and if so, try switching instead
|
||||
if @trainer.high_skill? && @user.can_switch_lax?
|
||||
badMoves = false
|
||||
if (max_score <= 20 && user_battler.turnCount > 2) ||
|
||||
(max_score <= 40 && user_battler.turnCount > 5)
|
||||
if (max_score <= 25 && user_battler.turnCount > 2) ||
|
||||
(max_score <= 50 && user_battler.turnCount > 5)
|
||||
badMoves = true if pbAIRandom(100) < 80
|
||||
end
|
||||
if !badMoves && max_score < 60 && user_battler.turnCount > 1
|
||||
if !badMoves && max_score < 50 && user_battler.turnCount >= 1
|
||||
badMoves = choices.none? { |c| user_battler.moves[c[0]].damagingMove? }
|
||||
badMoves = false if badMoves && pbAIRandom(100) < 10
|
||||
end
|
||||
if badMoves && pbEnemyShouldWithdrawEx?(true)
|
||||
if $INTERNAL
|
||||
PBDebug.log("[AI] #{user_battler.pbThis} (#{user_battler.index}) will switch due to terrible moves")
|
||||
end
|
||||
PBDebug.log("[AI] #{user_battler.pbThis} (#{user_battler.index}) will switch due to terrible moves")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
# Calculate a minimum score threshold and reduce all move scores by it
|
||||
threshold = (max_score * 0.85).floor
|
||||
choices.each { |c| c[1] = [c[1] - threshold, 0].max }
|
||||
total_score = choices.sum { |c| c[1] }
|
||||
choices.each { |c| c[3] = [c[1] - threshold, 0].max }
|
||||
total_score = choices.sum { |c| c[3] }
|
||||
|
||||
# Log the available choices
|
||||
if $INTERNAL
|
||||
PBDebug.log("[AI] Move choices for #{user_battler.pbThis(true)} (#{user_battler.index}):")
|
||||
choices.each_with_index do |c, i|
|
||||
chance = "0"
|
||||
chance = sprintf("%.1f", 100.0 * c[3] / total_score) if c[3] > 0
|
||||
while chance.length < 5
|
||||
chance = " " + chance
|
||||
end
|
||||
log_msg = " * #{chance}% chance: #{user_battler.moves[c[0]].name}"
|
||||
log_msg += " (against target #{c[2]})" if c[2] >= 0
|
||||
log_msg += " = score #{c[1]}"
|
||||
PBDebug.log(log_msg)
|
||||
end
|
||||
end
|
||||
|
||||
# Pick a move randomly from choices weighted by their scores
|
||||
randNum = pbAIRandom(total_score)
|
||||
choices.each do |c|
|
||||
randNum -= c[1]
|
||||
randNum -= c[3]
|
||||
next if randNum >= 0
|
||||
@battle.pbRegisterMove(user_battler.index, c[0], false)
|
||||
@battle.pbRegisterTarget(user_battler.index, c[2]) if c[2] >= 0
|
||||
@@ -244,7 +246,7 @@ class Battle::AI
|
||||
|
||||
# Log the result
|
||||
if @battle.choices[user_battler.index][2]
|
||||
PBDebug.log("[AI] #{user_battler.pbThis} (#{user_battler.index}) will use #{@battle.choices[user_battler.index][2].name}")
|
||||
PBDebug.log(" => will use #{@battle.choices[user_battler.index][2].name}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -62,22 +62,22 @@ class Battle::AI
|
||||
# TODO: Exception if user knows Baton Pass/Stored Power?
|
||||
case stat
|
||||
when :ATTACK
|
||||
return false if !@user.check_for_move { |move| move.physicalMove?(move.type) &&
|
||||
move.function != "UseUserBaseDefenseInsteadOfUserBaseAttack" &&
|
||||
move.function != "UseTargetAttackInsteadOfUserAttack" }
|
||||
return false if !@user.check_for_move { |m| m.physicalMove?(move.type) &&
|
||||
m.function != "UseUserBaseDefenseInsteadOfUserBaseAttack" &&
|
||||
m.function != "UseTargetAttackInsteadOfUserAttack" }
|
||||
when :DEFENSE
|
||||
each_foe_battler(@user.side) do |b, i|
|
||||
next if !b.check_for_move { |move| move.physicalMove?(move.type) ||
|
||||
move.function == "UseTargetDefenseInsteadOfTargetSpDef" }
|
||||
next if !b.check_for_move { |m| m.physicalMove?(m.type) ||
|
||||
m.function == "UseTargetDefenseInsteadOfTargetSpDef" }
|
||||
return true
|
||||
end
|
||||
return false
|
||||
when :SPECIAL_ATTACK
|
||||
return false if !@user.check_for_move { |move| move.specialMove?(move.rough_type) }
|
||||
return false if !@user.check_for_move { |m| m.specialMove?(m.type) }
|
||||
when :SPECIAL_DEFENSE
|
||||
each_foe_battler(@user.side) do |b, i|
|
||||
next if !b.check_for_move { |move| move.specialMove?(move.type) &&
|
||||
move.function != "UseTargetDefenseInsteadOfTargetSpDef" }
|
||||
next if !b.check_for_move { |m| m.specialMove?(m.type) &&
|
||||
m.function != "UseTargetDefenseInsteadOfTargetSpDef" }
|
||||
return true
|
||||
end
|
||||
return false
|
||||
@@ -86,7 +86,7 @@ class Battle::AI
|
||||
"PowerHigherWithUserFasterThanTarget",
|
||||
"PowerHigherWithUserPositiveStatStages"
|
||||
]
|
||||
if !@user.check_for_move { |move| moves_that_prefer_high_speed.include?(move.function) }
|
||||
if !@user.check_for_move { |m| moves_that_prefer_high_speed.include?(m.function) }
|
||||
each_foe_battler(@user.side) do |b, i|
|
||||
return true if b.faster_than?(@user)
|
||||
end
|
||||
@@ -241,7 +241,7 @@ class Battle::AI
|
||||
if @user.stages[stat] >= 3
|
||||
score -= 20
|
||||
else
|
||||
has_special_moves = @user.check_for_move { |move| move.specialMove?(move.rough_type) }
|
||||
has_special_moves = @user.check_for_move { |m| m.specialMove?(m.type) }
|
||||
inc = (has_special_moves) ? 5 : 10
|
||||
score += inc * (3 - @user.stages[stat]) * increment # 5 to 45
|
||||
score += 5 * increment if @user.hp == @user.totalhp
|
||||
@@ -262,9 +262,9 @@ class Battle::AI
|
||||
if @user.stages[stat] >= 3
|
||||
score -= 20
|
||||
else
|
||||
has_physical_moves = @user.check_for_move { |move| move.physicalMove?(move.type) &&
|
||||
move.function != "UseUserBaseDefenseInsteadOfUserBaseAttack" &&
|
||||
move.function != "UseTargetAttackInsteadOfUserAttack" }
|
||||
has_physical_moves = @user.check_for_move { |m| m.physicalMove?(m.type) &&
|
||||
m.function != "UseUserBaseDefenseInsteadOfUserBaseAttack" &&
|
||||
m.function != "UseTargetAttackInsteadOfUserAttack" }
|
||||
inc = (has_physical_moves) ? 5 : 10
|
||||
score += inc * (3 - @user.stages[stat]) * increment # 5 to 45
|
||||
score += 5 * increment if @user.hp == @user.totalhp
|
||||
@@ -288,7 +288,7 @@ class Battle::AI
|
||||
end
|
||||
# Don't prefer if any foe has Gyro Ball
|
||||
each_foe_battler(@user.side) do |b, i|
|
||||
next if !b.check_for_move { |move| move.function == "PowerHigherWithTargetFasterThanUser" }
|
||||
next if !b.check_for_move { |m| m.function == "PowerHigherWithTargetFasterThanUser" }
|
||||
score -= 10 * increment
|
||||
end
|
||||
# Don't prefer if user has Speed Boost (will be gaining Speed anyway)
|
||||
@@ -336,12 +336,12 @@ class Battle::AI
|
||||
pos_change = [@user.stages[stat] + increment, increment].min
|
||||
if pos_change > 0
|
||||
# Prefer if user has Stored Power
|
||||
if @user.check_for_move { |move| move.function == "PowerHigherWithUserPositiveStatStages" }
|
||||
if @user.check_for_move { |m| m.function == "PowerHigherWithUserPositiveStatStages" }
|
||||
score += 10 * pos_change
|
||||
end
|
||||
# Don't prefer if any foe has Punishment
|
||||
each_foe_battler(@user.side) do |b, i|
|
||||
next if !b.check_for_move { |move| move.function == "PowerHigherWithTargetPositiveStatStages" }
|
||||
next if !b.check_for_move { |m| m.function == "PowerHigherWithTargetPositiveStatStages" }
|
||||
score -= 10 * pos_change
|
||||
end
|
||||
end
|
||||
@@ -355,7 +355,7 @@ class Battle::AI
|
||||
when :ATTACK
|
||||
# TODO: Don't prefer if target has previously used a move that benefits
|
||||
# from user's Attack being boosted.
|
||||
# mini_score *= 0.3 if @target.check_for_move { |move| move.function == "UseTargetAttackInsteadOfUserAttack" } # Foul Play
|
||||
# mini_score *= 0.3 if @target.check_for_move { |m| m.function == "UseTargetAttackInsteadOfUserAttack" } # Foul Play
|
||||
# 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 &&
|
||||
@@ -393,7 +393,7 @@ class Battle::AI
|
||||
# (@user.hasActiveItem?(:BLACKSLUDGE) && @user.pbHasType?(:POISON))
|
||||
# Prefer if user knows any healing moves
|
||||
# # TODO: Is 1.2x for RaiseUserAtkDefAcc1 Coil (+Atk, +Def, +acc).
|
||||
# mini_score *= 1.3 if check_for_move(@user) { |move| move.healingMove? }
|
||||
# mini_score *= 1.3 if check_for_move(@user) { |m| m.healingMove? }
|
||||
# Prefer if user knows Pain Split or Leech Seed
|
||||
# # TODO: Leech Seed is 1.2x for RaiseUserAtkDefAcc1 Coil (+Atk, +Def, +acc).
|
||||
# mini_score *= 1.2 if @user.pbHasMoveFunction?("UserTargetAverageHP") # Pain Split
|
||||
@@ -483,7 +483,7 @@ class Battle::AI
|
||||
# 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? }
|
||||
# mini_score *= 1.3 if check_for_move(@user) { |m| m.healingMove? }
|
||||
# Prefer if user knows Pain Split or Leech Seed
|
||||
# mini_score *= 1.2 if @user.pbHasMoveFunction?("UserTargetAverageHP") # Pain Split
|
||||
# mini_score *= 1.3 if @user.pbHasMoveFunction?("StartLeechSeedTarget") # Leech Seed
|
||||
@@ -495,7 +495,7 @@ class Battle::AI
|
||||
|
||||
when :ACCURACY
|
||||
# Prefer if user knows any weaker moves
|
||||
mini_score *= 1.1 if check_for_move(@user) { |move| move.damagingMove? && move.basedamage < 95 }
|
||||
mini_score *= 1.1 if check_for_move(@user) { |m| m.damagingMove? && m.basedamage < 95 }
|
||||
# Prefer if target has a raised evasion
|
||||
sum_stages = @target.stages[:EVASION]
|
||||
mini_score *= 1 + sum_stages * 0.05 if sum_stages > 0
|
||||
@@ -521,7 +521,7 @@ class Battle::AI
|
||||
mini_score *= 1.3
|
||||
end
|
||||
# Prefer if user knows any healing moves
|
||||
mini_score *= 1.3 if check_for_move(@user) { |move| move.healingMove? }
|
||||
mini_score *= 1.3 if check_for_move(@user) { |m| move.healingMove? }
|
||||
# Prefer if user knows Pain Split or Leech Seed
|
||||
mini_score *= 1.2 if @user.pbHasMoveFunction?("UserTargetAverageHP") # Pain Split
|
||||
mini_score *= 1.3 if @user.pbHasMoveFunction?("StartLeechSeedTarget") # Leech Seed
|
||||
@@ -537,8 +537,8 @@ class Battle::AI
|
||||
|
||||
# TODO: Don't prefer if any foe has previously used a stat stage-clearing
|
||||
# move (Clear Smog/Haze).
|
||||
mini_score *= 0.3 if check_for_move(@target) { |move|
|
||||
["ResetTargetStatStages", "ResetAllBattlersStatStages"].include?(move.function)
|
||||
mini_score *= 0.3 if check_for_move(@target) { |m|
|
||||
["ResetTargetStatStages", "ResetAllBattlersStatStages"].include?(m.function)
|
||||
} # Clear Smog, Haze
|
||||
|
||||
# TODO: Prefer if user is faster than the target.
|
||||
@@ -561,8 +561,8 @@ class Battle::AI
|
||||
def get_score_for_terrain(terrain, move_user)
|
||||
ret = 0
|
||||
# Inherent effects of terrain
|
||||
@battlers.each do |b|
|
||||
next if !b || b.battler.fainted? || !b.battler.affectedByTerrain?
|
||||
each_battler do |b, i|
|
||||
next if !b.battler.affectedByTerrain?
|
||||
case terrain
|
||||
when :Electric
|
||||
# Immunity to sleep
|
||||
@@ -574,14 +574,14 @@ class Battle::AI
|
||||
ret += (b.opposes?(move_user)) ? -10 : 10
|
||||
end
|
||||
# Check for Electric moves
|
||||
if b.check_for_move { |move| move.type == :ELECTRIC && move.damagingMove? }
|
||||
if b.check_for_move { |m| m.type == :ELECTRIC && m.damagingMove? }
|
||||
ret += (b.opposes?(move_user)) ? -15 : 15
|
||||
end
|
||||
when :Grassy
|
||||
# End of round healing
|
||||
ret += (b.opposes?(move_user)) ? -10 : 10
|
||||
# Check for Grass moves
|
||||
if b.check_for_move { |move| move.type == :GRASS && move.damagingMove? }
|
||||
if b.check_for_move { |m| m.type == :GRASS && m.damagingMove? }
|
||||
ret += (b.opposes?(move_user)) ? -15 : 15
|
||||
end
|
||||
when :Misty
|
||||
@@ -592,16 +592,16 @@ class Battle::AI
|
||||
ret += (b.opposes?(move_user)) ? -5 : 5
|
||||
end
|
||||
# Check for Dragon moves
|
||||
if b.check_for_move { |move| move.type == :DRAGON && move.damagingMove? }
|
||||
if b.check_for_move { |m| m.type == :DRAGON && m.damagingMove? }
|
||||
ret += (b.opposes?(move_user)) ? 15 : -15
|
||||
end
|
||||
when :Psychic
|
||||
# Check for priority moves
|
||||
if b.check_for_move { |move| move.priority > 0 && move.pbTarget&.can_target_one_foe? }
|
||||
if b.check_for_move { |m| m.priority > 0 && m.pbTarget&.can_target_one_foe? }
|
||||
ret += (b.opposes?(move_user)) ? 10 : -10
|
||||
end
|
||||
# Check for Psychic moves
|
||||
if b.check_for_move { |move| move.type == :PSYCHIC && move.damagingMove? }
|
||||
if b.check_for_move { |m| m.type == :PSYCHIC && m.damagingMove? }
|
||||
ret += (b.opposes?(move_user)) ? -15 : 15
|
||||
end
|
||||
end
|
||||
@@ -613,8 +613,7 @@ class Battle::AI
|
||||
:Misty => :MISTYSEED,
|
||||
:Psychic => :PSYCHICSEED
|
||||
}[terrain]
|
||||
ai.battlers.each do |b|
|
||||
next if !b || b.battler.fainted?
|
||||
each_battler do |b, i|
|
||||
if b.has_active_item?(:TERRAINEXTENDER)
|
||||
ret += (b.opposes?(move_user)) ? -15 : 15
|
||||
elsif seed && b.has_active_item?(seed)
|
||||
@@ -622,7 +621,7 @@ class Battle::AI
|
||||
end
|
||||
end
|
||||
# Check for abilities/moves affected by the terrain
|
||||
if ai.trainer.medium_skill?
|
||||
if @trainer.medium_skill?
|
||||
abils = {
|
||||
:Electric => :SURGESURFER,
|
||||
:Grassy => :GRASSPELT,
|
||||
@@ -644,8 +643,8 @@ class Battle::AI
|
||||
:Misty => nil,
|
||||
:Psychic => nil
|
||||
}[terrain]
|
||||
ai.battlers.each do |b|
|
||||
next if !b || b.battler.fainted? || !b.battler.affectedByTerrain?
|
||||
each_battler do |b, i|
|
||||
next if !b.battler.affectedByTerrain?
|
||||
# Abilities
|
||||
if b.has_active_ability?(:MIMICRY)
|
||||
ret += (b.opposes?(move_user)) ? -5 : 5
|
||||
@@ -654,16 +653,16 @@ class Battle::AI
|
||||
ret += (b.opposes?(move_user)) ? -15 : 15
|
||||
end
|
||||
# Moves
|
||||
if b.check_for_move { |move| ["EffectDependsOnEnvironment",
|
||||
"SetUserTypesBasedOnEnvironment",
|
||||
"TypeAndPowerDependOnTerrain",
|
||||
"UseMoveDependingOnEnvironment"].include?(move.function) }
|
||||
if b.check_for_move { |m| ["EffectDependsOnEnvironment",
|
||||
"SetUserTypesBasedOnEnvironment",
|
||||
"TypeAndPowerDependOnTerrain",
|
||||
"UseMoveDependingOnEnvironment"].include?(m.function) }
|
||||
ret += (b.opposes?(move_user)) ? -10 : 10
|
||||
end
|
||||
if good_moves && b.check_for_move { |move| good_moves.include?(move.function) }
|
||||
if good_moves && b.check_for_move { |m| good_moves.include?(m.function) }
|
||||
ret += (b.opposes?(move_user)) ? -10 : 10
|
||||
end
|
||||
if bad_moves && b.check_for_move { |move| bad_moves.include?(move.function) }
|
||||
if bad_moves && b.check_for_move { |m| bad_moves.include?(m.function) }
|
||||
ret += (b.opposes?(move_user)) ? 10 : -10
|
||||
end
|
||||
end
|
||||
|
||||
@@ -129,12 +129,12 @@ Battle::AI::Handlers::MoveEffectScore.add("FailsIfUserDamagedThisTurn",
|
||||
if target.effects[PBEffects::HyperBeam] > 0 ||
|
||||
target.effects[PBEffects::Truant] ||
|
||||
(target.battler.asleep? && target.statusCount > 1) ||
|
||||
target.frozen?
|
||||
target.battler.frozen?
|
||||
score += 20
|
||||
elsif target.effects[PBEffects::Confusion] > 1 ||
|
||||
target.effects[PBEffects::Attract] == user.index
|
||||
score += 10
|
||||
elsif target.paralyzed?
|
||||
elsif target.battler.paralyzed?
|
||||
score += 5
|
||||
end
|
||||
end
|
||||
@@ -167,7 +167,7 @@ Battle::AI::Handlers::MoveEffectScore.add("FailsIfTargetActed",
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("CrashDamageIfFailsUnusableInGravity",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score - (100 - move.rough_accuracy) if user.takesIndirectDamage?
|
||||
next score - (100 - move.rough_accuracy) if user.battler.takesIndirectDamage?
|
||||
}
|
||||
)
|
||||
|
||||
@@ -448,7 +448,7 @@ Battle::AI::Handlers::MoveEffectScore.add("AddSpikesToFoeSide",
|
||||
inBattleIndices = battle.allSameSideBattlers(user.idxOpposingSide).map { |b| b.pokemonIndex }
|
||||
foe_reserves = []
|
||||
battle.pbParty(user.idxOpposingSide).each_with_index do |pkmn, idxParty|
|
||||
next if !pkmn || !pkmn.able? || inBattleIndices.include(idxParty)
|
||||
next if !pkmn || !pkmn.able? || inBattleIndices.include?(idxParty)
|
||||
if ai.trainer.medium_skill?
|
||||
# Check affected by entry hazard
|
||||
next if pkmn.hasItem?(:HEAVYDUTYBOOTS)
|
||||
@@ -484,7 +484,7 @@ Battle::AI::Handlers::MoveEffectScore.add("AddToxicSpikesToFoeSide",
|
||||
inBattleIndices = battle.allSameSideBattlers(user.idxOpposingSide).map { |b| b.pokemonIndex }
|
||||
foe_reserves = []
|
||||
battle.pbParty(user.idxOpposingSide).each_with_index do |pkmn, idxParty|
|
||||
next if !pkmn || !pkmn.able? || inBattleIndices.include(idxParty)
|
||||
next if !pkmn || !pkmn.able? || inBattleIndices.include?(idxParty)
|
||||
if ai.trainer.medium_skill?
|
||||
# Check affected by entry hazard
|
||||
next if pkmn.hasItem?(:HEAVYDUTYBOOTS)
|
||||
@@ -522,7 +522,7 @@ Battle::AI::Handlers::MoveEffectScore.add("AddStealthRocksToFoeSide",
|
||||
inBattleIndices = battle.allSameSideBattlers(user.idxOpposingSide).map { |b| b.pokemonIndex }
|
||||
foe_reserves = []
|
||||
battle.pbParty(user.idxOpposingSide).each_with_index do |pkmn, idxParty|
|
||||
next if !pkmn || !pkmn.able? || inBattleIndices.include(idxParty)
|
||||
next if !pkmn || !pkmn.able? || inBattleIndices.include?(idxParty)
|
||||
if ai.trainer.medium_skill?
|
||||
# Check affected by entry hazard
|
||||
next if pkmn.hasItem?(:HEAVYDUTYBOOTS)
|
||||
@@ -549,7 +549,7 @@ Battle::AI::Handlers::MoveEffectScore.add("AddStickyWebToFoeSide",
|
||||
inBattleIndices = battle.allSameSideBattlers(user.idxOpposingSide).map { |b| b.pokemonIndex }
|
||||
foe_reserves = []
|
||||
battle.pbParty(user.idxOpposingSide).each_with_index do |pkmn, idxParty|
|
||||
next if !pkmn || !pkmn.able? || inBattleIndices.include(idxParty)
|
||||
next if !pkmn || !pkmn.able? || inBattleIndices.include?(idxParty)
|
||||
if ai.trainer.medium_skill?
|
||||
# Check affected by entry hazard
|
||||
next if pkmn.hasItem?(:HEAVYDUTYBOOTS)
|
||||
|
||||
@@ -393,7 +393,7 @@ Battle::AI::Handlers::MoveEffectScore.add("FlinchTarget",
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("FlinchTargetFailsIfUserNotAsleep",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if !user.asleep?
|
||||
next true if !user.battler.asleep?
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("FlinchTargetFailsIfUserNotAsleep",
|
||||
@@ -649,7 +649,7 @@ Battle::AI::Handlers::MoveFailureCheck.add("SetTargetAbilityToInsomnia",
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("SetUserAbilityToTargetAbility",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if user.battle.unstoppableAbility?
|
||||
next true if user.battler.unstoppableAbility?
|
||||
next true if !target.ability || user.ability_id == target.ability_id
|
||||
next true if target.battler.ungainableAbility? ||
|
||||
[:POWEROFALCHEMY, :RECEIVER, :TRACE, :WONDERGUARD].include?(target.ability_id)
|
||||
@@ -673,7 +673,7 @@ Battle::AI::Handlers::MoveFailureCheck.add("SetTargetAbilityToUserAbility",
|
||||
next true if !user.ability || user.ability_id == target.ability_id
|
||||
next true if user.battler.ungainableAbility? ||
|
||||
[:POWEROFALCHEMY, :RECEIVER, :TRACE].include?(user.ability_id)
|
||||
next true if target.battle.unstoppableAbility? || target.ability_id == :TRUANT
|
||||
next true if target.battler.unstoppableAbility? || target.ability_id == :TRUANT
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("SetTargetAbilityToUserAbility",
|
||||
@@ -693,9 +693,9 @@ Battle::AI::Handlers::MoveFailureCheck.add("UserTargetSwapAbilities",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if !user.ability || !target.ability
|
||||
next true if Settings::MECHANICS_GENERATION <= 5 && user.ability_id == target.ability_id
|
||||
next true if user.battle.unstoppableAbility? || user.battler.ungainableAbility? ||
|
||||
next true if user.battler.unstoppableAbility? || user.battler.ungainableAbility? ||
|
||||
user.ability_id == :WONDERGUARD
|
||||
next true if target.battle.unstoppableAbility? || target.battler.ungainableAbility? ||
|
||||
next true if target.battler.unstoppableAbility? || target.battler.ungainableAbility? ||
|
||||
target.ability_id == :WONDERGUARD
|
||||
}
|
||||
)
|
||||
@@ -714,7 +714,7 @@ Battle::AI::Handlers::MoveEffectScore.add("UserTargetSwapAbilities",
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("NegateTargetAbility",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if target.unstoppableAbility? ||
|
||||
next true if target.battler.unstoppableAbility? ||
|
||||
target.effects[PBEffects::GastroAcid]
|
||||
}
|
||||
)
|
||||
|
||||
@@ -422,7 +422,7 @@ Battle::AI::Handlers::MoveEffectScore.add("StartLeechSeedTarget",
|
||||
if !user.check_for_move { |move| move.damagingMove? }
|
||||
score += 20
|
||||
end
|
||||
score -= 20 if target.has_active_ability?([:LIQUIDOOZE]) || !target.takesIndirectDamage?
|
||||
score -= 20 if target.has_active_ability?([:LIQUIDOOZE]) || !target.battler.takesIndirectDamage?
|
||||
end
|
||||
if ai.trainer.high_skill?
|
||||
if user.check_for_move { |move| move.is_a?(Battle::Move::ProtectMove) }
|
||||
|
||||
@@ -315,8 +315,8 @@ Battle::AI::Handlers::MoveFailureCheck.add("UseLastMoveUsed",
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("UseLastMoveUsedByTarget",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if !target.battle.lastRegularMoveUsed
|
||||
next true if GameData::Move.get(target.battle.lastRegularMoveUsed).flags.none? { |f| f[/^CanMirrorMove$/i] }
|
||||
next true if !target.battler.lastRegularMoveUsed
|
||||
next true if GameData::Move.get(target.battler.lastRegularMoveUsed).flags.none? { |f| f[/^CanMirrorMove$/i] }
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UseLastMoveUsedByTarget",
|
||||
|
||||
@@ -104,7 +104,7 @@ Battle::AI::Handlers::GeneralMoveScore.add(:damaging_moves_if_last_pokemon,
|
||||
if ai.trainer.medium_skill? && battle.pbAbleNonActiveCount(user.idxOwnSide) == 0 &&
|
||||
!(ai.trainer.high_skill? && target && battle.pbAbleNonActiveCount(target.idxOwnSide) > 0)
|
||||
next score * 0.9 if move.statusMove?
|
||||
next score * 1.1 if target_battler.hp <= target_battler.totalhp / 2
|
||||
next score * 1.1 if target && target.battler.hp <= target.battler.totalhp / 2
|
||||
end
|
||||
}
|
||||
)
|
||||
@@ -213,7 +213,7 @@ Battle::AI::Handlers::GeneralMoveScore.add(:thawing_move_against_frozen_target,
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveScore.add(:shiny_target,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score - 40 if target&.wild? && target&.battler.shiny?
|
||||
next score - 40 if target && target.wild? && target.battler.shiny?
|
||||
}
|
||||
)
|
||||
|
||||
@@ -260,7 +260,7 @@ Battle::AI::Handlers::GeneralMoveScore.add(:flinching_effects,
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveScore.add(:add_predicted_damage,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if move.damagingMove?
|
||||
if move.damagingMove? && target
|
||||
dmg = move.rough_damage
|
||||
score += [15.0 * dmg / target.hp, 20].min
|
||||
score += 10 if dmg > target.hp * 1.1 # Predicted to KO the target
|
||||
|
||||
@@ -138,7 +138,7 @@ class Battle::AI::AIBattler
|
||||
# Trapping damage
|
||||
if self.effects[PBEffects::Trapping] > 1 && @battler.takesIndirectDamage?
|
||||
amt = (Settings::MECHANICS_GENERATION >= 6) ? self.totalhp / 8 : self.totalhp / 16
|
||||
if @battlers[self.effects[PBEffects::TrappingUser]].has_active_item?(:BINDINGBAND)
|
||||
if @ai.battlers[self.effects[PBEffects::TrappingUser]].has_active_item?(:BINDINGBAND)
|
||||
amt = (Settings::MECHANICS_GENERATION >= 6) ? self.totalhp / 6 : self.totalhp / 8
|
||||
end
|
||||
ret += [amt, 1].max
|
||||
|
||||
@@ -371,17 +371,17 @@ class Battle::AI::AIMove
|
||||
end
|
||||
|
||||
def rough_accuracy
|
||||
# Determine user and target
|
||||
user = @ai.user
|
||||
user_battler = user.battler
|
||||
target = @ai.target
|
||||
target_battler = target.battler
|
||||
# "Always hit" effects and "always hit" accuracy
|
||||
if @ai.trainer.medium_skill?
|
||||
return 100 if target.effects[PBEffects::Telekinesis] > 0
|
||||
return 100 if target.effects[PBEffects::Minimize] && @move.tramplesMinimize? &&
|
||||
Settings::MECHANICS_GENERATION >= 6
|
||||
end
|
||||
# Determine user and target
|
||||
user = @ai.user
|
||||
user_battler = user.battler
|
||||
target = @ai.target
|
||||
target_battler = target.battler
|
||||
# Get base accuracy
|
||||
baseAcc = self.accuracy
|
||||
return 100 if baseAcc == 0
|
||||
@@ -486,7 +486,7 @@ class Battle::AI::AIMove
|
||||
case function
|
||||
when "BadPoisonTarget"
|
||||
modifiers[:base_accuracy] = 0 if Settings::MORE_TYPE_EFFECTS &&
|
||||
@move.statusMove? && @user.has_type?(:POISON)
|
||||
@move.statusMove? && user.has_type?(:POISON)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1207,7 +1207,7 @@ Battle::AbilityEffects::DamageCalcFromUser.add(:ANALYTIC,
|
||||
# are being used), so I'm choosing to ignore it. The effect is thus:
|
||||
# "power up the move if all other battlers on the field right now have
|
||||
# already moved".
|
||||
if move.move.pbMoveFailedLastInRound?(user, false)
|
||||
if move.pbMoveFailedLastInRound?(user, false)
|
||||
mults[:base_damage_multiplier] *= 1.3
|
||||
end
|
||||
}
|
||||
@@ -1528,7 +1528,7 @@ Battle::AbilityEffects::DamageCalcFromTarget.add(:FLOWERGIFT,
|
||||
Battle::AbilityEffects::DamageCalcFromTarget.add(:FLUFFY,
|
||||
proc { |ability, user, target, move, mults, baseDmg, type|
|
||||
mults[:final_damage_multiplier] *= 2 if move.calcType == :FIRE
|
||||
mults[:final_damage_multiplier] /= 2 if move.move.pbContactMove?(user)
|
||||
mults[:final_damage_multiplier] /= 2 if move.pbContactMove?(user)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -398,7 +398,7 @@ def pbRuledBattle(team1, team2, rule)
|
||||
items2[i] = p.item_id
|
||||
trainer2.party.push(p)
|
||||
end
|
||||
scene = Battle::DebugSceneNoLogging.new
|
||||
scene = Battle::DebugSceneNoVisuals.new
|
||||
battle = rule.createBattle(scene, trainer1, trainer2)
|
||||
battle.debug = true
|
||||
battle.controlPlayer = true
|
||||
|
||||
115
Data/Scripts/999_Main/002_debug battle test.rb
Normal file
115
Data/Scripts/999_Main/002_debug battle test.rb
Normal file
@@ -0,0 +1,115 @@
|
||||
def debug_set_up_trainer
|
||||
# Values to return
|
||||
trainer_array = []
|
||||
foe_items = [] # Intentionally left blank (for now)
|
||||
pokemon_array = []
|
||||
party_starts = [0]
|
||||
|
||||
# Choose random trainer type and trainer name
|
||||
trainer_type = GameData::TrainerType.keys.sample
|
||||
trainer_name = ["Alpha", "Bravo", "Charlie", "Delta", "Echo",
|
||||
"Foxtrot", "Golf", "Hotel", "India", "Juliette",
|
||||
"Kilo", "Lima", "Mike", "November", "Oscar",
|
||||
"Papa", "Quebec", "Romeo", "Sierra", "Tango",
|
||||
"Uniform", "Victor", "Whiskey", "X-ray", "Yankee", "Zulu"].sample
|
||||
|
||||
# Generate trainer
|
||||
trainer = NPCTrainer.new(trainer_name, trainer_type)
|
||||
trainer.id = $player.make_foreign_ID
|
||||
trainer.lose_text = "I lost."
|
||||
trainer_array.push(trainer)
|
||||
|
||||
# Generate party
|
||||
valid_species = []
|
||||
GameData::Species.each_species { |sp| valid_species.push(sp.species) }
|
||||
Settings::MAX_PARTY_SIZE.times do |i|
|
||||
this_species = valid_species.sample
|
||||
this_level = rand(1, Settings::MAXIMUM_LEVEL)
|
||||
pkmn = Pokemon.new(this_species, this_level, trainer)
|
||||
trainer.party.push(pkmn)
|
||||
pokemon_array.push(pkmn)
|
||||
end
|
||||
|
||||
# Return values
|
||||
return trainer_array, foe_items, pokemon_array, party_starts
|
||||
end
|
||||
|
||||
def debug_test_auto_battle(logging = false)
|
||||
old_internal = $INTERNAL
|
||||
$INTERNAL = logging
|
||||
echoln "Start of testing auto battle."
|
||||
echoln "" if !$INTERNAL
|
||||
PBDebug.log("")
|
||||
PBDebug.log("================================================================")
|
||||
PBDebug.log("")
|
||||
# Generate information for the foes
|
||||
foe_trainers, foe_items, foe_party, foe_party_starts = debug_set_up_trainer
|
||||
# Generate information for the player and partner trainer(s)
|
||||
player_trainers, ally_items, player_party, player_party_starts = debug_set_up_trainer
|
||||
# Log the combatants
|
||||
echo_participant = lambda do |trainer, party, index|
|
||||
trainer_txt = "Trainer #{index}: #{trainer.full_name}"
|
||||
($INTERNAL) ? PBDebug.log_header(trainer_txt) : echoln(trainer_txt)
|
||||
party.each do |pkmn|
|
||||
pkmn_txt = " #{pkmn.name}, Lv.#{pkmn.level}\r\n"
|
||||
pkmn_txt += " Ability: #{pkmn.ability&.name || "---"}\r\n"
|
||||
pkmn_txt += " Held item: #{pkmn.item&.name || "---"}"
|
||||
($INTERNAL) ? PBDebug.log(pkmn_txt) : echoln(pkmn_txt)
|
||||
moves_msg = " Moves: "
|
||||
pkmn.moves.each_with_index do |move, i|
|
||||
moves_msg += ", " if i > 0
|
||||
moves_msg += move.name
|
||||
end
|
||||
($INTERNAL) ? PBDebug.log(moves_msg) : echoln(moves_msg)
|
||||
end
|
||||
end
|
||||
echo_participant.call(player_trainers[0], player_party, 1)
|
||||
echo_participant.call(foe_trainers[0], foe_party, 2)
|
||||
echoln "" if !$INTERNAL
|
||||
# Create the battle scene (the visual side of it)
|
||||
scene = Battle::DebugSceneNoVisuals.new(logging)
|
||||
# Create the battle class (the mechanics side of it)
|
||||
battle = Battle.new(scene, player_party, foe_party, player_trainers, foe_trainers)
|
||||
battle.party1starts = player_party_starts
|
||||
battle.party2starts = foe_party_starts
|
||||
battle.ally_items = ally_items
|
||||
battle.items = foe_items
|
||||
|
||||
battle.debug = true
|
||||
battle.internalBattle = false
|
||||
battle.controlPlayer = true
|
||||
# Set various other properties in the battle class
|
||||
BattleCreationHelperMethods.prepare_battle(battle)
|
||||
# Perform the battle itself
|
||||
outcome = battle.pbStartBattle
|
||||
# End
|
||||
echoln ["Undecided",
|
||||
"Trainer 1 #{player_trainers[0].name} won",
|
||||
"Trainer 2 #{foe_trainers[0].name} won",
|
||||
"Ran/forfeited",
|
||||
"Wild Pokémon caught",
|
||||
"Draw"][outcome]
|
||||
echoln ""
|
||||
$INTERNAL = old_internal
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
# Add to Debug menu
|
||||
#===============================================================================
|
||||
MenuHandlers.add(:debug_menu, :test_auto_battle, {
|
||||
"name" => _INTL("Test Auto Battle"),
|
||||
"parent" => :main,
|
||||
"description" => _INTL("Runs an AI-controlled battle with no visuals."),
|
||||
"effect" => proc {
|
||||
debug_test_auto_battle
|
||||
}
|
||||
})
|
||||
|
||||
MenuHandlers.add(:debug_menu, :test_auto_battle_logging, {
|
||||
"name" => _INTL("Test Auto Battle with Logging"),
|
||||
"parent" => :main,
|
||||
"description" => _INTL("Runs an AI-controlled battle with no visuals. Logs messages."),
|
||||
"effect" => proc {
|
||||
debug_test_auto_battle(true)
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user