Decompiled source of ReapWhatYouSow v1.4.4

BepInEx/plugins/ReapWhatYouSow/ReapWhatYouSow.dll

Decompiled 2 months ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using DiskCardGame;
using HarmonyLib;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("DeathcardHauntedPastBridge")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("DeathcardHauntedPastBridge")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("db7267ec-21f9-407e-9884-22b7a9505b74")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace DeathcardHauntedPast_CursesBridge;

[BepInPlugin("lubeeloo.inscryption.reap_what_you_sow", "Reap ↔ What You Sow", "1.4.4")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class Plugin : BaseUnityPlugin
{
	public const string PluginGuid = "lubeeloo.inscryption.reap_what_you_sow";

	public const string PluginName = "Reap ↔ What You Sow";

	public const string PluginVersion = "1.4.4";

	internal static ManualLogSource LogS;

	private Harmony _harmony;

	internal static ConfigEntry<bool> C_EnablePoolMix;

	internal static ConfigEntry<int> C_MaxDeathcardsToBlend;

	internal static ConfigEntry<bool> C_EverlastingVengeance;

	internal static ConfigEntry<double> C_SwapChance;

	internal static ConfigEntry<bool> C_MixIntoLeshyBoss;

	internal static ConfigEntry<int> C_LeshyBossBlendCap;

	internal static int EncounterCount;

	internal static int HauntedSwapsThisEncounter;

	private void Awake()
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Expected O, but got Unknown
		//IL_015c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0171: Unknown result type (might be due to invalid IL or missing references)
		//IL_017e: Expected O, but got Unknown
		//IL_017e: Expected O, but got Unknown
		//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c4: Expected O, but got Unknown
		//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_020a: Expected O, but got Unknown
		//IL_02a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b0: Expected O, but got Unknown
		LogS = ((BaseUnityPlugin)this).Logger;
		_harmony = new Harmony("lubeeloo.inscryption.reap_what_you_sow");
		C_EnablePoolMix = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnablePoolMix", true, "Enable the pool-mix bridge features.");
		C_MaxDeathcardsToBlend = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxDeathcardsToBlend", 6, "Max saved deathcards to mix into Haunted Past pool per encounter (pool size is kept the same).");
		C_SwapChance = ((BaseUnityPlugin)this).Config.Bind<double>("General", "SwapChance", 0.5, "Probability [0..1] to swap Curses’ random Haunted Past deathcard for one of your saved deathcards.");
		C_MixIntoLeshyBoss = ((BaseUnityPlugin)this).Config.Bind<bool>("Boss", "MixIntoLeshyBoss", true, "If true, inject your saved deathcards into Leshy’s boss plan/blueprint (opponent plays them).");
		C_LeshyBossBlendCap = ((BaseUnityPlugin)this).Config.Bind<int>("Boss", "LeshyBossBlendCap", 6, "Max saved deathcards to inject into Leshy boss plan.");
		C_EverlastingVengeance = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EverlastingVengeance", false, "If true, every encounter past the first is guaranteed to be a Haunted Past event.");
		((BaseUnityPlugin)this).Logger.LogInfo((object)$"Bridge: config — EverlastingVengeance={C_EverlastingVengeance.Value}, SwapChance={C_SwapChance.Value}, PoolMix={C_EnablePoolMix.Value}");
		MethodInfo methodInfo = AccessTools.Method(typeof(TurnManager), "SetupPhase", (Type[])null, (Type[])null);
		if (methodInfo != null)
		{
			_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(EncounterHooks), "SetupPrefix", (Type[])null), new HarmonyMethod(typeof(EncounterHooks), "SetupPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}
		MethodInfo methodInfo2 = AccessTools.Method(typeof(TurnManager), "CleanupPhase", (Type[])null, (Type[])null);
		if (methodInfo2 != null)
		{
			_harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(EncounterHooks), "CleanupPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}
		MethodInfo methodInfo3 = AccessTools.Method(typeof(MapNodeManager), "Start", (Type[])null, (Type[])null);
		if (methodInfo3 != null)
		{
			_harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(typeof(MapHooks), "MapStartPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}
		Type type = AccessTools.TypeByName("Infiniscryption.Curses.Patchers.DeathcardHaunt") ?? AccessTools.TypeByName("Infiniscryption.Curses.DeathcardHaunt");
		MethodInfo methodInfo4 = ((type != null) ? AccessTools.Method(type, "GetRandomDeathcard", (Type[])null, (Type[])null) : null);
		if (methodInfo4 == null && type != null)
		{
			methodInfo4 = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.ReturnType == typeof(CardInfo) && m.Name.IndexOf("GetRandomDeathcard", StringComparison.OrdinalIgnoreCase) >= 0);
		}
		if (methodInfo4 != null)
		{
			_harmony.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, new HarmonyMethod(typeof(HauntedReplacePatch), "Post_GetRandomDeathcard", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Bridge: hooked Curses.GetRandomDeathcard (swap chance active).");
		}
		else
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"Bridge: Curses.GetRandomDeathcard not found (swap disabled).");
		}
		TryPatchOpponentCreateCardSafetyNet();
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Reap ↔ What You Sow v1.4.4 loaded.");
	}

	private void TryPatchOpponentCreateCardSafetyNet()
	{
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Expected O, but got Unknown
		try
		{
			Type typeFromHandle = typeof(Opponent);
			BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
			List<MethodInfo> list = (from m in typeFromHandle.GetMethods(bindingAttr)
				where m.Name == "CreateCard" && m.ReturnType == typeof(CardInfo)
				select m).ToList();
			foreach (MethodInfo item in list)
			{
				_harmony.Patch((MethodBase)item, (HarmonyMethod)null, new HarmonyMethod(typeof(CreateCardInterceptor), "Post_CreateCard", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			if (list.Count > 0)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Bridge: hooked Opponent.CreateCard safety net on {list.Count} overload(s).");
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Bridge: Opponent.CreateCard overloads returning CardInfo not found; safety net not applied.");
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Bridge: TryPatchOpponentCreateCardSafetyNet failed: " + ex));
		}
	}

	private void OnDestroy()
	{
		Harmony harmony = _harmony;
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
	}
}
internal static class MapHooks
{
	private static int _lastSetFrame = -1;

	public static void MapStartPostfix()
	{
		try
		{
			if (SaveFile.IsAscension && Plugin.C_EverlastingVengeance.Value && _lastSetFrame != Time.frameCount)
			{
				HauntForcer.ForceHauntEarly("MapStart");
				_lastSetFrame = Time.frameCount;
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logS = Plugin.LogS;
			if (logS != null)
			{
				logS.LogWarning((object)("[Bridge] MapStartPostfix failed: " + ex.Message));
			}
		}
	}
}
internal static class HauntedReplacePatch
{
	public static void Post_GetRandomDeathcard(ref CardInfo __result)
	{
		try
		{
			if (!SaveFile.IsAscension || (Object)(object)__result == (Object)null || !Plugin.C_EnablePoolMix.Value)
			{
				return;
			}
			List<CardModificationInfo> list = SaveManager.SaveFile?.deathCardMods;
			if (list == null || list.Count == 0 || (!(Plugin.C_SwapChance.Value >= 0.999) && (double)SeededRandom.Value(SaveManager.SaveFile.GetCurrentRandomSeed() ^ ((Plugin.EncounterCount + 1) * 73244475) ^ (Plugin.HauntedSwapsThisEncounter * 165902235) ^ 0xDC0DE) > Plugin.C_SwapChance.Value))
			{
				return;
			}
			CardInfo cardByName = CardLoader.GetCardByName("!DEATHCARD_BASE");
			if ((Object)(object)cardByName == (Object)null)
			{
				return;
			}
			int currentRandomSeed = SaveManager.SaveFile.GetCurrentRandomSeed();
			currentRandomSeed ^= (Plugin.EncounterCount + 1) * 165902235;
			currentRandomSeed ^= (Plugin.HauntedSwapsThisEncounter + 1) * 2135587861;
			int index = SeededRandom.Range(0, list.Count, currentRandomSeed ^ 0xC0FFEE);
			CardModificationInfo val = list[index];
			if (val == null)
			{
				return;
			}
			CardInfo val2 = Object.Instantiate<CardInfo>(cardByName);
			List<CardModificationInfo> orCreateMods = EncounterHooks.ReflectionHelpers.GetOrCreateMods(val2);
			if (orCreateMods != null)
			{
				orCreateMods.Add(val);
				EncounterHooks.ReflectionHelpers.TrySetHauntedFlag(val2);
				__result = val2;
				Plugin.HauntedSwapsThisEncounter++;
				ManualLogSource logS = Plugin.LogS;
				if (logS != null)
				{
					logS.LogInfo((object)$"[Bridge] Swapped Haunted Past deathcard for your saved one: {val2.DisplayedNameEnglish} (encounter {Plugin.EncounterCount}, swap #{Plugin.HauntedSwapsThisEncounter}).");
				}
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logS2 = Plugin.LogS;
			if (logS2 != null)
			{
				logS2.LogError((object)("[Bridge] Post_GetRandomDeathcard failed: " + ex));
			}
		}
	}
}
internal static class CreateCardInterceptor
{
	public static void Post_CreateCard(ref CardInfo __result)
	{
		try
		{
			if (!SaveFile.IsAscension || (Object)(object)__result == (Object)null || !Plugin.C_EnablePoolMix.Value || !(((Object)__result).name ?? "").StartsWith("!DEATHCARD_BASE", StringComparison.OrdinalIgnoreCase))
			{
				return;
			}
			List<CardModificationInfo> list = SaveManager.SaveFile?.deathCardMods;
			if (list == null || list.Count == 0 || (!(Plugin.C_SwapChance.Value >= 0.999) && (double)SeededRandom.Value(SaveManager.SaveFile.GetCurrentRandomSeed() ^ ((Plugin.EncounterCount + 1) * 73244475) ^ (Plugin.HauntedSwapsThisEncounter * 165902235) ^ 0xDC0DE) > Plugin.C_SwapChance.Value))
			{
				return;
			}
			CardInfo cardByName = CardLoader.GetCardByName("!DEATHCARD_BASE");
			if ((Object)(object)cardByName == (Object)null)
			{
				return;
			}
			int currentRandomSeed = SaveManager.SaveFile.GetCurrentRandomSeed();
			currentRandomSeed ^= (Plugin.EncounterCount + 1) * 165902235;
			currentRandomSeed ^= (Plugin.HauntedSwapsThisEncounter + 1) * 2135587861;
			int index = SeededRandom.Range(0, list.Count, currentRandomSeed ^ 0xC0FFEE);
			CardModificationInfo val = list[index];
			if (val == null)
			{
				return;
			}
			CardInfo val2 = Object.Instantiate<CardInfo>(cardByName);
			List<CardModificationInfo> orCreateMods = EncounterHooks.ReflectionHelpers.GetOrCreateMods(val2);
			if (orCreateMods != null)
			{
				orCreateMods.Add(val);
				EncounterHooks.ReflectionHelpers.TrySetHauntedFlag(val2);
				__result = val2;
				Plugin.HauntedSwapsThisEncounter++;
				ManualLogSource logS = Plugin.LogS;
				if (logS != null)
				{
					logS.LogInfo((object)$"[Bridge] Safety net swapped raw base deathcard -> {val2.DisplayedNameEnglish} (encounter {Plugin.EncounterCount}, swap #{Plugin.HauntedSwapsThisEncounter}).");
				}
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logS2 = Plugin.LogS;
			if (logS2 != null)
			{
				logS2.LogWarning((object)("[Bridge] CreateCardInterceptor failed: " + ex.Message));
			}
		}
	}
}
internal static class EncounterHooks
{
	internal static class PoolMixer
	{
		public static void MixIntoDeckKeepingSize(Deck deck, int cap)
		{
			try
			{
				if ((Object)(object)deck == (Object)null || cap <= 0)
				{
					return;
				}
				List<CardInfo> deckCards = ReflectionHelpers.GetDeckCards(deck);
				if (deckCards == null || deckCards.Count == 0)
				{
					return;
				}
				int count = deckCards.Count;
				List<CardInfo> list = BuildDeathcardInfos(Math.Min(cap, count));
				if (list.Count != 0)
				{
					List<CardInfo> list2 = new List<CardInfo>(deckCards.Count + list.Count);
					list2.AddRange(deckCards);
					list2.AddRange(list);
					ReflectionHelpers.Shuffle(list2);
					List<CardInfo> newOrder = list2.Take(count).ToList();
					ReflectionHelpers.ReplaceDeck(deck, newOrder);
					ManualLogSource logS = Plugin.LogS;
					if (logS != null)
					{
						logS.LogInfo((object)$"[Bridge] Pool mixed: +{list.Count}, kept size {count}.");
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logS2 = Plugin.LogS;
				if (logS2 != null)
				{
					logS2.LogError((object)("[Bridge] MixIntoDeckKeepingSize error: " + ex));
				}
			}
		}

		private static List<CardInfo> BuildDeathcardInfos(int limit)
		{
			List<CardInfo> list = new List<CardInfo>();
			try
			{
				SaveFile saveFile = SaveManager.SaveFile;
				if (saveFile == null || saveFile.deathCardMods == null || saveFile.deathCardMods.Count == 0)
				{
					return list;
				}
				CardInfo cardByName = CardLoader.GetCardByName("!DEATHCARD_BASE");
				if ((Object)(object)cardByName == (Object)null)
				{
					return list;
				}
				int num = 0;
				foreach (CardModificationInfo deathCardMod in saveFile.deathCardMods)
				{
					CardInfo val = Object.Instantiate<CardInfo>(cardByName);
					List<CardModificationInfo> orCreateMods = ReflectionHelpers.GetOrCreateMods(val);
					if (orCreateMods != null)
					{
						orCreateMods.Add(deathCardMod);
						list.Add(val);
						num++;
						if (num >= limit)
						{
							break;
						}
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logS = Plugin.LogS;
				if (logS != null)
				{
					logS.LogError((object)("[Bridge] BuildDeathcardInfos failed: " + ex));
				}
			}
			return list;
		}
	}

	internal static class LeshyBossInjector
	{
		public static int InjectIntoOpponentPlan(object opponent, int cap)
		{
			try
			{
				if (cap <= 0 || opponent == null)
				{
					return 0;
				}
				List<CardModificationInfo> list = SaveManager.SaveFile?.deathCardMods;
				if (list == null || list.Count == 0)
				{
					return 0;
				}
				CardInfo cardByName = CardLoader.GetCardByName("!DEATHCARD_BASE");
				if ((Object)(object)cardByName == (Object)null)
				{
					return 0;
				}
				List<List<CardInfo>> list2 = FindAllCardInfoLists(opponent);
				if (list2.Count == 0)
				{
					return 0;
				}
				int num = 0;
				int currentRandomSeed = SaveManager.SaveFile.GetCurrentRandomSeed();
				uint encounterCount = (uint)Plugin.EncounterCount;
				int num2 = currentRandomSeed ^ ((int)encounterCount * -1640531535);
				foreach (List<CardInfo> item in list2)
				{
					for (int i = 0; i < item.Count && num < cap; i++)
					{
						string text = (((Object)(object)item[i] != (Object)null) ? ((Object)item[i]).name : "");
						if (!string.IsNullOrEmpty(text))
						{
							string text2 = text.ToLowerInvariant();
							if (text2.Contains("boulder") || text2.Contains("goldnugget") || text2.Contains("smoke") || text2.Contains("stump"))
							{
								continue;
							}
						}
						if ((double)SeededRandom.Value(num2 ^ (i * 1597463007)) < 0.5)
						{
							int index = SeededRandom.Range(0, list.Count, num2 ^ (24589 + i));
							CardModificationInfo val = list[index];
							if (val == null)
							{
								continue;
							}
							CardInfo val2 = Object.Instantiate<CardInfo>(cardByName);
							List<CardModificationInfo> orCreateMods = ReflectionHelpers.GetOrCreateMods(val2);
							if (orCreateMods == null)
							{
								continue;
							}
							orCreateMods.Add(val);
							ReflectionHelpers.TrySetHauntedFlag(val2);
							item[i] = val2;
							num++;
						}
						if (num >= cap)
						{
							break;
						}
					}
					if (num >= cap)
					{
						break;
					}
				}
				return num;
			}
			catch (Exception ex)
			{
				ManualLogSource logS = Plugin.LogS;
				if (logS != null)
				{
					logS.LogWarning((object)("[Bridge] LeshyBossInjector failed: " + ex.Message));
				}
				return 0;
			}
		}

		private static List<List<CardInfo>> FindAllCardInfoLists(object root)
		{
			List<List<CardInfo>> list = new List<List<CardInfo>>();
			try
			{
				Type type = root.GetType();
				BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
				FieldInfo[] fields = type.GetFields(bindingAttr);
				foreach (FieldInfo fieldInfo in fields)
				{
					try
					{
						object? value = fieldInfo.GetValue(root);
						if (value is List<CardInfo> item)
						{
							list.Add(item);
						}
						if (value is List<List<CardInfo>> collection)
						{
							list.AddRange(collection);
						}
					}
					catch
					{
					}
				}
				PropertyInfo[] properties = type.GetProperties(bindingAttr);
				foreach (PropertyInfo propertyInfo in properties)
				{
					try
					{
						if (propertyInfo.CanRead)
						{
							object? value2 = propertyInfo.GetValue(root, null);
							if (value2 is List<CardInfo> item2)
							{
								list.Add(item2);
							}
							if (value2 is List<List<CardInfo>> collection2)
							{
								list.AddRange(collection2);
							}
						}
					}
					catch
					{
					}
				}
				object obj3 = TryGetMember(root, "blueprint") ?? TryGetMember(root, "Blueprint") ?? TryGetMember(root, "opponentBlueprint");
				if (obj3 != null)
				{
					Type type2 = obj3.GetType();
					BindingFlags bindingAttr2 = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
					fields = type2.GetFields(bindingAttr2);
					foreach (FieldInfo fieldInfo2 in fields)
					{
						try
						{
							object? value3 = fieldInfo2.GetValue(obj3);
							if (value3 is List<CardInfo> item3)
							{
								list.Add(item3);
							}
							if (value3 is List<List<CardInfo>> collection3)
							{
								list.AddRange(collection3);
							}
						}
						catch
						{
						}
					}
					properties = type2.GetProperties(bindingAttr2);
					foreach (PropertyInfo propertyInfo2 in properties)
					{
						try
						{
							if (propertyInfo2.CanRead)
							{
								object? value4 = propertyInfo2.GetValue(obj3, null);
								if (value4 is List<CardInfo> item4)
								{
									list.Add(item4);
								}
								if (value4 is List<List<CardInfo>> collection4)
								{
									list.AddRange(collection4);
								}
							}
						}
						catch
						{
						}
					}
				}
			}
			catch
			{
			}
			List<List<CardInfo>> list2 = new List<List<CardInfo>>();
			HashSet<int> hashSet = new HashSet<int>();
			foreach (List<CardInfo> item5 in list)
			{
				if (item5 != null)
				{
					int hashCode = RuntimeHelpers.GetHashCode(item5);
					if (hashSet.Add(hashCode))
					{
						list2.Add(item5);
					}
				}
			}
			return list2;
		}

		private static object TryGetMember(object obj, string name)
		{
			if (obj == null)
			{
				return null;
			}
			Type type = obj.GetType();
			BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
			FieldInfo field = type.GetField(name, bindingAttr);
			if (field != null)
			{
				try
				{
					return field.GetValue(obj);
				}
				catch
				{
				}
			}
			PropertyInfo property = type.GetProperty(name, bindingAttr);
			if (property != null && property.CanRead)
			{
				try
				{
					return property.GetValue(obj, null);
				}
				catch
				{
				}
			}
			return null;
		}
	}

	internal static class ReflectionHelpers
	{
		public static List<CardInfo> GetDeckCards(Deck deck)
		{
			try
			{
				FieldInfo field = ((object)deck).GetType().GetField("cards", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null && field.GetValue(deck) is List<CardInfo> collection)
				{
					return new List<CardInfo>(collection);
				}
			}
			catch
			{
			}
			return new List<CardInfo>();
		}

		public static void ReplaceDeck(Deck deck, List<CardInfo> newOrder)
		{
			try
			{
				FieldInfo field = ((object)deck).GetType().GetField("cards", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					field.SetValue(deck, new List<CardInfo>(newOrder));
					return;
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logS = Plugin.LogS;
				if (logS != null)
				{
					logS.LogWarning((object)("[Bridge] Reflection replace failed: " + ex.Message));
				}
			}
			try
			{
				int num = 256;
				while (deck.CardsInDeck > 0 && num-- > 0)
				{
					deck.Draw();
				}
				foreach (CardInfo item in newOrder)
				{
					deck.AddCard(item);
				}
			}
			catch (Exception ex2)
			{
				ManualLogSource logS2 = Plugin.LogS;
				if (logS2 != null)
				{
					logS2.LogError((object)("[Bridge] API replace failed: " + ex2));
				}
			}
		}

		public static List<CardModificationInfo> GetOrCreateMods(CardInfo card)
		{
			if ((Object)(object)card == (Object)null)
			{
				return null;
			}
			PropertyInfo property = typeof(CardInfo).GetProperty("Mods", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null)
			{
				List<CardModificationInfo> list = property.GetValue(card) as List<CardModificationInfo>;
				if (list == null)
				{
					list = new List<CardModificationInfo>();
					property.SetValue(card, list);
				}
				return list;
			}
			FieldInfo fieldInfo = typeof(CardInfo).GetField("mods", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? typeof(CardInfo).GetField("Mods", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (fieldInfo != null)
			{
				List<CardModificationInfo> list2 = fieldInfo.GetValue(card) as List<CardModificationInfo>;
				if (list2 == null)
				{
					list2 = new List<CardModificationInfo>();
					fieldInfo.SetValue(card, list2);
				}
				return list2;
			}
			return null;
		}

		public static void Shuffle<T>(IList<T> list)
		{
			if (list != null && list.Count >= 2)
			{
				Random random = new Random(Environment.TickCount);
				for (int num = list.Count - 1; num > 0; num--)
				{
					int index = random.Next(num + 1);
					T value = list[num];
					list[num] = list[index];
					list[index] = value;
				}
			}
		}

		public static void TrySetHauntedFlag(CardInfo card)
		{
			try
			{
				Type type = AccessTools.TypeByName("Infiniscryption.Curses.Patchers.DeathcardHaunt") ?? AccessTools.TypeByName("Infiniscryption.Curses.DeathcardHaunt");
				MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "SetHauntedCardFlag", new Type[1] { typeof(CardInfo) }, (Type[])null) : null);
				if (methodInfo != null)
				{
					methodInfo.Invoke(null, new object[1] { card });
					return;
				}
				PropertyInfo property = typeof(CardInfo).GetProperty("Haunted", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null && property.CanWrite && property.PropertyType == typeof(bool))
				{
					property.SetValue(card, true);
					return;
				}
				FieldInfo field = typeof(CardInfo).GetField("haunted", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null && field.FieldType == typeof(bool))
				{
					field.SetValue(card, true);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logS = Plugin.LogS;
				if (logS != null)
				{
					logS.LogWarning((object)("[Bridge] Could not set haunted flag: " + ex.Message));
				}
			}
		}
	}

	public static void SetupPrefix()
	{
		Plugin.EncounterCount++;
		Plugin.HauntedSwapsThisEncounter = 0;
	}

	public static void SetupPostfix()
	{
		try
		{
			if (!SaveFile.IsAscension)
			{
				return;
			}
			if (Plugin.C_EnablePoolMix.Value)
			{
				CardDrawPiles3D instance = Singleton<CardDrawPiles3D>.Instance;
				if ((Object)(object)instance != (Object)null && (Object)(object)instance.SideDeck != (Object)null)
				{
					PoolMixer.MixIntoDeckKeepingSize(instance.SideDeck, Plugin.C_MaxDeathcardsToBlend.Value);
				}
			}
			if (!Plugin.C_MixIntoLeshyBoss.Value || !IsLeshyBossEncounter())
			{
				return;
			}
			TurnManager instance2 = Singleton<TurnManager>.Instance;
			object obj = (((object)instance2)?.GetType().GetProperty("Opponent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(instance2, null);
			if (obj == null)
			{
				return;
			}
			int num = LeshyBossInjector.InjectIntoOpponentPlan(obj, Math.Max(1, Plugin.C_LeshyBossBlendCap.Value));
			if (num > 0)
			{
				ManualLogSource logS = Plugin.LogS;
				if (logS != null)
				{
					logS.LogInfo((object)$"[Bridge] Injected {num} deathcard(s) into Leshy boss plan.");
				}
			}
			else
			{
				ManualLogSource logS2 = Plugin.LogS;
				if (logS2 != null)
				{
					logS2.LogInfo((object)"[Bridge] Leshy boss plan injection found no editable plan/slots.");
				}
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logS3 = Plugin.LogS;
			if (logS3 != null)
			{
				logS3.LogError((object)("[Bridge] SetupPostfix error: " + ex));
			}
		}
	}

	public static void CleanupPostfix()
	{
		try
		{
			if (SaveFile.IsAscension && Plugin.C_EverlastingVengeance.Value)
			{
				HauntForcer.ForceHauntEarly("CleanupPostfix");
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logS = Plugin.LogS;
			if (logS != null)
			{
				logS.LogWarning((object)("[Bridge] CleanupPostfix failed: " + ex.Message));
			}
		}
	}

	private static bool IsLeshyBossEncounter()
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			Scene activeScene = SceneManager.GetActiveScene();
			string text = ((Scene)(ref activeScene)).name ?? "";
			TurnManager instance = Singleton<TurnManager>.Instance;
			object obj = (((object)instance)?.GetType().GetProperty("Opponent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(instance, null);
			if (obj == null)
			{
				return false;
			}
			string fullName = obj.GetType().FullName;
			bool flag = false;
			PropertyInfo property = obj.GetType().GetProperty("IsBoss", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null && property.GetValue(obj, null) is bool flag2)
			{
				flag = flag2;
			}
			bool num = (fullName ?? "").IndexOf("Leshy", StringComparison.OrdinalIgnoreCase) >= 0 || (text ?? "").IndexOf("Leshy", StringComparison.OrdinalIgnoreCase) >= 0;
			bool flag3 = flag || (text ?? "").IndexOf("Boss", StringComparison.OrdinalIgnoreCase) >= 0 || (fullName ?? "").IndexOf("Boss", StringComparison.OrdinalIgnoreCase) >= 0;
			return num && flag3;
		}
		catch
		{
			return false;
		}
	}
}
internal static class HauntForcer
{
	public const int FORCE_LEVEL = 11;

	private static bool _inForce = false;

	private static int _lastAppliedLevel = -1;

	private static int _lastAppliedFrame = -1;

	private static bool _cacheReady = false;

	private static List<(MethodInfo mi, object instance)> _setterMethods;

	private static List<MemberInfo> _hauntStatics;

	public static void ForceHauntEarly(string reason)
	{
		if (!Plugin.C_EverlastingVengeance.Value || _inForce || (Time.frameCount == _lastAppliedFrame && _lastAppliedLevel == 11))
		{
			return;
		}
		_inForce = true;
		try
		{
			int num = TryReadCurrentHaunt();
			if (num == 11)
			{
				ManualLogSource logS = Plugin.LogS;
				if (logS != null)
				{
					logS.LogInfo((object)$"[Bridge] Haunt already {num} (skip, reason: {reason}).");
				}
				return;
			}
			int num2 = TryWriteViaModdedSaveManager(11);
			int num3 = CallAnyHauntSetters(11);
			int num4 = BumpStaticHauntMembers(11);
			_lastAppliedLevel = 11;
			_lastAppliedFrame = Time.frameCount;
			ManualLogSource logS2 = Plugin.LogS;
			if (logS2 != null)
			{
				logS2.LogInfo((object)$"[Bridge] EverlastingVengeance — forced haunt (reason: {reason}), writes={num2}, setterCalls={num3}, staticUpdated={num4}.");
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logS3 = Plugin.LogS;
			if (logS3 != null)
			{
				logS3.LogWarning((object)("[Bridge] HauntForcer failed (" + reason + "): " + ex.Message));
			}
		}
		finally
		{
			_inForce = false;
		}
	}

	public static int TryReadCurrentHaunt()
	{
		string[] array = new string[4] { "InscryptionAPI.Saves.ModdedSaveManager", "InscryptionAPI.Helpers.ModdedSaveManager", "InscryptionAPI.ModdedSaveManager", "InscryptionAPI.Helpers.SaveHelper" };
		for (int i = 0; i < array.Length; i++)
		{
			Type type = AccessTools.TypeByName(array[i]);
			if (type == null)
			{
				continue;
			}
			MethodInfo methodInfo = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => string.Equals(m.Name, "GetValue", StringComparison.OrdinalIgnoreCase) && m.GetParameters().Length >= 2 && m.GetParameters()[0].ParameterType == typeof(string) && m.GetParameters()[1].ParameterType == typeof(string));
			if (!(methodInfo != null))
			{
				continue;
			}
			try
			{
				object obj = ((methodInfo.GetParameters().Length == 2) ? methodInfo.Invoke(null, new object[2] { "zorro.inscryption.infiniscryption.curses", "HAUNT_LEVEL" }) : methodInfo.Invoke(null, new object[3] { "zorro.inscryption.infiniscryption.curses", "HAUNT_LEVEL", -1 }));
				if (obj is int result)
				{
					return result;
				}
				if (obj is string s && int.TryParse(s, out var result2))
				{
					return result2;
				}
			}
			catch
			{
			}
		}
		Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(delegate(Assembly a)
		{
			string text = a.GetName().Name ?? "";
			return text.IndexOf("Infiniscryption", StringComparison.OrdinalIgnoreCase) >= 0 && text.IndexOf("Curses", StringComparison.OrdinalIgnoreCase) >= 0;
		});
		if (assembly != null)
		{
			Type[] types = assembly.GetTypes();
			foreach (Type type2 in types)
			{
				PropertyInfo[] properties = type2.GetProperties(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (PropertyInfo propertyInfo in properties)
				{
					if (!propertyInfo.CanRead || !propertyInfo.Name.ToLowerInvariant().Contains("haunt") || (!(propertyInfo.PropertyType == typeof(int)) && !(propertyInfo.PropertyType == typeof(float))))
					{
						continue;
					}
					try
					{
						object value = propertyInfo.GetValue(null, null);
						if (value is int result3)
						{
							return result3;
						}
						if (value is float)
						{
							float num = (float)value;
							return (int)num;
						}
					}
					catch
					{
					}
				}
				FieldInfo[] fields = type2.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo in fields)
				{
					if (!fieldInfo.Name.ToLowerInvariant().Contains("haunt") || (!(fieldInfo.FieldType == typeof(int)) && !(fieldInfo.FieldType == typeof(float))))
					{
						continue;
					}
					try
					{
						object value2 = fieldInfo.GetValue(null);
						if (value2 is int result4)
						{
							return result4;
						}
						if (value2 is float)
						{
							float num2 = (float)value2;
							return (int)num2;
						}
					}
					catch
					{
					}
				}
			}
		}
		return -1;
	}

	private static int TryWriteViaModdedSaveManager(int level)
	{
		int num = 0;
		try
		{
			string[] obj = new string[4] { "InscryptionAPI.Saves.ModdedSaveManager", "InscryptionAPI.Helpers.ModdedSaveManager", "InscryptionAPI.ModdedSaveManager", "InscryptionAPI.Helpers.SaveHelper" };
			Type type = null;
			string[] array = obj;
			for (int i = 0; i < array.Length; i++)
			{
				type = AccessTools.TypeByName(array[i]);
				if (type != null)
				{
					break;
				}
			}
			if (type == null)
			{
				return 0;
			}
			MethodInfo methodInfo = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo m)
			{
				if (!string.Equals(m.Name, "SetValue", StringComparison.OrdinalIgnoreCase))
				{
					return false;
				}
				ParameterInfo[] parameters = m.GetParameters();
				return parameters.Length == 3 && parameters[0].ParameterType == typeof(string) && parameters[1].ParameterType == typeof(string);
			});
			if (methodInfo == null)
			{
				return 0;
			}
			object obj2 = ((methodInfo.GetParameters()[2].ParameterType == typeof(int)) ? ((object)level) : ((object)level));
			methodInfo.Invoke(null, new object[3] { "zorro.inscryption.infiniscryption.curses", "HAUNT_LEVEL", obj2 });
			num++;
			MethodInfo method = type.GetMethod("Save", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			try
			{
				method?.Invoke(null, null);
			}
			catch
			{
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logS = Plugin.LogS;
			if (logS != null)
			{
				logS.LogWarning((object)("[Bridge] ModdedSaveManager write failed: " + ex.Message));
			}
		}
		return num;
	}

	private static void EnsureCache()
	{
		if (_cacheReady)
		{
			return;
		}
		_setterMethods = new List<(MethodInfo, object)>();
		_hauntStatics = new List<MemberInfo>();
		try
		{
			Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(delegate(Assembly a)
			{
				string text4 = a.GetName().Name ?? "";
				return text4.IndexOf("Infiniscryption", StringComparison.OrdinalIgnoreCase) >= 0 && text4.IndexOf("Curses", StringComparison.OrdinalIgnoreCase) >= 0;
			});
			if (assembly == null)
			{
				return;
			}
			Type[] types = assembly.GetTypes();
			foreach (Type type in types)
			{
				string text = (type.FullName ?? "").ToLowerInvariant();
				if (!text.Contains("haunt") && !text.Contains("deathcard") && !text.Contains("marker") && !text.Contains("map"))
				{
					continue;
				}
				MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo methodInfo in methods)
				{
					string text2 = methodInfo.Name.ToLowerInvariant();
					if (!text2.Contains("haunt") || (!text2.Contains("set") && !text2.Contains("update") && !text2.Contains("force") && !text2.Contains("apply")))
					{
						continue;
					}
					ParameterInfo[] parameters = methodInfo.GetParameters();
					if (parameters.Length != 1 || (!(parameters[0].ParameterType == typeof(int)) && !(parameters[0].ParameterType == typeof(float))))
					{
						continue;
					}
					if (methodInfo.IsStatic)
					{
						_setterMethods.Add((methodInfo, null));
						continue;
					}
					object obj = TryGetInstanceCandidate(type);
					if (obj != null)
					{
						_setterMethods.Add((methodInfo, obj));
					}
				}
				FieldInfo[] fields = type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo in fields)
				{
					if (fieldInfo.Name.ToLowerInvariant().Contains("haunt") && (fieldInfo.FieldType == typeof(int) || fieldInfo.FieldType == typeof(float)))
					{
						_hauntStatics.Add(fieldInfo);
					}
				}
				PropertyInfo[] properties = type.GetProperties(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (PropertyInfo propertyInfo in properties)
				{
					string text3 = propertyInfo.Name.ToLowerInvariant();
					if (propertyInfo.CanWrite && text3.Contains("haunt") && (propertyInfo.PropertyType == typeof(int) || propertyInfo.PropertyType == typeof(float)))
					{
						MethodInfo setMethod = propertyInfo.GetSetMethod(nonPublic: true);
						if (setMethod != null && setMethod.IsStatic)
						{
							_hauntStatics.Add(propertyInfo);
						}
					}
				}
			}
		}
		catch
		{
		}
		finally
		{
			_cacheReady = true;
		}
	}

	private static int CallAnyHauntSetters(int level)
	{
		EnsureCache();
		int num = 0;
		foreach (var (methodInfo, obj) in _setterMethods)
		{
			try
			{
				methodInfo.Invoke(obj, new object[1] { level });
				num++;
			}
			catch
			{
			}
		}
		return num;
	}

	private static int BumpStaticHauntMembers(int level)
	{
		EnsureCache();
		int num = 0;
		foreach (MemberInfo hauntStatic in _hauntStatics)
		{
			try
			{
				if (hauntStatic is FieldInfo fieldInfo && fieldInfo.IsStatic)
				{
					if (fieldInfo.FieldType == typeof(int))
					{
						fieldInfo.SetValue(null, level);
						num++;
					}
					else if (fieldInfo.FieldType == typeof(float))
					{
						fieldInfo.SetValue(null, (float)level);
						num++;
					}
				}
				else
				{
					if (!(hauntStatic is PropertyInfo propertyInfo) || !propertyInfo.CanWrite)
					{
						continue;
					}
					MethodInfo? setMethod = propertyInfo.GetSetMethod(nonPublic: true);
					if ((object)setMethod != null && setMethod.IsStatic)
					{
						if (propertyInfo.PropertyType == typeof(int))
						{
							propertyInfo.SetValue(null, level);
							num++;
						}
						else if (propertyInfo.PropertyType == typeof(float))
						{
							propertyInfo.SetValue(null, (float)level);
							num++;
						}
					}
					continue;
				}
			}
			catch
			{
			}
		}
		return num;
	}

	private static object TryGetInstanceCandidate(Type t)
	{
		BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
		try
		{
			FieldInfo fieldInfo = t.GetField("Instance", bindingAttr) ?? t.GetField("instance", bindingAttr) ?? t.GetField("Singleton", bindingAttr);
			if (fieldInfo != null)
			{
				return fieldInfo.GetValue(null);
			}
			PropertyInfo propertyInfo = t.GetProperty("Instance", bindingAttr) ?? t.GetProperty("instance", bindingAttr) ?? t.GetProperty("Singleton", bindingAttr);
			if (propertyInfo != null && propertyInfo.CanRead)
			{
				return propertyInfo.GetValue(null, null);
			}
		}
		catch
		{
		}
		try
		{
			if (typeof(Object).IsAssignableFrom(t))
			{
				return Object.FindObjectOfType(t);
			}
		}
		catch
		{
		}
		return null;
	}
}