地上の洞窟

どこにも行かず、液晶と「にらめっこ」し続ける人の物語。

【RGSS3】アニメーション改

アニメーション改
アニメーションを使った演出も自由自在!?

アニメーションの表示機能を大幅に軽量化します。
また、独自の様々な表示機能を追加します。

追加される機能

  • アニメーション表示速度の変更
  • アニメーションの表示無効化
  • アニメーションの描画のみ無効化
  • アニメーションの多重再生
  • アニメーションをキャラクターの現在位置に固定して表示
  • アニメーションを戦闘中のキャラクターに追随させて表示
  • アニメーションの任意位置再生

RGSS3のデフォルトでは、アニメーションの多重再生が出来ず
またマップ上でアニメーションを再生すると、
再生対象のキャラクターにアニメーションがくっついてきて
一部の演出においては不自然です。

それをなんやかんやして多重表示や固定表示できるようにしました。

これで二刀流キャラで長~~いアニメーションを表示しようとして
一つ目のアニメーションがぶった切られることもありません。

二刀流時のアニメーション多重再生
二刀流でアニメーションを重ねて再生!

弾を走って避けるとかそういう演出も簡単にできます。

マトリ〇クスごっこ?
マトリ〇クスごっこ

画面上の好きな位置にアニメーションを表示することも可能。

任意の位置にアニメーションを表示
王様避難し損ねたー!

これらの機能は基本、スクリプトを用いて呼び出すことになります。
詳しくはスクリプト内に記載してあるのでそちらを読んでください。

注意点

既存のアニメーション表示機能とは互換性がありません。(全く別物に作り替えちゃった)
そのため他のアニメーション改変スクリプトとは競合が発生すること確定です。
(申し訳ないで~~す;;)


更新履歴 内容
2023/11/16 スクロール処理の微修正
2023/11/15 ・1アニメーション内で同じレイヤー番号の
 アニメーションの表示、非表示が切り替わる場合、
 次に表示された際そのレイヤーのアニメーションが
 表示されなくなる不具合の修正
・透明度、拡大率をアニメーションごとに
 倍率で変更するオプションの追加
・表示優先度(Z座標)をアニメーションごとに
 定数で変更するオプションの追加
・スクロール時にアニメーションがずれにくくした
 (暫定。まだずれる場合あり)
2023/09/03 アニメーション0番(アニメーション無し)を
追加機能から再生しようとした際クラッシュする不具合の修正
2023/08/25 初版

→スクリプト一覧へ


スクリプト(ダブルクリックで全選択)↓

=begin =========================================================================
 ■ 「アニメーション改」- AnimationKai by 地上の洞窟
================================================================================
  
  アニメーションの軽量化及び、様々な表示機能を追加します。

================================================================================
 ■ 注意
================================================================================

  既存のアニメーション表示機能とは基本的に互換性がありません!
  そのため他のアニメーション改変系スクリプトを導入しても、
  反映されない・クラッシュするなどの不具合が生じる可能性があります。

================================================================================
 ■ 設定項目
=end #==========================================================================

module AnimationKai
  
  
  # RATE : アニメーションの基本再生速度
  # 低い程速くなります。1が最速で、それ以上は速くなりません。
  
  RATE = 4
  
  
  # RATE_CUSTOM : アニメーションごとの再生速度
  # アニメーションID => 再生速度  
  
  RATE_CUSTOM = {
    7 => 4,
    8 => 4,
    9 => 4,
  }


  # ZOOM_RATE : アニメーションごとの表示倍率
  # 1がデフォルト(100%)
  # アニメーションID => 倍率
  
  ZOOM_RATE = {
    79 => 1.0,
  }


  # OPACITY_RATE : アニメーションごとの表示倍率
  # 1がデフォルト(100%)かつ最大
  # アニメーションID => 倍率
  
  OPACITY_RATE = {
    79 => 1.0,
  }


  # Z_OVERRIDE : アニメーションごとのZ座標(表示優先度)
  # 300がデフォルト、-10などにすればキャラクターより下にアニメーションを表示
  # アニメーションID => Z座標

  Z_OVERRIDE = {
    79 => 300,
  }

  
  # RATE_VAR_ID : アニメーション速度を変更するゲーム内変数のID
  # ゲーム内変数の値分、再生速度に加算されます。
  # nilを指定した場合、ゲーム内変数は使用されません。
  
  RATE_VAR_ID = nil
  
  
  # DISABLE_SWITCH_ID : アニメーション再生を無効化するスイッチのID
  # スイッチがONになるとアニメーション再生が無効化されます。
  # nilを指定した場合、スイッチは使用されません。
  
  DISABLE_SWITCH_ID = nil
  
  
  # DISABLE_DRAW_SWITCH_ID : アニメーションの描画を無効化するスイッチのID
  # スイッチがONになるとアニメーションスプライトの描画が無効化されます。
  # 効果音やフラッシュなどの演出は引き続き行われます。
  # nilを指定した場合、スイッチは使用されません。
  
  DISABLE_DRAW_SWITCH_ID = nil


