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,276 @@
class PokeBattle_Animation
def initialize(sprites,viewport)
@sprites = sprites
@viewport = viewport
@pictureEx = [] # For all the PictureEx
@pictureSprites = [] # For all the sprites
@tempSprites = [] # For sprites that exist only for this animation
@animDone = false
createProcesses
end
def dispose
@tempSprites.each { |s| s.dispose if s }
end
def createProcesses; end
def empty?; return @pictureEx.length==0; end
def animDone?; return @animDone; end
def addSprite(s,origin=PictureOrigin::TopLeft)
num = @pictureEx.length
picture = PictureEx.new(s.z)
picture.x = s.x
picture.y = s.y
picture.visible = s.visible
picture.tone = s.tone.clone
picture.setOrigin(0,origin)
@pictureEx[num] = picture
@pictureSprites[num] = s
return picture
end
def addNewSprite(x,y,name,origin=PictureOrigin::TopLeft)
num = @pictureEx.length
picture = PictureEx.new(num)
picture.setXY(0,x,y)
picture.setName(0,name)
picture.setOrigin(0,origin)
@pictureEx[num] = picture
s = IconSprite.new(x,y,@viewport)
s.setBitmap(name)
@pictureSprites[num] = s
@tempSprites.push(s)
return picture
end
def update
return if @animDone
@tempSprites.each { |s| s.update if s }
finished = true
@pictureEx.each_with_index do |p,i|
next if !p.running?
finished = false
p.update
setPictureIconSprite(@pictureSprites[i],p)
end
@animDone = true if finished
end
end
module PokeBattle_BallAnimationMixin
# Returns the color that the Pokémon turns when it goes into or out of its
# Poké Ball.
def getBattlerColorFromPokeBall(poke_ball)
case poke_ball
when :GREATBALL then return Color.new(132, 189, 247)
when :SAFARIBALL then return Color.new(189, 247, 165)
when :ULTRABALL then return Color.new(255, 255, 123)
when :MASTERBALL then return Color.new(189, 165, 231)
when :NETBALL then return Color.new(173, 255, 206)
when :DIVEBALL then return Color.new( 99, 206, 247)
when :NESTBALL then return Color.new(247, 222, 82)
when :REPEATBALL then return Color.new(255, 198, 132)
when :TIMERBALL then return Color.new(239, 247, 247)
when :LUXURYBALL then return Color.new(255, 140, 82)
when :PREMIERBALL then return Color.new(255, 74, 82)
when :DUSKBALL then return Color.new(115, 115, 140)
when :HEALBALL then return Color.new(255, 198, 231)
when :QUICKBALL then return Color.new(140, 214, 255)
when :CHERISHBALL then return Color.new(247, 66, 41)
end
return Color.new(255, 181, 247) # Poké Ball, Sport Ball, Apricorn Balls, others
end
def addBallSprite(ballX, ballY, poke_ball)
file_path = sprintf("Graphics/Battle animations/ball_%s", poke_ball)
if !pbResolveBitmap(file_path)
file_path = sprintf("Graphics/Battle animations/ball_%02d", pbGetBallType(poke_ball).id_number)
end
ball = addNewSprite(ballX, ballY, file_path, PictureOrigin::Center)
@ballSprite = @pictureSprites.last
if @ballSprite.bitmap.width >= @ballSprite.bitmap.height
@ballSprite.src_rect.width = @ballSprite.bitmap.height / 2
ball.setSrcSize(0, @ballSprite.bitmap.height / 2, @ballSprite.bitmap.height)
end
return ball
end
def ballTracksHand(ball,traSprite,safariThrow=false)
# Back sprite isn't animated, no hand-tracking needed
if traSprite.bitmap.width<traSprite.bitmap.height*2
ball.setVisible(7,true)
ballStartX = traSprite.x
ballStartX -= ball.totalDuration*(Graphics.width/(2*16)) if !safariThrow
ballStartY = traSprite.y-traSprite.bitmap.height/2
return ballStartX, ballStartY
end
# Back sprite is animated, make the Poké Ball track the trainer's hand
coordSets = [[traSprite.x-44,traSprite.y-32],[-10,-36],[118,-4]]
case @trainer.trainer_type
when :POKEMONTRAINER_Leaf
coordSets = [[traSprite.x-30,traSprite.y-30],[-18,-36],[118,-6]]
when :POKEMONTRAINER_Brendan
coordSets = [[traSprite.x-46,traSprite.y-40],[-4,-30],[118,-2]]
when :POKEMONTRAINER_May
coordSets = [[traSprite.x-44,traSprite.y-38],[-8,-30],[122,0]]
end
# Arm stretched out behind player
ball.setVisible(0,true)
ball.setXY(0,coordSets[0][0],coordSets[0][1])
ball.moveDelta(0,5,-5*(Graphics.width/(2*16)),0) if !safariThrow
ball.setDelta(0,-12,0) if safariThrow
# Arm mid throw
ball.setDelta(5,coordSets[1][0],coordSets[1][1])
ball.moveDelta(5,2,-2*(Graphics.width/(2*16)),0) if !safariThrow
ball.setDelta(5,34,0) if safariThrow
# Start of throw
ball.setDelta(7,coordSets[2][0],coordSets[2][1])
ball.setDelta(7,-14,0) if safariThrow
# Update Poké Ball trajectory's start position
ballStartX = ballStartY = 0
coordSets.each do |c|
ballStartX += c[0]
ballStartY += c[1]
end
ballStartX -= ball.totalDuration*(Graphics.width/(2*16)) if !safariThrow
ballStartX += 8 if safariThrow # -12 + 34 - 14
return ballStartX, ballStartY
end
def trainerThrowingFrames(ball,trainer,traSprite)
ball.setZ(0,traSprite.z-1)
# Change trainer's frames
size = traSprite.src_rect.width # Width per frame
trainer.setSrc(0,size,0)
trainer.setSrc(5,size*2,0)
trainer.setSrc(7,size*3,0)
trainer.setSrc(9,size*4,0)
trainer.setSrc(18,0,0)
# Alter trainer's positioning
trainer.setDelta(0,-12,0)
trainer.setDelta(5,34,0)
trainer.setDelta(7,-14,0)
trainer.setDelta(9,28,0)
trainer.moveDelta(10,3,-6,6)
trainer.setDelta(18,-4,0)
trainer.setDelta(19,-26,-6)
# Make ball track the trainer's hand
ballStartX, ballStartY = ballTracksHand(ball,traSprite,true)
return ballStartX, ballStartY
end
def createBallTrajectory(ball,delay,duration,startX,startY,midX,midY,endX,endY)
# NOTE: This trajectory is the same regardless of whether the player's
# sprite is being shown on-screen (and sliding off while animating a
# throw). Instead, that throw animation and initialDelay are designed
# to make sure the Ball's trajectory starts at the same position.
ball.setVisible(delay,true)
a = 2*startY - 4*midY + 2*endY
b = 4*midY - 3*startY - endY
c = startY
for i in 1..duration
t = i.to_f/duration # t ranges from 0 to 1
x = startX + (endX-startX)*t # Linear in t
y = a*t**2 + b*t + c # Quadratic in t
ball.moveXY(delay+i-1,1,x,y)
end
createBallTumbling(ball,delay,duration)
end
def createBallTumbling(ball,delay,duration)
# Animate ball frames
numTumbles = 1
numFrames = 1
if @ballSprite && @ballSprite.bitmap.width>=@ballSprite.bitmap.height
# 2* because each frame is twice as tall as it is wide
numFrames = 2*@ballSprite.bitmap.width/@ballSprite.bitmap.height
end
if numFrames>1
curFrame = 0
for i in 1..duration
thisFrame = numFrames*numTumbles*i/duration
if thisFrame>curFrame
curFrame = thisFrame
ball.setSrc(delay+i-1,(curFrame%numFrames)*@ballSprite.bitmap.height/2,0)
end
end
ball.setSrc(delay+duration,0,0)
end
# Rotate ball
ball.moveAngle(delay,duration,360*3)
ball.setAngle(delay+duration,0)
end
def ballSetOpen(ball, delay, poke_ball)
file_path = sprintf("Graphics/Battle animations/ball_%s_open", poke_ball)
if !pbResolveBitmap(file_path)
file_path = sprintf("Graphics/Battle animations/ball_%02d_open", pbGetBallType(poke_ball).id_number)
end
ball.setName(delay, file_path)
if @ballSprite && @ballSprite.bitmap.width >= @ballSprite.bitmap.height
ball.setSrcSize(delay, @ballSprite.bitmap.height / 2, @ballSprite.bitmap.height)
end
end
def ballSetClosed(ball, delay, poke_ball)
file_path = sprintf("Graphics/Battle animations/ball_%s", poke_ball)
if !pbResolveBitmap(file_path)
file_path = sprintf("Graphics/Battle animations/ball_%02d", pbGetBallType(poke_ball).id_number)
end
ball.setName(delay, file_path)
if @ballSprite && @ballSprite.bitmap.width >= @ballSprite.bitmap.height
ball.setSrcSize(delay, @ballSprite.bitmap.height / 2, @ballSprite.bitmap.height)
end
end
def ballOpenUp(ball, delay, poke_ball, showSquish = true, playSE = true)
if showSquish
ball.moveZoomXY(delay, 1, 120, 80) # Squish
ball.moveZoom(delay + 5, 1, 100) # Unsquish
delay += 6
end
ball.setSE(delay, "Battle recall") if playSE
ballSetOpen(ball, delay, poke_ball)
end
def battlerAppear(battler,delay,battlerX,battlerY,batSprite,color)
battler.setVisible(delay,true)
battler.setOpacity(delay,255)
battler.moveXY(delay,5,battlerX,battlerY)
battler.moveZoom(delay,5,100,[batSprite,:pbPlayIntroAnimation])
# NOTE: As soon as the battler sprite finishes zooming, and just as it
# starts changing its tone to normal, it plays its intro animation.
color.alpha = 0
battler.moveColor(delay+5,10,color)
end
def battlerAbsorb(battler,delay,battlerX,battlerY,color)
color.alpha = 255
battler.moveColor(delay,10,color)
delay = battler.totalDuration
battler.moveXY(delay,5,battlerX,battlerY)
battler.moveZoom(delay,5,0)
battler.setVisible(delay+5,false)
end
# The regular Poké Ball burst animation.
def ballBurst(delay, ballX, ballY, poke_ball)
end
# The Poké Ball burst animation used when absorbing a wild Pokémon during a
# capture attempt.
def ballBurstCapture(delay, ballX, ballY, poke_ball)
end
def ballCaptureSuccess(ball, delay, ballX, ballY)
ball.setSE(delay, "Battle catch click")
ball.moveTone(delay, 4, Tone.new(-64, -64, -64, 128))
end
# The Poké Ball burst animation used when recalling a Pokémon.
def ballBurstRecall(delay, ballX, ballY, poke_ball)
end
end

View File

