Decompiled source of InfoOverhaul v1.0.0

InfoOverhaul.dll

Decompiled 3 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using InfoOverhaul.Compat;
using InfoOverhaul.Delta;
using InfoOverhaul.Stats;
using InfoOverhaul.UI;
using Microsoft.CodeAnalysis;
using TMPro;
using UnboundLib.GameModes;
using UnboundLib.Utils;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace InfoOverhaul
{
	internal static class IOLog
	{
		private static ManualLogSource _logger;

		internal static void Init(ManualLogSource logger)
		{
			_logger = logger;
		}

		internal static void Section(string title)
		{
			ManualLogSource logger = _logger;
			if (logger != null)
			{
				logger.LogInfo((object)("── " + title + " ──"));
			}
		}

		internal static void Line(string message)
		{
			ManualLogSource logger = _logger;
			if (logger != null)
			{
				logger.LogInfo((object)message);
			}
		}

		internal static void Warn(string message)
		{
			ManualLogSource logger = _logger;
			if (logger != null)
			{
				logger.LogWarning((object)message);
			}
		}

		internal static void Error(string message)
		{
			ManualLogSource logger = _logger;
			if (logger != null)
			{
				logger.LogError((object)message);
			}
		}
	}
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("InfoOverhaul", "Info Overhaul", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Logger;

		private void Awake()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			Logger = ((BaseUnityPlugin)this).Logger;
			IOLog.Init(Logger);
			IOLog.Section("Plugin Awake");
			new Harmony("InfoOverhaul").PatchAll();
			StatTemplateDeltaRegistrar.Init();
			MfmTier2Compat.Init();
			((Component)this).gameObject.AddComponent<PickStatsController>();
			((Component)this).gameObject.AddComponent<CardDeltaPreviewOverlay>();
			IOLog.Line("Plugin InfoOverhaul loaded.");
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "InfoOverhaul";

		public const string PLUGIN_NAME = "Info Overhaul";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}
namespace InfoOverhaul.UI
{
	public class CardDeltaPreviewOverlay : MonoBehaviour
	{
		[CompilerGenerated]
		private sealed class <OnPickSequenceEnd>d__19 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public CardDeltaPreviewOverlay <>4__this;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <OnPickSequenceEnd>d__19(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				int num = <>1__state;
				CardDeltaPreviewOverlay cardDeltaPreviewOverlay = <>4__this;
				if (num != 0)
				{
					return false;
				}
				<>1__state = -1;
				cardDeltaPreviewOverlay.OnPickSequenceEnded();
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		public static CardDeltaPreviewOverlay instance;

		private static readonly FieldInfo SpawnedCardsField = AccessTools.Field(typeof(CardChoice), "spawnedCards");

		private static readonly FieldInfo SelectedCardField = AccessTools.Field(typeof(CardChoice), "currentlySelectedCard");

		private const int RowsPerCol = 3;

		private const int MaxCols = 5;

		private Canvas _canvas;

		private TextMeshProUGUI _headerText;

		private TextMeshProUGUI[] _colTexts;

		private bool _pickSequenceActive;

		private void Awake()
		{
			instance = this;
			BuildUI();
			GameModeManager.AddHook("PickEnd", (Func<IGameModeHandler, IEnumerator>)OnPickSequenceEnd);
			IOLog.Section("CardDeltaPreviewOverlay initialized");
		}

		private void BuildUI()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: 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_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: 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_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: 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_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			_canvas = UIHelper.CreateFullscreenCanvas("IO_CardDeltaPreview", 55);
			Transform transform = ((Component)_canvas).transform;
			Vector2 anchorMin = new Vector2(0.18f, 0.085f);
			Vector2 anchorMax = new Vector2(0.82f, 0.22f);
			Color? bg = new Color(0.04f, 0.06f, 0.1f, 0.92f);
			RectTransform parent = UIHelper.CreatePanel(transform, "DeltaPanel", anchorMin, anchorMax, default(Vector2), default(Vector2), bg);
			RectTransform parent2 = UIHelper.CreatePanel((Transform)(object)parent, "Header", new Vector2(0.01f, 0.72f), new Vector2(0.99f, 0.98f));
			_headerText = UIHelper.CreateText((Transform)(object)parent2, "HeaderText", "Card preview", 20, (TextAlignmentOptions)4097, (Color?)new Color(0.75f, 0.85f, 1f));
			_colTexts = (TextMeshProUGUI[])(object)new TextMeshProUGUI[5];
			float num = 0.2f;
			for (int i = 0; i < 5; i++)
			{
				float num2 = (float)i * num + 0.01f;
				float num3 = (float)(i + 1) * num - 0.01f;
				RectTransform parent3 = UIHelper.CreatePanel((Transform)(object)parent, $"Col{i}", new Vector2(num2, 0.02f), new Vector2(num3, 0.7f));
				_colTexts[i] = UIHelper.CreateText((Transform)(object)parent3, "Text", string.Empty, 17, (TextAlignmentOptions)257, (Color?)new Color(0.92f, 0.94f, 1f));
				((TMP_Text)_colTexts[i]).richText = true;
			}
			((Component)_canvas).gameObject.SetActive(false);
		}

		public void OnPickSequenceStarted()
		{
			if ((Object)(object)GetLocalHumanPlayer() == (Object)null)
			{
				SetVisible(visible: false);
				return;
			}
			_pickSequenceActive = true;
			SetVisible(visible: true);
			Refresh();
		}

		public void OnPickSequenceEnded()
		{
			_pickSequenceActive = false;
			SetVisible(visible: false);
		}

		private void Update()
		{
			if (_pickSequenceActive && !((Object)(object)_canvas == (Object)null) && ((Component)_canvas).gameObject.activeSelf)
			{
				Refresh();
			}
		}

		private void Refresh()
		{
			Player localHumanPlayer = GetLocalHumanPlayer();
			if ((Object)(object)localHumanPlayer == (Object)null)
			{
				SetVisible(visible: false);
				return;
			}
			if (!TryGetHighlightedCard(out var card))
			{
				((TMP_Text)_headerText).text = "Card preview";
				SetColumns(null);
				((TMP_Text)_colTexts[0]).text = "—";
				return;
			}
			((TMP_Text)_headerText).text = "If " + card.cardName + ":";
			if (!CardDeltaRegistry.TryGetDeltas(localHumanPlayer, card, out var deltas))
			{
				SetColumns(null);
				((TMP_Text)_colTexts[0]).text = "N/A";
			}
			else if (deltas.Count == 0)
			{
				SetColumns(null);
				((TMP_Text)_colTexts[0]).text = "No stat changes";
			}
			else
			{
				SetColumns(deltas);
			}
		}

		private void SetColumns(IReadOnlyList<StatDeltaLine> deltas)
		{
			if (deltas == null)
			{
				for (int i = 0; i < 5; i++)
				{
					((TMP_Text)_colTexts[i]).text = string.Empty;
					((Component)_colTexts[i]).gameObject.SetActive(i == 0);
				}
				return;
			}
			int num = Mathf.Clamp(Mathf.CeilToInt((float)deltas.Count / 3f), 1, 5);
			for (int j = 0; j < 5; j++)
			{
				bool flag = j < num;
				((Component)_colTexts[j]).gameObject.SetActive(flag);
				if (!flag)
				{
					continue;
				}
				StringBuilder stringBuilder = new StringBuilder();
				int num2 = j * 3;
				int num3 = Mathf.Min(num2 + 3, deltas.Count);
				for (int k = num2; k < num3; k++)
				{
					if (k > num2)
					{
						stringBuilder.AppendLine();
					}
					stringBuilder.Append(deltas[k].FormatRichText());
				}
				((TMP_Text)_colTexts[j]).text = stringBuilder.ToString();
			}
		}

		private static bool TryGetHighlightedCard(out CardInfo card)
		{
			card = null;
			CardChoice val = CardChoice.instance;
			if ((Object)(object)val == (Object)null || !val.IsPicking || SpawnedCardsField == null || SelectedCardField == null)
			{
				return false;
			}
			if (!(SpawnedCardsField.GetValue(val) is List<GameObject> list) || list.Count == 0)
			{
				return false;
			}
			int num = (int)SelectedCardField.GetValue(val);
			num = Mathf.Clamp(num, 0, list.Count - 1);
			GameObject val2 = list[num];
			if ((Object)(object)val2 == (Object)null)
			{
				return false;
			}
			card = val2.GetComponent<CardInfo>();
			if ((Object)(object)card == (Object)null)
			{
				card = val2.GetComponentInChildren<CardInfo>();
			}
			return (Object)(object)card != (Object)null;
		}

		private static Player GetLocalHumanPlayer()
		{
			if ((Object)(object)PlayerManager.instance == (Object)null)
			{
				return null;
			}
			foreach (Player player in PlayerManager.instance.players)
			{
				if ((Object)(object)player == (Object)null)
				{
					continue;
				}
				PlayerAPI component = ((Component)player).GetComponent<PlayerAPI>();
				if (component == null || !((Behaviour)component).enabled)
				{
					object value = AccessTools.Field(typeof(CharacterData), "view").GetValue(player.data);
					if (value == null)
					{
						return player;
					}
					if ((bool)AccessTools.Property(value.GetType(), "IsMine").GetValue(value, null))
					{
						return player;
					}
				}
			}
			return null;
		}

		private void SetVisible(bool visible)
		{
			if ((Object)(object)_canvas != (Object)null)
			{
				((Component)_canvas).gameObject.SetActive(visible);
			}
		}

		[IteratorStateMachine(typeof(<OnPickSequenceEnd>d__19))]
		private IEnumerator OnPickSequenceEnd(IGameModeHandler gm)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <OnPickSequenceEnd>d__19(0)
			{
				<>4__this = this
			};
		}
	}
	public class PickStatsController : MonoBehaviour
	{
		[CompilerGenerated]
		private sealed class <OnPickSequenceEnd>d__14 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public PickStatsController <>4__this;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <OnPickSequenceEnd>d__14(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				int num = <>1__state;
				PickStatsController pickStatsController = <>4__this;
				if (num != 0)
				{
					return false;
				}
				<>1__state = -1;
				IOLog.Line("Pick sequence ended — auto-closing stats window.");
				pickStatsController.EndPickPhase("HookPickEnd");
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <OnPlayerPickEnd>d__13 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <OnPlayerPickEnd>d__13(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				if (<>1__state != 0)
				{
					return false;
				}
				<>1__state = -1;
				IOLog.Line($"Single player pick ended (picker={currentPickerID}); keeping stats UI up until pick sequence ends.");
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		public static PickStatsController instance;

		public static bool isPickPhase;

		public static int currentPickerID = -1;

		private Canvas _canvas;

		private StatsPopup _popup;

		private GameObject _showStatsBtnGo;

		private void Awake()
		{
			instance = this;
			BuildUI();
			GameModeManager.AddHook("PlayerPickEnd", (Func<IGameModeHandler, IEnumerator>)OnPlayerPickEnd);
			GameModeManager.AddHook("PickEnd", (Func<IGameModeHandler, IEnumerator>)OnPickSequenceEnd);
			IOLog.Section("PickStatsController initialized");
		}

		private void BuildUI()
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected O, but got Unknown
			_canvas = UIHelper.CreateFullscreenCanvas("IO_PickStatsCanvas", 160);
			RectTransform component = ((Component)_canvas).GetComponent<RectTransform>();
			Button val = UIHelper.AnchorButton(component, "ShowStatsBtn", "Show Stats", new Vector2(0.02f, 0.02f), new Vector2(0.16f, 0.08f), new Color(0.18f, 0.35f, 0.55f, 1f), 24);
			((UnityEvent)val.onClick).AddListener(new UnityAction(TogglePopup));
			_showStatsBtnGo = ((Component)val).gameObject;
			_popup = StatsPopup.Create((Transform)(object)component);
			((Component)_canvas).gameObject.SetActive(false);
		}

		public void BeginPickPhase(int pickerID, string source)
		{
			if (pickerID >= 0)
			{
				if ((Object)(object)GetLocalHumanPlayer() == (Object)null)
				{
					HideAll();
					IOLog.Line($"Pick phase for player {pickerID} (source={source}) — no local human, UI hidden.");
					return;
				}
				currentPickerID = pickerID;
				isPickPhase = true;
				((Component)_canvas).gameObject.SetActive(true);
				_showStatsBtnGo.SetActive(true);
				CardDeltaPreviewOverlay.instance?.OnPickSequenceStarted();
				IOLog.Line($"Pick phase active — picker={pickerID}, showing local stats (source={source}).");
			}
		}

		public void EndPickPhase(string source)
		{
			IOLog.Line($"Pick phase ended (source={source}), was picker={currentPickerID}");
			isPickPhase = false;
			currentPickerID = -1;
			CardDeltaPreviewOverlay.instance?.OnPickSequenceEnded();
			HideAll();
		}

		private void HideAll()
		{
			_popup?.Hide();
			if ((Object)(object)_canvas != (Object)null)
			{
				((Component)_canvas).gameObject.SetActive(false);
			}
		}

		private void TogglePopup()
		{
			Player localHumanPlayer = GetLocalHumanPlayer();
			if (!((Object)(object)localHumanPlayer == (Object)null))
			{
				if (_popup.IsVisible)
				{
					_popup.Hide();
				}
				else
				{
					_popup.Show(localHumanPlayer);
				}
			}
		}

		private static Player GetLocalHumanPlayer()
		{
			if ((Object)(object)PlayerManager.instance == (Object)null)
			{
				return null;
			}
			foreach (Player player in PlayerManager.instance.players)
			{
				if ((Object)(object)player == (Object)null)
				{
					continue;
				}
				PlayerAPI component = ((Component)player).GetComponent<PlayerAPI>();
				if (component == null || !((Behaviour)component).enabled)
				{
					object value = AccessTools.Field(typeof(CharacterData), "view").GetValue(player.data);
					if (value == null)
					{
						return player;
					}
					if ((bool)AccessTools.Property(value.GetType(), "IsMine").GetValue(value, null))
					{
						return player;
					}
				}
			}
			return null;
		}

		[IteratorStateMachine(typeof(<OnPlayerPickEnd>d__13))]
		private IEnumerator OnPlayerPickEnd(IGameModeHandler gm)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <OnPlayerPickEnd>d__13(0);
		}

		[IteratorStateMachine(typeof(<OnPickSequenceEnd>d__14))]
		private IEnumerator OnPickSequenceEnd(IGameModeHandler gm)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <OnPickSequenceEnd>d__14(0)
			{
				<>4__this = this
			};
		}
	}
	internal class StatsPopup : MonoBehaviour
	{
		private readonly List<TextMeshProUGUI> _leftLines = new List<TextMeshProUGUI>();

		private readonly List<TextMeshProUGUI> _rightLines = new List<TextMeshProUGUI>();

		private RectTransform _root;

		private Player _player;

		public bool IsVisible => ((Component)this).gameObject.activeSelf;

		public static StatsPopup Create(Transform parent)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("IO_StatsPopup");
			val.transform.SetParent(parent, false);
			StatsPopup statsPopup = val.AddComponent<StatsPopup>();
			statsPopup.Build();
			val.SetActive(false);
			return statsPopup;
		}

		private void Build()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: 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_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: 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_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: 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_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Expected O, but got Unknown
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0241: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_024a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			//IL_026e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: Unknown result type (might be due to invalid IL or missing references)
			//IL_027b: Unknown result type (might be due to invalid IL or missing references)
			//IL_027e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Unknown result type (might be due to invalid IL or missing references)
			_root = ((Component)this).gameObject.AddComponent<RectTransform>();
			_root.anchorMin = Vector2.zero;
			_root.anchorMax = Vector2.one;
			RectTransform root = _root;
			Vector2 offsetMin = (_root.offsetMax = Vector2.zero);
			root.offsetMin = offsetMin;
			GameObject val = new GameObject("Backdrop");
			val.transform.SetParent(((Component)this).transform, false);
			RectTransform obj = val.AddComponent<RectTransform>();
			obj.anchorMin = Vector2.zero;
			obj.anchorMax = Vector2.one;
			offsetMin = (obj.offsetMax = Vector2.zero);
			obj.offsetMin = offsetMin;
			((Graphic)val.AddComponent<Image>()).color = new Color(0.02f, 0.02f, 0.05f, 1f);
			Transform transform = ((Component)this).transform;
			Vector2 anchorMin = new Vector2(0.12f, 0.08f);
			Vector2 anchorMax = new Vector2(0.88f, 0.92f);
			Color? bg = new Color(0.08f, 0.09f, 0.14f, 1f);
			RectTransform parent = UIHelper.CreatePanel(transform, "Panel", anchorMin, anchorMax, default(Vector2), default(Vector2), bg);
			UIHelper.CreateText((Transform)(object)UIHelper.CreatePanel((Transform)(object)parent, "Title", new Vector2(0f, 0.9f), Vector2.one), "TitleText", "Player Stats", 36, (TextAlignmentOptions)514);
			((UnityEvent)UIHelper.AnchorButton(parent, "CloseBtn", "Close", new Vector2(0.82f, 0.91f), new Vector2(0.98f, 0.99f), new Color(0.45f, 0.12f, 0.12f), 22).onClick).AddListener(new UnityAction(Hide));
			RectTransform parent2 = UIHelper.CreatePanel((Transform)(object)parent, "Columns", new Vector2(0.04f, 0.04f), new Vector2(0.96f, 0.88f));
			RectTransform parent3 = UIHelper.CreatePanel((Transform)(object)parent2, "LeftColumn", new Vector2(0f, 0f), new Vector2(0.48f, 1f));
			RectTransform parent4 = UIHelper.CreatePanel((Transform)(object)parent2, "RightColumn", new Vector2(0.52f, 0f), Vector2.one);
			BuildColumn(parent3, _leftLines, 12);
			BuildColumn(parent4, _rightLines, 12);
		}

		private static void BuildColumn(RectTransform parent, List<TextMeshProUGUI> sink, int lineCount)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			float num = 1f / (float)lineCount;
			for (int i = 0; i < lineCount; i++)
			{
				float num2 = 1f - (float)i * num;
				float num3 = num2 - num;
				TextMeshProUGUI item = UIHelper.CreateText((Transform)(object)UIHelper.CreatePanel((Transform)(object)parent, $"Line{i}", new Vector2(0f, num3), new Vector2(1f, num2)), "Text", "—", 22, (TextAlignmentOptions)4097, (Color?)new Color(0.92f, 0.94f, 1f));
				sink.Add(item);
			}
		}

		public void Show(Player player)
		{
			_player = player;
			((Component)this).gameObject.SetActive(true);
			Refresh();
		}

		public void Hide()
		{
			((Component)this).gameObject.SetActive(false);
			_player = null;
		}

		private void Update()
		{
			if (IsVisible && !((Object)(object)_player == (Object)null))
			{
				Refresh();
			}
		}

		private void Refresh()
		{
			List<StatsReader.StatLine> stats = StatsReader.Collect(_player);
			ApplyColumn(_leftLines, stats, 0);
			ApplyColumn(_rightLines, stats, _leftLines.Count);
		}

		private static void ApplyColumn(List<TextMeshProUGUI> labels, List<StatsReader.StatLine> stats, int offset)
		{
			for (int i = 0; i < labels.Count; i++)
			{
				int num = offset + i;
				((TMP_Text)labels[i]).text = ((num < stats.Count) ? stats[num].Text : string.Empty);
			}
		}
	}
	internal static class UIHelper
	{
		public static Canvas CreateFullscreenCanvas(string name, int sortOrder = 100)
		{
			//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_000c: Expected O, but got Unknown
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			Object.DontDestroyOnLoad((Object)val);
			val.SetActive(false);
			Canvas val2 = val.AddComponent<Canvas>();
			val2.renderMode = (RenderMode)0;
			val2.sortingOrder = sortOrder;
			CanvasScaler obj = val.AddComponent<CanvasScaler>();
			obj.uiScaleMode = (ScaleMode)1;
			obj.referenceResolution = new Vector2(1920f, 1080f);
			val.AddComponent<GraphicRaycaster>();
			return val2;
		}

		public static RectTransform CreatePanel(Transform parent, string name, Vector2 anchorMin, Vector2 anchorMax, Vector2 offsetMin = default(Vector2), Vector2 offsetMax = default(Vector2), Color? bg = null)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.transform.SetParent(parent, false);
			RectTransform obj = val.AddComponent<RectTransform>();
			obj.anchorMin = anchorMin;
			obj.anchorMax = anchorMax;
			obj.offsetMin = offsetMin;
			obj.offsetMax = offsetMax;
			if (bg.HasValue)
			{
				((Graphic)val.AddComponent<Image>()).color = bg.Value;
			}
			return obj;
		}

		public static TextMeshProUGUI CreateText(Transform parent, string name, string text, int fontSize = 24, TextAlignmentOptions alignment = 514, Color? color = null)
		{
			//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_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.transform.SetParent(parent, false);
			RectTransform obj = val.AddComponent<RectTransform>();
			obj.anchorMin = Vector2.zero;
			obj.anchorMax = Vector2.one;
			obj.offsetMin = Vector2.zero;
			obj.offsetMax = Vector2.zero;
			TextMeshProUGUI obj2 = val.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj2).text = text;
			((TMP_Text)obj2).fontSize = fontSize;
			((TMP_Text)obj2).alignment = alignment;
			((Graphic)obj2).color = (Color)(((??)color) ?? Color.white);
			return obj2;
		}

		public static Button AnchorButton(RectTransform parent, string name, string label, Vector2 anchorMin, Vector2 anchorMax, Color bgColor, int fontSize)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: 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_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.transform.SetParent((Transform)(object)parent, false);
			RectTransform obj = val.AddComponent<RectTransform>();
			obj.anchorMin = anchorMin;
			obj.anchorMax = anchorMax;
			Vector2 offsetMin = (obj.offsetMax = Vector2.zero);
			obj.offsetMin = offsetMin;
			Image val2 = val.AddComponent<Image>();
			((Graphic)val2).color = bgColor;
			Button obj2 = val.AddComponent<Button>();
			((Selectable)obj2).targetGraphic = (Graphic)(object)val2;
			ColorBlock val3 = default(ColorBlock);
			((ColorBlock)(ref val3)).normalColor = bgColor;
			((ColorBlock)(ref val3)).highlightedColor = bgColor * 1.3f;
			((ColorBlock)(ref val3)).pressedColor = bgColor * 0.7f;
			((ColorBlock)(ref val3)).disabledColor = new Color(0.25f, 0.25f, 0.25f, 0.6f);
			((ColorBlock)(ref val3)).colorMultiplier = 1f;
			((ColorBlock)(ref val3)).fadeDuration = 0.1f;
			ColorBlock colors = val3;
			((Selectable)obj2).colors = colors;
			GameObject val4 = new GameObject("Label");
			val4.transform.SetParent(val.transform, false);
			RectTransform obj3 = val4.AddComponent<RectTransform>();
			obj3.anchorMin = Vector2.zero;
			obj3.anchorMax = Vector2.one;
			offsetMin = (obj3.offsetMax = Vector2.zero);
			obj3.offsetMin = offsetMin;
			TextMeshProUGUI obj4 = val4.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj4).text = label;
			((TMP_Text)obj4).fontSize = fontSize;
			((TMP_Text)obj4).alignment = (TextAlignmentOptions)514;
			((Graphic)obj4).color = Color.white;
			return obj2;
		}
	}
}
namespace InfoOverhaul.Stats
{
	internal static class PoisonResistanceStatsBridge
	{
		private static bool _resolved;

