From 6eacf393b370a7586276108705b805e222a069a5 Mon Sep 17 00:00:00 2001 From: cursed22bc Date: Sat, 28 Feb 2026 12:38:40 +0200 Subject: tile mapping and drawing --- tilemap.lua | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 tilemap.lua (limited to 'tilemap.lua') diff --git a/tilemap.lua b/tilemap.lua new file mode 100644 index 0000000..64ca520 --- /dev/null +++ b/tilemap.lua @@ -0,0 +1,192 @@ +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 -- cgit v1.2.3