#===============================================================================
# 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,secondAbility=false)
    @secondAbility=secondAbility
    @side = side
    super(sprites,viewport)
  end

  def createProcesses
    if @secondAbility
      return if !@sprites["ability2Bar_#{@side}"]
      bar = addSprite(@sprites["ability2Bar_#{@side}"])
    else
      return if !@sprites["abilityBar_#{@side}"]
      bar = addSprite(@sprites["abilityBar_#{@side}"])
    end

    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 = 5#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
