#=============================================================================== # #=============================================================================== class Battle::AI::AIBattler attr_reader :index, :side, :party_index attr_reader :battler def initialize(ai, index) @ai = ai @index = index @side = (@ai.battle.opposes?(@index)) ? 1 : 0 refresh_battler end def refresh_battler old_party_index = @party_index @battler = @ai.battle.battlers[@index] @party_index = battler.pokemonIndex if @party_index != old_party_index # TODO: Start of battle or Pokémon switched/shifted; recalculate roles, # etc. end end 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 gender; return battler.gender; end def turnCount; return battler.turnCount; end def effects; return battler.effects; end def stages; return battler.stages; end def statStageAtMax?(stat); return battler.statStageAtMax?(stat); end def statStageAtMin?(stat); return battler.statStageAtMin?(stat); end def moves; return battler.moves; end def wild? return @ai.battle.wildBattle? && opposes? end def name return sprintf("%s (%d)", battler.name, @index) end def opposes?(other = nil) return @side == 1 if other.nil? return other.side != @side end def idxOwnSide; return battler.idxOwnSide; end def pbOwnSide; return battler.pbOwnSide; end def idxOpposingSide; return battler.idxOpposingSide; end def pbOpposingSide; return battler.pbOpposingSide; end #============================================================================= # Returns how much damage this battler will take at the end of this round. def rough_end_of_round_damage ret = 0 # Future Sight/Doom Desire # TODO # Wish if @ai.battle.positions[@index].effects[PBEffects::Wish] == 1 && battler.canHeal? ret -= @ai.battle.positions[@index].effects[PBEffects::WishAmount] end # Sea of Fire if @ai.battle.sides[@side].effects[PBEffects::SeaOfFire] > 1 && battler.takesIndirectDamage? && !has_type?(:FIRE) ret += self.totalhp / 8 end # Grassy Terrain (healing) if @ai.battle.field.terrain == :Grassy && battler.affectedByTerrain? && battler.canHeal? ret -= [battler.totalhp / 16, 1].max end # Leftovers/Black Sludge if has_active_item?(:BLACKSLUDGE) if has_type?(:POISON) ret -= [battler.totalhp / 16, 1].max if battler.canHeal? else ret += [battler.totalhp / 8, 1].max if battler.takesIndirectDamage? end elsif has_active_item?(:LEFTOVERS) ret -= [battler.totalhp / 16, 1].max if battler.canHeal? end # Aqua Ring if self.effects[PBEffects::AquaRing] && battler.canHeal? amt = battler.totalhp / 16 amt = (amt * 1.3).floor if has_active_item?(:BIGROOT) ret -= [amt, 1].max end # Ingrain if self.effects[PBEffects::Ingrain] && battler.canHeal? amt = battler.totalhp / 16 amt = (amt * 1.3).floor if has_active_item?(:BIGROOT) ret -= [amt, 1].max end # Leech Seed if self.effects[PBEffects::LeechSeed] >= 0 if battler.takesIndirectDamage? ret += [battler.totalhp / 8, 1].max if battler.takesIndirectDamage? end else @ai.each_battler do |b, i| next if i == @index || b.effects[PBEffects::LeechSeed] != @index amt = [[b.totalhp / 8, b.hp].min, 1].max amt = (amt * 1.3).floor if has_active_item?(:BIGROOT) ret -= [amt, 1].max end end # Hyper Mode (Shadow Pokémon) if battler.inHyperMode? ret += [battler.totalhp / 24, 1].max end # Poison/burn/Nightmare if self.status == :POISON if has_active_ability?(:POISONHEAL) ret -= [battler.totalhp / 8, 1].max if battler.canHeal? elsif battler.takesIndirectDamage? mult = 2 mult = [self.effects[PBEffects::Toxic] + 1, 16].min if self.statusCount > 0 # Toxic ret += [mult * battler.totalhp / 16, 1].max end elsif self.status == :BURN if battler.takesIndirectDamage? amt = (Settings::MECHANICS_GENERATION >= 7) ? self.totalhp / 16 : self.totalhp / 8 amt = (amt / 2.0).round if has_active_ability?(:HEATPROOF) ret += [amt, 1].max end elsif battler.asleep? && self.statusCount > 1 && self.effects[PBEffects::Nightmare] ret += [battler.totalhp / 4, 1].max if battler.takesIndirectDamage? end # Curse if self.effects[PBEffects::Curse] ret += [battler.totalhp / 4, 1].max if battler.takesIndirectDamage? end # Trapping damage if self.effects[PBEffects::Trapping] > 1 && battler.takesIndirectDamage? amt = (Settings::MECHANICS_GENERATION >= 6) ? self.totalhp / 8 : self.totalhp / 16 if @ai.battlers[self.effects[PBEffects::TrappingUser]].has_active_item?(:BINDINGBAND) amt = (Settings::MECHANICS_GENERATION >= 6) ? self.totalhp / 6 : self.totalhp / 8 end ret += [amt, 1].max end # Perish Song return 999_999 if self.effects[PBEffects::PerishSong] == 1 # Bad Dreams if battler.asleep? && self.statusCount > 1 && battler.takesIndirectDamage? @ai.each_battler do |b, i| next if i == @index || !b.battler.near?(battler) || !b.has_active_ability?(:BADDREAMS) ret += [battler.totalhp / 8, 1].max end end # Sticky Barb if has_active_item?(:STICKYBARB) && battler.takesIndirectDamage? ret += [battler.totalhp / 8, 1].max end return ret end #============================================================================= def base_stat(stat) ret = 0 case stat when :ATTACK then ret = battler.attack when :DEFENSE then ret = battler.defense when :SPECIAL_ATTACK then ret = battler.spatk when :SPECIAL_DEFENSE then ret = battler.spdef when :SPEED then ret = battler.speed end return ret end def rough_stat(stat) return battler.pbSpeed if stat == :SPEED && @ai.trainer.high_skill? stage_mul = Battle::Battler::STAT_STAGE_MULTIPLIERS stage_div = Battle::Battler::STAT_STAGE_DIVISORS if [:ACCURACY, :EVASION].include?(stat) stage_mul = Battle::Battler::ACC_EVA_STAGE_MULTIPLIERS stage_div = Battle::Battler::ACC_EVA_STAGE_DIVISORS end stage = battler.stages[stat] + Battle::Battler::STAT_STAGE_MAXIMUM value = base_stat(stat) return (value.to_f * stage_mul[stage] / stage_div[stage]).floor end def faster_than?(other) return false if other.nil? this_speed = rough_stat(:SPEED) other_speed = other.rough_stat(:SPEED) return (this_speed > other_speed) ^ (@ai.battle.field.effects[PBEffects::TrickRoom] > 0) end #============================================================================= def types; return battler.types; end def pbTypes(withExtraType = false); return battler.pbTypes(withExtraType); end def has_type?(type) return false if !type active_types = pbTypes(true) return active_types.include?(GameData::Type.get(type).id) end # TODO: Also make a def effectiveness_of_move_against_battler which calls # pbCalcTypeModSingle instead of effectiveness_of_type_against_single_battler_type. # Why? def effectiveness_of_type_against_battler(type, user = nil) ret = Effectiveness::NORMAL_EFFECTIVE_MULTIPLIER return ret if !type return ret if type == :GROUND && has_type?(:FLYING) && has_active_item?(:IRONBALL) # Get effectivenesses if type == :SHADOW if battler.shadowPokemon? ret = Effectiveness::NOT_VERY_EFFECTIVE_MULTIPLIER else ret = Effectiveness::SUPER_EFFECTIVE_MULTIPLIER end else battler.pbTypes(true).each do |defend_type| ret *= effectiveness_of_type_against_single_battler_type(type, defend_type, user) end ret *= 2 if self.effects[PBEffects::TarShot] && type == :FIRE end return ret end #============================================================================= def ability_id; return battler.ability_id; end def ability; return battler.ability; end def ability_active? return battler.abilityActive? end def has_active_ability?(ability, ignore_fainted = false) return battler.hasActiveAbility?(ability, ignore_fainted) end def has_mold_breaker? return @ai.move.function == "IgnoreTargetAbility" || battler.hasMoldBreaker? end #============================================================================= def item_id; return battler.item_id; end def item; return battler.item; end def item_active? return battler.itemActive? end def has_active_item?(item) return battler.hasActiveItem?(item) end #============================================================================= def check_for_move ret = false battler.eachMove do |move| next if move.pp == 0 && move.total_pp > 0 next unless yield move ret = true break end return ret end def has_damaging_move_of_type?(*types) check_for_move do |m| return true if m.damagingMove? && types.include?(m.pbCalcType(battler)) end return false end def has_move_with_function?(*functions) check_for_move { |m| return true if functions.include?(m.function) } return false end #============================================================================= def can_attack? return false if self.effects[PBEffects::HyperBeam] > 0 return false if status == :SLEEP && statusCount > 1 return false if status == :FROZEN # Only 20% chance of unthawing; assune it won't return false if self.effects[PBEffects::Truant] && has_active_ability?(:TRUANT) return false if self.effects[PBEffects::Flinch] # NOTE: Confusion/infatuation/paralysis have higher chances of allowing the # attack, so the battler is treated as able to attack in those cases. return true end def can_switch_lax? return false if wild? @ai.battle.eachInTeamFromBattlerIndex(@index) do |pkmn, i| return true if @ai.battle.pbCanSwitchIn?(@index, i) end return false end # NOTE: This specifically means "is not currently trapped but can become # trapped by an effect". Similar to def pbCanSwitchOut? but this returns # false if any certain switching OR certain trapping applies. def can_become_trapped? return false if fainted? # Ability/item effects that allow switching no matter what if ability_active? && Battle::AbilityEffects.triggerCertainSwitching(ability, battler, @ai.battle) return false end if item_active? && Battle::ItemEffects.triggerCertainSwitching(item, battler, @ai.battle) return false end # Other certain switching effects return false if Settings::MORE_TYPE_EFFECTS && has_type?(:GHOST) # Other certain trapping effects return false if battler.trappedInBattle? # Trapping abilities/items ai.each_foe_battler(side) do |b, i| if b.ability_active? && Battle::AbilityEffects.triggerTrappingByTarget(b.ability, battler, b.battler, @ai.battle) return false end if b.item_active? && Battle::ItemEffects.triggerTrappingByTarget(b.item, battler, b.battler, @ai.battle) return false end end return true end #============================================================================= def wants_status_problem?(new_status) return true if new_status == :NONE if ability_active? case ability_id when :GUTS return true if stat_raise_worthwhile?(self, :ATTACK, true) when :MARVELSCALE return true if stat_raise_worthwhile?(self, :DEFENSE, true) when :QUICKFEET return true if stat_raise_worthwhile?(self, :SPEED, true) when :FLAREBOOST return true if new_status == :BURN && stat_raise_worthwhile?(self, :SPECIAL_ATTACK, true) when :TOXICBOOST return true if new_status == :POISON && stat_raise_worthwhile?(self, :ATTACK, true) when :POISONHEAL return true if new_status == :POISON when :MAGICGUARD # Want a harmless status problem to prevent getting a harmful one return true if new_status == :POISON || (new_status == :BURN && !stat_raise_worthwhile?(self, :ATTACK, true)) end end return true if new_status == :SLEEP && check_for_move { |m| m.usableWhenAsleep? } if has_move_with_function?("DoublePowerIfUserPoisonedBurnedParalyzed") return true if [:POISON, :BURN, :PARALYSIS].include?(new_status) end return false end #============================================================================= # TODO: Add more items. BASE_ITEM_RATINGS = { :ADAMANTORB => 3, :BLACKBELT => 2, :BLACKGLASSES => 2, :BLACKSLUDGE => -4, :CHARCOAL => 2, :CHOICEBAND => 4, :CHOICESCARF => 4, :CHOICESPECS => 4, :DEEPSEATOOTH => 4, :DRACOPLATE => 2, :DRAGONFANG => 2, :DREADPLATE => 2, :EARTHPLATE => 2, :FISTPLATE => 2, :FLAMEORB => -4, :FLAMEPLATE => 2, :GRISEOUSORB => 3, :HARDSTONE => 2, :ICICLEPLATE => 2, :INSECTPLATE => 2, :IRONBALL => -4, :IRONPLATE => 2, :LAGGINGTAIL => -2, :LEFTOVERS => 4, :LIFEORB => 3, :LIGHTBALL => 4, :LUSTROUSORB => 3, :MAGNET => 2, :MEADOWPLATE => 2, :METALCOAT => 2, :METRONOME => 1, :MINDPLATE => 2, :MIRACLESEED => 2, :MUSCLEBAND => 2, :MYSTICWATER => 2, :NEVERMELTICE => 2, :ODDINCENSE => 2, :PIXIEPLATE => 2, :POISONBARB => 2, :ROCKINCENSE => 2, :ROSEINCENSE => 2, :SEAINCENSE => 2, :SHARPBEAK => 2, :SILKSCARF => 2, :SILVERPOWDER => 2, :SKYPLATE => 2, :SOFTSAND => 2, :SOULDEW => 3, :SPELLTAG => 2, :SPLASHPLATE => 2, :SPOOKYPLATE => 2, :STICKYBARB => -2, :STONEPLATE => 2, :THICKCLUB => 4, :TOXICORB => -4, :TOXICPLATE => 2, :TWISTEDSPOON => 2, :WAVEINCENSE => 2, :WISEGLASSES => 2, :ZAPPLATE => 2 } # Returns a value indicating how beneficial the given item will be to this # battler if it is holding it. # Return values are typically between -10 and +10. 0 is indifferent, positive # values mean this battler benefits, negative values mean this battler suffers. def wants_item?(item) item = :NONE if !item item = item.id if !item.is_a?(Symbol) && item.respond_to?("id") return 0 if has_active_ability?(:KLUTZ) # TODO: Unnerve, other item-negating effects. ret = BASE_ITEM_RATINGS[item] || 0 # TODO: Add more context-sensitive modifications to the ratings from above. # Should they be moved into a handler? case item when :ADAMANTORB ret = 0 if !battler.isSpecies?(:DIALGA) || !has_damaging_move_of_type?(:DRAGON, :STEEL) when :BLACKBELT, :BLACKGLASSES, :CHARCOAL, :DRAGONFANG, :HARDSTONE, :MAGNET, :METALCOAT, :MIRACLESEED, :MYSTICWATER, :NEVERMELTICE, :POISONBARB, :SHARPBEAK, :SILKSCARF, :SILVERPOWDER, :SOFTSAND, :SPELLTAG, :TWISTEDSPOON, :DRACOPLATE, :DREADPLATE, :EARTHPLATE, :FISTPLATE, :FLAMEPLATE, :ICICLEPLATE, :INSECTPLATE, :IRONPLATE, :MEADOWPLATE, :MINDPLATE, :PIXIEPLATE, :SKYPLATE, :SPLASHPLATE, :SPOOKYPLATE, :STONEPLATE, :TOXICPLATE, :ZAPPLATE, :ODDINCENSE, :ROCKINCENSE, :ROSEINCENSE, :SEAINCENSE, :WAVEINCENSE boosted_type = { :BLACKBELT => :FIGHTING, :BLACKGLASSES => :DARK, :CHARCOAL => :FIRE, :DRAGONFANG => :DRAGON, :HARDSTONE => :ROCK, :MAGNET => :ELECTRIC, :METALCOAT => :STEEL, :MIRACLESEED => :GRASS, :MYSTICWATER => :WATER, :NEVERMELTICE => :ICE, :POISONBARB => :POISON, :SHARPBEAK => :FLYING, :SILKSCARF => :NORMAL, :SILVERPOWDER => :BUG, :SOFTSAND => :GROUND, :SPELLTAG => :GHOST, :TWISTEDSPOON => :PSYCHIC, :DRACOPLATE => :DRAGON, :DREADPLATE => :DARK, :EARTHPLATE => :GROUND, :FISTPLATE => :FIGHTING, :FLAMEPLATE => :FIRE, :ICICLEPLATE => :ICE, :INSECTPLATE => :BUG, :IRONPLATE => :STEEL, :MEADOWPLATE => :GRASS, :MINDPLATE => :PSYCHIC, :PIXIEPLATE => :FAIRY, :SKYPLATE => :FLYING, :SPLASHPLATE => :WATER, :SPOOKYPLATE => :GHOST, :STONEPLATE => :ROCK, :TOXICPLATE => :POISON, :ZAPPLATE => :ELECTRIC, :ODDINCENSE => :PSYCHIC, :ROCKINCENSE => :ROCK, :ROSEINCENSE => :GRASS, :SEAINCENSE => :WATER, :WAVEINCENSE => :WATER }[item] ret = 0 if !has_damaging_move_of_type?(boosted_type) when :BLACKSLUDGE ret = 4 if has_type?(:POISON) when :CHOICEBAND, :MUSCLEBAND ret = 0 if !check_for_move { |m| m.physicalMove?(m.type) } when :CHOICESPECS, :WISEGLASSES ret = 0 if !check_for_move { |m| m.specialMove?(m.type) } when :DEEPSEATOOTH ret = 0 if !battler.isSpecies?(:CLAMPERL) || !check_for_move { |m| m.specialMove?(m.type) } when :GRISEOUSORB ret = 0 if !battler.isSpecies?(:GIRATINA) || !has_damaging_move_of_type?(:DRAGON, :GHOST) when :IRONBALL ret = 0 if has_move_with_function?("ThrowUserItemAtTarget") when :LIGHTBALL ret = 0 if !battler.isSpecies?(:PIKACHU) || !check_for_move { |m| m.damagingMove? } when :LUSTROUSORB ret = 0 if !battler.isSpecies?(:PALKIA) || !has_damaging_move_of_type?(:DRAGON, :WATER) when :SOULDEW if !battler.isSpecies?(:LATIAS) && !battler.isSpecies?(:LATIOS) ret = 0 elsif Settings::SOUL_DEW_POWERS_UP_TYPES ret = 0 if !has_damaging_move_of_type?(:PSYCHIC, :DRAGON) else ret -= 2 if !check_for_move { |m| m.specialMove?(m.type) } # Also boosts SpDef end when :THICKCLUB ret = 0 if (!battler.isSpecies?(:CUBONE) && !battler.isSpecies?(:MAROWAK)) || !check_for_move { |m| m.physicalMove?(m.type) } end # Prefer if this battler knows Fling and it will do a lot of damage/have an # additional (negative) effect when flung if item != :NONE && has_move_with_function?("ThrowUserItemAtTarget") GameData::Item.get(item).flags.each do |flag| next if !flag[/^Fling_(\d+)$/i] amt = $~[1].to_i ret += 1 if amt >= 80 ret += 1 if amt >= 100 break end if [:FLAMEORB, :KINGSROCK, :LIGHTBALL, :POISONBARB, :RAZORFANG, :TOXICORB].include?(item) ret += 1 end 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, try_preserving_item = false) ret = 0 case item when :ORANBERRY, :BERRYJUICE, :ENIGMABERRY, :SITRUSBERRY # Healing ret += (hp > totalhp * 0.75) ? -6 : 6 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.0 / fraction_to_heal))) ? -6 : 6 ret = ret * 3 / 2 if GameData::Item.get(item).is_berry? && has_active_ability?(:RIPEN) # TODO: Check whether the item will cause confusion? 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) ? 6 : -6 when :PERSIMBERRY # Confusion cure ret += (self.effects[PBEffects::Confusion] > 1) ? 6 : -6 when :LUMBERRY # Any status/confusion cure ret += (status != :NONE || self.effects[PBEffects::Confusion] > 1) ? 6 : -6 when :MENTALHERB # Cure mental effects if self.effects[PBEffects::Attract] >= 0 || self.effects[PBEffects::Taunt] > 1 || self.effects[PBEffects::Encore] > 1 || self.effects[PBEffects::Torment] || self.effects[PBEffects::Disable] > 1 || self.effects[PBEffects::HealBlock] > 1 ret += 6 else ret -= 6 end 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 += (stat && @ai.stat_raise_worthwhile?(self, stat)) ? 8 : -8 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 ret += (battler.hasLoweredStatStages?) ? 8 : -8 when :MICLEBERRY # Raises accuracy of next move ret += (@ai.stat_raise_worthwhile?(self, :ACCURACY, true)) ? 6 : -6 when :LANSATBERRY # Focus energy ret += (self.effects[PBEffects::FocusEnergy] < 2) ? 6 : -6 when :LEPPABERRY # Restore PP ret += 6 ret = ret * 3 / 2 if GameData::Item.get(item).is_berry? && has_active_ability?(:RIPEN) end ret = 0 if ret < 0 && !try_preserving_item 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. # NOTE: This method assumes the ability isn't being negated. The calculations # that call this method separately check for it being negated, because # they need to do something special in that case. def wants_ability?(ability = :NONE) ability = ability.id if !ability.is_a?(Symbol) && ability.respond_to?("id") ret = BASE_ABILITY_RATINGS[ability] || 0 # TODO: Add more context-sensitive modifications to the ratings from above. # Should they be moved into a handler? case ability when :BLAZE ret = 0 if !has_damaging_move_of_type?(:FIRE) when :CUTECHARM, :RIVALRY ret = 0 if gender == 2 when :FRIENDGUARD, :HEALER, :SYMBOISIS, :TELEPATHY has_ally = false each_ally(@side) { |b, i| has_ally = true } ret = 0 if !has_ally when :GALEWINGS ret = 0 if !check_for_move { |m| m.type == :FLYING } when :HUGEPOWER, :PUREPOWER ret = 0 if !ai.stat_raise_worthwhile?(self, :ATTACK, true) when :IRONFIST ret = 0 if !check_for_move { |m| m.punchingMove? } when :LIQUIDVOICE ret = 0 if !check_for_move { |m| m.soundMove? } when :MEGALAUNCHER ret = 0 if !check_for_move { |m| m.pulseMove? } when :OVERGROW ret = 0 if !has_damaging_move_of_type?(:GRASS) when :PRANKSTER ret = 0 if !check_for_move { |m| m.statusMove? } when :PUNKROCK ret = 1 if !check_for_move { |m| m.damagingMove? && m.soundMove? } when :RECKLESS ret = 0 if !check_for_move { |m| m.recoilMove? } when :ROCKHEAD ret = 0 if !check_for_move { |m| m.recoilMove? && !m.is_a?(Battle::Move::CrashDamageIfFailsUnusableInGravity) } when :RUNAWAY ret = 0 if wild? when :SANDFORCE ret = 2 if !has_damaging_move_of_type?(:GROUND, :ROCK, :STEEL) when :SKILLLINK ret = 0 if !check_for_move { |m| m.is_a?(Battle::Move::HitTwoToFiveTimes) } when :STEELWORKER ret = 0 if !has_damaging_move_of_type?(:GRASS) when :SWARM ret = 0 if !has_damaging_move_of_type?(:BUG) when :TORRENT ret = 0 if !has_damaging_move_of_type?(:WATER) when :TRIAGE ret = 0 if !check_for_move { |m| m.healingMove? } end return ret end #============================================================================= private def effectiveness_of_type_against_single_battler_type(type, defend_type, user = nil) ret = Effectiveness.calculate(type, defend_type) if Effectiveness.ineffective_type?(type, defend_type) # Ring Target if has_active_item?(:RINGTARGET) ret = Effectiveness::NORMAL_EFFECTIVE_MULTIPLIER end # Foresight if (user&.has_active_ability?(:SCRAPPY) || self.effects[PBEffects::Foresight]) && defend_type == :GHOST ret = Effectiveness::NORMAL_EFFECTIVE_MULTIPLIER end # Miracle Eye if self.effects[PBEffects::MiracleEye] && defend_type == :DARK ret = Effectiveness::NORMAL_EFFECTIVE_MULTIPLIER end elsif Effectiveness.super_effective_type?(type, defend_type) # Delta Stream's weather if battler.effectiveWeather == :StrongWinds && defend_type == :FLYING ret = Effectiveness::NORMAL_EFFECTIVE_MULTIPLIER end end # Grounded Flying-type Pokémon become susceptible to Ground moves if !battler.airborne? && defend_type == :FLYING && type == :GROUND ret = Effectiveness::NORMAL_EFFECTIVE_MULTIPLIER end return ret end end