#==============================================================================
# ■ 技能消耗依赖 v1.3 by SailCat
#------------------------------------------------------------------------------
#   方法:本脚本插入到Main之前使用,并依赖数据库通用备注接口插件(在其之后)
#   版本:v1.3 (Build 171122)
#   效果:
#     1. 技能消耗SP以外的游戏数据,包括HP、道具、武器、防具、金钱、经验、变量
#     2. 技能使用场合依赖游戏条件,包括开关、状态、地图相关、装备、能力、步数等
#     3. 技能消耗额外的回合(技能冷却)
#   配置:插件为即插即用型,没有任何配置项,也不需要在脚本中设定任何东西
#   冲突:其他同类脚本,以及魔改了Scene_Skill的脚本
#   说明:
#     1. 技能消耗道具、武器、防具、金钱、变量时,有使用检查,不够消耗不让用。
#     2. 技能消耗HP、经验时,无使用检查,扣到0为止。
#     3. 技能依赖开关、状态、地图、地形、装备、属性均检查至所有条件严格相符。
#     4. 技能依赖能力检查条件为能力在指定范围内,步数检查条件为指定步数以上。
#     5. 备注写法:
#        (写在描述里,不同条件可以叠加用分号隔开,备注和描述以#号分隔)
#        (当[XX集]内容只有一条时,方括号可省略,当内容序号连续时,可写成子界)
#        (条件太多写不下时,可用超长备注法,见依赖插件的说明)
#        a) 技能消耗HP:ch=值
#        b) 技能消耗道具:ci=[消耗道具ID集]
#        消耗道具个数>1的情况下,ID后缀两位小数为消耗个数,如4.05消耗4号道具5个
#        写成4和4.00是不一样的,前者消耗4号道具1个,后者要求持有4号道具但不消耗
#        c) 技能消耗武器:cw=[消耗武器ID集],说明同道具,消耗的是持有的武器
#        d) 技能消耗防具:ca=[消耗防具ID集],说明同道具,消耗的是持有的防具
#        e) 技能消耗金钱:cg=值
#        f) 技能消耗经验:cx=值
#        g) 技能消耗变量:cv={变量ID=>消耗值},写成hash形式,不能省略花括号
#        h) 技能消耗回合:cd=值
#        i) 技能依赖开关:cs=[开关集],开关ID为正数要求ON,为负数要求OFF
#        j) 技能依赖状态:cc=[状态集],状态ID为正数要求有,为负数要求没有
#        k) 技能依赖地图:cm=[地图ID集],地图ID为正数要求在,为负数要求不在
#        l) 技能依赖地形:ct=[地形标志集],标志为正数要求是,为负数要求不是
#        m) 技能依赖装备:cq=[装备ID集],查身上装备,武器ID写负数,防具ID写正数
#        n) 技能依赖步数:cp=值
#        o) 技能依赖属性:ce=[属性ID集],查装备属性,正数要求有,负数要求没有
#        p) 技能依赖能力:cn=能力字符串,如"str300",有多个条件直接连写
#        能力可以用hp/sp/str/dex/agi/int,大小写不敏感
#        条件值的写法:300或300+表示大于等于300;300-表示小于等于300;
#                      =300表示恰好300;300~400表示300到400之间(闭区间)
#        q) 技能依赖茂密:cb+(要求茂密处)或cb-(要求非茂密处)
#        r) 技能依赖公式:cf:公式脚本字符串,公式要能返回true或false的真伪值
#        公式中可以使用a代表技能使用者,v代表变量,s代表开关
#==============================================================================
#==============================================================================
# ■ SailCat's 插件公用
#==============================================================================
module SailCat
  $sailcat_import ||= {}
  #--------------------------------------------------------------------------
  # ● 植入与依赖检查
  #--------------------------------------------------------------------------
  if $sailcat_import[:DataNoteCore].to_f >= 2.0
    $sailcat_import[:SkillCost] = 1.3
  else
    raise "缺少依赖插件,通用数据库备注接口(v2.0以上版本)"
  end
