2021-11-25 10:46:15 +01:00
|
|
|
PhysicalActor = Object:extend()
|
|
|
|
local hitboxStructure = require "birb.structures.hitbox"
|
|
|
|
|
|
|
|
-- 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()
|
|
|
|
|
2021-11-27 12:50:27 +01:00
|
|
|
self.ignoreList = {}
|
|
|
|
|
2021-11-25 10:46:15 +01:00
|
|
|
self:addUpdateFunction(self.autoMove)
|
|
|
|
end
|
|
|
|
|
|
|
|
function PhysicalActor:setBounceFactor(newBounceFactor)
|
|
|
|
self.bounceFactor = newBounceFactor or 0
|
|
|
|
end
|
|
|
|
|
2021-11-27 12:50:27 +01:00
|
|
|
function PhysicalActor:ignore(item)
|
|
|
|
assert(item.creationID ~= nil, "creationID shouldn't be nil")
|
|
|
|
table.insert(self.ignoreList, item.creationID)
|
|
|
|
end
|
|
|
|
|
2021-11-25 10:46:15 +01:00
|
|
|
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
|
2021-11-27 12:50:27 +01:00
|
|
|
elseif (utils.table.contain(self.ignoreList, other.owner.creationID)) then
|
|
|
|
return nil
|
2021-11-25 10:46:15 +01:00
|
|
|
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)
|
2021-11-25 11:34:25 +01:00
|
|
|
if (self.xfrc > 0) then
|
|
|
|
self.xsp = utils.math.toZero(self.xsp, self.xfrc * dt)
|
|
|
|
end
|
|
|
|
if (self.yfrc > 0) then
|
|
|
|
self.ysp = utils.math.toZero(self.ysp, self.yfrc * dt)
|
|
|
|
end
|
2021-11-25 10:46:15 +01:00
|
|
|
if (self.z ~= nil) then
|
2021-11-25 11:34:25 +01:00
|
|
|
if (self.zfrc > 0) then
|
|
|
|
self.zsp = utils.math.toZero(self.zsp, self.zfrc * dt)
|
|
|
|
end
|
2021-11-25 10:46:15 +01:00
|
|
|
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 hitbox = utils.table.parse(framedata, hitboxStructure, 1)
|
|
|
|
local anim = animationID or "null"
|
|
|
|
local frame = frameID or 0
|
|
|
|
local id = hitboxID or 0
|
|
|
|
|
|
|
|
if (hitbox.type == "main") then
|
|
|
|
self.mainHitbox:setFromData(hitbox.box, sx, sy)
|
|
|
|
else
|
|
|
|
local hitboxName = anim .. frame .. type .. id
|
|
|
|
self:addHitbox(hitboxName, hitbox.type, hitbox.box, sx, sy, hitbox.isSolid)
|
|
|
|
return hitboxName
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function PhysicalActor:addHitbox(name, type, data, sx, sy, isSolid)
|
|
|
|
isSolid = (isSolid == true)
|
|
|
|
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
|