Tackling of various AI "TODO" comments, a little tidying

This commit is contained in:
Maruno17
2023-02-17 21:36:08 +00:00
parent 81d069eef1
commit 0e4053f837
14 changed files with 275 additions and 549 deletions

View File

@@ -102,7 +102,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("FailsUnlessTargetShares
)
#===============================================================================
# TODO: Split some of this into a MoveEffectScore?
#
#===============================================================================
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("FailsIfUserDamagedThisTurn",
proc { |score, move, user, target, ai, battle|
@@ -475,10 +475,16 @@ Battle::AI::Handlers::MoveEffectScore.add("AddToxicSpikesToFoeSide",
if ai.trainer.medium_skill?
# Check affected by entry hazard
next if pkmn.hasItem?(:HEAVYDUTYBOOTS)
# TODO: Check pkmn's immunity to being poisoned.
# Check pkmn's immunity to being poisoned
next if battle.field.terrain == :Misty
next if pkmn.hasType?(:POISON)
next if pkmn.hasType?(:STEEL)
next if pkmn.hasAbility?(:IMMUNITY)
next if pkmn.hasAbility?(:PASTELVEIL)
next if pkmn.hasAbility?(:FLOWERVEIL) && pkmn.hasType?(:GRASS)
next if pkmn.hasAbility?(:LEAFGUARD) && [:Sun, :HarshSun].include?(battle.pbWeather)
next if pkmn.hasAbility?(:COMATOSE) && pkmn.isSpecies?(:KOMALA)
next if pkmn.hasAbility?(:SHIELDSDOWN) && pkmn.isSpecies?(:MINIOR) && pkmn.form < 7
# Check airborne
if !pkmn.hasItem?(:IRONBALL) &&
battle.field.effects[PBEffects::Gravity] == 0

View File

@@ -1,6 +1,5 @@
#===============================================================================
# TODO: This code can be called with a single target and with no targets. Make
# sure it doesn't assume that there is a target.
#
#===============================================================================
Battle::AI::Handlers::MoveFailureCheck.add("RaiseUserAttack1",
proc { |move, user, ai, battle|
@@ -67,8 +66,7 @@ Battle::AI::Handlers::MoveEffectScore.add("MaxUserAttackLoseHalfOfTotalHP",
)
#===============================================================================
# TODO: This code can be called with a single target and with no targets. Make
# sure it doesn't assume that there is a target.
#
#===============================================================================
Battle::AI::Handlers::MoveFailureCheck.copy("RaiseUserAttack1",
"RaiseUserDefense1")
@@ -92,8 +90,7 @@ Battle::AI::Handlers::MoveEffectScore.add("RaiseUserDefense1CurlUpUser",
)
#===============================================================================
# TODO: This code can be called with multiple targets and with no targets. Make
# sure it doesn't assume that there is a target.
#
#===============================================================================
Battle::AI::Handlers::MoveFailureCheck.copy("RaiseUserDefense1",
"RaiseUserDefense2")
@@ -196,23 +193,17 @@ Battle::AI::Handlers::MoveEffectScore.add("RaiseUserSpeed2LowerUserWeight",
proc { |score, move, user, ai, battle|
score = ai.get_score_for_target_stat_raise(score, user, move.move.statUp)
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.has_move_with_function?("PowerHigherWithUserHeavierThanTarget")
score -= 10
end
current_weight = user.battler.pbWeight
if current_weight > 1
score -= 5 if user.has_move_with_function?("PowerHigherWithUserHeavierThanTarget")
ai.each_foe_battler(user.side) do |b, i|
if b.has_move_with_function?("PowerHigherWithUserHeavierThanTarget")
score -= 10
score -= 5 if b.has_move_with_function?("PowerHigherWithUserHeavierThanTarget")
score += 5 if b.has_move_with_function?("PowerHigherWithTargetWeight")
# User will become susceptible to Sky Drop
if b.has_move_with_function?("TwoTurnAttackInvulnerableInSkyTargetCannotAct") &&
Settings::MECHANICS_GENERATION >= 6
score -= 7 if current_weight >= 2000 && current_weight < 3000
end
if b.has_move_with_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
@@ -1323,9 +1314,10 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserCopyTargetStatStages
)
#===============================================================================
# TODO: Account for stat theft before damage calculation. This would be complex,
# involving pbCanRaiseStatStage? and Contrary and Simple; do I want to
# account for all that or simplify things?
# NOTE: Accounting for the stat theft before damage calculation, to calculate a
# more accurate predicted damage, would be complex, involving
# pbCanRaiseStatStage? and Contrary and Simple; I'm not bothering with
# that.
#===============================================================================
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserStealTargetPositiveStatStages",
proc { |score, move, user, target, ai, battle|

View File

@@ -530,9 +530,9 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("CureTargetBurn",
proc { |score, move, user, target, ai, battle|
if target.status == :BURN
if target.opposes?(user)
score -= 40
score -= 10
else
score += 40
score += 10
end
end
next score
@@ -1228,9 +1228,6 @@ Battle::AI::Handlers::MoveFailureCheck.add("StartGravity",
)
Battle::AI::Handlers::MoveEffectScore.add("StartGravity",
proc { |score, move, user, ai, battle|
# TODO: Gravity increases accuracy of all moves. Prefer if user/ally has low
# accuracy moves, don't prefer if foes have them. Should "low
# accuracy" mean anything below 85%?
ai.each_battler do |b, i|
# Prefer grounding airborne foes, don't prefer grounding airborne allies
# Prefer making allies affected by terrain, don't prefer making foes
@@ -1252,6 +1249,11 @@ Battle::AI::Handlers::MoveEffectScore.add("StartGravity",
if b.effects[PBEffects::SkyDrop] >= 0
score += (user.opposes?(b)) ? -5 : 5
end
# Gravity raises accuracy of all moves; prefer if the user/ally has low
# accuracy moves, don't prefer if foes have any
if b.check_for_move { |m| m.accuracy < 85 }
score += (user.opposes?(b)) ? -5 : 5
end
# Prefer stopping foes' sky-based attacks, don't prefer stopping allies'
# sky-based attacks
if user.faster_than?(b) &&

View File

@@ -431,9 +431,20 @@ Battle::AI::Handlers::MoveEffectScore.add("EnsureNextCriticalHit",
next Battle::AI::MOVE_USELESS_SCORE if user.effects[PBEffects::LaserFocus] > 0
# Useless if the user's critical hit stage ensures critical hits already, or
# critical hits are impossible (e.g. via Lucky Chant)
# TODO: Critical hit rate is calculated using a target, but this move
# doesn't have a target. An error is shown when trying it.
crit_stage = move.rough_critical_hit_stage
crit_stage = 0
crit_stage = -1 if user.battler.pbOwnSide.effects[PBEffects::LuckyChant] > 0
if crit_stage >= 0 && user.ability_active? && ![:MERCILESS].include?(user.ability)
crit_stage = Battle::AbilityEffects.triggerCriticalCalcFromUser(user.battler.ability,
user.battler, user.battler, crit_stage)
end
if crit_stage >= 0 && user.item_active?
crit_stage = Battle::ItemEffects.triggerCriticalCalcFromUser(user.battler.item,
user.battler, user.battler, crit_stage)
end
if crit_stage >= 0 && crit_stage < 50
crit_stage += user.effects[PBEffects::FocusEnergy]
crit_stage = [crit_stage, Battle::Move::CRITICAL_HIT_RATIOS.length - 1].min
end
if crit_stage < 0 ||
crit_stage >= Battle::Move::CRITICAL_HIT_RATIOS.length ||
Battle::Move::CRITICAL_HIT_RATIOS[crit_stage] == 1
@@ -482,20 +493,22 @@ Battle::AI::Handlers::MoveEffectScore.add("StartPreventCriticalHitsAgainstUserSi
# make critical hits more likely
ai.each_foe_battler(user.side) do |b, i|
crit_stage = 0
if b.ability_active?
if crit_stage >= 0 && b.ability_active?
crit_stage = Battle::AbilityEffects.triggerCriticalCalcFromUser(b.battler.ability,
b.battler, user.battler, crit_stage)
next if crit_stage < 0
end
if b.item_active?
if crit_stage >= 0 && b.item_active?
crit_stage = Battle::ItemEffects.triggerCriticalCalcFromUser(b.battler.item,
b.battler, user.battler, crit_stage)
next if crit_stage < 0
end
crit_stage += b.effects[PBEffects::FocusEnergy]
crit_stage += 1 if b.check_for_move { |m| m.highCriticalRate? }
crit_stage = [crit_stage, Battle::Move::CRITICAL_HIT_RATIOS.length - 1].min
crit_stage = 3 if crit_stage < 3 && m.check_for_move { |m| m.pbCritialOverride(b.battler, user.battler) > 0 }
if crit_stage >= 0 && crit_stage < 50
crit_stage += b.effects[PBEffects::FocusEnergy]
crit_stage += 1 if b.check_for_move { |m| m.highCriticalRate? }
crit_stage = 99 if m.check_for_move { |m| m.pbCritialOverride(b.battler, user.battler) > 0 }
crit_stage = [crit_stage, Battle::Move::CRITICAL_HIT_RATIOS.length - 1].min
end
# TODO: Change the score depending on how much of an effect a critical hit
# will have? Critical hits ignore the user's offensive stat drops
# and the target's defensive stat raises, and multiply the damage.
@@ -1064,7 +1077,7 @@ Battle::AI::Handlers::MoveEffectScore.add("ProtectUserSideFromPriorityMoves",
next if !b.can_attack?
# TODO: There are more calculations that could be done to get a more
# accurate priority number.
next if !b.check_for_move { |m| m.priority > 0 && m.canProtectAgainst? }
next if !b.check_for_move { |m| m.pbPriority(b.battler) > 0 && m.canProtectAgainst? }
useless = false
# General preference
score += 4

View File

@@ -192,7 +192,6 @@ Battle::AI::Handlers::MoveEffectScore.add("AttackAndSkipNextTurn",
score -= 10 if !user.has_active_ability?(:TRUANT)
# Don't prefer if user is at a high HP (treat this move as a last resort)
score -= 10 if user.hp >= user.totalhp / 2
# TODO: Don't prefer if another of the user's moves could KO the target.
next score
}
)
@@ -215,7 +214,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttack",
# Don't prefer if target has a protecting move
if ai.trainer.high_skill? && !(user.has_active_ability?(:UNSEENFIST) && move.move.contactMove?)
has_protect_move = false
if move.move.pbTarget(user).num_targets > 1 &&
if move.pbTarget(user).num_targets > 1 &&
(Settings::MECHANICS_GENERATION >= 7 || move.damagingMove?)
if target.has_move_with_function?("ProtectUserSideFromMultiTargetDamagingMoves")
has_protect_move = true

View File

@@ -116,7 +116,7 @@ Battle::AI::Handlers::MoveEffectScore.add("HealUserHalfOfTotalHPLoseFlyingTypeTh
)
#===============================================================================
#
# TODO: Review score modifiers.
#===============================================================================
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("CureTargetStatusHealUserHalfOfTotalHP",
proc { |move, user, target, ai, battle|
@@ -585,8 +585,7 @@ Battle::AI::Handlers::MoveEffectScore.copy("UserFaintsHealAndCureReplacement",
"UserFaintsHealAndCureReplacementRestorePP")
#===============================================================================
# TODO: This code should be for a single battler (each is checked in turn).
# Should have a MoveEffectAgainstTargetScore instead.
#
#===============================================================================
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("StartPerishCountsForAllBattlers",
proc { |move, user, target, ai, battle|
@@ -604,11 +603,10 @@ Battle::AI::Handlers::MoveEffectScore.add("StartPerishCountsForAllBattlers",
allies_affected = 0
foes_affected = 0
foes_with_high_hp = 0
battle.allBattlers.each do |b|
next if b.effects[PBEffects::PerishSong] > 0
next if Battle::AbilityEffects.triggerMoveImmunity(b.ability, user.battler, b,
move.move, move.rough_type, battle, false)
if b.opposes?(user.index)
ai.each_battler do |b|
next if Battle::AI::Handlers.move_will_fail_against_target?("StartPerishCountsForAllBattlers",
move, user, b, ai, battle)
if b.opposes?(user)
foes_affected += 1
foes_with_high_hp += 1 if b.hp >= b.totalhp * 0.75
else

View File

@@ -78,7 +78,8 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HealAllyOrDamageFoe",
#===============================================================================
Battle::AI::Handlers::MoveFailureCheck.add("CurseTargetOrLowerUserSpd1RaiseUserAtkDef1",
proc { |move, user, ai, battle|
next false if user.has_type?(:GHOST)
next false if user.has_type?(:GHOST) ||
(move.rough_type == :GHOST && user.has_active_ability?([:LIBERO, :PROTEAN]))
will_fail = true
(move.move.statUp.length / 2).times do |i|
next if !user.battler.pbCanRaiseStatStage?(move.move.statUp[i * 2], user.battler, move.move)
@@ -95,14 +96,16 @@ Battle::AI::Handlers::MoveFailureCheck.add("CurseTargetOrLowerUserSpd1RaiseUserA
)
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("CurseTargetOrLowerUserSpd1RaiseUserAtkDef1",
proc { |move, user, target, ai, battle|
next false if !user.has_type?(:GHOST)
next false if !user.has_type?(:GHOST) &&
!(move.rough_type == :GHOST && user.has_active_ability?([:LIBERO, :PROTEAN]))
next true if target.effects[PBEffects::Curse] || !target.battler.takesIndirectDamage?
next false
}
)
Battle::AI::Handlers::MoveEffectScore.add("CurseTargetOrLowerUserSpd1RaiseUserAtkDef1",
proc { |score, move, user, ai, battle|
next score if user.has_type?(:GHOST)
next score if user.has_type?(:GHOST) ||
(move.rough_type == :GHOST && user.has_active_ability?([:LIBERO, :PROTEAN]))
score = ai.get_score_for_target_stat_raise(score, user, move.move.statUp)
next score if score == Battle::AI::MOVE_USELESS_SCORE
next ai.get_score_for_target_stat_drop(score, user, move.move.statDown, false)
@@ -110,7 +113,8 @@ Battle::AI::Handlers::MoveEffectScore.add("CurseTargetOrLowerUserSpd1RaiseUserAt
)
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("CurseTargetOrLowerUserSpd1RaiseUserAtkDef1",
proc { |score, move, user, target, ai, battle|
next score if !user.has_type?(:GHOST)
next score if !user.has_type?(:GHOST) &&
!(move.rough_type == :GHOST && user.has_active_ability?([:LIBERO, :PROTEAN]))
# Don't prefer if user will faint because of using this move
next Battle::AI::MOVE_USELESS_SCORE if user.hp <= user.totalhp / 2
# Prefer early on

View File

@@ -477,8 +477,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("LowerPPOfTargetLastMoveB
#===============================================================================
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("LowerPPOfTargetLastMoveBy4",
proc { |move, user, target, ai, battle|
last_move = target.battler.pbGetMoveWithID(target.battler.lastRegularMoveUsed)
next !last_move || last_move.pp == 0 || last_move.total_pp <= 0
next !target.check_for_move { |m| m.id == target.battler.lastRegularMoveUsed }
}
)
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("LowerPPOfTargetLastMoveBy4",
@@ -500,14 +499,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("DisableTargetLastMoveUs
proc { |move, user, target, ai, battle|
next true if target.effects[PBEffects::Disable] > 0 || !target.battler.lastRegularMoveUsed
next true if move.move.pbMoveFailedAromaVeil?(user.battler, target.battler, false)
will_fail = true
target.battler.eachMove do |m|
next if m.id != target.battler.lastRegularMoveUsed
next if m.pp == 0 && m.total_pp > 0
will_fail = false
break
end
next will_fail
next !target.check_for_move { |m| m.id == target.battler.lastRegularMoveUsed }
}
)
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetUsingSameMoveConsecutively",
@@ -563,13 +555,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("DisableTargetUsingDiffe
next true if target.effects[PBEffects::ShellTrap]
next true if move.move.pbMoveFailedAromaVeil?(user.battler, target.battler, false)
will_fail = true
target.battler.eachMove do |m|
next if m.id != target.battler.lastRegularMoveUsed
next if m.pp == 0 && m.total_pp > 0
will_fail = false
break
end
next will_fail
next !target.check_for_move { |m| m.id == target.battler.lastRegularMoveUsed }
}
)
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetUsingDifferentMove",
@@ -627,7 +613,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetStatusMoves
next Battle::AI::MOVE_USELESS_SCORE if !target.check_for_move { |m| m.statusMove? }
# Not worth using on a sleeping target that won't imminently wake up
if target.status == :SLEEP && target.statusCount > ((target.faster_than?(user)) ? 2 : 1)
if !target.check_for_move { |m| m.statusMove? && m.usableWhenAsleep? && (m.pp > 0 || m.total_pp == 0) }
if !target.check_for_move { |m| m.statusMove? && m.usableWhenAsleep? }
next Battle::AI::MOVE_USELESS_SCORE
end
end
@@ -652,8 +638,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetStatusMoves
"ProtectUserFromTargetingMovesSpikyShield", # Spiky Shield
"ProtectUserBanefulBunker" # Baneful Bunker
]
if target.check_for_move { |m| m.statusMove? && protection_moves.include?(m.function) &&
(m.pp > 0 || m.total_pp == 0) }
if target.check_for_move { |m| m.statusMove? && protection_moves.include?(m.function) }
score += 6
end
# Inherent preference
@@ -675,7 +660,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("DisableTargetHealingMov
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetHealingMoves",
proc { |score, move, user, target, ai, battle|
# Useless if the foe can't heal themselves with a move or some held items
if !target.check_for_move { |m| m.healingMove? && (m.pp > 0 || m.total_pp == 0) }
if !target.check_for_move { |m| m.healingMove? }
if !target.has_active_item?(:LEFTOVERS) &&
!(target.has_active_item?(:BLACKSLUDGE) && target.has_type?(:POISON))
next Battle::AI::MOVE_USELESS_SCORE
@@ -693,7 +678,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetHealingMove
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetSoundMoves",
proc { |score, move, user, target, ai, battle|
next score if target.effects[PBEffects::ThroatChop] > 1
next score if !target.check_for_move { |m| m.soundMove? && (m.pp > 0 || m.total_pp == 0) }
next score if !target.check_for_move { |m| m.soundMove? }
# Inherent preference
score += 8
next score
@@ -714,13 +699,9 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetMovesKnownB
shared_move = false
user_moves = user.battler.moves.map { |m| m.id }
ai.each_foe_battler(user.side) do |b, i|
b.battler.eachMove do |m|
next if !user_moves.include?(m.id)
next if m.pp == 0 && m.total_pp > 0
shared_move = true
break
end
break if shared_move
next if !b.check_for_move { |m| user_moves.include?(m.id) }
shared_move = true
break
end
next Battle::AI::MOVE_USELESS_SCORE if !shared_move
# Inherent preference