Yet more script rearranging

This commit is contained in:
Maruno17
2021-04-17 23:45:42 +01:00
parent 96c68e79a3
commit 2ca8a42949
236 changed files with 923 additions and 928 deletions

View File

@@ -0,0 +1,214 @@
module PokeBattle_BattleCommon
#=============================================================================
# Store caught Pokémon
#=============================================================================
def pbStorePokemon(pkmn)
# Nickname the Pokémon (unless it's a Shadow Pokémon)
if !pkmn.shadowPokemon?
if pbDisplayConfirm(_INTL("Would you like to give a nickname to {1}?", pkmn.name))
nickname = @scene.pbNameEntry(_INTL("{1}'s nickname?", pkmn.speciesName), pkmn)
pkmn.name = nickname
end
end
# Store the Pokémon
currentBox = @peer.pbCurrentBox
storedBox = @peer.pbStorePokemon(pbPlayer,pkmn)
if storedBox<0
pbDisplayPaused(_INTL("{1} has been added to your party.",pkmn.name))
@initialItems[0][pbPlayer.party.length-1] = pkmn.item_id if @initialItems
return
end
# Messages saying the Pokémon was stored in a PC box
creator = @peer.pbGetStorageCreatorName
curBoxName = @peer.pbBoxName(currentBox)
boxName = @peer.pbBoxName(storedBox)
if storedBox!=currentBox
if creator
pbDisplayPaused(_INTL("Box \"{1}\" on {2}'s PC was full.",curBoxName,creator))
else
pbDisplayPaused(_INTL("Box \"{1}\" on someone's PC was full.",curBoxName))
end
pbDisplayPaused(_INTL("{1} was transferred to box \"{2}\".",pkmn.name,boxName))
else
if creator
pbDisplayPaused(_INTL("{1} was transferred to {2}'s PC.",pkmn.name,creator))
else
pbDisplayPaused(_INTL("{1} was transferred to someone's PC.",pkmn.name))
end
pbDisplayPaused(_INTL("It was stored in box \"{1}\".",boxName))
end
end
# Register all caught Pokémon in the Pokédex, and store them.
def pbRecordAndStoreCaughtPokemon
@caughtPokemon.each do |pkmn|
pbPlayer.pokedex.register(pkmn) # In case the form changed upon leaving battle
# Record the Pokémon's species as owned in the Pokédex
if !pbPlayer.hasOwned?(pkmn.species)
pbPlayer.pokedex.set_owned(pkmn.species)
if $Trainer.has_pokedex
pbDisplayPaused(_INTL("{1}'s data was added to the Pokédex.",pkmn.name))
@scene.pbShowPokedex(pkmn.species)
end
end
# Record a Shadow Pokémon's species as having been caught
pbPlayer.pokedex.set_shadow_pokemon_owned(pkmn.species) if pkmn.shadowPokemon?
# Store caught Pokémon
pbStorePokemon(pkmn)
end
@caughtPokemon.clear
end
#=============================================================================
# Throw a Poké Ball
#=============================================================================
def pbThrowPokeBall(idxBattler,ball,catch_rate=nil,showPlayer=false)
# Determine which Pokémon you're throwing the Poké Ball at
battler = nil
if opposes?(idxBattler)
battler = @battlers[idxBattler]
else
battler = @battlers[idxBattler].pbDirectOpposing(true)
end
if battler.fainted?
battler.eachAlly do |b|
battler = b
break
end
end
# Messages
itemName = GameData::Item.get(ball).name
if battler.fainted?
if itemName.starts_with_vowel?
pbDisplay(_INTL("{1} threw an {2}!",pbPlayer.name,itemName))
else
pbDisplay(_INTL("{1} threw a {2}!",pbPlayer.name,itemName))
end
pbDisplay(_INTL("But there was no target..."))
return
end
if itemName.starts_with_vowel?
pbDisplayBrief(_INTL("{1} threw an {2}!",pbPlayer.name,itemName))
else
pbDisplayBrief(_INTL("{1} threw a {2}!",pbPlayer.name,itemName))
end
# Animation of opposing trainer blocking Poké Balls (unless it's a Snag Ball
# at a Shadow Pokémon)
if trainerBattle? && !(GameData::Item.get(ball).is_snag_ball? && battler.shadowPokemon?)
@scene.pbThrowAndDeflect(ball,1)
pbDisplay(_INTL("The Trainer blocked your Poké Ball! Don't be a thief!"))
return
end
# Calculate the number of shakes (4=capture)
pkmn = battler.pokemon
@criticalCapture = false
numShakes = pbCaptureCalc(pkmn,battler,catch_rate,ball)
PBDebug.log("[Threw Poké Ball] #{itemName}, #{numShakes} shakes (4=capture)")
# Animation of Ball throw, absorb, shake and capture/burst out
@scene.pbThrow(ball,numShakes,@criticalCapture,battler.index,showPlayer)
# Outcome message
case numShakes
when 0
pbDisplay(_INTL("Oh no! The Pokémon broke free!"))
BallHandlers.onFailCatch(ball,self,battler)
when 1
pbDisplay(_INTL("Aww! It appeared to be caught!"))
BallHandlers.onFailCatch(ball,self,battler)
when 2
pbDisplay(_INTL("Aargh! Almost had it!"))
BallHandlers.onFailCatch(ball,self,battler)
when 3
pbDisplay(_INTL("Gah! It was so close, too!"))
BallHandlers.onFailCatch(ball,self,battler)
when 4
pbDisplayBrief(_INTL("Gotcha! {1} was caught!",pkmn.name))
@scene.pbThrowSuccess # Play capture success jingle
pbRemoveFromParty(battler.index,battler.pokemonIndex)
# Gain Exp
if Settings::GAIN_EXP_FOR_CAPTURE
battler.captured = true
pbGainExp
battler.captured = false
end
battler.pbReset
if pbAllFainted?(battler.index)
@decision = (trainerBattle?) ? 1 : 4 # Battle ended by win/capture
end
# Modify the Pokémon's properties because of the capture
if GameData::Item.get(ball).is_snag_ball?
pkmn.owner = Pokemon::Owner.new_from_trainer(pbPlayer)
end
BallHandlers.onCatch(ball,self,pkmn)
pkmn.poke_ball = ball
pkmn.makeUnmega if pkmn.mega?
pkmn.makeUnprimal
pkmn.update_shadow_moves if pkmn.shadowPokemon?
pkmn.record_first_moves
# Reset form
pkmn.forced_form = nil if MultipleForms.hasFunction?(pkmn.species,"getForm")
@peer.pbOnLeavingBattle(self,pkmn,true,true)
# Make the Poké Ball and data box disappear
@scene.pbHideCaptureBall(idxBattler)
# Save the Pokémon for storage at the end of battle
@caughtPokemon.push(pkmn)
end
end
#=============================================================================
# Calculate how many shakes a thrown Poké Ball will make (4 = capture)
#=============================================================================
def pbCaptureCalc(pkmn,battler,catch_rate,ball)
return 4 if $DEBUG && Input.press?(Input::CTRL)
# Get a catch rate if one wasn't provided
catch_rate = pkmn.species_data.catch_rate if !catch_rate
# Modify catch_rate depending on the Poké Ball's effect
ultraBeast = [:NIHILEGO, :BUZZWOLE, :PHEROMOSA, :XURKITREE, :CELESTEELA,
:KARTANA, :GUZZLORD, :POIPOLE, :NAGANADEL, :STAKATAKA,
:BLACEPHALON].include?(pkmn.species)
if !ultraBeast || ball == :BEASTBALL
catch_rate = BallHandlers.modifyCatchRate(ball,catch_rate,self,battler,ultraBeast)
else
catch_rate /= 10
end
# First half of the shakes calculation
a = battler.totalhp
b = battler.hp
x = ((3*a-2*b)*catch_rate.to_f)/(3*a)
# Calculation modifiers
if battler.status == :SLEEP || battler.status == :FROZEN
x *= 2.5
elsif battler.status != :NONE
x *= 1.5
end
x = x.floor
x = 1 if x<1
# Definite capture, no need to perform randomness checks
return 4 if x>=255 || BallHandlers.isUnconditional?(ball,self,battler)
# Second half of the shakes calculation
y = ( 65536 / ((255.0/x)**0.1875) ).floor
# Critical capture check
if Settings::ENABLE_CRITICAL_CAPTURES
c = 0
numOwned = $Trainer.pokedex.owned_count
if numOwned>600; c = x*5/12
elsif numOwned>450; c = x*4/12
elsif numOwned>300; c = x*3/12
elsif numOwned>150; c = x*2/12
elsif numOwned>30; c = x/12
end
# Calculate the number of shakes
if c>0 && pbRandom(256)<c
@criticalCapture = true
return 4 if pbRandom(65536)<y
return 0
end
end
# Calculate the number of shakes
numShakes = 0
for i in 0...4
break if numShakes<i
numShakes += 1 if pbRandom(65536)<y
end
return numShakes
end
end

View File

