sonic-radiance/sonic-radiance.love/birb/modules/world/actors/mixins/physics.lua
2021-05-07 19:23:34 +02:00

273 lines
No EOL
6.8 KiB
Lua

PhysicalActor = Object:extend()
-- PHYSICS FUNCTIONS
-- Raw implementation of everything common in physics
function PhysicalActor:initPhysics(hitboxObj, isSolid)
self.isSolid = isSolid or false
self.xsp = 0
self.ysp = 0
self.zsp = 0
self.xfrc = 0
self.yfrc = 0
self.zfrc = 0
self:initGravity()
self:initHitboxes(hitboxObj)
self:setBounceFactor()
self:setFilter()
self:addUpdateFunction(self.autoMove)
end
function PhysicalActor:setBounceFactor(newBounceFactor)
self.bounceFactor = newBounceFactor or 0
end
function PhysicalActor:setFilter()
-- Init the bump filter
self.filter = function(item, other)
if (other.owner == self) then
-- ignore every collision with our own hitboxes
return nil
elseif (other.isSolid) then
return "slide"
else
return "cross"
end
end
end
function PhysicalActor:getFuturePosition(dt)
local dx, dy, dz
dx = self.x + self.xsp * dt
dy = self.y + self.ysp * dt
if (self.z ~= nil) then
dz = self.z + self.zsp * dt
end
return dx, dy, dz
end
function PhysicalActor:applyFriction(dt)
self.xsp = utils.math.toZero(self.xsp, self.xfrc * dt)
self.ysp = utils.math.toZero(self.ysp, self.yfrc * dt)
if (self.z ~= nil) then
self.zsp = utils.math.toZero(self.zsp, self.zfrc * dt)
end
end
function PhysicalActor:solveAllCollisions(cols)
for i, col in ipairs(cols) do
self:collisionResponse(col)
if (col.type == "touch") or (col.type == "bounce") or (col.type == "slide") then
self:changeSpeedToCollisionNormal(col.normal)
end
end
end
function PhysicalActor:collisionResponse(collision)
-- here come the response to the collision
end
function PhysicalActor:changeSpeedToCollisionNormal(normal)
-- Empty function in PhysicalActor
end
-- COORDINATE/MOVEMENT FUNCTIONS
-- Handle coordinate
-- Will be replaced by functions inside Actors or Rects/Point
function PhysicalActor:getViewCenter()
return self:getCenter()
end
-- GRAVITY SYSTEM FUNCTIONS
-- All functions related to gravity
function PhysicalActor:initGravity()
if (self.world.gravity.isDefault) then
self.grav = self.world.gravity.grav
else
self.grav = 0
end
self.onGround = false
end
function PhysicalActor:setGravity(grav)
-- It's basically now a function with two roles at once :
-- - activate the gravity
-- - use the gravity value the dev want
self.grav = grav or self.world.gravity.grav
end
function PhysicalActor:applyGravity(dt)
-- Empty function in PhysicalActor
end
function PhysicalActor:checkGround()
-- Empty function in PhysicalActor
end
function PhysicalActor:autoMove(dt)
self:updateHitboxes()
self.onGround = false
self:applyGravity(dt)
local cols, colNumber = self:moveToFuturePosition(dt)
-- apply after the movement the friction, until the player stop
-- note: the friction is applied according to the delta time,
-- thus the friction should be how much speed is substracted in 1 second
self:solveAllCollisions(cols)
self:applyFriction(dt)
end
-- HITBOX FUNCTIONS
-- All functions to handle hitboxes
function PhysicalActor:initHitboxes(hitboxObj)
self.Hitbox = hitboxObj
self:initMainHitbox()
self.hitboxes = {}
self.hitboxListFile = ""
self.hitboxList = nil
end
function PhysicalActor:initMainHitbox()
self.mainHitbox = self.Hitbox(self, self.type, self:packForHitbox(), 0, 0, self.isSolid)
self.mainHitbox:advertiseAsMainHitbox()
end
function PhysicalActor:setHitboxFile(file)
self.hitboxList = require(file)
self.hitboxListFile = file
end
function PhysicalActor:getAutomaticHitboxLoading()
return (self.hitboxList ~= nil)
end
function PhysicalActor:getHitboxFile()
return self.hitboxListFile
end
function PhysicalActor:getHitboxList(animation, frame)
if (animation == nil) or (self.hitboxList == nil) then
return self.hitboxList
else
local list = self.hitboxList[animation]
if (frame == nil) or (list == nil) then
return list
else
return list[frame]
end
end
end
function PhysicalActor:addHitboxFromFrameData(framedata, animationID, frameID, hitboxID)
local sx, sy = self.sprite:getScalling()
local type = framedata[1]
local box = framedata[2]
local isSolid = framedata[3] or false
local anim = animationID or "null"
local frame = frameID or 0
local id = hitboxID or 0
if (type == "main") then
self.mainHitbox:setFromData(box, sx, sy)
else
local hitboxName = anim .. frame .. type .. id
self:addHitbox(hitboxName, type, box, sx, sy, isSolid)
return hitboxName
end
end
function PhysicalActor:addHitbox(name, type, data, sx, sy, isSolid)
if (self.hitboxes[name] ~= nil) then
core.debug:logWarn("PhysicalActor", "the hitbox " .. name .. " already exists")
else
local hitbox = self.Hitbox(self, type, data, sx, sy, isSolid)
self.hitboxes[name] = hitbox
return hitbox
end
end
function PhysicalActor:updateHitboxes()
if (self.hitboxList ~= nil) then
self:purgeHitbox()
local animation, frame
animation = self.sprite:getCurrentAnimation()
frame = self.sprite:getRelativeFrame()
local hitboxList = self:getHitboxList(animation, frame)
if (hitboxList ~= nil) then
for i, v in ipairs(hitboxList) do
self:addHitboxFromFrameData(v, animation, frame, i)
end
end
end
end
function PhysicalActor:applyHitboxesCollisions(filter)
for k, v in pairs(self.hitboxes) do
self:applyHitboxCollisions(k, filter)
end
end
function PhysicalActor:applyHitboxCollisions(name, filter)
local cols, colNumber = {}, 0
local filter = filter or self.filter
if (self.isDestroyed == false) and (self.hitboxes[name] ~= nil) then
cols, colNumber = self.hitboxes[name]:checkCollision(filter)
local type = self.hitboxes[name].type
for i, col in ipairs(cols) do
self:hitboxResponse(name, type, col)
end
end
return cols, colNumber
end
function PhysicalActor:hitboxResponse(name, type, collision)
-- just a blank placeholder function
end
function PhysicalActor:removeHitbox(name)
if (self.hitboxes[name] ~= nil) then
self.hitboxes[name]:destroy()
self.hitboxes[name] = nil
end
end
function PhysicalActor:purgeHitbox()
for k, v in pairs(self.hitboxes) do
v:destroy()
end
self.hitboxes = {}
end
function PhysicalActor:drawHitboxes()
for k, v in pairs(self.hitboxes) do
v:draw()
end
self:drawMainHitbox()
end
function PhysicalActor:drawMainHitbox()
if (self.mainHitbox ~= nil) then
self.mainHitbox:draw()
end
end
return PhysicalActor