local Entity = require("entity") local Tilemap = {} Tilemap.__index = Tilemap local function loadMapData(mapPath) if type(mapPath) ~= "string" then return mapPath end if love and love.filesystem and love.filesystem.load then local chunk, err = love.filesystem.load(mapPath) if not chunk then error("Tilemap: failed to load '" .. tostring(mapPath) .. "': " .. tostring(err)) end return chunk() end local mod = mapPath:gsub("%.lua$", ""):gsub("/", ".") local ok, data = pcall(require, mod) if not ok then error("Tilemap: failed to load '" .. tostring(mapPath) .. "': " .. tostring(data)) end return data end local function objectToEntity(obj) local x = obj.x or 0 local y = obj.y or 0 local w = obj.width or 0 local h = obj.height or 0 local entity = Entity:new(x, y, w, h) entity:setPropertiesFromOptions({ properties = obj.properties or {}, name = obj.name, type = obj.type, id = obj.id, rotation = obj.rotation, visible = obj.visible }) return entity end function Tilemap:new(map) local self = setmetatable({}, Tilemap) self.entitiesTiles = {} self.entitiesSpawns = {} self.entitiesCameraBorders = {} self.layerBackground = nil self.layerGround = nil self.layerForeground = nil self.tileWidth = 16 self.tileHeight = 16 self.mapWidth = 0 self.mapHeight = 0 self.tilesetImage = nil self.tileQuads = {} local mapData = loadMapData(map) self.mapData = mapData self.tileWidth = mapData and (mapData.tilewidth or 16) or 16 self.tileHeight = mapData and (mapData.tileheight or 16) or 16 self.mapWidth = (mapData and mapData.width or 0) * self.tileWidth self.mapHeight = (mapData and mapData.height or 0) * self.tileHeight if type(map) == "string" then local basePath = map:gsub("%.lua$", ""):gsub("%.tmx$", "") local imagePath = basePath .. ".png" local ok, img = pcall(love.graphics.newImage, imagePath) if ok and img then self.tilesetImage = img local firstGid = (mapData.tilesets and mapData.tilesets[1] and mapData.tilesets[1].firstgid) or 1 local imgW, imgH = img:getDimensions() local tw, th = self.tileWidth, self.tileHeight local cols = math.floor(imgW / tw) local rows = math.floor(imgH / th) for index = 0, (cols * rows) - 1 do local qx = (index % cols) * tw local qy = math.floor(index / cols) * th self.tileQuads[firstGid + index] = love.graphics.newQuad(qx, qy, tw, th, imgW, imgH) end end end if mapData and mapData.layers then for _, layer in ipairs(mapData.layers) do if layer.type == "tilelayer" and layer.name then local n = layer.name:lower() if n == "background" then self.layerBackground = layer elseif n == "ground" then self.layerGround = layer elseif n == "foreground" then self.layerForeground = layer end elseif layer.type == "objectgroup" and layer.objects then local name = (layer.name or ""):gsub("%s+", "_"):lower() for _, obj in ipairs(layer.objects) do local entity = objectToEntity(obj) if name == "tiles" or name == "tile" then table.insert(self.entitiesTiles, entity) elseif name == "spawn" then table.insert(self.entitiesSpawns, entity) elseif name == "camera_border" then table.insert(self.entitiesCameraBorders, entity) end end end end end return self end function Tilemap:getEntitiesTiles() return self.entitiesTiles end function Tilemap:getEntitiesSpawns() return self.entitiesSpawns end function Tilemap:getEntitiesCameraBorders() return self.entitiesCameraBorders end -- Returns minX, maxX, minY, maxY from camera_border entities for use with Camera:setLimits. -- Returns nil, nil, nil, nil if there are no camera border entities. function Tilemap:getCameraLimits() local borders = self.entitiesCameraBorders if not borders or #borders == 0 then return nil, nil, nil, nil end local minX, maxX = math.huge, -math.huge local minY, maxY = math.huge, -math.huge for _, e in ipairs(borders) do local x, y = e.x or 0, e.y or 0 local w, h = e.width or 0, e.height or 0 minX = math.min(minX, x) maxX = math.max(maxX, x + w) minY = math.min(minY, y) maxY = math.max(maxY, y + h) end return minX, maxX, minY, maxY end function Tilemap:getMapData() return self.mapData end function Tilemap:getBackgroundLayer() return self.layerBackground end function Tilemap:getGroundLayer() return self.layerGround end function Tilemap:getForegroundLayer() return self.layerForeground end function Tilemap:getLayers() return { background = self.layerBackground, ground = self.layerGround, foreground = self.layerForeground } end function Tilemap:getTileWidth() return self.tileWidth end function Tilemap:getTileHeight() return self.tileHeight end function Tilemap:getMapWidth() return self.mapWidth end function Tilemap:getMapHeight() return self.mapHeight end function Tilemap:getTilesetImage() return self.tilesetImage end function Tilemap:getTileQuads() return self.tileQuads end return Tilemap