More AI function code rewrites themed around stat changes

This commit is contained in:
Maruno17
2022-12-25 00:39:25 +00:00
parent f33184d413
commit 1258e4b9c9
9 changed files with 114 additions and 167 deletions

View File

@@ -118,10 +118,11 @@ class Battle::Move::HealUserByTargetAttackLowerTargetAttack1 < Battle::Move
# has Contrary and is at +6" check too for symmetry. This move still
# works even if the stat stage cannot be changed due to an ability or
# other effect.
if !@battle.moldBreaker && target.hasActiveAbility?(:CONTRARY) &&
target.statStageAtMax?(:ATTACK)
@battle.pbDisplay(_INTL("But it failed!")) if show_message
return true
if !@battle.moldBreaker && target.hasActiveAbility?(:CONTRARY)
if target.statStageAtMax?(:ATTACK)
@battle.pbDisplay(_INTL("But it failed!")) if show_message
return true
end
elsif target.statStageAtMin?(:ATTACK)
@battle.pbDisplay(_INTL("But it failed!")) if show_message
return true

View File

@@ -176,16 +176,18 @@ class Battle::AI
score += t_score
affected_targets += 1
end
# Check if any targets were affected
# Set the default score if no targets were affected
if affected_targets == 0
score = (@trainer.has_skill_flag?("PredictMoveFailure")) ? MOVE_USELESS_SCORE : MOVE_BASE_SCORE
end
# Score based on how many targets were affected
if affected_targets == 0 && @trainer.has_skill_flag?("PredictMoveFailure")
return MOVE_FAIL_SCORE if !@move.move.worksWithNoTargets?
score = MOVE_USELESS_SCORE
else
affected_targets = 1 if affected_targets == 0 # To avoid dividing by 0
# TODO: Can this accounting for multiple targets be improved somehow?
score /= affected_targets # Average the score against multiple targets
score /= affected_targets if affected_targets > 1 # Average the score against multiple targets
# Bonus for affecting multiple targets
if @trainer.has_skill_flag?("PreferMultiTargetMoves")
if @trainer.has_skill_flag?("PreferMultiTargetMoves") && affected_targets > 1
score += (affected_targets - 1) * 10
end
end

View File

