mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2026-01-23 14:56:00 +00:00
Some rearranging of AI script files/methods
This commit is contained in:
@@ -11,8 +11,8 @@ class Battle::AI
|
||||
@battle = battle
|
||||
|
||||
# TODO: Move this elsewhere?
|
||||
@roles = [Array.new(@battle.pbParty(0).length) { |i| determine_roles(0, i) },
|
||||
Array.new(@battle.pbParty(1).length) { |i| determine_roles(1, i) }]
|
||||
@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
|
||||
|
||||
@@ -25,8 +25,8 @@ class Battle::AI
|
||||
moveData = GameData::Move.get(target.lastMoveUsed)
|
||||
moveType = moveData.type
|
||||
typeMod = @user.effectiveness_of_type_against_battler(moveType, foe)
|
||||
if Effectiveness.super_effective?(typeMod) && moveData.base_damage > 50
|
||||
switchChance = (moveData.base_damage > 70) ? 30 : 20
|
||||
if Effectiveness.super_effective?(typeMod) && moveData.power > 50
|
||||
switchChance = (moveData.power > 70) ? 30 : 20
|
||||
shouldSwitch = (pbAIRandom(100) < switchChance)
|
||||
end
|
||||
end
|
||||
@@ -169,7 +169,7 @@ class Battle::AI
|
||||
pkmn = party[i]
|
||||
sum = 0
|
||||
pkmn.moves.each do |m|
|
||||
next if m.base_damage == 0
|
||||
next if m.power == 0
|
||||
@battle.battlers[idxBattler].allOpposing.each do |b|
|
||||
bTypes = b.pbTypes(true)
|
||||
sum += Effectiveness.calculate(m.type, *bTypes)
|
||||
|
||||
@@ -536,7 +536,7 @@ class Battle::AI
|
||||
|
||||
when :ACCURACY
|
||||
# Prefer if user knows any weaker moves
|
||||
mini_score *= 1.1 if check_for_move(@user) { |m| m.damagingMove? && m.basedamage < 95 }
|
||||
mini_score *= 1.1 if check_for_move(@user) { |m| m.damagingMove? && m.power < 95 }
|
||||
# Prefer if target has a raised evasion
|
||||
sum_stages = @target.stages[:EVASION]
|
||||
mini_score *= 1 + sum_stages * 0.05 if sum_stages > 0
|
||||
@@ -996,465 +996,4 @@ class Battle::AI
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Returns a value indicating how beneficial the given item will be to the
|
||||
# given battler if it is holding it.
|
||||
# Return values are typically -2, -1, 0, 1 or 2. 0 is indifferent, positive
|
||||
# values mean the battler benefits, negative values mean the battler suffers.
|
||||
#=============================================================================
|
||||
def battler_wants_item?(battler, item = :NONE)
|
||||
item == :NONE if item.nil?
|
||||
# TODO: Add more items.
|
||||
preferred_items = [
|
||||
:CHOICESCARF,
|
||||
:LEFTOVERS
|
||||
]
|
||||
preferred_items.push(:BLACKSLUDGE) if battler.has_type?(:POISON)
|
||||
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 = [
|
||||
:BLACKSLUDGE,
|
||||
:FLAMEORB,
|
||||
:IRONBALL,
|
||||
:LAGGINGTAIL,
|
||||
:STICKYBARB,
|
||||
:TOXICORB
|
||||
]
|
||||
ret = 0
|
||||
if preferred_items.include?(item)
|
||||
ret = 2
|
||||
elsif unpreferred_items.include?(item)
|
||||
ret = -2
|
||||
end
|
||||
# Don't prefer if the battler knows Acrobatics
|
||||
if battler.has_move_with_function?("DoublePowerIfUserHasNoItem")
|
||||
ret += (item == :NONE) ? 1 : -1
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Items can be consumed by Stuff Cheeks, Teatime, Bug Bite/Pluck and Fling.
|
||||
#=============================================================================
|
||||
def get_score_change_for_consuming_item(battler, item)
|
||||
ret = 0
|
||||
case item
|
||||
when :ORANBERRY, :BERRYJUICE, :ENIGMABERRY, :SITRUSBERRY
|
||||
# Healing
|
||||
ret += (battler.hp > battler.totalhp * 3 / 4) ? -8 : 8
|
||||
ret = ret * 3 / 2 if GameData::Item.get(item).is_berry? && battler.has_active_ability?(:RIPEN)
|
||||
when :AGUAVBERRY, :FIGYBERRY, :IAPAPABERRY, :MAGOBERRY, :WIKIBERRY
|
||||
# Healing with confusion
|
||||
fraction_to_heal = 8 # Gens 6 and lower
|
||||
if Settings::MECHANICS_GENERATION == 7
|
||||
fraction_to_heal = 2
|
||||
elsif Settings::MECHANICS_GENERATION >= 8
|
||||
fraction_to_heal = 3
|
||||
end
|
||||
ret += (battler.hp > battler.totalhp * (1 - (1 / fraction_to_heal))) ? -8 : 8
|
||||
ret = ret * 3 / 2 if GameData::Item.get(item).is_berry? && battler.has_active_ability?(:RIPEN)
|
||||
# TODO: Check whether the item will cause confusion?
|
||||
when :ASPEARBERRY, :CHERIBERRY, :CHESTOBERRY, :PECHABERRY, :RAWSTBERRY
|
||||
# Status cure
|
||||
status = {
|
||||
:ASPEAR => :FROZEN,
|
||||
:CHERIBERRY => :PARALYSIS,
|
||||
:CHESTOBERRY => :SLEEP,
|
||||
:PECHABERRY => :POISON,
|
||||
:RAWSTBERRY => :BURN
|
||||
}[item]
|
||||
ret += (status && battler.status == status) ? 8 : -8
|
||||
when :PERSIMBERRY
|
||||
# Confusion cure
|
||||
ret += (battler.effects[PBEffects::Confusion] > 1) ? 8 : -8
|
||||
when :LUMBERRY
|
||||
# Any status/confusion cure
|
||||
ret += (battler.status != :NONE || battler.effects[PBEffects::Confusion] > 1) ? 8 : -8
|
||||
when :MENTALHERB
|
||||
# Cure mental effects
|
||||
ret += 8 if battler.effects[PBEffects::Attract] >= 0 ||
|
||||
battler.effects[PBEffects::Taunt] > 1 ||
|
||||
battler.effects[PBEffects::Encore] > 1 ||
|
||||
battler.effects[PBEffects::Torment] ||
|
||||
battler.effects[PBEffects::Disable] > 1 ||
|
||||
battler.effects[PBEffects::HealBlock] > 1
|
||||
when :APICOTBERRY, :GANLONBERRY, :LIECHIBERRY, :PETAYABERRY, :SALACBERRY,
|
||||
:KEEBERRY, :MARANGABERRY
|
||||
# Stat raise
|
||||
stat = {
|
||||
:APICOTBERRY => :SPECIAL_DEFENSE,
|
||||
:GANLONBERRY => :DEFENSE,
|
||||
:LIECHIBERRY => :ATTACK,
|
||||
:PETAYABERRY => :SPECIAL_ATTACK,
|
||||
:SALACBERRY => :SPEED,
|
||||
:KEEBERRY => :DEFENSE,
|
||||
:MARANGABERRY => :SPECIAL_DEFENSE
|
||||
}[item]
|
||||
ret += 8 if stat && stat_raise_worthwhile?(battler, stat)
|
||||
ret = ret * 3 / 2 if GameData::Item.get(item).is_berry? && battler.has_active_ability?(:RIPEN)
|
||||
when :STARFBERRY
|
||||
# Random stat raise
|
||||
ret += 8
|
||||
ret = ret * 3 / 2 if GameData::Item.get(item).is_berry? && battler.has_active_ability?(:RIPEN)
|
||||
when :WHITEHERB
|
||||
# Resets lowered stats
|
||||
reduced_stats = false
|
||||
GameData::Stat.each_battle do |s|
|
||||
next if battler.stages[s.id] >= 0
|
||||
reduced_stats = true
|
||||
break
|
||||
end
|
||||
ret += 8 if reduced_stats
|
||||
when :MICLEBERRY
|
||||
# Raises accuracy of next move
|
||||
ret += 8
|
||||
when :LANSATBERRY
|
||||
# Focus energy
|
||||
ret += 8 if battler.effects[PBEffects::FocusEnergy] < 2
|
||||
when :LEPPABERRY
|
||||
# Restore PP
|
||||
ret += 8
|
||||
ret = ret * 3 / 2 if GameData::Item.get(item).is_berry? && battler.has_active_ability?(:RIPEN)
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Returns a value indicating how beneficial the given ability will be to the
|
||||
# given battler if it has it.
|
||||
# Return values are typically between -10 and +10. 0 is indifferent, positive
|
||||
# values mean the battler benefits, negative values mean the battler suffers.
|
||||
#=============================================================================
|
||||
# These values are taken from the Complete-Fire-Red-Upgrade decomp here:
|
||||
# https://github.com/Skeli789/Complete-Fire-Red-Upgrade/blob/f7f35becbd111c7e936b126f6328fc52d9af68c8/src/ability_battle_effects.c#L41
|
||||
BASE_ABILITY_RATINGS = {
|
||||
:ADAPTABILITY => 8,
|
||||
:AERILATE => 8,
|
||||
:AFTERMATH => 5,
|
||||
:AIRLOCK => 5,
|
||||
:ANALYTIC => 5,
|
||||
:ANGERPOINT => 4,
|
||||
:ANTICIPATION => 0,
|
||||
:ARENATRAP => 9,
|
||||
:AROMAVEIL => 3,
|
||||
# :ASONECHILLINGNEIGH => 0,
|
||||
# :ASONEGRIMNEIGH => 0,
|
||||
:AURABREAK => 3,
|
||||
:BADDREAMS => 4,
|
||||
# :BALLFETCH => 0,
|
||||
# :BATTERY => 0,
|
||||
:BATTLEARMOR => 2,
|
||||
:BATTLEBOND => 6,
|
||||
:BEASTBOOST => 7,
|
||||
:BERSERK => 5,
|
||||
:BIGPECKS => 1,
|
||||
:BLAZE => 5,
|
||||
:BULLETPROOF => 7,
|
||||
:CHEEKPOUCH => 4,
|
||||
# :CHILLINGNEIGH => 0,
|
||||
:CHLOROPHYLL => 6,
|
||||
:CLEARBODY => 4,
|
||||
:CLOUDNINE => 5,
|
||||
:COLORCHANGE => 2,
|
||||
:COMATOSE => 6,
|
||||
:COMPETITIVE => 5,
|
||||
:COMPOUNDEYES => 7,
|
||||
:CONTRARY => 8,
|
||||
:CORROSION => 5,
|
||||
:COTTONDOWN => 3,
|
||||
# :CURIOUSMEDICINE => 0,
|
||||
:CURSEDBODY => 4,
|
||||
:CUTECHARM => 2,
|
||||
:DAMP => 2,
|
||||
:DANCER => 5,
|
||||
:DARKAURA => 6,
|
||||
:DAUNTLESSSHIELD => 3,
|
||||
:DAZZLING => 5,
|
||||
:DEFEATIST => -1,
|
||||
:DEFIANT => 5,
|
||||
:DELTASTREAM => 10,
|
||||
:DESOLATELAND => 10,
|
||||
:DISGUISE => 8,
|
||||
:DOWNLOAD => 7,
|
||||
:DRAGONSMAW => 8,
|
||||
:DRIZZLE => 9,
|
||||
:DROUGHT => 9,
|
||||
:DRYSKIN => 6,
|
||||
:EARLYBIRD => 4,
|
||||
:EFFECTSPORE => 4,
|
||||
:ELECTRICSURGE => 8,
|
||||
:EMERGENCYEXIT => 3,
|
||||
:FAIRYAURA => 6,
|
||||
:FILTER => 6,
|
||||
:FLAMEBODY => 4,
|
||||
:FLAREBOOST => 5,
|
||||
:FLASHFIRE => 6,
|
||||
:FLOWERGIFT => 4,
|
||||
# :FLOWERVEIL => 0,
|
||||
:FLUFFY => 5,
|
||||
:FORECAST => 6,
|
||||
:FOREWARN => 0,
|
||||
# :FRIENDGUARD => 0,
|
||||
:FRISK => 0,
|
||||
:FULLMETALBODY => 4,
|
||||
:FURCOAT => 7,
|
||||
:GALEWINGS => 6,
|
||||
:GALVANIZE => 8,
|
||||
:GLUTTONY => 3,
|
||||
:GOOEY => 5,
|
||||
:GORILLATACTICS => 4,
|
||||
:GRASSPELT => 2,
|
||||
:GRASSYSURGE => 8,
|
||||
# :GRIMNEIGH => 0,
|
||||
:GULPMISSLE => 3,
|
||||
:GUTS => 6,
|
||||
:HARVEST => 5,
|
||||
# :HEALER => 0,
|
||||
:HEATPROOF => 5,
|
||||
:HEAVYMETAL => -1,
|
||||
# :HONEYGATHER => 0,
|
||||
:HUGEPOWER => 10,
|
||||
:HUNGERSWITCH => 2,
|
||||
:HUSTLE => 7,
|
||||
:HYDRATION => 4,
|
||||
:HYPERCUTTER => 3,
|
||||
:ICEBODY => 3,
|
||||
:ICEFACE => 4,
|
||||
:ICESCALES => 7,
|
||||
# :ILLUMINATE => 0,
|
||||
:ILLUSION => 8,
|
||||
:IMMUNITY => 4,
|
||||
:IMPOSTER => 9,
|
||||
:INFILTRATOR => 6,
|
||||
:INNARDSOUT => 5,
|
||||
:INNERFOCUS => 2,
|
||||
:INSOMNIA => 4,
|
||||
:INTIMIDATE => 7,
|
||||
:INTREPIDSWORD => 3,
|
||||
:IRONBARBS => 6,
|
||||
:IRONFIST => 6,
|
||||
:JUSTIFIED => 4,
|
||||
:KEENEYE => 1,
|
||||
:KLUTZ => -1,
|
||||
:LEAFGUARD => 2,
|
||||
:LEVITATE => 7,
|
||||
:LIBERO => 8,
|
||||
:LIGHTMETAL => 2,
|
||||
:LIGHTNINGROD => 7,
|
||||
:LIMBER => 3,
|
||||
:LIQUIDOOZE => 3,
|
||||
:LIQUIDVOICE => 5,
|
||||
:LONGREACH => 3,
|
||||
:MAGICBOUNCE => 9,
|
||||
:MAGICGUARD => 9,
|
||||
:MAGICIAN => 3,
|
||||
:MAGMAARMOR => 1,
|
||||
:MAGNETPULL => 9,
|
||||
:MARVELSCALE => 5,
|
||||
:MEGALAUNCHER => 7,
|
||||
:MERCILESS => 4,
|
||||
:MIMICRY => 2,
|
||||
# :MINUS => 0,
|
||||
:MIRRORARMOR => 6,
|
||||
:MISTYSURGE => 8,
|
||||
:MOLDBREAKER => 7,
|
||||
:MOODY => 10,
|
||||
:MOTORDRIVE => 6,
|
||||
:MOXIE => 7,
|
||||
:MULTISCALE => 8,
|
||||
:MULTITYPE => 8,
|
||||
:MUMMY => 5,
|
||||
:NATURALCURE => 7,
|
||||
:NEUROFORCE => 6,
|
||||
:NEUTRALIZINGGAS => 5,
|
||||
:NOGUARD => 8,
|
||||
:NORMALIZE => -1,
|
||||
:OBLIVIOUS => 2,
|
||||
:OVERCOAT => 5,
|
||||
:OVERGROW => 5,
|
||||
:OWNTEMPO => 3,
|
||||
:PARENTALBOND => 10,
|
||||
:PASTELVEIL => 4,
|
||||
:PERISHBODY => -1,
|
||||
:PICKPOCKET => 3,
|
||||
:PICKUP => 1,
|
||||
:PIXILATE => 8,
|
||||
# :PLUS => 0,
|
||||
:POISONHEAL => 8,
|
||||
:POISONPOINT => 4,
|
||||
:POISONTOUCH => 4,
|
||||
:POWERCONSTRUCT => 10,
|
||||
# :POWEROFALCHEMY => 0,
|
||||
:POWERSPOT => 2,
|
||||
:PRANKSTER => 8,
|
||||
:PRESSURE => 5,
|
||||
:PRIMORDIALSEA => 10,
|
||||
:PRISMARMOR => 6,
|
||||
:PROPELLORTAIL => 2,
|
||||
:PROTEAN => 8,
|
||||
:PSYCHICSURGE => 8,
|
||||
:PUNKROCK => 2,
|
||||
:PUREPOWER => 10,
|
||||
:QUEENLYMAJESTY => 6,
|
||||
# :QUICKDRAW => 0,
|
||||
:QUICKFEET => 5,
|
||||
:RAINDISH => 3,
|
||||
:RATTLED => 3,
|
||||
# :RECEIVER => 0,
|
||||
:RECKLESS => 6,
|
||||
:REFRIGERATE => 8,
|
||||
:REGENERATOR => 8,
|
||||
:RIPEN => 4,
|
||||
:RIVALRY => 1,
|
||||
:RKSSYSTEM => 8,
|
||||
:ROCKHEAD => 5,
|
||||
:ROUGHSKIN => 6,
|
||||
# :RUNAWAY => 0,
|
||||
:SANDFORCE => 4,
|
||||
:SANDRUSH => 6,
|
||||
:SANDSPIT => 5,
|
||||
:SANDSTREAM => 9,
|
||||
:SANDVEIL => 3,
|
||||
:SAPSIPPER => 7,
|
||||
:SCHOOLING => 6,
|
||||
:SCRAPPY => 6,
|
||||
:SCREENCLEANER => 3,
|
||||
:SERENEGRACE => 8,
|
||||
:SHADOWSHIELD => 8,
|
||||
:SHADOWTAG => 10,
|
||||
:SHEDSKIN => 7,
|
||||
:SHEERFORCE => 8,
|
||||
:SHELLARMOR => 2,
|
||||
:SHIELDDUST => 5,
|
||||
:SHIELDSDOWN => 6,
|
||||
:SIMPLE => 8,
|
||||
:SKILLLINK => 7,
|
||||
:SLOWSTART => -2,
|
||||
:SLUSHRUSH => 5,
|
||||
:SNIPER => 3,
|
||||
:SNOWCLOAK => 3,
|
||||
:SNOWWARNING => 8,
|
||||
:SOLARPOWER => 3,
|
||||
:SOLIDROCK => 6,
|
||||
:SOULHEART => 7,
|
||||
:SOUNDPROOF => 4,
|
||||
:SPEEDBOOST => 9,
|
||||
:STAKEOUT => 6,
|
||||
:STALL => -1,
|
||||
:STALWART => 2,
|
||||
:STAMINA => 6,
|
||||
:STANCECHANGE => 10,
|
||||
:STATIC => 4,
|
||||
:STEADFAST => 2,
|
||||
:STEAMENGINE => 3,
|
||||
:STEELWORKER => 6,
|
||||
:STEELYSPIRIT => 2,
|
||||
:STENCH => 1,
|
||||
:STICKYHOLD => 3,
|
||||
:STORMDRAIN => 7,
|
||||
:STRONGJAW => 6,
|
||||
:STURDY => 6,
|
||||
:SUCTIONCUPS => 2,
|
||||
:SUPERLUCK => 3,
|
||||
:SURGESURFER => 4,
|
||||
:SWARM => 5,
|
||||
:SWEETVEIL => 4,
|
||||
:SWIFTSWIM => 6,
|
||||
# :SYMBIOSIS => 0,
|
||||
:SYNCHRONIZE => 4,
|
||||
:TANGLEDFEET => 2,
|
||||
:TANGLINGHAIR => 5,
|
||||
:TECHNICIAN => 8,
|
||||
# :TELEPATHY => 0,
|
||||
:TERAVOLT => 7,
|
||||
:THICKFAT => 7,
|
||||
:TINTEDLENS => 7,
|
||||
:TORRENT => 5,
|
||||
:TOUGHCLAWS => 7,
|
||||
:TOXICBOOST => 6,
|
||||
:TRACE => 6,
|
||||
:TRANSISTOR => 8,
|
||||
:TRIAGE => 7,
|
||||
:TRUANT => -2,
|
||||
:TURBOBLAZE => 7,
|
||||
:UNAWARE => 6,
|
||||
:UNBURDEN => 7,
|
||||
:UNNERVE => 3,
|
||||
# :UNSEENFIST => 0,
|
||||
:VICTORYSTAR => 6,
|
||||
:VITALSPIRIT => 4,
|
||||
:VOLTABSORB => 7,
|
||||
:WANDERINGSPIRIT => 2,
|
||||
:WATERABSORB => 7,
|
||||
:WATERBUBBLE => 8,
|
||||
:WATERCOMPACTION => 4,
|
||||
:WATERVEIL => 4,
|
||||
:WEAKARMOR => 2,
|
||||
:WHITESMOKE => 4,
|
||||
:WIMPOUT => 3,
|
||||
:WONDERGUARD => 10,
|
||||
:WONDERSKIN => 4,
|
||||
:ZENMODE => -1
|
||||
}
|
||||
|
||||
# TODO: This method assumes the ability isn't being negated. Should it return
|
||||
# 0 if it is? The calculations that call this method separately check
|
||||
# for it being negated, because they need to do something special in
|
||||
# that case, so I think it's okay for this method to ignore negation.
|
||||
def battler_wants_ability?(battler, ability = :NONE)
|
||||
ability = ability.id if !ability.is_a?(Symbol) && ability.respond_to?("id")
|
||||
# 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
|
||||
end
|
||||
|
||||
@@ -1,804 +0,0 @@
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# Struggle
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# None
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("DoesNothingCongratulations",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next Battle::AI::MOVE_USELESS_SCORE
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.copy("DoesNothingCongratulations",
|
||||
"DoesNothingFailsIfNoAlly")
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.copy("DoesNothingCongratulations",
|
||||
"DoesNothingUnusableInGravity")
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# AddMoneyGainedFromBattle
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.copy("DoesNothingCongratulations",
|
||||
"DoubleMoneyGainedFromBattle")
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("FailsIfNotUserFirstTurn",
|
||||
proc { |move, user, ai, battle|
|
||||
next user.turnCount > 0
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("FailsIfNotUserFirstTurn",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next score + 25 # Use it or lose it
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("FailsIfUserHasUnusedMove",
|
||||
proc { |move, user, ai, battle|
|
||||
has_another_move = false
|
||||
has_unused_move = false
|
||||
user.battler.eachMove do |m|
|
||||
next if m.id == move.id
|
||||
has_another_move = true
|
||||
next if user.battler.movesUsed.include?(m.id)
|
||||
has_unused_move = true
|
||||
break
|
||||
end
|
||||
next !has_another_move || has_unused_move
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("FailsIfUserNotConsumedBerry",
|
||||
proc { |move, user, ai, battle|
|
||||
next !user.battler.belched?
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("FailsIfTargetHasNoItem",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next !target.item || !target.item_active?
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("FailsUnlessTargetSharesTypeWithUser",
|
||||
proc { |move, user, target, ai, battle|
|
||||
user_types = user.pbTypes(true)
|
||||
target_types = target.pbTypes(true)
|
||||
next (user_types & target_types).empty?
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Split some of this into a MoveEffectScore?
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("FailsIfUserDamagedThisTurn",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Check whether user is faster than its foe(s) and could use this move
|
||||
user_faster_count = 0
|
||||
foe_faster_count = 0
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
if user.faster_than?(b)
|
||||
user_faster_count += 1
|
||||
else
|
||||
foe_faster_count += 1
|
||||
end
|
||||
end
|
||||
next Battle::AI::MOVE_USELESS_SCORE if user_faster_count == 0
|
||||
score += 10 if foe_faster_count == 0
|
||||
# Effects that make the target unlikely to act before the user
|
||||
if ai.trainer.high_skill?
|
||||
if !target.can_attack?
|
||||
score += 20
|
||||
elsif target.effects[PBEffects::Confusion] > 1 ||
|
||||
target.effects[PBEffects::Attract] == user.index
|
||||
score += 10
|
||||
elsif target.battler.paralyzed?
|
||||
score += 5
|
||||
end
|
||||
end
|
||||
# Don't risk using this move if target is weak
|
||||
score -= 10 if target.hp <= target.totalhp / 2
|
||||
score -= 10 if target.hp <= target.totalhp / 4
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("FailsIfTargetActed",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Check whether user is faster than its foe(s) and could use this move
|
||||
next Battle::AI::MOVE_USELESS_SCORE if target.faster_than?(user)
|
||||
score += 10
|
||||
# TODO: Predict the target switching/using an item.
|
||||
# TODO: Predict the target using a damaging move or Me First.
|
||||
# Don't risk using this move if target is weak
|
||||
score -= 10 if target.hp <= target.totalhp / 2
|
||||
score -= 10 if target.hp <= target.totalhp / 4
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("CrashDamageIfFailsUnusableInGravity",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
score -= (100 - move.rough_accuracy) if user.battler.takesIndirectDamage?
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("StartSunWeather",
|
||||
proc { |move, user, ai, battle|
|
||||
next [:HarshSun, :HeavyRain, :StrongWinds, move.move.weatherType].include?(battle.field.weather)
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartSunWeather",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next Battle::AI::MOVE_USELESS_SCORE if battle.pbCheckGlobalAbility(:AIRLOCK) ||
|
||||
battle.pbCheckGlobalAbility(:CLOUDNINE)
|
||||
score += 10 if battle.field.weather != :None # Prefer replacing another weather
|
||||
score += 15 if user.has_active_item?(:HEATROCK)
|
||||
score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP
|
||||
# Check for Fire/Water moves
|
||||
ai.each_battler do |b, i|
|
||||
if b.has_damaging_move_of_type?(:FIRE)
|
||||
score += (b.opposes?(user)) ? -15 : 15
|
||||
end
|
||||
if b.has_damaging_move_of_type?(:WATER)
|
||||
score += (b.opposes?(user)) ? 15 : -15
|
||||
end
|
||||
end
|
||||
# TODO: Check for freezing moves.
|
||||
# Check for abilities/other moves affected by sun
|
||||
# TODO: Check other battlers for these as well?
|
||||
if ai.trainer.medium_skill? && !user.has_active_item?(:UTILITYUMBRELLA)
|
||||
if user.has_active_ability?([:CHLOROPHYLL, :FLOWERGIFT, :FORECAST, :HARVEST, :LEAFGUARD, :SOLARPOWER])
|
||||
score += 15
|
||||
elsif user.has_active_ability?(:DRYSKIN)
|
||||
score -= 10
|
||||
end
|
||||
if user.has_move_with_function?("HealUserDependingOnWeather",
|
||||
"RaiseUserAtkSpAtk1Or2InSun",
|
||||
"TwoTurnAttackOneTurnInSun",
|
||||
"TypeAndPowerDependOnWeather")
|
||||
score += 10
|
||||
end
|
||||
if user.has_move_with_function?("ConfuseTargetAlwaysHitsInRainHitsTargetInSky",
|
||||
"ParalyzeTargetAlwaysHitsInRainHitsTargetInSky")
|
||||
score -= 10
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.copy("StartSunWeather",
|
||||
"StartRainWeather")
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartRainWeather",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next Battle::AI::MOVE_USELESS_SCORE if battle.pbCheckGlobalAbility(:AIRLOCK) ||
|
||||
battle.pbCheckGlobalAbility(:CLOUDNINE)
|
||||
score += 10 if battle.field.weather != :None # Prefer replacing another weather
|
||||
score += 15 if user.has_active_item?(:DAMPROCK)
|
||||
score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP
|
||||
# Check for Fire/Water moves
|
||||
ai.each_battler do |b, i|
|
||||
if b.has_damaging_move_of_type?(:WATER)
|
||||
score += (b.opposes?(user)) ? -15 : 15
|
||||
end
|
||||
if b.has_damaging_move_of_type?(:FIRE)
|
||||
score += (b.opposes?(user)) ? 15 : -15
|
||||
end
|
||||
end
|
||||
# Check for abilities/other moves affected by rain
|
||||
# TODO: Check other battlers for these as well?
|
||||
if ai.trainer.medium_skill? && !user.has_active_item?(:UTILITYUMBRELLA)
|
||||
if user.has_active_ability?([:DRYSKIN, :FORECAST, :HYDRATION, :RAINDISH, :SWIFTSWIM])
|
||||
score += 15
|
||||
end
|
||||
if user.has_move_with_function?("ConfuseTargetAlwaysHitsInRainHitsTargetInSky",
|
||||
"ParalyzeTargetAlwaysHitsInRainHitsTargetInSky",
|
||||
"TypeAndPowerDependOnWeather")
|
||||
score += 10
|
||||
end
|
||||
if user.has_move_with_function?("HealUserDependingOnWeather",
|
||||
"TwoTurnAttackOneTurnInSun")
|
||||
score -= 10
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.copy("StartSunWeather",
|
||||
"StartSandstormWeather")
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartSandstormWeather",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next Battle::AI::MOVE_USELESS_SCORE if battle.pbCheckGlobalAbility(:AIRLOCK) ||
|
||||
battle.pbCheckGlobalAbility(:CLOUDNINE)
|
||||
score += 10 if battle.field.weather != :None # Prefer replacing another weather
|
||||
score += 15 if user.has_active_item?(:SMOOTHROCK)
|
||||
score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP
|
||||
# Check for battlers affected by sandstorm's effects
|
||||
ai.each_battler do |b, i|
|
||||
if b.battler.takesSandstormDamage? # End of round damage
|
||||
score += (b.opposes?(user)) ? 15 : -15
|
||||
end
|
||||
if b.has_type?(:ROCK) # +SpDef for Rock types
|
||||
score += (b.opposes?(user)) ? -15 : 15
|
||||
end
|
||||
end
|
||||
# Check for abilities/moves affected by sandstorm
|
||||
# TODO: Check other battlers for these as well?
|
||||
if ai.trainer.medium_skill? && !user.has_active_item?(:UTILITYUMBRELLA)
|
||||
if user.has_active_ability?([:SANDFORCE, :SANDRUSH, :SANDVEIL])
|
||||
score += 15
|
||||
end
|
||||
if user.has_move_with_function?("HealUserDependingOnSandstorm",
|
||||
"TypeAndPowerDependOnWeather")
|
||||
score += 10
|
||||
end
|
||||
if user.has_move_with_function?("HealUserDependingOnWeather",
|
||||
"TwoTurnAttackOneTurnInSun")
|
||||
score -= 10
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.copy("StartSunWeather",
|
||||
"StartHailWeather")
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartHailWeather",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next Battle::AI::MOVE_USELESS_SCORE if battle.pbCheckGlobalAbility(:AIRLOCK) ||
|
||||
battle.pbCheckGlobalAbility(:CLOUDNINE)
|
||||
score += 10 if battle.field.weather != :None # Prefer replacing another weather
|
||||
score += 15 if user.has_active_item?(:ICYROCK)
|
||||
score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP
|
||||
# Check for battlers affected by hail's effects
|
||||
ai.each_battler do |b, i|
|
||||
if b.battler.takesHailDamage? # End of round damage
|
||||
score += (b.opposes?(user)) ? 15 : -15
|
||||
end
|
||||
end
|
||||
# Check for abilities/moves affected by hail
|
||||
# TODO: Check other battlers for these as well?
|
||||
if ai.trainer.medium_skill? && !user.has_active_item?(:UTILITYUMBRELLA)
|
||||
if user.has_active_ability?([:FORECAST, :ICEBODY, :SLUSHRUSH, :SNOWCLOAK])
|
||||
score += 15
|
||||
elsif user.ability == :ICEFACE
|
||||
score += 15
|
||||
end
|
||||
if user.has_move_with_function?("FreezeTargetAlwaysHitsInHail",
|
||||
"StartWeakenDamageAgainstUserSideIfHail",
|
||||
"TypeAndPowerDependOnWeather")
|
||||
score += 10
|
||||
end
|
||||
if user.has_move_with_function?("HealUserDependingOnWeather",
|
||||
"TwoTurnAttackOneTurnInSun")
|
||||
score -= 10
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("StartElectricTerrain",
|
||||
proc { |move, user, ai, battle|
|
||||
next battle.field.terrain == :Electric
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartElectricTerrain",
|
||||
proc { |score, move, user, ai, battle|
|
||||
score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP
|
||||
if battle.field.terrain != :None
|
||||
score -= ai.get_score_for_terrain(battle.field.terrain, user)
|
||||
end
|
||||
score += ai.get_score_for_terrain(:Electric, user)
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("StartGrassyTerrain",
|
||||
proc { |move, user, ai, battle|
|
||||
next battle.field.terrain == :Grassy
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartGrassyTerrain",
|
||||
proc { |score, move, user, ai, battle|
|
||||
score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP
|
||||
if battle.field.terrain != :None
|
||||
score -= ai.get_score_for_terrain(battle.field.terrain, user)
|
||||
end
|
||||
score += ai.get_score_for_terrain(:Grassy, user)
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("StartMistyTerrain",
|
||||
proc { |move, user, ai, battle|
|
||||
next battle.field.terrain == :Misty
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartMistyTerrain",
|
||||
proc { |score, move, user, ai, battle|
|
||||
score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP
|
||||
if battle.field.terrain != :None
|
||||
score -= ai.get_score_for_terrain(battle.field.terrain, user)
|
||||
end
|
||||
score += ai.get_score_for_terrain(:Misty, user)
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("StartPsychicTerrain",
|
||||
proc { |move, user, ai, battle|
|
||||
next battle.field.terrain == :Psychic
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartPsychicTerrain",
|
||||
proc { |score, move, user, ai, battle|
|
||||
score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP
|
||||
if battle.field.terrain != :None
|
||||
score -= ai.get_score_for_terrain(battle.field.terrain, user)
|
||||
end
|
||||
score += ai.get_score_for_terrain(:Psychic, user)
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("RemoveTerrain",
|
||||
proc { |move, user, ai, battle|
|
||||
next battle.field.terrain == :None
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("RemoveTerrain",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next score - ai.get_score_for_terrain(battle.field.terrain, user)
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("AddSpikesToFoeSide",
|
||||
proc { |move, user, ai, battle|
|
||||
next user.pbOpposingSide.effects[PBEffects::Spikes] >= 3
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("AddSpikesToFoeSide",
|
||||
proc { |score, move, user, ai, battle|
|
||||
inBattleIndices = battle.allSameSideBattlers(user.idxOpposingSide).map { |b| b.pokemonIndex }
|
||||
foe_reserves = []
|
||||
battle.pbParty(user.idxOpposingSide).each_with_index do |pkmn, idxParty|
|
||||
next if !pkmn || !pkmn.able? || inBattleIndices.include?(idxParty)
|
||||
if ai.trainer.medium_skill?
|
||||
# Check affected by entry hazard
|
||||
next if pkmn.hasItem?(:HEAVYDUTYBOOTS)
|
||||
# Check can take indirect damage
|
||||
next if pkmn.hasAbility?(:MAGICGUARD)
|
||||
# Check airborne
|
||||
if !pkmn.hasItem?(:IRONBALL) &&
|
||||
battle.field.effects[PBEffects::Gravity] == 0
|
||||
next if pkmn.hasType?(:FLYING)
|
||||
next if pkmn.hasAbility?(:LEVITATE)
|
||||
next if pkmn.hasItem?(:AIRBALLOON)
|
||||
end
|
||||
end
|
||||
foe_reserves.push(pkmn) # pkmn will be affected by Spikes
|
||||
end
|
||||
next Battle::AI::MOVE_USELESS_SCORE if foe_reserves.empty?
|
||||
multiplier = [8, 5, 3][user.pbOpposingSide.effects[PBEffects::Spikes]]
|
||||
score += multiplier * foe_reserves.length
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("AddToxicSpikesToFoeSide",
|
||||
proc { |move, user, ai, battle|
|
||||
next user.pbOpposingSide.effects[PBEffects::ToxicSpikes] >= 2
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("AddToxicSpikesToFoeSide",
|
||||
proc { |score, move, user, ai, battle|
|
||||
inBattleIndices = battle.allSameSideBattlers(user.idxOpposingSide).map { |b| b.pokemonIndex }
|
||||
foe_reserves = []
|
||||
battle.pbParty(user.idxOpposingSide).each_with_index do |pkmn, idxParty|
|
||||
next if !pkmn || !pkmn.able? || inBattleIndices.include?(idxParty)
|
||||
if ai.trainer.medium_skill?
|
||||
# Check affected by entry hazard
|
||||
next if pkmn.hasItem?(:HEAVYDUTYBOOTS)
|
||||
# TODO: Check pkmn's immunity to being poisoned.
|
||||
next if battle.field.terrain == :Misty
|
||||
next if pkmn.hasType?(:POISON)
|
||||
next if pkmn.hasType?(:STEEL)
|
||||
# Check airborne
|
||||
if !pkmn.hasItem?(:IRONBALL) &&
|
||||
battle.field.effects[PBEffects::Gravity] == 0
|
||||
next if pkmn.hasType?(:FLYING)
|
||||
next if pkmn.hasAbility?(:LEVITATE)
|
||||
next if pkmn.hasItem?(:AIRBALLOON)
|
||||
end
|
||||
end
|
||||
foe_reserves.push(pkmn) # pkmn will be affected by Toxic Spikes
|
||||
end
|
||||
next Battle::AI::MOVE_USELESS_SCORE if foe_reserves.empty?
|
||||
multiplier = [6, 4][user.pbOpposingSide.effects[PBEffects::ToxicSpikes]]
|
||||
score += multiplier * foe_reserves.length
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("AddStealthRocksToFoeSide",
|
||||
proc { |move, user, ai, battle|
|
||||
next user.pbOpposingSide.effects[PBEffects::StealthRock]
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("AddStealthRocksToFoeSide",
|
||||
proc { |score, move, user, ai, battle|
|
||||
inBattleIndices = battle.allSameSideBattlers(user.idxOpposingSide).map { |b| b.pokemonIndex }
|
||||
foe_reserves = []
|
||||
battle.pbParty(user.idxOpposingSide).each_with_index do |pkmn, idxParty|
|
||||
next if !pkmn || !pkmn.able? || inBattleIndices.include?(idxParty)
|
||||
if ai.trainer.medium_skill?
|
||||
# Check affected by entry hazard
|
||||
next if pkmn.hasItem?(:HEAVYDUTYBOOTS)
|
||||
# Check can take indirect damage
|
||||
next if pkmn.hasAbility?(:MAGICGUARD)
|
||||
end
|
||||
foe_reserves.push(pkmn) # pkmn will be affected by Stealth Rock
|
||||
end
|
||||
next Battle::AI::MOVE_USELESS_SCORE if foe_reserves.empty?
|
||||
next score + 8 * foe_reserves.length
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("AddStickyWebToFoeSide",
|
||||
proc { |move, user, ai, battle|
|
||||
next user.pbOpposingSide.effects[PBEffects::StickyWeb]
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("AddStickyWebToFoeSide",
|
||||
proc { |score, move, user, ai, battle|
|
||||
inBattleIndices = battle.allSameSideBattlers(user.idxOpposingSide).map { |b| b.pokemonIndex }
|
||||
foe_reserves = []
|
||||
battle.pbParty(user.idxOpposingSide).each_with_index do |pkmn, idxParty|
|
||||
next if !pkmn || !pkmn.able? || inBattleIndices.include?(idxParty)
|
||||
if ai.trainer.medium_skill?
|
||||
# Check affected by entry hazard
|
||||
next if pkmn.hasItem?(:HEAVYDUTYBOOTS)
|
||||
# Check airborne
|
||||
if !pkmn.hasItem?(:IRONBALL) &&
|
||||
battle.field.effects[PBEffects::Gravity] == 0
|
||||
next if pkmn.hasType?(:FLYING)
|
||||
next if pkmn.hasAbility?(:LEVITATE)
|
||||
next if pkmn.hasItem?(:AIRBALLOON)
|
||||
end
|
||||
end
|
||||
foe_reserves.push(pkmn) # pkmn will be affected by Sticky Web
|
||||
end
|
||||
next Battle::AI::MOVE_USELESS_SCORE if foe_reserves.empty?
|
||||
next score + 7 * foe_reserves.length
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("SwapSideEffects",
|
||||
proc { |move, user, ai, battle|
|
||||
has_effect = false
|
||||
2.times do |side|
|
||||
effects = battle.sides[side].effects
|
||||
move.move.number_effects.each do |e|
|
||||
next if effects[e] == 0
|
||||
has_effect = true
|
||||
break
|
||||
end
|
||||
break if has_effect
|
||||
move.move.boolean_effects.each do |e|
|
||||
next if !effects[e]
|
||||
has_effect = true
|
||||
break
|
||||
end
|
||||
break if has_effect
|
||||
end
|
||||
next !has_effect
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("SwapSideEffects",
|
||||
proc { |score, move, user, ai, battle|
|
||||
if ai.trainer.medium_skill?
|
||||
good_effects = [:AuroraVeil, :LightScreen, :Mist, :Rainbow, :Reflect,
|
||||
:Safeguard, :SeaOfFire, :Swamp, :Tailwind].map! { |e| PBEffects.const_get(e) }
|
||||
bad_effects = [:Spikes, :StealthRock, :StickyWeb, :ToxicSpikes].map! { |e| PBEffects.const_get(e) }
|
||||
bad_effects.each do |e|
|
||||
score += 10 if ![0, false, nil].include?(user.pbOwnSide.effects[e])
|
||||
score -= 10 if ![0, 1, false, nil].include?(user.pbOpposingSide.effects[e])
|
||||
end
|
||||
if ai.trainer.high_skill?
|
||||
good_effects.each do |e|
|
||||
score += 10 if ![0, 1, false, nil].include?(user.pbOpposingSide.effects[e])
|
||||
score -= 10 if ![0, false, nil].include?(user.pbOwnSide.effects[e])
|
||||
end
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("UserMakeSubstitute",
|
||||
proc { |move, user, ai, battle|
|
||||
next true if user.effects[PBEffects::Substitute] > 0
|
||||
next user.hp <= [user.totalhp / 4, 1].max
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UserMakeSubstitute",
|
||||
proc { |score, move, user, ai, battle|
|
||||
# Prefer more the higher the user's HP
|
||||
score += (8 * user.hp.to_f / user.totalhp).round
|
||||
# Prefer if foes don't know any moves that can bypass a substitute
|
||||
ai.each_battler do |b, i|
|
||||
score += 4 if !b.check_for_move { |m| m.ignoresSubstitute?(b.battler) }
|
||||
end
|
||||
# TODO: Predict incoming damage, and prefer if it's greater than
|
||||
# user.totalhp / 4?
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("RemoveUserBindingAndEntryHazards",
|
||||
proc { |score, move, user, ai, battle|
|
||||
score += 10 if user.effects[PBEffects::Trapping] > 0
|
||||
score += 15 if user.effects[PBEffects::LeechSeed] >= 0
|
||||
if battle.pbAbleNonActiveCount(user.idxOwnSide) > 0
|
||||
score += 15 if user.pbOwnSide.effects[PBEffects::Spikes] > 0
|
||||
score += 15 if user.pbOwnSide.effects[PBEffects::ToxicSpikes] > 0
|
||||
score += 20 if user.pbOwnSide.effects[PBEffects::StealthRock]
|
||||
score += 15 if user.pbOwnSide.effects[PBEffects::StickyWeb]
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("AttackTwoTurnsLater",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next battle.positions[target.index].effects[PBEffects::FutureSightCounter] > 0
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("AttackTwoTurnsLater",
|
||||
proc { |score, move, user, ai, battle|
|
||||
# Future Sight tends to be wasteful if down to last Pokémon
|
||||
score -= 20 if battle.pbAbleNonActiveCount(user.idxOwnSide) == 0
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("UserSwapsPositionsWithAlly",
|
||||
proc { |move, user, ai, battle|
|
||||
num_targets = 0
|
||||
idxUserOwner = battle.pbGetOwnerIndexFromBattlerIndex(user.index)
|
||||
ai.each_ally(user.side) do |b, i|
|
||||
next if battle.pbGetOwnerIndexFromBattlerIndex(b.index) != idxUserOwner
|
||||
next if !b.battler.near?(user.battler)
|
||||
num_targets += 1
|
||||
end
|
||||
next num_targets != 1
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UserSwapsPositionsWithAlly",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next score - 30 # Usually no point in using this
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("BurnAttackerBeforeUserActs",
|
||||
proc { |score, move, user, ai, battle|
|
||||
ai.each_foe_battler(user.side) do |b|
|
||||
next if !b.battler.affectedByContactEffect?
|
||||
next if !b.battler.pbCanBurn?(user.battler, false, move.move)
|
||||
if ai.trainer.high_skill?
|
||||
next if !b.check_for_move { |m| m.pbContactMove?(b.battler) }
|
||||
end
|
||||
score += 10 # Possible to burn
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("AllBattlersLoseHalfHPUserSkipsNextTurn",
|
||||
proc { |score, move, user, ai, battle|
|
||||
# HP halving
|
||||
foe_hp_lost = 0
|
||||
ally_hp_lost = 0
|
||||
ai.each_battler do |b, i|
|
||||
next if b.hp == 1
|
||||
if b.battler.opposes?(user.battler)
|
||||
foe_hp_lost += b.hp / 2
|
||||
else
|
||||
ally_hp_lost += b.hp / 2
|
||||
end
|
||||
end
|
||||
score += 15 * foe_hp_lost / ally_hp_lost
|
||||
score -= 15 * ally_hp_lost / foe_hp_lost
|
||||
# Recharging
|
||||
score = Battle::AI::Handlers.apply_move_effect_score("AttackAndSkipNextTurn",
|
||||
score, move, user, ai, battle)
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UserLosesHalfHP",
|
||||
proc { |score, move, user, ai, battle|
|
||||
score = Battle::AI::Handlers.apply_move_effect_score("UserLosesHalfOfTotalHP",
|
||||
score, move, user, ai, battle)
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.copy("StartSunWeather",
|
||||
"StartShadowSkyWeather")
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartShadowSkyWeather",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next Battle::AI::MOVE_USELESS_SCORE if battle.pbCheckGlobalAbility(:AIRLOCK) ||
|
||||
battle.pbCheckGlobalAbility(:CLOUDNINE)
|
||||
score += 10 if battle.field.weather != :None # Prefer replacing another weather
|
||||
score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP
|
||||
# Check for battlers affected by Shadow Sky's effects
|
||||
ai.each_battler do |b, i|
|
||||
if b.has_damaging_move_of_type?(:SHADOW)
|
||||
score += (b.opposes?(user)) ? 15 : -15
|
||||
end
|
||||
if b.battler.takesShadowSkyDamage? # End of round damage
|
||||
score += (b.opposes?(user)) ? 15 : -15
|
||||
end
|
||||
end
|
||||
# Check for moves affected by Shadow Sky
|
||||
# TODO: Check other battlers for these as well?
|
||||
if ai.trainer.medium_skill? && !user.has_active_item?(:UTILITYUMBRELLA)
|
||||
if user.has_move_with_function?("TypeAndPowerDependOnWeather")
|
||||
score += 10
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("RemoveAllScreensAndSafeguard",
|
||||
proc { |move, user, ai, battle|
|
||||
will_fail = true
|
||||
battle.sides.each do |side|
|
||||
will_fail = false if side.effects[PBEffects::AuroraVeil] > 0 ||
|
||||
side.effects[PBEffects::LightScreen] > 0 ||
|
||||
side.effects[PBEffects::Reflect] > 0 ||
|
||||
side.effects[PBEffects::Safeguard] > 0
|
||||
end
|
||||
next will_fail
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("RemoveAllScreensAndSafeguard",
|
||||
proc { |score, move, user, ai, battle|
|
||||
foe_side = user.pbOpposingSide
|
||||
# Useless if the foe's side has no screens/Safeguard to remove, or if
|
||||
# they'll end this round anyway
|
||||
if foe_side.effects[PBEffects::AuroraVeil] <= 1 &&
|
||||
foe_side.effects[PBEffects::LightScreen] <= 1 &&
|
||||
foe_side.effects[PBEffects::Reflect] <= 1 &&
|
||||
foe_side.effects[PBEffects::Safeguard] <= 1
|
||||
next Battle::AI::MOVE_USELESS_SCORE
|
||||
end
|
||||
# Prefer removing opposing screens
|
||||
score = Battle::AI::Handlers.apply_move_effect_score("RemoveScreens",
|
||||
score, move, user, ai, battle)
|
||||
# Don't prefer removing same side screens
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
score -= Battle::AI::Handlers.apply_move_effect_score("RemoveScreens",
|
||||
0, move, b, ai, battle)
|
||||
break
|
||||
end
|
||||
# Safeguard
|
||||
score += 10 if foe_side.effects[PBEffects::Safeguard] > 0
|
||||
score -= 10 if user.pbOwnSide.effects[PBEffects::Safeguard] > 0
|
||||
next score
|
||||
}
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,515 +0,0 @@
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveBasePower.add("HitTwoTimes",
|
||||
proc { |power, move, user, target, ai, battle|
|
||||
next power * move.move.pbNumHits(user.battler, [target.battler])
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HitTwoTimes",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Prefer if the target has a Substitute and the first hit can break it
|
||||
if target.effects[PBEffects::Substitute] > 0 && !move.move.ignoresSubstitute?(user.battler)
|
||||
dmg = move.rough_damage
|
||||
num_hits = move.move.pbNumHits(user.battler, [target.battler])
|
||||
score += 10 if target.effects[PBEffects::Substitute] < dmg * (num_hits - 1) / num_hits
|
||||
end
|
||||
# TODO: Consider effects that trigger per hit.
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveBasePower.copy("HitTwoTimes",
|
||||
"HitTwoTimesPoisonTarget")
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HitTwoTimesPoisonTarget",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Score for hitting multiple times
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("HitTwoTimes",
|
||||
score, move, user, target, ai, battle)
|
||||
# Score for poisoning
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("PoisonTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveBasePower.copy("HitTwoTimes",
|
||||
"HitTwoTimesFlinchTarget")
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HitTwoTimesFlinchTarget",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Score for hitting multiple times
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("HitTwoTimes",
|
||||
score, move, user, target, ai, battle)
|
||||
# Score for flinching
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("FlinchTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveBasePower.add("HitTwoTimesTargetThenTargetAlly",
|
||||
proc { |power, move, user, target, ai, battle|
|
||||
next power * 2
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveBasePower.add("HitThreeTimesPowersUpWithEachHit",
|
||||
proc { |power, move, user, target, ai, battle|
|
||||
next power * 6 # Hits do x1, x2, x3 ret in turn, for x6 in total
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HitThreeTimesPowersUpWithEachHit",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Prefer if the target has a Substitute and the first or second hit can break it
|
||||
if target.effects[PBEffects::Substitute] > 0 && !move.move.ignoresSubstitute?(user.battler)
|
||||
dmg = move.rough_damage
|
||||
score += 10 if target.effects[PBEffects::Substitute] < dmg / 2
|
||||
end
|
||||
# TODO: Consider effects that trigger per hit.
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveBasePower.copy("HitTwoTimes",
|
||||
"HitThreeTimesAlwaysCriticalHit")
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.copy("HitTwoTimes",
|
||||
"HitThreeTimesAlwaysCriticalHit")
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveBasePower.add("HitTwoToFiveTimes",
|
||||
proc { |power, move, user, target, ai, battle|
|
||||
next power * 5 if user.has_active_ability?(:SKILLLINK)
|
||||
next power * 31 / 10 # Average damage dealt
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HitTwoToFiveTimes",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Prefer if the target has a Substitute and the first hit(s) can break it
|
||||
if target.effects[PBEffects::Substitute] > 0 && !move.move.ignoresSubstitute?(user.battler)
|
||||
dmg = move.rough_damage
|
||||
num_hits = (user.has_active_ability?(:SKILLLINK)) ? 5 : 3 # 3 is about average
|
||||
score += 10 if target.effects[PBEffects::Substitute] < dmg * (num_hits - 1) / num_hits
|
||||
end
|
||||
# TODO: Consider effects that trigger per hit.
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveBasePower.add("HitTwoToFiveTimesOrThreeForAshGreninja",
|
||||
proc { |power, move, user, target, ai, battle|
|
||||
if user.battler.isSpecies?(:GRENINJA) && user.battler.form == 2
|
||||
next move.move.pbBaseDamage(power, user.battler, target.battler) * move.move.pbNumHits(user.battler, [target.battler])
|
||||
end
|
||||
next power * 5 if user.has_active_ability?(:SKILLLINK)
|
||||
next power * 31 / 10 # Average damage dealt
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.copy("HitTwoToFiveTimes",
|
||||
"HitTwoToFiveTimesOrThreeForAshGreninja")
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveBasePower.copy("HitTwoToFiveTimes",
|
||||
"HitTwoToFiveTimesRaiseUserSpd1LowerUserDef1")
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HitTwoToFiveTimesRaiseUserSpd1LowerUserDef1",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Score for being a multi-hit attack
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("HitTwoToFiveTimes",
|
||||
score, move, user, target, ai, battle)
|
||||
# Score for user's stat changes
|
||||
score = ai.get_score_for_target_stat_raise(score, user, [:SPEED, 1], false)
|
||||
score = ai.get_score_for_target_stat_drop(score, user, [:DEFENSE, 1], false)
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("HitOncePerUserTeamMember",
|
||||
proc { |move, user, ai, battle|
|
||||
will_fail = true
|
||||
battle.eachInTeamFromBattlerIndex(user.index) do |pkmn, i|
|
||||
next if !pkmn.able? || pkmn.status != :NONE
|
||||
will_fail = false
|
||||
break
|
||||
end
|
||||
next will_fail
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveBasePower.add("HitOncePerUserTeamMember",
|
||||
proc { |power, move, user, target, ai, battle|
|
||||
ret = 0
|
||||
battle.eachInTeamFromBattlerIndex(user.index) do |pkmn, _i|
|
||||
ret += 5 + (pkmn.baseStats[:ATTACK] / 10) if pkmn.able? && pkmn.status == :NONE
|
||||
end
|
||||
next ret
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HitOncePerUserTeamMember",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Prefer if the target has a Substitute and the first hit(s) can break it
|
||||
if target.effects[PBEffects::Substitute] > 0 && !move.move.ignoresSubstitute?(user.battler)
|
||||
dmg = move.rough_damage
|
||||
num_hits = 0
|
||||
battle.eachInTeamFromBattlerIndex(user.index) do |pkmn, _i|
|
||||
num_hits += 1 if pkmn.able? && pkmn.status == :NONE
|
||||
end
|
||||
score += 10 if target.effects[PBEffects::Substitute] < dmg * (num_hits - 1) / num_hits
|
||||
end
|
||||
# TODO: Consider effects that trigger per hit.
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("AttackAndSkipNextTurn",
|
||||
proc { |score, move, user, ai, battle|
|
||||
# Don't prefer because it uses up two turns
|
||||
score -= 10 if !user.has_active_ability?(:TRUANT)
|
||||
# Don't prefer if user is at a high HP (treat this move as a last resort)
|
||||
score -= 10 if user.hp >= user.totalhp / 2
|
||||
# TODO: Don't prefer if another of the user's moves could KO the target.
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttack",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Power Herb makes this a 1 turn move, the same as a move with no effect
|
||||
next score if user.has_active_item?(:POWERHERB)
|
||||
# Treat as a failure if user has Truant (the charging turn has no effect)
|
||||
next Battle::AI::MOVE_USELESS_SCORE if user.has_active_ability?(:TRUANT)
|
||||
# Useless if user will faint from EoR damage before finishing this attack
|
||||
next Battle::AI::MOVE_USELESS_SCORE if user.rough_end_of_round_damage >= user.hp
|
||||
# Don't prefer because it uses up two turns
|
||||
score -= 10
|
||||
# Don't prefer if user is at a low HP (time is better spent on quicker moves)
|
||||
score -= 8 if user.hp < user.totalhp / 2
|
||||
# Don't prefer if target has a protecting move
|
||||
if ai.trainer.high_skill? && !(user.has_active_ability?(:UNSEENFIST) && move.move.contactMove?)
|
||||
has_protect_move = false
|
||||
if move.move.pbTarget(user).num_targets > 1 &&
|
||||
(Settings::MECHANICS_GENERATION >= 7 || move.damagingMove?)
|
||||
if target.has_move_with_function?("ProtectUserSideFromMultiTargetDamagingMoves")
|
||||
has_protect_move = true
|
||||
end
|
||||
end
|
||||
if move.move.canProtectAgainst?
|
||||
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.has_move_with_function?("ProtectUserFromDamagingMovesKingsShield",
|
||||
"ProtectUserFromDamagingMovesObstruct")
|
||||
has_protect_move = true
|
||||
end
|
||||
end
|
||||
if move.rough_priority(user) > 0
|
||||
if target.has_move_with_function?("ProtectUserSideFromPriorityMoves")
|
||||
has_protect_move = true
|
||||
end
|
||||
end
|
||||
end
|
||||
score -= 15 if has_protect_move
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveBasePower.add("TwoTurnAttackOneTurnInSun",
|
||||
proc { |power, move, user, target, ai, battle|
|
||||
next move.move.pbBaseDamageMultiplier(power, user.battler, target.battler)
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackOneTurnInSun",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# In sunny weather this a 1 turn move, the same as a move with no effect
|
||||
next score if [:Sun, :HarshSun].include?(user.battler.effectiveWeather)
|
||||
# Score for being a two turn attack
|
||||
next Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttack",
|
||||
score, move, user, target, ai, battle)
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackParalyzeTarget",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Score for being a two turn attack
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttack",
|
||||
score, move, user, target, ai, battle)
|
||||
# Score for paralysing
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("ParalyzeTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackBurnTarget",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Score for being a two turn attack
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttack",
|
||||
score, move, user, target, ai, battle)
|
||||
# Score for burning
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("BurnTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackFlinchTarget",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Score for being a two turn attack
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttack",
|
||||
score, move, user, target, ai, battle)
|
||||
# Score for flinching
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("FlinchTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.copy("RaiseUserAtkDef1",
|
||||
"TwoTurnAttackRaiseUserSpAtkSpDefSpd2")
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackRaiseUserSpAtkSpDefSpd2",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Score for raising user's stats
|
||||
score = ai.get_score_for_target_stat_raise(score, user, move.move.statUp)
|
||||
# Score for being a two turn attack
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttack",
|
||||
score, move, user, target, ai, battle)
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackChargeRaiseUserDefense1",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Score for being a two turn attack
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttack",
|
||||
score, move, user, target, ai, battle)
|
||||
# Score for raising the user's stat
|
||||
score = Battle::AI::Handlers.apply_move_effect_score("RaiseUserDefense1",
|
||||
score, move, user, ai, battle)
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackChargeRaiseUserSpAtk1",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Score for being a two turn attack
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttack",
|
||||
score, move, user, target, ai, battle)
|
||||
# Score for raising the user's stat
|
||||
score = Battle::AI::Handlers.apply_move_effect_score("RaiseUserSpAtk1",
|
||||
score, move, user, ai, battle)
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackInvulnerableUnderground",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Score for being a two turn attack
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttack",
|
||||
score, move, user, target, ai, battle)
|
||||
# Score for being semi-invulnerable underground
|
||||
user.each_foe_battler(user.side) do |b, i|
|
||||
if b.check_for_move { |m| m.hitsDiggingTargets? }
|
||||
score -= 8
|
||||
else
|
||||
score += 5
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackInvulnerableUnderwater",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Score for being a two turn attack
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttack",
|
||||
score, move, user, target, ai, battle)
|
||||
# Score for being semi-invulnerable underwater
|
||||
user.each_foe_battler(user.side) do |b, i|
|
||||
if b.check_for_move { |m| m.hitsDivingTargets? }
|
||||
score -= 8
|
||||
else
|
||||
score += 5
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackInvulnerableInSky",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Score for being a two turn attack
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttack",
|
||||
score, move, user, target, ai, battle)
|
||||
# Score for being semi-invulnerable in the sky
|
||||
user.each_foe_battler(user.side) do |b, i|
|
||||
if b.check_for_move { |m| m.hitsFlyingTargets? }
|
||||
score -= 8
|
||||
else
|
||||
score += 5
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackInvulnerableInSkyParalyzeTarget",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Score for being a two turn attack and semi-invulnerable in the sky
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttackInvulnerableInSky",
|
||||
score, move, user, target, ai, battle)
|
||||
# Score for paralyzing the target
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("ParalyzeTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("TwoTurnAttackInvulnerableInSkyTargetCannotAct",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if !target.opposes?(user)
|
||||
next true if target.effects[PBEffects::Substitute] > 0 && !move.move.ignoresSubstitute?(user.battler)
|
||||
next true if Settings::MECHANICS_GENERATION >= 6 && target.battler.pbWeight >= 2000 # 200.0kg
|
||||
next true if target.battler.semiInvulnerable? || target.effects[PBEffects::SkyDrop] >= 0
|
||||
next false
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.copy("TwoTurnAttackInvulnerableInSky",
|
||||
"TwoTurnAttackInvulnerableInSkyTargetCannotAct")
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TwoTurnAttackInvulnerableRemoveProtections",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Score for being a two turn attack
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("TwoTurnAttack",
|
||||
score, move, user, target, ai, battle)
|
||||
# Score for being invulnerable
|
||||
score += 5
|
||||
# Score for removing protections
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("RemoveProtections",
|
||||
score, move, user, target, ai, battle)
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# MultiTurnAttackPreventSleeping
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# MultiTurnAttackConfuseUserAtEnd
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveBasePower.add("MultiTurnAttackPowersUpEachTurn",
|
||||
proc { |power, move, user, target, ai, battle|
|
||||
# NOTE: The * 2 (roughly) incorporates the higher damage done in subsequent
|
||||
# rounds. It is nearly the average damage this move will do per round,
|
||||
# assuming it hits for 3 rounds (hoping for hits in all 5 rounds is
|
||||
# optimistic).
|
||||
next move.move.pbBaseDamage(power, user.battler, target.battler) * 2
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveBasePower.add("MultiTurnAttackBideThenReturnDoubleDamage",
|
||||
proc { |power, move, user, target, ai, battle|
|
||||
next 40 # Representative value
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("MultiTurnAttackBideThenReturnDoubleDamage",
|
||||
proc { |score, move, user, ai, battle|
|
||||
# Useless if no foe has any damaging moves
|
||||
has_damaging_move = false
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
next if b.status == :SLEEP && b.statusCount > 2
|
||||
next if b.status == :FROZEN
|
||||
has_damaging_move = true if b.check_for_move { |m| m.damagingMove? }
|
||||
break if has_damaging_move
|
||||
end
|
||||
next Battle::AI::MOVE_USELESS_SCORE if !has_damaging_move
|
||||
# Don't prefer if the user isn't at high HP
|
||||
next Battle::AI::MOVE_USELESS_SCORE if user.hp <= user.totalhp / 4
|
||||
score -= 15 if user.hp <= user.totalhp / 2
|
||||
score -= 8 if user.hp <= user.totalhp * 3 / 4
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -1,685 +0,0 @@
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("HealUserFullyAndFallAsleep",
|
||||
proc { |move, user, ai, battle|
|
||||
next true if !user.battler.canHeal?
|
||||
next true if user.battler.asleep?
|
||||
next true if !user.battler.pbCanSleep?(user.battler, false, move.move, true)
|
||||
next false
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("HealUserFullyAndFallAsleep",
|
||||
proc { |score, move, user, ai, battle|
|
||||
# Consider how much HP will be restored
|
||||
if user.hp >= user.totalhp * 0.5
|
||||
score -= 10
|
||||
else
|
||||
score += 20 * (user.totalhp - user.hp) / user.totalhp
|
||||
end
|
||||
# Check whether an existing status problem will be removed
|
||||
score += 10 if user.status != :NONE
|
||||
# Check if user will be able to act while asleep
|
||||
if ai.trainer.medium_skill?
|
||||
if user.check_for_move { |m| m.usableWhenAsleep? }
|
||||
score += 10
|
||||
else
|
||||
score -= 10
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("HealUserHalfOfTotalHP",
|
||||
proc { |move, user, ai, battle|
|
||||
next !user.battler.canHeal?
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("HealUserHalfOfTotalHP",
|
||||
proc { |score, move, user, ai, battle|
|
||||
# Consider how much HP will be restored
|
||||
if user.hp >= user.totalhp * 0.5
|
||||
score -= 10
|
||||
else
|
||||
score += 20 * (user.totalhp - user.hp) / user.totalhp
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.copy("HealUserHalfOfTotalHP",
|
||||
"HealUserDependingOnWeather")
|
||||
Battle::AI::Handlers::MoveEffectScore.add("HealUserDependingOnWeather",
|
||||
proc { |score, move, user, ai, battle|
|
||||
# Consider how much HP will be restored
|
||||
if user.hp >= user.totalhp * 0.5
|
||||
score -= 10
|
||||
else
|
||||
case user.battler.effectiveWeather
|
||||
when :Sun, :HarshSun
|
||||
score += 5
|
||||
when :None, :StrongWinds
|
||||
else
|
||||
score -= 10
|
||||
end
|
||||
score += 20 * (user.totalhp - user.hp) / user.totalhp
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.copy("HealUserHalfOfTotalHP",
|
||||
"HealUserDependingOnSandstorm")
|
||||
Battle::AI::Handlers::MoveEffectScore.add("HealUserDependingOnSandstorm",
|
||||
proc { |score, move, user, ai, battle|
|
||||
# Consider how much HP will be restored
|
||||
if user.hp >= user.totalhp * 0.5
|
||||
score -= 10
|
||||
else
|
||||
score += 5 if user.battler.effectiveWeather == :Sandstorm
|
||||
score += 20 * (user.totalhp - user.hp) / user.totalhp
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.copy("HealUserHalfOfTotalHP",
|
||||
"HealUserHalfOfTotalHPLoseFlyingTypeThisTurn")
|
||||
Battle::AI::Handlers::MoveEffectScore.add("HealUserHalfOfTotalHPLoseFlyingTypeThisTurn",
|
||||
proc { |score, move, user, ai, battle|
|
||||
# Consider how much HP will be restored
|
||||
if user.hp >= user.totalhp * 0.5
|
||||
score -= 10
|
||||
else
|
||||
score += 20 * (user.totalhp - user.hp) / user.totalhp
|
||||
end
|
||||
if user.has_type?(:FLYING)
|
||||
# TODO: Decide whether losing the Flying type is good or bad. Look at
|
||||
# type effectiveness changes against the user, and for foes' Ground
|
||||
# moves. Anything else?
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("CureTargetStatusHealUserHalfOfTotalHP",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next !user.battler.canHeal? || target.status == :NONE
|
||||
}
|
||||
)
|
||||
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
|
||||
# Consider how much HP will be restored
|
||||
if user.hp >= user.totalhp * 0.5
|
||||
score -= 10
|
||||
else
|
||||
score += 20 * (user.totalhp - user.hp) / user.totalhp
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("HealUserByTargetAttackLowerTargetAttack1",
|
||||
proc { |move, user, target, ai, battle|
|
||||
if !battle.moldBreaker && target.has_active_ability?(:CONTRARY)
|
||||
next target.statStageAtMax?(:ATTACK)
|
||||
end
|
||||
next target.statStageAtMin?(:ATTACK)
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HealUserByTargetAttackLowerTargetAttack1",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Check whether lowering the target's Attack will have any impact
|
||||
if ai.trainer.medium_skill?
|
||||
score = ai.get_score_for_target_stat_drop(score, target, move.move.statDown)
|
||||
end
|
||||
# Consider how much HP will be restored
|
||||
heal_amt = target.rough_stat(:ATTACK)
|
||||
if heal_amt > user.totalhp * 0.3 # Only modify the score if it'll heal a decent amount
|
||||
# Things that affect healing caused by draining
|
||||
if target.has_active_ability?(:LIQUIDOOZE)
|
||||
score -= 20
|
||||
elsif user.battler.canHeal?
|
||||
if user.hp >= user.totalhp * 0.5
|
||||
score -= 10
|
||||
else
|
||||
heal_amt *= 1.3 if user.has_active_item?(:BIGROOT)
|
||||
heal_fraction = [user.totalhp - user.hp, heal_amt].min.to_f / user.totalhp
|
||||
score += 40 * heal_fraction * (user.totalhp - user.hp) / user.totalhp
|
||||
end
|
||||
end
|
||||
else
|
||||
score -= 10 if target.has_active_ability?(:LIQUIDOOZE)
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HealUserByHalfOfDamageDone",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Consider how much HP will be restored
|
||||
heal_amt = move.rough_damage / 2
|
||||
if heal_amt > user.totalhp * 0.3 # Only modify the score if it'll heal a decent amount
|
||||
# Things that affect healing caused by draining
|
||||
if target.has_active_ability?(:LIQUIDOOZE)
|
||||
score -= 20
|
||||
elsif user.battler.canHeal?
|
||||
heal_amt *= 1.3 if user.has_active_item?(:BIGROOT)
|
||||
heal_fraction = [user.totalhp - user.hp, heal_amt].min.to_f / user.totalhp
|
||||
score += 40 * heal_fraction * (user.totalhp - user.hp) / user.totalhp
|
||||
end
|
||||
else
|
||||
score -= 10 if target.has_active_ability?(:LIQUIDOOZE)
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("HealUserByHalfOfDamageDoneIfTargetAsleep",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next !target.battler.asleep?
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.copy("HealUserByHalfOfDamageDone",
|
||||
"HealUserByHalfOfDamageDoneIfTargetAsleep")
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HealUserByThreeQuartersOfDamageDone",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Consider how much HP will be restored
|
||||
heal_amt = move.rough_damage * 0.75
|
||||
if heal_amt > user.totalhp * 0.3 # Only modify the score if it'll heal a decent amount
|
||||
# Things that affect healing caused by draining
|
||||
if target.has_active_ability?(:LIQUIDOOZE)
|
||||
score -= 20
|
||||
elsif user.battler.canHeal?
|
||||
heal_amt *= 1.3 if user.has_active_item?(:BIGROOT)
|
||||
heal_fraction = [user.totalhp - user.hp, heal_amt].min.to_f / user.totalhp
|
||||
score += 40 * heal_fraction * (user.totalhp - user.hp) / user.totalhp
|
||||
end
|
||||
else
|
||||
score -= 10 if target.has_active_ability?(:LIQUIDOOZE)
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("HealUserAndAlliesQuarterOfTotalHP",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next !target.battler.canHeal?
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HealUserAndAlliesQuarterOfTotalHP",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Consider how much HP will be restored
|
||||
if target.hp >= target.totalhp * 0.75
|
||||
score -= 5
|
||||
else
|
||||
score += 15 * (target.totalhp - target.hp) / target.totalhp
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("HealUserAndAlliesQuarterOfTotalHPCureStatus",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next !target.battler.canHeal? && target.status == :NONE
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HealUserAndAlliesQuarterOfTotalHPCureStatus",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Consider how much HP will be restored
|
||||
if target.hp >= target.totalhp * 0.75
|
||||
score -= 5
|
||||
else
|
||||
score += 15 * (target.totalhp - target.hp) / target.totalhp
|
||||
end
|
||||
# Check whether an existing status problem will be removed
|
||||
score += 10 if target.status != :NONE
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("HealTargetHalfOfTotalHP",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next !target.battler.canHeal?
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HealTargetHalfOfTotalHP",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next Battle::AI::MOVE_USELESS_SCORE if target.opposes?(user)
|
||||
# Consider how much HP will be restored
|
||||
heal_amt = target.totalhp / 2
|
||||
heal_amt = target.totalhp * 0.75 if move.move.pulseMove? &&
|
||||
user.has_active_ability?(:MEGALAUNCHER)
|
||||
if target.hp >= target.totalhp * 0.5
|
||||
score -= 10
|
||||
else
|
||||
heal_fraction = [target.totalhp - target.hp, heal_amt].min.to_f / target.totalhp
|
||||
score += 40 * heal_fraction * (target.totalhp - target.hp) / target.totalhp
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.copy("HealTargetHalfOfTotalHP",
|
||||
"HealTargetDependingOnGrassyTerrain")
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HealTargetDependingOnGrassyTerrain",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next Battle::AI::MOVE_USELESS_SCORE if user.opposes?(target)
|
||||
# Consider how much HP will be restored
|
||||
heal_amt = target.totalhp / 2
|
||||
heal_amt = (target.totalhp * 2 / 3.0).round if battle.field.terrain == :Grassy
|
||||
if target.hp >= target.totalhp * 0.5
|
||||
score -= 10
|
||||
else
|
||||
heal_fraction = [target.totalhp - target.hp, heal_amt].min.to_f / target.totalhp
|
||||
score += 40 * heal_fraction * (target.totalhp - target.hp) / target.totalhp
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("HealUserPositionNextTurn",
|
||||
proc { |move, user, ai, battle|
|
||||
next battle.positions[user.index].effects[PBEffects::Wish] > 0
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("HealUserPositionNextTurn",
|
||||
proc { |score, move, user, ai, battle|
|
||||
# Consider how much HP will be restored
|
||||
if user.hp >= user.totalhp * 0.5
|
||||
score -= 10
|
||||
else
|
||||
score += 15 * (user.totalhp - user.hp) / user.totalhp
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("StartHealUserEachTurn",
|
||||
proc { |move, user, ai, battle|
|
||||
next user.effects[PBEffects::AquaRing]
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartHealUserEachTurn",
|
||||
proc { |score, move, user, ai, battle|
|
||||
score += 10
|
||||
score += 10 if user.has_active_item?(:BIGROOT)
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("StartHealUserEachTurnTrapUserInBattle",
|
||||
proc { |move, user, ai, battle|
|
||||
next user.effects[PBEffects::Ingrain]
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartHealUserEachTurnTrapUserInBattle",
|
||||
proc { |score, move, user, ai, battle|
|
||||
score += 5
|
||||
score += 10 if user.turnCount < 2
|
||||
score += 10 if user.has_active_item?(:BIGROOT)
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("StartDamageTargetEachTurnIfTargetAsleep",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next !target.battler.asleep? || target.effects[PBEffects::Nightmare]
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("StartDamageTargetEachTurnIfTargetAsleep",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next Battle::AI::MOVE_USELESS_SCORE if target.statusCount <= 1
|
||||
next score + 10 * target.statusCount
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("StartLeechSeedTarget",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if target.effects[PBEffects::LeechSeed] >= 0
|
||||
next true if target.has_type?(:GRASS) || !target.battler.takesIndirectDamage?
|
||||
next false
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("StartLeechSeedTarget",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Prefer early on
|
||||
score += 10 if user.turnCount < 2
|
||||
if ai.trainer.medium_skill?
|
||||
# Prefer if the user has no damaging moves
|
||||
score += 20 if !user.check_for_move { |m| m.damagingMove? }
|
||||
# Prefer if the target can't switch out to remove its seeding
|
||||
score += 10 if !battle.pbCanChooseNonActive?(target.index)
|
||||
# Don't prefer if the leeched HP will hurt the user
|
||||
score -= 20 if target.has_active_ability?([:LIQUIDOOZE])
|
||||
end
|
||||
if ai.trainer.high_skill?
|
||||
# Prefer if user can stall while damage is dealt
|
||||
if user.check_for_move { |m| m.is_a?(Battle::Move::ProtectMove) }
|
||||
score += 15
|
||||
end
|
||||
# Don't prefer if target can remove the seed
|
||||
if target.check_for_move { |m| m.is_a?(Battle::Move::RemoveUserBindingAndEntryHazards) }
|
||||
score -= 15
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UserLosesHalfOfTotalHP",
|
||||
proc { |score, move, user, ai, battle|
|
||||
score -= 15 # User will lose 50% HP, don't prefer this move
|
||||
if ai.trainer.medium_skill?
|
||||
score += 10 if user.hp >= user.totalhp * 0.75 # User at 75% HP or more
|
||||
score += 10 if user.hp <= user.totalhp * 0.25 # User at 25% HP or less
|
||||
end
|
||||
if ai.trainer.high_skill?
|
||||
reserves = battle.pbAbleNonActiveCount(user.idxOwnSide)
|
||||
foes = battle.pbAbleNonActiveCount(user.idxOpposingSide)
|
||||
if reserves == 0 # AI is down to its last Pokémon
|
||||
score += 30 # => Go out with a bang
|
||||
elsif foes == 0 # Foe is down to their last Pokémon, AI has reserves
|
||||
score += 20 # => Go for the kill
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("UserLosesHalfOfTotalHPExplosive",
|
||||
proc { |move, user, ai, battle|
|
||||
next !battle.moldBreaker && battle.pbCheckGlobalAbility(:DAMP)
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.copy("UserLosesHalfOfTotalHP",
|
||||
"UserLosesHalfOfTotalHPExplosive")
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.copy("UserLosesHalfOfTotalHPExplosive",
|
||||
"UserFaintsExplosive")
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UserFaintsExplosive",
|
||||
proc { |score, move, user, ai, battle|
|
||||
score -= 25 # User will faint, don't prefer this move
|
||||
if ai.trainer.medium_skill?
|
||||
score -= 10 if user.hp >= user.totalhp * 0.5 # User at 50% HP or more
|
||||
score += 10 if user.hp <= user.totalhp * 0.25 # User at 25% HP or less
|
||||
end
|
||||
if ai.trainer.high_skill?
|
||||
reserves = battle.pbAbleNonActiveCount(user.idxOwnSide)
|
||||
foes = battle.pbAbleNonActiveCount(user.idxOpposingSide)
|
||||
if reserves == 0 # AI is down to its last Pokémon
|
||||
score += 30 # => Go out with a bang
|
||||
elsif foes == 0 # Foe is down to their last Pokémon, AI has reserves
|
||||
score += 20 # => Go for the kill
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.copy("UserFaintsExplosive",
|
||||
"UserFaintsPowersUpInMistyTerrainExplosive")
|
||||
Battle::AI::Handlers::MoveBasePower.add("UserFaintsPowersUpInMistyTerrainExplosive",
|
||||
proc { |power, move, user, target, ai, battle|
|
||||
power = power * 3 / 2 if battle.field.terrain == :Misty
|
||||
next power
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.copy("UserFaintsExplosive",
|
||||
"UserFaintsPowersUpInMistyTerrainExplosive")
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveBasePower.add("UserFaintsFixedDamageUserHP",
|
||||
proc { |power, move, user, target, ai, battle|
|
||||
next user.hp
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.copy("UserFaintsExplosive",
|
||||
"UserFaintsFixedDamageUserHP")
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserFaintsLowerTargetAtkSpAtk2",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
score -= 25 # User will faint, don't prefer this move
|
||||
# Check the impact of lowering the target's stats
|
||||
score = ai.get_score_for_target_stat_drop(score, target, move.move.statDown)
|
||||
next score if score == Battle::AI::MOVE_USELESS_SCORE
|
||||
# Score for the user fainting
|
||||
if ai.trainer.medium_skill?
|
||||
score -= 10 if user.hp >= user.totalhp * 0.5 # User at 50% HP or more
|
||||
score += 10 if user.hp <= user.totalhp * 0.25 # User at 25% HP or less
|
||||
end
|
||||
if ai.trainer.high_skill?
|
||||
reserves = battle.pbAbleNonActiveCount(user.idxOwnSide)
|
||||
foes = battle.pbAbleNonActiveCount(user.idxOpposingSide)
|
||||
if reserves > 0 && foes == 0 # Foe is down to their last Pokémon, AI has reserves
|
||||
score += 20 # => Can afford to lose this Pokémon
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("UserFaintsHealAndCureReplacement",
|
||||
proc { |move, user, ai, battle|
|
||||
next !battle.pbCanChooseNonActive?(user.index)
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UserFaintsHealAndCureReplacement",
|
||||
proc { |score, move, user, ai, battle|
|
||||
score -= 25 # User will faint, don't prefer this move
|
||||
# Check whether the replacement user needs healing, and don't make the below
|
||||
# calculations if not
|
||||
if ai.trainer.medium_skill?
|
||||
need_healing = false
|
||||
battle.eachInTeamFromBattlerIndex(user.index) do |pkmn, party_index|
|
||||
next if pkmn.hp >= pkmn.totalhp * 0.75 && pkmn.status == :NONE
|
||||
need_healing = true
|
||||
break
|
||||
end
|
||||
next Battle::AI::MOVE_USELESS_SCORE if !need_healing
|
||||
end
|
||||
if ai.trainer.medium_skill?
|
||||
score -= 10 if user.hp >= user.totalhp * 0.5 # User at 50% HP or more
|
||||
score += 10 if user.hp <= user.totalhp * 0.25 # User at 25% HP or less
|
||||
end
|
||||
if ai.trainer.high_skill?
|
||||
reserves = battle.pbAbleNonActiveCount(user.idxOwnSide)
|
||||
foes = battle.pbAbleNonActiveCount(user.idxOpposingSide)
|
||||
if reserves > 0 && foes == 0 # Foe is down to their last Pokémon, AI has reserves
|
||||
score += 20 # => Can afford to lose this Pokémon
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.copy("UserFaintsHealAndCureReplacement",
|
||||
"UserFaintsHealAndCureReplacementRestorePP")
|
||||
Battle::AI::Handlers::MoveEffectScore.copy("UserFaintsHealAndCureReplacement",
|
||||
"UserFaintsHealAndCureReplacementRestorePP")
|
||||
|
||||
#===============================================================================
|
||||
# TODO: This code should be for a single battler (each is checked in turn).
|
||||
# Should have a MoveEffectAgainstTargetScore instead.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("StartPerishCountsForAllBattlers",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if target.effects[PBEffects::PerishSong] > 0
|
||||
next true if Battle::AbilityEffects.triggerMoveImmunity(target.ability, user.battler, target.battler,
|
||||
move.move, move.rough_type, battle, false)
|
||||
next false
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartPerishCountsForAllBattlers",
|
||||
proc { |score, move, user, ai, battle|
|
||||
score -= 15
|
||||
# Check which battlers will be affected by this move
|
||||
if ai.trainer.medium_skill?
|
||||
allies_affected = 0
|
||||
foes_affected = 0
|
||||
foes_with_high_hp = 0
|
||||
battle.allBattlers.each do |b|
|
||||
next if b.effects[PBEffects::PerishSong] > 0
|
||||
next if Battle::AbilityEffects.triggerMoveImmunity(b.ability, user.battler, b,
|
||||
move.move, move.rough_type, battle, false)
|
||||
if b.opposes?(user.index)
|
||||
foes_affected += 1
|
||||
foes_with_high_hp += 1 if b.hp >= b.totalhp * 0.75
|
||||
else
|
||||
allies_affected += 1
|
||||
end
|
||||
end
|
||||
next Battle::AI::MOVE_USELESS_SCORE if foes_affected == 0
|
||||
score += 15 if allies_affected == 0 # No downside for user; cancel out inherent negative score
|
||||
score += 15 * (foes_affected - allies_affected)
|
||||
score += 5 * foes_with_high_hp
|
||||
end
|
||||
if ai.trainer.high_skill?
|
||||
reserves = battle.pbAbleNonActiveCount(user.idxOwnSide)
|
||||
foes = battle.pbAbleNonActiveCount(user.idxOpposingSide)
|
||||
if foes == 0 # Foe is down to their last Pokémon, can't lose Perish count
|
||||
score += 30 # => Want to auto-win in 3 turns
|
||||
elsif reserves == 0 # AI is down to its last Pokémon, can't lose Perish count
|
||||
score -= 20 # => Don't want to auto-lose in 3 turns
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("AttackerFaintsIfUserFaints",
|
||||
proc { |move, user, ai, battle|
|
||||
next Settings::MECHANICS_GENERATION >= 7 && user.effects[PBEffects::DestinyBondPrevious]
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("AttackerFaintsIfUserFaints",
|
||||
proc { |score, move, user, ai, battle|
|
||||
score -= 25
|
||||
# Check whether user is faster than its foe(s) and could use this move
|
||||
user_faster_count = 0
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
user_faster_count += 1 if user.faster_than?(b)
|
||||
end
|
||||
next score if user_faster_count == 0 # Move will almost certainly have no effect
|
||||
score += 5 * user_faster_count
|
||||
# Prefer this move at lower user HP
|
||||
if ai.trainer.medium_skill?
|
||||
score += 20 if user.hp <= user.totalhp * 0.4
|
||||
score += 10 if user.hp <= user.totalhp * 0.25
|
||||
score += 15 if user.hp <= user.totalhp * 0.1
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("SetAttackerMovePPTo0IfUserFaints",
|
||||
proc { |score, move, user, ai, battle|
|
||||
score -= 25
|
||||
# Check whether user is faster than its foe(s) and could use this move
|
||||
user_faster_count = 0
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
user_faster_count += 1 if user.faster_than?(b)
|
||||
end
|
||||
next score if user_faster_count == 0 # Move will almost certainly have no effect
|
||||
score += 5 * user_faster_count
|
||||
# Prefer this move at lower user HP (not as preferred as Destiny Bond, though)
|
||||
if ai.trainer.medium_skill?
|
||||
score += 15 if user.hp <= user.totalhp * 0.4
|
||||
score += 10 if user.hp <= user.totalhp * 0.25
|
||||
score += 10 if user.hp <= user.totalhp * 0.1
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -1,325 +0,0 @@
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTakesTargetItem",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score if user.wild? || user.item
|
||||
next score if !target.item || target.battler.unlosableItem?(target.item)
|
||||
next score if user.battler.unlosableItem?(target.item)
|
||||
next score if target.effects[PBEffects::Substitute] > 0
|
||||
next score if target.has_active_ability?(:STICKYHOLD) && !battle.moldBreaker
|
||||
# User can steal the target's item; score it
|
||||
user_item_preference = ai.battler_wants_item?(user, target.item_id)
|
||||
user_no_item_preference = ai.battler_wants_item?(user, :NONE)
|
||||
target_item_preference = ai.battler_wants_item?(target, target.item_id)
|
||||
target_no_item_preference = ai.battler_wants_item?(target, :NONE)
|
||||
score += (user_item_preference - user_no_item_preference) * 3
|
||||
score += (target_item_preference - target_no_item_preference) * 3
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("TargetTakesUserItem",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if !user.item || user.battler.unlosableItem?(user.item)
|
||||
next true if target.item || target.battler.unlosableItem?(user.item)
|
||||
next false
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TargetTakesUserItem",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
user_item_preference = ai.battler_wants_item?(user, user.item_id)
|
||||
user_no_item_preference = ai.battler_wants_item?(user, :NONE)
|
||||
target_item_preference = ai.battler_wants_item?(target, user.item_id)
|
||||
target_no_item_preference = ai.battler_wants_item?(target, :NONE)
|
||||
score -= (user_item_preference - user_no_item_preference) * 3
|
||||
score -= (target_item_preference - target_no_item_preference) * 3
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("UserTargetSwapItems",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if user.wild?
|
||||
next true if !user.item && !target.item
|
||||
next true if user.battler.unlosableItem?(user.item) || user.battler.unlosableItem?(target.item)
|
||||
next true if target.battler.unlosableItem?(target.item) || target.battler.unlosableItem?(user.item)
|
||||
next true if target.has_active_ability?(:STICKYHOLD) && !battle.moldBreaker
|
||||
next false
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTargetSwapItems",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
user_new_item_preference = ai.battler_wants_item?(user, target.item_id)
|
||||
user_old_item_preference = ai.battler_wants_item?(user, user.item_id)
|
||||
target_new_item_preference = ai.battler_wants_item?(target, user.item_id)
|
||||
target_old_item_preference = ai.battler_wants_item?(target, target.item_id)
|
||||
score += (user_new_item_preference - user_old_item_preference) * 3
|
||||
score -= (target_new_item_preference - target_old_item_preference) * 3
|
||||
# Don't prefer if user used this move in the last round
|
||||
score -= 15 if user.battler.lastMoveUsed &&
|
||||
GameData::Move.get(user.battler.lastMoveUsed).function_code == "UserTargetSwapItems"
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("RestoreUserConsumedItem",
|
||||
proc { |move, user, ai, battle|
|
||||
next !user.battler.recycleItem || user.item
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("RestoreUserConsumedItem",
|
||||
proc { |score, move, user, ai, battle|
|
||||
user_new_item_preference = ai.battler_wants_item?(user, user.battler.recycleItem)
|
||||
user_old_item_preference = ai.battler_wants_item?(user, user.item_id)
|
||||
score += (user_new_item_preference - user_old_item_preference) * 4
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveBasePower.add("RemoveTargetItem",
|
||||
proc { |power, move, user, target, ai, battle|
|
||||
next move.move.pbBaseDamage(power, user.battler, target.battler)
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RemoveTargetItem",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score if user.wild?
|
||||
next score if !target.item || target.battler.unlosableItem?(target.item)
|
||||
next score if target.effects[PBEffects::Substitute] > 0
|
||||
next score if target.has_active_ability?(:STICKYHOLD) && !battle.moldBreaker
|
||||
# User can knock off the target's item; score it
|
||||
target_item_preference = ai.battler_wants_item?(target, target.item_id)
|
||||
target_no_item_preference = ai.battler_wants_item?(target, :NONE)
|
||||
score += (target_item_preference - target_no_item_preference) * 4
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DestroyTargetBerryOrGem",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score if !target.item || (!target.item.is_berry? &&
|
||||
!(Settings::MECHANICS_GENERATION >= 6 && target.item.is_gem?))
|
||||
next score if user.battler.unlosableItem?(target.item)
|
||||
next score if target.effects[PBEffects::Substitute] > 0
|
||||
next score if target.has_active_ability?(:STICKYHOLD) && !battle.moldBreaker
|
||||
# User can incinerate the target's item; score it
|
||||
target_item_preference = ai.battler_wants_item?(target, target.item_id)
|
||||
target_no_item_preference = ai.battler_wants_item?(target, :NONE)
|
||||
score += (target_item_preference - target_no_item_preference) * 4
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("CorrodeTargetItem",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if !target.item || target.unlosableItem?(target.item) ||
|
||||
target.effects[PBEffects::Substitute] > 0
|
||||
next true if target.has_active_ability?(:STICKYHOLD)
|
||||
next true if battle.corrosiveGas[target.index % 2][target.party_index]
|
||||
next false
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("CorrodeTargetItem",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
target_item_preference = ai.battler_wants_item?(target, target.item_id)
|
||||
target_no_item_preference = ai.battler_wants_item?(target, :NONE)
|
||||
score += (target_item_preference - target_no_item_preference) * 4
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("StartTargetCannotUseItem",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next target.effects[PBEffects::Embargo] > 0
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("StartTargetCannotUseItem",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next Battle::AI::MOVE_USELESS_SCORE if !target.item || !target.item_active?
|
||||
item_score = ai.battler_wants_item?(target, target.item_id)
|
||||
score += item_score * 5
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# 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 score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("UserConsumeBerryRaiseDefense2",
|
||||
proc { |move, user, ai, battle|
|
||||
item = user.item
|
||||
next !item || !item.is_berry? || !user.item_active?
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UserConsumeBerryRaiseDefense2",
|
||||
proc { |score, move, user, ai, battle|
|
||||
# Score for raising the user's stat
|
||||
score = Battle::AI::Handlers.apply_move_effect_score("RaiseUserDefense2",
|
||||
score, move, user, ai, battle)
|
||||
# Score for the consumed berry's effect
|
||||
score += ai.get_score_change_for_consuming_item(user, user.item_id)
|
||||
# Score for other results of consuming the berry
|
||||
if ai.trainer.medium_skill?
|
||||
# Prefer if user will heal itself with Cheek Pouch
|
||||
score += 5 if user.battler.canHeal? && user.hp < user.totalhp / 2 &&
|
||||
user.has_active_ability?(:CHEEKPOUCH)
|
||||
# Prefer if target can recover the consumed berry
|
||||
score += 8 if user.has_active_ability?(:HARVEST) ||
|
||||
user.has_move_with_function?("RestoreUserConsumedItem")
|
||||
# Prefer if user couldn't normally consume the berry
|
||||
score += 4 if !user.battler.canConsumeBerry?
|
||||
# Prefer if user will become able to use Belch
|
||||
score += 4 if !user.battler.belched? && user.has_move_with_function?("FailsIfUserNotConsumedBerry")
|
||||
# Prefer if user will benefit from not having an item
|
||||
score += 5 if user.has_active_ability?(:UNBURDEN)
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("AllBattlersConsumeBerry",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next !target.item || !target.item.is_berry? || target.battler.semiInvulnerable?
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("AllBattlersConsumeBerry",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Score for the consumed berry's effect
|
||||
score_change = ai.get_score_change_for_consuming_item(target, target.item_id)
|
||||
# Score for other results of consuming the berry
|
||||
if ai.trainer.medium_skill?
|
||||
# Prefer if target will heal itself with Cheek Pouch
|
||||
score_change += 5 if target.battler.canHeal? && target.hp < target.totalhp / 2 &&
|
||||
target.has_active_ability?(:CHEEKPOUCH)
|
||||
# Prefer if target can recover the consumed berry
|
||||
score_change += 8 if target.has_active_ability?(:HARVEST) ||
|
||||
target.has_move_with_function?("RestoreUserConsumedItem")
|
||||
# Prefer if target couldn't normally consume the berry
|
||||
score_change += 4 if !target.battler.canConsumeBerry?
|
||||
# Prefer if target will become able to use Belch
|
||||
score += 4 if !target.battler.belched? && target.has_move_with_function?("FailsIfUserNotConsumedBerry")
|
||||
# Prefer if target will benefit from not having an item
|
||||
score += 5 if target.has_active_ability?(:UNBURDEN)
|
||||
end
|
||||
score += (target.opposes?(user)) ? -score_change : score_change
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserConsumeTargetBerry",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score if !target.item || !target.item.is_berry?
|
||||
next score if user.battler.unlosableItem?(target.item)
|
||||
next score if target.effects[PBEffects::Substitute] > 0
|
||||
next score if target.has_active_ability?(:STICKYHOLD) && !battle.moldBreaker
|
||||
# Score the user gaining the item's effect
|
||||
score += ai.get_score_change_for_consuming_item(user, target.item_id)
|
||||
# Score for other results of consuming the berry
|
||||
if ai.trainer.medium_skill?
|
||||
# Prefer if user will heal itself with Cheek Pouch
|
||||
score += 5 if user.battler.canHeal? && user.hp < user.totalhp / 2 &&
|
||||
user.has_active_ability?(:CHEEKPOUCH)
|
||||
# Prefer if user will become able to use Belch
|
||||
score += 4 if !user.battler.belched? && user.has_move_with_function?("FailsIfUserNotConsumedBerry")
|
||||
# Don't prefer if target will benefit from not having an item
|
||||
score -= 5 if target.has_active_ability?(:UNBURDEN)
|
||||
end
|
||||
# Score the target no longer having the item
|
||||
target_item_preference = ai.battler_wants_item?(target, target.item_id)
|
||||
target_no_item_preference = ai.battler_wants_item?(target, :NONE)
|
||||
score += (target_item_preference - target_no_item_preference) * 4
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("ThrowUserItemAtTarget",
|
||||
proc { |move, user, ai, battle|
|
||||
item = user.item
|
||||
next true if !item || !user.item_active? || user.battler.unlosableItem?(item)
|
||||
next true if item.is_berry? && !user.battler.canConsumeBerry?
|
||||
next true if item.flags.none? { |f| f[/^Fling_/i] }
|
||||
next false
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveBasePower.add("ThrowUserItemAtTarget",
|
||||
proc { |power, move, user, target, ai, battle|
|
||||
next move.move.pbBaseDamage(power, user.battler, target.battler)
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("ThrowUserItemAtTarget",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
case user.item_id
|
||||
when :POISONBARB, :TOXICORB
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("PoisonTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
when :FLAMEORB
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("BurnTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
when :LIGHTBALL
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("ParalyzeTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
when :KINGSROCK, :RAZORFANG
|
||||
score = Battle::AI::Handlers.apply_move_effect_against_target_score("FlinchTarget",
|
||||
score, move, user, target, ai, battle)
|
||||
else
|
||||
score -= ai.get_score_change_for_consuming_item(target, user.item_id)
|
||||
end
|
||||
# Score for other results of consuming the berry
|
||||
if ai.trainer.medium_skill?
|
||||
# Don't prefer if target will become able to use Belch
|
||||
score -= 4 if user.item.is_berry? && !target.battler.belched? &&
|
||||
target.has_move_with_function?("FailsIfUserNotConsumedBerry")
|
||||
# Prefer if user will benefit from not having an item
|
||||
score += 5 if user.has_active_ability?(:UNBURDEN)
|
||||
end
|
||||
# Prefer if the user doesn't want its held item/don't prefer if it wants to
|
||||
# keep its held item
|
||||
user_item_preference = ai.battler_wants_item?(user, user.item_id)
|
||||
user_no_item_preference = ai.battler_wants_item?(user, :NONE)
|
||||
score += (user_item_preference - user_no_item_preference) * 4
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -1,619 +0,0 @@
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("RedirectAllMovesToUser",
|
||||
proc { |score, move, user, ai, battle|
|
||||
# Useless if there is no ally to redirect attacks from
|
||||
next Battle::AI::MOVE_USELESS_SCORE if user.battler.allAllies.length == 0
|
||||
# Prefer if ally is at low HP and user is at high HP
|
||||
if user.hp > user.totalhp * 2 / 3
|
||||
ai.each_ally(user.index) do |b, i|
|
||||
score += 10 if b.hp <= b.totalhp / 3
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RedirectAllMovesToTarget",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if target.opposes?(user)
|
||||
# Useless if target is a foe but there is only one foe
|
||||
next Battle::AI::MOVE_USELESS_SCORE if target.battler.allAllies.length == 0
|
||||
# Useless if there is no ally to attack the spotlighted foe
|
||||
next Battle::AI::MOVE_USELESS_SCORE if user.battler.allAllies.length == 0
|
||||
end
|
||||
# Generaly don't prefer this move, as it's a waste of the user's turn
|
||||
next score - 15
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# CannotBeRedirected
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveBasePower.add("RandomlyDamageOrHealTarget",
|
||||
proc { |power, move, user, target, ai, battle|
|
||||
next 50 # Average power, ish
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("RandomlyDamageOrHealTarget",
|
||||
proc { |score, move, user, ai, battle|
|
||||
# Generaly don't prefer this move, as it may heal the target instead
|
||||
next score - 8
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("HealAllyOrDamageFoe",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next !target.opposes?(user) && target.battler.canHeal?
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HealAllyOrDamageFoe",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if !target.opposes?(user)
|
||||
# Consider how much HP will be restored
|
||||
if target.hp >= target.totalhp * 0.5
|
||||
score -= 10
|
||||
else
|
||||
score += 20 * (target.totalhp - target.hp) / target.totalhp
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("CurseTargetOrLowerUserSpd1RaiseUserAtkDef1",
|
||||
proc { |move, user, ai, battle|
|
||||
next false if user.has_type?(:GHOST)
|
||||
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
|
||||
(move.move.statDown.length / 2).times do |i|
|
||||
next if !user.battler.pbCanLowerStatStage?(move.move.statDown[i * 2], user.battler, move.move)
|
||||
will_fail = false
|
||||
break
|
||||
end
|
||||
next will_fail
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("CurseTargetOrLowerUserSpd1RaiseUserAtkDef1",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next false if !user.has_type?(:GHOST)
|
||||
next true if target.effects[PBEffects::Curse] || !target.battler.takesIndirectDamage?
|
||||
next false
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("CurseTargetOrLowerUserSpd1RaiseUserAtkDef1",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next score if user.has_type?(:GHOST)
|
||||
score = ai.get_score_for_target_stat_raise(score, user, move.move.statUp)
|
||||
next score if score == Battle::AI::MOVE_USELESS_SCORE
|
||||
next ai.get_score_for_target_stat_drop(score, user, move.move.statDown, false)
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("CurseTargetOrLowerUserSpd1RaiseUserAtkDef1",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score if !user.has_type?(:GHOST)
|
||||
# Don't prefer if user will faint because of using this move
|
||||
next Battle::AI::MOVE_USELESS_SCORE if user.hp <= user.totalhp / 2
|
||||
# Prefer early on
|
||||
score += 10 if user.turnCount < 2
|
||||
if ai.trainer.medium_skill?
|
||||
# Prefer if the user has no damaging moves
|
||||
score += 20 if !user.check_for_move { |m| m.damagingMove? }
|
||||
# Prefer if the target can't switch out to remove its curse
|
||||
score += 10 if !battle.pbCanChooseNonActive?(target.index)
|
||||
end
|
||||
if ai.trainer.high_skill?
|
||||
# Prefer if user can stall while damage is dealt
|
||||
if user.check_for_move { |m| m.is_a?(Battle::Move::ProtectMove) }
|
||||
score += 8
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("EffectDependsOnEnvironment",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Determine this move's effect
|
||||
move.move.pbOnStartUse(user.battler, [target.battler])
|
||||
function_code = nil
|
||||
case move.move.secretPower
|
||||
when 2
|
||||
function_code = "SleepTarget"
|
||||
when 10
|
||||
function_code = "BurnTarget"
|
||||
when 0, 1
|
||||
function_code = "ParalyzeTarget"
|
||||
when 9
|
||||
function_code = "FreezeTarget"
|
||||
when 5
|
||||
function_code = "LowerTargetAttack1"
|
||||
when 14
|
||||
function_code = "LowerTargetDefense1"
|
||||
when 3
|
||||
function_code = "LowerTargetSpAtk1"
|
||||
when 4, 6, 12
|
||||
function_code = "LowerTargetSpeed1"
|
||||
when 8
|
||||
function_code = "LowerTargetAccuracy1"
|
||||
when 7, 11, 13
|
||||
function_code = "FlinchTarget"
|
||||
end
|
||||
if function_code
|
||||
next Battle::AI::Handlers.apply_move_effect_against_target_score(function_code,
|
||||
score, move, user, target, ai, battle)
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveBasePower.add("HitsAllFoesAndPowersUpInPsychicTerrain",
|
||||
proc { |power, move, user, target, ai, battle|
|
||||
next move.move.pbBaseDamage(power, user.battler, target.battler)
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("TargetNextFireMoveDamagesTarget",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next target.effects[PBEffects::Powder]
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TargetNextFireMoveDamagesTarget",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Effect wears off at the end of the round
|
||||
next Battle::AI::MOVE_USELESS_SCORE if target.faster_than?(user)
|
||||
# Prefer if target knows any Fire moves (moreso if that's the only type they know)
|
||||
if target.check_for_move { |m| m.pbCalcType(b.battler) == :FIRE }
|
||||
score += 10
|
||||
score += 10 if !target.check_for_move { |m| m.pbCalcType(b.battler) != :FIRE }
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
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.has_move_with_function?("DoublePowerAfterFusionBolt")
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
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.has_move_with_function?("DoublePowerAfterFusionFlare")
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("PowerUpAllyMove",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next target.effects[PBEffects::HelpingHand]
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("PowerUpAllyMove",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next Battle::AI::MOVE_USELESS_SCORE if !target.check_for_move { |m| m.damagingMove? }
|
||||
next score + 4
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveBasePower.add("CounterPhysicalDamage",
|
||||
proc { |power, move, user, target, ai, battle|
|
||||
next 60 # Representative value
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("CounterPhysicalDamage",
|
||||
proc { |score, move, user, ai, battle|
|
||||
has_physical_move = false
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
next if !b.can_attack?
|
||||
next if !b.check_for_move { |m| m.physicalMove?(m.type) &&
|
||||
(user.effects[PBEffects::Substitute] == 0 ||
|
||||
m.ignoresSubstitute?(b.battler)) }
|
||||
has_physical_move = true
|
||||
# Prefer if foe has a higher Attack than Special Attack
|
||||
score += 5 if b.rough_stat(:ATTACK) > b.rough_stat(:SPECIAL_ATTACK)
|
||||
# Prefer if the last move the foe used was physical
|
||||
if ai.trainer.medium_skill? && b.battler.lastMoveUsed
|
||||
score += 5 if GameData::Move.get(b.battler.lastMoveUsed).physical?
|
||||
end
|
||||
# Prefer if the foe is taunted into using a damaging move
|
||||
score += 4 if b.effects[PBEffects::Taunt] > 0
|
||||
end
|
||||
# Useless if no foes have a physical move to counter
|
||||
next Battle::AI::MOVE_USELESS_SCORE if !has_physical_move
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveBasePower.add("CounterSpecialDamage",
|
||||
proc { |power, move, user, target, ai, battle|
|
||||
next 60 # Representative value
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("CounterSpecialDamage",
|
||||
proc { |score, move, user, ai, battle|
|
||||
has_special_move = false
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
next if !b.can_attack?
|
||||
next if !b.check_for_move { |m| m.specialMove?(m.type) &&
|
||||
(user.effects[PBEffects::Substitute] == 0 ||
|
||||
m.ignoresSubstitute?(b.battler)) }
|
||||
has_special_move = true
|
||||
# Prefer if foe has a higher Special Attack than Attack
|
||||
score += 5 if b.rough_stat(:SPECIAL_ATTACK) > b.rough_stat(:ATTACK)
|
||||
# Prefer if the last move the foe used was special
|
||||
if ai.trainer.medium_skill? && b.battler.lastMoveUsed
|
||||
score += 5 if GameData::Move.get(b.battler.lastMoveUsed).special?
|
||||
end
|
||||
# Prefer if the foe is taunted into using a damaging move
|
||||
score += 4 if b.effects[PBEffects::Taunt] > 0
|
||||
end
|
||||
# Useless if no foes have a special move to counter
|
||||
next Battle::AI::MOVE_USELESS_SCORE if !has_special_move
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveBasePower.add("CounterDamagePlusHalf",
|
||||
proc { |power, move, user, target, ai, battle|
|
||||
next 60 # Representative value
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("CounterDamagePlusHalf",
|
||||
proc { |score, move, user, ai, battle|
|
||||
has_damaging_move = false
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
next if !b.can_attack? || user.faster_than?(b)
|
||||
next if !b.check_for_move { |m| m.damagingMove? &&
|
||||
(user.effects[PBEffects::Substitute] == 0 ||
|
||||
m.ignoresSubstitute?(b.battler)) }
|
||||
has_damaging_move = true
|
||||
# Prefer if the last move the foe used was damaging
|
||||
if ai.trainer.medium_skill? && b.battler.lastMoveUsed
|
||||
score += 5 if GameData::Move.get(b.battler.lastMoveUsed).damaging?
|
||||
end
|
||||
# Prefer if the foe is taunted into using a damaging move
|
||||
score += 6 if b.effects[PBEffects::Taunt] > 0
|
||||
end
|
||||
# Useless if no foes have a damaging move to counter
|
||||
next Battle::AI::MOVE_USELESS_SCORE if !has_damaging_move
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("UserAddStockpileRaiseDefSpDef1",
|
||||
proc { |move, user, ai, battle|
|
||||
next user.effects[PBEffects::Stockpile] >= 3
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UserAddStockpileRaiseDefSpDef1",
|
||||
proc { |score, move, user, ai, battle|
|
||||
score = ai.get_score_for_target_stat_raise(score, user, [:DEFENSE, 1, :SPECIAL_DEFENSE, 1], false)
|
||||
# More preferable if user also has Spit Up/Swallow
|
||||
if user.battler.pbHasMoveFunction?("PowerDependsOnUserStockpile",
|
||||
"HealUserDependingOnUserStockpile")
|
||||
score += [10, 8, 5, 3][user.effects[PBEffects::Stockpile]]
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# NOTE: Don't worry about the stat drops caused by losing the stockpile, because
|
||||
# if these moves are known, they want to be used.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("PowerDependsOnUserStockpile",
|
||||
proc { |move, user, ai, battle|
|
||||
next user.effects[PBEffects::Stockpile] == 0
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveBasePower.add("PowerDependsOnUserStockpile",
|
||||
proc { |power, move, user, target, ai, battle|
|
||||
next move.move.pbBaseDamage(power, user.battler, target.battler)
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("PowerDependsOnUserStockpile",
|
||||
proc { |score, move, user, ai, battle|
|
||||
# Slightly prefer to hold out for another Stockpile to make this move stronger
|
||||
score -= 5 if user.effects[PBEffects::Stockpile] < 2
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# NOTE: Don't worry about the stat drops caused by losing the stockpile, because
|
||||
# if these moves are known, they want to be used.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("HealUserDependingOnUserStockpile",
|
||||
proc { |move, user, ai, battle|
|
||||
next true if user.effects[PBEffects::Stockpile] == 0
|
||||
next true if !user.battler.canHeal? &&
|
||||
user.effects[PBEffects::StockpileDef] == 0 &&
|
||||
user.effects[PBEffects::StockpileSpDef] == 0
|
||||
next false
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("HealUserDependingOnUserStockpile",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next Battle::AI::MOVE_USELESS_SCORE if !user.battler.canHeal?
|
||||
# Consider how much HP will be restored
|
||||
if user.hp >= user.totalhp * 0.5
|
||||
score -= 10
|
||||
else
|
||||
# Slightly prefer to hold out for another Stockpile to make this move stronger
|
||||
score -= 5 if user.effects[PBEffects::Stockpile] < 2
|
||||
score += 20 * (user.totalhp - user.hp) / user.totalhp
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
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.has_move_with_function?("FirePledge", "WaterPledge")
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
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.has_move_with_function?("GrassPledge", "WaterPledge")
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
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.has_move_with_function?("GrassPledge", "FirePledge")
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# NOTE: The move that this move will become is determined in def
|
||||
# set_up_move_check, and the score for that move is calculated instead. If
|
||||
# this move cannot become another move and will fail, the score for this
|
||||
# move is calculated as normal (and the code below says it fails).
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("UseLastMoveUsed",
|
||||
proc { |move, user, ai, battle|
|
||||
next true if !battle.lastMoveUsed
|
||||
next move.move.moveBlacklist.include?(GameData::Move.get(battle.lastMoveUsed).function_code)
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# NOTE: The move that this move will become is determined in def
|
||||
# set_up_move_check, and the score for that move is calculated instead. If
|
||||
# this move cannot become another move and will fail, the score for this
|
||||
# move is calculated as normal (and the code below says it fails).
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("UseLastMoveUsedByTarget",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if !target.battler.lastRegularMoveUsed
|
||||
next GameData::Move.get(target.battler.lastRegularMoveUsed).flags.none? { |f| f[/^CanMirrorMove$/i] }
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("UseMoveTargetIsAboutToUse",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next !target.check_for_move { |m| m.damagingMove? && !move.move.moveBlacklist.include?(m.function_code) }
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UseMoveTargetIsAboutToUse",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next Battle::AI::MOVE_USELESS_SCORE if target.faster_than?(user)
|
||||
# Don't prefer if target knows any moves that can't be copied
|
||||
if target.check_for_move { |m| m.statusMove? || move.move.moveBlacklist.include?(m.function_code) }
|
||||
score -= 8
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# NOTE: The move that this move will become is determined in def
|
||||
# set_up_move_check, and the score for that move is calculated instead.
|
||||
#===============================================================================
|
||||
# UseMoveDependingOnEnvironment
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# UseRandomMove
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("UseRandomMoveFromUserParty",
|
||||
proc { |move, user, ai, battle|
|
||||
will_fail = true
|
||||
battle.pbParty(user.index).each_with_index do |pkmn, i|
|
||||
next if !pkmn || i == user.party_index
|
||||
next if Settings::MECHANICS_GENERATION >= 6 && pkmn.egg?
|
||||
pkmn.moves.each do |pkmn_move|
|
||||
next if move.move.moveBlacklist.include?(pkmn_move.function_code)
|
||||
next if pkmn_move.type == :SHADOW
|
||||
will_fail = false
|
||||
break
|
||||
end
|
||||
break if !will_fail
|
||||
end
|
||||
next will_fail
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("UseRandomUserMoveIfAsleep",
|
||||
proc { |move, user, ai, battle|
|
||||
will_fail = true
|
||||
user.battler.eachMoveWithIndex do |m, i|
|
||||
next if move.move.moveBlacklist.include?(m.function)
|
||||
next if !battle.pbCanChooseMove?(user.index, i, false, true)
|
||||
will_fail = false
|
||||
break
|
||||
end
|
||||
next will_fail
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("BounceBackProblemCausingStatusMoves",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next Battle::AI::MOVE_USELESS_SCORE if user.has_active_ability?(:MAGICBOUNCE)
|
||||
useless = true
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
next if !b.can_attack?
|
||||
next if !b.check_for_move { |m| m.statusMove? && m.canMagicCoat? }
|
||||
score += 4
|
||||
useless = false
|
||||
end
|
||||
next Battle::AI::MOVE_USELESS_SCORE if useless
|
||||
# Don't prefer the lower the user's HP is (better to try something else)
|
||||
if user.hp < user.totalhp / 2
|
||||
score -= 20 * (0.75 - (user.hp.to_f / user.totalhp)) # -5 to -15
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StealAndUseBeneficialStatusMove",
|
||||
proc { |score, move, user, ai, battle|
|
||||
useless = true
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
next if !b.can_attack?
|
||||
next if !b.check_for_move { |m| m.statusMove? && m.canSnatch? }
|
||||
score += 4
|
||||
useless = false
|
||||
end
|
||||
next Battle::AI::MOVE_USELESS_SCORE if useless
|
||||
# Don't prefer the lower the user's HP is (better to try something else)
|
||||
if user.hp < user.totalhp / 2
|
||||
score -= 20 * (0.75 - (user.hp.to_f / user.totalhp)) # -5 to -15
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("ReplaceMoveThisBattleWithTargetLastMoveUsed",
|
||||
proc { |move, user, ai, battle|
|
||||
next user.effects[PBEffects::Transform] || !user.battler.pbHasMove?(move.id)
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("ReplaceMoveThisBattleWithTargetLastMoveUsed",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next false if !user.faster_than?(target)
|
||||
last_move_data = GameData::Move.try_get(target.battler.lastRegularMoveUsed)
|
||||
next true if !last_move_data ||
|
||||
user.battler.pbHasMove?(target.battler.lastRegularMoveUsed) ||
|
||||
move.move.moveBlacklist.include?(last_move_data.function_code) ||
|
||||
last_move_data.type == :SHADOW
|
||||
next false
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("ReplaceMoveThisBattleWithTargetLastMoveUsed",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Generally don't prefer, as this wastes the user's turn just to gain a move
|
||||
# of unknown utility
|
||||
score -= 8
|
||||
# Slightly prefer if this move will definitely succeed, just for the sake of
|
||||
# getting rid of this move
|
||||
score += 5 if user.faster_than?(target)
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.copy("ReplaceMoveThisBattleWithTargetLastMoveUsed",
|
||||
"ReplaceMoveWithTargetLastMoveUsed")
|
||||
Battle::AI::Handlers::MoveEffectScore.copy("ReplaceMoveThisBattleWithTargetLastMoveUsed",
|
||||
"ReplaceMoveWithTargetLastMoveUsed")
|
||||
@@ -1,730 +0,0 @@
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("FleeFromBattle",
|
||||
proc { |move, user, ai, battle|
|
||||
next !battle.pbCanRun?(user.index)
|
||||
}
|
||||
)
|
||||
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
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("SwitchOutUserStatusMove",
|
||||
proc { |move, user, ai, battle|
|
||||
next !battle.pbCanRun?(user.index) if user.wild?
|
||||
next !battle.pbCanChooseNonActive?(user.index)
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("SwitchOutUserStatusMove",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next score + 10 if user.wild?
|
||||
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
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#===============================================================================
|
||||
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
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
# TODO: Might need both MoveEffectScore and MoveEffectAgainstTargetScore.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("LowerTargetAtkSpAtk1SwitchOutUser",
|
||||
proc { |move, user, target, ai, battle|
|
||||
will_fail = true
|
||||
(move.move.statDown.length / 2).times do |i|
|
||||
next if !target.battler.pbCanLowerStatStage?(move.move.statDown[i * 2], user.battler, move.move)
|
||||
will_fail = false
|
||||
break
|
||||
end
|
||||
next will_fail
|
||||
}
|
||||
)
|
||||
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)
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("SwitchOutUserPassOnEffects",
|
||||
proc { |move, user, ai, battle|
|
||||
next !battle.pbCanChooseNonActive?(user.index)
|
||||
}
|
||||
)
|
||||
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
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("SwitchOutTargetStatusMove",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if (!battle.moldBreaker && target.has_active_ability?(:SUCTIONCUPS)) ||
|
||||
target.effects[PBEffects::Ingrain]
|
||||
next true if !battle.canRun
|
||||
next true if battle.wildBattle? && target.level > user.level
|
||||
if battle.trainerBattle?
|
||||
will_fail = true
|
||||
battle.eachInTeamFromBattlerIndex(target.index) do |_pkmn, i|
|
||||
next if !battle.pbCanSwitchLax?(target.index, i)
|
||||
will_fail = false
|
||||
break
|
||||
end
|
||||
next will_fail
|
||||
end
|
||||
next false
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("SwitchOutTargetStatusMove",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
score += 20 if target.pbOwnSide.effects[PBEffects::Spikes] > 0
|
||||
score += 20 if target.pbOwnSide.effects[PBEffects::ToxicSpikes] > 0
|
||||
score += 20 if target.pbOwnSide.effects[PBEffects::StealthRock]
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("SwitchOutTargetDamagingMove",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if (battle.moldBreaker || !target.has_active_ability?(:SUCTIONCUPS)) &&
|
||||
!target.effects[PBEffects::Ingrain]
|
||||
score += 20 if target.pbOwnSide.effects[PBEffects::Spikes] > 0
|
||||
score += 20 if target.pbOwnSide.effects[PBEffects::ToxicSpikes] > 0
|
||||
score += 20 if target.pbOwnSide.effects[PBEffects::StealthRock]
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("BindTarget",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
next score if target.effects[PBEffects::Trapping] > 0
|
||||
next score if target.effects[PBEffects::Substitute] > 0
|
||||
# Prefer if the user has a Binding Band or Grip Claw (because why have it if
|
||||
# you don't want to use it?)
|
||||
score += 5 if user.has_active_item?([:BINDINGBAND, :GRIPCLAW])
|
||||
# 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?
|
||||
score += 8 # Prefer if the target will become trapped by this move
|
||||
eor_damage = target.rough_end_of_round_damage
|
||||
if eor_damage > 0
|
||||
# Prefer if the target will take damage at the end of each round on top
|
||||
# of binding damage
|
||||
score += 8
|
||||
elsif eor_damage < 0
|
||||
# Don't prefer if the target will heal itself at the end of each round
|
||||
score -= 8
|
||||
end
|
||||
# Prefer if the target has been Perish Songed
|
||||
score += 10 if target.effects[PBEffects::PerishSong] > 0
|
||||
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")
|
||||
score -= 8
|
||||
end
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveBasePower.add("BindTargetDoublePowerIfTargetUnderwater",
|
||||
proc { |power, move, user, target, ai, battle|
|
||||
next move.move.pbModifyDamage(power, user.battler, target.battler)
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.copy("BindTarget",
|
||||
"BindTargetDoublePowerIfTargetUnderwater")
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("TrapTargetInBattle",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next false if move.damagingMove?
|
||||
next true if target.effects[PBEffects::MeanLook] >= 0
|
||||
next true if Settings::MORE_TYPE_EFFECTS && target.has_type?(:GHOST)
|
||||
next false
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("TrapTargetInBattleLowerTargetDefSpDef1EachTurn",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next false if move.damagingMove?
|
||||
next true if target.effects[PBEffects::Octolock] >= 0
|
||||
next true if Settings::MORE_TYPE_EFFECTS && target.has_type?(:GHOST)
|
||||
next false
|
||||
}
|
||||
)
|
||||
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)
|
||||
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?
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("TrapAllBattlersInBattleForOneTurn",
|
||||
proc { |move, user, ai, battle|
|
||||
next battle.field.effects[PBEffects::FairyLock] > 0
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
# TODO: Review score modifiers.
|
||||
#===============================================================================
|
||||
# PursueSwitchingFoe
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("UsedAfterUserTakesPhysicalDamage",
|
||||
proc { |move, user, ai, battle|
|
||||
found_physical_move = false
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
next if !b.check_for_move { |m| m.physicalMove?(m.type) }
|
||||
found_physical_move = true
|
||||
break
|
||||
end
|
||||
next !found_physical_move
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UsedAfterUserTakesPhysicalDamage",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next Battle::AI::MOVE_USELESS_SCORE if user.effects[PBEffects::Substitute] > 0
|
||||
# Prefer if foes don't know any special moves
|
||||
found_special_move = false
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
next if !b.check_for_move { |m| m.specialMove?(m.type) }
|
||||
found_special_move = true
|
||||
break
|
||||
end
|
||||
score += 10 if !found_special_move
|
||||
# Generally not worth using
|
||||
next score - 10
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("UsedAfterAllyRoundWithDoublePower",
|
||||
proc { |score, move, user, ai, battle|
|
||||
# No score change if no allies know this move
|
||||
ally_has_move = false
|
||||
ai.each_same_side_battler(user.side) do |b, i|
|
||||
next if !b.has_move_with_function?(move.function)
|
||||
ally_has_move = true
|
||||
break
|
||||
end
|
||||
next score if !ally_has_move
|
||||
# Prefer for the sake of doubling in power
|
||||
score += 5
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("TargetActsNext",
|
||||
proc { |score, move, user, ai, battle|
|
||||
# Useless if the target is a foe
|
||||
next Battle::AI::MOVE_USELESS_SCORE if target.opposes?(user)
|
||||
# Compare the speeds of all battlers
|
||||
speeds = []
|
||||
ai.each_battler { |b, i| speeds.push([i, b.rough_stat(:SPEED)]) }
|
||||
if battle.field.effects[PBEffects::TrickRoom] > 0
|
||||
speeds.sort! { |a, b| a[1] <=> b[1] }
|
||||
else
|
||||
speeds.sort! { |a, b| b[1] <=> a[1] }
|
||||
end
|
||||
idx_user = speeds.index { |ele| ele[0] == user.index }
|
||||
idx_target = speeds.index { |ele| ele[0] == target.index }
|
||||
# Useless if the target is faster than the user
|
||||
next Battle::AI::MOVE_USELESS_SCORE if idx_target < idx_user
|
||||
# Useless if the target will move next anyway
|
||||
next Battle::AI::MOVE_USELESS_SCORE if idx_target - idx_user <= 1
|
||||
# Generally not worth using
|
||||
# NOTE: Because this move can be used against a foe but is being used on an
|
||||
# ally (since we're here in this code), this move's score will be
|
||||
# inverted later. A higher score here means this move will be less
|
||||
# preferred, which is the result we want.
|
||||
next score + 10
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("TargetActsLast",
|
||||
proc { |score, move, user, ai, battle|
|
||||
# Useless if the target is an ally
|
||||
next Battle::AI::MOVE_USELESS_SCORE if !target.opposes?(user)
|
||||
# Useless if the user has no ally (the point of this move is to let the ally
|
||||
# get in a hit before the foe)
|
||||
has_ally = false
|
||||
ai.each_ally(user.index) { |b, i| has_ally = true if b.can_attack? }
|
||||
next Battle::AI::MOVE_USELESS_SCORE if !has_ally
|
||||
# Compare the speeds of all battlers
|
||||
speeds = []
|
||||
ai.each_battler { |b, i| speeds.push([i, b.rough_stat(:SPEED)]) }
|
||||
if battle.field.effects[PBEffects::TrickRoom] > 0
|
||||
speeds.sort! { |a, b| a[1] <=> b[1] }
|
||||
else
|
||||
speeds.sort! { |a, b| b[1] <=> a[1] }
|
||||
end
|
||||
idx_user = speeds.index { |ele| ele[0] == user.index }
|
||||
idx_target = speeds.index { |ele| ele[0] == target.index }
|
||||
idx_slowest_ally = -1
|
||||
speeds.each_with_index { |ele, i| idx_slowest_ally = i if user.index.even? == ele[0].even? }
|
||||
# Useless if the target is faster than the user
|
||||
next Battle::AI::MOVE_USELESS_SCORE if idx_target < idx_user
|
||||
# Useless if the target will move last anyway
|
||||
next Battle::AI::MOVE_USELESS_SCORE if idx_target == speeds.length - 1
|
||||
# Useless if the slowest ally is faster than the target
|
||||
next Battle::AI::MOVE_USELESS_SCORE if idx_slowest_ally < idx_target
|
||||
# Generally not worth using
|
||||
next score - 10
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("TargetUsesItsLastUsedMoveAgain",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next target.battler.usingMultiTurnAttack?
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TargetUsesItsLastUsedMoveAgain",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# We don't ever want to make a foe act again
|
||||
next Battle::AI::MOVE_USELESS_SCORE if target.opposes?(user)
|
||||
# Useless if target will act before the user, as we don't know what move
|
||||
# will be instructed
|
||||
next Battle::AI::MOVE_USELESS_SCORE if target.faster_than?(user)
|
||||
next Battle::AI::MOVE_USELESS_SCORE if !target.battler.lastRegularMoveUsed
|
||||
mov = nil
|
||||
target.battler.eachMove do |m|
|
||||
mov = m if m.id == target.battler.lastRegularMoveUsed
|
||||
break if mov
|
||||
end
|
||||
next Battle::AI::MOVE_USELESS_SCORE if mov.nil? || (mov.pp == 0 && mov.total_pp > 0)
|
||||
next Battle::AI::MOVE_USELESS_SCORE if move.move.moveBlacklist.include?(mov.function)
|
||||
# Without lots of code here to determine good/bad moves, using this move is
|
||||
# likely to just be a waste of a turn
|
||||
# NOTE: Because this move can be used against a foe but is being used on an
|
||||
# ally (since we're here in this code), this move's score will be
|
||||
# inverted later. A higher score here means this move will be less
|
||||
# preferred, which is the result we want.
|
||||
score += 20
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartSlowerBattlersActFirst",
|
||||
proc { |score, move, user, ai, battle|
|
||||
# Get the speeds of all battlers
|
||||
ally_speeds = []
|
||||
foe_speeds = []
|
||||
ai.each_battler do |b, i|
|
||||
if b.opposes?(user)
|
||||
foe_speeds.push(rough_stat(:SPEED))
|
||||
foe_speeds.last *= 2 if user.pbOpposingSide.effects[PBEffects::Tailwind] > 1
|
||||
foe_speeds.last /= 2 if user.pbOpposingSide.effects[PBEffects::Swamp] > 1
|
||||
else
|
||||
ally_speeds.push(rough_stat(:SPEED))
|
||||
ally_speeds.last *= 2 if user.pbOwnSide.effects[PBEffects::Tailwind] > 1
|
||||
ally_speeds.last /= 2 if user.pbOwnSide.effects[PBEffects::Swamp] > 1
|
||||
end
|
||||
end
|
||||
# Just in case a side has no battlers
|
||||
next Battle::AI::MOVE_USELESS_SCORE if ally_speeds.length == 0 || foe_speeds.length == 0
|
||||
# Invert the speeds if Trick Room applies (and will last longer than this round)
|
||||
if battle.field.effects[PBEffects::TrickRoom] > 1
|
||||
foe_speeds.map! { |val| 100_000 - val } # 100_000 is higher than speed can
|
||||
ally_speeds.map! { |val| 100_000 - val } # possibly be; only order matters
|
||||
end
|
||||
# Score based on the relative speeds
|
||||
next Battle::AI::MOVE_USELESS_SCORE if ally_speeds.min > foe_speeds.max
|
||||
if foe_speeds.min > ally_speeds.max
|
||||
score += 20
|
||||
elsif ally_speeds.sum / ally_speeds.length < foe_speeds.sum / foe_speeds.length
|
||||
score += 10
|
||||
else
|
||||
score -= 10
|
||||
end
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# HigherPriorityInGrassyTerrain
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("LowerPPOfTargetLastMoveBy3",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if user.faster_than?(target)
|
||||
last_move = target.battler.pbGetMoveWithID(target.battler.lastRegularMoveUsed)
|
||||
if last_move && last_move.total_pp > 0
|
||||
next score + 20 if last_move.pp <= 3 # Will fully deplete the move's PP
|
||||
next score + 10 if last_move.pp <= 5
|
||||
next score - 10 if last_move.pp > 9 # Too much PP left to make a difference
|
||||
end
|
||||
end
|
||||
next score # Don't know which move it will affect; treat as just a damaging move
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("LowerPPOfTargetLastMoveBy4",
|
||||
proc { |move, user, target, ai, battle|
|
||||
last_move = target.battler.pbGetMoveWithID(target.battler.lastRegularMoveUsed)
|
||||
next !last_move || last_move.pp == 0 || last_move.total_pp <= 0
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("LowerPPOfTargetLastMoveBy4",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if user.faster_than?(target)
|
||||
last_move = target.battler.pbGetMoveWithID(target.battler.lastRegularMoveUsed)
|
||||
next score + 20 if last_move.pp <= 4 # Will fully deplete the move's PP
|
||||
next score + 10 if last_move.pp <= 6
|
||||
next score - 10 if last_move.pp > 10 # Too much PP left to make a difference
|
||||
end
|
||||
next score - 10 # Don't know which move it will affect; don't prefer
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("DisableTargetLastMoveUsed",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if target.effects[PBEffects::Disable] > 0 || !target.battler.lastRegularMoveUsed
|
||||
next true if move.move.pbMoveFailedAromaVeil?(user.battler, target.battler, false)
|
||||
will_fail = true
|
||||
target.battler.eachMove do |m|
|
||||
next if m.id != target.battler.lastRegularMoveUsed
|
||||
next if m.pp == 0 && m.total_pp > 0
|
||||
will_fail = false
|
||||
break
|
||||
end
|
||||
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
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("DisableTargetUsingSameMoveConsecutively",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if target.effects[PBEffects::Torment]
|
||||
next true if move.move.pbMoveFailedAromaVeil?(user.battler, target.battler, false)
|
||||
next false
|
||||
}
|
||||
)
|
||||
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
|
||||
# Inherent preference
|
||||
score += 8
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("DisableTargetUsingDifferentMove",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if target.effects[PBEffects::Encore] > 0
|
||||
next true if !target.battler.lastRegularMoveUsed ||
|
||||
move.move.moveBlacklist.include?(GameData::Move.get(target.battler.lastRegularMoveUsed).function_code)
|
||||
next true if target.effects[PBEffects::ShellTrap]
|
||||
next true if move.move.pbMoveFailedAromaVeil?(user.battler, target.battler, false)
|
||||
will_fail = true
|
||||
target.battler.eachMove do |m|
|
||||
next if m.id != target.battler.lastRegularMoveUsed
|
||||
next if m.pp == 0 && m.total_pp > 0
|
||||
will_fail = false
|
||||
break
|
||||
end
|
||||
next will_fail
|
||||
}
|
||||
)
|
||||
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)
|
||||
# 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
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("DisableTargetStatusMoves",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if target.effects[PBEffects::Taunt] > 0
|
||||
next true if move.move.pbMoveFailedAromaVeil?(user.battler, target.battler, false)
|
||||
next true if Settings::MECHANICS_GENERATION >= 6 &&
|
||||
!battle.moldBreaker && target.has_active_ability?(:OBLIVIOUS)
|
||||
next false
|
||||
}
|
||||
)
|
||||
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
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("DisableTargetHealingMoves",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if target.effects[PBEffects::HealBlock] > 0
|
||||
next true if move.move.pbMoveFailedAromaVeil?(user.battler, target.battler, false)
|
||||
next false
|
||||
}
|
||||
)
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetHealingMoves",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# 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
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#.
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetSoundMoves",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
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
|
||||
}
|
||||
)
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
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
|
||||
}
|
||||
)
|
||||
@@ -25,10 +25,10 @@ class Battle::AI::AIBattler
|
||||
def pokemon; return @battler.pokemon; end
|
||||
def level; return @battler.level; end
|
||||
def hp; return @battler.hp; end
|
||||
def totalhp; return @battler.totalhp; end
|
||||
def fainted?; return @battler.fainted?; end
|
||||
def status; return @battler.status; end
|
||||
def statusCount; return @battler.statusCount; end
|
||||
def totalhp; return @battler.totalhp; end
|
||||
def gender; return @battler.gender; end
|
||||
def turnCount; return @battler.turnCount; end
|
||||
def effects; return @battler.effects; end
|
||||
@@ -307,6 +307,467 @@ class Battle::AI::AIBattler
|
||||
|
||||
#=============================================================================
|
||||
|
||||
# Returns a value indicating how beneficial the given item will be to this
|
||||
# battler if it is holding it.
|
||||
# Return values are typically -2, -1, 0, 1 or 2. 0 is indifferent, positive
|
||||
# values mean this battler benefits, negative values mean this battler suffers.
|
||||
def wants_item?(item = :NONE)
|
||||
item == :NONE if item.nil?
|
||||
# TODO: Add more items.
|
||||
preferred_items = [
|
||||
:CHOICESCARF,
|
||||
:LEFTOVERS
|
||||
]
|
||||
preferred_items.push(:BLACKSLUDGE) if has_type?(:POISON)
|
||||
preferred_items.push(:IRONBALL) if has_move_with_function?("ThrowUserItemAtTarget")
|
||||
preferred_items.push(:CHOICEBAND) if check_for_move { |m| m.physicalMove?(m.type) }
|
||||
preferred_items.push(:CHOICESPECS) if check_for_move { |m| m.specialMove?(m.type) }
|
||||
unpreferred_items = [
|
||||
:BLACKSLUDGE,
|
||||
:FLAMEORB,
|
||||
:IRONBALL,
|
||||
:LAGGINGTAIL,
|
||||
:STICKYBARB,
|
||||
:TOXICORB
|
||||
]
|
||||
ret = 0
|
||||
if preferred_items.include?(item)
|
||||
ret = 2
|
||||
elsif unpreferred_items.include?(item)
|
||||
ret = -2
|
||||
end
|
||||
# Don't prefer if this battler knows Acrobatics
|
||||
if has_move_with_function?("DoublePowerIfUserHasNoItem")
|
||||
ret += (item == :NONE) ? 1 : -1
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
|
||||
# Items can be consumed by Stuff Cheeks, Teatime, Bug Bite/Pluck and Fling.
|
||||
def get_score_change_for_consuming_item(item)
|
||||
ret = 0
|
||||
case item
|
||||
when :ORANBERRY, :BERRYJUICE, :ENIGMABERRY, :SITRUSBERRY
|
||||
# Healing
|
||||
ret += (hp > totalhp * 3 / 4) ? -8 : 8
|
||||
ret = ret * 3 / 2 if GameData::Item.get(item).is_berry? && has_active_ability?(:RIPEN)
|
||||
when :AGUAVBERRY, :FIGYBERRY, :IAPAPABERRY, :MAGOBERRY, :WIKIBERRY
|
||||
# Healing with confusion
|
||||
fraction_to_heal = 8 # Gens 6 and lower
|
||||
if Settings::MECHANICS_GENERATION == 7
|
||||
fraction_to_heal = 2
|
||||
elsif Settings::MECHANICS_GENERATION >= 8
|
||||
fraction_to_heal = 3
|
||||
end
|
||||
ret += (hp > totalhp * (1 - (1 / fraction_to_heal))) ? -8 : 8
|
||||
ret = ret * 3 / 2 if GameData::Item.get(item).is_berry? && has_active_ability?(:RIPEN)
|
||||
# TODO: Check whether the item will cause confusion?
|
||||
when :ASPEARBERRY, :CHERIBERRY, :CHESTOBERRY, :PECHABERRY, :RAWSTBERRY
|
||||
# Status cure
|
||||
cured_status = {
|
||||
:ASPEAR => :FROZEN,
|
||||
:CHERIBERRY => :PARALYSIS,
|
||||
:CHESTOBERRY => :SLEEP,
|
||||
:PECHABERRY => :POISON,
|
||||
:RAWSTBERRY => :BURN
|
||||
}[item]
|
||||
ret += (cured_status && status == cured_status) ? 8 : -8
|
||||
when :PERSIMBERRY
|
||||
# Confusion cure
|
||||
ret += (effects[PBEffects::Confusion] > 1) ? 8 : -8
|
||||
when :LUMBERRY
|
||||
# Any status/confusion cure
|
||||
ret += (status != :NONE || effects[PBEffects::Confusion] > 1) ? 8 : -8
|
||||
when :MENTALHERB
|
||||
# Cure mental effects
|
||||
ret += 8 if effects[PBEffects::Attract] >= 0 ||
|
||||
effects[PBEffects::Taunt] > 1 ||
|
||||
effects[PBEffects::Encore] > 1 ||
|
||||
effects[PBEffects::Torment] ||
|
||||
effects[PBEffects::Disable] > 1 ||
|
||||
effects[PBEffects::HealBlock] > 1
|
||||
when :APICOTBERRY, :GANLONBERRY, :LIECHIBERRY, :PETAYABERRY, :SALACBERRY,
|
||||
:KEEBERRY, :MARANGABERRY
|
||||
# Stat raise
|
||||
stat = {
|
||||
:APICOTBERRY => :SPECIAL_DEFENSE,
|
||||
:GANLONBERRY => :DEFENSE,
|
||||
:LIECHIBERRY => :ATTACK,
|
||||
:PETAYABERRY => :SPECIAL_ATTACK,
|
||||
:SALACBERRY => :SPEED,
|
||||
:KEEBERRY => :DEFENSE,
|
||||
:MARANGABERRY => :SPECIAL_DEFENSE
|
||||
}[item]
|
||||
ret += 8 if stat && @ai.stat_raise_worthwhile?(self, stat)
|
||||
ret = ret * 3 / 2 if GameData::Item.get(item).is_berry? && has_active_ability?(:RIPEN)
|
||||
when :STARFBERRY
|
||||
# Random stat raise
|
||||
ret += 8
|
||||
ret = ret * 3 / 2 if GameData::Item.get(item).is_berry? && has_active_ability?(:RIPEN)
|
||||
when :WHITEHERB
|
||||
# Resets lowered stats
|
||||
reduced_stats = false
|
||||
GameData::Stat.each_battle do |s|
|
||||
next if stages[s.id] >= 0
|
||||
reduced_stats = true
|
||||
break
|
||||
end
|
||||
ret += 8 if reduced_stats
|
||||
when :MICLEBERRY
|
||||
# Raises accuracy of next move
|
||||
ret += 8
|
||||
when :LANSATBERRY
|
||||
# Focus energy
|
||||
ret += 8 if effects[PBEffects::FocusEnergy] < 2
|
||||
when :LEPPABERRY
|
||||
# Restore PP
|
||||
ret += 8
|
||||
ret = ret * 3 / 2 if GameData::Item.get(item).is_berry? && has_active_ability?(:RIPEN)
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
|
||||
# These values are taken from the Complete-Fire-Red-Upgrade decomp here:
|
||||
# https://github.com/Skeli789/Complete-Fire-Red-Upgrade/blob/f7f35becbd111c7e936b126f6328fc52d9af68c8/src/ability_battle_effects.c#L41
|
||||
BASE_ABILITY_RATINGS = {
|
||||
:ADAPTABILITY => 8,
|
||||
:AERILATE => 8,
|
||||
:AFTERMATH => 5,
|
||||
:AIRLOCK => 5,
|
||||
:ANALYTIC => 5,
|
||||
:ANGERPOINT => 4,
|
||||
:ANTICIPATION => 0,
|
||||
:ARENATRAP => 9,
|
||||
:AROMAVEIL => 3,
|
||||
# :ASONECHILLINGNEIGH => 0,
|
||||
# :ASONEGRIMNEIGH => 0,
|
||||
:AURABREAK => 3,
|
||||
:BADDREAMS => 4,
|
||||
# :BALLFETCH => 0,
|
||||
# :BATTERY => 0,
|
||||
:BATTLEARMOR => 2,
|
||||
:BATTLEBOND => 6,
|
||||
:BEASTBOOST => 7,
|
||||
:BERSERK => 5,
|
||||
:BIGPECKS => 1,
|
||||
:BLAZE => 5,
|
||||
:BULLETPROOF => 7,
|
||||
:CHEEKPOUCH => 4,
|
||||
# :CHILLINGNEIGH => 0,
|
||||
:CHLOROPHYLL => 6,
|
||||
:CLEARBODY => 4,
|
||||
:CLOUDNINE => 5,
|
||||
:COLORCHANGE => 2,
|
||||
:COMATOSE => 6,
|
||||
:COMPETITIVE => 5,
|
||||
:COMPOUNDEYES => 7,
|
||||
:CONTRARY => 8,
|
||||
:CORROSION => 5,
|
||||
:COTTONDOWN => 3,
|
||||
# :CURIOUSMEDICINE => 0,
|
||||
:CURSEDBODY => 4,
|
||||
:CUTECHARM => 2,
|
||||
:DAMP => 2,
|
||||
:DANCER => 5,
|
||||
:DARKAURA => 6,
|
||||
:DAUNTLESSSHIELD => 3,
|
||||
:DAZZLING => 5,
|
||||
:DEFEATIST => -1,
|
||||
:DEFIANT => 5,
|
||||
:DELTASTREAM => 10,
|
||||
:DESOLATELAND => 10,
|
||||
:DISGUISE => 8,
|
||||
:DOWNLOAD => 7,
|
||||
:DRAGONSMAW => 8,
|
||||
:DRIZZLE => 9,
|
||||
:DROUGHT => 9,
|
||||
:DRYSKIN => 6,
|
||||
:EARLYBIRD => 4,
|
||||
:EFFECTSPORE => 4,
|
||||
:ELECTRICSURGE => 8,
|
||||
:EMERGENCYEXIT => 3,
|
||||
:FAIRYAURA => 6,
|
||||
:FILTER => 6,
|
||||
:FLAMEBODY => 4,
|
||||
:FLAREBOOST => 5,
|
||||
:FLASHFIRE => 6,
|
||||
:FLOWERGIFT => 4,
|
||||
# :FLOWERVEIL => 0,
|
||||
:FLUFFY => 5,
|
||||
:FORECAST => 6,
|
||||
:FOREWARN => 0,
|
||||
# :FRIENDGUARD => 0,
|
||||
:FRISK => 0,
|
||||
:FULLMETALBODY => 4,
|
||||
:FURCOAT => 7,
|
||||
:GALEWINGS => 6,
|
||||
:GALVANIZE => 8,
|
||||
:GLUTTONY => 3,
|
||||
:GOOEY => 5,
|
||||
:GORILLATACTICS => 4,
|
||||
:GRASSPELT => 2,
|
||||
:GRASSYSURGE => 8,
|
||||
# :GRIMNEIGH => 0,
|
||||
:GULPMISSLE => 3,
|
||||
:GUTS => 6,
|
||||
:HARVEST => 5,
|
||||
# :HEALER => 0,
|
||||
:HEATPROOF => 5,
|
||||
:HEAVYMETAL => -1,
|
||||
# :HONEYGATHER => 0,
|
||||
:HUGEPOWER => 10,
|
||||
:HUNGERSWITCH => 2,
|
||||
:HUSTLE => 7,
|
||||
:HYDRATION => 4,
|
||||
:HYPERCUTTER => 3,
|
||||
:ICEBODY => 3,
|
||||
:ICEFACE => 4,
|
||||
:ICESCALES => 7,
|
||||
# :ILLUMINATE => 0,
|
||||
:ILLUSION => 8,
|
||||
:IMMUNITY => 4,
|
||||
:IMPOSTER => 9,
|
||||
:INFILTRATOR => 6,
|
||||
:INNARDSOUT => 5,
|
||||
:INNERFOCUS => 2,
|
||||
:INSOMNIA => 4,
|
||||
:INTIMIDATE => 7,
|
||||
:INTREPIDSWORD => 3,
|
||||
:IRONBARBS => 6,
|
||||
:IRONFIST => 6,
|
||||
:JUSTIFIED => 4,
|
||||
:KEENEYE => 1,
|
||||
:KLUTZ => -1,
|
||||
:LEAFGUARD => 2,
|
||||
:LEVITATE => 7,
|
||||
:LIBERO => 8,
|
||||
:LIGHTMETAL => 2,
|
||||
:LIGHTNINGROD => 7,
|
||||
:LIMBER => 3,
|
||||
:LIQUIDOOZE => 3,
|
||||
:LIQUIDVOICE => 5,
|
||||
:LONGREACH => 3,
|
||||
:MAGICBOUNCE => 9,
|
||||
:MAGICGUARD => 9,
|
||||
:MAGICIAN => 3,
|
||||
:MAGMAARMOR => 1,
|
||||
:MAGNETPULL => 9,
|
||||
:MARVELSCALE => 5,
|
||||
:MEGALAUNCHER => 7,
|
||||
:MERCILESS => 4,
|
||||
:MIMICRY => 2,
|
||||
# :MINUS => 0,
|
||||
:MIRRORARMOR => 6,
|
||||
:MISTYSURGE => 8,
|
||||
:MOLDBREAKER => 7,
|
||||
:MOODY => 10,
|
||||
:MOTORDRIVE => 6,
|
||||
:MOXIE => 7,
|
||||
:MULTISCALE => 8,
|
||||
:MULTITYPE => 8,
|
||||
:MUMMY => 5,
|
||||
:NATURALCURE => 7,
|
||||
:NEUROFORCE => 6,
|
||||
:NEUTRALIZINGGAS => 5,
|
||||
:NOGUARD => 8,
|
||||
:NORMALIZE => -1,
|
||||
:OBLIVIOUS => 2,
|
||||
:OVERCOAT => 5,
|
||||
:OVERGROW => 5,
|
||||
:OWNTEMPO => 3,
|
||||
:PARENTALBOND => 10,
|
||||
:PASTELVEIL => 4,
|
||||
:PERISHBODY => -1,
|
||||
:PICKPOCKET => 3,
|
||||
:PICKUP => 1,
|
||||
:PIXILATE => 8,
|
||||
# :PLUS => 0,
|
||||
:POISONHEAL => 8,
|
||||
:POISONPOINT => 4,
|
||||
:POISONTOUCH => 4,
|
||||
:POWERCONSTRUCT => 10,
|
||||
# :POWEROFALCHEMY => 0,
|
||||
:POWERSPOT => 2,
|
||||
:PRANKSTER => 8,
|
||||
:PRESSURE => 5,
|
||||
:PRIMORDIALSEA => 10,
|
||||
:PRISMARMOR => 6,
|
||||
:PROPELLORTAIL => 2,
|
||||
:PROTEAN => 8,
|
||||
:PSYCHICSURGE => 8,
|
||||
:PUNKROCK => 2,
|
||||
:PUREPOWER => 10,
|
||||
:QUEENLYMAJESTY => 6,
|
||||
# :QUICKDRAW => 0,
|
||||
:QUICKFEET => 5,
|
||||
:RAINDISH => 3,
|
||||
:RATTLED => 3,
|
||||
# :RECEIVER => 0,
|
||||
:RECKLESS => 6,
|
||||
:REFRIGERATE => 8,
|
||||
:REGENERATOR => 8,
|
||||
:RIPEN => 4,
|
||||
:RIVALRY => 1,
|
||||
:RKSSYSTEM => 8,
|
||||
:ROCKHEAD => 5,
|
||||
:ROUGHSKIN => 6,
|
||||
# :RUNAWAY => 0,
|
||||
:SANDFORCE => 4,
|
||||
:SANDRUSH => 6,
|
||||
:SANDSPIT => 5,
|
||||
:SANDSTREAM => 9,
|
||||
:SANDVEIL => 3,
|
||||
:SAPSIPPER => 7,
|
||||
:SCHOOLING => 6,
|
||||
:SCRAPPY => 6,
|
||||
:SCREENCLEANER => 3,
|
||||
:SERENEGRACE => 8,
|
||||
:SHADOWSHIELD => 8,
|
||||
:SHADOWTAG => 10,
|
||||
:SHEDSKIN => 7,
|
||||
:SHEERFORCE => 8,
|
||||
:SHELLARMOR => 2,
|
||||
:SHIELDDUST => 5,
|
||||
:SHIELDSDOWN => 6,
|
||||
:SIMPLE => 8,
|
||||
:SKILLLINK => 7,
|
||||
:SLOWSTART => -2,
|
||||
:SLUSHRUSH => 5,
|
||||
:SNIPER => 3,
|
||||
:SNOWCLOAK => 3,
|
||||
:SNOWWARNING => 8,
|
||||
:SOLARPOWER => 3,
|
||||
:SOLIDROCK => 6,
|
||||
:SOULHEART => 7,
|
||||
:SOUNDPROOF => 4,
|
||||
:SPEEDBOOST => 9,
|
||||
:STAKEOUT => 6,
|
||||
:STALL => -1,
|
||||
:STALWART => 2,
|
||||
:STAMINA => 6,
|
||||
:STANCECHANGE => 10,
|
||||
:STATIC => 4,
|
||||
:STEADFAST => 2,
|
||||
:STEAMENGINE => 3,
|
||||
:STEELWORKER => 6,
|
||||
:STEELYSPIRIT => 2,
|
||||
:STENCH => 1,
|
||||
:STICKYHOLD => 3,
|
||||
:STORMDRAIN => 7,
|
||||
:STRONGJAW => 6,
|
||||
:STURDY => 6,
|
||||
:SUCTIONCUPS => 2,
|
||||
:SUPERLUCK => 3,
|
||||
:SURGESURFER => 4,
|
||||
:SWARM => 5,
|
||||
:SWEETVEIL => 4,
|
||||
:SWIFTSWIM => 6,
|
||||
# :SYMBIOSIS => 0,
|
||||
:SYNCHRONIZE => 4,
|
||||
:TANGLEDFEET => 2,
|
||||
:TANGLINGHAIR => 5,
|
||||
:TECHNICIAN => 8,
|
||||
# :TELEPATHY => 0,
|
||||
:TERAVOLT => 7,
|
||||
:THICKFAT => 7,
|
||||
:TINTEDLENS => 7,
|
||||
:TORRENT => 5,
|
||||
:TOUGHCLAWS => 7,
|
||||
:TOXICBOOST => 6,
|
||||
:TRACE => 6,
|
||||
:TRANSISTOR => 8,
|
||||
:TRIAGE => 7,
|
||||
:TRUANT => -2,
|
||||
:TURBOBLAZE => 7,
|
||||
:UNAWARE => 6,
|
||||
:UNBURDEN => 7,
|
||||
:UNNERVE => 3,
|
||||
# :UNSEENFIST => 0,
|
||||
:VICTORYSTAR => 6,
|
||||
:VITALSPIRIT => 4,
|
||||
:VOLTABSORB => 7,
|
||||
:WANDERINGSPIRIT => 2,
|
||||
:WATERABSORB => 7,
|
||||
:WATERBUBBLE => 8,
|
||||
:WATERCOMPACTION => 4,
|
||||
:WATERVEIL => 4,
|
||||
:WEAKARMOR => 2,
|
||||
:WHITESMOKE => 4,
|
||||
:WIMPOUT => 3,
|
||||
:WONDERGUARD => 10,
|
||||
:WONDERSKIN => 4,
|
||||
:ZENMODE => -1
|
||||
}
|
||||
|
||||
# Returns a value indicating how beneficial the given ability will be to this
|
||||
# battler if it has 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 assumes the ability isn't being negated. Should it return
|
||||
# 0 if it is? The calculations that call this method separately check
|
||||
# for it being negated, because they need to do something special in
|
||||
# that case, so I think it's okay for this method to ignore negation.
|
||||
def wants_ability?(ability = :NONE)
|
||||
ability = ability.id if !ability.is_a?(Symbol) && ability.respond_to?("id")
|
||||
# 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 !has_damaging_move_of_type?(:FIRE)
|
||||
when :CUTECHARM, :RIVALRY
|
||||
return 0 if gender == 2
|
||||
when :FRIENDGUARD, :HEALER, :SYMBOISIS, :TELEPATHY
|
||||
has_ally = false
|
||||
each_ally(@side) { |b, i| has_ally = true }
|
||||
return 0 if !has_ally
|
||||
when :GALEWINGS
|
||||
return 0 if !check_for_move { |m| m.type == :FLYING }
|
||||
when :HUGEPOWER, :PUREPOWER
|
||||
return 0 if !check_for_move { |m| m.physicalMove?(m.type) &&
|
||||
m.function != "UseUserDefenseInsteadOfUserAttack" &&
|
||||
m.function != "UseTargetAttackInsteadOfUserAttack" }
|
||||
when :IRONFIST
|
||||
return 0 if !check_for_move { |m| m.punchingMove? }
|
||||
when :LIQUIDVOICE
|
||||
return 0 if !check_for_move { |m| m.soundMove? }
|
||||
when :MEGALAUNCHER
|
||||
return 0 if !check_for_move { |m| m.pulseMove? }
|
||||
when :OVERGROW
|
||||
return 0 if !has_damaging_move_of_type?(:GRASS)
|
||||
when :PRANKSTER
|
||||
return 0 if !check_for_move { |m| m.statusMove? }
|
||||
when :PUNKROCK
|
||||
return 1 if !check_for_move { |m| m.damagingMove? && m.soundMove? }
|
||||
when :RECKLESS
|
||||
return 0 if !check_for_move { |m| m.recoilMove? }
|
||||
when :ROCKHEAD
|
||||
return 0 if !check_for_move { |m| m.recoilMove? && !m.is_a?(Battle::Move::CrashDamageIfFailsUnusableInGravity) }
|
||||
when :RUNAWAY
|
||||
return 0 if wild?
|
||||
when :SANDFORCE
|
||||
return 2 if !has_damaging_move_of_type?(:GROUND, :ROCK, :STEEL)
|
||||
when :SKILLLINK
|
||||
return 0 if !check_for_move { |m| m.is_a?(Battle::Move::HitTwoToFiveTimes) }
|
||||
when :STEELWORKER
|
||||
return 0 if !has_damaging_move_of_type?(:GRASS)
|
||||
when :SWARM
|
||||
return 0 if !has_damaging_move_of_type?(:BUG)
|
||||
when :TORRENT
|
||||
return 0 if !has_damaging_move_of_type?(:WATER)
|
||||
when :TRIAGE
|
||||
return 0 if !check_for_move { |m| m.healingMove? }
|
||||
end
|
||||
ret = BASE_ABILITY_RATINGS[ability] || 0
|
||||
return ret
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
|
||||
private
|
||||
|
||||
def effectiveness_of_type_against_single_battler_type(type, defend_type, user = nil)
|
||||
|
||||
@@ -88,7 +88,7 @@ class Battle::AI::AIMove
|
||||
# Returns this move's base power, taking into account various effects that
|
||||
# modify it.
|
||||
def base_power
|
||||
ret = @move.baseDamage
|
||||
ret = @move.power
|
||||
ret = 60 if ret == 1
|
||||
return ret if !@ai.trainer.medium_skill?
|
||||
return Battle::AI::Handlers.get_base_power(function,
|
||||
@@ -96,8 +96,8 @@ class Battle::AI::AIMove
|
||||
end
|
||||
|
||||
def rough_damage
|
||||
power = base_power
|
||||
return power if @move.is_a?(Battle::Move::FixedDamageMove)
|
||||
base_dmg = base_power
|
||||
return base_dmg if @move.is_a?(Battle::Move::FixedDamageMove)
|
||||
stage_mul = [2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8]
|
||||
stage_div = [8, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2]
|
||||
# Get the user and target of this move
|
||||
@@ -132,7 +132,7 @@ class Battle::AI::AIMove
|
||||
|
||||
##### Calculate all multiplier effects #####
|
||||
multipliers = {
|
||||
:base_damage_multiplier => 1.0,
|
||||
:power_multiplier => 1.0,
|
||||
:attack_multiplier => 1.0,
|
||||
:defense_multiplier => 1.0,
|
||||
:final_damage_multiplier => 1.0
|
||||
@@ -142,9 +142,9 @@ class Battle::AI::AIMove
|
||||
((@ai.battle.pbCheckGlobalAbility(:DARKAURA) && calc_type == :DARK) ||
|
||||
(@ai.battle.pbCheckGlobalAbility(:FAIRYAURA) && calc_type == :FAIRY))
|
||||
if @ai.battle.pbCheckGlobalAbility(:AURABREAK)
|
||||
multipliers[:base_damage_multiplier] *= 2 / 3.0
|
||||
multipliers[:power_multiplier] *= 2 / 3.0
|
||||
else
|
||||
multipliers[:base_damage_multiplier] *= 4 / 3.0
|
||||
multipliers[:power_multiplier] *= 4 / 3.0
|
||||
end
|
||||
end
|
||||
|
||||
@@ -155,7 +155,7 @@ class Battle::AI::AIMove
|
||||
abilityBlacklist = [:ANALYTIC, :SNIPER, :TINTEDLENS, :AERILATE, :PIXILATE, :REFRIGERATE]
|
||||
if !abilityBlacklist.include?(user.ability_id)
|
||||
Battle::AbilityEffects.triggerDamageCalcFromUser(
|
||||
user.ability, user_battler, target_battler, @move, multipliers, power, calc_type
|
||||
user.ability, user_battler, target_battler, @move, multipliers, base_dmg, calc_type
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -164,7 +164,7 @@ class Battle::AI::AIMove
|
||||
user_battler.allAllies.each do |b|
|
||||
next if !b.abilityActive?
|
||||
Battle::AbilityEffects.triggerDamageCalcFromAlly(
|
||||
b.ability, user_battler, target_battler, @move, multipliers, power, calc_type
|
||||
b.ability, user_battler, target_battler, @move, multipliers, base_dmg, calc_type
|
||||
)
|
||||
end
|
||||
if target.ability_active?
|
||||
@@ -173,21 +173,21 @@ class Battle::AI::AIMove
|
||||
abilityBlacklist = [:FILTER, :SOLIDROCK]
|
||||
if !abilityBlacklist.include?(target.ability_id)
|
||||
Battle::AbilityEffects.triggerDamageCalcFromTarget(
|
||||
target.ability, user_battler, target_battler, @move, multipliers, power, calc_type
|
||||
target.ability, user_battler, target_battler, @move, multipliers, base_dmg, calc_type
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
if target.ability_active?
|
||||
Battle::AbilityEffects.triggerDamageCalcFromTargetNonIgnorable(
|
||||
target.ability, user_battler, target_battler, @move, multipliers, power, calc_type
|
||||
target.ability, user_battler, target_battler, @move, multipliers, base_dmg, calc_type
|
||||
)
|
||||
end
|
||||
if !@ai.battle.moldBreaker
|
||||
target_battler.allAllies.each do |b|
|
||||
next if !b.abilityActive?
|
||||
Battle::AbilityEffects.triggerDamageCalcFromTargetAlly(
|
||||
b.ability, user_battler, target_battler, @move, multipliers, power, calc_type
|
||||
b.ability, user_battler, target_battler, @move, multipliers, base_dmg, calc_type
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -201,7 +201,7 @@ class Battle::AI::AIMove
|
||||
itemBlacklist = [:EXPERTBELT, :LIFEORB]
|
||||
if !itemBlacklist.include?(user.item_id)
|
||||
Battle::ItemEffects.triggerDamageCalcFromUser(
|
||||
user.item, user_battler, target_battler, @move, multipliers, power, calc_type
|
||||
user.item, user_battler, target_battler, @move, multipliers, base_dmg, calc_type
|
||||
)
|
||||
user.effects[PBEffects::GemConsumed] = nil # Untrigger consuming of Gems
|
||||
end
|
||||
@@ -209,13 +209,13 @@ class Battle::AI::AIMove
|
||||
end
|
||||
if target.item_active? && target.item && !target.item.is_berry?
|
||||
Battle::ItemEffects.triggerDamageCalcFromTarget(
|
||||
target.item, user_battler, target_battler, @move, multipliers, power, calc_type
|
||||
target.item, user_battler, target_battler, @move, multipliers, base_dmg, calc_type
|
||||
)
|
||||
end
|
||||
|
||||
# Parental Bond
|
||||
if user.has_active_ability?(:PARENTALBOND)
|
||||
multipliers[:base_damage_multiplier] *= (Settings::MECHANICS_GENERATION >= 7) ? 1.25 : 1.5
|
||||
multipliers[:power_multiplier] *= (Settings::MECHANICS_GENERATION >= 7) ? 1.25 : 1.5
|
||||
end
|
||||
|
||||
# Me First
|
||||
@@ -226,24 +226,24 @@ class Battle::AI::AIMove
|
||||
# Charge
|
||||
if @ai.trainer.medium_skill? &&
|
||||
user.effects[PBEffects::Charge] > 0 && calc_type == :ELECTRIC
|
||||
multipliers[:base_damage_multiplier] *= 2
|
||||
multipliers[:power_multiplier] *= 2
|
||||
end
|
||||
|
||||
# Mud Sport and Water Sport
|
||||
if @ai.trainer.medium_skill?
|
||||
if calc_type == :ELECTRIC
|
||||
if @ai.battle.allBattlers.any? { |b| b.effects[PBEffects::MudSport] }
|
||||
multipliers[:base_damage_multiplier] /= 3
|
||||
multipliers[:power_multiplier] /= 3
|
||||
end
|
||||
if @ai.battle.field.effects[PBEffects::MudSportField] > 0
|
||||
multipliers[:base_damage_multiplier] /= 3
|
||||
multipliers[:power_multiplier] /= 3
|
||||
end
|
||||
elsif calc_type == :FIRE
|
||||
if @ai.battle.allBattlers.any? { |b| b.effects[PBEffects::WaterSport] }
|
||||
multipliers[:base_damage_multiplier] /= 3
|
||||
multipliers[:power_multiplier] /= 3
|
||||
end
|
||||
if @ai.battle.field.effects[PBEffects::WaterSportField] > 0
|
||||
multipliers[:base_damage_multiplier] /= 3
|
||||
multipliers[:power_multiplier] /= 3
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -253,13 +253,13 @@ class Battle::AI::AIMove
|
||||
terrain_multiplier = (Settings::MECHANICS_GENERATION >= 8) ? 1.3 : 1.5
|
||||
case @ai.battle.field.terrain
|
||||
when :Electric
|
||||
multipliers[:base_damage_multiplier] *= terrain_multiplier if calc_type == :ELECTRIC && user_battler.affectedByTerrain?
|
||||
multipliers[:power_multiplier] *= terrain_multiplier if calc_type == :ELECTRIC && user_battler.affectedByTerrain?
|
||||
when :Grassy
|
||||
multipliers[:base_damage_multiplier] *= terrain_multiplier if calc_type == :GRASS && user_battler.affectedByTerrain?
|
||||
multipliers[:power_multiplier] *= terrain_multiplier if calc_type == :GRASS && user_battler.affectedByTerrain?
|
||||
when :Psychic
|
||||
multipliers[:base_damage_multiplier] *= terrain_multiplier if calc_type == :PSYCHIC && user_battler.affectedByTerrain?
|
||||
multipliers[:power_multiplier] *= terrain_multiplier if calc_type == :PSYCHIC && user_battler.affectedByTerrain?
|
||||
when :Misty
|
||||
multipliers[:base_damage_multiplier] /= 2 if calc_type == :DRAGON && target_battler.affectedByTerrain?
|
||||
multipliers[:power_multiplier] /= 2 if calc_type == :DRAGON && target_battler.affectedByTerrain?
|
||||
end
|
||||
end
|
||||
|
||||
@@ -370,11 +370,11 @@ class Battle::AI::AIMove
|
||||
# TODO
|
||||
|
||||
##### Main damage calculation #####
|
||||
power = [(power * multipliers[:base_damage_multiplier]).round, 1].max
|
||||
atk = [(atk * multipliers[:attack_multiplier]).round, 1].max
|
||||
defense = [(defense * multipliers[:defense_multiplier]).round, 1].max
|
||||
damage = ((((2.0 * user.level / 5) + 2).floor * power * atk / defense).floor / 50).floor + 2
|
||||
damage = [(damage * multipliers[:final_damage_multiplier]).round, 1].max
|
||||
base_dmg = [(base_dmg * multipliers[:power_multiplier]).round, 1].max
|
||||
atk = [(atk * multipliers[:attack_multiplier]).round, 1].max
|
||||
defense = [(defense * multipliers[:defense_multiplier]).round, 1].max
|
||||
damage = ((((2.0 * user.level / 5) + 2).floor * base_dmg * atk / defense).floor / 50).floor + 2
|
||||
damage = [(damage * multipliers[:final_damage_multiplier]).round, 1].max
|
||||
ret = damage.floor
|
||||
ret = target.hp - 1 if @move.nonLethal?(user_battler, target_battler) && ret >= target.hp
|
||||
return ret
|
||||
|
||||
Reference in New Issue
Block a user