mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-06 06:01:46 +00:00
829 lines
33 KiB
Ruby
829 lines
33 KiB
Ruby
class PokeBattle_Battler
|
|
#=============================================================================
|
|
# Turn processing
|
|
#=============================================================================
|
|
def pbProcessTurn(choice, tryFlee = true)
|
|
return false if fainted?
|
|
# Wild roaming Pokémon always flee if possible
|
|
if tryFlee && @battle.wildBattle? && opposes? &&
|
|
@battle.rules["alwaysflee"] && @battle.pbCanRun?(@index)
|
|
pbBeginTurn(choice)
|
|
pbSEPlay("Battle flee")
|
|
@battle.pbDisplay(_INTL("{1} fled from battle!", pbThis))
|
|
@battle.decision = 3
|
|
pbEndTurn(choice)
|
|
return true
|
|
end
|
|
# Shift with the battler next to this one
|
|
if choice[0] == :Shift
|
|
idxOther = -1
|
|
case @battle.pbSideSize(@index)
|
|
when 2
|
|
idxOther = (@index + 2) % 4
|
|
when 3
|
|
if @index != 2 && @index != 3 # If not in middle spot already
|
|
idxOther = ((@index % 2) == 0) ? 2 : 3
|
|
end
|
|
end
|
|
if idxOther >= 0
|
|
@battle.pbSwapBattlers(@index, idxOther)
|
|
case @battle.pbSideSize(@index)
|
|
when 2
|
|
@battle.pbDisplay(_INTL("{1} moved across!", pbThis))
|
|
when 3
|
|
@battle.pbDisplay(_INTL("{1} moved to the center!", pbThis))
|
|
end
|
|
end
|
|
pbBeginTurn(choice)
|
|
pbCancelMoves
|
|
@lastRoundMoved = @battle.turnCount # Done something this round
|
|
return true
|
|
end
|
|
# If this battler's action for this round wasn't "use a move"
|
|
if choice[0] != :UseMove
|
|
# Clean up effects that end at battler's turn
|
|
pbBeginTurn(choice)
|
|
pbEndTurn(choice)
|
|
return false
|
|
end
|
|
# Turn is skipped if Pursuit was used during switch
|
|
if @effects[PBEffects::Pursuit]
|
|
@effects[PBEffects::Pursuit] = false
|
|
pbCancelMoves
|
|
pbEndTurn(choice)
|
|
@battle.pbJudge
|
|
return false
|
|
end
|
|
# Use the move
|
|
PBDebug.log("[Move usage] #{pbThis} started using #{choice[2].name}")
|
|
PBDebug.logonerr {
|
|
pbUseMove(choice, choice[2] == @battle.struggle)
|
|
}
|
|
@battle.pbJudge
|
|
# Update priority order
|
|
@battle.pbCalculatePriority if Settings::RECALCULATE_TURN_ORDER_AFTER_SPEED_CHANGES
|
|
return true
|
|
end
|
|
|
|
#=============================================================================
|
|
#
|
|
#=============================================================================
|
|
def pbBeginTurn(_choice)
|
|
# Cancel some lingering effects which only apply until the user next moves
|
|
@effects[PBEffects::BeakBlast] = false
|
|
@effects[PBEffects::DestinyBondPrevious] = @effects[PBEffects::DestinyBond]
|
|
@effects[PBEffects::DestinyBond] = false
|
|
@effects[PBEffects::Grudge] = false
|
|
@effects[PBEffects::MoveNext] = false
|
|
@effects[PBEffects::Quash] = 0
|
|
# Encore's effect ends if the encored move is no longer available
|
|
if @effects[PBEffects::Encore] > 0 && pbEncoredMoveIndex < 0
|
|
@effects[PBEffects::Encore] = 0
|
|
@effects[PBEffects::EncoreMove] = nil
|
|
end
|
|
end
|
|
|
|
# Called when the usage of various multi-turn moves is disrupted due to
|
|
# failing pbTryUseMove, being ineffective against all targets, or because
|
|
# Pursuit was used specially to intercept a switching foe.
|
|
# Cancels the use of multi-turn moves and counters thereof. Note that Hyper
|
|
# Beam's effect is NOT cancelled.
|
|
def pbCancelMoves(full_cancel = false)
|
|
# Outragers get confused anyway if they are disrupted during their final
|
|
# turn of using the move
|
|
if @effects[PBEffects::Outrage] == 1 && pbCanConfuseSelf?(false) && !full_cancel
|
|
pbConfuse(_INTL("{1} became confused due to fatigue!", pbThis))
|
|
end
|
|
# Cancel usage of most multi-turn moves
|
|
@effects[PBEffects::TwoTurnAttack] = nil
|
|
@effects[PBEffects::Rollout] = 0
|
|
@effects[PBEffects::Outrage] = 0
|
|
@effects[PBEffects::Uproar] = 0
|
|
@effects[PBEffects::Bide] = 0
|
|
@currentMove = nil
|
|
# Reset counters for moves which increase them when used in succession
|
|
@effects[PBEffects::FuryCutter] = 0
|
|
end
|
|
|
|
def pbEndTurn(_choice)
|
|
@lastRoundMoved = @battle.turnCount # Done something this round
|
|
if !@effects[PBEffects::ChoiceBand] &&
|
|
hasActiveItem?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF])
|
|
if @lastMoveUsed && pbHasMove?(@lastMoveUsed)
|
|
@effects[PBEffects::ChoiceBand] = @lastMoveUsed
|
|
elsif @lastRegularMoveUsed && pbHasMove?(@lastRegularMoveUsed)
|
|
@effects[PBEffects::ChoiceBand] = @lastRegularMoveUsed
|
|
end
|
|
end
|
|
@effects[PBEffects::BeakBlast] = false
|
|
@effects[PBEffects::Charge] = 0 if @effects[PBEffects::Charge] == 1
|
|
@effects[PBEffects::GemConsumed] = nil
|
|
@effects[PBEffects::ShellTrap] = false
|
|
@battle.eachBattler { |b| b.pbContinualAbilityChecks } # Trace, end primordial weathers
|
|
end
|
|
|
|
def pbConfusionDamage(msg)
|
|
@damageState.reset
|
|
@damageState.initialHP = @hp
|
|
confusionMove = PokeBattle_Confusion.new(@battle, nil)
|
|
confusionMove.calcType = confusionMove.pbCalcType(self) # nil
|
|
@damageState.typeMod = confusionMove.pbCalcTypeMod(confusionMove.calcType, self, self) # 8
|
|
confusionMove.pbCheckDamageAbsorption(self, self)
|
|
confusionMove.pbCalcDamage(self, self)
|
|
confusionMove.pbReduceDamage(self, self)
|
|
self.hp -= @damageState.hpLost
|
|
confusionMove.pbAnimateHitAndHPLost(self, [self])
|
|
@battle.pbDisplay(msg) # "It hurt itself in its confusion!"
|
|
confusionMove.pbRecordDamageLost(self, self)
|
|
confusionMove.pbEndureKOMessage(self)
|
|
pbFaint if fainted?
|
|
pbItemHPHealCheck
|
|
end
|
|
|
|
#=============================================================================
|
|
# Simple "use move" method, used when a move calls another move and for Future
|
|
# Sight's attack
|
|
#=============================================================================
|
|
def pbUseMoveSimple(moveID, target = -1, idxMove = -1, specialUsage = true)
|
|
choice = []
|
|
choice[0] = :UseMove # "Use move"
|
|
choice[1] = idxMove # Index of move to be used in user's moveset
|
|
if idxMove >= 0
|
|
choice[2] = @moves[idxMove]
|
|
else
|
|
choice[2] = PokeBattle_Move.from_pokemon_move(@battle, Pokemon::Move.new(moveID))
|
|
choice[2].pp = -1
|
|
end
|
|
choice[3] = target # Target (-1 means no target yet)
|
|
PBDebug.log("[Move usage] #{pbThis} started using the called/simple move #{choice[2].name}")
|
|
pbUseMove(choice, specialUsage)
|
|
end
|
|
|
|
#=============================================================================
|
|
# Master "use move" method
|
|
#=============================================================================
|
|
def pbUseMove(choice, specialUsage = false)
|
|
# NOTE: This is intentionally determined before a multi-turn attack can
|
|
# set specialUsage to true.
|
|
skipAccuracyCheck = (specialUsage && choice[2] != @battle.struggle)
|
|
# Start using the move
|
|
pbBeginTurn(choice)
|
|
# Force the use of certain moves if they're already being used
|
|
if usingMultiTurnAttack?
|
|
choice[2] = PokeBattle_Move.from_pokemon_move(@battle, Pokemon::Move.new(@currentMove))
|
|
specialUsage = true
|
|
elsif @effects[PBEffects::Encore] > 0 && choice[1] >= 0 &&
|
|
@battle.pbCanShowCommands?(@index)
|
|
idxEncoredMove = pbEncoredMoveIndex
|
|
if idxEncoredMove >= 0 && @battle.pbCanChooseMove?(@index, idxEncoredMove, false)
|
|
if choice[1] != idxEncoredMove # Change move if battler was Encored mid-round
|
|
choice[1] = idxEncoredMove
|
|
choice[2] = @moves[idxEncoredMove]
|
|
choice[3] = -1 # No target chosen
|
|
end
|
|
end
|
|
end
|
|
# Labels the move being used as "move"
|
|
move = choice[2]
|
|
return if !move # if move was not chosen somehow
|
|
# Try to use the move (inc. disobedience)
|
|
@lastMoveFailed = false
|
|
if !pbTryUseMove(choice, move, specialUsage, skipAccuracyCheck)
|
|
@lastMoveUsed = nil
|
|
@lastMoveUsedType = nil
|
|
if !specialUsage
|
|
@lastRegularMoveUsed = nil
|
|
@lastRegularMoveTarget = -1
|
|
end
|
|
@battle.pbGainExp # In case self is KO'd due to confusion
|
|
pbCancelMoves
|
|
pbEndTurn(choice)
|
|
return
|
|
end
|
|
move = choice[2] # In case disobedience changed the move to be used
|
|
return if !move # if move was not chosen somehow
|
|
# Subtract PP
|
|
if !specialUsage
|
|
if !pbReducePP(move)
|
|
@battle.pbDisplay(_INTL("{1} used {2}!", pbThis, move.name))
|
|
@battle.pbDisplay(_INTL("But there was no PP left for the move!"))
|
|
@lastMoveUsed = nil
|
|
@lastMoveUsedType = nil
|
|
@lastRegularMoveUsed = nil
|
|
@lastRegularMoveTarget = -1
|
|
@lastMoveFailed = true
|
|
pbCancelMoves
|
|
pbEndTurn(choice)
|
|
return
|
|
end
|
|
end
|
|
# Stance Change
|
|
if self.ability == :STANCECHANGE
|
|
if move.damagingMove?
|
|
user = pbFindUser(choice, move)
|
|
stanceChangeEffect(user, true)
|
|
elsif move.id == :KINGSSHIELD
|
|
user = pbFindUser(choice, move)
|
|
stanceChangeEffect(user, false)
|
|
end
|
|
end
|
|
# Calculate the move's type during this usage
|
|
move.calcType = move.pbCalcType(self)
|
|
# Start effect of Mold Breaker
|
|
@battle.moldBreaker = hasMoldBreaker?
|
|
# Remember that user chose a two-turn move
|
|
if move.pbIsChargingTurn?(self)
|
|
# Beginning the use of a two-turn attack
|
|
@effects[PBEffects::TwoTurnAttack] = move.id
|
|
@currentMove = move.id
|
|
else
|
|
@effects[PBEffects::TwoTurnAttack] = nil # Cancel use of two-turn attack
|
|
end
|
|
# Add to counters for moves which increase them when used in succession
|
|
move.pbChangeUsageCounters(self, specialUsage)
|
|
# Charge up Metronome item
|
|
if hasActiveItem?(:METRONOME) && !move.callsAnotherMove?
|
|
if @lastMoveUsed && @lastMoveUsed == move.id && !@lastMoveFailed
|
|
@effects[PBEffects::Metronome] += 1
|
|
else
|
|
@effects[PBEffects::Metronome] = 0
|
|
end
|
|
end
|
|
# Record move as having been used
|
|
@lastMoveUsed = move.id
|
|
@lastMoveUsedType = move.calcType # For Conversion 2
|
|
if !specialUsage
|
|
@lastRegularMoveUsed = move.id # For Disable, Encore, Instruct, Mimic, Mirror Move, Sketch, Spite
|
|
@lastRegularMoveTarget = choice[3] # For Instruct (remembering original target is fine)
|
|
@movesUsed.push(move.id) if !@movesUsed.include?(move.id) # For Last Resort
|
|
end
|
|
@battle.lastMoveUsed = move.id # For Copycat
|
|
@battle.lastMoveUser = @index # For "self KO" battle clause to avoid draws
|
|
@battle.successStates[@index].useState = 1 # Battle Arena - assume failure
|
|
# Find the default user (self or Snatcher) and target(s)
|
|
user = pbFindUser(choice, move)
|
|
user = pbChangeUser(choice, move, user)
|
|
targets = pbFindTargets(choice, move, user)
|
|
targets = pbChangeTargets(move, user, targets)
|
|
# Pressure
|
|
if !specialUsage
|
|
targets.each do |b|
|
|
next unless b.opposes?(user) && b.hasActiveAbility?(:PRESSURE)
|
|
PBDebug.log("[Ability triggered] #{b.pbThis}'s #{b.abilityName}")
|
|
user.pbReducePP(move)
|
|
end
|
|
if move.pbTarget(user).affects_foe_side
|
|
@battle.eachOtherSideBattler(user) do |b|
|
|
next unless b.hasActiveAbility?(:PRESSURE)
|
|
PBDebug.log("[Ability triggered] #{b.pbThis}'s #{b.abilityName}")
|
|
user.pbReducePP(move)
|
|
end
|
|
end
|
|
end
|
|
# Dazzling/Queenly Majesty make the move fail here
|
|
@battle.pbPriority(true).each do |b|
|
|
next if !b || !b.abilityActive?
|
|
if BattleHandlers.triggerMoveBlockingAbility(b.ability, b, user, targets, move, @battle)
|
|
@battle.pbDisplayBrief(_INTL("{1} used {2}!", user.pbThis, move.name))
|
|
@battle.pbShowAbilitySplash(b)
|
|
@battle.pbDisplay(_INTL("{1} cannot use {2}!", user.pbThis, move.name))
|
|
@battle.pbHideAbilitySplash(b)
|
|
user.lastMoveFailed = true
|
|
pbCancelMoves
|
|
pbEndTurn(choice)
|
|
return
|
|
end
|
|
end
|
|
# "X used Y!" message
|
|
# Can be different for Bide, Fling, Focus Punch and Future Sight
|
|
# NOTE: This intentionally passes self rather than user. The user is always
|
|
# self except if Snatched, but this message should state the original
|
|
# user (self) even if the move is Snatched.
|
|
move.pbDisplayUseMessage(self)
|
|
# Snatch's message (user is the new user, self is the original user)
|
|
if move.snatched
|
|
@lastMoveFailed = true # Intentionally applies to self, not user
|
|
@battle.pbDisplay(_INTL("{1} snatched {2}'s move!", user.pbThis, pbThis(true)))
|
|
end
|
|
# "But it failed!" checks
|
|
if move.pbMoveFailed?(user, targets)
|
|
PBDebug.log(sprintf("[Move failed] In function code %s's def pbMoveFailed?", move.function))
|
|
user.lastMoveFailed = true
|
|
pbCancelMoves
|
|
pbEndTurn(choice)
|
|
return
|
|
end
|
|
# Perform set-up actions and display messages
|
|
# Messages include Magnitude's number and Pledge moves' "it's a combo!"
|
|
move.pbOnStartUse(user, targets)
|
|
# Self-thawing due to the move
|
|
if user.status == :FROZEN && move.thawsUser?
|
|
user.pbCureStatus(false)
|
|
@battle.pbDisplay(_INTL("{1} melted the ice!", user.pbThis))
|
|
end
|
|
# Powder
|
|
if user.effects[PBEffects::Powder] && move.calcType == :FIRE
|
|
@battle.pbCommonAnimation("Powder", user)
|
|
@battle.pbDisplay(_INTL("When the flame touched the powder on the Pokémon, it exploded!"))
|
|
user.lastMoveFailed = true
|
|
if ![:Rain, :HeavyRain].include?(@battle.pbWeather) && user.takesIndirectDamage?
|
|
oldHP = user.hp
|
|
user.pbReduceHP((user.totalhp / 4.0).round, false)
|
|
user.pbFaint if user.fainted?
|
|
@battle.pbGainExp # In case user is KO'd by this
|
|
user.pbItemHPHealCheck
|
|
if user.pbAbilitiesOnDamageTaken(oldHP)
|
|
user.pbEffectsOnSwitchIn(true)
|
|
end
|
|
end
|
|
pbCancelMoves
|
|
pbEndTurn(choice)
|
|
return
|
|
end
|
|
# Primordial Sea, Desolate Land
|
|
if move.damagingMove?
|
|
case @battle.pbWeather
|
|
when :HeavyRain
|
|
if move.calcType == :FIRE
|
|
@battle.pbDisplay(_INTL("The Fire-type attack fizzled out in the heavy rain!"))
|
|
user.lastMoveFailed = true
|
|
pbCancelMoves
|
|
pbEndTurn(choice)
|
|
return
|
|
end
|
|
when :HarshSun
|
|
if move.calcType == :WATER
|
|
@battle.pbDisplay(_INTL("The Water-type attack evaporated in the harsh sunlight!"))
|
|
user.lastMoveFailed = true
|
|
pbCancelMoves
|
|
pbEndTurn(choice)
|
|
return
|
|
end
|
|
end
|
|
end
|
|
# Protean
|
|
if user.hasActiveAbility?(:PROTEAN) && !move.callsAnotherMove? && !move.snatched
|
|
if user.pbHasOtherType?(move.calcType) && !GameData::Type.get(move.calcType).pseudo_type
|
|
@battle.pbShowAbilitySplash(user)
|
|
user.pbChangeTypes(move.calcType)
|
|
typeName = GameData::Type.get(move.calcType).name
|
|
@battle.pbDisplay(_INTL("{1} transformed into the {2} type!", user.pbThis, typeName))
|
|
@battle.pbHideAbilitySplash(user)
|
|
# NOTE: The GF games say that if Curse is used by a non-Ghost-type
|
|
# Pokémon which becomes Ghost-type because of Protean, it should
|
|
# target and curse itself. I think this is silly, so I'm making it
|
|
# choose a random opponent to curse instead.
|
|
if move.function == "10D" && targets.length == 0 # Curse
|
|
choice[3] = -1
|
|
targets = pbFindTargets(choice, move, user)
|
|
end
|
|
end
|
|
end
|
|
#---------------------------------------------------------------------------
|
|
magicCoater = -1
|
|
magicBouncer = -1
|
|
if targets.length == 0 && move.pbTarget(user).num_targets > 0 && !move.worksWithNoTargets?
|
|
# def pbFindTargets should have found a target(s), but it didn't because
|
|
# they were all fainted
|
|
# All target types except: None, User, UserSide, FoeSide, BothSides
|
|
@battle.pbDisplay(_INTL("But there was no target..."))
|
|
user.lastMoveFailed = true
|
|
else
|
|
# We have targets, or move doesn't use targets
|
|
# Reset whole damage state, perform various success checks (not accuracy)
|
|
user.initialHP = user.hp
|
|
targets.each do |b|
|
|
b.damageState.reset
|
|
b.damageState.initialHP = b.hp
|
|
if !pbSuccessCheckAgainstTarget(move, user, b)
|
|
b.damageState.unaffected = true
|
|
end
|
|
end
|
|
# Magic Coat/Magic Bounce checks (for moves which don't target Pokémon)
|
|
if targets.length == 0 && move.canMagicCoat?
|
|
@battle.pbPriority(true).each do |b|
|
|
next if b.fainted? || !b.opposes?(user)
|
|
next if b.semiInvulnerable?
|
|
if b.effects[PBEffects::MagicCoat]
|
|
magicCoater = b.index
|
|
b.effects[PBEffects::MagicCoat] = false
|
|
break
|
|
elsif b.hasActiveAbility?(:MAGICBOUNCE) && !@battle.moldBreaker &&
|
|
!b.effects[PBEffects::MagicBounce]
|
|
magicBouncer = b.index
|
|
b.effects[PBEffects::MagicBounce] = true
|
|
break
|
|
end
|
|
end
|
|
end
|
|
# Get the number of hits
|
|
numHits = move.pbNumHits(user, targets)
|
|
# Process each hit in turn
|
|
realNumHits = 0
|
|
for i in 0...numHits
|
|
break if magicCoater >= 0 || magicBouncer >= 0
|
|
success = pbProcessMoveHit(move, user, targets, i, skipAccuracyCheck)
|
|
if !success
|
|
if i == 0 && targets.length > 0
|
|
hasFailed = false
|
|
targets.each do |t|
|
|
next if t.damageState.protected
|
|
hasFailed = t.damageState.unaffected
|
|
break if !t.damageState.unaffected
|
|
end
|
|
user.lastMoveFailed = hasFailed
|
|
end
|
|
break
|
|
end
|
|
realNumHits += 1
|
|
break if user.fainted?
|
|
break if [:SLEEP, :FROZEN].include?(user.status)
|
|
# NOTE: If a multi-hit move becomes disabled partway through doing those
|
|
# hits (e.g. by Cursed Body), the rest of the hits continue as
|
|
# normal.
|
|
break if !targets.any? { |t| !t.fainted? } # All targets are fainted
|
|
end
|
|
# Battle Arena only - attack is successful
|
|
@battle.successStates[user.index].useState = 2
|
|
if targets.length > 0
|
|
@battle.successStates[user.index].typeMod = 0
|
|
targets.each do |b|
|
|
next if b.damageState.unaffected
|
|
@battle.successStates[user.index].typeMod += b.damageState.typeMod
|
|
end
|
|
end
|
|
# Effectiveness message for multi-hit moves
|
|
# NOTE: No move is both multi-hit and multi-target, and the messages below
|
|
# aren't quite right for such a hypothetical move.
|
|
if numHits > 1
|
|
if move.damagingMove?
|
|
targets.each do |b|
|
|
next if b.damageState.unaffected || b.damageState.substitute
|
|
move.pbEffectivenessMessage(user, b, targets.length)
|
|
end
|
|
end
|
|
if realNumHits == 1
|
|
@battle.pbDisplay(_INTL("Hit 1 time!"))
|
|
elsif realNumHits > 1
|
|
@battle.pbDisplay(_INTL("Hit {1} times!", realNumHits))
|
|
end
|
|
end
|
|
# Magic Coat's bouncing back (move has targets)
|
|
targets.each do |b|
|
|
next if b.fainted?
|
|
next if !b.damageState.magicCoat && !b.damageState.magicBounce
|
|
@battle.pbShowAbilitySplash(b) if b.damageState.magicBounce
|
|
@battle.pbDisplay(_INTL("{1} bounced the {2} back!", b.pbThis, move.name))
|
|
@battle.pbHideAbilitySplash(b) if b.damageState.magicBounce
|
|
newChoice = choice.clone
|
|
newChoice[3] = user.index
|
|
newTargets = pbFindTargets(newChoice, move, b)
|
|
newTargets = pbChangeTargets(move, b, newTargets)
|
|
success = pbProcessMoveHit(move, b, newTargets, 0, false)
|
|
b.lastMoveFailed = true if !success
|
|
targets.each { |otherB| otherB.pbFaint if otherB && otherB.fainted? }
|
|
user.pbFaint if user.fainted?
|
|
end
|
|
# Magic Coat's bouncing back (move has no targets)
|
|
if magicCoater >= 0 || magicBouncer >= 0
|
|
mc = @battle.battlers[(magicCoater >= 0) ? magicCoater : magicBouncer]
|
|
if !mc.fainted?
|
|
user.lastMoveFailed = true
|
|
@battle.pbShowAbilitySplash(mc) if magicBouncer >= 0
|
|
@battle.pbDisplay(_INTL("{1} bounced the {2} back!", mc.pbThis, move.name))
|
|
@battle.pbHideAbilitySplash(mc) if magicBouncer >= 0
|
|
success = pbProcessMoveHit(move, mc, [], 0, false)
|
|
mc.lastMoveFailed = true if !success
|
|
targets.each { |b| b.pbFaint if b && b.fainted? }
|
|
user.pbFaint if user.fainted?
|
|
end
|
|
end
|
|
# Move-specific effects after all hits
|
|
targets.each { |b| move.pbEffectAfterAllHits(user, b) }
|
|
# Faint if 0 HP
|
|
targets.each { |b| b.pbFaint if b && b.fainted? }
|
|
user.pbFaint if user.fainted?
|
|
# External/general effects after all hits. Eject Button, Shell Bell, etc.
|
|
pbEffectsAfterMove(user, targets, move, realNumHits)
|
|
end
|
|
# End effect of Mold Breaker
|
|
@battle.moldBreaker = false
|
|
# Gain Exp
|
|
@battle.pbGainExp
|
|
# Battle Arena only - update skills
|
|
@battle.eachBattler { |b| @battle.successStates[b.index].updateSkill }
|
|
# Shadow Pokémon triggering Hyper Mode
|
|
pbHyperMode if @battle.choices[@index][0] != :None # Not if self is replaced
|
|
# End of move usage
|
|
pbEndTurn(choice)
|
|
# Instruct
|
|
@battle.eachBattler do |b|
|
|
next if !b.effects[PBEffects::Instruct] || !b.lastMoveUsed
|
|
b.effects[PBEffects::Instruct] = false
|
|
idxMove = -1
|
|
b.eachMoveWithIndex { |m, i| idxMove = i if m.id == b.lastMoveUsed }
|
|
next if idxMove < 0
|
|
oldLastRoundMoved = b.lastRoundMoved
|
|
@battle.pbDisplay(_INTL("{1} used the move instructed by {2}!", b.pbThis, user.pbThis(true)))
|
|
PBDebug.logonerr {
|
|
b.effects[PBEffects::Instructed] = true
|
|
b.pbUseMoveSimple(b.lastMoveUsed, b.lastRegularMoveTarget, idxMove, false)
|
|
b.effects[PBEffects::Instructed] = false
|
|
}
|
|
b.lastRoundMoved = oldLastRoundMoved
|
|
@battle.pbJudge
|
|
return if @battle.decision > 0
|
|
end
|
|
# Dancer
|
|
if !@effects[PBEffects::Dancer] && !user.lastMoveFailed && realNumHits > 0 &&
|
|
!move.snatched && magicCoater < 0 && @battle.pbCheckGlobalAbility(:DANCER) &&
|
|
move.danceMove?
|
|
dancers = []
|
|
@battle.pbPriority(true).each do |b|
|
|
dancers.push(b) if b.index != user.index && b.hasActiveAbility?(:DANCER)
|
|
end
|
|
while dancers.length > 0
|
|
nextUser = dancers.pop
|
|
oldLastRoundMoved = nextUser.lastRoundMoved
|
|
# NOTE: Petal Dance being used because of Dancer shouldn't lock the
|
|
# Dancer into using that move, and shouldn't contribute to its
|
|
# turn counter if it's already locked into Petal Dance.
|
|
oldOutrage = nextUser.effects[PBEffects::Outrage]
|
|
nextUser.effects[PBEffects::Outrage] += 1 if nextUser.effects[PBEffects::Outrage] > 0
|
|
oldCurrentMove = nextUser.currentMove
|
|
preTarget = choice[3]
|
|
preTarget = user.index if nextUser.opposes?(user) || !nextUser.opposes?(preTarget)
|
|
@battle.pbShowAbilitySplash(nextUser, true)
|
|
@battle.pbHideAbilitySplash(nextUser)
|
|
if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH
|
|
@battle.pbDisplay(_INTL("{1} kept the dance going with {2}!",
|
|
nextUser.pbThis, nextUser.abilityName))
|
|
end
|
|
PBDebug.logonerr {
|
|
nextUser.effects[PBEffects::Dancer] = true
|
|
nextUser.pbUseMoveSimple(move.id, preTarget)
|
|
nextUser.effects[PBEffects::Dancer] = false
|
|
}
|
|
nextUser.lastRoundMoved = oldLastRoundMoved
|
|
nextUser.effects[PBEffects::Outrage] = oldOutrage
|
|
nextUser.currentMove = oldCurrentMove
|
|
@battle.pbJudge
|
|
return if @battle.decision > 0
|
|
end
|
|
end
|
|
end
|
|
|
|
def stanceChangeEffect(user, attacking = false)
|
|
inSwordForm = user.effects[PBEffects::PowerTrick]
|
|
if !inSwordForm && attacking
|
|
user.effects[PBEffects::PowerTrick] = true
|
|
user.attack, user.defense = user.defense, user.attack
|
|
user.spatk, user.spdef = user.spdef, user.spatk
|
|
|
|
|
|
#changeForm(1,:AEGISLASH)
|
|
|
|
@battle.pbDisplay(_INTL("{1} changed to Sword Mode!", pbThis))
|
|
elsif inSwordForm && !attacking
|
|
user.effects[PBEffects::PowerTrick] = false
|
|
user.attack, user.defense = user.defense, user.attack
|
|
user.spatk, user.spdef = user.spdef, user.spatk
|
|
|
|
#user.changeForm(nil,:AEGISLASH)
|
|
|
|
@battle.pbDisplay(_INTL("{1} changed to Shield Mode!", pbThis))
|
|
end
|
|
end
|
|
|
|
def ensure_form_has_sprite(pokemon)
|
|
GameData::Species.sprite_filename(pokemon.dexNum)
|
|
end
|
|
|
|
|
|
def playChangeFormAnimation(animation = "UltraBurst2")
|
|
@battle.scene.pbChangePokemon(self, @pokemon)
|
|
@battle.scene.pbCommonAnimation(animation, self)
|
|
@battle.scene.pbRefreshOne(@index)
|
|
end
|
|
|
|
def changeSpecies(pokemon, speciesToReplace,newSpecies, animation = "UltraBurst2")
|
|
if pokemon.isFusion?()
|
|
replaceFusionSpecies(pokemon,speciesToReplace,newSpecies)
|
|
else
|
|
changeSpeciesSpecific(pokemon,newSpecies)
|
|
end
|
|
playChangeFormAnimation(animation)
|
|
end
|
|
|
|
|
|
|
|
# def changeUnfusedSpecies(pokemon, newSpecies, animation = "UltraBurst2")
|
|
#
|
|
# end
|
|
|
|
#For meloetta form change
|
|
|
|
def changeFormSpecies(oldForm, newForm,animation = "UltraBurst2")
|
|
|
|
@pokemon.changeFormSpecies(oldForm,newForm)
|
|
playChangeFormAnimation(animation)
|
|
|
|
# is_already_old_form = @pokemon.isFusionOf(oldForm) #A 466
|
|
# is_already_new_form = @pokemon.isFusionOf(newForm) #P
|
|
#
|
|
#
|
|
# #reverse the fusion if it's a meloA and meloP fusion
|
|
# # There's probably a smarter way to do this but laziness lol
|
|
# if is_already_old_form && is_already_new_form
|
|
# if @pokemon.species_data.get_body_species() == oldForm
|
|
# changeSpeciesSpecific(@pokemon,getFusedPokemonIdFromSymbols(newForm,oldForm))
|
|
# else
|
|
# changeSpeciesSpecific(@pokemon,getFusedPokemonIdFromSymbols(oldForm,newForm))
|
|
# end
|
|
# playChangeFormAnimation(animation)
|
|
# else
|
|
# changeSpecies(@pokemon, oldForm, newForm, animation) if is_already_old_form
|
|
# changeSpecies(@pokemon, newForm, oldForm, animation) if is_already_new_form
|
|
# end
|
|
end
|
|
|
|
|
|
def changeForm(newForm, formChangingSpecies, animation = "UltraBurst2")
|
|
spriteform_body = newForm if @pokemon.hasBodyOf?(formChangingSpecies)
|
|
spriteform_head = newForm if @pokemon.hasHeadOf?(formChangingSpecies)
|
|
|
|
#ensure_form_has_sprite(@pokemon)
|
|
|
|
if self.isFusion?
|
|
current_form_has_custom = customSpriteExistsSpecies(@pokemon.species)
|
|
new_form_has_custom = customSpriteExistsForm(@pokemon.species, spriteform_head, spriteform_body)
|
|
should_change_sprite = (current_form_has_custom && new_form_has_custom) || !current_form_has_custom
|
|
else
|
|
should_change_sprite=true
|
|
end
|
|
|
|
if should_change_sprite
|
|
@pokemon.spriteform_body = spriteform_body
|
|
@pokemon.spriteform_head = spriteform_head
|
|
end
|
|
playChangeFormAnimation(animation)
|
|
end
|
|
|
|
#=============================================================================
|
|
# Attack a single target
|
|
#=============================================================================
|
|
def pbProcessMoveHit(move, user, targets, hitNum, skipAccuracyCheck)
|
|
return false if user.fainted?
|
|
# For two-turn attacks being used in a single turn
|
|
move.pbInitialEffect(user, targets, hitNum)
|
|
numTargets = 0 # Number of targets that are affected by this hit
|
|
targets.each { |b| b.damageState.resetPerHit }
|
|
# Count a hit for Parental Bond (if it applies)
|
|
user.effects[PBEffects::ParentalBond] -= 1 if user.effects[PBEffects::ParentalBond] > 0
|
|
# Accuracy check (accuracy/evasion calc)
|
|
if hitNum == 0 || move.successCheckPerHit?
|
|
targets.each do |b|
|
|
next if b.damageState.unaffected
|
|
if pbSuccessCheckPerHit(move, user, b, skipAccuracyCheck)
|
|
numTargets += 1
|
|
else
|
|
b.damageState.missed = true
|
|
b.damageState.unaffected = true
|
|
end
|
|
end
|
|
# If failed against all targets
|
|
if targets.length > 0 && numTargets == 0 && !move.worksWithNoTargets?
|
|
targets.each do |b|
|
|
next if !b.damageState.missed || b.damageState.magicCoat
|
|
pbMissMessage(move, user, b)
|
|
end
|
|
move.pbCrashDamage(user)
|
|
user.pbItemHPHealCheck
|
|
pbCancelMoves
|
|
return false
|
|
end
|
|
end
|
|
# If we get here, this hit will happen and do something
|
|
#---------------------------------------------------------------------------
|
|
# Calculate damage to deal
|
|
if move.pbDamagingMove?
|
|
targets.each do |b|
|
|
next if b.damageState.unaffected
|
|
# Check whether Substitute/Disguise will absorb the damage
|
|
move.pbCheckDamageAbsorption(user, b)
|
|
# Calculate the damage against b
|
|
# pbCalcDamage shows the "eat berry" animation for SE-weakening
|
|
# berries, although the message about it comes after the additional
|
|
# effect below
|
|
move.pbCalcDamage(user, b, targets.length) # Stored in damageState.calcDamage
|
|
# Lessen damage dealt because of False Swipe/Endure/etc.
|
|
move.pbReduceDamage(user, b) # Stored in damageState.hpLost
|
|
end
|
|
end
|
|
# Show move animation (for this hit)
|
|
move.pbShowAnimation(move.id, user, targets, hitNum)
|
|
# Type-boosting Gem consume animation/message
|
|
if user.effects[PBEffects::GemConsumed] && hitNum == 0
|
|
# NOTE: The consume animation and message for Gems are shown now, but the
|
|
# actual removal of the item happens in def pbEffectsAfterMove.
|
|
@battle.pbCommonAnimation("UseItem", user)
|
|
@battle.pbDisplay(_INTL("The {1} strengthened {2}'s power!",
|
|
GameData::Item.get(user.effects[PBEffects::GemConsumed]).name, move.name))
|
|
end
|
|
# Messages about missed target(s) (relevant for multi-target moves only)
|
|
targets.each do |b|
|
|
next if !b.damageState.missed
|
|
pbMissMessage(move, user, b)
|
|
end
|
|
# Deal the damage (to all allies first simultaneously, then all foes
|
|
# simultaneously)
|
|
if move.pbDamagingMove?
|
|
# This just changes the HP amounts and does nothing else
|
|
targets.each do |b|
|
|
next if b.damageState.unaffected
|
|
move.pbInflictHPDamage(b)
|
|
end
|
|
# Animate the hit flashing and HP bar changes
|
|
move.pbAnimateHitAndHPLost(user, targets)
|
|
end
|
|
# Self-Destruct/Explosion's damaging and fainting of user
|
|
move.pbSelfKO(user) if hitNum == 0
|
|
user.pbFaint if user.fainted?
|
|
if move.pbDamagingMove?
|
|
targets.each do |b|
|
|
next if b.damageState.unaffected
|
|
# NOTE: This method is also used for the OKHO special message.
|
|
move.pbHitEffectivenessMessages(user, b, targets.length)
|
|
# Record data about the hit for various effects' purposes
|
|
move.pbRecordDamageLost(user, b)
|
|
end
|
|
# Close Combat/Superpower's stat-lowering, Flame Burst's splash damage,
|
|
# and Incinerate's berry destruction
|
|
targets.each do |b|
|
|
next if b.damageState.unaffected
|
|
move.pbEffectWhenDealingDamage(user, b)
|
|
end
|
|
# Ability/item effects such as Static/Rocky Helmet, and Grudge, etc.
|
|
targets.each do |b|
|
|
next if b.damageState.unaffected
|
|
pbEffectsOnMakingHit(move, user, b)
|
|
end
|
|
# Disguise/Endure/Sturdy/Focus Sash/Focus Band messages
|
|
targets.each do |b|
|
|
next if b.damageState.unaffected
|
|
move.pbEndureKOMessage(b)
|
|
end
|
|
# HP-healing held items (checks all battlers rather than just targets
|
|
# because Flame Burst's splash damage affects non-targets)
|
|
@battle.pbPriority(true).each { |b| b.pbItemHPHealCheck }
|
|
# Animate battlers fainting (checks all battlers rather than just targets
|
|
# because Flame Burst's splash damage affects non-targets)
|
|
@battle.pbPriority(true).each { |b| b.pbFaint if b && b.fainted? }
|
|
end
|
|
@battle.pbJudgeCheckpoint(user, move)
|
|
# Main effect (recoil/drain, etc.)
|
|
targets.each do |b|
|
|
next if b.damageState.unaffected
|
|
move.pbEffectAgainstTarget(user, b)
|
|
end
|
|
move.pbEffectGeneral(user)
|
|
targets.each { |b| b.pbFaint if b && b.fainted? }
|
|
user.pbFaint if user.fainted?
|
|
# Additional effect
|
|
if !user.hasActiveAbility?(:SHEERFORCE)
|
|
targets.each do |b|
|
|
next if b.damageState.calcDamage == 0
|
|
chance = move.pbAdditionalEffectChance(user, b)
|
|
next if chance <= 0
|
|
if @battle.pbRandom(100) < chance
|
|
move.pbAdditionalEffect(user, b)
|
|
end
|
|
end
|
|
end
|
|
# Make the target flinch (because of an item/ability)
|
|
targets.each do |b|
|
|
next if b.fainted?
|
|
next if b.damageState.calcDamage == 0 || b.damageState.substitute
|
|
chance = move.pbFlinchChance(user, b)
|
|
next if chance <= 0
|
|
if @battle.pbRandom(100) < chance
|
|
PBDebug.log("[Item/ability triggered] #{user.pbThis}'s King's Rock/Razor Fang or Stench")
|
|
b.pbFlinch(user)
|
|
end
|
|
end
|
|
# Message for and consuming of type-weakening berries
|
|
# NOTE: The "consume held item" animation for type-weakening berries occurs
|
|
# during pbCalcDamage above (before the move's animation), but the
|
|
# message about it only shows here.
|
|
targets.each do |b|
|
|
next if b.damageState.unaffected
|
|
next if !b.damageState.berryWeakened
|
|
@battle.pbDisplay(_INTL("The {1} weakened the damage to {2}!", b.itemName, b.pbThis(true)))
|
|
b.pbConsumeItem
|
|
end
|
|
targets.each { |b| b.pbFaint if b && b.fainted? }
|
|
user.pbFaint if user.fainted?
|
|
return true
|
|
end
|
|
end
|