Skip to content
Merged
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)