Decompiled source of BacklogImprovements v2.1.0

BepInEx/plugins/BacklogImprovements.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Pigeon;
using Pigeon.Movement;
using Pigeon.UI;
using Sparroh.UI;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("BacklogImprovements")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("BacklogImprovements")]
[assembly: AssemblyTitle("BacklogImprovements")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
public static class BacklogUI
{
	private sealed class WindowState
	{
		public DirectiveWindow Window;

		public GameObject Root;

		public UIButton EditButton;

		public UIButton ClearButton;

		public UIButton PageButton;

		public UIButton SingleButton;

		public UIButton CancelButton;

		public bool RerollSelecting;

		public readonly List<GameObject> RerollOverlays = new List<GameObject>();
	}

	private static readonly FieldInfo DirectivesField = AccessTools.Field(typeof(DirectiveWindow), "directives");

	private static readonly FieldInfo IsAnimatingField = AccessTools.Field(typeof(DirectiveWindow), "isAnimatingPage");

	private static readonly FieldInfo DirectiveField = AccessTools.Field(typeof(DirectiveButton), "directive");

	private static readonly Dictionary<int, WindowState> States = new Dictionary<int, WindowState>();

	private const float BarLeftAnchor = 0.12f;

	private const float CompactFontSize = 13f;

	private const float MainButtonMinWidth = 120f;

	private const float ButtonHeight = 32f;

	private const float BarWidth = 720f;

	private const float BarHeight = 40f;

	private const float BarBottomPad = 18f;

	private const float ButtonGap = 4f;

	private const float LabelPad = 8f;

	private const float TextWidthPad = 24f;

	public static bool IsPathEditing => PathLogic.IsEditing;

	public static bool IsRerollSelecting
	{
		get
		{
			foreach (KeyValuePair<int, WindowState> state in States)
			{
				if (state.Value.RerollSelecting)
				{
					return true;
				}
			}
			return false;
		}
	}

	public static bool IsAnySelectionMode
	{
		get
		{
			if (!IsPathEditing)
			{
				return IsRerollSelecting;
			}
			return true;
		}
	}

	public static void OnWindowOpened(DirectiveWindow window)
	{
		if ((Object)(object)window == (Object)null)
		{
			return;
		}
		FreePages.ApplyFreeNextPage(window);
		bool flag = BacklogImprovementsPlugin.EnablePreselect?.Value ?? true;
		bool flag2 = BacklogImprovementsPlugin.EnableReroll?.Value ?? true;
		if (!flag && !flag2)
		{
			return;
		}
		int instanceID = ((Object)window).GetInstanceID();
		if (States.TryGetValue(instanceID, out var value) && (Object)(object)value.Root != (Object)null)
		{
			value.Window = window;
			ExitPathEdit(value, activateNext: false, refreshWindow: false);
			ExitRerollSelection(value);
			RefreshAll(value);
			if (flag)
			{
				PathVisualizer.Refresh(window);
			}
			return;
		}
		WindowState windowState = BuildUi(window, flag, flag2);
		if (windowState != null)
		{
			States[instanceID] = windowState;
			RefreshAll(windowState);
			if (flag)
			{
				PathVisualizer.Refresh(window);
			}
		}
	}

	public static void OnWindowClosed(DirectiveWindow window)
	{
		if ((Object)(object)window == (Object)null)
		{
			return;
		}
		int instanceID = ((Object)window).GetInstanceID();
		if (States.TryGetValue(instanceID, out var value))
		{
			if (PathLogic.IsEditing)
			{
				PathLogic.SetEditing(editing: false);
			}
			PathVisualizer.Clear(window);
			DestroyState(value);
			States.Remove(instanceID);
		}
	}

	public static void OnDirectivesRefreshed(DirectiveWindow window)
	{
		if ((Object)(object)window == (Object)null)
		{
			return;
		}
		FreePages.ApplyFreeNextPage(window);
		if (!States.TryGetValue(((Object)window).GetInstanceID(), out var value))
		{
			return;
		}
		value.Window = window;
		RefreshAll(value);
		ConfigEntry<bool> enablePreselect = BacklogImprovementsPlugin.EnablePreselect;
		if (enablePreselect == null || enablePreselect.Value)
		{
			PathVisualizer.Refresh(window);
			if (!PathLogic.IsEditing)
			{
				PathLogic.TryAutoClaim(window);
			}
		}
		if (value.RerollSelecting)
		{
			RebuildRerollOverlays(value);
		}
	}

	public static void CleanupAll()
	{
		PathLogic.SetEditing(editing: false);
		foreach (KeyValuePair<int, WindowState> state in States)
		{
			DestroyState(state.Value);
		}
		States.Clear();
		PathVisualizer.CleanupAll();
	}

	public static bool TryHandleDirectiveClick(DirectiveButton button)
	{
		if ((Object)(object)button == (Object)null)
		{
			return false;
		}
		if (PathLogic.IsEditing)
		{
			return TryHandlePathClick(button);
		}
		if (IsRerollSelecting)
		{
			return TryHandleRerollClick(button);
		}
		return false;
	}

	private static bool TryHandlePathClick(DirectiveButton button)
	{
		WindowState windowState = FindStateForButton(button);
		if (windowState == null)
		{
			return false;
		}
		if (IsAnimating(windowState.Window))
		{
			return true;
		}
		object? obj = DirectiveField?.GetValue(button);
		DirectiveInstance val = (DirectiveInstance)((obj is DirectiveInstance) ? obj : null);
		int num = PathLogic.IndexOf(val);
		if (num < 0)
		{
			return true;
		}
		if (val != null && val.IsComplete && val.HasClaimedRewards)
		{
			UIDialog.Alert("Cannot Select", "That directive is already finished.", (Action)null, "OK");
			return true;
		}
		PathStore.ToggleSelection(PathStore.CurrentPage, num);
		RefreshAll(windowState);
		PathVisualizer.Refresh(windowState.Window);
		DirectiveWindow window = windowState.Window;
		if (window != null)
		{
			window.SetupDirectives(false);
		}
		return true;
	}

	private static bool TryHandleRerollClick(DirectiveButton button)
	{
		WindowState state = FindStateForButton(button);
		if (state == null || !state.RerollSelecting)
		{
			return false;
		}
		if (IsAnimating(state.Window))
		{
			return true;
		}
		object? obj = DirectiveField?.GetValue(button);
		DirectiveInstance val = (DirectiveInstance)((obj is DirectiveInstance) ? obj : null);
		int index = RerollLogic.IndexOf(val);
		if (index < 0 || !RerollLogic.IsEligible(val))
		{
			UIDialog.Alert("Cannot Reroll", "Only not-started directives can be rerolled.", (Action)null, "OK");
			return true;
		}
		int singleRerollCost = RerollLogic.GetSingleRerollCost();
		string text = val.GetTitle();
		if (string.IsNullOrEmpty(text))
		{
			text = "this directive";
		}
		UIDialog.Confirm("Reroll Directive", $"Reroll {text} for {singleRerollCost} gats?", (Action)delegate
		{
			if (RerollLogic.TryRerollSingle(index, out var error))
			{
				ExitRerollSelection(state);
				DirectiveWindow window = state.Window;
				if (window != null)
				{
					window.SetupDirectives(true);
				}
				RefreshAll(state);
				PlayRerollSound();
			}
			else
			{
				UIDialog.Alert("Reroll Failed", error ?? "Unknown error.", (Action)null, "OK");
			}
		}, (Action)null, $"Pay {singleRerollCost}", "Cancel");
		return true;
	}

	private static WindowState FindStateForButton(DirectiveButton button)
	{
		DirectiveWindow componentInParent = ((Component)button).GetComponentInParent<DirectiveWindow>();
		if ((Object)(object)componentInParent != (Object)null && States.TryGetValue(((Object)componentInParent).GetInstanceID(), out var value))
		{
			return value;
		}
		using (Dictionary<int, WindowState>.Enumerator enumerator = States.GetEnumerator())
		{
			if (enumerator.MoveNext())
			{
				return enumerator.Current.Value;
			}
		}
		return null;
	}

	private static WindowState BuildUi(DirectiveWindow window, bool preselect, bool reroll)
	{
		//IL_003e: 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_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: Expected O, but got Unknown
		try
		{
			Transform transform = ((Component)window).transform;
			RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			UITheme.Initialize();
			RectTransform val2 = UIFactory.CreateRect("BacklogImprovementsBar", (Transform)(object)val);
			val2.anchorMin = new Vector2(0.12f, 0f);
			val2.anchorMax = new Vector2(0.12f, 0f);
			val2.pivot = new Vector2(0f, 0f);
			val2.sizeDelta = new Vector2(720f, 40f);
			val2.anchoredPosition = new Vector2(0f, 18f);
			((Transform)val2).SetAsLastSibling();
			UIFactory.AddHorizontalLayout(((Component)val2).gameObject, 4f, new RectOffset(0, 0, 0, 0), (TextAnchor)3, false, false, true, true);
			WindowState state = new WindowState
			{
				Window = window,
				Root = ((Component)val2).gameObject
			};
			if (preselect)
			{
				state.EditButton = UIButton.Create((Transform)(object)val2, "Edit Path", (Action)delegate
				{
					OnEditClicked(state);
				}, (UIButtonStyle)1, (string)null, (float?)32f);
				ApplyCompactLabel(state.EditButton);
				FitButtonWidth(state.EditButton, 120f);
				state.ClearButton = UIButton.Create((Transform)(object)val2, "Clear", (Action)delegate
				{
					OnClearClicked(state);
				}, (UIButtonStyle)2, (string)null, (float?)32f);
				ApplyCompactLabel(state.ClearButton);
				FitButtonWidth(state.ClearButton, 80f);
			}
			if (reroll)
			{
				state.PageButton = UIButton.Create((Transform)(object)val2, "Reroll Page", (Action)delegate
				{
					OnRerollPageClicked(state);
				}, (UIButtonStyle)1, (string)null, (float?)32f);
				ApplyCompactLabel(state.PageButton);
				FitButtonWidth(state.PageButton, 120f);
				state.SingleButton = UIButton.Create((Transform)(object)val2, "Reroll One", (Action)delegate
				{
					OnRerollOneClicked(state);
				}, (UIButtonStyle)0, (string)null, (float?)32f);
				ApplyCompactLabel(state.SingleButton);
				FitButtonWidth(state.SingleButton, 120f);
				state.CancelButton = UIButton.Create((Transform)(object)val2, "Cancel", (Action)delegate
				{
					ExitRerollSelection(state);
				}, (UIButtonStyle)2, (string)null, (float?)32f);
				ApplyCompactLabel(state.CancelButton);
				state.CancelButton.SetWidth(90f);
				state.CancelButton.SetActive(false);
			}
			return state;
		}
		catch (Exception arg)
		{
			ManualLogSource log = BacklogImprovementsPlugin.Log;
			if (log != null)
			{
				log.LogError((object)$"Failed to build backlog UI: {arg}");
			}
			return null;
		}
	}

	private static void OnEditClicked(WindowState state)
	{
		if (!((Object)(object)state?.Window == (Object)null) && !IsAnimating(state.Window))
		{
			if (PathLogic.IsEditing)
			{
				ExitPathEdit(state, activateNext: true, refreshWindow: true);
				return;
			}
			ExitRerollSelection(state);
			EnterPathEdit(state);
		}
	}

	private static void OnClearClicked(WindowState state)
	{
		if ((Object)(object)state?.Window == (Object)null || IsAnimating(state.Window))
		{
			return;
		}
		int page = PathStore.CurrentPage;
		if (!PathStore.HasPath(page))
		{
			UIDialog.Alert("Nothing to Clear", "No preselected path on this page.", (Action)null, "OK");
			return;
		}
		UIDialog.Confirm("Clear Path", "Remove the preselected path for this backlog page?", (Action)delegate
		{
			PathStore.ClearPage(page);
			RefreshAll(state);
			PathVisualizer.Refresh(state.Window);
			DirectiveWindow window = state.Window;
			if (window != null)
			{
				window.SetupDirectives(false);
			}
		}, (Action)null, "Clear", "Cancel");
	}

	private static void EnterPathEdit(WindowState state)
	{
		PathLogic.SetEditing(editing: true);
		RefreshAll(state);
		DirectiveWindow window = state.Window;
		if (window != null)
		{
			window.SetupDirectives(false);
		}
		PathVisualizer.Refresh(state.Window);
	}

	private static void ExitPathEdit(WindowState state, bool activateNext, bool refreshWindow)
	{
		if (!PathLogic.IsEditing)
		{
			return;
		}
		PathLogic.SetEditing(editing: false);
		PathStore.Save();
		RefreshAll(state);
		if (refreshWindow)
		{
			DirectiveWindow window = state.Window;
			if (window != null)
			{
				window.SetupDirectives(false);
			}
		}
		PathVisualizer.Refresh(state.Window);
		if (activateNext && !PathLogic.HasBlockingDirective())
		{
			PathLogic.TryActivateNext(state.Window);
		}
	}

	private static void OnRerollPageClicked(WindowState state)
	{
		if ((Object)(object)state?.Window == (Object)null || IsAnimating(state.Window))
		{
			return;
		}
		ExitRerollSelection(state);
		ExitPathEdit(state, activateNext: false, refreshWindow: true);
		int num = RerollLogic.CountEligible();
		if (num <= 0)
		{
			UIDialog.Alert("Nothing to Reroll", "There are no not-started directives on this page.", (Action)null, "OK");
			return;
		}
		int cost = RerollLogic.GetPageRerollCost();
		if (!RerollLogic.CanAfford(cost))
		{
			UIDialog.Alert("Not Enough Gats", $"Need {cost} gats to reroll {num} directive(s).", (Action)null, "OK");
			return;
		}
		UIDialog.Confirm("Reroll Page", $"Reroll {num} not-started directive(s) for {cost} gats?\nActive and completed directives are kept.", (Action)delegate
		{
			if (RerollLogic.TryRerollPage(out var rerolledCount, out var error))
			{
				DirectiveWindow window = state.Window;
				if (window != null)
				{
					window.SetupDirectives(true);
				}
				RefreshAll(state);
				PlayRerollSound();
				ManualLogSource log = BacklogImprovementsPlugin.Log;
				if (log != null)
				{
					log.LogInfo((object)$"Rerolled {rerolledCount} backlog directive(s) for {cost} gats.");
				}
			}
			else
			{
				UIDialog.Alert("Reroll Failed", error ?? "Unknown error.", (Action)null, "OK");
			}
		}, (Action)null, $"Pay {cost}", "Cancel");
	}

	private static void OnRerollOneClicked(WindowState state)
	{
		if ((Object)(object)state?.Window == (Object)null || IsAnimating(state.Window))
		{
			return;
		}
		if (state.RerollSelecting)
		{
			ExitRerollSelection(state);
			return;
		}
		if (RerollLogic.CountEligible() <= 0)
		{
			UIDialog.Alert("Nothing to Reroll", "There are no not-started directives on this page.", (Action)null, "OK");
			return;
		}
		int singleRerollCost = RerollLogic.GetSingleRerollCost();
		if (!RerollLogic.CanAfford(singleRerollCost))
		{
			UIDialog.Alert("Not Enough Gats", $"Need {singleRerollCost} gats to reroll one directive.", (Action)null, "OK");
			return;
		}
		ExitPathEdit(state, activateNext: false, refreshWindow: true);
		EnterRerollSelection(state);
	}

	private static void EnterRerollSelection(WindowState state)
	{
		state.RerollSelecting = true;
		if (state.CancelButton != null)
		{
			state.CancelButton.SetActive(true);
		}
		if (state.SingleButton != null)
		{
			state.SingleButton.SetText("Click a directive…");
			FitButtonWidth(state.SingleButton, 120f);
		}
		if (state.PageButton != null)
		{
			state.PageButton.SetInteractable(false);
		}
		if (state.EditButton != null)
		{
			state.EditButton.SetInteractable(false);
		}
		if (state.ClearButton != null)
		{
			state.ClearButton.SetInteractable(false);
		}
		RebuildRerollOverlays(state);
	}

	private static void ExitRerollSelection(WindowState state)
	{
		if (state != null && state.RerollSelecting)
		{
			state.RerollSelecting = false;
			ClearRerollOverlays(state);
			if (state.CancelButton != null)
			{
				state.CancelButton.SetActive(false);
			}
			if (state.PageButton != null)
			{
				state.PageButton.SetInteractable(true);
			}
			if (state.EditButton != null)
			{
				state.EditButton.SetInteractable(true);
			}
			RefreshAll(state);
		}
	}

	private static void RebuildRerollOverlays(WindowState state)
	{
		ClearRerollOverlays(state);
		if ((Object)(object)state?.Window == (Object)null || !state.RerollSelecting || !(DirectivesField?.GetValue(state.Window) is DirectiveButton[] array))
		{
			return;
		}
		foreach (DirectiveButton val in array)
		{
			if (!((Object)(object)val == (Object)null) && RerollLogic.IsEligible((DirectiveInstance)/*isinst with value type is only supported in some contexts*/))
			{
				Transform transform = ((Component)val).transform;
				GameObject val2 = CreateEligibleOverlay((RectTransform)(object)((transform is RectTransform) ? transform : null));
				if ((Object)(object)val2 != (Object)null)
				{
					state.RerollOverlays.Add(val2);
				}
			}
		}
	}

	private static GameObject CreateEligibleOverlay(RectTransform target)
	{
		//IL_0011: 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)
		if ((Object)(object)target == (Object)null)
		{
			return null;
		}
		Image obj = UIFactory.CreateImage("RerollEligibleOverlay", (Transform)(object)target, UIColors.WithAlpha(UIColors.ButtonPrimary, 0.35f), false);
		UIFactory.ApplyWhiteSprite(obj);
		UIHelpers.SetFillParent(((Graphic)obj).rectTransform, UITheme.S(2f));
		((Transform)((Graphic)obj).rectTransform).SetAsLastSibling();
		return ((Component)obj).gameObject;
	}

	private static void ClearRerollOverlays(WindowState state)
	{
		if (state != null)
		{
			for (int i = 0; i < state.RerollOverlays.Count; i++)
			{
				UIHelpers.DestroySafe((Object)(object)state.RerollOverlays[i]);
			}
			state.RerollOverlays.Clear();
		}
	}

	private static void RefreshAll(WindowState state)
	{
		if (state != null)
		{
			RefreshPathLabels(state);
			RefreshRerollLabels(state);
		}
	}

	private static void RefreshPathLabels(WindowState state)
	{
		if (state.EditButton != null || state.ClearButton != null)
		{
			int count = PathStore.GetPath(PathStore.CurrentPage).Count;
			bool isEditing = PathLogic.IsEditing;
			if (state.EditButton != null)
			{
				state.EditButton.SetText(isEditing ? "Done" : "Edit Path");
				state.EditButton.SetStyle((UIButtonStyle)((!isEditing) ? 1 : 3));
				FitButtonWidth(state.EditButton, 120f);
				state.EditButton.SetInteractable(!state.RerollSelecting);
			}
			if (state.ClearButton != null)
			{
				state.ClearButton.SetInteractable((count > 0 || isEditing) && !state.RerollSelecting);
			}
		}
	}

	private static void RefreshRerollLabels(WindowState state)
	{
		if ((state.PageButton != null || state.SingleButton != null) && !state.RerollSelecting)
		{
			int num = RerollLogic.CountEligible();
			int num2 = num * RerollLogic.CostPerDirective;
			int costPerDirective = RerollLogic.CostPerDirective;
			bool isEditing = PathLogic.IsEditing;
			if (state.PageButton != null)
			{
				state.PageButton.SetText((num > 0) ? $"Reroll Page ({num2})" : "Reroll Page");
				FitButtonWidth(state.PageButton, 120f);
				state.PageButton.SetInteractable(!isEditing && num > 0 && RerollLogic.CanAfford(num2));
			}
			if (state.SingleButton != null)
			{
				state.SingleButton.SetText((num > 0) ? $"Reroll One ({costPerDirective})" : "Reroll One");
				FitButtonWidth(state.SingleButton, 120f);
				state.SingleButton.SetInteractable(!isEditing && num > 0 && RerollLogic.CanAfford(costPerDirective));
			}
		}
	}

	private static void ApplyCompactLabel(UIButton button)
	{
		if (!((Object)(object)((button != null) ? button.Label : null) == (Object)null))
		{
			TextMeshProUGUI label = button.Label;
			((TMP_Text)label).enableAutoSizing = false;
			((TMP_Text)label).fontSize = 13f;
			((TMP_Text)label).fontSizeMin = 13f;
			((TMP_Text)label).fontSizeMax = 13f;
			((TMP_Text)label).enableWordWrapping = false;
			((TMP_Text)label).overflowMode = (TextOverflowModes)0;
			UIHelpers.SetFillParent(((TMP_Text)label).rectTransform, 8f);
		}
	}

	private static void FitButtonWidth(UIButton button, float minWidth)
	{
		if (button == null)
		{
			return;
		}
		ApplyCompactLabel(button);
		float num = minWidth;
		if ((Object)(object)button.Label != (Object)null)
		{
			((TMP_Text)button.Label).ForceMeshUpdate(false, false);
			float num2 = ((TMP_Text)button.Label).preferredWidth + 24f;
			if (num2 > num)
			{
				num = num2;
			}
		}
		button.SetWidth(num);
	}

	private static bool IsAnimating(DirectiveWindow window)
	{
		if ((Object)(object)window == (Object)null || IsAnimatingField == null)
		{
			return false;
		}
		try
		{
			return (bool)IsAnimatingField.GetValue(window);
		}
		catch
		{
			return false;
		}
	}

	private static void DestroyState(WindowState state)
	{
		if (state != null)
		{
			ClearRerollOverlays(state);
			UIHelpers.DestroySafe((Object)(object)state.Root);
			state.Root = null;
			state.EditButton = null;
			state.ClearButton = null;
			state.PageButton = null;
			state.SingleButton = null;
			state.CancelButton = null;
			state.RerollSelecting = false;
		}
	}

	private static void PlayRerollSound()
	{
		try
		{
			if ((Object)(object)PlayerLook.Instance != (Object)null)
			{
				AkUnitySoundEngine.PostEvent("UI_Directive_Page_Complete2", ((Component)PlayerLook.Instance).gameObject);
			}
		}
		catch
		{
		}
	}

	public static void PlayPathSound()
	{
		try
		{
			if ((Object)(object)PlayerLook.Instance != (Object)null)
			{
				AkUnitySoundEngine.PostEvent("UI_Directive_Connect", ((Component)PlayerLook.Instance).gameObject);
			}
		}
		catch
		{
		}
	}
}
public static class FreePages
{
	public static void ApplyFreeNextPage(DirectiveWindow window)
	{
		if ((Object)(object)window == (Object)null)
		{
			return;
		}
		ConfigEntry<bool> enableFreePages = BacklogImprovementsPlugin.EnableFreePages;
		if (enableFreePages != null && !enableFreePages.Value)
		{
			return;
		}
		try
		{
			object? value = AccessTools.Field(typeof(DirectiveWindow), "nextPageButton").GetValue(window);
			HoverInfoHold val = (HoverInfoHold)((value is HoverInfoHold) ? value : null);
			if (!((Object)(object)val == (Object)null))
			{
				AccessTools.Field(typeof(HoverInfoHold), "cost").SetValue(val, Array.Empty<ResourceCost>());
			}
		}
		catch (Exception ex)
		{
			ManualLogSource log = BacklogImprovementsPlugin.Log;
			if (log != null)
			{
				log.LogWarning((object)("Free next page failed: " + ex.Message));
			}
		}
	}
}
[HarmonyPatch(typeof(DirectiveWindow))]
internal static class DirectiveWindowPatches
{
	[HarmonyPostfix]
	[HarmonyPatch("OnOpen")]
	private static void OnOpenPostfix(DirectiveWindow __instance)
	{
		BacklogUI.OnWindowOpened(__instance);
	}

	[HarmonyPostfix]
	[HarmonyPatch("SetupDirectives")]
	private static void SetupDirectivesPostfix(DirectiveWindow __instance)
	{
		BacklogUI.OnDirectivesRefreshed(__instance);
	}

	[HarmonyPostfix]
	[HarmonyPatch("OnDisable")]
	private static void OnDisablePostfix(DirectiveWindow __instance)
	{
		BacklogUI.OnWindowClosed(__instance);
	}
}
[HarmonyPatch(typeof(DirectiveWindow), "Update")]
internal static class DirectiveWindowUpdatePatch
{
	private static void Postfix(DirectiveWindow __instance)
	{
		//IL_001b: 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_005f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: 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_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_007c: Expected O, but got Unknown
		//IL_0096: 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)
		if (BacklogUI.IsAnySelectionMode)
		{
			try
			{
				if (PlayerInput.Controls != null)
				{
					MenuActions menu = PlayerInput.Controls.Menu;
					if (((MenuActions)(ref menu)).Click.WasPressedThisFrame() && !((Object)(object)EventSystem.current == (Object)null))
					{
						Vector2 position = ((Mouse.current == null) ? Vector2.op_Implicit(Input.mousePosition) : ((InputControl<Vector2>)(object)((Pointer)Mouse.current).position).ReadValue());
						PointerEventData val = new PointerEventData(EventSystem.current)
						{
							position = position
						};
						List<RaycastResult> list = new List<RaycastResult>();
						EventSystem.current.RaycastAll(val, list);
						for (int i = 0; i < list.Count; i++)
						{
							RaycastResult val2 = list[i];
							DirectiveButton componentInParent = ((RaycastResult)(ref val2)).gameObject.GetComponentInParent<DirectiveButton>();
							if ((Object)(object)componentInParent != (Object)null)
							{
								BacklogUI.TryHandleDirectiveClick(componentInParent);
								break;
							}
						}
					}
				}
				return;
			}
			catch
			{
				return;
			}
		}
		ConfigEntry<bool> enablePreselect = BacklogImprovementsPlugin.EnablePreselect;
		if (enablePreselect == null || enablePreselect.Value)
		{
			PathLogic.TryAutoClaim(__instance);
		}
	}
}
[HarmonyPatch(typeof(DirectiveButton))]
internal static class DirectiveButtonPatches
{
	private static readonly FieldInfo DirectiveField = AccessTools.Field(typeof(DirectiveButton), "directive");

	private static readonly MethodInfo SetupAvailableMethod = AccessTools.Method(typeof(DirectiveButton), "SetupAvailable", (Type[])null, (Type[])null);

	[HarmonyPrefix]
	[HarmonyPatch("HasUnlockAction")]
	private static bool HasUnlockActionPrefix(DirectiveButton __instance, out UnlockActionParams data, ref bool __result)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		data = default(UnlockActionParams);
		if (!BacklogUI.IsAnySelectionMode)
		{
			return true;
		}
		__result = false;
		return false;
	}

	[HarmonyPrefix]
	[HarmonyPatch("GetPrimaryBinding")]
	private static bool GetPrimaryBindingPrefix(DirectiveButton __instance, out InputAction binding, out string label, ref bool __result)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		if (!BacklogUI.IsAnySelectionMode)
		{
			binding = null;
			label = null;
			return true;
		}
		MenuActions menu = PlayerInput.Controls.Menu;
		binding = ((MenuActions)(ref menu)).Click;
		label = (BacklogUI.IsRerollSelecting ? "reroll" : "select");
		__result = true;
		return false;
	}

	[HarmonyPostfix]
	[HarmonyPatch("Setup")]
	private static void SetupPostfix(DirectiveButton __instance, DirectiveInstance directive)
	{
		if (!PathLogic.IsEditing || directive == null)
		{
			return;
		}
		try
		{
			if (!directive.IsComplete || !directive.HasClaimedRewards)
			{
				SetupAvailableMethod?.Invoke(__instance, null);
			}
		}
		catch (Exception ex)
		{
			ManualLogSource log = BacklogImprovementsPlugin.Log;
			if (log != null)
			{
				log.LogWarning((object)("SetupAvailable force failed: " + ex.Message));
			}
		}
	}
}
[HarmonyPatch(typeof(DirectiveInstance), "CanBeActivated")]
internal static class CanBeActivatedPatch
{
	private static bool Prefix(DirectiveInstance __instance, ref bool __result)
	{
		try
		{
			ConfigEntry<bool> enablePreselect = BacklogImprovementsPlugin.EnablePreselect;
			if (enablePreselect != null && !enablePreselect.Value)
			{
				return true;
			}
			if (PathLogic.IsEditing)
			{
				__result = false;
				return false;
			}
			if (!PathLogic.ShouldAllowActivation(__instance, out var forceTrue) && forceTrue)
			{
				__result = true;
				return false;
			}
			return true;
		}
		catch (Exception ex)
		{
			ManualLogSource log = BacklogImprovementsPlugin.Log;
			if (log != null)
			{
				log.LogError((object)("CanBeActivated patch: " + ex.Message));
			}
			return true;
		}
	}
}
[HarmonyPatch(typeof(PlayerData), "GetCurrrentDirectiveTier")]
internal static class DirectiveTierPatch
{
	private static void Postfix(ref int __result)
	{
		if (PathLogic.IsEditing)
		{
			__result = 99;
		}
	}
}
[HarmonyPatch(typeof(DirectiveInstance), "ClaimRewards")]
internal static class DirectiveInstanceClaimPatch
{
	private static void Postfix(DirectiveInstance __instance)
	{
		try
		{
			ConfigEntry<bool> enablePreselect = BacklogImprovementsPlugin.EnablePreselect;
			if ((enablePreselect == null || enablePreselect.Value) && !PathLogic.IsEditing)
			{
				DirectiveWindow window = Object.FindObjectOfType<DirectiveWindow>();
				PathLogic.OnDirectiveClaimed(__instance, window);
			}
		}
		catch (Exception ex)
		{
			ManualLogSource log = BacklogImprovementsPlugin.Log;
			if (log != null)
			{
				log.LogError((object)("DirectiveInstance.ClaimRewards postfix: " + ex.Message));
			}
		}
	}
}
public static class PathLogic
{
	private static readonly FieldInfo IsActiveField = AccessTools.Field(typeof(DirectiveInstance), "isActive");

