地上の洞窟

どこにも行かず、液晶と「にらめっこ」し続ける人の物語。

【RGSS3】CraftLib

「地上の洞窟」が制作した一部のスクリプトの動作に必要な「前提スクリプトです。
(ちなみに機能だけ増やして、機能を使うスクリプトが完成してなかったりする)


更新履歴 バージョン 内容
2023/09/18 v1.0.0 初版

→スクリプト一覧へ


スクリプト(ダブルクリックで全選択)↓

=begin =========================================================================
 ■ 「CraftLib」v1.0.0 by 地上の洞窟
================================================================================
  
  「地上の洞窟」が制作した、一部のスクリプトの動作に必要な前提ライブラリです。

================================================================================
 ■ 設定項目
=end #==========================================================================

module CraftLib

  # 既存のスクリプトに手を加えている部分で、各種機能の無効化ができます。
  # 機能が不要な場合や競合が発生した際お使いください。
  # true → 有効 | false → 無効

  # AUDIO_VOLUME_CONTROL : 音量変更機能
  AUDIO_VOLUME_CONTROL = true


  # SETUP_COMMENT : 注釈セットアップ(イベント)
  SETUP_COMMNENT = true


  # WINDOW_BASE_EXTEND : Window_Base拡張
  WINDOW_BASE_EXTEND = true

