Decompiled source of UpgradeDraft v1.0.13

plugins\UpgradeDraft\UpgradeDraft.dll

Decompiled 2 weeks 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.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using ModdingUtils.Utils;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.UI;
using UpgradeDraft.Models;
using UpgradeDraft.Services;
using UpgradeDraft.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("0.0.0.0")]
[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 UpgradeDraft
{
	[BepInPlugin("com.damian.rounds.upgradedraft", "Upgrade Draft", "1.0.13")]
	[BepInProcess("Rounds.exe")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class UpgradeDraftPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.damian.rounds.upgradedraft";

		public const string PluginName = "Upgrade Draft";

		public const string PluginVersion = "1.0.13";

		private static readonly int[] DefaultChances = new int[4] { 25, 50, 20, 5 };

		private Harmony _harmony;

		private int _chanceZero;

		private int _chanceOne;

		private int _chanceTwo;

		private int _chanceThree;

		internal static UpgradeDraftPlugin Instance { get; private set; }

		internal static ManualLogSource Log => ((BaseUnityPlugin)Instance).Logger;

		internal static PlayerDeckService DeckService { get; private set; }

		internal static DraftRollService RollService { get; private set; }

		internal static CardSelectionService SelectionService { get; private set; }

		internal static UpgradePreviewService PreviewService { get; private set; }

		internal static UpgradeCardVisualService VisualService { get; private set; }

		internal static DraftCardNetworkService NetworkService { get; private set; }

		internal static UpgradeDraftState State { get; private set; }

		internal static SkipRerollService SkipRerollService { get; private set; }

		internal ConfigEntry<int> NormalCardCount { get; private set; }

		internal ConfigEntry<int> ChanceZeroUpgrades { get; private set; }

		internal ConfigEntry<int> ChanceOneUpgrade { get; private set; }

		internal ConfigEntry<int> ChanceTwoUpgrades { get; private set; }

		internal ConfigEntry<int> ChanceThreeUpgrades { get; private set; }

		internal ConfigEntry<bool> EnableUpgradeVisuals { get; private set; }

		internal ConfigEntry<bool> EnableStatPreview { get; private set; }

		internal ConfigEntry<string> UpgradeBlacklist { get; private set; }

		internal ConfigEntry<bool> VerboseLogging { get; private set; }

		internal ConfigEntry<bool> EnableSkipForPoint { get; private set; }

		internal ConfigEntry<int> SkipPointsForReroll { get; private set; }

		internal ConfigEntry<bool> EnableSkipRerollOverlay { get; private set; }

		internal ConfigEntry<bool> AllowMultipleRerollsPerPick { get; private set; }

		private void Awake()
		{
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Expected O, but got Unknown
			Instance = this;
			BindConfig();
			ValidateConfig();
			State = new UpgradeDraftState(((BaseUnityPlugin)this).Logger);
			DeckService = new PlayerDeckService(((BaseUnityPlugin)this).Logger, this);
			RollService = new DraftRollService(((BaseUnityPlugin)this).Logger, this);
			SelectionService = new CardSelectionService(((BaseUnityPlugin)this).Logger, this, DeckService);
			PreviewService = new UpgradePreviewService(((BaseUnityPlugin)this).Logger, this);
			VisualService = new UpgradeCardVisualService(((BaseUnityPlugin)this).Logger, this, PreviewService);
			NetworkService = new DraftCardNetworkService(((BaseUnityPlugin)this).Logger, this, VisualService);
			SkipRerollService = new SkipRerollService(((BaseUnityPlugin)this).Logger, this);
			if ((Object)(object)((Component)this).GetComponent<SkipRerollOverlay>() == (Object)null)
			{
				((Component)this).gameObject.AddComponent<SkipRerollOverlay>();
			}
			_harmony = new Harmony("com.damian.rounds.upgradedraft");
			_harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Upgrade Draft 1.0.13 loaded.");
		}

		private void OnDestroy()
		{
			NetworkService?.Dispose();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		internal int GetCardCount()
		{
			int num = Math.Max(1, NormalCardCount.Value);
			if (num != 4)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)$"NormalCardCount is set to {num}. Values other than 4 are experimental.");
			}
			return num;
		}

		internal (int Zero, int One, int Two, int Three) GetWeightedChances()
		{
			return (_chanceZero, _chanceOne, _chanceTwo, _chanceThree);
		}

		internal bool IsVisualEnabled()
		{
			return EnableUpgradeVisuals.Value;
		}

		internal bool IsStatPreviewEnabled()
		{
			return EnableStatPreview.Value;
		}

		internal bool IsVerboseLoggingEnabled()
		{
			return VerboseLogging.Value;
		}

		internal bool IsSkipForPointEnabled()
		{
			return EnableSkipForPoint.Value;
		}

		internal bool IsSkipRerollOverlayEnabled()
		{
			return EnableSkipRerollOverlay.Value;
		}

		internal int GetSkipPointsForReroll()
		{
			return Math.Max(1, SkipPointsForReroll.Value);
		}

		internal bool IsMultipleRerollsPerPickAllowed()
		{
			return AllowMultipleRerollsPerPick.Value;
		}

		internal HashSet<string> GetBlacklist()
		{
			return new HashSet<string>(from v in (UpgradeBlacklist.Value ?? string.Empty).Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries)
				select v.Trim().ToLowerInvariant() into v
				where !string.IsNullOrWhiteSpace(v)
				select v);
		}

		private void BindConfig()
		{
			NormalCardCount = ((BaseUnityPlugin)this).Config.Bind<int>("General", "NormalCardCount", 4, "Number of visible card choices per draft pick. Values other than 4 are experimental.");
			ChanceZeroUpgrades = ((BaseUnityPlugin)this).Config.Bind<int>("Chances", "ChanceZeroUpgrades", 25, "Chance weight for showing 0 upgrade cards.");
			ChanceOneUpgrade = ((BaseUnityPlugin)this).Config.Bind<int>("Chances", "ChanceOneUpgrade", 50, "Chance weight for showing 1 upgrade card.");
			ChanceTwoUpgrades = ((BaseUnityPlugin)this).Config.Bind<int>("Chances", "ChanceTwoUpgrades", 20, "Chance weight for showing 2 upgrade cards.");
			ChanceThreeUpgrades = ((BaseUnityPlugin)this).Config.Bind<int>("Chances", "ChanceThreeUpgrades", 5, "Chance weight for showing 3 upgrade cards.");
			EnableUpgradeVisuals = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "EnableUpgradeVisuals", true, "Adds UPGRADE visuals and ownership info to upgrade cards.");
			EnableStatPreview = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "EnableStatPreview", true, "Appends conservative stacked stat preview lines for upgrade cards.");
			EnableSkipRerollOverlay = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "EnableSkipRerollOverlay", true, "Show the Upgrade Draft status panel during card pick.");
			UpgradeBlacklist = ((BaseUnityPlugin)this).Config.Bind<string>("Rules", "UpgradeBlacklist", string.Empty, "Comma-separated card names that are not allowed as upgrades.");
			EnableSkipForPoint = ((BaseUnityPlugin)this).Config.Bind<bool>("Rules", "EnableSkipForPoint", true, "Enable the 5th SKIP action card. If disabled, the action card is still shown as a no-stats pass card for compatibility.");
			SkipPointsForReroll = ((BaseUnityPlugin)this).Config.Bind<int>("Rules", "SkipPointsForReroll", 2, "Skip points required for the 5th action card to become REROLL.");
			AllowMultipleRerollsPerPick = ((BaseUnityPlugin)this).Config.Bind<bool>("Rules", "AllowMultipleRerollsPerPick", false, "Allow repeatedly picking REROLL during the same card choice.");
			VerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "VerboseLogging", false, "Enable extra debug logging.");
		}

		private void ValidateConfig()
		{
			NormalCardCount.Value = Math.Max(1, NormalCardCount.Value);
			SkipPointsForReroll.Value = Math.Max(1, SkipPointsForReroll.Value);
			int num = Math.Max(0, ChanceZeroUpgrades.Value);
			int num2 = Math.Max(0, ChanceOneUpgrade.Value);
			int num3 = Math.Max(0, ChanceTwoUpgrades.Value);
			int num4 = Math.Max(0, ChanceThreeUpgrades.Value);
			int num5 = num + num2 + num3 + num4;
			if (num5 <= 0)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Upgrade chance weights are invalid (sum <= 0). Falling back to defaults: 25/50/20/5.");
				_chanceZero = DefaultChances[0];
				_chanceOne = DefaultChances[1];
				_chanceTwo = DefaultChances[2];
				_chanceThree = DefaultChances[3];
				return;
			}
			if (num5 == 100)
			{
				_chanceZero = num;
				_chanceOne = num2;
				_chanceTwo = num3;
				_chanceThree = num4;
				return;
			}
			double num6 = 100.0 / (double)num5;
			_chanceZero = (int)Math.Round((double)num * num6);
			_chanceOne = (int)Math.Round((double)num2 * num6);
			_chanceTwo = (int)Math.Round((double)num3 * num6);
			_chanceThree = (int)Math.Round((double)num4 * num6);
			int num7 = _chanceZero + _chanceOne + _chanceTwo + _chanceThree;
			int num8 = 100 - num7;
			_chanceOne += num8;
			((BaseUnityPlugin)this).Logger.LogWarning((object)$"Upgrade chance weights sum to {num5} instead of 100. Normalized to {_chanceZero}/{_chanceOne}/{_chanceTwo}/{_chanceThree}.");
		}
	}
}
namespace UpgradeDraft.UI
{
	public sealed class DraftActionCardMarker : MonoBehaviour
	{
		public DraftActionType ActionType;

