using System.Numerics; using System.Runtime.InteropServices; using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Entities.Constants; using CounterStrikeSharp.API.Modules.Memory; using CounterStrikeSharp.API.Modules.Utils; using Vector = CounterStrikeSharp.API.Modules.Utils.Vector; namespace RayTraceAPI { #region Native Structs (matching C++ layout exactly) [Flags] public enum InteractionLayers: ulong { None = 0, Solid = 0x1, Hitboxes = 0x2, Trigger = 0x4, Sky = 0x8, PlayerClip = 0x10, NPCClip = 0x20, BlockLOS = 0x40, BlockLight = 0x80, Ladder = 0x100, Pickup = 0x200, BlockSound = 0x400, NoDraw = 0x800, Window = 0x1000, PassBullets = 0x2000, WorldGeometry = 0x4000, Water = 0x8000, Slime = 0x10000, TouchAll = 0x20000, Player = 0x40000, NPC = 0x80000, Debris = 0x100000, Physics_Prop = 0x200000, NavIgnore = 0x400000, NavLocalIgnore = 0x800000, PostProcessingVolume = 0x1000000, UnusedLayer3 = 0x2000000, CarriedObject = 0x4000000, PushAway = 0x8000000, ServerEntityOnClient = 0x10000000, CarriedWeapon = 0x20000000, StaticLevel = 0x40000000, csgo_team1 = 0x80000000, csgo_team2 = 0x100000000, csgo_grenadeclip = 0x200000000, csgo_droneclip = 0x400000000, csgo_moveable = 0x800000000, csgo_opaque = 0x1000000000, csgo_monster = 0x2000000000, csgo_thrown_grenade = 0x8000000000, MASK_SHOT_PHYSICS = Solid | PlayerClip | Window | PassBullets | Player | NPC | Physics_Prop, MASK_SHOT_HITBOX = Hitboxes | Player | NPC, MASK_SHOT_FULL = MASK_SHOT_PHYSICS | Hitboxes, MASK_WORLD_ONLY = Solid | Window | PassBullets, MASK_GRENADE = Solid | Window | Physics_Prop | PassBullets, MASK_BRUSH_ONLY = Solid | Window, MASK_PLAYER_MOVE = Solid | Window | PlayerClip | PassBullets, MASK_NPC_MOVE = Solid | Window | NPCClip | PassBullets } [StructLayout(LayoutKind.Explicit, Size = 32)] public unsafe struct TraceOptions { [FieldOffset(0)] public ulong InteractsAs; [FieldOffset(8)] public ulong InteractsWith; [FieldOffset(16)] public ulong InteractsExclude; [FieldOffset(24)] public int DrawBeam; public TraceOptions() { InteractsAs = 0; InteractsWith = (ulong)InteractionLayers.MASK_SHOT_PHYSICS; InteractsExclude = 0; DrawBeam = 0; } public TraceOptions(InteractionLayers interactsAs, InteractionLayers interactsWith, InteractionLayers interactsExclude = 0, bool drawBeam = false) { InteractsAs = (ulong)interactsAs; InteractsWith = (ulong)interactsWith; InteractsExclude = (ulong)interactsExclude; DrawBeam = drawBeam ? 1 : 0; } } [StructLayout(LayoutKind.Explicit, Size = 136)] public unsafe struct TraceResult { [FieldOffset(0)] public float StartPosX; [FieldOffset(4)] public float StartPosY; [FieldOffset(8)] public float StartPosZ; [FieldOffset(12)] public float EndPosX; [FieldOffset(16)] public float EndPosY; [FieldOffset(20)] public float EndPosZ; [FieldOffset(24)] public float HitPointX; [FieldOffset(28)] public float HitPointY; [FieldOffset(32)] public float HitPointZ; [FieldOffset(36)] public float NormalX; [FieldOffset(40)] public float NormalY; [FieldOffset(44)] public float NormalZ; [FieldOffset(48)] public float Fraction; [FieldOffset(52)] public float HitOffset; [FieldOffset(56)] public int TriangleIndex; [FieldOffset(60)] public int HitboxBoneIndex; [FieldOffset(64)] public int Contents; [FieldOffset(68)] public int RayType; [FieldOffset(72)] public int AllSolid; [FieldOffset(76)] public int ExactHitPoint; // pointer section [FieldOffset(80)] public nint HitEntityPtr; [FieldOffset(88)] public nint HitboxPtr; [FieldOffset(96)] public nint SurfacePropsPtr; [FieldOffset(104)] public nint BodyHandle; [FieldOffset(112)] public nint ShapeHandle; // thread safe heap pointer section [FieldOffset(120)] public nint BodyTransformPtr; [FieldOffset(128)] public nint ShapeAttributesPtr; public unsafe ref T GetRef(nint ptr) where T : unmanaged { if (ptr == 0) throw new NullReferenceException(); return ref *(T*)ptr; } public T? GetObject(nint ptr) where T : class { if (ptr == 0) return null; return (T)Activator.CreateInstance(typeof(T), ptr)!; } public Vector3 StartPos => new(StartPosX, StartPosY, StartPosZ); public Vector3 EndPos => new(EndPosX, EndPosY, EndPosZ); public Vector3 HitPoint => new(HitPointX, HitPointY, HitPointZ); public Vector3 Normal => new(NormalX, NormalY, NormalZ); public bool DidHit => Fraction < 1.0f; public bool IsAllSolid => AllSolid != 0; public bool HasExactHit => ExactHitPoint != 0; public CEntityInstance? HitEntity => GetObject(HitEntityPtr); } #endregion public interface CRayTraceInterface { public unsafe bool TraceShape(Vector origin, QAngle angles, CBaseEntity? ignoreEntity, TraceOptions options, out TraceResult result); public unsafe bool TraceEndShape(Vector origin, Vector endOrigin, CBaseEntity? ignoreEntity, TraceOptions options, out TraceResult result); public unsafe bool TraceHullShape(Vector vecStart, Vector vecEnd, Vector hullMins, Vector hullMaxs, CBaseEntity? ignoreEntity, TraceOptions options, out TraceResult result); } }