=begin =========================================================================
 ■ 仕様の説明
================================================================================

  ・アニメーション再生の高速化
  → 必要なスプライトのみ作成
     (全レイヤーを消費するようなアニメーションはあまり速くならない)

  ・二刀流使用時などで長いアニメーションを再生した時は
   一つ目のアニメーションを打ち切らず、二つ目を多重再生するようになります。

  ・スクリプトを使用して追加の表示機能を実行できます。
  
================================================================================
 ■ 追加機能の説明
================================================================================

  「アニメーション改」では以下の機能が追加されます。
  
  ・アニメーションの多重再生
  ・アニメーションをキャラクターの現在位置に固定して表示
  ・アニメーションを戦闘中のキャラクターに追随させて表示
  ・アニメーションの任意位置再生

  以下に記載されたスクリプトを実行することで、追加機能を呼び出せます。

==★ マップ上で使える機能 ★====================================================

--------------------------------------------------------------------------------
  AnimationKai.character(character, animation_id, mirror = false)
--------------------------------------------------------------------------------
  
  指定したキャラクターにアニメーションを表示します。

  character     => Game_CharacterBaseに属するキャラクター
  animation_id  => 再生するアニメーションのID
  mirror        => アニメーションの左右反転(省略可)

  例: AnimationKai.character($game_player, 7)

--------------------------------------------------------------------------------
  AnimationKai.character_pos(character, animation_id, mirror = false)
--------------------------------------------------------------------------------
  
  指定したキャラクターにアニメーションを表示します。
  再生されたアニメーションは、再生開始時のキャラクターの描画位置に固定されます。

  character     => Game_CharacterBaseに属するキャラクター
  animation_id  => 再生するアニメーションのID
  mirror        => アニメーションの左右反転(省略可)

  例: AnimationKai.character_pos($game_player, 91)

--------------------------------------------------------------------------------
  AnimationKai.event(event_id, animation_id, mirror = false)
--------------------------------------------------------------------------------
  
  指定したイベントにアニメーションを表示します。

  event_id      => イベントのID
  animation_id  => 再生するアニメーションのID
  mirror        => アニメーションの左右反転(省略可)

  例: AnimationKai.event(1, 7, true)

--------------------------------------------------------------------------------
  AnimationKai.event_pos(event_id, animation_id, mirror = false)
--------------------------------------------------------------------------------
  
  指定したイベントにアニメーションを表示します。
  再生されたアニメーションは、再生開始時のイベントの描画位置に固定されます。

  event_id      => イベントのID
  animation_id  => 再生するアニメーションのID
  mirror        => アニメーションの左右反転(省略可)

  例: AnimationKai.event_pos(1, 91)

--------------------------------------------------------------------------------
  AnimationKai.player(animation_id, mirror = false)
--------------------------------------------------------------------------------
  
  プレイヤーにアニメーションを表示します。

  animation_id  => 再生するアニメーションのID
  mirror        => アニメーションの左右反転(省略可)

  例: AnimationKai.player(7, true)

--------------------------------------------------------------------------------
  AnimationKai.player_pos(animation_id, mirror = false)
--------------------------------------------------------------------------------
  
  プレイヤーにアニメーションを表示します。
  再生されたアニメーションは、再生開始時のイベントの描画位置に固定されます。

  animation_id  => 再生するアニメーションのID
  mirror        => アニメーションの左右反転(省略可)

  例: AnimationKai.player_pos(91)

==★ 戦闘中に使える機能 ★======================================================

--------------------------------------------------------------------------------
  AnimationKai.battler(battler, animation_id, mirror = false)
--------------------------------------------------------------------------------
  
  バトラーにアニメーションを表示します。

  battler       => Game_BattlerBaseに属するバトラー
  animation_id  => 再生するアニメーションのID
  mirror        => アニメーションの左右反転(省略可)

  例: AnimationKai.battler($game_troop.members[0], 7)


--------------------------------------------------------------------------------
  AnimationKai.battler_trace(battler, animation_id, mirror = false)
--------------------------------------------------------------------------------
  
  バトラーにアニメーションを表示します。
  表示されたアニメーションは、バトラーの位置に合わせて追随します。
  
 (標準の戦闘システムではバトラーは移動しないためそのまま使う意味はありません)

  battler       => Game_BattlerBaseに属するバトラー
  animation_id  => 再生するアニメーションのID
  mirror        => アニメーションの左右反転(省略可)

  例: AnimationKai.battler_trace($game_troop.members[0], 7, true)

