404 lines
12 KiB
C++
404 lines
12 KiB
C++
#include <std_include.hpp>
|
|
#include "loader/component_loader.hpp"
|
|
#include "game/game.hpp"
|
|
#include "game/dvars.hpp"
|
|
|
|
#include <utils/hook.hpp>
|
|
|
|
namespace gameplay
|
|
{
|
|
namespace
|
|
{
|
|
template <typename T, typename R>
|
|
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<int>(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<void>(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<int64_t>(&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<int64_t>(&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<int64_t>(&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<int64_t>(&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<void(*)(void*, void*)>(0x140220000)(ps, pml);
|
|
}
|
|
}
|
|
|
|
float get_jump_height_stub(void* pmove)
|
|
{
|
|
auto jump_height = reinterpret_cast<float(*)(void*)>(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<float>(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<float>(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<game::mp::playerState_t*>(pm->ps);
|
|
|
|
assert(ps);
|
|
|
|
velocity_sqrd = (height * 2.0f) * static_cast<float>(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<int64_t>(&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<void>(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<game::mp::gentity_t*>(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<float>::max(),
|
|
game::DVAR_FLAG_REPLICATED, "The scale applied to the pushback force of a rocket");
|
|
}
|
|
};
|
|
}
|
|
|
|
REGISTER_COMPONENT(gameplay::component)
|