		private static PropertyInfo _instanceProp;

		private static MethodInfo _getPercent;

		public static bool IsAvailable
		{
			get
			{
				Resolve();
				if (_instanceProp != null)
				{
					return _getPercent != null;
				}
				return false;
			}
		}

		private static void Resolve()
		{
			if (_resolved)
			{
				return;
			}
			_resolved = true;
			try
			{
				Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "ShieldsMod");
				if (!(assembly == null))
				{
					Type type = assembly.GetType("ShieldsMod.Poison.PoisonResistanceManager");
					if (!(type == null))
					{
						_instanceProp = type.GetProperty("instance", BindingFlags.Static | BindingFlags.Public);
						_getPercent = type.GetMethod("GetReductionPercent", BindingFlags.Instance | BindingFlags.Public);
					}
				}
			}
			catch (Exception ex)
			{
				IOLog.Warn("PoisonResistanceStatsBridge resolve failed: " + ex.Message);
			}
		}

		public static bool TryGetPercent(int playerID, out float percent)
		{
			percent = 0f;
			Resolve();
			if (_instanceProp == null || _getPercent == null)
			{
				return false;
			}
			try
			{
				object value = _instanceProp.GetValue(null);
				if (value == null)
				{
					return false;
				}
				percent = (float)_getPercent.Invoke(value, new object[1] { playerID });
				return true;
			}
			catch (Exception ex)
			{
				IOLog.Warn("TryGetPercent failed: " + ex.Message);
				return false;
			}
		}
	}
	internal static class ShieldStatsBridge
	{
		private static bool _resolved;

		private static PropertyInfo _instanceProp;

		private static MethodInfo _getShield;

		private static FieldInfo _currentField;

		private static FieldInfo _maxField;

		public static bool IsAvailable
		{
			get
			{
				Resolve();
				if (_instanceProp != null)
				{
					return _getShield != null;
				}
				return false;
			}
		}

		private static void Resolve()
		{
			if (_resolved)
			{
				return;
			}
			_resolved = true;
			try
			{
				Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "ShieldsMod");
				if (assembly == null)
				{
					IOLog.Line("ShieldsMod not loaded — Shield Health will show as N/A.");
					return;
				}
				Type type = assembly.GetType("ShieldsMod.Shield.ShieldManager");
				Type type2 = assembly.GetType("ShieldsMod.Shield.ShieldState");
				if (type == null || type2 == null)
				{
					IOLog.Warn("ShieldsMod shield types not found.");
					return;
				}
				_instanceProp = type.GetProperty("instance", BindingFlags.Static | BindingFlags.Public);
				_getShield = type.GetMethod("GetShield", BindingFlags.Instance | BindingFlags.Public);
				_currentField = type2.GetField("Current", BindingFlags.Instance | BindingFlags.Public);
				_maxField = type2.GetField("Max", BindingFlags.Instance | BindingFlags.Public);
				if (_instanceProp == null || _getShield == null || _currentField == null || _maxField == null)
				{
					IOLog.Warn("ShieldsMod shield API incomplete.");
				}
			}
			catch (Exception ex)
			{
				IOLog.Error("ShieldStatsBridge resolve failed: " + ex.Message);
			}
		}

		public static bool TryGetShieldHealth(int playerID, out float current, out float max)
		{
			current = 0f;
			max = 0f;
			Resolve();
			if (_instanceProp == null || _getShield == null)
			{
				return false;
			}
			try
			{
				object value = _instanceProp.GetValue(null);
				if (value == null)
				{
					return false;
				}
				object obj = _getShield.Invoke(value, new object[1] { playerID });
				if (obj == null)
				{
					return false;
				}
				current = (float)_currentField.GetValue(obj);
				max = (float)_maxField.GetValue(obj);
				return true;
			}
			catch (Exception ex)
			{
				IOLog.Warn("TryGetShieldHealth failed: " + ex.Message);
				return false;
			}
		}
	}
	internal static class StatsReader
	{
		internal readonly struct StatLine
		{
			public readonly string Text;

			public StatLine(string text)
			{
				Text = text;
			}
		}

		public static List<StatLine> Collect(Player player)
		{
			List<StatLine> list = new List<StatLine>(24);
			if ((Object)(object)player == (Object)null)
			{
				return list;
			}
			Gun val = player.data?.weaponHandler?.gun;
			Block val2 = player.data?.block;
			list.Add(new StatLine($"HP: {player.data.health:f0} / {player.data.maxHealth:f0}"));
			list.Add(new StatLine($"Lives: {player.data.stats.respawns + 1:f0}"));
			list.Add(new StatLine(((Object)(object)val2 != (Object)null) ? $"Block CD: {val2.Cooldown():f2}s" : "Block CD: —"));
			list.Add(new StatLine(((Object)(object)val2 != (Object)null) ? $"Block Count: {val2.additionalBlocks + 1:f0}" : "Block Count: —"));
			list.Add(new StatLine(FormatShieldHealth(player.playerID)));
			list.Add(new StatLine(FormatPoisonResistance(player.playerID)));
			if ((Object)(object)val != (Object)null)
			{
				list.Add(new StatLine($"DMG: {val.damage * 55f * val.bulletDamageMultiplier:f0}"));
				list.Add(new StatLine($"Knockback: {val.knockback:f2}"));
				list.Add(new StatLine($"Life Steal: {player.data.stats.lifeSteal:f2}"));
				list.Add(new StatLine($"Damage Grow: {val.damageAfterDistanceMultiplier:f2}"));
				list.Add(new StatLine($"Bullet Slow: {val.slow:f2}"));
				list.Add(new StatLine($"Move SPD: {player.data.stats.movementSpeed:f2}"));
				list.Add(new StatLine($"Jump Height: {player.data.stats.jump:f2}"));
				list.Add(new StatLine($"Player Size: {player.data.stats.sizeMultiplier:f2}"));
				list.Add(new StatLine($"Attack SPD: {val.attackSpeed * val.attackSpeedMultiplier:f2}s"));
				list.Add(new StatLine($"Bullet SPD: {val.projectileSpeed:f2}"));
				list.Add(new StatLine($"Projectile SPD: {val.projectielSimulatonSpeed:f2}"));
				GunAmmo componentInChildren = ((Component)val).GetComponentInChildren<GunAmmo>();
				if ((Object)(object)componentInChildren != (Object)null)
				{
					float num = (componentInChildren.reloadTime + componentInChildren.reloadTimeAdd) * componentInChildren.reloadTimeMultiplier;
					list.Add(new StatLine($"Reload Time: {num:f2}s"));
					list.Add(new StatLine($"Ammo: {componentInChildren.maxAmmo:f0}"));
				}
				else
				{
					list.Add(new StatLine("Reload Time: —"));
					list.Add(new StatLine("Ammo: —"));
				}
				list.Add(new StatLine($"Bullet Gravity: {val.gravity:f2}"));
				list.Add(new StatLine($"Bullets: {val.numberOfProjectiles:f0}"));
				list.Add(new StatLine($"Bullet Range: {val.destroyBulletAfter:f2}"));
				list.Add(new StatLine($"Bounces: {val.reflects:f0}"));
				list.Add(new StatLine($"Bursts: {val.bursts:f0}"));
			}
			else
			{
				list.Add(new StatLine("DMG: —"));
				list.Add(new StatLine("Knockback: —"));
				list.Add(new StatLine($"Life Steal: {player.data.stats.lifeSteal:f2}"));
				list.Add(new StatLine("Damage Grow: —"));
				list.Add(new StatLine("Bullet Slow: —"));
				list.Add(new StatLine($"Move SPD: {player.data.stats.movementSpeed:f2}"));
				list.Add(new StatLine($"Jump Height: {player.data.stats.jump:f2}"));
				list.Add(new StatLine($"Player Size: {player.data.stats.sizeMultiplier:f2}"));
				list.Add(new StatLine("Attack SPD: —"));
				list.Add(new StatLine("Bullet SPD: —"));
				list.Add(new StatLine("Projectile SPD: —"));
				list.Add(new StatLine("Reload Time: —"));
				list.Add(new StatLine("Ammo: —"));
				list.Add(new StatLine("Bullet Gravity: —"));
				list.Add(new StatLine("Bullets: —"));
				list.Add(new StatLine("Bullet Range: —"));
				list.Add(new StatLine("Bounces: —"));
				list.Add(new StatLine("Bursts: —"));
			}
			return list;
		}

		private static string FormatShieldHealth(int playerID)
		{
			if (ShieldStatsBridge.TryGetShieldHealth(playerID, out var current, out var max))
			{
				return $"Shield Health: {current:f0} / {max:f0}";
			}
			if (!ShieldStatsBridge.IsAvailable)
			{
				return "Shield Health: N/A";
			}
			return "Shield Health: — / —";
		}

		private static string FormatPoisonResistance(int playerID)
		{
			if (PoisonResistanceStatsBridge.TryGetPercent(playerID, out var percent))
			{
				return $"Poison Resistance: {percent:f0}%";
			}
			if (!PoisonResistanceStatsBridge.IsAvailable)
			{
				return "Poison Resistance: N/A";
			}
			return "Poison Resistance: 0%";
		}
	}
}
namespace InfoOverhaul.Patches
{
	[HarmonyPatch]
	internal static class PickPhasePatches
	{
		[HarmonyPatch(typeof(CardChoiceVisuals), "Show")]
		[HarmonyPrefix]
		private static void CardChoiceVisuals_Show_Prefix(int pickerID, bool animateIn)
		{
			PickStatsController.instance?.BeginPickPhase(pickerID, "CardChoiceVisuals.Show");
		}

