Add Rapidfire
This commit is contained in:
parent
be4baf0f18
commit
a6c1a16aa6
@ -1,4 +1,4 @@
|
||||
<Tab name="Movement" padding="6 6 6 6">
|
||||
<Tab name="Movement" padding="0 6 6 6">
|
||||
<Box padding="12 6 6 6" width="content" height="content" name="Bunny hop">
|
||||
<List width="150">
|
||||
<AutoVariable width="fill" target="bunnyhop.enable" label="Enable bunny hop"/>
|
||||
@ -47,7 +47,7 @@
|
||||
<!-- <!AutoVariable width="fill" target="navbot.take-teleporters" label="take teleporters"/> -->
|
||||
</List>
|
||||
</Box>
|
||||
<Box padding="12 6 6 6" width="content" height="content" name="Anti-backstab" x="370">
|
||||
<Box padding="12 6 6 6" width="content" height="content" name="Anti-backstab" x="170" y="300">
|
||||
<List width="180">
|
||||
<AutoVariable width="fill" target="antibackstab.enable" label="Enable anti-backstab"/>
|
||||
<AutoVariable width="fill" target="antibackstab.angle" label="Detection angle"/>
|
||||
@ -56,11 +56,12 @@
|
||||
<AutoVariable width="fill" target="antibackstab.nope" label="Nope!" tooltip="Have the charachter say 'no'."/>
|
||||
</List>
|
||||
</Box>
|
||||
<Box padding="12 6 6 6" width="content" height="content" name="Warp" x="370" y="65">
|
||||
<List width="180">
|
||||
<Box padding="12 6 6 6" width="content" height="content" name="Warp" x="370">
|
||||
<List width="195">
|
||||
<AutoVariable width="fill" target="warp.enabled" label="Enable warp" tooltip="Allows you to charge a burst of speed."/>
|
||||
<AutoVariable width="fill" target="warp.speed" label="Warp speed" tooltip="The discharge speed (0.5 = +50%, 2 = +200%. +2300% is the maximum possible)."/>
|
||||
<AutoVariable width="fill" target="warp.key" label="Warp key" tooltip="Pressing this key will use all stored ticks to accelerate you."/>
|
||||
<AutoVariable width="fill" target="warp.charge-key" label="Charge key" tooltip="Hold down this key to charge warp."/>
|
||||
<AutoVariable width="fill" target="warp.draw" label="Draw warp" tooltip="Draws a bar indicating your warp readiness."/>
|
||||
<AutoVariable width="fill" target="warp.bar-size" label="Bar size"/>
|
||||
<AutoVariable width="fill" target="warp.bar-x" label="X position"/>
|
||||
@ -76,6 +77,13 @@
|
||||
<AutoVariable width="fill" target="warp.on-hit.backwards" label="Warp backwards" tooltip="Warp when hit option"/>
|
||||
<AutoVariable width="fill" target="warp.on-hit.left" label="Warp left" tooltip="Warp when hit option"/>
|
||||
<AutoVariable width="fill" target="warp.on-hit.right" label="Warp right" tooltip="Warp when hit option"/>
|
||||
<Box padding="12 6 6 6" width="content" height="content" name="Rapidfire">
|
||||
<List width="185">
|
||||
<AutoVariable width="fill" target="warp.rapidfire" label="Enable Rapidfire" tooltip="Allows you to shoot multiple shots at once or reduce time between shots."/>
|
||||
<AutoVariable width="fill" target="warp.rapidfire.no-movement" label="Prevent movement in rapidfire" tooltip="Attempt to not move when Rapidfiring."/>
|
||||
<AutoVariable width="fill" target="warp.rapidfire.key" label="Rapidfire key" tooltip="Optional. If set you can use this key to control when to rapidfire."/>
|
||||
</List>
|
||||
</Box>
|
||||
</List>
|
||||
</Box>
|
||||
</Tab>
|
||||
|
@ -80,7 +80,7 @@ struct offsets
|
||||
{
|
||||
return PlatformOffset(7, undefined, undefined);
|
||||
}
|
||||
static constexpr uint32_t CreateMoveEarly()
|
||||
static constexpr uint32_t CreateMoveInput()
|
||||
{
|
||||
return PlatformOffset(3, undefined, undefined);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include "settings/Bool.hpp"
|
||||
class INetMessage;
|
||||
namespace hacks::tf2::warp
|
||||
{
|
||||
|
@ -171,6 +171,7 @@ inline Vector GetAimAtAngles(Vector origin, Vector target, CachedEntity *punch_c
|
||||
}
|
||||
|
||||
void AimAt(Vector origin, Vector target, CUserCmd *cmd, bool compensate_punch = true);
|
||||
void FastStop();
|
||||
void AimAtHitbox(CachedEntity *ent, int hitbox, CUserCmd *cmd, bool compensate_punch = true);
|
||||
bool IsProjectileCrit(CachedEntity *ent);
|
||||
|
||||
|
@ -11,12 +11,14 @@ enum ec_types
|
||||
{
|
||||
/* Note: engine prediction is run on this kind of CreateMove */
|
||||
CreateMove = 0,
|
||||
/* Note: this is the CreatMove one layer higher, and should only be used for things that mess with command number*/
|
||||
CreateMoveEarly,
|
||||
/* This kind of CreateMove will run earlier than all CreateMove events
|
||||
/* Note: this is the CreateMove one layer higher, and should only be used for things that mess with command number*/
|
||||
CreateMoveLate,
|
||||
/* This kind of CreateMove will run earlier than "CreateMove" events
|
||||
* and guranteed to run before EnginePrediction
|
||||
*/
|
||||
CreateMove_NoEnginePred,
|
||||
/* Note: this is still CreateMove, just ran before original is called, needed in some cases like changing tickcount before original gets called*/
|
||||
CreateMoveEarly,
|
||||
#if ENABLE_VISUALS
|
||||
Draw,
|
||||
#endif
|
||||
|
@ -44,7 +44,7 @@ DECLARE_HOOKED_METHOD(DispatchUserMessage, bool, void *, int, bf_read &);
|
||||
DECLARE_HOOKED_METHOD(IN_KeyEvent, int, void *, int, ButtonCode_t, const char *);
|
||||
DECLARE_HOOKED_METHOD(FrameStageNotify, void, void *, ClientFrameStage_t);
|
||||
// IInput
|
||||
DECLARE_HOOKED_METHOD(CreateMoveEarly, void, IInput *, int, float, bool)
|
||||
DECLARE_HOOKED_METHOD(CreateMoveInput, void, IInput *, int, float, bool)
|
||||
DECLARE_HOOKED_METHOD(GetUserCmd, CUserCmd *, IInput *, int);
|
||||
// INetChannel
|
||||
DECLARE_HOOKED_METHOD(SendNetMsg, bool, INetChannel *, INetMessage &, bool, bool);
|
||||
|
@ -10,6 +10,11 @@
|
||||
#include <enums.hpp>
|
||||
#include "config.h"
|
||||
#include "vector"
|
||||
#include <optional>
|
||||
#include "interfaces.hpp"
|
||||
#include "sdk.hpp"
|
||||
|
||||
#pragma once
|
||||
|
||||
class CachedEntity;
|
||||
class Vector;
|
||||
@ -23,6 +28,7 @@ Vector ProjectilePrediction(CachedEntity *ent, int hb, float speed, float gravit
|
||||
Vector ProjectilePrediction_Engine(CachedEntity *ent, int hb, float speed, float gravitymod, float entgmod /* ignored */, float proj_startvelocity = 0.0f);
|
||||
|
||||
std::vector<Vector> Predict(Vector pos, float offset, Vector vel, Vector acceleration, std::pair<Vector, Vector> minmax, float time, int count, bool vischeck = true);
|
||||
Vector PredictStep(Vector pos, Vector &vel, Vector acceleration, std::pair<Vector, Vector> &minmax, float time, float steplength = g_GlobalVars->interval_per_tick, bool vischeck = true, std::optional<float> grounddistance = std::nullopt);
|
||||
float PlayerGravityMod(CachedEntity *player);
|
||||
|
||||
Vector EnginePrediction(CachedEntity *player, float time);
|
||||
|
@ -1065,7 +1065,7 @@ static CatCommand debug_print_crit_info("debug_print_crit_info", "Print a bunch
|
||||
});
|
||||
|
||||
static InitRoutine init([]() {
|
||||
EC::Register(EC::CreateMoveEarly, CreateMove, "crit_cm");
|
||||
EC::Register(EC::CreateMoveLate, CreateMove, "crit_cm");
|
||||
#if ENABLE_VISUALS
|
||||
EC::Register(EC::Draw, Draw, "crit_draw");
|
||||
#endif
|
||||
|
@ -218,7 +218,7 @@ void hack::Hook()
|
||||
|
||||
hooks::input.Set(g_IInput);
|
||||
hooks::input.HookMethod(HOOK_ARGS(GetUserCmd));
|
||||
hooks::input.HookMethod(HOOK_ARGS(CreateMoveEarly));
|
||||
hooks::input.HookMethod(HOOK_ARGS(CreateMoveInput));
|
||||
hooks::input.Apply();
|
||||
|
||||
#if ENABLE_VISUALS || ENABLE_TEXTMODE
|
||||
|
@ -12,13 +12,20 @@
|
||||
#include "MiscAimbot.hpp"
|
||||
#include "PlayerTools.hpp"
|
||||
#include "DetourHook.hpp"
|
||||
#include "WeaponData.hpp"
|
||||
#include "MiscTemporary.hpp"
|
||||
|
||||
namespace hacks::tf2::warp
|
||||
{
|
||||
static settings::Boolean enabled{ "warp.enabled", "false" };
|
||||
static settings::Boolean no_movement{ "warp.rapidfire.no-movement", "true" };
|
||||
static settings::Boolean rapidfire{ "warp.rapidfire", "false" };
|
||||
static settings::Boolean wait_full{ "warp.rapidfire.wait-full", "true" };
|
||||
static settings::Button rapidfire_key{ "warp.rapidfire.key", "<null>" };
|
||||
static settings::Float speed{ "warp.speed", "23" };
|
||||
static settings::Boolean draw{ "warp.draw", "false" };
|
||||
static settings::Button warp_key{ "warp.key", "<null>" };
|
||||
static settings::Button charge_key{ "warp.charge-key", "<null>" };
|
||||
static settings::Boolean charge_passively{ "warp.charge-passively", "true" };
|
||||
static settings::Boolean charge_in_jump{ "warp.charge-passively.jump", "true" };
|
||||
static settings::Boolean charge_no_input{ "warp.charge-passively.no-inputs", "false" };
|
||||
@ -34,6 +41,20 @@ static settings::Boolean warp_right{ "warp.on-hit.right", "true" };
|
||||
// Hidden control rvars for communtiy servers
|
||||
static settings::Int maxusrcmdprocessticks("warp.maxusrcmdprocessticks", "24");
|
||||
|
||||
bool in_warp = false;
|
||||
bool in_rapidfire = false;
|
||||
// Should we choke the packet or not? (in rapidfire)
|
||||
bool choke_packet = false;
|
||||
// Were we warping last tick?
|
||||
// why is this needed at all, why do i have to write this janky bs
|
||||
bool was_in_warp = false;
|
||||
|
||||
// How many ticks we have to add to our CreateMove packet
|
||||
int ticks_to_add = 0;
|
||||
|
||||
int GetMaxWarpTicks();
|
||||
void warpLogic();
|
||||
|
||||
// Draw control
|
||||
static settings::Int size{ "warp.bar-size", "100" };
|
||||
static settings::Int bar_x{ "warp.bar-x", "50" };
|
||||
@ -48,10 +69,58 @@ static bool charged = false;
|
||||
static bool should_warp = true;
|
||||
static bool was_hurt = false;
|
||||
|
||||
bool shouldRapidfire()
|
||||
{
|
||||
if (!rapidfire)
|
||||
return false;
|
||||
|
||||
// Already in rapidfire
|
||||
if (in_rapidfire)
|
||||
return false;
|
||||
|
||||
// No key set? Always run. Else check if key is held
|
||||
if (rapidfire_key && !rapidfire_key.isKeyDown())
|
||||
return false;
|
||||
|
||||
// Dead player
|
||||
if (CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer() || CE_BAD(LOCAL_W))
|
||||
return false;
|
||||
|
||||
// Weapon specific ignores, knives, Huntsman, Mediguns, and the grappling hook
|
||||
if (re::C_TFWeaponBase::GetWeaponID(RAW_ENT(LOCAL_W)) == 7 || LOCAL_W->m_iClassID() == CL_CLASS(CTFCompoundBow) || LOCAL_W->m_iClassID() == CL_CLASS(CWeaponMedigun) || LOCAL_W->m_iClassID() == CL_CLASS(CTFGrapplingHook))
|
||||
return false;
|
||||
|
||||
// Ignore throwables/consumables/etc
|
||||
if (g_pLocalPlayer->weapon_mode == weapon_throwable || g_pLocalPlayer->weapon_mode == weapon_consumable || g_pLocalPlayer->weapon_mode == weapon_pda)
|
||||
return false;
|
||||
|
||||
// Unrevved minigun cannot rapidfire
|
||||
if (LOCAL_W->m_iClassID() == CL_CLASS(CTFMinigun) && CE_INT(LOCAL_W, netvar.iWeaponState) != 3 && CE_INT(LOCAL_W, netvar.iWeaponState) != 2)
|
||||
return false;
|
||||
|
||||
// We do not have the amount of ticks needed, don't try it
|
||||
auto weapon_data = GetWeaponData(RAW_ENT(LOCAL_W));
|
||||
if (warp_amount < TIME_TO_TICKS(weapon_data->m_flTimeFireDelay) && (TIME_TO_TICKS(weapon_data->m_flTimeFireDelay) < *maxusrcmdprocessticks - 1 || (wait_full && warp_amount != GetMaxWarpTicks())))
|
||||
return false;
|
||||
|
||||
// Mouse 1 is held, do it.
|
||||
return current_user_cmd && current_user_cmd->buttons & IN_ATTACK;
|
||||
}
|
||||
|
||||
// Should we warp?
|
||||
bool shouldWarp(bool check_amount)
|
||||
{
|
||||
return ((warp_key && warp_key.isKeyDown()) || was_hurt) && (!check_amount || warp_amount);
|
||||
return
|
||||
// Ingame?
|
||||
g_IEngine->IsInGame() &&
|
||||
// Warp key held?
|
||||
(((warp_key && warp_key.isKeyDown())
|
||||
// Hurt warp?
|
||||
|| was_hurt
|
||||
// Rapidfire and trying to attack?
|
||||
|| shouldRapidfire()) &&
|
||||
// Do we have enough to warp?
|
||||
(!check_amount || warp_amount));
|
||||
}
|
||||
|
||||
// How many ticks of excess we have (for decimal speeds)
|
||||
@ -59,6 +128,10 @@ float excess_ticks = 0.0f;
|
||||
int GetWarpAmount(bool finalTick)
|
||||
{
|
||||
int max_extra_ticks = *maxusrcmdprocessticks - 1;
|
||||
|
||||
// Rapidfire ignores speed
|
||||
if (in_rapidfire)
|
||||
return max_extra_ticks;
|
||||
// No limit set
|
||||
if (!*maxusrcmdprocessticks)
|
||||
max_extra_ticks = INT_MAX;
|
||||
@ -106,8 +179,21 @@ void Warp(float accumulated_extra_samples, bool finalTick)
|
||||
if (warp_amnt)
|
||||
{
|
||||
int calls = std::min(warp_ticks, warp_amnt);
|
||||
|
||||
// Starts at 1 for the previous packet we already stored
|
||||
int packets_sent = 1;
|
||||
for (int i = 0; i < calls; i++)
|
||||
{
|
||||
// Choke unless we sent too many already
|
||||
choke_packet = true;
|
||||
|
||||
// We are sending the last one that fits in this clc_move, stop choking and send them all off
|
||||
if (packets_sent == 21 || i == calls - 1)
|
||||
{
|
||||
choke_packet = false;
|
||||
packets_sent = -1;
|
||||
}
|
||||
|
||||
original(accumulated_extra_samples, finalTick);
|
||||
// Only decrease ticks for the final CL_Move tick
|
||||
if (finalTick)
|
||||
@ -115,7 +201,9 @@ void Warp(float accumulated_extra_samples, bool finalTick)
|
||||
warp_amount--;
|
||||
warp_ticks--;
|
||||
}
|
||||
packets_sent++;
|
||||
}
|
||||
ticks_to_add = 0;
|
||||
}
|
||||
cl_move_detour.RestorePatch();
|
||||
|
||||
@ -140,39 +228,6 @@ int GetMaxWarpTicks()
|
||||
return ticks;
|
||||
}
|
||||
|
||||
void SendNetMessage(INetMessage &msg)
|
||||
{
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
// Credits to MrSteyk for this btw
|
||||
if (msg.GetGroup() == 0xA)
|
||||
{
|
||||
// Charge
|
||||
if (should_charge && !charged)
|
||||
{
|
||||
int ticks = GetMaxWarpTicks();
|
||||
auto movemsg = (CLC_Move *) &msg;
|
||||
|
||||
// Just null it :shrug:
|
||||
movemsg->m_nBackupCommands = 0;
|
||||
movemsg->m_nNewCommands = 0;
|
||||
movemsg->m_DataOut.Reset();
|
||||
movemsg->m_DataOut.m_nDataBits = 0;
|
||||
movemsg->m_DataOut.m_nDataBytes = 0;
|
||||
movemsg->m_DataOut.m_iCurBit = 0;
|
||||
|
||||
warp_amount++;
|
||||
if (warp_amount >= ticks)
|
||||
{
|
||||
warp_amount = ticks;
|
||||
charged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
should_charge = false;
|
||||
}
|
||||
|
||||
// Approximate demoknight shield speed at a given tick
|
||||
float approximateSpeedAtTick(int ticks_since_start, float initial_speed, float max_speed)
|
||||
{
|
||||
@ -188,8 +243,10 @@ int approximateTicksForDist(float distance, float initial_speed, int max_ticks)
|
||||
bool has_booties = false;
|
||||
if (CE_GOOD(LOCAL_E) && LOCAL_E->m_bAlivePlayer() && CE_GOOD(LOCAL_W))
|
||||
{
|
||||
// We have a skullcutter, mark as such
|
||||
if (CE_INT(LOCAL_W, netvar.iItemDefinitionIndex) == 172)
|
||||
is_skullcutter = true;
|
||||
// We have the Booties, mark as such
|
||||
if (HasWeapon(LOCAL_E, 405) || HasWeapon(LOCAL_E, 608))
|
||||
has_booties = true;
|
||||
}
|
||||
@ -236,20 +293,187 @@ peek_state current_peek_state = IDLE;
|
||||
// Used to determine when we should start moving back with peek
|
||||
static int charge_at_start = 0;
|
||||
|
||||
// Used to determine when demoknight warp should be over
|
||||
// Used to determine when demoknight warp should be running
|
||||
static bool was_overridden = false;
|
||||
|
||||
// How long since we are in one of the revved states?
|
||||
static int ticks_in_revved = 0;
|
||||
static bool replaced_last_tick = false;
|
||||
|
||||
// Original Player origin and velocity, needed to not break because our engine pred.
|
||||
// We adjust it as the ticks go.
|
||||
static Vector original_origin;
|
||||
static Vector original_velocity;
|
||||
|
||||
// Reset all the revv data
|
||||
void resetRevvstate()
|
||||
{
|
||||
ticks_in_revved = 0;
|
||||
replaced_last_tick = false;
|
||||
}
|
||||
|
||||
void handleMinigun()
|
||||
{
|
||||
// Minigun rapidfire will not work if you hold m1, so we Revv it up with m2 instead.
|
||||
if (rapidfire && warp_amount && CE_GOOD(LOCAL_W) && LOCAL_W->m_iClassID() == CL_CLASS(CTFMinigun))
|
||||
{
|
||||
// Revving
|
||||
if (CE_INT(LOCAL_W, netvar.iWeaponState) == 1)
|
||||
{
|
||||
// Replace m1 with m2 to make rapidfire work
|
||||
if (current_user_cmd->buttons & IN_ATTACK)
|
||||
{
|
||||
current_user_cmd->buttons |= IN_ATTACK2;
|
||||
current_user_cmd->buttons &= ~IN_ATTACK;
|
||||
replaced_last_tick = true;
|
||||
}
|
||||
else
|
||||
replaced_last_tick = false;
|
||||
}
|
||||
// Revved
|
||||
else if (CE_INT(LOCAL_W, netvar.iWeaponState) == 3)
|
||||
{
|
||||
// Once we are revved (via m1) we need to wait 23 ticks before we use rapidfire, else it will fail. Valve jank.
|
||||
if (replaced_last_tick && current_user_cmd->buttons & IN_ATTACK)
|
||||
{
|
||||
if (ticks_in_revved < 23)
|
||||
{
|
||||
current_user_cmd->buttons |= IN_ATTACK2;
|
||||
current_user_cmd->buttons &= ~IN_ATTACK;
|
||||
ticks_in_revved++;
|
||||
}
|
||||
// We waited the 23 ticks, skip over this code now
|
||||
else
|
||||
resetRevvstate();
|
||||
}
|
||||
// No m1 revv/not attacking
|
||||
else
|
||||
resetRevvstate();
|
||||
}
|
||||
// Other state was entered, e.g. we unrevved. Reset aswell
|
||||
else
|
||||
resetRevvstate();
|
||||
}
|
||||
}
|
||||
|
||||
// This is called first, it subsequently calls all the CreateMove functions.
|
||||
void CL_Move_hook(float accumulated_extra_samples, bool bFinalTick)
|
||||
{
|
||||
CL_Move_t original = (CL_Move_t) cl_move_detour.GetOriginalFunc();
|
||||
original(accumulated_extra_samples, bFinalTick);
|
||||
cl_move_detour.RestorePatch();
|
||||
|
||||
// Should we warp?
|
||||
if (shouldWarp(true))
|
||||
{
|
||||
in_warp = true;
|
||||
if (shouldRapidfire())
|
||||
{
|
||||
in_rapidfire = true;
|
||||
// Store original info
|
||||
original_origin = LOCAL_E->m_vecOrigin();
|
||||
velocity::EstimateAbsVelocity(RAW_ENT(LOCAL_E), original_velocity);
|
||||
// Zero out non z movement, it will just get messy else
|
||||
original_velocity.x = 0.0f;
|
||||
original_velocity.y = 0.0f;
|
||||
}
|
||||
|
||||
Warp(accumulated_extra_samples, bFinalTick);
|
||||
if (warp_amount < GetMaxWarpTicks())
|
||||
charged = false;
|
||||
in_warp = false;
|
||||
in_rapidfire = false;
|
||||
was_in_warp = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Run before we call the original, we need to adjust the tickcount on the command
|
||||
// and the global variable so our time based functions are synced properly.
|
||||
void CreateMoveEarly()
|
||||
{
|
||||
if (hacks::tf2::warp::in_rapidfire && current_user_cmd)
|
||||
{
|
||||
if (current_user_cmd)
|
||||
{
|
||||
g_GlobalVars->tickcount++;
|
||||
current_user_cmd->tick_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fix the player origin after engine prediction, as it tries to predict the local
|
||||
// player linearly and just breaks doubletap when falling/moving
|
||||
void CreateMoveFixPrediction()
|
||||
{
|
||||
if (hacks::tf2::warp::in_rapidfire && current_user_cmd)
|
||||
{
|
||||
// Run very simple gravity calculations to ensure we do not miss
|
||||
if (no_movement)
|
||||
{
|
||||
static ConVar *sv_gravity = g_ICvar->FindVar("sv_gravity");
|
||||
Vector gravity{ 0.0f, 0.0f, -sv_gravity->GetFloat() };
|
||||
|
||||
auto mins = RAW_ENT(LOCAL_E)->GetCollideable()->OBBMins();
|
||||
auto maxs = RAW_ENT(LOCAL_E)->GetCollideable()->OBBMaxs();
|
||||
std::pair<Vector, Vector> minmax{ mins, maxs };
|
||||
PredictStep(original_origin, original_velocity, gravity, minmax, 0.0f);
|
||||
// Restore from the engine prediction
|
||||
const_cast<Vector &>(RAW_ENT(LOCAL_E)->GetAbsOrigin()) = original_origin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This calls the warp logic and applies some rapidfire specific logic afterwards
|
||||
// if it applies.
|
||||
void CreateMove()
|
||||
{
|
||||
warpLogic();
|
||||
// Either in rapidfire, or the tick just after. Either way we need to force bSendPackets in some way.
|
||||
bool should_rapidfire = shouldRapidfire();
|
||||
if (in_rapidfire || should_rapidfire || was_in_warp)
|
||||
{
|
||||
// If choke packet is set or we are about to rapidfire, choke the packet (Latter is to ensure it is in the same batch as our rapidfire ones)
|
||||
if (choke_packet || should_rapidfire)
|
||||
*bSendPackets = false;
|
||||
|
||||
// We either just stopped warping, or are on the last tick of rapidfire and want to forcefully release all the usercmds in one packet.
|
||||
else
|
||||
*bSendPackets = true;
|
||||
|
||||
was_in_warp = false;
|
||||
}
|
||||
|
||||
// Attempt to stop fast in place to make movement smoother
|
||||
if (in_rapidfire && no_movement)
|
||||
FastStop();
|
||||
}
|
||||
|
||||
// Does all the logic related to charging and mode/settings specific actions like peek warp
|
||||
// and demoknight mode.
|
||||
void warpLogic()
|
||||
{
|
||||
if (!enabled)
|
||||
return;
|
||||
if (CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer())
|
||||
return;
|
||||
|
||||
// Handle minigun in rapidfire
|
||||
handleMinigun();
|
||||
|
||||
// Charge logic
|
||||
if (!shouldWarp(false))
|
||||
{
|
||||
warp_last_tick = false;
|
||||
current_state = ATTACK;
|
||||
current_peek_state = IDLE;
|
||||
|
||||
// Charge warp
|
||||
if (charge_key && charge_key.isKeyDown())
|
||||
{
|
||||
should_charge = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Vector velocity{};
|
||||
velocity::EstimateAbsVelocity(RAW_ENT(LOCAL_E), velocity);
|
||||
|
||||
@ -261,8 +485,13 @@ void CreateMove()
|
||||
ground_ticks = 0;
|
||||
}
|
||||
|
||||
bool button_block = (current_user_cmd->buttons & (IN_ATTACK | IN_ATTACK2));
|
||||
// Charge on minigun even with m2 held
|
||||
if (LOCAL_E->m_bAlivePlayer() && CE_GOOD(LOCAL_W) && LOCAL_W->m_iClassID() == CL_CLASS(CTFMinigun))
|
||||
button_block = current_user_cmd->buttons & IN_ATTACK;
|
||||
|
||||
// Bunch of checks, if they all pass we are standing still
|
||||
if ((ground_ticks > 1 || charge_in_jump) && (charge_no_input || velocity.IsZero()) && !HasCondition<TFCond_Charging>(LOCAL_E) && !current_user_cmd->forwardmove && !current_user_cmd->sidemove && !current_user_cmd->upmove && !(current_user_cmd->buttons & IN_JUMP) && !(current_user_cmd->buttons & (IN_ATTACK | IN_ATTACK2)))
|
||||
if ((ground_ticks > 1 || charge_in_jump) && (charge_no_input || velocity.IsZero()) && !HasCondition<TFCond_Charging>(LOCAL_E) && !current_user_cmd->forwardmove && !current_user_cmd->sidemove && !current_user_cmd->upmove && !(current_user_cmd->buttons & IN_JUMP) && !button_block)
|
||||
{
|
||||
if (!move_last_tick)
|
||||
should_charge = true;
|
||||
@ -270,14 +499,25 @@ void CreateMove()
|
||||
|
||||
return;
|
||||
}
|
||||
else if (charge_passively && (charge_in_jump || ground_ticks > 1) && !(current_user_cmd->buttons & (IN_ATTACK | IN_ATTACK2)))
|
||||
else if (charge_passively && (charge_in_jump || ground_ticks > 1))
|
||||
{
|
||||
// Use everxy xth tick for charging
|
||||
if (*warp_movement_ratio > 0 && !(tickcount % *warp_movement_ratio))
|
||||
should_charge = true;
|
||||
move_last_tick = true;
|
||||
bool button_block = (current_user_cmd->buttons & (IN_ATTACK | IN_ATTACK2));
|
||||
// Charge on minigun even with m2 held
|
||||
if (LOCAL_W->m_iClassID() == CL_CLASS(CTFMinigun))
|
||||
button_block = current_user_cmd->buttons & IN_ATTACK;
|
||||
|
||||
if (!button_block)
|
||||
{
|
||||
// Use every xth tick for charging
|
||||
if (*warp_movement_ratio > 0 && !(tickcount % *warp_movement_ratio))
|
||||
should_charge = true;
|
||||
move_last_tick = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Ignore the rest if rapidfire is/should be running
|
||||
else if (shouldRapidfire() || in_rapidfire)
|
||||
return;
|
||||
// Warp when hurt
|
||||
else if (was_hurt)
|
||||
{
|
||||
@ -476,6 +716,98 @@ void CreateMove()
|
||||
was_hurt_last_tick = was_hurt;
|
||||
}
|
||||
|
||||
// The second to last thing that gets called, its only job is to write the commands locally and then queue for sending.
|
||||
// We simply make the "backup" command buffer accessible which allows us to send more at once.
|
||||
//
|
||||
// Only called if *bSendPackets is true.
|
||||
void CL_SendMove_hook()
|
||||
{
|
||||
byte data[4000];
|
||||
|
||||
// the +4 one is choked commands
|
||||
int nextcommandnr = NET_INT(g_IBaseClientState, offsets::lastoutgoingcommand()) + NET_INT(g_IBaseClientState, offsets::lastoutgoingcommand() + 4) + 1;
|
||||
|
||||
// send the client update packet
|
||||
|
||||
CLC_Move moveMsg;
|
||||
|
||||
moveMsg.m_DataOut.StartWriting(data, sizeof(data));
|
||||
|
||||
// Determine number of backup commands to send along
|
||||
int cl_cmdbackup = 2;
|
||||
|
||||
// How many real new commands have queued up
|
||||
moveMsg.m_nNewCommands = 1 + NET_INT(g_IBaseClientState, offsets::lastoutgoingcommand() + 4);
|
||||
moveMsg.m_nNewCommands = std::clamp(moveMsg.m_nNewCommands, 0, 15);
|
||||
|
||||
// Excessive commands (Used for longer fakelag, credits to https://www.unknowncheats.me/forum/source-engine/370916-23-tick-guwop-fakelag-break-lag-compensation-running.html)
|
||||
int extra_commands = NET_INT(g_IBaseClientState, offsets::lastoutgoingcommand() + 4) + 1 - moveMsg.m_nNewCommands;
|
||||
cl_cmdbackup = std::max(2, extra_commands);
|
||||
moveMsg.m_nBackupCommands = std::clamp(cl_cmdbackup, 0, 7);
|
||||
|
||||
int numcmds = moveMsg.m_nNewCommands + moveMsg.m_nBackupCommands;
|
||||
|
||||
int from = -1; // first command is deltaed against zeros
|
||||
|
||||
bool bOK = true;
|
||||
|
||||
for (int to = nextcommandnr - numcmds + 1; to <= nextcommandnr; to++)
|
||||
{
|
||||
bool isnewcmd = to >= (nextcommandnr - moveMsg.m_nNewCommands + 1);
|
||||
|
||||
// Call the write to buffer
|
||||
typedef bool (*WriteUsercmdDeltaToBuffer_t)(IBaseClientDLL *, bf_write *, int, int, bool);
|
||||
|
||||
// first valid command number is 1
|
||||
bOK = bOK && vfunc<WriteUsercmdDeltaToBuffer_t>(g_IBaseClient, offsets::PlatformOffset(23, offsets::undefined, offsets::undefined), 0)(g_IBaseClient, &moveMsg.m_DataOut, from, to, isnewcmd);
|
||||
from = to;
|
||||
}
|
||||
|
||||
if (bOK)
|
||||
{
|
||||
// Make fakelag work as we want it to
|
||||
if (extra_commands > 0)
|
||||
((INetChannel *) g_IEngine->GetNetChannelInfo())->m_nChokedPackets -= extra_commands;
|
||||
|
||||
// only write message if all usercmds were written correctly, otherwise parsing would fail
|
||||
((INetChannel *) g_IEngine->GetNetChannelInfo())->SendNetMsg(moveMsg);
|
||||
}
|
||||
}
|
||||
|
||||
// Called after CL_SendMove to transmit the clc_move message. We choke it if we want to charge warp.
|
||||
void SendNetMessage(INetMessage &msg)
|
||||
{
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
// Credits to MrSteyk for this btw
|
||||
if (msg.GetGroup() == 0xA)
|
||||
{
|
||||
// Charge
|
||||
if (should_charge && !charged)
|
||||
{
|
||||
int ticks = GetMaxWarpTicks();
|
||||
auto movemsg = (CLC_Move *) &msg;
|
||||
|
||||
// Just null it :shrug:
|
||||
movemsg->m_nBackupCommands = 0;
|
||||
movemsg->m_nNewCommands = 0;
|
||||
movemsg->m_DataOut.Reset();
|
||||
movemsg->m_DataOut.m_nDataBits = 0;
|
||||
movemsg->m_DataOut.m_nDataBytes = 0;
|
||||
movemsg->m_DataOut.m_iCurBit = 0;
|
||||
|
||||
warp_amount++;
|
||||
if (warp_amount >= ticks)
|
||||
{
|
||||
warp_amount = ticks;
|
||||
charged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
should_charge = false;
|
||||
}
|
||||
|
||||
#if ENABLE_VISUALS
|
||||
void Draw()
|
||||
{
|
||||
@ -563,81 +895,14 @@ void rvarCallback(settings::VariableBase<bool> &, bool)
|
||||
yaw_selections.push_back(90.0f);
|
||||
}
|
||||
|
||||
void CL_SendMove_hook()
|
||||
{
|
||||
byte data[4000];
|
||||
|
||||
// the +4 one is choked commands
|
||||
int nextcommandnr = NET_INT(g_IBaseClientState, offsets::lastoutgoingcommand()) + NET_INT(g_IBaseClientState, offsets::lastoutgoingcommand() + 4) + 1;
|
||||
|
||||
// send the client update packet
|
||||
|
||||
CLC_Move moveMsg;
|
||||
|
||||
moveMsg.m_DataOut.StartWriting(data, sizeof(data));
|
||||
|
||||
// Determine number of backup commands to send along
|
||||
int cl_cmdbackup = 2;
|
||||
|
||||
// How many real new commands have queued up
|
||||
moveMsg.m_nNewCommands = 1 + NET_INT(g_IBaseClientState, offsets::lastoutgoingcommand() + 4);
|
||||
moveMsg.m_nNewCommands = std::clamp(moveMsg.m_nNewCommands, 0, 15);
|
||||
|
||||
// Excessive commands (Used for longer fakelag, credits to https://www.unknowncheats.me/forum/source-engine/370916-23-tick-guwop-fakelag-break-lag-compensation-running.html)
|
||||
int extra_commands = NET_INT(g_IBaseClientState, offsets::lastoutgoingcommand() + 4) + 1 - moveMsg.m_nNewCommands;
|
||||
cl_cmdbackup = std::max(2, extra_commands);
|
||||
moveMsg.m_nBackupCommands = std::clamp(cl_cmdbackup, 0, 7);
|
||||
|
||||
int numcmds = moveMsg.m_nNewCommands + moveMsg.m_nBackupCommands;
|
||||
|
||||
int from = -1; // first command is deltaed against zeros
|
||||
|
||||
bool bOK = true;
|
||||
|
||||
for (int to = nextcommandnr - numcmds + 1; to <= nextcommandnr; to++)
|
||||
{
|
||||
bool isnewcmd = to >= (nextcommandnr - moveMsg.m_nNewCommands + 1);
|
||||
|
||||
// Call the write to buffer
|
||||
typedef bool (*WriteUsercmdDeltaToBuffer_t)(IBaseClientDLL *, bf_write *, int, int, bool);
|
||||
|
||||
// first valid command number is 1
|
||||
bOK = bOK && vfunc<WriteUsercmdDeltaToBuffer_t>(g_IBaseClient, offsets::PlatformOffset(23, offsets::undefined, offsets::undefined), 0)(g_IBaseClient, &moveMsg.m_DataOut, from, to, isnewcmd);
|
||||
from = to;
|
||||
}
|
||||
|
||||
if (bOK)
|
||||
{
|
||||
// Make fakelag work as we want it to
|
||||
if (extra_commands > 0)
|
||||
((INetChannel *) g_IEngine->GetNetChannelInfo())->m_nChokedPackets -= extra_commands;
|
||||
|
||||
// only write message if all usercmds were written correctly, otherwise parsing would fail
|
||||
((INetChannel *) g_IEngine->GetNetChannelInfo())->SendNetMsg(moveMsg);
|
||||
}
|
||||
}
|
||||
|
||||
void CL_Move_hook(float accumulated_extra_samples, bool bFinalTick)
|
||||
{
|
||||
CL_Move_t original = (CL_Move_t) cl_move_detour.GetOriginalFunc();
|
||||
original(accumulated_extra_samples, bFinalTick);
|
||||
cl_move_detour.RestorePatch();
|
||||
|
||||
// Should we warp?
|
||||
if (shouldWarp(true))
|
||||
{
|
||||
Warp(accumulated_extra_samples, bFinalTick);
|
||||
if (warp_amount < GetMaxWarpTicks())
|
||||
charged = false;
|
||||
}
|
||||
}
|
||||
|
||||
static InitRoutine init([]() {
|
||||
static auto cl_move_addr = gSignatures.GetEngineSignature("55 89 E5 57 56 53 81 EC 9C 00 00 00 83 3D ? ? ? ? 01");
|
||||
cl_move_detour.Init(cl_move_addr, (void *) CL_Move_hook);
|
||||
|
||||
EC::Register(EC::LevelShutdown, LevelShutdown, "warp_levelshutdown");
|
||||
EC::Register(EC::CreateMove, CreateMoveFixPrediction, "warp_createmove_fixpred", EC::very_early);
|
||||
EC::Register(EC::CreateMove, CreateMove, "warp_createmove", EC::very_late);
|
||||
EC::Register(EC::CreateMoveEarly, CreateMoveEarly, "warp_createmove_early", EC::very_early);
|
||||
g_IEventManager2->AddListener(&listener, "player_hurt", false);
|
||||
EC::Register(
|
||||
EC::Shutdown,
|
||||
|
@ -1621,6 +1621,30 @@ void AimAtHitbox(CachedEntity *ent, int hitbox, CUserCmd *cmd, bool compensate_p
|
||||
AimAt(g_pLocalPlayer->v_Eye, r, cmd, compensate_punch);
|
||||
}
|
||||
|
||||
// Thanks to "copypaste" on UnknownCheats, this really helped
|
||||
void FastStop()
|
||||
{
|
||||
// Get velocity
|
||||
Vector vel;
|
||||
velocity::EstimateAbsVelocity(RAW_ENT(LOCAL_E), vel);
|
||||
|
||||
Vector direction;
|
||||
VectorAngles(vel, direction);
|
||||
float speed = vel.Length();
|
||||
|
||||
// Prevent overshooting
|
||||
speed *= 0.5f;
|
||||
|
||||
direction.y = current_user_cmd->viewangles.y - direction.y;
|
||||
|
||||
Vector negated_direction;
|
||||
AngleVectors2(VectorToQAngle(direction), &negated_direction);
|
||||
negated_direction *= -speed;
|
||||
|
||||
current_user_cmd->forwardmove = negated_direction.x;
|
||||
current_user_cmd->sidemove = negated_direction.y;
|
||||
}
|
||||
|
||||
bool IsEntityVisiblePenetration(CachedEntity *entity, int hb)
|
||||
{
|
||||
trace_t trace_visible;
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include "HookedMethods.hpp"
|
||||
#include "nospread.hpp"
|
||||
#include "Warp.hpp"
|
||||
|
||||
static settings::Boolean minigun_jump{ "misc.minigun-jump-tf2c", "false" };
|
||||
static settings::Boolean roll_speedhack{ "misc.roll-speedhack", "false" };
|
||||
@ -127,6 +128,7 @@ DEFINE_HOOKED_METHOD(CreateMove, bool, void *this_, float input_sample_time, CUs
|
||||
Vector vsilent, ang;
|
||||
|
||||
current_user_cmd = cmd;
|
||||
EC::run(EC::CreateMoveEarly);
|
||||
IF_GAME(IsTF2C())
|
||||
{
|
||||
if (CE_GOOD(LOCAL_W) && minigun_jump && LOCAL_W->m_iClassID() == CL_CLASS(CTFMinigun))
|
||||
@ -430,11 +432,11 @@ void WriteCmd(IInput *input, CUserCmd *cmd, int sequence_nr)
|
||||
}
|
||||
|
||||
// This gets called before the other CreateMove, but since we run original first in here all the stuff gets called after normal CreateMove is done
|
||||
DEFINE_HOOKED_METHOD(CreateMoveEarly, void, IInput *this_, int sequence_nr, float input_sample_time, bool arg3)
|
||||
DEFINE_HOOKED_METHOD(CreateMoveInput, void, IInput *this_, int sequence_nr, float input_sample_time, bool arg3)
|
||||
{
|
||||
bSendPackets = reinterpret_cast<bool *>((uintptr_t) __builtin_frame_address(1) - 8);
|
||||
// Call original function, includes Normal CreateMove
|
||||
original::CreateMoveEarly(this_, sequence_nr, input_sample_time, arg3);
|
||||
original::CreateMoveInput(this_, sequence_nr, input_sample_time, arg3);
|
||||
|
||||
CUserCmd *cmd = nullptr;
|
||||
if (this_ && GetCmds(this_) && sequence_nr > 0)
|
||||
@ -457,10 +459,10 @@ DEFINE_HOOKED_METHOD(CreateMoveEarly, void, IInput *this_, int sequence_nr, floa
|
||||
return;
|
||||
}
|
||||
|
||||
PROF_SECTION(CreateMoveEarly);
|
||||
PROF_SECTION(CreateMoveInput);
|
||||
|
||||
// Run EC
|
||||
EC::run(EC::CreateMoveEarly);
|
||||
EC::run(EC::CreateMoveLate);
|
||||
|
||||
// Write the usercmd
|
||||
WriteCmd(this_, current_late_user_cmd, sequence_nr);
|
||||
|
@ -56,7 +56,7 @@ Vector Predict(Vector &pos, Vector &vel, Vector acceleration, std::optional<floa
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector PredictStep(Vector pos, Vector &vel, Vector acceleration, std::pair<Vector, Vector> &minmax, float time, float steplength = g_GlobalVars->interval_per_tick, bool vischeck = true, std::optional<float> grounddistance = std::nullopt)
|
||||
Vector PredictStep(Vector pos, Vector &vel, Vector acceleration, std::pair<Vector, Vector> &minmax, float time, float steplength, bool vischeck, std::optional<float> grounddistance)
|
||||
{
|
||||
PROF_SECTION(PredictNew)
|
||||
Vector result = pos;
|
||||
|
Reference in New Issue
Block a user