367 lines
10 KiB
C#
367 lines
10 KiB
C#
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<Vector3> positions = new List<Vector3>();
|
|
|
|
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<int, Tracer> validProjectiles = new Dictionary<int, Tracer>();
|
|
|
|
private static List<HitMarker> activeHitMarkers = new List<HitMarker>();
|
|
|
|
private Dictionary<int, bool> playerAliveStatus = new Dictionary<int, bool>();
|
|
|
|
private static Dictionary<int, float> damageTimers = new Dictionary<int, float>();
|
|
|
|
private HashSet<int> blacklistedSeeds = new HashSet<int>();
|
|
|
|
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<Texture2D>("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<int, Tracer> keyValuePair) => Time.time - keyValuePair.Value.startTime < 5f).ToDictionary((KeyValuePair<int, Tracer> keyValuePair) => keyValuePair.Key, (KeyValuePair<int, Tracer> 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<int, Tracer> keyValuePair) => Time.time - keyValuePair.Value.startTime < 5f).ToDictionary((KeyValuePair<int, Tracer> keyValuePair) => keyValuePair.Key, (KeyValuePair<int, Tracer> 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;
|
|
}
|
|
}
|
|
}
|