More AI checks and fixes

This commit is contained in:
Maruno17
2022-09-10 15:26:38 +01:00
parent 9bd31300ad
commit eb8fc1d298
7 changed files with 254 additions and 215 deletions

View File

@@ -71,15 +71,22 @@ end
# (Belly Drum) # (Belly Drum)
#=============================================================================== #===============================================================================
class Battle::Move::MaxUserAttackLoseHalfOfTotalHP < Battle::Move class Battle::Move::MaxUserAttackLoseHalfOfTotalHP < Battle::Move
attr_reader :statUp
def canSnatch?; return true; end def canSnatch?; return true; end
def initialize(battle, move)
super
@statUp = [:ATTACK, 12]
end
def pbMoveFailed?(user, targets) def pbMoveFailed?(user, targets)
hpLoss = [user.totalhp / 2, 1].max hpLoss = [user.totalhp / 2, 1].max
if user.hp <= hpLoss if user.hp <= hpLoss
@battle.pbDisplay(_INTL("But it failed!")) @battle.pbDisplay(_INTL("But it failed!"))
return true return true
end end
return true if !user.pbCanRaiseStatStage?(:ATTACK, user, self, true) return true if !user.pbCanRaiseStatStage?(@statUp[0], user, self, true)
return false return false
end end
@@ -87,16 +94,18 @@ class Battle::Move::MaxUserAttackLoseHalfOfTotalHP < Battle::Move
hpLoss = [user.totalhp / 2, 1].max hpLoss = [user.totalhp / 2, 1].max
user.pbReduceHP(hpLoss, false, false) user.pbReduceHP(hpLoss, false, false)
if user.hasActiveAbility?(:CONTRARY) if user.hasActiveAbility?(:CONTRARY)
user.stages[:ATTACK] = -6 user.stages[@statUp[0]] = -6
user.statsLoweredThisRound = true user.statsLoweredThisRound = true
user.statsDropped = true user.statsDropped = true
@battle.pbCommonAnimation("StatDown", user) @battle.pbCommonAnimation("StatDown", user)
@battle.pbDisplay(_INTL("{1} cut its own HP and minimized its Attack!", user.pbThis)) @battle.pbDisplay(_INTL("{1} cut its own HP and minimized its {2}!",
user.pbThis, GameData::Stat.get(@statUp[0]).name))
else else
user.stages[:ATTACK] = 6 user.stages[@statUp[0]] = 6
user.statsRaisedThisRound = true user.statsRaisedThisRound = true
@battle.pbCommonAnimation("StatUp", user) @battle.pbCommonAnimation("StatUp", user)
@battle.pbDisplay(_INTL("{1} cut its own HP and maximized its Attack!", user.pbThis)) @battle.pbDisplay(_INTL("{1} cut its own HP and maximized its {2}!",
user.pbThis, GameData::Stat.get(@statUp[0]).name))
end end
user.pbItemHPHealCheck user.pbItemHPHealCheck
end end

View File