#===============================================================================
# ■ ここからソースコード
#===============================================================================

  #////////////////////////////////////////////////////////////////////////////
  # ◆ 拡張Class
  #////////////////////////////////////////////////////////////////////////////
  
  #============================================================================
  # ■ RIFile - INIファイルの読み書き
  #============================================================================

  class RIFile
    TEMP_NAME = "_initemp"
    OPEN_MODE = "r"
    WRITE_MODE = "w"
    BASE_ENCODING = "UTF-8"
    SUB_ENCODING = "Shift_JIS"
    SECTION = /\[(.*)\]/
    NAME_VALUE = /(\S*)\s*=\s*(\S*)/
    
    attr_reader :index
      
    def self.open(filename)
      rifile = self.allocate
      rifile.setup(filename, true)
      return rifile
    end

    def self.peek(filename, section, name)
      return unless File.exist?(filename)
      RIFile.new(filename)[section][name]
    end
    
    def initialize(filename)
      setup(filename)
    end
    
    def setup(filename, open_mode = false)
      @filename = filename
      @index = {}
      make_missing_file unless open_mode
      load_index
    end
    
    def clear
      @index.clear
    end
    
    def make_missing_file
      open(@filename, WRITE_MODE) unless File.exist?(@filename)
    end
    
    def load_index
      # 設定ファイルを行ごとに処理
      section = ''
      open(@filename, OPEN_MODE) do |file|
        text = file.read
        begin
          text.each_line do |line|
            if line[SECTION]
              section = $1
            elsif line[NAME_VALUE]
              (@index[section] ||= {})[$1] = $2
            end
          end
        rescue
          text.encode!(BASE_ENCODING, SUB_ENCODING)
          retry
        end
      end
    end
    
    def [](section)
      @index[section] ||= {}
    end
    
    def save
      begin
        # 書き出し
        open(TEMP_NAME, WRITE_MODE) do |file|
          @index.each do |section, keys|
            file.puts "[#{section}]"
            keys.each do |key, param|
              file.puts "#{key} = #{param}"
            end
            file.puts
          end
        end

        # 旧ファイル削除、新ファイル改名
        File.delete(@filename)
        File.rename(TEMP_NAME, @filename)
        true
      rescue
        File.delete(TEMP_NAME)
        false
      end
    end
  end

  #////////////////////////////////////////////////////////////////////////////
  # ◆ 拡張Module
  #////////////////////////////////////////////////////////////////////////////

  #============================================================================
  # ■ RAPIC - Rgss API by Craft | 拡張DLLを通した処理
  #============================================================================
  
  module RAPIC
    # FIND_WINDOW
    FIND_WINDOW = Win32API.new('user32', 'FindWindow', %w(p p), 'l')
    REG_NAME = "RGSS Player"
    WINDOW_NAME = RIFile.peek("./Game.ini", "Game", "Title")

    # RECT系
    GET_WINDOW_RECT = Win32API.new('user32', 'GetWindowRect', %w(l p), 'l')
    GET_CLIENT_RECT = Win32API.new('user32', 'GetClientRect', %w(l p), 'l')
    SYSTEM_PARAMETERS_INFO = Win32API.new('user32', 'SystemParametersInfo', %w(l l p l), 'l')
    RECT_PACK_STR = "i4"
    RECT_BUFFER = [0, 0, 0, 0].pack(RECT_PACK_STR)

    LEFT = X = 0
    TOP = Y = 1
    RIGHT = WIDTH = 2
    BOTTOM = HEIGHT = 3
    
    # MOVE_WINDOW
    MOVE_WINDOW = Win32API.new('user32', 'MoveWindow', %w(l i i i i l), 'l')

    # Mouse
    # GET_CURSOR_POS
    GET_CURSOR_POS = Win32API.new('user32', 'GetCursorPos', 'p', 'i')
    MOUSE_BUFFER = "\0" * 8
    MOUSE_BUFFER_PACK_STR = "l!l!"

    # SCREEN_TO_CLIENT
    SCREEN_TO_CLIENT = Win32API.new('user32', 'ScreenToClient', %w(l p), 'i')

    # Keyboard
    # GET_KEYBOARD_STATE
    GET_KEYBOARD_STATE = Win32API.new('user32', 'GetKeyboardState', 'p', 'i')
    KEYBOARD_BUFFER = "\0" * 256

    # GET_ASYNC_KEY_STATE
    GET_ASYNC_KEY_STATE = Win32API.new('user32', 'GetAsyncKeyState', 'i', 'i')

    # TO_UNICODE
    TO_UNICODE = Win32API.new('user32', 'ToUnicode', %w(l l p p i l), 'i')

    # IMM
    # IMM_GET_CONTEXT
    IMM_GET_CONTEXT = Win32API.new('Imm32', 'ImmGetContext', 'l', 'l')

    # IMM_RELEASE_CONTEXT
    IMM_RELEASE_CONTEXT = Win32API.new('Imm32', 'ImmReleaseContext', %w(l l), 'i')

    # IMM_SET_COMPOSITION_WINDOW
    IMM_SET_COMPOSITION_WINDOW = Win32API.new('Imm32', 'ImmSetCompositionWindow', %w(l p), 'i')
    COMPOSITIONFORM_PACK_STR = "Il!6"
    CFS_DEFAULT = 0x0000
    CFS_FORCE_POSITION = 0x0020
    CFS_POINT = 0x0002
    CFS_RECT = 0x0001

    # IMM_GET_OPEN_STATUS
    IMM_GET_OPEN_STATUS = Win32API.new('Imm32', 'ImmGetOpenStatus', 'l', 'l')
    
    # IMM_GET_COMPOSITION_STRING
    IMM_GET_COMPOSITION_STRING = Win32API.new('Imm32', 'ImmGetCompositionString', %w(l l p l), 'i')

    class << self
      def game_window_handle
        @game_window_handle ||= (
          if (handle = FIND_WINDOW.call(REG_NAME, WINDOW_NAME)) == 0
            handle = FIND_WINDOW.call(REG_NAME, nil)
          end
          handle
        )
      end

      def game_window_rect
        buffer = "".replace(RECT_BUFFER)
        if GET_WINDOW_RECT.call(game_window_handle, buffer) != 0
          rect = buffer.unpack(RECT_PACK_STR) # 左x, 上y, 右x, 下y
          rect[WIDTH] = rect[RIGHT] - rect[LEFT] # 幅
          rect[HEIGHT] = rect[BOTTOM] - rect[TOP] # 高さ
          return rect
        else
          return [0, 0, 0, 0]
        end
      end

      def game_client_rect
        buffer = "".replace(RECT_BUFFER)
        if GET_CLIENT_RECT.call(game_window_handle, buffer) != 0
          rect = buffer.unpack(RECT_PACK_STR) # 左x, 上y, 右x, 下y
          rect[WIDTH] = rect[RIGHT] - rect[LEFT] # 幅
          rect[HEIGHT] = rect[BOTTOM] - rect[TOP] # 高さ
          return rect
        else
          return [0, 0, 0, 0]
        end
      end

      def system_workarea_rect
        buffer = "".replace(RECT_BUFFER)
        # SPI_GETWORKAREA: 0x0030
        if SYSTEM_PARAMETERS_INFO.call(0x0030, 0, buffer, 0) != 0
          rect = buffer.unpack(RECT_PACK_STR) # 左x, 上y, 右x, 下y
          rect[WIDTH] = rect[RIGHT] - rect[LEFT] # 幅
          rect[HEIGHT] = rect[BOTTOM] - rect[TOP] # 高さ
          return rect
        else
          return [0, 0, 0, 0]
        end
      end

      def move_window(x, y, width, height)
        MOVE_WINDOW.call(game_window_handle, x, y, width, height, 1)
      end

      def get_mouse_position
        buffer = "".replace(MOUSE_BUFFER)
        GET_CURSOR_POS.call(buffer)
        SCREEN_TO_CLIENT.call(game_window_handle, buffer)
        result = buffer.unpack(MOUSE_BUFFER_PACK_STR)
        client_rect = game_client_rect
        w_rate = (w = Graphics.width.to_f) / client_rect[WIDTH]
        h_rate = (h = Graphics.height.to_f) / client_rect[HEIGHT]
        result[0] = (result[0] * w_rate).round
        result[1] = (result[1] * h_rate).round
        result
      end

      def get_keyboard_state(buffer)
        GET_KEYBOARD_STATE.call(buffer)
        buffer
      end

      def get_key_repeat?(id)
        GET_ASYNC_KEY_STATE.call(id)[0] == 1
      end

      def key_to_unicode(id, buffer, keyboard_state)
        return TO_UNICODE.call(id, 0, keyboard_state, buffer, buffer.size, 0)
      end

      def using_imm_context
        if block_given?
          yield himc = IMM_GET_CONTEXT.call(hwnd = game_window_handle)
          IMM_RELEASE_CONTEXT.call(hwnd, himc)
        end
      end

      def imm_set_window(style, *args)
        compostion_form = [style, *args]
        i = 1
        while i < 7
          compostion_form[i] ||= 0
          i += 1
        end
        using_imm_context do |himc|
          IMM_SET_COMPOSITION_WINDOW.call(
            himc,
            compostion_form.pack(COMPOSITIONFORM_PACK_STR)
          )
        end
      end

      def imm_get_string
        buffer = nil
        using_imm_context do |himc|
          length = IMM_GET_COMPOSITION_STRING.call(himc, 0x0008, nil, 0)
          buffer = "\0" * length
          IMM_GET_COMPOSITION_STRING.call(himc, 0x0008, buffer, length)
        end
        buffer
      end

      def imm_get_result_string
        buffer = nil
        using_imm_context do |himc|
          length = IMM_GET_COMPOSITION_STRING.call(himc, 0x0800, nil, 0)
          buffer = "\0" * length
          IMM_GET_COMPOSITION_STRING.call(himc, 0x0800, buffer, length)
        end
        buffer
      end

      def imm_open?
        result = nil
        using_imm_context { |himc| result = IMM_GET_OPEN_STATUS.call(himc) }
        result != 0
      end
    end
  end

  #============================================================================
  # ■ ResizeWindow - ゲームウィンドウの擬似的リサイズ
  #============================================================================

  module ResizeWindow
    @rate = 1.0

    class << self
      include CraftLib::RAPIC
      def rate
        @rate
      end
      
      def rate=(value)
        @rate = value
        resize
      end

      def percentage
        (@rate * 100).round
      end

      def percentage=(value)
        self.rate = value / 100.0
      end

      def max_rate
        window_rect = RAPIC.game_window_rect
        client_rect = RAPIC.game_client_rect
        workarea_rect = RAPIC.system_workarea_rect

        margin_width  = window_rect[WIDTH] - client_rect[WIDTH]
        margin_height = window_rect[HEIGHT] - client_rect[HEIGHT]
        
        w = Graphics.width.to_f
        h = Graphics.height.to_f
        
        base_rx = workarea_rect[WIDTH] / w
        base_ry = workarea_rect[HEIGHT] / h

        div_x = (w * base_rx + margin_width) / (w * base_rx)
        div_y = (h * base_ry + margin_height) / (h * base_ry)

        rx = base_rx / div_x
        ry = base_ry / div_y
        rx > ry ? ry : rx
      end


      def resize
        window_rect = RAPIC.game_window_rect
        client_rect = RAPIC.game_client_rect

        margin_width  = window_rect[WIDTH] - client_rect[WIDTH]
        margin_height = window_rect[HEIGHT] - client_rect[HEIGHT]
        
        rate = @rate > (max = max_rate) ? max : @rate

        width  = Graphics.width  * rate + margin_width
        height = Graphics.height * rate + margin_height

        x = window_rect[X] + (window_rect[WIDTH] / 2.0).round - (width / 2.0).round
        y = window_rect[Y] + (window_rect[HEIGHT] / 2.0).round - (height / 2.0).round
        RAPIC.move_window(x, y, width, height)
      end
    end
  end

  #============================================================================
  # ■ AInput - 改良Input
  #============================================================================

  module AInput
    REPEAT_DELAY = 20
    REPEAT_RATE = 6

    TEXT_REPEAT_DELAY = 26
    TEXT_REPEAT_RATE = 2

    REGEXP_STD_TEXT = /\P{Other}/

    @log = "".replace(RAPIC::KEYBOARD_BUFFER)
    @log_old = "".replace(RAPIC::KEYBOARD_BUFFER)
    @trigger = {}
    @repeat = {}
    @last_trigger = nil
    @last_repeat = nil
    @repeat_count = 0
    @repeat_delayed = false

    # Mouse
    @mouse_left_last = nil
    @mouse_right_last = nil
    @mouse_middle_last = nil

    # Text
    @text_key = []
    @text = []
    @text_input = ""
    @text_complete = ""
    @text_repeat = {}
    @text_repeat_count = 0
    @text_repeat_delayed = false
    @text_last_repeat = nil
    @text_cursor = 0
    @text_line = 0

    class << self
      def update
        @log_old.replace(@log)
        RAPIC.get_keyboard_state(@log)
        @repeat.delete_if { |id| @log.getbyte(id)[7] == 0 }
        @trigger.delete_if { |id| @log.getbyte(id)[7] == 0 }
        @last_repeat = nil if @log != @log_old
      end

      def press?(id)
        @log.getbyte(id)[7] == 1
      end

      def trigger?(id)
        @trigger[id] = true if !@trigger[id] && @log.getbyte(id)[7] == 1
      end

      def repeat?(id)
        if @last_repeat == id
          if @log.getbyte(id)[7] == 1
            if @repeat_delayed
              result = (@repeat_count % REPEAT_RATE) == 0
              @repeat_count += 1
              return result
            elsif @repeat_count > REPEAT_DELAY
              @repeat_count = 0
              @repeat_delayed = true
              return false
            else
              @repeat_count += 1
              return false
            end
          else
            @last_repeat = nil
            @repeat.delete(id)
            @repeat_count = 0
            @repeat_delayed = false
            return false
          end
        elsif !@repeat[id] && @log.getbyte(id)[7] == 1
          @last_repeat = id
          @repeat[id] = true
          @repeat_count = 0
          @repeat_delayed = false
          return true
        end
      end

      # Mouse

      def update_mouse
        @mouse_left_last = nil unless left_hold?
        @mouse_right_last = nil unless right_hold?
        @mouse_middle_last = nil unless middle_hold?
      end

      def mouse_pos
        RAPIC.get_mouse_position
      end

      def left_hold?
        @log.getbyte(1)[7] == 1
      end

      def right_hold?
        @log.getbyte(2)[7] == 1
      end

      def middle_hold?
        @log.getbyte(4)[7] == 1
      end

      def left_click?
        if !@mouse_left_last && @log.getbyte(1)[7] == 1 
          @mouse_left_last = true
        end
      end

      def right_click?
        if !@mouse_right_last && @log.getbyte(2)[7] == 1
          @mouse_right_last = true
        end
      end

      def middle_click?
        if !@mouse_middle_last && @log.getbyte(4)[7] == 1
          @mouse_middle_last = true
        end
      end

      # Text

      def result_text
        @text
      end

      def input_text
        @text_input
      end

      def text_current_line
        (@text[@text_line] ||= "")
      end

      def text_cursor_last?
        @text_cursor == text_current_line.size
      end

      def text_insert(str)
        if text_cursor_last?
          text_current_line << str
        else
          text_current_line.insert(@text_cursor, str)
        end
        @text_cursor += str.size
      end

      def text_end
        @text_cursor = text_current_line.size
      end

      def text_home
        @text_cursor = 0
      end

      def text_cursor_left
        if @text_cursor > 0
          @text_cursor -= 1
        elsif @text_line > 0
          @text_line -= 1
          @text_cursor = text_current_line.size
        end
      end

      def text_cursor_right
        if @text_cursor < text_current_line.size
          @text_cursor += 1
        elsif @text_line < @text.size - 1
          @text_line += 1
          @text_cursor = 0
        end
      end

      def text_cursor_up
        if @text_line > 0
          @text_line -= 1
          @text_cursor > (size = text_current_line.size) and @text_cursor = size
        end
      end

      def text_cursor_down
        if @text_line < @text.size - 1
          @text_line += 1
          @text_cursor > (size = text_current_line.size) and @text_cursor = size
        end
      end

      def text_backspace
        current = text_current_line
        if text_cursor_last? && current.size > 0
          current.chop!
          text_cursor_left
        elsif @text_cursor >= 1
          current.slice!(@text_cursor -= 1)
        elsif @text_line > 0
          text = @text.delete_at(@text_line)
          @text_line -= 1
          @text_cursor = (current = text_current_line).size
          current << text
        end
      end

      def text_delete
        if ((current = text_current_line).empty? || text_cursor_last?)
          current << @text.delete_at(@text_line + 1) if @text_line < @text.size - 1 
        else
          current.slice!(@text_cursor)
        end
      end

      def text_enter
        if text_cursor_last?
          @text.insert(@text_line += 1, "")
          @text_cursor = 0
        else
          current = text_current_line
          text = current.slice!(@text_cursor, current.size - @text_cursor)
          @text.insert(@text_line += 1, text)
          @text_cursor = 0
        end
      end

      def clear_text
        @text.clear
        @text_input.clear
        @text_complete.clear
        @text_cursor = 0
        @text_line = 0
      end

      def update_text
        if RAPIC.imm_open?
          update_text_key
          update_text_control
          update_text_ime
        else
          update_text_non_ime
          update_text_control
        end        
      end

      def update_text_key
        id = 0
        while id < 256
          @text_key[id] = RAPIC.get_key_repeat?(id)
          id += 1
        end
      end
      
      def update_text_ime
        if (complete_text = RAPIC.imm_get_result_string) != @text_complete
          text_insert((@text_complete = complete_text).encode('UTF-8', 'Shift_JIS'))
        end
        @text_input = RAPIC.imm_get_string
      end

      def update_text_non_ime
        id = 0
        while id < 256
          if (@text_key[id] = RAPIC.get_key_repeat?(id))
            buffer = "\0"
            if RAPIC.key_to_unicode(id, buffer, @log) > 0 && buffer[REGEXP_STD_TEXT]
              text_insert(buffer)
            end
          end
          id += 1
        end
      end
      
      def update_text_control
        return unless @text_input.empty?
        text_enter        if @text_key[13] # Enter
        text_backspace    if @text_key[ 8] # Backspace
        text_delete       if @text_key[46] # Delete
        text_end          if @text_key[35] # End
        text_home         if @text_key[36] # Home
        text_cursor_left  if @text_key[37] # ←
        text_cursor_up    if @text_key[38] # ↑
        text_cursor_right if @text_key[39] # →
        text_cursor_down  if @text_key[40] # ↓
      end
    end
  end

  #////////////////////////////////////////////////////////////////////////////
  # ◆ 「RGSS3」拡張Class
  #////////////////////////////////////////////////////////////////////////////

  #============================================================================
  # ■ Fake_Window_Base - 描画を行わず特定の処理のみ実行
  #============================================================================

  class Fake_Window_Base < Window_Base
    EMPTY_PROC = Proc.new { |*args| }
    FILTER = /draw_|update_/

    def initialize
      super(0, 0, 0, 0)
      self.visible = false
    end
    
    # 描画系メソッドの無効化
    self.instance_methods.each do |method_name|
      if method_name[FILTER]
        define_method(method_name, EMPTY_PROC)
      end
    end

    def calc_line_width_process(text)
      reset_font_settings
      text = convert_escape_characters(text)
      pos = {:x => 0, :y => 0, :new_x => 0, :height => calc_line_height(text)}
      until text.empty?
        case (char = text.slice!(0, 1))
        when "\e"
          process_escape_character(obtain_escape_code(text), text, pos)
        else
          pos[:x] += text_size(char).width
        end
      end
      pos[:x]
    end
  end
  FAKE_WINDOW_BASE = Fake_Window_Base.new

  #============================================================================
  # ■ Window_DialogCommand - 汎用はい/いいえコマンド
  #============================================================================

  class Window_DialogCommand < Window_Command
    TEXT_YES = "はい"
    TEXT_NO  = "いいえ"

    def initialize(text)
      @text = text
      super(0, 0)
      deactivate
      self.width = calc_line_width(@text.max) + standard_padding * 2
      self.x = Graphics.width  / 2 - self.width  / 2
      self.y = Graphics.height / 2 - self.height / 2
      self.back_opacity = 232 if back_opacity < 232
      self.openness = 0
      refresh
      select(1)
    end
    
    def window_height
      fitting_height(@text.size + visible_line_number)
    end
    
    def alignment
      return 1
    end
    
    def item_rect(index)
      rect = super(index)
      rect.y += line_height * @text.size
      rect
    end
    
    def make_command_list
      add_command(TEXT_YES, :ok)
      add_command(TEXT_NO, :cancel)
    end
    
    def process_ok
      if current_item_enabled?
        current_symbol == :cancel ? Sound.play_cancel : Sound.play_ok
        Input.update
        call_ok_handler
      else
        Sound.play_buzzer
      end
    end
    
    def refresh
      super
      @text.each_with_index do |str, line|
        draw_text_ex(0, line * line_height, str)
      end
    end
  end

  #============================================================================
  # ■ Window_DialogCommand - 汎用ダイアログ
  #============================================================================

  class Window_DialogText < Window_Base
    def initialize(text, width = nil, height = nil)
      width ||= calc_line_width(text.max) + standard_padding * 2
      height ||= line_height * text.size + standard_padding * 2
      super(0, 0, width, height)
      self.back_opacity = 232 if back_opacity < 232
      self.openness = 0
      @text = text
      @handler = {}
      refresh
    end
    
    def refresh
      contents.clear
      oy = contents.height / 2 - (line_height * @text.size) / 2
      @text.each_with_index do |str, line|
        draw_text_ex(
          contents.width / 2 - calc_line_width(str) / 2,
          oy + (line * line_height),
          str
        )
      end
    end
    
    def update
      super
      process_dialog_control
    end
    
    def process_dialog_control
      return unless open? && active
      return on_ok if Input.trigger?(:C) || Input.trigger?(:B)
    end
    
    def on_ok
      call_handler(:ok)
    end
    
    def set_handler(symbol, handle)
      @handler[symbol] = handle
    end
    
    def handle?(symbol)
      @handler.include?(symbol)
    end
    
    def call_handler(symbol)
      @handler[symbol].call if handle?(symbol)
    end
  end

  #==========================================================================
  # ■ WindowSet_Parallel - ウィンドウ並列配置の基本クラス
  #==========================================================================

  class WindowSet_Parallel
    def initialize(window_class, size)
      @window_class = window_class
      @size = size
      create_all_windows
    end
    
    def create_all_windows
      @windows = Array.new(@size) do |i|
        create_window(i)
      end
    end
    
    def create_window(i)
      @window_class.new(i)
    end
    
    def [](index)
      @windows[index]
    end
    
    def viewport=(viewport)
      @viewport = viewport
      @windows.each { |w| w.viewport = @viewport }
    end
    
    def update
      @windows.each { |w| w.update }
    end
    
    def dispose
      @windows.each { |w| w.dispose }
    end
  end
  
  #==========================================================================
  # ■ WindowSet_ParallelSelectable - 並列配置したウィンドウをコマンドとして扱う
  #==========================================================================

  class WindowSet_ParallelSelectable < WindowSet_Parallel
    attr_reader :index
    attr_reader :active
    
    def initialize(window_class, size)
      super(window_class, size)
      @index = 0
      @active = false
      @handler = {}
    end
    
    def set_handler(symbol, method)
      @handler[symbol] = method
    end
    
    def handle? (symbol)
      @handler.include?(symbol)
    end
    
    def call_handler(symbol)
      @handler[symbol].call if handle?(symbol)
    end
    
    def update
      super
      if @active
        update_selection
        update_cursor
      end
    end
    
    def update_selection
      return call_handler(:ok)     if Input.trigger?(:C)
      return call_handler(:cancel) if Input.trigger?(:B)
    end
    
    def update_cursor
      return false if @size <= 0
      last_index = @index
      cursor_down (Input.trigger?(:DOWN))   if Input.repeat?(:DOWN)
      cursor_up   (Input.trigger?(:UP))     if Input.repeat?(:UP)
      cursor_pagedown (Input.trigger?(:R))  if Input.repeat?(:R)
      cursor_pageup   (Input.trigger?(:L))  if Input.repeat?(:L)
      if @index != last_index
        action_on_select
        @windows[last_index].selected = false
        @windows[@index].selected = true
      end
    end
    
    def window_height
      @windows[0].height
    end
    
    def visible_max
      @viewport.rect.height / window_height
    end
    
    def top_index
      @viewport.oy / window_height
    end
    
    def top_index=(index)
      index = 0 if index < 0
      index = @size - visible_max if index > @size - visible_max
      @viewport.oy = index * window_height
    end
    
    def bottom_index
      top_index + visible_max - 1
    end
    
    def bottom_index=(index)
      self.top_index = index - (visible_max - 1)
    end
    
    def cursor_down(wrap)
      @index = (@index + 1) % @size if @index < @size - 1 || wrap
      ensure_cursor_visible
    end
    
    def cursor_up(wrap)
      @index = (@index - 1 + @size) % @size if @index > 0 || wrap
      ensure_cursor_visible
    end
    
    def cursor_pagedown(wrap)
      if top_index + visible_max < @size
        self.top_index += visible_max
        @index += visible_max
        (m = @size - 1) < @index and @index = m
      elsif @index < @size - 1
        @index = @size - 1
      elsif wrap
        @index = 0
        ensure_cursor_visible
      end
    end
    
    def cursor_pageup(wrap)
      if top_index > 0
        self.top_index -= visible_max
        @index -= visible_max
        0 > @index and @index = 0
      elsif @index > 0
        @index = 0
      elsif wrap
        @index = @size - 1
        ensure_cursor_visible
      end
    end
    
    def ensure_cursor_visible
      self.top_index = @index if @index < top_index
      self.bottom_index = @index if @index > bottom_index
    end
    
    def action_on_select
      Sound.play_cursor
    end
    
    def activate
      @active = true
      @viewport.oy = 0
      select(@index)
    end
    
    def deactivate
      @active = false
      unselect_all
    end
    
    def select(index)
      return false unless @windows[index]
      unselect_all
      @windows[index].selected = true
      ensure_cursor_visible
    end
    
    def unselect_all
      @windows.each { |w| w.selected = false }
    end
  end

  #==========================================================================
  # ■ Window_ParallelBase - ウィンドウ並列配置で表示するウィンドウの基本形
  #==========================================================================

  class Window_ParallelBase < Window_Base
    attr_reader :selected
    
    def initialize(i)
      super(0, window_height * i, window_width, window_height)
      @selected = false
    end
    
    def selected=(selected)
      @selected = selected
      update_cursor
      update_help
    end

    def help_window=(help_window)
      @help_window = help_window
    end
    
    def update_cursor
      if @selected
        cursor_rect.set(0, 0, contents.width, contents.height)
      else
        cursor_rect.empty
      end
    end

    def update_help

    end
    
    def window_width
      180
    end
    
    def window_height
      fitting_height(1)
    end
  end
