Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
9ede0c1
Update signature of ZSecuritySystemCameraManager::OnFrameUpdate and Z…
pavledev Feb 5, 2026
66fb7e2
Fix indentation in IKControllerOwner declaration
pavledev Feb 5, 2026
098a354
Add IsCustomFlagEnabled virtual function to IIKControllerOwner and ad…
pavledev Feb 5, 2026
2dc0fbb
Add missing virtual function to IIKControllerOwner
pavledev Feb 5, 2026
1d50cd0
Add offset comment for padding in ZHM5Health
pavledev Feb 5, 2026
706f855
Add and ZHM5WeaponRecoilController and ZHM5BaseController
pavledev Feb 5, 2026
7036c24
Add virtual functions to IFirearm and add new fields to ZHM5ItemWeapon
pavledev Feb 5, 2026
7e0abab
Fix padding in ZCharacterSubcontrollerInventory
pavledev Feb 5, 2026
23cd937
Add new cheat options to Player mod
pavledev Feb 5, 2026
5d1ef5f
Extend ZGameTime with constructors, operators, and conversions
pavledev Feb 5, 2026
f25a7fd
Replace padding with boolean field in ZGameTimeManager
pavledev Feb 5, 2026
ebebd2a
Use default ZGameTime initialization instead of raw zero
pavledev Feb 5, 2026
c8f10ee
Apply cheats only to player weapons
pavledev Feb 7, 2026
078bb7a
Add m_pOwner to ZHM5Item, add missing offset comments and update offs…
pavledev Feb 7, 2026
5c9ae6c
Add eLIMITED_AMMO item to ECustomFlags
pavledev Feb 7, 2026
ab5054c
Make IKControllerOwner::IsCustomFlagEnabled const
pavledev Feb 7, 2026
627699d
Add IsInfiniteAmmoEnabled method to ZHitman5
pavledev Feb 7, 2026
591e7e1
Refactor hook returns to use brace initialization
pavledev Feb 7, 2026
b05a9cd
Merge branch 'master' into feat/player-mod-options
pavledev Feb 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 169 additions & 7 deletions Mods/Player/Src/Player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ void Player::Init() {
&Player::ZSecuritySystemCameraManager_OnFrameUpdate
);
Hooks::ZSecuritySystemCamera_FrameUpdate->AddDetour(this, &Player::ZSecuritySystemCamera_FrameUpdate);

Hooks::ZHM5ItemWeapon_SetBulletsInMagazine->AddDetour(this, &Player::ZHM5ItemWeapon_SetBulletsInMagazine);
Hooks::ZHitmanMorphemePostProcessor_UpdateWeaponRecoil->AddDetour(
this,
&Player::ZHitmanMorphemePostProcessor_UpdateWeaponRecoil
);
Hooks::ZHM5WeaponRecoilController_RecoilWeapon->AddDetour(this, &Player::ZHM5WeaponRecoilController_RecoilWeapon);
Hooks::ZHM5ItemWeapon_FireProjectiles->AddDetour(this, &Player::ZHM5ItemWeapon_FireProjectiles);
Hooks::ZHM5ItemWeapon_IsFiring->AddDetour(this, &Player::ZHM5ItemWeapon_IsFiring);
Hooks::ZActor_YouGotHit->AddDetour(this, &Player::ZActor_YouGotHit);
}

