diff --git a/Amalgam/Amalgam.vcxproj b/Amalgam/Amalgam.vcxproj index 3c33541e..2b12e722 100644 --- a/Amalgam/Amalgam.vcxproj +++ b/Amalgam/Amalgam.vcxproj @@ -1260,6 +1260,7 @@ + @@ -1293,6 +1294,7 @@ + @@ -1308,11 +1310,18 @@ + + + + + + + @@ -1320,6 +1329,7 @@ + diff --git a/Amalgam/Amalgam.vcxproj.filters b/Amalgam/Amalgam.vcxproj.filters index 4206cdb1..ab0414ad 100644 --- a/Amalgam/Amalgam.vcxproj.filters +++ b/Amalgam/Amalgam.vcxproj.filters @@ -611,6 +611,16 @@ + + + + + + + + + + diff --git a/Amalgam/src/Features/Aimbot/AimbotMelee/AimbotMelee.cpp b/Amalgam/src/Features/Aimbot/AimbotMelee/AimbotMelee.cpp index 148bfcd3..7d3e0c1b 100644 --- a/Amalgam/src/Features/Aimbot/AimbotMelee/AimbotMelee.cpp +++ b/Amalgam/src/Features/Aimbot/AimbotMelee/AimbotMelee.cpp @@ -379,11 +379,11 @@ int CAimbotMelee::CanHit(Target_t& tTarget, CTFPlayer* pLocal, CTFWeaponBase* pW Vec3 vTraceEnd = m_vEyePos + vForward * flRange; SDK::TraceHull(m_vEyePos, vTraceEnd, {}, {}, MASK_SOLID, &filter, &trace); - bool bReturn = trace.m_pEnt && trace.m_pEnt == tTarget.m_pEntity; + bool bReturn = trace.m_pEnt == tTarget.m_pEntity; if (!bReturn) { SDK::TraceHull(m_vEyePos, vTraceEnd, vSwingMins, vSwingMaxs, MASK_SOLID, &filter, &trace); - bReturn = trace.m_pEnt && trace.m_pEnt == tTarget.m_pEntity; + bReturn = trace.m_pEnt == tTarget.m_pEntity; } if (bReturn && Vars::Aimbot::Melee::AutoBackstab.Value && pWeapon->GetWeaponID() == TF_WEAPON_KNIFE) @@ -411,7 +411,7 @@ int CAimbotMelee::CanHit(Target_t& tTarget, CTFPlayer* pLocal, CTFWeaponBase* pW vTraceEnd = m_vEyePos + vForward * flRange; SDK::TraceHull(m_vEyePos, vTraceEnd, vSwingMins, vSwingMaxs, MASK_SOLID, &filter, &trace); - if (trace.m_pEnt && trace.m_pEnt == tTarget.m_pEntity) + if (trace.m_pEnt == tTarget.m_pEntity) return 2; } } diff --git a/Amalgam/src/Features/Aimbot/AimbotProjectile/AimbotProjectile.cpp b/Amalgam/src/Features/Aimbot/AimbotProjectile/AimbotProjectile.cpp index f95d42ac..72d2a42c 100644 --- a/Amalgam/src/Features/Aimbot/AimbotProjectile/AimbotProjectile.cpp +++ b/Amalgam/src/Features/Aimbot/AimbotProjectile/AimbotProjectile.cpp @@ -16,6 +16,9 @@ #ifdef SPLASH_DEBUG5 static std::map s_mTraceCount = {}; #endif +#ifdef SPLASH_DEBUG2 +//#include "../../Visuals/Visuals.h" +#endif static inline std::vector GetTargets(CTFPlayer* pLocal, CTFWeaponBase* pWeapon) { @@ -270,6 +273,16 @@ static inline float ArmTime(CTFWeaponBase* pWeapon) return 0.f; } +static inline bool ShouldLob(Info_t& tInfo) +{ + return tInfo.m_flGravity && Vars::Aimbot::Projectile::Modifiers.Value & Vars::Aimbot::Projectile::ModifiersEnum::LobAngles; +} + +static inline bool ShouldLob(MoveStorage& tMoveStorage, Info_t& tInfo) +{ + return tInfo.m_bIgnoreTiming && (tMoveStorage.m_bFailed || tMoveStorage.m_pPlayer->IsOnGround()); +} + static inline bool AirSplash(CTFWeaponBase* pWeapon) { if (!(Vars::Aimbot::Projectile::Modifiers.Value & Vars::Aimbot::Projectile::ModifiersEnum::AirSplash)) @@ -349,7 +362,7 @@ Directs_t CAimbotProjectile::GetDirects() auto& tTarget = *m_tInfo.m_pTarget; uint8_t iFlags = PointFlagsEnum::Regular; - if (m_tInfo.m_flGravity && Vars::Aimbot::Projectile::Modifiers.Value & Vars::Aimbot::Projectile::ModifiersEnum::LobAngles) + if (ShouldLob(m_tInfo)) iFlags |= PointFlagsEnum::Lob; const Vec3 vMins = tTarget.m_pEntity->m_vecMins(), vMaxs = tTarget.m_pEntity->m_vecMaxs(); @@ -418,37 +431,36 @@ Splashes_t CAimbotProjectile::GetSplashes() return vSplashes; vSplashes.push_back(PointFlagsEnum::Regular); - if (m_tInfo.m_flGravity && Vars::Aimbot::Projectile::Modifiers.Value & Vars::Aimbot::Projectile::ModifiersEnum::LobAngles) + if (ShouldLob(m_tInfo)) vSplashes.push_back(PointFlagsEnum::Lob); return vSplashes; } -static inline std::vector ComputePoints(float flRadius, int iSamples, bool bSphere) +static inline std::vector ComputePoints(float flRadius, int iSamples) { std::vector vPoints = { { Vec3(0.f, 0.f, -1.f) * flRadius } }; + if (!iSamples) + return vPoints; - if (bSphere) + vPoints.reserve(iSamples + 1); + + float flRotateX = Vars::Aimbot::Projectile::SplashRotateX.Value < 0.f ? SDK::StdRandomFloat(0.f, 360.f) : Vars::Aimbot::Projectile::SplashRotateX.Value; + float flRotateY = Vars::Aimbot::Projectile::SplashRotateY.Value < 0.f ? SDK::StdRandomFloat(0.f, 360.f) : Vars::Aimbot::Projectile::SplashRotateY.Value; + + float a = Math::PI * (3.f - sqrtf(5.f)); + for (int n = 0; n < iSamples; n++) { - vPoints.reserve(iSamples + 1); + float t = a * n; + float y = 1 - (n / (iSamples - 1.f)) * 2; + float r = sqrtf(1 - powf(y, 2)); + float x = cosf(t) * r; + float z = sinf(t) * r; - float flRotateX = Vars::Aimbot::Projectile::SplashRotateX.Value < 0.f ? SDK::StdRandomFloat(0.f, 360.f) : Vars::Aimbot::Projectile::SplashRotateX.Value; - float flRotateY = Vars::Aimbot::Projectile::SplashRotateY.Value < 0.f ? SDK::StdRandomFloat(0.f, 360.f) : Vars::Aimbot::Projectile::SplashRotateY.Value; + Vec3 vPoint = Vec3(x, y, z) * flRadius; + vPoint = Math::RotatePoint(vPoint, {}, { flRotateX, flRotateY }); - float a = Math::PI * (3.f - sqrtf(5.f)); - for (int n = 0; n < iSamples; n++) - { - float t = a * n; - float y = 1 - (n / (iSamples - 1.f)) * 2; - float r = sqrtf(1 - powf(y, 2)); - float x = cosf(t) * r; - float z = sinf(t) * r; - - Vec3 vPoint = Vec3(x, y, z) * flRadius; - vPoint = Math::RotatePoint(vPoint, {}, { flRotateX, flRotateY }); - - vPoints.push_back(vPoint); - } + vPoints.push_back(vPoint); } return vPoints; @@ -520,53 +532,111 @@ static inline void HandleTrace(const Vec3& vPoint, std::vector& vPoints } static float s_flTotal = 0.f; -static inline void HandleFace(const Face_t& tFace, std::vector& vPoints, float flDensity, float flRadius, float flCutoff, const Vec3& vTargetEye, const Vec3& vTargetCenter, Info_t& tInfo, CGameTrace& trace, ITraceFilter& filter) +static inline void HandleFace(Face_t& tFace, std::vector& vPoints, float flDensity, float flRadius, float flCutoff, const Vec3& vTargetEye, const Vec3& vTargetCenter, const Vec3& vTargetOrigin, Info_t& tInfo, CGameTrace& trace, ITraceFilter& filter) { float flRadiusSqr = powf(flRadius, 2); float flRadius2Sqr = flRadiusSqr * 4; - int nMask = MASK_SHOT; - if (F::ProjSim.m_pObj->m_dragCoefficient && F::ProjSim.m_pObj->m_angDragBasis) - nMask |= CONTENTS_DISPSOLID; + int nMask = F::ProjSim.m_bPhysics ? MASK_SHOT | CONTENTS_DISPSOLID : MASK_SHOT; - for (int i = 0; ++i < tFace.m_vVertices.size() - 1;) + std::vector vVertices, vEpsilon; + if (tFace.m_iType != FaceTypeEnum::Prop) + vVertices = tFace.m_vVertices; + else + Math::ExpandPolygon(vVertices, tFace.m_vVertices, tFace.m_vNormal, SDK::StdRandomFloat(0.f, DIST_EPSILON), &vTargetEye); + + for (int i = 0, n = int(tFace.m_vVertices.size()), o = SDK::StdRandomInt(0, n - 1); ++i < n - 1;) { - const Vec3& vVertex1 = tFace.m_vVertices[0], &vVertex2 = tFace.m_vVertices[i], &vVertex3 = tFace.m_vVertices[i + 1]; + Vec3& vVertex1 = tFace.m_vVertices[o], &vVertex2 = tFace.m_vVertices[(o + i) % n], &vVertex3 = tFace.m_vVertices[(o + i + 1) % n]; Vec3 vDir21 = vVertex2 - vVertex1, vDir31 = vVertex3 - vVertex1; float flArea = vDir21.Cross(vDir31).Length() / 2; float flSamples = flDensity * flArea / flRadius2Sqr; - int iSamples = flSamples < flCutoff ? fmodf(s_flTotal += flSamples, flCutoff) < flSamples : ceilf(flSamples); + int iSamples = flSamples > flCutoff ? ceilf(flSamples) : fmodf(s_flTotal += flSamples, flCutoff) < flSamples; + if (!iSamples) + continue; -#if defined(SPLASH_DEBUG2) && defined(WORLD_DEBUG) - if (iSamples) - F::World.DrawFace({ { vVertex1, vVertex2, vVertex3 }, tFace.m_vNormal }, DrawTypeEnum::Edges | DrawTypeEnum::Faces); + // don't particularly like the hacky random epsilons + int iFaceClosest = SDK::StdRandomInt(iSamples < 2 ? 0 : iSamples < 4 ? 1 : 2, iSamples < 2 ? 1 : 2); + //int iEdgeClosest = SDK::StdRandomInt(iSamples < 8 ? 0 : iSamples < 16 ? 1 : 2, iSamples < 8 ? 1 : 2); // eats up a bit too much performance for my liking + int iEdgeRandom = SDK::StdRandomInt(iSamples < 8 ? 0 : iSamples < 16 ? 1 : 2, iSamples < 3 ? 0 : iSamples < 12 ? 1 : 2); + iEdgeRandom += iFaceClosest; //iEdgeClosest += iFaceClosest, iEdgeRandom += iEdgeClosest; //, iSamples += iEdgeRandom; +#ifdef SPLASH_DEBUG2 + Color_t tColor = { byte(SDK::StdRandomInt(0, 255)), byte(SDK::StdRandomInt(0, 255)), byte(SDK::StdRandomInt(0, 255)) }; +#ifdef WORLD_DEBUG + F::World.DrawFace({ { vVertex1, vVertex2, vVertex3 }, tFace.m_vNormal, tFace.m_iType }, DrawTypeEnum::Edges | DrawTypeEnum::Faces, tColor); #endif - - for (int i = 0; i < iSamples; i++) +#ifdef DEBUG_TEXT + F::Visuals.AddDebugText(std::format("{}:{}:{}"/*:{}"*/, iFaceClosest, /*iEdgeClosest,*/ iEdgeRandom, iSamples), (vVertex1 + vVertex2 + vVertex3) / 3, tColor); +#endif +#endif + for (int s = 0; s < iSamples; s++) { - float flRandom1 = SDK::StdRandomFloat(), flRandom2 = SDK::StdRandomFloat(); - if (flRandom1 + flRandom2 > 1) - flRandom1 = 1 - flRandom1, flRandom2 = 1 - flRandom2; - - Vec3 vPoint = vVertex1 + vDir21 * flRandom1 + vDir31 * flRandom2 + tFace.m_vNormal * (tInfo.m_vHull + CALC_EPSILON); + Vec3 vPoint, vNormal = tFace.m_vNormal; bool bInside = true; + if (s < iFaceClosest) // closest point + { + float flEpsilon = s == 0 && (tFace.m_iType == FaceTypeEnum::BoxBrush || iFaceClosest != 1 || SDK::StdRandomBool()) ? CALC_EPSILON : DIST_EPSILON; + vPoint = Math::ClosestPointOnTriangle(vTargetOrigin, vVertex1, vVertex2, vVertex3, &bInside); + vPoint += { SDK::StdRandomFloat(-flEpsilon, flEpsilon), SDK::StdRandomFloat(-flEpsilon, flEpsilon), SDK::StdRandomFloat(-flEpsilon, flEpsilon) }; + } + //else if (s < iEdgeClosest) // closest point on edge + //{ + // float flEpsilon = SDK::StdRandomBool() ? CALC_EPSILON : DIST_EPSILON; bInside = false; + // switch (SDK::StdRandomInt(0, 2)) + // { + // case 0: vPoint = Math::ClosestPointOnLine(vTargetOrigin, vVertex1, vVertex2); break; + // case 1: vPoint = Math::ClosestPointOnLine(vTargetOrigin, vVertex2, vVertex3); break; + // case 2: vPoint = Math::ClosestPointOnLine(vTargetOrigin, vVertex3, vVertex1); break; + // } + // vPoint += { SDK::StdRandomFloat(-flEpsilon, flEpsilon), SDK::StdRandomFloat(-flEpsilon, flEpsilon), SDK::StdRandomFloat(-flEpsilon, flEpsilon) }; + //} + else if (s < iEdgeRandom) // random point on edge + { + float flEpsilon = SDK::StdRandomBool() ? CALC_EPSILON : DIST_EPSILON; bInside = false; + switch (SDK::StdRandomInt(0, 2)) + { + case 0: vPoint = vVertex1.Lerp(vVertex2, SDK::StdRandomFloat()); break; + case 1: vPoint = vVertex2.Lerp(vVertex3, SDK::StdRandomFloat()); break; + case 2: vPoint = vVertex3.Lerp(vVertex1, SDK::StdRandomFloat()); break; + } + vPoint += { SDK::StdRandomFloat(-flEpsilon, flEpsilon), SDK::StdRandomFloat(-flEpsilon, flEpsilon), SDK::StdRandomFloat(-flEpsilon, flEpsilon) }; + } + else // random point on face + { + float flRandom1 = SDK::StdRandomFloat(), flRandom2 = SDK::StdRandomFloat(); + if (flRandom1 + flRandom2 > 1) + flRandom1 = 1 - flRandom1, flRandom2 = 1 - flRandom2; + vPoint = vVertex1 + vDir21 * flRandom1 + vDir31 * flRandom2; + } + vPoint += vNormal * (tInfo.m_vHull + CALC_EPSILON); if (vPoint.DistToSqr(vTargetCenter) > flRadiusSqr) continue; + if (!bInside) + { + if (vEpsilon.empty()) + Math::ExpandPolygon(vEpsilon = vVertices, tFace.m_vNormal, -DIST_EPSILON); + Math::ClosestPointOnPolygon(vPoint, vEpsilon, vNormal, &bInside); + + if (tFace.m_iType == FaceTypeEnum::Prop && !bInside) + vPoint += tFace.m_vNormal * SDK::StdRandomFloat(0.f, DIST_EPSILON); + } + #ifdef SPLASH_DEBUG5 - s_mTraceCount[__FUNCTION__": point contents"]++; + if (bInside) s_mTraceCount[__FUNCTION__": point contents"]++; #endif - if (I::EngineTrace->GetPointContents(vPoint) & MASK_SOLID) + if (bInside && I::EngineTrace->GetPointContents(vPoint) & MASK_SOLID) continue; - if (tFace.m_iType == FaceTypeEnum::Prop) - vPoint += tFace.m_vNormal * DIST_EPSILON; + int nSubMask = nMask; + if (!bInside) + vNormal = (vTargetEye - vPoint).Normalized(), nSubMask &= ~CONTENTS_MOVEABLE; - SDK::Trace(vPoint + tFace.m_vNormal * tInfo.m_flNormalOffset, vTargetEye, nMask, &filter, &trace); + SDK::Trace(vPoint + vNormal * tInfo.m_flNormalOffset, vTargetEye, nSubMask, &filter, &trace); #ifdef SPLASH_DEBUG5 s_mTraceCount[__FUNCTION__": vispos"]++; #endif #ifdef SPLASH_DEBUG2 - DrawTrace(!trace.DidHit(), Vars::Colors::IndicatorMisc.Value, trace); + DrawTrace(trace.fraction == 1.f, Vars::Colors::IndicatorMisc.Value, trace); #endif if (trace.fraction != 1.f) continue; @@ -594,7 +664,7 @@ void CAimbotProjectile::SetupSplashPoints(Vec3& vOrigin, std::vector& v if (pAngle) { Vec3 vForward, vRight, vUp; Math::AngleVectors(*pAngle, &vForward, &vRight, &vUp); - Vec3 vShootPos = m_tInfo.m_vLocalEye + (vForward * m_tInfo.m_vOffset.x) + (vRight * m_tInfo.m_vOffset.y) + (vUp * m_tInfo.m_vOffset.z); + Vec3 vShootPos = m_tInfo.m_vLocalEye + vForward * m_tInfo.m_vOffset.x + vRight * m_tInfo.m_vOffset.y + vUp * m_tInfo.m_vOffset.z; vForward = (vShootPos - vPoint).Normalized(); return vForward.Dot(vNormal) > 0; } @@ -606,9 +676,10 @@ void CAimbotProjectile::SetupSplashPoints(Vec3& vOrigin, std::vector& v }; // Trace - int iPoints = !m_tInfo.m_flGravity ? Vars::Aimbot::Projectile::SplashPointsDirect.Value : Vars::Aimbot::Projectile::SplashPointsArc.Value; + int iPoints = Vars::Aimbot::Projectile::SplashMode.Value == Vars::Aimbot::Projectile::SplashModeEnum::Face && !bAirSplash ? 0 + : !m_tInfo.m_flGravity ? Vars::Aimbot::Projectile::SplashPointsDirect.Value : Vars::Aimbot::Projectile::SplashPointsArc.Value; { - auto vPoints = ComputePoints(flRadius, iPoints, Vars::Aimbot::Projectile::SplashMode.Value == Vars::Aimbot::Projectile::SplashModeEnum::Trace || bAirSplash); + auto vPoints = ComputePoints(flRadius, iPoints); for (int i = 0; i < vPoints.size(); i++) { @@ -626,7 +697,7 @@ void CAimbotProjectile::SetupSplashPoints(Vec3& vOrigin, std::vector& v CalculateAngle(m_tInfo.m_vLocalEye, tPoint.m_vPoint, 0, tPoint.m_tSolution, iFlags); if (tPoint.m_tSolution.m_iCalculated == CalculateResultEnum::Bad) return false; - vPoint -= Vec3(0, 0, (m_tInfo.m_flGravity * 800.f * powf(tPoint.m_tSolution.m_flTime, 2)) / 2); + vPoint -= Vec3(0, 0, m_tInfo.m_flGravity * powf(tPoint.m_tSolution.m_flTime, 2) / 2); vAngle = Vec3(tPoint.m_tSolution.m_flPitch, tPoint.m_tSolution.m_flYaw); } return fCheckNormal(trace.plane.normal, vPoint, &vAngle); @@ -655,8 +726,8 @@ void CAimbotProjectile::SetupSplashPoints(Vec3& vOrigin, std::vector& v } // Face - float flDensity = !m_tInfo.m_flGravity ? Vars::Aimbot::Projectile::SplashDensityDirect.Value : Vars::Aimbot::Projectile::SplashDensityArc.Value; - if (Vars::Aimbot::Projectile::SplashMode.Value == Vars::Aimbot::Projectile::SplashModeEnum::Face && flDensity) + if (float flDensity = !m_tInfo.m_flGravity ? Vars::Aimbot::Projectile::SplashDensityDirect.Value : Vars::Aimbot::Projectile::SplashDensityArc.Value; + Vars::Aimbot::Projectile::SplashMode.Value == Vars::Aimbot::Projectile::SplashModeEnum::Face && flDensity) { Vec3 vMins = vTargetCenter - flRadius, vMaxs = vTargetCenter + flRadius; @@ -664,14 +735,14 @@ void CAimbotProjectile::SetupSplashPoints(Vec3& vOrigin, std::vector& v { Vec3 vPoint = std::reduce(vVertices.begin(), vVertices.end()) / vVertices.size(), vAngle; if (!m_tInfo.m_flGravity) - vAngle = Math::CalcAngle(m_tInfo.m_vLocalEye, trace.endpos); + vAngle = Math::CalcAngle(m_tInfo.m_vLocalEye, vPoint); else { Point_t tPoint = { vPoint, {} }; CalculateAngle(m_tInfo.m_vLocalEye, tPoint.m_vPoint, 0, tPoint.m_tSolution, iFlags); if (tPoint.m_tSolution.m_iCalculated == CalculateResultEnum::Bad) return false; - vPoint -= Vec3(0, 0, (m_tInfo.m_flGravity * 800.f * powf(tPoint.m_tSolution.m_flTime, 2)) / 2); + vPoint -= Vec3(0, 0, m_tInfo.m_flGravity * powf(tPoint.m_tSolution.m_flTime, 2) / 2); vAngle = Vec3(tPoint.m_tSolution.m_flPitch, tPoint.m_tSolution.m_flYaw); } return fCheckNormal(vNormal, vPoint, &vAngle); @@ -686,7 +757,7 @@ void CAimbotProjectile::SetupSplashPoints(Vec3& vOrigin, std::vector& v float flCutoff = Vars::Aimbot::Projectile::SplashSamplesCutoff.Value * powf(vFaces.size(), 2); s_flTotal = SDK::StdRandomFloat(); for (auto& tFace : vFaces) { - HandleFace(tFace, vSplashPoints, flDensity, flRadius, flCutoff, vTargetEye, vTargetCenter, m_tInfo, trace, filter); + HandleFace(tFace, vSplashPoints, flDensity, flRadius, flCutoff, vTargetEye, vTargetCenter, vOrigin, m_tInfo, trace, filter); //#if defined(SPLASH_DEBUG2) && defined(WORLD_DEBUG) // F::World.DrawFace(tFace, DrawTypeEnum::Edges | DrawTypeEnum::Faces); //#endif @@ -720,7 +791,7 @@ std::vector CAimbotProjectile::GetSplashPoints(Vec3 vOrigin, std::vecto flRadiusAirSqr = powf(m_tInfo.m_flRadius * Math::RemapVal(TICKS_TO_TIME(iSimTime), flLiveTime, flLiveTime + flRampTime, flAirdetRadius, 1.f), 2); } bool bLob = iFlags & CalculateFlagsEnum::LobAngle; - int iTolerance = m_tInfo.m_bIgnoreTiming && bLob ? std::numeric_limits::max() : !m_tInfo.m_iArmTime ? 0 : -1; + int iTolerance = m_tInfo.m_bIgnoreTiming && bLob ? std::numeric_limits::max() : m_tInfo.m_iArmTime ? -1 : 0; int iLimit = !bFirst ? m_tInfo.m_iSplashRestrict : std::max(m_tInfo.m_iSplashRestrict, Vars::Aimbot::Projectile::SplashRestrictFirst.Value); bool bSort = !bLob || !m_tInfo.m_bIgnoreTiming; @@ -787,20 +858,20 @@ static inline Vec3 PullPoint(const Vec3& vPoint, Vec3 vLocalPos, Info_t& tInfo, { auto fHeightenLocalPos = [&]() { // basic trajectory pass - const float flGrav = tInfo.m_flGravity * 800.f; + float flGrav = tInfo.m_flGravity; if (!flGrav) return vPoint; - const Vec3 vDelta = vTargetPos - vLocalPos; - const float flDist = vDelta.Length2D(); + Vec3 vDelta = vTargetPos - vLocalPos; + float flDist = vDelta.Length2D(); - const float flRoot = powf(tInfo.m_flVelocity, 4) - flGrav * (flGrav * powf(flDist, 2) + 2.f * vDelta.z * powf(tInfo.m_flVelocity, 2)); + float flRoot = powf(tInfo.m_flVelocity, 4) - flGrav * (flGrav * powf(flDist, 2) + 2.f * vDelta.z * powf(tInfo.m_flVelocity, 2)); if (flRoot < 0.f) return vPoint; float flPitch = atan((powf(tInfo.m_flVelocity, 2) - sqrt(flRoot)) / (flGrav * flDist)); float flTime = flDist / (cos(flPitch) * tInfo.m_flVelocity) - tInfo.m_flOffsetTime; - return vLocalPos + Vec3(0, 0, (flGrav * powf(flTime, 2)) / 2); + return vLocalPos + Vec3(0, 0, flGrav * powf(flTime, 2) / 2); }; vLocalPos = fHeightenLocalPos(); @@ -821,7 +892,7 @@ static inline float GetDrag(float flVelocity, ProjectileInfo* pInfo, int iFlags) { auto fGetDrag = [&](std::function fGetTypeDrag) { - if (!F::ProjSim.m_pObj->m_dragCoefficient || !F::ProjSim.m_pObj->m_dragBasis) + if (!F::ProjSim.m_bPhysics) return 0.f; if (Vars::Aimbot::Projectile::DragOverride.Value) @@ -902,7 +973,7 @@ static inline float GetDrag(float flVelocity, ProjectileInfo* pInfo, int iFlags) static inline bool GetAngle(const Vec3& vLocalPos, const Vec3& vTargetPos, int iFlags, Info_t& tInfo, float& flPitch, float& flYaw, float& flTime) { float flVelocity = tInfo.m_flVelocity; - float flGrav = tInfo.m_flGravity * 800.f; + float flGrav = tInfo.m_flGravity; Vec3 vDelta = vTargetPos - vLocalPos; float flDist = vDelta.Length2D(); float flDrag = GetDrag(flVelocity, F::ProjSim.m_pCurrent, iFlags); @@ -960,7 +1031,7 @@ void CAimbotProjectile::CalculateAngle(const Vec3& vLocalPos, const Vec3& vTarge float flPitch, flYaw; { // basic trajectory pass //Vec3 vForward, vRight, vUp; Math::AngleVectors(Math::CalcAngle(vLocalPos, vTargetPos), &vForward, &vRight, &vUp); - //Vec3 vShootPos = vLocalPos + (vForward * m_tInfo.m_vOffset.x) + (vRight * m_tInfo.m_vOffset.y) + (vUp * m_tInfo.m_vOffset.z); + //Vec3 vShootPos = vLocalPos + vForward * m_tInfo.m_vOffset.x + vRight * m_tInfo.m_vOffset.y + vUp * m_tInfo.m_vOffset.z; if (!GetAngle(vLocalPos, vTargetPos, iFlags, m_tInfo, flPitch, flYaw, tOut.m_flTime)) { tOut.m_iCalculated = CalculateResultEnum::Bad; @@ -1134,7 +1205,7 @@ bool CAimbotProjectile::TestAngle(const Vec3& vPoint, const Vec3& vAngles, int i #ifdef SPLASH_DEBUG4 Vec3 vHull = m_tProjInfo.m_vHull.Max(1); - G::BoxStorage.emplace_back(vPoint, -vHull, vHull, Vec3(), I::GlobalVars->curtime + 5.f, Color_t(255, 0, 0), Color_t(0, 0, 0, 0)); + G::BoxStorage.emplace_back(vPoint, -vHull, vHull, Vec3(), I::GlobalVars->curtime + 5.f, Color_t(0, 0, 0), Color_t(0, 0, 0, 0)); #endif if (!m_tProjInfo.m_flGravity) @@ -1146,7 +1217,7 @@ bool CAimbotProjectile::TestAngle(const Vec3& vPoint, const Vec3& vAngles, int i #ifdef SPLASH_DEBUG4 G::LineStorage.emplace_back(std::pair(m_tProjInfo.m_vPos, trace.endpos), I::GlobalVars->curtime + 5.f, Color_t(0, 0, 0)); #endif - if (trace.fraction < 0.999f && trace.m_pEnt != tTarget.m_pEntity) + if (Math::FullFraction(m_tProjInfo.m_vPos, vPoint, trace) < 0.999f && trace.m_pEnt != tTarget.m_pEntity) return false; } @@ -1157,7 +1228,7 @@ bool CAimbotProjectile::TestAngle(const Vec3& vPoint, const Vec3& vAngles, int i bool bDidHit = false; Vec3 vNew = F::ProjSim.GetOrigin(); int iTimingTolerance = TIME_TO_TICKS(m_tInfo.m_flBoundsTime); - float flRadiusSqr = powf(m_tProjInfo.m_flVelocity * TICK_INTERVAL + m_tProjInfo.m_vHull.z, 2); + float flRadiusSqr = iType != PointTypeEnum::Direct ? powf(m_tProjInfo.m_flVelocity * TICK_INTERVAL + m_tProjInfo.m_vHull.z, 2) : std::numeric_limits::max(); uint8_t iTraceInterval = iFlags == PointFlagsEnum::Lob ? Vars::Aimbot::Projectile::LobTraceInterval.Value : iType != PointTypeEnum::Direct ? Vars::Aimbot::Projectile::SplashTraceInterval.Value : Vars::Aimbot::Projectile::DirectTraceInterval.Value; @@ -1192,10 +1263,9 @@ bool CAimbotProjectile::TestAngle(const Vec3& vPoint, const Vec3& vAngles, int i { case PointTypeEnum::Direct: case PointTypeEnum::Geometry: - bHit = trace.DidHit(); - break; + bHit = trace.DidHit(); break; case PointTypeEnum::Air: - bHit = trace.endpos.DistToSqr(vPoint) < flRadiusSqr || trace.DidHit(); + bHit = trace.endpos.DistToSqr(vPoint) < flRadiusSqr || trace.DidHit(); break; } if (bHit) @@ -1204,24 +1274,22 @@ bool CAimbotProjectile::TestAngle(const Vec3& vPoint, const Vec3& vAngles, int i switch (iType) { case PointTypeEnum::Direct: - bTarget = trace.m_pEnt == tTarget.m_pEntity, bValid = bTarget && iSimTime - n < iTimingTolerance; - break; + bTarget = trace.m_pEnt == tTarget.m_pEntity, bValid = bTarget && iSimTime - n < iTimingTolerance; break; case PointTypeEnum::Geometry: - bValid = trace.endpos.DistToSqr(vPoint) < flRadiusSqr; - break; + bValid = trace.endpos.DistToSqr(vPoint) < flRadiusSqr; break; case PointTypeEnum::Air: - bValid = !trace.DidHit(); - break; + bValid = !trace.DidHit(); break; } if (bValid && iType != PointTypeEnum::Direct) { CGameTrace eyeTrace = {}; - SDK::Trace(trace.endpos + m_tInfo.m_flNormalOffset * trace.plane.normal, tTarget.m_vPos + m_tInfo.m_vTargetEye, MASK_SHOT, &filter, &eyeTrace); + SDK::Trace(trace.endpos + trace.plane.normal * m_tInfo.m_flNormalOffset, tTarget.m_vPos + m_tInfo.m_vTargetEye, MASK_SHOT, &filter, &eyeTrace); bValid = eyeTrace.fraction == 1.f; #ifdef SPLASH_DEBUG5 s_mTraceCount[__FUNCTION__": splash eye trace"]++; #endif #ifdef SPLASH_DEBUG4 + //G::LineStorage.emplace_back(std::pair(trace.endpos, trace.endpos + trace.plane.normal * m_tInfo.m_flNormalOffset), I::GlobalVars->curtime + 5.f, Color_t(255, 0, 255)); G::LineStorage.emplace_back(std::pair(eyeTrace.startpos, eyeTrace.endpos), I::GlobalVars->curtime + 5.f, Color_t(255, 0, 255)); G::BoxStorage.emplace_back(trace.endpos + m_tInfo.m_flNormalOffset * trace.plane.normal, -vHull, vHull, Vec3(), I::GlobalVars->curtime + 5.f, Color_t(255, 0, 255), Color_t(0, 0, 0, 0)); #endif @@ -1230,17 +1298,8 @@ bool CAimbotProjectile::TestAngle(const Vec3& vPoint, const Vec3& vAngles, int i #ifdef SPLASH_DEBUG4 if (bValid) G::BoxStorage.emplace_back(vPoint, -vHull, vHull, Vec3(), I::GlobalVars->curtime + 5.f, Color_t(0, 255, 0), Color_t(0, 0, 0, 0)); - else if (bTarget) - G::BoxStorage.emplace_back(vPoint, -vHull, vHull, Vec3(), I::GlobalVars->curtime + 5.f, Color_t(0, 0, 255), Color_t(0, 0, 0, 0)); else - { - G::BoxStorage.emplace_back(vPoint, -vHull, vHull, Vec3(), I::GlobalVars->curtime + 5.f, Color_t(127, 0, 255), Color_t(0, 0, 0, 0)); - if (iType != PointTypeEnum::Direct) - { - G::BoxStorage.emplace_back(trace.endpos, Vec3::Get(-1), Vec3::Get(1), Vec3(), I::GlobalVars->curtime + 5.f, Color_t(0, 0, 0), Color_t(0, 0, 0, 0)); - G::BoxStorage.emplace_back(vPoint, Vec3::Get(-1), Vec3::Get(1), Vec3(), I::GlobalVars->curtime + 5.f, Color_t(255, 255, 255), Color_t(0, 0, 0, 0)); - } - } + G::BoxStorage.emplace_back(vPoint, -vHull, vHull, Vec3(), I::GlobalVars->curtime + 5.f, Color_t(255, 0, 0), Color_t(0, 0, 0, 0)); #endif if (bValid) @@ -1272,7 +1331,7 @@ bool CAimbotProjectile::TestAngle(const Vec3& vPoint, const Vec3& vAngles, int i Vec3 vOffset = tOriginal.m_vOrigin - tTarget.m_vPos; Vec3 vPos = trace.endpos + F::ProjSim.GetVelocity().Normalized() * 16 + vOffset; - float flClosest = std::numeric_limits::max(); int iClosest = -1; + float flLowestDistance = std::numeric_limits::max(); int iClosest = -1; for (int nHitbox = 0; nHitbox < pSet->numhitboxes; ++nHitbox) { auto pBox = pSet->pHitbox(nHitbox); @@ -1280,12 +1339,9 @@ bool CAimbotProjectile::TestAngle(const Vec3& vPoint, const Vec3& vAngles, int i Vec3 vCenter; Math::VectorTransform({}, aBones[pBox->bone], vCenter); - const float flDist = vPos.DistToSqr(vCenter); - if (flDist < flClosest) - { - flClosest = flDist; - iClosest = nHitbox; - } + const float flDistance = vPos.DistToSqr(vCenter); + if (flDistance < flLowestDistance) + iClosest = nHitbox, flLowestDistance = flDistance; } if (iClosest != HITBOX_HEAD) break; @@ -1300,7 +1356,6 @@ bool CAimbotProjectile::TestAngle(const Vec3& vPoint, const Vec3& vAngles, int i filter.pSkip = tTarget.m_pEntity; filter.iPlayer = PLAYER_NONE; filter.bMisc = false; - flRadiusSqr = std::numeric_limits::max(); continue; } else @@ -1329,7 +1384,7 @@ bool CAimbotProjectile::HandlePoint(const Vec3& vOrigin, int iSimTime, float flP m_tInfo.m_pTarget->m_vPos = vOrigin; int iOriginalSimTime = iSimTime; - if (m_tInfo.m_iArmTime || m_tInfo.m_bIgnoreTiming && iFlags == PointFlagsEnum::Lob) + if (m_tInfo.m_iArmTime && iType == PointTypeEnum::Geometry || m_tInfo.m_bIgnoreTiming && iFlags == PointFlagsEnum::Lob) { if (flTime > m_tProjInfo.m_flLifetime) return false; @@ -1344,7 +1399,7 @@ bool CAimbotProjectile::HandlePoint(const Vec3& vOrigin, int iSimTime, float flP bReturn = m_iResult = true; m_flTimeTo = flTime + m_tInfo.m_flLatency; } - else if (m_iResult != 1 && !m_tInfo.m_pProjectile) + else if (!m_iResult && !m_tInfo.m_pProjectile) { switch (Vars::Aimbot::General::AimType.Value) { @@ -1381,7 +1436,7 @@ bool CAimbotProjectile::HandlePoint(const Vec3& vOrigin, int iSimTime, float flP } } - return bReturn; + return bReturn && m_iResult == 1; } bool CAimbotProjectile::HandleDirect(DirectHistory_t& mDirectHistory) @@ -1487,7 +1542,7 @@ int CAimbotProjectile::CanHit(Target_t& tTarget, CTFPlayer* pLocal, CTFWeaponBas m_tInfo.m_vHull = m_tProjInfo.m_vHull.Min(3); m_tInfo.m_vOffset = m_tProjInfo.m_vPos - m_tInfo.m_vLocalEye; m_tInfo.m_vOffset.y *= -1; - m_tInfo.m_flOffsetTime = m_tInfo.m_vOffset.Length() / m_tInfo.m_flVelocity; // silly + m_tInfo.m_flOffsetTime = m_tInfo.m_vOffset.Length() / m_tInfo.m_flVelocity; m_tInfo.m_flGravity = m_tProjInfo.m_flGravity; m_tInfo.m_iSplashRestrict = !m_tInfo.m_flGravity ? Vars::Aimbot::Projectile::SplashRestrictDirect.Value : Vars::Aimbot::Projectile::SplashRestrictArc.Value; @@ -1544,7 +1599,16 @@ int CAimbotProjectile::CanHit(Target_t& tTarget, CTFPlayer* pLocal, CTFWeaponBas iFlags |= CalculateFlagsEnum::LobAngle; int iTolerance = m_tInfo.m_bIgnoreTiming && iType == PointFlagsEnum::Lob ? std::numeric_limits::max() : -1; - Solution_t tSolution; CalculateAngle(m_tInfo.m_vLocalEye, vPoint, i, tSolution, iFlags, iTolerance); + Solution_t tSolution; + switch (iType) + { + case PointFlagsEnum::Lob: + if (ShouldLob(m_tMoveStorage, m_tInfo)) + goto end; + tSolution.m_iCalculated = CalculateResultEnum::Bad; break; + default: end: + CalculateAngle(m_tInfo.m_vLocalEye, vPoint, i, tSolution, iFlags, iTolerance); + } switch (tSolution.m_iCalculated) { case CalculateResultEnum::Good: @@ -1560,7 +1624,7 @@ int CAimbotProjectile::CanHit(Target_t& tTarget, CTFPlayer* pLocal, CTFWeaponBas for (auto it = vSplashes.begin(); it != vSplashes.end();) { uint8_t iFlags = CalculateFlagsEnum::AccountDrag; - if (!m_tInfo.m_bIgnoreTiming && *it == PointFlagsEnum::Lob) + if (*it == PointFlagsEnum::Lob && !m_tInfo.m_bIgnoreTiming) iFlags |= CalculateFlagsEnum::LobAngle; Solution_t tSolution; CalculateAngle(m_tInfo.m_vLocalEye, tTarget.m_vPos, i, tSolution, iFlags); @@ -1581,6 +1645,11 @@ int CAimbotProjectile::CanHit(Target_t& tTarget, CTFPlayer* pLocal, CTFWeaponBas it = vSplashes.erase(it); continue; } + if (*it == PointFlagsEnum::Lob && !ShouldLob(m_tMoveStorage, m_tInfo)) + { + ++it; + continue; + } mSplashHistory[*it].emplace_back(History_t(tTarget.m_vPos, i), fabsf(flTimeTo)); ++it; @@ -1841,6 +1910,9 @@ bool CAimbotProjectile::RunMain(CTFPlayer* pLocal, CTFWeaponBase* pWeapon, CUser #endif #if defined(SPLASH_DEBUG2) && defined(WORLD_DEBUG) G::TriangleStorage.clear(); +#endif +#if defined(SPLASH_DEBUG2) && defined(DEBUG_TEXT) + F::Visuals.ClearDebugText(); #endif for (auto& tTarget : vTargets) { @@ -2026,7 +2098,7 @@ bool CAimbotProjectile::TestAngle(CBaseEntity* pProjectile, const Vec3& vPoint, Vec3 vPos = vPoint; if (m_tInfo.m_flGravity) - vPos += Vec3(0, 0, (m_tInfo.m_flGravity * 800.f * pow(TICKS_TO_TIME(iSimTime), 2)) / 2); + vPos += Vec3(0, 0, m_tInfo.m_flGravity * pow(TICKS_TO_TIME(iSimTime), 2) / 2); Vec3 vForward = (vPos - m_tProjInfo.m_vPos).Normalized(); m_tProjInfo.m_vAng = Math::VectorAngles(vForward); @@ -2037,7 +2109,7 @@ bool CAimbotProjectile::TestAngle(CBaseEntity* pProjectile, const Vec3& vPoint, return false; SDK::Trace(vEyePos, trace.endpos, MASK_SOLID, &filter, &trace); - if (trace.fraction < 0.999f) + if (Math::FullFraction(vEyePos, trace.endpos, trace) < 0.999f) return false; if (!F::AutoAirblast.CanAirblastEntity(pLocal, pWeapon, pProjectile, vAngles)) @@ -2055,7 +2127,7 @@ bool CAimbotProjectile::TestAngle(CBaseEntity* pProjectile, const Vec3& vPoint, if (!m_tProjInfo.m_flGravity) { SDK::TraceHull(m_tProjInfo.m_vPos, vPoint, -m_tProjInfo.m_vHull, m_tProjInfo.m_vHull, nMask, &filter, &trace); - if (trace.fraction < 0.999f && trace.m_pEnt != tTarget.m_pEntity) + if (Math::FullFraction(m_tProjInfo.m_vPos, vPoint, trace) < 0.999f && trace.m_pEnt != tTarget.m_pEntity) return false; } @@ -2091,10 +2163,9 @@ bool CAimbotProjectile::TestAngle(CBaseEntity* pProjectile, const Vec3& vPoint, { case PointTypeEnum::Direct: case PointTypeEnum::Geometry: - bHit = trace.DidHit(); - break; + bHit = trace.DidHit(); break; case PointTypeEnum::Air: - bHit = trace.endpos.DistToSqr(vPoint) < flRadiusSqr || trace.DidHit(); + bHit = trace.endpos.DistToSqr(vPoint) < flRadiusSqr || trace.DidHit(); break; } if (bHit) @@ -2103,33 +2174,17 @@ bool CAimbotProjectile::TestAngle(CBaseEntity* pProjectile, const Vec3& vPoint, switch (iType) { case PointTypeEnum::Direct: - bTarget = trace.m_pEnt == tTarget.m_pEntity, bValid = bTarget && iSimTime - n < iTimingTolerance; - break; + bTarget = trace.m_pEnt == tTarget.m_pEntity, bValid = bTarget && iSimTime - n < iTimingTolerance; break; case PointTypeEnum::Geometry: - bValid = trace.endpos.DistToSqr(vPoint) < flRadiusSqr; - break; + bValid = trace.endpos.DistToSqr(vPoint) < flRadiusSqr; break; case PointTypeEnum::Air: - bValid = !trace.DidHit(); - break; + bValid = !trace.DidHit(); break; } if (bValid && iType != PointTypeEnum::Direct) { - bValid = SDK::VisPosWorld(nullptr, tTarget.m_pEntity, trace.endpos, vPoint, nMask); - if (bValid) - { - Vec3 vFrom = trace.endpos; - switch (pProjectile->GetClassID()) - { - case ETFClassID::CTFProjectile_Rocket: - case ETFClassID::CTFProjectile_SentryRocket: - case ETFClassID::CTFProjectile_EnergyBall: - vFrom += trace.plane.normal; - } - - CGameTrace eyeTrace = {}; - SDK::Trace(vFrom, tTarget.m_vPos + tTarget.m_pEntity->As()->GetViewOffset(), MASK_SHOT, &filter, &eyeTrace); - bValid = eyeTrace.fraction == 1.f; - } + CGameTrace eyeTrace = {}; + SDK::Trace(trace.endpos + trace.plane.normal * m_tInfo.m_flNormalOffset, tTarget.m_vPos + m_tInfo.m_vTargetEye, MASK_SHOT, &filter, &eyeTrace); + bValid = eyeTrace.fraction == 1.f; } if (bValid) @@ -2244,7 +2299,16 @@ bool CAimbotProjectile::CanHit(Target_t& tTarget, CTFPlayer* pLocal, CTFWeaponBa iFlags |= CalculateFlagsEnum::LobAngle; int iTolerance = m_tInfo.m_bIgnoreTiming && iType == PointFlagsEnum::Lob ? std::numeric_limits::max() : -1; - Solution_t tSolution; CalculateAngle(m_tInfo.m_vLocalEye, vPoint, i, tSolution, iFlags, iTolerance); + Solution_t tSolution; + switch (iType) + { + case PointFlagsEnum::Lob: + if (ShouldLob(m_tMoveStorage, m_tInfo)) + goto end; + tSolution.m_iCalculated = CalculateResultEnum::Bad; break; + default: end: + CalculateAngle(m_tInfo.m_vLocalEye, vPoint, i, tSolution, iFlags, iTolerance); + } switch (tSolution.m_iCalculated) { case CalculateResultEnum::Good: @@ -2260,7 +2324,7 @@ bool CAimbotProjectile::CanHit(Target_t& tTarget, CTFPlayer* pLocal, CTFWeaponBa for (auto it = vSplashes.begin(); it != vSplashes.end();) { uint8_t iFlags = CalculateFlagsEnum::AccountDrag; - if (!m_tInfo.m_bIgnoreTiming && *it == PointFlagsEnum::Lob) + if (*it == PointFlagsEnum::Lob && !m_tInfo.m_bIgnoreTiming) iFlags |= CalculateFlagsEnum::LobAngle; Solution_t tSolution; CalculateAngle(m_tInfo.m_vLocalEye, tTarget.m_vPos, i, tSolution, iFlags); @@ -2281,6 +2345,11 @@ bool CAimbotProjectile::CanHit(Target_t& tTarget, CTFPlayer* pLocal, CTFWeaponBa it = vSplashes.erase(it); continue; } + if (*it == PointFlagsEnum::Lob && !ShouldLob(m_tMoveStorage, m_tInfo)) + { + ++it; + continue; + } mSplashHistory[*it].emplace_back(History_t(tTarget.m_vPos, i), fabsf(flTimeTo)); ++it; diff --git a/Amalgam/src/Features/Aimbot/AutoDetonate/AutoDetonate.h b/Amalgam/src/Features/Aimbot/AutoDetonate/AutoDetonate.h index 7ef2dbef..d2703a59 100644 --- a/Amalgam/src/Features/Aimbot/AutoDetonate/AutoDetonate.h +++ b/Amalgam/src/Features/Aimbot/AutoDetonate/AutoDetonate.h @@ -2,7 +2,6 @@ #include "../../../SDK/SDK.h" #include "../AimbotGlobal/AimbotGlobal.h" -#include class CAutoDetonate { diff --git a/Amalgam/src/Features/ImGui/Menu/Menu.cpp b/Amalgam/src/Features/ImGui/Menu/Menu.cpp index 83ce15e6..ca4c709a 100644 --- a/Amalgam/src/Features/ImGui/Menu/Menu.cpp +++ b/Amalgam/src/Features/ImGui/Menu/Menu.cpp @@ -3362,6 +3362,13 @@ void CMenu::MenuSettings(int iTab) { FDropdown(Vars::World::Faces, FDropdownEnum::Left); FDropdown(Vars::World::Draw, FDropdownEnum::Right); + FSlider(Vars::World::Offset, FSliderEnum::Left); + FSlider(Vars::World::Resize, FSliderEnum::Right); + FColorPicker(Vars::World::BoxBrush, FColorPickerEnum::Left); + FColorPicker(Vars::World::PlaneBrush, FColorPickerEnum::Right); + FColorPicker(Vars::World::Displacement, FColorPickerEnum::Left); + FColorPicker(Vars::World::Prop, FColorPickerEnum::Right); + FColorPicker(Vars::World::Entity, FColorPickerEnum::Left); } EndSection(); #endif #ifdef DEBUG_HOOKS diff --git a/Amalgam/src/Features/NoSpread/NoSpreadProjectile/NoSpreadProjectile.cpp b/Amalgam/src/Features/NoSpread/NoSpreadProjectile/NoSpreadProjectile.cpp index 5697d992..c9fcdbdd 100644 --- a/Amalgam/src/Features/NoSpread/NoSpreadProjectile/NoSpreadProjectile.cpp +++ b/Amalgam/src/Features/NoSpread/NoSpreadProjectile/NoSpreadProjectile.cpp @@ -39,7 +39,8 @@ void CNoSpreadProjectile::Run(CTFPlayer* pLocal, CTFWeaponBase* pWeapon, CUserCm switch (pWeapon->GetWeaponID()) { case TF_WEAPON_COMPOUND_BOW: - if (I::GlobalVars->curtime - pWeapon->As()->m_flChargeBeginTime() > TF_ARROW_MAX_CHARGE_TIME) + if (pWeapon->As()->m_flChargeBeginTime() > 0.f + && I::GlobalVars->curtime - pWeapon->As()->m_flChargeBeginTime() > TF_ARROW_MAX_CHARGE_TIME) { vAngAdd.x -= float(SDK::RandomInt()) / VALVE_RAND_MAX * 12.f - 6.f; vAngAdd.y -= float(SDK::RandomInt()) / VALVE_RAND_MAX * 12.f - 6.f; diff --git a/Amalgam/src/Features/Resolver/Resolver.h b/Amalgam/src/Features/Resolver/Resolver.h index 47db7fee..2f475d9c 100644 --- a/Amalgam/src/Features/Resolver/Resolver.h +++ b/Amalgam/src/Features/Resolver/Resolver.h @@ -1,6 +1,5 @@ #pragma once #include "../../SDK/SDK.h" -#include struct ResolveData { diff --git a/Amalgam/src/Features/Simulation/MovementSimulation/MovementSimulation.cpp b/Amalgam/src/Features/Simulation/MovementSimulation/MovementSimulation.cpp index 617e6f8e..ab9e5d4c 100644 --- a/Amalgam/src/Features/Simulation/MovementSimulation/MovementSimulation.cpp +++ b/Amalgam/src/Features/Simulation/MovementSimulation/MovementSimulation.cpp @@ -228,54 +228,13 @@ bool CMovementSimulation::Initialize(CBaseEntity* pEntity, MoveStorage& tMoveSto tMoveStorage.m_bBunnyHop = true; // setup move data - if (!SetupMoveData(tMoveStorage)) - { - tMoveStorage.m_bFailed = true; - return false; - } - - const int iStrafeSamples = tMoveStorage.m_bDirectMove - ? Vars::Aimbot::Projectile::GroundSamples.Value - : Vars::Aimbot::Projectile::AirSamples.Value; + SetupMoveData(tMoveStorage); // calculate strafe if desired - bool bCalculated = bStrafe ? StrafePrediction(tMoveStorage, iStrafeSamples) : false; - - // really hope this doesn't work like shit - if (bHitchance && bCalculated && !pPlayer->m_vecVelocity().IsZero() && Vars::Aimbot::Projectile::HitChance.Value) + if (bStrafe) { - const auto& vRecords = m_mRecords[pPlayer->entindex()]; - const auto iSamples = vRecords.size(); - - float flCurrentChance = 1.f, flAverageYaw = 0.f; - for (size_t i = 0; i < iSamples; i++) + if (!StrafePrediction(tMoveStorage, bHitchance)) { - if (vRecords.size() <= i + 2) - break; - - const auto& pRecord1 = vRecords[i], &pRecord2 = vRecords[i + 1]; - const float flYaw1 = Math::VectorAngles(pRecord1.m_vDirection).y, flYaw2 = Math::VectorAngles(pRecord2.m_vDirection).y; - const float flTime1 = pRecord1.m_flSimTime, flTime2 = pRecord2.m_flSimTime; - const int iTicks = std::max(TIME_TO_TICKS(flTime1 - flTime2), 1); - - float flYaw = Math::NormalizeAngle(flYaw1 - flYaw2) / iTicks; - flAverageYaw += flYaw; - if (tMoveStorage.m_MoveData.m_flMaxSpeed) - flYaw *= std::clamp(pRecord1.m_vVelocity.Length2D() / tMoveStorage.m_MoveData.m_flMaxSpeed, 0.f, 1.f); - - if ((i + 1) % iStrafeSamples == 0 || i == iSamples - 1) - { - flAverageYaw /= i % iStrafeSamples + 1; - if (fabsf(tMoveStorage.m_flAverageYaw - flAverageYaw) > 0.5f) - flCurrentChance -= 1.f / ((iSamples - 1) / float(iStrafeSamples) + 1); - flAverageYaw = 0.f; - } - } - - if (flCurrentChance < Vars::Aimbot::Projectile::HitChance.Value / 100) - { - SDK::Output("MovementSimulation", std::format("Hitchance ({}% < {}%)", flCurrentChance * 100, Vars::Aimbot::Projectile::HitChance.Value).c_str(), { 80, 200, 120 }, Vars::Debug::Logging.Value); - tMoveStorage.m_bFailed = true; return false; } @@ -288,11 +247,8 @@ bool CMovementSimulation::Initialize(CBaseEntity* pEntity, MoveStorage& tMoveSto return true; } -bool CMovementSimulation::SetupMoveData(MoveStorage& tMoveStorage) +void CMovementSimulation::SetupMoveData(MoveStorage& tMoveStorage) { - if (!tMoveStorage.m_pPlayer) - return false; - tMoveStorage.m_MoveData.m_bFirstRunOfFunctions = false; tMoveStorage.m_MoveData.m_bGameCodeMovedPlayer = false; tMoveStorage.m_MoveData.m_nPlayerHandle = reinterpret_cast(tMoveStorage.m_pPlayer)->GetRefEHandle(); @@ -346,8 +302,6 @@ bool CMovementSimulation::SetupMoveData(MoveStorage& tMoveStorage) tMoveStorage.m_flPredictedSimTime = tMoveStorage.m_flSimTime + tMoveStorage.m_flPredictedDelta; tMoveStorage.m_vPredictedOrigin = tMoveStorage.m_MoveData.m_vecAbsOrigin; tMoveStorage.m_bDirectMove = tMoveStorage.m_pPlayer->IsOnGround() || tMoveStorage.m_pPlayer->IsSwimming(); - - return true; } static inline float GetGravity() @@ -527,14 +481,57 @@ void CMovementSimulation::GetAverageYaw(MoveStorage& tMoveStorage, int iSamples) SDK::Output("MovementSimulation", std::format("flAverageYaw calculated to {} from {} ({}){}", flAverageYaw, iTicks, iMinimum, bLocal ? " (local)" : "").c_str(), { 100, 255, 150 }, Vars::Debug::Logging.Value); } -bool CMovementSimulation::StrafePrediction(MoveStorage& tMoveStorage, int iSamples) +bool CMovementSimulation::StrafePrediction(MoveStorage& tMoveStorage, bool bHitchance) { if (tMoveStorage.m_bDirectMove ? !(Vars::Aimbot::Projectile::StrafePrediction.Value & Vars::Aimbot::Projectile::StrafePredictionEnum::Ground) : !(Vars::Aimbot::Projectile::StrafePrediction.Value & Vars::Aimbot::Projectile::StrafePredictionEnum::Air)) - return false; + return true; + + const int iStrafeSamples = tMoveStorage.m_bDirectMove + ? Vars::Aimbot::Projectile::GroundSamples.Value + : Vars::Aimbot::Projectile::AirSamples.Value; + + GetAverageYaw(tMoveStorage, iStrafeSamples); + + // really hope this doesn't work like shit + if (bHitchance && !tMoveStorage.m_pPlayer->m_vecVelocity().IsZero() && Vars::Aimbot::Projectile::HitChance.Value) + { + const auto& vRecords = m_mRecords[tMoveStorage.m_pPlayer->entindex()]; + const auto iSamples = vRecords.size(); + + float flCurrentChance = 1.f, flAverageYaw = 0.f; + for (size_t i = 0; i < iSamples; i++) + { + if (vRecords.size() <= i + 2) + break; + + const auto& pRecord1 = vRecords[i], & pRecord2 = vRecords[i + 1]; + const float flYaw1 = Math::VectorAngles(pRecord1.m_vDirection).y, flYaw2 = Math::VectorAngles(pRecord2.m_vDirection).y; + const float flTime1 = pRecord1.m_flSimTime, flTime2 = pRecord2.m_flSimTime; + const int iTicks = std::max(TIME_TO_TICKS(flTime1 - flTime2), 1); + + float flYaw = Math::NormalizeAngle(flYaw1 - flYaw2) / iTicks; + flAverageYaw += flYaw; + if (tMoveStorage.m_MoveData.m_flMaxSpeed) + flYaw *= std::clamp(pRecord1.m_vVelocity.Length2D() / tMoveStorage.m_MoveData.m_flMaxSpeed, 0.f, 1.f); + + if ((i + 1) % iStrafeSamples == 0 || i == iSamples - 1) + { + flAverageYaw /= i % iStrafeSamples + 1; + if (fabsf(tMoveStorage.m_flAverageYaw - flAverageYaw) > 0.5f) + flCurrentChance -= 1.f / ((iSamples - 1) / float(iStrafeSamples) + 1); + flAverageYaw = 0.f; + } + } + + if (flCurrentChance < Vars::Aimbot::Projectile::HitChance.Value / 100) + { + SDK::Output("MovementSimulation", std::format("Hitchance ({}% < {}%)", flCurrentChance * 100, Vars::Aimbot::Projectile::HitChance.Value).c_str(), { 80, 200, 120 }, Vars::Debug::Logging.Value); + return false; + } + } - GetAverageYaw(tMoveStorage, iSamples); return true; } diff --git a/Amalgam/src/Features/Simulation/MovementSimulation/MovementSimulation.h b/Amalgam/src/Features/Simulation/MovementSimulation/MovementSimulation.h index 7e62d175..ad9bf13c 100644 --- a/Amalgam/src/Features/Simulation/MovementSimulation/MovementSimulation.h +++ b/Amalgam/src/Features/Simulation/MovementSimulation/MovementSimulation.h @@ -44,9 +44,9 @@ private: void Store(MoveStorage& tMoveStorage); void Reset(MoveStorage& tMoveStorage); - bool SetupMoveData(MoveStorage& tMoveStorage); + void SetupMoveData(MoveStorage& tMoveStorage); void GetAverageYaw(MoveStorage& tMoveStorage, int iSamples); - bool StrafePrediction(MoveStorage& tMoveStorage, int iSamples); + bool StrafePrediction(MoveStorage& tMoveStorage, bool bHitchance = false); void SetBounds(CTFPlayer* pPlayer); void RestoreBounds(CTFPlayer* pPlayer); diff --git a/Amalgam/src/Features/Simulation/ProjectileSimulation/ProjectileSimulation.cpp b/Amalgam/src/Features/Simulation/ProjectileSimulation/ProjectileSimulation.cpp index 36fb3804..4687a9a2 100644 --- a/Amalgam/src/Features/Simulation/ProjectileSimulation/ProjectileSimulation.cpp +++ b/Amalgam/src/Features/Simulation/ProjectileSimulation/ProjectileSimulation.cpp @@ -11,7 +11,7 @@ bool CProjectileSimulation::GetInfoMain(CTFPlayer* pPlayer, CTFWeaponBase* pWeap return false; static auto sv_gravity = H::ConVars.FindVar("sv_gravity"); - float flGravity = sv_gravity->GetFloat() / 800.f; + float flGravity = sv_gravity->GetFloat(); // vphysics projectiles affected by server start gravity bool bDucking = pPlayer->m_fFlags() & FL_DUCKING; bool bRedirect = iFlags & ProjSimEnum::Redirect; @@ -118,7 +118,7 @@ bool CProjectileSimulation::GetInfoMain(CTFPlayer* pPlayer, CTFWeaponBase* pWeap tProjInfo = { pPlayer, pWeapon, uType, vPos, vAngle, vHull, flSpeed, 0.f }; return true; } - case TF_WEAPON_GRENADELAUNCHER: // vphysics projectiles affected by server start gravity + case TF_WEAPON_GRENADELAUNCHER: case TF_WEAPON_CANNON: { bool bCannon = pWeapon->GetWeaponID() == TF_WEAPON_CANNON; @@ -133,7 +133,7 @@ bool CProjectileSimulation::GetInfoMain(CTFPlayer* pPlayer, CTFWeaponBase* pWeap ? pWeapon->As()->m_flDetonateTime() > 0.f ? pWeapon->As()->m_flDetonateTime() - I::GlobalVars->curtime : flMortar : SDK::AttribHookValue(2.f, "fuse_mult", pWeapon); flLifeTime = ceilf(flLifeTime / GRENADE_CHECK_INTERVAL) * GRENADE_CHECK_INTERVAL + GetDesync(); - tProjInfo = { pPlayer, pWeapon, uType, vPos, vAngle, vHull, flSpeed, 1.f, flLifeTime }; + tProjInfo = { pPlayer, pWeapon, uType, vPos, vAngle, vHull, flSpeed, DEFAULT_GRAVITY, flLifeTime }; return true; } case TF_WEAPON_PIPEBOMBLAUNCHER: @@ -145,7 +145,7 @@ bool CProjectileSimulation::GetInfoMain(CTFPlayer* pPlayer, CTFWeaponBase* pWeap ? SDK::AttribHookValue(4.f, "stickybomb_charge_rate", pWeapon) * flAutoCharge : (pWeapon->As()->m_flChargeBeginTime() > 0.f ? I::GlobalVars->curtime - pWeapon->As()->m_flChargeBeginTime() : 0.f); float flSpeed = SDK::AttribHookValue(bMaxSpeed ? 2400.f : Math::RemapVal(flCharge, 0.f, SDK::AttribHookValue(4.f, "stickybomb_charge_rate", pWeapon), 900.f, 2400.f), "mult_projectile_range", pWeapon); - tProjInfo = { pPlayer, pWeapon, uType, vPos, vAngle, { 4.f, 4.f, 4.f }, flSpeed, 1.f }; + tProjInfo = { pPlayer, pWeapon, uType, vPos, vAngle, { 5.f, 5.f, 5.f }, flSpeed, DEFAULT_GRAVITY }; return true; } case TF_WEAPON_FLAREGUN: @@ -238,7 +238,7 @@ bool CProjectileSimulation::GetInfoMain(CTFPlayer* pPlayer, CTFWeaponBase* pWeap SDK::GetProjectileFireSetup(pPlayer, vAngles, { 16.f, 8.f, -6.f }, vPos, vAngle, 0.f, 0.f, bInterp); auto uType = FNV1A::Hash32Const("models/workshop_partner/weapons/c_models/c_sd_cleaver/c_sd_cleaver.mdl"); - tProjInfo = { pPlayer, pWeapon, uType, vPos, vAngle, { 1.f, 1.f, 10.f } /*weird, probably still inaccurate*/, 3000.f, 1.f, 2.2f }; + tProjInfo = { pPlayer, pWeapon, uType, vPos, vAngle, { 1.f, 1.f, 10.f } /*weird, probably still inaccurate*/, 3000.f, DEFAULT_GRAVITY, 2.2f }; return true; } case TF_WEAPON_BAT_WOOD: @@ -252,7 +252,7 @@ bool CProjectileSimulation::GetInfoMain(CTFPlayer* pPlayer, CTFWeaponBase* pWeap auto uType = bWrapAssassin ? FNV1A::Hash32Const("models/weapons/c_models/c_xms_festive_ornament.mdl") : FNV1A::Hash32Const("models/weapons/w_models/w_baseball.mdl"); Vec3 vForward; Math::AngleVectors(vAngle, &vForward); vPos = (bInterp ? pPlayer->GetAbsOrigin() : pPlayer->m_vecOrigin()) + (Vec3(0, 0, 50) + vForward * 32.f) * pPlayer->m_flModelScale(); // why? - tProjInfo = { pPlayer, pWeapon, uType, vPos, vAngle, { 3.f, 3.f, 3.f }, tf_scout_stunball_base_speed->GetFloat(), 1.f, bWrapAssassin ? 2.3f : 100.f }; + tProjInfo = { pPlayer, pWeapon, uType, vPos, vAngle, { 3.f, 3.f, 3.f }, tf_scout_stunball_base_speed->GetFloat(), DEFAULT_GRAVITY, bWrapAssassin ? 2.3f : 100.f }; return true; } case TF_WEAPON_JAR: @@ -267,7 +267,7 @@ bool CProjectileSimulation::GetInfoMain(CTFPlayer* pPlayer, CTFWeaponBase* pWeap case Sniper_s_TheSelfAwareBeautyMark: uType = FNV1A::Hash32Const("models/weapons/c_models/c_breadmonster/c_breadmonster.mdl"); break; case Scout_s_MutatedMilk: uType = FNV1A::Hash32Const("models/weapons/c_models/c_breadmonster/c_breadmonster_milk.mdl"); break; } - tProjInfo = { pPlayer, pWeapon, uType, vPos, vAngle, { 3.f, 3.f, 3.f }, 1000.f, 1.f, 2.2f }; + tProjInfo = { pPlayer, pWeapon, uType, vPos, vAngle, { 3.f, 3.f, 3.f }, 1000.f, DEFAULT_GRAVITY, 2.2f }; return true; } case TF_WEAPON_JAR_GAS: @@ -275,7 +275,7 @@ bool CProjectileSimulation::GetInfoMain(CTFPlayer* pPlayer, CTFWeaponBase* pWeap SDK::GetProjectileFireSetup(pPlayer, vAngles, { 16.f, 8.f, -6.f }, vPos, vAngle, 0.f, 0.f, bInterp); auto uType = FNV1A::Hash32Const("models/weapons/c_models/c_gascan/c_gascan.mdl"); - tProjInfo = { pPlayer, pWeapon, uType, vPos, vAngle, { 3.f, 3.f, 3.f }, 2000.f, 1.f, 2.2f }; + tProjInfo = { pPlayer, pWeapon, uType, vPos, vAngle, { 3.f, 3.f, 3.f }, 2000.f, DEFAULT_GRAVITY, 2.2f }; return true; } case TF_WEAPON_GRAPPLINGHOOK: @@ -325,7 +325,7 @@ bool CProjectileSimulation::GetInfoMain(CTFPlayer* pPlayer, CTFWeaponBase* pWeap return false; } -bool CProjectileSimulation::GetInfo(CTFPlayer* pPlayer, CTFWeaponBase* pWeapon, Vec3 vAngles, ProjectileInfo& tProjInfo, int iFlags, float flAutoCharge) +bool CProjectileSimulation::GetInfo(CTFPlayer* pPlayer, CTFWeaponBase* pWeapon, const Vec3& vAngles, ProjectileInfo& tProjInfo, int iFlags, float flAutoCharge) { bool bInitCheck = iFlags & ProjSimEnum::InitCheck; bool bInterp = iFlags & ProjSimEnum::Interp; @@ -479,6 +479,7 @@ bool CProjectileSimulation::Initialize(ProjectileInfo& tProjInfo, bool bSimulate m_pObj->m_dragBasis = vDragBasis; m_pObj->m_angDragBasis = vAngDragBasis; + m_bPhysics = m_pObj->m_dragCoefficient && m_pObj->m_dragBasis; } // set position and velocity @@ -586,11 +587,19 @@ bool CProjectileSimulation::Initialize(ProjectileInfo& tProjInfo, bool bSimulate } } - if (bSimulate && !F::ProjSim.m_pObj->IsDragEnabled() && m_pObj->m_dragBasis.IsZero()) // don't include vphysics projectiles - vVelocity.z += 400.f * tProjInfo.m_flGravity * TICK_INTERVAL; // i don't know why this makes it more accurate but it does + if (bSimulate && !m_bPhysics) + vVelocity.z += tProjInfo.m_flGravity * TICK_INTERVAL / 2; - m_pObj->SetPosition(tProjInfo.m_vPos, tProjInfo.m_vAng, true); - m_pObj->SetVelocity(&vVelocity, &vAngularVelocity); + if (m_bPhysics) + { + m_pObj->SetPosition(tProjInfo.m_vPos, tProjInfo.m_vAng, true); + m_pObj->SetVelocity(&vVelocity, &vAngularVelocity); + } + else + { + m_tObj.m_vOrigin = tProjInfo.m_vPos; + m_tObj.m_vVelocity = vVelocity; + } } // set m_pEnv params @@ -624,7 +633,7 @@ bool CProjectileSimulation::Initialize(ProjectileInfo& tProjInfo, bool bSimulate m_pEnv->SetPerformanceSettings(¶ms); m_pEnv->SetAirDensity(AIR_DENSITY); - m_pEnv->SetGravity({ 0.f, 0.f, -(800.f * tProjInfo.m_flGravity) }); + m_pEnv->SetGravity({ 0.f, 0.f, -tProjInfo.m_flGravity }); m_pEnv->ResetSimulationClock(); } @@ -632,7 +641,8 @@ bool CProjectileSimulation::Initialize(ProjectileInfo& tProjInfo, bool bSimulate if (tProjInfo.m_uType == FNV1A::Hash32Const("custom")) tProjInfo.m_uType = FNV1A::Hash32(Vars::Visuals::Trajectory::Type.Value.c_str()); - RunTick(tProjInfo, false); // simulate an initial time because dumb + if (m_bPhysics) + RunTick(tProjInfo, false); // simulate an initial time because dumb return true; } @@ -642,15 +652,27 @@ void CProjectileSimulation::RunTick(ProjectileInfo& tProjInfo, bool bPath) // bu if (bPath) tProjInfo.m_vPath.push_back(GetOrigin()); - m_pEnv->Simulate(TICK_INTERVAL); + if (m_bPhysics) + m_pEnv->Simulate(TICK_INTERVAL); + else + { + Vec3& vOrigin = m_tObj.m_vOrigin; + Vec3& vVelocity = m_tObj.m_vVelocity; + + if (tProjInfo.m_flGravity) + vVelocity.z -= tProjInfo.m_flGravity * TICK_INTERVAL; + + vOrigin += vVelocity * TICK_INTERVAL; + } /* // params.maxVelocity limits velocity uniformly - Vec3 vVelocity, vAngular; - m_pObj->GetVelocity(&vVelocity, &vAngular); static auto sv_maxvelocity = H::ConVars.FindVar("sv_maxvelocity"); - const float flMaxVel = sv_maxvelocity->GetFloat(); - vVelocity = { std::clamp(vVelocity.x, -flMaxVel, flMaxVel), std::clamp(vVelocity.y, -flMaxVel, flMaxVel), std::clamp(vVelocity.z, -flMaxVel, flMaxVel) }; - m_pObj->SetVelocity(&vVelocity, &vAngular); + + Vec3 vVelocity; + m_pObj->GetVelocity(&vVelocity, nullptr); + float flMaxVel = sv_maxvelocity->GetFloat(); + vVelocity = vVelocity.Clamp(-flMaxVel, flMaxVel); + m_pObj->SetVelocity(&vVelocity, nullptr); */ } @@ -659,8 +681,10 @@ Vec3 CProjectileSimulation::GetOrigin() if (!m_pObj) return {}; - Vec3 vOut; - m_pObj->GetPosition(&vOut, nullptr); + if (!m_bPhysics) + return m_tObj.m_vOrigin; + + Vec3 vOut; m_pObj->GetPosition(&vOut, nullptr); return vOut; } @@ -669,8 +693,10 @@ Vec3 CProjectileSimulation::GetVelocity() if (!m_pObj) return {}; - Vec3 vOut; - m_pObj->GetVelocity(&vOut, nullptr); + if (!m_bPhysics) + return m_tObj.m_vVelocity; + + Vec3 vOut; m_pObj->GetVelocity(&vOut, nullptr); return vOut; } diff --git a/Amalgam/src/Features/Simulation/ProjectileSimulation/ProjectileSimulation.h b/Amalgam/src/Features/Simulation/ProjectileSimulation/ProjectileSimulation.h index e06fca8c..14579b44 100644 --- a/Amalgam/src/Features/Simulation/ProjectileSimulation/ProjectileSimulation.h +++ b/Amalgam/src/Features/Simulation/ProjectileSimulation/ProjectileSimulation.h @@ -12,6 +12,7 @@ Enum(ProjSim, CorrectRandomAngles = 1 << 6 // or do, position matters ) +#define DEFAULT_GRAVITY 800.f #define GRENADE_CHECK_INTERVAL 0.195f struct ProjectileInfo @@ -30,7 +31,13 @@ struct ProjectileInfo std::vector m_vPath = {}; - int m_iFlags = 0; + uint8_t m_iFlags = 0; +}; + +struct PhysicsObject_t +{ + Vec3 m_vOrigin = {}; + Vec3 m_vVelocity = {}; }; class CProjectileSimulation @@ -53,7 +60,7 @@ private: }; public: - bool GetInfo(CTFPlayer* pPlayer, CTFWeaponBase* pWeapon, Vec3 vAngles, ProjectileInfo& tProjInfo, int iFlags = ProjSimEnum::Redirect | ProjSimEnum::InitCheck, float flAutoCharge = -1.f); + bool GetInfo(CTFPlayer* pPlayer, CTFWeaponBase* pWeapon, const Vec3& vAngles, ProjectileInfo& tProjInfo, int iFlags = ProjSimEnum::Redirect | ProjSimEnum::InitCheck, float flAutoCharge = -1.f); void SetupTrace(CTraceFilterCollideable& filter, int& nMask, CTFWeaponBase* pWeapon, int nTick = 0, bool bInterp = false); void GetInfo(CBaseEntity* pProjectile, ProjectileInfo& tProjInfo); @@ -165,7 +172,7 @@ public: float flReturn = 0.f; static auto sv_gravity = H::ConVars.FindVar("sv_gravity"); - float flGravity = sv_gravity->GetFloat() / 800.f; + float flGravity = sv_gravity->GetFloat(); switch (pProjectile->GetClassID()) { case ETFClassID::CBaseGrenade: @@ -191,7 +198,7 @@ public: case ETFClassID::CTFProjectile_ThrowableBreadMonster: case ETFClassID::CTFProjectile_ThrowableBrick: case ETFClassID::CTFProjectile_ThrowableRepel: - flReturn = 1.f; + flReturn = DEFAULT_GRAVITY; break; case ETFClassID::CTFProjectile_HealingBolt: flReturn = 0.2f * flGravity; @@ -209,7 +216,11 @@ public: } IPhysicsEnvironment* m_pEnv = nullptr; + IPhysicsObject* m_pObj = nullptr; + PhysicsObject_t m_tObj = {}; + bool m_bPhysics = false; + ProjectileInfo* m_pCurrent = nullptr; }; diff --git a/Amalgam/src/Features/Visuals/Visuals.cpp b/Amalgam/src/Features/Visuals/Visuals.cpp index 44660283..67f61b23 100644 --- a/Amalgam/src/Features/Visuals/Visuals.cpp +++ b/Amalgam/src/Features/Visuals/Visuals.cpp @@ -374,7 +374,7 @@ void CVisuals::DrawDebugInfo(CTFPlayer* pLocal) Vec3 vOrigin = pLocal->m_vecOrigin(); H::Draw.StringOutlined(fFont, x, y += nTall * 2, Vars::Menu::Theme::Active.Value, Vars::Menu::Theme::Background.Value, ALIGN_TOPLEFT, std::format("Origin: ({:.3f}, {:.3f}, {:.3f})", vOrigin.x, vOrigin.y, vOrigin.z).c_str()); Vec3 vVelocity = pLocal->m_vecVelocity(); - H::Draw.StringOutlined(fFont, x, y += nTall, Vars::Menu::Theme::Active.Value, Vars::Menu::Theme::Background.Value, ALIGN_TOPLEFT, std::format("Velocity: {:.3f} ({:.3f}, {:.3f}, {:.3f})", vVelocity.Length(), vVelocity.x, vVelocity.y, vVelocity.z).c_str()); + H::Draw.StringOutlined(fFont, x, y += nTall, Vars::Menu::Theme::Active.Value, Vars::Menu::Theme::Background.Value, ALIGN_TOPLEFT, std::format("Velocity: {:.3f}, {:.3f} ({:.3f}, {:.3f}, {:.3f})", vVelocity.Length(), vVelocity.Length2D(), vVelocity.x, vVelocity.y, vVelocity.z).c_str()); H::Draw.StringOutlined(fFont, x, y += nTall, Vars::Menu::Theme::Active.Value, Vars::Menu::Theme::Background.Value, ALIGN_TOPLEFT, std::format("Tickbase: {}", pLocal->m_nTickBase()).c_str()); //H::Draw.StringOutlined(fFont, x, y += nTall, Vars::Menu::Theme::Active.Value, Vars::Menu::Theme::Background.Value, ALIGN_TOPLEFT, std::format("Choke: {}, {}", G::Choking, I::ClientState->chokedcommands).c_str()); //H::Draw.StringOutlined(fFont, x, y += nTall, Vars::Menu::Theme::Active.Value, Vars::Menu::Theme::Background.Value, ALIGN_TOPLEFT, std::format("Ticks: {}, {}", F::Ticks.m_iShiftedTicks, F::Ticks.m_iShiftedGoal).c_str()); @@ -406,16 +406,41 @@ void CVisuals::DrawDebugInfo(CTFPlayer* pLocal) { if (Vars::Debug::Info.Value) y += nTall; - for (auto& [sString, tColor] : m_vDebugText) + for (auto& [sString, tColor, vPosition2D, vPosition3D] : m_vDebugText) + { + if (vPosition3D) + { + if (Vec3 vScreen; SDK::W2S(*vPosition3D, vScreen)) + H::Draw.StringOutlined(fFont, vScreen.x, vScreen.y, tColor, Vars::Menu::Theme::Background.Value, ALIGN_TOPLEFT, sString.c_str()); + } + else if (vPosition2D) + H::Draw.StringOutlined(fFont, vPosition2D->x, vPosition2D->y, tColor, Vars::Menu::Theme::Background.Value, ALIGN_TOPLEFT, sString.c_str()); + else H::Draw.StringOutlined(fFont, x, y += nTall, tColor, Vars::Menu::Theme::Background.Value, ALIGN_TOPLEFT, sString.c_str()); } + } #endif } #ifdef DEBUG_TEXT +void CVisuals::AddDebugText(const DebugText_t& sText) +{ + m_vDebugText.push_back(sText); +} + void CVisuals::AddDebugText(const std::string& sString, Color_t tColor) { - m_vDebugText.emplace_back(sString, tColor); + m_vDebugText.emplace_back(sString, tColor, std::nullopt, std::nullopt); +} + +void CVisuals::AddDebugText(const std::string& sString, const Vec2& vPosition, Color_t tColor) +{ + m_vDebugText.emplace_back(sString, tColor, vPosition, std::nullopt); +} + +void CVisuals::AddDebugText(const std::string& sString, const Vec3& vPosition, Color_t tColor) +{ + m_vDebugText.emplace_back(sString, tColor, std::nullopt, vPosition); } void CVisuals::ClearDebugText() @@ -863,7 +888,6 @@ void CVisuals::Store() if (!F::Groups.GetGroup(pEntity, pGroup, false) || !(pGroup->m_iTrajectory & TrajectoryEnum::Enabled)) continue; - bool bContains = m_mProjectiles.contains(pEntity); Projectile_t& tProjectile = m_mProjectiles[pEntity]; mProjectiles[pEntity]; @@ -947,7 +971,7 @@ void CVisuals::Store() } } - for (auto& [pEntity, tProjectile] : m_mProjectiles) + for (auto& pEntity : m_mProjectiles | std::views::keys) { if (!mProjectiles.contains(pEntity)) m_mProjectiles.erase(pEntity); @@ -1212,11 +1236,17 @@ void CVisuals::CreateMove(CTFPlayer* pLocal, CTFWeaponBase* pWeapon) { for (auto& tFace : vFaces) { + if (Vars::World::Offset.Value) + Math::OffsetPolygon(tFace.m_vVertices, tFace.m_vNormal, Vars::World::Offset.Value); + if (Vars::World::Resize.Value) + Math::ExpandPolygon(tFace.m_vVertices, tFace.m_vNormal, Vars::World::Resize.Value); + F::World.DrawFace(tFace, Vars::World::Draw.Value); + /* for (int i = 0; ++i < tFace.m_vVertices.size() - 1;) { - const Vec3& vVertex1 = tFace.m_vVertices[0], & vVertex2 = tFace.m_vVertices[i], & vVertex3 = tFace.m_vVertices[i + 1]; + const Vec3& vVertex1 = tFace.m_vVertices[0], &vVertex2 = tFace.m_vVertices[i], &vVertex3 = tFace.m_vVertices[i + 1]; Vec3 vDir21 = vVertex2 - vVertex1, vDir31 = vVertex3 - vVertex1; float flArea = vDir21.Cross(vDir31).Length() / 2; diff --git a/Amalgam/src/Features/Visuals/Visuals.h b/Amalgam/src/Features/Visuals/Visuals.h index a3955b96..9f0bd3f9 100644 --- a/Amalgam/src/Features/Visuals/Visuals.h +++ b/Amalgam/src/Features/Visuals/Visuals.h @@ -1,8 +1,6 @@ #pragma once #include "../../SDK/SDK.h" -#include - //#define DEBUG_TEXT struct Projectile_t @@ -30,6 +28,16 @@ struct PickupData_t Vec3 m_vLocation; }; +#ifdef DEBUG_TEXT +struct DebugText_t +{ + std::string m_sText = ""; + Color_t m_tColor = {}; + std::optional vPosition2D = std::nullopt; + std::optional vPosition3D = std::nullopt; +}; +#endif + class CVisuals { private: @@ -38,7 +46,7 @@ private: std::vector m_vPickups = {}; #ifdef DEBUG_TEXT - std::vector> m_vDebugText = {}; + std::vector m_vDebugText = {}; #endif public: @@ -52,7 +60,10 @@ public: void DrawDebugInfo(CTFPlayer* pLocal); #ifdef DEBUG_TEXT + void AddDebugText(const DebugText_t& sText); void AddDebugText(const std::string& sString, Color_t tColor = Vars::Menu::Theme::Active.Value); + void AddDebugText(const std::string& sString, const Vec2& vPosition, Color_t tColor = Vars::Menu::Theme::Active.Value); + void AddDebugText(const std::string& sString, const Vec3& vPosition, Color_t tColor = Vars::Menu::Theme::Active.Value); void ClearDebugText(); #endif diff --git a/Amalgam/src/Features/World/World.cpp b/Amalgam/src/Features/World/World.cpp index c43fedfd..ce0eb077 100644 --- a/Amalgam/src/Features/World/World.cpp +++ b/Amalgam/src/Features/World/World.cpp @@ -125,7 +125,7 @@ static inline void GetFacesForConvex(CPhysConvex* pConvex, std::vector& if (CPhysConvex* pConvex2 = I::PhysicsCollision->ConvexFromPlanes(reinterpret_cast(vPlanes.data()), int(vPlanes.size()), 0.f)) { - GetFacesForConvex(pConvex2, vFaces); + GetFacesForConvex(pConvex2, vFaces, 0.f, iType); I::PhysicsCollision->ConvexFree(pConvex2); } } @@ -265,11 +265,14 @@ void CWorld::CacheProps() m_iNeedsCache &= ~FaceTypeEnum::Prop; - CUtlVector vProps; I::StaticPropMgr->GetAllStaticProps(&vProps); - for (int i = 0; i < vProps.Count(); i++) + for (int i = I::MDLCache->m_MDLDict.First(); i != I::MDLCache->m_MDLDict.InvalidIndex(); i = I::MDLCache->m_MDLDict.Next(i)) { - vcollide_t* pCollide = I::ModelInfoClient->GetVCollide(vProps[i]->GetCollisionModel()); - if (!pCollide || m_mFaceCache.contains(pCollide)) + studiodata_t* pStudioData = I::MDLCache->m_MDLDict[i]; + if (!pStudioData) + continue; + + vcollide_t* pCollide = &pStudioData->m_VCollisionData; + if (m_mFaceCache.contains(pCollide)) continue; for (int iSolid = 0; iSolid < pCollide->solidCount; iSolid++) @@ -474,7 +477,7 @@ std::vector CWorld::GetFacesInAABB(const Vec3& vMins, const Vec3& vMaxs, vcollide_t* pCollide = I::ModelInfoClient->GetVCollide(pCollideable->GetCollisionModel()); if (!m_mFaceCache.contains(pCollide)) - continue; + continue; // cache as well? auto& vCache = m_mFaceCache[pCollide]; for (auto& tFace : vCache) @@ -503,8 +506,21 @@ std::vector CWorld::GetFacesInAABB(const Vec3& vMins, const Vec3& vMaxs, } #ifdef WORLD_DEBUG -void CWorld::DrawFace(const Face_t& tFace, int iFlags) +void CWorld::DrawFace(const Face_t& tFace, int iFlags, std::optional tColor) { + if (!tColor) + { + switch (tFace.m_iType) + { + case FaceTypeEnum::BoxBrush: tColor = Vars::World::BoxBrush.Value; break; + case FaceTypeEnum::PlaneBrush: tColor = Vars::World::PlaneBrush.Value; break; + case FaceTypeEnum::Displacement: tColor = Vars::World::Displacement.Value; break; + case FaceTypeEnum::Prop: tColor = Vars::World::Prop.Value; break; + case FaceTypeEnum::Entity: tColor = Vars::World::Entity.Value; break; + default: tColor = { 255, 255, 255 }; break; + } + } + if (iFlags & (DrawTypeEnum::Points | DrawTypeEnum::Edges)) { for (int i = 0; i < tFace.m_vVertices.size(); i++) @@ -512,9 +528,9 @@ void CWorld::DrawFace(const Face_t& tFace, int iFlags) const Vec3& vVertex1 = tFace.m_vVertices[i], &vVertex2 = tFace.m_vVertices[(i + 1) % tFace.m_vVertices.size()]; if (iFlags & DrawTypeEnum::Points) - G::BoxStorage.emplace_back(vVertex1, Vec3::Get(-1), Vec3::Get(1), Vec3(0, 0, 0), I::GlobalVars->curtime + 60.f, Color_t(255, 0, 0), Color_t(0, 0, 0, 0), true); + G::BoxStorage.emplace_back(vVertex1, Vec3::Get(-1), Vec3::Get(1), Vec3(0, 0, 0), I::GlobalVars->curtime + 60.f, *tColor, Color_t(0, 0, 0, 0), true); if (iFlags & DrawTypeEnum::Edges) - G::LineStorage.emplace_back(std::pair(vVertex1, vVertex2), I::GlobalVars->curtime + 60.f, Color_t(255, 0, 0), true); + G::LineStorage.emplace_back(std::pair(vVertex1, vVertex2), I::GlobalVars->curtime + 60.f, *tColor, true); } } @@ -523,14 +539,14 @@ void CWorld::DrawFace(const Face_t& tFace, int iFlags) for (int i = 0; ++i < tFace.m_vVertices.size() - 1;) { const Vec3& vVertex1 = tFace.m_vVertices[0], &vVertex2 = tFace.m_vVertices[i], &vVertex3 = tFace.m_vVertices[i + 1]; - G::TriangleStorage.emplace_back(std::array({ vVertex1, vVertex2, vVertex3 }), I::GlobalVars->curtime + 60.f, Color_t(255, 0, 0, 50), true); + G::TriangleStorage.emplace_back(std::array({ vVertex1, vVertex2, vVertex3 }), I::GlobalVars->curtime + 60.f, tColor->Alpha(50), true); } } if (iFlags & DrawTypeEnum::Normals) { - G::LineStorage.emplace_back(std::pair(tFace.m_vVertices[0], tFace.m_vVertices[0] + tFace.m_vNormal * 10), I::GlobalVars->curtime + 60.f, Color_t(255, 255, 0), true); - G::BoxStorage.emplace_back(tFace.m_vVertices[0] + tFace.m_vNormal * 10, Vec3::Get(-1), Vec3::Get(1), Vec3(), I::GlobalVars->curtime + 60.f, Color_t(255, 255, 0), Color_t(0, 0, 0, 0), true); + G::LineStorage.emplace_back(std::pair(tFace.m_vVertices[0], tFace.m_vVertices[0] + tFace.m_vNormal * 10), I::GlobalVars->curtime + 60.f, tColor->Inverse(), true); + G::BoxStorage.emplace_back(tFace.m_vVertices[0] + tFace.m_vNormal * 10, Vec3::Get(-1), Vec3::Get(1), Vec3(), I::GlobalVars->curtime + 60.f, tColor->Inverse(), Color_t(0, 0, 0, 0), true); } } #endif \ No newline at end of file diff --git a/Amalgam/src/Features/World/World.h b/Amalgam/src/Features/World/World.h index 1e08dce8..22d358dd 100644 --- a/Amalgam/src/Features/World/World.h +++ b/Amalgam/src/Features/World/World.h @@ -2,9 +2,6 @@ #include "../../SDK/SDK.h" #include -#define DIST_EPSILON 0.03125f -#define CALC_EPSILON 0.001f - //#define WORLD_DEBUG Enum(FaceType, None = 0, BoxBrush = 1 << 0, PlaneBrush = 1 << 1, Displacement = 1 << 2, Prop = 1 << 3, Entity = 1 << 4, All = BoxBrush | PlaneBrush | Displacement | Prop | Entity, Cache = BoxBrush | PlaneBrush | Prop | Entity) @@ -17,6 +14,15 @@ namespace Vars { "BoxBrush", "PlaneBrush", "Displacement", "Prop", "Entity"); CVarValues(Draw, "Draw", 0b0110, NOSAVE | DEBUGVAR | DROPDOWN_MULTI, nullptr, "Points", "Edges", "Faces", "Normals"); + + CVar(Offset, "Offset", 0.f, NOSAVE | DEBUGVAR | SLIDER_MIN | SLIDER_PRECISION, 0.f, 1.f, DIST_EPSILON); + CVar(Resize, "Resize", 0.f, NOSAVE | DEBUGVAR | SLIDER_MIN | SLIDER_PRECISION, 0.f, 1.f, DIST_EPSILON); + + CVar(BoxBrush, "BoxBrush", Color_t(255, 0, 0, 255), NOSAVE | DEBUGVAR); + CVar(PlaneBrush, "PlaneBrush", Color_t(0, 255, 0, 255), NOSAVE | DEBUGVAR); + CVar(Displacement, "Displacement", Color_t(0, 0, 255, 255), NOSAVE | DEBUGVAR); + CVar(Prop, "Prop", Color_t(255, 0, 255, 255), NOSAVE | DEBUGVAR); + CVar(Entity, "Entity", Color_t(255, 255, 0, 255), NOSAVE | DEBUGVAR); NAMESPACE_END(World) } #endif @@ -93,7 +99,7 @@ public: void SetNormalValidCallback(NormalValidCallback* pCallback = nullptr); #ifdef WORLD_DEBUG - void DrawFace(const Face_t& tFace, int iFlags = DrawTypeEnum::Points | DrawTypeEnum::Edges | DrawTypeEnum::Faces | DrawTypeEnum::Normals); + void DrawFace(const Face_t& tFace, int iFlags = DrawTypeEnum::Points | DrawTypeEnum::Edges | DrawTypeEnum::Faces | DrawTypeEnum::Normals, std::optional tColor = std::nullopt); #endif }; diff --git a/Amalgam/src/Hooks/CClientModeShared_DoPostScreenSpaceEffects.cpp b/Amalgam/src/Hooks/CClientModeShared_DoPostScreenSpaceEffects.cpp index bea7e422..5c29da03 100644 --- a/Amalgam/src/Hooks/CClientModeShared_DoPostScreenSpaceEffects.cpp +++ b/Amalgam/src/Hooks/CClientModeShared_DoPostScreenSpaceEffects.cpp @@ -14,7 +14,7 @@ MAKE_HOOK(CClientModeShared_DoPostScreenSpaceEffects, U::Memory.GetVirtual(I::Cl { DEBUG_RETURN(CClientModeShared_DoPostScreenSpaceEffects, rcx, pSetup); - if (SDK::CleanScreenshot()) + if (SDK::CleanScreenshot() || G::Unload) return CALL_ORIGINAL(rcx, pSetup); F::Visuals.ProjectileTrace(H::Entities.GetLocal(), H::Entities.GetWeapon()); diff --git a/Amalgam/src/Hooks/CM_TraceToDispTree.cpp b/Amalgam/src/Hooks/CM_TraceToDispTree.cpp index 1fb05885..2035d5ef 100644 --- a/Amalgam/src/Hooks/CM_TraceToDispTree.cpp +++ b/Amalgam/src/Hooks/CM_TraceToDispTree.cpp @@ -1,7 +1,6 @@ #include "../SDK/SDK.h" #include "../SDK/Definitions/Misc/TraceInfo.h" -#include MAKE_SIGNATURE(CM_TraceToDispTree_True, "engine.dll", "48 8B C4 48 89 58 ? 48 89 70 ? 57 48 81 EC ? ? ? ? F3 0F 10 01", 0x0); MAKE_SIGNATURE(CM_TraceToDispTree_False, "engine.dll", "48 8B C4 48 89 58 ? 48 89 70 ? 57 48 83 EC ? F3 0F 10 01", 0x0); diff --git a/Amalgam/src/Hooks/CModelLoader_Map_LoadModel.cpp b/Amalgam/src/Hooks/CModelLoader_Map_LoadModel.cpp index a4da3105..c8ba6e88 100644 --- a/Amalgam/src/Hooks/CModelLoader_Map_LoadModel.cpp +++ b/Amalgam/src/Hooks/CModelLoader_Map_LoadModel.cpp @@ -3,7 +3,7 @@ #include "../Features/World/World.h" MAKE_SIGNATURE(CModelLoader_Map_LoadModel, "engine.dll", "48 8B C4 48 89 58 ? 48 89 50 ? 48 89 48 ? 55 56 57 41 54 41 55 41 56 41 57 48 8D A8 ? ? ? ? 48 81 EC ? ? ? ? FF 05", 0x0); -MAKE_SIGNATURE(CStaticPropMgr_LevelInit, "engine.dll", "40 57 48 81 EC ? ? ? ? 80 B9", 0x0); +MAKE_SIGNATURE(R_LevelInit, "engine.dll", "48 83 EC ? 48 8D 0D ? ? ? ? FF 15 ? ? ? ? 48 8D 0D ? ? ? ? FF 15", 0x0); MAKE_SIGNATURE(CM_FreeMap, "engine.dll", "48 8D 0D ? ? ? ? E9 ? ? ? ? CC CC CC CC 48 89 5C 24 ? 57 48 83 EC ? 45 33 C9", 0x0); MAKE_HOOK(CModelLoader_Map_LoadModel, S::CModelLoader_Map_LoadModel(), void, @@ -18,12 +18,12 @@ MAKE_HOOK(CModelLoader_Map_LoadModel, S::CModelLoader_Map_LoadModel(), void, F::World.CacheEntities(); } -MAKE_HOOK(CStaticPropMgr_LevelInit, S::CStaticPropMgr_LevelInit(), void, - void* rcx) +MAKE_HOOK(R_LevelInit, S::R_LevelInit(), void, + ) { - DEBUG_RETURN(CStaticPropMgr_LevelInit, rcx); + DEBUG_RETURN(R_LevelInit); - CALL_ORIGINAL(rcx); + CALL_ORIGINAL(); F::World.CacheProps(); } diff --git a/Amalgam/src/SDK/Definitions/Interfaces.h b/Amalgam/src/SDK/Definitions/Interfaces.h index bbf52629..a67fed09 100644 --- a/Amalgam/src/SDK/Definitions/Interfaces.h +++ b/Amalgam/src/SDK/Definitions/Interfaces.h @@ -9,6 +9,7 @@ #include "Interfaces/CGlobalVarsBase.h" #include "Interfaces/CHLClient.h" #include "Interfaces/CHLTVCamera.h" +#include "Interfaces/CMDLCache.h" #include "Interfaces/CStaticPropMgr.h" #include "Interfaces/CTFGameRules.h" #include "Interfaces/CTFGCClientSystem.h" diff --git a/Amalgam/src/SDK/Definitions/Interfaces/CMDLCache.h b/Amalgam/src/SDK/Definitions/Interfaces/CMDLCache.h new file mode 100644 index 00000000..cffa0d06 --- /dev/null +++ b/Amalgam/src/SDK/Definitions/Interfaces/CMDLCache.h @@ -0,0 +1,118 @@ +#pragma once +#include "../Misc/IMDLCache.h" +#include "../Misc/IStudioDataCache.h" +#include "../Misc/CDefaultDataCacheClient.h" +#include "../Misc/VCollide.h" +#include "../Misc/Studio.h" +#include "../Misc/CUtlDict.h" +#include "../Misc/CUtlLinkedList.h" + +class IDataCacheSection; +typedef void* DataCacheHandle_t; + +struct studiodata_t +{ + DataCacheHandle_t m_MDLCache; + vcollide_t m_VCollisionData; + studiohwdata_t m_HardwareData; +#if defined( USE_HARDWARE_CACHE ) + DataCacheHandle_t m_HardwareDataCache; +#endif + unsigned short m_nFlags; + short m_nRefCount; + virtualmodel_t* m_pVirtualModel; + int m_nAnimBlockCount; + DataCacheHandle_t* m_pAnimBlock; + unsigned long* m_iFakeAnimBlockStall; + DataCacheHandle_t m_VertexCache; + bool m_VertexDataIsCompressed; + int m_nAutoplaySequenceCount; + unsigned short* m_pAutoplaySequenceList; + void* m_pUserData; +}; + +struct AsyncInfo_t +{ + FSAsyncControl_t hControl; + MDLHandle_t hModel; + MDLCacheDataType_t type; + int iAnimBlock; +}; + +class CThreadFastMutex +{ +public: + volatile uint32 m_ownerID; + int m_depth; +}; + +class CMDLCache : public CTier3AppSystem, public IStudioDataCache, public CDefaultDataCacheClient +{ + typedef CTier3AppSystem BaseClass; + +public: + virtual bool Connect(CreateInterfaceFn factory) = 0; + virtual void Disconnect() = 0; + virtual void* QueryInterface(const char* pInterfaceName) = 0; + virtual InitReturnVal_t Init() = 0; + virtual void Shutdown() = 0; + virtual MDLHandle_t FindMDL(const char* pMDLRelativePath) = 0; + virtual int AddRef(MDLHandle_t handle) = 0; + virtual int Release(MDLHandle_t handle) = 0; + virtual int GetRef(MDLHandle_t handle) = 0; + virtual void MarkAsLoaded(MDLHandle_t handle) = 0; + virtual studiohdr_t* GetStudioHdr(MDLHandle_t handle) = 0; + virtual studiohwdata_t* GetHardwareData(MDLHandle_t handle) = 0; + virtual vcollide_t* GetVCollide(MDLHandle_t handle) = 0; + virtual vcollide_t* GetVCollideEx(MDLHandle_t handle, bool synchronousLoad = true) = 0; + virtual unsigned char* GetAnimBlock(MDLHandle_t handle, int nBlock) = 0; + virtual virtualmodel_t* GetVirtualModel(MDLHandle_t handle) = 0; + virtual virtualmodel_t* GetVirtualModelFast(const studiohdr_t* pStudioHdr, MDLHandle_t handle) = 0; + virtual int GetAutoplayList(MDLHandle_t handle, unsigned short** pOut) = 0; + virtual void TouchAllData(MDLHandle_t handle) = 0; + virtual void SetUserData(MDLHandle_t handle, void* pData) = 0; + virtual void* GetUserData(MDLHandle_t handle) = 0; + virtual bool IsErrorModel(MDLHandle_t handle) = 0; + virtual void SetCacheNotify(IMDLCacheNotify* pNotify) = 0; + virtual vertexFileHeader_t* GetVertexData(MDLHandle_t handle) = 0; + virtual void Flush(MDLCacheFlush_t nFlushFlags = MDLCACHE_FLUSH_ALL) = 0; + virtual void Flush(MDLHandle_t handle, int nFlushFlags = MDLCACHE_FLUSH_ALL) = 0; + virtual const char* GetModelName(MDLHandle_t handle) = 0; + virtual void BeginLock() = 0; + virtual void EndLock() = 0; + virtual int* GetFrameUnlockCounterPtrOLD() = 0; + virtual int* GetFrameUnlockCounterPtr(MDLCacheDataType_t type) = 0; + virtual void FinishPendingLoads() = 0; + virtual bool GetVCollideSize(MDLHandle_t handle, int* pVCollideSize) = 0; + virtual void BeginMapLoad() = 0; + virtual void EndMapLoad() = 0; + virtual void InitPreloadData(bool rebuild) = 0; + virtual void ShutdownPreloadData() = 0; + virtual bool IsDataLoaded(MDLHandle_t handle, MDLCacheDataType_t type) = 0; + virtual studiohdr_t* LockStudioHdr(MDLHandle_t handle) = 0; + virtual void UnlockStudioHdr(MDLHandle_t handle) = 0; + virtual bool PreloadModel(MDLHandle_t handle) = 0; + virtual void ResetErrorModelStatus(MDLHandle_t handle) = 0; + virtual void MarkFrame() = 0; + virtual bool HandleCacheNotification(const DataCacheNotification_t& notification) = 0; + virtual bool GetItemName(DataCacheClientID_t clientId, const void* pItem, char* pDest, unsigned nMaxLen) = 0; + virtual bool GetAsyncLoad(MDLCacheDataType_t type) = 0; + virtual bool SetAsyncLoad(MDLCacheDataType_t type, bool bAsync) = 0; + +public: + IDataCacheSection* m_pModelCacheSection; + IDataCacheSection* m_pMeshCacheSection; + IDataCacheSection* m_pAnimBlockCacheSection; + int m_nModelCacheFrameLocks; + int m_nMeshCacheFrameLocks; + CUtlDict m_MDLDict; + IMDLCacheNotify* m_pCacheNotify; + CUtlFixedLinkedList m_PendingAsyncs; + CThreadFastMutex m_QueuedLoadingMutex; + CThreadFastMutex m_AsyncMutex; + bool m_bLostVideoMemory : 1; + bool m_bConnected : 1; + bool m_bInitialized : 1; +}; + +MAKE_INTERFACE_VERSION(CMDLCache, MDLCache, "datacache.dll", "MDLCache004"); \ No newline at end of file diff --git a/Amalgam/src/SDK/Definitions/Interfaces/IStudioRender.h b/Amalgam/src/SDK/Definitions/Interfaces/IStudioRender.h index 6f66621e..9ac045ab 100644 --- a/Amalgam/src/SDK/Definitions/Interfaces/IStudioRender.h +++ b/Amalgam/src/SDK/Definitions/Interfaces/IStudioRender.h @@ -16,7 +16,6 @@ struct studiomeshdata_t; struct LightDesc_t; struct studiohwdata_t; struct Ray_t; -struct vertexFileHeader_t; struct FlashlightState_t; struct FileHeader_t; typedef unsigned short MDLHandle_t; @@ -175,13 +174,6 @@ struct model_array_instance_t matrix3x4 modelToWorld; }; -class IStudioDataCache : public IAppSystem -{ -public: - virtual bool VerifyHeaders(studiohdr_t* pStudioHdr) = 0; - virtual vertexFileHeader_t* CacheVertexData(studiohdr_t* pStudioHdr) = 0; -}; - class IStudioRender : public IAppSystem { public: diff --git a/Amalgam/src/SDK/Definitions/Misc/CDefaultDataCacheClient.h b/Amalgam/src/SDK/Definitions/Misc/CDefaultDataCacheClient.h new file mode 100644 index 00000000..2e19098e --- /dev/null +++ b/Amalgam/src/SDK/Definitions/Misc/CDefaultDataCacheClient.h @@ -0,0 +1,55 @@ +#pragma once +#include "BaseTypes.h" + +typedef uint32 DataCacheClientID_t; + +enum DataCacheNotificationType_t +{ + DC_NONE, + DC_AGE_DISCARD, + DC_FLUSH_DISCARD, + DC_REMOVED, + DC_RELOCATE, + DC_PRINT_INF0, +}; + +struct DataCacheNotification_t +{ + DataCacheNotificationType_t type; + const char* pszSectionName; + DataCacheClientID_t clientId; + const void* pItemData; + unsigned nItemSize; +}; + +class IDataCacheClient +{ +public: + virtual bool HandleCacheNotification(const DataCacheNotification_t& notification) = 0; + virtual bool GetItemName(DataCacheClientID_t clientId, const void* pItem, char* pDest, unsigned nMaxLen) = 0; +}; + +//------------------------------------- + +class CDefaultDataCacheClient : public IDataCacheClient +{ +public: + virtual bool HandleCacheNotification(const DataCacheNotification_t& notification) + { + switch (notification.type) + { + case DC_AGE_DISCARD: + case DC_FLUSH_DISCARD: + case DC_REMOVED: + default: + //Assert(0); + return false; + } + return false; + } + + virtual bool GetItemName(DataCacheClientID_t clientId, const void* pItem, char* pDest, unsigned nMaxLen) + { + return false; + } +}; \ No newline at end of file diff --git a/Amalgam/src/SDK/Definitions/Misc/CUtlBlockMemory.h b/Amalgam/src/SDK/Definitions/Misc/CUtlBlockMemory.h new file mode 100644 index 00000000..cdd19931 --- /dev/null +++ b/Amalgam/src/SDK/Definitions/Misc/CUtlBlockMemory.h @@ -0,0 +1,332 @@ +#pragma once + +#pragma warning (push) +#pragma warning (disable:4100) +#pragma warning (disable:4514) + +#ifdef UTBLOCKLMEMORY_TRACK +#define UTLBLOCKMEMORY_TRACK_ALLOC() MemAlloc_RegisterAllocation( "Sum of all UtlBlockMemory", 0, NumAllocated() * sizeof(T), NumAllocated() * sizeof(T), 0 ) +#define UTLBLOCKMEMORY_TRACK_FREE() if ( !m_pMemory ) ; else MemAlloc_RegisterDeallocation( "Sum of all UtlBlockMemory", 0, NumAllocated() * sizeof(T), NumAllocated() * sizeof(T), 0 ) +#else +#define UTLBLOCKMEMORY_TRACK_ALLOC() ((void)0) +#define UTLBLOCKMEMORY_TRACK_FREE() ((void)0) +#endif + +__forceinline uint SmallestPowerOfTwoGreaterOrEqual(uint x) +{ + x -= 1; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + return x + 1; +} + +template< class T, class I > +class CUtlBlockMemory +{ +public: + // constructor, destructor + CUtlBlockMemory(int nGrowSize = 0, int nInitSize = 0); + ~CUtlBlockMemory(); + + // Set the size by which the memory grows - round up to the next power of 2 + void Init(int nGrowSize = 0, int nInitSize = 0); + + // here to match CUtlMemory, but only used by ResetDbgInfo, so it can just return NULL + T* Base() { return NULL; } + const T* Base() const { return NULL; } + + class Iterator_t + { + public: + Iterator_t(I i) : index(i) {} + I index; + + bool operator==(const Iterator_t it) const { return index == it.index; } + bool operator!=(const Iterator_t it) const { return index != it.index; } + }; + Iterator_t First() const { return Iterator_t(IsIdxValid(0) ? 0 : InvalidIndex()); } + Iterator_t Next(const Iterator_t& it) const { return Iterator_t(IsIdxValid(it.index + 1) ? it.index + 1 : InvalidIndex()); } + I GetIndex(const Iterator_t& it) const { return it.index; } + bool IsIdxAfter(I i, const Iterator_t& it) const { return i > it.index; } + bool IsValidIterator(const Iterator_t& it) const { return IsIdxValid(it.index); } + Iterator_t InvalidIterator() const { return Iterator_t(InvalidIndex()); } + + // element access + T& operator[](I i); + const T& operator[](I i) const; + T& Element(I i); + const T& Element(I i) const; + + // Can we use this index? + bool IsIdxValid(I i) const; + static I InvalidIndex() { return (I)-1; } + + void Swap(CUtlBlockMemory< T, I >& mem); + + // Size + int NumAllocated() const; + int Count() const { return NumAllocated(); } + + // Grows memory by max(num,growsize) rounded up to the next power of 2, and returns the allocation index/ptr + void Grow(int num = 1); + + // Makes sure we've got at least this much memory + void EnsureCapacity(int num); + + // Memory deallocation + void Purge(); + + // Purge all but the given number of elements + void Purge(int numElements); + +protected: + int Index(int major, int minor) const { return (major << m_nIndexShift) | minor; } + int MajorIndex(int i) const { return i >> m_nIndexShift; } + int MinorIndex(int i) const { return i & m_nIndexMask; } + void ChangeSize(int nBlocks); + int NumElementsInBlock() const { return m_nIndexMask + 1; } + + T** m_pMemory; + int m_nBlocks; + int m_nIndexMask : 27; + int m_nIndexShift : 5; +}; + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- + +template< class T, class I > +CUtlBlockMemory::CUtlBlockMemory(int nGrowSize, int nInitAllocationCount) + : m_pMemory(0), m_nBlocks(0), m_nIndexMask(0), m_nIndexShift(0) +{ + Init(nGrowSize, nInitAllocationCount); +} + +template< class T, class I > +CUtlBlockMemory::~CUtlBlockMemory() +{ + Purge(); +} + + +//----------------------------------------------------------------------------- +// Fast swap +//----------------------------------------------------------------------------- +template< class T, class I > +void CUtlBlockMemory::Swap(CUtlBlockMemory< T, I >& mem) +{ + std::swap(m_pMemory, mem.m_pMemory); + std::swap(m_nBlocks, mem.m_nBlocks); + std::swap(m_nIndexMask, mem.m_nIndexMask); + std::swap(m_nIndexShift, mem.m_nIndexShift); +} + + +//----------------------------------------------------------------------------- +// Set the size by which the memory grows - round up to the next power of 2 +//----------------------------------------------------------------------------- +template< class T, class I > +void CUtlBlockMemory::Init(int nGrowSize /* = 0 */, int nInitSize /* = 0 */) +{ + Purge(); + + if (nGrowSize == 0) + { + // default grow size is smallest size s.t. c++ allocation overhead is ~6% of block size + nGrowSize = (127 + sizeof(T)) / sizeof(T); + } + nGrowSize = SmallestPowerOfTwoGreaterOrEqual(nGrowSize); + m_nIndexMask = nGrowSize - 1; + + m_nIndexShift = 0; + while (nGrowSize > 1) + { + nGrowSize >>= 1; + ++m_nIndexShift; + } + //Assert(m_nIndexMask + 1 == (1 << m_nIndexShift)); + + Grow(nInitSize); +} + + +//----------------------------------------------------------------------------- +// element access +//----------------------------------------------------------------------------- +template< class T, class I > +inline T& CUtlBlockMemory::operator[](I i) +{ + //Assert(IsIdxValid(i)); + T* pBlock = m_pMemory[MajorIndex(i)]; + return pBlock[MinorIndex(i)]; +} + +template< class T, class I > +inline const T& CUtlBlockMemory::operator[](I i) const +{ + //Assert(IsIdxValid(i)); + const T* pBlock = m_pMemory[MajorIndex(i)]; + return pBlock[MinorIndex(i)]; +} + +template< class T, class I > +inline T& CUtlBlockMemory::Element(I i) +{ + //Assert(IsIdxValid(i)); + T* pBlock = m_pMemory[MajorIndex(i)]; + return pBlock[MinorIndex(i)]; +} + +template< class T, class I > +inline const T& CUtlBlockMemory::Element(I i) const +{ + //Assert(IsIdxValid(i)); + const T* pBlock = m_pMemory[MajorIndex(i)]; + return pBlock[MinorIndex(i)]; +} + + +//----------------------------------------------------------------------------- +// Size +//----------------------------------------------------------------------------- +template< class T, class I > +inline int CUtlBlockMemory::NumAllocated() const +{ + return m_nBlocks * NumElementsInBlock(); +} + + +//----------------------------------------------------------------------------- +// Is element index valid? +//----------------------------------------------------------------------------- +template< class T, class I > +inline bool CUtlBlockMemory::IsIdxValid(I i) const +{ + return (i >= 0) && (MajorIndex(i) < m_nBlocks); +} + +template< class T, class I > +void CUtlBlockMemory::Grow(int num) +{ + if (num <= 0) + return; + + int nBlockSize = NumElementsInBlock(); + int nBlocks = (num + nBlockSize - 1) / nBlockSize; + + ChangeSize(m_nBlocks + nBlocks); +} + +template< class T, class I > +void CUtlBlockMemory::ChangeSize(int nBlocks) +{ + UTLBLOCKMEMORY_TRACK_FREE(); // this must stay before the recalculation of m_nBlocks, since it implicitly uses the old value + + int nBlocksOld = m_nBlocks; + m_nBlocks = nBlocks; + + UTLBLOCKMEMORY_TRACK_ALLOC(); // this must stay after the recalculation of m_nBlocks, since it implicitly uses the new value + + if (m_pMemory) + { + // free old blocks if shrinking + // Only possible if m_pMemory is non-NULL (and avoids PVS-Studio warning) + for (int i = m_nBlocks; i < nBlocksOld; ++i) + { + UTLBLOCKMEMORY_TRACK_FREE(); + free((void*)m_pMemory[i]); + } + + MEM_ALLOC_CREDIT_CLASS(); + m_pMemory = (T**)realloc(m_pMemory, m_nBlocks * sizeof(T*)); + //Assert(m_pMemory); + } + else + { + MEM_ALLOC_CREDIT_CLASS(); + m_pMemory = (T**)malloc(m_nBlocks * sizeof(T*)); + //Assert(m_pMemory); + } + + if (!m_pMemory) + { + //Error("CUtlBlockMemory overflow!\n"); + } + + // allocate new blocks if growing + int nBlockSize = NumElementsInBlock(); + for (int i = nBlocksOld; i < m_nBlocks; ++i) + { + MEM_ALLOC_CREDIT_CLASS(); + m_pMemory[i] = (T*)malloc(nBlockSize * sizeof(T)); + //Assert(m_pMemory[i]); + } +} + + +//----------------------------------------------------------------------------- +// Makes sure we've got at least this much memory +//----------------------------------------------------------------------------- +template< class T, class I > +inline void CUtlBlockMemory::EnsureCapacity(int num) +{ + Grow(num - NumAllocated()); +} + + +//----------------------------------------------------------------------------- +// Memory deallocation +//----------------------------------------------------------------------------- +template< class T, class I > +void CUtlBlockMemory::Purge() +{ + if (!m_pMemory) + return; + + for (int i = 0; i < m_nBlocks; ++i) + { + UTLBLOCKMEMORY_TRACK_FREE(); + free((void*)m_pMemory[i]); + } + m_nBlocks = 0; + + UTLBLOCKMEMORY_TRACK_FREE(); + free((void*)m_pMemory); + m_pMemory = 0; +} + +template< class T, class I > +void CUtlBlockMemory::Purge(int numElements) +{ + //Assert(numElements >= 0); + + int nAllocated = NumAllocated(); + if (numElements > nAllocated) + { + // Ensure this isn't a grow request in disguise. + //Assert(numElements <= nAllocated); + return; + } + + if (numElements <= 0) + { + Purge(); + return; + } + + int nBlockSize = NumElementsInBlock(); + int nBlocksOld = m_nBlocks; + int nBlocks = (numElements + nBlockSize - 1) / nBlockSize; + + // If the number of blocks is the same as the allocated number of blocks, we are done. + if (nBlocks == m_nBlocks) + return; + + ChangeSize(nBlocks); +} + +#pragma warning (pop) \ No newline at end of file diff --git a/Amalgam/src/SDK/Definitions/Misc/CUtlDict.h b/Amalgam/src/SDK/Definitions/Misc/CUtlDict.h new file mode 100644 index 00000000..bda4095c --- /dev/null +++ b/Amalgam/src/SDK/Definitions/Misc/CUtlDict.h @@ -0,0 +1,327 @@ +#pragma once +#include "CUtlMap.h" + +enum EDictCompareType +{ + k_eDictCompareTypeCaseSensitive = 0, + k_eDictCompareTypeCaseInsensitive = 1, + k_eDictCompareTypeFilenames +}; + +#define FOR_EACH_DICT( dictName, iteratorName ) \ + for( int iteratorName=dictName.First(); iteratorName != dictName.InvalidIndex(); iteratorName = dictName.Next( iteratorName ) ) + +#define FOR_EACH_DICT_FAST( dictName, iteratorName ) \ + for ( int iteratorName = 0; iteratorName < dictName.MaxElement(); ++iteratorName ) if ( !dictName.IsValidIndex( iteratorName ) ) continue; else + +template +class CUtlDict +{ +public: + // constructor, destructor + // Left at growSize = 0, the memory will first allocate 1 element and double in size + // at each increment. + CUtlDict(int compareType = k_eDictCompareTypeCaseInsensitive, int growSize = 0, int initSize = 0); + ~CUtlDict(); + + void EnsureCapacity(int); + + // gets particular elements + T& Element(I i); + const T& Element(I i) const; + T& operator[](I i); + const T& operator[](I i) const; + + // gets element names + char* GetElementName(I i); + char const* GetElementName(I i) const; + + void SetElementName(I i, char const* pName); + + // Number of elements + unsigned int Count() const; + + // Number of allocated slots + I MaxElement() const; + + // Checks if a node is valid and in the tree + bool IsValidIndex(I i) const; + + // Invalid index + static I InvalidIndex(); + + // Insert method (inserts in order) + I Insert(const char* pName, const T& element); + I Insert(const char* pName); + + // Find method + I Find(const char* pName) const; + bool HasElement(const char* pName) const; + + // Remove methods + void RemoveAt(I i); + void Remove(const char* pName); + void RemoveAll(); + + // Purge memory + void Purge(); + void PurgeAndDeleteElements(); // Call delete on each element. + + // Iteration methods + I First() const; + I Next(I i) const; + + // Nested typedefs, for code that might need + // to fish out the index type from a given dict + typedef I IndexType_t; + +protected: + typedef CUtlMap DictElementMap_t; + DictElementMap_t m_Elements; +}; + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +template +CUtlDict::CUtlDict(int compareType, int growSize, int initSize) : m_Elements(growSize, initSize) +{ + if (compareType == k_eDictCompareTypeFilenames) + { + m_Elements.SetLessFunc(CaselessStringLessThanIgnoreSlashes); + } + else if (compareType == k_eDictCompareTypeCaseInsensitive) + { + m_Elements.SetLessFunc(CaselessStringLessThan); + } + else + { + m_Elements.SetLessFunc(StringLessThan); + } +} + +template +CUtlDict::~CUtlDict() +{ + Purge(); +} + +template +inline void CUtlDict::EnsureCapacity(int num) +{ + return m_Elements.EnsureCapacity(num); +} + +//----------------------------------------------------------------------------- +// gets particular elements +//----------------------------------------------------------------------------- +template +inline T& CUtlDict::Element(I i) +{ + return m_Elements[i]; +} + +template +inline const T& CUtlDict::Element(I i) const +{ + return m_Elements[i]; +} + +//----------------------------------------------------------------------------- +// gets element names +//----------------------------------------------------------------------------- +template +inline char* CUtlDict::GetElementName(I i) +{ + return (char*)m_Elements.Key(i); +} + +template +inline char const* CUtlDict::GetElementName(I i) const +{ + return m_Elements.Key(i); +} + +template +inline T& CUtlDict::operator[](I i) +{ + return Element(i); +} + +template +inline const T& CUtlDict::operator[](I i) const +{ + return Element(i); +} + +template +inline void CUtlDict::SetElementName(I i, char const* pName) +{ + MEM_ALLOC_CREDIT_CLASS(); + // TODO: This makes a copy of the old element + // TODO: This relies on the rb tree putting the most recently + // removed element at the head of the insert list + free((void*)m_Elements.Key(i)); + m_Elements.Reinsert(strdup(pName), i); +} + +//----------------------------------------------------------------------------- +// Num elements +//----------------------------------------------------------------------------- +template +inline unsigned int CUtlDict::Count() const +{ + return m_Elements.Count(); +} + +//----------------------------------------------------------------------------- +// Number of allocated slots +//----------------------------------------------------------------------------- +template +inline I CUtlDict::MaxElement() const +{ + return m_Elements.MaxElement(); +} + +//----------------------------------------------------------------------------- +// Checks if a node is valid and in the tree +//----------------------------------------------------------------------------- +template +inline bool CUtlDict::IsValidIndex(I i) const +{ + return m_Elements.IsValidIndex(i); +} + + +//----------------------------------------------------------------------------- +// Invalid index +//----------------------------------------------------------------------------- +template +inline I CUtlDict::InvalidIndex() +{ + return DictElementMap_t::InvalidIndex(); +} + + +//----------------------------------------------------------------------------- +// Delete a node from the tree +//----------------------------------------------------------------------------- +template +void CUtlDict::RemoveAt(I elem) +{ + free((void*)m_Elements.Key(elem)); + m_Elements.RemoveAt(elem); +} + + +//----------------------------------------------------------------------------- +// remove a node in the tree +//----------------------------------------------------------------------------- +template void CUtlDict::Remove(const char* search) +{ + I node = Find(search); + if (node != InvalidIndex()) + { + RemoveAt(node); + } +} + + +//----------------------------------------------------------------------------- +// Removes all nodes from the tree +//----------------------------------------------------------------------------- +template +void CUtlDict::RemoveAll() +{ + typename DictElementMap_t::IndexType_t index = m_Elements.FirstInorder(); + while (index != m_Elements.InvalidIndex()) + { + free((void*)m_Elements.Key(index)); + index = m_Elements.NextInorder(index); + } + + m_Elements.RemoveAll(); +} + +template +void CUtlDict::Purge() +{ + RemoveAll(); +} + + +template +void CUtlDict::PurgeAndDeleteElements() +{ + // Delete all the elements. + I index = m_Elements.FirstInorder(); + while (index != m_Elements.InvalidIndex()) + { + free((void*)m_Elements.Key(index)); + delete m_Elements[index]; + index = m_Elements.NextInorder(index); + } + + m_Elements.RemoveAll(); +} + + +//----------------------------------------------------------------------------- +// inserts a node into the tree +//----------------------------------------------------------------------------- +template +I CUtlDict::Insert(const char* pName, const T& element) +{ + MEM_ALLOC_CREDIT_CLASS(); + return m_Elements.Insert(strdup(pName), element); +} + +template +I CUtlDict::Insert(const char* pName) +{ + MEM_ALLOC_CREDIT_CLASS(); + return m_Elements.Insert(strdup(pName)); +} + + +//----------------------------------------------------------------------------- +// finds a node in the tree +//----------------------------------------------------------------------------- +template +I CUtlDict::Find(const char* pName) const +{ + MEM_ALLOC_CREDIT_CLASS(); + if (pName) + return m_Elements.Find(pName); + else + return InvalidIndex(); +} + +//----------------------------------------------------------------------------- +// returns true if we already have this node +//----------------------------------------------------------------------------- +template +bool CUtlDict::HasElement(const char* pName) const +{ + if (pName) + return m_Elements.IsValidIndex(m_Elements.Find(pName)); + else + return false; +} + + +//----------------------------------------------------------------------------- +// Iteration methods +//----------------------------------------------------------------------------- +template +I CUtlDict::First() const +{ + return m_Elements.FirstInorder(); +} + +template +I CUtlDict::Next(I i) const +{ + return m_Elements.NextInorder(i); +} \ No newline at end of file diff --git a/Amalgam/src/SDK/Definitions/Misc/CUtlFixedMemory.h b/Amalgam/src/SDK/Definitions/Misc/CUtlFixedMemory.h new file mode 100644 index 00000000..23705821 --- /dev/null +++ b/Amalgam/src/SDK/Definitions/Misc/CUtlFixedMemory.h @@ -0,0 +1,327 @@ +#pragma once +#include "../Steam/SteamTypes.h" + +#pragma warning (push) +#pragma warning (disable:4100) +#pragma warning (disable:4514) + +#ifdef UTLFIXEDMEMORY_TRACK +#define UTLFIXEDMEMORY_TRACK_ALLOC() MemAlloc_RegisterAllocation( "Sum of all UtlFixedMemory", 0, NumAllocated() * sizeof(T), NumAllocated() * sizeof(T), 0 ) +#define UTLFIXEDMEMORY_TRACK_FREE() if ( !m_pMemory ) ; else MemAlloc_RegisterDeallocation( "Sum of all UtlFixedMemory", 0, NumAllocated() * sizeof(T), NumAllocated() * sizeof(T), 0 ) +#else +#define UTLFIXEDMEMORY_TRACK_ALLOC() ((void)0) +#define UTLFIXEDMEMORY_TRACK_FREE() ((void)0) +#endif + +template< class T > +class CUtlFixedMemory +{ +public: + // constructor, destructor + CUtlFixedMemory(int nGrowSize = 0, int nInitSize = 0); + ~CUtlFixedMemory(); + + // Set the size by which the memory grows + void Init(int nGrowSize = 0, int nInitSize = 0); + + // here to match CUtlMemory, but only used by ResetDbgInfo, so it can just return NULL + T* Base() { return NULL; } + const T* Base() const { return NULL; } + +protected: + struct BlockHeader_t; + +public: + class Iterator_t + { + public: + Iterator_t(BlockHeader_t* p, int i) : m_pBlockHeader(p), m_nIndex(i) {} + BlockHeader_t* m_pBlockHeader; + intp m_nIndex; + + bool operator==(const Iterator_t it) const { return m_pBlockHeader == it.m_pBlockHeader && m_nIndex == it.m_nIndex; } + bool operator!=(const Iterator_t it) const { return m_pBlockHeader != it.m_pBlockHeader || m_nIndex != it.m_nIndex; } + }; + Iterator_t First() const { return m_pBlocks ? Iterator_t(m_pBlocks, 0) : InvalidIterator(); } + Iterator_t Next(const Iterator_t& it) const + { + //Assert(IsValidIterator(it)); + if (!IsValidIterator(it)) + return InvalidIterator(); + + BlockHeader_t* RESTRICT pHeader = it.m_pBlockHeader; + if (it.m_nIndex + 1 < pHeader->m_nBlockSize) + return Iterator_t(pHeader, it.m_nIndex + 1); + + return pHeader->m_pNext ? Iterator_t(pHeader->m_pNext, 0) : InvalidIterator(); + } + intp GetIndex(const Iterator_t& it) const + { + //Assert(IsValidIterator(it)); + if (!IsValidIterator(it)) + return InvalidIndex(); + + return (intp)(HeaderToBlock(it.m_pBlockHeader) + it.m_nIndex); + } + bool IsIdxAfter(intp i, const Iterator_t& it) const + { + //Assert(IsValidIterator(it)); + if (!IsValidIterator(it)) + return false; + + if (IsInBlock(i, it.m_pBlockHeader)) + return i > GetIndex(it); + + for (BlockHeader_t* RESTRICT pbh = it.m_pBlockHeader->m_pNext; pbh; pbh = pbh->m_pNext) + { + if (IsInBlock(i, pbh)) + return true; + } + return false; + } + bool IsValidIterator(const Iterator_t& it) const { return it.m_pBlockHeader && it.m_nIndex >= 0 && it.m_nIndex < it.m_pBlockHeader->m_nBlockSize; } + Iterator_t InvalidIterator() const { return Iterator_t(NULL, INVALID_INDEX); } + + // element access + T& operator[](intp i); + const T& operator[](intp i) const; + T& Element(intp i); + const T& Element(intp i) const; + + // Can we use this index? + bool IsIdxValid(intp i) const; + + // Specify the invalid ('null') index that we'll only return on failure + static const intp INVALID_INDEX = 0; // For use with COMPILE_TIME_ASSERT + static intp InvalidIndex() { return INVALID_INDEX; } + + // Size + int NumAllocated() const; + int Count() const { return NumAllocated(); } + + // Grows memory by max(num,growsize), and returns the allocation index/ptr + void Grow(int num = 1); + + // Makes sure we've got at least this much memory + void EnsureCapacity(int num); + + // Memory deallocation + void Purge(); + +protected: + // Fast swap - WARNING: Swap invalidates all ptr-based indices!!! + void Swap(CUtlFixedMemory< T >& mem); + + bool IsInBlock(intp i, BlockHeader_t* pBlockHeader) const + { + T* p = (T*)i; + const T* p0 = HeaderToBlock(pBlockHeader); + return p >= p0 && p < p0 + pBlockHeader->m_nBlockSize; + } + + struct BlockHeader_t + { + BlockHeader_t* m_pNext; + intp m_nBlockSize; + }; + + const T* HeaderToBlock(const BlockHeader_t* pHeader) const { return (T*)(pHeader + 1); } + const BlockHeader_t* BlockToHeader(const T* pBlock) const { return (BlockHeader_t*)(pBlock)-1; } + + BlockHeader_t* m_pBlocks; + int m_nAllocationCount; + int m_nGrowSize; +}; + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- + +template< class T > +CUtlFixedMemory::CUtlFixedMemory(int nGrowSize, int nInitAllocationCount) + : m_pBlocks(0), m_nAllocationCount(0), m_nGrowSize(0) +{ + Init(nGrowSize, nInitAllocationCount); +} + +template< class T > +CUtlFixedMemory::~CUtlFixedMemory() +{ + Purge(); +} + + +//----------------------------------------------------------------------------- +// Fast swap - WARNING: Swap invalidates all ptr-based indices!!! +//----------------------------------------------------------------------------- +template< class T > +void CUtlFixedMemory::Swap(CUtlFixedMemory< T >& mem) +{ + V_swap(m_pBlocks, mem.m_pBlocks); + V_swap(m_nAllocationCount, mem.m_nAllocationCount); + V_swap(m_nGrowSize, mem.m_nGrowSize); +} + + +//----------------------------------------------------------------------------- +// Set the size by which the memory grows - round up to the next power of 2 +//----------------------------------------------------------------------------- +template< class T > +void CUtlFixedMemory::Init(int nGrowSize /* = 0 */, int nInitSize /* = 0 */) +{ + Purge(); + + m_nGrowSize = nGrowSize; + + Grow(nInitSize); +} + +//----------------------------------------------------------------------------- +// element access +//----------------------------------------------------------------------------- +template< class T > +inline T& CUtlFixedMemory::operator[](intp i) +{ + //Assert(IsIdxValid(i)); + return *(T*)i; +} + +template< class T > +inline const T& CUtlFixedMemory::operator[](intp i) const +{ + //Assert(IsIdxValid(i)); + return *(T*)i; +} + +template< class T > +inline T& CUtlFixedMemory::Element(intp i) +{ + //Assert(IsIdxValid(i)); + return *(T*)i; +} + +template< class T > +inline const T& CUtlFixedMemory::Element(intp i) const +{ + //Assert(IsIdxValid(i)); + return *(T*)i; +} + + +//----------------------------------------------------------------------------- +// Size +//----------------------------------------------------------------------------- +template< class T > +inline int CUtlFixedMemory::NumAllocated() const +{ + return m_nAllocationCount; +} + + +//----------------------------------------------------------------------------- +// Is element index valid? +//----------------------------------------------------------------------------- +template< class T > +inline bool CUtlFixedMemory::IsIdxValid(intp i) const +{ +#ifdef _DEBUG + for (BlockHeader_t* pbh = m_pBlocks; pbh; pbh = pbh->m_pNext) + { + if (IsInBlock(i, pbh)) + return true; + } + return false; +#else + return i != InvalidIndex(); +#endif +} + +template< class T > +void CUtlFixedMemory::Grow(int num) +{ + if (num <= 0) + return; + + int nBlockSize = m_nGrowSize; + if (nBlockSize == 0) + { + if (m_nAllocationCount) + { + nBlockSize = m_nAllocationCount; + } + else + { + // Compute an allocation which is at least as big as a cache line... + nBlockSize = (31 + sizeof(T)) / sizeof(T); + //Assert(nBlockSize); + } + } + if (nBlockSize < num) + { + int n = (num + nBlockSize - 1) / nBlockSize; + //Assert(n * nBlockSize >= num); + //Assert((n - 1) * nBlockSize < num); + nBlockSize *= n; + } + m_nAllocationCount += nBlockSize; + + MEM_ALLOC_CREDIT_CLASS(); + BlockHeader_t* RESTRICT pBlockHeader = (BlockHeader_t*)malloc(sizeof(BlockHeader_t) + nBlockSize * sizeof(T)); + if (!pBlockHeader) + { + //Error("CUtlFixedMemory overflow!\n"); + } + pBlockHeader->m_pNext = NULL; + pBlockHeader->m_nBlockSize = nBlockSize; + + if (!m_pBlocks) + { + m_pBlocks = pBlockHeader; + } + else + { +#if 1 // IsIdxAfter assumes that newly allocated blocks are at the end + BlockHeader_t* RESTRICT pbh = m_pBlocks; + while (pbh->m_pNext) + { + pbh = pbh->m_pNext; + } + pbh->m_pNext = pBlockHeader; +#else + pBlockHeader = m_pBlocks; + pBlockHeader->m_pNext = m_pBlocks; +#endif + } +} + + +//----------------------------------------------------------------------------- +// Makes sure we've got at least this much memory +//----------------------------------------------------------------------------- +template< class T > +inline void CUtlFixedMemory::EnsureCapacity(int num) +{ + Grow(num - NumAllocated()); +} + + +//----------------------------------------------------------------------------- +// Memory deallocation +//----------------------------------------------------------------------------- +template< class T > +void CUtlFixedMemory::Purge() +{ + if (!m_pBlocks) + return; + + for (BlockHeader_t* pbh = m_pBlocks; pbh; ) + { + BlockHeader_t* pFree = pbh; + pbh = pbh->m_pNext; + free(pFree); + } + m_pBlocks = NULL; + m_nAllocationCount = 0; +} + +#pragma warning (pop) \ No newline at end of file diff --git a/Amalgam/src/SDK/Definitions/Misc/CUtlLinkedList.h b/Amalgam/src/SDK/Definitions/Misc/CUtlLinkedList.h new file mode 100644 index 00000000..20e2a77f --- /dev/null +++ b/Amalgam/src/SDK/Definitions/Misc/CUtlLinkedList.h @@ -0,0 +1,1255 @@ +#pragma once +#include "CUtlMemory.h" +#include "CUtlFixedMemory.h" +#include "CUtlBlockMemory.h" +#include "CUtlShared.h" + +#define FOR_EACH_LL( listName, iteratorName ) \ + for( int iteratorName=(listName).Head(); (listName).IsUtlLinkedList && iteratorName != (listName).InvalidIndex(); iteratorName = (listName).Next( iteratorName ) ) + +template +struct UtlLinkedListElem_t +{ + T m_Element; + I m_Previous; + I m_Next; + +private: + // No copy constructor for these... + UtlLinkedListElem_t(const UtlLinkedListElem_t&); +}; + +template , I > > +class CUtlLinkedList +{ +public: + typedef T ElemType_t; + typedef S IndexType_t; // should really be called IndexStorageType_t, but that would be a huge change + typedef I IndexLocalType_t; + typedef M MemoryAllocator_t; + static const bool IsUtlLinkedList = true; // Used to match this at compiletime + + // constructor, destructor + CUtlLinkedList(int growSize = 0, int initSize = 0); + ~CUtlLinkedList(); + + // gets particular elements + T& Element(I i); + T const& Element(I i) const; + T& operator[](I i); + T const& operator[](I i) const; + + // Make sure we have a particular amount of memory + void EnsureCapacity(int num); + + void SetGrowSize(int growSize); + + // Memory deallocation + void Purge(); + + // Delete all the elements then call Purge. + void PurgeAndDeleteElements(); + + // Insertion methods.... + I InsertBefore(I before); + I InsertAfter(I after); + I AddToHead(); + I AddToTail(); + + I InsertBefore(I before, T const& src); + I InsertAfter(I after, T const& src); + I AddToHead(T const& src); + I AddToTail(T const& src); + + // Find an element and return its index or InvalidIndex() if it couldn't be found. + I Find(const T& src) const; + + // Look for the element. If it exists, remove it and return true. Otherwise, return false. + bool FindAndRemove(const T& src); + + // Removal methods + void Remove(I elem); + void RemoveAll(); + + // Allocation/deallocation methods + // If multilist == true, then list list may contain many + // non-connected lists, and IsInList and Head + Tail are meaningless... + I Alloc(bool multilist = false); + void Free(I elem); + + // list modification + void LinkBefore(I before, I elem); + void LinkAfter(I after, I elem); + void Unlink(I elem); + void LinkToHead(I elem); + void LinkToTail(I elem); + + // invalid index (M will never allocate an element at this index) + inline static S InvalidIndex() { return (S)M::InvalidIndex(); } + + // Is a given index valid to use? (representible by S and not the invalid index) + static bool IndexInRange(I index); + + inline static size_t ElementSize() { return sizeof(ListElem_t); } + + // list statistics + int Count() const; + inline bool IsEmpty(void) const + { + return (Head() == InvalidIndex()); + } + + I MaxElementIndex() const; + I NumAllocated(void) const { return m_NumAlloced; } + + // Traversing the list + I Head() const; + I Tail() const; + I Previous(I i) const; + I Next(I i) const; + + // STL compatible const_iterator class + template < typename List_t > + class _CUtlLinkedList_constiterator_t + { + public: + typedef typename List_t::ElemType_t ElemType_t; + typedef typename List_t::IndexType_t IndexType_t; + + // Default constructor -- gives a currently unusable iterator. + _CUtlLinkedList_constiterator_t() + : m_list(0) + , m_index(List_t::InvalidIndex()) + { + } + // Normal constructor. + _CUtlLinkedList_constiterator_t(const List_t& list, IndexType_t index) + : m_list(&list) + , m_index(index) + { + } + + // Pre-increment operator++. This is the most efficient increment + // operator so it should always be used. + _CUtlLinkedList_constiterator_t& operator++() + { + m_index = m_list->Next(m_index); + return *this; + } + // Post-increment operator++. This is less efficient than pre-increment. + _CUtlLinkedList_constiterator_t operator++(int) + { + // Copy ourselves. + _CUtlLinkedList_constiterator_t temp = *this; + // Increment ourselves. + ++*this; + // Return the copy. + return temp; + } + + // Pre-decrement operator--. This is the most efficient decrement + // operator so it should always be used. + _CUtlLinkedList_constiterator_t& operator--() + { + //Assert(m_index != m_list->Head()); + if (m_index == m_list->InvalidIndex()) + { + m_index = m_list->Tail(); + } + else + { + m_index = m_list->Previous(m_index); + } + return *this; + } + // Post-decrement operator--. This is less efficient than post-decrement. + _CUtlLinkedList_constiterator_t operator--(int) + { + // Copy ourselves. + _CUtlLinkedList_constiterator_t temp = *this; + // Decrement ourselves. + --*this; + // Return the copy. + return temp; + } + + bool operator==(const _CUtlLinkedList_constiterator_t& other) const + { + //Assert(m_list == other.m_list); + return m_index == other.m_index; + } + + bool operator!=(const _CUtlLinkedList_constiterator_t& other) const + { + //Assert(m_list == other.m_list); + return m_index != other.m_index; + } + + const ElemType_t& operator*() const + { + return m_list->Element(m_index); + } + + const ElemType_t* operator->() const + { + return (&**this); + } + + protected: + // Use a pointer rather than a reference so that we can support + // assignment of iterators. + const List_t* m_list; + IndexType_t m_index; + }; + + // STL compatible iterator class, using derivation so that a non-const + // list can return a const_iterator. + template < typename List_t > + class _CUtlLinkedList_iterator_t : public _CUtlLinkedList_constiterator_t< List_t > + { + public: + typedef typename List_t::ElemType_t ElemType_t; + typedef typename List_t::IndexType_t IndexType_t; + typedef _CUtlLinkedList_constiterator_t< List_t > Base; + + // Default constructor -- gives a currently unusable iterator. + _CUtlLinkedList_iterator_t() + { + } + // Normal constructor. + _CUtlLinkedList_iterator_t(const List_t& list, IndexType_t index) + : _CUtlLinkedList_constiterator_t< List_t >(list, index) + { + } + + // Pre-increment operator++. This is the most efficient increment + // operator so it should always be used. + _CUtlLinkedList_iterator_t& operator++() + { + Base::m_index = Base::m_list->Next(Base::m_index); + return *this; + } + // Post-increment operator++. This is less efficient than pre-increment. + _CUtlLinkedList_iterator_t operator++(int) + { + // Copy ourselves. + _CUtlLinkedList_iterator_t temp = *this; + // Increment ourselves. + ++*this; + // Return the copy. + return temp; + } + + // Pre-decrement operator--. This is the most efficient decrement + // operator so it should always be used. + _CUtlLinkedList_iterator_t& operator--() + { + //Assert(Base::m_index != Base::m_list->Head()); + if (Base::m_index == Base::m_list->InvalidIndex()) + { + Base::m_index = Base::m_list->Tail(); + } + else + { + Base::m_index = Base::m_list->Previous(Base::m_index); + } + return *this; + } + // Post-decrement operator--. This is less efficient than post-decrement. + _CUtlLinkedList_iterator_t operator--(int) + { + // Copy ourselves. + _CUtlLinkedList_iterator_t temp = *this; + // Decrement ourselves. + --*this; + // Return the copy. + return temp; + } + + ElemType_t& operator*() const + { + // Const_cast to allow sharing the implementation with the + // base class. + List_t* pMutableList = const_cast(Base::m_list); + return pMutableList->Element(Base::m_index); + } + + ElemType_t* operator->() const + { + return (&**this); + } + }; + + typedef _CUtlLinkedList_constiterator_t > const_iterator; + typedef _CUtlLinkedList_iterator_t > iterator; + const_iterator begin() const + { + return const_iterator(*this, Head()); + } + iterator begin() + { + return iterator(*this, Head()); + } + + const_iterator end() const + { + return const_iterator(*this, InvalidIndex()); + } + iterator end() + { + return iterator(*this, InvalidIndex()); + } + + // Are nodes in the list or valid? + bool IsValidIndex(I i) const; + bool IsInList(I i) const; + +protected: + + // What the linked list element looks like + typedef UtlLinkedListElem_t ListElem_t; + + // constructs the class + I AllocInternal(bool multilist = false); + void ConstructList(); + + // Gets at the list element.... + ListElem_t& InternalElement(I i) { return m_Memory[i]; } + ListElem_t const& InternalElement(I i) const { return m_Memory[i]; } + + // copy constructors not allowed + CUtlLinkedList(CUtlLinkedList const& list) { Assert(0); } + + M m_Memory; + I m_Head; + I m_Tail; + I m_FirstFree; + I m_ElementCount; // The number actually in the list + I m_NumAlloced; // The number of allocated elements + typename M::Iterator_t m_LastAlloc; // the last index allocated + + // For debugging purposes; + // it's in release builds so this can be used in libraries correctly + ListElem_t* m_pElements; + + FORCEINLINE M const& Memory(void) const + { + return m_Memory; + } + + void ResetDbgInfo() + { + m_pElements = m_Memory.Base(); + } + +private: + // Faster version of Next that can only be used from tested code internal + // to this class, such as Find(). It avoids the cost of checking the index + // validity, which is a big win on debug builds. + I PrivateNext(I i) const; +}; + + +// this is kind of ugly, but until C++ gets templatized typedefs in C++0x, it's our only choice +template < class T > +class CUtlFixedLinkedList : public CUtlLinkedList< T, int, true, int, CUtlFixedMemory< UtlLinkedListElem_t< T, int > > > +{ +public: + CUtlFixedLinkedList(int growSize = 0, int initSize = 0) + : CUtlLinkedList< T, int, true, int, CUtlFixedMemory< UtlLinkedListElem_t< T, int > > >(growSize, initSize) {} + + typedef CUtlLinkedList< T, int, true, int, CUtlFixedMemory< UtlLinkedListElem_t< T, int > > > BaseClass; + bool IsValidIndex(int i) const + { + if (!BaseClass::Memory().IsIdxValid(i)) + return false; + +#ifdef _DEBUG // it's safe to skip this here, since the only way to get indices after m_LastAlloc is to use MaxElementIndex + if (BaseClass::Memory().IsIdxAfter(i, this->m_LastAlloc)) + { + //Assert(0); + return false; // don't read values that have been allocated, but not constructed + } +#endif + + return (BaseClass::Memory()[i].m_Previous != i) || (BaseClass::Memory()[i].m_Next == i); + } + +private: + int MaxElementIndex() const { /*Assert(0);*/ return BaseClass::InvalidIndex(); } // fixedmemory containers don't support iteration from 0..maxelements-1 + void ResetDbgInfo() {} +}; + +// this is kind of ugly, but until C++ gets templatized typedefs in C++0x, it's our only choice +template < class T, class I = unsigned short > +class CUtlBlockLinkedList : public CUtlLinkedList< T, I, true, I, CUtlBlockMemory< UtlLinkedListElem_t< T, I >, I > > +{ +public: + CUtlBlockLinkedList(int growSize = 0, int initSize = 0) + : CUtlLinkedList< T, I, true, I, CUtlBlockMemory< UtlLinkedListElem_t< T, I >, I > >(growSize, initSize) {} +protected: + void ResetDbgInfo() {} +}; + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- + +template +CUtlLinkedList::CUtlLinkedList(int growSize, int initSize) : + m_Memory(growSize, initSize), m_LastAlloc(m_Memory.InvalidIterator()) +{ + // Prevent signed non-int datatypes + COMPILE_TIME_ASSERT(sizeof(S) == 4 || (((S)-1) > 0)); + ConstructList(); + ResetDbgInfo(); +} + +template +CUtlLinkedList::~CUtlLinkedList() +{ + RemoveAll(); +} + +template +void CUtlLinkedList::ConstructList() +{ + m_Head = InvalidIndex(); + m_Tail = InvalidIndex(); + m_FirstFree = InvalidIndex(); + m_ElementCount = 0; + m_NumAlloced = 0; +} + + +//----------------------------------------------------------------------------- +// gets particular elements +//----------------------------------------------------------------------------- + +template +inline T& CUtlLinkedList::Element(I i) +{ + return m_Memory[i].m_Element; +} + +template +inline T const& CUtlLinkedList::Element(I i) const +{ + return m_Memory[i].m_Element; +} + +template +inline T& CUtlLinkedList::operator[](I i) +{ + return m_Memory[i].m_Element; +} + +template +inline T const& CUtlLinkedList::operator[](I i) const +{ + return m_Memory[i].m_Element; +} + +//----------------------------------------------------------------------------- +// list statistics +//----------------------------------------------------------------------------- + +template +inline int CUtlLinkedList::Count() const +{ +#ifdef MULTILIST_PEDANTIC_ASSERTS + AssertMsg(!ML, "CUtlLinkedList::Count() is meaningless for linked lists."); +#endif + return m_ElementCount; +} + +template +inline I CUtlLinkedList::MaxElementIndex() const +{ + return m_Memory.NumAllocated(); +} + + +//----------------------------------------------------------------------------- +// Traversing the list +//----------------------------------------------------------------------------- + +template +inline I CUtlLinkedList::Head() const +{ + return m_Head; +} + +template +inline I CUtlLinkedList::Tail() const +{ + return m_Tail; +} + +template +inline I CUtlLinkedList::Previous(I i) const +{ + //Assert(IsValidIndex(i)); + return InternalElement(i).m_Previous; +} + +template +inline I CUtlLinkedList::Next(I i) const +{ + //Assert(IsValidIndex(i)); + return InternalElement(i).m_Next; +} + +template +inline I CUtlLinkedList::PrivateNext(I i) const +{ + return InternalElement(i).m_Next; +} + + +//----------------------------------------------------------------------------- +// Are nodes in the list or valid? +//----------------------------------------------------------------------------- + +#pragma warning(push) +#pragma warning( disable: 4310 ) // Allows "(I)(S)M::INVALID_INDEX" below +template +inline bool CUtlLinkedList::IndexInRange(I index) // Static method +{ + // Since S is not necessarily the type returned by M, we need to check that M returns indices + // which are representable by S. A common case is 'S === unsigned short', 'I == int', in which + // case CUtlMemory will have 'InvalidIndex == (int)-1' (which casts to 65535 in S), and will + // happily return elements at index 65535 and above. + + // Do some static checks here: + // 'I' needs to be able to store 'S' + COMPILE_TIME_ASSERT(sizeof(I) >= sizeof(S)); + // 'S' should be unsigned (to avoid signed arithmetic errors for plausibly exhaustible ranges) + COMPILE_TIME_ASSERT((sizeof(S) > 2) || (((S)-1) > 0)); + // M::INVALID_INDEX should be storable in S to avoid ambiguities (e.g. with 65536) + COMPILE_TIME_ASSERT((M::INVALID_INDEX == -1) || (M::INVALID_INDEX == (S)M::INVALID_INDEX)); + + return (((S)index == index) && ((S)index != InvalidIndex())); +} +#pragma warning(pop) + +template +inline bool CUtlLinkedList::IsValidIndex(I i) const +{ + if (!m_Memory.IsIdxValid(i)) + return false; + + if (m_Memory.IsIdxAfter(i, m_LastAlloc)) + return false; // don't read values that have been allocated, but not constructed + + return (m_Memory[i].m_Previous != i) || (m_Memory[i].m_Next == i); +} + +template +inline bool CUtlLinkedList::IsInList(I i) const +{ + if (!m_Memory.IsIdxValid(i) || m_Memory.IsIdxAfter(i, m_LastAlloc)) + return false; // don't read values that have been allocated, but not constructed + + return Previous(i) != i; +} + +/* +template +inline bool CUtlFixedLinkedList::IsInList( int i ) const +{ + return m_Memory.IsIdxValid( i ) && (Previous( i ) != i); +} +*/ + +//----------------------------------------------------------------------------- +// Makes sure we have enough memory allocated to store a requested # of elements +//----------------------------------------------------------------------------- + +template< class T, class S, bool ML, class I, class M > +void CUtlLinkedList::EnsureCapacity(int num) +{ + MEM_ALLOC_CREDIT_CLASS(); + m_Memory.EnsureCapacity(num); + ResetDbgInfo(); +} + +template< class T, class S, bool ML, class I, class M > +void CUtlLinkedList::SetGrowSize(int growSize) +{ + RemoveAll(); + m_Memory.Init(growSize); + ResetDbgInfo(); +} + + +//----------------------------------------------------------------------------- +// Deallocate memory +//----------------------------------------------------------------------------- + +template +void CUtlLinkedList::Purge() +{ + RemoveAll(); + + m_Memory.Purge(); + m_FirstFree = InvalidIndex(); + m_NumAlloced = 0; + + //Routing "m_LastAlloc = m_Memory.InvalidIterator();" through a local const to sidestep an internal compiler error on 360 builds + const typename M::Iterator_t scInvalidIterator = m_Memory.InvalidIterator(); + m_LastAlloc = scInvalidIterator; + ResetDbgInfo(); +} + + +template +void CUtlLinkedList::PurgeAndDeleteElements() +{ + I iNext; + for (I i = Head(); i != InvalidIndex(); i = iNext) + { + iNext = Next(i); + delete Element(i); + } + + Purge(); +} + + +//----------------------------------------------------------------------------- +// Node allocation/deallocation +//----------------------------------------------------------------------------- +template +I CUtlLinkedList::AllocInternal(bool multilist) +{ + //Assert(!multilist || ML); +#ifdef MULTILIST_PEDANTIC_ASSERTS + //Assert(multilist == ML); +#endif + I elem; + if (m_FirstFree == InvalidIndex()) + { + //Assert(m_Memory.IsValidIterator(m_LastAlloc) || m_ElementCount == 0); + + typename M::Iterator_t it = m_Memory.IsValidIterator(m_LastAlloc) ? m_Memory.Next(m_LastAlloc) : m_Memory.First(); + + if (!m_Memory.IsValidIterator(it)) + { + MEM_ALLOC_CREDIT_CLASS(); + m_Memory.Grow(); + ResetDbgInfo(); + + it = m_Memory.IsValidIterator(m_LastAlloc) ? m_Memory.Next(m_LastAlloc) : m_Memory.First(); + + //Assert(m_Memory.IsValidIterator(it)); + if (!m_Memory.IsValidIterator(it)) + { + // We rarely if ever handle alloc failure. Continuing leads to corruption. + //Error("CUtlLinkedList overflow! (exhausted memory allocator)\n"); + return InvalidIndex(); + } + } + + // We can overflow before the utlmemory overflows, since S != I + if (!IndexInRange(m_Memory.GetIndex(it))) + { + // We rarely if ever handle alloc failure. Continuing leads to corruption. + //Error("CUtlLinkedList overflow! (exhausted index range)\n"); + return InvalidIndex(); + } + + m_LastAlloc = it; + elem = m_Memory.GetIndex(m_LastAlloc); + m_NumAlloced++; + } + else + { + elem = m_FirstFree; + m_FirstFree = InternalElement(m_FirstFree).m_Next; + } + + if (!multilist) + { + InternalElement(elem).m_Next = elem; + InternalElement(elem).m_Previous = elem; + } + else + { + InternalElement(elem).m_Next = InvalidIndex(); + InternalElement(elem).m_Previous = InvalidIndex(); + } + + return elem; +} + +template +I CUtlLinkedList::Alloc(bool multilist) +{ + I elem = AllocInternal(multilist); + if (elem == InvalidIndex()) + return elem; + + Construct(&Element(elem)); + + return elem; +} + +template +void CUtlLinkedList::Free(I elem) +{ + //Assert(IsValidIndex(elem) && IndexInRange(elem)); + Unlink(elem); + + ListElem_t& internalElem = InternalElement(elem); + Destruct(&internalElem.m_Element); + internalElem.m_Next = m_FirstFree; + m_FirstFree = elem; +} + +//----------------------------------------------------------------------------- +// Insertion methods; allocates and links (uses default constructor) +//----------------------------------------------------------------------------- + +template +I CUtlLinkedList::InsertBefore(I before) +{ + // Make a new node + I newNode = AllocInternal(); + if (newNode == InvalidIndex()) + return newNode; + + // Link it in + LinkBefore(before, newNode); + + // Construct the data + Construct(&Element(newNode)); + + return newNode; +} + +template +I CUtlLinkedList::InsertAfter(I after) +{ + // Make a new node + I newNode = AllocInternal(); + if (newNode == InvalidIndex()) + return newNode; + + // Link it in + LinkAfter(after, newNode); + + // Construct the data + Construct(&Element(newNode)); + + return newNode; +} + +template +inline I CUtlLinkedList::AddToHead() +{ + return InsertAfter(InvalidIndex()); +} + +template +inline I CUtlLinkedList::AddToTail() +{ + return InsertBefore(InvalidIndex()); +} + + +//----------------------------------------------------------------------------- +// Insertion methods; allocates and links (uses copy constructor) +//----------------------------------------------------------------------------- + +template +I CUtlLinkedList::InsertBefore(I before, T const& src) +{ + // Make a new node + I newNode = AllocInternal(); + if (newNode == InvalidIndex()) + return newNode; + + // Link it in + LinkBefore(before, newNode); + + // Construct the data + CopyConstruct(&Element(newNode), src); + + return newNode; +} + +template +I CUtlLinkedList::InsertAfter(I after, T const& src) +{ + // Make a new node + I newNode = AllocInternal(); + if (newNode == InvalidIndex()) + return newNode; + + // Link it in + LinkAfter(after, newNode); + + // Construct the data + CopyConstruct(&Element(newNode), src); + + return newNode; +} + +template +inline I CUtlLinkedList::AddToHead(T const& src) +{ + return InsertAfter(InvalidIndex(), src); +} + +template +inline I CUtlLinkedList::AddToTail(T const& src) +{ + return InsertBefore(InvalidIndex(), src); +} + + +//----------------------------------------------------------------------------- +// Removal methods +//----------------------------------------------------------------------------- + +template +I CUtlLinkedList::Find(const T& src) const +{ + // Cache the invalidIndex to avoid two levels of function calls on each iteration. + I invalidIndex = InvalidIndex(); + for (I i = Head(); i != invalidIndex; i = PrivateNext(i)) + { + if (Element(i) == src) + return i; + } + return InvalidIndex(); +} + + +template +bool CUtlLinkedList::FindAndRemove(const T& src) +{ + I i = Find(src); + if (i == InvalidIndex()) + { + return false; + } + else + { + Remove(i); + return true; + } +} + + +template +void CUtlLinkedList::Remove(I elem) +{ + Free(elem); +} + +template +void CUtlLinkedList::RemoveAll() +{ + // Have to do some convoluted stuff to invoke the destructor on all + // valid elements for the multilist case (since we don't have all elements + // connected to each other in a list). + + if (m_LastAlloc == m_Memory.InvalidIterator()) + { + //Assert(m_Head == InvalidIndex()); + //Assert(m_Tail == InvalidIndex()); + //Assert(m_FirstFree == InvalidIndex()); + //Assert(m_ElementCount == 0); + return; + } + + if (ML) + { + for (typename M::Iterator_t it = m_Memory.First(); it != m_Memory.InvalidIterator(); it = m_Memory.Next(it)) + { + I i = m_Memory.GetIndex(it); + if (IsValidIndex(i)) // skip elements already in the free list + { + ListElem_t& internalElem = InternalElement(i); + Destruct(&internalElem.m_Element); + internalElem.m_Previous = i; + internalElem.m_Next = m_FirstFree; + m_FirstFree = i; + } + + if (it == m_LastAlloc) + break; // don't destruct elements that haven't ever been constructed + } + } + else + { + I i = Head(); + I next; + while (i != InvalidIndex()) + { + next = Next(i); + ListElem_t& internalElem = InternalElement(i); + Destruct(&internalElem.m_Element); + internalElem.m_Previous = i; + internalElem.m_Next = next == InvalidIndex() ? m_FirstFree : next; + i = next; + } + if (Head() != InvalidIndex()) + { + m_FirstFree = Head(); + } + } + + // Clear everything else out + m_Head = InvalidIndex(); + m_Tail = InvalidIndex(); + m_ElementCount = 0; +} + + +//----------------------------------------------------------------------------- +// list modification +//----------------------------------------------------------------------------- + +template +void CUtlLinkedList::LinkBefore(I before, I elem) +{ + //Assert(IsValidIndex(elem)); + + // Unlink it if it's in the list at the moment + Unlink(elem); + + ListElem_t* RESTRICT pNewElem = &InternalElement(elem); + + // The element *after* our newly linked one is the one we linked before. + pNewElem->m_Next = before; + + S newElem_mPrevious; // we need to hang on to this for the compairson against InvalidIndex() + // below; otherwise we get a a load-hit-store on pNewElem->m_Previous, even + // with RESTRICT + if (before == InvalidIndex()) + { + // In this case, we're linking to the end of the list, so reset the tail + newElem_mPrevious = m_Tail; + pNewElem->m_Previous = m_Tail; + m_Tail = elem; + } + else + { + // Here, we're not linking to the end. Set the prev pointer to point to + // the element we're linking. + //Assert(IsInList(before)); + ListElem_t* RESTRICT beforeElem = &InternalElement(before); + pNewElem->m_Previous = newElem_mPrevious = beforeElem->m_Previous; + beforeElem->m_Previous = elem; + } + + // Reset the head if we linked to the head of the list + if (newElem_mPrevious == InvalidIndex()) + m_Head = elem; + else + InternalElement(newElem_mPrevious).m_Next = elem; + + // one more element baby + ++m_ElementCount; +} + +template +void CUtlLinkedList::LinkAfter(I after, I elem) +{ + //Assert(IsValidIndex(elem)); + + // Unlink it if it's in the list at the moment + if (IsInList(elem)) + Unlink(elem); + + ListElem_t& newElem = InternalElement(elem); + + // The element *before* our newly linked one is the one we linked after + newElem.m_Previous = after; + if (after == InvalidIndex()) + { + // In this case, we're linking to the head of the list, reset the head + newElem.m_Next = m_Head; + m_Head = elem; + } + else + { + // Here, we're not linking to the end. Set the next pointer to point to + // the element we're linking. + //Assert(IsInList(after)); + ListElem_t& afterElem = InternalElement(after); + newElem.m_Next = afterElem.m_Next; + afterElem.m_Next = elem; + } + + // Reset the tail if we linked to the tail of the list + if (newElem.m_Next == InvalidIndex()) + m_Tail = elem; + else + InternalElement(newElem.m_Next).m_Previous = elem; + + // one more element baby + ++m_ElementCount; +} + +template +void CUtlLinkedList::Unlink(I elem) +{ + //Assert(IsValidIndex(elem)); + if (IsInList(elem)) + { + ListElem_t* RESTRICT pOldElem = &m_Memory[elem]; + + // If we're the first guy, reset the head + // otherwise, make our previous node's next pointer = our next + if (pOldElem->m_Previous != InvalidIndex()) + { + m_Memory[pOldElem->m_Previous].m_Next = pOldElem->m_Next; + } + else + { + m_Head = pOldElem->m_Next; + } + + // If we're the last guy, reset the tail + // otherwise, make our next node's prev pointer = our prev + if (pOldElem->m_Next != InvalidIndex()) + { + m_Memory[pOldElem->m_Next].m_Previous = pOldElem->m_Previous; + } + else + { + m_Tail = pOldElem->m_Previous; + } + + // This marks this node as not in the list, + // but not in the free list either + pOldElem->m_Previous = pOldElem->m_Next = elem; + + // One less puppy + --m_ElementCount; + } +} + +template +inline void CUtlLinkedList::LinkToHead(I elem) +{ + LinkAfter(InvalidIndex(), elem); +} + +template +inline void CUtlLinkedList::LinkToTail(I elem) +{ + LinkBefore(InvalidIndex(), elem); +} + + +//----------------------------------------------------------------------------- +// Class to drop in to replace a CUtlLinkedList that needs to be more memory agressive +//----------------------------------------------------------------------------- + +struct UtlPtrLinkedListIndex_t__ +{ + int unused; +}; +typedef struct UtlPtrLinkedListIndex_t__* UtlPtrLinkedListIndex_t; + +template < typename T > +class CUtlPtrLinkedList +{ +public: + CUtlPtrLinkedList() + : m_pFirst(NULL), + m_nElems(0) + { + COMPILE_TIME_ASSERT(sizeof(IndexType_t) == sizeof(Node_t*)); + } + + ~CUtlPtrLinkedList() + { + RemoveAll(); + } + + typedef UtlPtrLinkedListIndex_t IndexType_t; + + T& operator[](IndexType_t i) + { + return ((Node_t*)i)->elem; + } + + const T& operator[](IndexType_t i) const + { + return ((Node_t*)i)->elem; + } + + IndexType_t AddToTail() + { + return DoInsertBefore((IndexType_t)m_pFirst, NULL); + } + + IndexType_t AddToTail(T const& src) + { + return DoInsertBefore((IndexType_t)m_pFirst, &src); + } + + IndexType_t AddToHead() + { + IndexType_t result = DoInsertBefore((IndexType_t)m_pFirst, NULL); + m_pFirst = ((Node_t*)result); + return result; + } + + IndexType_t AddToHead(T const& src) + { + IndexType_t result = DoInsertBefore((IndexType_t)m_pFirst, &src); + m_pFirst = ((Node_t*)result); + return result; + } + + IndexType_t InsertBefore(IndexType_t before) + { + return DoInsertBefore(before, NULL); + } + + IndexType_t InsertAfter(IndexType_t after) + { + Node_t* pBefore = ((Node_t*)after)->next; + return DoInsertBefore(pBefore, NULL); + } + + IndexType_t InsertBefore(IndexType_t before, T const& src) + { + return DoInsertBefore(before, &src); + } + + IndexType_t InsertAfter(IndexType_t after, T const& src) + { + Node_t* pBefore = ((Node_t*)after)->next; + return DoInsertBefore(pBefore, &src); + } + + void Remove(IndexType_t elem) + { + Node_t* p = (Node_t*)elem; + + if (p->pNext == p) + { + m_pFirst = NULL; + } + else + { + if (m_pFirst == p) + { + m_pFirst = p->pNext; + } + p->pNext->pPrev = p->pPrev; + p->pPrev->pNext = p->pNext; + } + + delete p; + m_nElems--; + } + + void RemoveAll() + { + Node_t* p = m_pFirst; + if (p) + { + do + { + Node_t* pNext = p->pNext; + delete p; + p = pNext; + } while (p != m_pFirst); + } + + m_pFirst = NULL; + m_nElems = 0; + } + + int Count() const + { + return m_nElems; + } + + IndexType_t Head() const + { + return (IndexType_t)m_pFirst; + } + + IndexType_t Next(IndexType_t i) const + { + Node_t* p = ((Node_t*)i)->pNext; + if (p != m_pFirst) + { + return (IndexType_t)p; + } + return NULL; + } + + bool IsValidIndex(IndexType_t i) const + { + Node_t* p = ((Node_t*)i); + return (p && p->pNext && p->pPrev); + } + + inline static IndexType_t InvalidIndex() + { + return NULL; + } +private: + + struct Node_t + { + Node_t() {} + Node_t(const T& _elem) : elem(_elem) {} + + T elem; + Node_t* pPrev, * pNext; + }; + + Node_t* AllocNode(const T* pCopyFrom) + { + MEM_ALLOC_CREDIT_CLASS(); + Node_t* p; + + if (!pCopyFrom) + { + p = new Node_t; + } + else + { + p = new Node_t(*pCopyFrom); + } + + return p; + } + + IndexType_t DoInsertBefore(IndexType_t before, const T* pCopyFrom) + { + Node_t* p = AllocNode(pCopyFrom); + Node_t* pBefore = (Node_t*)before; + if (pBefore) + { + p->pNext = pBefore; + p->pPrev = pBefore->pPrev; + pBefore->pPrev = p; + p->pPrev->pNext = p; + } + else + { + //Assert(!m_pFirst); + m_pFirst = p->pNext = p->pPrev = p; + } + + m_nElems++; + return (IndexType_t)p; + } + + Node_t* m_pFirst; + unsigned m_nElems; +}; \ No newline at end of file diff --git a/Amalgam/src/SDK/Definitions/Misc/CUtlMap.h b/Amalgam/src/SDK/Definitions/Misc/CUtlMap.h new file mode 100644 index 00000000..a9788261 --- /dev/null +++ b/Amalgam/src/SDK/Definitions/Misc/CUtlMap.h @@ -0,0 +1,227 @@ +#pragma once +#include "CUtlRBTree.h" + +#define FOR_EACH_MAP( mapName, iteratorName ) \ + for ( int iteratorName = (mapName).FirstInorder(); (mapName).IsUtlMap && iteratorName != (mapName).InvalidIndex(); iteratorName = (mapName).NextInorder( iteratorName ) ) + +#define FOR_EACH_MAP_FAST( mapName, iteratorName ) \ + for ( int iteratorName = 0; (mapName).IsUtlMap && iteratorName < (mapName).MaxElement(); ++iteratorName ) if ( !(mapName).IsValidIndex( iteratorName ) ) continue; else + +struct base_utlmap_t +{ +public: + // This enum exists so that FOR_EACH_MAP and FOR_EACH_MAP_FAST cannot accidentally + // be used on a type that is not a CUtlMap. If the code compiles then all is well. + // The check for IsUtlMap being true should be free. + // Using an enum rather than a static const bool ensures that this trick works even + // with optimizations disabled on gcc. + enum CompileTimeCheck + { + IsUtlMap = 1 + }; +}; + +template +class CUtlMap : public base_utlmap_t +{ +public: + typedef K KeyType_t; + typedef T ElemType_t; + typedef I IndexType_t; + + // Less func typedef + // Returns true if the first parameter is "less" than the second + typedef bool (*LessFunc_t)(const KeyType_t&, const KeyType_t&); + + // constructor, destructor + // Left at growSize = 0, the memory will first allocate 1 element and double in size + // at each increment. + // LessFunc_t is required, but may be set after the constructor using SetLessFunc() below + CUtlMap(int growSize = 0, int initSize = 0, LessFunc_t lessfunc = 0) + : m_Tree(growSize, initSize, CKeyLess(lessfunc)) + { + } + + CUtlMap(LessFunc_t lessfunc) + : m_Tree(CKeyLess(lessfunc)) + { + } + + void EnsureCapacity(int num) { m_Tree.EnsureCapacity(num); } + + // gets particular elements + ElemType_t& Element(IndexType_t i) { return m_Tree.Element(i).elem; } + const ElemType_t& Element(IndexType_t i) const { return m_Tree.Element(i).elem; } + ElemType_t& operator[](IndexType_t i) { return m_Tree.Element(i).elem; } + const ElemType_t& operator[](IndexType_t i) const { return m_Tree.Element(i).elem; } + KeyType_t& Key(IndexType_t i) { return m_Tree.Element(i).key; } + const KeyType_t& Key(IndexType_t i) const { return m_Tree.Element(i).key; } + + + // Num elements + unsigned int Count() const { return m_Tree.Count(); } + + // Max "size" of the vector + IndexType_t MaxElement() const { return m_Tree.MaxElement(); } + + // Checks if a node is valid and in the map + bool IsValidIndex(IndexType_t i) const { return m_Tree.IsValidIndex(i); } + + // Checks if the map as a whole is valid + bool IsValid() const { return m_Tree.IsValid(); } + + // Invalid index + static IndexType_t InvalidIndex() { return CTree::InvalidIndex(); } + + // Sets the less func + void SetLessFunc(LessFunc_t func) + { + m_Tree.SetLessFunc(CKeyLess(func)); + } + + // Insert method (inserts in order) + IndexType_t Insert(const KeyType_t& key, const ElemType_t& insert) + { + Node_t node; + node.key = key; + node.elem = insert; + return m_Tree.Insert(node); + } + + IndexType_t Insert(const KeyType_t& key) + { + Node_t node; + node.key = key; + return m_Tree.Insert(node); + } + + // Find method + IndexType_t Find(const KeyType_t& key) const + { + Node_t dummyNode; + dummyNode.key = key; + return m_Tree.Find(dummyNode); + } + + // Remove methods + void RemoveAt(IndexType_t i) { m_Tree.RemoveAt(i); } + bool Remove(const KeyType_t& key) + { + Node_t dummyNode; + dummyNode.key = key; + return m_Tree.Remove(dummyNode); + } + + void RemoveAll() { m_Tree.RemoveAll(); } + void Purge() { m_Tree.Purge(); } + + // Purges the list and calls delete on each element in it. + void PurgeAndDeleteElements(); + + // Iteration + IndexType_t FirstInorder() const { return m_Tree.FirstInorder(); } + IndexType_t NextInorder(IndexType_t i) const { return m_Tree.NextInorder(i); } + IndexType_t PrevInorder(IndexType_t i) const { return m_Tree.PrevInorder(i); } + IndexType_t LastInorder() const { return m_Tree.LastInorder(); } + + // If you change the search key, this can be used to reinsert the + // element into the map. + void Reinsert(const KeyType_t& key, IndexType_t i) + { + m_Tree[i].key = key; + m_Tree.Reinsert(i); + } + + IndexType_t InsertOrReplace(const KeyType_t& key, const ElemType_t& insert) + { + IndexType_t i = Find(key); + if (i != InvalidIndex()) + { + Element(i) = insert; + return i; + } + + return Insert(key, insert); + } + + void Swap(CUtlMap< K, T, I >& that) + { + m_Tree.Swap(that.m_Tree); + } + + + struct Node_t + { + Node_t() + { + } + + Node_t(const Node_t& from) + : key(from.key), + elem(from.elem) + { + } + + KeyType_t key; + ElemType_t elem; + }; + + class CKeyLess + { + public: + CKeyLess(LessFunc_t lessFunc) : m_LessFunc(lessFunc) {} + + bool operator!() const + { + return !m_LessFunc; + } + + bool operator()(const Node_t& left, const Node_t& right) const + { + return m_LessFunc(left.key, right.key); + } + + LessFunc_t m_LessFunc; + }; + + typedef CUtlRBTree CTree; + + CTree* AccessTree() { return &m_Tree; } + +protected: + CTree m_Tree; +}; + +//----------------------------------------------------------------------------- + +// Purges the list and calls delete on each element in it. +template< typename K, typename T, typename I > +inline void CUtlMap::PurgeAndDeleteElements() +{ + for (I i = 0; i < MaxElement(); ++i) + { + if (!IsValidIndex(i)) + continue; + + delete Element(i); + } + + Purge(); +} + +//----------------------------------------------------------------------------- + +// This is horrible and slow and meant to be used only when you're dealing with really +// non-time/memory-critical code and desperately want to copy a whole map element-by-element +// for whatever reason. +template < typename K, typename T, typename I > +void DeepCopyMap(const CUtlMap& pmapIn, CUtlMap* out_pmapOut) +{ + Assert(out_pmapOut); + + out_pmapOut->Purge(); + FOR_EACH_MAP_FAST(pmapIn, i) + { + out_pmapOut->Insert(pmapIn.Key(i), pmapIn.Element(i)); + } +} \ No newline at end of file diff --git a/Amalgam/src/SDK/Definitions/Misc/CUtlMemory.h b/Amalgam/src/SDK/Definitions/Misc/CUtlMemory.h index 3a88bba4..40476e1a 100644 --- a/Amalgam/src/SDK/Definitions/Misc/CUtlMemory.h +++ b/Amalgam/src/SDK/Definitions/Misc/CUtlMemory.h @@ -1,4 +1,5 @@ #pragma once +#include "CUtlShared.h" #include "../Interfaces/IMemAlloc.h" #pragma warning (push) diff --git a/Amalgam/src/SDK/Definitions/Misc/CUtlRBTree.h b/Amalgam/src/SDK/Definitions/Misc/CUtlRBTree.h new file mode 100644 index 00000000..bc967aaa --- /dev/null +++ b/Amalgam/src/SDK/Definitions/Misc/CUtlRBTree.h @@ -0,0 +1,1580 @@ +#pragma once +#include "CUtlMemory.h" +#include "CUtlFixedMemory.h" +#include "CUtlBlockMemory.h" +#include "CUtlShared.h" + +#pragma warning (push) +#pragma warning (disable:4996) + +template +class CDefOps +{ +public: + static bool LessFunc(const T& lhs, const T& rhs) { return (lhs < rhs); } +}; + +#define DefLessFunc( type ) CDefOps< type >::LessFunc + +template +class CDefLess +{ +public: + CDefLess() {} + CDefLess(int i) {} + inline bool operator()(const T& lhs, const T& rhs) const { return (lhs < rhs); } + inline bool operator!() const { return false; } +}; + +//------------------------------------- + +inline bool StringLessThan(const char* const& lhs, const char* const& rhs) { + if (!lhs) return false; + if (!rhs) return true; + return (strcmp(lhs, rhs) < 0); +} + +inline bool CaselessStringLessThan(const char* const& lhs, const char* const& rhs) { + if (!lhs) return false; + if (!rhs) return true; + return (stricmp(lhs, rhs) < 0); +} + + +// Same as CaselessStringLessThan, but it ignores differences in / and \. +inline bool CaselessStringLessThanIgnoreSlashes(const char* const& lhs, const char* const& rhs) +{ + const char* pa = lhs; + const char* pb = rhs; + while (*pa && *pb) + { + char a = *pa; + char b = *pb; + + // Check for dir slashes. + if (a == '/' || a == '\\') + { + if (b != '/' && b != '\\') + return ('/' < b); + } + else + { + if (a >= 'a' && a <= 'z') + a = 'A' + (a - 'a'); + + if (b >= 'a' && b <= 'z') + b = 'A' + (b - 'a'); + + if (a > b) + return false; + else if (a < b) + return true; + } + ++pa; + ++pb; + } + + // Filenames also must be the same length. + if (*pa != *pb) + { + // If pa shorter than pb then it's "less" + return (!*pa); + } + + return false; +} + +//------------------------------------- +// inline these two templates to stop multiple definitions of the same code +template <> inline bool CDefOps::LessFunc(const char* const& lhs, const char* const& rhs) { return StringLessThan(lhs, rhs); } +template <> inline bool CDefOps::LessFunc(char* const& lhs, char* const& rhs) { return StringLessThan(lhs, rhs); } + +//------------------------------------- + +template +void SetDefLessFunc(RBTREE_T& RBTree) +{ + RBTree.SetLessFunc(DefLessFunc(typename RBTREE_T::KeyType_t)); +} + +//----------------------------------------------------------------------------- +// A red-black binary search tree +//----------------------------------------------------------------------------- + +template < class I > +struct UtlRBTreeLinks_t +{ + I m_Left; + I m_Right; + I m_Parent; + I m_Tag; +}; + +template < class T, class I > +struct UtlRBTreeNode_t : public UtlRBTreeLinks_t< I > +{ + T m_Data; +}; + +template < class T, class I = unsigned short, typename L = bool (*)(const T&, const T&), class M = CUtlMemory< UtlRBTreeNode_t< T, I >, I > > +class CUtlRBTree +{ +public: + + typedef T KeyType_t; + typedef T ElemType_t; + typedef I IndexType_t; + + // Less func typedef + // Returns true if the first parameter is "less" than the second + typedef L LessFunc_t; + + // constructor, destructor + // Left at growSize = 0, the memory will first allocate 1 element and double in size + // at each increment. + // LessFunc_t is required, but may be set after the constructor using SetLessFunc() below + CUtlRBTree(int growSize = 0, int initSize = 0, const LessFunc_t& lessfunc = 0); + CUtlRBTree(const LessFunc_t& lessfunc); + ~CUtlRBTree(); + + void EnsureCapacity(int num); + + void CopyFrom(const CUtlRBTree& other); + + // gets particular elements + T& Element(I i); + T const& Element(I i) const; + T& operator[](I i); + T const& operator[](I i) const; + + // Gets the root + I Root() const; + + // Num elements + unsigned int Count() const; + + // Max "size" of the vector + // it's not generally safe to iterate from index 0 to MaxElement()-1 + // it IS safe to do so when using CUtlMemory as the allocator, + // but we should really remove patterns using this anyways, for safety and generality + I MaxElement() const; + + // Gets the children + I Parent(I i) const; + I LeftChild(I i) const; + I RightChild(I i) const; + + // Tests if a node is a left or right child + bool IsLeftChild(I i) const; + bool IsRightChild(I i) const; + + // Tests if root or leaf + bool IsRoot(I i) const; + bool IsLeaf(I i) const; + + // Checks if a node is valid and in the tree + bool IsValidIndex(I i) const; + + // Checks if the tree as a whole is valid + bool IsValid() const; + + // Invalid index + static I InvalidIndex(); + + // returns the tree depth (not a very fast operation) + int Depth(I node) const; + int Depth() const; + + // Sets the less func + void SetLessFunc(const LessFunc_t& func); + + // Allocation method + I NewNode(); + + // Insert method (inserts in order) + I Insert(T const& insert); + void Insert(const T* pArray, int nItems); + I InsertIfNotFound(T const& insert); + + // Find method + I Find(T const& search) const; + + // Remove methods + void RemoveAt(I i); + bool Remove(T const& remove); + void RemoveAll(); + void Purge(); + + bool HasElement(T const& search) const { return Find(search) != InvalidIndex(); } + + // Allocation, deletion + void FreeNode(I i); + + // Iteration + I FirstInorder() const; + I NextInorder(I i) const; + I PrevInorder(I i) const; + I LastInorder() const; + + I FirstPreorder() const; + I NextPreorder(I i) const; + I PrevPreorder(I i) const; + I LastPreorder() const; + + I FirstPostorder() const; + I NextPostorder(I i) const; + + // If you change the search key, this can be used to reinsert the + // element into the tree. + void Reinsert(I elem); + + // swap in place + void Swap(CUtlRBTree< T, I, L >& that); + +private: + // Can't copy the tree this way! + CUtlRBTree& operator=(const CUtlRBTree& other); + +protected: + enum NodeColor_t + { + RED = 0, + BLACK + }; + + typedef UtlRBTreeNode_t< T, I > Node_t; + typedef UtlRBTreeLinks_t< I > Links_t; + + // Sets the children + void SetParent(I i, I parent); + void SetLeftChild(I i, I child); + void SetRightChild(I i, I child); + void LinkToParent(I i, I parent, bool isLeft); + + // Gets at the links + Links_t const& Links(I i) const; + Links_t& Links(I i); + + // Checks if a link is red or black + bool IsRed(I i) const; + bool IsBlack(I i) const; + + // Sets/gets node color + NodeColor_t Color(I i) const; + void SetColor(I i, NodeColor_t c); + + // operations required to preserve tree balance + void RotateLeft(I i); + void RotateRight(I i); + void InsertRebalance(I i); + void RemoveRebalance(I i); + + // Insertion, removal + I InsertAt(I parent, bool leftchild); + + // copy constructors not allowed + CUtlRBTree(CUtlRBTree const& tree); + + // Inserts a node into the tree, doesn't copy the data in. + void FindInsertionPosition(T const& insert, I& parent, bool& leftchild); + + // Remove and add back an element in the tree. + void Unlink(I elem); + void Link(I elem); + + // Used for sorting. + LessFunc_t m_LessFunc; + + M m_Elements; + I m_Root; + I m_NumElements; + I m_FirstFree; + typename M::Iterator_t m_LastAlloc; // the last index allocated + + Node_t* m_pElements; + + FORCEINLINE M const& Elements(void) const + { + return m_Elements; + } + + + void ResetDbgInfo() + { + m_pElements = (Node_t*)m_Elements.Base(); + } +}; + +// this is kind of ugly, but until C++ gets templatized typedefs in C++0x, it's our only choice +template < class T, class I = int, typename L = bool (*)(const T&, const T&) > +class CUtlFixedRBTree : public CUtlRBTree< T, I, L, CUtlFixedMemory< UtlRBTreeNode_t< T, I > > > +{ +public: + + typedef L LessFunc_t; + + CUtlFixedRBTree(int growSize = 0, int initSize = 0, const LessFunc_t& lessfunc = 0) + : CUtlRBTree< T, I, L, CUtlFixedMemory< UtlRBTreeNode_t< T, I > > >(growSize, initSize, lessfunc) {} + CUtlFixedRBTree(const LessFunc_t& lessfunc) + : CUtlRBTree< T, I, L, CUtlFixedMemory< UtlRBTreeNode_t< T, I > > >(lessfunc) {} + + typedef CUtlRBTree< T, I, L, CUtlFixedMemory< UtlRBTreeNode_t< T, I > > > BaseClass; + bool IsValidIndex(I i) const + { + if (!BaseClass::Elements().IsIdxValid(i)) + return false; + +#ifdef _DEBUG // it's safe to skip this here, since the only way to get indices after m_LastAlloc is to use MaxElement() + if (BaseClass::Elements().IsIdxAfter(i, this->m_LastAlloc)) + { + //Assert(0); + return false; // don't read values that have been allocated, but not constructed + } +#endif + + return LeftChild(i) != i; + } + +protected: + void ResetDbgInfo() {} + +private: + // this doesn't make sense for fixed rbtrees, since there's no useful max pointer, and the index space isn't contiguous anyways + I MaxElement() const; +}; + +// this is kind of ugly, but until C++ gets templatized typedefs in C++0x, it's our only choice +template < class T, class I = unsigned short, typename L = bool (*)(const T&, const T&) > +class CUtlBlockRBTree : public CUtlRBTree< T, I, L, CUtlBlockMemory< UtlRBTreeNode_t< T, I >, I > > +{ +public: + typedef L LessFunc_t; + CUtlBlockRBTree(int growSize = 0, int initSize = 0, const LessFunc_t& lessfunc = 0) + : CUtlRBTree< T, I, L, CUtlBlockMemory< UtlRBTreeNode_t< T, I >, I > >(growSize, initSize, lessfunc) {} + CUtlBlockRBTree(const LessFunc_t& lessfunc) + : CUtlRBTree< T, I, L, CUtlBlockMemory< UtlRBTreeNode_t< T, I >, I > >(lessfunc) {} +protected: + void ResetDbgInfo() {} +}; + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline CUtlRBTree::CUtlRBTree(int growSize, int initSize, const LessFunc_t& lessfunc) : + m_Elements(growSize, initSize), + m_LessFunc(lessfunc), + m_Root(InvalidIndex()), + m_NumElements(0), + m_FirstFree(InvalidIndex()), + m_LastAlloc(m_Elements.InvalidIterator()) +{ + ResetDbgInfo(); +} + +template < class T, class I, typename L, class M > +inline CUtlRBTree::CUtlRBTree(const LessFunc_t& lessfunc) : + m_Elements(0, 0), + m_LessFunc(lessfunc), + m_Root(InvalidIndex()), + m_NumElements(0), + m_FirstFree(InvalidIndex()), + m_LastAlloc(m_Elements.InvalidIterator()) +{ + ResetDbgInfo(); +} + +template < class T, class I, typename L, class M > +inline CUtlRBTree::~CUtlRBTree() +{ + Purge(); +} + +template < class T, class I, typename L, class M > +inline void CUtlRBTree::EnsureCapacity(int num) +{ + m_Elements.EnsureCapacity(num); +} + +template < class T, class I, typename L, class M > +inline void CUtlRBTree::CopyFrom(const CUtlRBTree& other) +{ + Purge(); + m_Elements.EnsureCapacity(other.m_Elements.Count()); + memcpy(m_Elements.Base(), other.m_Elements.Base(), other.m_Elements.Count() * sizeof(T)); + m_LessFunc = other.m_LessFunc; + m_Root = other.m_Root; + m_NumElements = other.m_NumElements; + m_FirstFree = other.m_FirstFree; + m_LastAlloc = other.m_LastAlloc; + ResetDbgInfo(); +} + +//----------------------------------------------------------------------------- +// gets particular elements +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline T& CUtlRBTree::Element(I i) +{ + return m_Elements[i].m_Data; +} + +template < class T, class I, typename L, class M > +inline T const& CUtlRBTree::Element(I i) const +{ + return m_Elements[i].m_Data; +} + +template < class T, class I, typename L, class M > +inline T& CUtlRBTree::operator[](I i) +{ + return Element(i); +} + +template < class T, class I, typename L, class M > +inline T const& CUtlRBTree::operator[](I i) const +{ + return Element(i); +} + +//----------------------------------------------------------------------------- +// +// various accessors +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Gets the root +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline I CUtlRBTree::Root() const +{ + return m_Root; +} + +//----------------------------------------------------------------------------- +// Num elements +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline unsigned int CUtlRBTree::Count() const +{ + return (unsigned int)m_NumElements; +} + +//----------------------------------------------------------------------------- +// Max "size" of the vector +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline I CUtlRBTree::MaxElement() const +{ + return (I)m_Elements.NumAllocated(); +} + + +//----------------------------------------------------------------------------- +// Gets the children +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline I CUtlRBTree::Parent(I i) const +{ + return Links(i).m_Parent; +} + +template < class T, class I, typename L, class M > +inline I CUtlRBTree::LeftChild(I i) const +{ + return Links(i).m_Left; +} + +template < class T, class I, typename L, class M > +inline I CUtlRBTree::RightChild(I i) const +{ + return Links(i).m_Right; +} + +//----------------------------------------------------------------------------- +// Tests if a node is a left or right child +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline bool CUtlRBTree::IsLeftChild(I i) const +{ + return LeftChild(Parent(i)) == i; +} + +template < class T, class I, typename L, class M > +inline bool CUtlRBTree::IsRightChild(I i) const +{ + return RightChild(Parent(i)) == i; +} + + +//----------------------------------------------------------------------------- +// Tests if root or leaf +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline bool CUtlRBTree::IsRoot(I i) const +{ + return i == m_Root; +} + +template < class T, class I, typename L, class M > +inline bool CUtlRBTree::IsLeaf(I i) const +{ + return (LeftChild(i) == InvalidIndex()) && (RightChild(i) == InvalidIndex()); +} + + +//----------------------------------------------------------------------------- +// Checks if a node is valid and in the tree +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline bool CUtlRBTree::IsValidIndex(I i) const +{ + if (!m_Elements.IsIdxValid(i)) + return false; + + if (m_LastAlloc == m_Elements.InvalidIndex() || m_Elements.IsIdxAfter(i, m_LastAlloc)) + return false; // don't read values that have been allocated, but not constructed + + return LeftChild(i) != i; +} + + +//----------------------------------------------------------------------------- +// Invalid index +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline I CUtlRBTree::InvalidIndex() +{ + return (I)M::InvalidIndex(); +} + + +//----------------------------------------------------------------------------- +// returns the tree depth (not a very fast operation) +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline int CUtlRBTree::Depth() const +{ + return Depth(Root()); +} + +//----------------------------------------------------------------------------- +// Sets the children +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline void CUtlRBTree::SetParent(I i, I parent) +{ + Links(i).m_Parent = parent; +} + +template < class T, class I, typename L, class M > +inline void CUtlRBTree::SetLeftChild(I i, I child) +{ + Links(i).m_Left = child; +} + +template < class T, class I, typename L, class M > +inline void CUtlRBTree::SetRightChild(I i, I child) +{ + Links(i).m_Right = child; +} + +//----------------------------------------------------------------------------- +// Gets at the links +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline typename CUtlRBTree::Links_t const& CUtlRBTree::Links(I i) const +{ + // Sentinel node, makes life easier + static Links_t s_Sentinel = + { + InvalidIndex(), InvalidIndex(), InvalidIndex(), CUtlRBTree::BLACK + }; + + return (i != InvalidIndex()) ? *(Links_t*)&m_Elements[i] : *(Links_t*)&s_Sentinel; +} + +template < class T, class I, typename L, class M > +inline typename CUtlRBTree::Links_t& CUtlRBTree::Links(I i) +{ + //Assert(i != InvalidIndex()); + return *(Links_t*)&m_Elements[i]; +} + +//----------------------------------------------------------------------------- +// Checks if a link is red or black +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline bool CUtlRBTree::IsRed(I i) const +{ + return (Links(i).m_Tag == RED); +} + +template < class T, class I, typename L, class M > +inline bool CUtlRBTree::IsBlack(I i) const +{ + return (Links(i).m_Tag == BLACK); +} + + +//----------------------------------------------------------------------------- +// Sets/gets node color +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline typename CUtlRBTree::NodeColor_t CUtlRBTree::Color(I i) const +{ + return (NodeColor_t)Links(i).m_Tag; +} + +template < class T, class I, typename L, class M > +inline void CUtlRBTree::SetColor(I i, typename CUtlRBTree::NodeColor_t c) +{ + Links(i).m_Tag = (I)c; +} + +//----------------------------------------------------------------------------- +// Allocates/ deallocates nodes +//----------------------------------------------------------------------------- +#pragma warning(push) +#pragma warning(disable:4389) // '==' : signed/unsigned mismatch +template < class T, class I, typename L, class M > +I CUtlRBTree::NewNode() +{ + I elem; + + // Nothing in the free list; add. + if (m_FirstFree == InvalidIndex()) + { + //Assert(m_Elements.IsValidIterator(m_LastAlloc) || m_NumElements == 0); + typename M::Iterator_t it = m_Elements.IsValidIterator(m_LastAlloc) ? m_Elements.Next(m_LastAlloc) : m_Elements.First(); + if (!m_Elements.IsValidIterator(it)) + { + MEM_ALLOC_CREDIT_CLASS(); + m_Elements.Grow(); + + it = m_Elements.IsValidIterator(m_LastAlloc) ? m_Elements.Next(m_LastAlloc) : m_Elements.First(); + + //Assert(m_Elements.IsValidIterator(it)); + if (!m_Elements.IsValidIterator(it)) + { + //Error("CUtlRBTree overflow!\n"); + } + } + m_LastAlloc = it; + elem = m_Elements.GetIndex(m_LastAlloc); + //Assert(m_Elements.IsValidIterator(m_LastAlloc)); + } + else + { + elem = m_FirstFree; + m_FirstFree = Links(m_FirstFree).m_Right; + } + +#ifdef _DEBUG + // reset links to invalid.... + Links_t& node = Links(elem); + node.m_Left = node.m_Right = node.m_Parent = InvalidIndex(); +#endif + + Construct(&Element(elem)); + ResetDbgInfo(); + + return elem; +} +#pragma warning(pop) + +template < class T, class I, typename L, class M > +void CUtlRBTree::FreeNode(I i) +{ + //Assert(IsValidIndex(i) && (i != InvalidIndex())); + Destruct(&Element(i)); + SetLeftChild(i, i); // indicates it's in not in the tree + SetRightChild(i, m_FirstFree); + m_FirstFree = i; +} + + +//----------------------------------------------------------------------------- +// Rotates node i to the left +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +void CUtlRBTree::RotateLeft(I elem) +{ + I rightchild = RightChild(elem); + SetRightChild(elem, LeftChild(rightchild)); + if (LeftChild(rightchild) != InvalidIndex()) + SetParent(LeftChild(rightchild), elem); + + if (rightchild != InvalidIndex()) + SetParent(rightchild, Parent(elem)); + if (!IsRoot(elem)) + { + if (IsLeftChild(elem)) + SetLeftChild(Parent(elem), rightchild); + else + SetRightChild(Parent(elem), rightchild); + } + else + m_Root = rightchild; + + SetLeftChild(rightchild, elem); + if (elem != InvalidIndex()) + SetParent(elem, rightchild); +} + + +//----------------------------------------------------------------------------- +// Rotates node i to the right +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +void CUtlRBTree::RotateRight(I elem) +{ + I leftchild = LeftChild(elem); + SetLeftChild(elem, RightChild(leftchild)); + if (RightChild(leftchild) != InvalidIndex()) + SetParent(RightChild(leftchild), elem); + + if (leftchild != InvalidIndex()) + SetParent(leftchild, Parent(elem)); + if (!IsRoot(elem)) + { + if (IsRightChild(elem)) + SetRightChild(Parent(elem), leftchild); + else + SetLeftChild(Parent(elem), leftchild); + } + else + m_Root = leftchild; + + SetRightChild(leftchild, elem); + if (elem != InvalidIndex()) + SetParent(elem, leftchild); +} + + +//----------------------------------------------------------------------------- +// Rebalances the tree after an insertion +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +void CUtlRBTree::InsertRebalance(I elem) +{ + while (!IsRoot(elem) && (Color(Parent(elem)) == RED)) + { + I parent = Parent(elem); + I grandparent = Parent(parent); + + /* we have a violation */ + if (IsLeftChild(parent)) + { + I uncle = RightChild(grandparent); + if (IsRed(uncle)) + { + /* uncle is RED */ + SetColor(parent, BLACK); + SetColor(uncle, BLACK); + SetColor(grandparent, RED); + elem = grandparent; + } + else + { + /* uncle is BLACK */ + if (IsRightChild(elem)) + { + /* make x a left child, will change parent and grandparent */ + elem = parent; + RotateLeft(elem); + parent = Parent(elem); + grandparent = Parent(parent); + } + /* recolor and rotate */ + SetColor(parent, BLACK); + SetColor(grandparent, RED); + RotateRight(grandparent); + } + } + else + { + /* mirror image of above code */ + I uncle = LeftChild(grandparent); + if (IsRed(uncle)) + { + /* uncle is RED */ + SetColor(parent, BLACK); + SetColor(uncle, BLACK); + SetColor(grandparent, RED); + elem = grandparent; + } + else + { + /* uncle is BLACK */ + if (IsLeftChild(elem)) + { + /* make x a right child, will change parent and grandparent */ + elem = parent; + RotateRight(parent); + parent = Parent(elem); + grandparent = Parent(parent); + } + /* recolor and rotate */ + SetColor(parent, BLACK); + SetColor(grandparent, RED); + RotateLeft(grandparent); + } + } + } + SetColor(m_Root, BLACK); +} + + +//----------------------------------------------------------------------------- +// Insert a node into the tree +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +I CUtlRBTree::InsertAt(I parent, bool leftchild) +{ + I i = NewNode(); + LinkToParent(i, parent, leftchild); + ++m_NumElements; + + //Assert(IsValid()); + + return i; +} + +template < class T, class I, typename L, class M > +void CUtlRBTree::LinkToParent(I i, I parent, bool isLeft) +{ + Links_t& elem = Links(i); + elem.m_Parent = parent; + elem.m_Left = elem.m_Right = InvalidIndex(); + elem.m_Tag = RED; + + /* insert node in tree */ + if (parent != InvalidIndex()) + { + if (isLeft) + Links(parent).m_Left = i; + else + Links(parent).m_Right = i; + } + else + { + m_Root = i; + } + + InsertRebalance(i); +} + +//----------------------------------------------------------------------------- +// Rebalance the tree after a deletion +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +void CUtlRBTree::RemoveRebalance(I elem) +{ + while (elem != m_Root && IsBlack(elem)) + { + I parent = Parent(elem); + + // If elem is the left child of the parent + if (elem == LeftChild(parent)) + { + // Get our sibling + I sibling = RightChild(parent); + if (IsRed(sibling)) + { + SetColor(sibling, BLACK); + SetColor(parent, RED); + RotateLeft(parent); + + // We may have a new parent now + parent = Parent(elem); + sibling = RightChild(parent); + } + if ((IsBlack(LeftChild(sibling))) && (IsBlack(RightChild(sibling)))) + { + if (sibling != InvalidIndex()) + SetColor(sibling, RED); + elem = parent; + } + else + { + if (IsBlack(RightChild(sibling))) + { + SetColor(LeftChild(sibling), BLACK); + SetColor(sibling, RED); + RotateRight(sibling); + + // rotation may have changed this + parent = Parent(elem); + sibling = RightChild(parent); + } + SetColor(sibling, Color(parent)); + SetColor(parent, BLACK); + SetColor(RightChild(sibling), BLACK); + RotateLeft(parent); + elem = m_Root; + } + } + else + { + // Elem is the right child of the parent + I sibling = LeftChild(parent); + if (IsRed(sibling)) + { + SetColor(sibling, BLACK); + SetColor(parent, RED); + RotateRight(parent); + + // We may have a new parent now + parent = Parent(elem); + sibling = LeftChild(parent); + } + if ((IsBlack(RightChild(sibling))) && (IsBlack(LeftChild(sibling)))) + { + if (sibling != InvalidIndex()) + SetColor(sibling, RED); + elem = parent; + } + else + { + if (IsBlack(LeftChild(sibling))) + { + SetColor(RightChild(sibling), BLACK); + SetColor(sibling, RED); + RotateLeft(sibling); + + // rotation may have changed this + parent = Parent(elem); + sibling = LeftChild(parent); + } + SetColor(sibling, Color(parent)); + SetColor(parent, BLACK); + SetColor(LeftChild(sibling), BLACK); + RotateRight(parent); + elem = m_Root; + } + } + } + SetColor(elem, BLACK); +} + +template < class T, class I, typename L, class M > +void CUtlRBTree::Unlink(I elem) +{ + if (elem != InvalidIndex()) + { + I x, y; + + if ((LeftChild(elem) == InvalidIndex()) || + (RightChild(elem) == InvalidIndex())) + { + /* y has a NIL node as a child */ + y = elem; + } + else + { + /* find tree successor with a NIL node as a child */ + y = RightChild(elem); + while (LeftChild(y) != InvalidIndex()) + y = LeftChild(y); + } + + /* x is y's only child */ + if (LeftChild(y) != InvalidIndex()) + x = LeftChild(y); + else + x = RightChild(y); + + /* remove y from the parent chain */ + if (x != InvalidIndex()) + SetParent(x, Parent(y)); + if (!IsRoot(y)) + { + if (IsLeftChild(y)) + SetLeftChild(Parent(y), x); + else + SetRightChild(Parent(y), x); + } + else + m_Root = x; + + // need to store this off now, we'll be resetting y's color + NodeColor_t ycolor = Color(y); + if (y != elem) + { + // Standard implementations copy the data around, we cannot here. + // Hook in y to link to the same stuff elem used to. + SetParent(y, Parent(elem)); + SetRightChild(y, RightChild(elem)); + SetLeftChild(y, LeftChild(elem)); + + if (!IsRoot(elem)) + if (IsLeftChild(elem)) + SetLeftChild(Parent(elem), y); + else + SetRightChild(Parent(elem), y); + else + m_Root = y; + + if (LeftChild(y) != InvalidIndex()) + SetParent(LeftChild(y), y); + if (RightChild(y) != InvalidIndex()) + SetParent(RightChild(y), y); + + SetColor(y, Color(elem)); + } + + if ((x != InvalidIndex()) && (ycolor == BLACK)) + RemoveRebalance(x); + } +} + +template < class T, class I, typename L, class M > +void CUtlRBTree::Link(I elem) +{ + if (elem != InvalidIndex()) + { + I parent; + bool leftchild; + + FindInsertionPosition(Element(elem), parent, leftchild); + + LinkToParent(elem, parent, leftchild); + + //Assert(IsValid()); + } +} + +//----------------------------------------------------------------------------- +// Delete a node from the tree +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +void CUtlRBTree::RemoveAt(I elem) +{ + if (elem != InvalidIndex()) + { + Unlink(elem); + + FreeNode(elem); + --m_NumElements; + + //Assert(IsValid()); + } +} + + +//----------------------------------------------------------------------------- +// remove a node in the tree +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > bool CUtlRBTree::Remove(T const& search) +{ + I node = Find(search); + if (node != InvalidIndex()) + { + RemoveAt(node); + return true; + } + return false; +} + + +//----------------------------------------------------------------------------- +// Removes all nodes from the tree +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +void CUtlRBTree::RemoveAll() +{ + // Have to do some convoluted stuff to invoke the destructor on all + // valid elements for the multilist case (since we don't have all elements + // connected to each other in a list). + + if (m_LastAlloc == m_Elements.InvalidIterator()) + { + //Assert(m_Root == InvalidIndex()); + //Assert(m_FirstFree == InvalidIndex()); + //Assert(m_NumElements == 0); + return; + } + + for (typename M::Iterator_t it = m_Elements.First(); it != m_Elements.InvalidIterator(); it = m_Elements.Next(it)) + { + I i = m_Elements.GetIndex(it); + if (IsValidIndex(i)) // skip elements in the free list + { + Destruct(&Element(i)); + SetRightChild(i, m_FirstFree); + SetLeftChild(i, i); + m_FirstFree = i; + } + + if (it == m_LastAlloc) + break; // don't destruct elements that haven't ever been constucted + } + + // Clear everything else out + m_Root = InvalidIndex(); + // Technically, this iterator could become invalid. It will not, because it's + // always the same iterator. If we don't clear this here, the state of this + // container will be invalid after we start inserting elements again. + m_LastAlloc = m_Elements.InvalidIterator(); + m_FirstFree = InvalidIndex(); + m_NumElements = 0; + + //Assert(IsValid()); +} + +//----------------------------------------------------------------------------- +// Removes all nodes from the tree and purges memory +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +void CUtlRBTree::Purge() +{ + RemoveAll(); + m_Elements.Purge(); +} + + +//----------------------------------------------------------------------------- +// iteration +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +I CUtlRBTree::FirstInorder() const +{ + I i = m_Root; + while (LeftChild(i) != InvalidIndex()) + i = LeftChild(i); + return i; +} + +template < class T, class I, typename L, class M > +I CUtlRBTree::NextInorder(I i) const +{ + // Don't go into an infinite loop if it's a bad index + //Assert(IsValidIndex(i)); + if (!IsValidIndex(i)) + return InvalidIndex(); + + if (RightChild(i) != InvalidIndex()) + { + i = RightChild(i); + while (LeftChild(i) != InvalidIndex()) + i = LeftChild(i); + return i; + } + + I parent = Parent(i); + while (IsRightChild(i)) + { + i = parent; + if (i == InvalidIndex()) break; + parent = Parent(i); + } + return parent; +} + +template < class T, class I, typename L, class M > +I CUtlRBTree::PrevInorder(I i) const +{ + // Don't go into an infinite loop if it's a bad index + //Assert(IsValidIndex(i)); + if (!IsValidIndex(i)) + return InvalidIndex(); + + if (LeftChild(i) != InvalidIndex()) + { + i = LeftChild(i); + while (RightChild(i) != InvalidIndex()) + i = RightChild(i); + return i; + } + + I parent = Parent(i); + while (IsLeftChild(i)) + { + i = parent; + if (i == InvalidIndex()) break; + parent = Parent(i); + } + return parent; +} + +template < class T, class I, typename L, class M > +I CUtlRBTree::LastInorder() const +{ + I i = m_Root; + while (RightChild(i) != InvalidIndex()) + i = RightChild(i); + return i; +} + +template < class T, class I, typename L, class M > +I CUtlRBTree::FirstPreorder() const +{ + return m_Root; +} + +template < class T, class I, typename L, class M > +I CUtlRBTree::NextPreorder(I i) const +{ + if (LeftChild(i) != InvalidIndex()) + return LeftChild(i); + + if (RightChild(i) != InvalidIndex()) + return RightChild(i); + + I parent = Parent(i); + while (parent != InvalidIndex()) + { + if (IsLeftChild(i) && (RightChild(parent) != InvalidIndex())) + return RightChild(parent); + i = parent; + parent = Parent(parent); + } + return InvalidIndex(); +} + +template < class T, class I, typename L, class M > +I CUtlRBTree::PrevPreorder(I i) const +{ + //Assert(0); // not implemented yet + return InvalidIndex(); +} + +template < class T, class I, typename L, class M > +I CUtlRBTree::LastPreorder() const +{ + I i = m_Root; + while (1) + { + while (RightChild(i) != InvalidIndex()) + i = RightChild(i); + + if (LeftChild(i) != InvalidIndex()) + i = LeftChild(i); + else + break; + } + return i; +} + +template < class T, class I, typename L, class M > +I CUtlRBTree::FirstPostorder() const +{ + I i = m_Root; + while (!IsLeaf(i)) + { + if (LeftChild(i)) + i = LeftChild(i); + else + i = RightChild(i); + } + return i; +} + +template < class T, class I, typename L, class M > +I CUtlRBTree::NextPostorder(I i) const +{ + I parent = Parent(i); + if (parent == InvalidIndex()) + return InvalidIndex(); + + if (IsRightChild(i)) + return parent; + + if (RightChild(parent) == InvalidIndex()) + return parent; + + i = RightChild(parent); + while (!IsLeaf(i)) + { + if (LeftChild(i)) + i = LeftChild(i); + else + i = RightChild(i); + } + return i; +} + + +template < class T, class I, typename L, class M > +void CUtlRBTree::Reinsert(I elem) +{ + Unlink(elem); + Link(elem); +} + + +//----------------------------------------------------------------------------- +// returns the tree depth (not a very fast operation) +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +int CUtlRBTree::Depth(I node) const +{ + if (node == InvalidIndex()) + return 0; + + int depthright = Depth(RightChild(node)); + int depthleft = Depth(LeftChild(node)); + return Max(depthright, depthleft) + 1; +} + + +//#define UTLTREE_PARANOID + +//----------------------------------------------------------------------------- +// Makes sure the tree is valid after every operation +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +bool CUtlRBTree::IsValid() const +{ + if (!Count()) + return true; + + if (m_LastAlloc == m_Elements.InvalidIterator()) + return false; + + if (!m_Elements.IsIdxValid(Root())) + return false; + + if (Parent(Root()) != InvalidIndex()) + return false; + +#ifdef UTLTREE_PARANOID + + // First check to see that mNumEntries matches reality. + // count items on the free list + int numFree = 0; + for (int i = m_FirstFree; i != InvalidIndex(); i = RightChild(i)) + { + ++numFree; + if (!m_Elements.IsIdxValid(i)) + return false; + } + + // iterate over all elements, looking for validity + // based on the self pointers + int nElements = 0; + int numFree2 = 0; + for (M::Iterator_t it = m_Elements.First(); it != m_Elements.InvalidIterator(); it = m_Elements.Next(it)) + { + I i = m_Elements.GetIndex(it); + if (!IsValidIndex(i)) + { + ++numFree2; + } + else + { + ++nElements; + + int right = RightChild(i); + int left = LeftChild(i); + if ((right == left) && (right != InvalidIndex())) + return false; + + if (right != InvalidIndex()) + { + if (!IsValidIndex(right)) + return false; + if (Parent(right) != i) + return false; + if (IsRed(i) && IsRed(right)) + return false; + } + + if (left != InvalidIndex()) + { + if (!IsValidIndex(left)) + return false; + if (Parent(left) != i) + return false; + if (IsRed(i) && IsRed(left)) + return false; + } + } + + if (it == m_LastAlloc) + break; + } + if (numFree2 != numFree) + return false; + + if (nElements != m_NumElements) + return false; + +#endif // UTLTREE_PARANOID + + return true; +} + + +//----------------------------------------------------------------------------- +// Sets the less func +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +void CUtlRBTree::SetLessFunc(const typename CUtlRBTree::LessFunc_t& func) +{ + if (!m_LessFunc) + { + m_LessFunc = func; + } + else if (Count() > 0) + { + // need to re-sort the tree here.... + //Assert(0); + } +} + + +//----------------------------------------------------------------------------- +// inserts a node into the tree +//----------------------------------------------------------------------------- + +// Inserts a node into the tree, doesn't copy the data in. +template < class T, class I, typename L, class M > +void CUtlRBTree::FindInsertionPosition(T const& insert, I& parent, bool& leftchild) +{ + //Assert(m_LessFunc); + + /* find where node belongs */ + I current = m_Root; + parent = InvalidIndex(); + leftchild = false; + while (current != InvalidIndex()) + { + parent = current; + if (m_LessFunc(insert, Element(current))) + { + leftchild = true; current = LeftChild(current); + } + else + { + leftchild = false; current = RightChild(current); + } + } +} + +template < class T, class I, typename L, class M > +I CUtlRBTree::Insert(T const& insert) +{ + // use copy constructor to copy it in + I parent; + bool leftchild; + FindInsertionPosition(insert, parent, leftchild); + I newNode = InsertAt(parent, leftchild); + CopyConstruct(&Element(newNode), insert); + return newNode; +} + + +template < class T, class I, typename L, class M > +void CUtlRBTree::Insert(const T* pArray, int nItems) +{ + while (nItems--) + { + Insert(*pArray++); + } +} + + +template < class T, class I, typename L, class M > +I CUtlRBTree::InsertIfNotFound(T const& insert) +{ + // use copy constructor to copy it in + I parent; + bool leftchild; + + I current = m_Root; + parent = InvalidIndex(); + leftchild = false; + while (current != InvalidIndex()) + { + parent = current; + if (m_LessFunc(insert, Element(current))) + { + leftchild = true; current = LeftChild(current); + } + else if (m_LessFunc(Element(current), insert)) + { + leftchild = false; current = RightChild(current); + } + else + // Match found, no insertion + return InvalidIndex(); + } + + I newNode = InsertAt(parent, leftchild); + CopyConstruct(&Element(newNode), insert); + return newNode; +} + + +//----------------------------------------------------------------------------- +// finds a node in the tree +//----------------------------------------------------------------------------- +template < class T, class I, typename L, class M > +I CUtlRBTree::Find(T const& search) const +{ + //Assert(m_LessFunc); + + I current = m_Root; + while (current != InvalidIndex()) + { + if (m_LessFunc(search, Element(current))) + current = LeftChild(current); + else if (m_LessFunc(Element(current), search)) + current = RightChild(current); + else + break; + } + return current; +} + + +//----------------------------------------------------------------------------- +// swap in place +//----------------------------------------------------------------------------- +template < class T, class I, typename L, class M > +void CUtlRBTree::Swap(CUtlRBTree< T, I, L >& that) +{ + m_Elements.Swap(that.m_Elements); + V_swap(m_LessFunc, that.m_LessFunc); + V_swap(m_Root, that.m_Root); + V_swap(m_NumElements, that.m_NumElements); + V_swap(m_FirstFree, that.m_FirstFree); + V_swap(m_pElements, that.m_pElements); + V_swap(m_LastAlloc, that.m_LastAlloc); + //Assert(IsValid()); + //Assert(m_Elements.IsValidIterator(m_LastAlloc) || (m_NumElements == 0 && m_FirstFree == InvalidIndex())); +} + +#pragma warning (pop) \ No newline at end of file diff --git a/Amalgam/src/SDK/Definitions/Misc/CUtlShared.h b/Amalgam/src/SDK/Definitions/Misc/CUtlShared.h new file mode 100644 index 00000000..1587703b --- /dev/null +++ b/Amalgam/src/SDK/Definitions/Misc/CUtlShared.h @@ -0,0 +1,21 @@ +#pragma once + +#define COMPILE_TIME_ASSERT( pred ) static_assert( pred, "Compile time assert constraint is not true: " #pred ) + +template +inline void Construct(T* pMemory) +{ + ::new(pMemory) T; +} + +template +inline void CopyConstruct(T* pMemory, T const& src) +{ + ::new(pMemory) T(src); +} + +template +inline void Destruct(T* pMemory) +{ + pMemory->~T(); +} \ No newline at end of file diff --git a/Amalgam/src/SDK/Definitions/Misc/CUtlVector.h b/Amalgam/src/SDK/Definitions/Misc/CUtlVector.h index 5f529e04..652d276a 100644 --- a/Amalgam/src/SDK/Definitions/Misc/CUtlVector.h +++ b/Amalgam/src/SDK/Definitions/Misc/CUtlVector.h @@ -1,5 +1,6 @@ #pragma once #include "CUtlMemory.h" +#include "CUtlShared.h" #include #include @@ -8,23 +9,11 @@ #pragma warning (disable : 4514) #pragma warning (disable : 26495) -template -inline void Construct(T* pMemory) -{ - ::new(pMemory) T; -} +#define FOR_EACH_VEC( vecName, iteratorName ) \ + for ( int iteratorName = 0; (vecName).IsUtlVector && iteratorName < (vecName).Count(); iteratorName++ ) -template -inline void CopyConstruct(T* pMemory, T const& src) -{ - ::new(pMemory) T(src); -} - -template -inline void Destruct(T* pMemory) -{ - pMemory->~T(); -} +#define FOR_EACH_VEC_BACK( vecName, iteratorName ) \ + for ( int iteratorName = (vecName).Count()-1; (vecName).IsUtlVector && iteratorName >= 0; iteratorName-- ) template> class CUtlVector diff --git a/Amalgam/src/SDK/Definitions/Misc/IAppSystem.h b/Amalgam/src/SDK/Definitions/Misc/IAppSystem.h index 8c2c9e90..04464f4b 100644 --- a/Amalgam/src/SDK/Definitions/Misc/IAppSystem.h +++ b/Amalgam/src/SDK/Definitions/Misc/IAppSystem.h @@ -16,4 +16,40 @@ public: virtual void* QueryInterface(const char* pInterfaceName) = 0; virtual InitReturnVal_t Init() = 0; virtual void Shutdown() = 0; +}; + +template +class CBaseAppSystem : public IInterface +{ +public: + virtual bool Connect(CreateInterfaceFn factory) = 0; + virtual void Disconnect() = 0; + virtual void* QueryInterface(const char* pInterfaceName) = 0; + virtual InitReturnVal_t Init() = 0; + virtual void Shutdown() = 0; +}; + +template +class CTier0AppSystem : public CBaseAppSystem +{ +public: + bool m_bIsPrimaryAppSystem; +}; + +template +class CTier1AppSystem : public CTier0AppSystem +{ + typedef CTier0AppSystem BaseClass; +}; + +template +class CTier2AppSystem : public CTier1AppSystem +{ + typedef CTier1AppSystem BaseClass; +}; + +template +class CTier3AppSystem : public CTier2AppSystem +{ + typedef CTier2AppSystem BaseClass; }; \ No newline at end of file diff --git a/Amalgam/src/SDK/Definitions/Misc/IStudioDataCache.h b/Amalgam/src/SDK/Definitions/Misc/IStudioDataCache.h new file mode 100644 index 00000000..5359e51f --- /dev/null +++ b/Amalgam/src/SDK/Definitions/Misc/IStudioDataCache.h @@ -0,0 +1,12 @@ +#pragma once +#include "IAppSystem.h" + +struct studiohdr_t; +struct vertexFileHeader_t; + +class IStudioDataCache : public IAppSystem +{ +public: + virtual bool VerifyHeaders(studiohdr_t* pStudioHdr) = 0; + virtual vertexFileHeader_t* CacheVertexData(studiohdr_t* pStudioHdr) = 0; +}; \ No newline at end of file diff --git a/Amalgam/src/SDK/Definitions/Misc/bitvec.h b/Amalgam/src/SDK/Definitions/Misc/bitvec.h index 7303d7b9..81271fee 100644 --- a/Amalgam/src/SDK/Definitions/Misc/bitvec.h +++ b/Amalgam/src/SDK/Definitions/Misc/bitvec.h @@ -2,13 +2,10 @@ #include "BaseTypes.h" #include -#ifndef BITVEC_H -#define BITVEC_H #define FORCEINLINE_TEMPLATE __forceinline #define COMPILE_TIME_ASSERT( pred ) static_assert( pred, "Compile time assert constraint is not true: " #pred ) #define Plat_FastMemset memset -#define PAD_NUMBER(number, boundary) \ - ( ((number) + ((boundary)-1)) / (boundary) ) * (boundary) +#define PAD_NUMBER(number, boundary) ( ((number) + ((boundary)-1)) / (boundary) ) * (boundary) class CBitVecAccessor { @@ -1422,9 +1419,4 @@ inline void CBitVecAccessor::operator=(int val) inline CBitVecAccessor::operator uint32() { return m_pDWords[m_iBit >> 5] & (1 << (m_iBit & 31)); -} - - -//============================================================================= - -#endif // BITVEC_H +} \ No newline at end of file diff --git a/Amalgam/src/SDK/Definitions/Types.h b/Amalgam/src/SDK/Definitions/Types.h index c0c635f0..cb7cd558 100644 --- a/Amalgam/src/SDK/Definitions/Types.h +++ b/Amalgam/src/SDK/Definitions/Types.h @@ -9,6 +9,7 @@ #include #include #include +#include class Vec2 { @@ -1035,6 +1036,11 @@ struct Color_t return { r, g, b, to }; } + inline Color_t Inverse() const + { + return Color_t(255 - r, 255 - g, 255 - b, a); + } + inline float Brightness(float flRed = 0.299f, float flGreen = 0.587f, float flBlue = 0.114f) const { return r * flRed + g * flGreen + b * flBlue; diff --git a/Amalgam/src/SDK/Helpers/TraceFilters/TraceFilters.cpp b/Amalgam/src/SDK/Helpers/TraceFilters/TraceFilters.cpp index 01fcf244..f54858fe 100644 --- a/Amalgam/src/SDK/Helpers/TraceFilters/TraceFilters.cpp +++ b/Amalgam/src/SDK/Helpers/TraceFilters/TraceFilters.cpp @@ -25,18 +25,18 @@ bool CTraceFilterHitscan::ShouldHitEntity(IHandleEntity* pHandleEntity, int nCon case ETFClassID::CFuncAreaPortalWindow: case ETFClassID::CFuncRespawnRoomVisualizer: case ETFClassID::CTFReviveMarker: return false; - case ETFClassID::CTFMedigunShield: return pEntity->m_iTeamNum() != iTeam; + case ETFClassID::CTFMedigunShield: return !(nContentsMask & CONTENTS_PLAYERCLIP) && pEntity->m_iTeamNum() != iTeam; case ETFClassID::CTFPlayer: case ETFClassID::CBaseObject: case ETFClassID::CObjectSentrygun: case ETFClassID::CObjectDispenser: - case ETFClassID::CObjectTeleporter: - if (iType != SKIP_CHECK && (iWeapon == WEAPON_INCLUDE ? bWeapon : !bWeapon)) - return iType == FORCE_HIT ? true : false; + case ETFClassID::CObjectTeleporter: + if (!(nContentsMask & CONTENTS_MOVEABLE)) return false; + if (iType != SKIP_CHECK && (iWeapon == WEAPON_INCLUDE ? bWeapon : !bWeapon)) return iType == FORCE_HIT ? true : false; return pEntity->m_iTeamNum() != iTeam; } - return true; + return nContentsMask & CONTENTS_SOLID; } TraceType_t CTraceFilterHitscan::GetTraceType() const { @@ -62,44 +62,36 @@ bool CTraceFilterCollideable::ShouldHitEntity(IHandleEntity* pHandleEntity, int switch (pEntity->GetClassID()) { - case ETFClassID::CBaseEntity: + case ETFClassID::CBaseEntity: return nContentsMask & CONTENTS_SOLID; + case ETFClassID::CFunc_LOD: case ETFClassID::CBaseDoor: case ETFClassID::CDynamicProp: case ETFClassID::CPhysicsProp: case ETFClassID::CPhysicsPropMultiplayer: - case ETFClassID::CFunc_LOD: case ETFClassID::CObjectCartDispenser: case ETFClassID::CFuncTrackTrain: case ETFClassID::CFuncConveyor: case ETFClassID::CTFGenericBomb: - case ETFClassID::CTFPumpkinBomb: return true; - case ETFClassID::CFuncRespawnRoomVisualizer: - if (nContentsMask & CONTENTS_PLAYERCLIP) - return pEntity->m_iTeamNum() != iTeam; - break; - case ETFClassID::CTFMedigunShield: - if (!(nContentsMask & CONTENTS_PLAYERCLIP)) - return pEntity->m_iTeamNum() != iTeam; - break; + case ETFClassID::CTFPumpkinBomb: return nContentsMask & CONTENTS_MOVEABLE; + case ETFClassID::CFuncRespawnRoomVisualizer: return nContentsMask & CONTENTS_PLAYERCLIP && pEntity->m_iTeamNum() != iTeam; + case ETFClassID::CTFMedigunShield: return !(nContentsMask & CONTENTS_PLAYERCLIP) && pEntity->m_iTeamNum() != iTeam; case ETFClassID::CTFPlayer: - if (iPlayer == PLAYER_ALL) - return true; - if (iPlayer == PLAYER_NONE) - return false; - if (iType != SKIP_CHECK && (iWeapon == WEAPON_INCLUDE ? bWeapon : !bWeapon)) - return iType == FORCE_HIT ? true : false; + if (!(nContentsMask & CONTENTS_MONSTER)) return false; + if (iPlayer == PLAYER_ALL) return true; + if (iPlayer == PLAYER_NONE) return false; + if (iType != SKIP_CHECK && (iWeapon == WEAPON_INCLUDE ? bWeapon : !bWeapon)) return iType == FORCE_HIT ? true : false; return pEntity->m_iTeamNum() != iTeam; case ETFClassID::CBaseObject: case ETFClassID::CObjectSentrygun: - case ETFClassID::CObjectDispenser: return iObject == OBJECT_ALL ? true : iObject == OBJECT_NONE ? false : pEntity->m_iTeamNum() != iTeam; - case ETFClassID::CObjectTeleporter: return true; + case ETFClassID::CObjectDispenser: return nContentsMask & CONTENTS_MOVEABLE && (iObject == OBJECT_ALL ? true : iObject == OBJECT_NONE ? false : pEntity->m_iTeamNum() != iTeam); + case ETFClassID::CObjectTeleporter: return nContentsMask & CONTENTS_MOVEABLE; //case ETFClassID::CTFBaseBoss: //case ETFClassID::CTFTankBoss: //case ETFClassID::CMerasmus: //case ETFClassID::CEyeballBoss: //case ETFClassID::CHeadlessHatman: - //case ETFClassID::CZombie: return bMisc; - case ETFClassID::CTFGrenadePipebombProjectile: return bMisc && pEntity->As()->m_iType() == TF_GL_MODE_REMOTE_DETONATE; + //case ETFClassID::CZombie: return nContentsMask & CONTENTS_MONSTER && bMisc; + case ETFClassID::CTFGrenadePipebombProjectile: return nContentsMask & CONTENTS_MOVEABLE && bMisc && pEntity->As()->m_iType() == TF_GL_MODE_REMOTE_DETONATE; } return false; @@ -114,22 +106,22 @@ bool CTraceFilterWorldAndPropsOnly::ShouldHitEntity(IHandleEntity* pHandleEntity if (!pHandleEntity || pHandleEntity == pSkip) return false; if (pHandleEntity->GetRefEHandle().GetSerialNumber() == (1 << 15)) - return pHandleEntity->GetRefEHandle().GetEntryIndex() != iTeam; // just use team variable since cliententitylist can give us nullptrs for some props for whatever reason + return nContentsMask & CONTENTS_SOLID && pHandleEntity->GetRefEHandle().GetEntryIndex() != iTeam; // team variable since cliententitylist can give nullptrs auto pEntity = reinterpret_cast(pHandleEntity); if (iTeam == -1) iTeam = pSkip ? pSkip->m_iTeamNum() : 0; switch (pEntity->GetClassID()) { - case ETFClassID::CBaseEntity: + case ETFClassID::CBaseEntity: return nContentsMask & CONTENTS_SOLID; + case ETFClassID::CFunc_LOD: case ETFClassID::CBaseDoor: case ETFClassID::CDynamicProp: case ETFClassID::CPhysicsProp: case ETFClassID::CPhysicsPropMultiplayer: - case ETFClassID::CFunc_LOD: case ETFClassID::CObjectCartDispenser: case ETFClassID::CFuncTrackTrain: - case ETFClassID::CFuncConveyor: return true; + case ETFClassID::CFuncConveyor: return nContentsMask & CONTENTS_MOVEABLE; case ETFClassID::CFuncRespawnRoomVisualizer: return nContentsMask & CONTENTS_PLAYERCLIP && pEntity->m_iTeamNum() != iTeam; } diff --git a/Amalgam/src/SDK/SDK.cpp b/Amalgam/src/SDK/SDK.cpp index 87ffe671..b585d1ee 100644 --- a/Amalgam/src/SDK/SDK.cpp +++ b/Amalgam/src/SDK/SDK.cpp @@ -155,6 +155,10 @@ bool SDK::IsGameWindowInFocus() return hWindow == GetForegroundWindow() || !hWindow; } +bool SDK::StdRandomBool() +{ + return StdRandomInt(0, 1); +} int SDK::StdRandomInt(int iMin, int iMax) { std::uniform_int_distribution iDistribution(iMin, iMax); diff --git a/Amalgam/src/SDK/SDK.h b/Amalgam/src/SDK/SDK.h index f9236cc7..6d8ce856 100644 --- a/Amalgam/src/SDK/SDK.h +++ b/Amalgam/src/SDK/SDK.h @@ -88,6 +88,7 @@ namespace SDK bool IsGameWindowInFocus(); inline std::default_random_engine Random = {}; + bool StdRandomBool(); int StdRandomInt(int iMin = 0, int iMax = VALVE_RAND_MAX); float StdRandomFloat(float flMin = 0.f, float flMax = 1.f); diff --git a/Amalgam/src/Utils/Math/Math.h b/Amalgam/src/Utils/Math/Math.h index 6ca7ae1c..b15b14ba 100644 --- a/Amalgam/src/Utils/Math/Math.h +++ b/Amalgam/src/Utils/Math/Math.h @@ -1,12 +1,16 @@ #pragma once #include "BaseMath.h" #include "../../SDK/Definitions/Types.h" +#include "../Hash/FNV1A.h" #undef min #undef max #define FLT_COMPARE(x, y) (fabsf(x - y) <= FLT_EPSILON * fmaxf(1.f, fmaxf(fabsf(x), fabsf(y)))) +#define DIST_EPSILON 0.03125f +#define CALC_EPSILON 0.001f + namespace Math { inline void ClampAngles(Vec3& v) @@ -387,4 +391,160 @@ namespace Math mOut[2][2] = mIn1[2][0] * mIn2[0][2] + mIn1[2][1] * mIn2[1][2] + mIn1[2][2] * mIn2[2][2]; mOut[2][3] = mIn1[2][0] * mIn2[0][3] + mIn1[2][1] * mIn2[1][3] + mIn1[2][2] * mIn2[2][3] + mIn1[2][3]; } + + inline void OffsetPolygon(std::vector& vVertices, const Vec3& vNormal, float flOffset) + { + for (auto& vVertex : vVertices) + vVertex += vNormal * flOffset; + } + + inline void ExpandPolygon(std::vector& vOldVertices, std::vector& vNewVertices, const Vec3& vNormal, float flOffset, const Vec3* pTarget = nullptr) + { + Vec3 vForward = (vNewVertices.front() - vNewVertices.back()).Normalized(); + Vec3 vRight = vForward.Cross(vNormal).Normalized(); + + vOldVertices = vNewVertices; + for (int i = 0, n = int(vNewVertices.size()); i < n; i++) + { + Vec3& vVertex1 = vOldVertices[i], &vVertex2 = vOldVertices[(i + 1) % n], &vVertex3 = vOldVertices[(i + 2) % n]; + + Vec2 vPoint1 = { vVertex1.Dot(vForward), vVertex1.Dot(vRight) }; + Vec2 vPoint2 = { vVertex2.Dot(vForward), vVertex2.Dot(vRight) }; + Vec2 vPoint3 = { vVertex3.Dot(vForward), vVertex3.Dot(vRight) }; + + Vec2 vDelta21 = vPoint2 - vPoint1; + Vec2 vDelta32 = vPoint3 - vPoint2; + + Vec2 vNormal1 = Vec2(vDelta21.y, -vDelta21.x).Normalized(); + Vec2 vNormal2 = Vec2(vDelta32.y, -vDelta32.x).Normalized(); + + Vec2 vBisect = Vec2(vNormal1.x + vNormal2.x, vNormal1.y + vNormal2.y).Normalized(); + float flDistance = flOffset / vNormal1.Dot(vBisect); + vBisect *= flDistance; + + if (!pTarget) + vNewVertices[(i + 1) % n] += vForward * vBisect.x + vRight * vBisect.y; + else + { + Vec3 vToTarget = *pTarget - vVertex2; + if (Vec3 vDisplacementX = vForward * vBisect.x; vDisplacementX.Dot(vToTarget) > 0.f) + vNewVertices[(i + 1) % n] += vDisplacementX; + if (Vec3 vDisplacementY = vRight * vBisect.y; vDisplacementY.Dot(vToTarget) > 0.f) + vNewVertices[(i + 1) % n] += vDisplacementY; + } + } + } + inline void ExpandPolygon(std::vector& vVertices, const Vec3& vNormal, float flOffset, const Vec3* pTarget = nullptr) + { + std::vector vTemporary; + ExpandPolygon(vTemporary, vVertices, vNormal, flOffset, pTarget); + } + + inline Vec3 ClosestPointOnLine(const Vec3& vPoint, const Vec3& vPoint1, const Vec3& vPoint2) + { + Vec3 vDelta = vPoint2 - vPoint1; + + float flRatio = std::clamp((vPoint - vPoint1).Dot(vDelta) / vDelta.Dot(vDelta), 0.f, 1.f); + + return vPoint1 + vDelta * flRatio; + } + + inline Vec3 ClosestPointOnTriangle(const Vec3& vPoint, const Vec3& vPoint1, const Vec3& vPoint2, const Vec3& vPoint3, bool* pInside = nullptr) + { + Vec3 vDelta21 = vPoint2 - vPoint1; + Vec3 vDelta31 = vPoint3 - vPoint1; + if (pInside) *pInside = false; + + Vec3 vDeltaPT = vPoint - vPoint1; + float flDot1 = vDelta21.Dot(vDeltaPT); + float flDot2 = vDelta31.Dot(vDeltaPT); + if (flDot1 <= 0 && flDot2 <= 0) + return vPoint1; + + vDeltaPT = vPoint - vPoint2; + float flDot3 = vDelta21.Dot(vDeltaPT); + float flDot4 = vDelta31.Dot(vDeltaPT); + if (flDot3 >= 0 && flDot4 <= flDot3) + return vPoint2; + + vDeltaPT = vPoint - vPoint3; + float flDot5 = vDelta21.Dot(vDeltaPT); + float flDot6 = vDelta31.Dot(vDeltaPT); + if (flDot6 >= 0 && flDot5 <= flDot6) + return vPoint3; + + float flSideC = flDot1 * flDot4 - flDot3 * flDot2; + if (flSideC <= 0 && flDot1 >= 0 && flDot3 <= 0) + { + float flRatio = flDot1 / (flDot1 - flDot3); + return vPoint1 + vDelta21 * flRatio; + } + + float flSideB = flDot5 * flDot2 - flDot1 * flDot6; + if (flSideB <= 0 && flDot2 >= 0 && flDot6 <= 0) + { + float flRatio = flDot2 / (flDot2 - flDot6); + return vPoint1 + vDelta31 * flRatio; + } + + float flSideA = flDot3 * flDot6 - flDot5 * flDot4; + if (flSideA <= 0 && (flDot4 - flDot3) >= 0 && (flDot5 - flDot6) >= 0) + { + float flRatio = (flDot4 - flDot3) / (flDot4 - flDot3 + flDot5 - flDot6); + return vPoint2 + (vPoint3 - vPoint2) * flRatio; + } + + float flDenominator = 1 / (flSideA + flSideB + flSideC); + float flV = flSideB * flDenominator; + float flW = flSideC * flDenominator; + if (pInside) *pInside = true; + return vPoint1 + vDelta21 * flV + vDelta31 * flW; + } + + inline Vec3 ClosestPointOnPolygon(const Vec3& vPoint, const std::vector& vVertices, const Vec3& vNormal, bool* pInside = nullptr) + { + float flDot = vNormal.Dot(vPoint - vVertices.front()); + Vec3 vProjection = vPoint - flDot * vNormal; + + for (int i = 0, n = int(vVertices.size()); i < n; i++) + { + const Vec3& vVertex1 = vVertices[i], &vVertex2 = vVertices[(i + 1) % n]; + + Vec3 vEdge = vVertex2 - vVertex1; + Vec3 vToProjection = vProjection - vVertex1; + + if (vToProjection.Cross(vEdge).Dot(vNormal) < 0) + goto outside; + } + if (pInside) *pInside = true; + return vProjection; + + outside: + float flLowestDistance = std::numeric_limits::max(); + for (int i = 0, n = int(vVertices.size()); i < n; i++) + { + const Vec3& vVertex1 = vVertices[i], &vVertex2 = vVertices[(i + 1) % n]; + + Vec3 vLine = ClosestPointOnLine(vPoint, vVertex1, vVertex2); + float flDistance = vLine.DistToSqr(vPoint); + if (flDistance < flLowestDistance) + vProjection = vLine, flLowestDistance = flDistance; + } + if (pInside) *pInside = false; + return vProjection; + } + + // note: not clamped + inline float FullFraction(const Vec3& vDir, const CGameTrace& trace) + { + if (!trace.DidHit() || FNV1A::Hash32(trace.surface.name) == FNV1A::Hash32Const("**displacement**")) + return trace.fraction; + + float flFractionEpsilon = DIST_EPSILON / vDir.Dot(trace.plane.normal); + return trace.fraction - flFractionEpsilon; + } + inline float FullFraction(const Vec3& vStart, const Vec3& vEnd, const CGameTrace& trace) + { + return FullFraction(vEnd - vStart, trace); + } } \ No newline at end of file