mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-08 21:54:58 +00:00
Tackling of various AI "TODO" comments, a little tidying
This commit is contained in:
@@ -91,13 +91,6 @@ module Battle::AI::Handlers
|
||||
MoveBasePower = HandlerHash.new
|
||||
GeneralMoveScore = HandlerHash.new
|
||||
GeneralMoveAgainstTargetScore = HandlerHash.new
|
||||
# TODO: Make HandlerHashes for these?
|
||||
# Move type - uses main battle code via rough_type
|
||||
# Move accuracy - uses main battle code via rough_accuracy
|
||||
# Move target
|
||||
# Move additional effect chance
|
||||
# Move unselectable check
|
||||
# Move failure check
|
||||
ShouldSwitch = HandlerHash.new
|
||||
|
||||
def self.move_will_fail?(function_code, *args)
|
||||
|
||||
@@ -33,32 +33,35 @@ class Battle::AI
|
||||
set_up_move_check(move)
|
||||
# Predict whether the move will fail (generally)
|
||||
if @trainer.has_skill_flag?("PredictMoveFailure") && pbPredictMoveFailure
|
||||
PBDebug.log_ai("#{@user.name} is considering using #{move.name}...")
|
||||
PBDebug.log_ai("#{@user.name} is considering using #{@move.name}...")
|
||||
PBDebug.log_score_change(MOVE_FAIL_SCORE - MOVE_BASE_SCORE, "move will fail")
|
||||
add_move_to_choices(choices, idxMove, MOVE_FAIL_SCORE)
|
||||
next
|
||||
end
|
||||
# Get the move's target type
|
||||
target_data = @move.pbTarget(@user.battler)
|
||||
# TODO: Alter target_data if user has Protean and move is Curse.
|
||||
if @move.function == "CurseTargetOrLowerUserSpd1RaiseUserAtkDef1" &&
|
||||
@move.rough_type == :GHOST && @user.has_active_ability?([:LIBERO, :PROTEAN])
|
||||
target_data = GameData::Target.get((Settings::MECHANICS_GENERATION >= 8) ? :RandomNearFoe : :NearFoe)
|
||||
end
|
||||
case target_data.num_targets
|
||||
when 0 # No targets, affects the user or a side or the whole field
|
||||
# Includes: BothSides, FoeSide, None, User, UserSide
|
||||
PBDebug.log_ai("#{@user.name} is considering using #{move.name}...")
|
||||
PBDebug.log_ai("#{@user.name} is considering using #{@move.name}...")
|
||||
score = MOVE_BASE_SCORE
|
||||
PBDebug.logonerr { score = pbGetMoveScore }
|
||||
add_move_to_choices(choices, idxMove, score)
|
||||
when 1 # One target to be chosen by the trainer
|
||||
# Includes: Foe, NearAlly, NearFoe, NearOther, Other, RandomNearFoe, UserOrNearAlly
|
||||
# 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.
|
||||
redirected_target = get_redirected_target(target_data)
|
||||
num_targets = 0
|
||||
@battle.allBattlers.each do |b|
|
||||
next if redirected_target && b.index != redirected_target
|
||||
next if !@battle.pbMoveCanTarget?(@user.battler.index, b.index, target_data)
|
||||
# TODO: Should this sometimes consider targeting an ally? See def
|
||||
# pbGetMoveScoreAgainstTarget for more information.
|
||||
next if target_data.targets_foe && !@user.battler.opposes?(b)
|
||||
PBDebug.log_ai("#{@user.name} is considering using #{move.name} against #{b.name} (#{b.index})...")
|
||||
PBDebug.log_ai("#{@user.name} is considering using #{@move.name} against #{b.name} (#{b.index})...")
|
||||
score = MOVE_BASE_SCORE
|
||||
PBDebug.logonerr { score = pbGetMoveScore([b]) }
|
||||
add_move_to_choices(choices, idxMove, score, b.index)
|
||||
@@ -72,7 +75,7 @@ class Battle::AI
|
||||
next if !@battle.pbMoveCanTarget?(@user.battler.index, b.index, target_data)
|
||||
targets.push(b)
|
||||
end
|
||||
PBDebug.log_ai("#{@user.name} is considering using #{move.name}...")
|
||||
PBDebug.log_ai("#{@user.name} is considering using #{@move.name}...")
|
||||
score = MOVE_BASE_SCORE
|
||||
PBDebug.logonerr { score = pbGetMoveScore(targets) }
|
||||
add_move_to_choices(choices, idxMove, score)
|
||||
@@ -82,6 +85,44 @@ class Battle::AI
|
||||
return choices
|
||||
end
|
||||
|
||||
def get_redirected_target(target_data)
|
||||
return nil if @move.move.cannotRedirect?
|
||||
return nil if !target_data.can_target_one_foe? || target_data.num_targets != 1
|
||||
return nil if @user.has_active_ability?([:PROPELLERTAIL, :STALWART])
|
||||
priority = @battle.pbPriority(true)
|
||||
near_only = !target_data.can_choose_distant_target?
|
||||
# Spotlight, Follow Me/Rage Powder
|
||||
new_target = -1
|
||||
strength = 100 # Lower strength takes priority
|
||||
priority.each do |b|
|
||||
next if b.fainted? || b.effects[PBEffects::SkyDrop] >= 0
|
||||
next if !b.opposes?(@user.battler)
|
||||
next if near_only && !b.near?(@user.battler)
|
||||
if b.effects[PBEffects::Spotlight] > 0 && b.effects[PBEffects::Spotlight] - 50 < strength
|
||||
new_target = b.index
|
||||
strength = b.effects[PBEffects::Spotlight] - 50 # Spotlight takes priority
|
||||
elsif (b.effects[PBEffects::RagePowder] && @user.battler.affectedByPowder?) ||
|
||||
(b.effects[PBEffects::FollowMe] > 0 && b.effects[PBEffects::FollowMe] < strength)
|
||||
new_target = b.index
|
||||
strength = b.effects[PBEffects::FollowMe]
|
||||
end
|
||||
end
|
||||
return new_target if new_target
|
||||
calc_type = @move.rough_type
|
||||
priority.each do |b|
|
||||
next if b.index == @user.index
|
||||
next if near_only && !b.near?(@user.battler)
|
||||
case calc_type
|
||||
when :ELECTRIC
|
||||
new_target = b.index if b.hasActiveAbility?(:LIGHTNINGROD)
|
||||
when :WATER
|
||||
new_target = b.index if b.hasActiveAbility?(:STORMDRAIN)
|
||||
end
|
||||
break if new_target >= 0
|
||||
end
|
||||
return new_target
|
||||
end
|
||||
|
||||
def add_move_to_choices(choices, idxMove, score, idxTarget = -1)
|
||||
choices.push([idxMove, score, idxTarget])
|
||||
# If the user is a wild Pokémon, doubly prefer one of its moves (the choice
|
||||
@@ -123,19 +164,16 @@ class Battle::AI
|
||||
#=============================================================================
|
||||
# Returns whether the move will definitely fail (assuming no battle conditions
|
||||
# change between now and using the move).
|
||||
# TODO: Add skill checks in here for particular calculations?
|
||||
#=============================================================================
|
||||
def pbPredictMoveFailure
|
||||
# TODO: Something involving user.battler.usingMultiTurnAttack? (perhaps
|
||||
# earlier than this?).
|
||||
# User is asleep and will not wake up
|
||||
return true if @user.battler.asleep? && @user.statusCount > 1 && !@move.move.usableWhenAsleep?
|
||||
# User is awake and can't use moves that are only usable when asleep
|
||||
return true if !@user.battler.asleep? && @move.move.usableWhenAsleep?
|
||||
# User will be truanting
|
||||
# TODO: Should Truanting treat all moves as failing? If it does, it will
|
||||
# trigger switching due to terrible moves.
|
||||
# return true if @user.has_active_ability?(:TRUANT) && @user.effects[PBEffects::Truant]
|
||||
# NOTE: Truanting is not considered, because if it is, a Pokémon with Truant
|
||||
# will want to switch due to terrible moves every other round (because
|
||||
# all of its moves will fail), and this is disruptive and shouldn't be
|
||||
# how such Pokémon behave.
|
||||
# Primal weather
|
||||
return true if @battle.pbWeather == :HeavyRain && @move.rough_type == :FIRE
|
||||
return true if @battle.pbWeather == :HarshSun && @move.rough_type == :WATER
|
||||
@@ -151,8 +189,7 @@ class Battle::AI
|
||||
return true if @battle.field.terrain == :Psychic && @target.battler.affectedByTerrain? &&
|
||||
@target.opposes?(@user) && @move.rough_priority(@user) > 0
|
||||
# Immunity because of ability
|
||||
# TODO: Check for target-redirecting abilities that also provide immunity.
|
||||
# If an ally has such an ability, may want to just not prefer the move
|
||||
# TODO: If an ally has such an ability, may want to just not prefer the move
|
||||
# instead of predicting its failure, as might want to hit the ally
|
||||
# after all.
|
||||
return true if @move.move.pbImmunityByAbility(@user.battler, @target.battler, false)
|
||||
@@ -212,7 +249,6 @@ class Battle::AI
|
||||
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
|
||||
|
||||
@@ -7,7 +7,8 @@ class Battle::AI
|
||||
# could target a foe but is targeting an ally, the score is also inverted, but
|
||||
# only because it is inverted again in def pbGetMoveScoreAgainstTarget.
|
||||
#=============================================================================
|
||||
def get_score_for_target_stat_raise(score, target, stat_changes, whole_effect = true, fixed_change = false)
|
||||
def get_score_for_target_stat_raise(score, target, stat_changes, whole_effect = true,
|
||||
fixed_change = false, ignore_contrary = false)
|
||||
whole_effect = false if @move.damagingMove?
|
||||
# Decide whether the target raising its stat(s) is a good thing
|
||||
desire_mult = 1
|
||||
@@ -15,13 +16,13 @@ class Battle::AI
|
||||
(@move.pbTarget(@user.battler).targets_foe && target.index != @user.index)
|
||||
desire_mult = -1
|
||||
end
|
||||
# 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 > 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
|
||||
# If target has Contrary, use different calculations to score the stat change
|
||||
if !ignore_contrary && !fixed_change && !@battle.moldBreaker && target.has_active_ability?(:CONTRARY)
|
||||
if desire_mult > 0 && whole_effect
|
||||
PBDebug.log_score_change(MOVE_USELESS_SCORE - score, "don't prefer raising target's stats (it has Contrary)")
|
||||
return MOVE_USELESS_SCORE
|
||||
end
|
||||
return get_score_for_target_stat_drop(score, target, stat_changes, whole_effect, fixed_change, true)
|
||||
end
|
||||
# Don't make score changes if target will faint from EOR damage
|
||||
if target.rough_end_of_round_damage >= target.hp
|
||||
@@ -42,7 +43,6 @@ class Battle::AI
|
||||
return ret
|
||||
end
|
||||
end
|
||||
|
||||
# Figure out which stat raises can happen
|
||||
real_stat_changes = []
|
||||
stat_changes.each_with_index do |stat, idx|
|
||||
@@ -66,10 +66,8 @@ class Battle::AI
|
||||
if real_stat_changes.length == 0
|
||||
return (whole_effect) ? MOVE_USELESS_SCORE : score
|
||||
end
|
||||
|
||||
# Make score changes based on the general concept of raising stats at all
|
||||
score = get_target_stat_raise_score_generic(score, target, real_stat_changes, desire_mult)
|
||||
|
||||
# Make score changes based on the specific changes to each stat that will be
|
||||
# raised
|
||||
real_stat_changes.each do |change|
|
||||
@@ -81,7 +79,6 @@ class Battle::AI
|
||||
PBDebug.log_score_change(score - old_score, "raising the target's #{GameData::Stat.get(change[0]).name} by #{change[1]}")
|
||||
end
|
||||
end
|
||||
|
||||
return score
|
||||
end
|
||||
|
||||
@@ -146,12 +143,10 @@ class Battle::AI
|
||||
# able to 2HKO the target.
|
||||
# TODO: Prefer if foe is semi-invulnerable and target is faster (can't hit
|
||||
# the foe anyway).
|
||||
|
||||
# Prefer if move is a status move and it's the user's first/second turn
|
||||
if @user.turnCount < 2 && @move.statusMove?
|
||||
score += total_increment * desire_mult * 4
|
||||
end
|
||||
|
||||
# Prefer if user is at high HP, don't prefer if user is at low HP
|
||||
if target.index != @user.index
|
||||
if @user.hp >= @user.totalhp * 0.7
|
||||
@@ -166,97 +161,8 @@ class Battle::AI
|
||||
else
|
||||
score += total_increment * desire_mult * ((100 * target.hp / target.totalhp) - 50) / 4 # +5 to -12 per stage
|
||||
end
|
||||
|
||||
# TODO: Look at abilities that trigger upon stat raise. There are none.
|
||||
|
||||
return score
|
||||
|
||||
|
||||
=begin
|
||||
mini_score = 1.0
|
||||
# Determine whether the move boosts Attack, Special Attack or Speed (Bulk Up
|
||||
# is sometimes not considered a sweeping move)
|
||||
sweeping_stat = false
|
||||
offensive_stat = false
|
||||
stat_changes.each do |change|
|
||||
next if ![:ATTACK, :SPECIAL_ATTACK, :SPEED].include?(change[0])
|
||||
sweeping_stat = true
|
||||
next if @move.function == "RaiseUserAtkDef1" # Bulk Up (+Atk +Def)
|
||||
offensive_stat = true
|
||||
break
|
||||
end
|
||||
|
||||
# TODO: Prefer if user's moves won't do much damage.
|
||||
# Prefer if user has something that will limit damage taken
|
||||
mini_score *= 1.3 if @user.effects[PBEffects::Substitute] > 0 ||
|
||||
(@user.form == 0 && @user.ability_id == :DISGUISE)
|
||||
|
||||
# Don't prefer if user is badly poisoned
|
||||
mini_score *= 0.2 if @user.effects[PBEffects::Toxic] > 0 && !offensive_stat
|
||||
# Don't prefer if user is confused
|
||||
if @user.effects[PBEffects::Confusion] > 0
|
||||
# TODO: Especially don't prefer if the move raises Atk. Even more so if
|
||||
# the move raises the stat by 2+. Not quite so much if the move also
|
||||
# raises Def.
|
||||
mini_score *= 0.5
|
||||
end
|
||||
# Don't prefer if user is infatuated or Leech Seeded
|
||||
if @user.effects[PBEffects::Attract] >= 0 || @user.effects[PBEffects::LeechSeed] >= 0
|
||||
mini_score *= (offensive_stat) ? 0.6 : 0.3
|
||||
end
|
||||
# Don't prefer if user has an ability or item that will force it to switch
|
||||
# out
|
||||
if @user.hp < @user.totalhp * 3 / 4
|
||||
mini_score *= 0.3 if @user.hasActiveAbility?([:EMERGENCYEXIT, :WIMPOUT])
|
||||
mini_score *= 0.3 if @user.hasActiveItem?(:EJECTBUTTON)
|
||||
end
|
||||
|
||||
# Prefer if target has a status problem
|
||||
if @target.status != :NONE
|
||||
mini_score *= (sweeping_stat) ? 1.2 : 1.1
|
||||
case @target.status
|
||||
when :SLEEP, :FROZEN
|
||||
mini_score *= 1.3
|
||||
when :BURN
|
||||
# TODO: Prefer if the move boosts Sp Def.
|
||||
mini_score *= 1.1 if !offensive_stat
|
||||
end
|
||||
end
|
||||
# Prefer if target is yawning
|
||||
if @target.effects[PBEffects::Yawn] > 0
|
||||
mini_score *= (sweeping_stat) ? 1.7 : 1.3
|
||||
end
|
||||
# Prefer if target is recovering after Hyper Beam
|
||||
if @target.effects[PBEffects::HyperBeam] > 0
|
||||
mini_score *= (sweeping_stat) ? 1.3 : 1.2
|
||||
end
|
||||
# Prefer if target is Encored into a status move
|
||||
if @target.effects[PBEffects::Encore] > 0 &&
|
||||
GameData::Move.get(@target.effects[PBEffects::EncoreMove]).category == 2 # Status move
|
||||
# TODO: Why should this check greatly prefer raising both the user's defences?
|
||||
if sweeping_stat || @move.function == "RaiseUserDefSpDef1" # +Def +SpDef
|
||||
mini_score *= 1.5
|
||||
else
|
||||
mini_score *= 1.3
|
||||
end
|
||||
end
|
||||
# TODO: Don't prefer if target has previously used a move that would force
|
||||
# the user to switch (or Yawn/Perish Song which encourage it). Prefer
|
||||
# instead if the move raises evasion. Note this comes after the
|
||||
# dissociation of Bulk Up from sweeping_stat.
|
||||
|
||||
if @trainer.medium_skill?
|
||||
# TODO: Prefer if the maximum damage the target has dealt wouldn't hurt
|
||||
# the user much.
|
||||
end
|
||||
|
||||
# Don't prefer if it's not a single battle
|
||||
if !@battle.singleBattle?
|
||||
mini_score *= (offensive_stat) ? 0.25 : 0.5
|
||||
end
|
||||
|
||||
return mini_score
|
||||
=end
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
@@ -287,7 +193,6 @@ class Battle::AI
|
||||
inc = (has_special_moves) ? 10 : 20
|
||||
score += inc * inc_mult
|
||||
end
|
||||
|
||||
when :DEFENSE
|
||||
# Modify score depending on current stat stage
|
||||
if old_stage >= 2 && increment == 1
|
||||
@@ -295,7 +200,6 @@ class Battle::AI
|
||||
else
|
||||
score += 10 * inc_mult
|
||||
end
|
||||
|
||||
when :SPECIAL_ATTACK
|
||||
# Modify score depending on current stat stage
|
||||
# More strongly prefer if the target has no physical moves
|
||||
@@ -308,7 +212,6 @@ class Battle::AI
|
||||
inc = (has_physical_moves) ? 10 : 20
|
||||
score += inc * inc_mult
|
||||
end
|
||||
|
||||
when :SPECIAL_DEFENSE
|
||||
# Modify score depending on current stat stage
|
||||
if old_stage >= 2 && increment == 1
|
||||
@@ -316,7 +219,6 @@ class Battle::AI
|
||||
else
|
||||
score += 10 * inc_mult
|
||||
end
|
||||
|
||||
when :SPEED
|
||||
# Prefer if target is slower than a foe
|
||||
# TODO: Don't prefer if the target is too much slower than any foe that it
|
||||
@@ -345,7 +247,6 @@ class Battle::AI
|
||||
if target.has_active_ability?(:SPEEDBOOST)
|
||||
score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
|
||||
end
|
||||
|
||||
when :ACCURACY
|
||||
# Modify score depending on current stat stage
|
||||
if old_stage >= 2 && increment == 1
|
||||
@@ -361,15 +262,11 @@ class Battle::AI
|
||||
score += 10 * inc_mult
|
||||
end
|
||||
end
|
||||
|
||||
when :EVASION
|
||||
# Prefer if a foe will (probably) take damage at the end of the round
|
||||
# TODO: Should this take into account EOR healing, one-off damage and
|
||||
# damage-causing effects that wear off naturally (like Sea of Fire)?
|
||||
# TODO: Emerald AI also prefers if target is rooted via Ingrain.
|
||||
# Prefer if a foe of the target will take damage at the end of the round
|
||||
each_foe_battler(target.side) do |b, i|
|
||||
eor_damage = b.rough_end_of_round_damage
|
||||
next if eor_damage <= 0
|
||||
score += 4 * inc_mult if eor_damage > 0
|
||||
end
|
||||
# Modify score depending on current stat stage
|
||||
if old_stage >= 2 && increment == 1
|
||||
@@ -377,9 +274,7 @@ class Battle::AI
|
||||
else
|
||||
score += 10 * inc_mult
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Prefer if target has Stored Power
|
||||
if target.has_move_with_function?("PowerHigherWithUserPositiveStatStages")
|
||||
score += 5 * increment * desire_mult
|
||||
@@ -389,212 +284,7 @@ class Battle::AI
|
||||
next if !b.has_move_with_function?("PowerHigherWithTargetPositiveStatStages")
|
||||
score -= 5 * increment * desire_mult
|
||||
end
|
||||
|
||||
return score
|
||||
|
||||
|
||||
=begin
|
||||
mini_score = 1.0
|
||||
case stat
|
||||
when :ATTACK
|
||||
# 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 &&
|
||||
# (@user.hasActiveItem?(:FOCUSSASH) || (!@battle.moldBreaker && @user.hasActiveAbility?(:STURDY)))
|
||||
# if !(@battle.pbWeather == :Sandstorm && @user.takesSandstormDamage?) &&
|
||||
# !(@battle.pbWeather == :Hail && @user.takesHailDamage?) &&
|
||||
# !(@battle.pbWeather == :ShadowSky && @user.takesShadowSkyDamage?)
|
||||
# mini_score *= 1.4
|
||||
# end
|
||||
# end
|
||||
# Prefer if user has the Sweeper role
|
||||
# TODO: Is 1.1x for RaiseUserAtkDefAcc1 Coil (+Atk, +Def, +acc).
|
||||
# mini_score *= 1.3 if check_battler_role(@user, BattleRole::SWEEPER)
|
||||
|
||||
# # Don't prefer if user is burned or paralysed
|
||||
# mini_score *= 0.5 if @user.status == :BURN || @user.status == :PARALYSIS
|
||||
# # Don't prefer if user's Speed stat is lowered
|
||||
# sum_stages = @user.stages[:SPEED]
|
||||
# mini_score *= 1 + sum_stages * 0.05 if sum_stages < 0
|
||||
# # TODO: Prefer if target has previously used a HP-restoring move.
|
||||
# # TODO: Don't prefer if some of foes' stats are raised.
|
||||
# sum_stages = 0
|
||||
# [:ATTACK, :SPECIAL_ATTACK, :SPEED].each do |s|
|
||||
# sum_stages += @target.stages[s]
|
||||
# end
|
||||
# mini_score *= 1 - sum_stages * 0.05 if sum_stages > 0
|
||||
# # TODO: Don't prefer if target has Speed Boost (+Spd at end of each round).
|
||||
# mini_score *= 0.6 if @target.hasActiveAbility?(:SPEEDBOOST)
|
||||
# # TODO: Don't prefer if the target has previously used a priority move.
|
||||
|
||||
when :DEFENSE
|
||||
# Prefer if user has a healing item
|
||||
# TODO: Is 1.1x for RaiseUserAtkDefAcc1 Coil (+Atk, +Def, +acc).
|
||||
# mini_score *= 1.2 if @user.hasActiveItem?(:LEFTOVERS) ||
|
||||
# (@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) { |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
|
||||
# mini_score *= 1.3 if @user.pbHasMoveFunction?("StartLeechSeedTarget") # Leech Seed
|
||||
# Prefer if user has certain roles
|
||||
# # TODO: Is 1.1x for RaiseUserAtkDefAcc1 Coil (+Atk, +Def, +acc).
|
||||
# mini_score *= 1.3 if check_battler_role(@user, BattleRole::PHYSICALWALL, BattleRole::SPECIALWALL)
|
||||
# Don't prefer if user is badly poisoned
|
||||
mini_score *= 0.2 if @user.effects[PBEffects::Toxic] > 0
|
||||
# # TODO: Prefer if foes have higher Attack than Special Attack, and user
|
||||
# # doesn't have a wall role, user is faster and user has at least 75%
|
||||
# # HP. Don't prefer instead if user is slower (ignore HP).
|
||||
# TODO: Don't prefer if previous damage done by foes wouldn't hurt the
|
||||
# user much.
|
||||
|
||||
when :SPEED
|
||||
# Don't prefer if user has Speed Boost
|
||||
# mini_score *= 0.6 if @user.hasActiveAbility?(:SPEEDBOOST)
|
||||
# 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 &&
|
||||
# (@user.hasActiveItem?(:FOCUSSASH) || (!@battle.moldBreaker && @user.hasActiveAbility?(:STURDY)))
|
||||
# if !(@battle.pbWeather == :Sandstorm && @user.takesSandstormDamage?) &&
|
||||
# !(@battle.pbWeather == :Hail && @user.takesHailDamage?) &&
|
||||
# !(@battle.pbWeather == :ShadowSky && @user.takesShadowSkyDamage?)
|
||||
# mini_score *= 1.4
|
||||
# end
|
||||
# end
|
||||
# Prefer if user has the Sweeper role
|
||||
# mini_score *= 1.3 if check_battler_role(@user, BattleRole::SWEEPER)
|
||||
# TODO: Don't prefer if Trick Room applies or any foe has previously used
|
||||
# Trick Room.
|
||||
# mini_score *= 0.2 if @battle.field.effects[PBEffects::TrickRoom] > 0
|
||||
|
||||
# # Prefer if user's Attack/SpAtk stat (whichever is higher) is lowered
|
||||
# # TODO: Why?
|
||||
# if @user.attack > @user.spatk
|
||||
# sum_stages = @user.stages[:ATTACK]
|
||||
# mini_score *= 1 - sum_stages * 0.05 if sum_stages < 0
|
||||
# else
|
||||
# sum_stages = @user.stages[:SPATK]
|
||||
# mini_score *= 1 - sum_stages * 0.05 if sum_stages < 0
|
||||
# end
|
||||
# # Prefer if user has Moxie
|
||||
# mini_score *= 1.3 if @user.hasActiveAbility?(:MOXIE)
|
||||
# # Don't prefer if user is burned or paralysed
|
||||
# mini_score *= 0.2 if @user.status == :PARALYSIS
|
||||
# # TODO: Don't prefer if target has raised defenses.
|
||||
# sum_stages = 0
|
||||
# [:DEFENSE, :SPECIAL_DEFENSE].each { |s| sum_stages += @target.stages[s] }
|
||||
# mini_score *= 1 - sum_stages * 0.05 if sum_stages > 0
|
||||
# # TODO: Don't prefer if the target has previously used a priority move.
|
||||
# # TODO: Don't prefer if user is already faster than the target and there's
|
||||
# # only 1 unfainted foe (this check is done by Agility/Autotomize
|
||||
# # (both +2 Spd) only in Reborn.)
|
||||
|
||||
when :SPECIAL_ATTACK
|
||||
# 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 &&
|
||||
# (@user.hasActiveItem?(:FOCUSSASH) || (!@battle.moldBreaker && @user.hasActiveAbility?(:STURDY)))
|
||||
# if !(@battle.pbWeather == :Sandstorm && @user.takesSandstormDamage?) &&
|
||||
# !(@battle.pbWeather == :Hail && @user.takesHailDamage?) &&
|
||||
# !(@battle.pbWeather == :ShadowSky && @user.takesShadowSkyDamage?)
|
||||
# mini_score *= 1.4
|
||||
# end
|
||||
# end
|
||||
# Prefer if user has the Sweeper role
|
||||
# mini_score *= 1.3 if check_battler_role(@user, BattleRole::SWEEPER)
|
||||
|
||||
# # Don't prefer if user's Speed stat is lowered
|
||||
# sum_stages = @user.stages[:SPEED]
|
||||
# mini_score *= 1 + sum_stages * 0.05 if sum_stages < 0
|
||||
# # TODO: Prefer if target has previously used a HP-restoring move.
|
||||
# # TODO: Don't prefer if some of foes' stats are raised
|
||||
# sum_stages = 0
|
||||
# [:ATTACK, :SPECIAL_ATTACK, :SPEED].each do |s|
|
||||
# sum_stages += @target.stages[s]
|
||||
# end
|
||||
# mini_score *= 1 - sum_stages * 0.05 if sum_stages > 0
|
||||
# # TODO: Don't prefer if target has Speed Boost (+Spd at end of each round)
|
||||
# mini_score *= 0.6 if @target.hasActiveAbility?(:SPEEDBOOST)
|
||||
# # TODO: Don't prefer if the target has previously used a priority move.
|
||||
|
||||
when :SPECIAL_DEFENSE
|
||||
# Prefer if user has a healing item
|
||||
# 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) { |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
|
||||
# Prefer if user has certain roles
|
||||
# mini_score *= 1.3 if check_battler_role(@user, BattleRole::PHYSICALWALL, BattleRole::SPECIALWALL)
|
||||
# # TODO: Prefer if foes have higher Special Attack than Attack.
|
||||
# TODO: Don't prefer if previous damage done by foes wouldn't hurt the
|
||||
# user much.
|
||||
|
||||
when :ACCURACY
|
||||
# Prefer if user knows any weaker moves
|
||||
mini_score *= 1.1 if check_for_move(@user) { |m| m.damagingMove? && m.power < 95 }
|
||||
# Prefer if target has a raised evasion
|
||||
sum_stages = @target.stages[:EVASION]
|
||||
mini_score *= 1 + sum_stages * 0.05 if sum_stages > 0
|
||||
# Prefer if target has an item that lowers foes' accuracy
|
||||
mini_score *= 1.1 if @target.hasActiveItem?([:BRIGHTPOWDER, :LAXINCENSE])
|
||||
# Prefer if target has an ability that lowers foes' accuracy
|
||||
# TODO: Tangled Feet while user is confused?
|
||||
if (@battle.pbWeather == :Sandstorm && @target.hasActiveAbility?(:SANDVEIL)) ||
|
||||
(@battle.pbWeather == :Hail && @target.hasActiveAbility?(:SNOWCLOAK))
|
||||
mini_score *= 1.1
|
||||
end
|
||||
|
||||
when :EVASION
|
||||
# Prefer if user has a healing item
|
||||
mini_score *= 1.2 if @user.hasActiveItem?(:LEFTOVERS) ||
|
||||
(@user.hasActiveItem?(:BLACKSLUDGE) && @user.pbHasType?(:POISON))
|
||||
# Prefer if user has an item that lowers foes' accuracy
|
||||
mini_score *= 1.3 if @user.hasActiveItem?([:BRIGHTPOWDER, :LAXINCENSE])
|
||||
# Prefer if user has an ability that lowers foes' accuracy
|
||||
# TODO: Tangled Feet while user is confused?
|
||||
if (@battle.pbWeather == :Sandstorm && @user.hasActiveAbility?(:SANDVEIL)) ||
|
||||
(@battle.pbWeather == :Hail && @user.hasActiveAbility?(:SNOWCLOAK))
|
||||
mini_score *= 1.3
|
||||
end
|
||||
# Prefer if user knows any healing moves
|
||||
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
|
||||
# Prefer if user has certain roles
|
||||
mini_score *= 1.3 if check_battler_role(@user, BattleRole::PHYSICALWALL, BattleRole::SPECIALWALL)
|
||||
# TODO: Don't prefer if user's evasion stat is raised
|
||||
# TODO: Don't prefer if target has No Guard.
|
||||
mini_score *= 0.2 if @target.hasActiveAbility?(:NOGUARD)
|
||||
# TODO: Don't prefer if target has previously used any moves that never miss.
|
||||
|
||||
end
|
||||
|
||||
|
||||
# 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) { |m|
|
||||
["ResetTargetStatStages", "ResetAllBattlersStatStages"].include?(m.function)
|
||||
} # Clear Smog, Haze
|
||||
|
||||
# TODO: Prefer if user is faster than the target.
|
||||
# TODO: Is 1.3x for RaiseUserAtkDefAcc1 Coil (+Atk, +Def, +acc).
|
||||
mini_score *= 1.5 if @user.faster_than?(@target)
|
||||
# TODO: Don't prefer if target is a higher level than the user
|
||||
if @target.level > @user.level + 5
|
||||
mini_score *= 0.6
|
||||
if @target.level > @user.level + 10
|
||||
mini_score *= 0.2
|
||||
end
|
||||
end
|
||||
|
||||
return mini_score
|
||||
=end
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
@@ -606,10 +296,9 @@ class Battle::AI
|
||||
# ally, but only because it is inverted in def pbGetMoveScoreAgainstTarget
|
||||
# instead.
|
||||
# TODO: Revisit this method as parts may need rewriting.
|
||||
# TODO: fixed_change should make this ignore Mist/Clear Body/other effects
|
||||
# that prevent increments/decrements to stat stages.
|
||||
#=============================================================================
|
||||
def get_score_for_target_stat_drop(score, target, stat_changes, whole_effect = true, fixed_change = false)
|
||||
def get_score_for_target_stat_drop(score, target, stat_changes, whole_effect = true,
|
||||
fixed_change = false, ignore_contrary = false)
|
||||
whole_effect = false if @move.damagingMove?
|
||||
# Decide whether the target lowering its stat(s) is a good thing
|
||||
desire_mult = -1
|
||||
@@ -617,13 +306,13 @@ class Battle::AI
|
||||
(@move.pbTarget(@user.battler).targets_foe && target.index != @user.index)
|
||||
desire_mult = 1
|
||||
end
|
||||
# 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 < 0.
|
||||
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
|
||||
# If target has Contrary, use different calculations to score the stat change
|
||||
if !ignore_contrary && !fixed_change && !@battle.moldBreaker && target.has_active_ability?(:CONTRARY)
|
||||
if desire_mult > 0 && whole_effect
|
||||
PBDebug.log_score_change(MOVE_USELESS_SCORE - score, "don't prefer lowering target's stats (it has Contrary)")
|
||||
return MOVE_USELESS_SCORE
|
||||
end
|
||||
return get_score_for_target_stat_raise(score, target, stat_changes, whole_effect, fixed_change, true)
|
||||
end
|
||||
# Don't make score changes if target will faint from EOR damage
|
||||
if target.rough_end_of_round_damage >= target.hp
|
||||
@@ -642,7 +331,6 @@ class Battle::AI
|
||||
PBDebug.log(" ignore stat change (target's foes have Unaware)")
|
||||
return ret
|
||||
end
|
||||
|
||||
# Figure out which stat drops can happen
|
||||
real_stat_changes = []
|
||||
stat_changes.each_with_index do |stat, idx|
|
||||
@@ -666,10 +354,8 @@ class Battle::AI
|
||||
if real_stat_changes.length == 0
|
||||
return (whole_effect) ? MOVE_USELESS_SCORE : score
|
||||
end
|
||||
|
||||
# Make score changes based on the general concept of lowering stats at all
|
||||
score = get_target_stat_drop_score_generic(score, target, real_stat_changes, desire_mult)
|
||||
|
||||
# Make score changes based on the specific changes to each stat that will be
|
||||
# lowered
|
||||
real_stat_changes.each do |change|
|
||||
@@ -681,7 +367,6 @@ class Battle::AI
|
||||
PBDebug.log_score_change(score - old_score, "lowering the target's #{GameData::Stat.get(change[0]).name} by #{change[1]}")
|
||||
end
|
||||
end
|
||||
|
||||
return score
|
||||
end
|
||||
|
||||
@@ -746,12 +431,10 @@ class Battle::AI
|
||||
# TODO: Don't prefer if target is slower than user but is predicted to be able
|
||||
# to 2HKO user.
|
||||
# TODO: Don't prefer if target is semi-invulnerable and user is faster.
|
||||
|
||||
# Prefer if move is a status move and it's the user's first/second turn
|
||||
if @user.turnCount < 2 && @move.statusMove?
|
||||
score += total_decrement * desire_mult * 4
|
||||
end
|
||||
|
||||
# Prefer if user is at high HP, don't prefer if user is at low HP
|
||||
if target.index != @user.index
|
||||
if @user.hp >= @user.totalhp * 0.7
|
||||
@@ -766,9 +449,7 @@ class Battle::AI
|
||||
else
|
||||
score += total_decrement * desire_mult * ((100 * target.hp / target.totalhp) - 50) / 6 # +3 to -8 per stage
|
||||
end
|
||||
|
||||
# TODO: Look at abilities that trigger upon stat lowering.
|
||||
|
||||
return score
|
||||
end
|
||||
|
||||
@@ -801,7 +482,6 @@ class Battle::AI
|
||||
dec = (has_special_moves) ? 5 : 10
|
||||
score += dec * dec_mult
|
||||
end
|
||||
|
||||
when :DEFENSE
|
||||
# Modify score depending on current stat stage
|
||||
if old_stage <= -2 && decrement == 1
|
||||
@@ -809,7 +489,6 @@ class Battle::AI
|
||||
else
|
||||
score += 5 * dec_mult
|
||||
end
|
||||
|
||||
when :SPECIAL_ATTACK
|
||||
# Modify score depending on current stat stage
|
||||
# More strongly prefer if the target has no physical moves
|
||||
@@ -822,7 +501,6 @@ class Battle::AI
|
||||
dec = (has_physical_moves) ? 5 : 10
|
||||
score += dec * dec_mult
|
||||
end
|
||||
|
||||
when :SPECIAL_DEFENSE
|
||||
# Modify score depending on current stat stage
|
||||
if old_stage <= -2 && decrement == 1
|
||||
@@ -830,7 +508,6 @@ class Battle::AI
|
||||
else
|
||||
score += 5 * dec_mult
|
||||
end
|
||||
|
||||
when :SPEED
|
||||
# Prefer if target is faster than an ally
|
||||
# TODO: Don't prefer if the target is too much faster than any ally and
|
||||
@@ -849,7 +526,6 @@ class Battle::AI
|
||||
if target.has_active_ability?(:SPEEDBOOST)
|
||||
score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult)
|
||||
end
|
||||
|
||||
when :ACCURACY
|
||||
# Modify score depending on current stat stage
|
||||
if old_stage <= -2 && decrement == 1
|
||||
@@ -858,7 +534,6 @@ class Battle::AI
|
||||
score += 5 * dec_mult
|
||||
end
|
||||
# TODO: Prefer if target is poisoned/toxiced/Leech Seeded/cursed.
|
||||
|
||||
when :EVASION
|
||||
# Modify score depending on current stat stage
|
||||
if old_stage <= -2 && decrement == 1
|
||||
@@ -866,9 +541,7 @@ class Battle::AI
|
||||
else
|
||||
score += 5 * dec_mult
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Prefer if target has Stored Power
|
||||
if target.has_move_with_function?("PowerHigherWithUserPositiveStatStages")
|
||||
score += 5 * decrement * desire_mult
|
||||
@@ -878,7 +551,6 @@ class Battle::AI
|
||||
next if !b.has_move_with_function?("PowerHigherWithTargetPositiveStatStages")
|
||||
score -= 5 * decrement * desire_mult
|
||||
end
|
||||
|
||||
return score
|
||||
end
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# because of them.
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifier.
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO:
|
||||
# => Don't prefer damaging move if it won't KO, user has Stance Change and
|
||||
@@ -24,13 +24,13 @@
|
||||
|
||||
#===============================================================================
|
||||
# Don't prefer hitting a wild shiny Pokémon.
|
||||
# TODO: Review score modifier.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:shiny_target,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.wild? && target.battler.shiny?
|
||||
PBDebug.log_score_change(-40, "avoid attacking a shiny wild Pokémon")
|
||||
score -= 40
|
||||
old_score = score
|
||||
score -= 15
|
||||
PBDebug.log_score_change(score - old_score, "avoid attacking a shiny wild Pokémon")
|
||||
end
|
||||
next score
|
||||
}
|
||||
@@ -51,7 +51,7 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:add_predicted_damage,
|
||||
if move.damagingMove?
|
||||
dmg = move.rough_damage
|
||||
old_score = score
|
||||
score += ([30.0 * dmg / target.hp, 35].min).to_i
|
||||
score += ([25.0 * dmg / target.hp, 30].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
|
||||
@@ -115,14 +115,13 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:target_semi_invulnerabl
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifier.
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Less prefer two-turn moves, as the foe can see it coming and prepare for
|
||||
# it.
|
||||
|
||||
#===============================================================================
|
||||
# If target is frozen, don't prefer moves that could thaw them.
|
||||
# TODO: Review score modifier.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:thawing_move_against_frozen_target,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
@@ -138,7 +137,7 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:thawing_move_against_fr
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifier.
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Prefer move if it has a high critical hit rate, critical hits are
|
||||
# possible but not certain, and target has raised defences/user has
|
||||
@@ -169,7 +168,7 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:flinching_effects,
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifier.
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Don't prefer contact move if making contact with the target could
|
||||
# trigger an effect that's bad for the user (Static, etc.).
|
||||
@@ -177,29 +176,13 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:flinching_effects,
|
||||
# Baneful Bunker, and don't prefer move if so
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifier.
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Prefer a contact move if making contact with the target could trigger
|
||||
# an effect that's good for the user (Poison Touch/Pickpocket).
|
||||
|
||||
#===============================================================================
|
||||
# Don't prefer a dancing move if the target has the Dancer ability.
|
||||
# TODO: Review score modifier.
|
||||
# TODO: Check all battlers, not just the target.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:dance_move_against_dancer,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
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
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifier.
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Prefer a higher priority move if the user is slower than the foe(s) and
|
||||
# the user is at risk of being knocked out. Consider whether the foe(s)
|
||||
@@ -207,12 +190,32 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:dance_move_against_danc
|
||||
# moves?
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifier.
|
||||
# Don't prefer damaging moves if the target is Biding, unless the move will deal
|
||||
# enough damage to KO the target before it retaliates (assuming the move is used
|
||||
# repeatedly until the target retaliates). Doesn't do a score change if the user
|
||||
# will be immune to Bide's damage.
|
||||
#===============================================================================
|
||||
# TODO: Don't prefer damaging moves if the target is Biding, unless the move
|
||||
# will deal enough damage to KO the target before it retaliates (assuming
|
||||
# the move is used repeatedly until the target retaliates). Don't worry
|
||||
# about the target's Bide if the user will be immune to it.
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:avoid_damaging_a_biding_target,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.medium_skill? && target.effects[PBEffects::Bide] > 0 && move.damagingMove?
|
||||
eff = user.effectiveness_of_type_against_battler(:NORMAL, target) # Bide is Normal type
|
||||
if !Effectiveness.ineffective?(eff)
|
||||
dmg = move.rough_damage
|
||||
eor_dmg = target.rough_end_of_round_damage
|
||||
hits_possible = target.effects[PBEffects::Bide] - 1
|
||||
eor_dmg *= hits_possible
|
||||
hits_possible += 1 if user.faster_than?(target)
|
||||
if dmg * hits_possible + eor_dmg < target.hp * 1.05
|
||||
old_score = score
|
||||
score -= 15
|
||||
PBDebug.log_score_change(score - old_score, "don't want to damage the Biding target")
|
||||
end
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# Don't prefer damaging moves that will knock out the target if they are using
|
||||
@@ -237,22 +240,53 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:avoid_knocking_out_dest
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifier.
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Don't prefer Fire-type moves if target has previously used Powder.
|
||||
# TODO: Don't prefer Fire-type moves if target has previously used Powder and is
|
||||
# faster than the user.
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifier.
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Check memory for whether target has previously used Ion Deluge, and
|
||||
# don't prefer move if it's Normal-type and target is immune because
|
||||
# of its ability (Lightning Rod, etc.).
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifier.
|
||||
# Don't prefer a move that can be Magic Coated if the target (or any foe if the
|
||||
# move doesn't have a target) has Magic Bounce.
|
||||
#===============================================================================
|
||||
# TODO: Don't prefer a move that can be Magic Coated if the target (or any foe
|
||||
# if the move doesn't have a target) has Magic Bounce.
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:avoid_targeting_bouncable_move_against_Magic_Bouncer,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if move.statusMove? && move.canMagicCoat? &&
|
||||
!battle.moldBreaker && target.has_active_ability?(:MAGICBOUNCE)
|
||||
old_score = score
|
||||
score = Battle::AI::MOVE_USELESS_SCORE
|
||||
PBDebug.log_score_change(score - old_score, "useless because target will Magic Bounce it")
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::GeneralMoveScore.add(:avoid_bouncable_move_with_foe_Magic_Bouncer,
|
||||
proc { |score, move, user, ai, battle|
|
||||
if move.statusMove? && move.canMagicCoat? &&
|
||||
move.pbTarget(user.battler).num_targets == 0 && !battle.moldBreaker
|
||||
has_magic_bounce = false
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
next if !b.has_active_ability?(:MAGICBOUNCE)
|
||||
has_magic_bounce = true
|
||||
break
|
||||
end
|
||||
if has_magic_bounce
|
||||
old_score = score
|
||||
score = Battle::AI::MOVE_USELESS_SCORE
|
||||
PBDebug.log_score_change(score - old_score, "useless because a foe will Magic Bounce it")
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#===============================================================================
|
||||
@@ -261,13 +295,12 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:avoid_knocking_out_dest
|
||||
#===============================================================================
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifier.
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Prefer Shadow moves.
|
||||
# TODO: Prefer Shadow moves (for flavour).
|
||||
|
||||
#===============================================================================
|
||||
# If user is frozen, prefer a move that can thaw the user.
|
||||
# TODO: Review score modifier.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveScore.add(:thawing_move_when_frozen,
|
||||
proc { |score, move, user, ai, battle|
|
||||
@@ -285,6 +318,22 @@ Battle::AI::Handlers::GeneralMoveScore.add(:thawing_move_when_frozen,
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# Don't prefer a dancing move if the target has the Dancer ability.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveScore.add(:dance_move_against_dancer,
|
||||
proc { |score, move, user, ai, battle|
|
||||
if move.move.danceMove?
|
||||
old_score = score
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
score -= 12 if b.has_active_ability?(:DANCER)
|
||||
end
|
||||
PBDebug.log_score_change(score - old_score, "don't want to use a dance move because a foe has Dancer")
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# Pick a good move for the Choice items.
|
||||
# TODO: Review score modifier.
|
||||
@@ -298,26 +347,18 @@ Battle::AI::Handlers::GeneralMoveScore.add(:good_move_for_choice_item,
|
||||
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")
|
||||
PBDebug.log_score_change(score - old_score, "don't want to be Choiced into a status move")
|
||||
next score
|
||||
end
|
||||
# Don't prefer moves of certain types
|
||||
# Don't prefer moves which are 0x against at least one type
|
||||
move_type = move.rough_type
|
||||
# Most unpreferred types are 0x effective against another type, except
|
||||
# Fire/Water/Grass
|
||||
# TODO: Actually check through the types for 0x instead of hardcoding
|
||||
# them.
|
||||
# TODO: Reborn separately doesn't prefer Fire/Water/Grass/Electric, also
|
||||
# with a 0.95x score, meaning Electric can be 0.95x twice. Why are
|
||||
# these four types not preferred? Maybe because they're all not
|
||||
# very effective against Dragon.
|
||||
unpreferred_types = [:NORMAL, :FIGHTING, :POISON, :GROUND, :GHOST,
|
||||
:FIRE, :WATER, :GRASS, :ELECTRIC, :PSYCHIC, :DRAGON]
|
||||
old_score = score
|
||||
score -= 5 if unpreferred_types.include?(move_type)
|
||||
GameData::Type.each do |type_data|
|
||||
score -= 5 if type_data.immunities.include?(move_type)
|
||||
end
|
||||
# Don't prefer moves with lower accuracy
|
||||
score = score * move.accuracy / 100 if move.accuracy > 0
|
||||
# Don't prefer moves with low PP
|
||||
score -= 10 if move.move.pp < 6
|
||||
score -= 10 if move.move.pp <= 5
|
||||
PBDebug.log_score_change(score - old_score, "move is less suitable to be Choiced into")
|
||||
end
|
||||
end
|
||||
@@ -353,13 +394,13 @@ Battle::AI::Handlers::GeneralMoveScore.add(:prefer_damaging_moves_if_last_pokemo
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifier.
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Don't prefer a move that is stopped by Wide Guard if any foe has
|
||||
# previously used Wide Guard.
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifier.
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Don't prefer sound move if user hasn't been Throat Chopped but a foe has
|
||||
# previously used Throat Chop.
|
||||
|
||||
@@ -181,7 +181,6 @@ class Battle::AI::AIBattler
|
||||
return ret
|
||||
end
|
||||
|
||||
# TODO: Cache calculated rough stats? Forget them in def refresh_battler.
|
||||
def rough_stat(stat)
|
||||
return @battler.pbSpeed if stat == :SPEED && @ai.trainer.high_skill?
|
||||
stageMul = [2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8]
|
||||
@@ -215,7 +214,8 @@ class Battle::AI::AIBattler
|
||||
end
|
||||
else
|
||||
@battler.pbTypes(true).each do |defend_type|
|
||||
# TODO: Need to check the move's pbCalcTypeModSingle.
|
||||
# TODO: Need to check the move's pbCalcTypeModSingle because particular
|
||||
# moves can modify that method to give different effectivenesses.
|
||||
ret *= effectiveness_of_type_against_single_battler_type(type, defend_type, user)
|
||||
end
|
||||
ret *= 2 if self.effects[PBEffects::TarShot] && type == :FIRE
|
||||
@@ -229,14 +229,10 @@ class Battle::AI::AIBattler
|
||||
def ability; return @battler.ability; end
|
||||
|
||||
def ability_active?
|
||||
# Only a high skill AI knows what an opponent's ability is
|
||||
# return false if @ai.trainer.side != @side && !@ai.trainer.high_skill?
|
||||
return @battler.abilityActive?
|
||||
end
|
||||
|
||||
def has_active_ability?(ability, ignore_fainted = false)
|
||||
# Only a high skill AI knows what an opponent's ability is
|
||||
# return false if @ai.trainer.side != @side && !@ai.trainer.high_skill?
|
||||
return @battler.hasActiveAbility?(ability, ignore_fainted)
|
||||
end
|
||||
|
||||
@@ -250,14 +246,10 @@ class Battle::AI::AIBattler
|
||||
def item; return @battler.item; end
|
||||
|
||||
def item_active?
|
||||
# Only a high skill AI knows what an opponent's held item is
|
||||
# return false if @ai.trainer.side != @side && !@ai.trainer.high_skill?
|
||||
return @battler.itemActive?
|
||||
end
|
||||
|
||||
def has_active_item?(item)
|
||||
# Only a high skill AI knows what an opponent's held item is
|
||||
# return false if @ai.trainer.side != @side && !@ai.trainer.high_skill?
|
||||
return @battler.hasActiveItem?(item)
|
||||
end
|
||||
|
||||
@@ -266,6 +258,7 @@ class Battle::AI::AIBattler
|
||||
def check_for_move
|
||||
ret = false
|
||||
@battler.eachMove do |move|
|
||||
next if move.pp == 0 && move.total_pp > 0
|
||||
next unless yield move
|
||||
ret = true
|
||||
break
|
||||
@@ -708,10 +701,9 @@ class Battle::AI::AIBattler
|
||||
# battler if it has it.
|
||||
# Return values are typically between -10 and +10. 0 is indifferent, positive
|
||||
# values mean this battler benefits, negative values mean this battler suffers.
|
||||
# TODO: This method assumes the ability isn't being negated. Should it return
|
||||
# 0 if it is? The calculations that call this method separately check
|
||||
# for it being negated, because they need to do something special in
|
||||
# that case, so I think it's okay for this method to ignore negation.
|
||||
# NOTE: This method assumes the ability isn't being negated. The calculations
|
||||
# that call this method separately check for it being negated, because
|
||||
# they need to do something special in that case.
|
||||
def wants_ability?(ability = :NONE)
|
||||
ability = ability.id if !ability.is_a?(Symbol) && ability.respond_to?("id")
|
||||
# TODO: Ideally replace the above list of ratings with context-sensitive
|
||||
@@ -729,9 +721,7 @@ class Battle::AI::AIBattler
|
||||
when :GALEWINGS
|
||||
return 0 if !check_for_move { |m| m.type == :FLYING }
|
||||
when :HUGEPOWER, :PUREPOWER
|
||||
return 0 if !check_for_move { |m| m.physicalMove?(m.type) &&
|
||||
m.function != "UseUserDefenseInsteadOfUserAttack" &&
|
||||
m.function != "UseTargetAttackInsteadOfUserAttack" }
|
||||
return 0 if !ai.stat_raise_worthwhile?(self, :ATTACK, true)
|
||||
when :IRONFIST
|
||||
return 0 if !check_for_move { |m| m.punchingMove? }
|
||||
when :LIQUIDVOICE
|
||||
|
||||
@@ -40,14 +40,14 @@ class Battle::AI::AIMove
|
||||
|
||||
#=============================================================================
|
||||
|
||||
def pbTarget(_user)
|
||||
return @move.pbTarget(_user)
|
||||
def pbTarget(user)
|
||||
return @move.pbTarget((user.is_a?(Battle::AI::AIBattler)) ? user.battler : user)
|
||||
end
|
||||
|
||||
# Returns whether this move targets multiple battlers.
|
||||
def targets_multiple_battlers?
|
||||
user_battler = @ai.user.battler
|
||||
target_data = @move.pbTarget(user_battler)
|
||||
target_data = pbTarget(user_battler)
|
||||
return false if target_data.num_targets <= 1
|
||||
num_targets = 0
|
||||
case target_data.id
|
||||
@@ -70,8 +70,12 @@ class Battle::AI::AIMove
|
||||
#=============================================================================
|
||||
|
||||
def rough_priority(user)
|
||||
# TODO: More calculations here.
|
||||
return @move.priority
|
||||
ret = @move.pbPriority(user.battler)
|
||||
if user.ability_active?
|
||||
ret = Battle::AbilityEffects.triggerPriorityChange(user.ability, user.battler, @move, ret)
|
||||
user.battler.effects[PBEffects::Prankster] = false # Untrigger this
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
@@ -205,7 +209,6 @@ class Battle::AI::AIMove
|
||||
)
|
||||
user.effects[PBEffects::GemConsumed] = nil # Untrigger consuming of Gems
|
||||
end
|
||||
# TODO: Prefer (1.5x?) if item will be consumed and user has Unburden.
|
||||
end
|
||||
if target.item_active? && target.item && !target.item.is_berry?
|
||||
Battle::ItemEffects.triggerDamageCalcFromTarget(
|
||||
@@ -456,11 +459,13 @@ class Battle::AI::AIMove
|
||||
end
|
||||
# Item effects that alter accuracy calculation
|
||||
if user.item_active?
|
||||
# TODO: Zoom Lens needs to be checked differently (compare speeds of
|
||||
# user and target).
|
||||
Battle::ItemEffects.triggerAccuracyCalcFromUser(
|
||||
user.item, modifiers, user_battler, target_battler, @move, calc_type
|
||||
)
|
||||
if user.item == :ZOOMLENS
|
||||
mods[:accuracy_multiplier] *= 1.2 if target.faster_than?(user)
|
||||
else
|
||||
Battle::ItemEffects.triggerAccuracyCalcFromUser(
|
||||
user.item, modifiers, user_battler, target_battler, @move, calc_type
|
||||
)
|
||||
end
|
||||
end
|
||||
if target.item_active?
|
||||
Battle::ItemEffects.triggerAccuracyCalcFromTarget(
|
||||
@@ -553,10 +558,4 @@ class Battle::AI::AIMove
|
||||
(user.has_active_ability?(:SERENEGRACE) || user.pbOwnSide.effects[PBEffects::Rainbow] > 0)
|
||||
return 2
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
|
||||
# TODO:
|
||||
# pbBaseAccuracy(@ai.user.battler, @ai.target.battler) if @ai.trainer.medium_skill?
|
||||
# pbCriticalOverride(@ai.user.battler, @ai.target.battler)
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user