	private static readonly FieldInfo HasClaimedField = AccessTools.Field(typeof(DirectiveInstance), "hasClaimedRewards");

	private static readonly FieldInfo PropertiesField = AccessTools.Field(typeof(DirectiveInstance), "properties");

	private static bool _autoClaimBusy;

	private static bool _activateBusy;

	public static bool IsEditing { get; private set; }

	public static void SetEditing(bool editing)
	{
		IsEditing = editing;
	}

	public static int IndexOf(DirectiveInstance directive)
	{
		PlayerData instance = PlayerData.Instance;
		if (instance?.defaultDirectives == null || directive == null)
		{
			return -1;
		}
		return Array.IndexOf(instance.defaultDirectives, directive);
	}

	public static bool IsOnCurrentPath(int index)
	{
		return PathStore.Contains(PathStore.CurrentPage, index);
	}

	public static bool IsOnCurrentPath(DirectiveInstance directive)
	{
		int num = IndexOf(directive);
		if (num >= 0)
		{
			return IsOnCurrentPath(num);
		}
		return false;
	}

	public static int GetNextPathIndex()
	{
		PlayerData instance = PlayerData.Instance;
		if (instance?.defaultDirectives == null)
		{
			return -1;
		}
		List<int> path = PathStore.GetPath(PathStore.CurrentPage);
		for (int i = 0; i < path.Count; i++)
		{
			int num = path[i];
			if (num >= 0 && num < instance.defaultDirectives.Length)
			{
				DirectiveInstance val = instance.defaultDirectives[num];
				if (val != null && (!val.IsComplete || !val.HasClaimedRewards))
				{
					return num;
				}
			}
		}
		return -1;
	}

