From f7578002ea79b078236c59a1a7050406bd22616d Mon Sep 17 00:00:00 2001 From: Maruno17 Date: Thu, 19 Jan 2023 22:30:55 +0000 Subject: [PATCH] Rewrites of disabling move AI function codes, fixed various AI errors --- .../Scripts/010_Data/002_PBS data/005_Move.rb | 8 + .../003_Move/009_MoveEffects_MultiHit.rb | 16 +- .../013_MoveEffects_SwitchingActing.rb | 24 +-- .../004_Scene/004_Scene_PlayAnimations.rb | 2 +- .../011_Battle/005_AI/003_AI_Switch.rb | 2 +- .../011_Battle/005_AI/004_AI_ChooseMove.rb | 20 ++- .../005_AI/020_AI_Move_EffectScoresGeneric.rb | 156 +++++++++++------ .../005_AI/051_AI_MoveHandlers_Misc.rb | 48 ++--- .../052_AI_MoveHandlers_BattlerStats.rb | 22 +-- .../053_AI_MoveHandlers_BattlerOther.rb | 55 +++--- .../054_AI_MoveHandlers_MoveAttributes.rb | 16 +- .../005_AI/055_AI_MoveHandlers_MultiHit.rb | 14 +- .../058_AI_MoveHandlers_ChangeMoveEffect.rb | 10 +- .../059_AI_MoveHandlers_SwitchingActing.rb | 165 ++++++++++++++---- .../011_Battle/005_AI/102_AIBattler.rb | 12 ++ Data/Scripts/011_Battle/005_AI/103_AIMove.rb | 6 +- 16 files changed, 359 insertions(+), 217 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 8fbf276c0..0de64e6b9 100644 --- a/Data/Scripts/010_Data/002_PBS data/005_Move.rb +++ b/Data/Scripts/010_Data/002_PBS data/005_Move.rb @@ -84,6 +84,14 @@ module GameData return GameData::Type.get(@type).special? end + def damaging? + return @category != 2 + end + + def status? + return @category == 2 + end + def hidden_move? GameData::Item.each do |i| return true if i.is_HM? && i.move == @id diff --git a/Data/Scripts/011_Battle/003_Move/009_MoveEffects_MultiHit.rb b/Data/Scripts/011_Battle/003_Move/009_MoveEffects_MultiHit.rb index e6e2b2745..a78029fe6 100644 --- a/Data/Scripts/011_Battle/003_Move/009_MoveEffects_MultiHit.rb +++ b/Data/Scripts/011_Battle/003_Move/009_MoveEffects_MultiHit.rb @@ -134,21 +134,7 @@ end # Hits 2-5 times in a row. If the move does not fail, increases the user's Speed # by 1 stage and decreases the user's Defense by 1 stage. (Scale Shot) #=============================================================================== -class Battle::Move::HitTwoToFiveTimesRaiseUserSpd1LowerUserDef1 < Battle::Move - def multiHitMove?; return true; end - - def pbNumHits(user, targets) - hitChances = [ - 2, 2, 2, 2, 2, 2, 2, - 3, 3, 3, 3, 3, 3, 3, - 4, 4, 4, - 5, 5, 5 - ] - r = @battle.pbRandom(hitChances.length) - r = hitChances.length - 1 if user.hasActiveAbility?(:SKILLLINK) - return hitChances[r] - end - +class Battle::Move::HitTwoToFiveTimesRaiseUserSpd1LowerUserDef1 < Battle::Move::HitTwoToFiveTimes def pbEffectAfterAllHits(user, target) return if target.damageState.unaffected if user.pbCanLowerStatStage?(:DEFENSE, user, self) diff --git a/Data/Scripts/011_Battle/003_Move/013_MoveEffects_SwitchingActing.rb b/Data/Scripts/011_Battle/003_Move/013_MoveEffects_SwitchingActing.rb index ce54da1a9..ab04bead9 100644 --- a/Data/Scripts/011_Battle/003_Move/013_MoveEffects_SwitchingActing.rb +++ b/Data/Scripts/011_Battle/003_Move/013_MoveEffects_SwitchingActing.rb @@ -767,26 +767,26 @@ class Battle::Move::DisableTargetUsingDifferentMove < Battle::Move def initialize(battle, move) super @moveBlacklist = [ - "DisableTargetUsingDifferentMove", # Encore + "DisableTargetUsingDifferentMove", # Encore # Struggle - "Struggle", # Struggle + "Struggle", # Struggle # Moves that affect the moveset "ReplaceMoveThisBattleWithTargetLastMoveUsed", # Mimic - "ReplaceMoveWithTargetLastMoveUsed", # Sketch - "TransformUserIntoTarget", # Transform + "ReplaceMoveWithTargetLastMoveUsed", # Sketch + "TransformUserIntoTarget", # Transform # Moves that call other moves (see also below) - "UseLastMoveUsedByTarget" # Mirror Move + "UseLastMoveUsedByTarget" # Mirror Move ] if Settings::MECHANICS_GENERATION >= 7 @moveBlacklist += [ # Moves that call other moves -# "UseLastMoveUsedByTarget", # Mirror Move # See above - "UseLastMoveUsed", # Copycat - "UseMoveTargetIsAboutToUse", # Me First - "UseMoveDependingOnEnvironment", # Nature Power - "UseRandomUserMoveIfAsleep", # Sleep Talk - "UseRandomMoveFromUserParty", # Assist - "UseRandomMove" # Metronome +# "UseLastMoveUsedByTarget", # Mirror Move # See above + "UseLastMoveUsed", # Copycat + "UseMoveTargetIsAboutToUse", # Me First + "UseMoveDependingOnEnvironment", # Nature Power + "UseRandomUserMoveIfAsleep", # Sleep Talk + "UseRandomMoveFromUserParty", # Assist + "UseRandomMove" # Metronome ] end end diff --git a/Data/Scripts/011_Battle/004_Scene/004_Scene_PlayAnimations.rb b/Data/Scripts/011_Battle/004_Scene/004_Scene_PlayAnimations.rb index 97090b220..31284753e 100644 --- a/Data/Scripts/011_Battle/004_Scene/004_Scene_PlayAnimations.rb +++ b/Data/Scripts/011_Battle/004_Scene/004_Scene_PlayAnimations.rb @@ -442,7 +442,7 @@ class Battle::Scene moveType = moveData.type moveKind = moveData.category moveKind += 3 if target_data.num_targets > 1 || target_data.affects_foe_side - moveKind += 3 if moveKind == 2 && target_data.num_targets > 0 + moveKind += 3 if moveData.status? && target_data.num_targets > 0 # [one target physical, one target special, user status, # multiple targets physical, multiple targets special, non-user status] typeDefaultAnim = { diff --git a/Data/Scripts/011_Battle/005_AI/003_AI_Switch.rb b/Data/Scripts/011_Battle/005_AI/003_AI_Switch.rb index d27d46517..8ce5a23d4 100644 --- a/Data/Scripts/011_Battle/005_AI/003_AI_Switch.rb +++ b/Data/Scripts/011_Battle/005_AI/003_AI_Switch.rb @@ -107,7 +107,7 @@ class Battle::AI if spikes > 0 spikesDmg = [8, 6, 4][spikes - 1] next if pkmn.hp <= pkmn.totalhp / spikesDmg && - !pkmn.hasType?(:FLYING) && !pkmn.hasActiveAbility?(:LEVITATE) + !pkmn.hasType?(:FLYING) && !pkmn.hasAbility?(:LEVITATE) end end # moveType is the type of the target's last used move diff --git a/Data/Scripts/011_Battle/005_AI/004_AI_ChooseMove.rb b/Data/Scripts/011_Battle/005_AI/004_AI_ChooseMove.rb index 39daeecf0..856aa96ec 100644 --- a/Data/Scripts/011_Battle/005_AI/004_AI_ChooseMove.rb +++ b/Data/Scripts/011_Battle/005_AI/004_AI_ChooseMove.rb @@ -29,11 +29,11 @@ class Battle::AI end next end - PBDebug.log_ai("#{@user.name} is considering using #{move.name}...") # Set up move in class variables set_up_move_check(move) # Predict whether the move will fail (generally) if @trainer.has_skill_flag?("PredictMoveFailure") && pbPredictMoveFailure + PBDebug.log_ai("#{@user.name} is considering using #{move.name}...") PBDebug.log_score_change(MOVE_FAIL_SCORE - MOVE_BASE_SCORE, "move will fail") add_move_to_choices(choices, idxMove, MOVE_FAIL_SCORE) next @@ -43,6 +43,7 @@ class Battle::AI case target_data.num_targets when 0 # No targets, affects the user or a side or the whole field # Includes: BothSides, FoeSide, None, User, UserSide + PBDebug.log_ai("#{@user.name} is considering using #{move.name}...") score = MOVE_BASE_SCORE PBDebug.logonerr { score = pbGetMoveScore } add_move_to_choices(choices, idxMove, score) @@ -57,6 +58,7 @@ class Battle::AI # TODO: Should this sometimes consider targeting an ally? See def # pbGetMoveScoreAgainstTarget for more information. next if target_data.targets_foe && !@user.battler.opposes?(b) + PBDebug.log_ai("#{@user.name} is considering using #{move.name} against #{b.name} (#{b.index})...") score = MOVE_BASE_SCORE PBDebug.logonerr { score = pbGetMoveScore([b]) } add_move_to_choices(choices, idxMove, score, b.index) @@ -70,6 +72,7 @@ class Battle::AI next if !@battle.pbMoveCanTarget?(@user.battler.index, b.index, target_data) targets.push(b) end + PBDebug.log_ai("#{@user.name} is considering using #{move.name}...") score = MOVE_BASE_SCORE PBDebug.logonerr { score = pbGetMoveScore(targets) } add_move_to_choices(choices, idxMove, score) @@ -108,7 +111,7 @@ class Battle::AI move.pbOnStartUse(@user.battler, []) # Determine which move is used instead move = Battle::Move.from_pokemon_move(@battle, Pokemon::Move.new(move.npMove)) end - @move.set_up(move, @user) + @move.set_up(move) @battle.moldBreaker = @user.has_mold_breaker? end @@ -130,7 +133,9 @@ class Battle::AI # User is awake and can't use moves that are only usable when asleep return true if !@user.battler.asleep? && @move.move.usableWhenAsleep? # User will be truanting - return true if @user.has_active_ability?(:TRUANT) && @user.effects[PBEffects::Truant] + # TODO: Should Truanting treat all moves as failing? If it does, it will + # trigger switching due to terrible moves. +# return true if @user.has_active_ability?(:TRUANT) && @user.effects[PBEffects::Truant] # Primal weather return true if @battle.pbWeather == :HeavyRain && @move.rough_type == :FIRE return true if @battle.pbWeather == :HarshSun && @move.rough_type == :WATER @@ -301,14 +306,11 @@ class Battle::AI # Decide whether all choices are bad, and if so, try switching instead if @trainer.high_skill? && @user.can_switch_lax? badMoves = false - if (max_score <= MOVE_FAIL_SCORE && user_battler.turnCount > 2) || - (max_score <= MOVE_USELESS_SCORE && user_battler.turnCount > 4) + if max_score <= MOVE_USELESS_SCORE + badMoves = true + elsif max_score < MOVE_BASE_SCORE * move_score_threshold && user_battler.turnCount > 2 badMoves = true if pbAIRandom(100) < 80 end - if !badMoves && max_score <= MOVE_USELESS_SCORE && user_battler.turnCount >= 1 - badMoves = choices.none? { |c| user_battler.moves[c[0]].damagingMove? } - badMoves = false if badMoves && pbAIRandom(100) < 10 - end if badMoves PBDebug.log_ai("#{@user.name} wants to switch due to terrible moves") return if pbEnemyShouldWithdrawEx?(true) diff --git a/Data/Scripts/011_Battle/005_AI/020_AI_Move_EffectScoresGeneric.rb b/Data/Scripts/011_Battle/005_AI/020_AI_Move_EffectScoresGeneric.rb index b867aaaa2..1dac1450c 100644 --- a/Data/Scripts/011_Battle/005_AI/020_AI_Move_EffectScoresGeneric.rb +++ b/Data/Scripts/011_Battle/005_AI/020_AI_Move_EffectScoresGeneric.rb @@ -31,7 +31,7 @@ class Battle::AI end # Don't make score changes if foes have Unaware and target can't make use of # extra stat stages - if !target.check_for_move { |m| m.function == "PowerHigherWithUserPositiveStatStages" } + if !target.has_move_with_function?("PowerHigherWithUserPositiveStatStages") foe_is_aware = false each_foe_battler(target.side) do |b, i| foe_is_aware = true if !b.has_active_ability?(:UNAWARE) @@ -121,7 +121,7 @@ class Battle::AI "PowerHigherWithUserFasterThanTarget", "PowerHigherWithUserPositiveStatStages" ] - if !target.check_for_move { |m| moves_that_prefer_high_speed.include?(m.function) } + if !target.has_move_with_function?(*moves_that_prefer_high_speed) each_foe_battler(target.side) do |b, i| return true if b.faster_than?(target) end @@ -272,7 +272,7 @@ class Battle::AI end old_stage = target.stages[stat] new_stage = old_stage + increment - inc_mult = (stage_mul[new_stage].to_f * stage_div[old_stage]) / (stage_div[new_stage] * stage_mul[old_stage]) + inc_mult = (stage_mul[new_stage + 6].to_f * stage_div[old_stage + 6]) / (stage_div[new_stage + 6] * stage_mul[old_stage + 6]) inc_mult -= 1 inc_mult *= desire_mult # Stat-based score changes @@ -280,8 +280,8 @@ class Battle::AI when :ATTACK # Modify score depending on current stat stage # More strongly prefer if the target has no special moves - if old_stage >= 2 - score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult) + if old_stage >= 2 && increment == 1 + score -= 15 * ((target.opposes?(@user)) ? 1 : desire_mult) else has_special_moves = target.check_for_move { |m| m.specialMove?(m.type) } inc = (has_special_moves) ? 10 : 20 @@ -290,8 +290,8 @@ class Battle::AI when :DEFENSE # Modify score depending on current stat stage - if old_stage >= 2 - score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult) + if old_stage >= 2 && increment == 1 + score -= 15 * ((target.opposes?(@user)) ? 1 : desire_mult) else score += 10 * inc_mult end @@ -299,8 +299,8 @@ class Battle::AI when :SPECIAL_ATTACK # Modify score depending on current stat stage # More strongly prefer if the target has no physical moves - if old_stage >= 2 - score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult) + if old_stage >= 2 && increment == 1 + score -= 15 * ((target.opposes?(@user)) ? 1 : desire_mult) else has_physical_moves = target.check_for_move { |m| m.physicalMove?(m.type) && m.function != "UseUserDefenseInsteadOfUserAttack" && @@ -311,8 +311,8 @@ class Battle::AI when :SPECIAL_DEFENSE # Modify score depending on current stat stage - if old_stage >= 2 - score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult) + if old_stage >= 2 && increment == 1 + score -= 15 * ((target.opposes?(@user)) ? 1 : desire_mult) else score += 10 * inc_mult end @@ -333,12 +333,12 @@ class Battle::AI "PowerHigherWithUserFasterThanTarget", "PowerHigherWithUserPositiveStatStages" ] - if target.check_for_move { |m| moves_that_prefer_high_speed.include?(m.function) } + if target.has_move_with_function?(*moves_that_prefer_high_speed) score += 8 * inc_mult end # Don't prefer if any foe has Gyro Ball each_foe_battler(target.side) do |b, i| - next if !b.check_for_move { |m| m.function == "PowerHigherWithTargetFasterThanUser" } + next if !b.has_move_with_function?("PowerHigherWithTargetFasterThanUser") score -= 8 * inc_mult end # Don't prefer if target has Speed Boost (will be gaining Speed anyway) @@ -348,8 +348,8 @@ class Battle::AI when :ACCURACY # Modify score depending on current stat stage - if old_stage >= 2 - score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult) + if old_stage >= 2 && increment == 1 + score -= 15 * ((target.opposes?(@user)) ? 1 : desire_mult) else min_accuracy = 100 target.battler.moves.each do |m| @@ -372,8 +372,8 @@ class Battle::AI next if eor_damage <= 0 end # Modify score depending on current stat stage - if old_stage >= 2 - score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult) + if old_stage >= 2 && increment == 1 + score -= 15 * ((target.opposes?(@user)) ? 1 : desire_mult) else score += 10 * inc_mult end @@ -381,12 +381,12 @@ class Battle::AI end # Prefer if target has Stored Power - if target.check_for_move { |m| m.function == "PowerHigherWithUserPositiveStatStages" } + if target.has_move_with_function?("PowerHigherWithUserPositiveStatStages") score += 5 * increment * desire_mult end # Don't prefer if any foe has Punishment each_foe_battler(target.side) do |b, i| - next if !b.check_for_move { |m| m.function == "PowerHigherWithTargetPositiveStatStages" } + next if !b.has_move_with_function?("PowerHigherWithTargetPositiveStatStages") score -= 5 * increment * desire_mult end @@ -611,7 +611,7 @@ class Battle::AI #============================================================================= def get_score_for_target_stat_drop(score, target, stat_changes, whole_effect = true, fixed_change = false) whole_effect = false if @move.damagingMove? - # Decide whether the target raising its stat(s) is a good thing + # Decide whether the target lowering its stat(s) is a good thing desire_mult = -1 if target.opposes?(@user) || (@move.pbTarget(@user.battler).targets_foe && target.index != @user.index) @@ -619,7 +619,7 @@ class Battle::AI end # Discard status move/don't prefer damaging move if target has Contrary # TODO: Maybe this should return get_score_for_target_stat_raise if Contrary - # applies and desire_mult < 1. + # applies and desire_mult < 0. if !fixed_change && !@battle.moldBreaker && target.has_active_ability?(:CONTRARY) && desire_mult > 0 ret = (whole_effect) ? MOVE_USELESS_SCORE : score - 20 PBDebug.log_score_change(ret - score, "don't prefer lowering target's stats (it has Contrary)") @@ -643,7 +643,7 @@ class Battle::AI return ret end - # Figure out which stat raises can happen + # Figure out which stat drops can happen real_stat_changes = [] stat_changes.each_with_index do |stat, idx| next if idx.odd? @@ -655,7 +655,7 @@ class Battle::AI end next end - # Calculate amount that stat will be raised by + # Calculate amount that stat will be lowered by decrement = stat_changes[idx + 1] decrement *= 2 if !fixed_change && !@battle.moldBreaker && @user.has_active_ability?(:SIMPLE) decrement = [decrement, 6 + target.stages[stat]].min # The actual stages lost @@ -721,7 +721,7 @@ class Battle::AI "PowerHigherWithUserFasterThanTarget", "PowerHigherWithUserPositiveStatStages" ] - if !target.check_for_move { |m| moves_that_prefer_high_speed.include?(m.function) } + if !target.has_move_with_function?(*moves_that_prefer_high_speed) each_foe_battler(target.side) do |b, i| return true if !b.faster_than?(target) end @@ -786,7 +786,7 @@ class Battle::AI end old_stage = target.stages[stat] new_stage = old_stage - decrement - dec_mult = (stage_mul[old_stage].to_f * stage_div[new_stage]) / (stage_div[old_stage] * stage_mul[new_stage]) + dec_mult = (stage_mul[old_stage + 6].to_f * stage_div[new_stage + 6]) / (stage_div[old_stage + 6] * stage_mul[new_stage + 6]) dec_mult -= 1 dec_mult *= desire_mult # Stat-based score changes @@ -794,8 +794,8 @@ class Battle::AI when :ATTACK # Modify score depending on current stat stage # More strongly prefer if the target has no special moves - if old_stage <= -2 - score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult) + if old_stage <= -2 && decrement == 1 + score -= 15 * ((target.opposes?(@user)) ? 1 : desire_mult) else has_special_moves = target.check_for_move { |m| m.specialMove?(m.type) } dec = (has_special_moves) ? 5 : 10 @@ -804,8 +804,8 @@ class Battle::AI when :DEFENSE # Modify score depending on current stat stage - if old_stage <= -2 - score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult) + if old_stage <= -2 && decrement == 1 + score -= 15 * ((target.opposes?(@user)) ? 1 : desire_mult) else score += 5 * dec_mult end @@ -813,8 +813,8 @@ class Battle::AI when :SPECIAL_ATTACK # Modify score depending on current stat stage # More strongly prefer if the target has no physical moves - if old_stage <= -2 - score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult) + if old_stage <= -2 && decrement == 1 + score -= 15 * ((target.opposes?(@user)) ? 1 : desire_mult) else has_physical_moves = target.check_for_move { |m| m.physicalMove?(m.type) && m.function != "UseUserDefenseInsteadOfUserAttack" && @@ -825,8 +825,8 @@ class Battle::AI when :SPECIAL_DEFENSE # Modify score depending on current stat stage - if old_stage <= -2 - score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult) + if old_stage <= -2 && decrement == 1 + score -= 15 * ((target.opposes?(@user)) ? 1 : desire_mult) else score += 5 * dec_mult end @@ -842,7 +842,7 @@ class Battle::AI end # Prefer if any ally has Electro Ball each_foe_battler(target.side) do |b, i| - next if !b.check_for_move { |m| m.function == "PowerHigherWithUserFasterThanTarget" } + next if !b.has_move_with_function?("PowerHigherWithUserFasterThanTarget") score += 8 * dec_mult end # Don't prefer if target has Speed Boost (will be gaining Speed anyway) @@ -852,8 +852,8 @@ class Battle::AI when :ACCURACY # Modify score depending on current stat stage - if old_stage <= -2 - score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult) + if old_stage <= -2 && decrement == 1 + score -= 15 * ((target.opposes?(@user)) ? 1 : desire_mult) else score += 5 * dec_mult end @@ -861,8 +861,8 @@ class Battle::AI when :EVASION # Modify score depending on current stat stage - if old_stage <= -2 - score -= 20 * ((target.opposes?(@user)) ? 1 : desire_mult) + if old_stage <= -2 && decrement == 1 + score -= 15 * ((target.opposes?(@user)) ? 1 : desire_mult) else score += 5 * dec_mult end @@ -870,12 +870,12 @@ class Battle::AI end # Prefer if target has Stored Power - if target.check_for_move { |m| m.function == "PowerHigherWithUserPositiveStatStages" } + if target.has_move_with_function?("PowerHigherWithUserPositiveStatStages") score += 5 * decrement * desire_mult end # Don't prefer if any foe has Punishment each_foe_battler(target.side) do |b, i| - next if !b.check_for_move { |m| m.function == "PowerHigherWithTargetPositiveStatStages" } + next if !b.has_move_with_function?("PowerHigherWithTargetPositiveStatStages") score -= 5 * decrement * desire_mult end @@ -901,14 +901,14 @@ class Battle::AI ret += (b.opposes?(move_user)) ? -10 : 10 end # Check for Electric moves - if b.check_for_move { |m| m.type == :ELECTRIC && m.damagingMove? } + if b.has_damaging_move_of_type?(:ELECTRIC) ret += (b.opposes?(move_user)) ? -15 : 15 end when :Grassy # End of round healing ret += (b.opposes?(move_user)) ? -10 : 10 # Check for Grass moves - if b.check_for_move { |m| m.type == :GRASS && m.damagingMove? } + if b.has_damaging_move_of_type?(:GRASS) ret += (b.opposes?(move_user)) ? -15 : 15 end when :Misty @@ -919,7 +919,7 @@ class Battle::AI ret += (b.opposes?(move_user)) ? -5 : 5 end # Check for Dragon moves - if b.check_for_move { |m| m.type == :DRAGON && m.damagingMove? } + if b.has_damaging_move_of_type?(:DRAGON) ret += (b.opposes?(move_user)) ? 15 : -15 end when :Psychic @@ -928,7 +928,7 @@ class Battle::AI ret += (b.opposes?(move_user)) ? 10 : -10 end # Check for Psychic moves - if b.check_for_move { |m| m.type == :PSYCHIC && m.damagingMove? } + if b.has_damaging_move_of_type?(:PSYCHIC) ret += (b.opposes?(move_user)) ? -15 : 15 end end @@ -980,16 +980,16 @@ class Battle::AI ret += (b.opposes?(move_user)) ? -15 : 15 end # Moves - if b.check_for_move { |m| ["EffectDependsOnEnvironment", - "SetUserTypesBasedOnEnvironment", - "TypeAndPowerDependOnTerrain", - "UseMoveDependingOnEnvironment"].include?(m.function) } + if b.has_move_with_function?("EffectDependsOnEnvironment", + "SetUserTypesBasedOnEnvironment", + "TypeAndPowerDependOnTerrain", + "UseMoveDependingOnEnvironment") ret += (b.opposes?(move_user)) ? -10 : 10 end - if good_moves && b.check_for_move { |m| good_moves.include?(m.function) } + if good_moves && b.has_move_with_function?(*good_moves) ret += (b.opposes?(move_user)) ? -10 : 10 end - if bad_moves && b.check_for_move { |m| bad_moves.include?(m.function) } + if bad_moves && b.has_move_with_function?(*bad_moves) ret += (b.opposes?(move_user)) ? 10 : -10 end end @@ -1011,7 +1011,7 @@ class Battle::AI :LEFTOVERS ] preferred_items.push(:BLACKSLUDGE) if battler.has_type?(:POISON) - preferred_items.push(:IRONBALL) if battler.check_for_move { |m| m.function = "ThrowUserItemAtTarget" } + preferred_items.push(:IRONBALL) if battler.has_move_with_function?("ThrowUserItemAtTarget") preferred_items.push(:CHOICEBAND) if battler.check_for_move { |m| m.physicalMove?(m.type) } preferred_items.push(:CHOICESPECS) if battler.check_for_move { |m| m.specialMove?(m.type) } unpreferred_items = [ @@ -1029,7 +1029,7 @@ class Battle::AI ret = -2 end # Don't prefer if the battler knows Acrobatics - if battler.check_for_move { |m| m.function == "DoublePowerIfUserHasNoItem" } + if battler.has_move_with_function?("DoublePowerIfUserHasNoItem") ret += (item == :NONE) ? 1 : -1 end return ret @@ -1050,7 +1050,7 @@ class Battle::AI :AIRLOCK => 5, :ANALYTIC => 5, :ANGERPOINT => 4, - :ANTICIPATION => 2, + :ANTICIPATION => 0, :ARENATRAP => 9, :AROMAVEIL => 3, # :ASONECHILLINGNEIGH => 0, @@ -1109,9 +1109,9 @@ class Battle::AI # :FLOWERVEIL => 0, :FLUFFY => 5, :FORECAST => 6, - :FOREWARN => 2, + :FOREWARN => 0, # :FRIENDGUARD => 0, - :FRISK => 3, + :FRISK => 0, :FULLMETALBODY => 4, :FURCOAT => 7, :GALEWINGS => 6, @@ -1322,6 +1322,52 @@ class Battle::AI # TODO: Ideally replace the above list of ratings with context-sensitive # calculations. Should they all go in this method, or should there be # more handlers for each ability? + case ability + when :BLAZE + return 0 if !battler.has_damaging_move_of_type?(:FIRE) + when :CUTECHARM, :RIVALRY + return 0 if battler.gender == 2 + when :FRIENDGUARD, :HEALER, :SYMBOISIS, :TELEPATHY + has_ally = false + each_ally(battler.side) { |b, i| has_ally = true } + return 0 if !has_ally + when :GALEWINGS + return 0 if !battler.check_for_move { |m| m.type == :FLYING } + when :HUGEPOWER, :PUREPOWER + return 0 if !battler.check_for_move { |m| m.physicalMove?(m.type) && + m.function != "UseUserDefenseInsteadOfUserAttack" && + m.function != "UseTargetAttackInsteadOfUserAttack" } + when :IRONFIST + return 0 if !battler.check_for_move { |m| m.punchingMove? } + when :LIQUIDVOICE + return 0 if !battler.check_for_move { |m| m.soundMove? } + when :MEGALAUNCHER + return 0 if !battler.check_for_move { |m| m.pulseMove? } + when :OVERGROW + return 0 if !battler.has_damaging_move_of_type?(:GRASS) + when :PRANKSTER + return 0 if !battler.check_for_move { |m| m.statusMove? } + when :PUNKROCK + return 1 if !battler.check_for_move { |m| m.damagingMove? && m.soundMove? } + when :RECKLESS + return 0 if !battler.check_for_move { |m| m.recoilMove? } + when :ROCKHEAD + return 0 if !battler.check_for_move { |m| m.recoilMove? && !m.is_a?(Battle::Move::CrashDamageIfFailsUnusableInGravity) } + when :RUNAWAY + return 0 if battler.wild? + when :SANDFORCE + return 2 if !battler.has_damaging_move_of_type?(:GROUND, :ROCK, :STEEL) + when :SKILLLINK + return 0 if !battler.check_for_move { |m| m.is_a?(Battle::Move::HitTwoToFiveTimes) } + when :STEELWORKER + return 0 if !battler.has_damaging_move_of_type?(:GRASS) + when :SWARM + return 0 if !battler.has_damaging_move_of_type?(:BUG) + when :TORRENT + return 0 if !battler.has_damaging_move_of_type?(:WATER) + when :TRIAGE + return 0 if !battler.check_for_move { |m| m.healingMove? } + end ret = BASE_ABILITY_RATINGS[ability] || 0 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 e484fcf5a..33b815b95 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 @@ -185,10 +185,10 @@ Battle::AI::Handlers::MoveEffectScore.add("StartSunWeather", # Check for Fire/Water moves ai.battlers.each do |b| next if !b || b.battler.fainted? - if b.check_for_move { |m| m.type == :FIRE && m.damagingMove? } + if b.has_damaging_move_of_type?(:FIRE) score += (b.opposes?(user)) ? -15 : 15 end - if b.check_for_move { |m| m.type == :WATER && m.damagingMove? } + if b.has_damaging_move_of_type?(:WATER) score += (b.opposes?(user)) ? 15 : -15 end end @@ -201,14 +201,14 @@ Battle::AI::Handlers::MoveEffectScore.add("StartSunWeather", elsif user.has_active_ability?(:DRYSKIN) score -= 10 end - if user.check_for_move { |m| ["HealUserDependingOnWeather", - "RaiseUserAtkSpAtk1Or2InSun", - "TwoTurnAttackOneTurnInSun", - "TypeAndPowerDependOnWeather"].include?(m.function) } + if user.has_move_with_function?("HealUserDependingOnWeather", + "RaiseUserAtkSpAtk1Or2InSun", + "TwoTurnAttackOneTurnInSun", + "TypeAndPowerDependOnWeather") score += 10 end - if user.check_for_move { |m| ["ConfuseTargetAlwaysHitsInRainHitsTargetInSky", - "ParalyzeTargetAlwaysHitsInRainHitsTargetInSky"].include?(m.function) } + if user.has_move_with_function?("ConfuseTargetAlwaysHitsInRainHitsTargetInSky", + "ParalyzeTargetAlwaysHitsInRainHitsTargetInSky") score -= 10 end end @@ -231,10 +231,10 @@ Battle::AI::Handlers::MoveEffectScore.add("StartRainWeather", # Check for Fire/Water moves ai.battlers.each do |b| next if !b || b.battler.fainted? - if b.check_for_move { |m| m.type == :WATER && m.damagingMove? } + if b.has_damaging_move_of_type?(:WATER) score += (b.opposes?(user)) ? -15 : 15 end - if b.check_for_move { |m| m.type == :FIRE && m.damagingMove? } + if b.has_damaging_move_of_type?(:FIRE) score += (b.opposes?(user)) ? 15 : -15 end end @@ -244,13 +244,13 @@ Battle::AI::Handlers::MoveEffectScore.add("StartRainWeather", if user.has_active_ability?([:DRYSKIN, :FORECAST, :HYDRATION, :RAINDISH, :SWIFTSWIM]) score += 15 end - if user.check_for_move { |m| ["ConfuseTargetAlwaysHitsInRainHitsTargetInSky", - "ParalyzeTargetAlwaysHitsInRainHitsTargetInSky", - "TypeAndPowerDependOnWeather"].include?(m.function) } + if user.has_move_with_function?("ConfuseTargetAlwaysHitsInRainHitsTargetInSky", + "ParalyzeTargetAlwaysHitsInRainHitsTargetInSky", + "TypeAndPowerDependOnWeather") score += 10 end - if user.check_for_move { |m| ["HealUserDependingOnWeather", - "TwoTurnAttackOneTurnInSun"].include?(m.function) } + if user.has_move_with_function?("HealUserDependingOnWeather", + "TwoTurnAttackOneTurnInSun") score -= 10 end end @@ -286,12 +286,12 @@ Battle::AI::Handlers::MoveEffectScore.add("StartSandstormWeather", if user.has_active_ability?([:SANDFORCE, :SANDRUSH, :SANDVEIL]) score += 15 end - if user.check_for_move { |m| ["HealUserDependingOnSandstorm", - "TypeAndPowerDependOnWeather"].include?(m.function) } + if user.has_move_with_function?("HealUserDependingOnSandstorm", + "TypeAndPowerDependOnWeather") score += 10 end - if user.check_for_move { |m| ["HealUserDependingOnWeather", - "TwoTurnAttackOneTurnInSun"].include?(m.function) } + if user.has_move_with_function?("HealUserDependingOnWeather", + "TwoTurnAttackOneTurnInSun") score -= 10 end end @@ -326,13 +326,13 @@ Battle::AI::Handlers::MoveEffectScore.add("StartHailWeather", elsif user.ability == :ICEFACE score += 15 end - if user.check_for_move { |m| ["FreezeTargetAlwaysHitsInHail", - "StartWeakenDamageAgainstUserSideIfHail", - "TypeAndPowerDependOnWeather"].include?(m.function) } + if user.has_move_with_function?("FreezeTargetAlwaysHitsInHail", + "StartWeakenDamageAgainstUserSideIfHail", + "TypeAndPowerDependOnWeather") score += 10 end - if user.check_for_move { |m| ["HealUserDependingOnWeather", - "TwoTurnAttackOneTurnInSun"].include?(m.function) } + if user.has_move_with_function?("HealUserDependingOnWeather", + "TwoTurnAttackOneTurnInSun") score -= 10 end end 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 0d3807dd3..c08fa667f 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 @@ -84,7 +84,7 @@ Battle::AI::Handlers::MoveEffectScore.add("RaiseUserDefense1CurlUpUser", proc { |score, move, user, ai, battle| score = ai.get_score_for_target_stat_raise(score, user, move.move.statUp) if !user.effects[PBEffects::DefenseCurl] && - user.check_for_move { |m| m.function == "MultiTurnAttackPowersUpEachTurn" } + user.has_move_with_function?("MultiTurnAttackPowersUpEachTurn") score += 10 end next score @@ -148,7 +148,7 @@ Battle::AI::Handlers::MoveFailureCheck.copy("RaiseUserSpDef1", Battle::AI::Handlers::MoveEffectScore.add("RaiseUserSpDef1PowerUpElectricMove", proc { |score, move, user, ai, battle| score = ai.get_score_for_target_stat_raise(score, user, move.move.statUp) - if user.check_for_move { |m| m.damagingMove? && m.type == :ELECTRIC } + if user.has_damaging_move_of_type?(:ELECTRIC) score += 10 end next score @@ -201,14 +201,14 @@ Battle::AI::Handlers::MoveEffectScore.add("RaiseUserSpeed2LowerUserWeight", # because of those modifiers, and the score changes may need to be # different accordingly. if user.battler.pokemon.weight - user.effects[PBEffects::WeightChange] > 1 - if user.check_for_move { |m| m.function == "PowerHigherWithUserHeavierThanTarget" } + if user.has_move_with_function?("PowerHigherWithUserHeavierThanTarget") score -= 10 end ai.each_foe_battler(user.side) do |b, i| - if b.check_for_move { |m| m.function == "PowerHigherWithUserHeavierThanTarget" } + if b.has_move_with_function?("PowerHigherWithUserHeavierThanTarget") score -= 10 end - if b.check_for_move { |m| m.function == "PowerHigherWithTargetWeight" } + if b.has_move_with_function?("PowerHigherWithTargetWeight") score += 10 end # TODO: Check foes for Sky Drop and whether the user is too heavy for it @@ -528,7 +528,7 @@ Battle::AI::Handlers::MoveEffectScore.add("StartRaiseUserAtk1WhenDamaged", m.function != "UseUserDefenseInsteadOfUserAttack" && m.function != "UseTargetAttackInsteadOfUserAttack" } score += 8 - elsif user.check_for_move { |m| m.function == "PowerHigherWithUserPositiveStatStages" } + elsif user.has_move_with_function?("PowerHigherWithUserPositiveStatStages") score += 4 end next score @@ -715,12 +715,12 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetRandomStat2", score += ((100 * target.hp / target.totalhp) - 50) / 4 # +5 to -12 end # Prefer if target has Stored Power - if target.check_for_move { |m| m.function == "PowerHigherWithUserPositiveStatStages" } + if target.has_move_with_function?("PowerHigherWithUserPositiveStatStages") score += 8 end # Don't prefer if any foe has Punishment ai.each_foe_battler(target.side) do |b, i| - next if !b.check_for_move { |m| m.function == "PowerHigherWithTargetPositiveStatStages" } + next if !b.has_move_with_function?("PowerHigherWithTargetPositiveStatStages") score -= 5 end next score @@ -921,7 +921,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("LowerTargetSpeed1MakeTar if !target.effects[PBEffects::TarShot] eff = target.effectiveness_of_type_against_battler(:FIRE) if !Effectiveness.ineffective?(eff) - score += 8 * eff if user.check_for_move { |m| m.damagingMove? && m.pbCalcType(user.battler) == :FIRE } + score += 8 * eff if user.has_damaging_move_of_type?(:FIRE) end end next score @@ -1119,7 +1119,7 @@ Battle::AI::Handlers::MoveFailureCheck.add("RaisePlusMinusUserAndAlliesAtkSpAtk1 ) Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaisePlusMinusUserAndAlliesAtkSpAtk1", proc { |move, user, target, ai, battle| - next true if !target.hasActiveAbility?([:MINUS, :PLUS]) + next true if !target.has_active_ability?([:MINUS, :PLUS]) next !target.battler.pbCanRaiseStatStage?(:ATTACK, user.battler, move.move) && !target.battler.pbCanRaiseStatStage?(:SPECIAL_ATTACK, user.battler, move.move) } @@ -1157,7 +1157,7 @@ Battle::AI::Handlers::MoveFailureCheck.add("RaisePlusMinusUserAndAlliesDefSpDef1 ) Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaisePlusMinusUserAndAlliesDefSpDef1", proc { |move, user, target, ai, battle| - next true if !target.hasActiveAbility?([:MINUS, :PLUS]) + next true if !target.has_active_ability?([:MINUS, :PLUS]) next !target.battler.pbCanRaiseStatStage?(:DEFENSE, user.battler, move.move) && !target.battler.pbCanRaiseStatStage?(:SPECIAL_DEFENSE, user.battler, move.move) } diff --git a/Data/Scripts/011_Battle/005_AI/053_AI_MoveHandlers_BattlerOther.rb b/Data/Scripts/011_Battle/005_AI/053_AI_MoveHandlers_BattlerOther.rb index 64c1e78d7..38bddfdff 100644 --- a/Data/Scripts/011_Battle/005_AI/053_AI_MoveHandlers_BattlerOther.rb +++ b/Data/Scripts/011_Battle/005_AI/053_AI_MoveHandlers_BattlerOther.rb @@ -25,10 +25,10 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("SleepTarget", score += 15 # Prefer if the user or an ally has a move/ability that is better if the target is asleep ai.each_same_side_battler(user.side) do |b, i| - score += 5 if b.check_for_move { |m| ["DoublePowerIfTargetAsleepCureTarget", - "DoublePowerIfTargetStatusProblem", - "HealUserByHalfOfDamageDoneIfTargetAsleep", - "StartDamageTargetEachTurnIfTargetAsleep"].include?(m.function) } + score += 5 if b.has_move_with_function?("DoublePowerIfTargetAsleepCureTarget", + "DoublePowerIfTargetStatusProblem", + "HealUserByHalfOfDamageDoneIfTargetAsleep", + "StartDamageTargetEachTurnIfTargetAsleep") score += 10 if b.has_active_ability?(:BADDREAMS) end # Don't prefer if target benefits from having the sleep status problem @@ -119,8 +119,8 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("PoisonTarget", score += 10 * target.hp / target.totalhp # Prefer if the user or an ally has a move/ability that is better if the target is poisoned ai.each_same_side_battler(user.side) do |b, i| - score += 5 if b.check_for_move { |m| ["DoublePowerIfTargetPoisoned", - "DoublePowerIfTargetStatusProblem"].include?(m.function) } + score += 5 if b.has_move_with_function?("DoublePowerIfTargetPoisoned", + "DoublePowerIfTargetStatusProblem") score += 10 if b.has_active_ability?(:MERCILESS) end # Don't prefer if target benefits from having the poison status problem @@ -128,8 +128,8 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("PoisonTarget", score -= 25 if target.has_active_ability?(:POISONHEAL) score -= 15 if target.has_active_ability?(:SYNCHRONIZE) && user.battler.pbCanPoisonSynchronize?(target.battler) - score -= 5 if target.check_for_move { |m| ["DoublePowerIfUserPoisonedBurnedParalyzed", - "CureUserBurnPoisonParalysis"].include?(m.function) } + score -= 5 if target.has_move_with_function?("DoublePowerIfUserPoisonedBurnedParalyzed", + "CureUserBurnPoisonParalysis") score -= 10 if target.check_for_move { |m| m.function == "GiveUserStatusToTarget" && user.battler.pbCanPoison?(target.battler, false, m) } @@ -214,15 +214,15 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("ParalyzeTarget", score += 5 if target.effects[PBEffects::Attract] >= 0 # Prefer if the user or an ally has a move/ability that is better if the target is paralysed ai.each_same_side_battler(user.side) do |b, i| - score += 5 if b.check_for_move { |m| ["DoublePowerIfTargetParalyzedCureTarget", - "DoublePowerIfTargetStatusProblem"].include?(m.function) } + score += 5 if b.has_move_with_function?("DoublePowerIfTargetParalyzedCureTarget", + "DoublePowerIfTargetStatusProblem") end # Don't prefer if target benefits from having the paralysis status problem score -= 8 if target.has_active_ability?([:GUTS, :MARVELSCALE, :QUICKFEET]) score -= 15 if target.has_active_ability?(:SYNCHRONIZE) && user.battler.pbCanParalyzeSynchronize?(target.battler) - score -= 5 if target.check_for_move { |m| ["DoublePowerIfUserPoisonedBurnedParalyzed", - "CureUserBurnPoisonParalysis"].include?(m.function) } + score -= 5 if target.has_move_with_function?("DoublePowerIfUserPoisonedBurnedParalyzed", + "CureUserBurnPoisonParalysis") score -= 10 if target.check_for_move { |m| m.function == "GiveUserStatusToTarget" && user.battler.pbCanParalyze?(target.battler, false, m) } @@ -306,15 +306,15 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("BurnTarget", end # Prefer if the user or an ally has a move/ability that is better if the target is burned ai.each_same_side_battler(user.side) do |b, i| - score += 5 if b.check_for_move { |m| m.function == "DoublePowerIfTargetStatusProblem" } + score += 5 if b.has_move_with_function?("DoublePowerIfTargetStatusProblem") end # Don't prefer if target benefits from having the burn status problem score -= 8 if target.has_active_ability?([:FLAREBOOST, :GUTS, :MARVELSCALE, :QUICKFEET]) score -= 5 if target.has_active_ability?(:HEATPROOF) score -= 15 if target.has_active_ability?(:SYNCHRONIZE) && user.battler.pbCanBurnSynchronize?(target.battler) - score -= 5 if target.check_for_move { |m| ["DoublePowerIfUserPoisonedBurnedParalyzed", - "CureUserBurnPoisonParalysis"].include?(m.function) } + score -= 5 if target.has_move_with_function?("DoublePowerIfUserPoisonedBurnedParalyzed", + "CureUserBurnPoisonParalysis") score -= 10 if target.check_for_move { |m| m.function == "GiveUserStatusToTarget" && user.battler.pbCanBurn?(target.battler, false, m) } @@ -380,7 +380,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("FreezeTarget", score += 15 # Prefer if the user or an ally has a move/ability that is better if the target is frozen ai.each_same_side_battler(user.side) do |b, i| - score += 5 if b.check_for_move { |m| m.function == "DoublePowerIfTargetStatusProblem" } + score += 5 if b.has_move_with_function?("DoublePowerIfTargetStatusProblem") end # Don't prefer if target benefits from having the frozen status problem # NOTE: The target's Guts/Quick Feet will benefit from the target being @@ -722,15 +722,11 @@ Battle::AI::Handlers::MoveEffectScore.add("SetUserTypesBasedOnEnvironment", new_type = :NORMAL if !GameData::Type.exists?(new_type) end # Check if any user's moves will get STAB because of the type change - if user.check_for_move { |m| m.damagingMove? && m.pbCalcType(user.battler) == new_type } - score += 8 - end + score += 8 if user.has_damaging_move_of_type?(new_type) # Check if any user's moves will lose STAB because of the type change user.battler.pbTypes(true).each do |type| next if type == new_type - if user.check_for_move { |m| m.damagingMove? && m.pbCalcType(user.battler) == type } - score -= 8 - end + score -= 8 if user.has_damaging_move_of_type?(type) end # NOTE: Other things could be considered, like the foes' moves' # effectivenesses against the current and new user's type(s), and @@ -816,10 +812,9 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("SetUserTypesToUserMoveTy end # Check if any user's moves will get STAB because of the type change possible_types.each do |type| - if user.check_for_move { |m| m.damagingMove? && m.pbCalcType(user.battler) == type } - score += 10 - break - end + next if !user.has_damaging_move_of_type?(type) + score += 10 + break end # NOTE: Other things could be considered, like the foes' moves' # effectivenesses against the current and new user's type(s), and @@ -1142,7 +1137,7 @@ Battle::AI::Handlers::MoveEffectScore.add("StartUserAirborne", # Prefer if any foes have damaging Ground-type moves that do 1x or more # damage to the user ai.each_foe_battler(user.side) do |b, i| - next if !b.check_for_move { |m| m.damagingMove? && m.pbCalcType(b.battler) == :GROUND } + next if !b.has_damaging_move_of_type?(:GROUND) next if Effectiveness.resistant?(user.effectiveness_of_type_against_battler(:GROUND, b)) score += 5 end @@ -1181,7 +1176,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("StartUserAirborne", score += 4 if acc < 90 && acc != 0 score += 4 if acc <= 50 && acc != 0 end - next if !b.check_for_move { |m| m.damagingMove? && m.pbCalcType(b.battler) == :GROUND } + next if !b.has_damaging_move_of_type?(:GROUND) next if Effectiveness.resistant?(target.effectiveness_of_type_against_battler(:GROUND, b)) score -= 5 end @@ -1213,7 +1208,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HitsTargetInSkyGroundsTa score += 10 # Prefer if any allies have damaging Ground-type moves ai.each_foe_battler(target.side) do |b, i| - score += 5 if b.check_for_move { |m| m.damagingMove? && m.pbCalcType(b.battler) == :GROUND } + score += 5 if b.has_damaging_move_of_type?(:GROUND) end # Don't prefer if terrain exists (which the target will become affected by) if ai.trainer.medium_skill? @@ -1247,7 +1242,7 @@ Battle::AI::Handlers::MoveEffectScore.add("StartGravity", # Prefer if allies have any damaging Ground moves they'll be able to use # on a grounded foe, and vice versa ai.each_foe_battler(b.side) do |b2, j| - if b2.check_for_move { |m| m.damagingMove? && m.pbCalcType(b2.battler) == :GROUND } + if b2.has_damaging_move_of_type?(:GROUND) score += (user.opposes?(b2)) ? -5 : 5 end end 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 5055e0297..7f1dc5b43 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 @@ -504,13 +504,13 @@ Battle::AI::Handlers::MoveEffectScore.add("StartWeakenElectricMoves", end # Prefer if foes have Electric moves ai.each_foe_battler(user.side) do |b, i| - next if !b.check_for_move { |m| m.damagingMove? && m.pbCalcType(b.battler) == :ELECTRIC } + next if !b.has_damaging_move_of_type?(:ELECTRIC) score += 10 score += 5 if !b.check_for_move { |m| m.damagingMove? && m.pbCalcType(b.battler) != :ELECTRIC } end # Don't prefer if any allies have Electric moves ai.each_same_side_battler(user.side) do |b, i| - next if !b.check_for_move { |m| m.damagingMove? && m.pbCalcType(b.battler) == :ELECTRIC } + next if !b.has_damaging_move_of_type?(:ELECTRIC) score -= 8 score -= 4 if !b.check_for_move { |m| m.damagingMove? && m.pbCalcType(b.battler) != :ELECTRIC } end @@ -535,13 +535,13 @@ Battle::AI::Handlers::MoveEffectScore.add("StartWeakenFireMoves", end # Prefer if foes have Fire moves ai.each_foe_battler(user.side) do |b, i| - next if !b.check_for_move { |m| m.damagingMove? && m.pbCalcType(b.battler) == :FIRE } + next if !b.has_damaging_move_of_type?(:FIRE) score += 10 score += 5 if !b.check_for_move { |m| m.damagingMove? && m.pbCalcType(b.battler) != :FIRE } end # Don't prefer if any allies have Fire moves ai.each_same_side_battler(user.side) do |b, i| - next if !b.check_for_move { |m| m.damagingMove? && m.pbCalcType(b.battler) == :FIRE } + next if !b.has_damaging_move_of_type?(:FIRE) score -= 8 score -= 4 if !b.check_for_move { |m| m.damagingMove? && m.pbCalcType(b.battler) != :FIRE } end @@ -949,9 +949,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("EnsureNextMoveAlwaysHits next Battle::AI::MOVE_USELESS_SCORE if user.has_active_ability?(:NOGUARD) || target.has_active_ability?(:NOGUARD) next Battle::AI::MOVE_USELESS_SCORE if target.effects[PBEffects::Telekinesis] > 0 # Prefer if the user knows moves with low accuracy - # TODO: This isn't the correct use of check_for_move, since it should just - # loop through them instead. - user.check_for_move do |m| + user.battler.eachMove do |m| next if target.effects[PBEffects::Minimize] && m.tramplesMinimize? && Settings::MECHANICS_GENERATION >= 6 # TODO: There are other effects that make a move certain to hit. Account # for those as well. Score this move useless if no moves would @@ -978,7 +976,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("StartNegateTargetEvasion # Check if the user knows any moves that would benefit from negating the # target's Ghost type immunity if target.has_type?(:GHOST) - user.check_for_move do |m| + user.battler.eachMove do |m| next if !m.damagingMove? score += 10 if Effectiveness.ineffective_type?(m.pbCalcType(user.battler), :GHOST) end @@ -998,7 +996,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("StartNegateTargetEvasion # Check if the user knows any moves that would benefit from negating the # target's Dark type immunity if target.has_type?(:DARK) - user.check_for_move do |m| + user.battler.eachMove do |m| next if !m.damagingMove? score += 10 if Effectiveness.ineffective_type?(m.pbCalcType(user.battler), :DARK) end diff --git a/Data/Scripts/011_Battle/005_AI/055_AI_MoveHandlers_MultiHit.rb b/Data/Scripts/011_Battle/005_AI/055_AI_MoveHandlers_MultiHit.rb index 7f85d5dab..e1caa087c 100644 --- a/Data/Scripts/011_Battle/005_AI/055_AI_MoveHandlers_MultiHit.rb +++ b/Data/Scripts/011_Battle/005_AI/055_AI_MoveHandlers_MultiHit.rb @@ -215,27 +215,27 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttack", has_protect_move = false if move.move.pbTarget(user).num_targets > 1 && (Settings::MECHANICS_GENERATION >= 7 || move.damagingMove?) - if target.check_for_move { |m| m.function == "ProtectUserSideFromMultiTargetDamagingMoves" } + if target.has_move_with_function?("ProtectUserSideFromMultiTargetDamagingMoves") has_protect_move = true end end if move.move.canProtectAgainst? - if target.check_for_move { |m| ["ProtectUser", - "ProtectUserFromTargetingMovesSpikyShield", - "ProtectUserBanefulBunker"].include?(m.function) } + if target.has_move_with_function?("ProtectUser", + "ProtectUserFromTargetingMovesSpikyShield", + "ProtectUserBanefulBunker") has_protect_move = true end if move.damagingMove? # NOTE: Doesn't check for Mat Block because it only works on its # user's first turn in battle, so it can't be used in response # to this move charging up. - if target.check_for_move { |m| ["ProtectUserFromDamagingMovesKingsShield", - "ProtectUserFromDamagingMovesObstruct"].include?(m.function) } + if target.has_move_with_function?("ProtectUserFromDamagingMovesKingsShield", + "ProtectUserFromDamagingMovesObstruct") has_protect_move = true end end if move.rough_priority(user) > 0 - if target.check_for_move { |m| m.function == "ProtectUserSideFromPriorityMoves" } + if target.has_move_with_function?("ProtectUserSideFromPriorityMoves") has_protect_move = true end end 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 2ae1fe638..5fb12105d 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 @@ -206,7 +206,7 @@ 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" } + score += 10 if b.has_move_with_function?("DoublePowerAfterFusionBolt") end next score } @@ -219,7 +219,7 @@ 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" } + score += 10 if b.has_move_with_function?("DoublePowerAfterFusionFlare") end next score } @@ -380,7 +380,7 @@ 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) } + score += 10 if b.has_move_with_function?("FirePledge", "WaterPledge") end next score } @@ -393,7 +393,7 @@ 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) } + score += 10 if b.has_move_with_function?("GrassPledge", "WaterPledge") end next score } @@ -406,7 +406,7 @@ 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) } + score += 10 if b.has_move_with_function?("GrassPledge", "FirePledge") end 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 b7e4a8183..fa918e090 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 @@ -30,13 +30,7 @@ Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserStatusMove", else score -= total * 10 # special case: user has no damaging moves - hasDamagingMove = false - user.battler.eachMove do |m| - next if !m.damagingMove? - hasDamagingMove = true - break - end - score += 75 if !hasDamagingMove + score += 75 if !user.check_for_move { |m| m.damagingMove? } end end next score @@ -95,13 +89,7 @@ Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserPassOnEffects", else score += total * 10 # special case: user has no damaging moves - hasDamagingMove = false - user.battler.eachMove do |m| - next if !m.damagingMove? - hasDamagingMove = true - break - end - score += 75 if !hasDamagingMove + score += 75 if !user.check_for_move { |m| m.damagingMove? } end else score -= 100 @@ -192,7 +180,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("BindTarget", # Don't prefer if the target can remove the binding (and the binding has an # effect) if (!untrappable && !target.battler.trappedInBattle?) || target.battler.takesIndirectDamage? - if target.check_for_move { |m| m.function == "RemoveUserBindingAndEntryHazards" } + if target.has_move_with_function?("RemoveUserBindingAndEntryHazards") score -= 8 end end @@ -440,7 +428,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("LowerPPOfTargetLastMoveB ) #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("DisableTargetLastMoveUsed", proc { |move, user, target, ai, battle| @@ -456,9 +444,25 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("DisableTargetLastMoveUs next will_fail } ) +Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetUsingSameMoveConsecutively", + proc { |score, move, user, target, ai, battle| + next Battle::AI::MOVE_USELESS_SCORE if target.has_active_item?(:MENTALHERB) + # Prefer if the target is locked into using a single move, or will be + if target.effects[PBEffects::ChoiceBand] || + target.has_active_item?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF]) || + target.has_active_ability?(:GORILLATACTICS) + score += 8 + end + # PRefer disabling a damaging move + score += 5 if GameData::Move.try_get(target.battler.lastRegularMoveUsed)&.damaging? + # Inherent preference + score += 8 + next score + } +) #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("DisableTargetUsingSameMoveConsecutively", proc { |move, user, target, ai, battle| @@ -469,13 +473,21 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("DisableTargetUsingSameM ) Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetUsingSameMoveConsecutively", proc { |score, move, user, target, ai, battle| - next 0 if target.effects[PBEffects::Torment] + next Battle::AI::MOVE_USELESS_SCORE if target.has_active_item?(:MENTALHERB) + # Prefer if the target is locked into using a single move, or will be + if target.effects[PBEffects::ChoiceBand] || + target.has_active_item?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF]) || + target.has_active_ability?(:GORILLATACTICS) + score += 8 + end + # Inherent preference + score += 8 next score } ) #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("DisableTargetUsingDifferentMove", proc { |move, user, target, ai, battle| @@ -496,23 +508,44 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("DisableTargetUsingDiffe ) Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetUsingDifferentMove", proc { |score, move, user, target, ai, battle| + next Battle::AI::MOVE_USELESS_SCORE if target.has_active_item?(:MENTALHERB) if user.faster_than?(target) - moveData = GameData::Move.get(target.battler.lastRegularMoveUsed) - if moveData.category == 2 && # Status move - [:User, :BothSides].include?(moveData.target) - score += 60 - elsif moveData.category != 2 && # Damaging move - moveData.target == :NearOther && - Effectiveness.ineffective?(user.effectiveness_of_type_against_battler(moveData.type, target)) - score += 60 + # We know which move is going to be encored (assuming the target doesn't + # use a high priority move) + move_data = GameData::Move.get(target.battler.lastRegularMoveUsed) + if move_data.status? + # Prefer encoring status moves + if [:User, :BothSides].include?(move_data.target) + # TODO: This target distinction was in the old code. Is it appropriate? + score += 10 + else + score += 8 + end + elsif move_data.damaging? && move_data.target == :NearOther + # Prefer encoring damaging moves depending on their type effectiveness + # against the user + eff = user.effectiveness_of_type_against_battler(move_data.type, target) + if Effectiveness.ineffective?(eff) + score += 15 + elsif Effectiveness.not_very_effective?(eff) + score += 10 + elsif Effectiveness.super_effective?(eff) + score -= 5 + else + score += 5 + end end + else + # We don't know which move is going to be encored; just prefer limiting + # the target's options + score += 8 end next score } ) #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("DisableTargetStatusMoves", proc { |move, user, target, ai, battle| @@ -526,12 +559,45 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("DisableTargetStatusMove Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetStatusMoves", proc { |score, move, user, target, ai, battle| next Battle::AI::MOVE_USELESS_SCORE if !target.check_for_move { |m| m.statusMove? } + # Not worth using on a sleeping target that won't imminently wake up + if target.status == :SLEEP && target.statusCount > ((target.faster_than?(user)) ? 2 : 1) + if !target.check_for_move { |m| m.statusMove? && m.usableWhenAsleep? && (m.pp > 0 || m.total_pp == 0) } + next Battle::AI::MOVE_USELESS_SCORE + end + end + # Move is likely useless if the target will lock themselves into a move, + # because they'll likely lock themselves into a damaging move anyway + if !target.effects[PBEffects::ChoiceBand] + if target.has_active_item?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF]) || + target.has_active_ability?(:GORILLATACTICS) + next Battle::AI::MOVE_USELESS_SCORE + end + end + # Prefer if the target has a protection move + protection_moves = [ + "ProtectUser", # Detect, Protect + "ProtectUserSideFromPriorityMoves", # Quick Guard + "ProtectUserSideFromMultiTargetDamagingMoves", # Wide Guard + "UserEnduresFaintingThisTurn", # Endure + "ProtectUserSideFromDamagingMovesIfUserFirstTurn", # Mat Block + "ProtectUserSideFromStatusMoves", # Crafty Shield + "ProtectUserFromDamagingMovesKingsShield", # King's Shield + "ProtectUserFromDamagingMovesObstruct", # Obstruct + "ProtectUserFromTargetingMovesSpikyShield", # Spiky Shield + "ProtectUserBanefulBunker" # Baneful Bunker + ] + if target.check_for_move { |m| m.statusMove? && protection_moves.include?(m.function) && + (m.pp > 0 || m.total_pp == 0) } + score += 6 + end + # Inherent preference + score += 8 next score } ) #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("DisableTargetHealingMoves", proc { |move, user, target, ai, battle| @@ -542,28 +608,57 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("DisableTargetHealingMov ) Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetHealingMoves", proc { |score, move, user, target, ai, battle| - next Battle::AI::MOVE_USELESS_SCORE if !target.check_for_move { |m| m.healingMove? } + # Useless if the foe can't heal themselves with a move or some held items + if !target.check_for_move { |m| m.healingMove? && (m.pp > 0 || m.total_pp == 0) } + if !target.has_active_item?(:LEFTOVERS) && + !(target.has_active_item?(:BLACKSLUDGE) && target.has_type?(:POISON)) + next Battle::AI::MOVE_USELESS_SCORE + end + end + # Inherent preference + score += 8 next score } ) #=============================================================================== -# TODO: Review score modifiers. +#. #=============================================================================== Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetSoundMoves", proc { |score, move, user, target, ai, battle| - if target.effects[PBEffects::ThroatChop] == 0 - score += 10 if target.check_for_move { |m| m.soundMove? } - end + next score if target.effects[PBEffects::ThroatChop] > 1 + next score if !target.check_for_move { |m| m.soundMove? && (m.pp > 0 || m.total_pp == 0) } + # Inherent preference + score += 8 next score } ) #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== Battle::AI::Handlers::MoveFailureCheck.add("DisableTargetMovesKnownByUser", proc { |move, user, ai, battle| next user.effects[PBEffects::Imprison] } ) +Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetMovesKnownByUser", + proc { |score, move, user, target, ai, battle| + # Useless if the foes have no moves that the user also knows + shared_move = false + user_moves = user.battler.moves.map { |m| m.id } + ai.each_foe_battler(user.side) do |b, i| + b.battler.eachMove do |m| + next if !user_moves.include?(m.id) + next if m.pp == 0 && m.total_pp > 0 + shared_move = true + break + end + break if shared_move + end + next Battle::AI::MOVE_USELESS_SCORE if !shared_move + # Inherent preference + score += 6 + next score + } +) diff --git a/Data/Scripts/011_Battle/005_AI/102_AIBattler.rb b/Data/Scripts/011_Battle/005_AI/102_AIBattler.rb index a48791aa8..229fd8a4b 100644 --- a/Data/Scripts/011_Battle/005_AI/102_AIBattler.rb +++ b/Data/Scripts/011_Battle/005_AI/102_AIBattler.rb @@ -267,6 +267,18 @@ class Battle::AI::AIBattler return ret end + def has_damaging_move_of_type?(*types) + check_for_move do |m| + return true if m.damagingMove? && types.include?(m.pbCalcType(@battler)) + end + return false + end + + def has_move_with_function?(*functions) + check_for_move { |m| return true if functions.include?(m.function) } + return false + end + #============================================================================= def can_switch_lax? diff --git a/Data/Scripts/011_Battle/005_AI/103_AIMove.rb b/Data/Scripts/011_Battle/005_AI/103_AIMove.rb index 1af8ad728..e91a7431a 100644 --- a/Data/Scripts/011_Battle/005_AI/103_AIMove.rb +++ b/Data/Scripts/011_Battle/005_AI/103_AIMove.rb @@ -8,9 +8,9 @@ class Battle::AI::AIMove @ai = ai end - def set_up(move, ai_battler) + def set_up(move) @move = move - @ai_battler = ai_battler + @move.calcType = rough_type end #============================================================================= @@ -46,7 +46,7 @@ class Battle::AI::AIMove # Returns whether this move targets multiple battlers. def targets_multiple_battlers? - user_battler = @ai_battler.battler + user_battler = @ai.user.battler target_data = @move.pbTarget(user_battler) return false if target_data.num_targets <= 1 num_targets = 0