@@ -0,0 +1,785 @@
# Results of battle:
# 0 - Undecided or aborted
# 1 - Player won
# 2 - Player lost
# 3 - Player or wild Pokémon ran from battle, or player forfeited the match
# 4 - Wild Pokémon was caught
# 5 - Draw
# Possible actions a battler can take in a round:
# :None
# :UseMove
# :SwitchOut
# :UseItem
# :Call
# :Run
# :Shift
# NOTE: If you want to have more than 3 Pokémon on a side at once, you will need
# to edit some code. Mainly this is to change/add coordinates for the
# sprites, describe the relationships between Pokémon and trainers, and to
# change messages. The methods that will need editing are as follows:
# class PokeBattle_Battle
# def setBattleMode
# def pbGetOwnerIndexFromBattlerIndex
# def pbGetOpposingIndicesInOrder
# def nearBattlers?
# def pbStartBattleSendOut
# def pbEORShiftDistantBattlers
# def pbCanShift?
# def pbEndOfRoundPhase
# class TargetMenuDisplay
# def initialize
# class PokemonDataBox
# def initializeDataBoxGraphic
# module PokeBattle_SceneConstants
# def self.pbBattlerPosition
# def self.pbTrainerPosition
# class PokemonTemp
# def recordBattleRule
# (There is no guarantee that this list is complete.)
class PokeBattle_Battle
attr_reader :scene # Scene object for this battle
attr_reader :peer
attr_reader :field # Effects common to the whole of a battle
attr_reader :sides # Effects common to each side of a battle
attr_reader :positions # Effects that apply to a battler position
attr_reader :battlers # Currently active Pokémon
attr_reader :sideSizes # Array of number of battlers per side
attr_accessor :backdrop # Filename fragment used for background graphics
attr_accessor :backdropBase # Filename fragment used for base graphics
attr_accessor :time # Time of day (0=day, 1=eve, 2=night)
attr_accessor :environment # Battle surroundings (for mechanics purposes)
attr_reader :turnCount
attr_accessor :decision # Decision: 0=undecided; 1=win; 2=loss; 3=escaped; 4=caught
attr_reader :player # Player trainer (or array of trainers)
attr_reader :opponent # Opponent trainer (or array of trainers)
attr_accessor :items # Items held by opponents
attr_accessor :endSpeeches
attr_accessor :endSpeechesWin
attr_accessor :party1starts # Array of start indexes for each player-side trainer's party
attr_accessor :party2starts # Array of start indexes for each opponent-side trainer's party
attr_accessor :internalBattle # Internal battle flag
attr_accessor :debug # Debug flag
attr_accessor :canRun # True if player can run from battle
attr_accessor :canLose # True if player won't black out if they lose
attr_accessor :switchStyle # Switch/Set "battle style" option
attr_accessor :showAnims # "Battle Effects" option
attr_accessor :controlPlayer # Whether player's Pokémon are AI controlled
attr_accessor :expGain # Whether Pokémon can gain Exp/EVs
attr_accessor :moneyGain # Whether the player can gain/lose money
attr_accessor :rules
attr_accessor :choices # Choices made by each Pokémon this round
attr_accessor :megaEvolution # Battle index of each trainer's Pokémon to Mega Evolve
attr_reader :initialItems
attr_reader :recycleItems
attr_reader :belch
attr_reader :battleBond
attr_reader :usedInBattle # Whether each Pokémon was used in battle (for Burmy)
attr_reader :successStates # Success states
attr_accessor :lastMoveUsed # Last move used
attr_accessor :lastMoveUser # Last move user
attr_reader :switching # True if during the switching phase of the round
attr_reader :futureSight # True if Future Sight is hitting
attr_reader :endOfRound # True during the end of round
attr_accessor :moldBreaker # True if Mold Breaker applies
attr_reader :struggle # The Struggle move
include PokeBattle_BattleCommon
def pbRandom(x); return rand(x); end
#=============================================================================
# Creating the battle class
#=============================================================================
def initialize(scene,p1,p2,player,opponent)
if p1.length==0
raise ArgumentError.new(_INTL("Party 1 has no Pokémon."))
elsif p2.length==0
raise ArgumentError.new(_INTL("Party 2 has no Pokémon."))
end
@scene = scene
@peer = PokeBattle_BattlePeer.create
@battleAI = PokeBattle_AI.new(self)
@field = PokeBattle_ActiveField.new # Whole field (gravity/rooms)
@sides = [PokeBattle_ActiveSide.new, # Player's side
PokeBattle_ActiveSide.new] # Foe's side
@positions = [] # Battler positions
@battlers = []
@sideSizes = [1,1] # Single battle, 1v1
@backdrop = ""
@backdropBase = nil
@time = 0
@environment = :None # e.g. Tall grass, cave, still water
@turnCount = 0
@decision = 0
@caughtPokemon = []
player = [player] if !player.nil? && !player.is_a?(Array)
opponent = [opponent] if !opponent.nil? && !opponent.is_a?(Array)
@player = player # Array of Player/NPCTrainer objects, or nil
@opponent = opponent # Array of NPCTrainer objects, or nil
@items = nil
@endSpeeches = []
@endSpeechesWin = []
@party1 = p1
@party2 = p2
@party1order = Array.new(@party1.length) { |i| i }
@party2order = Array.new(@party2.length) { |i| i }
@party1starts = [0]
@party2starts = [0]
@internalBattle = true
@debug = false
@canRun = true
@canLose = false
@switchStyle = true
@showAnims = true
@controlPlayer = false
@expGain = true
@moneyGain = true
@rules = {}
@priority = []
@priorityTrickRoom = false
@choices = []
@megaEvolution = [
[-1] * (@player ? @player.length : 1),
[-1] * (@opponent ? @opponent.length : 1)
]
@initialItems = [
Array.new(@party1.length) { |i| (@party1[i]) ? @party1[i].item_id : nil },
Array.new(@party2.length) { |i| (@party2[i]) ? @party2[i].item_id : nil }
]
@recycleItems = [Array.new(@party1.length, nil), Array.new(@party2.length, nil)]
@belch = [Array.new(@party1.length, false), Array.new(@party2.length, false)]
@battleBond = [Array.new(@party1.length, false), Array.new(@party2.length, false)]
@usedInBattle = [Array.new(@party1.length, false), Array.new(@party2.length, false)]
@successStates = []
@lastMoveUsed = nil
@lastMoveUser = -1
@switching = false
@futureSight = false
@endOfRound = false
@moldBreaker = false
@runCommand = 0
@nextPickupUse = 0
if GameData::Move.exists?(:STRUGGLE)
@struggle = PokeBattle_Move.from_pokemon_move(self, Pokemon::Move.new(:STRUGGLE))
else
@struggle = PokeBattle_Struggle.new(self, nil)
end
end
#=============================================================================
# Information about the type and size of the battle
#=============================================================================
def wildBattle?; return @opponent.nil?; end
def trainerBattle?; return !@opponent.nil?; end
# Sets the number of battler slots on each side of the field independently.
# For "1v2" names, the first number is for the player's side and the second
# number is for the opposing side.
def setBattleMode(mode)
@sideSizes =
case mode
when "triple", "3v3" then [3, 3]
when "3v2" then [3, 2]
when "3v1" then [3, 1]
when "2v3" then [2, 3]
when "double", "2v2" then [2, 2]
when "2v1" then [2, 1]
when "1v3" then [1, 3]
when "1v2" then [1, 2]
else [1, 1] # Single, 1v1 (default)
end
end
def singleBattle?
return pbSideSize(0)==1 && pbSideSize(1)==1
end
def pbSideSize(index)
return @sideSizes[index%2]
end
def maxBattlerIndex
return (pbSideSize(0)>pbSideSize(1)) ? (pbSideSize(0)-1)*2 : pbSideSize(1)*2-1
end
#=============================================================================
# Trainers and owner-related methods
#=============================================================================
def pbPlayer; return @player[0]; end
# Given a battler index, returns the index within @player/@opponent of the
# trainer that controls that battler index.
# NOTE: You shouldn't ever have more trainers on a side than there are battler
# positions on that side. This method doesn't account for if you do.
def pbGetOwnerIndexFromBattlerIndex(idxBattler)
trainer = (opposes?(idxBattler)) ? @opponent : @player
return 0 if !trainer
case trainer.length
when 2
n = pbSideSize(idxBattler%2)
return [0,0,1][idxBattler/2] if n==3
return idxBattler/2 # Same as [0,1][idxBattler/2], i.e. 2 battler slots
when 3
return idxBattler/2
end
return 0
end
def pbGetOwnerFromBattlerIndex(idxBattler)
idxTrainer = pbGetOwnerIndexFromBattlerIndex(idxBattler)
return (opposes?(idxBattler)) ? @opponent[idxTrainer] : @player[idxTrainer]
end
def pbGetOwnerIndexFromPartyIndex(idxBattler,idxParty)
ret = -1
pbPartyStarts(idxBattler).each_with_index do |start,i|
break if start>idxParty
ret = i
end
return ret
end
# Only used for the purpose of an error message when one trainer tries to
# switch another trainer's Pokémon.
def pbGetOwnerFromPartyIndex(idxBattler,idxParty)
idxTrainer = pbGetOwnerIndexFromPartyIndex(idxBattler,idxParty)
return (opposes?(idxBattler)) ? @opponent[idxTrainer] : @player[idxTrainer]
end
def pbGetOwnerName(idxBattler)
idxTrainer = pbGetOwnerIndexFromBattlerIndex(idxBattler)
return @opponent[idxTrainer].full_name if opposes?(idxBattler) # Opponent
return @player[idxTrainer].full_name if idxTrainer>0 # Ally trainer
return @player[idxTrainer].name # Player
end
def pbGetOwnerItems(idxBattler)
return [] if !@items || !opposes?(idxBattler)
return @items[pbGetOwnerIndexFromBattlerIndex(idxBattler)]
end
# Returns whether the battler in position idxBattler is owned by the same
# trainer that owns the Pokémon in party slot idxParty. This assumes that
# both the battler position and the party slot are from the same side.
def pbIsOwner?(idxBattler,idxParty)
idxTrainer1 = pbGetOwnerIndexFromBattlerIndex(idxBattler)
idxTrainer2 = pbGetOwnerIndexFromPartyIndex(idxBattler,idxParty)
return idxTrainer1==idxTrainer2
end
def pbOwnedByPlayer?(idxBattler)
return false if opposes?(idxBattler)
return pbGetOwnerIndexFromBattlerIndex(idxBattler)==0
end
# Returns the number of Pokémon positions controlled by the given trainerIndex
# on the given side of battle.
def pbNumPositions(side,idxTrainer)
ret = 0
for i in 0...pbSideSize(side)
t = pbGetOwnerIndexFromBattlerIndex(i*2+side)
next if t!=idxTrainer
ret += 1
end
return ret
end
#=============================================================================
# Get party information (counts all teams on the same side)
#=============================================================================
def pbParty(idxBattler)
return (opposes?(idxBattler)) ? @party2 : @party1
end
def pbOpposingParty(idxBattler)
return (opposes?(idxBattler)) ? @party1 : @party2
end
def pbPartyOrder(idxBattler)
return (opposes?(idxBattler)) ? @party2order : @party1order
end
def pbPartyStarts(idxBattler)
return (opposes?(idxBattler)) ? @party2starts : @party1starts
end
# Returns the player's team in its display order. Used when showing the party
# screen.
def pbPlayerDisplayParty(idxBattler=0)
partyOrders = pbPartyOrder(idxBattler)
idxStart, _idxEnd = pbTeamIndexRangeFromBattlerIndex(idxBattler)
ret = []
eachInTeamFromBattlerIndex(idxBattler) { |pkmn,i| ret[partyOrders[i]-idxStart] = pkmn }
return ret
end
def pbAbleCount(idxBattler=0)
party = pbParty(idxBattler)
count = 0
party.each { |pkmn| count += 1 if pkmn && pkmn.able? }
return count
end
def pbAbleNonActiveCount(idxBattler=0)
party = pbParty(idxBattler)
inBattleIndices = []
eachSameSideBattler(idxBattler) { |b| inBattleIndices.push(b.pokemonIndex) }
count = 0
party.each_with_index do |pkmn,idxParty|
next if !pkmn || !pkmn.able?
next if inBattleIndices.include?(idxParty)
count += 1
end
return count
end
def pbAllFainted?(idxBattler=0)
return pbAbleCount(idxBattler)==0
end
# For the given side of the field (0=player's, 1=opponent's), returns an array
# containing the number of able Pokémon in each team.
def pbAbleTeamCounts(side)
party = pbParty(side)
partyStarts = pbPartyStarts(side)
ret = []
idxTeam = -1
nextStart = 0
party.each_with_index do |pkmn,i|
if i>=nextStart
idxTeam += 1
nextStart = (idxTeam<partyStarts.length-1) ? partyStarts[idxTeam+1] : party.length
end
next if !pkmn || !pkmn.able?
ret[idxTeam] = 0 if !ret[idxTeam]
ret[idxTeam] += 1
end
return ret
end
#=============================================================================
# Get team information (a team is only the Pokémon owned by a particular
# trainer)
#=============================================================================
def pbTeamIndexRangeFromBattlerIndex(idxBattler)
partyStarts = pbPartyStarts(idxBattler)
idxTrainer = pbGetOwnerIndexFromBattlerIndex(idxBattler)
idxPartyStart = partyStarts[idxTrainer]
idxPartyEnd = (idxTrainer<partyStarts.length-1) ? partyStarts[idxTrainer+1] : pbParty(idxBattler).length
return idxPartyStart, idxPartyEnd
end
def pbTeamLengthFromBattlerIndex(idxBattler)
idxPartyStart, idxPartyEnd = pbTeamIndexRangeFromBattlerIndex(idxBattler)
return idxPartyEnd-idxPartyStart
end
def eachInTeamFromBattlerIndex(idxBattler)
party = pbParty(idxBattler)
idxPartyStart, idxPartyEnd = pbTeamIndexRangeFromBattlerIndex(idxBattler)
party.each_with_index { |pkmn,i| yield pkmn,i if pkmn && i>=idxPartyStart && i<idxPartyEnd }
end
def eachInTeam(side,idxTrainer)
party = pbParty(side)
partyStarts = pbPartyStarts(side)
idxPartyStart = partyStarts[idxTrainer]
idxPartyEnd = (idxTrainer<partyStarts.length-1) ? partyStarts[idxTrainer+1] : party.length
party.each_with_index { |pkmn,i| yield pkmn,i if pkmn && i>=idxPartyStart && i<idxPartyEnd }
end
# Used for Illusion.
# NOTE: This cares about the temporary rearranged order of the team. That is,
# if you do some switching, the last Pokémon in the team could change
# and the Illusion could be a different Pokémon.
def pbLastInTeam(idxBattler)
party = pbParty(idxBattler)
partyOrders = pbPartyOrder(idxBattler)
idxPartyStart, idxPartyEnd = pbTeamIndexRangeFromBattlerIndex(idxBattler)
ret = -1
party.each_with_index do |pkmn,i|
next if i<idxPartyStart || i>=idxPartyEnd # Check the team only
next if !pkmn || !pkmn.able? # Can't copy a non-fainted Pokémon or egg
ret = i if partyOrders[i]>partyOrders[ret]
end
return ret
end
# Used to calculate money gained/lost after winning/losing a battle.
def pbMaxLevelInTeam(side,idxTrainer)
ret = 1
eachInTeam(side,idxTrainer) do |pkmn,_i|
ret = pkmn.level if pkmn.level>ret
end
return ret
end
#=============================================================================
# Iterate through battlers
#=============================================================================
def eachBattler
@battlers.each { |b| yield b if b && !b.fainted? }
end
def eachSameSideBattler(idxBattler=0)
idxBattler = idxBattler.index if idxBattler.respond_to?("index")
@battlers.each { |b| yield b if b && !b.fainted? && !b.opposes?(idxBattler) }
end
def eachOtherSideBattler(idxBattler=0)
idxBattler = idxBattler.index if idxBattler.respond_to?("index")
@battlers.each { |b| yield b if b && !b.fainted? && b.opposes?(idxBattler) }
end
def pbSideBattlerCount(idxBattler=0)
ret = 0
eachSameSideBattler(idxBattler) { |_b| ret += 1 }
return ret
end
def pbOpposingBattlerCount(idxBattler=0)
ret = 0
eachOtherSideBattler(idxBattler) { |_b| ret += 1 }
return ret
end
# This method only counts the player's Pokémon, not a partner trainer's.
def pbPlayerBattlerCount
ret = 0
eachSameSideBattler { |b| ret += 1 if b.pbOwnedByPlayer? }
return ret
end
def pbCheckGlobalAbility(abil)
eachBattler { |b| return b if b.hasActiveAbility?(abil) }
return nil
end
def pbCheckOpposingAbility(abil,idxBattler=0,nearOnly=false)
eachOtherSideBattler(idxBattler) do |b|
next if nearOnly && !b.near?(idxBattler)
return b if b.hasActiveAbility?(abil)
end
return nil
end
# Given a battler index, and using battle side sizes, returns an array of
# battler indices from the opposing side that are in order of most "opposite".
# Used when choosing a target and pressing up/down to move the cursor to the
# opposite side, and also when deciding which target to select first for some
# moves.
def pbGetOpposingIndicesInOrder(idxBattler)
case pbSideSize(0)
when 1
case pbSideSize(1)
when 1 # 1v1 single
return [0] if opposes?(idxBattler)
return [1]
when 2 # 1v2
return [0] if opposes?(idxBattler)
return [3,1]
when 3 # 1v3
return [0] if opposes?(idxBattler)
return [3,5,1]
end
when 2
case pbSideSize(1)
when 1 # 2v1
return [0,2] if opposes?(idxBattler)
return [1]
when 2 # 2v2 double
return [[3,1],[2,0],[1,3],[0,2]][idxBattler]
when 3 # 2v3
return [[5,3,1],[2,0],[3,1,5]][idxBattler] if idxBattler<3
return [0,2]
end
when 3
case pbSideSize(1)
when 1 # 3v1
return [2,0,4] if opposes?(idxBattler)
return [1]
when 2 # 3v2
return [[3,1],[2,4,0],[3,1],[2,0,4],[1,3]][idxBattler]
when 3 # 3v3 triple
return [[5,3,1],[4,2,0],[3,5,1],[2,0,4],[1,3,5],[0,2,4]][idxBattler]
end
end
return [idxBattler]
end
#=============================================================================
# Comparing the positions of two battlers
#=============================================================================
def opposes?(idxBattler1,idxBattler2=0)
idxBattler1 = idxBattler1.index if idxBattler1.respond_to?("index")
idxBattler2 = idxBattler2.index if idxBattler2.respond_to?("index")
return (idxBattler1&1)!=(idxBattler2&1)
end
def nearBattlers?(idxBattler1,idxBattler2)
return false if idxBattler1==idxBattler2
return true if pbSideSize(0)<=2 && pbSideSize(1)<=2
# Get all pairs of battler positions that are not close to each other
pairsArray = [[0,4],[1,5]] # Covers 3v1 and 1v3
case pbSideSize(0)
when 3
case pbSideSize(1)
when 3 # 3v3 (triple)
pairsArray.push([0,1])
pairsArray.push([4,5])
when 2 # 3v2
pairsArray.push([0,1])
pairsArray.push([3,4])
end
when 2 # 2v3
pairsArray.push([0,1])
pairsArray.push([2,5])
end
# See if any pair matches the two battlers being assessed
pairsArray.each do |pair|
return false if pair.include?(idxBattler1) && pair.include?(idxBattler2)
end
return true
end
#=============================================================================
# Altering a party or rearranging battlers
#=============================================================================
def pbRemoveFromParty(idxBattler,idxParty)
party = pbParty(idxBattler)
# Erase the Pokémon from the party
party[idxParty] = nil
# Rearrange the display order of the team to place the erased Pokémon last
# in it (to avoid gaps)
partyOrders = pbPartyOrder(idxBattler)
partyStarts = pbPartyStarts(idxBattler)
idxTrainer = pbGetOwnerIndexFromPartyIndex(idxBattler,idxParty)
idxPartyStart = partyStarts[idxTrainer]
idxPartyEnd = (idxTrainer<partyStarts.length-1) ? partyStarts[idxTrainer+1] : party.length
origPartyPos = partyOrders[idxParty] # Position of erased Pokémon initially
partyOrders[idxParty] = idxPartyEnd # Put erased Pokémon last in the team
party.each_with_index do |_pkmn,i|
next if i<idxPartyStart || i>=idxPartyEnd # Only check the team
next if partyOrders[i]<origPartyPos # Appeared before erased Pokémon
partyOrders[i] -= 1 # Appeared after erased Pokémon; bump it up by 1
end
end
def pbSwapBattlers(idxA,idxB)
return false if !@battlers[idxA] || !@battlers[idxB]
# Can't swap if battlers aren't owned by the same trainer
return false if opposes?(idxA,idxB)
return false if pbGetOwnerIndexFromBattlerIndex(idxA)!=pbGetOwnerIndexFromBattlerIndex(idxB)
@battlers[idxA], @battlers[idxB] = @battlers[idxB], @battlers[idxA]
@battlers[idxA].index, @battlers[idxB].index = @battlers[idxB].index, @battlers[idxA].index
@choices[idxA], @choices[idxB] = @choices[idxB], @choices[idxA]
@scene.pbSwapBattlerSprites(idxA,idxB)
# Swap the target of any battlers' effects that point at either of the
# swapped battlers, to ensure they still point at the correct target
# NOTE: LeechSeed is not swapped, because drained HP goes to whichever
# Pokémon is in the position that Leech Seed was used from.
# NOTE: PerishSongUser doesn't need to change, as it's only used to
# determine which side the Perish Song user was on, and a battler
# can't change sides.
effectsToSwap = [PBEffects::Attract,
PBEffects::BideTarget,
PBEffects::CounterTarget,
PBEffects::LockOnPos,
PBEffects::MeanLook,
PBEffects::MirrorCoatTarget,
PBEffects::SkyDrop,
PBEffects::TrappingUser]
eachBattler do |b|
for i in effectsToSwap
next if b.effects[i]!=idxA && b.effects[i]!=idxB
b.effects[i] = (b.effects[i]==idxA) ? idxB : idxA
end
end
return true
end
#=============================================================================
#
#=============================================================================
# Returns the battler representing the Pokémon at index idxParty in its party,
# on the same side as a battler with battler index of idxBattlerOther.
def pbFindBattler(idxParty,idxBattlerOther=0)
eachSameSideBattler(idxBattlerOther) { |b| return b if b.pokemonIndex==idxParty }
return nil
end
# Only used for Wish, as the Wishing Pokémon will no longer be in battle.
def pbThisEx(idxBattler,idxParty)
party = pbParty(idxBattler)
if opposes?(idxBattler)
return _INTL("The opposing {1}",party[idxParty].name) if trainerBattle?
return _INTL("The wild {1}",party[idxParty].name)
end
return _INTL("The ally {1}",party[idxParty].name) if !pbOwnedByPlayer?(idxBattler)
return party[idxParty].name
end
def pbSetSeen(battler)
return if !battler || !@internalBattle
pbPlayer.pokedex.register(battler.displaySpecies,battler.displayGender,battler.displayForm)
end
def nextPickupUse
@nextPickupUse += 1
return @nextPickupUse
end
#=============================================================================
# Weather and terrain
#=============================================================================
def defaultWeather=(value)
@field.defaultWeather = value
@field.weather = value
@field.weatherDuration = -1
end
# Returns the effective weather (note that weather effects can be negated)
def pbWeather
eachBattler { |b| return :None if b.hasActiveAbility?([:CLOUDNINE, :AIRLOCK]) }
return @field.weather
end
# Used for causing weather by a move or by an ability.
def pbStartWeather(user,newWeather,fixedDuration=false,showAnim=true)
return if @field.weather==newWeather
@field.weather = newWeather
duration = (fixedDuration) ? 5 : -1
if duration>0 && user && user.itemActive?
duration = BattleHandlers.triggerWeatherExtenderItem(user.item,
@field.weather,duration,user,self)
end
@field.weatherDuration = duration
weather_data = GameData::BattleWeather.try_get(@field.weather)
pbCommonAnimation(weather_data.animation) if showAnim && weather_data
pbHideAbilitySplash(user) if user
case @field.weather
when :Sun then pbDisplay(_INTL("The sunlight turned harsh!"))
when :Rain then pbDisplay(_INTL("It started to rain!"))
when :Sandstorm then pbDisplay(_INTL("A sandstorm brewed!"))
when :Hail then pbDisplay(_INTL("It started to hail!"))
when :HarshSun then pbDisplay(_INTL("The sunlight turned extremely harsh!"))
when :HeavyRain then pbDisplay(_INTL("A heavy rain began to fall!"))
when :StrongWinds then pbDisplay(_INTL("Mysterious strong winds are protecting Flying-type Pokémon!"))
when :ShadowSky then pbDisplay(_INTL("A shadow sky appeared!"))
end
# Check for end of primordial weather, and weather-triggered form changes
eachBattler { |b| b.pbCheckFormOnWeatherChange }
pbEndPrimordialWeather
end
def pbEndPrimordialWeather
oldWeather = @field.weather
# End Primordial Sea, Desolate Land, Delta Stream
case @field.weather
when :HarshSun
if !pbCheckGlobalAbility(:DESOLATELAND)
@field.weather = :None
pbDisplay("The harsh sunlight faded!")
end
when :HeavyRain
if !pbCheckGlobalAbility(:PRIMORDIALSEA)
@field.weather = :None
pbDisplay("The heavy rain has lifted!")
end
when :StrongWinds
if !pbCheckGlobalAbility(:DELTASTREAM)
@field.weather = :None
pbDisplay("The mysterious air current has dissipated!")
end
end
if @field.weather!=oldWeather
# Check for form changes caused by the weather changing
eachBattler { |b| b.pbCheckFormOnWeatherChange }
# Start up the default weather
pbStartWeather(nil,@field.defaultWeather) if @field.defaultWeather != :None
end
end
def defaultTerrain=(value)
@field.defaultTerrain = value
@field.terrain = value
@field.terrainDuration = -1
end
def pbStartTerrain(user,newTerrain,fixedDuration=true)
return if @field.terrain==newTerrain
@field.terrain = newTerrain
duration = (fixedDuration) ? 5 : -1
if duration>0 && user && user.itemActive?
duration = BattleHandlers.triggerTerrainExtenderItem(user.item,
newTerrain,duration,user,self)
end
@field.terrainDuration = duration
terrain_data = GameData::BattleTerrain.try_get(@field.terrain)
pbCommonAnimation(terrain_data.animation) if terrain_data
pbHideAbilitySplash(user) if user
case @field.terrain
when :Electric
pbDisplay(_INTL("An electric current runs across the battlefield!"))
when :Grassy
pbDisplay(_INTL("Grass grew to cover the battlefield!"))
when :Misty
pbDisplay(_INTL("Mist swirled about the battlefield!"))
when :Psychic
pbDisplay(_INTL("The battlefield got weird!"))
end
# Check for terrain seeds that boost stats in a terrain
eachBattler { |b| b.pbItemTerrainStatBoostCheck }
end
#=============================================================================
# Messages and animations
#=============================================================================
def pbDisplay(msg,&block)
@scene.pbDisplayMessage(msg,&block)
end
def pbDisplayBrief(msg)
@scene.pbDisplayMessage(msg,true)
end
def pbDisplayPaused(msg,&block)
@scene.pbDisplayPausedMessage(msg,&block)
end
def pbDisplayConfirm(msg)
return @scene.pbDisplayConfirmMessage(msg)
end
def pbShowCommands(msg,commands,canCancel=true)
@scene.pbShowCommands(msg,commands,canCancel)
end
def pbAnimation(move,user,targets,hitNum=0)
@scene.pbAnimation(move,user,targets,hitNum) if @showAnims
end
def pbCommonAnimation(name,user=nil,targets=nil)
@scene.pbCommonAnimation(name,user,targets) if @showAnims
end
def pbShowAbilitySplash(battler,delay=false,logTrigger=true)
PBDebug.log("[Ability triggered] #{battler.pbThis}'s #{battler.abilityName}") if logTrigger
return if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@scene.pbShowAbilitySplash(battler)
if delay
Graphics.frame_rate.times { @scene.pbUpdate } # 1 second
end
end
def pbHideAbilitySplash(battler)
return if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@scene.pbHideAbilitySplash(battler)
end
def pbReplaceAbilitySplash(battler)
return if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@scene.pbReplaceAbilitySplash(battler)
end
end

View File

