diff --git a/src/game/game.cpp b/src/game/game.cpp index 9ce3ab2..c091956 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -71,6 +71,10 @@ namespace game PM_trace_t PM_trace; + Jump_ClearState_t Jump_ClearState; + + Vec3Normalize_t Vec3Normalize; + Cmd_ExecuteSingleCommand_t Cmd_ExecuteSingleCommand; Com_Quit_f_t Com_Quit_f; @@ -220,13 +224,13 @@ namespace game return scrMemTreeGlob + 12 * size_t(MT_AllocIndex(numBytes, type)); } - __declspec(naked) dvar_t* dvar_find_malleable_var(const char* dvarName) + __declspec(naked) dvar_t* dvar_find_malleable_var(const char* dvar_name) { static DWORD func = 0x531320; __asm { - mov edi, dvarName + mov edi, dvar_name call func retn } @@ -294,7 +298,7 @@ namespace game } } - __declspec(naked) void scr_notify_id_multiplayer(unsigned int id, unsigned int stringValue, + __declspec(naked) void scr_notify_id_multiplayer(unsigned int id, unsigned int string_value, unsigned int paramcount) { static DWORD func = 0x56B5E0; @@ -302,7 +306,7 @@ namespace game __asm { mov eax, paramcount - push stringValue + push string_value push id call func add esp, 8h @@ -310,7 +314,7 @@ namespace game } } - __declspec(naked) void scr_notify_id_singleplayer(unsigned int id, unsigned int stringValue, + __declspec(naked) void scr_notify_id_singleplayer(unsigned int id, unsigned int string_value, unsigned int paramcount) { static DWORD func = 0x610980; @@ -318,7 +322,7 @@ namespace game __asm { mov eax, paramcount - push stringValue + push string_value push id call func add esp, 8h @@ -478,13 +482,13 @@ namespace game } } - __declspec(naked) void client_command_dedi(int clientNum) + __declspec(naked) void client_command_dedi(int client_num) { static DWORD func = 0x47EA40; __asm { - mov edi, clientNum + mov edi, client_num call func retn } @@ -512,14 +516,14 @@ namespace game addr->type = type; } - __declspec(naked) void cbuf_add_text_dedi(LocalClientNum_t localClientNum, const char* text) + __declspec(naked) void cbuf_add_text_dedi(LocalClientNum_t local_client_num, const char* text) { static DWORD func = 0x4CB5D0; __asm { mov eax, text - push localClientNum + push local_client_num call func add esp, 4h retn @@ -684,6 +688,10 @@ namespace game native::PM_trace = native::PM_trace_t(SELECT_VALUE(0x544BF0, 0x41CEB0, 0x0)); + native::Jump_ClearState = native::Jump_ClearState_t(SELECT_VALUE(0x514CE0, 0x4160F0, 0x0)); + + native::Vec3Normalize = native::Vec3Normalize_t(SELECT_VALUE(0x536A20, 0x415EE0, 0x0)); + native::Cmd_ExecuteSingleCommand = native::Cmd_ExecuteSingleCommand_t( SELECT_VALUE(0x4D6960, 0x5462B0, 0x4CC360)); diff --git a/src/game/game.hpp b/src/game/game.hpp index 2f1d5c9..d77c438 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -117,6 +117,12 @@ namespace game const Bounds* bounds, int passEntityNum, int contentMask); extern PM_trace_t PM_trace; + typedef void (*Jump_ClearState_t)(playerState_s* ps); + extern Jump_ClearState_t Jump_ClearState; + + typedef void (*Vec3Normalize_t)(float* v); + extern Vec3Normalize_t Vec3Normalize; + typedef void (*Cmd_ExecuteSingleCommand_t)(LocalClientNum_t localClientNum, int controllerIndex, const char* text); extern Cmd_ExecuteSingleCommand_t Cmd_ExecuteSingleCommand; @@ -151,6 +157,13 @@ namespace game constexpr auto ENTITYNUM_NONE = MAX_GENTITIES - 1u; extern gentity_s* g_entities; + // PM Global Definitions & Functions + constexpr auto JUMP_LAND_SLOWDOWN_TIME = 1800; + + // From Quake III, to match game's assembly + template + constexpr auto VectorScale(T v, R s, T out) { out[0] = v[0] * s; out[1] = v[1] * s; out[2] = v[2] * s; } + namespace mp { extern client_t* svs_clients; diff --git a/src/game/structs.hpp b/src/game/structs.hpp index 351ad99..d72ca9d 100644 --- a/src/game/structs.hpp +++ b/src/game/structs.hpp @@ -769,7 +769,23 @@ namespace game int bobCycle; float origin[3]; float velocity[3]; - unsigned char __pad0[0x4B8]; + int grenadeTimeLeft; + int throwbackGrenadeOwner; + int throwbackGrenadeTimeLeft; + unsigned int throwbackWeapon; + int remoteEyesEnt; + int remoteEyesTagname; + int remoteControlEnt; + int remoteTurretEnt; + int foliageSoundTime; + int gravity; + int speed; + int delta_angles; + int groundEntityNum; + float vLadderVec[3]; + int jumpTime; + float jumpOriginZ; + unsigned char __pad0[0x470]; unsigned int perks[0x2]; unsigned int perkSlots[0x9]; unsigned char __pad1[0x2DE8]; diff --git a/src/module/player_movement.cpp b/src/module/player_movement.cpp index 65c2654..f473ea5 100644 --- a/src/module/player_movement.cpp +++ b/src/module/player_movement.cpp @@ -1,15 +1,25 @@ #include -#include "player_movement.hpp" +#include "game/game.hpp" #include "utils/hook.hpp" +#include "player_movement.hpp" + const game::native::dvar_t* player_movement::player_sustainAmmo; +const game::native::dvar_t* player_movement::jump_slowdownEnable; +const game::native::dvar_t* player_movement::jump_ladderPushVel; +const game::native::dvar_t* player_movement::jump_enableFallDamage; +const game::native::dvar_t* player_movement::jump_height; const game::native::dvar_t* player_movement::pm_bounces; const game::native::dvar_t* player_movement::pm_playerEjection; const game::native::dvar_t* player_movement::pm_playerCollision; const game::native::dvar_t* player_movement::pm_rocketJump; const game::native::dvar_t* player_movement::pm_elevators; +DWORD player_movement::bounce_addr; +DWORD player_movement::dont_bounce_addr; +DWORD player_movement::push_off_ladder_addr; + 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) { @@ -19,11 +29,10 @@ void player_movement::pm_weapon_use_ammo(game::native::playerState_s* ps, const } } -__declspec(naked) void player_movement::pm_step_slide_move_stub_mp() +__declspec(naked) void player_movement::pm_step_slide_move_stub() { __asm { - // Check value of pm_bounces push eax mov eax, player_movement::pm_bounces cmp byte ptr [eax + 0xC], 1 @@ -34,42 +43,13 @@ __declspec(naked) void player_movement::pm_step_slide_move_stub_mp() // Code hook skipped cmp dword ptr [esp + 0x24], 0 - jnz dontBounce + jnz dont_bounce bounce: - push 0x424D58 - retn + jmp bounce_addr - dontBounce: - push 0x424D6C - retn - } -} - -__declspec(naked) void player_movement::pm_step_slide_move_stub_sp() -{ - __asm - { - // Check value of pm_bounces - push eax - mov eax, player_movement::pm_bounces - cmp byte ptr [eax + 0xC], 1 - pop eax - - // Bounce if enabled is true - je bounce - - // Code hook skipped - cmp dword ptr [esp + 0x24], 0 - jnz dontBounce - - bounce: - push 0x43D91F - retn - - dontBounce: - push 0x43D933 - retn + dont_bounce: + jmp dont_bounce_addr } } @@ -133,6 +113,150 @@ void player_movement::pm_trace_stub(const game::native::pmove_t* pm, game::nativ } } +__declspec(naked) void player_movement::jump_push_off_ladder_stub() +{ + __asm + { + push eax + mov eax, player_movement::jump_ladderPushVel + fld dword ptr [eax + 0xC] + pop eax + + jmp push_off_ladder_addr + } +} + +__declspec(naked) void player_movement::jump_start_stub() +{ + __asm + { + push eax + mov eax, player_movement::jump_height + fld dword ptr [eax + 0xC] + pop eax + + push 0x41696F + retn + } +} + +void player_movement::jump_apply_slowdown_stub(game::native::playerState_s* ps) +{ + assert(ps->pm_flags & game::native::PMF_JUMPING); + + auto scale = 1.0f; + + if (ps->pm_time <= game::native::JUMP_LAND_SLOWDOWN_TIME) + { + if (ps->pm_time == 0) + { + const auto height = ps->jumpOriginZ + 18.0f; + + if (height <= ps->origin[2]) + { + scale = 0.5f; + ps->pm_time = 1200; + } + else + { + scale = 0.65f; + ps->pm_time = game::native::JUMP_LAND_SLOWDOWN_TIME; + } + } + } + else + { + game::native::Jump_ClearState(ps); + scale = 0.65f; + } + + if ((ps->pm_flags & game::native::PMF_DIVING) == 0 + && player_movement::jump_slowdownEnable->current.enabled) + { + game::native::VectorScale(ps->velocity, scale, ps->velocity); + } +} + +float player_movement::jump_get_land_factor(game::native::playerState_s* ps) +{ + assert(ps->pm_flags & game::native::PMF_JUMPING); + assert(ps->pm_time <= game::native::JUMP_LAND_SLOWDOWN_TIME); + + if (!player_movement::jump_slowdownEnable->current.enabled + || (ps->pm_flags & game::native::PMF_DIVING) != 0) + { + return 1.0f; + } + + if (ps->pm_time < 1700) + { + return (ps->pm_time * 1.5f * 0.000588f) + 1.0f; + } + + return 2.5f; +} + +__declspec(naked) void player_movement::jump_get_land_factor_stub() +{ + __asm + { + pushad + + push eax // ps + call player_movement::jump_get_land_factor + add esp, 4 + + popad + ret + } +} + +__declspec(naked) void player_movement::pm_crash_land_stub_mp() +{ + static DWORD func = 0x41E6B0; + + __asm + { + push eax + mov eax, player_movement::jump_enableFallDamage + cmp byte ptr [eax + 0xC], 0 + pop eax + + // If disabled just return + jz skip_crash + + // Arguments are in the registers + call func + + skip_crash: + ret + } +} + +__declspec(naked) void player_movement::pm_crash_land_stub_sp() +{ + static DWORD func = 0x6405A0; + + __asm + { + push eax + mov eax, player_movement::jump_enableFallDamage + cmp byte ptr [eax + 0xC], 0 + pop eax + + // If disabled just return + jz skip_crash + + // ps is in the esi register + push [esp + 0x4] // pml + call func + add esp, 4 + + skip_crash: + ret + } +} + const game::native::dvar_t* player_movement::dvar_register_player_sustain_ammo(const char* dvar_name, bool value, unsigned __int16 /*flags*/, const char* description) { @@ -142,6 +266,33 @@ const game::native::dvar_t* player_movement::dvar_register_player_sustain_ammo(c return player_movement::player_sustainAmmo; } +const game::native::dvar_t* player_movement::dvar_register_jump_ladder_push_vel(const char* dvar_name, + float value, float min, float max, unsigned __int16 /*flags*/, const char* description) +{ + player_movement::jump_ladderPushVel = game::native::Dvar_RegisterFloat(dvar_name, + value, min, max, game::native::DVAR_CODINFO, description); + + return player_movement::jump_ladderPushVel; +} + +const game::native::dvar_t* player_movement::dvar_register_jump_slowdown_enable(const char* dvar_name, + bool value, unsigned __int16 /*flags*/, const char* description) +{ + player_movement::jump_slowdownEnable = game::native::Dvar_RegisterBool(dvar_name, + value, game::native::DVAR_CODINFO, description); + + return player_movement::jump_slowdownEnable; +} + +const game::native::dvar_t* player_movement::dvar_register_jump_height(const char* dvar_name, + float value, float min, float max, unsigned __int16 /*flags*/, const char* description) +{ + player_movement::jump_height = game::native::Dvar_RegisterFloat(dvar_name, + value, min, max, game::native::DVAR_CODINFO, description); + + return player_movement::jump_height; +} + void player_movement::patch_mp() { player_movement::pm_playerEjection = game::native::Dvar_RegisterBool("pm_playerEjection", @@ -149,13 +300,17 @@ void player_movement::patch_mp() player_movement::pm_rocketJump = game::native::Dvar_RegisterBool("pm_rocketJump", false, game::native::DVAR_CODINFO, "CoD4 rocket jumps"); + // Un-Cheat the dvars utils::hook(0x418D9C, &player_movement::dvar_register_player_sustain_ammo, HOOK_CALL).install()->quick(); + utils::hook(0x4160A7, &player_movement::dvar_register_jump_ladder_push_vel, HOOK_CALL).install()->quick(); + utils::hook(0x41602B, &player_movement::dvar_register_jump_height, HOOK_CALL).install()->quick(); + utils::hook(0x416074, &player_movement::dvar_register_jump_slowdown_enable, 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(); - utils::hook(0x424D51, &player_movement::pm_step_slide_move_stub_mp, HOOK_JUMP).install()->quick(); + utils::hook(0x424D51, &player_movement::pm_step_slide_move_stub, HOOK_JUMP).install()->quick(); utils::hook(0x4F9EFB, &player_movement::stuck_in_client_stub, HOOK_CALL).install()->quick(); // ClientEndFrame utils::hook(0x57CF45, &player_movement::cm_transformed_capsule_trace_stub, HOOK_CALL).install()->quick(); // SV_ClipMoveToEntity @@ -169,18 +324,31 @@ void player_movement::patch_mp() utils::hook(0x41F995, &player_movement::pm_trace_stub, HOOK_CALL).install()->quick(); // PM_CheckDuck utils::hook(0x41F8D8, &player_movement::pm_trace_stub, HOOK_CALL).install()->quick(); // PM_CheckDuck utils::hook(0x41F941, &player_movement::pm_trace_stub, HOOK_CALL).install()->quick(); // PM_CheckDuck + + utils::hook(0x416866, &player_movement::jump_push_off_ladder_stub, HOOK_JUMP).install()->quick(); // Jump_Check + utils::hook::nop(0x41686B, 1); // Nop skipped opcode + + utils::hook(0x416969, &player_movement::jump_start_stub, HOOK_JUMP).install()->quick(); // Jump_Check + utils::hook::nop(0x41696E, 1); // Nop skipped opcode + + utils::hook(0x4225CA, &player_movement::jump_apply_slowdown_stub, HOOK_CALL).install()->quick(); // PM_WalkMove + utils::hook(0x41669B, &player_movement::jump_get_land_factor_stub, HOOK_CALL).install()->quick(); // Jump_Start + + utils::hook(0x422BE0, &player_movement::pm_crash_land_stub_mp, HOOK_CALL).install()->quick(); // PM_GroundTrace } 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"); + player_movement::jump_ladderPushVel = game::native::Dvar_RegisterFloat("jump_ladderPushVel", + 128.0f, 0.0f, 1024.0f, game::native::DVAR_CODINFO, "The velocity of a jump off of a ladder"); 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(); - utils::hook(0x43D918, &player_movement::pm_step_slide_move_stub_sp, HOOK_JUMP).install()->quick(); + utils::hook(0x43D918, &player_movement::pm_step_slide_move_stub, HOOK_JUMP).install()->quick(); utils::hook(0x41F9A6, &player_movement::cm_transformed_capsule_trace_stub, HOOK_CALL).install()->quick(); // SV_ClipMoveToEntity utils::hook(0x57B14F, &player_movement::cm_transformed_capsule_trace_stub, HOOK_CALL).install()->quick(); // CG_ClipMoveToEntity @@ -191,6 +359,11 @@ void player_movement::patch_sp() utils::hook(0x64181A, &player_movement::pm_trace_stub, HOOK_CALL).install()->quick(); // PM_CheckDuck utils::hook(0x641701, &player_movement::pm_trace_stub, HOOK_CALL).install()->quick(); // PM_CheckDuck utils::hook(0x6417A9, &player_movement::pm_trace_stub, HOOK_CALL).install()->quick(); // PM_CheckDuck + + utils::hook(0x63EA46, player_movement::jump_push_off_ladder_stub, HOOK_JUMP).install()->quick(); // Jump_Check + utils::hook::nop(0x63EA4B, 1); // Nop skipped opcode + + utils::hook(0x6442DF, &player_movement::pm_crash_land_stub_sp, HOOK_CALL).install()->quick(); // PM_GroundTrace } void player_movement::post_load() @@ -203,12 +376,18 @@ void player_movement::post_load() return; } + player_movement::bounce_addr = SELECT_VALUE(0x43D91F, 0x424D58, 0x0); + player_movement::dont_bounce_addr = SELECT_VALUE(0x43D933, 0x424D6C, 0x0); + player_movement::push_off_ladder_addr = SELECT_VALUE(0x63EA4C, 0x41686C, 0x0); + player_movement::pm_bounces = game::native::Dvar_RegisterBool("pm_bounces", false, game::native::dvar_flags::DVAR_CODINFO, "CoD4 Bounces"); player_movement::pm_playerCollision = game::native::Dvar_RegisterBool("pm_playerCollision", true, game::native::DVAR_CODINFO, "Push intersecting players away from each other"); player_movement::pm_elevators = game::native::Dvar_RegisterBool("pm_elevators", false, game::native::DVAR_CODINFO, "CoD4 Elevators"); + player_movement::jump_enableFallDamage = game::native::Dvar_RegisterBool("jump_enableFallDamage", + true, game::native::dvar_flags::DVAR_CODINFO, "Enable fall damage"); if (game::is_mp()) this->patch_mp(); else if (game::is_sp()) this->patch_sp(); diff --git a/src/module/player_movement.hpp b/src/module/player_movement.hpp index c105c30..c291be9 100644 --- a/src/module/player_movement.hpp +++ b/src/module/player_movement.hpp @@ -1,6 +1,5 @@ #pragma once #include "loader/module_loader.hpp" -#include "game/game.hpp" class player_movement final : public module { @@ -9,20 +8,33 @@ public: private: static const game::native::dvar_t* player_sustainAmmo; + static const game::native::dvar_t* jump_slowdownEnable; + static const game::native::dvar_t* jump_ladderPushVel; + static const game::native::dvar_t* jump_enableFallDamage; + static const game::native::dvar_t* jump_height; static const game::native::dvar_t* pm_bounces; static const game::native::dvar_t* pm_playerEjection; static const game::native::dvar_t* pm_playerCollision; static const game::native::dvar_t* pm_rocketJump; static const game::native::dvar_t* pm_elevators; - 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 DWORD bounce_addr; + static DWORD dont_bounce_addr; + static DWORD push_off_ladder_addr; + static void pm_weapon_use_ammo(game::native::playerState_s* ps, const game::native::Weapon weapon, + bool is_alternate, int amount, game::native::PlayerHandIndex hand); + + static const game::native::dvar_t* dvar_register_jump_ladder_push_vel(const char* dvar_name, + float value, float min, float max, unsigned __int16 flags, const char* description); + static const game::native::dvar_t* dvar_register_jump_height(const char* dvar_name, + float value, float min, float max, unsigned __int16 flags, const char* description); + static const game::native::dvar_t* dvar_register_jump_slowdown_enable(const char* dvar_name, + bool value, unsigned __int16 flags, const char* description); 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 pm_step_slide_move_stub_mp(); - static void pm_step_slide_move_stub_sp(); + static void pm_step_slide_move_stub(); static int stuck_in_client_stub(game::native::gentity_s* self); static void cm_transformed_capsule_trace_stub(game::native::trace_t* results, const float* start, @@ -40,6 +52,17 @@ private: static void pm_trace_stub(const game::native::pmove_t* pm, game::native::trace_t* results, const float* start, const float* end, const game::native::Bounds* bounds, int pass_entity_num, int content_mask); + static void jump_push_off_ladder_stub(); + // On SP this is already unlocked + static void jump_start_stub(); + + static void jump_apply_slowdown_stub(game::native::playerState_s* ps); + static float jump_get_land_factor(game::native::playerState_s* ps); + static void jump_get_land_factor_stub(); + + static void pm_crash_land_stub_mp(); + static void pm_crash_land_stub_sp(); + static void patch_mp(); static void patch_sp(); }; diff --git a/src/module/test_clients.cpp b/src/module/test_clients.cpp index 13b2896..0c69ec8 100644 --- a/src/module/test_clients.cpp +++ b/src/module/test_clients.cpp @@ -2,7 +2,6 @@ #include "test_clients.hpp" #include "command.hpp" -#include "scheduler.hpp" #include "utils/hook.hpp" @@ -76,7 +75,7 @@ game::native::gentity_s* test_clients::sv_add_test_client() return nullptr; } - auto client = &game::native::mp::svs_clients[idx]; + const auto client = &game::native::mp::svs_clients[idx]; client->bIsTestClient = 1; game::native::SV_SendClientGameState(client); @@ -124,10 +123,10 @@ __declspec(naked) void test_clients::reset_reliable_mp() } } -bool test_clients::check_timeouts(const game::native::mp::client_t* cl) +bool test_clients::check_timeouts(const game::native::mp::client_t* client) { - return (!cl->bIsTestClient || cl->header.state == game::native::clientState_t::CS_ZOMBIE) - && cl->header.netchan.remoteAddress.type != game::native::netadrtype_t::NA_LOOPBACK; + return (!client->bIsTestClient || client->header.state == game::native::clientState_t::CS_ZOMBIE) + && client->header.netchan.remoteAddress.type != game::native::netadrtype_t::NA_LOOPBACK; } __declspec(naked) void test_clients::check_timeouts_stub_mp() diff --git a/src/module/test_clients.hpp b/src/module/test_clients.hpp index ff99abd..78529c9 100644 --- a/src/module/test_clients.hpp +++ b/src/module/test_clients.hpp @@ -18,6 +18,6 @@ private: static void reset_reliable_mp(); - static bool check_timeouts(const game::native::mp::client_t* cl); + static bool check_timeouts(const game::native::mp::client_t* client); static void check_timeouts_stub_mp(); };