--------------------------------------------------------------------------------
  AnimationKai.party(actor_id, animation_id, mirror = false)
--------------------------------------------------------------------------------
  
  戦闘中のパーティメンバーにアニメーションを表示します。
  
  (通常の戦闘システムではパーティメンバーのスプライトが表示されないため
   そのまま使うと効果音と画面のフラッシュのみが適応されます)

  actor_id      => パーティメンバーの並び順に対する番号
  animation_id  => 再生するアニメーションのID
  mirror        => アニメーションの左右反転(省略可)

  例: AnimationKai.party(0, 80)
  
--------------------------------------------------------------------------------
  AnimationKai.party_trace(actor_id, animation_id, mirror = false)
--------------------------------------------------------------------------------
  
  戦闘中のパーティメンバーにアニメーションを表示します。
  表示されたアニメーションは、メンバーの位置に合わせて追随します。
  
 (通常の戦闘システムではパーティメンバーのスプライトが表示されないため
   そのまま使うと効果音と画面のフラッシュのみが適応されます)
  
 (標準の戦闘システムではバトラーは移動しないためそのまま使う意味はありません)

  actor_id      => パーティメンバーの並び順に対する番号
  animation_id  => 再生するアニメーションのID
  mirror        => アニメーションの左右反転(省略可)
  
  例: AnimationKai.party_trace(0, 79)

--------------------------------------------------------------------------------
  AnimationKai.troop(enemy_id, animation_id, mirror = false)
--------------------------------------------------------------------------------
  
  戦闘中の敵グループから、指定した敵にアニメーションを表示します。

  enemy_id      => 敵の配置順に対応する番号
  animation_id  => 再生するアニメーションのID
  mirror        => アニメーションの左右反転(省略可)

  例: AnimationKai.troop(0, 7, true)
  
--------------------------------------------------------------------------------
  AnimationKai.troop_trace(enemy_id, animation_id, mirror = false)
--------------------------------------------------------------------------------
  
  戦闘中の敵グループから、指定した敵にアニメーションを表示します。
  表示されたアニメーションは、敵の位置に合わせて追随します。
  
  (標準の戦闘システムではバトラーは移動しないためそのまま使う意味はありません)

  enemy_id      => 敵の配置順に対応する番号
  animation_id  => 再生するアニメーションのID
  mirror        => アニメーションの左右反転(省略可)

  例: AnimationKai.troop_trace(0, 91)
  
==★ シーン上で使える機能 ★====================================================

  基本的にゲーム中のどこででも使えます。
  (Scene_Baseの派生クラス内かつ、update_basicが呼び出されていれば機能します)
  
--------------------------------------------------------------------------------
  AnimationKai.show(x, y, animation_id, mirror = false)
--------------------------------------------------------------------------------
  
  指定された任意の位置にアニメーションを表示します。
  絶対位置で表示位置を指定します。
  ほとんどの他の描画より、上に表示されます。
  
  x             => X座標
  y             => Y座標
  animation_id  => 再生するアニメーションのID
  mirror        => アニメーションの左右反転(省略可)

  例: AnimationKai.show(100, 100, 7)
  
--------------------------------------------------------------------------------
  AnimationKai.show_center(animation_id, mirror = false)
--------------------------------------------------------------------------------
  
  画面の中央にアニメーションを表示します。
  ほとんどの他の描画より、上に表示されます。
  
  animation_id  => 再生するアニメーションのID
  mirror        => アニメーションの左右反転(省略可)

  例: AnimationKai.show_center(80)
  
--------------------------------------------------------------------------------
  AnimationKai.show_r(x, y, animation_id, mirror = false)
--------------------------------------------------------------------------------
  
  指定された任意の位置にアニメーションを表示します。
  画面の中央からの相対位置で表示位置を指定します。
  ほとんどの他の描画より、上に表示されます。

  x             => X座標(0で中央)
  y             => Y座標(0で中央)
  animation_id  => 再生するアニメーションのID
  mirror        => アニメーションの左右反転(省略可)

  例: AnimationKai.show_r(0, 0, 7, true)
  
--------------------------------------------------------------------------------
  AnimationKai.show_p(x_percentage, y_percentage, animation_id, mirror = false)
--------------------------------------------------------------------------------
  
  指定された任意の位置にアニメーションを表示します。
  画面の中央を基準とした相対位置で、
  かつ画面の大きさに対するパーセンテージで表示位置を指定します。
  ほとんどの他の描画より、上に表示されます。

  x_percentage  => X座標 (0で中央、100で右端、-100で左端)
  y_percentage  => Y座標 (0で中央、100で下端、-100で上端)
  animation_id  => 再生するアニメーションのID
  mirror        => アニメーションの左右反転(省略可)

  例: AnimationKai.show_p(-30, 30, 7, true)
  
