mirror of
https://github.com/t5mat/crouchboostfix.git
synced 2025-12-06 18:08:38 +00:00
377 lines
13 KiB
SourcePawn
377 lines
13 KiB
SourcePawn
#include <sourcemod>
|
|
#include <sdkhooks>
|
|
#include <sdktools>
|
|
|
|
#pragma semicolon 1
|
|
#pragma newdecls required
|
|
|
|
public Plugin myinfo = {
|
|
name = "boostfix",
|
|
version = "1.0.0",
|
|
author = "https://github.com/t5mat",
|
|
description = "Fixes buggy push triggers; prevents crouchboosting",
|
|
url = "https://github.com/t5mat/boostfix",
|
|
};
|
|
|
|
#define SOLID_NONE (0)
|
|
#define FSOLID_NOT_SOLID (0x0004)
|
|
|
|
#define SF_TRIG_PUSH_ONCE (0x80)
|
|
#define SF_TRIG_PUSH_AFFECT_PLAYER_ON_LADDER (0x100)
|
|
|
|
#define VEC_HULL_MIN (view_as<float>({-16.0, -16.0, 0.0}))
|
|
#define VEC_HULL_MAX (view_as<float>({16.0, 16.0, 72.0}))
|
|
#define VEC_DUCK_HULL_MIN (view_as<float>({-16.0, -16.0, 0.0}))
|
|
#define VEC_DUCK_HULL_MAX (view_as<float>({16.0, 16.0, 54.0}))
|
|
|
|
#define CROUCHBOOST_TIME (0.25)
|
|
|
|
enum struct Engine
|
|
{
|
|
int m_nSolidType;
|
|
int m_usSolidFlags;
|
|
int m_spawnflags;
|
|
int m_vecBaseVelocity;
|
|
int m_flLaggedMovementValue;
|
|
int m_hGroundEntity;
|
|
Handle PassesTriggerFilters_;
|
|
int m_hMoveParent;
|
|
int m_vecAbsOrigin;
|
|
int m_angAbsRotation;
|
|
int m_vecAbsVelocity;
|
|
int m_flSpeed;
|
|
int m_vecPushDir;
|
|
|
|
void Initialize()
|
|
{
|
|
(this.m_nSolidType = FindSendPropInfo("CBaseEntity", "m_nSolidType")) == -1 && SetFailState("CBaseEntity::m_nSolidType");
|
|
(this.m_usSolidFlags = FindSendPropInfo("CBaseEntity", "m_usSolidFlags")) == -1 && SetFailState("CBaseEntity::m_usSolidFlags");
|
|
(this.m_spawnflags = FindSendPropInfo("CBaseTrigger", "m_spawnflags")) == -1 && SetFailState("CBaseTrigger::m_spawnflags");
|
|
(this.m_vecBaseVelocity = FindSendPropInfo("CBasePlayer", "m_vecBaseVelocity")) == -1 && SetFailState("CBasePlayer::m_vecBaseVelocity");
|
|
(this.m_flLaggedMovementValue = FindSendPropInfo("CBasePlayer", "m_flLaggedMovementValue")) == -1 && SetFailState("CBasePlayer::m_flLaggedMovementValue");
|
|
(this.m_hGroundEntity = FindSendPropInfo("CBasePlayer", "m_hGroundEntity")) == -1 && SetFailState("CBasePlayer::m_hGroundEntity");
|
|
|
|
GameData gd;
|
|
(gd = new GameData("rngfix.games")) == null && SetFailState("rngfix.games");
|
|
|
|
StartPrepSDKCall(SDKCall_Entity);
|
|
PrepSDKCall_SetFromConf(gd, SDKConf_Virtual, "CBaseTrigger::PassesTriggerFilters");
|
|
PrepSDKCall_SetReturnInfo(SDKType_Bool, SDKPass_Plain);
|
|
PrepSDKCall_AddParameter(SDKType_CBaseEntity, SDKPass_Pointer);
|
|
(this.PassesTriggerFilters_ = EndPrepSDKCall()) == null && SetFailState("CBaseTrigger::PassesTriggerFilters");
|
|
|
|
delete gd;
|
|
|
|
this.m_hMoveParent = -1;
|
|
this.m_vecPushDir = -1;
|
|
}
|
|
|
|
void InitializeCBaseEntityOffsets(int entity)
|
|
{
|
|
if (this.m_hMoveParent != -1) {
|
|
return;
|
|
}
|
|
|
|
(this.m_hMoveParent = FindDataMapInfo(entity, "m_hMoveParent")) == -1 && SetFailState("CBaseEntity::m_hMoveParent");
|
|
(this.m_vecAbsOrigin = FindDataMapInfo(entity, "m_vecAbsOrigin")) == -1 && SetFailState("CBaseEntity::m_vecAbsOrigin");
|
|
(this.m_angAbsRotation = FindDataMapInfo(entity, "m_angAbsRotation")) == -1 && SetFailState("CBaseEntity::m_angAbsRotation");
|
|
(this.m_vecAbsVelocity = FindDataMapInfo(entity, "m_vecAbsVelocity")) == -1 && SetFailState("CBaseEntity::m_vecAbsVelocity");
|
|
(this.m_flSpeed = FindDataMapInfo(entity, "m_flSpeed")) == -1 && SetFailState("CBaseEntity::m_flSpeed");
|
|
}
|
|
|
|
void InitializeCTriggerPushOffsets(int entity)
|
|
{
|
|
if (this.m_vecPushDir != -1) {
|
|
return;
|
|
}
|
|
|
|
(this.m_vecPushDir = FindDataMapInfo(entity, "m_vecPushDir")) == -1 && SetFailState("CTriggerPush::m_vecPushDir");
|
|
}
|
|
|
|
bool PassesTriggerFilters(int entity, int other)
|
|
{
|
|
return SDKCall(this.PassesTriggerFilters_, entity, other);
|
|
}
|
|
}
|
|
|
|
enum struct Client
|
|
{
|
|
float origin[3];
|
|
float velocity[3];
|
|
int flags;
|
|
float frame;
|
|
bool touching[4096];
|
|
float endTouchFrame[4096];
|
|
bool endTouchDuck[4096];
|
|
}
|
|
|
|
bool g_late;
|
|
ConVar g_boostfix_pushfix;
|
|
ConVar g_boostfix_crouchboostfix;
|
|
Engine g_engine;
|
|
Client g_clients[MAXPLAYERS + 1];
|
|
|
|
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
|
|
{
|
|
if (GetEngineVersion() != Engine_CSGO) {
|
|
FormatEx(error, err_max, "Not supported");
|
|
return APLRes_Failure;
|
|
}
|
|
|
|
g_late = late;
|
|
return APLRes_Success;
|
|
}
|
|
|
|
public void OnPluginStart()
|
|
{
|
|
g_engine.Initialize();
|
|
|
|
g_boostfix_pushfix = CreateConVar("boostfix_pushfix", "1", "Enable trigger_push fix", FCVAR_NOTIFY, true, 0.0, true, 1.0);
|
|
g_boostfix_crouchboostfix = CreateConVar("boostfix_crouchboostfix", "1", "Enable crouchboost prevention", FCVAR_NOTIFY, true, 0.0, true, 1.0);
|
|
|
|
if (g_late) {
|
|
for (int e = 0; e < sizeof(Client::touching); ++e) {
|
|
if ((e < 1 || e > sizeof(g_clients) - 1) && 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.InitializeCBaseEntityOffsets(entity);
|
|
|
|
if (StrEqual(classname, "trigger_push")) {
|
|
g_engine.InitializeCTriggerPushOffsets(entity);
|
|
|
|
for (int i = 0; i < sizeof(g_clients); ++i) {
|
|
g_clients[i].touching[entity] = false;
|
|
}
|
|
|
|
SDKHook(entity, SDKHook_StartTouch, Hook_PushStartTouch);
|
|
SDKHook(entity, SDKHook_EndTouch, Hook_PushEndTouch);
|
|
SDKHook(entity, SDKHook_Touch, Hook_PushTouch);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
g_clients[client].flags = GetEntityFlags(client);
|
|
|
|
g_clients[client].frame += GetEntDataFloat(client, g_engine.m_flLaggedMovementValue);
|
|
}
|
|
|
|
Action Hook_PushStartTouch(int entity, int other)
|
|
{
|
|
if (other > sizeof(g_clients) - 1) {
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
if (g_boostfix_crouchboostfix.BoolValue && 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 (!DoesHullEntityIntersect(origin, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX, 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].touching[entity] = !(g_clients[other].endTouchDuck[entity] || startTouchUnduck);
|
|
} else {
|
|
g_clients[other].touching[entity] = true;
|
|
}
|
|
|
|
if (!g_clients[other].touching[entity]) {
|
|
// Prevent outputs from being queued
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
Action Hook_PushEndTouch(int entity, int other)
|
|
{
|
|
if (other > sizeof(g_clients) - 1) {
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
g_clients[other].touching[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 (DoesHullEntityIntersect(origin, VEC_HULL_MIN, VEC_HULL_MAX, entity)) {
|
|
// This EndTouch was caused by a mid-air duck
|
|
g_clients[other].endTouchDuck[entity] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
Action Hook_PushTouch(int entity, int other)
|
|
{
|
|
if (other > sizeof(g_clients) - 1) {
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
if (!g_clients[other].touching[entity]) {
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
MoveType moveType = GetEntityMoveType(other);
|
|
|
|
if (moveType == MOVETYPE_VPHYSICS) {
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
if (!g_boostfix_pushfix.BoolValue) {
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
// https://github.com/perilouswithadollarsign/cstrike15_src/blob/29e4c1fda9698d5cebcdaf1a0de4b829fa149bf8/game/server/triggers.cpp#L2545
|
|
|
|
if (moveType == MOVETYPE_NONE || moveType == MOVETYPE_PUSH) {
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
if ((GetEntData(other, g_engine.m_nSolidType, 1) == SOLID_NONE) || (GetEntData(other, g_engine.m_usSolidFlags, 2) & FSOLID_NOT_SOLID)) {
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
if (GetEntDataEnt2(other, g_engine.m_hMoveParent) != -1) {
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
if (!g_engine.PassesTriggerFilters(entity, other)) {
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
float direction[3];
|
|
{
|
|
float rotation[3];
|
|
GetEntDataVector(entity, g_engine.m_angAbsRotation, rotation);
|
|
|
|
float local[3];
|
|
GetEntDataVector(entity, g_engine.m_vecPushDir, local);
|
|
|
|
float sy = Sine(DegToRad(rotation[1]));
|
|
float cy = Cosine(DegToRad(rotation[1]));
|
|
float sp = Sine(DegToRad(rotation[0]));
|
|
float cp = Cosine(DegToRad(rotation[0]));
|
|
float sr = Sine(DegToRad(rotation[2]));
|
|
float cr = Cosine(DegToRad(rotation[2]));
|
|
|
|
direction[0] = local[0] * (cp * cy) + local[1] * (sp * sr * cy - cr * sy) + local[2] * (sp * cr * cy + sr * sy);
|
|
direction[1] = local[0] * (cp * sy) + local[1] * (sp * sr * sy + cr * cy) + local[2] * (sp * cr * sy - sr * cy);
|
|
direction[2] = local[0] * (-sp) + local[1] * (sr * cp) + local[2] * (cr * cp);
|
|
}
|
|
|
|
float speed = GetEntDataFloat(entity, g_engine.m_flSpeed);
|
|
|
|
float push[3];
|
|
push = direction;
|
|
ScaleVector(push, speed);
|
|
|
|
if (GetEntData(entity, g_engine.m_spawnflags) & SF_TRIG_PUSH_ONCE) {
|
|
float velocity[3];
|
|
GetEntDataVector(other, g_engine.m_vecAbsVelocity, velocity);
|
|
AddVectors(velocity, push, velocity);
|
|
|
|
TeleportEntity(other, NULL_VECTOR, NULL_VECTOR, velocity);
|
|
if (direction[2] > 0.0) {
|
|
SetEntDataEnt2(other, g_engine.m_hGroundEntity, -1);
|
|
}
|
|
|
|
RemoveEdict(entity);
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
int flags = GetEntityFlags(other);
|
|
|
|
if (flags & FL_BASEVELOCITY) {
|
|
float base[3];
|
|
GetEntDataVector(other, g_engine.m_vecBaseVelocity, base);
|
|
AddVectors(push, base, push);
|
|
}
|
|
|
|
// https://forums.alliedmods.net/showpost.php?p=2561673&postcount=17
|
|
|
|
float velocity[3];
|
|
GetEntDataVector(other, g_engine.m_vecAbsVelocity, velocity);
|
|
velocity[2] += push[2] * GetTickInterval() * GetEntDataFloat(other, g_engine.m_flLaggedMovementValue);
|
|
push[2] = 0.0;
|
|
|
|
TeleportEntity(other, NULL_VECTOR, NULL_VECTOR, velocity);
|
|
SetEntDataVector(other, g_engine.m_vecBaseVelocity, push);
|
|
SetEntityFlags(other, flags | FL_BASEVELOCITY);
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
bool g_DoesHullEntityIntersect_hit;
|
|
|
|
bool DoesHullEntityIntersect(const float origin[3], const float mins[3], const float maxs[3], int entity, int mask = PARTITION_TRIGGER_EDICTS)
|
|
{
|
|
g_DoesHullEntityIntersect_hit = false;
|
|
TR_EnumerateEntitiesHull(origin, origin, mins, maxs, mask, Trace_DoesHullEntityIntersect, entity);
|
|
return g_DoesHullEntityIntersect_hit;
|
|
}
|
|
|
|
bool Trace_DoesHullEntityIntersect(int entity, any data)
|
|
{
|
|
if (entity == data) {
|
|
TR_ClipCurrentRayToEntity(MASK_ALL, entity);
|
|
g_DoesHullEntityIntersect_hit = TR_DidHit();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|