mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-08 21:54:58 +00:00
Added more debug logging to AI, fixed some bugs in AI
This commit is contained in:
@@ -48,6 +48,27 @@ module PBDebug
|
||||
end
|
||||
end
|
||||
|
||||
def self.log_ai(msg)
|
||||
if $DEBUG && $INTERNAL
|
||||
msg = "[AI] " + msg
|
||||
echoln msg.gsub("%", "%%")
|
||||
@@log.push(msg + "\r\n")
|
||||
PBDebug.flush # if @@log.length > 1024
|
||||
end
|
||||
end
|
||||
|
||||
def self.log_score_change(amt, msg)
|
||||
return if amt == 0
|
||||
if $DEBUG && $INTERNAL
|
||||
sign = (amt > 0) ? "+" : "-"
|
||||
amt_text = sprintf("%3d", amt.abs)
|
||||
msg = " #{sign}#{amt_text}: #{msg}"
|
||||
echoln msg.gsub("%", "%%")
|
||||
@@log.push(msg + "\r\n")
|
||||
PBDebug.flush # if @@log.length > 1024
|
||||
end
|
||||
end
|
||||
|
||||
def self.dump(msg)
|
||||
if $DEBUG && $INTERNAL
|
||||
File.open("Data/dumplog.txt", "a+b") { |f| f.write("#{msg}\r\n") }
|
||||
|
||||
@@ -129,7 +129,7 @@ class Battle
|
||||
# For choosing a replacement Pokémon when prompted in the middle of other
|
||||
# things happening (U-turn, Baton Pass, in def pbEORSwitch).
|
||||
def pbSwitchInBetween(idxBattler, checkLaxOnly = false, canCancel = false)
|
||||
return pbPartyScreen(idxBattler, checkLaxOnly, canCancel) if pbOwnedByPlayer?(idxBattler)
|
||||
return pbPartyScreen(idxBattler, checkLaxOnly, canCancel) if !@controlPlayer && pbOwnedByPlayer?(idxBattler)
|
||||
return @battleAI.pbDefaultChooseNewEnemy(idxBattler, pbParty(idxBattler))
|
||||
end
|
||||
|
||||
|
||||
@@ -207,7 +207,7 @@ class Battle
|
||||
next if !@battlers[idxBattler] || pbOwnedByPlayer?(idxBattler) != isPlayer
|
||||
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")
|
||||
PBDebug.log_ai("#{@battlers[idxBattler].pbThis} (#{idxBattler}) is forced to use a multi-turn move")
|
||||
next
|
||||
end
|
||||
# AI controls this battler
|
||||
|
||||
@@ -187,7 +187,6 @@ class Battle
|
||||
b.effects[PBEffects::Rage] = false if !pbChoseMoveFunctionCode?(i, "StartRaiseUserAtk1WhenDamaged")
|
||||
end
|
||||
# Calculate move order for this round
|
||||
PBDebug.log("")
|
||||
pbCalculatePriority(true)
|
||||
PBDebug.log("")
|
||||
# Perform actions
|
||||
|
||||
@@ -100,11 +100,18 @@ end
|
||||
# Poisons the target and decreases its Speed by 1 stage. (Toxic Thread)
|
||||
#===============================================================================
|
||||
class Battle::Move::PoisonTargetLowerTargetSpeed1 < Battle::Move
|
||||
attr_reader :statDown
|
||||
|
||||
def initialize(battle, move)
|
||||
super
|
||||
@statDown = [:SPEED, 1]
|
||||
end
|
||||
|
||||
def canMagicCoat?; return true; end
|
||||
|
||||
def pbFailsAgainstTarget?(user, target, show_message)
|
||||
if !target.pbCanPoison?(user, false, self) &&
|
||||
!target.pbCanLowerStatStage?(:SPEED, user, self)
|
||||
!target.pbCanLowerStatStage?(@statDown[0], user, self)
|
||||
@battle.pbDisplay(_INTL("But it failed!")) if show_message
|
||||
return true
|
||||
end
|
||||
@@ -113,8 +120,8 @@ class Battle::Move::PoisonTargetLowerTargetSpeed1 < Battle::Move
|
||||
|
||||
def pbEffectAgainstTarget(user, target)
|
||||
target.pbPoison(user) if target.pbCanPoison?(user, false, self)
|
||||
if target.pbCanLowerStatStage?(:SPEED, user, self)
|
||||
target.pbLowerStatStage(:SPEED, 1, user)
|
||||
if target.pbCanLowerStatStage?(@statDown[0], user, self)
|
||||
target.pbLowerStatStage(@statDown[0], @statDown[1], user)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -334,13 +334,20 @@ end
|
||||
# (Skull Bash)
|
||||
#===============================================================================
|
||||
class Battle::Move::TwoTurnAttackChargeRaiseUserDefense1 < Battle::Move::TwoTurnMove
|
||||
attr_reader :statUp
|
||||
|
||||
def initialize(battle, move)
|
||||
super
|
||||
@statUp = [:DEFENSE, 1]
|
||||
end
|
||||
|
||||
def pbChargingTurnMessage(user, targets)
|
||||
@battle.pbDisplay(_INTL("{1} tucked in its head!", user.pbThis))
|
||||
end
|
||||
|
||||
def pbChargingTurnEffect(user, target)
|
||||
if user.pbCanRaiseStatStage?(:DEFENSE, user, self)
|
||||
user.pbRaiseStatStage(:DEFENSE, 1, user)
|
||||
if user.pbCanRaiseStatStage?(@statUp[0], user, self)
|
||||
user.pbRaiseStatStage(@statUp[0], @statUp[1], user)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -109,6 +109,13 @@ end
|
||||
# it). (Strength Sap)
|
||||
#===============================================================================
|
||||
class Battle::Move::HealUserByTargetAttackLowerTargetAttack1 < Battle::Move
|
||||
attr_reader :statDown
|
||||
|
||||
def initialize(battle, move)
|
||||
super
|
||||
@statDown = [:ATTACK, 1]
|
||||
end
|
||||
|
||||
def healingMove?; return true; end
|
||||
def canMagicCoat?; return true; end
|
||||
|
||||
@@ -119,11 +126,11 @@ class Battle::Move::HealUserByTargetAttackLowerTargetAttack1 < Battle::Move
|
||||
# works even if the stat stage cannot be changed due to an ability or
|
||||
# other effect.
|
||||
if !@battle.moldBreaker && target.hasActiveAbility?(:CONTRARY)
|
||||
if target.statStageAtMax?(:ATTACK)
|
||||
if target.statStageAtMax?(@statDown[0])
|
||||
@battle.pbDisplay(_INTL("But it failed!")) if show_message
|
||||
return true
|
||||
end
|
||||
elsif target.statStageAtMin?(:ATTACK)
|
||||
elsif target.statStageAtMin?(@statDown[0])
|
||||
@battle.pbDisplay(_INTL("But it failed!")) if show_message
|
||||
return true
|
||||
end
|
||||
@@ -135,11 +142,11 @@ class Battle::Move::HealUserByTargetAttackLowerTargetAttack1 < Battle::Move
|
||||
stageMul = [2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8]
|
||||
stageDiv = [8, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2]
|
||||
atk = target.attack
|
||||
atkStage = target.stages[:ATTACK] + 6
|
||||
atkStage = target.stages[@statDown[0]] + 6
|
||||
healAmt = (atk.to_f * stageMul[atkStage] / stageDiv[atkStage]).floor
|
||||
# Reduce target's Attack stat
|
||||
if target.pbCanLowerStatStage?(:ATTACK, user, self)
|
||||
target.pbLowerStatStage(:ATTACK, 1, user)
|
||||
if target.pbCanLowerStatStage?(@statDown[0], user, self)
|
||||
target.pbLowerStatStage(@statDown[0], @statDown[1], user)
|
||||
end
|
||||
# Heal user
|
||||
if target.hasActiveAbility?(:LIQUIDOOZE, true)
|
||||
|
||||
@@ -57,6 +57,7 @@ class Battle::AI
|
||||
@battle.pbRegisterMegaEvolution(idxBattler) if pbEnemyShouldMegaEvolve?
|
||||
choices = pbGetMoveScores
|
||||
pbChooseMove(choices)
|
||||
PBDebug.log("")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
class Battle::AI
|
||||
#=============================================================================
|
||||
# Decide whether the opponent should use an item on the Pokémon
|
||||
# TODO: Maybe don't cure a status problem if the Pokémon has an ability or
|
||||
# something that makes it benefit from having that problem.
|
||||
#=============================================================================
|
||||
def pbEnemyShouldUseItem?
|
||||
item = nil
|
||||
@@ -14,7 +16,7 @@ class Battle::AI
|
||||
end
|
||||
# Register use of item
|
||||
@battle.pbRegisterItem(@user.index, item, idxTarget)
|
||||
PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) will use item #{GameData::Item.get(item).name}")
|
||||
PBDebug.log_ai("#{@user.name} will use item #{GameData::Item.get(item).name}")
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
@@ -133,12 +133,11 @@ class Battle::AI
|
||||
end
|
||||
if list.length > 0
|
||||
if batonPass >= 0 && @battle.pbRegisterMove(battler.index, batonPass, false)
|
||||
PBDebug.log("[AI] #{battler.pbThis} (#{battler.index}) will use Baton Pass to avoid Perish Song")
|
||||
PBDebug.log_ai("#{@user.name} will use Baton Pass to avoid Perish Song")
|
||||
return true
|
||||
end
|
||||
if @battle.pbRegisterSwitch(battler.index, list[0])
|
||||
PBDebug.log("[AI] #{battler.pbThis} (#{battler.index}) will switch with " +
|
||||
@battle.pbParty(battler.index)[list[0]].name)
|
||||
PBDebug.log_ai("#{@user.name} will switch with #{@battle.pbParty(battler.index)[list[0]].name}")
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -23,16 +23,18 @@ class Battle::AI
|
||||
# Unchoosable moves aren't considered
|
||||
if !@battle.pbCanChooseMove?(@user.index, idxMove, false)
|
||||
if move.pp == 0 && move.total_pp > 0
|
||||
PBDebug.log("[AI] #{@user.battler.pbThis} (#{@user.index}) cannot use move #{move.name} as it has no PP left")
|
||||
PBDebug.log_ai("#{@user.name} cannot use #{move.name} (no PP left)")
|
||||
else
|
||||
PBDebug.log("[AI] #{@user.battler.pbThis} (#{@user.index}) cannot choose to use #{move.name}")
|
||||
PBDebug.log_ai("#{@user.name} cannot choose to use #{move.name}")
|
||||
end
|
||||
next
|
||||
end
|
||||
PBDebug.log_ai("#{@user.name} is considering using #{move.name}...")
|
||||
# Set up move in class variables
|
||||
set_up_move_check(move)
|
||||
# Predict whether the move will fail (generally)
|
||||
if @trainer.has_skill_flag?("PredictMoveFailure") && pbPredictMoveFailure
|
||||
PBDebug.log_score_change(MOVE_FAIL_SCORE - MOVE_BASE_SCORE, "move will fail")
|
||||
add_move_to_choices(choices, idxMove, MOVE_FAIL_SCORE)
|
||||
next
|
||||
end
|
||||
@@ -49,6 +51,7 @@ class Battle::AI
|
||||
# TODO: Figure out first which targets are valid. Includes the call to
|
||||
# pbMoveCanTarget?, but also includes move-redirecting effects
|
||||
# like Lightning Rod. Skip any battlers that can't be targeted.
|
||||
num_targets = 0
|
||||
@battle.allBattlers.each do |b|
|
||||
next if !@battle.pbMoveCanTarget?(@user.battler.index, b.index, target_data)
|
||||
# TODO: Should this sometimes consider targeting an ally? See def
|
||||
@@ -57,7 +60,9 @@ class Battle::AI
|
||||
score = MOVE_BASE_SCORE
|
||||
PBDebug.logonerr { score = pbGetMoveScore([b]) }
|
||||
add_move_to_choices(choices, idxMove, score, b.index)
|
||||
num_targets += 1
|
||||
end
|
||||
PBDebug.log(" no valid targets") if num_targets == 0
|
||||
else # Multiple targets at once
|
||||
# Includes: AllAllies, AllBattlers, AllFoes, AllNearFoes, AllNearOthers, UserAndAllies
|
||||
targets = []
|
||||
@@ -100,7 +105,7 @@ class Battle::AI
|
||||
move = Battle::Move.from_pokemon_move(@battle, Pokemon::Move.new(target.battler.lastRegularMoveUsed))
|
||||
end
|
||||
when "UseMoveDependingOnEnvironment"
|
||||
move.move.pbOnStartUse(@user.battler, []) # Determine which move is used instead
|
||||
move.pbOnStartUse(@user.battler, []) # Determine which move is used instead
|
||||
move = Battle::Move.from_pokemon_move(@battle, Pokemon::Move.new(move.npMove))
|
||||
end
|
||||
@move.set_up(move, @user)
|
||||
@@ -197,13 +202,18 @@ class Battle::AI
|
||||
end
|
||||
# Score based on how many targets were affected
|
||||
if affected_targets == 0 && @trainer.has_skill_flag?("PredictMoveFailure")
|
||||
return MOVE_FAIL_SCORE if !@move.move.worksWithNoTargets?
|
||||
if !@move.move.worksWithNoTargets?
|
||||
PBDebug.log_score_change(MOVE_FAIL_SCORE - MOVE_BASE_SCORE, "move will fail")
|
||||
return MOVE_FAIL_SCORE
|
||||
end
|
||||
else
|
||||
# TODO: Can this accounting for multiple targets be improved somehow?
|
||||
score /= affected_targets if affected_targets > 1 # Average the score against multiple targets
|
||||
# Bonus for affecting multiple targets
|
||||
if @trainer.has_skill_flag?("PreferMultiTargetMoves") && affected_targets > 1
|
||||
old_score = score
|
||||
score += (affected_targets - 1) * 10
|
||||
PBDebug.log_score_change(score - old_score, "affects multiple battlers")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -212,8 +222,10 @@ class Battle::AI
|
||||
# Self-Destruct)
|
||||
if @trainer.has_skill_flag?("ScoreMoves")
|
||||
# Modify the score according to the move's effect
|
||||
old_score = score
|
||||
score = Battle::AI::Handlers.apply_move_effect_score(@move.function,
|
||||
score, @move, @user, self, @battle)
|
||||
PBDebug.log_score_change(score - old_score, "function code modifier (generic)")
|
||||
# Modify the score according to various other effects
|
||||
score = Battle::AI::Handlers.apply_general_move_score_modifiers(
|
||||
score, @move, @user, self, @battle)
|
||||
@@ -240,15 +252,18 @@ class Battle::AI
|
||||
#=============================================================================
|
||||
def pbGetMoveScoreAgainstTarget
|
||||
# Predict whether the move will fail against the target
|
||||
if @trainer.has_skill_flag?("PredictMoveFailure")
|
||||
return -1 if pbPredictMoveFailureAgainstTarget
|
||||
if @trainer.has_skill_flag?("PredictMoveFailure") && pbPredictMoveFailureAgainstTarget
|
||||
PBDebug.log(" move will not affect #{@target.name}")
|
||||
return -1
|
||||
end
|
||||
# Score the move
|
||||
score = MOVE_BASE_SCORE
|
||||
if @trainer.has_skill_flag?("ScoreMoves")
|
||||
# Modify the score according to the move's effect against the target
|
||||
old_score = score
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score(@move.function,
|
||||
MOVE_BASE_SCORE, @move, @user, @target, self, @battle)
|
||||
PBDebug.log_score_change(score - old_score, "function code modifier (against target)")
|
||||
# Modify the score according to various other effects against the target
|
||||
score = Battle::AI::Handlers.apply_general_move_against_target_score_modifiers(
|
||||
score, @move, @user, @target, self, @battle)
|
||||
@@ -256,9 +271,14 @@ class Battle::AI
|
||||
# Add the score against the target to the overall score
|
||||
target_data = @move.pbTarget(@user.battler)
|
||||
if target_data.targets_foe && !@target.opposes?(@user) && @target.index != @user.index
|
||||
return -1 if score == MOVE_USELESS_SCORE
|
||||
if score == MOVE_USELESS_SCORE
|
||||
PBDebug.log(" move is useless against #{@target.name}")
|
||||
return -1
|
||||
end
|
||||
# TODO: Is this reversal of the score okay?
|
||||
old_score = score
|
||||
score = 175 - score
|
||||
PBDebug.log_score_change(score - old_score, "score inverted (move targets ally but can target foe)")
|
||||
end
|
||||
return score
|
||||
end
|
||||
@@ -272,7 +292,7 @@ class Battle::AI
|
||||
# If no moves can be chosen, auto-choose a move or Struggle
|
||||
if choices.length == 0
|
||||
@battle.pbAutoChooseMove(user_battler.index)
|
||||
PBDebug.log("[AI] #{user_battler.pbThis} (#{user_battler.index}) will auto-use a move or Struggle")
|
||||
PBDebug.log_ai("#{@user.name} will auto-use a move or Struggle")
|
||||
return
|
||||
end
|
||||
# Figure out useful information about the choices
|
||||
@@ -289,9 +309,10 @@ class Battle::AI
|
||||
badMoves = choices.none? { |c| user_battler.moves[c[0]].damagingMove? }
|
||||
badMoves = false if badMoves && pbAIRandom(100) < 10
|
||||
end
|
||||
if badMoves && pbEnemyShouldWithdrawEx?(true)
|
||||
PBDebug.log("[AI] #{user_battler.pbThis} (#{user_battler.index}) will switch due to terrible moves")
|
||||
return
|
||||
if badMoves
|
||||
PBDebug.log_ai("#{@user.name} wants to switch due to terrible moves")
|
||||
return if pbEnemyShouldWithdrawEx?(true)
|
||||
PBDebug.log_ai("#{@user.name} won't switch after all")
|
||||
end
|
||||
end
|
||||
# Calculate a minimum score threshold and reduce all move scores by it
|
||||
@@ -300,12 +321,12 @@ class Battle::AI
|
||||
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}):")
|
||||
PBDebug.log_ai("Move choices for #{@user.name}:")
|
||||
choices.each_with_index do |c, i|
|
||||
chance = sprintf("%5.1f", (c[3] > 0) ? 100.0 * c[3] / total_score : 0)
|
||||
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]}"
|
||||
log_msg = " * #{chance}% to use #{user_battler.moves[c[0]].name}"
|
||||
log_msg += " (target #{c[2]})" if c[2] >= 0
|
||||
log_msg += ": score #{c[1]}"
|
||||
PBDebug.log(log_msg)
|
||||
end
|
||||
end
|
||||
@@ -320,7 +341,12 @@ class Battle::AI
|
||||
end
|
||||
# Log the result
|
||||
if @battle.choices[user_battler.index][2]
|
||||
PBDebug.log(" => will use #{@battle.choices[user_battler.index][2].name}")
|
||||
move_name = @battle.choices[user_battler.index][2].name
|
||||
if @battle.choices[user_battler.index][3] >= 0
|
||||
PBDebug.log(" => will use #{move_name} (target #{@battle.choices[user_battler.index][3]})")
|
||||
else
|
||||
PBDebug.log(" => will use #{move_name}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,7 +6,7 @@ class Battle::AI
|
||||
# be.
|
||||
def pbEnemyShouldMegaEvolve?
|
||||
if @battle.pbCanMegaEvolve?(@user.index) # Simple "always should if possible"
|
||||
PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) will Mega Evolve")
|
||||
PBDebug.log_ai("#{@user.name} will Mega Evolve")
|
||||
return true
|
||||
end
|
||||
return false
|
||||
|
||||
@@ -18,12 +18,16 @@ class Battle::AI
|
||||
# Discard status move/don't prefer damaging move if target has Contrary
|
||||
# TODO: Maybe this should return get_score_for_target_stat_drop if Contrary
|
||||
# applies and desire_mult < 1.
|
||||
if !fixed_change && !@battle.moldBreaker && target.has_active_ability?(:CONTRARY) && desire_mult > 1
|
||||
return (whole_effect) ? MOVE_USELESS_SCORE : score - 20
|
||||
if !fixed_change && !@battle.moldBreaker && target.has_active_ability?(:CONTRARY) && desire_mult > 0
|
||||
ret = (whole_effect) ? MOVE_USELESS_SCORE : score - 20
|
||||
PBDebug.log_score_change(ret - score, "don't prefer raising target's stats (it has Contrary)")
|
||||
return ret
|
||||
end
|
||||
# Don't make score changes if target will faint from EOR damage
|
||||
if target.rough_end_of_round_damage >= target.hp
|
||||
return (whole_effect) ? MOVE_USELESS_SCORE : score
|
||||
ret = (whole_effect) ? MOVE_USELESS_SCORE : score
|
||||
PBDebug.log(" ignore stat change (target predicted to faint this round)")
|
||||
return ret
|
||||
end
|
||||
# Don't make score changes if foes have Unaware and target can't make use of
|
||||
# extra stat stages
|
||||
@@ -33,7 +37,9 @@ class Battle::AI
|
||||
foe_is_aware = true if !b.has_active_ability?(:UNAWARE)
|
||||
end
|
||||
if !foe_is_aware
|
||||
return (whole_effect) ? MOVE_USELESS_SCORE : score
|
||||
ret = (whole_effect) ? MOVE_USELESS_SCORE : score
|
||||
PBDebug.log(" ignore stat change (target's foes have Unaware)")
|
||||
return ret
|
||||
end
|
||||
end
|
||||
|
||||
@@ -41,7 +47,14 @@ class Battle::AI
|
||||
real_stat_changes = []
|
||||
stat_changes.each_with_index do |stat, idx|
|
||||
next if idx.odd?
|
||||
next if !stat_raise_worthwhile?(target, stat, fixed_change)
|
||||
if !stat_raise_worthwhile?(target, stat, fixed_change)
|
||||
if target.index == @user.index
|
||||
PBDebug.log(" raising the user's #{GameData::Stat.get(stat).name} isn't worthwhile")
|
||||
else
|
||||
PBDebug.log(" raising the target's #{GameData::Stat.get(stat).name} isn't worthwhile")
|
||||
end
|
||||
next
|
||||
end
|
||||
# Calculate amount that stat will be raised by
|
||||
increment = stat_changes[idx + 1]
|
||||
increment *= 2 if !fixed_change && !@battle.moldBreaker && target.has_active_ability?(:SIMPLE)
|
||||
@@ -60,7 +73,13 @@ class Battle::AI
|
||||
# Make score changes based on the specific changes to each stat that will be
|
||||
# raised
|
||||
real_stat_changes.each do |change|
|
||||
old_score = score
|
||||
score = get_target_stat_raise_score_one(score, target, change[0], change[1], desire_mult)
|
||||
if target.index == @user.index
|
||||
PBDebug.log_score_change(score - old_score, "raising the user's #{GameData::Stat.get(change[0]).name} by #{change[1]}")
|
||||
else
|
||||
PBDebug.log_score_change(score - old_score, "raising the target's #{GameData::Stat.get(change[0]).name} by #{change[1]}")
|
||||
end
|
||||
end
|
||||
|
||||
return score
|
||||
@@ -601,12 +620,16 @@ class Battle::AI
|
||||
# Discard status move/don't prefer damaging move if target has Contrary
|
||||
# TODO: Maybe this should return get_score_for_target_stat_raise if Contrary
|
||||
# applies and desire_mult < 1.
|
||||
if !fixed_change && !@battle.moldBreaker && target.has_active_ability?(:CONTRARY) && desire_mult > 1
|
||||
return (whole_effect) ? MOVE_USELESS_SCORE : score - 20
|
||||
if !fixed_change && !@battle.moldBreaker && target.has_active_ability?(:CONTRARY) && desire_mult > 0
|
||||
ret = (whole_effect) ? MOVE_USELESS_SCORE : score - 20
|
||||
PBDebug.log_score_change(ret - score, "don't prefer lowering target's stats (it has Contrary)")
|
||||
return ret
|
||||
end
|
||||
# Don't make score changes if target will faint from EOR damage
|
||||
if target.rough_end_of_round_damage >= target.hp
|
||||
return (whole_effect) ? MOVE_USELESS_SCORE : score
|
||||
ret = (whole_effect) ? MOVE_USELESS_SCORE : score
|
||||
PBDebug.log(" ignore stat change (target predicted to faint this round)")
|
||||
return ret
|
||||
end
|
||||
# Don't make score changes if foes have Unaware and target can't make use of
|
||||
# its lowered stat stages
|
||||
@@ -615,14 +638,23 @@ class Battle::AI
|
||||
foe_is_aware = true if !b.has_active_ability?(:UNAWARE)
|
||||
end
|
||||
if !foe_is_aware
|
||||
return (whole_effect) ? MOVE_USELESS_SCORE : score
|
||||
ret = (whole_effect) ? MOVE_USELESS_SCORE : score
|
||||
PBDebug.log(" ignore stat change (target's foes have Unaware)")
|
||||
return ret
|
||||
end
|
||||
|
||||
# Figure out which stat raises can happen
|
||||
real_stat_changes = []
|
||||
stat_changes.each_with_index do |stat, idx|
|
||||
next if idx.odd?
|
||||
next if !stat_drop_worthwhile?(target, stat, fixed_change)
|
||||
if !stat_drop_worthwhile?(target, stat, fixed_change)
|
||||
if target.index == @user.index
|
||||
PBDebug.log(" lowering the user's #{GameData::Stat.get(stat).name} isn't worthwhile")
|
||||
else
|
||||
PBDebug.log(" lowering the target's #{GameData::Stat.get(stat).name} isn't worthwhile")
|
||||
end
|
||||
next
|
||||
end
|
||||
# Calculate amount that stat will be raised by
|
||||
decrement = stat_changes[idx + 1]
|
||||
decrement *= 2 if !fixed_change && !@battle.moldBreaker && @user.has_active_ability?(:SIMPLE)
|
||||
@@ -641,7 +673,13 @@ class Battle::AI
|
||||
# Make score changes based on the specific changes to each stat that will be
|
||||
# lowered
|
||||
real_stat_changes.each do |change|
|
||||
old_score = score
|
||||
score = get_target_stat_drop_score_one(score, target, change[0], change[1], desire_mult)
|
||||
if target.index == @user.index
|
||||
PBDebug.log_score_change(score - old_score, "lowering the user's #{GameData::Stat.get(change[0]).name} by #{change[1]}")
|
||||
else
|
||||
PBDebug.log_score_change(score - old_score, "lowering the target's #{GameData::Stat.get(change[0]).name} by #{change[1]}")
|
||||
end
|
||||
end
|
||||
|
||||
return score
|
||||
@@ -886,7 +924,7 @@ class Battle::AI
|
||||
end
|
||||
when :Psychic
|
||||
# Check for priority moves
|
||||
if b.check_for_move { |m| m.priority > 0 && m.pbTarget&.can_target_one_foe? }
|
||||
if b.check_for_move { |m| m.priority > 0 && m.pbTarget(b.battler)&.can_target_one_foe? }
|
||||
ret += (b.opposes?(move_user)) ? 10 : -10
|
||||
end
|
||||
# Check for Psychic moves
|
||||
|
||||
@@ -719,7 +719,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetRandomStat2",
|
||||
score += 8
|
||||
end
|
||||
# Don't prefer if any foe has Punishment
|
||||
each_foe_battler(target.side) do |b, i|
|
||||
ai.each_foe_battler(target.side) do |b, i|
|
||||
next if !b.check_for_move { |m| m.function == "PowerHigherWithTargetPositiveStatStages" }
|
||||
score -= 5
|
||||
end
|
||||
@@ -1182,7 +1182,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaisePlusMinusUserAndAll
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseGroundedGrassBattlersAtkSpAtk1",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if !b.pbHasType?(:GRASS) || b.airborne? || b.semiInvulnerable?
|
||||
next true if !target.has_type?(:GRASS) || target.battler.airborne? || target.battler.semiInvulnerable?
|
||||
next !target.battler.pbCanRaiseStatStage?(:ATTACK, user.battler, move.move) &&
|
||||
!target.battler.pbCanRaiseStatStage?(:SPECIAL_ATTACK, user.battler, move.move)
|
||||
}
|
||||
@@ -1198,7 +1198,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseGroundedGrassBattle
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseGrassBattlersDef1",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if !b.pbHasType?(:GRASS) || b.semiInvulnerable?
|
||||
next true if !target.has_type?(:GRASS) || target.battler.semiInvulnerable?
|
||||
next !target.battler.pbCanRaiseStatStage?(:DEFENSE, user.battler, move.move)
|
||||
}
|
||||
)
|
||||
@@ -1226,10 +1226,10 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTargetSwapAtkSpAtkSt
|
||||
end
|
||||
end
|
||||
next Battle::AI::MOVE_USELESS_SCORE if raises.length == 0 # No stat raises
|
||||
score += ai.get_score_for_target_stat_raise(score, user, raises, false, true) if raises.length > 0
|
||||
score += ai.get_score_for_target_stat_drop(score, target, raises, false, true) if raises.length > 0
|
||||
score += ai.get_score_for_target_stat_drop(score, user, drops, false, true) if drops.length > 0
|
||||
score += ai.get_score_for_target_stat_raise(score, target, drops, false, true) if drops.length > 0
|
||||
score = ai.get_score_for_target_stat_raise(score, user, raises, false, true) if raises.length > 0
|
||||
score = ai.get_score_for_target_stat_drop(score, target, raises, false, true) if raises.length > 0
|
||||
score = ai.get_score_for_target_stat_drop(score, user, drops, false, true) if drops.length > 0
|
||||
score = ai.get_score_for_target_stat_raise(score, target, drops, false, true) if drops.length > 0
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -1252,10 +1252,10 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTargetSwapDefSpDefSt
|
||||
end
|
||||
end
|
||||
next Battle::AI::MOVE_USELESS_SCORE if raises.length == 0 # No stat raises
|
||||
score += ai.get_score_for_target_stat_raise(score, user, raises, false, true) if raises.length > 0
|
||||
score += ai.get_score_for_target_stat_drop(score, target, raises, false, true) if raises.length > 0
|
||||
score += ai.get_score_for_target_stat_drop(score, user, drops, false, true) if drops.length > 0
|
||||
score += ai.get_score_for_target_stat_raise(score, target, drops, false, true) if drops.length > 0
|
||||
score = ai.get_score_for_target_stat_raise(score, user, raises, false, true) if raises.length > 0
|
||||
score = ai.get_score_for_target_stat_drop(score, target, raises, false, true) if raises.length > 0
|
||||
score = ai.get_score_for_target_stat_drop(score, user, drops, false, true) if drops.length > 0
|
||||
score = ai.get_score_for_target_stat_raise(score, target, drops, false, true) if drops.length > 0
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -1278,10 +1278,10 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTargetSwapStatStages
|
||||
end
|
||||
end
|
||||
next Battle::AI::MOVE_USELESS_SCORE if raises.length == 0 # No stat raises
|
||||
score += ai.get_score_for_target_stat_raise(score, user, raises, false, true) if raises.length > 0
|
||||
score += ai.get_score_for_target_stat_drop(score, target, raises, false, true) if raises.length > 0
|
||||
score += ai.get_score_for_target_stat_drop(score, user, drops, false, true) if drops.length > 0
|
||||
score += ai.get_score_for_target_stat_raise(score, target, drops, false, true) if drops.length > 0
|
||||
score = ai.get_score_for_target_stat_raise(score, user, raises, false, true) if raises.length > 0
|
||||
score = ai.get_score_for_target_stat_drop(score, target, raises, false, true) if raises.length > 0
|
||||
score = ai.get_score_for_target_stat_drop(score, user, drops, false, true) if drops.length > 0
|
||||
score = ai.get_score_for_target_stat_raise(score, target, drops, false, true) if drops.length > 0
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -1304,8 +1304,8 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserCopyTargetStatStages
|
||||
end
|
||||
end
|
||||
next Battle::AI::MOVE_USELESS_SCORE if raises.length == 0 # No stat raises
|
||||
score += ai.get_score_for_target_stat_raise(score, user, raises, false, true) if raises.length > 0
|
||||
score += ai.get_score_for_target_stat_drop(score, user, drops, false, true) if drops.length > 0
|
||||
score = ai.get_score_for_target_stat_raise(score, user, raises, false, true) if raises.length > 0
|
||||
score = ai.get_score_for_target_stat_drop(score, user, drops, false, true) if drops.length > 0
|
||||
if Settings::NEW_CRITICAL_HIT_RATE_MECHANICS
|
||||
if user.effects[PBEffects::FocusEnergy] > 0 && target.effects[PBEffects::FocusEnergy] == 0
|
||||
score -= 4
|
||||
@@ -1337,8 +1337,8 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserStealTargetPositiveS
|
||||
raises.push(target.stages[s.id])
|
||||
end
|
||||
if raises.length > 0
|
||||
score += ai.get_score_for_target_stat_raise(score, user, raises, false)
|
||||
score += ai.get_score_for_target_stat_drop(score, target, raises, false, true)
|
||||
score = ai.get_score_for_target_stat_raise(score, user, raises, false)
|
||||
score = ai.get_score_for_target_stat_drop(score, target, raises, false, true)
|
||||
end
|
||||
next score
|
||||
}
|
||||
@@ -1366,8 +1366,8 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("InvertTargetStatStages",
|
||||
end
|
||||
end
|
||||
next Battle::AI::MOVE_USELESS_SCORE if drops.length == 0 # No stats will drop
|
||||
score += ai.get_score_for_target_stat_raise(score, target, raises, false, true) if raises.length > 0
|
||||
score += ai.get_score_for_target_stat_drop(score, target, drops, false, true) if drops.length > 0
|
||||
score = ai.get_score_for_target_stat_raise(score, target, raises, false, true) if raises.length > 0
|
||||
score = ai.get_score_for_target_stat_drop(score, target, drops, false, true) if drops.length > 0
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -1388,8 +1388,8 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("ResetTargetStatStages",
|
||||
raises.push(target.stages[s.id])
|
||||
end
|
||||
end
|
||||
score += ai.get_score_for_target_stat_raise(score, target, raises, false, true) if raises.length > 0
|
||||
score += ai.get_score_for_target_stat_drop(score, target, drops, false, true) if drops.length > 0
|
||||
score = ai.get_score_for_target_stat_raise(score, target, raises, false, true) if raises.length > 0
|
||||
score = ai.get_score_for_target_stat_drop(score, target, drops, false, true) if drops.length > 0
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -1416,8 +1416,8 @@ Battle::AI::Handlers::MoveEffectScore.add("ResetAllBattlersStatStages",
|
||||
raises.push(b.stages[s.id])
|
||||
end
|
||||
end
|
||||
score += ai.get_score_for_target_stat_raise(score, b, raises, false, true) if raises.length > 0
|
||||
score += ai.get_score_for_target_stat_drop(score, b, drops, false, true) if drops.length > 0
|
||||
score = ai.get_score_for_target_stat_raise(score, b, raises, false, true) if raises.length > 0
|
||||
score = ai.get_score_for_target_stat_drop(score, b, drops, false, true) if drops.length > 0
|
||||
end
|
||||
next score
|
||||
}
|
||||
|
||||
@@ -783,7 +783,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("SetUserTypesToTargetTyp
|
||||
next true if !user.battler.canChangeType?
|
||||
next true if target.battler.pbTypes(true).empty?
|
||||
next true if user.battler.pbTypes == target.battler.pbTypes &&
|
||||
user.effects[PBEffects::Type3] == target.effects[PBEffects::Type3]
|
||||
user.effects[PBEffects::ExtraType] == target.effects[PBEffects::ExtraType]
|
||||
next false
|
||||
}
|
||||
)
|
||||
|
||||
@@ -157,7 +157,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HealUserByTargetAttackLo
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Check whether lowering the target's Attack will have any impact
|
||||
if ai.trainer.medium_skill?
|
||||
score = ai.get_score_for_target_stat_drop(score, target, [:ATTACK, 1])
|
||||
score = ai.get_score_for_target_stat_drop(score, target, move.move.statDown)
|
||||
end
|
||||
# Consider how much HP will be restored
|
||||
heal_amt = target.rough_stat(:ATTACK)
|
||||
|
||||
@@ -506,21 +506,24 @@ Battle::AI::Handlers::MoveFailureCheck.add("UseRandomUserMoveIfAsleep",
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("ReplaceMoveThisBattleWithTargetLastMoveUsed",
|
||||
proc { |move, user, ai, battle|
|
||||
next user.effects[PBEffects::Transform] || !user.battler.pbHasMove?(move.id)
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("ReplaceMoveThisBattleWithTargetLastMoveUsed",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if user.effects[PBEffects::Transform] || !user.battler.pbHasMove?(move.id)
|
||||
if user.faster_than?(target)
|
||||
last_move_data = GameData::Move.try_get(target.battler.lastRegularMoveUsed)
|
||||
next true if !last_move_data ||
|
||||
user.battler.pbHasMove?(target.battler.lastRegularMoveUsed) ||
|
||||
move.move.moveBlacklist.include?(last_move_data.function_code) ||
|
||||
last_move_data.type == :SHADOW
|
||||
end
|
||||
next false if !user.faster_than?(target)
|
||||
last_move_data = GameData::Move.try_get(target.battler.lastRegularMoveUsed)
|
||||
next true if !last_move_data ||
|
||||
user.battler.pbHasMove?(target.battler.lastRegularMoveUsed) ||
|
||||
move.move.moveBlacklist.include?(last_move_data.function_code) ||
|
||||
last_move_data.type == :SHADOW
|
||||
next false
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("ReplaceMoveThisBattleWithTargetLastMoveUsed",
|
||||
proc { |score, move, user, ai, battle|
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("ReplaceMoveThisBattleWithTargetLastMoveUsed",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Generally don't prefer, as this wastes the user's turn just to gain a move
|
||||
# of unknown utility
|
||||
score -= 8
|
||||
|
||||
@@ -28,7 +28,10 @@
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:shiny_target,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
score -= 40 if target.wild? && target.battler.shiny?
|
||||
if target.wild? && target.battler.shiny?
|
||||
PBDebug.log_score_change(-40, "avoid attacking a shiny wild Pokémon")
|
||||
score -= 40
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -47,10 +50,16 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:add_predicted_damage,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if move.damagingMove?
|
||||
dmg = move.rough_damage
|
||||
score += [20.0 * dmg / target.hp, 25].min
|
||||
score += 10 if dmg > target.hp * 1.1 # Predicted to KO the target
|
||||
old_score = score
|
||||
score += ([30.0 * dmg / target.hp, 35].min).to_i
|
||||
PBDebug.log_score_change(score - old_score, "damaging move (predicted damage #{dmg} = #{100 * dmg / target.hp}% of target's HP)")
|
||||
if dmg > target.hp * 1.1 # Predicted to KO the target
|
||||
old_score = score
|
||||
score += 10
|
||||
PBDebug.log_score_change(score - old_score, "predicted to KO the target")
|
||||
end
|
||||
end
|
||||
next score.to_i
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
@@ -60,7 +69,13 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:add_predicted_damage,
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:move_accuracy,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score * move.rough_accuracy / 100.0
|
||||
acc = move.rough_accuracy.to_i
|
||||
if acc < 90
|
||||
old_score = score
|
||||
score -= (0.2 * (100 - acc)).to_i # -2 (89%) to -19 (1%)
|
||||
PBDebug.log_score_change(score - old_score, "accuracy (predicted #{acc}%)")
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
@@ -89,7 +104,11 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:target_semi_invulnerabl
|
||||
miss = false if move.move.hitsDivingTargets?
|
||||
end
|
||||
end
|
||||
next Battle::AI::MOVE_USELESS_SCORE if miss
|
||||
if miss
|
||||
old_score = score
|
||||
score = Battle::AI::MOVE_USELESS_SCORE
|
||||
PBDebug.log_score_change(score - old_score, "target is semi-invulnerable")
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
@@ -109,7 +128,9 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:thawing_move_against_fr
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.medium_skill? && target.status == :FROZEN
|
||||
if move.rough_type == :FIRE || (Settings::MECHANICS_GENERATION >= 6 && move.move.thawsUser?)
|
||||
score -= 30
|
||||
old_score = score
|
||||
score -= 15
|
||||
PBDebug.log_score_change(score - old_score, "thaws the target")
|
||||
end
|
||||
end
|
||||
next score
|
||||
@@ -137,7 +158,9 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:flinching_effects,
|
||||
(move.damagingMove? &&
|
||||
(user.has_active_item?([:KINGSROCK, :RAZORFANG]) ||
|
||||
user.has_active_ability?(:STENCH)))
|
||||
score += 20
|
||||
old_score = score
|
||||
score += 8
|
||||
PBDebug.log_score_change(score - old_score, "flinching")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -166,7 +189,11 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:flinching_effects,
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:dance_move_against_dancer,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
score /= 2 if move.move.danceMove? && target.has_active_ability?(:DANCER)
|
||||
if move.move.danceMove? && target.has_active_ability?(:DANCER)
|
||||
old_score = score
|
||||
score -= 12
|
||||
PBDebug.log_score_change(score - old_score, "don't want to use a dance move on a target with Dancer")
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -199,8 +226,10 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:avoid_knocking_out_dest
|
||||
if ai.trainer.medium_skill? && move.damagingMove? && target.effects[PBEffects::DestinyBond]
|
||||
dmg = move.rough_damage
|
||||
if dmg > target.hp * 1.05 # Predicted to KO the target
|
||||
score -= 25
|
||||
score -= 20 if battle.pbAbleNonActiveCount(user.idxOwnSide) == 0
|
||||
old_score = score
|
||||
score -= 15
|
||||
score -= 10 if battle.pbAbleNonActiveCount(user.idxOwnSide) == 0
|
||||
PBDebug.log_score_change(score - old_score, "don't want to KO the Destiny Bonding target")
|
||||
end
|
||||
end
|
||||
next score
|
||||
@@ -243,14 +272,13 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:avoid_knocking_out_dest
|
||||
Battle::AI::Handlers::GeneralMoveScore.add(:thawing_move_when_frozen,
|
||||
proc { |score, move, user, ai, battle|
|
||||
if ai.trainer.medium_skill? && user.status == :FROZEN
|
||||
old_score = score
|
||||
if move.move.thawsUser?
|
||||
score += 30
|
||||
else
|
||||
user.battler.eachMove do |m|
|
||||
next unless m.thawsUser?
|
||||
score -= 30 # Don't prefer this move if user knows another move that thaws
|
||||
break
|
||||
end
|
||||
PBDebug.log_score_change(score - old_score, "move will thaw the user")
|
||||
elsif user.check_for_move { |m| m.thawsUser? }
|
||||
score -= 30 # Don't prefer this move if user knows another move that thaws
|
||||
PBDebug.log_score_change(score - old_score, "user knows another move will thaw it")
|
||||
end
|
||||
end
|
||||
next score
|
||||
@@ -267,7 +295,11 @@ Battle::AI::Handlers::GeneralMoveScore.add(:good_move_for_choice_item,
|
||||
if user.has_active_item?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF]) ||
|
||||
user.has_active_ability?(:GORILLATACTICS)
|
||||
# Really don't prefer status moves (except Trick)
|
||||
score *= 0.1 if move.statusMove? && move.function != "UserTargetSwapItems"
|
||||
if move.statusMove? && move.function != "UserTargetSwapItems"
|
||||
old_score = score
|
||||
score -= 25
|
||||
PBDebug.log_score_change(score - old_score, "move is not suitable to be Choiced into")
|
||||
end
|
||||
# Don't prefer moves of certain types
|
||||
move_type = move.rough_type
|
||||
# Most unpreferred types are 0x effective against another type, except
|
||||
@@ -280,11 +312,13 @@ Battle::AI::Handlers::GeneralMoveScore.add(:good_move_for_choice_item,
|
||||
# very effective against Dragon.
|
||||
unpreferred_types = [:NORMAL, :FIGHTING, :POISON, :GROUND, :GHOST,
|
||||
:FIRE, :WATER, :GRASS, :ELECTRIC, :PSYCHIC, :DRAGON]
|
||||
score *= 0.95 if unpreferred_types.include?(move_type)
|
||||
old_score = score
|
||||
score -= 5 if unpreferred_types.include?(move_type)
|
||||
# Don't prefer moves with lower accuracy
|
||||
score *= move.accuracy / 100.0 if move.accuracy > 0
|
||||
score = score * move.accuracy / 100 if move.accuracy > 0
|
||||
# Don't prefer moves with low PP
|
||||
score *= 0.9 if move.move.pp < 6
|
||||
score -= 10 if move.move.pp < 6
|
||||
PBDebug.log_score_change(score - old_score, "move is less suitable to be Choiced into")
|
||||
end
|
||||
end
|
||||
next score
|
||||
@@ -305,10 +339,13 @@ Battle::AI::Handlers::GeneralMoveScore.add(:prefer_damaging_moves_if_last_pokemo
|
||||
# Don't mess with scores just because a move is damaging; need to play well
|
||||
next score if ai.trainer.high_skill? && foes > reserves # AI is outnumbered
|
||||
# Prefer damaging moves depending on remaining Pokémon
|
||||
old_score = score
|
||||
if foes == 0 # Foe is down to their last Pokémon
|
||||
score *= 1.1 # => Go for the kill
|
||||
score += 10 # => Go for the kill
|
||||
PBDebug.log_score_change(score - old_score, "prefer damaging moves (no foe party Pokémon left)")
|
||||
elsif reserves == 0 # AI is down to its last Pokémon, foe has reserves
|
||||
score *= 1.05 # => Go out with a bang
|
||||
score += 5 # => Go out with a bang
|
||||
PBDebug.log_score_change(score - old_score, "prefer damaging moves (no ally party Pokémon left)")
|
||||
end
|
||||
end
|
||||
next score
|
||||
|
||||
@@ -40,6 +40,10 @@ class Battle::AI::AIBattler
|
||||
return @ai.battle.wildBattle? && opposes?
|
||||
end
|
||||
|
||||
def name
|
||||
return sprintf("%s (%d)", @battler.name, @index)
|
||||
end
|
||||
|
||||
def opposes?(other = nil)
|
||||
return @side == 1 if other.nil?
|
||||
return other.side != @side
|
||||
@@ -184,7 +188,7 @@ class Battle::AI::AIBattler
|
||||
#=============================================================================
|
||||
|
||||
def types; return @battler.types; end
|
||||
def pbTypes(withType3 = false); return @battler.pbTypes(withType3); end
|
||||
def pbTypes(withExtraType = false); return @battler.pbTypes(withExtraType); end
|
||||
|
||||
def has_type?(type)
|
||||
return false if !type
|
||||
@@ -208,7 +212,7 @@ class Battle::AI::AIBattler
|
||||
# TODO: Need to check the move's pbCalcTypeModSingle.
|
||||
ret *= effectiveness_of_type_against_single_battler_type(type, defend_type, user)
|
||||
end
|
||||
ret *= 2 if target.effects[PBEffects::TarShot] && type == :FIRE
|
||||
ret *= 2 if @battler.effects[PBEffects::TarShot] && type == :FIRE
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
@@ -396,7 +396,7 @@ class Battle::AI::AIMove
|
||||
# OHKO move accuracy
|
||||
if @move.is_a?(Battle::Move::OHKO)
|
||||
ret = self.accuracy + user.level - target.level
|
||||
ret -= 10 if function == "OHKOIce" && !user.pbHasType?(:ICE)
|
||||
ret -= 10 if function == "OHKOIce" && !user.has_type?(:ICE)
|
||||
return [ret, 0].max
|
||||
end
|
||||
# "Always hit" effects and "always hit" accuracy
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
# TODO: Better randomisation of moves, including tracking of how many times each
|
||||
# function code has been tested (note that some Pokémon may not be used in
|
||||
# battle, so their moves won't be score).
|
||||
# TODO: Add held items.
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
def debug_set_up_trainer
|
||||
# Values to return
|
||||
trainer_array = []
|
||||
@@ -6,7 +14,7 @@ def debug_set_up_trainer
|
||||
party_starts = [0]
|
||||
|
||||
# Choose random trainer type and trainer name
|
||||
trainer_type = GameData::TrainerType.keys.sample
|
||||
trainer_type = :CHAMPION # GameData::TrainerType.keys.sample
|
||||
trainer_name = ["Alpha", "Bravo", "Charlie", "Delta", "Echo",
|
||||
"Foxtrot", "Golf", "Hotel", "India", "Juliette",
|
||||
"Kilo", "Lima", "Mike", "November", "Oscar",
|
||||
@@ -24,8 +32,12 @@ def debug_set_up_trainer
|
||||
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)
|
||||
this_level = 100 # rand(1, Settings::MAXIMUM_LEVEL)
|
||||
pkmn = Pokemon.new(this_species, this_level, trainer, false)
|
||||
all_moves = pkmn.getMoveList.map { |m| m[1] }
|
||||
all_moves.uniq!
|
||||
moves = all_moves.sample(4)
|
||||
moves.each { |m| pkmn.learn_move(m) }
|
||||
trainer.party.push(pkmn)
|
||||
pokemon_array.push(pkmn)
|
||||
end
|
||||
@@ -37,7 +49,7 @@ end
|
||||
def debug_test_auto_battle(logging = false)
|
||||
old_internal = $INTERNAL
|
||||
$INTERNAL = logging
|
||||
echoln "Start of testing auto battle."
|
||||
echoln "Start of testing auto-battle."
|
||||
echoln "" if !$INTERNAL
|
||||
PBDebug.log("")
|
||||
PBDebug.log("================================================================")
|
||||
@@ -51,7 +63,7 @@ def debug_test_auto_battle(logging = false)
|
||||
trainer_txt = "[Trainer #{index}] #{trainer.full_name} [skill: #{trainer.skill_level}]"
|
||||
($INTERNAL) ? PBDebug.log_header(trainer_txt) : echoln(trainer_txt)
|
||||
party.each do |pkmn|
|
||||
pkmn_txt = "* #{pkmn.name}, Lv.#{pkmn.level}"
|
||||
pkmn_txt = "#{pkmn.name}, Lv.#{pkmn.level}"
|
||||
pkmn_txt += " [Ability: #{pkmn.ability&.name || "---"}]"
|
||||
pkmn_txt += " [Item: #{pkmn.item&.name || "---"}]"
|
||||
($INTERNAL) ? PBDebug.log(pkmn_txt) : echoln(pkmn_txt)
|
||||
@@ -85,18 +97,19 @@ def debug_test_auto_battle(logging = false)
|
||||
# Perform the battle itself
|
||||
outcome = battle.pbStartBattle
|
||||
# End
|
||||
echoln ["Undecided",
|
||||
text = ["Undecided",
|
||||
"Trainer 1 #{player_trainers[0].name} won",
|
||||
"Trainer 2 #{foe_trainers[0].name} won",
|
||||
"Ran/forfeited",
|
||||
"Wild Pokémon caught",
|
||||
"Draw"][outcome]
|
||||
echoln sprintf("%s after %d rounds", text, battle.turnCount + 1)
|
||||
echoln ""
|
||||
$INTERNAL = old_internal
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
# Add to Debug menu
|
||||
# Add to Debug menu.
|
||||
#===============================================================================
|
||||
MenuHandlers.add(:debug_menu, :test_auto_battle, {
|
||||
"name" => _INTL("Test Auto Battle"),
|
||||
@@ -115,5 +128,6 @@ MenuHandlers.add(:debug_menu, :test_auto_battle_logging, {
|
||||
"always_show" => false,
|
||||
"effect" => proc {
|
||||
debug_test_auto_battle(true)
|
||||
pbMessage(_INTL("Battle transcript was logged in Data/debuglog.txt."))
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user