@@ -623,7 +623,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetAttack2Confus
proc { |score, move, user, target, ai, battle|
next Battle::AI::MOVE_USELESS_SCORE if !target.battler.pbCanConfuse?(user.battler, false, move.move)
# Score for stat raise
stat_score = ai.get_score_for_target_stat_raise(score, target, [:ATTACK, 2], false)
score = ai.get_score_for_target_stat_raise(score, target, [:ATTACK, 2], false)
# Score for confusing the target
next Battle::AI::Handlers.apply_move_effect_against_target_score(
"ConfuseTarget", score, move, user, target, ai, battle)
@@ -643,7 +643,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetSpAtk1Confuse
proc { |score, move, user, target, ai, battle|
next Battle::AI::MOVE_USELESS_SCORE if !target.battler.pbCanConfuse?(user.battler, false, move.move)
# Score for stat raise
stat_score = ai.get_score_for_target_stat_raise(score, target, [:SPECIAL_ATTACK, 1], false)
score = ai.get_score_for_target_stat_raise(score, target, [:SPECIAL_ATTACK, 1], false)
# Score for confusing the target
next Battle::AI::Handlers.apply_move_effect_against_target_score(
"ConfuseTarget", score, move, user, target, ai, battle)
@@ -1020,7 +1020,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.copy("LowerTargetAtkDef1",
"LowerTargetAtkSpAtk1")
#===============================================================================
# TODO: Review score modifiers.
#
#===============================================================================
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("LowerPoisonedTargetAtkSpAtkSpd1",
proc { |move, user, target, ai, battle|
@@ -1033,164 +1033,124 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.copy("LowerTargetAtkSpAtk1",
"LowerPoisonedTargetAtkSpAtkSpd1")
#===============================================================================
# TODO: Review score modifiers.
# TODO: This code should be for a single battler (each is checked in turn).
# target should probably be treated as an enemy when deciding the score,
# since the score will be inverted elsewhere due to the target being an
# ally.
#
#===============================================================================
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseAlliesAtkDef1",
proc { |move, user, target, ai, battle|
will_fail = true
battle.allSameSideBattlers(user.battler).each do |b|
next if b.index == user.index
next if !b.pbCanRaiseStatStage?(:ATTACK, user.battler, move.move) &&
!b.pbCanRaiseStatStage?(:DEFENSE, user.battler, move.move)
will_fail = false
break
end
next will_fail
next !target.battler.pbCanRaiseStatStage?(:ATTACK, user.battler, move.move) &&
!target.battler.pbCanRaiseStatStage?(:DEFENSE, user.battler, move.move)
}
)
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseAlliesAtkDef1",
proc { |score, move, user, target, ai, battle|
user.battler.allAllies.each do |b|
score = ai.get_score_for_target_stat_raise(score, b, [:ATTACK, 1, :DEFENSE, 1])
end
next score
next ai.get_score_for_target_stat_raise(score, target, [:ATTACK, 1, :DEFENSE, 1])
}
)
#===============================================================================
# TODO: Review score modifiers.
# TODO: This code should be for a single battler (each is checked in turn).
# target should probably be treated as an enemy when deciding the score,
# since the score will be inverted elsewhere due to the target being an
# ally.
# TODO: Since this also affects the user, this will need a MoveEffectScore and a
# MoveFailureCheck.
#
#===============================================================================
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaisePlusMinusUserAndAlliesAtkSpAtk1",
proc { |move, user, target, ai, battle|
Battle::AI::Handlers::MoveFailureCheck.add("RaisePlusMinusUserAndAlliesAtkSpAtk1",
proc { |move, user, ai, battle|
will_fail = true
battle.allSameSideBattlers(user.battler).each do |b|
next if !b.hasActiveAbility?([:MINUS, :PLUS])
next if !b.pbCanRaiseStatStage?(:ATTACK, user.battler, move.move) &&
!b.pbCanRaiseStatStage?(:SPECIAL_ATTACK, user.battler, move.move)
ai.each_same_side_battler(user.side) do |b, i|
next if !b.has_active_ability?([:MINUS, :PLUS])
next if !b.battler.pbCanRaiseStatStage?(:ATTACK, user.battler, move.move) &&
!b.battler.pbCanRaiseStatStage?(:SPECIAL_ATTACK, user.battler, move.move)
will_fail = false
break
end
next will_fail
}
)
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaisePlusMinusUserAndAlliesAtkSpAtk1",
proc { |score, move, user, target, ai, battle|
# score = ai.get_score_for_target_stat_raise(score, user, [:ATTACK, 1, :SPECIAL_ATTACK, 1], false)
user.battler.allAllies.each do |b|
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaisePlusMinusUserAndAlliesAtkSpAtk1",
proc { |move, user, target, ai, battle|
next true if !target.hasActiveAbility?([:MINUS, :PLUS])
next !target.battler.pbCanRaiseStatStage?(:ATTACK, user.battler, move.move) &&
!target.battler.pbCanRaiseStatStage?(:SPECIAL_ATTACK, user.battler, move.move)
}
)
Battle::AI::Handlers::MoveEffectScore.add("RaisePlusMinusUserAndAlliesAtkSpAtk1",
proc { |score, move, user, ai, battle|
next score if move.pbTarget(user.battler) != :UserSide
ai.each_same_side_battler(user.side) do |b, i|
score = ai.get_score_for_target_stat_raise(score, b, [:ATTACK, 1, :SPECIAL_ATTACK, 1], false)
end
next score
}
)
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaisePlusMinusUserAndAlliesAtkSpAtk1",
proc { |score, move, user, target, ai, battle|
next ai.get_score_for_target_stat_raise(score, target, [:ATTACK, 1, :SPECIAL_ATTACK, 1])
}
)
#===============================================================================
# TODO: Review score modifiers.
# TODO: This code should be for a single battler (each is checked in turn).
# target should probably be treated as an enemy when deciding the score,
# since the score will be inverted elsewhere due to the target being an
# ally.
# TODO: Since this also affects the user, this will need a MoveEffectScore and a
# MoveFailureCheck.
#
#===============================================================================
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaisePlusMinusUserAndAlliesAtkSpAtk1",
proc { |move, user, target, ai, battle|
Battle::AI::Handlers::MoveFailureCheck.add("RaisePlusMinusUserAndAlliesDefSpDef1",
proc { |move, user, ai, battle|
will_fail = true
battle.allSameSideBattlers(user.battler).each do |b|
next if !b.hasActiveAbility?([:MINUS, :PLUS])
next if !b.pbCanRaiseStatStage?(:DEFENSE, user.battler, move.move) &&
!b.pbCanRaiseStatStage?(:SPECIAL_DEFENSE, user.battler, move.move)
ai.each_same_side_battler(user.side) do |b, i|
next if !b.has_active_ability?([:MINUS, :PLUS])
next if !b.battler.pbCanRaiseStatStage?(:DEFENSE, user.battler, move.move) &&
!b.battler.pbCanRaiseStatStage?(:SPECIAL_DEFENSE, user.battler, move.move)
will_fail = false
break
end
next will_fail
}
)
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaisePlusMinusUserAndAlliesDefSpDef1",
proc { |move, user, target, ai, battle|
next true if !target.hasActiveAbility?([:MINUS, :PLUS])
next !target.battler.pbCanRaiseStatStage?(:DEFENSE, user.battler, move.move) &&
!target.battler.pbCanRaiseStatStage?(:SPECIAL_DEFENSE, user.battler, move.move)
}
)
Battle::AI::Handlers::MoveEffectScore.add("RaisePlusMinusUserAndAlliesDefSpDef1",
proc { |score, move, user, ai, battle|
next score if move.pbTarget(user.battler) != :UserSide
ai.each_same_side_battler(user.side) do |b, i|
score = ai.get_score_for_target_stat_raise(score, b, [:DEFENSE, 1, :SPECIAL_DEFENSE, 1], false)
end
next score
}
)
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaisePlusMinusUserAndAlliesDefSpDef1",
proc { |score, move, user, target, ai, battle|
user.battler.allAllies.each do |b|
next if b.statStageAtMax?(:DEFENSE) && b.statStageAtMax?(:SPECIAL_DEFENSE)
score -= b.stages[:DEFENSE] * 10
score -= b.stages[:SPECIAL_DEFENSE] * 10
end
score -= user.stages[:DEFENSE] * 10
score -= user.stages[:SPECIAL_DEFENSE] * 10
next score
next ai.get_score_for_target_stat_raise(score, target, [:DEFENSE, 1, :SPECIAL_DEFENSE, 1])
}
)
#===============================================================================
# TODO: Review score modifiers.
# TODO: This code should be for a single battler (each is checked in turn).
# target should probably be treated as an enemy when deciding the score,
# since the score will be inverted elsewhere due to the target being an
# ally.
#
#===============================================================================
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseGroundedGrassBattlersAtkSpAtk1",
proc { |move, user, target, ai, battle|
will_fail = true
battle.allBattlers.each do |b|
next if !b.pbHasType?(:GRASS) || b.airborne? || b.semiInvulnerable?
next if !b.pbCanRaiseStatStage?(:ATTACK, user.battler, move.move) &&
!b.pbCanRaiseStatStage?(:SPECIAL_ATTACK, user.battler, move.move)
will_fail = false
break
end
next will_fail
next true if !b.pbHasType?(:GRASS) || b.airborne? || b.semiInvulnerable?
next !target.battler.pbCanRaiseStatStage?(:ATTACK, user.battler, move.move) &&
!target.battler.pbCanRaiseStatStage?(:SPECIAL_ATTACK, user.battler, move.move)
}
)
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseGroundedGrassBattlersAtkSpAtk1",
proc { |score, move, user, target, ai, battle|
battle.allBattlers.each do |b|
if user.battler.opposes?(b)
score -= 20
else
score -= b.stages[:ATTACK] * 10
score -= b.stages[:SPECIAL_ATTACK] * 10
end
end
next score
next ai.get_score_for_target_stat_raise(score, target, [:ATTACK, 1, :SPECIAL_ATTACK, 1])
}
)
#===============================================================================
# TODO: Review score modifiers.
# TODO: This code should be for a single battler (each is checked in turn).
# target should probably be treated as an enemy when deciding the score,
# since the score will be inverted elsewhere due to the target being an
# ally.
#
#===============================================================================
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseGrassBattlersDef1",
proc { |move, user, target, ai, battle|
will_fail = true
battle.allBattlers.each do |b|
next if !b.pbHasType?(:GRASS) || b.semiInvulnerable?
next if !b.pbCanRaiseStatStage?(:DEFENSE, user.battler, move.move)
will_fail = false
break
end
next will_fail
next true if !b.pbHasType?(:GRASS) || b.semiInvulnerable?
next !target.battler.pbCanRaiseStatStage?(:DEFENSE, user.battler, move.move)
}
)
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseGrassBattlersDef1",
proc { |score, move, user, target, ai, battle|
battle.allBattlers.each do |b|
if user.battler.opposes?(b)
score -= 20
else
score -= user.stages[:DEFENSE] * 10
end
end
next score
next ai.get_score_for_target_stat_raise(score, target, [:DEFENSE, 1])
}
)

