设置地图通行度
# @taroxd metadata 1.0
# @require taroxd_core
# @display 设置地图通行度
# @id set_passage
# @help 
#
#  使用方法2:** require 显示地图通行度 **
#
#    测试模式下,把 EDIT_MODE 设为 true,
#    然后在地图上按下确定键即可改变当前位置的通行度。
#    该模式下,角色可以自由穿透。
#
#    △表示不改变,○表示可以通行,×表示不可通行。颜色表示最终的通行度。
#
#    需要清空设置的话,删除设置文件(见 FILE 常量)即可。
#
#    需要重置一张地图的设置的话,可以调用如下脚本:
#      Taroxd::Passage.clear(map_id)
#    其中 map_id 为地图 ID。若要清除当前地图的通行度,map_id 可以不填。
#    注意,清除后,通行度的显示并不会立即改变。重新打开游戏即可看到效果。

module Taroxd::Passage

  # 地图通行度信息会保存到这个文件。建议每次编辑前备份该文件。
  FILE = 'Data/MapPassage.rvdata2'

  # 是否打开编辑模式。需要前置脚本“显示地图通行度”才可打开。
  EDIT_MODE = false

  # 编辑方式(可整合鼠标脚本)
  EDIT_TRIGGER = -> { Input.trigger?(:C) }
  EDIT_POINT   = -> { [$game_player.x, $game_player.y] }

  # 常量,不建议改动
  DEFAULT    = 0
  PASSABLE   = 1
  IMPASSABLE = 2
  TEXTS = ['△', '○', '×']

  SIZE = TEXTS.size

  # 设置区域通行度。一个以区域ID为键的哈希表。(未设置的区域,ID为0)
  # 哈希表值的意义如下
  # PASSABLE / true:该区域强制可以通行。
  # IMPASSABLE / false:该区域强制不可通行。
  # [区域ID1, 区域ID2, ...]:该区域只能通行至指定的区域。

  # 设置例:
  # REGIONS = {
  #   1 => PASSABLE,
  #   2 => false,
  #   3 => [3, 4],         # 只有通过4号区域才能出入3号区域
  #   5 => [*0..63] - [6]  # 5号区域与6号区域的边界线不可通行
  # }
  REGIONS = {}

  # 通行度的哈希表。以地图ID为键,以通行度的二维 Table 为值。
  @data = File.exist?(FILE) ? load_data(FILE) : {}

  class << self

    # 获取 x, y 坐标处的 d 方向通行度设定。(DEFAULT/PASSABLE/IMPASSABLE)
    def [](x, y, d)
      data[x, y] == DEFAULT ? region_passable(x, y, d) : data[x, y]
    end

    # 更新,编辑模式下应每帧调用一次
    def update
      return unless EDIT_TRIGGER.call
      x, y = EDIT_POINT.call
      data[x, y] = (data[x, y] + 1) % SIZE
      save
    end

    # 获取当前地图的数据
    def data
      table = @data[map_id] ||= Table.new(width, height)
      if table.xsize < width || table.ysize < height
        update_table(table)
      else
        table
      end
    end

    # 清除一个地图的设置
    def clear(map_id = $game_map.map_id)
      @data.delete(map_id)
      save
    end

    private

    # 区域设置中能否通行(DEFAULT/PASSABLE/IMPASSABLE)
    def region_passable(x, y, d)
      settings = REGIONS[$game_map.region_id(x, y)]
      case settings
      when true, PASSABLE then PASSABLE
      when false, IMPASSABLE then IMPASSABLE
      when Enumerable
        x2 = $game_map.round_x_with_direction(x, d)
        y2 = $game_map.round_y_with_direction(y, d)
        settings.include?($game_map.region_id(x2, y2)) ? DEFAULT : IMPASSABLE
      else DEFAULT
      end
    end

    # 如果表格不够大,那么重新建立表格
    def update_table(table)
      @data[map_id] = new_table = Table.new(width, height)
      table.xsize.times do |x|
        table.ysize.times do |y|
          new_table[x, y] = table[x, y]
        end
      end
      new_table
    end

    # 将所有数据保存到文件
    def save
      save_data(@data, FILE)
    end

    # 获取当前地图信息
    def map_id
      $game_map.map_id
    end

    def width
      $game_map.width
    end

    def height
      $game_map.height
    end
  end
end

class Game_Map
  psg = Taroxd::Passage

  def_chain :passable? do |old, x, y, d|
    case psg[x, y, d]
    when psg::DEFAULT    then old.call(x, y, d)
    when psg::PASSABLE   then true
    when psg::IMPASSABLE then false
    end
  end
