diff options
| author | aura <nw@moneybot.cc> | 2026-04-25 22:46:52 +0200 |
|---|---|---|
| committer | aura <nw@moneybot.cc> | 2026-04-25 22:46:52 +0200 |
| commit | 216c729c8762f4d0388d8750905a91fe1de64ccf (patch) | |
| tree | 72f0f2f201f7156f220a46a1837c2768abdbb278 /source/sourcemod/scripting/movementapi | |
| parent | 6018b8161bdc2bc7eee15500fbd9658d8ad0fc3f (diff) | |
wip on removing gokz gamemode features
Diffstat (limited to 'source/sourcemod/scripting/movementapi')
| -rw-r--r-- | source/sourcemod/scripting/movementapi/forwards.sp | 268 | ||||
| -rw-r--r-- | source/sourcemod/scripting/movementapi/hooks.sp | 580 | ||||
| -rw-r--r-- | source/sourcemod/scripting/movementapi/natives.sp | 183 | ||||
| -rw-r--r-- | source/sourcemod/scripting/movementapi/stocks.sp | 200 |
4 files changed, 1231 insertions, 0 deletions
diff --git a/source/sourcemod/scripting/movementapi/forwards.sp b/source/sourcemod/scripting/movementapi/forwards.sp new file mode 100644 index 0000000..9cf0b72 --- /dev/null +++ b/source/sourcemod/scripting/movementapi/forwards.sp @@ -0,0 +1,268 @@ +static Handle H_OnStartDucking; +static Handle H_OnStopDucking; +static Handle H_OnStartTouchGround; +static Handle H_OnStopTouchGround; +static Handle H_OnChangeMovetype; +static Handle H_OnPlayerJump; + +static Handle H_OnPlayerMovePre; +static Handle H_OnPlayerMovePost; +static Handle H_OnDuckPre; +static Handle H_OnDuckPost; +static Handle H_OnLadderMovePre; +static Handle H_OnLadderMovePost; +static Handle H_OnFullLadderMovePre; +static Handle H_OnFullLadderMovePost; +static Handle H_OnJumpPre; +static Handle H_OnJumpPost; +static Handle H_OnAirAcceleratePre; +static Handle H_OnAirAcceleratePost; +static Handle H_OnWalkMovePre; +static Handle H_OnWalkMovePost; +static Handle H_OnCategorizePositionPre; +static Handle H_OnCategorizePositionPost; + +void CreateGlobalForwards() +{ + H_OnStartDucking = CreateGlobalForward("Movement_OnStartDucking", ET_Ignore, Param_Cell); + H_OnStopDucking = CreateGlobalForward("Movement_OnStopDucking", ET_Ignore, Param_Cell); + H_OnStartTouchGround = CreateGlobalForward("Movement_OnStartTouchGround", ET_Ignore, Param_Cell); + H_OnStopTouchGround = CreateGlobalForward("Movement_OnStopTouchGround", ET_Ignore, Param_Cell, Param_Cell, Param_Cell, Param_Cell); + 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_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); + + H_OnDuckPre = CreateGlobalForward("Movement_OnDuckPre", ET_Event, Param_Cell, Param_Array, Param_Array); + H_OnDuckPost = CreateGlobalForward("Movement_OnDuckPost", ET_Event, Param_Cell, Param_Array, Param_Array); + + H_OnLadderMovePre = CreateGlobalForward("Movement_OnLadderMovePre", ET_Event, Param_Cell, Param_Array, Param_Array); + H_OnLadderMovePost = CreateGlobalForward("Movement_OnLadderMovePost", ET_Event, Param_Cell, Param_Array, Param_Array); + + H_OnFullLadderMovePre = CreateGlobalForward("Movement_OnFullLadderMovePre", ET_Event, Param_Cell, Param_Array, Param_Array); + H_OnFullLadderMovePost = CreateGlobalForward("Movement_OnFullLadderMovePost", ET_Event, Param_Cell, Param_Array, Param_Array); + + H_OnJumpPre = CreateGlobalForward("Movement_OnJumpPre", ET_Event, Param_Cell, Param_Array, Param_Array); + H_OnJumpPost = CreateGlobalForward("Movement_OnJumpPost", ET_Event, Param_Cell, Param_Array, Param_Array); + + H_OnAirAcceleratePre = CreateGlobalForward("Movement_OnAirAcceleratePre", ET_Event, Param_Cell, Param_Array, Param_Array); + H_OnAirAcceleratePost = CreateGlobalForward("Movement_OnAirAcceleratePost", ET_Event, Param_Cell, Param_Array, Param_Array); + + H_OnWalkMovePre = CreateGlobalForward("Movement_OnWalkMovePre", ET_Event, Param_Cell, Param_Array, Param_Array); + H_OnWalkMovePost = CreateGlobalForward("Movement_OnWalkMovePost", ET_Event, Param_Cell, Param_Array, Param_Array); + + H_OnCategorizePositionPre = CreateGlobalForward("Movement_OnCategorizePositionPre", ET_Event, Param_Cell, Param_Array, Param_Array); + H_OnCategorizePositionPost = CreateGlobalForward("Movement_OnCategorizePositionPost", ET_Event, Param_Cell, Param_Array, Param_Array); +} + +void Call_OnStartDucking(int client) +{ + Call_StartForward(H_OnStartDucking); + Call_PushCell(client); + Call_Finish(); +} + +void Call_OnStopDucking(int client) +{ + Call_StartForward(H_OnStopDucking); + Call_PushCell(client); + Call_Finish(); +} + +void Call_OnStartTouchGround(int client) +{ + Call_StartForward(H_OnStartTouchGround); + Call_PushCell(client); + Call_Finish(); +} + +void Call_OnStopTouchGround(int client, bool jumped, bool ladderJump, bool jumpbug) +{ + Call_StartForward(H_OnStopTouchGround); + Call_PushCell(client); + Call_PushCell(jumped); + Call_PushCell(ladderJump); + Call_PushCell(jumpbug); + Call_Finish(); + // Immediately update OldOnGround state, so we can catch takeoffs that happen outside movement processing. + gB_OldOnGround[client] = false; +} + + +void Call_OnChangeMovetype(int client, MoveType oldMovetype, MoveType newMovetype) +{ + Call_StartForward(H_OnChangeMovetype); + Call_PushCell(client); + Call_PushCell(oldMovetype); + Call_PushCell(newMovetype); + Call_Finish(); +} + +void Call_OnPlayerJump(int client, bool jumpbug) +{ + Call_StartForward(H_OnPlayerJump); + Call_PushCell(client); + Call_PushCell(jumpbug); + Call_Finish(); +} + +Action Call_OnPlayerMovePre(int client, float origin[3], float velocity[3], Action &result) +{ + Call_StartForward(H_OnPlayerMovePre); + Call_PushCell(client); + Call_PushArrayEx(origin, 3, SM_PARAM_COPYBACK); + Call_PushArrayEx(velocity, 3, SM_PARAM_COPYBACK); + Call_Finish(result); + return result; +} + +Action Call_OnPlayerMovePost(int client, float origin[3], float velocity[3], Action &result) +{ + Call_StartForward(H_OnPlayerMovePost); + Call_PushCell(client); + Call_PushArrayEx(origin, 3, SM_PARAM_COPYBACK); + Call_PushArrayEx(velocity, 3, SM_PARAM_COPYBACK); + Call_Finish(result); + return result; +} + +Action Call_OnDuckPre(int client, float origin[3], float velocity[3], Action &result) +{ + Call_StartForward(H_OnDuckPre); + Call_PushCell(client); + Call_PushArrayEx(origin, 3, SM_PARAM_COPYBACK); + Call_PushArrayEx(velocity, 3, SM_PARAM_COPYBACK); + Call_Finish(result); + return result; +} + +Action Call_OnDuckPost(int client, float origin[3], float velocity[3], Action &result) +{ + Call_StartForward(H_OnDuckPost); + Call_PushCell(client); + Call_PushArrayEx(origin, 3, SM_PARAM_COPYBACK); + Call_PushArrayEx(velocity, 3, SM_PARAM_COPYBACK); + Call_Finish(result); + return result; +} + +Action Call_OnLadderMovePre(int client, float origin[3], float velocity[3], Action &result) +{ + Call_StartForward(H_OnLadderMovePre); + Call_PushCell(client); + Call_PushArrayEx(origin, 3, SM_PARAM_COPYBACK); + Call_PushArrayEx(velocity, 3, SM_PARAM_COPYBACK); + Call_Finish(result); + return result; +} + +Action Call_OnLadderMovePost(int client, float origin[3], float velocity[3], Action &result) +{ + Call_StartForward(H_OnLadderMovePost); + Call_PushCell(client); + Call_PushArrayEx(origin, 3, SM_PARAM_COPYBACK); + Call_PushArrayEx(velocity, 3, SM_PARAM_COPYBACK); + Call_Finish(result); + return result; +} + +Action Call_OnFullLadderMovePre(int client, float origin[3], float velocity[3], Action &result) +{ + Call_StartForward(H_OnFullLadderMovePre); + Call_PushCell(client); + Call_PushArrayEx(origin, 3, SM_PARAM_COPYBACK); + Call_PushArrayEx(velocity, 3, SM_PARAM_COPYBACK); + Call_Finish(result); + return result; +} + +Action Call_OnFullLadderMovePost(int client, float origin[3], float velocity[3], Action &result) +{ + Call_StartForward(H_OnFullLadderMovePost); + Call_PushCell(client); + Call_PushArrayEx(origin, 3, SM_PARAM_COPYBACK); + Call_PushArrayEx(velocity, 3, SM_PARAM_COPYBACK); + Call_Finish(result); + return result; +} + +Action Call_OnJumpPre(int client, float origin[3], float velocity[3], Action &result) +{ + Call_StartForward(H_OnJumpPre); + Call_PushCell(client); + Call_PushArrayEx(origin, 3, SM_PARAM_COPYBACK); + Call_PushArrayEx(velocity, 3, SM_PARAM_COPYBACK); + Call_Finish(result); + return result; +} + +Action Call_OnJumpPost(int client, float origin[3], float velocity[3], Action &result) +{ + Call_StartForward(H_OnJumpPost); + Call_PushCell(client); + Call_PushArrayEx(origin, 3, SM_PARAM_COPYBACK); + Call_PushArrayEx(velocity, 3, SM_PARAM_COPYBACK); + Call_Finish(result); + return result; +} + +Action Call_OnAirAcceleratePre(int client, float origin[3], float velocity[3], Action &result) +{ + Call_StartForward(H_OnAirAcceleratePre); + Call_PushCell(client); + Call_PushArrayEx(origin, 3, SM_PARAM_COPYBACK); + Call_PushArrayEx(velocity, 3, SM_PARAM_COPYBACK); + Call_Finish(result); + return result; +} + +Action Call_OnAirAcceleratePost(int client, float origin[3], float velocity[3], Action &result) +{ + Call_StartForward(H_OnAirAcceleratePost); + Call_PushCell(client); + Call_PushArrayEx(origin, 3, SM_PARAM_COPYBACK); + Call_PushArrayEx(velocity, 3, SM_PARAM_COPYBACK); + Call_Finish(result); + return result; +} + +Action Call_OnWalkMovePre(int client, float origin[3], float velocity[3], Action &result) +{ + Call_StartForward(H_OnWalkMovePre); + Call_PushCell(client); + Call_PushArrayEx(origin, 3, SM_PARAM_COPYBACK); + Call_PushArrayEx(velocity, 3, SM_PARAM_COPYBACK); + Call_Finish(result); + return result; +} + +Action Call_OnWalkMovePost(int client, float origin[3], float velocity[3], Action &result) +{ + Call_StartForward(H_OnWalkMovePost); + Call_PushCell(client); + Call_PushArrayEx(origin, 3, SM_PARAM_COPYBACK); + Call_PushArrayEx(velocity, 3, SM_PARAM_COPYBACK); + Call_Finish(result); + return result; +} + +Action Call_OnCategorizePositionPre(int client, float origin[3], float velocity[3], Action &result) +{ + Call_StartForward(H_OnCategorizePositionPre); + Call_PushCell(client); + Call_PushArrayEx(origin, 3, SM_PARAM_COPYBACK); + Call_PushArrayEx(velocity, 3, SM_PARAM_COPYBACK); + Call_Finish(result); + return result; +} + +Action Call_OnCategorizePositionPost(int client, float origin[3], float velocity[3], Action &result) +{ + Call_StartForward(H_OnCategorizePositionPost); + Call_PushCell(client); + Call_PushArrayEx(origin, 3, SM_PARAM_COPYBACK); + Call_PushArrayEx(velocity, 3, SM_PARAM_COPYBACK); + Call_Finish(result); + return result; +}
\ No newline at end of file diff --git a/source/sourcemod/scripting/movementapi/hooks.sp b/source/sourcemod/scripting/movementapi/hooks.sp new file mode 100644 index 0000000..7b97b62 --- /dev/null +++ b/source/sourcemod/scripting/movementapi/hooks.sp @@ -0,0 +1,580 @@ +static DynamicDetour H_OnPlayerMove; +static DynamicDetour H_OnDuck; +static DynamicDetour H_OnLadderMove; +static DynamicDetour H_OnFullLadderMove; +static DynamicDetour H_OnJump; +static DynamicDetour H_OnAirAccelerate; +static DynamicDetour H_OnWalkMove; +static DynamicDetour H_OnCategorizePosition; + +float gF_Origin[MAXPLAYERS + 1][3]; +float gF_Velocity[MAXPLAYERS + 1][3]; + +bool gB_ProcessingLadderMove[MAXPLAYERS + 1]; +float gF_PreLadderMoveVelocity[MAXPLAYERS + 1][3]; +bool gB_TakeoffFromLadder[MAXPLAYERS + 1]; +float gF_PostLadderMoveOrigin[MAXPLAYERS + 1][3]; +float gF_PostLadderMoveVelocity[MAXPLAYERS + 1][3]; + +bool gB_ProcessingDuck[MAXPLAYERS + 1]; +bool gB_Ducking[MAXPLAYERS + 1]; +bool gB_PrevOnGround[MAXPLAYERS + 1]; +bool gB_Duckbugged[MAXPLAYERS + 1]; +float gF_PostDuckOrigin[MAXPLAYERS + 1][3]; + +bool gB_Jumpbugged[MAXPLAYERS + 1]; + +bool gB_WalkMoved[MAXPLAYERS + 1]; +float gF_PostWalkMoveVelocity[MAXPLAYERS + 1][3]; +float gF_PostAAOrigin[MAXPLAYERS + 1][3]; +float gF_PostAAVelocity[MAXPLAYERS + 1][3]; + +bool gB_OldWalkMoved[MAXPLAYERS + 1]; + +void HookGameMovementFunctions() +{ + HookGameMovementFunction(H_OnDuck, "CCSGameMovement::Duck", DHooks_OnDuck_Pre, DHooks_OnDuck_Post); + HookGameMovementFunction(H_OnLadderMove, "CGameMovement::LadderMove", DHooks_OnLadderMove_Pre, DHooks_OnLadderMove_Post); + HookGameMovementFunction(H_OnFullLadderMove, "CGameMovement::FullLadderMove", DHooks_OnFullLadderMove_Pre, DHooks_OnFullLadderMove_Post); + HookGameMovementFunction(H_OnAirAccelerate, "CGameMovement::AirAccelerate", DHooks_OnAirAccelerate_Pre, DHooks_OnAirAccelerate_Post); + HookGameMovementFunction(H_OnWalkMove, "CGameMovement::WalkMove", DHooks_OnWalkMove_Pre, DHooks_OnWalkMove_Post); + HookGameMovementFunction(H_OnJump, "CCSGameMovement::OnJump", DHooks_OnJump_Pre, DHooks_OnJump_Post); + HookGameMovementFunction(H_OnPlayerMove, "CCSGameMovement::PlayerMove", DHooks_OnPlayerMove_Pre, DHooks_OnPlayerMove_Post); + HookGameMovementFunction(H_OnCategorizePosition, "CGameMovement::CategorizePosition", DHooks_OnCategorizePosition_Pre, DHooks_OnCategorizePosition_Post); +} + +Action UpdateMoveData(Address pThis, int client, Function func) +{ + GameMove_GetOrigin(pThis, gF_Origin[client]); + GameMove_GetVelocity(pThis, gF_Velocity[client]); + Action result; + Call_StartFunction(INVALID_HANDLE, func); + Call_PushCell(client); + Call_PushArrayEx(gF_Origin[client], 3, SM_PARAM_COPYBACK); + Call_PushArrayEx(gF_Velocity[client], 3, SM_PARAM_COPYBACK); + Call_Finish(result); + if (result != Plugin_Continue) + { + GameMove_SetOrigin(pThis, gF_Origin[client]); + GameMove_SetVelocity(pThis, gF_Velocity[client]); + } + return result; +} + +public MRESReturn DHooks_OnDuck_Pre(Address pThis) +{ + int client = GetClientFromGameMovementAddress(pThis); + if (!IsPlayerAlive(client) || IsFakeClient(client) || Movement_GetMovetype(client) == MOVETYPE_NOCLIP) + { + return MRES_Ignored; + } + Action result = UpdateMoveData(pThis, client, Call_OnDuckPre); + + gB_Ducking[client] = Movement_GetDucking(client); + gB_ProcessingDuck[client] = true; + + if (result != Plugin_Continue) + { + return MRES_Handled; + } + else + { + return MRES_Ignored; + } +} + +public MRESReturn DHooks_OnDuck_Post(Address pThis) +{ + int client = GetClientFromGameMovementAddress(pThis); + if (!IsPlayerAlive(client) || IsFakeClient(client) || Movement_GetMovetype(client) == MOVETYPE_NOCLIP) + { + return MRES_Ignored; + } + + if (gB_Ducking[client] && !gB_OldDucking[client]) + { + Call_OnStartDucking(client); + } + else if (!gB_Ducking[client] && gB_OldDucking[client]) + { + Call_OnStopDucking(client); + } + gB_ProcessingDuck[client] = false; + GameMove_GetOrigin(pThis, gF_PostDuckOrigin[client]); + + Action result = UpdateMoveData(pThis, client, Call_OnDuckPost); + if (result != Plugin_Continue) + { + return MRES_Handled; + } + else + { + return MRES_Ignored; + } +} + +public MRESReturn DHooks_OnLadderMove_Pre(Address pThis) +{ + int client = GetClientFromGameMovementAddress(pThis); + if (!IsPlayerAlive(client) || IsFakeClient(client) || Movement_GetMovetype(client) == MOVETYPE_NOCLIP) + { + return MRES_Ignored; + } + Action result = UpdateMoveData(pThis, client, Call_OnLadderMovePre); + + gB_ProcessingLadderMove[client] = true; + GameMove_GetVelocity(pThis, gF_PreLadderMoveVelocity[client]); + + if (result != Plugin_Continue) + { + return MRES_Handled; + } + else + { + return MRES_Ignored; + } +} + +public MRESReturn DHooks_OnLadderMove_Post(Address pThis, DHookReturn hReturn) +{ + // While the movetype changed here, the vertical velocity is not yet updated. + // gF_PostLadderMoveVelocity can be incorrect here. + int client = GetClientFromGameMovementAddress(pThis); + if (!IsPlayerAlive(client) || IsFakeClient(client) || Movement_GetMovetype(client) == MOVETYPE_NOCLIP) + { + return MRES_Ignored; + } + + GameMove_GetOrigin(pThis, gF_PostLadderMoveOrigin[client]); + GameMove_GetVelocity(pThis, gF_PostLadderMoveVelocity[client]); + gB_ProcessingLadderMove[client] = false; + bool returnValue = DHookGetReturn(hReturn); + // If this returns false, and the movetype was originally MOVETYPE_LADDER, that means the player will change movetype and takeoff (LAJ) + // If this returns true, the movetype can still change in FullLadderMove by jumping (LAH) + // The current movetype here is still ladder, but it will change right after this function call. + if (!returnValue && Movement_GetMovetype(client) == MOVETYPE_LADDER) + { + gF_TakeoffVelocity[client] = gF_PostLadderMoveVelocity[client]; + gF_TakeoffOrigin[client] = gF_PostLadderMoveOrigin[client]; + gI_TakeoffTick[client] = gI_TickCount[client]; + gI_TakeoffCmdNum[client] = gI_Cmdnum[client]; + gB_Jumped[client] = false; + gB_HitPerf[client] = false; + Call_OnChangeMovetype(client, MOVETYPE_LADDER, MOVETYPE_WALK); + } + else if (returnValue && gMT_OldMovetype[client] != MOVETYPE_LADDER) + { + if (Movement_GetMovetype(client) == MOVETYPE_LADDER) + { + gF_LandingOrigin[client] = gF_PostLadderMoveOrigin[client]; + // We don't really care about nobug origin when player lands on ladder. + gF_NobugLandingOrigin[client] = gF_LandingOrigin[client]; + gF_LandingVelocity[client] = gF_PreLadderMoveVelocity[client]; + gI_LandingCmdNum[client] = gI_Cmdnum[client]; + gI_LandingTick[client] = gI_TickCount[client]; + Call_OnChangeMovetype(client, MOVETYPE_WALK, MOVETYPE_LADDER); + } + } + else if (returnValue && gMT_OldMovetype[client] == MOVETYPE_LADDER) + { + // Player is on the ladder and in the air, pressing jump pushes them away from the ladder. + float curtime = GetGameTime(); + int buttons = GetClientButtons(client); + float ignoreLadderJumpTime = GetEntPropFloat(client, Prop_Data, "m_ignoreLadderJumpTime"); + if (buttons & IN_JUMP && ignoreLadderJumpTime <= curtime) + { + gF_TakeoffVelocity[client] = gF_PostLadderMoveVelocity[client]; + gF_TakeoffOrigin[client] = gF_PostLadderMoveOrigin[client]; + gI_TakeoffTick[client] = gI_TickCount[client]; + gI_TakeoffCmdNum[client] = gI_Cmdnum[client]; + gB_Jumped[client] = false; + gB_HitPerf[client] = false; + gB_TakeoffFromLadder[client] = true; + Call_OnChangeMovetype(client, MOVETYPE_LADDER, MOVETYPE_WALK); + } + } + Action result = UpdateMoveData(pThis, client, Call_OnLadderMovePost); + if (result != Plugin_Continue) + { + return MRES_Handled; + } + else + { + return MRES_Ignored; + } +} + +public MRESReturn DHooks_OnFullLadderMove_Pre(Address pThis) +{ + int client = GetClientFromGameMovementAddress(pThis); + if (!IsPlayerAlive(client) || IsFakeClient(client)) + { + return MRES_Ignored; + } + Action result = UpdateMoveData(pThis, client, Call_OnFullLadderMovePre); + + if (result != Plugin_Continue) + { + return MRES_Handled; + } + else + { + return MRES_Ignored; + } +} + +public MRESReturn DHooks_OnJump_Pre(Address pThis, DHookParam hParams) +{ + int client = GetClientFromGameMovementAddress(pThis); + if (!IsPlayerAlive(client) || IsFakeClient(client)) + { + return MRES_Ignored; + } + + gB_Jumped[client] = true; + if (gB_Duckbugged[client]) + { + gB_Jumpbugged[client] = true; + } + + // HitPerf must be modified here so plugins can know if player hits a perf or not. + // Not a perf if last movetype was ladder, because jumping works differently on ladders. + if (gMT_OldMovetype[client] != MOVETYPE_LADDER) + { + // If you walked on the last tick then clearly it's not going to be a perf. + // Can't perf if you don't jump. + gB_HitPerf[client] = !gB_OldWalkMoved[client]; + } + else + { + gB_HitPerf[client] = false; + } + + Action result = UpdateMoveData(pThis, client, Call_OnJumpPre); + + if (result != Plugin_Continue) + { + return MRES_Handled; + } + else + { + return MRES_Ignored; + } +} + +public MRESReturn DHooks_OnJump_Post(Address pThis, DHookParam hParams) +{ + int client = GetClientFromGameMovementAddress(pThis); + if (!IsPlayerAlive(client) || IsFakeClient(client)) + { + return MRES_Ignored; + } + // We need to update LadderMove velocity again in case of jumping. + GameMove_GetVelocity(pThis, gF_PostLadderMoveVelocity[client]); + + // Current origin because the player hasn't moved yet. + gF_TakeoffOrigin[client] = gF_Origin[client]; + gF_TakeoffVelocity[client] = gF_Velocity[client]; + gI_TakeoffCmdNum[client] = gI_Cmdnum[client]; + gI_TakeoffTick[client] = gI_TickCount[client]; + + // OnJump will only be called if the client previously touched some sort of ground, so Call_OnStopTouchGround should always be called. + Call_OnStopTouchGround(client, true, gB_TakeoffFromLadder[client], gB_Jumpbugged[client]); + + Action result = UpdateMoveData(pThis, client, Call_OnJumpPost); + if (result != Plugin_Continue) + { + return MRES_Handled; + } + else + { + return MRES_Ignored; + } +} + +public MRESReturn DHooks_OnFullLadderMove_Post(Address pThis) +{ + int client = GetClientFromGameMovementAddress(pThis); + if (!IsPlayerAlive(client) || IsFakeClient(client) || Movement_GetMovetype(client) == MOVETYPE_NOCLIP) + { + return MRES_Ignored; + } + + Action result = UpdateMoveData(pThis, client, Call_OnFullLadderMovePost); + if (result != Plugin_Continue) + { + return MRES_Handled; + } + else + { + return MRES_Ignored; + } +} +// We hook AirAccelerate because TryPlayerMove in AirMove can change velocity +// AirAccelerate velocity is required for nobug landing origin. +public MRESReturn DHooks_OnAirAccelerate_Pre(Address pThis, DHookParam hParams) +{ + int client = GetClientFromGameMovementAddress(pThis); + if (!IsPlayerAlive(client) || IsFakeClient(client)) + { + return MRES_Ignored; + } + Action result = UpdateMoveData(pThis, client, Call_OnAirAcceleratePre); + + if (result != Plugin_Continue) + { + return MRES_Handled; + } + else + { + return MRES_Ignored; + } +} + +public MRESReturn DHooks_OnAirAccelerate_Post(Address pThis, DHookParam hParams) +{ + int client = GetClientFromGameMovementAddress(pThis); + if (!IsPlayerAlive(client) || IsFakeClient(client)) + { + return MRES_Ignored; + } + + GameMove_GetOrigin(pThis, gF_PostAAOrigin[client]); + GameMove_GetVelocity(pThis, gF_PostAAVelocity[client]); + + Action result = UpdateMoveData(pThis, client, Call_OnAirAcceleratePost); + if (result != Plugin_Continue) + { + return MRES_Handled; + } + else + { + return MRES_Ignored; + } +} + +// WalkMove is called too early to detect if the player is still on ground or not. +public MRESReturn DHooks_OnWalkMove_Pre(Address pThis) +{ + int client = GetClientFromGameMovementAddress(pThis); + if (!IsPlayerAlive(client) || IsFakeClient(client)) + { + return MRES_Ignored; + } + Action result = UpdateMoveData(pThis, client, Call_OnWalkMovePre); + + if (result != Plugin_Continue) + { + return MRES_Handled; + } + else + { + return MRES_Ignored; + } +} + +public MRESReturn DHooks_OnWalkMove_Post(Address pThis) +{ + int client = GetClientFromGameMovementAddress(pThis); + if (!IsPlayerAlive(client) || IsFakeClient(client)) + { + return MRES_Ignored; + } + + GameMove_GetVelocity(pThis, gF_PostWalkMoveVelocity[client]); + gB_WalkMoved[client] = true; + + Action result = UpdateMoveData(pThis, client, Call_OnWalkMovePost); + if (result != Plugin_Continue) + { + return MRES_Handled; + } + else + { + return MRES_Ignored; + } +} + +public MRESReturn DHooks_OnPlayerMove_Pre(Address pThis) +{ + int client = GetClientFromGameMovementAddress(pThis); + if (!IsPlayerAlive(client) || IsFakeClient(client)) + { + return MRES_Ignored; + } + + gB_Duckbugged[client] = false; + gB_WalkMoved[client] = false; + gB_Jumpbugged[client] = false; + gB_Jumped[client] = false; + gB_TakeoffFromLadder[client] = false; + + Action result = UpdateMoveData(pThis, client, Call_OnPlayerMovePre); + + if (result != Plugin_Continue) + { + return MRES_Handled; + } + else + { + return MRES_Ignored; + } +} + +public MRESReturn DHooks_OnPlayerMove_Post(Address pThis) +{ + int client = GetClientFromGameMovementAddress(pThis); + if (!IsPlayerAlive(client) || IsFakeClient(client)) + { + return MRES_Ignored; + } + Action result = UpdateMoveData(pThis, client, Call_OnPlayerMovePost); + + if (result != Plugin_Continue) + { + return MRES_Handled; + } + else + { + return MRES_Ignored; + } +} + +public MRESReturn DHooks_OnCategorizePosition_Pre(Address pThis) +{ + int client = GetClientFromGameMovementAddress(pThis); + if (!IsPlayerAlive(client) || IsFakeClient(client)) + { + return MRES_Ignored; + } + Action result = UpdateMoveData(pThis, client, Call_OnCategorizePositionPre); + + gB_PrevOnGround[client] = Movement_GetOnGround(client); + + if (result != Plugin_Continue) + { + return MRES_Handled; + } + else + { + return MRES_Ignored; + } +} + +public MRESReturn DHooks_OnCategorizePosition_Post(Address pThis) +{ + int client = GetClientFromGameMovementAddress(pThis); + if (!IsPlayerAlive(client) || IsFakeClient(client)) + { + return MRES_Ignored; + } + bool ground = Movement_GetOnGround(client); + // Ground state changed! + if (gB_PrevOnGround[client] != ground) + { + if (ground) // Landing + { + NobugLandingOrigin(client, gF_NobugLandingOrigin[client]); + + gF_LandingOrigin[client] = gF_Origin[client]; + gI_LandingCmdNum[client] = gI_Cmdnum[client]; + gI_LandingTick[client] = gI_TickCount[client]; + Call_OnStartTouchGround(client); + } + else // Takeoff + { + gF_TakeoffOrigin[client] = gF_OldOrigin[client]; + // Note: Jumping isn't detected here. + if (gB_WalkMoved[client]) + { + gF_TakeoffVelocity[client] = gF_PostWalkMoveVelocity[client]; + } + else + { + gF_TakeoffVelocity[client] = gF_PostLadderMoveVelocity[client]; + } + gI_TakeoffTick[client] = gI_TickCount[client]; + gI_TakeoffCmdNum[client] = gI_Cmdnum[client]; + gB_Jumped[client] = false; + gB_HitPerf[client] = false; + bool hadLadderMoveType = Movement_GetMovetype(client) == MOVETYPE_LADDER || gMT_OldMovetype[client] == MOVETYPE_LADDER; + Call_OnStopTouchGround(client, false, hadLadderMoveType && !gB_WalkMoved[client], false); + } + } + + Action result = UpdateMoveData(pThis, client, Call_OnCategorizePositionPost); + if (result != Plugin_Continue) + { + return MRES_Handled; + } + else + { + return MRES_Ignored; + } +} + +static void NobugLandingOrigin(int client, float landingOrigin[3]) +{ + // NOTE: Get ground position and distance to ground. + float groundEndPoint[3]; + groundEndPoint = gF_Origin[client]; + groundEndPoint[2] -= 2.0; + float mins[3] = {-16.0, -16.0, 0.0}; + float maxs[3] = {16.0, 16.0, 0.0}; + TR_TraceHullFilter(gF_Origin[client], groundEndPoint, mins, maxs, MASK_PLAYERSOLID, TraceEntityFilterPlayers, client); + + float groundPos[3]; + TR_GetEndPosition(groundPos); + + // NOTE: This is almost guaranteed to hit because CategorizePosition does + // the exact same trace to determine if the player is on the ground or not. + if (!TR_DidHit()) + { + // Use groundEndPoint if trace fails, because this MIGHT + // give less distance in this extremely rare case. + groundPos = groundEndPoint; + } + + gB_Duckbugged[client] = gB_ProcessingDuck[client]; + float distanceToGround = gF_Origin[client][2] - groundPos[2]; + float velocity[3], origin[3]; + // If there's any distance to the ground, then we'll trace it with this one. + + // It seems like sometimes the player can end up ever so slighly above this "ground" value, + // likely due to floating point precision error. Treat it as a bugged jump as well. + if (distanceToGround > 0.001 || gB_ProcessingDuck[client]) + { + // Use the current origin and velocity if we're not touching the ground + gF_LandingVelocity[client] = gF_Velocity[client]; + velocity = gF_Velocity[client]; + origin = gF_Origin[client]; + } + else + { + // NOTE: Use gF_OldVelocity and gF_OldOrigin if jump is potentially bugged. + gF_LandingVelocity[client] = gF_PostAAVelocity[client]; + velocity = gF_PostAAVelocity[client]; + origin = gF_PostAAOrigin[client]; + } + + float firstTraceEndpoint[3], scaledVelocity[3]; + scaledVelocity = velocity; + ScaleVector(scaledVelocity, GetTickInterval()); + AddVectors(origin, scaledVelocity, firstTraceEndpoint); + + TR_TraceHullFilter(origin, firstTraceEndpoint, mins, maxs, MASK_PLAYERSOLID, TraceEntityFilterPlayers, client); + if (!TR_DidHit()) + { + // It is possible to not hit the trace, if your vertical velocity is low enough. + // In an extreme case, you would need 10 more traces for this to hit. + // It is also possible to miss the trace on a flat jump, by hitting the very edge of a block. + + // Use groundPos, because this will give no distance advantage to the player, but + // it will let the player not have his jump invalidated. + landingOrigin = groundPos; + } + else + { + TR_GetEndPosition(landingOrigin); + } +} diff --git a/source/sourcemod/scripting/movementapi/natives.sp b/source/sourcemod/scripting/movementapi/natives.sp new file mode 100644 index 0000000..f8cd7a5 --- /dev/null +++ b/source/sourcemod/scripting/movementapi/natives.sp @@ -0,0 +1,183 @@ +void CreateNatives() +{ + CreateNative("Movement_GetJumped", Native_GetJumped); + CreateNative("Movement_GetHitPerf", Native_GetHitPerf); + CreateNative("Movement_GetTakeoffOrigin", Native_GetTakeoffOrigin); + CreateNative("Movement_GetTakeoffVelocity", Native_GetTakeoffVelocity); + CreateNative("Movement_GetTakeoffSpeed", Native_GetTakeoffSpeed); + CreateNative("Movement_GetTakeoffTick", Native_GetTakeoffTick); + CreateNative("Movement_GetTakeoffCmdNum", Native_GetTakeoffCmdNum); + CreateNative("Movement_GetNobugLandingOrigin", Native_GetNobugLandingOrigin); + CreateNative("Movement_GetLandingOrigin", Native_GetLandingOrigin); + CreateNative("Movement_GetLandingVelocity", Native_GetLandingVelocity); + CreateNative("Movement_GetLandingSpeed", Native_GetLandingSpeed); + CreateNative("Movement_GetLandingTick", Native_GetLandingTick); + CreateNative("Movement_GetLandingCmdNum", Native_GetLandingCmdNum); + CreateNative("Movement_GetTurning", Native_GetTurning); + CreateNative("Movement_GetTurningLeft", Native_GetTurningLeft); + CreateNative("Movement_GetTurningRight", Native_GetTurningRight); + CreateNative("Movement_GetMaxSpeed", Native_GetMaxSpeed); + CreateNative("Movement_GetDuckbugged", Native_GetDuckbugged); + CreateNative("Movement_GetJumpbugged", Native_GetJumpbugged); + CreateNative("Movement_GetProcessingOrigin", Native_GetProcessingOrigin); + CreateNative("Movement_GetProcessingVelocity", Native_GetProcessingVelocity); + CreateNative("Movement_SetTakeoffOrigin", Native_SetTakeoffOrigin); + CreateNative("Movement_SetTakeoffVelocity", Native_SetTakeoffVelocity); + CreateNative("Movement_SetLandingOrigin", Native_SetLandingOrigin); + CreateNative("Movement_SetLandingVelocity", Native_SetLandingVelocity); +} + +public int Native_GetJumped(Handle plugin, int numParams) +{ + return gB_Jumped[GetNativeCell(1)]; +} + +public int Native_GetHitPerf(Handle plugin, int numParams) +{ + return gB_HitPerf[GetNativeCell(1)]; +} + +public int Native_GetTakeoffOrigin(Handle plugin, int numParams) +{ + return SetNativeArray(2, gF_TakeoffOrigin[GetNativeCell(1)], 3); +} + +public int Native_GetTakeoffVelocity(Handle plugin, int numParams) +{ + return SetNativeArray(2, gF_TakeoffVelocity[GetNativeCell(1)], 3); +} + +public int Native_GetTakeoffSpeed(Handle plugin, int numParams) +{ + return view_as<int>(GetVectorHorizontalLength(gF_TakeoffVelocity[GetNativeCell(1)])); +} + +public int Native_GetTakeoffTick(Handle plugin, int numParams) +{ + return gI_TakeoffTick[GetNativeCell(1)]; +} + +public int Native_GetTakeoffCmdNum(Handle plugin, int numParams) +{ + return gI_TakeoffCmdNum[GetNativeCell(1)]; +} + +public int Native_GetNobugLandingOrigin(Handle plugin, int numParams) +{ + return SetNativeArray(2, gF_NobugLandingOrigin[GetNativeCell(1)], 3); +} + +public int Native_GetLandingOrigin(Handle plugin, int numParams) +{ + return SetNativeArray(2, gF_LandingOrigin[GetNativeCell(1)], 3); +} + +public int Native_GetLandingVelocity(Handle plugin, int numParams) +{ + return SetNativeArray(2, gF_LandingVelocity[GetNativeCell(1)], 3); +} + +public int Native_GetLandingSpeed(Handle plugin, int numParams) +{ + return view_as<int>(GetVectorHorizontalLength(gF_LandingVelocity[GetNativeCell(1)])); +} + +public int Native_GetLandingTick(Handle plugin, int numParams) +{ + return gI_LandingTick[GetNativeCell(1)]; +} + +public int Native_GetLandingCmdNum(Handle plugin, int numParams) +{ + return gI_LandingCmdNum[GetNativeCell(1)]; +} + +public int Native_GetTurning(Handle plugin, int numParams) +{ + return view_as<int>(gB_Turning[GetNativeCell(1)]); +} + +public int Native_GetTurningLeft(Handle plugin, int numParams) +{ + return view_as<int>(gB_TurningLeft[GetNativeCell(1)]); +} + +public int Native_GetTurningRight(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + return view_as<int>(gB_Turning[client] && !gB_TurningLeft[client]); +} + +public int Native_GetMaxSpeed(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + return view_as<int>(GetMaxSpeed(client)); +} + +public int Native_GetDuckbugged(Handle plugin, int numParams) +{ + return view_as<int>(gB_Duckbugged[GetNativeCell(1)]); +} + +public int Native_GetJumpbugged(Handle plugin, int numParams) +{ + return view_as<int>(gB_Jumpbugged[GetNativeCell(1)]); +} + +public int Native_GetProcessingOrigin(Handle plugin, int numParams) +{ + return SetNativeArray(2, gF_Origin[GetNativeCell(1)], 3); +} + +public int Native_GetProcessingVelocity(Handle plugin, int numParams) +{ + return SetNativeArray(2, gF_Velocity[GetNativeCell(1)], 3); +} + +public int Native_SetTakeoffOrigin(Handle plugin, int numParams) +{ + float array[3]; + GetNativeArray(2, array, sizeof(array)); + for (int i = 0; i < 3; i++) + { + gF_TakeoffOrigin[GetNativeCell(1)][i] = array[i]; + } + + return 0; +} + +public int Native_SetTakeoffVelocity(Handle plugin, int numParams) +{ + float array[3]; + GetNativeArray(2, array, sizeof(array)); + for (int i = 0; i < 3; i++) + { + gF_TakeoffVelocity[GetNativeCell(1)][i] = array[i]; + } + + return 0; +} + +public int Native_SetLandingOrigin(Handle plugin, int numParams) +{ + float array[3]; + GetNativeArray(2, array, sizeof(array)); + for (int i = 0; i < 3; i++) + { + gF_LandingOrigin[GetNativeCell(1)][i] = array[i]; + } + + return 0; +} + +public int Native_SetLandingVelocity(Handle plugin, int numParams) +{ + float array[3]; + GetNativeArray(2, array, sizeof(array)); + for (int i = 0; i < 3; i++) + { + gF_LandingVelocity[GetNativeCell(1)][i] = array[i]; + } + + return 0; +}
\ No newline at end of file diff --git a/source/sourcemod/scripting/movementapi/stocks.sp b/source/sourcemod/scripting/movementapi/stocks.sp new file mode 100644 index 0000000..e080efc --- /dev/null +++ b/source/sourcemod/scripting/movementapi/stocks.sp @@ -0,0 +1,200 @@ +stock void GameMove_SetVelocity(Address addr, float velocity[3]) +{ + if (velocity[0] != velocity[0] || velocity[1] != velocity[1] || velocity[2] != velocity[2]) + { + return; + } + static int mvOffset; + static int velocityOffset; + if (!mvOffset) + { + char buffer[8]; + if (!gH_GameData.GetKeyValue("CGameMovement::mv", buffer, sizeof(buffer))) + { + ThrowError("Failed to get CGameMovement::mv offset."); + return; + } + mvOffset = StringToInt(buffer); + if (!gH_GameData.GetKeyValue("CMoveData::m_vecVelocity", buffer, sizeof(buffer))) + { + ThrowError("Failed to get CMoveData::m_vecVelocity offset."); + return; + } + velocityOffset = StringToInt(buffer); + } + + Address mvAddress = view_as<Address>(LoadFromAddress(view_as<Address>(view_as<int>(addr) + mvOffset), NumberType_Int32)); + // TODO: idk if raw cast works here or not + StoreToAddress(view_as<Address>(view_as<int>(mvAddress) + velocityOffset), view_as<int>(velocity[0]), NumberType_Int32); + StoreToAddress(view_as<Address>(view_as<int>(mvAddress) + velocityOffset + 4), view_as<int>(velocity[1]), NumberType_Int32); + StoreToAddress(view_as<Address>(view_as<int>(mvAddress) + velocityOffset + 8), view_as<int>(velocity[2]), NumberType_Int32); +} + +stock void GameMove_SetOrigin(Address addr, float origin[3]) +{ + if (origin[0] != origin[0] || origin[1] != origin[1] || origin[2] != origin[2]) + { + return; + } + static int mvOffset; + static int originOffset; + if (!mvOffset) + { + char buffer[8]; + if (!gH_GameData.GetKeyValue("CGameMovement::mv", buffer, sizeof(buffer))) + { + ThrowError("Failed to get CGameMovement::mv offset."); + return; + } + mvOffset = StringToInt(buffer); + if (!gH_GameData.GetKeyValue("CMoveData::m_vecAbsOrigin", buffer, sizeof(buffer))) + { + ThrowError("Failed to get CMoveData::m_vecAbsOrigin offset."); + return; + } + originOffset = StringToInt(buffer); + } + + Address mvAddress = view_as<Address>(LoadFromAddress(view_as<Address>(view_as<int>(addr) + mvOffset), NumberType_Int32)); + // TODO: idk if raw cast works here or not + StoreToAddress(view_as<Address>(view_as<int>(mvAddress) + originOffset), view_as<int>(origin[0]), NumberType_Int32); + StoreToAddress(view_as<Address>(view_as<int>(mvAddress) + originOffset + 4), view_as<int>(origin[1]), NumberType_Int32); + StoreToAddress(view_as<Address>(view_as<int>(mvAddress) + originOffset + 8), view_as<int>(origin[2]), NumberType_Int32); +} + +stock void GameMove_GetVelocity(Address addr, float result[3]) +{ + static int mvOffset; + static int velocityOffset; + if (!mvOffset) + { + char buffer[8]; + if (!gH_GameData.GetKeyValue("CGameMovement::mv", buffer, sizeof(buffer))) + { + ThrowError("Failed to get CGameMovement::mv offset."); + return; + } + mvOffset = StringToInt(buffer); + if (!gH_GameData.GetKeyValue("CMoveData::m_vecVelocity", buffer, sizeof(buffer))) + { + ThrowError("Failed to get CMoveData::m_vecVelocity offset."); + return; + } + velocityOffset = StringToInt(buffer); + } + + Address mvAddress = view_as<Address>(LoadFromAddress(view_as<Address>(view_as<int>(addr) + mvOffset), NumberType_Int32)); + result[0] = view_as<float>(LoadFromAddress(view_as<Address>(view_as<int>(mvAddress) + velocityOffset), NumberType_Int32)); + result[1] = view_as<float>(LoadFromAddress(view_as<Address>(view_as<int>(mvAddress) + velocityOffset + 4), NumberType_Int32)); + result[2] = view_as<float>(LoadFromAddress(view_as<Address>(view_as<int>(mvAddress) + velocityOffset + 8), NumberType_Int32)); +} + +stock void GameMove_GetOrigin(Address addr, float result[3]) +{ + static int mvOffset; + static int originOffset; + if (!mvOffset) + { + char buffer[8]; + if (!gH_GameData.GetKeyValue("CGameMovement::mv", buffer, sizeof(buffer))) + { + ThrowError("Failed to get CGameMovement::mv offset."); + return; + } + mvOffset = StringToInt(buffer); + if (!gH_GameData.GetKeyValue("CMoveData::m_vecAbsOrigin", buffer, sizeof(buffer))) + { + ThrowError("Failed to get CMoveData::m_vecAbsOrigin offset."); + return; + } + originOffset = StringToInt(buffer); + } + + Address mvAddress = view_as<Address>(LoadFromAddress(view_as<Address>(view_as<int>(addr) + mvOffset), NumberType_Int32)); + result[0] = view_as<float>(LoadFromAddress(view_as<Address>(view_as<int>(mvAddress) + originOffset), NumberType_Int32)); + result[1] = view_as<float>(LoadFromAddress(view_as<Address>(view_as<int>(mvAddress) + originOffset + 4), NumberType_Int32)); + result[2] = view_as<float>(LoadFromAddress(view_as<Address>(view_as<int>(mvAddress) + originOffset + 8), NumberType_Int32)); +} + +stock void GameMove_GetEyeAngles(Address addr, float result[3]) +{ + static int mvOffset; + static int viewAngleOffset; + if (!mvOffset) + { + char buffer[8]; + if (!gH_GameData.GetKeyValue("CGameMovement::mv", buffer, sizeof(buffer))) + { + ThrowError("Failed to get CGameMovement::mv offset."); + return; + } + mvOffset = StringToInt(buffer); + if (!gH_GameData.GetKeyValue("CMoveData::m_viewAngleOffset", buffer, sizeof(buffer))) + { + ThrowError("Failed to get CMoveData::m_viewAngleOffset offset."); + return; + } + viewAngleOffset = StringToInt(buffer); + } + + Address mvAddress = view_as<Address>(LoadFromAddress(view_as<Address>(view_as<int>(addr) + mvOffset), NumberType_Int32)); + result[0] = view_as<float>(LoadFromAddress(view_as<Address>(view_as<int>(mvAddress) + viewAngleOffset), NumberType_Int32)); + result[1] = view_as<float>(LoadFromAddress(view_as<Address>(view_as<int>(mvAddress) + viewAngleOffset + 4), NumberType_Int32)); + result[2] = view_as<float>(LoadFromAddress(view_as<Address>(view_as<int>(mvAddress) + viewAngleOffset + 8), NumberType_Int32)); +} + +stock int GetEntityFromAddress(Address pEntity) { + static int offs_RefEHandle; + if (offs_RefEHandle) { + return EntRefToEntIndex(LoadFromAddress(pEntity + view_as<Address>(offs_RefEHandle), NumberType_Int32) | (1 << 31)); + } + + // if we don't have it already, attempt to lookup offset based on SDK information + // CWorld is derived from CBaseEntity so it should have both offsets + int offs_angRotation = FindDataMapInfo(0, "m_angRotation"), + offs_vecViewOffset = FindDataMapInfo(0, "m_vecViewOffset"); + if (offs_angRotation == -1) { + ThrowError("Could not find offset for ((CBaseEntity) CWorld)::m_angRotation"); + } else if (offs_vecViewOffset == -1) { + ThrowError("Could not find offset for ((CBaseEntity) CWorld)::m_vecViewOffset"); + } else if ((offs_angRotation + 0x0C) != (offs_vecViewOffset - 0x04)) { + char game[32]; + GetGameFolderName(game, sizeof(game)); + ThrowError("Could not confirm offset of CBaseEntity::m_RefEHandle " + ... "(incorrect assumption for game '%s'?)", game); + } + + // offset seems right, cache it for the next call + offs_RefEHandle = offs_angRotation + 0x0C; + return GetEntityFromAddress(pEntity); +} + +stock int GetClientFromGameMovementAddress(Address addr) +{ + char buffer[8]; + if (!gH_GameData.GetKeyValue("CGameMovement::player", buffer, sizeof(buffer))) + { + ThrowError("Failed to get CGameMovement::player offset."); + return -1; + } + int offset = StringToInt(buffer); + Address playerAddr = view_as<Address>(LoadFromAddress(view_as<Address>(view_as<int>(addr) + offset), NumberType_Int32)); + return GetEntityFromAddress(playerAddr); +} + +stock void HookGameMovementFunction(DynamicDetour handle, char[] fName, DHookCallback preCallback, DHookCallback postCallback) +{ + handle = DynamicDetour.FromConf(gH_GameData, fName); + if (!handle) + { + SetFailState("Failed to find %s config", fName); + } + if (!handle.Enable(Hook_Pre, preCallback)) + { + SetFailState("Failed to enable detour on %s", fName); + } + if (!handle.Enable(Hook_Post, postCallback)) + { + SetFailState("Failed to enable detour on %s", fName); + } +} |
