Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

E2 Collision Core(now part of propcore) #3099

Merged
merged 14 commits into from
Jul 22, 2024
29 changes: 28 additions & 1 deletion lua/entities/gmod_wire_expression2/core/custom/cl_prop.lua
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,31 @@ E2Helper.Descriptions["setEyeTarget(e:v)"] = "For NPCs, sets the eye target to t
E2Helper.Descriptions["setEyeTargetLocal(e:v)"] = "Sets the eye target to the local eye position"
E2Helper.Descriptions["setEyeTargetWorld(e:v)"] = "Sets the eye target to the world position"
E2Helper.Descriptions["setFlexScale(e:n)"] = "Sets the flex scale of the entity"
E2Helper.Descriptions["setFlexWeight"] = "Sets the weight of the flex"
E2Helper.Descriptions["setFlexWeight"] = "Sets the weight of the flex"
E2Helper.Descriptions["trackCollision(e)"] = "Starts tracking collisions for the entity, will fire event entityCollision when they occur.\nDoes not track when entities hit world, only other entities.\nReturns 1 on success or 0 on error in non-strict"
E2Helper.Descriptions["isTrackingCollision(e)"] = "Returns 1 if the entity's collisions are already being tracked, 0 if not. Errors on an invalid ent"
E2Helper.Descriptions["stopTrackingCollision(e)"] = "Stops tracking collisions for the entity.\nError in strict if entity is invalid or entity isn't being tracked"
E2Helper.Descriptions["hitPos(xcd:)"] = "Returns a vector of where the collision ocurred"
E2Helper.Descriptions["pos(xcd:)"] = E2Helper.Descriptions["hitPos(xcd:)"] .. "\nAlias of hitPos(xcd:)"
E2Helper.Descriptions["position(xcd:)"] = E2Helper.Descriptions["pos(xcd:)"]
E2Helper.Descriptions["ourOldVelocity(xcd:)"] = "Returns a vector of the velocity of the tracked entity before the collision occurred."
E2Helper.Descriptions["entityOldVelocity(xcd:)"] = E2Helper.Descriptions["ourOldVelocity(xcd:)"] .. "\nAlias of ourOldVelocity(xcd:)"
E2Helper.Descriptions["theirOldVelocity(xcd:)"] = "Returns a vector of the velocity of the hit entity before the collision occurred"
E2Helper.Descriptions["hitEntityOldVelocity(xcd:)"] = E2Helper.Descriptions["theirOldVelocity(xcd:)"] .. "\nAlias of theirOldVelocity(xcd:)"
E2Helper.Descriptions["hitNormal(xcd:)"] = "Returns the hitnormal(vector) of the surface on the tracked entity that hit the other entity"
E2Helper.Descriptions["hitSpeed(xcd:)"] = "Returns a vector of the speed the impact occurred with"
E2Helper.Descriptions["ourNewVelocity(xcd:)"] = "Returns a vector of the velocity of the tracked entity after the collision occurred."
E2Helper.Descriptions["entityNewVelocity(xcd:)"] = E2Helper.Descriptions["ourNewVelocity(xcd:)"] .. "\nAlias of ourNewVelocity(xcd:)"
E2Helper.Descriptions["theirNewVelocity(xcd:)"] = "Returns a vector of the velocity of the hit entity after the collision occurred."
E2Helper.Descriptions["hitEntityNewVelocity(xcd:)"] = E2Helper.Descriptions["theirNewVelocity(xcd:)"] .. "\nAlias of theirNewVelocity(xcd:)"
E2Helper.Descriptions["ourOldAngularVelocity(xcd:)"] = "Returns a vector of the angular velocity of the tracked entity before the collision occurred."
E2Helper.Descriptions["entityOldAngularVelocity(xcd:)"] = E2Helper.Descriptions["ourOldAngularVelocity(xcd:)"] .. "\nAlias of ourOldAngularVelocity(xcd:)"
E2Helper.Descriptions["theirOldAngularVelocity(xcd:)"] = "Returns a vector of the angular velocity of the hit entity before the collision occurred."
E2Helper.Descriptions["hitEntityOldAngularVelocity(xcd:)"] = E2Helper.Descriptions["theirOldAngularVelocity(xcd:)"] .. "\nAlias of ourOldAngularVelocity(xcd:)"
E2Helper.Descriptions["speed(xcd:)"] = "Returns a number representing the speed at which the collision occurred."
E2Helper.Descriptions["ourSurfaceProps(xcd:)"] = "Returns a number representing the surface properties of the tracked entity"
E2Helper.Descriptions["entitySurfaceProps(xcd:)"] = E2Helper.Descriptions["ourSurfaceProps(xcd:)"] .. "\nAlias of ourSurfaceProps(xcd:)"
E2Helper.Descriptions["theirSurfaceProps(xcd:)"] = "Returns a number representing the surface properties of the hit entity"
E2Helper.Descriptions["hitEntitySurfaceProps(xcd:)"] = E2Helper.Descriptions["theirSurfaceProps(xcd:)"] .. "\nAlias of theirSurfaceProps(xcd:)"
E2Helper.Descriptions["deltaTime(xcd:)"] = "Returns a number representing how long ago the last collision between the tracked entity and the hit entity was, in seconds.\nCapped at 1 second."
E2Helper.Descriptions["hitEntity(xcd:)"] = "Returns the entity that was hit for this collision."
277 changes: 277 additions & 0 deletions lua/entities/gmod_wire_expression2/core/custom/prop.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1277,3 +1277,280 @@ registerCallback("destruct",
end
end
)

