physics = {} local asserts = require("shared.asserts") local utils = require("shared.utils") function physics.keyreleased_debug(key, _) if key == "kp4" then player.x = player.x -1 end if key == "kp6" then player.x = player.x +1 end if key == "kp8" then player.y = player.y -1 end if key == "kp2" then player.y = player.y +1 end end function physics.remove_solid(x, y, sizex, sizey) asserts.number("physics.remove_solid", 4, x, y, sizex, sizey) for coord_x=x, x + sizex-1 do for coord_y=y, y + sizey-1 do levelloop.Canvas.physics[coord_x*levelloop.world.x+coord_y] = nil end end end function physics.draw_solid(x, y, sizex, sizey) asserts.number("physics.draw_solid", 4, x, y, sizex, sizey) for coord_x=x, x + sizex-1 do for coord_y=y, y + sizey-1 do levelloop.Canvas.physics[coord_x*levelloop.world.x+coord_y] = {0, 0, 0, true } end end end function draw_colision(x, y, x2, y2, pixelx, pixely) utils.color_push() love.graphics.setLineWidth(1) love.graphics.setDefaultFilter("nearest", "nearest", 0) love.graphics.setCanvas(levelloop.Canvas.dbg2) if pixelx then love.graphics.setColor(.6, 0, .6, 1) love.graphics.rectangle("fill", x, y, x2, y2) love.graphics.setColor(1, 0, 0, 1) love.graphics.points(pixelx, pixely) else love.graphics.setColor(0, 1, 0, 1) love.graphics.rectangle("fill", x, y, x2, y2) end love.graphics.setCanvas() utils.color_pop() end function physics.is_blocked_x(x, y, sizex) x, y = math.floor(x), math.floor(y) asserts.number("physics.is_blocked_x", 3, x, y, sizex) for coord_x=x ,x + sizex -1 do if levelloop.Canvas.physics[coord_x * levelloop.world.x+y] and levelloop.Canvas.physics[coord_x*levelloop.world.x+y][4] then if PHYSICS_DEBUG then draw_colision(x, y, sizex, 1, coord_x, y) end return true end end if PHYSICS_DEBUG then draw_colision(x, y, sizex, 1) end return false end function physics.is_blocked_y(x, y, sizey) x, y = math.floor(x), math.floor(y) asserts.number("physics.is_blocked_y", 3, x, y, sizey) for coord_y=y, y + sizey -1 do if levelloop.Canvas.physics[x*levelloop.world.x+coord_y] and levelloop.Canvas.physics[x*levelloop.world.x+coord_y][4] then if PHYSICS_DEBUG then draw_colision(x, y, 1, sizey, x, coord_y) end return true end end if PHYSICS_DEBUG then draw_colision(x, y, 1, sizey) end return false end function physics.keypressed( key, code, isrepeat) if isrepeat then return end if camera.target == 0 then return end local player = participants[levelloop.clientID] if key == "p" then player.flying = not player.flying levelloop.remoteCall("moveFlying", player.flying) if player.flying then player.speed.y = 0 end end end function physics.input(dt) if camera.target == 0 then return end local player = participants[levelloop.clientID] local speedModified = dt * 20 if love.keyboard.isDown("rshift") or love.keyboard.isDown("lshift") then speedModified = speedModified + (60 * dt) end if love.keyboard.isDown("a") then player.speed.x = player.speed.x - speedModified elseif love.keyboard.isDown("d") then player.speed.x = player.speed.x + speedModified end if love.keyboard.isDown("w") and player.flying then player.speed.y = player.speed.y - speedModified elseif love.keyboard.isDown("s") and player.flying then player.speed.y = player.speed.y + speedModified end if love.keyboard.isDown("space") and not player.flying then local posx = player.x + player.avatar:getWidth() local posy = player.y + player.avatar:getHeight() if physics.is_blocked_x(posx - player.avatar:getWidth(), posy, player.avatar:getWidth()) or player.y == levelloop.world.y - player.avatar:getHeight() then player.speed.y = -25 end end end function physics.cap_world(width, height, newx, newy, new_sx, new_sy) if newx > levelloop.world.x - width then newx = levelloop.world.x - width new_sx = 0 end if newx < 0 then newx = 0 new_sx = 0 end if newy > levelloop.world.y - height then newy = levelloop.world.y - height new_sy = 0 end if newy < 0 then newy = 0 new_sy = 0 end return newx, newy, new_sx, new_sy end function physics.updatePlayer(dt, player, localplayer) local new_sx, new_sy if player.flying then new_sy = player.speed.y - player.speed.y / 6 new_sx = player.speed.x - player.speed.x / 6 else new_sx = player.speed.x - player.speed.x / 16 new_sy = player.speed.y - player.speed.y / 16 end if not new_sx then new_sx = 0 end if not new_sy then new_sy = 0 end if (new_sx < 0.001 and new_sx > -0.001) and (new_sy < 0.001 and new_sy > -0.001) then return end --falling if not player.flying then new_sy = new_sy +dt * 100 end --new positions local futureX = player.x + player.speed.x local futureY = player.y + player.speed.y assert(futureX and futureY) -- cap boundaries futureX, futureY, new_sx, new_sy = physics.cap_world(player.avatar:getWidth(), player.avatar:getHeight(), futureX, futureY, new_sx, new_sy) local granularity = math.sqrt(math.abs(player.speed.x * player.speed.x)+ math.abs(player.speed.y * player.speed.y)) + 1 local stepsizeX = player.speed.x / granularity local stepsizeY = player.speed.y / granularity assert(granularity and stepsizeX and stepsizeY) local currentX = player.x local currentY = player.y if granularity < 1 then granularity = 1 end if player.flying then player.x, player.y = futureX, futureY player.speed.x = new_sx player.speed.y = new_sy if localplayer then physics.send_update(player, dt) end return end local blocked_x, blocked_y = false, false for iteration=1, granularity do intermediateX = currentX + (stepsizeX * iteration) intermediateY = currentY + (stepsizeY * iteration) if intermediateY > futureY then -- UP if physics.is_blocked_x(intermediateX, intermediateY, player.avatar:getWidth()) then intermediateY = currentY blocked_y = true end end if intermediateY < futureY then --DOWN local lowerY = intermediateY + player.avatar:getHeight() -1 if physics.is_blocked_x(intermediateX, lowerY, player.avatar:getWidth()) then intermediateY = currentY blocked_y = true end end if intermediateX > futureX then -- ONLY LEFT if physics.is_blocked_y(intermediateX, intermediateY, player.avatar:getHeight()) then blocked_x = true end end if intermediateX < futureX then -- ONLY RIGHT local lowerX = intermediateX + player.avatar:getWidth() -1 if physics.is_blocked_y(lowerX, intermediateY, player.avatar:getHeight()) then blocked_x = true end end intermediateX, intermediateY, new_sx, new_sy = physics.cap_world(player.avatar:getWidth(), player.avatar:getHeight(), intermediateX, intermediateY, new_sx, new_sy) if blocked_x and not blocked_y then -- X blocked player.y = intermediateY return end if blocked_y and not blocked_x then player.x = intermediateX return end if blocked_x and blocked_y then return end if not blocked_x and not blocked_y then player.x = intermediateX player.y = intermediateY end end player.speed.x = new_sx player.speed.y = new_sy if localplayer then physics.send_update(player, dt) end end function physics.update(dt) for i, player in pairs(participants) do assert(player.x and player.y and player.speed.y and player.speed.x, "Player has an invalid position!") physics.updatePlayer(dt, player, i == levelloop.clientID) end local player = participants[levelloop.clientID] ui.updateSpeed(player.speed.x, player.speed.y, player.x, player.y) end local last = {} last.packet, last.x, last.y, last.speedx, last.speedy = 0, 0, 0, 0, 0 function physics.send_update(player, dt) if last.packet > 0.03 then if last.x ~= player.x or last.y ~= player.y then levelloop.remoteCall("moveUpdate", player.x, player.y, player.speed.x, player.speed.y) last.packet = 0 last.x, last.y = player.x, player.y last.speedx, last.speedy = player.speedx, player.speedy end else last.packet = last.packet + dt end end