简易调试控制台
# @taroxd metadata 1.0
# @id console
# @require taroxd_core
# @require fast_forward
# @display 简易调试控制台
# 感谢 Fux2 提供的 gets 修复脚本。

if $TEST

module Taroxd::Console

  KEY = :F5
  
  HOOK_STDIN = true   # 使得在新版控制台中也可以输入

  exit_help = 'exit 为真时,返回游戏。'

# @help
  HELP = <<-EOF.gsub(/^ {4}/, '')

    在控制台中可以执行任意脚本。下面是一些预定义的方法。

    exit
      退出控制台并返回游戏。

    help
      显示这段帮助。
      
    _ (变量)
      上一次执行的结果。

    recover(exit = true)
      完全恢复。#{exit_help}

    save(index = 0, exit = true)
      存档到指定位置。#{exit_help}

    load(index = 0, exit = true)
      从指定位置读档。#{exit_help}

    kill(hp = 0, exit = true)
      将敌方全体的 HP 设为 hp。仅战斗中可用。#{exit_help}

    suicide(hp = 0, exit = true)
      将己方全体的 HP 设为 hp。#{exit_help}

    fast_forward(*args)
      调用 Taroxd::FastForward。

    ff(*args)
      调用 Taroxd::FastForward 并返回游戏。

  EOF

  class << self

    EXIT_IDENTIFIER = Object.new   # 返回该值时,退出控制台并回到游戏

    # 获取窗口句柄
    console = Win32API.new('Kernel32', 'GetConsoleWindow', '', 'L').call
    game = Win32API.new('user32', 'GetActiveWindow', '', 'L').call
    hwnd = game
    set_window_pos = Win32API.new('user32', 'SetWindowPos', 'LLLLLLL', 'L')

    # 切换窗口
    define_method :switch_window do
      hwnd = hwnd == game ? console : game
      set_window_pos.call(hwnd, 0, 0, 0, 0, 0, 3)
    end

    # 如果按下按键,则进入控制台
    def update
      start if Input.trigger?(KEY)
    end

    alias_method :get_binding, :binding

    # 进入控制台
    def start
      switch_window
      binding = get_binding
      begin
        while (line = gets)
          next unless line[/\S/]
          _ = eval(line, binding)
          if _.equal?(EXIT_IDENTIFIER)
            switch_window
            Input.update    # 防止按下的 Enter 被游戏判定
            break
          end
          print '=> '
          p _
        end
      rescue => e
        p e
        retry
      end
    end

    def exit
      EXIT_IDENTIFIER
    end

    def help
      puts HELP
    end

    def recover(to_exit = true)
      $game_party.recover_all
      !to_exit || exit
    end

    def save(index = 0, to_exit = true)
      Sound.play_save
      DataManager.save_game_without_rescue(index)
      !to_exit || exit
    end

    def load(index = 0, to_exit = true)
      DataManager.load_game_without_rescue(index)
      Sound.play_load
      $game_system.on_after_load
      SceneManager.goto(Scene_Map)
      !to_exit || exit
    end

    def kill(hp = 0, to_exit = true)
      return to_exit && exit unless $game_party.in_battle
      $game_troop.each { |a| a.hp = hp }
      !to_exit || exit
    end

    def suicide(hp = 0, to_exit = true)
      $game_party.each { |a| a.hp = hp }
      !to_exit || exit
    end

    define_method :fast_forward, Taroxd::FastForward

    def ff(*args)
      fast_forward(*args)
      exit
    end
  end
  
  module ReadFileHooker
    ReadProcessMemory = Win32API.new('kernel32', 'ReadProcessMemory', 'llpll', 'l')
    WriteProcessMemory = Win32API.new('kernel32', 'WriteProcessMemory', 'llpll', 'l')
    VirtualProtect = Win32API.new('kernel32', 'VirtualProtect', 'lllp', 'l')
    GetModuleHandle = Win32API.new('kernel32', 'GetModuleHandle', 'p', 'l')
    GetProcAddress = Win32API.new('kernel32', 'GetProcAddress', 'lp' , 'l')
    GetCurrentProcess = Win32API.new('kernel32', 'GetCurrentProcess', 'v', 'l')

    def self.hook
      hook_addr = CAD - PROC - 5
      writemem(CAD + CAL - 6, @origin_code_readfile, 6)
      writemem(PROC, [0xE9, hook_addr, 0x90].pack("ClC"), 6)
      yield
    ensure
      writemem(PROC, @origin_code_readfile, 6)
    end
    
    class << ::Taroxd::Console
      def gets
        ReadFileHooker.hook { STDIN.gets }
      end
    end

    private

    def self.readmem(addr, buf, len)
      ReadProcessMemory.call(@hProc, addr, buf, len, 0)
    end
 
    def self.writemem(addr, buf, len)
      WriteProcessMemory.call(@hProc, addr, buf, len, 0)
    end
 
    def self.unprotect(addr, len)
      VirtualProtect.call(addr, len, 0x40, "\0" * 4)
    end
 
    def self.getmodule(name)
      GetModuleHandle.call(name)
    end
 
    def self.getaddr(dll, name)
      GetProcAddress.call(dll, name)
    end
 
    @hProc = GetCurrentProcess.call
    raise "cannot open process" if @hProc == 0
    
    HookCode = [0xC7,0x44,0x24,0x0C,0x12,0x05,0x00,0x00,*[0]*6].pack("C*")
    CAD = [HookCode].pack("p").unpack("L").first
    CAL = HookCode.bytes.count
    dll = getmodule("kernel32")
    PROC = getaddr(dll, "ReadFile")
    
    @origin_code_readfile = "\0" * 6
    readmem(PROC, @origin_code_readfile, 6)
    unprotect(CAD, CAL)
  end if HOOK_STDIN