end

if $TEST && Taroxd::Passage::EDIT_MODE

  class Game_Player < Game_Character

    # 每帧调用一次 Taroxd::Passage.update
    def_after :update, Taroxd::Passage.method(:update)

    def debug_through?
      true
    end
  end

  class Taroxd::PlanePassage < Plane

    TEXT_RECT = Rect.new(0, 0, 32, 32)
    const_set :VISIBLE, true
    psg = Taroxd::Passage
    include psg

    # 通行度文字的位图缓存
    def text_bitmaps
      @text_bitmap_cache ||= TEXTS.map do |text|
        bitmap = Bitmap.new(TEXT_RECT.width, TEXT_RECT.height)
        bitmap.draw_text(TEXT_RECT, text, 1)
        bitmap
      end
    end

    # 绘制通行度的设置情况
    def_after :draw_point do |x, y|
      bitmap.blt(x * 32, y * 32, text_bitmaps[psg.data[x, y]], TEXT_RECT)
    end

    # 更新通行度的变化
    def_after :update do
      return unless EDIT_TRIGGER.call
      x, y = EDIT_POINT.call
      bitmap.clear_rect(x * 32, y * 32, 32, 32)
      draw_point(x, y)
    end

    def_before :dispose do
      @text_bitmaps_cache.each(&:dispose) if @text_bitmaps
    end
  end
end
多路线系统
# @taroxd metadata 1.0
# @require taroxd_core
# @require global
# @id route
# @display 多路线系统
# @help 
#  游戏中的变量和开关是所有路线共有的。
#  该脚本可以进行路线的切换。
#  游戏开始时,路线 id 为 0。
#
#  进入空路线时,队伍无成员,不持有任何物品。玩家的位置不变。
#  建议用以下方式来初始化一条路线:
#  淡出画面 - route.id = id - 初始化路线 - 淡入画面
#
#  -- 用法 -- 在事件脚本中输入 --
#    route.id:获取当前路线的 id。
#    route.id = id:
#      切换到第 id 号路线,无淡入淡出效果。
#    set_route(id):
#      切换到第 id 号路线,有淡入淡出效果。
#    route << id:将第 id 号路线合并入当前路线,并清除第 id 号路线。
#    route.clear(id):清除第 id 号路线。

# 路线类,保存了路线 id 和数据。该类的实例会存入存档。
class Taroxd::Route

  # 数据
  class Contents

    attr_reader :party, :map_id, :x, :y, :d

    def initialize
      @party = $game_party
      @map_id = $game_map.map_id
      @x = $game_player.x
      @y = $game_player.y
      @d = $game_player.direction
    end

    def restore
      $game_party = @party
      $game_player.reserve_transfer(@map_id, @x, @y, @d)
    end
  end

  def self.current
    Taroxd::Global[:route] ||= new
  end

  attr_reader :id

  def initialize
    @id = 0
    @data = []   # Contents 实例的数组
  end

  def id=(id)
    return if @id == id
    @data[@id] = Contents.new
    @id = id
    contents = @data[id]
    contents ? contents.restore : init_route
    on_change
  end

  # 合并路线
  def <<(id)
    if @id != id && @data[id]
      $game_party.merge_party(@data[id].party)
      clear(id)
      on_change
    end
    self
  end

  def clear(id = nil)
    id ? @data[id] = nil : @data.clear
  end

  def on_change
    $game_player.refresh
    $game_map.need_refresh = true
  end

  private

  # 进入一条新路线时执行的内容
  def init_route
    $game_party = Game_Party.new
  end
end

class Game_Interpreter

  def route
    Taroxd::Route.current
  end

  # 设置路线并淡入淡出
  def set_route(id)
    return if $game_party.in_battle
    command_221           # 淡出画面
    route.id = id
    Fiber.yield while $game_player.transfer?
    command_222           # 淡入画面
  end

end

class Game_Party < Game_Unit

  # 合并金钱、角色、物品
  def merge_party(other)
    gold, actors, items, weapons, armors = other.merge_contents
    gain_gold(gold)
    @actors |= actors
    merge_item @items,   items,   $data_items
    merge_item @weapons, weapons, $data_weapons
    merge_item @armors,  armors,  $data_armors
  end

  protected

  def merge_contents
    [@gold, @actors, @items, @weapons, @armors]
  end

  private

  def merge_item(to, from, database)
    to.merge!(from) do |id, v1, v2|
      [v1 + v2, max_item_number(database[id])].min
    end
  end
