From 3e8fb4962277a3b5e95e15249eec148e2f4809a1 Mon Sep 17 00:00:00 2001
From: rei-2 <174158077+rei-2@users.noreply.github.com>
Date: Mon, 25 Aug 2025 13:01:40 -0400
Subject: [PATCH] fixes
---
Amalgam/Amalgam.vcxproj | 1 +
Amalgam/Amalgam.vcxproj.filters | 1 +
Amalgam/src/Features/CritHack/CritHack.cpp | 24 ++++++++++++++++---
Amalgam/src/Features/CritHack/CritHack.h | 5 ++++
Amalgam/src/Features/Misc/Misc.cpp | 2 +-
.../Visuals/SpectatorList/SpectatorList.cpp | 5 ++--
.../CTFWeaponBase_CalcIsAttackCritical.cpp | 1 -
...TFWeaponBase_CanFireRandomCriticalShot.cpp | 20 ++++++++++++++++
.../src/SDK/Definitions/Main/CTFPlayer.cpp | 6 ++---
9 files changed, 55 insertions(+), 10 deletions(-)
create mode 100644 Amalgam/src/Hooks/CTFWeaponBase_CanFireRandomCriticalShot.cpp
diff --git a/Amalgam/Amalgam.vcxproj b/Amalgam/Amalgam.vcxproj
index 734d6f94..5f746dab 100644
--- a/Amalgam/Amalgam.vcxproj
+++ b/Amalgam/Amalgam.vcxproj
@@ -990,6 +990,7 @@
+
diff --git a/Amalgam/Amalgam.vcxproj.filters b/Amalgam/Amalgam.vcxproj.filters
index a19528d6..ef31c983 100644
--- a/Amalgam/Amalgam.vcxproj.filters
+++ b/Amalgam/Amalgam.vcxproj.filters
@@ -195,6 +195,7 @@
+
diff --git a/Amalgam/src/Features/CritHack/CritHack.cpp b/Amalgam/src/Features/CritHack/CritHack.cpp
index a0a88397..5eb46ab1 100644
--- a/Amalgam/src/Features/CritHack/CritHack.cpp
+++ b/Amalgam/src/Features/CritHack/CritHack.cpp
@@ -9,10 +9,12 @@
#define TF_DAMAGE_CRIT_CHANCE_MELEE 0.15f
#define TF_DAMAGE_CRIT_DURATION_RAPID 2.0f
+#define STATS_SEND_FREQUENCY 1.f
+
#define SEED_ATTEMPTS 4096
#define BUCKET_ATTEMPTS 1000
-//#define SERVER_CRIT_DATA
+#define SERVER_CRIT_DATA
int CCritHack::GetCritCommand(CTFWeaponBase* pWeapon, int iCommandNumber, bool bCrit, bool bSafe)
{
@@ -227,6 +229,16 @@ void CCritHack::UpdateInfo(CTFPlayer* pLocal, CTFWeaponBase* pWeapon)
if (auto pResource = H::Entities.GetResource())
{
m_iResourceDamage = pResource->m_iDamage(I::EngineClient->GetLocalPlayer());
+ /* // more of a proof of concept for resyncing crit damage
+ if (m_flLastDamageTime < I::GlobalVars->curtime + STATS_SEND_FREQUENCY * 2)
+ { // attempt to resync damages
+ m_iRangedDamage = m_iResourceDamage - m_iMeleeDamage;
+
+ float flObservedCritChance = pWeapon->m_flObservedCritChance();
+ m_iCritDamage = (TF_DAMAGE_CRIT_MULTIPLIER * flObservedCritChance * m_iResourceDamage) / (1 + 2 * flObservedCritChance);
+ SDK::Output("Info", std::format("{}, {}", m_iRangedDamage, m_iCritDamage).c_str());
+ }
+ */
m_iDesyncDamage = m_iRangedDamage + m_iMeleeDamage - m_iResourceDamage;
}
}
@@ -278,6 +290,8 @@ void CCritHack::Reset()
m_flDamageTilFlip = 0;
m_mHealthHistory.clear();
+
+ //m_flLastDamageTime = 0.f;
}
@@ -295,11 +309,13 @@ int CCritHack::GetCritRequest(CUserCmd* pCmd, CTFWeaponBase* pWeapon)
void CCritHack::Run(CTFPlayer* pLocal, CTFWeaponBase* pWeapon, CUserCmd* pCmd)
{
- if (!pWeapon || !pLocal->IsAlive() || !I::EngineClient->IsInGame()
- || pLocal->IsCritBoosted() || pWeapon->m_flCritTime() > I::GlobalVars->curtime || !WeaponCanCrit(pWeapon))
+ if (!pWeapon || !pLocal->IsAlive() || pLocal->IsAGhost() || !I::EngineClient->IsInGame())
return;
UpdateInfo(pLocal, pWeapon);
+ if (pLocal->IsCritBoosted() || pWeapon->m_flCritTime() > I::GlobalVars->curtime || !WeaponCanCrit(pWeapon))
+ return;
+
if (pWeapon->GetWeaponID() == TF_WEAPON_MINIGUN && pCmd->buttons & IN_ATTACK)
pCmd->buttons &= ~IN_ATTACK2;
@@ -445,6 +461,8 @@ void CCritHack::Event(IGameEvent* pEvent, uint32_t uHash, CTFPlayer* pLocal)
}
}
+ //m_flLastDamageTime = I::GlobalVars->curtime;
+
CTFWeaponBase* pWeapon = nullptr;
for (int i = 0; i < MAX_WEAPONS; i++)
{
diff --git a/Amalgam/src/Features/CritHack/CritHack.h b/Amalgam/src/Features/CritHack/CritHack.h
index 9df02984..70403799 100644
--- a/Amalgam/src/Features/CritHack/CritHack.h
+++ b/Amalgam/src/Features/CritHack/CritHack.h
@@ -52,6 +52,8 @@ private:
float m_flCritChance = 0.f;
float m_flMultCritChance = 1.f;
+ //float m_flLastDamageTime = 0.f;
+
public:
void Run(CTFPlayer* pLocal, CTFWeaponBase* pWeapon, CUserCmd* pCmd);
void Event(IGameEvent* pEvent, uint32_t uHash, CTFPlayer* pLocal);
@@ -60,6 +62,9 @@ public:
bool WeaponCanCrit(CTFWeaponBase* pWeapon, bool bWeaponOnly = false);
int PredictCmdNum(CTFPlayer* pLocal, CTFWeaponBase* pWeapon, CUserCmd* pCmd);
+
+ float GetCritDamage() { return m_iCritDamage; }
+ float GetRangedDamage() { return m_iRangedDamage; }
};
ADD_FEATURE(CCritHack, CritHack);
\ No newline at end of file
diff --git a/Amalgam/src/Features/Misc/Misc.cpp b/Amalgam/src/Features/Misc/Misc.cpp
index c5ef2fa4..b66dff9f 100644
--- a/Amalgam/src/Features/Misc/Misc.cpp
+++ b/Amalgam/src/Features/Misc/Misc.cpp
@@ -211,7 +211,7 @@ void CMisc::AntiAFK(CTFPlayer* pLocal, CUserCmd* pCmd)
void CMisc::InstantRespawnMVM(CTFPlayer* pLocal)
{
- if (!Vars::Misc::MannVsMachine::InstantRespawn.Value || !pLocal->IsAlive())
+ if (!Vars::Misc::MannVsMachine::InstantRespawn.Value || pLocal->IsAlive())
return;
KeyValues* kv = new KeyValues("MVM_Revive_Response");
diff --git a/Amalgam/src/Features/Visuals/SpectatorList/SpectatorList.cpp b/Amalgam/src/Features/Visuals/SpectatorList/SpectatorList.cpp
index d391d40c..4b369591 100644
--- a/Amalgam/src/Features/Visuals/SpectatorList/SpectatorList.cpp
+++ b/Amalgam/src/Features/Visuals/SpectatorList/SpectatorList.cpp
@@ -17,13 +17,14 @@ bool CSpectatorList::GetSpectators(CTFPlayer* pTarget)
auto pPlayer = I::ClientEntityList->GetClientEntity(n)->As();
bool bLocal = n == I::EngineClient->GetLocalPlayer();
- if (pResource->m_bValid(n) && pResource->m_iTeam(I::EngineClient->GetLocalPlayer()) != TEAM_SPECTATOR && pResource->m_iTeam(n) == TEAM_SPECTATOR)
+ if (pResource->m_bValid(n) && !pResource->IsFakePlayer(n)
+ && pResource->m_iTeam(I::EngineClient->GetLocalPlayer()) != TEAM_SPECTATOR && pResource->m_iTeam(n) == TEAM_SPECTATOR)
{
m_vSpectators.emplace_back(F::PlayerUtils.GetPlayerName(n, pResource->GetName(n)), "possible", -1.f, false, n);
continue;
}
- if (pTarget->entindex() == n
+ if (pTarget->entindex() == n || pResource->IsFakePlayer(n)
|| !pPlayer || !pPlayer->IsPlayer() || pPlayer->IsAlive()
|| pTarget->IsDormant() != pPlayer->IsDormant()
|| pResource->m_iTeam(iTarget) != pResource->m_iTeam(n))
diff --git a/Amalgam/src/Hooks/CTFWeaponBase_CalcIsAttackCritical.cpp b/Amalgam/src/Hooks/CTFWeaponBase_CalcIsAttackCritical.cpp
index a84a6237..f44b0be2 100644
--- a/Amalgam/src/Hooks/CTFWeaponBase_CalcIsAttackCritical.cpp
+++ b/Amalgam/src/Hooks/CTFWeaponBase_CalcIsAttackCritical.cpp
@@ -2,7 +2,6 @@
#include "../Features/CritHack/CritHack.h"
-
MAKE_HOOK(CTFWeaponBase_CalcIsAttackCritical, S::CTFWeaponBase_CalcIsAttackCritical(), void,
void* rcx)
{
diff --git a/Amalgam/src/Hooks/CTFWeaponBase_CanFireRandomCriticalShot.cpp b/Amalgam/src/Hooks/CTFWeaponBase_CanFireRandomCriticalShot.cpp
new file mode 100644
index 00000000..cd2b1a8b
--- /dev/null
+++ b/Amalgam/src/Hooks/CTFWeaponBase_CanFireRandomCriticalShot.cpp
@@ -0,0 +1,20 @@
+#include "../SDK/SDK.h"
+
+#include "../Features/CritHack/CritHack.h"
+
+MAKE_SIGNATURE(CTFWeaponBase_CanFireRandomCriticalShot, "client.dll", "F3 0F 58 0D ? ? ? ? 0F 2F 89", 0x0);
+
+MAKE_HOOK(CTFWeaponBase_CanFireRandomCriticalShot, S::CTFWeaponBase_CanFireRandomCriticalShot(), bool,
+ void* rcx, float flCritChance)
+{ // not present on the client so it will always be a crit behind otherwise
+ int nRandomRangedCritDamage = F::CritHack.GetCritDamage();
+ int nTotalDamage = F::CritHack.GetRangedDamage();
+ if (!nTotalDamage)
+ return true;
+
+ auto pWeapon = reinterpret_cast(rcx);
+ float flNormalizedDamage = nRandomRangedCritDamage / TF_DAMAGE_CRIT_MULTIPLIER;
+ pWeapon->m_flObservedCritChance() = flNormalizedDamage / (flNormalizedDamage + (nTotalDamage - nRandomRangedCritDamage));
+
+ return CALL_ORIGINAL(rcx, flCritChance);
+}
\ No newline at end of file
diff --git a/Amalgam/src/SDK/Definitions/Main/CTFPlayer.cpp b/Amalgam/src/SDK/Definitions/Main/CTFPlayer.cpp
index 6cb6cd4e..c0fb3c06 100644
--- a/Amalgam/src/SDK/Definitions/Main/CTFPlayer.cpp
+++ b/Amalgam/src/SDK/Definitions/Main/CTFPlayer.cpp
@@ -45,7 +45,7 @@ bool CTFPlayer::InCond(ETFCond eCond)
switch (eCond / 32)
{
case 0:
- return m_nPlayerCond() & (1 << eCond);
+ return m_nPlayerCond() & (1 << eCond) || _condition_bits() & (1 << eCond);
case 1:
return m_nPlayerCondEx() & (1 << (eCond - 32));
case 2:
@@ -63,7 +63,7 @@ void CTFPlayer::AddCond(ETFCond eCond)
switch (eCond / 32)
{
case 0:
- m_nPlayerCond() |= (1 << eCond); break;
+ m_nPlayerCond() |= (1 << eCond), _condition_bits() |= (1 << eCond); break;
case 1:
m_nPlayerCondEx() |= (1 << (eCond - 32)); break;
case 2:
@@ -80,7 +80,7 @@ void CTFPlayer::RemoveCond(ETFCond eCond)
switch (eCond / 32)
{
case 0:
- m_nPlayerCond() &= ~(1 << eCond); break;
+ m_nPlayerCond() &= ~(1 << eCond), _condition_bits() &= ~(1 << eCond); break;
case 1:
m_nPlayerCondEx() &= ~(1 << (eCond - 32)); break;
case 2: