Can now also be used outside of LÖVE, in a low capacity.
This commit is contained in:
parent
bc3752da1b
commit
89884e59c4
126
spritesheet.lua
126
spritesheet.lua
|
@ -10,6 +10,16 @@ local error, error_internal do
|
|||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Checks that LÖVE is defined; if not, run in information loading
|
||||
-- mode only.
|
||||
|
||||
local define_love = true
|
||||
if type(love) ~= 'table' then
|
||||
io.stderr:write '[Spritesheet]: Loaded in non-LÖVE environment. Can still load spritesheet data,\n but image drawing methods will not be defined.\n'
|
||||
define_love = false
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Util
|
||||
|
||||
|
@ -17,8 +27,19 @@ local function calculate_animation_duration (self, frame_i)
|
|||
local frame_i = frame_i or math.huge
|
||||
assert(type(self) == 'table')
|
||||
assert(type(frame_i) == 'number')
|
||||
|
||||
-- If time_total is provided, return that.
|
||||
if self.time_total then
|
||||
assert(self.time == nil)
|
||||
self.time = self.time_total/#self
|
||||
return self.time_total
|
||||
end
|
||||
|
||||
-- Easy if number
|
||||
if type(self.time) == 'number' then return math.min(#self, frame_i) * self.time end
|
||||
if type(self.time) == 'number' then
|
||||
return math.min(#self, frame_i) * self.time
|
||||
end
|
||||
|
||||
-- Sum if table
|
||||
local sum = 0
|
||||
for i = 1, math.min(#self, frame_i) do sum = sum + self.time[i] end
|
||||
|
@ -51,6 +72,8 @@ function Sprite.new (quad, imagesheet)
|
|||
return setmetatable({ quad = quad, imagesheet = imagesheet, is_sprite = true }, Sprite)
|
||||
end
|
||||
|
||||
if define_love then
|
||||
|
||||
function Sprite:generateImage ()
|
||||
local imagesheet, quad = self.imagesheet, self.quad
|
||||
self.func = function (x, y)
|
||||
|
@ -75,6 +98,8 @@ function Sprite:getSheetDimensions()
|
|||
return self.imagesheet.image:getDimensions()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
setmetatable(Sprite, {__call = function(_, ...) return Sprite.new(...) end})
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
@ -85,25 +110,26 @@ local Animation = {}
|
|||
|
||||
function Animation.new (self)
|
||||
assert(type(self) == 'table')
|
||||
assert(type(self.time) == 'table' or type(self.time) == 'number' and self.time > 0)
|
||||
assert(type(self.time) == 'table' or type(self.time) == 'number' and self.time > 0 or type(self.time_total) == 'number')
|
||||
assert(#self > 0)
|
||||
assert(type(self.time) == 'number' or #self == #self.time)
|
||||
|
||||
assert(type(self.time) == 'number' or type(self.time_total) == 'number' or #self == #self.time)
|
||||
if self.time_total then assert(self.time == nil) end
|
||||
assert(self.wrap == nil or self.wrap == true or self.wrap == false)
|
||||
|
||||
local self = setmetatable(self, Animation)
|
||||
self.duration = calculate_animation_duration(self)
|
||||
self.is_animation = true
|
||||
|
||||
-- Contact frame?
|
||||
if self.contact_frame then
|
||||
-- why was this minus one? It screwed up the audio.
|
||||
--self.contact_time = calculate_animation_duration(self, self.contact_frame - 1)
|
||||
self.contact_time = calculate_animation_duration(self, self.contact_frame)
|
||||
end
|
||||
--
|
||||
return self
|
||||
end
|
||||
|
||||
if define_love then
|
||||
|
||||
function Animation:generateImage ()
|
||||
self.func = function (x, y, t)
|
||||
t = t or 0
|
||||
|
@ -138,6 +164,8 @@ function Animation:getSheetDimensions()
|
|||
return self.imagesheet.image:getDimensions()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
setmetatable(Animation, {__call = function(_, ...) return Animation.new(...) end})
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
@ -152,7 +180,7 @@ local function calculate_frame_times (time_list, nr_frames)
|
|||
assert(type(time_list) == 'table' or type(time_list) == 'number')
|
||||
assert(type(nr_frames) == 'number')
|
||||
|
||||
local frame_times = { [0] = -math.huge }
|
||||
local frame_times = { [0] = -math.huge, orig = not define_love and time_list or nil }
|
||||
|
||||
if type(time_list) == 'number' then
|
||||
for i = 1, nr_frames do
|
||||
|
@ -168,27 +196,33 @@ local function calculate_frame_times (time_list, nr_frames)
|
|||
return frame_times
|
||||
end
|
||||
|
||||
local function load_quads (image, quad_data, imagesheet)
|
||||
assert(image.typeOf and image:typeOf('Image'))
|
||||
assert(type(quad_data) == 'table')
|
||||
local function load_quads (_, _, quad_data, imagesheet)
|
||||
assert(type(imagesheet) == 'table')
|
||||
assert(type(imagesheet.tiles_per_row) == 'number')
|
||||
assert(type(imagesheet.tiles_per_column) == 'number')
|
||||
|
||||
local image_width, image_height = image:getDimensions()
|
||||
local tile_width, tile_height = quad_data.tile_width, quad_data.tile_height
|
||||
local tiles_per_row = image_width/tile_width
|
||||
local max_quad_id = tiles_per_row * (image_height/tile_height) - 1
|
||||
local tile_width, tile_height = imagesheet.tile_width, imagesheet.tile_height
|
||||
local tiles_per_row = imagesheet.tiles_per_row
|
||||
local max_quad_id = tiles_per_row * imagesheet.tiles_per_column - 1
|
||||
local quad_cache = {}
|
||||
|
||||
local function quad_from_id (id)
|
||||
-- Error checking
|
||||
if type(id) ~= 'number' then error('All quad ids must be natural numbers, but one was %s (%s)', id, type(id)) end
|
||||
if id ~= math.floor(id) then error('All quad ids must be natural numbers, but one was %03.03f (floating point number)', id) end
|
||||
if id % 1 ~= 0 then error('All quad ids must be natural numbers, but one was %03.03f (floating point number)', id) end
|
||||
if not (0 <= id and id <= max_quad_id) then error('All quad ids must - for this spritesheet ("%s") - be natural numbers equal/below %i, but one was %s = 0x%X', imagesheet.filename, max_quad_id, id, id) end
|
||||
|
||||
-- Calculate
|
||||
if not quad_cache[id] then
|
||||
quad_cache[id] = love.graphics.newQuad((id%tiles_per_row)*tile_width, math.floor(id/tiles_per_row)*tile_height, tile_width, tile_height, image_width, image_height)
|
||||
local quad = quad_cache[id]
|
||||
if quad == nil then
|
||||
if define_love then
|
||||
quad = love.graphics.newQuad((id%tiles_per_row)*tile_width, math.floor(id/tiles_per_row)*tile_height, tile_width, tile_height, imagesheet.width, imagesheet.height)
|
||||
else
|
||||
quad = { id = id, (id%tiles_per_row)*tile_width, math.floor(id/tiles_per_row)*tile_height, tile_width, tile_height }
|
||||
end
|
||||
quad_cache[id] = quad
|
||||
end
|
||||
return quad_cache[id]
|
||||
return quad
|
||||
end
|
||||
|
||||
local function visit_animation (t)
|
||||
|
@ -203,7 +237,7 @@ local function load_quads (image, quad_data, imagesheet)
|
|||
return Sprite(quad_from_id(n), imagesheet)
|
||||
end
|
||||
|
||||
local function visit_node (t)
|
||||
local function visit_node (t, already_seen)
|
||||
assert(type(t) == 'table')
|
||||
|
||||
for key, val in pairs(t) do
|
||||
|
@ -211,12 +245,14 @@ local function load_quads (image, quad_data, imagesheet)
|
|||
if val_type == 'number' then
|
||||
t[key] = visit_quad(val)
|
||||
elseif val_type == 'table' then
|
||||
local visit_func = val.is_animation and visit_animation or visit_node
|
||||
visit_func(val)
|
||||
local visit_func = rawget(val,'is_animation') and visit_animation or visit_node
|
||||
visit_func(val, already_seen)
|
||||
end
|
||||
end
|
||||
|
||||
error.strict_table(t)
|
||||
if type(error) == 'table' and define_love then
|
||||
error.strict_table(t)
|
||||
end
|
||||
end
|
||||
|
||||
assert(type(quad_data) == 'table')
|
||||
|
@ -232,7 +268,14 @@ local function load_quad_data (filename)
|
|||
|
||||
-- Attempt to load file
|
||||
for filetype in pairs(SPRITESHEET_DATA_FILETYPES) do
|
||||
local chunk, error_msg = love.filesystem.load(filename..'.lua')
|
||||
|
||||
local chunk, error_msg
|
||||
if define_love then
|
||||
chunk, error_msg = love.filesystem.load(filename..'.lua')
|
||||
else
|
||||
chunk, error_msg = loadfile(filename..'.lua')
|
||||
end
|
||||
|
||||
if chunk then
|
||||
local data = setfenv(chunk, SPRITESHEET_ENV)()
|
||||
|
||||
|
@ -292,25 +335,36 @@ function SpriteSheet.new (filename)
|
|||
-- spriteimage itself if you can, as it will silently ignore
|
||||
-- several errors.
|
||||
|
||||
local img, width, height
|
||||
if define_love then
|
||||
img = love.graphics.newImage(filename..'.png')
|
||||
width, height = img:getDimensions()
|
||||
else
|
||||
img = require 'imlib2'.image.load(filename..'.png')
|
||||
width = img:get_width()
|
||||
height = img:get_height()
|
||||
end
|
||||
|
||||
-- Set info
|
||||
local self = setmetatable({}, SpriteSheet)
|
||||
self.filename = filename
|
||||
self.image = love.graphics.newImage(filename..'.png')
|
||||
self.image = img
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.origin_x = 0
|
||||
self.origin_y = 0
|
||||
|
||||
-- TODO: Give warning/error due to rounding down.
|
||||
quad_data.tile_width = quad_data.tile_width or math.floor(self.image:getWidth() / quad_data.tiles_per_row)
|
||||
quad_data.tile_height = quad_data.tile_height or math.floor(self.image:getHeight() / quad_data.tiles_per_column)
|
||||
print(self.tile_width, self.tile_height)
|
||||
self.tiles_per_row = quad_data.tiles_per_row or math.floor(width / quad_data.tile_width)
|
||||
self.tiles_per_column = quad_data.tiles_per_column or math.floor(height / quad_data.tile_height)
|
||||
|
||||
self.tile_width = quad_data.tile_width
|
||||
self.tile_height = quad_data.tile_height
|
||||
self.tile_width = quad_data.tile_width or math.floor(width / self.tiles_per_row)
|
||||
self.tile_height = quad_data.tile_height or math.floor(height / self.tiles_per_column)
|
||||
|
||||
-- Error checking
|
||||
do
|
||||
local rem_width = self.image:getWidth() % self.tile_width
|
||||
local rem_height = self.image:getHeight() % self.tile_height
|
||||
local rem_width = width % self.tile_width
|
||||
local rem_height = height % self.tile_height
|
||||
if not quad_data.force_uneven_tile_size and (rem_width ~= 0 or rem_height ~= 0) then
|
||||
local s = ('Bad spritesheet "%s". Image size (%i, %i) must be dividable by tile size (%i, %i)')
|
||||
:format(filename, self.image:getWidth(), self.image:getHeight(), self.tile_width, self.tile_height)
|
||||
|
@ -326,7 +380,7 @@ function SpriteSheet.new (filename)
|
|||
end
|
||||
|
||||
-- Import quads into SpriteSheet
|
||||
self.quads = load_quads(self.image, quad_data, self)
|
||||
self.quads = load_quads(width, height, quad_data, self)
|
||||
if rawget(self.quads, 'is_sprite') or rawget(self.quads, 'is_animation') then
|
||||
self.only_quads = self.quads
|
||||
else
|
||||
|
@ -344,15 +398,19 @@ function SpriteSheet:setOrigin (ox, oy, mode)
|
|||
assert(type(ox) == 'number')
|
||||
assert(type(oy) == 'number')
|
||||
|
||||
self.origin_orig = { ox, oy, mode }
|
||||
|
||||
if mode == 'absolute' then
|
||||
self.origin_x = ox
|
||||
self.origin_y = oy
|
||||
elseif mode == 'relative' or mode == nil then
|
||||
self.origin_x = self.tile_width * ox
|
||||
self.origin_y = self.tile_height * oy
|
||||
self.origin_x = math.floor(self.tile_width * ox)
|
||||
self.origin_y = math.floor(self.tile_height * oy)
|
||||
else
|
||||
error('Unknown origin mode %s (%s)', mode, type(mode))
|
||||
end
|
||||
assert(self.origin_x % 1 == 0)
|
||||
assert(self.origin_y % 1 == 0)
|
||||
end
|
||||
|
||||
function SpriteSheet:getQuadKeys ()
|
||||
|
|
Loading…
Reference in New Issue
Block a user