Auto-Reflect Improvements
This commit is contained in:
parent
b022a74519
commit
19b6ff3cfd
@ -840,6 +840,9 @@ float EffectiveTargetingRange() {
|
||||
// Melees use a close range, TODO add dynamic range for demoknight swords
|
||||
if (GetWeaponMode() == weapon_melee) {
|
||||
return 100.0f;
|
||||
// Pyros only have so much untill their flames hit
|
||||
} else if ( g_pLocalPlayer->weapon()->m_iClassID == CL_CLASS(CTFFlameThrower) ) {
|
||||
return 185.0f;
|
||||
}
|
||||
// Else return user settings
|
||||
return (float)max_range;
|
||||
|
@ -12,56 +12,132 @@
|
||||
|
||||
namespace hacks { namespace tf { namespace autoreflect {
|
||||
|
||||
CatVar enabled(CV_SWITCH, "reflect_enabled", "0", "AutoReflect", "Master AutoReflect switch");
|
||||
// Vars for user settings
|
||||
CatVar enabled(CV_SWITCH, "reflect_enabled", "0", "Auto Reflect", "Master AutoReflect switch");
|
||||
CatVar idle_only(CV_SWITCH, "reflect_only_idle", "0", "Only when not shooting", "Don't AutoReflect if you're holding M1");
|
||||
CatVar legit(CV_SWITCH, "reflect_legit", "0", "Legit Reflect", "Only Auto-airblasts projectiles that you can see, doesnt move your crosshair");
|
||||
CatVar dodgeball(CV_SWITCH, "reflect_dodgeball", "0", "Dodgeball Mode", "Allows auto-reflect to work in dodgeball servers");
|
||||
|
||||
CatVar stickies(CV_SWITCH, "reflect_stickybombs", "0", "Reflect stickies", "Reflect Stickybombs");
|
||||
CatVar max_distance(CV_INT, "reflect_distance", "200", "Distance", "Maximum distance to reflect at", true, 300.0f);
|
||||
// TODO setup proj sorting
|
||||
// TODO CatVar bigProj(CV_SWITCH, "reflect_big_projectile", "0", "Reflect big projectiles", "Reflect Rockets");
|
||||
// TODO CatVar smallProj(CV_SWITCH, "reflect_small_projectile", "0", "Reflect small projectiles", "Reflect Huntsman arrows, Crusaders bolts");
|
||||
// TODO CatVar miscProj(CV_SWITCH, "reflect_misc_projectile", "0", "Reflect other", "Reflect jarate, milk");
|
||||
|
||||
|
||||
// Function called by game for movement
|
||||
void CreateMove() {
|
||||
// Check if user settings allow Auto Reflect
|
||||
if (!enabled) return;
|
||||
|
||||
// Check if player is using a flame thrower
|
||||
if (g_pLocalPlayer->weapon()->m_iClassID != CL_CLASS(CTFFlameThrower)) return;
|
||||
// If user settings allow, return if local player is in attack
|
||||
if (idle_only && (g_pUserCmd->buttons & IN_ATTACK)) return;
|
||||
|
||||
CachedEntity* closest = 0;
|
||||
// Create some book-keeping vars
|
||||
float closest_dist = 0.0f;
|
||||
Vector closest_vec;
|
||||
// Loop to find the closest entity
|
||||
for (int i = 0; i < HIGHEST_ENTITY; i++) {
|
||||
|
||||
// Find an ent from the for loops current tick
|
||||
CachedEntity* ent = ENTITY(i);
|
||||
|
||||
// Check if null or dormant
|
||||
if (CE_BAD(ent)) continue;
|
||||
|
||||
// Check if ent should be reflected
|
||||
if (!ShouldReflect(ent)) continue;
|
||||
//if (ent->Var<Vector>(eoffsets.vVelocity).IsZero(1.0f)) continue;
|
||||
float dist = ent->m_vecOrigin.DistToSqr(g_pLocalPlayer->v_Origin);
|
||||
if (dist < closest_dist || !closest) {
|
||||
closest = ent;
|
||||
|
||||
// Grab latency
|
||||
float latency = g_IEngine->GetNetChannelInfo()->GetLatency(FLOW_INCOMING) + g_IEngine->GetNetChannelInfo()->GetLatency(FLOW_OUTGOING);
|
||||
// Create a vector variable to store our velocity
|
||||
Vector velocity;
|
||||
// Grab Velocity of projectile
|
||||
velocity::EstimateAbsVelocity(RAW_ENT(ent), velocity);
|
||||
// Predict a vector for where the projectile will be
|
||||
Vector predictedProj = ent->m_vecOrigin + (velocity * latency);;
|
||||
|
||||
// Dont vischeck if ent is stickybomb or if dodgeball mode is enabled
|
||||
if (!IsEntStickyBomb(ent) && !dodgeball) {
|
||||
// Vis check the predicted vector
|
||||
if (!IsVectorVisible(g_pLocalPlayer->v_Origin, predictedProj)) continue;
|
||||
} /*else {
|
||||
// Stickys are weird, we use a different way to vis check them
|
||||
// Vis checking stickys are wonky, I quit, just ignore the check >_>
|
||||
//if (!VisCheckEntFromEnt(ent, LOCAL_E)) continue;
|
||||
}*/
|
||||
|
||||
// Calculate distance
|
||||
float dist = predictedProj.DistToSqr(g_pLocalPlayer->v_Origin);
|
||||
|
||||
// If legit mode is on, we check to see if reflecting will work if we dont aim at the projectile
|
||||
if (legit) {
|
||||
if ( GetFov(g_pLocalPlayer->v_OrigViewangles, g_pLocalPlayer->v_Eye, predictedProj) > 85.0f ) continue;
|
||||
}
|
||||
|
||||
// Compare our info to the others and determine if its the best, if we dont have a projectile already, then we save it here
|
||||
if (dist < closest_dist || closest_dist == 0.0f) {
|
||||
closest_dist = dist;
|
||||
closest_vec = predictedProj;
|
||||
}
|
||||
}
|
||||
if (CE_BAD(closest)) return;
|
||||
if (closest_dist == 0 || closest_dist > SQR((int)max_distance)) return;
|
||||
|
||||
Vector tr = (closest->m_vecOrigin - g_pLocalPlayer->v_Eye);
|
||||
Vector angles;
|
||||
VectorAngles(tr, angles);
|
||||
fClampAngle(angles);
|
||||
g_pUserCmd->viewangles = angles;
|
||||
g_pLocalPlayer->bUseSilentAngles = true;
|
||||
// Determine whether the closest projectile is whithin our parameters, preferably 185 units should be our limit, sqr is around the number below
|
||||
if (closest_dist == 0 || closest_dist > 34650) return;
|
||||
|
||||
// We dont want to aim if legit is true
|
||||
if (!legit) {
|
||||
// Aim at predicted projectile
|
||||
AimAt(g_pLocalPlayer->v_Eye, closest_vec, g_pUserCmd);
|
||||
// Use silent angles
|
||||
g_pLocalPlayer->bUseSilentAngles = true;
|
||||
}
|
||||
|
||||
// Airblast
|
||||
g_pUserCmd->buttons |= IN_ATTACK2;
|
||||
|
||||
// Function is finished, return
|
||||
return;
|
||||
}
|
||||
|
||||
// Function to determine whether an ent is good to reflect
|
||||
bool ShouldReflect(CachedEntity* ent) {
|
||||
if (CE_BAD(ent)) return false;
|
||||
// Check if the entity is a projectile
|
||||
if (ent->m_Type != ENTITY_PROJECTILE) return false;
|
||||
if (CE_INT(ent, netvar.iTeamNum) == g_pLocalPlayer->team) return false;
|
||||
// If projectile is already deflected, don't deflect it again.
|
||||
if (CE_INT(ent, (ent->m_bGrenadeProjectile ?
|
||||
/* NetVar for grenades */ netvar.Grenade_iDeflected :
|
||||
/* For rockets */ netvar.Rocket_iDeflected))) return false;
|
||||
logging::Info("Is projectile");
|
||||
|
||||
// We dont want to do these checks in dodgeball, it breakes if we do
|
||||
if (!dodgeball) {
|
||||
// Check if the projectile is your own teams
|
||||
if (!ent->m_bEnemy) return false;
|
||||
logging::Info("isnt team");
|
||||
|
||||
// If projectile is already deflected, don't deflect it again.
|
||||
if (CE_INT(ent, (ent->m_bGrenadeProjectile ?
|
||||
/* NetVar for grenades */ netvar.Grenade_iDeflected :
|
||||
/* For rockets */ netvar.Rocket_iDeflected))) return false;
|
||||
logging::Info("pass already reflect");
|
||||
}
|
||||
|
||||
// Check if the projectile is a sticky bomb and if the user settings allow it to be reflected
|
||||
if (IsEntStickyBomb(ent) && !stickies) return false;
|
||||
|
||||
// Target passed the test, return true
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsEntStickyBomb(CachedEntity* ent) {
|
||||
// Check if the projectile is a sticky bomb
|
||||
if (ent->m_iClassID == CL_CLASS(CTFGrenadePipebombProjectile)) {
|
||||
if (CE_INT(ent, netvar.iPipeType) == 1) {
|
||||
if (!stickies) return false;
|
||||
// Ent passed and should be reflected
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
// Ent didnt pass the test so return false
|
||||
return false;
|
||||
}
|
||||
|
||||
}}}
|
||||
|
@ -19,6 +19,7 @@ extern CatVar max_distance;
|
||||
|
||||
void CreateMove();
|
||||
bool ShouldReflect(CachedEntity* ent);
|
||||
bool IsEntStickyBomb(CachedEntity* ent);
|
||||
|
||||
}}}
|
||||
|
||||
|
114
src/helpers.cpp
114
src/helpers.cpp
@ -316,6 +316,7 @@ bool IsEntityVectorVisible(CachedEntity* entity, Vector endpos) {
|
||||
return (trace_object.fraction >= 0.99f || (((IClientEntity*)trace_object.m_pEnt)) == RAW_ENT(entity));
|
||||
}
|
||||
|
||||
// For when you need to vis check something that isnt the local player
|
||||
bool VisCheckEntFromEnt(CachedEntity* startEnt, CachedEntity* endEnt) {
|
||||
// We setSelf as the starting ent as we dont want to hit it, we want the other ent
|
||||
trace_t trace;
|
||||
@ -336,6 +337,27 @@ bool VisCheckEntFromEnt(CachedEntity* startEnt, CachedEntity* endEnt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use when you need to vis check something but its not the ent origin that you use, so we check from the vector to the ent, ignoring the first just in case
|
||||
bool VisCheckEntFromEntVector(Vector startVector, CachedEntity* startEnt, CachedEntity* endEnt) {
|
||||
// We setSelf as the starting ent as we dont want to hit it, we want the other ent
|
||||
trace_t trace;
|
||||
trace::filter_default.SetSelf(RAW_ENT(startEnt));
|
||||
|
||||
// Setup the trace starting with the origin of the starting ent attemting to hit the origin of the end ent
|
||||
Ray_t ray;
|
||||
ray.Init(startVector, endEnt->m_vecOrigin);
|
||||
{
|
||||
PROF_SECTION(IEVV_TraceRay);
|
||||
g_ITrace->TraceRay(ray, MASK_SHOT_HULL, &trace::filter_default, &trace);
|
||||
}
|
||||
// Is the entity that we hit our target ent? if so, the vis check passes
|
||||
if (trace.m_pEnt) {
|
||||
if ((((IClientEntity*)trace.m_pEnt)) == RAW_ENT(endEnt)) return true;
|
||||
}
|
||||
// Since we didnt hit our target ent, the vis check failed so return false
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector GetBuildingPosition(CachedEntity* ent) {
|
||||
Vector res;
|
||||
res = ent->m_vecOrigin;
|
||||
@ -688,98 +710,6 @@ bool IsEntityVisiblePenetration(CachedEntity* entity, int hb) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
class CMoveData;
|
||||
|
||||
/*void RunEnginePrediction(IClientEntity* ent, CUserCmd *ucmd) {
|
||||
// we are going to require some helper functions for this to work
|
||||
// notably SetupMove, FinishMove and ProcessMovement
|
||||
|
||||
|
||||
// setup the types of the functions
|
||||
typedef void(*SetupMoveFn)(IClientEntity *, CUserCmd *, class IMoveHelper *, CMoveData *);
|
||||
typedef void(*FinishMoveFn)(IClientEntity *, CUserCmd*, CMoveData*);
|
||||
typedef void(*ProcessMovementFn)(IClientEntity *, CMoveData *);
|
||||
typedef void(*StartTrackPredictionErrorsFn)(IClientEntity *);
|
||||
typedef void(*FinishTrackPredictionErrorsFn)(IClientEntity *);
|
||||
|
||||
// get the vtable
|
||||
void **predictionVtable = *(void ***)prediction;
|
||||
logging::Info("predictionVtable 0x%08x", predictionVtable);
|
||||
// get the functions
|
||||
SetupMoveFn oSetupMove = (SetupMoveFn) predictionVtable[19];
|
||||
FinishMoveFn oFinishMove = (FinishMoveFn) predictionVtable[20];
|
||||
|
||||
// get the vtable
|
||||
void **gameMovementVtable = *(void ***)gamemovement;
|
||||
logging::Info("gameMovementVtable 0x%08x", gameMovementVtable);
|
||||
// get the functions
|
||||
ProcessMovementFn oProcessMovement = (ProcessMovementFn) gameMovementVtable[2];
|
||||
StartTrackPredictionErrorsFn oStartTrackPredictionErrors = (StartTrackPredictionErrorsFn) gameMovementVtable[3];
|
||||
FinishTrackPredictionErrorsFn oFinishTrackPredictionErrors = (FinishTrackPredictionErrorsFn) gameMovementVtable[4];
|
||||
|
||||
// use this as movedata (should be big enough - otherwise the stack will die!)
|
||||
unsigned char moveData[2048];
|
||||
CMoveData *pMoveData = (CMoveData *)&(moveData[0]);
|
||||
logging::Info("pMoveData 0x%08x", pMoveData);
|
||||
|
||||
// back up globals
|
||||
float frameTime = gvars->frametime;
|
||||
float curTime = gvars->curtime;
|
||||
|
||||
CUserCmd defaultCmd;
|
||||
if(ucmd == NULL)
|
||||
{
|
||||
ucmd = &defaultCmd;
|
||||
}
|
||||
|
||||
// set the current command
|
||||
NET_VAR(ent, 0x105C, void*) = ucmd;
|
||||
|
||||
// set up the globals
|
||||
gvars->curtime = gvars->interval_per_tick * NET_INT(ent, netvar.nTickBase);
|
||||
gvars->frametime = gvars->interval_per_tick;
|
||||
|
||||
oStartTrackPredictionErrors(ent);
|
||||
|
||||
logging::Info("StartTrackPredictionErrors(ent)");
|
||||
oSetupMove(ent, ucmd, NULL, pMoveData);
|
||||
logging::Info("oSetupMove");
|
||||
oProcessMovement(ent, pMoveData);
|
||||
logging::Info("oProcessMovement");
|
||||
oFinishMove(ent, ucmd, pMoveData);
|
||||
logging::Info("oFinishMove");
|
||||
|
||||
oFinishTrackPredictionErrors(ent);
|
||||
logging::Info("oFinishTrackPredictionErrors");
|
||||
// reset the current command
|
||||
NET_VAR(ent, 0x105C, void *) = 0;
|
||||
|
||||
// restore globals
|
||||
gvars->frametime = frameTime;
|
||||
gvars->curtime = curTime;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
float oldCurtime;
|
||||
float oldFrametime;
|
||||
|
||||
void StartPrediction(CUserCmd* cmd) {
|
||||
oldCurtime = gvars->curtime;
|
||||
oldFrametime = gvars->frametime;
|
||||
gvars->curtime = NET_INT(g_pLocalPlayer->entity, netvar.nTickBase) * gvars->interval_per_tick;
|
||||
gvars->frametime = gvars->interval_per_tick;
|
||||
//gamemovement->
|
||||
}
|
||||
|
||||
void EndPrediction() {
|
||||
gvars->curtime = oldCurtime;
|
||||
gvars->frametime = oldFrametime;
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
void PrintChat(const char* fmt, ...) {
|
||||
CHudBaseChat* chat = (CHudBaseChat*)g_CHUD->FindElement("CHudChat");
|
||||
if (chat) {
|
||||
|
@ -74,7 +74,8 @@ void VectorAngles(Vector &forward, Vector &angles);
|
||||
bool IsEntityVisible(CachedEntity* entity, int hb);
|
||||
bool IsEntityVectorVisible(CachedEntity* entity, Vector endpos);
|
||||
bool VisCheckEntFromEnt(CachedEntity* startEnt, CachedEntity* endEnt);
|
||||
|
||||
bool VisCheckEntFromEntVector(Vector startVector, CachedEntity* startEnt, CachedEntity* endEnt);
|
||||
|
||||
bool LineIntersectsBox(Vector& bmin, Vector& bmax, Vector& lmin, Vector& lmax);
|
||||
|
||||
float DistToSqr(CachedEntity* entity);
|
||||
|
@ -100,9 +100,8 @@
|
||||
"name": "Auto Sticky",
|
||||
"list": [
|
||||
"sticky_enabled",
|
||||
"sticky_distance",
|
||||
"sticky_buildings",
|
||||
"sticky_visable"
|
||||
"sticky_legit"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
Reference in New Issue
Block a user