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