local Tilemap = require("tilemap") local Entity = require("entity") local Camera = require("camera") local World = {} World.__index = World local GRAVITY = 9.81 * 64 local PHYSICS_DT = 1 / 60 function World:new() local self = setmetatable({}, World) self.physicsWorld = nil self.tilemap = nil self.mapData = nil self.groundEntities = {} self.player = nil self.enemies = {} self.entities = {} self.camera = nil return self end function World:load(levelPath) if self.physicsWorld then self.groundEntities = {} end self.physicsWorld = love.physics.newWorld(0, GRAVITY) self.physicsWorld:setCallbacks(nil, nil, nil, nil) self.tilemap = Tilemap:new(levelPath) self.mapData = self.tilemap:getMapData() if not self.mapData then error("World:load - no map data from " .. tostring(levelPath)) end local tileWidth = self.tilemap:getTileWidth() local tileHeight = self.tilemap:getTileHeight() local groundLayer = self.tilemap:getGroundLayer() if groundLayer and groundLayer.data then local w = self.mapData.width or 0 local data = groundLayer.data for i, gid in ipairs(data) do if gid and gid ~= 0 then local idx = i - 1 local tx = idx % w local ty = math.floor(idx / w) local x = tx * tileWidth local y = ty * tileHeight local tileEntity = Entity:new(x, y, tileWidth, tileHeight) tileEntity:enablePhysics(self.physicsWorld, "static") table.insert(self.groundEntities, tileEntity) end end end self.player = nil self.enemies = {} self.entities = {} local spawns = self.tilemap:getEntitiesSpawns() for _, spawn in ipairs(spawns) do local entityType = spawn:get("entity", "") if entityType == "Player" and not self.player then self.player = Entity:new(spawn.x, spawn.y, spawn.width, spawn.height) self.player:enablePhysics(self.physicsWorld, "dynamic") self.player.isPlayer = true table.insert(self.entities, self.player) else local e = Entity:new(spawn.x, spawn.y, spawn.width, spawn.height) e:setPropertiesFromOptions({ properties = spawn.properties or {}, name = spawn.name, type = spawn.type }) if entityType ~= "" then table.insert(self.enemies, e) end table.insert(self.entities, e) end end end function World:setCamera(camera) self.camera = camera self.camera:setLimits(self.tilemap:getCameraLimits()) end function World:getPlayer() return self.player end function World:getPhysicsWorld() return self.physicsWorld end function World:getMapData() return self.mapData end local function drawTileLayer(layer, tileWidth, tileHeight, tilesetImage, tileQuads) if not layer or not layer.visible or not layer.data then return end local w = layer.width or 0 local data = layer.data for i, gid in ipairs(data) do if gid and gid ~= 0 then local idx = i - 1 local tx = idx % w local ty = math.floor(idx / w) local x = tx * tileWidth local y = ty * tileHeight if tilesetImage and tileQuads and tileQuads[gid] then love.graphics.draw(tilesetImage, tileQuads[gid], x, y) else local r = ((gid * 17) % 256) / 255 local g = ((gid * 31 + 50) % 256) / 255 local b = ((gid * 47 + 100) % 256) / 255 love.graphics.setColor(r, g, b, 1) love.graphics.rectangle("fill", x, y, tileWidth, tileHeight) love.graphics.setColor(1, 1, 1, 1) end end end end function World:draw() local tw = self.tilemap:getTileWidth() local th = self.tilemap:getTileHeight() local tilesetImage = self.tilemap:getTilesetImage() local tileQuads = self.tilemap:getTileQuads() drawTileLayer(self.tilemap:getBackgroundLayer(), tw, th, tilesetImage, tileQuads) drawTileLayer(self.tilemap:getGroundLayer(), tw, th, tilesetImage, tileQuads) for _, e in ipairs(self.entities) do if e.draw then e:draw() else World.drawEntityDefault(e) end end drawTileLayer(self.tilemap:getForegroundLayer(), tw, th, tilesetImage, tileQuads) end -- TODO remove. draw method handled by each entity function World.drawEntityDefault(entity) love.graphics.setColor(0.2, 0.6, 1, 1) if entity.isPlayer then love.graphics.setColor(1, 0.3, 0.2, 1) end love.graphics.rectangle("fill", entity.x, entity.y, entity.width, entity.height) love.graphics.setColor(1, 1, 1, 1) end function World:update(dt) if not self.physicsWorld then return end self.physicsWorld:update(PHYSICS_DT) if self.camera then self.camera:setTarget(self.player) self.camera:update(dt) end for _, e in ipairs(self.entities) do if e.body then e:syncFromPhysicsBody() end if e.update then e:update(dt) end end end return World