Added script to make two random AI trainers battle, tweaked battle messages when logged, fixed typos

This commit is contained in:
Maruno17
2022-09-06 22:57:51 +01:00
parent b8c61a6038
commit eecb7a1453
27 changed files with 335 additions and 154 deletions

View File

@@ -7,7 +7,9 @@ module PBDebug
rescue
PBDebug.log("")
PBDebug.log("**Exception: #{$!.message}")
PBDebug.log($!.backtrace.inspect.to_s)
backtrace = ""
$!.backtrace.each { |line| backtrace += line + "\r\n" }
PBDebug.log(backtrace)
PBDebug.log("")
pbPrintException($!) # if $INTERNAL
PBDebug.flush
@@ -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

View File

@@ -246,7 +246,8 @@ class Battle
#=============================================================================
def pbStartBattle
PBDebug.log("")
PBDebug.log("******************************************")
PBDebug.log("================================================================")
PBDebug.log("")
logMsg = "[Started battle] "
if @sideSizes[0] == 1 && @sideSizes[1] == 1
logMsg += "Single "
@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -47,7 +47,7 @@ class Battle::Battler
return false
end
# Use the move
PBDebug.log("[Move usage] #{pbThis} started using #{choice[2].name}")
PBDebug.log("[Use move] #{pbThis} (#{@index}) used #{choice[2].name}")
PBDebug.logonerr {
pbUseMove(choice, choice[2] == @battle.struggle)
}
@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -23,10 +23,17 @@ end
# (Fell Stinger (Gen 6-))
#===============================================================================
class Battle::Move::RaiseUserAttack2IfTargetFaints < Battle::Move
attr_reader :statUp
def initialize(battle, move)
super
@statUp = [:ATTACK, 2]
end
def pbEffectAfterAllHits(user, target)
return if !target.damageState.fainted
return if !user.pbCanRaiseStatStage?(:ATTACK, user, self)
user.pbRaiseStatStage(:ATTACK, 2, user)
return if !user.pbCanRaiseStatStage?(@statUp[0], user, self)
user.pbRaiseStatStage(@statUp[0], @statUp[1], user)
end
end
@@ -45,10 +52,17 @@ end
# (Fell Stinger (Gen 7+))
#===============================================================================
class Battle::Move::RaiseUserAttack3IfTargetFaints < Battle::Move
attr_reader :statUp
def initialize(battle, move)
super
@statUp = [:ATTACK, 3]
end
def pbEffectAfterAllHits(user, target)
return if !target.damageState.fainted
return if !user.pbCanRaiseStatStage?(:ATTACK, user, self)
user.pbRaiseStatStage(:ATTACK, 3, user)
return if !user.pbCanRaiseStatStage?(@statUp[0], user, self)
user.pbRaiseStatStage(@statUp[0], @statUp[1], user)
end
end

View File

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

View File

@@ -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.

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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]
}
)

View File

@@ -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) }

View File

@@ -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",

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)
}
)

View File

@@ -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

View 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)
}
})