@@ -0,0 +1,869 @@
#===============================================================================
# Shows the battle scene fading in while elements slide around into place
#===============================================================================
class BattleIntroAnimation < PokeBattle_Animation
def initialize(sprites,viewport,battle)
@battle = battle
super(sprites,viewport)
end
def createProcesses
appearTime = 20 # This is in 1/20 seconds
# Background
if @sprites["battle_bg2"]
makeSlideSprite("battle_bg",0.5,appearTime)
makeSlideSprite("battle_bg2",0.5,appearTime)
end
# Bases
makeSlideSprite("base_0",1,appearTime,PictureOrigin::Bottom)
makeSlideSprite("base_1",-1,appearTime,PictureOrigin::Center)
# Player sprite, partner trainer sprite
@battle.player.each_with_index do |_p,i|
makeSlideSprite("player_#{i+1}",1,appearTime,PictureOrigin::Bottom)
end
# Opposing trainer sprite(s) or wild Pokémon sprite(s)
if @battle.trainerBattle?
@battle.opponent.each_with_index do |_p,i|
makeSlideSprite("trainer_#{i+1}",-1,appearTime,PictureOrigin::Bottom)
end
else # Wild battle
@battle.pbParty(1).each_with_index do |_pkmn,i|
idxBattler = 2*i+1
makeSlideSprite("pokemon_#{idxBattler}",-1,appearTime,PictureOrigin::Bottom)
end
end
# Shadows
for i in 0...@battle.battlers.length
makeSlideSprite("shadow_#{i}",((i%2)==0) ? 1 : -1,appearTime,PictureOrigin::Center)
end
# Fading blackness over whole screen
blackScreen = addNewSprite(0,0,"Graphics/Battle animations/black_screen")
blackScreen.setZ(0,999)
blackScreen.moveOpacity(0,8,0)
# Fading blackness over command bar
blackBar = addNewSprite(@sprites["cmdBar_bg"].x,@sprites["cmdBar_bg"].y,
"Graphics/Battle animations/black_bar")
blackBar.setZ(0,998)
blackBar.moveOpacity(appearTime*3/4,appearTime/4,0)
end
def makeSlideSprite(spriteName,deltaMult,appearTime,origin=nil)
# If deltaMult is positive, the sprite starts off to the right and moves
# left (for sprites on the player's side and the background).
return if !@sprites[spriteName]
s = addSprite(@sprites[spriteName],origin)
s.setDelta(0,(Graphics.width*deltaMult).floor,0)
s.moveDelta(0,appearTime,(-Graphics.width*deltaMult).floor,0)
end
end
#===============================================================================
# Shows wild Pokémon fading back to their normal color, and triggers their intro
# animations
#===============================================================================
class BattleIntroAnimation2 < PokeBattle_Animation
def initialize(sprites,viewport,sideSize)
@sideSize = sideSize
super(sprites,viewport)
end
def createProcesses
for i in 0...@sideSize
idxBattler = 2*i+1
next if !@sprites["pokemon_#{idxBattler}"]
battler = addSprite(@sprites["pokemon_#{idxBattler}"],PictureOrigin::Bottom)
battler.moveTone(0,4,Tone.new(0,0,0,0))
battler.setCallback(10*i,[@sprites["pokemon_#{idxBattler}"],:pbPlayIntroAnimation])
end
end
end
#===============================================================================
# Makes a side's party bar and balls appear
#===============================================================================
class LineupAppearAnimation < PokeBattle_Animation
BAR_DISPLAY_WIDTH = 248
def initialize(sprites,viewport,side,party,partyStarts,fullAnim)
@side = side
@party = party
@partyStarts = partyStarts
@fullAnim = fullAnim # True at start of battle, false when switching
resetGraphics(sprites)
super(sprites,viewport)
end
def resetGraphics(sprites)
bar = sprites["partyBar_#{@side}"]
case @side
when 0 # Player's lineup
barX = Graphics.width - BAR_DISPLAY_WIDTH
barY = Graphics.height - 142
ballX = barX + 44
ballY = barY - 30
when 1 # Opposing lineup
barX = BAR_DISPLAY_WIDTH
barY = 114
ballX = barX - 44 - 30 # 30 is width of ball icon
ballY = barY - 30
barX -= bar.bitmap.width
end
ballXdiff = 32*(1-2*@side)
bar.x = barX
bar.y = barY
bar.opacity = 255
bar.visible = false
for i in 0...PokeBattle_SceneConstants::NUM_BALLS
ball = sprites["partyBall_#{@side}_#{i}"]
ball.x = ballX
ball.y = ballY
ball.opacity = 255
ball.visible = false
ballX += ballXdiff
end
end
def getPartyIndexFromBallIndex(idxBall)
# Player's lineup (just show balls for player's party)
if @side==0
return idxBall if @partyStarts.length<2
return idxBall if idxBall<@partyStarts[1]
return -1
end
# Opposing lineup
# NOTE: This doesn't work well for 4+ opposing trainers.
ballsPerTrainer = PokeBattle_SceneConstants::NUM_BALLS/@partyStarts.length # 6/3/2
startsIndex = idxBall/ballsPerTrainer
teamIndex = idxBall%ballsPerTrainer
ret = @partyStarts[startsIndex]+teamIndex
if startsIndex<@partyStarts.length-1
# There is a later trainer, don't spill over into its team
return -1 if ret>=@partyStarts[startsIndex+1]
end
return ret
end
def createProcesses
bar = addSprite(@sprites["partyBar_#{@side}"])
bar.setVisible(0,true)
dir = (@side==0) ? 1 : -1
bar.setDelta(0,dir*Graphics.width/2,0)
bar.moveDelta(0,8,-dir*Graphics.width/2,0)
delay = bar.totalDuration
for i in 0...PokeBattle_SceneConstants::NUM_BALLS
createBall(i,(@fullAnim) ? delay+i*2 : 0,dir)
end
end
def createBall(idxBall,delay,dir)
# Choose ball's graphic
idxParty = getPartyIndexFromBallIndex(idxBall)
graphicFilename = "Graphics/Pictures/Battle/icon_ball_empty"
if idxParty>=0 && idxParty<@party.length && @party[idxParty]
if !@party[idxParty].able?
graphicFilename = "Graphics/Pictures/Battle/icon_ball_faint"
elsif @party[idxParty].status != :NONE
graphicFilename = "Graphics/Pictures/Battle/icon_ball_status"
else
graphicFilename = "Graphics/Pictures/Battle/icon_ball"
end
end
# Set up ball sprite
ball = addSprite(@sprites["partyBall_#{@side}_#{idxBall}"])
ball.setVisible(delay,true)
ball.setName(delay,graphicFilename)
ball.setDelta(delay,dir*Graphics.width/2,0)
ball.moveDelta(delay,8,-dir*Graphics.width/2,0)
end
end
#===============================================================================
# Makes a Pokémon's data box appear
#===============================================================================
class DataBoxAppearAnimation < PokeBattle_Animation
def initialize(sprites,viewport,idxBox)
@idxBox = idxBox
super(sprites,viewport)
end
def createProcesses
return if !@sprites["dataBox_#{@idxBox}"]
box = addSprite(@sprites["dataBox_#{@idxBox}"])
box.setVisible(0,true)
dir = ((@idxBox%2)==0) ? 1 : -1
box.setDelta(0,dir*Graphics.width/2,0)
box.moveDelta(0,8,-dir*Graphics.width/2,0)
end
end
#===============================================================================
# Makes a Pokémon's data box disappear
#===============================================================================
class DataBoxDisappearAnimation < PokeBattle_Animation
def initialize(sprites,viewport,idxBox)
@idxBox = idxBox
super(sprites,viewport)
end
def createProcesses
return if !@sprites["dataBox_#{@idxBox}"] || !@sprites["dataBox_#{@idxBox}"].visible
box = addSprite(@sprites["dataBox_#{@idxBox}"])
dir = ((@idxBox%2)==0) ? 1 : -1
box.moveDelta(0,8,dir*Graphics.width/2,0)
box.setVisible(8,false)
end
end
#===============================================================================
# Makes a Pokémon's ability bar appear
#===============================================================================
class AbilitySplashAppearAnimation < PokeBattle_Animation
def initialize(sprites,viewport,side)
@side = side
super(sprites,viewport)
end
def createProcesses
return if !@sprites["abilityBar_#{@side}"]
bar = addSprite(@sprites["abilityBar_#{@side}"])
bar.setVisible(0,true)
dir = (@side==0) ? 1 : -1
bar.moveDelta(0,8,dir*Graphics.width/2,0)
end
end
#===============================================================================
# Makes a Pokémon's ability bar disappear
#===============================================================================
class AbilitySplashDisappearAnimation < PokeBattle_Animation
def initialize(sprites,viewport,side)
@side = side
super(sprites,viewport)
end
def createProcesses
return if !@sprites["abilityBar_#{@side}"]
bar = addSprite(@sprites["abilityBar_#{@side}"])
dir = (@side==0) ? -1 : 1
bar.moveDelta(0,8,dir*Graphics.width/2,0)
bar.setVisible(8,false)
end
end
#===============================================================================
# Make an enemy trainer slide on-screen from the right. Makes the previous
# trainer slide off to the right first if it is on-screen.
# Used at the end of battle.
#===============================================================================
class TrainerAppearAnimation < PokeBattle_Animation
def initialize(sprites,viewport,idxTrainer)
@idxTrainer = idxTrainer
super(sprites,viewport)
end
def createProcesses
delay = 0
# Make old trainer sprite move off-screen first if necessary
if @idxTrainer>0 && @sprites["trainer_#{@idxTrainer}"].visible
oldTrainer = addSprite(@sprites["trainer_#{@idxTrainer}"],PictureOrigin::Bottom)
oldTrainer.moveDelta(delay,8,Graphics.width/4,0)
oldTrainer.setVisible(delay+8,false)
delay = oldTrainer.totalDuration
end
# Make new trainer sprite move on-screen
if @sprites["trainer_#{@idxTrainer+1}"]
trainerX, trainerY = PokeBattle_SceneConstants.pbTrainerPosition(1)
trainerX += 64+Graphics.width/4
newTrainer = addSprite(@sprites["trainer_#{@idxTrainer+1}"],PictureOrigin::Bottom)
newTrainer.setVisible(delay,true)
newTrainer.setXY(delay,trainerX,trainerY)
newTrainer.moveDelta(delay,8,-Graphics.width/4,0)
end
end
end
#===============================================================================
# Shows the player (and partner) and the player party lineup sliding off screen.
# Shows the player's/partner's throwing animation (if they have one).
# Doesn't show the ball thrown or the Pokémon.
#===============================================================================
class PlayerFadeAnimation < PokeBattle_Animation
def initialize(sprites,viewport,fullAnim=false)
@fullAnim = fullAnim # True at start of battle, false when switching
super(sprites,viewport)
end
def createProcesses
# NOTE: The movement speeds of trainers/bar/balls are all different.
# Move trainer sprite(s) off-screen
spriteNameBase = "player"
i = 1
while @sprites[spriteNameBase+"_#{i}"]
pl = @sprites[spriteNameBase+"_#{i}"]
i += 1
next if !pl.visible || pl.x<0
trainer = addSprite(pl,PictureOrigin::Bottom)
trainer.moveDelta(0,16,-Graphics.width/2,0)
# Animate trainer sprite(s) if they have multiple frames
if pl.bitmap && !pl.bitmap.disposed? && pl.bitmap.width>=pl.bitmap.height*2
size = pl.src_rect.width # Width per frame
trainer.setSrc(0,size,0)
trainer.setSrc(5,size*2,0)
trainer.setSrc(7,size*3,0)
trainer.setSrc(9,size*4,0)
end
trainer.setVisible(16,false)
end
# Move and fade party bar/balls
delay = 3
if @sprites["partyBar_0"] && @sprites["partyBar_0"].visible
partyBar = addSprite(@sprites["partyBar_0"])
partyBar.moveDelta(delay,16,-Graphics.width/4,0) if @fullAnim
partyBar.moveOpacity(delay,12,0)
partyBar.setVisible(delay+12,false)
partyBar.setOpacity(delay+12,255)
end
for i in 0...PokeBattle_SceneConstants::NUM_BALLS
next if !@sprites["partyBall_0_#{i}"] || !@sprites["partyBall_0_#{i}"].visible
partyBall = addSprite(@sprites["partyBall_0_#{i}"])
partyBall.moveDelta(delay+2*i,16,-Graphics.width,0) if @fullAnim
partyBall.moveOpacity(delay,12,0)
partyBall.setVisible(delay+12,false)
partyBall.setOpacity(delay+12,255)
end
end
end
#===============================================================================
# Shows the enemy trainer(s) and the enemy party lineup sliding off screen.
# Doesn't show the ball thrown or the Pokémon.
#===============================================================================
class TrainerFadeAnimation < PokeBattle_Animation
def initialize(sprites,viewport,fullAnim=false)
@fullAnim = fullAnim # True at start of battle, false when switching
super(sprites,viewport)
end
def createProcesses
# NOTE: The movement speeds of trainers/bar/balls are all different.
# Move trainer sprite(s) off-screen
spriteNameBase = "trainer"
i = 1
while @sprites[spriteNameBase+"_#{i}"]
trSprite = @sprites[spriteNameBase+"_#{i}"]
i += 1
next if !trSprite.visible || trSprite.x>Graphics.width
trainer = addSprite(trSprite,PictureOrigin::Bottom)
trainer.moveDelta(0,16,Graphics.width/2,0)
trainer.setVisible(16,false)
end
# Move and fade party bar/balls
delay = 3
if @sprites["partyBar_1"] && @sprites["partyBar_1"].visible
partyBar = addSprite(@sprites["partyBar_1"])
partyBar.moveDelta(delay,16,Graphics.width/4,0) if @fullAnim
partyBar.moveOpacity(delay,12,0)
partyBar.setVisible(delay+12,false)
partyBar.setOpacity(delay+12,255)
end
for i in 0...PokeBattle_SceneConstants::NUM_BALLS
next if !@sprites["partyBall_1_#{i}"] || !@sprites["partyBall_1_#{i}"].visible
partyBall = addSprite(@sprites["partyBall_1_#{i}"])
partyBall.moveDelta(delay+2*i,16,Graphics.width,0) if @fullAnim
partyBall.moveOpacity(delay,12,0)
partyBall.setVisible(delay+12,false)
partyBall.setOpacity(delay+12,255)
end
end
end
#===============================================================================
# Shows a Pokémon being sent out on the player's side (including by a partner).
# Includes the Poké Ball being thrown.
#===============================================================================
class PokeballPlayerSendOutAnimation < PokeBattle_Animation
include PokeBattle_BallAnimationMixin
def initialize(sprites,viewport,idxTrainer,battler,startBattle,idxOrder=0)
@idxTrainer = idxTrainer
@battler = battler
@showingTrainer = startBattle
@idxOrder = idxOrder
@trainer = @battler.battle.pbGetOwnerFromBattlerIndex(@battler.index)
sprites["pokemon_#{battler.index}"].visible = false
@shadowVisible = sprites["shadow_#{battler.index}"].visible
sprites["shadow_#{battler.index}"].visible = false
super(sprites,viewport)
end
def createProcesses
batSprite = @sprites["pokemon_#{@battler.index}"]
shaSprite = @sprites["shadow_#{@battler.index}"]
traSprite = @sprites["player_#{@idxTrainer}"]
# Calculate the Poké Ball graphic to use
poke_ball = (batSprite.pkmn) ? batSprite.pkmn.poke_ball : nil
# Calculate the color to turn the battler sprite
col = getBattlerColorFromPokeBall(poke_ball)
col.alpha = 255
# Calculate start and end coordinates for battler sprite movement
ballPos = PokeBattle_SceneConstants.pbBattlerPosition(@battler.index,batSprite.sideSize)
battlerStartX = ballPos[0] # Is also where the Ball needs to end
battlerStartY = ballPos[1] # Is also where the Ball needs to end + 18
battlerEndX = batSprite.x
battlerEndY = batSprite.y
# Calculate start and end coordinates for Poké Ball sprite movement
ballStartX = -6
ballStartY = 202
ballMidX = 0 # Unused in trajectory calculation
ballMidY = battlerStartY-144
# Set up Poké Ball sprite
ball = addBallSprite(ballStartX,ballStartY,poke_ball)
ball.setZ(0,25)
ball.setVisible(0,false)
# Poké Ball tracking the player's hand animation (if trainer is visible)
if @showingTrainer && traSprite && traSprite.x>0
ball.setZ(0,traSprite.z-1)
ballStartX, ballStartY = ballTracksHand(ball,traSprite)
end
delay = ball.totalDuration # 0 or 7
# Poké Ball trajectory animation
createBallTrajectory(ball,delay,12,
ballStartX,ballStartY,ballMidX,ballMidY,battlerStartX,battlerStartY-18)
ball.setZ(9,batSprite.z-1)
delay = ball.totalDuration+4
delay += 10*@idxOrder # Stagger appearances if multiple Pokémon are sent out at once
ballOpenUp(ball,delay-2,poke_ball)
ballBurst(delay,battlerStartX,battlerStartY-18,poke_ball)
ball.moveOpacity(delay+2,2,0)
# Set up battler sprite
battler = addSprite(batSprite,PictureOrigin::Bottom)
battler.setXY(0,battlerStartX,battlerStartY)
battler.setZoom(0,0)
battler.setColor(0,col)
# Battler animation
battlerAppear(battler,delay,battlerEndX,battlerEndY,batSprite,col)
if @shadowVisible
# Set up shadow sprite
shadow = addSprite(shaSprite,PictureOrigin::Center)
shadow.setOpacity(0,0)
# Shadow animation
shadow.setVisible(delay,@shadowVisible)
shadow.moveOpacity(delay+5,10,255)
end
end
end
#===============================================================================
# Shows a Pokémon being sent out on the opposing side.
# Includes the Poké Ball being "thrown" (although here the Poké Ball just
# appears in the spot where it opens up rather than being thrown to there).
#===============================================================================
class PokeballTrainerSendOutAnimation < PokeBattle_Animation
include PokeBattle_BallAnimationMixin
def initialize(sprites,viewport,idxTrainer,battler,startBattle,idxOrder)
@idxTrainer = idxTrainer
@battler = battler
@showingTrainer = startBattle
@idxOrder = idxOrder
sprites["pokemon_#{battler.index}"].visible = false
@shadowVisible = sprites["shadow_#{battler.index}"].visible
sprites["shadow_#{battler.index}"].visible = false
super(sprites,viewport)
end
def createProcesses
batSprite = @sprites["pokemon_#{@battler.index}"]
shaSprite = @sprites["shadow_#{@battler.index}"]
# Calculate the Poké Ball graphic to use
poke_ball = (batSprite.pkmn) ? batSprite.pkmn.poke_ball : nil
# Calculate the color to turn the battler sprite
col = getBattlerColorFromPokeBall(poke_ball)
col.alpha = 255
# Calculate start and end coordinates for battler sprite movement
ballPos = PokeBattle_SceneConstants.pbBattlerPosition(@battler.index,batSprite.sideSize)
battlerStartX = ballPos[0]
battlerStartY = ballPos[1]
battlerEndX = batSprite.x
battlerEndY = batSprite.y
# Set up Poké Ball sprite
ball = addBallSprite(0,0,poke_ball)
ball.setZ(0,batSprite.z-1)
# Poké Ball animation
createBallTrajectory(ball,battlerStartX,battlerStartY)
delay = ball.totalDuration+6
delay += 10 if @showingTrainer # Give time for trainer to slide off screen
delay += 10*@idxOrder # Stagger appearances if multiple Pokémon are sent out at once
ballOpenUp(ball,delay-2,poke_ball)
ballBurst(delay,battlerStartX,battlerStartY-18,poke_ball)
ball.moveOpacity(delay+2,2,0)
# Set up battler sprite
battler = addSprite(batSprite,PictureOrigin::Bottom)
battler.setXY(0,battlerStartX,battlerStartY)
battler.setZoom(0,0)
battler.setColor(0,col)
# Battler animation
battlerAppear(battler,delay,battlerEndX,battlerEndY,batSprite,col)
if @shadowVisible
# Set up shadow sprite
shadow = addSprite(shaSprite,PictureOrigin::Center)
shadow.setOpacity(0,0)
# Shadow animation
shadow.setVisible(delay,@shadowVisible)
shadow.moveOpacity(delay+5,10,255)
end
end
def createBallTrajectory(ball,destX,destY)
# NOTE: In HGSS, there isn't a Poké Ball arc under any circumstance (neither
# when throwing out the first Pokémon nor when switching/replacing a
# fainted Pokémon). This is probably worth changing.
ball.setXY(0,destX,destY-4)
end
end
#===============================================================================
# Shows a Pokémon being recalled into its Poké Ball
#===============================================================================
class BattlerRecallAnimation < PokeBattle_Animation
include PokeBattle_BallAnimationMixin
def initialize(sprites,viewport,idxBattler)
@idxBattler = idxBattler
super(sprites,viewport)
end
def createProcesses
batSprite = @sprites["pokemon_#{@idxBattler}"]
shaSprite = @sprites["shadow_#{@idxBattler}"]
# Calculate the Poké Ball graphic to use
poke_ball = (batSprite.pkmn) ? batSprite.pkmn.poke_ball : nil
# Calculate the color to turn the battler sprite
col = getBattlerColorFromPokeBall(poke_ball)
col.alpha = 0
# Calculate end coordinates for battler sprite movement
ballPos = PokeBattle_SceneConstants.pbBattlerPosition(@idxBattler,batSprite.sideSize)
battlerEndX = ballPos[0]
battlerEndY = ballPos[1]
# Set up battler sprite
battler = addSprite(batSprite,PictureOrigin::Bottom)
battler.setVisible(0,true)
battler.setColor(0,col)
# Set up Poké Ball sprite
ball = addBallSprite(battlerEndX,battlerEndY,poke_ball)
ball.setZ(0,batSprite.z+1)
# Poké Ball animation
ballOpenUp(ball,0,poke_ball)
delay = ball.totalDuration
ballBurstRecall(delay,battlerEndX,battlerEndY,poke_ball)
ball.moveOpacity(10,2,0)
# Battler animation
battlerAbsorb(battler,delay,battlerEndX,battlerEndY,col)
if shaSprite.visible
# Set up shadow sprite
shadow = addSprite(shaSprite,PictureOrigin::Center)
# Shadow animation
shadow.moveOpacity(0,10,0)
shadow.setVisible(delay,false)
end
end
end
#===============================================================================
# Shows a Pokémon flashing after taking damage
#===============================================================================
class BattlerDamageAnimation < PokeBattle_Animation
def initialize(sprites,viewport,idxBattler,effectiveness)
@idxBattler = idxBattler
@effectiveness = effectiveness
super(sprites,viewport)
end
def createProcesses
batSprite = @sprites["pokemon_#{@idxBattler}"]
shaSprite = @sprites["shadow_#{@idxBattler}"]
# Set up battler/shadow sprite
battler = addSprite(batSprite,PictureOrigin::Bottom)
shadow = addSprite(shaSprite,PictureOrigin::Center)
# Animation
delay = 0
case @effectiveness
when 0 then battler.setSE(delay, "Battle damage normal")
when 1 then battler.setSE(delay, "Battle damage weak")
when 2 then battler.setSE(delay, "Battle damage super")
end
4.times do # 4 flashes, each lasting 0.2 (4/20) seconds
battler.setVisible(delay,false)
shadow.setVisible(delay,false)
battler.setVisible(delay+2,true) if batSprite.visible
shadow.setVisible(delay+2,true) if shaSprite.visible
delay += 4
end
# Restore original battler/shadow sprites visibilities
battler.setVisible(delay,batSprite.visible)
shadow.setVisible(delay,shaSprite.visible)
end
end
#===============================================================================
# Shows a Pokémon fainting
#===============================================================================
class BattlerFaintAnimation < PokeBattle_Animation
def initialize(sprites,viewport,idxBattler,battle)
@idxBattler = idxBattler
@battle = battle
super(sprites,viewport)
end
def createProcesses
batSprite = @sprites["pokemon_#{@idxBattler}"]
shaSprite = @sprites["shadow_#{@idxBattler}"]
# Set up battler/shadow sprite
battler = addSprite(batSprite,PictureOrigin::Bottom)
shadow = addSprite(shaSprite,PictureOrigin::Center)
# Get approx duration depending on sprite's position/size. Min 20 frames.
battlerTop = batSprite.y-batSprite.height
cropY = PokeBattle_SceneConstants.pbBattlerPosition(@idxBattler,
@battle.pbSideSize(@idxBattler))[1]
cropY += 8
duration = (cropY-battlerTop)/8
duration = 10 if duration<10 # Min 0.5 seconds
# Animation
# Play cry
delay = 10
cry = GameData::Species.cry_filename_from_pokemon(batSprite.pkmn)
if cry
battler.setSE(0, cry, nil, 75) # 75 is pitch
delay = GameData::Species.cry_length(batSprite.pkmn) * 20 / Graphics.frame_rate
end
# Sprite drops down
shadow.setVisible(delay,false)
battler.setSE(delay,"Pkmn faint")
battler.moveOpacity(delay,duration,0)
battler.moveDelta(delay,duration,0,cropY-battlerTop)
battler.setCropBottom(delay,cropY)
battler.setVisible(delay+duration,false)
battler.setOpacity(delay+duration,255)
end
end
#===============================================================================
# Shows the player's Poké Ball being thrown to capture a Pokémon
#===============================================================================
class PokeballThrowCaptureAnimation < PokeBattle_Animation
include PokeBattle_BallAnimationMixin
def initialize(sprites,viewport,
poke_ball,numShakes,critCapture,battler,showingTrainer)
@poke_ball = poke_ball
@numShakes = (critCapture) ? 1 : numShakes
@critCapture = critCapture
@battler = battler
@showingTrainer = showingTrainer # Only true if a Safari Zone battle
@shadowVisible = sprites["shadow_#{battler.index}"].visible
@trainer = battler.battle.pbPlayer
super(sprites,viewport)
end
def createProcesses
# Calculate start and end coordinates for battler sprite movement
batSprite = @sprites["pokemon_#{@battler.index}"]
shaSprite = @sprites["shadow_#{@battler.index}"]
traSprite = @sprites["player_1"]
ballPos = PokeBattle_SceneConstants.pbBattlerPosition(@battler.index,batSprite.sideSize)
battlerStartX = batSprite.x
battlerStartY = batSprite.y
ballStartX = -6
ballStartY = 246
ballMidX = 0 # Unused in arc calculation
ballMidY = 78
ballEndX = ballPos[0]
ballEndY = 112
ballGroundY = ballPos[1]-4
# Set up Poké Ball sprite
ball = addBallSprite(ballStartX,ballStartY,@poke_ball)
ball.setZ(0,batSprite.z+1)
@ballSpriteIndex = (@numShakes>=4 || @critCapture) ? @tempSprites.length-1 : -1
# Set up trainer sprite (only visible in Safari Zone battles)
if @showingTrainer && traSprite
if traSprite.bitmap.width>=traSprite.bitmap.height*2
trainer = addSprite(traSprite,PictureOrigin::Bottom)
# Trainer animation
ballStartX, ballStartY = trainerThrowingFrames(ball,trainer,traSprite)
end
end
delay = ball.totalDuration # 0 or 7
# Poké Ball arc animation
ball.setSE(delay,"Battle throw")
createBallTrajectory(ball,delay,16,
ballStartX,ballStartY,ballMidX,ballMidY,ballEndX,ballEndY)
ball.setZ(9,batSprite.z+1)
ball.setSE(delay+16,"Battle ball hit")
# Poké Ball opens up
delay = ball.totalDuration+6
ballOpenUp(ball,delay,@poke_ball,true,false)
# Set up battler sprite
battler = addSprite(batSprite,PictureOrigin::Bottom)
# Poké Ball absorbs battler
delay = ball.totalDuration
ballBurstCapture(delay,ballEndX,ballEndY,@poke_ball)
delay = ball.totalDuration+4
# NOTE: The Pokémon does not change color while being absorbed into a Poké
# Ball during a capture attempt. This may be an oversight in HGSS.
battler.setSE(delay,"Battle jump to ball")
battler.moveXY(delay,5,ballEndX,ballEndY)
battler.moveZoom(delay,5,0)
battler.setVisible(delay+5,false)
if @shadowVisible
# Set up shadow sprite
shadow = addSprite(shaSprite,PictureOrigin::Center)
# Shadow animation
shadow.moveOpacity(delay,5,0)
shadow.moveZoom(delay,5,0)
shadow.setVisible(delay+5,false)
end
# Poké Ball closes
delay = battler.totalDuration
ballSetClosed(ball,delay,@poke_ball)
ball.moveTone(delay,3,Tone.new(96,64,-160,160))
ball.moveTone(delay+5,3,Tone.new(0,0,0,0))
# Poké Ball critical capture animation
delay = ball.totalDuration+3
if @critCapture
ball.setSE(delay,"Battle ball shake")
ball.moveXY(delay,1,ballEndX+4,ballEndY)
ball.moveXY(delay+1,2,ballEndX-4,ballEndY)
ball.moveXY(delay+3,2,ballEndX+4,ballEndY)
ball.setSE(delay+4,"Battle ball shake")
ball.moveXY(delay+5,2,ballEndX-4,ballEndY)
ball.moveXY(delay+7,1,ballEndX,ballEndY)
delay = ball.totalDuration+3
end
# Poké Ball drops to the ground
for i in 0...4
t = [4,4,3,2][i] # Time taken to rise or fall for each bounce
d = [1,2,4,8][i] # Fraction of the starting height each bounce rises to
delay -= t if i==0
if i>0
ball.setZoomXY(delay,100+5*(5-i),100-5*(5-i)) # Squish
ball.moveZoom(delay,2,100) # Unsquish
ball.moveXY(delay,t,ballEndX,ballGroundY-(ballGroundY-ballEndY)/d)
end
ball.moveXY(delay+t,t,ballEndX,ballGroundY)
ball.setSE(delay+2*t,"Battle ball drop",100-i*7)
delay = ball.totalDuration
end
battler.setXY(ball.totalDuration,ballEndX,ballGroundY)
# Poké Ball shakes
delay = ball.totalDuration+12
for i in 0...[@numShakes,3].min
ball.setSE(delay,"Battle ball shake")
ball.moveXY(delay,2,ballEndX-2*(4-i),ballGroundY)
ball.moveAngle(delay,2,5*(4-i)) # positive means counterclockwise
ball.moveXY(delay+2,4,ballEndX+2*(4-i),ballGroundY)
ball.moveAngle(delay+2,4,-5*(4-i)) # negative means clockwise
ball.moveXY(delay+6,2,ballEndX,ballGroundY)
ball.moveAngle(delay+6,2,0)
delay = ball.totalDuration+8
end
if @numShakes==0 || (@numShakes<4 && !@critCapture)
# Poké Ball opens
ball.setZ(delay,batSprite.z-1)
ballOpenUp(ball,delay,@poke_ball,false)
ballBurst(delay,ballEndX,ballGroundY,@poke_ball)
ball.moveOpacity(delay+2,2,0)
# Battler emerges
col = getBattlerColorFromPokeBall(@poke_ball)
col.alpha = 255
battler.setColor(delay,col)
battlerAppear(battler,delay,battlerStartX,battlerStartY,batSprite,col)
if @shadowVisible
shadow.setVisible(delay+5,true)
shadow.setZoom(delay+5,100)
shadow.moveOpacity(delay+5,10,255)
end
else
# Pokémon was caught
ballCaptureSuccess(ball,delay,ballEndX,ballGroundY)
end
end
def dispose
if @ballSpriteIndex>=0
# Capture was successful, the Poké Ball sprite should stay around after
# this animation has finished.
@sprites["captureBall"] = @tempSprites[@ballSpriteIndex]
@tempSprites[@ballSpriteIndex] = nil
end
super
end
end
#===============================================================================
# Shows the player throwing a Poké Ball and it being deflected
#===============================================================================
class PokeballThrowDeflectAnimation < PokeBattle_Animation
include PokeBattle_BallAnimationMixin
def initialize(sprites,viewport,poke_ball,battler)
@poke_ball = poke_ball
@battler = battler
super(sprites,viewport)
end
def createProcesses
# Calculate start and end coordinates for battler sprite movement
batSprite = @sprites["pokemon_#{@battler.index}"]
ballPos = PokeBattle_SceneConstants.pbBattlerPosition(@battler.index,batSprite.sideSize)
ballStartX = -6
ballStartY = 246
ballMidX = 190 # Unused in arc calculation
ballMidY = 78
ballEndX = ballPos[0]
ballEndY = 112
# Set up Poké Ball sprite
ball = addBallSprite(ballStartX,ballStartY,@poke_ball)
ball.setZ(0,90)
# Poké Ball arc animation
ball.setSE(0,"Battle throw")
createBallTrajectory(ball,0,16,
ballStartX,ballStartY,ballMidX,ballMidY,ballEndX,ballEndY)
# Poké Ball knocked back
delay = ball.totalDuration
ball.setSE(delay,"Battle ball drop")
ball.moveXY(delay,8,-32,Graphics.height-96+32) # Back to player's corner
createBallTumbling(ball,delay,8)
end
end

