From d8f38947f412cd6bd2d6018df86f8a370d2ebb1f Mon Sep 17 00:00:00 2001 From: Maruno17 Date: Sun, 22 Jan 2023 21:21:19 +0000 Subject: [PATCH] AI function code rewrites, added Shadow Sky's missing effects, fixed Shadow End's recoil damage --- .../Scripts/010_Data/002_PBS data/005_Move.rb | 2 +- .../003_Move/003_Move_UsageCalculations.rb | 2 + .../008_MoveEffects_MoveAttributes.rb | 8 +- .../005_AI/051_AI_MoveHandlers_Misc.rb | 116 +++++++++++------- .../054_AI_MoveHandlers_MoveAttributes.rb | 94 ++++++++++---- .../005_AI/056_AI_MoveHandlers_Healing.rb | 6 +- .../005_AI/057_AI_MoveHandlers_Items.rb | 61 +++++++-- .../059_AI_MoveHandlers_SwitchingActing.rb | 42 +++---- .../011_Battle/005_AI/102_AIBattler.rb | 14 ++- Data/Scripts/011_Battle/005_AI/103_AIMove.rb | 1 + .../002_ShadowPokemon_Other.rb | 8 +- 11 files changed, 243 insertions(+), 111 deletions(-) diff --git a/Data/Scripts/010_Data/002_PBS data/005_Move.rb b/Data/Scripts/010_Data/002_PBS data/005_Move.rb index 0de64e6b9..8f4b3b7c3 100644 --- a/Data/Scripts/010_Data/002_PBS data/005_Move.rb +++ b/Data/Scripts/010_Data/002_PBS data/005_Move.rb @@ -750,7 +750,7 @@ module GameData when "12F" then new_code = "TrapTargetInBattle" when "130" then new_code = "UserLosesHalfHP" when "131" then new_code = "StartShadowSkyWeather" - when "132" then new_code = "RemoveAllScreens" + when "132" then new_code = "RemoveAllScreensAndSafeguard" when "133" then new_code = "DoesNothingFailsIfNoAlly" when "134" then new_code = "DoesNothingCongratulations" when "135" then new_code = "FreezeTargetSuperEffectiveAgainstWater" diff --git a/Data/Scripts/011_Battle/003_Move/003_Move_UsageCalculations.rb b/Data/Scripts/011_Battle/003_Move/003_Move_UsageCalculations.rb index 125bfa0bb..0413fe63b 100644 --- a/Data/Scripts/011_Battle/003_Move/003_Move_UsageCalculations.rb +++ b/Data/Scripts/011_Battle/003_Move/003_Move_UsageCalculations.rb @@ -423,6 +423,8 @@ class Battle::Move if target.pbHasType?(:ROCK) && specialMove? && @function != "UseTargetDefenseInsteadOfTargetSpDef" multipliers[:defense_multiplier] *= 1.5 end + when :ShadowSky + multipliers[:final_damage_multiplier] *= 1.5 if type == :SHADOW end # Critical hits if target.damageState.critical 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 4c77b8555..95e66e544 100644 --- a/Data/Scripts/011_Battle/003_Move/008_MoveEffects_MoveAttributes.rb +++ b/Data/Scripts/011_Battle/003_Move/008_MoveEffects_MoveAttributes.rb @@ -839,9 +839,9 @@ class Battle::Move::RemoveScreens < Battle::Move end def pbShowAnimation(id, user, targets, hitNum = 0, showAnimation = true) - if user.pbOpposingSide.effects[PBEffects::LightScreen] > 0 || - user.pbOpposingSide.effects[PBEffects::Reflect] > 0 || - user.pbOpposingSide.effects[PBEffects::AuroraVeil] > 0 + if user.pbOpposingSide.effects[PBEffects::AuroraVeil] > 0 || + user.pbOpposingSide.effects[PBEffects::LightScreen] > 0 || + user.pbOpposingSide.effects[PBEffects::Reflect] > 0 hitNum = 1 # Wall-breaking anim end super @@ -1540,6 +1540,8 @@ class Battle::Move::TypeAndPowerDependOnWeather < Battle::Move ret = :ROCK if GameData::Type.exists?(:ROCK) when :Hail ret = :ICE if GameData::Type.exists?(:ICE) + when :ShadowSky + ret = :NONE end return ret 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 33b815b95..73e392da5 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 @@ -109,8 +109,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("FailsIfUserDamagedThisTu # Check whether user is faster than its foe(s) and could use this move user_faster_count = 0 foe_faster_count = 0 - ai.battlers.each_with_index do |b, i| - next if !user.opposes?(b) || b.battler.fainted? + ai.each_foe_battler(user.side) do |b, i| if user.faster_than?(b) user_faster_count += 1 else @@ -121,10 +120,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("FailsIfUserDamagedThisTu score += 10 if foe_faster_count == 0 # Effects that make the target unlikely to act before the user if ai.trainer.high_skill? - if target.effects[PBEffects::HyperBeam] > 0 || - target.effects[PBEffects::Truant] || - (target.battler.asleep? && target.statusCount > 1) || - target.battler.frozen? + if !target.can_attack? score += 20 elsif target.effects[PBEffects::Confusion] > 1 || target.effects[PBEffects::Attract] == user.index @@ -183,8 +179,7 @@ Battle::AI::Handlers::MoveEffectScore.add("StartSunWeather", score += 15 if user.has_active_item?(:HEATROCK) score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP # Check for Fire/Water moves - ai.battlers.each do |b| - next if !b || b.battler.fainted? + ai.each_battler do |b, i| if b.has_damaging_move_of_type?(:FIRE) score += (b.opposes?(user)) ? -15 : 15 end @@ -229,8 +224,7 @@ Battle::AI::Handlers::MoveEffectScore.add("StartRainWeather", score += 15 if user.has_active_item?(:DAMPROCK) score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP # Check for Fire/Water moves - ai.battlers.each do |b| - next if !b || b.battler.fainted? + ai.each_battler do |b, i| if b.has_damaging_move_of_type?(:WATER) score += (b.opposes?(user)) ? -15 : 15 end @@ -271,8 +265,7 @@ Battle::AI::Handlers::MoveEffectScore.add("StartSandstormWeather", score += 15 if user.has_active_item?(:SMOOTHROCK) score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP # Check for battlers affected by sandstorm's effects - ai.battlers.each do |b| - next if !b || b.battler.fainted? + ai.each_battler do |b, i| if b.battler.takesSandstormDamage? # End of round damage score += (b.opposes?(user)) ? 15 : -15 end @@ -312,8 +305,7 @@ Battle::AI::Handlers::MoveEffectScore.add("StartHailWeather", score += 15 if user.has_active_item?(:ICYROCK) score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP # Check for battlers affected by hail's effects - ai.battlers.each do |b| - next if !b || b.battler.fainted? + ai.each_battler do |b, i| if b.battler.takesHailDamage? # End of round damage score += (b.opposes?(user)) ? 15 : -15 end @@ -667,9 +659,9 @@ Battle::AI::Handlers::MoveFailureCheck.add("UserSwapsPositionsWithAlly", proc { |move, user, ai, battle| num_targets = 0 idxUserOwner = battle.pbGetOwnerIndexFromBattlerIndex(user.index) - user.battler.allAllies.each do |b| + ai.each_ally(user.side) do |b, i| next if battle.pbGetOwnerIndexFromBattlerIndex(b.index) != idxUserOwner - next if !b.near?(user) + next if !b.battler.near?(user.battler) num_targets += 1 end next num_targets != 1 @@ -686,8 +678,7 @@ Battle::AI::Handlers::MoveEffectScore.add("UserSwapsPositionsWithAlly", #=============================================================================== Battle::AI::Handlers::MoveEffectScore.add("BurnAttackerBeforeUserActs", proc { |score, move, user, ai, battle| - ai.battlers.each do |b| - next if !b || !b.opposes?(user) + ai.each_foe_battler(user.side) do |b| next if !b.battler.affectedByContactEffect? next if !b.battler.pbCanBurn?(user.battler, false, move.move) if ai.trainer.high_skill? @@ -700,31 +691,43 @@ Battle::AI::Handlers::MoveEffectScore.add("BurnAttackerBeforeUserActs", ) #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== -Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("AllBattlersLoseHalfHPUserSkipsNextTurn", - proc { |move, user, target, ai, battle| - next target.hp <= 1 - } -) -Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("AllBattlersLoseHalfHPUserSkipsNextTurn", - proc { |score, move, user, target, ai, battle| - score += 20 if target.hp >= target.totalhp / 2 +Battle::AI::Handlers::MoveEffectScore.add("AllBattlersLoseHalfHPUserSkipsNextTurn", + proc { |score, move, user, ai, battle| + # HP halving + foe_hp_lost = 0 + ally_hp_lost = 0 + ai.each_battler do |b, i| + next if b.hp == 1 + if b.battler.opposes?(user.battler) + foe_hp_lost += b.hp / 2 + else + ally_hp_lost += b.hp / 2 + end + end + score += 15 * foe_hp_lost / ally_hp_lost + score -= 15 * ally_hp_lost / foe_hp_lost + # Recharging + score = Battle::AI::Handlers.apply_move_effect_score("AttackAndSkipNextTurn", + score, move, user, ai, battle) next score } ) #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== Battle::AI::Handlers::MoveEffectScore.add("UserLosesHalfHP", proc { |score, move, user, ai, battle| - next score - 40 + score = Battle::AI::Handlers.apply_move_effect_score("UserLosesHalfOfTotalHP", + score, move, user, ai, battle) + next score } ) #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== Battle::AI::Handlers::MoveFailureCheck.copy("StartSunWeather", "StartShadowSkyWeather") @@ -734,39 +737,64 @@ Battle::AI::Handlers::MoveEffectScore.add("StartShadowSkyWeather", battle.pbCheckGlobalAbility(:CLOUDNINE) score += 10 if battle.field.weather != :None # Prefer replacing another weather score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP + # Check for battlers affected by Shadow Sky's effects + ai.each_battler do |b, i| + if b.has_damaging_move_of_type?(:SHADOW) + score += (b.opposes?(user)) ? 15 : -15 + end + if b.battler.takesShadowSkyDamage? # End of round damage + score += (b.opposes?(user)) ? 15 : -15 + end + end + # Check for moves affected by Shadow Sky + # TODO: Check other battlers for these as well? + if ai.trainer.medium_skill? && !user.has_active_item?(:UTILITYUMBRELLA) + if user.has_move_with_function?("TypeAndPowerDependOnWeather") + score += 10 + end + end next score } ) #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== -Battle::AI::Handlers::MoveFailureCheck.add("RemoveAllScreens", +Battle::AI::Handlers::MoveFailureCheck.add("RemoveAllScreensAndSafeguard", proc { |move, user, ai, battle| will_fail = true battle.sides.each do |side| will_fail = false if side.effects[PBEffects::AuroraVeil] > 0 || - side.effects[PBEffects::Reflect] > 0 || side.effects[PBEffects::LightScreen] > 0 || + side.effects[PBEffects::Reflect] > 0 || side.effects[PBEffects::Safeguard] > 0 end next will_fail } ) -Battle::AI::Handlers::MoveEffectScore.add("RemoveAllScreens", +Battle::AI::Handlers::MoveEffectScore.add("RemoveAllScreensAndSafeguard", proc { |score, move, user, ai, battle| - if user.pbOpposingSide.effects[PBEffects::AuroraVeil] > 0 || - user.pbOpposingSide.effects[PBEffects::Reflect] > 0 || - user.pbOpposingSide.effects[PBEffects::LightScreen] > 0 || - user.pbOpposingSide.effects[PBEffects::Safeguard] > 0 - score += 30 + foe_side = user.pbOpposingSide + # Useless if the foe's side has no screens/Safeguard to remove, or if + # they'll end this round anyway + if foe_side.effects[PBEffects::AuroraVeil] <= 1 && + foe_side.effects[PBEffects::LightScreen] <= 1 && + foe_side.effects[PBEffects::Reflect] <= 1 && + foe_side.effects[PBEffects::Safeguard] <= 1 + next Battle::AI::MOVE_USELESS_SCORE end - if user.pbOwnSide.effects[PBEffects::AuroraVeil] > 0 || - user.pbOwnSide.effects[PBEffects::Reflect] > 0 || - user.pbOwnSide.effects[PBEffects::LightScreen] > 0 || - user.pbOwnSide.effects[PBEffects::Safeguard] > 0 - score -= 70 + # Prefer removing opposing screens + score = Battle::AI::Handlers.apply_move_effect_score("RemoveScreens", + score, move, user, ai, battle) + # Don't prefer removing same side screens + ai.each_foe_battler(user.side) do |b, i| + score -= Battle::AI::Handlers.apply_move_effect_score("RemoveScreens", + 0, move, b, ai, battle) + break end + # Safeguard + score += 10 if foe_side.effects[PBEffects::Safeguard] > 0 + score -= 10 if user.pbOwnSide.effects[PBEffects::Safeguard] > 0 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 7f1dc5b43..cbe9a7491 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 @@ -371,9 +371,7 @@ Battle::AI::Handlers::MoveEffectScore.add("DoublePowerIfUserLostHPThisTurn", proc { |score, move, user, ai, battle| # Prefer if user is slower than its foe(s) and the foe(s) can attack ai.each_foe_battler(user.side) do |b, i| - next if user.faster_than?(b) || (b.status == :SLEEP && b.statusCount > 1) || - b.status == :FROZEN || b.effects[PBEffects::HyperBeam] > 0 || - b.effects[PBEffects::Truant] || b.effects[PBEffects::SkyDrop] >= 0 + next if user.faster_than?(b) || !b.can_attack? score += 4 end next score @@ -388,9 +386,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DoublePowerIfTargetLostH # Prefer if a user's ally is faster than the user and that ally can attack ai.each_foe_battler(target.side) do |b, i| next if i == user.index - next if user.faster_than?(b) || (b.status == :SLEEP && b.statusCount > 1) || - b.status == :FROZEN || b.effects[PBEffects::HyperBeam] > 0 || - b.effects[PBEffects::Truant] || b.effects[PBEffects::SkyDrop] >= 0 + next if user.faster_than?(b) || !b.can_attack? score += 4 end next score @@ -428,25 +424,85 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DoublePowerIfTargetNotAc # AlwaysCriticalHit #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== Battle::AI::Handlers::MoveEffectScore.add("EnsureNextCriticalHit", proc { |score, move, user, ai, battle| next Battle::AI::MOVE_USELESS_SCORE if user.effects[PBEffects::LaserFocus] > 0 - # TODO: Useless if user will already always critical hit ("AlwaysCriticalHit" - # or Lucky Chant/crit stage is +3/etc.). - next score + 10 + # Useless if the user's critical hit stage ensures critical hits already, or + # critical hits are impossible (e.g. via Lucky Chant) + crit_stage = move.rough_critical_hit_stage + if crit_stage < 0 || + crit_stage >= Battle::Move::CRITICAL_HIT_RATIOS.length || + Battle::Move::CRITICAL_HIT_RATIOS[crit_stage] == 1 + next Battle::AI::MOVE_USELESS_SCORE + end + # Prefer if user knows a damaging move which won't definitely critical hit + if user.check_for_move { |m| m.damagingMove? && m.function != "AlwaysCriticalHit"} + # 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. + score += 10 + end + next score } ) #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== Battle::AI::Handlers::MoveFailureCheck.add("StartPreventCriticalHitsAgainstUserSide", proc { |move, user, ai, battle| next user.pbOwnSide.effects[PBEffects::LuckyChant] > 0 } ) +Battle::AI::Handlers::MoveEffectScore.add("StartPreventCriticalHitsAgainstUserSide", + proc { |score, move, user, ai, battle| + # Useless if Pokémon on the user's side are immune to critical hits + user_side_immune = true + ai.each_same_side_battler(user.side) do |b, i| + crit_stage = 0 + if b.ability_active? + crit_stage = Battle::AbilityEffects.triggerCriticalCalcFromTarget(b.battler.ability, + b.battler, b.battler, crit_stage) + next if crit_stage < 0 + end + if b.item_active? + crit_stage = Battle::ItemEffects.triggerCriticalCalcFromTarget(b.battler.item, + b.battler, b.battler, crit_stage) + next if crit_stage < 0 + end + user_side_immune = false + break + end + next Battle::AI::MOVE_USELESS_SCORE if user_side_immune + # Prefer if any foe has an increased critical hit rate or moves/effects that + # make critical hits more likely + ai.each_foe_battler(user.side) do |b, i| + crit_stage = 0 + if 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? + 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 m.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 } + # 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. + score += 5 * crit_stage if crit_stage > 0 + score += 10 if b.effects[PBEffects::LaserFocus] > 0 + end + next score + } +) #=============================================================================== # @@ -466,9 +522,7 @@ Battle::AI::Handlers::MoveEffectScore.add("UserEnduresFaintingThisTurn", # Prefer for each foe that can attack useless = true ai.each_foe_battler(user.side) do |b, i| - next if (b.status == :SLEEP && b.statusCount > 1) || - b.status == :FROZEN || b.effects[PBEffects::HyperBeam] > 0 || - b.effects[PBEffects::Truant] || b.effects[PBEffects::SkyDrop] >= 0 + next if !b.can_attack? useless = false score += 4 end @@ -639,15 +693,15 @@ Battle::AI::Handlers::MoveEffectScore.add("StartWeakenDamageAgainstUserSideIfHai Battle::AI::Handlers::MoveEffectScore.add("RemoveScreens", proc { |score, move, user, ai, battle| # Prefer if allies have physical moves that are being weakened - if user.pbOpposingSide.effects[PBEffects::Reflect] > 0 || - user.pbOpposingSide.effects[PBEffects::AuroraVeil] > 0 + if user.pbOpposingSide.effects[PBEffects::Reflect] > 1 || + user.pbOpposingSide.effects[PBEffects::AuroraVeil] > 1 ai.each_same_side_battler(user.side) do |b, i| score += 10 if b.check_for_move { |m| m.physicalMove?(m.type) } end end # Prefer if allies have special moves that are being weakened - if user.pbOpposingSide.effects[PBEffects::LightScreen] > 0 || - user.pbOpposingSide.effects[PBEffects::AuroraVeil] > 0 + if user.pbOpposingSide.effects[PBEffects::LightScreen] > 1 || + user.pbOpposingSide.effects[PBEffects::AuroraVeil] > 1 ai.each_same_side_battler(user.side) do |b, i| score += 10 if b.check_for_move { |m| m.specialMove?(m.type) } end @@ -666,9 +720,7 @@ Battle::AI::Handlers::MoveEffectScore.add("ProtectUser", # Prefer for each foe that can attack useless = true ai.each_foe_battler(user.side) do |b, i| - next if (b.status == :SLEEP && b.statusCount > 1) || - b.status == :FROZEN || b.effects[PBEffects::HyperBeam] > 0 || - b.effects[PBEffects::Truant] || b.effects[PBEffects::SkyDrop] >= 0 + next if !b.can_attack? useless = false score += 4 score += 4 if b.effects[PBEffects::TwoTurnAttack] diff --git a/Data/Scripts/011_Battle/005_AI/056_AI_MoveHandlers_Healing.rb b/Data/Scripts/011_Battle/005_AI/056_AI_MoveHandlers_Healing.rb index ae060fadd..8e4767643 100644 --- a/Data/Scripts/011_Battle/005_AI/056_AI_MoveHandlers_Healing.rb +++ b/Data/Scripts/011_Battle/005_AI/056_AI_MoveHandlers_Healing.rb @@ -646,8 +646,7 @@ Battle::AI::Handlers::MoveEffectScore.add("AttackerFaintsIfUserFaints", score -= 25 # Check whether user is faster than its foe(s) and could use this move user_faster_count = 0 - ai.battlers.each_with_index do |b, i| - next if !user.opposes?(b) || b.battler.fainted? + ai.each_foe_battler(user.side) do |b, i| user_faster_count += 1 if user.faster_than?(b) end next score if user_faster_count == 0 # Move will almost certainly have no effect @@ -670,8 +669,7 @@ Battle::AI::Handlers::MoveEffectScore.add("SetAttackerMovePPTo0IfUserFaints", score -= 25 # Check whether user is faster than its foe(s) and could use this move user_faster_count = 0 - ai.battlers.each_with_index do |b, i| - next if !user.opposes?(b) || b.battler.fainted? + ai.each_foe_battler(user.side) do |b, i| user_faster_count += 1 if user.faster_than?(b) end next score if user_faster_count == 0 # Move will almost certainly have no effect diff --git a/Data/Scripts/011_Battle/005_AI/057_AI_MoveHandlers_Items.rb b/Data/Scripts/011_Battle/005_AI/057_AI_MoveHandlers_Items.rb index 6935dd4c8..18b002249 100644 --- a/Data/Scripts/011_Battle/005_AI/057_AI_MoveHandlers_Items.rb +++ b/Data/Scripts/011_Battle/005_AI/057_AI_MoveHandlers_Items.rb @@ -13,8 +13,8 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTakesTargetItem", user_no_item_preference = ai.battler_wants_item?(user, :NONE) target_item_preference = ai.battler_wants_item?(target, target.item_id) target_no_item_preference = ai.battler_wants_item?(target, :NONE) - score += (user_item_preference - user_no_item_preference) * 5 - score += (target_item_preference - target_no_item_preference) * 5 + score += (user_item_preference - user_no_item_preference) * 3 + score += (target_item_preference - target_no_item_preference) * 3 next score } ) @@ -35,8 +35,8 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TargetTakesUserItem", user_no_item_preference = ai.battler_wants_item?(user, :NONE) target_item_preference = ai.battler_wants_item?(target, user.item_id) target_no_item_preference = ai.battler_wants_item?(target, :NONE) - score -= (user_item_preference - user_no_item_preference) * 5 - score -= (target_item_preference - target_no_item_preference) * 5 + score -= (user_item_preference - user_no_item_preference) * 3 + score -= (target_item_preference - target_no_item_preference) * 3 next score } ) @@ -60,8 +60,8 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTargetSwapItems", user_old_item_preference = ai.battler_wants_item?(user, user.item_id) target_new_item_preference = ai.battler_wants_item?(target, user.item_id) target_old_item_preference = ai.battler_wants_item?(target, target.item_id) - score += (user_new_item_preference - user_old_item_preference) * 5 - score -= (target_new_item_preference - target_old_item_preference) * 5 + score += (user_new_item_preference - user_old_item_preference) * 3 + score -= (target_new_item_preference - target_old_item_preference) * 3 # Don't prefer if user used this move in the last round score -= 15 if user.battler.lastMoveUsed && GameData::Move.get(user.battler.lastMoveUsed).function_code == "UserTargetSwapItems" @@ -81,7 +81,7 @@ Battle::AI::Handlers::MoveEffectScore.add("RestoreUserConsumedItem", proc { |score, move, user, ai, battle| user_new_item_preference = ai.battler_wants_item?(user, user.battler.recycleItem) user_old_item_preference = ai.battler_wants_item?(user, user.item_id) - score += (user_new_item_preference - user_old_item_preference) * 8 + score += (user_new_item_preference - user_old_item_preference) * 4 next score } ) @@ -103,7 +103,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RemoveTargetItem", # User can knock off the target's item; score it target_item_preference = ai.battler_wants_item?(target, target.item_id) target_no_item_preference = ai.battler_wants_item?(target, :NONE) - score += (target_item_preference - target_no_item_preference) * 5 + score += (target_item_preference - target_no_item_preference) * 4 next score } ) @@ -121,7 +121,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DestroyTargetBerryOrGem" # User can incinerate the target's item; score it target_item_preference = ai.battler_wants_item?(target, target.item_id) target_no_item_preference = ai.battler_wants_item?(target, :NONE) - score += (target_item_preference - target_no_item_preference) * 8 + score += (target_item_preference - target_no_item_preference) * 4 next score } ) @@ -142,7 +142,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("CorrodeTargetItem", proc { |score, move, user, target, ai, battle| target_item_preference = ai.battler_wants_item?(target, target.item_id) target_no_item_preference = ai.battler_wants_item?(target, :NONE) - score += (target_item_preference - target_no_item_preference) * 8 + score += (target_item_preference - target_no_item_preference) * 4 next score } ) @@ -258,13 +258,13 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserConsumeTargetBerry", # User can consume the target's berry; score it target_item_preference = ai.battler_wants_item?(target, target.item_id) target_no_item_preference = ai.battler_wants_item?(target, :NONE) - score += (target_item_preference - target_no_item_preference) * 8 + score += (target_item_preference - target_no_item_preference) * 4 next score } ) #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== Battle::AI::Handlers::MoveFailureCheck.add("ThrowUserItemAtTarget", proc { |move, user, ai, battle| @@ -280,3 +280,40 @@ Battle::AI::Handlers::MoveBasePower.add("ThrowUserItemAtTarget", next move.move.pbBaseDamage(power, user.battler, target.battler) } ) +Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("ThrowUserItemAtTarget", + proc { |score, move, user, target, ai, battle| + case user.item_id + when :POISONBARB, :TOXICORB + score = Battle::AI::Handlers.apply_move_effect_against_target_score("PoisonTarget", + score, move, user, target, ai, battle) + when :FLAMEORB + score = Battle::AI::Handlers.apply_move_effect_against_target_score("BurnTarget", + score, move, user, target, ai, battle) + when :LIGHTBALL + score = Battle::AI::Handlers.apply_move_effect_against_target_score("ParalyzeTarget", + score, move, user, target, ai, battle) + when :KINGSROCK, :RAZORFANG + score = Battle::AI::Handlers.apply_move_effect_against_target_score("FlinchTarget", + score, move, user, target, ai, battle) + else + # TODO: Berries/Berry Juice/Mental Herb/White Herb also have Fling + # effects. Should they be accounted for individually, or is it okay + # to consider it bad to Fling these in general? Note that they all + # do minimal damage so this move probably won't be used anyway. + if Battle::ItemEffects::HPHeal[user.item_id] || + Battle::ItemEffects::StatusCure[user.item_id] || + Battle::ItemEffects::OnEndOfUsingMove[user.item_id] || + Battle::ItemEffects::OnEndOfUsingMoveStatRestore[user.item_id] + score -= 8 + end + end + # Prefer if the user doesn't want its held item/don't prefer if it wants to + # keep its held item + user_item_preference = ai.battler_wants_item?(user, user.item_id) + user_no_item_preference = ai.battler_wants_item?(user, :NONE) + score += (user_item_preference - user_no_item_preference) * 4 + # Prefer if user will benefit from not having an item + score += 5 if user.has_active_ability?(:UNBURDEN) + next score + } +) diff --git a/Data/Scripts/011_Battle/005_AI/059_AI_MoveHandlers_SwitchingActing.rb b/Data/Scripts/011_Battle/005_AI/059_AI_MoveHandlers_SwitchingActing.rb index fa918e090..855aeb493 100644 --- a/Data/Scripts/011_Battle/005_AI/059_AI_MoveHandlers_SwitchingActing.rb +++ b/Data/Scripts/011_Battle/005_AI/059_AI_MoveHandlers_SwitchingActing.rb @@ -289,14 +289,10 @@ Battle::AI::Handlers::MoveEffectScore.add("UsedAfterAllyRoundWithDoublePower", ) #=============================================================================== -# TODO: Review score modifiers. This could reasonably used on an ally. +# #=============================================================================== Battle::AI::Handlers::MoveEffectScore.add("TargetActsNext", proc { |score, move, user, ai, battle| - # Useless if the user has no ally - has_ally = false - ai.each_ally(user.index) { |b, i| has_ally = true } - next Battle::AI::MOVE_USELESS_SCORE if !has_ally # Useless if the target is a foe next Battle::AI::MOVE_USELESS_SCORE if target.opposes?(user) # Compare the speeds of all battlers @@ -313,21 +309,27 @@ Battle::AI::Handlers::MoveEffectScore.add("TargetActsNext", next Battle::AI::MOVE_USELESS_SCORE if idx_target < idx_user # Useless if the target will move next anyway next Battle::AI::MOVE_USELESS_SCORE if idx_target - idx_user <= 1 - next score + # Generally not worth using + # NOTE: Because this move can be used against a foe but is being used on an + # ally (since we're here in this code), this move's score will be + # inverted later. A higher score here means this move will be less + # preferred, which is the result we want. + next score + 10 } ) #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== Battle::AI::Handlers::MoveEffectScore.add("TargetActsLast", proc { |score, move, user, ai, battle| - # Useless if the user has no ally - has_ally = false - ai.each_ally(user.index) { |b, i| has_ally = true } - next Battle::AI::MOVE_USELESS_SCORE if !has_ally # Useless if the target is an ally next Battle::AI::MOVE_USELESS_SCORE if !target.opposes?(user) + # Useless if the user has no ally (the point of this move is to let the ally + # get in a hit before the foe) + has_ally = false + ai.each_ally(user.index) { |b, i| has_ally = true if b.can_attack? } + next Battle::AI::MOVE_USELESS_SCORE if !has_ally # Compare the speeds of all battlers speeds = [] ai.each_battler { |b, i| speeds.push([i, rough_stat(:SPEED)]) } @@ -338,11 +340,16 @@ Battle::AI::Handlers::MoveEffectScore.add("TargetActsLast", end idx_user = speeds.index { |ele| ele[0] == user.index } idx_target = speeds.index { |ele| ele[0] == target.index } + idx_slowest_ally = -1 + speeds.each_with_index { |ele, i| idx_slowest_ally = i if user.index.even? == ele[0].even? } # Useless if the target is faster than the user next Battle::AI::MOVE_USELESS_SCORE if idx_target < idx_user # Useless if the target will move last anyway next Battle::AI::MOVE_USELESS_SCORE if idx_target == speeds.length - 1 - next score + # Useless if the slowest ally is faster than the target + next Battle::AI::MOVE_USELESS_SCORE if idx_slowest_ally < idx_target + # Generally not worth using + next score - 10 } ) @@ -378,16 +385,9 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TargetUsesItsLastUsedMov # StartSlowerBattlersActFirst #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== -Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HigherPriorityInGrassyTerrain", - proc { |score, move, user, target, ai, battle| - if ai.trainer.medium_skill? && battle.field.terrain == :Grassy - score += 15 if target.faster_than?(user) - end - next score - } -) +# HigherPriorityInGrassyTerrain #=============================================================================== # diff --git a/Data/Scripts/011_Battle/005_AI/102_AIBattler.rb b/Data/Scripts/011_Battle/005_AI/102_AIBattler.rb index 229fd8a4b..1fed99761 100644 --- a/Data/Scripts/011_Battle/005_AI/102_AIBattler.rb +++ b/Data/Scripts/011_Battle/005_AI/102_AIBattler.rb @@ -212,7 +212,7 @@ class Battle::AI::AIBattler # TODO: Need to check the move's pbCalcTypeModSingle. ret *= effectiveness_of_type_against_single_battler_type(type, defend_type, user) end - ret *= 2 if @battler.effects[PBEffects::TarShot] && type == :FIRE + ret *= 2 if self.effects[PBEffects::TarShot] && type == :FIRE end return ret end @@ -281,6 +281,18 @@ class Battle::AI::AIBattler #============================================================================= + def can_attack? + return false if self.effects[PBEffects::SkyDrop] >= 0 + return false if self.effects[PBEffects::HyperBeam] > 0 + return false if status == :SLEEP && statusCount > 1 + return false if status == :FROZEN # Only 20% chance of unthawing; assune it won't + return false if self.effects[PBEffects::Truant] + return false if self.effects[PBEffects::Flinch] + # NOTE: Confusion/infatuation/paralysis have higher chances of allowing the + # attack, so the battler is treated as able to attack in those cases. + return true + end + def can_switch_lax? return false if wild? @ai.battle.eachInTeamFromBattlerIndex(@index) do |pkmn, i| diff --git a/Data/Scripts/011_Battle/005_AI/103_AIMove.rb b/Data/Scripts/011_Battle/005_AI/103_AIMove.rb index e91a7431a..eae63b93a 100644 --- a/Data/Scripts/011_Battle/005_AI/103_AIMove.rb +++ b/Data/Scripts/011_Battle/005_AI/103_AIMove.rb @@ -546,6 +546,7 @@ class Battle::AI::AIMove # 2 = additional effect will work # 3 = additional effect has an increased chance to work def additional_effect_usability(user, target) + return 3 if self.function == "ThrowUserItemAtTarget" return 0 if @move.addlEffect == 0 # Doesn't have an additional effect return 1 if target.has_active_ability?(:SHIELDDUST) && !@ai.battle.moldBreaker return 3 if (Settings::MECHANICS_GENERATION >= 6 || self.function != "EffectDependsOnEnvironment") && diff --git a/Data/Scripts/014_Pokemon/001_Pokemon-related/002_ShadowPokemon_Other.rb b/Data/Scripts/014_Pokemon/001_Pokemon-related/002_ShadowPokemon_Other.rb index 49a2f355a..63322ed6d 100644 --- a/Data/Scripts/014_Pokemon/001_Pokemon-related/002_ShadowPokemon_Other.rb +++ b/Data/Scripts/014_Pokemon/001_Pokemon-related/002_ShadowPokemon_Other.rb @@ -366,7 +366,7 @@ end #=============================================================================== class Battle::Move::UserLosesHalfHP < Battle::Move::RecoilMove def pbRecoilDamage(user, target) - return (target.damageState.totalHPLost / 2.0).round + return (user.hp / 2.0).round end def pbEffectAfterAllHits(user, target) @@ -398,13 +398,13 @@ end # Ends the effects of Light Screen, Reflect and Safeguard on both sides. # (Shadow Shed) #=============================================================================== -class Battle::Move::RemoveAllScreens < Battle::Move +class Battle::Move::RemoveAllScreensAndSafeguard < Battle::Move def pbMoveFailed?(user, targets) will_fail = true @battle.sides.each do |side| will_fail = false if side.effects[PBEffects::AuroraVeil] > 0 || - side.effects[PBEffects::Reflect] > 0 || side.effects[PBEffects::LightScreen] > 0 || + side.effects[PBEffects::Reflect] > 0 || side.effects[PBEffects::Safeguard] > 0 end if will_fail @@ -417,8 +417,8 @@ class Battle::Move::RemoveAllScreens < Battle::Move def pbEffectGeneral(user) @battle.sides.each do |i| i.effects[PBEffects::AuroraVeil] = 0 - i.effects[PBEffects::Reflect] = 0 i.effects[PBEffects::LightScreen] = 0 + i.effects[PBEffects::Reflect] = 0 i.effects[PBEffects::Safeguard] = 0 end @battle.pbDisplay(_INTL("It broke all barriers!"))