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 c916e1b73..3f2202bf8 100644 --- a/Data/Scripts/011_Battle/003_Move/013_MoveEffects_SwitchingActing.rb +++ b/Data/Scripts/011_Battle/003_Move/013_MoveEffects_SwitchingActing.rb @@ -59,7 +59,7 @@ end #=============================================================================== # After inflicting damage, user switches out. Ignores trapping moves. -# (U-turn, Volt Switch) +# (Flip Turn, U-turn, Volt Switch) #=============================================================================== class Battle::Move::SwitchOutUserDamagingMove < Battle::Move def pbEndOfMoveUsageEffect(user, targets, numHits, switchedBattlers) 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 3697b2e48..679504673 100644 --- a/Data/Scripts/011_Battle/005_AI/004_AI_ChooseMove.rb +++ b/Data/Scripts/011_Battle/005_AI/004_AI_ChooseMove.rb @@ -6,7 +6,7 @@ class Battle::AI # Returns a value between 0.0 and 1.0. All move scores are lowered by this # value multiplied by the highest-scoring move's score. def move_score_threshold - return 0.6 + 0.35 * (([@trainer.skill, 100].min / 100.0) ** 0.5) # 0.6 to 0.95 + return 0.6 + 0.3 * (([@trainer.skill, 100].min / 100.0) ** 0.5) # 0.6 to 0.9 end #============================================================================= @@ -147,8 +147,8 @@ 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) @battle.moldBreaker = @user.has_mold_breaker? + @move.set_up(move) end def set_up_move_check_target(target) @@ -158,6 +158,7 @@ class Battle::AI if @target.battler.lastRegularMoveUsed && GameData::Move.get(@target.battler.lastRegularMoveUsed).has_flag?("CanMirrorMove") mov = Battle::Move.from_pokemon_move(@battle, Pokemon::Move.new(@target.battler.lastRegularMoveUsed)) + @battle.moldBreaker = @user.has_mold_breaker? @move.set_up(mov) end end @@ -291,7 +292,7 @@ class Battle::AI # TODO: The above also applies if the move is Heal Pulse or a few other moves # like that, which CAN target a foe but you'd never do so. Maybe use a # move flag to determine such moves? The implication is that such moves - # wouldn't apply the "175 - score" bit, which would make their + # wouldn't apply the "185 - score" bit, which would make their # MoveHandlers do the opposite calculations to other moves with the same # targets, but is this desirable? #============================================================================= @@ -322,7 +323,7 @@ class Battle::AI end # TODO: Is this reversal of the score okay? old_score = score - score = 175 - score + score = 185 - score PBDebug.log_score_change(score - old_score, "score inverted (move targets ally but can target foe)") end return score diff --git a/Data/Scripts/011_Battle/005_AI/102_AIBattler.rb b/Data/Scripts/011_Battle/005_AI/102_AIBattler.rb index 78ef2b77b..79426b36e 100644 --- a/Data/Scripts/011_Battle/005_AI/102_AIBattler.rb +++ b/Data/Scripts/011_Battle/005_AI/102_AIBattler.rb @@ -299,6 +299,59 @@ class Battle::AI::AIBattler return false end + # NOTE: This specifically means "is not currently trapped but can become + # trapped by an effect". Similar to def pbCanSwitchOut? but this returns + # false if any certain switching OR certain trapping applies. + def can_become_trapped? + return false if fainted? + # Ability/item effects that allow switching no matter what + if ability_active? && Battle::AbilityEffects.triggerCertainSwitching(ability, @battler, @ai.battle) + return false + end + if item_active? && Battle::ItemEffects.triggerCertainSwitching(item, @battler, @ai.battle) + return false + end + # Other certain switching effects + return false if Settings::MORE_TYPE_EFFECTS && has_type?(:GHOST) + # Other certain trapping effects + return false if @battler.trappedInBattle? + # Trapping abilities/items + ai.each_foe_battler(side) do |b, i| + if b.ability_active? && + Battle::AbilityEffects.triggerTrappingByTarget(b.ability, @battler, b.battler, @ai.battle) + return false + end + if b.item_active? && + Battle::ItemEffects.triggerTrappingByTarget(b.item, @battler, b.battler, @ai.battle) + return false + end + end + return true + end + + #============================================================================= + + def wants_status_problem?(new_status) + return true if new_status == :NONE + if ability_active? + case ability_id + when :GUTS + return true if stat_raise_worthwhile?(self, :ATTACK, true) + when :MARVELSCALE + return true if stat_raise_worthwhile?(self, :DEFENSE, true) + when :QUICKFEET + return true if stat_raise_worthwhile?(self, :SPEED, true) + when :FLAREBOOST + return true if new_status == :BURN && stat_raise_worthwhile?(self, :SPECIAL_ATTACK, true) + end + end + return true if new_status == :SLEEP && check_for_move { |m| m.usableWhenAsleep? } + if has_move_with_function?("DoublePowerIfUserPoisonedBurnedParalyzed") + return true if [:POISON, :BURN, :PARALYSIS].include?(new_status) + end + return false + end + #============================================================================= # TODO: Add more items. diff --git a/Data/Scripts/011_Battle/005_AI/103_AIMove.rb b/Data/Scripts/011_Battle/005_AI/103_AIMove.rb index e4062af31..6e50d341b 100644 --- a/Data/Scripts/011_Battle/005_AI/103_AIMove.rb +++ b/Data/Scripts/011_Battle/005_AI/103_AIMove.rb @@ -11,6 +11,8 @@ class Battle::AI::AIMove def set_up(move) @move = move @move.calcType = rough_type + @ai.battle.moldBreaker ||= ["IgnoreTargetAbility", + "CategoryDependsOnHigherDamageIgnoreTargetAbility"].include?(function) end #============================================================================= diff --git a/Data/Scripts/011_Battle/005b_AI move function codes/052_AI_MoveHandlers_BattlerStats.rb b/Data/Scripts/011_Battle/005b_AI move function codes/052_AI_MoveHandlers_BattlerStats.rb index ff90635a8..8efbe5d2f 100644 --- a/Data/Scripts/011_Battle/005b_AI move function codes/052_AI_MoveHandlers_BattlerStats.rb +++ b/Data/Scripts/011_Battle/005b_AI move function codes/052_AI_MoveHandlers_BattlerStats.rb @@ -455,13 +455,7 @@ Battle::AI::Handlers::MoveEffectScore.copy("RaiseUserAtkSpAtk1", Battle::AI::Handlers::MoveFailureCheck.add("RaiseUserMainStats1LoseThirdOfTotalHP", proc { |move, user, ai, battle| next true if user.hp <= [user.totalhp / 3, 1].max - will_fail = true - (move.move.statUp.length / 2).times do |i| - next if !user.battler.pbCanRaiseStatStage?(move.move.statUp[i * 2], user.battler, move.move) - will_fail = false - break - end - next will_fail + next Battle::AI::Handlers.move_will_fail?("RaiseUserAtkDef1", move, user, ai, battle) } ) Battle::AI::Handlers::MoveEffectScore.add("RaiseUserMainStats1LoseThirdOfTotalHP", @@ -478,27 +472,35 @@ Battle::AI::Handlers::MoveEffectScore.add("RaiseUserMainStats1LoseThirdOfTotalHP ) #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== Battle::AI::Handlers::MoveFailureCheck.add("RaiseUserMainStats1TrapUserInBattle", proc { |move, user, ai, battle| next true if user.effects[PBEffects::NoRetreat] - will_fail = true - (move.move.statUp.length / 2).times do |i| - next if !user.battler.pbCanRaiseStatStage?(move.move.statUp[i * 2], user.battler, move.move) - will_fail = false - break - end - next will_fail + next Battle::AI::Handlers.move_will_fail?("RaiseUserAtkDef1", move, user, ai, battle) } ) Battle::AI::Handlers::MoveEffectScore.add("RaiseUserMainStats1TrapUserInBattle", proc { |score, move, user, ai, battle| # Score for stat increase score = ai.get_score_for_target_stat_raise(score, user, move.move.statUp) - # Score for trapping user in battle - if ai.trainer.medium_skill? && !user.battler.trappedInBattle? - score -= 10 if user.hp <= user.totalhp / 2 + # Score for user becoming trapped in battle + if user.can_become_trapped? && battle.pbCanChooseNonActive?(user.index) + # Not worth trapping if user will faint this round anyway + eor_damage = user.rough_end_of_round_damage + if eor_damage >= user.hp + next (move.damagingMove?) ? score : Battle::AI::MOVE_USELESS_SCORE + end + # Score for user becoming trapped in battle + # TODO: These checks are related to desire to switch, and there can be a lot + # more things to consider, e.g. effectiveness of the target's moves + # against its foes. Also applies to other code that calls + # can_become_trapped? + if user.effects[PBEffects::PerishSong] > 0 || + user.effects[PBEffects::Attract] >= 0 || + eor_damage > 0 + score -= 12 + end end next score } diff --git a/Data/Scripts/011_Battle/005b_AI move function codes/053_AI_MoveHandlers_BattlerOther.rb b/Data/Scripts/011_Battle/005b_AI move function codes/053_AI_MoveHandlers_BattlerOther.rb index 772a948f8..59dd520af 100644 --- a/Data/Scripts/011_Battle/005b_AI move function codes/053_AI_MoveHandlers_BattlerOther.rb +++ b/Data/Scripts/011_Battle/005b_AI move function codes/053_AI_MoveHandlers_BattlerOther.rb @@ -506,23 +506,16 @@ Battle::AI::Handlers::MoveEffectScore.add("CureUserPartyStatus", ) #=============================================================================== -# TODO: Review score modifiers. -# TODO: target should probably be treated as an enemy when deciding the score, -# since the score will be inverted elsewhere due to the target being an -# ally. +# #=============================================================================== Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("CureTargetBurn", proc { |score, move, user, target, ai, battle| add_effect = move.get_score_change_for_additional_effect(user, target) next score if add_effect == -999 # Additional effect will be negated if target.status == :BURN - if target.opposes?(user) - score -= add_effect - score -= 10 - else - score += add_effect - score += 10 - end + score -= add_effect + score -= 10 + score += 15 if target.wants_status_problem?(:BURN) end next score } @@ -1089,7 +1082,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("NegateTargetAbilityIfTar ) #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== # IgnoreTargetAbility diff --git a/Data/Scripts/011_Battle/005b_AI move function codes/054_AI_MoveHandlers_MoveAttributes.rb b/Data/Scripts/011_Battle/005b_AI move function codes/054_AI_MoveHandlers_MoveAttributes.rb index bad40ae56..1233c1518 100644 --- a/Data/Scripts/011_Battle/005b_AI move function codes/054_AI_MoveHandlers_MoveAttributes.rb +++ b/Data/Scripts/011_Battle/005b_AI move function codes/054_AI_MoveHandlers_MoveAttributes.rb @@ -267,13 +267,16 @@ Battle::AI::Handlers::MoveBasePower.copy("DoublePowerIfTargetHPLessThanHalf", "DoublePowerIfUserPoisonedBurnedParalyzed") #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== Battle::AI::Handlers::MoveBasePower.copy("DoublePowerIfTargetHPLessThanHalf", "DoublePowerIfTargetAsleepCureTarget") Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DoublePowerIfTargetAsleepCureTarget", proc { |score, move, user, target, ai, battle| - score -= 20 if target.status == :SLEEP && target.statusCount > 1 # Will cure status + if target.status == :SLEEP && target.statusCount > 1 # Will cure status + score -= 10 + score += 15 if target.wants_status_problem?(:SLEEP) + end next score } ) @@ -288,13 +291,16 @@ Battle::AI::Handlers::MoveBasePower.add("DoublePowerIfTargetPoisoned", ) #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== Battle::AI::Handlers::MoveBasePower.copy("DoublePowerIfTargetPoisoned", "DoublePowerIfTargetParalyzedCureTarget") Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DoublePowerIfTargetParalyzedCureTarget", proc { |score, move, user, target, ai, battle| - score -= 20 if target.status == :PARALYSIS # Will cure status + if target.status == :PARALYSIS # Will cure status + score -= 10 + score += 15 if target.wants_status_problem?(:PARALYSIS) + end next score } ) @@ -1284,7 +1290,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.copy("PoisonTarget", "CategoryDependsOnHigherDamagePoisonTarget") #=============================================================================== -# TODO: Review score modifiers. Category part is already accounted for. +# #=============================================================================== # CategoryDependsOnHigherDamageIgnoreTargetAbility diff --git a/Data/Scripts/011_Battle/005b_AI move function codes/056_AI_MoveHandlers_Healing.rb b/Data/Scripts/011_Battle/005b_AI move function codes/056_AI_MoveHandlers_Healing.rb index 76aca574f..86f4099bb 100644 --- a/Data/Scripts/011_Battle/005b_AI move function codes/056_AI_MoveHandlers_Healing.rb +++ b/Data/Scripts/011_Battle/005b_AI move function codes/056_AI_MoveHandlers_Healing.rb @@ -116,7 +116,7 @@ Battle::AI::Handlers::MoveEffectScore.add("HealUserHalfOfTotalHPLoseFlyingTypeTh ) #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("CureTargetStatusHealUserHalfOfTotalHP", proc { |move, user, target, ai, battle| @@ -125,13 +125,9 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("CureTargetStatusHealUse ) Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("CureTargetStatusHealUserHalfOfTotalHP", proc { |score, move, user, target, ai, battle| - # TODO: Add high level checks for whether the target wants to lose their - # status problem, and change the score accordingly. - if target.opposes?(user) - score -= 10 - else - score += 15 - end + # Will cure status + score -= 10 + score += 15 if target.wants_status_problem?(target.status) # Consider how much HP will be restored if user.hp >= user.totalhp * 0.5 score -= 10 @@ -422,7 +418,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("StartLeechSeedTarget", score += 15 end # Don't prefer if target can remove the seed - if target.check_for_move { |m| m.is_a?(Battle::Move::RemoveUserBindingAndEntryHazards) } + if target.has_move_with_function?("RemoveUserBindingAndEntryHazards") score -= 15 end end diff --git a/Data/Scripts/011_Battle/005b_AI move function codes/057_AI_MoveHandlers_Items.rb b/Data/Scripts/011_Battle/005b_AI move function codes/057_AI_MoveHandlers_Items.rb index 09fad1164..17882cf07 100644 --- a/Data/Scripts/011_Battle/005b_AI move function codes/057_AI_MoveHandlers_Items.rb +++ b/Data/Scripts/011_Battle/005b_AI move function codes/057_AI_MoveHandlers_Items.rb @@ -165,13 +165,40 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("StartTargetCannotUseItem ) #=============================================================================== -# TODO: Review score modifiers. -# TODO: This code shouldn't make use of target. +# #=============================================================================== Battle::AI::Handlers::MoveEffectScore.add("StartNegateHeldItems", proc { |score, move, user, ai, battle| - next Battle::AI::MOVE_USELESS_SCORE if battle.field.effects[PBEffects::MagicRoom] > 0 - score += 30 if !user.item # && target.item + next if battle.field.effects[PBEffects::MagicRoom] == 1 # About to expire anyway + any_held_items = false + total_want = 0 # Positive means foes want their items more than allies do + ai.each_battler do |b, i| + next if !b.item + # Skip b if its item is disabled + if ai.trainer.medium_skill? + if battle.field.effects[PBEffects::MagicRoom] > 0 + # NOTE: Same as b.item_active? but ignoring the Magic Room part. + next if b.effects[PBEffects::Embargo] > 0 + next if battle.corrosiveGas[b.index % 2][b.party_index] + next if b.has_active_ability?(:KLUTZ) + else + next if !b.item_active? + end + end + # Rate b's held item and add it to total_want + any_held_items = true + want = b.wants_item?(b.item_id) + total_want += (b.opposes?(user)) ? want : -want + end + # Alter score + next Battle::AI::MOVE_USELESS_SCORE if !any_held_items + if battle.field.effects[PBEffects::MagicRoom] > 0 + next Battle::AI::MOVE_USELESS_SCORE if total_want >= 0 + score -= [total_want, -5].max * 5 # Will enable items, prefer if allies affected more + else + next Battle::AI::MOVE_USELESS_SCORE if total_want <= 0 + score += [total_want, 5].min * 5 # Will disable items, prefer if foes affected more + end next score } ) diff --git a/Data/Scripts/011_Battle/005b_AI move function codes/059_AI_MoveHandlers_SwitchingActing.rb b/Data/Scripts/011_Battle/005b_AI move function codes/059_AI_MoveHandlers_SwitchingActing.rb index 63b74e369..24c940b5f 100644 --- a/Data/Scripts/011_Battle/005b_AI move function codes/059_AI_MoveHandlers_SwitchingActing.rb +++ b/Data/Scripts/011_Battle/005b_AI move function codes/059_AI_MoveHandlers_SwitchingActing.rb @@ -9,7 +9,7 @@ Battle::AI::Handlers::MoveFailureCheck.add("FleeFromBattle", Battle::AI::Handlers::MoveEffectScore.add("FleeFromBattle", proc { |score, move, user, ai, battle| # Generally don't prefer (don't want to end the battle too easily) - next score - 15 + next score - 20 } ) @@ -24,21 +24,19 @@ Battle::AI::Handlers::MoveFailureCheck.add("SwitchOutUserStatusMove", ) Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserStatusMove", proc { |score, move, user, ai, battle| - next score + 10 if user.wild? + # Wild Pokémon run from battle + next score - 20 if user.wild? + # Trainer-owned Pokémon switch out if ai.trainer.has_skill_flag?("ReserveLastPokemon") && battle.pbTeamAbleNonActiveCount(user.index) == 1 - score -= 60 # Don't switch in ace - else - score += 40 if user.effects[PBEffects::Confusion] > 0 - total = 0 - GameData::Stat.each_battle { |s| total += user.stages[s.id] } - if total <= 0 || user.turnCount == 0 - score += 60 - else - score -= total * 10 - # special case: user has no damaging moves - score += 75 if !user.check_for_move { |m| m.damagingMove? } - end + next Battle::AI::MOVE_USELESS_SCORE # Don't switch in ace end + # Prefer if the user switching out will lose a negative effect + score += 10 if user.effects[PBEffects::Confusion] > 1 + # Prefer if the user doesn't have any damaging moves + # TODO: Check effectiveness of moves. + score += 15 if !user.check_for_move { |m| m.damagingMove? } + # Don't prefer the more stat raises the user has + GameData::Stat.each_battle { |s| score -= user.stages[s.id] * 5 } next score } ) @@ -48,8 +46,14 @@ Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserStatusMove", #=============================================================================== Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserDamagingMove", proc { |score, move, user, ai, battle| - next 0 if !battle.pbCanChooseNonActive?(user.index) - next 0 if ai.trainer.has_skill_flag?("ReserveLastPokemon") && battle.pbTeamAbleNonActiveCount(user.index) == 1 # Don't switch in ace + next score if !battle.pbCanChooseNonActive?(user.index) + # Don't want to switch in ace + score -= 20 if ai.trainer.has_skill_flag?("ReserveLastPokemon") && + battle.pbTeamAbleNonActiveCount(user.index) == 1 + # Prefer if the user switching out will lose a negative effect + score += 10 if user.effects[PBEffects::Confusion] > 1 + # Don't prefer the more stat raises the user has + GameData::Stat.each_battle { |s| score -= user.stages[s.id] * 5 } next score } ) @@ -72,6 +76,18 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("LowerTargetAtkSpAtk1Swi Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("LowerTargetAtkSpAtk1SwitchOutUser", proc { |score, move, user, target, ai, battle| score = ai.get_score_for_target_stat_drop(score, target, move.move.statDown, false) + if battle.pbCanChooseNonActive?(user.index) + # Don't want to switch in ace + score -= 20 if ai.trainer.has_skill_flag?("ReserveLastPokemon") && + battle.pbTeamAbleNonActiveCount(user.index) == 1 + # Prefer if the user switching out will lose a negative effect + score += 10 if user.effects[PBEffects::Confusion] > 1 + # Prefer if the user doesn't have any damaging moves + # TODO: Check effectiveness of moves. + score += 15 if !user.check_for_move { |m| m.damagingMove? } + # Don't prefer the more stat raises the user has + GameData::Stat.each_battle { |s| score -= user.stages[s.id] * 5 } + end next score } ) @@ -86,20 +102,16 @@ Battle::AI::Handlers::MoveFailureCheck.add("SwitchOutUserPassOnEffects", ) Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserPassOnEffects", proc { |score, move, user, ai, battle| - if battle.pbCanChooseNonActive?(user.index) - score -= 40 if user.effects[PBEffects::Confusion] > 0 - total = 0 - GameData::Stat.each_battle { |s| total += user.stages[s.id] } - if total <= 0 || user.turnCount == 0 - score -= 60 - else - score += total * 10 - # special case: user has no damaging moves - score += 75 if !user.check_for_move { |m| m.damagingMove? } - end - else - score -= 100 - end + # Don't want to switch in ace + score -= 20 if ai.trainer.has_skill_flag?("ReserveLastPokemon") && + battle.pbTeamAbleNonActiveCount(user.index) == 1 + # Don't prefer if the user will pass on a negative effect + score -= 10 if user.effects[PBEffects::Confusion] > 1 + # Prefer if the user doesn't have any damaging moves + # TODO: Check effectiveness of moves. + score += 15 if !user.check_for_move { |m| m.damagingMove? } + # Prefer if the user will pass on good stat stages + GameData::Stat.each_battle { |s| score += user.stages[s.id] * 5 } next score } ) @@ -162,14 +174,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("BindTarget", # Target will take damage at the end of each round from the binding score += 8 if target.battler.takesIndirectDamage? # Check whether the target will be trapped in battle by the binding - untrappable = Settings::MORE_TYPE_EFFECTS && target.has_type?(:GHOST) - if !untrappable && target.ability_active? - untrappable = Battle::AbilityEffects.triggerCertainSwitching(target.ability, target.battler, battle) - end - if !untrappable && target.item_active? - untrappable = Battle::ItemEffects.triggerCertainSwitching(target.ability, target.battler, battle) - end - if !untrappable && !target.battler.trappedInBattle? + if target.can_become_trapped? score += 8 # Prefer if the target will become trapped by this move eor_damage = target.rough_end_of_round_damage if eor_damage > 0 @@ -185,8 +190,9 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("BindTarget", end # 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.has_move_with_function?("RemoveUserBindingAndEntryHazards") + if target.can_become_trapped? || target.battler.takesIndirectDamage? + if ai.trainer.medium_skill? && + target.has_move_with_function?("RemoveUserBindingAndEntryHazards") score -= 8 end end @@ -206,8 +212,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.copy("BindTarget", "BindTargetDoublePowerIfTargetUnderwater") #=============================================================================== -# TODO: Review score modifiers. -# TODO: Include get_score_change_for_additional_effect usage. +# #=============================================================================== Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("TrapTargetInBattle", proc { |move, user, target, ai, battle| @@ -217,15 +222,56 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("TrapTargetInBattle", next false } ) +Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TrapTargetInBattle", + proc { |score, move, user, target, ai, battle| + if !target.can_become_trapped? || !battle.pbCanChooseNonActive?(target.index) + next (move.damagingMove?) ? score : Battle::AI::MOVE_USELESS_SCORE + end + # Not worth trapping if target will faint this round anyway + eor_damage = target.rough_end_of_round_damage + if eor_damage >= target.hp + next (move.damagingMove?) ? score : Battle::AI::MOVE_USELESS_SCORE + end + # Score for EOR damage (intentionally before Rapid Spin check) + hp_fraction = (Settings::MECHANICS_GENERATION >= 6) ? 8 : 16 + if user.has_active_item?(:BINDINGBAND) + hp_fraction = (Settings::MECHANICS_GENERATION >= 6) ? 6 : 8 + end + rounds_to_deplete_hp = (hp_fraction.to_f * target.hp / target.totalhp).ceil + score += 30 / rounds_to_deplete_hp + # Not worth trapping if target can remove the binding + if ai.trainer.medium_skill? && + target.has_move_with_function?("RemoveUserBindingAndEntryHazards") + next (move.damagingMove?) ? score : Battle::AI::MOVE_USELESS_SCORE + end + # Score for being an additional effect + add_effect = move.get_score_change_for_additional_effect(user, target) + next score if add_effect == -999 # Additional effect will be negated + score += add_effect + # Score for target becoming trapped in battle + # TODO: These checks are related to desire to switch, and there can be a lot + # more things to consider, e.g. effectiveness of the target's moves + # against its foes. Also applies to other code that calls + # can_become_trapped? + if target.effects[PBEffects::PerishSong] > 0 || + target.effects[PBEffects::Attract] >= 0 || + eor_damage > 0 + score += 12 + end + next score + } +) #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== Battle::AI::Handlers::MoveFailureAgainstTargetCheck.copy("TrapTargetInBattle", "TrapTargetInBattleMainEffect") +Battle::AI::Handlers::MoveEffectAgainstTargetScore.copy("TrapTargetInBattle", + "TrapTargetInBattleMainEffect") #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("TrapTargetInBattleLowerTargetDefSpDef1EachTurn", proc { |move, user, target, ai, battle| @@ -237,20 +283,63 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("TrapTargetInBattleLower ) Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TrapTargetInBattleLowerTargetDefSpDef1EachTurn", proc { |score, move, user, target, ai, battle| - score += 30 if !target.battler.trappedInBattle? - score -= 50 if !target.battler.pbCanLowerStatStage?(:DEFENSE, user.battler, move.move) && - !target.battler.pbCanLowerStatStage?(:SPECIAL_DEFENSE, user.battler, move.move) + # Score for stat drop + score = ai.get_score_for_target_stat_drop(score, target, [:DEFENSE, 1, :SPECIAL_DEFENSE], false) + # Score for target becoming trapped in battle + if target.can_become_trapped? && battle.pbCanChooseNonActive?(target.index) + # Not worth trapping if target will faint this round anyway + eor_damage = target.rough_end_of_round_damage + if eor_damage >= target.hp + next (move.damagingMove?) ? score : Battle::AI::MOVE_USELESS_SCORE + end + # Not worth trapping if target can remove the binding + if target.has_move_with_function?("RemoveUserBindingAndEntryHazards") + next (move.damagingMove?) ? score : Battle::AI::MOVE_USELESS_SCORE + end + # Score for target becoming trapped in battle + # TODO: These checks are related to desire to switch, and there can be a lot + # more things to consider, e.g. effectiveness of the target's moves + # against its foes. Also applies to other code that calls + # can_become_trapped? + if target.effects[PBEffects::PerishSong] > 0 || + target.effects[PBEffects::Attract] >= 0 || + eor_damage > 0 + score += 12 + end + end next score } ) #=============================================================================== -# TODO: Review score modifiers. +# #=============================================================================== Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TrapUserAndTargetInBattle", proc { |score, move, user, target, ai, battle| - if target.effects[PBEffects::JawLock] < 0 - score += 40 if !user.battler.trappedInBattle? && !target.battler.trappedInBattle? + # NOTE: Don't worry about scoring for the user also becoming trapped in + # battle, because it knows this move and accepts what it's getting + # itself into. + if target.can_become_trapped? && battle.pbCanChooseNonActive?(target.index) + # Not worth trapping if target will faint this round anyway + eor_damage = target.rough_end_of_round_damage + if eor_damage >= target.hp + next (move.damagingMove?) ? score : Battle::AI::MOVE_USELESS_SCORE + end + # Not worth trapping if target can remove the binding + if ai.trainer.medium_skill? && + target.has_move_with_function?("RemoveUserBindingAndEntryHazards") + next (move.damagingMove?) ? score : Battle::AI::MOVE_USELESS_SCORE + end + # Score for target becoming trapped in battle + # TODO: These checks are related to desire to switch, and there can be a lot + # more things to consider, e.g. effectiveness of the target's moves + # against its foes. Also applies to other code that calls + # can_become_trapped? + if target.effects[PBEffects::PerishSong] > 0 || + target.effects[PBEffects::Attract] >= 0 || + eor_damage > 0 + score += 12 + end end next score } diff --git a/Data/Scripts/011_Battle/006_Other battle code/001_PBEffects.rb b/Data/Scripts/011_Battle/006_Other battle code/001_PBEffects.rb index af515bf32..855457c1d 100644 --- a/Data/Scripts/011_Battle/006_Other battle code/001_PBEffects.rb +++ b/Data/Scripts/011_Battle/006_Other battle code/001_PBEffects.rb @@ -28,92 +28,92 @@ module PBEffects Encore = 23 EncoreMove = 24 Endure = 25 - ExtraType = 111 - FirstPledge = 26 - FlashFire = 27 - Flinch = 28 - FocusEnergy = 29 - FocusPunch = 30 - FollowMe = 31 - Foresight = 32 - FuryCutter = 33 - GastroAcid = 34 - GemConsumed = 35 - Grudge = 36 - HealBlock = 37 - HelpingHand = 38 - HyperBeam = 39 - Illusion = 40 - Imprison = 41 - Ingrain = 42 - Instruct = 43 - Instructed = 44 - JawLock = 45 - KingsShield = 46 - LaserFocus = 47 - LeechSeed = 48 - LockOn = 49 - LockOnPos = 50 - MagicBounce = 51 - MagicCoat = 52 - MagnetRise = 53 - MeanLook = 54 - MeFirst = 55 - Metronome = 56 - MicleBerry = 57 - Minimize = 58 - MiracleEye = 59 - MirrorCoat = 60 - MirrorCoatTarget = 61 - MoveNext = 62 - MudSport = 63 - Nightmare = 64 - NoRetreat = 65 - Obstruct = 66 - Octolock = 67 - Outrage = 68 - ParentalBond = 69 - PerishSong = 70 - PerishSongUser = 71 - PickupItem = 72 - PickupUse = 73 - Pinch = 74 # Battle Palace only - Powder = 75 - PowerTrick = 76 - Prankster = 77 - PriorityAbility = 78 - PriorityItem = 79 - Protect = 80 - ProtectRate = 81 - Quash = 82 - Rage = 83 - RagePowder = 84 # Used along with FollowMe - Rollout = 85 - Roost = 86 - ShellTrap = 87 - SkyDrop = 88 - SlowStart = 89 - SmackDown = 90 - Snatch = 91 - SpikyShield = 92 - Spotlight = 93 - Stockpile = 94 - StockpileDef = 95 - StockpileSpDef = 96 - Substitute = 97 - TarShot = 98 - Taunt = 99 - Telekinesis = 100 - ThroatChop = 101 - Torment = 102 - Toxic = 103 - Transform = 104 - TransformSpecies = 105 - Trapping = 106 # Trapping move - TrappingMove = 107 - TrappingUser = 108 - Truant = 109 - TwoTurnAttack = 110 + ExtraType = 26 + FirstPledge = 27 + FlashFire = 28 + Flinch = 29 + FocusEnergy = 30 + FocusPunch = 31 + FollowMe = 32 + Foresight = 33 + FuryCutter = 34 + GastroAcid = 35 + GemConsumed = 36 + Grudge = 37 + HealBlock = 38 + HelpingHand = 39 + HyperBeam = 40 + Illusion = 41 + Imprison = 42 + Ingrain = 43 + Instruct = 44 + Instructed = 45 + JawLock = 46 + KingsShield = 47 + LaserFocus = 48 + LeechSeed = 49 + LockOn = 50 + LockOnPos = 51 + MagicBounce = 52 + MagicCoat = 53 + MagnetRise = 54 + MeanLook = 55 + MeFirst = 56 + Metronome = 57 + MicleBerry = 58 + Minimize = 59 + MiracleEye = 60 + MirrorCoat = 61 + MirrorCoatTarget = 62 + MoveNext = 63 + MudSport = 64 + Nightmare = 65 + NoRetreat = 66 + Obstruct = 67 + Octolock = 68 + Outrage = 69 + ParentalBond = 70 + PerishSong = 71 + PerishSongUser = 72 + PickupItem = 73 + PickupUse = 74 + Pinch = 75 # Battle Palace only + Powder = 76 + PowerTrick = 77 + Prankster = 78 + PriorityAbility = 79 + PriorityItem = 80 + Protect = 81 + ProtectRate = 82 + Quash = 83 + Rage = 84 + RagePowder = 85 # Used along with FollowMe + Rollout = 86 + Roost = 87 + ShellTrap = 88 + SkyDrop = 89 + SlowStart = 90 + SmackDown = 91 + Snatch = 92 + SpikyShield = 93 + Spotlight = 94 + Stockpile = 95 + StockpileDef = 96 + StockpileSpDef = 97 + Substitute = 98 + TarShot = 99 + Taunt = 100 + Telekinesis = 101 + ThroatChop = 102 + Torment = 103 + Toxic = 104 + Transform = 105 + TransformSpecies = 106 + Trapping = 107 # Trapping move that deals EOR damage + TrappingMove = 108 + TrappingUser = 109 + Truant = 110 + TwoTurnAttack = 111 Unburden = 112 Uproar = 113 WaterSport = 114 @@ -123,56 +123,56 @@ module PBEffects #============================================================================= # These effects apply to a battler position #============================================================================= - FutureSightCounter = 0 - FutureSightMove = 1 - FutureSightUserIndex = 2 - FutureSightUserPartyIndex = 3 - HealingWish = 4 - LunarDance = 5 - Wish = 6 - WishAmount = 7 - WishMaker = 8 + FutureSightCounter = 700 + FutureSightMove = 701 + FutureSightUserIndex = 702 + FutureSightUserPartyIndex = 703 + HealingWish = 704 + LunarDance = 705 + Wish = 706 + WishAmount = 707 + WishMaker = 708 #============================================================================= # These effects apply to a side #============================================================================= - AuroraVeil = 0 - CraftyShield = 1 - EchoedVoiceCounter = 2 - EchoedVoiceUsed = 3 - LastRoundFainted = 4 - LightScreen = 5 - LuckyChant = 6 - MatBlock = 7 - Mist = 8 - QuickGuard = 9 - Rainbow = 10 - Reflect = 11 - Round = 12 - Safeguard = 13 - SeaOfFire = 14 - Spikes = 15 - StealthRock = 16 - StickyWeb = 17 - Swamp = 18 - Tailwind = 19 - ToxicSpikes = 20 - WideGuard = 21 + AuroraVeil = 800 + CraftyShield = 801 + EchoedVoiceCounter = 802 + EchoedVoiceUsed = 803 + LastRoundFainted = 804 + LightScreen = 805 + LuckyChant = 806 + MatBlock = 807 + Mist = 808 + QuickGuard = 809 + Rainbow = 810 + Reflect = 811 + Round = 812 + Safeguard = 813 + SeaOfFire = 814 + Spikes = 815 + StealthRock = 816 + StickyWeb = 817 + Swamp = 818 + Tailwind = 819 + ToxicSpikes = 820 + WideGuard = 821 #============================================================================= # These effects apply to the battle (i.e. both sides) #============================================================================= - AmuletCoin = 0 - FairyLock = 1 - FusionBolt = 2 - FusionFlare = 3 - Gravity = 4 - HappyHour = 5 - IonDeluge = 6 - MagicRoom = 7 - MudSportField = 8 - PayDay = 9 - TrickRoom = 10 - WaterSportField = 11 - WonderRoom = 12 + AmuletCoin = 900 + FairyLock = 901 + FusionBolt = 902 + FusionFlare = 903 + Gravity = 904 + HappyHour = 905 + IonDeluge = 906 + MagicRoom = 907 + MudSportField = 908 + PayDay = 909 + TrickRoom = 910 + WaterSportField = 911 + WonderRoom = 912 end