Add a ton of projectile prediction improvements
- Huntsman Aimbot now properly aims, works with CanShoot on or off, and with AutoShoot disabled - Pills are now properly aimed - Crossbow now targets teammates if they are missing health - Prediction now no longer passes through walls or gets stuck on janky static prop hitboxes - Make The Loose cannon work with aimbot and crithack
This commit is contained in:
parent
2c074d5598
commit
ab2b701e64
@ -130,6 +130,7 @@ public:
|
||||
offset_t hObserverTarget;
|
||||
|
||||
offset_t flChargeBeginTime;
|
||||
offset_t flDetonateTime;
|
||||
offset_t flLastFireTime;
|
||||
offset_t flObservedCritChance;
|
||||
offset_t hThrower;
|
||||
|
@ -52,6 +52,7 @@ void NetVars::Init()
|
||||
this->AttributeList = gNetvars.get_offset("DT_EconEntity", "m_AttributeManager", "m_Item",
|
||||
"m_AttributeList"); // hmmm
|
||||
this->flChargeBeginTime = gNetvars.get_offset("DT_WeaponPipebombLauncher", "PipebombLauncherLocalData", "m_flChargeBeginTime");
|
||||
this->flDetonateTime = gNetvars.get_offset("DT_WeaponGrenadeLauncher", "m_flDetonateTime");
|
||||
this->flLastFireTime = gNetvars.get_offset("DT_TFWeaponBase", "LocalActiveTFWeaponData", "m_flLastFireTime");
|
||||
this->flObservedCritChance = gNetvars.get_offset("DT_TFWeaponBase", "LocalActiveTFWeaponData", "m_flObservedCritChance");
|
||||
this->bDistributed = gNetvars.get_offset("DT_CurrencyPack", "m_bDistributed");
|
||||
|
112
src/crits.cpp
112
src/crits.cpp
@ -314,9 +314,29 @@ bool shouldCrit()
|
||||
// Melee mode with melee out and in range?
|
||||
if (shouldMeleeCrit())
|
||||
return true;
|
||||
static bool pressed_key_last_tick = false;
|
||||
static int loose_cannon_countdown = 0;
|
||||
// Crit key + enabled, for melee, the crit key MUST be set
|
||||
if (enabled && ((g_pLocalPlayer->weapon_mode != weapon_melee && !crit_key) || crit_key.isKeyDown()))
|
||||
{
|
||||
pressed_key_last_tick = true;
|
||||
return true;
|
||||
}
|
||||
// Special code for loose cannon since projectile is delayed
|
||||
if (!crit_key.isKeyDown() && (pressed_key_last_tick || loose_cannon_countdown))
|
||||
{
|
||||
if (pressed_key_last_tick)
|
||||
loose_cannon_countdown = 7;
|
||||
pressed_key_last_tick = false;
|
||||
static unsigned last_tickcount = 0;
|
||||
if (tickcount != last_tickcount)
|
||||
{
|
||||
last_tickcount = tickcount;
|
||||
loose_cannon_countdown--;
|
||||
}
|
||||
if (CE_GOOD(LOCAL_W) && LOCAL_W->m_iClassID() == CL_CLASS(CTFCannon))
|
||||
return true;
|
||||
}
|
||||
// Force crits on sticky launcher
|
||||
/*if (force_ticks)
|
||||
{
|
||||
@ -353,7 +373,7 @@ bool canWeaponCrit(bool draw = false)
|
||||
// Misc checks
|
||||
if (!isAllowedToWithdrawFromBucket(weapon))
|
||||
return false;
|
||||
if (!draw && !CanShoot() && !isRapidFire(weapon))
|
||||
if (!draw && !CanShoot() && !isRapidFire(weapon) && LOCAL_W->m_iClassID() != CL_CLASS(CTFCannon))
|
||||
return false;
|
||||
if (!draw && CE_INT(LOCAL_W, netvar.iItemDefinitionIndex) == 730 && !can_beggars_crit)
|
||||
return false;
|
||||
@ -689,6 +709,9 @@ void CreateMove()
|
||||
else
|
||||
return;
|
||||
}
|
||||
else if (LOCAL_W->m_iClassID() == CL_CLASS(CTFCannon))
|
||||
{
|
||||
}
|
||||
else if (!can_beggars_crit)
|
||||
return;
|
||||
}
|
||||
@ -1062,49 +1085,54 @@ void LevelShutdown()
|
||||
}
|
||||
|
||||
// Prints basically everything you need to know about crits
|
||||
static CatCommand debug_print_crit_info("debug_print_crit_info", "Print a bunch of useful crit info", []() {
|
||||
if (CE_BAD(LOCAL_E))
|
||||
return;
|
||||
static CatCommand debug_print_crit_info("debug_print_crit_info", "Print a bunch of useful crit info",
|
||||
[]()
|
||||
{
|
||||
if (CE_BAD(LOCAL_E))
|
||||
return;
|
||||
|
||||
logging::Info("Player specific information:");
|
||||
logging::Info("Ranged Damage this round: %d", cached_damage - round_damage);
|
||||
logging::Info("Melee Damage this round: %d", melee_damage);
|
||||
logging::Info("Crit Damage this round: %d", crit_damage);
|
||||
logging::Info("Observed crit chance: %f", getObservedCritChance());
|
||||
if (CE_GOOD(LOCAL_W))
|
||||
logging::Info("Player specific information:");
|
||||
logging::Info("Ranged Damage this round: %d", cached_damage - round_damage);
|
||||
logging::Info("Melee Damage this round: %d", melee_damage);
|
||||
logging::Info("Crit Damage this round: %d", crit_damage);
|
||||
logging::Info("Observed crit chance: %f", getObservedCritChance());
|
||||
if (CE_GOOD(LOCAL_W))
|
||||
{
|
||||
IClientEntity *wep = RAW_ENT(LOCAL_W);
|
||||
weapon_info info(wep);
|
||||
logging::Info("Weapon specific information:");
|
||||
logging::Info("Crit bucket: %f", info.crit_bucket);
|
||||
logging::Info("Needed Crit chance: %f", critMultInfo(wep).second);
|
||||
logging::Info("Added per shot: %f", added_per_shot);
|
||||
if (isRapidFire(wep))
|
||||
logging::Info("Subtracted per Rapidfire crit: %f", getWithdrawAmount(wep));
|
||||
else
|
||||
logging::Info("Subtracted per crit: %f", getWithdrawAmount(wep));
|
||||
logging::Info("Damage Until crit: %d", damageUntilToCrit(wep));
|
||||
logging::Info("Shots until crit: %d", shotsUntilCrit(wep));
|
||||
}
|
||||
});
|
||||
|
||||
static InitRoutine init(
|
||||
[]()
|
||||
{
|
||||
IClientEntity *wep = RAW_ENT(LOCAL_W);
|
||||
weapon_info info(wep);
|
||||
logging::Info("Weapon specific information:");
|
||||
logging::Info("Crit bucket: %f", info.crit_bucket);
|
||||
logging::Info("Needed Crit chance: %f", critMultInfo(wep).second);
|
||||
logging::Info("Added per shot: %f", added_per_shot);
|
||||
if (isRapidFire(wep))
|
||||
logging::Info("Subtracted per Rapidfire crit: %f", getWithdrawAmount(wep));
|
||||
else
|
||||
logging::Info("Subtracted per crit: %f", getWithdrawAmount(wep));
|
||||
logging::Info("Damage Until crit: %d", damageUntilToCrit(wep));
|
||||
logging::Info("Shots until crit: %d", shotsUntilCrit(wep));
|
||||
}
|
||||
});
|
||||
|
||||
static InitRoutine init([]() {
|
||||
EC::Register(EC::CreateMoveLate, CreateMove, "crit_cm");
|
||||
EC::Register(EC::CreateMoveLate, CreateMove, "crit_cm");
|
||||
#if ENABLE_VISUALS
|
||||
EC::Register(EC::Draw, Draw, "crit_draw");
|
||||
EC::Register(EC::Draw, Draw, "crit_draw");
|
||||
#endif
|
||||
EC::Register(EC::LevelShutdown, LevelShutdown, "crit_lvlshutdown");
|
||||
g_IGameEventManager->AddListener(&listener, false);
|
||||
HookNetvar({ "DT_TFWeaponBase", "LocalActiveTFWeaponData", "m_flObservedCritChance" }, observed_crit_chance_hook, observedcritchance_nethook);
|
||||
EC::Register(
|
||||
EC::Shutdown,
|
||||
[]() {
|
||||
g_IGameEventManager->RemoveListener(&listener);
|
||||
observed_crit_chance_hook.restore();
|
||||
},
|
||||
"crit_shutdown");
|
||||
// Attached in game, out of sync
|
||||
if (g_IEngine->IsInGame())
|
||||
is_out_of_sync = true;
|
||||
});
|
||||
EC::Register(EC::LevelShutdown, LevelShutdown, "crit_lvlshutdown");
|
||||
g_IGameEventManager->AddListener(&listener, false);
|
||||
HookNetvar({ "DT_TFWeaponBase", "LocalActiveTFWeaponData", "m_flObservedCritChance" }, observed_crit_chance_hook, observedcritchance_nethook);
|
||||
EC::Register(
|
||||
EC::Shutdown,
|
||||
[]()
|
||||
{
|
||||
g_IGameEventManager->RemoveListener(&listener);
|
||||
observed_crit_chance_hook.restore();
|
||||
},
|
||||
"crit_shutdown");
|
||||
// Attached in game, out of sync
|
||||
if (g_IEngine->IsInGame())
|
||||
is_out_of_sync = true;
|
||||
});
|
||||
} // namespace criticals
|
||||
|
@ -119,6 +119,11 @@ static void spectatorUpdate()
|
||||
};
|
||||
}
|
||||
|
||||
static bool playerTeamCheck(CachedEntity *entity)
|
||||
{
|
||||
return (int) teammates == 2 || (entity->m_bEnemy() && !teammates) || (!entity->m_bEnemy() && teammates) || (CE_GOOD(LOCAL_W) && LOCAL_W->m_iClassID() == CL_CLASS(CTFCrossbow) && entity->m_iHealth() < entity->m_iMaxHealth());
|
||||
}
|
||||
|
||||
#if ENABLE_VISUALS
|
||||
static settings::Boolean fov_draw{ "aimbot.fov-circle.enable", "0" };
|
||||
static settings::Float fovcircle_opacity{ "aimbot.fov-circle.opacity", "0.7" };
|
||||
@ -347,37 +352,56 @@ static void CreateMove()
|
||||
// flNextPrimaryAttack meme
|
||||
if (only_can_shoot && g_pLocalPlayer->weapon()->m_iClassID() != CL_CLASS(CTFMinigun) && g_pLocalPlayer->weapon()->m_iClassID() != CL_CLASS(CTFLaserPointer))
|
||||
{
|
||||
// Handle Compound bow
|
||||
// Handle Huntsman
|
||||
if (g_pLocalPlayer->weapon()->m_iClassID() == CL_CLASS(CTFCompoundBow))
|
||||
{
|
||||
bool release = false;
|
||||
if (autoshoot)
|
||||
current_user_cmd->buttons |= IN_ATTACK;
|
||||
// Grab time when charge began
|
||||
current_user_cmd->buttons |= IN_ATTACK;
|
||||
float begincharge = CE_FLOAT(g_pLocalPlayer->weapon(), netvar.flChargeBeginTime);
|
||||
float charge = g_GlobalVars->curtime - begincharge;
|
||||
if (!begincharge)
|
||||
charge = 0.0f;
|
||||
int damage = std::floor(50.0f + 70.0f * fminf(1.0f, charge));
|
||||
int charge_damage = std::floor(50.0f + 70.0f * fminf(1.0f, charge)) * 3.0f;
|
||||
if (!wait_for_charge || (damage >= target_entity->m_iHealth() || charge_damage >= target_entity->m_iHealth()))
|
||||
if (HasCondition<TFCond_Slowed>(LOCAL_E) && (autoshoot || !(current_user_cmd->buttons & IN_ATTACK)) && (!wait_for_charge || (charge >= 1.0f || damage >= target_entity->m_iHealth() || charge_damage >= target_entity->m_iHealth())))
|
||||
release = true;
|
||||
// Shoot projectile
|
||||
if (release)
|
||||
DoAutoshoot();
|
||||
static bool currently_charging_huntsman = false;
|
||||
|
||||
// Hunstman started charging
|
||||
if (CE_FLOAT(g_pLocalPlayer->weapon(), netvar.flChargeBeginTime) != 0)
|
||||
currently_charging_huntsman = true;
|
||||
|
||||
// Huntsman was released
|
||||
if (!(current_user_cmd->buttons & IN_ATTACK) && currently_charging_huntsman)
|
||||
{
|
||||
currently_charging_huntsman = false;
|
||||
DoAutoshoot();
|
||||
Aim(target_entity);
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
// Not release type weapon
|
||||
}
|
||||
// Loose cannon
|
||||
else if (LOCAL_W->m_iClassID() == CL_CLASS(CTFCannon))
|
||||
{
|
||||
// TODO: add logic for charge time
|
||||
bool release = false;
|
||||
if (autoshoot)
|
||||
current_user_cmd->buttons |= IN_ATTACK;
|
||||
float detonate_time = CE_FLOAT(LOCAL_W, netvar.flDetonateTime);
|
||||
// Currently charging up
|
||||
if (detonate_time > g_GlobalVars->curtime)
|
||||
{
|
||||
if (wait_for_charge)
|
||||
{
|
||||
// Shoot when a straight shot would result in only 100ms left on fuse upon target hit
|
||||
float best_charge = PredictEntity(target_entity, false).DistTo(g_pLocalPlayer->v_Eye) / cur_proj_speed + 0.1;
|
||||
if (detonate_time - g_GlobalVars->curtime <= best_charge)
|
||||
release = true;
|
||||
}
|
||||
else
|
||||
release = true;
|
||||
}
|
||||
if (release)
|
||||
{
|
||||
DoAutoshoot();
|
||||
Aim(target_entity);
|
||||
}
|
||||
}
|
||||
// Not release type weapon
|
||||
else if (LOCAL_W->m_iClassID() == CL_CLASS(CTFPipebombLauncher))
|
||||
{
|
||||
float chargebegin = CE_FLOAT(LOCAL_W, netvar.flChargeBeginTime);
|
||||
@ -655,6 +679,11 @@ CachedEntity *RetrieveBestTarget(bool aimkey_state)
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Crossbow logic
|
||||
if (!ent->m_bEnemy() && ent->m_Type() == ENTITY_PLAYER && CE_GOOD(LOCAL_W) && LOCAL_W->m_iClassID() == CL_CLASS(CTFCrossbow))
|
||||
{
|
||||
scr = ((ent->m_iMaxHealth() - ent->m_iHealth()) / ent->m_iMaxHealth()) * (*priority_mode == 2 ? 16384.0f : 2000.0f);
|
||||
}
|
||||
// Compare the top score to our current ents score
|
||||
if (scr > target_highest_score)
|
||||
{
|
||||
@ -689,7 +718,7 @@ bool IsTargetStateGood(CachedEntity *entity)
|
||||
if (!entity->m_bAlivePlayer())
|
||||
return false;
|
||||
// Teammates
|
||||
if ((int) teammates != 2 && ((!entity->m_bEnemy() && !teammates) || (entity->m_bEnemy() && teammates)))
|
||||
if (!playerTeamCheck(entity))
|
||||
return false;
|
||||
// Distance
|
||||
if (EffectiveTargetingRange())
|
||||
@ -1055,22 +1084,21 @@ void DoAutoshoot(CachedEntity *target_entity)
|
||||
return;
|
||||
if (IsPlayerDisguised(g_pLocalPlayer->entity) && !autoshoot_disguised)
|
||||
return;
|
||||
// Handle Compound bow
|
||||
if (g_pLocalPlayer->weapon()->m_iClassID() == CL_CLASS(CTFCompoundBow))
|
||||
// Handle Huntsman/Loose cannon
|
||||
if (g_pLocalPlayer->weapon()->m_iClassID() == CL_CLASS(CTFCompoundBow) || g_pLocalPlayer->weapon()->m_iClassID() == CL_CLASS(CTFCannon))
|
||||
{
|
||||
// Release hunstman if over huntsmans limit
|
||||
if (begancharge)
|
||||
if (!only_can_shoot)
|
||||
{
|
||||
current_user_cmd->buttons &= ~IN_ATTACK;
|
||||
hacks::shared::antiaim::SetSafeSpace(5);
|
||||
begancharge = false;
|
||||
}
|
||||
// Pull string if charge isnt enough
|
||||
else
|
||||
{
|
||||
current_user_cmd->buttons |= IN_ATTACK;
|
||||
begancharge = true;
|
||||
if (!begancharge)
|
||||
{
|
||||
current_user_cmd->buttons |= IN_ATTACK;
|
||||
begancharge = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
begancharge = false;
|
||||
current_user_cmd->buttons &= ~IN_ATTACK;
|
||||
hacks::shared::antiaim::SetSafeSpace(5);
|
||||
return;
|
||||
}
|
||||
else
|
||||
@ -1491,6 +1519,7 @@ bool UpdateAimkey()
|
||||
static bool aimkey_flip = false;
|
||||
static bool pressed_last_tick = false;
|
||||
bool allow_aimkey = true;
|
||||
static bool last_allow_aimkey = true;
|
||||
|
||||
// Check if aimkey is used
|
||||
if (aimkey && aimkey_mode)
|
||||
@ -1516,6 +1545,19 @@ bool UpdateAimkey()
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// Huntsman and Loose Cannon need special logic since we aim upon m1 being released
|
||||
if (!autoshoot && CE_GOOD(LOCAL_W) && (LOCAL_W->m_iClassID() == CL_CLASS(CTFCompoundBow) || LOCAL_W->m_iClassID() == CL_CLASS(CTFCannon)))
|
||||
{
|
||||
if (!allow_aimkey && last_allow_aimkey)
|
||||
{
|
||||
allow_aimkey = true;
|
||||
last_allow_aimkey = false;
|
||||
}
|
||||
else
|
||||
last_allow_aimkey = allow_aimkey;
|
||||
}
|
||||
else
|
||||
last_allow_aimkey = allow_aimkey;
|
||||
pressed_last_tick = key_down;
|
||||
}
|
||||
// Return whether the aimkey allows aiming
|
||||
@ -1608,15 +1650,17 @@ void rvarCallback(settings::VariableBase<float> &, float after)
|
||||
{
|
||||
force_backtrack_aimbot = after >= 200.0f;
|
||||
}
|
||||
static InitRoutine EC([]() {
|
||||
hacks::tf2::backtrack::latency.installChangeCallback(rvarCallback);
|
||||
EC::Register(EC::LevelInit, Reset, "INIT_Aimbot", EC::average);
|
||||
EC::Register(EC::LevelShutdown, Reset, "RESET_Aimbot", EC::average);
|
||||
EC::Register(EC::CreateMove, CreateMove, "CM_Aimbot", EC::late);
|
||||
EC::Register(EC::CreateMoveWarp, CreateMoveWarp, "CMW_Aimbot", EC::late);
|
||||
static InitRoutine EC(
|
||||
[]()
|
||||
{
|
||||
hacks::tf2::backtrack::latency.installChangeCallback(rvarCallback);
|
||||
EC::Register(EC::LevelInit, Reset, "INIT_Aimbot", EC::average);
|
||||
EC::Register(EC::LevelShutdown, Reset, "RESET_Aimbot", EC::average);
|
||||
EC::Register(EC::CreateMove, CreateMove, "CM_Aimbot", EC::late);
|
||||
EC::Register(EC::CreateMoveWarp, CreateMoveWarp, "CMW_Aimbot", EC::late);
|
||||
#if ENABLE_VISUALS
|
||||
EC::Register(EC::Draw, DrawText, "DRAW_Aimbot", EC::average);
|
||||
EC::Register(EC::Draw, DrawText, "DRAW_Aimbot", EC::average);
|
||||
#endif
|
||||
});
|
||||
});
|
||||
|
||||
} // namespace hacks::shared::aimbot
|
||||
|
@ -1283,7 +1283,9 @@ bool GetProjectileData(CachedEntity *weapon, float &speed, float &gravity, float
|
||||
}
|
||||
case CL_CLASS(CTFCannon):
|
||||
{
|
||||
rspeed = 1453.9f;
|
||||
rspeed = 1453.9f;
|
||||
rgrav = 1.0f;
|
||||
rinitial_vel = 200.0f;
|
||||
break;
|
||||
}
|
||||
case CL_CLASS(CTFGrenadeLauncher):
|
||||
@ -1314,8 +1316,21 @@ bool GetProjectileData(CachedEntity *weapon, float &speed, float &gravity, float
|
||||
case CL_CLASS(CTFCompoundBow):
|
||||
{
|
||||
float chargetime = g_GlobalVars->curtime - CE_FLOAT(weapon, netvar.flChargeBeginTime);
|
||||
rspeed = RemapValClamped(chargetime, 0.0f, 1.f, 1800, 2600);
|
||||
rgrav = RemapValClamped(chargetime, 0.0f, 1.f, 0.5, 0.1) - 0.05;
|
||||
if (CE_FLOAT(weapon, netvar.flChargeBeginTime) == 0)
|
||||
chargetime = 0;
|
||||
else
|
||||
{
|
||||
static const ConVar *pUpdateRate = g_pCVar->FindVar("cl_updaterate");
|
||||
if (!pUpdateRate)
|
||||
pUpdateRate = g_pCVar->FindVar("cl_updaterate");
|
||||
else
|
||||
{
|
||||
chargetime += TICKS_TO_TIME(1);
|
||||
// chargetime += ROUND_TO_TICKS(MAX(cl_interp->GetFloat(), cl_interp_ratio->GetFloat() / pUpdateRate->GetFloat()));
|
||||
}
|
||||
}
|
||||
rspeed = RemapValClamped(chargetime, 0.0f, 1.f, 1800, 2600);
|
||||
rgrav = RemapValClamped(chargetime, 0.0f, 1.f, 0.5, 0.1);
|
||||
break;
|
||||
}
|
||||
case CL_CLASS(CTFBat_Giftwrap):
|
||||
@ -1834,10 +1849,10 @@ Vector getShootPos(Vector angle)
|
||||
vecOffset = Vector(16.0f, 6.0f, -8.0f);
|
||||
break;
|
||||
|
||||
// Huntsman
|
||||
/*case CL_CLASS(CTFCompoundBow):
|
||||
vecOffset = Vector(23.5f, -8.0f, -3.0f);
|
||||
break;*/
|
||||
// Huntsman
|
||||
case CL_CLASS(CTFCompoundBow):
|
||||
vecOffset = Vector(23.5f, -8.0f, -3.0f);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
@ -5,6 +5,7 @@
|
||||
* Author: nullifiedcat
|
||||
*/
|
||||
#include "common.hpp"
|
||||
#include "navparser.hpp"
|
||||
#include <settings/Bool.hpp>
|
||||
#include <boost/circular_buffer.hpp>
|
||||
|
||||
@ -144,7 +145,7 @@ Vector PredictStep(Vector pos, Vector &vel, const Vector &acceleration, std::pai
|
||||
Vector result = pos;
|
||||
|
||||
// If we should do strafe prediction, then we still need to do the calculations, but instead of applying them we simply calculate the distance traveled and use that info together with strafe pred
|
||||
if (strafepred && (grounddistance ? *grounddistance > 0.1f : DistanceToGround(pos, minmax->first, minmax->second) > 0.1f))
|
||||
if (strafepred && (grounddistance ? *grounddistance > 0.1f : (minmax ? DistanceToGround(pos, minmax->first, minmax->second) : DistanceToGround(pos)) > 0.1f))
|
||||
{
|
||||
auto newpos = result + (acceleration / 2.0f) * pow(steplength, 2) + vel * steplength;
|
||||
// Strafe pred does not predict Z! The player can't control his Z anyway, so it is pointless.
|
||||
@ -153,9 +154,9 @@ Vector PredictStep(Vector pos, Vector &vel, const Vector &acceleration, std::pai
|
||||
}
|
||||
else
|
||||
result += (acceleration / 2.0f) * pow(steplength, 2) + vel * steplength;
|
||||
|
||||
vel += acceleration * steplength;
|
||||
|
||||
bool moved_upwards = false;
|
||||
if (vischeck)
|
||||
{
|
||||
Vector modify = result;
|
||||
@ -166,22 +167,110 @@ Vector PredictStep(Vector pos, Vector &vel, const Vector &acceleration, std::pai
|
||||
|
||||
{
|
||||
PROF_SECTION(PredictTraces)
|
||||
Ray_t ray;
|
||||
trace_t trace;
|
||||
if (minmax)
|
||||
ray.Init(modify, low, minmax->first, minmax->second);
|
||||
else
|
||||
ray.Init(modify, low);
|
||||
g_ITrace->TraceRay(ray, MASK_PLAYERSOLID, &trace::filter_no_player, &trace);
|
||||
|
||||
float dist = pos.z - trace.endpos.z;
|
||||
if (trace.m_pEnt && std::fabs(dist) < 63.0f)
|
||||
grounddistance = dist;
|
||||
// First, ensure we're not slightly below the floor, up to 18 HU will snap up
|
||||
trace_t upwards_trace;
|
||||
Ray_t ray;
|
||||
Vector endpos = result;
|
||||
endpos.z += 18;
|
||||
if (minmax)
|
||||
ray.Init(endpos, result, minmax->first, minmax->second);
|
||||
else
|
||||
ray.Init(endpos, result);
|
||||
g_ITrace->TraceRay(ray, MASK_PLAYERSOLID, &trace::filter_no_player, &upwards_trace);
|
||||
|
||||
// We hit something, snap to it
|
||||
if (upwards_trace.DidHit() && !upwards_trace.startsolid)
|
||||
{
|
||||
result = upwards_trace.endpos;
|
||||
grounddistance = 0.0f;
|
||||
moved_upwards = true;
|
||||
}
|
||||
|
||||
// Now check actual ground distance
|
||||
else
|
||||
{
|
||||
trace_t trace;
|
||||
if (minmax)
|
||||
ray.Init(modify, low, minmax->first, minmax->second);
|
||||
else
|
||||
ray.Init(modify, low);
|
||||
g_ITrace->TraceRay(ray, MASK_PLAYERSOLID, &trace::filter_no_player, &trace);
|
||||
|
||||
float dist = pos.z - trace.endpos.z;
|
||||
if (trace.m_pEnt && grounddistance > -18.0f)
|
||||
grounddistance = dist;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (grounddistance)
|
||||
if (result.z < pos.z - *grounddistance)
|
||||
result.z = pos.z - *grounddistance;
|
||||
|
||||
// Check if we hit a wall, if so, snap to it and distance ourselves a bit from it
|
||||
if (vischeck && !moved_upwards)
|
||||
{
|
||||
{
|
||||
PROF_SECTION(PredictTraces)
|
||||
Ray_t ray;
|
||||
trace_t trace;
|
||||
if (minmax)
|
||||
ray.Init(pos, result, minmax->first, minmax->second);
|
||||
else
|
||||
ray.Init(pos, result);
|
||||
g_ITrace->TraceRay(ray, MASK_PLAYERSOLID, &trace::filter_no_player, &trace);
|
||||
|
||||
// Hit a wall, scratch along it
|
||||
if (trace.DidHit())
|
||||
{
|
||||
Vector hitpos = trace.endpos;
|
||||
Vector normal_wall = trace.plane.normal;
|
||||
Vector normal_trace(hitpos.x - pos.x, hitpos.y - pos.y, 0.0f);
|
||||
normal_trace = normal_trace.Normalized();
|
||||
|
||||
// Angle of impact determines speed
|
||||
float impact_angle = acos(normal_trace.Dot(normal_wall));
|
||||
|
||||
// Ignore floor planes (They have no components we can use)
|
||||
if (!normal_wall.AsVector2D().IsZero(0.001f))
|
||||
{
|
||||
// We can get a plane in Normal form and determine the direction from there
|
||||
// aka n1*x+n2*y+n3*z=d
|
||||
float d = normal_wall.Dot(hitpos);
|
||||
|
||||
Vector point1;
|
||||
Vector point2;
|
||||
|
||||
// The above will be invalid due to a division by 0
|
||||
if (normal_wall.y == 0.0f)
|
||||
{
|
||||
point1 = Vector((d - normal_wall.y * vel.y - normal_wall.z * hitpos.z) / normal_wall.x, vel.y, hitpos.z);
|
||||
point2 = Vector((d - normal_wall.y * 2.0f * vel.y - normal_wall.z * hitpos.z) / normal_wall.x, vel.y * 2.0f, hitpos.z);
|
||||
}
|
||||
else
|
||||
{
|
||||
point1 = Vector(vel.x, (d - normal_wall.x * vel.x - normal_wall.z * hitpos.z) / normal_wall.y, hitpos.z);
|
||||
point2 = Vector(vel.x * 2.0f, (d - normal_wall.x * vel.x * 2.0f - normal_wall.z * hitpos.z) / normal_wall.y, hitpos.z);
|
||||
}
|
||||
|
||||
hitpos += normal_wall * vel * steplength;
|
||||
result = hitpos;
|
||||
// Adjust velocity depending on angle
|
||||
float speed = vel.Length2D() * (PI - impact_angle);
|
||||
|
||||
// Adjust new velocity
|
||||
Vector2D new_vel = (point2.AsVector2D() - point1.AsVector2D());
|
||||
// Ensure we have no 0 length
|
||||
if (new_vel.Length())
|
||||
{
|
||||
new_vel /= new_vel.Length();
|
||||
vel.x = new_vel.x * speed;
|
||||
vel.y = new_vel.y * speed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -479,6 +568,7 @@ std::pair<Vector, Vector> ProjectilePrediction(CachedEntity *ent, int hb, float
|
||||
|
||||
auto strafe_pred = initializeStrafePrediction(ent);
|
||||
|
||||
float currenttime_before = currenttime;
|
||||
for (int steps = 0; steps < maxsteps; steps++, currenttime += steplength)
|
||||
{
|
||||
current = last = PredictStep(last, velocity, acceleration, &minmax, steplength, strafe_pred ? &*strafe_pred : nullptr);
|
||||
@ -505,7 +595,7 @@ std::pair<Vector, Vector> ProjectilePrediction(CachedEntity *ent, int hb, float
|
||||
// S = at^2/2 ; t = sqrt(2S/a)*/
|
||||
Vector result = bestpos + hitbox_offset;
|
||||
Vector initialvel_result = result;
|
||||
initialvel_result.z -= proj_startvelocity * besttime;
|
||||
initialvel_result.z -= proj_startvelocity * (besttime - currenttime_before);
|
||||
/*logging::Info("[Pred][%d] delta: %.2f %.2f %.2f", result.x - origin.x,
|
||||
result.y - origin.y, result.z - origin.z);*/
|
||||
return { result, initialvel_result };
|
||||
@ -521,10 +611,20 @@ float DistanceToGround(CachedEntity *ent)
|
||||
|
||||
float DistanceToGround(Vector origin, Vector mins, Vector maxs)
|
||||
{
|
||||
trace_t ground_trace;
|
||||
// First, ensure we're not slightly below the floor, up to 18 HU will snap up
|
||||
trace_t upwards_trace;
|
||||
Ray_t ray;
|
||||
Vector endpos = origin;
|
||||
endpos.z -= 8192;
|
||||
endpos.z += 18;
|
||||
ray.Init(endpos, origin, mins, maxs);
|
||||
g_ITrace->TraceRay(ray, MASK_PLAYERSOLID, &trace::filter_no_player, &upwards_trace);
|
||||
|
||||
// We hit something, snap to it
|
||||
if (upwards_trace.DidHit() && !upwards_trace.startsolid)
|
||||
return -std::fabs(origin.z - upwards_trace.endpos.z);
|
||||
|
||||
trace_t ground_trace;
|
||||
endpos.z = origin.z - 8192;
|
||||
ray.Init(origin, endpos, mins, maxs);
|
||||
g_ITrace->TraceRay(ray, MASK_PLAYERSOLID, &trace::filter_no_player, &ground_trace);
|
||||
return std::fabs(origin.z - ground_trace.endpos.z);
|
||||
@ -532,10 +632,20 @@ float DistanceToGround(Vector origin, Vector mins, Vector maxs)
|
||||
|
||||
float DistanceToGround(Vector origin)
|
||||
{
|
||||
trace_t ground_trace;
|
||||
// First, ensure we're not slightly below the floor, up to 18 HU will snap up
|
||||
trace_t upwards_trace;
|
||||
Ray_t ray;
|
||||
Vector endpos = origin;
|
||||
endpos.z -= 8192;
|
||||
endpos.z += 18;
|
||||
ray.Init(endpos, origin);
|
||||
g_ITrace->TraceRay(ray, MASK_PLAYERSOLID, &trace::filter_no_player, &upwards_trace);
|
||||
|
||||
// We hit something, snap to it
|
||||
if (upwards_trace.DidHit() && !upwards_trace.startsolid)
|
||||
return -std::fabs(origin.z - upwards_trace.endpos.z);
|
||||
|
||||
trace_t ground_trace;
|
||||
endpos.z = origin.z - 8192;
|
||||
ray.Init(origin, endpos);
|
||||
g_ITrace->TraceRay(ray, MASK_PLAYERSOLID, &trace::filter_no_player, &ground_trace);
|
||||
return std::fabs(origin.z - ground_trace.endpos.z);
|
||||
|
Reference in New Issue
Block a user