void Player::OnDrawMenu() {
Expand Down Expand Up @@ -59,6 +69,16 @@ void Player::OnDrawUI(const bool p_HasFocus) {
ToggleInfiniteAmmo();
}

ImGui::Checkbox("No Reload", &m_IsNoReloadEnabled);

ImGui::Checkbox("No Recoil", &m_IsNoRecoilEnabled);

ImGui::Checkbox("Super Accuracy", &m_IsSuperAccuracyEnabled);

ImGui::Checkbox("RapidFire", &m_IsRapidFireEnabled);

ImGui::Checkbox("One Hit Kill", &m_IsOneHitKillEnabled);

static char s_OutfitName[2048] { "" };
static uint8_t s_CurrentCharacterSetIndex = 0;
static std::string s_CurrentCharSetCharacterType = "HeroA";
Expand Down Expand Up @@ -606,35 +626,177 @@ DEFINE_PLUGIN_DETOUR(Player, void, OnClearScene, ZEntitySceneContext* th, bool p
m_IsInfiniteAmmoEnabled = false;
m_GlobalOutfitKit = {};

return HookResult<void>(HookAction::Continue());
return { HookAction::Continue() };
}

DEFINE_PLUGIN_DETOUR(
Player,
void,
ZSecuritySystemCameraManager_OnFrameUpdate,
ZSecuritySystemCameraManager* th,
const SGameUpdateEvent* const updateEvent
const SGameUpdateEvent& updateEvent
) {
if (m_IsInvisible) {
return HookResult<void>(HookAction::Return());
return { HookAction::Return() };
}

return HookResult<void>(HookAction::Continue());
return { HookAction::Continue() };
}

DEFINE_PLUGIN_DETOUR(
Player,
void,
ZSecuritySystemCamera_FrameUpdate,
ZSecuritySystemCamera* th,
const SGameUpdateEvent* const updateEvent
const SGameUpdateEvent& updateEvent
) {
if (m_IsInvisible) {
return HookResult<void>(HookAction::Return());
return { HookAction::Return() };
}

return { HookAction::Continue() };
}

DEFINE_PLUGIN_DETOUR(Player, void, ZHM5ItemWeapon_SetBulletsInMagazine, IFirearm* th, int32_t nBullets) {
if (!m_IsNoReloadEnabled) {
return { HookAction::Continue() };
}

const auto s_LocalHitman = SDK()->GetLocalPlayer();

if (!s_LocalHitman) {
return { HookAction::Continue() };
}

ZHM5ItemWeapon* s_HM5ItemWeapon = static_cast<ZHM5ItemWeapon*>(th);

if (s_HM5ItemWeapon->m_pOwner != s_LocalHitman.m_entityRef) {
return { HookAction::Continue() };
}

if (s_HM5ItemWeapon->m_nBulletsFired == s_HM5ItemWeapon->m_nBulletsToFire) {
s_HM5ItemWeapon->m_nBulletsFired = 0;
}

if (nBullets != 0) {
return { HookAction::Continue() };
}

if (!s_LocalHitman.m_pInterfaceRef->IsInfiniteAmmoEnabled()) {
auto s_Character = s_LocalHitman.m_pInterfaceRef->m_pCharacter.m_pInterfaceRef;
auto s_Controllers = &s_Character->m_rSubcontrollerContainer.m_pInterfaceRef->m_aReferencedControllers;
auto s_Inventory = static_cast<ZCharacterSubcontrollerInventory*>((*s_Controllers)[6].m_pInterfaceRef);

const eAmmoType s_AmmoType = th->GetAmmoType();

uint32 s_AmmoInPocket = Functions::ZCharacterSubcontrollerInventory_GetAmmoInPocketForType->Call(
s_Inventory,
s_AmmoType
);

if (s_AmmoInPocket > 0) {
s_AmmoInPocket -= s_HM5ItemWeapon->GetMagazineCapacity();

s_Inventory->m_nAmmoInPocket[static_cast<size_t>(s_AmmoType)] = s_AmmoInPocket;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we guaranteed to have an element in at this index?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes


nBullets = s_HM5ItemWeapon->GetMagazineCapacity();
}
}
else {
nBullets = s_HM5ItemWeapon->GetMagazineCapacity();
}

p_Hook->CallOriginal(th, nBullets);

return { HookAction::Return() };
}

DEFINE_PLUGIN_DETOUR(
Player,
void,
ZHitmanMorphemePostProcessor_UpdateWeaponRecoil,
ZHitmanMorphemePostProcessor* th,
float fDeltaTime,
const THashMap<int32_t, int32_t, TDefaultHashMapPolicy<int32_t>>& charboneMap,
TArrayRef<int32_t> hierarchy
) {
if (m_IsNoRecoilEnabled) {
return { HookAction::Return() };
}

return { HookAction::Continue() };
}

DEFINE_PLUGIN_DETOUR(
Player,
void,
ZHM5WeaponRecoilController_RecoilWeapon,
ZHM5WeaponRecoilController* th,
const TEntityRef<ZHM5ItemWeapon>& rWeapon
) {
if (!m_IsNoRecoilEnabled) {
return { HookAction::Continue() };
}

p_Hook->CallOriginal(th, rWeapon);

th->m_vRecoil = SVector2(0.f, 0.f);

return { HookAction::Return() };
}

DEFINE_PLUGIN_DETOUR(Player, bool, ZHM5ItemWeapon_FireProjectiles, ZHM5ItemWeapon* th, bool bMayStartSound) {
if (!m_IsSuperAccuracyEnabled) {
return { HookAction::Continue() };
}

const auto s_LocalHitman = SDK()->GetLocalPlayer();

if (!s_LocalHitman) {
return { HookAction::Continue() };
}

if (th->m_pOwner != s_LocalHitman.m_entityRef) {
return { HookAction::Continue() };
}

bool s_Result = p_Hook->CallOriginal(th, bMayStartSound);

th->m_fPrecisionFactor = 0.f;

return { HookAction::Return(), s_Result };
}

DEFINE_PLUGIN_DETOUR(Player, bool, ZHM5ItemWeapon_IsFiring, IFirearm* th) {
if (!m_IsRapidFireEnabled) {
return { HookAction::Continue() };
}

const auto s_LocalHitman = SDK()->GetLocalPlayer();

if (!s_LocalHitman) {
return { HookAction::Continue() };
}

ZHM5ItemWeapon* s_HM5ItemWeapon = static_cast<ZHM5ItemWeapon*>(th);

if (s_HM5ItemWeapon->m_pOwner != s_LocalHitman.m_entityRef) {
return { HookAction::Continue() };
}

s_HM5ItemWeapon->m_tLastShootTime = ZGameTime {};

return { HookAction::Continue() };
}

DEFINE_PLUGIN_DETOUR(Player, bool, ZActor_YouGotHit, IBaseCharacter* th, const SHitInfo& hitInfo) {
if (m_IsOneHitKillEnabled) {
ZActor* s_Actor = static_cast<ZActor*>(th);

s_Actor->m_fCurrentHitPoints = 0.f;
}

return HookResult<void>(HookAction::Continue());
return { HookAction::Continue() };
}

DEFINE_ZHM_PLUGIN(Player);
47 changes: 45 additions & 2 deletions Mods/Player/Src/Player.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,56 @@ class Player : public IPluginInterface {

DECLARE_PLUGIN_DETOUR(Player, void, OnClearScene, ZEntitySceneContext* th, bool p_FullyUnloadScene);

DECLARE_PLUGIN_DETOUR(Player, void, ZSecuritySystemCameraManager_OnFrameUpdate, ZSecuritySystemCameraManager* th, const SGameUpdateEvent* const updateEvent);
DECLARE_PLUGIN_DETOUR(Player, void, ZSecuritySystemCamera_FrameUpdate, ZSecuritySystemCamera* th, const SGameUpdateEvent* const updateEvent);
DECLARE_PLUGIN_DETOUR(
Player,
void,
ZSecuritySystemCameraManager_OnFrameUpdate,
ZSecuritySystemCameraManager* th,
const SGameUpdateEvent& updateEvent
);
DECLARE_PLUGIN_DETOUR(
Player,
void,
ZSecuritySystemCamera_FrameUpdate,
ZSecuritySystemCamera* th,
const SGameUpdateEvent& updateEvent
);

DECLARE_PLUGIN_DETOUR(Player, void, ZHM5ItemWeapon_SetBulletsInMagazine, IFirearm* th, int32_t nBullets);

DECLARE_PLUGIN_DETOUR(
Player,
void,
ZHitmanMorphemePostProcessor_UpdateWeaponRecoil,
ZHitmanMorphemePostProcessor* th,
float fDeltaTime,
const THashMap<int32_t, int32_t, TDefaultHashMapPolicy<int32_t>>& charboneMap,
TArrayRef<int32_t> hierarchy
);

DECLARE_PLUGIN_DETOUR(
Player,
void,
ZHM5WeaponRecoilController_RecoilWeapon,
ZHM5WeaponRecoilController* th,
const TEntityRef<ZHM5ItemWeapon>& rWeapon
);

DECLARE_PLUGIN_DETOUR(Player, bool, ZHM5ItemWeapon_FireProjectiles, ZHM5ItemWeapon* th, bool bMayStartSound);

DECLARE_PLUGIN_DETOUR(Player, bool, ZHM5ItemWeapon_IsFiring, IFirearm* th);

DECLARE_PLUGIN_DETOUR(Player, bool, ZActor_YouGotHit, IBaseCharacter* th, const SHitInfo& hitInfo);

bool m_PlayerMenuActive = false;
bool m_IsInvincible = false;
bool m_IsInvisible = false;
bool m_IsInfiniteAmmoEnabled = false;
bool m_IsNoReloadEnabled = false;
bool m_IsNoRecoilEnabled = false;
bool m_IsSuperAccuracyEnabled = false;
bool m_IsRapidFireEnabled = false;
bool m_IsOneHitKillEnabled = false;

const std::vector<std::string> m_CharSetCharacterTypes = { "Actor", "Nude", "HeroA" };

Expand Down
7 changes: 7 additions & 0 deletions ZHMModSDK/Include/Functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class IItemBase;
class ZStashPointEntity;
class ZTimeOfDayManager;
class ZHM5Health;
class ZHM5WeaponControl;

namespace bfx {
class AreaHandle;
Expand Down Expand Up @@ -247,4 +248,10 @@ class ZHMSDK_API Functions {

static EngineFunction<float32(const ZHM5Health* th)>* ZHM5Health_GetHP;
static EngineFunction<float32(const ZHM5Health* th)>* ZHM5Health_GetMaxHitpoints;

static EngineFunction<uint32_t(
ZCharacterSubcontrollerInventory* th, eAmmoType AmmoType
)>* ZCharacterSubcontrollerInventory_GetAmmoInPocketForType;

static EngineFunction<float(ZHM5WeaponControl* th)>* ZHM5WeaponControl_GetCrosshairScale;
};
Loading