View File

@@ -0,0 +1,67 @@
module PokeBattle_SceneConstants
USE_ABILITY_SPLASH = true
# Text colors
MESSAGE_BASE_COLOR = Color.new(80, 80, 88)
MESSAGE_SHADOW_COLOR = Color.new(160, 160, 168)
# The number of party balls to show in each side's lineup.
NUM_BALLS = Settings::MAX_PARTY_SIZE
# Centre bottom of the player's side base graphic
PLAYER_BASE_X = 128
PLAYER_BASE_Y = Settings::SCREEN_HEIGHT - 80
# Centre middle of the foe's side base graphic
FOE_BASE_X = Settings::SCREEN_WIDTH - 128
FOE_BASE_Y = (Settings::SCREEN_HEIGHT * 3 / 4) - 112
# Returns where the centre bottom of a battler's sprite should be, given its
# index and the number of battlers on its side, assuming the battler has
# metrics of 0 (those are added later).
def self.pbBattlerPosition(index, sideSize = 1)
# Start at the centre of the base for the appropriate side
if (index & 1) == 0
ret = [PLAYER_BASE_X, PLAYER_BASE_Y]
else
ret = [FOE_BASE_X, FOE_BASE_Y]
end
# Shift depending on index (no shifting needed for sideSize of 1)
case sideSize
when 2
ret[0] += [-48, 48, 32, -32][index]
ret[1] += [ 0, 0, 16, -16][index]
when 3
ret[0] += [-80, 80, 0, 0, 80, -80][index]
ret[1] += [ 0, 0, 8, -8, 16, -16][index]
end
return ret
end
# Returns where the centre bottom of a trainer's sprite should be, given its
# side (0/1), index and the number of trainers on its side.
def self.pbTrainerPosition(side, index = 0, sideSize = 1)
# Start at the centre of the base for the appropriate side
if side == 0
ret = [PLAYER_BASE_X, PLAYER_BASE_Y - 16]
else
ret = [FOE_BASE_X, FOE_BASE_Y + 6]
end
# Shift depending on index (no shifting needed for sideSize of 1)
case sideSize
when 2
ret[0] += [-48, 48, 32, -32][2 * index + side]
ret[1] += [ 0, 0, 0, -16][2 * index + side]
when 3
ret[0] += [-80, 80, 0, 0, 80, -80][2 * index + side]
ret[1] += [ 0, 0, 0, -8, 0, -16][2 * index + side]
end
return ret
end
# Default focal points of user and target in animations - do not change!
# Is the centre middle of each sprite
FOCUSUSER_X = 128 # 144
FOCUSUSER_Y = 224 # 188
FOCUSTARGET_X = 384 # 352
FOCUSTARGET_Y = 96 # 108, 98
end

View File