		[HarmonyPatch(typeof(CardChoice), "StartPick")]
		[HarmonyPostfix]
		private static void CardChoice_StartPick_Postfix(int picksToSet, int pickerIDToSet)
		{
			PickStatsController.instance?.BeginPickPhase(pickerIDToSet, "CardChoice.StartPick");
		}
	}
}
namespace InfoOverhaul.Delta
{
	public static class CardDeltaRegistry
	{
		public delegate IReadOnlyList<StatDeltaLine> DeltaProvider(Player player, CardInfo card);

		public delegate IReadOnlyList<(string label, string before, string after)> SimpleDeltaProvider(Player player, CardInfo card);

		private static readonly Dictionary<string, DeltaProvider> Providers = new Dictionary<string, DeltaProvider>(StringComparer.Ordinal);

		private static readonly Dictionary<string, List<DeltaProvider>> Addons = new Dictionary<string, List<DeltaProvider>>(StringComparer.Ordinal);

		public static void RegisterSimple(string cardName, SimpleDeltaProvider provider)
		{
			Register(cardName, (Player player, CardInfo card) => ToLines(provider(player, card)));
		}

		public static void Register(string cardName, DeltaProvider provider)
		{
			if (!string.IsNullOrEmpty(cardName) && provider != null)
			{
				Providers[cardName] = provider;
				IOLog.Line("CardDeltaRegistry — registered '" + cardName + "'.");
			}
		}