end

Scene_Base.send :def_after, :update, Taroxd::Console.method(:update)

end # if $TEST
全局配置管理器
# @taroxd metadata 1.0
# @display 全局配置管理器
# @id config_manager
module Taroxd
  module ConfigManager
    SAVEFILE_NAME = 'config.rvdata2'

    if File.exist?(SAVEFILE_NAME)
      @data = load_data(SAVEFILE_NAME)
    else
      @data = {}
    end

    def self.[](key)
      @data[key]
    end

    def self.[]=(key, value)
      @data[key] = value
      on_change
    end

    def self.on_change
      save_data @data, SAVEFILE_NAME
    end

    # This method can be used for consecutive value changes,
    # so that the file is written only once.
    # The methods defined in Hash can be used for data manipulation.
    def self.data
      if block_given?
        begin
          yield @data
        ensure
          on_change
        end
      else
        @data
      end
    end
  end
end
颜色扩展
# @taroxd metadata 1.0
# @require taroxd_core
# @id color_ext
# @display 颜色扩展
# @help 
#  扩展了 \C 控制符的功能。大小写不敏感。
#  可用的颜色名称可以在下面长长的列表中找到,当然也可以自定义。
#  自定义时,注意使用小写字母。
#
#  使用范例:
#    \C[1]这是普通的蓝色,\C[Blue]这也是。
#    \C[66CCFF]这是洛天依的蓝色,\C[6CF]这也是。
#    \C[66CCFF7F]这是有半透明效果的洛天依的蓝色。
#
#  Taroxd::ColorExt 模块
#
#    常量:COLORS
#      哈希表,键为小写字母的 symbol,值为对应的颜色。
#      :"66ccff" 这样的键也是可以接受的。
#      :"1" 到 :"31" 之间的符号返回窗口皮肤中的颜色。
#      找不到值时,返回 Color.new
#
#    模块方法:hex_code(code)
#      接受一个字符串为参数,返回对应的颜色。
#      code 可以是 "66ccff" 这样的
#
#    模块方法:text_color,normal_color 等等
#      与 Window_Base 的对应方法相同。
#      可以让 Sprite 类包含这个模块,使这些方法在 Sprite 类中也可用。


