Decompiled source of Better Stats v0.9.2

BetterStatsMod.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("BetterStatsMod")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.9.2.0")]
[assembly: AssemblyInformationalVersion("0.9.2+6f338016fa16cdb7ae5a4e6a963d45471eefad4a")]
[assembly: AssemblyProduct("BetterStatsMod")]
[assembly: AssemblyTitle("BetterStatsMod")]
[assembly: AssemblyVersion("0.9.2.0")]
namespace BetterStatsModGenerated
{
	internal static class ModInfo
	{
		public const string GUID = "com.zelevlin.betterstats";

		public const string NAME = "Better_Stats";

		public const string VERSION = "0.9.2";
	}
}
namespace BetterStatsMod
{
	[BepInPlugin("com.zelevlin.betterstats", "Better_Stats", "0.9.2")]
	public sealed class Plugin : BasePlugin
	{
		internal static ManualLogSource LogSrc;

		internal static FileLogger FileLog;

		internal static Harmony Harmony;

		internal static bool EnableDebugLogs;

		internal static bool EnableVerboseLogs;

		internal static readonly bool IsDebugBuild;

		public override void Load()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			LogSrc = ((BasePlugin)this).Log;
			if (IsDebugBuild)
			{
				InitFileLogger();
				LogInfo("Loaded Better_Stats v0.9.2");
			}
			try
			{
				Harmony = new Harmony("com.zelevlin.betterstats");
				PatchHeadquarters();
				if (IsDebugBuild)
				{
					OverlayUi.Ensure();
					OverlayState.Refresh();
					ItemsExperiment.Initialize();
					LogInfo("Init finished");
				}
			}
			catch (Exception value)
			{
				LogError($"Load() exception: {value}");
			}
		}