================================================================================
 ■ ここからソースコード
=end #==========================================================================

  I_ANI = 0
  I_RATE = 1
  I_COUNT = 2
  I_DURATION = 3
  I_OX = 4
  I_OY = 5
  I_FRAMES = 6
  I_TIMING = 7
  I_TIMING_S = 8
  I_TIMING_I = 9
  I_TIMING_FRAME = 10
  I_TO_SCREEN = 11
  I_DISABLE_DRAW = 12
  I_BITMAP1 = 13
  I_BITMAP2 = 14
  I_SPRITES = 15
  I_LAST_CELL_MAX = 16
  I_MIRROR = 17
  I_EXTRA = 18

  FAKE_CHARACTER = Game_CharacterBase.new
  
  $anm_kai_viewport = nil
  $anm_kai_scene_animation = []
  $anm_kai_screen = []
  $anm_kai_bmp_count = Hash.new(0)

  class << self
    def character(character, animation_id, mirror = false)
      character.animation(animation_id, mirror)
    end

    def character_pos(chracter, animation_id, mirror = false)
      character.animation_pos(animation_id, mirror) 
    end

    def event(event_id, animation_id, mirror = false)
      return unless (event = $game_map.events[event_id])
      event.animation(animation_id, mirror)
    end

    def event_pos(event_id, animation_id, mirror = false)
      return unless (event = $game_map.events[event_id])
      event.animation_pos(animation_id, mirror)
    end

    def player(animation_id, mirror = false)
      $game_player.animation(animation_id, mirror)
    end

    def player_pos(animation_id, mirror = false)
      $game_player.animation_pos(animation_id, mirror)
    end

    def battler(battler, animation_id, mirror = false)
      battler.animation(animation_id, mirror)
    end

    def battler_trace(battler, animation_id, mirror = false)
      battler.animation_trace(animation_id, mirror)
    end

    def party(actor_id, animation_id, mirror = false)
      return unless (actor = $game_party.members[actor_id])
      actor.animation(animation_id, mirror)
    end

    def party_trace(actor_id, animation_id, mirror = false)
      return unless (actor = $game_party.members[actor_id])
      actor.animation_trace(animation_id, mirror)
    end

    def troop(enemy_id, animation_id, mirror = false)
      return unless (enemy = $game_troop.members[enemy_id])
      enemy.animation(animation_id, mirror)
    end

    def troop_trace(enemy_id, animation_id, mirror = false)
      return unless (enemy = $game_troop.members[enemy_id])
      enemy.animation_trace(animation_id, mirror)
    end

    def show(x, y, animation_id, mirror = false)
      $anm_kai_scene_animation << Animator.new(x, y, animation_id, mirror)
    end

    def show_center(animation_id, mirror = false)
      $anm_kai_scene_animation << Animator.new(
        Graphics.width / 2,
        Graphics.height / 2,
        animation_id,
        mirror
      )
    end

    def show_r(x, y, animation_id, mirror = false)
      $anm_kai_scene_animation << Animator.new(
        x + Graphics.width / 2,
        y + Graphics.height / 2,
        animation_id,
        mirror
      )
    end

    def show_p(x_percentage, y_percentage, animation_id, mirror = false)
      $anm_kai_scene_animation << Animator.new(
        x_percentage / 100.0 * Graphics.width / 2 + Graphics.width / 2,
        y_percentage / 100.0 * Graphics.height / 2 + Graphics.height / 2,
        animation_id,
        mirror
      )
    end
    
    def run_customize
      customize_params(3, ZOOM_RATE)
      customize_params(6, OPACITY_RATE)
    end

    def customize_params(param_id, data)
      data.each do |id, rate|
        animation = $data_animations[id]
        animation.frames.each do |frame|
          cell_data = frame.cell_data
          cell_max = frame.cell_max
          cell_index = 0
          while cell_index < cell_max
            if cell_data[cell_index, 0] >= 0
              cell_data[cell_index, param_id] *= rate
            end
            cell_index += 1
          end
        end
      end
    end

    def fixed_x(real_x)
      FAKE_CHARACTER.anm_kai_screen_x(real_x)
    end

    def fixed_y(real_y)
      FAKE_CHARACTER.anm_kai_screen_y(real_y)
    end
  end

  class Animator
    def initialize(x, y, animation_id, mirror)
      @animation = $data_animations[animation_id]
      @mirror = mirror
      @frames = @animation.frames
      @timings = @animation.timings
      @timing_size = @timings.size
      @timing_frame = @timing_size > 0 ? @timings[0].frame : 0
      @timing_index = 0
      @count = 0
      
      (@ani_rate = 
        (AnimationKai::RATE_CUSTOM[@animation.id] || AnimationKai::RATE) + 
        (AnimationKai::RATE_VAR_ID && $game_variables[AnimationKai::RATE_VAR_ID] || 0)
      ) < 1 and @ani_rate = 1

      @duration = @animation.frame_max * @ani_rate + 1

      (@disable_draw = 
        AnimationKai::DISABLE_DRAW_SWITCH_ID &&
        $game_switches[AnimationKai::DISABLE_DRAW_SWITCH_ID]
      )

      unless @disable_draw
        @ox = x
        @oy = y

        unless @animation.animation1_name.empty?
          $anm_kai_bmp_count[
            @bitmap1 = Cache.animation(
              @animation.animation1_name,
              @animation.animation1_hue
            )
          ] += 1
        end

        unless @animation.animation2_name.empty?
          $anm_kai_bmp_count[
            @bitmap2 = Cache.animation(
              @animation.animation2_name,
              @animation.animation2_hue
            )
          ] += 1
        end
        @sprites = []
        @last_cell_max = 0
      end
    end

    def update
      if ((@duration -= 1) % @ani_rate) == 0
        if @duration > 0
          update_sprite unless @disable_draw
          update_timing if (@count >= @timing_frame) && (@timing_index < @timing_size)
          @count += 1
        else
          dispose
          return true
        end
      end
      return false
    end

    def update_sprite
      cell_data = (frame = @frames[@count]).cell_data
      cell_max = frame.cell_max > @last_cell_max ? frame.cell_max : @last_cell_max
      @last_cell_max = frame.cell_max

      i = 0
      while i < cell_max
        sprite = @sprites[i]
        pattern = cell_data[i, 0]
        if pattern && pattern >= 0
          bitmap = pattern < 100 ? @bitmap1 : @bitmap2
          if bitmap
            unless sprite
              @sprites[i] = sprite = Sprite.new($anm_kai_viewport)
              sprite.ox = 96
              sprite.oy = 96
              sprite.z = 300 + i
            end
            sprite.bitmap = bitmap
            sprite.src_rect.set(
              pattern % 5 * 192,
              pattern % 100 / 5 * 192, 192, 192
            )
            sprite.visible = true
            if @mirror
              sprite.x = @ox - cell_data[i, 1]
              sprite.y = @oy + cell_data[i, 2]
              sprite.angle = ((360 - cell_data[i, 4]) % 360)
              sprite.mirror = (cell_data[i, 5] == 0)
            else
              sprite.x = @ox + cell_data[i, 1]
              sprite.y = @oy + cell_data[i, 2]
              sprite.angle = cell_data[i, 4]
              sprite.mirror = (cell_data[i, 5] == 1)
            end
            sprite.zoom_x = sprite.zoom_y = cell_data[i, 3] / 100.0
            sprite.opacity = cell_data[i, 6]# * opacity / 255.0
            sprite.blend_type = cell_data[i, 7]
          end
        else
          sprite.visible = false if sprite
        end
        i += 1
      end
    end

    def update_timing
      while @timing_index < @timing_size
        break if @count < (@timing_frame = @timings[@timing_index].frame)
        (timing = @timings[@timing_index]).se.play
        if $anm_kai_viewport && timing.flash_scope == 2
          $anm_kai_viewport.flash(timing.flash_color, timing.flash_duration * @ani_rate)
        end
        @timing_index += 1
      end
    end

    def dispose
      if @bitmap1 && ($anm_kai_bmp_count[@bitmap1] -= 1) == 0
        $anm_kai_bmp_count.delete(@bitmap1)
        @bitmap1.dispose
      end
  
      if @bitmap2 && ($anm_kai_bmp_count[@bitmap2] -= 1) == 0
        $anm_kai_bmp_count.delete(@bitmap1)
        @bitmap2.dispose
      end

      i = 0
      while i < 16
        (sprite = @sprites[i]) and sprite.dispose
        i += 1
      end
      @sprites.clear
    end
  end
