mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-08 21:54:58 +00:00
Resolved all remaining TODO comments for AI (except testing), fixed effects of moves that can end the battle
This commit is contained in:
@@ -5,13 +5,10 @@ class Battle::AI
|
||||
attr_reader :battle
|
||||
attr_reader :trainer
|
||||
attr_reader :battlers
|
||||
attr_reader :roles
|
||||
attr_reader :user, :target, :move
|
||||
|
||||
def initialize(battle)
|
||||
@battle = battle
|
||||
@roles = [Array.new(@battle.pbParty(0).length) { |i| determine_roles(0, i) },
|
||||
Array.new(@battle.pbParty(1).length) { |i| determine_roles(1, i) }]
|
||||
end
|
||||
|
||||
def create_ai_objects
|
||||
|
||||
@@ -97,9 +97,7 @@ class Battle::AI
|
||||
reserves.sort! { |a, b| b[1] <=> a[1] } # Sort from highest to lowest rated
|
||||
# Don't bother choosing to switch if all replacements are poorly rated
|
||||
if @trainer.high_skill? && !mandatory
|
||||
# TODO: Should the current battler be rated as well, to provide a
|
||||
# threshold instead of using a threshold of 100?
|
||||
return -1 if reserves[0][1] < 100 # Best replacement rated at <100, don't switch
|
||||
return -1 if reserves[0][1] < 100 # If best replacement rated at <100, don't switch
|
||||
end
|
||||
# Return the party index of the best rated replacement Pokémon
|
||||
return reserves[0][0]
|
||||
@@ -136,9 +134,8 @@ class Battle::AI
|
||||
pkmn.moves.each do |m|
|
||||
next if m.power == 0 || (m.pp == 0 && m.total_pp > 0)
|
||||
@battle.battlers[idxBattler].allOpposing.each do |b|
|
||||
next if pokemon_can_absorb_move?(b.pokemon, m, m.type)
|
||||
bTypes = b.pbTypes(true)
|
||||
# TODO: Consider Wonder Guard, Volt Absorb et al. Consider pkmn's
|
||||
# ability if it changes the user's types or powers up their moves?
|
||||
score += m.power * Effectiveness.calculate(m.type, *bTypes) / 10
|
||||
end
|
||||
end
|
||||
@@ -175,7 +172,6 @@ end
|
||||
|
||||
#===============================================================================
|
||||
# Pokémon is about to faint because of Perish Song.
|
||||
# TODO: Also switch to remove other negative effects like Disable, Yawn.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::ShouldSwitch.add(:perish_song,
|
||||
proc { |battler, reserves, ai, battle|
|
||||
@@ -443,65 +439,60 @@ Battle::AI::Handlers::ShouldSwitch.add(:battler_is_useless,
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# Pokémon can't do anything to a Wonder Guard foe.
|
||||
# TODO: Check other abilities that provide immunities?
|
||||
# Pokémon can't do anything to any foe because its ability absorbs all damage
|
||||
# the Pokémon can deal out.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::ShouldSwitch.add(:foe_has_wonder_guard,
|
||||
Battle::AI::Handlers::ShouldSwitch.add(:foe_absorbs_all_moves_with_its_ability,
|
||||
proc { |battler, reserves, ai, battle|
|
||||
next false if battler.battler.turnCount < 2 # Don't switch out too quickly
|
||||
next false if battler.battler.hasMoldBreaker?
|
||||
non_wonder_guard_foe_exists = false
|
||||
has_super_effective_move = false
|
||||
foe_types = battler.pbTypes(true)
|
||||
next false if foe_types.length == 0
|
||||
# Check if battler can damage any of its foes
|
||||
can_damage_foe = false
|
||||
ai.each_foe_battler(battler.side) do |b, i|
|
||||
if !b.has_active_ability?(:WONDERGUARD)
|
||||
non_wonder_guard_foe_exists = true
|
||||
break
|
||||
end
|
||||
if ai.trainer.high_skill? && b.rough_end_of_round_damage > 0
|
||||
non_wonder_guard_foe_exists = true # Wonder Guard is being overcome already
|
||||
can_damage_foe = true # Foe is being damaged already
|
||||
break
|
||||
end
|
||||
# Check for super-effective damaging moves
|
||||
# Check for battler's moves that can damage the foe (b)
|
||||
battler.battler.eachMove do |move|
|
||||
next if move.statusMove?
|
||||
if ["IgnoreTargetAbility",
|
||||
"CategoryDependsOnHigherDamageIgnoreTargetAbility"].include?(move.function)
|
||||
has_super_effective_move = true
|
||||
can_damage_foe = true
|
||||
break
|
||||
end
|
||||
eff = Effectiveness.calculate(move.pbCalcType(battler.battler), *foe_types)
|
||||
if Effectiveness.super_effective?(eff)
|
||||
has_super_effective_move = true
|
||||
if !ai.pokemon_can_absorb_move?(b, move, move.pbCalcType(battler.battler))
|
||||
can_damage_foe = true
|
||||
break
|
||||
end
|
||||
end
|
||||
break if has_super_effective_move
|
||||
break if can_damage_foe
|
||||
end
|
||||
if !non_wonder_guard_foe_exists && !has_super_effective_move
|
||||
# Check reserves for super-effective moves; only switch if there are any
|
||||
reserve_has_super_effective_move = false
|
||||
reserves.each do |pkmn|
|
||||
next false if can_damage_foe
|
||||
# Check if a reserve could damage any foe; only switch if one could
|
||||
reserve_can_damage_foe = false
|
||||
reserves.each do |pkmn|
|
||||
ai.each_foe_battler(battler.side) do |b, i|
|
||||
# Check for reserve's moves that can damage the foe (b)
|
||||
pkmn.moves.each do |move|
|
||||
next if move.status_move?
|
||||
if ["IgnoreTargetAbility",
|
||||
"CategoryDependsOnHigherDamageIgnoreTargetAbility"].include?(move.function_code)
|
||||
reserve_has_super_effective_move = true
|
||||
reserve_can_damage_foe = true
|
||||
break
|
||||
end
|
||||
eff = Effectiveness.calculate(move.type, *foe_types)
|
||||
if Effectiveness.super_effective?(eff)
|
||||
reserve_has_super_effective_move = true
|
||||
if !ai.pokemon_can_absorb_move?(b, move, move.type)
|
||||
reserve_can_damage_foe = true
|
||||
break
|
||||
end
|
||||
end
|
||||
break if reserve_has_super_effective_move
|
||||
break if reserve_can_damage_foe
|
||||
end
|
||||
next false if !reserve_has_super_effective_move
|
||||
PBDebug.log_ai("#{battler.name} wants to switch because it can't do anything against Wonder Guard")
|
||||
next true
|
||||
break if reserve_can_damage_foe
|
||||
end
|
||||
next false
|
||||
next false if !reserve_can_damage_foe
|
||||
PBDebug.log_ai("#{battler.name} wants to switch because it can't damage the foe(s)")
|
||||
next true
|
||||
}
|
||||
)
|
||||
|
||||
@@ -576,7 +567,6 @@ Battle::AI::Handlers::ShouldSwitch.add(:sudden_death,
|
||||
#===============================================================================
|
||||
# Pokémon is within 5 levels of the foe, and foe's last move was super-effective
|
||||
# and powerful.
|
||||
# TODO: Review switch deciding.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::ShouldSwitch.add(:high_damage_from_foe,
|
||||
proc { |battler, reserves, ai, battle|
|
||||
@@ -653,7 +643,7 @@ Battle::AI::Handlers::ShouldNotSwitch.add(:battler_has_super_effective_move,
|
||||
# NOTE: Ideally this would ignore foes that move cannot target, but that
|
||||
# is complicated enough to implement that I'm not bothering. It's
|
||||
# also rare that it would matter.
|
||||
eff = b.effectiveness_of_type_against_battler(move_type, battler)
|
||||
eff = b.effectiveness_of_type_against_battler(move_type, battler, move)
|
||||
has_super_effective_move = Effectiveness.super_effective?(eff)
|
||||
break if has_super_effective_move
|
||||
end
|
||||
@@ -670,8 +660,6 @@ Battle::AI::Handlers::ShouldNotSwitch.add(:battler_has_super_effective_move,
|
||||
#===============================================================================
|
||||
# Don't bother switching if the battler has 4 or more positive stat stages.
|
||||
# Negative stat stages are ignored.
|
||||
# TODO: Ignore this if deciding whether to use Baton Pass (assuming move-scoring
|
||||
# uses this code).
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::ShouldNotSwitch.add(:battler_has_very_raised_stats,
|
||||
proc { |battler, reserves, ai, battle|
|
||||
|
||||
@@ -146,34 +146,31 @@ Battle::AI::Handlers::GeneralMoveScore.add(:any_battler_can_Snatch_move,
|
||||
|
||||
#===============================================================================
|
||||
# Pick a good move for the Choice items.
|
||||
# TODO: Review score modifier.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveScore.add(:good_move_for_choice_item,
|
||||
proc { |score, move, user, ai, battle|
|
||||
if ai.trainer.medium_skill?
|
||||
if user.has_active_item?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF]) ||
|
||||
user.has_active_ability?(:GORILLATACTICS)
|
||||
old_score = score
|
||||
# Really don't prefer status moves (except Trick)
|
||||
if move.statusMove? && move.function != "UserTargetSwapItems"
|
||||
score -= 25
|
||||
PBDebug.log_score_change(score - old_score, "don't want to be Choiced into a status move")
|
||||
next score
|
||||
end
|
||||
# Don't prefer moves which are 0x against at least one type
|
||||
move_type = move.rough_type
|
||||
GameData::Type.each do |type_data|
|
||||
score -= 8 if type_data.immunities.include?(move_type)
|
||||
end
|
||||
# Don't prefer moves with lower accuracy
|
||||
if move.accuracy > 0
|
||||
score -= (0.4 * (100 - move.accuracy)).to_i # -0 (100%) to -39 (1%)
|
||||
end
|
||||
# Don't prefer moves with low PP
|
||||
score -= 10 if move.move.pp <= 5
|
||||
PBDebug.log_score_change(score - old_score, "move is less suitable to be Choiced into")
|
||||
end
|
||||
next score if !ai.trainer.medium_skill?
|
||||
next score if !user.has_active_item?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF]) &&
|
||||
!user.has_active_ability?(:GORILLATACTICS)
|
||||
old_score = score
|
||||
# Really don't prefer status moves (except Trick)
|
||||
if move.statusMove? && move.function != "UserTargetSwapItems"
|
||||
score -= 25
|
||||
PBDebug.log_score_change(score - old_score, "don't want to be Choiced into a status move")
|
||||
next score
|
||||
end
|
||||
# Don't prefer moves which are 0x against at least one type
|
||||
move_type = move.rough_type
|
||||
GameData::Type.each do |type_data|
|
||||
score -= 8 if type_data.immunities.include?(move_type)
|
||||
end
|
||||
# Don't prefer moves with lower accuracy
|
||||
if move.accuracy > 0
|
||||
score -= (0.4 * (100 - move.accuracy)).to_i # -0 (100%) to -39 (1%)
|
||||
end
|
||||
# Don't prefer moves with low PP
|
||||
score -= 10 if move.move.pp <= 5
|
||||
PBDebug.log_score_change(score - old_score, "move is less suitable to be Choiced into")
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -205,22 +202,42 @@ Battle::AI::Handlers::GeneralMoveScore.add(:damaging_move_and_either_side_no_res
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
# Don't prefer Fire-type moves if target knows Powder and is faster than the
|
||||
# user.
|
||||
#===============================================================================
|
||||
# TODO: Don't prefer Fire-type moves if target has previously used Powder and is
|
||||
# faster than the user.
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:target_can_powder_fire_moves,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.high_skill? && move.rough_type == :FIRE &&
|
||||
target.has_move_with_function?("TargetNextFireMoveDamagesTarget") &&
|
||||
target.faster_than?(user)
|
||||
old_score = score
|
||||
score -= 5 # Only 5 because we're not sure target will use Powder
|
||||
PBDebug.log_score_change(score - old_score, "target knows Powder and could negate Fire moves")
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
# Don't prefer moves if target knows a move that can make them Electric-type,
|
||||
# and if target is unaffected by Electric moves.
|
||||
#===============================================================================
|
||||
# TODO: Don't prefer Normal-type moves if target has previously used Ion Deluge
|
||||
# and is immune to Electric moves.
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Don't prefer a move that is stopped by Wide Guard if any foe has
|
||||
# previously used Wide Guard.
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:target_can_make_moves_Electric_and_be_immune,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score if !ai.trainer.high_skill?
|
||||
next score if !target.has_move_with_function?("TargetMovesBecomeElectric") &&
|
||||
!(move.rough_type == :NORMAL && target.has_move_with_function?("NormalMovesBecomeElectric"))
|
||||
next score if !ai.pokemon_can_absorb_move?(target, move, :ELECTRIC) &&
|
||||
!Effectiveness.ineffective?(target.effectiveness_of_type_against_battler(:ELECTRIC, user))
|
||||
priority = move.rough_priority(user)
|
||||
if priority > 0 || (priority == 0 && target.faster_than?(user)) # Target goes first
|
||||
old_score = score
|
||||
score -= 5 # Only 5 because we're not sure target will use Electrify/Ion Deluge
|
||||
PBDebug.log_score_change(score - old_score, "target knows Electrify/Ion Deluge and is immune to Electric moves")
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# Don't prefer attacking the target if they'd be semi-invulnerable.
|
||||
@@ -293,6 +310,12 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:predicted_damage,
|
||||
old_score = score
|
||||
score += 10
|
||||
PBDebug.log_score_change(score - old_score, "predicted to KO the target")
|
||||
if move.move.multiHitMove? && target.hp == target.totalhp &&
|
||||
(target.has_active_ability?(:STURDY) || target.has_active_item?(:FOCUSSASH))
|
||||
old_score = score
|
||||
score += 8
|
||||
PBDebug.log_score_change(score - old_score, "predicted to overcome the target's Sturdy/Focus Sash")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -313,6 +336,7 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:external_flinching_effe
|
||||
if battle.moldBreaker || !target.has_active_ability?([:INNERFOCUS, :SHIELDDUST])
|
||||
old_score = score
|
||||
score += 8
|
||||
score += 5 if move.move.multiHitMove?
|
||||
PBDebug.log_score_change(score - old_score, "added chance to cause flinching")
|
||||
end
|
||||
end
|
||||
@@ -338,25 +362,67 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:thawing_move_against_fr
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
# Don't prefer a damaging move if it will trigger the target's ability or held
|
||||
# item when used, e.g. Effect Spore/Rough Skin, Pickpocket, Rocky Helmet, Red
|
||||
# Card.
|
||||
# NOTE: These abilities/items may not be triggerable after all (e.g. they
|
||||
# require the move to make contact but it doesn't), or may have a negative
|
||||
# effect for the target (e.g. Air Balloon popping), but it's too much
|
||||
# effort to go into detail deciding all this.
|
||||
#===============================================================================
|
||||
# TODO: Check all effects that trigger upon using a move, including per-hit
|
||||
# stuff in def pbEffectsOnMakingHit (worse if the move is a multi-hit one)
|
||||
# and end-of-move stuff in def pbEffectsAfterMove.
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:trigger_target_ability_or_item_upon_hit,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.high_skill? && move.damagingMove? && target.effects[PBEffects::Substitute] == 0
|
||||
if target.ability_active?
|
||||
if Battle::AbilityEffects::OnBeingHit[target.ability] ||
|
||||
(Battle::AbilityEffects::AfterMoveUseFromTarget[target.ability] &&
|
||||
(!user.has_active_ability?(:SHEERFORCE) || move.move.addlEffect == 0))
|
||||
old_score = score
|
||||
score += 8
|
||||
PBDebug.log_score_change(score - old_score, "can trigger the target's ability")
|
||||
end
|
||||
end
|
||||
if target.battler.isSpecies?(:CRAMORANT) && target.ability == :GULPMISSILE &&
|
||||
target.battler.form > 0 && !target.effects[PBEffects::Transform]
|
||||
old_score = score
|
||||
score += 8
|
||||
PBDebug.log_score_change(score - old_score, "can trigger the target's ability")
|
||||
end
|
||||
if target.item_active?
|
||||
if Battle::ItemEffects::OnBeingHit[target.item] ||
|
||||
(Battle::ItemEffects::AfterMoveUseFromTarget[target.item] &&
|
||||
(!user.has_active_ability?(:SHEERFORCE) || move.move.addlEffect == 0))
|
||||
old_score = score
|
||||
score += 8
|
||||
PBDebug.log_score_change(score - old_score, "can trigger the target's item")
|
||||
end
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
# Prefer a damaging move if it will trigger the user's ability when used, e.g.
|
||||
# Poison Touch, Magician.
|
||||
#===============================================================================
|
||||
# TODO: Prefer a contact move if making contact with the target could trigger
|
||||
# an effect that's good for the user (Poison Touch/Pickpocket).
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# TODO: Don't prefer contact move if making contact with the target could
|
||||
# trigger an effect that's bad for the user (Static, etc.).
|
||||
# => Also check if target has previously used Spiky Shield/King's Shield/
|
||||
# Baneful Bunker, and don't prefer move if so
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:trigger_user_ability_upon_hit,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.high_skill? && user.ability_active? && move.damagingMove? &&
|
||||
target.effects[PBEffects::Substitute] == 0
|
||||
# NOTE: The only ability with an OnDealingHit effect also requires the
|
||||
# move to make contact. The only abilities with an OnEndOfUsingMove
|
||||
# effect revolve around damaging moves.
|
||||
if (Battle::AbilityEffects::OnDealingHit[user.ability] && move.move.contactMove?) ||
|
||||
Battle::AbilityEffects::OnEndOfUsingMove[user.ability]
|
||||
old_score = score
|
||||
score += 8
|
||||
PBDebug.log_score_change(score - old_score, "can trigger the user's ability")
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# Don't prefer damaging moves that will knock out the target if they are using
|
||||
@@ -387,10 +453,23 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:knocking_out_a_destiny_
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
# Don't prefer damaging moves if the target is using Rage, unless the move will
|
||||
# deal enough damage to KO the target within two rounds.
|
||||
#===============================================================================
|
||||
# TODO: Don't prefer damaging moves if the target is using Rage and they benefit
|
||||
# from the raised Attack.
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:damaging_a_raging_target,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.medium_skill? && target.effects[PBEffects::Rage] && move.damagingMove?
|
||||
# Worth damaging the target if it can be knocked out within two rounds
|
||||
if ai.trainer.has_skill_flag?("HPAware")
|
||||
next score if (move.rough_damage + target.rough_end_of_round_damage) * 2 > target.hp * 1.1
|
||||
end
|
||||
old_score = score
|
||||
score -= 10
|
||||
PBDebug.log_score_change(score - old_score, "don't want to damage a Raging target")
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# Don't prefer damaging moves if the target is Biding, unless the move will deal
|
||||
|
||||
@@ -38,14 +38,16 @@ class Battle::AI
|
||||
|
||||
# Assumes that pkmn's ability is not negated by a global effect (e.g.
|
||||
# Neutralizing Gas).
|
||||
# pkmn is either a Battle::AI::AIBattler or a Pokemon. move is a Battle::Move.
|
||||
# pkmn is either a Battle::AI::AIBattler or a Pokemon.
|
||||
# move is a Battle::Move or a Pokemon::Move.
|
||||
def pokemon_can_absorb_move?(pkmn, move, move_type)
|
||||
return false if pkmn.is_a?(Battle::AI::AIBattler) && !pkmn.ability_active?
|
||||
# Check pkmn's ability
|
||||
# Anything with a Battle::AbilityEffects::MoveImmunity handler
|
||||
case pkmn.ability_id
|
||||
when :BULLETPROOF
|
||||
return move.bombMove?
|
||||
move_data = GameData::Move.get(move.id)
|
||||
return move_data.has_flag?("Bomb")
|
||||
when :FLASHFIRE
|
||||
return move_type == :FIRE
|
||||
when :LIGHTNINGROD, :MOTORDRIVE, :VOLTABSORB
|
||||
@@ -53,7 +55,8 @@ class Battle::AI
|
||||
when :SAPSIPPER
|
||||
return move_type == :GRASS
|
||||
when :SOUNDPROOF
|
||||
return move.soundMove?
|
||||
move_data = GameData::Move.get(move.id)
|
||||
return move_data.has_flag?("Sound")
|
||||
when :STORMDRAIN, :WATERABSORB, :DRYSKIN
|
||||
return move_type == :WATER
|
||||
when :TELEPATHY
|
||||
@@ -62,7 +65,7 @@ class Battle::AI
|
||||
when :WONDERGUARD
|
||||
types = pkmn.types
|
||||
types = pkmn.pbTypes(true) if pkmn.is_a?(Battle::AI::AIBattler)
|
||||
return Effectiveness.super_effective_type?(move_type, *types)
|
||||
return !Effectiveness.super_effective_type?(move_type, *types)
|
||||
end
|
||||
return false
|
||||
end
|
||||
@@ -157,24 +160,72 @@ class Battle::AI
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
# TODO: Add more items.
|
||||
BASE_ITEM_RATINGS = {
|
||||
4 => [:CHOICEBAND, :CHOICESCARF, :CHOICESPECS, :DEEPSEATOOTH, :LEFTOVERS,
|
||||
:LIGHTBALL, :THICKCLUB],
|
||||
3 => [:ADAMANTORB, :GRISEOUSORB, :LIFEORB, :LUSTROUSORB, :SOULDEW],
|
||||
2 => [:BLACKBELT, :BLACKGLASSES, :CHARCOAL, :DRAGONFANG, :HARDSTONE,
|
||||
10 => [:EVIOLITE, :FOCUSSASH, :LIFEORB, :THICKCLUB],
|
||||
9 => [:ASSAULTVEST, :BLACKSLUDGE, :CHOICEBAND, :CHOICESCARF, :CHOICESPECS,
|
||||
:DEEPSEATOOTH, :LEFTOVERS],
|
||||
8 => [:LEEK, :STICK, :THROATSPRAY, :WEAKNESSPOLICY],
|
||||
7 => [:EXPERTBELT, :LIGHTBALL, :LUMBERRY, :POWERHERB, :ROCKYHELMET,
|
||||
:SITRUSBERRY],
|
||||
6 => [:KINGSROCK, :LIECHIBERRY, :LIGHTCLAY, :PETAYABERRY, :RAZORFANG,
|
||||
:REDCARD, :SALACBERRY, :SHELLBELL, :WHITEHERB,
|
||||
# Type-resisting berries
|
||||
:BABIRIBERRY, :CHARTIBERRY, :CHILANBERRY, :CHOPLEBERRY, :COBABERRY,
|
||||
:COLBURBERRY, :HABANBERRY, :KASIBBERRY, :KEBIABERRY, :OCCABERRY,
|
||||
:PASSHOBERRY, :PAYAPABERRY, :RINDOBERRY, :ROSELIBERRY, :SHUCABERRY,
|
||||
:TANGABERRY, :WACANBERRY, :YACHEBERRY,
|
||||
# Gems
|
||||
:BUGGEM, :DARKGEM, :DRAGONGEM, :ELECTRICGEM, :FAIRYGEM, :FIGHTINGGEM,
|
||||
:FIREGEM, :FLYINGGEM, :GHOSTGEM, :GRASSGEM, :GROUNDGEM, :ICEGEM,
|
||||
:NORMALGEM, :POISONGEM, :PSYCHICGEM, :ROCKGEM, :STEELGEM, :WATERGEM,
|
||||
# Legendary Orbs
|
||||
:ADAMANTORB, :GRISEOUSORB, :LUSTROUSORB, :SOULDEW,
|
||||
# Berries that heal HP and may confuse
|
||||
:AGUAVBERRY, :FIGYBERRY, :IAPAPABERRY, :MAGOBERRY, :WIKIBERRY],
|
||||
5 => [:CUSTAPBERRY, :DEEPSEASCALE, :EJECTBUTTON, :FOCUSBAND, :JABOCABERRY,
|
||||
:KEEBERRY, :LANSATBERRY, :MARANGABERRY, :MENTALHERB, :METRONOME,
|
||||
:MUSCLEBAND, :QUICKCLAW, :RAZORCLAW, :ROWAPBERRY, :SCOPELENS,
|
||||
:WISEGLASSES,
|
||||
# Type power boosters
|
||||
:BLACKBELT, :BLACKGLASSES, :CHARCOAL, :DRAGONFANG, :HARDSTONE,
|
||||
:MAGNET, :METALCOAT, :MIRACLESEED, :MYSTICWATER, :NEVERMELTICE,
|
||||
:POISONBARB, :SHARPBEAK, :SILKSCARF, :SILVERPOWDER, :SOFTSAND,
|
||||
:POISONBARB, :SHARPBEAK, :SILKSCARF,:SILVERPOWDER, :SOFTSAND,
|
||||
:SPELLTAG, :TWISTEDSPOON,
|
||||
:ODDINCENSE, :ROCKINCENSE, :ROSEINCENSE, :SEAINCENSE, :WAVEINCENSE,
|
||||
# Plates
|
||||
:DRACOPLATE, :DREADPLATE, :EARTHPLATE, :FISTPLATE, :FLAMEPLATE,
|
||||
:ICICLEPLATE, :INSECTPLATE, :IRONPLATE, :MEADOWPLATE, :MINDPLATE,
|
||||
:PIXIEPLATE, :SKYPLATE, :SPLASHPLATE, :SPOOKYPLATE, :STONEPLATE,
|
||||
:TOXICPLATE, :ZAPPLATE,
|
||||
:ODDINCENSE, :ROCKINCENSE, :ROSEINCENSE, :SEAINCENSE, :WAVEINCENSE,
|
||||
:MUSCLEBAND, :WISEGLASSES],
|
||||
1 => [:METRONOME],
|
||||
-2 => [:LAGGINGTAIL, :STICKYBARB],
|
||||
-4 => [:BLACKSLUDGE, :FLAMEORB, :IRONBALL, :TOXICORB]
|
||||
# Weather/terrain extenders
|
||||
:DAMPROCK, :HEATROCK, :ICYROCK, :SMOOTHROCK, :TERRAINEXTENDER],
|
||||
4 => [:ADRENALINEORB, :APICOTBERRY, :BLUNDERPOLICY, :CHESTOBERRY,
|
||||
:EJECTPACK, :ENIGMABERRY, :GANLONBERRY, :HEAVYDUTYBOOTS,
|
||||
:ROOMSERVICE, :SAFETYGOGGLES, :SHEDSHELL, :STARFBERRY],
|
||||
3 => [:BIGROOT, :BRIGHTPOWDER, :LAXINCENSE, :LEPPABERRY, :PERSIMBERRY,
|
||||
:PROTECTIVEPADS, :UTILITYUMBRELLA,
|
||||
# Status problem-curing berries (except Chesto which is in 4)
|
||||
:ASPEARBERRY, :CHERIBERRY, :PECHABERRY, :RAWSTBERRY],
|
||||
2 => [:ABSORBBULB, :BERRYJUICE, :CELLBATTERY, :GRIPCLAW, :LUMINOUSMOSS,
|
||||
:MICLEBERRY, :ORANBERRY, :SNOWBALL, :WIDELENS, :ZOOMLENS,
|
||||
# Terrain seeds
|
||||
:ELECTRICSEED, :GRASSYSEED, :MISTYSEED, :PSYCHICSEED],
|
||||
1 => [:AIRBALLOON, :BINDINGBAND, :DESTINYKNOT, :FLOATSTONE, :LUCKYPUNCH,
|
||||
:METALPOWDER, :QUICKPOWDER,
|
||||
# Drives
|
||||
:BURNDRIVE, :CHILLDRIVE, :DOUSEDRIVE, :SHOCKDRIVE,
|
||||
# Memories
|
||||
:BUGMEMORY, :DARKMEMORY, :DRAGONMEMORY, :ELECTRICMEMORY,
|
||||
:FAIRYMEMORY, :FIGHTINGMEMORY, :FIREMEMORY, :FLYINGMEMORY,
|
||||
:GHOSTMEMORY, :GRASSMEMORY, :GROUNDMEMORY, :ICEMEMORY, :POISONMEMORY,
|
||||
:PSYCHICMEMORY, :ROCKMEMORY, :STEELMEMORY, :WATERMEMORY
|
||||
],
|
||||
0 => [:SMOKEBALL],
|
||||
-5 => [:FULLINCENSE, :LAGGINGTAIL, :RINGTARGET],
|
||||
-6 => [:MACHOBRACE, :POWERANKLET, :POWERBAND, :POWERBELT, :POWERBRACER,
|
||||
:POWERLENS, :POWERWEIGHT],
|
||||
-7 => [:FLAMEORB, :IRONBALL, :TOXICORB],
|
||||
-9 => [:STICKYBARB]
|
||||
}
|
||||
end
|
||||
|
||||
@@ -342,9 +393,71 @@ Battle::AI::Handlers::ItemRanking.add(:ADAMANTORB,
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:AGUAVBERRY,
|
||||
proc { |item, score, battler, ai|
|
||||
if Settings::MECHANICS_GENERATION == 7 # Heals 50%
|
||||
score += 2
|
||||
elsif Settings::MECHANICS_GENERATION <= 6 # Heals 12.5%
|
||||
score -= 3
|
||||
end
|
||||
if ai.trainer.high_skill?
|
||||
if battler.battler.nature.stat_changes.any? { |val| val[0] == :SPECIAL_DEFENSE && val[1] < 0 }
|
||||
score -= 2 # Will confuse
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:ASSAULTVEST,
|
||||
proc { |item, score, battler, ai|
|
||||
if ai.trainer.high_skill?
|
||||
score += 1 if !battler.check_for_move { |m| m.statusMove? && !m.is_a?(Battle::Move::UseMoveTargetIsAboutToUse) }
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:BERRYJUICE,
|
||||
proc { |item, score, battler, ai|
|
||||
next [10 - (battler.totalhp / 15), 1].max
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:BIGROOT,
|
||||
proc { |item, score, battler, ai|
|
||||
next score if battler.check_for_move do |m|
|
||||
m.is_a?(Battle::Move::HealUserByHalfOfDamageDone) ||
|
||||
m.is_a?(Battle::Move::HealUserByHalfOfDamageDoneIfTargetAsleep) ||
|
||||
m.is_a?(Battle::Move::HealUserByThreeQuartersOfDamageDone) ||
|
||||
m.is_a?(Battle::Move::HealUserByTargetAttackLowerTargetAttack1) ||
|
||||
m.is_a?(Battle::Move::StartLeechSeedTarget)
|
||||
end
|
||||
next 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:BINDINGBAND,
|
||||
proc { |item, score, battler, ai|
|
||||
next score if battler.check_for_move { |m| m.is_a?(Battle::Move::BindTarget) }
|
||||
next 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.copy(:BINDINGBAND, :GRIPCLAW)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:BLACKSLUDGE,
|
||||
proc { |item, score, battler, ai|
|
||||
next 4 if battler.has_type?(:POISON)
|
||||
next score if battler.has_type?(:POISON)
|
||||
next -9
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:CHESTOBERRY,
|
||||
proc { |item, score, battler, ai|
|
||||
if ai.trainer.high_skill?
|
||||
score += 1 if battler.has_move_with_function("HealUserFullyAndFallAsleep")
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -367,6 +480,20 @@ Battle::AI::Handlers::ItemRanking.add(:CHOICESPECS,
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.copy(:CHOICESPECS, :WISEGLASSES)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:DEEPSEASCALE,
|
||||
proc { |item, score, battler, ai|
|
||||
next score if battler.battler.isSpecies?(:CLAMPERL)
|
||||
next 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:DAMPROCK,
|
||||
proc { |item, score, battler, ai|
|
||||
next score if battler.check_for_move { |m| m.is_a?(Battle::Move::StartRainWeather) }
|
||||
next 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:DEEPSEATOOTH,
|
||||
proc { |item, score, battler, ai|
|
||||
next score if battler.battler.isSpecies?(:CLAMPERL) &&
|
||||
@@ -375,6 +502,62 @@ Battle::AI::Handlers::ItemRanking.add(:DEEPSEATOOTH,
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:ELECTRICSEED,
|
||||
proc { |item, score, battler, ai|
|
||||
next score if battler.check_for_move { |m| m.is_a?(Battle::Move::StartElectricTerrain) }
|
||||
next 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:EVIOLITE,
|
||||
proc { |item, score, battler, ai|
|
||||
next score if battler.battler.pokemon.species_data.get_evolutions(true).length > 0
|
||||
next 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:FIGYBERRY,
|
||||
proc { |item, score, battler, ai|
|
||||
if Settings::MECHANICS_GENERATION == 7 # Heals 50%
|
||||
score += 2
|
||||
elsif Settings::MECHANICS_GENERATION <= 6 # Heals 12.5%
|
||||
score -= 3
|
||||
end
|
||||
if ai.trainer.high_skill?
|
||||
if battler.battler.nature.stat_changes.any? { |val| val[0] == :ATTACK && val[1] < 0 }
|
||||
score -= 2 # Will confuse
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:FLAMEORB,
|
||||
proc { |item, score, battler, ai|
|
||||
next 0 if battler.status != :NONE
|
||||
next 7 if battler.wants_status_problem?(:BURN)
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:FULLINCENSE,
|
||||
proc { |item, score, battler, ai|
|
||||
if ai.trainer.high_skill?
|
||||
score = 7 if battler.has_active_ability?(:ANALYTIC)
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.copy(:FULLINCENSE, :LAGGINGTAIL)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:GRASSYSEED,
|
||||
proc { |item, score, battler, ai|
|
||||
next score if battler.check_for_move { |m| m.is_a?(Battle::Move::StartGrassyTerrain) }
|
||||
next 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:GRISEOUSORB,
|
||||
proc { |item, score, battler, ai|
|
||||
next score if battler.battler.isSpecies?(:GIRATINA) &&
|
||||
@@ -383,6 +566,36 @@ Battle::AI::Handlers::ItemRanking.add(:GRISEOUSORB,
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:HEATROCK,
|
||||
proc { |item, score, battler, ai|
|
||||
next score if battler.check_for_move { |m| m.is_a?(Battle::Move::StartSunWeather) }
|
||||
next 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:IAPAPABERRY,
|
||||
proc { |item, score, battler, ai|
|
||||
if Settings::MECHANICS_GENERATION == 7 # Heals 50%
|
||||
score += 2
|
||||
elsif Settings::MECHANICS_GENERATION <= 6 # Heals 12.5%
|
||||
score -= 3
|
||||
end
|
||||
if ai.trainer.high_skill?
|
||||
if battler.battler.nature.stat_changes.any? { |val| val[0] == :DEFENSE && val[1] < 0 }
|
||||
score -= 2 # Will confuse
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:ICYROCK,
|
||||
proc { |item, score, battler, ai|
|
||||
next score if battler.check_for_move { |m| m.is_a?(Battle::Move::StartHailWeather) }
|
||||
next 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:IRONBALL,
|
||||
proc { |item, score, battler, ai|
|
||||
next 0 if battler.has_move_with_function?("ThrowUserItemAtTarget")
|
||||
@@ -390,6 +603,27 @@ Battle::AI::Handlers::ItemRanking.add(:IRONBALL,
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:KINGSROCK,
|
||||
proc { |item, score, battler, ai|
|
||||
if ai.trainer.high_skill?
|
||||
score += 1 if battler.check_for_move { |m| m.multiHitMove? }
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.copy(:KINGSROCK, :RAZORFANG)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:LEEK,
|
||||
proc { |item, score, battler, ai|
|
||||
next score if (battler.battler.isSpecies?(:FARFETCHD) || battler.battler.isSpecies?(:SIRFETCHD)) &&
|
||||
battler.check_for_move { |m| m.damagingMove? }
|
||||
next 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.copy(:LEEK, :STICK)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:LIGHTBALL,
|
||||
proc { |item, score, battler, ai|
|
||||
next score if battler.battler.isSpecies?(:PIKACHU) &&
|
||||
@@ -398,6 +632,24 @@ Battle::AI::Handlers::ItemRanking.add(:LIGHTBALL,
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:LIGHTCLAY,
|
||||
proc { |item, score, battler, ai|
|
||||
next score if battler.check_for_move do |m|
|
||||
m.is_a?(Battle::Move::StartWeakenPhysicalDamageAgainstUserSide) ||
|
||||
m.is_a?(Battle::Move::StartWeakenSpecialDamageAgainstUserSide) ||
|
||||
m.is_a?(Battle::Move::StartWeakenDamageAgainstUserSideIfHail)
|
||||
end
|
||||
next 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:LUCKYPUNCH,
|
||||
proc { |item, score, battler, ai|
|
||||
next score if battler.battler.isSpecies?(:CHANSEY)
|
||||
next 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:LUSTROUSORB,
|
||||
proc { |item, score, battler, ai|
|
||||
next score if battler.battler.isSpecies?(:PALKIA) &&
|
||||
@@ -406,18 +658,106 @@ Battle::AI::Handlers::ItemRanking.add(:LUSTROUSORB,
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:MAGOBERRY,
|
||||
proc { |item, score, battler, ai|
|
||||
if Settings::MECHANICS_GENERATION == 7 # Heals 50%
|
||||
score += 2
|
||||
elsif Settings::MECHANICS_GENERATION <= 6 # Heals 12.5%
|
||||
score -= 3
|
||||
end
|
||||
if ai.trainer.high_skill?
|
||||
if battler.battler.nature.stat_changes.any? { |val| val[0] == :SPEED && val[1] < 0 }
|
||||
score -= 2 # Will confuse
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:METALPOWDER,
|
||||
proc { |item, score, battler, ai|
|
||||
next score if battler.battler.isSpecies?(:DITTO) && !battler.effects[PBEffects::Transform]
|
||||
next 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.copy(:METALPOWDER, :QUICKPOWDER)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:MISTYSEED,
|
||||
proc { |item, score, battler, ai|
|
||||
next score if battler.check_for_move { |m| m.is_a?(Battle::Move::StartMistyTerrain) }
|
||||
next 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:ORANBERRY,
|
||||
proc { |item, score, battler, ai|
|
||||
next [10 - (battler.totalhp / 8), 1].max
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:POWERHERB,
|
||||
proc { |item, score, battler, ai|
|
||||
next score if battler.check_for_move do |m|
|
||||
m.is_a?(Battle::Move::TwoTurnMove) &&
|
||||
!m.is_a?(Battle::Move::TwoTurnAttackInvulnerableInSkyTargetCannotAct)
|
||||
end
|
||||
next 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:PSYCHICSEED,
|
||||
proc { |item, score, battler, ai|
|
||||
next score if battler.check_for_move { |m| m.is_a?(Battle::Move::StartPsychicTerrain) }
|
||||
next 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:RINGTARGET,
|
||||
proc { |item, score, battler, ai|
|
||||
has_immunity = false
|
||||
battler.pbTypes(true).each do |type|
|
||||
has_immunity = GameData::Type.get(type).immunities.length > 0
|
||||
break if has_immunity
|
||||
end
|
||||
next score if has_immunity
|
||||
next 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:SMOOTHROCK,
|
||||
proc { |item, score, battler, ai|
|
||||
next score if battler.check_for_move { |m| m.is_a?(Battle::Move::StartSandstormWeather) }
|
||||
next 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:SOULDEW,
|
||||
proc { |item, score, battler, ai|
|
||||
next 0 if !battler.battler.isSpecies?(:LATIAS) && !battler.battler.isSpecies?(:LATIOS)
|
||||
if Settings::SOUL_DEW_POWERS_UP_TYPES
|
||||
next 0 if !battler.has_damaging_move_of_type?(:PSYCHIC, :DRAGON)
|
||||
elsif !battler.check_for_move { |m| m.specialMove?(m.type) }
|
||||
next 1 # Also boosts SpDef
|
||||
elsif battler.check_for_move { |m| m.specialMove?(m.type) }
|
||||
next 10
|
||||
else
|
||||
next 6 # Boosts SpDef
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:TERRAINEXTENDER,
|
||||
proc { |item, score, battler, ai|
|
||||
next score if battler.check_for_move do |m|
|
||||
m.is_a?(Battle::Move::StartElectricTerrain) ||
|
||||
m.is_a?(Battle::Move::StartGrassyTerrain) ||
|
||||
m.is_a?(Battle::Move::StartMistyTerrain) ||
|
||||
m.is_a?(Battle::Move::StartPsychicTerrain)
|
||||
end
|
||||
next 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:THICKCLUB,
|
||||
proc { |item, score, battler, ai|
|
||||
next score if (battler.battler.isSpecies?(:CUBONE) || battler.battler.isSpecies?(:MAROWAK)) &&
|
||||
@@ -426,6 +766,61 @@ Battle::AI::Handlers::ItemRanking.add(:THICKCLUB,
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:THROATSPRAY,
|
||||
proc { |item, score, battler, ai|
|
||||
next score if battler.check_for_move { |m| m.soundMove? }
|
||||
next 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:TOXICORB,
|
||||
proc { |item, score, battler, ai|
|
||||
next 0 if battler.status != :NONE
|
||||
next 7 if battler.wants_status_problem?(:POISON)
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:WHITEHERB,
|
||||
proc { |item, score, battler, ai|
|
||||
if ai.trainer.high_skill?
|
||||
score += 1 if battler.has_move_with_function("LowerUserDefSpDef1RaiseUserAtkSpAtkSpd2")
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:WIKIBERRY,
|
||||
proc { |item, score, battler, ai|
|
||||
if Settings::MECHANICS_GENERATION == 7 # Heals 50%
|
||||
score += 2
|
||||
elsif Settings::MECHANICS_GENERATION <= 6 # Heals 12.5%
|
||||
score -= 3
|
||||
end
|
||||
if ai.trainer.high_skill?
|
||||
if battler.battler.nature.stat_changes.any? { |val| val[0] == :SPECIAL_ATTACK && val[1] < 0 }
|
||||
score -= 2 # Will confuse
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.add(:ZOOMLENS,
|
||||
proc { |item, score, battler, ai|
|
||||
if ai.trainer.high_skill?
|
||||
score += 1 if battler.stages[:ACCURACY] < 0
|
||||
battler.battler.eachMove do |m|
|
||||
next if m.accuracy == 0 || m.is_a?(Battle::Move::OHKO)
|
||||
next if m.accuracy > 70
|
||||
score += 1
|
||||
break
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.addIf(:type_boosting_items,
|
||||
proc { |item|
|
||||
next [:BLACKBELT, :BLACKGLASSES, :CHARCOAL, :DRAGONFANG, :HARDSTONE,
|
||||
@@ -469,3 +864,36 @@ Battle::AI::Handlers::ItemRanking.addIf(:type_boosting_items,
|
||||
next 0
|
||||
}
|
||||
)
|
||||
|
||||
Battle::AI::Handlers::ItemRanking.addIf(:gems,
|
||||
proc { |item|
|
||||
next [:FIREGEM, :WATERGEM, :ELECTRICGEM, :GRASSGEM, :ICEGEM, :FIGHTINGGEM,
|
||||
:POISONGEM, :GROUNDGEM, :FLYINGGEM, :PSYCHICGEM, :BUGGEM, :ROCKGEM,
|
||||
:GHOSTGEM, :DRAGONGEM, :DARKGEM, :STEELGEM, :FAIRYGEM, :NORMALGEM].include?(item)
|
||||
},
|
||||
proc { |item, score, battler, ai|
|
||||
score += 2 if Settings::MECHANICS_GENERATION <= 5 # 1.5x boost rather than 1.3x
|
||||
boosted_type = {
|
||||
:BUGGEM => :BUG,
|
||||
:DARKGEM => :DARK,
|
||||
:DRAGONGEM => :DRAGON,
|
||||
:ELECTRICGEM => :ELECTRIC,
|
||||
:FAIRYGEM => :FAIRY,
|
||||
:FIGHTINGGEM => :FIGHTING,
|
||||
:FIREGEM => :FIRE,
|
||||
:FLYINGGEM => :FLYING,
|
||||
:GHOSTGEM => :GHOST,
|
||||
:GRASSGEM => :GRASS,
|
||||
:GROUNDGEM => :GROUND,
|
||||
:ICEGEM => :ICE,
|
||||
:NORMALGEM => :NORMAL,
|
||||
:POISONGEM => :POISON,
|
||||
:PSYCHICGEM => :PSYCHIC,
|
||||
:ROCKGEM => :ROCK,
|
||||
:STEELGEM => :STEEL,
|
||||
:WATERGEM => :WATER,
|
||||
}[item]
|
||||
next score if boosted_type && battler.has_damaging_move_of_type?(boosted_type)
|
||||
next 0
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
class Battle::AI
|
||||
# Determine the roles filled by a Pokémon on a given side at a given party
|
||||
# index.
|
||||
# Roles are:
|
||||
# :ace
|
||||
# :baton_passer
|
||||
# :cleric
|
||||
# :field_setter
|
||||
# :lead
|
||||
# :phazer
|
||||
# :physical_wall
|
||||
# :pivot
|
||||
# :screener
|
||||
# :second
|
||||
# :special_wall
|
||||
# :spinner
|
||||
# :stall_breaker
|
||||
# :status_absorber
|
||||
# :sweeper
|
||||
# :tank
|
||||
# :trapper
|
||||
# :weather_setter
|
||||
# NOTE: Reborn has the REVENGEKILLER role which compares mon's speed with
|
||||
# opponent (only when deciding whether to switch mon in) - this
|
||||
# comparison should be calculated when needed instead of being a role.
|
||||
def determine_roles(side, index)
|
||||
pkmn = @battle.pbParty(side)[index]
|
||||
ret = []
|
||||
return ret if !pkmn || pkmn.egg?
|
||||
# Check for moves indicative of particular roles
|
||||
hasHealMove = false
|
||||
pkmn.moves.each do |m|
|
||||
next if !m
|
||||
move = Battle::Move.from_pokemon_move(@battle, m)
|
||||
hasHealMove = true if !hasHealMove && move.healingMove?
|
||||
case move.function
|
||||
when "SleepTargetNextTurn", # Yawn
|
||||
"StartPerishCountsForAllBattlers", # Perish Song
|
||||
"SwitchOutTargetStatusMove", # Roar
|
||||
"SwitchOutTargetDamagingMove" # Circle Throw
|
||||
ret.push(:phazer)
|
||||
when "CureUserPartyStatus" # Aromatherapy/Heal Bell
|
||||
ret.push(:cleric)
|
||||
when "DisableTargetStatusMoves" # Taunt
|
||||
ret.push(:stall_breaker)
|
||||
when "HealUserPositionNextTurn" # Wish
|
||||
ret.push(:cleric) if pkmn.ev[:HP] == Pokemon::EV_STAT_LIMIT
|
||||
when "HealUserFullyAndFallAsleep" # Rest
|
||||
ret.push(:status_absorber)
|
||||
when "SwitchOutUserPassOnEffects" # Baton Pass
|
||||
ret.push(:baton_passer)
|
||||
when "SwitchOutUserStatusMove", "SwitchOutUserDamagingMove" # Teleport (Gen 8+), U-turn
|
||||
ret.push(:pivot) if hasHealMove
|
||||
when "RemoveUserBindingAndEntryHazards" # Rapid Spin
|
||||
ret.push(:spinner)
|
||||
when "StartElectricTerrain", "StartGrassyTerrain",
|
||||
"StartMistyTerrain", "StartPsychicTerrain" # Terrain moves
|
||||
ret.push(:field_setter)
|
||||
else
|
||||
ret.push(:weather_setter) if move.is_a?(Battle::Move::WeatherMove)
|
||||
end
|
||||
end
|
||||
# Check EVs, nature and moves for combinations indicative of particular roles
|
||||
if pkmn.ev[:SPEED] == Pokemon::EV_STAT_LIMIT
|
||||
if [:MODEST, :ADAMANT, # SpAtk+ Atk-, Atk+ SpAtk-
|
||||
:TIMID, :JOLLY].include?(pkmn.nature) # Spd+ Atk-, Spd+ SpAtk-
|
||||
ret.push(:sweeper)
|
||||
end
|
||||
end
|
||||
if hasHealMove
|
||||
if pkmn.nature.stat_changes.any? { |change| change[0] == :DEFENSE && change[1] > 0 } &&
|
||||
!pkmn.nature.stat_changes.any? { |change| change[0] == :DEFENSE && change[1] < 0 }
|
||||
ret.push(:physical_wall) if pkmn.ev[:DEFENSE] == Pokemon::EV_STAT_LIMIT
|
||||
elsif pkmn.nature.stat_changes.any? { |change| change[0] == :SPECIAL_DEFENSE && change[1] > 0 } &&
|
||||
!pkmn.nature.stat_changes.any? { |change| change[0] == :SPECIAL_DEFENSE && change[1] < 0 }
|
||||
ret.push(:special_wall) if pkmn.ev[:SPECIAL_DEFENSE] == Pokemon::EV_STAT_LIMIT
|
||||
end
|
||||
else
|
||||
ret.push(:tank) if pkmn.ev[:HP] == Pokemon::EV_STAT_LIMIT
|
||||
end
|
||||
# Check for abilities indicative of particular roles
|
||||
case pkmn.ability_id
|
||||
when :REGENERATOR
|
||||
ret.push(:pivot)
|
||||
when :GUTS, :QUICKFEET, :FLAREBOOST, :TOXICBOOST, :NATURALCURE, :MAGICGUARD,
|
||||
:MAGICBOUNCE, :HYDRATION
|
||||
ret.push(:status_absorber)
|
||||
when :SHADOWTAG, :ARENATRAP, :MAGNETPULL
|
||||
ret.push(:trapper)
|
||||
when :DROUGHT, :DRIZZLE, :SANDSTREAM, :SNOWWARNING, :PRIMORDIALSEA,
|
||||
:DESOLATELAND, :DELTASTREAM
|
||||
ret.push(:weather_setter)
|
||||
when :GRASSYSURGE, :ELECTRICSURGE, :MISTYSURGE, :PSYCHICSURGE
|
||||
ret.push(:field_setter)
|
||||
end
|
||||
# Check for items indicative of particular roles
|
||||
case pkmn.item_id
|
||||
when :LIGHTCLAY
|
||||
ret.push(:screener)
|
||||
when :ASSAULTVEST
|
||||
ret.push(:tank)
|
||||
when :CHOICEBAND, :CHOICESPECS
|
||||
ret.push(:stall_breaker)
|
||||
ret.push(:sweeper) if pkmn.ev[:SPEED] == Pokemon::EV_STAT_LIMIT
|
||||
when :CHOICESCARF
|
||||
ret.push(:sweeper) if pkmn.ev[:SPEED] == Pokemon::EV_STAT_LIMIT
|
||||
when :TOXICORB, :FLAMEORB
|
||||
ret.push(:status_absorber)
|
||||
when :TERRAINEXTENDER
|
||||
ret.push(:field_setter)
|
||||
end
|
||||
# Check for position in team, level relative to other levels in team
|
||||
partyStarts = @battle.pbPartyStarts(side)
|
||||
if partyStarts.include?(index + 1) || index == @battle.pbParty(side).length - 1
|
||||
ret.push(:ace) # Last in party, assumed to be the best Pokémon
|
||||
else
|
||||
ret.push(:lead) if partyStarts.include?(index) # First in party
|
||||
idxTrainer = @battle.pbGetOwnerIndexFromPartyIndex(side, index)
|
||||
maxLevel = @battle.pbMaxLevelInTeam(side, idxTrainer)
|
||||
if pkmn.level >= maxLevel
|
||||
ret.push(:second)
|
||||
else
|
||||
secondHighest = true
|
||||
seenHigherLevel = false
|
||||
@battle.eachInTeam(side, @battle.pbGetOwnerIndexFromPartyIndex(side, index)) do |p|
|
||||
next if p.level < pkmn.level
|
||||
if seenHigherLevel
|
||||
secondHighest = false
|
||||
break
|
||||
end
|
||||
seenHigherLevel = true
|
||||
end
|
||||
# NOTE: There can be multiple "second"s if all their levels are equal
|
||||
# and the highest in the team (and none are the ace).
|
||||
ret.push(:second) if secondHighest
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def check_role(side, idxBattler, *roles)
|
||||
role_array = @roles[side][idxBattler]
|
||||
roles.each do |r|
|
||||
return true if role_array.include?(r)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
def check_battler_role(battler, *roles)
|
||||
side = idxParty.idxOwnSide
|
||||
idxParty = idxParty.pokemonIndex
|
||||
return check_role(side, idxParty, *roles)
|
||||
end
|
||||
end
|
||||
@@ -16,10 +16,6 @@ class Battle::AI::AIBattler
|
||||
old_party_index = @party_index
|
||||
@battler = @ai.battle.battlers[@index]
|
||||
@party_index = battler.pokemonIndex
|
||||
if @party_index != old_party_index
|
||||
# TODO: Start of battle or Pokémon switched/shifted; recalculate roles,
|
||||
# etc. What is etc.?
|
||||
end
|
||||
end
|
||||
|
||||
def pokemon; return battler.pokemon; end
|
||||
@@ -232,12 +228,7 @@ class Battle::AI::AIBattler
|
||||
end
|
||||
alias pbHasType? has_type?
|
||||
|
||||
# TODO: Also make a def effectiveness_of_move_against_battler which calls
|
||||
# pbCalcTypeModSingle instead of effectiveness_of_type_against_single_battler_type,
|
||||
# for moves with custom def pbCalcTypeMod, e.g. Freeze-Dry. When would
|
||||
# that be used instead? Or rather, when would THIS method be used if
|
||||
# that existed?
|
||||
def effectiveness_of_type_against_battler(type, user = nil)
|
||||
def effectiveness_of_type_against_battler(type, user = nil, move = nil)
|
||||
ret = Effectiveness::NORMAL_EFFECTIVE_MULTIPLIER
|
||||
return ret if !type
|
||||
return ret if type == :GROUND && has_type?(:FLYING) && has_active_item?(:IRONBALL)
|
||||
@@ -250,7 +241,16 @@ class Battle::AI::AIBattler
|
||||
end
|
||||
else
|
||||
battler.pbTypes(true).each do |defend_type|
|
||||
ret *= effectiveness_of_type_against_single_battler_type(type, defend_type, user)
|
||||
mult = effectiveness_of_type_against_single_battler_type(type, defend_type, user)
|
||||
if move
|
||||
case move.function
|
||||
when "HitsTargetInSkyGroundsTarget"
|
||||
mult = Effectiveness::NORMAL_EFFECTIVE_MULTIPLIER if type == :GROUND && defend_type == :FLYING
|
||||
when "FreezeTargetSuperEffectiveAgainstWater"
|
||||
mult = Effectiveness::SUPER_EFFECTIVE_MULTIPLIER if defend_type == :WATER
|
||||
end
|
||||
end
|
||||
ret *= mult
|
||||
end
|
||||
ret *= 2 if self.effects[PBEffects::TarShot] && type == :FIRE
|
||||
end
|
||||
@@ -414,7 +414,9 @@ class Battle::AI::AIBattler
|
||||
break
|
||||
end
|
||||
# Modify the rating based on ability-specific contexts
|
||||
ret = Battle::AI::Handlers.modify_ability_ranking(ability, ret, self, @ai)
|
||||
if @ai.trainer.medium_skill?
|
||||
ret = Battle::AI::Handlers.modify_ability_ranking(ability, ret, self, @ai)
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
@@ -424,13 +426,12 @@ class Battle::AI::AIBattler
|
||||
# battler if it is holding it.
|
||||
# Return values are typically between -10 and +10. 0 is indifferent, positive
|
||||
# values mean this battler benefits, negative values mean this battler suffers.
|
||||
# TODO: This method shouldn't check for item effect negation, to work the same
|
||||
# as def wants_ability?.
|
||||
# NOTE: This method assumes the item isn't being negated. The calculations
|
||||
# that call this method separately check for it being negated, because
|
||||
# they need to do something special in that case.
|
||||
def wants_item?(item)
|
||||
item = :NONE if !item
|
||||
item = item.id if !item.is_a?(Symbol) && item.respond_to?("id")
|
||||
return 0 if has_active_ability?(:KLUTZ)
|
||||
# TODO: Unnerve, other item-negating effects.
|
||||
# Get the base item rating
|
||||
ret = 0
|
||||
Battle::AI::BASE_ITEM_RATINGS.each_pair do |val, items|
|
||||
@@ -439,7 +440,9 @@ class Battle::AI::AIBattler
|
||||
break
|
||||
end
|
||||
# Modify the rating based on item-specific contexts
|
||||
ret = Battle::AI::Handlers.modify_item_ranking(item, ret, self, @ai)
|
||||
if @ai.trainer.medium_skill?
|
||||
ret = Battle::AI::Handlers.modify_item_ranking(item, ret, self, @ai)
|
||||
end
|
||||
# Prefer if this battler knows Fling and it will do a lot of damage/have an
|
||||
# additional (negative) effect when flung
|
||||
if item != :NONE && has_move_with_function?("ThrowUserItemAtTarget")
|
||||
@@ -481,7 +484,18 @@ class Battle::AI::AIBattler
|
||||
end
|
||||
ret += (hp > totalhp * (1 - (1.0 / fraction_to_heal))) ? -6 : 6
|
||||
ret = ret * 3 / 2 if GameData::Item.get(item).is_berry? && has_active_ability?(:RIPEN)
|
||||
# TODO: Check whether the item will cause confusion?
|
||||
if @ai.trainer.high_skill?
|
||||
flavor_stat = {
|
||||
:AGUAVBERRY => :SPECIAL_DEFENSE,
|
||||
:FIGYBERRY => :ATTACK,
|
||||
:IAPAPABERRY => :DEFENSE,
|
||||
:MAGOBERRY => :SPEED,
|
||||
:WIKIBERRY => :SPECIAL_ATTACK
|
||||
}[item]
|
||||
if @battler.nature.stat_changes.any? { |val| val[0] == flavor_stat && val[1] < 0 }
|
||||
ret -= 3 if @battler.pbCanConfuseSelf?(false)
|
||||
end
|
||||
end
|
||||
when :ASPEARBERRY, :CHERIBERRY, :CHESTOBERRY, :PECHABERRY, :RAWSTBERRY
|
||||
# Status cure
|
||||
cured_status = {
|
||||
|
||||
@@ -101,7 +101,6 @@ class Battle::AI::AIMove
|
||||
# Get the move's type
|
||||
calc_type = rough_type
|
||||
# Decide whether the move has 50% chance of higher of being a critical hit
|
||||
# TODO: Make this a gradient/probability rather than all-or-nothing?
|
||||
crit_stage = rough_critical_hit_stage
|
||||
is_critical = crit_stage >= Battle::Move::CRITICAL_HIT_RATIOS.length ||
|
||||
Battle::Move::CRITICAL_HIT_RATIOS[crit_stage] <= 2
|
||||
@@ -325,7 +324,7 @@ class Battle::AI::AIMove
|
||||
end
|
||||
end
|
||||
# Type effectiveness
|
||||
typemod = target.effectiveness_of_type_against_battler(calc_type, user)
|
||||
typemod = target.effectiveness_of_type_against_battler(calc_type, user, @move)
|
||||
multipliers[:final_damage_multiplier] *= typemod
|
||||
# Burn
|
||||
if @ai.trainer.high_skill? && user.status == :BURN && physicalMove?(calc_type) &&
|
||||
|
||||
Reference in New Issue
Block a user