#include #include "loader/component_loader.hpp" #include "game/game.hpp" #include "game/dvars.hpp" #include namespace gameplay { namespace { 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; } utils::hook::detour pm_weapon_use_ammo_hook; int stuck_in_client_stub(void* self) { if (dvars::g_playerEjection->current.enabled) { return utils::hook::invoke(0x140386950, self); // StuckInClient } return 0; } void cm_transformed_capsule_trace_stub(game::trace_t* results, const float* start, const float* end, game::Bounds* bounds, game::Bounds* capsule, int contents, const float* origin, const float* angles) { if (dvars::g_playerCollision->current.enabled) { utils::hook::invoke(0x1403F3050, results, start, end, bounds, capsule, contents, origin, angles); // CM_TransformedCapsuleTrace } } const auto g_gravity_stub = utils::hook::assemble([](utils::hook::assembler& a) { a.push(rax); a.mov(rax, qword_ptr(reinterpret_cast(&dvars::g_gravity))); a.mov(eax, dword_ptr(rax, 0x10)); a.mov(dword_ptr(rbx, 0x5C), eax); a.mov(eax, ptr(rbx, 0x33E8)); a.mov(ptr(rbx, 0x25C), eax); a.pop(rax); a.jmp(0x1403828D5); }); const auto g_speed_stub = utils::hook::assemble([](utils::hook::assembler& a) { a.push(rax); a.mov(rax, qword_ptr(reinterpret_cast(&dvars::g_speed))); a.mov(eax, dword_ptr(rax, 0x10)); a.mov(dword_ptr(rdi, 0x60), eax); a.pop(rax); a.mov(eax, ptr(rdi, 0xEA4)); a.add(eax, ptr(rdi, 0xEA0)); a.jmp(0x140383796); }); const auto pm_bouncing_stub_sp = utils::hook::assemble([](utils::hook::assembler& a) { const auto no_bounce = a.newLabel(); const auto loc_14046ED26 = a.newLabel(); a.push(rax); a.mov(rax, qword_ptr(reinterpret_cast(&dvars::pm_bouncing))); a.mov(al, byte_ptr(rax, 0x10)); a.cmp(ptr(rbp, -0x40), al); a.pop(rax); a.jz(no_bounce); a.jmp(0x14046EC7E); a.bind(no_bounce); a.cmp(ptr(rbp, -0x80), r13d); a.jnz(loc_14046ED26); a.jmp(0x14046EC6C); a.bind(loc_14046ED26); a.jmp(0x14046ED26); }); const auto pm_bouncing_stub_mp = utils::hook::assemble([](utils::hook::assembler& a) { const auto no_bounce = a.newLabel(); const auto loc_140228FB8 = a.newLabel(); a.push(rax); a.mov(rax, qword_ptr(reinterpret_cast(&dvars::pm_bouncing))); a.mov(al, byte_ptr(rax, 0x10)); a.cmp(byte_ptr(rbp, -0x38), al); a.pop(rax); a.jz(no_bounce); a.jmp(0x140229019); a.bind(no_bounce); a.cmp(dword_ptr(rbp, -0x70), 0); a.jnz(loc_140228FB8); a.jmp(0x14022900B); a.bind(loc_140228FB8); a.jmp(0x140228FB8); }); const void pm_crashland_stub(void* ps, void* pml) { if (dvars::jump_enableFallDamage->current.enabled) { reinterpret_cast(0x140220000)(ps, pml); } } float get_jump_height_stub(void* pmove) { auto jump_height = reinterpret_cast(0x140213140)(pmove); if (jump_height == 39.f) { jump_height = dvars::jump_height->current.value; } return jump_height; } void jump_apply_slowdown_stub(game::mp::playerState_t* ps) { assert(ps->pm_flags & game::PMF_JUMPING); float scale = 1.0f; if (ps->pm_time > 1800) { game::Jump_ClearState(ps); scale = 0.65f; } else if (ps->pm_time == 0) { if (ps->jumpOriginZ + 18.0f <= ps->origin[2]) { ps->pm_time = 1200; scale = 0.5f; } else { ps->pm_time = 1800; scale = 0.65f; } } if (dvars::jump_slowdownEnable->current.enabled) { VectorScale(ps->velocity, scale, ps->velocity); } } float jump_get_slowdown_friction(game::mp::playerState_t* ps) { assert(ps->pm_flags & game::PMF_JUMPING); assert(ps->pm_time <= game::JUMP_LAND_SLOWDOWN_TIME); if (!dvars::jump_slowdownEnable->current.enabled) { return 1.0f; } if (ps->pm_time < 1700) { return static_cast(ps->pm_time) * 1.5f * 0.00058823527f + 1.0f; } return 2.5f; } float jump_reduce_friction_stub(game::mp::playerState_t* ps) { float control; assert(ps->pm_flags & game::PMF_JUMPING); if (ps->pm_time > game::JUMP_LAND_SLOWDOWN_TIME) { game::Jump_ClearState(ps); control = 1.0f; } else { control = jump_get_slowdown_friction(ps); } return control; } float jump_get_land_factor(game::mp::playerState_t* ps) { assert(ps->pm_flags & game::PMF_JUMPING); assert(ps->pm_time <= game::JUMP_LAND_SLOWDOWN_TIME); if (!dvars::jump_slowdownEnable->current.enabled) { return 1.0f; } if (ps->pm_time < 1700) { return static_cast(ps->pm_time) * 1.5f * 0.00058823527f + 1.0f; } return 2.5f; } void jump_start_stub(game::pmove_t* pm, game::pml_t* pml, float height) { static_assert(offsetof(game::mp::playerState_t, groundEntityNum) == 0x70); static_assert(offsetof(game::mp::playerState_t, pm_time) == 0x8); static_assert(offsetof(game::mp::playerState_t, sprintState.sprintButtonUpRequired) == 0x240); static_assert(offsetof(game::pml_t, frametime) == 0x24); static_assert(offsetof(game::pml_t, walking) == 0x2C); static_assert(offsetof(game::pml_t, groundPlane) == 0x30); static_assert(offsetof(game::pml_t, almostGroundPlane) == 0x34); float factor; float velocity_sqrd; game::mp::playerState_t* ps; ps = static_cast(pm->ps); assert(ps); velocity_sqrd = (height * 2.0f) * static_cast(ps->gravity); if ((ps->pm_flags & game::PMF_JUMPING) != 0 && ps->pm_time <= game::JUMP_LAND_SLOWDOWN_TIME) { factor = jump_get_land_factor(ps); assert(factor); velocity_sqrd = velocity_sqrd / factor; } pml->walking = 0; pml->groundPlane = 0; ps->groundEntityNum = game::ENTITYNUM_NONE; ps->jumpTime = pm->cmd.serverTime; ps->jumpOriginZ = ps->origin[2]; ps->velocity[2] = std::sqrtf(velocity_sqrd); ps->pm_flags &= ~(game::PMF_UNK1 | game::PMF_UNK2); ps->pm_flags |= game::PMF_JUMPING; ps->pm_time = 0; ps->sprintState.sprintButtonUpRequired = 0; ps->aimSpreadScale = ps->aimSpreadScale + dvars::jump_spreadAdd->current.value; if (ps->aimSpreadScale > 255.0f) { ps->aimSpreadScale = 255.0f; } } const auto jump_push_off_ladder_stub = utils::hook::assemble([](utils::hook::assembler& a) { a.mov(rax, qword_ptr(reinterpret_cast(&dvars::jump_ladderPushVel))); a.movaps(xmm8, dword_ptr(rax, 0x10)); a.mulss(xmm6, xmm8); a.mulss(xmm7, xmm8); a.jmp(0x140213494); }); void pm_player_trace_stub(game::pmove_t* move, game::trace_t* trace, const float* f3, const float* f4, const game::Bounds* bounds, int a6, int a7) { game::PM_playerTrace(move, trace, f3, f4, bounds, a6, a7); if (dvars::g_enableElevators->current.enabled) { trace->startsolid = false; } } void pm_trace_stub(const game::pmove_t* move, game::trace_t* trace, const float* f3, const float* f4, const game::Bounds* bounds, int a6, int a7) { game::PM_trace(move, trace, f3, f4, bounds, a6, a7); if (dvars::g_enableElevators->current.enabled) { trace->allsolid = false; } } void pm_weapon_use_ammo_stub(game::playerState_t* ps, game::Weapon weapon, bool is_alternate, int amount, game::PlayerHandIndex hand) { if (!dvars::player_sustainAmmo->current.enabled) { pm_weapon_use_ammo_hook.invoke(ps, weapon, is_alternate, amount, hand); } } game::mp::gentity_t* weapon_rocket_launcher_fire_stub(game::mp::gentity_t* ent, game::Weapon weapon, float spread, game::weaponParms* wp, const float* gun_vel, game::mp::missileFireParms* fire_parms, bool magic_bullet) { auto* result = utils::hook::invoke(0x1403DB8A0, ent, weapon, spread, wp, gun_vel, fire_parms, magic_bullet); if (ent->client != nullptr && wp->weapDef->inventoryType != game::WEAPINVENTORY_EXCLUSIVE) { const auto scale = dvars::g_rocketPushbackScale->current.value; ent->client->ps.velocity[0] += (0.0f - wp->forward[0]) * scale; ent->client->ps.velocity[1] += (0.0f - wp->forward[1]) * scale; ent->client->ps.velocity[2] += (0.0f - wp->forward[2]) * scale; } return result; } } class component final : public component_interface { public: void post_unpack() override { // Implement bouncing dvar if (game::environment::is_sp()) { utils::hook::nop(0x14046EC5C, 16); } utils::hook::jump( SELECT_VALUE(0x14046EC5C, 0x140228FFF), SELECT_VALUE(pm_bouncing_stub_sp, pm_bouncing_stub_mp), true); dvars::pm_bouncing = game::Dvar_RegisterBool("pm_bouncing", false, game::DVAR_FLAG_REPLICATED, "Enable bouncing"); dvars::player_sustainAmmo = game::Dvar_RegisterBool("player_sustainAmmo", false, game::DVAR_FLAG_REPLICATED, "Firing weapon will not decrease clip ammo."); pm_weapon_use_ammo_hook.create(SELECT_VALUE(0x140479640, 0x140238A90), &pm_weapon_use_ammo_stub); if (game::environment::is_sp()) return; // Implement player ejection dvar dvars::g_playerEjection = game::Dvar_RegisterBool("g_playerEjection", true, game::DVAR_FLAG_REPLICATED, "Flag whether player ejection is on or off"); utils::hook::call(0x140382C13, stuck_in_client_stub); // Implement player collision dvar dvars::g_playerCollision = game::Dvar_RegisterBool("g_playerCollision", true, game::DVAR_FLAG_REPLICATED, "Flag whether player collision is on or off"); utils::hook::call(0x14048A49A, cm_transformed_capsule_trace_stub); // SV_ClipMoveToEntity utils::hook::call(0x1402B5B88, cm_transformed_capsule_trace_stub); // CG_ClipMoveToEntity // Implement gravity dvar utils::hook::nop(0x1403828C8, 13); utils::hook::jump(0x1403828C8, g_gravity_stub, true); dvars::g_gravity = game::Dvar_RegisterInt("g_gravity", 800, 0, 1000, game::DVAR_FLAG_NONE, "Game gravity in inches per second squared"); // Implement speed dvar utils::hook::nop(0x140383789, 13); utils::hook::jump(0x140383789, g_speed_stub, true); dvars::g_speed = game::Dvar_RegisterInt("g_speed", 190, 0, 999, game::DVAR_FLAG_NONE, "Maximum player speed"); utils::hook::call(0x140225857, jump_apply_slowdown_stub); utils::hook::call(0x1402210A2, jump_reduce_friction_stub); utils::hook::call(0x140213015, jump_start_stub); dvars::jump_slowdownEnable = game::Dvar_RegisterBool("jump_slowdownEnable", true, game::DVAR_FLAG_REPLICATED, "Slow player movement after jumping"); dvars::jump_spreadAdd = game::Dvar_RegisterFloat("jump_spreadAdd", 64.0f, 0.0f, 512.0f, game::DVAR_FLAG_REPLICATED, "The amount of spread scale to add as a side effect of jumping"); utils::hook::call(0x1402219A5, pm_crashland_stub); dvars::jump_enableFallDamage = game::Dvar_RegisterBool("jump_enableFallDamage", true, game::DVAR_FLAG_REPLICATED, "Enable fall damage"); utils::hook::call(0x140213007, get_jump_height_stub); dvars::jump_height = game::Dvar_RegisterFloat("jump_height", 39.f, 0.f, 1024.f, game::DVAR_FLAG_REPLICATED, "Jump height"); utils::hook::jump(0x140213484, jump_push_off_ladder_stub, true); dvars::jump_ladderPushVel = game::Dvar_RegisterFloat("jump_ladderPushVel", 128.f, 0.f, 1024.f, game::DVAR_FLAG_REPLICATED, "Ladder push velocity"); utils::hook::call(0x140221F92, pm_player_trace_stub); utils::hook::call(0x140221FFA, pm_player_trace_stub); utils::hook::call(0x14021F0E3, pm_trace_stub); dvars::g_enableElevators = game::Dvar_RegisterBool("g_enableElevators", false, game::DVAR_FLAG_REPLICATED, "Enable Elevators"); utils::hook::call(0x1403D933E, weapon_rocket_launcher_fire_stub); dvars::g_rocketPushbackScale = game::Dvar_RegisterFloat("g_rocketPushbackScale", 1.0f, 1.0f, std::numeric_limits::max(), game::DVAR_FLAG_REPLICATED, "The scale applied to the pushback force of a rocket"); } }; } REGISTER_COMPONENT(gameplay::component)