// // Created by Michal Přikryl on 30.08.2025. // Copyright (c) 2025 slynxcz. All rights reserved. // #include "raytrace.h" #include #include "schema/CBaseModelEntity.h" #include "vectorextends.h" #include "dynlibutils/memaddr.h" #include "dynlibutils/module.h" #include "colors.h" #include "log.h" namespace RayTracePlugin::RayTrace { CRayTrace g_CRayTrace; using TraceShapeFn = bool(*)(void* pThis, Ray_t& ray, Vector& start, Vector& end, CTraceFilter* filter, CGameTrace* trace); static TraceShapeFn s_TraceShape = nullptr; bool Initialize() { void* pCNavPhysicsInterfaceVTable = DynLibUtils::CModule(shared::g_pServer).GetVirtualTableByName("CNavPhysicsInterface"); if (!pCNavPhysicsInterfaceVTable) { FP_ERROR("Failed to find CNavPhysicsInterface vtable!"); return false; } auto table = static_cast(pCNavPhysicsInterfaceVTable); s_TraceShape = reinterpret_cast(table[shared::g_pGameConfig->GetOffset("CNavPhysicsInterface_TraceShape")]); return true; } static void DrawBeam(const Vector& start, const Vector& end, const Color& color) { CBeam* beam = UTIL_CreateEntityByName("env_beam"); if (!beam) return; beam->m_clrRender().SetColor(color.r(), color.g(), color.b(), color.a()); beam->m_fWidth() = 1.5f; beam->m_nRenderMode() = kRenderNormal; beam->m_nRenderFX() = kRenderFxNone; beam->Teleport(&start, &VectorExtends::RotationZero, &VectorExtends::VectorZero); beam->m_vecEndPos() = end; beam->DispatchSpawn(); } std::optional TraceShapeEx( const Vector& start, const Vector& end, CTraceFilter& filterInc, Ray_t rayInc) { if (!s_TraceShape) { FP_ERROR("CNavPhysicsInterface::TraceShape is not bound!"); return std::nullopt; } CGameTrace tr{}; Vector startCopy = start; Vector endCopy = end; s_TraceShape(nullptr, rayInc, startCopy, endCopy, &filterInc, &tr); TraceResult r{}; r.StartPosX = tr.m_vStartPos.x; r.StartPosY = tr.m_vStartPos.y; r.StartPosZ = tr.m_vStartPos.z; r.EndPosX = tr.m_vEndPos.x; r.EndPosY = tr.m_vEndPos.y; r.EndPosZ = tr.m_vEndPos.z; r.HitPointX = tr.m_vHitPoint.x; r.HitPointY = tr.m_vHitPoint.y; r.HitPointZ = tr.m_vHitPoint.z; r.NormalX = tr.m_vHitNormal.x; r.NormalY = tr.m_vHitNormal.y; r.NormalZ = tr.m_vHitNormal.z; r.Fraction = tr.m_flFraction; r.HitOffset = tr.m_flHitOffset; r.TriangleIndex = tr.m_nTriangle; r.HitboxBoneIndex = tr.m_nHitboxBoneIndex; r.Contents = tr.m_nContents; r.RayType = static_cast(tr.m_eRayType); r.AllSolid = tr.m_bStartInSolid; r.ExactHitPoint = tr.m_bExactHitPoint; r.HitEntity = tr.m_pEnt; r.Hitbox = const_cast(tr.m_pHitbox); r.SurfaceProps = const_cast(tr.m_pSurfaceProperties); r.BodyHandle = tr.m_hBody; r.ShapeHandle = tr.m_hShape; r.BodyTransform = tr.m_BodyTransform; r.ShapeAttributes = tr.m_ShapeAttributes; return r; } std::optional TraceShape( const Vector& origin, const QAngle& viewangles, CEntityInstance* ignoreEntity, const TraceOptions* opts) { Vector forward; AngleVectors(viewangles, &forward); Vector endOrigin{ origin.x + forward.x * 8192.f, origin.y + forward.y * 8192.f, origin.z + forward.z * 8192.f }; CTraceFilterEx filter = ignoreEntity ? CTraceFilterEx(static_cast(ignoreEntity)) : CTraceFilterEx(); filter.m_nInteractsAs = 0; filter.m_nInteractsWith = static_cast(MASK_SHOT_PHYSICS); filter.m_nInteractsExclude = 0; if (opts) { if (opts->InteractsAs != 0) filter.m_nInteractsAs = opts->InteractsAs; if (opts->InteractsWith != static_cast(MASK_SHOT_PHYSICS)) filter.m_nInteractsWith = opts->InteractsWith; if (opts->InteractsExclude != 0) filter.m_nInteractsExclude = opts->InteractsExclude; } Ray_t ray; auto res = TraceShapeEx(origin, endOrigin, filter, ray); if (opts && opts->DrawBeam) { Color col = res.has_value() ? colors::Red().ToValveColor() : colors::Green().ToValveColor(); DrawBeam(origin, res ? Vector(res->EndPosX, res->EndPosY, res->EndPosZ) : endOrigin, col); } return res; } std::optional TraceEndShape( const Vector& origin, const Vector& endOrigin, CEntityInstance* ignoreEntity, const TraceOptions* opts) { CTraceFilterEx filter = ignoreEntity ? CTraceFilterEx(static_cast(ignoreEntity)) : CTraceFilterEx(); filter.m_nInteractsAs = 0; filter.m_nInteractsWith = static_cast(MASK_SHOT_PHYSICS); filter.m_nInteractsExclude = 0; if (opts) { if (opts->InteractsAs != 0) filter.m_nInteractsAs = opts->InteractsAs; if (opts->InteractsWith != static_cast(MASK_SHOT_PHYSICS)) filter.m_nInteractsWith = opts->InteractsWith; if (opts->InteractsExclude != 0) filter.m_nInteractsExclude = opts->InteractsExclude; } Ray_t ray; auto res = TraceShapeEx(origin, endOrigin, filter, ray); if (opts && opts->DrawBeam) { Color col = res.has_value() ? colors::Red().ToValveColor() : colors::Green().ToValveColor(); DrawBeam(origin, res ? Vector(res->EndPosX, res->EndPosY, res->EndPosZ) : endOrigin, col); } return res; } std::optional TraceHullShape(const Vector &vecStart, const Vector &vecEnd, const Vector &hullMins, const Vector &hullMaxs, CEntityInstance *ignoreEntity, const TraceOptions *opts) { CTraceFilterEx filter = ignoreEntity ? CTraceFilterEx(static_cast(ignoreEntity)) : CTraceFilterEx(); filter.m_nInteractsAs = 0; filter.m_nInteractsWith = static_cast(MASK_SHOT_PHYSICS); filter.m_nInteractsExclude = 0; if (opts) { if (opts->InteractsAs != 0) filter.m_nInteractsAs = opts->InteractsAs; if (opts->InteractsWith != static_cast(MASK_SHOT_PHYSICS)) filter.m_nInteractsWith = opts->InteractsWith; if (opts->InteractsExclude != 0) filter.m_nInteractsExclude = opts->InteractsExclude; } Ray_t ray; ray.Init(hullMins, hullMaxs); auto res = TraceShapeEx(vecStart, vecEnd, filter, ray); if (opts && opts->DrawBeam) { Color col = res.has_value() ? colors::Red().ToValveColor() : colors::Green().ToValveColor(); DrawBeam(vecStart, res ? Vector(res->EndPosX, res->EndPosY, res->EndPosZ) : vecEnd, col); } return res; } }