Credits to fysion for help on this Code: #include "CAimbot.h" #include "SDK.h" #include "CEntity.h" #include "trace.h" #include "Util.h" #include "CDrawManager.h" #include "CAimHelper.h" #include "projectile.h" #include "F1_Cache.h" #include "CPlayerManager.h" // simple define for sniper zoom #define ZOOM_BASE_DAMAGE 150.0f #define SNIPERRIFLE_BASE_DAMANGE 50.0f const char *CAimbot::name() const { return "AIMBOT"; } void CAimbot::processCommand( CUserCmd *pUserCmd ) { _TRY { if( enabled->getValue() ) { static int lastTick = 0; //if( lastTick != gInts.Globals->tickcount ) //{ // lastTick = gInts.Globals->tickcount; // // clear the hitbox cache // //hitboxCache.clear(); //} oldAngles = pUserCmd->viewangles; // this does the silent movement fix aim( pUserCmd ); return; } return; } _CATCH { targetSystem = switchTargetSystem( targetSystem, tSystem->getValue() ); } } bool CAimbot::paint() { if( !enabled->getValue() ) return false; // draw the current fov if( tSystem->getValue() == targetHelpers::fov ) { // TODO its either 16/9 or 9/16 float actualFov = 2 * atan( ( 16 / 9 ) * ( 3 / 4 ) * tan( DEG2RAD( static_cast< float >( CEntity<>{me}.get<int>( gEntVars.iFov ) ) / 2 ) ) ); float radius = tan( fovLimit->getValue() / 2 ) / RAD2DEG( actualFov ) / 2; gDrawManager.DrawCircle( gScreenSize.iScreenWidth / 2, gScreenSize.iScreenHeight / 2, radius, 10, COLORCODE( 255, 255, 255, 255 ) ); } if( !projectilePrediction->getValue() ) return false; //int y = 500; //gDrawManager.DrawString( "hud", 0, y, COLOR_OBJ, "time: %f", time ); //y += gDrawManager.GetHudHeight(); //gDrawManager.DrawString( "hud", 0, y, COLOR_OBJ, "asymetric time: %f", asymTime ); //y += gDrawManager.GetHudHeight(); //gDrawManager.DrawString( "hud", 0, y, COLOR_OBJ, "speed: %f", speed ); //y += gDrawManager.GetHudHeight(); //gDrawManager.DrawString( "hud", 0, y, COLOR_OBJ, "dist: %f", distance ); //y += gDrawManager.GetHudHeight(); //projectileHelper::PredictPath( targetSystem->getLastBestTarget().ent ); Vector vTargetScreen; //if(gDrawManager.WorldToScreen(predPos, vTargetScreen) ) //{ // gDrawManager.DrawRect( vTargetScreen.x, vTargetScreen.y, 16, 16, gDrawManager.dwGetTeamColor( 2 ) ); //} return true; } // update the entitys stats in the entity loop bool CAimbot::processEntity( int index ) { CEntity<> ent{ index }; // if its not a player //if(!isPlayer(ent)) //{ // // TODO add world check here // // we need to make sure we dont aim at entities that are not players // // so remove it from the map // //stats.erase(index); // targetSystem.removeTarget(index); // return false; //} // if its not valid then its not valid if( !isValidTarget( ent ) ) { targetSystem->removeTarget( index ); return false; } Vector v = /*getHitBoxVector( ent, findBestHitbox( ent ) );*/ ent->GetAbsOrigin(); //if( !visible( ent ) ) //{ // targetSystem->removeTarget( index ); // return false; //} targetSystem->addTarget( { index, v } ); //if(stats[index] == 8192.0f) //{ // stats.erase(index); // return false; //} if( !isValidBuilding( ent ) ) { if( !checkCond( ent ) ) { targetSystem->removeTarget( index ); return false; } if( ignoreFriends->getValue() ) { // we dont want to hurt friends if( gPlayerManager.getModeForPlayer(index) == CPlayerManager::playerMode::Friend ) { targetSystem->removeTarget( index ); return false; } } } //projectileHelper::PredictPath(index); return true; } void CAimbot::menuUpdate( F1_IConVar ** menuArray, int & currIndex ) { menuArray[ currIndex++ ] = aimbotSwitch; if(aimbotSwitch->getValue() ) { menuArray[ currIndex++ ] = enabled; menuArray[ currIndex++ ] = aimMelee; menuArray[ currIndex++ ] = autoHitbox; menuArray[ currIndex++ ] = hitbox; menuArray[ currIndex++ ] = zooomOnly; menuArray[ currIndex++ ] = waitForCharge; menuArray[ currIndex++ ] = ignoreFriends; menuArray[ currIndex++ ] = ignoreCond; menuArray[ currIndex++ ] = latencyPrediction; menuArray[ currIndex++ ] = autoShoot; menuArray[ currIndex++ ] = aimOnClick; menuArray[ currIndex++ ] = projectilePrediction; if( projectilePrediction->getValue() == true ) menuArray[ currIndex++ ] = projectileQuality; menuArray[ currIndex++ ] = predMode; menuArray[ currIndex++ ] = tSystem; menuArray[ currIndex++ ] = fovLimit; menuArray[ currIndex++ ] = useSilent; } } //float CAimbot::getDistance(CEntity<> &ent) //{ // // if(ent.isNull()) // return 8192.0f; // // return getDistanceToVector(ent->GetAbsOrigin()); //} // inline float CAimbot::getDistanceToVector( Vector v ) { CBaseEntity *pLocal = gInts.EntList->GetClientEntity( me ); if( !pLocal ) return 8192.0f; return pLocal->GetAbsOrigin().DistToSqr( v ); } bool CAimbot::visible( CEntity<> &ent ) { trace_t trace; Ray_t ray; CBaseFilter filter; CEntity<> local{ me }; filter.SetIgnoreEntity( local.castToPointer<CBaseEntity>() ); // the get hitbox already attempts to predict it ( if that is enabled ) Vector hit = getHitBoxVector( ent, findBestHitbox( ent ) ); // use the predicted origin the view offset is insignificant ray.Init( gLocalPlayerVars.pred.origin + local.get<Vector>( gEntVars.vecViewOffset ), hit ); gInts.EngineTrace->TraceRay( ray, MASK_AIMBOT | CONTENTS_HITBOX, &filter, &trace ); if( trace.m_pEnt ) { return ( trace.m_pEnt == ent.castToPointer<CBaseEntity>() && trace.hitGroup != 0 ); } return true; } bool CAimbot::visible( CEntity<> &ent, int hitbox ) { trace_t trace; Ray_t ray; CBaseFilter filter; CEntity<> local{ me }; filter.SetIgnoreEntity( local.castToPointer<CBaseEntity>() ); // the get hitbox already attempts to predict it ( if that is enabled ) Vector hit = getHitBoxVector( ent, hitbox ); // use the predicted origin the view offset is insignificant ray.Init( gLocalPlayerVars.pred.origin + local.get<Vector>( gEntVars.vecViewOffset ), hit ); gInts.EngineTrace->TraceRay( ray, MASK_AIMBOT | CONTENTS_HITBOX, &filter, &trace ); if( trace.m_pEnt ) { return ( trace.m_pEnt == ent.castToPointer<CBaseEntity>() && trace.hitGroup != 0 ); } return true; } bool CAimbot::visible( CEntity<> &ent, Vector v ) { trace_t trace; Ray_t ray; CBaseFilter filter; CEntity<> local{ me }; filter.SetIgnoreEntity( local.castToPointer<CBaseEntity>() ); // use the predicted origin the view offset is insignificant ray.Init( gLocalPlayerVars.pred.origin + local.get<Vector>( gEntVars.vecViewOffset ), v ); gInts.EngineTrace->TraceRay( ray, MASK_AIMBOT | CONTENTS_HITBOX, &filter, &trace ); if( trace.m_pEnt ) { return ( trace.m_pEnt == ent.castToPointer<CBaseEntity>() && trace.hitGroup != 0 ); } return true; } void CAimbot::aim( CUserCmd *pUserCmd ) { Vector angles; if( targetSystem->getType() != tSystem->getValue() ) { targetSystem = switchTargetSystem( targetSystem, tSystem->getValue() ); } CTarget target = targetSystem->getBestTarget(); if( target.ent == -1) { valid = false; return; } switch( tSystem->getValue() ) { case targetHelpers::distance: if( targetSystem->getBestStat() == FLT_MAX ) return; break; case targetHelpers::fov: if( targetSystem->getBestStat() > fovLimit->getValue() ) return; break; default: break; } CEntity<> local{ me }; if( !aimMelee->getValue() ) { // if we have a melee weapon dont aim if( gLocalPlayerVars.wepTag.isMelee() || gLocalPlayerVars.wepTag.isPda() ) return; } CEntity<> other{ target.ent }; CEntity<> localWeapon{ HANDLE2INDEX( local.get<int>( gEntVars.hActiveWeapon ) ) }; // if we manually attack, ALWAYS aim and attack if( !( pUserCmd->buttons & IN_ATTACK ) ) { if( aimOnClick->getValue() ) return; if( gLocalPlayerVars.Class == tf_classes::TF2_Sniper ) { if( gLocalPlayerVars.cond & tf_cond::TFCond_Zoomed ) { // if we cant get them with the no charge if( other.get<int>( gEntVars.iHealth ) > SNIPERRIFLE_BASE_DAMANGE ) { float damage = localWeapon.get<float>( gEntVars.flChargedDamage ); // minium charge to be able to headshot if( damage < 15.0f ) { return; } // respect zoom damage if( waitForCharge->getValue() && ZOOM_BASE_DAMAGE + damage < other.get<int>( gEntVars.iHealth ) ) { return; } } // otherwise we can just shoot and kill } else { if( zooomOnly->getValue() && gLocalPlayerVars.wepTag.isPrimary() ) { return; } } } else if( gLocalPlayerVars.Class == tf_classes::TF2_Spy ) { // just assume we are using the ambassador if( gLocalPlayerVars.activeWeapon == classId::CTFRevolver ) { CTFBaseWeaponGun *tfWeap = localWeapon.castToPointer<CTFBaseWeaponGun>(); float spread; try { // the one tick behind will hurt here, so its in try catch to protect spread = tfWeap->WeaponGetSpread(); } catch( ... ) { spread = 0.0f; } // Log::Console("%f", spread); if( spread > 0 ) return; } else { // if we cant fire, dont aim (bullettime is a little backwards) if( bulletTime( local, true ) ) return; } } else { // if we cant fire, dont aim (bullettime is a little backwards) //if(!bCanShoot(local, pUserCmd)) // return; } } // we need to call run simulation before even getting the hitbox // TODO find a better way of doing this (maybe use some form of runCommand) if( predMode->getValue() == predictionMode::Pred_RunSim ) { CUtilMove::safeRunSimulation( gInts.Prediction, other.castToPointer<CBaseEntity>() ); } else if(predMode->getValue() == predictionMode::Pred_RunCommmand ) { CUtilMove::safeRunCommand( gInts.Prediction, other ); } Vector hit; do { hit = getHitBoxVector( other, findBestHitbox( other ) ); if( visible( other, hit ) == false ) { targetSystem->removeTarget( target.ent ); target = targetSystem->getBestTarget(); if( target.ent == -1 ) return; other = target.ent; } else { break; } } while( true ); // once again use the predicted origin of our local player VectorAngles( hit - ( gLocalPlayerVars.pred.origin + local.get<Vector>( gEntVars.vecViewOffset ) ), angles ); ClampAngle( angles ); // Vector oldView = pUserCmd->viewangles; CAimbot::silentMovementFix( pUserCmd, angles ); pUserCmd->viewangles = angles; if( useSilent->getValue() != true ) gInts.Engine->SetViewAngles( angles ); if( autoShoot->getValue() ) pUserCmd->buttons |= IN_ATTACK; // attack when autoshoot enabled return; } bool CAimbot::isValidTarget( CEntity<> &ent ) { if( ent.isNull() ) return false; if( ent->IsDormant() ) return false; //if(!isPlayer(ent)) // return false; if( ent.index() == me ) return false; if( ent->GetClientClass()->iClassID != classId::CTFPlayer ) { player_info_t temp; if( !gInts.Engine->GetPlayerInfo(ent.index(), &temp) ) return false; } else { if( ent.get<BYTE>( gEntVars.iLifeState ) != LIFE_ALIVE ) return false; } if( ent.get<int>( gEntVars.iTeam ) == gLocalPlayerVars.team ) return false; return true; } Vector CAimbot::getHitBoxVector( CEntity<> &ent, int hitbox ) { //Vector vHitbox = /*hitboxCache[{ent, hitbox}]*/{ 0,0,0 }; //if( vHitbox == Vector( 0, 0, 0 ) ) //{ // if( !isValidBuilding( ent ) ) // { // PDWORD model = ent->GetModel(); // if( !model ) // return vHitbox; // PDWORD pStudioHdr = gInts.ModelInfo->GetStudiomodel( model ); // if( !pStudioHdr ) // return vHitbox; // if( !ent->SetupBones( BoneToWorld, 128, 0x100, 0 ) ) // return vHitbox; // box = GetHitbox( hitbox, pStudioHdr ); // if( !box ) // return vHitbox; // VectorTransform( box->bbmin, BoneToWorld[ box->bone ], Min ); // VectorTransform( box->bbmax, BoneToWorld[ box->bone ], Max ); // vHitbox = ( Min + Max ) / 2; // // this either will predict or just return the same thing back // Vector pred = predict( ent, vHitbox ); // //hitboxCache[{ent, hitbox}] = pred; // return pred; // } // else // { // Vector temp = { 0,0,0 }; // ent->GetWorldSpaceCenter( temp ); // return temp; // } //} //else //{ // // Log::Console("returning cached value of (%f,%f,%f)", vHitbox.x, vHitbox.y, vHitbox.z); // return vHitbox; //} return predict(ent, gCache.getHitboxPosition( ent.index(), ( tf_hitbox ) hitbox, true )); } inline mstudiobbox_t *CAimbot::GetHitbox( int iHitbox, DWORD *pHeader ) { int HitboxSetIndex = *( int * ) ( ( DWORD ) pHeader + 0xB0 ); mstudiohitboxset_t *pSet = ( mstudiohitboxset_t * ) ( ( ( PBYTE ) pHeader ) + HitboxSetIndex ); return ( mstudiobbox_t * ) pSet->pHitbox( iHitbox ); } inline bool CAimbot::isPlayer( CEntity<> &ent ) { if( !ent.isNull() ) return ent->GetClientClass()->iClassID == classId::CTFPlayer; return false; } //int CAimbot::selectTarget() //{ // std::pair<int, float> target; // // // this is the furthest our gun can shoot // // therefore it should be reasonable that all other entitys should be closer than this // target.second = 8192.0f; // target.first = -1; // // // loop through data // for(auto &t : stats) // { // // if we have already got the lowest distance, stop looping // if(target.second == 1.0f) // break; // // float currDist = t.second; // // // if priority is enabled // if(variables[10].vars[0].bGet()) // { // vecVars &vars = variables[10].vars; // // scale the distances by priority // // CEntity<> tar{t.first}; // // if(!tar.isNull()) // { // if(!tar->IsDormant()) // { // tf_classes Class = (tf_classes)tar.get<int>(gEntVars.iClass); // currDist /= vars[Class].iGet(); // } // } // } // // if(currDist < target.second) // { // target = t; // } // } // // return target.first; //} inline bool CAimbot::checkCond( CEntity<> &ent ) { if( !ignoreCond->getValue() ) return true; int cond = ent.get<int>( gEntVars.iPlayerCond ); // people we shouldnt hit if( cond & tf_cond::TFCond_Cloaked || cond & tf_cond::TFCond_Ubercharged || cond & tf_cond::TFCond_UberchargeFading || cond & tf_cond::TFCond_CloakFlicker || cond & tf_cond::TFCond_DeadRingered || cond & tf_cond::TFCond_Bonked ) return false; // TODO add netvars for cond ex and ex2 return true; } namespace predictionFuncs { float TimeToImpact( Vector from, Vector to, float flSpeed ) { //float flSpeed = gPrediction.GetProjectileSpeed(); float flTime = 1.0f; float flDistance = 1.0f; flDistance = ( from - to ).Length(); flTime = flDistance / flSpeed; //float flNextPrimaryAttack = *(float*)((DWORD)m_weapon + 0x1660) - g_pGlobalVars->curtime; //if(flNextPrimaryAttack > 0.0f) // flTime += flNextPrimaryAttack; return flTime; } Vector PredictedPos( CBaseEntity *target, Vector pos, float time ) { Vector vel, vpos; vel = EstimateAbsVelocity( target ); vpos = pos + vel * time; /* FIX: this flag is based on CURRENT position, not the predicted position, so they could actually be on the floor when they aren't */ if( !( CEntity<>{target->GetIndex()}.get<int>( gEntVars.iFlags ) & FL_ONGROUND ) ) vpos += ( Vector{ 0, 0, -800.0f } * 0.5f * time * time ); return vpos; } bool PredictProjectiles( CBaseEntity *target, Vector in, Vector &out, float speed ) { Vector vShootPos, vTargetVelocity, vOutPos = in; Vector vAngles; float flDelta = 0.01f; float flTargetSpeed = 0.0f; float flProjTime = 1.0f; float flTimeFromPlayerToNewPos = 1.0f; Ray_t ray; trace_t tr; out = in; if( !target ) return false; //if(!ResetFrameData()) // return false; //if(!Util::HasProjectileLauncher(m_weaponID)) // return true; //Util::GetLocalEyePos( vShootPos ); vShootPos = gLocalPlayerVars.pred.origin + gLocalPlayerVars.viewOffset; float flProjectileSpeed = speed; vTargetVelocity = EstimateAbsVelocity( target ); flTargetSpeed = vTargetVelocity.Length(); /* This could cause a crash! */ if( flTargetSpeed == 0.0f ) return true; float flDeltaOfTimes = 1.0f; //int nPoints = 0; /* Keep going until we find a point in space that it takes the same time for the projectile to reach as it does for the player, THAT will be the most probable hit location */ while( flDeltaOfTimes > 0.0f ) { vOutPos = PredictedPos( target, in, flDelta ); // the position at guestimated time x flProjTime = TimeToImpact( vShootPos, vOutPos, flProjectileSpeed ); // the time it takes for our projectile to reach position predicted in space flTimeFromPlayerToNewPos = ( in - vOutPos ).Length() / flTargetSpeed; //the time it takes to get from the current position to the predicted position in space flDeltaOfTimes = flProjTime - flTimeFromPlayerToNewPos; flDelta += ( gInts.Globals->interval_per_tick ); } ray.Init(in, vOutPos); CBaseFilter pTrace; pTrace.SetIgnoreSelf(target); gInts.EngineTrace->TraceRay(ray, MASK_SOLID, &pTrace, &tr); /* If the predicted position intersected with an object, stop the prediction there */ if( /*tr.m_pEnt = g_pClientEntList->GetListedEntity( 0 )->GetBaseEntity( )*/tr.contents & CONTENTS_SOLID && tr.fraction != 1.0f) { vOutPos = tr.end; if((vShootPos - tr.end).Length() < 400.0f) return false; float flDistanceToFloor = (in - tr.end).Length(); float flTimeFromPlayerToFloor = flDistanceToFloor / flTargetSpeed; // how long will it take the player to get there? flProjTime = TimeToImpact(vShootPos, tr.end, speed); // how long will it take our bullet to get there if(fabs(flTimeFromPlayerToFloor - flProjTime) > 0.1f) { return false; } } out = vOutPos; return true; } } // does all of the possible predictions that are needed inline Vector CAimbot::predict( CEntity<> &ent, Vector v ) { // Vector vel{0.f,0.f,0.f}; // clear the positions //positions.clear(); float latency = gInts.Engine->GetNetChannelInfo()->GetLatency( FLOW_OUTGOING ) + gInts.Engine->GetNetChannelInfo()->GetLatency( FLOW_INCOMING ); //Log::Console( "latency %f", latency ); Vector velocity = EstimateAbsVelocity( ent.castToPointer<CBaseEntity>() ); if( projectilePrediction->getValue() ) { currPos = v; // prevent devide by 0 errors later on speed = 10000.0f; CEntity<> local{ me }; CEntity<> localwep{ HANDLE2INDEX( local.get<int>( gEntVars.hActiveWeapon ) ) }; if( !localwep.isNull() ) { classId wepID = localwep->GetClientClass()->iClassID; switch( wepID ) { case classId::CTFRocketLauncher: case classId::CTFRocketLauncher_AirStrike: case classId::CTFRocketLauncher_Mortar: speed = 1100.0f; break; case classId::CTFRocketLauncher_DirectHit: speed = 1980.0f; break; default: break; } } //(pos + (vel * time)) + (1/2 * (grav * (time ^ 2))) Vector oldVector = local->GetAbsOrigin() - v; distance = oldVector.Length(); time = 1.0f; Vector localPos = gLocalPlayerVars.pred.origin + gLocalPlayerVars.viewOffset; //velocity *= gInts.Globals->interval_per_tick; //if(ent.get<int>(gEntVars.iFlags) & FL_ONGROUND) //{ // // if they are on the ground then we can use simple methods to predict where they will be // time = ((distance / speed) * latency) /* * projectileMult*/; // //asymTime = asymetricInterceptionTime(localPos, currPos, {speed, 0, 0}, velocity); // asymTime = asymetricInterceptionTime( localPos, currPos, { speed, 0, 0 }, velocity ); // //if(time == otherTime) // // Log::Console("TIMES ARE EQUAL!"); // //v += (velocity * time); // v += calculateAddition(asymTime, velocity); // //Log::Console( "%f : %f", time, asymTime ); //} //else //{ // // TODO use sv_gravity instead of hardcoding this // Vector grav = {0, 0, -800}; // // local position, target position, local velocity, target velocity, target gravity. // asymTime = asymetricSingleAcceleratedInterceptionTime(localPos, currPos, {speed, 0, 0}, velocity, grav) * latency; // //v += grav * powf(time, 2) * 0.5; // //v += velocity * time; // v += calculateAddition( asymTime, velocity, grav ); //} //Vector newV; //predictionFuncs::PredictProjectiles( ent.castToPointer<CBaseEntity>(), v, newV, speed ); //v = newV; v += projectileHelper::PredictCorrection( localwep.castToPointer<CBaseCombatWeapon>(), ent, localPos, projectileQuality->getValue() ); // if they are not on the ground // v -= (0.5 * (grav * (powf(time, 2))) * gInts.Globals->frametime); // no prediction is enabled just return the input // v += vel; } if( latencyPrediction->getValue() ) { // scale the velocity by the latency v += velocity + Vector{ latency, latency, latency }; } predPos = v; return v; } inline void CAimbot::silentMovementFix( CUserCmd *pUserCmd, Vector angles ) { Vector vecSilent( pUserCmd->forwardmove, pUserCmd->sidemove, pUserCmd->upmove ); float flSpeed = sqrt( vecSilent.x * vecSilent.x + vecSilent.y * vecSilent.y ); Vector angMove; VectorAngles( vecSilent, angMove ); float flYaw = DEG2RAD( angles.y - pUserCmd->viewangles.y + angMove.y ); pUserCmd->forwardmove = cos( flYaw ) * flSpeed; pUserCmd->sidemove = sin( flYaw ) * flSpeed; // pUserCmd->viewangles = angles; } bool CAimbot::isValidBuilding( CEntity<> &ent ) { //ClientClass *pClass = ent->GetClientClass(); //if( pClass ) //{ // switch( ent->GetClientClass()->iClassID ) // { // case classId::CObjectTeleporter: // case classId::CObjectSentrygun: // case classId::CObjectDispenser: // return true; // default: // return false; // } //} return false; } inline int CAimbot::findBestHitbox( CEntity<>& ent ) { CEntity<> local{ me }; // if autoHitbox is not enabled then just return the desired hitbox if( !autoHitbox->getValue() ) return ( int ) hitbox->getValue(); // find the best hitbox for each class and weapon // by default, aim for torso tf_hitbox bestHitbox = tf_hitbox::spine_2; switch( gLocalPlayerVars.Class ) { // these are the only 2 classes where we want to aim at the head (for headshotting power) case tf_classes::TF2_Sniper: // if we are using smg then aim at the chest if( gLocalPlayerVars.wepTag.isSecondary() ) break; case tf_classes::TF2_Spy: bestHitbox = tf_hitbox::head; break; default: break; } // first check if the ideal hitbox is visible if( visible( ent, ( int )bestHitbox ) ) { return ( int )bestHitbox; } else { // iterate through all hitboxes for( int i = 0; i < 17; i++ ) { // check if the hitbox is visible if( visible( ent, i ) ) { // if this hitbox is visible, use it bestHitbox = ( tf_hitbox ) i; break; } } } // return the best hitbox now return ( int )bestHitbox; }