	public static bool TryActivateNext(DirectiveWindow window = null)
	{
		if (_activateBusy || IsEditing)
		{
			return false;
		}
		ConfigEntry<bool> enablePreselect = BacklogImprovementsPlugin.EnablePreselect;
		if (enablePreselect != null && !enablePreselect.Value)
		{
			return false;
		}
		PlayerData instance = PlayerData.Instance;
		if (instance?.defaultDirectives == null)
		{
			return false;
		}
		if (HasBlockingDirective(instance))
		{
			return false;
		}
		int nextPathIndex = GetNextPathIndex();
		if (nextPathIndex < 0)
		{
			return false;
		}
		DirectiveInstance val = instance.defaultDirectives[nextPathIndex];
		if (val == null || !val.CanBeActivated())
		{
			return false;
		}
		_activateBusy = true;
		try
		{
			val.Activate();
			if (val.IsDefault())
			{
				instance.currentDefaultDirective = nextPathIndex;
			}
			if (window != null)
			{
				window.SetupDirectives(true);
			}
			ManualLogSource log = BacklogImprovementsPlugin.Log;
			if (log != null)
			{
				log.LogInfo((object)$"Activated path node {nextPathIndex} (tier {val.Tier}).");
			}
			return true;
		}
		catch (Exception ex)
		{
			ManualLogSource log2 = BacklogImprovementsPlugin.Log;
			if (log2 != null)
			{
				log2.LogError((object)$"Failed to activate path node {nextPathIndex}: {ex.Message}");
			}
			return false;
		}
		finally
		{
			_activateBusy = false;
		}
	}

