Rewrite Backtrack
I mean when i already rewrite crithack why not Backtrack too
This commit is contained in:
parent
7516c62b3c
commit
9dcfc6b8cf
@ -113,14 +113,15 @@
|
||||
</Box>
|
||||
<Box padding="12 6 6 6" width="content" height="content" name="Backtracking" x="340" y="125">
|
||||
<List width="150">
|
||||
<AutoVariable width="fill" target="backtrack.enable" label="Enable Backtracking" tooltip="Allow hitting enemies in the past."/>
|
||||
<AutoVariable width="fill" target="aimbot.backtrack" label="Aim at Backtracking"/>
|
||||
<AutoVariable width="fill" target="backtrack.enabled" label="Enable Backtracking" tooltip="Allow hitting enemies in the past."/>
|
||||
<AutoVariable width="fill" target="aimbot.backtrack" label="Aim at Backtracking" tooltip="Aim at Backtrack ticks"/>
|
||||
<AutoVariable width="fill" target="misc.ping-reducer.enable" label="Ping Reducer"/>
|
||||
<AutoVariable width="fill" target="misc.ping-reducer.target" label="Target Ping"/>
|
||||
<AutoVariable width="fill" target="backtrack.latency" label="Fake Latency" min="0" max="1000" step="25"/>
|
||||
<AutoVariable width="fill" target="backtrack.draw" label="Draw Backtrack"/>
|
||||
<AutoVariable width="fill" target="backtrack.draw-skeleton" label="Draw Skeleton"/>
|
||||
<AutoVariable width="fill" target="backtrack.chams_glow" label="Chams/Glow Support"/>
|
||||
<AutoVariable width="fill" target="backtrack.draw" label="Draw Backtrack" tooltip="Draw ticks on screen"/>
|
||||
<AutoVariable width="fill" target="backtrack.chams" label="Backtrack Chams" tooltip="Draw Chams for the ticks"/>
|
||||
<AutoVariable width="fill" target="backtrack.chams.ticks" label="Chams Amount" tooltip="How many ticks to draw (WARNING, this can cause lag)"/>
|
||||
<AutoVariable width="fill" target="backtrack.chams.team-color" label="Teamcolor Chams" tooltip="Use Teamcolor instead of grey chams"/>
|
||||
<LabeledObject width="fill" label="Backtrack Slots">
|
||||
<Select target="backtrack.slots">
|
||||
<Option name="All" value="0"/>
|
||||
|
@ -14,7 +14,6 @@ class IClientEntity;
|
||||
|
||||
namespace hacks::shared::aimbot
|
||||
{
|
||||
bool IsBacktracking();
|
||||
extern settings::Boolean ignore_cloak;
|
||||
|
||||
// Used to store aimbot data to prevent calculating it again
|
||||
@ -30,7 +29,7 @@ struct AimbotCalculatedData_s
|
||||
|
||||
// Functions used to calculate aimbot data, and if already calculated use it
|
||||
const Vector &PredictEntity(CachedEntity *entity);
|
||||
bool VischeckPredictedEntity(CachedEntity *entity, bool Backtracking);
|
||||
bool VischeckPredictedEntity(CachedEntity *entity);
|
||||
bool BacktrackVisCheck(CachedEntity *entity);
|
||||
|
||||
// Variable used to tell when the aimbot has found a target
|
||||
@ -45,7 +44,7 @@ void Reset();
|
||||
// Stuff to make storing functions easy
|
||||
CachedEntity *CurrentTarget();
|
||||
bool ShouldAim();
|
||||
CachedEntity *RetrieveBestTarget(bool aimkey_state, bool Backtracking = false);
|
||||
CachedEntity *RetrieveBestTarget(bool aimkey_state);
|
||||
bool IsTargetStateGood(CachedEntity *entity);
|
||||
void Aim(CachedEntity *entity);
|
||||
void DoAutoshoot(CachedEntity *target = nullptr);
|
||||
|
@ -1,63 +1,105 @@
|
||||
/*
|
||||
* Backtrack.hpp
|
||||
*
|
||||
* Created on: May 15, 2018
|
||||
* Author: bencat07
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "common.hpp"
|
||||
#include <boost/circular_buffer.hpp>
|
||||
|
||||
namespace hacks::shared::backtrack
|
||||
namespace hacks::tf2::backtrack
|
||||
{
|
||||
extern settings::Int latency;
|
||||
extern settings::Boolean enable;
|
||||
class CIncomingSequence
|
||||
{
|
||||
public:
|
||||
int inreliablestate;
|
||||
int sequencenr;
|
||||
float curtime;
|
||||
|
||||
struct hitboxData
|
||||
{
|
||||
Vector center{ 0.0f, 0.0f, 0.0f };
|
||||
Vector min{ 0.0f, 0.0f, 0.0f };
|
||||
Vector max{ 0.0f, 0.0f, 0.0f };
|
||||
};
|
||||
|
||||
struct BacktrackData
|
||||
{
|
||||
int tickcount{ 0 };
|
||||
std::array<hitboxData, 18> hitboxes;
|
||||
hitboxData collidable{};
|
||||
float viewangles{ 0.0f };
|
||||
float simtime{ 0.0f };
|
||||
Vector entorigin{ 0.0f, 0.0f, 0.0f };
|
||||
int index{ 0 };
|
||||
matrix3x4_t bones[128]{};
|
||||
};
|
||||
void Init();
|
||||
void AddLatencyToNetchan(INetChannel *);
|
||||
void UpdateIncomingSequences();
|
||||
extern int lastincomingsequencenumber;
|
||||
extern int BestTick;
|
||||
extern int iBestTarget;
|
||||
struct CIncomingSequence
|
||||
{
|
||||
CIncomingSequence(int instate, int seqnr, float time)
|
||||
{
|
||||
inreliablestate = instate;
|
||||
sequencenr = seqnr;
|
||||
curtime = time;
|
||||
}
|
||||
int inreliablestate;
|
||||
int sequencenr;
|
||||
float curtime;
|
||||
};
|
||||
typedef boost::circular_buffer_space_optimized<CIncomingSequence> circular_buf;
|
||||
extern circular_buf sequences;
|
||||
extern BacktrackData headPositions[PLAYER_ARRAY_SIZE][66];
|
||||
|
||||
extern bool isBacktrackEnabled;
|
||||
extern bool Vischeck_Success;
|
||||
float getLatency();
|
||||
float getRealLatency();
|
||||
int getTicks();
|
||||
bool ValidTick(BacktrackData &i, CachedEntity *ent);
|
||||
} // namespace hacks::shared::backtrack
|
||||
class hitboxData
|
||||
{
|
||||
public:
|
||||
Vector center{ 0.0f, 0.0f, 0.0f };
|
||||
Vector min{ 0.0f, 0.0f, 0.0f };
|
||||
Vector max{ 0.0f, 0.0f, 0.0f };
|
||||
};
|
||||
|
||||
class BacktrackData
|
||||
{
|
||||
public:
|
||||
int tickcount{};
|
||||
|
||||
std::array<hitboxData, 18> hitboxes{};
|
||||
Vector m_vecOrigin{};
|
||||
Vector m_vecAngles{};
|
||||
|
||||
Vector m_vecMins{};
|
||||
Vector m_vecMaxs{};
|
||||
|
||||
float m_flSimulationTime{};
|
||||
bool has_updated{};
|
||||
|
||||
matrix3x4_t bones[128]{};
|
||||
};
|
||||
|
||||
// Let's try OOP for once
|
||||
class Backtrack
|
||||
{
|
||||
// Internal rvars
|
||||
settings::Boolean enabled{ "backtrack.enabled", "false" };
|
||||
settings::Boolean draw{ "backtrack.draw", "false" };
|
||||
|
||||
std::vector<CIncomingSequence> sequences;
|
||||
int current_tickcount;
|
||||
std::array<std::unique_ptr<std::array<BacktrackData, 67>>, PLAYER_ARRAY_SIZE> backtrack_data;
|
||||
int lastincomingsequence{ 0 };
|
||||
// Used to make transition smooth(er)
|
||||
float latency_rampup = 0.0f;
|
||||
|
||||
// Which data to apply in the late CreateMove
|
||||
CachedEntity *bt_ent;
|
||||
std::optional<BacktrackData> bt_data;
|
||||
|
||||
bool isEnabled();
|
||||
float getLatency();
|
||||
int getTicks();
|
||||
bool getBestInternalTick(CachedEntity *, BacktrackData &, std::optional<BacktrackData> &);
|
||||
void ApplyBacktrack();
|
||||
|
||||
// Stuff that has to be accessible from outside, mostly functions
|
||||
public:
|
||||
settings::Float latency{ "backtrack.latency", "0" };
|
||||
settings::Int bt_slots{ "backtrack.slots", "0" };
|
||||
settings::Boolean chams{ "backtrack.chams", "false" };
|
||||
settings::Int chams_ticks{ "backtrack.chams.ticks", "1" };
|
||||
settings::Boolean team_color{ "backtrack.chams.team-color", "true" };
|
||||
|
||||
// Check if backtrack is enabled
|
||||
bool isBacktrackEnabled;
|
||||
// Event callbacks
|
||||
void CreateMove();
|
||||
void CreateMoveLate();
|
||||
#if ENABLE_VISUALS
|
||||
void Draw();
|
||||
#endif
|
||||
void LevelShutdown();
|
||||
|
||||
void adjustPing(INetChannel *);
|
||||
void updateDatagram();
|
||||
void resetData(int);
|
||||
bool isGoodTick(BacktrackData &);
|
||||
bool defaultTickFilter(CachedEntity *, BacktrackData);
|
||||
bool defaultEntFilter(CachedEntity *);
|
||||
|
||||
// Various functions for getting backtrack ticks
|
||||
std::vector<BacktrackData> getGoodTicks(int);
|
||||
std::optional<BacktrackData> getBestTick(CachedEntity *, std::function<bool(CachedEntity *, BacktrackData &, std::optional<BacktrackData> &)>);
|
||||
std::optional<BacktrackData> getClosestEntTick(CachedEntity *, Vector, std::function<bool(CachedEntity *, BacktrackData)>);
|
||||
std::optional<std::pair<CachedEntity *, BacktrackData>> getClosestTick(Vector, std::function<bool(CachedEntity *)>, std::function<bool(CachedEntity *, BacktrackData)>);
|
||||
|
||||
void SetBacktrackData(CachedEntity *ent, BacktrackData);
|
||||
};
|
||||
} // namespace hacks::tf2::backtrack
|
||||
extern hacks::tf2::backtrack::Backtrack g_IBacktrack;
|
||||
|
@ -8,15 +8,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.hpp"
|
||||
#include "Backtrack.hpp"
|
||||
|
||||
namespace hacks::shared::triggerbot
|
||||
{
|
||||
|
||||
void CreateMove();
|
||||
CachedEntity *FindEntInSight(float range);
|
||||
bool ShouldShoot();
|
||||
bool IsTargetStateGood(CachedEntity *entity, bool backtrack = false);
|
||||
CachedEntity *FindEntInSight(float range);
|
||||
bool IsTargetStateGood(CachedEntity *entity, std::optional<hacks::tf2::backtrack::BacktrackData> bt_data = {});
|
||||
CachedEntity *FindEntInSight(float range, bool no_players = false);
|
||||
bool HeadPreferable(CachedEntity *target);
|
||||
bool UpdateAimkey();
|
||||
float EffectiveTargetingRange();
|
||||
|
@ -556,10 +556,6 @@ void fixBucket(IClientEntity *weapon, CUserCmd *cmd)
|
||||
INetChannel *ch = (INetChannel *) g_IEngine->GetNetChannelInfo();
|
||||
if (!ch)
|
||||
return;
|
||||
auto addr = ch->GetRemoteAddress();
|
||||
// Local server needs no fixing
|
||||
if (addr.type == NA_LOOPBACK)
|
||||
return;
|
||||
|
||||
static int last_weapon;
|
||||
// This tracks only when bucket is updated
|
||||
|
@ -114,9 +114,14 @@ int GetSentry()
|
||||
}
|
||||
|
||||
settings::Boolean ignore_cloak{ "aimbot.target.ignore-cloaked-spies", "1" };
|
||||
// Projectile info
|
||||
bool projectile_mode{ false };
|
||||
float cur_proj_speed{ 0.0f };
|
||||
float cur_proj_grav{ 0.0f };
|
||||
|
||||
bool shouldBacktrack()
|
||||
{
|
||||
return *enable && (*backtrackAimbot || force_backtrack_aimbot) && hacks::shared::backtrack::isBacktrackEnabled;
|
||||
return *enable && !projectile_mode && (*backtrackAimbot || force_backtrack_aimbot) && g_IBacktrack.isBacktrackEnabled;
|
||||
}
|
||||
|
||||
bool IsBacktracking()
|
||||
@ -170,15 +175,10 @@ int target_eid{ 0 };
|
||||
CachedEntity *target = 0;
|
||||
CachedEntity *target_last = 0;
|
||||
bool foundTarget = false;
|
||||
// Projectile info
|
||||
bool projectile_mode{ false };
|
||||
float cur_proj_speed{ 0.0f };
|
||||
float cur_proj_grav{ 0.0f };
|
||||
|
||||
// If slow aimbot allows autoshoot
|
||||
bool slow_can_shoot = false;
|
||||
bool projectileAimbotRequired;
|
||||
// Track Backtrack tick for Entity
|
||||
static std::pair<int, int> good_tick = { -1, -1 };
|
||||
|
||||
// This array will store calculated projectile/hitscan predictions
|
||||
// for current frame, to avoid performing them again
|
||||
@ -200,6 +200,7 @@ static void CreateMove()
|
||||
// aimkey would break so we save it to a var to use it twice
|
||||
bool aimkey_status = UpdateAimkey();
|
||||
// check if we need to run projectile Aimbot code
|
||||
projectile_mode = false;
|
||||
projectileAimbotRequired = false;
|
||||
if (projectile_aimbot && (g_pLocalPlayer->weapon_mode == weapon_projectile || g_pLocalPlayer->weapon_mode == weapon_throwable))
|
||||
projectileAimbotRequired = true;
|
||||
@ -464,9 +465,8 @@ bool ShouldAim()
|
||||
}
|
||||
|
||||
// Function to find a suitable target
|
||||
CachedEntity *RetrieveBestTarget(bool aimkey_state, bool Backtracking)
|
||||
CachedEntity *RetrieveBestTarget(bool aimkey_state)
|
||||
{
|
||||
|
||||
// If we have a previously chosen target, target lock is on, and the aimkey
|
||||
// is allowed, then attemt to keep the previous target
|
||||
if (target_lock && foundTarget && aimkey_state)
|
||||
@ -499,7 +499,6 @@ CachedEntity *RetrieveBestTarget(bool aimkey_state, bool Backtracking)
|
||||
// Check whether the current ent is good enough to target
|
||||
if (IsTargetStateGood(ent))
|
||||
{
|
||||
|
||||
// Distance Priority, Uses this is melee is used
|
||||
if (GetWeaponMode() == weaponmode::weapon_melee || (int) priority_mode == 2)
|
||||
scr = 4096.0f - calculated_data_array[i].aim_position.DistTo(g_pLocalPlayer->v_Eye);
|
||||
@ -589,31 +588,17 @@ bool IsTargetStateGood(CachedEntity *entity)
|
||||
}
|
||||
else
|
||||
{
|
||||
namespace bt = hacks::shared::backtrack;
|
||||
for (int i = 0; i < 66; i++)
|
||||
{
|
||||
if (!bt::ValidTick(bt::headPositions[entity->m_IDX][i], entity))
|
||||
continue;
|
||||
Vector bbox_min = bt::headPositions[entity->m_IDX][i].collidable.min;
|
||||
Vector bbox_max = bt::headPositions[entity->m_IDX][i].collidable.max;
|
||||
for (int j = 17; j >= 0; j--)
|
||||
{
|
||||
Vector aim_at = bt::headPositions[entity->m_IDX][i].hitboxes[j].center;
|
||||
Vector newangle = GetAimAtAngles(g_pLocalPlayer->v_Eye, aim_at);
|
||||
Vector new_vec = GetForwardVector(g_pLocalPlayer->v_Eye, newangle, swingrange);
|
||||
if (new_vec.x > bbox_min.x && new_vec.x < bbox_max.x && new_vec.y > bbox_min.y && new_vec.y < bbox_max.y && new_vec.z > bbox_min.z && new_vec.z < bbox_max.z)
|
||||
{
|
||||
/*auto it = bt::headPositions[entity->m_IDX][i];
|
||||
current_user_cmd->tick_count = it.tickcount;
|
||||
Vector &angles = NET_VECTOR(RAW_ENT(entity), netvar.m_angEyeAngles);
|
||||
float &simtime = NET_FLOAT(RAW_ENT(entity), netvar.m_flSimulationTime);
|
||||
angles.y = it.viewangles;
|
||||
simtime = it.simtime;*/
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
// This does vischecks and everything
|
||||
auto data = g_IBacktrack.getClosestEntTick(entity, LOCAL_E->m_vecOrigin(), std::bind(&hacks::tf2::backtrack::Backtrack::defaultTickFilter, &g_IBacktrack, std::placeholders::_1, std::placeholders::_2));
|
||||
// No data found
|
||||
if (!data)
|
||||
return false;
|
||||
// Is within range?
|
||||
Vector melee_vec = data->m_vecOrigin;
|
||||
melee_vec.z = g_pLocalPlayer->v_Eye.z;
|
||||
// Out of range
|
||||
if (melee_vec.DistTo(g_pLocalPlayer->v_Eye) >= swingrange)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -711,7 +696,7 @@ bool IsTargetStateGood(CachedEntity *entity)
|
||||
cd.hitbox = hitbox;
|
||||
|
||||
// Vis check + fov check
|
||||
if (!VischeckPredictedEntity(entity, IsBacktracking() && !projectile_mode))
|
||||
if (!VischeckPredictedEntity(entity))
|
||||
return false;
|
||||
if (LOCAL_W->m_iClassID() == CL_CLASS(CTFLaserPointer))
|
||||
{
|
||||
@ -721,7 +706,15 @@ bool IsTargetStateGood(CachedEntity *entity)
|
||||
Vector pos = GetBuildingPosition(ENTITY(sentry));
|
||||
if (hitbox == -1 || !entity->hitboxes.GetHitbox(cd.hitbox))
|
||||
return false;
|
||||
if (!IsVectorVisible(pos, entity->hitboxes.GetHitbox(cd.hitbox)->center, false, ENTITY(sentry)))
|
||||
if (shouldBacktrack())
|
||||
{
|
||||
// This does vischecks and everything
|
||||
auto data = g_IBacktrack.getClosestEntTick(entity, LOCAL_E->m_vecOrigin(), std::bind(&hacks::tf2::backtrack::Backtrack::defaultTickFilter, &g_IBacktrack, std::placeholders::_1, std::placeholders::_2));
|
||||
// No data found
|
||||
if (!data)
|
||||
return false;
|
||||
}
|
||||
else if (!IsVectorVisible(pos, entity->hitboxes.GetHitbox(cd.hitbox)->center, false, ENTITY(sentry)))
|
||||
return false;
|
||||
}
|
||||
if (*fov > 0.0f && cd.fov > *fov)
|
||||
@ -771,7 +764,7 @@ bool IsTargetStateGood(CachedEntity *entity)
|
||||
AimbotCalculatedData_s &cd = calculated_data_array[entity->m_IDX];
|
||||
|
||||
// Vis and fov checks
|
||||
if (!VischeckPredictedEntity(entity, false))
|
||||
if (!VischeckPredictedEntity(entity))
|
||||
return false;
|
||||
if (LOCAL_W->m_iClassID() == CL_CLASS(CTFLaserPointer))
|
||||
{
|
||||
@ -823,7 +816,7 @@ bool IsTargetStateGood(CachedEntity *entity)
|
||||
AimbotCalculatedData_s &cd = calculated_data_array[entity->m_IDX];
|
||||
|
||||
// Vis and fov check
|
||||
if (!VischeckPredictedEntity(entity, false))
|
||||
if (!VischeckPredictedEntity(entity))
|
||||
return false;
|
||||
if (LOCAL_W->m_iClassID() == CL_CLASS(CTFLaserPointer))
|
||||
{
|
||||
@ -831,7 +824,15 @@ bool IsTargetStateGood(CachedEntity *entity)
|
||||
if (sentry == -1)
|
||||
return false;
|
||||
Vector pos = GetBuildingPosition(ENTITY(sentry));
|
||||
if (!IsVectorVisible(pos, entity->m_vecOrigin(), false))
|
||||
if (shouldBacktrack())
|
||||
{
|
||||
// This does vischecks and everything
|
||||
auto data = g_IBacktrack.getClosestEntTick(entity, LOCAL_E->m_vecOrigin(), std::bind(&hacks::tf2::backtrack::Backtrack::defaultTickFilter, &g_IBacktrack, std::placeholders::_1, std::placeholders::_2));
|
||||
// No data found
|
||||
if (!data)
|
||||
return false;
|
||||
}
|
||||
else if (!IsVectorVisible(pos, entity->m_vecOrigin(), false))
|
||||
return false;
|
||||
}
|
||||
if (*fov > 0.0f && cd.fov > *fov)
|
||||
@ -849,7 +850,6 @@ bool IsTargetStateGood(CachedEntity *entity)
|
||||
// A function to aim at a specific entitiy
|
||||
void Aim(CachedEntity *entity)
|
||||
{
|
||||
namespace bt = hacks::shared::backtrack;
|
||||
if (*miss_chance > 0 && UniformRandomInt(0, 99) < *miss_chance)
|
||||
return;
|
||||
|
||||
@ -872,10 +872,12 @@ void Aim(CachedEntity *entity)
|
||||
auto hitboxcenter = hb->center;
|
||||
if (shouldBacktrack() && entity->m_Type() == ENTITY_PLAYER)
|
||||
{
|
||||
const auto &bt_hb = bt::headPositions[entity->m_IDX][good_tick.first].hitboxes[cd.hitbox];
|
||||
hitboxcenter = bt_hb.center;
|
||||
hitboxmin = bt_hb.min;
|
||||
hitboxmax = bt_hb.max;
|
||||
// This does vischecks and everything
|
||||
auto data = g_IBacktrack.getClosestEntTick(entity, LOCAL_E->m_vecOrigin(), std::bind(&hacks::tf2::backtrack::Backtrack::defaultTickFilter, &g_IBacktrack, std::placeholders::_1, std::placeholders::_2));
|
||||
auto bt_hb = data->hitboxes.at(cd.hitbox);
|
||||
hitboxcenter = bt_hb.center;
|
||||
hitboxmin = bt_hb.min;
|
||||
hitboxmax = bt_hb.max;
|
||||
}
|
||||
// get positions
|
||||
minx = hitboxmin.x;
|
||||
@ -898,7 +900,7 @@ void Aim(CachedEntity *entity)
|
||||
// Create Vectors
|
||||
const Vector positions[13] = { { minx, centery, minz }, { maxx, centery, minz }, { minx, centery, maxz }, { maxx, centery, maxz }, { centerx, miny, minz }, { centerx, maxy, minz }, { centerx, miny, maxz }, { centerx, maxy, maxz }, { minx, miny, centerz }, { maxx, maxy, centerz }, { minx, miny, centerz }, { maxx, maxy, centerz }, hitboxcenter };
|
||||
for (int i = 0; i < 13; ++i)
|
||||
if (IsVectorVisible(g_pLocalPlayer->v_Eye, positions[i]))
|
||||
if (IsEntityVectorVisible(entity, positions[i]))
|
||||
{
|
||||
tr = (positions[i] - g_pLocalPlayer->v_Eye);
|
||||
break;
|
||||
@ -919,8 +921,15 @@ void Aim(CachedEntity *entity)
|
||||
if (silent && !slow_aim)
|
||||
g_pLocalPlayer->bUseSilentAngles = true;
|
||||
// Set tick count to target's (backtrack messes with this)
|
||||
if (!bt::isBacktrackEnabled && nolerp && entity->m_IDX <= g_IEngine->GetMaxClients())
|
||||
if (!shouldBacktrack() && nolerp && entity->m_IDX <= g_IEngine->GetMaxClients())
|
||||
current_user_cmd->tick_count = TIME_TO_TICKS(CE_FLOAT(entity, netvar.m_flSimulationTime));
|
||||
// Set Backtrack data
|
||||
if (shouldBacktrack() && entity->m_Type() == ENTITY_PLAYER)
|
||||
{
|
||||
auto data = g_IBacktrack.getClosestEntTick(entity, LOCAL_E->m_vecOrigin(), std::bind(&hacks::tf2::backtrack::Backtrack::defaultTickFilter, &g_IBacktrack, std::placeholders::_1, std::placeholders::_2));
|
||||
if (data)
|
||||
g_IBacktrack.SetBacktrackData(entity, *data);
|
||||
}
|
||||
// Finish function
|
||||
return;
|
||||
}
|
||||
@ -1016,7 +1025,7 @@ void DoAutoshoot(CachedEntity *target_entity)
|
||||
if (attack)
|
||||
{
|
||||
// TO DO: Sending both reload and attack will activate the hitmans heatmaker ability
|
||||
// Don't activate it only on first kill (or somehow activate it before shoot)
|
||||
// Don't activate it only on first kill (or somehow activate it before a shot)
|
||||
current_user_cmd->buttons |= IN_ATTACK | (*autoreload && CarryingHeatmaker() ? IN_RELOAD : 0);
|
||||
if (target_entity)
|
||||
{
|
||||
@ -1037,9 +1046,8 @@ const Vector &PredictEntity(CachedEntity *entity)
|
||||
if (cd.predict_tick == tickcount)
|
||||
return result;
|
||||
|
||||
if (!shouldBacktrack() || projectile_mode || entity->m_Type() != ENTITY_PLAYER)
|
||||
if (!shouldBacktrack() || entity->m_Type() != ENTITY_PLAYER)
|
||||
{
|
||||
|
||||
// Players
|
||||
if ((entity->m_Type() == ENTITY_PLAYER))
|
||||
{
|
||||
@ -1082,45 +1090,12 @@ const Vector &PredictEntity(CachedEntity *entity)
|
||||
}
|
||||
else
|
||||
{
|
||||
// Players only
|
||||
if ((entity->m_Type() == ENTITY_PLAYER))
|
||||
auto data = g_IBacktrack.getClosestEntTick(entity, LOCAL_E->m_vecOrigin(), std::bind(&hacks::tf2::backtrack::Backtrack::defaultTickFilter, &g_IBacktrack, std::placeholders::_1, std::placeholders::_2));
|
||||
if (data)
|
||||
{
|
||||
if (GetWeaponMode() != weapon_melee)
|
||||
{
|
||||
namespace bt = hacks::shared::backtrack;
|
||||
auto hb = bt::headPositions[entity->m_IDX][good_tick.first];
|
||||
cd.predict_tick = tickcount;
|
||||
result = hb.hitboxes[cd.hitbox].center;
|
||||
cd.fov = GetFov(g_pLocalPlayer->v_OrigViewangles, g_pLocalPlayer->v_Eye, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
namespace bt = hacks::shared::backtrack;
|
||||
for (int i = 0; i < 66; i++)
|
||||
{
|
||||
if (!bt::ValidTick(bt::headPositions[entity->m_IDX][i], entity))
|
||||
continue;
|
||||
Vector bbox_min = bt::headPositions[entity->m_IDX][i].collidable.min;
|
||||
Vector bbox_max = bt::headPositions[entity->m_IDX][i].collidable.max;
|
||||
for (int j = 17; j >= 0; j--)
|
||||
{
|
||||
float swingrange = re::C_TFWeaponBaseMelee::GetSwingRange(RAW_ENT(LOCAL_W));
|
||||
Vector aim_at = bt::headPositions[entity->m_IDX][i].hitboxes[j].center;
|
||||
Vector newangle = GetAimAtAngles(g_pLocalPlayer->v_Eye, aim_at);
|
||||
Vector new_vec = GetForwardVector(g_pLocalPlayer->v_Eye, newangle, swingrange);
|
||||
if (new_vec.x > bbox_min.x && new_vec.x < bbox_max.x && new_vec.y > bbox_min.y && new_vec.y < bbox_max.y && new_vec.z > bbox_min.z && new_vec.z < bbox_max.z)
|
||||
{
|
||||
result = new_vec;
|
||||
auto it = bt::headPositions[entity->m_IDX][i];
|
||||
current_user_cmd->tick_count = it.tickcount;
|
||||
Vector &angles = NET_VECTOR(RAW_ENT(entity), netvar.m_angEyeAngles);
|
||||
float &simtime = NET_FLOAT(RAW_ENT(entity), netvar.m_flSimulationTime);
|
||||
angles.y = it.viewangles;
|
||||
simtime = it.simtime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
result = data->hitboxes.at(cd.hitbox).center;
|
||||
cd.predict_tick = tickcount;
|
||||
cd.fov = GetFov(g_pLocalPlayer->v_OrigViewangles, g_pLocalPlayer->v_Eye, result);
|
||||
}
|
||||
}
|
||||
// Return the found vector
|
||||
@ -1154,7 +1129,6 @@ int BestHitbox(CachedEntity *target)
|
||||
float begincharge = CE_FLOAT(g_pLocalPlayer->weapon(), netvar.flChargeBeginTime);
|
||||
float charge = g_GlobalVars->curtime - begincharge;
|
||||
int damage = std::floor(50.0f + 70.0f * fminf(1.0f, charge));
|
||||
int charge_damage = std::floor(50.0f + 70.0f * fminf(1.0f, charge)) * 3.0f;
|
||||
if (damage >= target->m_iHealth())
|
||||
preferred = hitbox_t::spine_3;
|
||||
else
|
||||
@ -1239,34 +1213,6 @@ int BestHitbox(CachedEntity *target)
|
||||
{
|
||||
headonly = true;
|
||||
}
|
||||
|
||||
if (IsBacktracking() && !projectile_mode)
|
||||
{
|
||||
namespace bt = hacks::shared::backtrack;
|
||||
good_tick = { -1, -1 };
|
||||
auto ticks = bt::headPositions[target->m_IDX];
|
||||
for (int i = 0; i < 66; i++)
|
||||
{
|
||||
if (!ticks[i].tickcount)
|
||||
continue;
|
||||
if (!bt::ValidTick(ticks[i], target))
|
||||
continue;
|
||||
if (*backtrackVischeckAll)
|
||||
for (int j = 0; j < 18; j++)
|
||||
{
|
||||
if (IsEntityVectorVisible(target, ticks[i].hitboxes[j].center))
|
||||
{
|
||||
good_tick = { i, target->m_IDX };
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (IsEntityVectorVisible(target, ticks[i].hitboxes[0].center))
|
||||
{
|
||||
good_tick = { i, target->m_IDX };
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Head only
|
||||
if (headonly)
|
||||
{
|
||||
@ -1275,24 +1221,39 @@ int BestHitbox(CachedEntity *target)
|
||||
IF_GAME(IsCSS())
|
||||
return 12;
|
||||
}
|
||||
// If the prefered hitbox vis check passes, use it
|
||||
|
||||
if (IsBacktracking() && !projectile_mode)
|
||||
// Backtracking and preferred hitbox
|
||||
if (IsBacktracking())
|
||||
{
|
||||
namespace bt = hacks::shared::backtrack;
|
||||
if (good_tick.first != -1)
|
||||
if (IsEntityVectorVisible(target, bt::headPositions[target->m_IDX - 1][good_tick.first].hitboxes[preferred].center))
|
||||
return preferred;
|
||||
auto data = g_IBacktrack.getClosestEntTick(target, LOCAL_E->m_vecOrigin(), std::bind(&hacks::tf2::backtrack::Backtrack::defaultTickFilter, &g_IBacktrack, std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
if (data)
|
||||
{
|
||||
if (*backtrackVischeckAll)
|
||||
for (int j = 0; j < 18; j++)
|
||||
{
|
||||
if (IsEntityVectorVisible(target, (*data).hitboxes[j].center))
|
||||
return j;
|
||||
}
|
||||
else if (IsEntityVectorVisible(target, (*data).hitboxes[0].center))
|
||||
return 0;
|
||||
}
|
||||
// Nothing found, falling through to further below
|
||||
}
|
||||
else if (target->hitboxes.VisibilityCheck(preferred))
|
||||
return preferred;
|
||||
// Else attempt to find a hitbox at all
|
||||
if (IsBacktracking() && !projectile_mode && good_tick.first != -1)
|
||||
if (IsBacktracking())
|
||||
{
|
||||
namespace bt = hacks::shared::backtrack;
|
||||
for (int i = 0; i < 18; i++)
|
||||
if (IsEntityVectorVisible(target, bt::headPositions[target->m_IDX][good_tick.first].hitboxes.at(i).center))
|
||||
return i;
|
||||
// We already vischecked
|
||||
if (!*backtrackVischeckAll)
|
||||
{
|
||||
auto data = g_IBacktrack.getClosestEntTick(target, LOCAL_E->m_vecOrigin(), std::bind(&hacks::tf2::backtrack::Backtrack::defaultTickFilter, &g_IBacktrack, std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
for (int i = 0; i < 18; i++)
|
||||
if (IsEntityVectorVisible(target, (*data).hitboxes.at(i).center))
|
||||
return i;
|
||||
}
|
||||
}
|
||||
else
|
||||
for (int i = projectile_mode ? 1 : 0; i < target->hitboxes.GetNumHitboxes() && i < 6; i++)
|
||||
@ -1304,52 +1265,12 @@ int BestHitbox(CachedEntity *target)
|
||||
break;
|
||||
case 1:
|
||||
{ // AUTO-CLOSEST priority, Return closest hitbox to crosshair
|
||||
int hb = ClosestHitbox(target);
|
||||
if (IsBacktracking() && !projectile_mode)
|
||||
{
|
||||
hb = std::min(hb, 17);
|
||||
namespace bt = hacks::shared::backtrack;
|
||||
good_tick = { -1, -1 };
|
||||
auto ticks = bt::headPositions[target->m_IDX];
|
||||
for (int i = 0; i < 66; i++)
|
||||
{
|
||||
if (!ticks[i].tickcount)
|
||||
continue;
|
||||
if (!bt::ValidTick(ticks[i], target))
|
||||
continue;
|
||||
if (IsEntityVectorVisible(target, ticks[i].hitboxes[hb].center))
|
||||
{
|
||||
good_tick = { i, target->m_IDX };
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return hb;
|
||||
return ClosestHitbox(target);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
{ // STATIC priority, Return a user chosen hitbox
|
||||
int hb = *hitbox;
|
||||
if (IsBacktracking() && !projectile_mode)
|
||||
{
|
||||
hb = std::min(hb, 17);
|
||||
namespace bt = hacks::shared::backtrack;
|
||||
good_tick = { -1, -1 };
|
||||
auto ticks = bt::headPositions[target->m_IDX];
|
||||
for (int i = 0; i < 66; i++)
|
||||
{
|
||||
if (!ticks[i].tickcount)
|
||||
continue;
|
||||
if (!bt::ValidTick(ticks[i], target))
|
||||
continue;
|
||||
if (IsEntityVectorVisible(target, ticks[i].hitboxes[hb].center))
|
||||
{
|
||||
good_tick = { i, target->m_IDX };
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return hb;
|
||||
return *hitbox;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -1382,13 +1303,13 @@ int ClosestHitbox(CachedEntity *target)
|
||||
}
|
||||
|
||||
// Function to get predicted visual checks
|
||||
bool VischeckPredictedEntity(CachedEntity *entity, bool Backtracking)
|
||||
bool VischeckPredictedEntity(CachedEntity *entity)
|
||||
{
|
||||
// Retrieve predicted data
|
||||
AimbotCalculatedData_s &cd = calculated_data_array[entity->m_IDX];
|
||||
if (cd.vcheck_tick == tickcount)
|
||||
return cd.visible;
|
||||
if (!Backtracking)
|
||||
if (!shouldBacktrack() || entity->m_Type() != ENTITY_PLAYER)
|
||||
{
|
||||
// Update info
|
||||
cd.vcheck_tick = tickcount;
|
||||
@ -1404,21 +1325,9 @@ bool VischeckPredictedEntity(CachedEntity *entity, bool Backtracking)
|
||||
}
|
||||
else
|
||||
{
|
||||
namespace bt = hacks::shared::backtrack;
|
||||
auto ticks = bt::headPositions[entity->m_IDX];
|
||||
if (good_tick.first != -1 && good_tick.second == entity->m_IDX && IsEntityVectorVisible(entity, PredictEntity(entity)))
|
||||
{
|
||||
cd.vcheck_tick = tickcount;
|
||||
cd.visible = true;
|
||||
if (g_pLocalPlayer->weapon_mode != weapon_melee)
|
||||
{
|
||||
current_user_cmd->tick_count = ticks[good_tick.first].tickcount;
|
||||
Vector &angles = CE_VECTOR(entity, netvar.m_angEyeAngles);
|
||||
float &simtime = CE_FLOAT(entity, netvar.m_flSimulationTime);
|
||||
angles.y = ticks[good_tick.first].viewangles;
|
||||
simtime = ticks[good_tick.first].simtime;
|
||||
}
|
||||
}
|
||||
auto data = g_IBacktrack.getClosestEntTick(entity, LOCAL_E->m_vecOrigin(), std::bind(&hacks::tf2::backtrack::Backtrack::defaultTickFilter, &g_IBacktrack, std::placeholders::_1, std::placeholders::_2));
|
||||
if (data && IsEntityVectorVisible(entity, data->hitboxes.at(cd.hitbox == -1 ? 0 : cd.hitbox).center, MASK_SHOT))
|
||||
cd.visible = true;
|
||||
else
|
||||
cd.visible = false;
|
||||
}
|
||||
@ -1606,12 +1515,12 @@ static void DrawText()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
void rvarCallback(settings::VariableBase<int> &, float after)
|
||||
void rvarCallback(settings::VariableBase<float> &, float after)
|
||||
{
|
||||
force_backtrack_aimbot = after > 200.0f;
|
||||
force_backtrack_aimbot = after >= 200.0f;
|
||||
}
|
||||
static InitRoutine EC([]() {
|
||||
hacks::shared::backtrack::latency.installChangeCallback(rvarCallback);
|
||||
g_IBacktrack.latency.installChangeCallback(rvarCallback);
|
||||
EC::Register(EC::LevelInit, Reset, "INIT_Aimbot", EC::average);
|
||||
EC::Register(EC::LevelShutdown, Reset, "RESET_Aimbot", EC::average);
|
||||
EC::Register(EC::CreateMove, CreateMove, "CM_Aimbot", EC::late);
|
||||
|
@ -34,7 +34,7 @@ int ClosestDistanceHitbox(CachedEntity *target)
|
||||
}
|
||||
return closest;
|
||||
}
|
||||
int ClosestDistanceHitbox(hacks::shared::backtrack::BacktrackData btd)
|
||||
int ClosestDistanceHitbox(hacks::tf2::backtrack::BacktrackData btd)
|
||||
{
|
||||
int closest = -1;
|
||||
float closest_dist = FLT_MAX, dist = 0.0f;
|
||||
@ -59,29 +59,29 @@ bool angleCheck(CachedEntity *from, CachedEntity *to, std::optional<Vector> targ
|
||||
{
|
||||
Vector tarAngle = CE_VECTOR(to, netvar.m_angEyeAngles);
|
||||
|
||||
Vector wsc_spy_to_victim;
|
||||
Vector vecToTarget;
|
||||
if (target_pos)
|
||||
wsc_spy_to_victim = *target_pos - from->m_vecOrigin();
|
||||
vecToTarget = *target_pos - from->m_vecOrigin();
|
||||
else
|
||||
wsc_spy_to_victim = to->m_vecOrigin() - from->m_vecOrigin();
|
||||
wsc_spy_to_victim.z = 0;
|
||||
wsc_spy_to_victim.NormalizeInPlace();
|
||||
vecToTarget = to->m_vecOrigin() - from->m_vecOrigin();
|
||||
vecToTarget.z = 0;
|
||||
vecToTarget.NormalizeInPlace();
|
||||
|
||||
Vector eye_spy;
|
||||
AngleVectors2(VectorToQAngle(from_angle), &eye_spy);
|
||||
eye_spy.z = 0;
|
||||
eye_spy.NormalizeInPlace();
|
||||
Vector vecOwnerForward;
|
||||
AngleVectors2(VectorToQAngle(from_angle), &vecOwnerForward);
|
||||
vecOwnerForward.z = 0;
|
||||
vecOwnerForward.NormalizeInPlace();
|
||||
|
||||
Vector eye_victim;
|
||||
AngleVectors2(VectorToQAngle(tarAngle), &eye_victim);
|
||||
eye_victim.z = 0;
|
||||
eye_victim.NormalizeInPlace();
|
||||
Vector vecTargetForward;
|
||||
AngleVectors2(VectorToQAngle(tarAngle), &vecTargetForward);
|
||||
vecTargetForward.z = 0;
|
||||
vecTargetForward.NormalizeInPlace();
|
||||
|
||||
if (DotProduct(wsc_spy_to_victim, eye_victim) <= 0.0f)
|
||||
if (DotProduct(vecToTarget, vecTargetForward) <= 0.0f)
|
||||
return false;
|
||||
if (DotProduct(wsc_spy_to_victim, eye_spy) <= 0.5f)
|
||||
if (DotProduct(vecToTarget, vecOwnerForward) <= 0.5f)
|
||||
return false;
|
||||
if (DotProduct(eye_spy, eye_victim) <= -0.3f)
|
||||
if (DotProduct(vecOwnerForward, vecTargetForward) <= -0.3f)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@ -90,31 +90,43 @@ static bool angleCheck(CachedEntity *target, std::optional<Vector> target_pos, V
|
||||
{
|
||||
Vector tarAngle = CE_VECTOR(target, netvar.m_angEyeAngles);
|
||||
|
||||
Vector wsc_spy_to_victim;
|
||||
// Get a vector from owner origin to target origin
|
||||
Vector vecToTarget;
|
||||
|
||||
Vector local_worldspace;
|
||||
VectorLerp(RAW_ENT(LOCAL_E)->GetCollideable()->OBBMins(), RAW_ENT(LOCAL_E)->GetCollideable()->OBBMaxs(), 0.5f, local_worldspace);
|
||||
local_worldspace += LOCAL_E->m_vecOrigin();
|
||||
if (target_pos)
|
||||
wsc_spy_to_victim = *target_pos - LOCAL_E->m_vecOrigin();
|
||||
vecToTarget = *target_pos - local_worldspace;
|
||||
else
|
||||
wsc_spy_to_victim = target->m_vecOrigin() - LOCAL_E->m_vecOrigin();
|
||||
wsc_spy_to_victim.z = 0;
|
||||
wsc_spy_to_victim.NormalizeInPlace();
|
||||
{
|
||||
Vector target_worldspace;
|
||||
VectorLerp(RAW_ENT(target)->GetCollideable()->OBBMins(), RAW_ENT(target)->GetCollideable()->OBBMaxs(), 0.5f, target_worldspace);
|
||||
target_worldspace += LOCAL_E->m_vecOrigin();
|
||||
vecToTarget = target_worldspace - local_worldspace;
|
||||
}
|
||||
|
||||
Vector eye_spy;
|
||||
AngleVectors2(VectorToQAngle(local_angle), &eye_spy);
|
||||
eye_spy.z = 0;
|
||||
eye_spy.NormalizeInPlace();
|
||||
vecToTarget.z = 0;
|
||||
vecToTarget.NormalizeInPlace();
|
||||
|
||||
Vector eye_victim;
|
||||
AngleVectors2(VectorToQAngle(tarAngle), &eye_victim);
|
||||
eye_victim.z = 0;
|
||||
eye_victim.NormalizeInPlace();
|
||||
// Get owner forward view vector
|
||||
Vector vecOwnerForward;
|
||||
AngleVectors2(VectorToQAngle(local_angle), &vecOwnerForward);
|
||||
vecOwnerForward.z = 0;
|
||||
vecOwnerForward.NormalizeInPlace();
|
||||
|
||||
if (DotProduct(wsc_spy_to_victim, eye_victim) <= 0.0f)
|
||||
return false;
|
||||
if (DotProduct(wsc_spy_to_victim, eye_spy) <= 0.5f)
|
||||
return false;
|
||||
if (DotProduct(eye_spy, eye_victim) <= -0.3f)
|
||||
return false;
|
||||
return true;
|
||||
// Get target forward view vector
|
||||
Vector vecTargetForward;
|
||||
AngleVectors2(VectorToQAngle(tarAngle), &vecTargetForward);
|
||||
vecTargetForward.z = 0;
|
||||
vecTargetForward.NormalizeInPlace();
|
||||
|
||||
// Make sure owner is behind, facing and aiming at target's back
|
||||
float flPosVsTargetViewDot = DotProduct(vecToTarget, vecTargetForward); // Behind?
|
||||
float flPosVsOwnerViewDot = DotProduct(vecToTarget, vecOwnerForward); // Facing?
|
||||
float flViewAnglesDot = DotProduct(vecTargetForward, vecOwnerForward); // Facestab?
|
||||
|
||||
return (flPosVsTargetViewDot > 0.f && flPosVsOwnerViewDot > 0.5 && flViewAnglesDot > -0.3f);
|
||||
}
|
||||
|
||||
static bool doLegitBackstab()
|
||||
@ -212,119 +224,79 @@ static bool doRageBackstab()
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Make accessible to the filter
|
||||
static bool legit_stab = false;
|
||||
static Vector newangle_apply;
|
||||
|
||||
static bool doBacktrackStab()
|
||||
bool backtrackFilter(CachedEntity *ent, hacks::tf2::backtrack::BacktrackData tick, std::optional<hacks::tf2::backtrack::BacktrackData> &best_tick)
|
||||
{
|
||||
float swingrange = re::C_TFWeaponBaseMelee::GetSwingRange(RAW_ENT(LOCAL_W));
|
||||
CachedEntity *ent;
|
||||
if (hacks::shared::backtrack::iBestTarget < 1)
|
||||
return false;
|
||||
// Get the best ent decided by backtrack (no reason to do work twice)
|
||||
ent = ENTITY(hacks::shared::backtrack::iBestTarget);
|
||||
if (!ent->m_bEnemy() || !player_tools::shouldTarget(ent))
|
||||
return false;
|
||||
// Get the ent's backtrack ticks
|
||||
auto &btd = hacks::shared::backtrack::headPositions[ent->m_IDX];
|
||||
|
||||
for (int i = 0; i < 66; i++)
|
||||
Vector target_worldspace;
|
||||
VectorLerp(tick.m_vecMins, tick.m_vecMaxs, 0.5f, target_worldspace);
|
||||
target_worldspace += tick.m_vecOrigin;
|
||||
Vector distcheck = target_worldspace;
|
||||
distcheck.z = g_pLocalPlayer->v_Eye.z;
|
||||
|
||||
// Angle check
|
||||
Vector newangle;
|
||||
if (legit_stab)
|
||||
newangle = g_pLocalPlayer->v_OrigViewangles;
|
||||
else
|
||||
newangle = GetAimAtAngles(g_pLocalPlayer->v_Eye, distcheck);
|
||||
if (!angleCheck(ent, target_worldspace, newangle) && !canFaceStab(ent))
|
||||
return false;
|
||||
|
||||
Vector min = tick.m_vecMins + tick.m_vecOrigin;
|
||||
Vector max = tick.m_vecMaxs + tick.m_vecOrigin;
|
||||
Vector hit;
|
||||
|
||||
// Check if we can hit the enemies hitbox
|
||||
if (hacks::shared::triggerbot::CheckLineBox(min, max, g_pLocalPlayer->v_Eye, GetForwardVector(g_pLocalPlayer->v_Eye, newangle, swingrange * 0.95f), hit))
|
||||
{
|
||||
auto &btp = btd[i];
|
||||
// Check if the backtrack positions are valid
|
||||
if (!hacks::shared::backtrack::ValidTick(btp, ent))
|
||||
continue;
|
||||
Vector distcheck = btp.entorigin;
|
||||
distcheck.z = g_pLocalPlayer->v_Eye.z;
|
||||
// dont stab while inside the enemy
|
||||
if (distcheck.DistTo(g_pLocalPlayer->v_Eye) < 20.0f)
|
||||
continue;
|
||||
// Get and calculate an angle to use to backstab the ent
|
||||
auto hitbox = ClosestDistanceHitbox(btp);
|
||||
if (hitbox == -1)
|
||||
continue;
|
||||
Vector newangle = GetAimAtAngles(g_pLocalPlayer->v_Eye, btp.hitboxes.at(hitbox).center);
|
||||
if (!angleCheck(ent, btp.entorigin, newangle) && !canFaceStab(ent))
|
||||
continue;
|
||||
Vector &min = btp.collidable.min;
|
||||
Vector &max = btp.collidable.max;
|
||||
Vector hit;
|
||||
// Check if we can hit the enemies hitbox
|
||||
if (hacks::shared::triggerbot::CheckLineBox(min, max, g_pLocalPlayer->v_Eye, GetForwardVector(g_pLocalPlayer->v_Eye, newangle, swingrange * 0.95f), hit))
|
||||
// Check if this tick is closer
|
||||
if (!best_tick || (*best_tick).m_vecOrigin.DistTo(LOCAL_E->m_vecOrigin()) > tick.m_vecOrigin.DistTo(LOCAL_E->m_vecOrigin()))
|
||||
{
|
||||
current_user_cmd->tick_count = btp.tickcount;
|
||||
current_user_cmd->viewangles = newangle;
|
||||
current_user_cmd->buttons |= IN_ATTACK;
|
||||
g_pLocalPlayer->bUseSilentAngles = true;
|
||||
*bSendPackets = true;
|
||||
newangle_apply = newangle;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Don't duplicate these loops
|
||||
static bool doLegitBacktrackStab() // lol
|
||||
static bool doBacktrackStab(bool legit = false)
|
||||
{
|
||||
float swingrange = re::C_TFWeaponBaseMelee::GetSwingRange(RAW_ENT(LOCAL_W));
|
||||
CachedEntity *ent;
|
||||
if (hacks::shared::backtrack::iBestTarget < 1)
|
||||
return false;
|
||||
ent = ENTITY(hacks::shared::backtrack::iBestTarget);
|
||||
if (!ent->m_bEnemy() || !player_tools::shouldTarget(ent))
|
||||
return false;
|
||||
auto &btd = hacks::shared::backtrack::headPositions[ent->m_IDX];
|
||||
Vector newangle = g_pLocalPlayer->v_OrigViewangles;
|
||||
std::vector<float> yangles;
|
||||
float best_scr = FLT_MAX;
|
||||
hacks::shared::backtrack::BacktrackData *best_tick;
|
||||
for (int ii = 0; ii < 66; ii++)
|
||||
CachedEntity *stab_ent = nullptr;
|
||||
hacks::tf2::backtrack::BacktrackData stab_data;
|
||||
// Set for our filter
|
||||
legit_stab = legit;
|
||||
// Get the Best tick
|
||||
for (int i = 0; i <= g_IEngine->GetMaxClients(); i++)
|
||||
{
|
||||
std::vector<float> yangles_tmp;
|
||||
auto &i = btd[ii];
|
||||
CachedEntity *ent = ENTITY(i);
|
||||
// Targeting checks
|
||||
if (CE_BAD(ent) || !ent->m_bAlivePlayer() || !ent->m_bEnemy() || !player_tools::shouldTarget(ent))
|
||||
continue;
|
||||
|
||||
Vector distcheck = i.entorigin;
|
||||
distcheck.z = g_pLocalPlayer->v_Eye.z;
|
||||
if (distcheck.DistTo(g_pLocalPlayer->v_Eye) < best_scr && distcheck.DistTo(g_pLocalPlayer->v_Eye) > 20.0f)
|
||||
// Get the best tick for that ent
|
||||
auto tick_data = g_IBacktrack.getBestTick(ent, backtrackFilter);
|
||||
|
||||
// We found something matching the criterias, break out
|
||||
if (tick_data)
|
||||
{
|
||||
if (!hacks::shared::backtrack::ValidTick(i, ent))
|
||||
continue;
|
||||
if (!angleCheck(ent, i.entorigin, newangle) && !canFaceStab(ent))
|
||||
continue;
|
||||
|
||||
Vector &min = i.collidable.min;
|
||||
Vector &max = i.collidable.max;
|
||||
|
||||
// Get the min and max for the hitbox
|
||||
Vector minz(fminf(min.x, max.x), fminf(min.y, max.y), fminf(min.z, max.z));
|
||||
Vector maxz(fmaxf(min.x, max.x), fmaxf(min.y, max.y), fmaxf(min.z, max.z));
|
||||
|
||||
// Shrink the hitbox here
|
||||
Vector size = maxz - minz;
|
||||
Vector smod = { size.x * 0.20f, size.y * 0.20f, 0 };
|
||||
|
||||
// Save the changes to the vectors
|
||||
minz += smod;
|
||||
maxz -= smod;
|
||||
maxz.z += 20.0f;
|
||||
|
||||
Vector hit;
|
||||
if (hacks::shared::triggerbot::CheckLineBox(minz, maxz, g_pLocalPlayer->v_Eye, GetForwardVector(g_pLocalPlayer->v_Eye, newangle, swingrange), hit))
|
||||
yangles_tmp.push_back(newangle.y);
|
||||
|
||||
if (!yangles_tmp.empty())
|
||||
{
|
||||
yangles.clear();
|
||||
best_scr = distcheck.DistTo(g_pLocalPlayer->v_Eye);
|
||||
best_tick = &i;
|
||||
yangles = yangles_tmp;
|
||||
}
|
||||
stab_data = *tick_data;
|
||||
stab_ent = ent;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!yangles.empty() && best_tick)
|
||||
|
||||
// We found a good ent
|
||||
if (stab_ent)
|
||||
{
|
||||
newangle.y = yangles.at(std::floor((float) yangles.size() / 2));
|
||||
current_user_cmd->tick_count = best_tick->tickcount;
|
||||
current_user_cmd->viewangles = newangle;
|
||||
g_IBacktrack.SetBacktrackData(stab_ent, stab_data);
|
||||
current_user_cmd->buttons |= IN_ATTACK;
|
||||
current_user_cmd->viewangles = newangle_apply;
|
||||
g_pLocalPlayer->bUseSilentAngles = true;
|
||||
*bSendPackets = true;
|
||||
return true;
|
||||
@ -349,11 +321,11 @@ void CreateMove()
|
||||
doRageBackstab();
|
||||
break;
|
||||
case 2:
|
||||
if (hacks::shared::backtrack::isBacktrackEnabled)
|
||||
if (g_IBacktrack.isBacktrackEnabled)
|
||||
{
|
||||
if (*hacks::shared::backtrack::latency <= 190 && doRageBackstab())
|
||||
if (*g_IBacktrack.latency <= 190 && doRageBackstab())
|
||||
break;
|
||||
doBacktrackStab();
|
||||
doBacktrackStab(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -361,11 +333,11 @@ void CreateMove()
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (hacks::shared::backtrack::isBacktrackEnabled)
|
||||
if (g_IBacktrack.isBacktrackEnabled)
|
||||
{
|
||||
if (*hacks::shared::backtrack::latency <= 190 && doLegitBackstab())
|
||||
if (*g_IBacktrack.latency <= 190 && doLegitBackstab())
|
||||
break;
|
||||
doLegitBacktrackStab();
|
||||
doBacktrackStab(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1,317 +1,75 @@
|
||||
/*
|
||||
* Backtrack.cpp
|
||||
* Remade on May the 3rd 2020
|
||||
* Author: BenCat07
|
||||
*
|
||||
* Created on: May 15, 2018
|
||||
* Author: bencat07
|
||||
*/
|
||||
#include "Backtrack.hpp"
|
||||
|
||||
#include "common.hpp"
|
||||
#include "hacks/Aimbot.hpp"
|
||||
#include "hacks/Backtrack.hpp"
|
||||
#include <boost/circular_buffer.hpp>
|
||||
#include <settings/Bool.hpp>
|
||||
#include "PlayerTools.hpp"
|
||||
#include <hacks/Backtrack.hpp>
|
||||
#include "MiscTemporary.hpp"
|
||||
|
||||
namespace hacks::shared::backtrack
|
||||
namespace hacks::tf2::backtrack
|
||||
{
|
||||
static settings::Boolean draw_bt{ "backtrack.draw", "false" };
|
||||
static settings::Boolean draw_skeleton{ "backtrack.draw-skeleton", "false" };
|
||||
static settings::Float mindistance{ "backtrack.min-distance", "60" };
|
||||
|
||||
static settings::Int slots{ "backtrack.slots", "0" };
|
||||
|
||||
settings::Boolean enable{ "backtrack.enable", "false" };
|
||||
settings::Boolean backtrack_chams_glow{ "backtrack.chams_glow", "true" };
|
||||
settings::Int latency{ "backtrack.latency", "0" };
|
||||
settings::Boolean enable_latency_rampup{ "backtrack.latency.rampup", "true" };
|
||||
|
||||
void EmptyBacktrackData(BacktrackData &i);
|
||||
std::pair<int, int> getBestEntBestTick();
|
||||
bool shouldBacktrack();
|
||||
|
||||
BacktrackData headPositions[PLAYER_ARRAY_SIZE][66]{};
|
||||
int lastincomingsequencenumber = 0;
|
||||
bool isBacktrackEnabled = false;
|
||||
bool Vischeck_Success = false;
|
||||
|
||||
circular_buf sequences{ 2048 };
|
||||
void UpdateIncomingSequences()
|
||||
{
|
||||
INetChannel *ch = (INetChannel *) g_IEngine->GetNetChannelInfo();
|
||||
if (ch)
|
||||
{
|
||||
int m_nInSequenceNr = ch->m_nInSequenceNr;
|
||||
int instate = ch->m_nInReliableState;
|
||||
if (m_nInSequenceNr > lastincomingsequencenumber)
|
||||
{
|
||||
lastincomingsequencenumber = m_nInSequenceNr;
|
||||
sequences.push_front(CIncomingSequence(instate, m_nInSequenceNr, g_GlobalVars->realtime));
|
||||
}
|
||||
if (sequences.size() > 2048)
|
||||
sequences.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void AddLatencyToNetchan(INetChannel *ch)
|
||||
// Apply Backtrack
|
||||
void Backtrack::ApplyBacktrack()
|
||||
{
|
||||
if (!isBacktrackEnabled)
|
||||
return;
|
||||
for (auto &seq : sequences)
|
||||
if (bt_ent)
|
||||
{
|
||||
if (g_GlobalVars->realtime - seq.curtime > getLatency() / 1000.0f)
|
||||
{
|
||||
ch->m_nInReliableState = seq.inreliablestate;
|
||||
ch->m_nInSequenceNr = seq.sequencenr;
|
||||
break;
|
||||
}
|
||||
current_user_cmd->tick_count = (*bt_data).tickcount;
|
||||
CE_FLOAT(bt_ent, netvar.m_angEyeAngles) = (*bt_data).m_vecAngles.x;
|
||||
CE_FLOAT(bt_ent, netvar.m_angEyeAngles + 4) = (*bt_data).m_vecAngles.y;
|
||||
CE_FLOAT(bt_ent, netvar.m_flSimulationTime) = (*bt_data).m_flSimulationTime;
|
||||
}
|
||||
}
|
||||
void Init()
|
||||
{
|
||||
for (int i = 0; i < PLAYER_ARRAY_SIZE; i++)
|
||||
for (int j = 0; j < 66; j++)
|
||||
headPositions[i][j] = {};
|
||||
|
||||
BestTick = iBestTarget = -1;
|
||||
// Update tick to apply
|
||||
void Backtrack::SetBacktrackData(CachedEntity *ent, BacktrackData tick)
|
||||
{
|
||||
bt_ent = ent;
|
||||
bt_data = tick;
|
||||
}
|
||||
|
||||
int BestTick = -1;
|
||||
int iBestTarget = -1;
|
||||
bool istickvalid[PLAYER_ARRAY_SIZE][66]{};
|
||||
bool istickinvalid[PLAYER_ARRAY_SIZE][66]{};
|
||||
static float latency_rampup = 0.0f;
|
||||
|
||||
static void Run()
|
||||
// Get Best tick for Backtrack (crosshair/fov based)
|
||||
bool Backtrack::getBestInternalTick(CachedEntity *, BacktrackData &data, std::optional<BacktrackData> &best_tick)
|
||||
{
|
||||
if (!shouldBacktrack())
|
||||
{
|
||||
isBacktrackEnabled = false;
|
||||
latency_rampup = 0;
|
||||
return;
|
||||
}
|
||||
// Best Score
|
||||
float bestScore = FLT_MAX;
|
||||
|
||||
// Limit to 2 ticks
|
||||
latency_rampup += 1.0f / 66.0f;
|
||||
latency_rampup = std::min(latency_rampup, 1.0f);
|
||||
// Are we using a melee weapon?
|
||||
bool is_melee = false;
|
||||
if (g_pLocalPlayer->weapon_mode == weapon_melee)
|
||||
is_melee = true;
|
||||
|
||||
UpdateIncomingSequences();
|
||||
isBacktrackEnabled = true;
|
||||
// Get the FOV of the best tick if available
|
||||
if (best_tick)
|
||||
bestScore = GetFov(g_pLocalPlayer->v_OrigViewangles, g_pLocalPlayer->v_Eye, (*best_tick).hitboxes.at(head).center);
|
||||
|
||||
if (CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer() || CE_BAD(LOCAL_W))
|
||||
return;
|
||||
if (g_Settings.bInvalid)
|
||||
return;
|
||||
if (!current_user_cmd)
|
||||
return;
|
||||
for (auto &a : istickvalid)
|
||||
for (auto &b : a)
|
||||
b = false;
|
||||
for (auto &a : istickinvalid)
|
||||
for (auto &b : a)
|
||||
b = false;
|
||||
CUserCmd *cmd = current_user_cmd;
|
||||
float bestFov = 99999;
|
||||
|
||||
float prev_distance = 9999;
|
||||
std::pair<int, int> bestEntBestTick = getBestEntBestTick();
|
||||
BestTick = bestEntBestTick.second;
|
||||
iBestTarget = bestEntBestTick.first;
|
||||
// Fill backtrack data (stored in headPositions)
|
||||
{
|
||||
PROF_SECTION(cm_bt_ent_loop)
|
||||
for (int i = 1; i <= g_IEngine->GetMaxClients(); i++)
|
||||
{
|
||||
CachedEntity *pEntity = ENTITY(i);
|
||||
if (CE_BAD(pEntity) || !pEntity->m_bAlivePlayer())
|
||||
{
|
||||
for (BacktrackData &btd : headPositions[i])
|
||||
btd.simtime = FLT_MAX;
|
||||
continue;
|
||||
}
|
||||
if (!pEntity->m_bEnemy())
|
||||
continue;
|
||||
if (pEntity->m_Type() != ENTITY_PLAYER)
|
||||
continue;
|
||||
if (!pEntity->hitboxes.GetHitbox(0))
|
||||
continue;
|
||||
if (HasCondition<TFCond_HalloweenGhostMode>(pEntity))
|
||||
continue;
|
||||
if (!*bSendPackets)
|
||||
headPositions[i][cmd->command_number % getTicks()] = {};
|
||||
else
|
||||
{
|
||||
auto &hbd = headPositions[i][cmd->command_number % getTicks()];
|
||||
float _viewangles = CE_VECTOR(pEntity, netvar.m_angEyeAngles).y;
|
||||
hbd.viewangles = (_viewangles > 180) ? _viewangles - 360 : _viewangles;
|
||||
hbd.simtime = CE_FLOAT(pEntity, netvar.m_flSimulationTime);
|
||||
hbd.entorigin = pEntity->InternalEntity()->GetAbsOrigin();
|
||||
hbd.tickcount = cmd->tick_count;
|
||||
if (nolerp)
|
||||
{
|
||||
static const ConVar *pUpdateRate = g_pCVar->FindVar("cl_updaterate");
|
||||
if (!pUpdateRate)
|
||||
pUpdateRate = g_pCVar->FindVar("cl_updaterate");
|
||||
else
|
||||
{
|
||||
|
||||
float interp = MAX(cl_interp->GetFloat(), cl_interp_ratio->GetFloat() / pUpdateRate->GetFloat());
|
||||
hbd.tickcount += TIME_TO_TICKS(interp);
|
||||
hbd.simtime += interp;
|
||||
}
|
||||
}
|
||||
|
||||
pEntity->hitboxes.InvalidateCache();
|
||||
for (size_t i = 0; i < 18; i++)
|
||||
{
|
||||
hbd.hitboxes[i].center = pEntity->hitboxes.GetHitbox(i)->center;
|
||||
hbd.hitboxes[i].min = pEntity->hitboxes.GetHitbox(i)->min;
|
||||
hbd.hitboxes[i].max = pEntity->hitboxes.GetHitbox(i)->max;
|
||||
}
|
||||
hbd.collidable.min = RAW_ENT(pEntity)->GetCollideable()->OBBMins() + hbd.entorigin;
|
||||
hbd.collidable.max = RAW_ENT(pEntity)->GetCollideable()->OBBMaxs() + hbd.entorigin;
|
||||
hbd.collidable.center = (hbd.collidable.min + hbd.collidable.max) / 2;
|
||||
memcpy((void *) hbd.bones, (void *) pEntity->hitboxes.bones, sizeof(matrix3x4_t) * 128);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (iBestTarget != -1 && CanShoot())
|
||||
{
|
||||
CachedEntity *tar = ENTITY(iBestTarget);
|
||||
if (CE_GOOD(tar))
|
||||
{
|
||||
if (cmd->buttons & IN_ATTACK)
|
||||
{
|
||||
// ok just in case
|
||||
if (CE_BAD(tar))
|
||||
return;
|
||||
auto i = headPositions[iBestTarget][BestTick];
|
||||
cmd->tick_count = i.tickcount;
|
||||
Vector &angles = NET_VECTOR(RAW_ENT(tar), netvar.m_angEyeAngles);
|
||||
float &simtime = NET_FLOAT(RAW_ENT(tar), netvar.m_flSimulationTime);
|
||||
angles.y = i.viewangles;
|
||||
simtime = i.simtime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CatCommand print_bones("debug_print_bones", "debug print bone id + name", []() {
|
||||
if (CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer())
|
||||
return;
|
||||
// Get player model
|
||||
const model_t *model = RAW_ENT(LOCAL_E)->GetModel();
|
||||
if (not model)
|
||||
return;
|
||||
// Get Studio models (for bones)
|
||||
studiohdr_t *hdr = g_IModelInfo->GetStudiomodel(model);
|
||||
if (not hdr)
|
||||
return;
|
||||
// Get the name of the bones
|
||||
for (int i = 0; i < hdr->numbones; i++)
|
||||
logging::Info(format(std::string(hdr->pBone(i)->pszName()), " ", i).c_str());
|
||||
});
|
||||
static std::vector<int> bones_leg_r = { 17, 16, 15 };
|
||||
static std::vector<int> bones_leg_l = { 14, 13, 12 };
|
||||
static std::vector<int> bones_bottom = { 15, 1, 12 };
|
||||
static std::vector<int> bones_spine = { 1, 2, 3, 4, 5, 0 };
|
||||
static std::vector<int> bones_arm_r = { 9, 10, 11 };
|
||||
static std::vector<int> bones_arm_l = { 6, 7, 8 };
|
||||
static std::vector<int> bones_up = { 9, 5, 6 };
|
||||
|
||||
#if ENABLE_VISUALS
|
||||
void DrawBone(std::vector<int> hitbox, std::array<hitboxData, 18> hitboxes)
|
||||
{
|
||||
for (int i = 0; i < hitbox.size() - 1; i++)
|
||||
{
|
||||
Vector bone1 = hitboxes.at(hitbox.at(i)).center;
|
||||
Vector bone2 = hitboxes.at(hitbox.at(i + 1)).center;
|
||||
Vector draw_position1, draw_position2;
|
||||
if (draw::WorldToScreen(bone1, draw_position1) && draw::WorldToScreen(bone2, draw_position2))
|
||||
draw::Line(draw_position1.x, draw_position1.y, draw_position2.x - draw_position1.x, draw_position2.y - draw_position1.y, colors::white, 1.0f);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
static void Draw()
|
||||
{
|
||||
#if ENABLE_VISUALS
|
||||
if (!isBacktrackEnabled)
|
||||
return;
|
||||
// :b:ones for non drawable ents
|
||||
if (draw_skeleton)
|
||||
for (int i = 0; i <= g_IEngine->GetMaxClients(); i++)
|
||||
{
|
||||
CachedEntity *ent = ENTITY(i);
|
||||
if (CE_BAD(ent) || !ent->m_bAlivePlayer() || i == g_IEngine->GetLocalPlayer())
|
||||
continue;
|
||||
auto head_pos = headPositions[i];
|
||||
// Usable vector instead of ptr to c style array, also used to filter valid and invalid ticks
|
||||
std::vector<BacktrackData> usable;
|
||||
for (int i = 0; i < 66; i++)
|
||||
{
|
||||
if (ValidTick(head_pos[i], ent))
|
||||
usable.push_back(head_pos[i]);
|
||||
}
|
||||
// Crash much?
|
||||
if (usable.size())
|
||||
{
|
||||
DrawBone(bones_leg_l, usable[0].hitboxes);
|
||||
DrawBone(bones_leg_r, usable[0].hitboxes);
|
||||
DrawBone(bones_bottom, usable[0].hitboxes);
|
||||
DrawBone(bones_spine, usable[0].hitboxes);
|
||||
DrawBone(bones_arm_l, usable[0].hitboxes);
|
||||
DrawBone(bones_arm_r, usable[0].hitboxes);
|
||||
DrawBone(bones_up, usable[0].hitboxes);
|
||||
}
|
||||
}
|
||||
if (!draw_bt)
|
||||
return;
|
||||
for (int i = 0; i <= g_IEngine->GetMaxClients(); i++)
|
||||
{
|
||||
CachedEntity *ent = ENTITY(i);
|
||||
if (CE_BAD(ent))
|
||||
continue;
|
||||
for (int j = 0; j < getTicks(); j++)
|
||||
{
|
||||
if (!ValidTick(headPositions[i][j], ent))
|
||||
continue;
|
||||
auto hbpos = headPositions[i][j].hitboxes.at(head).center;
|
||||
auto min = headPositions[i][j].hitboxes.at(head).min;
|
||||
auto max = headPositions[i][j].hitboxes.at(head).max;
|
||||
if (!hbpos.x && !hbpos.y && !hbpos.z)
|
||||
continue;
|
||||
Vector out;
|
||||
if (draw::WorldToScreen(hbpos, out))
|
||||
{
|
||||
float size = 0.0f;
|
||||
if (abs(max.x - min.x) > abs(max.y - min.y))
|
||||
size = abs(max.x - min.x);
|
||||
else
|
||||
size = abs(max.y - min.y);
|
||||
|
||||
if (i == iBestTarget && j == BestTick)
|
||||
draw::Rectangle(out.x, out.y, size / 2, size / 2, colors::red);
|
||||
else
|
||||
draw::Rectangle(out.x, out.y, size / 4, size / 4, colors::green);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Internal only, use isBacktrackEnabled var instead
|
||||
bool shouldBacktrack()
|
||||
{
|
||||
if (!*enable)
|
||||
float FOVDistance = GetFov(g_pLocalPlayer->v_OrigViewangles, g_pLocalPlayer->v_Eye, data.hitboxes.at(head).center);
|
||||
if (FOVDistance >= bestScore)
|
||||
return false;
|
||||
CachedEntity *wep = g_pLocalPlayer->weapon();
|
||||
|
||||
// Found new best fov, now vischeck
|
||||
if (FOVDistance < bestScore)
|
||||
{
|
||||
// Vischeck check
|
||||
if (!is_melee && !IsVectorVisible(g_pLocalPlayer->v_Eye, data.hitboxes.at(head).center, false))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
// Should never be called but gcc likes to complain anyways
|
||||
return false;
|
||||
}
|
||||
// Is backtrack enabled?
|
||||
bool Backtrack::isEnabled()
|
||||
{
|
||||
if (!*enabled)
|
||||
return false;
|
||||
CachedEntity *wep = LOCAL_W;
|
||||
if (CE_BAD(wep))
|
||||
return false;
|
||||
if (*slots == 0)
|
||||
if (*bt_slots == 0)
|
||||
return true;
|
||||
int slot = re::C_BaseCombatWeapon::GetSlot(RAW_ENT(wep));
|
||||
switch ((int) slots)
|
||||
switch ((int) bt_slots)
|
||||
{
|
||||
case 1:
|
||||
if (slot == 0)
|
||||
@ -340,153 +98,386 @@ bool shouldBacktrack()
|
||||
}
|
||||
return false;
|
||||
}
|
||||
float getRealLatency()
|
||||
|
||||
// Main Tracking logic
|
||||
void Backtrack::CreateMove()
|
||||
{
|
||||
auto ch = (INetChannel *) g_IEngine->GetNetChannelInfo();
|
||||
if (!ch)
|
||||
return 0.0f;
|
||||
float Latency = ch->GetLatency(FLOW_OUTGOING);
|
||||
static auto cl_updaterate = g_ICvar->FindVar("cl_updaterate");
|
||||
if (cl_updaterate && cl_updaterate->GetFloat() > 0.001f)
|
||||
// Update enabled status
|
||||
isBacktrackEnabled = isEnabled();
|
||||
if (!isBacktrackEnabled)
|
||||
{
|
||||
Latency += -0.5f / cl_updaterate->GetFloat();
|
||||
if (nolerp)
|
||||
latency_rampup = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
if (CE_BAD(LOCAL_E) || CE_BAD(LOCAL_W) || !LOCAL_E->m_bAlivePlayer())
|
||||
{
|
||||
latency_rampup = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
// Make Latency not instantly spike
|
||||
latency_rampup += 1.0f / 66.0f;
|
||||
latency_rampup = std::min(latency_rampup, 1.0f);
|
||||
|
||||
updateDatagram();
|
||||
|
||||
// Clear data
|
||||
bt_ent = nullptr;
|
||||
bt_data.reset();
|
||||
|
||||
current_tickcount = current_user_cmd->tick_count;
|
||||
|
||||
// the "Base" Backtracking
|
||||
std::optional<BacktrackData> best_data;
|
||||
CachedEntity *best_ent = nullptr;
|
||||
|
||||
for (int i = 0; i <= g_IEngine->GetMaxClients(); i++)
|
||||
{
|
||||
if (i == g_pLocalPlayer->entity_idx)
|
||||
{
|
||||
float interp = MAX(cl_interp->GetFloat(), cl_interp_ratio->GetFloat() / cl_updaterate->GetFloat());
|
||||
Latency += interp;
|
||||
resetData(i);
|
||||
continue;
|
||||
}
|
||||
CachedEntity *ent = ENTITY(i);
|
||||
if (CE_BAD(ent) || !ent->m_bAlivePlayer() || ent->m_Type() != ENTITY_PLAYER || !ent->m_bEnemy())
|
||||
{
|
||||
resetData(i);
|
||||
continue;
|
||||
}
|
||||
if (!ent->hitboxes.GetHitbox(0))
|
||||
{
|
||||
resetData(i);
|
||||
continue;
|
||||
}
|
||||
auto &backtrack_ent = backtrack_data.at(i);
|
||||
|
||||
// Have no data, create it
|
||||
if (!backtrack_ent)
|
||||
backtrack_ent.reset(new std::array<BacktrackData, 67>);
|
||||
|
||||
int current_index = current_user_cmd->tick_count % getTicks();
|
||||
|
||||
// Our current tick
|
||||
auto ¤t_tick = (*backtrack_ent).at(current_index);
|
||||
|
||||
// Previous tick
|
||||
int last_index = current_index - 1;
|
||||
if (last_index < 0)
|
||||
last_index = getTicks() - 1;
|
||||
|
||||
auto &previous_tick = (*backtrack_ent).at(last_index);
|
||||
|
||||
// Update basics
|
||||
current_tick.tickcount = current_user_cmd->tick_count;
|
||||
|
||||
current_tick.m_vecAngles.x = CE_FLOAT(ent, netvar.m_angEyeAngles);
|
||||
current_tick.m_vecAngles.y = CE_FLOAT(ent, netvar.m_angEyeAngles + 4);
|
||||
|
||||
current_tick.m_vecOrigin = ent->m_vecOrigin();
|
||||
current_tick.m_vecMins = RAW_ENT(ent)->GetCollideable()->OBBMins();
|
||||
current_tick.m_vecMaxs = RAW_ENT(ent)->GetCollideable()->OBBMaxs();
|
||||
|
||||
current_tick.m_flSimulationTime = CE_FLOAT(ent, netvar.m_flSimulationTime);
|
||||
|
||||
// Update hitboxes
|
||||
ent->hitboxes.InvalidateCache();
|
||||
for (size_t i = 0; i < 18; i++)
|
||||
{
|
||||
current_tick.hitboxes[i].center = ent->hitboxes.GetHitbox(i)->center;
|
||||
current_tick.hitboxes[i].min = ent->hitboxes.GetHitbox(i)->min;
|
||||
current_tick.hitboxes[i].max = ent->hitboxes.GetHitbox(i)->max;
|
||||
}
|
||||
|
||||
// Copy bones (for chams/glow)
|
||||
memcpy((void *) current_tick.bones, (void *) ent->hitboxes.bones, sizeof(matrix3x4_t) * 128);
|
||||
|
||||
// Check if tick updated or not (fakelag)
|
||||
current_tick.has_updated = !previous_tick.m_flSimulationTime || previous_tick.m_flSimulationTime != current_tick.m_flSimulationTime;
|
||||
|
||||
// Get best tick for this ent
|
||||
std::optional<BacktrackData> data = getBestTick(ent, std::bind(&Backtrack::getBestInternalTick, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
// Check if actually the best tick we have in total
|
||||
if (data && (!best_data || getBestInternalTick(ent, *data, best_data)))
|
||||
{
|
||||
best_data = data;
|
||||
best_ent = ent;
|
||||
}
|
||||
}
|
||||
else if (!cl_updaterate)
|
||||
cl_updaterate = g_ICvar->FindVar("cl_updaterate");
|
||||
return MAX(0.0f, Latency) * 1000.f;
|
||||
if (best_data && best_ent)
|
||||
SetBacktrackData(best_ent, *best_data);
|
||||
}
|
||||
|
||||
float getLatency()
|
||||
void Backtrack::CreateMoveLate()
|
||||
{
|
||||
auto ch = (INetChannel *) g_IEngine->GetNetChannelInfo();
|
||||
if (!ch)
|
||||
return 0;
|
||||
float Latency = *latency;
|
||||
Latency = std::min(Latency, 800.0f);
|
||||
Latency -= getRealLatency();
|
||||
Latency = std::max(Latency, 0.0f);
|
||||
if (enable_latency_rampup)
|
||||
Latency = Latency * latency_rampup;
|
||||
Latency = ROUND_TO_TICKS(Latency / 1000.0f) * 1000.0f;
|
||||
return Latency;
|
||||
ApplyBacktrack();
|
||||
}
|
||||
|
||||
int getTicks()
|
||||
#if ENABLE_VISUALS
|
||||
// Drawing
|
||||
void Backtrack::Draw()
|
||||
{
|
||||
// Get Latency in seconds
|
||||
float latency = getLatency() / 1000.0f;
|
||||
// Latency in ticks
|
||||
int ticks = TIME_TO_TICKS(latency);
|
||||
// We can backtrack 200ms into the future of the latency
|
||||
ticks += TIME_TO_TICKS(0.2f);
|
||||
// Clamp between 0.2s and 1s in ticks (12-66)
|
||||
return clamp(ticks, TIME_TO_TICKS(0.2f), TIME_TO_TICKS(1.0f));
|
||||
if (!isBacktrackEnabled || !draw)
|
||||
return;
|
||||
|
||||
if (CE_BAD(LOCAL_E))
|
||||
return;
|
||||
|
||||
for (int i = 0; i <= g_IEngine->GetMaxClients(); i++)
|
||||
{
|
||||
auto data = getGoodTicks(i);
|
||||
if (data.empty())
|
||||
continue;
|
||||
for (auto &tick : data)
|
||||
{
|
||||
auto hbpos = tick.hitboxes.at(head).center;
|
||||
auto min = tick.hitboxes.at(head).min;
|
||||
auto max = tick.hitboxes.at(head).max;
|
||||
if (!hbpos.x && !hbpos.y && !hbpos.z)
|
||||
continue;
|
||||
Vector out;
|
||||
if (draw::WorldToScreen(hbpos, out))
|
||||
{
|
||||
float size = 0.0f;
|
||||
if (abs(max.x - min.x) > abs(max.y - min.y))
|
||||
size = abs(max.x - min.x);
|
||||
else
|
||||
size = abs(max.y - min.y);
|
||||
|
||||
rgba_t draw_color = colors::green;
|
||||
// Found our target tick
|
||||
if (bt_ent && tick.tickcount == (*bt_data).tickcount && i == bt_ent->m_IDX)
|
||||
draw_color = colors::red;
|
||||
draw::Rectangle(out.x, out.y, size / 4, size / 4, draw_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Reset things
|
||||
void Backtrack::LevelShutdown()
|
||||
{
|
||||
lastincomingsequence = 0;
|
||||
sequences.clear();
|
||||
for (int i = 0; i < PLAYER_ARRAY_SIZE; i++)
|
||||
resetData(i);
|
||||
}
|
||||
|
||||
bool ValidTick(BacktrackData &i, CachedEntity *ent)
|
||||
// Change Datagram data
|
||||
void Backtrack::adjustPing(INetChannel *ch)
|
||||
{
|
||||
if (!(fabsf(NET_FLOAT(RAW_ENT(ent), netvar.m_flSimulationTime) * 1000.0f - getLatency() - i.simtime * 1000.0f) < ROUND_TO_TICKS(200.0f)))
|
||||
if (!isBacktrackEnabled)
|
||||
return;
|
||||
for (auto &seq : sequences)
|
||||
{
|
||||
if (g_GlobalVars->realtime - seq.curtime >= getLatency() / 1000.0f)
|
||||
{
|
||||
ch->m_nInReliableState = seq.inreliablestate;
|
||||
ch->m_nInSequenceNr = seq.sequencenr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Latency to add for backtrack
|
||||
float Backtrack::getLatency()
|
||||
{
|
||||
INetChannel *ch = (INetChannel *) g_IEngine->GetNetChannelInfo();
|
||||
// Track what actual latency we have
|
||||
float real_latency = 0.0f;
|
||||
|
||||
// If we have a netchannel (just in case) set real latency to it
|
||||
if (ch)
|
||||
real_latency = ch->GetLatency(FLOW_OUTGOING) * 1000.0f;
|
||||
|
||||
// Fix the latency
|
||||
float backtrack_latency = *latency - real_latency;
|
||||
|
||||
// Clamp and apply rampup
|
||||
backtrack_latency = latency_rampup * std::clamp(backtrack_latency, 0.0f, std::max(800.0f - real_latency, 0.0f));
|
||||
|
||||
return backtrack_latency;
|
||||
}
|
||||
|
||||
// Update our sequences
|
||||
void Backtrack::updateDatagram()
|
||||
{
|
||||
INetChannel *ch = (INetChannel *) g_IEngine->GetNetChannelInfo();
|
||||
if (ch)
|
||||
{
|
||||
int m_nInSequenceNr = ch->m_nInSequenceNr;
|
||||
int instate = ch->m_nInReliableState;
|
||||
if (m_nInSequenceNr > lastincomingsequence)
|
||||
{
|
||||
lastincomingsequence = m_nInSequenceNr;
|
||||
sequences.insert(sequences.begin(), CIncomingSequence(instate, m_nInSequenceNr, g_GlobalVars->realtime));
|
||||
}
|
||||
if (sequences.size() > 2048)
|
||||
sequences.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
// Get How many ticks we should Store and use
|
||||
int Backtrack::getTicks()
|
||||
{
|
||||
float max_lat = getLatency() + 200.0f;
|
||||
|
||||
// Clamp
|
||||
max_lat = std::min(1000.0f, max_lat);
|
||||
|
||||
// Get difference
|
||||
int ticks = TIME_TO_TICKS(max_lat / 1000.0f);
|
||||
return ticks;
|
||||
};
|
||||
|
||||
void Backtrack::resetData(int entidx)
|
||||
{
|
||||
// Clear everything
|
||||
backtrack_data[entidx].reset();
|
||||
}
|
||||
|
||||
bool Backtrack::isGoodTick(BacktrackData &tick)
|
||||
{
|
||||
// This tick hasn't updated since the last one, Entity might be dropping packets
|
||||
if (!tick.has_updated)
|
||||
return false;
|
||||
// How big a difference is between the ping we fake and the tick passed?
|
||||
int delta_ticks = current_tickcount - TIME_TO_TICKS(getLatency() / 1000.0f) - tick.tickcount;
|
||||
|
||||
// Difference may not be greater than 200ms, shave off a few ticks just to be safe.
|
||||
if (fabsf(TICKS_TO_TIME(delta_ticks)) <= 0.2f - TICKS_TO_TIME(3))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read only vector of good ticks
|
||||
std::vector<BacktrackData> Backtrack::getGoodTicks(int entidx)
|
||||
{
|
||||
std::vector<BacktrackData> to_return;
|
||||
// Invalid
|
||||
if (!backtrack_data.at(entidx))
|
||||
return to_return;
|
||||
|
||||
// Check all ticks
|
||||
for (auto &tick : *backtrack_data.at(entidx))
|
||||
if (isGoodTick(tick))
|
||||
to_return.push_back(tick);
|
||||
|
||||
// Sort so that oldest ticks come first
|
||||
std::sort(to_return.begin(), to_return.end(), [](BacktrackData &a, BacktrackData &b) { return a.tickcount < b.tickcount; });
|
||||
|
||||
return to_return;
|
||||
}
|
||||
|
||||
// This function is so other files can Easily get the best tick matching their criteria
|
||||
std::optional<BacktrackData> Backtrack::getBestTick(CachedEntity *ent, std::function<bool(CachedEntity *ent, BacktrackData &data, std::optional<BacktrackData> &best_tick)> callback)
|
||||
{
|
||||
std::optional<BacktrackData> best_tick;
|
||||
|
||||
// No data recorded
|
||||
if (!backtrack_data.at(ent->m_IDX))
|
||||
return best_tick;
|
||||
|
||||
// Let the callback do the lifting
|
||||
for (auto &tick : getGoodTicks(ent->m_IDX))
|
||||
// Call the callback, and if we have a new best tick assign it
|
||||
if (callback(ent, tick, best_tick))
|
||||
best_tick = tick;
|
||||
|
||||
// Return best result
|
||||
return best_tick;
|
||||
}
|
||||
|
||||
// Default filter method. Checks for vischeck on Hitscan weapons.
|
||||
bool Backtrack::defaultTickFilter(CachedEntity *ent, BacktrackData tick)
|
||||
{
|
||||
// Not hitscan, no vischeck needed
|
||||
if (g_pLocalPlayer->weapon_mode != weapon_hitscan)
|
||||
return true;
|
||||
// Return visibility
|
||||
return IsEntityVectorVisible(ent, tick.hitboxes.at(head).center, MASK_SHOT);
|
||||
}
|
||||
|
||||
bool Backtrack::defaultEntFilter(CachedEntity *ent)
|
||||
{
|
||||
// Dormant
|
||||
if (CE_BAD(ent))
|
||||
return false;
|
||||
// Friend check
|
||||
if (playerlist::IsFriend(ent))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void EmptyBacktrackData(BacktrackData &i)
|
||||
// Get Closest tick of a specific entity
|
||||
std::optional<BacktrackData> Backtrack::getClosestEntTick(CachedEntity *ent, Vector vec, std::function<bool(CachedEntity *, BacktrackData)> tick_filter)
|
||||
{
|
||||
i = {};
|
||||
}
|
||||
std::optional<BacktrackData> return_value;
|
||||
// No entry
|
||||
if (!backtrack_data.at(ent->m_IDX))
|
||||
return return_value;
|
||||
|
||||
// This func is internal only
|
||||
std::pair<int, int> getBestEntBestTick()
|
||||
{
|
||||
int bestEnt = -1;
|
||||
int bestTick = -1;
|
||||
bool vischeck_priority = false;
|
||||
Vischeck_Success = false;
|
||||
if (GetWeaponMode() == weapon_melee)
|
||||
float distance = FLT_MAX;
|
||||
|
||||
// Go through all Good ticks
|
||||
for (auto &tick : getGoodTicks(ent->m_IDX))
|
||||
{
|
||||
float bestDist = 9999.0f;
|
||||
for (int i = 0; i <= g_IEngine->GetMaxClients(); i++)
|
||||
// Found Closer tick
|
||||
if (tick.m_vecOrigin.DistTo(vec) < distance)
|
||||
{
|
||||
CachedEntity *tar = ENTITY(i);
|
||||
if (CE_GOOD(tar))
|
||||
// Does the tick pass the filter
|
||||
if (tick_filter(ent, tick))
|
||||
{
|
||||
if (tar != LOCAL_E && tar->m_bEnemy())
|
||||
{
|
||||
|
||||
for (int j = 0; j < getTicks(); j++)
|
||||
{
|
||||
if (ValidTick(headPositions[i][j], ENTITY(i)))
|
||||
{
|
||||
float dist = headPositions[i][j].hitboxes.at(spine_3).center.DistTo(g_pLocalPlayer->v_Eye);
|
||||
if (dist < bestDist)
|
||||
{
|
||||
bestEnt = i;
|
||||
bestTick = j;
|
||||
bestDist = dist;
|
||||
Vischeck_Success = true;
|
||||
vischeck_priority = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return_value = tick;
|
||||
distance = tick.m_vecOrigin.DistTo(vec);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
return return_value;
|
||||
}
|
||||
|
||||
// Get Closest tick of any (enemy) entity, Second Parameter is to allow custom filters for entity criteria, third for ticks. We provide defaults for vischecks + melee for the second one
|
||||
std::optional<std::pair<CachedEntity *, BacktrackData>> Backtrack::getClosestTick(Vector vec, std::function<bool(CachedEntity *)> ent_filter, std::function<bool(CachedEntity *, BacktrackData)> tick_filter)
|
||||
{
|
||||
float distance = FLT_MAX;
|
||||
CachedEntity *best_ent = nullptr;
|
||||
BacktrackData best_data;
|
||||
|
||||
std::optional<std::pair<CachedEntity *, BacktrackData>> return_val;
|
||||
|
||||
for (int i = 0; i <= g_IEngine->GetMaxClients(); i++)
|
||||
{
|
||||
float bestFov = 180.0f;
|
||||
for (int i = 0; i <= g_IEngine->GetMaxClients(); i++)
|
||||
CachedEntity *ent = ENTITY(i);
|
||||
// These checks are always present
|
||||
if (CE_INVALID(ent) || !ent->m_bAlivePlayer() || !ent->m_bEnemy())
|
||||
continue;
|
||||
// true = passes check
|
||||
if (!ent_filter(ent))
|
||||
continue;
|
||||
auto closest_entdata = getClosestEntTick(ent, vec, tick_filter);
|
||||
// Closer than the stuff we have
|
||||
if (closest_entdata && (*closest_entdata).m_vecOrigin.DistTo(vec) <= distance)
|
||||
{
|
||||
CachedEntity *tar = ENTITY(i);
|
||||
if (CE_GOOD(tar))
|
||||
{
|
||||
if (tar != LOCAL_E && tar->m_bEnemy())
|
||||
{
|
||||
for (int j = 0; j < getTicks(); j++)
|
||||
{
|
||||
if (ValidTick(headPositions[i][j], tar))
|
||||
{
|
||||
float FOVDistance = GetFov(g_pLocalPlayer->v_OrigViewangles, g_pLocalPlayer->v_Eye, headPositions[i][j].hitboxes.at(head).center);
|
||||
if (FOVDistance > bestFov && vischeck_priority)
|
||||
continue;
|
||||
bool Vischeck_suceeded = IsVectorVisible(g_pLocalPlayer->v_Eye, headPositions[i][j].hitboxes.at(0).center, true);
|
||||
if (FOVDistance < bestFov || (Vischeck_suceeded && !vischeck_priority))
|
||||
{
|
||||
bestEnt = i;
|
||||
bestTick = j;
|
||||
bestFov = FOVDistance;
|
||||
if (Vischeck_suceeded)
|
||||
{
|
||||
Vischeck_Success = true;
|
||||
vischeck_priority = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
distance = (*closest_entdata).m_vecOrigin.DistTo(vec);
|
||||
best_data = *closest_entdata;
|
||||
best_ent = ent;
|
||||
}
|
||||
}
|
||||
return std::make_pair(bestEnt, bestTick);
|
||||
if (best_ent)
|
||||
return_val = std::pair<CachedEntity *, BacktrackData>(best_ent, best_data);
|
||||
return return_val;
|
||||
}
|
||||
static InitRoutine EC([]() {
|
||||
EC::Register(EC::LevelInit, Init, "INIT_Backtrack", EC::average);
|
||||
EC::Register(EC::CreateMove, Run, "CM_Backtrack", EC::early);
|
||||
|
||||
static InitRoutine init([]() {
|
||||
EC::Register(EC::CreateMove, std::bind(&Backtrack::CreateMove, &g_IBacktrack), "backtrack_cm", EC::early);
|
||||
EC::Register(EC::CreateMove, std::bind(&Backtrack::CreateMoveLate, &g_IBacktrack), "backtrack_cmlate", EC::very_late);
|
||||
#if ENABLE_VISUALS
|
||||
EC::Register(EC::Draw, Draw, "DRAW_Backtrack", EC::average);
|
||||
EC::Register(EC::Draw, std::bind(&Backtrack::Draw, &g_IBacktrack), "backtrack_draw");
|
||||
#endif
|
||||
EC::Register(EC::LevelShutdown, std::bind(&Backtrack::LevelShutdown, &g_IBacktrack), "backtrack_levelshutdown");
|
||||
});
|
||||
static CatCommand debug_flowout("debug_flowout", "test", []() {
|
||||
auto ch = (INetChannel *) g_IEngine->GetNetChannelInfo();
|
||||
logging::Info("Out Avg: %f In Avg: %f Out current: %f In Current: %f", 1000.0f * ch->GetAvgLatency(FLOW_OUTGOING), 1000.0f * ch->GetAvgLatency(FLOW_INCOMING), 1000.0f * ch->GetLatency(FLOW_OUTGOING), 1000.0f * ch->GetLatency(FLOW_INCOMING));
|
||||
});
|
||||
static CatCommand debug_richpresence("debug_presence", "Debug stuff", []() {
|
||||
g_ISteamFriends->SetRichPresence("steam_display", "#TF_RichPresence_State_PlayingGeneric");
|
||||
g_ISteamFriends->SetRichPresence("currentmap", "Cathooking");
|
||||
});
|
||||
} // namespace hacks::shared::backtrack
|
||||
} // namespace hacks::tf2::backtrack
|
||||
// Global interface
|
||||
hacks::tf2::backtrack::Backtrack g_IBacktrack;
|
||||
|
@ -54,8 +54,17 @@ std::pair<CachedEntity *, Vector> FindBestEnt(bool teammate, bool Predict, bool
|
||||
if (zcheck && (ent->m_vecOrigin().z - LOCAL_E->m_vecOrigin().z) > 200.0f)
|
||||
continue;
|
||||
float scr = ent->m_flDistance();
|
||||
if (g_IBacktrack.isBacktrackEnabled && demoknight_mode)
|
||||
{
|
||||
auto data = g_IBacktrack.getClosestEntTick(ent, LOCAL_E->m_vecOrigin(), std::bind(&hacks::tf2::backtrack::Backtrack::defaultTickFilter, &g_IBacktrack, std::placeholders::_1, std::placeholders::_2));
|
||||
// No entity
|
||||
if (!data)
|
||||
scr = FLT_MAX;
|
||||
else
|
||||
scr = (*data).m_vecOrigin.DistTo(LOCAL_E->m_vecOrigin());
|
||||
}
|
||||
// Demoknight
|
||||
if (fov_check)
|
||||
if (demoknight_mode)
|
||||
{
|
||||
if (scr >= range)
|
||||
continue;
|
||||
@ -100,8 +109,17 @@ std::pair<CachedEntity *, Vector> FindBestEnt(bool teammate, bool Predict, bool
|
||||
if (zcheck && (ent->m_vecOrigin().z - LOCAL_E->m_vecOrigin().z) > 200.0f)
|
||||
continue;
|
||||
float scr = ent->m_flDistance();
|
||||
if (g_IBacktrack.isBacktrackEnabled && demoknight_mode)
|
||||
{
|
||||
auto data = g_IBacktrack.getClosestEntTick(ent, LOCAL_E->m_vecOrigin(), std::bind(&hacks::tf2::backtrack::Backtrack::defaultTickFilter, &g_IBacktrack, std::placeholders::_1, std::placeholders::_2));
|
||||
// No entity
|
||||
if (!data)
|
||||
scr = FLT_MAX;
|
||||
else
|
||||
scr = (*data).m_vecOrigin.DistTo(LOCAL_E->m_vecOrigin());
|
||||
}
|
||||
// Demoknight
|
||||
if (fov_check)
|
||||
if (demoknight_mode)
|
||||
{
|
||||
if (scr >= range)
|
||||
continue;
|
||||
|
@ -38,57 +38,50 @@ float target_time = 0.0f;
|
||||
|
||||
int last_hb_traced = 0;
|
||||
Vector forward;
|
||||
void DoBacktrack()
|
||||
|
||||
// Filters for backtrack
|
||||
bool tick_filter(CachedEntity *entity, hacks::tf2::backtrack::BacktrackData tick)
|
||||
{
|
||||
namespace bt = hacks::shared::backtrack;
|
||||
// Check if it intersects any hitbox
|
||||
int num_hitboxes = 18;
|
||||
|
||||
CachedEntity *tar = (bt::iBestTarget != -1) ? ENTITY(bt::iBestTarget) : nullptr;
|
||||
if (CE_BAD(tar))
|
||||
return;
|
||||
if (bt::BestTick == -1)
|
||||
return;
|
||||
if (!IsTargetStateGood(tar, true))
|
||||
return;
|
||||
auto &tick = bt::headPositions[bt::iBestTarget][bt::BestTick];
|
||||
|
||||
if (!ValidTick(tick, tar))
|
||||
return;
|
||||
auto min = tick.hitboxes.at(head).min;
|
||||
auto max = tick.hitboxes.at(head).max;
|
||||
if (!min.x && !max.x)
|
||||
return;
|
||||
|
||||
// Get the min and max for the hitbox
|
||||
Vector minz(fminf(min.x, max.x), fminf(min.y, max.y), fminf(min.z, max.z));
|
||||
Vector maxz(fmaxf(min.x, max.x), fmaxf(min.y, max.y), fmaxf(min.z, max.z));
|
||||
|
||||
// Shrink the hitbox here
|
||||
Vector size = maxz - minz;
|
||||
Vector smod = size * 0.05f * (int) accuracy;
|
||||
|
||||
// Save the changes to the vectors
|
||||
minz += smod;
|
||||
maxz -= smod;
|
||||
|
||||
// Trace and test if it hits the smaller hitbox, if it fails
|
||||
// we
|
||||
// return false
|
||||
Vector hit;
|
||||
|
||||
if (!IsVectorVisible(g_pLocalPlayer->v_Eye, minz) && !IsVectorVisible(g_pLocalPlayer->v_Eye, maxz))
|
||||
return;
|
||||
if (CheckLineBox(minz, maxz, g_pLocalPlayer->v_Eye, forward, hit))
|
||||
// Only need head hitbox
|
||||
if (HeadPreferable(entity))
|
||||
num_hitboxes = 1;
|
||||
for (int i = 0; i < num_hitboxes; i++)
|
||||
{
|
||||
current_user_cmd->tick_count = tick.tickcount;
|
||||
current_user_cmd->buttons |= IN_ATTACK;
|
||||
return;
|
||||
auto hitbox = tick.hitboxes.at(i);
|
||||
auto min = hitbox.min;
|
||||
auto max = hitbox.max;
|
||||
// Get the min and max for the hitbox
|
||||
Vector minz(fminf(min.x, max.x), fminf(min.y, max.y), fminf(min.z, max.z));
|
||||
Vector maxz(fmaxf(min.x, max.x), fmaxf(min.y, max.y), fmaxf(min.z, max.z));
|
||||
|
||||
// Shrink the hitbox here
|
||||
Vector size = maxz - minz;
|
||||
// Use entire hitbox if accuracy unset
|
||||
Vector smod = size * 0.05f * (*accuracy > 0 ? *accuracy : 20);
|
||||
|
||||
// Save the changes to the vectors
|
||||
minz += smod;
|
||||
maxz -= smod;
|
||||
|
||||
// Trace and test if it hits the smaller hitbox, if it hits we can return true
|
||||
Vector hit;
|
||||
// Found a good one
|
||||
if (CheckLineBox(minz, maxz, g_pLocalPlayer->v_Eye, forward, hit))
|
||||
{
|
||||
// Is tick visible
|
||||
if (IsVectorVisible(g_pLocalPlayer->v_Eye, hitbox.center))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// The main function of the triggerbot
|
||||
void CreateMove()
|
||||
{
|
||||
|
||||
float backup_time = target_time;
|
||||
target_time = 0;
|
||||
|
||||
@ -99,26 +92,51 @@ void CreateMove()
|
||||
// Reset our last hitbox traced
|
||||
last_hb_traced = -1;
|
||||
|
||||
// Get and ent in front of the player
|
||||
CachedEntity *ent = FindEntInSight(EffectiveTargetingRange());
|
||||
// Get an ent in front of the player
|
||||
CachedEntity *ent = nullptr;
|
||||
std::optional<hacks::tf2::backtrack::BacktrackData> bt_data;
|
||||
|
||||
// Check if can backtrack, shoot if we can
|
||||
if (hacks::shared::backtrack::isBacktrackEnabled)
|
||||
if (!g_IBacktrack.isBacktrackEnabled)
|
||||
ent = FindEntInSight(EffectiveTargetingRange());
|
||||
// Backtrack, use custom filter to check if tick is in crosshair
|
||||
else
|
||||
{
|
||||
// We need to return because we can't hit non backtrackable ticks if we
|
||||
// have backtrack latency.
|
||||
DoBacktrack();
|
||||
return;
|
||||
// Set up forward Vector
|
||||
float sp, sy, cp, cy;
|
||||
QAngle angle;
|
||||
g_IEngine->GetViewAngles(angle);
|
||||
sy = sinf(DEG2RAD(angle[1]));
|
||||
cy = cosf(DEG2RAD(angle[1]));
|
||||
sp = sinf(DEG2RAD(angle[0]));
|
||||
cp = cosf(DEG2RAD(angle[0]));
|
||||
forward.x = cp * cy;
|
||||
forward.y = cp * sy;
|
||||
forward.z = -sp;
|
||||
forward = forward * EffectiveTargetingRange() + g_pLocalPlayer->v_Eye;
|
||||
|
||||
// Call closest tick with our Tick filter func
|
||||
auto closest_data = g_IBacktrack.getClosestTick(g_pLocalPlayer->v_Eye, std::bind(&hacks::tf2::backtrack::Backtrack::defaultEntFilter, &g_IBacktrack, std::placeholders::_1), tick_filter);
|
||||
|
||||
// No results, try to grab a building
|
||||
if (!closest_data)
|
||||
{
|
||||
ent = FindEntInSight(EffectiveTargetingRange(), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Assign entity
|
||||
ent = (*closest_data).first;
|
||||
bt_data = (*closest_data).second;
|
||||
g_IBacktrack.SetBacktrackData(ent, *bt_data);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if dormant or null to prevent crashes
|
||||
if (CE_BAD(ent))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine whether the triggerbot should shoot, then act accordingly
|
||||
if (IsTargetStateGood(ent))
|
||||
if (IsTargetStateGood(ent, bt_data))
|
||||
{
|
||||
target_time = backup_time;
|
||||
if (delay)
|
||||
@ -224,9 +242,8 @@ bool ShouldShoot()
|
||||
}
|
||||
|
||||
// A second check to determine whether a target is good enough to be aimed at
|
||||
bool IsTargetStateGood(CachedEntity *entity, bool backtrack)
|
||||
bool IsTargetStateGood(CachedEntity *entity, std::optional<hacks::tf2::backtrack::BacktrackData> tick)
|
||||
{
|
||||
|
||||
// Check for Players
|
||||
if (entity->m_Type() == ENTITY_PLAYER)
|
||||
{
|
||||
@ -273,44 +290,47 @@ bool IsTargetStateGood(CachedEntity *entity, bool backtrack)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Head hitbox detection
|
||||
if (HeadPreferable(entity) && !backtrack)
|
||||
// Backtrack did these in the tick check
|
||||
if (!tick)
|
||||
{
|
||||
if (last_hb_traced != hitbox_t::head)
|
||||
return false;
|
||||
}
|
||||
|
||||
// If usersettings tell us to use accuracy improvements and the cached
|
||||
// hitbox isnt null, then we check if it hits here
|
||||
if (*accuracy && !backtrack)
|
||||
{
|
||||
|
||||
// Get a cached hitbox for the one traced
|
||||
hitbox_cache::CachedHitbox *hb = entity->hitboxes.GetHitbox(last_hb_traced);
|
||||
// Check for null
|
||||
if (hb)
|
||||
// Head hitbox detection
|
||||
if (HeadPreferable(entity))
|
||||
{
|
||||
// Get the min and max for the hitbox
|
||||
Vector minz(fminf(hb->min.x, hb->max.x), fminf(hb->min.y, hb->max.y), fminf(hb->min.z, hb->max.z));
|
||||
Vector maxz(fmaxf(hb->min.x, hb->max.x), fmaxf(hb->min.y, hb->max.y), fmaxf(hb->min.z, hb->max.z));
|
||||
|
||||
// Shrink the hitbox here
|
||||
Vector size = maxz - minz;
|
||||
Vector smod = size * 0.05f * *accuracy;
|
||||
|
||||
// Save the changes to the vectors
|
||||
minz += smod;
|
||||
maxz -= smod;
|
||||
|
||||
// Trace and test if it hits the smaller hitbox, if it fails
|
||||
// we
|
||||
// return false
|
||||
Vector hit;
|
||||
if (!CheckLineBox(minz, maxz, g_pLocalPlayer->v_Eye, forward, hit))
|
||||
if (last_hb_traced != hitbox_t::head)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If usersettings tell us to use accuracy improvements and the cached
|
||||
// hitbox isnt null, then we check if it hits here
|
||||
if (*accuracy)
|
||||
{
|
||||
|
||||
// Get a cached hitbox for the one traced
|
||||
hitbox_cache::CachedHitbox *hb = entity->hitboxes.GetHitbox(last_hb_traced);
|
||||
// Check for null
|
||||
if (hb)
|
||||
{
|
||||
// Get the min and max for the hitbox
|
||||
Vector minz(fminf(hb->min.x, hb->max.x), fminf(hb->min.y, hb->max.y), fminf(hb->min.z, hb->max.z));
|
||||
Vector maxz(fmaxf(hb->min.x, hb->max.x), fmaxf(hb->min.y, hb->max.y), fmaxf(hb->min.z, hb->max.z));
|
||||
|
||||
// Shrink the hitbox here
|
||||
Vector size = maxz - minz;
|
||||
Vector smod = size * 0.05f * *accuracy;
|
||||
|
||||
// Save the changes to the vectors
|
||||
minz += smod;
|
||||
maxz -= smod;
|
||||
|
||||
// Trace and test if it hits the smaller hitbox, if it fails
|
||||
// we
|
||||
// return false
|
||||
Vector hit;
|
||||
if (!CheckLineBox(minz, maxz, g_pLocalPlayer->v_Eye, forward, hit))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Target passed the tests so return true
|
||||
return true;
|
||||
|
||||
@ -376,9 +396,8 @@ bool IsTargetStateGood(CachedEntity *entity, bool backtrack)
|
||||
}
|
||||
|
||||
// A function to return a potential entity in front of the player
|
||||
CachedEntity *FindEntInSight(float range)
|
||||
CachedEntity *FindEntInSight(float range, bool no_players)
|
||||
{
|
||||
|
||||
// We dont want to hit ourself so we set an ignore
|
||||
trace_t trace;
|
||||
trace::filter_default.SetSelf(RAW_ENT(g_pLocalPlayer->entity));
|
||||
@ -407,9 +426,11 @@ CachedEntity *FindEntInSight(float range)
|
||||
// Return an ent if that is what we hit
|
||||
if (trace.m_pEnt)
|
||||
{
|
||||
|
||||
last_hb_traced = trace.hitbox;
|
||||
return ENTITY(((IClientEntity *) trace.m_pEnt)->entindex());
|
||||
last_hb_traced = trace.hitbox;
|
||||
CachedEntity *ent = ENTITY(((IClientEntity *) trace.m_pEnt)->entindex());
|
||||
// Player check
|
||||
if (!no_players || ent->m_Type() != ENTITY_PLAYER)
|
||||
return ent;
|
||||
}
|
||||
|
||||
// Since we didnt hit and entity, the vis check failed so return 0
|
||||
|
@ -395,14 +395,13 @@ DEFINE_HOOKED_METHOD(CreateMove, bool, void *this_, float input_sample_time, CUs
|
||||
// LoadSavedState();
|
||||
}
|
||||
g_Settings.is_create_move = false;
|
||||
if (nolerp && !hacks::shared::backtrack::isBacktrackEnabled)
|
||||
if (nolerp)
|
||||
{
|
||||
static const ConVar *pUpdateRate = g_pCVar->FindVar("cl_updaterate");
|
||||
if (!pUpdateRate)
|
||||
pUpdateRate = g_pCVar->FindVar("cl_updaterate");
|
||||
else
|
||||
{
|
||||
|
||||
float interp = MAX(cl_interp->GetFloat(), cl_interp_ratio->GetFloat() / pUpdateRate->GetFloat());
|
||||
cmd->tick_count += TIME_TO_TICKS(interp);
|
||||
}
|
||||
|
@ -26,8 +26,6 @@ namespace hooked_methods
|
||||
|
||||
DEFINE_HOOKED_METHOD(LevelInit, void, void *this_, const char *name)
|
||||
{
|
||||
hacks::shared::backtrack::lastincomingsequencenumber = 0;
|
||||
hacks::shared::backtrack::sequences.clear();
|
||||
firstcm = true;
|
||||
// nav::init = false;
|
||||
playerlist::Save();
|
||||
|
@ -5,17 +5,18 @@
|
||||
* Author: bencat07
|
||||
*/
|
||||
#include "HookedMethods.hpp"
|
||||
#include "hacks/Backtrack.hpp"
|
||||
#include "Backtrack.hpp"
|
||||
namespace hooked_methods
|
||||
{
|
||||
DEFINE_HOOKED_METHOD(SendDatagram, int, INetChannel *ch, bf_write *buf)
|
||||
{
|
||||
if (!isHackActive() || !ch || CE_BAD(LOCAL_E) || !round(*hacks::shared::backtrack::latency))
|
||||
if (!isHackActive() || !ch || CE_BAD(LOCAL_E) || std::floor(*g_IBacktrack.latency) == 0)
|
||||
return original::SendDatagram(ch, buf);
|
||||
|
||||
int in = ch->m_nInSequenceNr;
|
||||
int state = ch->m_nInReliableState;
|
||||
hacks::shared::backtrack::AddLatencyToNetchan(ch);
|
||||
// Do backtrack things
|
||||
g_IBacktrack.adjustPing(ch);
|
||||
|
||||
int ret = original::SendDatagram(ch, buf);
|
||||
ch->m_nInSequenceNr = in;
|
||||
|
@ -22,10 +22,6 @@ namespace effect_chams
|
||||
{
|
||||
extern settings::Boolean enable;
|
||||
} // namespace effect_chams
|
||||
namespace hacks::shared::backtrack
|
||||
{
|
||||
extern settings::Boolean backtrack_chams_glow;
|
||||
}
|
||||
namespace hooked_methods
|
||||
{
|
||||
// Global scope so we can deconstruct on shutdown
|
||||
@ -49,7 +45,7 @@ DEFINE_HOOKED_METHOD(DrawModelExecute, void, IVModelRender *this_, const DrawMod
|
||||
if (!isHackActive())
|
||||
return original::DrawModelExecute(this_, state, info, bone);
|
||||
|
||||
if (!(hacks::shared::backtrack::isBacktrackEnabled /*|| (hacks::shared::antiaim::force_fakelag && hacks::shared::antiaim::isEnabled())*/ || spectator_target || no_arms || no_hats || (*clean_screenshots && g_IEngine->IsTakingScreenshot()) || CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer()))
|
||||
if (!(g_IBacktrack.isBacktrackEnabled /*|| (hacks::shared::antiaim::force_fakelag && hacks::shared::antiaim::isEnabled())*/ || spectator_target || no_arms || no_hats || (*clean_screenshots && g_IEngine->IsTakingScreenshot()) || CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer()))
|
||||
{
|
||||
return original::DrawModelExecute(this_, state, info, bone);
|
||||
}
|
||||
@ -118,7 +114,7 @@ DEFINE_HOOKED_METHOD(DrawModelExecute, void, IVModelRender *this_, const DrawMod
|
||||
aa_draw = false;
|
||||
angles.y = backup;
|
||||
}*/
|
||||
if (hacks::shared::backtrack::isBacktrackEnabled && hacks::shared::backtrack::backtrack_chams_glow)
|
||||
if (g_IBacktrack.isBacktrackEnabled && g_IBacktrack.chams)
|
||||
{
|
||||
const char *name = g_IModelInfo->GetModelName(info.pModel);
|
||||
if (name)
|
||||
@ -126,48 +122,48 @@ DEFINE_HOOKED_METHOD(DrawModelExecute, void, IVModelRender *this_, const DrawMod
|
||||
std::string sname = name;
|
||||
if (sname.find("models/player") || sname.find("models/weapons") || sname.find("models/workshop/player") || sname.find("models/workshop/weapons"))
|
||||
{
|
||||
|
||||
if (IDX_GOOD(info.entity_index) && info.entity_index <= g_IEngine->GetMaxClients() && info.entity_index != g_IEngine->GetLocalPlayer())
|
||||
{
|
||||
CachedEntity *ent = ENTITY(info.entity_index);
|
||||
if (CE_GOOD(ent) && ent->m_bAlivePlayer())
|
||||
{
|
||||
// Backup Blend
|
||||
float orig_blend = g_IVRenderView->GetBlend();
|
||||
// Make Backtrack stuff seethrough
|
||||
g_IVRenderView->SetBlend(1.0f);
|
||||
// Get Backtrack data for target entity
|
||||
auto head_pos = hacks::shared::backtrack::headPositions[info.entity_index];
|
||||
// Usable vector instead of ptr to c style array, also used to filter valid and invalid ticks
|
||||
std::vector<hacks::shared::backtrack::BacktrackData> usable;
|
||||
for (int i = 0; i < 66; i++)
|
||||
auto good_ticks = g_IBacktrack.getGoodTicks(info.entity_index);
|
||||
|
||||
// Check if valid
|
||||
if (!good_ticks.empty())
|
||||
{
|
||||
if (hacks::shared::backtrack::ValidTick(head_pos[i], ent))
|
||||
usable.push_back(head_pos[i]);
|
||||
}
|
||||
// Crash much?
|
||||
if (usable.size())
|
||||
{
|
||||
// Sort
|
||||
std::sort(usable.begin(), usable.end(), [](hacks::shared::backtrack::BacktrackData &a, hacks::shared::backtrack::BacktrackData &b) { return a.tickcount < b.tickcount; });
|
||||
// Make our own Chamsish Material
|
||||
// Render Chams/Glow stuff
|
||||
CMatRenderContextPtr ptr(GET_RENDER_CONTEXT);
|
||||
// Backup Blend
|
||||
float orig_blend = g_IVRenderView->GetBlend();
|
||||
// Make Backtrack stuff Solid color
|
||||
g_IVRenderView->SetBlend(1.0f);
|
||||
|
||||
rgba_t mod_original;
|
||||
// Save color just in case, then set to white
|
||||
// Save color just in case, then set to team color
|
||||
g_IVRenderView->GetColorModulation(mod_original.rgba);
|
||||
g_IVRenderView->SetColorModulation(colors::white);
|
||||
g_IVRenderView->SetColorModulation(g_IBacktrack.team_color ? colors::EntityF(ent) : colors::FromRGBA8(100, 100, 100, 255));
|
||||
// Important for Depth
|
||||
ptr->DepthRange(0.0f, 1.0f);
|
||||
// Apply our material
|
||||
g_IVModelRender->ForcedMaterialOverride(mat_dme_chams);
|
||||
// Run Original
|
||||
original::DrawModelExecute(this_, state, info, usable[0].bones);
|
||||
if (!g_IBacktrack.team_color)
|
||||
g_IVModelRender->ForcedMaterialOverride(mat_dme_chams);
|
||||
|
||||
// Draw as many ticks as desired
|
||||
for (unsigned i = 0; i <= (unsigned) std::max(*g_IBacktrack.chams_ticks, 1); i++)
|
||||
{
|
||||
// Can't draw more than we have
|
||||
if (i >= good_ticks.size())
|
||||
break;
|
||||
original::DrawModelExecute(this_, state, info, good_ticks[i].bones);
|
||||
}
|
||||
// Revert
|
||||
g_IVRenderView->SetColorModulation(mod_original.rgba);
|
||||
g_IVModelRender->ForcedMaterialOverride(nullptr);
|
||||
g_IVRenderView->SetBlend(orig_blend);
|
||||
}
|
||||
g_IVRenderView->SetBlend(orig_blend);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user