Decompiled source of NoRedraw v1.2.0

NoRedraw.dll

Decompiled 3 weeks 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 BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using NoRedraw.Patches;
using Photon.Pun;
using TMPro;
using UnboundLib;
using UnboundLib.GameModes;
using UnboundLib.Networking;
using UnboundLib.Utils.UI;
using UnityEngine;
using UnityEngine.Events;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("NoRedraw")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("NoRedraw")]
[assembly: AssemblyTitle("NoRedraw")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace NoRedraw
{
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("com.nic.rounds.noredraw", "No Redraw", "1.1.0")]
	[BepInProcess("Rounds.exe")]
	public class NoRedraw : BaseUnityPlugin
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static UnityAction <>9__16_0;

			public static UnityAction<bool> <>9__17_0;

			public static UnityAction<bool> <>9__17_1;

			public static UnityAction<bool> <>9__17_2;

			public static Func<string, bool> <>9__23_0;

			public static Func<string, bool> <>9__25_0;

			public static Func<CardInfo, bool> <>9__27_0;

			internal void <Start>b__16_0()
			{
			}

			internal void <BuildMenu>b__17_0(bool value)
			{
				Enabled = value;
				EnabledConfig.Value = value;
			}

			internal void <BuildMenu>b__17_1(bool value)
			{
				DiscardOnDraw = value;
				DiscardOnDrawConfig.Value = value;
			}

			internal void <BuildMenu>b__17_2(bool value)
			{
				ResetOnNewRound = value;
				ResetOnNewRoundConfig.Value = value;
			}

			internal bool <GetCardAliases>b__23_0(string name)
			{
				return !string.IsNullOrWhiteSpace(name);
			}

			internal bool <IsCardRemoved>b__25_0(string alias)
			{
				return PickedCards.Contains(alias);
			}

			internal bool <GetRandomAllowedCard>b__27_0(CardInfo c)
			{
				return (Object)(object)c != (Object)null && !IsCardRemoved(c);
			}
		}

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

			private object <>2__current;

			public IGameModeHandler gm;

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

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

			[DebuggerHidden]
			public <OnGameStart>d__20(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;
				PickedCards.Clear();
				Log.LogInfo((object)"Card pool reset — new game.");
				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 <OnRoundStart>d__21 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public IGameModeHandler gm;

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

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

			[DebuggerHidden]
			public <OnRoundStart>d__21(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;
				if (Enabled && ResetOnNewRound)
				{
					PickedCards.Clear();
					Log.LogInfo((object)"Card pool reset — new round.");
				}
				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 string ModId = "com.nic.rounds.noredraw";

		private const string ModName = "No Redraw";

		public const string Version = "1.1.0";

		internal static ManualLogSource Log;

		private static ConfigEntry<bool> EnabledConfig;

		private static ConfigEntry<bool> ResetOnNewRoundConfig;

		private static ConfigEntry<bool> DiscardOnDrawConfig;

		public static bool Enabled = true;

		public static bool ResetOnNewRound = false;

		public static bool DiscardOnDraw = false;

		public static HashSet<string> PickedCards = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		public static NoRedraw instance { get; private set; }

		private void Awake()
		{
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Log.LogInfo((object)"NoRedraw Awake() running...");
			EnabledConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("NoRedraw", "Enabled", true, "When enabled, cards are removed from the draw pool after use");
			ResetOnNewRoundConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("NoRedraw", "ResetOnNewRound", false, "If true, the pool resets every round instead of only on new game");
			DiscardOnDrawConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("NoRedraw", "DiscardOnDraw", false, "If true, cards are discarded when drawn (shown as options), not just when picked");
			try
			{
				Harmony val = new Harmony("com.nic.rounds.noredraw");
				val.PatchAll();
				Log.LogInfo((object)"Harmony patches applied successfully.");
				ApplyUnboundConditionPatch(val);
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Harmony patching FAILED: " + ex));
			}
		}

		private void Start()
		{
			//IL_004e: 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_0059: Expected O, but got Unknown
			instance = this;
			Enabled = EnabledConfig.Value;
			ResetOnNewRound = ResetOnNewRoundConfig.Value;
			DiscardOnDraw = DiscardOnDrawConfig.Value;
			object obj = <>c.<>9__16_0;
			if (obj == null)
			{
				UnityAction val = delegate
				{
				};
				<>c.<>9__16_0 = val;
				obj = (object)val;
			}
			Unbound.RegisterMenu("No Redraw", (UnityAction)obj, (Action<GameObject>)BuildMenu, (GameObject)null, false);
			Unbound.RegisterHandshake("com.nic.rounds.noredraw", (Action)OnHandShakeCompleted);
			GameModeManager.AddHook("GameStart", (Func<IGameModeHandler, IEnumerator>)OnGameStart);
			GameModeManager.AddHook("RoundStart", (Func<IGameModeHandler, IEnumerator>)OnRoundStart);
			Unbound.RegisterCredits("No Redraw", new string[1] { "Nic" }, "GitHub", "https://github.com/nic/NoRedraw");
			Log.LogInfo((object)("NoRedraw Start() complete. Enabled=" + Enabled + " ResetOnNewRound=" + ResetOnNewRound + " DiscardOnDraw=" + DiscardOnDraw));
		}

		private void BuildMenu(GameObject menu)
		{
			TextMeshProUGUI val = default(TextMeshProUGUI);
			MenuHandler.CreateText("No Redraw Settings", menu, ref val, 60, true, (Color?)null, (TMP_FontAsset)null, (Material)null, (TextAlignmentOptions?)null);
			MenuHandler.CreateToggle(Enabled, "Enable No Redraw", menu, (UnityAction<bool>)delegate(bool value)
			{
				Enabled = value;
				EnabledConfig.Value = value;
			}, 50, true, (Color?)null, (TMP_FontAsset)null, (Material)null, (TextAlignmentOptions?)null);
			MenuHandler.CreateToggle(DiscardOnDraw, "True Deck Mode (discard on draw)", menu, (UnityAction<bool>)delegate(bool value)
			{
				DiscardOnDraw = value;
				DiscardOnDrawConfig.Value = value;
			}, 50, true, (Color?)null, (TMP_FontAsset)null, (Material)null, (TextAlignmentOptions?)null);
			MenuHandler.CreateToggle(ResetOnNewRound, "Reset Pool Each Round", menu, (UnityAction<bool>)delegate(bool value)
			{
				ResetOnNewRound = value;
				ResetOnNewRoundConfig.Value = value;
			}, 50, true, (Color?)null, (TMP_FontAsset)null, (Material)null, (TextAlignmentOptions?)null);
			MenuHandler.CreateText(" ", menu, ref val, 20, true, (Color?)null, (TMP_FontAsset)null, (Material)null, (TextAlignmentOptions?)null);
			MenuHandler.CreateText("OFF: cards removed only when picked.\nTRUE DECK: cards removed when shown as options.\nPool auto-resets when empty.", menu, ref val, 30, true, (Color?)null, (TMP_FontAsset)null, (Material)null, (TextAlignmentOptions?)null);
		}

		private void OnHandShakeCompleted()
		{
			if (PhotonNetwork.IsMasterClient)
			{
				NetworkingManager.RPC_Others(typeof(NoRedraw), "SyncSettings", new object[3] { Enabled, ResetOnNewRound, DiscardOnDraw });
			}
		}

		[UnboundRPC]
		private static void SyncSettings(bool enabled, bool resetOnNewRound, bool discardOnDraw)
		{
			Enabled = enabled;
			ResetOnNewRound = resetOnNewRound;
			DiscardOnDraw = discardOnDraw;
		}

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

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

		internal static string GetCardKey(CardInfo cardInfo)
		{
			if ((Object)(object)cardInfo == (Object)null)
			{
				return string.Empty;
			}
			string text = (((Object)(object)cardInfo.sourceCard != (Object)null) ? cardInfo.sourceCard.cardName : null);
			if (!string.IsNullOrWhiteSpace(text))
			{
				return text.Trim();
			}
			return string.IsNullOrWhiteSpace(cardInfo.cardName) ? string.Empty : cardInfo.cardName.Trim();
		}

		internal static string[] GetCardAliases(CardInfo cardInfo)
		{
			if ((Object)(object)cardInfo == (Object)null)
			{
				return Array.Empty<string>();
			}
			return new string[3]
			{
				GetCardKey(cardInfo),
				string.IsNullOrWhiteSpace(cardInfo.cardName) ? null : cardInfo.cardName.Trim(),
				((Object)(object)cardInfo.sourceCard == (Object)null || string.IsNullOrWhiteSpace(cardInfo.sourceCard.cardName)) ? null : cardInfo.sourceCard.cardName.Trim()
			}.Where((string name) => !string.IsNullOrWhiteSpace(name)).Cast<string>().Distinct<string>(StringComparer.OrdinalIgnoreCase)
				.ToArray();
		}

		private static void ApplyUnboundConditionPatch(Harmony harmony)
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			try
			{
				Type type = AccessTools.TypeByName("CardChoiceSpawnUniqueCardPatch.CardChoicePatchSpawnUniqueCard");
				if (type == null)
				{
					Log.LogInfo((object)"CardChoiceSpawnUniqueCardPatch not present — skipping condition patch.");
					return;
				}
				MethodBase methodBase = AccessTools.Method(type, "GetCondition", (Type[])null, (Type[])null);
				if (methodBase == null)
				{
					Log.LogWarning((object)"CardChoiceSpawnUniqueCardPatch.GetCondition method not found.");
					return;
				}
				HarmonyMethod val = new HarmonyMethod(AccessTools.Method(typeof(Patch_CardChoiceSpawnUniqueCardPatch_GetCondition), "Postfix", (Type[])null, (Type[])null));
				harmony.Patch(methodBase, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Log.LogInfo((object)"Successfully patched CardChoiceSpawnUniqueCardPatch.GetCondition.");
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Failed to apply UnboundLib condition patch: " + ex));
			}
		}

		internal static bool IsCardRemoved(CardInfo cardInfo)
		{
			return GetCardAliases(cardInfo).Any((string alias) => PickedCards.Contains(alias));
		}

		internal static float GetCardWeight(CardInfo cardInfo)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected I4, but got Unknown
			if ((Object)(object)cardInfo == (Object)null)
			{
				return 0f;
			}
			Rarity rarity = cardInfo.rarity;
			Rarity val = rarity;
			return (int)val switch
			{
				0 => 10f, 
				1 => 4f, 
				2 => 1f, 
				_ => 0f, 
			};
		}

		internal static GameObject GetRandomAllowedCard(CardInfo[] cards)
		{
			if (cards == null || cards.Length == 0)
			{
				return null;
			}
			CardInfo[] array = cards.Where((CardInfo c) => (Object)(object)c != (Object)null && !IsCardRemoved(c)).ToArray();
			if (array.Length == 0)
			{
				return null;
			}
			float num = ((IEnumerable<CardInfo>)array).Sum((Func<CardInfo, float>)GetCardWeight);
			if (num <= 0f)
			{
				return ((Component)array[Random.Range(0, array.Length)]).gameObject;
			}
			float num2 = Random.Range(0f, num);
			CardInfo[] array2 = array;
			foreach (CardInfo val in array2)
			{
				num2 -= GetCardWeight(val);
				if (num2 <= 0f)
				{
					return ((Component)val).gameObject;
				}
			}
			return ((Component)array[^1]).gameObject;
		}

		internal static void RememberCard(CardInfo cardInfo, string reason)
		{
			string[] cardAliases = GetCardAliases(cardInfo);
			if (cardAliases.Length != 0)
			{
				bool flag = false;
				string[] array = cardAliases;
				foreach (string item in array)
				{
					flag |= PickedCards.Add(item);
				}
				if (flag)
				{
					string text = cardAliases[0];
					string text2 = ((cardAliases.Length > 1) ? (" aliases=[" + string.Join(", ", cardAliases) + "]") : string.Empty);
					Log.LogInfo((object)(reason + ": \"" + text + "\" — " + PickedCards.Count + " unique entries removed." + text2));
				}
			}
		}
	}
}
namespace NoRedraw.Patches
{
	[HarmonyPatch(typeof(CardChoice), "Pick")]
	internal class Patch_CardChoice_Pick
	{
		private static void Prefix(GameObject pickedCard)
		{
			if (!NoRedraw.Enabled || (Object)(object)pickedCard == (Object)null || NoRedraw.DiscardOnDraw)
			{
				return;
			}
			CardInfo component = pickedCard.GetComponent<CardInfo>();
			if (!((Object)(object)component == (Object)null))
			{
				if (!string.IsNullOrEmpty(component.cardName) && (Object)(object)component.sourceCard != (Object)null && !string.Equals(component.cardName, component.sourceCard.cardName, StringComparison.OrdinalIgnoreCase))
				{
					NoRedraw.Log.LogInfo((object)("Pick name mismatch: runtime=\"" + component.cardName + "\" source=\"" + component.sourceCard.cardName + "\""));
				}
				NoRedraw.RememberCard(component, "Picked");
			}
		}
	}
	[HarmonyPatch(typeof(CardChoice), "GetRanomCard")]
	[HarmonyPriority(800)]
	internal class Patch_CardChoice_GetRanomCard
	{
		private static CardInfo[]? _originalCards;

