Some more AI function code rewrites

This commit is contained in:
Maruno17
2022-12-04 19:09:46 +00:00
parent c53a52564b
commit 539a47671d
8 changed files with 218 additions and 91 deletions

View File

@@ -138,8 +138,16 @@ end
# user's Attack and Defense by 1 stage each. (Curse)
#===============================================================================
class Battle::Move::CurseTargetOrLowerUserSpd1RaiseUserAtkDef1 < Battle::Move
attr_reader :statUp, :statDown
def ignoresSubstitute?(user); return true; end
def initialize(battle, move)
super
@statUp = [:ATTACK, 1, :DEFENSE, 1]
@statDown = [:SPEED, 1]
end
def pbTarget(user)
if user.pbHasType?(:GHOST)
ghost_target = (Settings::MECHANICS_GENERATION >= 8) ? :RandomNearFoe : :NearFoe
@@ -150,9 +158,18 @@ class Battle::Move::CurseTargetOrLowerUserSpd1RaiseUserAtkDef1 < Battle::Move
def pbMoveFailed?(user, targets)
return false if user.pbHasType?(:GHOST)
if !user.pbCanLowerStatStage?(:SPEED, user, self) &&
!user.pbCanRaiseStatStage?(:ATTACK, user, self) &&
!user.pbCanRaiseStatStage?(:DEFENSE, user, self)
failed = true
(@statUp.length / 2).times do |i|
next if !user.pbCanRaiseStatStage?(@statUp[i * 2], user, self)
failed = false
break
end
(@statDown.length / 2).times do |i|
next if !user.pbCanLowerStatStage?(@statDown[i * 2], user, self)
failed = false
break
end
if failed
@battle.pbDisplay(_INTL("But it failed!"))
return true
end
@@ -170,15 +187,19 @@ class Battle::Move::CurseTargetOrLowerUserSpd1RaiseUserAtkDef1 < Battle::Move
def pbEffectGeneral(user)
return if user.pbHasType?(:GHOST)
# Non-Ghost effect
if user.pbCanLowerStatStage?(:SPEED, user, self)
user.pbLowerStatStage(:SPEED, 1, user)
showAnim = true
(@statDown.length / 2).times do |i|
next if !user.pbCanLowerStatStage?(@statDown[i * 2], user, self)
if user.pbLowerStatStage(@statDown[i * 2], @statDown[(i * 2) + 1], user, showAnim)
showAnim = false
end
end
showAnim = true
if user.pbCanRaiseStatStage?(:ATTACK, user, self)
showAnim = false if user.pbRaiseStatStage(:ATTACK, 1, user, showAnim)
end
if user.pbCanRaiseStatStage?(:DEFENSE, user, self)
user.pbRaiseStatStage(:DEFENSE, 1, user, showAnim)
(@statUp.length / 2).times do |i|
next if !user.pbCanRaiseStatStage?(@statUp[i * 2], user, self)
if user.pbRaiseStatStage(@statUp[i * 2], @statUp[(i * 2) + 1], user, showAnim)
showAnim = false
end
end
end

View File

@@ -36,6 +36,10 @@ class Battle::AI
battler.turnCount && battler.turnCount >= 5
shouldSwitch = true
end
# Pokémon is about to faint because of Perish Song
if !shouldSwitch && battler.effects[PBEffects::PerishSong] == 1
shouldSwitch = true
end
# Pokémon is Perish Songed and has Baton Pass
if @trainer.high_skill? && battler.effects[PBEffects::PerishSong] == 1
battler.eachMoveWithIndex do |m, i|
@@ -87,15 +91,11 @@ class Battle::AI
shouldSwitch = true
end
end
# Pokémon is about to faint because of Perish Song
if !shouldSwitch && battler.effects[PBEffects::PerishSong] == 1
shouldSwitch = true
end
if shouldSwitch
list = []
idxPartyStart, idxPartyEnd = @battle.pbTeamIndexRangeFromBattlerIndex(battler.index)
@battle.pbParty(battler.index).each_with_index do |pkmn, i|
next if i == idxPartyEnd - 1 # Don't choose to switch in ace
next if @trainer.has_skill_flag?("ReserveLastPokemon") && i == idxPartyEnd - 1 # Don't choose to switch in ace
next if !@battle.pbCanSwitch?(battler.index, i)
# If perish count is 1, it may be worth it to switch
# even with Spikes, since Perish Song's effect will end
@@ -152,7 +152,9 @@ class Battle::AI
enemies = []
idxPartyStart, idxPartyEnd = @battle.pbTeamIndexRangeFromBattlerIndex(idxBattler)
party.each_with_index do |_p, i|
next if i == idxPartyEnd - 1 && enemies.length > 0 # Ignore ace if possible
if @trainer.has_skill_flag?("ReserveLastPokemon")
next if i == idxPartyEnd - 1 && enemies.length > 0 # Ignore ace if possible
end
enemies.push(i) if @battle.pbCanSwitchLax?(idxBattler, i)
end
return -1 if enemies.length == 0