end
module RPG
  #============================================================================
  # ■ Skill_ItemCost
  #----------------------------------------------------------------------------
  #  处理技能消耗道具、武器、防具的内部类
  #============================================================================
  class Skill_ItemCost
    #--------------------------------------------------------------------------
    # ● 定义实例变量
    #--------------------------------------------------------------------------
    attr_accessor :id                     # 消耗的物品ID
    attr_accessor :number                 # 消耗的数目
    #--------------------------------------------------------------------------
    # ● 初期化
    #--------------------------------------------------------------------------
    def initialize(n)
      n = 0 unless n.is_a?(Numeric)
      n += 0.01 if n.is_a?(Fixnum)
      @id = Integer(n).abs
      @number = [Integer((n - @id) * 100.0 + 1e-6), 0].max
    end
    #--------------------------------------------------------------------------
    # ● 检查符合性
    #--------------------------------------------------------------------------
    def match?(proc)
      return true if @id == 0
      proc.call(@id) >= [@number, 1].max
    end
  end
  #============================================================================
  # ■ Skill_EquipCheck
  #----------------------------------------------------------------------------
  #  处理技能依赖装备状态的内部类
  #============================================================================
  class Skill_EquipCheck
    #--------------------------------------------------------------------------
    # ● 定义实例变量
    #--------------------------------------------------------------------------
    attr_accessor :weapons                # 依赖的武器ID(空为不检查)
    attr_accessor :armors1                # 依赖的盾牌ID(空为不检查)
    attr_accessor :armors2                # 依赖的头盔ID(空为不检查)
    attr_accessor :armors3                # 依赖的护甲ID(空为不检查)
    attr_accessor :armors4                # 依赖的饰品ID(空为不检查)
    #--------------------------------------------------------------------------
    # ● 初期化
    #--------------------------------------------------------------------------
    def initialize(ary)
      @weapons = @armors1 = @armors2 = @armors3 = @armors4 = []
      ary.map! {|i| i.to_a}.flatten!
      ary.each do |i|
        if i < 0
          @weapon_id.push(-i)
        else
          kind = $data_armors[i].kind + 1
          instance_eval("@armors#{kind}.push(#{i})") rescue nil
        end
      end
    end
    #--------------------------------------------------------------------------
    # ● 检查符合性
    #--------------------------------------------------------------------------
    def match?(a)
      return false if [email protected]? and [email protected]?(a.weapon_id)
      return false if [email protected]? and [email protected]?(a.armor1_id)
      return false if [email protected]? and [email protected]?(a.armor2_id)
      return false if [email protected]? and [email protected]?(a.armor3_id)
      return false if [email protected]? and [email protected]?(a.armor4_id)
      return true
    end
  end
  #============================================================================
  # ■ Skill_AbilityCheck
  #----------------------------------------------------------------------------
  #  处理技能依赖能力的内部类
  #============================================================================
  class Skill_AbilityCheck
    #--------------------------------------------------------------------------
    # ● 常量
    #--------------------------------------------------------------------------
    REGEX = /(hp|sp|str|dex|agi|int)(=?([0-9]+)([+\-~])?([0-9]+)?)/i
    #--------------------------------------------------------------------------
    # ● 定义实例变量
    #--------------------------------------------------------------------------
    attr_accessor :hp_range               # 依赖的 HP % 范围
    attr_accessor :sp_range               # 依赖的 SP % 范围
    attr_accessor :str_range              # 依赖的力量值范围
    attr_accessor :dex_range              # 依赖的灵巧值范围
    attr_accessor :agi_range              # 依赖的速度值范围
    attr_accessor :int_range              # 依赖的魔力值范围
    #--------------------------------------------------------------------------
    # ● 初期化
    #--------------------------------------------------------------------------
    def initialize(str)
      @hp_range = @sp_range = 0..100
      @str_range = @dex_range = @agi_range = @int_range = 0..999
      ary = str.scan(REGEX)
      ary.each do |a|
        name = a[0].downcase
        min = a[2]
        max = a[4] || a[2]
        if a[1][0] != 61
          max = (name[1] == 112 ? 100 : 999) if a[3] == nil or a[3] == "+"
          min = 0 if a[3] == "-" and a[4] == nil
        end
        instance_eval("@#{name}_range = #{min}..#{max}") rescue nil
      end
    end
    #--------------------------------------------------------------------------
    # ● 检查符合性
    #--------------------------------------------------------------------------
    def match?(a)
      return false unless @hp_range === a.hp * 100 / a.maxhp
      return false unless @sp_range === a.sp * 100 / [a.maxsp, 1].max
      return false unless @str_range === a.str
      return false unless @dex_range === a.dex
      return false unless @agi_range === a.agi
      return false unless @int_range === a.int
      return true
    end
  end
  #============================================================================
  # ■ RPG::Skill
  #----------------------------------------------------------------------------
  #  数据库的技能类扩展
  #============================================================================
  class Skill
    #--------------------------------------------------------------------------
    # ● 备注定义
    #--------------------------------------------------------------------------
    def hp_cost;       _ch(0).abs;                                        end
    def exp_cost;      _cx(0).abs;                                        end
    def gold_cost;     _cg(0).abs;                                        end
    def cooldown;      _cd(0).abs;                                        end
    def variable_cost; _cv({});                                           end
    def item_cost;     _ci([]).to_a.map {|f| Skill_ItemCost.new(f)};      end
    def weapon_cost;   _cw([]).to_a.map {|f| Skill_ItemCost.new(f)};      end
    def armor_cost;    _ca([]).to_a.map {|f| Skill_ItemCost.new(f)};      end
    def switch_check;  flat_array(_cs([]));                               end
    def state_check;   flat_array(_cc([]));                               end
    def map_check;     flat_array(_cm([]));                               end
    def terrain_check; flat_array(_ct([]));                               end
    def element_check; flat_array(_ce([]));                               end
    def bush_check;    _cb(nil);                                          end
    def step_check;    _cp(0).abs;                                        end
    def equip_check;   Skill_EquipCheck.new(_cq([]).to_a);                end
    def ability_check; Skill_AbilityCheck.new(_cn("").to_s);              end
    def eval_check;    _cf("true");                                       end
    #--------------------------------------------------------------------------
    # ● 公式依赖判定
    #--------------------------------------------------------------------------
    def eval_match?(actor)
      result = lambda {|a, s, v| eval(eval_check) rescue true}
      result.call(actor, $game_switches, $game_variables)
    end
    #--------------------------------------------------------------------------
    # ● 通用正负依赖判定
    #--------------------------------------------------------------------------
    def pn_match?(set, obj, proc = Proc.new{|a, b| a == b})
      (set.reject {|s| (s > 0) == proc.call(obj, s.abs)}).empty?
    end
    #--------------------------------------------------------------------------
    # ● 变量消耗判定
    #--------------------------------------------------------------------------
    def variable_match?
      variable_cost.each do |k, v|
        return false if $game_variables[k] < v
      end
      return true
    end
    #--------------------------------------------------------------------------
    # ● 道具消耗判定
    #--------------------------------------------------------------------------
    def item_match?(set, proc)
      set.each do |s|
        return false unless s.match?(proc)
      end
      return true
    end
  end