@@ -0,0 +1,659 @@
#===============================================================================
# Data box for regular battles
#===============================================================================
class PokemonDataBox < SpriteWrapper
attr_reader :battler
attr_accessor :selected
attr_reader :animatingHP
attr_reader :animatingExp
# Time in seconds to fully fill the Exp bar (from empty).
EXP_BAR_FILL_TIME = 1.75
# Maximum time in seconds to make a change to the HP bar.
HP_BAR_CHANGE_TIME = 1.0
STATUS_ICON_HEIGHT = 16
NAME_BASE_COLOR = Color.new(72,72,72)
NAME_SHADOW_COLOR = Color.new(184,184,184)
MALE_BASE_COLOR = Color.new(48,96,216)
MALE_SHADOW_COLOR = NAME_SHADOW_COLOR
FEMALE_BASE_COLOR = Color.new(248,88,40)
FEMALE_SHADOW_COLOR = NAME_SHADOW_COLOR
def initialize(battler,sideSize,viewport=nil)
super(viewport)
@battler = battler
@sprites = {}
@spriteX = 0
@spriteY = 0
@spriteBaseX = 0
@selected = 0
@frame = 0
@showHP = false # Specifically, show the HP numbers
@animatingHP = false
@showExp = false # Specifically, show the Exp bar
@animatingExp = false
@expFlash = 0
initializeDataBoxGraphic(sideSize)
initializeOtherGraphics(viewport)
refresh
end
def initializeDataBoxGraphic(sideSize)
onPlayerSide = ((@battler.index%2)==0)
# Get the data box graphic and set whether the HP numbers/Exp bar are shown
if sideSize==1 # One Pokémon on side, use the regular dara box BG
bgFilename = ["Graphics/Pictures/Battle/databox_normal",
"Graphics/Pictures/Battle/databox_normal_foe"][@battler.index%2]
if onPlayerSide
@showHP = true
@showExp = true
end
else # Multiple Pokémon on side, use the thin dara box BG
bgFilename = ["Graphics/Pictures/Battle/databox_thin",
"Graphics/Pictures/Battle/databox_thin_foe"][@battler.index%2]
end
@databoxBitmap = AnimatedBitmap.new(bgFilename)
# Determine the co-ordinates of the data box and the left edge padding width
if onPlayerSide
@spriteX = Graphics.width - 244
@spriteY = Graphics.height - 192
@spriteBaseX = 34
else
@spriteX = -16
@spriteY = 36
@spriteBaseX = 16
end
case sideSize
when 2
@spriteX += [-12, 12, 0, 0][@battler.index]
@spriteY += [-20, -34, 34, 20][@battler.index]
when 3
@spriteX += [-12, 12, -6, 6, 0, 0][@battler.index]
@spriteY += [-42, -46, 4, 0, 50, 46][@battler.index]
end
end
def initializeOtherGraphics(viewport)
# Create other bitmaps
@numbersBitmap = AnimatedBitmap.new(_INTL("Graphics/Pictures/Battle/icon_numbers"))
@hpBarBitmap = AnimatedBitmap.new(_INTL("Graphics/Pictures/Battle/overlay_hp"))
@expBarBitmap = AnimatedBitmap.new(_INTL("Graphics/Pictures/Battle/overlay_exp"))
# Create sprite to draw HP numbers on
@hpNumbers = BitmapSprite.new(124,16,viewport)
pbSetSmallFont(@hpNumbers.bitmap)
@sprites["hpNumbers"] = @hpNumbers
# Create sprite wrapper that displays HP bar
@hpBar = SpriteWrapper.new(viewport)
@hpBar.bitmap = @hpBarBitmap.bitmap
@hpBar.src_rect.height = @hpBarBitmap.height/3
@sprites["hpBar"] = @hpBar
# Create sprite wrapper that displays Exp bar
@expBar = SpriteWrapper.new(viewport)
@expBar.bitmap = @expBarBitmap.bitmap
@sprites["expBar"] = @expBar
# Create sprite wrapper that displays everything except the above
@contents = BitmapWrapper.new(@databoxBitmap.width,@databoxBitmap.height)
self.bitmap = @contents
self.visible = false
self.z = 150+((@battler.index)/2)*5
pbSetSystemFont(self.bitmap)
end
def dispose
pbDisposeSpriteHash(@sprites)
@databoxBitmap.dispose
@numbersBitmap.dispose
@hpBarBitmap.dispose
@expBarBitmap.dispose
@contents.dispose
super
end
def x=(value)
super
@hpBar.x = value+@spriteBaseX+102
@expBar.x = value+@spriteBaseX+6
@hpNumbers.x = value+@spriteBaseX+80
end
def y=(value)
super
@hpBar.y = value+40
@expBar.y = value+74
@hpNumbers.y = value+52
end
def z=(value)
super
@hpBar.z = value+1
@expBar.z = value+1
@hpNumbers.z = value+2
end
def opacity=(value)
super
for i in @sprites
i[1].opacity = value if !i[1].disposed?
end
end
def visible=(value)
super
for i in @sprites
i[1].visible = value if !i[1].disposed?
end
@expBar.visible = (value && @showExp)
end
def color=(value)
super
for i in @sprites
i[1].color = value if !i[1].disposed?
end
end
def battler=(b)
@battler = b
self.visible = (@battler && !@battler.fainted?)
end
def hp
return (@animatingHP) ? @currentHP : @battler.hp
end
def exp_fraction
return (@animatingExp) ? @currentExp.to_f/@rangeExp : @battler.pokemon.exp_fraction
end
def animateHP(oldHP,newHP,rangeHP)
@currentHP = oldHP
@endHP = newHP
@rangeHP = rangeHP
# NOTE: A change in HP takes the same amount of time to animate, no matter
# how big a change it is.
@hpIncPerFrame = (newHP-oldHP).abs/(HP_BAR_CHANGE_TIME*Graphics.frame_rate)
# minInc is the smallest amount that HP is allowed to change per frame.
# This avoids a tiny change in HP still taking HP_BAR_CHANGE_TIME seconds.
minInc = (rangeHP*4)/(@hpBarBitmap.width*HP_BAR_CHANGE_TIME*Graphics.frame_rate)
@hpIncPerFrame = minInc if @hpIncPerFrame<minInc
@animatingHP = true
end
def animateExp(oldExp,newExp,rangeExp)
@currentExp = oldExp
@endExp = newExp
@rangeExp = rangeExp
# NOTE: Filling the Exp bar from empty to full takes EXP_BAR_FILL_TIME
# seconds no matter what. Filling half of it takes half as long, etc.
@expIncPerFrame = rangeExp/(EXP_BAR_FILL_TIME*Graphics.frame_rate)
@animatingExp = true
pbSEPlay("Pkmn exp gain") if @showExp
end
def pbDrawNumber(number,btmp,startX,startY,align=0)
# -1 means draw the / character
n = (number == -1) ? [10] : number.to_i.digits.reverse
charWidth = @numbersBitmap.width/11
charHeight = @numbersBitmap.height
startX -= charWidth*n.length if align==1
n.each do |i|
btmp.blt(startX,startY,@numbersBitmap.bitmap,Rect.new(i*charWidth,0,charWidth,charHeight))
startX += charWidth
end
end
def refresh
self.bitmap.clear
return if !@battler.pokemon
textPos = []
imagePos = []
# Draw background panel
self.bitmap.blt(0,0,@databoxBitmap.bitmap,Rect.new(0,0,@databoxBitmap.width,@databoxBitmap.height))
# Draw Pokémon's name
nameWidth = self.bitmap.text_size(@battler.name).width
nameOffset = 0
nameOffset = nameWidth-116 if nameWidth>116
textPos.push([@battler.name,@spriteBaseX+8-nameOffset,0,false,NAME_BASE_COLOR,NAME_SHADOW_COLOR])
# Draw Pokémon's gender symbol
case @battler.displayGender
when 0 # Male
textPos.push([_INTL(""),@spriteBaseX+126,0,false,MALE_BASE_COLOR,MALE_SHADOW_COLOR])
when 1 # Female
textPos.push([_INTL(""),@spriteBaseX+126,0,false,FEMALE_BASE_COLOR,FEMALE_SHADOW_COLOR])
end
pbDrawTextPositions(self.bitmap,textPos)
# Draw Pokémon's level
imagePos.push(["Graphics/Pictures/Battle/overlay_lv",@spriteBaseX+140,16])
pbDrawNumber(@battler.level,self.bitmap,@spriteBaseX+162,16)
# Draw shiny icon
if @battler.shiny?
shinyX = (@battler.opposes?(0)) ? 206 : -6 # Foe's/player's
imagePos.push(["Graphics/Pictures/shiny",@spriteBaseX+shinyX,36])
end
# Draw Mega Evolution/Primal Reversion icon
if @battler.mega?
imagePos.push(["Graphics/Pictures/Battle/icon_mega",@spriteBaseX+8,34])
elsif @battler.primal?
primalX = (@battler.opposes?) ? 208 : -28 # Foe's/player's
if @battler.isSpecies?(:KYOGRE)
imagePos.push(["Graphics/Pictures/Battle/icon_primal_Kyogre",@spriteBaseX+primalX,4])
elsif @battler.isSpecies?(:GROUDON)
imagePos.push(["Graphics/Pictures/Battle/icon_primal_Groudon",@spriteBaseX+primalX,4])
end
end
# Draw owned icon (foe Pokémon only)
if @battler.owned? && @battler.opposes?(0)
imagePos.push(["Graphics/Pictures/Battle/icon_own",@spriteBaseX+8,36])
end
# Draw status icon
if @battler.status != :NONE
s = GameData::Status.get(@battler.status).id_number
if s == :POISON && @battler.statusCount > 0 # Badly poisoned
s = GameData::Status::DATA.keys.length / 2
end
imagePos.push(["Graphics/Pictures/Battle/icon_statuses",@spriteBaseX+24,36,
0,(s-1)*STATUS_ICON_HEIGHT,-1,STATUS_ICON_HEIGHT])
end
pbDrawImagePositions(self.bitmap,imagePos)
refreshHP
refreshExp
end
def refreshHP
@hpNumbers.bitmap.clear
return if !@battler.pokemon
# Show HP numbers
if @showHP
pbDrawNumber(self.hp,@hpNumbers.bitmap,54,2,1)
pbDrawNumber(-1,@hpNumbers.bitmap,54,2) # / char
pbDrawNumber(@battler.totalhp,@hpNumbers.bitmap,70,2)
end
# Resize HP bar
w = 0
if self.hp>0
w = @hpBarBitmap.width.to_f*self.hp/@battler.totalhp
w = 1 if w<1
# NOTE: The line below snaps the bar's width to the nearest 2 pixels, to
# fit in with the rest of the graphics which are doubled in size.
w = ((w/2.0).round)*2
end
@hpBar.src_rect.width = w
hpColor = 0 # Green bar
hpColor = 1 if self.hp<=@battler.totalhp/2 # Yellow bar
hpColor = 2 if self.hp<=@battler.totalhp/4 # Red bar
@hpBar.src_rect.y = hpColor*@hpBarBitmap.height/3
end
def refreshExp
return if !@showExp
w = exp_fraction * @expBarBitmap.width
# NOTE: The line below snaps the bar's width to the nearest 2 pixels, to
# fit in with the rest of the graphics which are doubled in size.
w = ((w/2).round)*2
@expBar.src_rect.width = w
end
def updateHPAnimation
return if !@animatingHP
if @currentHP<@endHP # Gaining HP
@currentHP += @hpIncPerFrame
@currentHP = @endHP if @currentHP>=@endHP
elsif @currentHP>@endHP # Losing HP
@currentHP -= @hpIncPerFrame
@currentHP = @endHP if @currentHP<=@endHP
end
# Refresh the HP bar/numbers
refreshHP
@animatingHP = false if @currentHP==@endHP
end
def updateExpAnimation
return if !@animatingExp
if !@showExp # Not showing the Exp bar, no need to waste time animating it
@currentExp = @endExp
@animatingExp = false
return
end
if @currentExp<@endExp # Gaining Exp
@currentExp += @expIncPerFrame
@currentExp = @endExp if @currentExp>=@endExp
elsif @currentExp>@endExp # Losing Exp
@currentExp -= @expIncPerFrame
@currentExp = @endExp if @currentExp<=@endExp
end
# Refresh the Exp bar
refreshExp
return if @currentExp!=@endExp # Exp bar still has more to animate
# Exp bar is completely filled, level up with a flash and sound effect
if @currentExp>=@rangeExp
if @expFlash==0
pbSEStop
@expFlash = Graphics.frame_rate/5
pbSEPlay("Pkmn exp full")
self.flash(Color.new(64,200,248,192),@expFlash)
for i in @sprites
i[1].flash(Color.new(64,200,248,192),@expFlash) if !i[1].disposed?
end
else
@expFlash -= 1
@animatingExp = false if @expFlash==0
end
else
pbSEStop
# Exp bar has finished filling, end animation
@animatingExp = false
end
end
QUARTER_ANIM_PERIOD = Graphics.frame_rate*3/20
def updatePositions(frameCounter)
self.x = @spriteX
self.y = @spriteY
# Data box bobbing while Pokémon is selected
if @selected==1 || @selected==2 # Choosing commands/targeted or damaged
case (frameCounter/QUARTER_ANIM_PERIOD).floor
when 1 then self.y = @spriteY-2
when 3 then self.y = @spriteY+2
end
end
end
def update(frameCounter=0)
super()
# Animate HP bar
updateHPAnimation
# Animate Exp bar
updateExpAnimation
# Update coordinates of the data box
updatePositions(frameCounter)
pbUpdateSpriteHash(@sprites)
end
end
#===============================================================================
# Splash bar to announce a triggered ability
#===============================================================================
class AbilitySplashBar < SpriteWrapper
attr_reader :battler
TEXT_BASE_COLOR = Color.new(0,0,0)
TEXT_SHADOW_COLOR = Color.new(248,248,248)
def initialize(side,viewport=nil)
super(viewport)
@side = side
@battler = nil
# Create sprite wrapper that displays background graphic
@bgBitmap = AnimatedBitmap.new(_INTL("Graphics/Pictures/Battle/ability_bar"))
@bgSprite = SpriteWrapper.new(viewport)
@bgSprite.bitmap = @bgBitmap.bitmap
@bgSprite.src_rect.y = (side==0) ? 0 : @bgBitmap.height/2
@bgSprite.src_rect.height = @bgBitmap.height/2
# Create bitmap that displays the text
@contents = BitmapWrapper.new(@bgBitmap.width,@bgBitmap.height/2)
self.bitmap = @contents
pbSetSystemFont(self.bitmap)
# Position the bar
self.x = (side==0) ? -Graphics.width/2 : Graphics.width
self.y = (side==0) ? 180 : 80
self.z = 120
self.visible = false
end
def dispose
@bgSprite.dispose
@bgBitmap.dispose
@contents.dispose
super
end
def x=(value)
super
@bgSprite.x = value
end
def y=(value)
super
@bgSprite.y = value
end
def z=(value)
super
@bgSprite.z = value-1
end
def opacity=(value)
super
@bgSprite.opacity = value
end
def visible=(value)
super
@bgSprite.visible = value
end
def color=(value)
super
@bgSprite.color = value
end
def battler=(value)
@battler = value
refresh
end
def refresh
self.bitmap.clear
return if !@battler
textPos = []
textX = (@side==0) ? 10 : self.bitmap.width-8
# Draw Pokémon's name
textPos.push([_INTL("{1}'s",@battler.name),textX,-4,@side==1,
TEXT_BASE_COLOR,TEXT_SHADOW_COLOR,true])
# Draw Pokémon's ability
textPos.push([@battler.abilityName,textX,26,@side==1,
TEXT_BASE_COLOR,TEXT_SHADOW_COLOR,true])
pbDrawTextPositions(self.bitmap,textPos)
end
def update
super
@bgSprite.update
end
end
#===============================================================================
# Pokémon sprite (used in battle)
#===============================================================================
class PokemonBattlerSprite < RPG::Sprite
attr_reader :pkmn
attr_accessor :index
attr_accessor :selected
attr_reader :sideSize
def initialize(viewport,sideSize,index,battleAnimations)
super(viewport)
@pkmn = nil
@sideSize = sideSize
@index = index
@battleAnimations = battleAnimations
# @selected: 0 = not selected, 1 = choosing action bobbing for this Pokémon,
# 2 = flashing when targeted
@selected = 0
@frame = 0
@updating = false
@spriteX = 0 # Actual x coordinate
@spriteY = 0 # Actual y coordinate
@spriteXExtra = 0 # Offset due to "bobbing" animation
@spriteYExtra = 0 # Offset due to "bobbing" animation
@_iconBitmap = nil
self.visible = false
end
def dispose
@_iconBitmap.dispose if @_iconBitmap
@_iconBitmap = nil
self.bitmap = nil if !self.disposed?
super
end
def x; return @spriteX; end
def y; return @spriteY; end
def x=(value)
@spriteX = value
super(value+@spriteXExtra)
end
def y=(value)
@spriteY = value
super(value+@spriteYExtra)
end
def width; return (self.bitmap) ? self.bitmap.width : 0; end
def height; return (self.bitmap) ? self.bitmap.height : 0; end
def visible=(value)
@spriteVisible = value if !@updating # For selection/targeting flashing
super
end
# Set sprite's origin to bottom middle
def pbSetOrigin
return if !@_iconBitmap
self.ox = @_iconBitmap.width/2
self.oy = @_iconBitmap.height
end
def pbSetPosition
return if !@_iconBitmap
pbSetOrigin
if (@index%2)==0
self.z = 50+5*@index/2
else
self.z = 50-5*(@index+1)/2
end
# Set original position
p = PokeBattle_SceneConstants.pbBattlerPosition(@index,@sideSize)
@spriteX = p[0]
@spriteY = p[1]
# Apply metrics
@pkmn.species_data.apply_metrics_to_sprite(self, @index)
end
def setPokemonBitmap(pkmn,back=false)
@pkmn = pkmn
@_iconBitmap.dispose if @_iconBitmap
@_iconBitmap = GameData::Species.sprite_bitmap_from_pokemon(@pkmn, back)
self.bitmap = (@_iconBitmap) ? @_iconBitmap.bitmap : nil
pbSetPosition
end
# This method plays the battle entrance animation of a Pokémon. By default
# this is just playing the Pokémon's cry, but you can expand on it. The
# recommendation is to create a PictureEx animation and push it into
# the @battleAnimations array.
def pbPlayIntroAnimation(pictureEx=nil)
return if !@pkmn
GameData::Species.play_cry_from_pokemon(@pkmn)
end
QUARTER_ANIM_PERIOD = Graphics.frame_rate*3/20
SIXTH_ANIM_PERIOD = Graphics.frame_rate*2/20
def update(frameCounter=0)
return if !@_iconBitmap
@updating = true
# Update bitmap
@_iconBitmap.update
self.bitmap = @_iconBitmap.bitmap
# Pokémon sprite bobbing while Pokémon is selected
@spriteYExtra = 0
if @selected==1 # When choosing commands for this Pokémon
case (frameCounter/QUARTER_ANIM_PERIOD).floor
when 1 then @spriteYExtra = 2
when 3 then @spriteYExtra = -2
end
end
self.x = self.x
self.y = self.y
self.visible = @spriteVisible
# Pokémon sprite blinking when targeted
if @selected==2 && @spriteVisible
case (frameCounter/SIXTH_ANIM_PERIOD).floor
when 2, 5 then self.visible = false
else self.visible = true
end
end
@updating = false
end
end
#===============================================================================
# Shadow sprite for Pokémon (used in battle)
#===============================================================================
class PokemonBattlerShadowSprite < RPG::Sprite
attr_reader :pkmn
attr_accessor :index
attr_accessor :selected
def initialize(viewport,sideSize,index)
super(viewport)
@pkmn = nil
@sideSize = sideSize
@index = index
@_iconBitmap = nil
self.visible = false
end
def dispose
@_iconBitmap.dispose if @_iconBitmap
@_iconBitmap = nil
self.bitmap = nil if !self.disposed?
super
end
def width; return (self.bitmap) ? self.bitmap.width : 0; end
def height; return (self.bitmap) ? self.bitmap.height : 0; end
# Set sprite's origin to centre
def pbSetOrigin
return if !@_iconBitmap
self.ox = @_iconBitmap.width/2
self.oy = @_iconBitmap.height/2
end
def pbSetPosition
return if !@_iconBitmap
pbSetOrigin
self.z = 3
# Set original position
p = PokeBattle_SceneConstants.pbBattlerPosition(@index,@sideSize)
self.x = p[0]
self.y = p[1]
# Apply metrics
@pkmn.species_data.apply_metrics_to_sprite(self, @index, true)
end
def setPokemonBitmap(pkmn)
@pkmn = pkmn
@_iconBitmap.dispose if @_iconBitmap
@_iconBitmap = GameData::Species.shadow_bitmap_from_pokemon(@pkmn)
self.bitmap = (@_iconBitmap) ? @_iconBitmap.bitmap : nil
pbSetPosition
end
def update(frameCounter=0)
return if !@_iconBitmap
# Update bitmap
@_iconBitmap.update
self.bitmap = @_iconBitmap.bitmap
end
end

View File

