mirror of
https://github.com/t5mat/crouchboostfix.git
synced 2025-12-06 18:08:38 +00:00
261 lines
8.8 KiB
SourcePawn
261 lines
8.8 KiB
SourcePawn
#include <sourcemod>
|
|
#include <sdkhooks>
|
|
#include <sdktools>
|
|
|
|
#pragma semicolon 1
|
|
#pragma newdecls required
|
|
|
|
public Plugin myinfo = {
|
|
name = "crouchboostfix",
|
|
version = "2.0.2",
|
|
author = "https://github.com/t5mat",
|
|
description = "Prevents crouchboosting",
|
|
url = "https://github.com/t5mat/crouchboostfix",
|
|
};
|
|
|
|
#define CROUCHBOOST_TIME (0.25)
|
|
|
|
#define MAX_ENTITIES (4096)
|
|
|
|
enum struct Engine
|
|
{
|
|
int m_vecMins;
|
|
int m_vecMaxs;
|
|
int m_flLaggedMovementValue;
|
|
|
|
int m_vecAbsOrigin;
|
|
int m_vecAbsVelocity;
|
|
|
|
void Initialize(int entity = -1, const char[] classname = "")
|
|
{
|
|
static bool start = false;
|
|
if (!start) {
|
|
start = true;
|
|
(this.m_vecMins = FindSendPropInfo("CBaseEntity", "m_vecMins")) == -1 && SetFailState("CBaseEntity::m_vecMins");
|
|
(this.m_vecMaxs = FindSendPropInfo("CBaseEntity", "m_vecMaxs")) == -1 && SetFailState("CBaseEntity::m_vecMaxs");
|
|
(this.m_flLaggedMovementValue = FindSendPropInfo("CBasePlayer", "m_flLaggedMovementValue")) == -1 && SetFailState("CBasePlayer::m_flLaggedMovementValue");
|
|
}
|
|
|
|
static bool CBaseEntity = false;
|
|
if (!CBaseEntity && entity != -1) {
|
|
CBaseEntity = true;
|
|
(this.m_vecAbsOrigin = FindDataMapInfo(entity, "m_vecAbsOrigin")) == -1 && SetFailState("CBaseEntity::m_vecAbsOrigin");
|
|
(this.m_vecAbsVelocity = FindDataMapInfo(entity, "m_vecAbsVelocity")) == -1 && SetFailState("CBaseEntity::m_vecAbsVelocity");
|
|
}
|
|
}
|
|
}
|
|
|
|
enum struct Client
|
|
{
|
|
float origin[3];
|
|
float velocity[3];
|
|
float mins[3];
|
|
float maxs[3];
|
|
int flags;
|
|
float frame;
|
|
bool validTouch[MAX_ENTITIES];
|
|
float endTouchFrame[MAX_ENTITIES];
|
|
bool endTouchDuck[MAX_ENTITIES];
|
|
}
|
|
|
|
bool g_late;
|
|
ConVar g_crouchboostfix_enabled;
|
|
Engine g_engine;
|
|
Client g_clients[MAXPLAYERS + 1];
|
|
|
|
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
|
|
{
|
|
RegPluginLibrary("crouchboostfix");
|
|
g_late = late;
|
|
return APLRes_Success;
|
|
}
|
|
|
|
public void OnPluginStart()
|
|
{
|
|
g_engine.Initialize();
|
|
|
|
g_crouchboostfix_enabled = CreateConVar("crouchboostfix_enabled", "1", "Enable crouchboost prevention", FCVAR_NOTIFY, true, 0.0, true, 1.0);
|
|
|
|
AutoExecConfig();
|
|
|
|
HookEntityOutput("trigger_multiple", "OnStartTouch", Hook_EntityOutput);
|
|
HookEntityOutput("trigger_multiple", "OnEndTouch", Hook_EntityOutput);
|
|
HookEntityOutput("trigger_push", "OnStartTouch", Hook_EntityOutput);
|
|
HookEntityOutput("trigger_push", "OnEndTouch", Hook_EntityOutput);
|
|
HookEntityOutput("trigger_gravity", "OnStartTouch", Hook_EntityOutput);
|
|
HookEntityOutput("trigger_gravity", "OnEndTouch", Hook_EntityOutput);
|
|
|
|
if (g_late) {
|
|
for (int e = 0; e < sizeof(Client::validTouch); ++e) {
|
|
if (IsValidEntity(e)) {
|
|
char classname[64];
|
|
GetEntityClassname(e, classname, sizeof(classname));
|
|
OnEntityCreated(e, classname);
|
|
}
|
|
}
|
|
|
|
for (int c = 1; c <= MaxClients; ++c) {
|
|
if (IsClientInGame(c)) {
|
|
OnClientPutInServer(c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnEntityCreated(int entity, const char[] classname)
|
|
{
|
|
g_engine.Initialize(entity, classname);
|
|
|
|
bool push = StrEqual(classname, "trigger_push");
|
|
if (StrEqual(classname, "trigger_multiple") || push || StrEqual(classname, "trigger_gravity")) {
|
|
for (int i = 0; i < sizeof(g_clients); ++i) {
|
|
g_clients[i].validTouch[entity] = false;
|
|
g_clients[i].endTouchFrame[entity] = -1.0;
|
|
}
|
|
|
|
SDKHook(entity, SDKHook_StartTouch, Hook_TriggerStartTouch);
|
|
SDKHook(entity, SDKHook_EndTouchPost, Hook_TriggerEndTouchPost);
|
|
if (push) {
|
|
SDKHook(entity, SDKHook_Touch, Hook_TriggerPushTouch);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnClientPutInServer(int client)
|
|
{
|
|
g_clients[client].frame = 0.0;
|
|
for (int i = 0; i < sizeof(Client::endTouchFrame); ++i) {
|
|
g_clients[client].endTouchFrame[i] = -1.0;
|
|
}
|
|
|
|
SDKHook(client, SDKHook_PreThinkPost, Hook_ClientPreThinkPost);
|
|
}
|
|
|
|
Action Hook_EntityOutput(const char[] output, int caller, int activator, float delay)
|
|
{
|
|
caller = EntRefToEntIndex(caller);
|
|
if (caller == -1 || caller > sizeof(Client::validTouch) - 1) {
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
activator = EntRefToEntIndex(activator);
|
|
if (activator < 1 || activator > sizeof(g_clients) - 1) {
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
if (g_crouchboostfix_enabled.BoolValue && !g_clients[activator].validTouch[caller]) {
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
void Hook_ClientPreThinkPost(int client)
|
|
{
|
|
GetEntDataVector(client, g_engine.m_vecAbsOrigin, g_clients[client].origin);
|
|
GetEntDataVector(client, g_engine.m_vecAbsVelocity, g_clients[client].velocity);
|
|
GetEntDataVector(client, g_engine.m_vecMins, g_clients[client].mins);
|
|
GetEntDataVector(client, g_engine.m_vecMaxs, g_clients[client].maxs);
|
|
g_clients[client].flags = GetEntityFlags(client);
|
|
|
|
g_clients[client].frame += GetEntDataFloat(client, g_engine.m_flLaggedMovementValue);
|
|
}
|
|
|
|
Action Hook_TriggerStartTouch(int entity, int other)
|
|
{
|
|
if (other > sizeof(g_clients) - 1) {
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
if (g_clients[other].endTouchFrame[entity] != -1.0 && g_clients[other].frame - g_clients[other].endTouchFrame[entity] < CROUCHBOOST_TIME / GetTickInterval()) {
|
|
bool startTouchUnduck = false;
|
|
|
|
if (!g_clients[other].endTouchDuck[entity]) {
|
|
// Were we mid-air last tick?
|
|
if (!(g_clients[other].flags & FL_ONGROUND)) {
|
|
// Did we unduck?
|
|
if ((g_clients[other].flags & FL_DUCKING) && !(GetEntityFlags(other) & FL_DUCKING)) {
|
|
// Had we not unducked, would we still be not touching the trigger?
|
|
float origin[3];
|
|
origin = g_clients[other].velocity;
|
|
ScaleVector(origin, GetTickInterval() * GetEntDataFloat(other, g_engine.m_flLaggedMovementValue));
|
|
AddVectors(origin, g_clients[other].origin, origin);
|
|
if (!DoHullEntityIntersect(origin, g_clients[other].mins, g_clients[other].maxs, entity)) {
|
|
// This StartTouch was caused by a mid-air unduck
|
|
startTouchUnduck = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If this StartTouch happened too soon after the last EndTouch, and either:
|
|
// - the last EndTouch was caused by a mid-air duck, or
|
|
// - this StartTouch was caused by a mid-air unduck
|
|
// then this StartTouch is considered "invalid", disable pushing so we don't get boosted again
|
|
g_clients[other].validTouch[entity] = !(g_clients[other].endTouchDuck[entity] || startTouchUnduck);
|
|
} else {
|
|
g_clients[other].validTouch[entity] = true;
|
|
}
|
|
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
void Hook_TriggerEndTouchPost(int entity, int other)
|
|
{
|
|
if (other > sizeof(g_clients) - 1) {
|
|
return;
|
|
}
|
|
|
|
g_clients[other].validTouch[entity] = false;
|
|
g_clients[other].endTouchFrame[entity] = g_clients[other].frame;
|
|
g_clients[other].endTouchDuck[entity] = false;
|
|
|
|
// Were we mid-air last tick?
|
|
if (!(g_clients[other].flags & FL_ONGROUND)) {
|
|
// Did we duck?
|
|
if (!(g_clients[other].flags & FL_DUCKING) && (GetEntityFlags(other) & FL_DUCKING)) {
|
|
// Had we not ducked, would we still be touching the trigger?
|
|
float origin[3];
|
|
origin = g_clients[other].velocity;
|
|
ScaleVector(origin, GetTickInterval() * GetEntDataFloat(other, g_engine.m_flLaggedMovementValue));
|
|
AddVectors(origin, g_clients[other].origin, origin);
|
|
if (DoHullEntityIntersect(origin, g_clients[other].mins, g_clients[other].maxs, entity)) {
|
|
// This EndTouch was caused by a mid-air duck
|
|
g_clients[other].endTouchDuck[entity] = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Action Hook_TriggerPushTouch(int entity, int other)
|
|
{
|
|
if (other > sizeof(g_clients) - 1) {
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
if (g_crouchboostfix_enabled.BoolValue && !g_clients[other].validTouch[entity]) {
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
bool g_DoHullEntityIntersect_hit;
|
|
|
|
bool DoHullEntityIntersect(const float origin[3], const float mins[3], const float maxs[3], int entity, int mask = PARTITION_TRIGGER_EDICTS)
|
|
{
|
|
g_DoHullEntityIntersect_hit = false;
|
|
TR_EnumerateEntitiesHull(origin, origin, mins, maxs, mask, Trace_DoHullEntityIntersect, entity);
|
|
return g_DoHullEntityIntersect_hit;
|
|
}
|
|
|
|
bool Trace_DoHullEntityIntersect(int entity, any data)
|
|
{
|
|
if (entity == data) {
|
|
TR_ClipCurrentRayToEntity(MASK_ALL, entity);
|
|
g_DoHullEntityIntersect_hit = TR_DidHit();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|