# Your ideas on how to get a player unstuck from geometry.

I’ve got a semi-functional idea of how to do it, but it’d be great to hear what other ways exist to solve this problem or if anyone has encountered it and solved it before.

If a player is stuck in geometry ie (ply:GetPhysicsObject():IsPenetrating returns true) how can we find a suitable location within a short proximity to put them at?

My idea is to do six short traces; one trace per face on the bounding box, check which traces get blocked and move in the opposite direction until the trace is free. Do this for every blocked trace. This seems like it’ll unstick them most of the time.

Any other ideas?

Check ULX’s code. I believe it has a solution. I think it’s basically that and warns that it can’t position the player if all of the traces fail.

## [LUA] – TODO: Nocollided props still trigger even with MASK_PLAYERSOLID!

local ply = nil

– WeHateGarbage
local function PlayerNotStuck()

``````t.start = ply:GetPos()
t.endpos = t.start
t.filter = ply

return util.TraceEntity(t,ply).StartSolid == false
``````

end

local NewPos = nil
local function FindPassableSpace( direction, step )

``````local i = 0
while ( i &lt; 100 ) do
local origin = ply:GetPos()

--origin = VectorMA( origin, step, direction )
origin = origin + step * direction

ply:SetPos( origin )
if ( PlayerNotStuck( ply ) ) then
NewPos = ply:GetPos()
return true
end
i = i + 1
end
return false
``````

end

/*
Purpose: Unstucks player
Note: Very expensive to call, you have been warned!
*/
local function UnstuckPlayer( pl )
ply = pl

``````NewPos = ply:GetPos()
local OldPos = NewPos

if ( !PlayerNotStuck( ply ) ) then

local angle = ply:GetAngles()

local forward = angle:Forward()
local right = angle:Right()
local up = angle:Up()

local SearchScale = 1 -- Increase and it will unstuck you from even harder places but with lost accuracy. Please, don't try higher values than 12
if ( !FindPassableSpace(  forward, SearchScale ) )
then
if ( !FindPassableSpace(  right, SearchScale ) )
then
if ( !FindPassableSpace(  right, -SearchScale ) )		// left
then
if ( !FindPassableSpace(  up, SearchScale ) )	// up
then
if ( !FindPassableSpace(  up, -SearchScale ) )	// down
then
if ( !FindPassableSpace(  forward, -SearchScale ) )	// back
then

-- spam spam spam

--Msg( "Can't find the world for player "..tostring(ply).."
``````

" )

``````							return false

end
end
end
end
end
end

if OldPos == NewPos then
return true -- Not stuck?
else
ply:SetPos( NewPos )
if SERVER and ply and ply:IsValid() and ply:GetPhysicsObject():IsValid() then
if ply:IsPlayer() then
ply:SetVelocity(vector_origin)
end
ply:GetPhysicsObject():SetVelocity(vector_origin) -- prevents bugs :s
end

return true
end

end
``````

## end

local meta= FindMetaTable"Player"

``````/*	Unstucks a player
returns:
true:	Unstucked
false:	Could not UnStuck
else:	Not stuck
*/
function meta:UnStuck()
return UnstuckPlayer(self)
end
``````

end

[/LUA]

Valve code dirty port, but it works (at least used to) for what I used it for

No-clip???

There gamemodes which don’t allow noclip.

Kill in the console.

Flub is there a reason you’re posting that in a help thread of the lua section? Seems pretty clear that he wants a script solution.

Hey.

Python, thanks for the post. Seems like that code would work for sure. Fortunately, after a bit, I was able to come up with a solution that worked enough for the situations it was intended to solve. I won’t post the code here unless someone wants to see it (the math is confusing at first, second, and probably third glance).

It essentially traces the edges of the player’s bounding box and moves the player out of geometry until each edge is clear. Does 2 traces per edge (because back faces don’t stop traces) and doesn’t do Z aligned edges so that’s 16 traces at the worst case scenario (usually 2-4 traces). Won’t work if all points on the bounding box are stuck, but that is very improbable given the scenario that causes stuckness.

**[Gamemode.CanPlayerSuicide

Posting it would be helpful even if just for use in our own gamemodes, etc.

Sure thing, keep in mind this solves X and Y penetration not z axis, though I should add z axis.

[lua]
local meta = FindMetaTable(“Player”);
local function CheckAndAdjust(posMax, posMin, tr, npos)
tr.start, tr.endpos = posMin, posMax;
local res = util.TraceLine(tr);
if (res.Hit && !res.StartSolid) then
return (npos - (posMax - res.HitPos));
else
tr.start, tr.endpos = posMax, posMin;
res = util.TraceLine(tr);
if (res.Hit && !res.StartSolid) then
return (npos - (posMin - res.HitPos));
end
end
return npos;
end

–Tries to remove a player from any geometry they may be stuck in
–Note: Uses bounding box corners which are axis aligned thus all vector math doesn’t include normals
–Currently only works on the x and y axis, doesn’t untrap z axis
function meta:UnTrap()
–Lef = bmin.x Rig = bmax.x
–Bot = bmin.z Top = bmax.z
–bAc = bmin.y Fro = bmax.y
local tr = { filter = self };
local res;
local traceline = util.TraceLine; --save time hurrrrrrrrrrrrr

``````--Note: min to max subtract, max to min add
--Check top face

--Left/right checks for top and bottom
local bmin, bmax = self:WorldSpaceAABB();
local TFL = Vector(bmin.x, bmax.y, bmax.z);
local TFR = bmax;
local bmin, bmax = self:WorldSpaceAABB();
local TAL = Vector(bmin.x, bmin.y, bmax.z);
local TAR = Vector(bmax.x, bmin.y, bmax.z);
local bmin, bmax = self:WorldSpaceAABB();
local BFL = Vector(bmin.x, bmax.y, bmin.z);
local BFR = Vector(bmax.x, bmax.y, bmin.z);
local bmin, bmax = self:WorldSpaceAABB();
local BAL = bmin;
local BAR = Vector(bmax.x, bmin.y, bmin.z);
--Front/back checks for top and bottom
local bmin, bmax = self:WorldSpaceAABB();
local TFL = Vector(bmin.x, bmax.y, bmax.z);
local TAL = Vector(bmin.x, bmin.y, bmax.z);
local bmin, bmax = self:WorldSpaceAABB();
local TFR = bmax;
local TAR = Vector(bmax.x, bmin.y, bmax.z);