@@ -0,0 +1,552 @@
#===============================================================================
# Base class for all three menu classes below
#===============================================================================
class BattleMenuBase
attr_accessor :x
attr_accessor :y
attr_reader :z
attr_reader :visible
attr_reader :color
attr_reader :index
attr_reader :mode
# NOTE: Button width is half the width of the graphic containing them all.
BUTTON_HEIGHT = 46
TEXT_BASE_COLOR = PokeBattle_SceneConstants::MESSAGE_BASE_COLOR
TEXT_SHADOW_COLOR = PokeBattle_SceneConstants::MESSAGE_SHADOW_COLOR
def initialize(viewport=nil)
@x = 0
@y = 0
@z = 0
@visible = false
@color = Color.new(0,0,0,0)
@index = 0
@mode = 0
@disposed = false
@sprites = {}
@visibility = {}
end
def dispose
return if disposed?
pbDisposeSpriteHash(@sprites)
@disposed = true
end
def disposed?; return @disposed; end
def z=(value)
@z = value
for i in @sprites
i[1].z = value if !i[1].disposed?
end
end
def visible=(value)
@visible = value
for i in @sprites
i[1].visible = (value && @visibility[i[0]]) if !i[1].disposed?
end
end
def color=(value)
@color = value
for i in @sprites
i[1].color = value if !i[1].disposed?
end
end
def index=(value)
oldValue = @index
@index = value
@cmdWindow.index = @index if @cmdWindow
refresh if @index!=oldValue
end
def mode=(value)
oldValue = @mode
@mode = value
refresh if @mode!=oldValue
end
def addSprite(key,sprite)
@sprites[key] = sprite
@visibility[key] = true
end
def setIndexAndMode(index,mode)
oldIndex = @index
oldMode = @mode
@index = index
@mode = mode
@cmdWindow.index = @index if @cmdWindow
refresh if @index!=oldIndex || @mode!=oldMode
end
def refresh; end
def update
pbUpdateSpriteHash(@sprites)
end
end
#===============================================================================
# Command menu (Fight/Pokémon/Bag/Run)
#===============================================================================
class CommandMenuDisplay < BattleMenuBase
# If true, displays graphics from Graphics/Pictures/Battle/overlay_command.png
# and Graphics/Pictures/Battle/cursor_command.png.
# If false, just displays text and the command window over the graphic
# Graphics/Pictures/Battle/overlay_message.png. You will need to edit def
# pbShowWindow to make the graphic appear while the command menu is being
# displayed.
USE_GRAPHICS = true
# Lists of which button graphics to use in different situations/types of battle.
MODES = [
[0,2,1,3], # 0 = Regular battle
[0,2,1,9], # 1 = Regular battle with "Cancel" instead of "Run"
[0,2,1,4], # 2 = Regular battle with "Call" instead of "Run"
[5,7,6,3], # 3 = Safari Zone
[0,8,1,3] # 4 = Bug Catching Contest
]
def initialize(viewport,z)
super(viewport)
self.x = 0
self.y = Graphics.height-96
# Create message box (shows "What will X do?")
@msgBox = Window_UnformattedTextPokemon.newWithSize("",
self.x+16,self.y+2,220,Graphics.height-self.y,viewport)
@msgBox.baseColor = TEXT_BASE_COLOR
@msgBox.shadowColor = TEXT_SHADOW_COLOR
@msgBox.windowskin = nil
addSprite("msgBox",@msgBox)
if USE_GRAPHICS
# Create background graphic
background = IconSprite.new(self.x,self.y,viewport)
background.setBitmap("Graphics/Pictures/Battle/overlay_command")
addSprite("background",background)
# Create bitmaps
@buttonBitmap = AnimatedBitmap.new(_INTL("Graphics/Pictures/Battle/cursor_command"))
# Create action buttons
@buttons = Array.new(4) do |i| # 4 command options, therefore 4 buttons
button = SpriteWrapper.new(viewport)
button.bitmap = @buttonBitmap.bitmap
button.x = self.x+Graphics.width-260
button.x += (((i%2)==0) ? 0 : @buttonBitmap.width/2-4)
button.y = self.y+6
button.y += (((i/2)==0) ? 0 : BUTTON_HEIGHT-4)
button.src_rect.width = @buttonBitmap.width/2
button.src_rect.height = BUTTON_HEIGHT
addSprite("button_#{i}",button)
next button
end
else
# Create command window (shows Fight/Bag/Pokémon/Run)
@cmdWindow = Window_CommandPokemon.newWithSize([],
self.x+Graphics.width-240,self.y,240,Graphics.height-self.y,viewport)
@cmdWindow.columns = 2
@cmdWindow.columnSpacing = 4
@cmdWindow.ignore_input = true
addSprite("cmdWindow",@cmdWindow)
end
self.z = z
refresh
end
def dispose
super
@buttonBitmap.dispose if @buttonBitmap
end
def z=(value)
super
@msgBox.z += 1
@cmdWindow.z += 1 if @cmdWindow
end
def setTexts(value)
@msgBox.text = value[0]
return if USE_GRAPHICS
commands = []
for i in 1..4
commands.push(value[i]) if value[i] && value[i]!=nil
end
@cmdWindow.commands = commands
end
def refreshButtons
return if !USE_GRAPHICS
for i in 0...@buttons.length
button = @buttons[i]
button.src_rect.x = (i==@index) ? @buttonBitmap.width/2 : 0
button.src_rect.y = MODES[@mode][i]*BUTTON_HEIGHT
button.z = self.z + ((i==@index) ? 3 : 2)
end
end
def refresh
@msgBox.refresh
@cmdWindow.refresh if @cmdWindow
refreshButtons
end
end
#===============================================================================
# Fight menu (choose a move)
#===============================================================================
class FightMenuDisplay < BattleMenuBase
attr_reader :battler
attr_reader :shiftMode
# If true, displays graphics from Graphics/Pictures/Battle/overlay_fight.png
# and Graphics/Pictures/Battle/cursor_fight.png.
# If false, just displays text and the command window over the graphic
# Graphics/Pictures/Battle/overlay_message.png. You will need to edit def
# pbShowWindow to make the graphic appear while the command menu is being
# displayed.
USE_GRAPHICS = true
TYPE_ICON_HEIGHT = 28
# Text colours of PP of selected move
PP_COLORS = [
Color.new(248,72,72),Color.new(136,48,48), # Red, zero PP
Color.new(248,136,32),Color.new(144,72,24), # Orange, 1/4 of total PP or less
Color.new(248,192,0),Color.new(144,104,0), # Yellow, 1/2 of total PP or less
TEXT_BASE_COLOR,TEXT_SHADOW_COLOR # Black, more than 1/2 of total PP
]
def initialize(viewport,z)
super(viewport)
self.x = 0
self.y = Graphics.height-96
@battler = nil
@shiftMode = 0
# NOTE: @mode is for the display of the Mega Evolution button.
# 0=don't show, 1=show unpressed, 2=show pressed
if USE_GRAPHICS
# Create bitmaps
@buttonBitmap = AnimatedBitmap.new(_INTL("Graphics/Pictures/Battle/cursor_fight"))
@typeBitmap = AnimatedBitmap.new(_INTL("Graphics/Pictures/types"))
@megaEvoBitmap = AnimatedBitmap.new(_INTL("Graphics/Pictures/Battle/cursor_mega"))
@shiftBitmap = AnimatedBitmap.new(_INTL("Graphics/Pictures/Battle/cursor_shift"))
# Create background graphic
background = IconSprite.new(0,Graphics.height-96,viewport)
background.setBitmap("Graphics/Pictures/Battle/overlay_fight")
addSprite("background",background)
# Create move buttons
@buttons = Array.new(Pokemon::MAX_MOVES) do |i|
button = SpriteWrapper.new(viewport)
button.bitmap = @buttonBitmap.bitmap
button.x = self.x+4
button.x += (((i%2)==0) ? 0 : @buttonBitmap.width/2-4)
button.y = self.y+6
button.y += (((i/2)==0) ? 0 : BUTTON_HEIGHT-4)
button.src_rect.width = @buttonBitmap.width/2
button.src_rect.height = BUTTON_HEIGHT
addSprite("button_#{i}",button)
next button
end
# Create overlay for buttons (shows move names)
@overlay = BitmapSprite.new(Graphics.width,Graphics.height-self.y,viewport)
@overlay.x = self.x
@overlay.y = self.y
pbSetNarrowFont(@overlay.bitmap)
addSprite("overlay",@overlay)
# Create overlay for selected move's info (shows move's PP)
@infoOverlay = BitmapSprite.new(Graphics.width,Graphics.height-self.y,viewport)
@infoOverlay.x = self.x
@infoOverlay.y = self.y
pbSetNarrowFont(@infoOverlay.bitmap)
addSprite("infoOverlay",@infoOverlay)
# Create type icon
@typeIcon = SpriteWrapper.new(viewport)
@typeIcon.bitmap = @typeBitmap.bitmap
@typeIcon.x = self.x+416
@typeIcon.y = self.y+20
@typeIcon.src_rect.height = TYPE_ICON_HEIGHT
addSprite("typeIcon",@typeIcon)
# Create Mega Evolution button
@megaButton = SpriteWrapper.new(viewport)
@megaButton.bitmap = @megaEvoBitmap.bitmap
@megaButton.x = self.x+120
@megaButton.y = self.y-@megaEvoBitmap.height/2
@megaButton.src_rect.height = @megaEvoBitmap.height/2
addSprite("megaButton",@megaButton)
# Create Shift button
@shiftButton = SpriteWrapper.new(viewport)
@shiftButton.bitmap = @shiftBitmap.bitmap
@shiftButton.x = self.x+4
@shiftButton.y = self.y-@shiftBitmap.height
addSprite("shiftButton",@shiftButton)
else
# Create message box (shows type and PP of selected move)
@msgBox = Window_AdvancedTextPokemon.newWithSize("",
self.x+320,self.y,Graphics.width-320,Graphics.height-self.y,viewport)
@msgBox.baseColor = TEXT_BASE_COLOR
@msgBox.shadowColor = TEXT_SHADOW_COLOR
pbSetNarrowFont(@msgBox.contents)
addSprite("msgBox",@msgBox)
# Create command window (shows moves)
@cmdWindow = Window_CommandPokemon.newWithSize([],
self.x,self.y,320,Graphics.height-self.y,viewport)
@cmdWindow.columns = 2
@cmdWindow.columnSpacing = 4
@cmdWindow.ignore_input = true
pbSetNarrowFont(@cmdWindow.contents)
addSprite("cmdWindow",@cmdWindow)
end
self.z = z
end
def dispose
super
@buttonBitmap.dispose if @buttonBitmap
@typeBitmap.dispose if @typeBitmap
@megaEvoBitmap.dispose if @megaEvoBitmap
@shiftBitmap.dispose if @shiftBitmap
end
def z=(value)
super
@msgBox.z += 1 if @msgBox
@cmdWindow.z += 2 if @cmdWindow
@overlay.z += 5 if @overlay
@infoOverlay.z += 6 if @infoOverlay
@typeIcon.z += 1 if @typeIcon
end
def battler=(value)
@battler = value
refresh
refreshButtonNames
end
def shiftMode=(value)
oldValue = @shiftMode
@shiftMode = value
refreshShiftButton if @shiftMode!=oldValue
end
def refreshButtonNames
moves = (@battler) ? @battler.moves : []
if !USE_GRAPHICS
# Fill in command window
commands = []
for i in 0...[4, moves.length].max
commands.push((moves[i]) ? moves[i].name : "-")
end
@cmdWindow.commands = commands
return
end
# Draw move names onto overlay
@overlay.bitmap.clear
textPos = []
@buttons.each_with_index do |button,i|
next if !@visibility["button_#{i}"]
x = button.x-self.x+button.src_rect.width/2
y = button.y-self.y+2
moveNameBase = TEXT_BASE_COLOR
if moves[i].type
# NOTE: This takes a colour from a particular pixel in the button
# graphic and makes the move name's base colour that same colour.
# The pixel is at coordinates 10,34 in the button box. If you
# change the graphic, you may want to change/remove the below line
# of code to ensure the font is an appropriate colour.
moveNameBase = button.bitmap.get_pixel(10,button.src_rect.y+34)
end
textPos.push([moves[i].name,x,y,2,moveNameBase,TEXT_SHADOW_COLOR])
end
pbDrawTextPositions(@overlay.bitmap,textPos)
end
def refreshSelection
moves = (@battler) ? @battler.moves : []
if USE_GRAPHICS
# Choose appropriate button graphics and z positions
@buttons.each_with_index do |button,i|
if !moves[i]
@visibility["button_#{i}"] = false
next
end
@visibility["button_#{i}"] = true
button.src_rect.x = (i==@index) ? @buttonBitmap.width/2 : 0
button.src_rect.y = GameData::Type.get(moves[i].type).id_number * BUTTON_HEIGHT
button.z = self.z + ((i==@index) ? 4 : 3)
end
end
refreshMoveData(moves[@index])
end
def refreshMoveData(move)
# Write PP and type of the selected move
if !USE_GRAPHICS
moveType = GameData::Type.get(move.type).name
if move.total_pp<=0
@msgBox.text = _INTL("PP: ---<br>TYPE/{1}",moveType)
else
@msgBox.text = _ISPRINTF("PP: {1: 2d}/{2: 2d}<br>TYPE/{3:s}",
move.pp,move.total_pp,moveType)
end
return
end
@infoOverlay.bitmap.clear
if !move
@visibility["typeIcon"] = false
return
end
@visibility["typeIcon"] = true
# Type icon
type_number = GameData::Type.get(move.type).id_number
@typeIcon.src_rect.y = type_number * TYPE_ICON_HEIGHT
# PP text
if move.total_pp>0
ppFraction = [(4.0*move.pp/move.total_pp).ceil,3].min
textPos = []
textPos.push([_INTL("PP: {1}/{2}",move.pp,move.total_pp),
448,44,2,PP_COLORS[ppFraction*2],PP_COLORS[ppFraction*2+1]])
pbDrawTextPositions(@infoOverlay.bitmap,textPos)
end
end
def refreshMegaEvolutionButton
return if !USE_GRAPHICS
@megaButton.src_rect.y = (@mode - 1) * @megaEvoBitmap.height / 2
@megaButton.x = self.x + ((@shiftMode > 0) ? 204 : 120)
@megaButton.z = self.z - 1
@visibility["megaButton"] = (@mode > 0)
end
def refreshShiftButton
return if !USE_GRAPHICS
@shiftButton.src_rect.y = (@shiftMode - 1) * @shiftBitmap.height
@shiftButton.z = self.z - 1
@visibility["shiftButton"] = (@shiftMode > 0)
end
def refresh
return if !@battler
refreshSelection
refreshMegaEvolutionButton
refreshShiftButton
end
end
#===============================================================================
# Target menu (choose a move's target)
# NOTE: Unlike the command and fight menus, this one doesn't have a textbox-only
# version.
#===============================================================================
class TargetMenuDisplay < BattleMenuBase
attr_accessor :mode
# Lists of which button graphics to use in different situations/types of battle.
MODES = [
[0,2,1,3], # 0 = Regular battle
[0,2,1,9], # 1 = Regular battle with "Cancel" instead of "Run"
[0,2,1,4], # 2 = Regular battle with "Call" instead of "Run"
[5,7,6,3], # 3 = Safari Zone
[0,8,1,3] # 4 = Bug Catching Contest
]
CMD_BUTTON_WIDTH_SMALL = 170
TEXT_BASE_COLOR = Color.new(240,248,224)
TEXT_SHADOW_COLOR = Color.new(64,64,64)
def initialize(viewport,z,sideSizes)
super(viewport)
@sideSizes = sideSizes
maxIndex = (@sideSizes[0]>@sideSizes[1]) ? (@sideSizes[0]-1)*2 : @sideSizes[1]*2-1
@smallButtons = (@sideSizes.max>2)
self.x = 0
self.y = Graphics.height-96
@texts = []
# NOTE: @mode is for which buttons are shown as selected.
# 0=select 1 button (@index), 1=select all buttons with text
# Create bitmaps
@buttonBitmap = AnimatedBitmap.new(_INTL("Graphics/Pictures/Battle/cursor_target"))
# Create target buttons
@buttons = Array.new(maxIndex+1) do |i|
numButtons = @sideSizes[i%2]
next if numButtons<=i/2
# NOTE: Battler indexes go from left to right from the perspective of
# that side's trainer, so inc is different for each side for the
# same value of i/2.
inc = ((i%2)==0) ? i/2 : numButtons-1-i/2
button = SpriteWrapper.new(viewport)
button.bitmap = @buttonBitmap.bitmap
button.src_rect.width = (@smallButtons) ? CMD_BUTTON_WIDTH_SMALL : @buttonBitmap.width/2
button.src_rect.height = BUTTON_HEIGHT
if @smallButtons
button.x = self.x+170-[0,82,166][numButtons-1]
else
button.x = self.x+138-[0,116][numButtons-1]
end
button.x += (button.src_rect.width-4)*inc
button.y = self.y+6
button.y += (BUTTON_HEIGHT-4)*((i+1)%2)
addSprite("button_#{i}",button)
next button
end
# Create overlay (shows target names)
@overlay = BitmapSprite.new(Graphics.width,Graphics.height-self.y,viewport)
@overlay.x = self.x
@overlay.y = self.y
pbSetNarrowFont(@overlay.bitmap)
addSprite("overlay",@overlay)
self.z = z
refresh
end
def dispose
super
@buttonBitmap.dispose if @buttonBitmap
end
def z=(value)
super
@overlay.z += 5 if @overlay
end
def setDetails(texts,mode)
@texts = texts
@mode = mode
refresh
end
def refreshButtons
# Choose appropriate button graphics and z positions
@buttons.each_with_index do |button,i|
next if !button
sel = false
buttonType = 0
if @texts[i]
sel ||= (@mode==0 && i==@index)
sel ||= (@mode==1)
buttonType = ((i%2)==0) ? 1 : 2
end
buttonType = 2*buttonType + ((@smallButtons) ? 1 : 0)
button.src_rect.x = (sel) ? @buttonBitmap.width/2 : 0
button.src_rect.y = buttonType*BUTTON_HEIGHT
button.z = self.z + ((sel) ? 3 : 2)
end
# Draw target names onto overlay
@overlay.bitmap.clear
textpos = []
@buttons.each_with_index do |button,i|
next if !button || @texts[i].nil? || @texts[i]==""
x = button.x-self.x+button.src_rect.width/2
y = button.y-self.y+2
textpos.push([@texts[i],x,y,2,TEXT_BASE_COLOR,TEXT_SHADOW_COLOR])
end
pbDrawTextPositions(@overlay.bitmap,textpos)
end
def refresh
refreshButtons
end
end

View File