@@ -0,0 +1,539 @@
class PokeBattle_Battle
class BattleAbortedException < Exception; end
def pbAbort
raise BattleAbortedException.new("Battle aborted")
end
#=============================================================================
# Makes sure all Pokémon exist that need to. Alter the type of battle if
# necessary. Will never try to create battler positions, only delete them
# (except for wild Pokémon whose number of positions are fixed). Reduces the
# size of each side by 1 and tries again. If the side sizes are uneven, only
# the larger side's size will be reduced by 1 each time, until both sides are
# an equal size (then both sides will be reduced equally).
#=============================================================================
def pbEnsureParticipants
# Prevent battles larger than 2v2 if both sides have multiple trainers
# NOTE: This is necessary to ensure that battlers can never become unable to
# hit each other due to being too far away. In such situations,
# battlers will move to the centre position at the end of a round, but
# because they cannot move into a position owned by a different
# trainer, it's possible that battlers will be unable to move close
# enough to hit each other if there are multiple trainers on each
# side.
if trainerBattle? && (@sideSizes[0]>2 || @sideSizes[1]>2) &&
@player.length>1 && @opponent.length>1
raise _INTL("Can't have battles larger than 2v2 where both sides have multiple trainers")
end
# Find out how many Pokémon each trainer has
side1counts = pbAbleTeamCounts(0)
side2counts = pbAbleTeamCounts(1)
# Change the size of the battle depending on how many wild Pokémon there are
if wildBattle? && side2counts[0]!=@sideSizes[1]
if @sideSizes[0]==@sideSizes[1]
# Even number of battlers per side, change both equally
@sideSizes = [side2counts[0],side2counts[0]]
else
# Uneven number of battlers per side, just change wild side's size
@sideSizes[1] = side2counts[0]
end
end
# Check if battle is possible, including changing the number of battlers per
# side if necessary
loop do
needsChanging = false
for side in 0...2 # Each side in turn
next if side==1 && wildBattle? # Wild side's size already checked above
sideCounts = (side==0) ? side1counts : side2counts
requireds = []
# Find out how many Pokémon each trainer on side needs to have
for i in 0...@sideSizes[side]
idxTrainer = pbGetOwnerIndexFromBattlerIndex(i*2+side)
requireds[idxTrainer] = 0 if requireds[idxTrainer].nil?
requireds[idxTrainer] += 1
end
# Compare the have values with the need values
if requireds.length>sideCounts.length
raise _INTL("Error: def pbGetOwnerIndexFromBattlerIndex gives invalid owner index ({1} for battle type {2}v{3}, trainers {4}v{5})",
requireds.length-1,@sideSizes[0],@sideSizes[1],side1counts.length,side2counts.length)
end
sideCounts.each_with_index do |_count,i|
if !requireds[i] || requireds[i]==0
raise _INTL("Player-side trainer {1} has no battler position for their Pokémon to go (trying {2}v{3} battle)",
i+1,@sideSizes[0],@sideSizes[1]) if side==0
raise _INTL("Opposing trainer {1} has no battler position for their Pokémon to go (trying {2}v{3} battle)",
i+1,@sideSizes[0],@sideSizes[1]) if side==1
end
next if requireds[i]<=sideCounts[i] # Trainer has enough Pokémon to fill their positions
if requireds[i]==1
raise _INTL("Player-side trainer {1} has no able Pokémon",i+1) if side==0
raise _INTL("Opposing trainer {1} has no able Pokémon",i+1) if side==1
end
# Not enough Pokémon, try lowering the number of battler positions
needsChanging = true
break
end
break if needsChanging
end
break if !needsChanging
# Reduce one or both side's sizes by 1 and try again
if wildBattle?
PBDebug.log("#{@sideSizes[0]}v#{@sideSizes[1]} battle isn't possible " +
"(#{side1counts} player-side teams versus #{side2counts[0]} wild Pokémon)")
newSize = @sideSizes[0]-1
else
PBDebug.log("#{@sideSizes[0]}v#{@sideSizes[1]} battle isn't possible " +
"(#{side1counts} player-side teams versus #{side2counts} opposing teams)")
newSize = @sideSizes.max-1
end
if newSize==0
raise _INTL("Couldn't lower either side's size any further, battle isn't possible")
end
for side in 0...2
next if side==1 && wildBattle? # Wild Pokémon's side size is fixed
next if @sideSizes[side]==1 || newSize>@sideSizes[side]
@sideSizes[side] = newSize
end
PBDebug.log("Trying #{@sideSizes[0]}v#{@sideSizes[1]} battle instead")
end
end
#=============================================================================
# Set up all battlers
#=============================================================================
def pbCreateBattler(idxBattler,pkmn,idxParty)
if !@battlers[idxBattler].nil?
raise _INTL("Battler index {1} already exists",idxBattler)
end
@battlers[idxBattler] = PokeBattle_Battler.new(self,idxBattler)
@positions[idxBattler] = PokeBattle_ActivePosition.new
pbClearChoice(idxBattler)
@successStates[idxBattler] = PokeBattle_SuccessState.new
@battlers[idxBattler].pbInitialize(pkmn,idxParty)
end
def pbSetUpSides
ret = [[],[]]
for side in 0...2
# Set up wild Pokémon
if side==1 && wildBattle?
pbParty(1).each_with_index do |pkmn,idxPkmn|
pbCreateBattler(2*idxPkmn+side,pkmn,idxPkmn)
# Changes the Pokémon's form upon entering battle (if it should)
@peer.pbOnEnteringBattle(self,pkmn,true)
pbSetSeen(@battlers[2*idxPkmn+side])
@usedInBattle[side][idxPkmn] = true
end
next
end
# Set up player's Pokémon and trainers' Pokémon
trainer = (side==0) ? @player : @opponent
requireds = []
# Find out how many Pokémon each trainer on side needs to have
for i in 0...@sideSizes[side]
idxTrainer = pbGetOwnerIndexFromBattlerIndex(i*2+side)
requireds[idxTrainer] = 0 if requireds[idxTrainer].nil?
requireds[idxTrainer] += 1
end
# For each trainer in turn, find the needed number of Pokémon for them to
# send out, and initialize them
battlerNumber = 0
trainer.each_with_index do |_t,idxTrainer|
ret[side][idxTrainer] = []
eachInTeam(side,idxTrainer) do |pkmn,idxPkmn|
next if !pkmn.able?
idxBattler = 2*battlerNumber+side
pbCreateBattler(idxBattler,pkmn,idxPkmn)
ret[side][idxTrainer].push(idxBattler)
battlerNumber += 1
break if ret[side][idxTrainer].length>=requireds[idxTrainer]
end
end
end
return ret
end
#=============================================================================
# Send out all battlers at the start of battle
#=============================================================================
def pbStartBattleSendOut(sendOuts)
# "Want to battle" messages
if wildBattle?
foeParty = pbParty(1)
case foeParty.length
when 1
pbDisplayPaused(_INTL("Oh! A wild {1} appeared!",foeParty[0].name))
when 2
pbDisplayPaused(_INTL("Oh! A wild {1} and {2} appeared!",foeParty[0].name,
foeParty[1].name))
when 3
pbDisplayPaused(_INTL("Oh! A wild {1}, {2} and {3} appeared!",foeParty[0].name,
foeParty[1].name,foeParty[2].name))
end
else # Trainer battle
case @opponent.length
when 1
pbDisplayPaused(_INTL("You are challenged by {1}!",@opponent[0].full_name))
when 2
pbDisplayPaused(_INTL("You are challenged by {1} and {2}!",@opponent[0].full_name,
@opponent[1].full_name))
when 3
pbDisplayPaused(_INTL("You are challenged by {1}, {2} and {3}!",
@opponent[0].full_name,@opponent[1].full_name,@opponent[2].full_name))
end
end
# Send out Pokémon (opposing trainers first)
for side in [1,0]
next if side==1 && wildBattle?
msg = ""
toSendOut = []
trainers = (side==0) ? @player : @opponent
# Opposing trainers and partner trainers's messages about sending out Pokémon
trainers.each_with_index do |t,i|
next if side==0 && i==0 # The player's message is shown last
msg += "\r\n" if msg.length>0
sent = sendOuts[side][i]
case sent.length
when 1
msg += _INTL("{1} sent out {2}!",t.full_name,@battlers[sent[0]].name)
when 2
msg += _INTL("{1} sent out {2} and {3}!",t.full_name,
@battlers[sent[0]].name,@battlers[sent[1]].name)
when 3
msg += _INTL("{1} sent out {2}, {3} and {4}!",t.full_name,
@battlers[sent[0]].name,@battlers[sent[1]].name,@battlers[sent[2]].name)
end
toSendOut.concat(sent)
end
# The player's message about sending out Pokémon
if side==0
msg += "\r\n" if msg.length>0
sent = sendOuts[side][0]
case sent.length
when 1
msg += _INTL("Go! {1}!",@battlers[sent[0]].name)
when 2
msg += _INTL("Go! {1} and {2}!",@battlers[sent[0]].name,@battlers[sent[1]].name)
when 3
msg += _INTL("Go! {1}, {2} and {3}!",@battlers[sent[0]].name,
@battlers[sent[1]].name,@battlers[sent[2]].name)
end
toSendOut.concat(sent)
end
pbDisplayBrief(msg) if msg.length>0
# The actual sending out of Pokémon
animSendOuts = []
toSendOut.each do |idxBattler|
animSendOuts.push([idxBattler,@battlers[idxBattler].pokemon])
end
pbSendOut(animSendOuts,true)
end
end
#=============================================================================
# Start a battle
#=============================================================================
def pbStartBattle
PBDebug.log("")
PBDebug.log("******************************************")
logMsg = "[Started battle] "
if @sideSizes[0]==1 && @sideSizes[1]==1
logMsg += "Single "
elsif @sideSizes[0]==2 && @sideSizes[1]==2
logMsg += "Double "
elsif @sideSizes[0]==3 && @sideSizes[1]==3
logMsg += "Triple "
else
logMsg += "#{@sideSizes[0]}v#{@sideSizes[1]} "
end
logMsg += "wild " if wildBattle?
logMsg += "trainer " if trainerBattle?
logMsg += "battle (#{@player.length} trainer(s) vs. "
logMsg += "#{pbParty(1).length} wild Pokémon)" if wildBattle?
logMsg += "#{@opponent.length} trainer(s))" if trainerBattle?
PBDebug.log(logMsg)
pbEnsureParticipants
begin
pbStartBattleCore
rescue BattleAbortedException
@decision = 0
@scene.pbEndBattle(@decision)
end
return @decision
end
def pbStartBattleCore
# Set up the battlers on each side
sendOuts = pbSetUpSides
# Create all the sprites and play the battle intro animation
@scene.pbStartBattle(self)
# Show trainers on both sides sending out Pokémon
pbStartBattleSendOut(sendOuts)
# Weather announcement
weather_data = GameData::BattleWeather.try_get(@field.weather)
pbCommonAnimation(weather_data.animation) if weather_data
case @field.weather
when :Sun then pbDisplay(_INTL("The sunlight is strong."))
when :Rain then pbDisplay(_INTL("It is raining."))
when :Sandstorm then pbDisplay(_INTL("A sandstorm is raging."))
when :Hail then pbDisplay(_INTL("Hail is falling."))
when :HarshSun then pbDisplay(_INTL("The sunlight is extremely harsh."))
when :HeavyRain then pbDisplay(_INTL("It is raining heavily."))
when :StrongWinds then pbDisplay(_INTL("The wind is strong."))
when :ShadowSky then pbDisplay(_INTL("The sky is shadowy."))
end
# Terrain announcement
terrain_data = GameData::BattleTerrain.try_get(@field.terrain)
pbCommonAnimation(terrain_data.animation) if terrain_data
case @field.terrain
when :Electric
pbDisplay(_INTL("An electric current runs across the battlefield!"))
when :Grassy
pbDisplay(_INTL("Grass is covering the battlefield!"))
when :Misty
pbDisplay(_INTL("Mist swirls about the battlefield!"))
when :Psychic
pbDisplay(_INTL("The battlefield is weird!"))
end
# Abilities upon entering battle
pbOnActiveAll
# Main battle loop
pbBattleLoop
end
#=============================================================================
# Main battle loop
#=============================================================================
def pbBattleLoop
@turnCount = 0
loop do # Now begin the battle loop
PBDebug.log("")
PBDebug.log("***Round #{@turnCount+1}***")
if @debug && @turnCount>=100
@decision = pbDecisionOnTime
PBDebug.log("")
PBDebug.log("***Undecided after 100 rounds, aborting***")
pbAbort
break
end
PBDebug.log("")
# Command phase
PBDebug.logonerr { pbCommandPhase }
break if @decision>0
# Attack phase
PBDebug.logonerr { pbAttackPhase }
break if @decision>0
# End of round phase
PBDebug.logonerr { pbEndOfRoundPhase }
break if @decision>0
@turnCount += 1
end
pbEndOfBattle
end
#=============================================================================
# End of battle
#=============================================================================
def pbGainMoney
return if !@internalBattle || !@moneyGain
# Money rewarded from opposing trainers
if trainerBattle?
tMoney = 0
@opponent.each_with_index do |t,i|
tMoney += pbMaxLevelInTeam(1, i) * t.base_money
end
tMoney *= 2 if @field.effects[PBEffects::AmuletCoin]
tMoney *= 2 if @field.effects[PBEffects::HappyHour]
oldMoney = pbPlayer.money
pbPlayer.money += tMoney
moneyGained = pbPlayer.money-oldMoney
if moneyGained>0
pbDisplayPaused(_INTL("You got ${1} for winning!",moneyGained.to_s_formatted))
end
end
# Pick up money scattered by Pay Day
if @field.effects[PBEffects::PayDay]>0
@field.effects[PBEffects::PayDay] *= 2 if @field.effects[PBEffects::AmuletCoin]
@field.effects[PBEffects::PayDay] *= 2 if @field.effects[PBEffects::HappyHour]
oldMoney = pbPlayer.money
pbPlayer.money += @field.effects[PBEffects::PayDay]
moneyGained = pbPlayer.money-oldMoney
if moneyGained>0
pbDisplayPaused(_INTL("You picked up ${1}!",moneyGained.to_s_formatted))
end
end
end
def pbLoseMoney
return if !@internalBattle || !@moneyGain
return if $game_switches[Settings::NO_MONEY_LOSS]
maxLevel = pbMaxLevelInTeam(0,0) # Player's Pokémon only, not partner's
multiplier = [8,16,24,36,48,64,80,100,120]
idxMultiplier = [pbPlayer.badge_count, multiplier.length - 1].min
tMoney = maxLevel*multiplier[idxMultiplier]
tMoney = pbPlayer.money if tMoney>pbPlayer.money
oldMoney = pbPlayer.money
pbPlayer.money -= tMoney
moneyLost = oldMoney-pbPlayer.money
if moneyLost>0
if trainerBattle?
pbDisplayPaused(_INTL("You gave ${1} to the winner...",moneyLost.to_s_formatted))
else
pbDisplayPaused(_INTL("You panicked and dropped ${1}...",moneyLost.to_s_formatted))
end
end
end
def pbEndOfBattle
oldDecision = @decision
@decision = 4 if @decision==1 && wildBattle? && @caughtPokemon.length>0
case oldDecision
##### WIN #####
when 1
PBDebug.log("")
PBDebug.log("***Player won***")
if trainerBattle?
@scene.pbTrainerBattleSuccess
case @opponent.length
when 1
pbDisplayPaused(_INTL("You defeated {1}!",@opponent[0].full_name))
when 2
pbDisplayPaused(_INTL("You defeated {1} and {2}!",@opponent[0].full_name,
@opponent[1].full_name))
when 3
pbDisplayPaused(_INTL("You defeated {1}, {2} and {3}!",@opponent[0].full_name,
@opponent[1].full_name,@opponent[2].full_name))
end
@opponent.each_with_index do |_t,i|
@scene.pbShowOpponent(i)
msg = (@endSpeeches[i] && @endSpeeches[i]!="") ? @endSpeeches[i] : "..."
pbDisplayPaused(msg.gsub(/\\[Pp][Nn]/,pbPlayer.name))
end
end
# Gain money from winning a trainer battle, and from Pay Day
pbGainMoney if @decision!=4
# Hide remaining trainer
@scene.pbShowOpponent(@opponent.length) if trainerBattle? && @caughtPokemon.length>0
##### LOSE, DRAW #####
when 2, 5
PBDebug.log("")
PBDebug.log("***Player lost***") if @decision==2
PBDebug.log("***Player drew with opponent***") if @decision==5
if @internalBattle
pbDisplayPaused(_INTL("You have no more Pokémon that can fight!"))
if trainerBattle?
case @opponent.length
when 1
pbDisplayPaused(_INTL("You lost against {1}!",@opponent[0].full_name))
when 2
pbDisplayPaused(_INTL("You lost against {1} and {2}!",
@opponent[0].full_name,@opponent[1].full_name))
when 3
pbDisplayPaused(_INTL("You lost against {1}, {2} and {3}!",
@opponent[0].full_name,@opponent[1].full_name,@opponent[2].full_name))
end
end
# Lose money from losing a battle
pbLoseMoney
pbDisplayPaused(_INTL("You blacked out!")) if !@canLose
elsif @decision==2
if @opponent
@opponent.each_with_index do |_t,i|
@scene.pbShowOpponent(i)
msg = (@endSpeechesWin[i] && @endSpeechesWin[i]!="") ? @endSpeechesWin[i] : "..."
pbDisplayPaused(msg.gsub(/\\[Pp][Nn]/,pbPlayer.name))
end
end
end
##### CAUGHT WILD POKÉMON #####
when 4
@scene.pbWildBattleSuccess if !Settings::GAIN_EXP_FOR_CAPTURE
end
# Register captured Pokémon in the Pokédex, and store them
pbRecordAndStoreCaughtPokemon
# Collect Pay Day money in a wild battle that ended in a capture
pbGainMoney if @decision==4
# Pass on Pokérus within the party
if @internalBattle
infected = []
$Trainer.party.each_with_index do |pkmn,i|
infected.push(i) if pkmn.pokerusStage==1
end
infected.each do |idxParty|
strain = $Trainer.party[idxParty].pokerusStrain
if idxParty>0 && $Trainer.party[idxParty-1].pokerusStage==0
$Trainer.party[idxParty-1].givePokerus(strain) if rand(3)==0 # 33%
end
if idxParty<$Trainer.party.length-1 && $Trainer.party[idxParty+1].pokerusStage==0
$Trainer.party[idxParty+1].givePokerus(strain) if rand(3)==0 # 33%
end
end
end
# Clean up battle stuff
@scene.pbEndBattle(@decision)
@battlers.each do |b|
next if !b
pbCancelChoice(b.index) # Restore unused items to Bag
BattleHandlers.triggerAbilityOnSwitchOut(b.ability,b,true) if b.abilityActive?
end
pbParty(0).each_with_index do |pkmn,i|
next if !pkmn
@peer.pbOnLeavingBattle(self,pkmn,@usedInBattle[0][i],true) # Reset form
pkmn.item = @initialItems[0][i]
end
return @decision
end
#=============================================================================
# Judging
#=============================================================================
def pbJudgeCheckpoint(user,move=nil); end
def pbDecisionOnTime
counts = [0,0]
hpTotals = [0,0]
for side in 0...2
pbParty(side).each do |pkmn|
next if !pkmn || !pkmn.able?
counts[side] += 1
hpTotals[side] += pkmn.hp
end
end
return 1 if counts[0]>counts[1] # Win (player has more able Pokémon)
return 2 if counts[0]<counts[1] # Loss (foe has more able Pokémon)
return 1 if hpTotals[0]>hpTotals[1] # Win (player has more HP in total)
return 2 if hpTotals[0]<hpTotals[1] # Loss (foe has more HP in total)
return 5 # Draw
end
# Unused
def pbDecisionOnTime2
counts = [0,0]
hpTotals = [0,0]
for side in 0...2
pbParty(side).each do |pkmn|
next if !pkmn || !pkmn.able?
counts[side] += 1
hpTotals[side] += 100*pkmn.hp/pkmn.totalhp
end
hpTotals[side] /= counts[side] if counts[side]>1
end
return 1 if counts[0]>counts[1] # Win (player has more able Pokémon)
return 2 if counts[0]<counts[1] # Loss (foe has more able Pokémon)
return 1 if hpTotals[0]>hpTotals[1] # Win (player has a bigger average HP %)
return 2 if hpTotals[0]<hpTotals[1] # Loss (foe has a bigger average HP %)
return 5 # Draw
end
def pbDecisionOnDraw; return 5; end # Draw
def pbJudge
fainted1 = pbAllFainted?(0)
fainted2 = pbAllFainted?(1)
if fainted1 && fainted2; @decision = pbDecisionOnDraw # Draw
elsif fainted1; @decision = 2 # Loss
elsif fainted2; @decision = 1 # Win
end
end
end

View File

