From 19edc51369a311a899536ffbe5f2495c6427dce3 Mon Sep 17 00:00:00 2001
From: rei-2 <174158077+rei-2@users.noreply.github.com>
Date: Tue, 23 Jun 2026 14:55:43 -0400
Subject: [PATCH] grappling hook projectile aim support some reorganization
---
Amalgam/Amalgam.vcxproj | 1 +
Amalgam/Amalgam.vcxproj.filters | 1 +
Amalgam/cpp.hint | 2 +
.../Aimbot/AimbotGlobal/AimbotGlobal.cpp | 4 +-
.../AimbotProjectile/AimbotProjectile.cpp | 17 +-
.../MovementSimulation/MovementSimulation.cpp | 11 +-
.../MovementSimulation/MovementSimulation.h | 1 +
Amalgam/src/Hooks/CHLClient_CreateMove.cpp | 104 +---------
.../Hooks/CTFGrapplingHook_ItemPostFrame.cpp | 25 +++
Amalgam/src/SDK/Definitions/Main/KeyValues.h | 6 +-
Amalgam/src/SDK/SDK.cpp | 180 ++++++++++++++----
Amalgam/src/SDK/SDK.h | 14 +-
12 files changed, 213 insertions(+), 153 deletions(-)
create mode 100644 Amalgam/src/Hooks/CTFGrapplingHook_ItemPostFrame.cpp
diff --git a/Amalgam/Amalgam.vcxproj b/Amalgam/Amalgam.vcxproj
index 6d9e9405..b395297e 100644
--- a/Amalgam/Amalgam.vcxproj
+++ b/Amalgam/Amalgam.vcxproj
@@ -962,6 +962,7 @@
+
diff --git a/Amalgam/Amalgam.vcxproj.filters b/Amalgam/Amalgam.vcxproj.filters
index b24f0678..f18e2b6d 100644
--- a/Amalgam/Amalgam.vcxproj.filters
+++ b/Amalgam/Amalgam.vcxproj.filters
@@ -208,6 +208,7 @@
+
diff --git a/Amalgam/cpp.hint b/Amalgam/cpp.hint
index f39f9431..0c705a77 100644
--- a/Amalgam/cpp.hint
+++ b/Amalgam/cpp.hint
@@ -1,3 +1,5 @@
+#define no_inline __declspec(noinline)
+
#define VA_LIST(...) __VA_ARGS__
#define STRINGIFY(x) STRINGIFY2(x)
diff --git a/Amalgam/src/Features/Aimbot/AimbotGlobal/AimbotGlobal.cpp b/Amalgam/src/Features/Aimbot/AimbotGlobal/AimbotGlobal.cpp
index c8eea794..22f7ebc9 100644
--- a/Amalgam/src/Features/Aimbot/AimbotGlobal/AimbotGlobal.cpp
+++ b/Amalgam/src/Features/Aimbot/AimbotGlobal/AimbotGlobal.cpp
@@ -367,7 +367,9 @@ bool CAimbotGlobal::ShouldHoldAttack(CTFWeaponBase* pWeapon)
break;
[[fallthrough]];
case Vars::Aimbot::General::AimHoldsFireEnum::Always:
- if (!F::Aimbot.m_bRunningSecondary && !G::CanPrimaryAttack && G::LastUserCmd->buttons & IN_ATTACK && Vars::Aimbot::General::AimType.Value && !pWeapon->IsInReload())
+ if (!(G::LastUserCmd->buttons & IN_ATTACK) || !Vars::Aimbot::General::AimType.Value || F::Aimbot.m_bRunningSecondary)
+ break;
+ if (!G::CanPrimaryAttack && !pWeapon->IsInReload() || pWeapon->GetWeaponID() == TF_WEAPON_GRAPPLINGHOOK && pWeapon->As()->m_hProjectile())
return true;
}
return false;
diff --git a/Amalgam/src/Features/Aimbot/AimbotProjectile/AimbotProjectile.cpp b/Amalgam/src/Features/Aimbot/AimbotProjectile/AimbotProjectile.cpp
index 0587e790..0bd84f5b 100644
--- a/Amalgam/src/Features/Aimbot/AimbotProjectile/AimbotProjectile.cpp
+++ b/Amalgam/src/Features/Aimbot/AimbotProjectile/AimbotProjectile.cpp
@@ -2035,12 +2035,27 @@ bool CAimbotProjectile::RunMain(CTFPlayer* pLocal, CTFWeaponBase* pWeapon, CUser
pCmd->buttons &= ~IN_ATTACK;
}
break;
+ case TF_WEAPON_GRAPPLINGHOOK:
+ break;
default:
pCmd->buttons |= IN_ATTACK;
}
}
- F::Aimbot.m_bRan = G::Attacking = SDK::IsAttacking(pLocal, pWeapon, pCmd, true);
+ if (nWeaponID != TF_WEAPON_GRAPPLINGHOOK)
+ F::Aimbot.m_bRan = G::Attacking = SDK::IsAttacking(pLocal, pWeapon, pCmd, true);
+ else
+ {
+ Vec3 vOriginalAngles = pCmd->viewangles; int iOriginalButtons = pCmd->buttons;
+ pCmd->viewangles = tTarget.m_vAngleTo, pCmd->buttons |= IN_ATTACK;
+ F::Aimbot.m_bRan = G::Attacking = SDK::IsAttacking(pLocal, pWeapon, pCmd, true);
+ pCmd->viewangles = vOriginalAngles, pCmd->buttons = iOriginalButtons;
+ if (!G::Attacking)
+ continue;
+
+ if (Vars::Aimbot::General::AutoShoot.Value)
+ pCmd->buttons |= IN_ATTACK;
+ }
DrawVisuals(iResult, tTarget, m_vPlayerPath, m_vProjectilePath, m_vBoxes);
Aim(pCmd, tTarget.m_vAngleTo);
diff --git a/Amalgam/src/Features/Simulation/MovementSimulation/MovementSimulation.cpp b/Amalgam/src/Features/Simulation/MovementSimulation/MovementSimulation.cpp
index 6010da1b..81e9fc77 100644
--- a/Amalgam/src/Features/Simulation/MovementSimulation/MovementSimulation.cpp
+++ b/Amalgam/src/Features/Simulation/MovementSimulation/MovementSimulation.cpp
@@ -236,7 +236,7 @@ bool CMovementSimulation::Initialize(CBaseEntity* pEntity, MoveStorage& tMoveSto
// setup move data
SetupMoveData(tMoveStorage);
- if (I::GameMovement->player = pPlayer, I::GameMovement->mv = &tMoveStorage.m_MoveData; I::GameMovement->CheckStuck())
+ if (CheckStuck(tMoveStorage))
{
tMoveStorage.m_bFailed = true;
return true;
@@ -319,6 +319,15 @@ void CMovementSimulation::SetupMoveData(MoveStorage& tMoveStorage)
tMoveStorage.m_bDirectMove = tMoveStorage.m_pPlayer->IsOnGround() || tMoveStorage.m_pPlayer->IsSwimming();
}
+bool CMovementSimulation::CheckStuck(MoveStorage& tMoveStorage)
+{
+ I::GameMovement->player = I::GameMovement->m_pTFPlayer = tMoveStorage.m_pPlayer, I::GameMovement->mv = &tMoveStorage.m_MoveData;
+ SetBounds(tMoveStorage.m_pPlayer);
+ bool bReturn = I::GameMovement->CheckStuck();
+ RestoreBounds(tMoveStorage.m_pPlayer);
+ return bReturn;
+}
+
static inline float GetFrictionScale(float flVelocityXY, float flTurn, float flVelocityZ, float flMin = 50.f, float flMax = 150.f)
{
if (0.f >= flVelocityZ || flVelocityZ > 250.f)
diff --git a/Amalgam/src/Features/Simulation/MovementSimulation/MovementSimulation.h b/Amalgam/src/Features/Simulation/MovementSimulation/MovementSimulation.h
index 5fbde04b..9049eb90 100644
--- a/Amalgam/src/Features/Simulation/MovementSimulation/MovementSimulation.h
+++ b/Amalgam/src/Features/Simulation/MovementSimulation/MovementSimulation.h
@@ -46,6 +46,7 @@ private:
void Reset(MoveStorage& tMoveStorage);
void SetupMoveData(MoveStorage& tMoveStorage);
+ bool CheckStuck(MoveStorage& tMoveStorage);
void GetAverageYaw(MoveStorage& tMoveStorage, int iSamples);
bool StrafePrediction(MoveStorage& tMoveStorage, bool bHitchance = false);
diff --git a/Amalgam/src/Hooks/CHLClient_CreateMove.cpp b/Amalgam/src/Hooks/CHLClient_CreateMove.cpp
index 815335f5..a6f2eafb 100644
--- a/Amalgam/src/Hooks/CHLClient_CreateMove.cpp
+++ b/Amalgam/src/Hooks/CHLClient_CreateMove.cpp
@@ -15,9 +15,7 @@
#include "../Features/Spectate/Spectate.h"
#include "../Features/AntiCheatCompatibility/AntiCheatCompatibility.h"
-MAKE_SIGNATURE(IHasGenericMeter_GetMeterMultiplier, "client.dll", "F3 0F 10 81 ? ? ? ? C3 CC CC CC CC CC CC CC 48 85 D2", 0x0);
-
-static inline void UpdateInfo(CTFPlayer* pLocal, CTFWeaponBase* pWeapon, CUserCmd* pCmd)
+static no_inline void UpdateInfo(CTFPlayer* pLocal, CTFWeaponBase* pWeapon, CUserCmd* pCmd)
{
G::PSilentAngles = G::SilentAngles = G::Attacking = G::Throwing = false;
G::LastUserCmd = G::CurrentUserCmd ? G::CurrentUserCmd : pCmd;
@@ -27,105 +25,7 @@ static inline void UpdateInfo(CTFPlayer* pLocal, CTFWeaponBase* pWeapon, CUserCm
if (!pWeapon)
return;
- G::CanPrimaryAttack = G::CanSecondaryAttack = G::Reloading = false;
-
- if (pWeapon->GetMaxClip1() != WEAPON_NOCLIP && !pWeapon->m_bReloadsSingly())
- { // dumb fix
- float flOldCurtime = I::GlobalVars->curtime;
- I::GlobalVars->curtime = TICKS_TO_TIME(pLocal->m_nTickBase());
- pWeapon->CheckReload();
- I::GlobalVars->curtime = flOldCurtime;
- }
-
- bool bCanAttack = pLocal->CanAttack();
- {
- static int iStaticItemDefinitionIndex = 0;
- int iOldItemDefinitionIndex = iStaticItemDefinitionIndex;
- int iNewItemDefinitionIndex = iStaticItemDefinitionIndex = pWeapon->m_iItemDefinitionIndex();
-
- if (iNewItemDefinitionIndex != iOldItemDefinitionIndex || !bCanAttack || !pWeapon->m_iClip1())
- F::Ticks.m_iWait = -1;
- }
- if (bCanAttack)
- {
- G::CanPrimaryAttack = pWeapon->CanPrimaryAttack();
- G::CanSecondaryAttack = pWeapon->CanSecondaryAttack();
-
- switch (pWeapon->GetWeaponID())
- {
- case TF_WEAPON_FLAME_BALL:
- if (G::CanPrimaryAttack)
- {
- // do this, otherwise it will be a tick behind
- float flFrametime = TICK_INTERVAL * 100;
- float flMeterMult = S::IHasGenericMeter_GetMeterMultiplier.Call(pWeapon->m_pMeter());
- float flRate = SDK::AttribHookValue(1.f, "item_meter_charge_rate", pWeapon) - 1;
- float flMult = SDK::AttribHookValue(1.f, "mult_item_meter_charge_rate", pWeapon);
- float flTankPressure = pLocal->m_flTankPressure() + flFrametime * flMeterMult / (flRate * flMult);
-
- if (G::CanPrimaryAttack && flTankPressure < 100.f)
- G::CanPrimaryAttack = G::CanSecondaryAttack = false;
- }
- break;
- case TF_WEAPON_MINIGUN:
- {
- int iState = pWeapon->As()->m_iWeaponState();
- if (iState != AC_STATE_FIRING && iState != AC_STATE_SPINNING || !pWeapon->HasPrimaryAmmoForShot())
- G::CanPrimaryAttack = false;
- break;
- }
- case TF_WEAPON_FLAREGUN_REVENGE:
- if (pCmd->buttons & IN_ATTACK2)
- G::CanPrimaryAttack = false;
- break;
- case TF_WEAPON_BAT_WOOD:
- case TF_WEAPON_BAT_GIFTWRAP:
- if (!pWeapon->HasPrimaryAmmoForShot())
- G::CanSecondaryAttack = false;
- break;
- case TF_WEAPON_MEDIGUN:
- case TF_WEAPON_BUILDER:
- case TF_WEAPON_LASER_POINTER:
- break;
- case TF_WEAPON_PARTICLE_CANNON:
- {
- float flChargeBeginTime = pWeapon->As()->m_flChargeBeginTime();
- if (flChargeBeginTime > 0)
- {
- float flTotalChargeTime = TICKS_TO_TIME(pLocal->m_nTickBase()) - flChargeBeginTime;
- if (flTotalChargeTime < TF_PARTICLE_MAX_CHARGE_TIME)
- {
- G::CanPrimaryAttack = G::CanSecondaryAttack = false;
- break;
- }
- }
- [[fallthrough]];
- }
- default:
- if (pWeapon->GetSlot() != SLOT_MELEE)
- {
- bool bAmmo = pWeapon->HasPrimaryAmmoForShot();
- bool bReload = pWeapon->IsInReload();
- if (!bAmmo && pWeapon->m_iItemDefinitionIndex() != Soldier_m_TheBeggarsBazooka)
- G::CanPrimaryAttack = G::CanSecondaryAttack = false;
- if (bReload && bAmmo && !G::CanPrimaryAttack)
- G::Reloading = true;
- }
- }
- if (G::CanPrimaryAttack)
- {
- switch (pWeapon->GetWeaponID())
- {
- case TF_WEAPON_FLAMETHROWER:
- case TF_WEAPON_FLAME_BALL:
- case TF_WEAPON_FLAREGUN:
- case TF_WEAPON_FLAREGUN_REVENGE:
- if (pLocal->IsUnderwater())
- G::CanPrimaryAttack = G::CanSecondaryAttack = false;
- }
- }
- }
-
+ SDK::CanAttack(pLocal, pWeapon, pCmd, G::CanPrimaryAttack, G::CanSecondaryAttack, G::Reloading);
G::Attacking = SDK::IsAttacking(pLocal, pWeapon, pCmd);
G::PrimaryWeaponType = SDK::GetWeaponType(pWeapon, &G::SecondaryWeaponType);
G::CanHeadshot = pWeapon->CanHeadshot() || pWeapon->AmbassadorCanHeadshot(TICKS_TO_TIME(pLocal->m_nTickBase()));
diff --git a/Amalgam/src/Hooks/CTFGrapplingHook_ItemPostFrame.cpp b/Amalgam/src/Hooks/CTFGrapplingHook_ItemPostFrame.cpp
new file mode 100644
index 00000000..8c6abee3
--- /dev/null
+++ b/Amalgam/src/Hooks/CTFGrapplingHook_ItemPostFrame.cpp
@@ -0,0 +1,25 @@
+#include "../SDK/SDK.h"
+
+MAKE_SIGNATURE(CTFGrapplingHook_ItemPostFrame, "client.dll", "40 53 57 48 83 EC ? 48 8B D9 E8 ? ? ? ? 48 8B F8 48 85 C0 0F 84 ? ? ? ? 8B 88", 0x0);
+MAKE_SIGNATURE(CTFGrapplingHook_PrimaryAttack, "client.dll", "48 89 5C 24 ? 48 89 7C 24 ? 55 48 8D 6C 24 ? 48 81 EC ? ? ? ? 48 8B D9 E8 ? ? ? ? 8B 93", 0x0);
+MAKE_SIGNATURE(CTFGrapplingHook_ItemPostFrame_Fired, "client.dll", "40 38 35 ? ? ? ? 75 ? 48 8B 03", 0x0);
+
+static bool s_bOriginalFired = false;
+
+MAKE_HOOK(CTFGrapplingHook_ItemPostFrame, S::CTFGrapplingHook_ItemPostFrame(), void,
+ void* rcx)
+{
+ static bool& bFired = *reinterpret_cast(U::Memory.RelToAbs(S::CTFGrapplingHook_ItemPostFrame_Fired()));
+ s_bOriginalFired = bFired;
+ bFired = false; // this var breaks prediction for this weapon pretty hard otherwise
+ CALL_ORIGINAL(rcx);
+}
+
+MAKE_HOOK(CTFGrapplingHook_PrimaryAttack, S::CTFGrapplingHook_PrimaryAttack(), void,
+ void* rcx)
+{
+ bool bOriginalFirstTimePredicted = I::Prediction->m_bFirstTimePredicted;
+ I::Prediction->m_bFirstTimePredicted = !s_bOriginalFired; // don't spam deny sound
+ CALL_ORIGINAL(rcx);
+ I::Prediction->m_bFirstTimePredicted = bOriginalFirstTimePredicted;
+}
\ No newline at end of file
diff --git a/Amalgam/src/SDK/Definitions/Main/KeyValues.h b/Amalgam/src/SDK/Definitions/Main/KeyValues.h
index 01ee6412..9db05515 100644
--- a/Amalgam/src/SDK/Definitions/Main/KeyValues.h
+++ b/Amalgam/src/SDK/Definitions/Main/KeyValues.h
@@ -2,6 +2,8 @@
#include "../Types.h"
#include
+class CKeyValuesGrowableStringTable;
+
enum types_t
{
TYPE_NONE = 0,
@@ -33,12 +35,12 @@ private:
char m_iDataType;
char m_bHasEscapeSequences;
char m_bEvaluateConditionals;
- char unused[1];
+ char m_bUnknown;
KeyValues* m_pPeer;
KeyValues* m_pSub;
KeyValues* m_pChain;
- void* m_pUnknown;
+ CKeyValuesGrowableStringTable* m_pGrowableStringTable;
public:
class AutoDelete
diff --git a/Amalgam/src/SDK/SDK.cpp b/Amalgam/src/SDK/SDK.cpp
index 2121076f..232c563c 100644
--- a/Amalgam/src/SDK/SDK.cpp
+++ b/Amalgam/src/SDK/SDK.cpp
@@ -3,10 +3,12 @@
#include "../Features/ImGui/Notifications/Notifications.h"
#include "../Features/ImGui/Menu/Menu.h"
#include "../Features/EnginePrediction/EnginePrediction.h"
+#include "../Features/Ticks/Ticks.h"
#pragma warning (disable : 6385)
MAKE_SIGNATURE(CAttributeManager_AttribHookValue_Float, "client.dll", "4C 8B DC 49 89 5B ? 49 89 6B ? 56 57 41 54 41 56 41 57 48 83 EC ? 48 8B 3D ? ? ? ? 4C 8D 35", 0x0);
+MAKE_SIGNATURE(IHasGenericMeter_GetMeterMultiplier, "client.dll", "F3 0F 10 81 ? ? ? ? C3 CC CC CC CC CC CC CC 48 85 D2", 0x0);
static BOOL CALLBACK TeamFortressWindow(HWND hWindow, LPARAM lParam)
{
@@ -418,6 +420,13 @@ bool SDK::PredictOrigin(Vec3& vOut, const Vec3& vOrigin, const Vec3& vVelocity,
return !trace.DidHit();
}
+float SDK::GetGravity()
+{
+ static auto sv_gravity = H::ConVars.FindVar("sv_gravity");
+
+ return sv_gravity->GetFloat();
+}
+
bool SDK::IsLoopback()
{
auto pNetChan = I::EngineClient->GetNetChannelInfo();
@@ -437,6 +446,38 @@ int SDK::GetWinningTeam()
return 0;
}
+const char* SDK::GetClassByIndex(const int nClass, bool bLower)
+{
+ static const char* szClassesUpper[] = {
+ "Unknown", "Scout", "Sniper", "Soldier", "Demoman", "Medic", "Heavy", "Pyro", "Spy", "Engineer"
+ };
+ static const char* szClassesLower[] = {
+ "unknown", "scout", "sniper", "soldier", "demoman", "medic", "heavy", "pyro", "spy", "engineer"
+ };
+
+ if (!bLower)
+ return nClass < 10 && nClass > 0 ? szClassesUpper[nClass] : szClassesUpper[0];
+ else
+ return nClass < 10 && nClass > 0 ? szClassesLower[nClass] : szClassesLower[0];
+}
+
+float SDK::AttribHookValue(float value, const char* name, void* econent, void* buffer, bool isGlobalConstString)
+{
+ return S::CAttributeManager_AttribHookValue_Float.Call(value, name, econent, buffer, isGlobalConstString);
+}
+
+float SDK::MaxSpeed(CTFPlayer* pPlayer, bool bIncludeCrouch, bool bIgnoreSpecialAbility)
+{
+ float flSpeed = pPlayer->CalculateMaxSpeed(bIgnoreSpecialAbility);
+
+ if (pPlayer->InCond(TF_COND_SPEED_BOOST) || pPlayer->InCond(TF_COND_HALLOWEEN_SPEED_BOOST))
+ flSpeed *= 1.35f;
+ if (bIncludeCrouch && pPlayer->IsDucking() && pPlayer->IsOnGround())
+ flSpeed /= 3;
+
+ return flSpeed;
+}
+
EWeaponType SDK::GetWeaponType(CTFWeaponBase* pWeapon, EWeaponType* pSecondaryType)
{
if (pSecondaryType)
@@ -478,7 +519,6 @@ EWeaponType SDK::GetWeaponType(CTFWeaponBase* pWeapon, EWeaponType* pSecondaryTy
case TF_WEAPON_PDA_SPY_BUILD:
case TF_WEAPON_INVIS:
case TF_WEAPON_BUFF_ITEM:
- case TF_WEAPON_GRAPPLINGHOOK:
case TF_WEAPON_ROCKETPACK:
return EWeaponType::UNKNOWN;
case TF_WEAPON_CLEAVER:
@@ -502,25 +542,107 @@ EWeaponType SDK::GetWeaponType(CTFWeaponBase* pWeapon, EWeaponType* pSecondaryTy
case TF_WEAPON_JAR_MILK:
case TF_WEAPON_JAR_GAS:
case TF_WEAPON_LUNCHBOX:
+ case TF_WEAPON_GRAPPLINGHOOK:
return EWeaponType::PROJECTILE;
}
return EWeaponType::HITSCAN;
}
-const char* SDK::GetClassByIndex(const int nClass, bool bLower)
+void SDK::CanAttack(CTFPlayer* pLocal, CTFWeaponBase* pWeapon, const CUserCmd* pCmd, bool& bPrimary, bool& bSecondary, bool& bReloading)
{
- static const char* szClassesUpper[] = {
- "Unknown", "Scout", "Sniper", "Soldier", "Demoman", "Medic", "Heavy", "Pyro", "Spy", "Engineer"
- };
- static const char* szClassesLower[] = {
- "unknown", "scout", "sniper", "soldier", "demoman", "medic", "heavy", "pyro", "spy", "engineer"
- };
+ bPrimary = bSecondary = bReloading = false;
- if (!bLower)
- return nClass < 10 && nClass > 0 ? szClassesUpper[nClass] : szClassesUpper[0];
- else
- return nClass < 10 && nClass > 0 ? szClassesLower[nClass] : szClassesLower[0];
+ if (pWeapon->GetMaxClip1() != WEAPON_NOCLIP && !pWeapon->m_bReloadsSingly())
+ { // dumb fix
+ float flOldCurtime = I::GlobalVars->curtime;
+ I::GlobalVars->curtime = TICKS_TO_TIME(pLocal->m_nTickBase());
+ pWeapon->CheckReload();
+ I::GlobalVars->curtime = flOldCurtime;
+ }
+
+ bool bCanAttack = pLocal->CanAttack();
+ {
+ static int iStaticItemDefinitionIndex = 0;
+ int iOldItemDefinitionIndex = iStaticItemDefinitionIndex;
+ int iNewItemDefinitionIndex = iStaticItemDefinitionIndex = pWeapon->m_iItemDefinitionIndex();
+
+ if (iNewItemDefinitionIndex != iOldItemDefinitionIndex || !bCanAttack || !pWeapon->m_iClip1())
+ F::Ticks.m_iWait = -1;
+ }
+ if (bCanAttack)
+ {
+ bPrimary = pWeapon->CanPrimaryAttack();
+ bSecondary = pWeapon->CanSecondaryAttack();
+
+ switch (pWeapon->GetWeaponID())
+ {
+ case TF_WEAPON_FLAME_BALL:
+ if (bPrimary)
+ {
+ // do this, otherwise it will be a tick behind
+ float flFrametime = TICK_INTERVAL * 100;
+ float flMeterMult = S::IHasGenericMeter_GetMeterMultiplier.Call(pWeapon->m_pMeter());
+ float flRate = SDK::AttribHookValue(1.f, "item_meter_charge_rate", pWeapon) - 1;
+ float flMult = SDK::AttribHookValue(1.f, "mult_item_meter_charge_rate", pWeapon);
+ float flTankPressure = pLocal->m_flTankPressure() + flFrametime * flMeterMult / (flRate * flMult);
+
+ if (bPrimary && flTankPressure < 100.f)
+ bPrimary = bSecondary = false;
+ }
+ break;
+ case TF_WEAPON_MINIGUN:
+ if (int iState = pWeapon->As()->m_iWeaponState(); iState != AC_STATE_FIRING && iState != AC_STATE_SPINNING || !pWeapon->HasPrimaryAmmoForShot())
+ bPrimary = false;
+ break;
+ case TF_WEAPON_FLAREGUN_REVENGE:
+ if (pCmd->buttons & IN_ATTACK2)
+ bPrimary = false;
+ break;
+ case TF_WEAPON_BAT_WOOD:
+ case TF_WEAPON_BAT_GIFTWRAP:
+ if (!pWeapon->HasPrimaryAmmoForShot())
+ bSecondary = false;
+ break;
+ case TF_WEAPON_MEDIGUN:
+ case TF_WEAPON_BUILDER:
+ case TF_WEAPON_LASER_POINTER:
+ break;
+ case TF_WEAPON_PARTICLE_CANNON:
+ if (float flChargeBeginTime = pWeapon->As()->m_flChargeBeginTime(); flChargeBeginTime > 0)
+ {
+ float flTotalChargeTime = TICKS_TO_TIME(pLocal->m_nTickBase()) - flChargeBeginTime;
+ if (flTotalChargeTime < TF_PARTICLE_MAX_CHARGE_TIME)
+ {
+ bPrimary = bSecondary = false;
+ break;
+ }
+ }
+ [[fallthrough]];
+ default:
+ if (pWeapon->GetSlot() != SLOT_MELEE)
+ {
+ bool bAmmo = pWeapon->HasPrimaryAmmoForShot();
+ bool bReload = pWeapon->IsInReload();
+ if (!bAmmo && pWeapon->m_iItemDefinitionIndex() != Soldier_m_TheBeggarsBazooka)
+ bPrimary = bSecondary = false;
+ if (bReload && bAmmo && !bPrimary)
+ bReloading = true;
+ }
+ }
+ if (bPrimary)
+ {
+ switch (pWeapon->GetWeaponID())
+ {
+ case TF_WEAPON_FLAMETHROWER:
+ case TF_WEAPON_FLAME_BALL:
+ case TF_WEAPON_FLAREGUN:
+ case TF_WEAPON_FLAREGUN_REVENGE:
+ if (pLocal->IsUnderwater())
+ bPrimary = bSecondary = false;
+ }
+ }
+ }
}
int SDK::IsAttacking(CTFPlayer* pLocal, CTFWeaponBase* pWeapon, const CUserCmd* pCmd, bool bTickBase)
@@ -621,14 +743,14 @@ int SDK::IsAttacking(CTFPlayer* pLocal, CTFWeaponBase* pWeapon, const CUserCmd*
if (!G::CanPrimaryAttack || !(pCmd->buttons & IN_ATTACK) || pWeapon->As()->m_hProjectile())
return false;
- Vec3 vPos, vAngle; GetProjectileFireSetup(pLocal, pCmd->viewangles, { 23.5f, -8.f, -3.f }, vPos, vAngle, false);
+ static auto tf_grapplinghook_max_distance = H::ConVars.FindVar("tf_grapplinghook_max_distance");
+ const float flGrappleDistance = tf_grapplinghook_max_distance->GetFloat();
+
+ Vec3 vPos, vAngle; GetProjectileFireSetup(pLocal, pCmd->viewangles, { 23.5f, -8.f, -3.f }, vPos, vAngle, 2000.f);
Vec3 vForward; Math::AngleVectors(vAngle, &vForward);
CGameTrace trace = {};
- CTraceFilterHitscan filter = {};
- filter.pSkip = pLocal;
- static auto tf_grapplinghook_max_distance = H::ConVars.FindVar("tf_grapplinghook_max_distance");
- const float flGrappleDistance = tf_grapplinghook_max_distance->GetFloat();
+ CTraceFilterWorldAndPropsOnly filter = {};
Trace(vPos, vPos + vForward * flGrappleDistance, MASK_SOLID, &filter, &trace);
return trace.DidHit() && !(trace.surface.flags & SURF_SKY);
}
@@ -694,30 +816,6 @@ int SDK::IsAttacking(CTFPlayer* pLocal, CTFWeaponBase* pWeapon, const CUserCmd*
return G::CanPrimaryAttack && pCmd->buttons & IN_ATTACK ? 1 : G::Reloading && pCmd->buttons & IN_ATTACK ? 2 : 0;
}
-float SDK::GetGravity()
-{
- static auto sv_gravity = H::ConVars.FindVar("sv_gravity");
-
- return sv_gravity->GetFloat();
-}
-
-float SDK::MaxSpeed(CTFPlayer* pPlayer, bool bIncludeCrouch, bool bIgnoreSpecialAbility)
-{
- float flSpeed = pPlayer->CalculateMaxSpeed(bIgnoreSpecialAbility);
-
- if (pPlayer->InCond(TF_COND_SPEED_BOOST) || pPlayer->InCond(TF_COND_HALLOWEEN_SPEED_BOOST))
- flSpeed *= 1.35f;
- if (bIncludeCrouch && pPlayer->IsDucking() && pPlayer->IsOnGround())
- flSpeed /= 3;
-
- return flSpeed;
-}
-
-float SDK::AttribHookValue(float value, const char* name, void* econent, void* buffer, bool isGlobalConstString)
-{
- return S::CAttributeManager_AttribHookValue_Float.Call(value, name, econent, buffer, isGlobalConstString);
-}
-
void SDK::FixMovement(CUserCmd* pCmd, const Vec3& vCurAngle, const Vec3& vTargetAngle)
{
bool bCurOOB = fabsf(Math::NormalizeAngle(vCurAngle.x)) > 90.f;
diff --git a/Amalgam/src/SDK/SDK.h b/Amalgam/src/SDK/SDK.h
index 4d7e045e..7bfa0c88 100644
--- a/Amalgam/src/SDK/SDK.h
+++ b/Amalgam/src/SDK/SDK.h
@@ -23,6 +23,8 @@
#include
#include
+#define no_inline __declspec(noinline)
+
#define VK_0 0x30
#define VK_1 0x31
#define VK_2 0x32
@@ -122,16 +124,18 @@ namespace SDK
Vec3 PredictOrigin(const Vec3& vOrigin, const Vec3& vVelocity, float flLatency, bool bTrace = true, const Vec3& vMins = {}, const Vec3& vMaxs = {}, unsigned int nMask = MASK_SOLID, float flNormal = 0.f);
bool PredictOrigin(Vec3& vOut, const Vec3& vOrigin, const Vec3& vVelocity, float flLatency, bool bTrace = true, const Vec3& vMins = {}, const Vec3& vMaxs = {}, unsigned int nMask = MASK_SOLID, float flNormal = 0.f);
+ float GetGravity();
bool IsLoopback();
int GetRoundState();
int GetWinningTeam();
- EWeaponType GetWeaponType(CTFWeaponBase* pWeapon, EWeaponType* pSecondaryType = nullptr);
- const char* GetClassByIndex(const int nClass, bool bLower = true);
- int IsAttacking(CTFPlayer* pLocal, CTFWeaponBase* pWeapon, const CUserCmd* pCmd, bool bTickBase = false);
- float GetGravity();
- float MaxSpeed(CTFPlayer* pPlayer, bool bIncludeCrouch = false, bool bIgnoreSpecialAbility = false);
+ const char* GetClassByIndex(const int nClass, bool bLower = true);
float AttribHookValue(float value, const char* name, void* econent, void* buffer = nullptr, bool isGlobalConstString = true);
+ float MaxSpeed(CTFPlayer* pPlayer, bool bIncludeCrouch = false, bool bIgnoreSpecialAbility = false);
+
+ EWeaponType GetWeaponType(CTFWeaponBase* pWeapon, EWeaponType* pSecondaryType = nullptr);
+ void CanAttack(CTFPlayer* pLocal, CTFWeaponBase* pWeapon, const CUserCmd* pCmd, bool& bPrimary, bool& bSecondary, bool& bReloading);
+ int IsAttacking(CTFPlayer* pLocal, CTFWeaponBase* pWeapon, const CUserCmd* pCmd, bool bTickBase = false);
void FixMovement(CUserCmd* pCmd, const Vec3& vCurAngle, const Vec3& vTargetAngle);
void FixMovement(CUserCmd* pCmd, const Vec3& vTargetAngle);