		public static void RegisterAddon(string cardName, DeltaProvider addon)
		{
			if (!string.IsNullOrEmpty(cardName) && addon != null)
			{
				if (!Addons.TryGetValue(cardName, out var value))
				{
					value = new List<DeltaProvider>();
					Addons[cardName] = value;
				}
				value.Add(addon);
				IOLog.Line("CardDeltaRegistry — addon registered for '" + cardName + "'.");
			}
		}

		public static void RegisterAddonSimple(string cardName, SimpleDeltaProvider addon)
		{
			RegisterAddon(cardName, (Player player, CardInfo card) => ToLines(addon(player, card)));
		}

		public static bool IsRegistered(string cardName)
		{
			if (!string.IsNullOrEmpty(cardName))
			{
				if (!Providers.ContainsKey(cardName))
				{
					if (Addons.TryGetValue(cardName, out var value))
					{
						return value.Count > 0;
					}
					return false;
				}
				return true;
			}
			return false;
		}

		public static bool TryGetDeltas(Player player, CardInfo card, out IReadOnlyList<StatDeltaLine> deltas)
		{
			deltas = null;
			if ((Object)(object)player == (Object)null || (Object)(object)card == (Object)null)
			{
				return false;
			}
			string text = ResolveCardName(card);
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			DeltaProvider value;
			bool flag = Providers.TryGetValue(text, out value);
			List<DeltaProvider> value2;
			bool flag2 = Addons.TryGetValue(text, out value2) && value2.Count > 0;
			if (!flag && !flag2)
			{
				return false;
			}
			List<StatDeltaLine> list = new List<StatDeltaLine>();
			if (flag)
			{
				list.AddRange(value(player, card) ?? Array.Empty<StatDeltaLine>());
			}
			if (flag2)
			{
				foreach (DeltaProvider item in value2)
				{
					list.AddRange(item(player, card) ?? Array.Empty<StatDeltaLine>());
				}
			}
			deltas = list;
			return true;
		}

