summaryrefslogtreecommitdiff
path: root/world.lua
diff options
context:
space:
mode:
authorcursed22bc <admin@pixeldawn.org>2026-03-01 12:30:05 +0200
committercursed22bc <admin@pixeldawn.org>2026-03-01 12:30:05 +0200
commitde94226e1b302cee2a006f78f0153aa5fa081f47 (patch)
tree31859ec7de767dafe5e7474e14f74f473eab6ba8 /world.lua
parent8f9182baff36d33c07c5eb8df795e3cfcd5307a5 (diff)
player and animation
Diffstat (limited to 'world.lua')
-rw-r--r--world.lua159
1 files changed, 144 insertions, 15 deletions
diff --git a/world.lua b/world.lua
index 6890e60..b9ba01f 100644
--- a/world.lua
+++ b/world.lua
@@ -1,5 +1,6 @@
local Tilemap = require("tilemap")
local Entity = require("entity")
+local Player = require("player")
local Camera = require("camera")
local World = {}
@@ -18,6 +19,10 @@ function World:new()
self.enemies = {}
self.entities = {}
self.camera = nil
+ self.groundContacts = {}
+ self.contactCounts = {}
+ self.contactEntity = {}
+ self.contactKind = {}
return self
end
@@ -26,7 +31,17 @@ function World:load(mapPath, tilesetPath)
self.groundEntities = {}
end
self.physicsWorld = love.physics.newWorld(0, GRAVITY)
- self.physicsWorld:setCallbacks(nil, nil, nil, nil)
+ self.physicsWorld:setCallbacks(
+ function(fa, fb, contact)
+ local udA, udB = fa:getUserData(), fb:getUserData()
+ local nx, ny = contact:getNormal()
+ self:_onBeginContact(udA, udB, nx, ny, contact)
+ end,
+ function(fa, fb, contact)
+ local udA, udB = fa:getUserData(), fb:getUserData()
+ self:_onEndContact(udA, udB, contact)
+ end
+ )
self.tilemap = Tilemap:new(mapPath, tilesetPath)
self.mapData = self.tilemap:getMapData()
@@ -39,18 +54,38 @@ function World:load(mapPath, tilesetPath)
local groundLayer = self.tilemap:getGroundLayer()
if groundLayer and groundLayer.data then
- local w = self.mapData.width or 0
+ local mapW = self.mapData.width or 0
+ local mapH = self.mapData.height 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)
+
+ local solid = {}
+ for row = 0, mapH - 1 do
+ solid[row] = {}
+ for col = 0, mapW - 1 do
+ local idx = row * mapW + col + 1
+ local gid = data[idx]
+ solid[row][col] = (gid and gid ~= 0)
+ end
+ end
+
+ for row = 0, mapH - 1 do
+ local col = 0
+ while col < mapW do
+ if solid[row][col] then
+ local startCol = col
+ while col < mapW and solid[row][col] do
+ col = col + 1
+ end
+ local stripWidth = (col - startCol) * tileWidth
+ local x = startCol * tileWidth
+ local y = row * tileHeight
+ local strip = Entity:new(x, y, stripWidth, tileHeight)
+ strip:enablePhysics(self.physicsWorld, "static")
+ strip.fixture:setUserData("ground")
+ table.insert(self.groundEntities, strip)
+ else
+ col = col + 1
+ end
end
end
end
@@ -62,8 +97,7 @@ function World:load(mapPath, tilesetPath)
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 = Player.new(self.physicsWorld, spawn.x, spawn.y)
self.player.isPlayer = true
table.insert(self.entities, self.player)
else
@@ -78,6 +112,82 @@ function World:load(mapPath, tilesetPath)
end
+function World:_addContact(entity, kind)
+ self.contactCounts[entity] = self.contactCounts[entity] or { floor = 0, wall = 0, ceiling = 0 }
+ self.contactCounts[entity][kind] = (self.contactCounts[entity][kind] or 0) + 1
+ entity.contact = entity.contact or { floor = 0, wall = 0, ceiling = 0 }
+ entity.contact[kind] = 1
+end
+
+function World:_removeContact(entity, kind)
+ self.contactCounts[entity] = self.contactCounts[entity] or { floor = 0, wall = 0, ceiling = 0 }
+ self.contactCounts[entity][kind] = math.max(0, (self.contactCounts[entity][kind] or 0) - 1)
+ entity.contact = entity.contact or { floor = 0, wall = 0, ceiling = 0 }
+ entity.contact[kind] = (self.contactCounts[entity][kind] > 0) and 1 or 0
+end
+
+function World:_normalToContactType(nx, ny)
+ local ax, ay = math.abs(nx), math.abs(ny)
+ if ay >= ax then
+ if ny < -0.3 then return "floor" end
+ if ny > 0.3 then return "ceiling" end
+ else
+ if ax > 0.3 then return "wall" end
+ end
+ return nil
+end
+
+function World:_isTrackedEntity(ud)
+ return type(ud) == "table" and (ud.body ~= nil or ud.x ~= nil)
+end
+
+function World:_isPlayerLike(ud)
+ return type(ud) == "table" and ud.jump ~= nil
+end
+
+function World:_onBeginContact(udA, udB, nx, ny, contact)
+ if udA == "ground" and self:_isTrackedEntity(udB) then
+ local kind = self:_normalToContactType(nx, ny)
+ if kind then
+ self:_addContact(udB, kind)
+ self.contactEntity[contact] = udB
+ self.contactKind[contact] = kind
+ end
+ self.groundContacts[udB] = (self.groundContacts[udB] or 0) + 1
+ if self:_isPlayerLike(udB) then udB.grounded = true end
+ elseif udB == "ground" and self:_isTrackedEntity(udA) then
+ local kind = self:_normalToContactType(-nx, -ny)
+ if kind then
+ self:_addContact(udA, kind)
+ self.contactEntity[contact] = udA
+ self.contactKind[contact] = kind
+ end
+ self.groundContacts[udA] = (self.groundContacts[udA] or 0) + 1
+ if self:_isPlayerLike(udA) then udA.grounded = true end
+ end
+end
+
+function World:_onEndContact(udA, udB, contact)
+ local ent = self.contactEntity[contact]
+ local kind = ent and self.contactKind[contact]
+ if ent and kind then
+ self:_removeContact(ent, kind)
+ self.contactEntity[contact] = nil
+ self.contactKind[contact] = nil
+ end
+ if udA == "ground" and self:_isTrackedEntity(udB) then
+ self.groundContacts[udB] = math.max(0, (self.groundContacts[udB] or 0) - 1)
+ if self:_isPlayerLike(udB) then
+ udB.grounded = (self.groundContacts[udB] or 0) > 0
+ end
+ elseif udB == "ground" and self:_isTrackedEntity(udA) then
+ self.groundContacts[udA] = math.max(0, (self.groundContacts[udA] or 0) - 1)
+ if self:_isPlayerLike(udA) then
+ udA.grounded = (self.groundContacts[udA] or 0) > 0
+ end
+ end
+end
+
function World:setCamera(camera)
self.camera = camera
self.camera:setLimits(self.tilemap:getCameraLimits())
@@ -134,10 +244,29 @@ function World:draw()
if e.draw then e:draw() else World.drawEntityDefault(e) end
end
+ if DEBUG then
+ World.drawPhysicsBodyOutlines(self.entities)
+ World.drawPhysicsBodyOutlines(self.groundEntities)
+ end
+
drawTileLayer(self.tilemap:getForegroundLayer(), tw, th, tilesetImage, tileQuads)
end
--- TODO remove. draw method handled by each entity
+function World.drawPhysicsBodyOutlines(entityList)
+ if not entityList then return end
+ love.graphics.setColor(0, 1, 0, 1)
+ for _, e in ipairs(entityList) do
+ local body = e.body
+ if body and e.physicsWidth and e.physicsHeight then
+ local cx, cy = body:getX(), body:getY()
+ local x = cx - e.physicsWidth / 2
+ local y = cy - e.physicsHeight / 2
+ love.graphics.rectangle("line", x, y, e.physicsWidth, e.physicsHeight)
+ end
+ end
+ love.graphics.setColor(1, 1, 1, 1)
+end
+
function World.drawEntityDefault(entity)
love.graphics.setColor(0.2, 0.6, 1, 1)
if entity.isPlayer then