#==============================================================================
# ■ 精灵残影 by 老鹰（http://oneeyedeagle.lofter.com/）
#==============================================================================
$imported ||= {}
$imported["EAGLE-SpriteGhost"] = true
#==============================================================================
# - 2020.6.10.19 新增事件残影
#=============================================================================
# - 本插件新增了精灵残影特效的全局模块
#-----------------------------------------------------------------------------
# 【使用 - 通用】
#
#  - 利用脚本为指定精灵添加残影更新
#
#        SPRITE_GHOST.add(obj[, params])
#
#     其中 obj 为 Sprite类及其子类的实例
#     其中 params 为传入参数的Hash，可省略
#              （具体参数及预设值可见 INIT_PARAMS 常量）
#     参数一览：
#       :t  → 残影的出现间隔时间
#       :tc  → 每次残影出现间隔的倒计时（当倒计时为0时出现残影，之后赋值 :t）
#       :opa → 残影每帧减少的透明度
#       :blend_type → 残影显示模式（0正常，1加法，2减法）
#
#  - 利用脚本为指定精灵解除残影
#
#        SPRITE_GHOST.finish(obj)
#
#-----------------------------------------------------------------------------
# 【使用 - 事件中的显示图片】
#
#  - 利用脚本为指定显示图片添加残影更新
#
#        SPRITE_GHOST.add_pic(id[, params])
#
#     其中 id 为显示图片的ID序号（同编辑器中显示序号）
#    【注意】在事件的显示图片指令后，请手动等待1帧，确保图片精灵生成，
#            再调用该脚本绑定残影
#
#  - 利用脚本为指定显示图片解除残影
#
#        SPRITE_GHOST.finish_pic(obj)
#
# 【示例】
#
#     SPRITE_GHOST.add_pic(1) → 为 1 号显示图片绑定残影效果
#
#-----------------------------------------------------------------------------
# 【使用 - 地图上的人物】
#
#  - 利用脚本为指定事件添加残影更新
#
#        SPRITE_GHOST.add_chara(id[, params])
#
#     其中 id 为正整数时，代表对应 id 号的事件
#             为负整数时，代表队伍中 数据库 id 号角色
#             为 0 或未找到对应角色时，代表玩家
#
#  - 利用脚本为指定事件解除残影
#
#        SPRITE_GHOST.finish_chara(obj)
#
# 【示例】
#
#     SPRITE_GHOST.add_chara(0) → 为玩家绑定残影效果
#
#-----------------------------------------------------------------------------
# 【原理】
#
#  - 新增了一个全局的模块，用于更新精灵的残影特效
#    残影特效的本质是精灵拷贝，其中 bitmap 为引用，并不拷贝释放
#
#  - 出于效率考虑，当绑定精灵的opacity为0、或者位于屏幕外时，将自动停止生成残影
#
#=============================================================================

module SPRITE_GHOST
  #--------------------------------------------------------------------------
  # ● 【常量】定义参数预设值
  #--------------------------------------------------------------------------
  INIT_PARAMS = {
    :t => 1, # 残影的出现间隔时间
    :tc => 0, # 每次残影出现间隔的倒计时（当倒计时为0时出现残影，之后赋值 :t）
    :opa => 20, # 残影每帧减少的透明度
    :blend_type => 0, # 残影显示模式（0正常，1加法，2减法）
  }
  #--------------------------------------------------------------------------
  # ● 新增残影处理
  #--------------------------------------------------------------------------
  def self.add(obj, params = {})
    return if obj.nil?
    params.merge!(INIT_PARAMS)
    params[:active] = true # 当为 true 时，能够生成残影
    @datas[obj].finish if @datas[obj]
    @datas[obj] = Process_Ghost.new(obj, params)
  end
  #--------------------------------------------------------------------------
  # ● 结束残影生成
  #--------------------------------------------------------------------------
  def self.finish(obj)
    return if @datas[obj].nil?
    @datas[obj].params[:active] = false
  end
#=============================================================================
# ○ RGSS图片
#=============================================================================
  def self.add_pic(id, params = {})
    begin
      s = SceneManager.scene.spriteset.get_picture_sprite(id)
      add(s, params)
    rescue
      p "请不要在 Scene_Map 和 Scene_Battle 之外的场景使用 SPRITE_GHOST.add_pic"
    end
  end
  def self.finish_pic(id)
    begin
      s = SceneManager.scene.spriteset.get_picture_sprite(id)
      finish(s)
    rescue
      p "请不要在 Scene_Map 和 Scene_Battle 之外的场景使用 SPRITE_GHOST.finish_pic"
    end
  end