	public static bool HasBlockingDirective(PlayerData data = null)
	{
		data = data ?? PlayerData.Instance;
		if (data?.defaultDirectives == null)
		{
			return false;
		}
		for (int i = 0; i < data.defaultDirectives.Length; i++)
		{
			DirectiveInstance val = data.defaultDirectives[i];
			if (val != null)
			{
				if (val.IsActive)
				{
					return true;
				}
				if (val.IsComplete && !val.HasClaimedRewards)
				{
					return true;
				}
			}
		}
		return false;
	}

	public static bool TryAutoClaim(DirectiveWindow window = null)
	{
		if (IsEditing || _autoClaimBusy)
		{
			return false;
		}
		ConfigEntry<bool> enablePreselect = BacklogImprovementsPlugin.EnablePreselect;
		if (enablePreselect != null && !enablePreselect.Value)
		{
			return false;
		}
		PlayerData instance = PlayerData.Instance;
		if (instance?.defaultDirectives == null)
		{
			return false;
		}
		int currentDefaultDirective = instance.currentDefaultDirective;
		if (currentDefaultDirective < 0 || currentDefaultDirective >= instance.defaultDirectives.Length)
		{
			return false;
		}
		if (!IsOnCurrentPath(currentDefaultDirective))
		{
			return false;
		}
		DirectiveInstance val = instance.defaultDirectives[currentDefaultDirective];
		if (val == null || !val.CanClaimRewards())
		{
			return false;
		}
		_autoClaimBusy = true;
		try
		{
			val.ClaimRewards();
			if (window != null)
			{
				window.SetupDirectives(true);
			}
			ManualLogSource log = BacklogImprovementsPlugin.Log;
			if (log != null)
			{
				log.LogInfo((object)$"Auto-claimed path node {currentDefaultDirective}.");
			}
			return true;
		}
		catch (Exception ex)
		{
			ManualLogSource log2 = BacklogImprovementsPlugin.Log;
			if (log2 != null)
			{
				log2.LogError((object)("Auto-claim failed: " + ex.Message));
			}
			return false;
		}
		finally
		{
			_autoClaimBusy = false;
		}
	}

