using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Jonathan's Task List")] [assembly: AssemblyDescription("Always-visible mission objectives for Burglin' Gnomes.")] [assembly: AssemblyCompany("Daniel Rose")] [assembly: AssemblyProduct("Jonathan's Task List")] [assembly: ComVisible(false)] [assembly: AssemblyFileVersion("0.1.42.0")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = "")] [assembly: AssemblyVersion("0.1.42.0")] 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; } } } namespace JonathansTaskList { [BepInPlugin("jonathan.burglignomes.tasklist", "Jonathan's Task List", "0.1.42")] public sealed class Plugin : BaseUnityPlugin { private readonly struct TaskState { public int Current { get; } public int Max { get; } public bool Completed { get; } public TaskState(int current, int max, bool completed) { Current = current; Max = max; Completed = completed || (max > 0 && current >= max); } } public const string PluginGuid = "jonathan.burglignomes.tasklist"; public const string PluginName = "Jonathan's Task List"; public const string PluginVersion = "0.1.42"; private const int ObjectiveFontSize = 14; private const int MaxObjectiveRows = 5; private const float AutoStateRefreshInterval = 0.5f; private const float DefaultTopRightMargin = 24f; private const float DefaultTopMargin = 32f; private const string CompletionMarker = "✓"; private ConfigEntry toggleKey; private ConfigEntry secondaryToggleKey; private ConfigEntry alternateToggleKey; private ConfigEntry panelX; private ConfigEntry panelY; private ConfigEntry panelScale; private ConfigEntry panelPlacementVersion; private ConfigEntry autoCaptureWhileTabHeld; private ConfigEntry lockPanel; private readonly List capturedObjectives = new List(); private Rect panelRect; private bool showPanel; private bool stylesReady; private bool isDragging; private Vector2 dragOffset; private GUIStyle paperStyle; private GUIStyle titleStyle; private GUIStyle objectiveStyle; private GUIStyle smallStyle; private Texture2D paperTexture; private Font? bahnschrift; private float lastAutoCaptureTime; private float lastStateRefreshTime; private bool firstGuiLogged; private bool inputSystemLogged; private bool rawCaptureLogged; private string lastRawCaptureSignature = string.Empty; private string lastKeptCaptureSignature = string.Empty; private readonly Dictionary previousInputSystemKeyStates = new Dictionary(); private string lastSceneName = string.Empty; private void Awake() { //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Expected O, but got Unknown //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Invalid comparison between Unknown and I4 //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) toggleKey = ((BaseUnityPlugin)this).Config.Bind("Hotkeys", "ToggleTaskList", (KeyCode)96, "Captures visible objectives and toggles Jonathan's Task List."); secondaryToggleKey = ((BaseUnityPlugin)this).Config.Bind("Hotkeys", "SecondaryToggleTaskList", (KeyCode)290, "Also captures visible objectives and toggles Jonathan's Task List."); alternateToggleKey = ((BaseUnityPlugin)this).Config.Bind("Hotkeys", "AlternateToggleTaskList", (KeyCode)277, "Alternate key to capture visible objectives and toggle Jonathan's Task List."); panelX = ((BaseUnityPlugin)this).Config.Bind("Panel", "X", 100f, "Panel X position."); panelY = ((BaseUnityPlugin)this).Config.Bind("Panel", "Y", 32f, "Panel Y position."); panelScale = ((BaseUnityPlugin)this).Config.Bind("Panel", "Scale", 1f, new ConfigDescription("Panel scale.", (AcceptableValueBase)(object)new AcceptableValueRange(0.6f, 2.5f), Array.Empty())); panelPlacementVersion = ((BaseUnityPlugin)this).Config.Bind("Panel", "PlacementVersion", 0, "Tracks default panel placement migrations."); lockPanel = ((BaseUnityPlugin)this).Config.Bind("Panel", "LockPanel", false, "Prevents dragging the task list panel."); autoCaptureWhileTabHeld = ((BaseUnityPlugin)this).Config.Bind("Capture", "AutoCaptureWhileTabHeld", true, "Updates the task list while TAB is held and the game's objectives are visible."); if ((int)toggleKey.Value == 289) { toggleKey.Value = (KeyCode)96; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Migrated task-list hotkey from F8 to BackQuote because F8 conflicts with many mods."); } panelRect = new Rect(panelX.Value, panelY.Value, 270f, 190f); ((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded. Toggle={2}/{3}/{4}", "Jonathan's Task List", "0.1.42", toggleKey.Value, secondaryToggleKey.Value, alternateToggleKey.Value)); } private void Start() { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Jonathan's Task List is active. Listening through legacy input, Unity GUI key events, and Unity Input System."); } private void Update() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected I4, but got Unknown //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected I4, but got Unknown //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected I4, but got Unknown HideOnMainMenu(); if (AnyKeyDown((KeyCode)(int)toggleKey.Value, (KeyCode)(int)secondaryToggleKey.Value, (KeyCode)(int)alternateToggleKey.Value)) { ToggleTaskList("configured hotkey"); } if (autoCaptureWhileTabHeld.Value && (SafeKeyHeld((KeyCode)9) || InputSystemKeyHeld((KeyCode)9)) && Time.unscaledTime - lastAutoCaptureTime > 0.4f) { CaptureVisibleObjectives(showPanelWhenCaptured: true); lastAutoCaptureTime = Time.unscaledTime; } if (capturedObjectives.Count > 0 && Time.unscaledTime - lastStateRefreshTime > 0.5f) { RefreshCapturedObjectiveState(); lastStateRefreshTime = Time.unscaledTime; } } private void OnGUI() { EnsureStyles(); if (!firstGuiLogged) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Jonathan's Task List OnGUI is active."); firstGuiLogged = true; } HandleGuiHotkeys(); if (showPanel) { DrawObjectivePanel(); } } private void HandleGuiHotkeys() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) Event current = Event.current; if (current != null && (int)current.type == 4 && (current.keyCode == toggleKey.Value || current.keyCode == secondaryToggleKey.Value || current.keyCode == alternateToggleKey.Value)) { ToggleTaskList($"GUI {current.keyCode}"); current.Use(); } } private bool AnyKeyDown(params KeyCode[] keyCodes) { //IL_0009: 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_0014: Unknown result type (might be due to invalid IL or missing references) foreach (KeyCode keyCode in keyCodes) { if (SafeKeyDown(keyCode) || InputSystemKeyDown(keyCode)) { return true; } } return false; } private void ToggleTaskList(string source) { CaptureVisibleObjectives(); showPanel = !showPanel; ((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("Task list toggled {0} by {1}. Captured lines: {2}.", showPanel ? "on" : "off", source, capturedObjectives.Count)); } private void HideOnMainMenu() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); string text = ((Scene)(ref activeScene)).name ?? string.Empty; if (!string.Equals(text, lastSceneName, StringComparison.Ordinal)) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Active scene changed to '" + text + "'.")); lastSceneName = text; } string text2 = text.ToLowerInvariant(); if ((text2.Contains("menu") || text2.Contains("title")) && (showPanel || capturedObjectives.Count > 0)) { showPanel = false; capturedObjectives.Clear(); rawCaptureLogged = false; lastRawCaptureSignature = string.Empty; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Task list hidden because the main menu/title scene is active."); } } private bool SafeKeyDown(KeyCode keyCode) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) try { return Input.GetKeyDown(keyCode); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Legacy key check failed: " + ex.GetType().Name + ": " + ex.Message)); return false; } } private bool SafeKeyHeld(KeyCode keyCode) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) try { return Input.GetKey(keyCode); } catch { return false; } } private bool InputSystemKeyDown(KeyCode keyCode) { //IL_0001: 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_002f: Unknown result type (might be due to invalid IL or missing references) KeyControl inputSystemKey = GetInputSystemKey(keyCode); if (inputSystemKey == null) { return false; } LogInputSystemOnce(); bool isPressed = ((ButtonControl)inputSystemKey).isPressed; previousInputSystemKeyStates.TryGetValue(keyCode, out var value); previousInputSystemKeyStates[keyCode] = isPressed; if (isPressed) { return !value; } return false; } private bool InputSystemKeyHeld(KeyCode keyCode) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) KeyControl inputSystemKey = GetInputSystemKey(keyCode); if (inputSystemKey == null) { return false; } LogInputSystemOnce(); return ((ButtonControl)inputSystemKey).isPressed; } private KeyControl? GetInputSystemKey(KeyCode keyCode) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Invalid comparison between Unknown and I4 //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Invalid comparison between Unknown and I4 //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Expected I4, but got Unknown //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected I4, but got Unknown Keyboard current = Keyboard.current; if (current == null) { return null; } if ((int)keyCode <= 57) { if ((int)keyCode == 9) { return current.tabKey; } switch (keyCode - 48) { case 0: return current.digit0Key; case 1: return current.digit1Key; case 2: return current.digit2Key; case 3: return current.digit3Key; case 4: return current.digit4Key; case 5: return current.digit5Key; case 6: return current.digit6Key; case 7: return current.digit7Key; case 8: return current.digit8Key; case 9: return current.digit9Key; } } else { if ((int)keyCode == 96) { return current.backquoteKey; } switch (keyCode - 277) { case 31: return current.leftAltKey; case 30: return current.rightAltKey; case 5: return current.f1Key; case 6: return current.f2Key; case 7: return current.f3Key; case 8: return current.f4Key; case 9: return current.f5Key; case 10: return current.f6Key; case 11: return current.f7Key; case 12: return current.f8Key; case 13: return current.f9Key; case 14: return current.f10Key; case 15: return current.f11Key; case 16: return current.f12Key; case 0: return current.insertKey; case 1: return current.homeKey; case 2: return current.endKey; case 3: return current.pageUpKey; case 4: return current.pageDownKey; } } return null; } private void LogInputSystemOnce() { if (!inputSystemLogged) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Jonathan's Task List found Unity Input System keyboard."); inputSystemLogged = true; } } private void DrawObjectivePanel() { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Expected O, but got Unknown //IL_01e5: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Clamp(panelScale.Value, 0.6f, 2.5f); titleStyle.fontSize = 16; objectiveStyle.fontSize = 14; float num2 = 300f; float num3 = (num2 + 28f) * num; float height = CalculatePanelHeight(num2) * num; ((Rect)(ref panelRect)).width = num3; ((Rect)(ref panelRect)).height = height; ApplyTopRightPlacementIfNeeded(num3); GUI.Box(panelRect, GUIContent.none, paperStyle); Rect dragArea = default(Rect); ((Rect)(ref dragArea))..ctor(((Rect)(ref panelRect)).x, ((Rect)(ref panelRect)).y, ((Rect)(ref panelRect)).width, 28f * num); HandlePanelDrag(dragArea); Matrix4x4 matrix = GUI.matrix; GUIUtility.ScaleAroundPivot(new Vector2(num, num), ((Rect)(ref panelRect)).position); Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref panelRect)).x + 14f, ((Rect)(ref panelRect)).y + 10f, num2, ((Rect)(ref panelRect)).height / num - 20f); GUI.Label(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y, ((Rect)(ref val)).width, 22f), "Tasks", titleStyle); if (capturedObjectives.Count == 0) { GUI.Label(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y + 30f, ((Rect)(ref val)).width, 70f), "Hold TAB so the game objectives are visible, then press the toggle hotkey.", objectiveStyle); } else { float num4 = ((Rect)(ref val)).y + 30f; foreach (string capturedObjective in capturedObjectives) { string text = FormatObjectiveForDisplay(capturedObjective); float num5 = objectiveStyle.CalcHeight(new GUIContent(text), ((Rect)(ref val)).width); GUI.Label(new Rect(((Rect)(ref val)).x, num4, ((Rect)(ref val)).width, num5), text, objectiveStyle); num4 += num5 + 8f; } } GUI.matrix = matrix; } private float CalculatePanelHeight(float contentWidth) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown float num = 58f; if (capturedObjectives.Count == 0) { return 120f; } foreach (string capturedObjective in capturedObjectives) { num += objectiveStyle.CalcHeight(new GUIContent(FormatObjectiveForDisplay(capturedObjective)), contentWidth) + 8f; } return Mathf.Max(120f, num); } private static string FormatObjectiveForDisplay(string objective) { return Regex.Replace(objective, "\\bDONE\\s*$", "DONE", RegexOptions.IgnoreCase); } private void ApplyTopRightPlacementIfNeeded(float panelWidth) { if (panelPlacementVersion.Value < 2) { float num = ((Screen.width > 0) ? ((float)Screen.width) : 1920f); ((Rect)(ref panelRect)).x = Mathf.Max(24f, num - panelWidth - 24f); ((Rect)(ref panelRect)).y = 32f; SavePanelPosition(); panelPlacementVersion.Value = 2; ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Moved the task-list panel to the top-right at X={((Rect)(ref panelRect)).x:0}, Y={((Rect)(ref panelRect)).y:0}."); } } private void HandlePanelDrag(Rect dragArea) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Invalid comparison between Unknown and I4 //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Invalid comparison between Unknown and I4 //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0047: 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) if (!lockPanel.Value) { Event current = Event.current; if ((int)current.type == 0 && current.button == 0 && ((Rect)(ref dragArea)).Contains(current.mousePosition)) { isDragging = true; dragOffset = current.mousePosition - ((Rect)(ref panelRect)).position; current.Use(); } if (isDragging && (int)current.type == 3) { ((Rect)(ref panelRect)).position = current.mousePosition - dragOffset; SavePanelPosition(); current.Use(); } if ((int)current.type == 1) { isDragging = false; } } } private void SavePanelPosition() { panelX.Value = ((Rect)(ref panelRect)).x; panelY.Value = ((Rect)(ref panelRect)).y; } private void CaptureVisibleObjectives(bool showPanelWhenCaptured = false) { List list = NormalizeCapturedCandidates(ObjectiveTextScanner.FindVisibleObjectiveText()).Take(5).ToList(); ApplyTaskManagerState(list); string text = string.Join(" | ", list); if (list.Count > 0 && (!rawCaptureLogged || !string.Equals(text, lastRawCaptureSignature, StringComparison.Ordinal))) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Task capture order: " + text)); rawCaptureLogged = true; lastRawCaptureSignature = text; } capturedObjectives.RemoveAll(IsGenericTaskHeader); if (list.Count == 0) { return; } if (list.Count >= 5) { capturedObjectives.Clear(); capturedObjectives.AddRange(list); } else { bool allowAdd = capturedObjectives.Count < 5; foreach (string item in list) { AddOrUpdateTaskLine(capturedObjectives, item, allowAdd); allowAdd = capturedObjectives.Count < 5; } } if (capturedObjectives.Count > 0) { if (showPanelWhenCaptured) { showPanel = true; } string a = string.Join(" | ", capturedObjectives); if (!string.Equals(a, lastKeptCaptureSignature, StringComparison.Ordinal)) { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Captured {list.Count} visible objective line(s); keeping {capturedObjectives.Count} total line(s)."); lastKeptCaptureSignature = a; } } } private void RefreshCapturedObjectiveState() { string a = string.Join(" | ", capturedObjectives); ApplyTaskManagerState(capturedObjectives); string text = string.Join(" | ", capturedObjectives); if (!string.Equals(a, text, StringComparison.Ordinal)) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Auto-updated task state: " + text)); lastKeptCaptureSignature = text; lastRawCaptureSignature = text; } } private static List NormalizeCapturedCandidates(List rawCandidates) { List list = new List(); foreach (string rawCandidate in rawCandidates) { string text = NormalizeCompletionMarker(rawCandidate.Trim()); if (text.Length == 0 || IsGenericTaskHeader(text)) { continue; } bool completed = IsCompleted(text); string line = RemoveCompletionMarker(text); if (ExtractProgress(line) == null) { string text2 = ExtractTrailingProgress(line); if (text2 != null) { AddOrUpdateTaskLine(list, FormatTaskLine(GetTaskBaseName(line), text2, completed)); } else { AddOrUpdateTaskLine(list, FormatTaskLine(GetTaskBaseName(line), null, completed)); } } } return list; } private static void ApplyTaskManagerState(List tasks) { List list = ReadTaskManagerStates(); if (list.Count < tasks.Count) { return; } for (int i = 0; i < tasks.Count; i++) { TaskState taskState = list[i]; string taskBaseName = GetTaskBaseName(tasks[i]); if (taskBaseName.Length != 0 && !IsGenericTaskHeader(taskBaseName)) { if (taskState.Completed) { tasks[i] = FormatTaskLine(taskBaseName, null, completed: true); } else if (taskState.Max > 1) { tasks[i] = FormatTaskLine(taskBaseName, $"{taskState.Current}/{taskState.Max}", completed: false); } } } } private static List ReadTaskManagerStates() { List list = new List(); try { Component val = ((IEnumerable)Resources.FindObjectsOfTypeAll()).FirstOrDefault((Func)((Component component) => (Object)(object)component != (Object)null && (Object)(object)component.gameObject != (Object)null && component.gameObject.activeInHierarchy && string.Equals(((object)component).GetType().Name, "PlayerTaskManager", StringComparison.Ordinal))); if ((Object)(object)val == (Object)null) { return list; } object obj = ((object)val).GetType().GetProperty("CurrentTasks", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(val, null); if (obj == null) { return list; } PropertyInfo property = obj.GetType().GetProperty("Length", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); PropertyInfo property2 = obj.GetType().GetProperty("Item", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property == null || property2 == null) { return list; } int val2 = Convert.ToInt32(property.GetValue(obj, null)); for (int num = 0; num < Math.Min(val2, 5); num++) { object value = property2.GetValue(obj, new object[1] { num }); if (value != null) { list.Add(new TaskState(ReadIntField(value, "currentCompletionCount"), ReadIntField(value, "maxCompletionCount"), ReadBoolField(value, "wasCompleted"))); } } } catch { list.Clear(); } return list; } private static int ReadIntField(object instance, string fieldName) { object obj = instance.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(instance); if (obj != null) { return Convert.ToInt32(obj); } return 0; } private static bool ReadBoolField(object instance, string fieldName) { object obj = instance.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(instance); if (obj != null) { return Convert.ToBoolean(obj); } return false; } private static void AddOrUpdateTaskLine(List tasks, string incomingLine, bool allowAdd = true) { string taskBaseName = GetTaskBaseName(incomingLine); if (taskBaseName.Length == 0 || IsGenericTaskHeader(taskBaseName)) { return; } for (int i = 0; i < tasks.Count; i++) { if (string.Equals(GetTaskBaseName(tasks[i]), taskBaseName, StringComparison.OrdinalIgnoreCase)) { tasks[i] = incomingLine; return; } } if (allowAdd) { tasks.Add(incomingLine); } } private static bool IsGenericTaskHeader(string line) { string text = GetTaskBaseName(line).ToLowerInvariant(); switch (text) { default: return text == "objectives"; case "task": case "tasks": case "objective": return true; } } private static string? ExtractProgress(string line) { Match match = Regex.Match(line.Trim(), "^(\\d+\\s*/\\s*\\d+)(?:\\s+tasks?)?$", RegexOptions.IgnoreCase); if (!match.Success) { return null; } return Regex.Replace(match.Groups[1].Value, "\\s+", string.Empty); } private static string? ExtractTrailingProgress(string line) { Match match = Regex.Match(RemoveCompletionMarker(line).Trim(), "\\b(\\d+\\s*/\\s*\\d+)\\)?\\s*$", RegexOptions.IgnoreCase); if (!match.Success) { return null; } return Regex.Replace(match.Groups[1].Value, "\\s+", string.Empty); } private static bool HasProgress(string line) { return Regex.IsMatch(line, "\\(\\d+\\s*/\\s*\\d+\\)|\\b\\d+\\s*/\\s*\\d+\\b", RegexOptions.IgnoreCase); } private static string GetTaskBaseName(string line) { return Regex.Replace(Regex.Replace(RemoveCompletionMarker(line), "\\s*\\(?\\b\\d+\\s*/\\s*\\d+\\b(?:\\s+tasks?)?\\)?\\s*$", string.Empty, RegexOptions.IgnoreCase), "\\s+", " ").Trim(); } private static string FormatTaskLine(string taskBase, string? progress, bool completed) { taskBase = GetTaskBaseName(taskBase); string text = taskBase; if (!string.IsNullOrWhiteSpace(progress)) { text = text + " (" + progress + ")"; } if (!completed && !IsCompleteProgress(progress)) { return text; } return taskBase + " DONE"; } private static bool IsCompleteProgress(string? progress) { if (string.IsNullOrWhiteSpace(progress)) { return false; } Match match = Regex.Match(progress, "^(\\d+)\\s*/\\s*(\\d+)$"); if (match.Success && int.TryParse(match.Groups[1].Value, out var result) && int.TryParse(match.Groups[2].Value, out var result2) && result2 > 0) { return result >= result2; } return false; } private static string NormalizeCompletionMarker(string line) { return Regex.Replace(line, "[\\u2713\\u2714]\\s*$", "✓").Trim(); } private static string RemoveCompletionMarker(string line) { return Regex.Replace(line, "(?:\\s*(?:[\\u2713\\u2714]|DONE))+\\s*$", string.Empty, RegexOptions.IgnoreCase).Trim(); } private static bool IsCompleted(string line) { return Regex.IsMatch(line.Trim(), "(?:[\\u2713\\u2714]|DONE)\\s*$", RegexOptions.IgnoreCase); } private void EnsureStyles() { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Expected O, but got Unknown //IL_0099: Expected O, but got Unknown //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Expected O, but got Unknown //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Expected O, but got Unknown //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0173: 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_019c: Expected O, but got Unknown if (!stylesReady) { bahnschrift = Font.CreateDynamicFontFromOSFont(new string[2] { "Bahnschrift", "Arial" }, 14); paperTexture = MakeTexture(new Color(0.93f, 0.9f, 0.8f, 0.96f)); GUIStyle val = new GUIStyle(GUI.skin.box); val.normal.background = paperTexture; val.border = new RectOffset(8, 8, 8, 8); val.padding = new RectOffset(12, 12, 10, 10); paperStyle = val; GUIStyle val2 = new GUIStyle(GUI.skin.label) { font = bahnschrift, fontSize = 14, fontStyle = (FontStyle)1 }; val2.normal.textColor = new Color(0.15f, 0.12f, 0.08f, 1f); titleStyle = val2; GUIStyle val3 = new GUIStyle(GUI.skin.label) { font = bahnschrift, fontSize = 14, wordWrap = true, richText = true }; val3.normal.textColor = new Color(0.12f, 0.1f, 0.07f, 1f); objectiveStyle = val3; GUIStyle val4 = new GUIStyle(GUI.skin.label) { font = bahnschrift, fontSize = 11, wordWrap = true }; val4.normal.textColor = new Color(0.18f, 0.15f, 0.1f, 1f); smallStyle = val4; stylesReady = true; } } private Texture2D MakeTexture(Color color) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); Color[] pixels = Enumerable.Repeat(color, 4).ToArray(); val.SetPixels(pixels); val.Apply(); return val; } } internal static class ObjectiveTextScanner { private readonly struct TextCandidate { public string Line { get; } public float Y { get; } public float X { get; } public int SiblingIndex { get; } public string SourceType { get; } public string Path { get; } public TextCandidate(string line, float y, float x, int siblingIndex, string sourceType, string path) { Line = line; Y = y; X = x; SiblingIndex = siblingIndex; SourceType = sourceType; Path = path; } } private static readonly string[] ObjectiveWords = new string[11] { "objective", "mission", "task", "steal", "break", "bring", "collect", "escape", "complete", "tase", "shoot" }; public static List FindVisibleObjectiveText() { //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) List list = new List(); Component[] array = Resources.FindObjectsOfTypeAll(); foreach (Component val in array) { if ((Object)(object)val == (Object)null || (Object)(object)val.gameObject == (Object)null || !val.gameObject.activeInHierarchy || !LooksLikeTextComponent(((object)val).GetType().Name)) { continue; } string text = TryReadText(val); if (string.IsNullOrWhiteSpace(text)) { continue; } bool objectiveContext = LooksLikeObjectiveContext(val, text); foreach (string item in SplitLines(text)) { if (LooksLikeObjectiveLine(item, objectiveContext)) { Vector2 textPosition = GetTextPosition(val); list.Add(new TextCandidate(NormalizeLine(item), textPosition.y, textPosition.x, val.transform.GetSiblingIndex(), ((object)val).GetType().Name, GetTransformPath(val.transform))); } } } return (from candidate in (from @group in (from candidate in PairProgressByPosition(list) orderby candidate.Y descending, candidate.X, candidate.SiblingIndex select candidate).GroupBy((TextCandidate candidate) => candidate.Line, StringComparer.OrdinalIgnoreCase) select @group.First()).Take(24).ToList() select candidate.Line).ToList(); } private static List PairProgressByPosition(List candidates) { List list = candidates.Where((TextCandidate candidate) => !IsProgressOnlyLine(candidate.Line) && !IsCompletionOnlyLine(candidate.Line) && !IsGenericTaskHeaderLine(candidate.Line)).ToList(); List list2 = candidates.Where((TextCandidate candidate) => IsProgressOnlyLine(candidate.Line)).ToList(); List list3 = candidates.Where((TextCandidate candidate) => IsCompletionOnlyLine(candidate.Line)).ToList(); List list4 = new List(); foreach (TextCandidate item in list3) { int num = FindNearestTaskIndex(list, item, allowLeftOfTask: true, 60f); if (num >= 0) { TextCandidate textCandidate = list[num]; if (!HasCompletionMarker(textCandidate.Line)) { list[num] = new TextCandidate(textCandidate.Line + " ✓", textCandidate.Y, textCandidate.X, textCandidate.SiblingIndex, textCandidate.SourceType, textCandidate.Path); } } } foreach (TextCandidate item2 in list2) { int num2 = FindSameRowProgressTaskIndex(list, item2); if (num2 >= 0) { TextCandidate textCandidate2 = list[num2]; string line = (HasCompletionMarker(textCandidate2.Line) ? (RemoveCompletionMarker(textCandidate2.Line) + " " + item2.Line + " ✓") : (textCandidate2.Line + " " + item2.Line)); list[num2] = new TextCandidate(line, textCandidate2.Y, textCandidate2.X, textCandidate2.SiblingIndex, textCandidate2.SourceType, textCandidate2.Path); } else { list4.Add(item2); } } return list.Concat(list4).ToList(); } private static int FindSameRowProgressTaskIndex(List taskCandidates, TextCandidate progressCandidate) { int result = -1; float num = float.MaxValue; for (int i = 0; i < taskCandidates.Count; i++) { if (Regex.IsMatch(taskCandidates[i].Line, "\\b\\d+\\s*/\\s*\\d+\\b")) { continue; } float num2 = Math.Abs(taskCandidates[i].Y - progressCandidate.Y); if (num2 > 14f) { continue; } float num3 = progressCandidate.X - taskCandidates[i].X; if (!(num3 < -40f) && !(num3 > 700f)) { float num4 = num2 + Math.Abs(num3) * 0.01f; if (num4 < num) { num = num4; result = i; } } } return result; } private static int FindNearestTaskIndex(List taskCandidates, TextCandidate markerCandidate, bool allowLeftOfTask, float maxYDistance = 24f) { int result = -1; float num = float.MaxValue; for (int i = 0; i < taskCandidates.Count; i++) { if (Regex.IsMatch(taskCandidates[i].Line, "\\b\\d+\\s*/\\s*\\d+\\b")) { continue; } float num2 = Math.Abs(taskCandidates[i].Y - markerCandidate.Y); if (num2 > maxYDistance) { continue; } float num3 = markerCandidate.X - taskCandidates[i].X; float num4 = (allowLeftOfTask ? (-180f) : (-20f)); if (!(num3 < num4) && !(num3 > 700f)) { float num5 = num2 + Math.Abs(num3) * 0.01f; if (num5 < num) { num = num5; result = i; } } } return result; } private static Vector2 GetTextPosition(Component component) { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) Transform transform = component.transform; RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null); if ((Object)(object)val != (Object)null) { Vector3[] array = (Vector3[])(object)new Vector3[4]; val.GetWorldCorners(array); float num = (array[0].x + array[2].x) * 0.5f; float num2 = (array[0].y + array[2].y) * 0.5f; return new Vector2(num, num2); } Vector3 position = component.transform.position; return new Vector2(position.x, position.y); } private static string GetTransformPath(Transform transform) { List list = new List(); Transform val = transform; int num = 0; while ((Object)(object)val != (Object)null && num < 10) { list.Add(((Object)val).name); val = val.parent; num++; } list.Reverse(); return string.Join("/", list); } private static bool LooksLikeTextComponent(string typeName) { if (typeName.IndexOf("Text", StringComparison.OrdinalIgnoreCase) < 0) { return typeName.IndexOf("TMP", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } private static string? TryReadText(Component component) { Type type = ((object)component).GetType(); PropertyInfo property = type.GetProperty("text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.PropertyType == typeof(string)) { return property.GetValue(component, null) as string; } return (type.GetField("m_text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetField("text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(component) as string; } private static bool LooksLikeObjectiveContext(Component component, string text) { string lowerText = text.ToLowerInvariant(); if (ObjectiveWords.Any((string word) => lowerText.Contains(word))) { return true; } Transform val = component.transform; int num = 0; while ((Object)(object)val != (Object)null && num < 8) { string text2 = ((Object)val).name.ToLowerInvariant(); if (text2.Contains("objective") || text2.Contains("mission") || text2.Contains("task")) { return true; } val = val.parent; num++; } return false; } private static IEnumerable SplitLines(string text) { return from line in text.Replace("\r", "\n").Split(new char[1] { '\n' }, StringSplitOptions.RemoveEmptyEntries).Select(NormalizeLine) where line.Length > 0 select line; } private static string NormalizeLine(string line) { return line.Trim().TrimStart('-', '*', '>', '[', ']').Trim(); } private static bool LooksLikeObjectiveLine(string line, bool objectiveContext) { if (line.Length < 3 || line.Length > 140) { return false; } if (IsProgressOnlyLine(line)) { return true; } if (IsCompletionOnlyLine(line)) { return true; } if (line.Count(char.IsLetter) < 3) { return false; } string lower = line.ToLowerInvariant(); if (objectiveContext && !LooksLikeGenericUiLabel(lower)) { return true; } if (!ObjectiveWords.Any((string word) => lower.Contains(word))) { return Regex.IsMatch(lower, "\\b\\d+\\s*/\\s*\\d+\\b"); } return true; } private static bool IsProgressOnlyLine(string line) { return Regex.IsMatch(line.Trim(), "^\\d+\\s*/\\s*\\d+$", RegexOptions.IgnoreCase); } private static bool IsGenericTaskHeaderLine(string line) { string text = Regex.Replace(Regex.Replace(line, "\\s*\\(?\\b\\d+\\s*/\\s*\\d+\\b(?:\\s+tasks?)?\\)?\\s*$", string.Empty, RegexOptions.IgnoreCase), "\\s+", " ").Trim().ToLowerInvariant(); switch (text) { default: return text == "objectives"; case "task": case "tasks": case "objective": return true; } } private static bool IsCompletionOnlyLine(string line) { return Regex.IsMatch(line.Trim(), "^(?:task\\s+)?completed!?\\s*$", RegexOptions.IgnoreCase); } private static bool HasCompletionMarker(string line) { return Regex.IsMatch(line.Trim(), "[\\u2713\\u2714]\\s*$"); } private static string RemoveCompletionMarker(string line) { return Regex.Replace(line, "\\s*[\\u2713\\u2714]\\s*$", string.Empty).Trim(); } private static bool LooksLikeGenericUiLabel(string lower) { switch (lower) { default: return lower == "controls"; case "objectives": case "mission": case "missions": case "tasks": case "settings": case "resume": case "quit": case "back": case "audio": case "video": return true; } } } }