@@ -99,9 +99,6 @@ class Battle::AI
@target = (target) ? @battlers[target.index] : @user @target = (target) ? @battlers[target.index] : @user
@target&.refresh_battler @target&.refresh_battler
@battle.moldBreaker = @user.has_mold_breaker? @battle.moldBreaker = @user.has_mold_breaker?
# Determine whether user or target is faster, and store that result so it
# doesn't need recalculating
@user_faster = @user.faster_than?(@target)
end end
#============================================================================= #=============================================================================
@@ -110,7 +107,6 @@ class Battle::AI
# TODO: Add skill checks in here for particular calculations? # TODO: Add skill checks in here for particular calculations?
#============================================================================= #=============================================================================
def pbPredictMoveFailure def pbPredictMoveFailure
return false if !@trainer.has_skill_flag?("PredictMoveFailure")
# TODO: Something involving user.usingMultiTurnAttack? (perhaps earlier than # TODO: Something involving user.usingMultiTurnAttack? (perhaps earlier than
# this?). # this?).
# User is asleep and will not wake up # User is asleep and will not wake up
@@ -152,20 +148,22 @@ class Battle::AI
#============================================================================= #=============================================================================
def pbGetMoveScore(move, target = nil) def pbGetMoveScore(move, target = nil)
set_up_move_check(move, target) set_up_move_check(move, target)
user_battler = @user.battler
target_battler = @target.battler
# Predict whether the move will fail # Predict whether the move will fail
return 25 if pbPredictMoveFailure if @trainer.has_skill_flag?("PredictMoveFailure")
return 25 if pbPredictMoveFailure
end
# Get the base score for the move # Get the base score for the move
if @move.damagingMove? score = 100
# Is also the predicted damage amount as a percentage of target's current HP # if @move.damagingMove?
score = pbGetDamagingMoveBaseScore # # Is also the predicted damage amount as a percentage of target's current HP
else # Status moves # score = pbGetDamagingMoveBaseScore
# Depends on the move's effect # else # Status moves
score = pbGetStatusMoveBaseScore # # Depends on the move's effect
end # score = pbGetStatusMoveBaseScore
# end
# Modify the score according to the move's effect # Modify the score according to the move's effect
score = Battle::AI::Handlers.apply_move_effect_score(@move.function, score = Battle::AI::Handlers.apply_move_effect_score(@move.function,
score, @move, @user, @target, self, @battle) score, @move, @user, @target, self, @battle)
@@ -200,10 +198,10 @@ class Battle::AI
if @trainer.high_skill? && @user.can_switch_lax? if @trainer.high_skill? && @user.can_switch_lax?
badMoves = false badMoves = false
if (max_score <= 25 && user_battler.turnCount > 2) || if (max_score <= 25 && user_battler.turnCount > 2) ||
(max_score <= 50 && user_battler.turnCount > 5) (max_score <= 60 && user_battler.turnCount > 4)
badMoves = true if pbAIRandom(100) < 80 badMoves = true if pbAIRandom(100) < 80
end end
if !badMoves && max_score < 50 && user_battler.turnCount >= 1 if !badMoves && max_score <= 60 && user_battler.turnCount >= 1
badMoves = choices.none? { |c| user_battler.moves[c[0]].damagingMove? } badMoves = choices.none? { |c| user_battler.moves[c[0]].damagingMove? }
badMoves = false if badMoves && pbAIRandom(100) < 10 badMoves = false if badMoves && pbAIRandom(100) < 10
end end
@@ -222,11 +220,7 @@ class Battle::AI
if $INTERNAL if $INTERNAL
PBDebug.log("[AI] Move choices for #{user_battler.pbThis(true)} (#{user_battler.index}):") PBDebug.log("[AI] Move choices for #{user_battler.pbThis(true)} (#{user_battler.index}):")
choices.each_with_index do |c, i| choices.each_with_index do |c, i|
chance = "0" chance = sprintf("%5.1f", (c[3] > 0) ? 100.0 * c[3] / total_score : 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 = " * #{chance}% chance: #{user_battler.moves[c[0]].name}"
log_msg += " (against target #{c[2]})" if c[2] >= 0 log_msg += " (against target #{c[2]})" if c[2] >= 0
log_msg += " = score #{c[1]}" log_msg += " = score #{c[1]}"

View File

@@ -31,7 +31,7 @@ class Battle::AI
mod2 = Effectiveness::NORMAL_EFFECTIVE mod2 = Effectiveness::NORMAL_EFFECTIVE
if pkmn.types.length > 1 if pkmn.types.length > 1
mod2 = Effectiveness.calculate(pkmn.types[1], target_battler.types[0], target_battler.types[1]) mod2 = Effectiveness.calculate(pkmn.types[1], target_battler.types[0], target_battler.types[1])
mod2 = mod2.to_f / Effectivenesss::NORMAL_EFFECTIVE mod2 = mod2.to_f / Effectiveness::NORMAL_EFFECTIVE
end end
return mod1 * mod2 return mod1 * mod2
end end

View File

@@ -120,14 +120,14 @@ class Battle::AI
# Prefer if move is a status move and it's the user's first/second turn # Prefer if move is a status move and it's the user's first/second turn
if @user.turnCount < 2 && @move.statusMove? if @user.turnCount < 2 && @move.statusMove?
score += total_increment * 5 score += total_increment * 4
end end
# Prefer if user is at high HP, don't prefer if user is at low HP # Prefer if user is at high HP, don't prefer if user is at low HP
if @user.hp >= @user.totalhp * 0.7 if @user.hp >= @user.totalhp * 0.7
score += 10 * total_increment score += 4 * total_increment
else else
score += total_increment * ((100 * @user.hp / @user.totalhp) - 50) / 2 # +10 to -25 per stage score += total_increment * ((100 * @user.hp / @user.totalhp) - 50) / 4 # +5 to -12 per stage
end end
# Don't prefer if user is about to faint due to EOR damage # Don't prefer if user is about to faint due to EOR damage
@@ -234,69 +234,81 @@ class Battle::AI
# Make score changes based on the raising of a specific stat. # Make score changes based on the raising of a specific stat.
#============================================================================= #=============================================================================
def get_user_stat_raise_score_one(score, stat, increment) def get_user_stat_raise_score_one(score, stat, increment)
# Figure out how much the stat will actually change by
stage_mul = [2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8]
stage_div = [8, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2]
if [:ACCURACY, :EVASION].include?(stat)
stage_mul = [3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7, 8, 9]
stage_div = [9, 8, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3]
end
old_stage = @user.stages[stat]
new_stage = old_stage + increment
inc_mult = (stage_mul[new_stage].to_f * stage_div[old_stage]) / (stage_div[new_stage] * stage_mul[old_stage])
inc_mult -= 1
# Stat-based score changes
case stat case stat
when :ATTACK when :ATTACK
# Modify score depending on current stat stage # Modify score depending on current stat stage
# More strongly prefer if the user has no special moves # More strongly prefer if the user has no special moves
if @user.stages[stat] >= 3 if old_stage >= 3
score -= 20 score -= 20
else else
has_special_moves = @user.check_for_move { |m| m.specialMove?(m.type) } has_special_moves = @user.check_for_move { |m| m.specialMove?(m.type) }
inc = (has_special_moves) ? 5 : 10 inc = (has_special_moves) ? 5 : 10
score += inc * (3 - @user.stages[stat]) * increment # 5 to 45 score += inc * (3 - old_stage) * inc_mult
score += 5 * increment if @user.hp == @user.totalhp score += 5 * inc_mult if @user.hp == @user.totalhp
end end
when :DEFENSE when :DEFENSE
# Modify score depending on current stat stage # Modify score depending on current stat stage
if @user.stages[stat] >= 3 if old_stage >= 3
score -= 20 score -= 20
else else
score += 5 * (3 - @user.stages[stat]) * increment # 5 to 45 score += 5 * (3 - old_stage) * inc_mult
score += 5 * increment if @user.hp == @user.totalhp score += 5 * inc_mult if @user.hp == @user.totalhp
end end
when :SPECIAL_ATTACK when :SPECIAL_ATTACK
# Modify score depending on current stat stage # Modify score depending on current stat stage
# More strongly prefer if the user has no physical moves # More strongly prefer if the user has no physical moves
if @user.stages[stat] >= 3 if old_stage >= 3
score -= 20 score -= 20
else else
has_physical_moves = @user.check_for_move { |m| m.physicalMove?(m.type) && has_physical_moves = @user.check_for_move { |m| m.physicalMove?(m.type) &&
m.function != "UseUserBaseDefenseInsteadOfUserBaseAttack" && m.function != "UseUserBaseDefenseInsteadOfUserBaseAttack" &&
m.function != "UseTargetAttackInsteadOfUserAttack" } m.function != "UseTargetAttackInsteadOfUserAttack" }
inc = (has_physical_moves) ? 5 : 10 inc = (has_physical_moves) ? 5 : 10
score += inc * (3 - @user.stages[stat]) * increment # 5 to 45 score += inc * (3 - old_stage) * inc_mult
score += 5 * increment if @user.hp == @user.totalhp score += 5 * inc_mult if @user.hp == @user.totalhp
end end
when :SPECIAL_DEFENSE when :SPECIAL_DEFENSE
# Modify score depending on current stat stage # Modify score depending on current stat stage
if @user.stages[stat] >= 3 if old_stage >= 3
score -= 20 score -= 20
else else
score += 5 * (3 - @user.stages[stat]) * increment # 5 to 45 score += 5 * (3 - old_stage) * inc_mult
score += 5 * increment if @user.hp == @user.totalhp score += 5 * inc_mult if @user.hp == @user.totalhp
end end
when :SPEED when :SPEED
# Prefer if user is slower than a foe # Prefer if user is slower than a foe
each_foe_battler(@user.side) do |b, i| each_foe_battler(@user.side) do |b, i|
next if @user.faster_than?(b) next if @user.faster_than?(b)
score += 15 * increment score += 15 * inc_mult
break break
end end
# Don't prefer if any foe has Gyro Ball # Don't prefer if any foe has Gyro Ball
each_foe_battler(@user.side) do |b, i| each_foe_battler(@user.side) do |b, i|
next if !b.check_for_move { |m| m.function == "PowerHigherWithTargetFasterThanUser" } next if !b.check_for_move { |m| m.function == "PowerHigherWithTargetFasterThanUser" }
score -= 10 * increment score -= 8 * inc_mult
end end
# Don't prefer if user has Speed Boost (will be gaining Speed anyway) # Don't prefer if user has Speed Boost (will be gaining Speed anyway)
score -= 20 if @user.has_active_ability?(:SPEEDBOOST) score -= 20 if @user.has_active_ability?(:SPEEDBOOST)
when :ACCURACY when :ACCURACY
# Modify score depending on current stat stage # Modify score depending on current stat stage
if @user.stages[stat] >= 3 if old_stage >= 3
score -= 20 score -= 20
else else
min_accuracy = 100 min_accuracy = 100
@@ -304,12 +316,10 @@ class Battle::AI
next if m.accuracy == 0 || m.is_a?(Battle::Move::OHKO) next if m.accuracy == 0 || m.is_a?(Battle::Move::OHKO)
min_accuracy = m.accuracy if m.accuracy < min_accuracy min_accuracy = m.accuracy if m.accuracy < min_accuracy
end end
stageMul = [3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7, 8, 9] min_accuracy *= stage_mul[old_stage] / stage_div[old_stage]
stageDiv = [9, 8, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3]
min_accuracy *= stageMul[@user.stages[stat]] / stageDiv[@user.stages[stat]]
if min_accuracy < 90 if min_accuracy < 90
score += 5 * (3 - @user.stages[stat]) * increment # 5 to 45 score += 5 * (3 - old_stage) * inc_mult
score += 5 * increment if @user.hp == @user.totalhp score += 5 * inc_mult if @user.hp == @user.totalhp
end end
end end
@@ -323,26 +333,26 @@ class Battle::AI
score += 60 * eor_damage / b.totalhp if eor_damage > 0 score += 60 * eor_damage / b.totalhp if eor_damage > 0
end end
# Modify score depending on current stat stage # Modify score depending on current stat stage
if @user.stages[stat] >= 3 if old_stage >= 3
score -= 20 score -= 20
else else
score += 5 * (3 - @user.stages[stat]) * increment # 5 to 45 score += 5 * (3 - old_stage) * inc_mult
score += 5 * increment if @user.hp == @user.totalhp score += 5 * inc_mult if @user.hp == @user.totalhp
end end
end end
# Check impact on moves of gaining stat stages # Check impact on moves of gaining stat stages
pos_change = [@user.stages[stat] + increment, increment].min pos_change = [old_stage + increment, increment].min
if pos_change > 0 if pos_change > 0
# Prefer if user has Stored Power # Prefer if user has Stored Power
if @user.check_for_move { |m| m.function == "PowerHigherWithUserPositiveStatStages" } if @user.check_for_move { |m| m.function == "PowerHigherWithUserPositiveStatStages" }
score += 10 * pos_change score += 5 * pos_change
end end
# Don't prefer if any foe has Punishment # Don't prefer if any foe has Punishment
each_foe_battler(@user.side) do |b, i| each_foe_battler(@user.side) do |b, i|
next if !b.check_for_move { |m| m.function == "PowerHigherWithTargetPositiveStatStages" } next if !b.check_for_move { |m| m.function == "PowerHigherWithTargetPositiveStatStages" }
score -= 10 * pos_change score -= 5 * pos_change
end end
end end
@@ -543,7 +553,7 @@ class Battle::AI
# TODO: Prefer if user is faster than the target. # TODO: Prefer if user is faster than the target.
# TODO: Is 1.3x for RaiseUserAtkDefAcc1 Coil (+Atk, +Def, +acc). # TODO: Is 1.3x for RaiseUserAtkDefAcc1 Coil (+Atk, +Def, +acc).
mini_score *= 1.5 if @user_faster mini_score *= 1.5 if @user.faster_than?(@target)
# TODO: Don't prefer if target is a higher level than the user # TODO: Don't prefer if target is a higher level than the user
if @target.level > @user.level + 5 if @target.level > @user.level + 5
mini_score *= 0.6 mini_score *= 0.6

View File

@@ -22,10 +22,15 @@ Battle::AI::Handlers::MoveEffectScore.copy("RaiseUserAttack1",
"RaiseUserAttack2") "RaiseUserAttack2")
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.copy("RaiseUserAttack2", Battle::AI::Handlers::MoveEffectScore.add("RaiseUserAttack2IfTargetFaints",
"RaiseUserAttack2IfTargetFaints") proc { |score, move, user, target, ai, battle|
if move.rough_damage >= target.hp * 0.9
next ai.get_score_for_user_stat_raise(score)
end
}
)
#=============================================================================== #===============================================================================
# #
@@ -36,13 +41,13 @@ Battle::AI::Handlers::MoveEffectScore.copy("RaiseUserAttack2",
"RaiseUserAttack3") "RaiseUserAttack3")
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.copy("RaiseUserAttack2IfTargetFaints", Battle::AI::Handlers::MoveEffectScore.copy("RaiseUserAttack2IfTargetFaints",
"RaiseUserAttack3IfTargetFaints") "RaiseUserAttack3IfTargetFaints")
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveFailureCheck.add("MaxUserAttackLoseHalfOfTotalHP", Battle::AI::Handlers::MoveFailureCheck.add("MaxUserAttackLoseHalfOfTotalHP",
proc { |move, user, target, ai, battle| proc { |move, user, target, ai, battle|
@@ -52,20 +57,9 @@ Battle::AI::Handlers::MoveFailureCheck.add("MaxUserAttackLoseHalfOfTotalHP",
) )
Battle::AI::Handlers::MoveEffectScore.add("MaxUserAttackLoseHalfOfTotalHP", Battle::AI::Handlers::MoveEffectScore.add("MaxUserAttackLoseHalfOfTotalHP",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
score += (6 - user.stages[:ATTACK]) * 10 score = ai.get_score_for_user_stat_raise(score)
if ai.trainer.medium_skill? # Don't prefer the lower the user's HP is
hasPhysicalAttack = false score -= 80 * (1 - (@user.hp.to_f / @user.totalhp)) # 0 to -40
user.battler.eachMove do |m|
next if !m.physicalMove?(m.type)
hasPhysicalAttack = true
break
end
if hasPhysicalAttack
score += 40
elsif ai.trainer.high_skill?
score -= 90
end
end
next score next score
} }
) )
@@ -190,12 +184,37 @@ Battle::AI::Handlers::MoveEffectScore.copy("RaiseUserSpeed1",
"RaiseUserSpeed2") "RaiseUserSpeed2")
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveFailureCheck.copy("RaiseUserSpeed2", Battle::AI::Handlers::MoveFailureCheck.copy("RaiseUserSpeed2",
"RaiseUserSpeed2LowerUserWeight") "RaiseUserSpeed2LowerUserWeight")
Battle::AI::Handlers::MoveEffectScore.copy("RaiseUserSpeed2", Battle::AI::Handlers::MoveEffectScore.add("RaiseUserSpeed2LowerUserWeight",
"RaiseUserSpeed2LowerUserWeight") proc { |score, move, user, target, ai, battle|
score = ai.get_score_for_user_stat_raise(score)
if ai.trainer.medium_skill?
# TODO: Take into account weight-modifying items/abilities? This "> 1"
# line can probably ignore them, but these moves' powers will change
# because of those modifiers, and the score changes may need to be
# different accordingly.
if user.battler.pokemon.weight - user.effects[PBEffects::WeightChange] > 1
if user.check_for_move { |m| m.function == "PowerHigherWithUserHeavierThanTarget" }
score -= 10
end
ai.each_foe_battler(user.side) do |b|
if b.check_for_move { |m| m.function == "PowerHigherWithUserHeavierThanTarget" }
score -= 10
end
if b.check_for_move { |m| m.function == "PowerHigherWithTargetWeight" }
score += 10
end
# TODO: Check foes for Sky Drop and whether the user is too heavy for it
# but the weight reduction will make it susceptible.
end
end
end
next score
}
)
#=============================================================================== #===============================================================================
# #
@@ -246,12 +265,24 @@ Battle::AI::Handlers::MoveEffectScore.copy("RaiseUserEvasion1",
"RaiseUserEvasion2") "RaiseUserEvasion2")
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveFailureCheck.copy("RaiseUserEvasion2", Battle::AI::Handlers::MoveFailureCheck.copy("RaiseUserEvasion2",
"RaiseUserEvasion2MinimizeUser") "RaiseUserEvasion2MinimizeUser")
Battle::AI::Handlers::MoveEffectScore.copy("RaiseUserEvasion2", Battle::AI::Handlers::MoveEffectScore.add("RaiseUserEvasion2MinimizeUser",
"RaiseUserEvasion2MinimizeUser") proc { |score, move, user, target, ai, battle|
score = ai.get_score_for_user_stat_raise(score)
if ai.trainer.medium_skill? && !user.effects[PBEffects::Minimize]
ai.each_foe_battler(user.side) do |b|
# Moves that do double damage and (in Gen 6+) have perfect accuracy
if b.check_for_move { |m| m.tramplesMinimize? }
score -= (Settings::MECHANICS_GENERATION >= 6) ? 15 : 10
end
end
end
next score
}
)
#=============================================================================== #===============================================================================
# #
@@ -262,7 +293,7 @@ Battle::AI::Handlers::MoveEffectScore.copy("RaiseUserEvasion2",
"RaiseUserEvasion3") "RaiseUserEvasion3")
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveFailureCheck.add("RaiseUserCriticalHitRate2", Battle::AI::Handlers::MoveFailureCheck.add("RaiseUserCriticalHitRate2",
proc { |move, user, target, ai, battle| proc { |move, user, target, ai, battle|
@@ -271,9 +302,22 @@ Battle::AI::Handlers::MoveFailureCheck.add("RaiseUserCriticalHitRate2",
) )
Battle::AI::Handlers::MoveEffectScore.add("RaiseUserCriticalHitRate2", Battle::AI::Handlers::MoveEffectScore.add("RaiseUserCriticalHitRate2",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
if move.statusMove? || user.effects[PBEffects::FocusEnergy] < 2 next score - 40 if !user.check_for_move { |m| m.damagingMove? }
next score + 30 score += 15
if ai.trainer.medium_skill?
# Other effects that raise the critical hit rate
if user.item_active?
if [:RAZORCLAW, :SCOPELENS].include?(user.item_id) ||
(user.item_id == :LUCKYPUNCH && user.battler.isSpecies?(:CHANSEY)) ||
([:LEEK, :STICK].include?(user.item_id) &&
(user.battler.isSpecies?(:FARFETCHD) || user.battler.isSpecies?(:SIRFETCHD)))
score += 10
end
end
# Critical hits do more damage
score += 10 if user.has_active_ability?(:SNIPER)
end end
next score
} }
) )
@@ -1438,44 +1482,31 @@ Battle::AI::Handlers::MoveEffectScore.add("RaiseGrassBattlersDef1",
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.add("UserTargetSwapAtkSpAtkStages", Battle::AI::Handlers::MoveEffectScore.add("UserTargetSwapAtkSpAtkStages",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
if ai.trainer.medium_skill? user_attack = user.stages[:ATTACK]
aatk = user.stages[:ATTACK] user_spatk = user.stages[:SPECIAL_ATTACK]
aspa = user.stages[:SPECIAL_ATTACK] target_attack = target.stages[:ATTACK]
oatk = target.stages[:ATTACK] target_spatk = target.stages[:SPECIAL_ATTACK]
ospa = target.stages[:SPECIAL_ATTACK] next score - 40 if user_attack >= target_attack && user_spatk >= target_spatk
if aatk >= oatk && aspa >= ospa next score - 20 if user_attack + user_spatk <= target_attack + target_spatk
score -= 80 score += (target_attack - user_attack) * 10
else score += (target_spatk - user_spatk) * 10
score += (oatk - aatk) * 10
score += (ospa - aspa) * 10
end
else
score -= 50
end
next score next score
} }
) )
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. # TODO: Review score modifiers.
# TODO: Review score modifiers.
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.add("UserTargetSwapDefSpDefStages", Battle::AI::Handlers::MoveEffectScore.add("UserTargetSwapDefSpDefStages",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
if ai.trainer.medium_skill? user_def = user.stages[:DEFENSE]
adef = user.stages[:DEFENSE] user_spdef = user.stages[:SPECIAL_DEFENSE]
aspd = user.stages[:SPECIAL_DEFENSE] target_def = target.stages[:DEFENSE]
odef = target.stages[:DEFENSE] target_spdef = target.stages[:SPECIAL_DEFENSE]
ospd = target.stages[:SPECIAL_DEFENSE] next score - 40 if user_def >= target_def && user_spdef >= target_spdef
if adef >= odef && aspd >= ospd next score - 20 if user_def + user_spdef <= target_def + target_spdef
score -= 80 score += (target_def - user_def) * 10
else score += (target_spdef - user_spdef) * 10
score += (odef - adef) * 10
score += (ospd - aspd) * 10
end
else
score -= 50
end
next score next score
} }
) )
@@ -1485,17 +1516,16 @@ Battle::AI::Handlers::MoveEffectScore.add("UserTargetSwapDefSpDefStages",
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.add("UserTargetSwapStatStages", Battle::AI::Handlers::MoveEffectScore.add("UserTargetSwapStatStages",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
if ai.trainer.medium_skill? user_stages = 0
userStages = 0 target_stages = 0
targetStages = 0 target_stage_better = false
GameData::Stat.each_battle do |s| GameData::Stat.each_battle do |s|
userStages += user.stages[s.id] user_stages += user.stages[s.id]
targetStages += target.stages[s.id] target_stages += target.stages[s.id]
end target_stage_better = true if target.stages[s.id] > user.stages[s.id]
score += (targetStages - userStages) * 10
else
score -= 50
end end
next score - 40 if !target_stage_better
score += (target_stages - user_stages) * 10
next score next score
} }
) )
@@ -1505,17 +1535,13 @@ Battle::AI::Handlers::MoveEffectScore.add("UserTargetSwapStatStages",
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.add("UserCopyTargetStatStages", Battle::AI::Handlers::MoveEffectScore.add("UserCopyTargetStatStages",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
if ai.trainer.medium_skill? equal = true
equal = true GameData::Stat.each_battle do |s|
GameData::Stat.each_battle do |s| stagediff = target.stages[s.id] - user.stages[s.id]
stagediff = target.stages[s.id] - user.stages[s.id] score += stagediff * 10
score += stagediff * 10 equal = false if stagediff != 0
equal = false if stagediff != 0
end
score -= 80 if equal
else
score -= 50
end end
next 60 if equal # No stat changes
next score next score
} }
) )
@@ -1584,19 +1610,17 @@ Battle::AI::Handlers::MoveFailureCheck.add("ResetAllBattlersStatStages",
) )
Battle::AI::Handlers::MoveEffectScore.add("ResetAllBattlersStatStages", Battle::AI::Handlers::MoveEffectScore.add("ResetAllBattlersStatStages",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
if ai.trainer.medium_skill? stages = 0
stages = 0 battle.allBattlers.each do |b|
battle.allBattlers.each do |b| totalStages = 0
totalStages = 0 GameData::Stat.each_battle { |s| totalStages += b.stages[s.id] }
GameData::Stat.each_battle { |s| totalStages += b.stages[s.id] } if b.opposes?(user.battler)
if b.opposes?(user.battler) stages += totalStages
stages += totalStages else
else stages -= totalStages
stages -= totalStages
end
end end
next score + stages * 10
end end
next score + stages * 10
} }
) )
@@ -1614,19 +1638,13 @@ Battle::AI::Handlers::MoveFailureCheck.add("StartUserSideImmunityToStatStageLowe
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.add("UserSwapBaseAtkDef", Battle::AI::Handlers::MoveEffectScore.add("UserSwapBaseAtkDef",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
if ai.trainer.medium_skill? aatk = user.rough_stat(:ATTACK)
aatk = user.rough_stat(:ATTACK) adef = user.rough_stat(:DEFENSE)
adef = user.rough_stat(:DEFENSE) next score - 40 if aatk == adef || user.effects[PBEffects::PowerTrick] # No flip-flopping
if aatk == adef || if adef > aatk # Prefer a higher Attack
user.effects[PBEffects::PowerTrick] # No flip-flopping score += 20
score -= 90
elsif adef > aatk # Prefer a higher Attack
score += 30
else
score -= 30
end
else else
score -= 30 score -= 20
end end
next score next score
} }
@@ -1637,12 +1655,10 @@ Battle::AI::Handlers::MoveEffectScore.add("UserSwapBaseAtkDef",
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.add("UserTargetSwapBaseSpeed", Battle::AI::Handlers::MoveEffectScore.add("UserTargetSwapBaseSpeed",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
if ai.trainer.medium_skill? if user.speed > target.speed
if user.speed > target.speed score += 25
score += 50 else
else score -= 25
score -= 70
end
end end
next score next score
} }
@@ -1653,20 +1669,15 @@ Battle::AI::Handlers::MoveEffectScore.add("UserTargetSwapBaseSpeed",
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.add("UserTargetAverageBaseAtkSpAtk", Battle::AI::Handlers::MoveEffectScore.add("UserTargetAverageBaseAtkSpAtk",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
if ai.trainer.medium_skill? user_atk = user.battler.attack
aatk = user.rough_stat(:ATTACK) user_spatk = user.battler.spatk
aspatk = user.rough_stat(:SPECIAL_ATTACK) target_atk = target.battler.attack
oatk = target.rough_stat(:ATTACK) target_spatk = target.battler.spatk
ospatk = target.rough_stat(:SPECIAL_ATTACK) next score - 40 if user_atk > target_atk && user_spatk > target_spatk
if aatk < oatk && aspatk < ospatk if user_atk + user_spatk < target_atk + target_spatk
score += 50 score += 20
elsif aatk + aspatk < oatk + ospatk
score += 30
else
score -= 50
end
else else
score -= 30 score -= 20
end end
next score next score
} }
@@ -1677,20 +1688,15 @@ Battle::AI::Handlers::MoveEffectScore.add("UserTargetAverageBaseAtkSpAtk",
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.add("UserTargetAverageBaseDefSpDef", Battle::AI::Handlers::MoveEffectScore.add("UserTargetAverageBaseDefSpDef",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
if ai.trainer.medium_skill? user_def = user.rough_stat(:DEFENSE)
adef = user.rough_stat(:DEFENSE) user_spdef = user.rough_stat(:SPECIAL_DEFENSE)
aspdef = user.rough_stat(:SPECIAL_DEFENSE) target_def = target.rough_stat(:DEFENSE)
odef = target.rough_stat(:DEFENSE) target_spdef = target.rough_stat(:SPECIAL_DEFENSE)
ospdef = target.rough_stat(:SPECIAL_DEFENSE) next score - 40 if user_def > target_def && user_spdef > target_spdef
if adef < odef && aspdef < ospdef if user_def + user_spdef < target_def + target_spdef
score += 50 score += 20
elsif adef + aspdef < odef + ospdef
score += 30
else
score -= 50
end
else else
score -= 30 score -= 20
end end
next score next score
} }
@@ -1701,12 +1707,10 @@ Battle::AI::Handlers::MoveEffectScore.add("UserTargetAverageBaseDefSpDef",
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.add("UserTargetAverageHP", Battle::AI::Handlers::MoveEffectScore.add("UserTargetAverageHP",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
if target.effects[PBEffects::Substitute] > 0 if user.hp >= (user.hp + target.hp) / 2
score -= 90 score -= 25
elsif user.hp >= (user.hp + target.hp) / 2
score -= 90
else else
score += 40 score += 25
end end
next score next score
} }

View File

@@ -56,11 +56,25 @@ Battle::AI::Handlers::GeneralMoveScore.add(:dance_move_against_dancer,
# lowered offences (Atk/Def or SpAtk/SpDef, whichever is relevant). # lowered offences (Atk/Def or SpAtk/SpDef, whichever is relevant).
#=============================================================================== #===============================================================================
# Don't prefer damaging moves that will knock out the target if they are using
# Destiny Bond.
# TODO: Review score modifier. # TODO: Review score modifier.
#===============================================================================
# TODO: Don't prefer damaging moves if target is Destiny Bonding.
# => Also don't prefer damaging moves if user is slower than the target, move # => Also don't prefer damaging moves if user is slower than the target, move
# is likely to be lethal, and target has previously used Destiny Bond # is likely to be lethal, and target has previously used Destiny Bond
#===============================================================================
Battle::AI::Handlers::GeneralMoveScore.add(:avoid_knocking_out_destiny_bonder,
proc { |score, move, user, target, ai, battle|
if ai.trainer.medium_skill? && move.damagingMove? &&
target && 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
end
next score
end
}
)
#=============================================================================== #===============================================================================
# TODO: Review score modifier. # TODO: Review score modifier.
@@ -88,24 +102,26 @@ Battle::AI::Handlers::GeneralMoveScore.add(:dance_move_against_dancer,
# an effect that's good for the user (Poison Touch/Pickpocket). # an effect that's good for the user (Poison Touch/Pickpocket).
#=============================================================================== #===============================================================================
# Prefer damaging moves if the foe is down to their last Pokémon (opportunistic).
# Prefer damaging moves if the AI is down to its last Pokémon but the foe has
# more (desperate).
# TODO: Review score modifier. # TODO: Review score modifier.
#=============================================================================== #===============================================================================
# TODO: Don't prefer a status move if user has a damaging move that will KO Battle::AI::Handlers::GeneralMoveScore.add(:prefer_damaging_moves_if_last_pokemon,
# the target.
# => If target has previously used a move that will hurt the user by 30% of
# its current HP or more, moreso don't prefer a status move.
#===============================================================================
# Prefer damaging moves if AI has no more Pokémon or AI is less clever.
# TODO: Review score modifier.
#===============================================================================
Battle::AI::Handlers::GeneralMoveScore.add(:damaging_moves_if_last_pokemon,
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
if ai.trainer.medium_skill? && battle.pbAbleNonActiveCount(user.idxOwnSide) == 0 && if ai.trainer.medium_skill? && move.damagingMove?
!(ai.trainer.high_skill? && target && battle.pbAbleNonActiveCount(target.idxOwnSide) > 0) reserves = battle.pbAbleNonActiveCount(user.idxOwnSide)
next score * 0.9 if move.statusMove? foes = battle.pbAbleNonActiveCount(user.idxOpposingSide)
next score * 1.1 if target && target.battler.hp <= target.battler.totalhp / 2 # 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
if foes == 0 # Foe is down to their last Pokémon
score *= 1.1 # => Go for the kill
elsif reserves == 0 # AI is down to its last Pokémon, foe has reserves
score *= 1.05 # => Go out with a bang
end
end end
next score
} }
) )
@@ -256,7 +272,11 @@ Battle::AI::Handlers::GeneralMoveScore.add(:flinching_effects,
#=============================================================================== #===============================================================================
# Adjust score based on how much damage it can deal. # Adjust score based on how much damage it can deal.
# Prefer the move even more if it's predicted to do enough damage to KO the
# target.
# TODO: Review score modifier. # TODO: Review score modifier.
# => If target has previously used a move that will hurt the user by 30% of
# its current HP or more, moreso don't prefer a status move.
#=============================================================================== #===============================================================================
Battle::AI::Handlers::GeneralMoveScore.add(:add_predicted_damage, Battle::AI::Handlers::GeneralMoveScore.add(:add_predicted_damage,
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|

View File

@@ -48,14 +48,14 @@ def debug_test_auto_battle(logging = false)
player_trainers, ally_items, player_party, player_party_starts = debug_set_up_trainer player_trainers, ally_items, player_party, player_party_starts = debug_set_up_trainer
# Log the combatants # Log the combatants
echo_participant = lambda do |trainer, party, index| echo_participant = lambda do |trainer, party, index|
trainer_txt = "Trainer #{index}: #{trainer.full_name}" trainer_txt = "[Trainer #{index}] #{trainer.full_name} [skill: #{trainer.skill_level}]"
($INTERNAL) ? PBDebug.log_header(trainer_txt) : echoln(trainer_txt) ($INTERNAL) ? PBDebug.log_header(trainer_txt) : echoln(trainer_txt)
party.each do |pkmn| party.each do |pkmn|
pkmn_txt = " #{pkmn.name}, Lv.#{pkmn.level}\r\n" pkmn_txt = "* #{pkmn.name}, Lv.#{pkmn.level}"
pkmn_txt += " Ability: #{pkmn.ability&.name || "---"}\r\n" pkmn_txt += " [Ability: #{pkmn.ability&.name || "---"}]"
pkmn_txt += " Held item: #{pkmn.item&.name || "---"}" pkmn_txt += " [Item: #{pkmn.item&.name || "---"}]"
($INTERNAL) ? PBDebug.log(pkmn_txt) : echoln(pkmn_txt) ($INTERNAL) ? PBDebug.log(pkmn_txt) : echoln(pkmn_txt)
moves_msg = " Moves: " moves_msg = " Moves: "
pkmn.moves.each_with_index do |move, i| pkmn.moves.each_with_index do |move, i|
moves_msg += ", " if i > 0 moves_msg += ", " if i > 0
moves_msg += move.name moves_msg += move.name
@@ -64,6 +64,8 @@ def debug_test_auto_battle(logging = false)
end end
end end
echo_participant.call(player_trainers[0], player_party, 1) echo_participant.call(player_trainers[0], player_party, 1)
PBDebug.log("")
echoln "" if !$INTERNAL
echo_participant.call(foe_trainers[0], foe_party, 2) echo_participant.call(foe_trainers[0], foe_party, 2)
echoln "" if !$INTERNAL echoln "" if !$INTERNAL
# Create the battle scene (the visual side of it) # Create the battle scene (the visual side of it)