#=============================================================================
# ○ RGSS地图人物
#=============================================================================
  def self.add_chara(id, params = {})
    begin
      s = SceneManager.scene.spriteset.get_chara_sprite(id)
      add(s, params)
    rescue
      p "请不要在 Scene_Map 之外的场景使用 SPRITE_GHOST.add_chara"
    end
  end
  def self.finish_chara(id)
    begin
      s = SceneManager.scene.spriteset.get_chara_sprite(id)
      finish(s)
    rescue
      p "请不要在 Scene_Map 之外的场景使用 SPRITE_GHOST.finish_chara"
    end
  end
#=============================================================================
# ○ 核心
#=============================================================================
  #--------------------------------------------------------------------------
  # ● 初始化
  #--------------------------------------------------------------------------
  @datas = {} # obj => process
  #--------------------------------------------------------------------------
  # ● 更新（随 Scene_Base#update_basic）
  #--------------------------------------------------------------------------
  def self.update
    @datas.delete_if { |obj, pro| pro.finish? }
    @datas.keys.each do |obj|
      @datas[obj].update
      @datas[obj].finish if @datas[obj].need_finish?
    end
  end
  #--------------------------------------------------------------------------
  # ● 强制结束并清除
  #--------------------------------------------------------------------------
  def self.clear
    @datas.each { |obj, pro| pro.finish }
    @datas.clear
  end
#=============================================================================
# ○ 通用
#=============================================================================
  #--------------------------------------------------------------------------
  # ● 精灵位于屏幕外？
  #--------------------------------------------------------------------------
  def self.out_of_screen?(s)
    s.x - s.ox + s.width < 0 || s.y - s.oy + s.height < 0 ||
    s.x - s.ox > Graphics.width || s.y - s.oy > Graphics.height
  end
  #--------------------------------------------------------------------------
  # ● 获取精灵实际占用矩形
  #--------------------------------------------------------------------------
  def self.get_rect(s)
    x = s.x - s.ox * s.zoom_x; y = s.y - s.oy * s.zoom_y
    w = s.width * s.zoom_x; h = s.height * s.zoom_y
    Rect.new(x, y, w, h)
  end
  #--------------------------------------------------------------------------
  # ● 矩形之间碰撞？
  #--------------------------------------------------------------------------
  def self.rect_collide_rect?(rect1, rect2)
    if((rect1.x > rect2.x && rect1.x > rect2.x + rect2.width-1) ||
       (rect1.x < rect2.x && rect1.x + rect1.width-1 < rect2.x) ||
       (rect1.y > rect2.y && rect1.y > rect2.y + rect2.height-1) ||
       (rect1.y < rect2.y && rect1.y + rect1.height-1 < rect2.y))
      return false
    end
    return true
  end
  #--------------------------------------------------------------------------
  # ● 精灵重叠？
  #--------------------------------------------------------------------------
  def self.sprite_on_sprite?(s1, s2)
    r1 = get_rect(s1)
    r2 = get_rect(s2)
    rect_collide_rect?(r1, r2)
  end
#=============================================================================
# ○ 残影管理类
#=============================================================================
class Process_Ghost
  attr_reader :obj, :params
  #--------------------------------------------------------------------------
  # ● 初始化
  #--------------------------------------------------------------------------
  def initialize(obj, params)
    @obj = obj
    @params = params
    @sprites = []
    @flag_fin = false
  end
  #--------------------------------------------------------------------------
  # ● 更新
  #--------------------------------------------------------------------------
  def update
    update_sprites
    return if !active?
    @params[:tc] -= 1
    new_sprite if @params[:tc] <= 0
  end
  #--------------------------------------------------------------------------
  # ● 更新全部残影
  #--------------------------------------------------------------------------
  def update_sprites
    @sprites.each { |s| s.opacity -= @params[:opa] }
  end
  #--------------------------------------------------------------------------
  # ● 新增一个残影
  #--------------------------------------------------------------------------
  def new_sprite
    # 去除头部已经结束的残影，优化效率
    if !@sprites.empty? && @sprites[0].finish?
      s = @sprites.shift
      s.dispose
    end
    s = Sprite_Ghost.new(@obj)
    s.blend_type = @params[:blend_type]
    @sprites.push(s)
    @params[:tc] = @params[:t]
  end
  #--------------------------------------------------------------------------
  # ● 继续生成残影？
  #--------------------------------------------------------------------------
  def active?
    return false if @params[:active] == false
    return false if @obj.opacity <= 0
    return false if SPRITE_GHOST.out_of_screen?(@obj)
    return true
  end
  #--------------------------------------------------------------------------
  # ● 需要结束？
  #--------------------------------------------------------------------------
  def need_finish?
    return false if @sprites.any? { |s| !s.finish? }
    return true
  end
  #--------------------------------------------------------------------------
  # ● 强制结束
  #--------------------------------------------------------------------------
  def finish
    @flag_fin = true
    @sprites.each { |s| s.dispose }
    @sprites.clear
  end
  #--------------------------------------------------------------------------
  # ● 已经结束？
  #--------------------------------------------------------------------------
  def finish?
    @flag_fin
  end