	public static void OnDirectiveClaimed(DirectiveInstance claimed, DirectiveWindow window = null)
	{
		if (!IsEditing && claimed != null)
		{
			ConfigEntry<bool> enablePreselect = BacklogImprovementsPlugin.EnablePreselect;
			if ((enablePreselect == null || enablePreselect.Value) && IsOnCurrentPath(claimed))
			{
				TryActivateNext(window);
			}
		}
	}

	public static bool ShouldAllowActivation(DirectiveInstance directive, out bool forceTrue)
	{
		forceTrue = false;
		if (directive == null)
		{
			return true;
		}
		if (IsEditing)
		{
			forceTrue = false;
			return false;
		}
		int num = IndexOf(directive);
		if (num < 0 || !IsOnCurrentPath(num))
		{
			return true;
		}
		int nextPathIndex = GetNextPathIndex();
		if (num != nextPathIndex)
		{
			return true;
		}
		if (directive.IsActive || directive.IsComplete)
		{
			return true;
		}
		PlayerData instance = PlayerData.Instance;
		if (instance == null)
		{
			return true;
		}
		for (int i = 0; i < instance.defaultDirectives.Length; i++)
		{
			DirectiveInstance val = instance.defaultDirectives[i];
			if (val != null && val != directive && (val.IsActive || (val.IsComplete && !val.HasClaimedRewards)))
			{
				return true;
			}
		}
		forceTrue = true;
		return false;
	}

	public static bool TryForceCompleteCurrent()
	{
		PlayerData instance = PlayerData.Instance;
		if (instance?.defaultDirectives == null)
		{
			return false;
		}
		int num = instance.currentDefaultDirective;
		if (num < 0 || num >= instance.defaultDirectives.Length)
		{
			num = -1;
			for (int i = 0; i < instance.defaultDirectives.Length; i++)
			{
				if (instance.defaultDirectives[i] != null && instance.defaultDirectives[i].IsActive)
				{
					num = i;
					break;
				}
			}
		}
		if (num < 0)
		{
			ManualLogSource log = BacklogImprovementsPlugin.Log;
			if (log != null)
			{
				log.LogWarning((object)"Force-complete: no active directive.");
			}
			return false;
		}
		DirectiveInstance val = instance.defaultDirectives[num];
		if (val == null)
		{
			return false;
		}
		try
		{
			if (val.IsActive)
			{
				val.CompleteAllProperties();
			}
			else
			{
				ForceCompleteProperties(val);
			}
			if (!val.IsComplete)
			{
				ForceCompleteProperties(val);
			}
			ManualLogSource log2 = BacklogImprovementsPlugin.Log;
			if (log2 != null)
			{
				log2.LogInfo((object)$"Force-completed directive index {num}.");
			}
			DirectiveWindow val2 = Object.FindObjectOfType<DirectiveWindow>();
			if (val2 != null)
			{
				val2.SetupDirectives(true);
			}
			if (IsOnCurrentPath(num) && val.CanClaimRewards())
			{
				val.ClaimRewards();
				if (val2 != null)
				{
					val2.SetupDirectives(true);
				}
			}
			return true;
		}
		catch (Exception ex)
		{
			ManualLogSource log3 = BacklogImprovementsPlugin.Log;
			if (log3 != null)
			{
				log3.LogError((object)("Force-complete failed: " + ex.Message));
			}
			return false;
		}
	}