		internal static string ResolveCardName(CardInfo card)
		{
			if (!string.IsNullOrEmpty(card.cardName))
			{
				return card.cardName;
			}
			if (!((Object)(object)card.sourceCard != (Object)null))
			{
				return null;
			}
			return card.sourceCard.cardName;
		}

		private static IReadOnlyList<StatDeltaLine> ToLines(IReadOnlyList<(string label, string before, string after)> tuples)
		{
			if (tuples == null || tuples.Count == 0)
			{
				return Array.Empty<StatDeltaLine>();
			}
			return tuples.Select(((string label, string before, string after) t) => (string.IsNullOrEmpty(t.before) && string.IsNullOrEmpty(t.after)) ? StatDeltaLine.Note(t.label) : new StatDeltaLine(t.label, t.before, t.after)).ToList();
		}
	}
	public static class CardDeltaTier2
	{
		public static void RegisterEffectNotes(string cardName, params string[] notes)
		{
			if (notes != null && notes.Length != 0)
			{
				CardDeltaRegistry.RegisterAddon(cardName, (Player _, CardInfo __) => notes.Select(StatDeltaLine.Note).ToList());
			}
		}

		public static void RegisterEffectNotes(string cardName, Func<Player, CardInfo, IReadOnlyList<string>> notes)
		{
			if (notes != null)
			{
				CardDeltaRegistry.RegisterAddon(cardName, (Player player, CardInfo card) => (notes(player, card) ?? Array.Empty<string>()).Select(StatDeltaLine.Note).ToList());
			}
		}

