poppy-client/lua/physics.lua

289 lines
7.8 KiB
Lua

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