		public int PickerId;
	}
	public sealed class SkipRerollOverlay : MonoBehaviour
	{
		private GUIStyle _badgeStyle;

		private GUIStyle _actionStyle;

		private GUIStyle _labelStyle;

		private GUIStyle _titleStyle;

		private void Awake()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: 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_0076: Expected O, but got Unknown
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: 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_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Expected O, but got Unknown
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: 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_00d0: 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_00e5: Expected O, but got Unknown
			GUIStyle val = new GUIStyle(GUI.skin.box)
			{
				fontSize = 22,
				fontStyle = (FontStyle)1,
				alignment = (TextAnchor)4
			};
			val.normal.textColor = Color.cyan;
			_badgeStyle = val;
			GUIStyle val2 = new GUIStyle(GUI.skin.box)
			{
				fontSize = 18,
				fontStyle = (FontStyle)1,
				alignment = (TextAnchor)4
			};
			val2.normal.textColor = Color.white;
			_actionStyle = val2;
			GUIStyle val3 = new GUIStyle(GUI.skin.label)
			{
				fontSize = 15,
				alignment = (TextAnchor)3
			};
			val3.normal.textColor = Color.white;
			_labelStyle = val3;
			GUIStyle val4 = new GUIStyle(GUI.skin.label)
			{
				fontSize = 18,
				fontStyle = (FontStyle)1,
				alignment = (TextAnchor)3
			};
			val4.normal.textColor = Color.yellow;
			_titleStyle = val4;
		}

		private void Update()
		{
			if (IsReady())
			{
				CardChoice instance = CardChoice.instance;
				_ = instance.pickrID;
				if (UpgradeDraftPlugin.SkipRerollService.IsCardChoiceOpen(instance) && !UpgradeDraftPlugin.SkipRerollService.IsCardChoiceBusy(instance))
				{
					UpgradeDraftPlugin.SkipRerollService.IsLocalClientControllingPick(instance);
				}
			}
		}

		private void OnGUI()
		{
			//IL_00a2: 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_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			if (!IsReady())
			{
				return;
			}
			CardChoice instance = CardChoice.instance;
			if (instance == null)
			{
				return;
			}
			int pickrID = instance.pickrID;
			if (UpgradeDraftPlugin.SkipRerollService.IsCardChoiceOpen(instance))
			{
				int skipPoints = UpgradeDraftPlugin.State.GetSkipPoints(pickrID);
				bool num = UpgradeDraftPlugin.SkipRerollService.IsLocalClientControllingPick(instance);
				int skipPointsForReroll = UpgradeDraftPlugin.Instance.GetSkipPointsForReroll();
				string text = ((skipPoints >= skipPointsForReroll) ? "REROLL" : "SKIP");
				string text2 = ((skipPoints >= skipPointsForReroll) ? "Pick the 5th card to redraw this choice." : "Pick the 5th card to pass and gain a point.");
				float num2 = 390f;
				float num3 = 150f;
				float num4 = 24f;
				float num5 = Mathf.Max(24f, (float)Screen.height * 0.18f);
				GUI.Box(new Rect(num4, num5, num2, num3), string.Empty);
				GUI.Box(new Rect(num4 + 12f, num5 + 12f, 54f, 54f), "UD", _badgeStyle);
				GUI.Label(new Rect(num4 + 78f, num5 + 10f, num2 - 90f, 28f), "Upgrade Draft", _titleStyle);
				GUI.Label(new Rect(num4 + 78f, num5 + 38f, num2 - 90f, 22f), $"Skip Points: {skipPoints}/{skipPointsForReroll}", _labelStyle);
				GUI.Label(new Rect(num4 + 78f, num5 + 60f, num2 - 90f, 22f), "4 draft cards + 1 action card", _labelStyle);
				GUI.Box(new Rect(num4 + 16f, num5 + 88f, 110f, 34f), text, _actionStyle);
				if (!num)
				{
					GUI.Label(new Rect(num4 + 138f, num5 + 94f, num2 - 154f, 22f), "Waiting for the picking player.", _labelStyle);
				}
				else
				{
					GUI.Label(new Rect(num4 + 138f, num5 + 94f, num2 - 154f, 22f), text2, _labelStyle);
				}
			}
		}