	private static void ForceCompleteProperties(DirectiveInstance directive)
	{
		if (directive == null || PropertiesField == null || !(PropertiesField.GetValue(directive) is IList list))
		{
			return;
		}
		for (int i = 0; i < list.Count; i++)
		{
			object? obj = list[i];
			DirectivePropertyInstance val = (DirectivePropertyInstance)((obj is DirectivePropertyInstance) ? obj : null);
			if (val != null)
			{
				val.Current = val.Target;
			}
		}
		if (directive.IsActive)
		{
			directive.Deactivate();
		}
	}
}
public static class PathStore
{
	private static readonly Dictionary<int, List<int>> PagePaths = new Dictionary<int, List<int>>();

	private static string FilePath => Path.Combine(Paths.ConfigPath, "sparroh.backlogimprovements.txt");

	private static string LegacyFilePath => Path.Combine(Paths.ConfigPath, "sparroh.preselectbacklog.txt");

	public static IReadOnlyDictionary<int, List<int>> All => PagePaths;

	public static int CurrentPage
	{
		get
		{
			try
			{
				return PlayerData.Instance?.currentDirectivePage ?? 0;
			}
			catch
			{
				return 0;
			}
		}
	}

	public static void Load()
	{
		PagePaths.Clear();
		try
		{
			string text = FilePath;
			if (!File.Exists(text) && File.Exists(LegacyFilePath))
			{
				text = LegacyFilePath;
				ManualLogSource log = BacklogImprovementsPlugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"Migrating preselected paths from legacy PreselectBacklog config.");
				}
			}
			if (!File.Exists(text))
			{
				return;
			}
			string[] array = File.ReadAllLines(text);
			foreach (string text2 in array)
			{
				if (string.IsNullOrWhiteSpace(text2))
				{
					continue;
				}
				string[] array2 = text2.Split(new char[1] { ',' });
				if (array2.Length == 2 && int.TryParse(array2[0].Trim(), out var result) && int.TryParse(array2[1].Trim(), out var result2))
				{
					if (!PagePaths.TryGetValue(result, out var value))
					{
						value = new List<int>();
						PagePaths[result] = value;
					}
					if (!value.Contains(result2))
					{
						value.Add(result2);
					}
				}
			}
			foreach (KeyValuePair<int, List<int>> item in PagePaths.ToList())
			{
				PagePaths[item.Key] = SortByTier(item.Value);
			}
			if (text == LegacyFilePath && PagePaths.Count > 0)
			{
				Save();
			}
		}
		catch (Exception ex)
		{
			ManualLogSource log2 = BacklogImprovementsPlugin.Log;
			if (log2 != null)
			{
				log2.LogError((object)("Failed to load preselected paths: " + ex.Message));
			}
		}
	}

	public static void Save()
	{
		try
		{
			Directory.CreateDirectory(Paths.ConfigPath);
			List<string> list = new List<string>();
			foreach (KeyValuePair<int, List<int>> item in PagePaths.OrderBy((KeyValuePair<int, List<int>> k) => k.Key))
			{
				foreach (int item2 in item.Value)
				{
					list.Add($"{item.Key},{item2}");
				}
			}
			File.WriteAllLines(FilePath, list);
		}
		catch (Exception ex)
		{
			ManualLogSource log = BacklogImprovementsPlugin.Log;
			if (log != null)
			{
				log.LogError((object)("Failed to save preselected paths: " + ex.Message));
			}
		}
	}

	public static List<int> GetPath(int page)
	{
		if (!PagePaths.TryGetValue(page, out var value))
		{
			value = new List<int>();
			PagePaths[page] = value;
		}
		return value;
	}

	public static List<int> GetPathCopy(int page)
	{
		return new List<int>(GetPath(page));
	}

	public static bool HasPath(int page)
	{
		if (PagePaths.TryGetValue(page, out var value))
		{
			return value.Count > 0;
		}
		return false;
	}

	public static bool Contains(int page, int index)
	{
		if (PagePaths.TryGetValue(page, out var value))
		{
			return value.Contains(index);
		}
		return false;
	}

	public static void SetPath(int page, List<int> indices)
	{
		PagePaths[page] = SortByTier(indices ?? new List<int>());
		Save();
	}

	public static void ClearPage(int page)
	{
		if (PagePaths.ContainsKey(page))
		{
			PagePaths[page] = new List<int>();
			Save();
		}
	}

	public static void ToggleSelection(int page, int index)
	{
		List<int> path = GetPath(page);
		if (path.Contains(index))
		{
			path.Remove(index);
			Save();
			return;
		}
		PlayerData data = PlayerData.Instance;
		if (data?.defaultDirectives == null || index < 0 || index >= data.defaultDirectives.Length)
		{
			return;
		}
		DirectiveInstance val = data.defaultDirectives[index];
		if (val == null)
		{
			return;
		}
		int selectedTier = val.Tier;
		path.RemoveAll(delegate(int i)
		{
			if (i < 0 || i >= data.defaultDirectives.Length)
			{
				return true;
			}
			DirectiveInstance val2 = data.defaultDirectives[i];
			return val2 == null || val2.Tier == selectedTier;
		});
		path.Add(index);
		PagePaths[page] = SortByTier(path);
		Save();
	}

	public static List<int> SortByTier(List<int> indices)
	{
		PlayerData data = PlayerData.Instance;
		if (data?.defaultDirectives == null || indices == null)
		{
			return indices ?? new List<int>();
		}
		return (from i in indices.Where((int i) => i >= 0 && i < data.defaultDirectives.Length && data.defaultDirectives[i] != null).Distinct()
			orderby data.defaultDirectives[i].Tier, i
			select i).ToList();
	}
}
public static class PathVisualizer
{
	private sealed class VisualState
	{
		public DirectiveWindow Window;

		public Transform LineParent;

		public readonly List<GameObject> Lines = new List<GameObject>();

		public readonly List<GameObject> Badges = new List<GameObject>();

		public readonly List<GameObject> Overlays = new List<GameObject>();
	}

	private static readonly FieldInfo DirectivesField = AccessTools.Field(typeof(DirectiveWindow), "directives");

	private static readonly FieldInfo LinesField = AccessTools.Field(typeof(DirectiveWindow), "lines");

	private static readonly FieldInfo OutlineField = AccessTools.Field(typeof(DirectiveButton), "outline");

	private static readonly Dictionary<int, VisualState> States = new Dictionary<int, VisualState>();

	private const float LineWidth = 4f;

	private const float BadgeSize = 22f;

	public static void Refresh(DirectiveWindow window)
	{
		if ((Object)(object)window == (Object)null)
		{
			return;
		}
		ConfigEntry<bool> enablePreselect = BacklogImprovementsPlugin.EnablePreselect;
		if (enablePreselect != null && !enablePreselect.Value)
		{
			Clear(window);
			return;
		}
		int instanceID = ((Object)window).GetInstanceID();
		if (!States.TryGetValue(instanceID, out var value))
		{
			value = new VisualState
			{
				Window = window
			};
			States[instanceID] = value;
		}
		else
		{
			value.Window = window;
		}
		EnsureLineParent(value);
		ClearVisuals(value);
		if (!(DirectivesField?.GetValue(window) is DirectiveButton[] array))
		{
			return;
		}
		List<int> path = PathStore.GetPath(PathStore.CurrentPage);
		if (path == null || path.Count == 0)
		{
			return;
		}
		List<int> list = PathStore.SortByTier(new List<int>(path));
		for (int i = 0; i < list.Count; i++)
		{
			int num = list[i];
			if (num >= 0 && num < array.Length && !((Object)(object)array[num] == (Object)null))
			{
				CreateBadge(value, array[num], i + 1);
				CreateOverlay(value, array[num]);
				ApplyOutline(array[num], selected: true);
			}
		}
		for (int j = 0; j < list.Count - 1; j++)
		{
			int num2 = list[j];
			int num3 = list[j + 1];
			if (num2 >= 0 && num3 >= 0 && num2 < array.Length && num3 < array.Length && !((Object)(object)array[num2] == (Object)null) && !((Object)(object)array[num3] == (Object)null))
			{
				CreateLine(value, array[num2], array[num3]);
			}
		}
	}

