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 RerollOverlays = new List(); } 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 States = new Dictionary(); 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 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 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 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(); if ((Object)(object)componentInParent != (Object)null && States.TryGetValue(((Object)componentInParent).GetInstanceID(), out var value)) { return value; } using (Dictionary.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 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()); } } 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)(object)((Pointer)Mouse.current).position).ReadValue()); PointerEventData val = new PointerEventData(EventSystem.current) { position = position }; List list = new List(); EventSystem.current.RaycastAll(val, list); for (int i = 0; i < list.Count; i++) { RaycastResult val2 = list[i]; DirectiveButton componentInParent = ((RaycastResult)(ref val2)).gameObject.GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null) { BacklogUI.TryHandleDirectiveClick(componentInParent); break; } } } } return; } catch { return; } } ConfigEntry 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 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 enablePreselect = BacklogImprovementsPlugin.EnablePreselect; if ((enablePreselect == null || enablePreselect.Value) && !PathLogic.IsEditing) { DirectiveWindow window = Object.FindObjectOfType(); 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 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 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 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 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(); 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> PagePaths = new Dictionary>(); 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> 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(); PagePaths[result] = value; } if (!value.Contains(result2)) { value.Add(result2); } } } foreach (KeyValuePair> 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 list = new List(); foreach (KeyValuePair> item in PagePaths.OrderBy((KeyValuePair> 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 GetPath(int page) { if (!PagePaths.TryGetValue(page, out var value)) { value = new List(); PagePaths[page] = value; } return value; } public static List GetPathCopy(int page) { return new List(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 indices) { PagePaths[page] = SortByTier(indices ?? new List()); Save(); } public static void ClearPage(int page) { if (PagePaths.ContainsKey(page)) { PagePaths[page] = new List(); Save(); } } public static void ToggleSelection(int page, int index) { List 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 SortByTier(List indices) { PlayerData data = PlayerData.Instance; if (data?.defaultDirectives == null || indices == null) { return indices ?? new List(); } 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 Lines = new List(); public readonly List Badges = new List(); public readonly List Overlays = new List(); } 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 States = new Dictionary(); private const float LineWidth = 4f; private const float BadgeSize = 22f; public static void Refresh(DirectiveWindow window) { if ((Object)(object)window == (Object)null) { return; } ConfigEntry 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 path = PathStore.GetPath(PathStore.CurrentPage); if (path == null || path.Count == 0) { return; } List list = PathStore.SortByTier(new List(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 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(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } Graphic component2 = ((Component)val3).gameObject.GetComponent(); 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 EnablePreselect; public static ConfigEntry EnableReroll; public static ConfigEntry EnableFreePages; public static ConfigEntry 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("Features", "EnablePreselect", true, "Enable path preselect, auto-claim, and auto-activate."); EnableReroll = ((BaseUnityPlugin)this).Config.Bind("Features", "EnableReroll", true, "Enable reroll page / reroll one controls."); EnableFreePages = ((BaseUnityPlugin)this).Config.Bind("Features", "EnableFreePages", true, "Remove the resource cost for generating the next backlog page."); CostPerDirective = ((BaseUnityPlugin)this).Config.Bind("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 list = new List(); 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) { } } }