		private static bool IsReady()
		{
			if ((Object)(object)UpgradeDraftPlugin.Instance == (Object)null || UpgradeDraftPlugin.State == null || UpgradeDraftPlugin.SkipRerollService == null)
			{
				return false;
			}
			if (!UpgradeDraftPlugin.Instance.IsSkipRerollOverlayEnabled())
			{
				return false;
			}
			return CardChoice.instance != null;
		}
	}
	public sealed class UpgradeCardMarker : MonoBehaviour
	{
		public bool IsUpgrade;

		public int OwnedBefore;

		public int OwnedAfter;

		public string SourceCardName;
	}
	public sealed class UpgradeCardVisualService
	{
		private readonly ManualLogSource _logger;

		private readonly UpgradeDraftPlugin _plugin;

		private readonly UpgradePreviewService _previewService;

		public UpgradeCardVisualService(ManualLogSource logger, UpgradeDraftPlugin plugin, UpgradePreviewService previewService)
		{
			_logger = logger;
			_plugin = plugin;
			_previewService = previewService;
		}

		public void ApplyUpgradeVisual(GameObject spawnedCard, DraftCardEntry entry)
		{
			if (!_plugin.IsVisualEnabled() || spawnedCard == null || entry == null)
			{
				return;
			}
			try
			{
				PrepareChoiceCard(spawnedCard);
				UpgradeCardMarker obj = spawnedCard.GetComponent<UpgradeCardMarker>() ?? spawnedCard.AddComponent<UpgradeCardMarker>();
				obj.IsUpgrade = true;
				obj.OwnedBefore = entry.OwnedBefore;
				obj.OwnedAfter = entry.OwnedAfter;
				obj.SourceCardName = entry.Card.cardName;
				ApplyTextComponents(spawnedCard, entry);
			}
			catch (Exception ex)
			{
				_logger.LogWarning((object)("Failed to apply upgrade visuals on card object '" + ((Object)spawnedCard).name + "'. Error: " + ex.Message));
			}
		}

		public void ApplyUpgradeVisual(GameObject spawnedCard, string sourceCardName, int ownedBefore, int ownedAfter)
		{
			if (!_plugin.IsVisualEnabled() || spawnedCard == null)
			{
				return;
			}
			try
			{
				PrepareChoiceCard(spawnedCard);
				CardInfo component = spawnedCard.GetComponent<CardInfo>();
				if (component != null)
				{
					UpgradeCardMarker upgradeCardMarker = spawnedCard.GetComponent<UpgradeCardMarker>() ?? spawnedCard.AddComponent<UpgradeCardMarker>();
					upgradeCardMarker.IsUpgrade = true;
					upgradeCardMarker.OwnedBefore = ((ownedBefore >= 0) ? ownedBefore : 0);
					upgradeCardMarker.OwnedAfter = ((ownedAfter < 0) ? (upgradeCardMarker.OwnedBefore + 1) : ownedAfter);
					upgradeCardMarker.SourceCardName = (string.IsNullOrWhiteSpace(sourceCardName) ? component.cardName : sourceCardName);
					ApplyUpgradeTextComponents(spawnedCard, upgradeCardMarker.SourceCardName, upgradeCardMarker.OwnedBefore, upgradeCardMarker.OwnedAfter);
				}
			}
			catch (Exception ex)
			{
				_logger.LogWarning((object)("Failed to apply networked upgrade visuals on card object '" + ((Object)spawnedCard).name + "'. Error: " + ex.Message));
			}
		}

		public void ApplyActionVisual(GameObject spawnedCard, DraftCardEntry entry, int pickerId)
		{
			if (spawnedCard != null && entry != null && entry.IsAction)
			{
				ApplyActionVisual(spawnedCard, entry.ActionType, pickerId);
			}
		}

		public void ApplyActionVisual(GameObject spawnedCard, DraftActionType actionType, int pickerId)
		{
			if (spawnedCard == null || actionType == DraftActionType.None)
			{
				return;
			}
			try
			{
				PrepareChoiceCard(spawnedCard);
				DraftActionCardMarker obj = spawnedCard.GetComponent<DraftActionCardMarker>() ?? spawnedCard.AddComponent<DraftActionCardMarker>();
				obj.ActionType = actionType;
				obj.PickerId = pickerId;
				ApplyActionTextComponents(spawnedCard, actionType);
			}
			catch (Exception ex)
			{
				_logger.LogWarning((object)("Failed to apply action card visuals on card object '" + ((Object)spawnedCard).name + "'. Error: " + ex.Message));
			}
		}

		public void PrepareChoiceCard(GameObject spawnedCard)
		{
			if (spawnedCard == null)
			{
				return;
			}
			try
			{
				Collider2D[] componentsInChildren = spawnedCard.GetComponentsInChildren<Collider2D>(true);
				foreach (Collider2D val in componentsInChildren)
				{
					if (val != null)
					{
						((Behaviour)val).enabled = false;
					}
				}
				Collider[] componentsInChildren2 = spawnedCard.GetComponentsInChildren<Collider>(true);
				foreach (Collider val2 in componentsInChildren2)
				{
					if (val2 != null)
					{
						val2.enabled = false;
					}
				}
				Rigidbody[] componentsInChildren3 = spawnedCard.GetComponentsInChildren<Rigidbody>(true);
				foreach (Rigidbody val3 in componentsInChildren3)
				{
					if (val3 != null)
					{
						val3.detectCollisions = false;
						val3.isKinematic = true;
					}
				}
				Rigidbody2D[] componentsInChildren4 = spawnedCard.GetComponentsInChildren<Rigidbody2D>(true);
				foreach (Rigidbody2D val4 in componentsInChildren4)
				{
					if (val4 != null)
					{
						val4.simulated = false;
					}
				}
			}
			catch (Exception ex)
			{
				if (_plugin.IsVerboseLoggingEnabled())
				{
					_logger.LogWarning((object)("Failed to prepare card-choice physics for '" + ((Object)spawnedCard).name + "'. Error: " + ex.Message));
				}
			}
		}

		private void ApplyTextComponents(GameObject spawnedCard, DraftCardEntry entry)
		{
			string titleText = _previewService.BuildUpgradeTitle(entry);
			string descText = BuildShortUpgradeDescription(entry);
			ApplyTextComponents(spawnedCard, titleText, descText, "Upgrade card");
		}

		private void ApplyUpgradeTextComponents(GameObject spawnedCard, string sourceCardName, int ownedBefore, int ownedAfter)
		{
			string titleText = "UPGRADE: " + sourceCardName;
			string descText = $"Upgrade: grants another copy. Owned: {ownedBefore} -> {ownedAfter}";
			ApplyTextComponents(spawnedCard, titleText, descText, "Networked upgrade card");
		}

		private void ApplyTextComponents(GameObject spawnedCard, string titleText, string descText, string label)
		{
			List<Component> list = (from c in spawnedCard.GetComponentsInChildren<Component>(true)
				where (Object)(object)c != (Object)null
				where HasStringTextProperty(((object)c).GetType())
				select c).ToList();
			if (list.Count == 0)
			{
				_logger.LogWarning((object)(label + " '" + ((Object)spawnedCard).name + "' has no discovered text components to patch."));
				return;
			}
			bool flag = false;
			foreach (Component item in list)
			{
				string text = ((Object)item.gameObject).name.ToLowerInvariant();
				if (!string.IsNullOrWhiteSpace(GetTextProperty(item)))
				{
					if (text.Contains("title") || text.Contains("name"))
					{
						SetTextProperty(item, titleText);
						flag = true;
					}
					else if (text.Contains("desc") || text.Contains("description"))
					{
						SetTextProperty(item, descText);
						flag = true;
					}
				}
			}
			if (!flag)
			{
				_logger.LogWarning((object)(label + " '" + ((Object)spawnedCard).name + "' did not expose recognizable title/description text components."));
			}
		}

		private void ApplyActionTextComponents(GameObject spawnedCard, DraftActionType actionType)
		{
			string value = ((actionType == DraftActionType.Reroll) ? "REROLL" : "SKIP");
			string value2 = ((actionType == DraftActionType.Reroll) ? "Spend skip points to redraw this card choice." : "Gain 1 skip point and pass this card choice.");
			foreach (Component item in (from c in spawnedCard.GetComponentsInChildren<Component>(true)
				where (Object)(object)c != (Object)null
				where HasStringTextProperty(((object)c).GetType())
				select c).ToList())
			{
				string text = ((Object)item.gameObject).name.ToLowerInvariant();
				if (!string.IsNullOrWhiteSpace(GetTextProperty(item)))
				{
					if (text.Contains("title") || text.Contains("name"))
					{
						SetTextProperty(item, value);
					}
					else if (text.Contains("desc") || text.Contains("description"))
					{
						SetTextProperty(item, value2);
					}
				}
			}
		}

		private static string BuildShortUpgradeDescription(DraftCardEntry entry)
		{
			string text = entry.Card.cardDestription ?? string.Empty;
			text = text.Trim();
			string text2 = $"Upgrade: grants another copy. Owned: {entry.OwnedBefore} -> {entry.OwnedAfter}";
			if (string.IsNullOrWhiteSpace(text))
			{
				return text2;
			}
			return text + "\n" + text2;
		}

		private static bool HasStringTextProperty(Type type)
		{
			PropertyInfo property = type.GetProperty("text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null && property.PropertyType == typeof(string) && property.CanRead)
			{
				return property.CanWrite;
			}
			return false;
		}

		private static string GetTextProperty(Component component)
		{
			return ((object)component).GetType().GetProperty("text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(component, null) as string;
		}

		private static void SetTextProperty(Component component, string value)
		{
			((object)component).GetType().GetProperty("text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.SetValue(component, value, null);
		}
	}
}
namespace UpgradeDraft.Services
{
	public sealed class CardSelectionService
	{
		private readonly ManualLogSource _logger;

		private readonly UpgradeDraftPlugin _plugin;

		private readonly PlayerDeckService _deckService;

		public CardSelectionService(ManualLogSource logger, UpgradeDraftPlugin plugin, PlayerDeckService deckService)
		{
			_logger = logger;
			_plugin = plugin;
			_deckService = deckService;
		}

		public List<DraftCardEntry> BuildDraftEntries(CardChoice cardChoice, Player player, DraftPlan plan)
		{
			HashSet<string> blacklist = _plugin.GetBlacklist();
			List<CardInfo> eligibleUpgradeCards = _deckService.GetEligibleUpgradeCards(player, blacklist);
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			List<DraftCardEntry> list = new List<DraftCardEntry>();
			foreach (CardInfo item in PickRandomDistinct(eligibleUpgradeCards, plan.ActualUpgradeCount))
			{
				int ownedCount = _deckService.GetOwnedCount(player, item);
				list.Add(new DraftCardEntry(item, isUpgrade: true, ownedCount));
				hashSet.Add(_deckService.GetStableCardId(item));
			}
			List<CardInfo> list2 = SelectNewCards(cardChoice, player, plan.NewCardCount, hashSet, strictUnique: true, strictNotOwned: true);
			if (list2.Count < plan.NewCardCount)
			{
				_logger.LogWarning((object)$"Unable to fill all new-card slots with strict unique+not-owned rules. Needed {plan.NewCardCount}, got {list2.Count}. Trying duplicate-new fallback.");
				int wanted = plan.NewCardCount - list2.Count;
				list2.AddRange(SelectNewCards(cardChoice, player, wanted, hashSet, strictUnique: false, strictNotOwned: true));
			}
			if (list2.Count < plan.NewCardCount)
			{
				_logger.LogWarning((object)$"Still missing new cards after duplicate-new fallback. Needed {plan.NewCardCount}, got {list2.Count}. Trying broad safety fallback.");
				int wanted2 = plan.NewCardCount - list2.Count;
				list2.AddRange(SelectNewCards(cardChoice, player, wanted2, hashSet, strictUnique: false, strictNotOwned: false));
			}
			foreach (CardInfo item2 in list2)
			{
				int ownedCount2 = _deckService.GetOwnedCount(player, item2);
				list.Add(new DraftCardEntry(item2, isUpgrade: false, ownedCount2));
				hashSet.Add(_deckService.GetStableCardId(item2));
			}
			if (list.Count < plan.TotalCardCount)
			{
				_logger.LogWarning((object)$"Draft pool has {list.Count}/{plan.TotalCardCount} cards after all selection fallbacks. Letting vanilla SpawnUniqueCard fill remainder as safety.");
			}
			return list;
		}

		private List<CardInfo> SelectNewCards(CardChoice cardChoice, Player player, int wanted, ISet<string> existingCardIds, bool strictUnique, bool strictNotOwned)
		{
			List<CardInfo> list = new List<CardInfo>();
			if (wanted <= 0)
			{
				return list;
			}
			int num = 0;
			int num2 = Math.Max(1000, wanted * 400);
			while (list.Count < wanted && num < num2)
			{
				num++;
				CardInfo randomCardWithCondition = Cards.instance.GetRandomCardWithCondition(player, (Gun)null, (GunAmmo)null, (CharacterData)null, (HealthHandler)null, (Gravity)null, (Block)null, (CharacterStatModifiers)null, BuildSelectionCondition(cardChoice, player, existingCardIds, list, strictUnique, strictNotOwned), 1000);
				if (randomCardWithCondition != null && (!strictNotOwned || !_deckService.PlayerOwnsCard(player, randomCardWithCondition)) && !_deckService.IsBlacklisted(randomCardWithCondition, _plugin.GetBlacklist()))
				{
					string stableCardId = _deckService.GetStableCardId(randomCardWithCondition);
					if (!strictUnique || !existingCardIds.Contains(stableCardId))
					{
						list.Add(randomCardWithCondition);
						existingCardIds.Add(stableCardId);
					}
				}
			}
			return list;
		}

		private Func<CardInfo, Player, Gun, GunAmmo, CharacterData, HealthHandler, Gravity, Block, CharacterStatModifiers, bool> BuildSelectionCondition(CardChoice cardChoice, Player player, ISet<string> selectedIds, IEnumerable<CardInfo> localNewCards, bool strictUnique, bool strictNotOwned)
		{
			HashSet<string> localIds = new HashSet<string>(localNewCards.Where((CardInfo c) => c != null).Select(_deckService.GetStableCardId), StringComparer.Ordinal);
			Func<CardInfo, Player, bool> baseCondition = BuildCardChoiceCompatibleCondition(cardChoice);
			return delegate(CardInfo card, Player p, Gun gun, GunAmmo gunAmmo, CharacterData data, HealthHandler health, Gravity gravity, Block block, CharacterStatModifiers stats)
			{
				if (card == null)
				{
					return false;
				}
				if (strictNotOwned && _deckService.PlayerOwnsCard(player, card))
				{
					return false;
				}
				if (_deckService.IsBlacklisted(card, _plugin.GetBlacklist()))
				{
					return false;
				}
				if (!Cards.instance.PlayerIsAllowedCard(player, card))
				{
					return false;
				}
				if (!baseCondition(card, player))
				{
					return false;
				}
				if (strictUnique)
				{
					string stableCardId = _deckService.GetStableCardId(card);
					if (selectedIds.Contains(stableCardId) || localIds.Contains(stableCardId))
					{
						return false;
					}
				}
				return true;
			};
		}

		private Func<CardInfo, Player, bool> BuildCardChoiceCompatibleCondition(CardChoice cardChoice)
		{
			return delegate(CardInfo card, Player player)
			{
				if (card == null)
				{
					return false;
				}
				try
				{
					if ((Object)(object)cardChoice != (Object)null && cardChoice.pickrID != -1)
					{
						object fieldOrPropertyValue = ReflectionUtils.GetFieldOrPropertyValue(((Component)player.data).GetComponent("Holding"), "holdable");
						Component val = (Component)((fieldOrPropertyValue is Component) ? fieldOrPropertyValue : null);
						if (val != null)
						{
							Gun component = val.GetComponent<Gun>();
							Gun component2 = ((Component)card).GetComponent<Gun>();
							if (component != null && component2 != null && component2.lockGunToDefault && component.lockGunToDefault)
							{
								return false;
							}
						}
					}
				}
				catch (Exception ex)
				{
					if (_plugin.IsVerboseLoggingEnabled())
					{
						_logger.LogWarning((object)("CardChoice-compatible condition failed to inspect lockGunToDefault: " + ex.Message));
					}
				}
				return true;
			};
		}

		private List<CardInfo> PickRandomDistinct(List<CardInfo> cards, int amount)
		{
			List<CardInfo> list = new List<CardInfo>();
			if (cards == null || cards.Count == 0 || amount <= 0)
			{
				return list;
			}
			List<CardInfo> list2 = cards.Where((CardInfo c) => c != null).ToList();
			for (int i = 0; i < list2.Count; i++)
			{
				int index = Random.Range(i, list2.Count);
				CardInfo value = list2[i];
				list2[i] = list2[index];
				list2[index] = value;
			}
			for (int j = 0; j < Math.Min(amount, list2.Count); j++)
			{
				list.Add(list2[j]);
			}
			return list;
		}
	}
	public sealed class DraftCardNetworkService : IDisposable
	{
		[CompilerGenerated]
		private sealed class <ApplyWhenSpawned>d__9 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public int viewId;

			public bool isUpgrade;

			public DraftCardNetworkService <>4__this;

			public string sourceName;

			public int ownedBefore;

			public int ownedAfter;

			public DraftActionType actionType;

			public int pickerId;

			private int <attempt>5__2;

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

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

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

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

			private bool MoveNext()
			{
				int num = <>1__state;
				DraftCardNetworkService draftCardNetworkService = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<attempt>5__2 = 0;
					break;
				case 1:
					<>1__state = -1;
					<attempt>5__2++;
					break;
				}
				if (<attempt>5__2 < 90)
				{
					PhotonView photonView = PhotonNetwork.GetPhotonView(viewId);
					if (photonView != null && ((Component)photonView).gameObject != null)
					{
						if (isUpgrade)
						{
							draftCardNetworkService._visualService.ApplyUpgradeVisual(((Component)photonView).gameObject, sourceName, ownedBefore, ownedAfter);
						}
						else if (actionType != 0)
						{
							draftCardNetworkService._visualService.ApplyActionVisual(((Component)photonView).gameObject, actionType, pickerId);
						}
						else
						{
							draftCardNetworkService._visualService.PrepareChoiceCard(((Component)photonView).gameObject);
						}
						return false;
					}
					<>2__current = null;
					<>1__state = 1;
					return true;
				}
				if (draftCardNetworkService._plugin.IsVerboseLoggingEnabled())
				{
					draftCardNetworkService._logger.LogWarning((object)$"Timed out waiting for PhotonView {viewId} to apply UpgradeDraft visual sync.");
				}
				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();
			}
		}

		private const byte VisualEventCode = 87;

		private const int MaxApplyAttempts = 90;

		private readonly ManualLogSource _logger;

		private readonly UpgradeDraftPlugin _plugin;

		private readonly UpgradeCardVisualService _visualService;

		public DraftCardNetworkService(ManualLogSource logger, UpgradeDraftPlugin plugin, UpgradeCardVisualService visualService)
		{
			_logger = logger;
			_plugin = plugin;
			_visualService = visualService;
			PhotonNetwork.NetworkingClient.EventReceived += OnEventReceived;
		}

		public void Dispose()
		{
			if (PhotonNetwork.NetworkingClient != null)
			{
				PhotonNetwork.NetworkingClient.EventReceived -= OnEventReceived;
			}
		}

		public void BroadcastSpawn(GameObject spawnedCard, DraftCardEntry entry, int pickerId)
		{
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Expected O, but got Unknown
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			if (spawnedCard != null && entry != null)
			{
				PhotonView component = spawnedCard.GetComponent<PhotonView>();
				if (component != null)
				{
					object[] array = new object[7]
					{
						component.ViewID,
						entry.IsUpgrade,
						(int)entry.ActionType,
						pickerId,
						entry.OwnedBefore,
						entry.OwnedAfter,
						((Object)(object)entry.Card != (Object)null) ? entry.Card.cardName : string.Empty
					};
					RaiseEventOptions val = new RaiseEventOptions
					{
						Receivers = (ReceiverGroup)0
					};
					PhotonNetwork.RaiseEvent((byte)87, (object)array, val, SendOptions.SendReliable);
				}
			}
		}

		private void OnEventReceived(EventData photonEvent)
		{
			if (photonEvent.Code != 87 || !(photonEvent.CustomData is object[] array) || array.Length < 7)
			{
				return;
			}
			try
			{
				int viewId = Convert.ToInt32(array[0]);
				bool isUpgrade = Convert.ToBoolean(array[1]);
				DraftActionType actionType = (DraftActionType)Convert.ToInt32(array[2]);
				int pickerId = Convert.ToInt32(array[3]);
				int ownedBefore = Convert.ToInt32(array[4]);
				int ownedAfter = Convert.ToInt32(array[5]);
				string sourceName = (array[6] as string) ?? string.Empty;
				((MonoBehaviour)_plugin).StartCoroutine(ApplyWhenSpawned(viewId, isUpgrade, actionType, pickerId, ownedBefore, ownedAfter, sourceName));
			}
			catch (Exception ex)
			{
				_logger.LogWarning((object)("Failed to parse UpgradeDraft visual sync event: " + ex.Message));
			}
		}

		[IteratorStateMachine(typeof(<ApplyWhenSpawned>d__9))]
		private IEnumerator ApplyWhenSpawned(int viewId, bool isUpgrade, DraftActionType actionType, int pickerId, int ownedBefore, int ownedAfter, string sourceName)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <ApplyWhenSpawned>d__9(0)
			{
				<>4__this = this,
				viewId = viewId,
				isUpgrade = isUpgrade,
				actionType = actionType,
				pickerId = pickerId,
				ownedBefore = ownedBefore,
				ownedAfter = ownedAfter,
				sourceName = sourceName
			};
		}
	}
	public sealed class DraftRollService
	{
		private readonly ManualLogSource _logger;

		private readonly UpgradeDraftPlugin _plugin;

		public DraftRollService(ManualLogSource logger, UpgradeDraftPlugin plugin)
		{
			_logger = logger;
			_plugin = plugin;
		}

		public DraftPlan BuildPlan(int eligibleUpgradeCount)
		{
			int cardCount = _plugin.GetCardCount();
			int rollValue;
			int num = RollDesiredUpgradeCount(out rollValue);
			int num2 = Math.Min(Math.Max(0, num), Math.Max(0, eligibleUpgradeCount));
			DraftPlan draftPlan = new DraftPlan
			{
				DesiredUpgradeCount = num,
				ActualUpgradeCount = num2,
				NewCardCount = Math.Max(0, cardCount - num2),
				TotalCardCount = cardCount,
				RollValue = rollValue,
				FallbackReason = string.Empty
			};
			if (num2 != num)
			{
				draftPlan.FallbackReason = $"Clamped upgrades from desired {num} to {num2} because only {eligibleUpgradeCount} eligible upgrade card(s) exist.";
				_logger.LogWarning((object)draftPlan.FallbackReason);
			}
			return draftPlan;
		}

		private int RollDesiredUpgradeCount(out int rollValue)
		{
			(int, int, int, int) weightedChances = _plugin.GetWeightedChances();
			rollValue = Random.Range(0, 100);
			int item = weightedChances.Item1;
			int num = item + weightedChances.Item2;
			int num2 = num + weightedChances.Item3;
			if (rollValue < item)
			{
				return 0;
			}
			if (rollValue < num)
			{
				return 1;
			}
			if (rollValue < num2)
			{
				return 2;
			}
			return 3;
		}
	}
	public sealed class PlayerDeckService
	{
		private readonly ManualLogSource _logger;

		private readonly UpgradeDraftPlugin _plugin;

		public PlayerDeckService(ManualLogSource logger, UpgradeDraftPlugin plugin)
		{
			_logger = logger;
			_plugin = plugin;
		}

		public List<CardInfo> GetOwnedCards(Player player)
		{
			if (player == null)
			{
				return new List<CardInfo>();
			}
			try
			{
				object fieldOrPropertyValue = ReflectionUtils.GetFieldOrPropertyValue(player, "data");
				if (fieldOrPropertyValue == null)
				{
					return new List<CardInfo>();
				}
				List<CardInfo> list = ReflectionUtils.ToTypedList<CardInfo>(ReflectionUtils.GetFieldOrPropertyValue(fieldOrPropertyValue, "currentCards"));
				if (list.Count > 0)
				{
					return list;
				}
				list = ReflectionUtils.ToTypedList<CardInfo>(ReflectionUtils.GetFieldOrPropertyValue(fieldOrPropertyValue, "cards"));
				if (list.Count > 0)
				{
					return list;
				}
			}
			catch (Exception ex)
			{
				_logger.LogWarning((object)$"Failed to read owned cards for player {player.playerID}: {ex.Message}");
			}
			return new List<CardInfo>();
		}

		public int GetOwnedCount(Player player, CardInfo card)
		{
			if (player == null || card == null)
			{
				return 0;
			}
			string cardName = ((Object)((Component)card).gameObject).name;
			return GetOwnedCards(player).Count((CardInfo c) => c != null && string.Equals(((Object)((Component)c).gameObject).name, cardName, StringComparison.Ordinal));
		}

		public bool PlayerOwnsCard(Player player, CardInfo card)
		{
			return GetOwnedCount(player, card) > 0;
		}

		public List<CardInfo> GetEligibleUpgradeCards(Player player, ISet<string> blacklistLower)
		{
			List<CardInfo> ownedCards = GetOwnedCards(player);
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			List<CardInfo> list = new List<CardInfo>();
			foreach (CardInfo item in ownedCards)
			{
				if (IsEligibleUpgradeCard(player, item, blacklistLower))
				{
					string stableCardId = GetStableCardId(item);
					if (hashSet.Add(stableCardId))
					{
						list.Add(item);
					}
				}
			}
			return list;
		}

		public bool IsEligibleUpgradeCard(Player player, CardInfo card, ISet<string> blacklistLower)
		{
			if (player == null || card == null)
			{
				return false;
			}
			if (!PlayerOwnsCard(player, card))
			{
				return false;
			}
			if (!card.allowMultiple)
			{
				return false;
			}
			if (IsBlacklisted(card, blacklistLower))
			{
				return false;
			}
			if (!Cards.instance.PlayerIsAllowedCard(player, card))
			{
				return false;
			}
			return true;
		}

		public bool IsBlacklisted(CardInfo card, ISet<string> blacklistLower)
		{
			if (card == null || blacklistLower == null || blacklistLower.Count == 0)
			{
				return false;
			}
			string item = (card.cardName ?? string.Empty).Trim().ToLowerInvariant();
			string item2 = (((Object)(object)((Component)card).gameObject != (Object)null) ? ((Object)((Component)card).gameObject).name : string.Empty).Replace("(Clone)", string.Empty).Trim().ToLowerInvariant();
			if (!blacklistLower.Contains(item))
			{
				return blacklistLower.Contains(item2);
			}
			return true;
		}

		public string GetStableCardId(CardInfo card)
		{
			if (card == null)
			{
				return "null";
			}
			if (!string.IsNullOrWhiteSpace(card.cardName))
			{
				return card.cardName.Trim().ToLowerInvariant();
			}
			string text = (((Object)(object)((Component)card).gameObject != (Object)null) ? ((Object)((Component)card).gameObject).name : string.Empty);
			if (!string.IsNullOrWhiteSpace(text))
			{
				return text.Replace("(Clone)", string.Empty).Trim().ToLowerInvariant();
			}
			return ((Object)card).GetInstanceID().ToString();
		}
	}
	internal static class ReflectionUtils
	{
		internal static object GetFieldOrPropertyValue(object instance, string name)
		{
			if (instance == null || string.IsNullOrWhiteSpace(name))
			{
				return null;
			}
			Type type = instance.GetType();
			FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				return field.GetValue(instance);
			}
			return type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(instance, null);
		}

		internal static bool TrySetProperty(object instance, string propertyName, object value)
		{
			if (instance == null || string.IsNullOrWhiteSpace(propertyName))
			{
				return false;
			}
			PropertyInfo property = instance.GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property == null || !property.CanWrite)
			{
				return false;
			}
			property.SetValue(instance, value, null);
			return true;
		}

		internal static List<T> ToTypedList<T>(object maybeList) where T : class
		{
			if (maybeList == null)
			{
				return new List<T>();
			}
			if (maybeList is IList<T> source)
			{
				return source.Where((T item) => item != null).ToList();
			}
			if (maybeList is IEnumerable enumerable)
			{
				List<T> list = new List<T>();
				{
					foreach (object item3 in enumerable)
					{
						if (item3 is T item2)
						{
							list.Add(item2);
						}
					}
					return list;
				}
			}
			return new List<T>();
		}
	}
	public sealed class SkipRerollService
	{
		[CompilerGenerated]
		private sealed class <RerollAfterSelectionFrame>d__9 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public CardChoice cardChoice;

			public int pickerId;

			public SkipRerollService <>4__this;

			public int pointsSpent;

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

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

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

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

			private bool MoveNext()
			{
				int num = <>1__state;
				SkipRerollService skipRerollService = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = null;
					<>1__state = 1;
					return true;
				case 1:
				{
					<>1__state = -1;
					if (cardChoice == null)
					{
						return false;
					}
					cardChoice.pickrID = pickerId;
					UpgradeDraftPlugin.State.ClearDraftPlanOnly("Reroll action card picked.");
					Traverse val = Traverse.Create((object)cardChoice).Field("picks");
					int value = val.GetValue<int>();
					val.SetValue((object)(value + 1));
					if (AccessTools.Method(((object)cardChoice).GetType(), "ReplaceCards", (Type[])null, (Type[])null).Invoke(cardChoice, new object[2] { null, true }) is IEnumerator enumerator)
					{
						UpgradeDraftPlugin.State.MarkRerolledThisPick();
						((MonoBehaviour)cardChoice).StartCoroutine(enumerator);
						int skipPoints = UpgradeDraftPlugin.State.GetSkipPoints(pickerId);
						skipRerollService._logger.LogInfo((object)$"Player {pickerId} picked REROLL. Spent {pointsSpent} skip points (remaining: {skipPoints}).");
					}
					else
					{
						val.SetValue((object)value);
						skipRerollService._logger.LogWarning((object)"CardChoice.ReplaceCards did not return IEnumerator. Reroll action could not start coroutine.");
					}
					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();
			}
		}

		private readonly ManualLogSource _logger;

		private readonly UpgradeDraftPlugin _plugin;

		public SkipRerollService(ManualLogSource logger, UpgradeDraftPlugin plugin)
		{
			_logger = logger;
			_plugin = plugin;
		}

		public bool IsCardChoiceBusy(CardChoice cardChoice)
		{
			try
			{
				object value = Traverse.Create((object)cardChoice).Field("isPlaying").GetValue();
				if (value is bool)
				{
					return (bool)value;
				}
			}
			catch (Exception)
			{
			}
			return false;
		}

		public bool IsCardChoiceOpen(CardChoice cardChoice)
		{
			if (cardChoice == null || cardChoice.pickrID < 0)
			{
				return false;
			}
			try
			{
				object value = Traverse.Create((object)cardChoice).Field("IsPicking").GetValue();
				bool flag = default(bool);
				int num;
				if (value is bool)
				{
					flag = (bool)value;
					num = 1;
				}
				else
				{
					num = 0;
				}
				if (((uint)num & (flag ? 1u : 0u)) != 0)
				{
					return true;
				}
			}
			catch (Exception)
			{
			}
			try
			{
				if (Traverse.Create((object)cardChoice).Field("spawnedCards").GetValue() is ICollection collection)
				{
					return collection.Count > 0;
				}
			}
			catch (Exception)
			{
			}
			return false;
		}

		public bool IsLocalClientControllingPick(CardChoice cardChoice)
		{
			Player val = ResolvePickingPlayer(cardChoice);
			if (val == null)
			{
				return true;
			}
			try
			{
				CharacterData data = val.data;
				if (data == null)
				{
					return true;
				}
				object fieldOrPropertyValue = ReflectionUtils.GetFieldOrPropertyValue(data, "view");
				if (fieldOrPropertyValue == null)
				{
					return true;
				}
				object fieldOrPropertyValue2 = ReflectionUtils.GetFieldOrPropertyValue(fieldOrPropertyValue, "IsMine");
				if (fieldOrPropertyValue2 is bool)
				{
					return (bool)fieldOrPropertyValue2;
				}
			}
			catch (Exception ex)
			{
				if (_plugin.IsVerboseLoggingEnabled())
				{
					_logger.LogWarning((object)("Unable to resolve local pick ownership. Defaulting to visible controls. Error: " + ex.Message));
				}
			}
			return true;
		}

		public bool CanReroll(CardChoice cardChoice, int pickerId, out string reason)
		{
			reason = string.Empty;
			if (cardChoice == null || pickerId < 0)
			{
				reason = "invalid_card_choice";
				return false;
			}
			if (IsCardChoiceBusy(cardChoice))
			{
				reason = "card_choice_busy";
				return false;
			}
			int skipPointsForReroll = _plugin.GetSkipPointsForReroll();
			if (UpgradeDraftPlugin.State.GetSkipPoints(pickerId) < skipPointsForReroll)
			{
				reason = "not_enough_points";
				return false;
			}
			if (UpgradeDraftPlugin.State.RerolledThisPick && !_plugin.IsMultipleRerollsPerPickAllowed())
			{
				reason = "already_rerolled_this_pick";
				return false;
			}
			if (AccessTools.Method(((object)cardChoice).GetType(), "ReplaceCards", (Type[])null, (Type[])null) == null)
			{
				reason = "replace_cards_method_missing";
				return false;
			}
			return true;
		}

		private Player ResolvePickingPlayer(CardChoice cardChoice)
		{
			if (cardChoice == null || PlayerManager.instance == null)
			{
				return null;
			}
			try
			{
				if (Convert.ToInt32(Traverse.Create((object)cardChoice).Field("pickerType").GetValue()) == 0)
				{
					return PlayerManager.instance.GetPlayersInTeam(cardChoice.pickrID).FirstOrDefault();
				}
				if (cardChoice.pickrID >= 0 && cardChoice.pickrID < PlayerManager.instance.players.Count())
				{
					return PlayerManager.instance.players[cardChoice.pickrID];
				}
			}
			catch
			{
			}
			return null;
		}

		public bool TryReroll(CardChoice cardChoice, int pickerId)
		{
			if (!CanReroll(cardChoice, pickerId, out var reason))
			{
				if (_plugin.IsVerboseLoggingEnabled())
				{
					_logger.LogInfo((object)$"Reroll denied for player {pickerId}. Reason={reason}");
				}
				return false;
			}
			int skipPointsForReroll = _plugin.GetSkipPointsForReroll();
			if (!UpgradeDraftPlugin.State.TryConsumeSkipPoints(pickerId, skipPointsForReroll))
			{
				_logger.LogWarning((object)$"Failed to consume reroll points for player {pickerId}. This should not happen after CanReroll check.");
				return false;
			}
			((MonoBehaviour)cardChoice).StartCoroutine(RerollAfterSelectionFrame(cardChoice, pickerId, skipPointsForReroll));
			return true;
		}

		[IteratorStateMachine(typeof(<RerollAfterSelectionFrame>d__9))]
		private IEnumerator RerollAfterSelectionFrame(CardChoice cardChoice, int pickerId, int pointsSpent)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <RerollAfterSelectionFrame>d__9(0)
			{
				<>4__this = this,
				cardChoice = cardChoice,
				pickerId = pickerId,
				pointsSpent = pointsSpent
			};
		}
	}
	public sealed class UpgradeDraftState
	{
		private readonly ManualLogSource _logger;

		private readonly Queue<DraftCardEntry> _pendingEntries = new Queue<DraftCardEntry>();

		private readonly Dictionary<int, DraftCardEntry> _spawnedCardMap = new Dictionary<int, DraftCardEntry>();

		private readonly Dictionary<int, int> _skipPointsByPlayerId = new Dictionary<int, int>();

		private int _currentPickerId = -999;

		private bool _planReady;

		private bool _rerolledThisPick;

		public bool HasPlanForCurrentPick => _planReady;

		public int CurrentPickerId => _currentPickerId;

		public bool RerolledThisPick => _rerolledThisPick;

		public UpgradeDraftState(ManualLogSource logger)
		{
			_logger = logger;
		}

		public void BeginPick(int pickerId)
		{
			if (_currentPickerId != pickerId)
			{
				Clear($"Picker changed from {_currentPickerId} to {pickerId}.");
			}
			_currentPickerId = pickerId;
			_rerolledThisPick = false;
		}

		public void SetPlannedEntries(IEnumerable<DraftCardEntry> entries)
		{
			_pendingEntries.Clear();
			foreach (DraftCardEntry entry in entries)
			{
				_pendingEntries.Enqueue(entry);
			}
			_planReady = true;
		}

		public bool TryDequeueNext(out DraftCardEntry entry)
		{
			entry = null;
			if (_pendingEntries.Count == 0)
			{
				return false;
			}
			entry = _pendingEntries.Dequeue();
			return true;
		}

		public void RegisterSpawnedCard(GameObject spawnedCard, DraftCardEntry entry)
		{
			if (spawnedCard != null && entry != null)
			{
				_spawnedCardMap[((Object)spawnedCard).GetInstanceID()] = entry;
			}
		}

		public bool TryGetSpawnedEntry(GameObject spawnedCard, out DraftCardEntry entry)
		{
			entry = null;
			if (spawnedCard == null)
			{
				return false;
			}
			return _spawnedCardMap.TryGetValue(((Object)spawnedCard).GetInstanceID(), out entry);
		}

		public void Clear(string reason = "")
		{
			if (!string.IsNullOrWhiteSpace(reason))
			{
				_logger.LogDebug((object)("Clearing UpgradeDraft state: " + reason));
			}
			_pendingEntries.Clear();
			_spawnedCardMap.Clear();
			_planReady = false;
			_currentPickerId = -999;
			_rerolledThisPick = false;
		}

		public void ClearDraftPlanOnly(string reason = "")
		{
			if (!string.IsNullOrWhiteSpace(reason))
			{
				_logger.LogDebug((object)("Clearing draft plan only: " + reason));
			}
			_pendingEntries.Clear();
			_spawnedCardMap.Clear();
			_planReady = false;
		}

		public int GetSkipPoints(int playerId)
		{
			if (!_skipPointsByPlayerId.TryGetValue(playerId, out var value))
			{
				return 0;
			}
			return value;
		}

		public int AddSkipPoint(int playerId)
		{
			int num = GetSkipPoints(playerId) + 1;
			_skipPointsByPlayerId[playerId] = num;
			return num;
		}

		public bool TryConsumeSkipPoints(int playerId, int pointsToConsume)
		{
			if (pointsToConsume <= 0)
			{
				return true;
			}
			int skipPoints = GetSkipPoints(playerId);
			if (skipPoints < pointsToConsume)
			{
				return false;
			}
			_skipPointsByPlayerId[playerId] = skipPoints - pointsToConsume;
			return true;
		}

		public void MarkRerolledThisPick()
		{
			_rerolledThisPick = true;
		}
	}
	public sealed class UpgradePreviewService
	{
		private static readonly Regex NumericRegex = new Regex("([+-]?\\d+(\\.\\d+)?)", RegexOptions.Compiled);

		private readonly ManualLogSource _logger;

		private readonly UpgradeDraftPlugin _plugin;

		public UpgradePreviewService(ManualLogSource logger, UpgradeDraftPlugin plugin)
		{
			_logger = logger;
			_plugin = plugin;
		}

		public string BuildUpgradeTitle(DraftCardEntry entry)
		{
			return "UPGRADE: " + entry.Card.cardName;
		}

		public string BuildUpgradeDescription(DraftCardEntry entry)
		{
			StringBuilder stringBuilder = new StringBuilder();
			string text = entry.Card.cardDestription ?? string.Empty;
			if (!string.IsNullOrWhiteSpace(text))
			{
				stringBuilder.AppendLine(text.Trim());
			}
			stringBuilder.AppendLine("Upgrade pick: selecting this card grants another copy of the same card.");
			stringBuilder.AppendLine($"Owned: {entry.OwnedBefore} -> {entry.OwnedAfter}");
			if (_plugin.IsStatPreviewEnabled())
			{
				string value = BuildStackedStatPreview(entry);
				if (!string.IsNullOrWhiteSpace(value))
				{
					stringBuilder.AppendLine();
					stringBuilder.AppendLine(value);
				}
			}
			return stringBuilder.ToString().TrimEnd();
		}

		public string BuildOwnedLine(DraftCardEntry entry)
		{
			return $"Owned: {entry.OwnedBefore} -> {entry.OwnedAfter}";
		}

		public string BuildStackedStatPreview(DraftCardEntry entry)
		{
			CardInfoStat[] cardStats = entry.Card.cardStats;
			if (cardStats == null || cardStats.Length == 0)
			{
				return "Stack preview: no card stat lines available.";
			}
			List<string> list = new List<string>();
			CardInfoStat[] array = cardStats;
			foreach (CardInfoStat val in array)
			{
				if (TryBuildStackLine(val, entry.OwnedAfter, out var line))
				{
					list.Add(line);
				}
				else
				{
					list.Add(val.stat + ": " + val.amount);
				}
			}
			return string.Join("\n", list);
		}

		private bool TryBuildStackLine(CardInfoStat stat, int ownedAfter, out string line)
		{
			line = null;
			if (string.IsNullOrWhiteSpace(stat.amount))
			{
				return false;
			}
			Match match = NumericRegex.Match(stat.amount);
			if (!match.Success)
			{
				return false;
			}
			if (!float.TryParse(match.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return false;
			}
			string text = stat.amount.Replace(match.Value, string.Empty).Trim();
			float value = result * (float)ownedAfter;
			if (stat.amount.Contains("%"))
			{
				line = stat.stat + ": " + stat.amount + " (" + FormatSigned(value) + "% total from this card after pick)";
				return true;
			}
			if (!string.IsNullOrWhiteSpace(text))
			{
				line = stat.stat + ": " + stat.amount + " (" + FormatSigned(value) + " " + text + " total from this card after pick)";
				return true;
			}
			line = stat.stat + ": " + stat.amount + " (" + FormatSigned(value) + " total from this card after pick)";
			return true;
		}

		private static string FormatSigned(float value)
		{
			if (!(value >= 0f))
			{
				return value.ToString("0.##", CultureInfo.InvariantCulture);
			}
			return $"+{value:0.##}";
		}
	}
}
namespace UpgradeDraft.Patches
{
	[HarmonyPatch(typeof(CardChoice), "Pick")]
	internal static class CardChoiceActionPickPatch
	{
		private static bool Prefix(CardChoice __instance, GameObject pickedCard)
		{
			if (pickedCard == null)
			{
				return true;
			}
			DraftActionCardMarker component = pickedCard.GetComponent<DraftActionCardMarker>();
			if (component == null)
			{
				return true;
			}
			try
			{
				if (component.ActionType == DraftActionType.Skip)
				{
					return !HandleSkip(__instance, pickedCard, component.PickerId);
				}
				if (component.ActionType == DraftActionType.Reroll)
				{
					if (UpgradeDraftPlugin.SkipRerollService.TryReroll(__instance, component.PickerId))
					{
						return false;
					}
					UpgradeDraftPlugin.Log.LogWarning((object)"REROLL action card failed. Ending the pick without applying action-card stats to avoid corrupting card choice.");
					return !HandleActionEndPick(__instance, pickedCard, component.PickerId);
				}
			}
			catch (Exception arg)
			{
				UpgradeDraftPlugin.Log.LogWarning((object)$"Failed to handle UpgradeDraft action card: {arg}");
			}
			return false;
		}