end
#=============================================================================
# ○ 实际显示的残影精灵
#=============================================================================
class Sprite_Ghost < Sprite
  #--------------------------------------------------------------------------
  # ● 初始化
  #--------------------------------------------------------------------------
  def initialize(s)
    super(nil)
    reset(s)
  end
  #--------------------------------------------------------------------------
  # ● 重置
  #--------------------------------------------------------------------------
  def reset(s)
    self.viewport = s.viewport if s.viewport
    self.bitmap = s.bitmap
    self.src_rect = s.src_rect
    self.x = s.x
    self.y = s.y
    self.z = s.z
    self.z -= 1 if self.z > 0
    self.ox = s.ox
    self.oy = s.oy
    self.zoom_x = s.zoom_x
    self.zoom_y = s.zoom_y
    self.angle = s.angle
    self.mirror = s.mirror
    self.opacity = s.opacity
    self.color = s.color
  end
  #--------------------------------------------------------------------------
  # ● 已经结束？
  #--------------------------------------------------------------------------
  def finish?
    self.opacity == 0
  end
  #--------------------------------------------------------------------------
  # ● 释放
  #--------------------------------------------------------------------------
  def dispose
    self.viewport = nil
    self.bitmap = nil
    super
  end
end
end # end of module
#=============================================================================
# ○ 绑定
#=============================================================================
class Scene_Base
  #--------------------------------------------------------------------------
  # ● 基础更新
  #--------------------------------------------------------------------------
  alias eagle_sprite_ghost_update_basic update_basic
  def update_basic
    eagle_sprite_ghost_update_basic
    SPRITE_GHOST.update
  end
  #--------------------------------------------------------------------------
  # ● 结束处理
  #--------------------------------------------------------------------------
  alias eagle_sprite_ghost_terminate terminate
  def terminate
    eagle_sprite_ghost_terminate
    SPRITE_GHOST.clear
  end
end
#=============================================================================
# ○ Scene_Map
#=============================================================================
class Spriteset_Map
  attr_reader :character_sprites
  #--------------------------------------------------------------------------
  # ● 获取图片精灵
  #--------------------------------------------------------------------------
  def get_picture_sprite(id)
    @picture_sprites[id] || nil
  end
  #--------------------------------------------------------------------------
  # ● 获取事件对象
  #--------------------------------------------------------------------------
  def get_chara_obj(id)
    if id > 0 # 第id号事件
      chara = $game_map.events[id]
      chara ||= $game_map.events[game_message.event_id]
      return chara
    elsif id < 0 # 队伍中数据库id号角色
      id = id.abs
      $game_player.followers.each { |f|
        return f if f.actor && f.actor.actor.id == id
      }
    end
    return $game_player
  end
  #--------------------------------------------------------------------------
  # ● 获取事件精灵
  #--------------------------------------------------------------------------
  def get_chara_sprite(id)
    chara = get_chara_obj(id)
    @character_sprites.each do |s|
      return s if s.character == chara
    end
  end
end
class Scene_Map; attr_reader :spriteset; end
#=============================================================================
# ○ Scene_Battle
#=============================================================================
class Spriteset_Battle
  #--------------------------------------------------------------------------
  # ● 获取图片精灵
  #--------------------------------------------------------------------------
  def get_picture_sprite(id)
    @picture_sprites[id] || nil
  end
end
class Scene_Battle; attr_reader :spriteset; end
