Rewrite backtrack
Increases hitrate immensely, also aims to lower code complexity (for modules like aimbot and triggerbot) and does "proper" backtracking, aka teleporting the entity back to where they were originally
This commit is contained in:
parent
3ef56a4e95
commit
71f1cb2189
@ -46,6 +46,8 @@ public:
|
|||||||
offset_t iFlags;
|
offset_t iFlags;
|
||||||
offset_t iHealth;
|
offset_t iHealth;
|
||||||
|
|
||||||
|
offset_t m_vecOrigin;
|
||||||
|
|
||||||
// sentry
|
// sentry
|
||||||
offset_t m_iAmmoShells; // sentry shells
|
offset_t m_iAmmoShells; // sentry shells
|
||||||
offset_t m_iAmmoRockets; // use only with if (GetLevel() == 3)
|
offset_t m_iAmmoRockets; // use only with if (GetLevel() == 3)
|
||||||
@ -184,6 +186,7 @@ public:
|
|||||||
offset_t m_nSequence;
|
offset_t m_nSequence;
|
||||||
offset_t m_flSimulationTime;
|
offset_t m_flSimulationTime;
|
||||||
offset_t m_flAnimTime;
|
offset_t m_flAnimTime;
|
||||||
|
offset_t m_flCycle;
|
||||||
offset_t m_angRotation;
|
offset_t m_angRotation;
|
||||||
|
|
||||||
offset_t m_hOwnerEntity;
|
offset_t m_hOwnerEntity;
|
||||||
|
@ -102,5 +102,8 @@ enum hitbox_t
|
|||||||
foot_L = 14,
|
foot_L = 14,
|
||||||
hip_R = 15,
|
hip_R = 15,
|
||||||
knee_R = 16,
|
knee_R = 16,
|
||||||
foot_R = 17
|
foot_R = 17,
|
||||||
|
|
||||||
|
// Always last
|
||||||
|
HITBOXES_SIZE
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
|
#include "entityhitboxcache.hpp"
|
||||||
|
|
||||||
namespace hacks::tf2::backtrack
|
namespace hacks::tf2::backtrack
|
||||||
{
|
{
|
||||||
@ -18,75 +19,45 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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
|
class BacktrackData
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
int tickcount{};
|
int tickcount{};
|
||||||
|
int entidx{};
|
||||||
|
|
||||||
std::array<hitboxData, 18> hitboxes{};
|
bool in_range{};
|
||||||
Vector m_vecOrigin{};
|
|
||||||
Vector m_vecAngles{};
|
|
||||||
|
|
||||||
Vector m_vecMins{};
|
|
||||||
Vector m_vecMaxs{};
|
|
||||||
|
|
||||||
float m_flSimulationTime{};
|
|
||||||
bool has_updated{};
|
|
||||||
|
|
||||||
std::vector<matrix3x4_t> bones{};
|
std::vector<matrix3x4_t> bones{};
|
||||||
|
|
||||||
|
Vector m_vecOrigin{};
|
||||||
|
Vector m_vecAngles{};
|
||||||
|
float simtime;
|
||||||
|
float animtime;
|
||||||
|
float cycle;
|
||||||
|
int sequence;
|
||||||
|
std::array<hitbox_cache::CachedHitbox, HITBOXES_SIZE> hitboxes{};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Stuff that has to be accessible from outside, mostly functions
|
|
||||||
|
|
||||||
extern settings::Float latency;
|
extern settings::Float latency;
|
||||||
extern settings::Int bt_slots;
|
void adjustPing(INetChannel *ch);
|
||||||
|
bool backtrackEnabled();
|
||||||
|
bool isTickInRange(int tickcount);
|
||||||
|
void MoveToTick(BacktrackData data);
|
||||||
|
void RestoreEntity(int entidx);
|
||||||
|
bool hasData();
|
||||||
|
std::optional<BacktrackData> getData();
|
||||||
|
|
||||||
|
extern std::vector<std::vector<BacktrackData>> bt_data;
|
||||||
|
|
||||||
#if ENABLE_VISUALS
|
#if ENABLE_VISUALS
|
||||||
extern settings::Boolean chams;
|
extern settings::Boolean chams;
|
||||||
extern settings::Boolean chams_wireframe;
|
extern settings::Boolean chams_wireframe;
|
||||||
extern settings::Int chams_ticks;
|
extern settings::Int chams_ticks;
|
||||||
extern settings::Rgba chams_color;
|
extern settings::Rgba chams_color;
|
||||||
extern settings::Boolean chams_solid;
|
|
||||||
extern settings::Boolean chams_overlay;
|
extern settings::Boolean chams_overlay;
|
||||||
extern settings::Rgba chams_color_overlay;
|
extern settings::Rgba chams_color_overlay;
|
||||||
extern settings::Float chams_envmap_tint_r;
|
extern settings::Float chams_envmap_tint_r;
|
||||||
extern settings::Float chams_envmap_tint_g;
|
extern settings::Float chams_envmap_tint_g;
|
||||||
extern settings::Float chams_envmap_tint_b;
|
extern settings::Float chams_envmap_tint_b;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Check if backtrack is enabled
|
|
||||||
extern bool isBacktrackEnabled;
|
|
||||||
#if ENABLE_VISUALS
|
|
||||||
// Drawing Backtrack chams
|
|
||||||
extern bool isDrawing;
|
|
||||||
#endif
|
|
||||||
// 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
|
} // namespace hacks::tf2::backtrack
|
||||||
|
@ -8,17 +8,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
|
#include "Backtrack.hpp"
|
||||||
|
|
||||||
namespace hacks::tf2::backtrack
|
|
||||||
{
|
|
||||||
class BacktrackData;
|
|
||||||
}
|
|
||||||
namespace hacks::shared::triggerbot
|
namespace hacks::shared::triggerbot
|
||||||
{
|
{
|
||||||
|
|
||||||
void CreateMove();
|
void CreateMove();
|
||||||
bool ShouldShoot();
|
bool ShouldShoot();
|
||||||
bool IsTargetStateGood(CachedEntity *entity, hacks::tf2::backtrack::BacktrackData *bt_data = nullptr);
|
bool IsTargetStateGood(CachedEntity *entity, std::optional<tf2::backtrack::BacktrackData> bt_data = std::nullopt);
|
||||||
CachedEntity *FindEntInSight(float range, bool no_players = false);
|
CachedEntity *FindEntInSight(float range, bool no_players = false);
|
||||||
bool HeadPreferable(CachedEntity *target);
|
bool HeadPreferable(CachedEntity *target);
|
||||||
bool UpdateAimkey();
|
bool UpdateAimkey();
|
||||||
|
@ -35,8 +35,8 @@ public:
|
|||||||
inline static int SetAbsOrigin(IClientEntity *self, Vector const &origin)
|
inline static int SetAbsOrigin(IClientEntity *self, Vector const &origin)
|
||||||
{
|
{
|
||||||
typedef int (*SetAbsOrigin_t)(IClientEntity *, Vector const &);
|
typedef int (*SetAbsOrigin_t)(IClientEntity *, Vector const &);
|
||||||
uintptr_t addr = e8call_direct(gSignatures.GetClientSignature("E8 ? ? ? ? EB 7D 8B 42 04"));
|
static uintptr_t addr = e8call_direct(gSignatures.GetClientSignature("E8 ? ? ? ? EB 7D 8B 42 04"));
|
||||||
SetAbsOrigin_t SetAbsOrigin_fn = SetAbsOrigin_t(addr);
|
static SetAbsOrigin_t SetAbsOrigin_fn = SetAbsOrigin_t(addr);
|
||||||
|
|
||||||
return SetAbsOrigin_fn(self, origin);
|
return SetAbsOrigin_fn(self, origin);
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,9 @@ void NetVars::Init()
|
|||||||
this->m_Collision = gNetvars.get_offset("DT_BaseEntity", "m_Collision");
|
this->m_Collision = gNetvars.get_offset("DT_BaseEntity", "m_Collision");
|
||||||
this->m_flSimulationTime = gNetvars.get_offset("DT_BaseEntity", "m_flSimulationTime");
|
this->m_flSimulationTime = gNetvars.get_offset("DT_BaseEntity", "m_flSimulationTime");
|
||||||
this->m_flAnimTime = gNetvars.get_offset("DT_BaseEntity", "AnimTimeMustBeFirst", "m_flAnimTime");
|
this->m_flAnimTime = gNetvars.get_offset("DT_BaseEntity", "AnimTimeMustBeFirst", "m_flAnimTime");
|
||||||
|
this->m_flCycle = gNetvars.get_offset("DT_BaseAnimating", "serveranimdata", "m_flCycle");
|
||||||
this->m_angRotation = gNetvars.get_offset("DT_BaseEntity", "m_angRotation");
|
this->m_angRotation = gNetvars.get_offset("DT_BaseEntity", "m_angRotation");
|
||||||
|
this->m_vecOrigin = gNetvars.get_offset("DT_BaseEntity", "m_vecOrigin");
|
||||||
|
|
||||||
IF_GAME(IsTF2())
|
IF_GAME(IsTF2())
|
||||||
{
|
{
|
||||||
|
@ -261,15 +261,38 @@ bool shouldMeleeCrit()
|
|||||||
{
|
{
|
||||||
if (!melee || g_pLocalPlayer->weapon_mode != weapon_melee)
|
if (!melee || g_pLocalPlayer->weapon_mode != weapon_melee)
|
||||||
return false;
|
return false;
|
||||||
if (hacks::tf2::backtrack::isBacktrackEnabled)
|
if (hacks::tf2::backtrack::backtrackEnabled())
|
||||||
{
|
{
|
||||||
// Closest tick for melee (default filter carry)
|
// Closest tick for melee
|
||||||
auto closest_tick = hacks::tf2::backtrack::getClosestTick(LOCAL_E->m_vecOrigin(), hacks::tf2::backtrack::defaultEntFilter, hacks::tf2::backtrack::defaultTickFilter);
|
std::optional<hacks::tf2::backtrack::BacktrackData> closest_tick = std::nullopt;
|
||||||
|
float best_distance = FLT_MAX;
|
||||||
|
|
||||||
|
if (hacks::tf2::backtrack::hasData())
|
||||||
|
{
|
||||||
|
closest_tick = hacks::tf2::backtrack::getData();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (auto &ent_data : hacks::tf2::backtrack::bt_data)
|
||||||
|
{
|
||||||
|
for (auto &tick : ent_data)
|
||||||
|
{
|
||||||
|
if (!tick.in_range)
|
||||||
|
continue;
|
||||||
|
float distance = tick.m_vecOrigin.DistTo(g_pLocalPlayer->v_Origin);
|
||||||
|
if (distance < best_distance)
|
||||||
|
{
|
||||||
|
best_distance = distance;
|
||||||
|
closest_tick = tick;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// Valid backtrack target
|
// Valid backtrack target
|
||||||
if (closest_tick)
|
if (closest_tick)
|
||||||
{
|
{
|
||||||
// Out of range, don't crit
|
// Out of range, don't crit
|
||||||
if ((*closest_tick).second.m_vecOrigin.DistTo(LOCAL_E->m_vecOrigin()) >= re::C_TFWeaponBaseMelee::GetSwingRange(RAW_ENT(LOCAL_W)) + 150.0f)
|
if (closest_tick->m_vecOrigin.DistTo(LOCAL_E->m_vecOrigin()) >= re::C_TFWeaponBaseMelee::GetSwingRange(RAW_ENT(LOCAL_W)) + 150.0f)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -140,7 +140,7 @@ matrix3x4_t *EntityHitboxCache::GetBones(int numbones)
|
|||||||
numbones = MAXSTUDIOBONES;
|
numbones = MAXSTUDIOBONES;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bones.size() < (size_t) numbones)
|
if (bones.size() != (size_t) numbones)
|
||||||
bones.resize(numbones);
|
bones.resize(numbones);
|
||||||
if (g_Settings.is_create_move)
|
if (g_Settings.is_create_move)
|
||||||
{
|
{
|
||||||
|
@ -154,9 +154,27 @@ float cur_proj_speed{ 0.0f };
|
|||||||
float cur_proj_grav{ 0.0f };
|
float cur_proj_grav{ 0.0f };
|
||||||
float cur_proj_start_vel{ 0.0f };
|
float cur_proj_start_vel{ 0.0f };
|
||||||
|
|
||||||
bool shouldBacktrack()
|
bool shouldbacktrack_cache = false;
|
||||||
|
|
||||||
|
void updateShouldBacktrack()
|
||||||
{
|
{
|
||||||
return !projectile_mode && (*backtrackAimbot || force_backtrack_aimbot) && hacks::tf2::backtrack::isBacktrackEnabled;
|
if (!hacks::tf2::backtrack::backtrackEnabled() || hacks::tf2::backtrack::hasData() || projectile_mode || !(*backtrackAimbot || force_backtrack_aimbot))
|
||||||
|
shouldbacktrack_cache = false;
|
||||||
|
else
|
||||||
|
shouldbacktrack_cache = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool shouldBacktrack(CachedEntity *ent)
|
||||||
|
{
|
||||||
|
if (!shouldbacktrack_cache)
|
||||||
|
return false;
|
||||||
|
if (ent && ent->m_Type() != ENTITY_PLAYER)
|
||||||
|
return false;
|
||||||
|
if (hacks::tf2::backtrack::bt_data.size() < ent->m_IDX)
|
||||||
|
return false;
|
||||||
|
if (hacks::tf2::backtrack::bt_data[ent->m_IDX - 1].empty())
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Am I holding Hitman's Heatmaker ?
|
// Am I holding Hitman's Heatmaker ?
|
||||||
@ -165,23 +183,6 @@ static bool CarryingHeatmaker()
|
|||||||
return CE_INT(LOCAL_W, netvar.iItemDefinitionIndex) == 752;
|
return CE_INT(LOCAL_W, netvar.iItemDefinitionIndex) == 752;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backtrack filter for aimbot
|
|
||||||
bool aimbotTickFilter(CachedEntity *ent, hacks::tf2::backtrack::BacktrackData tick)
|
|
||||||
{
|
|
||||||
// FOV check
|
|
||||||
if (fov > 0.0f)
|
|
||||||
{
|
|
||||||
float fov_scr = GetFov(g_pLocalPlayer->v_OrigViewangles, g_pLocalPlayer->v_Eye, tick.hitboxes.at(0).center);
|
|
||||||
// Failed FOV check
|
|
||||||
if (fov_scr > fov)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Not hitscan, no vischeck needed
|
|
||||||
if (g_pLocalPlayer->weapon_mode != weapon_hitscan)
|
|
||||||
return true;
|
|
||||||
// Return visibility
|
|
||||||
return IsEntityVectorVisible(ent, tick.hitboxes.at(head).center, true, MASK_SHOT);
|
|
||||||
}
|
|
||||||
static void doAutoZoom(bool target_found)
|
static void doAutoZoom(bool target_found)
|
||||||
{
|
{
|
||||||
bool isIdle = target_found ? false : hacks::shared::followbot::isIdle();
|
bool isIdle = target_found ? false : hacks::shared::followbot::isIdle();
|
||||||
@ -235,6 +236,7 @@ static void CreateMove()
|
|||||||
slow_aim = *normal_slow_aim;
|
slow_aim = *normal_slow_aim;
|
||||||
fov = *normal_fov;
|
fov = *normal_fov;
|
||||||
|
|
||||||
|
updateShouldBacktrack();
|
||||||
spectatorUpdate();
|
spectatorUpdate();
|
||||||
|
|
||||||
if (CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer() || CE_BAD(LOCAL_W))
|
if (CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer() || CE_BAD(LOCAL_W))
|
||||||
@ -413,11 +415,11 @@ bool MouseMoving()
|
|||||||
{
|
{
|
||||||
if ((g_GlobalVars->curtime - last_mouse_check) < 0.02)
|
if ((g_GlobalVars->curtime - last_mouse_check) < 0.02)
|
||||||
{
|
{
|
||||||
auto previous = SDL_GetMouseState(&PreviousX, &PreviousY);
|
SDL_GetMouseState(&PreviousX, &PreviousY);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto current = SDL_GetMouseState(&CurrentX, &CurrentY);
|
SDL_GetMouseState(&CurrentX, &CurrentY);
|
||||||
last_mouse_check = g_GlobalVars->curtime;
|
last_mouse_check = g_GlobalVars->curtime;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -509,8 +511,23 @@ CachedEntity *RetrieveBestTarget(bool aimkey_state)
|
|||||||
{
|
{
|
||||||
if (CE_GOOD(target_last))
|
if (CE_GOOD(target_last))
|
||||||
{
|
{
|
||||||
|
if (shouldBacktrack(target_last))
|
||||||
|
{
|
||||||
|
for (auto &bt_tick : hacks::tf2::backtrack::bt_data[target_last->m_IDX - 1])
|
||||||
|
{
|
||||||
|
if (bt_tick.in_range)
|
||||||
|
{
|
||||||
|
hacks::tf2::backtrack::MoveToTick(bt_tick);
|
||||||
|
if (IsTargetStateGood(target_last))
|
||||||
|
return target_last;
|
||||||
|
// Restore if bad target
|
||||||
|
hacks::tf2::backtrack::RestoreEntity(target_last->m_IDX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if previous target is still good
|
// Check if previous target is still good
|
||||||
if (IsTargetStateGood(target_last))
|
else if (IsTargetStateGood(target_last))
|
||||||
{
|
{
|
||||||
// If it is then return it again
|
// If it is then return it again
|
||||||
return target_last;
|
return target_last;
|
||||||
@ -520,15 +537,38 @@ CachedEntity *RetrieveBestTarget(bool aimkey_state)
|
|||||||
|
|
||||||
float target_highest_score, scr = 0.0f;
|
float target_highest_score, scr = 0.0f;
|
||||||
CachedEntity *ent;
|
CachedEntity *ent;
|
||||||
CachedEntity *target_highest_ent = 0;
|
CachedEntity *target_highest_ent = 0;
|
||||||
target_highest_score = -256;
|
target_highest_score = -256;
|
||||||
|
std::optional<hacks::tf2::backtrack::BacktrackData> bt_tick = std::nullopt;
|
||||||
for (int i = 1; i <= HIGHEST_ENTITY; i++)
|
for (int i = 1; i <= HIGHEST_ENTITY; i++)
|
||||||
{
|
{
|
||||||
ent = ENTITY(i);
|
ent = ENTITY(i);
|
||||||
if (CE_BAD(ent))
|
if (CE_BAD(ent))
|
||||||
continue; // Check for null and dormant
|
continue; // Check for null and dormant
|
||||||
// Check whether the current ent is good enough to target
|
// Check whether the current ent is good enough to target
|
||||||
if (IsTargetStateGood(ent))
|
bool isTargetGood = false;
|
||||||
|
|
||||||
|
static std::optional<hacks::tf2::backtrack::BacktrackData> temp_bt_tick = std::nullopt;
|
||||||
|
if (shouldBacktrack(ent))
|
||||||
|
{
|
||||||
|
for (auto &bt_tick : hacks::tf2::backtrack::bt_data[ent->m_IDX - 1])
|
||||||
|
{
|
||||||
|
if (bt_tick.in_range)
|
||||||
|
{
|
||||||
|
hacks::tf2::backtrack::MoveToTick(bt_tick);
|
||||||
|
if (IsTargetStateGood(ent))
|
||||||
|
{
|
||||||
|
isTargetGood = true;
|
||||||
|
temp_bt_tick = bt_tick;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
hacks::tf2::backtrack::RestoreEntity(ent->m_IDX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
isTargetGood = IsTargetStateGood(ent);
|
||||||
|
if (isTargetGood)
|
||||||
{
|
{
|
||||||
// Distance Priority, Uses this is melee is used
|
// Distance Priority, Uses this is melee is used
|
||||||
if (GetWeaponMode() == weaponmode::weapon_melee || (int) priority_mode == 2)
|
if (GetWeaponMode() == weaponmode::weapon_melee || (int) priority_mode == 2)
|
||||||
@ -561,10 +601,17 @@ CachedEntity *RetrieveBestTarget(bool aimkey_state)
|
|||||||
{
|
{
|
||||||
target_highest_score = scr;
|
target_highest_score = scr;
|
||||||
target_highest_ent = ent;
|
target_highest_ent = ent;
|
||||||
|
bt_tick = temp_bt_tick;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restore tick
|
||||||
|
if (shouldBacktrack(ent))
|
||||||
|
hacks::tf2::backtrack::RestoreEntity(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (target_highest_ent && bt_tick)
|
||||||
|
hacks::tf2::backtrack::MoveToTick(*bt_tick);
|
||||||
return target_highest_ent;
|
return target_highest_ent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -596,34 +643,17 @@ bool IsTargetStateGood(CachedEntity *entity)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
float swingrange = EffectiveTargetingRange();
|
float swingrange = EffectiveTargetingRange();
|
||||||
if (!shouldBacktrack() || entity->m_Type() != ENTITY_PLAYER)
|
int hb = BestHitbox(entity);
|
||||||
{
|
if (hb == -1)
|
||||||
int hb = BestHitbox(entity);
|
return false;
|
||||||
if (hb == -1)
|
Vector newangle = GetAimAtAngles(g_pLocalPlayer->v_Eye, entity->hitboxes.GetHitbox(hb)->center, LOCAL_E);
|
||||||
return false;
|
trace_t trace;
|
||||||
Vector newangle = GetAimAtAngles(g_pLocalPlayer->v_Eye, entity->hitboxes.GetHitbox(hb)->center, LOCAL_E);
|
Ray_t ray;
|
||||||
trace_t trace;
|
trace::filter_default.SetSelf(RAW_ENT(g_pLocalPlayer->entity));
|
||||||
Ray_t ray;
|
ray.Init(g_pLocalPlayer->v_Eye, GetForwardVector(g_pLocalPlayer->v_Eye, newangle, swingrange, LOCAL_E));
|
||||||
trace::filter_default.SetSelf(RAW_ENT(g_pLocalPlayer->entity));
|
g_ITrace->TraceRay(ray, MASK_SHOT_HULL, &trace::filter_default, &trace);
|
||||||
ray.Init(g_pLocalPlayer->v_Eye, GetForwardVector(g_pLocalPlayer->v_Eye, newangle, swingrange, LOCAL_E));
|
if ((IClientEntity *) trace.m_pEnt != RAW_ENT(entity))
|
||||||
g_ITrace->TraceRay(ray, MASK_SHOT_HULL, &trace::filter_default, &trace);
|
return false;
|
||||||
if ((IClientEntity *) trace.m_pEnt != RAW_ENT(entity))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// This does vischecks and everything
|
|
||||||
auto data = hacks::tf2::backtrack::getClosestEntTick(entity, LOCAL_E->m_vecOrigin(), aimbotTickFilter);
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Rage only check
|
// Rage only check
|
||||||
@ -731,15 +761,7 @@ bool IsTargetStateGood(CachedEntity *entity)
|
|||||||
Vector pos = GetBuildingPosition(ENTITY(sentry));
|
Vector pos = GetBuildingPosition(ENTITY(sentry));
|
||||||
if (hitbox == -1 || !entity->hitboxes.GetHitbox(cd.hitbox))
|
if (hitbox == -1 || !entity->hitboxes.GetHitbox(cd.hitbox))
|
||||||
return false;
|
return false;
|
||||||
if (shouldBacktrack())
|
if (!IsVectorVisible(pos, entity->hitboxes.GetHitbox(cd.hitbox)->center, false, ENTITY(sentry)))
|
||||||
{
|
|
||||||
// This does vischecks and everything
|
|
||||||
auto data = hacks::tf2::backtrack::getClosestEntTick(entity, LOCAL_E->m_vecOrigin(), aimbotTickFilter);
|
|
||||||
// No data found
|
|
||||||
if (!data)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (!IsVectorVisible(pos, entity->hitboxes.GetHitbox(cd.hitbox)->center, false, ENTITY(sentry)))
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (fov > 0.0f && cd.fov > fov && tickcount > hacks::shared::aimbot::last_target_ignore_timer)
|
if (fov > 0.0f && cd.fov > fov && tickcount > hacks::shared::aimbot::last_target_ignore_timer)
|
||||||
@ -878,15 +900,7 @@ bool IsTargetStateGood(CachedEntity *entity)
|
|||||||
if (sentry == -1)
|
if (sentry == -1)
|
||||||
return false;
|
return false;
|
||||||
Vector pos = GetBuildingPosition(ENTITY(sentry));
|
Vector pos = GetBuildingPosition(ENTITY(sentry));
|
||||||
if (shouldBacktrack())
|
if (!IsVectorVisible(pos, entity->m_vecOrigin(), false))
|
||||||
{
|
|
||||||
// This does vischecks and everything
|
|
||||||
auto data = hacks::tf2::backtrack::getClosestEntTick(entity, LOCAL_E->m_vecOrigin(), aimbotTickFilter);
|
|
||||||
// No data found
|
|
||||||
if (!data)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (!IsVectorVisible(pos, entity->m_vecOrigin(), false))
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (fov > 0.0f && cd.fov > fov)
|
if (fov > 0.0f && cd.fov > fov)
|
||||||
@ -924,15 +938,7 @@ void Aim(CachedEntity *entity)
|
|||||||
auto hitboxmin = hb->min;
|
auto hitboxmin = hb->min;
|
||||||
auto hitboxmax = hb->max;
|
auto hitboxmax = hb->max;
|
||||||
auto hitboxcenter = hb->center;
|
auto hitboxcenter = hb->center;
|
||||||
if (shouldBacktrack())
|
|
||||||
{
|
|
||||||
// This does vischecks and everything
|
|
||||||
auto data = hacks::tf2::backtrack::getClosestEntTick(entity, LOCAL_E->m_vecOrigin(), aimbotTickFilter);
|
|
||||||
auto bt_hb = data->hitboxes.at(cd.hitbox);
|
|
||||||
hitboxcenter = bt_hb.center;
|
|
||||||
hitboxmin = bt_hb.min;
|
|
||||||
hitboxmax = bt_hb.max;
|
|
||||||
}
|
|
||||||
// get positions
|
// get positions
|
||||||
minx = hitboxmin.x;
|
minx = hitboxmin.x;
|
||||||
miny = hitboxmin.y;
|
miny = hitboxmin.y;
|
||||||
@ -971,15 +977,8 @@ void Aim(CachedEntity *entity)
|
|||||||
if (silent && !slow_aim)
|
if (silent && !slow_aim)
|
||||||
g_pLocalPlayer->bUseSilentAngles = true;
|
g_pLocalPlayer->bUseSilentAngles = true;
|
||||||
// Set tick count to targets (backtrack messes with this)
|
// Set tick count to targets (backtrack messes with this)
|
||||||
if (!shouldBacktrack() && nolerp && entity->m_IDX <= g_IEngine->GetMaxClients())
|
if (!shouldBacktrack(entity) && nolerp && entity->m_IDX <= g_IEngine->GetMaxClients())
|
||||||
current_user_cmd->tick_count = TIME_TO_TICKS(CE_FLOAT(entity, netvar.m_flSimulationTime));
|
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 = hacks::tf2::backtrack::getClosestEntTick(entity, LOCAL_E->m_vecOrigin(), aimbotTickFilter);
|
|
||||||
if (data)
|
|
||||||
hacks::tf2::backtrack::SetBacktrackData(entity, *data);
|
|
||||||
}
|
|
||||||
aimed_this_tick = true;
|
aimed_this_tick = true;
|
||||||
// Finish function
|
// Finish function
|
||||||
return;
|
return;
|
||||||
@ -1095,84 +1094,71 @@ Vector PredictEntity(CachedEntity *entity, bool vischeck)
|
|||||||
// Pull out predicted data
|
// Pull out predicted data
|
||||||
AimbotCalculatedData_s &cd = calculated_data_array[entity->m_IDX];
|
AimbotCalculatedData_s &cd = calculated_data_array[entity->m_IDX];
|
||||||
Vector &result = cd.aim_position;
|
Vector &result = cd.aim_position;
|
||||||
if (cd.predict_tick == tickcount && cd.predict_type == vischeck)
|
if (cd.predict_tick == tickcount && cd.predict_type == vischeck && !shouldBacktrack(entity))
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
if (!shouldBacktrack() || entity->m_Type() != ENTITY_PLAYER)
|
// Players
|
||||||
|
if ((entity->m_Type() == ENTITY_PLAYER))
|
||||||
{
|
{
|
||||||
// Players
|
// If using projectiles, predict a vector
|
||||||
if ((entity->m_Type() == ENTITY_PLAYER))
|
if (projectileAimbotRequired)
|
||||||
{
|
{
|
||||||
// If using projectiles, predict a vector
|
std::pair<Vector, Vector> tmp_result;
|
||||||
if (projectileAimbotRequired)
|
// Use prediction engine if user settings allow
|
||||||
{
|
if (engine_projpred)
|
||||||
std::pair<Vector, Vector> tmp_result;
|
tmp_result = ProjectilePrediction_Engine(entity, cd.hitbox, cur_proj_speed, cur_proj_grav, 0, cur_proj_start_vel);
|
||||||
// Use prediction engine if user settings allow
|
|
||||||
if (engine_projpred)
|
|
||||||
tmp_result = ProjectilePrediction_Engine(entity, cd.hitbox, cur_proj_speed, cur_proj_grav, 0, cur_proj_start_vel);
|
|
||||||
else
|
|
||||||
tmp_result = ProjectilePrediction(entity, cd.hitbox, cur_proj_speed, cur_proj_grav, PlayerGravityMod(entity), cur_proj_start_vel);
|
|
||||||
|
|
||||||
// Don't use the intial velocity compensated one in vischecks
|
|
||||||
if (vischeck)
|
|
||||||
result = tmp_result.first;
|
|
||||||
else
|
|
||||||
result = tmp_result.second;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
tmp_result = ProjectilePrediction(entity, cd.hitbox, cur_proj_speed, cur_proj_grav, PlayerGravityMod(entity), cur_proj_start_vel);
|
||||||
// If using extrapolation, then predict a vector
|
|
||||||
if (extrapolate)
|
|
||||||
result = SimpleLatencyPrediction(entity, cd.hitbox);
|
|
||||||
// else just grab strait from the hitbox
|
|
||||||
else
|
|
||||||
GetHitbox(entity, cd.hitbox, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Buildings
|
|
||||||
else if (entity->m_Type() == ENTITY_BUILDING)
|
|
||||||
{
|
|
||||||
if (projectileAimbotRequired)
|
|
||||||
{
|
|
||||||
std::pair<Vector, Vector> tmp_result;
|
|
||||||
tmp_result = BuildingPrediction(entity, GetBuildingPosition(entity), cur_proj_speed, cur_proj_grav, cur_proj_start_vel);
|
|
||||||
|
|
||||||
// Don't use the intial velocity compensated one in vischecks
|
// Don't use the intial velocity compensated one in vischecks
|
||||||
if (vischeck)
|
if (vischeck)
|
||||||
result = tmp_result.first;
|
result = tmp_result.first;
|
||||||
else
|
|
||||||
result = tmp_result.second;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
result = GetBuildingPosition(entity);
|
result = tmp_result.second;
|
||||||
}
|
}
|
||||||
// NPCs (Skeletons, merasmus, etc)
|
|
||||||
else if (entity->m_Type() == ENTITY_NPC)
|
|
||||||
{
|
|
||||||
result = entity->hitboxes.GetHitbox(std::max(0, entity->hitboxes.GetNumHitboxes() / 2 - 1))->center;
|
|
||||||
}
|
|
||||||
// Other
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = entity->m_vecOrigin();
|
// If using extrapolation, then predict a vector
|
||||||
|
if (extrapolate)
|
||||||
|
result = SimpleLatencyPrediction(entity, cd.hitbox);
|
||||||
|
// else just grab strait from the hitbox
|
||||||
|
else
|
||||||
|
GetHitbox(entity, cd.hitbox, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
cd.predict_tick = tickcount;
|
|
||||||
cd.predict_type = vischeck;
|
|
||||||
|
|
||||||
cd.fov = GetFov(g_pLocalPlayer->v_OrigViewangles, g_pLocalPlayer->v_Eye, result);
|
|
||||||
}
|
}
|
||||||
|
// Buildings
|
||||||
|
else if (entity->m_Type() == ENTITY_BUILDING)
|
||||||
|
{
|
||||||
|
if (projectileAimbotRequired)
|
||||||
|
{
|
||||||
|
std::pair<Vector, Vector> tmp_result;
|
||||||
|
tmp_result = BuildingPrediction(entity, GetBuildingPosition(entity), cur_proj_speed, cur_proj_grav, cur_proj_start_vel);
|
||||||
|
|
||||||
|
// Don't use the intial velocity compensated one in vischecks
|
||||||
|
if (vischeck)
|
||||||
|
result = tmp_result.first;
|
||||||
|
else
|
||||||
|
result = tmp_result.second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
result = GetBuildingPosition(entity);
|
||||||
|
}
|
||||||
|
// NPCs (Skeletons, merasmus, etc)
|
||||||
|
else if (entity->m_Type() == ENTITY_NPC)
|
||||||
|
{
|
||||||
|
result = entity->hitboxes.GetHitbox(std::max(0, entity->hitboxes.GetNumHitboxes() / 2 - 1))->center;
|
||||||
|
}
|
||||||
|
// Other
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto data = hacks::tf2::backtrack::getClosestEntTick(entity, LOCAL_E->m_vecOrigin(), aimbotTickFilter);
|
result = entity->m_vecOrigin();
|
||||||
if (data)
|
|
||||||
{
|
|
||||||
result = data->hitboxes.at(cd.hitbox).center;
|
|
||||||
cd.predict_tick = tickcount;
|
|
||||||
cd.predict_type = vischeck;
|
|
||||||
cd.fov = GetFov(g_pLocalPlayer->v_OrigViewangles, g_pLocalPlayer->v_Eye, result);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cd.predict_tick = tickcount;
|
||||||
|
cd.predict_type = vischeck;
|
||||||
|
|
||||||
|
cd.fov = GetFov(g_pLocalPlayer->v_OrigViewangles, g_pLocalPlayer->v_Eye, result);
|
||||||
|
|
||||||
// Return the found vector
|
// Return the found vector
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -1303,38 +1289,15 @@ int BestHitbox(CachedEntity *target)
|
|||||||
return 12;
|
return 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backtracking and preferred hitbox
|
// preferred hitbox
|
||||||
if (shouldBacktrack())
|
if (target->hitboxes.VisibilityCheck(preferred))
|
||||||
{
|
|
||||||
auto data = hacks::tf2::backtrack::getClosestEntTick(target, LOCAL_E->m_vecOrigin(), aimbotTickFilter);
|
|
||||||
|
|
||||||
if (data)
|
|
||||||
{
|
|
||||||
// First check preferred hitbox
|
|
||||||
if (IsEntityVectorVisible(target, (*data).hitboxes[preferred].center, false))
|
|
||||||
return preferred;
|
|
||||||
|
|
||||||
// Then check the rest
|
|
||||||
if (*backtrackVischeckAll)
|
|
||||||
for (int j = head; j < foot_R; j++)
|
|
||||||
{
|
|
||||||
if (IsEntityVectorVisible(target, (*data).hitboxes[j].center, false))
|
|
||||||
return j;
|
|
||||||
}
|
|
||||||
else if (IsEntityVectorVisible(target, (*data).hitboxes.at(head).center, false))
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// Nothing found, falling through to further below
|
|
||||||
}
|
|
||||||
else if (target->hitboxes.VisibilityCheck(preferred))
|
|
||||||
return preferred;
|
return preferred;
|
||||||
// Else attempt to find any hitbox at all
|
// Else attempt to find any hitbox at all
|
||||||
if (!shouldBacktrack())
|
for (int i = projectile_mode ? 1 : 0; i < target->hitboxes.GetNumHitboxes() && i < 6; i++)
|
||||||
for (int i = projectile_mode ? 1 : 0; i < target->hitboxes.GetNumHitboxes() && i < 6; i++)
|
{
|
||||||
{
|
if (target->hitboxes.VisibilityCheck(i))
|
||||||
if (target->hitboxes.VisibilityCheck(i))
|
return i;
|
||||||
return i;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
@ -1381,28 +1344,17 @@ bool VischeckPredictedEntity(CachedEntity *entity)
|
|||||||
{
|
{
|
||||||
// Retrieve predicted data
|
// Retrieve predicted data
|
||||||
AimbotCalculatedData_s &cd = calculated_data_array[entity->m_IDX];
|
AimbotCalculatedData_s &cd = calculated_data_array[entity->m_IDX];
|
||||||
if (cd.vcheck_tick == tickcount)
|
if (cd.vcheck_tick == tickcount && !shouldBacktrack(entity))
|
||||||
return cd.visible;
|
return cd.visible;
|
||||||
if (!shouldBacktrack() || entity->m_Type() != ENTITY_PLAYER)
|
// Update info
|
||||||
{
|
cd.vcheck_tick = tickcount;
|
||||||
// Update info
|
if (extrapolate || projectileAimbotRequired || entity->m_Type() != ENTITY_PLAYER)
|
||||||
cd.vcheck_tick = tickcount;
|
cd.visible = IsEntityVectorVisible(entity, PredictEntity(entity, true), true);
|
||||||
if (extrapolate || projectileAimbotRequired || entity->m_Type() != ENTITY_PLAYER)
|
|
||||||
cd.visible = IsEntityVectorVisible(entity, PredictEntity(entity, true), true);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
trace_t trace;
|
|
||||||
cd.visible = IsEntityVectorVisible(entity, PredictEntity(entity, true), false, MASK_SHOT, &trace);
|
|
||||||
if (cd.visible && cd.hitbox == head && trace.hitbox != head)
|
|
||||||
cd.visible = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto data = hacks::tf2::backtrack::getClosestEntTick(entity, LOCAL_E->m_vecOrigin(), aimbotTickFilter);
|
trace_t trace;
|
||||||
if (data && IsEntityVectorVisible(entity, data->hitboxes.at((cd.hitbox == -1 || cd.hitbox >= 18) ? 0 : cd.hitbox).center, false, MASK_SHOT))
|
cd.visible = IsEntityVectorVisible(entity, PredictEntity(entity, true), false, MASK_SHOT, &trace);
|
||||||
cd.visible = true;
|
if (cd.visible && cd.hitbox == head && trace.hitbox != head)
|
||||||
else
|
|
||||||
cd.visible = false;
|
cd.visible = false;
|
||||||
}
|
}
|
||||||
return cd.visible;
|
return cd.visible;
|
||||||
|
@ -321,32 +321,24 @@ static bool doRageBackstab()
|
|||||||
static bool legit_stab = false;
|
static bool legit_stab = false;
|
||||||
static Vector newangle_apply;
|
static Vector newangle_apply;
|
||||||
|
|
||||||
bool backtrackFilter(CachedEntity *ent, hacks::tf2::backtrack::BacktrackData tick, std::optional<hacks::tf2::backtrack::BacktrackData> &best_tick)
|
bool IsTickGood(hacks::tf2::backtrack::BacktrackData tick)
|
||||||
{
|
{
|
||||||
Vector target_worldspace;
|
CachedEntity *ent = ENTITY(tick.entidx);
|
||||||
VectorLerp(tick.m_vecMins, tick.m_vecMaxs, 0.5f, target_worldspace);
|
Vector target_vec = tick.m_vecOrigin;
|
||||||
target_worldspace += tick.m_vecOrigin;
|
|
||||||
Vector distcheck = target_worldspace;
|
|
||||||
distcheck.z = g_pLocalPlayer->v_Eye.z;
|
|
||||||
|
|
||||||
// Angle check
|
Vector target_worldspace = target_vec;
|
||||||
Vector newangle;
|
target_worldspace += (RAW_ENT(ent)->GetCollideable()->OBBMins() + RAW_ENT(ent)->GetCollideable()->OBBMaxs()) / 2.0f;
|
||||||
if (legit_stab)
|
|
||||||
newangle = g_pLocalPlayer->v_OrigViewangles;
|
Vector angle = GetAimAtAngles(g_pLocalPlayer->v_Eye, target_worldspace);
|
||||||
else
|
|
||||||
newangle = GetAimAtAngles(g_pLocalPlayer->v_Eye, distcheck, LOCAL_E);
|
if (!angleCheck(ent, target_worldspace, angle))
|
||||||
if (!angleCheck(ent, target_worldspace, newangle) && !canFaceStab(ent))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Check if we can hit the enemy
|
trace_t trace;
|
||||||
if (doMovedSwingTrace(ent, tick.m_vecOrigin))
|
if (doMovedSwingTrace(ent, target_vec))
|
||||||
{
|
{
|
||||||
// Check if this tick is closer
|
newangle_apply = angle;
|
||||||
if (!best_tick || (*best_tick).m_vecOrigin.DistTo(LOCAL_E->m_vecOrigin()) > tick.m_vecOrigin.DistTo(LOCAL_E->m_vecOrigin()))
|
return true;
|
||||||
{
|
|
||||||
newangle_apply = newangle;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -359,29 +351,29 @@ static bool doBacktrackStab(bool legit = false)
|
|||||||
// Set for our filter
|
// Set for our filter
|
||||||
legit_stab = legit;
|
legit_stab = legit;
|
||||||
// Get the Best tick
|
// Get the Best tick
|
||||||
for (int i = 0; i <= g_IEngine->GetMaxClients(); i++)
|
for (int i = 1; i <= g_IEngine->GetMaxClients(); i++)
|
||||||
{
|
{
|
||||||
CachedEntity *ent = ENTITY(i);
|
CachedEntity *ent = ENTITY(i);
|
||||||
// Targeting checks
|
// Targeting checks
|
||||||
if (CE_BAD(ent) || !ent->m_bAlivePlayer() || !ent->m_bEnemy() || !player_tools::shouldTarget(ent) || IsPlayerInvulnerable(ent))
|
if (CE_BAD(ent) || !ent->m_bAlivePlayer() || !ent->m_bEnemy() || !player_tools::shouldTarget(ent) || IsPlayerInvulnerable(ent))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Get the best tick for that ent
|
for (auto &bt_tick : hacks::tf2::backtrack::bt_data[i - 1])
|
||||||
auto tick_data = hacks::tf2::backtrack::getBestTick(ent, backtrackFilter);
|
|
||||||
|
|
||||||
// We found something matching the criterias, break out
|
|
||||||
if (tick_data)
|
|
||||||
{
|
{
|
||||||
stab_data = *tick_data;
|
if (bt_tick.in_range && IsTickGood(bt_tick))
|
||||||
stab_ent = ent;
|
{
|
||||||
break;
|
// We found something matching the criterias, break out
|
||||||
|
stab_data = bt_tick;
|
||||||
|
stab_ent = ent;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We found a good ent
|
// We found a good ent
|
||||||
if (stab_ent)
|
if (stab_ent)
|
||||||
{
|
{
|
||||||
hacks::tf2::backtrack::SetBacktrackData(stab_ent, stab_data);
|
hacks::tf2::backtrack::MoveToTick(stab_data);
|
||||||
current_user_cmd->buttons |= IN_ATTACK;
|
current_user_cmd->buttons |= IN_ATTACK;
|
||||||
current_user_cmd->viewangles = newangle_apply;
|
current_user_cmd->viewangles = newangle_apply;
|
||||||
g_pLocalPlayer->bUseSilentAngles = true;
|
g_pLocalPlayer->bUseSilentAngles = true;
|
||||||
@ -406,6 +398,8 @@ void CreateMove()
|
|||||||
return;
|
return;
|
||||||
if (!CanShoot())
|
if (!CanShoot())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
bool shouldBacktrack = backtrack::backtrackEnabled() && !backtrack::hasData();
|
||||||
switch (*mode)
|
switch (*mode)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
@ -415,7 +409,7 @@ void CreateMove()
|
|||||||
doRageBackstab();
|
doRageBackstab();
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
if (hacks::tf2::backtrack::isBacktrackEnabled)
|
if (shouldBacktrack)
|
||||||
{
|
{
|
||||||
if (*hacks::tf2::backtrack::latency <= 190 && doRageBackstab())
|
if (*hacks::tf2::backtrack::latency <= 190 && doRageBackstab())
|
||||||
break;
|
break;
|
||||||
@ -427,7 +421,7 @@ void CreateMove()
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
if (hacks::tf2::backtrack::isBacktrackEnabled)
|
if (shouldBacktrack)
|
||||||
{
|
{
|
||||||
if (*hacks::tf2::backtrack::latency <= 190 && doLegitBackstab())
|
if (*hacks::tf2::backtrack::latency <= 190 && doLegitBackstab())
|
||||||
break;
|
break;
|
||||||
|
@ -1,38 +1,14 @@
|
|||||||
/*
|
#include "common.hpp"
|
||||||
* Remade on May the 3rd 2020
|
|
||||||
* Author: BenCat07
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#include "Backtrack.hpp"
|
#include "Backtrack.hpp"
|
||||||
#include "PlayerTools.hpp"
|
|
||||||
#include "memory"
|
|
||||||
|
|
||||||
namespace hacks::tf2::backtrack
|
namespace hacks::tf2::backtrack
|
||||||
{
|
{
|
||||||
// Internal rvars
|
static settings::Boolean enabled("backtrack.enabled", "false");
|
||||||
static settings::Boolean enabled{ "backtrack.enabled", "false" };
|
settings::Float latency("backtrack.latency", "0");
|
||||||
static settings::Boolean draw{ "backtrack.draw", "false" };
|
static settings::Int bt_slots("backtrack.slots", "0");
|
||||||
|
|
||||||
static std::vector<CIncomingSequence> sequences;
|
|
||||||
static int current_tickcount;
|
|
||||||
static std::vector<std::unique_ptr<std::array<BacktrackData, 67>>> backtrack_data;
|
|
||||||
static int lastincomingsequence{ 0 };
|
|
||||||
// Used to make transition smooth(er)
|
|
||||||
static float latency_rampup = 0.0f;
|
|
||||||
|
|
||||||
// Which data to apply in the late CreateMove
|
|
||||||
static CachedEntity *bt_ent = nullptr;
|
|
||||||
static std::optional<BacktrackData> bt_data;
|
|
||||||
|
|
||||||
static bool isEnabled();
|
|
||||||
static float getLatency();
|
|
||||||
static int getTicks();
|
|
||||||
static bool getBestInternalTick(CachedEntity *, BacktrackData &, std::optional<BacktrackData> &);
|
|
||||||
static void ApplyBacktrack();
|
|
||||||
|
|
||||||
settings::Float latency{ "backtrack.latency", "0" };
|
|
||||||
settings::Int bt_slots{ "backtrack.slots", "0" };
|
|
||||||
#if ENABLE_VISUALS
|
#if ENABLE_VISUALS
|
||||||
|
static settings::Boolean draw("backtrack.draw", "false");
|
||||||
settings::Boolean chams{ "backtrack.chams", "false" };
|
settings::Boolean chams{ "backtrack.chams", "false" };
|
||||||
settings::Boolean chams_wireframe{ "backtrack.chams.wireframe", "false" };
|
settings::Boolean chams_wireframe{ "backtrack.chams.wireframe", "false" };
|
||||||
settings::Int chams_ticks{ "backtrack.chams.ticks", "1" };
|
settings::Int chams_ticks{ "backtrack.chams.ticks", "1" };
|
||||||
@ -44,63 +20,73 @@ settings::Float chams_envmap_tint_g{ "backtrack.chams.envmap.tint.g", "0" };
|
|||||||
settings::Float chams_envmap_tint_b{ "backtrack.chams.envmap.tint.b", "1" };
|
settings::Float chams_envmap_tint_b{ "backtrack.chams.envmap.tint.b", "1" };
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static bool isEnabled();
|
||||||
|
|
||||||
|
#define MAX_BACKTRACK_TICKS 66
|
||||||
|
|
||||||
// Check if backtrack is enabled
|
// Check if backtrack is enabled
|
||||||
bool isBacktrackEnabled;
|
static bool isBacktrackEnabled = false;
|
||||||
#if ENABLE_VISUALS
|
|
||||||
// Drawing Backtrack chams
|
|
||||||
bool isDrawing;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Apply Backtrack
|
// Is any backtrack tick set for a player currently?
|
||||||
void ApplyBacktrack()
|
static std::optional<BacktrackData> set_data;
|
||||||
|
bool hasData()
|
||||||
{
|
{
|
||||||
if (!isBacktrackEnabled)
|
return (bool) set_data;
|
||||||
return;
|
}
|
||||||
if (bt_ent && bt_data)
|
|
||||||
|
std::optional<BacktrackData> getData()
|
||||||
|
{
|
||||||
|
return set_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lastincomingsequence = 0;
|
||||||
|
std::deque<CIncomingSequence> sequences;
|
||||||
|
static float latency_rampup = 0.0f;
|
||||||
|
|
||||||
|
// Store draw positions in CreateMove, also store the selected tick one too
|
||||||
|
static std::vector<Vector> draw_positions;
|
||||||
|
static std::optional<Vector> red_position;
|
||||||
|
|
||||||
|
std::vector<std::vector<BacktrackData>> bt_data;
|
||||||
|
// Update our sequences
|
||||||
|
void updateDatagram()
|
||||||
|
{
|
||||||
|
INetChannel *ch = (INetChannel *) g_IEngine->GetNetChannelInfo();
|
||||||
|
if (ch)
|
||||||
{
|
{
|
||||||
current_user_cmd->tick_count = (*bt_data).tickcount;
|
int m_nInSequenceNr = ch->m_nInSequenceNr;
|
||||||
CE_FLOAT(bt_ent, netvar.m_angEyeAngles) = (*bt_data).m_vecAngles.x;
|
int instate = ch->m_nInReliableState;
|
||||||
CE_FLOAT(bt_ent, netvar.m_angEyeAngles + 4) = (*bt_data).m_vecAngles.y;
|
if (m_nInSequenceNr > lastincomingsequence)
|
||||||
CE_FLOAT(bt_ent, netvar.m_flSimulationTime) = (*bt_data).m_flSimulationTime;
|
{
|
||||||
|
lastincomingsequence = m_nInSequenceNr;
|
||||||
|
sequences.insert(sequences.begin(), CIncomingSequence(instate, m_nInSequenceNr, g_GlobalVars->realtime));
|
||||||
|
}
|
||||||
|
if (sequences.size() > 2048)
|
||||||
|
sequences.pop_back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update tick to apply
|
// Latency to add for backtrack
|
||||||
void SetBacktrackData(CachedEntity *ent, BacktrackData tick)
|
float getLatency()
|
||||||
{
|
{
|
||||||
bt_ent = ent;
|
INetChannel *ch = (INetChannel *) g_IEngine->GetNetChannelInfo();
|
||||||
bt_data = tick;
|
// 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;
|
||||||
|
|
||||||
|
// Clamp and apply rampup, also ensure we do not go out of the 1000.0f bounds
|
||||||
|
float backtrack_latency = latency_rampup * std::clamp(*latency, 0.0f, 800.0f - real_latency);
|
||||||
|
|
||||||
|
return backtrack_latency;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get Best tick for Backtrack (crosshair/fov based)
|
bool isTickInRange(int tickcount)
|
||||||
bool getBestInternalTick(CachedEntity *, BacktrackData &data, std::optional<BacktrackData> &best_tick)
|
|
||||||
{
|
{
|
||||||
// Best Score
|
int delta_tickcount = abs(tickcount - current_user_cmd->tick_count + TIME_TO_TICKS(getLatency() / 1000.0f));
|
||||||
float bestScore = FLT_MAX;
|
return TICKS_TO_TIME(delta_tickcount) <= 0.2f - TICKS_TO_TIME(2);
|
||||||
|
|
||||||
// Are we using a melee weapon?
|
|
||||||
bool is_melee = false;
|
|
||||||
if (g_pLocalPlayer->weapon_mode == weapon_melee)
|
|
||||||
is_melee = 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);
|
|
||||||
|
|
||||||
float FOVDistance = GetFov(g_pLocalPlayer->v_OrigViewangles, g_pLocalPlayer->v_Eye, data.hitboxes.at(head).center);
|
|
||||||
if (FOVDistance >= bestScore)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// 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?
|
// Is backtrack enabled?
|
||||||
@ -145,207 +131,6 @@ bool isEnabled()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main Tracking logic
|
|
||||||
void CreateMove()
|
|
||||||
{
|
|
||||||
// Update enabled status
|
|
||||||
isBacktrackEnabled = isEnabled();
|
|
||||||
if (!isBacktrackEnabled)
|
|
||||||
{
|
|
||||||
latency_rampup = 0.0f;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return if local entity is bad (Still have backtrack run while dead so ping does not fluctuate heavily)
|
|
||||||
if (CE_BAD(LOCAL_E))
|
|
||||||
{
|
|
||||||
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();
|
|
||||||
|
|
||||||
// Don't run rest
|
|
||||||
if (CE_BAD(LOCAL_W) || !LOCAL_E->m_bAlivePlayer())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// 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 = 1; i <= g_IEngine->GetMaxClients(); i++)
|
|
||||||
{
|
|
||||||
if (i == g_pLocalPlayer->entity_idx)
|
|
||||||
{
|
|
||||||
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 - 1);
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
auto model = (const model_t *) RAW_ENT(ent)->GetModel();
|
|
||||||
if (model)
|
|
||||||
{
|
|
||||||
auto shdr = g_IModelInfo->GetStudiomodel(model);
|
|
||||||
if (shdr)
|
|
||||||
{
|
|
||||||
int numbones = shdr->numbones;
|
|
||||||
if (numbones != current_tick.bones.size())
|
|
||||||
current_tick.bones.resize(numbones);
|
|
||||||
|
|
||||||
memcpy((void *) ¤t_tick.bones[0], (void *) &ent->hitboxes.bones[0], sizeof(matrix3x4_t) * numbones);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
current_tick.bones.resize(0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
current_tick.bones.resize(0);
|
|
||||||
|
|
||||||
// Check if tick updated or not (fakelag)
|
|
||||||
current_tick.has_updated = !previous_tick.m_flSimulationTime || previous_tick.m_flSimulationTime != current_tick.m_flSimulationTime;
|
|
||||||
|
|
||||||
// Special Case that invalidates all the previous ticks
|
|
||||||
// if the new tick is too far away all the other ones get marked as not updated and thus invalid
|
|
||||||
if (current_tick.m_vecOrigin.AsVector2D().DistTo(previous_tick.m_vecOrigin.AsVector2D()) > 64)
|
|
||||||
{
|
|
||||||
for (auto &tick : *backtrack_ent)
|
|
||||||
{
|
|
||||||
// Older than current tick, mark invalid
|
|
||||||
if (tick.tickcount < current_tick.tickcount)
|
|
||||||
tick.has_updated = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get best tick for this ent
|
|
||||||
std::optional<BacktrackData> data = getBestTick(ent, getBestInternalTick);
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (best_data && best_ent)
|
|
||||||
SetBacktrackData(best_ent, *best_data);
|
|
||||||
}
|
|
||||||
void CreateMoveLate()
|
|
||||||
{
|
|
||||||
ApplyBacktrack();
|
|
||||||
}
|
|
||||||
|
|
||||||
#if ENABLE_VISUALS
|
|
||||||
// Drawing
|
|
||||||
void Draw()
|
|
||||||
{
|
|
||||||
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_s;
|
|
||||||
draw::Rectangle(out.x, out.y, size / 4, size / 4, draw_color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Resize our backtrackdata
|
|
||||||
void LevelInit()
|
|
||||||
{
|
|
||||||
backtrack_data.resize(g_IEngine->GetMaxClients());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset things
|
|
||||||
void LevelShutdown()
|
|
||||||
{
|
|
||||||
lastincomingsequence = 0;
|
|
||||||
sequences.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change Datagram data
|
// Change Datagram data
|
||||||
void adjustPing(INetChannel *ch)
|
void adjustPing(INetChannel *ch)
|
||||||
{
|
{
|
||||||
@ -362,207 +147,235 @@ void adjustPing(INetChannel *ch)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Latency to add for backtrack
|
// Move target entity to tick
|
||||||
float getLatency()
|
void MoveToTick(BacktrackData data)
|
||||||
{
|
{
|
||||||
INetChannel *ch = (INetChannel *) g_IEngine->GetNetChannelInfo();
|
CachedEntity *target = ENTITY(data.entidx);
|
||||||
// Track what actual latency we have
|
|
||||||
float real_latency = 0.0f;
|
|
||||||
|
|
||||||
// If we have a netchannel (just in case) set real latency to it
|
// Set entity data to match the target data
|
||||||
if (ch)
|
re::C_BasePlayer::SetAbsOrigin(RAW_ENT(target), data.m_vecOrigin);
|
||||||
real_latency = ch->GetLatency(FLOW_OUTGOING) * 1000.0f;
|
re::C_BasePlayer::GetEyeAngles(RAW_ENT(target)) = data.m_vecAngles;
|
||||||
|
|
||||||
// Fix the latency
|
// Need to reconstruct a bunch of data
|
||||||
float backtrack_latency = *latency - real_latency;
|
target->hitboxes.InvalidateCache();
|
||||||
|
|
||||||
// Clamp and apply rampup
|
// Mark all the hitboxes as valid so we don't recalc them and use the old data
|
||||||
backtrack_latency = latency_rampup * std::clamp(backtrack_latency, 0.0f, std::max(800.0f - real_latency, 0.0f));
|
// We already have
|
||||||
|
for (int i = hitbox_t::head; i <= foot_R; i++)
|
||||||
|
{
|
||||||
|
target->hitboxes.m_CacheValidationFlags[i] = true;
|
||||||
|
target->hitboxes.m_CacheInternal.at(i) = data.hitboxes.at(i);
|
||||||
|
}
|
||||||
|
|
||||||
return backtrack_latency;
|
// Sync animation properly
|
||||||
|
CE_FLOAT(target, netvar.m_flSimulationTime) = data.simtime;
|
||||||
|
CE_FLOAT(target, netvar.m_flCycle) = data.cycle;
|
||||||
|
CE_FLOAT(target, netvar.m_flAnimTime) = data.animtime;
|
||||||
|
CE_INT(target, netvar.m_nSequence) = data.sequence;
|
||||||
|
|
||||||
|
// Thanks to the epic doghook developers (mainly F1ssion and MrSteyk)
|
||||||
|
// I do not have to find all of these signatures and dig through ida
|
||||||
|
struct BoneCache;
|
||||||
|
|
||||||
|
typedef BoneCache *(*GetBoneCache_t)(unsigned);
|
||||||
|
typedef void (*BoneCacheUpdateBones_t)(BoneCache *, matrix3x4_t * bones, unsigned, float time);
|
||||||
|
static auto hitbox_bone_cache_handle_offset = *(unsigned *) (gSignatures.GetClientSignature("8B 86 ? ? ? ? 89 04 24 E8 ? ? ? ? 85 C0 89 C3 74 48") + 2);
|
||||||
|
static auto studio_get_bone_cache = (GetBoneCache_t) gSignatures.GetClientSignature("55 89 E5 56 53 BB ? ? ? ? 83 EC 50 C7 45 D8");
|
||||||
|
static auto bone_cache_update_bones = (BoneCacheUpdateBones_t) gSignatures.GetClientSignature("55 89 E5 57 31 FF 56 53 83 EC 1C 8B 5D 08 0F B7 53 10");
|
||||||
|
|
||||||
|
auto hitbox_bone_cache_handle = CE_VAR(target, hitbox_bone_cache_handle_offset, unsigned);
|
||||||
|
if (hitbox_bone_cache_handle)
|
||||||
|
{
|
||||||
|
BoneCache *bone_cache = studio_get_bone_cache(hitbox_bone_cache_handle);
|
||||||
|
if (bone_cache && !data.bones.empty())
|
||||||
|
bone_cache_update_bones(bone_cache, data.bones.data(), data.bones.size(), g_GlobalVars->curtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy old bones to avoid really really slow setupbones logic
|
||||||
|
target->hitboxes.bones = data.bones;
|
||||||
|
target->hitboxes.bones_setup = true;
|
||||||
|
|
||||||
|
// We need to update their positions so the rays actually work, this requires some hacky stuff
|
||||||
|
uintptr_t collisionprop = (uintptr_t) RAW_ENT(target) + netvar.m_Collision;
|
||||||
|
|
||||||
|
typedef void (*UpdateParition_t)(uintptr_t prop);
|
||||||
|
static auto sig_update = gSignatures.GetClientSignature("55 89 E5 57 56 53 83 EC 3C 8B 5D ? 8B 43 ? 8B 90");
|
||||||
|
static UpdateParition_t UpdatePartition_fn = (UpdateParition_t) sig_update;
|
||||||
|
|
||||||
|
// Mark for update
|
||||||
|
int *entity_flags = (int *) ((uintptr_t) RAW_ENT(target) + 400);
|
||||||
|
// (EFL_DIRTY_SURROUNDING_COLLISION_BOUNDS | EFL_DIRTY_SPATIAL_PARTITION)
|
||||||
|
*entity_flags |= (1 << 14) | (1 << 15);
|
||||||
|
|
||||||
|
// Update
|
||||||
|
UpdatePartition_fn(collisionprop);
|
||||||
|
set_data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update our sequences
|
// Restore ent to original state
|
||||||
void updateDatagram()
|
void RestoreEntity(int entidx)
|
||||||
{
|
{
|
||||||
INetChannel *ch = (INetChannel *) g_IEngine->GetNetChannelInfo();
|
MoveToTick(bt_data[entidx - 1][0]);
|
||||||
if (ch)
|
// Undo tick setting
|
||||||
|
set_data = std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateMoveEarly()
|
||||||
|
{
|
||||||
|
draw_positions.clear();
|
||||||
|
isBacktrackEnabled = isEnabled();
|
||||||
|
if (!isBacktrackEnabled)
|
||||||
{
|
{
|
||||||
int m_nInSequenceNr = ch->m_nInSequenceNr;
|
latency_rampup = 0.0f;
|
||||||
int instate = ch->m_nInReliableState;
|
bt_data.clear();
|
||||||
if (m_nInSequenceNr > lastincomingsequence)
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CE_GOOD(LOCAL_E))
|
||||||
|
updateDatagram();
|
||||||
|
else
|
||||||
|
sequences.clear();
|
||||||
|
|
||||||
|
latency_rampup += g_GlobalVars->interval_per_tick;
|
||||||
|
latency_rampup = std::min(1.0f, latency_rampup);
|
||||||
|
if ((int) bt_data.size() != g_IEngine->GetMaxClients())
|
||||||
|
bt_data.resize(g_IEngine->GetMaxClients());
|
||||||
|
|
||||||
|
for (int i = 1; i <= g_IEngine->GetMaxClients(); i++)
|
||||||
|
{
|
||||||
|
CachedEntity *ent = ENTITY(i);
|
||||||
|
int index = i - 1;
|
||||||
|
|
||||||
|
auto &ent_data = bt_data[index];
|
||||||
|
|
||||||
|
if (CE_BAD(ent) || !ent->m_bAlivePlayer() || HasCondition<TFCond_HalloweenGhostMode>(ent) || !ent->m_bEnemy())
|
||||||
{
|
{
|
||||||
lastincomingsequence = m_nInSequenceNr;
|
ent_data.clear();
|
||||||
sequences.insert(sequences.begin(), CIncomingSequence(instate, m_nInSequenceNr, g_GlobalVars->realtime));
|
continue;
|
||||||
|
}
|
||||||
|
BacktrackData data{};
|
||||||
|
data.entidx = i;
|
||||||
|
data.m_vecAngles = ent->m_vecAngle();
|
||||||
|
data.m_vecOrigin = ent->m_vecOrigin();
|
||||||
|
data.tickcount = current_user_cmd->tick_count;
|
||||||
|
|
||||||
|
data.simtime = CE_FLOAT(ent, netvar.m_flSimulationTime);
|
||||||
|
data.animtime = CE_FLOAT(ent, netvar.m_flAnimTime);
|
||||||
|
data.cycle = CE_FLOAT(ent, netvar.m_flCycle);
|
||||||
|
data.sequence = CE_INT(ent, netvar.m_nSequence);
|
||||||
|
|
||||||
|
ent->hitboxes.GetHitbox(0);
|
||||||
|
// Copy bones (for chams/glow)
|
||||||
|
data.bones = ent->hitboxes.bones;
|
||||||
|
|
||||||
|
for (int i = head; i <= foot_R; i++)
|
||||||
|
data.hitboxes.at(i) = *ent->hitboxes.GetHitbox(i);
|
||||||
|
|
||||||
|
ent_data.insert(ent_data.begin(), data);
|
||||||
|
if (ent_data.size() > MAX_BACKTRACK_TICKS)
|
||||||
|
ent_data.pop_back();
|
||||||
|
|
||||||
|
for (auto &tick : ent_data)
|
||||||
|
{
|
||||||
|
tick.in_range = isTickInRange(tick.tickcount);
|
||||||
|
|
||||||
|
if (!tick.in_range)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
#if ENABLE_VISUALS
|
||||||
|
if (draw)
|
||||||
|
{
|
||||||
|
MoveToTick(tick);
|
||||||
|
Vector draw_pos = ent->hitboxes.GetHitbox(0)->center;
|
||||||
|
draw_positions.push_back(draw_pos);
|
||||||
|
RestoreEntity(i);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
if (sequences.size() > 2048)
|
|
||||||
sequences.pop_back();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get How many ticks we should Store and use
|
void CreateMoveLate()
|
||||||
int getTicks()
|
|
||||||
{
|
{
|
||||||
float max_lat = getLatency() + 200.0f;
|
if (!isBacktrackEnabled)
|
||||||
|
return;
|
||||||
|
|
||||||
// Clamp
|
red_position = std::nullopt;
|
||||||
max_lat = std::min(1000.0f, max_lat);
|
|
||||||
|
|
||||||
// Get difference
|
// Bad player
|
||||||
int ticks = TIME_TO_TICKS(max_lat / 1000.0f);
|
if (CE_BAD(LOCAL_E) || HasCondition<TFCond_HalloweenGhostMode>(LOCAL_E) || !LOCAL_E->m_bAlivePlayer())
|
||||||
return ticks;
|
return;
|
||||||
};
|
|
||||||
|
|
||||||
void resetData(int entidx)
|
// No data set yet, try to get nearest to cursor
|
||||||
{
|
if (!set_data && !g_pLocalPlayer->bUseSilentAngles)
|
||||||
// Clear everything
|
|
||||||
backtrack_data.at(entidx - 1).reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool 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> getGoodTicks(int entidx)
|
|
||||||
{
|
|
||||||
std::vector<BacktrackData> to_return;
|
|
||||||
// Invalid
|
|
||||||
if (entidx <= 0 || (int) backtrack_data.size() < entidx || !backtrack_data.at(entidx - 1))
|
|
||||||
return to_return;
|
|
||||||
|
|
||||||
// Check all ticks
|
|
||||||
for (auto &tick : *backtrack_data.at(entidx - 1))
|
|
||||||
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> 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 (ent->m_IDX <= 0 || backtrack_data.size() < ent->m_IDX || !backtrack_data.at(ent->m_IDX - 1))
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
// 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 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, true, MASK_SHOT);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool defaultEntFilter(CachedEntity *ent)
|
|
||||||
{
|
|
||||||
// Dormant
|
|
||||||
if (CE_BAD(ent))
|
|
||||||
return false;
|
|
||||||
// Should we even target them
|
|
||||||
if (!player_tools::shouldTarget(ent))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get Closest tick of a specific entity
|
|
||||||
std::optional<BacktrackData> getClosestEntTick(CachedEntity *ent, Vector vec, std::function<bool(CachedEntity *, BacktrackData)> tick_filter)
|
|
||||||
{
|
|
||||||
std::optional<BacktrackData> return_value;
|
|
||||||
// No entry
|
|
||||||
if (ent->m_IDX <= 0 || backtrack_data.size() < ent->m_IDX || !backtrack_data.at(ent->m_IDX - 1))
|
|
||||||
return return_value;
|
|
||||||
|
|
||||||
float distance = FLT_MAX;
|
|
||||||
|
|
||||||
// Go through all Good ticks
|
|
||||||
for (auto &tick : getGoodTicks(ent->m_IDX))
|
|
||||||
{
|
{
|
||||||
// Found Closer tick
|
float cursor_distance = FLT_MAX;
|
||||||
if (tick.m_vecOrigin.DistTo(vec) < distance)
|
for (auto &ent_data : bt_data)
|
||||||
{
|
{
|
||||||
// Does the tick pass the filter
|
for (auto &tick_data : ent_data)
|
||||||
if (tick_filter(ent, tick))
|
|
||||||
{
|
{
|
||||||
return_value = tick;
|
if (isTickInRange(tick_data.tickcount))
|
||||||
distance = tick.m_vecOrigin.DistTo(vec);
|
{
|
||||||
|
float distance = GetFov(LOCAL_E->m_vecAngle(), g_pLocalPlayer->v_Eye, tick_data.hitboxes.at(0).center);
|
||||||
|
if (distance < cursor_distance)
|
||||||
|
{
|
||||||
|
cursor_distance = distance;
|
||||||
|
set_data = tick_data;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return return_value;
|
|
||||||
|
// Still no data set, be sad and return
|
||||||
|
if (!set_data)
|
||||||
|
return;
|
||||||
|
current_user_cmd->tick_count = set_data->tickcount;
|
||||||
|
red_position = set_data->hitboxes.at(0).center;
|
||||||
|
RestoreEntity(set_data->entidx);
|
||||||
|
set_data = std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
void Shutdown()
|
||||||
std::optional<std::pair<CachedEntity *, BacktrackData>> getClosestTick(Vector vec, std::function<bool(CachedEntity *)> ent_filter, std::function<bool(CachedEntity *, BacktrackData)> tick_filter)
|
|
||||||
{
|
{
|
||||||
float distance = FLT_MAX;
|
bt_data.clear();
|
||||||
CachedEntity *best_ent = nullptr;
|
sequences.clear();
|
||||||
BacktrackData best_data;
|
lastincomingsequence = 0;
|
||||||
|
|
||||||
std::optional<std::pair<CachedEntity *, BacktrackData>> return_val;
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
distance = (*closest_entdata).m_vecOrigin.DistTo(vec);
|
|
||||||
best_data = *closest_entdata;
|
|
||||||
best_ent = ent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (best_ent)
|
|
||||||
return_val = std::pair<CachedEntity *, BacktrackData>(best_ent, best_data);
|
|
||||||
return return_val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool backtrackEnabled()
|
||||||
|
{
|
||||||
|
return isBacktrackEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if ENABLE_VISUALS
|
||||||
|
void Draw()
|
||||||
|
{
|
||||||
|
if (!isBacktrackEnabled || !draw)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!g_IEngine->IsInGame())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Vector screen;
|
||||||
|
for (auto &pos : draw_positions)
|
||||||
|
{
|
||||||
|
if (draw::WorldToScreen(pos, screen))
|
||||||
|
draw::Rectangle(screen.x - 2, screen.y - 2, 4, 4, colors::green);
|
||||||
|
}
|
||||||
|
if (red_position && draw::WorldToScreen(*red_position, screen))
|
||||||
|
draw::Rectangle(screen.x - 2, screen.y - 2, 4, 4, colors::red);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static InitRoutine init([]() {
|
static InitRoutine init([]() {
|
||||||
EC::Register(EC::CreateMove, CreateMove, "backtrack_cm", EC::early);
|
EC::Register(EC::CreateMove, CreateMoveEarly, "bt_update", EC::very_early);
|
||||||
EC::Register(EC::CreateMoveWarp, CreateMove, "backtrack_cmw", EC::early);
|
EC::Register(EC::CreateMove, CreateMoveLate, "bt_createmove", EC::very_late);
|
||||||
EC::Register(EC::CreateMove, CreateMoveLate, "backtrack_cmlate", EC::very_late);
|
EC::Register(EC::Shutdown, Shutdown, "bt_shutdown");
|
||||||
EC::Register(EC::CreateMoveWarp, CreateMoveLate, "backtrack_cmwlate", EC::very_late);
|
EC::Register(EC::LevelInit, Shutdown, "bt_shutdown");
|
||||||
#if ENABLE_VISUALS
|
#if ENABLE_VISUALS
|
||||||
EC::Register(EC::Draw, Draw, "backtrack_draw");
|
EC::Register(EC::Draw, Draw, "bt_draw");
|
||||||
#endif
|
#endif
|
||||||
EC::Register(EC::LevelShutdown, LevelShutdown, "backtrack_levelshutdown");
|
|
||||||
EC::Register(EC::LevelInit, LevelInit, "backtrack_levelinit");
|
|
||||||
LevelInit();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
} // namespace hacks::tf2::backtrack
|
} // namespace hacks::tf2::backtrack
|
||||||
|
@ -27,12 +27,17 @@ static Timer previous_entity_delay{};
|
|||||||
// TODO: Refactor this jank
|
// TODO: Refactor this jank
|
||||||
std::pair<CachedEntity *, Vector> FindBestEnt(bool teammate, bool Predict, bool zcheck, bool demoknight_mode, float range)
|
std::pair<CachedEntity *, Vector> FindBestEnt(bool teammate, bool Predict, bool zcheck, bool demoknight_mode, float range)
|
||||||
{
|
{
|
||||||
CachedEntity *bestent = nullptr;
|
|
||||||
float bestscr = FLT_MAX;
|
CachedEntity *bestent = nullptr;
|
||||||
|
float bestscr = FLT_MAX;
|
||||||
|
std::optional<backtrack::BacktrackData> best_data = std::nullopt;
|
||||||
Vector predicted{};
|
Vector predicted{};
|
||||||
// Too long since we focused it
|
// Too long since we focused it
|
||||||
if (previous_entity_delay.check(100))
|
if (previous_entity_delay.check(100))
|
||||||
prevent = -1;
|
prevent = -1;
|
||||||
|
|
||||||
|
bool shouldBacktrack = backtrack::backtrackEnabled() && !backtrack::hasData();
|
||||||
|
|
||||||
for (int i = 0; i < 1; i++)
|
for (int i = 0; i < 1; i++)
|
||||||
{
|
{
|
||||||
if (prevent != -1)
|
if (prevent != -1)
|
||||||
@ -51,22 +56,44 @@ std::pair<CachedEntity *, Vector> FindBestEnt(bool teammate, bool Predict, bool
|
|||||||
target = ProjectilePrediction(ent, 1, sandwich_speed, grav, PlayerGravityMod(ent), initial_vel).second;
|
target = ProjectilePrediction(ent, 1, sandwich_speed, grav, PlayerGravityMod(ent), initial_vel).second;
|
||||||
else
|
else
|
||||||
target = ent->hitboxes.GetHitbox(1)->center;
|
target = ent->hitboxes.GetHitbox(1)->center;
|
||||||
if (!hacks::tf2::backtrack::isBacktrackEnabled && !IsEntityVectorVisible(ent, target))
|
if (!shouldBacktrack && !IsEntityVectorVisible(ent, target))
|
||||||
continue;
|
continue;
|
||||||
if (zcheck && (ent->m_vecOrigin().z - LOCAL_E->m_vecOrigin().z) > 200.0f)
|
if (zcheck && (ent->m_vecOrigin().z - LOCAL_E->m_vecOrigin().z) > 200.0f)
|
||||||
continue;
|
continue;
|
||||||
float scr = ent->m_flDistance();
|
float scr = ent->m_flDistance();
|
||||||
if (hacks::tf2::backtrack::isBacktrackEnabled && demoknight_mode)
|
std::optional<backtrack::BacktrackData> data = std::nullopt;
|
||||||
|
if (!shouldBacktrack && demoknight_mode)
|
||||||
{
|
{
|
||||||
auto data = hacks::tf2::backtrack::getClosestEntTick(ent, LOCAL_E->m_vecOrigin(), hacks::tf2::backtrack::defaultTickFilter);
|
if (backtrack::bt_data.size() >= ent->m_IDX)
|
||||||
// No entity
|
|
||||||
if (!data)
|
|
||||||
scr = FLT_MAX;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
target = (*data).m_vecOrigin;
|
float tick_score = FLT_MAX;
|
||||||
scr = (*data).m_vecOrigin.DistTo(LOCAL_E->m_vecOrigin());
|
for (auto &tick : backtrack::bt_data.at(ent->m_IDX - 1))
|
||||||
|
{
|
||||||
|
if (!tick.in_range)
|
||||||
|
continue;
|
||||||
|
float dist = tick.m_vecOrigin.DistTo(ent->m_vecOrigin());
|
||||||
|
if (dist < tick_score)
|
||||||
|
{
|
||||||
|
backtrack::MoveToTick(tick);
|
||||||
|
if (IsEntityVectorVisible(ent, ent->hitboxes.GetHitbox(1)->center))
|
||||||
|
{
|
||||||
|
data = tick;
|
||||||
|
tick_score = dist;
|
||||||
|
}
|
||||||
|
backtrack::RestoreEntity(ent->m_IDX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// No entity
|
||||||
|
if (!data)
|
||||||
|
scr = FLT_MAX;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
target = (*data).m_vecOrigin;
|
||||||
|
scr = (*data).m_vecOrigin.DistTo(LOCAL_E->m_vecOrigin());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
scr = FLT_MAX;
|
||||||
}
|
}
|
||||||
// Demoknight
|
// Demoknight
|
||||||
if (demoknight_mode)
|
if (demoknight_mode)
|
||||||
@ -91,6 +118,8 @@ std::pair<CachedEntity *, Vector> FindBestEnt(bool teammate, bool Predict, bool
|
|||||||
if (bestent && predicted.z)
|
if (bestent && predicted.z)
|
||||||
{
|
{
|
||||||
previous_entity_delay.update();
|
previous_entity_delay.update();
|
||||||
|
if (demoknight_mode && best_data)
|
||||||
|
backtrack::MoveToTick(*best_data);
|
||||||
return { bestent, predicted };
|
return { bestent, predicted };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -109,22 +138,45 @@ std::pair<CachedEntity *, Vector> FindBestEnt(bool teammate, bool Predict, bool
|
|||||||
target = ProjectilePrediction(ent, 1, sandwich_speed, grav, PlayerGravityMod(ent)).second;
|
target = ProjectilePrediction(ent, 1, sandwich_speed, grav, PlayerGravityMod(ent)).second;
|
||||||
else
|
else
|
||||||
target = ent->hitboxes.GetHitbox(1)->center;
|
target = ent->hitboxes.GetHitbox(1)->center;
|
||||||
if (!hacks::tf2::backtrack::isBacktrackEnabled && !IsEntityVectorVisible(ent, target))
|
if (!shouldBacktrack && !IsEntityVectorVisible(ent, target))
|
||||||
continue;
|
continue;
|
||||||
if (zcheck && (ent->m_vecOrigin().z - LOCAL_E->m_vecOrigin().z) > 200.0f)
|
if (zcheck && (ent->m_vecOrigin().z - LOCAL_E->m_vecOrigin().z) > 200.0f)
|
||||||
continue;
|
continue;
|
||||||
float scr = ent->m_flDistance();
|
float scr = ent->m_flDistance();
|
||||||
if (hacks::tf2::backtrack::isBacktrackEnabled && demoknight_mode)
|
std::optional<backtrack::BacktrackData> data = std::nullopt;
|
||||||
|
|
||||||
|
if (!shouldBacktrack && demoknight_mode)
|
||||||
{
|
{
|
||||||
auto data = hacks::tf2::backtrack::getClosestEntTick(ent, LOCAL_E->m_vecOrigin(), hacks::tf2::backtrack::defaultTickFilter);
|
if (backtrack::bt_data.size() >= ent->m_IDX)
|
||||||
// No entity
|
|
||||||
if (!data)
|
|
||||||
scr = FLT_MAX;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
target = (*data).m_vecOrigin;
|
float tick_score = FLT_MAX;
|
||||||
scr = (*data).m_vecOrigin.DistTo(LOCAL_E->m_vecOrigin());
|
for (auto &tick : backtrack::bt_data.at(ent->m_IDX - 1))
|
||||||
|
{
|
||||||
|
if (!tick.in_range)
|
||||||
|
continue;
|
||||||
|
float dist = tick.m_vecOrigin.DistTo(ent->m_vecOrigin());
|
||||||
|
if (dist < tick_score)
|
||||||
|
{
|
||||||
|
backtrack::MoveToTick(tick);
|
||||||
|
if (IsEntityVectorVisible(ent, ent->hitboxes.GetHitbox(1)->center))
|
||||||
|
{
|
||||||
|
data = tick;
|
||||||
|
tick_score = dist;
|
||||||
|
}
|
||||||
|
backtrack::RestoreEntity(ent->m_IDX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// No entity
|
||||||
|
if (!data)
|
||||||
|
scr = FLT_MAX;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
target = (*data).m_vecOrigin;
|
||||||
|
scr = (*data).m_vecOrigin.DistTo(LOCAL_E->m_vecOrigin());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
scr = FLT_MAX;
|
||||||
}
|
}
|
||||||
// Demoknight
|
// Demoknight
|
||||||
if (demoknight_mode)
|
if (demoknight_mode)
|
||||||
@ -144,8 +196,12 @@ std::pair<CachedEntity *, Vector> FindBestEnt(bool teammate, bool Predict, bool
|
|||||||
predicted = target;
|
predicted = target;
|
||||||
bestscr = scr;
|
bestscr = scr;
|
||||||
prevent = ent->m_IDX;
|
prevent = ent->m_IDX;
|
||||||
|
if (shouldBacktrack)
|
||||||
|
best_data = data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (demoknight_mode && best_data)
|
||||||
|
backtrack::MoveToTick(*best_data);
|
||||||
return { bestent, predicted };
|
return { bestent, predicted };
|
||||||
}
|
}
|
||||||
static float slow_change_dist_y{};
|
static float slow_change_dist_y{};
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
#include <hacks/Trigger.hpp>
|
#include <hacks/Trigger.hpp>
|
||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
#include <hacks/Backtrack.hpp>
|
|
||||||
#include <PlayerTools.hpp>
|
#include <PlayerTools.hpp>
|
||||||
#include <settings/Bool.hpp>
|
#include <settings/Bool.hpp>
|
||||||
#include "Backtrack.hpp"
|
#include "Backtrack.hpp"
|
||||||
@ -40,46 +39,6 @@ float target_time = 0.0f;
|
|||||||
int last_hb_traced = 0;
|
int last_hb_traced = 0;
|
||||||
Vector forward;
|
Vector forward;
|
||||||
|
|
||||||
// Filters for backtrack
|
|
||||||
bool tick_filter(CachedEntity *entity, hacks::tf2::backtrack::BacktrackData tick)
|
|
||||||
{
|
|
||||||
// Check if it intersects any hitbox
|
|
||||||
int num_hitboxes = 18;
|
|
||||||
|
|
||||||
// Only need head hitbox
|
|
||||||
if (HeadPreferable(entity))
|
|
||||||
num_hitboxes = 1;
|
|
||||||
for (int i = 0; i < num_hitboxes; i++)
|
|
||||||
{
|
|
||||||
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
|
// The main function of the triggerbot
|
||||||
void CreateMove()
|
void CreateMove()
|
||||||
{
|
{
|
||||||
@ -95,39 +54,51 @@ void CreateMove()
|
|||||||
|
|
||||||
// Get an ent in front of the player
|
// Get an ent in front of the player
|
||||||
CachedEntity *ent = nullptr;
|
CachedEntity *ent = nullptr;
|
||||||
std::optional<hacks::tf2::backtrack::BacktrackData> bt_data;
|
|
||||||
|
|
||||||
if (!hacks::tf2::backtrack::isBacktrackEnabled)
|
bool state_good = false;
|
||||||
ent = FindEntInSight(EffectiveTargetingRange());
|
|
||||||
// Backtrack, use custom filter to check if tick is in crosshair
|
bool shouldBacktrack = tf2::backtrack::backtrackEnabled() && !hacks::tf2::backtrack::hasData();
|
||||||
|
|
||||||
|
if (shouldBacktrack)
|
||||||
|
{
|
||||||
|
float target_range = EffectiveTargetingRange();
|
||||||
|
for (auto &ent_data : tf2::backtrack::bt_data)
|
||||||
|
{
|
||||||
|
if (state_good)
|
||||||
|
break;
|
||||||
|
for (auto &tick_data : ent_data)
|
||||||
|
{
|
||||||
|
if (!tick_data.in_range)
|
||||||
|
continue;
|
||||||
|
tf2::backtrack::MoveToTick(tick_data);
|
||||||
|
ent = FindEntInSight(target_range);
|
||||||
|
// Restore the data
|
||||||
|
tf2::backtrack::RestoreEntity(tick_data.entidx);
|
||||||
|
if (ent)
|
||||||
|
{
|
||||||
|
state_good = IsTargetStateGood(ent, tick_data);
|
||||||
|
if (state_good)
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
tf2::backtrack::RestoreEntity(tick_data.entidx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Set up forward Vector
|
ent = FindEntInSight(EffectiveTargetingRange());
|
||||||
forward = GetForwardVector(EffectiveTargetingRange(), LOCAL_E);
|
|
||||||
|
|
||||||
// Call closest tick with our Tick filter func
|
|
||||||
auto closest_data = hacks::tf2::backtrack::getClosestTick(g_pLocalPlayer->v_Eye, hacks::tf2::backtrack::defaultEntFilter, 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;
|
|
||||||
hacks::tf2::backtrack::SetBacktrackData(ent, *bt_data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if dormant or null to prevent crashes
|
// Check if dormant or null to prevent crashes
|
||||||
if (CE_BAD(ent))
|
if (CE_BAD(ent))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (!shouldBacktrack)
|
||||||
|
state_good = IsTargetStateGood(ent, std::nullopt);
|
||||||
|
|
||||||
// Determine whether the triggerbot should shoot, then act accordingly
|
// Determine whether the triggerbot should shoot, then act accordingly
|
||||||
if (IsTargetStateGood(ent, bt_data ? &*bt_data : nullptr))
|
if (state_good)
|
||||||
{
|
{
|
||||||
target_time = backup_time;
|
target_time = backup_time;
|
||||||
if (delay)
|
if (delay)
|
||||||
@ -235,8 +206,10 @@ bool ShouldShoot()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A second check to determine whether a target is good enough to be aimed at
|
// A second check to determine whether a target is good enough to be aimed at
|
||||||
bool IsTargetStateGood(CachedEntity *entity, hacks::tf2::backtrack::BacktrackData *tick)
|
bool IsTargetStateGood(CachedEntity *entity, std::optional<tf2::backtrack::BacktrackData> bt_data)
|
||||||
{
|
{
|
||||||
|
if (bt_data)
|
||||||
|
tf2::backtrack::MoveToTick(*bt_data);
|
||||||
// Check for Players
|
// Check for Players
|
||||||
if (entity->m_Type() == ENTITY_PLAYER)
|
if (entity->m_Type() == ENTITY_PLAYER)
|
||||||
{
|
{
|
||||||
@ -283,46 +256,41 @@ bool IsTargetStateGood(CachedEntity *entity, hacks::tf2::backtrack::BacktrackDat
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backtrack did these in the tick check
|
// Head hitbox detection
|
||||||
if (!tick)
|
if (HeadPreferable(entity))
|
||||||
{
|
{
|
||||||
// Head hitbox detection
|
if (last_hb_traced != hitbox_t::head)
|
||||||
if (HeadPreferable(entity))
|
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)
|
||||||
{
|
{
|
||||||
if (last_hb_traced != hitbox_t::head)
|
// 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;
|
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
|
// Target passed the tests so return true
|
||||||
return true;
|
return true;
|
||||||
@ -407,7 +375,7 @@ CachedEntity *FindEntInSight(float range, bool no_players)
|
|||||||
g_ITrace->TraceRay(ray, 0x4200400B, &trace::filter_default, &trace);
|
g_ITrace->TraceRay(ray, 0x4200400B, &trace::filter_default, &trace);
|
||||||
|
|
||||||
// Return an ent if that is what we hit
|
// Return an ent if that is what we hit
|
||||||
if (trace.m_pEnt)
|
if (trace.DidHit() && trace.m_pEnt && (IClientEntity *) trace.m_pEnt != g_IEntityList->GetClientEntity(0))
|
||||||
{
|
{
|
||||||
last_hb_traced = trace.hitbox;
|
last_hb_traced = trace.hitbox;
|
||||||
CachedEntity *ent = ENTITY(((IClientEntity *) trace.m_pEnt)->entindex());
|
CachedEntity *ent = ENTITY(((IClientEntity *) trace.m_pEnt)->entindex());
|
||||||
|
@ -532,7 +532,7 @@ void ApplyChams(ChamColors colors, bool recurse, bool render_original, bool over
|
|||||||
|
|
||||||
DEFINE_HOOKED_METHOD(DrawModelExecute, void, IVModelRender *this_, const DrawModelState_t &state, const ModelRenderInfo_t &info, matrix3x4_t *bone)
|
DEFINE_HOOKED_METHOD(DrawModelExecute, void, IVModelRender *this_, const DrawModelState_t &state, const ModelRenderInfo_t &info, matrix3x4_t *bone)
|
||||||
{
|
{
|
||||||
if (!isHackActive() || effect_glow::g_EffectGlow.drawing || chams_attachment_drawing || (*clean_screenshots && g_IEngine->IsTakingScreenshot()) || CE_BAD(LOCAL_E) || (!enable && !no_hats && !no_arms && !blend_zoom && !arms_chams && !local_weapon_chams && !(hacks::tf2::backtrack::chams && hacks::tf2::backtrack::isBacktrackEnabled)))
|
if (!isHackActive() || effect_glow::g_EffectGlow.drawing || chams_attachment_drawing || (*clean_screenshots && g_IEngine->IsTakingScreenshot()) || CE_BAD(LOCAL_E) || (!enable && !no_hats && !no_arms && !blend_zoom && !arms_chams && !local_weapon_chams /*&& !(hacks::tf2::backtrack::chams && hacks::tf2::backtrack::isBacktrackEnabled)*/))
|
||||||
return original::DrawModelExecute(this_, state, info, bone);
|
return original::DrawModelExecute(this_, state, info, bone);
|
||||||
|
|
||||||
PROF_SECTION(DrawModelExecute);
|
PROF_SECTION(DrawModelExecute);
|
||||||
@ -770,35 +770,43 @@ DEFINE_HOOKED_METHOD(DrawModelExecute, void, IVModelRender *this_, const DrawMod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Backtrack chams
|
// Backtrack chams
|
||||||
using namespace hacks::tf2;
|
namespace bt = hacks::tf2::backtrack;
|
||||||
if (backtrack::chams && backtrack::isBacktrackEnabled)
|
if (bt::chams && bt::backtrackEnabled())
|
||||||
{
|
{
|
||||||
// TODO: Allow for a fade between the entity's color and a specified color, it would look cool but i'm lazy
|
// TODO: Allow for a fade between the entity's color and a specified color, it would look cool but i'm lazy
|
||||||
if (ent->m_bAlivePlayer())
|
if (ent->m_bAlivePlayer() && (int) bt::bt_data.size() >= info.entity_index > 0)
|
||||||
{
|
{
|
||||||
// Get ticks
|
// Get ticks
|
||||||
auto good_ticks = backtrack::getGoodTicks(info.entity_index);
|
auto ticks = bt::bt_data.at(info.entity_index - 1);
|
||||||
|
|
||||||
|
std::vector<bt::BacktrackData> good_ticks;
|
||||||
|
for (auto &tick : ticks)
|
||||||
|
{
|
||||||
|
if (tick.in_range)
|
||||||
|
good_ticks.push_back(tick);
|
||||||
|
}
|
||||||
if (!good_ticks.empty())
|
if (!good_ticks.empty())
|
||||||
{
|
{
|
||||||
// Setup chams according to user settings
|
// Setup chams according to user settings
|
||||||
ChamColors backtrack_colors;
|
ChamColors backtrack_colors;
|
||||||
backtrack_colors.rgba = *backtrack::chams_color;
|
backtrack_colors.rgba = *bt::chams_color;
|
||||||
backtrack_colors.rgba_overlay = *backtrack::chams_color_overlay;
|
backtrack_colors.rgba_overlay = *bt::chams_color_overlay;
|
||||||
backtrack_colors.envmap_r = *backtrack::chams_envmap_tint_r;
|
backtrack_colors.envmap_r = *bt::chams_envmap_tint_r;
|
||||||
backtrack_colors.envmap_g = *backtrack::chams_envmap_tint_g;
|
backtrack_colors.envmap_g = *bt::chams_envmap_tint_g;
|
||||||
backtrack_colors.envmap_b = *backtrack::chams_envmap_tint_b;
|
backtrack_colors.envmap_b = *bt::chams_envmap_tint_b;
|
||||||
|
|
||||||
for (unsigned i = 0; i <= (unsigned) std::max(*backtrack::chams_ticks, 1); i++)
|
for (unsigned i = 0; i <= (unsigned) std::max(*bt::chams_ticks, 1); i++)
|
||||||
{
|
{
|
||||||
// Can't draw more than we have
|
// Can't draw more than we have
|
||||||
if (i >= good_ticks.size())
|
if (i >= good_ticks.size())
|
||||||
break;
|
break;
|
||||||
if (!good_ticks[i].bones.empty())
|
if (!good_ticks[i].bones.empty())
|
||||||
ApplyChams(backtrack_colors, false, false, *backtrack::chams_overlay, false, *backtrack::chams_wireframe, false, entity, this_, state, info, &good_ticks[i].bones[0]);
|
ApplyChams(backtrack_colors, false, false, *bt::chams_overlay, false, *bt::chams_wireframe, false, entity, this_, state, info, good_ticks[i].bones.data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset it!
|
// Reset it!
|
||||||
g_IVModelRender->ForcedMaterialOverride(nullptr);
|
g_IVModelRender->ForcedMaterialOverride(nullptr);
|
||||||
g_IVRenderView->SetColorModulation(original_color);
|
g_IVRenderView->SetColorModulation(original_color);
|
||||||
@ -818,7 +826,7 @@ DEFINE_HOOKED_METHOD(DrawModelExecute, void, IVModelRender *this_, const DrawMod
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Don't do it when we are trying to enforce backtrack chams
|
// Don't do it when we are trying to enforce backtrack chams
|
||||||
if (!hacks::tf2::backtrack::isDrawing)
|
// if (!hacks::tf2::backtrack::isDrawing)
|
||||||
return original::DrawModelExecute(this_, state, info, bone);
|
return original::DrawModelExecute(this_, state, info, bone);
|
||||||
} // namespace hooked_methods
|
} // namespace hooked_methods
|
||||||
} // namespace hooked_methods
|
} // namespace hooked_methods
|
||||||
|
Reference in New Issue
Block a user