mirror of
https://github.com/tanema/light_world.lua.git
synced 2024-12-24 20:24:19 +00:00
1389 lines
36 KiB
Lua
Executable File
1389 lines
36 KiB
Lua
Executable File
--- Map object
|
|
-- @module map
|
|
|
|
local path = (...):gsub('%.[^%.]+$', '') .. "."
|
|
local pluginPath = string.gsub(path, "[.]", "/") .. "plugins/"
|
|
local Map = {}
|
|
|
|
-- https://github.com/stevedonovan/Penlight/blob/master/lua/pl/path.lua#L286
|
|
local function formatPath(path)
|
|
local np_gen1,np_gen2 = '[^SEP]+SEP%.%.SEP?','SEP+%.?SEP'
|
|
local np_pat1, np_pat2 = np_gen1:gsub('SEP','/'), np_gen2:gsub('SEP','/')
|
|
local k
|
|
|
|
repeat -- /./ -> /
|
|
path,k = path:gsub(np_pat2,'/')
|
|
until k == 0
|
|
|
|
repeat -- A/../ -> (empty)
|
|
path,k = path:gsub(np_pat1,'')
|
|
until k == 0
|
|
|
|
if path == '' then path = '.' end
|
|
|
|
return path
|
|
end
|
|
|
|
-- Compensation for scale/rotation shift
|
|
local function compensate(tile, x, y, tw, th)
|
|
local tx = x + tile.offset.x
|
|
local ty = y + tile.offset.y
|
|
local origx = tx
|
|
local origy = ty
|
|
local compx = 0
|
|
local compy = 0
|
|
|
|
if tile.sx < 0 then compx = tw end
|
|
if tile.sy < 0 then compy = th end
|
|
|
|
if tile.r > 0 then
|
|
tx = tx + th - compy
|
|
ty = ty + th - tw + compx
|
|
elseif tile.r < 0 then
|
|
tx = tx + compy
|
|
ty = ty + th - compx
|
|
else
|
|
tx = tx + compx
|
|
ty = ty + compy
|
|
end
|
|
|
|
return tx, ty
|
|
end
|
|
|
|
-- Cache images in main STI module
|
|
local function cache_image(sti, path)
|
|
local image = love.graphics.newImage(path)
|
|
image:setFilter("nearest", "nearest")
|
|
sti.cache[path] = image
|
|
end
|
|
|
|
--- Instance a new map
|
|
-- @param path Path to the map file
|
|
-- @param plugins A list of plugins to load
|
|
-- @param ox Offset of map on the X axis (in pixels)
|
|
-- @param oy Offset of map on the Y axis (in pixels)
|
|
-- @return nil
|
|
function Map:init(STI, path, plugins, ox, oy)
|
|
if type(plugins) == "table" then
|
|
self:loadPlugins(plugins)
|
|
end
|
|
|
|
self:resize()
|
|
self.objects = {}
|
|
self.tiles = {}
|
|
self.tileInstances = {}
|
|
self.drawRange = {
|
|
sx = 1,
|
|
sy = 1,
|
|
ex = self.width,
|
|
ey = self.height,
|
|
}
|
|
self.offsetx = ox or 0
|
|
self.offsety = oy or 0
|
|
self.sti = STI
|
|
|
|
-- Set tiles, images
|
|
local gid = 1
|
|
for i, tileset in ipairs(self.tilesets) do
|
|
assert(tileset.image, "STI does not support Tile Collections.\nYou need to create a Texture Atlas.")
|
|
|
|
-- Cache images
|
|
local formatted_path = formatPath(path .. tileset.image)
|
|
if not self.sti.cache[formatted_path] then
|
|
cache_image(self.sti, formatted_path)
|
|
end
|
|
|
|
-- Pull images from cache
|
|
tileset.image = self.sti.cache[formatted_path]
|
|
|
|
gid = self:setTiles(i, tileset, gid)
|
|
end
|
|
|
|
-- Set layers
|
|
for i, layer in ipairs(self.layers) do
|
|
self:setLayer(layer, path)
|
|
end
|
|
end
|
|
|
|
--- Load plugins
|
|
-- @param plugins A list of plugins to load
|
|
-- @return nil
|
|
function Map:loadPlugins(plugins)
|
|
for _, plugin in ipairs(plugins) do
|
|
local p = pluginPath .. plugin .. ".lua"
|
|
if love.filesystem.isFile(p) then
|
|
local file = love.filesystem.load(p)()
|
|
for k, func in pairs(file) do
|
|
if not self[k] then
|
|
self[k] = func
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Create Tiles
|
|
-- @param index Index of the Tileset
|
|
-- @param tileset Tileset data
|
|
-- @param gid First Global ID in Tileset
|
|
-- @return number Next Tileset's first Global ID
|
|
function Map:setTiles(index, tileset, gid)
|
|
local function getTiles(i, t, m, s)
|
|
i = i - m
|
|
local n = 0
|
|
|
|
while i >= t do
|
|
i = i - t
|
|
if n ~= 0 then i = i - s end
|
|
if i >= 0 then n = n + 1 end
|
|
end
|
|
|
|
return n
|
|
end
|
|
|
|
local quad = love.graphics.newQuad
|
|
local mw = self.tilewidth
|
|
local iw = tileset.imagewidth
|
|
local ih = tileset.imageheight
|
|
local tw = tileset.tilewidth
|
|
local th = tileset.tileheight
|
|
local s = tileset.spacing
|
|
local m = tileset.margin
|
|
local w = getTiles(iw, tw, m, s)
|
|
local h = getTiles(ih, th, m, s)
|
|
|
|
for y = 1, h do
|
|
for x = 1, w do
|
|
local id = gid - tileset.firstgid
|
|
local qx = (x - 1) * tw + m + (x - 1) * s
|
|
local qy = (y - 1) * th + m + (y - 1) * s
|
|
local properties, terrain, animation, objectGroup
|
|
|
|
for _, tile in pairs(tileset.tiles) do
|
|
if tile.id == id then
|
|
properties = tile.properties
|
|
animation = tile.animation
|
|
objectGroup = tile.objectGroup
|
|
|
|
if tile.terrain then
|
|
terrain = {}
|
|
|
|
for i=1, #tile.terrain do
|
|
terrain[i] = tileset.terrains[tile.terrain[i] + 1]
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local tile = {
|
|
id = id,
|
|
gid = gid,
|
|
tileset = index,
|
|
quad = quad(qx, qy, tw, th, iw, ih),
|
|
properties = properties or {},
|
|
terrain = terrain,
|
|
animation = animation,
|
|
objectGroup = objectGroup,
|
|
frame = 1,
|
|
time = 0,
|
|
width = tw,
|
|
height = th,
|
|
sx = 1,
|
|
sy = 1,
|
|
r = 0,
|
|
offset = {
|
|
x = -mw + tileset.tileoffset.x,
|
|
y = -th + tileset.tileoffset.y,
|
|
},
|
|
}
|
|
|
|
if self.orientation == "isometric" then
|
|
tile.offset.x = -mw / 2
|
|
end
|
|
|
|
self.tiles[gid] = tile
|
|
gid = gid + 1
|
|
end
|
|
end
|
|
|
|
return gid
|
|
end
|
|
|
|
--- Create Layers
|
|
-- @param layer Layer data
|
|
-- @param path (Optional) Path to an Image Layer's image
|
|
-- @return nil
|
|
function Map:setLayer(layer, path)
|
|
if layer.encoding then
|
|
if layer.encoding == "base64" then
|
|
local ffi = assert(require "ffi", "Compressed maps require LuaJIT FFI.\nPlease Switch your interperator to LuaJIT or your Tile Layer Format to \"CSV\".")
|
|
local fd = love.filesystem.newFileData(layer.data, "data", "base64"):getString()
|
|
|
|
local function getDecompressedData(data)
|
|
local d = {}
|
|
local decoded = ffi.cast("uint32_t*", data)
|
|
|
|
for i=0, data:len() / ffi.sizeof("uint32_t") do
|
|
table.insert(d, tonumber(decoded[i]))
|
|
end
|
|
|
|
return d
|
|
end
|
|
|
|
if not layer.compression then
|
|
layer.data = getDecompressedData(fd)
|
|
else
|
|
assert(love.math.decompress, "zlib and gzip compression require LOVE 0.10.0+.\nPlease set your Tile Layer Format to \"Base64 (uncompressed)\" or \"CSV\".")
|
|
|
|
if layer.compression == "zlib" then
|
|
local data = love.math.decompress(fd, "zlib")
|
|
layer.data = getDecompressedData(data)
|
|
end
|
|
|
|
if layer.compression == "gzip" then
|
|
local data = love.math.decompress(fd, "gzip")
|
|
layer.data = getDecompressedData(data)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
layer.x = (layer.x or 0) + self.offsetx
|
|
layer.y = (layer.y or 0) + self.offsety
|
|
layer.update = function(dt) return end
|
|
|
|
if layer.type == "tilelayer" then
|
|
self:setTileData(layer)
|
|
self:setSpriteBatches(layer)
|
|
layer.draw = function() self:drawTileLayer(layer) end
|
|
elseif layer.type == "objectgroup" then
|
|
self:setObjectData(layer)
|
|
self:setObjectCoordinates(layer)
|
|
self:setObjectSpriteBatches(layer)
|
|
layer.draw = function() self:drawObjectLayer(layer) end
|
|
elseif layer.type == "imagelayer" then
|
|
layer.draw = function() self:drawImageLayer(layer) end
|
|
|
|
if layer.image ~= "" then
|
|
local formatted_path = formatPath(path .. layer.image)
|
|
if not self.sti.cache[formatted_path] then
|
|
cache_image(self.sti, formatted_path)
|
|
end
|
|
|
|
layer.image = self.sti.cache[formatted_path]
|
|
layer.width = layer.image:getWidth()
|
|
layer.height = layer.image:getHeight()
|
|
end
|
|
end
|
|
|
|
self.layers[layer.name] = layer
|
|
end
|
|
|
|
--- Add Tiles to Tile Layer
|
|
-- @param layer The Tile Layer
|
|
-- @return nil
|
|
function Map:setTileData(layer)
|
|
local i = 1
|
|
local map = {}
|
|
|
|
for y = 1, layer.height do
|
|
map[y] = {}
|
|
for x = 1, layer.width do
|
|
local gid = layer.data[i]
|
|
|
|
if gid > 0 then
|
|
map[y][x] = self.tiles[gid] or self:setFlippedGID(gid)
|
|
end
|
|
|
|
i = i + 1
|
|
end
|
|
end
|
|
|
|
layer.data = map
|
|
end
|
|
|
|
--- Add Objects to Layer
|
|
-- @param layer The Object Layer
|
|
-- @return nil
|
|
function Map:setObjectData(layer)
|
|
for _, object in ipairs(layer.objects) do
|
|
object.layer = layer
|
|
self.objects[object.id] = object
|
|
end
|
|
end
|
|
|
|
--- Correct position and orientation of Objects in an Object Layer
|
|
-- @param layer The Object Layer
|
|
-- @return nil
|
|
function Map:setObjectCoordinates(layer)
|
|
local function convertEllipseToPolygon(x, y, w, h, max_segments)
|
|
local function calc_segments(segments)
|
|
local function vdist(a, b)
|
|
local c = {
|
|
x = a.x - b.x,
|
|
y = a.y - b.y,
|
|
}
|
|
|
|
return c.x * c.x + c.y * c.y
|
|
end
|
|
|
|
segments = segments or 64
|
|
local vertices = {}
|
|
|
|
local v = { 1, 2, math.ceil(segments/4-1), math.ceil(segments/4) }
|
|
|
|
local m
|
|
if love.physics then
|
|
m = love.physics.getMeter()
|
|
else
|
|
m = 32
|
|
end
|
|
|
|
for _, i in ipairs(v) do
|
|
local angle = (i / segments) * math.pi * 2
|
|
local px = x + w / 2 + math.cos(angle) * w / 2
|
|
local py = y + h / 2 + math.sin(angle) * h / 2
|
|
|
|
table.insert(vertices, { x = px / m, y = py / m })
|
|
end
|
|
|
|
local dist1 = vdist(vertices[1], vertices[2])
|
|
local dist2 = vdist(vertices[3], vertices[4])
|
|
|
|
-- Box2D threshold
|
|
if dist1 < 0.0025 or dist2 < 0.0025 then
|
|
return calc_segments(segments-2)
|
|
end
|
|
|
|
return segments
|
|
end
|
|
|
|
local segments = calc_segments(max_segments)
|
|
local vertices = {}
|
|
|
|
table.insert(vertices, { x = x + w / 2, y = y + h / 2 })
|
|
|
|
for i=0, segments do
|
|
local angle = (i / segments) * math.pi * 2
|
|
local px = x + w / 2 + math.cos(angle) * w / 2
|
|
local py = y + h / 2 + math.sin(angle) * h / 2
|
|
|
|
table.insert(vertices, { x = px, y = py })
|
|
end
|
|
|
|
return vertices
|
|
end
|
|
|
|
local function rotateVertex(v, x, y, cos, sin)
|
|
local vertex = {
|
|
x = v.x,
|
|
y = v.y,
|
|
}
|
|
|
|
vertex.x = vertex.x - x
|
|
vertex.y = vertex.y - y
|
|
|
|
local vx = cos * vertex.x - sin * vertex.y
|
|
local vy = sin * vertex.x + cos * vertex.y
|
|
|
|
return vx + x, vy + y
|
|
end
|
|
|
|
local function updateVertex(vertex, x, y, cos, sin)
|
|
if self.orientation == "isometric" then
|
|
x, y = self:convertIsometricToScreen(x, y)
|
|
vertex.x, vertex.y = self:convertIsometricToScreen(vertex.x, vertex.y)
|
|
end
|
|
|
|
return rotateVertex(vertex, x, y, cos, sin)
|
|
end
|
|
|
|
for _, object in ipairs(layer.objects) do
|
|
local x = layer.x + object.x
|
|
local y = layer.y + object.y
|
|
local w = object.width
|
|
local h = object.height
|
|
local r = object.rotation
|
|
local cos = math.cos(math.rad(r))
|
|
local sin = math.sin(math.rad(r))
|
|
|
|
if object.shape == "rectangle" and not object.gid then
|
|
object.rectangle = {}
|
|
|
|
local vertices = {
|
|
{ x=x, y=y },
|
|
{ x=x + w, y=y },
|
|
{ x=x + w, y=y + h },
|
|
{ x=x, y=y + h },
|
|
}
|
|
|
|
for _, vertex in ipairs(vertices) do
|
|
vertex.x, vertex.y = updateVertex(vertex, x, y, cos, sin)
|
|
table.insert(object.rectangle, { x = vertex.x, y = vertex.y })
|
|
end
|
|
elseif object.shape == "ellipse" then
|
|
object.ellipse = {}
|
|
local vertices = convertEllipseToPolygon(x, y, w, h)
|
|
|
|
for _, vertex in ipairs(vertices) do
|
|
vertex.x, vertex.y = updateVertex(vertex, x, y, cos, sin)
|
|
table.insert(object.ellipse, { x = vertex.x, y = vertex.y })
|
|
end
|
|
elseif object.shape == "polygon" then
|
|
for _, vertex in ipairs(object.polygon) do
|
|
vertex.x = vertex.x + x
|
|
vertex.y = vertex.y + y
|
|
vertex.x, vertex.y = updateVertex(vertex, x, y, cos, sin)
|
|
end
|
|
elseif object.shape == "polyline" then
|
|
for _, vertex in ipairs(object.polyline) do
|
|
vertex.x = vertex.x + x
|
|
vertex.y = vertex.y + y
|
|
vertex.x, vertex.y = updateVertex(vertex, x, y, cos, sin)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Batch Tiles in Tile Layer for improved draw speed
|
|
-- @param layer The Tile Layer
|
|
-- @return nil
|
|
function Map:setSpriteBatches(layer)
|
|
local newBatch = love.graphics.newSpriteBatch
|
|
local w = love.graphics.getWidth()
|
|
local h = love.graphics.getHeight()
|
|
local tw = self.tilewidth
|
|
local th = self.tileheight
|
|
local bw = math.ceil(w / tw)
|
|
local bh = math.ceil(h / th)
|
|
local sx = 1
|
|
local sy = 1
|
|
local ex = layer.width
|
|
local ey = layer.height
|
|
local ix = 1
|
|
local iy = 1
|
|
|
|
-- Determine order to add tiles to sprite batch
|
|
-- Defaults to right-down
|
|
if self.renderorder == "right-up" then
|
|
sx, ex, ix = sx, ex, 1
|
|
sy, ey, iy = ey, sy, -1
|
|
elseif self.renderorder == "left-down" then
|
|
sx, ex, ix = ex, sx, -1
|
|
sy, ey, iy = sy, ey, 1
|
|
elseif self.renderorder == "left-up" then
|
|
sx, ex, ix = ex, sx, -1
|
|
sy, ey, iy = ey, sy, -1
|
|
end
|
|
|
|
-- Minimum of 400 tiles per batch
|
|
if bw < 20 then bw = 20 end
|
|
if bh < 20 then bh = 20 end
|
|
|
|
local size = bw * bh
|
|
local batches = {
|
|
width = bw,
|
|
height = bh,
|
|
data = {},
|
|
}
|
|
|
|
for y=sy, ey, iy do
|
|
local by = math.ceil(y / bh)
|
|
|
|
for x=sx, ex, ix do
|
|
local tile = layer.data[y][x]
|
|
local bx = math.ceil(x / bw)
|
|
local id
|
|
|
|
if tile then
|
|
local ts = tile.tileset
|
|
local image = self.tilesets[tile.tileset].image
|
|
|
|
batches.data[ts] = batches.data[ts] or {}
|
|
batches.data[ts][by] = batches.data[ts][by] or {}
|
|
batches.data[ts][by][bx] = batches.data[ts][by][bx] or newBatch(image, size)
|
|
|
|
local batch = batches.data[ts][by][bx]
|
|
local tx, ty
|
|
|
|
if self.orientation == "orthogonal" then
|
|
tx, ty = compensate(tile, x*tw, y*th, tw, th)
|
|
elseif self.orientation == "isometric" then
|
|
tx = (x - y) * (tw / 2) + tile.offset.x + layer.width * tw / 2
|
|
ty = (x + y) * (th / 2) + tile.offset.y
|
|
elseif self.orientation == "staggered" or self.orientation == "hexagonal" then
|
|
if self.staggeraxis == "y" then
|
|
if self.staggerindex == "odd" then
|
|
if y % 2 == 0 then
|
|
tx = x * tw + tw / 2 + (self.hexsidelength or 0) + tile.offset.x
|
|
else
|
|
tx = x * tw + (self.hexsidelength or 0) + tile.offset.x
|
|
end
|
|
else
|
|
if y % 2 == 0 then
|
|
tx = x * tw + (self.hexsidelength or 0) + tile.offset.x
|
|
else
|
|
tx = x * tw + tw / 2 + (self.hexsidelength or 0) + tile.offset.x
|
|
end
|
|
end
|
|
|
|
if self.orientation == "hexagonal" then
|
|
ty = y * (th - (th - self.hexsidelength) / 2) + tile.offset.y + (th - (th - self.hexsidelength) / 2)
|
|
else
|
|
ty = y * th / 2 + tile.offset.y + th / 2
|
|
end
|
|
else
|
|
if self.staggerindex == "odd" then
|
|
if x % 2 == 0 then
|
|
ty = y * th + th / 2 + (self.hexsidelength or 0) + tile.offset.y
|
|
else
|
|
ty = y * th + (self.hexsidelength or 0) + tile.offset.y
|
|
end
|
|
else
|
|
if x % 2 == 0 then
|
|
ty = y * th + (self.hexsidelength or 0) + tile.offset.y
|
|
else
|
|
ty = y * th + th / 2 + (self.hexsidelength or 0) + tile.offset.y
|
|
end
|
|
end
|
|
|
|
if self.orientation == "hexagonal" then
|
|
tx = x * (tw - (tw - self.hexsidelength) / 2) + tile.offset.x + (tw - (tw - self.hexsidelength) / 2)
|
|
else
|
|
tx = x * tw / 2 + tile.offset.x + tw / 2
|
|
end
|
|
end
|
|
end
|
|
|
|
id = batch:add(tile.quad, tx, ty, tile.r, tile.sx, tile.sy)
|
|
self.tileInstances[tile.gid] = self.tileInstances[tile.gid] or {}
|
|
table.insert(self.tileInstances[tile.gid], {
|
|
layer = layer,
|
|
batch = batch,
|
|
id = id,
|
|
gid = tile.gid,
|
|
x = tx,
|
|
y = ty,
|
|
r = tile.r,
|
|
oy = 0
|
|
})
|
|
end
|
|
end
|
|
end
|
|
|
|
layer.batches = batches
|
|
end
|
|
|
|
--- Batch Tiles in Object Layer for improved draw speed
|
|
-- @param layer The Object Layer
|
|
-- @return nil
|
|
function Map:setObjectSpriteBatches(layer)
|
|
local newBatch = love.graphics.newSpriteBatch
|
|
local tw = self.tilewidth
|
|
local th = self.tileheight
|
|
local batches = {}
|
|
|
|
for _, object in ipairs(layer.objects) do
|
|
if object.gid then
|
|
local tile = self.tiles[object.gid] or self:setFlippedGID(object.gid)
|
|
local ts = tile.tileset
|
|
local image = self.tilesets[tile.tileset].image
|
|
|
|
batches[ts] = batches[ts] or newBatch(image, 100)
|
|
|
|
local batch = batches[ts]
|
|
local tx = object.x + tw + tile.offset.x
|
|
local ty = object.y + tile.offset.y
|
|
local tr = math.rad(object.rotation)
|
|
local oy = 0
|
|
|
|
-- Compensation for scale/rotation shift
|
|
if tile.sx == 1 and tile.sy == 1 then
|
|
if tr ~= 0 then
|
|
ty = ty + th
|
|
oy = th
|
|
end
|
|
else
|
|
if tile.sx < 0 then tx = tx + tw end
|
|
if tile.sy < 0 then ty = ty + th end
|
|
if tr > 0 then tx = tx + tw end
|
|
if tr < 0 then ty = ty + th end
|
|
end
|
|
|
|
id = batch:add(tile.quad, tx, ty, tr, tile.sx, tile.sy, 0, oy)
|
|
self.tileInstances[tile.gid] = self.tileInstances[tile.gid] or {}
|
|
table.insert(self.tileInstances[tile.gid], {
|
|
layer = layer,
|
|
batch = batch,
|
|
id = id,
|
|
gid = tile.gid,
|
|
x = tx,
|
|
y = ty,
|
|
r = tr,
|
|
oy = oy
|
|
})
|
|
end
|
|
end
|
|
|
|
layer.batches = batches
|
|
end
|
|
|
|
--- Only draw what is visible on screen for improved draw speed
|
|
-- @param tx Translate X axis (in pixels)
|
|
-- @param ty Translate Y axis (in pixels)
|
|
-- @param w Width of screen (in pixels)
|
|
-- @param h Height of screen (in pixels)
|
|
-- @return nil
|
|
function Map:setDrawRange(tx, ty, w, h)
|
|
local tw, th = self.tilewidth, self.tileheight
|
|
local sx, sy, ex, ey
|
|
|
|
if self.orientation == "orthogonal" then
|
|
sx = math.ceil(tx / tw)
|
|
sy = math.ceil(ty / th)
|
|
ex = math.ceil(sx + w / tw)
|
|
ey = math.ceil(sy + h / th)
|
|
elseif self.orientation == "isometric" then
|
|
sx = math.ceil(((ty / (th / 2)) + (tx / (tw / 2))) / 2)
|
|
sy = math.ceil(((ty / (th / 2)) - (tx / (tw / 2))) / 2 - h / th)
|
|
ex = math.ceil(sx + (h / th) + (w / tw))
|
|
ey = math.ceil(sy + (h / th) * 2 + (w / tw))
|
|
elseif self.orientation == "staggered" or self.orientation == "hexagonal" then
|
|
sx = math.ceil(tx / tw - 1)
|
|
sy = math.ceil(ty / th)
|
|
ex = math.ceil(sx + w / tw + 1)
|
|
ey = math.ceil(sy + h / th * 2)
|
|
end
|
|
|
|
self.drawRange.sx = sx
|
|
self.drawRange.sy = sy
|
|
self.drawRange.ex = ex
|
|
self.drawRange.ey = ey
|
|
end
|
|
|
|
--- Create a Custom Layer to place userdata in (such as player sprites)
|
|
-- @param name Name of Custom Layer
|
|
-- @param index Draw order within Layer stack
|
|
-- @return table Custom Layer
|
|
function Map:addCustomLayer(name, index)
|
|
local index = index or #self.layers + 1
|
|
local layer = {
|
|
type = "customlayer",
|
|
name = name,
|
|
visible = true,
|
|
opacity = 1,
|
|
properties = {},
|
|
}
|
|
|
|
function layer:draw() return end
|
|
function layer:update(dt) return end
|
|
|
|
table.insert(self.layers, index, layer)
|
|
self.layers[name] = self.layers[index]
|
|
|
|
return layer
|
|
end
|
|
|
|
--- Convert another Layer into a Custom Layer
|
|
-- @param index Index or name of Layer to convert
|
|
-- @return table Custom Layer
|
|
function Map:convertToCustomLayer(index)
|
|
local layer = assert(self.layers[index], "Layer not found: " .. index)
|
|
|
|
layer.type = "customlayer"
|
|
layer.x = nil
|
|
layer.y = nil
|
|
layer.width = nil
|
|
layer.height = nil
|
|
layer.encoding = nil
|
|
layer.data = nil
|
|
layer.objects = nil
|
|
layer.image = nil
|
|
|
|
function layer:draw() return end
|
|
function layer:update(dt) return end
|
|
|
|
return layer
|
|
end
|
|
|
|
--- Remove a Layer from the Layer stack
|
|
-- @param index Index or name of Layer to convert
|
|
-- @return nil
|
|
function Map:removeLayer(index)
|
|
local layer = assert(self.layers[index], "Layer not found: " .. index)
|
|
|
|
if type(index) == "string" then
|
|
for i, layer in ipairs(self.layers) do
|
|
if layer.name == index then
|
|
table.remove(self.layers, i)
|
|
self.layers[index] = nil
|
|
break
|
|
end
|
|
end
|
|
else
|
|
local name = self.layers[index].name
|
|
table.remove(self.layers, index)
|
|
self.layers[name] = nil
|
|
end
|
|
|
|
-- Remove tile instances
|
|
if layer.batches then
|
|
for gid, tiles in pairs(self.tileInstances) do
|
|
for i=#tiles, 1, -1 do
|
|
local tile = tiles[i]
|
|
if tile.layer == layer then
|
|
table.remove(tiles, i)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Remove objects
|
|
if layer.objects then
|
|
for i, object in pairs(self.objects) do
|
|
if object.layer == layer then
|
|
self.objects[i] = nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Animate Tiles and update every Layer
|
|
-- @param dt Delta Time
|
|
-- @return nil
|
|
function Map:update(dt)
|
|
for gid, tile in pairs(self.tiles) do
|
|
local update = false
|
|
|
|
if tile.animation then
|
|
tile.time = tile.time + dt * 1000
|
|
|
|
while tile.time > tonumber(tile.animation[tile.frame].duration) do
|
|
update = true
|
|
tile.time = tile.time - tonumber(tile.animation[tile.frame].duration)
|
|
tile.frame = tile.frame + 1
|
|
|
|
if tile.frame > #tile.animation then tile.frame = 1 end
|
|
end
|
|
|
|
if update and self.tileInstances[tile.gid] then
|
|
for _, j in pairs(self.tileInstances[tile.gid]) do
|
|
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)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
for _, layer in ipairs(self.layers) do
|
|
layer:update(dt)
|
|
end
|
|
end
|
|
|
|
--- Draw every Layer
|
|
-- @return nil
|
|
function Map:draw()
|
|
local current_canvas = love.graphics.getCanvas()
|
|
love.graphics.setCanvas(self.canvas)
|
|
if self.canvas.clear then
|
|
self.canvas:clear()
|
|
else
|
|
local r,g,b,a = love.graphics.getBackgroundColor()
|
|
love.graphics.clear(r,g,b,a,self.canvas)
|
|
end
|
|
|
|
for _, layer in ipairs(self.layers) do
|
|
if layer.visible and layer.opacity > 0 then
|
|
self:drawLayer(layer)
|
|
end
|
|
end
|
|
|
|
love.graphics.setCanvas(current_canvas)
|
|
love.graphics.push()
|
|
love.graphics.origin()
|
|
love.graphics.draw(self.canvas)
|
|
love.graphics.pop()
|
|
end
|
|
|
|
--- Draw an individual Layer
|
|
-- @param layer The Layer to draw
|
|
-- @return nil
|
|
function Map:drawLayer(layer)
|
|
love.graphics.setColor(255, 255, 255, 255 * layer.opacity)
|
|
layer:draw()
|
|
love.graphics.setColor(255, 255, 255, 255)
|
|
end
|
|
|
|
--- Default draw function for Tile Layers
|
|
-- @param layer The Tile Layer to draw
|
|
-- @return nil
|
|
function Map:drawTileLayer(layer)
|
|
if type(layer) == "string" or type(layer) == "number" then
|
|
layer = self.layers[layer]
|
|
end
|
|
|
|
assert(layer.type == "tilelayer", "Invalid layer type: " .. layer.type .. ". Layer must be of type: tilelayer")
|
|
|
|
local bw = layer.batches.width
|
|
local bh = layer.batches.height
|
|
local sx = math.ceil((self.drawRange.sx - layer.x / self.tilewidth - 1) / bw)
|
|
local sy = math.ceil((self.drawRange.sy - layer.y / self.tileheight - 1) / bh)
|
|
local ex = math.ceil((self.drawRange.ex - layer.x / self.tilewidth + 1) / bw)
|
|
local ey = math.ceil((self.drawRange.ey - layer.y / self.tileheight + 1) / bh)
|
|
local ix = 1
|
|
local iy = 1
|
|
local mx = math.ceil(self.width / bw)
|
|
local my = math.ceil(self.height / bh)
|
|
|
|
-- Determine order to draw batches
|
|
-- Defaults to right-down
|
|
if self.renderorder == "right-up" then
|
|
sx, ex, ix = sx, ex, 1
|
|
sy, ey, iy = ey, sy, -1
|
|
elseif self.renderorder == "left-down" then
|
|
sx, ex, ix = ex, sx, -1
|
|
sy, ey, iy = sy, ey, 1
|
|
elseif self.renderorder == "left-up" then
|
|
sx, ex, ix = ex, sx, -1
|
|
sy, ey, iy = ey, sy, -1
|
|
end
|
|
|
|
for by=sy, ey, iy do
|
|
for bx=sx, ex, ix do
|
|
if bx >= 1 and bx <= mx and by >= 1 and by <= my then
|
|
for _, batches in pairs(layer.batches.data) do
|
|
local batch = batches[by] and batches[by][bx]
|
|
|
|
if batch then
|
|
love.graphics.draw(batch, math.floor(layer.x), math.floor(layer.y))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Default draw function for Object Layers
|
|
-- @param layer The Object Layer to draw
|
|
-- @return nil
|
|
function Map:drawObjectLayer(layer)
|
|
if type(layer) == "string" or type(layer) == "number" then
|
|
layer = self.layers[layer]
|
|
end
|
|
|
|
assert(layer.type == "objectgroup", "Invalid layer type: " .. layer.type .. ". Layer must be of type: objectgroup")
|
|
|
|
local line = { 160, 160, 160, 255 * layer.opacity }
|
|
local fill = { 160, 160, 160, 255 * layer.opacity * 0.2 }
|
|
local shadow = { 0, 0, 0, 255 * layer.opacity }
|
|
local reset = { 255, 255, 255, 255 * layer.opacity }
|
|
|
|
local function sortVertices(obj)
|
|
local vertices = {{},{}}
|
|
|
|
for _, vertex in ipairs(obj) do
|
|
table.insert(vertices[1], vertex.x)
|
|
table.insert(vertices[1], vertex.y)
|
|
table.insert(vertices[2], vertex.x+1)
|
|
table.insert(vertices[2], vertex.y+1)
|
|
end
|
|
|
|
return vertices
|
|
end
|
|
|
|
local function drawShape(obj, shape)
|
|
local vertices = sortVertices(obj)
|
|
|
|
if shape == "polyline" then
|
|
love.graphics.setColor(shadow)
|
|
love.graphics.line(vertices[2])
|
|
love.graphics.setColor(line)
|
|
love.graphics.line(vertices[1])
|
|
|
|
return
|
|
elseif shape == "polygon" then
|
|
love.graphics.setColor(fill)
|
|
if not love.math.isConvex(vertices[1]) then
|
|
local triangles = love.math.triangulate(vertices[1])
|
|
for _, triangle in ipairs(triangles) do
|
|
love.graphics.polygon("fill", triangle)
|
|
end
|
|
else
|
|
love.graphics.polygon("fill", vertices[1])
|
|
end
|
|
else
|
|
love.graphics.setColor(fill)
|
|
love.graphics.polygon("fill", vertices[1])
|
|
end
|
|
|
|
love.graphics.setColor(shadow)
|
|
love.graphics.polygon("line", vertices[2])
|
|
love.graphics.setColor(line)
|
|
love.graphics.polygon("line", vertices[1])
|
|
end
|
|
|
|
for _, object in ipairs(layer.objects) do
|
|
if object.shape == "rectangle" and not object.gid then
|
|
drawShape(object.rectangle, "rectangle")
|
|
elseif object.shape == "ellipse" then
|
|
drawShape(object.ellipse, "ellipse")
|
|
elseif object.shape == "polygon" then
|
|
drawShape(object.polygon, "polygon")
|
|
elseif object.shape == "polyline" then
|
|
drawShape(object.polyline, "polyline")
|
|
end
|
|
end
|
|
|
|
love.graphics.setColor(reset)
|
|
for _, batch in pairs(layer.batches) do
|
|
love.graphics.draw(batch, 0, 0)
|
|
end
|
|
end
|
|
|
|
--- Default draw function for Image Layers
|
|
-- @param layer The Image Layer to draw
|
|
-- @return nil
|
|
function Map:drawImageLayer(layer)
|
|
if type(layer) == "string" or type(layer) == "number" then
|
|
layer = self.layers[layer]
|
|
end
|
|
|
|
assert(layer.type == "imagelayer", "Invalid layer type: " .. layer.type .. ". Layer must be of type: imagelayer")
|
|
|
|
if layer.image ~= "" then
|
|
love.graphics.draw(layer.image, layer.x, layer.y)
|
|
end
|
|
end
|
|
|
|
--- Resize the drawable area of the Map
|
|
-- @param w The new width of the drawable area (in pixels)
|
|
-- @param h The new Height of the drawable area (in pixels)
|
|
-- @return nil
|
|
function Map:resize(w, h)
|
|
w = w or love.graphics.getWidth()
|
|
h = h or love.graphics.getHeight()
|
|
|
|
self.canvas = love.graphics.newCanvas(w, h)
|
|
self.canvas:setFilter("nearest", "nearest")
|
|
end
|
|
|
|
--- Create flipped or rotated Tiles based on bitop flags
|
|
-- @param gid The flagged Global ID
|
|
-- @return table Flipped Tile
|
|
function Map:setFlippedGID(gid)
|
|
local bit31 = 2147483648
|
|
local bit30 = 1073741824
|
|
local bit29 = 536870912
|
|
local flipX = false
|
|
local flipY = false
|
|
local flipD = false
|
|
local realgid = gid
|
|
|
|
if realgid >= bit31 then
|
|
realgid = realgid - bit31
|
|
flipX = not flipX
|
|
end
|
|
|
|
if realgid >= bit30 then
|
|
realgid = realgid - bit30
|
|
flipY = not flipY
|
|
end
|
|
|
|
if realgid >= bit29 then
|
|
realgid = realgid - bit29
|
|
flipD = not flipD
|
|
end
|
|
|
|
local tile = self.tiles[realgid]
|
|
local data = {
|
|
id = tile.id,
|
|
gid = gid,
|
|
tileset = tile.tileset,
|
|
frame = tile.frame,
|
|
time = tile.time,
|
|
width = tile.width,
|
|
height = tile.height,
|
|
offset = tile.offset,
|
|
quad = tile.quad,
|
|
properties = tile.properties,
|
|
terrain = tile.terrain,
|
|
animation = tile.animation,
|
|
sx = tile.sx,
|
|
sy = tile.sy,
|
|
r = tile.r,
|
|
}
|
|
|
|
if flipX then
|
|
if flipY and flipD then
|
|
data.r = math.rad(-90)
|
|
data.sy = -1
|
|
elseif flipY then
|
|
data.sx = -1
|
|
data.sy = -1
|
|
elseif flipD then
|
|
data.r = math.rad(90)
|
|
else
|
|
data.sx = -1
|
|
end
|
|
elseif flipY then
|
|
if flipD then
|
|
data.r = math.rad(-90)
|
|
else
|
|
data.sy = -1
|
|
end
|
|
elseif flipD then
|
|
data.r = math.rad(90)
|
|
data.sy = -1
|
|
end
|
|
|
|
self.tiles[gid] = data
|
|
|
|
return self.tiles[gid]
|
|
end
|
|
|
|
--- Get custom properties from Layer
|
|
-- @param layer The Layer
|
|
-- @return table List of properties
|
|
function Map:getLayerProperties(layer)
|
|
local l = self.layers[layer]
|
|
|
|
if not l then return {} end
|
|
|
|
return l.properties
|
|
end
|
|
|
|
--- Get custom properties from Tile
|
|
-- @param layer The Layer that the Tile belongs to
|
|
-- @param x The X axis location of the Tile (in tiles)
|
|
-- @param y The Y axis location of the Tile (in tiles)
|
|
-- @return table List of properties
|
|
function Map:getTileProperties(layer, x, y)
|
|
local tile = self.layers[layer].data[y][x]
|
|
|
|
if not tile then return {} end
|
|
|
|
return tile.properties
|
|
end
|
|
|
|
--- Get custom properties from Object
|
|
-- @param layer The Layer that the Object belongs to
|
|
-- @param object The index or name of the Object
|
|
-- @return table List of properties
|
|
function Map:getObjectProperties(layer, object)
|
|
local o = self.layers[layer].objects
|
|
|
|
if type(object) == "number" then
|
|
o = o[object]
|
|
else
|
|
for _, v in ipairs(o) do
|
|
if v.name == object then
|
|
o = v
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
if not o then return {} end
|
|
|
|
return o.properties
|
|
end
|
|
|
|
--- Project isometric position to orthoganal position
|
|
-- @param x The X axis location of the point (in pixels)
|
|
-- @param y The Y 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)
|
|
function Map:convertIsometricToScreen(x, y)
|
|
local mw = self.width
|
|
local tw = self.tilewidth
|
|
local th = self.tileheight
|
|
local ox = mw * tw / 2
|
|
local sx = (x - y) + ox
|
|
local sy = (x + y) / 2
|
|
|
|
return sx, sy
|
|
end
|
|
|
|
--- Project orthoganal position to isometric position
|
|
-- @param x The X axis location of the point (in pixels)
|
|
-- @param y The Y 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)
|
|
function Map:convertScreenToIsometric(x, y)
|
|
local mw = self.width
|
|
local mh = self.height
|
|
local tw = self.tilewidth
|
|
local th = self.tileheight
|
|
local ox = mw * tw / 2
|
|
local oy = mh * th / 2
|
|
local tx = (x / 2 + y) - ox / 2
|
|
local ty = (-x / 2 + y) + oy
|
|
|
|
return tx, ty
|
|
end
|
|
|
|
--- Convert tile space to screen space
|
|
-- @param x The X 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 Y axis location of the point (in pixels)
|
|
function Map:convertWorldToScreen(x,y)
|
|
if self.orientation == "orthogonal" then
|
|
local tw = self.tilewidth
|
|
local th = self.tileheight
|
|
local sx = x * tw
|
|
local sy = y * th
|
|
|
|
return sx, sy
|
|
elseif self.orientation == "isometric" then
|
|
local mw = self.width
|
|
local tw = self.tilewidth
|
|
local th = self.tileheight
|
|
local ox = mw * tw / 2
|
|
local sx = (x - y) * tw / 2 + ox
|
|
local sy = (x + y) * th / 2
|
|
|
|
return sx, sy
|
|
elseif self.orientation == "staggered" then
|
|
local tw = self.tilewidth
|
|
local th = self.tileheight
|
|
local sx = x * tw + math.abs(math.ceil(y) % 2) * (tw / 2) - (math.ceil(y) % 2 * tw/2)
|
|
local sy = y * (th / 2) + th/2
|
|
|
|
return sx, sy
|
|
end
|
|
end
|
|
|
|
--- Convert screen space to tile space
|
|
-- @param x The X axis location of the point (in pixels)
|
|
-- @param y The Y axis location of the point (in pixels)
|
|
-- @return number The X axis location of the point (in tiles)
|
|
-- @return number The Y axis location of the point (in tiles)
|
|
function Map:convertScreenToWorld(x,y)
|
|
if self.orientation == "orthogonal" then
|
|
local tw = self.tilewidth
|
|
local th = self.tileheight
|
|
local tx = x / tw
|
|
local ty = y / th
|
|
|
|
return tx, ty
|
|
elseif self.orientation == "isometric" then
|
|
local mw = self.width
|
|
local tw = self.tilewidth
|
|
local th = self.tileheight
|
|
local ox = mw * tw / 2
|
|
local tx = y / th + (x - ox) / tw
|
|
local ty = y / th - (x - ox) / tw
|
|
|
|
return tx, ty
|
|
elseif self.orientation == "staggered" then
|
|
local function topLeft(x, y)
|
|
if (math.ceil(y) % 2) then
|
|
return x, y - 1
|
|
else
|
|
return x - 1, y - 1
|
|
end
|
|
end
|
|
|
|
local function topRight(x, y)
|
|
if (math.ceil(y) % 2) then
|
|
return x + 1, y - 1
|
|
else
|
|
return x, y - 1
|
|
end
|
|
end
|
|
|
|
local function bottomLeft(x, y)
|
|
if (math.ceil(y) % 2) then
|
|
return x, y + 1
|
|
else
|
|
return x - 1, y + 1
|
|
end
|
|
end
|
|
|
|
local function bottomRight(x, y)
|
|
if (math.ceil(y) % 2) then
|
|
return x + 1, y + 1
|
|
else
|
|
return x, y + 1
|
|
end
|
|
end
|
|
|
|
local tw = self.tilewidth
|
|
local th = self.tileheight
|
|
local hh = th / 2
|
|
local ratio = th / tw
|
|
local tx = x / tw
|
|
local ty = y / th * 2
|
|
local ctx = math.ceil(x / tw)
|
|
local cty = math.ceil(y / th) * 2
|
|
local rx = x - ctx * tw
|
|
local ry = y - (cty / 2) * th
|
|
|
|
if (hh - rx * ratio > ry) then
|
|
return topLeft(tx, ty)
|
|
elseif (-hh + rx * ratio > ry) then
|
|
return topRight(tx, ty)
|
|
elseif (hh + rx * ratio < ry) then
|
|
return bottomLeft(tx, ty)
|
|
elseif (hh * 3 - rx * ratio < ry) then
|
|
return bottomRight(tx, ty)
|
|
end
|
|
|
|
return tx, ty
|
|
end
|
|
end
|
|
|
|
return Map
|
|
|
|
--- A list of individual layers indexed both by draw order and name
|
|
-- @table Map.layers
|
|
-- @see TileLayer
|
|
-- @see ObjectLayer
|
|
-- @see ImageLayer
|
|
-- @see CustomLayer
|
|
|
|
--- A list of individual tiles indexed by Global ID
|
|
-- @table Map.tiles
|
|
-- @see Tile
|
|
-- @see Map.tileInstances
|
|
|
|
--- A list of tile instances indexed by Global ID
|
|
-- @table Map.tileInstances
|
|
-- @see TileInstance
|
|
-- @see Tile
|
|
-- @see Map.tiles
|
|
|
|
--- A list of individual objects indexed by Global ID
|
|
-- @table Map.objects
|
|
-- @see Object
|
|
|
|
--- @table TileLayer
|
|
-- @field name The name of the layer
|
|
-- @field x Position on the X axis (in pixels)
|
|
-- @field y Position on the Y axis (in pixels)
|
|
-- @field width Width of layer (in tiles)
|
|
-- @field height Height of layer (in tiles)
|
|
-- @field visible Toggle if layer is visible or hidden
|
|
-- @field opacity Opacity of layer
|
|
-- @field properties Custom properties
|
|
-- @field data A two dimensional table filled with individual tiles indexed by [y][x] (in tiles)
|
|
-- @field update Update function
|
|
-- @field draw Draw function
|
|
-- @see Map.layers
|
|
-- @see Tile
|
|
|
|
--- @table ObjectLayer
|
|
-- @field name The name of the layer
|
|
-- @field x Position on the X axis (in pixels)
|
|
-- @field y Position on the Y axis (in pixels)
|
|
-- @field visible Toggle if layer is visible or hidden
|
|
-- @field opacity Opacity of layer
|
|
-- @field properties Custom properties
|
|
-- @field objects List of objects indexed by draw order
|
|
-- @field update Update function
|
|
-- @field draw Draw function
|
|
-- @see Map.layers
|
|
-- @see Object
|
|
|
|
--- @table ImageLayer
|
|
-- @field name The name of the layer
|
|
-- @field x Position on the X axis (in pixels)
|
|
-- @field y Position on the Y axis (in pixels)
|
|
-- @field visible Toggle if layer is visible or hidden
|
|
-- @field opacity Opacity of layer
|
|
-- @field properties Custom properties
|
|
-- @field image Image to be drawn
|
|
-- @field update Update function
|
|
-- @field draw Draw function
|
|
-- @see Map.layers
|
|
|
|
--- Custom Layers are used to place userdata such as sprites within the draw order of the map.
|
|
-- @table CustomLayer
|
|
-- @field name The name of the layer
|
|
-- @field x Position on the X axis (in pixels)
|
|
-- @field y Position on the Y axis (in pixels)
|
|
-- @field visible Toggle if layer is visible or hidden
|
|
-- @field opacity Opacity of layer
|
|
-- @field properties Custom properties
|
|
-- @field update Update function
|
|
-- @field draw Draw function
|
|
-- @see Map.layers
|
|
-- @usage
|
|
-- -- Create a Custom Layer
|
|
-- local spriteLayer = map:addCustomLayer("Sprite Layer", 3)
|
|
--
|
|
-- -- Add data to Custom Layer
|
|
-- spriteLayer.sprites = {
|
|
-- player = {
|
|
-- image = love.graphics.newImage("assets/sprites/player.png"),
|
|
-- x = 64,
|
|
-- y = 64,
|
|
-- r = 0,
|
|
-- }
|
|
-- }
|
|
--
|
|
-- -- Update callback for Custom Layer
|
|
-- function spriteLayer:update(dt)
|
|
-- for _, sprite in pairs(self.sprites) do
|
|
-- sprite.r = sprite.r + math.rad(90 * dt)
|
|
-- end
|
|
-- end
|
|
--
|
|
-- -- Draw callback for Custom Layer
|
|
-- function spriteLayer:draw()
|
|
-- for _, sprite in pairs(self.sprites) do
|
|
-- local x = math.floor(sprite.x)
|
|
-- local y = math.floor(sprite.y)
|
|
-- local r = sprite.r
|
|
-- love.graphics.draw(sprite.image, x, y, r)
|
|
-- end
|
|
-- end
|
|
|
|
--- @table Tile
|
|
-- @field id Local ID within Tileset
|
|
-- @field gid Global ID
|
|
-- @field tileset Tileset ID
|
|
-- @field quad Quad object
|
|
-- @field properties Custom properties
|
|
-- @field terrain Terrain data
|
|
-- @field animation Animation data
|
|
-- @field frame Current animation frame
|
|
-- @field time Time spent on current animation frame
|
|
-- @field width Width of tile
|
|
-- @field height Height of tile
|
|
-- @field sx Scale value on the X axis
|
|
-- @field sy Scale value on the Y axis
|
|
-- @field r Rotation of tile (in radians)
|
|
-- @field offset Offset drawing position
|
|
-- @field offset.x Offset value on the X axis
|
|
-- @field offset.y Offset value on the Y axis
|
|
-- @see Map.tiles
|
|
|
|
--- @table TileInstance
|
|
-- @field batch Spritebatch the Tile Instance belongs to
|
|
-- @field id ID within the spritebatch
|
|
-- @field gid Global ID
|
|
-- @field x Position on the X axis (in pixels)
|
|
-- @field y Position on the Y axis (in pixels)
|
|
-- @see Map.tileInstances
|
|
-- @see Tile
|
|
|
|
--- @table Object
|
|
-- @field id Global ID
|
|
-- @field name Name of object (non-unique)
|
|
-- @field shape Shape of object
|
|
-- @field x Position of object on X axis (in pixels)
|
|
-- @field y Position of object on Y axis (in pixels)
|
|
-- @field width Width of object (in pixels)
|
|
-- @field height Heigh tof object (in pixels)
|
|
-- @field rotation Rotation of object (in radians)
|
|
-- @field visible Toggle if object is visible or hidden
|
|
-- @field properties Custom properties
|
|
-- @field ellipse List of verticies of specific shape
|
|
-- @field rectangle List of verticies of specific shape
|
|
-- @field polygon List of verticies of specific shape
|
|
-- @field polyline List of verticies of specific shape
|
|
-- @see Map.objects
|