Taroxd 基础设置
# @taroxd metadata 1.0
# @display Taroxd 基础设置
# @id taroxd_core
# @help 
#
# 模块或类
#   方法名(参数) { block } / 别名 -> 返回值的类型或介绍
#     方法的介绍。
#     如果返回值没有给出,表明返回值无意义或没有确定的类型。
#
# Module
#   get_access_control(method_name) -> Symbol or nil
#     获取方法的访问控制。返回 :public, :protected, :private 或 nil(未定义)
#     method_name 为字符串或符号(Symbol),下同。
#
#   def_after(method_name) { |args| block } -> method_name
#   def_after(method_name, hook) -> method_name
#     重定义方法 method_name。
#     第一种形式中,将 block 定义为方法,在原方法之后调用。
#     第二种形式中,
#     * 如果 hook 为符号或字符串,那么会在原方法之后调用对应的方法。
#     * 如果 hook 可以响应方法 call,那么会在原方法之后调用 call。
#     * 如果 hook 是 UnboundMethod,那么会将 hook 绑定到对象后调用。
#
#     不改变原方法的返回值和访问控制。
#     无论哪种调用,参数都和原来的参数相同。
#     以下 def_* 的所有方法均有这两种形式。为了方便起见只写出第一种。
#
#     例:
#     class Game_Actor < Game_Battler
#       def_after :initialize do |actor_id|
#         @my_attribute = actor_id
#       end
#     end
#
#   def_after!(method_name) { |args| block } -> method_name
#     与 def_after 类似,但改变原方法的返回值。
#
#   def_before(method_name) { |args| block } -> method_name
#     与 def_after 类似,但在原方法之前调用。
#
#   def_with(method_name) { |old, args| block } -> method_name
#     与 def_after! 类似,但参数不同。old 参数是原方法的返回值。
#
#   def_chain(method_name) { |old, args| block } -> method_name
#     与 def_with 类似,但 old 参数为对应原方法的一个 Method 对象。
#
#   def_and(method_name) { |args| block } -> method_name
#     与 def_after 类似,但仅当原方法的返回值为真时调用。
#     仅当原方法的返回值为真时,才会改变原方法的返回值。
#
#   def_or(method_name) { |args| block } -> method_name
#     与 def_after 类似,但仅当原方法的返回值为伪时调用。
#     仅当原方法的返回值为假时,才会改变原方法的返回值。
#
#   def_if(method_name) { |args| block } -> method_name
#     与 def_before 类似,但仅当调用结果为真时调用原方法。
#     仅当调用的结果为假时,改变原方法的返回值为 nil。
#
#   def_unless(method_name) { |args| block } -> method_name
#     与 def_before 类似,但仅当调用结果为假时调用原方法。
#     仅当调用的结果为真时,改变原方法的返回值为 nil。
#
# Taroxd::ReadNote
#   该模块由以下类 extend。
#   RPG::BaseItem
#   RPG::Map
#   RPG::Event(将事件名称作为备注)
#   RPG::Tileset
#   RPG::Class::Learning
#
#   note_i(method_name, default = 0)
#     定义方法 method_name,读取备注中 <method_name x> 形式的内容。其中 x 为整数。
#     读取到的话,定义的方法会返回读取到的整数值,否则返回 default。
#
#   例:RPG::UsableItem.note_i :item_cost
#
#   note_f(method_name, default = 0.0)
#     与 note_i 类似,但读取的是实数。
#
#   note_s(method_name, default = '')
#     与 note_i 类似,但读取的是字符串。
#
#   note_bool(method_name)
#     定义 method_name 方法,判断备注中是否存在 <method_name> 子字符串。
#     特别地,若 method_name 以问号或感叹号结尾,读取内容中不需要包含问号、感叹号。
#     (上面几个方法也是一样)
#
# Taroxd::DisposeBitmap
#   dispose
#     释放位图后调用父类的 dispose 方法。
#
# Spriteset_Map / Spriteset_Battle
#   self.use_sprite(SpriteClass)
#   self.use_sprite(SpriteClass) { block }
#     在 Spriteset 中使用一个 SpriteClass 的精灵。
#     如果给出 block,则生成精灵使用的参数是 block 在 Spriteset 的上下文中执行后的返回值。
#     自动管理精灵的生成、更新、释放。
#
#    例:Spriteset_Map.use_sprite(Sprite_OnMap) { @viewport1 }
#
# Fixnum
#   id -> self
#     返回 self。这使得之后一些方法的参数既可以用数据库的数据,也可以用整数。
#
# Enumerable
#   sum(base = 0) { |element| block }
#     对所有元素 yield 后求和。base 为初始值。
#     没有 block 参数时,对所有元素求和。
#
#   例:
#   class Game_Unit
#     def tgr_sum
#       alive_members.sum(&:tgr)
#     end
#   end
#
#   pi(base = 1) { |element| block }
#     与 sum 类似,但对所有元素求积。
#
# Game_BaseItem
#   id / item_id -> Fixnum
#     返回物品 id。
#
# Game_BattlerBase
#   mtp -> Numeric
#     返回 max_tp。
#
#   initialized? -> true
#     返回 true。
#
# Game_Battler
#   note_objects -> Enumerator
#   note_objects { |obj| ... } -> self
#     迭代战斗者拥有的所有带有备注的对象。
#    (角色、职业、装备、技能、敌人、状态等)
#     没有 block 给出时,返回 Enumerator 对象。
#
#   note -> String
#     返回数据库对象的备注。
#
#   equips / weapons / armors -> []
#     返回空数组。
#
#   skill?(skill) -> true / false
#     判断是否拥有技能。skill 可以为技能 ID,也可以为 RPG::Skill 的实例。
#
#   skill_learn?(skill) -> true / false
#     判断是否习得技能。skill 可以为技能 ID,也可以为 RPG::Skill 的实例。
#
# Game_Actor
#   data_object -> RPG::Actor
#     返回数据库对象,等价于 actor。
#
#   weapon?(item) / armor?(item) -> true / false
#     检测是否装备对应的物品。item 可以为装备 ID,也可以为 RPG::EquipItem 的实例。
#
#   initialized? -> true / false
#     判断角色是否已经初始化。
#
# Game_Enemy
#   id -> Fixnum
#     返回敌人 id。
#
#   data_object -> RPG::Enemy
#     返回数据库对象,等价于 enemy。
#
#   skills -> Array
#     返回敌人的所有技能实例的数组。
#     skill? 方法判断是否拥有技能与该数组有关。
#
#   basic_skills -> Array
#     返回敌人行动列表中的技能 ID 的数组(元素可以重复)。
#     skill_learn? 方法判断是否学会技能与该数组有关。
#
# Game_Actors
#   include Enumerable
#
#   each -> Enumerator
#   each { |actor| block } -> self
#     迭代已经初始化的每个角色。
#     如果没有给出 block,返回一个 Enumerator 对象。
#
#   include?(actor)
#     角色是否已经初始化。actor 可以为 Game_Actor / RPG::Actor 的对象或 ID。
#
# Game_Unit
#   include Enumerable
#
#   each -> Enumerator
#   each { |battler| block } -> self
#     迭代队伍中的每个成员。
#     如果没有给出 block,返回一个 Enumerator 对象。
#     等价于 members.each。
#
#   each_member -> Enumerator / self
#     each 的别名。
#
#   self[*args] / slice(*args) -> Game_Actor / Array / nil
#     等价于 members[*args]
#
#   empty? -> true / false
#     判断队伍是否为空。
#
#   size / length -> Integer
#     返回队伍人数。
#
# Game_Party
#   include?(actor) -> true / false
#     队伍中是否存在角色。actor 可以为 Game_Actor / RPG::Actor 的对象或 ID。
#
# Game_Map
#   id -> Fixnum
#     返回地图 ID。
#
#   data_object -> RPG::Map
#     返回数据库对象。
#
#   note -> String
#     返回备注内容。
#
# Game_CharacterBase
#   same_pos?(character) -> true / false
#     判定是否处在相同位置。
#     character 是任意能够响应方法 x 和 y 的对象。
#     仅作为点(x, y)进行比较,不考虑 Game_Vehicle 的地图 ID 等问题。

