Плохо! Плохо!:  0
Страница 7 из 7 ПерваяПервая ... 567
Показано с 61 по 62 из 62

Тема: Open RGSS3: Разработка

  1. #61
    Пользователь Аватар для insider
    Информация о пользователе
    Регистрация
    24.04.2008
    Сообщений
    86
    Репутация: 1 Добавить или отнять репутацию

    По умолчанию

    Интересный проект. Жаль, что заморожен. Тоже интересует переписывание базовых классов, но не с целью совместимости XP и VX, а с целью портирования RGSS Player'а на другие платформы. Накидаю в этом посту найденную информацию, вдруг кому-нибудь пригодится или на что-нибудь сподвигнет. Нет - так нет. В общем, пусть будет тут.


    Спойлер Table за авторством vgvgf:
    Код:
    class Table
      def initialize(x, y = 1, z = 1)
         @xsize, @ysize, @zsize = x, y, z
         @data = Array.new(x * y * z, 0)
      end
      def [](x, y = 0, z = 0)
         @data[x + y * @xsize + z * @xsize * @ysize]
      end
      def []=(*args)
         x = args[0]
         y = args.size > 2 ? args[1] :0
         z = args.size > 3 ? args[2] :0
         v = args.pop
         @data[x + y * @xsize + z * @xsize * @ysize] = v
      end
      def _dump(d = 0)
         s = [3].pack('L')
         s += [@xsize].pack('L') + [@ysize].pack('L') + [@zsize].pack('L')
         s += [@xsize * @ysize * @zsize].pack('L')
         for z in 0...@zsize
            for y in 0...@ysize
               for x in 0...@xsize
                  s += [@data[x + y * @xsize + z * @xsize * @ysize]].pack('S')
               end
            end
         end
         s
      end
      def self._load(s)
         size = s[0, 4].unpack('L')[0]
         nx = s[4, 4].unpack('L')[0]
         ny = s[8, 4].unpack('L')[0]
         nz = s[12, 4].unpack('L')[0]
         data = []
         pointer = 20
         loop do
            data.push(*s[pointer, 2].unpack('S'))
            pointer += 2
            break if pointer > s.size - 1
         end
         t = Table.new(nx, ny, nz)
         n = 0
         for z in 0...nz
            for y in 0...ny
               for x in 0...nx
                  t[x, y, z] = data[n]
                  n += 1
               end
            end
         end
         t
      end
      attr_reader(:xsize, :ysize, :zsize, :data)
    end


    Спойлер Color за авторством vgvgf:
    Код:
    class Color
      def initialize(r, g, b, a = 255)
         @red = r
         @green = g
         @blue = b
         @alpha = a
      end
      def set(r, g, b, a = 255)
         @red = r
         @green = g
         @blue = b
         @alpha = a
      end
      def color
         Color.new(@red, @green, @blue, @alpha)
      end
      def _dump(d = 0)
         [@red, @green, @blue, @alpha].pack('d4')
      end
      def self._load(s)
         Color.new(*s.unpack('d4'))
      end
      attr_accessor(:red, :green, :blue, :alpha)
    end


    Спойлер Tone за авторством vgvgf:
    Код:
    class Tone
      def initialize(r, g, b, a = 0)
         @red = r
         @green = g
         @blue = b
         @gray = a
      end
      def set(r, g, b, a = 0)
         @red = r
         @green = g
         @blue = b
         @gray = a
      end
      def color
         Color.new(@red, @green, @blue, @gray)
      end
      def _dump(d = 0)
         [@red, @green, @blue, @gray].pack('d4')
      end
      def self._load(s)
         Tone.new(*s.unpack('d4'))
      end
      attr_accessor(:red, :green, :blue, :gray)
    end


    Спойлер Window за авторством Selwyn:
    Код:
    #==============================================================================
    #  –  Window - Hidden RGSS Class
    #------------------------------------------------------------------------------
    #  by Selwyn
    #==============================================================================
    
    #==============================================================================
    #  –  Bitmap
    #==============================================================================
    
    class Bitmap
      #--------------------------------------------------------------------------
      #  — erase
      #--------------------------------------------------------------------------
      def erase(*args)
        if args.size == 1
          rect = args[0]
        elsif args.size == 4
          rect = Rect.new(*args)
        end
        fill_rect(rect, Color.new(0, 0, 0, 0))
      end
    end
    
    #==============================================================================
    #  –¡ SG
    #------------------------------------------------------------------------------
    #  Selwyn's Graphics module
    #==============================================================================
    
    module SG
    #==============================================================================
    #  –  SG::Skin
    #==============================================================================
    
    class Skin
      #--------------------------------------------------------------------------
      #  — instances settings
      #--------------------------------------------------------------------------
      attr_reader   :margin
      attr_accessor :bitmap
      #--------------------------------------------------------------------------
      #  — initialize
      #--------------------------------------------------------------------------
      def initialize
        @bitmap = nil
        @values = {}
        @values['bg'] = Rect.new(0, 0, 128, 128)
        @values['pause0'] = Rect.new(160, 64, 16, 16)
        @values['pause1'] = Rect.new(176, 64, 16, 16)
        @values['pause2'] = Rect.new(160, 80, 16, 16)
        @values['pause3'] = Rect.new(176, 80, 16, 16)
        @values['arrow_up'] = Rect.new(152, 16, 16, 8)
        @values['arrow_down'] = Rect.new(152, 40, 16, 8)
        @values['arrow_left'] = Rect.new(144, 24, 8, 16)
        @values['arrow_right'] = Rect.new(168, 24, 8, 16)
        self.margin = 16
      end
      #--------------------------------------------------------------------------
      #  — width
      #--------------------------------------------------------------------------
      def margin=(width)
        @margin = width
        set_values
      end
      #--------------------------------------------------------------------------
      #  — set_values
      #--------------------------------------------------------------------------
      def set_values
        w = @margin
        @values['ul_corner'] = Rect.new(128, 0, w, w)
        @values['ur_corner'] = Rect.new(192-w, 0, w, w)
        @values['dl_corner'] = Rect.new(128, 64-w, w, w)
        @values['dr_corner'] = Rect.new(192-w, 64-w, w, w)
        @values['up'] = Rect.new(128+w, 0, 64-2*w, w)
        @values['down'] = Rect.new(128+w, 64-w, 64-2*w, w)
        @values['left'] = Rect.new(128, w, w, 64-2*w)
        @values['right'] = Rect.new(192-w, w, w, 64-2*w)
      end
      #--------------------------------------------------------------------------
      #  — []
      #--------------------------------------------------------------------------
      def [](value)
        return @values[value]
      end
    end
      
    
    #==============================================================================
    #  –  SG::Cursor_Rect
    #==============================================================================
    
    class Cursor_Rect < ::Sprite
      #--------------------------------------------------------------------------
      #  — instances settings
      #--------------------------------------------------------------------------
      attr_reader   :height, :width, :skin, :margin
      #--------------------------------------------------------------------------
      #  — initialize
      #--------------------------------------------------------------------------
      def initialize(viewport)
        super(viewport)
        @width = 0
        @height = 0
        @skin = nil
        @margin = 0
        @rect = {}
        @rect['cursor_up'] = Rect.new(129, 64, 30, 1)
        @rect['cursor_down'] = Rect.new(129, 95, 30, 1)
        @rect['cursor_left'] = Rect.new(128, 65, 1, 30)
        @rect['cursor_right'] = Rect.new(159, 65, 1, 30)
        @rect['upleft'] = Rect.new(128, 64, 1, 1)
        @rect['upright'] = Rect.new(159, 64, 1, 1)
        @rect['downleft'] = Rect.new(128, 95, 1, 1)
        @rect['downright'] = Rect.new(159, 95, 1, 1)
        @rect['bg'] = Rect.new(129, 65, 30, 30)
      end
      #--------------------------------------------------------------------------
      #  — margin=
      #--------------------------------------------------------------------------
      def margin=(margin)
        @margin = margin
        set(x, y, width, height)
      end
      #--------------------------------------------------------------------------
      #  — skin=
      #--------------------------------------------------------------------------
      def skin=(skin)
        @skin = skin
        draw_rect
      end
      #--------------------------------------------------------------------------
      #  — width=
      #--------------------------------------------------------------------------
      def width=(width)
        return if @width == width
        @width = width
        if @width == 0 and self.bitmap != nil
          self.bitmap.dispose
          self.bitmap = nil
        end
        draw_rect
      end
      #--------------------------------------------------------------------------
      #  — height=
      #--------------------------------------------------------------------------
      def height=(height)
        return if @height == height
        @height = height
        if @height == 0 and self.bitmap != nil
          self.bitmap.dispose
          self.bitmap = nil
        end
        draw_rect
      end
      #--------------------------------------------------------------------------
      #  — set
      #--------------------------------------------------------------------------
      def set(x, y, width, height)
        self.x = x + @margin
        self.y = y + @margin
        if @width != width or @height != height
          @width = width
          @height = height
          if width > 0 and height > 0
            draw_rect
          end
        end
      end
      #--------------------------------------------------------------------------
      #  — empty
      #--------------------------------------------------------------------------
      def empty
        self.x = 0
        self.y = 0
        self.width = 0
        self.height = 0
      end
      #--------------------------------------------------------------------------
      #  — draw_rect
      #--------------------------------------------------------------------------
      def draw_rect
        return if @skin == nil
        if @width > 0 and @height > 0
          self.bitmap = Bitmap.new(@width, @height)
          rect = Rect.new(1, 1, @width - 2, @height - 2)
          self.bitmap.stretch_blt(rect, @skin, @rect['bg'])
          self.bitmap.blt(0, 0, @skin, @rect['upleft'])
          self.bitmap.blt(@width-1, 0, @skin, @rect['upright'])
          self.bitmap.blt(0, @height-1, @skin, @rect['downright'])
          self.bitmap.blt(@width-1, @height-1, @skin, @rect['downleft'])
          rect = Rect.new(1, 0, @width - 2, 1)
          self.bitmap.stretch_blt(rect, @skin, @rect['cursor_up'])
          rect = Rect.new(0, 1, 1, @height - 2)
          self.bitmap.stretch_blt(rect, @skin, @rect['cursor_left'])
          rect = Rect.new(1, @height-1, @width - 2, 1)
          self.bitmap.stretch_blt(rect, @skin, @rect['cursor_down'])
          rect = Rect.new(@width - 1, 1, 1, @height - 2)
          self.bitmap.stretch_blt(rect, @skin, @rect['cursor_right'])
        end
      end
    end
    
    #==============================================================================
    #  –  SG::Window
    #------------------------------------------------------------------------------
    #  
    #==============================================================================
    
    class Window
      #--------------------------------------------------------------------------
      #  — set instances variables
      #--------------------------------------------------------------------------
      attr_reader(:x, :y, :z, :width, :height, :ox, :oy, :opacity, :back_opacity,
                  :stretch, :contents_opacity, :visible, :pause)
      attr_accessor :active
      #--------------------------------------------------------------------------
      #  — initialize
      #--------------------------------------------------------------------------
      def initialize()
        @skin = Skin.new
        @viewport = Viewport.new(0, 0, 0, 0)
        @cr_vport = Viewport.new(0, 0, 0, 0)
        @width = 0
        @height = 0
        @ox = 0
        @oy = 0
        @opacity = 255
        @back_opacity = 255
        @contents_opacity = 255
        @frame   = Sprite.new()
        @bg      = Sprite.new()
        @window  = Sprite.new(@viewport)
        @pause_s = Sprite.new()
        @arrows = []
        for i in 0...4
          @arrows.push(Sprite.new(@cr_vport))
          @arrows[i].bitmap = Bitmap.new(16, 16)
          @arrows[i].visible = false
        end
        @cursor_rect = Cursor_Rect.new(@cr_vport)
        @cursor_rect.margin = @skin.margin
        @cursor_fade = true
        @pause_s.visible = false
        @pause = false
        @active = true
        @stretch = true
        @visible = true
        self.x = 0
        self.y = 0
        self.z = 100
        self.windowskin = RPG::Cache.windowskin($game_system.windowskin_name)
      end
      #--------------------------------------------------------------------------
      #  — contents=
      #--------------------------------------------------------------------------
      def contents=(bmp)
        @window.bitmap = bmp
        if bmp != nil
          if bmp.width > @viewport.rect.width
             bmp.height > @viewport.rect.height
            draw_arrows
          end
        end
      end
      #--------------------------------------------------------------------------
      #  — contents
      #--------------------------------------------------------------------------
      def contents
        return @window.bitmap
      end
      #--------------------------------------------------------------------------
      #  — dispose
      #--------------------------------------------------------------------------
      def dispose
        @bg.dispose
        @frame.dispose
        @window.dispose
        @cursor_rect.dispose
        @viewport.dispose
        @pause_s.dispose
        @cr_vport.dispose
        for arrow in @arrows
          arrow.dispose
        end
      end
      #--------------------------------------------------------------------------
      #  — update
      #--------------------------------------------------------------------------
      def update
        @window.update
        @cursor_rect.update
        @viewport.update
        @cr_vport.update
        @pause_s.src_rect = @skin["pause#{(Graphics.frame_count / 8) % 4}"]
        @pause_s.update
        update_visible
        update_arrows
        if @cursor_fade
          @cursor_rect.opacity -= 10
          @cursor_fade = false if @cursor_rect.opacity <= 100
        else
          @cursor_rect.opacity += 10
          @cursor_fade = true if @cursor_rect.opacity >= 255
        end
      end
      #--------------------------------------------------------------------------
      #  — update_visible
      #--------------------------------------------------------------------------
      def update_visible
        @frame.visible = @visible
        @bg.visible = @visible
        @window.visible = @visible
        @cursor_rect.visible = @visible
        if @pause
          @pause_s.visible = @visible
        else
          @pause_s.visible = false
        end
      end
      #--------------------------------------------------------------------------
      #  — pause=
      #--------------------------------------------------------------------------
      def pause=(pause)
        @pause = pause
        update_visible
      end
      #--------------------------------------------------------------------------
      #  — update_arrows
      #--------------------------------------------------------------------------
      def update_arrows
        if @window.bitmap == nil or @visible == false
          for arrow in @arrows
            arrow.visible = false
          end
        else
          @arrows[0].visible = @oy > 0
          @arrows[1].visible = @ox > 0
          @arrows[2].visible = (@window.bitmap.width - @ox) > @viewport.rect.width
          @arrows[3].visible = (@window.bitmap.height - @oy) > @viewport.rect.height
        end
      end
      #--------------------------------------------------------------------------
      #  — visible=
      #--------------------------------------------------------------------------
      def visible=(visible)
        @visible = visible
        update_visible
        update_arrows
      end
      #--------------------------------------------------------------------------
      #  — x=
      #--------------------------------------------------------------------------
      def x=(x)
        @x = x
        @bg.x = x + 2
        @frame.x = x
        @viewport.rect.x = x + @skin.margin
        @cr_vport.rect.x = x
        @pause_s.x = x + (@width / 2) - 8
        set_arrows
      end
      #--------------------------------------------------------------------------
      #  — y=
      #--------------------------------------------------------------------------
      def y=(y)
        @y = y
        @bg.y = y + 2
        @frame.y = y
        @viewport.rect.y = y + @skin.margin
        @cr_vport.rect.y = y
        @pause_s.y = y + @height - @skin.margin
        set_arrows
      end
      #--------------------------------------------------------------------------
      #  — z=
      #--------------------------------------------------------------------------
      def z=(z)
        @z = z
        @bg.z = z
        @frame.z = z + 1
        @cr_vport.z = z + 2
        @viewport.z = z + 3
        @pause_s.z = z + 4
      end
      #--------------------------------------------------------------------------
      #  — ox=
      #--------------------------------------------------------------------------
      def ox=(ox)
        return if @ox == ox
        @ox = ox
        @viewport.ox = ox
        update_arrows
      end
      #--------------------------------------------------------------------------
      #  — oy=
      #--------------------------------------------------------------------------
      def oy=(oy)
        return if @oy == oy
        @oy = oy
        @viewport.oy = oy
        update_arrows
      end
      #--------------------------------------------------------------------------
      #  — width=
      #--------------------------------------------------------------------------
      def width=(width)
        @width = width
        @viewport.rect.width = width - @skin.margin * 2
        @cr_vport.rect.width = width
        if @width > 0 and @height > 0
          @frame.bitmap = Bitmap.new(@width, @height)
          @bg.bitmap = Bitmap.new(@width - 4, @height - 4)
          draw_window
        end
        self.x = @x
        self.y = @y
      end
      #--------------------------------------------------------------------------
      #  — height=
      #--------------------------------------------------------------------------
      def height=(height)
        @height = height
        @viewport.rect.height = height - @skin.margin * 2
        @cr_vport.rect.height = height
        if @height > 0 and @width > 0
          @frame.bitmap = Bitmap.new(@width, @height)
          @bg.bitmap = Bitmap.new(@width - 4, @height - 4)
          draw_window
        end
        self.x = @x
        self.y = @y
      end
      #--------------------------------------------------------------------------
      #  — opacity=
      #--------------------------------------------------------------------------
      def opacity=(opacity)
        value = [[opacity, 255].min, 0].max
        @opacity = value
        @contents_opacity = value
        @back_opacity = value
        @frame.opacity = value
        @bg.opacity = value
        @window.opacity = value
      end
      #--------------------------------------------------------------------------
      #  — back_opacity=
      #--------------------------------------------------------------------------
      def back_opacity=(opacity)
        value = [[opacity, 255].min, 0].max
        @back_opacity = value
        @bg.opacity = value
      end
      #--------------------------------------------------------------------------
      #  — contents_opacity=
      #--------------------------------------------------------------------------
      def contents_opacity=(opacity)
        value = [[opacity, 255].min, 0].max
        @contents_opacity = value
        @window.opacity = value
      end
      #--------------------------------------------------------------------------
      #  — cursor_rect
      #--------------------------------------------------------------------------
      def cursor_rect
        return @cursor_rect
      end
      #--------------------------------------------------------------------------
      #  — cursor_rect=
      #--------------------------------------------------------------------------
      def cursor_rect=(rect)
        @cursor_rect.x = rect.x
        @cursor_rect.y = rect.y
        if @cursor_rect.width != rect.width or @cursor_rect.height != rect.height
          @cursor_rect.set(@cursor_rect.x, @cursor_rect.y, rect.width, rect.height)
        end
      end
      #--------------------------------------------------------------------------
      #  — windowskin
      #--------------------------------------------------------------------------
      def windowskin
        return @skin.bitmap
      end
      #--------------------------------------------------------------------------
      #  — windowskin=
      #--------------------------------------------------------------------------
      def windowskin=(windowskin)
        return if windowskin == nil
        if @skin.bitmap != windowskin
          @pause_s.bitmap = windowskin
          @pause_s.src_rect = @skin['pause0']
          @skin.bitmap = windowskin
          @cursor_rect.skin = windowskin
          draw_window
          draw_arrows
        end
      end
      #--------------------------------------------------------------------------
      #  — margin=
      #--------------------------------------------------------------------------
      def margin=(margin)
        if @skin.margin != margin
          @skin.margin = margin
          self.x = @x
          self.y = @y
          temp = @height
          self.height = 0
          self.width = @width
          self.height = temp
          @cursor_rect.margin = margin
          set_arrows
        end
      end
      #--------------------------------------------------------------------------
      #  — stretch=
      #--------------------------------------------------------------------------
      def stretch=(bool)
        if @stretch != bool
          @stretch = bool
          draw_window
        end
      end
      #--------------------------------------------------------------------------
      #  — set_arrows
      #--------------------------------------------------------------------------
      def set_arrows
        @arrows[0].x = @width / 2 - 8
        @arrows[0].y = 8
        @arrows[1].x = 8
        @arrows[1].y = @height / 2 - 8
        @arrows[2].x = @width - 16
        @arrows[2].y = @height / 2 - 8
        @arrows[3].x = @width / 2 - 8
        @arrows[3].y = @height - 16
      end
      #--------------------------------------------------------------------------
      #  — draw_arrows
      #--------------------------------------------------------------------------
      def draw_arrows
        return if @skin.bitmap == nil
        @arrows[0].bitmap.blt(0, 0, @skin.bitmap, @skin['arrow_up'])
        @arrows[1].bitmap.blt(0, 0, @skin.bitmap, @skin['arrow_left'])
        @arrows[2].bitmap.blt(0, 0, @skin.bitmap, @skin['arrow_right'])
        @arrows[3].bitmap.blt(0, 0, @skin.bitmap, @skin['arrow_down'])
        update_arrows
      end
      #--------------------------------------------------------------------------
      #  — draw_window
      #--------------------------------------------------------------------------
      def draw_window
        return if @skin.bitmap == nil
        return if @width == 0 or @height == 0
        m = @skin.margin
        if @frame.bitmap.nil?
          @frame.bitmap = Bitmap.new(@width, @height)
          @bg.bitmap = Bitmap.new(@width - 4, @height - 4)
        end
        @frame.bitmap.clear
        @bg.bitmap.clear
        if @stretch
          dest_rect = Rect.new(0, 0, @width-4, @height-4)
          @bg.bitmap.stretch_blt(dest_rect, @skin.bitmap, @skin['bg'])
        else
          bgw = Integer((@width-4) / 128) + 1
          bgh = Integer((@height-4) / 128) + 1
          for x in 0..bgw
            for y in 0..bgh
              @bg.bitmap.blt(x * 128, y * 128, @skin.bitmap, @skin['bg']) 
            end
          end
        end
        bx = Integer((@width - m*2) / @skin['up'].width) + 1
        by = Integer((@height - m*2) / @skin['left'].height) + 1
        for x in 0..bx
          w = @skin['up'].width
          @frame.bitmap.blt(x * w + m, 0, @skin.bitmap, @skin['up'])
          @frame.bitmap.blt(x * w + m, @height - m, @skin.bitmap, @skin['down'])
        end
        for y in 0..by
          h = @skin['left'].height
          @frame.bitmap.blt(0, y * h + m, @skin.bitmap, @skin['left'])
          @frame.bitmap.blt(@width - m, y * h + m, @skin.bitmap, @skin['right'])
        end
        @frame.bitmap.erase(@width - m, 0, m, m)
        @frame.bitmap.erase(0, @height - m, m, m)
        @frame.bitmap.erase(@width - m, @height - m, m, m)
        @frame.bitmap.blt(0, 0, @skin.bitmap, @skin['ul_corner'])
        @frame.bitmap.blt(@width - m, 0, @skin.bitmap, @skin['ur_corner'])
        @frame.bitmap.blt(0, @height - m, @skin.bitmap, @skin['dl_corner'])
        @frame.bitmap.blt(@width - m, @height - m, @skin.bitmap, @skin['dr_corner'])
      end
    end
    
    end


    Спойлер Plane за авторством Selwyn:
    Код:
    #==============================================================================
    #  –  SG::Plane
    #------------------------------------------------------------------------------
    # by Selwyn
    #==============================================================================
    module SG; end
    
    class SG::Plane
      attr_reader :ox, :oy
      #--------------------------------------------------------------------------
      #  — initialize
      #--------------------------------------------------------------------------
      def initialize(viewport = nil)
        @sprite = Sprite.new(viewport)
        @max_width = 640
        @max_height = 480
        @bitmap = nil
        @ox = 0
        @oy = 0
      end
      #--------------------------------------------------------------------------
      #  — z, zoom_x, zoom_y, opacity, blend_type, color, tone
      #    z=, zoom_x=, zoom_y=, opacity=, blend_type=, color=, tone=
      #--------------------------------------------------------------------------
      def method_missing(symbol, *args)
        @sprite.method(symbol).call(*args)
      end
      #--------------------------------------------------------------------------
      #  — bitmap=
      #--------------------------------------------------------------------------
      def bitmap=(bitmap)
        @bitmap = bitmap
        refresh
      end
      #--------------------------------------------------------------------------
      #  — ox=
      #--------------------------------------------------------------------------
      def ox=(ox)
        w = @sprite.viewport != nil ? @sprite.viewport.rect.width : @max_width
        @ox = ox % w
        @sprite.ox = @ox
      end
      #--------------------------------------------------------------------------
      #  — oy=
      #--------------------------------------------------------------------------
      def oy=(oy)
        h = @sprite.viewport != nil ? @sprite.viewport.rect.height : @max_height
        @oy = oy % h
        @sprite.oy = @oy
      end
      #--------------------------------------------------------------------------
      #  — refresh
      #--------------------------------------------------------------------------
      def refresh
        return if @bitmap.nil?
        w = @sprite.viewport != nil ? @sprite.viewport.rect.width : @max_width
        h = @sprite.viewport != nil ? @sprite.viewport.rect.height : @max_height
        if @sprite.bitmap != nil
          @sprite.bitmap.dispose
        end
        @sprite.bitmap = Bitmap.new(w * 2, h * 2)
        
        max_x = w / @bitmap.width
        max_y = h / @bitmap.height
        for x in 0..max_x
          for y in 0..max_y
            @sprite.bitmap.blt(x * @bitmap.width, y * @bitmap.height,
             @bitmap, Rect.new(0, 0, @bitmap.width, @bitmap.height))
          end
        end
        for i in 1...4
          x = i % 2 * w
          y = i / 2 * h
          @sprite.bitmap.blt(x, y, @sprite.bitmap, Rect.new(0, 0, w, h))
        end
      end
    end


    Спойлер Tilemap от Poccil:
    Код:
    class CustomTilemapAutotiles
      attr_accessor :changed
      def initialize
       @changed=true
       @tiles=[nil,nil,nil,nil,nil,nil,nil]
      end
      def []=(i,value)
       @tiles[i]=value
       @changed=true
      end
      def [](i)
       return @tiles[i]
      end
    end
     
    class TilemapNEW
      Animated_Autotiles_Frames = 15
      Autotiles = [
        [ [27, 28, 33, 34], [ 5, 28, 33, 34], [27,  6, 33, 34], [ 5,  6, 33, 34],
          [27, 28, 33, 12], [ 5, 28, 33, 12], [27,  6, 33, 12], [ 5,  6, 33, 12] ],
        [ [27, 28, 11, 34], [ 5, 28, 11, 34], [27,  6, 11, 34], [ 5,  6, 11, 34],
          [27, 28, 11, 12], [ 5, 28, 11, 12], [27,  6, 11, 12], [ 5,  6, 11, 12] ],
        [ [25, 26, 31, 32], [25,  6, 31, 32], [25, 26, 31, 12], [25,  6, 31, 12],
          [15, 16, 21, 22], [15, 16, 21, 12], [15, 16, 11, 22], [15, 16, 11, 12] ],
        [ [29, 30, 35, 36], [29, 30, 11, 36], [ 5, 30, 35, 36], [ 5, 30, 11, 36],
          [39, 40, 45, 46], [ 5, 40, 45, 46], [39,  6, 45, 46], [ 5,  6, 45, 46] ],
        [ [25, 30, 31, 36], [15, 16, 45, 46], [13, 14, 19, 20], [13, 14, 19, 12],
          [17, 18, 23, 24], [17, 18, 11, 24], [41, 42, 47, 48], [ 5, 42, 47, 48] ],
        [ [37, 38, 43, 44], [37,  6, 43, 44], [13, 18, 19, 24], [13, 14, 43, 44],
          [37, 42, 43, 48], [17, 18, 47, 48], [13, 18, 43, 48], [ 1,  2,  7,  8] ]
      ]
      FlashOpacity=[100,90,80,70,80,90]
      attr_reader :tileset
      attr_reader :autotiles
      attr_reader :map_data
      attr_accessor :flash_data
      attr_accessor :priorities
      attr_reader :visible
      attr_accessor :ox
      attr_accessor :oy
      attr_reader :viewport
      def initialize(viewport)
        @tileset    = nil  # Refers to Map Tileset Name
        @autotiles  = CustomTilemapAutotiles.new
        @map_data   = nil  # Refers to 3D Array Of Tile Settings
        @flash_data = nil  # Refers to 3D Array of Tile Flashdata
        @priorities = nil  # Refers to Tileset Priorities
        @visible    = true # Refers to Tileset Visibleness
        @ox         = 0    # Bitmap Offsets
        @oy         = 0    # bitmap Offsets
        @plane       = false
        @selfviewport=Viewport.new(0,0,640,480)
        @viewport=viewport ? viewport : @selfviewport
        @tiles=[]
        @autotileInfo=[]
        @regularTileInfo=[]
        @oldOx=0
        @oldOy=0
        @layer0=Sprite.new(viewport)
        @layer0.visible=true
        @nowshown=false
        @layer0.bitmap=Bitmap.new(@viewport.rect.width*2,@viewport.rect.height*2)
        @flash=nil
        @layer0.ox=0
        @layer0.oy=0
        @oxLayer0=0
        @oyLayer0=0
        @oxFlash=0
        @oyFlash=0
        @layer0.z=0
        @priotiles=[]
        @prioautotiles=[]
        @autosprites=[]
        @framecount=[]
        @tilesetChanged=true
        @flashChanged=false
        @firsttime=true
        @disposed=false
        @usedsprites=false
        @layer0clip=true
        @firsttimeflash=true
        @fullyrefreshed=false
        @fullyrefreshedautos=false
      end
      def disposed?
       return @disposed
      end
      def flash_data=(value)
       @flash_data=value
       @flashChanged=true  
      end
      def update
        if @autotiles.changed
          refresh_autotiles
          repaintAutotiles
        end
        if @flashChanged
          refresh_flash
        end
        if @tilesetChanged
          refresh_tileset
        end
        if @flash
         @flash.opacity=FlashOpacity[(Graphics.frame_count/2) % 6]
        end
        if !(@oldOx==@ox && @oldOy==@oy &&
               !@tilesetChanged &&
               !@autotiles.changed)
          refresh
        end
        if (Graphics.frame_count % Animated_Autotiles_Frames == 0) || @nowshown
          repaintAutotiles
          refresh(true)
        end
        @nowshown=false
        @autotiles.changed=false
        @tilesetChanged=false
      end
    def priorities=(value)
      @priorities=value
      @tilesetChanged=true
    end
    def tileset=(value)
      @tileset=value
      @tilesetChanged=true
    end
    def shown?
       return false if !@visible
       ysize=@map_data.ysize
       xsize=@map_data.xsize
       xStart=(@ox/32)-1
       xEnd=((@ox+@viewport.rect.width)/32)+1
       yStart=(@oy/32)-1
       yEnd=((@oy+@viewport.rect.height)/32)+1
       xStart=0 if xStart<0
       xStart=xsize-1 if xStart>=xsize
       xEnd=0 if xEnd<0
       xEnd=xsize-1 if xEnd>=xsize
       yStart=0 if yStart<0
       yStart=ysize-1 if yStart>=ysize
       yEnd=0 if yEnd<0
       yEnd=ysize-1 if yEnd>=ysize
       return (xStart<xEnd && yStart<yEnd)
    end
    def dispose
    return if disposed?
    @help.dispose if @help
    @help=nil
    i=0;len=@autotileInfo.length;while i<len
      if @autotileInfo[i]
         @autotileInfo[i].dispose
         @autotileInfo[i]=nil
      end
      i+=1
    end
    i=0;len=@regularTileInfo.length;while i<len
      if @regularTileInfo[i]
         @regularTileInfo[i].dispose
         @regularTileInfo[i]=nil
      end
      i+=1
    end
    i=0;len=@tiles.length;while i<len
      @tiles[i].dispose
      @tiles[i]=nil
      i+=2
    end
    i=0;len=@autosprites.length;while i<len
      @autosprites[i].dispose
      @autosprites[i]=nil
      i+=2
    end
    if @layer0
      @layer0.bitmap.dispose if !@layer0.disposed?
      @layer0.bitmap=nil if !@layer0.disposed?
      @layer0.dispose
      @layer0=nil
    end
    if @flash
      @flash.bitmap.dispose if !@flash.disposed?
      @flash.bitmap=nil if !@flash.disposed?
      @flash.dispose
      @flash=nil
    end
    for i in 0...7
      self.autotiles[i]=nil
    end
    @tiles.clear
    @autosprites.clear
    @autotileInfo.clear
    @regularTileInfo.clear
    @tilemap=nil
    @tileset=nil
    @priorities=nil
    @selfviewport.dispose
    @selfviewport=nil
    @disposed=true
    end
     
    def bltAutotile(bitmap,x,y,id,frame)
      return if frame<0
      autotile=@autotiles[id/48-1]
      return if !autotile
      if autotile.height==32
        anim=frame*32
        src_rect=Rect.new(anim,0,32,32)
        bitmap.blt(x,y,autotile,src_rect)
      else
        anim=frame*96
        id%=48
        tiles = Autotiles[id>>3][id&7]
        src=Rect.new(0,0,0,0)
        for i in 0...4
          tile_position = tiles[i] - 1
          src.set(tile_position % 6 * 16 + anim,
           tile_position / 6 * 16, 16, 16)
          bitmap.blt(i%2*16+x,i/2*16+y, autotile, src)
        end
      end
    end
     
    def autotileNumFrames(id)
      autotile=@autotiles[id/48-1]
      return 0 if !autotile || autotile.disposed?
      frames=1
      if autotile.height==32
       frames=autotile.width/32
      else
       frames=autotile.width/96
      end
      return frames
    end
     
    def autotileFrame(id)
      autotile=@autotiles[id/48-1]
      return -1 if !autotile || autotile.disposed?
      frames=1
      if autotile.height==32
       frames=autotile.width/32
      else
       frames=autotile.width/96
      end
      return (Graphics.frame_count/Animated_Autotiles_Frames)%frames
    end
     
    def repaintAutotiles
    for i in 0...@autotileInfo.length
      next if !@autotileInfo[i]
      frame=autotileFrame(i)
      bltAutotile(@autotileInfo[i],0,0,i,frame)
    end
    end
     
    def getAutotile(sprite,id)
      anim=autotileFrame(id)
      return if anim<0
      bitmap=@autotileInfo[id]
      if !bitmap
        bitmap=Bitmap.new(32,32)
        bltAutotile(bitmap,0,0,id,anim)
        @autotileInfo[id]=bitmap
      end
      sprite.bitmap=bitmap if !sprite.equal?(bitmap) || sprite.bitmap!=bitmap
    end
     
    def getRegularTile(sprite,id)
    if false
      sprite.bitmap=@tileset if !sprite.equal?(@tileset) || sprite.bitmap!=@tileset
      sprite.src_rect.set((id - 384) % 8 * 32, (id - 384) / 8 * 32,32,32)
    else
      bitmap=@regularTileInfo[id]
      if !bitmap
       bitmap=Bitmap.new(32,32)
       rect=Rect.new((id - 384) % 8 * 32, (id - 384) / 8 * 32,32,32)
       bitmap.blt(0,0,@tileset,rect)
       @regularTileInfo[id]=bitmap
      end
      sprite.bitmap=bitmap if !sprite.equal?(bitmap) || sprite.bitmap!=bitmap
    end
    end
     
    def addTile(tiles,count,xpos,ypos,id)
       if id>=384
         if count>=tiles.length
          sprite=Sprite.new(@viewport)
          tiles.push(sprite,0)
         else
          sprite=tiles[count]
          tiles[count+1]=0
         end
         sprite.visible=@visible
         sprite.x=xpos
         sprite.y=ypos
         getRegularTile(sprite,id)
         spriteZ=(@priorities[id]==0||!@priorities[id]) ? 0 : ypos+@priorities[id]*32+32
         sprite.z=spriteZ
         count+=2
       else
         if count>=tiles.length
          sprite=Sprite.new(@viewport)
          tiles.push(sprite,1)
         else
          sprite=tiles[count]
          tiles[count+1]=1
         end
         sprite.visible=@visible
         sprite.x=xpos
         sprite.y=ypos
         getAutotile(sprite,id)
         spriteZ=(@priorities[id]==0||!@priorities[id]) ? 0 : ypos+@priorities[id]*32+32
         sprite.z=spriteZ
         count+=2
       end
       return count
    end
     
    def refresh_tileset
    i=0;len=@regularTileInfo.length;while i<len
      if @regularTileInfo[i]
         @regularTileInfo[i].dispose
         @regularTileInfo[i]=nil
      end
      i+=1
    end
    @regularTileInfo.clear
    @priotiles.clear
    ysize=@map_data.ysize
    xsize=@map_data.xsize
    zsize=@map_data.zsize
    if xsize>100 || ysize>100
      @fullyrefreshed=false
    else
      for z in 0...zsize
       for y in 0...ysize
        for x in 0...xsize
         id = @map_data[x, y, z]
         next if id==0 || !@priorities[id]
         next if @priorities[id]==0
         @priotiles.push([x,y,z,id])
        end
       end
      end
      @fullyrefreshed=true
    end
    end
     
    def refresh_flash
    if @flash_data && !@flash
      @flash=Sprite.new(viewport)
      @flash.visible=true
      @flash.z=1
      @flash.blend_type=1
      @flash.bitmap=Bitmap.new(@viewport.rect.width*2,@viewport.rect.height*2)
      @firsttimeflash=true
    elsif !@flash_data && @flash
      @flash.bitmap.dispose if @flash.bitmap
      @flash.dispose
      @flash=nil
      @firsttimeflash=false
    end
    end
     
    def refresh_autotiles
    i=0;len=@autotileInfo.length;while i<len
      if @autotileInfo[i]
         @autotileInfo[i].dispose
         @autotileInfo[i]=nil
      end
      i+=1
    end
    i=0;len=@autosprites.length;while i<len
      if @autosprites[i]
         @autosprites[i].dispose
         @autosprites[i]=nil
      end
      i+=2
    end
    @autosprites.clear
    @autotileInfo.clear
    @prioautotiles.clear
    hasanimated=false
    for i in 0...7
      numframes=autotileNumFrames(48*(i+1))
      hasanimated=true if numframes>=2
      @framecount[i]=numframes
    end
    if hasanimated
      ysize=@map_data.ysize
      xsize=@map_data.xsize
      zsize=@map_data.zsize
      if xsize>100 || ysize>100
        @fullyrefreshedautos=false
      else
        for y in 0...ysize
         for x in 0...xsize
          haveautotile=false
          for z in 0...zsize
           id = @map_data[x, y, z]
           next if id==0 || id>=384 || @priorities[id]!=0 || !@priorities[id]
           next if @framecount[id/48-1]<2
           haveautotile=true
           break
          end
          @prioautotiles.push([x,y]) if haveautotile
         end
        end
        @fullyrefreshedautos=true
      end
    else
      @fullyrefreshedautos=true
    end
    end
     
    def map_data=(value)
    @map_data=value
    @tilesetChanged=true
    end
     
    def refreshFlashSprite
    return if !@flash || @flash_data.nil?
    ptX=@ox-@oxFlash
    ptY=@oy-@oyFlash
    if !@firsttimeflash && !@usedsprites &&
        ptX>=0 && ptX+@viewport.rect.width<=@flash.bitmap.width &&
        ptY>=0 && ptY+@viewport.rect.height<=@flash.bitmap.height
      @flash.ox=0
      @flash.oy=0
      @flash.src_rect.set(ptX.round,ptY.round,
         @viewport.rect.width,@viewport.rect.height)
      return
    end
    width=@flash.bitmap.width
    height=@flash.bitmap.height
    bitmap=@flash.bitmap
    ysize=@map_data.ysize
    xsize=@map_data.xsize
    zsize=@map_data.zsize
    @firsttimeflash=false
    @oxFlash=@ox-(width>>2)
    @oyFlash=@oy-(height>>2)
    @flash.ox=0
    @flash.oy=0
    @flash.src_rect.set(width>>2,height>>2,
         @viewport.rect.width,@viewport.rect.height)
    @flash.bitmap.clear
    @oxFlash=@oxFlash.floor
    @oyFlash=@oyFlash.floor
    xStart=(@oxFlash>>5)
    xStart=0 if xStart<0
    yStart=(@oyFlash>>5)
    yStart=0 if yStart<0
    xEnd=xStart+(width>>5)+1
    yEnd=yStart+(height>>5)+1
    xEnd=xsize if xEnd>=xsize
    yEnd=ysize if yEnd>=ysize
    if xStart<xEnd && yStart<yEnd
      yrange=yStart...yEnd
      xrange=xStart...xEnd
      tmpcolor=Color.new(0,0,0,0)
      for y in yrange
       ypos=(y<<5)-@oyFlash
       for x in xrange
         xpos=(x<<5)-@oxFlash
         id = @flash_data[x, y, 0]
         r=(id>>8)&15
         g=(id>>4)&15
         b=(id)&15
         tmpcolor.set(r*16,g*16,b*16)
         bitmap.fill_rect(xpos,ypos,32,32,tmpcolor)
       end
      end
    end
    end
     
     
    def refreshLayer0(autotiles=false)
    ptX=@ox-@oxLayer0
    ptY=@oy-@oyLayer0
    if !autotiles && !@firsttime && !@usedsprites &&
        ptX>=0 && ptX+@viewport.rect.width<=@layer0.bitmap.width &&
        ptY>=0 && ptY+@viewport.rect.height<=@layer0.bitmap.height
      if @layer0clip
       @layer0.ox=0
       @layer0.oy=0
       @layer0.src_rect.set(ptX.round,ptY.round,
         @viewport.rect.width,@viewport.rect.height)
      else
       @layer0.ox=ptX.round
       @layer0.oy=ptY.round
       @layer0.src_rect.set(0,0,@layer0.bitmap.width,@layer0.bitmap.height)
      end
      return true
    end
    width=@layer0.bitmap.width
    height=@layer0.bitmap.height
    bitmap=@layer0.bitmap
    ysize=@map_data.ysize
    xsize=@map_data.xsize
    zsize=@map_data.zsize
    if autotiles
      return true if @fullyrefreshedautos && @prioautotiles.length==0
      return true if !shown?
      xStart=(@oxLayer0>>5)
      xStart=0 if xStart<0
      yStart=(@oyLayer0>>5)
      yStart=0 if yStart<0
      xEnd=xStart+(width>>5)+1
      yEnd=yStart+(height>>5)+1
      xEnd=xsize if xEnd>xsize
      yEnd=ysize if yEnd>ysize
      return true if xStart>=xEnd || yStart>=yEnd
      trans=Color.new(0,0,0,0)
      temprect=Rect.new(0,0,0,0)
      tilerect=Rect.new(0,0,32,32)
      range=0...zsize
      overallcount=0
      count=0
      if !@fullyrefreshedautos
       for y in yStart..yEnd
        for x in xStart..xEnd
         haveautotile=false
         for z in range
          id = @map_data[x, y, z]
          next if id<48 || id>=384 || @priorities[id]!=0 || !@priorities[id]
          next if @framecount[id/48-1]<2
          haveautotile=true
          break
         end
         next if !haveautotile
         overallcount+=1
         xpos=(x<<5)-@oxLayer0
         ypos=(y<<5)-@oyLayer0
         bitmap.fill_rect(xpos,ypos,0,0,trans) if overallcount<=2000
         for z in range
          id = @map_data[x,y,z]
          next if id<48 || @priorities[id]!=0 || !@priorities[id]
          if overallcount>2000
           count=addTile(@autosprites,count,xpos,ypos,id)
           next
          elsif id>=384
           temprect.set((id - 384) % 8 * 32, (id - 384) / 8 * 32,32,32)
           bitmap.blt(xpos,ypos,@tileset,temprect)
          else
           tilebitmap=@autotileInfo[id]
           if !tilebitmap
            anim=autotileFrame(id)
            next if anim<0
            tilebitmap=Bitmap.new(32,32)
            bltAutotile(tilebitmap,0,0,id,anim)
            @autotileInfo[id]=tilebitmap
           end
           bitmap.blt(xpos,ypos,tilebitmap,tilerect)
          end
         end
        end
       end
      else
       for tile in @prioautotiles
        x=tile[0]
        y=tile[1]
        next if x<xStart||x>xEnd
        next if y<yStart||y>yEnd
        overallcount+=1
        xpos=(x<<5)-@oxLayer0
        ypos=(y<<5)-@oyLayer0
        bitmap.fill_rect(xpos,ypos,0,0,trans) if overallcount<=2000
        for z in range
         id = @map_data[x,y,z]
         next if id<48 || @priorities[id]!=0 || !@priorities[id]
         if overallcount>2000
          count=addTile(@autosprites,count,xpos,ypos,id)
          next
         elsif id>=384
          temprect.set((id - 384) % 8 * 32, (id - 384) / 8 * 32,32,32)
          bitmap.blt(xpos,ypos,@tileset,temprect)
         else
          tilebitmap=@autotileInfo[id]
          if !tilebitmap
            anim=autotileFrame(id)
            next if anim<0
            tilebitmap=Bitmap.new(32,32)
            bltAutotile(tilebitmap,0,0,id,anim)
            @autotileInfo[id]=tilebitmap
          end
          bitmap.blt(xpos,ypos,tilebitmap,tilerect)
         end
        end
       end
      end
      Graphics.frame_reset if overallcount>2000
      @usedsprites=false
      return true
    end
    return false if @usedsprites
    @firsttime=false
    @oxLayer0=@ox-(width>>2)
    @oyLayer0=@oy-(height>>2)
    if @layer0clip
      @layer0.ox=0
      @layer0.oy=0
      @layer0.src_rect.set(width>>2,height>>2,
         @viewport.rect.width,@viewport.rect.height)
    else
      @layer0.ox=(width>>2)
      @layer0.oy=(height>>2)
    end
    @layer0.bitmap.clear
    @oxLayer0=@oxLayer0.floor
    @oyLayer0=@oyLayer0.floor
    xStart=(@oxLayer0>>5)
    xStart=0 if xStart<0
    yStart=(@oyLayer0>>5)
    yStart=0 if yStart<0
    xEnd=xStart+(width>>5)+1
    yEnd=yStart+(height>>5)+1
    xEnd=xsize if xEnd>=xsize
    yEnd=ysize if yEnd>=ysize
    if xStart<xEnd && yStart<yEnd
      tmprect=Rect.new(0,0,0,0)
      yrange=yStart...yEnd
      xrange=xStart...xEnd
      for z in 0...zsize
       for y in yrange
        ypos=(y<<5)-@oyLayer0
        for x in xrange
         xpos=(x<<5)-@oxLayer0
         id = @map_data[x, y, z]
         next if id==0 || @priorities[id]!=0 || !@priorities[id]
         if id>=384
           tmprect.set((id - 384) % 8 * 32, (id - 384) / 8 * 32,32,32)
           bitmap.blt(xpos,ypos,@tileset,tmprect)
         else
           frame=autotileFrame(id)
           bltAutotile(bitmap,xpos,ypos,id,frame)
         end
        end
       end
      end
      Graphics.frame_reset
    end
    return true
    end
    def getResizeFactor
      return $ResizeFactor ? $ResizeFactor : 1.0
    end
    def ox=(val)
       val=(val*getResizeFactor).to_i
       val=(val/getResizeFactor).to_i
       wasshown=self.shown?
       @ox=val.floor
       @nowshown=(!wasshown && self.shown?)
    end
    def oy=(val)
       val=(val*getResizeFactor).to_i
       val=(val/getResizeFactor).to_i
       wasshown=self.shown?
       @oy=val.floor
       @nowshown=(!wasshown && self.shown?)
    end
    def visible=(val)
       wasshown=@visible
       @visible=val
       @nowshown=(!wasshown && val)
    end
    def refresh(autotiles=false)
    @oldOx=@ox
    @oldOy=@oy
    usesprites=false
    if @layer0
      @layer0.visible=@visible
      usesprites=!refreshLayer0(autotiles)
      if autotiles && !usesprites
       return
      end
    else
      usesprites=true
    end
    refreshFlashSprite
    vpx=@viewport.rect.x
    vpy=@viewport.rect.y
    vpr=@viewport.rect.width+vpx
    vpb=@viewport.rect.height+vpy
    xsize=@map_data.xsize
    ysize=@map_data.ysize
    minX=(@ox/32)-1
    maxX=((@ox+@viewport.rect.width)/32)+1
    minY=(@oy/32)-1
    maxY=((@oy+@viewport.rect.height)/32)+1
    minX=0 if minX<0
    minX=xsize-1 if minX>=xsize
    maxX=0 if maxX<0
    maxX=xsize-1 if maxX>=xsize
    minY=0 if minY<0
    minY=ysize-1 if minY>=ysize
    maxY=0 if maxY<0
    maxY=ysize-1 if maxY>=ysize
    count=0
    if minX<maxX && minY<maxY
      @usedsprites=usesprites || @usedsprites
      if @layer0
       @layer0.visible=false if usesprites
      end
      if @fullyrefreshed
       for prio in @priotiles
        next if prio[0]<minX||prio[0]>maxX
        next if prio[1]<minY||prio[1]>maxY
        id=prio[3]
        xpos=(prio[0]<<5)-@ox
        ypos=(prio[1]<<5)-@oy
        count=addTile(@tiles,count,xpos,ypos,id)
       end
      else
       for z in 0...@map_data.zsize
        for y in minY..maxY
         for x in minX..maxX
          id = @map_data[x, y, z]
          next if id==0 || !@priorities[id]
          next if @priorities[id]==0
          xpos=(x<<5)-@ox
          ypos=(y<<5)-@oy
          count=addTile(@tiles,count,xpos,ypos,id)
         end
        end
       end
      end
    end
    if count<@tiles.length
      bigchange=(count<=(@tiles.length*2/3)) && (@tiles.length*2/3)>25
      j=count;len=@tiles.length;while j<len
       sprite=@tiles[j]
       @tiles[j+1]=-1
       if bigchange
        sprite.dispose
        @tiles[j]=nil
        @tiles[j+1]=nil
       elsif !@tiles[j].disposed?
        sprite.visible=false if sprite.visible
       end
       j+=2
      end
      @tiles.compact! if bigchange
    end
    end
     
    end


    Спойлер Tilemap от RTH:
    Код:
    #==============================================================================
    # Tilemap
    #------------------------------------------------------------------------------
    # Author of THIS VERSION: RTH
    #==============================================================================
     
    class Tilemap
     
      # Autotile Update Rate
      AUTOTILE_UPDATE = 10
     
      # Autotiles Index (Dont Change these numbers)
      INDEX = [
        [ [27, 28, 33, 34], [ 5, 28, 33, 34], [27,  6, 33, 34], [ 5,  6, 33, 34],
          [27, 28, 33, 12], [ 5, 28, 33, 12], [27,  6, 33, 12], [ 5,  6, 33, 12] ],
        [ [27, 28, 11, 34], [ 5, 28, 11, 34], [27,  6, 11, 34], [ 5,  6, 11, 34],
          [27, 28, 11, 12], [ 5, 28, 11, 12], [27,  6, 11, 12], [ 5,  6, 11, 12] ],
        [ [25, 26, 31, 32], [25,  6, 31, 32], [25, 26, 31, 12], [25,  6, 31, 12],
          [15, 16, 21, 22], [15, 16, 21, 12], [15, 16, 11, 22], [15, 16, 11, 12] ],
        [ [29, 30, 35, 36], [29, 30, 11, 36], [ 5, 30, 35, 36], [ 5, 30, 11, 36],
          [39, 40, 45, 46], [ 5, 40, 45, 46], [39,  6, 45, 46], [ 5,  6, 45, 46] ],
        [ [25, 30, 31, 36], [15, 16, 45, 46], [13, 14, 19, 20], [13, 14, 19, 12],
          [17, 18, 23, 24], [17, 18, 11, 24], [41, 42, 47, 48], [ 5, 42, 47, 48] ],
        [ [37, 38, 43, 44], [37,  6, 43, 44], [13, 18, 19, 24], [13, 14, 43, 44],
          [37, 42, 43, 48], [17, 18, 47, 48], [13, 18, 43, 48], [ 1,  2,  7,  8] ]
      ]
     
      attr_accessor :ox
      attr_accessor :oy
      attr_accessor :tileset
      attr_accessor :autotiles
      attr_accessor :priorities
      attr_accessor :map_data
     
      def initialize(viewport)
        @viewport = viewport
        @swidth = (@viewport.rect.width / 32)
        @sheight = (@viewport.rect.height / 32)
        @data = nil
        @data_priorities = nil
        @priorities = nil
        @map_data = nil
        @tileset = nil
        @autotiles = []
        @sprites = {}
        @bitmaps = {}
        @cache = {}
        @need_counters = []
        @counter = []
        @max_counters = []
        @ox = 0
        @oy = 0
        @time_counter = 1
        @need_refresh = false
      end
     
      def refresh
        @need_refresh = false
        @table = Table.new(384)
        @size = 0
        @data = @map_data
        @data_priorities = @priorities
        min_x = [@ox / 32 - 1, 0].max
        min_y = [@oy / 32 - 1, 0].max
        max_x = [min_x + @swidth + 1 * 2, @data.xsize - 1].min
        max_y = [min_y + @sheight + 1 * 2, @data.ysize - 1].min
        (min_x..max_x).each {|x|
          (min_y..max_y).each {|y|
            (0..2).each {|z|
              tile_id = @data[x, y, z]
              next if tile_id == 0
              priority = @priorities[tile_id]
              key = [x, y, z, priority]
              @sprites[key] = Sprite.new(@viewport)
              @sprites[key].x = x * 32 - @ox
              @sprites[key].y = y * 32 - @oy
              @sprites[key].z = (y * 32 - @oy) + ((priority + 1) * 32)
              if tile_id < 384
                tile = get_autotile(tile_id)
              else
                tile = get_tile(tile_id)
              end
              @sprites[key].bitmap = tile
            }
          }
        }
      end
     
      def get_autotile(tile_id)
        if tile_id >= 384
          return get_tile(tile_id)
        end
        autotile = @autotiles[(tile_id/48) - 1]
        n = (autotile.width / 96)
        if @bitmaps[tile_id].nil?
          @bitmaps[tile_id] = []
        end
        if n > 1
          if @table[tile_id] == 0
            @counter[tile_id] = 0
            @max_counters[tile_id] = n
            @table[tile_id] = 1
            @size += 1
          end
        end
        for i in 0...n
          @bitmaps[tile_id][i] = get_tile(tile_id, i)
        end
        if @counter[tile_id].nil?
          return @bitmaps[tile_id][0]
        end
        return @bitmaps[tile_id][@counter[tile_id]]
      end
     
      def get_tile(tile_id, c=0)
        key = [tile_id, c]
        if @cache[key].nil?
          @cache[key] = Bitmap.new(32, 32)
          if tile_id < 384
            anim = c * 96
            id = tile_id % 48
            tiles = INDEX[id >> 3][id % 8]
            autotile = @autotiles[(tile_id/48) - 1]
            for i in 0...4
              tile_position = tiles[i] - 1
              rect = Rect.new(tile_position % 6 * 16 + anim, tile_position / 6 * 16, 16, 16)
              @cache[key].blt((i%2) * 16, (i/2) * 16, autotile, rect)
            end
          else
            @cache[key].blt(0, 0, @tileset, Rect.new((tile_id - 384) % 8 * 32, (tile_id - 384) / 8 * 32, 32, 32))
          end
        end
        return @cache[key]
      end
     
      def tileset=(valor)
        for v in @cache.values
          v.dispose
        end
        @cache = {}
        @tileset = valor
        @need_refresh = true
      end
     
      def dispose
        @cache.values.each {|v| v.dispose }
        @cache = {}
        @bitmaps.values.each {|a|
          next if a.nil?
          a.each {|v|
            next if v.nil?
            v.dispose unless v.disposed?
          }
        }
        @sprites.values.each {|sprite|
          next if sprite.nil?
          sprite.dispose
        }
        @sprites = {}
      end
     
      def update
        if @data != @map_data or @priorities != @data_priorities or @need_refresh
          refresh
          return
        end
        min_x = [@ox / 32 - 1, 0].max
        min_y = [@oy / 32 - 1, 0].max
        max_x = [min_x + @swidth + 1 * 2, @data.xsize - 1].min
        max_y = [min_y + @sheight + 1 * 2, @data.ysize - 1].min
        if AUTOTILE_UPDATE > 0
          @time_counter = (@time_counter + 1) % AUTOTILE_UPDATE
        end
        checked = []
        for x in min_x..max_x
          rx = ((x * 32) - @ox)
          cx = (rx < 0 or rx > (@swidth - 32))
          ax = (((rx + 32) < 0) or (rx > @viewport.rect.width))
          for y in min_y..max_y
            ry = ((y * 32) - @oy)
            cy = (ry < 0 or ry > (@sheight - 32))
            ay = (((ry + 32) < 0) or (ry > @viewport.rect.height))
            for z in 0..2
              tile_id = @data[x, y, z]
              next if tile_id == 0
              priority = @priorities[tile_id]
              key = [x, y, z, priority]
              if ay or ax
                if @sprites[key] != nil
                  @sprites[key].dispose
                  @sprites[key] = nil
                end
                next
              end
              if @sprites[key].nil?
                @sprites[key] = Sprite.new(@viewport)
                @sprites[key].x = rx
                @sprites[key].y = ry
                @sprites[key].z = ry + ((priority + 1) * 32)
                if tile_id < 384
                  tile = get_autotile(tile_id)
                else
                  tile = get_tile(tile_id)
                end
                @sprites[key].bitmap = tile
                next
              end
              if @changed_ox or cy
                if @sprites[key].x != rx
                  @sprites[key].x = rx
                end
              end
              if @changed_oy or cx
                if @sprites[key].y != ry
                  @sprites[key].y = ry
                  @sprites[key].z = ry + ((priority + 1) * 32)
                end
              end
              next if (@time_counter != 0)
              if @table[tile_id] == 1
                if checked[tile_id] != true
                  @counter[tile_id] = (@counter[tile_id] + 1) % @max_counters[tile_id]
                  checked[tile_id] = true
                end
                @sprites[key].bitmap = @bitmaps[tile_id][@counter[tile_id]]
              end
            end
          end
        end
        if @changed_ox
          @changed_ox = false
        end
        if @changed_oy
          @changed_oy = false
        end
      end
     
      def ox=(valor)
        return if @ox == valor
        @ox = valor
        @changed_ox = true
      end
     
      def oy=(valor)
        return if @oy == valor
        @oy = valor
        @changed_oy = true
      end
     
    end


    Спойлер Audio на основе FMODex от Cowlol:
    Спойлер FModEx:
    Код:
    #==============================================================================
    # ** FMOD Ex Audio
    #------------------------------------------------------------------------------
    #  Script by            :   Hiretsukan (Kevin Gadd)
    #                           janus@luminance.org
    #  Modified by:         :   RPG/Cowlol (Firas Assad)
    #                           ArePeeGee (AIM name)
    #  Last Update          :   September 23rd, 2008
    #  Version              :   1.5
    #------------------------------------------------------------------------------
    # A rewrite of the built-in Audio module to extend its functionality
    # using the FMOD Ex library (http://www.fmod.org/). In particular,
    # it supports several new formats and has the ability to get current
    # playing position and seek to a new one, and to set loop points.
    # Extensions to Game_System and Scene_Battle makes the memorize BGM/BGS
    # event command remember position as well, and map music to resume
    # playing from the position it stopped at before a battle, instead
    # of starting all over like default RMXP behavior.
    #------------------------------------------------------------------------------
    # Usage:
    #
    # You need to copy the file fmodex.dll to your game folder (folder where
    # your Game.exe and project file are). I've provided the DLL with the
    # demo, and you can also get the latest version from FMOD's official
    # website (http://www.fmod.org/index.php/download).
    #
    # There are three scripts that could be used, the real script is only
    # the first one, but the other scripts are provided for convenience:
    #
    # FModEx 
    #           This includes the core functionality and the Audio module
    #           rewrite. It's the main script and should be placed on
    #           top of all scripts using it. Most people would just add a 
    #           new section above the Game_System or Game_Temp section at 
    #           the very top of the script editor and paste script there.
    # Game_System
    #           This script rewrites parts of the Game_System class to 
    #           allow memorize_bgm to support remembering position,
    #           and to allow the play methods to take an extra parameter
    #           specifying position to start playing from.
    #           Add it anywhere under the Game_System script section.
    #           If you don't need any of these features you don't need this
    #           script.
    # Other Classes
    #           This is really an optional script that modifies the default
    #           battle system to memorize map BGM and position instead of
    #           starting over after the battle. Add it anywhere under
    #           the Scene_Battle classes.
    #
    # Aside from integration with RMXP's default scripts, you can use
    # this script the same way you used the old Audio class You could also
    # access the FMod module for more options at a lower level, such as
    # setting loop points (FMod.bgm_set_loop_points(first, second) in
    # milliseconds) and getting current BGS position (FMod.bgs_position) to
    # name a few.
    #------------------------------------------------------------------------------
    # Compatibility:
    #
    # This script has different levels of compatibility depending on what
    # scripts you use:
    #
    # FModEx 
    #           This script overwrites the Audio module, but since
    #           few if any other scripts would do that, it's pretty
    #           much compatible with the majority of other scripts
    #           without modifications.
    # Game_System
    #           The following methods of Game_System are redefined:
    #             bgm_play, bgm_restore, bgs_play, bgs_restore
    #           Any other script that redefines these methods may
    #           not work well with this one. Most scripts wouldn't
    #           do that, except ones dealing with audio as well.
    #           I've marked the areas in the script where I made
    #           changes to help you resolve any conflicts
    #           The following methods of Game_System are aliased:
    #             bgm_memorize, bgs_memorize
    #           Other scripts that redefine these methods should
    #           be placed above the Game_System script.
    # Other Classes
    #           The following methods of Scene_Battle are redefined:
    #             judge, start_phase5, update_phase5
    #           Any other script that redefines these methods may
    #           not work well with this one. Custom battle system
    #           scripts may do that, so you might consider not
    #           including the Other Classes section and just
    #           manually add position memorizing functionality.
    #           I've marked the areas in the script where I made
    #           changes to help you resolve any conflicts
    #           The following methods of Game_Temp/Scene_Map are aliased:
    #             Game_Temp#initialize, Scene_Map #call_battle
    #           Other scripts that redefine these methods should
    #           be placed above the Other Classes script.
    #
    # So in other words, FModEx is the most compatible, followed by
    # Game_System, and finally the last and optional Other Classes. 
    # If you use a custom battle system you probably shouldn't include
    # Other Classes anyway. This isn't an SDK script and I never tested
    # it with the SDK. It'll probably work, but refer to SDK documentation
    # for more information.
    #------------------------------------------------------------------------------
    # Version Info:
    #   - Version 1.5:
    #       - Made the Volume and Pitch paramters to Audio ME/SE playing
    #         methods optional. (Thanks panchokoster!)
    #       - Script now uses FMOD's software mixer instead of hardware
    #         acceleration. This solves issues with certain sound cards.
    #       - A message is now displayed when a file isn't found, instead
    #         of just throwing the error number.
    #       - Added an independent thread to handle updating Audio module,
    #         instead of calling it in $game_system#update. This should
    #         ensure that it's called in all scenes. (Thanks Zeriab!)
    #       - Updated fading calculations to depend on seconds instead
    #         of game frames.
    #   - Version 1.4:
    #       - Fixed a bug where file isn't found if RTP path doesn't end
    #         with a backslash (thanks Atoa!).
    #       - Added BGM fading in after a ME is done playing to mimic
    #         original Audio class behavior, the volume increment when
    #         fading is specified by constant Audio::BGM_FADE_IN_INCREMENT.
    #       - Several minor bug fixes and minor behavior changes relating
    #         to fading out and stopping sounds.
    #   - Version 1.3:
    #       - Fixed a bug with ME fading out.
    #       - Added methods to get BGM and BGS length.
    #       - Providing -1 as loop end point to set_loop_points methods
    #         makes the sound file's end the loop end point.
    #       - Changed implementation of set_loop_points a bit.
    #       - Position of BGM/BGS to be played after fading is now
    #         remembered instead of starting all over.
    #   - Version 1.2:
    #       - Fully documented the script, and fixed some bugs.
    #       - Completely rewrote Audio module, allowing FMOD to handle
    #         BGMs, BGSs, MEs, and SEs except of just BGMs.
    #       - Fixed RTP reading to use registry instead of special files..
    #   - Version 1.1:
    #       - Added position tracking and adjusting.
    #       - Added loop point support.
    #       - Implemented BGM fading.
    #   - Version 1.0:
    #       - Hiretsukan (Kevin Gadd)'s initial release.
    #------------------------------------------------------------------------------
    # Known bugs:
    #
    #   - MIDI abrupt start when seeking or restoring from position
    #   - Found a bug or have some ideas for the next version? Please tell me!
    #------------------------------------------------------------------------------
    # Terms of Use:
    #
    #   Use of this script is subject to the permissive BSD-like license below.
    #   That basically means you could use it in any way you like as long
    #   as you keep the following copyright and license unchanged and available,
    #   and don't use name of copyright holder to promote products based on
    #   this software. Note, however, that this license only applies to the
    #   script, and not to the FMOD library. For more information about FMOD
    #   licenses consult FMOD website: http://www.fmod.org/index.php/sales
    #   It's free for non-commercial use, and they provide several types
    #   of licenses for different types of developers.
    #
    # Copyright (c) 2005, Kevin Gadd
    # All rights reserved.
    #
    # Redistribution and use in source and binary forms, with or without
    # modification, are permitted provided that the following conditions are met:
    #     * Redistributions of source code must retain the above copyright
    #       notice, this list of conditions and the following disclaimer.
    #     * Redistributions in binary form must reproduce the above copyright
    #       notice, this list of conditions and the following disclaimer in the
    #       documentation and/or other materials provided with the distribution.
    #     * The name of the contributors may not be used to endorse or promote 
    #       products derived from this software without specific prior written 
    #       permission.
    #
    # THIS SOFTWARE IS PROVIDED BY Kevin Gadd ''AS IS'' AND ANY
    # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    # DISCLAIMED. IN NO EVENT SHALL Kevin Gadd BE LIABLE FOR ANY
    # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    #==============================================================================
    
    #==============================================================================
    # ** FModEx
    #------------------------------------------------------------------------------
    #  FMOD Ex binding by Kevin Gadd (janus@luminance.org)
    #==============================================================================
    
    module FModEx
      #--------------------------------------------------------------------------
      # * Constants
      #--------------------------------------------------------------------------
      # FMOD_INITFLAGS flags
      FMOD_INIT_NORMAL = 0
      # FMOD_RESULT flags
      FMOD_OK = 0
      FMOD_ERR_CHANNEL_STOLEN = 11
      FMOD_ERR_FILE_NOT_FOUND = 23
      FMOD_ERR_INVALID_HANDLE = 36
      # FMOD_MODE flags
      FMOD_DEFAULT = 0
      FMOD_LOOP_OFF = 1
      FMOD_LOOP_NORMAL = 2
      FMOD_LOOP_BIDI = 4
      FMOD_LOOP_BITMASK = 7
      FMOD_2D = 8
      FMOD_3D = 16
      FMOD_HARDWARE = 32
      FMOD_SOFTWARE = 64
      FMOD_CREATESTREAM = 128
      FMOD_CREATESAMPLE = 256
      FMOD_OPENUSER = 512
      FMOD_OPENMEMORY = 1024
      FMOD_OPENRAW = 2048
      FMOD_OPENONLY = 4096
      FMOD_ACCURATETIME = 8192
      FMOD_MPEGSEARCH = 16384
      FMOD_NONBLOCKING = 32768
      FMOD_UNIQUE = 65536
      # The default mode that the script uses
      FMOD_DEFAULT_SOFTWARWE = FMOD_LOOP_OFF | FMOD_2D | FMOD_SOFTWARE
      # FMOD_CHANNELINDEX flags
      FMOD_CHANNEL_FREE = -1
      FMOD_CHANNEL_REUSE = -2
      # FMOD_TIMEUNIT_flags
      FMOD_TIMEUNIT_MS = 1
      FMOD_TIMEUNIT_PCM = 2
      # The default time unit the script uses
      FMOD_DEFAULT_UNIT = FMOD_TIMEUNIT_MS
      # Types supported by FMOD Ex
      FMOD_FILE_TYPES = ['ogg', 'aac', 'wma', 'mp3', 'wav', 'it', 'xm', 'mod', 's3m', 'mid', 'midi']
      
      #============================================================================
      # ** DLL
      #----------------------------------------------------------------------------
      #  A class that manages importing functions from the DLL
      #============================================================================
      
      class DLL
        #--------------------------------------------------------------------------
        # * Public Instance Variables
        #--------------------------------------------------------------------------
        attr_accessor :filename           # DLL file name for instance    
        attr_accessor :functions          # hash of functions imported (by name)
        #--------------------------------------------------------------------------
        # * Object Initialization
        #     filename  : Name of the DLL
        #--------------------------------------------------------------------------
        def initialize(filename = 'fmodex.dll')
          @filename = filename
          @functions = {}
          @handle = 0            # Handle to the DLL
          # Load specified library into the address space of game process
          w32_LL = Win32API.new('kernel32.dll', 'LoadLibrary', 'p', 'l')
          @handle = w32_LL.call(filename)
          # System functions:
          self.import('System_Create', 'p')
          self.import('System_Init', 'llll')
          self.import('System_Close', 'l')
          self.import('System_Release', 'l')
          self.import('System_CreateSound', 'lpllp')
          self.import('System_CreateStream', 'lpllp')
          self.import('System_PlaySound', 'llllp')
          # Sound functions:
          self.import('Sound_Release', 'l')
          self.import('Sound_GetMode', 'lp')
          self.import('Sound_SetMode', 'll')
          self.import('Sound_SetLoopPoints', 'lllll')
          self.import('Sound_GetLength', 'lpl')
          # Channel functions:
          self.import('Channel_Stop', 'l')
          self.import('Channel_IsPlaying', 'lp')
          self.import('Channel_GetPaused', 'lp')
          self.import('Channel_SetPaused', 'll')
          self.import('Channel_GetVolume', 'lp')
          self.import('Channel_SetVolume', 'll')
          self.import('Channel_GetPan', 'lp')
          self.import('Channel_SetPan', 'll')
          self.import('Channel_GetFrequency', 'lp')
          self.import('Channel_SetFrequency', 'll')
          self.import('Channel_GetPosition', 'lpl')
          self.import('Channel_SetPosition', 'lll')
        end
        #--------------------------------------------------------------------------
        # * Create a Win32API Object And Add it to Hashtable
        #     name      : Function name
        #     args      : Argument types (p = pointer, l = int, v = void)
        #     returnType: Type of value returned by function
        #--------------------------------------------------------------------------
        def import(name, args = '', returnType = 'l')
          @functions[name] = Win32API.new(@filename, 'FMOD_' + name, args, returnType)
        end
        #--------------------------------------------------------------------------
        # * Get Function by Name
        #     key       : Function name
        #--------------------------------------------------------------------------
        def [](key)
          return @functions[key]
        end
        #--------------------------------------------------------------------------
        # * Call a Function With Passed Arguments
        #     name      : Function name
        #     args      : Argument to function
        #--------------------------------------------------------------------------
        def invoke(name, *args)
          fn = @functions[name]
          raise "function not imported: #{name}" if fn.nil?
          result = fn.call(*args)
          unless result == FMOD_OK or result == FMOD_ERR_CHANNEL_STOLEN or
            result == FMOD_ERR_FILE_NOT_FOUND
            raise "FMOD Ex returned error #{result}"
          end
          return result
        end
        #--------------------------------------------------------------------------
        # * Store Float as Binary Int Because Floats Can't be Passed Directly
        #     f         : Float to convert
        #--------------------------------------------------------------------------
        def convertFloat(f)
          # First pack the float in a string as a native binary float
          temp = [f].pack('f')
          # Then unpack the native binary float as an integer
          return unpackInt(temp)
        end
        #--------------------------------------------------------------------------
        # * Unpack Binary Data to Integer
        #     s         : String containing binary data
        #--------------------------------------------------------------------------
        def unpackInt(s)
          return s.unpack('l')[0]
        end
        #--------------------------------------------------------------------------
        # * Unpack Binary Data to Float
        #     s         : String containing binary data
        #--------------------------------------------------------------------------
        def unpackFloat(s)
          return s.unpack('f')[0]
        end
        #--------------------------------------------------------------------------
        # * Unpack Binary Data to Boolean
        #     s         : String containing binary data
        #--------------------------------------------------------------------------
        def unpackBool(s)
          return s.unpack('l')[0] != 0
        end
      end
    
      #============================================================================
      # ** System
      #----------------------------------------------------------------------------
      #  A class that manages an instance of FMOD::System
      #============================================================================
      
      class System
        #--------------------------------------------------------------------------
        # * Public Instance Variables
        #-------------------------------------------------------------------------- 
        attr_accessor :fmod               # Instance of DLL class (fmodex.dll)
        attr_accessor :handle             # Handle (pointer) to System object
        attr_accessor :maxChannels        # Maximum number of channels
        #--------------------------------------------------------------------------
        # * Object Initialization
        #     fmod            : An instance of DLL class
        #     maxChannels     : Maximum number of used channels
        #     flags           : FMOD_INITFLAGS
        #     extraDriverData : Driver specific data
        #--------------------------------------------------------------------------
        def initialize(theDLL, maxChannels = 32, flags = FMOD_INIT_NORMAL, extraDriverData = 0)
          @fmod = theDLL
          @maxChannels = maxChannels
          # Create and initialize FMOD::System
          temp = 0.chr * 4
          @fmod.invoke('System_Create', temp)
          @handle = @fmod.unpackInt(temp)
          @fmod.invoke('System_Init', @handle, maxChannels, flags, extraDriverData)
        end
        #--------------------------------------------------------------------------
        # * Create FMOD::Sound (fully loaded into memory by default)
        #     filename        : Name of file to open
        #     mode            : FMOD_MODE flags
        #--------------------------------------------------------------------------
        def createSound(filename, mode = FMOD_DEFAULT_SOFTWARWE)
          # Create sound and return it
          temp = 0.chr * 4
          result = @fmod.invoke('System_CreateSound', @handle, filename, mode, 0, temp)
          raise "File not found: \"#{filename}\"" if result == FMOD_ERR_FILE_NOT_FOUND
          newSound = Sound.new(self, @fmod.unpackInt(temp))
          return newSound
        end
        #--------------------------------------------------------------------------
        # * Create Streamed FMOD::Sound (chunks loaded on demand)
        #     filename        : Name of file to open
        #     mode            : FMOD_MODE flags
        #--------------------------------------------------------------------------
        def createStream(filename, mode = FMOD_DEFAULT_SOFTWARWE)
          # Create sound and return it
          temp = 0.chr * 4
          result = @fmod.invoke('System_CreateStream', @handle, filename, mode, 0, temp)
          raise "File not found: \"#{filename}\"" if result == FMOD_ERR_FILE_NOT_FOUND
          newSound = Sound.new(self, @fmod.unpackInt(temp))
          return newSound
        end
        #--------------------------------------------------------------------------
        # * Close And Release System
        #--------------------------------------------------------------------------
        def dispose
          if (@handle > 0)
            @fmod.invoke('System_Close', @handle)
            @fmod.invoke('System_Release', @handle)
            @handle = 0
          end
          @fmod = nil
        end
      end
    
      #============================================================================
      # ** Sound
      #----------------------------------------------------------------------------
      #  A class that manages an instance of FMOD::Sound
      #============================================================================
      
      class Sound
        #--------------------------------------------------------------------------
        # * Public Instance Variables
        #-------------------------------------------------------------------------- 
        attr_accessor :system             # System that created this Sound
        attr_accessor :fmod               # Instance of DLL class (fmodex.dll)
        attr_accessor :handle             # Handle (pointer) to Sound object
        #--------------------------------------------------------------------------
        # * Object Initialization
        #     theSystem       : The System that created this Sound object
        #     handle          : Handle to the FMOD::Sound object
        #--------------------------------------------------------------------------
        def initialize(theSystem, theHandle)
          @system = theSystem
          @fmod = theSystem.fmod
          @handle = theHandle
        end
        #--------------------------------------------------------------------------
        # * Play Sound
        #     paused          : Start paused?
        #     channel         : Channel allocated to sound (nil for automatic)
        #--------------------------------------------------------------------------
        def play(paused = false, channel = nil)
          # If channel wasn't specified, let FMOD pick a free one,
          # otherwise use the passed channel (id from 0 to maxChannels)
          unless channel
            temp = 0.chr * 4
          else
            temp = [channel].pack('l')
          end
          @fmod.invoke('System_PlaySound', @system.handle, 
                    (channel == nil) ? FMOD_CHANNEL_FREE : FMOD_CHANNEL_REUSE, 
                    @handle,
                    (paused == true) ? 1 : 0, 
                    temp)
          theChannel = @fmod.unpackInt(temp)
          # Create a Channel object based on returned channel
          newChannel = Channel.new(self, theChannel)
          return newChannel
        end
        #--------------------------------------------------------------------------
        # * Get FMOD_MODE Bits
        #--------------------------------------------------------------------------
        def mode
          temp = 0.chr * 4
          @fmod.invoke('Sound_GetMode', @handle, temp)
          return @fmod.unpackInt(temp)
        end
        #--------------------------------------------------------------------------
        # * Set FMOD_MODE Bits
        #--------------------------------------------------------------------------
        def mode=(newMode)
          @fmod.invoke('Sound_SetMode', @handle, newMode)
        end
        #--------------------------------------------------------------------------
        # * Get FMOD_LOOP_MODE
        #--------------------------------------------------------------------------  
        def loopMode
          temp = 0.chr * 4
          @fmod.invoke('Sound_GetMode', @handle, temp)
          return @fmod.unpackInt(temp) & FMOD_LOOP_BITMASK
        end
        #--------------------------------------------------------------------------
        # * Set FMOD_LOOP_MODE
        #--------------------------------------------------------------------------  
        def loopMode=(newMode)
          @fmod.invoke('Sound_SetMode', @handle, (self.mode & ~FMOD_LOOP_BITMASK) | newMode)
        end
        #--------------------------------------------------------------------------
        # * Return Sound Length
        #-------------------------------------------------------------------------- 
        def length(unit = FMOD_DEFAULT_UNIT)
          temp = 0.chr * 4
          @fmod.invoke('Sound_GetLength', @handle, temp, unit)
          return @fmod.unpackInt(temp)
        end
        #--------------------------------------------------------------------------
        # * Set Loop Points
        #     first           : Loop start point in milliseconds
        #     second          : Loop end point in milliseconds
        #     unit            : FMOD_TIMEUNIT for points
        #--------------------------------------------------------------------------    
        def setLoopPoints(first, second, unit = FMOD_DEFAULT_UNIT)
          @fmod.invoke('Sound_SetLoopPoints', @handle, first, unit, second, unit)
        end
        #--------------------------------------------------------------------------
        # * Release Sound
        #-------------------------------------------------------------------------- 
        def dispose
          if (@handle > 0)
            @fmod.invoke('Sound_Release', @handle)
            @handle = 0
          end
          @fmod = nil
          @system = nil
        end
      end
    
      #============================================================================
      # ** Channel
      #----------------------------------------------------------------------------
      #  A class that represents an FMOD::Channel
      #============================================================================
      
      class Channel
        #--------------------------------------------------------------------------
        # * Public Instance Variables
        #-------------------------------------------------------------------------- 
        attr_accessor :system             # System that created the Sound
        attr_accessor :sound              # Sound using the Channel
        attr_accessor :fmod               # Instance of DLL class (fmodex.dll)
        attr_accessor :handle             # Handle (pointer) to Sound object
        #--------------------------------------------------------------------------
        # * Object Initialization
        #     theSound        : The Sound using this Channel object
        #     handle          : Handle to the FMOD::Channel object
        #--------------------------------------------------------------------------
        def initialize(theSound, theHandle)
          @sound = theSound
          @system = theSound.system
          @fmod = theSound.system.fmod
          @handle = theHandle
        end
        #--------------------------------------------------------------------------
        # * Stop Channel and Make it Available for Other Sounds
        #--------------------------------------------------------------------------
        def stop
          @fmod.invoke('Channel_Stop', @handle)
        end
        #--------------------------------------------------------------------------
        # * Is the Channel Handle Valid?
        #--------------------------------------------------------------------------
        def valid?
          temp = 0.chr * 4
          begin
            result = @fmod.invoke('Channel_IsPlaying', @handle, temp)
          rescue
            if (result == FMOD_ERR_INVALID_HANDLE)
              return false
            else
              raise
            end
          end
          # If we get here then it's valid
          return true
        end
        #--------------------------------------------------------------------------
        # * Is the Channel Playing?
        #--------------------------------------------------------------------------
        def playing?
          temp = 0.chr * 4
          @fmod.invoke('Channel_IsPlaying', @handle, temp)
          return @fmod.unpackBool(temp)
        end
        #--------------------------------------------------------------------------
        # * Get Channel Volume Level (0.0 -> 1.0)
        #--------------------------------------------------------------------------
        def volume
          temp = 0.chr * 4
          @fmod.invoke('Channel_GetVolume', @handle, temp)
          return @fmod.unpackFloat(temp)
        end
        #--------------------------------------------------------------------------
        # * Set Channel Volume Level (0.0 -> 1.0)
        #--------------------------------------------------------------------------
        def volume=(newVolume)
          @fmod.invoke('Channel_SetVolume', @handle, @fmod.convertFloat(newVolume))
        end
        #--------------------------------------------------------------------------
        # * Get Channel Pan Position (-1.0 -> 1.0)
        #--------------------------------------------------------------------------
        def pan
          temp = 0.chr * 4
          @fmod.invoke('Channel_GetPan', @handle, temp)
          return @fmod.unpackFloat(temp)
        end
        #--------------------------------------------------------------------------
        # * Set Channel Pan Position (-1.0 -> 1.0)
        #--------------------------------------------------------------------------
        def pan=(newPan)
          @fmod.invoke('Channel_SetPan', @handle, @fmod.convertFloat(newPan))
        end
        #--------------------------------------------------------------------------
        # * Get Channel Frequency in HZ (Speed/Pitch)
        #--------------------------------------------------------------------------
        def frequency
          temp = 0.chr * 4
          @fmod.invoke('Channel_GetFrequency', @handle, temp)
          return @fmod.unpackFloat(temp)
        end
        #--------------------------------------------------------------------------
        # * Set Channel Frequency in HZ (Speed/Pitch)
        #--------------------------------------------------------------------------
        def frequency=(newFrequency)
          @fmod.invoke('Channel_SetFrequency', @handle, @fmod.convertFloat(newFrequency))
        end
        #--------------------------------------------------------------------------
        # * Is Channel Paused?
        #--------------------------------------------------------------------------
        def paused
          temp = 0.chr * 4
          @fmod.invoke('Channel_GetPaused', @handle, temp)
          return @fmod.unpackBool(temp)
        end
        #--------------------------------------------------------------------------
        # * Pause Channel
        #--------------------------------------------------------------------------
        def paused=(newPaused)
          @fmod.invoke('Channel_SetPaused', @handle, (newPaused == true) ? 1 : 0)
        end
        #--------------------------------------------------------------------------
        # * Get Current Playback Position
        #     unit            : FMOD_TIMEUNIT to return position in
        #--------------------------------------------------------------------------   
        def position(unit = FMOD_DEFAULT_UNIT)
          temp = 0.chr * 4
          @fmod.invoke('Channel_GetPosition', @handle, temp, unit)
          return @fmod.unpackInt(temp)
        end
        #--------------------------------------------------------------------------
        # * Set Current Playback Position
        #     newPosition     : New playback position
        #     unit            : FMOD_TIMEUNIT to use when setting position
        #--------------------------------------------------------------------------    
        def position=(newPosition, unit = FMOD_DEFAULT_UNIT)
          @fmod.invoke('Channel_SetPosition', @handle, newPosition, unit)
        end
        #--------------------------------------------------------------------------
        # * Dispose of Channel
        #--------------------------------------------------------------------------  
        def dispose
          @handle = 0
          @sound = nil
          @system = nil
          @fmod = nil
        end
      end
      
    end
    
    #==============================================================================
    # ** FMod
    #------------------------------------------------------------------------------
    #  A higher level module to access FMOD Ex
    #==============================================================================
    
    module FMod
      
      #============================================================================
      # ** SoundFile
      #----------------------------------------------------------------------------
      #  Represents a Sound file (BGM, BGS, SE, etc.) and associated Channel
      #============================================================================
      
      class SoundFile
        #--------------------------------------------------------------------------
        # * Public Instance Variables
        #--------------------------------------------------------------------------
        attr_accessor :name                     # File name
        attr_accessor :sound                    # FModEx::Sound object
        attr_accessor :channel                  # Channel playing sound
        attr_accessor :volume                   # Volume in RPG::AudioFile format
        attr_accessor :pitch                    # Pitch in RPG::AudioFile format
        attr_accessor :looping                  # Sound loops
        attr_accessor :streaming                # Sound is streamed
        attr_accessor :length                   # Sound length in milliseconds
        #--------------------------------------------------------------------------
        # * Object Initialization
        #--------------------------------------------------------------------------
        def initialize(name, sound, channel, volume, pitch, looping, streaming, length)
          @name = name
          @sound = sound
          @channel = channel
          @volume = volume
          @pitch = pitch
          @looping = looping
          @streaming = streaming
          @length = length
        end
      end
      #--------------------------------------------------------------------------
      # * Instance Variables
      #--------------------------------------------------------------------------
      @fmod_dll = FModEx::DLL.new               # The FMOD Ex DLL
      @fmod = FModEx::System.new(@fmod_dll)     # The global System object
      @fmod_se = []                             # Array of Sound Effects
      @rtp_folder = nil                         # Name of RTP folder
      #--------------------------------------------------------------------------
      # * Get Path of RTP Folder From Registry
      #-------------------------------------------------------------------------- 
      def self.getRTPFolder
        if @rtp_folder
          return @rtp_folder
        end
        open_key = Win32API.new('advapi32.dll', 'RegOpenKeyExA', 'LPLLP', 'L')
        query_value = Win32API.new('advapi32.dll', 'RegQueryValueExA', 'LPLPPP', 'L')
        close_key = Win32API.new('advapi32', 'RegCloseKey', 'L', 'L')
        key = 0.chr * 4
        # Open a HKEY_LOCAL_MACHINE with KEY_READ attribute and save handle in key
        open_key.call(0x80000002, 'Software\Enterbrain\RGSS\RTP', 0, 0x20019, key)
        key = @fmod_dll.unpackInt(key)
        type = 0.chr * 4
        size = 0.chr * 4
        # Query to get string size
        query_value.call(key, 'Standard', 0, type, 0, size)
        data = ' ' * @fmod_dll.unpackInt(size)
        # Query the string value itself using size
        query_value.call(key, 'Standard', 0, type, data, size)
        @rtp_folder = data.chop
        close_key.call(key)
        # Make sure the directory ends with a backslash
        @rtp_folder += "\\" if @rtp_folder[-1].chr != "\\"
        return @rtp_folder
      end
      #--------------------------------------------------------------------------
      # * Return Proper File Name (With Extensions)
      #     name            : Name of the file
      #     extensions      : Extensions to add to file name
      #-------------------------------------------------------------------------- 
      def self.checkExtensions(name, extensions)
        if FileTest.exist?(name)
          return name
        end
        # Add extension if needed
        extensions.each do |ext|
          if FileTest.exist?(name + '.' + ext)
            return name + '.' + ext
          end
        end
        # File doesn't exist
        return name
      end
      #--------------------------------------------------------------------------
      # * Get Valid File Name
      #     name            : Name of the file
      #-------------------------------------------------------------------------- 
      def self.selectBGMFilename(name)
        name = name.gsub("/", "\\")
        # See if file exists in game folder
        localname = self.checkExtensions(name, FModEx::FMOD_FILE_TYPES)
        # See if file exists in RTP
        commonname = self.checkExtensions(getRTPFolder + name, FModEx::FMOD_FILE_TYPES)
        if FileTest.exist?(localname)
          return localname
        end
        if FileTest.exist?(commonname)
          return commonname
        end
        # An invalid name was provided
        return name
      end
      #--------------------------------------------------------------------------
      # * Play a Sound File Then Return it
      #     name            : Name of the file
      #     volume          : Channel volume
      #     pitch           : Channel frequency
      #     position        : Starting position in milliseconds
      #     looping         : Does the sound loop?
      #     streaming       : Stream sound or load whole thing to memory?
      #-------------------------------------------------------------------------- 
      def self.play(name, volume, pitch, position, looping, streaming)
        # Get a valid file name
        filename = self.selectBGMFilename(name)
        # Create Sound or Stream and set initial values
        sound = streaming ? @fmod.createStream(filename) : @fmod.createSound(filename)
        sound.loopMode = looping ? FModEx::FMOD_LOOP_NORMAL : FModEx::FMOD_LOOP_OFF
        channel = sound.play
        volume = volume * 1.0
        pitch = pitch * 1.0
        file_length = sound.length(FModEx::FMOD_DEFAULT_UNIT)
        sound_file = SoundFile.new(filename, sound, channel, volume, 
                                    pitch, looping, streaming, file_length)
        sound_file.channel.volume = volume / 100.0
        sound_file.channel.frequency = sound_file.channel.frequency * pitch / 100
        sound_file.channel.position = position
        return sound_file
      end
      #--------------------------------------------------------------------------
      # * Stop and Dispose of Sound File
      #-------------------------------------------------------------------------- 
      def self.stop(sound_file)
        unless sound_file and sound_file.channel
          return
        end
        # Stop channel, then clear variables and dispose of bgm
        sound_file.channel.stop
        sound_file.channel = nil
        sound_file.sound.dispose
      end
      #--------------------------------------------------------------------------
      # * Return Length in Milliseconds
      #-------------------------------------------------------------------------- 
      def self.get_length(sound_file, unit = FModEx::FMOD_DEFAULT_UNIT)
        return sound_file.length(unit)
      end
      #--------------------------------------------------------------------------
      # * Check if Another Sound File is Playing
      #-------------------------------------------------------------------------- 
      def self.already_playing?(sound_file, name, position = 0)
        # Get a valid file name
        filename = self.selectBGMFilename(name)
        if (sound_file)
          # If the same sound file is already playing don't play it again
          if (sound_file.name == filename and position == 0)
            return true
          end
          # If another sound file is playing, stop it
          if sound_file.channel
            self.stop(sound_file)
          end
        end
        # No sound file is playing or it was already stopped
        return false
      end
      #--------------------------------------------------------------------------
      # * Check if Sound File is Playing
      #--------------------------------------------------------------------------  
      def self.playing?(sound_file)
        unless sound_file and sound_file.channel
          return false
        end
        return sound_file.channel.playing?
      end
      #--------------------------------------------------------------------------
      # * Get Current Sound File Playing Position
      #-------------------------------------------------------------------------- 
      def self.get_position(sound_file)
        unless sound_file and sound_file.channel
          return 0
        end
        return sound_file.channel.position
      end
      #--------------------------------------------------------------------------
      # * Seek to a New Sound File Playing Position
      #-------------------------------------------------------------------------- 
      def self.set_position(sound_file, new_pos)
        unless sound_file and sound_file.channel
          return
        end
        sound_file.channel.position = new_pos
      end
      #--------------------------------------------------------------------------
      # * Get Current Sound File Volume
      #-------------------------------------------------------------------------- 
      def self.get_volume(sound_file)
        unless sound_file
          return 0
        end
        return sound_file.volume
      end
      #--------------------------------------------------------------------------
      # * Set Sound File Volume
      #-------------------------------------------------------------------------- 
      def self.set_volume(sound_file, volume)
        unless sound_file and sound_file.channel
          return
        end
        sound_file.volume = volume * 1.0
        sound_file.channel.volume = volume / 100.0
      end
      #--------------------------------------------------------------------------
      # * Set Loop Points
      #     first           : Loop start point in milliseconds
      #     second          : Loop end point in milliseconds (-1 for file end)
      #     unit            : FMOD_TIMEUNIT for points
      #-------------------------------------------------------------------------- 
      def self.set_loop_points(sound_file, first, second, unit = FModEx::FMOD_DEFAULT_UNIT)
        unless sound_file and sound_file.channel
          return
        end
        # If second is -1 then set loop end to the file end
        if second == -1
          second = sound_file.length - 1
        end
        # Set loop points and reflush stream buffer
        sound_file.channel.sound.setLoopPoints(first, second, unit)
        sound_file.channel.position = sound_file.channel.position
        return sound_file
      end
      #--------------------------------------------------------------------------
      # * Play BGM (or ME)
      #     name            : Name of the file
      #     volume          : Channel volume
      #     pitch           : Channel frequency
      #     position        : Starting position in milliseconds
      #     looping         : Does the BGM loop?
      #-------------------------------------------------------------------------- 
      def self.bgm_play(name, volume, pitch, position = 0, looping = true)
        return if self.already_playing?(@fmod_bgm, name, position)
        # Now play the new BGM as a stream
        @fmod_bgm = self.play(name, volume, pitch, position, looping, true)
      end
      #--------------------------------------------------------------------------
      # * Stop and Dispose of BGM
      #-------------------------------------------------------------------------- 
      def self.bgm_stop
        self.stop(@fmod_bgm)
        @fmod_bgm = nil
      end
      #--------------------------------------------------------------------------
      # * Return BGM Length in Milliseconds
      #-------------------------------------------------------------------------- 
      def self.bgm_length(sound_file)
        self.get_length(@fmod_bgm)
      end
      #--------------------------------------------------------------------------
      # * Check if a BGM is Playing
      #--------------------------------------------------------------------------  
      def self.bgm_playing?
        return self.playing?(@fmod_bgm)
      end
      #--------------------------------------------------------------------------
      # * Get Current BGM Playing Position
      #-------------------------------------------------------------------------- 
      def self.bgm_position
        return self.get_position(@fmod_bgm)
      end
      #--------------------------------------------------------------------------
      # * Seek to New BGM Playing Position
      #-------------------------------------------------------------------------- 
      def self.bgm_position=(new_pos)
        self.set_position(@fmod_bgm, new_pos)
      end
      #--------------------------------------------------------------------------
      # * Get Current BGM Volume
      #-------------------------------------------------------------------------- 
      def self.bgm_volume
        return self.get_volume(@fmod_bgm)
      end
      #--------------------------------------------------------------------------
      # * Set BGM Volume
      #-------------------------------------------------------------------------- 
      def self.bgm_volume=(volume)
        self.set_volume(@fmod_bgm, volume)
      end
      #--------------------------------------------------------------------------
      # * Set Loop Points
      #     first           : Loop start point in milliseconds
      #     second          : Loop end point in milliseconds
      #     unit            : FMOD_TIMEUNIT for points
      #-------------------------------------------------------------------------- 
      def self.bgm_set_loop_points(first, second, unit = FModEx::FMOD_DEFAULT_UNIT)
        @fmod_bgm = self.set_loop_points(@fmod_bgm, first, second, unit)
      end
      #--------------------------------------------------------------------------
      # * Play BGS
      #     name            : Name of the file
      #     volume          : Channel volume
      #     pitch           : Channel frequency
      #     position        : Starting position in milliseconds
      #     looping         : Does the BGS loop?
      #-------------------------------------------------------------------------- 
      def self.bgs_play(name, volume, pitch, position = 0, looping = true)
        return if self.already_playing?(@fmod_bgs, name, position)
        # Now play the new BGS as a stream
        @fmod_bgs = self.play(name, volume, pitch, position, looping, true)
      end
      #--------------------------------------------------------------------------
      # * Stop and Dispose of BGS
      #-------------------------------------------------------------------------- 
      def self.bgs_stop
        self.stop(@fmod_bgs)
        @fmod_bgs = nil
      end
      #--------------------------------------------------------------------------
      # * Return BGS Length in Milliseconds
      #-------------------------------------------------------------------------- 
      def self.bgm_length(sound_file)
        self.get_length(@fmod_bgs)
      end
      #--------------------------------------------------------------------------
      # * Check if a BGS is Playing
      #--------------------------------------------------------------------------  
      def self.bgs_playing?
        return self.playing?(@fmod_bgs)
      end
      #--------------------------------------------------------------------------
      # * Get Current BGS Playing Position
      #-------------------------------------------------------------------------- 
      def self.bgs_position
        return self.get_position(@fmod_bgs)
      end
      #--------------------------------------------------------------------------
      # * Seek to New BGS Playing Position
      #-------------------------------------------------------------------------- 
      def self.bgs_position=(new_pos)
        self.set_position(@fmod_bgs, new_pos)
      end
      #--------------------------------------------------------------------------
      # * Get Current BGS Volume
      #-------------------------------------------------------------------------- 
      def self.bgs_volume
        return self.get_volume(@fmod_bgs)
      end
      #--------------------------------------------------------------------------
      # * Set BGS Volume
      #-------------------------------------------------------------------------- 
      def self.bgs_volume=(volume)
        self.set_volume(@fmod_bgs, volume)
      end
      #--------------------------------------------------------------------------
      # * Set Loop Points
      #     first           : Loop start point in milliseconds
      #     second          : Loop end point in milliseconds
      #     unit            : FMOD_TIMEUNIT for points
      #-------------------------------------------------------------------------- 
      def self.bgs_set_loop_points(first, second, unit = FModEx::FMOD_DEFAULT_UNIT)
        @fmod_bgs = self.set_loop_points(@fmod_bgs, first, second, unit)
      end
      #--------------------------------------------------------------------------
      # * Play SE
      #     name            : Name of the file
      #     volume          : Channel volume
      #     pitch           : Channel frequency
      #-------------------------------------------------------------------------- 
      def self.se_play(name, volume, pitch)
        if @fmod_se.size > @fmod.maxChannels
          se = @fmod_se.shift
          self.stop(se)
        end
        # Load SE into memory and play it
        @fmod_se << self.play(name, volume, pitch, 0, false, false)
      end
      #--------------------------------------------------------------------------
      # * Stop and Dispose of all SEs
      #-------------------------------------------------------------------------- 
      def self.se_stop
        for se in @fmod_se
          self.stop(se)
        end
        @fmod_se.clear
      end
      #--------------------------------------------------------------------------
      # * Get Rid of Non-Playing SEs
      #--------------------------------------------------------------------------  
      def self.se_clean
        for se in @fmod_se
          unless self.playing?(se)
            self.stop(se)
            @fmod_se.delete(se)
          end
        end
      end
      #--------------------------------------------------------------------------
      # * Check if There's Some SE in SE Array
      #--------------------------------------------------------------------------  
      def self.se_list_empty?
        return @fmod_se.empty?
      end
      #--------------------------------------------------------------------------
      # * Dispose of Everything
      #--------------------------------------------------------------------------  
      def self.dispose
        self.bgm_stop
        self.bgs_stop
        self.se_stop
        @fmod.dispose
      end
    end
    
    #==============================================================================
    # ** Audio
    #------------------------------------------------------------------------------
    #  The module that carries out music and sound processing.
    #==============================================================================
    
    module Audio
      #--------------------------------------------------------------------------
      # * Constants
      #--------------------------------------------------------------------------
      BGM_FADE_IN_INCREMENT = 5     # BGM volume incremented 0.2 seconds
      #--------------------------------------------------------------------------
      # * Instance Variables
      #--------------------------------------------------------------------------
      @bgm_fading_out = false       # BGM started fading out
      @bgm_fade_decrement = 0.0     # BGM volume decremented each update
      @bgs_fading_out = false       # BGS started fading out
      @bgs_fade_decrement = 0.0     # BGS volume decremented each update
      @me_fading_out = false        # ME started fading out
      @me_fade_decrement = 0.0      # ME volume decremented each update
      @me_playing = false           # Is some ME playing?
      @playing_bgm = nil            # BGM currently being played
      @next_bgm = nil               # The BGM to be played after fading out
      @next_bgm_position = 0        # Starting position of next bgm
      @next_bgs = nil               # The BGS to be played after fading out
      @next_bgs_position = 0        # Starting position of next bgm
      @next_me = nil                # The ME to be played after fading
      #--------------------------------------------------------------------------
      # * Starts BGM Playback
      #     name            : Name of the file
      #     volume          : Channel volume
      #     pitch           : Channel frequency
      #     position        : Starting position in milliseconds
      #-------------------------------------------------------------------------- 
      def Audio.bgm_play(filename, volume = 100, pitch = 100, position = 0, 
                          fade_in = false)
        if @bgm_fading_out and !fade_in
          @next_bgm = RPG::AudioFile.new(filename, volume, pitch)
          @next_bgm_position = position
          return
        end
        start_volume = volume
        if fade_in
          @bgm_target_volume = volume unless @bgm_fading_in
          @bgm_fading_in = true
          start_volume = 0
        end
        @bgm_fading_out = false
        # If a ME is playing we wait until it's over before playing BGM
        unless @me_playing
          FMod::bgm_play(filename, start_volume, pitch, position)
        end
        @playing_bgm = RPG::AudioFile.new(filename, volume, pitch)
        @memorized_bgm = @playing_bgm
        @memorized_bgm_position = position
      end
      #--------------------------------------------------------------------------
      # * Stops BGM Playback
      #-------------------------------------------------------------------------- 
      def Audio.bgm_stop
        @memorized_bgm = nil
        @playing_bgm = nil
        @bgm_fading_in = false
        @bgm_fading_out = false
        # MEs are internally BGMs, but are stopped with me_stop instead
        if @me_playing
          return
        end
        FMod::bgm_stop
      end
      #--------------------------------------------------------------------------
      # * Starts BGM fadeout.
      #     time            : Length of the fadeout in milliseconds.
      #-------------------------------------------------------------------------- 
      def Audio.bgm_fade(time)
        return if @me_playing or !FMod::bgm_playing?
        @bgm_fading_out = true
        time = time / 1000
        @bgm_fade_decrement = FMod::bgm_volume / (time * 5)
      end
      #--------------------------------------------------------------------------
      # * Starts BGS Playback
      #     name            : Name of the file
      #     volume          : Channel volume
      #     pitch           : Channel frequency
      #     position        : Starting position in milliseconds
      #-------------------------------------------------------------------------- 
      def Audio.bgs_play(filename, volume = 100, pitch = 100, position = 0)
        if @bgs_fading_out
          @next_bgs = RPG::AudioFile.new(filename, volume, pitch)
          @next_bgs_position = position
          return
        end
        FMod::bgs_play(filename, volume, pitch, position)
      end
      #--------------------------------------------------------------------------
      # * Stops BGS Playback
      #-------------------------------------------------------------------------- 
      def Audio.bgs_stop
        FMod::bgs_stop
        @bgs_fading_out = false
      end
      #--------------------------------------------------------------------------
      # * Starts BGS fadeout.
      #     time            : Length of the fadeout in milliseconds.
      #-------------------------------------------------------------------------- 
      def Audio.bgs_fade(time)
        return unless FMod::bgs_playing?
        @bgs_fading_out = true
        time = time / 1000
        @bgs_fade_decrement = FMod::bgs_volume / (time * 5)
      end
      #--------------------------------------------------------------------------
      # * Starts ME Playback
      #     name            : Name of the file
      #     volume          : Channel volume
      #     pitch           : Channel frequency
      #-------------------------------------------------------------------------- 
      def Audio.me_play(filename, volume = 100, pitch = 100)
        if @me_fading_out
          @next_me = RPG::AudioFile.new(filename, volume, pitch)
          return
        end
        if @bgm_fading_out
          self.bgm_stop
        end
        # Memorize playing bgm
        if @playing_bgm and !@me_playing
          bgm = @playing_bgm
          @playing_bgm = RPG::AudioFile.new(bgm.name, FMod::bgm_volume, bgm.pitch)
          @memorized_bgm = @playing_bgm
          @memorized_bgm_position = FMod::bgm_position
        end
        @me_playing = true
        FMod::bgm_play(filename, volume, pitch, 0, false)
      end
      #--------------------------------------------------------------------------
      # * Stops ME Playback
      #-------------------------------------------------------------------------- 
      def Audio.me_stop
        return unless @me_playing
        @me_playing = false
        @me_fading_out = false
        # Play memorized bgm, fading in
        if @memorized_bgm and !@bgm_fading_out
          bgm = @memorized_bgm
          self.bgm_play(bgm.name, bgm.volume, bgm.pitch, @memorized_bgm_position, true)
        else
          self.bgm_stop
        end
      end
      #--------------------------------------------------------------------------
      # * Starts ME fadeout.
      #     time            : Length of the fadeout in milliseconds.
      #-------------------------------------------------------------------------- 
      def Audio.me_fade(time)
        return unless FMod::bgm_playing?
        @me_fading_out = true
        time = time / 1000
        @bgm_fade_decrement = FMod::bgm_volume / (time * 5)
      end
      #--------------------------------------------------------------------------
      # * Starts SE Playback
      #     name            : Name of the file
      #     volume          : Channel volume
      #     pitch           : Channel frequency
      #-------------------------------------------------------------------------- 
      def Audio.se_play(filename, volume = 100, pitch = 100) 
        FMod::se_play(filename, volume, pitch)
      end
      #--------------------------------------------------------------------------
      # * Stops SE Playback
      #-------------------------------------------------------------------------- 
      def Audio.se_stop 
        FMod::se_stop
      end
      #--------------------------------------------------------------------------
      # * Update ME Playback, SE Disposal and Fading, Called Each Frame
      #-------------------------------------------------------------------------- 
      def Audio.update
        # Stop ME when it's over (and continue playing BGM)
        if @me_playing
          unless FMod::bgm_playing?
            self.me_stop
          end
        end
        # Remove any finished SEs
        unless FMod::se_list_empty?
          FMod::se_clean
        end
        if @bgm_fading_in
          # Stop fading when target is reached, otherwise increase volume
          if FMod::bgm_volume >= @bgm_target_volume
            @bgm_fading_in = false
          else
            current_volume = FMod::bgm_volume + BGM_FADE_IN_INCREMENT
            FMod::bgm_volume = current_volume
          end
        end
        if FMod::bgm_playing? and @bgm_fading_out and 
            !@me_playing
          if FMod::bgm_volume <= 0
            @bgm_fading_out = false
            self.bgm_stop
            # If another BGM played while fading out, play it (most recent)
            if @next_bgm
              self.bgm_play(@next_bgm.name, @next_bgm.volume,
                            @next_bgm.pitch, @next_bgm_position)
              @next_bgm = nil
            end
          else
            current_volume = FMod::bgm_volume - @bgm_fade_decrement
            FMod::bgm_volume = current_volume
          end
        end
        if FMod::bgs_playing? and @bgs_fading_out
          if FMod::bgs_volume <= 0
            @bgs_fading_out = false
            self.bgs_stop
            # If another BGS played while fading out, play it (most recent)
            if @next_bgs
              self.bgs_play(@next_bgs.name, @next_bgs.volume, 
                            @next_bgs.pitch, @next_bgs_position)
              @next_bgs = nil
            end
          else
            current_volume = FMod::bgs_volume - @bgs_fade_decrement
            FMod::bgs_volume = current_volume
          end
        end
        if FMod::bgm_playing? and @me_fading_out
          if FMod::bgm_volume <= 0
            # If another ME played while fading out, play it (most recent)
            if @next_me
              self.me_play(@next_me.name, @next_me.volume, @next_me.pitch)
              @next_me = nil
            else
              @me_fading_out = false
              self.me_stop
            end
          else
            current_volume = FMod::bgm_volume - @bgm_fade_decrement
            FMod::bgm_volume = current_volume
          end
        end
      end
    end
    
    # Create an endless loop to update Audio module
    Thread.new do
      loop do
        sleep 0.2
        Audio.update
      end
    end
    Спойлер Game_System:
    Код:
    #==============================================================================
    # ** Game_System
    #------------------------------------------------------------------------------
    #  This class handles data surrounding the system. Backround music, etc.
    #  is managed here as well. Refer to "$game_system" for the instance of 
    #  this class.
    #==============================================================================
    
    class Game_System
      #--------------------------------------------------------------------------
      # * Play Background Music
      #     bgm : background music to be played
      #--------------------------------------------------------------------------
    ############################################################################
    #   ADDED pos (POSITION) AS PARAMETER TO METHOD
    ############################################################################
      def bgm_play(bgm, pos = 0)
        @playing_bgm = bgm
        if bgm != nil and bgm.name != ""
          Audio.bgm_play("Audio/BGM/" + bgm.name, bgm.volume, bgm.pitch, pos)
    ############################################################################
        else
          Audio.bgm_stop
        end
        Graphics.frame_reset
      end
      #--------------------------------------------------------------------------
      # * Memorize Background Music
      #--------------------------------------------------------------------------
      alias :fmodex_old_system_bgm_memorize :bgm_memorize unless $@
      def bgm_memorize
        fmodex_old_system_bgm_memorize
        @bgm_memorized_position = FMod::bgm_position
      end
      #--------------------------------------------------------------------------
      # * Restore Background Music
      #--------------------------------------------------------------------------
      def bgm_restore
    ############################################################################
    #   ADDED @bgm_memorized_position AS PARAMETER TO METHOD
    ############################################################################
        bgm_play(@memorized_bgm, @bgm_memorized_position)
    ############################################################################
      end
      #--------------------------------------------------------------------------
      # * Play Background Sound
      #     bgs : background sound to be played
      #--------------------------------------------------------------------------
    ############################################################################
    #   ADDED pos (POSITION) AS PARAMETER TO METHOD
    ############################################################################
      def bgs_play(bgs, pos = 0)
        @playing_bgs = bgs
        if bgs != nil and bgs.name != ""
          Audio.bgs_play("Audio/BGS/" + bgs.name, bgs.volume, bgs.pitch, pos)
    ############################################################################
        else
          Audio.bgs_stop
        end
        Graphics.frame_reset
      end
      #--------------------------------------------------------------------------
      # * Stop Background Sound
      #--------------------------------------------------------------------------
      def bgs_stop
        @playing_bgs = nil
        Audio.bgs_stop
      end
      #--------------------------------------------------------------------------
      # * Memorize Background Sound
      #--------------------------------------------------------------------------
      alias :fmodex_old_system_bgs_memorize :bgs_memorize unless $@
      def bgs_memorize
        fmodex_old_system_bgs_memorize
        @bgs_memorized_position = FMod::bgs_position
      end
      #--------------------------------------------------------------------------
      # * Restore Background Sound
      #--------------------------------------------------------------------------
      def bgs_restore
    ############################################################################
    #   ADDED @bgs_memorized_position AS PARAMETER TO METHOD
    ############################################################################
        bgs_play(@memorized_bgs, @bgs_memorized_position)
    ############################################################################
      end
    end
    Спойлер Other Classes:
    Код:
    class Game_Temp
      attr_accessor :map_bgm_pos
      alias :fmodex_old_temp_initialize :initialize unless $@
      def initialize
        fmodex_old_temp_initialize
        @map_bgm_pos = 0
      end
    end
    
    class Scene_Map
      alias :fmodex_old_map_call_battle :call_battle unless $@
      def call_battle
        $game_temp.map_bgm_pos = FMod.bgm_position
        fmodex_old_map_call_battle
      end
    end
    
    class Scene_Battle
      def judge
        # If all dead determinant is true, or number of members in party is 0
        if $game_party.all_dead? or $game_party.actors.size == 0
          # If possible to lose
          if $game_temp.battle_can_lose
    ############################################################################
    #   ADDED $game_temp.map_bgm_pos AS 2ND PARAMETE TO bgm_play
    ############################################################################
            # Return to BGM before battle starts
            $game_system.bgm_play($game_temp.map_bgm, $game_temp.map_bgm_pos)
    ############################################################################        
            # Battle ends
            battle_end(2)
            # Return true
            return true
          end
          # Set game over flag
          $game_temp.gameover = true
          # Return true
          return true
        end
        # Return false if even 1 enemy exists
        for enemy in $game_troop.enemies
          if enemy.exist?
            return false
          end
        end
        # Start after battle phase (win)
        start_phase5
        # Return true
        return true
      end
      
      def start_phase5
        # Shift to phase 5
        @phase = 5
    ############################################################################
    #   ADDED $game_temp.map_bgm_pos AS 2ND PARAMETE TO bgm_play
    #   AND SWITCHED PLAY ORDER SO BGM IS PLAYED FIRST
    ############################################################################
        # Return to BGM before battle started
        $game_system.bgm_play($game_temp.map_bgm, $game_temp.map_bgm_pos)
        # Play battle end ME
        $game_system.me_play($game_system.battle_end_me)
    ############################################################################
        # Initialize EXP, amount of gold, and treasure
        exp = 0
        gold = 0
        treasures = []
        # Loop
        for enemy in $game_troop.enemies
          # If enemy is not hidden
          unless enemy.hidden
            # Add EXP and amount of gold obtained
            exp += enemy.exp
            gold += enemy.gold
            # Determine if treasure appears
            if rand(100) < enemy.treasure_prob
              if enemy.item_id > 0
                treasures.push($data_items[enemy.item_id])
              end
              if enemy.weapon_id > 0
                treasures.push($data_weapons[enemy.weapon_id])
              end
              if enemy.armor_id > 0
                treasures.push($data_armors[enemy.armor_id])
              end
            end
          end
        end
        # Treasure is limited to a maximum of 6 items
        treasures = treasures[0..5]
        # Obtaining EXP
        for i in 0...$game_party.actors.size
          actor = $game_party.actors[i]
          if actor.cant_get_exp? == false
            last_level = actor.level
            actor.exp += exp
            if actor.level > last_level
              @status_window.level_up(i)
            end
          end
        end
        # Obtaining gold
        $game_party.gain_gold(gold)
        # Obtaining treasure
        for item in treasures
          case item
          when RPG::Item
            $game_party.gain_item(item.id, 1)
          when RPG::Weapon
            $game_party.gain_weapon(item.id, 1)
          when RPG::Armor
            $game_party.gain_armor(item.id, 1)
          end
        end
        # Make battle result window
        @result_window = Window_BattleResult.new(exp, gold, treasures)
        # Set wait count
        @phase5_wait_count = 100
      end
    
      
      def update_phase5
        # If wait count is larger than 0
        if @phase5_wait_count > 0
          # Decrease wait count
          @phase5_wait_count -= 1
          # If wait count reaches 0
          if @phase5_wait_count == 0
            # Show result window
            @result_window.visible = true
            # Clear main phase flag
            $game_temp.battle_main_phase = false
            # Refresh status window
            @status_window.refresh
          end
          return
        end
        # If C button was pressed
        if Input.trigger?(Input::C)
    ############################################################################
    #   REMOVED $game_system.play_bgm BECAUSE IT'S DONE AUTOMATICALLY
    #   WHEN ME IS DONE PLAYING
    ############################################################################
    ############################################################################
          # Battle ends
          battle_end(0)
        end
      end
    end


    Дальше интереснее. Тут лежит проект (жаль заброшен еще в 2009ом) с полностью опенсорсной имплементацией первого RGSS на базе SDL: https://bitbucket.org/lukas/openrgss/. Можно успешно собрать под винду/мак, вроде как даже базовые проекты работают.

    Еще у меня есть кое-какие подвижки в исследовании кода RGSS102E.dll (старовата, но тут уж что имею).

    Напоследок, есть группа людей, занимающаяся разработкой кросс-платформенных редактора + движка под названием Advanced RPG Creator. Он успешно развивается, периодически выкладываются билды. Но самое главное, что в этом редакторе есть поддержка конвертации проектов RMXP. Для этого хочешь-нет, а им тоже надо знать специфику работы внутренних классов RGSS, чтобы написать их собственную реализацию. Какую-то информацию можно найти тут: https://forum.chaos-project.com/inde...oard,78.0.html Не много, но что есть. Было бы неплохо наложить ручки на их исходники, если они где-то есть.

  2. #62
    Хранитель Форума Аватар для Валера
    Информация о пользователе
    Регистрация
    15.04.2008
    Адрес
    Москва
    Сообщений
    14,006
    Записей в дневнике
    3
    Репутация: 166 Добавить или отнять репутацию

    Спойлер И как тебе не стыдно-то, а, Валера?:


    Оборона форта: http://rghost.ru/8kLGxFtD2
    Сделать, чтоб все происходило, как я хочу, - вот, собственно, и весь мейкер!
    Адский Рейд: http://rpgmaker.su/vbdownloads.php?d...downloadid=106

Страница 7 из 7 ПерваяПервая ... 567

Информация о теме

Пользователи, просматривающие эту тему

Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)

Социальные закладки

Социальные закладки

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •  
Open RGSS3: Разработка