@@ -0,0 +1,349 @@
# Battle scene (the visuals of the battle)
class PokeBattle_Scene
attr_accessor :abortable # For non-interactive battles, can quit immediately
attr_reader :viewport
attr_reader :sprites
BLANK = 0
MESSAGE_BOX = 1
COMMAND_BOX = 2
FIGHT_BOX = 3
TARGET_BOX = 4
MESSAGE_PAUSE_TIME = (Graphics.frame_rate*1.0).floor # 1 second
#=============================================================================
# Updating and refreshing
#=============================================================================
def pbUpdate(cw=nil)
pbGraphicsUpdate
pbInputUpdate
pbFrameUpdate(cw)
end
def pbGraphicsUpdate
# Update lineup animations
if @animations.length>0
shouldCompact = false
@animations.each_with_index do |a,i|
a.update
if a.animDone?
a.dispose
@animations[i] = nil
shouldCompact = true
end
end
@animations.compact! if shouldCompact
end
# Update other graphics
@sprites["battle_bg"].update if @sprites["battle_bg"].respond_to?("update")
Graphics.update
@frameCounter += 1
@frameCounter = @frameCounter%(Graphics.frame_rate*12/20)
end
def pbInputUpdate
Input.update
if Input.trigger?(Input::BACK) && @abortable && !@aborted
@aborted = true
@battle.pbAbort
end
end
def pbFrameUpdate(cw=nil)
cw.update if cw
@battle.battlers.each_with_index do |b,i|
next if !b
@sprites["dataBox_#{i}"].update(@frameCounter) if @sprites["dataBox_#{i}"]
@sprites["pokemon_#{i}"].update(@frameCounter) if @sprites["pokemon_#{i}"]
@sprites["shadow_#{i}"].update(@frameCounter) if @sprites["shadow_#{i}"]
end
end
def pbRefresh
@battle.battlers.each_with_index do |b,i|
next if !b
@sprites["dataBox_#{i}"].refresh if @sprites["dataBox_#{i}"]
end
end
def pbRefreshOne(idxBattler)
@sprites["dataBox_#{idxBattler}"].refresh if @sprites["dataBox_#{idxBattler}"]
end
#=============================================================================
# Party lineup
#=============================================================================
# Returns whether the party line-ups are currently coming on-screen
def inPartyAnimation?
return @animations.length>0
end
#=============================================================================
# Window displays
#=============================================================================
def pbShowWindow(windowType)
# NOTE: If you are not using fancy graphics for the command/fight menus, you
# will need to make "messageBox" also visible if the windowtype if
# COMMAND_BOX/FIGHT_BOX respectively.
@sprites["messageBox"].visible = (windowType==MESSAGE_BOX)
@sprites["messageWindow"].visible = (windowType==MESSAGE_BOX)
@sprites["commandWindow"].visible = (windowType==COMMAND_BOX)
@sprites["fightWindow"].visible = (windowType==FIGHT_BOX)
@sprites["targetWindow"].visible = (windowType==TARGET_BOX)
end
# This is for the end of brief messages, which have been lingering on-screen
# while other things happened. This is only called when another message wants
# to be shown, and makes the brief message linger for one more second first.
# Some animations skip this extra second by setting @briefMessage to false
# despite not having any other messages to show.
def pbWaitMessage
return if !@briefMessage
pbShowWindow(MESSAGE_BOX)
cw = @sprites["messageWindow"]
MESSAGE_PAUSE_TIME.times do
pbUpdate(cw)
end
cw.text = ""
cw.visible = false
@briefMessage = false
end
# NOTE: A regular message is displayed for 1 second after it fully appears (or
# less if Back/Use is pressed). Disappears automatically after that time.
def pbDisplayMessage(msg,brief=false)
pbWaitMessage
pbShowWindow(MESSAGE_BOX)
cw = @sprites["messageWindow"]
cw.setText(msg)
PBDebug.log(msg)
yielded = false
i = 0
loop do
pbUpdate(cw)
if !cw.busy?
if !yielded
yield if block_given? # For playing SE as soon as the message is all shown
yielded = true
end
if brief
# NOTE: A brief message lingers on-screen while other things happen. A
# regular message has to end before the game can continue.
@briefMessage = true
break
end
if i>=MESSAGE_PAUSE_TIME # Autoclose after 1 second
cw.text = ""
cw.visible = false
break
end
i += 1
end
if Input.trigger?(Input::BACK) || Input.trigger?(Input::USE) || @abortable
if cw.busy?
pbPlayDecisionSE if cw.pausing? && !@abortable
cw.skipAhead
elsif !@abortable
cw.text = ""
cw.visible = false
break
end
end
end
end
alias pbDisplay pbDisplayMessage
# NOTE: A paused message has the arrow in the bottom corner indicating there
# is another message immediately afterward. It is displayed for 3
# seconds after it fully appears (or less if B/C is pressed) and
# disappears automatically after that time, except at the end of battle.
def pbDisplayPausedMessage(msg)
pbWaitMessage
pbShowWindow(MESSAGE_BOX)
cw = @sprites["messageWindow"]
cw.text = _INTL("{1}\1",msg)
PBDebug.log(msg)
yielded = false
i = 0
loop do
pbUpdate(cw)
if !cw.busy?
if !yielded
yield if block_given? # For playing SE as soon as the message is all shown
yielded = true
end
if !@battleEnd
if i>=MESSAGE_PAUSE_TIME*3 # Autoclose after 3 seconds
cw.text = ""
cw.visible = false
break
end
i += 1
end
end
if Input.trigger?(Input::BACK) || Input.trigger?(Input::USE) || @abortable
if cw.busy?
pbPlayDecisionSE if cw.pausing? && !@abortable
cw.skipAhead
elsif !@abortable
cw.text = ""
pbPlayDecisionSE
break
end
end
end
end
def pbDisplayConfirmMessage(msg)
return pbShowCommands(msg,[_INTL("Yes"),_INTL("No")],1)==0
end
def pbShowCommands(msg,commands,defaultValue)
pbWaitMessage
pbShowWindow(MESSAGE_BOX)
dw = @sprites["messageWindow"]
dw.text = msg
cw = Window_CommandPokemon.new(commands)
cw.x = Graphics.width-cw.width
cw.y = Graphics.height-cw.height-dw.height
cw.z = dw.z+1
cw.index = 0
cw.viewport = @viewport
PBDebug.log(msg)
loop do
cw.visible = (!dw.busy?)
pbUpdate(cw)
dw.update
if Input.trigger?(Input::BACK) && defaultValue>=0
if dw.busy?
pbPlayDecisionSE if dw.pausing?
dw.resume
else
cw.dispose
dw.text = ""
return defaultValue
end
elsif Input.trigger?(Input::USE)
if dw.busy?
pbPlayDecisionSE if dw.pausing?
dw.resume
else
cw.dispose
dw.text = ""
return cw.index
end
end
end
end
#=============================================================================
# Sprites
#=============================================================================
def pbAddSprite(id,x,y,filename,viewport)
sprite = IconSprite.new(x,y,viewport)
if filename
sprite.setBitmap(filename) rescue nil
end
@sprites[id] = sprite
return sprite
end
def pbAddPlane(id,filename,viewport)
sprite = AnimatedPlane.new(viewport)
if filename
sprite.setBitmap(filename)
end
@sprites[id] = sprite
return sprite
end
def pbDisposeSprites
pbDisposeSpriteHash(@sprites)
end
# Used by Ally Switch.
def pbSwapBattlerSprites(idxA,idxB)
@sprites["pokemon_#{idxA}"], @sprites["pokemon_#{idxB}"] = @sprites["pokemon_#{idxB}"], @sprites["pokemon_#{idxA}"]
@sprites["shadow_#{idxA}"], @sprites["shadow_#{idxB}"] = @sprites["shadow_#{idxB}"], @sprites["shadow_#{idxA}"]
@lastCmd[idxA], @lastCmd[idxB] = @lastCmd[idxB], @lastCmd[idxA]
@lastMove[idxA], @lastMove[idxB] = @lastMove[idxB], @lastMove[idxA]
[idxA,idxB].each do |i|
@sprites["pokemon_#{i}"].index = i
@sprites["pokemon_#{i}"].pbSetPosition
@sprites["shadow_#{i}"].index = i
@sprites["shadow_#{i}"].pbSetPosition
@sprites["dataBox_#{i}"].battler = @battle.battlers[i]
end
pbRefresh
end
#=============================================================================
# Phases
#=============================================================================
def pbBeginCommandPhase
@sprites["messageWindow"].text = ""
end
def pbBeginAttackPhase
pbSelectBattler(-1)
pbShowWindow(MESSAGE_BOX)
end
def pbBeginEndOfRoundPhase
end
def pbEndBattle(_result)
@abortable = false
pbShowWindow(BLANK)
# Fade out all sprites
pbBGMFade(1.0)
pbFadeOutAndHide(@sprites)
pbDisposeSprites
end
#=============================================================================
#
#=============================================================================
def pbSelectBattler(idxBattler,selectMode=1)
numWindows = @battle.sideSizes.max*2
for i in 0...numWindows
sel = (idxBattler.is_a?(Array)) ? !idxBattler[i].nil? : i==idxBattler
selVal = (sel) ? selectMode : 0
@sprites["dataBox_#{i}"].selected = selVal if @sprites["dataBox_#{i}"]
@sprites["pokemon_#{i}"].selected = selVal if @sprites["pokemon_#{i}"]
end
end
def pbChangePokemon(idxBattler,pkmn)
idxBattler = idxBattler.index if idxBattler.respond_to?("index")
pkmnSprite = @sprites["pokemon_#{idxBattler}"]
shadowSprite = @sprites["shadow_#{idxBattler}"]
back = !@battle.opposes?(idxBattler)
pkmnSprite.setPokemonBitmap(pkmn,back)
shadowSprite.setPokemonBitmap(pkmn)
# Set visibility of battler's shadow
shadowSprite.visible = pkmn.species_data.shows_shadow? if shadowSprite && !back
end
def pbResetMoveIndex(idxBattler)
@lastMove[idxBattler] = 0
end
#=============================================================================
#
#=============================================================================
# This method is called when the player wins a wild Pokémon battle.
# This method can change the battle's music for example.
def pbWildBattleSuccess
@battleEnd = true
pbBGMPlay(pbGetWildVictoryME)
end
# This method is called when the player wins a trainer battle.
# This method can change the battle's music for example.
def pbTrainerBattleSuccess
@battleEnd = true
pbBGMPlay(pbGetTrainerVictoryME(@battle.opponent))
end
end

View File

@@ -0,0 +1,191 @@
class PokeBattle_Scene
#=============================================================================
# Create the battle scene and its elements
#=============================================================================
def initialize
@battle = nil
@abortable = false
@aborted = false
@battleEnd = false
@animations = []
@frameCounter = 0
end
# Called whenever the battle begins.
def pbStartBattle(battle)
@battle = battle
@viewport = Viewport.new(0,0,Graphics.width,Graphics.height)
@viewport.z = 99999
@lastCmd = Array.new(@battle.battlers.length,0)
@lastMove = Array.new(@battle.battlers.length,0)
pbInitSprites
pbBattleIntroAnimation
end
def pbInitSprites
@sprites = {}
# The background image and each side's base graphic
pbCreateBackdropSprites
# Create message box graphic
messageBox = pbAddSprite("messageBox",0,Graphics.height-96,
"Graphics/Pictures/Battle/overlay_message",@viewport)
messageBox.z = 195
# Create message window (displays the message)
msgWindow = Window_AdvancedTextPokemon.newWithSize("",
16,Graphics.height-96+2,Graphics.width-32,96,@viewport)
msgWindow.z = 200
msgWindow.opacity = 0
msgWindow.baseColor = PokeBattle_SceneConstants::MESSAGE_BASE_COLOR
msgWindow.shadowColor = PokeBattle_SceneConstants::MESSAGE_SHADOW_COLOR
msgWindow.letterbyletter = true
@sprites["messageWindow"] = msgWindow
# Create command window
@sprites["commandWindow"] = CommandMenuDisplay.new(@viewport,200)
# Create fight window
@sprites["fightWindow"] = FightMenuDisplay.new(@viewport,200)
# Create targeting window
@sprites["targetWindow"] = TargetMenuDisplay.new(@viewport,200,@battle.sideSizes)
pbShowWindow(MESSAGE_BOX)
# The party lineup graphics (bar and balls) for both sides
for side in 0...2
partyBar = pbAddSprite("partyBar_#{side}",0,0,
"Graphics/Pictures/Battle/overlay_lineup",@viewport)
partyBar.z = 120
partyBar.mirror = true if side==0 # Player's lineup bar only
partyBar.visible = false
for i in 0...PokeBattle_SceneConstants::NUM_BALLS
ball = pbAddSprite("partyBall_#{side}_#{i}",0,0,nil,@viewport)
ball.z = 121
ball.visible = false
end
# Ability splash bars
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@sprites["abilityBar_#{side}"] = AbilitySplashBar.new(side,@viewport)
end
end
# Player's and partner trainer's back sprite
@battle.player.each_with_index do |p,i|
pbCreateTrainerBackSprite(i,p.trainer_type,@battle.player.length)
end
# Opposing trainer(s) sprites
if @battle.trainerBattle?
@battle.opponent.each_with_index do |p,i|
pbCreateTrainerFrontSprite(i,p.trainer_type,@battle.opponent.length)
end
end
# Data boxes and Pokémon sprites
@battle.battlers.each_with_index do |b,i|
next if !b
@sprites["dataBox_#{i}"] = PokemonDataBox.new(b,@battle.pbSideSize(i),@viewport)
pbCreatePokemonSprite(i)
end
# Wild battle, so set up the Pokémon sprite(s) accordingly
if @battle.wildBattle?
@battle.pbParty(1).each_with_index do |pkmn,i|
index = i*2+1
pbChangePokemon(index,pkmn)
pkmnSprite = @sprites["pokemon_#{index}"]
pkmnSprite.tone = Tone.new(-80,-80,-80)
pkmnSprite.visible = true
end
end
end
def pbCreateBackdropSprites
case @battle.time
when 1 then time = "eve"
when 2 then time = "night"
end
# Put everything together into backdrop, bases and message bar filenames
backdropFilename = @battle.backdrop
baseFilename = @battle.backdrop
baseFilename = sprintf("%s_%s",baseFilename,@battle.backdropBase) if @battle.backdropBase
messageFilename = @battle.backdrop
if time
trialName = sprintf("%s_%s",backdropFilename,time)
if pbResolveBitmap(sprintf("Graphics/Battlebacks/"+trialName+"_bg"))
backdropFilename = trialName
end
trialName = sprintf("%s_%s",baseFilename,time)
if pbResolveBitmap(sprintf("Graphics/Battlebacks/"+trialName+"_base0"))
baseFilename = trialName
end
trialName = sprintf("%s_%s",messageFilename,time)
if pbResolveBitmap(sprintf("Graphics/Battlebacks/"+trialName+"_message"))
messageFilename = trialName
end
end
if !pbResolveBitmap(sprintf("Graphics/Battlebacks/"+baseFilename+"_base0")) &&
@battle.backdropBase
baseFilename = @battle.backdropBase
if time
trialName = sprintf("%s_%s",baseFilename,time)
if pbResolveBitmap(sprintf("Graphics/Battlebacks/"+trialName+"_base0"))
baseFilename = trialName
end
end
end
# Finalise filenames
battleBG = "Graphics/Battlebacks/"+backdropFilename+"_bg"
playerBase = "Graphics/Battlebacks/"+baseFilename+"_base0"
enemyBase = "Graphics/Battlebacks/"+baseFilename+"_base1"
messageBG = "Graphics/Battlebacks/"+messageFilename+"_message"
# Apply graphics
bg = pbAddSprite("battle_bg",0,0,battleBG,@viewport)
bg.z = 0
bg = pbAddSprite("battle_bg2",-Graphics.width,0,battleBG,@viewport)
bg.z = 0
bg.mirror = true
for side in 0...2
baseX, baseY = PokeBattle_SceneConstants.pbBattlerPosition(side)
base = pbAddSprite("base_#{side}",baseX,baseY,
(side==0) ? playerBase : enemyBase,@viewport)
base.z = 1
if base.bitmap
base.ox = base.bitmap.width/2
base.oy = (side==0) ? base.bitmap.height : base.bitmap.height/2
end
end
cmdBarBG = pbAddSprite("cmdBar_bg",0,Graphics.height-96,messageBG,@viewport)
cmdBarBG.z = 180
end
def pbCreateTrainerBackSprite(idxTrainer,trainerType,numTrainers=1)
if idxTrainer==0 # Player's sprite
trainerFile = GameData::TrainerType.player_back_sprite_filename(trainerType)
else # Partner trainer's sprite
trainerFile = GameData::TrainerType.back_sprite_filename(trainerType)
end
spriteX, spriteY = PokeBattle_SceneConstants.pbTrainerPosition(0,idxTrainer,numTrainers)
trainer = pbAddSprite("player_#{idxTrainer+1}",spriteX,spriteY,trainerFile,@viewport)
return if !trainer.bitmap
# Alter position of sprite
trainer.z = 30+idxTrainer
if trainer.bitmap.width>trainer.bitmap.height*2
trainer.src_rect.x = 0
trainer.src_rect.width = trainer.bitmap.width/5
end
trainer.ox = trainer.src_rect.width/2
trainer.oy = trainer.bitmap.height
end
def pbCreateTrainerFrontSprite(idxTrainer,trainerType,numTrainers=1)
trainerFile = GameData::TrainerType.front_sprite_filename(trainerType)
spriteX, spriteY = PokeBattle_SceneConstants.pbTrainerPosition(1,idxTrainer,numTrainers)
trainer = pbAddSprite("trainer_#{idxTrainer+1}",spriteX,spriteY,trainerFile,@viewport)
return if !trainer.bitmap
# Alter position of sprite
trainer.z = 7+idxTrainer
trainer.ox = trainer.src_rect.width/2
trainer.oy = trainer.bitmap.height
end
def pbCreatePokemonSprite(idxBattler)
sideSize = @battle.pbSideSize(idxBattler)
batSprite = PokemonBattlerSprite.new(@viewport,sideSize,idxBattler,@animations)
@sprites["pokemon_#{idxBattler}"] = batSprite
shaSprite = PokemonBattlerShadowSprite.new(@viewport,sideSize,idxBattler)
shaSprite.visible = false
@sprites["shadow_#{idxBattler}"] = shaSprite
end
end

View File

