Files
infinitefusion-e18/Data/Scripts/007_Objects and windows/007_BitmapSprite.rb

372 lines
12 KiB
Ruby

#===============================================================================
# Sprite class that maintains a bitmap of its own.
# This bitmap can't be changed to a different one.
#===============================================================================
class BitmapSprite < Sprite
attr_reader :text_themes
def initialize(width, height, viewport = nil)
super(viewport)
self.bitmap = Bitmap.new(width, height)
@text_themes = {}
@initialized = true
end
def dispose
self.bitmap.dispose if !self.disposed?
super
end
def bitmap=(value)
super(value) if !@initialized
end
#-----------------------------------------------------------------------------
def add_text_theme(id, base_color, shadow_color = nil)
@text_themes[id] = [base_color, shadow_color]
end
# TODO: Replaces def pbDrawTextPositions.
def draw_themed_text(string, text_x, text_y, align = :left, theme = :default, outline = :shadow)
string_size = self.bitmap.text_size(string)
case align
when :right
text_x -= string_size.width
when :center
text_x -= (string_size.width / 2)
end
if !@text_themes[theme]
theme = (@text_themes[:default]) ? :default : @text_themes.keys.first
end
case outline || :shadow
when :shadow
draw_shadowed_text(string, text_x, text_y, theme)
when :outline
draw_outlined_text(string, text_x, text_y, theme)
when :none
draw_plain_text(string, text_x, text_y, theme)
end
end
# TODO: Replaces def pbDrawShadowText.
def draw_shadowed_text(string, text_x, text_y, theme)
return if !@text_themes[theme]
base_color, shadow_color = @text_themes[theme]
string_size = self.bitmap.text_size(string)
string_width = string_size.width + 1
string_height = string_size.height + 1
if shadow_color && shadow_color.alpha > 0
self.bitmap.font.color = shadow_color
self.bitmap.draw_text(text_x + 2, text_y, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x, text_y + 2, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x + 2, text_y + 2, string_width, string_height, string, 0)
end
if base_color && base_color.alpha > 0
self.bitmap.font.color = base_color
self.bitmap.draw_text(text_x, text_y, string_width, string_height, string, 0)
end
end
# TODO: Replaces def pbDrawOutlineText.
def draw_outlined_text(string, text_x, text_y, theme)
return if !@text_themes[theme]
base_color, shadow_color = @text_themes[theme]
string_size = self.bitmap.text_size(string)
string_width = string_size.width + 1
string_height = string_size.height + 1
if shadow_color && shadow_color.alpha > 0
self.bitmap.font.color = shadow_color
self.bitmap.draw_text(text_x - 2, text_y - 2, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x, text_y - 2, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x + 2, text_y - 2, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x - 2, text_y, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x + 2, text_y, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x - 2, text_y + 2, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x, text_y + 2, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x + 2, text_y + 2, string_width, string_height, string, 0)
end
if base_color && base_color.alpha > 0
self.bitmap.font.color = base_color
self.bitmap.draw_text(text_x, text_y, string_width, string_height, string, 0)
end
end
# TODO: Replaces def pbDrawPlainText.
def draw_plain_text(string, text_x, text_y, theme)
return if !@text_themes[theme]
base_color = @text_themes[theme][0]
return if !base_color || base_color.alpha == 0
string_size = self.bitmap.text_size(string)
string_width = string_size.width + 1
string_height = string_size.height + 1
self.bitmap.font.color = base_color
self.bitmap.draw_text(text_x, text_y, string_width, string_height, string, 0)
end
#-----------------------------------------------------------------------------
# TODO: Replaces def pbDrawImagePositions.
def draw_image(filename, image_x, image_y, src_x = 0, src_y = 0, src_width = -1, src_height = -1)
src_bitmap = (filename.is_a?(AnimatedBitmap)) ? filename : AnimatedBitmap.new(pbBitmapName(filename))
src_width = (src_width >= 0) ? src_width : src_bitmap.width
src_height = (src_height >= 0) ? src_height : src_bitmap.height
src_rect = Rect.new(src_x, src_y, src_width, src_height)
self.bitmap.blt(image_x, image_y, src_bitmap.bitmap, src_rect)
src_bitmap.dispose if !filename.is_a?(AnimatedBitmap)
end
end
#===============================================================================
#
#===============================================================================
class AnimatedSprite < Sprite
attr_reader :frame
attr_reader :framewidth
attr_reader :frameheight
attr_reader :framecount
attr_reader :animname
# frameskip is in 1/20ths of a second, and is the time between frame changes.
def initializeLong(animname, framecount, framewidth, frameheight, frameskip)
@animname = pbBitmapName(animname)
@time_per_frame = [1, frameskip].max / 20.0
raise _INTL("Frame width is 0") if framewidth == 0
raise _INTL("Frame height is 0") if frameheight == 0
begin
@animbitmap = AnimatedBitmap.new(animname).deanimate
rescue
@animbitmap = Bitmap.new(framewidth, frameheight)
end
if @animbitmap.width % framewidth != 0
raise _INTL("Bitmap's width ({1}) is not a multiple of frame width ({2}) [Bitmap={3}]",
@animbitmap.width, framewidth, animname)
end
if @animbitmap.height % frameheight != 0
raise _INTL("Bitmap's height ({1}) is not a multiple of frame height ({2}) [Bitmap={3}]",
@animbitmap.height, frameheight, animname)
end
@framecount = framecount
@framewidth = framewidth
@frameheight = frameheight
@framesperrow = @animbitmap.width / @framewidth
@playing = false
self.bitmap = @animbitmap
self.src_rect.width = @framewidth
self.src_rect.height = @frameheight
self.frame = 0
end
# Shorter version of AnimatedSprite. All frames are placed on a single row
# of the bitmap, so that the width and height need not be defined beforehand.
# frameskip is in 1/20ths of a second, and is the time between frame changes.
def initializeShort(animname, framecount, frameskip)
@animname = pbBitmapName(animname)
@time_per_frame = [1, frameskip].max / 20.0
begin
@animbitmap = AnimatedBitmap.new(animname).deanimate
rescue
@animbitmap = Bitmap.new(framecount * 4, 32)
end
if @animbitmap.width % framecount != 0
raise _INTL("Bitmap's width ({1}) is not a multiple of frame count ({2}) [Bitmap={3}]",
@animbitmap.width, framewidth, animname)
end
@framecount = framecount
@framewidth = @animbitmap.width / @framecount
@frameheight = @animbitmap.height
@framesperrow = framecount
@playing = false
self.bitmap = @animbitmap
self.src_rect.width = @framewidth
self.src_rect.height = @frameheight
self.frame = 0
end
def initialize(*args)
if args.length == 1
super(args[0][3])
initializeShort(args[0][0], args[0][1], args[0][2])
else
super(args[5])
initializeLong(args[0], args[1], args[2], args[3], args[4])
end
end
def self.create(animname, framecount, frameskip, viewport = nil)
return self.new([animname, framecount, frameskip, viewport])
end
def dispose
return if disposed?
@animbitmap.dispose
@animbitmap = nil
super
end
def playing?
return @playing
end
def frame=(value)
@frame = value
self.src_rect.x = @frame % @framesperrow * @framewidth
self.src_rect.y = @frame / @framesperrow * @frameheight
end
def start
@playing = true
end
alias play start
def stop
@playing = false
end
def update
super
if @playing
new_frame = (System.uptime / @time_per_frame).to_i % self.framecount
self.frame = new_frame if self.frame != new_frame
end
end
end
#===============================================================================
# Displays an icon bitmap in a sprite. Supports animated images.
#===============================================================================
class IconSprite < Sprite
attr_reader :name
def initialize(*args)
case args.length
when 0
super(nil)
self.bitmap = nil
when 1
super(args[0])
self.bitmap = nil
when 2
super(nil)
self.x = args[0]
self.y = args[1]
else
super(args[2])
self.x = args[0]
self.y = args[1]
end
@name = ""
@_iconbitmap = nil
end
def dispose
clearBitmaps
super
end
# Sets the icon's filename. Alias for setBitmap.
def name=(value)
setBitmap(value)
end
# Sets the icon's filename.
def setBitmap(file, hue = 0)
oldrc = self.src_rect
clearBitmaps
@name = file
return if file.nil?
if file == ""
@_iconbitmap = nil
else
@_iconbitmap = AnimatedBitmap.new(file, hue)
# for compatibility
self.bitmap = @_iconbitmap ? @_iconbitmap.bitmap : nil
self.src_rect = oldrc
end
end
def clearBitmaps
@_iconbitmap&.dispose
@_iconbitmap = nil
self.bitmap = nil if !self.disposed?
end
def update
super
return if !@_iconbitmap
@_iconbitmap.update
if self.bitmap != @_iconbitmap.bitmap
oldrc = self.src_rect
self.bitmap = @_iconbitmap.bitmap
self.src_rect = oldrc
end
end
end
#===============================================================================
# Sprite class that stores multiple bitmaps, and displays only one at once.
#===============================================================================
class ChangelingSprite < Sprite
# Key is the mode (a symbol).
# Value is one of:
# filepath
# [filepath, src_x, src_y, src_width, src_height]
BITMAPS = {}
def initialize(x = 0, y = 0, viewport = nil)
super(viewport)
self.x = x
self.y = y
@bitmaps = {}
@changeling_data = {}
@current_bitmap = nil
initialize_changeling_data
end
def initialize_changeling_data
self.class::BITMAPS.each_pair { |mode, data| add_bitmap(mode, data) }
end
def dispose
return if disposed?
@bitmaps.each_value { |bm| bm.dispose }
@bitmaps.clear
super
end
#-----------------------------------------------------------------------------
def add_bitmap(mode, *data)
raise ArgumentError.new(_INTL("wrong number of arguments (given {1}, expected 2 or 6)", data.length + 1)) if ![1, 5].include?(data.length)
filepath = (data[0].is_a?(Array)) ? data[0][0] : data[0]
@bitmaps[filepath] = AnimatedBitmap.new(filepath) if !@bitmaps[filepath]
@changeling_data[mode] = (data[0].is_a?(Array) ? data[0].clone : [data[0]])
end
def change_bitmap(mode)
@current_mode = mode
if @current_mode && @changeling_data[@current_mode]
data = @changeling_data[@current_mode]
@current_bitmap = @bitmaps[data[0]]
self.bitmap = @current_bitmap.bitmap
if data.length > 1
self.src_rect.set(data[1], data[2], data[3], data[4])
else
self.src_rect.set(0, 0, self.bitmap.width, self.bitmap.height)
end
else
@current_bitmap = nil
self.bitmap = nil
end
end
#-----------------------------------------------------------------------------
def update
return if disposed?
@bitmaps.each_value { |bm| bm.update }
self.bitmap = @current_bitmap.bitmap if @current_bitmap
end
end