From 01e98c8f97e25619a75357cb87ed3f0f4860b2ab Mon Sep 17 00:00:00 2001 From: Maruno17 Date: Wed, 8 Feb 2023 22:19:45 +0000 Subject: [PATCH] Rewrote AI for move function codes for counters, protection removal, Wonder Room, Substitute. Fixed Counters working with damage absorbed by a substitute. --- .../011_Battle/003_Move/002_Move_Usage.rb | 14 +-- .../008_MoveEffects_MoveAttributes.rb | 14 +-- .../005_AI/051_AI_MoveHandlers_Misc.rb | 8 +- .../052_AI_MoveHandlers_BattlerStats.rb | 101 ++++++++++++------ .../054_AI_MoveHandlers_MoveAttributes.rb | 23 +++- .../058_AI_MoveHandlers_ChangeMoveEffect.rb | 77 +++++++++---- 6 files changed, 154 insertions(+), 83 deletions(-) diff --git a/Data/Scripts/011_Battle/003_Move/002_Move_Usage.rb b/Data/Scripts/011_Battle/003_Move/002_Move_Usage.rb index d7065eb92..e7e86a307 100644 --- a/Data/Scripts/011_Battle/003_Move/002_Move_Usage.rb +++ b/Data/Scripts/011_Battle/003_Move/002_Move_Usage.rb @@ -374,12 +374,14 @@ class Battle::Move # code. moveType = nil moveType = :NORMAL if @function == "TypeDependsOnUserIVs" # Hidden Power - if physicalMove?(moveType) - target.effects[PBEffects::Counter] = damage - target.effects[PBEffects::CounterTarget] = user.index - elsif specialMove?(moveType) - target.effects[PBEffects::MirrorCoat] = damage - target.effects[PBEffects::MirrorCoatTarget] = user.index + if !target.damageState.substitute + if physicalMove?(moveType) + target.effects[PBEffects::Counter] = damage + target.effects[PBEffects::CounterTarget] = user.index + elsif specialMove?(moveType) + target.effects[PBEffects::MirrorCoat] = damage + target.effects[PBEffects::MirrorCoatTarget] = user.index + end end if target.effects[PBEffects::Bide] > 0 target.effects[PBEffects::BideDamage] += damage diff --git a/Data/Scripts/011_Battle/003_Move/008_MoveEffects_MoveAttributes.rb b/Data/Scripts/011_Battle/003_Move/008_MoveEffects_MoveAttributes.rb index 972acdc01..d47a661b7 100644 --- a/Data/Scripts/011_Battle/003_Move/008_MoveEffects_MoveAttributes.rb +++ b/Data/Scripts/011_Battle/003_Move/008_MoveEffects_MoveAttributes.rb @@ -991,20 +991,8 @@ end #=============================================================================== # Ends target's protections immediately. (Hyperspace Hole) #=============================================================================== -class Battle::Move::RemoveProtectionsBypassSubstitute < Battle::Move +class Battle::Move::RemoveProtectionsBypassSubstitute < Battle::Move::RemoveProtections def ignoresSubstitute?(user); return true; end - - def pbEffectAgainstTarget(user, target) - target.effects[PBEffects::BanefulBunker] = false - target.effects[PBEffects::KingsShield] = false - target.effects[PBEffects::Obstruct] = false - target.effects[PBEffects::Protect] = false - target.effects[PBEffects::SpikyShield] = false - target.pbOwnSide.effects[PBEffects::CraftyShield] = false - target.pbOwnSide.effects[PBEffects::MatBlock] = false - target.pbOwnSide.effects[PBEffects::QuickGuard] = false - target.pbOwnSide.effects[PBEffects::WideGuard] = false - end end #=============================================================================== diff --git a/Data/Scripts/011_Battle/005_AI/051_AI_MoveHandlers_Misc.rb b/Data/Scripts/011_Battle/005_AI/051_AI_MoveHandlers_Misc.rb index 73e392da5..237516b2d 100644 --- a/Data/Scripts/011_Battle/005_AI/051_AI_MoveHandlers_Misc.rb +++ b/Data/Scripts/011_Battle/005_AI/051_AI_MoveHandlers_Misc.rb @@ -601,7 +601,7 @@ Battle::AI::Handlers::MoveEffectScore.add("SwapSideEffects", ) #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== Battle::AI::Handlers::MoveFailureCheck.add("UserMakeSubstitute", proc { |move, user, ai, battle| @@ -612,7 +612,11 @@ Battle::AI::Handlers::MoveFailureCheck.add("UserMakeSubstitute", Battle::AI::Handlers::MoveEffectScore.add("UserMakeSubstitute", proc { |score, move, user, ai, battle| # Prefer more the higher the user's HP - score += 8.0 * user.hp / user.totalhp + score += (8 * user.hp.to_f / user.totalhp).round + # Prefer if foes don't know any moves that can bypass a substitute + ai.each_battler do |b, i| + score += 4 if !b.check_for_move { |m| m.ignoresSubstitute?(b.battler) } + end # TODO: Predict incoming damage, and prefer if it's greater than # user.totalhp / 4? next score diff --git a/Data/Scripts/011_Battle/005_AI/052_AI_MoveHandlers_BattlerStats.rb b/Data/Scripts/011_Battle/005_AI/052_AI_MoveHandlers_BattlerStats.rb index c4836b060..ee6a5c00d 100644 --- a/Data/Scripts/011_Battle/005_AI/052_AI_MoveHandlers_BattlerStats.rb +++ b/Data/Scripts/011_Battle/005_AI/052_AI_MoveHandlers_BattlerStats.rb @@ -1323,7 +1323,6 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserCopyTargetStatStages ) #=============================================================================== -# TODO: Review score modifiers. # 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? @@ -1507,57 +1506,56 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTargetAverageBaseAtk target_atk = target.base_stat(:ATTACK) target_spatk = target.base_stat(:SPECIAL_ATTACK) next Battle::AI::MOVE_USELESS_SCORE if user_atk >= target_atk && user_spatk >= target_spatk + change_matters = false # Score based on changes to Attack - atk_change_matters = false if target_atk > user_atk # User's Attack will be raised if ai.stat_raise_worthwhile?(user, :ATTACK, true) score += (20 * ((target_atk.to_f / user_atk) - 1)).to_i - atk_change_matters = true + change_matters = true end # Target's Attack will be lowered if ai.stat_drop_worthwhile?(target, :ATTACK, true) score += (20 * ((target_atk.to_f / user_atk) - 1)).to_i - atk_change_matters = true + change_matters = true end elsif target_atk < user_atk # User's Attack will be lowered if ai.stat_drop_worthwhile?(user, :ATTACK, true) score -= (20 * ((user_atk.to_f / target_atk) - 1)).to_i - atk_change_matters = true + change_matters = true end # Target's Attack will be raised if ai.stat_raise_worthwhile?(target, :ATTACK, true) score -= (20 * ((user_atk.to_f / target_atk) - 1)).to_i - atk_change_matters = true + change_matters = true end end # Score based on changes to Special Attack - spatk_change_matters = false if target_spatk > user_spatk # User's Special Attack will be raised if ai.stat_raise_worthwhile?(user, :SPECIAL_ATTACK, true) score += (20 * ((target_spatk.to_f / user_spatk) - 1)).to_i - spatk_change_matters = true + change_matters = true end # Target's Special Attack will be lowered if ai.stat_drop_worthwhile?(target, :SPECIAL_ATTACK, true) score += (20 * ((target_spatk.to_f / user_spatk) - 1)).to_i - spatk_change_matters = true + change_matters = true end elsif target_spatk < user_spatk # User's Special Attack will be lowered if ai.stat_drop_worthwhile?(user, :SPECIAL_ATTACK, true) score -= (20 * ((user_spatk.to_f / target_spatk) - 1)).to_i - spatk_change_matters = true + change_matters = true end # Target's Special Attack will be raised if ai.stat_raise_worthwhile?(target, :SPECIAL_ATTACK, true) score -= (20 * ((user_spatk.to_f / target_spatk) - 1)).to_i - spatk_change_matters = true + change_matters = true end end - next Battle::AI::MOVE_USELESS_SCORE if !atk_change_matters && !spatk_change_matters + next Battle::AI::MOVE_USELESS_SCORE if !change_matters next score } ) @@ -1572,57 +1570,56 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTargetAverageBaseDef target_def = target.base_stat(:DEFENSE) target_spdef = target.base_stat(:SPECIAL_DEFENSE) next Battle::AI::MOVE_USELESS_SCORE if user_def >= target_def && user_spdef >= target_spdef + change_matters = false # Score based on changes to Defense - def_change_matters = false if target_def > user_def # User's Defense will be raised - if ai.stat_raise_worthwhile?(user, :ATTACK, true) + if ai.stat_raise_worthwhile?(user, :DEFENSE, true) score += (20 * ((target_def.to_f / user_def) - 1)).to_i - def_change_matters = true + change_matters = true end # Target's Defense will be lowered - if ai.stat_drop_worthwhile?(target, :ATTACK, true) + if ai.stat_drop_worthwhile?(target, :DEFENSE, true) score += (20 * ((target_def.to_f / user_def) - 1)).to_i - def_change_matters = true + change_matters = true end elsif target_def < user_def # User's Defense will be lowered - if ai.stat_drop_worthwhile?(user, :ATTACK, true) + if ai.stat_drop_worthwhile?(user, :DEFENSE, true) score -= (20 * ((user_def.to_f / target_def) - 1)).to_i - def_change_matters = true + change_matters = true end # Target's Defense will be raised - if ai.stat_raise_worthwhile?(target, :ATTACK, true) + if ai.stat_raise_worthwhile?(target, :DEFENSE, true) score -= (20 * ((user_def.to_f / target_def) - 1)).to_i - def_change_matters = true + change_matters = true end end # Score based on changes to Special Defense - spdef_change_matters = false if target_spdef > user_spdef # User's Special Defense will be raised - if ai.stat_raise_worthwhile?(user, :SPECIAL_ATTACK, true) + if ai.stat_raise_worthwhile?(user, :SPECIAL_DEFENSE, true) score += (20 * ((target_spdef.to_f / user_spdef) - 1)).to_i - spdef_change_matters = true + change_matters = true end # Target's Special Defense will be lowered - if ai.stat_drop_worthwhile?(target, :SPECIAL_ATTACK, true) + if ai.stat_drop_worthwhile?(target, :SPECIAL_DEFENSE, true) score += (20 * ((target_spdef.to_f / user_spdef) - 1)).to_i - spdef_change_matters = true + change_matters = true end elsif target_spdef < user_spdef # User's Special Defense will be lowered - if ai.stat_drop_worthwhile?(user, :SPECIAL_ATTACK, true) + if ai.stat_drop_worthwhile?(user, :SPECIAL_DEFENSE, true) score -= (20 * ((user_spdef.to_f / target_spdef) - 1)).to_i - spdef_change_matters = true + change_matters = true end # Target's Special Defense will be raised - if ai.stat_raise_worthwhile?(target, :SPECIAL_ATTACK, true) + if ai.stat_raise_worthwhile?(target, :SPECIAL_DEFENSE, true) score -= (20 * ((user_spdef.to_f / target_spdef) - 1)).to_i - spdef_change_matters = true + change_matters = true end end - next Battle::AI::MOVE_USELESS_SCORE if !def_change_matters && !spdef_change_matters + next Battle::AI::MOVE_USELESS_SCORE if !change_matters next score } ) @@ -1673,7 +1670,43 @@ Battle::AI::Handlers::MoveEffectScore.add("StartUserSideDoubleSpeed", ) #=============================================================================== -# TODO: Review score modifiers. -# TODO: This code shouldn't make use of target. +# #=============================================================================== -# StartSwapAllBattlersBaseDefensiveStats +Battle::AI::Handlers::MoveEffectScore.add("StartSwapAllBattlersBaseDefensiveStats", + proc { |score, move, user, ai, battle| + any_change_matters = false + ai.each_battler do |b, i| + b_def = b.base_stat(:DEFENSE) + b_spdef = b.base_stat(:SPECIAL_DEFENSE) + next if b_def == b_spdef + score_change = 0 + if b_def > b_spdef + # Battler's Defense will be lowered + if ai.stat_drop_worthwhile?(b, :DEFENSE, true) + score_change -= (20 * ((b_def.to_f / b_spdef) - 1)).to_i + any_change_matters = true + end + # Battler's Special Defense will be raised + if ai.stat_raise_worthwhile?(b, :SPECIAL_DEFENSE, true) + score_change += (20 * ((b_def.to_f / b_spdef) - 1)).to_i + any_change_matters = true + end + else + # Battler's Special Defense will be lowered + if ai.stat_drop_worthwhile?(b, :SPECIAL_DEFENSE, true) + score_change -= (20 * ((b_spdef.to_f / b_def) - 1)).to_i + any_change_matters = true + end + # Battler's Defense will be raised + if ai.stat_raise_worthwhile?(b, :DEFENSE, true) + score_change += (20 * ((b_spdef.to_f / b_def) - 1)).to_i + any_change_matters = true + end + end + score += (b.opposes?(user)) ? -score_change : score_change + end + next Battle::AI::MOVE_USELESS_SCORE if !any_change_matters + next Battle::AI::MOVE_USELESS_SCORE if score <= Battle::AI::MOVE_BASE_SCORE + next score + } +) diff --git a/Data/Scripts/011_Battle/005_AI/054_AI_MoveHandlers_MoveAttributes.rb b/Data/Scripts/011_Battle/005_AI/054_AI_MoveHandlers_MoveAttributes.rb index b9a886da9..0f898cc13 100644 --- a/Data/Scripts/011_Battle/005_AI/054_AI_MoveHandlers_MoveAttributes.rb +++ b/Data/Scripts/011_Battle/005_AI/054_AI_MoveHandlers_MoveAttributes.rb @@ -1138,17 +1138,28 @@ Battle::AI::Handlers::MoveEffectScore.add("ProtectUserSideFromMultiTargetDamagin ) #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== -# RemoveProtections +Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RemoveProtections", + proc { |score, move, user, target, ai, battle| + if target.check_for_move { |m| (m.is_a?(Battle::Move::ProtectMove) || + m.is_a?(Battle::Move::ProtectUserSideFromStatusMoves) || + m.is_a?(Battle::Move::ProtectUserSideFromDamagingMovesIfUserFirstTurn)) && + !m.is_a?(Battle::Move::UserEnduresFaintingThisTurn) } + score += 5 + end + next score + } +) #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== -# RemoveProtectionsBypassSubstitute +Battle::AI::Handlers::MoveEffectAgainstTargetScore.copy("RemoveProtectionsBypassSubstitute", + "RemoveProtections") #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== Battle::AI::Handlers::MoveFailureCheck.add("HoopaRemoveProtectionsBypassSubstituteLowerUserDef1", proc { |move, user, ai, battle| @@ -1157,6 +1168,8 @@ Battle::AI::Handlers::MoveFailureCheck.add("HoopaRemoveProtectionsBypassSubstitu ) Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HoopaRemoveProtectionsBypassSubstituteLowerUserDef1", proc { |score, move, user, target, ai, battle| + score = Battle::AI::Handlers.apply_move_effect_against_target_score("RemoveProtections", + score, move, user, target, ai, battle) next ai.get_score_for_target_stat_drop(score, user, move.move.statDown, false) } ) diff --git a/Data/Scripts/011_Battle/005_AI/058_AI_MoveHandlers_ChangeMoveEffect.rb b/Data/Scripts/011_Battle/005_AI/058_AI_MoveHandlers_ChangeMoveEffect.rb index 432cc68d3..668a71a03 100644 --- a/Data/Scripts/011_Battle/005_AI/058_AI_MoveHandlers_ChangeMoveEffect.rb +++ b/Data/Scripts/011_Battle/005_AI/058_AI_MoveHandlers_ChangeMoveEffect.rb @@ -241,8 +241,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("PowerUpAllyMove", ) #=============================================================================== -# TODO: Review score modifiers. -# TODO: This code shouldn't make use of target. +# #=============================================================================== Battle::AI::Handlers::MoveBasePower.add("CounterPhysicalDamage", proc { |power, move, user, target, ai, battle| @@ -251,22 +250,30 @@ Battle::AI::Handlers::MoveBasePower.add("CounterPhysicalDamage", ) Battle::AI::Handlers::MoveEffectScore.add("CounterPhysicalDamage", proc { |score, move, user, ai, battle| -# next Battle::AI::MOVE_USELESS_SCORE if target.effects[PBEffects::HyperBeam] > 0 - attack = user.rough_stat(:ATTACK) - spatk = user.rough_stat(:SPECIAL_ATTACK) - if attack * 1.5 < spatk - score -= 60 -# elsif ai.trainer.medium_skill? && target.battler.lastMoveUsed -# moveData = GameData::Move.get(target.battler.lastMoveUsed) -# score += 60 if moveData.physical? + has_physical_move = false + ai.each_foe_battler(user.side) do |b, i| + next if !b.can_attack? + next if !b.check_for_move { |m| m.physicalMove?(m.type) && + (user.effects[PBEffects::Substitute] == 0 || + m.ignoresSubstitute?(b.battler)) } + has_physical_move = true + # Prefer if foe has a higher Attack than Special Attack + score += 5 if b.rough_stat(:ATTACK) > b.rough_stat(:SPECIAL_ATTACK) + # Prefer if the last move the foe used was physical + if ai.trainer.medium_skill? && b.battler.lastMoveUsed + score += 5 if GameData::Move.get(b.battler.lastMoveUsed).physical? + end + # Prefer if the foe is taunted into using a damaging move + score += 4 if b.effects[PBEffects::Taunt] > 0 end + # Useless if no foes have a physical move to counter + next Battle::AI::MOVE_USELESS_SCORE if !has_physical_move next score } ) #=============================================================================== -# TODO: Review score modifiers. -# TODO: This code shouldn't make use of target. +# #=============================================================================== Battle::AI::Handlers::MoveBasePower.add("CounterSpecialDamage", proc { |power, move, user, target, ai, battle| @@ -275,22 +282,30 @@ Battle::AI::Handlers::MoveBasePower.add("CounterSpecialDamage", ) Battle::AI::Handlers::MoveEffectScore.add("CounterSpecialDamage", proc { |score, move, user, ai, battle| -# next Battle::AI::MOVE_USELESS_SCORE if target.effects[PBEffects::HyperBeam] > 0 - attack = user.rough_stat(:ATTACK) - spatk = user.rough_stat(:SPECIAL_ATTACK) - if attack > spatk * 1.5 - score -= 60 -# elsif ai.trainer.medium_skill? && target.battler.lastMoveUsed -# moveData = GameData::Move.get(target.battler.lastMoveUsed) -# score += 60 if moveData.special? + has_special_move = false + ai.each_foe_battler(user.side) do |b, i| + next if !b.can_attack? + next if !b.check_for_move { |m| m.specialMove?(m.type) && + (user.effects[PBEffects::Substitute] == 0 || + m.ignoresSubstitute?(b.battler)) } + has_special_move = true + # Prefer if foe has a higher Special Attack than Attack + score += 5 if b.rough_stat(:SPECIAL_ATTACK) > b.rough_stat(:ATTACK) + # Prefer if the last move the foe used was special + if ai.trainer.medium_skill? && b.battler.lastMoveUsed + score += 5 if GameData::Move.get(b.battler.lastMoveUsed).special? + end + # Prefer if the foe is taunted into using a damaging move + score += 4 if b.effects[PBEffects::Taunt] > 0 end + # Useless if no foes have a special move to counter + next Battle::AI::MOVE_USELESS_SCORE if !has_special_move next score } ) #=============================================================================== -# TODO: Review score modifiers. -# TODO: This code shouldn't make use of target. +# #=============================================================================== Battle::AI::Handlers::MoveBasePower.add("CounterDamagePlusHalf", proc { |power, move, user, target, ai, battle| @@ -299,7 +314,23 @@ Battle::AI::Handlers::MoveBasePower.add("CounterDamagePlusHalf", ) Battle::AI::Handlers::MoveEffectScore.add("CounterDamagePlusHalf", proc { |score, move, user, ai, battle| -# next Battle::AI::MOVE_USELESS_SCORE if target.effects[PBEffects::HyperBeam] > 0 + has_damaging_move = false + ai.each_foe_battler(user.side) do |b, i| + next if !b.can_attack? || user.faster_than?(b) + next if !b.check_for_move { |m| m.damagingMove? && + (user.effects[PBEffects::Substitute] == 0 || + m.ignoresSubstitute?(b.battler)) } + has_damaging_move = true + # Prefer if the last move the foe used was damaging + if ai.trainer.medium_skill? && b.battler.lastMoveUsed + score += 5 if GameData::Move.get(b.battler.lastMoveUsed).damaging? + end + # Prefer if the foe is taunted into using a damaging move + score += 6 if b.effects[PBEffects::Taunt] > 0 + end + # Useless if no foes have a damaging move to counter + next Battle::AI::MOVE_USELESS_SCORE if !has_damaging_move + next score } )