using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("RepoModDev")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.1.0")] [assembly: AssemblyInformationalVersion("1.1.1")] [assembly: AssemblyProduct("MonsterCompass")] [assembly: AssemblyTitle("MonsterCompass")] [assembly: AssemblyVersion("1.1.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 MonsterCompass { internal static class BearingMath { internal static (float X, float Y) Edge(float right, float forward, float halfWidth, float halfHeight) { if (Math.Abs(right) + Math.Abs(forward) < 0.0001f) { return (X: 0f, Y: 0f - halfHeight); } float num = Math.Max(Math.Abs(right) / halfWidth, Math.Abs(forward) / halfHeight); return (X: right / num, Y: (0f - forward) / num); } internal static float Fade(float age, float duration) { if (age < 0f || age >= duration) { return 0f; } float num = Math.Min(0.35f, duration * 0.2f); float num2 = Math.Min(1.5f, duration * 0.4f); return Math.Min(1f, Math.Min(age / num, (duration - age) / num2)); } } [BepInPlugin("RepoModDev.MonsterCompass", "Monster Compass", "1.1.0")] [BepInProcess("REPO.exe")] public sealed class Plugin : BaseUnityPlugin { private sealed class Cue { internal readonly Enemy Enemy; internal readonly float SpawnTime; internal float VisibleAge; internal Cue(Enemy enemy) { Enemy = enemy; SpawnTime = Time.unscaledTime; } } private readonly struct DrawCue { internal readonly Vector2 Position; internal readonly float Angle; internal readonly float Alpha; internal DrawCue(Vector2 position, float angle, float alpha) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) Position = position; Angle = angle; Alpha = alpha; } } public const string PluginId = "RepoModDev.MonsterCompass"; public const string Version = "1.1.0"; internal static Plugin? Instance; private Harmony? harmony; private readonly List cues = new List(); private readonly List draws = new List(); private readonly SpawnTracker spawnTracker = new SpawnTracker(); private static readonly FieldInfo ParentEnemy = AccessTools.Field(typeof(EnemyParent), "Enemy"); private static readonly FieldInfo ParentSpawned = AccessTools.Field(typeof(EnemyParent), "Spawned"); private static readonly FieldInfo ParentSetupDone = AccessTools.Field(typeof(EnemyParent), "SetupDone"); private float nextReconcile; private float statusUntil; private GUIStyle? statusStyle; private ConfigEntry enabledSetting; private ConfigEntry continuous; private ConfigEntry duration; private ConfigEntry opacity; private ConfigEntry size; private ConfigEntry margin; private ConfigEntry maxDistance; private ConfigEntry nearDistance; private ConfigEntry farDistance; private ConfigEntry color; private ConfigEntry toggle; private string lastStatus = ""; private Texture2D? glow; private Texture2D? arrow; private void Awake() { //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Expected O, but got Unknown //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Expected O, but got Unknown //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Expected O, but got Unknown //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Expected O, but got Unknown //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Expected O, but got Unknown //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Expected O, but got Unknown //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_02a6: Expected O, but got Unknown ((Component)this).transform.SetParent((Transform)null); ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); Instance = this; enabledSetting = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Enable direction hints. Re-enabling discovers monsters already spawned. Toggle with F7."); continuous = ((BaseUnityPlugin)this).Config.Bind("General", "ContinuousTracking", true, "Legacy compatibility setting. Monster hints now remain active while the monster is active."); duration = ((BaseUnityPlugin)this).Config.Bind("Hints", "Duration", 8f, "Legacy setting retained for config compatibility; cues now remain while the monster is active."); opacity = ((BaseUnityPlugin)this).Config.Bind("Hints", "Opacity", 0.45f, new ConfigDescription("Maximum hint opacity.", (AcceptableValueBase)(object)new AcceptableValueRange(0.05f, 1f), Array.Empty())); size = ((BaseUnityPlugin)this).Config.Bind("Hints", "Size", 38f, new ConfigDescription("Hint size at 1080p; scales with screen height.", (AcceptableValueBase)(object)new AcceptableValueRange(16f, 100f), Array.Empty())); margin = ((BaseUnityPlugin)this).Config.Bind("Hints", "EdgeMargin", 24f, new ConfigDescription("Edge inset at 1080p.", (AcceptableValueBase)(object)new AcceptableValueRange(8f, 150f), Array.Empty())); maxDistance = ((BaseUnityPlugin)this).Config.Bind("Hints", "MaxDistance", 0f, new ConfigDescription("Maximum 3D distance in metres; 0 means unlimited.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1000f), Array.Empty())); nearDistance = ((BaseUnityPlugin)this).Config.Bind("Hints", "NearDistance", 5f, new ConfigDescription("Distance at which the hint reaches maximum intensity.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 30f), Array.Empty())); farDistance = ((BaseUnityPlugin)this).Config.Bind("Hints", "FarDistance", 35f, new ConfigDescription("Distance at which the hint is least intense.", (AcceptableValueBase)(object)new AcceptableValueRange(5f, 200f), Array.Empty())); color = ((BaseUnityPlugin)this).Config.Bind("Hints", "Color", new Color(1f, 0.42f, 0.23f, 1f), "Hint tint. Opacity also multiplies the color alpha."); toggle = ((BaseUnityPlugin)this).Config.Bind("General", "ToggleKey", new KeyboardShortcut((KeyCode)288, Array.Empty()), "Toggle hints without opening a menu."); glow = MakeTexture(chevron: false); arrow = MakeTexture(chevron: true); SceneManager.activeSceneChanged += SceneChanged; harmony = new Harmony("RepoModDev.MonsterCompass"); harmony.PatchAll(typeof(Plugin).Assembly); enabledSetting.SettingChanged += EnabledChanged; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Monster Compass 1.1.0 loaded. F7 toggles hints."); } internal void ObserveEnemy(Enemy enemy, bool spawned, string source) { if (!enabledSetting.Value || !Object.op_Implicit((Object)(object)enemy)) { return; } bool flag = spawnTracker.Observe(((Object)enemy).GetInstanceID(), spawned); if (!spawned) { for (int num = cues.Count - 1; num >= 0; num--) { if ((Object)(object)cues[num].Enemy == (Object)(object)enemy) { cues.RemoveAt(num); } } } else { if (!flag) { return; } for (int num2 = cues.Count - 1; num2 >= 0; num2--) { if (!Object.op_Implicit((Object)(object)cues[num2].Enemy) || (Object)(object)cues[num2].Enemy == (Object)(object)enemy) { cues.RemoveAt(num2); } } if (cues.Count >= 64) { cues.RemoveAt(0); } cues.Add(new Cue(enemy)); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Spawn cue queued ({source}): {((Object)enemy).name}; pending={cues.Count}"); } } private void EnabledChanged(object sender, EventArgs args) { cues.Clear(); draws.Clear(); spawnTracker.Clear(); nextReconcile = 0f; statusUntil = Time.unscaledTime + 3f; lastStatus = ""; ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Monster Compass enabled: {enabledSetting.Value}"); } private void ReconcileEnemies() { if (!Object.op_Implicit((Object)(object)EnemyDirector.instance) || EnemyDirector.instance.enemiesSpawned == null) { return; } foreach (EnemyParent item in EnemyDirector.instance.enemiesSpawned) { if (Object.op_Implicit((Object)(object)item)) { object? value = ParentEnemy.GetValue(item); Enemy val = (Enemy)((value is Enemy) ? value : null); if (val != null && Object.op_Implicit((Object)(object)val)) { bool spawned = (bool)ParentSetupDone.GetValue(item) && (bool)ParentSpawned.GetValue(item) && ((Component)val).gameObject.activeInHierarchy; ObserveEnemy(val, spawned, "registry"); } } } } private void Update() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) KeyboardShortcut value = toggle.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { enabledSetting.Value = !enabledSetting.Value; } if (enabledSetting.Value && Time.unscaledTime >= nextReconcile) { nextReconcile = Time.unscaledTime + 0.25f; ReconcileEnemies(); } } private void LateUpdate() { //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Invalid comparison between Unknown and I4 //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Unknown result type (might be due to invalid IL or missing references) //IL_027c: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02b5: Unknown result type (might be due to invalid IL or missing references) //IL_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_02bf: Unknown result type (might be due to invalid IL or missing references) //IL_02d2: Unknown result type (might be due to invalid IL or missing references) //IL_02d4: Unknown result type (might be due to invalid IL or missing references) //IL_02dc: Unknown result type (might be due to invalid IL or missing references) //IL_02de: Unknown result type (might be due to invalid IL or missing references) //IL_03a4: Unknown result type (might be due to invalid IL or missing references) draws.Clear(); if (!enabledSetting.Value) { cues.Clear(); return; } float unscaledTime = Time.unscaledTime; for (int num = cues.Count - 1; num >= 0; num--) { Cue cue = cues[num]; if (!Object.op_Implicit((Object)(object)cue.Enemy) || !((Component)cue.Enemy).gameObject.activeInHierarchy) { cues.RemoveAt(num); } } if (cues.Count == 0) { return; } if (!Object.op_Implicit((Object)(object)GameDirector.instance) || !Object.op_Implicit((Object)(object)PlayerAvatar.instance) || !Object.op_Implicit((Object)(object)RunManager.instance)) { Status("Waiting for local player/game initialization"); return; } if (SemiFunc.MenuLevel() || (int)GameDirector.instance.currentState != 2) { Status($"Waiting for gameplay: {GameDirector.instance.currentState}"); return; } if (GameDirector.instance.DisableInput || SemiFunc.IsSpectating()) { Status("Hidden: input disabled or spectating"); return; } Camera val = SemiFunc.MainCamera(); if (!Object.op_Implicit((Object)(object)val) || !((Component)val).gameObject.activeInHierarchy) { Status("Waiting for camera"); return; } Status("Rendering spawn cues"); Vector3 val2 = Vector3.ProjectOnPlane(((Component)val).transform.forward, Vector3.up); if (((Vector3)(ref val2)).sqrMagnitude < 0.0001f) { val2 = Vector3.Cross(((Component)val).transform.right, Vector3.up); } ((Vector3)(ref val2)).Normalize(); Vector3 val3 = Vector3.Cross(Vector3.up, val2); Vector3 normalized = ((Vector3)(ref val3)).normalized; float num2 = Mathf.Clamp((float)Screen.height / 1080f, 0.5f, 3f); float num3 = (margin.Value + size.Value * 0.5f) * num2; float halfWidth = Mathf.Max(1f, (float)Screen.width * 0.5f - num3); float halfHeight = Mathf.Max(1f, (float)Screen.height * 0.5f - num3); Vector2 position = default(Vector2); foreach (Cue cue2 in cues) { cue2.VisibleAge += Time.unscaledDeltaTime; float visibleAge = cue2.VisibleAge; if (unscaledTime - cue2.SpawnTime < 0.15f) { continue; } Vector3 val4 = ((Component)cue2.Enemy).transform.position - ((Component)val).transform.position; if (!(maxDistance.Value > 0f) || !(((Vector3)(ref val4)).sqrMagnitude > maxDistance.Value * maxDistance.Value)) { Vector3 val5 = Vector3.ProjectOnPlane(val4, Vector3.up); if (!(((Vector3)(ref val5)).sqrMagnitude < 0.0001f)) { float num4 = Vector3.Dot(val5, normalized); float num5 = Vector3.Dot(val5, val2); (float, float) tuple = BearingMath.Edge(num4, num5, halfWidth, halfHeight); ((Vector2)(ref position))..ctor((float)Screen.width * 0.5f + tuple.Item1, (float)Screen.height * 0.5f + tuple.Item2); float magnitude = ((Vector3)(ref val5)).magnitude; float num6 = Mathf.InverseLerp(farDistance.Value, nearDistance.Value, magnitude); float num7 = Mathf.Lerp(0.92f, 1f, num6) + Mathf.Lerp(0.02f, 0.18f, num6) * Mathf.Sin(visibleAge * Mathf.Lerp(2f, 8f, num6)); float num8 = Mathf.Lerp(0.18f, 1f, num6); draws.Add(new DrawCue(position, Mathf.Atan2(num4, num5) * 57.29578f, Mathf.Clamp01(opacity.Value * num8 * num7))); } } } } private void Status(string message) { if (!(lastStatus == message)) { lastStatus = message; ((BaseUnityPlugin)this).Logger.LogInfo((object)message); } } private void OnGUI() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) bool flag = Time.unscaledTime < statusUntil; if ((int)Event.current.type != 7 || (draws.Count == 0 && !flag) || !Object.op_Implicit((Object)(object)glow) || !Object.op_Implicit((Object)(object)arrow)) { return; } Color val = GUI.color; Matrix4x4 matrix = GUI.matrix; int depth = GUI.depth; try { GUI.depth = -50; GUI.matrix = Matrix4x4.identity; if (flag) { if (statusStyle == null) { statusStyle = new GUIStyle(GUI.skin.box) { alignment = (TextAnchor)4, fontSize = 18 }; } GUI.color = Color.white; string arg = (enabledSetting.Value ? "ON" : "OFF"); string text = $"Monster hints: {arg} ({toggle.Value})"; GUI.Box(new Rect((float)Screen.width / 2f - 230f, 75f, 460f, 38f), text, statusStyle); } float num = size.Value * Mathf.Clamp((float)Screen.height / 1080f, 0.5f, 3f); Rect val2 = default(Rect); foreach (DrawCue draw in draws) { Color value = color.Value; value.a *= draw.Alpha; GUI.color = value; ((Rect)(ref val2))..ctor(draw.Position.x - num / 2f, draw.Position.y - num / 2f, num, num); GUI.DrawTexture(val2, (Texture)(object)glow); GUIUtility.RotateAroundPivot(draw.Angle, draw.Position); GUI.DrawTexture(val2, (Texture)(object)arrow); GUI.matrix = Matrix4x4.identity; } } finally { GUI.color = val; GUI.matrix = matrix; GUI.depth = depth; } } private static Texture2D MakeTexture(bool chevron) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(64, 64, (TextureFormat)4, false) { name = (chevron ? "MonsterCompassArrow" : "MonsterCompassGlow"), hideFlags = (HideFlags)61, wrapMode = (TextureWrapMode)1, filterMode = (FilterMode)1 }; Color[] array = (Color[])(object)new Color[4096]; for (int i = 0; i < 64; i++) { for (int j = 0; j < 64; j++) { float num = ((float)j + 0.5f) / 64f * 2f - 1f; float num2 = ((float)i + 0.5f) / 64f * 2f - 1f; float num4; if (chevron) { float num3 = Mathf.Abs(num2 - (0.48f - Mathf.Abs(num) * 0.9f)); num4 = Mathf.Clamp01((0.1f - num3) * 32f) * Mathf.Clamp01((0.6f - Mathf.Abs(num)) * 32f); } else { num4 = Mathf.Pow(Mathf.Clamp01(1f - Mathf.Sqrt(num * num + num2 * num2)), 2f) * 0.65f; } array[i * 64 + j] = new Color(1f, 1f, 1f, num4); } } val.SetPixels(array); val.Apply(false, true); return val; } private void SceneChanged(Scene previous, Scene current) { cues.Clear(); draws.Clear(); spawnTracker.Clear(); nextReconcile = 0f; lastStatus = ""; } private void OnDestroy() { SceneManager.activeSceneChanged -= SceneChanged; enabledSetting.SettingChanged -= EnabledChanged; Harmony? obj = harmony; if (obj != null) { obj.UnpatchSelf(); } cues.Clear(); draws.Clear(); if (Object.op_Implicit((Object)(object)glow)) { Object.Destroy((Object)(object)glow); } if (Object.op_Implicit((Object)(object)arrow)) { Object.Destroy((Object)(object)arrow); } if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } } [HarmonyPatch(typeof(EnemyParent), "SpawnRPC")] internal static class EnemySpawnPatch { [HarmonyPrefix] private static void Prefix(bool ___Spawned, out bool __state) { __state = ___Spawned; } [HarmonyPostfix] private static void Postfix(Enemy ___Enemy, bool ___Spawned, bool __state) { if (!__state && ___Spawned) { Plugin.Instance?.ObserveEnemy(___Enemy, spawned: true, "RPC"); } } } [HarmonyPatch(typeof(EnemyParent), "DespawnRPC")] internal static class EnemyDespawnPatch { [HarmonyPostfix] private static void Postfix(Enemy ___Enemy, bool ___Spawned) { if (!___Spawned) { Plugin.Instance?.ObserveEnemy(___Enemy, spawned: false, "RPC"); } } } internal sealed class SpawnTracker { private readonly HashSet active = new HashSet(); internal bool Observe(int id, bool spawned) { if (spawned) { return active.Add(id); } active.Remove(id); return false; } internal void Clear() { active.Clear(); } } }