end

#//////////////////////////////////////////////////////////////////////////////
# ◆ RGSS3改変
#//////////////////////////////////////////////////////////////////////////////

#==============================================================================
# ■ Audio - 音量変更(AUDIO_VOLUME_CONTROL)
#==============================================================================

if CraftLib::AUDIO_VOLUME_CONTROL
  module Audio
    module Volume
      @master = true
      @master_volume = 100
      
      @bgm = true
      @bgm_volume = 100
      
      @bgs = true
      @bgs_volume = 100
      
      @me = true
      @me_volume = 100
      
      @se = true
      @se_volume = 100
      
      class << self
        def replay
          RPG::BGM.last.replay
          RPG::BGS.last.replay
        end
        
        def master
          @master
        end
        
        def master_volume
          @master_volume
        end
        
        def master=(flag)
          @master = flag
          replay
        end
        
        def master_volume=(volume)
          @master_volume = volume
          replay
        end
        
        def bgm
          @bgm
        end
        
        def bgm_volume
          @bgm_volume
        end
        
        def bgm=(flag)
          @bgm = flag
          replay
        end
        
        def bgm_volume=(volume)
          @bgm_volume = volume
          replay
        end
        
        def total_bgm_volume
          @master && @bgm ? (@master_volume / 100.0) * (@bgm_volume / 100.0) : 0
        end
        
        def bgs
          @bgs
        end
        
        def bgs_volume
          @bgs_volume
        end
        
        def bgs=(flag)
          @bgs = flag
          replay
        end
        
        def bgs_volume=(volume)
          @bgs_volume = volume
          replay
        end
        
        def total_bgs_volume
          @master && @bgs ? (@master_volume / 100.0) * (@bgs_volume / 100.0) : 0
        end
        
        def me
          @me
        end
        
        def me_volume
          @me_volume
        end
        
        def me=(flag)
          @me = flag
        end
        
        def me_volume=(volume)
          @me_volume = volume
        end
        
        def total_me_volume
          @master && @me ? (@master_volume / 100.0) * (@me_volume / 100.0) : 0
        end
        
        def se
          @se
        end
        
        def se_volume
          @se_volume
        end
        
        def se=(flag)
          @se = flag
        end
        
        def se_volume=(volume)
          @se_volume = volume
        end
        
        def total_se_volume
          @master && @se ? (@master_volume / 100.0) * (@se_volume / 100.0) : 0
        end
      end
    end

    class << self
      alias clib_bgm_play bgm_play
      def bgm_play(filename, volume = 100, pitch = 100, pos = 0)
        clib_bgm_play(filename, volume * Volume.total_bgm_volume, pitch, pos)
      end

      alias clib_bgs_play bgs_play
      def bgs_play(filename, volume = 100, pitch = 100, pos = 0)
        clib_bgs_play(filename, volume * Volume.total_bgs_volume, pitch, pos)
      end

      alias clib_me_play me_play
      def me_play(filename, volume = 100, pitch = 100)
        clib_me_play(filename, volume * Volume.total_me_volume, pitch)
      end

      alias clib_se_play se_play
      def se_play(filename, volume = 100, pitch = 100)
        clib_se_play(filename, volume * Volume.total_se_volume, pitch)
      end
    end
  end
end

#==============================================================================
# ■ Game_Event - 注釈セットアップ(SETUP_COMMNENT)
#==============================================================================

if CraftLib::SETUP_COMMNENT
  module CraftLib::SetupComment
    @func = {}

    class << self
      def register(regexp, proc = nil, &block)
        @func[regexp] = proc || block
      end

      def run(event, comment)
        @func.each { |regexp, proc| comment.scan(regexp) { |m| proc.call(event, m) } }
      end
    end
  end

  class Game_Event < Game_Character
    alias clib_setup_page_settings setup_page_settings
    def setup_page_settings
      clib_setup_page_settings
      i = 0
      while (code = (command = @list[i]).code) == 108 || code == 408
        CraftLib::SetupComment.run(self, command.parameters[0])
        i += 1
      end
    end
  end
end

#==============================================================================
# ■ Window_Base - 機能拡張(WINDOW_BASE_EXTEND)
#==============================================================================

if CraftLib::WINDOW_BASE_EXTEND
  class Window_Base < Window
    def calc_line_width(text)
      CraftLib::FAKE_WINDOW_BASE.calc_line_width_process(text)
    end
  end
end