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() self.ignoreList = {} self:addUpdateFunction(self.autoMove) end function PhysicalActor:setBounceFactor(newBounceFactor) self.bounceFactor = newBounceFactor or 0 end function PhysicalActor:ignore(item) assert(item.creationID ~= nil, "creationID shouldn't be nil") table.insert(self.ignoreList, item.creationID) 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 (utils.table.contain(self.ignoreList, other.owner.creationID)) then 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) 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 if (self.z ~= nil) then if (self.zfrc > 0) then self.zsp = utils.math.toZero(self.zsp, self.zfrc * dt) end 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