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 t = {start=nil,endpos=nil,mask=MASK_PLAYERSOLID,filter=nil}
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 < 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 :slight_smile:

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

http://wiki.garrysmod.com/favicon.ico](wiki.garrysmod.com/?title=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
--TODO: Add stuck checks between successful adjustments to quit early

--Left/right checks for top and bottom
local bmin, bmax = self:WorldSpaceAABB();
local TFL = Vector(bmin.x, bmax.y, bmax.z);
local TFR = bmax;
self:SetPos(CheckAndAdjust(TFL, TFR, tr, self:GetPos()));
local bmin, bmax = self:WorldSpaceAABB();
local TAL = Vector(bmin.x, bmin.y, bmax.z);
local TAR = Vector(bmax.x, bmin.y, bmax.z);
self:SetPos(CheckAndAdjust(TAL, TAR, tr, self:GetPos()));
local bmin, bmax = self:WorldSpaceAABB();
local BFL = Vector(bmin.x, bmax.y, bmin.z);
local BFR = Vector(bmax.x, bmax.y, bmin.z);
self:SetPos(CheckAndAdjust(BFL, BFR, tr, self:GetPos()));
local bmin, bmax = self:WorldSpaceAABB();
local BAL = bmin;
local BAR = Vector(bmax.x, bmin.y, bmin.z);
self:SetPos(CheckAndAdjust(BAL, BAR, tr, self:GetPos()));
--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);
self:SetPos(CheckAndAdjust(TFL, TAL, tr, self:GetPos()));
local bmin, bmax = self:WorldSpaceAABB();
local TFR = bmax;
local TAR = Vector(bmax.x, bmin.y, bmax.z);
self:SetPos(CheckAndAdjust(TFR, TAR, tr, self:GetPos()));
local bmin, bmax = self:WorldSpaceAABB();
local BFL = Vector(bmin.x, bmax.y, bmin.z);
local BAL = bmin;
self:SetPos(CheckAndAdjust(BFL, BAL, tr, self:GetPos()));
local bmin, bmax = self:WorldSpaceAABB();
local BFR = Vector(bmax.x, bmax.y, bmin.z);
local BAR = Vector(bmax.x, bmin.y, bmin.z);
self:SetPos(CheckAndAdjust(BFR, BAR, tr, self:GetPos()));

end[/lua]