Интересный проект. Жаль, что заморожен. Тоже интересует переписывание базовых классов, но не с целью совместимости 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 Не много, но что есть. Было бы неплохо наложить ручки на их исходники, если они где-то есть.
Социальные закладки