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 iHealth;
|
||||
|
||||
offset_t m_vecOrigin;
|
||||
|
||||
// sentry
|
||||
offset_t m_iAmmoShells; // sentry shells
|
||||
offset_t m_iAmmoRockets; // use only with if (GetLevel() == 3)
|
||||
@ -184,6 +186,7 @@ public:
|
||||
offset_t m_nSequence;
|
||||
offset_t m_flSimulationTime;
|
||||
offset_t m_flAnimTime;
|
||||
offset_t m_flCycle;
|
||||
offset_t m_angRotation;
|
||||
|
||||
offset_t m_hOwnerEntity;
|
||||
|
@ -102,5 +102,8 @@ enum hitbox_t
|
||||
foot_L = 14,
|
||||
hip_R = 15,
|
||||
knee_R = 16,
|
||||
foot_R = 17
|
||||
foot_R = 17,
|
||||
|
||||
// Always last
|
||||
HITBOXES_SIZE
|
||||
};
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "common.hpp"
|
||||
#include "entityhitboxcache.hpp"
|
||||
|
||||
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
|
||||
{
|
||||
public:
|
||||
int tickcount{};
|
||||
int entidx{};
|
||||
|
||||
std::array<hitboxData, 18> hitboxes{};
|
||||
Vector m_vecOrigin{};
|
||||
Vector m_vecAngles{};
|
||||
|
||||
Vector m_vecMins{};
|
||||
Vector m_vecMaxs{};
|
||||
|
||||
float m_flSimulationTime{};
|
||||
bool has_updated{};
|
||||
bool in_range{};
|
||||
|
||||
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::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
|
||||
extern settings::Boolean chams;
|
||||
extern settings::Boolean chams_wireframe;
|
||||
extern settings::Int chams_ticks;
|
||||
extern settings::Rgba chams_color;
|
||||
extern settings::Boolean chams_solid;
|
||||
extern settings::Boolean chams_overlay;
|
||||
extern settings::Rgba chams_color_overlay;
|
||||
extern settings::Float chams_envmap_tint_r;
|
||||
extern settings::Float chams_envmap_tint_g;
|
||||
extern settings::Float chams_envmap_tint_b;
|
||||
#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
|
||||
|
@ -8,17 +8,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.hpp"
|
||||
#include "Backtrack.hpp"
|
||||
|
||||
namespace hacks::tf2::backtrack
|
||||
{
|
||||
class BacktrackData;
|
||||
}
|
||||
namespace hacks::shared::triggerbot
|
||||
{
|
||||
|
||||
void CreateMove();
|
||||
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);
|
||||
bool HeadPreferable(CachedEntity *target);
|
||||
bool UpdateAimkey();
|
||||
|
@ -35,8 +35,8 @@ public:
|
||||
inline static int SetAbsOrigin(IClientEntity *self, Vector const &origin)
|
||||
{
|
||||
typedef int (*SetAbsOrigin_t)(IClientEntity *, Vector const &);
|
||||
uintptr_t addr = e8call_direct(gSignatures.GetClientSignature("E8 ? ? ? ? EB 7D 8B 42 04"));
|
||||
SetAbsOrigin_t SetAbsOrigin_fn = SetAbsOrigin_t(addr);
|
||||
static uintptr_t addr = e8call_direct(gSignatures.GetClientSignature("E8 ? ? ? ? EB 7D 8B 42 04"));
|
||||
static SetAbsOrigin_t SetAbsOrigin_fn = SetAbsOrigin_t(addr);
|
||||
|
||||
return SetAbsOrigin_fn(self, origin);
|
||||
}
|
||||
|
@ -29,7 +29,9 @@ void NetVars::Init()
|
||||
this->m_Collision = gNetvars.get_offset("DT_BaseEntity", "m_Collision");
|
||||
this->m_flSimulationTime = gNetvars.get_offset("DT_BaseEntity", "m_flSimulationTime");
|
||||
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_vecOrigin = gNetvars.get_offset("DT_BaseEntity", "m_vecOrigin");
|
||||
|
||||
IF_GAME(IsTF2())
|
||||
{
|
||||
|
@ -261,15 +261,38 @@ bool shouldMeleeCrit()
|
||||
{
|
||||
if (!melee || g_pLocalPlayer->weapon_mode != weapon_melee)
|
||||
return false;
|
||||
if (hacks::tf2::backtrack::isBacktrackEnabled)
|
||||
if (hacks::tf2::backtrack::backtrackEnabled())
|
||||
{
|
||||
// Closest tick for melee (default filter carry)
|
||||
auto closest_tick = hacks::tf2::backtrack::getClosestTick(LOCAL_E->m_vecOrigin(), hacks::tf2::backtrack::defaultEntFilter, hacks::tf2::backtrack::defaultTickFilter);
|
||||
// Closest tick for melee
|
||||
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
|
||||
if (closest_tick)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
else
|
||||
|
@ -140,7 +140,7 @@ matrix3x4_t *EntityHitboxCache::GetBones(int numbones)
|
||||
numbones = MAXSTUDIOBONES;
|
||||
}
|
||||
|
||||
if (bones.size() < (size_t) numbones)
|
||||
if (bones.size() != (size_t) numbones)
|
||||
bones.resize(numbones);
|
||||
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_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 ?
|
||||
@ -165,23 +183,6 @@ static bool CarryingHeatmaker()
|
||||
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)
|
||||
{
|
||||
bool isIdle = target_found ? false : hacks::shared::followbot::isIdle();
|
||||
@ -235,6 +236,7 @@ static void CreateMove()
|
||||
slow_aim = *normal_slow_aim;
|
||||
fov = *normal_fov;
|
||||
|
||||
updateShouldBacktrack();
|
||||
spectatorUpdate();
|
||||
|
||||
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)
|
||||
{
|
||||
auto previous = SDL_GetMouseState(&PreviousX, &PreviousY);
|
||||
SDL_GetMouseState(&PreviousX, &PreviousY);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto current = SDL_GetMouseState(&CurrentX, &CurrentY);
|
||||
SDL_GetMouseState(&CurrentX, &CurrentY);
|
||||
last_mouse_check = g_GlobalVars->curtime;
|
||||
}
|
||||
|
||||
@ -509,8 +511,23 @@ CachedEntity *RetrieveBestTarget(bool aimkey_state)
|
||||
{
|
||||
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
|
||||
if (IsTargetStateGood(target_last))
|
||||
else if (IsTargetStateGood(target_last))
|
||||
{
|
||||
// If it is then return it again
|
||||
return target_last;
|
||||
@ -520,15 +537,38 @@ CachedEntity *RetrieveBestTarget(bool aimkey_state)
|
||||
|
||||
float target_highest_score, scr = 0.0f;
|
||||
CachedEntity *ent;
|
||||
CachedEntity *target_highest_ent = 0;
|
||||
target_highest_score = -256;
|
||||
CachedEntity *target_highest_ent = 0;
|
||||
target_highest_score = -256;
|
||||
std::optional<hacks::tf2::backtrack::BacktrackData> bt_tick = std::nullopt;
|
||||
for (int i = 1; i <= HIGHEST_ENTITY; i++)
|
||||
{
|
||||
ent = ENTITY(i);
|
||||
if (CE_BAD(ent))
|
||||
continue; // Check for null and dormant
|
||||
// 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
|
||||
if (GetWeaponMode() == weaponmode::weapon_melee || (int) priority_mode == 2)
|
||||
@ -561,10 +601,17 @@ CachedEntity *RetrieveBestTarget(bool aimkey_state)
|
||||
{
|
||||
target_highest_score = scr;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -596,34 +643,17 @@ bool IsTargetStateGood(CachedEntity *entity)
|
||||
else
|
||||
{
|
||||
float swingrange = EffectiveTargetingRange();
|
||||
if (!shouldBacktrack() || entity->m_Type() != ENTITY_PLAYER)
|
||||
{
|
||||
int hb = BestHitbox(entity);
|
||||
if (hb == -1)
|
||||
return false;
|
||||
Vector newangle = GetAimAtAngles(g_pLocalPlayer->v_Eye, entity->hitboxes.GetHitbox(hb)->center, LOCAL_E);
|
||||
trace_t trace;
|
||||
Ray_t ray;
|
||||
trace::filter_default.SetSelf(RAW_ENT(g_pLocalPlayer->entity));
|
||||
ray.Init(g_pLocalPlayer->v_Eye, GetForwardVector(g_pLocalPlayer->v_Eye, newangle, swingrange, LOCAL_E));
|
||||
g_ITrace->TraceRay(ray, MASK_SHOT_HULL, &trace::filter_default, &trace);
|
||||
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;
|
||||
}
|
||||
int hb = BestHitbox(entity);
|
||||
if (hb == -1)
|
||||
return false;
|
||||
Vector newangle = GetAimAtAngles(g_pLocalPlayer->v_Eye, entity->hitboxes.GetHitbox(hb)->center, LOCAL_E);
|
||||
trace_t trace;
|
||||
Ray_t ray;
|
||||
trace::filter_default.SetSelf(RAW_ENT(g_pLocalPlayer->entity));
|
||||
ray.Init(g_pLocalPlayer->v_Eye, GetForwardVector(g_pLocalPlayer->v_Eye, newangle, swingrange, LOCAL_E));
|
||||
g_ITrace->TraceRay(ray, MASK_SHOT_HULL, &trace::filter_default, &trace);
|
||||
if ((IClientEntity *) trace.m_pEnt != RAW_ENT(entity))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Rage only check
|
||||
@ -731,15 +761,7 @@ bool IsTargetStateGood(CachedEntity *entity)
|
||||
Vector pos = GetBuildingPosition(ENTITY(sentry));
|
||||
if (hitbox == -1 || !entity->hitboxes.GetHitbox(cd.hitbox))
|
||||
return false;
|
||||
if (shouldBacktrack())
|
||||
{
|
||||
// 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)))
|
||||
if (!IsVectorVisible(pos, entity->hitboxes.GetHitbox(cd.hitbox)->center, false, ENTITY(sentry)))
|
||||
return false;
|
||||
}
|
||||
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)
|
||||
return false;
|
||||
Vector pos = GetBuildingPosition(ENTITY(sentry));
|
||||
if (shouldBacktrack())
|
||||
{
|
||||
// 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))
|
||||
if (!IsVectorVisible(pos, entity->m_vecOrigin(), false))
|
||||
return false;
|
||||
}
|
||||
if (fov > 0.0f && cd.fov > fov)
|
||||
@ -924,15 +938,7 @@ void Aim(CachedEntity *entity)
|
||||
auto hitboxmin = hb->min;
|
||||
auto hitboxmax = hb->max;
|
||||
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
|
||||
minx = hitboxmin.x;
|
||||
miny = hitboxmin.y;
|
||||
@ -971,15 +977,8 @@ void Aim(CachedEntity *entity)
|
||||
if (silent && !slow_aim)
|
||||
g_pLocalPlayer->bUseSilentAngles = true;
|
||||
// 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));
|
||||
// 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;
|
||||
// Finish function
|
||||
return;
|
||||
@ -1095,84 +1094,71 @@ Vector PredictEntity(CachedEntity *entity, bool vischeck)
|
||||
// Pull out predicted data
|
||||
AimbotCalculatedData_s &cd = calculated_data_array[entity->m_IDX];
|
||||
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;
|
||||
|
||||
if (!shouldBacktrack() || entity->m_Type() != ENTITY_PLAYER)
|
||||
// Players
|
||||
if ((entity->m_Type() == ENTITY_PLAYER))
|
||||
{
|
||||
// Players
|
||||
if ((entity->m_Type() == ENTITY_PLAYER))
|
||||
// If using projectiles, predict a vector
|
||||
if (projectileAimbotRequired)
|
||||
{
|
||||
// If using projectiles, predict a vector
|
||||
if (projectileAimbotRequired)
|
||||
{
|
||||
std::pair<Vector, Vector> tmp_result;
|
||||
// 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;
|
||||
}
|
||||
std::pair<Vector, Vector> tmp_result;
|
||||
// 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
|
||||
{
|
||||
// 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);
|
||||
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;
|
||||
}
|
||||
// Don't use the intial velocity compensated one in vischecks
|
||||
if (vischeck)
|
||||
result = tmp_result.first;
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
auto data = hacks::tf2::backtrack::getClosestEntTick(entity, LOCAL_E->m_vecOrigin(), aimbotTickFilter);
|
||||
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);
|
||||
}
|
||||
result = entity->m_vecOrigin();
|
||||
}
|
||||
|
||||
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 result;
|
||||
}
|
||||
@ -1303,38 +1289,15 @@ int BestHitbox(CachedEntity *target)
|
||||
return 12;
|
||||
}
|
||||
|
||||
// Backtracking and preferred hitbox
|
||||
if (shouldBacktrack())
|
||||
{
|
||||
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))
|
||||
// preferred hitbox
|
||||
if (target->hitboxes.VisibilityCheck(preferred))
|
||||
return preferred;
|
||||
// Else attempt to find any hitbox at all
|
||||
if (!shouldBacktrack())
|
||||
for (int i = projectile_mode ? 1 : 0; i < target->hitboxes.GetNumHitboxes() && i < 6; i++)
|
||||
{
|
||||
if (target->hitboxes.VisibilityCheck(i))
|
||||
return i;
|
||||
}
|
||||
for (int i = projectile_mode ? 1 : 0; i < target->hitboxes.GetNumHitboxes() && i < 6; i++)
|
||||
{
|
||||
if (target->hitboxes.VisibilityCheck(i))
|
||||
return i;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
@ -1381,28 +1344,17 @@ bool VischeckPredictedEntity(CachedEntity *entity)
|
||||
{
|
||||
// Retrieve predicted data
|
||||
AimbotCalculatedData_s &cd = calculated_data_array[entity->m_IDX];
|
||||
if (cd.vcheck_tick == tickcount)
|
||||
if (cd.vcheck_tick == tickcount && !shouldBacktrack(entity))
|
||||
return cd.visible;
|
||||
if (!shouldBacktrack() || entity->m_Type() != ENTITY_PLAYER)
|
||||
{
|
||||
// Update info
|
||||
cd.vcheck_tick = tickcount;
|
||||
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;
|
||||
}
|
||||
}
|
||||
// Update info
|
||||
cd.vcheck_tick = tickcount;
|
||||
if (extrapolate || projectileAimbotRequired || entity->m_Type() != ENTITY_PLAYER)
|
||||
cd.visible = IsEntityVectorVisible(entity, PredictEntity(entity, true), true);
|
||||
else
|
||||
{
|
||||
auto data = hacks::tf2::backtrack::getClosestEntTick(entity, LOCAL_E->m_vecOrigin(), aimbotTickFilter);
|
||||
if (data && IsEntityVectorVisible(entity, data->hitboxes.at((cd.hitbox == -1 || cd.hitbox >= 18) ? 0 : cd.hitbox).center, false, MASK_SHOT))
|
||||
cd.visible = 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;
|
||||
}
|
||||
return cd.visible;
|
||||
|
@ -321,32 +321,24 @@ static bool doRageBackstab()
|
||||
static bool legit_stab = false;
|
||||
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;
|
||||
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;
|
||||
CachedEntity *ent = ENTITY(tick.entidx);
|
||||
Vector target_vec = tick.m_vecOrigin;
|
||||
|
||||
// Angle check
|
||||
Vector newangle;
|
||||
if (legit_stab)
|
||||
newangle = g_pLocalPlayer->v_OrigViewangles;
|
||||
else
|
||||
newangle = GetAimAtAngles(g_pLocalPlayer->v_Eye, distcheck, LOCAL_E);
|
||||
if (!angleCheck(ent, target_worldspace, newangle) && !canFaceStab(ent))
|
||||
Vector target_worldspace = target_vec;
|
||||
target_worldspace += (RAW_ENT(ent)->GetCollideable()->OBBMins() + RAW_ENT(ent)->GetCollideable()->OBBMaxs()) / 2.0f;
|
||||
|
||||
Vector angle = GetAimAtAngles(g_pLocalPlayer->v_Eye, target_worldspace);
|
||||
|
||||
if (!angleCheck(ent, target_worldspace, angle))
|
||||
return false;
|
||||
|
||||
// Check if we can hit the enemy
|
||||
if (doMovedSwingTrace(ent, tick.m_vecOrigin))
|
||||
trace_t trace;
|
||||
if (doMovedSwingTrace(ent, target_vec))
|
||||
{
|
||||
// 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()))
|
||||
{
|
||||
newangle_apply = newangle;
|
||||
return true;
|
||||
}
|
||||
newangle_apply = angle;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -359,29 +351,29 @@ static bool doBacktrackStab(bool legit = false)
|
||||
// Set for our filter
|
||||
legit_stab = legit;
|
||||
// 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);
|
||||
// Targeting checks
|
||||
if (CE_BAD(ent) || !ent->m_bAlivePlayer() || !ent->m_bEnemy() || !player_tools::shouldTarget(ent) || IsPlayerInvulnerable(ent))
|
||||
continue;
|
||||
|
||||
// Get the best tick for that ent
|
||||
auto tick_data = hacks::tf2::backtrack::getBestTick(ent, backtrackFilter);
|
||||
|
||||
// We found something matching the criterias, break out
|
||||
if (tick_data)
|
||||
for (auto &bt_tick : hacks::tf2::backtrack::bt_data[i - 1])
|
||||
{
|
||||
stab_data = *tick_data;
|
||||
stab_ent = ent;
|
||||
break;
|
||||
if (bt_tick.in_range && IsTickGood(bt_tick))
|
||||
{
|
||||
// We found something matching the criterias, break out
|
||||
stab_data = bt_tick;
|
||||
stab_ent = ent;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We found a good 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->viewangles = newangle_apply;
|
||||
g_pLocalPlayer->bUseSilentAngles = true;
|
||||
@ -406,6 +398,8 @@ void CreateMove()
|
||||
return;
|
||||
if (!CanShoot())
|
||||
return;
|
||||
|
||||
bool shouldBacktrack = backtrack::backtrackEnabled() && !backtrack::hasData();
|
||||
switch (*mode)
|
||||
{
|
||||
case 0:
|
||||
@ -415,7 +409,7 @@ void CreateMove()
|
||||
doRageBackstab();
|
||||
break;
|
||||
case 2:
|
||||
if (hacks::tf2::backtrack::isBacktrackEnabled)
|
||||
if (shouldBacktrack)
|
||||
{
|
||||
if (*hacks::tf2::backtrack::latency <= 190 && doRageBackstab())
|
||||
break;
|
||||
@ -427,7 +421,7 @@ void CreateMove()
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (hacks::tf2::backtrack::isBacktrackEnabled)
|
||||
if (shouldBacktrack)
|
||||
{
|
||||
if (*hacks::tf2::backtrack::latency <= 190 && doLegitBackstab())
|
||||
break;
|
||||
|
@ -1,38 +1,14 @@
|
||||
/*
|
||||
* Remade on May the 3rd 2020
|
||||
* Author: BenCat07
|
||||
*
|
||||
*/
|
||||
#include "common.hpp"
|
||||
#include "Backtrack.hpp"
|
||||
#include "PlayerTools.hpp"
|
||||
#include "memory"
|
||||
|
||||
namespace hacks::tf2::backtrack
|
||||
{
|
||||
// Internal rvars
|
||||
static settings::Boolean enabled{ "backtrack.enabled", "false" };
|
||||
static settings::Boolean draw{ "backtrack.draw", "false" };
|
||||
static settings::Boolean enabled("backtrack.enabled", "false");
|
||||
settings::Float latency("backtrack.latency", "0");
|
||||
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
|
||||
static settings::Boolean draw("backtrack.draw", "false");
|
||||
settings::Boolean chams{ "backtrack.chams", "false" };
|
||||
settings::Boolean chams_wireframe{ "backtrack.chams.wireframe", "false" };
|
||||
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" };
|
||||
#endif
|
||||
|
||||
static bool isEnabled();
|
||||
|
||||
#define MAX_BACKTRACK_TICKS 66
|
||||
|
||||
// Check if backtrack is enabled
|
||||
bool isBacktrackEnabled;
|
||||
#if ENABLE_VISUALS
|
||||
// Drawing Backtrack chams
|
||||
bool isDrawing;
|
||||
#endif
|
||||
static bool isBacktrackEnabled = false;
|
||||
|
||||
// Apply Backtrack
|
||||
void ApplyBacktrack()
|
||||
// Is any backtrack tick set for a player currently?
|
||||
static std::optional<BacktrackData> set_data;
|
||||
bool hasData()
|
||||
{
|
||||
if (!isBacktrackEnabled)
|
||||
return;
|
||||
if (bt_ent && bt_data)
|
||||
return (bool) set_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;
|
||||
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;
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
// Update tick to apply
|
||||
void SetBacktrackData(CachedEntity *ent, BacktrackData tick)
|
||||
// Latency to add for backtrack
|
||||
float getLatency()
|
||||
{
|
||||
bt_ent = ent;
|
||||
bt_data = tick;
|
||||
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;
|
||||
|
||||
// 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 getBestInternalTick(CachedEntity *, BacktrackData &data, std::optional<BacktrackData> &best_tick)
|
||||
bool isTickInRange(int tickcount)
|
||||
{
|
||||
// Best Score
|
||||
float bestScore = FLT_MAX;
|
||||
|
||||
// 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;
|
||||
int delta_tickcount = abs(tickcount - current_user_cmd->tick_count + TIME_TO_TICKS(getLatency() / 1000.0f));
|
||||
return TICKS_TO_TIME(delta_tickcount) <= 0.2f - TICKS_TO_TIME(2);
|
||||
}
|
||||
|
||||
// Is backtrack enabled?
|
||||
@ -145,207 +131,6 @@ bool isEnabled()
|
||||
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
|
||||
void adjustPing(INetChannel *ch)
|
||||
{
|
||||
@ -362,207 +147,235 @@ void adjustPing(INetChannel *ch)
|
||||
}
|
||||
}
|
||||
|
||||
// Latency to add for backtrack
|
||||
float getLatency()
|
||||
// Move target entity to tick
|
||||
void MoveToTick(BacktrackData data)
|
||||
{
|
||||
INetChannel *ch = (INetChannel *) g_IEngine->GetNetChannelInfo();
|
||||
// Track what actual latency we have
|
||||
float real_latency = 0.0f;
|
||||
CachedEntity *target = ENTITY(data.entidx);
|
||||
|
||||
// If we have a netchannel (just in case) set real latency to it
|
||||
if (ch)
|
||||
real_latency = ch->GetLatency(FLOW_OUTGOING) * 1000.0f;
|
||||
// Set entity data to match the target data
|
||||
re::C_BasePlayer::SetAbsOrigin(RAW_ENT(target), data.m_vecOrigin);
|
||||
re::C_BasePlayer::GetEyeAngles(RAW_ENT(target)) = data.m_vecAngles;
|
||||
|
||||
// Fix the latency
|
||||
float backtrack_latency = *latency - real_latency;
|
||||
// Need to reconstruct a bunch of data
|
||||
target->hitboxes.InvalidateCache();
|
||||
|
||||
// Clamp and apply rampup
|
||||
backtrack_latency = latency_rampup * std::clamp(backtrack_latency, 0.0f, std::max(800.0f - real_latency, 0.0f));
|
||||
// Mark all the hitboxes as valid so we don't recalc them and use the old data
|
||||
// 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
|
||||
void updateDatagram()
|
||||
// Restore ent to original state
|
||||
void RestoreEntity(int entidx)
|
||||
{
|
||||
INetChannel *ch = (INetChannel *) g_IEngine->GetNetChannelInfo();
|
||||
if (ch)
|
||||
MoveToTick(bt_data[entidx - 1][0]);
|
||||
// Undo tick setting
|
||||
set_data = std::nullopt;
|
||||
}
|
||||
|
||||
void CreateMoveEarly()
|
||||
{
|
||||
draw_positions.clear();
|
||||
isBacktrackEnabled = isEnabled();
|
||||
if (!isBacktrackEnabled)
|
||||
{
|
||||
int m_nInSequenceNr = ch->m_nInSequenceNr;
|
||||
int instate = ch->m_nInReliableState;
|
||||
if (m_nInSequenceNr > lastincomingsequence)
|
||||
latency_rampup = 0.0f;
|
||||
bt_data.clear();
|
||||
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;
|
||||
sequences.insert(sequences.begin(), CIncomingSequence(instate, m_nInSequenceNr, g_GlobalVars->realtime));
|
||||
ent_data.clear();
|
||||
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
|
||||
int getTicks()
|
||||
void CreateMoveLate()
|
||||
{
|
||||
float max_lat = getLatency() + 200.0f;
|
||||
if (!isBacktrackEnabled)
|
||||
return;
|
||||
|
||||
// Clamp
|
||||
max_lat = std::min(1000.0f, max_lat);
|
||||
red_position = std::nullopt;
|
||||
|
||||
// Get difference
|
||||
int ticks = TIME_TO_TICKS(max_lat / 1000.0f);
|
||||
return ticks;
|
||||
};
|
||||
// Bad player
|
||||
if (CE_BAD(LOCAL_E) || HasCondition<TFCond_HalloweenGhostMode>(LOCAL_E) || !LOCAL_E->m_bAlivePlayer())
|
||||
return;
|
||||
|
||||
void resetData(int entidx)
|
||||
{
|
||||
// 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))
|
||||
// No data set yet, try to get nearest to cursor
|
||||
if (!set_data && !g_pLocalPlayer->bUseSilentAngles)
|
||||
{
|
||||
// Found Closer tick
|
||||
if (tick.m_vecOrigin.DistTo(vec) < distance)
|
||||
float cursor_distance = FLT_MAX;
|
||||
for (auto &ent_data : bt_data)
|
||||
{
|
||||
// Does the tick pass the filter
|
||||
if (tick_filter(ent, tick))
|
||||
for (auto &tick_data : ent_data)
|
||||
{
|
||||
return_value = tick;
|
||||
distance = tick.m_vecOrigin.DistTo(vec);
|
||||
if (isTickInRange(tick_data.tickcount))
|
||||
{
|
||||
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
|
||||
std::optional<std::pair<CachedEntity *, BacktrackData>> getClosestTick(Vector vec, std::function<bool(CachedEntity *)> ent_filter, std::function<bool(CachedEntity *, BacktrackData)> tick_filter)
|
||||
void Shutdown()
|
||||
{
|
||||
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++)
|
||||
{
|
||||
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;
|
||||
bt_data.clear();
|
||||
sequences.clear();
|
||||
lastincomingsequence = 0;
|
||||
}
|
||||
|
||||
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([]() {
|
||||
EC::Register(EC::CreateMove, CreateMove, "backtrack_cm", EC::early);
|
||||
EC::Register(EC::CreateMoveWarp, CreateMove, "backtrack_cmw", EC::early);
|
||||
EC::Register(EC::CreateMove, CreateMoveLate, "backtrack_cmlate", EC::very_late);
|
||||
EC::Register(EC::CreateMoveWarp, CreateMoveLate, "backtrack_cmwlate", EC::very_late);
|
||||
EC::Register(EC::CreateMove, CreateMoveEarly, "bt_update", EC::very_early);
|
||||
EC::Register(EC::CreateMove, CreateMoveLate, "bt_createmove", EC::very_late);
|
||||
EC::Register(EC::Shutdown, Shutdown, "bt_shutdown");
|
||||
EC::Register(EC::LevelInit, Shutdown, "bt_shutdown");
|
||||
#if ENABLE_VISUALS
|
||||
EC::Register(EC::Draw, Draw, "backtrack_draw");
|
||||
EC::Register(EC::Draw, Draw, "bt_draw");
|
||||
#endif
|
||||
EC::Register(EC::LevelShutdown, LevelShutdown, "backtrack_levelshutdown");
|
||||
EC::Register(EC::LevelInit, LevelInit, "backtrack_levelinit");
|
||||
LevelInit();
|
||||
});
|
||||
|
||||
} // namespace hacks::tf2::backtrack
|
||||
|
@ -27,12 +27,17 @@ static Timer previous_entity_delay{};
|
||||
// TODO: Refactor this jank
|
||||
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{};
|
||||
// Too long since we focused it
|
||||
if (previous_entity_delay.check(100))
|
||||
prevent = -1;
|
||||
|
||||
bool shouldBacktrack = backtrack::backtrackEnabled() && !backtrack::hasData();
|
||||
|
||||
for (int i = 0; i < 1; i++)
|
||||
{
|
||||
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;
|
||||
else
|
||||
target = ent->hitboxes.GetHitbox(1)->center;
|
||||
if (!hacks::tf2::backtrack::isBacktrackEnabled && !IsEntityVectorVisible(ent, target))
|
||||
if (!shouldBacktrack && !IsEntityVectorVisible(ent, target))
|
||||
continue;
|
||||
if (zcheck && (ent->m_vecOrigin().z - LOCAL_E->m_vecOrigin().z) > 200.0f)
|
||||
continue;
|
||||
float scr = ent->m_flDistance();
|
||||
if (hacks::tf2::backtrack::isBacktrackEnabled && demoknight_mode)
|
||||
float scr = ent->m_flDistance();
|
||||
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);
|
||||
// No entity
|
||||
if (!data)
|
||||
scr = FLT_MAX;
|
||||
else
|
||||
if (backtrack::bt_data.size() >= ent->m_IDX)
|
||||
{
|
||||
target = (*data).m_vecOrigin;
|
||||
scr = (*data).m_vecOrigin.DistTo(LOCAL_E->m_vecOrigin());
|
||||
float tick_score = FLT_MAX;
|
||||
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
|
||||
if (demoknight_mode)
|
||||
@ -91,6 +118,8 @@ std::pair<CachedEntity *, Vector> FindBestEnt(bool teammate, bool Predict, bool
|
||||
if (bestent && predicted.z)
|
||||
{
|
||||
previous_entity_delay.update();
|
||||
if (demoknight_mode && best_data)
|
||||
backtrack::MoveToTick(*best_data);
|
||||
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;
|
||||
else
|
||||
target = ent->hitboxes.GetHitbox(1)->center;
|
||||
if (!hacks::tf2::backtrack::isBacktrackEnabled && !IsEntityVectorVisible(ent, target))
|
||||
if (!shouldBacktrack && !IsEntityVectorVisible(ent, target))
|
||||
continue;
|
||||
if (zcheck && (ent->m_vecOrigin().z - LOCAL_E->m_vecOrigin().z) > 200.0f)
|
||||
continue;
|
||||
float scr = ent->m_flDistance();
|
||||
if (hacks::tf2::backtrack::isBacktrackEnabled && demoknight_mode)
|
||||
float scr = ent->m_flDistance();
|
||||
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);
|
||||
// No entity
|
||||
if (!data)
|
||||
scr = FLT_MAX;
|
||||
else
|
||||
if (backtrack::bt_data.size() >= ent->m_IDX)
|
||||
{
|
||||
target = (*data).m_vecOrigin;
|
||||
scr = (*data).m_vecOrigin.DistTo(LOCAL_E->m_vecOrigin());
|
||||
float tick_score = FLT_MAX;
|
||||
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
|
||||
if (demoknight_mode)
|
||||
@ -144,8 +196,12 @@ std::pair<CachedEntity *, Vector> FindBestEnt(bool teammate, bool Predict, bool
|
||||
predicted = target;
|
||||
bestscr = scr;
|
||||
prevent = ent->m_IDX;
|
||||
if (shouldBacktrack)
|
||||
best_data = data;
|
||||
}
|
||||
}
|
||||
if (demoknight_mode && best_data)
|
||||
backtrack::MoveToTick(*best_data);
|
||||
return { bestent, predicted };
|
||||
}
|
||||
static float slow_change_dist_y{};
|
||||
|
@ -8,7 +8,6 @@
|
||||
|
||||
#include <hacks/Trigger.hpp>
|
||||
#include "common.hpp"
|
||||
#include <hacks/Backtrack.hpp>
|
||||
#include <PlayerTools.hpp>
|
||||
#include <settings/Bool.hpp>
|
||||
#include "Backtrack.hpp"
|
||||
@ -40,46 +39,6 @@ float target_time = 0.0f;
|
||||
int last_hb_traced = 0;
|
||||
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
|
||||
void CreateMove()
|
||||
{
|
||||
@ -95,39 +54,51 @@ void CreateMove()
|
||||
|
||||
// Get an ent in front of the player
|
||||
CachedEntity *ent = nullptr;
|
||||
std::optional<hacks::tf2::backtrack::BacktrackData> bt_data;
|
||||
|
||||
if (!hacks::tf2::backtrack::isBacktrackEnabled)
|
||||
ent = FindEntInSight(EffectiveTargetingRange());
|
||||
// Backtrack, use custom filter to check if tick is in crosshair
|
||||
bool state_good = false;
|
||||
|
||||
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
|
||||
{
|
||||
// Set up forward Vector
|
||||
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);
|
||||
}
|
||||
ent = FindEntInSight(EffectiveTargetingRange());
|
||||
}
|
||||
|
||||
// Check if dormant or null to prevent crashes
|
||||
if (CE_BAD(ent))
|
||||
return;
|
||||
|
||||
if (!shouldBacktrack)
|
||||
state_good = IsTargetStateGood(ent, std::nullopt);
|
||||
|
||||
// Determine whether the triggerbot should shoot, then act accordingly
|
||||
if (IsTargetStateGood(ent, bt_data ? &*bt_data : nullptr))
|
||||
if (state_good)
|
||||
{
|
||||
target_time = backup_time;
|
||||
if (delay)
|
||||
@ -235,8 +206,10 @@ bool ShouldShoot()
|
||||
}
|
||||
|
||||
// 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
|
||||
if (entity->m_Type() == ENTITY_PLAYER)
|
||||
{
|
||||
@ -283,46 +256,41 @@ bool IsTargetStateGood(CachedEntity *entity, hacks::tf2::backtrack::BacktrackDat
|
||||
return false;
|
||||
}
|
||||
|
||||
// Backtrack did these in the tick check
|
||||
if (!tick)
|
||||
// Head hitbox detection
|
||||
if (HeadPreferable(entity))
|
||||
{
|
||||
// Head hitbox detection
|
||||
if (HeadPreferable(entity))
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
@ -407,7 +375,7 @@ CachedEntity *FindEntInSight(float range, bool no_players)
|
||||
g_ITrace->TraceRay(ray, 0x4200400B, &trace::filter_default, &trace);
|
||||
|
||||
// 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;
|
||||
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)
|
||||
{
|
||||
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);
|
||||
|
||||
PROF_SECTION(DrawModelExecute);
|
||||
@ -770,35 +770,43 @@ DEFINE_HOOKED_METHOD(DrawModelExecute, void, IVModelRender *this_, const DrawMod
|
||||
}
|
||||
}
|
||||
// Backtrack chams
|
||||
using namespace hacks::tf2;
|
||||
if (backtrack::chams && backtrack::isBacktrackEnabled)
|
||||
namespace bt = hacks::tf2::backtrack;
|
||||
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
|
||||
if (ent->m_bAlivePlayer())
|
||||
if (ent->m_bAlivePlayer() && (int) bt::bt_data.size() >= info.entity_index > 0)
|
||||
{
|
||||
// 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())
|
||||
{
|
||||
// Setup chams according to user settings
|
||||
ChamColors backtrack_colors;
|
||||
backtrack_colors.rgba = *backtrack::chams_color;
|
||||
backtrack_colors.rgba_overlay = *backtrack::chams_color_overlay;
|
||||
backtrack_colors.envmap_r = *backtrack::chams_envmap_tint_r;
|
||||
backtrack_colors.envmap_g = *backtrack::chams_envmap_tint_g;
|
||||
backtrack_colors.envmap_b = *backtrack::chams_envmap_tint_b;
|
||||
backtrack_colors.rgba = *bt::chams_color;
|
||||
backtrack_colors.rgba_overlay = *bt::chams_color_overlay;
|
||||
backtrack_colors.envmap_r = *bt::chams_envmap_tint_r;
|
||||
backtrack_colors.envmap_g = *bt::chams_envmap_tint_g;
|
||||
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
|
||||
if (i >= good_ticks.size())
|
||||
break;
|
||||
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!
|
||||
g_IVModelRender->ForcedMaterialOverride(nullptr);
|
||||
g_IVRenderView->SetColorModulation(original_color);
|
||||
@ -818,7 +826,7 @@ DEFINE_HOOKED_METHOD(DrawModelExecute, void, IVModelRender *this_, const DrawMod
|
||||
return;
|
||||
}
|
||||
// Don't do it when we are trying to enforce backtrack chams
|
||||
if (!hacks::tf2::backtrack::isDrawing)
|
||||
return original::DrawModelExecute(this_, state, info, bone);
|
||||
// if (!hacks::tf2::backtrack::isDrawing)
|
||||
return original::DrawModelExecute(this_, state, info, bone);
|
||||
} // namespace hooked_methods
|
||||
} // namespace hooked_methods
|
||||
|
Reference in New Issue
Block a user