		public static void RegisterOverrideSimple(string cardName, CardDeltaRegistry.SimpleDeltaProvider provider)
		{
			CardDeltaRegistry.RegisterSimple(cardName, provider);
		}
	}
	public readonly struct StatDeltaLine
	{
		private const string IncreaseColor = "#66FF88";

		private const string DecreaseColor = "#FF6666";

		private const string NeutralColor = "#EBF0FF";

		public readonly string Label;

		public readonly string Before;

		public readonly string After;

		public readonly bool IsNote;

		public StatDeltaLine(string label, string before, string after, bool isNote = false)
		{
			Label = label;
			Before = before;
			After = after;
			IsNote = isNote;
		}

		public static StatDeltaLine Note(string text)
		{
			return new StatDeltaLine(text, null, null, isNote: true);
		}

		public string Format()
		{
			if (!IsNote)
			{
				return Label + ": " + Before + " --> " + After;
			}
			return Label;
		}

		public string FormatRichText()
		{
			if (IsNote)
			{
				return Label;
			}
			if (!TryParseStatValue(Before, out var value) || !TryParseStatValue(After, out var value2))
			{
				return Format();
			}
			string text = ((value2 > value) ? "#66FF88" : ((value2 < value) ? "#FF6666" : "#EBF0FF"));
			return Label + ": " + Before + " --> <color=" + text + ">" + After + "</color>";
		}

		private static bool TryParseStatValue(string text, out float value)
		{
			value = 0f;
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			int i;
			for (i = 0; i < text.Length && (char.IsDigit(text[i]) || text[i] == '.' || text[i] == '-'); i++)
			{
			}
			if (i == 0)
			{
				return false;
			}
			return float.TryParse(text.Substring(0, i), NumberStyles.Float, CultureInfo.InvariantCulture, out value);
		}
	}
	internal static class StatTemplateDeltaProvider
	{
		private const float Epsilon = 0.0001f;