		private void InitFileLogger()
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			try
			{
				string text = Path.GetDirectoryName(typeof(Plugin).Assembly.Location);
				if (string.IsNullOrEmpty(text))
				{
					text = Paths.PluginPath;
				}
				string text2 = Path.Combine(text, "BetterStatsMod.log");
				FileLog = new FileLogger(text2);
				FileLog.Info("FileLogger initialized at " + text2);
			}
			catch (Exception ex)
			{
				ManualLogSource logSrc = LogSrc;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(24, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("FileLogger init failed: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				logSrc.LogWarning(val);
			}
		}

		internal static void LogInfo(string message)
		{
			if (IsDebugBuild && (EnableVerboseLogs || !IsNoisyLog(message)))
			{
				ManualLogSource logSrc = LogSrc;
				if (logSrc != null)
				{
					logSrc.LogInfo((object)message);
				}
				FileLog?.Info(message);
			}
		}

		internal static void LogWarning(string message)
		{
			if (IsDebugBuild)
			{
				ManualLogSource logSrc = LogSrc;
				if (logSrc != null)
				{
					logSrc.LogWarning((object)message);
				}
				FileLog?.Warn(message);
			}
		}

		internal static void LogError(string message)
		{
			if (IsDebugBuild)
			{
				ManualLogSource logSrc = LogSrc;
				if (logSrc != null)
				{
					logSrc.LogError((object)message);
				}
				FileLog?.Error(message);
			}
		}

		internal static void LogAutoSelect(string message)
		{
			if (IsDebugBuild && EnableDebugLogs && !string.IsNullOrEmpty(message))
			{
				string text = "AutoSelect: " + message;
				ManualLogSource logSrc = LogSrc;
				if (logSrc != null)
				{
					logSrc.LogInfo((object)text);
				}
				FileLog?.Info(text);
			}
		}

		private static bool IsNoisyLog(string message)
		{
			if (string.IsNullOrEmpty(message))
			{
				return false;
			}
			if (!message.StartsWith("Auto select ", StringComparison.Ordinal) && !message.StartsWith("Auto-select ", StringComparison.Ordinal) && !message.StartsWith("TryComputeDamageParamWithTempEquip", StringComparison.Ordinal) && !message.StartsWith("TrySetEquipItem", StringComparison.Ordinal) && !message.StartsWith("DamageCalculatorResolver", StringComparison.Ordinal) && !message.StartsWith("DamageParamResolver", StringComparison.Ordinal) && !message.StartsWith("StatusDisplay set", StringComparison.Ordinal) && !message.StartsWith("NormalUnitModel update", StringComparison.Ordinal) && !message.StartsWith("HeroModel update", StringComparison.Ordinal) && !message.StartsWith("Input=OnSlotDecide", StringComparison.Ordinal) && !message.StartsWith("Input=Decide", StringComparison.Ordinal) && !message.StartsWith("Manual reselect", StringComparison.Ordinal))
			{
				return message.StartsWith("HQ unit dump", StringComparison.Ordinal);
			}
			return true;
		}

		private static void PatchHeadquarters()
		{
			PatchMethod("P2.Bases.HeadQuarters.NormalUnitUI", "update", "NormalUnitUIUpdate_Postfix");
			PatchMethod("P2S.Bases.HeadQuarters.NormalUnitUI", "update", "NormalUnitUIUpdate_Postfix");
			PatchMethod("P2.Bases.HeadQuarters.NormalUnitEquipSlot", "update", "NormalUnitEquipSlotUpdate_Postfix");
			PatchMethod("P2S.Bases.HeadQuarters.NormalUnitEquipSlot", "update", "NormalUnitEquipSlotUpdate_Postfix");
			PatchMethod("P2.Bases.HeadQuarters.UnitEquipSlot", "update", "UnitEquipSlotUpdate_Postfix");
			PatchMethod("P2S.Bases.HeadQuarters.UnitEquipSlot", "update", "UnitEquipSlotUpdate_Postfix");
			PatchMethod("P2.Bases.HeadQuarters.NormalUnitModel", "onChangeEquip", "NormalUnitModelOnChangeEquip_Postfix");
			PatchMethod("P2S.Bases.HeadQuarters.NormalUnitModel", "onChangeEquip", "NormalUnitModelOnChangeEquip_Postfix");
			PatchMethod("P2.Bases.HeadQuarters.HeroModel", "onChangeEquip", "HeroModelOnChangeEquip_Postfix");
			PatchMethod("P2S.Bases.HeadQuarters.HeroModel", "onChangeEquip", "HeroModelOnChangeEquip_Postfix");
			PatchMethod("P2.Bases.HeadQuarters.NormalUnitModel", "update", "NormalUnitModelUpdate_Postfix");
			PatchMethod("P2S.Bases.HeadQuarters.NormalUnitModel", "update", "NormalUnitModelUpdate_Postfix");
			PatchMethod("P2.Bases.HeadQuarters.HeroModel", "update", "HeroModelUpdate_Postfix");
			PatchMethod("P2S.Bases.HeadQuarters.HeroModel", "update", "HeroModelUpdate_Postfix");
			PatchMethod("P2.Bases.HeadQuarters.HeadQuartersMainObserver", "update", "Update_Postfix", typeof(HeadQuartersMainObserverHooks));
			PatchMethod("P2S.Bases.HeadQuarters.HeadQuartersMainObserver", "update", "Update_Postfix", typeof(HeadQuartersMainObserverHooks));
			PatchMethodWithPrefix("P2.Bases.HeadQuarters.SquadOrgView", "renderCursor", "RenderCursor_Prefix", typeof(SquadOrgViewHooks));
			PatchMethod("P2.Bases.HeadQuarters.SquadOrgView", "renderCursor", "RenderCursor_Postfix", typeof(SquadOrgViewHooks));
			PatchMethodWithPrefix("P2S.Bases.HeadQuarters.SquadOrgView", "renderCursor", "RenderCursor_Prefix", typeof(SquadOrgViewHooks));
			PatchMethod("P2S.Bases.HeadQuarters.SquadOrgView", "renderCursor", "RenderCursor_Postfix", typeof(SquadOrgViewHooks));
			PatchMethod("P2.Bases.HeadQuarters.SquadSelectModel", "getCurAddingParam", "SquadSelectModelGetCurAddingParam_Postfix", typeof(SquadSelectHooks));
			PatchMethod("P2S.Bases.HeadQuarters.SquadSelectModel", "getCurAddingParam", "SquadSelectModelGetCurAddingParam_Postfix", typeof(SquadSelectHooks));
			PatchMethod("P2.Bases.HeadQuarters.SquadSelectObserver", "getCurAddingParam", "SquadSelectObserverGetCurAddingParam_Postfix", typeof(SquadSelectHooks));
			PatchMethod("P2S.Bases.HeadQuarters.SquadSelectObserver", "getCurAddingParam", "SquadSelectObserverGetCurAddingParam_Postfix", typeof(SquadSelectHooks));
			PatchMethod("P2.Bases.HeadQuarters.SquadStatusView", "update", "SquadStatusViewUpdate_Postfix", typeof(SquadStatusViewHooks));
			PatchMethod("P2S.Bases.HeadQuarters.SquadStatusView", "update", "SquadStatusViewUpdate_Postfix", typeof(SquadStatusViewHooks));
			PatchMethod("P2.Bases.HeadQuarters.SquadSelectView", "update", "SquadSelectViewUpdate_Postfix", typeof(SquadStatusViewHooks));
			PatchMethod("P2S.Bases.HeadQuarters.SquadSelectView", "update", "SquadSelectViewUpdate_Postfix", typeof(SquadStatusViewHooks));
			PatchMethod("P2.Bases.HeadQuarters.SquadOrgObserver", "getUnitAddingParam", "GetUnitAddingParam_Postfix", typeof(SquadOrgObserverHooks));
			PatchMethod("P2S.Bases.HeadQuarters.SquadOrgObserver", "getUnitAddingParam", "GetUnitAddingParam_Postfix", typeof(SquadOrgObserverHooks));
			Type type = FindTypeByName("P2.Game.Unit.UnitAddingParam") ?? FindTypeByName("P2S.Game.Unit.UnitAddingParam");
			PatchMethod("P2.Bases.Organization.Managed.SquadStatusDisplay", "notifySquadChange", "NotifySquadChange_Postfix", typeof(SquadStatusDisplayHooks), (type == null) ? null : new Type[1] { type });
			PatchMethod("P2S.Bases.Organization.Managed.SquadStatusDisplay", "notifySquadChange", "NotifySquadChange_Postfix", typeof(SquadStatusDisplayHooks), (type == null) ? null : new Type[1] { type });
			PatchMethod("UnityEngine.UI.CanvasUpdateRegistry", "PerformUpdate", "CanvasUpdateRegistryPerformUpdate_Postfix", typeof(OverlayPulse));
			PatchMethodWithPrefix("P2.Bases.Organization.ItemMenu", "decide", "Decide_Prefix", typeof(ItemMenuHooks));
			PatchMethod("P2.Bases.Organization.ItemMenu", "decide", "Decide_Postfix", typeof(ItemMenuHooks));
			PatchMethodWithPrefix("P2S.Bases.Organization.ItemMenu", "decide", "Decide_Prefix", typeof(ItemMenuHooks));
			PatchMethod("P2S.Bases.Organization.ItemMenu", "decide", "Decide_Postfix", typeof(ItemMenuHooks));
			PatchMethodWithPrefix("P2.Bases.Organization.Managed.ItemMenu", "decide", "Decide_Prefix", typeof(ItemMenuHooks));
			PatchMethod("P2.Bases.Organization.Managed.ItemMenu", "decide", "Decide_Postfix", typeof(ItemMenuHooks));
			PatchMethodWithPrefix("P2S.Bases.Organization.Managed.ItemMenu", "decide", "Decide_Prefix", typeof(ItemMenuHooks));
			PatchMethod("P2S.Bases.Organization.Managed.ItemMenu", "decide", "Decide_Postfix", typeof(ItemMenuHooks));
			PatchMethodWithPrefix("P2.Bases.Organization.ItemSelectWindow", "onSlotDecide", "OnSlotDecide_Prefix", typeof(ItemSelectWindowHooks));
			PatchMethod("P2.Bases.Organization.ItemSelectWindow", "onSlotDecide", "OnSlotDecide_Postfix", typeof(ItemSelectWindowHooks));
			PatchMethodWithPrefix("P2S.Bases.Organization.ItemSelectWindow", "onSlotDecide", "OnSlotDecide_Prefix", typeof(ItemSelectWindowHooks));
			PatchMethod("P2S.Bases.Organization.ItemSelectWindow", "onSlotDecide", "OnSlotDecide_Postfix", typeof(ItemSelectWindowHooks));
			PatchMethodWithPrefix("P2.Bases.Organization.Managed.ItemSelectWindow", "onSlotDecide", "OnSlotDecide_Prefix", typeof(ItemSelectWindowHooks));
			PatchMethod("P2.Bases.Organization.Managed.ItemSelectWindow", "onSlotDecide", "OnSlotDecide_Postfix", typeof(ItemSelectWindowHooks));
			PatchMethodWithPrefix("P2S.Bases.Organization.Managed.ItemSelectWindow", "onSlotDecide", "OnSlotDecide_Prefix", typeof(ItemSelectWindowHooks));
			PatchMethod("P2S.Bases.Organization.Managed.ItemSelectWindow", "onSlotDecide", "OnSlotDecide_Postfix", typeof(ItemSelectWindowHooks));
		}

		private static void PatchMethod(string typeName, string methodName, string postfixName, Type postfixType = null, Type[] parameterTypes = null)
		{
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Expected O, but got Unknown
			Type type = FindTypeByName(typeName);
			if (type == null)
			{
				LogInfo("Type not found: " + typeName);
				return;
			}
			MethodInfo methodInfo = ((parameterTypes == null) ? AccessTools.Method(type, methodName, (Type[])null, (Type[])null) : AccessTools.Method(type, methodName, parameterTypes, (Type[])null));
			if (methodInfo == null)
			{
				LogWarning(typeName + "." + methodName + " not found");
				return;
			}
			Type type2 = postfixType ?? typeof(HeadquartersHooks);
			MethodInfo method = type2.GetMethod(postfixName, BindingFlags.Static | BindingFlags.Public);
			if (method == null)
			{
				LogWarning("Postfix not found: " + type2.FullName + "." + postfixName);
				return;
			}
			HarmonyMethod val = new HarmonyMethod(method);
			Harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			LogInfo($"Patched: {typeName}.{methodName} -> {postfixName}");
		}

		private static void PatchMethodWithPrefix(string typeName, string methodName, string prefixName, Type prefixType = null, Type[] parameterTypes = null)
		{
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Expected O, but got Unknown
			Type type = FindTypeByName(typeName);
			if (type == null)
			{
				LogInfo("Type not found: " + typeName);
				return;
			}
			MethodInfo methodInfo = ((parameterTypes == null) ? AccessTools.Method(type, methodName, (Type[])null, (Type[])null) : AccessTools.Method(type, methodName, parameterTypes, (Type[])null));
			if (methodInfo == null)
			{
				LogWarning(typeName + "." + methodName + " not found");
				return;
			}
			Type type2 = prefixType ?? typeof(HeadquartersHooks);
			MethodInfo method = type2.GetMethod(prefixName, BindingFlags.Static | BindingFlags.Public);
			if (method == null)
			{
				LogWarning("Prefix not found: " + type2.FullName + "." + prefixName);
				return;
			}
			HarmonyMethod val = new HarmonyMethod(method);
			Harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			LogInfo($"Patched prefix: {typeName}.{methodName} -> {prefixName}");
		}

		internal static Type FindTypeByName(string fullName)
		{
			string[] array = new string[8] { "UnityEditor", "BepInEx", "0Harmony", "Harmony", "System", "mscorlib", "netstandard", "Mono." };
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				string name = assembly.GetName().Name;
				bool flag = false;
				string[] array2 = array;
				foreach (string value in array2)
				{
					if (name.StartsWith(value, StringComparison.OrdinalIgnoreCase))
					{
						flag = true;
						break;
					}
				}
				if (flag)
				{
					continue;
				}
				try
				{
					Type type = assembly.GetType(fullName, throwOnError: false);
					if (type != null)
					{
						return type;
					}
				}
				catch
				{
				}
			}
			return null;
		}
	}
	public static class HeadquartersHooks
	{
		private static int _lastUiState = int.MinValue;

		private static int _lastEquipIndex = int.MinValue;

		private static int _lastSelectSlot = int.MinValue;

		public static void NormalUnitUIUpdate_Postfix(object __instance, uint dt)
		{
			if (Plugin.EnableDebugLogs)
			{
				int? num = ReflectionUtil.TryGetInt(__instance, "state_");
				if (num.HasValue && num.Value != _lastUiState)
				{
					_lastUiState = num.Value;
					Plugin.LogInfo($"NormalUnitUI state={_lastUiState}");
				}
			}
		}

		public static void NormalUnitEquipSlotUpdate_Postfix(object __instance, uint dt)
		{
			TrackEquipSelection("NormalUnitEquipSlot", __instance);
		}

		public static void UnitEquipSlotUpdate_Postfix(object __instance, uint dt)
		{
			TrackEquipSelection("UnitEquipSlot", __instance);
		}

		private static void TrackEquipSelection(string label, object instance)
		{
			if (!Plugin.EnableDebugLogs)
			{
				return;
			}
			int? num = ReflectionUtil.TryGetInt(instance, "curSelectSlot_");
			int? num2 = null;
			bool flag = false;
			if (num.HasValue)
			{
				num2 = ReflectionUtil.TryGetArrayElementInt(ReflectionUtil.TryGetObject(instance, "equipIndex_"), num.Value);
			}
			if (num2.HasValue && num2.Value != _lastEquipIndex)
			{
				_lastEquipIndex = num2.Value;
				flag = true;
			}
			if (num.HasValue && num.Value != _lastSelectSlot)
			{
				_lastSelectSlot = num.Value;
				flag = true;
			}
			if (flag)
			{
				int? num3 = null;
				int? num4 = null;
				object obj = ReflectionUtil.TryGetObject(instance, "model_");
				if (obj != null && _lastSelectSlot >= 0)
				{
					num3 = ReflectionUtil.TryInvokeInt(obj, "getEquipItemId", (uint)_lastSelectSlot);
					num4 = ReflectionUtil.TryGetInt(ReflectionUtil.TryGetObject(obj, "itemMenu_"), "itemId_");
				}
				Plugin.LogInfo($"{label} slot={_lastSelectSlot} equipIndex={_lastEquipIndex} selectedItemId={num4} equippedItemId={num3}");
				OverlayState.UpdateSelection(_lastSelectSlot, _lastEquipIndex, num4, num3, null);
			}
		}

		public static void NormalUnitModelOnChangeEquip_Postfix(object __instance, uint equipIndex, string name)
		{
			if (Plugin.EnableDebugLogs)
			{
				int? num = ReflectionUtil.TryGetInt(ReflectionUtil.TryGetObject(__instance, "itemMenu_"), "itemId_");
				int? num2 = ReflectionUtil.TryGetInt(__instance, "slotID_");
				int? num3 = ReflectionUtil.TryGetInt(__instance, "unitID_");
				int? equippedItemId = null;
				if (num2.HasValue && num2.Value >= 0)
				{
					equippedItemId = ReflectionUtil.TryInvokeInt(__instance, "getEquipItemId", (uint)num2.Value);
				}
				Plugin.LogInfo($"NormalUnitModel onChangeEquip equipIndex={equipIndex} name={name} slotId={num2} unitId={num3} selectedItemId={num}");
				OverlayState.UpdateOnChangeEquip(equipIndex, name, num2, num3, num, equippedItemId);
			}
		}

		public static void HeroModelOnChangeEquip_Postfix(object __instance, uint equipIndex, string name)
		{
			if (Plugin.EnableDebugLogs)
			{
				int? num = ReflectionUtil.TryGetInt(ReflectionUtil.TryGetObject(__instance, "itemMenu_"), "itemId_");
				int? num2 = ReflectionUtil.TryGetInt(__instance, "slotID_");
				int? num3 = ReflectionUtil.TryGetInt(__instance, "unitID_");
				int? equippedItemId = null;
				if (num2.HasValue && num2.Value >= 0)
				{
					equippedItemId = ReflectionUtil.TryInvokeInt(__instance, "getEquipItemId", (uint)num2.Value);
				}
				Plugin.LogInfo($"HeroModel onChangeEquip equipIndex={equipIndex} name={name} slotId={num2} unitId={num3} selectedItemId={num}");
				OverlayState.UpdateOnChangeEquip(equipIndex, name, num2, num3, num, equippedItemId);
			}
		}

		public static void NormalUnitModelUpdate_Postfix(object __instance, uint dt)
		{
			OverlayState.UpdateFromModel(__instance, "NormalUnitModel");
		}

		public static void HeroModelUpdate_Postfix(object __instance, uint dt)
		{
			OverlayState.UpdateFromModel(__instance, "HeroModel");
		}
	}
	public static class OverlayPulse
	{
		public static void CanvasUpdateRegistryPerformUpdate_Postfix()
		{
			OverlayUi.Ensure();
			OverlayState.Refresh();
			OverlayState.UpdatePendingDoubleConfirm();
		}
	}
	public static class HeadQuartersMainObserverHooks
	{
		public static void Update_Postfix(object __instance, uint dt)
		{
			object obj = ReflectionUtil.TryGetObject(__instance, "squadOrgObserver_");
			OverlayState.SetSquadOrgObserver(obj);
			if (obj != null)
			{
				OverlayState.NotifyHeadquartersActive();
			}
		}
	}
	public static class SquadOrgViewHooks
	{
		public static bool RenderCursor_Prefix(uint selectIndex)
		{
			if (OverlayState.IsCursorMovementSuppressed())
			{
				return false;
			}
			return true;
		}

		public static void RenderCursor_Postfix(uint selectIndex)
		{
			if (!OverlayState.IsCursorMovementSuppressed())
			{
				OverlayState.SetActiveUnitCursorIndex(selectIndex);
			}
		}
	}
	public static class ItemMenuHooks
	{
		public static void Decide_Prefix(uint itemId)
		{
			Plugin.LogInfo($"Input=Decide prefix itemId={itemId} {OverlayState.BuildSelectionSnapshot()}");
		}

		public static void Decide_Postfix(uint itemId)
		{
			if (OverlayState.TryConsumeAutoSelectCommit(itemId))
			{
				Plugin.LogInfo($"Input=Auto itemId={itemId}");
			}
			else
			{
				Plugin.LogInfo($"Input=Manual itemId={itemId}");
				OverlayState.NotifyManualEquipCommit(itemId);
			}
		}
	}
	public static class ItemSelectWindowHooks
	{
		public static void OnSlotDecide_Prefix(object __instance)
		{
			Plugin.LogInfo("Input=OnSlotDecide prefix " + OverlayState.BuildSelectionSnapshot());
		}

		public static void OnSlotDecide_Postfix(object __instance)
		{
			Plugin.LogInfo("Input=OnSlotDecide postfix " + OverlayState.BuildSelectionSnapshot());
		}
	}
	public static class SquadSelectHooks
	{
		public static void SquadSelectModelGetCurAddingParam_Postfix(ref object __0, bool __result)
		{
			OverlayState.UpdateHoverAddingParam(__result ? __0 : null, "SquadSelectModel");
		}

		public static void SquadSelectObserverGetCurAddingParam_Postfix(ref object __0, bool __result)
		{
			OverlayState.UpdateHoverAddingParam(__result ? __0 : null, "SquadSelectObserver");
		}
	}
	public static class SquadStatusViewHooks
	{
		public static void SquadStatusViewUpdate_Postfix(object __instance, uint dt)
		{
			OverlayState.UpdateStatusDisplayFromView(__instance);
		}

		public static void SquadSelectViewUpdate_Postfix(object __instance, uint dt)
		{
			OverlayState.UpdateStatusDisplayFromView(__instance);
			object obj = ReflectionUtil.TryGetObject(__instance, "model_");
			int? num = ReflectionUtil.TryGetInt(obj, "unitID_");
			if (num.HasValue && num.Value >= 0 && ReflectionUtil.TryInvokeBoolWithOutObject(obj, "getCurAddingParam", out var outValue))
			{
				OverlayState.UpdateHoverAddingParam(outValue, "SquadSelectView");
			}
		}
	}
	public static class SquadStatusDisplayHooks
	{
		public static void NotifySquadChange_Postfix(object __instance, object unitAddingParam)
		{
			OverlayState.UpdateStatusDisplayFromDisplay(__instance);
			OverlayState.UpdateHoverAddingParam(unitAddingParam, "SquadStatusDisplay");
		}
	}
	public static class SquadOrgObserverHooks
	{
		public static void GetUnitAddingParam_Postfix(uint slotID, object unitAddingParam)
		{
			if (OverlayState.ShouldUseSquadOrgAddingParam(slotID))
			{
				OverlayState.UpdateHoverAddingParam(unitAddingParam, "SquadOrgObserver");
			}
		}
	}
	public static class OverlayState
	{
		private static bool EnableAutoSelectOverlay = true;

		private static bool EnableAutoSelectFromHover = true;

		private static bool EnableAutoSelectDoubleConfirm = true;

		private const int HeroSquadIndex = 1;

		private const float AutoSelectDoubleConfirmDelaySeconds = 0.1f;

		private const float HeadquartersActiveTimeoutSeconds = 1.5f;

		private static float _lastHeadquartersHeartbeatTime = -1f;

		private static int _slot = -1;

		private static int _equipIndex = -1;

		private static int? _selectedItemId;

		private static int? _hoverItemId;

		private static int _hoverIndex = -1;

		private static int? _equippedItemId;

		private static int? _confirmedSelectedItemId;

		private static string _lastEquipName = string.Empty;

		private static uint? _lastEquipIndex;

		private static int? _unitId;

		private static string _source = "-";

		private static string _activeModelSource;

		private static string _activeUnitName;

		private static int? _activeUnitId;

		private static int? _activeSlotId;

		private static int? _activeSquadIndex;

		private static int? _activeCursorIndex;

		private static int? _lastActiveSquadForRefresh;

		private static bool _activeIsHero;

		private static bool _activeIsNormalUnit;

		private static bool _activeOpenItemMenu;

		private static bool _activeHasUnitId;

		private static object _itemMenu;

		private static object _itemOperator;

		private static object _squad;

		private static object _equippedDamageParam;

		private static object _autoSelectedDamageParam;

		private static object _statusDisplay;

		private static object _lastEquippedDamageParam;

		private static object _lastAutoSelectedDamageParam;

		private static int? _lastEquippedDamageItemId;

		private static object _lastDamageSquad;

		private static int? _lastDamageUnitId;

		private static object _lastStatusDisplay;

		private static object _hoverUnitAddingParam;

		private static object _lastHoverUnitAddingParam;

		private static string _lastAddingParamSource;

		private static float _lastAddingParamTime = -1f;

		private static int? _lastAutoSelectedItemIdForParam;

		private static int? _lastEquippedItemIdForParam;

		private static object _lastLoggedStatusDisplay;

		private static object _lastLoggedAutoSelectedParam;

		private static object _squadOrgObserver;

		private static object _squadModel;

		private static bool _didDumpSquadOrgUnits;

		private static float _lastDumpAttemptTime = -1f;

		private static int? _heroUnitIndexInSquad;

		private static int? _lastHeroSlotId;

		private static int? _lastHeroSelectedItemId;

		private static int? _lastHeroEquippedItemId;

		private static int? _lastHeroHoverItemId;

		private static int? _lastNormalSlotId;

		private static int? _lastNormalSelectedItemId;

		private static int? _lastNormalEquippedItemId;

		private static int? _lastNormalHoverItemId;

		private static object _lastStatsItemOperator;

		private static int? _autoSelectedStatsItemId;

		private static string _autoSelectedStatsText = string.Empty;

		private static int? _equippedStatsItemId;

		private static string _equippedStatsText = string.Empty;

		private static object _autoSelectedStatsDamageParam;

		private static object _equippedStatsDamageParam;

		private static int? _lastActiveUnitForEquippedStats;

		private static int? _differenceStatsAutoItemId;

		private static int? _differenceStatsEquippedItemId;

		private static string _differenceStatsText = string.Empty;

		private static string[] _differenceStatsReleaseLines = Array.Empty<string>();

		private static object _lastDifferenceAutoParam;

		private static object _lastDifferenceEquippedParam;

		private static bool _equippedStatsNeedsUpdate = true;

		private static string _equippedStatsUpdateReason = "-";

		private static int? _lastManualEquipItemId;

		private static int? _lastAutoSelectedHoverItemId;

		private static int _lastAutoSelectedHoverIndex = -1;

		private static object _itemSelectWindow;

		private static bool _autoSelectCommitPending;

		private static int? _autoSelectCommitItemId;

		private static float _pendingSecondConfirmAt = -1f;

		private static int _pendingSecondConfirmHoverIndex = -1;

		private static int? _pendingSecondConfirmItemId;

		private static bool _suppressSquadOrgCursorMovement;

		private static int? _pendingReselectItemId;

		public static bool IsCursorMovementSuppressed()
		{
			return _suppressSquadOrgCursorMovement;
		}

		private static void SetCursorMovementSuppressed(bool suppress)
		{
			_suppressSquadOrgCursorMovement = suppress;
		}

		public static void UpdateSelection(int slot, int equipIndex, int? selectedItemId, int? equippedItemId, int? unitId)
		{
			_slot = slot;
			_equipIndex = equipIndex;
			if (selectedItemId.HasValue)
			{
				_selectedItemId = selectedItemId;
			}
			if (equippedItemId.HasValue)
			{
				_equippedItemId = equippedItemId;
			}
			if (unitId.HasValue)
			{
				_unitId = unitId;
			}
			int? confirmedSelectedItemId = ResolveConfirmedSelected(_selectedItemId, _equippedItemId);
			if (confirmedSelectedItemId.HasValue)
			{
				_confirmedSelectedItemId = confirmedSelectedItemId;
			}
			Refresh();
		}

		public static void UpdateOnChangeEquip(uint equipIndex, string name, int? slotId, int? unitId, int? selectedItemId, int? equippedItemId)
		{
			_lastEquipIndex = equipIndex;
			_lastEquipName = name ?? string.Empty;
			if (slotId.HasValue)
			{
				_slot = slotId.Value;
			}
			if (unitId.HasValue)
			{
				_unitId = unitId;
			}
			if (selectedItemId.HasValue)
			{
				_selectedItemId = selectedItemId;
			}
			if (equippedItemId.HasValue)
			{
				_equippedItemId = equippedItemId;
			}
			int? confirmedSelectedItemId = ResolveConfirmedSelected(_selectedItemId, _equippedItemId);
			if (confirmedSelectedItemId.HasValue)
			{
				_confirmedSelectedItemId = confirmedSelectedItemId;
			}
			Refresh();
		}

		public static void UpdateFromModel(object model, string source)
		{
			if (model == null)
			{
				return;
			}
			bool flag = false;
			int? num = ReflectionUtil.TryGetInt(model, "slotID_");
			int? num2 = ReflectionUtil.TryGetInt(model, "unitID_");
			string text = ReflectionUtil.TryGetString(model, "name_");
			object obj = ReflectionUtil.TryInvokeObject(model, "getSquad") ?? ReflectionUtil.TryGetObject(model, "squad_");
			object obj2 = ReflectionUtil.TryGetObject(model, "itemMenu_");
			int? num3 = ReflectionUtil.TryGetInt(obj2, "itemId_");
			int? num4 = null;
			int num5 = -1;
			int? num6 = null;
			bool flag2 = string.Equals(source, "HeroModel", StringComparison.Ordinal);
			bool hasValue = num2.HasValue;
			bool flag3 = !flag2;
			bool valueOrDefault = ReflectionUtil.TryGetBool(model, "isOpenItemMenu_").GetValueOrDefault();
			if (_squadModel != null)
			{
				int? num7 = ReflectionUtil.TryGetInt(_squadModel, "selectLineIndex_");
				if (num7.HasValue)
				{
					_activeSquadIndex = num7.Value;
				}
			}
			if (_activeSquadIndex.GetValueOrDefault() == 1 && flag3)
			{
				return;
			}
			if (flag3 && hasValue && (!string.Equals(_activeModelSource, source, StringComparison.Ordinal) || _activeUnitId != num2))
			{
				_activeModelSource = source;
				_activeIsHero = false;
				_activeIsNormalUnit = true;
				_activeOpenItemMenu = valueOrDefault;
				_activeHasUnitId = true;
				_activeUnitId = num2;
				_activeUnitName = text;
				_activeSlotId = num;
				_itemMenu = null;
				_itemSelectWindow = null;
				_itemOperator = null;
				_lastAutoSelectedHoverItemId = null;
				_lastAutoSelectedHoverIndex = -1;
				_autoSelectCommitPending = false;
				_autoSelectCommitItemId = null;
				_lastAddingParamSource = null;
				_lastAddingParamTime = -1f;
			}
			if (valueOrDefault)
			{
				bool flag4 = flag2 && !hasValue;
				bool flag5 = string.Equals(_activeModelSource, "HeroModel", StringComparison.Ordinal);
				if (_squadModel != null)
				{
					int? num8 = ReflectionUtil.TryGetInt(_squadModel, "selectLineIndex_");
					if (num8.HasValue)
					{
						_activeSquadIndex = num8.Value;
					}
				}
				if (_activeSquadIndex.GetValueOrDefault() == 1 && flag3)
				{
					return;
				}
				if (flag2 || flag3 || flag4 || !_activeIsNormalUnit || !_activeOpenItemMenu)
				{
					_activeModelSource = source;
					_activeIsHero = flag2 && !hasValue;
					_activeIsNormalUnit = flag3;
					_activeOpenItemMenu = true;
					_activeHasUnitId = hasValue;
					_activeUnitId = (hasValue ? num2 : null);
					_activeUnitName = text;
					_activeSlotId = num;
					_itemMenu = null;
					_itemSelectWindow = null;
					_itemOperator = null;
					_lastAutoSelectedHoverItemId = null;
					_lastAutoSelectedHoverIndex = -1;
					_autoSelectCommitPending = false;
					_autoSelectCommitItemId = null;
					_lastAddingParamSource = null;
					_lastAddingParamTime = -1f;
					if (flag2 && !flag5)
					{
						MarkEquippedStatsDirty("hero-open");
						_lastActiveUnitForEquippedStats = null;
					}
				}
			}
			else
			{
				if (_activeModelSource != null && !string.Equals(source, _activeModelSource, StringComparison.Ordinal))
				{
					return;
				}
				if (string.Equals(_activeModelSource, source, StringComparison.Ordinal))
				{
					_activeOpenItemMenu = false;
					_activeIsHero = flag2 && !hasValue;
					_activeIsNormalUnit = flag3;
					_activeHasUnitId = hasValue;
					if (!string.IsNullOrEmpty(text))
					{
						_activeUnitName = text;
					}
					if (num2.HasValue)
					{
						_activeUnitId = num2;
					}
					if (num.HasValue)
					{
						_activeSlotId = num;
					}
				}
			}
			if (num.HasValue && num.Value >= 0)
			{
				num6 = ReflectionUtil.TryInvokeInt(model, "getEquipItemId", (uint)num.Value);
			}
			if (obj2 != null)
			{
				if (obj2 != _itemMenu)
				{
					_itemMenu = obj2;
					_itemOperator = ReflectionUtil.TryGetObject(obj2, "itemOperator_");
					_activeModelSource = source;
					_lastAutoSelectedHoverItemId = null;
					_lastAutoSelectedHoverIndex = -1;
					_autoSelectCommitPending = false;
					_autoSelectCommitItemId = null;
					flag = true;
				}
				object obj3 = ReflectionUtil.TryGetObject(obj2, "itemSelectWindow_");
				if (obj3 != _itemSelectWindow)
				{
					_itemSelectWindow = obj3;
					_lastAutoSelectedHoverItemId = null;
					_lastAutoSelectedHoverIndex = -1;
				}
				if (obj3 != null)
				{
					int? num9 = ReflectionUtil.TryGetInt(obj3, "selectIndex_");
					if (num9.HasValue)
					{
						num5 = num9.Value;
						object obj4 = ReflectionUtil.TryGetListElement(ReflectionUtil.TryGetObject(obj3, "belongingItemIdArray_"), num5);
						num4 = ReflectionUtil.TryGetInt(obj4, "itemId");
						if (!num4.HasValue && obj4 != null)
						{
							try
							{
								num4 = Convert.ToInt32(obj4);
							}
							catch
							{
							}
						}
					}
				}
			}
			if (num.HasValue && num.Value != _slot)
			{
				_slot = num.Value;
				flag = true;
			}
			if (num2.HasValue && num2 != _unitId)
			{
				_unitId = num2;
				flag = true;
			}
			if (flag2 && !hasValue && _unitId.HasValue)
			{
				_unitId = null;
				flag = true;
			}
			if (num3.HasValue && num3 != _selectedItemId)
			{
				TryHandleManualReselectPattern(_selectedItemId, num3);
				_selectedItemId = num3;
				flag = true;
			}
			if (num4.HasValue && num4 != _hoverItemId)
			{
				_hoverItemId = num4;
				flag = true;
				TryAutoSelectHoverItem(num4, num5);
			}
			if (num5 != _hoverIndex)
			{
				_hoverIndex = num5;
				flag = true;
			}
			if (num6.HasValue && num6 != _equippedItemId)
			{
				_equippedItemId = num6;
				flag = true;
			}
			TryClearAutoSelectPendingOnModelChange(num3, num6);
			if (_source != source)
			{
				_source = source;
				flag = true;
			}
			if (obj != null && obj != _squad)
			{
				_squad = obj;
				flag = true;
			}
			int? num10 = ResolveConfirmedSelected(num3, num6);
			if (num10.HasValue && num10 != _confirmedSelectedItemId)
			{
				_confirmedSelectedItemId = num10;
				flag = true;
			}
			if (!flag)
			{
				return;
			}
			bool flag6 = string.Equals(source, "HeroModel", StringComparison.Ordinal);
			int? num11 = (flag6 ? _lastHeroSlotId : _lastNormalSlotId);
			int? num12 = (flag6 ? _lastHeroSelectedItemId : _lastNormalSelectedItemId);
			int? num13 = (flag6 ? _lastHeroEquippedItemId : _lastNormalEquippedItemId);
			int? num14 = (flag6 ? _lastHeroHoverItemId : _lastNormalHoverItemId);
			if (num != num11 || num3 != num12 || num6 != num13 || (EnableAutoSelectOverlay && num4 != num14))
			{
				string value = (EnableAutoSelectOverlay ? $" hoverItemId={num4} hoverIndex={num5}" : string.Empty);
				Plugin.LogInfo($"{source} update slotId={num} unitId={num2} selectedItemId={num3}{value} equippedItemId={num6}");
				if (flag6)
				{
					_lastHeroSlotId = num;
					_lastHeroSelectedItemId = num3;
					_lastHeroEquippedItemId = num6;
					_lastHeroHoverItemId = num4;
				}
				else
				{
					_lastNormalSlotId = num;
					_lastNormalSelectedItemId = num3;
					_lastNormalEquippedItemId = num6;
					_lastNormalHoverItemId = num4;
				}
			}
		}

		public static void Refresh()
		{
			OverlayUi.SetText("Better Stats\nSource: " + _source + "\nActive unit: " + FormatActiveUnit() + "\nSelected item: " + FormatNullable(_selectedItemId) + "\n" + $"Hover item: {FormatNullable(_hoverItemId)} (index {FormatInt(_hoverIndex)})\n" + "Equipped item: " + FormatNullable(_equippedItemId) + "\n" + $"Slot: {FormatInt(_slot)}  EquipIndex: {FormatInt(_equipIndex)}  Squad: {FormatNullable(_activeSquadIndex)}\n" + "UnitId: " + FormatNullable(_unitId) + "\nLast equip change: " + FormatEquipChange());
			if (_lastDamageSquad != _squad || _lastDamageUnitId != _unitId || _lastEquippedDamageItemId != _equippedItemId)
			{
				_lastDamageSquad = _squad;
				_lastDamageUnitId = _unitId;
				_lastEquippedDamageItemId = _equippedItemId;
				_equippedDamageParam = DamageCalculatorResolver.TryCalcUnitDamage(_squad, _unitId);
			}
			if (_lastStatusDisplay != _statusDisplay || _lastHoverUnitAddingParam != _hoverUnitAddingParam)
			{
				_lastStatusDisplay = _statusDisplay;
				_lastHoverUnitAddingParam = _hoverUnitAddingParam;
				_lastAutoSelectedItemIdForParam = null;
				_lastEquippedItemIdForParam = null;
			}
			if (_activeIsNormalUnit)
			{
				TryResolveNormalUnitAddingParam();
			}
			if (_activeSquadIndex != _lastActiveSquadForRefresh)
			{
				_lastActiveSquadForRefresh = _activeSquadIndex;
				MarkEquippedStatsDirty("squad-change");
				_autoSelectedStatsItemId = null;
				_equippedStatsItemId = null;
				_autoSelectedStatsDamageParam = null;
				_equippedStatsDamageParam = null;
				_lastAutoSelectedDamageParam = null;
				_lastEquippedDamageParam = null;
				_differenceStatsAutoItemId = null;
				_differenceStatsEquippedItemId = null;
				_lastDifferenceAutoParam = null;
				_lastDifferenceEquippedParam = null;
				_lastActiveUnitForEquippedStats = null;
			}
			if (_activeIsNormalUnit && _lastActiveUnitForEquippedStats != _activeUnitId)
			{
				_lastActiveUnitForEquippedStats = _activeUnitId;
				MarkEquippedStatsDirty("unit-change");
			}
			int? equippedItemId = _equippedItemId;
			if (EnableAutoSelectOverlay && (_lastAutoSelectedItemIdForParam != equippedItemId || _lastAutoSelectedDamageParam != _autoSelectedDamageParam))
			{
				Plugin.LogInfo($"Auto select compute slot={_slot} autoSelectedItemId={equippedItemId} statusDisplay={_statusDisplay?.GetType().FullName ?? "-"} addingParam={_hoverUnitAddingParam?.GetType().FullName ?? "-"}");
				_autoSelectedDamageParam = UnitAddingParamResolver.TryComputeDamageParamWithTempEquip(_statusDisplay, _hoverUnitAddingParam, _slot, equippedItemId, _itemOperator);
				_lastAutoSelectedItemIdForParam = equippedItemId;
				Plugin.LogInfo("Auto select result damageParam=" + ((_autoSelectedDamageParam == null) ? "-" : _autoSelectedDamageParam.GetType().FullName));
			}
			if (_lastEquippedItemIdForParam != _equippedItemId || _lastEquippedDamageParam != _equippedDamageParam)
			{
				_equippedDamageParam = UnitAddingParamResolver.TryComputeDamageParamWithTempEquip(_statusDisplay, _hoverUnitAddingParam, _slot, _equippedItemId, _itemOperator);
				_lastEquippedItemIdForParam = _equippedItemId;
				_lastEquippedDamageParam = _equippedDamageParam;
			}
			if (_lastStatsItemOperator != _itemOperator)
			{
				_lastStatsItemOperator = _itemOperator;
				_autoSelectedStatsItemId = null;
				_equippedStatsItemId = null;
				_autoSelectedStatsDamageParam = null;
				_equippedStatsDamageParam = null;
				_differenceStatsAutoItemId = null;
				_differenceStatsEquippedItemId = null;
				_lastDifferenceAutoParam = null;
				_lastDifferenceEquippedParam = null;
			}
			if (_equippedStatsNeedsUpdate && (_equippedStatsItemId != _equippedItemId || _lastEquippedDamageParam != _equippedDamageParam))
			{
				_equippedStatsText = ItemStatResolver.BuildStatsFromDamageParam("Equipped stats", _equippedDamageParam, _equippedItemId);
				_equippedStatsItemId = _equippedItemId;
				_equippedStatsDamageParam = _equippedDamageParam;
				_equippedStatsNeedsUpdate = false;
				Plugin.LogInfo($"Equipped stats updated itemId={_equippedItemId} reason={_equippedStatsUpdateReason}");
			}
			if (EnableAutoSelectOverlay && _autoSelectedDamageParam != null)
			{
				if (_autoSelectedStatsItemId != equippedItemId || _lastAutoSelectedDamageParam != _autoSelectedDamageParam)
				{
					_autoSelectedStatsText = ItemStatResolver.BuildStatsFromDamageParam("Auto selected stats", _autoSelectedDamageParam, equippedItemId);
					_autoSelectedStatsItemId = equippedItemId;
					_autoSelectedStatsDamageParam = _autoSelectedDamageParam;
					_lastAutoSelectedDamageParam = _autoSelectedDamageParam;
				}
			}
			else
			{
				_autoSelectedStatsText = string.Empty;
				_autoSelectedStatsItemId = null;
				_autoSelectedStatsDamageParam = null;
				_lastAutoSelectedDamageParam = null;
			}
			if (EnableAutoSelectOverlay && _autoSelectedStatsDamageParam != null && _equippedStatsDamageParam != null)
			{
				if (_differenceStatsAutoItemId != _autoSelectedStatsItemId || _differenceStatsEquippedItemId != _equippedStatsItemId || _lastDifferenceAutoParam != _autoSelectedStatsDamageParam || _lastDifferenceEquippedParam != _equippedStatsDamageParam)
				{
					_differenceStatsText = ItemStatResolver.BuildStatsDifference("Stats difference", _autoSelectedStatsDamageParam, _equippedStatsDamageParam, _autoSelectedStatsItemId, _equippedStatsItemId);
					_differenceStatsReleaseLines = ItemStatResolver.BuildStatsDifferenceNumberLines(_autoSelectedStatsDamageParam, _equippedStatsDamageParam, _autoSelectedStatsItemId, _equippedStatsItemId);
					_differenceStatsAutoItemId = _autoSelectedStatsItemId;
					_differenceStatsEquippedItemId = _equippedStatsItemId;
					_lastDifferenceAutoParam = _autoSelectedStatsDamageParam;
					_lastDifferenceEquippedParam = _equippedStatsDamageParam;
				}
			}
			else
			{
				_differenceStatsText = string.Empty;
				_differenceStatsReleaseLines = Array.Empty<string>();
				_differenceStatsAutoItemId = null;
				_differenceStatsEquippedItemId = null;
				_lastDifferenceAutoParam = null;
				_lastDifferenceEquippedParam = null;
			}
			OverlayUi.SetStatsText(_equippedStatsText, _autoSelectedStatsText, _differenceStatsText);
			OverlayUi.SetAutoSelectedVisible(EnableAutoSelectOverlay);
			bool num = IsHeadquartersActive();
			ReleaseOverlayUi.SetVisible(num);
			ReleaseOverlayUi.SetText(num ? _differenceStatsReleaseLines : Array.Empty<string>());
			TryDumpSquadOrgUnits();
		}

		private static string FormatNullable(int? value)
		{
			if (!value.HasValue)
			{
				return "-";
			}
			return value.Value.ToString();
		}

		private static string FormatInt(int value)
		{
			if (value < 0)
			{
				return "-";
			}
			return value.ToString();
		}

		private static string FormatEquipChange()
		{
			if (_lastEquipIndex.HasValue || !string.IsNullOrEmpty(_lastEquipName))
			{
				return _lastEquipName + " (" + (_lastEquipIndex?.ToString() ?? "-") + ")";
			}
			return "-";
		}

		private static void MarkEquippedStatsDirty(string reason)
		{
			_equippedStatsNeedsUpdate = true;
			_equippedStatsUpdateReason = (string.IsNullOrEmpty(reason) ? "-" : reason);
		}

		private static string FormatActiveUnit()
		{
			string value = (string.IsNullOrEmpty(_activeUnitName) ? "-" : _activeUnitName);
			string value2 = (_activeUnitId.HasValue ? _activeUnitId.Value.ToString() : "-");
			string value3 = (_activeHasUnitId ? "Unit" : (_activeIsHero ? "Hero" : "Unit"));
			string value4 = (_activeSlotId.HasValue ? _activeSlotId.Value.ToString() : "-");
			return $"{value3} {value} (slot {value4}, id {value2})";
		}

		private static string FormatBool(bool? value)
		{
			if (!value.HasValue)
			{
				return "-";
			}
			if (!value.Value)
			{
				return "false";
			}
			return "true";
		}

		private static void TryAutoSelectHoverItem(int? hoverItemId, int hoverIndex)
		{
			if (!EnableAutoSelectFromHover || !hoverItemId.HasValue || _itemMenu == null || hoverIndex < 0)
			{
				return;
			}
			int value = hoverItemId.Value;
			if (value < 0 || (_lastAutoSelectedHoverItemId == value && _lastAutoSelectedHoverIndex == hoverIndex))
			{
				return;
			}
			if ((_selectedItemId.HasValue && _selectedItemId.Value == value) || (_confirmedSelectedItemId.HasValue && _confirmedSelectedItemId.Value == value))
			{
				_lastAutoSelectedHoverItemId = value;
				_lastAutoSelectedHoverIndex = hoverIndex;
				return;
			}
			Plugin.LogInfo($"Auto-select hover item {value}");
			_lastAutoSelectedHoverItemId = value;
			_lastAutoSelectedHoverIndex = hoverIndex;
			_autoSelectCommitPending = true;
			_autoSelectCommitItemId = value;
			SetCursorMovementSuppressed(suppress: true);
			if (_itemSelectWindow != null)
			{
				ReflectionUtil.TrySetInt(_itemSelectWindow, "selectIndex_", hoverIndex);
				ReflectionUtil.TryInvokeObject(_itemSelectWindow, "onSlotDecide");
				if (EnableAutoSelectDoubleConfirm)
				{
					ScheduleDoubleConfirm(hoverIndex, value);
				}
			}
			else
			{
				ReflectionUtil.TryInvokeObject(_itemMenu, "decide", (uint)value);
				if (EnableAutoSelectDoubleConfirm)
				{
					ScheduleDoubleConfirm(hoverIndex, value);
				}
			}
		}

		internal static bool TryConsumeAutoSelectCommit(uint itemId)
		{
			if (!_autoSelectCommitPending)
			{
				return false;
			}
			if (_autoSelectCommitItemId.HasValue && _autoSelectCommitItemId.Value != itemId)
			{
				_autoSelectCommitPending = false;
				_autoSelectCommitItemId = null;
				Plugin.LogInfo($"Auto-select commit mismatch itemId={itemId}");
				return false;
			}
			_autoSelectCommitPending = false;
			_autoSelectCommitItemId = null;
			Plugin.LogInfo($"Auto-select commit consumed itemId={itemId}");
			if (!EnableAutoSelectDoubleConfirm)
			{
				SetCursorMovementSuppressed(suppress: false);
			}
			return true;
		}

		private static void TryHandleManualReselectPattern(int? prevSelected, int? nextSelected)
		{
			if (!nextSelected.HasValue || _autoSelectCommitPending)
			{
				return;
			}
			int value = nextSelected.Value;
			int? num = prevSelected;
			if (value < 0)
			{
				if (num.HasValue && num.Value >= 0)
				{
					_pendingReselectItemId = num.Value;
					Plugin.LogInfo($"Manual reselect armed itemId={num.Value}");
				}
			}
			else if (_pendingReselectItemId.HasValue && num.HasValue && num.Value < 0)
			{
				if (_pendingReselectItemId.Value == value)
				{
					_pendingReselectItemId = null;
					MarkEquippedStatsDirty("manual-reselect");
					_lastManualEquipItemId = value;
					Plugin.LogInfo($"Manual reselect detected itemId={value}");
				}
				else
				{
					_pendingReselectItemId = null;
				}
			}
		}

		internal static string BuildSelectionSnapshot()
		{
			return $"selected={FormatNullable(_selectedItemId)} equipped={FormatNullable(_equippedItemId)} hover={FormatNullable(_hoverItemId)} hoverIndex={FormatInt(_hoverIndex)} slot={FormatInt(_slot)} unit={FormatNullable(_unitId)}";
		}

		private static void TryClearAutoSelectPendingOnModelChange(int? selectedItemId, int? equippedItemId)
		{
			if (_autoSelectCommitPending && _autoSelectCommitItemId.HasValue)
			{
				int value = _autoSelectCommitItemId.Value;
				if ((selectedItemId.HasValue && selectedItemId.Value == value) || (equippedItemId.HasValue && equippedItemId.Value == value))
				{
					_autoSelectCommitPending = false;
					_autoSelectCommitItemId = null;
					Plugin.LogInfo($"Auto-select commit cleared by model change itemId={value}");
				}
			}
		}

		internal static void NotifyManualEquipCommit(uint itemId)
		{
			MarkEquippedStatsDirty("manual-commit");
			_lastManualEquipItemId = (int)itemId;
			if (_activeIsHero)
			{
				if (!_equippedItemId.HasValue || _equippedItemId.Value != (int)itemId)
				{
					_equippedItemId = (int)itemId;
				}
			}
			Plugin.LogInfo($"Manual equip commit detected for item {itemId}");
		}

		internal static void UpdatePendingDoubleConfirm()
		{
			if (!(_pendingSecondConfirmAt < 0f) && !(Time.realtimeSinceStartup < _pendingSecondConfirmAt))
			{
				int pendingSecondConfirmHoverIndex = _pendingSecondConfirmHoverIndex;
				int? pendingSecondConfirmItemId = _pendingSecondConfirmItemId;
				ClearPendingDoubleConfirm();
				Plugin.LogAutoSelect($"triggering double confirm itemId={pendingSecondConfirmItemId} index={pendingSecondConfirmHoverIndex} at {Time.realtimeSinceStartup:0.000}");
				bool value = InputUtil.SendSpaceKey();
				Plugin.LogInfo($"Auto-select double confirm (space) itemId={pendingSecondConfirmItemId} index={pendingSecondConfirmHoverIndex} sent={value}");
				Plugin.LogAutoSelect($"space key send result={value}");
			}
		}

		private static void ScheduleDoubleConfirm(int hoverIndex, int itemId)
		{
			SetCursorMovementSuppressed(suppress: true);
			_pendingSecondConfirmHoverIndex = hoverIndex;
			_pendingSecondConfirmItemId = itemId;
			float value = (_pendingSecondConfirmAt = Time.realtimeSinceStartup + 0.1f);
			Plugin.LogInfo($"Auto-select double confirm scheduled itemId={itemId} index={hoverIndex}");
			Plugin.LogAutoSelect($"scheduled itemId={itemId} index={hoverIndex} at {Time.realtimeSinceStartup:0.000} -> {value:0.000}");
		}

		private static void ClearPendingDoubleConfirm()
		{
			_pendingSecondConfirmAt = -1f;
			_pendingSecondConfirmHoverIndex = -1;
			_pendingSecondConfirmItemId = null;
			SetCursorMovementSuppressed(suppress: false);
		}

		public static void UpdateStatusDisplayFromView(object view)
		{
			if (view == null)
			{
				return;
			}
			object obj = ReflectionUtil.TryGetObject(view, "squadStatusDisplay_") ?? ReflectionUtil.TryInvokeObject(view, "getDisplay") ?? ReflectionUtil.TryGetObject(view, "display_");
			if (obj != null && obj != _statusDisplay)
			{
				_statusDisplay = obj;
				if (Plugin.EnableDebugLogs && _lastLoggedStatusDisplay != obj)
				{
					_lastLoggedStatusDisplay = obj;
					Plugin.LogInfo("StatusDisplay set: " + obj.GetType().FullName);
				}
			}
		}

		public static void UpdateStatusDisplayFromDisplay(object display)
		{
			if (display != null && _statusDisplay != display)
			{
				_statusDisplay = display;
				if (Plugin.EnableDebugLogs && _lastLoggedStatusDisplay != display)
				{
					_lastLoggedStatusDisplay = display;
					Plugin.LogInfo("StatusDisplay set: " + display.GetType().FullName);
				}
			}
		}

		public static void UpdateHoverAddingParam(object addingParam, string source)
		{
			if (_hoverUnitAddingParam != addingParam)
			{
				_hoverUnitAddingParam = addingParam;
				_lastAddingParamSource = source;
				_lastAddingParamTime = Time.realtimeSinceStartup;
				if (EnableAutoSelectOverlay && Plugin.EnableDebugLogs && _lastLoggedAutoSelectedParam != addingParam)
				{
					_lastLoggedAutoSelectedParam = addingParam;
					Plugin.LogInfo("Auto select adding param: " + ((addingParam == null) ? "-" : addingParam.GetType().FullName));
				}
			}
		}

		public static bool ShouldUseSquadOrgAddingParam(uint slotId)
		{
			if (!_activeIsNormalUnit)
			{
				return false;
			}
			if (_slot < 0)
			{
				return true;
			}
			return slotId == (uint)_slot;
		}

		private static bool IsNormalUnitAddingParamSource(string source)
		{
			if (!string.Equals(source, "SquadOrgObserver", StringComparison.Ordinal))
			{
				return string.Equals(source, "SquadModelUnit", StringComparison.Ordinal);
			}
			return true;
		}

		private static int ResolveUnitIndex(object unitArray)
		{
			int num = ReflectionUtil.TryGetListCount(unitArray);
			int? activeUnitId = _activeUnitId;
			int? num2 = null;
			if (activeUnitId.HasValue)
			{
				num2 = activeUnitId.Value;
				if (num2.Value < 0)
				{
					num2 = null;
				}
			}
			int? num3 = null;
			if (activeUnitId.HasValue && new int?(activeUnitId.Value + 1).Value < 0)
			{
				num3 = null;
			}
			int?[] array = new int?[1] { num2 };
			for (int i = 0; i < array.Length; i++)
			{
				int? num4 = array[i];
				if (num4.HasValue && (num <= 0 || num4.Value < num) && num4.Value >= 0)
				{
					return num4.Value;
				}
			}
			return -1;
		}

		public static void SetSquadOrgObserver(object observer)
		{
			if (observer != null && observer != _squadOrgObserver)
			{
				_squadOrgObserver = observer;
				_squadModel = ReflectionUtil.TryGetObject(observer, "paragetoSquadModel_");
				_didDumpSquadOrgUnits = false;
			}
		}

		public static void NotifyHeadquartersActive()
		{
			_lastHeadquartersHeartbeatTime = Time.realtimeSinceStartup;
		}

		public static void SetActiveUnitCursorIndex(uint selectIndex)
		{
			_activeCursorIndex = (int)selectIndex;
		}

		private static bool IsHeadquartersActive()
		{
			if (_lastHeadquartersHeartbeatTime < 0f)
			{
				return false;
			}
			return Time.realtimeSinceStartup - _lastHeadquartersHeartbeatTime <= 1.5f;
		}

		private static void TryDumpSquadOrgUnits()
		{
			if (_squadOrgObserver == null || _didDumpSquadOrgUnits || (_lastDumpAttemptTime >= 0f && Time.realtimeSinceStartup - _lastDumpAttemptTime < 1f))
			{
				return;
			}
			_lastDumpAttemptTime = Time.realtimeSinceStartup;
			try
			{
				object obj = ReflectionUtil.TryGetObject(_squadOrgObserver, "paragetoSquadModel_");
				if (obj == null || !ReflectionUtil.TryInvokeBool(obj, "isReadyOrganization").GetValueOrDefault())
				{
					return;
				}
				object listObj = ReflectionUtil.TryInvokeObject(obj, "getSquadArray");
				int num = ReflectionUtil.TryGetListCount(listObj);
				if (num <= 0)
				{
					return;
				}
				Plugin.LogInfo($"HQ unit dump: squads={num}");
				for (int i = 0; i < num; i++)
				{
					object obj2 = ReflectionUtil.TryGetListElement(listObj, i);
					object listObj2 = ReflectionUtil.TryGetObject(obj2, "unitArray_");
					int num2 = ReflectionUtil.TryGetListCount(listObj2);
					string text = ReflectionUtil.TryGetString(obj2, "unitName_");
					bool? value = ReflectionUtil.TryGetBool(obj2, "isHero_");
					int? value2 = ReflectionUtil.TryGetInt(obj2, "heroIndex_");
					Plugin.LogInfo($"HQ squad={i} name={text ?? "-"} units={num2} squadHero={FormatBool(value)} heroIndex={FormatNullable(value2)}");
					for (int j = 0; j < num2; j++)
					{
						object obj3 = ReflectionUtil.TryGetListElement(listObj2, j);
						string text2 = ReflectionUtil.TryGetString(obj3, "name_");
						bool? value3 = ReflectionUtil.TryGetBool(obj3, "isHero_");
						object obj4 = ReflectionUtil.TryGetObject(obj3, "unitAddingParam_");
						int? value4 = ReflectionUtil.TryGetInt(obj4, "weaponId");
						int? value5 = ReflectionUtil.TryGetInt(obj4, "cap");
						Plugin.LogInfo($"HQ squad={i} unit={j} name={text2 ?? "-"} hero={FormatBool(value3)} weaponId={FormatNullable(value4)} cap={FormatNullable(value5)}");
					}
				}
				_didDumpSquadOrgUnits = true;
			}
			catch (Exception ex)
			{
				Plugin.LogWarning("HQ unit dump failed: " + ex.Message);
			}
		}

		private static void TryResolveNormalUnitAddingParam()
		{
			if (_squadModel == null || (!_activeUnitId.HasValue && !_activeSlotId.HasValue))
			{
				return;
			}
			try
			{
				object obj = ReflectionUtil.TryInvokeObject(_squadModel, "getCurSquad");
				if (obj == null)
				{
					int? num = ReflectionUtil.TryGetInt(_squadModel, "selectLineIndex_");
					if (num.HasValue && num.Value >= 0)
					{
						obj = ReflectionUtil.TryGetListElement(ReflectionUtil.TryInvokeObject(_squadModel, "getSquadArray"), num.Value);
					}
				}
				object obj2 = ReflectionUtil.TryGetObject(obj, "unitArray_");
				int num2 = ResolveUnitIndex(obj2);
				int? num3 = ReflectionUtil.TryGetInt(_squadModel, "selectLineIndex_");
				if (num3.HasValue)
				{
					_activeSquadIndex = num3.Value;
				}
				int? num4 = ReflectionUtil.TryGetInt(obj, "heroIndex_");
				if (ReflectionUtil.TryGetBool(obj, "isHero_").GetValueOrDefault() && num4.HasValue && num4.Value >= 0)
				{
					_heroUnitIndexInSquad = num4.Value;
				}
				else
				{
					_heroUnitIndexInSquad = null;
				}
				if (_heroUnitIndexInSquad.HasValue && num2 == _heroUnitIndexInSquad.Value)
				{
					_activeIsHero = true;
					_activeIsNormalUnit = false;
					_activeHasUnitId = false;
					_activeUnitId = null;
					_lastAddingParamSource = null;
					_lastAddingParamTime = -1f;
					_lastActiveUnitForEquippedStats = null;
					return;
				}
				object obj3 = ReflectionUtil.TryGetListElement(obj2, num2);
				if (obj3 == null && _activeSlotId.HasValue)
				{
					obj3 = ReflectionUtil.TryGetListElement(obj2, _activeSlotId.Value);
				}
				object obj4 = ReflectionUtil.TryGetObject(obj3, "unitAddingParam_") ?? ReflectionUtil.TryInvokeObject(obj3, "getAddingParam");
				if (obj4 != null)
				{
					string text = ReflectionUtil.TryGetString(obj3, "name_");
					if (!string.IsNullOrEmpty(text))
					{
						_activeUnitName = text;
					}
					UpdateHoverAddingParam(obj4, "SquadModelUnit");
				}
			}
			catch (Exception ex)
			{
				if (Plugin.EnableDebugLogs)
				{
					Plugin.LogWarning("Resolve normal unit adding param failed: " + ex.Message);
				}
			}
		}

		private static int ResolveSquadSlotCount()
		{
			int? num = ReflectionUtil.TryGetStaticInt(Plugin.FindTypeByName("P2.Bases.HeadQuarters.NormalUnitModel") ?? Plugin.FindTypeByName("P2S.Bases.HeadQuarters.NormalUnitModel"), "slotNum_g");
			if (num.HasValue && num.Value > 0)
			{
				return num.Value;
			}
			return ReflectionUtil.TryGetStaticInt(Plugin.FindTypeByName("P2.Bases.HeadQuarters.HeroModel") ?? Plugin.FindTypeByName("P2S.Bases.HeadQuarters.HeroModel"), "slotNum_g").GetValueOrDefault();
		}

		private static int? ResolveConfirmedSelected(int? selectedItemId, int? equippedItemId)
		{
			if (selectedItemId.HasValue && selectedItemId.Value >= 0)
			{
				return selectedItemId;
			}
			if (equippedItemId.HasValue && equippedItemId.Value >= 0)
			{
				return equippedItemId;
			}
			return null;
		}
	}
	internal static class InputUtil
	{
		private struct INPUT
		{
			public int type;

			public InputUnion u;
		}

		[StructLayout(LayoutKind.Explicit)]
		private struct InputUnion
		{
			[FieldOffset(0)]
			public KEYBDINPUT ki;
		}

		private struct KEYBDINPUT
		{
			public ushort wVk;

			public ushort wScan;

			public uint dwFlags;

			public uint time;

			public IntPtr dwExtraInfo;
		}

		private const int INPUT_KEYBOARD = 1;

		private const uint KEYEVENTF_KEYUP = 2u;

		private const uint KEYEVENTF_SCANCODE = 8u;

		private const ushort VK_SPACE = 32;

		private const ushort SCAN_SPACE = 57;

		[DllImport("user32.dll", SetLastError = true)]
		private static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);

		[DllImport("user32.dll", SetLastError = true)]
		private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);

		internal static bool SendSpaceKey()
		{
			if (TrySendInputVkSpace())
			{
				Plugin.LogAutoSelect("SendSpaceKey via vk input succeeded");
				return true;
			}
			if (TrySendInputScanSpace())
			{
				Plugin.LogAutoSelect("SendSpaceKey via scan input succeeded");
				return true;
			}
			int lastWin32Error = Marshal.GetLastWin32Error();
			Plugin.LogWarning($"SendInput failed for space. LastError={lastWin32Error}");
			try
			{
				keybd_event(32, 57, 0u, UIntPtr.Zero);
				keybd_event(32, 57, 2u, UIntPtr.Zero);
				Plugin.LogAutoSelect("SendSpaceKey via keybd_event succeeded");
				return true;
			}
			catch (Exception ex)
			{
				Plugin.LogWarning("keybd_event failed for space: " + ex.Message);
				Plugin.LogAutoSelect("SendSpaceKey via keybd_event failed: " + ex.Message);
				return false;
			}
		}

		private static bool TrySendInputVkSpace()
		{
			INPUT[] array = new INPUT[2]
			{
				new INPUT
				{
					type = 1,
					u = new InputUnion
					{
						ki = new KEYBDINPUT
						{
							wVk = 32,
							wScan = 0,
							dwFlags = 0u,
							time = 0u,
							dwExtraInfo = IntPtr.Zero
						}
					}
				},
				new INPUT
				{
					type = 1,
					u = new InputUnion
					{
						ki = new KEYBDINPUT
						{
							wVk = 32,
							wScan = 0,
							dwFlags = 2u,
							time = 0u,
							dwExtraInfo = IntPtr.Zero
						}
					}
				}
			};
			return SendInput((uint)array.Length, array, Marshal.SizeOf<INPUT>()) == array.Length;
		}

		private static bool TrySendInputScanSpace()
		{
			INPUT[] array = new INPUT[2]
			{
				new INPUT
				{
					type = 1,
					u = new InputUnion
					{
						ki = new KEYBDINPUT
						{
							wVk = 0,
							wScan = 57,
							dwFlags = 8u,
							time = 0u,
							dwExtraInfo = IntPtr.Zero
						}
					}
				},
				new INPUT
				{
					type = 1,
					u = new InputUnion
					{
						ki = new KEYBDINPUT
						{
							wVk = 0,
							wScan = 57,
							dwFlags = 10u,
							time = 0u,
							dwExtraInfo = IntPtr.Zero
						}
					}
				}
			};
			return SendInput((uint)array.Length, array, Marshal.SizeOf<INPUT>()) == array.Length;
		}
	}
	public static class ItemStatResolver
	{
		private static Type _laboCommonType;

		private static Type _weaponParamType;

		private static MethodInfo _getInstanceMethod;

		private static MethodInfo _getWeaponParamMethod;

		private static MethodInfo _getDamageParamByIdsMethod;

		private static MethodInfo _getDamageParamByNameMethod;

		private static Dictionary<int, string> _equipNameById;

		private static bool _equipNameLoaded;

		public static string BuildStats(string title, int? itemId, object itemOperator)
		{
			if (!itemId.HasValue || itemId.Value < 0)
			{
				return title + "\nItem: -";
			}
			ItemStats itemStats = TryGetStats(itemId.Value, itemOperator);
			if (itemStats != null)
			{
				string value = FormatDamage(itemStats);
				string value2 = FormatFloat(itemStats.AttackSpeed) + " (wait " + FormatFloat(itemStats.AttackWait) + ")";
				string value3 = FormatTriplePercent(itemStats.CritRatio, itemStats.KnockbackRatio, itemStats.CncRatio);
				string value4 = FormatTriplePercent(itemStats.IgniteRatio, itemStats.FreezeRatio, itemStats.SleepRatio);
				string value5 = FormatTriplePercent(itemStats.ResistIgnite, itemStats.ResistFreeze, itemStats.ResistSleep);
				return $"{title}\nItem: {itemId}\nHP: {FormatFloat(itemStats.Hp)}\nDamage: {value}\nAtk Speed: {value2}\nCrit/KB/Cnc: {value3}\nIgn/Frz/Sleep: {value4}\nResist I/F/S: {value5}";
			}
			return $"{title}\nItem: {itemId}\nStats: -";
		}

		public static string BuildStatsFromDamageParam(string title, object damageParam, int? itemId)
		{
			if (damageParam == null)
			{
				return title + "\nItem: " + FormatItemId(itemId) + "\nStats: -";
			}
			ItemStats itemStats = BuildStatsFromDamageParam(damageParam);
			if (itemStats == null)
			{
				return title + "\nItem: " + FormatItemId(itemId) + "\nStats: -";
			}
			string value = FormatDamage(itemStats);
			string value2 = FormatFloat(itemStats.AttackSpeed) + " (wait " + FormatFloat(itemStats.AttackWait) + ")";
			string value3 = FormatTriplePercent(itemStats.CritRatio, itemStats.KnockbackRatio, itemStats.CncRatio);
			string value4 = FormatTriplePercent(itemStats.IgniteRatio, itemStats.FreezeRatio, itemStats.SleepRatio);
			string value5 = FormatTriplePercent(itemStats.ResistIgnite, itemStats.ResistFreeze, itemStats.ResistSleep);
			return $"{title}\nItem: {FormatItemId(itemId)}\nHP: {FormatFloat(itemStats.Hp)}\nDamage: {value}\nAtk Speed: {value2}\nCrit/KB/Cnc: {value3}\nIgn/Frz/Sleep: {value4}\nResist I/F/S: {value5}";
		}

		public static string BuildStatsDifference(string title, object autoDamageParam, object equippedDamageParam, int? autoItemId, int? equippedItemId)
		{
			if (autoDamageParam != null && equippedDamageParam != null)
			{
				ItemStats itemStats = BuildStatsFromDamageParam(autoDamageParam);
				ItemStats itemStats2 = BuildStatsFromDamageParam(equippedDamageParam);
				if (itemStats != null && itemStats2 != null)
				{
					string value = ColorizeSignedTokens(FormatSignedFloat(Diff(itemStats.Hp, itemStats2.Hp)));
					string value2 = ColorizeSignedTokens(FormatDamageDifference(itemStats, itemStats2));
					float? value3 = Diff(itemStats.AttackSpeed, itemStats2.AttackSpeed);
					float? value4 = Diff(itemStats.AttackWait, itemStats2.AttackWait);
					string text = ColorizeAttackSpeedSignedFloat(value3);
					string text2 = ColorizeAttackSpeedSignedFloat(value4);
					string value5 = text + " (wait " + text2 + ")";
					string value6 = ColorizeSignedTokens(FormatTripleSignedPercent(Diff(itemStats.CritRatio, itemStats2.CritRatio), Diff(itemStats.KnockbackRatio, itemStats2.KnockbackRatio), Diff(itemStats.CncRatio, itemStats2.CncRatio)));
					string value7 = ColorizeSignedTokens(FormatTripleSignedPercent(Diff(itemStats.IgniteRatio, itemStats2.IgniteRatio), Diff(itemStats.FreezeRatio, itemStats2.FreezeRatio), Diff(itemStats.SleepRatio, itemStats2.SleepRatio)));
					string value8 = ColorizeSignedTokens(FormatTripleSignedPercent(Diff(itemStats.ResistIgnite, itemStats2.ResistIgnite), Diff(itemStats.ResistFreeze, itemStats2.ResistFreeze), Diff(itemStats.ResistSleep, itemStats2.ResistSleep)));
					return $"{title}\nAuto item: {FormatItemId(autoItemId)}\nEquipped item: {FormatItemId(equippedItemId)}\nHP: {value}\nDamage: {value2}\nAtk Speed: {value5}\nCrit/KB/Cnc: {value6}\nIgn/Frz/Sleep: {value7}\nResist I/F/S: {value8}";
				}
				return $"{title}\nAuto item: {FormatItemId(autoItemId)}\nEquipped item: {FormatItemId(equippedItemId)}\nStats: -";
			}
			return $"{title}\nAuto item: {FormatItemId(autoItemId)}\nEquipped item: {FormatItemId(equippedItemId)}\nStats: -";
		}

		public static string[] BuildStatsDifferenceNumberLines(object autoDamageParam, object equippedDamageParam, int? autoItemId, int? equippedItemId)
		{
			if (autoDamageParam != null && equippedDamageParam != null)
			{
				ItemStats itemStats = BuildStatsFromDamageParam(autoDamageParam);
				ItemStats itemStats2 = BuildStatsFromDamageParam(equippedDamageParam);
				if (itemStats != null && itemStats2 != null)
				{
					string text = ColorizeSignedTokens(FormatSignedFloat(Diff(itemStats.Hp, itemStats2.Hp)));
					string text2 = ColorizeSignedTokens(FormatDamageDifference(itemStats, itemStats2));
					string text3 = ColorizeAttackSpeedSignedFloat(Diff(itemStats.AttackWait, itemStats2.AttackWait));
					string text4 = ColorizeSignedTokens(FormatSignedPercent(Diff(itemStats.CritRatio, itemStats2.CritRatio)));
					string text5 = ColorizeSignedTokens(FormatSignedPercent(Diff(itemStats.KnockbackRatio, itemStats2.KnockbackRatio)));
					string text6 = ColorizeSignedTokens(FormatSignedPercent(Diff(itemStats.CncRatio, itemStats2.CncRatio)));
					string text7 = ColorizeSignedTokens(FormatSignedPercent(Diff(itemStats.IgniteRatio, itemStats2.IgniteRatio)));
					string text8 = ColorizeSignedTokens(FormatSignedPercent(Diff(itemStats.FreezeRatio, itemStats2.FreezeRatio)));
					string text9 = ColorizeSignedTokens(FormatSignedPercent(Diff(itemStats.SleepRatio, itemStats2.SleepRatio)));
					string text10 = ColorizeSignedTokens(FormatSignedPercent(Diff(itemStats.ResistIgnite, itemStats2.ResistIgnite)));
					string text11 = ColorizeSignedTokens(FormatSignedPercent(Diff(itemStats.ResistFreeze, itemStats2.ResistFreeze)));
					string text12 = ColorizeSignedTokens(FormatSignedPercent(Diff(itemStats.ResistSleep, itemStats2.ResistSleep)));
					return new string[12]
					{
						text, text2, text3, text10, text11, text12, text4, text5, text6, text7,
						text8, text9
					};
				}
				return new string[12]
				{
					"-", "-", "-", "-", "-", "-", "-", "-", "-", "-",
					"-", "-"
				};
			}
			return new string[12]
			{
				"-", "-", "-", "-", "-", "-", "-", "-", "-", "-",
				"-", "-"
			};
		}

		public static string TryGetEquipName(int itemId)
		{
			return TryGetEquipNameByItemId(itemId);
		}

		public static string BuildStatsSummary(object damageParam)
		{
			if (damageParam == null)
			{
				return "-";
			}
			ItemStats itemStats = BuildStatsFromDamageParam(damageParam);
			if (itemStats == null)
			{
				return "-";
			}
			return $"HP={FormatFloat(itemStats.Hp)} Dmg={FormatDamage(itemStats)} AtkSpd={FormatFloat(itemStats.AttackSpeed)} Crit={FormatPercent(itemStats.CritRatio)} KB={FormatPercent(itemStats.KnockbackRatio)} Cnc={FormatPercent(itemStats.CncRatio)}";
		}

		private static ItemStats TryGetStats(int itemId, object itemOperator)
		{
			if (itemOperator == null)
			{
				return null;
			}
			object obj = ReflectionUtil.TryInvokeObject(itemOperator, "getItemParam", itemId);
			if (obj == null)
			{
				return null;
			}
			string text = ReflectionUtil.TryGetString(obj, "modelName");
			string text2 = ReflectionUtil.TryGetString(obj, "userString");
			string text3 = ReflectionUtil.TryGetString(obj, "iconModelName");
			string text4 = TryGetEquipNameByItemId(itemId);
			object obj2 = TryGetWeaponParamForItem(itemId, text4, text2, text, text3);
			object obj3 = null;
			if (obj2 != null)
			{
				object obj4 = ReflectionUtil.TryGetObject(obj2, "statusRefParam");
				int? categoryId = ReflectionUtil.TryGetInt(obj4, "categoryId");
				int? paramId = ReflectionUtil.TryGetInt(obj4, "paramId");
				obj3 = TryGetDamageParamByIds(categoryId, paramId);
			}
			if (obj3 == null)
			{
				if (Plugin.EnableDebugLogs)
				{
					Plugin.LogInfo($"Stats not found for itemId={itemId} modelName={text} userString={text2} iconModelName={text3}");
				}
				return null;
			}
			return BuildStatsFromDamageParam(obj3);
		}

		private static ItemStats BuildStatsFromDamageParam(object damageParam)
		{
			ItemStats itemStats = new ItemStats();
			object obj = ReflectionUtil.TryGetObject(damageParam, "attackBase");
			itemStats.Hp = ReflectionUtil.TryGetFloat(obj, "hitPoint");
			itemStats.AttackWait = ReflectionUtil.TryGetFloat(obj, "attackWait");
			if (itemStats.AttackWait.HasValue && itemStats.AttackWait.Value > 0.0001f)
			{
				itemStats.AttackSpeed = 1f / itemStats.AttackWait.Value;
			}
			object obj2 = ReflectionUtil.TryGetObject(damageParam, "attackCalc");
			itemStats.MinDamage = ReflectionUtil.TryGetFloat(obj2, "minBaseDamage");
			itemStats.MaxDamage = ReflectionUtil.TryGetFloat(obj2, "maxBaseDamage");
			itemStats.AttackPower = ReflectionUtil.TryGetFloat(obj2, "attackNBPower");
			object ratioObj = ReflectionUtil.TryGetObject(damageParam, "attackRatio");
			itemStats.CritRatio = GetRatio(ratioObj, 0);
			itemStats.KnockbackRatio = GetRatio(ratioObj, 1);
			itemStats.CncRatio = GetRatio(ratioObj, 2);
			itemStats.IgniteRatio = GetRatio(ratioObj, 3);
			itemStats.SleepRatio = GetRatio(ratioObj, 4);
			itemStats.FreezeRatio = GetRatio(ratioObj, 5);
			object ratioObj2 = ReflectionUtil.TryGetObject(damageParam, "sufferAvoidRatio");
			itemStats.ResistIgnite = GetRatio(ratioObj2, 3);
			itemStats.ResistSleep = GetRatio(ratioObj2, 4);
			itemStats.ResistFreeze = GetRatio(ratioObj2, 5);
			return itemStats;
		}

		private static object TryGetWeaponParamForItem(int itemId, params string[] names)
		{
			object obj = null;
			if (names == null || names.Length == 0)
			{
				return null;
			}
			foreach (string text in names)
			{
				if (string.IsNullOrEmpty(text))
				{
					continue;
				}
				object obj2 = TryGetWeaponParam(text);
				if (obj2 != null)
				{
					if (MatchesItemId(obj2, itemId))
					{
						return obj2;
					}
					if (obj == null)
					{
						obj = obj2;
					}
				}
			}
			return obj;
		}

		private static bool MatchesItemId(object weaponParam, int itemId)
		{
			int? num = ReflectionUtil.TryGetInt(weaponParam, "itemId");
			if (num.HasValue)
			{
				return num.Value == itemId;
			}
			return false;
		}

		private static float? GetRatio(object ratioObj, int index)
		{
			if (ratioObj == null)
			{
				return null;
			}
			return ReflectionUtil.TryGetArrayElementFloat(ReflectionUtil.TryGetObject(ratioObj, "ratio"), index);
		}

		private static object TryGetWeaponParam(string resourceName)
		{
			if (string.IsNullOrEmpty(resourceName))
			{
				return null;
			}
			EnsureLaboCommon();
			if (_laboCommonType == null || _weaponParamType == null || _getInstanceMethod == null || _getWeaponParamMethod == null)
			{
				return null;
			}
			object obj = _getInstanceMethod.Invoke(null, null);
			if (obj == null)
			{
				return null;
			}
			try
			{
				return _getWeaponParamMethod.MakeGenericMethod(_weaponParamType).Invoke(obj, new object[1] { resourceName });
			}
			catch
			{
				return null;
			}
		}

		private static string TryGetEquipNameByItemId(int itemId)
		{
			EnsureLaboCommon();
			if (_laboCommonType == null)
			{
				return null;
			}
			if (!_equipNameLoaded)
			{
				_equipNameLoaded = true;
				_equipNameById = new Dictionary<int, string>();
				string[] array = new string[9] { "equipParamName1", "equipParamName2", "equipParamName3", "equipParamName4", "equipParamName5", "equipParamName6", "equipParamName7", "equipParamName8", "equipParamName9" };
				foreach (string name in array)
				{
					FieldInfo field = _laboCommonType.GetField(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					if (field == null)
					{
						continue;
					}
					object obj = null;
					try
					{
						obj = field.GetValue(null);
					}
					catch
					{
					}
					if (obj == null)
					{
						continue;
					}
					int? num = ReflectionUtil.TryGetCollectionLength(obj);
					if (!num.HasValue)
					{
						continue;
					}
					for (int j = 0; j < num.Value; j++)
					{
						object obj3 = ReflectionUtil.TryGetListElement(obj, j);
						if (obj3 != null)
						{
							int? num2 = ReflectionUtil.TryGetInt(obj3, "id");
							string value = ReflectionUtil.TryGetString(obj3, "str");
							if (num2.HasValue && num2.Value >= 0 && !string.IsNullOrEmpty(value) && !_equipNameById.ContainsKey(num2.Value))
							{
								_equipNameById[num2.Value] = value;
							}
						}
					}
				}
			}
			if (_equipNameById != null && _equipNameById.TryGetValue(itemId, out var value2))
			{
				return value2;
			}
			return null;
		}

		private static object TryGetDamageParamByIds(int? categoryId, int? paramId)
		{
			if (!categoryId.HasValue || !paramId.HasValue)
			{
				return null;
			}
			EnsureLaboCommon();
			if (_getDamageParamByIdsMethod == null)
			{
				return null;
			}
			return _getDamageParamByIdsMethod.Invoke(null, new object[2]
			{
				(uint)categoryId.Value,
				(uint)paramId.Value
			});
		}

		private static object TryGetDamageParamByName(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return null;
			}
			EnsureLaboCommon();
			if (_getDamageParamByNameMethod == null)
			{
				return null;
			}
			return _getDamageParamByNameMethod.Invoke(null, new object[1] { name });
		}

		private static void EnsureLaboCommon()
		{
			if (_laboCommonType == null)
			{
				_laboCommonType = Plugin.FindTypeByName("P2.LaboCommon") ?? Plugin.FindTypeByName("P2S.LaboCommon");
			}
			if (_weaponParamType == null)
			{
				_weaponParamType = Plugin.FindTypeByName("P2.Game.Unit.WeaponParam.BaseParam") ?? Plugin.FindTypeByName("P2S.Game.Unit.WeaponParam.BaseParam");
			}
			if (!(_laboCommonType == null))
			{
				if ((object)_getInstanceMethod == null)
				{
					_getInstanceMethod = _laboCommonType.GetMethod("getInstance", BindingFlags.Static | BindingFlags.Public);
				}
				if ((object)_getWeaponParamMethod == null)
				{
					_getWeaponParamMethod = _laboCommonType.GetMethod("getWeaponParam", BindingFlags.Instance | BindingFlags.Public);
				}
				if ((object)_getDamageParamByIdsMethod == null)
				{
					_getDamageParamByIdsMethod = _laboCommonType.GetMethod("getDamageParam_List", BindingFlags.Static | BindingFlags.Public, null, new Type[2]
					{
						typeof(uint),
						typeof(uint)
					}, null);
				}
				if ((object)_getDamageParamByNameMethod == null)
				{
					_getDamageParamByNameMethod = _laboCommonType.GetMethod("getDamageParam_Param", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null) ?? _laboCommonType.GetMethod("getDamageParam_List", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null);
				}
			}
		}

		private static string FormatDamage(ItemStats stats)
		{
			string text = "-";
			if (stats.MinDamage.HasValue || stats.MaxDamage.HasValue)
			{
				float? value = stats.MinDamage ?? stats.MaxDamage;
				float? value2 = stats.MaxDamage ?? stats.MinDamage;
				if (value.HasValue && value2.HasValue)
				{
					text = ((Math.Abs(value.Value - value2.Value) < 0.001f) ? FormatFloat(value) : (FormatFloat(value) + "-" + FormatFloat(value2)));
				}
			}
			if (text == "-" && stats.AttackPower.HasValue)
			{
				text = FormatFloat(stats.AttackPower);
			}
			return text;
		}

		private static string FormatDamageDifference(ItemStats autoStats, ItemStats equippedStats)
		{
			float? num = Diff(autoStats.MinDamage, equippedStats.MinDamage);
			float? num2 = Diff(autoStats.MaxDamage, equippedStats.MaxDamage);
			string text = "-";
			if (num.HasValue || num2.HasValue)
			{
				float? value = num ?? num2;
				float? value2 = num2 ?? num;
				if (value.HasValue && value2.HasValue)
				{
					text = ((Math.Abs(value.Value - value2.Value) < 0.001f) ? FormatSignedFloat(value) : (FormatSignedFloat(value) + "-" + FormatSignedFloat(value2)));
				}
			}
			float? value3 = Diff(autoStats.AttackPower, equippedStats.AttackPower);
			if (text == "-" && value3.HasValue)
			{
				text = FormatSignedFloat(value3);
			}
			return text;
		}

		private static string FormatFloat(float? value)
		{
			if (!value.HasValue)
			{
				return "-";
			}
			return value.Value.ToString("0.##");
		}

		private static string FormatItemId(int? itemId)
		{
			if (!itemId.HasValue || itemId.Value < 0)
			{
				return "-";
			}
			return itemId.Value.ToString();
		}

		private static string FormatPercent(float? ratio)
		{
			if (!ratio.HasValue)
			{
				return "-";
			}
			return (ratio.Value * 100f).ToString("0.#") + "%";
		}

		private static float? Diff(float? left, float? right)
		{
			if (!left.HasValue || !right.HasValue)
			{
				return null;
			}
			return left.Value - right.Value;
		}

		private static string FormatSignedFloat(float? value)
		{
			if (!value.HasValue)
			{
				return "-";
			}
			float value2 = value.Value;
			if (Math.Abs(value2) < 0.0001f)
			{
				return "0";
			}
			string text = Math.Abs(value2).ToString("0.##");
			if (!(value2 > 0f))
			{
				return "-" + text;
			}
			return "+" + text;
		}

		private static string ColorizeAttackSpeedSignedFloat(float? value)
		{
			if (!value.HasValue)
			{
				return "-";
			}
			float value2 = value.Value;
			if (Math.Abs(value2) < 0.0001f)
			{
				return "0";
			}
			string value3 = Math.Abs(value2).ToString("0.##");
			string value4 = ((value2 > 0f) ? "#ff6666" : "#55ff55");
			return $"<color={value4}>{((value2 > 0f) ? "+" : "-")}{value3}</color>";
		}

		private static string FormatSignedPercent(float? ratio)
		{
			if (!ratio.HasValue)
			{
				return "-";
			}
			float num = ratio.Value * 100f;
			if (Math.Abs(num) < 0.0001f)
			{
				return "0%";
			}
			string text = Math.Abs(num).ToString("0.#") + "%";
			if (!(num > 0f))
			{
				return "-" + text;
			}
			return "+" + text;
		}

		private static string FormatTripleSignedPercent(float? a, float? b, float? c)
		{
			return $"{FormatSignedPercent(a)}/{FormatSignedPercent(b)}/{FormatSignedPercent(c)}";
		}

		private static string ColorizeSignedTokens(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return text;
			}
			StringBuilder stringBuilder = new StringBuilder(text.Length + 16);
			int i = 0;
			while (i < text.Length)
			{
				char c = text[i];
				if ((c == '+' || c == '-') && i + 1 < text.Length && (char.IsDigit(text[i + 1]) || text[i + 1] == '.'))
				{
					int num = i;
					for (i++; i < text.Length; i++)
					{
						char c2 = text[i];
						if (!char.IsDigit(c2) && c2 != '.' && c2 != ',' && c2 != '%')
						{
							break;
						}
					}
					string text2 = text.Substring(num, i - num);
					string value = ((text2[0] == '+') ? "#55ff55" : "#ff6666");
					stringBuilder.Append("<color=").Append(value).Append(">")
						.Append(text2)
						.Append("</color>");
				}
				else
				{
					stringBuilder.Append(c);
					i++;
				}
			}
			return stringBuilder.ToString();
		}

		private static string FormatTriplePercent(float? a, float? b, float? c)
		{
			return $"{FormatPercent(a)}/{FormatPercent(b)}/{FormatPercent(c)}";
		}
	}
	public sealed class ItemStats
	{
		public float? Hp;

		public float? MinDamage;

		public float? MaxDamage;

		public float? AttackPower;

		public float? AttackWait;

		public float? AttackSpeed;

		public float? CritRatio;

		public float? KnockbackRatio;

		public float? CncRatio;

		public float? IgniteRatio;

		public float? FreezeRatio;

		public float? SleepRatio;

		public float? ResistIgnite;

		public float? ResistFreeze;

		public float? ResistSleep;
	}
	public static class OverlayUi
	{
		private static GameObject _root;

		private static Text _text;

		private static Text _equippedText;

		private static Text _autoSelectedText;

		private static Text _differenceText;

		private static GameObject _autoSelectedPanel;

		private static GameObject _differencePanel;

		private static string _content = string.Empty;

		private static string _equippedContent = string.Empty;

		private static string _autoSelectedContent = string.Empty;

		private static string _differenceContent = string.Empty;

		public static void Ensure()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: 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_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.IsDebugBuild && !((Object)(object)_root != (Object)null))
			{
				_root = new GameObject("BetterStatsOverlay");
				Object.DontDestroyOnLoad((Object)(object)_root);
				Canvas obj = _root.AddComponent<Canvas>();
				obj.renderMode = (RenderMode)0;
				obj.sortingOrder = 9999;
				CanvasScaler obj2 = _root.AddComponent<CanvasScaler>();
				obj2.uiScaleMode = (ScaleMode)1;
				obj2.referenceResolution = new Vector2(1920f, 1080f);
				obj2.matchWidthOrHeight = 0.5f;
				_root.AddComponent<GraphicRaycaster>();
				_text = CreateText("MainText", new Vector2(20f, 50f), new Vector2(420f, 240f));
				_equippedText = CreatePanelWithText("EquippedStatsPanel", "EquippedStatsText", new Vector2(460f, 50f), new Vector2(360f, 240f));
				_autoSelectedText = CreatePanelWithText("AutoSelectedStatsPanel", "AutoSelectedStatsText", new Vector2(840f, 50f), new Vector2(360f, 240f));
				_differenceText = CreatePanelWithText("DifferenceStatsPanel", "DifferenceStatsText", new Vector2(1220f, 50f), new Vector2(360f, 240f));
				_autoSelectedPanel = (((Object)(object)_autoSelectedText != (Object)null) ? ((Component)((Component)_autoSelectedText).transform.parent).gameObject : null);
				_differencePanel = (((Object)(object)_differenceText != (Object)null) ? ((Component)((Component)_differenceText).transform.parent).gameObject : null);
				_text.text = _content;
				_equippedText.text = _equippedContent;
				_autoSelectedText.text = _autoSelectedContent;
				_differenceText.text = _differenceContent;
			}
		}

		public static void SetText(string text)
		{
			_content = text ?? string.Empty;
			if ((Object)(object)_text == (Object)null)
			{
				Ensure();
			}
			if ((Object)(object)_text != (Object)null)
			{
				_text.text = _content;
			}
		}

		public static void SetStatsText(string equipped, string autoSelected, string difference)
		{
			_equippedContent = equipped ?? string.Empty;
			_autoSelectedContent = autoSelected ?? string.Empty;
			_differenceContent = difference ?? string.Empty;
			if ((Object)(object)_equippedText == (Object)null || (Object)(object)_autoSelectedText == (Object)null || (Object)(object)_differenceText == (Object)null)
			{
				Ensure();
			}
			if ((Object)(object)_equippedText != (Object)null)
			{
				_equippedText.text = _equippedContent;
			}
			if ((Object)(object)_autoSelectedText != (Object)null)
			{
				_autoSelectedText.text = _autoSelectedContent;
			}
			if ((Object)(object)_differenceText != (Object)null)
			{
				_differenceText.text = _differenceContent;
			}
		}

		public static void SetAutoSelectedVisible(bool visible)
		{
			if ((Object)(object)_autoSelectedPanel == (Object)null)
			{
				Ensure();
			}
			if ((Object)(object)_autoSelectedPanel != (Object)null)
			{
				_autoSelectedPanel.SetActive(visible);
			}
			if ((Object)(object)_differencePanel != (Object)null)
			{
				_differencePanel.SetActive(visible);
			}
		}

		private static Text CreateText(string name, Vector2 position, Vector2 size)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.transform.SetParent(_root.transform, false);
			Text obj = val.AddComponent<Text>();
			obj.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
			obj.fontSize = 22;
			obj.alignment = (TextAnchor)0;
			((Graphic)obj).color = new Color(1f, 1f, 1f, 0.95f);
			obj.supportRichText = true;
			RectTransform rectTransform = ((Graphic)obj).rectTransform;
			rectTransform.anchorMin = new Vector2(0f, 0f);
			rectTransform.anchorMax = new Vector2(0f, 0f);
			rectTransform.pivot = new Vector2(0f, 0f);
			rectTransform.anchoredPosition = position;
			rectTransform.sizeDelta = size;
			return obj;
		}

		private static Text CreatePanelWithText(string panelName, string textName, Vector2 position, Vector2 size)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: 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_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: 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_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(panelName);
			val.transform.SetParent(_root.transform, false);
			((Graphic)val.AddComponent<Image>()).color = new Color(0f, 0f, 0f, 0.7f);
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0f, 0f);
			component.anchorMax = new Vector2(0f, 0f);
			component.pivot = new Vector2(0f, 0f);
			component.anchoredPosition = position;
			component.sizeDelta = size;
			GameObject val2 = new GameObject(textName);
			val2.transform.SetParent(val.transform, false);
			Text obj = val2.AddComponent<Text>();
			obj.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
			obj.fontSize = 22;
			obj.alignment = (TextAnchor)0;
			((Graphic)obj).color = new Color(1f, 1f, 1f, 0.95f);
			obj.supportRichText = true;
			RectTransform rectTransform = ((Graphic)obj).rectTransform;
			rectTransform.anchorMin = new Vector2(0f, 0f);
			rectTransform.anchorMax = new Vector2(1f, 1f);
			rectTransform.pivot = new Vector2(0f, 0f);
			rectTransform.offsetMin = new Vector2(8f, 8f);
			rectTransform.offsetMax = new Vector2(-8f, -8f);
			return obj;
		}
	}
	public static class ReleaseOverlayUi
	{
		private const int LineCount = 12;

		private static readonly string[] DefaultLines = new string[12]
		{
			"-", "-", "-", "-", "-", "-", "-", "-", "-", "-",
			"-", "-"
		};

		private static GameObject _root;

		private static Text[] _texts;

		private static string[] _content = Array.Empty<string>();

		private static bool _visible;

		private static bool IsReady()
		{
			if ((Object)(object)_root == (Object)null)
			{
				return false;
			}
			if (_texts == null || _texts.Length == 0)
			{
				return false;
			}
			for (int i = 0; i < _texts.Length; i++)
			{
				if ((Object)(object)_texts[i] == (Object)null)
				{
					return false;
				}
			}
			return true;
		}

		public static void SetVisible(bool visible)
		{
			if (_visible == visible)
			{
				return;
			}
			_visible = visible;
			if (!_visible)
			{
				if ((Object)(object)_root != (Object)null)
				{
					_root.SetActive(false);
				}
				return;
			}
			Ensure();
			if ((Object)(object)_root != (Object)null)
			{
				_root.SetActive(true);
			}
			UpdateTexts();
		}

		public static void Ensure()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: 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_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Expected O, but got Unknown
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			//IL_024d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			//IL_0277: Unknown result type (might be due to invalid IL or missing references)
			//IL_0282: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0327: Unknown result type (might be due to invalid IL or missing references)
			//IL_0353: Unknown result type (might be due to invalid IL or missing references)
			//IL_0367: Unknown result type (might be due to invalid IL or missing references)
			//IL_0383: Unknown result type (might be due to invalid IL or missing references)
			//IL_0398: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c9: Unknown result type (might be due to invalid IL or missing references)
			if (_visible && !IsReady())
			{
				if ((Object)(object)_root != (Object)null)
				{
					Object.Destroy((Object)(object)_root);
					_root = null;
				}
				_root = new GameObject("BetterStatsReleaseOverlay");
				Object.DontDestroyOnLoad((Object)(object)_root);
				Canvas obj = _root.AddComponent<Canvas>();
				obj.renderMode = (RenderMode)0;
				obj.sortingOrder = 9998;
				CanvasScaler obj2 = _root.AddComponent<CanvasScaler>();
				obj2.uiScaleMode = (ScaleMode)1;
				obj2.referenceResolution = new Vector2(1920f, 1080f);
				obj2.matchWidthOrHeight = 0.5f;
				_root.AddComponent<GraphicRaycaster>();
				_texts = (Text[])(object)new Text[12];
				Vector2[] array = (Vector2[])(object)new Vector2[12]
				{
					new Vector2(-480f, 250f),
					new Vector2(-480f, 200f),
					new Vector2(-480f, 150f),
					new Vector2(-480f, 100f),
					new Vector2(-480f, 50f),
					new Vector2(-480f, 0f),
					new Vector2(35f, 250f),
					new Vector2(35f, 200f),
					new Vector2(35f, 150f),
					new Vector2(35f, 100f),
					new Vector2(35f, 50f),
					new Vector2(35f, 0f)
				};
				for (int i = 0; i < 12; i++)
				{
					Vector2 anchoredPosition = ((i < array.Length) ? array[i] : Vector2.zero);
					GameObject val = new GameObject($"ReleaseDifferencePanel{i}");
					val.transform.SetParent(_root.transform, false);
					((Graphic)val.AddComponent<Image>()).color = new Color(0f, 0f, 0f, 0.5f);
					RectTransform component = val.GetComponent<RectTransform>();
					component.anchorMin = new Vector2(0.5f, 0.5f);
					component.anchorMax = new Vector2(0.5f, 0.5f);
					component.pivot = new Vector2(0.5f, 0.5f);
					component.anchoredPosition = anchoredPosition;
					float num = ((i == 1) ? 100f : 80f);
					component.sizeDelta = new Vector2(num, 32f);
					GameObject val2 = new GameObject($"ReleaseDifferenceLine{i}");
					val2.transform.SetParent(val.transform, false);
					Text val3 = val2.AddComponent<Text>();
					val3.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
					val3.fontSize = 26;
					val3.alignment = (TextAnchor)4;
					((Graphic)val3).color = new Color(1f, 1f, 1f, 0.98f);
					val3.supportRichText = true;
					Shadow obj3 = val2.AddComponent<Shadow>();
					obj3.effectColor = new Color(0f, 0f, 0f, 0.9f);
					obj3.effectDistance = new Vector2(1f, -1f);
					RectTransform rectTransform = ((Graphic)val3).rectTransform;
					rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
					rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
					rectTransform.pivot = new Vector2(0.5f, 0.5f);
					rectTransform.anchoredPosition = Vector2.zero;
					rectTransform.sizeDelta = new Vector2(num, 32f);
					_texts[i] = val3;
				}
			}
		}

		public static void SetText(string[] lines)
		{
			_content = lines ?? Array.Empty<string>();
			if (_visible)
			{
				Ensure();
				UpdateTexts();
			}
		}

		private static void UpdateTexts()
		{
			if (IsReady() && _texts != null)
			{
				string[] array = _content;
				if (array.Length == 0)
				{
					array = DefaultLines;
				}
				int num = Math.Min(_texts.Length, array.Length);
				for (int i = 0; i < num; i++)
				{
					_texts[i].text = array[i];
				}
				for (int j = num; j < _texts.Length; j++)
				{
					_texts[j].text = string.Empty;
				}
			}
		}
	}
	public static class ReflectionUtil
	{
		public static int? TryGetInt(object obj, string name)
		{
			if (obj == null || string.IsNullOrEmpty(name))
			{
				return null;
			}
			Type type = obj.GetType();
			FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				try
				{
					return Convert.ToInt32(field.GetValue(obj));
				}
				catch
				{
				}
			}
			PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null && property.GetIndexParameters().Length == 0)
			{
				try
				{
					return Convert.ToInt32(property.GetValue(obj, null));
				}
				catch
				{
				}
			}
			return null;
		}

		public static float? TryGetFloat(object obj, string name)
		{
			if (obj == null || string.IsNullOrEmpty(name))
			{
				return null;
			}
			Type type = obj.GetType();
			FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				try
				{
					return Convert.ToSingle(field.GetValue(obj));
				}
				catch
				{
				}
			}
			PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null && property.GetIndexParameters().Length == 0)
			{
				try
				{
					return Convert.ToSingle(property.GetValue(obj, null));
				}
				catch
				{
				}
			}
			return null;
		}

		public static string TryGetString(object obj, string name)
		{
			if (obj == null || string.IsNullOrEmpty(name))
			{
				return null;
			}
			Type type = obj.GetType();
			FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				try
				{
					return Convert.ToString(field.GetValue(obj));
				}
				catch
				{
				}
			}
			PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null && property.GetIndexParameters().Length == 0)
			{
				try
				{
					return Convert.ToString(property.GetValue(obj, null));
				}
				catch
				{
				}
			}
			return null;
		}

		public static bool? TryGetBool(object obj, string name)
		{
			if (obj == null || string.IsNullOrEmpty(name))
			{
				return null;
			}
			Type type = obj.GetType();
			FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				try
				{
					return Convert.ToBoolean(field.GetValue(obj));
				}
				catch
				{
				}
			}
			PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null && property.GetIndexParameters().Length == 0)
			{
				try
				{
					return Convert.ToBoolean(property.GetValue(obj, null));
				}
				catch
				{
				}
			}
			return null;
		}

		public static int? TryGetStaticInt(Type type, string name)
		{
			if (type == null || string.IsNullOrEmpty(name))
			{
				return null;
			}
			FieldInfo field = type.GetField(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				try
				{
					return Convert.ToInt32(field.GetValue(null));
				}
				catch
				{
				}
			}
			PropertyInfo property = type.GetProperty(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null && property.GetIndexParameters().Length == 0)
			{
				try
				{
					return Convert.ToInt32(property.GetValue(null, null));
				}
				catch
				{
				}
			}
			return null;
		}

		public static int TryGetListCount(object listObj)
		{
			if (listObj == null)
			{
				return 0;
			}
			if (listObj is IList list)
			{
				return list.Count;
			}
			Type type = listObj.GetType();
			PropertyInfo property = type.GetProperty("Count", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null)
			{
				try
				{
					return Convert.ToInt32(property.GetValue(listObj, null));
				}
				catch
				{
				}
			}
			MethodInfo method = type.GetMethod("get_Count", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (method != null)
			{
				try
				{
					return Convert.ToInt32(method.Invoke(listObj, null));
				}
				catch
				{
				}
			}
			return 0;
		}

		public static bool? TryInvokeBool(object obj, string name, params object[] args)
		{
			if (obj == null || string.IsNullOrEmpty(name))
			{
				return null;
			}
			Type type = obj.GetType();
			MethodInfo methodInfo = null;
			MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo2 in methods)
			{
				if (string.Equals(methodInfo2.Name, name, StringComparison.Ordinal) && !(methodInfo2.ReturnType != typeof(bool)) && methodInfo2.GetParameters().Length == args.Length)
				{
					methodInfo = methodInfo2;
					break;
				}
			}
			if (methodInfo == null)
			{
				return null;
			}
			try
			{
				object obj2 = methodInfo.Invoke(obj, args);
				bool flag = default(bool);
				int num;
				if (obj2 is bool)
				{
					flag = (bool)obj2;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch
			{
				return null;
			}
		}

		public static object TryGetObject(object obj, string name)
		{
			if (obj == null || string.IsNullOrEmpty(name))
			{
				return null;
			}
			Type type = obj.GetType();
			FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				try
				{
					return field.GetValue(obj);
				}
				catch
				{
				}
			}