		private static void Prefix(CardChoice __instance)
		{
			if (NoRedraw.Enabled && NoRedraw.PickedCards.Count != 0)
			{
				_originalCards = __instance.cards;
				CardInfo[] array = __instance.cards.Where((CardInfo c) => !NoRedraw.IsCardRemoved(c)).ToArray();
				if (array.Length != 0)
				{
					__instance.cards = array;
					NoRedraw.Log.LogInfo((object)("Filtered pool: " + array.Length + "/" + _originalCards.Length + " cards available."));
				}
				else
				{
					NoRedraw.Log.LogInfo((object)"All cards drawn! Resetting pool.");
					NoRedraw.PickedCards.Clear();
					_originalCards = null;
				}
			}
		}

		private static void Postfix(CardChoice __instance, ref GameObject __result)
		{
			if (_originalCards != null)
			{
				__instance.cards = _originalCards;
				_originalCards = null;
			}
			if (!NoRedraw.Enabled || (Object)(object)__result == (Object)null)
			{
				return;
			}
			CardInfo component = __result.GetComponent<CardInfo>();
			if ((Object)(object)component == (Object)null || !NoRedraw.IsCardRemoved(component))
			{
				return;
			}
			GameObject randomAllowedCard = NoRedraw.GetRandomAllowedCard(__instance.cards);
			if ((Object)(object)randomAllowedCard == (Object)null)
			{
				NoRedraw.Log.LogWarning((object)("Blocked repeat draw \"" + component.cardName + "\" but found no replacement."));
				return;
			}
			CardInfo component2 = randomAllowedCard.GetComponent<CardInfo>();
			if ((Object)(object)component2 == (Object)null || NoRedraw.IsCardRemoved(component2))
			{
				NoRedraw.Log.LogWarning((object)"Blocked repeat draw but replacement was invalid.");
				return;
			}
			NoRedraw.Log.LogWarning((object)("Blocked repeat draw \"" + NoRedraw.GetCardKey(component) + "\" and replaced it with \"" + NoRedraw.GetCardKey(component2) + "\"."));
			__result = randomAllowedCard;
		}
	}
	[HarmonyPatch(typeof(CardChoice), "SpawnUniqueCard")]
	[HarmonyPriority(800)]
	internal class Patch_CardChoice_SpawnUniqueCard
	{
		private static CardInfo[]? _originalCards;

