local Entity = require("entity") local Liquid = require("liquid") local LiquidSurface = require("liquidSurface") 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, shape = obj.shape }) entity.polygon = obj.polygon entity.polyline = obj.polyline return entity end function Tilemap:new(mapPath, tilesets) local self = setmetatable({}, Tilemap) self.entitiesTiles = {} self.entitiesSpawns = {} self.entitiesCameraBorders = {} self.entitiesLiquidPolygons = {} self.entitiesLiquidSurfaces = {} self.layerBackground = nil self.layerGround = nil self.layerForeground = nil self.tileWidth = 8 self.tileHeight = 8 self.mapWidth = 0 self.mapHeight = 0 self.tileGidInfo = {} self.layerTileDimensions = {} local mapData = loadMapData(mapPath) self.mapData = mapData self.tileWidth = mapData and (mapData.tilewidth or 8) or 8 self.tileHeight = mapData and (mapData.tileheight or 8) or 8 self.mapWidth = (mapData and mapData.width or 0) * self.tileWidth self.mapHeight = (mapData and mapData.height or 0) * self.tileHeight if mapData and mapData.tilesets then for _, ts in ipairs(mapData.tilesets) do local info = tilesets[ts.name] if info then local ok, img = pcall(love.graphics.newImage, info.path) if ok and img then local tw = info.tilewidth or self.tileWidth local th = info.tileheight or self.tileHeight local imgW, imgH = img:getDimensions() 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 local gid = ts.firstgid + index local quad = love.graphics.newQuad(qx, qy, tw, th, imgW, imgH) self.tileGidInfo[gid] = { image = img, quad = quad, tilewidth = tw, tileheight = th } end end 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 local dims = nil if layer.data then for _, gid in ipairs(layer.data) do if gid and gid ~= 0 then local gi = self.tileGidInfo[gid] if gi then dims = { tilewidth = gi.tilewidth, tileheight = gi.tileheight } break end end end end self.layerTileDimensions[n] = dims or { tilewidth = self.tileWidth, tileheight = self.tileHeight } 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) elseif name == "liquid" then table.insert(self.entitiesLiquidPolygons, Liquid:new(entity)) elseif name == "liquid_surface" then table.insert(self.entitiesLiquidSurfaces, LiquidSurface:new(entity)) end end end end end return self end function Tilemap:getEntitiesLiquidPolygons() return self.entitiesLiquidPolygons end function Tilemap:getEntitiesLiquidSurfaces() return self.entitiesLiquidSurfaces end function Tilemap:getEntitiesTiles() return self.entitiesTiles end function Tilemap:getEntitiesSpawns() return self.entitiesSpawns end function Tilemap:getEntitiesCameraBorders() return self.entitiesCameraBorders end 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:getLayerTileDimensions(layerName) return self.layerTileDimensions[layerName:lower()] or { tilewidth = self.tileWidth, tileheight = self.tileHeight } end function Tilemap:getMapWidth() return self.mapWidth end function Tilemap:getMapHeight() return self.mapHeight end function Tilemap:getTileGidInfo() return self.tileGidInfo end return Tilemap