end

class Sprite_Base < Sprite
  alias anm_kai_initialize initialize
  def initialize(viewport = nil)
    anm_kai_initialize(viewport)
    @anm_kai_animation = []
  end

  def animation?
    !@anm_kai_animation.empty?
  end

  def update
    super
    update_animation unless @anm_kai_animation.empty?
  end

  def start_animation(animation, mirror = false)
    return unless animation
    return if animation.to_screen? && $anm_kai_screen.include?(animation)
    return if AnimationKai::DISABLE_SWITCH_ID && $game_switches[AnimationKai::DISABLE_SWITCH_ID]

    (@anm_kai_animation ||= []) << create_ani_data(animation, mirror)
  end

  def create_ani_data(animation, mirror)
    (rate = 
      (AnimationKai::RATE_CUSTOM[animation.id] || AnimationKai::RATE) + 
      (AnimationKai::RATE_VAR_ID && $game_variables[AnimationKai::RATE_VAR_ID] || 0)
    ) < 1 and rate = 1

    (disable_draw = 
      !@use_sprite ||
      AnimationKai::DISABLE_DRAW_SWITCH_ID &&
      $game_switches[AnimationKai::DISABLE_DRAW_SWITCH_ID]
    )

    unless disable_draw
      unless animation.animation1_name.empty?
        $anm_kai_bmp_count[
          bitmap1 = Cache.animation(
            animation.animation1_name,
            animation.animation1_hue
          )
        ] += 1
      end
      
      unless animation.animation2_name.empty?
        $anm_kai_bmp_count[
          bitmap2 = Cache.animation(
            animation.animation2_name,
            animation.animation2_hue
          )
        ] += 1
      end
    end
    
    if animation.to_screen?
      if viewport
        ani_ox = viewport.rect.width / 2
        ani_oy = viewport.rect.height / 2
      else
        ani_ox = Graphics.width / 2
        ani_oy = Graphics.height / 2
      end
    else
      ani_ox = x - ox + width / 2
      ani_oy = y - oy
      case animation.position
      when 1 then ani_oy += height / 2
      when 2 then ani_oy += height
      end
    end

    [
      animation,
      rate,
      0,
      animation.frame_max * rate + 1,
      ani_ox,
      ani_oy,
      animation.frames,
      animation.timings,
      animation.timings.size,
      0,
      0,
      animation.to_screen?,
      disable_draw,
      bitmap1,
      bitmap2,
      [],
      0,
      mirror,
      false
    ]
  end

  def update_animation
    $anm_kai_screen.clear unless $anm_kai_screen.empty?
    @anm_kai_animation.delete_if do |ani_data|
      duration = (ani_data[AnimationKai::I_DURATION] -= 1)
      if duration % ani_data[AnimationKai::I_RATE] == 0
        if duration > 0
          ani_data_update_sprite(ani_data) unless ani_data[AnimationKai::I_DISABLE_DRAW]
          ani_data_update_timing(ani_data)
          ani_data[AnimationKai::I_COUNT] += 1
        else
          ani_data_dispose(ani_data)
          next true
        end
      end
      false
    end
  end

  def dispose_animation
    @anm_kai_animation.each { |ani_data| ani_data_dispose(ani_data) }.clear
  end

  def end_animation
    # do nothing
  end

  def ani_data_update_sprite(ani_data)
    sprites = ani_data[AnimationKai::I_SPRITES]
    ox = ani_data[AnimationKai::I_OX]
    oy = ani_data[AnimationKai::I_OY]
    mirror = ani_data[AnimationKai::I_MIRROR]
    frame = ani_data[AnimationKai::I_FRAMES][ani_data[AnimationKai::I_COUNT]]
    cell_data = frame.cell_data
    cell_max = (
      if frame.cell_max > ani_data[AnimationKai::I_LAST_CELL_MAX]
        frame.cell_max
      else
        ani_data[AnimationKai::I_LAST_CELL_MAX]
      end
    )
    ani_data[AnimationKai::I_LAST_CELL_MAX] = frame.cell_max

    i = 0
    while i < cell_max
      sprite = sprites[i]
      pattern = cell_data[i, 0]
      if pattern && pattern >= 0
        if (bitmap = ani_data[pattern < 100 ? AnimationKai::I_BITMAP1 : AnimationKai::I_BITMAP2])
          unless sprite
            sprites[i] = sprite = Sprite.new(viewport)
            sprite.ox = 96
            sprite.oy = 96
            sprite.z = z + (AnimationKai::Z_OVERRIDE[ani_data[AnimationKai::I_ANI].id] || 300) + i
          end
          sprite.bitmap = bitmap
          sprite.src_rect.set(
            pattern % 5 * 192,
            pattern % 100 / 5 * 192, 192, 192
          )
          sprite.visible = true
          if mirror
            sprite.x = ox - cell_data[i, 1]
            sprite.y = oy + cell_data[i, 2]
            sprite.angle = ((360 - cell_data[i, 4]) % 360)
            sprite.mirror = (cell_data[i, 5] == 0)
          else
            sprite.x = ox + cell_data[i, 1]
            sprite.y = oy + cell_data[i, 2]
            sprite.angle = cell_data[i, 4]
            sprite.mirror = (cell_data[i, 5] == 1)
          end
          sprite.zoom_x = sprite.zoom_y = cell_data[i, 3] / 100.0
          sprite.opacity = cell_data[i, 6]# * opacity / 255.0
          sprite.blend_type = cell_data[i, 7]
        end
      else
        sprite.visible = false if sprite
      end
      i += 1
    end
  end

  def ani_data_update_timing(ani_data)
    count = ani_data[AnimationKai::I_COUNT]
    frame = ani_data[AnimationKai::I_TIMING_FRAME]
    return if count < frame

    timing_index = ani_data[AnimationKai::I_TIMING_I]
    timing_size = ani_data[AnimationKai::I_TIMING_S]
    return unless timing_index < timing_size

    timings = ani_data[AnimationKai::I_TIMING]
    
    while timing_index < timing_size
      break if count < (frame = (timing = timings[timing_index]).frame)
      timing.se.play
      case timing.flash_scope
      when 1
        flash(timing.flash_color, timing.flash_duration * ani_data[AnimationKai::I_RATE])
      when 2
        viewport.flash(timing.flash_color, timing.flash_duration * ani_data[AnimationKai::I_RATE]) if viewport
      when 3
        flash(nil, timing.flash_duration * ani_data[AnimationKai::I_RATE])
      end
      timing_index += 1
    end

    ani_data[AnimationKai::I_TIMING_FRAME] = frame
    ani_data[AnimationKai::I_TIMING_I] = timing_index
  end

  def ani_data_dispose(ani_data)
    if (bitmap = ani_data[AnimationKai::I_BITMAP1]) && ($anm_kai_bmp_count[bitmap] -= 1) == 0
      $anm_kai_bmp_count.delete(bitmap)
      bitmap.dispose
    end

    if (bitmap = ani_data[AnimationKai::I_BITMAP2]) && ($anm_kai_bmp_count[bitmap] -= 1) == 0
      $anm_kai_bmp_count.delete(bitmap)
      bitmap.dispose
    end
    
    ani_data[AnimationKai::I_SPRITES].each { |sprite| sprite.dispose if sprite }.clear
  end