		private static bool HandleSkip(CardChoice cardChoice, GameObject pickedCard, int pickerId)
		{
			if (cardChoice == null || pickedCard == null || pickerId < 0)
			{
				return false;
			}
			int num = UpgradeDraftPlugin.State.AddSkipPoint(pickerId);
			UpgradeDraftPlugin.State.ClearDraftPlanOnly("Skip action card picked.");
			if (!HandleActionEndPick(cardChoice, pickedCard, pickerId))
			{
				return false;
			}
			UpgradeDraftPlugin.Log.LogInfo((object)$"Player {pickerId} picked SKIP and gained 1 skip point (total: {num}).");
			return true;
		}

		private static bool HandleActionEndPick(CardChoice cardChoice, GameObject pickedCard, int pickerId)
		{
			if (cardChoice == null || pickedCard == null || pickerId < 0)
			{
				return false;
			}
			int[] array = AccessTools.Method(typeof(CardChoice), "CardIDs", (Type[])null, (Type[])null)?.Invoke(cardChoice, null) as int[];
			PhotonView component = ((Component)cardChoice).GetComponent<PhotonView>();
			PhotonView component2 = pickedCard.GetComponent<PhotonView>();
			PublicInt component3 = pickedCard.GetComponent<PublicInt>();
			if (array == null || component == null || component2 == null || component3 == null)
			{
				UpgradeDraftPlugin.Log.LogWarning((object)"Skip action card could not access vanilla card-pick RPC data.");
				return false;
			}
			component.RPC("RPCA_DoEndPick", (RpcTarget)0, new object[4] { array, component2.ViewID, component3.theInt, pickerId });
			return true;
		}
	}
	[HarmonyPatch(typeof(CardChoice), "RPCA_DoEndPick")]
	internal static class CardChoiceEndPickCleanupPatch
	{
		[CompilerGenerated]
		private sealed class <CleanupLeftoverChoiceCards>d__1 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public int[] cardIDs;

