iw6-mod/src/client/component/gameplay.cpp

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)