end

class Sprite_Character < Sprite_Base
  # 新しいエフェクトの設定(☆再定義)
  def setup_new_effect
    setup_animation
    setup_animation_queue
    setup_balloon
  end

  def setup_animation
    if @anm_kai_animation.empty? && @character.animation_id > 0
      start_animation($data_animations[@character.animation_id])
    end
  end

  def setup_balloon
    if !@balloon_sprite && @character.balloon_id > 0
      @balloon_id = @character.balloon_id
      start_balloon
    end
  end

  def setup_animation_queue
    if @character.animation_queue.empty?
      @character.animation_id = 0 if @character.animation_id > 0
    else
      @character.animation_queue.each do |q|
        if (animation = $data_animations[q[0]])
          ani_data = create_ani_data(animation, q[1])
          ani_data[AnimationKai::I_EXTRA] = [@character.real_x, @character.real_y] if q[2]
          @anm_kai_animation << ani_data
        end
      end.clear
    end
  end

  alias anm_kai_update_position update_position
  def update_position
    anm_kai_update_position
    anm_kai_move_animation
  end
  
  def anm_kai_move_animation
    return if @anm_kai_animation.empty?
    @anm_kai_animation.each do |ani_data|
      next if ani_data[AnimationKai::I_TO_SCREEN]

      if ani_data[AnimationKai::I_EXTRA]
        rx, ry = ani_data[AnimationKai::I_EXTRA]
        ani_ox = AnimationKai.fixed_x(rx) - ox + width / 2
        ani_oy = AnimationKai.fixed_y(ry) - oy
      else
        ani_ox = x - ox + width / 2
        ani_oy = y - oy  
      end

      case ani_data[AnimationKai::I_ANI].position
      when 1 then ani_oy += height / 2
      when 2 then ani_oy += height
      end
      
      ani_data[AnimationKai::I_OX] = ani_ox
      ani_data[AnimationKai::I_OY] = ani_oy
      
      frame = ani_data[AnimationKai::I_FRAMES][ani_data[AnimationKai::I_COUNT - 1]]
      cell_data = frame.cell_data
      cell_max = frame.cell_max
      mirror = ani_data[AnimationKai::I_MIRROR]

      i = 0
      while i < cell_max
        if sprite = ani_data[AnimationKai::I_SPRITES][i]
          if mirror
            sprite.x = ani_ox - cell_data[i, 1]
            sprite.y = ani_oy + cell_data[i, 2]
          else
            sprite.x = ani_ox + cell_data[i, 1]
            sprite.y = ani_oy + cell_data[i, 2]
          end
        end
        i += 1
      end
    end 
  end

  if method_defined?(:vxace_sp1_update_position) && method_defined?(:move_animation)
    def move_animation(dx, dy)
      # do nothing
    end
  end
