1
0

Can now also be used outside of LÖVE, in a low capacity.

This commit is contained in:
Jon Michael Aanes 2020-07-07 12:52:37 +02:00
parent bc3752da1b
commit 89884e59c4

View File

@ -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 ()