diff --git a/src/game/game.cpp b/src/game/game.cpp index 1e30101..34bf66d 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -13,6 +13,8 @@ namespace game DB_LoadXAssets_t DB_LoadXAssets; + Dvar_RegisterBool_t Dvar_RegisterBool; + Dvar_SetIntByName_t Dvar_SetIntByName; Dvar_SetFromStringByName_t Dvar_SetFromStringByName; @@ -57,6 +59,8 @@ namespace game SEH_LocalizeTextMessage_t SEH_LocalizeTextMessage; + PM_WeaponUseAmmo_t PM_WeaponUseAmmo; + decltype(longjmp)* _longjmp; CmdArgs* sv_cmd_args; @@ -602,6 +606,8 @@ namespace game native::DB_LoadXAssets = native::DB_LoadXAssets_t(SELECT_VALUE(0x48A8E0, 0x4CD020, 0x44F770)); + native::Dvar_RegisterBool = native::Dvar_RegisterBool_t(SELECT_VALUE(0x4914D0, 0x5BE9F0, 0x0)); + native::Dvar_SetIntByName = native::Dvar_SetIntByName_t(SELECT_VALUE(0x5396B0, 0x5BF560, 0x0)); native::Dvar_SetFromStringByName = native::Dvar_SetFromStringByName_t( @@ -650,6 +656,8 @@ namespace game native::SEH_LocalizeTextMessage = native::SEH_LocalizeTextMessage_t( SELECT_VALUE(0x41EA20, 0x57E240, 0x0)); + native::PM_WeaponUseAmmo = native::PM_WeaponUseAmmo_t(SELECT_VALUE(0x463F80, 0x42E930, 0x0)); + native::_longjmp = reinterpret_cast(SELECT_VALUE(0x73AC20, 0x7363BC, 0x655558)); native::sv_cmd_args = reinterpret_cast(SELECT_VALUE(0x1757218, 0x1CAA998, 0x1B5E7D8)); diff --git a/src/game/game.hpp b/src/game/game.hpp index 6479014..bd0dbd6 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -23,6 +23,10 @@ namespace game typedef void (*DB_LoadXAssets_t)(XZoneInfo* zoneInfo, unsigned int zoneCount, int sync); extern DB_LoadXAssets_t DB_LoadXAssets; + typedef const dvar_t* (*Dvar_RegisterBool_t)(const char* dvarName, bool value, + unsigned __int16 flags, const char* description); + extern Dvar_RegisterBool_t Dvar_RegisterBool; + typedef void (*Dvar_SetIntByName_t)(const char* dvarName, int value); extern Dvar_SetIntByName_t Dvar_SetIntByName; @@ -89,6 +93,9 @@ namespace game typedef char* (*SEH_LocalizeTextMessage_t)(const char* pszInputBuffer, const char* pszMessageType, msgLocErrType_t errType); extern SEH_LocalizeTextMessage_t SEH_LocalizeTextMessage; + typedef void (*PM_WeaponUseAmmo_t)(playerState_s* ps, const Weapon weapon, bool isAlternate, int amount, PlayerHandIndex hand); + extern PM_WeaponUseAmmo_t PM_WeaponUseAmmo; + extern decltype(longjmp)* _longjmp; constexpr auto CMD_MAX_NESTING = 8; diff --git a/src/game/structs.hpp b/src/game/structs.hpp index 831da31..7e71567 100644 --- a/src/game/structs.hpp +++ b/src/game/structs.hpp @@ -533,6 +533,31 @@ namespace game const char* name; }; + enum dvar_flags : std::uint16_t + { + DVAR_ARCHIVE = 0x1, + DVAR_CHEAT = 0x4, + DVAR_CODINFO = 0x8, + DVAR_SCRIPTINFO = 0x10, + DVAR_SERVERINFO = 0x400, + DVAR_WRITEPROTECTED = 0x800, + DVAR_READONLY = 0x2000, + }; // Incomplete + + enum dvar_type : std::int8_t + { + DVAR_TYPE_BOOL = 0x0, + DVAR_TYPE_FLOAT = 0x1, + DVAR_TYPE_FLOAT_2 = 0x2, + DVAR_TYPE_FLOAT_3 = 0x3, + DVAR_TYPE_FLOAT_4 = 0x4, + DVAR_TYPE_INT = 0x5, + DVAR_TYPE_ENUM = 0x6, + DVAR_TYPE_STRING = 0x7, + DVAR_TYPE_COLOR = 0x8, + DVAR_TYPE_FLOAT_3_COLOR = 0x9, + }; + union DvarValue { bool enabled; @@ -610,6 +635,33 @@ namespace game static_assert(sizeof(usercmd_s) == 0x2C); + enum PlayerHandIndex + { + WEAPON_HAND_RIGHT = 0, + WEAPON_HAND_LEFT = 1, + NUM_WEAPON_HANDS = 2, + WEAPON_HAND_DEFAULT = 0, + }; + + struct Weapon_s + { + unsigned int padding : 8; + unsigned int scopeVariation : 3; + unsigned int weaponOthers : 4; + unsigned int weaponUnderBarrels : 2; + unsigned int weaponScopes : 3; + unsigned int weaponIdx : 8; + unsigned int weaponVariation : 4; + }; + + union Weapon + { + Weapon_s _s_0; + unsigned int data; + }; + + static_assert(sizeof(Weapon) == 4); + struct playerState_s { unsigned char __pad0[0x4EC]; diff --git a/src/module/player_movement.cpp b/src/module/player_movement.cpp new file mode 100644 index 0000000..338d264 --- /dev/null +++ b/src/module/player_movement.cpp @@ -0,0 +1,51 @@ +#include +#include "player_movement.hpp" + +#include "utils/hook.hpp" + +const game::native::dvar_t* player_movement::player_sustainAmmo; + +void player_movement::post_load() +{ + if (game::is_mp()) this->patch_mp(); + else if (game::is_sp()) this->patch_sp(); +} + +void player_movement::pm_weapon_use_ammo(game::native::playerState_s* ps, const game::native::Weapon weapon, + bool is_alternate, int amount, game::native::PlayerHandIndex hand) +{ + if (!player_movement::player_sustainAmmo->current.enabled) + { + game::native::PM_WeaponUseAmmo(ps, weapon, is_alternate, amount, hand); + } +} + +const game::native::dvar_t* player_movement::dvar_register_player_sustain_ammo(const char* dvar_name, + bool value, unsigned __int16 /*flags*/, const char* description) +{ + player_movement::player_sustainAmmo = game::native::Dvar_RegisterBool(dvar_name, + value, game::native::DVAR_CODINFO, description); + + return player_movement::player_sustainAmmo; +} + +void player_movement::patch_mp() +{ + utils::hook(0x418D9C, &player_movement::dvar_register_player_sustain_ammo, HOOK_CALL).install()->quick(); + + utils::hook(0x42B5DA, &player_movement::pm_weapon_use_ammo, HOOK_CALL).install()->quick(); + utils::hook(0x42B2BD, &player_movement::pm_weapon_use_ammo, HOOK_CALL).install()->quick(); + utils::hook(0x42AE95, &player_movement::pm_weapon_use_ammo, HOOK_CALL).install()->quick(); +} + +void player_movement::patch_sp() +{ + player_movement::player_sustainAmmo = game::native::Dvar_RegisterBool("player_sustainAmmo", + false, game::native::DVAR_CODINFO, "Firing weapon will not decrease clip ammo"); + + utils::hook(0x648C3A, &player_movement::pm_weapon_use_ammo, HOOK_CALL).install()->quick(); + utils::hook(0x64891D, &player_movement::pm_weapon_use_ammo, HOOK_CALL).install()->quick(); + utils::hook(0x6484E2, &player_movement::pm_weapon_use_ammo, HOOK_CALL).install()->quick(); +} + +REGISTER_MODULE(player_movement); diff --git a/src/module/player_movement.hpp b/src/module/player_movement.hpp new file mode 100644 index 0000000..1066761 --- /dev/null +++ b/src/module/player_movement.hpp @@ -0,0 +1,21 @@ +#pragma once +#include "loader/module_loader.hpp" +#include "game/game.hpp" + +class player_movement final : public module +{ +public: + void post_load() override; + +private: + static const game::native::dvar_t* player_sustainAmmo; + + static void pm_weapon_use_ammo(game::native::playerState_s* ps, const game::native::Weapon weapon, + bool isAlternate, int amount, game::native::PlayerHandIndex hand); + + static const game::native::dvar_t* dvar_register_player_sustain_ammo(const char* dvar_name, + bool value, unsigned __int16 flags, const char* description); + + static void patch_mp(); + static void patch_sp(); +};