end

class Sprite_Battler < Sprite_Base
  alias anm_kai_setup_new_animation setup_new_animation
  def setup_new_animation
    anm_kai_setup_new_animation
    setup_animation_queue
  end

  def setup_animation_queue
    return if @battler.animation_queue.empty?
    @battler.animation_queue.each do |q|
      if (animation = $data_animations[q[0]])
        ani_data = create_ani_data(animation, q[1])
        ani_data[AnimationKai::I_EXTRA] = true if q[2]
        @anm_kai_animation << ani_data
      end
    end.clear
  end

  def ani_data_update_sprite(ani_data)
    if ani_data[AnimationKai::I_EXTRA]
      unless ani_data[AnimationKai::I_ANI].to_screen?
        ani_ox = x - ox + width / 2
        ani_oy = y - oy
        case ani_data[AnimationKai::I_ANI].position
        when 1 then ani_oy += height / 2
        when 2 then ani_oy += height
        end
        ani_data[AnimationKai::I_OX] = ani_ox
        ani_data[AnimationKai::I_OY] = ani_oy
      end
    end
    super(ani_data)
  end

  alias anm_kai_update_position update_position
  def update_position
    move_animation
    anm_kai_update_position
  end

  def move_animation
    return if @anm_kai_animation.empty?
    @anm_kai_animation.each do |ani_data|
      next if ani_data[AnimationKai::I_TO_SCREEN]
      if ani_data[AnimationKai::I_EXTRA]
        dx = x - ani_data[AnimationKai::I_OX]
        dy = y - ani_data[AnimationKai::I_OY]
        case ani_data[AnimationKai::I_ANI].position
        when 0 then dy -= height
        when 1 then dy -= height / 2
        end
        ani_data[AnimationKai::I_OX] += dx
        ani_data[AnimationKai::I_OY] += dy
        sprites = ani_data[AnimationKai::I_SPRITES]
        i = 0
        while i < 16
          if (sprite = sprites[i])
            sprite.x += dx
            sprite.y += dy
          end
          i += 1
        end
      end
    end
  end