@@ -0,0 +1,263 @@
class PokeBattle_Battle
#=============================================================================
# Gaining Experience
#=============================================================================
def pbGainExp
# Play wild victory music if it's the end of the battle (has to be here)
@scene.pbWildBattleSuccess if wildBattle? && pbAllFainted?(1) && !pbAllFainted?(0)
return if !@internalBattle || !@expGain
# Go through each battler in turn to find the Pokémon that participated in
# battle against it, and award those Pokémon Exp/EVs
expAll = (GameData::Item.exists?(:EXPALL) && $PokemonBag.pbHasItem?(:EXPALL))
p1 = pbParty(0)
@battlers.each do |b|
next unless b && b.opposes? # Can only gain Exp from fainted foes
next if b.participants.length==0
next unless b.fainted? || b.captured
# Count the number of participants
numPartic = 0
b.participants.each do |partic|
next unless p1[partic] && p1[partic].able? && pbIsOwner?(0,partic)
numPartic += 1
end
# Find which Pokémon have an Exp Share
expShare = []
if !expAll
eachInTeam(0,0) do |pkmn,i|
next if !pkmn.able?
next if !pkmn.hasItem?(:EXPSHARE) && GameData::Item.try_get(@initialItems[0][i]) != :EXPSHARE
expShare.push(i)
end
end
# Calculate EV and Exp gains for the participants
if numPartic>0 || expShare.length>0 || expAll
# Gain EVs and Exp for participants
eachInTeam(0,0) do |pkmn,i|
next if !pkmn.able?
next unless b.participants.include?(i) || expShare.include?(i)
pbGainEVsOne(i,b)
pbGainExpOne(i,b,numPartic,expShare,expAll)
end
# Gain EVs and Exp for all other Pokémon because of Exp All
if expAll
showMessage = true
eachInTeam(0,0) do |pkmn,i|
next if !pkmn.able?
next if b.participants.include?(i) || expShare.include?(i)
pbDisplayPaused(_INTL("Your party Pokémon in waiting also got Exp. Points!")) if showMessage
showMessage = false
pbGainEVsOne(i,b)
pbGainExpOne(i,b,numPartic,expShare,expAll,false)
end
end
end
# Clear the participants array
b.participants = []
end
end
def pbGainEVsOne(idxParty,defeatedBattler)
pkmn = pbParty(0)[idxParty] # The Pokémon gaining EVs from defeatedBattler
evYield = defeatedBattler.pokemon.evYield
# Num of effort points pkmn already has
evTotal = 0
GameData::Stat.each_main { |s| evTotal += pkmn.ev[s.id] }
# Modify EV yield based on pkmn's held item
if !BattleHandlers.triggerEVGainModifierItem(pkmn.item,pkmn,evYield)
BattleHandlers.triggerEVGainModifierItem(@initialItems[0][idxParty],pkmn,evYield)
end
# Double EV gain because of Pokérus
if pkmn.pokerusStage>=1 # Infected or cured
evYield.each_key { |stat| evYield[stat] *= 2 }
end
# Gain EVs for each stat in turn
if pkmn.shadowPokemon? && pkmn.saved_ev
pkmn.saved_ev.each_value { |e| evTotal += e }
GameData::Stat.each_main do |s|
evGain = evYield[s.id].clamp(0, Pokemon::EV_STAT_LIMIT - pkmn.ev[s.id] - pkmn.saved_ev[s.id])
evGain = evGain.clamp(0, Pokemon::EV_LIMIT - evTotal)
pkmn.saved_ev[s.id] += evGain
evTotal += evGain
end
else
GameData::Stat.each_main do |s|
evGain = evYield[s.id].clamp(0, Pokemon::EV_STAT_LIMIT - pkmn.ev[s.id])
evGain = evGain.clamp(0, Pokemon::EV_LIMIT - evTotal)
pkmn.ev[s.id] += evGain
evTotal += evGain
end
end
end
def pbGainExpOne(idxParty,defeatedBattler,numPartic,expShare,expAll,showMessages=true)
pkmn = pbParty(0)[idxParty] # The Pokémon gaining EVs from defeatedBattler
growth_rate = pkmn.growth_rate
# Don't bother calculating if gainer is already at max Exp
if pkmn.exp>=growth_rate.maximum_exp
pkmn.calc_stats # To ensure new EVs still have an effect
return
end
isPartic = defeatedBattler.participants.include?(idxParty)
hasExpShare = expShare.include?(idxParty)
level = defeatedBattler.level
# Main Exp calculation
exp = 0
a = level*defeatedBattler.pokemon.base_exp
if expShare.length>0 && (isPartic || hasExpShare)
if numPartic==0 # No participants, all Exp goes to Exp Share holders
exp = a / (Settings::SPLIT_EXP_BETWEEN_GAINERS ? expShare.length : 1)
elsif Settings::SPLIT_EXP_BETWEEN_GAINERS # Gain from participating and/or Exp Share
exp = a/(2*numPartic) if isPartic
exp += a/(2*expShare.length) if hasExpShare
else # Gain from participating and/or Exp Share (Exp not split)
exp = (isPartic) ? a : a/2
end
elsif isPartic # Participated in battle, no Exp Shares held by anyone
exp = a / (Settings::SPLIT_EXP_BETWEEN_GAINERS ? numPartic : 1)
elsif expAll # Didn't participate in battle, gaining Exp due to Exp All
# NOTE: Exp All works like the Exp Share from Gen 6+, not like the Exp All
# from Gen 1, i.e. Exp isn't split between all Pokémon gaining it.
exp = a/2
end
return if exp<=0
# Pokémon gain more Exp from trainer battles
exp = (exp*1.5).floor if trainerBattle?
# Scale the gained Exp based on the gainer's level (or not)
if Settings::SCALED_EXP_FORMULA
exp /= 5
levelAdjust = (2*level+10.0)/(pkmn.level+level+10.0)
levelAdjust = levelAdjust**5
levelAdjust = Math.sqrt(levelAdjust)
exp *= levelAdjust
exp = exp.floor
exp += 1 if isPartic || hasExpShare
else
exp /= 7
end
# Foreign Pokémon gain more Exp
isOutsider = (pkmn.owner.id != pbPlayer.id ||
(pkmn.owner.language != 0 && pkmn.owner.language != pbPlayer.language))
if isOutsider
if pkmn.owner.language != 0 && pkmn.owner.language != pbPlayer.language
exp = (exp*1.7).floor
else
exp = (exp*1.5).floor
end
end
# Modify Exp gain based on pkmn's held item
i = BattleHandlers.triggerExpGainModifierItem(pkmn.item,pkmn,exp)
if i<0
i = BattleHandlers.triggerExpGainModifierItem(@initialItems[0][idxParty],pkmn,exp)
end
exp = i if i>=0
# Make sure Exp doesn't exceed the maximum
expFinal = growth_rate.add_exp(pkmn.exp, exp)
expGained = expFinal-pkmn.exp
return if expGained<=0
# "Exp gained" message
if showMessages
if isOutsider
pbDisplayPaused(_INTL("{1} got a boosted {2} Exp. Points!",pkmn.name,expGained))
else
pbDisplayPaused(_INTL("{1} got {2} Exp. Points!",pkmn.name,expGained))
end
end
curLevel = pkmn.level
newLevel = growth_rate.level_from_exp(expFinal)
if newLevel<curLevel
debugInfo = "Levels: #{curLevel}->#{newLevel} | Exp: #{pkmn.exp}->#{expFinal} | gain: #{expGained}"
raise RuntimeError.new(
_INTL("{1}'s new level is less than its\r\ncurrent level, which shouldn't happen.\r\n[Debug: {2}]",
pkmn.name,debugInfo))
end
# Give Exp
if pkmn.shadowPokemon?
pkmn.exp += expGained
return
end
tempExp1 = pkmn.exp
battler = pbFindBattler(idxParty)
loop do # For each level gained in turn...
# EXP Bar animation
levelMinExp = growth_rate.minimum_exp_for_level(curLevel)
levelMaxExp = growth_rate.minimum_exp_for_level(curLevel + 1)
tempExp2 = (levelMaxExp<expFinal) ? levelMaxExp : expFinal
pkmn.exp = tempExp2
@scene.pbEXPBar(battler,levelMinExp,levelMaxExp,tempExp1,tempExp2)
tempExp1 = tempExp2
curLevel += 1
if curLevel>newLevel
# Gained all the Exp now, end the animation
pkmn.calc_stats
battler.pbUpdate(false) if battler
@scene.pbRefreshOne(battler.index) if battler
break
end
# Levelled up
pbCommonAnimation("LevelUp",battler) if battler
oldTotalHP = pkmn.totalhp
oldAttack = pkmn.attack
oldDefense = pkmn.defense
oldSpAtk = pkmn.spatk
oldSpDef = pkmn.spdef
oldSpeed = pkmn.speed
if battler && battler.pokemon
battler.pokemon.changeHappiness("levelup")
end
pkmn.calc_stats
battler.pbUpdate(false) if battler
@scene.pbRefreshOne(battler.index) if battler
pbDisplayPaused(_INTL("{1} grew to Lv. {2}!",pkmn.name,curLevel))
@scene.pbLevelUp(pkmn,battler,oldTotalHP,oldAttack,oldDefense,
oldSpAtk,oldSpDef,oldSpeed)
# Learn all moves learned at this level
moveList = pkmn.getMoveList
moveList.each { |m| pbLearnMove(idxParty,m[1]) if m[0]==curLevel }
end
end
#=============================================================================
# Learning a move
#=============================================================================
def pbLearnMove(idxParty,newMove)
pkmn = pbParty(0)[idxParty]
return if !pkmn
pkmnName = pkmn.name
battler = pbFindBattler(idxParty)
moveName = GameData::Move.get(newMove).name
# Find a space for the new move in pkmn's moveset and learn it
for i in 0...Pokemon::MAX_MOVES
m = pkmn.moves[i]
return if m && m.id==newMove # Already knows the new move
pkmn.moves[i] = Pokemon::Move.new(newMove)
battler.moves[i] = PokeBattle_Move.from_pokemon_move(self, pkmn.moves[i]) if battler
pbDisplay(_INTL("{1} learned {2}!",pkmnName,moveName)) { pbSEPlay("Pkmn move learnt") }
battler.pbCheckFormOnMovesetChange if battler
return
end
# pkmn already knows four moves, need to forget one to learn newMove
loop do
pbDisplayPaused(_INTL("{1} wants to learn {2}, but it already knows four moves.",pkmnName,moveName))
if pbDisplayConfirm(_INTL("Forget a move to learn {1}?",moveName))
pbDisplayPaused(_INTL("Which move should be forgotten?"))
forgetMove = @scene.pbForgetMove(pkmn,newMove)
if forgetMove>=0
oldMoveName = pkmn.moves[forgetMove].name
pkmn.moves[forgetMove] = Pokemon::Move.new(newMove) # Replaces current/total PP
battler.moves[forgetMove] = PokeBattle_Move.from_pokemon_move(self, pkmn.moves[forgetMove]) if battler
pbDisplayPaused(_INTL("1, 2, and... ... ... Ta-da!"))
pbDisplayPaused(_INTL("{1} forgot how to use {2}. And...",pkmnName,oldMoveName))
pbDisplay(_INTL("{1} learned {2}!",pkmnName,moveName)) { pbSEPlay("Pkmn move learnt") }
battler.pbCheckFormOnMovesetChange if battler
break
elsif pbDisplayConfirm(_INTL("Give up on learning {1}?",moveName))
pbDisplay(_INTL("{1} did not learn {2}.",pkmnName,moveName))
break
end
elsif pbDisplayConfirm(_INTL("Give up on learning {1}?",moveName))
pbDisplay(_INTL("{1} did not learn {2}.",pkmnName,moveName))
break
end
end
end
end

View File

@@ -0,0 +1,249 @@
class PokeBattle_Battle
#=============================================================================
# Choosing a move/target
#=============================================================================
def pbCanChooseMove?(idxBattler,idxMove,showMessages,sleepTalk=false)
battler = @battlers[idxBattler]
move = battler.moves[idxMove]
return false unless move
if move.pp==0 && move.total_pp>0 && !sleepTalk
pbDisplayPaused(_INTL("There's no PP left for this move!")) if showMessages
return false
end
if battler.effects[PBEffects::Encore]>0
idxEncoredMove = battler.pbEncoredMoveIndex
return false if idxEncoredMove>=0 && idxMove!=idxEncoredMove
end
return battler.pbCanChooseMove?(move,true,showMessages,sleepTalk)
end
def pbCanChooseAnyMove?(idxBattler,sleepTalk=false)
battler = @battlers[idxBattler]
battler.eachMoveWithIndex do |m,i|
next if m.pp==0 && m.total_pp>0 && !sleepTalk
if battler.effects[PBEffects::Encore]>0
idxEncoredMove = battler.pbEncoredMoveIndex
next if idxEncoredMove>=0 && i!=idxEncoredMove
end
next if !battler.pbCanChooseMove?(m,true,false,sleepTalk)
return true
end
return false
end
# Called when the Pokémon is Encored, or if it can't use any of its moves.
# Makes the Pokémon use the Encored move (if Encored), or Struggle.
def pbAutoChooseMove(idxBattler,showMessages=true)
battler = @battlers[idxBattler]
if battler.fainted?
pbClearChoice(idxBattler)
return true
end
# Encore
idxEncoredMove = battler.pbEncoredMoveIndex
if idxEncoredMove>=0 && pbCanChooseMove?(idxBattler,idxEncoredMove,false)
encoreMove = battler.moves[idxEncoredMove]
@choices[idxBattler][0] = :UseMove # "Use move"
@choices[idxBattler][1] = idxEncoredMove # Index of move to be used
@choices[idxBattler][2] = encoreMove # PokeBattle_Move object
@choices[idxBattler][3] = -1 # No target chosen yet
return true if singleBattle?
if pbOwnedByPlayer?(idxBattler)
if showMessages
pbDisplayPaused(_INTL("{1} has to use {2}!",battler.name,encoreMove.name))
end
return pbChooseTarget(battler,encoreMove)
end
return true
end
# Struggle
if pbOwnedByPlayer?(idxBattler) && showMessages
pbDisplayPaused(_INTL("{1} has no moves left!",battler.name))
end
@choices[idxBattler][0] = :UseMove # "Use move"
@choices[idxBattler][1] = -1 # Index of move to be used
@choices[idxBattler][2] = @struggle # Struggle PokeBattle_Move object
@choices[idxBattler][3] = -1 # No target chosen yet
return true
end
def pbRegisterMove(idxBattler,idxMove,showMessages=true)
battler = @battlers[idxBattler]
move = battler.moves[idxMove]
return false if !pbCanChooseMove?(idxBattler,idxMove,showMessages)
@choices[idxBattler][0] = :UseMove # "Use move"
@choices[idxBattler][1] = idxMove # Index of move to be used
@choices[idxBattler][2] = move # PokeBattle_Move object
@choices[idxBattler][3] = -1 # No target chosen yet
return true
end
def pbChoseMove?(idxBattler,moveID)
return false if !@battlers[idxBattler] || @battlers[idxBattler].fainted?
if @choices[idxBattler][0]==:UseMove && @choices[idxBattler][1]
return @choices[idxBattler][2].id == moveID
end
return false
end
def pbChoseMoveFunctionCode?(idxBattler,code)
return false if @battlers[idxBattler].fainted?
idxMove = @choices[idxBattler][1]
if @choices[idxBattler][0]==:UseMove && @choices[idxBattler][1]
return @choices[idxBattler][2].function == code
end
return false
end
def pbRegisterTarget(idxBattler,idxTarget)
@choices[idxBattler][3] = idxTarget # Set target of move
end
# Returns whether the idxTarget will be targeted by a move with target_data
# used by a battler in idxUser.
def pbMoveCanTarget?(idxUser,idxTarget,target_data)
return false if target_data.num_targets == 0
case target_data.id
when :NearAlly
return false if opposes?(idxUser,idxTarget)
return false if !nearBattlers?(idxUser,idxTarget)
when :UserOrNearAlly
return true if idxUser==idxTarget
return false if opposes?(idxUser,idxTarget)
return false if !nearBattlers?(idxUser,idxTarget)
when :UserAndAllies
return false if opposes?(idxUser,idxTarget)
when :NearFoe, :RandomNearFoe, :AllNearFoes
return false if !opposes?(idxUser,idxTarget)
return false if !nearBattlers?(idxUser,idxTarget)
when :Foe
return false if !opposes?(idxUser,idxTarget)
when :AllFoes
return false if !opposes?(idxUser,idxTarget)
when :NearOther, :AllNearOthers
return false if !nearBattlers?(idxUser,idxTarget)
when :Other
return false if idxUser==idxTarget
end
return true
end
#=============================================================================
# Turn order calculation (priority)
#=============================================================================
def pbCalculatePriority(fullCalc=false,indexArray=nil)
needRearranging = false
if fullCalc
@priorityTrickRoom = (@field.effects[PBEffects::TrickRoom]>0)
# Recalculate everything from scratch
randomOrder = Array.new(maxBattlerIndex+1) { |i| i }
(randomOrder.length-1).times do |i| # Can't use shuffle! here
r = i+pbRandom(randomOrder.length-i)
randomOrder[i], randomOrder[r] = randomOrder[r], randomOrder[i]
end
@priority.clear
for i in 0..maxBattlerIndex
b = @battlers[i]
next if !b
# [battler, speed, sub-priority, priority, tie-breaker order]
bArray = [b,b.pbSpeed,0,0,randomOrder[i]]
if @choices[b.index][0]==:UseMove || @choices[b.index][0]==:Shift
# Calculate move's priority
if @choices[b.index][0]==:UseMove
move = @choices[b.index][2]
pri = move.priority
if b.abilityActive?
pri = BattleHandlers.triggerPriorityChangeAbility(b.ability,b,move,pri)
end
bArray[3] = pri
@choices[b.index][4] = pri
end
# Calculate sub-priority (first/last within priority bracket)
# NOTE: Going fast beats going slow. A Pokémon with Stall and Quick
# Claw will go first in its priority bracket if Quick Claw
# triggers, regardless of Stall.
subPri = 0
# Abilities (Stall)
if b.abilityActive?
newSubPri = BattleHandlers.triggerPriorityBracketChangeAbility(b.ability,
b,subPri,self)
if subPri!=newSubPri
subPri = newSubPri
b.effects[PBEffects::PriorityAbility] = true
b.effects[PBEffects::PriorityItem] = false
end
end
# Items (Quick Claw, Custap Berry, Lagging Tail, Full Incense)
if b.itemActive?
newSubPri = BattleHandlers.triggerPriorityBracketChangeItem(b.item,
b,subPri,self)
if subPri!=newSubPri
subPri = newSubPri
b.effects[PBEffects::PriorityAbility] = false
b.effects[PBEffects::PriorityItem] = true
end
end
bArray[2] = subPri
end
@priority.push(bArray)
end
needRearranging = true
else
if (@field.effects[PBEffects::TrickRoom]>0)!=@priorityTrickRoom
needRearranging = true
@priorityTrickRoom = (@field.effects[PBEffects::TrickRoom]>0)
end
# Just recheck all battler speeds
@priority.each do |orderArray|
next if !orderArray
next if indexArray && !indexArray.include?(orderArray[0].index)
oldSpeed = orderArray[1]
orderArray[1] = orderArray[0].pbSpeed
needRearranging = true if orderArray[1]!=oldSpeed
end
end
# Reorder the priority array
if needRearranging
@priority.sort! { |a,b|
if a[3]!=b[3]
# Sort by priority (highest value first)
b[3]<=>a[3]
elsif a[2]!=b[2]
# Sort by sub-priority (highest value first)
b[2]<=>a[2]
elsif @priorityTrickRoom
# Sort by speed (lowest first), and use tie-breaker if necessary
(a[1]==b[1]) ? b[4]<=>a[4] : a[1]<=>b[1]
else
# Sort by speed (highest first), and use tie-breaker if necessary
(a[1]==b[1]) ? b[4]<=>a[4] : b[1]<=>a[1]
end
}
# Write the priority order to the debug log
logMsg = (fullCalc) ? "[Round order] " : "[Round order recalculated] "
comma = false
@priority.each do |orderArray|
logMsg += ", " if comma
logMsg += "#{orderArray[0].pbThis(comma)} (#{orderArray[0].index})"
comma = true
end
PBDebug.log(logMsg)
end
end
def pbPriority(onlySpeedSort=false)
ret = []
if onlySpeedSort
# Sort battlers by their speed stats and tie-breaker order only.
tempArray = []
@priority.each { |pArray| tempArray.push([pArray[0],pArray[1],pArray[4]]) }
tempArray.sort! { |a,b| (a[1]==b[1]) ? b[2]<=>a[2] : b[1]<=>a[1] }
tempArray.each { |tArray| ret.push(tArray[0]) }
else
# Sort battlers by priority, sub-priority and their speed. Ties are
# resolved in the same way each time this method is called in a round.
@priority.each { |pArray| ret.push(pArray[0]) if !pArray[0].fainted? }
end
return ret
end
end

View File