-- * Collision tracking

registerType("collision", "xcd", nil,
nil,
nil,
nil,
function(v)
return not istable(v) or not v.HitPos
end
)

-- These are just the types we care about
-- Helps filter out physobjs cause that's not an e2 type
local typefilter = {
entity = "e",
vector = "v",
number = "n",
}

local newE2Table = E2Lib.newE2Table

__e2setcost(20)

e2function table collision:toTable()
local E2CD = newE2Table()
for k,v in pairs(this) do
local type = typefilter[string.lower(type(v))]
if type then
if type == "v" then
-- These need to be given copies, otherwise E2s modifications will propagate.
E2CD.s[k] = Vector(v)
else
E2CD.s[k] = v
end
E2CD.stypes[k] = type
end
end
return E2CD
end

-- Getter functions below, sorted by return type

__e2setcost(5)

local function GetHitPos(self,collision)
if not collision then return self:throw("Invalid collision data!") end
return Vector(collision.HitPos)
end

-- * Vectors

e2function vector collision:hitPos()
return GetHitPos(self,this)
end

e2function vector collision:pos()
return GetHitPos(self,this)
end

e2function vector collision:position()
return GetHitPos(self,this)
end

e2function vector collision:ourOldVelocity()
if not this then return self:throw("Invalid collision data!",Vector(0,0,0)) end
return Vector(this.OurOldVelocity)
end

e2function vector collision:entityOldVelocity()
if not this then return self:throw("Invalid collision data!",Vector(0,0,0)) end
return Vector(this.OurOldVelocity)
end

e2function vector collision:theirOldVelocity()
if not this then return self:throw("Invalid collision data!",Vector(0,0,0)) end
return Vector(this.TheirOldVelocity)
end

e2function vector collision:hitEntityOldVelocity()
if not this then return self:throw("Invalid collision data!",Vector(0,0,0)) end
return Vector(this.TheirOldVelocity)
end

e2function vector collision:hitNormal()
if not this then return self:throw("Invalid collision data!",Vector(0,0,0)) end
return Vector(this.HitNormal)
end

e2function vector collision:hitSpeed()
if not this then return self:throw("Invalid collision data!",Vector(0,0,0)) end
return Vector(this.HitSpeed)
end

e2function vector collision:ourNewVelocity()
if not this then return self:throw("Invalid collision data!",Vector(0,0,0)) end
return Vector(this.OurNewVelocity)
end

e2function vector collision:entityNewVelocity()
if not this then return self:throw("Invalid collision data!",Vector(0,0,0)) end
return Vector(this.OurNewVelocity)
end

e2function vector collision:theirNewVelocity()
if not this then return self:throw("Invalid collision data!",Vector(0,0,0)) end
return Vector(this.TheirNewVelocity)
end

e2function vector collision:hitEntityNewVelocity()
if not this then return self:throw("Invalid collision data!",Vector(0,0,0)) end
return Vector(this.TheirNewVelocity)
end

e2function vector collision:ourOldAngularVelocity()
if not this then return self:throw("Invalid collision data!",Vector(0,0,0)) end
return Vector(this.OurOldAngularVelocity)
end

e2function vector collision:entityOldAngularVelocity()
if not this then return self:throw("Invalid collision data!",Vector(0,0,0)) end
return Vector(this.OurOldAngularVelocity)
end

e2function vector collision:theirOldAngularVelocity()
if not this then return self:throw("Invalid collision data!",Vector(0,0,0)) end
return Vector(this.TheirOldAngularVelocity)
end

e2function vector collision:hitEntityOldAngularVelocity()
if not this then return self:throw("Invalid collision data!",Vector(0,0,0)) end
return Vector(this.TheirOldAngularVelocity)
end

-- * Numbers
__e2setcost(2)

e2function number collision:speed()
if not this then return self:throw("Invalid collision data!",0) end
return this.Speed
end