end

class Game_CharacterBase
  attr_accessor :animation_queue

  alias anm_kai_initialize initialize
  def initialize
    anm_kai_initialize
    @animation_queue = []
  end

  def anm_kai_screen_x(real_x)
    @real_x = real_x
    screen_x
  end

  def anm_kai_screen_y(real_y)
    @real_y = real_y
    screen_y
  end

  def animation(id, mirror = false)
    return if AnimationKai::DISABLE_SWITCH_ID && $game_switches[AnimationKai::DISABLE_SWITCH_ID]
    @animation_queue << [id, mirror, false] # [id, mirror, fixed]
  end

  def animation_pos(id, mirror = false)
    return if AnimationKai::DISABLE_SWITCH_ID && $game_switches[AnimationKai::DISABLE_SWITCH_ID]
    @animation_queue << [id, mirror, true] # [id, mirror, fixed]
  end
end

class Game_BattlerBase
  attr_accessor :animation_queue

  alias anm_kai_initialize initialize
  def initialize
    anm_kai_initialize
    @animation_queue = []
  end

  def animation(id, mirror = false)
    return if AnimationKai::DISABLE_SWITCH_ID && $game_switches[AnimationKai::DISABLE_SWITCH_ID]
    @animation_queue << [id, mirror, false] # [id, mirror, trace]
  end

  def animation_trace(id, mirror = false)
    return if AnimationKai::DISABLE_SWITCH_ID && $game_switches[AnimationKai::DISABLE_SWITCH_ID]
    @animation_queue << [id, mirror, true] # [id, mirror, trace]
  end
end

class Scene_Base
  alias anm_kai_update_basic update_basic
  def update_basic
    anm_kai_update_basic
    unless $anm_kai_scene_animation.empty?
      $anm_kai_scene_animation.delete_if { |ani| ani.update }
      @viewport.update
    end
  end

  alias anm_kai_create_main_viewport create_main_viewport
  def create_main_viewport
    anm_kai_create_main_viewport
    $anm_kai_viewport = @viewport
  end

  alias anm_kai_dispose_main_viewport dispose_main_viewport
  def dispose_main_viewport
    anm_kai_dispose_main_viewport
    $anm_kai_viewport = nil
  end
end

module DataManager
  class << self
    alias anm_kai_load_data_base load_database
    def load_database
      anm_kai_load_data_base
      AnimationKai.run_customize
    end
  end
end