module Taroxd::ColorExt
  COLORS = {
    aliceblue:            Color.new(240, 248, 255),
    antiquewhite:         Color.new(250, 235, 215),
    aqua:                 Color.new(0, 255, 255),
    aquamarine:           Color.new(127, 255, 212),
    azure:                Color.new(240, 255, 255),
    beige:                Color.new(245, 245, 220),
    bisque:               Color.new(255, 228, 196),
    black:                Color.new(0, 0, 0),
    blanchedalmond:       Color.new(255, 235, 205),
    blue:                 Color.new(0, 0, 255),
    blueviolet:           Color.new(138, 43, 226),
    brown:                Color.new(165, 42, 42),
    burlywood:            Color.new(222, 184, 135),
    cadetblue:            Color.new(95, 158, 160),
    chartreuse:           Color.new(127, 255, 0),
    chocolate:            Color.new(210, 105, 30),
    coral:                Color.new(255, 127, 80),
    cornflowerblue:       Color.new(100, 149, 237),
    cornsilk:             Color.new(255, 248, 220),
    crimson:              Color.new(220, 20, 60),
    cyan:                 Color.new(0, 255, 255),
    darkblue:             Color.new(0, 0, 139),
    darkcyan:             Color.new(0, 139, 139),
    darkgoldenrod:        Color.new(184, 134, 11),
    darkgray:             Color.new(169, 169, 169),
    darkgreen:            Color.new(0, 100, 0),
    darkkhaki:            Color.new(189, 183, 107),
    darkmagenta:          Color.new(139, 0, 139),
    darkolivegreen:       Color.new(85, 107, 47),
    darkorange:           Color.new(255, 140, 0),
    darkorchid:           Color.new(153, 50, 204),
    darkred:              Color.new(139, 0, 0),
    darksalmon:           Color.new(233, 150, 122),
    darkseagreen:         Color.new(143, 188, 143),
    darkslateblue:        Color.new(72, 61, 139),
    darkslategray:        Color.new(47, 79, 79),
    darkturquoise:        Color.new(0, 206, 209),
    darkviolet:           Color.new(148, 0, 211),
    deeppink:             Color.new(255, 20, 147),
    deepskyblue:          Color.new(0, 191, 255),
    dimgray:              Color.new(105, 105, 105),
    dodgerblue:           Color.new(30, 144, 255),
    firebrick:            Color.new(178, 34, 34),
    floralwhite:          Color.new(255, 250, 240),
    forestgreen:          Color.new(34, 139, 34),
    fuchsia:              Color.new(255, 0, 255),
    gainsboro:            Color.new(220, 220, 220),
    ghostwhite:           Color.new(248, 248, 255),
    gold:                 Color.new(255, 215, 0),
    goldenrod:            Color.new(218, 165, 32),
    gray:                 Color.new(128, 128, 128),
    green:                Color.new(0, 128, 0),
    greenyellow:          Color.new(173, 255, 47),
    honeydew:             Color.new(240, 255, 240),
    hotpink:              Color.new(255, 105, 180),
    indianred:            Color.new(205, 92, 92),
    indigo:               Color.new(75, 0, 130),
    ivory:                Color.new(255, 255, 240),
    khaki:                Color.new(240, 230, 140),
    lavender:             Color.new(230, 230, 250),
    lavenderblush:        Color.new(255, 240, 245),
    lawngreen:            Color.new(124, 252, 0),
    lemonchiffon:         Color.new(255, 250, 205),
    lightblue:            Color.new(173, 216, 230),
    lightcoral:           Color.new(240, 128, 128),
    lightcyan:            Color.new(224, 255, 255),
    lightgoldenrodyellow: Color.new(250, 250, 210),
    lightgreen:           Color.new(144, 238, 144),
    lightgrey:            Color.new(211, 211, 211),
    lightpink:            Color.new(255, 182, 193),
    lightsalmon:          Color.new(255, 160, 122),
    lightseagreen:        Color.new(32, 178, 170),
    lightskyblue:         Color.new(135, 206, 250),
    lightslategray:       Color.new(119, 136, 153),
    lightsteelblue:       Color.new(176, 196, 222),
    lightyellow:          Color.new(255, 255, 224),
    lime:                 Color.new(0, 255, 0),
    limegreen:            Color.new(50, 205, 50),
    linen:                Color.new(250, 240, 230),
    magenta:              Color.new(255, 0, 255),
    maroon:               Color.new(128, 0, 0),
    mediumaquamarine:     Color.new(102, 205, 170),
    mediumblue:           Color.new(0, 0, 205),
    mediumorchid:         Color.new(186, 85, 211),
    mediumpurple:         Color.new(147, 112, 219),
    mediumseagreen:       Color.new(60, 179, 113),
    mediumslateblue:      Color.new(123, 104, 238),
    mediumspringgreen:    Color.new(0, 250, 154),
    mediumturquoise:      Color.new(72, 209, 204),
    mediumvioletred:      Color.new(199, 21, 133),
    midnightblue:         Color.new(25, 25, 112),
    mintcream:            Color.new(245, 255, 250),
    mistyrose:            Color.new(255, 228, 225),
    moccasin:             Color.new(255, 228, 181),
    navajowhite:          Color.new(255, 222, 173),
    navy:                 Color.new(0, 0, 128),
    oldlace:              Color.new(253, 245, 230),
    olive:                Color.new(128, 128, 0),
    olivedrab:            Color.new(107, 142, 35),
    orange:               Color.new(255, 165, 0),
    orangered:            Color.new(255, 69, 0),
    orchid:               Color.new(218, 112, 214),
    palegoldenrod:        Color.new(238, 232, 170),
    palegreen:            Color.new(152, 251, 152),
    paleturquoise:        Color.new(175, 238, 238),
    palevioletred:        Color.new(219, 112, 147),
    papayawhip:           Color.new(255, 239, 213),
    peachpuff:            Color.new(255, 218, 185),
    peru:                 Color.new(205, 133, 63),
    pink:                 Color.new(255, 192, 203),
    plum:                 Color.new(221, 160, 221),
    powderblue:           Color.new(176, 224, 230),
    purple:               Color.new(128, 0, 128),
    red:                  Color.new(255, 0, 0),
    rosybrown:            Color.new(188, 143, 143),
    royalblue:            Color.new(65, 105, 225),
    saddlebrown:          Color.new(139, 69, 19),
    salmon:               Color.new(250, 128, 114),
    sandybrown:           Color.new(244, 164, 96),
    seagreen:             Color.new(46, 139, 87),
    seashell:             Color.new(255, 245, 238),
    sienna:               Color.new(160, 82, 45),
    silver:               Color.new(192, 192, 192),
    skyblue:              Color.new(135, 206, 235),
    slateblue:            Color.new(106, 90, 205),
    slategray:            Color.new(112, 128, 144),
    snow:                 Color.new(255, 250, 250),
    springgreen:          Color.new(0, 255, 127),
    steelblue:            Color.new(70, 130, 180),
    tan:                  Color.new(210, 180, 140),
    teal:                 Color.new(0, 128, 128),
    thistle:              Color.new(216, 191, 216),
    tomato:               Color.new(255, 99, 71),
    turquoise:            Color.new(64, 224, 208),
    violet:               Color.new(238, 130, 238),
    wheat:                Color.new(245, 222, 179),
    white:                Color.new(255, 255, 255),
    whitesmoke:           Color.new(245, 245, 245),
    yellow:               Color.new(255, 255, 0),
    yellowgreen:          Color.new(154, 205, 50),
  }
  COLORS.default_proc = -> h, k { h[k] = hex_code(k.to_s) || Color.new }

  module_function

  # 用颜色代码获取颜色
  def hex_code(code)
    case code.size
    when 3, 4
      Color.new(*code.each_char.map { |c| c.hex * 0x11 })
    when 6, 8
      Color.new(*code.scan(/../).map(&:hex))
    end
  end

  def windowskin
    Cache.system("Window")
  end

  def text_color(n)
    windowskin.get_pixel(64 + (n % 8) * 8, 96 + (n / 8) * 8)
  end

  def normal_color;      text_color(0);   end;
  def system_color;      text_color(16);  end;
  def crisis_color;      text_color(17);  end;
  def knockout_color;    text_color(18);  end;
  def gauge_back_color;  text_color(19);  end;
  def hp_gauge_color1;   text_color(20);  end;
  def hp_gauge_color2;   text_color(21);  end;
  def mp_gauge_color1;   text_color(22);  end;
  def mp_gauge_color2;   text_color(23);  end;
  def mp_cost_color;     text_color(23);  end;
  def power_up_color;    text_color(24);  end;
  def power_down_color;  text_color(25);  end;
  def tp_gauge_color1;   text_color(28);  end;
  def tp_gauge_color2;   text_color(29);  end;
  def tp_cost_color;     text_color(29);  end;
  32.times { |i| COLORS[:"#{i}"] = text_color(i) }
end

class Window_Base
  def_chain :process_escape_character do |old, code, text, pos|
    if code.casecmp('C').zero?
      sym = text.slice!(/^\[\w+]/i)[1..-2].downcase.to_sym
      change_color Taroxd::ColorExt::COLORS[sym]
    else
      old.call(code, text, pos)
    end
  end
end