Skip to content

Commit 88acbaa

Browse files
KxnrlVauff
andauthored
refactor: knockback system (#344)
* refactor knoback system * adjust something * Fix weapon lookup --------- Co-authored-by: Vauff <mctehkitti@gmail.com>
1 parent 5ba22c2 commit 88acbaa

File tree

6 files changed

+79
-66
lines changed

6 files changed

+79
-66
lines changed

src/cs2_sdk/entity/ctakedamageinfo.h

Lines changed: 31 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/**
1+
/**
22
* =============================================================================
33
* CS2Fixes
44
* Copyright (C) 2023-2025 Source2ZE
@@ -77,12 +77,14 @@ struct AttackerInfo_t
7777
int m_iTeamChecked;
7878
int m_nTeam;
7979
};
80+
static_assert(sizeof(AttackerInfo_t) == 20);
8081

8182
// No idea what this is meant to have, but OnTakeDamage_Alive expects this and we only care about pInfo
8283
struct CTakeDamageInfoContainer
8384
{
8485
CTakeDamageInfo* pInfo;
8586
};
87+
class CGameTrace;
8688

8789
class CTakeDamageInfo
8890
{
@@ -100,50 +102,42 @@ class CTakeDamageInfo
100102
addresses::CTakeDamageInfo_Constructor(this, pInflictor, pAttacker, pAbility, &vec3_origin, &vec3_origin, flDamage, bitsDamageType, 0, nullptr);
101103
}
102104

103-
Vector m_vecDamageForce;
104-
Vector m_vecDamagePosition;
105-
Vector m_vecReportedPosition;
106-
Vector m_vecDamageDirection;
107-
CHandle<CBaseEntity> m_hInflictor;
108-
CHandle<CBaseEntity> m_hAttacker;
109-
CHandle<CBaseEntity> m_hAbility;
110-
float m_flDamage;
111-
float m_flTotalledDamage;
112-
float m_flTotalledDamageAbsorbed;
113-
DamageTypes_t m_bitsDamageType;
114-
int32_t m_iDamageCustom;
115-
uint8_t m_iAmmoType;
105+
Vector m_vecDamageForce; // 0x8 | 8
106+
Vector m_vecDamagePosition; // 0x14 | 20
107+
Vector m_vecReportedPosition; // 0x20 | 32
108+
Vector m_vecDamageDirection; // 0x2c | 44
109+
CHandle<CBaseEntity> m_hInflictor; // 0x38 | 56
110+
CHandle<CBaseEntity> m_hAttacker; // 0x3c | 60
111+
CHandle<CBaseEntity> m_hAbility; // 0x40 | 64
112+
float m_flDamage; // 0x44 | 68
113+
float m_flTotalledDamage; // 0x48 | 72
114+
float m_flTotalledDamageAbsorbed; // 0x4c | 76
115+
DamageTypes_t m_bitsDamageType; // 0x50 | 80
116+
int32_t m_iDamageCustom; // 0x54 | 84
117+
uint8_t m_iAmmoType; // 0x58 | 88
116118

117119
private:
118-
[[maybe_unused]] uint8_t __pad0059[0xf];
120+
[[maybe_unused]] uint8_t _x51[15]; // 0x59
119121

120122
public:
121-
float m_flOriginalDamage;
122-
bool m_bShouldBleed;
123-
bool m_bShouldSpark;
123+
float m_flOriginalDamage; // 0x68 | 104
124+
bool m_bShouldBleed; // 0x6c | 108
125+
bool m_bShouldSpark; // 0x6d | 109
124126

125127
private:
126-
[[maybe_unused]] uint8_t __pad006e[0x2];
128+
[[maybe_unused]] uint8_t _x66[0x2]; // 0x66
127129

128130
public:
129-
float m_flDamageAbsorbed;
131+
float m_flDamageAbsorbed; // 0x70 | 112
132+
CGameTrace* m_pTrace; // 0x78 | 120
133+
TakeDamageFlags_t m_nDamageFlags; // 0x80 | 128
134+
int32_t m_nNumObjectsPenetrated; // 0x88 | 136
135+
float m_flFriendlyFireDamageReductionRatio; // 0x8c | 140
136+
void* m_hScriptInstance; // 0x90 | 144
137+
AttackerInfo_t m_AttackerInfo; // 0x98 | 152
138+
bool m_bInTakeDamageFlow; // 0xac | 172
130139

131140
private:
132-
[[maybe_unused]] uint8_t __pad0074[0x8];
133-
134-
public:
135-
TakeDamageFlags_t m_nDamageFlags;
136-
137-
private:
138-
[[maybe_unused]] uint8_t __pad0084[0x4];
139-
140-
public:
141-
int32_t m_nNumObjectsPenetrated;
142-
float m_flFriendlyFireDamageReductionRatio;
143-
uint64_t m_hScriptInstance;
144-
AttackerInfo_t m_AttackerInfo;
145-
bool m_bInTakeDamageFlow;
146-
147-
private:
148-
[[maybe_unused]] uint8_t __pad00ad[0x4];
141+
[[maybe_unused]] int32_t m_nUnknown2; // 0xa0 | 176
149142
};
143+
static_assert(sizeof(CTakeDamageInfo) == 184);

src/detours.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ CConVar<bool> g_cvarBlockMolotovSelfDmg("cs2f_block_molotov_self_dmg", FCVAR_NON
9090
CConVar<bool> g_cvarBlockAllDamage("cs2f_block_all_dmg", FCVAR_NONE, "Whether to block all damage to players", false);
9191
CConVar<bool> g_cvarFixBlockDamage("cs2f_fix_block_dmg", FCVAR_NONE, "Whether to fix block-damage on players", false);
9292

93-
void FASTCALL Detour_CBaseEntity_TakeDamageOld(CBaseEntity* pThis, CTakeDamageInfo* inputInfo)
93+
int64 FASTCALL Detour_CBaseEntity_TakeDamageOld(CBaseEntity* pThis, CTakeDamageInfo* inputInfo)
9494
{
9595
#ifdef _DEBUG
9696
Message("\n--------------------------------\n"
@@ -111,7 +111,7 @@ void FASTCALL Detour_CBaseEntity_TakeDamageOld(CBaseEntity* pThis, CTakeDamageIn
111111

112112
// Block all player damage if desired
113113
if (g_cvarBlockAllDamage.Get() && pThis->IsPawn())
114-
return;
114+
return 0;
115115

116116
CBaseEntity* pInflictor = inputInfo->m_hInflictor.Get();
117117
const char* pszInflictorClass = pInflictor ? pInflictor->GetClassname() : "";
@@ -137,9 +137,14 @@ void FASTCALL Detour_CBaseEntity_TakeDamageOld(CBaseEntity* pThis, CTakeDamageIn
137137

138138
// Prevent molly on self
139139
if (g_cvarBlockMolotovSelfDmg.Get() && inputInfo->m_hAttacker == pThis && !V_strncmp(pszInflictorClass, "inferno", 7))
140-
return;
140+
return 0;
141+
142+
const auto damage = CBaseEntity_TakeDamageOld(pThis, inputInfo);
143+
144+
if (damage > 0 && g_cvarEnableZR.Get() && pThis->IsPawn())
145+
ZR_OnPlayerTakeDamage(reinterpret_cast<CCSPlayerPawn*>(pThis), inputInfo, static_cast<int32>(damage));
141146

142-
CBaseEntity_TakeDamageOld(pThis, inputInfo);
147+
return damage;
143148
}
144149

145150
CConVar<bool> g_cvarUseOldPush("cs2f_use_old_push", FCVAR_NONE, "Whether to use the old CSGO trigger_push behavior", false);

src/detours.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/**
1+
/**
22
* =============================================================================
33
* CS2Fixes
44
* Copyright (C) 2023-2025 Source2ZE
@@ -63,7 +63,7 @@ void FASTCALL Detour_UTIL_SayTextFilter(IRecipientFilter&, const char*, CCSPlaye
6363
void FASTCALL Detour_UTIL_SayText2Filter(IRecipientFilter&, CCSPlayerController*, uint64, const char*, const char*, const char*, const char*, const char*);
6464
bool FASTCALL Detour_IsHearingClient(void*, int);
6565
void FASTCALL Detour_TriggerPush_Touch(CTriggerPush* pPush, CBaseEntity* pOther);
66-
void FASTCALL Detour_CBaseEntity_TakeDamageOld(CBaseEntity* pThis, CTakeDamageInfo* inputInfo);
66+
int64 FASTCALL Detour_CBaseEntity_TakeDamageOld(CBaseEntity* pThis, CTakeDamageInfo* inputInfo);
6767
bool FASTCALL Detour_CCSPlayer_WeaponServices_CanUse(CCSPlayer_WeaponServices*, CBasePlayerWeapon*);
6868
void FASTCALL Detour_CCSPlayer_WeaponServices_EquipWeapon(CCSPlayer_WeaponServices*, CBasePlayerWeapon*);
6969
bool FASTCALL Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSymbolLarge* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value, int nOutputID);

src/events.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,9 +198,6 @@ CConVar<bool> g_cvarEnableTopDefender("cs2f_topdefender_enable", FCVAR_NONE, "Wh
198198

199199
GAME_EVENT_F(player_hurt)
200200
{
201-
if (g_cvarEnableZR.Get())
202-
ZR_OnPlayerHurt(pEvent);
203-
204201
if (!g_cvarEnableTopDefender.Get())
205202
return;
206203

src/zombiereborn.cpp

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/**
1+
/**
22
* =============================================================================
33
* CS2Fixes
44
* Copyright (C) 2023-2025 Source2ZE
@@ -276,13 +276,13 @@ void ZRClass::Override(ordered_json jsonKeys, std::string szClassname)
276276
}
277277

278278
ZRHumanClass::ZRHumanClass(ordered_json jsonKeys, std::string szClassname) :
279-
ZRClass(jsonKeys, szClassname, CS_TEAM_CT){};
279+
ZRClass(jsonKeys, szClassname, CS_TEAM_CT) {};
280280

281281
ZRZombieClass::ZRZombieClass(ordered_json jsonKeys, std::string szClassname) :
282282
ZRClass(jsonKeys, szClassname, CS_TEAM_T),
283283
iHealthRegenCount(jsonKeys.value("health_regen_count", 0)),
284284
flHealthRegenInterval(jsonKeys.value("health_regen_interval", 0)),
285-
flKnockback(jsonKeys.value("knockback", 1.0)){};
285+
flKnockback(jsonKeys.value("knockback", 1.0)) {};
286286

287287
void ZRZombieClass::Override(ordered_json jsonKeys, std::string szClassname)
288288
{
@@ -1066,7 +1066,10 @@ void ZR_OnPlayerSpawn(CCSPlayerController* pController)
10661066

10671067
void ZR_ApplyKnockback(CCSPlayerPawn* pHuman, CCSPlayerPawn* pVictim, int iDamage, const char* szWeapon, int hitgroup, float classknockback)
10681068
{
1069-
std::shared_ptr<ZRWeapon> pWeapon = g_pZRWeaponConfig->FindWeapon(szWeapon);
1069+
if (V_strlen(szWeapon) <= 7)
1070+
return;
1071+
1072+
std::shared_ptr<ZRWeapon> pWeapon = g_pZRWeaponConfig->FindWeapon(szWeapon + 7);
10701073
std::shared_ptr<ZRHitgroup> pHitgroup = g_pZRHitgroupConfig->FindHitgroupIndex(hitgroup);
10711074
// player shouldn't be able to pick up that weapon in the first place, but just in case
10721075
if (!pWeapon)
@@ -1602,21 +1605,35 @@ void ZR_Hook_ClientCommand_JoinTeam(CPlayerSlot slot, const CCommand& args)
16021605
SpawnPlayer(pController);
16031606
}
16041607

1605-
void ZR_OnPlayerHurt(IGameEvent* pEvent)
1608+
void ZR_OnPlayerTakeDamage(CCSPlayerPawn* pVictimPawn, const CTakeDamageInfo* pInfo, const int32 damage)
16061609
{
1607-
CCSPlayerController* pAttackerController = (CCSPlayerController*)pEvent->GetPlayerController("attacker");
1608-
CCSPlayerController* pVictimController = (CCSPlayerController*)pEvent->GetPlayerController("userid");
1609-
const char* szWeapon = pEvent->GetString("weapon");
1610-
int iDmgHealth = pEvent->GetInt("dmg_health");
1611-
int iHitGroup = pEvent->GetInt("hitgroup");
1610+
// bullet only
1611+
if ((pInfo->m_bitsDamageType & DMG_BULLET) == 0 || !pInfo->m_pTrace || !pInfo->m_pTrace->m_pHitbox)
1612+
return;
1613+
1614+
const auto pVictimController = reinterpret_cast<CCSPlayerController*>(pVictimPawn->GetController());
1615+
if (!pVictimController || !pVictimController->IsConnected())
1616+
return;
16121617

1613-
// grenade and molotov knockbacks are handled by TakeDamage detours
1614-
if (!pAttackerController || !pVictimController || !V_strncmp(szWeapon, "inferno", 7) || !V_strncmp(szWeapon, "hegrenade", 9))
1618+
if (!pInfo->m_AttackerInfo.m_bIsPawn)
16151619
return;
16161620

1617-
if (pAttackerController->m_iTeamNum() == CS_TEAM_CT && pVictimController->m_iTeamNum() == CS_TEAM_T)
1621+
const auto pKillerPawn = pInfo->m_AttackerInfo.m_hAttackerPawn.Get();
1622+
if (!pKillerPawn || !pKillerPawn->IsPawn()) // I don't know why this maybe non-pawn entity??
1623+
return;
1624+
1625+
const auto pAbility = pInfo->m_hAbility.Get();
1626+
if (!pAbility)
1627+
return;
1628+
1629+
const char* pszWeapon = pAbility->GetClassname();
1630+
1631+
if (!V_strncasecmp(pszWeapon, "weapon_", 7))
1632+
pszWeapon = reinterpret_cast<CBasePlayerWeapon*>(pAbility)->GetWeaponClassname();
1633+
1634+
if (pKillerPawn->m_iTeamNum() == CS_TEAM_CT && pVictimPawn->m_iTeamNum() == CS_TEAM_T)
16181635
{
1619-
float flClassKnockback = 1.0f;
1636+
auto flClassKnockback = 1.0f;
16201637

16211638
if (pVictimController->GetZEPlayer())
16221639
{
@@ -1626,7 +1643,7 @@ void ZR_OnPlayerHurt(IGameEvent* pEvent)
16261643
flClassKnockback = static_pointer_cast<ZRZombieClass>(activeClass)->flKnockback;
16271644
}
16281645

1629-
ZR_ApplyKnockback((CCSPlayerPawn*)pAttackerController->GetPawn(), (CCSPlayerPawn*)pVictimController->GetPawn(), iDmgHealth, szWeapon, iHitGroup, flClassKnockback);
1646+
ZR_ApplyKnockback(pKillerPawn, pVictimPawn, damage, pszWeapon, pInfo->m_pTrace->m_pHitbox->m_nGroupId, flClassKnockback);
16301647
}
16311648
}
16321649

src/zombiereborn.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/**
1+
/**
22
* =============================================================================
33
* CS2Fixes
44
* Copyright (C) 2023-2025 Source2ZE
@@ -136,7 +136,7 @@ struct ZRClass
136136
struct ZRHumanClass : ZRClass
137137
{
138138
ZRHumanClass(std::shared_ptr<ZRHumanClass> pClass) :
139-
ZRClass(pClass, CS_TEAM_CT){};
139+
ZRClass(pClass, CS_TEAM_CT) {};
140140
ZRHumanClass(ordered_json jsonKeys, std::string szClassname);
141141
};
142142

@@ -149,7 +149,7 @@ struct ZRZombieClass : ZRClass
149149
ZRClass(pClass, CS_TEAM_T),
150150
iHealthRegenCount(pClass->iHealthRegenCount),
151151
flHealthRegenInterval(pClass->flHealthRegenInterval),
152-
flKnockback(pClass->flKnockback){};
152+
flKnockback(pClass->flKnockback) {};
153153
ZRZombieClass(ordered_json jsonKeys, std::string szClassname);
154154
void PrintInfo()
155155
{
@@ -226,7 +226,7 @@ class CZRRegenTimer : public CTimerBase
226226
{
227227
public:
228228
CZRRegenTimer(float flRegenInterval, int iRegenAmount, CHandle<CCSPlayerPawn> hPawnHandle) :
229-
CTimerBase(flRegenInterval, false, false), m_iRegenAmount(iRegenAmount), m_hPawnHandle(hPawnHandle){};
229+
CTimerBase(flRegenInterval, false, false), m_iRegenAmount(iRegenAmount), m_hPawnHandle(hPawnHandle) {};
230230

231231
bool Execute();
232232
static void StartRegen(float flRegenInterval, int iRegenAmount, CCSPlayerController* pController);
@@ -291,7 +291,7 @@ void ZR_OnLevelInit();
291291
void ZR_OnRoundPrestart(IGameEvent* pEvent);
292292
void ZR_OnRoundStart(IGameEvent* pEvent);
293293
void ZR_OnPlayerSpawn(CCSPlayerController* pController);
294-
void ZR_OnPlayerHurt(IGameEvent* pEvent);
294+
void ZR_OnPlayerTakeDamage(CCSPlayerPawn* pVictimPawn, const CTakeDamageInfo* pInfo, const int32 damage);
295295
void ZR_OnPlayerDeath(IGameEvent* pEvent);
296296
void ZR_OnRoundFreezeEnd(IGameEvent* pEvent);
297297
void ZR_OnRoundTimeWarning(IGameEvent* pEvent);

0 commit comments

Comments
 (0)