using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core.Capabilities; using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.utils; using RayTraceAPI; using System.Drawing; using System.Numerics; using TraceOptions = RayTraceAPI.TraceOptions; using Vector = CounterStrikeSharp.API.Modules.Utils.Vector; namespace src.utils { public static class RayTrace { private static PluginCapability RayTraceInterface { get; } = new("raytrace:craytraceinterface"); public static CustomTraceResult? TraceShape(CCSPlayerController player, Vector startPos, Vector endPos, ulong? mask = null, ulong? contents = null) { if (player == null || !player.IsValid) return null; var playerPawn = player.PlayerPawn?.Value; if (playerPawn == null || !playerPawn.IsValid || playerPawn.Handle == IntPtr.Zero || playerPawn.Collision == null || playerPawn.LifeState != (byte)LifeState_t.LIFE_ALIVE || playerPawn.CBodyComponent?.SceneNode == null) return null; CRayTraceInterface? rayTrace; try { rayTrace = RayTraceInterface.Get(); } catch { return null; } if (rayTrace == null) return null; if (mask == null) { try { if (playerPawn.Collision?.CollisionAttribute != null) { mask = playerPawn.Collision.CollisionAttribute.InteractsWith | (ulong)InteractionLayers.Hitboxes; mask &= ~(ulong)InteractionLayers.PlayerClip; } else mask = (ulong)(InteractionLayers.Solid | InteractionLayers.Hitboxes); } catch { mask = (ulong)(InteractionLayers.Solid | InteractionLayers.Hitboxes); } } contents ??= 0; bool drawBeam = Config.LoadedConfig.TraceRayBeam; TraceOptions options = new() { InteractsWith = (ulong)mask, InteractsExclude = (ulong)contents, DrawBeam = drawBeam == true ? 1 : 0, }; Vector mins = new(-0.5f, -0.5f, -0.5f); Vector maxs = new(0.5f, 0.5f, 0.5f); TraceResult result = default; try { rayTrace.TraceHullShape(startPos, endPos, mins, maxs, playerPawn, options, out result); } catch (Exception ex) { Console.WriteLine($"[jRandomSkills] A memory error was caught during RayTrace: {ex.Message}"); return null; } return new CustomTraceResult(result, startPos, (ulong)mask, (ulong)contents, drawBeam); } public static CustomTraceResult? EyeTrace(CCSPlayerController player) { if (player == null || !player.IsValid) return null; var playerPawn = player.PlayerPawn?.Value; if (playerPawn == null || !playerPawn.IsValid || playerPawn.AbsOrigin == null) return null; var playerInfo = PlayerManager.GetPlayerByIndex(player.Index); if (playerInfo == null) return null; float maxDistance = SkillsInfo.GetValue(playerInfo.Skill, "maxDistance"); if (maxDistance == 0) maxDistance = 4096f; Vector startPos = new(playerPawn.AbsOrigin.X, playerPawn.AbsOrigin.Y, playerPawn.AbsOrigin.Z + playerPawn.ViewOffset.Z); Vector endPos = startPos + SkillUtils.GetForwardVector(playerPawn.EyeAngles) * maxDistance; return TraceShape(player, startPos, endPos); } public static CustomTraceResult? TraceHullShape(Vector startPos, Vector endPos, CCSPlayerController player, Vector? mins = null, Vector? maxs = null, ulong? mask = null, ulong? contents = null, QAngle? angle = null) { if (player == null || !player.IsValid) return null; var playerPawn = player.PlayerPawn?.Value; if (playerPawn == null || !playerPawn.IsValid || playerPawn.Handle == IntPtr.Zero || playerPawn.Collision == null || playerPawn.LifeState != (byte)LifeState_t.LIFE_ALIVE || playerPawn.CBodyComponent?.SceneNode == null) return null; CRayTraceInterface? rayTrace; try { rayTrace = RayTraceInterface.Get(); } catch { return null; } if (rayTrace == null) return null; Vector safeMins = mins ?? playerPawn.Collision.Mins; Vector safeMaxs = maxs ?? playerPawn.Collision.Maxs; ulong safeMask = mask ?? playerPawn.Collision.CollisionAttribute.InteractsWith; ulong safeContents = contents ?? 0; bool drawBeam = Config.LoadedConfig.TraceRayBeam; TraceResult result = default; TraceOptions options = new() { InteractsWith = safeMask, InteractsExclude = safeContents, DrawBeam = drawBeam == true ? 1 : 0, }; CEntityInstance? entityToIgnore = (playerPawn.LifeState == (byte)LifeState_t.LIFE_ALIVE) ? playerPawn : null; try { rayTrace.TraceHullShape(startPos, endPos, safeMins, safeMaxs, entityToIgnore, options, out result); } catch (Exception) { return null; } if (drawBeam) { angle ??= new(0, playerPawn.EyeAngles.Y, 0); DrawBoxEdges(startPos, endPos, angle, safeMins, safeMaxs, Color.Green); } return new CustomTraceResult(result, startPos, safeMask, safeContents, drawBeam); } private static void DrawBoxEdges(Vector start, Vector end, QAngle angles, Vector mins, Vector maxs, Color color) { AngleVectors(angles, out Vector forward, out Vector right, out Vector up); float halfLength = (maxs.X - mins.X) / 2.0f; Vector visualStart = start - (forward * halfLength); Vector visualEnd = end + (forward * halfLength); Vector GetVertex(Vector center, float rx, float ux) { return center + (right * rx) + (up * ux); } Vector[] s = [ GetVertex(visualStart, mins.Y, mins.Z), GetVertex(visualStart, maxs.Y, mins.Z), GetVertex(visualStart, maxs.Y, maxs.Z), GetVertex(visualStart, mins.Y, maxs.Z) ]; Vector[] e = [ GetVertex(visualEnd, mins.Y, mins.Z), GetVertex(visualEnd, maxs.Y, mins.Z), GetVertex(visualEnd, maxs.Y, maxs.Z), GetVertex(visualEnd, mins.Y, maxs.Z) ]; for (int i = 0; i < 4; i++) { int next = (i + 1) % 4; CreateBeamLine(s[i], s[next], color); CreateBeamLine(e[i], e[next], color); CreateBeamLine(s[i], e[i], color); } } private static void AngleVectors(QAngle angles, out Vector forward, out Vector right, out Vector up) { float sp, sy, cp, cy, sr, cr; float pitch = angles.X * (MathF.PI / 180.0f); float yaw = angles.Y * (MathF.PI / 180.0f); float roll = angles.Z * (MathF.PI / 180.0f); sp = MathF.Sin(pitch); cp = MathF.Cos(pitch); sy = MathF.Sin(yaw); cy = MathF.Cos(yaw); sr = MathF.Sin(roll); cr = MathF.Cos(roll); forward = new Vector(cp * cy, cp * sy, -sp); right = new Vector(-1 * sr * sp * cy + cr * sy, -1 * sr * sp * sy - cr * cy, -1 * sr * cp); up = new Vector(cr * sp * cy + sr * sy, cr * sp * sy - sr * cy, cr * cp); } private static void CreateBeamLine(Vector start, Vector end, Color color) { var beam = Utilities.CreateEntityByName("env_beam"); if (beam == null || !beam.IsValid) return; beam.Render = color; beam.Width = 1.3f; beam.Teleport(start, new QAngle(0, 0, 0), new Vector(0, 0, 0)); beam.EndPos.X = end.X; beam.EndPos.Y = end.Y; beam.EndPos.Z = end.Z; beam.DispatchSpawn(); } public static float Distance(this CustomTraceResult result) { return Vector3.Distance(result.StartPos, result.EndPos); } public static Vector3 Direction(this CustomTraceResult result) { return Vector3.Normalize(result.EndPos - result.Normal); } public static bool HitEntityByDesignerName(this CustomTraceResult result, out T? entity, string designerName, DesignerNameMatchType matchType = DesignerNameMatchType.Equals) where T : CEntityInstance { T? val = (T?)Activator.CreateInstance(typeof(T), result.HitEntity); if ((object?)val != null && matchType switch { DesignerNameMatchType.Equals => val.DesignerName == designerName, DesignerNameMatchType.StartsWith => val.DesignerName.StartsWith(designerName, StringComparison.OrdinalIgnoreCase), DesignerNameMatchType.EndsWith => val.DesignerName.EndsWith(designerName, StringComparison.OrdinalIgnoreCase), _ => false, }) { entity = val; return true; } entity = null; return false; } public static bool HitEntity(this CustomTraceResult result, out CBaseEntity? entity) { CEntityInstance entityInstance = new(result.HitEntity); if (string.IsNullOrEmpty(entityInstance.DesignerName)) { entity = null; return false; } entity = entityInstance.As(); return entity != null; } public static bool HitPlayer(this CustomTraceResult result, out CCSPlayerController? player) { if (result.HitEntityByDesignerName(out CCSPlayerPawn? entity, "player")) { player = entity?.OriginalController.Value; return player != null; } player = null; return false; } public static bool HitWeapon(this CustomTraceResult result, out CBasePlayerWeapon? weapon) { return result.HitEntityByDesignerName(out weapon, "weapon_", DesignerNameMatchType.StartsWith); } public static bool HitChicken(this CustomTraceResult result, out CChicken? chicken) { return result.HitEntityByDesignerName(out chicken, "chicken"); } public static bool HitButton(this CustomTraceResult result, out CBaseButton? button) { return result.HitEntityByDesignerName(out button, "func_door"); } public static bool HitBuyzone(this CustomTraceResult result, out CBuyZone? buyzone) { return result.HitEntityByDesignerName(out buyzone, "func_buyzone"); } public static bool HitSky(this CustomTraceResult result, out CEnvSky? sky) { return result.HitEntityByDesignerName(out sky, "env_sky"); } public static bool HitDoor(this CustomTraceResult result, out CBaseDoor? door) { return result.HitEntityByDesignerName(out door, "func_door"); } public static bool HitDoor(this CustomTraceResult result, out CRotDoor? door) { return result.HitEntityByDesignerName(out door, "func_door_rotating"); } public static bool HitLadder(this CustomTraceResult result, out CFuncLadder? ladder) { return result.HitEntityByDesignerName(out ladder, "func_ladder"); } public static bool HitGrenade(this CustomTraceResult result, out CBaseCSGrenade? grenade) { return result.HitEntityByDesignerName(out grenade, "grenade"); } public static bool HitPlantedC4(this CustomTraceResult result, out CPlantedC4? c4) { return result.HitEntityByDesignerName(out c4, "planted_c4"); } public static bool HitPointWorldText(this CustomTraceResult result, out CPointWorldText? pointWorldText) { return result.HitEntityByDesignerName(out pointWorldText, "point_worldtext"); } public static bool HitC4(this CustomTraceResult result, out CC4? c4) { return result.HitEntityByDesignerName(out c4, "weapon_c4"); } public static bool HitWorld(this CustomTraceResult result, out CWorld? world) { return result.HitEntityByDesignerName(out world, "worldent"); } public enum DesignerNameMatchType { Equals, StartsWith, EndsWith } } }