end
动态值槽
# @taroxd metadata 1.0
# @display 动态值槽
# @require taroxd_core
# @id roll_gauge
# @help 给值槽增加动态的滚动效果
class Taroxd::Transition

  # value: 当前值。changing: 当前是否正在变化
  attr_reader :value, :changing

  # get_target.call 获取到变化的数据。可以使用 block 代替 get_target。
  def initialize(duration, get_target = nil, &block)
    @duration = duration
    @get_target = get_target || block
    @value = @target = @get_target.call
    @d = 0
  end

  # 更新值槽的值。如果值槽发生变化,返回 true。
  def update
    @target = @get_target.call
    @changing = @value != @target
    update_transition if @changing
    @changing
  end

  private

  def update_transition
    @d = @duration if @d.zero?
    @d -= 1
    @value = if @d.zero?
      @target
    else
      (@value * @d + @target).fdiv(@d + 1)
    end
  end
end

# include 之后,可用 @gauge_transitions[actor][:hp] 等
# 获取 Taroxd::Transition 的实例。
module Taroxd::RollGauge

  Transition = Taroxd::Transition

  def initialize(*)
    @gauge_transitions = make_gauge_transitions
    @gauge_roll_count = 0
    super
  end

  def update
    super
    if (@gauge_roll_count += 1) >= gauge_roll_interval
      roll_all_gauge if update_gauge_transitions && visible
      @gauge_roll_count = 0
    end
  end

  def draw_actor_hp(actor, x, y, width = 124)
    hp = @gauge_transitions[actor][:hp].value
    rate = hp.fdiv(actor.mhp)
    draw_gauge(x, y, width, rate, hp_gauge_color1, hp_gauge_color2)
    change_color(system_color)
    draw_text(x, y, 30, line_height, Vocab::hp_a)
    draw_current_and_max_values(x, y, width, hp.to_i, actor.mhp,
      hp_color(actor), normal_color)
  end

  def draw_actor_mp(actor, x, y, width = 124)
    mp = @gauge_transitions[actor][:mp].value
    mmp = actor.mmp
    rate = mmp.zero? ? 0 : mp.fdiv(actor.mmp)
    draw_gauge(x, y, width, rate, mp_gauge_color1, mp_gauge_color2)
    change_color(system_color)
    draw_text(x, y, 30, line_height, Vocab::mp_a)
    draw_current_and_max_values(x, y, width, mp.to_i, actor.mmp,
      mp_color(actor), normal_color)
  end

  def draw_actor_tp(actor, x, y, width = 124)
    tp = @gauge_transitions[actor][:tp].value
    rate = tp.fdiv(actor.max_tp)
    draw_gauge(x, y, width, rate, tp_gauge_color1, tp_gauge_color2)
    change_color(system_color)
    draw_text(x, y, 30, line_height, Vocab::tp_a)
    change_color(tp_color(actor))
    draw_text(x + width - 42, y, 42, line_height, tp.to_i, 2)
  end

  private

  # 获取 make_gauge_transitions 生成的对象
  attr_reader :gauge_transitions

  # 值槽滚动所需的帧数
  def gauge_roll_frame
    30
  end

  # 每隔多少帧更新一次值槽
  def gauge_roll_interval
    1
  end

  # 生成值槽变化的数据。可在子类重定义。
  # 默认的定义中,可以通过 gauge_transitions[actor][:hp] 等方式获取数据。
  def make_gauge_transitions
    Hash.new { |hash, actor|
      hash[actor] = Hash.new do |h, k|
        h[k] = Transition.new(gauge_roll_times, actor.method(k))
      end
    }.compare_by_identity
  end

  # 更新渐变的值。
  # 返回真值则触发一次刷新。
  # 每 gauge_roll_interval 帧调用一次。
  def update_gauge_transitions
    need_roll = false
    gauge_transitions.each_value do |hash|
      hash.each_value do |t|
        need_roll = true if t.update
      end
    end
    need_roll
  end

  # 值槽滚动所需的次数。
  def gauge_roll_times
    gauge_roll_frame / gauge_roll_interval
  end

  # 滚动所有值槽。可在子类重定义。
  def roll_all_gauge
    refresh
  end
end

class Window_BattleStatus
  include Taroxd::RollGauge
end

class Window_MenuStatus < Window_Selectable

  include Taroxd::RollGauge

  def roll_all_gauge
    item_max.times do |i|
      actor = $game_party.members[i]
      rect = item_rect(i)
      rect.x += 108
      rect.y += line_height / 2
      contents.clear_rect(rect)
      draw_actor_simple_status(actor, rect.x, rect.y)
    end
  end
end