Decompiled source of Card Delete Mechanic v1.0.0

Rounds2Mod.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using ModdingUtils.Utils;
using UnboundLib;
using UnboundLib.GameModes;
using UnboundLib.Networking;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.1", FrameworkDisplayName = ".NET Framework 4.7.1")]
[assembly: AssemblyCompany("Rounds2Mod")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+661a91535814621160b5d94d61679bade4fe1c2d")]
[assembly: AssemblyProduct("Mod for ROUNDS")]
[assembly: AssemblyTitle("Rounds2Mod")]
[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 Rounds2Mod
{
	public class CardBarDeleteButton : MonoBehaviour, IPointerClickHandler, IEventSystemHandler, IPointerEnterHandler, IPointerExitHandler
	{
		private static readonly FieldInfo s_cardField = AccessTools.Field(typeof(CardBarButton), "card");

		private Image icon;

		private Color originalColor;

		private void Awake()
		{
			//IL_0024: 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)
			icon = ((Component)this).GetComponentInChildren<Image>();
			if ((Object)(object)icon != (Object)null)
			{
				originalColor = ((Graphic)icon).color;
			}
		}

		public void OnPointerEnter(PointerEventData eventData)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			if (CardDeleteManager.isPickPhase && CardDeleteManager.IsLocalPlayerTurn())
			{
				if ((Object)(object)icon != (Object)null)
				{
					((Graphic)icon).color = new Color(1f, 0.28f, 0.28f, originalColor.a);
				}
				CardInfo cardInfo = GetCardInfo();
				if ((Object)(object)cardInfo != (Object)null)
				{
					CardDeleteLog.Line("Hover (delete mode): '" + cardInfo.cardName + "' on '" + ((Object)((Component)this).gameObject).name + "'");
				}
			}
		}

		public void OnPointerExit(PointerEventData eventData)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)icon != (Object)null)
			{
				((Graphic)icon).color = originalColor;
			}
		}

		public void OnPointerClick(PointerEventData eventData)
		{
			CardDeleteLog.Section("Card bar click");
			if (!CardDeleteManager.isPickPhase)
			{
				CardDeleteLog.Line("Ignored: not in pick phase.");
				return;
			}
			if (!CardDeleteManager.IsLocalPlayerTurn())
			{
				CardDeleteLog.Line("Ignored: not local player's turn.");
				return;
			}
			if ((Object)(object)((Component)this).GetComponent<CardBarButton>() == (Object)null)
			{
				CardDeleteLog.Warn("Ignored: no CardBarButton on this object.");
				return;
			}
			CardInfo cardInfo = GetCardInfo();
			if ((Object)(object)cardInfo == (Object)null)
			{
				CardDeleteLog.Warn("Ignored: CardBarButton.card is null.");
				return;
			}
			CardDeleteLog.Line("Valid delete click on '" + cardInfo.cardName + "' (objectName='" + ((Object)cardInfo).name + "').");
			CardDeleteManager.instance?.RequestDelete(cardInfo);
		}

		private CardInfo GetCardInfo()
		{
			CardBarButton component = ((Component)this).GetComponent<CardBarButton>();
			return (CardInfo)(((Object)(object)component == (Object)null) ? null : /*isinst with value type is only supported in some contexts*/);
		}

		private void OnDestroy()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)icon != (Object)null)
			{
				((Graphic)icon).color = originalColor;
			}
		}
	}
	internal static class CardDeleteLog
	{
		private const string Sep = "=====================";

		public static void Section(string title)
		{
			Plugin.Logger.LogInfo((object)"=====================");
			Plugin.Logger.LogInfo((object)("[Rounds2Mod] " + title));
			Plugin.Logger.LogInfo((object)"=====================");
		}

		public static void Line(string message)
		{
			Plugin.Logger.LogInfo((object)("[Rounds2Mod] " + message));
		}

		public static void Warn(string message)
		{
			Plugin.Logger.LogWarning((object)("[Rounds2Mod] " + message));
		}

		public static void Error(string message)
		{
			Plugin.Logger.LogError((object)("[Rounds2Mod] " + message));
		}
	}
	public class CardDeleteManager : MonoBehaviour
	{
		[CompilerGenerated]
		private sealed class <OnPlayerPickEnd>d__13 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public IGameModeHandler gm;

			public CardDeleteManager <>4__this;

			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;
				CardDeleteLog.Section("Pick phase ended (HookPlayerPickEnd)");
				CardDeleteLog.Line($"Was picker playerID={currentPickerID}");
				isPickPhase = false;
				currentPickerID = -1;
				<>4__this.DisableDeleteMode();
				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 <OnPlayerPickStart>d__11 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public IGameModeHandler gm;

			public CardDeleteManager <>4__this;

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

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

			[DebuggerHidden]
			public <OnPlayerPickStart>d__11(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;
				CardDeleteLog.Section("HookPlayerPickStart (info only)");
				IGameModeHandler obj = gm;
				CardDeleteLog.Line("gameMode=" + (((obj != null) ? obj.Name : null) ?? "unknown") + "; waiting for Harmony Show/StartPick patch to fire.");
				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 CardDeleteManager instance;

		public static bool isPickPhase;

		public static int currentPickerID = -1;

		private static readonly MethodInfo s_rpca_assignCard = AccessTools.Method(typeof(Cards), "RPCA_AssignCard", new Type[7]
		{
			typeof(string),
			typeof(int),
			typeof(bool),
			typeof(string),
			typeof(float),
			typeof(float),
			typeof(bool)
		}, (Type[])null);

		private static readonly MethodInfo s_rpca_donePicking = AccessTools.Method(typeof(CardChoice), "RPCA_DonePicking", (Type[])null, (Type[])null);

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

		private static readonly FieldInfo s_isPlayingField = AccessTools.Field(typeof(CardChoice), "isPlaying");

		private static readonly FieldInfo s_picksField = AccessTools.Field(typeof(CardChoice), "picks");

		private static MethodInfo s_photonDestroy;

		private static PropertyInfo s_photonOfflineMode;

		private static readonly FieldInfo s_cardBarButtonCard = AccessTools.Field(typeof(CardBarButton), "card");

		private void Start()
		{
			instance = this;
			GameModeManager.AddHook("PlayerPickStart", (Func<IGameModeHandler, IEnumerator>)OnPlayerPickStart);
			GameModeManager.AddHook("PlayerPickEnd", (Func<IGameModeHandler, IEnumerator>)OnPlayerPickEnd);
			CardDeleteLog.Section("CardDeleteManager initialized");
			CardDeleteLog.Line("Registered HookPlayerPickStart / HookPlayerPickEnd.");
		}

		[IteratorStateMachine(typeof(<OnPlayerPickStart>d__11))]
		private IEnumerator OnPlayerPickStart(IGameModeHandler gm)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <OnPlayerPickStart>d__11(0)
			{
				<>4__this = this,
				gm = gm
			};
		}

		public void BeginPickPhase(int pickerID, string source)
		{
			if (pickerID < 0)
			{
				CardDeleteLog.Warn($"BeginPickPhase ignored (pickerID={pickerID}, source={source}).");
				return;
			}
			if (isPickPhase && currentPickerID == pickerID)
			{
				CardDeleteLog.Line($"BeginPickPhase skipped — already active for player {pickerID} (source={source}).");
				return;
			}
			currentPickerID = pickerID;
			isPickPhase = true;
			CardDeleteLog.Section("Pick phase active");
			CardDeleteLog.Line($"pickerID={pickerID}, source={source}");
			CardDeleteLog.Line($"IsPicking={CardChoice.instance?.IsPicking}, pickrID={CardChoice.instance?.pickrID}, localTurn={IsLocalPlayerTurn()}");
			EnableDeleteMode(pickerID);
		}

		[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)
			{
				<>4__this = this,
				gm = gm
			};
		}

		private void EnableDeleteMode(int pickerID)
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Expected O, but got Unknown
			if ((Object)(object)CardBarHandler.instance == (Object)null)
			{
				CardDeleteLog.Warn("EnableDeleteMode: CardBarHandler.instance is null.");
				return;
			}
			CardBar[] value = Traverse.Create((object)CardBarHandler.instance).Field("cardBars").GetValue<CardBar[]>();
			if (value == null || pickerID < 0 || pickerID >= value.Length)
			{
				CardDeleteLog.Warn($"EnableDeleteMode: invalid pickerID={pickerID}, bars={((value != null) ? value.Length : 0)}");
				return;
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			CardBar val = value[pickerID];
			foreach (Transform item in ((Component)val).transform)
			{
				Transform val2 = item;
				if (!((Component)val2).gameObject.activeSelf)
				{
					num2++;
					continue;
				}
				CardBarButton component = ((Component)val2).GetComponent<CardBarButton>();
				if (!((Object)(object)component == (Object)null))
				{
					object? obj = s_cardBarButtonCard?.GetValue(component);
					CardInfo val3 = (CardInfo)((obj is CardInfo) ? obj : null);
					if ((Object)(object)val3 == (Object)null)
					{
						num3++;
					}
					else if ((Object)(object)((Component)val2).GetComponent<CardBarDeleteButton>() == (Object)null)
					{
						((Component)val2).gameObject.AddComponent<CardBarDeleteButton>();
						num++;
					}
				}
			}
			CardDeleteLog.Line($"EnableDeleteMode: attached={num}, skipped(inactive)={num2}, skipped(noCard)={num3} for player {pickerID}.");
			if (num == 0)
			{
				CardDeleteLog.Line("Player has no deletable cards this turn (deck is empty).");
			}
		}

		private void DisableDeleteMode()
		{
			int num = Object.FindObjectsOfType<CardBarDeleteButton>().Length;
			CardBarDeleteButton[] array = Object.FindObjectsOfType<CardBarDeleteButton>();
			foreach (CardBarDeleteButton cardBarDeleteButton in array)
			{
				Object.Destroy((Object)(object)cardBarDeleteButton);
			}
			CardDeleteLog.Line($"DisableDeleteMode: removed {num} CardBarDeleteButton component(s).");
		}

		public void RequestDelete(CardInfo card)
		{
			CardDeleteLog.Section("Delete requested (local click)");
			CardDeleteLog.Line($"Card='{card?.cardName}' objectName='{((card != null) ? ((Object)card).name : null)}', pickerID={currentPickerID}");
			CardDeleteLog.Line("Broadcasting URPC_SyncDelete to all clients.");
			NetworkingManager.RPC(typeof(CardDeleteManager), "URPC_SyncDelete", new object[2]
			{
				currentPickerID,
				((Object)card).name
			});
		}

		[UnboundRPC]
		public static void URPC_SyncDelete(int playerID, string cardObjectName)
		{
			CardDeleteLog.Section("URPC_SyncDelete (all clients)");
			CardDeleteLog.Line($"playerID={playerID}, deleteCardObjectName='{cardObjectName}'");
			Player val = PlayerManager.instance.players.Find((Player p) => p.playerID == playerID);
			if ((Object)(object)val == (Object)null)
			{
				CardDeleteLog.Error($"URPC_SyncDelete: player {playerID} not found.");
				return;
			}
			int num = val.data.currentCards?.Count ?? 0;
			List<string> list = (from c in val.data.currentCards
				where (Object)(object)c != (Object)null && ((Object)c).name != cardObjectName
				select ((Object)c).name).ToList();
			CardDeleteLog.Section("Deletion logic — snapshot");
			CardDeleteLog.Line($"currentCards before reset: {num}");
			CardDeleteLog.Line("Removing: '" + cardObjectName + "'");
			CardDeleteLog.Line(string.Format("Survivors to reapply ({0}): [{1}]", list.Count, string.Join(", ", list)));
			CardDeleteLog.Section("Deletion logic — stat reset");
			try
			{
				Cards.RPCA_FullReset(playerID);
				CardDeleteLog.Line("RPCA_FullReset completed.");
			}
			catch (Exception arg)
			{
				CardDeleteLog.Error($"RPCA_FullReset failed: {arg}");
			}
			try
			{
				Cards.RPCA_ClearCardBar(playerID);
				CardDeleteLog.Line("RPCA_ClearCardBar completed.");
			}
			catch (Exception arg2)
			{
				CardDeleteLog.Error($"RPCA_ClearCardBar failed: {arg2}");
			}
			CardDeleteLog.Section("Deletion logic — reapply survivors");
			if (s_rpca_assignCard == null)
			{
				CardDeleteLog.Error("RPCA_AssignCard not found via reflection.");
			}
			else
			{
				foreach (string item in list)
				{
					try
					{
						s_rpca_assignCard.Invoke(null, new object[7] { item, playerID, true, "", 0f, 0f, true });
						CardDeleteLog.Line("Reapplied '" + item + "' (reassign=true).");
					}
					catch (Exception arg3)
					{
						CardDeleteLog.Error($"Failed to reapply '{item}': {arg3}");
					}
				}
			}
			CardDeleteLog.Line($"currentCards after reapply: {val.data.currentCards?.Count ?? 0}");
			EndPickPhase();
		}

		private static void EndPickPhase()
		{
			CardDeleteLog.Section("EndPickPhase");
			CardChoice val = CardChoice.instance;
			if ((Object)(object)val == (Object)null)
			{
				CardDeleteLog.Error("CardChoice.instance is null.");
				return;
			}
			CardDeleteLog.Line($"Before: IsPicking={val.IsPicking}, pickrID={val.pickrID}");
			((MonoBehaviour)val).StopAllCoroutines();
			CardDeleteLog.Line("Stopped all CardChoice coroutines.");
			if (s_isPlayingField != null)
			{
				s_isPlayingField.SetValue(val, false);
			}
			if (s_picksField != null)
			{
				s_picksField.SetValue(val, 0);
			}
			CleanupSpawnedDraftCards(val);
			if (s_rpca_donePicking == null)
			{
				CardDeleteLog.Warn("RPCA_DonePicking not found; falling back to IsPicking=false.");
				val.IsPicking = false;
			}
			else
			{
				try
				{
					s_rpca_donePicking.Invoke(val, null);
					CardDeleteLog.Line("Invoked CardChoice.RPCA_DonePicking().");
				}
				catch (Exception arg)
				{
					CardDeleteLog.Error($"RPCA_DonePicking failed: {arg}; setting IsPicking=false directly.");
					val.IsPicking = false;
				}
			}
			CardDeleteLog.Line($"After: IsPicking={val.IsPicking}");
		}

		private static void CleanupSpawnedDraftCards(CardChoice cc)
		{
			CardDeleteLog.Section("EndPickPhase — cleanup draft cards");
			if (s_spawnedCardsField == null)
			{
				CardDeleteLog.Warn("spawnedCards field not found.");
				return;
			}
			if (!(s_spawnedCardsField.GetValue(cc) is List<GameObject> list) || list.Count == 0)
			{
				CardDeleteLog.Line("No spawned draft cards to clean up.");
				return;
			}
			CardDeleteLog.Line($"Cleaning {list.Count} spawned draft card(s).");
			foreach (GameObject item in list.ToList())
			{
				if ((Object)(object)item == (Object)null)
				{
					continue;
				}
				try
				{
					CardVisuals componentInChildren = item.GetComponentInChildren<CardVisuals>();
					if ((Object)(object)componentInChildren != (Object)null)
					{
						componentInChildren.Leave();
					}
				}
				catch (Exception ex)
				{
					CardDeleteLog.Warn("CardVisuals.Leave on '" + ((Object)item).name + "': " + ex.Message);
				}
				DestroyDraftCardObject(item);
			}
			list.Clear();
			CardDeleteLog.Line("spawnedCards cleared.");
		}

		private static void DestroyDraftCardObject(GameObject go)
		{
			if ((Object)(object)go == (Object)null)
			{
				return;
			}
			try
			{
				if (TryPhotonDestroy(go))
				{
					CardDeleteLog.Line("Photon-destroyed draft card '" + ((Object)go).name + "'.");
					return;
				}
			}
			catch (Exception ex)
			{
				CardDeleteLog.Warn("Photon destroy failed for '" + ((Object)go).name + "': " + ex.Message);
			}
			Object.Destroy((Object)(object)go);
			CardDeleteLog.Line("Destroyed draft card '" + ((Object)go).name + "' locally.");
		}

		private static bool TryPhotonDestroy(GameObject go)
		{
			if (s_photonDestroy == null)
			{
				Type type = AccessTools.TypeByName("Photon.Pun.PhotonNetwork");
				if (type == null)
				{
					return false;
				}
				s_photonOfflineMode = type.GetProperty("OfflineMode", BindingFlags.Static | BindingFlags.Public);
				s_photonDestroy = type.GetMethod("Destroy", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(GameObject) }, null);
			}
			if (s_photonDestroy == null)
			{
				return false;
			}
			if (s_photonOfflineMode != null && (bool)s_photonOfflineMode.GetValue(null, null))
			{
				return false;
			}
			Type type2 = AccessTools.TypeByName("Photon.Pun.PhotonView");
			if (type2 == null)
			{
				return false;
			}
			if ((Object)(object)go.GetComponent(type2) == (Object)null)
			{
				return false;
			}
			s_photonDestroy.Invoke(null, new object[1] { go });
			return true;
		}

		public static bool IsLocalPlayerTurn()
		{
			if (currentPickerID < 0)
			{
				return false;
			}
			Player val = PlayerManager.instance.players.Find((Player p) => p.playerID == currentPickerID);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			object value = AccessTools.Field(typeof(CharacterData), "view").GetValue(val.data);
			if (value == null)
			{
				return true;
			}
			return (bool)AccessTools.Property(value.GetType(), "IsMine").GetValue(value, null);
		}
	}
	[HarmonyPatch]
	internal static class CardDeletePatches
	{
		[HarmonyPatch(typeof(CardChoiceVisuals), "Show")]
		[HarmonyPrefix]
		private static void CardChoiceVisuals_Show_Prefix(int pickerID, bool animateIn)
		{
			CardDeleteManager.instance?.BeginPickPhase(pickerID, "CardChoiceVisuals.Show");
		}

		[HarmonyPatch(typeof(CardChoice), "StartPick")]
		[HarmonyPostfix]
		private static void CardChoice_StartPick_Postfix(int picksToSet, int pickerIDToSet)
		{
			CardDeleteManager.instance?.BeginPickPhase(pickerIDToSet, "CardChoice.StartPick");
		}
	}
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("Rounds2Mod", "Mod for ROUNDS", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Logger;

		private void Awake()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			Logger = ((BaseUnityPlugin)this).Logger;
			CardDeleteLog.Section("Plugin Awake");
			new Harmony("Rounds2Mod").PatchAll();
			((Component)this).gameObject.AddComponent<CardDeleteManager>();
			CardDeleteLog.Line("Plugin Rounds2Mod loaded.");
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "Rounds2Mod";

		public const string PLUGIN_NAME = "Mod for ROUNDS";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}