end
#==============================================================================
# ■ Game_Battler
#==============================================================================
class Game_Battler
  #--------------------------------------------------------------------------
  # ● 使用技能的额外消耗
  #     skill : 特技
  #--------------------------------------------------------------------------
  def skill_cost_extra(skill)
    self.hp -= skill.hp_cost
  end
end
#==============================================================================
# ■ Game_Actor
#==============================================================================
class Game_Actor
  #--------------------------------------------------------------------------
  # ● 定义实例变量
  #--------------------------------------------------------------------------
  attr_reader   :cooldowns             # 冷却中的技能集
  #--------------------------------------------------------------------------
  # ● 方法重定义
  #--------------------------------------------------------------------------
  unless method_defined? :sailcat_skill_can_use?
    alias sailcat_skillcost_skill_can_use? skill_can_use?
  end
  #--------------------------------------------------------------------------
  # ● 可以使用特技判定
  #     skill_id : 特技 ID
  #--------------------------------------------------------------------------
  def skill_can_use?(skill_id)
    # 获得对应的特技实体
    skill = $data_skills[skill_id]
    # 装备不符的情况下,不能使用
    if not skill.equip_check.match?(self)
      return false
    # 能力不符的情况下,不能使用
    elsif not skill.ability_check.match?(self)
      return false
    # 步数不够的情况下,不能使用
    elsif $game_party.steps < skill.step_check
      return false
    # 金钱不够的情况下,不能使用
    elsif $game_party.silver < skill.gold_cost
      return false
    # 属性不符的情况下,不能使用
    elsif not skill.pn_match?(skill.element_check, self, Proc.new{|a,b|
      a.element_set.include?(b) or ([a.armor1_id, a.armor2_id,
      a.armor3_id, a.armor4_id].inject(false) {|res, id|
        res |= id > 0 and $data_armors[id].guard_element_set.include?(b)})
      })
      return false
    # 地图不符的情况下,不能使用
    elsif not skill.pn_match?(skill.map_check, $game_map.map_id)
      return false
    # 地形不符的情况下,不能使用
    elsif not skill.pn_match?(skill.terrain_check, $game_player.terrain_tag)
      return false
    # 状态不符的情况下,不能使用
    elsif not skill.pn_match?(skill.state_check, self, 
      Proc.new{|a, b| a.state?(b)})
      return false
    # 茂盛不符的情况下,不能使用
    elsif skill.bush_check != nil and 
      skill.bush_check != ($game_player.bush_depth == 12)
      return false
    # 开关不符的情况下,不能使用
    elsif not skill.pn_match?(skill.switch_check, $game_switches,
      Proc.new{|a, b| a[b]})
      return false
    # 战斗时,冷却中的情况下,不能使用
    elsif $game_temp.in_battle and cooldown?(skill_id)
      return false
    # 变量不符的情况下,不能使用
    elsif not skill.variable_match?
      return false
    # 道具不够的情况下,不能使用
    elsif not skill.item_match?(skill.item_cost,
      Proc.new{|n| $game_party.item_number(n)})
      return false
    # 武器不够的情况下,不能使用
    elsif not skill.item_match?(skill.weapon_cost,
      Proc.new{|n| $game_party.weapon_number(n)})
      return false
    # 防具不够的情况下,不能使用
    elsif not skill.item_match?(skill.armor_cost,
      Proc.new{|n| $game_party.armor_number(n)})
      return false
    # 公式计算为false或nil的情况下,不能使用
    elsif not skill.eval_match?(self)
      return false
    end
    # 进行原始判定
    sailcat_skillcost_skill_can_use?(skill_id)
  end
  #--------------------------------------------------------------------------
  # ● 清除冷却技能
  #--------------------------------------------------------------------------
  def reset_cooldown
    @cooldowns = {}
  end
  #--------------------------------------------------------------------------
  # ● 设置冷却技能
  #     skill_id : 特技 ID
  #--------------------------------------------------------------------------
  def set_cooldown(skill_id)
    if $data_skills[skill_id].cooldown > 0
      @cooldowns[skill_id] = $data_skills[skill_id].cooldown
    end
  end
  #--------------------------------------------------------------------------
  # ● 更新冷却技能,该方法一回合调用一次
  #--------------------------------------------------------------------------
  def update_cooldown
    @cooldowns.each_key {|k| @cooldowns[k] -= 1}
    @cooldowns.delete_if {|k, v| v == 0}
  end
  #--------------------------------------------------------------------------
  # ● 冷却中判定
  #     skill_id : 特技 ID
  #--------------------------------------------------------------------------
  def cooldown?(skill_id)
    @cooldowns.has_key?(skill_id)
  end
  #--------------------------------------------------------------------------
  # ● 使用技能的额外消耗
  #     skill : 特技 ID
  #--------------------------------------------------------------------------
  def skill_cost_extra(skill)
    super
    self.exp -= skill.exp_cost
    $game_party.lose_gold(skill.gold_cost)
    skill.item_cost.each {|i| $game_party.lose_item(i.id, i.number)}
    skill.weapon_cost.each {|i| $game_party.lose_weapon(i.id, i.number)}
    skill.armor_cost.each {|i| $game_party.lose_armor(i.id, i.number)}
    skill.variable_cost.each_pair {|k, v| $game_variables[k] -= v}
    set_cooldown(skill.id) if $game_temp.in_battle
  end