			public int targetCardID;

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

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

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

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

			private bool MoveNext()
			{
				//IL_0028: Unknown result type (might be due to invalid IL or missing references)
				//IL_0032: Expected O, but got Unknown
				//IL_005a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0064: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForSeconds(0.45f);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					CleanupNow(cardIDs, targetCardID, includePickedCard: false);
					<>2__current = (object)new WaitForSeconds(1.05f);
					<>1__state = 2;
					return true;
				case 2:
					<>1__state = -1;
					CleanupNow(cardIDs, targetCardID, includePickedCard: true);
					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();
			}
		}

		private static void Postfix(int[] cardIDs, int targetCardID)
		{
			if (!((Object)(object)UpgradeDraftPlugin.Instance == (Object)null) && cardIDs != null && cardIDs.Length != 0)
			{
				((MonoBehaviour)UpgradeDraftPlugin.Instance).StartCoroutine(CleanupLeftoverChoiceCards(cardIDs, targetCardID));
			}
		}

		[IteratorStateMachine(typeof(<CleanupLeftoverChoiceCards>d__1))]
		private static IEnumerator CleanupLeftoverChoiceCards(int[] cardIDs, int targetCardID)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <CleanupLeftoverChoiceCards>d__1(0)
			{
				cardIDs = cardIDs,
				targetCardID = targetCardID
			};
		}

		private static void CleanupNow(int[] cardIDs, int targetCardID, bool includePickedCard)
		{
			foreach (int num in cardIDs)
			{
				if (!includePickedCard && num == targetCardID)
				{
					continue;
				}
				PhotonView photonView = PhotonNetwork.GetPhotonView(num);
				if (photonView != null && ((Component)photonView).gameObject != null)
				{
					UpgradeDraftPlugin.VisualService?.PrepareChoiceCard(((Component)photonView).gameObject);
					if (PhotonNetwork.InRoom && photonView.IsMine)
					{
						PhotonNetwork.Destroy(((Component)photonView).gameObject);
					}
					else
					{
						Object.Destroy((Object)(object)((Component)photonView).gameObject);
					}
				}
			}
		}
	}
	[HarmonyPatch(typeof(CardChoice), "StartPick")]
	internal static class CardChoiceStartPickPatch
	{
		private static void Prefix(CardChoice __instance, int pickerIDToSet)
		{
			if (UpgradeDraftPlugin.State != null)
			{
				UpgradeDraftPlugin.State.Clear("Starting new StartPick flow.");
				UpgradeDraftPlugin.State.BeginPick(pickerIDToSet);
			}
		}
	}
	[HarmonyPatch(typeof(CardChoice), "DoPick")]
	internal static class CardChoiceDoPickPatch
	{
		private static void Prefix(CardChoice __instance, int picketIDToSet)
		{
			if (UpgradeDraftPlugin.State != null)
			{
				UpgradeDraftPlugin.State.Clear("Starting new DoPick flow.");
				UpgradeDraftPlugin.State.BeginPick(picketIDToSet);
			}
		}
	}
	internal static class CardChoiceSlotLimiter
	{
		public static void Apply(CardChoice cardChoice)
		{
			UpgradeDraftPlugin instance = UpgradeDraftPlugin.Instance;
			if ((Object)(object)instance == (Object)null || cardChoice == null)
			{
				return;
			}
			int num = instance.GetCardCount() + 1;
			if (num <= 0)
			{
				return;
			}
			Traverse val = Traverse.Create((object)cardChoice);
			Transform[] value = val.Field("children").GetValue<Transform[]>();
			if (value != null && value.Length > num)
			{
				Transform[] array = (Transform[])(object)new Transform[num];
				Array.Copy(value, array, num);
				val.Field("children").SetValue((object)array);
				if (instance.IsVerboseLoggingEnabled())
				{
					UpgradeDraftPlugin.Log.LogInfo((object)$"Limited CardChoice spawn slots from {value.Length} to {num}.");
				}
			}
		}
	}
	[HarmonyPatch(typeof(CardChoice), "Start")]
	internal static class CardChoiceStartLimitSlotsPatch
	{
		private static void Postfix(CardChoice __instance)
		{
			CardChoiceSlotLimiter.Apply(__instance);
		}
	}
	[HarmonyPatch(typeof(CardChoice), "SpawnUniqueCard")]
	internal static class CardChoiceSpawnUniqueCardPatch
	{
		private static bool Prefix(ref GameObject __result, CardChoice __instance, Vector3 pos, Quaternion rot)
		{
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			UpgradeDraftPlugin instance = UpgradeDraftPlugin.Instance;
			if ((Object)(object)instance == (Object)null || UpgradeDraftPlugin.State == null)
			{
				return true;
			}
			try
			{
				Player val = ResolvePickingPlayer(__instance);
				if (val == null)
				{
					UpgradeDraftPlugin.Log.LogWarning((object)"Could not resolve picking player. Falling back to vanilla SpawnUniqueCard.");
					return true;
				}
				UpgradeDraftPlugin.State.BeginPick(val.playerID);
				if (!UpgradeDraftPlugin.State.HasPlanForCurrentPick)
				{
					BuildPlanForCurrentPick(__instance, val);
				}
				if (!UpgradeDraftPlugin.State.TryDequeueNext(out var entry) || entry == null || entry.Card == null)
				{
					if (instance.IsVerboseLoggingEnabled())
					{
						UpgradeDraftPlugin.Log.LogInfo((object)"No queued draft entry available; falling back to vanilla SpawnUniqueCard.");
					}
					return true;
				}
				GameObject val2 = SpawnCardObject(__instance, entry.Card, pos, rot);
				if (val2 == null)
				{
					UpgradeDraftPlugin.Log.LogWarning((object)"Custom spawn returned null. Falling back to vanilla SpawnUniqueCard.");
					return true;
				}
				UpgradeDraftPlugin.VisualService.PrepareChoiceCard(val2);
				if (entry.IsAction)
				{
					UpgradeDraftPlugin.VisualService.ApplyActionVisual(val2, entry, val.playerID);
				}
				else if (entry.IsUpgrade)
				{
					UpgradeDraftPlugin.VisualService.ApplyUpgradeVisual(val2, entry);
				}
				UpgradeDraftPlugin.NetworkService?.BroadcastSpawn(val2, entry, val.playerID);
				UpgradeDraftPlugin.State.RegisterSpawnedCard(val2, entry);
				__result = val2;
				return false;
			}
			catch (Exception arg)
			{
				UpgradeDraftPlugin.Log.LogWarning((object)$"UpgradeDraft SpawnUniqueCard patch failed: {arg}");
				return true;
			}
		}

		private static void BuildPlanForCurrentPick(CardChoice cardChoice, Player pickingPlayer)
		{
			int count = UpgradeDraftPlugin.DeckService.GetEligibleUpgradeCards(pickingPlayer, UpgradeDraftPlugin.Instance.GetBlacklist()).Count;
			DraftPlan draftPlan = UpgradeDraftPlugin.RollService.BuildPlan(count);
			List<DraftCardEntry> list = UpgradeDraftPlugin.SelectionService.BuildDraftEntries(cardChoice, pickingPlayer, draftPlan);
			CardInfo val = ResolveActionCardSource(cardChoice);
			if (val != null)
			{
				DraftActionType actionType = ((UpgradeDraftPlugin.State.GetSkipPoints(pickingPlayer.playerID) < UpgradeDraftPlugin.Instance.GetSkipPointsForReroll()) ? DraftActionType.Skip : DraftActionType.Reroll);
				list.Add(new DraftCardEntry(val, actionType));
			}
			else
			{
				UpgradeDraftPlugin.Log.LogWarning((object)"Unable to resolve an action-card source. The 5th skip/reroll card will not be shown.");
			}
			UpgradeDraftPlugin.State.SetPlannedEntries(list);
			if (!string.IsNullOrWhiteSpace(draftPlan.FallbackReason))
			{
				UpgradeDraftPlugin.Log.LogWarning((object)draftPlan.FallbackReason);
			}
			if (UpgradeDraftPlugin.Instance.IsVerboseLoggingEnabled())
			{
				UpgradeDraftPlugin.Log.LogInfo((object)$"Built draft plan (roll={draftPlan.RollValue}): desiredUpgrades={draftPlan.DesiredUpgradeCount}, actualUpgrades={draftPlan.ActualUpgradeCount}, newCards={draftPlan.NewCardCount}, total={draftPlan.TotalCardCount}, generatedEntries={list.Count}.");
			}
		}

		private static CardInfo ResolveActionCardSource(CardChoice cardChoice)
		{
			try
			{
				return Traverse.Create((object)cardChoice).Field("cards").GetValue<CardInfo[]>()?.FirstOrDefault((Func<CardInfo, bool>)((CardInfo card) => card != null && ((Component)card).gameObject != null));
			}
			catch (Exception ex)
			{
				UpgradeDraftPlugin.Log.LogWarning((object)("Failed to resolve action-card source: " + ex.Message));
				return null;
			}
		}

		private static Player ResolvePickingPlayer(CardChoice cardChoice)
		{
			try
			{
				if (Convert.ToInt32(Traverse.Create((object)cardChoice).Field("pickerType").GetValue()) == 0)
				{
					return PlayerManager.instance.GetPlayersInTeam(cardChoice.pickrID).FirstOrDefault();
				}
				if (cardChoice.pickrID >= 0 && cardChoice.pickrID < PlayerManager.instance.players.Count())
				{
					return PlayerManager.instance.players[cardChoice.pickrID];
				}
			}
			catch (Exception ex)
			{
				UpgradeDraftPlugin.Log.LogWarning((object)("Failed to resolve picking player from CardChoice. Error: " + ex.Message));
			}
			return null;
		}

		private static GameObject SpawnCardObject(CardChoice cardChoice, CardInfo sourceCard, Vector3 pos, Quaternion rot)
		{
			//IL_002d: 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)
			if (sourceCard == null)
			{
				return null;
			}
			try
			{
				object? obj = typeof(CardChoice).InvokeMember("Spawn", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod, null, cardChoice, new object[3]
				{
					((Component)sourceCard).gameObject,
					pos,
					rot
				});
				GameObject val = (GameObject)((obj is GameObject) ? obj : null);
				if (val == null)
				{
					return null;
				}
				CardInfo component = val.GetComponent<CardInfo>();
				if (component != null)
				{
					component.sourceCard = sourceCard;
				}
				return val;
			}
			catch (Exception ex)
			{
				UpgradeDraftPlugin.Log.LogWarning((object)("Failed to spawn custom card object for '" + sourceCard.cardName + "': " + ex.Message));
				return null;
			}
		}
	}
	[HarmonyPatch(typeof(CardVisuals), "ChangeSelected")]
	internal static class CardVisualsChoiceOverlayPatch
	{
		private static bool Prefix(CardVisuals __instance, bool setSelected)
		{
			if (!ShouldStabilizeChoiceVisual(__instance))
			{
				return true;
			}
			try
			{
				ApplyStableChoiceVisual(__instance, setSelected);
			}
			catch (Exception ex)
			{
				if ((Object)(object)UpgradeDraftPlugin.Instance != (Object)null && UpgradeDraftPlugin.Instance.IsVerboseLoggingEnabled())
				{
					UpgradeDraftPlugin.Log.LogWarning((object)("Failed to stabilize card-choice visual: " + ex.Message));
				}
			}
			return false;
		}

		private static bool ShouldStabilizeChoiceVisual(CardVisuals visuals)
		{
			if (visuals == null || (Object)(object)UpgradeDraftPlugin.Instance == (Object)null)
			{
				return false;
			}
			CardChoice instance = CardChoice.instance;
			if (instance == null || instance.pickrID < 0 || !instance.IsPicking)
			{
				return false;
			}
			return ((Component)visuals).GetComponentInParent<CardInfo>() != null;
		}

		private static void ApplyStableChoiceVisual(CardVisuals visuals, bool setSelected)
		{
			//IL_00a6: 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_00c3: 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_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			visuals.isSelected = setSelected;
			if (visuals.objectsToToggle != null)
			{
				GameObject[] objectsToToggle = visuals.objectsToToggle;
				foreach (GameObject val in objectsToToggle)
				{
					if (val != null)
					{
						val.SetActive(false);
					}
				}
			}
			CanvasGroup[] componentsInChildren = ((Component)visuals).GetComponentsInChildren<CanvasGroup>(true);
			foreach (CanvasGroup val2 in componentsInChildren)
			{
				if (val2 != null)
				{
					val2.alpha = 1f;
				}
			}
			CardAnimation[] componentsInChildren2 = ((Component)visuals).GetComponentsInChildren<CardAnimation>(true);
			foreach (CardAnimation val3 in componentsInChildren2)
			{
				if (val3 != null)
				{
					((Behaviour)val3).enabled = true;
				}
			}
			CurveAnimation[] componentsInChildren3 = ((Component)visuals).GetComponentsInChildren<CurveAnimation>(true);
			foreach (CurveAnimation val4 in componentsInChildren3)
			{
				if (val4 != null && (int)val4.currentState != 0)
				{
					val4.PlayIn();
				}
			}
			Color color = (setSelected ? visuals.defaultColor : Color.Lerp(visuals.defaultColor, visuals.chillColor, 0.35f));
			if (visuals.images != null)
			{
				Image[] images = visuals.images;
				foreach (Image val5 in images)
				{
					if (val5 != null)
					{
						((Graphic)val5).color = color;
					}
				}
			}
			if (visuals.nameText != null)
			{
				((Graphic)visuals.nameText).color = color;
			}
			ScaleShake component = ((Component)visuals).GetComponent<ScaleShake>();
			if (component != null)
			{
				component.targetScale = (setSelected ? 1.12f : 1f);
			}
		}
	}
}
namespace UpgradeDraft.Models
{
	public enum DraftActionType
	{
		None,
		Skip,
		Reroll
	}
	public sealed class DraftCardEntry
	{
		public CardInfo Card { get; }