	public static void Clear(DirectiveWindow window)
	{
		if (!((Object)(object)window == (Object)null))
		{
			int instanceID = ((Object)window).GetInstanceID();
			if (States.TryGetValue(instanceID, out var value))
			{
				ClearVisuals(value);
				UIHelpers.DestroySafe((Object)(object)(((Object)(object)value.LineParent != (Object)null && ((Object)value.LineParent).name == "PreselectPathLines") ? ((Component)value.LineParent).gameObject : null));
				States.Remove(instanceID);
			}
		}
	}

	public static void CleanupAll()
	{
		foreach (KeyValuePair<int, VisualState> state in States)
		{
			ClearVisuals(state.Value);
		}
		States.Clear();
	}

	private static void EnsureLineParent(VisualState state)
	{
		if ((Object)(object)state.LineParent != (Object)null)
		{
			return;
		}
		try
		{
			if (LinesField?.GetValue(state.Window) is IList { Count: >0 } list)
			{
				object? obj = list[0];
				Component val = (Component)((obj is Component) ? obj : null);
				if ((Object)(object)val != (Object)null)
				{
					state.LineParent = val.transform.parent;
					return;
				}
			}
		}
		catch
		{
		}
		Transform transform = ((Component)state.Window).transform;
		RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
		RectTransform val3 = UIFactory.CreateRect("PreselectPathLines", (Transform)(object)val2);
		UIHelpers.SetFillParent(val3, 0f);
		((Transform)val3).SetAsFirstSibling();
		Image component = ((Component)val3).gameObject.GetComponent<Image>();
		if ((Object)(object)component != (Object)null)
		{
			Object.Destroy((Object)(object)component);
		}
		Graphic component2 = ((Component)val3).gameObject.GetComponent<Graphic>();
		if ((Object)(object)component2 != (Object)null)
		{
			component2.raycastTarget = false;
		}
		state.LineParent = (Transform)(object)val3;
	}

