Код:
#===============================================================================
# Custom Window v1.2
# Автор оригинала: poccil
# Над скриптом измывались: Vitamant [Jabber: vitamant@jabber.ru]
#===============================================================================
class Window
#-----------------------------------------------------------------------------
# Свойства, доступные только для чтения
#-----------------------------------------------------------------------------
attr_reader :x # Координата X окна.
attr_reader :y # Координата Y окна.
attr_reader :z # Координата Z окна.
attr_reader :width # Ширина окна.
attr_reader :height # Высота окна.
attr_reader :opacity # Непрозрачность окна (0-255).
attr_reader :back_opacity # Непрозрачность фона окна (0-255).
attr_reader :contents # Ссылка на рисунок (Bitmap), используемый как содержимое окна.
attr_reader :ox # Координата X начальной точки содержимого окна.
attr_reader :oy # Координата Y начальной точки содержимого окна.
attr_reader :space_x # Смещение содержимого окна относительно рамки по оси X.
attr_reader :space_y # Смещение содержимого окна относительно рамки по оси Y.
attr_reader :contents_opacity # Непрозрачность содержимого окна (0-255).
attr_reader :windowskin # Ссылка на рисунок (Bitmap), используемый как скин окна.
attr_reader :stretch # Метод отображения фона. Если TRUE, растягивает графику фона.
attr_reader :pause # Видимость графики паузы.
attr_reader :cursor_rect # Контейнер рисунка курсора-указателя (Rect).
attr_reader :viewport # Возвращает область просмотра (Viewport).
attr_reader :active # Состояние мерцания указателя. Если TRUE, то курсор мерцает.
attr_reader :visible # Видимость окна. Если TRUE, то окно видимо.
attr_reader :openness
attr_reader :skingrid
#-----------------------------------------------------------------------------
# Инициализация
#-----------------------------------------------------------------------------
def initialize
@x = 0 # Координата X окна.
@y = 0 # Координата Y окна.
@z = 0 # Координата Z окна.
@width = 0 # Ширина окна.
@height = 0 # Высота окна.
@opacity = 255 # Непрозрачность окна (0-255).
@back_opacity = 255 # Непрозрачность фона окна (0-255).
@ox = 0 # Координата X начальной точки содержимого окна.
@oy = 0 # Координата Y начальной точки содержимого окна.
@space_x = 0 # Смещение содержимого окна относительно рамки по оси X.
@space_y = 0 # Смещение содержимого окна относительно рамки по оси Y.
@contents_opacity = 255 # Непрозрачность содержимого окна (0-255).
@stretch = true # Метод отображения фона. Если TRUE, растягивает графику фона.
@pause = false # Видимость графики паузы.
@cursor_rect = Window::CursorRect.new(self) # Контейнер рисунка курсора-указателя (Rect).
@active = true # Состояние мерцания указателя. Если TRUE, то курсор мерцает.
@visible = true # Видимость окна. Если TRUE, то окно видимо.
@openness = 255
@skingrid = Window::Skingrid.new
@pauseframe = 0
@pauseopacity = 255 # непрозрачность значка ожидания
@cursorblink = 0 # см. def update
@cursoropacity = 255 # непрозрачность курсора
@disposed = false
@sprites = {}
@sidebitmaps = [nil, nil, nil, nil]
@spritekeys = [
"back",
"corner0","side0","scroll0",
"corner1","side1","scroll1",
"corner2","side2","scroll2",
"corner3","side3","scroll3",
"cursor","contents","pause"
]
for i in @spritekeys
@sprites[i] = Sprite.new
end
private_refresh(true)
end
#-----------------------------------------------------------------------------
# Освобождает окно. Если окно уже было освобождёно, возвращает false.
#-----------------------------------------------------------------------------
def dispose
return false if @disposed
@sprites.free
@sidebitmaps.free
@cursorbitmap.free
@backbitmap.free
@windowskin = nil
@contents = nil
@disposed = true
end
#-----------------------------------------------------------------------------
# Возвращает TRUE, если окно было освобождёно.
#-----------------------------------------------------------------------------
def disposed?
return @disposed
end
#-----------------------------------------------------------------------------
# Задает новое значение координаты X окна.
# value :integer - новое значение
#-----------------------------------------------------------------------------
def x=(value)
@x = value
private_refresh
end
#-----------------------------------------------------------------------------
# Задает новое значение координаты Y окна.
# value :integer - новое значение
#-----------------------------------------------------------------------------
def y=(value)
@y = value
private_refresh
end
#-----------------------------------------------------------------------------
# Задает новое значение координаты Z окна.
# value :integer - новое значение
#-----------------------------------------------------------------------------
def z=(value)
@z = value
private_refresh
end
#-----------------------------------------------------------------------------
# Изменяет ширину окна.
# value :integer - новое значение
#-----------------------------------------------------------------------------
def width=(value)
@width = value
private_refresh(true)
end
#-----------------------------------------------------------------------------
# Изменяет высоту окна.
# value :integer - новое значение
#-----------------------------------------------------------------------------
def height=(value)
@height = value
private_refresh(true)
end
#-----------------------------------------------------------------------------
# Изменяет непрозрачность окна.
# value :integer {0~255} - новое значение
#-----------------------------------------------------------------------------
def opacity=(value)
@opacity = value.correct(0, 255)
private_refresh
end
#-----------------------------------------------------------------------------
# Изменяет непрозрачность фона окна.
# value :integer {0~255} - новое значение
#-----------------------------------------------------------------------------
def back_opacity=(value)
@back_opacity = value.correct(0, 255)
private_refresh
end
#-----------------------------------------------------------------------------
# Заменяет содержимое окна.
# value :bitmap - новый рисунок
#-----------------------------------------------------------------------------
def contents=(value)
@contents = value
private_refresh
end
#-----------------------------------------------------------------------------
# Задает новое значение координаты X содержимого окна.
# value :integer - новое значение
#-----------------------------------------------------------------------------
def ox=(value)
@ox = value
private_refresh
end
#-----------------------------------------------------------------------------
# Задает новое значение координаты Y содержимого окна.
# value :integer - новое значение
#-----------------------------------------------------------------------------
def oy=(value)
@oy = value
private_refresh
end
#-----------------------------------------------------------------------------
# Изменяет расстояние между границей окна и его содержимым по оси OX
# value :integer - новое значение
#-----------------------------------------------------------------------------
def space_x=(value)
@space_x = value
private_refresh(true)
end
#-----------------------------------------------------------------------------
# Изменяет расстояние между границей окна и его содержимым по оси OY
# value :integer - новое значение
#-----------------------------------------------------------------------------
def space_y=(value)
@space_y = value
private_refresh(true)
end
#-----------------------------------------------------------------------------
# Изменяет непрозрачность содержимого окна.
# value :integer {0~255} - новое значение
#-----------------------------------------------------------------------------
def contents_opacity=(value)
@contents_opacity = value.correct(0, 255)
private_refresh
end
#-----------------------------------------------------------------------------
# Заменяет шкурку окна.
# value :bitmap - новый скин
#-----------------------------------------------------------------------------
def windowskin=(value)
return false unless value.exists? || value.is_a?(Bitmap)
# Расчитываем положение элементов оформления
if value.width == 192 && value.height = 128 # RPG Maker XP
@skingrid.back.set ( 0, 0, 128, 128)
@skingrid.blind.set ( 0, 0, 0, 0)
@skingrid.border.set(128, 0, 64, 64)
@skingrid.cursor.set(128, 64, 32, 32)
@skingrid.pause.set (160, 64, 32, 32)
@skingrid.colors.set(152, 24, 16, 16)
@skingrid.color.set ( 0, 0, 2, 2)
elsif value.width == 128 && value.height == 128 # RPG Maker VX
@skingrid.back.set ( 0, 0, 64, 64)
@skingrid.blind.set ( 0, 64, 64, 64)
@skingrid.border.set( 64, 0, 64, 64)
@skingrid.cursor.set( 64, 64, 32, 32)
@skingrid.pause.set ( 96, 64, 32, 32)
@skingrid.colors.set( 64, 96, 64, 32)
@skingrid.color.set ( 0, 0, 8, 8)
else
return false
end
b = @skingrid.border
@skingrid.side_rects[0].set(b.x + 16, b.y, 32, 16)
@skingrid.side_rects[1].set(b.x, b.y + 16, 16, 32)
@skingrid.side_rects[2].set(b.x + 48, b.y + 16, 16, 32)
@skingrid.side_rects[3].set(b.x + 16, b.y + 48, 32, 16)
p = @skingrid.pause
pw = @skingrid.pause_width = p.width / 2
ph = @skingrid.pause_height = p.height / 2
@skingrid.pause_rects = [
p.x, p.y,
p.x + pw, p.y,
p.x, p.y + ph,
p.x + pw, p.y + ph
]
# Цепляем шкурку
@windowskin = value
for i in 0..3
@sprites["corner#{i}"].bitmap = value
@sprites["scroll#{i}"].bitmap = value
end
@sprites["pause"].bitmap = value
# Задаем изображения углов и знаков прокрутки
@sprites["corner0"].src_rect.set(b.x, b.y, 16, 16) # верхний-левый
@sprites["corner1"].src_rect.set(b.x + 48, b.y, 16, 16) # верхний-правый
@sprites["corner2"].src_rect.set(b.x, b.y + 48, 16, 16) # нижний-левый
@sprites["corner3"].src_rect.set(b.x + 48, b.y + 48, 16, 16) # нижний- правый
@sprites["scroll0"].src_rect.set(b.x + 24, b.y + 16, 16, 8) # верхний
@sprites["scroll3"].src_rect.set(b.x + 24, b.y + 40, 16, 8) # нижний
@sprites["scroll1"].src_rect.set(b.x + 16, b.y + 24, 8, 16) # левый
@sprites["scroll2"].src_rect.set(b.x + 40, b.y + 24, 8, 16) # правый
private_refresh(true)
end
#-----------------------------------------------------------------------------
# Изменяет способ отображения фона.
# value :boolean - если true, растягивает графику фона
#-----------------------------------------------------------------------------
def stretch=(value)
@stretch = value
private_refresh(true)
end
#-----------------------------------------------------------------------------
# Включает или отключает отображение значка ожидания.
# value :boolean - если true, отображает значок ожидания
#-----------------------------------------------------------------------------
def pause=(value)
@pause = value
@pauseopacity = 0 unless value
private_refresh
end
#-----------------------------------------------------------------------------
# Изменяет положение прямоугольника курсора.
# value :rect - новый прямоугольник
#-----------------------------------------------------------------------------
def cursor_rect=(value)
unless value.is_a?(Rect)
@cursor_rect.clear
else
@cursor_rect.set(value.x, value.y, value.width, value.height)
end
end
#-----------------------------------------------------------------------------
# Задает новую область просмотра.
# value :viewport - новая область просмотра
#-----------------------------------------------------------------------------
def viewport=(value)
@viewport = value
for i in @spritekeys
#@sprites[i].dispose
if @sprites[i].is_a?(Sprite)
#@sprites[i] = Sprite.new(@viewport)
@sprites[i].viewport = @viewport
elsif @sprites[i].is_a?(Plane)
#@sprites[i] = Plane.new(@viewport)
@sprites[i].viewport = @viewport
else
@sprites[i].dispose
@sprites[i] = nil
end
end
private_refresh(true)
end
#-----------------------------------------------------------------------------
# Включает или отключает мерцание курсора.
# value :boolean - если true, курсор будет мерцать
#-----------------------------------------------------------------------------
def active=(value)
@active = value
private_refresh(true)
end
#-----------------------------------------------------------------------------
# Включает или отключает видимость окна и всех его элементов.
# value :boolean - если true, окно видимо
#-----------------------------------------------------------------------------
def visible=(value)
@visible = value
private_refresh
end
#-----------------------------------------------------------------------------
#
#-----------------------------------------------------------------------------
def openness=(value)
@openness = value.correct(0, 255)
private_refresh
end
#-----------------------------------------------------------------------------
# Обновляет мерцание указателя и значок ожидания.
#-----------------------------------------------------------------------------
def update
return if @disposed # Если окно было освобождено, ничего не делаем
# Если окно активно - мерцаем курсором, меняя его непрозрачность
if @active
if @cursorblink == 0
@cursoropacity -= 8
@cursorblink = 1 if @cursoropacity <= 128
else
@cursoropacity += 8
@cursorblink = 0 if @cursoropacity >= 255
end
unless @cursor_rect.empty?
@sprites["cursor"].opacity = @cursoropacity * @contents_opacity / 255
end
# Если окно неактивно - делаем курсор полупрозрачным
else
unless @cursoropacity == 128
@cursoropacity = 128
@sprites["cursor"].opacity = @cursoropacity * @contents_opacity / 255
end
end
# Если значок ожидания отображен, постепенно проявляем его
if @pause
@pauseframe = (Graphics.frame_count / 8) % 4
@pauseopacity = [@pauseopacity + 64, 255].min
@sprites["pause"].opacity = @pauseopacity
end
@sprites.each_value { |value| value.update }
end
#-----------------------------------------------------------------------------
# Возвращает изображение заданного размера. Если существующее изображение
# удовлетворяет требованиям и не было освобождено, возварщает его.
# bitmap :bitmap - исходное изображение
# width :integer - требуемая ширина изображения
# height :integer - требуемая высота изображения
#-----------------------------------------------------------------------------
def ensure_bitmap(bitmap, width, height)
if !bitmap || bitmap.disposed? || bitmap.width < width || bitmap.height < height
bitmap.dispose if bitmap
bitmap = Bitmap.new([1, width].max, [1, height].max)
end
return bitmap
end
#-----------------------------------------------------------------------------
# Заполняет указанную область рисунка "плитками", взятыми из другого рисунка
# dst_bitmap :bitmap - рисунок, на который будет производиться копирование
# dst_rect :rect - область, которая будет заполнена
# src_bitmap :bitmap - рисунок, с которого будет производиться копирование
# src_rect :rect - область, определяющая положение "плитки"
#-----------------------------------------------------------------------------
def tile_bitmap(dst_bitmap, dst_rect, src_bitmap, src_rect)
return if !src_bitmap || src_bitmap.disposed?
left = dst_rect.x
top = dst_rect.y
y = 0; loop do break unless y < dst_rect.height
x = 0; loop do break unless x < dst_rect.width
dst_bitmap.blt(x + left, y + top, src_bitmap, src_rect)
x += src_rect.width
end
y += src_rect.height
end
end
#-----------------------------------------------------------------------------
# Перерисовывает окно и его содержимое
# ???? :boolean - если true, ????
#-----------------------------------------------------------------------------
def private_refresh (changeBitmap = false)
return if @disposed
# Цепляем содержимое
@sprites["contents"].bitmap = @contents
# Если есть шкурка - делаем окно видимым...
if @windowskin.exists?
for i in 0..3
@sprites["corner#{i}"].opacity = @opacity
@sprites["corner#{i}"].visible = @visible
@sprites["side#{i}"].opacity = @opacity
@sprites["side#{i}"].visible = @visible
@sprites["scroll#{i}"].opacity = @opacity
@sprites["scroll#{i}"].visible = @visible
end
@sprites["back"].opacity = @back_opacity * @opacity / 255
@sprites["contents"].opacity = @contents_opacity
@sprites["back"].visible = @visible
@sprites["contents"].visible = @visible && @openness == 255
@sprites["pause"].visible = @visible && @pause
@sprites["cursor"].visible = @visible && @openness == 255
hascontents = @contents.exists?
@sprites["scroll0"].visible = @visible && hascontents && @oy > 0
@sprites["scroll1"].visible = @visible && hascontents && @ox > 0
@sprites["scroll2"].visible = @visible && hascontents && (@contents.width - @ox) > @width - @space_x * 2
@sprites["scroll3"].visible = @visible && hascontents && (@contents.height - @oy) > @height - @space_y * 2
# ...если шкурки нет - делаем окошко невидимым
else
for i in 0...4
@sprites["corner#{i}"].visible = false
@sprites["side#{i}"].visible = false
@sprites["scroll#{i}"].visible = false
end
@sprites["contents"].visible = @visible && @openness == 255
@sprites["contents"].opacity = @contents_opacity
@sprites["back"].visible = false
@sprites["pause"].visible = false
@sprites["cursor"].visible = false
end
# Задаем высоту спрайтов
@sprites.each_value { |sprite| sprite.z = @z }
@sprites["cursor"].z = @z + 1
@sprites["contents"].z = @z + 2
@sprites["pause"].z = @z + 2
# Задаем размеры содержимого окна
if @width > @space_x * 2 && @height > @space_y * 2
@sprites["contents"].src_rect.set(@ox, @oy, @width - @space_x * 2, @height - @space_y * 2)
else
@sprites["contents"].src_rect.set(0, 0, 0, 0)
end
# Обновляем анимацию паузы
@sprites["pause"].src_rect.set(
@skingrid.pause_rects[@pauseframe * 2],
@skingrid.pause_rects[@pauseframe * 2 + 1],
@skingrid.pause_width, @skingrid.pause_height
)
# Изменяем положение элементов окна на экране
@sprites["pause"].x = @x + @width - 24
@sprites["pause"].y = @y + @height - 24
@sprites["contents"].x = @x + @space_x
@sprites["contents"].y = @y + @space_y
@sprites["corner0"].x = @x
@sprites["corner0"].y = @y
@sprites["corner1"].x = @x + @width - 16
@sprites["corner1"].y = @y
@sprites["corner2"].x = @x
@sprites["corner2"].y = @y + @height - 16
@sprites["corner3"].x = @x + @width - 16
@sprites["corner3"].y = @y + @height - 16
@sprites["side0"].x = @x + 16
@sprites["side0"].y = @y
@sprites["side1"].x = @x
@sprites["side1"].y = @y + 16
@sprites["side2"].x = @x + @width - 16
@sprites["side2"].y = @y + 16
@sprites["side3"].x = @x + 16
@sprites["side3"].y = @y + @height - 16
@sprites["scroll0"].x = @x + @width / 2 - 8
@sprites["scroll0"].y = @y + 8
@sprites["scroll1"].x = @x + 8
@sprites["scroll1"].y = @y + @height / 2 - 8
@sprites["scroll2"].x = @x + @width - 16
@sprites["scroll2"].y = @y + @height / 2 - 8
@sprites["scroll3"].x = @x + @width / 2 - 8
@sprites["scroll3"].y = @y + @height - 16
@sprites["back"].x = @x + 2
@sprites["back"].y = @y + 2
@sprites["cursor"].x = @x + @space_x + @cursor_rect.x
@sprites["cursor"].y = @y + @space_y + @cursor_rect.y
if changeBitmap && @windowskin.exists?
width = @cursor_rect.width
height = @cursor_rect.height
# Если рамка курсора существует, перерисовывает курсор
unless @cursor_rect.empty?
cursor_x = @skingrid.cursor.x
cursor_y = @skingrid.cursor.y
cursorrects=[
# границы
Rect.new(cursor_x + 2, cursor_y + 0, 28, 2),
Rect.new(cursor_x + 0, cursor_y + 2, 2, 28),
Rect.new(cursor_x + 30, cursor_y + 2, 2, 28),
Rect.new(cursor_x + 2, cursor_y + 30, 28, 2),
# углы
Rect.new(cursor_x + 0, cursor_y + 0, 2, 2),
Rect.new(cursor_x + 30, cursor_y + 0, 2, 2),
Rect.new(cursor_x + 0, cursor_y + 30, 2, 2),
Rect.new(cursor_x + 30, cursor_y + 30, 2, 2),
# фон
Rect.new(cursor_x + 2, cursor_y + 2, 28, 28)
]
shift = 2 # Смещение элементов
# Рисуем фон...
@cursorbitmap = ensure_bitmap(@cursorbitmap, width, height)
@cursorbitmap.clear
@sprites["cursor"].bitmap = @cursorbitmap
@sprites["cursor"].src_rect.set(0, 0, width, height)
rect = Rect.new(shift, shift, width - shift * 2, height - shift * 2)
@cursorbitmap.stretch_blt(rect, @windowskin, cursorrects[8])
# Рисуем углы...
@cursorbitmap.blt(0, 0, @windowskin, cursorrects[4])# верхний-левый
@cursorbitmap.blt(width - shift, 0, @windowskin, cursorrects[5]) # верхний-правый
@cursorbitmap.blt(0, height - shift, @windowskin, cursorrects[6]) # нижний-правый
@cursorbitmap.blt(width - shift, height - shift, @windowskin, cursorrects[7]) # нижний-левый
# Рисуем границы...
#...верхнюю
rect = Rect.new(shift, 0, width - shift * 2, shift)
@cursorbitmap.stretch_blt(rect, @windowskin, cursorrects[0])
#...левую
rect = Rect.new(0, shift, shift, height - shift * 2)
@cursorbitmap.stretch_blt(rect, @windowskin, cursorrects[1])
#...правую
rect = Rect.new(width - shift, shift, shift, height - shift * 2)
@cursorbitmap.stretch_blt(rect, @windowskin, cursorrects[2])
#...нижнюю
rect = Rect.new(shift, height - shift, width - shift * 2, shift)
@cursorbitmap.stretch_blt(rect, @windowskin, cursorrects[3])
else # Если рамки курсора нет - делаем его невидимым
@sprites["cursor"].visible = false
@sprites["cursor"].src_rect.set(0, 0, 0, 0)
end
# Рисуем стороны окна
for i in 0..3
dwidth = (i == 0 || i == 3) ? @width - 32 : 16
dheight = (i == 0 || i == 3) ? 16 : @height - 32
@sidebitmaps[i] = ensure_bitmap(@sidebitmaps[i], dwidth, dheight)
@sprites["side#{i}"].bitmap = @sidebitmaps[i]
@sprites["side#{i}"].src_rect.set(0, 0, dwidth, dheight)
@sidebitmaps[i].clear
side_rects = @skingrid.side_rects
if side_rects[i].exists?
@sidebitmaps[i].stretch_blt(@sprites["side#{i}"].src_rect,
@windowskin, side_rects[i])
end
end
# Рисуем фон окна, если есть где рисовать
backwidth = @width - 4
backheight = @height - 4
if backwidth > 0 && backheight > 0
@backbitmap = ensure_bitmap(@backbitmap, backwidth, backheight)
@sprites["back"].bitmap = @backbitmap
@sprites["back"].src_rect.set(0, 0, backwidth, backheight)
@backbitmap.clear
if @stretch # Если true, то
# ...растягиваем фон...
@backbitmap.stretch_blt(@sprites["back"].src_rect, @windowskin, @skingrid.back)
else
# ...иначе, укладываем его "плитками"
tile_bitmap(@backbitmap, @sprites["back"].src_rect, @windowskin, @skingrid.back)
end
unless @skingrid.blind.empty?
# накладываем поверх фона текстуру, если она есть
tile_bitmap(@backbitmap, @sprites["back"].src_rect, @windowskin, @skingrid.blind)
end
else
# Если рисовать негде - делаем фон невидимым
@sprites["back"].visible = false
@sprites["back"].src_rect.set(0, 0, 0, 0)
end
end
# Если окно открывается, "разворачиваем" его
unless @openness == 255
opn = @openness / 255.0
for k in @spritekeys
sprite = @sprites[k]
ratio = (@height <= 0) ? 0 : (sprite.y - @y) * 1.0 / @height
sprite.zoom_y = opn
sprite.oy = 0
sprite.y = (@y + (@height / 2.0) + (@height * ratio * opn) - (@height / 2 * opn)).floor
end
else
# Иначе, приводим его к нормальному виду
for k in @spritekeys
sprite = @sprites[k]
sprite.zoom_y = 1.0
end
end
i = 0
# Ensure Z order
for k in @spritekeys
sprite = @sprites[k]
y = sprite.y
sprite.y = i
sprite.oy = (sprite.zoom_y <= 0) ? 0 : (i - y) / sprite.zoom_y
end
end
end
Социальные закладки