@@ -0,0 +1,412 @@
class PokeBattle_Battle
#=============================================================================
# Choosing Pokémon to switch
#=============================================================================
# Checks whether the replacement Pokémon (at party index idxParty) can enter
# battle.
# NOTE: Messages are only shown while in the party screen when choosing a
# command for the next round.
def pbCanSwitchLax?(idxBattler,idxParty,partyScene=nil)
return true if idxParty<0
party = pbParty(idxBattler)
return false if idxParty>=party.length
return false if !party[idxParty]
if party[idxParty].egg?
partyScene.pbDisplay(_INTL("An Egg can't battle!")) if partyScene
return false
end
if !pbIsOwner?(idxBattler,idxParty)
owner = pbGetOwnerFromPartyIndex(idxBattler,idxParty)
partyScene.pbDisplay(_INTL("You can't switch {1}'s Pokémon with one of yours!",
owner.name)) if partyScene
return false
end
if party[idxParty].fainted?
partyScene.pbDisplay(_INTL("{1} has no energy left to battle!",
party[idxParty].name)) if partyScene
return false
end
if pbFindBattler(idxParty,idxBattler)
partyScene.pbDisplay(_INTL("{1} is already in battle!",
party[idxParty].name)) if partyScene
return false
end
return true
end
# Check whether the currently active Pokémon (at battler index idxBattler) can
# switch out (and that its replacement at party index idxParty can switch in).
# NOTE: Messages are only shown while in the party screen when choosing a
# command for the next round.
def pbCanSwitch?(idxBattler,idxParty=-1,partyScene=nil)
# Check whether party Pokémon can switch in
return false if !pbCanSwitchLax?(idxBattler,idxParty,partyScene)
# Make sure another battler isn't already choosing to switch to the party
# Pokémon
eachSameSideBattler(idxBattler) do |b|
next if choices[b.index][0]!=:SwitchOut || choices[b.index][1]!=idxParty
partyScene.pbDisplay(_INTL("{1} has already been selected.",
pbParty(idxBattler)[idxParty].name)) if partyScene
return false
end
# Check whether battler can switch out
battler = @battlers[idxBattler]
return true if battler.fainted?
# Ability/item effects that allow switching no matter what
if battler.abilityActive?
if BattleHandlers.triggerCertainSwitchingUserAbility(battler.ability,battler,self)
return true
end
end
if battler.itemActive?
if BattleHandlers.triggerCertainSwitchingUserItem(battler.item,battler,self)
return true
end
end
# Other certain switching effects
return true if Settings::MORE_TYPE_EFFECTS && battler.pbHasType?(:GHOST)
# Other certain trapping effects
if battler.effects[PBEffects::Trapping]>0 ||
battler.effects[PBEffects::MeanLook]>=0 ||
battler.effects[PBEffects::Ingrain] ||
@field.effects[PBEffects::FairyLock]>0
partyScene.pbDisplay(_INTL("{1} can't be switched out!",battler.pbThis)) if partyScene
return false
end
# Trapping abilities/items
eachOtherSideBattler(idxBattler) do |b|
next if !b.abilityActive?
if BattleHandlers.triggerTrappingTargetAbility(b.ability,battler,b,self)
partyScene.pbDisplay(_INTL("{1}'s {2} prevents switching!",
b.pbThis,b.abilityName)) if partyScene
return false
end
end
eachOtherSideBattler(idxBattler) do |b|
next if !b.itemActive?
if BattleHandlers.triggerTrappingTargetItem(b.item,battler,b,self)
partyScene.pbDisplay(_INTL("{1}'s {2} prevents switching!",
b.pbThis,b.itemName)) if partyScene
return false
end
end
return true
end
def pbCanChooseNonActive?(idxBattler)
pbParty(idxBattler).each_with_index do |_pkmn,i|
return true if pbCanSwitchLax?(idxBattler,i)
end
return false
end
def pbRegisterSwitch(idxBattler,idxParty)
return false if !pbCanSwitch?(idxBattler,idxParty)
@choices[idxBattler][0] = :SwitchOut
@choices[idxBattler][1] = idxParty # Party index of Pokémon to switch in
@choices[idxBattler][2] = nil
return true
end
#=============================================================================
# Open the party screen and potentially pick a replacement Pokémon (or AI
# chooses replacement)
#=============================================================================
# Open party screen and potentially choose a Pokémon to switch with. Used in
# all instances where the party screen is opened.
def pbPartyScreen(idxBattler,checkLaxOnly=false,canCancel=false,shouldRegister=false)
ret = -1
@scene.pbPartyScreen(idxBattler,canCancel) { |idxParty,partyScene|
if checkLaxOnly
next false if !pbCanSwitchLax?(idxBattler,idxParty,partyScene)
else
next false if !pbCanSwitch?(idxBattler,idxParty,partyScene)
end
if shouldRegister
next false if idxParty<0 || !pbRegisterSwitch(idxBattler,idxParty)
end
ret = idxParty
next true
}
return ret
end
# For choosing a replacement Pokémon when prompted in the middle of other
# things happening (U-turn, Baton Pass, in def pbSwitch).
def pbSwitchInBetween(idxBattler,checkLaxOnly=false,canCancel=false)
return pbPartyScreen(idxBattler,checkLaxOnly,canCancel) if pbOwnedByPlayer?(idxBattler)
return @battleAI.pbDefaultChooseNewEnemy(idxBattler,pbParty(idxBattler))
end
#=============================================================================
# Switching Pokémon
#=============================================================================
# General switching method that checks if any Pokémon need to be sent out and,
# if so, does. Called at the end of each round.
def pbEORSwitch(favorDraws=false)
return if @decision>0 && !favorDraws
return if @decision==5 && favorDraws
pbJudge
return if @decision>0
# Check through each fainted battler to see if that spot can be filled.
switched = []
loop do
switched.clear
@battlers.each do |b|
next if !b || !b.fainted?
idxBattler = b.index
next if !pbCanChooseNonActive?(idxBattler)
if !pbOwnedByPlayer?(idxBattler) # Opponent/ally is switching in
next if wildBattle? && opposes?(idxBattler) # Wild Pokémon can't switch
idxPartyNew = pbSwitchInBetween(idxBattler)
opponent = pbGetOwnerFromBattlerIndex(idxBattler)
# NOTE: The player is only offered the chance to switch their own
# Pokémon when an opponent replaces a fainted Pokémon in single
# battles. In double battles, etc. there is no such offer.
if @internalBattle && @switchStyle && trainerBattle? && pbSideSize(0)==1 &&
opposes?(idxBattler) && !@battlers[0].fainted? && !switched.include?(0) &&
pbCanChooseNonActive?(0) && @battlers[0].effects[PBEffects::Outrage]==0
idxPartyForName = idxPartyNew
enemyParty = pbParty(idxBattler)
if enemyParty[idxPartyNew].ability == :ILLUSION
new_index = pbLastInTeam(idxBattler)
idxPartyForName = new_index if new_index >= 0
end
if pbDisplayConfirm(_INTL("{1} is about to send in {2}. Will you switch your Pokémon?",
opponent.full_name, enemyParty[idxPartyForName].name))
idxPlayerPartyNew = pbSwitchInBetween(0,false,true)
if idxPlayerPartyNew>=0
pbMessageOnRecall(@battlers[0])
pbRecallAndReplace(0,idxPlayerPartyNew)
switched.push(0)
end
end
end
pbRecallAndReplace(idxBattler,idxPartyNew)
switched.push(idxBattler)
elsif trainerBattle? # Player switches in in a trainer battle
idxPlayerPartyNew = pbGetReplacementPokemonIndex(idxBattler) # Owner chooses
pbRecallAndReplace(idxBattler,idxPlayerPartyNew)
switched.push(idxBattler)
else # Player's Pokémon has fainted in a wild battle
switch = false
if !pbDisplayConfirm(_INTL("Use next Pokémon?"))
switch = (pbRun(idxBattler,true)<=0)
else
switch = true
end
if switch
idxPlayerPartyNew = pbGetReplacementPokemonIndex(idxBattler) # Owner chooses
pbRecallAndReplace(idxBattler,idxPlayerPartyNew)
switched.push(idxBattler)
end
end
end
break if switched.length==0
pbPriority(true).each do |b|
b.pbEffectsOnSwitchIn(true) if switched.include?(b.index)
end
end
end
def pbGetReplacementPokemonIndex(idxBattler,random=false)
if random
return -1 if !pbCanSwitch?(idxBattler) # Can battler switch out?
choices = [] # Find all Pokémon that can switch in
eachInTeamFromBattlerIndex(idxBattler) do |_pkmn,i|
choices.push(i) if pbCanSwitchLax?(idxBattler,i)
end
return -1 if choices.length==0
return choices[pbRandom(choices.length)]
else
return pbSwitchInBetween(idxBattler,true)
end
end
# Actually performs the recalling and sending out in all situations.
def pbRecallAndReplace(idxBattler,idxParty,randomReplacement=false,batonPass=false)
@scene.pbRecall(idxBattler) if !@battlers[idxBattler].fainted?
@battlers[idxBattler].pbAbilitiesOnSwitchOut # Inc. primordial weather check
@scene.pbShowPartyLineup(idxBattler&1) if pbSideSize(idxBattler)==1
pbMessagesOnReplace(idxBattler,idxParty) if !randomReplacement
pbReplace(idxBattler,idxParty,batonPass)
end
def pbMessageOnRecall(battler)
if battler.pbOwnedByPlayer?
if battler.hp<=battler.totalhp/4
pbDisplayBrief(_INTL("Good job, {1}! Come back!",battler.name))
elsif battler.hp<=battler.totalhp/2
pbDisplayBrief(_INTL("OK, {1}! Come back!",battler.name))
elsif battler.turnCount>=5
pbDisplayBrief(_INTL("{1}, that's enough! Come back!",battler.name))
elsif battler.turnCount>=2
pbDisplayBrief(_INTL("{1}, come back!",battler.name))
else
pbDisplayBrief(_INTL("{1}, switch out! Come back!",battler.name))
end
else
owner = pbGetOwnerName(battler.index)
pbDisplayBrief(_INTL("{1} withdrew {2}!",owner,battler.name))
end
end
# Only called from def pbRecallAndReplace and Battle Arena's def pbSwitch.
def pbMessagesOnReplace(idxBattler,idxParty)
party = pbParty(idxBattler)
newPkmnName = party[idxParty].name
if party[idxParty].ability == :ILLUSION
new_index = pbLastInTeam(idxBattler)
newPkmnName = party[new_index].name if new_index >= 0
end
if pbOwnedByPlayer?(idxBattler)
opposing = @battlers[idxBattler].pbDirectOpposing
if opposing.fainted? || opposing.hp==opposing.totalhp
pbDisplayBrief(_INTL("You're in charge, {1}!",newPkmnName))
elsif opposing.hp>=opposing.totalhp/2
pbDisplayBrief(_INTL("Go for it, {1}!",newPkmnName))
elsif opposing.hp>=opposing.totalhp/4
pbDisplayBrief(_INTL("Just a little more! Hang in there, {1}!",newPkmnName))
else
pbDisplayBrief(_INTL("Your opponent's weak! Get 'em, {1}!",newPkmnName))
end
else
owner = pbGetOwnerFromBattlerIndex(idxBattler)
pbDisplayBrief(_INTL("{1} sent out {2}!",owner.full_name,newPkmnName))
end
end
# Only called from def pbRecallAndReplace above and Battle Arena's def
# pbSwitch.
def pbReplace(idxBattler,idxParty,batonPass=false)
party = pbParty(idxBattler)
idxPartyOld = @battlers[idxBattler].pokemonIndex
# Initialise the new Pokémon
@battlers[idxBattler].pbInitialize(party[idxParty],idxParty,batonPass)
# Reorder the party for this battle
partyOrder = pbPartyOrder(idxBattler)
partyOrder[idxParty],partyOrder[idxPartyOld] = partyOrder[idxPartyOld],partyOrder[idxParty]
# Send out the new Pokémon
pbSendOut([[idxBattler,party[idxParty]]])
pbCalculatePriority(false,[idxBattler]) if Settings::RECALCULATE_TURN_ORDER_AFTER_SPEED_CHANGES
end
# Called from def pbReplace above and at the start of battle.
# sendOuts is an array; each element is itself an array: [idxBattler,pkmn]
def pbSendOut(sendOuts,startBattle=false)
sendOuts.each { |b| @peer.pbOnEnteringBattle(self,b[1]) }
@scene.pbSendOutBattlers(sendOuts,startBattle)
sendOuts.each do |b|
@scene.pbResetMoveIndex(b[0])
pbSetSeen(@battlers[b[0]])
@usedInBattle[b[0]&1][b[0]/2] = true
end
end
#=============================================================================
# Effects upon a Pokémon entering battle
#=============================================================================
# Called at the start of battle only.
def pbOnActiveAll
# Weather-inducing abilities, Trace, Imposter, etc.
pbCalculatePriority(true)
pbPriority(true).each { |b| b.pbEffectsOnSwitchIn(true) }
pbCalculatePriority
# Check forms are correct
eachBattler { |b| b.pbCheckForm }
end
# Called when a Pokémon switches in (entry effects, entry hazards).
def pbOnActiveOne(battler)
return false if battler.fainted?
# Introduce Shadow Pokémon
if battler.opposes? && battler.shadowPokemon?
pbCommonAnimation("Shadow",battler)
pbDisplay(_INTL("Oh!\nA Shadow Pokémon!"))
end
# Record money-doubling effect of Amulet Coin/Luck Incense
if !battler.opposes? && [:AMULETCOIN, :LUCKINCENSE].include?(battler.item_id)
@field.effects[PBEffects::AmuletCoin] = true
end
# Update battlers' participants (who will gain Exp/EVs when a battler faints)
eachBattler { |b| b.pbUpdateParticipants }
# Healing Wish
if @positions[battler.index].effects[PBEffects::HealingWish]
pbCommonAnimation("HealingWish",battler)
pbDisplay(_INTL("The healing wish came true for {1}!",battler.pbThis(true)))
battler.pbRecoverHP(battler.totalhp)
battler.pbCureStatus(false)
@positions[battler.index].effects[PBEffects::HealingWish] = false
end
# Lunar Dance
if @positions[battler.index].effects[PBEffects::LunarDance]
pbCommonAnimation("LunarDance",battler)
pbDisplay(_INTL("{1} became cloaked in mystical moonlight!",battler.pbThis))
battler.pbRecoverHP(battler.totalhp)
battler.pbCureStatus(false)
battler.eachMove { |m| m.pp = m.total_pp }
@positions[battler.index].effects[PBEffects::LunarDance] = false
end
# Entry hazards
# Stealth Rock
if battler.pbOwnSide.effects[PBEffects::StealthRock] && battler.takesIndirectDamage? &&
GameData::Type.exists?(:ROCK)
bTypes = battler.pbTypes(true)
eff = Effectiveness.calculate(:ROCK, bTypes[0], bTypes[1], bTypes[2])
if !Effectiveness.ineffective?(eff)
eff = eff.to_f / Effectiveness::NORMAL_EFFECTIVE
oldHP = battler.hp
battler.pbReduceHP(battler.totalhp*eff/8,false)
pbDisplay(_INTL("Pointed stones dug into {1}!",battler.pbThis))
battler.pbItemHPHealCheck
if battler.pbAbilitiesOnDamageTaken(oldHP) # Switched out
return pbOnActiveOne(battler) # For replacement battler
end
end
end
# Spikes
if battler.pbOwnSide.effects[PBEffects::Spikes]>0 && battler.takesIndirectDamage? &&
!battler.airborne?
spikesDiv = [8,6,4][battler.pbOwnSide.effects[PBEffects::Spikes]-1]
oldHP = battler.hp
battler.pbReduceHP(battler.totalhp/spikesDiv,false)
pbDisplay(_INTL("{1} is hurt by the spikes!",battler.pbThis))
battler.pbItemHPHealCheck
if battler.pbAbilitiesOnDamageTaken(oldHP) # Switched out
return pbOnActiveOne(battler) # For replacement battler
end
end
# Toxic Spikes
if battler.pbOwnSide.effects[PBEffects::ToxicSpikes]>0 && !battler.fainted? &&
!battler.airborne?
if battler.pbHasType?(:POISON)
battler.pbOwnSide.effects[PBEffects::ToxicSpikes] = 0
pbDisplay(_INTL("{1} absorbed the poison spikes!",battler.pbThis))
elsif battler.pbCanPoison?(nil,false)
if battler.pbOwnSide.effects[PBEffects::ToxicSpikes]==2
battler.pbPoison(nil,_INTL("{1} was badly poisoned by the poison spikes!",battler.pbThis),true)
else
battler.pbPoison(nil,_INTL("{1} was poisoned by the poison spikes!",battler.pbThis))
end
end
end
# Sticky Web
if battler.pbOwnSide.effects[PBEffects::StickyWeb] && !battler.fainted? &&
!battler.airborne?
pbDisplay(_INTL("{1} was caught in a sticky web!",battler.pbThis))
if battler.pbCanLowerStatStage?(:SPEED)
battler.pbLowerStatStage(:SPEED,1,nil)
battler.pbItemStatRestoreCheck
end
end
# Battler faints if it is knocked out because of an entry hazard above
if battler.fainted?
battler.pbFaint
pbGainExp
pbJudge
return false
end
battler.pbCheckForm
return true
end
end

View File

@@ -0,0 +1,148 @@
class PokeBattle_Battle
#=============================================================================
# Choosing to use an item
#=============================================================================
def pbCanUseItemOnPokemon?(item,pkmn,battler,scene,showMessages=true)
if !pkmn || pkmn.egg?
scene.pbDisplay(_INTL("It won't have any effect.")) if showMessages
return false
end
# Embargo
if battler && battler.effects[PBEffects::Embargo]>0
scene.pbDisplay(_INTL("Embargo's effect prevents the item's use on {1}!",
battler.pbThis(true))) if showMessages
return false
end
return true
end
# NOTE: Using a Poké Ball consumes all your actions for the round. The method
# below is one half of making this happen; the other half is in the
# ItemHandlers::CanUseInBattle for Poké Balls.
def pbItemUsesAllActions?(item)
return true if GameData::Item.get(item).is_poke_ball?
return false
end
def pbRegisterItem(idxBattler,item,idxTarget=nil,idxMove=nil)
# Register for use of item on a Pokémon in the party
@choices[idxBattler][0] = :UseItem
@choices[idxBattler][1] = item # ID of item to be used
@choices[idxBattler][2] = idxTarget # Party index of Pokémon to use item on
@choices[idxBattler][3] = idxMove # Index of move to recharge (Ethers)
# Delete the item from the Bag. If it turns out it will have no effect, it
# will be re-added to the Bag later.
pbConsumeItemInBag(item,idxBattler)
return true
end
#=============================================================================
# Using an item
#=============================================================================
def pbConsumeItemInBag(item,idxBattler)
return if !item
useType = GameData::Item.get(item).battle_use
return if useType==0 || (useType>=6 && useType<=10) # Not consumed upon use
if pbOwnedByPlayer?(idxBattler)
if !$PokemonBag.pbDeleteItem(item)
raise _INTL("Tried to consume item that wasn't in the Bag somehow.")
end
else
items = pbGetOwnerItems(idxBattler)
items.each_with_index do |thisItem,i|
next if thisItem!=item
items[i] = nil
break
end
items.compact!
end
end
def pbReturnUnusedItemToBag(item,idxBattler)
return if item!
useType = GameData::Item.get(item).battle_use
return if useType==0 || (useType>=6 && useType<=10) # Not consumed upon use
if pbOwnedByPlayer?(idxBattler)
if $PokemonBag && $PokemonBag.pbCanStore?(item)
$PokemonBag.pbStoreItem(item)
else
raise _INTL("Couldn't return unused item to Bag somehow.")
end
else
items = pbGetOwnerItems(idxBattler)
items.push(item) if items
end
end
def pbUseItemMessage(item,trainerName)
itemName = GameData::Item.get(item).name
if itemName.starts_with_vowel?
pbDisplayBrief(_INTL("{1} used an {2}.",trainerName,itemName))
else
pbDisplayBrief(_INTL("{1} used a {2}.",trainerName,itemName))
end
end
# Uses an item on a Pokémon in the trainer's party.
def pbUseItemOnPokemon(item,idxParty,userBattler)
trainerName = pbGetOwnerName(userBattler.index)
pbUseItemMessage(item,trainerName)
pkmn = pbParty(userBattler.index)[idxParty]
battler = pbFindBattler(idxParty,userBattler.index)
ch = @choices[userBattler.index]
if ItemHandlers.triggerCanUseInBattle(item,pkmn,battler,ch[3],true,self,@scene,false)
ItemHandlers.triggerBattleUseOnPokemon(item,pkmn,battler,ch,@scene)
ch[1] = nil # Delete item from choice
return
end
pbDisplay(_INTL("But it had no effect!"))
# Return unused item to Bag
pbReturnUnusedItemToBag(item,userBattler.index)
end
# Uses an item on a Pokémon in battle that belongs to the trainer.
def pbUseItemOnBattler(item,idxParty,userBattler)
trainerName = pbGetOwnerName(userBattler.index)
pbUseItemMessage(item,trainerName)
battler = pbFindBattler(idxParty,userBattler.index)
ch = @choices[userBattler.index]
if battler
if ItemHandlers.triggerCanUseInBattle(item,battler.pokemon,battler,ch[3],true,self,@scene,false)
ItemHandlers.triggerBattleUseOnBattler(item,battler,@scene)
ch[1] = nil # Delete item from choice
return
else
pbDisplay(_INTL("But it had no effect!"))
end
else
pbDisplay(_INTL("But it's not where this item can be used!"))
end
# Return unused item to Bag
pbReturnUnusedItemToBag(item,userBattler.index)
end
# Uses a Poké Ball in battle directly.
def pbUsePokeBallInBattle(item,idxBattler,userBattler)
idxBattler = userBattler.index if idxBattler<0
battler = @battlers[idxBattler]
ItemHandlers.triggerUseInBattle(item,battler,self)
@choices[userBattler.index][1] = nil # Delete item from choice
end
# Uses an item in battle directly.
def pbUseItemInBattle(item,idxBattler,userBattler)
trainerName = pbGetOwnerName(userBattler.index)
pbUseItemMessage(item,trainerName)
battler = (idxBattler<0) ? userBattler : @battlers[idxBattler]
pkmn = battler.pokemon
ch = @choices[userBattler.index]
if ItemHandlers.triggerCanUseInBattle(item,pkmn,battler,ch[3],true,self,@scene,false)
ItemHandlers.triggerUseInBattle(item,battler,self)
ch[1] = nil # Delete item from choice
return
end
pbDisplay(_INTL("But it had no effect!"))
# Return unused item to Bag
pbReturnUnusedItemToBag(item,userBattler.index)
end
end