	private static void CreateLine(VisualState state, DirectiveButton from, DirectiveButton to)
	{
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: 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)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_006d: 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_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0091: 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_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00af: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: 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_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00be: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_011a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_0133: Unknown result type (might be due to invalid IL or missing references)
		//IL_0145: Unknown result type (might be due to invalid IL or missing references)
		//IL_0150: 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_015d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0162: 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_0169: 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)
		if ((Object)(object)state.LineParent == (Object)null)
		{
			return;
		}
		Transform transform = ((Component)from).transform;
		RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null);
		Transform transform2 = ((Component)to).transform;
		RectTransform val2 = (RectTransform)(object)((transform2 is RectTransform) ? transform2 : null);
		Transform lineParent = state.LineParent;
		RectTransform val3 = (RectTransform)(object)((lineParent is RectTransform) ? lineParent : null);
		if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null) && !((Object)(object)val3 == (Object)null))
		{
			Vector2 val4 = Vector2.op_Implicit(((Transform)val3).InverseTransformPoint(((Transform)val).position));
			Vector2 val5 = Vector2.op_Implicit(((Transform)val3).InverseTransformPoint(((Transform)val2).position)) - val4;
			float magnitude = ((Vector2)(ref val5)).magnitude;
			if (!(magnitude < 1f))
			{
				Vector2 val6 = val5 / magnitude;
				Rect rect = val.rect;
				float num = ((Rect)(ref rect)).height * 0.5f;
				Vector2 anchoredPosition = val4 + val6 * num;
				float num2 = Mathf.Max(0f, magnitude - num * 2f);
				Image val7 = UIFactory.CreateImage("PreselectPathLine", state.LineParent, UIColors.WithAlpha(UIColors.Macaroon, 0.95f), false);
				UIFactory.ApplyWhiteSprite(val7);
				RectTransform rectTransform = ((Graphic)val7).rectTransform;
				Vector2 val8 = default(Vector2);
				((Vector2)(ref val8))..ctor(0.5f, 0.5f);
				rectTransform.anchorMax = val8;
				rectTransform.anchorMin = val8;
				rectTransform.pivot = new Vector2(0.5f, 0f);
				rectTransform.sizeDelta = new Vector2(4f, num2);
				rectTransform.anchoredPosition = anchoredPosition;
				((Transform)rectTransform).localRotation = Quaternion.FromToRotation(Vector2.op_Implicit(Vector2.up), Vector2.op_Implicit(val6));
				((Transform)rectTransform).localScale = Vector3.one;
				((Transform)rectTransform).SetAsFirstSibling();
				state.Lines.Add(((Component)val7).gameObject);
			}
		}
	}

	private static void CreateBadge(VisualState state, DirectiveButton button, int order)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: 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_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_0089: 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)
		Transform transform = ((Component)button).transform;
		RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null);
		if (!((Object)(object)val == (Object)null))
		{
			Image val2 = UIFactory.CreateImage("PreselectBadge", (Transform)(object)val, UIColors.Macaroon, false);
			UIFactory.ApplyWhiteSprite(val2);
			RectTransform rectTransform = ((Graphic)val2).rectTransform;
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(1f, 1f);
			rectTransform.anchorMax = val3;
			rectTransform.anchorMin = val3;
			rectTransform.pivot = new Vector2(1f, 1f);
			rectTransform.sizeDelta = new Vector2(22f, 22f);
			rectTransform.anchoredPosition = new Vector2(-2f, -2f);
			((Transform)rectTransform).SetAsLastSibling();
			TextMeshProUGUI obj = UIFactory.CreateTmp("Num", (Transform)(object)rectTransform, order.ToString(), 14f, (Color?)UIColors.PanelBg, (TextAlignmentOptions)514, false);
			UIHelpers.SetFillParent(((TMP_Text)obj).rectTransform, 1f);
			((Graphic)obj).raycastTarget = false;
			((TMP_Text)obj).fontStyle = (FontStyles)1;
			state.Badges.Add(((Component)val2).gameObject);
		}
	}

	private static void CreateOverlay(VisualState state, DirectiveButton button)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		Transform transform = ((Component)button).transform;
		RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null);
		if (!((Object)(object)val == (Object)null))
		{
			Image val2 = UIFactory.CreateImage("PreselectOverlay", (Transform)(object)val, UIColors.WithAlpha(UIColors.Macaroon, PathLogic.IsEditing ? 0.3f : 0.18f), false);
			UIFactory.ApplyWhiteSprite(val2);
			UIHelpers.SetFillParent(((Graphic)val2).rectTransform, 2f);
			((Transform)((Graphic)val2).rectTransform).SetAsLastSibling();
			state.Overlays.Add(((Component)val2).gameObject);
		}
	}

	private static void ApplyOutline(DirectiveButton button, bool selected)
	{
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			object? obj = OutlineField?.GetValue(button);
			PolygonOutline val = (PolygonOutline)((obj is PolygonOutline) ? obj : null);
			if (!((Object)(object)val == (Object)null) && selected)
			{
				((Component)val).gameObject.SetActive(true);
				((Graphic)val).color = UIColors.Macaroon;
				((OutlineGraphic)val).SetValue(PathLogic.IsEditing ? 10f : 7f);
			}
		}
		catch
		{
		}
	}

	private static void ClearVisuals(VisualState state)
	{
		if (state != null)
		{
			for (int i = 0; i < state.Lines.Count; i++)
			{
				UIHelpers.DestroySafe((Object)(object)state.Lines[i]);
			}
			state.Lines.Clear();
			for (int j = 0; j < state.Badges.Count; j++)
			{
				UIHelpers.DestroySafe((Object)(object)state.Badges[j]);
			}
			state.Badges.Clear();
			for (int k = 0; k < state.Overlays.Count; k++)
			{
				UIHelpers.DestroySafe((Object)(object)state.Overlays[k]);
			}
			state.Overlays.Clear();
		}
	}
}
[BepInPlugin("sparroh.backlogimprovements", "BacklogImprovements", "2.1.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[MycoMod(/*Could not decode attribute arguments.*/)]
public class BacklogImprovementsPlugin : BaseUnityPlugin
{
	public const string PluginGUID = "sparroh.backlogimprovements";

	public const string PluginName = "BacklogImprovements";

	public const string PluginVersion = "2.1.0";

	public const string LegacyPreselectGuid = "sparroh.preselectbacklog";

	internal static ManualLogSource Log;

	public static BacklogImprovementsPlugin Instance;

	public static ConfigEntry<bool> EnablePreselect;

	public static ConfigEntry<bool> EnableReroll;

	public static ConfigEntry<bool> EnableFreePages;

	public static ConfigEntry<int> CostPerDirective;

	private const bool sparrohmode = false;

	private Harmony _harmony;

	private InputAction _forceCompleteAction;

	private void Awake()
	{
		//IL_009d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a7: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		Instance = this;
		EnablePreselect = ((BaseUnityPlugin)this).Config.Bind<bool>("Features", "EnablePreselect", true, "Enable path preselect, auto-claim, and auto-activate.");
		EnableReroll = ((BaseUnityPlugin)this).Config.Bind<bool>("Features", "EnableReroll", true, "Enable reroll page / reroll one controls.");
		EnableFreePages = ((BaseUnityPlugin)this).Config.Bind<bool>("Features", "EnableFreePages", true, "Remove the resource cost for generating the next backlog page.");
		CostPerDirective = ((BaseUnityPlugin)this).Config.Bind<int>("Reroll", "CostPerDirective", 50, "Gats cost to reroll a single backlog directive. Page reroll costs this amount times the number of eligible directives.");
		PathStore.Load();
		try
		{
			_harmony = new Harmony("sparroh.backlogimprovements");
			_harmony.PatchAll();
			Log.LogInfo((object)"BacklogImprovements v2.1.0 loaded.");
		}
		catch (Exception arg)
		{
			Log.LogError((object)string.Format("Failed to initialize {0}: {1}", "BacklogImprovements", arg));
		}
		SetupForceCompleteAction();
	}

	private void SetupForceCompleteAction()
	{
		try
		{
			InputAction forceCompleteAction = _forceCompleteAction;
			if (forceCompleteAction != null)
			{
				forceCompleteAction.Dispose();
			}
			_forceCompleteAction = null;
		}
		catch (Exception ex)
		{
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogWarning((object)("Could not bind force-complete key: " + ex.Message));
			}
		}
	}

	private void OnDestroy()
	{
		try
		{
			InputAction forceCompleteAction = _forceCompleteAction;
			if (forceCompleteAction != null)
			{
				forceCompleteAction.Disable();
			}
			InputAction forceCompleteAction2 = _forceCompleteAction;
			if (forceCompleteAction2 != null)
			{
				forceCompleteAction2.Dispose();
			}
			_forceCompleteAction = null;
			BacklogUI.CleanupAll();
			PathStore.Save();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
		catch (Exception ex)
		{
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogError((object)("Cleanup failed: " + ex.Message));
			}
		}
	}
}
public static class RerollLogic
{
	public static int CostPerDirective => Mathf.Max(0, BacklogImprovementsPlugin.CostPerDirective?.Value ?? 50);

	public static bool IsEligible(DirectiveInstance directive)
	{
		if (directive == null)
		{
			return false;
		}
		if (!directive.IsActive)
		{
			return !directive.IsComplete;
		}
		return false;
	}

	public static int CountEligible()
	{
		PlayerData instance = PlayerData.Instance;
		if (instance?.defaultDirectives == null)
		{
			return 0;
		}
		int num = 0;
		for (int i = 0; i < instance.defaultDirectives.Length; i++)
		{
			if (IsEligible(instance.defaultDirectives[i]))
			{
				num++;
			}
		}
		return num;
	}

	public static int GetPageRerollCost()
	{
		return CountEligible() * CostPerDirective;
	}

	public static int GetSingleRerollCost()
	{
		return CostPerDirective;
	}

	public static bool CanAfford(int cost)
	{
		if (cost <= 0)
		{
			return true;
		}
		PlayerData instance = PlayerData.Instance;
		Global instance2 = Global.Instance;
		PlayerResource val = ((instance2 != null) ? instance2.ScripResource : null);
		if (instance == null || (Object)(object)val == (Object)null)
		{
			return false;
		}
		return instance.CanAffordCost(val, cost);
	}

	public static bool TryCharge(int cost)
	{
		if (cost <= 0)
		{
			return true;
		}
		PlayerData instance = PlayerData.Instance;
		Global instance2 = Global.Instance;
		PlayerResource val = ((instance2 != null) ? instance2.ScripResource : null);
		if (instance == null || (Object)(object)val == (Object)null)
		{
			return false;
		}
		return instance.TryRemoveResource(val, cost);
	}

	public static bool TryRerollSingle(int index, out string error)
	{
		error = null;
		PlayerData instance = PlayerData.Instance;
		if (instance?.defaultDirectives == null)
		{
			error = "Player data not ready.";
			return false;
		}
		if (index < 0 || index >= instance.defaultDirectives.Length)
		{
			error = "Invalid directive.";
			return false;
		}
		if (!IsEligible(instance.defaultDirectives[index]))
		{
			error = "That directive cannot be rerolled (active or already complete).";
			return false;
		}
		int singleRerollCost = GetSingleRerollCost();
		if (!CanAfford(singleRerollCost))
		{
			error = $"Not enough gats (need {singleRerollCost}).";
			return false;
		}
		if (!TryCharge(singleRerollCost))
		{
			error = $"Not enough gats (need {singleRerollCost}).";
			return false;
		}
		ReplaceAt(instance, index);
		return true;
	}

	public static bool TryRerollPage(out int rerolledCount, out string error)
	{
		rerolledCount = 0;
		error = null;
		PlayerData instance = PlayerData.Instance;
		if (instance?.defaultDirectives == null)
		{
			error = "Player data not ready.";
			return false;
		}
		List<int> list = new List<int>();
		for (int i = 0; i < instance.defaultDirectives.Length; i++)
		{
			if (IsEligible(instance.defaultDirectives[i]))
			{
				list.Add(i);
			}
		}
		if (list.Count == 0)
		{
			error = "No eligible directives to reroll.";
			return false;
		}
		int num = list.Count * CostPerDirective;
		if (!CanAfford(num))
		{
			error = $"Not enough gats (need {num}).";
			return false;
		}
		if (!TryCharge(num))
		{
			error = $"Not enough gats (need {num}).";
			return false;
		}
		for (int j = 0; j < list.Count; j++)
		{
			ReplaceAt(instance, list[j]);
		}
		rerolledCount = list.Count;
		return true;
	}

	private static void ReplaceAt(PlayerData data, int index)
	{
		int tierFromDirectiveIndex = data.GetTierFromDirectiveIndex(index);
		try
		{
			DirectiveInstance obj = data.defaultDirectives[index];
			if (obj != null)
			{
				obj.Destroy();
			}
		}
		catch (Exception ex)
		{
			ManualLogSource log = BacklogImprovementsPlugin.Log;
			if (log != null)
			{
				log.LogWarning((object)$"Destroy directive {index}: {ex.Message}");
			}
		}
		data.defaultDirectives[index] = data.CreateDirective(tierFromDirectiveIndex);
	}

	public static int IndexOf(DirectiveInstance directive)
	{
		PlayerData instance = PlayerData.Instance;
		if (instance?.defaultDirectives == null || directive == null)
		{
			return -1;
		}
		return Array.IndexOf(instance.defaultDirectives, directive);
	}
}
namespace BacklogImprovements
{
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "BacklogImprovements";

		public const string PLUGIN_NAME = "BacklogImprovements";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}