View File

@@ -246,9 +246,9 @@ class Battle::AI
score -= 20
else
has_special_moves = @user.check_for_move { |m| m.specialMove?(m.type) }
inc = (has_special_moves) ? 5 : 10
score += inc * (2 - old_stage) * inc_mult
score += 4 * inc_mult if @user.hp == @user.totalhp
inc = (has_special_moves) ? 10 : 20
score += inc * inc_mult
score += 8 * inc_mult if @user.hp == @user.totalhp
end
when :DEFENSE
@@ -256,8 +256,8 @@ class Battle::AI
if old_stage >= 2
score -= 20
else
score += 5 * (2 - old_stage) * inc_mult
score += 4 * inc_mult if @user.hp == @user.totalhp
score += 10 * inc_mult
score += 8 * inc_mult if @user.hp == @user.totalhp
end
when :SPECIAL_ATTACK
@@ -269,9 +269,9 @@ class Battle::AI
has_physical_moves = @user.check_for_move { |m| m.physicalMove?(m.type) &&
m.function != "UseUserDefenseInsteadOfUserAttack" &&
m.function != "UseTargetAttackInsteadOfUserAttack" }
inc = (has_physical_moves) ? 5 : 10
score += inc * (2 - old_stage) * inc_mult
score += 4 * inc_mult if @user.hp == @user.totalhp
inc = (has_physical_moves) ? 10 : 20
score += inc * inc_mult
score += 8 * inc_mult if @user.hp == @user.totalhp
end
when :SPECIAL_DEFENSE
@@ -279,8 +279,8 @@ class Battle::AI
if old_stage >= 2
score -= 20
else
score += 5 * (2 - old_stage) * inc_mult
score += 4 * inc_mult if @user.hp == @user.totalhp
score += 10 * inc_mult
score += 8 * inc_mult if @user.hp == @user.totalhp
end
when :SPEED
@@ -312,8 +312,8 @@ class Battle::AI
end
min_accuracy = min_accuracy * stage_mul[old_stage] / stage_div[old_stage]
if min_accuracy < 90
score += 5 * (2 - old_stage) * inc_mult
score += 4 * inc_mult if @user.hp == @user.totalhp
score += 10 * inc_mult
score += 8 * inc_mult if @user.hp == @user.totalhp
end
end
@@ -330,8 +330,8 @@ class Battle::AI
if old_stage >= 2
score -= 20
else
score += 5 * (2 - old_stage) * inc_mult
score += 4 * inc_mult if @user.hp == @user.totalhp
score += 10 * (2 - old_stage) * inc_mult
score += 8 * inc_mult if @user.hp == @user.totalhp
end
end

View File