View File

@@ -0,0 +1,151 @@
class PokeBattle_Battle
#=============================================================================
# Running from battle
#=============================================================================
def pbCanRun?(idxBattler)
return false if trainerBattle?
battler = @battlers[idxBattler]
return false if !@canRun && !battler.opposes?
return true if battler.pbHasType?(:GHOST) && Settings::MORE_TYPE_EFFECTS
return true if battler.abilityActive? &&
BattleHandlers.triggerRunFromBattleAbility(battler.ability,battler)
return true if battler.itemActive? &&
BattleHandlers.triggerRunFromBattleItem(battler.item,battler)
return false if battler.effects[PBEffects::Trapping]>0 ||
battler.effects[PBEffects::MeanLook]>=0 ||
battler.effects[PBEffects::Ingrain] ||
@field.effects[PBEffects::FairyLock]>0
eachOtherSideBattler(idxBattler) do |b|
return false if b.abilityActive? &&
BattleHandlers.triggerTrappingTargetAbility(b.ability,battler,b,self)
return false if b.itemActive? &&
BattleHandlers.triggerTrappingTargetItem(b.item,battler,b,self)
end
return true
end
# Return values:
# -1: Failed fleeing
# 0: Wasn't possible to attempt fleeing, continue choosing action for the round
# 1: Succeeded at fleeing, battle will end
# duringBattle is true for replacing a fainted Pokémon during the End Of Round
# phase, and false for choosing the Run command.
def pbRun(idxBattler,duringBattle=false)
battler = @battlers[idxBattler]
if battler.opposes?
return 0 if trainerBattle?
@choices[idxBattler][0] = :Run
@choices[idxBattler][1] = 0
@choices[idxBattler][2] = nil
return -1
end
# Fleeing from trainer battles
if trainerBattle?
if $DEBUG && Input.press?(Input::CTRL)
if pbDisplayConfirm(_INTL("Treat this battle as a win?"))
@decision = 1
return 1
elsif pbDisplayConfirm(_INTL("Treat this battle as a loss?"))
@decision = 2
return 1
end
elsif @internalBattle
pbDisplayPaused(_INTL("No! There's no running from a Trainer battle!"))
elsif pbDisplayConfirm(_INTL("Would you like to forfeit the match and quit now?"))
pbSEPlay("Battle flee")
pbDisplay(_INTL("{1} forfeited the match!",self.pbPlayer.name))
@decision = 3
return 1
end
return 0
end
# Fleeing from wild battles
if $DEBUG && Input.press?(Input::CTRL)
pbSEPlay("Battle flee")
pbDisplayPaused(_INTL("You got away safely!"))
@decision = 3
return 1
end
if !@canRun
pbDisplayPaused(_INTL("You can't escape!"))
return 0
end
if !duringBattle
if battler.pbHasType?(:GHOST) && Settings::MORE_TYPE_EFFECTS
pbSEPlay("Battle flee")
pbDisplayPaused(_INTL("You got away safely!"))
@decision = 3
return 1
end
# Abilities that guarantee escape
if battler.abilityActive?
if BattleHandlers.triggerRunFromBattleAbility(battler.ability,battler)
pbShowAbilitySplash(battler,true)
pbHideAbilitySplash(battler)
pbSEPlay("Battle flee")
pbDisplayPaused(_INTL("You got away safely!"))
@decision = 3
return 1
end
end
# Held items that guarantee escape
if battler.itemActive?
if BattleHandlers.triggerRunFromBattleItem(battler.item,battler)
pbSEPlay("Battle flee")
pbDisplayPaused(_INTL("{1} fled using its {2}!",
battler.pbThis,battler.itemName))
@decision = 3
return 1
end
end
# Other certain trapping effects
if battler.effects[PBEffects::Trapping]>0 ||
battler.effects[PBEffects::MeanLook]>=0 ||
battler.effects[PBEffects::Ingrain] ||
@field.effects[PBEffects::FairyLock]>0
pbDisplayPaused(_INTL("You can't escape!"))
return 0
end
# Trapping abilities/items
eachOtherSideBattler(idxBattler) do |b|
next if !b.abilityActive?
if BattleHandlers.triggerTrappingTargetAbility(b.ability,battler,b,self)
pbDisplayPaused(_INTL("{1} prevents escape with {2}!",b.pbThis,b.abilityName))
return 0
end
end
eachOtherSideBattler(idxBattler) do |b|
next if !b.itemActive?
if BattleHandlers.triggerTrappingTargetItem(b.item,battler,b,self)
pbDisplayPaused(_INTL("{1} prevents escape with {2}!",b.pbThis,b.itemName))
return 0
end
end
end
# Fleeing calculation
# Get the speeds of the Pokémon fleeing and the fastest opponent
# NOTE: Not pbSpeed, because using unmodified Speed.
@runCommand += 1 if !duringBattle # Make it easier to flee next time
speedPlayer = @battlers[idxBattler].speed
speedEnemy = 1
eachOtherSideBattler(idxBattler) do |b|
speed = b.speed
speedEnemy = speed if speedEnemy<speed
end
# Compare speeds and perform fleeing calculation
if speedPlayer>speedEnemy
rate = 256
else
rate = (speedPlayer*128)/speedEnemy
rate += @runCommand*30
end
if rate>=256 || @battleAI.pbAIRandom(256)<rate
pbSEPlay("Battle flee")
pbDisplayPaused(_INTL("You got away safely!"))
@decision = 3
return 1
end
pbDisplayPaused(_INTL("You couldn't get away!"))
return -1
end
end

View File

@@ -0,0 +1,189 @@
class PokeBattle_Battle
#=============================================================================
# Shifting a battler to another position in a battle larger than double
#=============================================================================
def pbCanShift?(idxBattler)
return false if pbSideSize(0)<=2 && pbSideSize(1)<=2 # Double battle or smaller
idxOther = -1
case pbSideSize(idxBattler)
when 1
return false # Only one battler on that side
when 2
idxOther = (idxBattler+2)%4
when 3
return false if idxBattler==2 || idxBattler==3 # In middle spot already
idxOther = ((idxBattler%2)==0) ? 2 : 3
end
return false if pbGetOwnerIndexFromBattlerIndex(idxBattler)!=pbGetOwnerIndexFromBattlerIndex(idxOther)
return true
end
def pbRegisterShift(idxBattler)
@choices[idxBattler][0] = :Shift
@choices[idxBattler][1] = 0
@choices[idxBattler][2] = nil
return true
end
#=============================================================================
# Calling at a battler
#=============================================================================
def pbRegisterCall(idxBattler)
@choices[idxBattler][0] = :Call
@choices[idxBattler][1] = 0
@choices[idxBattler][2] = nil
return true
end
def pbCall(idxBattler)
battler = @battlers[idxBattler]
trainerName = pbGetOwnerName(idxBattler)
pbDisplay(_INTL("{1} called {2}!",trainerName,battler.pbThis(true)))
pbDisplay(_INTL("{1}!",battler.name))
if battler.shadowPokemon?
if battler.inHyperMode?
battler.pokemon.hyper_mode = false
battler.pokemon.adjustHeart(-300)
pbDisplay(_INTL("{1} came to its senses from the Trainer's call!",battler.pbThis))
else
pbDisplay(_INTL("But nothing happened!"))
end
elsif battler.status == :SLEEP
battler.pbCureStatus
elsif battler.pbCanRaiseStatStage?(:ACCURACY,battler)
battler.pbRaiseStatStage(:ACCURACY,1,battler)
else
pbDisplay(_INTL("But nothing happened!"))
end
end
#=============================================================================
# Choosing to Mega Evolve a battler
#=============================================================================
def pbHasMegaRing?(idxBattler)
return true if !pbOwnedByPlayer?(idxBattler) # Assume AI trainer have a ring
Settings::MEGA_RINGS.each { |item| return true if $PokemonBag.pbHasItem?(item) }
return false
end
def pbGetMegaRingName(idxBattler)
if pbOwnedByPlayer?(idxBattler)
Settings::MEGA_RINGS.each do |item|
return GameData::Item.get(item).name if $PokemonBag.pbHasItem?(item)
end
end
# NOTE: Add your own Mega objects for particular NPC trainers here.
# if pbGetOwnerFromBattlerIndex(idxBattler).trainer_type == :BUGCATCHER
# return _INTL("Mega Net")
# end
return _INTL("Mega Ring")
end
def pbCanMegaEvolve?(idxBattler)
return false if $game_switches[Settings::NO_MEGA_EVOLUTION]
return false if !@battlers[idxBattler].hasMega?
return false if wildBattle? && opposes?(idxBattler)
return true if $DEBUG && Input.press?(Input::CTRL)
return false if @battlers[idxBattler].effects[PBEffects::SkyDrop]>=0
return false if !pbHasMegaRing?(idxBattler)
side = @battlers[idxBattler].idxOwnSide
owner = pbGetOwnerIndexFromBattlerIndex(idxBattler)
return @megaEvolution[side][owner]==-1
end
def pbRegisterMegaEvolution(idxBattler)
side = @battlers[idxBattler].idxOwnSide
owner = pbGetOwnerIndexFromBattlerIndex(idxBattler)
@megaEvolution[side][owner] = idxBattler
end
def pbUnregisterMegaEvolution(idxBattler)
side = @battlers[idxBattler].idxOwnSide
owner = pbGetOwnerIndexFromBattlerIndex(idxBattler)
@megaEvolution[side][owner] = -1 if @megaEvolution[side][owner]==idxBattler
end
def pbToggleRegisteredMegaEvolution(idxBattler)
side = @battlers[idxBattler].idxOwnSide
owner = pbGetOwnerIndexFromBattlerIndex(idxBattler)
if @megaEvolution[side][owner]==idxBattler
@megaEvolution[side][owner] = -1
else
@megaEvolution[side][owner] = idxBattler
end
end
def pbRegisteredMegaEvolution?(idxBattler)
side = @battlers[idxBattler].idxOwnSide
owner = pbGetOwnerIndexFromBattlerIndex(idxBattler)
return @megaEvolution[side][owner]==idxBattler
end
#=============================================================================
# Mega Evolving a battler
#=============================================================================
def pbMegaEvolve(idxBattler)
battler = @battlers[idxBattler]
return if !battler || !battler.pokemon
return if !battler.hasMega? || battler.mega?
trainerName = pbGetOwnerName(idxBattler)
# Break Illusion
if battler.hasActiveAbility?(:ILLUSION)
BattleHandlers.triggerTargetAbilityOnHit(battler.ability,nil,battler,nil,self)
end
# Mega Evolve
case battler.pokemon.megaMessage
when 1 # Rayquaza
pbDisplay(_INTL("{1}'s fervent wish has reached {2}!",trainerName,battler.pbThis))
else
pbDisplay(_INTL("{1}'s {2} is reacting to {3}'s {4}!",
battler.pbThis,battler.itemName,trainerName,pbGetMegaRingName(idxBattler)))
end
pbCommonAnimation("MegaEvolution",battler)
battler.pokemon.makeMega
battler.form = battler.pokemon.form
battler.pbUpdate(true)
@scene.pbChangePokemon(battler,battler.pokemon)
@scene.pbRefreshOne(idxBattler)
pbCommonAnimation("MegaEvolution2",battler)
megaName = battler.pokemon.megaName
if !megaName || megaName==""
megaName = _INTL("Mega {1}", battler.pokemon.speciesName)
end
pbDisplay(_INTL("{1} has Mega Evolved into {2}!",battler.pbThis,megaName))
side = battler.idxOwnSide
owner = pbGetOwnerIndexFromBattlerIndex(idxBattler)
@megaEvolution[side][owner] = -2
if battler.isSpecies?(:GENGAR) && battler.mega?
battler.effects[PBEffects::Telekinesis] = 0
end
pbCalculatePriority(false,[idxBattler]) if Settings::RECALCULATE_TURN_ORDER_AFTER_MEGA_EVOLUTION
# Trigger ability
battler.pbEffectsOnSwitchIn
end
#=============================================================================
# Primal Reverting a battler
#=============================================================================
def pbPrimalReversion(idxBattler)
battler = @battlers[idxBattler]
return if !battler || !battler.pokemon
return if !battler.hasPrimal? || battler.primal?
if battler.isSpecies?(:KYOGRE)
pbCommonAnimation("PrimalKyogre",battler)
elsif battler.isSpecies?(:GROUDON)
pbCommonAnimation("PrimalGroudon",battler)
end
battler.pokemon.makePrimal
battler.form = battler.pokemon.form
battler.pbUpdate(true)
@scene.pbChangePokemon(battler,battler.pokemon)
@scene.pbRefreshOne(idxBattler)
if battler.isSpecies?(:KYOGRE)
pbCommonAnimation("PrimalKyogre2",battler)
elsif battler.isSpecies?(:GROUDON)
pbCommonAnimation("PrimalGroudon2",battler)
end
pbDisplay(_INTL("{1}'s Primal Reversion!\nIt reverted to its primal form!",battler.pbThis))
end
end

View File

@@ -0,0 +1,250 @@
class PokeBattle_Battle
#=============================================================================
# Clear commands
#=============================================================================
def pbClearChoice(idxBattler)
@choices[idxBattler] = [] if !@choices[idxBattler]
@choices[idxBattler][0] = :None
@choices[idxBattler][1] = 0
@choices[idxBattler][2] = nil
@choices[idxBattler][3] = -1
end
def pbCancelChoice(idxBattler)
# If idxBattler's choice was to use an item, return that item to the Bag
if @choices[idxBattler][0] == :UseItem
item = @choices[idxBattler][1]
pbReturnUnusedItemToBag(item, idxBattler) if item
end
# If idxBattler chose to Mega Evolve, cancel it
pbUnregisterMegaEvolution(idxBattler)
# Clear idxBattler's choice
pbClearChoice(idxBattler)
end
#=============================================================================
# Use main command menu (Fight/Pokémon/Bag/Run)
#=============================================================================
def pbCommandMenu(idxBattler,firstAction)
return @scene.pbCommandMenu(idxBattler,firstAction)
end
#=============================================================================
# Check whether actions can be taken
#=============================================================================
def pbCanShowCommands?(idxBattler)
battler = @battlers[idxBattler]
return false if !battler || battler.fainted?
return false if battler.usingMultiTurnAttack?
return true
end
def pbCanShowFightMenu?(idxBattler)
battler = @battlers[idxBattler]
# Encore
return false if battler.effects[PBEffects::Encore]>0
# No moves that can be chosen (will Struggle instead)
usable = false
battler.eachMoveWithIndex do |_m,i|
next if !pbCanChooseMove?(idxBattler,i,false)
usable = true
break
end
return usable
end
#=============================================================================
# Use sub-menus to choose an action, and register it if is allowed
#=============================================================================
# Returns true if a choice was made, false if cancelled.
def pbFightMenu(idxBattler)
# Auto-use Encored move or no moves choosable, so auto-use Struggle
return pbAutoChooseMove(idxBattler) if !pbCanShowFightMenu?(idxBattler)
# Battle Palace only
return true if pbAutoFightMenu(idxBattler)
# Regular move selection
ret = false
@scene.pbFightMenu(idxBattler,pbCanMegaEvolve?(idxBattler)) { |cmd|
case cmd
when -1 # Cancel
when -2 # Toggle Mega Evolution
pbToggleRegisteredMegaEvolution(idxBattler)
next false
when -3 # Shift
pbUnregisterMegaEvolution(idxBattler)
pbRegisterShift(idxBattler)
ret = true
else # Chose a move to use
next false if cmd<0 || !@battlers[idxBattler].moves[cmd] ||
!@battlers[idxBattler].moves[cmd].id
next false if !pbRegisterMove(idxBattler,cmd)
next false if !singleBattle? &&
!pbChooseTarget(@battlers[idxBattler],@battlers[idxBattler].moves[cmd])
ret = true
end
next true
}
return ret
end
def pbAutoFightMenu(idxBattler); return false; end
def pbChooseTarget(battler,move)
target_data = move.pbTarget(battler)
idxTarget = @scene.pbChooseTarget(battler.index,target_data)
return false if idxTarget<0
pbRegisterTarget(battler.index,idxTarget)
return true
end
def pbItemMenu(idxBattler,firstAction)
if !@internalBattle
pbDisplay(_INTL("Items can't be used here."))
return false
end
ret = false
@scene.pbItemMenu(idxBattler,firstAction) { |item,useType,idxPkmn,idxMove,itemScene|
next false if !item
battler = pkmn = nil
case useType
when 1, 2, 6, 7 # Use on Pokémon/Pokémon's move
next false if !ItemHandlers.hasBattleUseOnPokemon(item)
battler = pbFindBattler(idxPkmn,idxBattler)
pkmn = pbParty(idxBattler)[idxPkmn]
next false if !pbCanUseItemOnPokemon?(item,pkmn,battler,itemScene)
when 3, 8 # Use on battler
next false if !ItemHandlers.hasBattleUseOnBattler(item)
battler = pbFindBattler(idxPkmn,idxBattler)
pkmn = battler.pokemon if battler
next false if !pbCanUseItemOnPokemon?(item,pkmn,battler,itemScene)
when 4, 9 # Poké Balls
next false if idxPkmn<0
battler = @battlers[idxPkmn]
pkmn = battler.pokemon if battler
when 5, 10 # No target (Poké Doll, Guard Spec., Launcher items)
battler = @battlers[idxBattler]
pkmn = battler.pokemon if battler
else
next false
end
next false if !pkmn
next false if !ItemHandlers.triggerCanUseInBattle(item,
pkmn,battler,idxMove,firstAction,self,itemScene)
next false if !pbRegisterItem(idxBattler,item,idxPkmn,idxMove)
ret = true
next true
}
return ret
end
def pbPartyMenu(idxBattler)
ret = -1
if @debug
ret = @battleAI.pbDefaultChooseNewEnemy(idxBattler,pbParty(idxBattler))
else
ret = pbPartyScreen(idxBattler,false,true,true)
end
return ret>=0
end
def pbRunMenu(idxBattler)
# Regardless of succeeding or failing to run, stop choosing actions
return pbRun(idxBattler)!=0
end
def pbCallMenu(idxBattler)
return pbRegisterCall(idxBattler)
end
def pbDebugMenu
# NOTE: This doesn't do anything yet. Maybe you can write your own debugging
# options!
end
#=============================================================================
# Command phase
#=============================================================================
def pbCommandPhase
@scene.pbBeginCommandPhase
# Reset choices if commands can be shown
@battlers.each_with_index do |b,i|
next if !b
pbClearChoice(i) if pbCanShowCommands?(i)
end
# Reset choices to perform Mega Evolution if it wasn't done somehow
for side in 0...2
@megaEvolution[side].each_with_index do |megaEvo,i|
@megaEvolution[side][i] = -1 if megaEvo>=0
end
end
# Choose actions for the round (player first, then AI)
pbCommandPhaseLoop(true) # Player chooses their actions
return if @decision!=0 # Battle ended, stop choosing actions
pbCommandPhaseLoop(false) # AI chooses their actions
end
def pbCommandPhaseLoop(isPlayer)
# NOTE: Doing some things (e.g. running, throwing a Poké Ball) takes up all
# your actions in a round.
actioned = []
idxBattler = -1
loop do
break if @decision!=0 # Battle ended, stop choosing actions
idxBattler += 1
break if idxBattler>=@battlers.length
next if !@battlers[idxBattler] || pbOwnedByPlayer?(idxBattler)!=isPlayer
next if @choices[idxBattler][0]!=:None # Action is forced, can't choose one
next if !pbCanShowCommands?(idxBattler) # Action is forced, can't choose one
# AI controls this battler
if @controlPlayer || !pbOwnedByPlayer?(idxBattler)
@battleAI.pbDefaultChooseEnemyCommand(idxBattler)
next
end
# Player chooses an action
actioned.push(idxBattler)
commandsEnd = false # Whether to cancel choosing all other actions this round
loop do
cmd = pbCommandMenu(idxBattler,actioned.length==1)
# If being Sky Dropped, can't do anything except use a move
if cmd>0 && @battlers[idxBattler].effects[PBEffects::SkyDrop]>=0
pbDisplay(_INTL("Sky Drop won't let {1} go!",@battlers[idxBattler].pbThis(true)))
next
end
case cmd
when 0 # Fight
break if pbFightMenu(idxBattler)
when 1 # Bag
if pbItemMenu(idxBattler,actioned.length==1)
commandsEnd = true if pbItemUsesAllActions?(@choices[idxBattler][1])
break
end
when 2 # Pokémon
break if pbPartyMenu(idxBattler)
when 3 # Run
# NOTE: "Run" is only an available option for the first battler the
# player chooses an action for in a round. Attempting to run
# from battle prevents you from choosing any other actions in
# that round.
if pbRunMenu(idxBattler)
commandsEnd = true
break
end
when 4 # Call
break if pbCallMenu(idxBattler)
when -2 # Debug
pbDebugMenu
next
when -1 # Go back to previous battler's action choice
next if actioned.length<=1
actioned.pop # Forget this battler was done
idxBattler = actioned.last-1
pbCancelChoice(idxBattler+1) # Clear the previous battler's choice
actioned.pop # Forget the previous battler was done
break
end
pbCancelChoice(idxBattler)
end
break if commandsEnd
end
end
end