View File

@@ -501,7 +501,7 @@ Battle::AI::Handlers::MoveFailureCheck.add("StartWeakenFireMoves",
end
}
)
Battle::AI::Handlers::MoveEffectScore.add("StartWeakenElectricMoves",
Battle::AI::Handlers::MoveEffectScore.add("StartWeakenFireMoves",
proc { |score, move, user, ai, battle|
# Don't prefer the lower the user's HP is
if user.hp < user.totalhp / 2
@@ -782,7 +782,8 @@ Battle::AI::Handlers::MoveFailureCheck.add("HoopaRemoveProtectionsBypassSubstitu
)
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HoopaRemoveProtectionsBypassSubstituteLowerUserDef1",
proc { |score, move, user, target, ai, battle|
next score + 20 if target.stages[:DEFENSE] > 0
score = ai.get_score_for_target_stat_drop(score, user, move.move.statDown, false)
next score
}
)

View File

@@ -128,7 +128,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.copy("HitTwoToFiveTimes",
"HitTwoToFiveTimesOrThreeForAshGreninja")
#===============================================================================
# TODO: Review score modifiers.
#
#===============================================================================
Battle::AI::Handlers::MoveBasePower.copy("HitTwoToFiveTimes",
"HitTwoToFiveTimesRaiseUserSpd1LowerUserDef1")
@@ -137,13 +137,9 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HitTwoToFiveTimesRaiseUs
# Score for being a multi-hit attack
score = Battle::AI::Handlers.apply_move_effect_against_target_score("HitTwoToFiveTimes",
score, move, user, target, ai, battle)
# User's stat changes
aspeed = user.rough_stat(:SPEED)
ospeed = target.rough_stat(:SPEED)
if aspeed < ospeed && aspeed * 1.5 > ospeed
score += 15 # Will become faster than the target
end
score += user.stages[:DEFENSE] * 10
# Score for user's stat changes
score = ai.get_score_for_target_stat_raise(score, user, [:SPEED, 1], false)
score = ai.get_score_for_target_stat_drop(score, user, [:DEFENSE, 1], false)
next score
}
)