end
#==============================================================================
# ■ Game_Party
#==============================================================================
class Game_Party
  #--------------------------------------------------------------------------
  # ● 清除冷却技能
  #--------------------------------------------------------------------------
  def reset_cooldown
    actors.each {|a| a.reset_cooldown}
  end
  #--------------------------------------------------------------------------
  # ● 更新冷却技能,该方法一回合调用一次
  #--------------------------------------------------------------------------
  def update_cooldown
    actors.each {|a| a.update_cooldown}
  end
end
#==============================================================================
# ■ Scene_Battle
#==============================================================================
class Scene_Battle
  #--------------------------------------------------------------------------
  # ● 方法重定义
  #--------------------------------------------------------------------------
  unless method_defined? :sailcat_start_phase1
    alias sailcat_skillcost_start_phase1 start_phase1
    alias sailcat_skillcost_start_phase4 start_phase4
    alias sailcat_skillcost_make_skill_action_result make_skill_action_result
  end
  #--------------------------------------------------------------------------
  # ● 开始自由战斗回合
  #--------------------------------------------------------------------------
  def start_phase1
    # 调用原方法
    sailcat_skillcost_start_phase1
    # 清除技能冷却
    $game_party.reset_cooldown
  end
  #--------------------------------------------------------------------------
  # ● 开始主回合
  #--------------------------------------------------------------------------
  def start_phase4
    # 调用原方法
    sailcat_skillcost_start_phase4
    # 更新技能冷却
    $game_party.update_cooldown
  end
  #--------------------------------------------------------------------------
  # ● 生成特技行动结果
  #--------------------------------------------------------------------------
  def make_skill_action_result
    # 调用原方法
    sailcat_skillcost_make_skill_action_result
    # 额外消耗
    @active_battler.skill_cost_extra(@skill)
  end
