Hi HyperKiLLeR,
Thank you for your teleportation plugin. Yours is the only version with a goto, which is a feature I wanted. It mostly works for L4D2. I say mostly because if you aim at a wall or a ceiling they will become stuck.
I had an idea. Get the collision point, and then do a bounds check with the client mins/maxs. If the player would become stuck, walk the ray backwards a little bit and try it again, until you collide with yourself or you've taken 100 steps. If you can't succeed, try again from the feet; this allows you to hit the ceiling. If that fails too, then they teleport to you, much like sm_goto.
I also wanted to change to a MASK_SHOT filter for collision, so I can teleport through fences and stuff like that. And I wanted ProcessTargetString support.
So I made these modifications and tested them and they work for me on my L4D2 server. I am sharing them with you in case you wish to add them to your plugin.
I think the backwards-ray-tracing with slightly inflated bounding box idea is cool enough that I'll share that bit in-line so that forum searches can find it. It might help some other people trying to teleport stuff without letting it get stuck.
PHP Code:
// Traces forward from a client's eye or foot position until the ray hits MASK_SHOT
// Then checks for the possibility to get stuck. If teleportee would get stuck,
// walk the ray backwards little by little until they won't get stuck anymore
// If we get too close to the client's position, or we take 100 steps, fail with client pos
stock bool:GetCollisionPoint(client, Float:pos[3], bool:eyes=true)
{
decl Float:vOrigin[3], Float:vAngles[3], Float:vBackwards[3];
new bool:failed = false;
new loopLimit = 100; // only check 100 times, as a precaution against runaway loops
if (eyes)
{
GetClientEyePosition(client, vOrigin);
}
else
{
// if eyes is false, fall back to the AbsOrigin ( = feet)
GetClientAbsOrigin(client, vOrigin);
}
GetClientEyeAngles(client, vAngles);
GetAngleVectors(vAngles, vBackwards, NULL_VECTOR, NULL_VECTOR);
NormalizeVector(vBackwards, vBackwards);
ScaleVector(vBackwards, 10.0); // TODO: percentage of distance from endpoint to eyes instead of fixed distance?
new Handle:trace = TR_TraceRayFilterEx(vOrigin, vAngles, MASK_SHOT, RayType_Infinite, TraceEntityFilterPlayer);
if (TR_DidHit(trace))
{
TR_GetEndPosition(pos, trace);
//PrintToChat(client, "endpos %f %f %f", pos[0], pos[1], pos[2]);
while (IsPlayerStuck(pos, client) && !failed) // iteratively check if they would become stuck
{
SubtractVectors(pos, vBackwards, pos); // if they would, subtract backwards from the position
//PrintToChat(client, "endpos %f %f %f", pos[0], pos[1], pos[2]);
if (GetVectorDistance(pos, vOrigin) < 10 || loopLimit-- < 1)
{
failed = true; // If we get all the way back to the origin without colliding, we have failed
//PrintToChat(client, "failed to find endpos");
pos = vOrigin; // Use the client position as a fallback
}
}
}
CloseHandle(trace);
return !failed; // If we have not failed, return true to let the caller know pos has teleport coordinates
}
#define BOUNDINGBOX_INFLATION_OFFSET 3
// Checks to see if a player would collide with MASK_SOLID (i.e. they would be stuck)
// Inflates player mins/maxs a little bit for better protection against sticking
// Thanks to andersso for the basis of this function
stock bool:IsPlayerStuck(Float:pos[3], client)
{
new Float:mins[3];
new Float:maxs[3];
GetClientMins(client, mins);
GetClientMaxs(client, maxs);
// inflate the sizes just a little bit
for (new i=0; i<sizeof(mins); i++)
{
mins[i] -= BOUNDINGBOX_INFLATION_OFFSET;
maxs[i] += BOUNDINGBOX_INFLATION_OFFSET;
}
TR_TraceHullFilter(pos, pos, mins, maxs, MASK_SOLID, TraceEntityFilterPlayer, client);
return TR_DidHit();
}
// filter out players, since we can't get stuck on them
public bool:TraceEntityFilterPlayer(entity, contentsMask)
{
return entity <= 0 || entity > MaxClients;
}