using System; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace TrajectoryVisualizer { [BepInPlugin("com.syntx.stonewards.trajectoryvisualizer", "Trajectory Visualizer", "2.5.2")] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "com.syntx.stonewards.trajectoryvisualizer"; public const string PluginName = "Trajectory Visualizer"; public const string PluginVersion = "2.5.2"; internal static Plugin Instance; internal static ManualLogSource Log; internal static ConfigEntry ShowBombTrajectory; internal static ConfigEntry ShowArrowTrajectory; internal static ConfigEntry Gravity; internal static ConfigEntry LineWidth; internal static ConfigEntry RightOffset; internal static ConfigEntry DownOffset; internal static ConfigEntry MarkerSize; internal static ConfigEntry DashLength; internal static ConfigEntry DashGap; private Harmony _harmony; private void Awake() { //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; ShowBombTrajectory = ((BaseUnityPlugin)this).Config.Bind("General", "ShowBombTrajectory", true, "Show bomb arc while LMB is held."); ShowArrowTrajectory = ((BaseUnityPlugin)this).Config.Bind("General", "ShowArrowTrajectory", true, "Show arrow arc while LMB is held."); Gravity = ((BaseUnityPlugin)this).Config.Bind("General", "Gravity", 9.81f, "Same gravity as projectiles: v += down * g * dt."); LineWidth = ((BaseUnityPlugin)this).Config.Bind("General", "LineWidth", 0.025f, "Dash thickness."); RightOffset = ((BaseUnityPlugin)this).Config.Bind("General", "RightOffset", 0.22f, "Start slightly right of the crosshair."); DownOffset = ((BaseUnityPlugin)this).Config.Bind("General", "DownOffset", 0.22f, "Start slightly below the camera."); MarkerSize = ((BaseUnityPlugin)this).Config.Bind("General", "MarkerSize", 0.16f, "Impact marker size."); DashLength = ((BaseUnityPlugin)this).Config.Bind("General", "DashLength", 0.55f, "Length of each dash."); DashGap = ((BaseUnityPlugin)this).Config.Bind("General", "DashGap", 0.38f, "Gap between dashes."); Log.LogInfo((object)"===================================================="); Log.LogInfo((object)"Trajectory Visualizer v2.5.2 loaded"); Log.LogInfo((object)"Dashed arc. Thin ray only (no fat overlap). Archer force at any charge."); Log.LogInfo((object)"===================================================="); UnityCache.Init(); _harmony = new Harmony("com.syntx.stonewards.trajectoryvisualizer"); int num = 0; num += Patch("FirstPersonController", "Update", "OnPlayerUpdate"); num += Patch("ThrowableTool", "OnPrimaryAction", "OnThrowPrimary"); num += Patch("BowTool", "OnPrimaryAction", "OnBowPrimary"); Log.LogInfo((object)("Patches applied: " + num)); } private int Patch(string typeName, string methodName, string postfixName) { //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Expected O, but got Unknown Type type = AccessTools.TypeByName(typeName); if (type == null) { Log.LogError((object)("Type not found: " + typeName)); return 0; } MethodInfo methodInfo = null; MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo2 in methods) { if (methodInfo2.Name == methodName) { methodInfo = methodInfo2; break; } } if (methodInfo == null) { Log.LogError((object)("Method not found: " + typeName + "." + methodName)); return 0; } try { MethodInfo method = typeof(Patches).GetMethod(postfixName, BindingFlags.Static | BindingFlags.Public); _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("OK " + typeName + "." + methodInfo.Name)); return 1; } catch (Exception ex) { Log.LogError((object)("FAIL " + typeName + "." + methodName + ": " + ex.Message)); return 0; } } } internal static class UnityCache { private const BindingFlags Inst = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private const BindingFlags Stat = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private const int MaxDashes = 28; internal static Type Vector3Type; internal static Type ColorType; internal static Type LineRendererType; internal static Type GameObjectType; internal static Type ObjectType; internal static Type ShaderType; internal static Type MaterialType; internal static Type PhysicsType; internal static Type TransformType; internal static Type RendererType; internal static PropertyInfo CameraMain; internal static PropertyInfo TransformProp; internal static PropertyInfo PositionProp; internal static PropertyInfo ForwardProp; internal static PropertyInfo RightProp; internal static PropertyInfo LocalScaleProp; internal static PropertyInfo TimeSinceLevelLoad; internal static MethodInfo InputGetMouseButton; internal static MethodInfo ShaderFind; internal static MethodInfo DontDestroyOnLoad; internal static MethodInfo DestroyImmediate; internal static MethodInfo AddComponentGeneric; internal static MethodInfo AddComponentTyped; internal static MethodInfo CreatePrimitive; internal static MethodInfo GetComponentType; internal static MethodInfo RaycastAll; internal static MethodInfo OverlapSphere; internal static MethodInfo MaterialSetColor; internal static ConstructorInfo Vector3Ctor; internal static ConstructorInfo ColorCtor; internal static ConstructorInfo GameObjectCtor; internal static ConstructorInfo MaterialCtor; internal static FieldInfo VecX; internal static FieldInfo VecY; internal static FieldInfo VecZ; internal static PropertyInfo HitCollider; internal static PropertyInfo HitDistance; internal static PropertyInfo HitPoint; internal static PropertyInfo ColliderGameObject; internal static PropertyInfo GameObjectName; internal static PropertyInfo GameObjectTag; internal static PropertyInfo GameObjectTransform; internal static MethodInfo GetComponentInParentTyped; internal static object QueryCollide; internal static object PrimitiveSphere; internal static Type EnemyHitboxType; internal static Type EnemyControllerType; internal static Type HitboxType; internal static Type PlayerHitboxType; internal static PropertyInfo HitboxTypeProp; internal static object WhiteColor; internal static object RedColor; internal static object SharedMaterial; internal static object[] DashLines = new object[28]; internal static object[] DashGos = new object[28]; internal static MethodInfo[] DashSetPositions = new MethodInfo[28]; internal static Array[] DashBuffers = new Array[28]; internal static object MarkerGo; internal static object MarkerRenderer; internal static object MarkerMaterial; internal static bool Ready; internal static bool DashesReady; internal static bool LoggedFail; internal static void Init() { try { Type type = AccessTools.TypeByName("UnityEngine.Camera"); TransformType = AccessTools.TypeByName("UnityEngine.Transform"); Type type2 = AccessTools.TypeByName("UnityEngine.Input"); Type type3 = AccessTools.TypeByName("UnityEngine.Time"); Vector3Type = AccessTools.TypeByName("UnityEngine.Vector3"); ColorType = AccessTools.TypeByName("UnityEngine.Color"); LineRendererType = AccessTools.TypeByName("UnityEngine.LineRenderer"); GameObjectType = AccessTools.TypeByName("UnityEngine.GameObject"); ObjectType = AccessTools.TypeByName("UnityEngine.Object"); ShaderType = AccessTools.TypeByName("UnityEngine.Shader"); MaterialType = AccessTools.TypeByName("UnityEngine.Material"); PhysicsType = AccessTools.TypeByName("UnityEngine.Physics"); RendererType = AccessTools.TypeByName("UnityEngine.Renderer"); Type type4 = AccessTools.TypeByName("UnityEngine.RaycastHit"); Type type5 = AccessTools.TypeByName("UnityEngine.Collider"); Type type6 = AccessTools.TypeByName("UnityEngine.QueryTriggerInteraction"); Type type7 = AccessTools.TypeByName("UnityEngine.PrimitiveType"); EnemyHitboxType = AccessTools.TypeByName("EnemyHitbox"); EnemyControllerType = AccessTools.TypeByName("EnemyController"); HitboxType = AccessTools.TypeByName("Hitbox"); PlayerHitboxType = AccessTools.TypeByName("PlayerHitbox"); if (HitboxType != null) { HitboxTypeProp = HitboxType.GetProperty("Type", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } if (type != null) { CameraMain = type.GetProperty("main", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); TransformProp = type.GetProperty("transform", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } if (TransformType != null) { PositionProp = TransformType.GetProperty("position", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); ForwardProp = TransformType.GetProperty("forward", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); RightProp = TransformType.GetProperty("right", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); LocalScaleProp = TransformType.GetProperty("localScale", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } if (type3 != null) { TimeSinceLevelLoad = type3.GetProperty("timeSinceLevelLoad", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (TimeSinceLevelLoad == null) { TimeSinceLevelLoad = type3.GetProperty("time", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } } if (type2 != null) { InputGetMouseButton = type2.GetMethod("GetMouseButton", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(int) }, null); } if (ShaderType != null) { ShaderFind = ShaderType.GetMethod("Find", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(string) }, null); } if (ObjectType != null) { DontDestroyOnLoad = ObjectType.GetMethod("DontDestroyOnLoad", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { ObjectType }, null); DestroyImmediate = ObjectType.GetMethod("DestroyImmediate", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { ObjectType }, null); if (DestroyImmediate == null) { DestroyImmediate = ObjectType.GetMethod("Destroy", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { ObjectType }, null); } } if (GameObjectType != null) { GameObjectCtor = GameObjectType.GetConstructor(new Type[1] { typeof(string) }); AddComponentTyped = GameObjectType.GetMethod("AddComponent", new Type[1] { typeof(Type) }); GetComponentType = GameObjectType.GetMethod("GetComponent", new Type[1] { typeof(Type) }); GameObjectName = GameObjectType.GetProperty("name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); GameObjectTag = GameObjectType.GetProperty("tag", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); GameObjectTransform = GameObjectType.GetProperty("transform", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo[] methods = GameObjectType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name == "AddComponent" && methodInfo.IsGenericMethodDefinition && methodInfo.GetParameters().Length == 0) { AddComponentGeneric = methodInfo; } } if (type7 != null) { PrimitiveSphere = Enum.ToObject(type7, 0); CreatePrimitive = GameObjectType.GetMethod("CreatePrimitive", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { type7 }, null); } } if (Vector3Type != null) { Vector3Ctor = Vector3Type.GetConstructor(new Type[3] { typeof(float), typeof(float), typeof(float) }); VecX = Vector3Type.GetField("x"); VecY = Vector3Type.GetField("y"); VecZ = Vector3Type.GetField("z"); } if (ColorType != null) { ColorCtor = ColorType.GetConstructor(new Type[4] { typeof(float), typeof(float), typeof(float), typeof(float) }); } if (MaterialType != null && ShaderType != null) { MaterialCtor = MaterialType.GetConstructor(new Type[1] { ShaderType }); MaterialSetColor = MaterialType.GetMethod("SetColor", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2] { typeof(string), ColorType }, null); } if (type6 != null) { QueryCollide = Enum.ToObject(type6, 2); } if (PhysicsType != null && Vector3Type != null) { MethodInfo[] methods = PhysicsType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo2 in methods) { ParameterInfo[] parameters = methodInfo2.GetParameters(); if (methodInfo2.Name == "RaycastAll" && parameters.Length >= 3 && RaycastAll == null) { RaycastAll = methodInfo2; } if (methodInfo2.Name == "OverlapSphere" && parameters.Length >= 2 && OverlapSphere == null) { OverlapSphere = methodInfo2; } } methods = PhysicsType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo3 in methods) { ParameterInfo[] parameters2 = methodInfo3.GetParameters(); if (methodInfo3.Name == "RaycastAll" && parameters2.Length == 5) { RaycastAll = methodInfo3; } if (methodInfo3.Name == "OverlapSphere" && parameters2.Length == 4) { OverlapSphere = methodInfo3; } } } if (type4 != null) { HitCollider = type4.GetProperty("collider", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); HitDistance = type4.GetProperty("distance", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); HitPoint = type4.GetProperty("point", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } if (type5 != null) { ColliderGameObject = type5.GetProperty("gameObject", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo[] methods = type5.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo4 in methods) { if (methodInfo4.Name == "GetComponentInParent" && methodInfo4.GetParameters().Length == 1 && methodInfo4.GetParameters()[0].ParameterType == typeof(Type)) { GetComponentInParentTyped = methodInfo4; } } } Ready = type != null && Vector3Type != null && GameObjectType != null && LineRendererType != null; Plugin.Log.LogInfo((object)("Unity cache ready. timeSinceLevelLoad=" + (TimeSinceLevelLoad != null) + " RaycastAll=" + (RaycastAll != null))); } catch (Exception ex) { Plugin.Log.LogError((object)("UnityCache.Init failed: " + ex)); } } internal static float Now() { try { if (TimeSinceLevelLoad != null) { return Convert.ToSingle(TimeSinceLevelLoad.GetValue(null)); } } catch { } return 0f; } internal static bool IsLmbHeld() { try { if (InputGetMouseButton != null) { return Convert.ToBoolean(InputGetMouseButton.Invoke(null, new object[1] { 0 })); } } catch { } if (!Patches.ThrowHeld) { return Patches.BowHeld; } return true; } internal static bool TryGetCamVectors(out Vec3 pos, out Vec3 forward, out Vec3 right) { pos = default(Vec3); forward = default(Vec3); right = default(Vec3); try { object obj = ((CameraMain != null) ? CameraMain.GetValue(null) : null); if (obj == null) { return false; } object obj2 = ((TransformProp != null) ? TransformProp.GetValue(obj) : null); if (obj2 == null) { return false; } object value = PositionProp.GetValue(obj2); object value2 = ForwardProp.GetValue(obj2); object value3 = RightProp.GetValue(obj2); if (value == null || value2 == null || value3 == null) { return false; } pos = Vec3.From(value); forward = Vec3.From(value2); right = Vec3.From(value3); return true; } catch { return false; } } internal static object MakeV3(float x, float y, float z) { return Vector3Ctor.Invoke(new object[3] { x, y, z }); } internal static object MakeColor(float r, float g, float b, float a) { return ColorCtor.Invoke(new object[4] { r, g, b, a }); } private static object FindShader() { if (ShaderFind == null) { return null; } string[] array = new string[4] { "Sprites/Default", "Hidden/Internal-Colored", "Unlit/Color", "Universal Render Pipeline/Unlit" }; foreach (string text in array) { object obj = ShaderFind.Invoke(null, new object[1] { text }); if (obj != null) { return obj; } } return null; } private static object MakeMaterial() { object obj = FindShader(); if (MaterialCtor == null || obj == null) { return null; } return MaterialCtor.Invoke(new object[1] { obj }); } internal static void EnsureVisuals() { if (DashesReady || !Ready) { return; } try { SharedMaterial = MakeMaterial(); WhiteColor = MakeColor(1f, 1f, 1f, 1f); RedColor = MakeColor(1f, 0.08f, 0.08f, 1f); for (int i = 0; i < 28; i++) { object obj = GameObjectCtor.Invoke(new object[1] { "SW_Dash_" + i }); object obj2 = ((!(AddComponentGeneric != null)) ? AddComponentTyped.Invoke(obj, new object[1] { LineRendererType }) : AddComponentGeneric.MakeGenericMethod(LineRendererType).Invoke(obj, null)); DashGos[i] = obj; DashLines[i] = obj2; DashBuffers[i] = Array.CreateInstance(Vector3Type, 2); SetProp(obj2, "useWorldSpace", true); SetProp(obj2, "shadowCastingMode", 0); SetProp(obj2, "receiveShadows", false); SetProp(obj2, "positionCount", 2); SetProp(obj2, "enabled", false); if (SharedMaterial != null) { SetProp(obj2, "material", SharedMaterial); } MethodInfo[] methods = obj2.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name == "SetPositions" && methodInfo.GetParameters().Length == 1) { DashSetPositions[i] = methodInfo; break; } } if (DontDestroyOnLoad != null) { DontDestroyOnLoad.Invoke(null, new object[1] { obj }); } } EnsureMarker(); DashesReady = true; Plugin.Log.LogInfo((object)"Dashed trajectory ready."); } catch (Exception ex) { Plugin.Log.LogError((object)("EnsureVisuals failed: " + ex)); } } private static void EnsureMarker() { if (MarkerGo != null) { return; } try { if (CreatePrimitive != null && PrimitiveSphere != null) { MarkerGo = CreatePrimitive.Invoke(null, new object[1] { PrimitiveSphere }); SetProp(MarkerGo, "name", "SW_ImpactMarker"); object obj = ((GetComponentType != null) ? GetComponentType.Invoke(MarkerGo, new object[1] { AccessTools.TypeByName("UnityEngine.Collider") }) : null); if (obj != null && DestroyImmediate != null) { DestroyImmediate.Invoke(null, new object[1] { obj }); } MarkerRenderer = ((GetComponentType != null) ? GetComponentType.Invoke(MarkerGo, new object[1] { RendererType }) : null); MarkerMaterial = MakeMaterial(); if (MarkerRenderer != null && MarkerMaterial != null) { SetProp(MarkerRenderer, "material", MarkerMaterial); } if (DontDestroyOnLoad != null) { DontDestroyOnLoad.Invoke(null, new object[1] { MarkerGo }); } SetActive(MarkerGo, on: false); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Marker failed: " + ex.Message)); } } private static void SetProp(object obj, string name, object value) { if (obj == null) { return; } try { PropertyInfo property = obj.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanWrite) { property.SetValue(obj, value, null); } } catch { } } private static void SetActive(object go, bool on) { if (go == null) { return; } try { MethodInfo method = go.GetType().GetMethod("SetActive", new Type[1] { typeof(bool) }); if (method != null) { method.Invoke(go, new object[1] { on }); } } catch { } } internal static void HideLine() { if (DashesReady) { for (int i = 0; i < 28; i++) { SetProp(DashLines[i], "enabled", false); } SetActive(MarkerGo, on: false); } } internal static void DrawArc(Vec3 origin, Vec3 dir, float speed, out bool hitsEnemy) { hitsEnemy = false; EnsureVisuals(); if (!DashesReady) { return; } float value = Plugin.Gravity.Value; float num = 0.03f; int num2 = 70; Vec3 vec = origin; Vec3 p = origin; bool flag = false; Vec3[] array = new Vec3[num2]; int count = 0; array[count++] = origin; for (int i = 1; i < num2; i++) { if (flag) { break; } float num3 = (float)i * num; Vec3 vec2 = new Vec3(origin.x + dir.x * speed * num3, origin.y + dir.y * speed * num3 - 0.5f * value * num3 * num3, origin.z + dir.z * speed * num3); try { if (TryHit(vec, vec2, out var enemy, out var hitPoint)) { vec2 = hitPoint; hitsEnemy = enemy; flag = true; } } catch { } if (i > 4 && vec2.y < origin.y - 80f) { flag = true; } array[count++] = vec2; vec = vec2; p = vec2; } object color = (hitsEnemy ? RedColor : WhiteColor); ApplyColor(SharedMaterial, color); DrawDashes(array, count, color); DrawImpactMarker(p, color); } private static void DrawDashes(Vec3[] pts, int count, object color) { float num = Plugin.DashLength.Value; float num2 = Plugin.DashGap.Value; if (num < 0.08f) { num = 0.08f; } if (num2 < 0.05f) { num2 = 0.05f; } float num3 = num + num2; int num4 = 0; float num5 = 0f; for (int i = 1; i < count; i++) { if (num4 >= 28) { break; } Vec3 a = pts[i - 1]; Vec3 b = pts[i]; float num6 = Dist(a, b); if (num6 < 0.0001f) { continue; } float num7 = num5; float num8 = num5 + num6; int num9 = (int)Math.Floor(num7 / num3); int num10 = (int)Math.Floor(num8 / num3); for (int j = num9; j <= num10; j++) { if (num4 >= 28) { break; } float num11 = (float)j * num3; float num12 = num11 + num; float num13 = ((num7 > num11) ? num7 : num11); float num14 = ((num8 < num12) ? num8 : num12); if (num14 > num13 + 0.001f) { float t = (num13 - num7) / num6; float t2 = (num14 - num7) / num6; SetDash(num4++, Lerp(a, b, t), Lerp(a, b, t2), color); } } num5 = num8; } for (int k = num4; k < 28; k++) { SetProp(DashLines[k], "enabled", false); } } private static void SetDash(int i, Vec3 a, Vec3 b, object color) { object obj = DashLines[i]; if (obj != null) { SetProp(obj, "enabled", true); SetProp(obj, "positionCount", 2); float value = Plugin.LineWidth.Value; SetProp(obj, "startWidth", value); SetProp(obj, "endWidth", value); SetProp(obj, "startColor", color); SetProp(obj, "endColor", color); Array array = DashBuffers[i]; array.SetValue(MakeV3(a.x, a.y, a.z), 0); array.SetValue(MakeV3(b.x, b.y, b.z), 1); if (DashSetPositions[i] != null) { DashSetPositions[i].Invoke(obj, new object[1] { array }); } } } private static float Dist(Vec3 a, Vec3 b) { float num = a.x - b.x; float num2 = a.y - b.y; float num3 = a.z - b.z; return (float)Math.Sqrt(num * num + num2 * num2 + num3 * num3); } private static Vec3 Lerp(Vec3 a, Vec3 b, float t) { return new Vec3(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t); } private static void ApplyColor(object mat, object color) { if (mat == null || color == null) { return; } SetProp(mat, "color", color); if (!(MaterialSetColor != null)) { return; } try { MaterialSetColor.Invoke(mat, new object[2] { "_Color", color }); } catch { } try { MaterialSetColor.Invoke(mat, new object[2] { "_BaseColor", color }); } catch { } } private static void DrawImpactMarker(Vec3 p, object color) { if (MarkerGo == null) { return; } SetActive(MarkerGo, on: true); object obj = ((GameObjectTransform != null) ? GameObjectTransform.GetValue(MarkerGo) : null); if (obj != null) { if (PositionProp != null) { PositionProp.SetValue(obj, MakeV3(p.x, p.y, p.z), null); } float value = Plugin.MarkerSize.Value; if (LocalScaleProp != null) { LocalScaleProp.SetValue(obj, MakeV3(value, value, value), null); } } ApplyColor(MarkerMaterial, color); if (MarkerRenderer != null) { SetProp(MarkerRenderer, "enabled", true); } } private static object InvokeRaycastAll(object origin, object direction, float dist) { if (RaycastAll == null) { return null; } ParameterInfo[] parameters = RaycastAll.GetParameters(); if (parameters.Length >= 5) { return RaycastAll.Invoke(null, new object[5] { origin, direction, dist, -1, QueryCollide ?? ((object)2) }); } if (parameters.Length == 4) { return RaycastAll.Invoke(null, new object[4] { origin, direction, dist, -1 }); } return RaycastAll.Invoke(null, new object[3] { origin, direction, dist }); } private static object InvokeOverlap(object pos, float radius) { if (OverlapSphere == null) { return null; } ParameterInfo[] parameters = OverlapSphere.GetParameters(); if (parameters.Length >= 4) { return OverlapSphere.Invoke(null, new object[4] { pos, radius, -1, QueryCollide ?? ((object)2) }); } if (parameters.Length == 3) { return OverlapSphere.Invoke(null, new object[3] { pos, radius, -1 }); } return OverlapSphere.Invoke(null, new object[2] { pos, radius }); } private static bool TryHit(Vec3 from, Vec3 to, out bool enemy, out Vec3 hitPoint) { enemy = false; hitPoint = to; Vec3 vec = new Vec3(to.x - from.x, to.y - from.y, to.z - from.z); float num = (float)Math.Sqrt(vec.SqrMag()); if (num < 0.001f) { return false; } Vec3 vec2 = vec.Mul(1f / num); try { object origin = MakeV3(from.x, from.y, from.z); object direction = MakeV3(vec2.x, vec2.y, vec2.z); Array array = InvokeRaycastAll(origin, direction, num + 0.01f) as Array; object obj = null; float num2 = float.MaxValue; bool flag = false; if (array != null) { for (int i = 0; i < array.Length; i++) { object value = array.GetValue(i); if (value == null) { continue; } float num3 = ((HitDistance != null) ? Convert.ToSingle(HitDistance.GetValue(value)) : ((float)i)); if (!(num3 < 0.04f) && !IsPlayerHit(value)) { bool flag2 = IsEnemyHitObject((HitCollider != null) ? HitCollider.GetValue(value) : null); if (obj == null || (flag2 && !flag) || (!flag && num3 < num2)) { obj = value; num2 = num3; flag = flag2; } if (flag2) { break; } } } } if (obj == null) { return false; } enemy = flag; object obj2 = ((HitPoint != null) ? HitPoint.GetValue(obj) : null); if (obj2 != null) { hitPoint = Vec3.From(obj2); } return true; } catch { return false; } } private static bool IsPlayerHit(object hit) { return IsPlayerHitCollider((HitCollider != null) ? HitCollider.GetValue(hit) : null); } private static bool IsPlayerHitCollider(object col) { if (col == null) { return false; } if (PlayerHitboxType != null && FindComponent(col, PlayerHitboxType) != null) { return true; } object obj = ((ColliderGameObject != null) ? ColliderGameObject.GetValue(col) : null); string text = ((obj != null && GameObjectName != null) ? Convert.ToString(GameObjectName.GetValue(obj)) : ""); if (!string.IsNullOrEmpty(text)) { return text.IndexOf("Player", StringComparison.OrdinalIgnoreCase) >= 0; } return false; } private static bool IsEnemyHitObject(object obj) { if (obj == null) { return false; } if (PlayerHitboxType != null && FindComponent(obj, PlayerHitboxType) != null) { return false; } if (EnemyHitboxType != null && FindComponent(obj, EnemyHitboxType) != null) { return true; } if (EnemyControllerType != null && FindComponent(obj, EnemyControllerType) != null) { return true; } object obj2 = ((HitboxType != null) ? FindComponent(obj, HitboxType) : null); if (obj2 != null && HitboxTypeProp != null) { object value = HitboxTypeProp.GetValue(obj2); string text = ((value != null) ? Convert.ToString(value) : ""); if (text.IndexOf("PLAYER", StringComparison.OrdinalIgnoreCase) >= 0) { return false; } if (text.IndexOf("NORMAL", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("ELITE", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("BOSS", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } return false; } private static object FindComponent(object obj, Type type) { if (obj == null || type == null) { return null; } try { if (type.IsInstanceOfType(obj)) { return obj; } MethodInfo method = obj.GetType().GetMethod("GetComponent", new Type[1] { typeof(Type) }); if (method != null) { object obj2 = method.Invoke(obj, new object[1] { type }); if (obj2 != null) { return obj2; } } if (GetComponentInParentTyped != null) { object obj3 = GetComponentInParentTyped.Invoke(obj, new object[1] { type }); if (obj3 != null) { return obj3; } } } catch { } return null; } } public static class Patches { internal static bool ThrowHeld; internal static bool BowHeld; private static bool _wasLmb; private static float _holdStart; private static object _cachedItem; private static int _equipTick; private static int _hideFrames; private static float _lastLoggedCharge = -1f; public static void OnThrowPrimary(bool _B) { ThrowHeld = _B; if (_B) { _holdStart = UnityCache.Now(); } else { UnityCache.HideLine(); } } public static void OnBowPrimary(bool _B) { BowHeld = _B; if (_B) { _holdStart = UnityCache.Now(); } else { UnityCache.HideLine(); } } public static void OnPlayerUpdate(object __instance) { if (!IsLocalPlayer(__instance)) { return; } bool num = UnityCache.IsLmbHeld(); if (num && !_wasLmb) { _holdStart = UnityCache.Now(); } _wasLmb = num; if (!num) { if (_hideFrames < 2) { UnityCache.HideLine(); _hideFrames++; } ThrowHeld = false; BowHeld = false; return; } _hideFrames = 0; if ((_equipTick++ & 3) == 0 || _cachedItem == null) { _cachedItem = GetEquippedItem(__instance); } object cachedItem = _cachedItem; string name = cachedItem?.GetType().Name; bool flag = Plugin.ShowBombTrajectory.Value && IsBombTool(name); bool flag2 = Plugin.ShowArrowTrajectory.Value && IsBowTool(name); if (!flag && !flag2) { UnityCache.HideLine(); return; } if (!UnityCache.TryGetCamVectors(out var pos, out var forward, out var right)) { UnityCache.HideLine(); return; } float charge = GetCharge01(cachedItem); float speed = GetSpeed(cachedItem, __instance, flag, charge); UnityCache.DrawArc(GetStart(cachedItem, pos, forward, right), forward.Normalized(), speed, out var _); if (Math.Abs(charge - _lastLoggedCharge) > 0.24f) { _lastLoggedCharge = charge; Plugin.Log.LogInfo((object)((flag ? "bomb" : "arrow") + " charge=" + charge.ToString("0.00") + " speed=" + speed.ToString("0.0") + " hold=" + (UnityCache.Now() - _holdStart).ToString("0.00"))); } } private static Vec3 GetStart(object item, Vec3 camPos, Vec3 forward, Vec3 right) { Vec3 result = camPos.Add(forward.Mul(0.35f)).Add(right.Mul(Plugin.RightOffset.Value)).Add(new Vec3(0f, 0f - Plugin.DownOffset.Value, 0f)); if (item == null) { return result; } try { object member = GetMember(item, "bowPosition"); if (member != null && UnityCache.PositionProp != null) { object value = UnityCache.PositionProp.GetValue(member); if (value != null) { return Vec3.From(value).Add(forward.Mul(0.12f)); } } } catch { } return result; } private static float GetCharge01(object tool) { float num = UnityCache.Now(); float num2 = 0.75f; float num3 = _holdStart; bool flag = false; if (tool != null) { float num4 = ToFloat(GetMember(tool, "aimingDuration"), 0f); if (num4 > 0.05f) { num2 = num4; } if (GetMember(tool, "isFullyCharged") is bool flag2) { flag = flag2; } float num5 = ToFloat(GetMember(tool, "aimingStartTime"), 0f); if (num5 > 0.05f) { num3 = num5; } } if (flag) { return 1f; } float num6 = (num - num3) / num2; if (num6 < 0f) { num6 = 0f; } if (num6 > 1f) { num6 = 1f; } return num6; } private static float GetSpeed(object item, object player, bool bomb, float charge) { float num = (bomb ? 8f : 18f); float num2 = (bomb ? 22f : 42f); if (bomb && item != null) { object member = GetMember(item, "forceRange"); if (member != null) { float num3 = ToFloat(GetMember(member, "x"), 0f); float num4 = ToFloat(GetMember(member, "y"), 0f); if (num3 > 0.1f) { num = num3; } if (num4 > 0.1f) { num2 = num4; } } } if (!bomb && player != null) { try { object obj = GetMember(player, "FPSSettingsSO") ?? GetMember(player, "fpsSettingsSo"); object obj2 = GetMember(obj, "BowForce") ?? GetMember(obj, "bowForce"); if (obj2 != null) { float num5 = ToFloat(GetMember(obj2, "x"), 0f); float num6 = ToFloat(GetMember(obj2, "y"), 0f); if (num5 > 0.1f) { num = num5; } if (num6 > 0.1f) { num2 = num6; } } } catch { } } float num7 = num + (num2 - num) * charge; if (!bomb && player != null) { try { object obj4 = GetMember(player, "playerClass") ?? GetMember(player, "NetworkplayerClass"); int num8 = ((obj4 != null) ? Convert.ToInt32(obj4) : (-1)); object obj5 = GetMember(player, "PlayerArchetypeSO") ?? GetMember(player, "playerArchetypeSO"); object obj6 = GetMember(obj5, "ArcherStats") ?? GetMember(obj5, "archerStats"); float num9 = ToFloat(GetMember(obj6, "ArrowForceMultiplayer") ?? GetMember(obj6, "arrowForceMultiplayer"), 1f); if (num9 > 0.05f && (num8 == 1 || obj6 != null)) { num7 *= num9; } } catch { } } float num10 = ToFloat(GetMember((player != null) ? (GetMember(player, "PlayerStats") ?? GetMember(player, "playerStats")) : null, "ProjectileSpeed"), 1f); if (!bomb && num10 > 0.05f && num10 < 10f) { num7 *= num10; } return num7; } private static float ToFloat(object v, float fallback) { if (v == null) { return fallback; } try { return Convert.ToSingle(v); } catch { return fallback; } } private static bool IsBombTool(string name) { if (!string.IsNullOrEmpty(name)) { if (name.IndexOf("Throwable", StringComparison.OrdinalIgnoreCase) < 0) { return name.IndexOf("Bomb", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } return false; } private static bool IsBowTool(string name) { if (!string.IsNullOrEmpty(name)) { return name.IndexOf("Bow", StringComparison.OrdinalIgnoreCase) >= 0; } return false; } private static bool IsLocalPlayer(object player) { if (player == null) { return false; } try { PropertyInfo property = player.GetType().GetProperty("isLocalPlayer", BindingFlags.Instance | BindingFlags.Public); if (property != null) { return Convert.ToBoolean(property.GetValue(player)); } } catch { } return true; } private static object GetEquippedItem(object player) { try { object obj = GetMember(player, "PlayerEquipment") ?? GetMember(player, "playerEquipment"); if (obj == null) { return null; } return GetMember(obj, "CurrentEquippedItem") ?? GetMember(obj, "currentEquippedItem"); } catch { return null; } } private static object GetMember(object obj, string name) { if (obj == null) { return null; } PropertyInfo property = obj.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { return property.GetValue(obj); } FieldInfo fieldInfo = AccessTools.Field(obj.GetType(), name); if (!(fieldInfo != null)) { return null; } return fieldInfo.GetValue(obj); } } internal struct Vec3 { public float x; public float y; public float z; public Vec3(float x, float y, float z) { this.x = x; this.y = y; this.z = z; } public static Vec3 From(object obj) { return new Vec3(Convert.ToSingle(UnityCache.VecX.GetValue(obj)), Convert.ToSingle(UnityCache.VecY.GetValue(obj)), Convert.ToSingle(UnityCache.VecZ.GetValue(obj))); } public Vec3 Add(Vec3 o) { return new Vec3(x + o.x, y + o.y, z + o.z); } public Vec3 Mul(float s) { return new Vec3(x * s, y * s, z * s); } public float SqrMag() { return x * x + y * y + z * z; } public Vec3 Normalized() { float num = (float)Math.Sqrt(SqrMag()); if (num < 0.0001f) { return new Vec3(0f, 0f, 1f); } return new Vec3(x / num, y / num, z / num); } } }