		public static IReadOnlyList<StatDeltaLine> Compute(Player player, CardInfo card)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)card == (Object)null)
			{
				return Array.Empty<StatDeltaLine>();
			}
			GameObject obj = (((Object)(object)card.sourceCard != (Object)null) ? ((Component)card.sourceCard).gameObject : ((Component)card).gameObject);
			Gun component = obj.GetComponent<Gun>();
			CharacterStatModifiers component2 = obj.GetComponent<CharacterStatModifiers>();
			Block componentInChildren = obj.GetComponentInChildren<Block>();
			if (!HasStatTemplate(component, component2, componentInChildren))
			{
				return Array.Empty<StatDeltaLine>();
			}
			Gun val = player.data?.weaponHandler?.gun;
			Block val2 = player.data?.block;
			CharacterData data = player.data;
			CharacterStatModifiers component3 = ((Component)player).GetComponent<CharacterStatModifiers>();
			GunAmmo val3 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponentInChildren<GunAmmo>() : null);
			List<StatDeltaLine> list = new List<StatDeltaLine>();
			if ((Object)(object)component2 != (Object)null && (Object)(object)data != (Object)null && (Object)(object)component3 != (Object)null)
			{
				TryAdd(list, "HP", data.maxHealth, data.maxHealth * component2.health, "f0");
				TryAdd(list, "Lives", (float)component3.respawns + 1f, (float)(component3.respawns + component2.respawns) + 1f, "f0");
				TryAdd(list, "Move SPD", component3.movementSpeed, component3.movementSpeed * component2.movementSpeed, "f2");
				TryAdd(list, "Jump Height", component3.jump, component3.jump * component2.jump, "f2");
				TryAdd(list, "Player Size", component3.sizeMultiplier, component3.sizeMultiplier * component2.sizeMultiplier, "f2");
				TryAdd(list, "Life Steal", component3.lifeSteal, component3.lifeSteal + component2.lifeSteal, "f2");
			}
			if ((Object)(object)componentInChildren != (Object)null && (Object)(object)val2 != (Object)null)
			{
				float before = val2.Cooldown();
				float after = (val2.cooldown + val2.cdAdd + componentInChildren.cdAdd) * (val2.cdMultiplier * componentInChildren.cdMultiplier);
				TryAdd(list, "Block CD", before, after, "f2", "s");
				TryAdd(list, "Block Count", (float)val2.additionalBlocks + 1f, (float)(val2.additionalBlocks + componentInChildren.additionalBlocks) + 1f, "f0");
			}
			if ((Object)(object)component != (Object)null && (Object)(object)val != (Object)null)
			{
				float before2 = val.damage * 55f * val.bulletDamageMultiplier;
				float after2 = ApplyGunDamage(val.damage, val.numberOfProjectiles, component) * 55f * (val.bulletDamageMultiplier * component.bulletDamageMultiplier);
				TryAdd(list, "DMG", before2, after2, "f0");
				TryAdd(list, "Knockback", val.knockback, ApplyKnockback(val.knockback, val.numberOfProjectiles, component), "f2");
				TryAdd(list, "Damage Grow", val.damageAfterDistanceMultiplier, val.damageAfterDistanceMultiplier * component.damageAfterDistanceMultiplier, "f2");
				TryAdd(list, "Bullet Slow", val.slow, val.slow + component.slow, "f2");
				TryAdd(list, "Attack SPD", val.attackSpeed * val.attackSpeedMultiplier, val.attackSpeed * component.attackSpeed * val.attackSpeedMultiplier, "f2", "s");
				TryAdd(list, "Bullet SPD", val.projectileSpeed, val.projectileSpeed * component.projectileSpeed, "f2");
				TryAdd(list, "Projectile SPD", val.projectielSimulatonSpeed, val.projectielSimulatonSpeed * component.projectielSimulatonSpeed, "f2");
				TryAdd(list, "Bullet Gravity", val.gravity, val.gravity * component.gravity, "f2");
				TryAdd(list, "Bullets", val.numberOfProjectiles, val.numberOfProjectiles + component.numberOfProjectiles, "f0");
				TryAdd(list, "Bounces", val.reflects, val.reflects + component.reflects, "f0");
				TryAdd(list, "Bursts", val.bursts, val.bursts + component.bursts, "f0");
				if (component.destroyBulletAfter != 0f)
				{
					TryAdd(list, "Bullet Range", val.destroyBulletAfter, component.destroyBulletAfter, "f2");
				}
				else
				{
					TryAdd(list, "Bullet Range", val.destroyBulletAfter, val.destroyBulletAfter + component.destroyBulletAfter, "f2");
				}
			}
			if ((Object)(object)component != (Object)null && (Object)(object)val3 != (Object)null)
			{
				float before3 = (val3.reloadTime + val3.reloadTimeAdd) * val3.reloadTimeMultiplier;
				float after3 = (val3.reloadTime + val3.reloadTimeAdd + component.reloadTimeAdd) * (val3.reloadTimeMultiplier * component.reloadTime);
				TryAdd(list, "Reload Time", before3, after3, "f2", "s");
				TryAdd(list, "Ammo", val3.maxAmmo, Mathf.Clamp(val3.maxAmmo + component.ammo, 1, 90), "f0");
			}
			return list;
		}

		internal static bool HasStatTemplate(Gun cardGun, CharacterStatModifiers cardPlayer, Block cardBlock)
		{
			if ((Object)(object)cardGun != (Object)null && GunHasChanges(cardGun))
			{
				return true;
			}
			if ((Object)(object)cardPlayer != (Object)null && PlayerHasChanges(cardPlayer))
			{
				return true;
			}
			if ((Object)(object)cardBlock != (Object)null && BlockHasChanges(cardBlock))
			{
				return true;
			}
			return false;
		}

		private static bool GunHasChanges(Gun g)
		{
			if (Mathf.Approximately(g.damage, 1f) && Mathf.Approximately(g.bulletDamageMultiplier, 1f) && Mathf.Approximately(g.knockback, 1f) && Mathf.Approximately(g.attackSpeed, 1f) && Mathf.Approximately(g.projectileSpeed, 1f) && Mathf.Approximately(g.projectielSimulatonSpeed, 1f) && Mathf.Approximately(g.gravity, 1f) && Mathf.Approximately(g.damageAfterDistanceMultiplier, 1f) && Mathf.Approximately(g.reloadTime, 1f) && Mathf.Approximately(g.reloadTimeAdd, 0f) && g.ammo == 0 && g.numberOfProjectiles == 0 && g.reflects == 0 && g.bursts == 0 && g.slow == 0f)
			{
				return g.destroyBulletAfter != 0f;
			}
			return true;
		}

		private static bool PlayerHasChanges(CharacterStatModifiers s)
		{
			if (Mathf.Approximately(s.health, 1f) && Mathf.Approximately(s.movementSpeed, 1f) && Mathf.Approximately(s.jump, 1f) && Mathf.Approximately(s.sizeMultiplier, 1f) && s.respawns == 0)
			{
				return s.lifeSteal != 0f;
			}
			return true;
		}

		private static bool BlockHasChanges(Block b)
		{
			if (Mathf.Approximately(b.cdMultiplier, 1f) && b.cdAdd == 0f)
			{
				return b.additionalBlocks != 0;
			}
			return true;
		}

		private static float ProjectileBlendFactor(int cardProjectiles, int currentProjectiles)
		{
			if (cardProjectiles == 0 || currentProjectiles == 1)
			{
				return 1f;
			}
			return (float)cardProjectiles / (float)(cardProjectiles + currentProjectiles);
		}

		private static float ApplyGunDamage(float currentDamage, int currentProjectiles, Gun cardGun)
		{
			float num = ProjectileBlendFactor(cardGun.numberOfProjectiles, currentProjectiles);
			return Mathf.Max(currentDamage * (1f - num * (1f - cardGun.damage)), 0.25f);
		}

		private static float ApplyKnockback(float currentKnockback, int currentProjectiles, Gun cardGun)
		{
			float num = ProjectileBlendFactor(cardGun.numberOfProjectiles, currentProjectiles);
			return currentKnockback * (1f - num * (1f - cardGun.knockback));
		}

		private static void TryAdd(List<StatDeltaLine> lines, string label, float before, float after, string format, string suffix = "")
		{
			if (!(Mathf.Abs(before - after) < 0.0001f))
			{
				lines.Add(new StatDeltaLine(label, before.ToString(format) + suffix, after.ToString(format) + suffix));
			}
		}
	}
	internal static class StatTemplateDeltaRegistrar
	{
		internal static void Init()
		{
			CardManager.AddAllCardsCallback((Action<CardInfo[]>)RegisterAll);
			IOLog.Line("StatTemplateDeltaRegistrar — waiting for CardManager.FirstTimeStart.");
		}

		private static void RegisterAll(CardInfo[] cards)
		{
			IOLog.Section("StatTemplateDeltaRegistrar — RegisterAll");
			int num = 0;
			int num2 = 0;
			foreach (CardInfo val in cards)
			{
				if ((Object)(object)val == (Object)null || string.IsNullOrEmpty(val.cardName))
				{
					continue;
				}
				if (CardDeltaRegistry.IsRegistered(val.cardName))
				{
					num2++;
					continue;
				}
				GameObject obj = (((Object)(object)val.sourceCard != (Object)null) ? ((Component)val.sourceCard).gameObject : ((Component)val).gameObject);
				Gun component = obj.GetComponent<Gun>();
				CharacterStatModifiers component2 = obj.GetComponent<CharacterStatModifiers>();
				Block componentInChildren = obj.GetComponentInChildren<Block>();
				if (StatTemplateDeltaProvider.HasStatTemplate(component, component2, componentInChildren))
				{
					CardDeltaRegistry.Register(val.cardName, StatTemplateDeltaProvider.Compute);
					num++;
				}
			}
			IOLog.Line($"Registered {num} stat-template cards (skipped {num2} already registered).");
		}
	}
}
namespace InfoOverhaul.Compat
{
	internal static class MfmTier2Compat
	{
		internal static void Init()
		{
			CardManager.AddAllCardsCallback((Action<CardInfo[]>)delegate
			{
				RegisterEffectNotes();
			});
		}

		private static void RegisterEffectNotes()
		{
			IOLog.Section("MfmTier2Compat — RegisterEffectNotes");
			IOLog.Line("MfmTier2Compat ready (no notes registered yet — add per card as validated).");
		}
	}
}