Decompiled source of GridSolverCoru v1.1.0

BepInEx/plugins/GridSolverCoru.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Pigeon.UI;
using Sparroh.UI;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("GridSolverCoru")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+43d5f77c3d56dd34f465fe381150b7afa2528d50")]
[assembly: AssemblyProduct("GridSolverCoru")]
[assembly: AssemblyTitle("GridSolverCoru")]
[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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
[BepInPlugin("sparroh.gridsolverplus", "GridSolverCoru", "1.1.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[MycoMod(/*Could not decode attribute arguments.*/)]
public class GridSolverPlugin : BaseUnityPlugin
{
	public const string PluginGUID = "sparroh.gridsolverplus";

	public const string PluginName = "GridSolverCoru";

	public const string PluginVersion = "1.1.0";

	internal static ManualLogSource Logger;

	public static GridSolverPlugin Instance;

	private UpgradeSolver upgradeSolver;

	private void Awake()
	{
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Expected O, but got Unknown
		Logger = ((BaseUnityPlugin)this).Logger;
		Instance = this;
		upgradeSolver = new UpgradeSolver();
		UpgradeSolver.Instance = upgradeSolver;
		Harmony val = new Harmony("sparroh.gridsolverplus");
		val.PatchAll();
		Logger.LogInfo((object)"GridSolverCoru v1.1.0 loaded.");
	}

	private void OnDestroy()
	{
		try
		{
			upgradeSolver?.SolverUI?.Close();
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Failed to close UpgradeSolver UI: " + ex.Message));
		}
	}

	private void Update()
	{
		upgradeSolver?.Update();
	}

	private void OnGUI()
	{
		upgradeSolver?.OnGUI();
	}
}
public class UpgradeSolver
{
	public Coroutine? SolverCoroutine;

	internal static UpgradeSolver Instance;

	internal SolverUI SolverUI = new SolverUI(null, null);

	public void Update()
	{
		SolverUI.Update();
		if (GearActionBar.IsGearMenuOpen())
		{
			SolverUI.EnsureBarIfNeeded();
		}
	}

	internal void OnUpgradeWindowOpen(IUpgradeWindow window, IUpgradable gear, MonoBehaviour mono)
	{
		SolverUI.ResetSelectionState();
		SolverUI.UpgradeWindow = window;
		SolverUI.CurrentGear = gear;
		SolverUI.OnWindowOpened();
		SolverUI.PatchUpgradeClick();
		mono.StartCoroutine(DelayPatch());
	}

	private IEnumerator DelayPatch()
	{
		yield return null;
		SolverUI.PatchUpgradeClick();
	}

	internal void OnUpgradeWindowClosed()
	{
		SolverUI.Close();
	}

	public void OnGUI()
	{
		SolverUI.OnGUI();
	}
}
public static class UpgradeSolverPatches
{
	[HarmonyPatch(typeof(GearDetailsWindow), "OnOpen")]
	public static class GearDetailsWindowPatch
	{
		[HarmonyPostfix]
		public static void Postfix(GearDetailsWindow __instance)
		{
			object? obj = _upgradablePrefabProperty?.GetValue(__instance);
			IUpgradable gear = (IUpgradable)((obj is IUpgradable) ? obj : null);
			UpgradeSolver.Instance.OnUpgradeWindowOpen((IUpgradeWindow)(object)__instance, gear, (MonoBehaviour)(object)GridSolverPlugin.Instance);
		}
	}

	[HarmonyPatch(typeof(GearDetailsWindow), "OnCloseCallback")]
	public static class GearDetailsWindowClosePatch
	{
		[HarmonyPostfix]
		public static void Postfix()
		{
			UpgradeSolver.Instance.OnUpgradeWindowClosed();
		}
	}

	[HarmonyPatch(typeof(OuroGearWindow), "OnOpen")]
	public static class OuroGearWindowPatch
	{
		[HarmonyPostfix]
		public static void Postfix(OuroGearWindow __instance)
		{
			object? obj = _selectedGearField?.GetValue(__instance);
			IUpgradable gear = (IUpgradable)((obj is IUpgradable) ? obj : null);
			UpgradeSolver.Instance.OnUpgradeWindowOpen((IUpgradeWindow)(object)__instance, gear, (MonoBehaviour)(object)GridSolverPlugin.Instance);
		}
	}

	[HarmonyPatch(typeof(OuroGearWindow), "OnCloseCallback")]
	public static class OuroGearWindowClosePatch
	{
		[HarmonyPostfix]
		public static void Postfix()
		{
			UpgradeSolver.Instance.OnUpgradeWindowClosed();
		}
	}

	private static readonly PropertyInfo _upgradablePrefabProperty = typeof(GearDetailsWindow).GetProperty("UpgradablePrefab", BindingFlags.Instance | BindingFlags.Public);

	private static readonly FieldInfo _selectedGearField = typeof(OuroGearWindow).GetField("selectedGear", BindingFlags.Instance | BindingFlags.NonPublic);
}
public class SolverUI
{
	private readonly Color _defaultSecondaryColor = new Color(0.9434f, 0.9434f, 0.9434f, 1f);

	private readonly Color _grayedOutColor = new Color(0.9434f, 0.9434f, 0.9434f, 0.1484f);

	internal IUpgradeWindow? UpgradeWindow;

	internal IUpgradable? CurrentGear;

	private readonly HashSet<DefaultButton> _patchedButtons = new HashSet<DefaultButton>();

	private Dictionary<int, GearUpgradeUI> _selectedUpgrades = new Dictionary<int, GearUpgradeUI>();

	private GearUpgradeUI? _hoveredUpgrade;

	private bool _showSolveButton;

	private bool _solveButtonEnabled;

	private readonly HashSet<UpgradeInstance> _toggledThisSession = new HashSet<UpgradeInstance>();

	private readonly InputActionMap _solverControls;

	private readonly InputAction _addForSolve;

	private bool _barRegistered;

	private bool _lastSolveEnabled;

	private bool _lastCancelEnabled;

	private bool _lastClearEnabled;

	private bool _lastClearGridEnabled;

	private bool _buttonStateInitialized;

	private static readonly PropertyInfo? _detailsGearProperty = typeof(GearDetailsWindow).GetProperty("UpgradablePrefab", BindingFlags.Instance | BindingFlags.Public);

	private static readonly FieldInfo? _ouroGearField = typeof(OuroGearWindow).GetField("selectedGear", BindingFlags.Instance | BindingFlags.NonPublic);

	private Solver? _activeSolver;

	private static FieldInfo? GetPrivateField(string name, Type? type)
	{
		try
		{
			while (type != null)
			{
				FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					return field;
				}
				type = type.BaseType;
			}
		}
		catch
		{
		}
		return null;
	}

	public SolverUI(IUpgradeWindow? upgradeWindow, IUpgradable? currentGear)
	{
		//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)
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: Expected O, but got Unknown
		//IL_00af: Unknown result type (might be due to invalid IL or missing references)
		UpgradeWindow = upgradeWindow;
		CurrentGear = currentGear;
		_solverControls = new InputActionMap("SolverControls");
		_addForSolve = InputActionSetupExtensions.AddAction(_solverControls, "AddForSolve", (InputActionType)0, (string)null, (string)null, (string)null, (string)null, (string)null);
		InputActionSetupExtensions.AddBinding(_addForSolve, "<Keyboard>/n", (string)null, (string)null, (string)null);
	}

	internal void OnWindowOpened()
	{
		_showSolveButton = true;
		_solveButtonEnabled = false;
		EnsureButtons();
		RefreshButtonStates();
	}

	internal void ResetSelectionState()
	{
		ResetSelectionColors();
		_selectedUpgrades.Clear();
		_toggledThisSession.Clear();
		_hoveredUpgrade = null;
		_solveButtonEnabled = false;
		RefreshButtonStates();
	}

	private IUpgradable? GetGearFromWindow()
	{
		if (UpgradeWindow == null)
		{
			return null;
		}
		try
		{
			IUpgradeWindow? upgradeWindow = UpgradeWindow;
			GearDetailsWindow val = (GearDetailsWindow)(object)((upgradeWindow is GearDetailsWindow) ? upgradeWindow : null);
			if (val != null)
			{
				object? obj = _detailsGearProperty?.GetValue(val);
				return (IUpgradable?)((obj is IUpgradable) ? obj : null);
			}
			IUpgradeWindow? upgradeWindow2 = UpgradeWindow;
			OuroGearWindow val2 = (OuroGearWindow)(object)((upgradeWindow2 is OuroGearWindow) ? upgradeWindow2 : null);
			if (val2 != null)
			{
				object? obj2 = _ouroGearField?.GetValue(val2);
				return (IUpgradable?)((obj2 is IUpgradable) ? obj2 : null);
			}
		}
		catch
		{
		}
		return CurrentGear;
	}

	internal bool SyncGearWithWindow()
	{
		if (UpgradeWindow == null)
		{
			return false;
		}
		IUpgradable gearFromWindow = GetGearFromWindow();
		if (gearFromWindow == null || gearFromWindow == CurrentGear)
		{
			return false;
		}
		GridSolverPlugin.Logger.LogInfo((object)"Gear changed in open window; clearing solver selection.");
		CurrentGear = gearFromWindow;
		ResetSelectionState();
		PatchUpgradeClick();
		return true;
	}

	private bool IsUpgradeUIInCurrentWindow(GearUpgradeUI ui)
	{
		if (UpgradeWindow == null || (Object)(object)ui == (Object)null)
		{
			return false;
		}
		IUpgradeWindow? upgradeWindow = UpgradeWindow;
		Component val = (Component)(object)((upgradeWindow is Component) ? upgradeWindow : null);
		if (val != null && (Object)(object)val != (Object)null)
		{
			return ((Component)ui).transform.IsChildOf(val.transform);
		}
		object? obj = GetPrivateField("upgradeListParent", ((object)UpgradeWindow).GetType())?.GetValue(UpgradeWindow);
		Transform val2 = (Transform)((obj is Transform) ? obj : null);
		if ((Object)(object)val2 != (Object)null)
		{
			return ((Component)ui).transform.IsChildOf(val2);
		}
		return true;
	}

	internal void EnsureBarIfNeeded()
	{
		if (GearActionBar.IsGearMenuOpen())
		{
			_showSolveButton = true;
			EnsureButtons();
			RefreshButtonStates();
		}
	}

	private void SelectUpgrade()
	{
		//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_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0116: Unknown result type (might be due to invalid IL or missing references)
		//IL_0117: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01be: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0259: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_hoveredUpgrade == (Object)null || !((HoverInfoUpgrade)_hoveredUpgrade).Upgrade.IsUnlocked || !IsUpgradeUIInCurrentWindow(_hoveredUpgrade) || !Solver.UpgradeBelongsToGear(CurrentGear, ((HoverInfoUpgrade)_hoveredUpgrade).Upgrade))
		{
			return;
		}
		UpgradeInstance upgrade = ((HoverInfoUpgrade)_hoveredUpgrade).Upgrade;
		RarityData val = Global.Instance.Rarities[upgrade.Upgrade.Rarity];
		FieldInfo privateField = GetPrivateField("button", typeof(GearUpgradeUI));
		FieldInfo privateField2 = GetPrivateField("hoverColor", typeof(DefaultButton));
		if (privateField == null)
		{
			return;
		}
		if (_selectedUpgrades.TryGetValue(upgrade.InstanceID, out var value))
		{
			object? value2 = privateField.GetValue(value);
			DefaultButton val2 = (DefaultButton)((value2 is DefaultButton) ? value2 : null);
			if ((Object)(object)val2 != (Object)null)
			{
				val2.SetDefaultColor(val.backgroundColor);
			}
			_selectedUpgrades.Remove(upgrade.InstanceID);
			_solveButtonEnabled = _selectedUpgrades.Count > 0;
			RefreshButtonStates();
			return;
		}
		if (!upgrade.CanStack)
		{
			GearUpgradeUI val3 = ((IEnumerable<GearUpgradeUI>)_selectedUpgrades.Values).FirstOrDefault((Func<GearUpgradeUI, bool>)((GearUpgradeUI u) => (Object)(object)u != (Object)null && ((HoverInfoUpgrade)u).Upgrade != null && ((object)((HoverInfoUpgrade)u).Upgrade.SourceUpgradeID/*cast due to .constrained prefix*/).Equals((object?)upgrade.SourceUpgradeID)));
			if ((Object)(object)val3 != (Object)null)
			{
				Color backgroundColor = Global.Instance.Rarities[((HoverInfoUpgrade)val3).Upgrade.Upgrade.Rarity].backgroundColor;
				object? value3 = privateField.GetValue(val3);
				DefaultButton val4 = (DefaultButton)((value3 is DefaultButton) ? value3 : null);
				if ((Object)(object)val4 != (Object)null)
				{
					val4.SetDefaultColor(backgroundColor);
				}
				_selectedUpgrades.Remove(((HoverInfoUpgrade)val3).Upgrade.InstanceID);
			}
		}
		object? value4 = privateField.GetValue(_hoveredUpgrade);
		DefaultButton val5 = (DefaultButton)((value4 is DefaultButton) ? value4 : null);
		if ((Object)(object)val5 != (Object)null && privateField2 != null)
		{
			Color? val6 = privateField2.GetValue(val5) as Color?;
			if (val6.HasValue)
			{
				val5.SetDefaultColor(val6.Value);
			}
		}
		_selectedUpgrades[upgrade.InstanceID] = _hoveredUpgrade;
		_solveButtonEnabled = true;
		RefreshButtonStates();
	}

	internal void RebuildSelectedUpgrades()
	{
		//IL_0147: Unknown result type (might be due to invalid IL or missing references)
		if (UpgradeWindow == null)
		{
			return;
		}
		FieldInfo privateField = GetPrivateField("upgradeListParent", ((object)UpgradeWindow).GetType());
		FieldInfo privateField2 = GetPrivateField("button", typeof(GearUpgradeUI));
		FieldInfo privateField3 = GetPrivateField("hoverColor", typeof(DefaultButton));
		if (privateField == null || privateField2 == null)
		{
			return;
		}
		object? value = privateField.GetValue(UpgradeWindow);
		Transform val = (Transform)((value is Transform) ? value : null);
		if ((Object)(object)val == (Object)null)
		{
			return;
		}
		List<GearUpgradeUI> source = (from x in ((Component)val).GetComponentsInChildren<GearUpgradeUI>()
			where _selectedUpgrades.ContainsKey(((HoverInfoUpgrade)x).Upgrade.InstanceID)
			select x).ToList();
		_selectedUpgrades = source.ToDictionary((GearUpgradeUI ui) => ((HoverInfoUpgrade)ui).Upgrade.InstanceID);
		foreach (KeyValuePair<int, GearUpgradeUI> selectedUpgrade in _selectedUpgrades)
		{
			object? value2 = privateField2.GetValue(selectedUpgrade.Value);
			DefaultButton val2 = (DefaultButton)((value2 is DefaultButton) ? value2 : null);
			Color? val3 = privateField3?.GetValue(val2) as Color?;
			if ((Object)(object)val2 != (Object)null && val3.HasValue)
			{
				val2.SetDefaultColor(val3.Value);
			}
		}
		_solveButtonEnabled = _selectedUpgrades.Count > 0;
		RefreshButtonStates();
	}

	internal void Close()
	{
		_solverControls.Disable();
		_showSolveButton = false;
		_patchedButtons.Clear();
		_selectedUpgrades.Clear();
		_toggledThisSession.Clear();
		_hoveredUpgrade = null;
		_solveButtonEnabled = false;
		Coroutine solverCoroutine = UpgradeSolver.Instance.SolverCoroutine;
		if (solverCoroutine != null)
		{
			((MonoBehaviour)GridSolverPlugin.Instance).StopCoroutine(solverCoroutine);
			UpgradeSolver.Instance.SolverCoroutine = null;
			_activeSolver?.AbortAndRestore();
		}
		_activeSolver = null;
		RefreshButtonStates();
	}

	internal void Update()
	{
		SyncGearWithWindow();
		if (Keyboard.current != null)
		{
			if (!((ButtonControl)Keyboard.current.nKey).isPressed)
			{
				if (_toggledThisSession.Count > 0)
				{
					_toggledThisSession.Clear();
				}
			}
			else
			{
				GearUpgradeUI val = null;
				if (UIRaycaster.RaycastForComponent<GearUpgradeUI>(ref val))
				{
					UpgradeInstance upgrade = ((HoverInfoUpgrade)val).Upgrade;
					if (upgrade != null && !_toggledThisSession.Contains(upgrade))
					{
						_hoveredUpgrade = val;
						SelectUpgrade();
						_toggledThisSession.Add(upgrade);
					}
				}
			}
		}
		GearActionBar.Tick();
		EnsureBarIfNeeded();
	}

	private void EnsureButtons()
	{
		if (!_barRegistered)
		{
			GearActionBar.Register("clear_grid", "Unequip All", 10, (Action)OnClearGridClicked, (UIButtonStyle)2);
			GearActionBar.Register("solve", "Solve", 20, (Action)OnSolveClicked, (UIButtonStyle)1);
			GearActionBar.Register("cancel_solve", "Cancel", 30, (Action)OnCancelClicked, (UIButtonStyle)2);
			GearActionBar.Register("clear_selection", "Deselect", 40, (Action)OnClearClicked, (UIButtonStyle)0);
			GearActionBar.SetSlotVisible("clear_grid", true);
			GearActionBar.SetSlotVisible("solve", true);
			GearActionBar.SetSlotVisible("cancel_solve", true);
			GearActionBar.SetSlotVisible("clear_selection", true);
			_barRegistered = true;
		}
	}

	private void RefreshButtonStates()
	{
		if (_barRegistered)
		{
			bool flag = UpgradeSolver.Instance != null && UpgradeSolver.Instance.SolverCoroutine != null;
			bool flag2 = _solveButtonEnabled && !flag;
			bool flag3 = flag;
			bool flag4 = _selectedUpgrades.Count > 0 && !flag;
			bool flag5 = _showSolveButton && !flag;
			if (!_buttonStateInitialized || flag2 != _lastSolveEnabled || flag3 != _lastCancelEnabled || flag4 != _lastClearEnabled || flag5 != _lastClearGridEnabled)
			{
				_buttonStateInitialized = true;
				_lastSolveEnabled = flag2;
				_lastCancelEnabled = flag3;
				_lastClearEnabled = flag4;
				_lastClearGridEnabled = flag5;
				GearActionBar.SetInteractable("solve", flag2);
				GearActionBar.SetInteractable("cancel_solve", flag3);
				GearActionBar.SetInteractable("clear_selection", flag4);
				GearActionBar.SetInteractable("clear_grid", flag5);
			}
		}
	}

	private void OnSolveClicked()
	{
		if (UpgradeWindow == null || !_solveButtonEnabled || UpgradeSolver.Instance.SolverCoroutine != null || SyncGearWithWindow())
		{
			return;
		}
		if (CurrentGear == null)
		{
			UIDialog.Alert("Solve Error", "Could not determine which gear this window is editing.", (Action)null, "OK");
			return;
		}
		List<int> list = (from kv in _selectedUpgrades
			where (Object)(object)kv.Value == (Object)null || !Solver.UpgradeBelongsToGear(CurrentGear, ((HoverInfoUpgrade)kv.Value).Upgrade)
			select kv.Key).ToList();
		foreach (int item in list)
		{
			GridSolverPlugin.Logger.LogWarning((object)$"Dropping stale/foreign selected upgrade (InstanceID {item}) before solve.");
			_selectedUpgrades.Remove(item);
		}
		if (_selectedUpgrades.Count == 0)
		{
			_solveButtonEnabled = false;
			RefreshButtonStates();
			return;
		}
		foreach (GearUpgradeUI value in _selectedUpgrades.Values)
		{
			GridSolverPlugin.Logger.LogInfo((object)("\t • " + FormatUpgrade(value)));
		}
		List<UpgradeInstance> upgrades = (from u in _selectedUpgrades
			select ((HoverInfoUpgrade)u.Value).Upgrade into u
			orderby Solver.GetPlacementPriority(u.Upgrade) descending, u.GetPattern(false).GetCellCount() descending
			select u).ToList();
		Solver solver = new Solver(UpgradeWindow, CurrentGear, upgrades);
		_activeSolver = solver;
		solver.TrySolve(delegate(bool success)
		{
			GridSolverPlugin.Logger.LogInfo((object)(success ? "Found a solution" : "No solution"));
			UpgradeSolver.Instance.SolverCoroutine = null;
			_activeSolver = null;
			if (success)
			{
				ResetSelectionColors();
				_selectedUpgrades.Clear();
				_solveButtonEnabled = false;
			}
			else
			{
				UIDialog.Alert("Solve Failed", solver.FailureMessage ?? "No valid arrangement found for the selected upgrades.", (Action)null, "OK");
				RebuildSelectedUpgrades();
				((MonoBehaviour)GridSolverPlugin.Instance).StartCoroutine(RebuildSelectionNextFrame());
			}
			RefreshButtonStates();
		});
		RefreshButtonStates();
	}

	private IEnumerator RebuildSelectionNextFrame()
	{
		yield return null;
		RebuildSelectedUpgrades();
	}

	private void OnCancelClicked()
	{
		if (UpgradeSolver.Instance.SolverCoroutine != null)
		{
			((MonoBehaviour)GridSolverPlugin.Instance).StopCoroutine(UpgradeSolver.Instance.SolverCoroutine);
			UpgradeSolver.Instance.SolverCoroutine = null;
			_activeSolver?.AbortAndRestore();
			_activeSolver = null;
			RebuildSelectedUpgrades();
			((MonoBehaviour)GridSolverPlugin.Instance).StartCoroutine(RebuildSelectionNextFrame());
			RefreshButtonStates();
		}
	}

	private void OnClearClicked()
	{
		ResetSelectionColors();
		_selectedUpgrades.Clear();
		_solveButtonEnabled = false;
		RefreshButtonStates();
	}

	private void OnClearGridClicked()
	{
		if (UpgradeWindow == null || UpgradeSolver.Instance.SolverCoroutine != null)
		{
			return;
		}
		SyncGearWithWindow();
		if (CurrentGear == null)
		{
			return;
		}
		object? obj = GetPrivateField("equipSlots", ((object)UpgradeWindow).GetType())?.GetValue(UpgradeWindow);
		ModuleEquipSlots val = (ModuleEquipSlots)((obj is ModuleEquipSlots) ? obj : null);
		if ((Object)(object)val == (Object)null)
		{
			GridSolverPlugin.Logger.LogWarning((object)"Unequip All: could not access the window's equip slots.");
			return;
		}
		List<UpgradeInstance> list = (from inst in Solver.EnumerateInstances(CurrentGear)
			where inst.IsEquipped(CurrentGear)
			orderby Solver.GetPlacementPriority(inst)
			select inst).ToList();
		int num = 0;
		foreach (UpgradeInstance item in list)
		{
			if (val.Unequip(CurrentGear, item))
			{
				num++;
			}
		}
		GridSolverPlugin.Logger.LogInfo((object)$"Unequip All: removed {num} mod(s) from the current gear.");
	}

	private void ResetSelectionColors()
	{
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		FieldInfo privateField = GetPrivateField("button", typeof(GearUpgradeUI));
		foreach (GearUpgradeUI value in _selectedUpgrades.Values)
		{
			try
			{
				if (!((Object)(object)value == (Object)null))
				{
					RarityData val = Global.Instance.Rarities[((HoverInfoUpgrade)value).Upgrade.Upgrade.Rarity];
					object? obj = privateField?.GetValue(value);
					DefaultButton val2 = (DefaultButton)((obj is DefaultButton) ? obj : null);
					if ((Object)(object)val2 != (Object)null)
					{
						val2.SetDefaultColor(val.backgroundColor);
					}
				}
			}
			catch
			{
			}
		}
	}

	private static string FormatUpgrade(GearUpgradeUI upgrade)
	{
		UpgradeInstance upgrade2 = ((HoverInfoUpgrade)upgrade).Upgrade;
		return $"[{upgrade2.Upgrade.RarityName}] {upgrade2.Upgrade.Name} ({upgrade2.InstanceID})";
	}

	internal void PatchUpgradeClick()
	{
		//IL_0131: Unknown result type (might be due to invalid IL or missing references)
		//IL_013b: Expected O, but got Unknown
		//IL_014b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0155: Expected O, but got Unknown
		if (UpgradeWindow == null)
		{
			return;
		}
		_solverControls.Enable();
		FieldInfo privateField = GetPrivateField("upgradeListParent", ((object)UpgradeWindow).GetType());
		FieldInfo privateField2 = GetPrivateField("button", typeof(GearUpgradeUI));
		if (privateField == null || privateField2 == null)
		{
			return;
		}
		object? value = privateField.GetValue(UpgradeWindow);
		Transform val = (Transform)((value is Transform) ? value : null);
		if ((Object)(object)val == (Object)null)
		{
			return;
		}
		List<GearUpgradeUI> list = (from x in ((Component)val).GetComponentsInChildren<GearUpgradeUI>()
			where ((Component)x).gameObject.activeSelf
			select x).ToList();
		foreach (GearUpgradeUI upgradeUI in list)
		{
			object? value2 = privateField2.GetValue(upgradeUI);
			DefaultButton val2 = (DefaultButton)((value2 is DefaultButton) ? value2 : null);
			if (val2 != null && _patchedButtons.Add(val2))
			{
				((Button)val2).OnHoverExit.AddListener((UnityAction)delegate
				{
					_hoveredUpgrade = null;
				});
				((Button)val2).OnHoverEnter.AddListener((UnityAction)delegate
				{
					_hoveredUpgrade = upgradeUI;
				});
			}
		}
	}

	public void OnGUI()
	{
	}
}
public static class HexMapUtils
{
	private static readonly FieldInfo _hexMapWidthField = typeof(HexMap).GetField("width", BindingFlags.Instance | BindingFlags.NonPublic);

	private static readonly FieldInfo _hexMapHeightField = typeof(HexMap).GetField("height", BindingFlags.Instance | BindingFlags.NonPublic);

	private static readonly FieldInfo _hexMapNodesField = typeof(HexMap).GetField("nodes", BindingFlags.Instance | BindingFlags.NonPublic);

	public static HexMap CloneHexMap(HexMap original)
	{
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Expected O, but got Unknown
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		if (original == null)
		{
			return null;
		}
		int num = (int)_hexMapWidthField.GetValue(original);
		int num2 = (int)_hexMapHeightField.GetValue(original);
		HexMap val = new HexMap(num, num2);
		for (int i = 0; i < num2; i++)
		{
			for (int j = 0; j < num; j++)
			{
				val[j, i] = original[j, i];
			}
		}
		return val;
	}

	public static HexMap CanonicalizeHexMap(HexMap map)
	{
		//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: Expected O, but got Unknown
		//IL_011a: Unknown result type (might be due to invalid IL or missing references)
		//IL_011f: Unknown result type (might be due to invalid IL or missing references)
		if (map == null)
		{
			return null;
		}
		int num = (int)_hexMapWidthField.GetValue(map);
		int num2 = (int)_hexMapHeightField.GetValue(map);
		int num3 = int.MaxValue;
		int num4 = int.MaxValue;
		bool flag = false;
		for (int i = 0; i < num2; i++)
		{
			for (int j = 0; j < num; j++)
			{
				if (map[j, i].enabled)
				{
					if (j < num3)
					{
						num3 = j;
					}
					if (i < num4)
					{
						num4 = i;
					}
					flag = true;
				}
			}
		}
		if (!flag)
		{
			return CloneHexMap(map);
		}
		int num5 = num - num3;
		int num6 = num2 - num4;
		HexMap val = new HexMap(num5, num6);
		for (int k = 0; k < num2; k++)
		{
			for (int l = 0; l < num; l++)
			{
				if (map[l, k].enabled)
				{
					int num7 = l - num3;
					int num8 = k - num4;
					val[num7, num8] = map[l, k];
				}
			}
		}
		return val;
	}

	public static bool HexMapsEqual(HexMap a, HexMap b)
	{
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		//IL_00df: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
		if (a == null && b == null)
		{
			return true;
		}
		if (a == null || b == null)
		{
			return false;
		}
		int num = (int)_hexMapWidthField.GetValue(a);
		int num2 = (int)_hexMapHeightField.GetValue(a);
		int num3 = (int)_hexMapWidthField.GetValue(b);
		int num4 = (int)_hexMapHeightField.GetValue(b);
		if (num != num3 || num2 != num4)
		{
			return false;
		}
		for (int i = 0; i < num2; i++)
		{
			for (int j = 0; j < num; j++)
			{
				Node val = a[j, i];
				Node val2 = b[j, i];
				if (val.enabled != val2.enabled || (val.enabled && val.connections != val2.connections))
				{
					return false;
				}
			}
		}
		return true;
	}
}
public class Offset(int offsetX, int offsetY)
{
	public readonly int OffsetX = offsetX;

	public readonly int OffsetY = offsetY;
}
public class Solver
{
	private struct Placement
	{
		public ulong[] Mask;

		public int OffsetX;

		public int OffsetY;

		public byte Rotation;
	}

	private static readonly HashSet<string> GridExpandingUpgradeNames = new HashSet<string> { "Boundary Incursion", "Edge Fault", "Multiversal Thievery" };

	private static readonly HashSet<string> MajorGridExpanderNames = new HashSet<string> { "Multiversal Thievery" };

	private static readonly HashSet<string> CellCountModifyingUpgradeNames = new HashSet<string> { "Handheld Pocket Universe" };

	private static readonly FieldInfo _modifiedMapWidthField = GetPrivateField("width", typeof(HexMap));

	private static readonly FieldInfo _modifiedMapHeightField = GetPrivateField("height", typeof(HexMap));

	private static readonly FieldInfo _hexMapWidthField = GetPrivateField("width", typeof(HexMap));

	private static readonly FieldInfo _hexMapHeightField = GetPrivateField("height", typeof(HexMap));

	private readonly IUpgradeWindow _upgradeWindow;

	private readonly List<UpgradeInstance> _upgrades;

	private readonly IUpgradable _gear;

	private readonly ModuleEquipSlots _equipSlots;

	private readonly HexMap _hexMap;

	private List<UpgradeInstance> _specials;

	private List<UpgradeInstance> _normals;

	private List<UpgradeInstance> _expanders;

	private UpgradeInstance _hpu;

	private readonly Dictionary<Tuple<UpgradeInstance, int>, Offset> _offsetCache = new Dictionary<Tuple<UpgradeInstance, int>, Offset>();

	private readonly Dictionary<UpgradeInstance, List<int>> _uniqueRotationsCache = new Dictionary<UpgradeInstance, List<int>>();

	private readonly List<Tuple<UpgradeInstance, sbyte, sbyte, byte>> _previousLayout = new List<Tuple<UpgradeInstance, sbyte, sbyte, byte>>();

	private bool _foundSolution;

	private const long NormalSolveNodeCap = 6000000L;

	private const long NormalSolveTimeCapMs = 4000L;

	private long _normalNodes;

	private Stopwatch _normalTimer;

	public string? FailureMessage { get; private set; }

	private static bool HasName(Upgrade u)
	{
		return ((u != null) ? u.Name : null) != null;
	}

	private static bool HasName(UpgradeInstance i)
	{
		object obj;
		if (i == null)
		{
			obj = null;
		}
		else
		{
			Upgrade upgrade = i.Upgrade;
			obj = ((upgrade != null) ? upgrade.Name : null);
		}
		return obj != null;
	}

	internal static bool IsMajorGridExpander(Upgrade upgrade)
	{
		return HasName(upgrade) && MajorGridExpanderNames.Contains(upgrade.Name);
	}

	internal static bool IsMajorGridExpander(UpgradeInstance inst)
	{
		return HasName(inst) && MajorGridExpanderNames.Contains(inst.Upgrade.Name);
	}

	internal static bool IsGridExpander(Upgrade upgrade)
	{
		return HasName(upgrade) && GridExpandingUpgradeNames.Contains(upgrade.Name);
	}

	internal static bool IsGridExpander(UpgradeInstance inst)
	{
		return HasName(inst) && GridExpandingUpgradeNames.Contains(inst.Upgrade.Name);
	}

	internal static bool IsCellCountModifier(Upgrade upgrade)
	{
		return HasName(upgrade) && CellCountModifyingUpgradeNames.Contains(upgrade.Name);
	}

	internal static bool IsCellCountModifier(UpgradeInstance inst)
	{
		return HasName(inst) && CellCountModifyingUpgradeNames.Contains(inst.Upgrade.Name);
	}

	private static int GetEffectiveCellCount(UpgradeInstance upgrade, bool hasHPU)
	{
		int cellCount = upgrade.GetPattern(false).GetCellCount();
		if (!hasHPU)
		{
			return cellCount;
		}
		if (IsCellCountModifier(upgrade))
		{
			return cellCount;
		}
		return upgrade.Upgrade.RarityName switch
		{
			"Standard" => 1, 
			"Rare" => 2, 
			"Epic" => 3, 
			"Exotic" => 4, 
			_ => cellCount, 
		};
	}

	internal static int GetPlacementPriority(UpgradeInstance inst)
	{
		if (IsMajorGridExpander(inst))
		{
			return 3;
		}
		if (IsGridExpander(inst))
		{
			return 2;
		}
		if (IsCellCountModifier(inst))
		{
			return 1;
		}
		return 0;
	}

	internal static int GetPlacementPriority(Upgrade upgrade)
	{
		if (IsMajorGridExpander(upgrade))
		{
			return 3;
		}
		if (IsGridExpander(upgrade))
		{
			return 2;
		}
		if (IsCellCountModifier(upgrade))
		{
			return 1;
		}
		return 0;
	}

	private static FieldInfo? GetPrivateField(string name, Type type)
	{
		try
		{
			return type.GetField(name, BindingFlags.Instance | BindingFlags.NonPublic);
		}
		catch
		{
			return null;
		}
	}

	private static int MaxRotationsFor(UpgradeInstance upgrade)
	{
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			return (!PlayerData.Instance.CanRotateUpgrade(upgrade.Upgrade.Rarity)) ? 1 : 6;
		}
		catch
		{
			return (!PlayerData.Instance.canRotateUpgrades) ? 1 : 6;
		}
	}

	internal static bool UpgradeBelongsToGear(IUpgradable gear, UpgradeInstance instance)
	{
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Invalid comparison between Unknown and I4
		if (gear == null || instance == null)
		{
			return false;
		}
		try
		{
			object gear2 = instance.Gear;
			if (gear2 != null)
			{
				if (gear2 == gear)
				{
					return true;
				}
				if ((int)gear.GearType == 5 && gear2 == Global.Instance)
				{
					return true;
				}
			}
		}
		catch
		{
		}
		try
		{
			foreach (UpgradeInstance item in EnumerateInstances(gear))
			{
				if (item == instance || item.InstanceID == instance.InstanceID)
				{
					return true;
				}
			}
			return false;
		}
		catch
		{
			return true;
		}
	}

	public Solver(IUpgradeWindow upgradeWindow, IUpgradable gear, List<UpgradeInstance> upgrades)
	{
		_upgradeWindow = upgradeWindow;
		_gear = gear;
		_upgrades = upgrades.Where(delegate(UpgradeInstance u)
		{
			bool flag = UpgradeBelongsToGear(gear, u);
			if (!flag)
			{
				ManualLogSource logger = GridSolverPlugin.Logger;
				object obj2;
				if (u == null)
				{
					obj2 = null;
				}
				else
				{
					Upgrade upgrade = u.Upgrade;
					obj2 = ((upgrade != null) ? upgrade.Name : null);
				}
				logger.LogWarning((object)("Solver rejected '" + (string?)obj2 + "' — it does not belong to the target gear."));
			}
			return flag;
		}).ToList();
		ref ModuleEquipSlots equipSlots = ref _equipSlots;
		object? obj = GetPrivateField("equipSlots", ((object)upgradeWindow).GetType())?.GetValue(upgradeWindow);
		equipSlots = (ModuleEquipSlots)((obj is ModuleEquipSlots) ? obj : null);
		FieldInfo privateField = GetPrivateField("hexMap", typeof(ModuleEquipSlots));
		if ((Object)(object)_equipSlots != (Object)null && privateField != null)
		{
			ref HexMap hexMap = ref _hexMap;
			object? value = privateField.GetValue(_equipSlots);
			hexMap = (HexMap)((value is HexMap) ? value : null);
		}
		Dictionary<string, List<UpgradeInstance>> dictionary = new Dictionary<string, List<UpgradeInstance>>();
		foreach (UpgradeInstance upgrade2 in _upgrades)
		{
			HexMap pattern = upgrade2.GetPattern(false);
			string hexMapKey = GetHexMapKey(HexMapUtils.CanonicalizeHexMap(pattern));
			if (!dictionary.ContainsKey(hexMapKey))
			{
				dictionary[hexMapKey] = new List<UpgradeInstance>();
			}
			dictionary[hexMapKey].Add(upgrade2);
			_uniqueRotationsCache[upgrade2] = GetUniqueRotations(upgrade2);
		}
		_expanders = (from u in _upgrades
			where IsGridExpander(u)
			orderby GetPlacementPriority(u) descending
			select u).ToList();
		List<UpgradeInstance> list = _upgrades.Where((UpgradeInstance u) => IsCellCountModifier(u)).ToList();
		_normals = _upgrades.Where((UpgradeInstance u) => GetPlacementPriority(u) == 0).ToList();
		if (list.Count == 1)
		{
			_hpu = list[0];
			_specials = _expanders;
			return;
		}
		_hpu = null;
		_specials = (from u in _expanders.Concat(list)
			orderby GetPlacementPriority(u) descending
			select u).ToList();
	}

	private List<int> GetUniqueRotations(UpgradeInstance upgrade)
	{
		HexMap pattern = upgrade.GetPattern(false);
		Dictionary<string, int> dictionary = new Dictionary<string, int>();
		List<int> list = new List<int>();
		int num = MaxRotationsFor(upgrade);
		for (int i = 0; i < num; i++)
		{
			HexMap modifiedMap = pattern.GetModifiedMap(i, (HexMap)null);
			HexMap map = HexMapUtils.CanonicalizeHexMap(modifiedMap);
			string hexMapKey = GetHexMapKey(map);
			if (!dictionary.ContainsKey(hexMapKey))
			{
				dictionary[hexMapKey] = i;
				list.Add(i);
			}
		}
		return list;
	}

	private string GetHexMapKey(HexMap map)
	{
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: 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_0071: 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_007d: Expected I4, but got Unknown
		if (map == null)
		{
			return "";
		}
		int num = (int)_hexMapWidthField.GetValue(map);
		int num2 = (int)_hexMapHeightField.GetValue(map);
		StringBuilder stringBuilder = new StringBuilder();
		for (int i = 0; i < num2; i++)
		{
			for (int j = 0; j < num; j++)
			{
				Node val = map[j, i];
				stringBuilder.Append(val.enabled ? '1' : '0');
				stringBuilder.Append((int)val.connections);
				stringBuilder.Append(',');
			}
			stringBuilder.Append(';');
		}
		return stringBuilder.ToString();
	}

	private Offset GetOffsetsCached(UpgradeInstance upgrade, int rotation, UpgradeEquipCell cell)
	{
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: 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)
		Tuple<UpgradeInstance, int> key = new Tuple<UpgradeInstance, int>(upgrade, rotation);
		if (_offsetCache.TryGetValue(key, out var value))
		{
			return new Offset(cell.X + value.offsetX, cell.Y + value.offsetY);
		}
		HexMap pattern = upgrade.GetPattern(false);
		HexMap modifiedMap = pattern.GetModifiedMap(rotation, (HexMap)null);
		int num = ((_modifiedMapWidthField != null) ? ((int)_modifiedMapWidthField.GetValue(modifiedMap)) : 8);
		int num2 = num / 2;
		int num3 = 0;
		int num4 = 0;
		for (int i = 0; i < ((_modifiedMapHeightField != null) ? ((int)_modifiedMapHeightField.GetValue(modifiedMap)) : 8); i++)
		{
			Node val = modifiedMap[num2, i];
			if (val.enabled)
			{
				num3 += i;
				num4++;
			}
		}
		int num5 = ((num4 > 0) ? (num3 / num4) : 0);
		int num6 = -num5;
		if (num2 % 2 == 1)
		{
			num6--;
		}
		value = new Offset(-num2, num6);
		_offsetCache[key] = value;
		return new Offset(cell.X + value.offsetX, cell.Y + value.offsetY);
	}

	internal static IEnumerable<UpgradeInstance> EnumerateInstances(IUpgradable gear)
	{
		List<UpgradeInfo> merged = new List<UpgradeInfo>(PlayerData.GetAllUpgrades(gear, true));
		if ((int)gear.GearType == 5)
		{
			IUpgradable playerUpgradesPrefab = (IUpgradable)(object)Global.Instance;
			merged.AddRange(PlayerData.GetAllUpgrades(playerUpgradesPrefab, true));
		}
		foreach (UpgradeInfo upgradeInfo in merged)
		{
			if (upgradeInfo?.Instances == null)
			{
				continue;
			}
			foreach (UpgradeInstance item in upgradeInfo.Instances.OfType<UpgradeInstance>().ToList())
			{
				yield return item;
			}
		}
	}

	private void SnapshotEquipped()
	{
		_previousLayout.Clear();
		sbyte item = default(sbyte);
		sbyte item2 = default(sbyte);
		foreach (UpgradeInstance upgrade in _upgrades)
		{
			if (upgrade.IsEquipped(_gear) && upgrade.GetPosition(_gear, ref item, ref item2))
			{
				_previousLayout.Add(new Tuple<UpgradeInstance, sbyte, sbyte, byte>(upgrade, item, item2, upgrade.GetRotation(_gear)));
			}
		}
		_previousLayout.Sort((Tuple<UpgradeInstance, sbyte, sbyte, byte> a, Tuple<UpgradeInstance, sbyte, sbyte, byte> b) => GetPlacementPriority(b.Item1).CompareTo(GetPlacementPriority(a.Item1)));
	}

	private void RestoreEquipped()
	{
		if ((Object)(object)_equipSlots == (Object)null)
		{
			return;
		}
		foreach (Tuple<UpgradeInstance, sbyte, sbyte, byte> item in _previousLayout)
		{
			if (!_equipSlots.EquipModule(_gear, item.Item1, (int)item.Item2, (int)item.Item3, item.Item4, true, false))
			{
				GridSolverPlugin.Logger.LogWarning((object)("Could not restore '" + item.Item1.Upgrade.Name + "' to its previous slot."));
			}
		}
	}

	private void ClearSlots()
	{
		if ((Object)(object)_equipSlots == (Object)null)
		{
			return;
		}
		foreach (UpgradeInstance item in _upgrades.OrderBy((UpgradeInstance i) => GetPlacementPriority(i)))
		{
			_equipSlots.Unequip(_gear, item);
		}
	}

	private int GridWidth()
	{
		return (_hexMap != null && _hexMapWidthField != null) ? ((int)_hexMapWidthField.GetValue(_hexMap)) : 8;
	}

	private int GridHeight()
	{
		return (_hexMap != null && _hexMapHeightField != null) ? ((int)_hexMapHeightField.GetValue(_hexMap)) : 8;
	}

	private IEnumerator SolveDfs(int index)
	{
		if ((Object)(object)_equipSlots == (Object)null)
		{
			_foundSolution = false;
			yield break;
		}
		if (index >= _specials.Count)
		{
			_foundSolution = SolveNormals();
			yield break;
		}
		UpgradeInstance upgrade = _specials[index];
		List<int> uniqueRotations = _uniqueRotationsCache[upgrade];
		int h = GridHeight();
		int w = GridWidth();
		for (int y = 0; y < h; y++)
		{
			for (int x = 0; x < w; x++)
			{
				foreach (int rotation in uniqueRotations)
				{
					UpgradeEquipCell cell = null;
					try
					{
						cell = _equipSlots.GetCell(x, y);
					}
					catch
					{
					}
					if (cell == null || ((HoverInfoUpgrade)cell).Upgrade != null)
					{
						continue;
					}
					Offset offset = GetOffsetsCached(upgrade, rotation, cell);
					if (_equipSlots.EquipModule(_gear, upgrade, offset.offsetX, offset.offsetY, (byte)rotation, true, false))
					{
						yield return SolveDfs(index + 1);
						if (_foundSolution)
						{
							yield break;
						}
						_equipSlots.Unequip(_gear, upgrade);
					}
				}
			}
		}
	}

	private static int WordCount(int cellCount)
	{
		return (cellCount + 63) / 64;
	}

	private static int PopCount(ulong[] a)
	{
		int num = 0;
		for (int i = 0; i < a.Length; i++)
		{
			ulong num2 = a[i];
			while (num2 != 0)
			{
				num2 &= num2 - 1;
				num++;
			}
		}
		return num;
	}

	private static bool Intersects(ulong[] a, ulong[] b)
	{
		for (int i = 0; i < a.Length; i++)
		{
			if ((a[i] & b[i]) != 0)
			{
				return true;
			}
		}
		return false;
	}

	private List<int> ComputeCells(HexMap modMap, int offsetX, int offsetY, int W, int H)
	{
		int num = ((_modifiedMapWidthField != null) ? ((int)_modifiedMapWidthField.GetValue(modMap)) : 8);
		int num2 = ((_modifiedMapHeightField != null) ? ((int)_modifiedMapHeightField.GetValue(modMap)) : 8);
		int num3 = offsetX + num / 2;
		List<int> list = new List<int>(8);
		for (int i = 0; i < num2; i++)
		{
			for (int j = 0; j < num; j++)
			{
				bool enabled;
				try
				{
					enabled = modMap[j, i].enabled;
				}
				catch
				{
					continue;
				}
				if (enabled)
				{
					int num4 = j + offsetX;
					int num5 = i + offsetY;
					if (j % 2 == 1 && num4 % 2 == 0)
					{
						num5++;
					}
					if (num3 % 2 == 1)
					{
						num5++;
					}
					if (num3 % 2 == 1 && num / 2 % 2 == 0)
					{
						num5--;
					}
					if (num4 < 0 || num5 < 0 || num4 >= W || num5 >= H)
					{
						return null;
					}
					list.Add(num5 * W + num4);
				}
			}
		}
		return list;
	}

	private List<Placement> EnumeratePlacements(UpgradeInstance upgrade, int W, int H, int words, ulong[] baseOccupied)
	{
		List<Placement> list = new List<Placement>();
		HashSet<string> hashSet = new HashSet<string>();
		List<int> uniqueRotations = GetUniqueRotations(upgrade);
		HexMap pattern = upgrade.GetPattern(false);
		for (int i = 0; i < H; i++)
		{
			for (int j = 0; j < W; j++)
			{
				foreach (int item2 in uniqueRotations)
				{
					UpgradeEquipCell val = null;
					try
					{
						val = _equipSlots.GetCell(j, i);
					}
					catch
					{
					}
					if (val == null)
					{
						continue;
					}
					Offset offsetsCached = GetOffsetsCached(upgrade, item2, val);
					HexMap modifiedMap = pattern.GetModifiedMap(item2, (HexMap)null);
					List<int> list2 = ComputeCells(modifiedMap, offsetsCached.offsetX, offsetsCached.offsetY, W, H);
					if (list2 == null)
					{
						continue;
					}
					ulong[] array = new ulong[words];
					foreach (int item3 in list2)
					{
						array[item3 >> 6] |= (ulong)(1L << item3);
					}
					if (!Intersects(array, baseOccupied))
					{
						string item = MaskKey(array);
						if (hashSet.Add(item))
						{
							list.Add(new Placement
							{
								Mask = array,
								OffsetX = offsetsCached.offsetX,
								OffsetY = offsetsCached.offsetY,
								Rotation = (byte)item2
							});
						}
					}
				}
			}
		}
		return list;
	}

	private static string MaskKey(ulong[] mask)
	{
		StringBuilder stringBuilder = new StringBuilder(mask.Length * 17);
		for (int i = 0; i < mask.Length; i++)
		{
			stringBuilder.Append(mask[i].ToString("x16"));
			stringBuilder.Append(',');
		}
		return stringBuilder.ToString();
	}

	private bool SolveNormals()
	{
		if ((Object)(object)_equipSlots == (Object)null)
		{
			return false;
		}
		List<UpgradeInstance> list = new List<UpgradeInstance>();
		if (_hpu != null)
		{
			list.Add(_hpu);
		}
		list.AddRange(_normals);
		if (list.Count == 0)
		{
			return true;
		}
		int num = GridWidth();
		int num2 = GridHeight();
		int num3 = num * num2;
		if (num3 <= 0)
		{
			return false;
		}
		int num4 = WordCount(num3);
		ulong[] array = new ulong[num4];
		for (int i = 0; i < num2; i++)
		{
			for (int j = 0; j < num; j++)
			{
				UpgradeEquipCell val = null;
				try
				{
					val = _equipSlots.GetCell(j, i);
				}
				catch
				{
				}
				if ((Object)(object)val != (Object)null && ((HoverInfoUpgrade)val).Upgrade != null)
				{
					int num5 = i * num + j;
					array[num5 >> 6] |= (ulong)(1L << num5);
				}
			}
		}
		int count = list.Count;
		List<Placement>[] array2 = new List<Placement>[count];
		int[] array3 = new int[count];
		string[] array4 = new string[count];
		int num6 = 0;
		if (_hpu != null)
		{
			array2[0] = EnumeratePlacements(_hpu, num, num2, num4, array);
			if (array2[0].Count == 0)
			{
				return false;
			}
			array3[0] = PopCount(array2[0][0].Mask);
			array4[0] = PieceSignature(array2[0]);
			num6 = 1;
		}
		UpgradeInstance parked = null;
		if (_hpu != null && !ParkHpu(num, num2, out parked))
		{
			return false;
		}
		try
		{
			for (int k = num6; k < count; k++)
			{
				array2[k] = EnumeratePlacements(list[k], num, num2, num4, array);
				if (array2[k].Count == 0)
				{
					return false;
				}
				array3[k] = PopCount(array2[k][0].Mask);
				array4[k] = PieceSignature(array2[k]);
			}
		}
		finally
		{
			if (parked != null)
			{
				_equipSlots.Unequip(_gear, parked);
			}
		}
		ulong[][] neighbors = BuildNeighbors(num, num2, num4);
		int[] array5 = new int[count];
		for (int l = 0; l < count; l++)
		{
			array5[l] = -1;
		}
		List<int> list2 = new List<int>(count);
		for (int m = 0; m < count; m++)
		{
			list2.Add(m);
		}
		_normalNodes = 0L;
		_normalTimer = Stopwatch.StartNew();
		if (!NormalDfs(list2, array, array2, array3, array4, neighbors, num3, num4, array5))
		{
			return false;
		}
		List<UpgradeInstance> list3 = new List<UpgradeInstance>();
		for (int n = 0; n < count; n++)
		{
			Placement placement = array2[n][array5[n]];
			if (!_equipSlots.EquipModule(_gear, list[n], placement.OffsetX, placement.OffsetY, placement.Rotation, true, false))
			{
				ManualLogSource logger = GridSolverPlugin.Logger;
				Upgrade upgrade = list[n].Upgrade;
				logger.LogWarning((object)("Bitmask solution failed to apply '" + ((upgrade != null) ? upgrade.Name : null) + "'; rolling back."));
				foreach (UpgradeInstance item in list3)
				{
					_equipSlots.Unequip(_gear, item);
				}
				return false;
			}
			list3.Add(list[n]);
		}
		return true;
	}

	private bool ParkHpu(int W, int H, out UpgradeInstance parked)
	{
		parked = null;
		List<int> uniqueRotations = GetUniqueRotations(_hpu);
		for (int i = 0; i < H; i++)
		{
			for (int j = 0; j < W; j++)
			{
				UpgradeEquipCell val = null;
				try
				{
					val = _equipSlots.GetCell(j, i);
				}
				catch
				{
				}
				if (val == null || ((HoverInfoUpgrade)val).Upgrade != null)
				{
					continue;
				}
				foreach (int item in uniqueRotations)
				{
					Offset offsetsCached = GetOffsetsCached(_hpu, item, val);
					if (_equipSlots.EquipModule(_gear, _hpu, offsetsCached.offsetX, offsetsCached.offsetY, (byte)item, true, false))
					{
						parked = _hpu;
						return true;
					}
				}
			}
		}
		return false;
	}

	private static string PieceSignature(List<Placement> placements)
	{
		List<string> list = new List<string>(placements.Count);
		foreach (Placement placement in placements)
		{
			list.Add(MaskKey(placement.Mask));
		}
		list.Sort(StringComparer.Ordinal);
		return string.Join("|", list);
	}

	private static ulong[][] BuildNeighbors(int W, int H, int words)
	{
		int num = W * H;
		ulong[][] array = new ulong[num][];
		for (int i = 0; i < H; i++)
		{
			for (int j = 0; j < W; j++)
			{
				ulong[] array2 = new ulong[words];
				(int, int)[] array3 = ((j % 2 != 0) ? new(int, int)[6]
				{
					(0, -1),
					(1, 0),
					(1, 1),
					(0, 1),
					(-1, 1),
					(-1, 0)
				} : new(int, int)[6]
				{
					(0, -1),
					(1, -1),
					(1, 0),
					(0, 1),
					(-1, 0),
					(-1, -1)
				});
				(int, int)[] array4 = array3;
				for (int k = 0; k < array4.Length; k++)
				{
					(int, int) tuple = array4[k];
					int item = tuple.Item1;
					int item2 = tuple.Item2;
					int num2 = j + item;
					int num3 = i + item2;
					if (num2 >= 0 && num2 < W && num3 >= 0 && num3 < H)
					{
						int num4 = num3 * W + num2;
						array2[num4 >> 6] |= (ulong)(1L << num4);
					}
				}
				array[i * W + j] = array2;
			}
		}
		return array;
	}

	private bool NormalDfs(List<int> remaining, ulong[] occupied, List<Placement>[] placements, int[] sizes, string[] signatures, ulong[][] neighbors, int N, int words, int[] chosen)
	{
		if (++_normalNodes > 6000000 || _normalTimer.ElapsedMilliseconds > 4000)
		{
			return false;
		}
		if (remaining.Count == 0)
		{
			return true;
		}
		int num = N - PopCount(occupied);
		int num2 = 0;
		int num3 = int.MaxValue;
		for (int i = 0; i < remaining.Count; i++)
		{
			int num4 = sizes[remaining[i]];
			num2 += num4;
			if (num4 < num3)
			{
				num3 = num4;
			}
		}
		if (num < num2)
		{
			return false;
		}
		if (num3 > 1 && UsableCells(occupied, neighbors, N, words, num3) < num2)
		{
			return false;
		}
		if (num == num2)
		{
			int num5 = FirstEmpty(occupied, N);
			if (num5 < 0)
			{
				return false;
			}
			ulong num6 = (ulong)(1L << num5);
			int num7 = num5 >> 6;
			HashSet<string> hashSet = new HashSet<string>();
			for (int j = 0; j < remaining.Count; j++)
			{
				int num8 = remaining[j];
				if (!hashSet.Add(signatures[num8]))
				{
					continue;
				}
				List<Placement> list = placements[num8];
				for (int k = 0; k < list.Count; k++)
				{
					ulong[] mask = list[k].Mask;
					if ((mask[num7] & num6) != 0 && !Intersects(mask, occupied))
					{
						Or(occupied, mask);
						chosen[num8] = k;
						remaining.RemoveAt(j);
						if (NormalDfs(remaining, occupied, placements, sizes, signatures, neighbors, N, words, chosen))
						{
							return true;
						}
						remaining.Insert(j, num8);
						chosen[num8] = -1;
						AndNot(occupied, mask);
						if (_normalNodes > 6000000 || _normalTimer.ElapsedMilliseconds > 4000)
						{
							return false;
						}
					}
				}
			}
			return false;
		}
		int index = -1;
		int num9 = int.MaxValue;
		for (int l = 0; l < remaining.Count; l++)
		{
			int num10 = remaining[l];
			int num11 = 0;
			List<Placement> list2 = placements[num10];
			for (int m = 0; m < list2.Count; m++)
			{
				if (!Intersects(list2[m].Mask, occupied))
				{
					num11++;
				}
			}
			if (num11 == 0)
			{
				return false;
			}
			if (num11 < num9)
			{
				num9 = num11;
				index = l;
				if (num11 == 1)
				{
					break;
				}
			}
		}
		int num12 = remaining[index];
		remaining.RemoveAt(index);
		List<Placement> list3 = placements[num12];
		for (int n = 0; n < list3.Count; n++)
		{
			ulong[] mask2 = list3[n].Mask;
			if (!Intersects(mask2, occupied))
			{
				Or(occupied, mask2);
				chosen[num12] = n;
				if (NormalDfs(remaining, occupied, placements, sizes, signatures, neighbors, N, words, chosen))
				{
					return true;
				}
				chosen[num12] = -1;
				AndNot(occupied, mask2);
				if (_normalNodes > 6000000 || _normalTimer.ElapsedMilliseconds > 4000)
				{
					remaining.Insert(index, num12);
					return false;
				}
			}
		}
		remaining.Insert(index, num12);
		return false;
	}

	private static void Or(ulong[] target, ulong[] mask)
	{
		for (int i = 0; i < target.Length; i++)
		{
			target[i] |= mask[i];
		}
	}

	private static void AndNot(ulong[] target, ulong[] mask)
	{
		for (int i = 0; i < target.Length; i++)
		{
			target[i] &= ~mask[i];
		}
	}

	private static int TrailingZeros(ulong v)
	{
		if (v == 0)
		{
			return 64;
		}
		int num = 0;
		while ((v & 1) == 0)
		{
			v >>= 1;
			num++;
		}
		return num;
	}

	private static int FirstEmpty(ulong[] occupied, int N)
	{
		for (int i = 0; i < N; i++)
		{
			if ((occupied[i >> 6] & (ulong)(1L << i)) == 0)
			{
				return i;
			}
		}
		return -1;
	}

	private static int UsableCells(ulong[] occupied, ulong[][] neighbors, int N, int words, int minSize)
	{
		ulong[] array = new ulong[words];
		int num = 0;
		for (int i = 0; i < N; i++)
		{
			int num2 = i >> 6;
			ulong num3 = (ulong)(1L << i);
			if ((occupied[num2] & num3) != 0 || (array[num2] & num3) != 0)
			{
				continue;
			}
			int num4 = 0;
			List<int> list = new List<int> { i };
			array[num2] |= num3;
			while (list.Count > 0)
			{
				int num5 = list[list.Count - 1];
				list.RemoveAt(list.Count - 1);
				num4++;
				ulong[] array2 = neighbors[num5];
				for (int j = 0; j < words; j++)
				{
					ulong num6 = array2[j] & ~occupied[j] & ~array[j];
					while (num6 != 0)
					{
						ulong num7 = num6 & (~num6 + 1);
						int num8 = TrailingZeros(num7);
						num6 &= num6 - 1;
						int item = (j << 6) + num8;
						array[j] |= num7;
						list.Add(item);
					}
				}
			}
			if (num4 >= minSize)
			{
				num += num4;
			}
		}
		return num;
	}

	private IEnumerator SolveAndNotify(Action<bool> onComplete)
	{
		yield return SolveDfs(0);
		if (!_foundSolution)
		{
			FailureMessage = "No valid arrangement found for the selected upgrades. Your previous layout has been restored — deselect one or more upgrades and try again.";
			RestoreEquipped();
		}
		onComplete(_foundSolution);
	}

	public void TrySolve(Action<bool> onComplete)
	{
		_foundSolution = false;
		FailureMessage = null;
		if (_upgrades.Count == 0)
		{
			FailureMessage = "None of the selected upgrades belong to this gear.";
			onComplete(obj: false);
			return;
		}
		SnapshotEquipped();
		ClearSlots();
		if (!CanFitAll())
		{
			FailureMessage = "The selected upgrades cannot fit in the available space. Your previous layout has been restored.";
			RestoreEquipped();
			onComplete(obj: false);
		}
		else
		{
			UpgradeSolver.Instance.SolverCoroutine = ((MonoBehaviour)GridSolverPlugin.Instance).StartCoroutine(SolveAndNotify(onComplete));
		}
	}

	public void AbortAndRestore()
	{
		if ((Object)(object)_equipSlots == (Object)null)
		{
			return;
		}
		foreach (UpgradeInstance item in _upgrades.OrderBy((UpgradeInstance u) => GetPlacementPriority(u)))
		{
			_equipSlots.Unequip(_gear, item);
		}
		RestoreEquipped();
	}

	public bool CanFitAll()
	{
		if (_hexMap == null)
		{
			return true;
		}
		bool hasHPU = _upgrades.Any((UpgradeInstance u) => IsCellCountModifier(u));
		List<UpgradeInstance> list = _upgrades.Where((UpgradeInstance u) => IsGridExpander(u)).ToList();
		List<UpgradeInstance> list2 = _upgrades.Where((UpgradeInstance u) => IsCellCountModifier(u)).ToList();
		List<UpgradeInstance> source = _upgrades.Where((UpgradeInstance u) => !IsGridExpander(u) && !IsCellCountModifier(u)).ToList();
		foreach (UpgradeInstance item in list)
		{
			bool flag = false;
			int num = ((_hexMapHeightField != null) ? ((int)_hexMapHeightField.GetValue(_hexMap)) : 8);
			int num2 = ((_hexMapWidthField != null) ? ((int)_hexMapWidthField.GetValue(_hexMap)) : 8);
			List<int> value;
			List<int> list3 = (_uniqueRotationsCache.TryGetValue(item, out value) ? value : new List<int> { 0 });
			for (int num3 = 0; num3 < num; num3++)
			{
				if (flag)
				{
					break;
				}
				for (int num4 = 0; num4 < num2; num4++)
				{
					if (flag)
					{
						break;
					}
					foreach (int item2 in list3)
					{
						UpgradeEquipCell val = null;
						try
						{
							val = _equipSlots.GetCell(num4, num3);
						}
						catch
						{
						}
						if (!((Object)(object)val == (Object)null) && ((HoverInfoUpgrade)val).Upgrade == null)
						{
							Offset offsetsCached = GetOffsetsCached(item, item2, val);
							if (_equipSlots.EquipModule(_gear, item, offsetsCached.offsetX, offsetsCached.offsetY, (byte)item2, true, false))
							{
								flag = true;
								break;
							}
						}
					}
				}
			}
			if (flag)
			{
				continue;
			}
			foreach (UpgradeInstance item3 in list)
			{
				if (item3 == item)
				{
					break;
				}
				_equipSlots.Unequip(_gear, item3);
			}
			return false;
		}
		foreach (UpgradeInstance item4 in list2)
		{
			bool flag2 = false;
			int num5 = ((_hexMapHeightField != null) ? ((int)_hexMapHeightField.GetValue(_hexMap)) : 8);
			int num6 = ((_hexMapWidthField != null) ? ((int)_hexMapWidthField.GetValue(_hexMap)) : 8);
			List<int> value2;
			List<int> list4 = (_uniqueRotationsCache.TryGetValue(item4, out value2) ? value2 : new List<int> { 0 });
			for (int num7 = 0; num7 < num5; num7++)
			{
				if (flag2)
				{
					break;
				}
				for (int num8 = 0; num8 < num6; num8++)
				{
					if (flag2)
					{
						break;
					}
					foreach (int item5 in list4)
					{
						UpgradeEquipCell val2 = null;
						try
						{
							val2 = _equipSlots.GetCell(num8, num7);
						}
						catch
						{
						}
						if (!((Object)(object)val2 == (Object)null) && ((HoverInfoUpgrade)val2).Upgrade == null)
						{
							Offset offsetsCached2 = GetOffsetsCached(item4, item5, val2);
							if (_equipSlots.EquipModule(_gear, item4, offsetsCached2.offsetX, offsetsCached2.offsetY, (byte)item5, true, false))
							{
								flag2 = true;
								break;
							}
						}
					}
				}
			}
			if (flag2)
			{
				continue;
			}
			foreach (UpgradeInstance item6 in list)
			{
				_equipSlots.Unequip(_gear, item6);
			}
			foreach (UpgradeInstance item7 in list2)
			{
				if (item7 == item4)
				{
					break;
				}
				_equipSlots.Unequip(_gear, item7);
			}
			return false;
		}
		int num9 = ((_hexMapHeightField != null) ? ((int)_hexMapHeightField.GetValue(_hexMap)) : 8);
		int num10 = ((_hexMapWidthField != null) ? ((int)_hexMapWidthField.GetValue(_hexMap)) : 8);
		int num11 = 0;
		for (int num12 = 0; num12 < num9; num12++)
		{
			for (int num13 = 0; num13 < num10; num13++)
			{
				try
				{
					UpgradeEquipCell cell = _equipSlots.GetCell(num13, num12);
					if ((Object)(object)cell != (Object)null && ((HoverInfoUpgrade)cell).Upgrade == null)
					{
						num11++;
					}
				}
				catch
				{
				}
			}
		}
		int num14 = source.Select((UpgradeInstance u) => GetEffectiveCellCount(u, hasHPU)).Sum();
		foreach (UpgradeInstance item8 in list)
		{
			_equipSlots.Unequip(_gear, item8);
		}
		foreach (UpgradeInstance item9 in list2)
		{
			_equipSlots.Unequip(_gear, item9);
		}
		return num14 <= num11;
	}
}
namespace GridSolverPlus
{
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "GridSolverCoru";

		public const string PLUGIN_NAME = "GridSolverCoru";

		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)
		{
		}
	}
}