diff --git a/Amalgam/src/Features/Aimbot/AimbotHitscan/AimbotHitscan.cpp b/Amalgam/src/Features/Aimbot/AimbotHitscan/AimbotHitscan.cpp index f97fa21..a3c85ea 100644 --- a/Amalgam/src/Features/Aimbot/AimbotHitscan/AimbotHitscan.cpp +++ b/Amalgam/src/Features/Aimbot/AimbotHitscan/AimbotHitscan.cpp @@ -314,7 +314,10 @@ int CAimbotHitscan::CanHit(Target_t& target, CTFPlayer* pLocal, CTFWeaponBase* p if (Vars::Aimbot::General::Ignore.Value & UNSIMULATED && G::ChokeMap[target.m_pEntity->entindex()] > Vars::Aimbot::General::TickTolerance.Value) return false; - Vec3 vEyePos = pLocal->GetShootPos(); + Vec3 vEyePos = pLocal->GetShootPos(), vPeekPos = {}; + float flSpread = pWeapon->GetWeaponSpread(); + if (flSpread) + vPeekPos = pLocal->GetShootPos() + pLocal->GetAbsVelocity() * TICKS_TO_TIME(-Vars::Aimbot::General::HitscanPeek.Value); const float flMaxRange = powf(GetMaxRange(pWeapon), 2.f); auto pModel = target.m_pEntity->GetModel(); @@ -350,6 +353,16 @@ int CAimbotHitscan::CanHit(Target_t& target, CTFPlayer* pLocal, CTFWeaponBase* p } } + static int iPreferredTick = 0; // if we're doubletapping, we can't change viewangles so have a preferred tick to use + if (!I::ClientState->chokedcommands) + iPreferredTick = 0; + if (iPreferredTick) + { + auto pivot = std::find_if(vRecords.begin(), vRecords.end(), [](auto& s) -> bool { return s.iTickCount == iPreferredTick; }); + if (pivot != vRecords.end()) + std::rotate(vRecords.begin(), pivot, pivot + 1); + } + auto RayToOBB = [](const Vec3& origin, const Vec3& direction, const Vec3& position, const Vec3& min, const Vec3& max, const matrix3x4 orientation) -> bool { if (Vars::Aimbot::General::AimType.Value != 2) @@ -446,7 +459,11 @@ int CAimbotHitscan::CanHit(Target_t& target, CTFPlayer* pLocal, CTFWeaponBase* p Vec3 vForward = {}; Math::AngleVectors(vAngles, &vForward); - if (SDK::VisPos(pLocal, target.m_pEntity, vEyePos, vTransformed)) + bool bPeekCheck = flSpread ? SDK::VisPos(pLocal, target.m_pEntity, vPeekPos, vTransformed) : true; + if (bPeekCheck) + flSpread = 0.f; // only use with a single hitbox + + if (SDK::VisPos(pLocal, target.m_pEntity, vEyePos, vTransformed) && bPeekCheck) { target.m_vAngleTo = vAngles; if (RayToOBB(vEyePos, vForward, vCenter, vMins, vMaxs, boneMatrix[pair.first->bone])) // for the time being, no vischecks against other hitboxes @@ -460,6 +477,8 @@ int CAimbotHitscan::CanHit(Target_t& target, CTFPlayer* pLocal, CTFWeaponBase* p } if (bWillHit) { + iPreferredTick = pTick.iTickCount; + target.m_Tick = pTick; target.m_vPos = vTransformed; if (target.m_TargetType == ETargetType::PLAYER) @@ -510,7 +529,11 @@ int CAimbotHitscan::CanHit(Target_t& target, CTFPlayer* pLocal, CTFWeaponBase* p Vec3 vForward = {}; Math::AngleVectors(vAngles, &vForward); - if (SDK::VisPos(pLocal, target.m_pEntity, vEyePos, vTransformed)) + bool bPeekCheck = flSpread ? SDK::VisPos(pLocal, target.m_pEntity, vPeekPos, vTransformed) : true; + if (bPeekCheck) + flSpread = 0.f; // only use with a single hitbox + + if (SDK::VisPos(pLocal, target.m_pEntity, vEyePos, vTransformed) && bPeekCheck) { target.m_vAngleTo = vAngles; if (RayToOBB(vEyePos, vForward, target.m_pEntity->m_vecOrigin(), vMins, vMaxs, transform)) // for the time being, no vischecks against other hitboxes diff --git a/Amalgam/src/Features/Aimbot/AimbotMelee/AimbotMelee.cpp b/Amalgam/src/Features/Aimbot/AimbotMelee/AimbotMelee.cpp index 8aedcbb..ee0f75b 100644 --- a/Amalgam/src/Features/Aimbot/AimbotMelee/AimbotMelee.cpp +++ b/Amalgam/src/Features/Aimbot/AimbotMelee/AimbotMelee.cpp @@ -648,6 +648,7 @@ bool CAimbotMelee::RunSapper(CTFPlayer* pLocal, CTFWeaponBase* pWeapon, CUserCmd if (pCmd->buttons & IN_ATTACK) { + G::IsAttacking = true; target.m_vAngleTo = Aim(pCmd->viewangles, Math::CalcAngle(vLocalPos, target.m_vPos)); target.m_vAngleTo.x = pCmd->viewangles.x; // we don't need to care about pitch Aim(pCmd, target.m_vAngleTo); diff --git a/Amalgam/src/Features/ImGui/Menu/Menu.cpp b/Amalgam/src/Features/ImGui/Menu/Menu.cpp index be9eda4..d276755 100644 --- a/Amalgam/src/Features/ImGui/Menu/Menu.cpp +++ b/Amalgam/src/Features/ImGui/Menu/Menu.cpp @@ -152,15 +152,16 @@ void CMenu::MenuAimbot() { if (Section("debug## aimbot")) { + FSlider("hitscan peek", Vars::Aimbot::General::HitscanPeek, 0, 10); FSlider("offset## nospread", Vars::Aimbot::General::NoSpreadOffset, -5.f, 5.f, 1.f, "%.1f", FSlider_Precision); - FSlider("average", Vars::Aimbot::General::NoSpreadAverage, 1, 100); + FSlider("average", Vars::Aimbot::General::NoSpreadAverage, 1, 25); } EndSection(); } if (Section("Backtrack")) { FToggle("Enabled", Vars::Backtrack::Enabled); FToggle("Prefer on shot", Vars::Backtrack::PreferOnShot, FToggle_Middle); - FSlider("Fake latency", Vars::Backtrack::Latency, 0, F::Backtrack.flMaxUnlag * 1000, 5, "%d", FSlider_Clamp); // unreliable above 900 + FSlider("Fake latency", Vars::Backtrack::Latency, 0, F::Backtrack.flMaxUnlag * 1000, 5, "%d", FSlider_Clamp); // unreliable above 1000 - ping probably FSlider("Fake interp", Vars::Backtrack::Interp, 0, F::Backtrack.flMaxUnlag * 1000, 5, "%d", FSlider_Clamp); FSlider("Window", Vars::Backtrack::Window, 1, 200, 5, "%d", FSlider_Clamp); } EndSection(); diff --git a/Amalgam/src/Features/Misc/Misc.cpp b/Amalgam/src/Features/Misc/Misc.cpp index 98ca586..730dc85 100644 --- a/Amalgam/src/Features/Misc/Misc.cpp +++ b/Amalgam/src/Features/Misc/Misc.cpp @@ -512,6 +512,24 @@ void CMisc::AntiWarp(CTFPlayer* pLocal, CUserCmd* pCmd) } else vVelocity = pLocal->m_vecVelocity(); + + /* + static bool bSet = false; + + if (!G::AntiWarp) + { + bSet = false; + return; + } + + if (!G::IsAttacking && !bSet) + { + bSet = true; + SDK::StopMovement(pLocal, pCmd); + } + else + pCmd->forwardmove = pCmd->sidemove = 0.f; + */ } void CMisc::LegJitter(CTFPlayer* pLocal, CUserCmd* pCmd, bool pSendPacket) diff --git a/Amalgam/src/Features/Simulation/MovementSimulation/MovementSimulation.cpp b/Amalgam/src/Features/Simulation/MovementSimulation/MovementSimulation.cpp index a762d46..9f99ec3 100644 --- a/Amalgam/src/Features/Simulation/MovementSimulation/MovementSimulation.cpp +++ b/Amalgam/src/Features/Simulation/MovementSimulation/MovementSimulation.cpp @@ -125,7 +125,21 @@ void CMovementSimulation::FillVelocities() if (TIME_TO_TICKS(flSimTime - flOldSimTime) <= 0) return; - mVelocities[iEntIndex].push_front({ vVelocity, flSimTime }); + const VelocityData vRecord = { + vVelocity, + pEntity->IsOnGround(), + flSimTime + }; + + if (!mVelocities[iEntIndex].empty()) + { + const VelocityData vLast = mVelocities[iEntIndex][0]; + + if (vRecord.m_bGrounded != vLast.m_bGrounded) + mVelocities[iEntIndex].clear(); + } + + mVelocities[iEntIndex].push_front(vRecord); if (mVelocities[iEntIndex].size() > 66) mVelocities[iEntIndex].pop_back(); @@ -217,8 +231,8 @@ bool CMovementSimulation::Initialize(CBaseEntity* pEntity, PlayerStorage& player break; const auto& pRecord1 = mVelocityRecords[i], &pRecord2 = mVelocityRecords[i + 1]; - const float flYaw1 = Math::VelocityToAngles(pRecord1.first).y, flYaw2 = Math::VelocityToAngles(pRecord2.first).y; - const float flTime1 = pRecord1.second, flTime2 = pRecord2.second; + const float flYaw1 = Math::VelocityToAngles(pRecord1.m_vVelocity).y, flYaw2 = Math::VelocityToAngles(pRecord2.m_vVelocity).y; + const float flTime1 = pRecord1.m_flSimTime, flTime2 = pRecord2.m_flSimTime; float flYaw = (flYaw1 - flYaw2) / TIME_TO_TICKS(flTime1 - flTime2); flYaw = fmodf(flYaw + 180.f, 360.f); @@ -283,14 +297,14 @@ bool CMovementSimulation::SetupMoveData(PlayerStorage& playerStorage) return true; } -bool CMovementSimulation::GetYawDifference(const std::deque>& mVelocityRecords, size_t i, float* flYaw) +bool CMovementSimulation::GetYawDifference(const std::deque& mVelocityRecords, size_t i, float* flYaw) { if (mVelocityRecords.size() <= i + 2) return false; const auto& pRecord1 = mVelocityRecords[i], &pRecord2 = mVelocityRecords[i + 1]; - const float flYaw1 = Math::VelocityToAngles(pRecord1.first).y, flYaw2 = Math::VelocityToAngles(pRecord2.first).y; - const float flTime1 = pRecord1.second, flTime2 = pRecord2.second; + const float flYaw1 = Math::VelocityToAngles(pRecord1.m_vVelocity).y, flYaw2 = Math::VelocityToAngles(pRecord2.m_vVelocity).y; + const float flTime1 = pRecord1.m_flSimTime, flTime2 = pRecord2.m_flSimTime; const int iTicks = std::max(TIME_TO_TICKS(flTime1 - flTime2), 1); *flYaw = (flYaw1 - flYaw2) / iTicks; @@ -300,7 +314,7 @@ bool CMovementSimulation::GetYawDifference(const std::deque 0 ? 1 : -1; - if (fabsf(*flYaw) < 640.f / pRecord1.first.Length2D() / iTicks) // dumb way to get straight bool + if (fabsf(*flYaw) < 200.f / pRecord1.m_vVelocity.Length2D() / iTicks) // dumb way to get straight bool return false; return !i || iLastSign == iCurSign ? true : false; diff --git a/Amalgam/src/Features/Simulation/MovementSimulation/MovementSimulation.h b/Amalgam/src/Features/Simulation/MovementSimulation/MovementSimulation.h index f211dd7..94f9f65 100644 --- a/Amalgam/src/Features/Simulation/MovementSimulation/MovementSimulation.h +++ b/Amalgam/src/Features/Simulation/MovementSimulation/MovementSimulation.h @@ -66,6 +66,13 @@ struct PlayerStorage bool m_bInitFailed = false; }; +struct VelocityData +{ + Vec3 m_vVelocity = {}; + bool m_bGrounded = true; + float m_flSimTime = 0.f; +}; + class CMovementSimulation { private: @@ -73,7 +80,7 @@ private: void Reset(PlayerStorage& playerStorage); bool SetupMoveData(PlayerStorage& playerStorage); - bool GetYawDifference(const std::deque>& mPositionRecords, size_t i, float* flYaw); + bool GetYawDifference(const std::deque& mPositionRecords, size_t i, float* flYaw); float GetAverageYaw(const int iIndex, const int iSamples); bool StrafePrediction(PlayerStorage& playerStorage, const int iSamples); @@ -81,7 +88,7 @@ private: bool m_bOldFirstTimePredicted = false; float m_flOldFrametime = 0.f; - std::unordered_map>> mVelocities; + std::unordered_map> mVelocities; public: void FillVelocities(); diff --git a/Amalgam/src/Features/Visuals/Visuals.cpp b/Amalgam/src/Features/Visuals/Visuals.cpp index 3428fb5..63bf389 100644 --- a/Amalgam/src/Features/Visuals/Visuals.cpp +++ b/Amalgam/src/Features/Visuals/Visuals.cpp @@ -334,7 +334,7 @@ void CVisuals::DrawDebugInfo(CTFPlayer* pLocal) Vec3 vVelocity = pLocal->m_vecVelocity(); H::Draw.String(fFont, x, y += fFont.m_nTall + 1, { 255, 255, 255, 255 }, ALIGN_TOPLEFT, "Velocity: %.3f (%.3f, %.3f, %.3f)", vVelocity.Length(), vVelocity.x, vVelocity.y, vVelocity.z); - H::Draw.String(fFont, x, y += fFont.m_nTall + 1, { 255, 255, 255, 255 }, ALIGN_TOPLEFT, std::format("Attacking: {}", G::IsAttacking).c_str()); + H::Draw.String(fFont, x, y += fFont.m_nTall + 1, { 255, 255, 255, 255 }, ALIGN_TOPLEFT, std::format("Attacking: {} ({}, {})", G::IsAttacking, G::CanPrimaryAttack, G::CanSecondaryAttack).c_str()); H::Draw.String(fFont, x, y += fFont.m_nTall + 1, { 255, 255, 255, 255 }, ALIGN_TOPLEFT, "RoundState: %i", SDK::GetRoundState()); diff --git a/Amalgam/src/Hooks/BaseClientDLL_FrameStageNotify.cpp b/Amalgam/src/Hooks/BaseClientDLL_FrameStageNotify.cpp index bae7632..0117224 100644 --- a/Amalgam/src/Hooks/BaseClientDLL_FrameStageNotify.cpp +++ b/Amalgam/src/Hooks/BaseClientDLL_FrameStageNotify.cpp @@ -39,11 +39,11 @@ MAKE_HOOK(BaseClientDLL_FrameStageNotify, U::Memory.GetVFunc(I::BaseClientDLL, 3 H::Entities.Fill(); for (auto& pEntity : H::Entities.GetGroup(EGroupType::PLAYERS_ALL)) { - G::VelocityMap[pEntity->entindex()] = { pEntity->m_vecOrigin(), pEntity->m_vecMaxs().z - pEntity->m_vecMins().z, pEntity->m_flSimulationTime() }; - if (pEntity->entindex() == I::EngineClient->GetLocalPlayer()) continue; // local player managed in CPrediction_RunCommand + G::VelocityMap[pEntity->entindex()] = { pEntity->m_vecOrigin(), pEntity->m_vecMaxs().z - pEntity->m_vecMins().z, pEntity->m_flSimulationTime() }; + static auto sv_maxusrcmdprocessticks = U::ConVars.FindVar("sv_maxusrcmdprocessticks"); const int iTicks = sv_maxusrcmdprocessticks ? sv_maxusrcmdprocessticks->GetInt() : 24; if (auto iDifference = std::min(TIME_TO_TICKS(pEntity->m_flSimulationTime() - pEntity->m_flOldSimulationTime()), iTicks)) diff --git a/Amalgam/src/Hooks/ClientModeShared_CreateMove.cpp b/Amalgam/src/Hooks/ClientModeShared_CreateMove.cpp index 16832fe..6357ac5 100644 --- a/Amalgam/src/Hooks/ClientModeShared_CreateMove.cpp +++ b/Amalgam/src/Hooks/ClientModeShared_CreateMove.cpp @@ -44,32 +44,51 @@ MAKE_HOOK(ClientModeShared_CreateMove, U::Memory.GetVFunc(I::ClientModeShared, 2 if (G::WeaponDefIndex != nOldDefIndex || !pWeapon->m_iClip1() || !pLocal->IsAlive() || pLocal->IsTaunting() || pLocal->IsBonked() || pLocal->IsAGhost() || pLocal->IsInBumperKart()) G::WaitForShift = 1; - bool bCanAttack = pLocal->CanAttack() && (pWeapon->m_iWeaponID() == TF_WEAPON_FLAME_BALL ? pLocal->m_flTankPressure() >= 100.f : true); - G::CanPrimaryAttack = bCanAttack && pWeapon->CanPrimaryAttack(pLocal); - G::CanSecondaryAttack = bCanAttack && pWeapon->CanSecondaryAttack(pLocal); + G::CanPrimaryAttack = G::CanSecondaryAttack = false; + bool bCanAttack = pLocal->CanAttack(); switch (SDK::GetRoundState()) { case GR_STATE_BETWEEN_RNDS: case GR_STATE_GAME_OVER: - if (pLocal->m_fFlags() & FL_FROZEN) - G::CanPrimaryAttack = G::CanSecondaryAttack = false; + bCanAttack = bCanAttack && !(pLocal->m_fFlags() & FL_FROZEN); } - if (pWeapon->m_iSlot() != SLOT_MELEE) + if (bCanAttack) { - if (pWeapon->IsInReload()) - G::CanPrimaryAttack = pWeapon->HasPrimaryAmmoForShot(); + switch (pWeapon->m_iWeaponID()) + { + case TF_WEAPON_FLAME_BALL: + G::CanPrimaryAttack = G::CanSecondaryAttack = pLocal->m_flTankPressure() >= 100.f; + break; + case TF_WEAPON_BUILDER: + G::CanPrimaryAttack = true; + break; + default: + G::CanPrimaryAttack = pWeapon->CanPrimaryAttack(pLocal); + G::CanSecondaryAttack = pWeapon->CanSecondaryAttack(pLocal); - if (pWeapon->m_iWeaponID() == TF_WEAPON_MINIGUN && pWeapon->As()->m_iWeaponState() != AC_STATE_FIRING && pWeapon->As()->m_iWeaponState() != AC_STATE_SPINNING) - G::CanPrimaryAttack = false; + if (pWeapon->m_iSlot() == SLOT_MELEE) + break; + + if (pWeapon->IsInReload()) + G::CanPrimaryAttack = pWeapon->HasPrimaryAmmoForShot(); - if (pWeapon->m_iWeaponID() == TF_WEAPON_FLAREGUN_REVENGE && pCmd->buttons & IN_ATTACK2) - G::CanPrimaryAttack = false; + if (G::WeaponDefIndex != Soldier_m_TheBeggarsBazooka && pWeapon->m_iClip1() == 0) + G::CanPrimaryAttack = false; - if (pWeapon->m_bEnergyWeapon() && !pWeapon->m_flEnergy()) - G::CanPrimaryAttack = false; + if (pWeapon->m_bEnergyWeapon() && !pWeapon->m_flEnergy()) + G::CanPrimaryAttack = false; - if (G::WeaponDefIndex != Soldier_m_TheBeggarsBazooka && pWeapon->m_iClip1() == 0) - G::CanPrimaryAttack = false; + switch (pWeapon->m_iWeaponID()) + { + case TF_WEAPON_MINIGUN: + if (pWeapon->As()->m_iWeaponState() != AC_STATE_FIRING && pWeapon->As()->m_iWeaponState() != AC_STATE_SPINNING) + G::CanPrimaryAttack = false; + break; + case TF_WEAPON_FLAREGUN_REVENGE: + if (pCmd->buttons & IN_ATTACK2) + G::CanPrimaryAttack = false; + } + } } G::IsAttacking = SDK::IsAttacking(pLocal, pWeapon, pCmd); diff --git a/Amalgam/src/SDK/SDK.cpp b/Amalgam/src/SDK/SDK.cpp index 8679e2b..65f35c6 100644 --- a/Amalgam/src/SDK/SDK.cpp +++ b/Amalgam/src/SDK/SDK.cpp @@ -431,11 +431,9 @@ bool SDK::IsAttacking(CTFPlayer* pLocal, CTFWeaponBase* pWeapon, const CUserCmd* return trace.DidHit() && !(trace.surface.flags & 0x0004) /*SURF_SKY*/; } case TF_WEAPON_MINIGUN: - { return (pWeapon->As()->m_iWeaponState() == AC_STATE_FIRING || pWeapon->As()->m_iWeaponState() == AC_STATE_SPINNING) && pCmd->buttons & IN_ATTACK && G::CanPrimaryAttack; } - } return pCmd->buttons & IN_ATTACK && G::CanPrimaryAttack; } @@ -469,7 +467,7 @@ bool SDK::StopMovement(CTFPlayer* pLocal, CUserCmd* pCmd) if (!G::IsAttacking) { const float direction = Math::VelocityToAngles(pLocal->m_vecVelocity()).y; - pCmd->viewangles = { -90, direction, 0 }; + pCmd->viewangles = { 90, direction, 0 }; pCmd->sidemove = 0; pCmd->forwardmove = 0; return true; } diff --git a/Amalgam/src/SDK/Vars.h b/Amalgam/src/SDK/Vars.h index 1755bfd..b72de29 100644 --- a/Amalgam/src/SDK/Vars.h +++ b/Amalgam/src/SDK/Vars.h @@ -119,6 +119,7 @@ namespace Vars CVar(FOVCircle, true) CVar(NoSpread, false) + CVar(HitscanPeek, 3, NOSAVE) // debug CVar(NoSpreadOffset, 0.f, NOSAVE) // debug CVar(NoSpreadAverage, 5, NOSAVE) // debug SUBNAMESPACE_END(Global)