View File

@@ -0,0 +1,190 @@
class PokeBattle_Battle
#=============================================================================
# Attack phase actions
#=============================================================================
# Quick Claw, Custap Berry's "X let it move first!" message.
def pbAttackPhasePriorityChangeMessages
pbPriority.each do |b|
if b.effects[PBEffects::PriorityAbility] && b.abilityActive?
BattleHandlers.triggerPriorityBracketUseAbility(b.ability,b,self)
elsif b.effects[PBEffects::PriorityItem] && b.itemActive?
BattleHandlers.triggerPriorityBracketUseItem(b.item,b,self)
end
end
end
def pbAttackPhaseCall
pbPriority.each do |b|
next unless @choices[b.index][0]==:Call && !b.fainted?
b.lastMoveFailed = false # Counts as a successful move for Stomping Tantrum
pbCall(b.index)
end
end
def pbPursuit(idxSwitcher)
@switching = true
pbPriority.each do |b|
next if b.fainted? || !b.opposes?(idxSwitcher) # Shouldn't hit an ally
next if b.movedThisRound? || !pbChoseMoveFunctionCode?(b.index,"088") # Pursuit
# Check whether Pursuit can be used
next unless pbMoveCanTarget?(b.index,idxSwitcher,@choices[b.index][2].pbTarget(b))
next unless pbCanChooseMove?(b.index,@choices[b.index][1],false)
next if b.status == :SLEEP || b.status == :FROZEN
next if b.effects[PBEffects::SkyDrop]>=0
next if b.hasActiveAbility?(:TRUANT) && b.effects[PBEffects::Truant]
# Mega Evolve
if !wildBattle? || !b.opposes?
owner = pbGetOwnerIndexFromBattlerIndex(b.index)
pbMegaEvolve(b.index) if @megaEvolution[b.idxOwnSide][owner]==b.index
end
# Use Pursuit
@choices[b.index][3] = idxSwitcher # Change Pursuit's target
if b.pbProcessTurn(@choices[b.index],false)
b.effects[PBEffects::Pursuit] = true
end
break if @decision>0 || @battlers[idxSwitcher].fainted?
end
@switching = false
end
def pbAttackPhaseSwitch
pbPriority.each do |b|
next unless @choices[b.index][0]==:SwitchOut && !b.fainted?
idxNewPkmn = @choices[b.index][1] # Party index of Pokémon to switch to
b.lastMoveFailed = false # Counts as a successful move for Stomping Tantrum
@lastMoveUser = b.index
# Switching message
pbMessageOnRecall(b)
# Pursuit interrupts switching
pbPursuit(b.index)
return if @decision>0
# Switch Pokémon
pbRecallAndReplace(b.index,idxNewPkmn)
b.pbEffectsOnSwitchIn(true)
end
end
def pbAttackPhaseItems
pbPriority.each do |b|
next unless @choices[b.index][0] == :UseItem && !b.fainted?
b.lastMoveFailed = false # Counts as a successful move for Stomping Tantrum
item = @choices[b.index][1]
next if !item
case GameData::Item.get(item).battle_use
when 1, 2, 6, 7 # Use on Pokémon/Pokémon's move
pbUseItemOnPokemon(item, @choices[b.index][2], b) if @choices[b.index][2] >= 0
when 3, 8 # Use on battler
pbUseItemOnBattler(item, @choices[b.index][2], b)
when 4, 9 # Use Poké Ball
pbUsePokeBallInBattle(item, @choices[b.index][2], b)
when 5, 10 # Use directly
pbUseItemInBattle(item, @choices[b.index][2], b)
else
next
end
return if @decision > 0
end
pbCalculatePriority if Settings::RECALCULATE_TURN_ORDER_AFTER_SPEED_CHANGES
end
def pbAttackPhaseMegaEvolution
pbPriority.each do |b|
next if wildBattle? && b.opposes?
next unless @choices[b.index][0]==:UseMove && !b.fainted?
owner = pbGetOwnerIndexFromBattlerIndex(b.index)
next if @megaEvolution[b.idxOwnSide][owner]!=b.index
pbMegaEvolve(b.index)
end
end
def pbAttackPhaseMoves
# Show charging messages (Focus Punch)
pbPriority.each do |b|
next unless @choices[b.index][0]==:UseMove && !b.fainted?
next if b.movedThisRound?
@choices[b.index][2].pbDisplayChargeMessage(b)
end
# Main move processing loop
loop do
priority = pbPriority
# Forced to go next
advance = false
priority.each do |b|
next unless b.effects[PBEffects::MoveNext] && !b.fainted?
next unless @choices[b.index][0]==:UseMove || @choices[b.index][0]==:Shift
next if b.movedThisRound?
advance = b.pbProcessTurn(@choices[b.index])
break if advance
end
return if @decision>0
next if advance
# Regular priority order
priority.each do |b|
next if b.effects[PBEffects::Quash]>0 || b.fainted?
next unless @choices[b.index][0]==:UseMove || @choices[b.index][0]==:Shift
next if b.movedThisRound?
advance = b.pbProcessTurn(@choices[b.index])
break if advance
end
return if @decision>0
next if advance
# Quashed
quashLevel = 0
loop do
quashLevel += 1
moreQuash = false
priority.each do |b|
moreQuash = true if b.effects[PBEffects::Quash]>quashLevel
next unless b.effects[PBEffects::Quash]==quashLevel && !b.fainted?
next unless @choices[b.index][0]==:UseMove || @choices[b.index][0]==:Shift
next if b.movedThisRound?
advance = b.pbProcessTurn(@choices[b.index])
break
end
break if advance || !moreQuash
end
return if @decision>0
next if advance
# Check for all done
priority.each do |b|
if !b.fainted? && !b.movedThisRound?
advance = true if @choices[b.index][0]==:UseMove || @choices[b.index][0]==:Shift
end
break if advance
end
next if advance
# All Pokémon have moved; end the loop
break
end
end
#=============================================================================
# Attack phase
#=============================================================================
def pbAttackPhase
@scene.pbBeginAttackPhase
# Reset certain effects
@battlers.each_with_index do |b,i|
next if !b
b.turnCount += 1 if !b.fainted?
@successStates[i].clear
if @choices[i][0]!=:UseMove && @choices[i][0]!=:Shift && @choices[i][0]!=:SwitchOut
b.effects[PBEffects::DestinyBond] = false
b.effects[PBEffects::Grudge] = false
end
b.effects[PBEffects::Rage] = false if !pbChoseMoveFunctionCode?(i,"093") # Rage
end
PBDebug.log("")
# Calculate move order for this round
pbCalculatePriority(true)
# Perform actions
pbAttackPhasePriorityChangeMessages
pbAttackPhaseCall
pbAttackPhaseSwitch
return if @decision>0
pbAttackPhaseItems
return if @decision>0
pbAttackPhaseMegaEvolution
pbAttackPhaseMoves
end
end

View File

