Decompiled source of JonathansTaskList v0.1.42

BepInEx\plugins\JonathansTaskList\JonathansTaskList.dll

Decompiled 2 hours ago
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<KeyCode> toggleKey;

		private ConfigEntry<KeyCode> secondaryToggleKey;

		private ConfigEntry<KeyCode> alternateToggleKey;

		private ConfigEntry<float> panelX;

		private ConfigEntry<float> panelY;

		private ConfigEntry<float> panelScale;

		private ConfigEntry<int> panelPlacementVersion;

		private ConfigEntry<bool> autoCaptureWhileTabHeld;

		private ConfigEntry<bool> lockPanel;

		private readonly List<string> capturedObjectives = new List<string>();

		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<KeyCode, bool> previousInputSystemKeyStates = new Dictionary<KeyCode, bool>();

		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<KeyCode>("Hotkeys", "ToggleTaskList", (KeyCode)96, "Captures visible objectives and toggles Jonathan's Task List.");
			secondaryToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Hotkeys", "SecondaryToggleTaskList", (KeyCode)290, "Also captures visible objectives and toggles Jonathan's Task List.");
			alternateToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Hotkeys", "AlternateToggleTaskList", (KeyCode)277, "Alternate key to capture visible objectives and toggle Jonathan's Task List.");
			panelX = ((BaseUnityPlugin)this).Config.Bind<float>("Panel", "X", 100f, "Panel X position.");
			panelY = ((BaseUnityPlugin)this).Config.Bind<float>("Panel", "Y", 32f, "Panel Y position.");
			panelScale = ((BaseUnityPlugin)this).Config.Bind<float>("Panel", "Scale", 1f, new ConfigDescription("Panel scale.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.6f, 2.5f), Array.Empty<object>()));
			panelPlacementVersion = ((BaseUnityPlugin)this).Config.Bind<int>("Panel", "PlacementVersion", 0, "Tracks default panel placement migrations.");
			lockPanel = ((BaseUnityPlugin)this).Config.Bind<bool>("Panel", "LockPanel", false, "Prevents dragging the task list panel.");
			autoCaptureWhileTabHeld = ((BaseUnityPlugin)this).Config.Bind<bool>("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*$", "<color=#B00000>DONE</color>", 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<string> 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<string> NormalizeCapturedCandidates(List<string> rawCandidates)
		{
			List<string> list = new List<string>();
			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<string> tasks)
		{
			List<TaskState> 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<TaskState> ReadTaskManagerStates()
		{
			List<TaskState> list = new List<TaskState>();
			try
			{
				Component val = ((IEnumerable<Component>)Resources.FindObjectsOfTypeAll<Component>()).FirstOrDefault((Func<Component, bool>)((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<string> 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>(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<string> 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<TextCandidate> list = new List<TextCandidate>();
			Component[] array = Resources.FindObjectsOfTypeAll<Component>();
			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, string>((TextCandidate candidate) => candidate.Line, StringComparer.OrdinalIgnoreCase)
					select @group.First()).Take(24).ToList()
				select candidate.Line).ToList();
		}

		private static List<TextCandidate> PairProgressByPosition(List<TextCandidate> candidates)
		{
			List<TextCandidate> list = candidates.Where((TextCandidate candidate) => !IsProgressOnlyLine(candidate.Line) && !IsCompletionOnlyLine(candidate.Line) && !IsGenericTaskHeaderLine(candidate.Line)).ToList();
			List<TextCandidate> list2 = candidates.Where((TextCandidate candidate) => IsProgressOnlyLine(candidate.Line)).ToList();
			List<TextCandidate> list3 = candidates.Where((TextCandidate candidate) => IsCompletionOnlyLine(candidate.Line)).ToList();
			List<TextCandidate> list4 = new List<TextCandidate>();
			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<TextCandidate> 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<TextCandidate> 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<string> list = new List<string>();
			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<string> 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;
			}
		}
	}
}