@@ -0,0 +1,470 @@
class PokeBattle_Scene
#=============================================================================
# The player chooses a main command for a Pokémon
# Return values: -1=Cancel, 0=Fight, 1=Bag, 2=Pokémon, 3=Run, 4=Call
#=============================================================================
def pbCommandMenu(idxBattler,firstAction)
shadowTrainer = (GameData::Type.exists?(:SHADOW) && @battle.trainerBattle?)
cmds = [
_INTL("What will\n{1} do?",@battle.battlers[idxBattler].name),
_INTL("Fight"),
_INTL("Bag"),
_INTL("Pokémon"),
(shadowTrainer) ? _INTL("Call") : (firstAction) ? _INTL("Run") : _INTL("Cancel")
]
ret = pbCommandMenuEx(idxBattler,cmds,(shadowTrainer) ? 2 : (firstAction) ? 0 : 1)
ret = 4 if ret==3 && shadowTrainer # Convert "Run" to "Call"
ret = -1 if ret==3 && !firstAction # Convert "Run" to "Cancel"
return ret
end
# Mode: 0 = regular battle with "Run" (first choosable action in the round only)
# 1 = regular battle with "Cancel"
# 2 = regular battle with "Call" (for Shadow Pokémon battles)
# 3 = Safari Zone
# 4 = Bug Catching Contest
def pbCommandMenuEx(idxBattler,texts,mode=0)
pbShowWindow(COMMAND_BOX)
cw = @sprites["commandWindow"]
cw.setTexts(texts)
cw.setIndexAndMode(@lastCmd[idxBattler],mode)
pbSelectBattler(idxBattler)
ret = -1
loop do
oldIndex = cw.index
pbUpdate(cw)
# Update selected command
if Input.trigger?(Input::LEFT)
cw.index -= 1 if (cw.index&1)==1
elsif Input.trigger?(Input::RIGHT)
cw.index += 1 if (cw.index&1)==0
elsif Input.trigger?(Input::UP)
cw.index -= 2 if (cw.index&2)==2
elsif Input.trigger?(Input::DOWN)
cw.index += 2 if (cw.index&2)==0
end
pbPlayCursorSE if cw.index!=oldIndex
# Actions
if Input.trigger?(Input::USE) # Confirm choice
pbPlayDecisionSE
ret = cw.index
@lastCmd[idxBattler] = ret
break
elsif Input.trigger?(Input::BACK) && mode==1 # Cancel
pbPlayCancelSE
break
elsif Input.trigger?(Input::F9) && $DEBUG # Debug menu
pbPlayDecisionSE
ret = -2
break
end
end
return ret
end
#=============================================================================
# The player chooses a move for a Pokémon to use
#=============================================================================
def pbFightMenu(idxBattler,megaEvoPossible=false)
battler = @battle.battlers[idxBattler]
cw = @sprites["fightWindow"]
cw.battler = battler
moveIndex = 0
if battler.moves[@lastMove[idxBattler]] && battler.moves[@lastMove[idxBattler]].id
moveIndex = @lastMove[idxBattler]
end
cw.shiftMode = (@battle.pbCanShift?(idxBattler)) ? 1 : 0
cw.setIndexAndMode(moveIndex,(megaEvoPossible) ? 1 : 0)
needFullRefresh = true
needRefresh = false
loop do
# Refresh view if necessary
if needFullRefresh
pbShowWindow(FIGHT_BOX)
pbSelectBattler(idxBattler)
needFullRefresh = false
end
if needRefresh
if megaEvoPossible
newMode = (@battle.pbRegisteredMegaEvolution?(idxBattler)) ? 2 : 1
cw.mode = newMode if newMode!=cw.mode
end
needRefresh = false
end
oldIndex = cw.index
# General update
pbUpdate(cw)
# Update selected command
if Input.trigger?(Input::LEFT)
cw.index -= 1 if (cw.index&1)==1
elsif Input.trigger?(Input::RIGHT)
if battler.moves[cw.index+1] && battler.moves[cw.index+1].id
cw.index += 1 if (cw.index&1)==0
end
elsif Input.trigger?(Input::UP)
cw.index -= 2 if (cw.index&2)==2
elsif Input.trigger?(Input::DOWN)
if battler.moves[cw.index+2] && battler.moves[cw.index+2].id
cw.index += 2 if (cw.index&2)==0
end
end
pbPlayCursorSE if cw.index!=oldIndex
# Actions
if Input.trigger?(Input::USE) # Confirm choice
pbPlayDecisionSE
break if yield cw.index
needFullRefresh = true
needRefresh = true
elsif Input.trigger?(Input::BACK) # Cancel fight menu
pbPlayCancelSE
break if yield -1
needRefresh = true
elsif Input.trigger?(Input::ACTION) # Toggle Mega Evolution
if megaEvoPossible
pbPlayDecisionSE
break if yield -2
needRefresh = true
end
elsif Input.trigger?(Input::SPECIAL) # Shift
if cw.shiftMode>0
pbPlayDecisionSE
break if yield -3
needRefresh = true
end
end
end
@lastMove[idxBattler] = cw.index
end
#=============================================================================
# Opens the party screen to choose a Pokémon to switch in (or just view its
# summary screens)
#=============================================================================
def pbPartyScreen(idxBattler,canCancel=false)
# Fade out and hide all sprites
visibleSprites = pbFadeOutAndHide(@sprites)
# Get player's party
partyPos = @battle.pbPartyOrder(idxBattler)
partyStart, _partyEnd = @battle.pbTeamIndexRangeFromBattlerIndex(idxBattler)
modParty = @battle.pbPlayerDisplayParty(idxBattler)
# Start party screen
scene = PokemonParty_Scene.new
switchScreen = PokemonPartyScreen.new(scene,modParty)
switchScreen.pbStartScene(_INTL("Choose a Pokémon."),@battle.pbNumPositions(0,0))
# Loop while in party screen
loop do
# Select a Pokémon
scene.pbSetHelpText(_INTL("Choose a Pokémon."))
idxParty = switchScreen.pbChoosePokemon
if idxParty<0
next if !canCancel
break
end
# Choose a command for the selected Pokémon
cmdSwitch = -1
cmdSummary = -1
commands = []
commands[cmdSwitch = commands.length] = _INTL("Switch In") if modParty[idxParty].able?
commands[cmdSummary = commands.length] = _INTL("Summary")
commands[commands.length] = _INTL("Cancel")
command = scene.pbShowCommands(_INTL("Do what with {1}?",modParty[idxParty].name),commands)
if cmdSwitch>=0 && command==cmdSwitch # Switch In
idxPartyRet = -1
partyPos.each_with_index do |pos,i|
next if pos!=idxParty+partyStart
idxPartyRet = i
break
end
break if yield idxPartyRet, switchScreen
elsif cmdSummary>=0 && command==cmdSummary # Summary
scene.pbSummary(idxParty,true)
end
end
# Close party screen
switchScreen.pbEndScene
# Fade back into battle screen
pbFadeInAndShow(@sprites,visibleSprites)
end
#=============================================================================
# Opens the Bag screen and chooses an item to use
#=============================================================================
def pbItemMenu(idxBattler,_firstAction)
# Fade out and hide all sprites
visibleSprites = pbFadeOutAndHide(@sprites)
# Set Bag starting positions
oldLastPocket = $PokemonBag.lastpocket
oldChoices = $PokemonBag.getAllChoices
$PokemonBag.lastpocket = @bagLastPocket if @bagLastPocket!=nil
$PokemonBag.setAllChoices(@bagChoices) if @bagChoices!=nil
# Start Bag screen
itemScene = PokemonBag_Scene.new
itemScene.pbStartScene($PokemonBag,true,Proc.new { |item|
useType = GameData::Item.get(item).battle_use
next useType && useType>0
},false)
# Loop while in Bag screen
wasTargeting = false
loop do
# Select an item
item = itemScene.pbChooseItem
break if !item
# Choose a command for the selected item
item = GameData::Item.get(item)
itemName = item.name
useType = item.battle_use
cmdUse = -1
commands = []
commands[cmdUse = commands.length] = _INTL("Use") if useType && useType!=0
commands[commands.length] = _INTL("Cancel")
command = itemScene.pbShowCommands(_INTL("{1} is selected.",itemName),commands)
next unless cmdUse>=0 && command==cmdUse # Use
# Use types:
# 0 = not usable in battle
# 1 = use on Pokémon (lots of items), consumed
# 2 = use on Pokémon's move (Ethers), consumed
# 3 = use on battler (X items, Persim Berry), consumed
# 4 = use on opposing battler (Poké Balls), consumed
# 5 = use no target (Poké Doll, Guard Spec., Launcher items), consumed
# 6 = use on Pokémon (Blue Flute), not consumed
# 7 = use on Pokémon's move, not consumed
# 8 = use on battler (Red/Yellow Flutes), not consumed
# 9 = use on opposing battler, not consumed
# 10 = use no target (Poké Flute), not consumed
case useType
when 1, 2, 3, 6, 7, 8 # Use on Pokémon/Pokémon's move/battler
# Auto-choose the Pokémon/battler whose action is being decided if they
# are the only available Pokémon/battler to use the item on
case useType
when 1, 6 # Use on Pokémon
if @battle.pbTeamLengthFromBattlerIndex(idxBattler)==1
break if yield item.id, useType, @battle.battlers[idxBattler].pokemonIndex, -1, itemScene
end
when 3, 8 # Use on battler
if @battle.pbPlayerBattlerCount==1
break if yield item.id, useType, @battle.battlers[idxBattler].pokemonIndex, -1, itemScene
end
end
# Fade out and hide Bag screen
itemScene.pbFadeOutScene
# Get player's party
party = @battle.pbParty(idxBattler)
partyPos = @battle.pbPartyOrder(idxBattler)
partyStart, _partyEnd = @battle.pbTeamIndexRangeFromBattlerIndex(idxBattler)
modParty = @battle.pbPlayerDisplayParty(idxBattler)
# Start party screen
pkmnScene = PokemonParty_Scene.new
pkmnScreen = PokemonPartyScreen.new(pkmnScene,modParty)
pkmnScreen.pbStartScene(_INTL("Use on which Pokémon?"),@battle.pbNumPositions(0,0))
idxParty = -1
# Loop while in party screen
loop do
# Select a Pokémon
pkmnScene.pbSetHelpText(_INTL("Use on which Pokémon?"))
idxParty = pkmnScreen.pbChoosePokemon
break if idxParty<0
idxPartyRet = -1
partyPos.each_with_index do |pos,i|
next if pos!=idxParty+partyStart
idxPartyRet = i
break
end
next if idxPartyRet<0
pkmn = party[idxPartyRet]
next if !pkmn || pkmn.egg?
idxMove = -1
if useType==2 || useType==7 # Use on Pokémon's move
idxMove = pkmnScreen.pbChooseMove(pkmn,_INTL("Restore which move?"))
next if idxMove<0
end
break if yield item.id, useType, idxPartyRet, idxMove, pkmnScene
end
pkmnScene.pbEndScene
break if idxParty>=0
# Cancelled choosing a Pokémon; show the Bag screen again
itemScene.pbFadeInScene
when 4, 9 # Use on opposing battler (Poké Balls)
idxTarget = -1
if @battle.pbOpposingBattlerCount(idxBattler)==1
@battle.eachOtherSideBattler(idxBattler) { |b| idxTarget = b.index }
break if yield item.id, useType, idxTarget, -1, itemScene
else
wasTargeting = true
# Fade out and hide Bag screen
itemScene.pbFadeOutScene
# Fade in and show the battle screen, choosing a target
tempVisibleSprites = visibleSprites.clone
tempVisibleSprites["commandWindow"] = false
tempVisibleSprites["targetWindow"] = true
idxTarget = pbChooseTarget(idxBattler,GameData::Target.get(:Foe),tempVisibleSprites)
if idxTarget>=0
break if yield item.id, useType, idxTarget, -1, self
end
# Target invalid/cancelled choosing a target; show the Bag screen again
wasTargeting = false
pbFadeOutAndHide(@sprites)
itemScene.pbFadeInScene
end
when 5, 10 # Use with no target
break if yield item.id, useType, idxBattler, -1, itemScene
end
end
@bagLastPocket = $PokemonBag.lastpocket
@bagChoices = $PokemonBag.getAllChoices
$PokemonBag.lastpocket = oldLastPocket
$PokemonBag.setAllChoices(oldChoices)
# Close Bag screen
itemScene.pbEndScene
# Fade back into battle screen (if not already showing it)
pbFadeInAndShow(@sprites,visibleSprites) if !wasTargeting
end
#=============================================================================
# The player chooses a target battler for a move/item (non-single battles only)
#=============================================================================
# Returns an array containing battler names to display when choosing a move's
# target.
# nil means can't select that position, "" means can select that position but
# there is no battler there, otherwise is a battler's name.
def pbCreateTargetTexts(idxBattler,target_data)
texts = Array.new(@battle.battlers.length) do |i|
next nil if !@battle.battlers[i]
showName = false
# NOTE: Targets listed here are ones with num_targets of 0, plus
# RandomNearFoe which should look like it targets the user. All
# other targets are handled by the "else" part.
case target_data.id
when :None, :User, :RandomNearFoe
showName = (i==idxBattler)
when :UserSide
showName = !@battle.opposes?(i,idxBattler)
when :FoeSide
showName = @battle.opposes?(i,idxBattler)
when :BothSides
showName = true
else
showName = @battle.pbMoveCanTarget?(i,idxBattler,target_data)
end
next nil if !showName
next (@battle.battlers[i].fainted?) ? "" : @battle.battlers[i].name
end
return texts
end
# Returns the initial position of the cursor when choosing a target for a move
# in a non-single battle.
def pbFirstTarget(idxBattler,target_data)
case target_data.id
when :NearAlly
@battle.eachSameSideBattler(idxBattler) do |b|
next if b.index==idxBattler || !@battle.nearBattlers?(b,idxBattler)
next if b.fainted?
return b.index
end
@battle.eachSameSideBattler(idxBattler) do |b|
next if b.index==idxBattler || !@battle.nearBattlers?(b,idxBattler)
return b.index
end
when :NearFoe, :NearOther
indices = @battle.pbGetOpposingIndicesInOrder(idxBattler)
indices.each { |i| return i if @battle.nearBattlers?(i,idxBattler) && !@battle.battlers[i].fainted? }
indices.each { |i| return i if @battle.nearBattlers?(i,idxBattler) }
when :Foe, :Other
indices = @battle.pbGetOpposingIndicesInOrder(idxBattler)
indices.each { |i| return i if !@battle.battlers[i].fainted? }
indices.each { |i| return i }
end
return idxBattler # Target the user initially
end
def pbChooseTarget(idxBattler,target_data,visibleSprites=nil)
pbShowWindow(TARGET_BOX)
cw = @sprites["targetWindow"]
# Create an array of battler names (only valid targets are named)
texts = pbCreateTargetTexts(idxBattler,target_data)
# Determine mode based on target_data
mode = (target_data.num_targets == 1) ? 0 : 1
cw.setDetails(texts,mode)
cw.index = pbFirstTarget(idxBattler,target_data)
pbSelectBattler((mode==0) ? cw.index : texts,2) # Select initial battler/data box
pbFadeInAndShow(@sprites,visibleSprites) if visibleSprites
ret = -1
loop do
oldIndex = cw.index
pbUpdate(cw)
# Update selected command
if mode==0 # Choosing just one target, can change index
if Input.trigger?(Input::LEFT) || Input.trigger?(Input::RIGHT)
inc = ((cw.index%2)==0) ? -2 : 2
inc *= -1 if Input.trigger?(Input::RIGHT)
indexLength = @battle.sideSizes[cw.index%2]*2
newIndex = cw.index
loop do
newIndex += inc
break if newIndex<0 || newIndex>=indexLength
next if texts[newIndex].nil?
cw.index = newIndex
break
end
elsif (Input.trigger?(Input::UP) && (cw.index%2)==0) ||
(Input.trigger?(Input::DOWN) && (cw.index%2)==1)
tryIndex = @battle.pbGetOpposingIndicesInOrder(cw.index)
tryIndex.each do |idxBattlerTry|
next if texts[idxBattlerTry].nil?
cw.index = idxBattlerTry
break
end
end
if cw.index!=oldIndex
pbPlayCursorSE
pbSelectBattler(cw.index,2) # Select the new battler/data box
end
end
if Input.trigger?(Input::USE) # Confirm
ret = cw.index
pbPlayDecisionSE
break
elsif Input.trigger?(Input::BACK) # Cancel
ret = -1
pbPlayCancelSE
break
end
end
pbSelectBattler(-1) # Deselect all battlers/data boxes
return ret
end
#=============================================================================
# Opens a Pokémon's summary screen to try to learn a new move
#=============================================================================
# Called whenever a Pokémon should forget a move. It should return -1 if the
# selection is canceled, or 0 to 3 to indicate the move to forget. It should
# not allow HM moves to be forgotten.
def pbForgetMove(pkmn,moveToLearn)
ret = -1
pbFadeOutIn {
scene = PokemonSummary_Scene.new
screen = PokemonSummaryScreen.new(scene)
ret = screen.pbStartForgetScreen([pkmn],0,moveToLearn)
}
return ret
end
#=============================================================================
# Opens the nicknaming screen for a newly caught Pokémon
#=============================================================================
def pbNameEntry(helpText,pkmn)
return pbEnterPokemonName(helpText, 0, Pokemon::MAX_NAME_SIZE, "", pkmn)
end
#=============================================================================
# Shows the Pokédex entry screen for a newly caught Pokémon
#=============================================================================
def pbShowPokedex(species)
pbFadeOutIn {
scene = PokemonPokedexInfo_Scene.new
screen = PokemonPokedexInfoScreen.new(scene)
screen.pbDexEntry(species)
}
end
end

View File

