mirror of
https://github.com/t5mat/crouchboostfix.git
synced 2025-12-06 18:08:38 +00:00
Initial commit
This commit is contained in:
commit
fee9898651
19
LICENSE.md
Normal file
19
LICENSE.md
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2021 mat
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
376
boostfix.sp
Normal file
376
boostfix.sp
Normal file
@ -0,0 +1,376 @@
|
||||
#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;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user