changeset 2:0c46edeabad6

scrolling map
author Radomir Dopieralski <sheep-devel@sheep.art.pl>
date Sat, 15 Dec 2012 13:10:41 +0100
parents 71b36e588242
children f5de6c75c4d0
files AdvTiledLoader/Base64.lua AdvTiledLoader/Grid.lua AdvTiledLoader/Loader.lua AdvTiledLoader/Map.lua AdvTiledLoader/Object.lua AdvTiledLoader/ObjectLayer.lua AdvTiledLoader/README.md AdvTiledLoader/Tile.lua AdvTiledLoader/TileLayer.lua AdvTiledLoader/TileSet.lua AdvTiledLoader/VERSION.txt AdvTiledLoader/external/crc32lua.lua AdvTiledLoader/external/deflatelua.lua AdvTiledLoader/external/gunziplua.lua AdvTiledLoader/external/numberlua.lua AdvTiledLoader/external/optparse.lua AdvTiledLoader/external/xml.lua AdvTiledLoader/init.lua level01.tmx main.lua shadow.png sprite.lua tiles.png villain.png
diffstat 24 files changed, 4286 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AdvTiledLoader/Base64.lua	Sat Dec 15 13:10:41 2012 +0100
@@ -0,0 +1,203 @@
+---------------------------------------------------------------------------------------------------
+-- -= Base64 =-
+---------------------------------------------------------------------------------------------------
+
+-- Make some functions easier to write
+local floor = math.floor
+local sub = string.sub
+local gsub = string.gsub
+local rem = table.remove
+
+---------------------------------------------------------------------------------------------------
+-- Our base64 value table
+local base64 = { ['A']=0,['B']=1,['C']=2,['D']=3,['E']=4,['F']=5,['G']=6,['H']=7,['I']=8,
+                ['J']=9,['K']=10,['L']=11,['M']=12,['N']=13,['O']=14,['P']=15,['Q']=16,
+                ['R']=17,['S']=18,['T']=19,['U']=20,['V']=21,['W']=22,['X']=23,['Y']=24,
+                ['Z']=25,['a']=26,['b']=27,['c']=28,['d']=29,['e']=30,['f']=31,['g']=32,
+                ['h']=33,['i']=34,['j']=35,['k']=36,['l']=37,['m']=38,['n']=39,['o']=40,
+                ['p']=41,['q']=42,['r']=43,['s']=44,['t']=45,['u']=46,['v']=47,['w']=48,
+                ['x']=49,['y']=50,['z']=51,['0']=52,['1']=53,['2']=54,['3']=55,['4']=56,
+                ['5']=57,['6']=58,['7']=59,['8']=60,['9']=61,['+']=62,['/']=63,['=']=nil}
+
+---------------------------------------------------------------------------------------------------
+-- Decimal values for binary digits
+local bin ={}
+local mult = 1
+for i = 1,40 do
+    bin[i] = mult
+    mult = mult*2
+end
+
+---------------------------------------------------------------------------------------------------
+-- A buffer we will use to process the bits
+local buffer = 0
+local pos = 0
+local function clearBuffer()
+    buffer = 0
+    pos = 1
+end
+
+---------------------------------------------------------------------------------------------------
+-- Shift all of the bits up in the buffer and put the base64 number on the bottom
+local function pushBase64(n)
+    if base64[n] == nil then return end
+    buffer = buffer * bin[7] + base64[n]
+    pos = pos + 6
+end
+
+---------------------------------------------------------------------------------------------------
+-- Get an int out of the buffer. This is tricky. The byte order is in little endian so we're going
+-- to have to isolate and cut the bytes out and then move them around.
+local function getInt()
+    -- If our buffer isn't filled all the way then fill it with zeros
+    while pos < 33 do 
+        buffer = buffer * bin[2] 
+        pos = pos + 1
+    end
+    -- Move the buffer position to just below the integer.
+    pos = pos - 32
+    
+    -- Swap the first and forth byte and then the second and third.
+    local tmp = floor((buffer%bin[33+pos-1])/bin[25+pos-1]) +
+                floor((buffer%bin[25+pos-1])/bin[17+pos-1])*bin[9] +
+                floor((buffer%bin[17+pos-1])/bin[9+pos-1])*bin[17] + 
+                floor((buffer%bin[9+pos-1])/bin[pos])*bin[25]
+    
+    -- We've got our integer so let's cut that portion out of the buffer
+    buffer = buffer % bin[pos]
+    -- Return the int
+    return  tmp
+end
+
+---------------------------------------------------------------------------------------------------
+-- Get a byte out of the buffer
+local function getByte()
+    -- If our buffer isn't filled all the way then fill it with zeros
+    while pos < 9 do
+        buffer = buffer * bin[2] 
+        pos = pos + 1
+    end
+    -- Move the buffer position to just below the byte.
+    pos = pos - 8
+    -- Cut out the byte
+    local tmp = floor((buffer%bin[9+pos-1])/bin[pos])
+    -- Delete the byte from the buffer
+    buffer = buffer % bin[pos]
+    -- Return the byte
+    return tmp
+end
+
+---------------------------------------------------------------------------------------------------
+-- Glues together an integer from four bytes. Little endian
+local function glueInt(b1, b2, b3, b4)
+    return b1%bin[9] + b2%bin[9]*bin[9] + b3%bin[9]*bin[17] + b4%bin[9]*bin[25]
+end
+
+---------------------------------------------------------------------------------------------------
+-- A Lua set that will filter out characters that aren't in the base64 table
+local set = "[^%a%d%+%/%=]"
+
+---------------------------------------------------------------------------------------------------
+-- Decodes a base64 string into the given type
+local function decode(mode, raw)
+
+    -- Make sure the mode is supported
+    assert(mode=="string" or mode=="int" or mode=="byte", "Base64 decode - Invalid mode: " .. mode)
+
+    -- Clear the buffer
+    clearBuffer()
+    
+    -- Filters undefined characters out of the string
+    raw = gsub(raw, set, "")
+    
+    local size = 0          -- Size of the returned type in bits
+    local val = {}          -- A table containing the data to be returned
+    local raw_pos = 1       -- The position of the progress through the raw base64 string
+    local raw_size = #raw   -- The size of the base64 string
+    local char = ""         -- The current base64 character to be processed
+    
+    -- If we're expected to return an int then the bit size is 32, otherwise it's 8
+    if mode == "int" then size = 32 else size = 8 end
+    
+    -- While we still have input
+    while raw_pos <= raw_size do
+        -- Fill the buffer until we have enough bits
+        while pos <= size and raw_pos <= raw_size do
+            char = sub(raw,raw_pos,raw_pos)
+            pushBase64( char )
+            raw_pos = raw_pos + 1
+        end
+        -- If a nil character is encountered the end the loop
+        if char == "=" then break end
+        -- Get data from the buffer depending on the type
+        if mode == "string" then val[#val+1] = string.char( getByte() ) end
+        if mode == "byte" then val[#val+1] = getByte() end
+        if mode == "int" then val[#val+1] = getInt() end
+    end
+    
+    if mode == "string" then return table.concat(val) end
+    return val
+end
+
+---------------------------------------------------------------------------------------------------
+-- Encodes a table of ints into a base64 string.
+local function encode(ints)
+
+    -- Clear the buffer
+    clearBuffer()
+    
+    local size = 0          -- Size of the returned type in bits
+    local val = {}          -- A table containing the data to be returned
+    local char = ""         -- The current base64 character to be processed
+    
+    -- If we're expected to return an int then the bit size is 32, otherwise it's 8
+    if mode == "int" then size = 32 else size = 8 end
+    
+    -- While we still have input
+    while raw_pos <= raw_size do
+        -- Fill the buffer until we have enough bits
+        while pos <= size and raw_pos <= raw_size do
+            char = sub(raw,raw_pos,raw_pos)
+            pushBase64( char )
+            raw_pos = raw_pos + 1
+        end
+        -- If a nil character is encountered the end the loop
+        if char == "=" then break end
+        -- Get data from the buffer depending on the type
+        if mode == "string" then val[#val+1] = string.char( getByte() ) end
+        if mode == "byte" then val[#val+1] = getByte() end
+        if mode == "int" then val[#val+1] = getInt() end
+    end
+    
+    if mode == "string" then return table.concat(val) end
+    return val
+end
+
+---------------------------------------------------------------------------------------------------
+-- Returns the functions
+return {decode = decode, glueInt = glueInt, base64 = base64}
+
+
+--[[Copyright (c) 2011-2012 Casey Baxter
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+Except as contained in this notice, the name(s) of the above copyright holders
+shall not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.--]]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AdvTiledLoader/Grid.lua	Sat Dec 15 13:10:41 2012 +0100
@@ -0,0 +1,172 @@
+---------------------------------------------------------------------------------------------------
+-- -= Grid =-
+---------------------------------------------------------------------------------------------------
+local Grid = {class = "Grid"}
+Grid.__index = Grid 
+
+---------------------------------------------------------------------------------------------------
+-- Creates and returns a new grid
+function Grid:new()
+    local grid = {}
+    grid.cells = {}
+    return setmetatable(grid, Grid)
+end
+
+---------------------------------------------------------------------------------------------------
+-- Gets the value of a single cell
+function Grid:get(x,y)
+    return self.cells[x] and self.cells[x][y]
+end
+
+---------------------------------------------------------------------------------------------------
+-- Sets the value of a single cell
+function Grid:set(x,y,value)
+    if not self.cells[x] then self.cells[x] = {} end
+    self.cells[x][y] = value
+end
+
+---------------------------------------------------------------------------------------------------
+-- Iterate over all values
+local x, y, row, val
+function Grid:iterate()
+    x, y, row, val = nil, nil, nil, nil
+    return function()
+        repeat
+            if not y then 
+                x, row = next(self.cells, x) 
+                if not row then return end
+            end
+            y, val = next(row, y) 
+        until y
+        return x, y, val
+    end
+end
+
+---------------------------------------------------------------------------------------------------
+-- Iterate over a rectangle shape
+function Grid:rectangle(startX, startY, width, height, includeNil)
+    local x, y = startX, startY
+    return function()
+        while y <= startY + height do
+            while x <= startX + width do
+                x = x+1
+                if self(x-1,y) ~= nil or includeNil then 
+                    return x-1, y, self(x-1,y)
+                end
+            end
+            x = startX
+            y = y+1
+        end
+        return nil
+    end
+end
+
+---------------------------------------------------------------------------------------------------
+-- Iterate over a line. Set noDiag to true to keep from traversing diagonally.
+function Grid:line(startX, startY, endX, endY, noDiag, includeNil)  
+    local dx = math.abs(endX - startX)
+    local dy = math.abs(endY - startY)
+    local x = startX
+    local y = startY
+    local incrX = endX > startX and 1 or -1 
+    local incrY = endY > startY and 1 or -1 
+    local err = dx - dy
+    local err2 = err*2
+    local i = 1+dx+dy
+    local rx,ry,rv 
+    local checkX = false
+    return function()
+        while i>0 do 
+            rx,ry,rv = x,y,self(x,y)
+            err2 = err*2
+            while true do
+                checkX = not checkX     
+                if checkX == true or not noDiag then 
+                    if err2 > -dy then
+                        err = err - dy
+                        x = x + incrX
+                        i = i-1
+                        if noDiag then break end
+                    end
+                end
+                if checkX == false or not noDiag then
+                    if err2 < dx then
+                        err = err + dx
+                        y = y + incrY
+                        i = i-1
+                        if noDiag then break end
+                    end
+                end
+                if not noDiag then break end
+            end
+            if rx == endX and ry == endY then i = 0 end
+            if rv ~= nil or includeNil then return rx,ry,rv end
+        end
+        return nil
+    end
+end
+
+---------------------------------------------------------------------------------------------------
+-- Iterates over a circle of cells
+function Grid:circle(cx, cy, r, includeNil)
+    local x,y
+    x = x or cx-r
+    return function()
+        repeat
+            y = y == nil and cy or y <= cy and y-1 or y+1
+            while ((cx-x)*(cx-x)+(cy-y)*(cy-y)) >= r*r do
+                if x > cx+r then return nil end
+                x = x + (y < cy and 0 or 1)
+                y = cy + (y < cy and 1 or 0)
+            end
+        until self(x,y) ~= nil or includeNil
+        return x,y,self(x,y)
+    end
+end
+
+---------------------------------------------------------------------------------------------------
+-- Cleans the grid of empty rows. 
+function Grid:clean()
+    for key,row in pairs(self.cells) do
+        if not next(row) then self.cells[key] = nil end
+    end
+end
+
+---------------------------------------------------------------------------------------------------
+-- Clears the grid
+function Grid:clear()
+    self.cells = {}
+end
+
+---------------------------------------------------------------------------------------------------
+-- This makes calling the grid as a function act like Grid.get.
+Grid.__call = Grid.get
+
+---------------------------------------------------------------------------------------------------
+-- Returns the grid class
+return Grid
+
+--------------------------------------------------------------------------------------
+-- Copyright (c) 2011-2012 Casey Baxter
+
+-- Permission is hereby granted, free of charge, to any person obtaining a copy
+-- of this software and associated documentation files (the "Software"), to deal
+-- in the Software without restriction, including without limitation the rights
+-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+-- copies of the Software, and to permit persons to whom the Software is
+-- furnished to do so, subject to the following conditions:
+
+-- The above copyright notice and this permission notice shall be included in
+-- all copies or substantial portions of the Software.
+
+-- Except as contained in this notice, the name(s) of the above copyright holders
+-- shall not be used in advertising or otherwise to promote the sale, use or
+-- other dealings in this Software without prior written authorization.
+
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+-- THE SOFTWARE.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AdvTiledLoader/Loader.lua	Sat Dec 15 13:10:41 2012 +0100
@@ -0,0 +1,767 @@
+---------------------------------------------------------------------------------------------------
+-- -= Loader =-
+---------------------------------------------------------------------------------------------------
+
+-- Define path so lua knows where to look for files.
+TILED_LOADER_PATH = TILED_LOADER_PATH or ({...})[1]:gsub("[%.\\/][Ll]oader$", "") .. '.'
+
+-- A cache to store tileset images so we don't load them multiple times. Filepaths are keys.
+local cache = setmetatable({}, {__mode = "v"})
+
+-- This stores cached images' original dimensions. Images are weak keys.
+local cache_imagesize = setmetatable({}, {__mode="k"})
+
+-- Decompresses gzip and zlib
+local decompress = require(TILED_LOADER_PATH .. "external/deflatelua")
+
+-- XML parser
+local xml = require(TILED_LOADER_PATH .. "external/xml")
+
+-- Base64 parser, Turns base64 strings into other data formats
+local base64 = require(TILED_LOADER_PATH .. "Base64")
+
+-- Get the map classes
+local Map = require(TILED_LOADER_PATH .. "Map")
+local TileSet = require(TILED_LOADER_PATH .. "TileSet")
+local TileLayer = require(TILED_LOADER_PATH .. "TileLayer")
+local Tile = require(TILED_LOADER_PATH .. "Tile")
+local Object = require(TILED_LOADER_PATH .. "Object")
+local ObjectLayer = require(TILED_LOADER_PATH .. "ObjectLayer")
+
+local Loader = {
+    path = "",                      -- The path to tmx files.
+    filterMin = "nearest",          -- The default min filter for image:setFilter()
+    filterMag = "nearest",          -- The default mag filter for image:setFilter()
+    useSpriteBatch = true,          -- The default setting for map.useSpriteBatch
+    drawObjects = true,             -- The default setting for map.drawObjects. 
+    saveDirectory = "Saved Maps",   -- The directory to use for Loader.save()
+}
+
+local filename  -- The name of the tmx file
+local fullpath  -- The full path to the tmx file minus the name
+
+----------------------------------------------------------------------------------------------------
+-- Loads a Map from a tmx file and returns it.
+function Loader.load(tofile)
+    
+    -- Get the raw path
+    fullpath = Loader.path .. tofile
+    
+    -- Process directory up
+    while string.find(fullpath, "[/\\][^/^\\]+[/\\]%.%.[/\\]") do
+        fullpath = string.gsub(fullpath, "[/\\][^/^\\]+[/\\]%.%.[/\\]", "/", 1)
+    end
+    
+    -- Get the file name
+    filename = string.match(fullpath, "[^/^\\]+$")
+    
+    -- Get the path to the file
+    fullpath = string.gsub(fullpath, "[^/^\\]+$", "")
+    
+    -- Find out if the file is in the game or save directory
+    if love.filesystem.exists(fullpath .. filename) then
+    elseif love.filesystem.exists(fullpath .. filename .. ".tmx") then
+        filename = filename .. ".tmx"
+    elseif love.filesystem.exists(Loader.saveDirectory.."/"..filename) then
+        fullpath = Loader.saveDirectory .. "/"
+    elseif love.filesystem.exists(Loader.saveDirectory .. "/" .. filename .. ".tmx") then
+        fullpath = Loader.saveDirectory .. "/"
+        filename = filename .. ".tmx"
+    else
+        error("Loader.load() Could not find find the file in these locations:\n" ..
+            "\nGame Directory: " .. fullpath .. filename ..
+            "\nGame Directory: " .. fullpath .. filename .. ".tmx" ..
+            "\nSave Directory: " .. Loader.saveDirectory.."/"..filename ..
+            "\nSave Directory: " .. Loader.saveDirectory.."/"..filename .. ".tmx")
+    end
+    
+    -- Read the file and parse it into a table
+    local t = love.filesystem.read(fullpath .. filename)
+    t = xml.string_to_table(t)
+    
+    -- Get the map and expand it
+    for _, v in pairs(t) do
+        if v.label == "map" then
+            return Loader._expandMap(fullpath .. filename, v)
+        end
+    end
+    
+    -- If we made this this far then there wasn't a map tag
+    error("Loader.load - No map found in file " .. fullpath .. filename)
+end
+
+----------------------------------------------------------------------------------------------------
+-- Saves a map to a file.
+function Loader.save(map, filename)
+    local mapx = Loader._compactMap(map)
+    if not love.filesystem.exists(Loader.saveDirectory) then
+        love.filesystem.mkdir(Loader.saveDirectory)
+    end
+    love.filesystem.write( Loader.saveDirectory .. "/" .. filename, xml.table_to_string(mapx) )
+end
+
+----------------------------------------------------------------------------------------------------
+-- Checks to see if a saved map exists
+function Loader.savedMapExists(filename)
+    if love.filesystem.exists(Loader.saveDirectory.."/"..filename) then
+        return true
+    elseif love.filesystem.exists(Loader.saveDirectory .. "/" .. filename .. ".tmx") then
+        return true
+    end
+    return false
+end
+
+----------------------------------------------------------------------------------------------------
+-- Private
+----------------------------------------------------------------------------------------------------
+
+-- Returns a new image from the filename. 
+function Loader._newImage(source)
+    return love.graphics.newImage(source), source:getWidth(), source:getHeight()
+end
+
+----------------------------------------------------------------------------------------------------
+-- Checks to see if the table is a valid XML table
+function Loader._checkXML(t)
+    assert(type(t) == "table", "Loader._checkXML - Passed value is not a table")
+    assert(t ~= Loader, "Loader._checkXML - Passed table is the Loader class. " ..
+                        "You probably used a : instead of a .")
+    assert(t.label, "Loader._checkXML - Table does not contain a label value")
+    assert(t.xarg, "Loader._checkXML - Table does not contain an xarg table")
+end
+
+----------------------------------------------------------------------------------------------------
+-- This is used to eliminate naming conflicts. It checks to see if the string is inside the table and
+-- continues to rename it until there isn't a conflict.
+function Loader._checkName(t, str)
+    while t[str] do
+        if string.find(str, "%(%d+%)$") == nil then str = str .. "(1)" end
+        str = string.gsub(str, "%(%d+%)$", function(a) return "(" .. 
+                                tonumber( string.sub(a, string.find(a, "%d+")) ) + 1 .. ")" end)
+    end
+    return str
+end
+
+----------------------------------------------------------------------------------------------------
+-- Processes a properties table and returns it
+function Loader._expandProperties(t)
+
+    -- Do some checking
+    Loader._checkXML(t)
+    if t.label ~= "properties" then
+        error("Loader._expandProperties - Passed value is not a properties table")
+    end
+    
+    -- Create a properties table and populate it. Will attempt to convert the property to
+    -- the appropriate type.
+    local prop = {}
+    for _,v in pairs(t) do
+        Loader._checkXML(t)
+        if v.label == "property" then
+            if v.xarg.value == "true" then
+                prop[v.xarg.name] = true
+            elseif v.xarg.value == "false" then
+                prop[v.xarg.name] = false
+            else
+                prop[v.xarg.name] = tonumber(v.xarg.value) or v.xarg.value
+            end
+        end
+    end
+    
+    -- Return the properties
+    return prop
+end
+
+----------------------------------------------------------------------------------------------------
+-- Process Map data from xml table
+function Loader._expandMap(name, t)
+    
+    -- Do some checking
+    Loader._checkXML(t)
+    assert(t.label == "map", "Loader._expandMap - Passed table is not a map")
+    assert(t.xarg.width, t.xarg.height, t.xarg.tilewidth, t.xarg.tileheight,
+           "Loader._expandMap - Map data is corrupt")
+
+    -- We'll use these for temporary storage
+    local map, tileset, tilelayer, objectlayer, props
+    local props = {}
+    
+    -- Get the properties
+    for _, v in ipairs(t) do
+        if v.label == "properties" then
+            props = Loader._expandProperties(v)
+        end
+    end
+    
+    -- Create the map from the settings
+    local map = Map:new(name, tonumber(t.xarg.width),tonumber(t.xarg.height), 
+                        tonumber(t.xarg.tilewidth), tonumber(t.xarg.tileheight), 
+                        t.xarg.orientation, props.atl_directory or fullpath, props)
+
+    -- Apply the loader settings if atl_useSpriteBatch or atl_drawObjects was not set
+    map.useSpriteBatch = map.useSpriteBatch == nil and Loader.useSpriteBatch or map.useSpriteBatch 
+    map.drawObjects = map.drawObjects == nil and  Loader.drawObjects or map.drawObjects
+    
+    -- Now we fill it with the content
+    for _, v in ipairs(t) do
+        
+        -- Process TileSet
+        if v.label == "tileset" then 
+            tileset = Loader._expandTileSet(v, map)
+            map.tilesets[tileset.name] = tileset
+            map:updateTiles()
+        end
+            
+        -- Process TileLayer
+        if v.label == "layer" then
+            tilelayer = Loader._expandTileLayer(v, map)
+            map.layers[tilelayer.name] = tilelayer
+            map.layerOrder[#map.layerOrder + 1] = tilelayer
+        end
+        
+        -- Process ObjectLayer
+        if v.label == "objectgroup" then
+            objectlayer = Loader._expandObjectLayer(v, map)
+            map.layers[objectlayer.name] = objectlayer
+            map.layerOrder[#map.layerOrder + 1] = objectlayer
+        end
+        
+        -- Process CustomLayer
+        if v.label == "customlayer" then
+            map:newCustomLayer(v.xarg.name)
+            for _, v2 in ipairs(v) do
+                if v2.label == "data" then
+                    map(v.xarg.name).data = v2[1]
+                end
+            end
+        end
+        
+    end
+    
+    -- Return our map
+    return map
+end
+
+----------------------------------------------------------------------------------------------------
+-- Process TileSet from xml table
+function Loader._expandTileSet(t, map)
+
+    -- Do some checking
+    Loader._checkXML(t)
+    assert(t.label == "tileset", "Loader._expandTileSet - Passed table is not a tileset")
+    
+    -- If the tileset is an external one then replace it as the tileset. The firstgid is 
+    -- stored in the tileset tag in the original file while the rest of the tileset information 
+    -- is stored in the external file.
+    if t.xarg.source then 
+        local gid = t.xarg.firstgid
+        t = love.filesystem.read(map._directory .. t.xarg.source)
+        for _,v in pairs(xml.string_to_table(t)) do if v.label == "tileset" then t = v end end
+        t.xarg.firstgid = gid
+    end
+    
+    if not t.xarg.name or not t.xarg.tilewidth or not t.xarg.tileheight or not t.xarg.firstgid then
+        error("Loader._expandTileSet - Tileset data is corrupt")
+    end
+
+    
+    -- Temporary storage
+    local image, imagePath, imageWidth, imageHeight, path, prop, tileSetProperties, trans
+    local tileProperties = {}
+    local offsetX, offsetY = 0, 0
+    
+    -- Process elements
+    for _, v in ipairs(t) do
+        -- Process image
+        if v.label == "image" then 
+            imagePath = v.xarg.source
+            path = map._directory .. v.xarg.source
+            -- Process directory up
+            while string.find(path, "[^/^\\]+[/\\]%.%.[/\\]") do
+                path = string.gsub(path, "[^/^\\]+[/\\]%.%.[/\\]", "", 1)
+            end
+            -- If the image is in the cache then load it
+            if cache[path] then
+                image = cache[path]
+                imageWidth = cache_imagesize[image].width
+                imageHeight = cache_imagesize[image].height
+            -- Else load it and store in the cache
+            else
+                image = love.image.newImageData(path) 
+                -- transparent color
+                if v.xarg.trans then
+                    trans = { tonumber( "0x" .. v.xarg.trans:sub(1,2) ), 
+                              tonumber( "0x" .. v.xarg.trans:sub(3,4) ), 
+                              tonumber( "0x" .. v.xarg.trans:sub(5,6) )}
+                    image:mapPixel( function(x,y,r,g,b,a)
+                    return r,g,b, (trans[1] == r and trans[2] == g and trans[3] ==b and 0) or a  end
+                    )
+                end
+                -- Set the image information
+                image, imageWidth, imageHeight = Loader._newImage(image)
+                image:setFilter(Loader.filterMin, Loader.filterMag)
+                -- Cache the created image
+                cache[path] = image
+                cache_imagesize[image] = {width = imageWidth, height = imageHeight}
+            end
+        end
+        
+        -- Process tile properties
+        if v.label == "tile" then 
+            for _, v2 in ipairs(v) do
+                if v2.label == "properties" then
+                    -- Store the property. We must increase the id the starting gid
+                    if not v.xarg.id then error(v.xarg.id) end
+                    tileProperties[v.xarg.id+t.xarg.firstgid] = Loader._expandProperties(v2)
+                end
+            end
+        end
+        
+        -- Process tile set properties
+        if v.label == "properties" then
+            tileSetProperties = Loader._expandProperties(v)
+        end
+        
+        -- Get the tile offset if there is one.
+        if v.label == "tileoffset" then
+            offsetX, offsetY = tonumber(v.xarg.x or 0), tonumber(v.xarg.y or 0)
+        end
+
+    end
+    
+    -- Make sure that an image was loaded
+    assert(image, "Loader._expandTileSet - Tileset did not contain an image")
+
+    -- Return the TileSet
+    local tileset = TileSet:new(image, imagePath, Loader._checkName(map.tilesets, t.xarg.name), 
+                       tonumber(t.xarg.tilewidth), tonumber(t.xarg.tileheight),
+                       tonumber(imageWidth), tonumber(imageHeight),
+                       tonumber(t.xarg.firstgid), tonumber(t.xarg.spacing), tonumber(t.xarg.margin),
+                       offsetX, offsetY, trans, tileProperties, tileSetProperties)
+    return tileset
+end
+
+----------------------------------------------------------------------------------------------------
+-- Process TileLayer from xml table
+function Loader._expandTileLayer(t, map)
+
+    -- Do some checking
+    Loader._checkXML(t)
+    assert(t.label == "layer", "Loader._expandTileLayer - Passed table is not a tileset")
+    
+    -- Process elements
+    local data, properties
+    for _, v in ipairs(t) do
+        Loader._checkXML(t)
+        
+        -- Process data
+        if v.label == "data" then 
+            data = Loader._expandTileLayerData(v) 
+        end
+        
+        -- Process TileLayer properties
+        if v.label == "properties" then
+            properties = Loader._expandProperties(v)
+        end
+    end
+    
+    -- Create the new layer
+    local layer = TileLayer:new(map, t.xarg.name, t.xarg.opacity, properties)
+    
+    -- Set the visibility
+    layer.visible = not (t.xarg.visible == "0")
+    
+    -- Populate the tiles and return the layer
+    layer:_populate(data)
+    return layer
+end
+
+----------------------------------------------------------------------------------------------------
+-- Process TileLayer data from xml table
+function Loader._expandTileLayerData(t)
+
+    -- Do some checking
+    Loader._checkXML(t)
+    assert(t.label == "data", "Loader._expandTileLayerData - Passed table is not TileLayer data")
+    
+    local data = {}
+    
+    -- If encoded by comma seperated value (csv) then cut each value out and put it into a table.
+    if t.xarg.encoding == "csv" then
+            string.gsub(t[1], "[%-%d]+", function(a) data[#data+1] = tonumber(a) or 0 end)
+    end
+    
+    -- Base64 encoding. See base64.lua for more details.
+    if t.xarg.encoding == "base64" then
+    
+        -- If a compression method is used
+        if t.xarg.compression == "gzip" or t.xarg.compression == "zlib"  then
+            -- Select the appropriate function
+            local decomp = t.xarg.compression == "gzip" and decompress.gunzip or decompress.inflate_zlib
+            -- Decompress the string into bytes
+            local bytes = {}
+            decomp({input = base64.decode("string", t[1]), output = function(b) bytes[#bytes+1] = b end})
+            -- Glue the bytes into ints
+            for i=1,#bytes,4 do
+                data[#data+1] = base64.glueInt(bytes[i],bytes[i+1],bytes[i+2],bytes[i+3])
+            end
+        -- If there is no compression then just convert to ints
+        else
+            data = base64.decode("int", t[1])
+        end
+    end
+    
+    -- If there is no encoding then the file is probably saved as XML
+    if t.xarg.encoding == nil then
+        for k,v in ipairs(t) do
+            if v.label == "tile" then 
+                data[#data+1] = tonumber(v.xarg.gid)
+            end
+        end
+    end
+    
+    -- Return the data
+    return data
+end
+
+----------------------------------------------------------------------------------------------------
+-- Process ObjectLayer from xml table
+function Loader._expandObjectLayer(t, map)
+
+    -- Do some checking
+    Loader._checkXML(t)
+    if t.label ~= "objectgroup" then
+        error("Loader._expandObjectLayer - Passed table is not ObjectLayer data")
+    end
+    
+    -- Tiled stores colors in hexidecimal format that looks like "#FFFFFF" 
+    -- We need go convert them into base 10 RGB format
+    if t.xarg.color == nil then t.xarg.color = "#000000" end
+    local color = { tonumber( "0x" .. t.xarg.color:sub(2,3) ), 
+                    tonumber( "0x" .. t.xarg.color:sub(4,5) ), 
+                    tonumber( "0x" .. t.xarg.color:sub(6,7) )}
+    
+    -- Create a new layer
+    local layer = ObjectLayer:new(map, Loader._checkName(map.layers, t.xarg.name), color, 
+                                  t.xarg.opacity)
+                    
+    -- Process elements
+    local objects = {}
+    local prop, obj, poly
+    for _, v in ipairs(t) do
+    
+        -- Process objects
+        local obj
+        if v.label == "object" then
+            obj = Object:new(layer, v.xarg.name, v.xarg.type, tonumber(v.xarg.x), 
+                                tonumber(v.xarg.y), tonumber(v.xarg.width), 
+                                tonumber(v.xarg.height), tonumber(v.xarg.gid) )
+            objects[#objects+1] = obj
+            for _, v2 in ipairs(v) do
+            
+                -- Process object properties
+                if v2.label == "properties" then 
+                    obj.properties = Loader._expandProperties(v2)
+                end
+                
+                -- Process polyline objects
+                local polylineFunct = function(a) 
+                    obj.polyline[#obj.polyline+1] = tonumber(a) or 0 
+                end
+                if v2.label == "polyline" then
+                    obj.polyline = {}
+                    string.gsub(v2.xarg.points, "[%-%d]+", polylineFunct)
+                end
+                
+                -- Process polyline objects
+                local polygonFunct = function(a) 
+                    obj.polygon[#obj.polygon+1] = tonumber(a) or 0 
+                end
+                if v2.label == "polygon" then
+                    obj.polygon = {}
+                    string.gsub(v2.xarg.points, "[%-%d]+", polygonFunct)
+                end
+            
+            end
+            obj:updateDrawInfo()
+        end
+        
+        -- Process properties
+        if v.label == "properties" then
+            prop = Loader._expandProperties(v)
+        end
+        
+    end
+    
+    -- Set the properties and object tables
+    layer.properties = prop or {}
+    layer.objects = objects
+    
+    -- Set the visibility
+    layer.visible = not (t.xarg.visible == "0")
+    
+    -- Return the layer
+    return layer
+end
+
+----------------------------------------------------------------------------------------------------
+-- Compact a Map 
+function Loader._compactMap(map)
+
+    -- Set the ATL properties
+    map.properties.atl_directory = map._directory
+    map.properties.atl_useSpritebatch = map.useSpriteBatch
+    map.properties.atl_viewX = map.viewX
+    map.properties.atl_viewY = map.viewY
+    map.properties.atl_viewW = map.viewW
+    map.properties.atl_viewH = map.viewH
+    map.properties.atl_viewScaling = map.viewScaling
+    map.properties.atl_viewPadding = map.viewPadding
+    map.properties.atl_drawObjects = map.drawObjects
+
+    -- <map>
+    local mapx = {label="map", xarg = {
+        version = "1.0",
+        orientation = map.orientation,
+        width = map.width,
+        height = map.height,
+        tilewidth = map.tileWidth,
+        tileheight = map.tileHeight,
+    }}
+    
+    -- <tileset>
+    for k,tileset in pairs(map.tilesets) do
+        mapx[#mapx+1] = Loader._compactTileSet(tileset)
+    end
+    
+    local layer, layerx
+    for i = 1,#map.layerOrder do
+        layer = map.layerOrder[i]
+        
+        -- <layer>
+        if layer.class == "TileLayer" then
+            mapx[#mapx+1] = Loader._compactTileLayer(layer)
+            
+        -- <objectgroup>
+        elseif layer.class == "ObjectLayer" then
+            mapx[#mapx+1] = Loader._compactObjectLayer(layer)
+            
+        -- <custom>
+        elseif layer.encode then
+            local layerx = {label="customlayer", xarg={name=layer.name}}
+            layerx[1] = {label="data", xarg={}}
+            layerx[1][1] = layer:encode()
+            mapx[#mapx+1] = layerx
+        end
+        
+    end
+    
+    -- <properties>
+    mapx[#mapx+1] = Loader._compactProperties(map.properties)
+    return mapx
+end
+
+----------------------------------------------------------------------------------------------------
+-- Compact a TileSet
+function Loader._compactTileSet(tileset)
+
+    -- <Tileset>
+    local tilesetx = {label="tileset", xarg = {
+        firstgid = tileset.firstgid,
+        name = tileset.name,
+        tilewidth = tileset.tileWidth,
+        tileheight = tileset.tileHeight,
+        spacing = tileset.spacing,
+        margin = tileset.margin
+    }}
+    
+    -- <image>
+    tilesetx[1] = {label="image", xarg = {
+        source = tileset.imagePath,
+        trans = tileset.trans,
+    }}
+    
+    -- <tileoffset>
+    if tileset.offsetX ~= 0 or tileset.offsetY ~= 0 then
+        tilesetx[2] =  {label="tileoffset", xarg={x = tileset.offsetX, y = tileset.offsetY}}
+    end
+    
+    -- <properties>
+    if next(tileset.properties) then
+        tilesetx[#tilesetx+1] = Loader._compactProperties(tileset.properties)
+    end
+    
+    -- <tile>
+    if  next(tileset.tileProperties) then
+        local tilex
+        for k,props in pairs(tileset.tileProperties) do
+            tilex = {label = "tile", xarg = {id = k - tileset.firstgid}}
+            tilex[1] = Loader._compactProperties(props)
+            tilesetx[#tilesetx+1] = tilex
+        end
+    end
+    
+    return tilesetx
+end
+
+----------------------------------------------------------------------------------------------------
+-- Compact a TileLayer
+function Loader._compactTileLayer(layer)
+
+    -- Set the ATL properties
+    layer.properties.atl_useSpriteBatch = layer.useSpriteBatch
+    layer.properties.atl_parallaxX = layer.parallaxX
+    layer.properties.atl_parallaxY = layer.parallaxY
+    layer.properties.atl_offsetX = layer.offsetX
+    layer.properties.atl_offsetY = layer.offsetY
+
+    -- <layer>
+    local layerx = {label="layer", xarg = {
+        name = layer.name,
+        opacity = layer.opacity,
+        visible = layer.visible and 1 or 0,
+        width = layer.map.width,
+        height = layer.map.height,
+    }}
+    
+    -- <data>
+    local tiles = {}
+    local flipBits = 2^29
+    local flipValue 
+    for x, y, tile in layer:rectangle(0, 0, layer.map.width-1, layer.map.height-1, true) do
+        if tile then
+            flipValue = layer._flippedTiles(x,y) and layer._flippedTiles(x,y) * flipBits or 0
+            tiles[#tiles+1] = tile.id + flipValue
+        else
+            tiles[#tiles+1] = 0
+        end
+    end
+    layerx[1] = {label ="data", xarg = {encoding="csv"}, [1] = table.concat(tiles,",")}
+
+    -- <properties>
+    if next(layer.properties) then
+        layerx[2] = Loader._compactProperties(layer.properties)
+    end
+
+    return layerx
+end
+
+----------------------------------------------------------------------------------------------------
+-- Compact an ObjectLayer
+function Loader._compactObjectLayer(layer)
+
+    -- <objectgroup>
+    local layerx = {label = "objectgroup", xarg = {
+        name = layer.name,
+        color = string.format("#%x%x%x", layer.color[1], layer.color[2], layer.color[3]),
+        opacity = layer.opacity,
+        visible = layer.visible and 1 or 0,
+    }}
+    
+    -- <object>
+    for i = 1,#layer.objects do
+        object = layer.objects[i]
+        layerx[#layerx+1] = Loader._compactObject(layer.objects[i])
+    end
+
+    -- <properties>
+    if next(layer.properties) then
+        layerx[#layerx+1] = Loader._compactProperties(layer.properties)
+    end
+
+    return layerx
+end
+
+----------------------------------------------------------------------------------------------------
+-- Compact an Object
+function Loader._compactObject(object)
+
+    -- <object>
+    local objectx = {label="object", xarg = {
+        name = object.name,
+        type = object.type,
+        x = object.x,
+        y = object.y,
+        width = object.width,
+        height = object.height,
+        gid = object.gid,
+        visible = object.visible,
+    }}
+    
+    -- <polyline>
+    if object.polyline then
+        local polylinex = {label="polyline", xarg = {}}
+        local points = {}
+        for i = 1,#object.polyline,2 do
+            points[#points+1] = object.polyline[i] .. "," .. object.polyline[i+1]
+        end
+        polylinex.xarg.points = table.concat(points, " ")
+        objectx[#objectx+1] = polylinex
+    end
+    
+    -- <polygon>
+    if object.polygon then
+        local polygonx = {label="polygon", xarg = {}}
+        local points = {}
+        for i = 1,#object.polygon,2 do
+            points[#points+1] = object.polygon[i] .. "," .. object.polygon[i+1]
+        end
+        polygonx.xarg.points = table.concat(points, " ")
+        objectx[#objectx+1] = polygonx
+    end
+
+    -- <properties>
+    if next(object.properties) then
+        objectx[#objectx+1] = Loader.compactProperties(object.properties)
+    end
+    
+    return objectx
+end
+
+----------------------------------------------------------------------------------------------------
+-- Compact Properties
+function Loader._compactProperties(prop)
+    
+    if not prop then return end
+    
+    -- <properties>
+    local propx = {label="properties", xarg={}}
+    
+    -- <property>
+    for k,v in pairs(prop) do
+        propx[#propx+1] = {label="property", xarg={name=k, value=v}}
+    end
+
+    return propx
+end
+
+----------------------------------------------------------------------------------------------------
+-- Return the loader
+return Loader
+
+
+--[[Copyright (c) 2011-2012 Casey Baxter
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+Except as contained in this notice, the name(s) of the above copyright holders
+shall not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.--]]
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AdvTiledLoader/Map.lua	Sat Dec 15 13:10:41 2012 +0100
@@ -0,0 +1,357 @@
+---------------------------------------------------------------------------------------------------
+-- -= Map =-
+---------------------------------------------------------------------------------------------------
+-- Setup
+
+-- Import the other classes
+TILED_LOADER_PATH = TILED_LOADER_PATH or ({...})[1]:gsub("[%.\\/][Mm]ap$", "") .. '.'
+local Tile = require( TILED_LOADER_PATH .. "Tile")
+local TileSet = require( TILED_LOADER_PATH .. "TileSet")
+local TileLayer = require( TILED_LOADER_PATH .. "TileLayer")
+local Object = require( TILED_LOADER_PATH .. "Object")
+local ObjectLayer = require( TILED_LOADER_PATH .. "ObjectLayer")
+
+-- Localize some functions so they are faster
+local love = love
+local table = table
+local ceil = math.ceil
+local floor = math.floor
+
+-- Make our map class
+local Map = {class = "Map"}
+Map.__index = Map
+
+---------------------------------------------------------------------------------------------------
+-- Returns a new map
+function Map:new(name, width, height, tileWidth, tileHeight, orientation, path, prop)
+
+    -- Our map
+    local map = setmetatable({}, Map)
+    prop = prop or {}
+    
+    -- Public:
+    map.name = name or "Unnamed Map"                        -- Name of the map
+    map.width = width or 0                                  -- Width of the map in tiles
+    map.height = height or 0                                -- Height of the map in tiles
+    map.tileWidth = tileWidth or 0                          -- Width in pixels of each tile
+    map.tileHeight = tileHeight or 0                        -- Height in pixels of each tile
+    map.orientation = orientation or "orthogonal"           -- Type of map. orthogonal or isometric
+    map.properties = prop or {}                             -- Properties of the map set by Tiled
+    map.useSpriteBatch = prop.atl_useSpriteBatch            -- True = TileLayers use sprite batches
+    map.visible = true                                      -- False = the map will not be drawn
+    map.drawObjects = prop.atl_drawObjects                  -- True = object layers will be drawn
+    
+    map.viewX = prop.atl_viewX or 0                         -- X coord of the viewing screen. 
+    map.viewY = prop.atl_viewY or 0                         -- Y coord of the viewing screen. 
+    map.viewW = prop.atl_viewW or love.graphics.getWidth()  -- The width of the viewing screen
+    map.viewH = prop.atl_viewH or love.graphics.getHeight() -- The height of the viewing screen
+    map.viewScaling = 1                                     -- The game scaling
+    map.viewPadding = 10                                    -- Padding for the view
+    
+    map.offsetX = prop.atl_offsetX or 0  -- Drawing offset X
+    map.offsetY = prop.atl_offsetY or 0  -- Drawing offset Y
+    
+    map.layers  = {}            -- Layers of the map indexed by name
+    map.tilesets = {}           -- Tilesets indexed by name
+    map.tiles = {}              -- Tiles indexed by id
+    map.layerOrder = {}         -- The order of the layers. Callbacks are called in this order.
+
+    -- Private:
+    map._widestTile = 0                 -- The widest tile on the map.
+    map._highestTile = 0                -- The tallest tile on the map.
+    map._forceRedraw = false            -- If true then the next redraw is forced
+    map._previousUseSpriteBatch = false -- The previous useSpiteBatch.
+    map._tileClipboard  =   nil         -- The value that stored for tile copying and pasting.
+    map._directory = path               -- The directory the map is in
+    
+    -- Return the new map
+    return map
+end
+
+---------------------------------------------------------------------------------------------------
+-- Creates a new tileset and adds it to the map. The map will then auto-update its tiles.
+function Map:newTileSet(...)
+    if not name then
+        error("Map:newTileSet - The name parameter is invalid")
+    end
+    self.tilesets[name] = TileSet:new(...)
+    self:updateTiles()
+    return self.tilesets[name]
+end
+
+---------------------------------------------------------------------------------------------------
+-- Creates a new TileLayer and adds it to the map. The position parameter is the position to insert
+-- the layer into the layerOrder.
+function Map:newTileLayer(position, ...)
+    if self.layers[name] then 
+        error( string.format("Map:newTileLayer - The layer name \"%s\" already exists.", name) )
+    end
+    self.layers[name] = TileLayer:new(self, ...)
+    table.insert(self.layerOrder, position or #self.layerOrder + 1, self.layers[name])
+    return self.layers[name]
+end
+
+---------------------------------------------------------------------------------------------------
+-- Creates a new ObjectLayer and inserts it into the map
+function Map:newObjectLayer(position, ...)
+    if self.layers[name] then 
+        error( string.format("Map:newObjectLayer - The layer name \"%s\" already exists.", name) )
+    end
+    self.layers[name] = ObjectLayer:new(self, ...)
+    table.insert(self.layerOrder, position or #self.layerOrder + 1, self.layers[name])
+    return self.layers[name]
+end
+
+---------------------------------------------------------------------------------------------------
+-- Add a custom layer to the map. You can include a predefined layer table or one will be created.
+function Map:newCustomLayer(name, position, layer)
+    if self.layers[name] then 
+        error( string.format("Map:newCustomLayer - The layer name \"%s\" already exists.", name) )
+    end
+    self.layers[name] = layer or {name=name}
+    self.layers[name].class = "CustomLayer"
+    table.insert(self.layerOrder, position or #self.layerOrder + 1, self.layers[name])
+    return self.layers[name]
+end
+
+---------------------------------------------------------------------------------------------------
+-- Cuts tiles out of tilesets and stores them in the tiles tables under their id
+-- Call this after the tilesets are set up
+function Map:updateTiles()
+    self.tiles = {}
+    self._widestTile = 0
+    self._highestTile = 0
+    for _, ts in pairs(self.tilesets) do
+        if ts.tileWidth > self._widestTile then self._widestTile = ts.tileWidth end
+        if ts.tileHeight > self._highestTile then self._highestTile = ts.tileHeight end
+        for id, val in pairs(ts:getTiles()) do
+            self.tiles[id] = val
+        end
+    end
+end
+
+---------------------------------------------------------------------------------------------------
+-- Forces the map to redraw the sprite batches.
+function Map:forceRedraw()
+    self._forceRedraw = true
+end
+
+---------------------------------------------------------------------------------------------------
+-- Performs a callback on all map layers.
+local layer
+function Map:callback(cb, ...)
+    if cb == "draw" then self:_updateTileRange() end
+    for i = 1, #self.layerOrder do
+        layer = self.layerOrder[i]
+        if layer[cb] then layer[cb](layer, ...) end
+    end
+end
+
+---------------------------------------------------------------------------------------------------
+-- Draw the map.
+function Map:draw()
+    if self.visible then self:callback("draw") end
+end
+
+---------------------------------------------------------------------------------------------------
+-- Returns the position of the layer inside the map's layerOrder. Can be the layer name or table.
+function Map:layerPosition(layer)
+    if type(layer) == "string" then layer = self.layers[layer] end
+    for i = 1,#self.layerOrder do
+        if self.layerOrder[i] == layer then return i end
+    end
+end
+
+---------------------------------------------------------------------------------------------------
+-- Returns the position of the layer inside the map's layerOrder. The passed layers can be the 
+-- layer name, the layer tables themselves, or the layer positions.
+function Map:swapLayers(layer1, layer2)
+    if type(layer1) ~= "number" then layer1 = self:layerPosition(layer1) end
+    if type(layer2) ~= "number" then layer2 = self:layerPosition(layer2) end
+    local bubble = self.layers[layer1]
+    self.layers[layer1] = self.layers[layer2]
+    self.layers[layer2] = bubble
+end
+
+---------------------------------------------------------------------------------------------------
+-- Removes a layer from the map
+function Map:removeLayer(layer)
+    if type(layer) ~= "number" then layer = self:layerPosition(layer) end
+    layer = table.remove(self.layerOrder, layer)
+    self.layers[layer.name] = nil
+    return layer
+end
+
+---------------------------------------------------------------------------------------------------
+-- Turns an isometric location into a world location. The unit length for isometric tiles is always
+-- the map's tileHeight. This is both for width and height.
+local h, tw, th
+function Map:fromIso(x, y)
+    h, tw, th = self.height, self.tileWidth, self.tileHeight
+    return ((x-y)/th + h - 1)*tw/2, (x+y)/2
+end
+
+---------------------------------------------------------------------------------------------------
+-- Turns a world location into an isometric location
+local ix, iy
+function Map:toIso(a, b)
+    a, b = a or 0, b or 0
+    h, tw, th = self.height, self.tileWidth, self.tileHeight
+    ix = b - (h-1)*th/2 + (a*th)/tw 
+    iy = 2*b - ix
+    return ix, iy
+end
+
+---------------------------------------------------------------------------------------------------
+-- Sets the draw range
+function Map:setDrawRange(x,y,w,h)
+    self.viewX, self.viewY, self.viewW, self.viewH = x, y, w, h
+    self.viewPadding = 0
+    self.viewScaling = 1
+end
+
+---------------------------------------------------------------------------------------------------
+-- Automatically sets the draw range to fit the display
+function Map:autoDrawRange(tx, ty, scale, pad)
+    tx, ty, scale, pad = tx or 0, ty or 0, scale or 1, pad or 0
+    self.viewX = -tx
+    self.viewY = -ty
+    self.viewW = love.graphics.getWidth()
+    self.viewH = love.graphics.getHeight()
+    self.viewScaling = scale > 0.001 and scale or 0.001
+    self.viewPadding = pad
+end
+
+---------------------------------------------------------------------------------------------------
+-- Returns the normal draw range
+function Map:getDrawRange()
+    return self.viewX - self.viewPadding, self.viewY - self.viewPadding,
+            self.viewW/self.viewScaling + self.viewPadding*2, 
+            self.viewH/self.viewScaling + self.viewPadding*2
+end
+
+----------------------------------------------------------------------------------------------------
+-- Private Functions
+----------------------------------------------------------------------------------------------------
+-- This is an internal function used to update the map's _tileRange, _previousTileRange, and 
+-- _specialRedraw
+local x1, y1, x2, y2, highOffset, widthOffset, tr, ptr, layer
+function Map:_updateTileRange()
+    
+    -- Offset to make sure we can always draw the highest and widest tile
+    heightOffset = self._highestTile - self.tileHeight
+    widthOffset = self._widestTile - self.tileWidth
+    
+    -- Go through each layer
+    for i = 1,#self.layerOrder do
+        layer = self.layerOrder[i]
+
+        -- If the layer is a TileLayer
+        if layer.class == "TileLayer" then
+        
+            -- Get the draw range. 
+            x1 = self.viewX * layer.parallaxX - self.viewPadding + layer.offsetX
+            y1 = self.viewY * layer.parallaxY - self.viewPadding + layer.offsetY
+            x2 = self.viewW/self.viewScaling + self.viewPadding*2
+            y2 = self.viewH/self.viewScaling + self.viewPadding*2
+            
+            -- Apply the offset
+            x1 = x1 - self.offsetX - layer.offsetX
+            y1 = y1 - self.offsetY - layer.offsetY
+        
+            -- Calculate the _tileRange for orthogonal tiles
+            if self.orientation == "orthogonal" then
+        
+                -- Limit the drawing range. We must make sure we can draw the tiles that are bigger
+                -- than the self's tileWidth and tileHeight.
+                if x1 and y1 and x2 and y2 then
+                    x2 = ceil(x2/self.tileWidth)
+                    y2 = ceil((y2+heightOffset)/self.tileHeight)
+                    x1 = floor((x1-widthOffset)/self.tileWidth)
+                    y1 = floor(y1/self.tileHeight)
+            
+                    -- Make sure that we stay within the boundry of the map
+                    x1 = x1 > 0 and x1 or 0
+                    y1 = y1 > 0 and y1 or 0
+                    x2 = x2 < self.width and x2 or self.width - 1
+                    y2 = y2 < self.height and y2 or self.height - 1
+            
+                else
+                    -- If the drawing range isn't defined then we draw all the tiles
+                    x1, y1, x2, y2 = 0, 0, self.width-1, self.height-1
+                end
+            
+            -- Calculate the _tileRange for isometric tiles.
+            else
+                -- If the drawRange is set
+                if x1 and y1 and x2 and y2 then
+                    x1, y1 = self:toIso(x1-self._widestTile,y1)
+                    x1, y1 = ceil(x1/self.tileHeight), ceil(y1/self.tileHeight)-1
+                    x2 = ceil((x2+self._widestTile)/self.tileWidth)
+                    y2 = ceil((y2+heightOffset)/self.tileHeight)
+                -- else draw everything
+                else
+                    x1 = 0
+                    y1 = 0
+                    x2 = self.width - 1
+                    y2 = self.height - 1
+                end
+            end
+        
+            -- Assign the new values to the tile range
+            tr, ptr = layer._tileRange, layer._previousTileRange
+            ptr[1], ptr[2], ptr[3], ptr[4] = tr[1], tr[2], tr[3], tr[4]
+            tr[1], tr[2], tr[3], tr[4] =  x1, y1, x2, y2
+        
+            -- If the tile range or useSpriteBatch is different than the last frame then we need to 
+            -- update its sprite batches.
+            layer._redraw = self.useSpriteBatch ~= self._previousUseSpriteBatch or 
+                            self._forceRedraw or
+                            tr[1] ~= ptr[1] or 
+                            tr[2] ~= ptr[2] or 
+                            tr[3] ~= ptr[3] or 
+                            tr[4] ~= ptr[4]
+        end
+    end
+    
+    -- Set the previous useSpritebatch
+    self._previousUseSpriteBatch = self.useSpriteBatch
+                          
+    -- Reset the forced special redraw
+    self._forceRedraw = false
+end
+
+---------------------------------------------------------------------------------------------------
+-- Calling the map as a function will return the layer
+function Map:__call(layerName)
+    return self.layers[layerName]
+end
+
+---------------------------------------------------------------------------------------------------
+-- Returns the Map class
+return Map
+
+
+--[[Copyright (c) 2011-2012 Casey Baxter
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+Except as contained in this notice, the name(s) of the above copyright holders
+shall not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.--]]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AdvTiledLoader/Object.lua	Sat Dec 15 13:10:41 2012 +0100
@@ -0,0 +1,239 @@
+---------------------------------------------------------------------------------------------------
+-- -= Object =-
+---------------------------------------------------------------------------------------------------
+-- Setup
+local Object = {class = "Object"}
+Object.__index = Object
+
+---------------------------------------------------------------------------------------------------
+
+-- Returns a new Object
+function Object:new(layer, name, type, x, y, width, height, gid, prop)
+
+    -- Public:
+    local obj = setmetatable({}, Object)
+    obj.layer = layer           -- The layer that the object belongs to
+    obj.name = name or ""       -- Name of the object
+    obj.type = type or ""       -- Type of the object
+    obj.x = x or 0              -- X location on the map
+    obj.y = y  or 0             -- Y location on the map
+    obj.width = width or 0      -- Object width in tiles
+    obj.height = height or 0    -- Object height in tiles
+    obj.gid = gid               -- The object's associated tile. If false an outline will be drawn.
+    obj.visible = true          -- If false then the object will not be drawn
+    obj.properties = prop or {} -- Properties set by tiled.
+    obj.polygon = nil           -- Polygon points. Set by the Loader if defined in Tiled.
+    obj.polyline = nil          -- Polyline points. Set by the Loader if defined in Tiled.
+    
+    -- drawInfo stores values needed to actually draw the object. You can either set these yourself
+    -- or use updateDrawInfo to do it automatically.
+    obj.drawInfo = {
+    
+        -- x and y are the drawing location of the object. This is different than the object's x and
+        -- y value which is the object's placement on the map.
+        x = 0,      -- The x draw location
+        y = 0,      -- The y draw location
+        
+        -- These limit the drawing of the object. If the object falls out of the bounds of
+        -- the map's drawRange then the object will not be drawn. 
+        left = 0,   -- The leftmost point on the object
+        right = 0,  -- The rightmost point on the object
+        top = 0,    -- The highest point on the object
+        bottom = 0, -- The lowest point on the object
+        
+        -- The order to draw the object in relation to other objects. Usually equal to bottom.
+        order = 0,
+        
+        -- In addition to this, other drawing information can be stored in the numerical 
+        -- indicies which is context sensitive to the map's orientation, if the object has a gid, or
+        -- of the object is a polygon or polyline object.
+    } 
+    
+    -- Update the draw info
+    Object.updateDrawInfo(obj)
+    
+    -- Return our object
+    return obj
+end
+
+---------------------------------------------------------------------------------------------------
+-- Updates the draw information. Call this every time the object moves or changes size.
+function Object:updateDrawInfo()
+    local di = self.drawInfo
+    local map = self.layer.map
+        
+    if self.polygon or self.polyline then
+        -- Reset the draw info
+        self.drawInfo = {}
+        di = self.drawInfo
+        -- Set the box to the first vertex
+        local vertexes = self.polygon or self.polyline
+        -- Create the draw information for each vertex
+        for k,v in ipairs(vertexes) do
+            if k%2 == 1 then
+                if map.orientation == "isometric" then 
+                    di[k], di[k+1] = map:fromIso(self.x+vertexes[k], self.y+vertexes[k+1])
+                else
+                    di[k], di[k+1] = self.x+vertexes[k], self.y+vertexes[k+1]
+                end
+                di[k], di[k+1] = di[k] - map.offsetX, di[k+1] - map.offsetY
+            end
+        end
+        -- Set the start draw location
+        di.x, di.y = di[1], di[2]
+        -- Prime the bounds
+        di.left, di.right, di.top, di.bottom = di.x, di.x, di.y, di.y
+        -- Go through each vertex and find the highest and lowest values for the bounds.
+        for k,v in ipairs(di) do
+            -- if it's odd then it's an x value
+            if k%2 == 1 then
+                if v < di.left then di.left = v end
+                if v > di.right then di.right = v end
+            -- if it's even it's a y value
+            else
+                if v < di.top then di.top = v end
+                if v > di.bottom then di.bottom = v end
+            end
+        end
+        di.order = di.bottom
+    
+    -- Isometric map
+    elseif map.orientation == "isometric" then
+        -- Is a tile object
+        if self.gid then
+            local t = map.tiles[self.gid]
+            local tw, th = t.width, t.height
+            di.x, di.y  = map:fromIso(self.x, self.y)
+            di.x, di.y = di.x - map.offsetX, di.y - map.offsetY
+            di.order = di.y
+            di.x, di.y = di.x - map.tileWidth/2, di.y - th
+            di.left, di.right, di.top, di.bottom = di.x, di.x+tw, di.y , di.y +th
+        -- Is not a tile object
+        else
+            di[1], di[2] = map:fromIso(self.x, self.y)
+            di[3], di[4] = map:fromIso(self.x + self.width, self.y)
+            di[5], di[6] = map:fromIso(self.x + self.width, self.y + self.height)
+            di[7], di[8] = map:fromIso(self.x, self.y + self.height)
+            for i =1,7,2 do
+                di[i] = di[i] - map.offsetX
+                di[i+1] = di[i+1] - map.offsetY
+            end
+            di.left, di.right, di.top, di.bottom = di[7], di[3], di[2], di[6]
+            di.order = 1
+        end
+        
+    -- Orthogonal map
+    else
+        -- Is a tile object
+        if self.gid then
+            local t = map.tiles[self.gid]
+            local tw, th = t.width, t.height
+            di.x, di.y = self.x - map.offsetX, self.y - map.offsetY
+            di.order = di.y
+            di.y = di.y - th
+            di.left, di.top, di.right, di.bottom = di.x, di.y, di.x+tw, di.y+th
+        -- Is not a tile object
+        else
+            di.x, di.y = self.x - map.offsetX, self.y - map.offsetY
+            di[1], di[2] = di.x, di.y
+            di[3], di[4] = self.width > 20 and self.width or 20, self.height > 20 and self.height or 20
+            di.left, di.top, di.right, di.bottom = di.x, di.y , di.x+di[3], di.y +di[4]
+            di.order = 1
+        end
+    end
+end
+
+---------------------------------------------------------------------------------------------------
+-- Draw the object. The passed color is the color of the object layer the object belongs to.
+function Object:draw(x, y, r, g, b, a)
+
+    if not self.visible then return end
+    
+    local di = self.drawInfo
+    love.graphics.setLineWidth(2)
+
+    -- The object is a polyline.
+    if self.polyline then
+        love.graphics.push()
+        love.graphics.translate(0,1)
+        love.graphics.setColor(0, 0, 0, a)
+        love.graphics.line( unpack(di) )
+        love.graphics.pop()
+        love.graphics.setColor(r, g, b, a)
+        love.graphics.line( unpack(di) )
+        
+    -- The object is a polygon.
+    elseif self.polygon then
+        love.graphics.push()
+        love.graphics.translate(0,1)
+        love.graphics.setColor(0, 0, 0, a)
+        love.graphics.polygon( "line", unpack(di) )
+        love.graphics.pop()
+        love.graphics.setColor(r,g,b,a)
+        love.graphics.polygon( "line", unpack(di) ) 
+        
+    -- The object is a tile object. Draw the tile.
+    elseif self.gid then
+        local tile = self.layer.map.tiles[self.gid]
+        tile:draw(di.x, di.y - tile.height)
+        
+    -- Map is isometric. Draw a parallelogram.
+    elseif self.layer.map.orientation == "isometric" then
+        love.graphics.setColor(r, g, b, a/5)
+        love.graphics.polygon("fill", unpack(di))
+        
+        love.graphics.push()
+        love.graphics.translate(0,1)
+        love.graphics.setColor(0, 0, 0, a)
+        love.graphics.polygon("line", unpack(di))
+        love.graphics.pop()
+            
+        love.graphics.setColor(r,g,b,a)
+        love.graphics.polygon("line", unpack(di))
+
+    -- Map is orthogonal. Draw a rectangle.
+    else
+        love.graphics.setColor(r, g, b, a/5)
+        love.graphics.rectangle("fill", unpack(di))
+            
+        love.graphics.setColor(0, 0, 0, a)
+        love.graphics.push()
+        love.graphics.translate(1,1)
+        love.graphics.rectangle("line", unpack(di))
+        love.graphics.print(self.name, di.x, di.y-20)
+        love.graphics.pop()
+            
+        love.graphics.setColor(r,g,b,a)
+        love.graphics.rectangle("line", unpack(di))
+        love.graphics.print(self.name, di.x, di.y-20)
+    end
+end
+
+---------------------------------------------------------------------------------------------------
+-- Returns the Object class
+return Object
+
+
+--[[Copyright (c) 2011-2012 Casey Baxter
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+Except as contained in this notice, the name(s) of the above copyright holders
+shall not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.--]]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AdvTiledLoader/ObjectLayer.lua	Sat Dec 15 13:10:41 2012 +0100
@@ -0,0 +1,141 @@
+---------------------------------------------------------------------------------------------------
+-- -= ObjectLayer =-
+---------------------------------------------------------------------------------------------------
+-- Setup
+TILED_LOADER_PATH = TILED_LOADER_PATH or ({...})[1]:gsub("[%.\\/][Oo]bject[Ll]ayer$", "") .. '.'
+local love = love
+local unpack = unpack
+local pairs = pairs
+local ipairs = ipairs
+local Object = require( TILED_LOADER_PATH .. "Object")
+local ObjectLayer = {class = "ObjectLayer"}
+local grey = {128,128,128,255}
+ObjectLayer.__index = ObjectLayer
+
+---------------------------------------------------------------------------------------------------
+-- Creates and returns a new ObjectLayer
+function ObjectLayer:new(map, name, color, opacity, prop)
+    
+    -- Create a new table for our object layer and do some error checking.
+    local layer = setmetatable({}, ObjectLayer)
+    
+    layer.map = map                             -- The map this layer belongs to
+    layer.name = name or "Unnamed ObjectLayer"  -- The name of this layer
+    layer.color = color or grey                 -- The color theme
+    layer.opacity = opacity or 1                -- The opacity
+    layer.objects = {}                          -- The layer's objects indexed numerically
+    layer.properties = prop or {}               -- Properties set by Tiled.
+    layer.visible = true                        -- If false then the layer will not be drawn
+    
+    -- Return the new object layer
+    return layer
+end
+
+---------------------------------------------------------------------------------------------------
+-- Creates a new object, automatically inserts it into the layer, and then returns it
+function ObjectLayer:newObject(name, type, x, y, width, height, gid, prop)
+    local obj = Object:new(self, name, type, x, y, width, height, gid, prop)
+    self.objects[#self.objects+1] = obj
+    return obj
+end
+
+---------------------------------------------------------------------------------------------------
+-- Sorting function for objects. We'll use this below in ObjectLayer:draw()
+local function drawSort(o1, o2) 
+    return o1.drawInfo.order < o2.drawInfo.order 
+end
+
+---------------------------------------------------------------------------------------------------
+-- Draws the object layer. The way the objects are drawn depends on the map orientation and
+-- if the object has an associated tile. It tries to draw the objects as closely to the way
+-- Tiled does it as possible.
+local di, dr, drawList, r, g, b, a, line, obj, offsetX, offsetY
+function ObjectLayer:draw()
+
+    -- Early exit if the layer is not visible.
+    if not self.visible then return end
+
+    -- Exit if objects are not suppose to be drawn
+    if not self.map.drawObjects then return end
+
+    di = nil                            -- The draw info
+    dr = {self.map:getDrawRange()}      -- The drawing range. [1-4] = x, y, width, height
+    drawList = {}                       -- A list of the objects to be drawn
+    r,g,b,a = 255, 255, 255, 255 ---love.graphics.getColor()    -- Save the color so we can set it back at the end
+    line = love.graphics.getLineWidth() -- Save the line width too
+    self.color[4] = 255 * self.opacity  -- Set the opacity
+    
+    -- Put only objects that are on the screen in the draw list. If the screen range isn't defined
+    -- add all objects
+    for i = 1, #self.objects do
+        obj = self.objects[i]
+        obj:updateDrawInfo()
+        di = obj.drawInfo
+        if dr[1] and dr[2] and dr[3] and dr[4] then
+            if  di.right > dr[1]-20 and 
+                di.bottom > dr[2]-20 and 
+                di.left < dr[1]+dr[3]+20 and 
+                di.top < dr[2]+dr[4]+20 then 
+                    drawList[#drawList+1] = obj
+            end
+        else
+            drawList[#drawList+1] = obj
+        end
+    end
+    
+    -- Sort the draw list by the object's draw order
+    table.sort(drawList, drawSort)
+
+    -- Draw all the objects in the draw list.
+    offsetX, offsetY = self.map.offsetX, self.map.offsetY
+    for i = 1, #drawList do 
+        obj = drawList[i]
+        love.graphics.setColor(r,b,g,a)
+        drawList[i]:draw(di.x, di.y, unpack(self.color or neutralColor))
+    end
+    
+    -- Reset the color and line width
+    love.graphics.setColor(r,b,g,a)
+    love.graphics.setLineWidth(line)
+end
+
+---------------------------------------------------------------------------------------------------
+-- Changes an object layer into a custom layer. A function can be passed to convert objects.
+function ObjectLayer:toCustomLayer(convert)
+    if convert then
+        for i = 1, #self.objects do
+            self.objects[i] = convert(self.objects[i])
+        end
+    end
+    self.class = "CustomLayer"
+    return setmetatable(self, nil)
+end
+
+---------------------------------------------------------------------------------------------------
+-- Return the ObjectLayer class
+return ObjectLayer
+
+
+--[[Copyright (c) 2011-2012 Casey Baxter
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+Except as contained in this notice, the name(s) of the above copyright holders
+shall not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.--]]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AdvTiledLoader/README.md	Sat Dec 15 13:10:41 2012 +0100
@@ -0,0 +1,83 @@
+#Advanced Tiled Loader
+========
+Advanced Tiled Loader (ATL) loads and renders [Tiled](http://www.mapeditor.org/) maps inside of the [Löve2D](http://love2d.org) game framework.
+
+Supported features include:
+* Multiple Layers
+* All object types (regular, polygon, and tile)
+* Properties
+* Transparent colors
+* Margins and spacing
+* External tilesets
+* zlib/gzip compression
+* Isometric maps
+* Flipped and rotated tiles
+* Saving loaded maps
+
+Example files can be found at https://github.com/Kadoba/Advanced-Tiled-Loader-Examples
+
+----------------------------------------------------------------------------------------------------
+# Quick Example
+  
+```lua     
+
+-- Gets the loader
+loader = require("AdvTiledLoader/loader.lua")
+
+ -- Path to the tmx files. The file structure must be similar to how they are saved in Tiled
+loader.path = "maps/"
+
+ -- Loads the map file and returns it
+map = loader.load("desert.tmx")
+
+-- Draws the map
+map:draw()
+
+-- Limits the drawing range of the map. Important for performance
+map:setDrawRange(0,0,love.graphics.getWidth(), love.graphics.getHeight())
+
+-- Automatically sets the drawing range to the size of the screen.
+map:autoDrawRange(tx, ty, scale, padding)
+
+-- Accessing individual layers
+map.layers["layer name"]
+
+-- A shortcut for accessing specific layers
+map("layer name")
+
+-- Finding a specific tile
+map.layers["layer name"]:get(5,5)
+
+-- A shortcut for finding a specific tile
+map("layer name")(5,5)
+
+-- Iterating over all tiles in a layer
+for x, y, tile in map("layer name"):iterate() do
+   print( string.format("Tile at (%d,%d) has an id of %d", x, y, tile.id) )
+end
+
+-- Iterating over all objects in a layer
+for i, obj in pairs( map("object layer").objects ) do
+	print( "Hi, my name is " .. obj.name )
+end
+
+-- Find all objects of a specific type in all layers
+for _, layer in pairs(map.layers) do
+   if layer.class == "ObjectLayer" then
+		for _, obj in pairs(player.objects) do
+			if obj.type == "enemy" then print(obj.name) end
+		end
+   end
+end
+
+-- draw the tile with the id 4 at (100,100)
+map.tiles[4]:draw(100,100)
+
+-- Access the tile's properties set by Tiled
+map.tiles[4].properties
+
+-- Turns off drawing of non-tiled objects.
+map.drawObjects = false
+
+```
+----------------------------------------------------------------------------------------------------
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AdvTiledLoader/Tile.lua	Sat Dec 15 13:10:41 2012 +0100
@@ -0,0 +1,57 @@
+---------------------------------------------------------------------------------------------------
+-- -= Tile =-
+---------------------------------------------------------------------------------------------------
+
+-- Setup
+local assert = assert
+local Tile = {class = "Tile"}
+Tile.__index = Tile
+
+-- Creates a new tile and returns it.
+function Tile:new(id, tileset, quad, width, height, prop)
+    if not id or not tileset or not quad then
+        error("Tile:new - Needs at least 3 parameters for id, tileset and quad.")
+    end
+    local tile = setmetatable({}, Tile)
+    tile.id = id                    -- The id of the tile
+    tile.tileset = tileset          -- The tileset this tile belongs to
+    tile.quad = quad                -- The of the tileset that defines the tile
+    tile.width = width or 0         -- The width of the tile in pixels
+    tile.height = height or 0       -- The height of the tile in pixels
+    tile.properties = prop or {}    -- The properties of the tile set in Tiled
+    return tile
+end
+
+-- Draws the tile at the given location 
+function Tile:draw(x, y, rotation, scaleX, scaleY, offsetX, offsetY)
+    love.graphics.drawq(self.tileset.image, self.quad, self.tileset.offsetX + x, 
+                        self.tileset.offsetY + y, rotation, scaleX, scaleY, offsetX, offsetY)
+end
+
+-- Return the Tile class
+return Tile
+
+
+--[[Copyright (c) 2011-2012 Casey Baxter
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+Except as contained in this notice, the name(s) of the above copyright holders
+shall not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.--]]
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AdvTiledLoader/TileLayer.lua	Sat Dec 15 13:10:41 2012 +0100
@@ -0,0 +1,448 @@
+---------------------------------------------------------------------------------------------------
+-- -= TileLayer =-
+---------------------------------------------------------------------------------------------------
+-- Setup
+TILED_LOADER_PATH = TILED_LOADER_PATH or ({...})[1]:gsub("[%.\\/][Tt]ile[Ll]ayer$", "") .. '.'
+local math = math
+local type = type
+local love = love
+local Grid = require(TILED_LOADER_PATH .. "Grid")
+local TileLayer = {class = "TileLayer"}
+TileLayer.__index = function(t, i) return TileLayer[i] or Grid[i] end
+TileLayer.__call = Grid.__call
+
+----------------------------------------------------------------------------------------------------
+-- Returns a new TileLayer
+function TileLayer:new(map, name, opacity, prop)
+    if not map or not name then 
+        error("TileLayer:new - Needs at least 2 parameters for the map and name.")
+    end
+    
+    local tl = setmetatable(Grid:new(), TileLayer)
+    
+    prop = prop or {}
+    
+    -- Public:
+    tl.name = name                              -- The name of the tile layer
+    tl.map = map                                -- The map that this layer belongs to
+    tl.opacity = opacity or 1                   -- The opacity to draw the tiles (0-1)
+    tl.properties = prop                        -- Properties set by Tiled
+    tl.visible = true                           -- If false then the layer will not be drawn
+    
+    tl.useSpriteBatch = prop.alt_useSpriteBatch -- If true then the layer is rendered with sprite 
+                                                -- batches. If false then the layer will not use 
+                                                -- sprite batches. If nil then map.useSpriteBatch 
+                                                -- will be used. 
+    
+    tl.parallaxX = prop.atl_parallaxX or 1      -- The horizontal speed of the parallax. 1 is normal
+    tl.parallaxY = prop.atl_parallaxX or 1      -- The vertical speed of the parallax. 1 is normal
+    tl.offsetX = prop.atl_offsetX or 0          -- Drawing offset X
+    tl.offsetY = prop.atl_offsetY or 0          -- Drawing offset Y
+    
+    -- Private:
+    tl._redraw = true                   -- If true then the layer needs to redraw tis sprite batches.
+    tl._tileRange = {0,0,0,0}           -- Keeps the drawn tile range for the layer
+    tl._previousTileRange = {0,0,0,0}   -- Previous _tileRange
+    tl._batches = {}                    -- Keeps track of the sprite batches for each tileset
+    tl._flippedTiles = Grid:new()       -- Stores the flipped tile locations. 
+                                            -- 1 = flipped X, 2 = flipped Y, 3 = both
+    tl._afterTileFunction = nil         -- A function that handles drawing things after individual
+                                            -- tiles. This will not work with SpriteBatches.
+    tl._afterTileArgs = nil             -- Starting arguments for the after tile function
+    tl._previousUseSpriteBatch = false  -- The previous useSpriteBatch. If this is different then we 
+                                            -- need to force a special redraw
+    return tl
+end
+
+----------------------------------------------------------------------------------------------------
+-- Clears the tile layer of its after tile function.
+function TileLayer:clearAfterTileFunction()
+    self._afterTileFunction = nil
+    self._afterTileArgs = nil
+end
+
+----------------------------------------------------------------------------------------------------
+-- Sets a function that will be called after every tile. funct(layer, x, y, drawX, drawY ...)
+function TileLayer:setAfterTileFunction(funct, ...)
+    self._afterTileFunction = funct
+    local args = {...}
+    if #args > 0 then self._afterTileArgs = args end
+end
+
+----------------------------------------------------------------------------------------------------
+-- These are used in TileLayer:draw() but since that function is called so often we'll define them
+-- outside to prevent them from being created and destroyed all the time.
+local map, tile, tiles, postDraw, useSpriteBatch, tile, width, height
+local at, drawX, drawY, flipX, flipY, r, g, b, a, halfW, halfH
+local x1, y1, x2, y2
+-- Draws the TileLayer.
+function TileLayer:draw()
+
+    -- Early exit of the layer is not visible
+    if not self.visible then return end
+
+    -- We access these a lot so we'll shorted them a bit. 
+    map, tiles = self.map, self.map.tiles
+    postDraw = self.postDraw
+    
+    -- If useSpriteBatch was changed then we need to force the sprite batches to redraw.
+    if self.useSpriteBatch ~= self._previousUseSpriteBatch then map:forceRedraw() end
+    
+    -- Set the previous useSpriteBatch
+    self._previousUseSpriteBatch = self.useSpriteBatch
+    
+    -- If useSpriteBatch is set for this layer then use that, otherwise use the map's setting.
+    useSpriteBatch = self.useSpriteBatch ~= nil and self.useSpriteBatch or map.useSpriteBatch
+    
+    -- We'll blend the set alpha in with the current alpha
+    r,g,b,a = love.graphics.getColor()
+    love.graphics.setColor(r,g,b, a*self.opacity)
+    
+    -- Clear sprite batches if the screen has changed.
+    if self._redraw and useSpriteBatch then
+        for k,v in pairs(self._batches) do
+            v:clear()
+        end
+    end
+    
+    -- Get the tile range
+    x1, y1, x2, y2 = self._tileRange[1], self._tileRange[2], self._tileRange[3], self._tileRange[4]
+    
+    -- Translate for the parallax
+    if self.parallaxX ~= 1 or self.parallaxY ~= 1 then
+        love.graphics.push()
+        love.graphics.translate(math.floor(map.viewX - map.viewX*self.parallaxX), 
+                                math.floor(map.viewY - map.viewY*self.parallaxY))
+    end
+    
+    -- Only draw if we're not using sprite batches or we need to update the sprite batches.
+    if self._redraw or not useSpriteBatch then
+    
+        -- Bind the sprite batches
+        if useSpriteBatch then 
+            for k, batch in pairs(self._batches) do
+                batch:bind()
+            end
+        end
+    
+        -- Orthogonal tiles
+        if map.orientation == "orthogonal" then
+        
+            -- Go through each tile
+            for x,y,tile in self:rectangle(x1,y1,x2,y2) do
+            
+                -- Get the half-width and half-height
+                halfW, halfH = tile.width*0.5, tile.height*0.5
+                
+                -- Draw the tile from the bottom left corner
+                drawX, drawY = (x)*map.tileWidth, (y+1)*map.tileHeight
+                
+                -- Apply the offset
+                drawX = drawX - map.offsetX - self.offsetX
+                drawY = drawY - map.offsetY - self.offsetY
+                
+                -- Get the flipped tiles
+                if self._flippedTiles(x,y) then
+                    rot =  (self._flippedTiles(x,y) % 2) == 1 and true or false
+                    flipY = (self._flippedTiles(x,y) % 4) >= 2 and -1 or 1
+                    flipX = self._flippedTiles(x,y) >= 4 and -1 or 1
+                    if rot then flipX, flipY = -flipY, flipX end
+                else
+                    rot, flipX, flipY = false, 1, 1
+                end
+                
+                -- If we are using spritebatches
+                if useSpriteBatch then
+                    -- If we dont have a spritebatch for the current tile's tileset then make one
+                    if not self._batches[tile.tileset] then 
+                        self._batches[tile.tileset] = love.graphics.newSpriteBatch(
+                                                        tile.tileset.image, map.width * map.height)
+                        self._batches[tile.tileset]:bind()
+                    end
+                    -- Add the quad to the spritebatch
+                    self._batches[tile.tileset]:addq(tile.quad, drawX + halfW, 
+                                    drawY - halfH, 
+                                    rot and math.pi*1.5 or 0, 
+                                    flipX, flipY, halfW, halfH)
+                                    
+                -- If we are not using spritebatches
+                else
+                    -- Draw the tile
+                    tile:draw(drawX + halfW,
+                          drawY - halfH, 
+                          rot and math.pi*1.5 or 0, 
+                          flipX, flipY, halfW, halfH)
+                          
+                    -- Call the after tile function
+                    if self._afterTileFunction then 
+                        if self._afterTileArgs then
+                            self._afterTileFunction(self, x, y, drawX, drawY, 
+                                                    unpack(self._afterTileArgs))
+                        else
+                            self._afterTileFunction(self, x, y, drawX, drawY)
+                        end
+                    end
+                end
+            end
+        end
+        
+        -- Isometric tiles
+        if map.orientation == "isometric" then
+            local x,y
+            
+            -- Get the starting x drawing location
+            draw_start = map.height * map.tileWidth/2
+            
+            -- Draw each tile starting from the top left tile. Make sure we have enough
+            -- room to draw the widest and tallest tile in the map.
+            for down=0,y2 do 
+                for layer=0,1 do
+                    for right=0,x2 do
+                        x = x1 + right + down + layer - 1
+                        y = y1 - right + down - 1
+                        
+                        -- If there is a tile row
+                        if self(x,y) then
+                        
+                            -- Check and see if the tile is flipped
+                            if self._flippedTiles(x,y) then
+                                rot =  (self._flippedTiles(x,y) % 2) == 1 and true or false
+                                flipY = (self._flippedTiles(x,y) % 4) >= 2 and -1 or 1
+                                flipX = self._flippedTiles(x,y) >= 4 and -1 or 1
+                                if rot then flipX, flipY = -flipY, flipX end
+                            else
+                                rot, flipX, flipY = false, 1, 1
+                            end
+                            
+                            -- Get the tile
+                            tile = self(x,y)
+                            
+                            -- If the tile exists then draw the tile
+                            if tile then 
+                            
+                                -- Get the half-width and half-height
+                                halfW, halfH = tile.width*0.5, tile.height*0.5
+                                
+                                -- Get the tile draw location
+                                drawX = math.floor(draw_start + map.tileWidth/2 * (x - y-2))
+                                drawY = math.floor(map.tileHeight/2 * (x + y+2))
+                                
+                                -- Apply the offset
+                                drawX = drawX - map.offsetX - self.offsetX
+                                drawY = drawY - map.offsetY - self.offsetY
+                                
+                                -- Using sprite batches
+                                if useSpriteBatch then
+                                    -- If we dont have a spritebatch for the current tile's tileset 
+                                    -- then make one
+                                    if not self._batches[tile.tileset] then 
+                                        self._batches[tile.tileset] = love.graphics.newSpriteBatch(
+                                                                            tile.tileset.image, 
+                                                                            map.width * map.height)
+                                    end
+                                    -- Add the tile to the sprite batch.
+                                    self._batches[tile.tileset]:addq(tile.quad, drawX + halfW + 
+                                                                (rot and halfW or 0), 
+                                                                drawY-halfH+(rot and halfW or 0), 
+                                                                rot and math.pi*1.5 or 0, 
+                                                                flipX, flipY, halfW, halfH)
+                                    -- Bind the sprite batch
+                                    self._batches[tile.tileset]:bind()
+                                                                    
+                                -- Not using sprite batches
+                                else
+                                    tile:draw(drawX + halfW + (rot and halfW or 0), 
+                                                drawY - halfH + (rot and halfW or 0), 
+                                                rot and math.pi*1.5 or 0, 
+                                                flipX, flipY, halfW, halfH)
+                                                
+                                    -- Call the after tile function
+                                    if self._afterTileFunction then 
+                                        if self._afterTileArgs then
+                                            self._afterTileFunction(self, x, y, drawX, drawY,
+                                                                    unpack(self._afterTileArgs))
+                                        else
+                                            self._afterTileFunction(self, x, y, drawX, drawY)
+                                        end
+                                    end
+                                    
+                                end
+                            end
+                        end
+                    end
+                end
+            end
+        end
+        
+        -- Unbind the sprite batches
+         if useSpriteBatch then 
+            for k, batch in pairs(self._batches) do
+                batch:unbind()
+            end
+        end
+        
+    end
+    
+    -- We finished redrawing
+    self._redraw = false
+    
+    -- If sprite batches are turned on then render them
+    if useSpriteBatch then
+        for k, batch in pairs(self._batches) do
+            love.graphics.draw(batch)
+        end
+    end
+    
+    -- If we applied a translation for our parallax then remove it
+    if self.parallaxX ~= 1 or self.parallaxY ~= 1 then
+        love.graphics.pop()
+    end
+    
+    -- Change the color back
+    love.graphics.setColor(r,g,b,a)
+end
+
+----------------------------------------------------------------------------------------------------
+-- This copies a tile so that you can paste it in another spot. The pasted tile will keep the
+-- rotation and flipped status. You can copy and paste between layers.
+local flippedVal = 2^29
+function TileLayer:tileCopy(x,y)
+    if not self(x,y) then 
+        self.map._tileClipboard = 0 
+    else
+        self.map._tileClipboard = self(x,y).id + (self._flippedTiles(x,y) or 0) * flippedVal
+    end
+end
+
+----------------------------------------------------------------------------------------------------
+-- Paste a copied tile.
+function TileLayer:tilePaste(x,y)
+    self._redraw = true
+    if not self.map._tileClipboard then
+        error("TileLayer:tilePaste() - A tile must be copied with tileCopy() before pasting")
+    end
+    local clip = self.map._tileClipboard 
+    if clip / flippedVal > 0 then
+        self._flippedTiles:set(x, y, math.floor(clip / flippedVal))
+    end
+    self:set(x, y, self.map.tiles[clip % flippedVal])
+end
+
+----------------------------------------------------------------------------------------------------
+-- Flip the tile's X. If doFlip is not specified then the flip is toggled.
+function TileLayer:tileFlipX(x, y, doFlip)
+    self._redraw = true
+    local flip = self._flippedTiles(x,y) or 0
+    if doFlip ~= false and flip < 4 then 
+        flip = flip + 4
+    elseif doFlip ~= true and flip >= 4 then 
+        flip = flip - 4
+    end
+    self._flippedTiles:set(x, y, flip ~= 0 and flip or nil)
+end
+
+----------------------------------------------------------------------------------------------------
+-- Flip the tile's Y. If doFlip is not specified then the flip is toggled.
+function TileLayer:tileFlipY(x, y, doFlip)
+    self.redraw = true
+    local flip = self._flippedTiles(x,y) or 0
+    if doFlip ~= false and flip % 4 < 2 then 
+        flip = flip + 2
+    elseif doFlip ~= true and flip % 4 >= 2 then 
+        flip = flip - 2
+    end
+    self._flippedTiles:set(x, y, flip ~= 0 and flip or nil)
+end
+
+----------------------------------------------------------------------------------------------------
+-- Rotate the tile.
+function TileLayer:tileRotate(x, y, rot)
+    local flip = self._flippedTiles(x,y) or 0
+    if rot then flip = rot % 8
+    elseif flip == 0 then flip = 5
+    elseif flip == 1 then flip = 4 
+    elseif flip == 2 then flip = 1
+    elseif flip == 3 then flip = 0 
+    elseif flip == 4 then flip = 7
+    elseif flip == 5 then flip = 6
+    elseif flip == 6 then flip = 3
+    elseif flip == 7 then flip = 2 
+    end
+    self._flippedTiles:set(x, y, flip ~= 0 and flip or nil)
+end
+
+----------------------------------------------------------------------------------------------------
+-- Private
+----------------------------------------------------------------------------------------------------
+
+----------------------------------------------------------------------------------------------------
+-- Creates the tile data from a table containing each tile id in sequential order
+-- from left-to-right, top-to-bottom.
+str = ""
+function TileLayer:_populate(t)
+
+    str = ""
+    local lasty = 0
+    for i=1,#t do
+        str = str .. t[i]
+        if lasty ~= math.floor(i/self.map.width) then
+            str = str .. "\n"
+            lasty = math.floor(i/self.map.width)
+        end
+    end
+    
+    -- Some temporary storage
+    local width, height =  self.map.width, self.map.height
+    local tileID
+    
+    -- The values that indicate flipped tiles are in the last three binary digits. We need
+    -- to seperate those.
+    local flipped = 2^29
+    
+    -- Clear the tiles
+    self:clear()
+    
+    -- Go through every tile
+    for x,y,v in self:rectangle(0,0,width-1,height-1,true) do
+        tileID = t[width*y+x+1] or 0
+        
+        -- If the tile has a value in the last three binary digits then we seperate them
+        if tileID >= flipped then 
+            self._flippedTiles:set(x, y, math.floor(tileID / flipped))
+            tileID = tileID % flipped
+        end
+        
+        -- Set the tile
+        self:set(x, y, self.map.tiles[tileID])
+    end
+end
+
+----------------------------------------------------------------------------------------------------
+-- Return the TileLayer class
+return TileLayer
+
+
+--[[Copyright (c) 2011-2012 Casey Baxter
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+Except as contained in this notice, the name(s) of the above copyright holders
+shall not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.--]]
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AdvTiledLoader/TileSet.lua	Sat Dec 15 13:10:41 2012 +0100
@@ -0,0 +1,102 @@
+---------------------------------------------------------------------------------------------------
+-- -= TileSet =-
+---------------------------------------------------------------------------------------------------
+-- Setup
+TILED_LOADER_PATH = TILED_LOADER_PATH or ({...})[1]:gsub("[%.\\/][Tt]ile[Ss]et", "") .. '.'
+local ceil = math.ceil
+local Tile = require( TILED_LOADER_PATH .. "Tile")
+local TileSet = {class = "TileSet"}
+TileSet.__index = TileSet
+
+----------------------------------------------------------------------------------------------------
+-- Creates a new tileset.
+function TileSet:new(img, imgPath, name, tw, th, w, h, gid, space, marg, offx, offy, trans, tprop, prop)
+    local ts = {}
+    
+    -- Public:
+    ts.image = img                  -- The image of the tileset
+    ts.imagePath = imgPath          -- The path to the image file
+    ts.name = name                  -- Name of the tilseset
+    ts.tileWidth = tw               -- The width of each tile in pixels
+    ts.tileHeight = th              -- The height of each tile in pixels
+    ts.width = w                    -- The width of the tileset image in pixels
+    ts.height = h                   -- The height of the tileset image in pixels
+    ts.firstgid = gid               -- The id of the first tile
+    ts.spacing = space or 0         -- The spacing in pixels between each tile
+    ts.margin = marg or 0           -- The margin in pixels surrounding the entire tile set.
+    ts.trans = trans                -- The transparency value. Only used when saving maps.
+    ts.offsetX = offx               -- The X offset.
+    ts.offsetY = offy               -- The Y offset.
+    ts.tileProperties = tprop or {} -- Properties of contained tiles indexed by the tile's gid
+    ts.properties = prop or {}      -- The properties of the tileset
+    
+    return setmetatable(ts, TileSet)
+end
+
+----------------------------------------------------------------------------------------------------
+-- Returns the width in tiles
+function TileSet:tilesWide()
+    return ceil( (self.width - self.margin*2 - self.spacing) / 
+                      (self.tileWidth + self.spacing) )
+end
+
+----------------------------------------------------------------------------------------------------
+-- Returns the height in tiles
+function TileSet:tilesHigh()
+    return ceil( (self.height - self.margin*2 - self.spacing) / 
+                      (self.tileHeight + self.spacing) )
+end
+
+----------------------------------------------------------------------------------------------------
+-- Produces tiles from the settings and returns them in a table indexed by their id.
+-- These are cut out left-to-right, top-to-bottom.
+function TileSet:getTiles()
+    local x,y = self.margin, self.margin
+    local tiles = {}
+    local quad = false
+    local id = self.firstgid
+    local imageWidth, imageHeight = self.image:getWidth(), self.image:getHeight()
+
+    for i = 1, self:tilesHigh() do
+        for j = 1, self:tilesWide() do
+            quad = love.graphics.newQuad(x, y, self.tileWidth, self.tileHeight, imageWidth, imageHeight)
+            tiles[id] = Tile:new(id, self, quad, self.tileWidth, self.tileHeight, self.tileProperties[id])
+            x = x + self.tileWidth + self.spacing
+            id = id + 1
+        end
+        x = self.margin
+        y = y + self.tileHeight + self.spacing
+    end
+    
+    return tiles
+end
+
+----------------------------------------------------------------------------------------------------
+-- Return the TileSet class
+return TileSet
+
+
+--[[Copyright (c) 2011-2012 Casey Baxter
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+Except as contained in this notice, the name(s) of the above copyright holders
+shall not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.--]]
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AdvTiledLoader/VERSION.txt	Sat Dec 15 13:10:41 2012 +0100
@@ -0,0 +1,141 @@
+Advanced Tiled Loader
+--------------------------------------------------------------------------------------
+
+CHANGES:
+0.12.1 (10/31/12)
+- TileLayer:drawAfterTile() has been reworked into TileLayer:newAfterTileFunction()
+- Arguments for the Map functions that create and add map components directly to the 
+	map have changed. (Map:newTileLayer(), Map:newObjectLayer(), etc)
+
+0.12.0 (10/17/12)
+- Maps can now be saved with Loader.save()
+- Introduced ATL properties
+
+0.11.2 (10/08/12)
+- TileLayers will now calculate their own viewable tile range.
+- Added parallax support for TileLayers.
+- Added offset support for Maps and TileLayers.
+- You can now load .tmx files without including the extension in the file name.
+- Map.drawRange has been removed and replaced with viewX, viewY, viewScaling, and viewPadding.
+	Map:autoDrawRange() and other such interface functions have not changed.
+
+
+0.11.1 (09/26/12)
+- Added a 'visible' value to Map, ObjectLayer, TileLayer, and Object
+- Created ObjectLayer:toCustomLayer()
+- Created Map:removeLayer()
+
+0.11.0 (09/04/12)
+- Renamed Map.drawList to Map.layerOrder.
+- Renamed Map.drawPosition to Map.layerPosition.
+- Combined Map.tileLayers and Map.objectLayers into one table - Map.layers.
+- The functionality of tileData is now combined with TileLayer.
+- Changed Map.__call to return layers by their name.
+- Created Map.callback() to forward love callbacks to layers.
+- Created Map.swapLayers()
+- Created Map.newCustomLayers() which is a new interface for creating custom layers.
+- Classes now remember their class name.
+- Removed object movement functions to deter the use of objects other than reading data from.
+- Removed the fixPO2 option for the loader.
+- Generally cleaned up the code and made it easier to read.
+- Properties are now automatically converted into proper types (numbers, boolean, string) when loaded.
+- Readme file changed. There is now a new VERSION.txt file for version information.
+
+
+0.10.2 (04/26/12)
+- Relative paths now fully work. Before, ATL did not like to travel backwards in 
+	a directory from a map file to reach a tileset.
+
+0.10.1 (04/20/12)
+- Added quit compatibility for love 0.8.0 in the example files
+- AdvTiledLoader can now be correctly required from any subfolder
+- Fixed a bug with map.drawObjects not working
+- Undefined object colors will now appear grey instead of black
+- Added Loader.drawObjects and Loader.useSpriteBatch values. These values will be 
+	applied to any new map that is loaded.
+- Fixed a bug where the map would be drawn incorrectly if the map's width and height 
+	were different
+
+0.10.0 (04/11/12)
+- 2d arrays are replaced with a much easier to use grid class.
+- Tiles are now directly inserted into TileLayer.tileData. You no longer have to 
+	look them up via their ID in Map.tiles.
+- Tile indexes are now the same as their coordinants as shown inside the Tiled.
+- Added support for Tiled version 0.8.0. This includes tile rotation, polygon 
+	objects, polyline objects, tilemap offset, and tilemap propterties.
+- Moved the object drawing code from object layers to the objects themselves.
+- Added functions tileCopy(), tilePaste(), tileRotate(), tileFlipX(), tileFlipY() 
+	to TileLayers.
+
+0.9.0 (8/22/11)
+- Advanced Tile Loader now has a wiki page and a github repository!
+- Cleaned up the code quite a bit.
+- Classes are now broken up into individual files rather than grouping similar 
+	ones into one file.
+- Many identifiers have been changed to be less confusing and more consistent. Now 
+	camelCase is used to separate words instead of underscores. Internal 
+	functions and data are now prefixed with an underscore.
+- Sprite batch mode can now be set through the map or through individual tile 
+	layers. Tile layer settings take precedence.
+- A bug with layer transparency has been fixed.
+- Tile:draw() now accepts parameters for scaling, rotation, and offset.
+- TileLayer:drawAfterTile() now works with multiple functions
+- Forcing a redraw is now automatic when you switch sprite batch modes.
+- Added support for flipped tiles that were introduced in Tiled version 0.7.1
+
+0.8.2 (5/9/11):
+- Tileset images are now cached between maps.
+- Added an option to automatically pad images for PO2. To do this set 
+	Loader.fix_po2 to true.
+- Changed Map.tl and Map.ol back to their old names. Map.tl and Map.ol remain as 
+	aliases.
+- Tile layers can now render using sprite batches by setting TileLayer.use_batch 
+	to true.
+- Added an init.lua file.
+- Removed hardcoded require() paths. Added in global TILED_LOADER_PATH to point 
+	to the library path.
+- Renamed the TileSet functions getWidth() and getHeight() to tilesWide() and 
+	tilesHigh().
+
+0.8.1 (3/10/11):
+- Renamed Map.tilelayers and Map.objectlayers to Map.tl and Map.ol respectively.
+- Added function Tile.drawAfterTile()
+- You may now define a draw() function for objects which overrides the default 
+	drawing routine
+
+0.8.0 (2/28/11):
+- Initial release
+
+
+For more information see here:
+http://love2d.org/forums/viewtopic.php?f=5&t=2567
+
+--------------------------------------------------------------------------------------
+
+
+All images and code is subject to the following license unless otherwise stated:
+
+
+Copyright (c) 2011-2012 Casey Baxter
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+Except as contained in this notice, the name(s) of the above copyright holders
+shall not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AdvTiledLoader/external/crc32lua.lua	Sat Dec 15 13:10:41 2012 +0100
@@ -0,0 +1,134 @@
+--[[
+dmlib.crc32
+CRC-32 checksum implemented entirely in Lua.  This is similar to [1-2].
+
+References
+  [1] http://www.axlradius.com/freestuff/CRC32.java
+  [2] http://www.gamedev.net/reference/articles/article1941.asp
+  [3] http://java.sun.com/j2se/1.5.0/docs/api/java/util/zip/CRC32.html
+  [4] http://www.dsource.org/projects/tango/docs/current/tango.io.digest.Crc32.html
+  [5] http://pydoc.org/1.5.2/zlib.html#-crc32
+  [6] http://www.python.org/doc/2.5.2/lib/module-binascii.html
+
+(c) 2008 David Manura.  Licensed under the same terms as Lua (MIT).
+--]]
+
+
+local M = {}
+
+local type = type
+local require = require
+local setmetatable = setmetatable
+local _G = _G
+
+local bxor = require ( TILED_LOADER_PATH .. "external.numberlua" ) . bxor
+
+--[[NATIVE_BITOPS
+local bxor = bit.bxor
+local rshift = bit.rshift
+local bnot = bit.bnot
+--]]
+
+-- CRC-32-IEEE 802.3 (V.42)
+local POLY = 0xEDB88320
+
+
+local function memoize(f)
+  local mt = {}
+  local t = setmetatable({}, mt)
+  function mt:__index(k)
+    local v = f(k); t[k] = v
+    return v
+  end
+  return t
+end
+
+
+local function get(i)
+  local crc = i
+  for j=1,8 do
+    local b = crc % 2
+    crc = (crc - b) / 2
+    if b == 1 then crc = bxor(crc, POLY) end
+  end
+  return crc
+end
+local crc_table = memoize(get)
+
+
+local function crc32_byte(byte, crc)
+  crc = 0xffffffff - (crc or 0)
+  local v1 = (crc - crc % 256) / 256
+  local v2 = crc_table[bxor(crc % 256, byte)]
+  return 0xffffffff - bxor(v1, v2)
+end
+--[[NATIVE_BITOPS
+local function crc32_byte(byte, crc)
+  crc = bnot(crc or 0)
+  local v1 = rshift(crc, 8)
+  local v2 = crc_table[bxor(crc % 256, byte)]
+  return bnot(bxor(v1, v2))
+end
+--]]
+M.crc32_byte = crc32_byte
+
+
+local function crc32_string(s, crc)
+  crc = crc or 0
+  for i=1,#s do
+    crc = crc32_byte(s:byte(i), crc)
+  end
+  return crc
+end
+M.crc32_string = crc32_string
+
+
+local function crc32(s, crc)
+  if type(s) == 'string' then
+    return crc32_string(s, crc)
+  else
+    return crc32_byte(s, crc)
+  end
+end
+M.crc32 = crc32
+
+
+--DEBUG:
+--local s = "test123"
+--local crc = 0xffffffff
+--for i=1,#s do
+--  local byte = s:sub(i,i):byte()
+--  crc = crc32(crc, byte)
+--end
+--crc = 0xffffffff - crc
+--print(string.format("%08X", crc))
+
+
+return M
+
+
+--[[
+LICENSE
+
+Copyright (C) 2008, David Manura.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+(end license)
+--]]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AdvTiledLoader/external/deflatelua.lua	Sat Dec 15 13:10:41 2012 +0100
@@ -0,0 +1,768 @@
+-- dmlib.deflate
+-- deflate (and gunzip) implemented in Lua.
+--
+-- Note: only supports decompression.
+-- Compression not implemented.
+--
+-- References
+-- [1] DEFLATE Compressed Data Format Specification version 1.3
+--     http://tools.ietf.org/html/rfc1951
+-- [2] GZIP file format specification version 4.3
+--     http://tools.ietf.org/html/rfc1952
+-- [3] http://en.wikipedia.org/wiki/DEFLATE
+-- [4] pyflate, by Paul Sladen
+--     http://www.paul.sladen.org/projects/pyflate/
+-- [5] Compress::Zlib::Perl - partial pure Perl implementation of
+--     Compress::Zlib
+--     http://search.cpan.org/~nwclark/Compress-Zlib-Perl/Perl.pm
+--
+-- (c) 2008 David Manura.  Licensed under the same terms as Lua (MIT).
+
+local assert = assert
+local error = error
+local ipairs = ipairs
+local pairs = pairs
+local print = print
+local require = require
+local tostring = tostring
+local type = type
+local setmetatable = setmetatable
+local io = io
+local math = math
+local table_sort = table.sort
+local math_max = math.max
+local string_char = string.char
+local io_open = io.open
+local _G = _G
+
+local crc32 = require( TILED_LOADER_PATH .. "external.crc32lua" ) . crc32_byte
+
+local DEBUG = false
+
+--[[NATIVE_BITOPS
+local band = bit.band
+local lshift = bit.lshift
+local rshift = bit.rshift
+--]]
+
+local M = {}
+
+
+local function warn(s)
+  io.stderr:write(s, '\n')
+end
+
+
+local function debug(...)
+  print('DEBUG', ...)
+end
+
+
+local function runtime_error(s, level)
+  level = level or 1
+  error({s}, level+1)
+end
+
+
+local function make_outstate(outbs)
+  local outstate = {}
+  outstate.outbs = outbs
+  outstate.window = {}
+  outstate.window_pos = 1
+  return outstate
+end
+
+
+local function output(outstate, byte)
+  -- debug('OUTPUT:', s)
+  local window_pos = outstate.window_pos
+  outstate.outbs(byte)
+  outstate.window[window_pos] = byte
+  outstate.window_pos = window_pos % 32768 + 1  -- 32K
+end
+
+
+local function noeof(val)
+  return assert(val, 'unexpected end of file')
+end
+
+
+local function hasbit(bits, bit)
+  return bits % (bit + bit) >= bit
+end
+
+
+local function memoize(f)
+  local mt = {}
+  local t = setmetatable({}, mt)
+  function mt:__index(k)
+    local v = f(k)
+    t[k] = v
+    return v
+  end
+  return t
+end
+
+
+-- small optimization (lookup table for powers of 2)
+local pow2 = memoize(function(n) return 2^n end)
+
+--local tbits = memoize(
+--  function(bits)
+--    return memoize( function(bit) return getbit(bits, bit) end )
+--  end )
+
+
+-- weak metatable marking objects as bitstream type
+local is_bitstream = setmetatable({}, {__mode='k'})
+
+
+-- DEBUG
+-- prints LSB first
+local function bits_tostring(bits, nbits)
+  local s = ''
+  local tmp = bits
+  local function f()
+    local b = tmp % 2 == 1 and 1 or 0
+    s = s .. b
+    tmp = (tmp - b) / 2
+  end
+  if nbits then
+    for i=1,nbits do f() end
+  else
+    while tmp ~= 0 do f() end
+  end
+
+  return s
+end
+
+
+local function bytestream_from_file(fh)
+  local o = {}
+  function o:read()
+    local sb = fh:read(1)
+    if sb then return sb:byte() end
+  end
+  return o
+end
+
+
+local function bytestream_from_string(s)
+  local i = 1
+  local o = {}
+  function o:read()
+    local by
+    if i <= #s then
+      by = s:byte(i)
+      i = i + 1
+    end
+    return by
+  end
+  return o
+end
+
+
+local function bytestream_from_function(f)
+  local i = 0
+  local buffer = ''
+  local o = {}
+  function o:read()
+    i = i + 1
+    if i > #buffer then
+      buffer = f()
+      if not buffer then return end
+      i = 1
+    end
+    return buffer:byte(i,i)
+  end
+  return o
+end
+
+
+local left
+local function bitstream_from_bytestream(bys)
+  local buf_byte = 0
+  local buf_nbit = 0
+  local o = {}
+
+  function o:nbits_left_in_byte()
+    return buf_nbit
+  end
+
+  function o:read(nbits)
+    nbits = nbits or 1
+    while buf_nbit < nbits do
+      local byte = bys:read()
+      if not byte then return end  -- note: more calls also return nil
+      buf_byte = buf_byte + pow2[buf_nbit] * byte
+      buf_nbit = buf_nbit + 8
+    end
+    local m = pow2[nbits]
+    local bits = buf_byte % m
+    buf_byte = (buf_byte - bits) / m
+    buf_nbit = buf_nbit - nbits
+    return bits
+  end
+  --[[NATIVE_BITOPS
+  function o:read(nbits)
+    nbits = nbits or 1
+    while buf_nbit < nbits do
+      local byte = bys:read()
+      if not byte then return end  -- note: more calls also return nil
+      buf_byte = buf_byte + lshift(byte, buf_nbit)
+      buf_nbit = buf_nbit + 8
+    end
+    local bits
+    if nbits == 0 then
+      bits = 0
+    elseif nbits == 32 then
+      bits = buf_byte
+      buf_byte = 0
+    else
+      bits = band(buf_byte, rshift(0xffffffff, 32 - nbits))
+      buf_byte = rshift(buf_byte, nbits)
+    end
+    buf_nbit = buf_nbit - nbits
+    return bits
+  end
+  --]]
+  
+  is_bitstream[o] = true
+
+  return o
+end
+
+
+local function get_bitstream(o)
+  local bs
+  if is_bitstream[o] then
+    return o
+  elseif io.type(o) == 'file' then
+    bs = bitstream_from_bytestream(bytestream_from_file(o))
+  elseif type(o) == 'string' then
+    bs = bitstream_from_bytestream(bytestream_from_string(o))
+  elseif type(o) == 'function' then
+    bs = bitstream_from_bytestream(bytestream_from_function(o))
+  else
+    runtime_error 'unrecognized type'
+  end
+  return bs
+end
+
+
+local function get_obytestream(o)
+  local bs
+  if io.type(o) == 'file' then
+    bs = function(sbyte) o:write(string_char(sbyte)) end
+  elseif type(o) == 'function' then
+    bs = o
+  else
+    runtime_error('unrecognized type: ' .. tostring(o))
+  end
+  return bs
+end
+
+
+local function HuffmanTable(init, is_full)
+  local t = {}
+  if is_full then
+    for val,nbits in pairs(init) do
+      if nbits ~= 0 then
+        t[#t+1] = {val=val, nbits=nbits}
+        --debug('*',val,nbits)
+      end
+    end
+  else
+    for i=1,#init-2,2 do
+      local firstval, nbits, nextval = init[i], init[i+1], init[i+2]
+      --debug(val, nextval, nbits)
+      if nbits ~= 0 then
+        for val=firstval,nextval-1 do
+          t[#t+1] = {val=val, nbits=nbits}
+        end
+      end
+    end
+  end
+  table_sort(t, function(a,b)
+    return a.nbits == b.nbits and a.val < b.val or a.nbits < b.nbits
+  end)
+
+  -- assign codes
+  local code = 1  -- leading 1 marker
+  local nbits = 0
+  for i,s in ipairs(t) do
+    if s.nbits ~= nbits then
+      code = code * pow2[s.nbits - nbits]
+      nbits = s.nbits
+    end
+    s.code = code
+    --debug('huffman code:', i, s.nbits, s.val, code, bits_tostring(code))
+    code = code + 1
+  end
+
+  local minbits = math.huge
+  local look = {}
+  for i,s in ipairs(t) do
+    minbits = math.min(minbits, s.nbits)
+    look[s.code] = s.val
+  end
+
+  --for _,o in ipairs(t) do
+  --  debug(':', o.nbits, o.val)
+  --end
+
+  -- function t:lookup(bits) return look[bits] end
+
+  local function msb(bits, nbits)
+    local res = 0
+    for i=1,nbits do
+      local b = bits % 2
+      bits = (bits - b) / 2
+      res = res * 2 + b
+    end
+    return res
+  end
+  --[[NATIVE_BITOPS
+  local function msb(bits, nbits)
+    local res = 0
+    for i=1,nbits do
+      res = lshift(res, 1) + band(bits, 1)
+      bits = rshift(bits, 1)
+    end
+    return res
+  end
+  --]]
+  
+  local tfirstcode = memoize(
+    function(bits) return pow2[minbits] + msb(bits, minbits) end)
+
+  function t:read(bs)
+    local code = 1 -- leading 1 marker
+    local nbits = 0
+    while 1 do
+      if nbits == 0 then  -- small optimization (optional)
+        code = tfirstcode[noeof(bs:read(minbits))]
+        nbits = nbits + minbits
+      else
+        local b = noeof(bs:read())
+        nbits = nbits + 1
+        code = code * 2 + b   -- MSB first
+        --[[NATIVE_BITOPS
+        code = lshift(code, 1) + b   -- MSB first
+        --]]
+      end
+      --debug('code?', code, bits_tostring(code))
+      local val = look[code]
+      if val then
+        --debug('FOUND', val)
+        return val
+      end
+    end
+  end
+
+  return t
+end
+
+
+local function parse_gzip_header(bs)
+  -- local FLG_FTEXT = 2^0
+  local FLG_FHCRC = 2^1
+  local FLG_FEXTRA = 2^2
+  local FLG_FNAME = 2^3
+  local FLG_FCOMMENT = 2^4
+
+  local id1 = bs:read(8)
+  local id2 = bs:read(8)
+  if id1 ~= 31 or id2 ~= 139 then
+    runtime_error 'not in gzip format'
+  end
+  local cm = bs:read(8)  -- compression method
+  local flg = bs:read(8) -- FLaGs
+  local mtime = bs:read(32) -- Modification TIME
+  local xfl = bs:read(8) -- eXtra FLags
+  local os = bs:read(8) -- Operating System
+
+  if DEBUG then
+    debug("CM=", cm)
+    debug("FLG=", flg)
+    debug("MTIME=", mtime)
+    -- debug("MTIME_str=",os.date("%Y-%m-%d %H:%M:%S",mtime)) -- non-portable
+    debug("XFL=", xfl)
+    debug("OS=", os)
+  end
+
+  if not os then runtime_error 'invalid header' end
+
+  if hasbit(flg, FLG_FEXTRA) then
+    local xlen = bs:read(16)
+    local extra = 0
+    for i=1,xlen do
+      extra = bs:read(8)
+    end
+    if not extra then runtime_error 'invalid header' end
+  end
+
+  local function parse_zstring(bs)
+    repeat
+      local by = bs:read(8)
+      if not by then runtime_error 'invalid header' end
+    until by == 0
+  end
+
+  if hasbit(flg, FLG_FNAME) then
+    parse_zstring(bs)
+  end
+
+  if hasbit(flg, FLG_FCOMMENT) then
+    parse_zstring(bs)
+  end
+
+  if hasbit(flg, FLG_FHCRC) then
+    local crc16 = bs:read(16)
+    if not crc16 then runtime_error 'invalid header' end
+    -- IMPROVE: check CRC.  where is an example .gz file that
+    -- has this set?
+    if DEBUG then
+      debug("CRC16=", crc16)
+    end
+  end
+end
+
+local function parse_zlib_header(bs)
+  local cm = bs:read(4) -- Compression Method
+  local cinfo = bs:read(4) -- Compression info
+  local fcheck = bs:read(5) -- FLaGs: FCHECK (check bits for CMF and FLG)
+  local fdict = bs:read(1) -- FLaGs: FDICT (present dictionary)
+  local flevel = bs:read(2) -- FLaGs: FLEVEL (compression level)
+  local cmf = cinfo * 16  + cm -- CMF (Compresion Method and flags)
+  local flg = fcheck + fdict * 32 + flevel * 64 -- FLaGs
+  
+  if cm ~= 8 then -- not "deflate"
+    runtime_error("unrecognized zlib compression method: " + cm)
+  end
+  local window_size
+  if cinfo > 7 then
+    runtime_error("invalid zlib window size: cinfo=" + cinfo)
+  end
+  local window_size = 2^(cinfo + 8)
+  
+  if (cmf*256 + flg) %  31 ~= 0 then
+    runtime_error("invalid zlib header (bad fcheck sum)")
+  end
+  
+  if fdict == 1 then
+    runtime_error("FIX:TODO - FDICT not currently implemented")
+    local dictid = bs:read(32)
+  end
+  
+  return window_size
+end
+
+local function parse_huffmantables(bs)
+    local hlit = bs:read(5)  -- # of literal/length codes - 257
+    local hdist = bs:read(5) -- # of distance codes - 1
+    local hclen = noeof(bs:read(4)) -- # of code length codes - 4
+
+    local ncodelen_codes = hclen + 4
+    local codelen_init = {}
+    local codelen_vals = {
+      16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}
+    for i=1,ncodelen_codes do
+      local nbits = bs:read(3)
+      local val = codelen_vals[i]
+      codelen_init[val] = nbits
+    end
+    local codelentable = HuffmanTable(codelen_init, true)
+
+    local function decode(ncodes)
+      local init = {}
+      local nbits
+      local val = 0
+      while val < ncodes do
+        local codelen = codelentable:read(bs)
+        --FIX:check nil?
+        local nrepeat
+        if codelen <= 15 then
+          nrepeat = 1
+          nbits = codelen
+          --debug('w', nbits)
+        elseif codelen == 16 then
+          nrepeat = 3 + noeof(bs:read(2))
+          -- nbits unchanged
+        elseif codelen == 17 then
+          nrepeat = 3 + noeof(bs:read(3))
+          nbits = 0
+        elseif codelen == 18 then
+          nrepeat = 11 + noeof(bs:read(7))
+          nbits = 0
+        else
+          error 'ASSERT'
+        end
+        for i=1,nrepeat do
+          init[val] = nbits
+          val = val + 1
+        end
+      end
+      local huffmantable = HuffmanTable(init, true)
+      return huffmantable
+    end
+
+    local nlit_codes = hlit + 257
+    local ndist_codes = hdist + 1
+
+    local littable = decode(nlit_codes)
+    local disttable = decode(ndist_codes)
+
+    return littable, disttable
+end
+
+
+local tdecode_len_base
+local tdecode_len_nextrabits
+local tdecode_dist_base
+local tdecode_dist_nextrabits
+local function parse_compressed_item(bs, outstate, littable, disttable)
+  local val = littable:read(bs)
+  --debug(val, val < 256 and string_char(val))
+  if val < 256 then -- literal
+    output(outstate, val)
+  elseif val == 256 then -- end of block
+    return true
+  else
+    if not tdecode_len_base then
+      local t = {[257]=3}
+      local skip = 1
+      for i=258,285,4 do
+        for j=i,i+3 do t[j] = t[j-1] + skip end
+        if i ~= 258 then skip = skip * 2 end
+      end
+      t[285] = 258
+      tdecode_len_base = t
+      --for i=257,285 do debug('T1',i,t[i]) end
+    end
+    if not tdecode_len_nextrabits then
+      local t = {}
+      for i=257,285 do
+        local j = math_max(i - 261, 0)
+        t[i] = (j - (j % 4)) / 4
+        --[[NATIVE_BITOPS
+        t[i] = rshift(j, 2)
+        --]]
+      end
+      t[285] = 0
+      tdecode_len_nextrabits = t
+      --for i=257,285 do debug('T2',i,t[i]) end
+    end
+    local len_base = tdecode_len_base[val]
+    local nextrabits = tdecode_len_nextrabits[val]
+    local extrabits = bs:read(nextrabits)
+    local len = len_base + extrabits
+
+    if not tdecode_dist_base then
+      local t = {[0]=1}
+      local skip = 1
+      for i=1,29,2 do
+        for j=i,i+1 do t[j] = t[j-1] + skip end
+        if i ~= 1 then skip = skip * 2 end
+      end
+      tdecode_dist_base = t
+      --for i=0,29 do debug('T3',i,t[i]) end
+    end
+    if not tdecode_dist_nextrabits then
+      local t = {}
+      for i=0,29 do
+        local j = math_max(i - 2, 0)
+        t[i] = (j - (j % 2)) / 2
+        --[[NATIVE_BITOPS
+        t[i] = rshift(j, 1)
+        --]]
+      end
+      tdecode_dist_nextrabits = t
+      --for i=0,29 do debug('T4',i,t[i]) end
+    end
+    local dist_val = disttable:read(bs)
+    local dist_base = tdecode_dist_base[dist_val]
+    local dist_nextrabits = tdecode_dist_nextrabits[dist_val]
+    local dist_extrabits = bs:read(dist_nextrabits)
+    local dist = dist_base + dist_extrabits
+
+    --debug('BACK', len, dist)
+    for i=1,len do
+      local pos = (outstate.window_pos - 1 - dist) % 32768 + 1  -- 32K
+      output(outstate, assert(outstate.window[pos], 'invalid distance'))
+    end
+  end
+  return false
+end
+
+
+local function parse_block(bs, outstate)
+  local bfinal = bs:read(1)
+  local btype = bs:read(2)
+
+  local BTYPE_NO_COMPRESSION = 0
+  local BTYPE_FIXED_HUFFMAN = 1
+  local BTYPE_DYNAMIC_HUFFMAN = 2
+  local BTYPE_RESERVED = 3
+
+  if DEBUG then
+    debug('bfinal=', bfinal)
+    debug('btype=', btype)
+  end
+
+  if btype == BTYPE_NO_COMPRESSION then
+    bs:read(bs:nbits_left_in_byte())
+    local len = bs:read(16)
+    local nlen = noeof(bs:read(16))
+
+    for i=1,len do
+      local by = noeof(bs:read(8))
+      output(outstate, by)
+    end
+  elseif btype == BTYPE_FIXED_HUFFMAN or btype == BTYPE_DYNAMIC_HUFFMAN then
+    local littable, disttable
+    if btype == BTYPE_DYNAMIC_HUFFMAN then
+      littable, disttable = parse_huffmantables(bs)
+    else
+      littable  = HuffmanTable {0,8, 144,9, 256,7, 280,8, 288,nil}
+      disttable = HuffmanTable {0,5, 32,nil}
+    end
+
+    repeat
+      local is_done = parse_compressed_item(
+        bs, outstate, littable, disttable)
+    until is_done
+  else
+    runtime_error 'unrecognized compression type'
+  end
+
+  return bfinal ~= 0
+end
+
+
+local function deflate(t)
+  local bs = get_bitstream(t.input)
+  local outbs = get_obytestream(t.output)
+  local outstate = make_outstate(outbs)
+
+  repeat
+    local is_final = parse_block(bs, outstate)
+  until is_final
+end
+M.deflate = deflate
+
+-- http://tools.ietf.org/html/rfc1952
+local function gunzip(t)
+  local bs = get_bitstream(t.input)
+  local outbs = get_obytestream(t.output)
+  local disable_crc = t.disable_crc
+  if disable_crc == nil then disable_crc = false end
+
+  parse_gzip_header(bs)
+
+  local data_crc32 = 0
+
+  deflate{input=bs, output=
+    disable_crc and outbs or
+      function(byte)
+        data_crc32 = crc32(byte, data_crc32)
+        outbs(byte)
+      end
+  }
+
+  bs:read(bs:nbits_left_in_byte())
+
+  local expected_crc32 = bs:read(32)
+  local isize = bs:read(32) -- ignored
+  if DEBUG then
+    debug('crc32=', expected_crc32)
+    debug('isize=', isize)
+  end
+  if not disable_crc and data_crc32 then
+    if data_crc32 ~= expected_crc32 then
+      runtime_error('invalid compressed data--crc error')
+    end    
+  end
+  if bs:read() then
+    warn 'trailing garbage ignored'
+  end
+end
+M.gunzip = gunzip
+
+-- adler32 checksum
+-- see ADLER32 in http://tools.ietf.org/html/rfc1950 .
+local function adler32(byte, crc)
+  local s1 = crc % 65536
+  local s2 = (crc - s1) / 65536
+  s1 = (s1 + byte) % 65521
+  s2 = (s2 + s1) % 65521
+  return s2*65536 + s1
+end -- 65521 is the largest prime smaller than 2^16
+
+-- http://tools.ietf.org/html/rfc1950
+local function inflate_zlib(t)
+  local bs = get_bitstream(t.input)
+  local outbs = get_obytestream(t.output)
+  local disable_crc = t.disable_crc
+  if disable_crc == nil then disable_crc = false end
+  
+  local window_size = parse_zlib_header(bs)
+  
+  local data_adler32 = 1
+  
+  deflate{input=bs, output=
+    disable_crc and outbs or
+      function(byte)
+        data_adler32 = adler32(byte, data_adler32)
+        outbs(byte)
+      end
+  }
+
+  bs:read(bs:nbits_left_in_byte())
+  
+  local b3 = bs:read(8)
+  local b2 = bs:read(8)
+  local b1 = bs:read(8)
+  local b0 = bs:read(8)
+  local expected_adler32 = ((b3*256 + b2)*256 + b1)*256 + b0
+  if DEBUG then
+    debug('alder32=', expected_adler32)
+  end
+  if not disable_crc then
+    if data_adler32 ~= expected_adler32 then
+      runtime_error('invalid compressed data--crc error')
+    end    
+  end
+  if bs:read() then
+    warn 'trailing garbage ignored'
+  end
+end
+M.inflate_zlib = inflate_zlib
+
+return M
+
+--[[
+LICENSE
+
+Copyright (C) 2008, David Manura.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+(end license)
+--]]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AdvTiledLoader/external/gunziplua.lua	Sat Dec 15 13:10:41 2012 +0100
@@ -0,0 +1,161 @@
+-- bin.gunzip
+-- gunzip command partially reimplemented in Lua.
+--
+-- Note: this does not implement all of the GNU
+-- gunzip[1] command-line options and might have
+-- slightly different behavior.
+--
+-- This is designed to be called from a shell script:
+--
+--   #!/bin/env lua
+--   package.path = '?.lua;' .. package.path
+--   require 'dmlib.command_gunzip' (...)
+--
+-- References
+--
+--   [1] http://www.gnu.org/software/gzip/
+--
+-- (c) 2008 David Manura.  Licensed under the same terms as Lua (MIT).
+
+local assert = assert
+local error = error
+local ipairs = ipairs
+local require = require
+local xpcall = xpcall
+local type = type
+local io = io
+local os = os
+local string = string
+local debug = require "debug"
+local debug_traceback = debug.traceback
+local _G = _G
+
+local DEFLATE = require( TILED_LOADER_PATH .. "external.deflatelua" )
+
+local OptionParser = require( TILED_LOADER_PATH .. "external.optparse" ) . OptionParser
+
+local version = '0.1'
+
+
+local function runtime_assert(val, msg)
+  if not val then error({msg}, val) end
+  return val
+end
+
+
+local function runtime_error(s, level)
+  level = level or 1
+  error({s}, level+1)
+end
+
+
+local function file_exists(filename)
+  local fh = io.open(filename)
+  if fh then fh:close(); return true end
+  return false
+end
+
+
+-- Run gunzip command, given command-line arguments.
+local function call(...)
+  local opt = OptionParser{usage="%prog [options] [gzip-file...]",
+                           version=string.format("gunzip %s", version),
+                           add_help_option=false}
+  opt.add_option{"-h", "--help", action="store_true", dest="help",
+                 help="give this help"}
+  opt.add_option{
+    "-c", "--stdout", dest="stdout", action="store_true",
+    help="write on standard output, keep original files unchanged"}
+  opt.add_option{
+    "-f", "--force", dest="force", action="store_true",
+    help="force overwrite of output file"}
+  opt.add_option{
+    "--disable-crc", dest="disable_crc", action="store_true",
+    help="skip CRC check (faster performance)"}
+
+
+  local options, args = opt.parse_args()
+
+  local gzipfiles = args
+
+  if options.help then
+    opt.print_help()
+    os.exit()
+  end
+
+  local ok, err = xpcall(function()
+    local outfile_of = {}
+    local out_of = {}
+
+    for _,gzipfile in ipairs(gzipfiles) do
+      local base = gzipfile:match('(.+)%.[gG][zZ]$')
+      if not base then
+        runtime_error(gzipfile .. ': unknown suffix')
+      end
+      outfile_of[gzipfile] = base
+
+      out_of[gzipfile] =
+        (options.stdout or not gzipfile) and assert(io.stdout)
+        or outfile_of[gzipfile]
+
+      if type(out_of[gzipfile]) == 'string' then
+        if file_exists(out_of[gzipfile]) then
+          io.stderr:write(out_of[gzipfile] ..
+            ' already exists; do you wish to overwrite(y or n)? ')
+          if not io.stdin:read'*l':match'^[yY]' then
+            runtime_error 'not overwritten'
+          end
+        end
+      end
+    end
+
+    for _,gzipfile in ipairs(gzipfiles) do
+      local fh = gzipfile and runtime_assert(io.open(gzipfile, 'rb'))
+                 or assert(io.stdin)
+      local ofh = type(out_of[gzipfile]) == 'string' and
+        runtime_assert(io.open(out_of[gzipfile], 'wb'))
+        or out_of[gzipfile]
+
+      DEFLATE.gunzip {input=fh, output=ofh,
+        disable_crc=options.disable_crc}
+    end
+
+    for _,gzipfile in ipairs(gzipfiles) do
+      assert(os.remove(gzipfile))
+    end
+
+  end, debug_traceback)
+  if not ok then
+    if type(err) == 'table' then err = err[1] end
+    io.stderr:write('error: ' .. err, '\n')
+  end
+end
+
+
+return call
+
+--[[
+LICENSE
+
+Copyright (C) 2008, David Manura.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+(end license)
+--]]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AdvTiledLoader/external/numberlua.lua	Sat Dec 15 13:10:41 2012 +0100
@@ -0,0 +1,91 @@
+-- dmlib.bit
+-- Bitwise operations implemented entirely in Lua.
+-- Represents bit arrays as non-negative Lua numbers.[1]
+-- Can represent 32-bit bit arrays, when Lua is compiled
+-- with lua_Number being double-precision IEEE 754 floating point.
+--
+-- Based partly on Roberto Ierusalimschy's post
+-- in http://lua-users.org/lists/lua-l/2002-09/msg00134.html .
+--
+-- References
+--
+--   [1] http://lua-users.org/wiki/FloatingPoint
+--
+-- (c) 2008 David Manura.  Licensed under the same terms as Lua (MIT).
+
+
+local function memoize(f)
+  local mt = {}
+  local t = setmetatable({}, mt)
+  function mt:__index(k)
+    local v = f(k); t[k] = v
+    return v
+  end
+  return t
+end
+
+local function make_bitop_uncached(t, m)
+  local function bitop(a, b)
+    local res,p = 0,1
+    while a ~= 0 and b ~= 0 do
+      local am, bm = a%m, b%m
+      res = res + t[am][bm]*p
+      a = (a - am) / m
+      b = (b - bm) / m
+      p = p*m
+    end
+    res = res + (a+b)*p
+    return res
+  end
+  return bitop
+end
+
+local function make_bitop(t)
+  local op1 = make_bitop_uncached(t,2^1)
+  local op2 = memoize(function(a)
+    return memoize(function(b)
+      return op1(a, b)
+    end)
+  end)
+  return make_bitop_uncached(op2, 2^(t.n or 1))
+end
+
+local bxor = make_bitop {[0]={[0]=0,[1]=1},[1]={[0]=1,[1]=0}, n=4}
+local F8 = 2^32 - 1
+local function bnot(a)   return F8 - a end
+local function band(a,b) return ((a+b) - bxor(a,b))/2 end
+local function bor(a,b)  return F8 - band(F8 - a, F8 - b) end
+
+local M = {}
+M.bxor = bxor
+M.bnot = bnot
+M.band = band
+M.bor = bor
+
+return M
+
+--[[
+LICENSE
+
+Copyright (C) 2008, David Manura.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+(end license)
+--]]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AdvTiledLoader/external/optparse.lua	Sat Dec 15 13:10:41 2012 +0100
@@ -0,0 +1,158 @@
+-- lua-optparse [1]
+-- Lua-based partial reimplementation of Python's optparse[2-3] command-line
+-- parsing module.
+--
+-- Note: Python also supports getopt[4].
+--
+-- References
+--  [1] http://lua-users.org/wiki/CommandLineParsing
+--  [2] http://docs.python.org/lib/optparse-defining-options.html
+--  [3] http://blog.doughellmann.com/2007/08/pymotw-optparse.html
+--  [4] http://docs.python.org/lib/module-getopt.html
+--
+-- (c) 2008 David Manura.  Licensed under the same terms as Lua (MIT).
+
+local ipairs = ipairs
+local unpack = unpack
+local io = io
+local table = table
+local os = os
+local arg = arg
+local _G = _G
+
+
+local function OptionParser(t)
+  local usage = t.usage
+  local version = t.version
+
+  local o = {}
+  local option_descriptions = {}
+  local option_of = {}
+
+  function o.fail(s) -- extension
+    io.stderr:write(s .. '\n')
+    os.exit(1)
+  end
+
+  function o.add_option(optdesc)
+    option_descriptions[#option_descriptions+1] = optdesc
+    for _,v in ipairs(optdesc) do
+      option_of[v] = optdesc
+    end
+  end
+  function o.parse_args()
+    -- expand options (e.g. "--input=file" -> "--input", "file")
+    local arg = {unpack(arg)}
+    for i=#arg,1,-1 do local v = arg[i]
+      local flag, val = v:match('^(%-%-%w+)=(.*)')
+      if flag then
+        arg[i] = flag
+        table.insert(arg, i+1, val)
+      end
+    end
+
+    local options = {}
+    local args = {}
+    local i = 1
+    while i <= #arg do local v = arg[i]
+      local optdesc = option_of[v]
+      if optdesc then
+        local action = optdesc.action
+        local val
+        if action == 'store' or action == nil then
+          i = i + 1
+          val = arg[i]
+          if not val then o.fail('option requires an argument ' .. v) end
+        elseif action == 'store_true' then
+          val = true
+        elseif action == 'store_false' then
+          val = false
+        end
+        options[optdesc.dest] = val
+      else
+        if v:match('^%-') then o.fail('invalid option ' .. v) end
+        args[#args+1] = v
+      end
+      i = i + 1
+    end
+    if options.help then
+      o.print_help()
+      os.exit()
+    end
+    if options.version then
+      io.stdout:write(t.version .. "\n")
+      os.exit()
+    end
+    return options, args
+  end
+
+  local function flags_str(optdesc)
+    local sflags = {}
+    local action = optdesc.action
+    for _,flag in ipairs(optdesc) do
+      local sflagend
+      if action == nil or action == 'store' then
+        local metavar = optdesc.metavar or optdesc.dest:upper()
+        sflagend = #flag == 2 and ' ' .. metavar
+                              or  '=' .. metavar
+      else
+        sflagend = ''
+      end
+      sflags[#sflags+1] = flag .. sflagend
+    end
+    return table.concat(sflags, ', ')
+  end
+
+  function o.print_help()
+    io.stdout:write("Usage: " .. usage:gsub('%%prog', arg[0]) .. "\n")
+    io.stdout:write("\n")
+    io.stdout:write("Options:\n")
+    for _,optdesc in ipairs(option_descriptions) do
+      io.stdout:write("  " .. flags_str(optdesc) ..
+                      "  " .. optdesc.help .. "\n")
+    end
+  end
+  if t.add_help_option == nil or t.add_help_option == true then
+    o.add_option{"--help", action="store_true", dest="help",
+                 help="show this help message and exit"}
+  end
+  if t.version then
+    o.add_option{"--version", action="store_true", dest="version",
+                 help="output version info."}
+  end
+  return o
+end
+
+
+local t = {}
+t.OptionParser = OptionParser
+
+
+return t
+
+
+--[[
+LICENSE
+
+Copyright (C) 2008, David Manura.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+(end license)
+--]]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AdvTiledLoader/external/xml.lua	Sat Dec 15 13:10:41 2012 +0100
@@ -0,0 +1,129 @@
+-- XML parser from http://lua-users.org/wiki/LuaXml
+
+local escaped = {
+  ['&quot;'] = '"',
+  ['&amp;'] = '&',
+  ['&apos;'] = "'",
+  ['&lt;'] = '<',
+  ['&gt;'] = '>'
+}
+
+local xml = {}
+
+---------------------------------------------------------------------------------------------------
+local function args_to_table(s)
+  local arg = {}
+  string.gsub(s, "(%w+)=([\"'])(.-)%2", function (w, _, a)
+    for f, t in pairs(escaped) do
+      a = string.gsub(a, f, t)
+    end
+    arg[w] = a
+  end)
+  return arg
+end
+    
+---------------------------------------------------------------------------------------------------
+function xml.string_to_table(s)
+  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=args_to_table(xarg), empty=1})
+    elseif c == "" then   -- start tag
+      top = {label=label, xarg=args_to_table(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].label)
+  end
+  return stack[1]
+end
+
+---------------------------------------------------------------------------------------------------
+local function args_to_string(args)
+    if not args then return "" end
+    local str = ""
+    for k,v in pairs(args) do
+        str = str .. string.format(' %s="%s"', k, tostring(v))
+    end
+    return str
+end
+
+---------------------------------------------------------------------------------------------------
+local function expand(t, indent)
+    indent = indent .. "    "
+    local str = {}
+    for i = 1,#t do
+        if type(t[i]) == "table" then
+            str[#str+1] = xml.table_to_string(t[i], indent)
+        else
+            str[#str+1] = indent
+            str[#str+1] = t[i]
+            str[#str+1] = "\n"
+        end
+    end
+    return str
+end
+
+---------------------------------------------------------------------------------------------------
+local function flatten (t)
+  local n = { }
+  local function flatten_aux(t)
+    for k, v in ipairs(t) do
+      if type(v) == "table" then
+        flatten_aux(v)
+      else
+        n[#n+1] = v
+      end
+    end
+  end
+  flatten_aux(t)
+  return n
+end
+
+---------------------------------------------------------------------------------------------------
+function xml.table_to_string(t, indent)
+    if not indent then indent = "" end
+    local label = t.label
+    local xarg = args_to_string(t.xarg)
+    local str = {}
+    if #t > 0 then
+        local body = expand(t,indent)
+        str = {indent, "<", label, xarg, ">\n", body, indent, "</", label, ">\n"}
+    else
+        str = {indent, "<", label, xarg, "/>\n"}
+    end
+    if indent == "" then
+        return table.concat(flatten(str))
+    else
+        return str
+    end
+end
+
+return xml
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AdvTiledLoader/init.lua	Sat Dec 15 13:10:41 2012 +0100
@@ -0,0 +1,38 @@
+TILED_LOADER_PATH = TILED_LOADER_PATH or ({...})[1]:gsub("[%.\\/]init$", "") .. '.'
+
+-- Return the classes in a table
+return {
+    Map = require(TILED_LOADER_PATH  .. "Map"),
+    TileLayer = require(TILED_LOADER_PATH  .. "TileLayer"),
+    Tile = require(TILED_LOADER_PATH  .. "Tile"),
+    TileSet = require(TILED_LOADER_PATH  .. "TileSet"),
+    Object = require(TILED_LOADER_PATH  .. "Object"),
+    ObjectLayer = require(TILED_LOADER_PATH  .. "ObjectLayer"),
+    Loader = require(TILED_LOADER_PATH  .. "Loader"),
+    Grid = require(TILED_LOADER_PATH .. "Grid"),
+}
+
+
+--[[Copyright (c) 2011-2012 Casey Baxter
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+Except as contained in this notice, the name(s) of the above copyright holders
+shall not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.--]]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/level01.tmx	Sat Dec 15 13:10:41 2012 +0100
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" orientation="orthogonal" width="21" height="16" tilewidth="48" tileheight="48">
+ <tileset firstgid="1" name="tiles" tilewidth="48" tileheight="48">
+  <image source="tiles.png" width="128" height="128"/>
+ </tileset>
+ <layer name="Tile Layer 1" width="21" height="16">
+  <data encoding="base64" compression="zlib">
+   eJxjZGBgYByCmAkPJqQOXZ4YMwmZQ6zbyDWTkjCidbgTspOc8BqMZpKjn1pmkptWSDWT3LRKLT245PHpI8VMamMAsJQB1A==
+  </data>
+ </layer>
+</map>
--- a/main.lua	Sat Dec 15 10:23:56 2012 +0100
+++ b/main.lua	Sat Dec 15 13:10:41 2012 +0100
@@ -1,22 +1,59 @@
+local ATL = require("AdvTiledLoader") 
 local anim8 = require("anim8") 
 local sprite = require("sprite") 
 
 local sprites = {}
+local shadows = {}
+local map
+local tx, ty = 0, 0
+local DX = {0, 1, 0, -1}
+local DY = {-1, 0, 1, 0}
+local villain
+
+function newVillain(x, y, image)
+    local grid = anim8.newGrid(64, 64, 128, 128)
+    local villain = sprite.newSprite(
+        x, y,
+        image,
+        anim8.newAnimation('loop', grid('1-2,1'), 0.25)
+    )
+    function villain:walk(dt, dir)
+        self.x = self.x + DX[dir] * dt * 250
+        self.y = self.y + DY[dir] * dt * 250
+    end
+    return villain
+end
+
+function newCivilian(x, y, image)
+    local grid = anim8.newGrid(64, 64, 128, 128)
+    local civilian = sprite.newSprite(
+        x, y,
+        image,
+        anim8.newAnimation('loop', grid('1-2,1'), 0.25)
+    )
+    return civilian
+end
 
 function love.load()
-    local VILLAIN_IMAGE = love.graphics.newImage('villain.png')
-    local CIVILIAN_IMAGE = love.graphics.newImage('civilian.png')
-    local grid = anim8.newGrid(64, 64, 128, 128)
-    table.insert(sprites, sprite.newSprite(
-        255,
-        255,
-        VILLAIN_IMAGE,
-        anim8.newAnimation('loop', grid('1-2,1'), 0.25)
-    ))
+    local shadow_image = love.graphics.newImage('shadow.png')
+    local civilian_image = love.graphics.newImage('civilian.png')
+    local villain_image = love.graphics.newImage('villain.png')
+    villain = newVillain(48, 48, villain_image)    
+    sprites[1] = villain
+    shadows[1] = sprite.newShadow(shadow_image, villain)
+    sprites[2] = newCivilian(128, 64, civilian_image)
+    shadows[2] = sprite.newShadow(shadow_image, sprites[2])
+    map = ATL.Loader.load('level01.tmx') 
 end
 
 
 function love.draw()
+    love.graphics.translate(math.floor(tx), math.floor(ty))
+    map:autoDrawRange(math.floor(tx), math.floor(ty), 1, 0)
+    map:draw()
+    for k, shadow in pairs(shadows) do
+        shadow:draw()
+    end
     for k, sprite in pairs(sprites) do
         sprite:draw()
     end
@@ -24,6 +61,26 @@
 
 
 function love.update(dt)
+    local scroll_speed = 250
+    local scroll_margin = 64
+    if love.keyboard.isDown("up") then villain:walk(dt, 1) end
+    if love.keyboard.isDown("right") then villain:walk(dt, 2) end
+    if love.keyboard.isDown("down") then villain:walk(dt, 3) end
+    if love.keyboard.isDown("left") then villain:walk(dt, 4) end
+    if villain.x + tx < scroll_margin then
+        tx = tx + dt * scroll_speed
+    else
+        if villain.x + tx > 800 - 64 - scroll_margin then
+            tx = tx - dt * scroll_speed
+        end
+    end
+    if villain.y + ty < scroll_margin then
+        ty = ty + dt * scroll_speed
+    else
+        if villain.y + ty > 600 - 64 - scroll_margin then
+            ty = ty - dt * scroll_speed
+        end
+    end
     for k, sprite in pairs(sprites) do
         sprite:update(dt)
     end
Binary file shadow.png has changed
--- a/sprite.lua	Sat Dec 15 10:23:56 2012 +0100
+++ b/sprite.lua	Sat Dec 15 13:10:41 2012 +0100
@@ -1,5 +1,4 @@
 local Sprite = {}
-local Sprite_mt = { __index = Sprite }
 
 local function newSprite(x, y, image, animation)
     local sprite = setmetatable({
@@ -7,7 +6,7 @@
         y = y,
         image = image,
         animation = animation,
-    }, Sprite_mt)
+    }, { __index = Sprite })
     return sprite
 end
 
@@ -19,8 +18,27 @@
     self.animation:update(dt)
 end
 
+
+local Shadow = {}
+
+function newShadow(image, sprite)
+    return setmetatable({
+        image = image,
+        sprite = sprite,
+    }, { __index = Shadow })
+end
+
+function Shadow:draw()
+    love.graphics.draw(self.image, self.sprite.x, self.sprite.y + 8)
+end
+
+function Shadow:update(dt)
+end
+
+
 local sprite = {
     newSprite = newSprite,
+    newShadow = newShadow,
 }
 
 return sprite
Binary file tiles.png has changed
Binary file villain.png has changed