		private static void Prefix(CardChoice __instance)
		{
			if (NoRedraw.Enabled && NoRedraw.PickedCards.Count != 0)
			{
				_originalCards = __instance.cards;
				CardInfo[] array = __instance.cards.Where((CardInfo c) => (Object)(object)c != (Object)null && !NoRedraw.IsCardRemoved(c)).ToArray();
				if (array.Length != 0)
				{
					__instance.cards = array;
					return;
				}
				NoRedraw.Log.LogInfo((object)"SpawnUniqueCard: all cards exhausted — resetting pool.");
				NoRedraw.PickedCards.Clear();
				_originalCards = null;
			}
		}

		private static void Postfix(CardChoice __instance, ref GameObject __result)
		{
			if (_originalCards != null)
			{
				__instance.cards = _originalCards;
				_originalCards = null;
			}
			if (!NoRedraw.Enabled || !NoRedraw.DiscardOnDraw || (Object)(object)__result == (Object)null)
			{
				return;
			}
			CardInfo component = __result.GetComponent<CardInfo>();
			if (!((Object)(object)component == (Object)null))
			{
				if (!string.IsNullOrEmpty(component.cardName) && (Object)(object)component.sourceCard != (Object)null && !string.Equals(component.cardName, component.sourceCard.cardName, StringComparison.OrdinalIgnoreCase))
				{
					NoRedraw.Log.LogInfo((object)("Draw name mismatch: runtime=\"" + component.cardName + "\" source=\"" + component.sourceCard.cardName + "\""));
				}
				NoRedraw.RememberCard(component, "Drawn & discarded");
			}
		}
	}
	internal class Patch_CardChoiceSpawnUniqueCardPatch_GetCondition
	{
		public static void Postfix(CardChoice instance, ref Func<CardInfo, Player, Gun, GunAmmo, CharacterData, HealthHandler, Gravity, Block, CharacterStatModifiers, bool> __result)
		{
			if (__result == null)
			{
				return;
			}
			Func<CardInfo, Player, Gun, GunAmmo, CharacterData, HealthHandler, Gravity, Block, CharacterStatModifiers, bool> original = __result;
			__result = delegate(CardInfo card, Player player, Gun gun, GunAmmo gunAmmo, CharacterData data, HealthHandler health, Gravity gravity, Block block, CharacterStatModifiers stats)
			{
				if (!original(card, player, gun, gunAmmo, data, health, gravity, block, stats))
				{
					return false;
				}
				if (!NoRedraw.Enabled || (Object)(object)card == (Object)null)
				{
					return true;
				}
				bool flag = NoRedraw.IsCardRemoved(card);
				if (flag)
				{
					NoRedraw.Log.LogInfo((object)("Rejected removed card in external unique-card patch: \"" + NoRedraw.GetCardKey(card) + "\""));
				}
				return !flag;
			};
		}
	}
}