@@ -0,0 +1,666 @@
class PokeBattle_Battle
#=============================================================================
# Decrement effect counters
#=============================================================================
def pbEORCountDownBattlerEffect(priority,effect)
priority.each do |b|
next if b.fainted? || b.effects[effect]==0
b.effects[effect] -= 1
yield b if block_given? && b.effects[effect]==0
end
end
def pbEORCountDownSideEffect(side,effect,msg)
if @sides[side].effects[effect]>0
@sides[side].effects[effect] -= 1
pbDisplay(msg) if @sides[side].effects[effect]==0
end
end
def pbEORCountDownFieldEffect(effect,msg)
if @field.effects[effect]>0
@field.effects[effect] -= 1
if @field.effects[effect]==0
pbDisplay(msg)
if effect==PBEffects::MagicRoom
pbPriority(true).each { |b| b.pbItemTerrainStatBoostCheck }
end
end
end
end
#=============================================================================
# End Of Round weather
#=============================================================================
def pbEORWeather(priority)
# NOTE: Primordial weather doesn't need to be checked here, because if it
# could wear off here, it will have worn off already.
# Count down weather duration
@field.weatherDuration -= 1 if @field.weatherDuration>0
# Weather wears off
if @field.weatherDuration==0
case @field.weather
when :Sun then pbDisplay(_INTL("The sunlight faded."))
when :Rain then pbDisplay(_INTL("The rain stopped."))
when :Sandstorm then pbDisplay(_INTL("The sandstorm subsided."))
when :Hail then pbDisplay(_INTL("The hail stopped."))
when :ShadowSky then pbDisplay(_INTL("The shadow sky faded."))
end
@field.weather = :None
# Check for form changes caused by the weather changing
eachBattler { |b| b.pbCheckFormOnWeatherChange }
# Start up the default weather
pbStartWeather(nil,@field.defaultWeather) if @field.defaultWeather != :None
return if @field.weather == :None
end
# Weather continues
weather_data = GameData::BattleWeather.try_get(@field.weather)
pbCommonAnimation(weather_data.animation) if weather_data
case @field.weather
# when :Sun then pbDisplay(_INTL("The sunlight is strong."))
# when :Rain then pbDisplay(_INTL("Rain continues to fall."))
when :Sandstorm then pbDisplay(_INTL("The sandstorm is raging."))
when :Hail then pbDisplay(_INTL("The hail is crashing down."))
# when :HarshSun then pbDisplay(_INTL("The sunlight is extremely harsh."))
# when :HeavyRain then pbDisplay(_INTL("It is raining heavily."))
# when :StrongWinds then pbDisplay(_INTL("The wind is strong."))
when :ShadowSky then pbDisplay(_INTL("The shadow sky continues."))
end
# Effects due to weather
curWeather = pbWeather
priority.each do |b|
# Weather-related abilities
if b.abilityActive?
BattleHandlers.triggerEORWeatherAbility(b.ability,curWeather,b,self)
b.pbFaint if b.fainted?
end
# Weather damage
# NOTE:
case curWeather
when :Sandstorm
next if !b.takesSandstormDamage?
pbDisplay(_INTL("{1} is buffeted by the sandstorm!",b.pbThis))
@scene.pbDamageAnimation(b)
b.pbReduceHP(b.totalhp/16,false)
b.pbItemHPHealCheck
b.pbFaint if b.fainted?
when :Hail
next if !b.takesHailDamage?
pbDisplay(_INTL("{1} is buffeted by the hail!",b.pbThis))
@scene.pbDamageAnimation(b)
b.pbReduceHP(b.totalhp/16,false)
b.pbItemHPHealCheck
b.pbFaint if b.fainted?
when :ShadowSky
next if !b.takesShadowSkyDamage?
pbDisplay(_INTL("{1} is hurt by the shadow sky!",b.pbThis))
@scene.pbDamageAnimation(b)
b.pbReduceHP(b.totalhp/16,false)
b.pbItemHPHealCheck
b.pbFaint if b.fainted?
end
end
end
#=============================================================================
# End Of Round terrain
#=============================================================================
def pbEORTerrain
# Count down terrain duration
@field.terrainDuration -= 1 if @field.terrainDuration>0
# Terrain wears off
if @field.terrain != :None && @field.terrainDuration == 0
case @field.terrain
when :Electric
pbDisplay(_INTL("The electric current disappeared from the battlefield!"))
when :Grassy
pbDisplay(_INTL("The grass disappeared from the battlefield!"))
when :Misty
pbDisplay(_INTL("The mist disappeared from the battlefield!"))
when :Psychic
pbDisplay(_INTL("The weirdness disappeared from the battlefield!"))
end
@field.terrain = :None
# Start up the default terrain
pbStartTerrain(nil, @field.defaultTerrain, false) if @field.defaultTerrain != :None
return if @field.terrain == :None
end
# Terrain continues
terrain_data = GameData::BattleTerrain.try_get(@field.terrain)
pbCommonAnimation(terrain_data.animation) if terrain_data
case @field.terrain
when :Electric then pbDisplay(_INTL("An electric current is running across the battlefield."))
when :Grassy then pbDisplay(_INTL("Grass is covering the battlefield."))
when :Misty then pbDisplay(_INTL("Mist is swirling about the battlefield."))
when :Psychic then pbDisplay(_INTL("The battlefield is weird."))
end
end
#=============================================================================
# End Of Round shift distant battlers to middle positions
#=============================================================================
def pbEORShiftDistantBattlers
# Move battlers around if none are near to each other
# NOTE: This code assumes each side has a maximum of 3 battlers on it, and
# is not generalised to larger side sizes.
if !singleBattle?
swaps = [] # Each element is an array of two battler indices to swap
for side in 0...2
next if pbSideSize(side)==1 # Only battlers on sides of size 2+ need to move
# Check if any battler on this side is near any battler on the other side
anyNear = false
eachSameSideBattler(side) do |b|
eachOtherSideBattler(b) do |otherB|
next if !nearBattlers?(otherB.index,b.index)
anyNear = true
break
end
break if anyNear
end
break if anyNear
# No battlers on this side are near any battlers on the other side; try
# to move them
# NOTE: If we get to here (assuming both sides are of size 3 or less),
# there is definitely only 1 able battler on this side, so we
# don't need to worry about multiple battlers trying to move into
# the same position. If you add support for a side of size 4+,
# this code will need revising to account for that, as well as to
# add more complex code to ensure battlers will end up near each
# other.
eachSameSideBattler(side) do |b|
# Get the position to move to
pos = -1
case pbSideSize(side)
when 2 then pos = [2,3,0,1][b.index] # The unoccupied position
when 3 then pos = (side==0) ? 2 : 3 # The centre position
end
next if pos<0
# Can't move if the same trainer doesn't control both positions
idxOwner = pbGetOwnerIndexFromBattlerIndex(b.index)
next if pbGetOwnerIndexFromBattlerIndex(pos)!=idxOwner
swaps.push([b.index,pos])
end
end
# Move battlers around
swaps.each do |pair|
next if pbSideSize(pair[0])==2 && swaps.length>1
next if !pbSwapBattlers(pair[0],pair[1])
case pbSideSize(side)
when 2
pbDisplay(_INTL("{1} moved across!",@battlers[pair[1]].pbThis))
when 3
pbDisplay(_INTL("{1} moved to the center!",@battlers[pair[1]].pbThis))
end
end
end
end
#=============================================================================
# End Of Round phase
#=============================================================================
def pbEndOfRoundPhase
PBDebug.log("")
PBDebug.log("[End of round]")
@endOfRound = true
@scene.pbBeginEndOfRoundPhase
pbCalculatePriority # recalculate speeds
priority = pbPriority(true) # in order of fastest -> slowest speeds only
# Weather
pbEORWeather(priority)
# Future Sight/Doom Desire
@positions.each_with_index do |pos,idxPos|
next if !pos || pos.effects[PBEffects::FutureSightCounter]==0
pos.effects[PBEffects::FutureSightCounter] -= 1
next if pos.effects[PBEffects::FutureSightCounter]>0
next if !@battlers[idxPos] || @battlers[idxPos].fainted? # No target
moveUser = nil
eachBattler do |b|
next if b.opposes?(pos.effects[PBEffects::FutureSightUserIndex])
next if b.pokemonIndex!=pos.effects[PBEffects::FutureSightUserPartyIndex]
moveUser = b
break
end
next if moveUser && moveUser.index==idxPos # Target is the user
if !moveUser # User isn't in battle, get it from the party
party = pbParty(pos.effects[PBEffects::FutureSightUserIndex])
pkmn = party[pos.effects[PBEffects::FutureSightUserPartyIndex]]
if pkmn && pkmn.able?
moveUser = PokeBattle_Battler.new(self,pos.effects[PBEffects::FutureSightUserIndex])
moveUser.pbInitDummyPokemon(pkmn,pos.effects[PBEffects::FutureSightUserPartyIndex])
end
end
next if !moveUser # User is fainted
move = pos.effects[PBEffects::FutureSightMove]
pbDisplay(_INTL("{1} took the {2} attack!",@battlers[idxPos].pbThis,
GameData::Move.get(move).name))
# NOTE: Future Sight failing against the target here doesn't count towards
# Stomping Tantrum.
userLastMoveFailed = moveUser.lastMoveFailed
@futureSight = true
moveUser.pbUseMoveSimple(move,idxPos)
@futureSight = false
moveUser.lastMoveFailed = userLastMoveFailed
@battlers[idxPos].pbFaint if @battlers[idxPos].fainted?
pos.effects[PBEffects::FutureSightCounter] = 0
pos.effects[PBEffects::FutureSightMove] = nil
pos.effects[PBEffects::FutureSightUserIndex] = -1
pos.effects[PBEffects::FutureSightUserPartyIndex] = -1
end
# Wish
@positions.each_with_index do |pos,idxPos|
next if !pos || pos.effects[PBEffects::Wish]==0
pos.effects[PBEffects::Wish] -= 1
next if pos.effects[PBEffects::Wish]>0
next if !@battlers[idxPos] || !@battlers[idxPos].canHeal?
wishMaker = pbThisEx(idxPos,pos.effects[PBEffects::WishMaker])
@battlers[idxPos].pbRecoverHP(pos.effects[PBEffects::WishAmount])
pbDisplay(_INTL("{1}'s wish came true!",wishMaker))
end
# Sea of Fire damage (Fire Pledge + Grass Pledge combination)
curWeather = pbWeather
for side in 0...2
next if sides[side].effects[PBEffects::SeaOfFire]==0
next if [:Rain, :HeavyRain].include?(curWeather)
@battle.pbCommonAnimation("SeaOfFire") if side==0
@battle.pbCommonAnimation("SeaOfFireOpp") if side==1
priority.each do |b|
next if b.opposes?(side)
next if !b.takesIndirectDamage? || b.pbHasType?(:FIRE)
oldHP = b.hp
@scene.pbDamageAnimation(b)
b.pbReduceHP(b.totalhp/8,false)
pbDisplay(_INTL("{1} is hurt by the sea of fire!",b.pbThis))
b.pbItemHPHealCheck
b.pbAbilitiesOnDamageTaken(oldHP)
b.pbFaint if b.fainted?
end
end
# Status-curing effects/abilities and HP-healing items
priority.each do |b|
next if b.fainted?
# Grassy Terrain (healing)
if @field.terrain == :Grassy && b.affectedByTerrain? && b.canHeal?
PBDebug.log("[Lingering effect] Grassy Terrain heals #{b.pbThis(true)}")
b.pbRecoverHP(b.totalhp/16)
pbDisplay(_INTL("{1}'s HP was restored.",b.pbThis))
end
# Healer, Hydration, Shed Skin
BattleHandlers.triggerEORHealingAbility(b.ability,b,self) if b.abilityActive?
# Black Sludge, Leftovers
BattleHandlers.triggerEORHealingItem(b.item,b,self) if b.itemActive?
end
# Aqua Ring
priority.each do |b|
next if !b.effects[PBEffects::AquaRing]
next if !b.canHeal?
hpGain = b.totalhp/16
hpGain = (hpGain*1.3).floor if b.hasActiveItem?(:BIGROOT)
b.pbRecoverHP(hpGain)
pbDisplay(_INTL("Aqua Ring restored {1}'s HP!",b.pbThis(true)))
end
# Ingrain
priority.each do |b|
next if !b.effects[PBEffects::Ingrain]
next if !b.canHeal?
hpGain = b.totalhp/16
hpGain = (hpGain*1.3).floor if b.hasActiveItem?(:BIGROOT)
b.pbRecoverHP(hpGain)
pbDisplay(_INTL("{1} absorbed nutrients with its roots!",b.pbThis))
end
# Leech Seed
priority.each do |b|
next if b.effects[PBEffects::LeechSeed]<0
next if !b.takesIndirectDamage?
recipient = @battlers[b.effects[PBEffects::LeechSeed]]
next if !recipient || recipient.fainted?
oldHP = b.hp
oldHPRecipient = recipient.hp
pbCommonAnimation("LeechSeed",recipient,b)
hpLoss = b.pbReduceHP(b.totalhp/8)
recipient.pbRecoverHPFromDrain(hpLoss,b,
_INTL("{1}'s health is sapped by Leech Seed!",b.pbThis))
recipient.pbAbilitiesOnDamageTaken(oldHPRecipient) if recipient.hp<oldHPRecipient
b.pbItemHPHealCheck
b.pbAbilitiesOnDamageTaken(oldHP)
b.pbFaint if b.fainted?
recipient.pbFaint if recipient.fainted?
end
# Damage from Hyper Mode (Shadow Pokémon)
priority.each do |b|
next if !b.inHyperMode? || @choices[b.index][0]!=:UseMove
hpLoss = b.totalhp/24
@scene.pbDamageAnimation(b)
b.pbReduceHP(hpLoss,false)
pbDisplay(_INTL("The Hyper Mode attack hurts {1}!",b.pbThis(true)))
b.pbFaint if b.fainted?
end
# Damage from poisoning
priority.each do |b|
next if b.fainted?
next if b.status != :POISON
if b.statusCount>0
b.effects[PBEffects::Toxic] += 1
b.effects[PBEffects::Toxic] = 15 if b.effects[PBEffects::Toxic]>15
end
if b.hasActiveAbility?(:POISONHEAL)
if b.canHeal?
anim_name = GameData::Status.get(:POISON).animation
pbCommonAnimation(anim_name, b) if anim_name
pbShowAbilitySplash(b)
b.pbRecoverHP(b.totalhp/8)
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
pbDisplay(_INTL("{1}'s HP was restored.",b.pbThis))
else
pbDisplay(_INTL("{1}'s {2} restored its HP.",b.pbThis,b.abilityName))
end
pbHideAbilitySplash(b)
end
elsif b.takesIndirectDamage?
oldHP = b.hp
dmg = (b.statusCount==0) ? b.totalhp/8 : b.totalhp*b.effects[PBEffects::Toxic]/16
b.pbContinueStatus { b.pbReduceHP(dmg,false) }
b.pbItemHPHealCheck
b.pbAbilitiesOnDamageTaken(oldHP)
b.pbFaint if b.fainted?
end
end
# Damage from burn
priority.each do |b|
next if b.status != :BURN || !b.takesIndirectDamage?
oldHP = b.hp
dmg = (Settings::MECHANICS_GENERATION >= 7) ? b.totalhp/16 : b.totalhp/8
dmg = (dmg/2.0).round if b.hasActiveAbility?(:HEATPROOF)
b.pbContinueStatus { b.pbReduceHP(dmg,false) }
b.pbItemHPHealCheck
b.pbAbilitiesOnDamageTaken(oldHP)
b.pbFaint if b.fainted?
end
# Damage from sleep (Nightmare)
priority.each do |b|
b.effects[PBEffects::Nightmare] = false if !b.asleep?
next if !b.effects[PBEffects::Nightmare] || !b.takesIndirectDamage?
oldHP = b.hp
b.pbReduceHP(b.totalhp/4)
pbDisplay(_INTL("{1} is locked in a nightmare!",b.pbThis))
b.pbItemHPHealCheck
b.pbAbilitiesOnDamageTaken(oldHP)
b.pbFaint if b.fainted?
end
# Curse
priority.each do |b|
next if !b.effects[PBEffects::Curse] || !b.takesIndirectDamage?
oldHP = b.hp
b.pbReduceHP(b.totalhp/4)
pbDisplay(_INTL("{1} is afflicted by the curse!",b.pbThis))
b.pbItemHPHealCheck
b.pbAbilitiesOnDamageTaken(oldHP)
b.pbFaint if b.fainted?
end
# Trapping attacks (Bind/Clamp/Fire Spin/Magma Storm/Sand Tomb/Whirlpool/Wrap)
priority.each do |b|
next if b.fainted? || b.effects[PBEffects::Trapping]==0
b.effects[PBEffects::Trapping] -= 1
moveName = GameData::Move.get(b.effects[PBEffects::TrappingMove]).name
if b.effects[PBEffects::Trapping]==0
pbDisplay(_INTL("{1} was freed from {2}!",b.pbThis,moveName))
else
case b.effects[PBEffects::TrappingMove]
when :BIND then pbCommonAnimation("Bind", b)
when :CLAMP then pbCommonAnimation("Clamp", b)
when :FIRESPIN then pbCommonAnimation("FireSpin", b)
when :MAGMASTORM then pbCommonAnimation("MagmaStorm", b)
when :SANDTOMB then pbCommonAnimation("SandTomb", b)
when :WRAP then pbCommonAnimation("Wrap", b)
when :INFESTATION then pbCommonAnimation("Infestation", b)
else pbCommonAnimation("Wrap", b)
end
if b.takesIndirectDamage?
hpLoss = (Settings::MECHANICS_GENERATION >= 6) ? b.totalhp/8 : b.totalhp/16
if @battlers[b.effects[PBEffects::TrappingUser]].hasActiveItem?(:BINDINGBAND)
hpLoss = (Settings::MECHANICS_GENERATION >= 6) ? b.totalhp/6 : b.totalhp/8
end
@scene.pbDamageAnimation(b)
b.pbReduceHP(hpLoss,false)
pbDisplay(_INTL("{1} is hurt by {2}!",b.pbThis,moveName))
b.pbItemHPHealCheck
# NOTE: No need to call pbAbilitiesOnDamageTaken as b can't switch out.
b.pbFaint if b.fainted?
end
end
end
# Taunt
pbEORCountDownBattlerEffect(priority,PBEffects::Taunt) { |battler|
pbDisplay(_INTL("{1}'s taunt wore off!",battler.pbThis))
}
# Encore
priority.each do |b|
next if b.fainted? || b.effects[PBEffects::Encore]==0
idxEncoreMove = b.pbEncoredMoveIndex
if idxEncoreMove>=0
b.effects[PBEffects::Encore] -= 1
if b.effects[PBEffects::Encore]==0 || b.moves[idxEncoreMove].pp==0
b.effects[PBEffects::Encore] = 0
pbDisplay(_INTL("{1}'s encore ended!",b.pbThis))
end
else
PBDebug.log("[End of effect] #{b.pbThis}'s encore ended (encored move no longer known)")
b.effects[PBEffects::Encore] = 0
b.effects[PBEffects::EncoreMove] = nil
end
end
# Disable/Cursed Body
pbEORCountDownBattlerEffect(priority,PBEffects::Disable) { |battler|
battler.effects[PBEffects::DisableMove] = nil
pbDisplay(_INTL("{1} is no longer disabled!",battler.pbThis))
}
# Magnet Rise
pbEORCountDownBattlerEffect(priority,PBEffects::MagnetRise) { |battler|
pbDisplay(_INTL("{1}'s electromagnetism wore off!",battler.pbThis))
}
# Telekinesis
pbEORCountDownBattlerEffect(priority,PBEffects::Telekinesis) { |battler|
pbDisplay(_INTL("{1} was freed from the telekinesis!",battler.pbThis))
}
# Heal Block
pbEORCountDownBattlerEffect(priority,PBEffects::HealBlock) { |battler|
pbDisplay(_INTL("{1}'s Heal Block wore off!",battler.pbThis))
}
# Embargo
pbEORCountDownBattlerEffect(priority,PBEffects::Embargo) { |battler|
pbDisplay(_INTL("{1} can use items again!",battler.pbThis))
battler.pbItemTerrainStatBoostCheck
}
# Yawn
pbEORCountDownBattlerEffect(priority,PBEffects::Yawn) { |battler|
if battler.pbCanSleepYawn?
PBDebug.log("[Lingering effect] #{battler.pbThis} fell asleep because of Yawn")
battler.pbSleep
end
}
# Perish Song
perishSongUsers = []
priority.each do |b|
next if b.fainted? || b.effects[PBEffects::PerishSong]==0
b.effects[PBEffects::PerishSong] -= 1
pbDisplay(_INTL("{1}'s perish count fell to {2}!",b.pbThis,b.effects[PBEffects::PerishSong]))
if b.effects[PBEffects::PerishSong]==0
perishSongUsers.push(b.effects[PBEffects::PerishSongUser])
b.pbReduceHP(b.hp)
end
b.pbItemHPHealCheck
b.pbFaint if b.fainted?
end
if perishSongUsers.length>0
# If all remaining Pokemon fainted by a Perish Song triggered by a single side
if (perishSongUsers.find_all { |idxBattler| opposes?(idxBattler) }.length==perishSongUsers.length) ||
(perishSongUsers.find_all { |idxBattler| !opposes?(idxBattler) }.length==perishSongUsers.length)
pbJudgeCheckpoint(@battlers[perishSongUsers[0]])
end
end
# Check for end of battle
if @decision>0
pbGainExp
return
end
for side in 0...2
# Reflect
pbEORCountDownSideEffect(side,PBEffects::Reflect,
_INTL("{1}'s Reflect wore off!",@battlers[side].pbTeam))
# Light Screen
pbEORCountDownSideEffect(side,PBEffects::LightScreen,
_INTL("{1}'s Light Screen wore off!",@battlers[side].pbTeam))
# Safeguard
pbEORCountDownSideEffect(side,PBEffects::Safeguard,
_INTL("{1} is no longer protected by Safeguard!",@battlers[side].pbTeam))
# Mist
pbEORCountDownSideEffect(side,PBEffects::Mist,
_INTL("{1} is no longer protected by mist!",@battlers[side].pbTeam))
# Tailwind
pbEORCountDownSideEffect(side,PBEffects::Tailwind,
_INTL("{1}'s Tailwind petered out!",@battlers[side].pbTeam))
# Lucky Chant
pbEORCountDownSideEffect(side,PBEffects::LuckyChant,
_INTL("{1}'s Lucky Chant wore off!",@battlers[side].pbTeam))
# Pledge Rainbow
pbEORCountDownSideEffect(side,PBEffects::Rainbow,
_INTL("The rainbow on {1}'s side disappeared!",@battlers[side].pbTeam(true)))
# Pledge Sea of Fire
pbEORCountDownSideEffect(side,PBEffects::SeaOfFire,
_INTL("The sea of fire around {1} disappeared!",@battlers[side].pbTeam(true)))
# Pledge Swamp
pbEORCountDownSideEffect(side,PBEffects::Swamp,
_INTL("The swamp around {1} disappeared!",@battlers[side].pbTeam(true)))
# Aurora Veil
pbEORCountDownSideEffect(side,PBEffects::AuroraVeil,
_INTL("{1}'s Aurora Veil wore off!",@battlers[side].pbTeam(true)))
end
# Trick Room
pbEORCountDownFieldEffect(PBEffects::TrickRoom,
_INTL("The twisted dimensions returned to normal!"))
# Gravity
pbEORCountDownFieldEffect(PBEffects::Gravity,
_INTL("Gravity returned to normal!"))
# Water Sport
pbEORCountDownFieldEffect(PBEffects::WaterSportField,
_INTL("The effects of Water Sport have faded."))
# Mud Sport
pbEORCountDownFieldEffect(PBEffects::MudSportField,
_INTL("The effects of Mud Sport have faded."))
# Wonder Room
pbEORCountDownFieldEffect(PBEffects::WonderRoom,
_INTL("Wonder Room wore off, and Defense and Sp. Def stats returned to normal!"))
# Magic Room
pbEORCountDownFieldEffect(PBEffects::MagicRoom,
_INTL("Magic Room wore off, and held items' effects returned to normal!"))
# End of terrains
pbEORTerrain
priority.each do |b|
next if b.fainted?
# Hyper Mode (Shadow Pokémon)
if b.inHyperMode?
if pbRandom(100)<10
b.pokemon.hyper_mode = false
b.pokemon.adjustHeart(-50)
pbDisplay(_INTL("{1} came to its senses!",b.pbThis))
else
pbDisplay(_INTL("{1} is in Hyper Mode!",b.pbThis))
end
end
# Uproar
if b.effects[PBEffects::Uproar]>0
b.effects[PBEffects::Uproar] -= 1
if b.effects[PBEffects::Uproar]==0
pbDisplay(_INTL("{1} calmed down.",b.pbThis))
else
pbDisplay(_INTL("{1} is making an uproar!",b.pbThis))
end
end
# Slow Start's end message
if b.effects[PBEffects::SlowStart]>0
b.effects[PBEffects::SlowStart] -= 1
if b.effects[PBEffects::SlowStart]==0
pbDisplay(_INTL("{1} finally got its act together!",b.pbThis))
end
end
# Bad Dreams, Moody, Speed Boost
BattleHandlers.triggerEOREffectAbility(b.ability,b,self) if b.abilityActive?
# Flame Orb, Sticky Barb, Toxic Orb
BattleHandlers.triggerEOREffectItem(b.item,b,self) if b.itemActive?
# Harvest, Pickup
BattleHandlers.triggerEORGainItemAbility(b.ability,b,self) if b.abilityActive?
end
pbGainExp
return if @decision>0
# Form checks
priority.each { |b| b.pbCheckForm(true) }
# Switch Pokémon in if possible
pbEORSwitch
return if @decision>0
# In battles with at least one side of size 3+, move battlers around if none
# are near to any foes
pbEORShiftDistantBattlers
# Try to make Trace work, check for end of primordial weather
priority.each { |b| b.pbContinualAbilityChecks }
# Reset/count down battler-specific effects (no messages)
eachBattler do |b|
b.effects[PBEffects::BanefulBunker] = false
b.effects[PBEffects::Charge] -= 1 if b.effects[PBEffects::Charge]>0
b.effects[PBEffects::Counter] = -1
b.effects[PBEffects::CounterTarget] = -1
b.effects[PBEffects::Electrify] = false
b.effects[PBEffects::Endure] = false
b.effects[PBEffects::FirstPledge] = 0
b.effects[PBEffects::Flinch] = false
b.effects[PBEffects::FocusPunch] = false
b.effects[PBEffects::FollowMe] = 0
b.effects[PBEffects::HelpingHand] = false
b.effects[PBEffects::HyperBeam] -= 1 if b.effects[PBEffects::HyperBeam]>0
b.effects[PBEffects::KingsShield] = false
b.effects[PBEffects::LaserFocus] -= 1 if b.effects[PBEffects::LaserFocus]>0
if b.effects[PBEffects::LockOn]>0 # Also Mind Reader
b.effects[PBEffects::LockOn] -= 1
b.effects[PBEffects::LockOnPos] = -1 if b.effects[PBEffects::LockOn]==0
end
b.effects[PBEffects::MagicBounce] = false
b.effects[PBEffects::MagicCoat] = false
b.effects[PBEffects::MirrorCoat] = -1
b.effects[PBEffects::MirrorCoatTarget] = -1
b.effects[PBEffects::Powder] = false
b.effects[PBEffects::Prankster] = false
b.effects[PBEffects::PriorityAbility] = false
b.effects[PBEffects::PriorityItem] = false
b.effects[PBEffects::Protect] = false
b.effects[PBEffects::RagePowder] = false
b.effects[PBEffects::Roost] = false
b.effects[PBEffects::Snatch] = 0
b.effects[PBEffects::SpikyShield] = false
b.effects[PBEffects::Spotlight] = 0
b.effects[PBEffects::ThroatChop] -= 1 if b.effects[PBEffects::ThroatChop]>0
b.lastHPLost = 0
b.lastHPLostFromFoe = 0
b.tookDamage = false
b.tookPhysicalHit = false
b.lastRoundMoveFailed = b.lastMoveFailed
b.lastAttacker.clear
b.lastFoeAttacker.clear
end
# Reset/count down side-specific effects (no messages)
for side in 0...2
@sides[side].effects[PBEffects::CraftyShield] = false
if !@sides[side].effects[PBEffects::EchoedVoiceUsed]
@sides[side].effects[PBEffects::EchoedVoiceCounter] = 0
end
@sides[side].effects[PBEffects::EchoedVoiceUsed] = false
@sides[side].effects[PBEffects::MatBlock] = false
@sides[side].effects[PBEffects::QuickGuard] = false
@sides[side].effects[PBEffects::Round] = false
@sides[side].effects[PBEffects::WideGuard] = false
end
# Reset/count down field-specific effects (no messages)
@field.effects[PBEffects::IonDeluge] = false
@field.effects[PBEffects::FairyLock] -= 1 if @field.effects[PBEffects::FairyLock]>0
@field.effects[PBEffects::FusionBolt] = false
@field.effects[PBEffects::FusionFlare] = false
@endOfRound = false
end
end