using System.Collections.Generic; using System.Linq; using System.Reflection; using EFT.UI; using stupid.solutions.Utils; using UnityEngine; namespace stupid.solutions.Features.ESP; public class TracerESP : MonoBehaviour { private class HitMarker { public Vector3 position; public float displayTime; public float damage; public Vector3 floatingOffset; public bool isHeadshot; public int playerId; public HitMarker(Vector3 position, float displayTime, float damage, bool isHeadshot, int playerId) { this.position = position; this.displayTime = displayTime; this.damage = damage; floatingOffset = Vector3.zero; this.isHeadshot = isHeadshot; this.playerId = playerId; } } private class Tracer { public List positions = new List(); public int fireIndex; public int fragmentIndex; public int randomSeed; public float startTime; public bool hitTarget; public bool hitMarkerDrawn; public Vector3? startPosition; public bool startUsed; public Tracer(int fireIndex, int fragmentIndex, int randomSeed, Vector3? startPosition = null) { this.fireIndex = fireIndex; this.fragmentIndex = fragmentIndex; this.randomSeed = randomSeed; this.startPosition = startPosition; startUsed = false; startTime = Time.time; hitTarget = false; hitMarkerDrawn = false; } public void AddPosition(Vector3 position) { if (!hitTarget) { positions.Add(position); } } } public bool hitmarkerloaded; private static Dictionary validProjectiles = new Dictionary(); private static List activeHitMarkers = new List(); private Dictionary playerAliveStatus = new Dictionary(); private static Dictionary damageTimers = new Dictionary(); private HashSet blacklistedSeeds = new HashSet(); private bool clearalivestatus; private float hitMarkerDuration = 1f; private static float hitMarkerVolume = 0.2f; private float damageAccumulationTime = 0.5f; private static Texture2D hitMarkerTexture; private bool isReactionOnShotHooked; public static TestHook ReactionOnShotHook; private static MethodInfo originalMethod; public void Start() { ConsoleScreen.Log("Init GL material...."); GameUtils.InitMaterial(); } public void Update() { if (!Settings.tracers && !Settings.HitMarkers) { return; } if (Main.GameWorld == null && clearalivestatus) { playerAliveStatus.Clear(); clearalivestatus = false; } else if (Main.GameWorld != null && !clearalivestatus) { clearalivestatus = true; } if (Main.GameWorld == null) { return; } if (!hitmarkerloaded && Main.bundle != null) { hitMarkerTexture = Main.bundle.LoadAsset("hitmarkerImage"); hitmarkerloaded = hitMarkerTexture != null; ConsoleScreen.Log((hitMarkerTexture != null) ? "Hitmarker image loaded successfully!" : "Failed to load hitmarker image!"); } if (hitMarkerTexture == null) { return; } if (Main.OnlineGamePlayers.Count > 0) { if (!isReactionOnShotHooked && Settings.HitMarkers) { isReactionOnShotHooked = true; } _F163 obj = Main.GameWorld?.ClientBallisticCalculator; int activeShotsCount = obj.ActiveShotsCount; if (obj == null) { return; } for (int i = 0; i < activeShotsCount; i++) { _F16A activeShot = obj.GetActiveShot(i); if (activeShot == null) { continue; } if (activeShot.PlayerProfileID == Main.LocalPlayer.ProfileId && !validProjectiles.ContainsKey(activeShot.RandomSeed)) { Tracer value = new Tracer(activeShot.FireIndex, activeShot.FragmentIndex, activeShot.RandomSeed, activeShot.StartPosition); validProjectiles[activeShot.RandomSeed] = value; } if (validProjectiles.ContainsKey(activeShot.RandomSeed)) { Tracer tracer = validProjectiles[activeShot.RandomSeed]; if (tracer.startPosition.HasValue && !tracer.startUsed) { tracer.positions.Add(tracer.startPosition.Value); tracer.startUsed = true; } tracer.AddPosition(activeShot.CurrentPosition); } } activeHitMarkers.RemoveAll((HitMarker marker) => Time.time - marker.displayTime > hitMarkerDuration); validProjectiles = validProjectiles.Where((KeyValuePair keyValuePair) => Time.time - keyValuePair.Value.startTime < 5f).ToDictionary((KeyValuePair keyValuePair) => keyValuePair.Key, (KeyValuePair keyValuePair) => keyValuePair.Value); return; } _F163 obj2 = Main.GameWorld?.SharedBallisticsCalculator; int activeShotsCount2 = obj2.ActiveShotsCount; if (obj2 == null) { return; } for (int num = 0; num < activeShotsCount2; num++) { _F16A activeShot2 = obj2.GetActiveShot(num); if (activeShot2 == null) { continue; } if (activeShot2.PlayerProfileID == Main.LocalPlayer.ProfileId && !validProjectiles.ContainsKey(activeShot2.RandomSeed)) { Tracer value2 = new Tracer(activeShot2.FireIndex, activeShot2.FragmentIndex, activeShot2.RandomSeed, activeShot2.StartPosition); validProjectiles[activeShot2.RandomSeed] = value2; } if (!validProjectiles.ContainsKey(activeShot2.RandomSeed)) { continue; } Tracer tracer2 = validProjectiles[activeShot2.RandomSeed]; if (tracer2.startPosition.HasValue && !tracer2.startUsed) { tracer2.positions.Add(tracer2.startPosition.Value); tracer2.startUsed = true; } tracer2.AddPosition(activeShot2.CurrentPosition); if (Main.OnlineGamePlayers.Count >= 1 || !activeShot2.HasAchievedTarget || tracer2.hitMarkerDrawn) { continue; } tracer2.hitTarget = true; tracer2.hitMarkerDrawn = true; if (!(activeShot2.HittedBallisticCollider is BodyPartCollider { Player: var player } bodyPartCollider)) { continue; } bool flag = bodyPartCollider.BodyPartType == EBodyPart.Head; if (player == null) { continue; } int playerId = player.ProfileId.GetHashCode(); if (playerAliveStatus.ContainsKey(playerId) && !playerAliveStatus[playerId]) { continue; } float current = player.HealthController.GetBodyPartHealth(EBodyPart.Common).Current; float num2 = player.HealthController.GetBodyPartHealth(EBodyPart.Common).Maximum - current; if (damageTimers.ContainsKey(playerId) && Time.time - damageTimers[playerId] < damageAccumulationTime) { HitMarker hitMarker = activeHitMarkers.FirstOrDefault((HitMarker m) => m.playerId == playerId); if (hitMarker != null) { hitMarker.damage += num2; hitMarker.displayTime = Time.time; } } else { activeHitMarkers.Add(new HitMarker(activeShot2.HitPoint, Time.time, num2, flag, playerId)); damageTimers[playerId] = Time.time; } if (Settings.HitMarkers) { if (flag && Main.headshotsound != null) { Main.audioSourcehit.clip = Main.headshotsound; Main.audioSourcehit.volume = hitMarkerVolume; Main.audioSourcehit.Play(); } else if (Main.hitMarkerSound != null) { Main.audioSourcehit.clip = Main.hitMarkerSound; Main.audioSourcehit.volume = hitMarkerVolume; Main.audioSourcehit.Play(); } } bool isAlive = player.HealthController.IsAlive; playerAliveStatus[playerId] = isAlive; if (!isAlive) { damageTimers.Remove(playerId); } } activeHitMarkers.RemoveAll((HitMarker marker) => Time.time - marker.displayTime > hitMarkerDuration); validProjectiles = validProjectiles.Where((KeyValuePair keyValuePair) => Time.time - keyValuePair.Value.startTime < 5f).ToDictionary((KeyValuePair keyValuePair) => keyValuePair.Key, (KeyValuePair keyValuePair) => keyValuePair.Value); } private static void ProcessOnlineHitMarker(Vector3 hitPosition, bool isHeadshot) { Vector3 position = GameUtils.WorldPointToScreenPoint(hitPosition); ConsoleScreen.Log($"Drawing Hitmarker at : {hitPosition} Headshot ? {isHeadshot}"); if (position.z > 0f) { DrawHitMarker(position, 32f, isHeadshot); } } private void Tracers() { if (!Settings.tracers) { return; } if (GameUtils.DrawMaterial == null) { GameUtils.InitMaterial(); } Camera mainCamera = Main.MainCamera; if (mainCamera == null) { return; } GameUtils.DrawMaterial.SetPass(0); GL.PushMatrix(); GL.LoadProjectionMatrix(mainCamera.projectionMatrix); GL.modelview = mainCamera.worldToCameraMatrix; GL.Begin(1); foreach (Tracer value in validProjectiles.Values) { if (value.positions.Count >= 1) { for (int i = 0; i < value.positions.Count - 1; i++) { Vector3 v = value.positions[i]; Vector3 v2 = value.positions[i + 1]; GL.Color(Settings.TracerColor); GL.Vertex(v); GL.Vertex(v2); } } } GL.End(); GL.PopMatrix(); } private void OnGUI() { Tracers(); if (!Settings.HitMarkers) { return; } foreach (HitMarker activeHitMarker in activeHitMarkers) { Vector3 vector = GameUtils.WorldPointToScreenPoint(activeHitMarker.position); if (vector.z > 0f && vector.x >= 0f && vector.x <= (float)Screen.width && vector.y >= 0f && vector.y <= (float)Screen.height) { DrawDamageNumbers(activeHitMarker, vector); DrawHitMarker(vector, 32f, activeHitMarker.isHeadshot); } } } private void DrawDamageNumbers(HitMarker hitMarker, Vector3 screenPosition) { hitMarker.floatingOffset.y += Time.deltaTime * 20f; Vector3 vector = screenPosition + hitMarker.floatingOffset; vector.y = (float)Screen.height - vector.y; if (hitMarker.isHeadshot) { string text = "HEADSHOT"; GUI.color = new Color(1f, 0f, 0f, 1f); GUI.Label(new Rect(vector.x - 10f, vector.y - 40f, 100f, 20f), text); } string text2 = ""; if (Main.GamePlayers.Count > 0) { text2 = "- " + hitMarker.damage.ToString("F0") + " hp "; } GUI.color = Settings.HitmarkerHeadShotColor; GUI.Label(new Rect(vector.x - 10f, vector.y - 20f, 100f, 20f), text2); GUI.color = Color.white; } private static void DrawHitMarker(Vector3 position, float size, bool isHeadshot) { if (!(hitMarkerTexture == null)) { GUI.color = (isHeadshot ? Settings.HitmarkerHeadShotColor : Settings.HitmarkerHitColor); GUI.DrawTexture(new Rect(position.x - size / 2f, position.y - size / 2f, size, size), hitMarkerTexture); GUI.color = Color.white; } } }