@ -5,22 +5,22 @@
-- @license MIT/X11
-- @license MIT/X11
local STI = {
local STI = {
_LICENSE = " MIT/X11 " ,
_LICENSE = " MIT/X11 " ,
_URL = " https://github.com/karai17/Simple-Tiled-Implementation " ,
_URL = " https://github.com/karai17/Simple-Tiled-Implementation " ,
_VERSION = " 1.2.3.0 " ,
_VERSION = " 1.2.3.0 " ,
_DESCRIPTION = " Simple Tiled Implementation is a Tiled Map Editor library designed for the *awesome* LÖVE framework. " ,
_DESCRIPTION = " Simple Tiled Implementation is a Tiled Map Editor library designed for the *awesome* LÖVE framework. " ,
cache = { }
cache = { } ,
}
}
STI.__index = STI
STI.__index = STI
local love = _G.love
local love = _G.love
local cwd = ( ... ) : gsub ( ' %.init$ ' , ' ' ) .. " . "
local cwd = ( ... ) : gsub ( " %.init$ " , " " ) .. " . "
local utils = require ( cwd .. " utils " )
local utils = require ( cwd .. " utils " )
local ceil = math.ceil
local ceil = math.ceil
local floor = math.floor
local floor = math.floor
local lg = require ( cwd .. " graphics " )
local lg = require ( cwd .. " graphics " )
local atlas = require ( cwd .. " atlas " )
local atlas = require ( cwd .. " atlas " )
local Map = { }
local Map = { }
Map.__index = Map
Map.__index = Map
local function new ( map , plugins , ox , oy )
local function new ( map , plugins , ox , oy )
@ -31,10 +31,7 @@ local function new(map, plugins, ox, oy)
else
else
-- Check for valid map type
-- Check for valid map type
local ext = map : sub ( - 4 , - 1 )
local ext = map : sub ( - 4 , - 1 )
assert ( ext == " .lua " , string.format (
assert ( ext == " .lua " , string.format ( " Invalid file type: %s. File must be of type: lua. " , ext ) )
" Invalid file type: %s. File must be of type: lua. " ,
ext
) )
-- Get directory of map
-- Get directory of map
dir = map : reverse ( ) : find ( " [/ \\ ] " ) or " "
dir = map : reverse ( ) : find ( " [/ \\ ] " ) or " "
@ -79,58 +76,58 @@ function Map:init(path, plugins, ox, oy)
end
end
self : resize ( )
self : resize ( )
self.objects = { }
self.objects = { }
self.tiles = { }
self.tiles = { }
self.tileInstances = { }
self.tileInstances = { }
self.offsetx = ox or 0
self.offsetx = ox or 0
self.offsety = oy or 0
self.offsety = oy or 0
self.freeBatchSprites = { }
self.freeBatchSprites = { }
setmetatable ( self.freeBatchSprites , { __mode = ' k ' } )
setmetatable ( self.freeBatchSprites , { __mode = " k " } )
-- Set tiles, images
-- Set tiles, images
local gid = 1
local gid = 1
for i , tileset in ipairs ( self.tilesets ) do
for i , tileset in ipairs ( self.tilesets ) do
assert ( not tileset.filename , " STI does not support external Tilesets. \n You need to embed all Tilesets. " )
assert ( not tileset.filename , " STI does not support external Tilesets. \n You need to embed all Tilesets. " )
if tileset.image then
if tileset.image then
-- Cache images
-- Cache images
if lg.isCreated then
if lg.isCreated then
local formatted_path = utils.format_path ( path .. tileset.image )
local formatted_path = utils.format_path ( path .. tileset.image )
if not STI.cache [ formatted_path ] then
if not STI.cache [ formatted_path ] then
utils.fix_transparent_color ( tileset , formatted_path )
utils.fix_transparent_color ( tileset , formatted_path )
utils.cache_image ( STI , formatted_path , tileset.image )
utils.cache_image ( STI , formatted_path , tileset.image )
else
else
tileset.image = STI.cache [ formatted_path ]
tileset.image = STI.cache [ formatted_path ]
end
end
end
end
gid = self : setTiles ( i , tileset , gid )
gid = self : setTiles ( i , tileset , gid )
elseif tileset.tilecount > 0 then
elseif tileset.tilecount > 0 then
-- Build atlas for image collection
-- Build atlas for image collection
local files , ids = { } , { }
local files , ids = { } , { }
for j = 1 , # tileset.tiles do
for j = 1 , # tileset.tiles do
files [ j ] = utils.format_path ( path .. tileset.tiles [ j ] . image )
files [ j ] = utils.format_path ( path .. tileset.tiles [ j ] . image )
ids [ j ] = tileset.tiles [ j ] . id
ids [ j ] = tileset.tiles [ j ] . id
end
end
local map = atlas.Atlas ( files , " ids " , ids )
local map = atlas.Atlas ( files , " ids " , ids )
if lg.isCreated then
if lg.isCreated then
local formatted_path = utils.format_path ( path .. tileset.name )
local formatted_path = utils.format_path ( path .. tileset.name )
if not STI.cache [ formatted_path ] then
if not STI.cache [ formatted_path ] then
-- No need to fix transparency color for collections
-- No need to fix transparency color for collections
utils.cache_image ( STI , formatted_path , map.image )
utils.cache_image ( STI , formatted_path , map.image )
tileset.image = map.image
tileset.image = map.image
else
else
tileset.image = STI.cache [ formatted_path ]
tileset.image = STI.cache [ formatted_path ]
end
end
end
end
gid = self : setAtlasTiles ( i , tileset , map.coords , gid )
gid = self : setAtlasTiles ( i , tileset , map.coords , gid )
end
end
end
end
local layers = { }
local layers = { }
@ -174,7 +171,7 @@ end
-- @param plugins A list of plugins to load
-- @param plugins A list of plugins to load
function Map : loadPlugins ( plugins )
function Map : loadPlugins ( plugins )
for _ , plugin in ipairs ( plugins ) do
for _ , plugin in ipairs ( plugins ) do
local pluginModulePath = cwd .. ' plugins. ' .. plugin
local pluginModulePath = cwd .. " plugins. " .. plugin
local ok , pluginModule = pcall ( require , pluginModulePath )
local ok , pluginModule = pcall ( require , pluginModulePath )
if ok then
if ok then
for k , func in pairs ( pluginModule ) do
for k , func in pairs ( pluginModule ) do
@ -192,19 +189,19 @@ end
-- @param gid First Global ID in Tileset
-- @param gid First Global ID in Tileset
-- @return number Next Tileset's first Global ID
-- @return number Next Tileset's first Global ID
function Map : setTiles ( index , tileset , gid )
function Map : setTiles ( index , tileset , gid )
local quad = lg.newQuad
local quad = lg.newQuad
local imageW = tileset.imagewidth
local imageW = tileset.imagewidth
local imageH = tileset.imageheight
local imageH = tileset.imageheight
local tileW = tileset.tilewidth
local tileW = tileset.tilewidth
local tileH = tileset.tileheight
local tileH = tileset.tileheight
local margin = tileset.margin
local margin = tileset.margin
local spacing = tileset.spacing
local spacing = tileset.spacing
local w = utils.get_tiles ( imageW , tileW , margin , spacing )
local w = utils.get_tiles ( imageW , tileW , margin , spacing )
local h = utils.get_tiles ( imageH , tileH , margin , spacing )
local h = utils.get_tiles ( imageH , tileH , margin , spacing )
for y = 1 , h do
for y = 1 , h do
for x = 1 , w do
for x = 1 , w do
local id = gid - tileset.firstgid
local id = gid - tileset.firstgid
local quadX = ( x - 1 ) * tileW + margin + ( x - 1 ) * spacing
local quadX = ( x - 1 ) * tileW + margin + ( x - 1 ) * spacing
local quadY = ( y - 1 ) * tileH + margin + ( y - 1 ) * spacing
local quadY = ( y - 1 ) * tileH + margin + ( y - 1 ) * spacing
local type = " "
local type = " "
@ -212,10 +209,10 @@ function Map:setTiles(index, tileset, gid)
for _ , tile in pairs ( tileset.tiles ) do
for _ , tile in pairs ( tileset.tiles ) do
if tile.id == id then
if tile.id == id then
properties = tile.properties
properties = tile.properties
animation = tile.animation
animation = tile.animation
objectGroup = tile.objectGroup
objectGroup = tile.objectGroup
type = tile.type
type = tile.type
if tile.terrain then
if tile.terrain then
terrain = { }
terrain = { }
@ -228,31 +225,27 @@ function Map:setTiles(index, tileset, gid)
end
end
local tile = {
local tile = {
id = id ,
id = id ,
gid = gid ,
gid = gid ,
tileset = index ,
tileset = index ,
type = type ,
type = type ,
quad = quad (
quad = quad ( quadX , quadY , tileW , tileH , imageW , imageH ) ,
quadX , quadY ,
properties = properties or { } ,
tileW , tileH ,
terrain = terrain ,
imageW , imageH
animation = animation ,
) ,
properties = properties or { } ,
terrain = terrain ,
animation = animation ,
objectGroup = objectGroup ,
objectGroup = objectGroup ,
frame = 1 ,
frame = 1 ,
time = 0 ,
time = 0 ,
width = tileW ,
width = tileW ,
height = tileH ,
height = tileH ,
sx = 1 ,
sx = 1 ,
sy = 1 ,
sy = 1 ,
r = 0 ,
r = 0 ,
offset = tileset.tileoffset ,
offset = tileset.tileoffset ,
}
}
self.tiles [ gid ] = tile
self.tiles [ gid ] = tile
gid = gid + 1
gid = gid + 1
end
end
end
end
@ -266,50 +259,46 @@ end
-- @param gid First Global ID in Tileset
-- @param gid First Global ID in Tileset
-- @return number Next Tileset's first Global ID
-- @return number Next Tileset's first Global ID
function Map : setAtlasTiles ( index , tileset , coords , gid )
function Map : setAtlasTiles ( index , tileset , coords , gid )
local quad = lg.newQuad
local quad = lg.newQuad
local imageW = tileset.image : getWidth ( )
local imageW = tileset.image : getWidth ( )
local imageH = tileset.image : getHeight ( )
local imageH = tileset.image : getHeight ( )
local firstgid = tileset.firstgid
local firstgid = tileset.firstgid
for i = 1 , # tileset.tiles do
for i = 1 , # tileset.tiles do
local tile = tileset.tiles [ i ]
local tile = tileset.tiles [ i ]
if tile.terrain then
if tile.terrain then
terrain = { }
terrain = { }
for j = 1 , # tile.terrain do
for j = 1 , # tile.terrain do
terrain [ j ] = tileset.terrains [ tile.terrain [ j ] + 1 ]
terrain [ j ] = tileset.terrains [ tile.terrain [ j ] + 1 ]
end
end
end
end
local tile = {
local tile = {
id = tile.id ,
id = tile.id ,
gid = firstgid + tile.id ,
gid = firstgid + tile.id ,
tileset = index ,
tileset = index ,
class = tile.class ,
class = tile.class ,
quad = quad (
quad = quad ( coords [ i ] . x , coords [ i ] . y , tile.width , tile.height , imageW , imageH ) ,
coords [ i ] . x , coords [ i ] . y ,
properties = tile.properties or { } ,
tile.width , tile.height ,
terrain = terrain ,
imageW , imageH
animation = tile.animation ,
) ,
objectGroup = tile.objectGroup ,
properties = tile.properties or { } ,
frame = 1 ,
terrain = terrain ,
time = 0 ,
animation = tile.animation ,
width = tile.width ,
objectGroup = tile.objectGroup ,
height = tile.height ,
frame = 1 ,
sx = 1 ,
time = 0 ,
sy = 1 ,
width = tile.width ,
r = 0 ,
height = tile.height ,
offset = tileset.tileoffset ,
sx = 1 ,
}
sy = 1 ,
r = 0 ,
-- Be aware that in collections self.tiles can be a sparse array
offset = tileset.tileoffset ,
self.tiles [ tile.gid ] = tile
}
end
-- Be aware that in collections self.tiles can be a sparse array
return gid + # tileset.tiles
self.tiles [ tile.gid ] = tile
end
return gid + # tileset.tiles
end
end
--- Create Layers
--- Create Layers
@ -318,13 +307,19 @@ end
function Map : setLayer ( layer , path )
function Map : setLayer ( layer , path )
if layer.encoding then
if layer.encoding then
if layer.encoding == " base64 " then
if layer.encoding == " base64 " then
assert ( require " ffi " , " Compressed maps require LuaJIT FFI. \n Please Switch your interperator to LuaJIT or your Tile Layer Format to \" CSV \" . " )
assert (
require ( " ffi " ) ,
' Compressed maps require LuaJIT FFI. \n Please Switch your interperator to LuaJIT or your Tile Layer Format to "CSV". '
)
local fd = love.data . decode ( " string " , " base64 " , layer.data )
local fd = love.data . decode ( " string " , " base64 " , layer.data )
if not layer.compression then
if not layer.compression then
layer.data = utils.get_decompressed_data ( fd )
layer.data = utils.get_decompressed_data ( fd )
else
else
assert ( love.data . decompress , " zlib and gzip compression require LOVE 11.0+. \n Please set your Tile Layer Format to \" Base64 (uncompressed) \" or \" CSV \" . " )
assert (
love.data . decompress ,
' zlib and gzip compression require LOVE 11.0+. \n Please set your Tile Layer Format to "Base64 (uncompressed)" or "CSV". '
)
if layer.compression == " zlib " then
if layer.compression == " zlib " then
local data = love.data . decompress ( " string " , " zlib " , fd )
local data = love.data . decompress ( " string " , " zlib " , fd )
@ -339,21 +334,27 @@ function Map:setLayer(layer, path)
end
end
end
end
layer.x = ( layer.x or 0 ) + layer.offsetx + self.offsetx
layer.x = ( layer.x or 0 ) + layer.offsetx + self.offsetx
layer.y = ( layer.y or 0 ) + layer.offsety + self.offsety
layer.y = ( layer.y or 0 ) + layer.offsety + self.offsety
layer.update = function ( ) end
layer.update = function ( ) end
if layer.type == " tilelayer " then
if layer.type == " tilelayer " then
self : setTileData ( layer )
self : setTileData ( layer )
self : setSpriteBatches ( layer )
self : setSpriteBatches ( layer )
layer.draw = function ( ) self : drawTileLayer ( layer ) end
layer.draw = function ( )
self : drawTileLayer ( layer )
end
elseif layer.type == " objectgroup " then
elseif layer.type == " objectgroup " then
self : setObjectData ( layer )
self : setObjectData ( layer )
self : setObjectCoordinates ( layer )
self : setObjectCoordinates ( layer )
self : setObjectSpriteBatches ( layer )
self : setObjectSpriteBatches ( layer )
layer.draw = function ( ) self : drawObjectLayer ( layer ) end
layer.draw = function ( )
self : drawObjectLayer ( layer )
end
elseif layer.type == " imagelayer " then
elseif layer.type == " imagelayer " then
layer.draw = function ( ) self : drawImageLayer ( layer ) end
layer.draw = function ( )
self : drawImageLayer ( layer )
end
if layer.image ~= " " then
if layer.image ~= " " then
local formatted_path = utils.format_path ( path .. layer.image )
local formatted_path = utils.format_path ( path .. layer.image )
@ -361,8 +362,8 @@ function Map:setLayer(layer, path)
utils.cache_image ( STI , formatted_path )
utils.cache_image ( STI , formatted_path )
end
end
layer.image = STI.cache [ formatted_path ]
layer.image = STI.cache [ formatted_path ]
layer.width = layer.image : getWidth ( )
layer.width = layer.image : getWidth ( )
layer.height = layer.image : getHeight ( )
layer.height = layer.image : getHeight ( )
end
end
end
end
@ -380,7 +381,7 @@ function Map:setTileData(layer)
return
return
end
end
local i = 1
local i = 1
local map = { }
local map = { }
for y = 1 , layer.height do
for y = 1 , layer.height do
@ -404,7 +405,7 @@ end
-- @param layer The Object Layer
-- @param layer The Object Layer
function Map : setObjectData ( layer )
function Map : setObjectData ( layer )
for _ , object in ipairs ( layer.objects ) do
for _ , object in ipairs ( layer.objects ) do
object.layer = layer
object.layer = layer
self.objects [ object.id ] = object
self.objects [ object.id ] = object
end
end
end
end
@ -413,10 +414,10 @@ end
-- @param layer The Object Layer
-- @param layer The Object Layer
function Map : setObjectCoordinates ( layer )
function Map : setObjectCoordinates ( layer )
for _ , object in ipairs ( layer.objects ) do
for _ , object in ipairs ( layer.objects ) do
local x = layer.x + object.x
local x = layer.x + object.x
local y = layer.y + object.y
local y = layer.y + object.y
local w = object.width
local w = object.width
local h = object.height
local h = object.height
local cos = math.cos ( math.rad ( object.rotation ) )
local cos = math.cos ( math.rad ( object.rotation ) )
local sin = math.sin ( math.rad ( object.rotation ) )
local sin = math.sin ( math.rad ( object.rotation ) )
@ -424,10 +425,10 @@ function Map:setObjectCoordinates(layer)
object.rectangle = { }
object.rectangle = { }
local vertices = {
local vertices = {
{ x = x , y = y } ,
{ x = x , y = y } ,
{ x = x + w , y = y } ,
{ x = x + w , y = y } ,
{ x = x + w , y = y + h } ,
{ x = x + w , y = y + h } ,
{ x = x , y = y + h } ,
{ x = x , y = y + h } ,
}
}
for _ , vertex in ipairs ( vertices ) do
for _ , vertex in ipairs ( vertices ) do
@ -444,14 +445,14 @@ function Map:setObjectCoordinates(layer)
end
end
elseif object.shape == " polygon " then
elseif object.shape == " polygon " then
for _ , vertex in ipairs ( object.polygon ) do
for _ , vertex in ipairs ( object.polygon ) do
vertex.x = vertex.x + x
vertex.x = vertex.x + x
vertex.y = vertex.y + y
vertex.y = vertex.y + y
vertex.x , vertex.y = utils.rotate_vertex ( self , vertex , x , y , cos , sin )
vertex.x , vertex.y = utils.rotate_vertex ( self , vertex , x , y , cos , sin )
end
end
elseif object.shape == " polyline " then
elseif object.shape == " polyline " then
for _ , vertex in ipairs ( object.polyline ) do
for _ , vertex in ipairs ( object.polyline ) do
vertex.x = vertex.x + x
vertex.x = vertex.x + x
vertex.y = vertex.y + y
vertex.y = vertex.y + y
vertex.x , vertex.y = utils.rotate_vertex ( self , vertex , x , y , cos , sin )
vertex.x , vertex.y = utils.rotate_vertex ( self , vertex , x , y , cos , sin )
end
end
end
end
@ -527,16 +528,16 @@ end
-- @param number Tile location on Y axis (in tiles)
-- @param number Tile location on Y axis (in tiles)
function Map : addNewLayerTile ( layer , chunk , tile , x , y )
function Map : addNewLayerTile ( layer , chunk , tile , x , y )
local tileset = tile.tileset
local tileset = tile.tileset
local image = self.tilesets [ tile.tileset ] . image
local image = self.tilesets [ tile.tileset ] . image
local batches
local batches
local size
local size
if chunk then
if chunk then
batches = chunk.batches
batches = chunk.batches
size = chunk.width * chunk.height
size = chunk.width * chunk.height
else
else
batches = layer.batches
batches = layer.batches
size = layer.width * layer.height
size = layer.width * layer.height
end
end
batches [ tileset ] = batches [ tileset ] or lg.newSpriteBatch ( image , size )
batches [ tileset ] = batches [ tileset ] or lg.newSpriteBatch ( image , size )
@ -547,11 +548,11 @@ function Map:addNewLayerTile(layer, chunk, tile, x, y)
local instance = {
local instance = {
layer = layer ,
layer = layer ,
chunk = chunk ,
chunk = chunk ,
gid = tile.gid ,
gid = tile.gid ,
x = tileX ,
x = tileX ,
y = tileY ,
y = tileY ,
r = tile.r ,
r = tile.r ,
oy = 0
oy = 0 ,
}
}
-- NOTE: STI can run headless so it is not guaranteed that a batch exists.
-- NOTE: STI can run headless so it is not guaranteed that a batch exists.
@ -575,10 +576,10 @@ function Map:set_batches(layer, chunk)
local offsetX = chunk and chunk.x or 0
local offsetX = chunk and chunk.x or 0
local offsetY = chunk and chunk.y or 0
local offsetY = chunk and chunk.y or 0
local startX = 1
local startX = 1
local startY = 1
local startY = 1
local endX = chunk and chunk.width or layer.width
local endX = chunk and chunk.width or layer.width
local endY = chunk and chunk.height or layer.height
local endY = chunk and chunk.height or layer.height
local incrementX = 1
local incrementX = 1
local incrementY = 1
local incrementY = 1
@ -683,7 +684,7 @@ end
-- @param layer The Object Layer
-- @param layer The Object Layer
function Map : setObjectSpriteBatches ( layer )
function Map : setObjectSpriteBatches ( layer )
local newBatch = lg.newSpriteBatch
local newBatch = lg.newSpriteBatch
local batches = { }
local batches = { }
if layer.draworder == " topdown " then
if layer.draworder == " topdown " then
table.sort ( layer.objects , function ( a , b )
table.sort ( layer.objects , function ( a , b )
@ -693,13 +694,13 @@ function Map:setObjectSpriteBatches(layer)
for _ , object in ipairs ( layer.objects ) do
for _ , object in ipairs ( layer.objects ) do
if object.gid then
if object.gid then
local tile = self.tiles [ object.gid ] or self : setFlippedGID ( object.gid )
local tile = self.tiles [ object.gid ] or self : setFlippedGID ( object.gid )
local tileset = tile.tileset
local tileset = tile.tileset
local image = self.tilesets [ tileset ] . image
local image = self.tilesets [ tileset ] . image
batches [ tileset ] = batches [ tileset ] or newBatch ( image )
batches [ tileset ] = batches [ tileset ] or newBatch ( image )
local sx = object.width / tile.width
local sx = object.width / tile.width
local sy = object.height / tile.height
local sy = object.height / tile.height
-- Tiled rotates around bottom left corner, where love2D rotates around top left corner
-- Tiled rotates around bottom left corner, where love2D rotates around top left corner
@ -731,14 +732,14 @@ function Map:setObjectSpriteBatches(layer)
end
end
local instance = {
local instance = {
id = batch : add ( tile.quad , tileX , tileY , tileR , tile.sx * sx , tile.sy * sy , ox , oy ) ,
id = batch : add ( tile.quad , tileX , tileY , tileR , tile.sx * sx , tile.sy * sy , ox , oy ) ,
batch = batch ,
batch = batch ,
layer = layer ,
layer = layer ,
gid = tile.gid ,
gid = tile.gid ,
x = tileX ,
x = tileX ,
y = tileY - oy ,
y = tileY - oy ,
r = tileR ,
r = tileR ,
oy = oy
oy = oy ,
}
}
self.tileInstances [ tile.gid ] = self.tileInstances [ tile.gid ] or { }
self.tileInstances [ tile.gid ] = self.tileInstances [ tile.gid ] or { }
@ -756,12 +757,12 @@ end
function Map : addCustomLayer ( name , index )
function Map : addCustomLayer ( name , index )
index = index or # self.layers + 1
index = index or # self.layers + 1
local layer = {
local layer = {
type = " customlayer " ,
type = " customlayer " ,
name = name ,
name = name ,
visible = true ,
visible = true ,
opacity = 1 ,
opacity = 1 ,
properties = { } ,
properties = { } ,
}
}
function layer . draw ( ) end
function layer . draw ( ) end
function layer . update ( ) end
function layer . update ( ) end
@ -778,16 +779,16 @@ end
function Map : convertToCustomLayer ( index )
function Map : convertToCustomLayer ( index )
local layer = assert ( self.layers [ index ] , " Layer not found: " .. index )
local layer = assert ( self.layers [ index ] , " Layer not found: " .. index )
layer.type = " customlayer "
layer.type = " customlayer "
layer.x = nil
layer.x = nil
layer.y = nil
layer.y = nil
layer.width = nil
layer.width = nil
layer.height = nil
layer.height = nil
layer.encoding = nil
layer.encoding = nil
layer.data = nil
layer.data = nil
layer.chunks = nil
layer.chunks = nil
layer.objects = nil
layer.objects = nil
layer.image = nil
layer.image = nil
function layer . draw ( ) end
function layer . draw ( ) end
function layer . update ( ) end
function layer . update ( ) end
@ -862,16 +863,19 @@ function Map:update(dt)
tile.time = tile.time + dt * 1000
tile.time = tile.time + dt * 1000
while tile.time > tonumber ( tile.animation [ tile.frame ] . duration ) do
while tile.time > tonumber ( tile.animation [ tile.frame ] . duration ) do
update = true
update = true
tile.time = tile.time - tonumber ( tile.animation [ tile.frame ] . duration )
tile.time = tile.time - tonumber ( tile.animation [ tile.frame ] . duration )
tile.frame = tile.frame + 1
tile.frame = tile.frame + 1
if tile.frame > # tile.animation then tile.frame = 1 end
if tile.frame > # tile.animation then
tile.frame = 1
end
end
end
if update and self.tileInstances [ tile.gid ] then
if update and self.tileInstances [ tile.gid ] then
for _ , j in pairs ( self.tileInstances [ tile.gid ] ) do
for _ , j in pairs ( self.tileInstances [ tile.gid ] ) do
local t = self.tiles [ tonumber ( tile.animation [ tile.frame ] . tileid ) + self.tilesets [ tile.tileset ] . firstgid ]
local t =
self.tiles [ tonumber ( tile.animation [ tile.frame ] . tileid ) + self.tilesets [ tile.tileset ] . firstgid ]
j.batch : set ( j.id , t.quad , j.x , j.y , j.r , tile.sx , tile.sy , 0 , j.oy )
j.batch : set ( j.id , t.quad , j.x , j.y , j.r , tile.sx , tile.sy , 0 , j.oy )
end
end
end
end
@ -948,18 +952,18 @@ end
--- Draw an individual Layer
--- Draw an individual Layer
-- @param layer The Layer to draw
-- @param layer The Layer to draw
function Map . drawLayer ( _ , layer )
function Map . drawLayer ( _ , layer )
local r , g , b , a = lg.getColor ( )
local r , g , b , a = lg.getColor ( )
-- if the layer has a tintcolor set
-- if the layer has a tintcolor set
if layer.tintcolor then
if layer.tintcolor then
r , g , b , a = unpack ( layer.tintcolor )
r , g , b , a = unpack ( layer.tintcolor )
a = a or 255 -- alpha may not be specified
a = a or 255 -- alpha may not be specified
lg.setColor ( r / 255 , g / 255 , b / 255 , a / 255 ) -- Tiled uses 0-255
lg.setColor ( r / 255 , g / 255 , b / 255 , a / 255 ) -- Tiled uses 0-255
-- if a tintcolor is not given just use the current color
-- if a tintcolor is not given just use the current color
else
else
lg.setColor ( r , g , b , a * layer.opacity )
lg.setColor ( r , g , b , a * layer.opacity )
end
end
layer : draw ( )
layer : draw ( )
lg.setColor ( r , g , b , a )
lg.setColor ( r , g , b , a )
end
end
--- Default draw function for Tile Layers
--- Default draw function for Tile Layers
@ -996,10 +1000,10 @@ function Map:drawObjectLayer(layer)
assert ( layer.type == " objectgroup " , " Invalid layer type: " .. layer.type .. " . Layer must be of type: objectgroup " )
assert ( layer.type == " objectgroup " , " Invalid layer type: " .. layer.type .. " . Layer must be of type: objectgroup " )
local line = { 160 , 160 , 160 , 255 * layer.opacity }
local line = { 160 , 160 , 160 , 255 * layer.opacity }
local fill = { 160 , 160 , 160 , 255 * layer.opacity * 0.5 }
local fill = { 160 , 160 , 160 , 255 * layer.opacity * 0.5 }
local r , g , b , a = lg.getColor ( )
local r , g , b , a = lg.getColor ( )
local reset = { r , g , b , a * layer.opacity }
local reset = { r , g , b , a * layer.opacity }
local function sortVertices ( obj )
local function sortVertices ( obj )
local vertex = { }
local vertex = { }
@ -1058,7 +1062,7 @@ function Map:drawObjectLayer(layer)
for _ , batch in pairs ( layer.batches ) do
for _ , batch in pairs ( layer.batches ) do
lg.draw ( batch , 0 , 0 )
lg.draw ( batch , 0 , 0 )
end
end
lg.setColor ( r , g , b , a )
lg.setColor ( r , g , b , a )
end
end
--- Default draw function for Image Layers
--- Default draw function for Image Layers
@ -1118,51 +1122,51 @@ end
-- @param gid The flagged Global ID
-- @param gid The flagged Global ID
-- @return table Flipped Tile
-- @return table Flipped Tile
function Map : setFlippedGID ( gid )
function Map : setFlippedGID ( gid )
local bit31 = 2147483648
local bit31 = 2147483648
local bit30 = 1073741824
local bit30 = 1073741824
local bit29 = 536870912
local bit29 = 536870912
local flipX = false
local flipX = false
local flipY = false
local flipY = false
local flipD = false
local flipD = false
local realgid = gid
local realgid = gid
if realgid >= bit31 then
if realgid >= bit31 then
realgid = realgid - bit31
realgid = realgid - bit31
flipX = not flipX
flipX = not flipX
end
end
if realgid >= bit30 then
if realgid >= bit30 then
realgid = realgid - bit30
realgid = realgid - bit30
flipY = not flipY
flipY = not flipY
end
end
if realgid >= bit29 then
if realgid >= bit29 then
realgid = realgid - bit29
realgid = realgid - bit29
flipD = not flipD
flipD = not flipD
end
end
local tile = self.tiles [ realgid ]
local tile = self.tiles [ realgid ]
local data = {
local data = {
id = tile.id ,
id = tile.id ,
gid = gid ,
gid = gid ,
tileset = tile.tileset ,
tileset = tile.tileset ,
frame = tile.frame ,
frame = tile.frame ,
time = tile.time ,
time = tile.time ,
width = tile.width ,
width = tile.width ,
height = tile.height ,
height = tile.height ,
offset = tile.offset ,
offset = tile.offset ,
quad = tile.quad ,
quad = tile.quad ,
properties = tile.properties ,
properties = tile.properties ,
terrain = tile.terrain ,
terrain = tile.terrain ,
animation = tile.animation ,
animation = tile.animation ,
sx = tile.sx ,
sx = tile.sx ,
sy = tile.sy ,
sy = tile.sy ,
r = tile.r ,
r = tile.r ,
}
}
if flipX then
if flipX then
if flipY and flipD then
if flipY and flipD then
data.r = math.rad ( - 90 )
data.r = math.rad ( - 90 )
data.sy = - 1
data.sy = - 1
elseif flipY then
elseif flipY then
data.sx = - 1
data.sx = - 1
@ -1179,7 +1183,7 @@ function Map:setFlippedGID(gid)
data.sy = - 1
data.sy = - 1
end
end
elseif flipD then
elseif flipD then
data.r = math.rad ( 90 )
data.r = math.rad ( 90 )
data.sy = - 1
data.sy = - 1
end
end
@ -1284,22 +1288,9 @@ function Map:swapTile(instance, tile)
-- Update sprite batch
-- Update sprite batch
if instance.batch then
if instance.batch then
if tile then
if tile then
instance.batch : set (
instance.batch : set ( instance.id , tile.quad , instance.x , instance.y , tile.r , tile.sx , tile.sy )
instance.id ,
tile.quad ,
instance.x ,
instance.y ,
tile.r ,
tile.sx ,
tile.sy
)
else
else
instance.batch : set (
instance.batch : set ( instance.id , instance.x , instance.y , 0 , 0 )
instance.id ,
instance.x ,
instance.y ,
0 ,
0 )
self.freeBatchSprites [ instance.batch ] = self.freeBatchSprites [ instance.batch ] or { }
self.freeBatchSprites [ instance.batch ] = self.freeBatchSprites [ instance.batch ] or { }
table.insert ( self.freeBatchSprites [ instance.batch ] , instance )
table.insert ( self.freeBatchSprites [ instance.batch ] , instance )
@ -1329,12 +1320,12 @@ function Map:swapTile(instance, tile)
newInstance.layer = instance.layer
newInstance.layer = instance.layer
newInstance.batch = instance.batch
newInstance.batch = instance.batch
newInstance.id = instance.id
newInstance.id = instance.id
newInstance.gid = tile.gid or 0
newInstance.gid = tile.gid or 0
newInstance.x = instance.x
newInstance.x = instance.x
newInstance.y = instance.y
newInstance.y = instance.y
newInstance.r = tile.r or 0
newInstance.r = tile.r or 0
newInstance.oy = tile.r ~= 0 and tile.height or 0
newInstance.oy = tile.r ~= 0 and tile.height or 0
table.insert ( self.tileInstances [ tile.gid ] , newInstance )
table.insert ( self.tileInstances [ tile.gid ] , newInstance )
end
end
end
end
@ -1344,35 +1335,26 @@ end
-- @param y The Y axis location of the point (in tiles)
-- @param y The Y axis location of the point (in tiles)
-- @return number The X axis location of the point (in pixels)
-- @return number The X axis location of the point (in pixels)
-- @return number The Y axis location of the point (in pixels)
-- @return number The Y axis location of the point (in pixels)
function Map : convertTileToPixel ( x , y )
function Map : convertTileToPixel ( x , y )
if self.orientation == " orthogonal " then
if self.orientation == " orthogonal " then
local tileW = self.tilewidth
local tileW = self.tilewidth
local tileH = self.tileheight
local tileH = self.tileheight
return
return x * tileW , y * tileH
x * tileW ,
y * tileH
elseif self.orientation == " isometric " then
elseif self.orientation == " isometric " then
local mapH = self.height
local mapH = self.height
local tileW = self.tilewidth
local tileW = self.tilewidth
local tileH = self.tileheight
local tileH = self.tileheight
local offsetX = mapH * tileW / 2
local offsetX = mapH * tileW / 2
return
return ( x - y ) * tileW / 2 + offsetX , ( x + y ) * tileH / 2
( x - y ) * tileW / 2 + offsetX ,
elseif self.orientation == " staggered " or self.orientation == " hexagonal " then
( x + y ) * tileH / 2
local tileW = self.tilewidth
elseif self.orientation == " staggered " or
local tileH = self.tileheight
self.orientation == " hexagonal " then
local tileW = self.tilewidth
local tileH = self.tileheight
local sideLen = self.hexsidelength or 0
local sideLen = self.hexsidelength or 0
if self.staggeraxis == " x " then
if self.staggeraxis == " x " then
return
return x * tileW , ceil ( y ) * ( tileH + sideLen ) + ( ceil ( y ) % 2 == 0 and tileH or 0 )
x * tileW ,
ceil ( y ) * ( tileH + sideLen ) + ( ceil ( y ) % 2 == 0 and tileH or 0 )
else
else
return
return ceil ( x ) * ( tileW + sideLen ) + ( ceil ( x ) % 2 == 0 and tileW or 0 ) , y * tileH
ceil ( x ) * ( tileW + sideLen ) + ( ceil ( x ) % 2 == 0 and tileW or 0 ) ,
y * tileH
end
end
end
end
end
end
@ -1386,20 +1368,16 @@ function Map:convertPixelToTile(x, y)
if self.orientation == " orthogonal " then
if self.orientation == " orthogonal " then
local tileW = self.tilewidth
local tileW = self.tilewidth
local tileH = self.tileheight
local tileH = self.tileheight
return
return x / tileW , y / tileH
x / tileW ,
y / tileH
elseif self.orientation == " isometric " then
elseif self.orientation == " isometric " then
local mapH = self.height
local mapH = self.height
local tileW = self.tilewidth
local tileW = self.tilewidth
local tileH = self.tileheight
local tileH = self.tileheight
local offsetX = mapH * tileW / 2
local offsetX = mapH * tileW / 2
return
return y / tileH + ( x - offsetX ) / tileW , y / tileH - ( x - offsetX ) / tileW
y / tileH + ( x - offsetX ) / tileW ,
y / tileH - ( x - offsetX ) / tileW
elseif self.orientation == " staggered " then
elseif self.orientation == " staggered " then
local staggerX = self.staggeraxis == " x "
local staggerX = self.staggeraxis == " x "
local even = self.staggerindex == " even "
local even = self.staggerindex == " even "
local function topLeft ( x , y )
local function topLeft ( x , y )
if staggerX then
if staggerX then
@ -1474,48 +1452,48 @@ function Map:convertPixelToTile(x, y)
y = y - ( even and tileH / 2 or 0 )
y = y - ( even and tileH / 2 or 0 )
end
end
local halfH = tileH / 2
local halfH = tileH / 2
local ratio = tileH / tileW
local ratio = tileH / tileW
local referenceX = ceil ( x / tileW )
local referenceX = ceil ( x / tileW )
local referenceY = ceil ( y / tileH )
local referenceY = ceil ( y / tileH )
local relativeX = x - referenceX * tileW
local relativeX = x - referenceX * tileW
local relativeY = y - referenceY * tileH
local relativeY = y - referenceY * tileH
if ( halfH - relativeX * ratio > relativeY ) then
if halfH - relativeX * ratio > relativeY then
return topLeft ( referenceX , referenceY )
return topLeft ( referenceX , referenceY )
elseif ( - halfH + relativeX * ratio > relativeY ) then
elseif - halfH + relativeX * ratio > relativeY then
return topRight ( referenceX , referenceY )
return topRight ( referenceX , referenceY )
elseif ( halfH + relativeX * ratio < relativeY ) then
elseif halfH + relativeX * ratio < relativeY then
return bottomLeft ( referenceX , referenceY )
return bottomLeft ( referenceX , referenceY )
elseif ( halfH * 3 - relativeX * ratio < relativeY ) then
elseif halfH * 3 - relativeX * ratio < relativeY then
return bottomRight ( referenceX , referenceY )
return bottomRight ( referenceX , referenceY )
end
end
return referenceX , referenceY
return referenceX , referenceY
elseif self.orientation == " hexagonal " then
elseif self.orientation == " hexagonal " then
local staggerX = self.staggeraxis == " x "
local staggerX = self.staggeraxis == " x "
local even = self.staggerindex == " even "
local even = self.staggerindex == " even "
local tileW = self.tilewidth
local tileW = self.tilewidth
local tileH = self.tileheight
local tileH = self.tileheight
local sideLenX = 0
local sideLenX = 0
local sideLenY = 0
local sideLenY = 0
local colW = tileW / 2
local colW = tileW / 2
local rowH = tileH / 2
local rowH = tileH / 2
if staggerX then
if staggerX then
sideLenX = self.hexsidelength
sideLenX = self.hexsidelength
x = x - ( even and tileW or ( tileW - sideLenX ) / 2 )
x = x - ( even and tileW or ( tileW - sideLenX ) / 2 )
colW = colW - ( colW - sideLenX / 2 ) / 2
colW = colW - ( colW - sideLenX / 2 ) / 2
else
else
sideLenY = self.hexsidelength
sideLenY = self.hexsidelength
y = y - ( even and tileH or ( tileH - sideLenY ) / 2 )
y = y - ( even and tileH or ( tileH - sideLenY ) / 2 )
rowH = rowH - ( rowH - sideLenY / 2 ) / 2
rowH = rowH - ( rowH - sideLenY / 2 ) / 2
end
end
local referenceX = ceil ( x ) / ( colW * 2 )
local referenceX = ceil ( x ) / ( colW * 2 )
local referenceY = ceil ( y ) / ( rowH * 2 )
local referenceY = ceil ( y ) / ( rowH * 2 )
-- If in staggered line, then shift reference by 0.5 of other axes
-- If in staggered line, then shift reference by 0.5 of other axes
if staggerX then
if staggerX then
if ( floor ( referenceX ) % 2 == 0 ) == even then
if ( floor ( referenceX ) % 2 == 0 ) == even then
referenceY = referenceY - 0.5
referenceY = referenceY - 0.5
@ -1526,31 +1504,31 @@ function Map:convertPixelToTile(x, y)
end
end
end
end
local relativeX = x - referenceX * colW * 2
local relativeX = x - referenceX * colW * 2
local relativeY = y - referenceY * rowH * 2
local relativeY = y - referenceY * rowH * 2
local centers
local centers
if staggerX then
if staggerX then
local left = sideLenX / 2
local left = sideLenX / 2
local centerX = left + colW
local centerX = left + colW
local centerY = tileH / 2
local centerY = tileH / 2
centers = {
centers = {
{ x = left , y = centerY } ,
{ x = left , y = centerY } ,
{ x = centerX , y = centerY - rowH } ,
{ x = centerX , y = centerY - rowH } ,
{ x = centerX , y = centerY + rowH } ,
{ x = centerX , y = centerY + rowH } ,
{ x = centerX + colW , y = centerY } ,
{ x = centerX + colW , y = centerY } ,
}
}
else
else
local top = sideLenY / 2
local top = sideLenY / 2
local centerX = tileW / 2
local centerX = tileW / 2
local centerY = top + rowH
local centerY = top + rowH
centers = {
centers = {
{ x = centerX , y = top } ,
{ x = centerX , y = top } ,
{ x = centerX - colW , y = centerY } ,
{ x = centerX - colW , y = centerY } ,
{ x = centerX + colW , y = centerY } ,
{ x = centerX + colW , y = centerY } ,
{ x = centerX , y = centerY + rowH }
{ x = centerX , y = centerY + rowH } ,
}
}
end
end
@ -1571,24 +1549,22 @@ function Map:convertPixelToTile(x, y)
end
end
local offsetsStaggerX = {
local offsetsStaggerX = {
{ x = 1 , y = 1 } ,
{ x = 1 , y = 1 } ,
{ x = 2 , y = 0 } ,
{ x = 2 , y = 0 } ,
{ x = 2 , y = 1 } ,
{ x = 2 , y = 1 } ,
{ x = 3 , y = 1 } ,
{ x = 3 , y = 1 } ,
}
}
local offsetsStaggerY = {
local offsetsStaggerY = {
{ x = 1 , y = 1 } ,
{ x = 1 , y = 1 } ,
{ x = 0 , y = 2 } ,
{ x = 0 , y = 2 } ,
{ x = 1 , y = 2 } ,
{ x = 1 , y = 2 } ,
{ x = 1 , y = 3 } ,
{ x = 1 , y = 3 } ,
}
}
local offsets = staggerX and offsetsStaggerX or offsetsStaggerY
local offsets = staggerX and offsetsStaggerX or offsetsStaggerY
return
return referenceX + offsets [ nearest ] . x , referenceY + offsets [ nearest ] . y
referenceX + offsets [ nearest ] . x ,
referenceY + offsets [ nearest ] . y
end
end
end
end