end
#==============================================================================
# ■ Scene_Skill
#==============================================================================
class Scene_Skill
  #--------------------------------------------------------------------------
  # ● 刷新画面 (特技窗口被激活的情况下)
  #--------------------------------------------------------------------------
  def update_skill
    # 按下 B 键的情况下
    if Input.trigger?(Input::B)
      # 演奏取消 SE
      $game_system.se_play($data_system.cancel_se)
      # 切换到菜单画面
      $scene = Scene_Menu.new(1)
      return
    end
    # 按下 C 键的情况下
    if Input.trigger?(Input::C)
      # 获取特技窗口现在选择的特技的数据
      @skill = @skill_window.skill
      # 不能使用的情况下
      if @skill == nil or not @actor.skill_can_use?(@skill.id)
        # 演奏冻结 SE
        $game_system.se_play($data_system.buzzer_se)
        return
      end
      # 演奏确定 SE
      $game_system.se_play($data_system.decision_se)
      # 效果范围是我方的情况下
      if @skill.scope >= 3
        # 激活目标窗口
        @skill_window.active = false
        @target_window.x = (@skill_window.index + 1) % 2 * 304
        @target_window.visible = true
        @target_window.active = true
        # 设置效果范围 (单体/全体) 的对应光标位置
        if @skill.scope == 4 || @skill.scope == 6
          @target_window.index = -1
        elsif @skill.scope == 7
          @target_window.index = @actor_index - 10
        else
          @target_window.index = 0
        end
      # 效果在我方以外的情况下
      else
        # 公共事件 ID 有效的情况下
        if @skill.common_event_id > 0
          # 预约调用公共事件
          $game_temp.common_event_id = @skill.common_event_id
          # 演奏特技使用时的 SE
          $game_system.se_play(@skill.menu_se)
          # 消耗 SP
          @actor.sp -= @skill.sp_cost
          # 额外的消耗
          @actor.skill_cost_extra(@skill)
          # 再生成各窗口的内容
          @status_window.refresh
          @skill_window.refresh
          @target_window.refresh
          # 切换到地图画面
          $scene = Scene_Map.new
          return
        end
      end
      return
    end
    # 按下 R 键的情况下
    if Input.trigger?(Input::R)
      # 演奏光标 SE
      $game_system.se_play($data_system.cursor_se)
      # 移至下一位角色
      @actor_index += 1
      @actor_index %= $game_party.actors.size
      # 切换到别的特技画面
      $scene = Scene_Skill.new(@actor_index)
      return
    end
    # 按下 L 键的情况下
    if Input.trigger?(Input::L)
      # 演奏光标 SE
      $game_system.se_play($data_system.cursor_se)
      # 移至上一位角色
      @actor_index += $game_party.actors.size - 1
      @actor_index %= $game_party.actors.size
      # 切换到别的特技画面
      $scene = Scene_Skill.new(@actor_index)
      return
    end
  end
  #--------------------------------------------------------------------------
  # ● 刷新画面 (目标窗口被激活的情况下)
  #--------------------------------------------------------------------------
  def update_target
    # 按下 B 键的情况下
    if Input.trigger?(Input::B)
      # 演奏取消 SE
      $game_system.se_play($data_system.cancel_se)
      # 删除目标窗口
      @skill_window.active = true
      @target_window.visible = false
      @target_window.active = false
      return
    end
    # 按下 C 键的情况下
    if Input.trigger?(Input::C)
      # 因为 SP 不足而无法使用的情况下
      unless @actor.skill_can_use?(@skill.id)
        # 演奏冻结 SE
        $game_system.se_play($data_system.buzzer_se)
        return
      end
      # 目标是全体的情况下
      if @target_window.index == -1
        # 对同伴全体应用特技使用效果
        used = false
        for i in $game_party.actors
          used |= i.skill_effect(@actor, @skill)
        end
      end
      # 目标是使用者的情况下
      if @target_window.index <= -2
        # 对目标角色应用特技的使用效果
        target = $game_party.actors[@target_window.index + 10]
        used = target.skill_effect(@actor, @skill)
      end
      # 目标是单体的情况下
      if @target_window.index >= 0
        # 对目标角色应用特技的使用效果
        target = $game_party.actors[@target_window.index]
        used = target.skill_effect(@actor, @skill)
      end
      # 使用特技的情况下
      if used
        # 演奏特技使用时的 SE
        $game_system.se_play(@skill.menu_se)
        # 消耗 SP
        @actor.sp -= @skill.sp_cost
        # 额外的消耗
        @actor.skill_cost_extra(@skill)
        # 再生成各窗口内容
        @status_window.refresh
        @skill_window.refresh
        @target_window.refresh
        # 全灭的情况下
        if $game_party.all_dead?
          # 切换到游戏结束画面
          $scene = Scene_Gameover.new
          return
        end
        # 公共事件 ID 有效的情况下
        if @skill.common_event_id > 0
          # 预约调用公共事件
          $game_temp.common_event_id = @skill.common_event_id
          # 切换到地图画面
          $scene = Scene_Map.new
          return
        end
      end
      # 无法使用特技的情况下
      unless used
        # 演奏冻结 SE
        $game_system.se_play($data_system.buzzer_se)
      end
      return
    end
  end
end