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;
}
}