Skip to content
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."
245 changes: 245 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,248 @@ 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 chip = self.entity
local callbackID = ent:AddCallback("PhysicsCollide",
function( us, cd )
chip:ExecuteEvent("entityCollision",{us,cd.HitEntity,cd})
end)
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm pretty sure this function is executed in the physics engine context which can allow user code to break the physics engine by doing weird stuff inside of this hook. Collisions should be stored in a table and then execute the E2 via a think hook, similar to what SF does.

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 would I want to set something up like E2's tick hook to run the event on the chip for each queued collision?

Copy link
Contributor

Choose a reason for hiding this comment

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

What the heck is that timer there for?
Yeah, create a tick hook when collisions get queued and remove it after it runs.

Copy link
Contributor

Choose a reason for hiding this comment

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

What the heck is that timer there for?

Copy link
Member Author

Choose a reason for hiding this comment

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

What the heck is that timer there for?

The timer there goes back to 2009(pre-github presumably), the last time it was touched was for style standardization in 2013, so I don't know either.

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

registerCallback("construct", function( self )
self.data.E2TrackedCollisions = {}
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"},
})