e2function number collision:ourSurfaceProps()
if not this then return self:throw("Invalid collision data!",0) end
return this.OurSurfaceProps
end

e2function number collision:entitySurfaceProps()
if not this then return self:throw("Invalid collision data!",0) end
return this.OurSurfaceProps
end

e2function number collision:theirSurfaceProps()
if not this then return self:throw("Invalid collision data!",0) end
return this.TheirSurfaceProps
end

e2function number collision:hitEntitySurfaceProps()
if not this then return self:throw("Invalid collision data!",0) end
return this.TheirSurfaceProps
end

e2function number collision:deltaTime()
if not this then return self:throw("Invalid collision data!",0) end
return this.DeltaTime
end

-- * Entities

e2function entity collision:hitEntity()
if not this then return self:throw("Invalid collision data!",Entity(0)) end
return this.HitEntity
end


__e2setcost( 20 )

e2function number trackCollision( entity ent )
if IsValid(ent) then
local entIndex = ent:EntIndex()
if self.data.E2TrackedCollisions[entIndex] then
return self:throw("Attempting to track collisions for an already tracked entity",0) -- Already being tracked.
end
local ctx = self
local callbackID = ent:AddCallback("PhysicsCollide",
function( us, cd )
table.insert(ctx.data.E2QueuedCollisions,{us=us,xcd=cd})
end)
self.data.E2TrackedCollisions[entIndex] = callbackID -- This ID is needed to remove the physcollide callback
ent:CallOnRemove("E2Chip_CCB" .. callbackID, function()
self.data.E2TrackedCollisions[entIndex] = nil
end)
return 1
end
return self:throw("Attempting to track collisions for an invalid entity",0)
end

__e2setcost( 5 )

e2function number isTrackingCollision( entity ent )
if not IsValid(ent) then
return self:throw("Attempting to check tracking of collisions for an invalid entity",0)
end
if self.data.E2TrackedCollisions[ent:EntIndex()] then
return 1
else
return 0
end
end

e2function void stopTrackingCollision( entity ent )
if IsValid(ent) then
local entIndex = ent:EntIndex()
if self.data.E2TrackedCollisions[entIndex] then
local callbackID = self.data.E2TrackedCollisions[entIndex]
ent:RemoveCallOnRemove("E2Chip_CCB" .. callbackID)
ent:RemoveCallback("PhysicsCollide", callbackID)
self.data.E2TrackedCollisions[entIndex] = nil
else
return self:throw("Attempting to stop tracking collisions for an untracked entity",nil)
end
else
return self:throw("Attempting to stop tracking collisions for an invalid entity",nil)
end
end

local registered_chips = {}

registerCallback("construct", function( self )
self.data.E2TrackedCollisions = {}
self.data.E2QueuedCollisions = {}
end)

registerCallback("destruct", function( self )
for k,v in pairs(self.data.E2TrackedCollisions) do
local ent = Entity(tonumber(k))
if IsValid(ent) then
ent:RemoveCallOnRemove("E2Chip_CCB" .. v)
ent:RemoveCallback("PhysicsCollide", v)
end
end
end)



E2Lib.registerEvent("entityCollision", {
{"Entity", "e"},
{"HitEntity", "e"},
{"CollisionData", "xcd"},
},
function(ctx) -- Event constructor
registered_chips[ctx.entity] = ctx
end,
function(ctx) -- Event destructor
registered_chips[ctx.entity] = nil
end
)

local function E2CollisionEventHandler()
for chip,ctx in pairs(registered_chips) do
if IsValid(chip) then
if not chip.error then
for _,i in ipairs(ctx.data.E2QueuedCollisions) do
chip:ExecuteEvent("entityCollision",{i.us,i.xcd.HitEntity,i.xcd})
if chip.error then break end
end
end
-- Wipe queued collisions regardless of error
ctx.data.E2QueuedCollisions = {}
end
end
end

hook.Add("Think", "Expression2CollisionClock", E2CollisionEventHandler)
timer.Create("Expression2CollisionClock", 5, 0, function()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this timer exist. You should add the think hook when a collision is queued and remove it after it runs.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So like, the function I have right now is fine, just have it added on a collision and have it hook.remove itself after run?
Or do you want a unique hook generated for each chip / collision to run the event once for that single event?

Is there a big cost to hook.add overwriting a pre-existing hook that might make it worth storing a bool for the hooks existence locally to check against?

On the matter of the timer, I figured it was a failsafe for the e2 tick hook getting cleared during load but if we shift away from the check being every think to just the think after a collision the timer can definitely go.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even better is

if not var then
    var = true
    timer.Simple(0,function()
        processCollisions()
        var = false
    end)
end

hook.Add("Think", "Expression2CollisionClock", E2CollisionEventHandler)
end)
Loading