class Battle::Move #============================================================================= # Move's type calculation #============================================================================= def pbBaseType(user) ret = @type if ret && user.abilityActive? ret = Battle::AbilityEffects.triggerModifyMoveBaseType(user.ability, user, self, ret) end return ret end def pbCalcType(user) @powerBoost = false ret = pbBaseType(user) if ret && GameData::Type.exists?(:ELECTRIC) if @battle.field.effects[PBEffects::IonDeluge] && ret == :NORMAL ret = :ELECTRIC @powerBoost = false end if user.effects[PBEffects::Electrify] ret = :ELECTRIC @powerBoost = false end end return ret end #============================================================================= # Type effectiveness calculation #============================================================================= def pbCalcTypeModSingle(moveType, defType, user, target) ret = Effectiveness.calculate(moveType, defType) if Effectiveness.ineffective_type?(moveType, defType) # Ring Target if target.hasActiveItem?(:RINGTARGET) ret = Effectiveness::NORMAL_EFFECTIVE_MULTIPLIER end # Foresight if (user.hasActiveAbility?(:SCRAPPY) || target.effects[PBEffects::Foresight]) && defType == :GHOST ret = Effectiveness::NORMAL_EFFECTIVE_MULTIPLIER end # Miracle Eye if target.effects[PBEffects::MiracleEye] && defType == :DARK ret = Effectiveness::NORMAL_EFFECTIVE_MULTIPLIER end elsif Effectiveness.super_effective_type?(moveType, defType) # Delta Stream's weather if target.effectiveWeather == :StrongWinds && defType == :FLYING ret = Effectiveness::NORMAL_EFFECTIVE_MULTIPLIER end end # Grounded Flying-type Pokémon become susceptible to Ground moves if !target.airborne? && defType == :FLYING && moveType == :GROUND ret = Effectiveness::NORMAL_EFFECTIVE_MULTIPLIER end return ret end def pbCalcTypeMod(moveType, user, target) ret = Effectiveness::NORMAL_EFFECTIVE_MULTIPLIER return ret if !moveType return ret if moveType == :GROUND && target.pbHasType?(:FLYING) && target.hasActiveItem?(:IRONBALL) # Get effectivenesses if moveType == :SHADOW if target.shadowPokemon? ret = Effectiveness::NOT_VERY_EFFECTIVE_MULTIPLIER else ret = Effectiveness::SUPER_EFFECTIVE_MULTIPLIER end else target.pbTypes(true).each do |type| ret *= pbCalcTypeModSingle(moveType, type, user, target) end ret *= 2 if target.effects[PBEffects::TarShot] && moveType == :FIRE end return ret end #============================================================================= # Accuracy check #============================================================================= def pbBaseAccuracy(user, target); return @accuracy; end # Accuracy calculations for one-hit KO moves are handled elsewhere. def pbAccuracyCheck(user, target) # "Always hit" effects and "always hit" accuracy return true if target.effects[PBEffects::Telekinesis] > 0 return true if target.effects[PBEffects::Minimize] && tramplesMinimize? && Settings::MECHANICS_GENERATION >= 6 baseAcc = pbBaseAccuracy(user, target) return true if baseAcc == 0 # Calculate all multiplier effects modifiers = {} modifiers[:base_accuracy] = baseAcc modifiers[:accuracy_stage] = user.stages[:ACCURACY] modifiers[:evasion_stage] = target.stages[:EVASION] modifiers[:accuracy_multiplier] = 1.0 modifiers[:evasion_multiplier] = 1.0 pbCalcAccuracyModifiers(user, target, modifiers) # Check if move can't miss return true if modifiers[:base_accuracy] == 0 # Calculation accStage = [[modifiers[:accuracy_stage], -6].max, 6].min + 6 evaStage = [[modifiers[:evasion_stage], -6].max, 6].min + 6 stageMul = [3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7, 8, 9] stageDiv = [9, 8, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3] accuracy = 100.0 * stageMul[accStage] / stageDiv[accStage] evasion = 100.0 * stageMul[evaStage] / stageDiv[evaStage] accuracy = (accuracy * modifiers[:accuracy_multiplier]).round evasion = (evasion * modifiers[:evasion_multiplier]).round evasion = 1 if evasion < 1 threshold = modifiers[:base_accuracy] * accuracy / evasion # Calculation r = @battle.pbRandom(100) if Settings::AFFECTION_EFFECTS && @battle.internalBattle && target.pbOwnedByPlayer? && target.affection_level == 5 && !target.mega? return true if r < threshold - 10 target.damageState.affection_missed = true if r < threshold return false end return r < threshold end def pbCalcAccuracyModifiers(user, target, modifiers) # Ability effects that alter accuracy calculation if user.abilityActive? Battle::AbilityEffects.triggerAccuracyCalcFromUser( user.ability, modifiers, user, target, self, @calcType ) end user.allAllies.each do |b| next if !b.abilityActive? Battle::AbilityEffects.triggerAccuracyCalcFromAlly( b.ability, modifiers, user, target, self, @calcType ) end if target.abilityActive? && !@battle.moldBreaker Battle::AbilityEffects.triggerAccuracyCalcFromTarget( target.ability, modifiers, user, target, self, @calcType ) end # Item effects that alter accuracy calculation if user.itemActive? Battle::ItemEffects.triggerAccuracyCalcFromUser( user.item, modifiers, user, target, self, @calcType ) end if target.itemActive? Battle::ItemEffects.triggerAccuracyCalcFromTarget( target.item, modifiers, user, target, self, @calcType ) end # Other effects, inc. ones that set accuracy_multiplier or evasion_stage to # specific values if @battle.field.effects[PBEffects::Gravity] > 0 modifiers[:accuracy_multiplier] *= 5 / 3.0 end if user.effects[PBEffects::MicleBerry] user.effects[PBEffects::MicleBerry] = false modifiers[:accuracy_multiplier] *= 1.2 end modifiers[:evasion_stage] = 0 if target.effects[PBEffects::Foresight] && modifiers[:evasion_stage] > 0 modifiers[:evasion_stage] = 0 if target.effects[PBEffects::MiracleEye] && modifiers[:evasion_stage] > 0 end #============================================================================= # Critical hit check #============================================================================= # Return values: # -1: Never a critical hit. # 0: Calculate normally. # 1: Always a critical hit. def pbCritialOverride(user, target); return 0; end # Returns whether the move will be a critical hit. def pbIsCritical?(user, target) return false if target.pbOwnSide.effects[PBEffects::LuckyChant] > 0 c = 0 # Ability effects that alter critical hit rate if c >= 0 && user.abilityActive? c = Battle::AbilityEffects.triggerCriticalCalcFromUser(user.ability, user, target, c) end if c >= 0 && target.abilityActive? && !@battle.moldBreaker c = Battle::AbilityEffects.triggerCriticalCalcFromTarget(target.ability, user, target, c) end # Item effects that alter critical hit rate if c >= 0 && user.itemActive? c = Battle::ItemEffects.triggerCriticalCalcFromUser(user.item, user, target, c) end if c >= 0 && target.itemActive? c = Battle::ItemEffects.triggerCriticalCalcFromTarget(target.item, user, target, c) end return false if c < 0 # Move-specific "always/never a critical hit" effects case pbCritialOverride(user, target) when 1 then return true when -1 then return false end # Other effects return true if c > 50 # Merciless return true if user.effects[PBEffects::LaserFocus] > 0 c += 1 if highCriticalRate? c += user.effects[PBEffects::FocusEnergy] c += 1 if user.inHyperMode? && @type == :SHADOW # Set up the critical hit ratios ratios = CRITICAL_HIT_RATIOS c = ratios.length - 1 if c >= ratios.length # Calculation return true if ratios[c] == 1 r = @battle.pbRandom(ratios[c]) return true if r == 0 if r == 1 && Settings::AFFECTION_EFFECTS && @battle.internalBattle && user.pbOwnedByPlayer? && user.affection_level == 5 && !target.mega? target.damageState.affection_critical = true return true end return false end #============================================================================= # Damage calculation #============================================================================= def pbBaseDamage(baseDmg, user, target); return baseDmg; end def pbBaseDamageMultiplier(damageMult, user, target); return damageMult; end def pbModifyDamage(damageMult, user, target); return damageMult; end def pbGetAttackStats(user, target) if specialMove? return user.spatk, user.stages[:SPECIAL_ATTACK] + 6 end return user.attack, user.stages[:ATTACK] + 6 end def pbGetDefenseStats(user, target) if specialMove? return target.spdef, target.stages[:SPECIAL_DEFENSE] + 6 end return target.defense, target.stages[:DEFENSE] + 6 end def pbCalcDamage(user, target, numTargets = 1) return if statusMove? if target.damageState.disguise || target.damageState.iceFace target.damageState.calcDamage = 1 return end stageMul = [2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8] stageDiv = [8, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2] # Get the move's type type = @calcType # nil is treated as physical # Calculate whether this hit deals critical damage target.damageState.critical = pbIsCritical?(user, target) # Calcuate base power of move baseDmg = pbBaseDamage(@baseDamage, user, target) # Calculate user's attack stat atk, atkStage = pbGetAttackStats(user, target) if !target.hasActiveAbility?(:UNAWARE) || @battle.moldBreaker atkStage = 6 if target.damageState.critical && atkStage < 6 atk = (atk.to_f * stageMul[atkStage] / stageDiv[atkStage]).floor end # Calculate target's defense stat defense, defStage = pbGetDefenseStats(user, target) if !user.hasActiveAbility?(:UNAWARE) defStage = 6 if target.damageState.critical && defStage > 6 defense = (defense.to_f * stageMul[defStage] / stageDiv[defStage]).floor end # Calculate all multiplier effects multipliers = { :base_damage_multiplier => 1.0, :attack_multiplier => 1.0, :defense_multiplier => 1.0, :final_damage_multiplier => 1.0 } pbCalcDamageMultipliers(user, target, numTargets, type, baseDmg, multipliers) # Main damage calculation baseDmg = [(baseDmg * 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 * baseDmg * atk / defense).floor / 50).floor + 2 damage = [(damage * multipliers[:final_damage_multiplier]).round, 1].max target.damageState.calcDamage = damage end def pbCalcDamageMultipliers(user, target, numTargets, type, baseDmg, multipliers) # Global abilities if (@battle.pbCheckGlobalAbility(:DARKAURA) && type == :DARK) || (@battle.pbCheckGlobalAbility(:FAIRYAURA) && type == :FAIRY) if @battle.pbCheckGlobalAbility(:AURABREAK) multipliers[:base_damage_multiplier] *= 2 / 3.0 else multipliers[:base_damage_multiplier] *= 4 / 3.0 end end # Ability effects that alter damage if user.abilityActive? Battle::AbilityEffects.triggerDamageCalcFromUser( user.ability, user, target, self, multipliers, baseDmg, type ) end if !@battle.moldBreaker # NOTE: It's odd that the user's Mold Breaker prevents its partner's # beneficial abilities (i.e. Flower Gift boosting Atk), but that's # how it works. user.allAllies.each do |b| next if !b.abilityActive? Battle::AbilityEffects.triggerDamageCalcFromAlly( b.ability, user, target, self, multipliers, baseDmg, type ) end if target.abilityActive? Battle::AbilityEffects.triggerDamageCalcFromTarget( target.ability, user, target, self, multipliers, baseDmg, type ) Battle::AbilityEffects.triggerDamageCalcFromTargetNonIgnorable( target.ability, user, target, self, multipliers, baseDmg, type ) end target.allAllies.each do |b| next if !b.abilityActive? Battle::AbilityEffects.triggerDamageCalcFromTargetAlly( b.ability, user, target, self, multipliers, baseDmg, type ) end end # Item effects that alter damage if user.itemActive? Battle::ItemEffects.triggerDamageCalcFromUser( user.item, user, target, self, multipliers, baseDmg, type ) end if target.itemActive? Battle::ItemEffects.triggerDamageCalcFromTarget( target.item, user, target, self, multipliers, baseDmg, type ) end # Parental Bond's second attack if user.effects[PBEffects::ParentalBond] == 1 multipliers[:base_damage_multiplier] /= (Settings::MECHANICS_GENERATION >= 7) ? 4 : 2 end # Other if user.effects[PBEffects::MeFirst] multipliers[:base_damage_multiplier] *= 1.5 end if user.effects[PBEffects::HelpingHand] && !self.is_a?(Battle::Move::Confusion) multipliers[:base_damage_multiplier] *= 1.5 end if user.effects[PBEffects::Charge] > 0 && type == :ELECTRIC multipliers[:base_damage_multiplier] *= 2 end # Mud Sport if type == :ELECTRIC if @battle.allBattlers.any? { |b| b.effects[PBEffects::MudSport] } multipliers[:base_damage_multiplier] /= 3 end if @battle.field.effects[PBEffects::MudSportField] > 0 multipliers[:base_damage_multiplier] /= 3 end end # Water Sport if type == :FIRE if @battle.allBattlers.any? { |b| b.effects[PBEffects::WaterSport] } multipliers[:base_damage_multiplier] /= 3 end if @battle.field.effects[PBEffects::WaterSportField] > 0 multipliers[:base_damage_multiplier] /= 3 end end # Terrain moves terrain_multiplier = (Settings::MECHANICS_GENERATION >= 8) ? 1.3 : 1.5 case @battle.field.terrain when :Electric multipliers[:base_damage_multiplier] *= terrain_multiplier if type == :ELECTRIC && user.affectedByTerrain? when :Grassy multipliers[:base_damage_multiplier] *= terrain_multiplier if type == :GRASS && user.affectedByTerrain? when :Psychic multipliers[:base_damage_multiplier] *= terrain_multiplier if type == :PSYCHIC && user.affectedByTerrain? when :Misty multipliers[:base_damage_multiplier] /= 2 if type == :DRAGON && target.affectedByTerrain? end # Badge multipliers if @battle.internalBattle if user.pbOwnedByPlayer? if physicalMove? && @battle.pbPlayer.badge_count >= Settings::NUM_BADGES_BOOST_ATTACK multipliers[:attack_multiplier] *= 1.1 elsif specialMove? && @battle.pbPlayer.badge_count >= Settings::NUM_BADGES_BOOST_SPATK multipliers[:attack_multiplier] *= 1.1 end end if target.pbOwnedByPlayer? if physicalMove? && @battle.pbPlayer.badge_count >= Settings::NUM_BADGES_BOOST_DEFENSE multipliers[:defense_multiplier] *= 1.1 elsif specialMove? && @battle.pbPlayer.badge_count >= Settings::NUM_BADGES_BOOST_SPDEF multipliers[:defense_multiplier] *= 1.1 end end end # Multi-targeting attacks if numTargets > 1 multipliers[:final_damage_multiplier] *= 0.75 end # Weather case user.effectiveWeather when :Sun, :HarshSun case type when :FIRE multipliers[:final_damage_multiplier] *= 1.5 when :WATER multipliers[:final_damage_multiplier] /= 2 end when :Rain, :HeavyRain case type when :FIRE multipliers[:final_damage_multiplier] /= 2 when :WATER multipliers[:final_damage_multiplier] *= 1.5 end when :Sandstorm if target.pbHasType?(:ROCK) && specialMove? && @function != "UseTargetDefenseInsteadOfTargetSpDef" multipliers[:defense_multiplier] *= 1.5 end end # Critical hits if target.damageState.critical if Settings::NEW_CRITICAL_HIT_RATE_MECHANICS multipliers[:final_damage_multiplier] *= 1.5 else multipliers[:final_damage_multiplier] *= 2 end end # Random variance if !self.is_a?(Battle::Move::Confusion) random = 85 + @battle.pbRandom(16) multipliers[:final_damage_multiplier] *= random / 100.0 end # STAB if type && user.pbHasType?(type) if user.hasActiveAbility?(:ADAPTABILITY) multipliers[:final_damage_multiplier] *= 2 else multipliers[:final_damage_multiplier] *= 1.5 end end # Type effectiveness multipliers[:final_damage_multiplier] *= target.damageState.typeMod # Burn if user.status == :BURN && physicalMove? && damageReducedByBurn? && !user.hasActiveAbility?(:GUTS) multipliers[:final_damage_multiplier] /= 2 end # Aurora Veil, Reflect, Light Screen if !ignoresReflect? && !target.damageState.critical && !user.hasActiveAbility?(:INFILTRATOR) if target.pbOwnSide.effects[PBEffects::AuroraVeil] > 0 if @battle.pbSideBattlerCount(target) > 1 multipliers[:final_damage_multiplier] *= 2 / 3.0 else multipliers[:final_damage_multiplier] /= 2 end elsif target.pbOwnSide.effects[PBEffects::Reflect] > 0 && physicalMove? if @battle.pbSideBattlerCount(target) > 1 multipliers[:final_damage_multiplier] *= 2 / 3.0 else multipliers[:final_damage_multiplier] /= 2 end elsif target.pbOwnSide.effects[PBEffects::LightScreen] > 0 && specialMove? if @battle.pbSideBattlerCount(target) > 1 multipliers[:final_damage_multiplier] *= 2 / 3.0 else multipliers[:final_damage_multiplier] /= 2 end end end # Minimize if target.effects[PBEffects::Minimize] && tramplesMinimize? multipliers[:final_damage_multiplier] *= 2 end # Move-specific base damage modifiers multipliers[:base_damage_multiplier] = pbBaseDamageMultiplier(multipliers[:base_damage_multiplier], user, target) # Move-specific final damage modifiers multipliers[:final_damage_multiplier] = pbModifyDamage(multipliers[:final_damage_multiplier], user, target) end #============================================================================= # Additional effect chance #============================================================================= def pbAdditionalEffectChance(user, target, effectChance = 0) return 0 if target.hasActiveAbility?(:SHIELDDUST) && !@battle.moldBreaker ret = (effectChance > 0) ? effectChance : @addlEffect if (Settings::MECHANICS_GENERATION >= 6 || @function != "EffectDependsOnEnvironment") && (user.hasActiveAbility?(:SERENEGRACE) || user.pbOwnSide.effects[PBEffects::Rainbow] > 0) ret *= 2 end ret = 100 if $DEBUG && Input.press?(Input::CTRL) return ret end # NOTE: Flinching caused by a move's effect is applied in that move's code, # not here. def pbFlinchChance(user, target) return 0 if flinchingMove? return 0 if target.hasActiveAbility?(:SHIELDDUST) && !@battle.moldBreaker ret = 0 if user.hasActiveAbility?(:STENCH, true) || user.hasActiveItem?([:KINGSROCK, :RAZORFANG], true) ret = 10 end ret *= 2 if user.hasActiveAbility?(:SERENEGRACE) || user.pbOwnSide.effects[PBEffects::Rainbow] > 0 return ret end end