View File

@@ -158,10 +158,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HealUserByTargetAttackLo
proc { |score, move, user, target, ai, battle|
# Check whether lowering the target's Attack will have any impact
if ai.trainer.medium_skill?
if target.battler.pbCanLowerStatStage?(:ATTACK, user.battler, move.move) &&
target.check_for_move { |m| m.physicalMove?(m.type) }
score += target.stages[:ATTACK] * 10
end
score = ai.get_score_for_target_stat_drop(score, target, [:ATTACK, 1])
end
# Consider how much HP will be restored
heal_amt = target.rough_stat(:ATTACK)
@@ -521,15 +518,11 @@ Battle::AI::Handlers::MoveEffectScore.copy("UserFaintsExplosive",
#===============================================================================
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserFaintsLowerTargetAtkSpAtk2",
proc { |score, move, user, target, ai, battle|
next Battle::AI::MOVE_USELESS_SCORE if !target.battler.pbCanLowerStatStage?(:ATTACK, user.battler) &&
!target.battler.pbCanLowerStatStage?(:SPECIAL_ATTACK, user.battler)
score -= 25 # User will faint, don't prefer this move
# Check the impact of lowering the target's stats
if target.stages[:ATTACK] < 0 && target.stages[:SPECIAL_ATTACK] < 0
score -= 20
elsif target.stages[:ATTACK] > 0 || target.stages[:SPECIAL_ATTACK] > 0
score += 10
end
score = ai.get_score_for_target_stat_drop(score, target, move.move.statDown)
next score if score == Battle::AI::MOVE_USELESS_SCORE
# Score for the user fainting
if ai.trainer.medium_skill?
score -= 10 if user.hp >= user.totalhp * 0.5 # User at 50% HP or more
score += 10 if user.hp <= user.totalhp * 0.25 # User at 25% HP or less

View File

@@ -85,41 +85,38 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HealAllyOrDamageFoe",
)
#===============================================================================
# TODO: Review score modifiers.
#
#===============================================================================
Battle::AI::Handlers::MoveFailureCheck.add("CurseTargetOrLowerUserSpd1RaiseUserAtkDef1",
proc { |move, user, ai, battle|
if !user.has_type?(:GHOST)
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)
will_fail = false
break
end
(move.move.statDown.length / 2).times do |i|
next if !user.battler.pbCanLowerStatStage?(move.move.statDown[i * 2], user.battler, move.move)
will_fail = false
break
end
next will_fail
next false if user.has_type?(:GHOST)
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)
will_fail = false
break
end
(move.move.statDown.length / 2).times do |i|
next if !user.battler.pbCanLowerStatStage?(move.move.statDown[i * 2], user.battler, move.move)
will_fail = false
break
end
next will_fail
}
)
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("CurseTargetOrLowerUserSpd1RaiseUserAtkDef1",
proc { |move, user, target, ai, battle|
if user.has_type?(:GHOST)
next true if target.effects[PBEffects::Curse] || !target.battler.takesIndirectDamage?
end
next false if !user.has_type?(:GHOST)
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)
avg = user.stages[:SPEED] * 10
avg -= user.stages[:ATTACK] * 10
avg -= user.stages[:DEFENSE] * 10
score += avg / 3
next score
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)
}
)
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("CurseTargetOrLowerUserSpd1RaiseUserAtkDef1",
@@ -138,7 +135,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("CurseTargetOrLowerUserSp
if ai.trainer.high_skill?
# Prefer if user can stall while damage is dealt
if user.check_for_move { |m| m.is_a?(Battle::Move::ProtectMove) }
score += 15
score += 8
end
end
next score
@@ -293,14 +290,10 @@ Battle::AI::Handlers::MoveFailureCheck.add("UserAddStockpileRaiseDefSpDef1",
)
Battle::AI::Handlers::MoveEffectScore.add("UserAddStockpileRaiseDefSpDef1",
proc { |score, move, user, ai, battle|
avg = 0
avg -= user.stages[:DEFENSE] * 10
avg -= user.stages[:SPECIAL_DEFENSE] * 10
score += avg / 2
if user.battler.pbHasMoveFunction?("PowerDependsOnUserStockpile",
"HealUserDependingOnUserStockpile") # Spit Up, Swallow
score += 20 # More preferable if user also has Spit Up/Swallow
end
score = ai.get_score_for_target_stat_raise(score, user, [:DEFENSE, 1, :SPECIAL_DEFENSE, 1], false)
# More preferable if user also has Spit Up/Swallow
score += 20 if user.battler.pbHasMoveFunction?("PowerDependsOnUserStockpile",
"HealUserDependingOnUserStockpile")
next score
}
)

View File

@@ -73,9 +73,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("LowerTargetAtkSpAtk1Swi
)
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("LowerTargetAtkSpAtk1SwitchOutUser",
proc { |score, move, user, target, ai, battle|
avg = target.stages[:ATTACK] * 10
avg += target.stages[:SPECIAL_ATTACK] * 10
score += avg / 2
score = ai.get_score_for_target_stat_drop(score, target, move.move.statDown, false)
next score
}
)

View File

@@ -429,6 +429,9 @@ class Battle::AI::AIMove
target_battler = target.battler
# OHKO special calculation
if @ai.trainer.medium_skill?
# TODO: This is insufficient for OHKO moves, as they should also ignore
# effects like Telekinesis and Minimize but def rough_accuracy
# treats them as applying to OHKO moves.
case function
when "OHKO", "OHKOHitsUndergroundTarget"
modifiers[:base_accuracy] = self.accuracy + user.level - target.level