		public bool IsUpgrade { get; }

		public bool IsAction => ActionType != DraftActionType.None;

		public DraftActionType ActionType { get; }

		public int OwnedBefore { get; }

		public int OwnedAfter { get; }

		public DraftCardEntry(CardInfo card, bool isUpgrade, int ownedBefore)
		{
			Card = card ?? throw new ArgumentNullException("card");
			IsUpgrade = isUpgrade;
			OwnedBefore = ((ownedBefore >= 0) ? ownedBefore : 0);
			OwnedAfter = (IsUpgrade ? (OwnedBefore + 1) : OwnedBefore);
			ActionType = DraftActionType.None;
		}

		public DraftCardEntry(CardInfo card, DraftActionType actionType)
		{
			if (actionType == DraftActionType.None)
			{
				throw new ArgumentException("Action card entry requires an action type.", "actionType");
			}
			Card = card ?? throw new ArgumentNullException("card");
			ActionType = actionType;
			IsUpgrade = false;
			OwnedBefore = 0;
			OwnedAfter = 0;
		}
	}
	public sealed class DraftPlan
	{
		public int DesiredUpgradeCount { get; set; }

		public int ActualUpgradeCount { get; set; }

		public int NewCardCount { get; set; }

		public int TotalCardCount { get; set; }

		public int RollValue { get; set; }

		public string FallbackReason { get; set; }
	}
}