module Taroxd end

module Taroxd::Def

  Singleton = Module.new { Object.send :include, self }
  Module.send :include, self

  def get_access_control(sym)
    return :public    if public_method_defined?    sym
    return :protected if protected_method_defined? sym
    return :private   if private_method_defined?   sym
    nil
  end

  template = lambda do |singleton|
    if singleton
      klass = 'singleton_class'
      get_method = 'method'
      define = 'define_singleton_method'
    else
      klass = 'self'
      get_method = 'instance_method'
      define = 'define_method'
    end
    %(
      def <name>(sym, hook = nil, &b)
        access = #{klass}.get_access_control sym
        old = #{get_method} sym
        if b
          #{define} sym, &b
          hook = #{get_method} sym
        end
        if hook.respond_to? :to_sym
          hook = hook.to_sym
          #{define} sym do |*args, &block|
            <pattern_sym>
          end
        elsif hook.respond_to? :call
          #{define} sym do |*args, &block|
            <pattern_call>
          end
        elsif hook.kind_of? UnboundMethod
          #{define} sym do |*args, &block|
            <pattern_unbound>
          end
        end
        #{klass}.__send__ access, sym
        sym
      end
    )
  end

  # 保存模板和替换 'hook(' 字符串的字符
  template = {false => template.call(false), true => template.call(true)}

  # 替换掉 pattern 中的语法
  gsub_pattern = lambda do |pattern, singleton|
    old = singleton ? 'old' : 'old.bind(self)'
    pattern.gsub('*', '*args, &block')
           .gsub(/old(\()?/) { $1 ? "#{old}.call(" : old }
  end

  # 存入代替 "hook(" 的字符串
  template['sym']     = '__send__(hook, '
  template['call']    = 'hook.call('
  template['unbound'] = 'hook.bind(self).call('

  # 获取定义方法内容的字符串
  # 得到的代码较长,请输出查看
  code = lambda do |name, pattern, singleton|
    pattern = gsub_pattern.call(pattern, singleton)
    template[singleton]
      .sub('<name>', name)
      .gsub(/<pattern_(\w+?)>/) { pattern.gsub('hook(', template[$1]) }
  end

  main = TOPLEVEL_BINDING.eval('self')

  # 定义 def_ 系列方法的方法
  define_singleton_method :def_ do |name, pattern|
    name = "#{__method__}#{name}"
    module_eval code.call(name, pattern, false)
    Singleton.module_eval code.call("singleton_#{name}", pattern, true)
    main.define_singleton_method name, &Kernel.method(name)
  end

  # 实际定义 def_ 系列的方法
  def_ :after,  'ret = old(*); hook(*); ret'
  def_ :after!, 'old(*); hook(*)'
  def_ :before, 'hook(*); old(*)'
  def_ :with,   'hook(old(*), *)'
  def_ :chain,  'hook(old, *)'
  def_ :and,    'old(*) && hook(*)'
  def_ :or,     'old(*) || hook(*)'
  def_ :if,     'old(*) if hook(*)'
  def_ :unless, 'old(*) unless hook(*)'
end

module Taroxd::ReadNote

  include RPG
  BaseItem.extend        self
  Map.extend             self
  Event.extend           self
  Tileset.extend         self
  Class::Learning.extend self

  # 获取 note 的方法
  def note_method
    :note
  end

  # 事件名称作为备注
  def Event.note_method
    :name
  end

  # 备注模板
  def note_any(name, default, re, capture)
    name = name.to_s
    mark = name.slice!(/[?!]\Z/)
    re = re.source if re.kind_of?(Regexp)
    re = "/<#{name.gsub('_', '\s*')}#{re}>/i"
    default = default.inspect
    class_eval %{
      def #{name}
        return @#{name} if instance_variable_defined? :@#{name}
        @#{name} = #{note_method} =~ #{re} ? (#{capture}) : (#{default})
      end
    }, __FILE__, __LINE__
    alias_method name + mark, name if mark
  end

  def note_i(name, default = 0)
    note_any(name, default, '\s*(-?\d+)', '$1.to_i')
  end

  def note_f(name, default = 0.0)
    note_any(name, default, '\s*(-?\d+(?:\.\d+)?)', '$1.to_f')
  end

  def note_s(name, default = '')
    note_any(name, default, '\s*(\S.*)', '$1')
  end

  def note_bool(name)
    note_any(name, false, '', 'true')
  end
end

module Taroxd::DisposeBitmap
  def dispose
    bitmap.dispose if bitmap
    super
  end
end

module Taroxd::SpritesetDSL

  # 方法名
  CREATE_METHOD_NAME  = :create_taroxd_sprites
  UPDATE_METHOD_NAME  = :update_taroxd_sprites
  DISPOSE_METHOD_NAME = :dispose_taroxd_sprites

  # 定义管理精灵的方法
  def self.extended(klass)
    klass.class_eval do
      sprites = nil

      define_method CREATE_METHOD_NAME do
        sprites = klass.sprite_list.map do |sprite_class, get_args|
          if get_args
            sprite_class.new(*instance_eval(&get_args))
          else
            sprite_class.new
          end
        end
      end

      define_method(UPDATE_METHOD_NAME)  { sprites.each(&:update)  }
      define_method(DISPOSE_METHOD_NAME) { sprites.each(&:dispose) }
    end
  end

  def use_sprite(klass, &get_args)
    sprite_list.push [klass, get_args]
  end

  def sprite_list
    @_taroxd_use_sprite ||= []
  end

  # 在一系列方法上触发钩子
  def sprite_method_hook(name)
    def_after :"create_#{name}",  CREATE_METHOD_NAME
    def_after :"update_#{name}",  UPDATE_METHOD_NAME
    def_after :"dispose_#{name}", DISPOSE_METHOD_NAME
  end
end

class Fixnum < Integer
  alias_method :id, :to_int
end

module Enumerable
  def sum(base = 0)
    block_given? ? inject(base) { |a, e| a + yield(e) } : inject(base, :+)
  end

  def pi(base = 1)
    block_given? ? inject(base) { |a, e| a * yield(e) } : inject(base, :*)
  end
end

class Game_BaseItem
  attr_reader :item_id
  alias_method :id, :item_id
end

class Game_BattlerBase

  def initialized?
    true
  end

  def mtp
    max_tp
  end
end

class Game_Battler < Game_BattlerBase

  # 迭代拥有备注的对象
  def note_objects
    return to_enum(__method__) unless block_given?
    states.each { |e| yield e }
    equips.each { |e| yield e if e }
    skills.each { |e| yield e }
    yield data_object
    yield self.class if actor?
  end

  def regenerate_tp
    self.tp += max_tp * trg
  end

  def data_object
    raise NotImplementedError
  end

  def note
    data_object.note
  end

  def id
    data_object.id
  end

  def skills
    (basic_skills | added_skills).sort.map { |id| $data_skills[id] }
  end

  def equips
    []
  end
  alias_method :weapons, :equips
  alias_method :armors,  :equips
  alias_method :basic_skills, :equips

  def skill?(skill)
    basic_skills.include?(skill.id) || added_skills.include?(skill.id)
  end

  def skill_learn?(skill)
    basic_skills.include?(skill.id)
  end
end

class Game_Actor < Game_Battler

  alias_method :data_object, :actor

  def weapon?(weapon)
    @equips.any? { |item| item.id == weapon.id && item.is_weapon? }
  end

  def armor?(armor)
    @equips.any? { |item| item.id == armor.id && item.is_armor? }
  end

  def initialized?
    $game_actors.include?(self)
  end

  private

  def basic_skills
    @skills
  end
end

class Game_Enemy < Game_Battler

  alias_method :data_object, :enemy
  alias_method :id, :enemy_id

  def basic_skills
    enemy.actions.map(&:skill_id)
  end
end

class Game_Actors

  include Enumerable

  def each
    return to_enum(__method__) unless block_given?
    @data.each { |actor| yield actor if actor }
    self
  end

  def include?(actor)
    @data[actor.id]
  end
end

class Game_Unit

  include Enumerable

  def to_a
    members
  end

  def each
    return to_enum(__method__) unless block_given?
    members.each { |battler| yield battler }
    self
  end
  alias_method :each_member, :each

  def [](*args)
    members[*args]
  end
  alias_method :slice, :[]

  def empty?
    members.empty?
  end

  def size
    members.size
  end
  alias_method :length, :size
end

class Game_Party < Game_Unit
  def include?(actor)
    @actors.include?(actor.id)
  end
end

class Game_Map

  alias_method :id, :map_id

  def data_object
    @map
  end

  def note
    @map.note
  end
end

class Game_CharacterBase
  def same_pos?(character)
    @x == character.x && @y == character.y
  end
end

class Spriteset_Map
  extend Taroxd::SpritesetDSL
  sprite_method_hook :timer
end

class Spriteset_Battle
  extend Taroxd::SpritesetDSL
  sprite_method_hook :timer
end
使用目标的扩展
# @taroxd metadata 1.0
# @require taroxd_core
# @id target_ext
# @display 使用目标的扩展
# @help 设置技能、物品的使用目标。
# 使用方法:
#   在技能/物品的备注栏写下类似于下面例子的备注,设置可选择的全体目标。
#
#   该脚本不影响菜单中使用技能/物品。
#
#   用 a 指代使用者,用 b 或 self 指代目标。
#
#   例1:存活队员中 hp 比例最小者。
#   <target>
#     select: alive?
#     min_by: hp_rate
#   </target>
#
#   例2:所有 hp 大于 50 的队员
#   <target>
#     select: hp > 50
#   </target>
#
#   例3:除自己之外的全体队友
#   <target>
#     select: alive? && b != a
#   </target>
#
#   例4:无视生死
#   <target></target>

module Taroxd::TargetExt

  RE_OUTER = /<target>(.*?)<\/target>/mi # 整体设置
  SEPARATOR = ':'                        # 每一行中的分隔符

  # 实例方法,用于选择目标的窗口中

  # virtual
  # 返回可以选择的所有目标
  def targets_to_select
    []
  end

  # 返回设置的目标
  # 若 actor 未初始化,或没有设置目标,返回 nil
  def selectable_targets
    actor = BattleManager.actor
    return unless actor
    item = actor.input.item
    return unless item
    item.get_targets(actor, targets_to_select)
  end

  # 由于父类可能未定义,不调用 super
  def enable?(battler)
    targets = selectable_targets
    !targets || targets.include?(battler)
  end

  def current_item_enabled?
    super && enable?(targets_to_select[index])
  end

  # 模块方法,用于读取备注

  def self.parse_note(note)
    note =~ RE_OUTER ? parse_settings($1) : false
  end

  private

  # lambda do |battlers, a|
  #   battlers.select { |b| b.instance_eval { alive? && b != a } }
  # end
  def self.parse_settings(settings)
    eval %(
      lambda do |battlers, a|
        battlers#{extract_settings(settings)}
      end
    )
  end

  def self.extract_settings(settings)
    settings.each_line.map { |line|
      method, _, block = line.partition(SEPARATOR).map(&:strip)
      if method.empty?
        ''
      elsif block.empty?
        ".#{method}"
      else
        ".#{method} { |b| b.instance_eval { #{block} } }"
      end
    }.join
  end

end

class RPG::UsableItem < RPG::BaseItem

  # 缓存并返回生成的 lambda。
  # 如果不存在,返回伪值。
  def get_target_lambda
    @get_target = Taroxd::TargetExt.parse_note(@note) if @get_target.nil?
    @get_target
  end

  # 返回目标的数组。a:使用者。
  # 如果没有设置,返回 nil。
  def get_targets(a, battlers = nil)
    return unless get_target_lambda
    battlers ||= (for_friend? ? a.friends_unit : a.opponents_unit).members
    Array(get_target_lambda.call(battlers, a))
  end

end

class Game_Action

  def_chain :targets_for_opponents do |old|
    targets = item.get_targets(@subject)
    if !targets
      old.call
    elsif item.for_random?
      Array.new(item.number_of_targets) { random_target(targets) }
    elsif item.for_one?
      num = 1 + (attack? ? subject.atk_times_add.to_i : 0)
      target = if @target_index < 0
        random_target(targets)
      else
        eval_smooth_target(opponents_unit, @target_index)
      end
      Array.new(num, target)
    else
      targets
    end
  end

  def_chain :targets_for_friends do |old|
    targets = item.get_targets(@subject)
    if !targets
      old.call
    elsif item.for_user?
      [subject]
    else
      if item.for_one?
        if @target_index < 0
          [random_target(targets)]
        else
          [eval_smooth_target(friends_unit, @target_index)]
        end
      else
        targets
      end
    end
  end

  private

  def eval_smooth_target(unit, index)
    unit[index] || unit[0]
  end

  def random_target(targets)
    tgr_rand = rand * targets.sum(&:tgr)
    targets.each do |target|
      tgr_rand -= target.tgr
      return target if tgr_rand < 0
    end
    targets.first
  end
end

class Game_BattlerBase
  # 如果设置了目标,必须存在目标才可使用
  def_and :usable_item_conditions_met? do |item|
    targets = item.get_targets(self)
    !targets || !targets.empty?
  end
end

class Game_Battler < Game_BattlerBase
  # 如果设置了目标,删除是否死亡的测试
  def_chain :item_test do |old, user, item|
    if item.get_target_lambda
      return true if $game_party.in_battle
      return true if item.for_opponent?
      return true if item.damage.recover? && item.damage.to_hp? && hp < mhp
      return true if item.damage.recover? && item.damage.to_mp? && mp < mmp
      return true if item_has_any_valid_effects?(user, item)
      false
    else
      old.call(user, item)
    end
  end
end

class Window_BattleActor < Window_BattleStatus

  include Taroxd::TargetExt

  def targets_to_select
    $game_party.battle_members
  end

  def draw_actor_name(actor, x, y, width = 112)
    change_color(hp_color(actor), enable?(actor))
    draw_text(x, y, width, line_height, actor.name)
  end
end

class Window_BattleEnemy < Window_Selectable

  include Taroxd::TargetExt

  def targets_to_select
    $game_troop.alive_members
  end

  def draw_item(index)
    enemy = $game_troop.alive_members[index]
    change_color(normal_color, enable?(enemy))
    name = enemy.name
    draw_text(item_rect_for_text(index), name)
  end
end
简化 handler 设置
# @taroxd metadata 1.0
# @display 简化 handler 设置
# @id symbol_handler
# @help 简化命令窗口的 handler 设置
module Taroxd

  # Window_Command 的子类 include 后,会自动对场景调用 symbol 对应的
  # command_symbol 方法,无需再 set_handler。
  module SymbolHandler

    def handle?(symbol)
      super || symbol_to_command(symbol)
    end

    def call_handler(symbol)
      @handler[symbol].call if @handler.key?(symbol)
      command = symbol_to_command(symbol)
      receiver.send(command) if command
    end

    private

    # 以下方法可由子类覆盖。

    # 调用者。默认为当前场景。
    def receiver
      SceneManager.scene
    end

    def command_prefix
      'command_'
    end

    # 返回符号对应的场景方法名。
    # 场景不能响应 command_symbol 时,返回 nil。
    def symbol_to_command(symbol)
      sym = :"#{command_prefix}#{symbol}"
      sym if receiver.respond_to?(sym)
    end
  end
end