summaryrefslogtreecommitdiff
path: root/tiled/tiled.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tiled/tiled.lua')
-rw-r--r--tiled/tiled.lua244
1 files changed, 244 insertions, 0 deletions
diff --git a/tiled/tiled.lua b/tiled/tiled.lua
new file mode 100644
index 0000000..0e3b590
--- /dev/null
+++ b/tiled/tiled.lua
@@ -0,0 +1,244 @@
+-- see https://love2d.org/wiki/TiledMapLoader for latest version
+-- loader for "tiled" map editor maps (.tmx,xml-based) http://www.mapeditor.org/
+-- supports multiple layers
+-- NOTE : function ReplaceMapTileClass (tx,ty,oldTileType,newTileType,fun_callback) end
+-- NOTE : function TransmuteMap (from_to_table) end -- from_to_table[old]=new
+-- NOTE : function GetMousePosOnMap () return gMouseX+gCamX-gScreenW/2,gMouseY+gCamY-gScreenH/2 end
+
+kTileSize = 32
+kMapTileTypeEmpty = 0
+local floor = math.floor
+local ceil = math.ceil
+local max = math.max
+local min = math.min
+local abs = math.abs
+gTileMap_LayerInvisByName = {}
+
+function TiledMap_Load (filepath,tilesize,spritepath_removeold,spritepath_prefix)
+ spritepath_removeold = spritepath_removeold or "../"
+ spritepath_prefix = spritepath_prefix or ""
+ kTileSize = tilesize or kTileSize or 32
+ gTileGfx = {}
+
+ local tiletype,layers = TiledMap_Parse(filepath)
+ gMapLayers = layers
+ for first_gid,path in pairs(tiletype) do
+ path = spritepath_prefix .. string.gsub(path,"^"..string.gsub(spritepath_removeold,"%.","%%."),"")
+ local raw = love.image.newImageData(path)
+ local w,h = raw:getWidth(),raw:getHeight()
+ local gid = first_gid
+ local e = kTileSize
+ for y=0,floor(h/kTileSize)-1 do
+ for x=0,floor(w/kTileSize)-1 do
+ local sprite = love.image.newImageData(kTileSize,kTileSize)
+ sprite:paste(raw,0,0,x*e,y*e,e,e)
+ gTileGfx[gid] = love.graphics.newImage(sprite)
+ gid = gid + 1
+ end
+ end
+ end
+end
+
+function TiledMap_GetMapW () return gMapLayers.width end
+function TiledMap_GetMapH () return gMapLayers.height end
+
+-- returns the mapwidth actually used by tiles
+function TiledMap_GetMapWUsed ()
+ local maxx = 0
+ local miny = 0
+ local maxy = 0
+ for layerid,layer in pairs(gMapLayers) do
+ if (type(layer) == "table") then for ty,row in pairs(layer) do
+ if (type(row) == "table") then for tx,t in pairs(row) do
+ if (t and t ~= kMapTileTypeEmpty) then
+ miny = min(miny,ty)
+ maxy = max(maxy,ty)
+ maxx = max(maxx,tx)
+ end
+ end end
+ end end
+ end
+ return maxx + 1,miny,maxy+1
+end
+
+-- x,y= position for nearest-distance(square,not round), z= layer, maxrad= optional limit for searching
+-- returns x,y
+-- if x,y can be far outside map, set a sensible maxrad, otherwise it'll get very slow since searching outside map isn't optimized
+function TiledMap_GetNearestTileByTypeOnLayer (x,y,z,iTileType,maxrad)
+ local w = TiledMap_GetMapW()
+ local h = TiledMap_GetMapW()
+ local maxrad2 = max(x,w-x,y,h-y) if (maxrad) then maxrad2 = min(maxrad2,maxrad) end
+ if (TiledMap_GetMapTile(x,y,z) == iTileType) then return x,y end
+ for r = 1,maxrad2 do
+ for i=-r,r do
+ local xa,ya = x+i,y-r if (TiledMap_GetMapTile(xa,ya,z) == iTileType) then return xa,ya end -- top
+ local xa,ya = x+i,y+r if (TiledMap_GetMapTile(xa,ya,z) == iTileType) then return xa,ya end -- bot
+ local xa,ya = x-r,y+i if (TiledMap_GetMapTile(xa,ya,z) == iTileType) then return xa,ya end -- left
+ local xa,ya = x+r,y+i if (TiledMap_GetMapTile(xa,ya,z) == iTileType) then return xa,ya end -- right
+ end
+ end
+end
+
+function TiledMap_GetMapTile (tx,ty,layerid) -- coords in tiles
+ local row = gMapLayers[layerid][ty]
+ return row and row[tx] or kMapTileTypeEmpty
+end
+
+function TiledMap_SetMapTile (tx,ty,layerid,v) -- coords in tiles
+ local row = gMapLayers[layerid][ty]
+ if (not row) then row = {} gMapLayers[layerid][ty] = row end
+ row[tx] = v
+end
+
+-- todo : maybe optimize during parse xml for types registered as to-be-listed before parsing ?
+function TiledMap_ListAllOfTypeOnLayer (layerid,iTileType)
+ local res = {}
+ local w = TiledMap_GetMapW()
+ local h = TiledMap_GetMapH()
+ for x=0,w-1 do
+ for y=0,h-1 do
+ if (TiledMap_GetMapTile(x,y,layerid) == iTileType) then table.insert(res,{x=x,y=y}) end
+ end
+ end
+ return res
+end
+
+function TiledMap_GetLayerZByName (layername) for z,layer in ipairs(gMapLayers) do if (layer.name == layername) then return z end end end
+function TiledMap_SetLayerInvisByName (layername) gTileMap_LayerInvisByName[layername] = true end
+
+function TiledMap_IsLayerVisible (z)
+ local layer = gMapLayers[z]
+ return layer and (not gTileMap_LayerInvisByName[layer.name or "?"])
+end
+
+function TiledMap_GetTilePosUnderMouse (mx,my,camx,camy)
+ return floor((mx+camx-love.graphics.getWidth()/2)/kTileSize),
+ floor((my+camy-love.graphics.getHeight()/2)/kTileSize)
+end
+
+function TiledMap_DrawNearCam (camx,camy,fun_layercallback)
+ camx,camy = floor(camx),floor(camy)
+ local screen_w = love.graphics.getWidth()
+ local screen_h = love.graphics.getHeight()
+ local minx,maxx = floor((camx-screen_w/2)/kTileSize),ceil((camx+screen_w/2)/kTileSize)
+ local miny,maxy = floor((camy-screen_h/2)/kTileSize),ceil((camy+screen_h/2)/kTileSize)
+ for z = 1,#gMapLayers do
+ if (fun_layercallback) then fun_layercallback(z,gMapLayers[z]) end
+ if (TiledMap_IsLayerVisible(z)) then
+ for x = minx,maxx do
+ for y = miny,maxy do
+ local gfx = gTileGfx[TiledMap_GetMapTile(x,y,z)]
+ if (gfx) then
+ local sx = x*kTileSize - camx + screen_w/2
+ local sy = y*kTileSize - camy + screen_h/2
+ love.graphics.draw(gfx,sx,sy) -- x, y, r, sx, sy, ox, oy
+ end
+ end
+ end
+ end
+ end
+end
+
+
+-- ***** ***** ***** ***** ***** xml parser
+
+
+-- LoadXML from http://lua-users.org/wiki/LuaXml
+function LoadXML(s)
+ local function LoadXML_parseargs(s)
+ local arg = {}
+ string.gsub(s, "(%w+)=([\"'])(.-)%2", function (w, _, a)
+ arg[w] = a
+ end)
+ return arg
+ end
+ local stack = {}
+ local top = {}
+ table.insert(stack, top)
+ local ni,c,label,xarg, empty
+ local i, j = 1, 1
+ while true do
+ ni,j,c,label,xarg, empty = string.find(s, "<(%/?)([%w:]+)(.-)(%/?)>", i)
+ if not ni then break end
+ local text = string.sub(s, i, ni-1)
+ if not string.find(text, "^%s*$") then
+ table.insert(top, text)
+ end
+ if empty == "/" then -- empty element tag
+ table.insert(top, {label=label, xarg=LoadXML_parseargs(xarg), empty=1})
+ elseif c == "" then -- start tag
+ top = {label=label, xarg=LoadXML_parseargs(xarg)}
+ table.insert(stack, top) -- new level
+ else -- end tag
+ local toclose = table.remove(stack) -- remove top
+ top = stack[#stack]
+ if #stack < 1 then
+ error("nothing to close with "..label)
+ end
+ if toclose.label ~= label then
+ error("trying to close "..toclose.label.." with "..label)
+ end
+ table.insert(top, toclose)
+ end
+ i = j+1
+ end
+ local text = string.sub(s, i)
+ if not string.find(text, "^%s*$") then
+ table.insert(stack[#stack], text)
+ end
+ if #stack > 1 then
+ error("unclosed "..stack[stack.n].label)
+ end
+ return stack[1]
+end
+
+
+-- ***** ***** ***** ***** ***** parsing the tilemap xml file
+
+local function getTilesets(node)
+ local tiles = {}
+ for k, sub in ipairs(node) do
+ if (sub.label == "tileset") then
+ tiles[tonumber(sub.xarg.firstgid)] = sub[1].xarg.source
+ end
+ end
+ return tiles
+end
+
+local function getLayers(node)
+ local layers = {}
+ layers.width = 0
+ layers.height = 0
+ for k, sub in ipairs(node) do
+ if (sub.label == "layer") then -- and sub.xarg.name == layer_name
+ layers.width = max(layers.width ,tonumber(sub.xarg.width ) or 0)
+ layers.height = max(layers.height,tonumber(sub.xarg.height) or 0)
+ local layer = {}
+ table.insert(layers,layer)
+ layer.name = sub.xarg.name
+ --~ print("layername",layer.name)
+ width = tonumber(sub.xarg.width)
+ i = 0
+ j = 0
+ for l, child in ipairs(sub[1]) do
+ if (j == 0) then
+ layer[i] = {}
+ end
+ layer[i][j] = tonumber(child.xarg.gid)
+ j = j + 1
+ if j >= width then
+ j = 0
+ i = i + 1
+ end
+ end
+ end
+ end
+ return layers
+end
+
+function TiledMap_Parse(filename)
+ local xml = LoadXML(jin.filesystem.read(filename))
+ local tiles = getTilesets(xml[2])
+ local layers = getLayers(xml[2])
+ return tiles, layers
+end \ No newline at end of file