@@ -0,0 +1,539 @@
class PokeBattle_Scene
#=============================================================================
# Animates the battle intro
#=============================================================================
def pbBattleIntroAnimation
# Make everything appear
introAnim = BattleIntroAnimation.new(@sprites,@viewport,@battle)
loop do
introAnim.update
pbUpdate
break if introAnim.animDone?
end
introAnim.dispose
# Post-appearance activities
# Trainer battle: get ready to show the party lineups (they are brought
# on-screen by a separate animation)
if @battle.trainerBattle?
# NOTE: Here is where you'd make trainer sprites animate if they had an
# entrance animation. Be sure to set it up like a Pokémon entrance
# animation, i.e. add them to @animations so that they can play out
# while party lineups appear and messages show.
pbShowPartyLineup(0,true)
pbShowPartyLineup(1,true)
return
end
# Wild battle: play wild Pokémon's intro animations (including cry), show
# data box(es), return the wild Pokémon's sprite(s) to normal colour, show
# shiny animation(s)
# Set up data box animation
for i in 0...@battle.sideSizes[1]
idxBattler = 2*i+1
next if !@battle.battlers[idxBattler]
dataBoxAnim = DataBoxAppearAnimation.new(@sprites,@viewport,idxBattler)
@animations.push(dataBoxAnim)
end
# Set up wild Pokémon returning to normal colour and playing intro
# animations (including cry)
@animations.push(BattleIntroAnimation2.new(@sprites,@viewport,@battle.sideSizes[1]))
# Play all the animations
while inPartyAnimation?; pbUpdate; end
# Show shiny animation for wild Pokémon
if @battle.showAnims
for i in 0...@battle.sideSizes[1]
idxBattler = 2*i+1
next if !@battle.battlers[idxBattler] || !@battle.battlers[idxBattler].shiny?
pbCommonAnimation("Shiny",@battle.battlers[idxBattler])
end
end
end
#=============================================================================
# Animates a party lineup appearing for the given side
#=============================================================================
def pbShowPartyLineup(side,fullAnim=false)
@animations.push(LineupAppearAnimation.new(@sprites,@viewport,
side,@battle.pbParty(side),@battle.pbPartyStarts(side),fullAnim))
if !fullAnim
while inPartyAnimation?; pbUpdate; end
end
end
#=============================================================================
# Animates an opposing trainer sliding in from off-screen. Will animate a
# previous trainer that is already on-screen slide off first. Used at the end
# of battle.
#=============================================================================
def pbShowOpponent(idxTrainer)
# Set up trainer appearing animation
appearAnim = TrainerAppearAnimation.new(@sprites,@viewport,idxTrainer)
@animations.push(appearAnim)
# Play the animation
while inPartyAnimation?; pbUpdate; end
end
#=============================================================================
# Animates a trainer's sprite and party lineup hiding (if they are visible).
# Animates a Pokémon being sent out into battle, then plays the shiny
# animation for it if relevant.
# sendOuts is an array; each element is itself an array: [idxBattler,pkmn]
#=============================================================================
def pbSendOutBattlers(sendOuts,startBattle=false)
return if sendOuts.length==0
# If party balls are still appearing, wait for them to finish showing up, as
# the FadeAnimation will make them disappear.
while inPartyAnimation?; pbUpdate; end
@briefMessage = false
# Make all trainers and party lineups disappear (player-side trainers may
# animate throwing a Poké Ball)
if @battle.opposes?(sendOuts[0][0])
fadeAnim = TrainerFadeAnimation.new(@sprites,@viewport,startBattle)
else
fadeAnim = PlayerFadeAnimation.new(@sprites,@viewport,startBattle)
end
# For each battler being sent out, set the battler's sprite and create two
# animations (the Poké Ball moving and battler appearing from it, and its
# data box appearing)
sendOutAnims = []
sendOuts.each_with_index do |b,i|
pkmn = @battle.battlers[b[0]].effects[PBEffects::Illusion] || b[1]
pbChangePokemon(b[0],pkmn)
pbRefresh
if @battle.opposes?(b[0])
sendOutAnim = PokeballTrainerSendOutAnimation.new(@sprites,@viewport,
@battle.pbGetOwnerIndexFromBattlerIndex(b[0])+1,
@battle.battlers[b[0]],startBattle,i)
else
sendOutAnim = PokeballPlayerSendOutAnimation.new(@sprites,@viewport,
@battle.pbGetOwnerIndexFromBattlerIndex(b[0])+1,
@battle.battlers[b[0]],startBattle,i)
end
dataBoxAnim = DataBoxAppearAnimation.new(@sprites,@viewport,b[0])
sendOutAnims.push([sendOutAnim,dataBoxAnim,false])
end
# Play all animations
loop do
fadeAnim.update
sendOutAnims.each do |a|
next if a[2]
a[0].update
a[1].update if a[0].animDone?
a[2] = true if a[1].animDone?
end
pbUpdate
if !inPartyAnimation?
break if !sendOutAnims.any? { |a| !a[2] }
end
end
fadeAnim.dispose
sendOutAnims.each { |a| a[0].dispose; a[1].dispose }
# Play shininess animations for shiny Pokémon
sendOuts.each do |b|
next if !@battle.showAnims || !@battle.battlers[b[0]].shiny?
pbCommonAnimation("Shiny",@battle.battlers[b[0]])
end
end
#=============================================================================
# Animates a Pokémon being recalled into its Poké Ball and its data box hiding
#=============================================================================
def pbRecall(idxBattler)
@briefMessage = false
# Recall animation
recallAnim = BattlerRecallAnimation.new(@sprites,@viewport,idxBattler)
loop do
recallAnim.update if recallAnim
pbUpdate
break if recallAnim.animDone?
end
recallAnim.dispose
# Data box disappear animation
dataBoxAnim = DataBoxDisappearAnimation.new(@sprites,@viewport,idxBattler)
loop do
dataBoxAnim.update
pbUpdate
break if dataBoxAnim.animDone?
end
dataBoxAnim.dispose
end
#=============================================================================
# Ability splash bar animations
#=============================================================================
def pbShowAbilitySplash(battler)
return if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH
side = battler.index%2
pbHideAbilitySplash(battler) if @sprites["abilityBar_#{side}"].visible
@sprites["abilityBar_#{side}"].battler = battler
abilitySplashAnim = AbilitySplashAppearAnimation.new(@sprites,@viewport,side)
loop do
abilitySplashAnim.update
pbUpdate
break if abilitySplashAnim.animDone?
end
abilitySplashAnim.dispose
end
def pbHideAbilitySplash(battler)
return if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH
side = battler.index%2
return if !@sprites["abilityBar_#{side}"].visible
abilitySplashAnim = AbilitySplashDisappearAnimation.new(@sprites,@viewport,side)
loop do
abilitySplashAnim.update
pbUpdate
break if abilitySplashAnim.animDone?
end
abilitySplashAnim.dispose
end
def pbReplaceAbilitySplash(battler)
return if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH
pbShowAbilitySplash(battler)
end
#=============================================================================
# HP change animations
#=============================================================================
# Shows a HP-changing common animation and animates a data box's HP bar.
# Called by def pbReduceHP, def pbRecoverHP.
def pbHPChanged(battler,oldHP,showAnim=false)
@briefMessage = false
if battler.hp>oldHP
pbCommonAnimation("HealthUp",battler) if showAnim && @battle.showAnims
elsif battler.hp<oldHP
pbCommonAnimation("HealthDown",battler) if showAnim && @battle.showAnims
end
@sprites["dataBox_#{battler.index}"].animateHP(oldHP,battler.hp,battler.totalhp)
while @sprites["dataBox_#{battler.index}"].animatingHP
pbUpdate
end
end
def pbDamageAnimation(battler,effectiveness=0)
@briefMessage = false
# Damage animation
damageAnim = BattlerDamageAnimation.new(@sprites,@viewport,battler.index,effectiveness)
loop do
damageAnim.update
pbUpdate
break if damageAnim.animDone?
end
damageAnim.dispose
end
# Animates battlers flashing and data boxes' HP bars because of damage taken
# by an attack. targets is an array, which are all animated simultaneously.
# Each element in targets is also an array: [battler, old HP, effectiveness]
def pbHitAndHPLossAnimation(targets)
@briefMessage = false
# Set up animations
damageAnims = []
targets.each do |t|
anim = BattlerDamageAnimation.new(@sprites,@viewport,t[0].index,t[2])
damageAnims.push(anim)
@sprites["dataBox_#{t[0].index}"].animateHP(t[1],t[0].hp,t[0].totalhp)
end
# Update loop
loop do
damageAnims.each { |a| a.update }
pbUpdate
allDone = true
targets.each do |t|
next if !@sprites["dataBox_#{t[0].index}"].animatingHP
allDone = false
break
end
next if !allDone
damageAnims.each do |a|
next if a.animDone?
allDone = false
break
end
next if !allDone
break
end
damageAnims.each { |a| a.dispose }
end
#=============================================================================
# Animates a data box's Exp bar
#=============================================================================
def pbEXPBar(battler,startExp,endExp,tempExp1,tempExp2)
return if !battler
startExpLevel = tempExp1-startExp
endExpLevel = tempExp2-startExp
expRange = endExp-startExp
dataBox = @sprites["dataBox_#{battler.index}"]
dataBox.animateExp(startExpLevel,endExpLevel,expRange)
while dataBox.animatingExp; pbUpdate; end
end
#=============================================================================
# Shows stats windows upon a Pokémon levelling up
#=============================================================================
def pbLevelUp(pkmn,_battler,oldTotalHP,oldAttack,oldDefense,oldSpAtk,oldSpDef,oldSpeed)
pbTopRightWindow(
_INTL("Max. HP<r>+{1}\r\nAttack<r>+{2}\r\nDefense<r>+{3}\r\nSp. Atk<r>+{4}\r\nSp. Def<r>+{5}\r\nSpeed<r>+{6}",
pkmn.totalhp-oldTotalHP,pkmn.attack-oldAttack,pkmn.defense-oldDefense,
pkmn.spatk-oldSpAtk,pkmn.spdef-oldSpDef,pkmn.speed-oldSpeed))
pbTopRightWindow(
_INTL("Max. HP<r>{1}\r\nAttack<r>{2}\r\nDefense<r>{3}\r\nSp. Atk<r>{4}\r\nSp. Def<r>{5}\r\nSpeed<r>{6}",
pkmn.totalhp,pkmn.attack,pkmn.defense,pkmn.spatk,pkmn.spdef,pkmn.speed))
end
#=============================================================================
# Animates a Pokémon fainting
#=============================================================================
def pbFaintBattler(battler)
@briefMessage = false
# Pokémon plays cry and drops down, data box disappears
faintAnim = BattlerFaintAnimation.new(@sprites,@viewport,battler.index,@battle)
dataBoxAnim = DataBoxDisappearAnimation.new(@sprites,@viewport,battler.index)
loop do
faintAnim.update
dataBoxAnim.update
pbUpdate
break if faintAnim.animDone? && dataBoxAnim.animDone?
end
faintAnim.dispose
dataBoxAnim.dispose
end
#=============================================================================
# Animates throwing a Poké Ball at a Pokémon in an attempt to catch it
#=============================================================================
def pbThrow(ball,shakes,critical,targetBattler,showPlayer=false)
@briefMessage = false
captureAnim = PokeballThrowCaptureAnimation.new(@sprites,@viewport,
ball,shakes,critical,@battle.battlers[targetBattler],showPlayer)
loop do
captureAnim.update
pbUpdate
break if captureAnim.animDone? && !inPartyAnimation?
end
captureAnim.dispose
end
def pbThrowSuccess
return if @battle.opponent
@briefMessage = false
pbMEPlay(pbGetWildCaptureME)
i = 0
loop do
pbUpdate
break if i>=Graphics.frame_rate*3.5 # 3.5 seconds
i += 1
end
pbMEStop
end
def pbHideCaptureBall(idxBattler)
# NOTE: It's not really worth writing a whole PokeBattle_Animation class for
# making the capture ball fade out.
ball = @sprites["captureBall"]
return if !ball
# Data box disappear animation
dataBoxAnim = DataBoxDisappearAnimation.new(@sprites,@viewport,idxBattler)
loop do
dataBoxAnim.update
ball.opacity -= 12*20/Graphics.frame_rate if ball.opacity>0
pbUpdate
break if dataBoxAnim.animDone? && ball.opacity<=0
end
dataBoxAnim.dispose
end
def pbThrowAndDeflect(ball,idxBattler)
@briefMessage = false
throwAnim = PokeballThrowDeflectAnimation.new(@sprites,@viewport,
ball,@battle.battlers[idxBattler])
loop do
throwAnim.update
pbUpdate
break if throwAnim.animDone?
end
throwAnim.dispose
end
#=============================================================================
# Hides all battler shadows before yielding to a move animation, and then
# restores the shadows afterwards
#=============================================================================
def pbSaveShadows
# Remember which shadows were visible
shadows = Array.new(@battle.battlers.length) do |i|
shadow = @sprites["shadow_#{i}"]
ret = (shadow) ? shadow.visible : false
shadow.visible = false if shadow
next ret
end
# Yield to other code, i.e. playing an animation
yield
# Restore shadow visibility
for i in 0...@battle.battlers.length
shadow = @sprites["shadow_#{i}"]
shadow.visible = shadows[i] if shadow
end
end
#=============================================================================
# Loads a move/common animation
#=============================================================================
# Returns the animation ID to use for a given move/user. Returns nil if that
# move has no animations defined for it.
def pbFindMoveAnimDetails(move2anim,moveID,idxUser,hitNum=0)
id_number = GameData::Move.get(moveID).id_number
noFlip = false
if (idxUser&1)==0 # On player's side
anim = move2anim[0][id_number]
else # On opposing side
anim = move2anim[1][id_number]
noFlip = true if anim
anim = move2anim[0][id_number] if !anim
end
return [anim+hitNum,noFlip] if anim
return nil
end
# Returns the animation ID to use for a given move. If the move has no
# animations, tries to use a default move animation depending on the move's
# type. If that default move animation doesn't exist, trues to use Tackle's
# move animation. Returns nil if it can't find any of these animations to use.
def pbFindMoveAnimation(moveID, idxUser, hitNum)
begin
move2anim = pbLoadMoveToAnim
# Find actual animation requested (an opponent using the animation first
# looks for an OppMove version then a Move version)
anim = pbFindMoveAnimDetails(move2anim, moveID, idxUser, hitNum)
return anim if anim
# Actual animation not found, get the default animation for the move's type
moveData = GameData::Move.get(moveID)
target_data = GameData::Target.get(moveData.target)
moveType = moveData.type
moveKind = moveData.category
moveKind += 3 if target_data.num_targets > 1 || target_data.affects_foe_side
moveKind += 3 if moveKind == 2 && target_data.num_targets > 0
# [one target physical, one target special, user status,
# multiple targets physical, multiple targets special, non-user status]
typeDefaultAnim = {
:NORMAL => [:TACKLE, :SONICBOOM, :DEFENSECURL, :EXPLOSION, :SWIFT, :TAILWHIP],
:FIGHTING => [:MACHPUNCH, :AURASPHERE, :DETECT, nil, nil, nil],
:FLYING => [:WINGATTACK, :GUST, :ROOST, nil, :AIRCUTTER, :FEATHERDANCE],
:POISON => [:POISONSTING, :SLUDGE, :ACIDARMOR, nil, :ACID, :POISONPOWDER],
:GROUND => [:SANDTOMB, :MUDSLAP, nil, :EARTHQUAKE, :EARTHPOWER, :MUDSPORT],
:ROCK => [:ROCKTHROW, :POWERGEM, :ROCKPOLISH, :ROCKSLIDE, nil, :SANDSTORM],
:BUG => [:TWINEEDLE, :BUGBUZZ, :QUIVERDANCE, nil, :STRUGGLEBUG, :STRINGSHOT],
:GHOST => [:LICK, :SHADOWBALL, :GRUDGE, nil, nil, :CONFUSERAY],
:STEEL => [:IRONHEAD, :MIRRORSHOT, :IRONDEFENSE, nil, nil, :METALSOUND],
:FIRE => [:FIREPUNCH, :EMBER, :SUNNYDAY, nil, :INCINERATE, :WILLOWISP],
:WATER => [:CRABHAMMER, :WATERGUN, :AQUARING, nil, :SURF, :WATERSPORT],
:GRASS => [:VINEWHIP, :MEGADRAIN, :COTTONGUARD, :RAZORLEAF, nil, :SPORE],
:ELECTRIC => [:THUNDERPUNCH, :THUNDERSHOCK, :CHARGE, nil, :DISCHARGE, :THUNDERWAVE],
:PSYCHIC => [:ZENHEADBUTT, :CONFUSION, :CALMMIND, nil, :SYNCHRONOISE, :MIRACLEEYE],
:ICE => [:ICEPUNCH, :ICEBEAM, :MIST, nil, :POWDERSNOW, :HAIL],
:DRAGON => [:DRAGONCLAW, :DRAGONRAGE, :DRAGONDANCE, nil, :TWISTER, nil],
:DARK => [:PURSUIT, :DARKPULSE, :HONECLAWS, nil, :SNARL, :EMBARGO],
:FAIRY => [:TACKLE, :FAIRYWIND, :MOONLIGHT, nil, :SWIFT, :SWEETKISS]
}
if typeDefaultAnim[moveType]
anims = typeDefaultAnim[moveType]
if GameData::Move.exists?(anims[moveKind])
anim = pbFindMoveAnimDetails(move2anim, anims[moveKind], idxUser)
end
if !anim && moveKind >= 3 && GameData::Move.exists?(anims[moveKind - 3])
anim = pbFindMoveAnimDetails(move2anim, anims[moveKind - 3], idxUser)
end
if !anim && GameData::Move.exists?(anims[2])
anim = pbFindMoveAnimDetails(move2anim, anims[2], idxUser)
end
end
return anim if anim
# Default animation for the move's type not found, use Tackle's animation
if GameData::Move.exists?(:TACKLE)
return pbFindMoveAnimDetails(move2anim, :TACKLE, idxUser)
end
rescue
end
return nil
end
#=============================================================================
# Plays a move/common animation
#=============================================================================
# Plays a move animation.
def pbAnimation(moveID,user,targets,hitNum=0)
animID = pbFindMoveAnimation(moveID,user.index,hitNum)
return if !animID
anim = animID[0]
target = (targets && targets.is_a?(Array)) ? targets[0] : targets
animations = pbLoadBattleAnimations
return if !animations
pbSaveShadows {
if animID[1] # On opposing side and using OppMove animation
pbAnimationCore(animations[anim],target,user,true)
else # On player's side, and/or using Move animation
pbAnimationCore(animations[anim],user,target)
end
}
end
# Plays a common animation.
def pbCommonAnimation(animName,user=nil,target=nil)
return if !animName || animName==""
target = target[0] if target && target.is_a?(Array)
animations = pbLoadBattleAnimations
return if !animations
animations.each do |a|
next if !a || a.name!="Common:"+animName
pbAnimationCore(a,user,(target!=nil) ? target : user)
return
end
end
def pbAnimationCore(animation,user,target,oppMove=false)
return if !animation
@briefMessage = false
userSprite = (user) ? @sprites["pokemon_#{user.index}"] : nil
targetSprite = (target) ? @sprites["pokemon_#{target.index}"] : nil
# Remember the original positions of Pokémon sprites
oldUserX = (userSprite) ? userSprite.x : 0
oldUserY = (userSprite) ? userSprite.y : 0
oldTargetX = (targetSprite) ? targetSprite.x : oldUserX
oldTargetY = (targetSprite) ? targetSprite.y : oldUserY
# Create the animation player
animPlayer = PBAnimationPlayerX.new(animation,user,target,self,oppMove)
# Apply a transformation to the animation based on where the user and target
# actually are. Get the centres of each sprite.
userHeight = (userSprite && userSprite.bitmap && !userSprite.bitmap.disposed?) ? userSprite.bitmap.height : 128
if targetSprite
targetHeight = (targetSprite.bitmap && !targetSprite.bitmap.disposed?) ? targetSprite.bitmap.height : 128
else
targetHeight = userHeight
end
animPlayer.setLineTransform(
PokeBattle_SceneConstants::FOCUSUSER_X,PokeBattle_SceneConstants::FOCUSUSER_Y,
PokeBattle_SceneConstants::FOCUSTARGET_X,PokeBattle_SceneConstants::FOCUSTARGET_Y,
oldUserX,oldUserY-userHeight/2,
oldTargetX,oldTargetY-targetHeight/2)
# Play the animation
animPlayer.start
loop do
animPlayer.update
pbUpdate
break if animPlayer.animDone?
end
animPlayer.dispose
# Return Pokémon sprites to their original positions
if userSprite
userSprite.x = oldUserX
userSprite.y = oldUserY
userSprite.pbSetOrigin
end
if targetSprite
targetSprite.x = oldTargetX
targetSprite.y = oldTargetY
targetSprite.pbSetOrigin
end
end
end