Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions addons/sourcemod/scripting/include/movementapi.inc
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,19 @@ forward void Movement_OnPlayerJump(int client, bool jumpbug);
*/
forward void Movement_OnPlayerEdgebug(int client, float origin[3], float velocity[3]);

/**
* Called when a player pixelsurfs.
* A pixelsurf also matches the edgebug signature, so Movement_OnPlayerEdgebug fires on the same tick as well:
* A consumer wanting pure edgebugs should ignore any tick that also raised this forward.
* Called once per tick the player stays suspended on the surf.
* Setting velocity when this is called may not be effective.
*
* @param client Client index.
* @param origin Player origin before pixelsurf.
* @param velocity Player velocity before pixelsurf.
*/
forward void Movement_OnPlayerPixelsurf(int client, float origin[3], float velocity[3]);

/**
* Called before PlayerMove movement function is called.
* Modifying origin or velocity parameters will change player's origin and velocity accordingly.
Expand Down
2 changes: 1 addition & 1 deletion addons/sourcemod/scripting/movementapi.sp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public Plugin myinfo =
name = "MovementAPI",
author = "DanZay",
description = "Provides API focused on player movement",
version = "2.4.4",
version = "2.4.5",
url = "https://github.com/danzayau/MovementAPI"
};

Expand Down
11 changes: 11 additions & 0 deletions addons/sourcemod/scripting/movementapi/forwards.sp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ static Handle H_OnStopTouchGround;
static Handle H_OnChangeMovetype;
static Handle H_OnPlayerJump;
static Handle H_OnPlayerEdgebug;
static Handle H_OnPlayerPixelsurf;

static Handle H_OnPlayerMovePre;
static Handle H_OnPlayerMovePost;
Expand Down Expand Up @@ -34,6 +35,7 @@ void CreateGlobalForwards()
H_OnChangeMovetype = CreateGlobalForward("Movement_OnChangeMovetype", ET_Ignore, Param_Cell, Param_Cell, Param_Cell);
H_OnPlayerJump = CreateGlobalForward("Movement_OnPlayerJump", ET_Ignore, Param_Cell, Param_Cell);
H_OnPlayerEdgebug = CreateGlobalForward("Movement_OnPlayerEdgebug", ET_Ignore, Param_Cell, Param_Array, Param_Array);
H_OnPlayerPixelsurf = CreateGlobalForward("Movement_OnPlayerPixelsurf", ET_Ignore, Param_Cell, Param_Array, Param_Array);

H_OnPlayerMovePre = CreateGlobalForward("Movement_OnPlayerMovePre", ET_Event, Param_Cell, Param_Array, Param_Array);
H_OnPlayerMovePost = CreateGlobalForward("Movement_OnPlayerMovePost", ET_Event, Param_Cell, Param_Array, Param_Array);
Expand Down Expand Up @@ -124,6 +126,15 @@ void Call_OnPlayerEdgebug(int client, float origin[3], float velocity[3])
Call_Finish();
}

void Call_OnPlayerPixelsurf(int client, float origin[3], float velocity[3])
{
Call_StartForward(H_OnPlayerPixelsurf);
Call_PushCell(client);
Call_PushArray(origin, 3);
Call_PushArray(velocity, 3);
Call_Finish();
}

Action Call_OnPlayerMovePre(int client, float origin[3], float velocity[3], Action &result)
{
Call_StartForward(H_OnPlayerMovePre);
Expand Down
125 changes: 121 additions & 4 deletions addons/sourcemod/scripting/movementapi/hooks.sp
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
#define PX_TOP_NORMAL_Z 0.999
#define PX_WALL_PROBE_DIST 40.0
#define PX_WALL_DIRS 8
#define PX_WALL_NORMAL_Z 0.3
#define PX_WALL_HALF_WIDTH 16.0
#define PX_WALL_PRESS_MARGIN 2.0
#define PX_FLOOR_PROBE_DROP 2.0
#define PX_FLOOR_PROBE_TOL 4.0

static DynamicDetour H_OnPlayerMove;
static DynamicDetour H_OnDuck;
static DynamicDetour H_OnLadderMove;
Expand Down Expand Up @@ -569,6 +578,7 @@ public MRESReturn DHooks_OnTryPlayerMove_Post(Address pThis, DHookReturn hReturn
Address m_TouchList_m_pElements = LoadFromAddress(moveHelperAddr + view_as<Address>(8) + view_as<Address>(16), NumberType_Int32);

bool hitStandableSurface = false;
float bestStandableNormalZ = 0.0;
static ConVar sv_standable_normal;
if (sv_standable_normal == INVALID_HANDLE)
{
Expand All @@ -583,29 +593,41 @@ public MRESReturn DHooks_OnTryPlayerMove_Post(Address pThis, DHookReturn hReturn
if (trace.plane.normal.z >= sv_standable_normal.FloatValue)
{
hitStandableSurface = true;
if (trace.plane.normal.z > bestStandableNormalZ)
{
bestStandableNormalZ = trace.plane.normal.z;
}
}
}

// Edgebug detection
// Edgebug / pixelsurf detection

if (hitStandableSurface)
{
float currentOrigin[3], groundEndPoint[3];

GameMove_GetOrigin(pThis, currentOrigin);
groundEndPoint = currentOrigin;
groundEndPoint[2] -= 2.0;
float mins[3] = {-16.0, -16.0, 0.0};
float maxs[3] = {16.0, 16.0, 0.0};
TR_TraceHullFilter(currentOrigin, groundEndPoint, mins, maxs, MASK_PLAYERSOLID, TraceEntityFilterPlayers, client);

float groundPos[3];
TR_GetEndPosition(groundPos);

// Note: Origin and velocity are not updated yet.
if (!TR_DidHit())
{
Call_OnPlayerEdgebug(client, gF_Origin[client], gF_Velocity[client]);

float wallPos[3], wallNorm[3];
if (bestStandableNormalZ >= PX_TOP_NORMAL_Z
&& GetPressedWall(client, currentOrigin, wallPos, wallNorm)
&& !FloorProtrudesFromWall(client, wallPos, wallNorm, currentOrigin[2]))
{
Call_OnPlayerPixelsurf(client, gF_Origin[client], gF_Velocity[client]);
}
}
}

Expand All @@ -620,6 +642,101 @@ public MRESReturn DHooks_OnTryPlayerMove_Post(Address pThis, DHookReturn hReturn
}
}

// Pixelsurf 1/2: is the player pressed flush against a wall, and if so which one?
static bool GetPressedWall(int client, const float origin[3], float wallPos[3], float wallNorm[3])
{
float heights[2];
heights[0] = 18.0;
heights[1] = 40.0;

for (int h = 0; h < sizeof(heights); h++)
{
float start[3];
start = origin;
start[2] += heights[h];

for (int d = 0; d < PX_WALL_DIRS; d++)
{
float yaw = DegToRad(d * (360.0 / PX_WALL_DIRS));
float dir[3];
dir[0] = Cosine(yaw);
dir[1] = Sine(yaw);

float end[3];
end[0] = start[0] + dir[0] * PX_WALL_PROBE_DIST;
end[1] = start[1] + dir[1] * PX_WALL_PROBE_DIST;
end[2] = start[2];

TR_TraceRayFilter(start, end, MASK_PLAYERSOLID, RayType_EndPoint, TraceEntityFilterPlayers, client);
if (!TR_DidHit())
{
continue;
}

float normal[3];
TR_GetPlaneNormal(null, normal);
if (FloatAbs(normal[2]) >= PX_WALL_NORMAL_Z)
{
continue;
}

float hitPos[3];
TR_GetEndPosition(hitPos);
float perp = FloatAbs((start[0] - hitPos[0]) * normal[0] + (start[1] - hitPos[1]) * normal[1]);
float support = PX_WALL_HALF_WIDTH * (FloatAbs(normal[0]) + FloatAbs(normal[1]));
if (perp <= support + PX_WALL_PRESS_MARGIN)
{
wallPos = hitPos;
wallNorm = normal;
return true;
}
}
}
return false;
}

// Pixelsurf 2/2: does a real, walkable floor protrude from the wall at the catch height?
static bool FloorProtrudesFromWall(int client, const float wallPos[3], const float wallNorm[3], float catchZ)
{
float outs[2];
outs[0] = 4.0;
outs[1] = 8.0;

for (int i = 0; i < sizeof(outs); i++)
{
float start[3];
start[0] = wallPos[0] + wallNorm[0] * outs[i];
start[1] = wallPos[1] + wallNorm[1] * outs[i];
start[2] = catchZ + PX_FLOOR_PROBE_DROP;

float end[3];
end[0] = start[0];
end[1] = start[1];
end[2] = catchZ - PX_FLOOR_PROBE_DROP;

TR_TraceRayFilter(start, end, MASK_PLAYERSOLID, RayType_EndPoint, TraceEntityFilterPlayers, client);
if (!TR_DidHit())
{
continue;
}

float normal[3];
TR_GetPlaneNormal(null, normal);
if (normal[2] < PX_TOP_NORMAL_Z)
{
continue;
}

float hitPos[3];
TR_GetEndPosition(hitPos);
if (FloatAbs(hitPos[2] - catchZ) <= PX_FLOOR_PROBE_TOL)
{
return true;
}
}
return false;
}

static void NobugLandingOrigin(int client, float landingOrigin[3])
{
// Jump is bugged, try to use the trace result of TryPlayerMove if possible.
Expand Down