快进对话
# @taroxd metadata 1.0
# @id fast_message
# @display 快进对话
module Taroxd
  module FastMessage
    KEY = :CTRL      # 按此键快进对话
    ENABLED = $TEST  # true: 启用; $TEST: 仅测试模式; false: 不启用
    SPEED = 3        # 文字快进速度。可以为小数,但不能小于 1。

    @counter = 0

    # 是否在输出一个字符后等待。show_fast: 是否快进
    def self.wait?(show_fast)
      !show_fast && !Input.press?(KEY) || (@counter += 1) % SPEED < 1
    end

    # 已经进入等待输入的情况下,是否继续等待输入
    def self.keep_pause?
      [:B, :C, KEY].none? { |k| Input.trigger?(k) }
    end

    # 尚未进入等待输入的情况下,是否跳过等待输入
    def self.skip_pause?
      Input.press?(KEY)
    end
  end
end

class Window_Message < Window_Base

  FastMessage = Taroxd::FastMessage

  def input_pause
    return if FastMessage.skip_pause?
    self.pause = true
    wait(10)
    Fiber.yield while FastMessage.keep_pause?
    Input.update
    self.pause = false
  end

  def wait_for_one_character
    update_show_fast
    Fiber.yield if FastMessage.wait?(@show_fast || @line_show_fast)
  end
end if Taroxd::FastMessage::ENABLED
快进游戏
# @taroxd metadata 1.0
# @id fast_forward
# @display 快进游戏
# @help 
#      跳过 frame 帧。keys 为这 frame 帧按下的按键。
#      每一帧都视为重新按下按键。
#
#    示例:
#      Taroxd::FastForward.call(999, :C)
#        跳过 999 帧。每帧都视为重新按下确定键。

module Taroxd

  dirs = {DOWN: 2, LEFT: 4, RIGHT: 6, UP: 8}

  # 获取 dir4, dir8。
  dir_4_8 = lambda do |keys|
    dir8 = dirs.inject(5) do |dir, (key, value)|
      keys.include?(key) ? dir + value - 5 : dir
    end

    return 0, 0 if dir8 == 5
    return dir8, dir8 if [2, 4, 6, 8].include?(dir8)
    keys.reverse_each { |key| return dirs[key], dir8 if dirs[key] }
    [0, 0] # 理论上不会执行
  end

  FastForward = lambda do |frame, *keys|
    # 保留原方法的哈希表
    # 方法名(Symbol)为键,method 对象为值
    graphics_methods = {}
    input_methods = {}

    # 重定义 Graphics 的方法
    define_graphics_method = lambda do |name, &block|
      graphics_methods[name] = Graphics.method(name)
      Graphics.define_singleton_method(name, block)
    end

    # 重定义 Input 的方法
    define_input_method = lambda do |name, &block|
      input_methods[name] = Input.method(name)
      Input.define_singleton_method(name, block)
    end

    # 将方法恢复到原先的状态
    restore = lambda do
      input_methods.each do |name, method|
        Input.define_singleton_method(name, method)
      end
      graphics_methods.each do |name, method|
        Graphics.define_singleton_method(name, method)
      end
    end

    # 重定义 dir4, dir8 方法。value:方法的返回值
    define_dir_method = lambda do |name, value|
      define_input_method.call(name) { value } unless value == 5
    end

    # 重定义持续一段时间的 Graphics 模块方法。effect:该方法的副作用
    define_duration_method = lambda do |name, &effect|
      define_graphics_method.call name do |*args|
        duration = args.first || 1
        if frame < duration
          restore.call
          send name, *args
        else
          frame -= duration
          effect.call(*args) if effect
          nil
        end
      end
    end

    unless keys.empty?
      [:trigger?, :press?, :repeat?].each do |name|
        define_input_method.call(name) { |key| keys.include?(key) }
      end
    end

    dir4, dir8 = dir_4_8.call(keys)
    define_dir_method.call(:dir4, dir4)
    define_dir_method.call(:dir8, dir8)
    define_graphics_method.call(:freeze) {}
    define_duration_method.call :update
    define_duration_method.call :wait
    define_duration_method.call(:fadeout) { self.brightness = 0 }
    define_duration_method.call(:fadein) { self.brightness = 255 }
    define_duration_method.call :transition do
      graphics_methods[:transition].call(0)
    end
  end
end
额外战斗行动
# @taroxd metadata 1.0
# @id extra_action
# @require taroxd_core
# @display 额外战斗行动
# @help 
#    在战斗公式或事件指令-脚本中输入
#      battler.extra_skill(skill_id, target_index)
#    即可产生一次额外的行动(对应 skill_id 的技能)。
#    target_index 省略时,目标默认为 battler 上次的目标。
#
#    battler.extra_item(item_id, target_index)
#      与 extra_skill 相同。行动内容为对应 item_id 的物品。
#
#    battler.extra_action(skill_id, target_index)
#      与 extra_skill 相同。
#
#    注意,额外的行动也是有消耗的(包括 MP、物品等)
#    当消耗不满足,或者因为其他原因无法行动时,额外行动无效。


class Taroxd::ExtraAction < Game_Action

  # 默认目标。-2: 上次目标, -1: 随机
  DEFAULT_TARGET_INDEX = -2

  class << self

    def new(_, _)
      super.tap { |action| @actions.push action }
    end

    # 获取最后生成的 action 对象并移除这个对象。
    # 如果没有 action,返回 nil。
    def current!
      @actions.pop
    end

    def clear
      @actions = []
    end
  end

  def initialize(subject, target_index)
    super(subject)
    @target_index = target_index
  end

  def make_targets
    @target_index = @subject.last_target_index if @target_index == -2
    super
  end
end

class Game_Battler < Game_BattlerBase
  
  ExtraAction = Taroxd::ExtraAction

  def extra_skill(id, target_index = ExtraAction::DEFAULT_TARGET_INDEX)
    ExtraAction.new(self, target_index).set_skill(id)
  end

  alias_method :extra_action, :extra_skill

  def extra_item(id, target_index = ExtraAction::DEFAULT_TARGET_INDEX)
    ExtraAction.new(self, target_index).set_item(id)
  end
end

class Scene_Battle < Scene_Base

  def_before :battle_start, Taroxd::ExtraAction.method(:clear)

  def_before :process_forced_action do
    action = Taroxd::ExtraAction.current!
    return unless action
    last_subject = @subject
    @subject = action.subject
    @subject.actions.unshift(action)
    process_action
    @subject = last_subject
  end
end