@@ -404,22 +404,27 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("StartDamageTargetEachTur
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("StartLeechSeedTarget",
proc { |move, user, target, ai, battle|
next true if target.effects[PBEffects::LeechSeed] >= 0
next true if target.has_type?(:GRASS)
next true if target.has_type?(:GRASS) || !target.battler.takesIndirectDamage?
}
)
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("StartLeechSeedTarget",
proc { |score, move, user, target, ai, battle|
# Prefer early on
score += 10 if user.turnCount < 2
if ai.trainer.medium_skill?
if !user.check_for_move { |m| m.damagingMove? }
score += 20
end
score -= 20 if target.has_active_ability?([:LIQUIDOOZE]) || !target.battler.takesIndirectDamage?
# Prefer if the user has no damaging moves
score += 20 if !user.check_for_move { |m| m.damagingMove? }
# Prefer if the target can't switch out to remove its seeding
score += 10 if !battle.pbCanChooseNonActive?(target.index)
# Don't prefer if the leeched HP will hurt the user
score -= 20 if target.has_active_ability?([:LIQUIDOOZE])
end
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
end
# Don't prefer if target can remove the seed
if target.check_for_move { |m| m.is_a?(Battle::Move::RemoveUserBindingAndEntryHazards) }
score -= 15
end

View File

@@ -1,18 +1,32 @@
#===============================================================================
# TODO: Review score modifiers.
#
#===============================================================================
Battle::AI::Handlers::MoveEffectScore.add("RedirectAllMovesToUser",
proc { |score, move, user, ai, battle|
# Useless if there is no ally to redirect attacks from
next Battle::AI::MOVE_USELESS_SCORE if user.battler.allAllies.length == 0
# Prefer if ally is at low HP and user is at high HP
if user.hp > user.totalhp * 2 / 3
ai.each_ally(user.index) do |b, i|
score += 10 if b.hp <= b.totalhp / 3
end
end
}
)
#===============================================================================
# TODO: Review score modifiers.
#
#===============================================================================
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RedirectAllMovesToTarget",
proc { |score, move, user, target, ai, battle|
next Battle::AI::MOVE_USELESS_SCORE if user.battler.allAllies.length == 0
if target.opposes?(user)
# Useless if target is a foe but there is only one foe
next Battle::AI::MOVE_USELESS_SCORE if target.battler.allAllies.length == 0
# Useless if there is no ally to attack the spotlighted foe
next Battle::AI::MOVE_USELESS_SCORE if user.battler.allAllies.length == 0
end
# Generaly don't prefer this move, as it's a waste of the user's turn
next score - 15
}
)
@@ -49,7 +63,7 @@ Battle::AI::Handlers::MoveBasePower.add("RandomlyDamageOrHealTarget",
)
#===============================================================================
# TODO: Review score modifiers.
#
#===============================================================================
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("HealAllyOrDamageFoe",
proc { |move, user, target, ai, battle|
@@ -59,8 +73,12 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("HealAllyOrDamageFoe",
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HealAllyOrDamageFoe",
proc { |score, move, user, target, ai, battle|
if !target.opposes?(user)
score += 50
score -= target.hp * 100 / target.totalhp
# Consider how much HP will be restored
if target.hp >= target.totalhp * 0.5
score -= 10
else
score += 20 * (target.totalhp - target.hp) / target.totalhp
end
end
next score
}
@@ -72,15 +90,26 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HealAllyOrDamageFoe",
Battle::AI::Handlers::MoveFailureCheck.add("CurseTargetOrLowerUserSpd1RaiseUserAtkDef1",
proc { |move, user, ai, battle|
if !user.has_type?(:GHOST)
next true if !user.battler.pbCanLowerStatStage?(:SPEED, user.battler, move.move) &&
!user.battler.pbCanRaiseStatStage?(:ATTACK, user.battler, move.move) &&
!user.battler.pbCanRaiseStatStage?(:DEFENSE, user.battler, move.move)
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
end
}
)
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("CurseTargetOrLowerUserSpd1RaiseUserAtkDef1",
proc { |move, user, target, ai, battle|
next true if user.has_type?(:GHOST) && target.effects[PBEffects::Curse]
if user.has_type?(:GHOST)
next true if target.effects[PBEffects::Curse] || !target.battler.takesIndirectDamage?
end
}
)
Battle::AI::Handlers::MoveEffectScore.add("CurseTargetOrLowerUserSpd1RaiseUserAtkDef1",
@@ -96,12 +125,20 @@ 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)
if user.hp <= user.totalhp / 2
if battle.pbAbleNonActiveCount(user.idxOwnSide) == 0
score -= 90
else
score -= 50
score -= 30 if battle.switchStyle
# 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
score += 10 if user.turnCount < 2
if ai.trainer.medium_skill?
# Prefer if the user has no damaging moves
score += 20 if !user.check_for_move { |m| m.damagingMove? }
# Prefer if the target can't switch out to remove its curse
score += 10 if !battle.pbCanChooseNonActive?(target.index)
end
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
end
end
next score
@@ -144,14 +181,30 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TargetNextFireMoveDamage
)
#===============================================================================
# TODO: Review score modifiers.
#
#===============================================================================
# DoublePowerAfterFusionFlare
Battle::AI::Handlers::MoveEffectScore.add("DoublePowerAfterFusionFlare",
proc { |score, move, user, ai, battle|
# Prefer if an ally knows Fusion Flare
ai.each_ally(user.index) do |b, i|
score += 10 if b.check_for_move { |m| m.function == "DoublePowerAfterFusionBolt" }
end
next score
}
)
#===============================================================================
# TODO: Review score modifiers.
#
#===============================================================================
# DoublePowerAfterFusionBolt
Battle::AI::Handlers::MoveEffectScore.add("DoublePowerAfterFusionBolt",
proc { |score, move, user, ai, battle|
# Prefer if an ally knows Fusion Bolt
ai.each_ally(user.index) do |b, i|
score += 10 if b.check_for_move { |m| m.function == "DoublePowerAfterFusionFlare" }
end
next score
}
)
#===============================================================================
# TODO: Review score modifiers.
@@ -287,19 +340,43 @@ Battle::AI::Handlers::MoveEffectScore.add("HealUserDependingOnUserStockpile",
)
#===============================================================================
# TODO: Review score modifiers.
#
#===============================================================================
# GrassPledge
Battle::AI::Handlers::MoveEffectScore.add("GrassPledge",
proc { |score, move, user, ai, battle|
# Prefer if an ally knows a different Pledge move
ai.each_ally(user.index) do |b, i|
score += 10 if b.check_for_move { |m| ["FirePledge", "WaterPledge"].include?(m.function) }
end
next score
}
)
#===============================================================================
# TODO: Review score modifiers.
#
#===============================================================================
# FirePledge
Battle::AI::Handlers::MoveEffectScore.add("FirePledge",
proc { |score, move, user, ai, battle|
# Prefer if an ally knows a different Pledge move
ai.each_ally(user.index) do |b, i|
score += 10 if b.check_for_move { |m| ["GrassPledge", "WaterPledge"].include?(m.function) }
end
next score
}
)
#===============================================================================
# TODO: Review score modifiers.
#
#===============================================================================
# WaterPledge
Battle::AI::Handlers::MoveEffectScore.add("WaterPledge",
proc { |score, move, user, ai, battle|
# Prefer if an ally knows a different Pledge move
ai.each_ally(user.index) do |b, i|
score += 10 if b.check_for_move { |m| ["GrassPledge", "FirePledge"].include?(m.function) }
end
next score
}
)
#===============================================================================
# TODO: Review score modifiers.
@@ -392,29 +469,36 @@ Battle::AI::Handlers::MoveFailureCheck.add("UseRandomUserMoveIfAsleep",
# StealAndUseBeneficialStatusMove
#===============================================================================
# TODO: Review score modifiers.
#
#===============================================================================
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("ReplaceMoveThisBattleWithTargetLastMoveUsed",
proc { |move, user, target, ai, battle|
next true if user.effects[PBEffects::Transform] || user.battler.pbHasMove?(move.id)
last_move_data = GameData::Move.try_get(target.battler.lastRegularMoveUsed)
next true if !last_move_data ||
user.battler.pbHasMove?(target.battler.lastRegularMoveUsed) ||
move.move.moveBlacklist.include?(last_move_data.function_code) ||
last_move_data.type == :SHADOW
next true if user.effects[PBEffects::Transform] || !user.battler.pbHasMove?(move.id)
if user.faster_than?(target)
last_move_data = GameData::Move.try_get(target.battler.lastRegularMoveUsed)
next true if !last_move_data ||
user.battler.pbHasMove?(target.battler.lastRegularMoveUsed) ||
move.move.moveBlacklist.include?(last_move_data.function_code) ||
last_move_data.type == :SHADOW
end
}
)
Battle::AI::Handlers::MoveEffectScore.add("ReplaceMoveThisBattleWithTargetLastMoveUsed",
proc { |score, move, user, ai, battle|
# Generally don't prefer, as this wastes the user's turn just to gain a move
# of unknown utility
score -= 8
# Slightly prefer if this move will definitely succeed, just for the sake of
# getting rid of this move
score += 5 if user.faster_than?(target)
next score
}
)
#===============================================================================
# TODO: Review score modifiers.
#
#===============================================================================
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("ReplaceMoveWithTargetLastMoveUsed",
proc { |move, user, target, ai, battle|
next true if user.effects[PBEffects::Transform] || !user.battler.pbHasMove?(move.id)
last_move_data = GameData::Move.try_get(target.battler.lastRegularMoveUsed)
next true if !last_move_data ||
user.battler.pbHasMove?(target.battler.lastRegularMoveUsed) ||
move.move.moveBlacklist.include?(last_move_data.function_code) ||
last_move_data.type == :SHADOW
}
)
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.copy("ReplaceMoveThisBattleWithTargetLastMoveUsed",
"ReplaceMoveWithTargetLastMoveUsed")
Battle::AI::Handlers::MoveEffectScore.copy("ReplaceMoveThisBattleWithTargetLastMoveUsed",
"ReplaceMoveWithTargetLastMoveUsed")

View File

@@ -22,8 +22,8 @@ Battle::AI::Handlers::MoveFailureCheck.add("SwitchOutUserStatusMove",
Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserStatusMove",
proc { |score, move, user, ai, battle|
next score + 10 if user.wild?
if battle.pbTeamAbleNonActiveCount(user.index) == 1 # Don't switch in ace
score -= 60
if ai.trainer.has_skill_flag?("ReserveLastPokemon") && battle.pbTeamAbleNonActiveCount(user.index) == 1
score -= 60 # Don't switch in ace
else
score += 40 if user.effects[PBEffects::Confusion] > 0
total = 0
@@ -51,8 +51,8 @@ Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserStatusMove",
#===============================================================================
Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserDamagingMove",
proc { |score, move, user, ai, battle|
next 0 if !battle.pbCanChooseNonActive?(user.index) ||
battle.pbTeamAbleNonActiveCount(user.index) == 1 # Don't switch in ace
next 0 if !battle.pbCanChooseNonActive?(user.index)
next 0 if ai.trainer.has_skill_flag?("ReserveLastPokemon") && battle.pbTeamAbleNonActiveCount(user.index) == 1 # Don't switch in ace
}
)
@@ -326,20 +326,24 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HigherPriorityInGrassyTe
)
#===============================================================================
# TODO: Review score modifiers.
#
#===============================================================================
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("LowerPPOfTargetLastMoveBy3",
proc { |score, move, user, target, ai, battle|
last_move = target.battler.pbGetMoveWithID(target.battler.lastRegularMoveUsed)
if last_move && last_move.total_pp > 0 && last_move.pp <= 3
score += 50
if user.faster_than?(target)
last_move = target.battler.pbGetMoveWithID(target.battler.lastRegularMoveUsed)
if last_move && last_move.total_pp > 0
next score + 20 if last_move.pp <= 3 # Will fully deplete the move's PP
next score + 10 if last_move.pp <= 5
next score - 10 if last_move.pp > 9 # Too much PP left to make a difference
end
end
next score
next score # Don't know which move it will affect; treat as just a damaging move
}
)
#===============================================================================
# TODO: Review score modifiers.
#
#===============================================================================
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("LowerPPOfTargetLastMoveBy4",
proc { |move, user, target, ai, battle|
@@ -349,7 +353,13 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("LowerPPOfTargetLastMove
)
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("LowerPPOfTargetLastMoveBy4",
proc { |score, move, user, target, ai, battle|
next Battle::AI::MOVE_USELESS_SCORE
if user.faster_than?(target)
last_move = target.battler.pbGetMoveWithID(target.battler.lastRegularMoveUsed)
next score + 20 if last_move.pp <= 4 # Will fully deplete the move's PP
next score + 10 if last_move.pp <= 6
next score - 10 if last_move.pp > 10 # Too much PP left to make a difference
end
next score - 10 # Don't know which move it will affect; don't prefer
}
)

View File

@@ -2,7 +2,6 @@
# TODO: Review score modifier.
#===============================================================================
# TODO:
# => Prefer move if it will KO the target (moreso if user is slower than target)
# => Don't prefer damaging move if it won't KO, user has Stance Change and
# is in shield form, and user is slower than the target
# => Check memory for past damage dealt by a target's non-high priority move,
@@ -38,6 +37,7 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:shiny_target,
# => If target has previously used a move that will hurt the user by 30% of
# its current HP or more, moreso don't prefer a status move.
# => Include EOR damage in this?
# => Prefer move if it will KO the target (moreso if user is slower than target)
#===============================================================================
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:add_predicted_damage,
proc { |score, move, user, target, ai, battle|

View File

@@ -13,6 +13,7 @@
# PredictMoveFailure
# ScoreMoves
# PreferMultiTargetMoves
# ReserveLastPokemon (don't switch it in if possible)
#===============================================================================
class Battle::AI::AITrainer
attr_reader :side, :trainer_index
@@ -36,6 +37,10 @@ class Battle::AI::AITrainer
@skill_flags.push("ScoreMoves")
@skill_flags.push("PreferMultiTargetMoves")
end
if @skill >= 100
# TODO: Also have flag "DontReserveLastPokemon" which negates this.
@skill_flags.push("ReserveLastPokemon")
end
end
def has_skill_flag?(flag)