using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using DiskCardGame; using HarmonyLib; using InscryptionAPI.Card; using OLD_SA.Dialogue; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("OldSa_CardTraits")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("OldSa_CardTraits")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("4ebfbc44-c273-4a7d-92ed-5b86f52380fa")] [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 OLD_SA.CardTraits; public static class CardTrait { public const string Featherless = "OLD_SA_Featherless"; public const string Jackal = "OLD_SA_Jackal"; public const string WeakHatchling = "OLD_SA_WeakHatchling"; public const string Infinite = "OLD_SA_Infinite"; } public static class CardTraitAPI { public static HashSet TriggeredTraitDialogues = new HashSet(); public static bool ShouldTriggerDialogue(string dialogueID) { if (string.IsNullOrEmpty(dialogueID)) { return false; } if (TriggeredTraitDialogues.Contains(dialogueID)) { return false; } TriggeredTraitDialogues.Add(dialogueID); OLD_SA_SaveManager.Save(); return true; } public static List GetTriggeredDialogues() { return new List(TriggeredTraitDialogues); } public static void LoadTriggeredDialogues(List data) { TriggeredTraitDialogues.Clear(); if (data == null) { return; } foreach (string datum in data) { if (!string.IsNullOrEmpty(datum)) { TriggeredTraitDialogues.Add(datum); } } } public static void ClearTriggeredDialogues() { TriggeredTraitDialogues.Clear(); } } public static class CardTraitConditionAPI { private static Dictionary RequiredAdjacent = new Dictionary(); public static void RegisterAdjacentRequirement(string trait, string cardName) { RequiredAdjacent[trait] = cardName; } public static bool CheckAdjacentRequirement(PlayableCard card, string trait) { if (!RequiredAdjacent.ContainsKey(trait)) { return true; } string text = RequiredAdjacent[trait]; foreach (CardSlot adjacentSlot in Singleton.Instance.GetAdjacentSlots(card.Slot)) { if ((Object)(object)adjacentSlot.Card != (Object)null && ((Object)((Card)adjacentSlot.Card).Info).name == text) { return true; } } return false; } } public static class CardTraitDialogueManager { public static IEnumerator TryTrigger(PlayableCard card) { if (!((Object)(object)card == (Object)null) && !((Object)(object)((Card)card).Info == (Object)null)) { string dialogueID = CardExtensions.GetExtendedProperty(((Card)card).Info, "OLD_SA_TraitDialogueID"); string dialogue = CardExtensions.GetExtendedProperty(((Card)card).Info, "OLD_SA_TraitDialogue"); yield return TryTrigger(dialogueID, dialogue); } } public static IEnumerator TryTrigger(string dialogueID, string dialogue) { if (!string.IsNullOrEmpty(dialogueID) && !string.IsNullOrEmpty(dialogue) && !CardTraitDialogueSaveData.HasTriggered(dialogueID)) { yield return Singleton.Instance.ShowUntilInput(dialogue, -0.65f, 0.4f, (Emotion)0, (LetterAnimation)0, (Speaker)0, (string[])null, true); CardTraitDialogueSaveData.AddTriggered(dialogueID); } } } public static class CardTraitDialogueQueue { private static string id; private static string text; public static void Add(string dialogueID, string dialogue) { if (!string.IsNullOrEmpty(dialogueID) && !string.IsNullOrEmpty(dialogue)) { id = dialogueID; text = dialogue; } } public static bool HasDialogue() { return !string.IsNullOrEmpty(id) && !string.IsNullOrEmpty(text); } public static IEnumerator Play() { if (HasDialogue()) { string currentID = id; string currentText = text; id = null; text = null; yield return Singleton.Instance.ShowUntilInput(currentText, -0.65f, 0.4f, (Emotion)0, (LetterAnimation)0, (Speaker)0, (string[])null, true); CardTraitDialogueSaveData.AddTriggered(currentID); } } } public class CardTraitDialogueRunner : MonoBehaviour { private static CardTraitDialogueRunner instance; private bool playing; public static CardTraitDialogueRunner Instance { get { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown if ((Object)(object)instance == (Object)null) { GameObject val = new GameObject("CardTraitDialogueRunner"); Object.DontDestroyOnLoad((Object)(object)val); instance = val.AddComponent(); } return instance; } } public void Queue(string dialogueID, string dialogue) { CardTraitDialogueQueue.Add(dialogueID, dialogue); } private void Update() { if (!playing && CardTraitDialogueQueue.HasDialogue()) { ((MonoBehaviour)this).StartCoroutine(PlayQueue()); } } private IEnumerator PlayQueue() { playing = true; yield return CardTraitDialogueQueue.Play(); playing = false; } } public static class CardTraitDialogueSaveData { private static HashSet Triggered = new HashSet(); public static bool HasTriggered(string id) { if (string.IsNullOrEmpty(id)) { return false; } return Triggered.Contains(id); } public static void AddTriggered(string id) { if (!string.IsNullOrEmpty(id) && !Triggered.Contains(id)) { Triggered.Add(id); } } public static void Remove(string id) { if (!string.IsNullOrEmpty(id)) { Triggered.Remove(id); } } public static void Clear() { Triggered.Clear(); } public static List GetSaveList() { return new List(Triggered); } public static void LoadFromList(List data) { Triggered.Clear(); if (data == null) { return; } foreach (string datum in data) { if (!string.IsNullOrEmpty(datum)) { Triggered.Add(datum); } } } } [HarmonyPatch(typeof(PlayableCard), "AddTemporaryMod")] public static class CardTraitFlyingPatch { [HarmonyPrefix] public static bool Prefix(PlayableCard __instance, CardModificationInfo mod) { if ((Object)(object)__instance == (Object)null || (Object)(object)((Card)__instance).Info == (Object)null || mod == null || mod.abilities == null) { return true; } if (!mod.abilities.Contains((Ability)19)) { return true; } if (mod.singletonId == "wind_blessing_flying") { return true; } if (!CardTraitHelper.HasTrait(__instance, "OLD_SA_Featherless")) { return true; } ((MonoBehaviour)__instance).StartCoroutine(CardTraitDialogueManager.TryTrigger(__instance)); mod.abilities.Remove((Ability)19); if (mod.abilities.Count == 0 && mod.attackAdjustment == 0 && mod.healthAdjustment == 0 && (mod.negateAbilities == null || mod.negateAbilities.Count == 0)) { return false; } return true; } } public static class CardTraitHelper { public static bool HasTrait(PlayableCard card, string trait) { if ((Object)(object)card == (Object)null || (Object)(object)((Card)card).Info == (Object)null) { return false; } if (CardExtensions.GetExtendedProperty(((Card)card).Info, trait) == "true") { return true; } foreach (CardModificationInfo mod in ((Card)card).Info.Mods) { if (mod.singletonId == trait) { return true; } } foreach (CardModificationInfo temporaryMod in card.TemporaryMods) { if (temporaryMod.singletonId == trait) { return true; } } return false; } } [HarmonyPatch(typeof(DeckInfo), "ModifyCard")] public static class FeatherlessDeckModifyPatch { [HarmonyPrefix] public static bool Prefix(CardInfo card, CardModificationInfo mod) { //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Invalid comparison between Unknown and I4 if ((Object)(object)card == (Object)null || mod == null || mod.abilities == null) { return true; } if (!mod.abilities.Contains((Ability)19)) { return true; } if (CardExtensions.GetExtendedProperty(card, "OLD_SA_Featherless") != "true") { return true; } mod.abilities.Remove((Ability)19); string extendedProperty = CardExtensions.GetExtendedProperty(card, "OLD_SA_TraitDialogueID"); string extendedProperty2 = CardExtensions.GetExtendedProperty(card, "OLD_SA_TraitDialogue"); CardTraitDialogueQueue.Add(extendedProperty, extendedProperty2); if (mod.abilities.Count == 0 && mod.attackAdjustment == 0 && mod.healthAdjustment == 0 && (mod.negateAbilities == null || mod.negateAbilities.Count == 0) && (mod.addGemCost == null || mod.addGemCost.Count == 0) && (mod.specialAbilities == null || mod.specialAbilities.Count == 0) && (int)mod.statIcon == 0) { return false; } return true; } } [HarmonyPatch(typeof(RandomAbility), "ChooseAbility")] public static class FeatherlessRandomAbilityPatch { [HarmonyPostfix] public static void Postfix(RandomAbility __instance, ref Ability __result) { //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Expected I4, but got Unknown if ((Object)(object)__instance == (Object)null) { return; } PlayableCard card = Traverse.Create((object)__instance).Field("Card").GetValue(); if (!((Object)(object)card == (Object)null) && !((Object)(object)((Card)card).Info == (Object)null) && CardTraitHelper.HasTrait(card, "OLD_SA_Featherless") && (int)__result == 19) { List learnedAbilities = AbilitiesUtil.GetLearnedAbilities(false, 0, 5, (AbilityMetaCategory)1); learnedAbilities.Remove((Ability)19); learnedAbilities.RemoveAll((Ability x) => card.HasAbility(x)); if (learnedAbilities.Count > 0) { __result = (Ability)(int)learnedAbilities[SeededRandom.Range(0, learnedAbilities.Count, SaveManager.SaveFile.GetCurrentRandomSeed())]; } else { __result = (Ability)15; } } } } [HarmonyPatch(typeof(PlayableCard), "AddTemporaryMod")] public static class InfinitePermanentStatsPatch { [HarmonyPrefix] public static bool Prefix(PlayableCard __instance, CardModificationInfo mod) { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown if ((Object)(object)__instance == (Object)null || (Object)(object)((Card)__instance).Info == (Object)null || mod == null) { return true; } if (!CardTraitHelper.HasTrait(__instance, "OLD_SA_Infinite")) { return true; } int num = ((mod.attackAdjustment > 0) ? mod.attackAdjustment : 0); int num2 = ((mod.healthAdjustment > 0) ? mod.healthAdjustment : 0); if (num == 0 && num2 == 0) { return true; } CardModificationInfo val = new CardModificationInfo(); val.attackAdjustment = num; val.healthAdjustment = num2; ((Card)__instance).Info.Mods.Add(val); mod.attackAdjustment -= num; mod.healthAdjustment -= num2; if (mod.attackAdjustment != 0 || mod.healthAdjustment != 0 || mod.bloodCostAdjustment != 0 || mod.bonesCostAdjustment != 0 || mod.energyCostAdjustment != 0 || mod.gemify || mod.fromOverclock || (mod.abilities != null && mod.abilities.Count > 0) || (mod.negateAbilities != null && mod.negateAbilities.Count > 0)) { return true; } return false; } } public static class JackalFusionManager { public static void ReplaceDeckCards(CardInfo jackal, CardInfo wolf, CardInfo result) { if (RunState.Run == null) { return; } DeckInfo playerDeck = RunState.Run.playerDeck; if (playerDeck == null) { return; } if ((Object)(object)jackal != (Object)null) { ((CardCollectionInfo)playerDeck).RemoveCardByName(((Object)jackal).name); } if ((Object)(object)wolf != (Object)null) { ((CardCollectionInfo)playerDeck).RemoveCardByName(((Object)wolf).name); } if ((Object)(object)result != (Object)null) { ((CardCollectionInfo)playerDeck).AddCard(result); } foreach (CardInfo card in ((CardCollectionInfo)playerDeck).Cards) { } } } public static class JackalTraitAPI { public const string Trait = "OLD_SA_Jackal"; public const string ConditionCard = "OLD_SA_TraitConditionCard"; public const string TransformCard = "OLD_SA_TraitTransformCard"; public const string DialogueID = "OLD_SA_TraitDialogueID"; public const string Dialogue = "OLD_SA_TraitDialogue"; public static string GetConditionCard(PlayableCard card) { if (!CardTraitHelper.HasTrait(card, "OLD_SA_Jackal")) { return null; } return CardExtensions.GetExtendedProperty(((Card)card).Info, "OLD_SA_TraitConditionCard"); } public static string GetTransformCard(PlayableCard card) { if (!CardTraitHelper.HasTrait(card, "OLD_SA_Jackal")) { return null; } return CardExtensions.GetExtendedProperty(((Card)card).Info, "OLD_SA_TraitTransformCard"); } public static string GetDialogueID(PlayableCard card) { if (!CardTraitHelper.HasTrait(card, "OLD_SA_Jackal")) { return null; } return CardExtensions.GetExtendedProperty(((Card)card).Info, "OLD_SA_TraitDialogueID"); } public static string GetDialogue(PlayableCard card) { if (!CardTraitHelper.HasTrait(card, "OLD_SA_Jackal")) { return null; } return CardExtensions.GetExtendedProperty(((Card)card).Info, "OLD_SA_TraitDialogue"); } } public static class JackalTraitPatch { private static bool transforming; public static IEnumerator CheckJackalFusion() { if (transforming || (Object)(object)Singleton.Instance == (Object)null) { yield break; } foreach (CardSlot slot in Singleton.Instance.GetSlots(true)) { if ((Object)(object)slot.Card == (Object)null) { continue; } PlayableCard jackal = slot.Card; if (CardTraitHelper.HasTrait(jackal, "OLD_SA_Jackal")) { PlayableCard wolf = FindWolf(jackal); if ((Object)(object)wolf != (Object)null) { yield return Fusion(jackal, wolf); yield break; } } } } private static PlayableCard FindWolf(PlayableCard jackal) { foreach (CardSlot adjacentSlot in Singleton.Instance.GetAdjacentSlots(jackal.Slot)) { if ((Object)(object)adjacentSlot.Card == (Object)null || !(((Object)((Card)adjacentSlot.Card).Info).name == JackalTraitAPI.GetConditionCard(jackal))) { continue; } return adjacentSlot.Card; } return null; } private static IEnumerator Fusion(PlayableCard jackal, PlayableCard wolf) { transforming = true; CardInfo result = CardLoader.GetCardByName(JackalTraitAPI.GetTransformCard(jackal)); if ((Object)(object)result == (Object)null) { transforming = false; yield break; } CardSlot spawnSlot = jackal.Slot; CardInfo jackalInfo = ((Card)jackal).Info; CardInfo wolfInfo = ((Card)wolf).Info; yield return CardTraitDialogueManager.TryTrigger(jackal); if ((Object)(object)jackal != (Object)null) { ((Card)jackal).Anim.ExitBoard(0.5f, Vector3.zero); } if ((Object)(object)wolf != (Object)null) { ((Card)wolf).Anim.ExitBoard(0.5f, Vector3.zero); } yield return (object)new WaitForSeconds(0.6f); if ((Object)(object)jackal != (Object)null) { Object.Destroy((Object)(object)((Component)jackal).gameObject); } if ((Object)(object)wolf != (Object)null) { Object.Destroy((Object)(object)((Component)wolf).gameObject); } yield return (object)new WaitForSeconds(0.2f); yield return Singleton.Instance.CreateCardInSlot(result, spawnSlot, 0.1f, true); JackalFusionManager.ReplaceDeckCards(jackalInfo, wolfInfo, result); transforming = false; } } [HarmonyPatch(typeof(BoardManager), "AssignCardToSlot")] public static class JackalFusionTriggerPatch { public static IEnumerator Postfix(IEnumerator __result) { while (__result.MoveNext()) { yield return __result.Current; } yield return (object)new WaitForSeconds(0.05f); if (HasJackalOnBoard()) { yield return JackalTraitPatch.CheckJackalFusion(); } } private static bool HasJackalOnBoard() { if ((Object)(object)Singleton.Instance == (Object)null) { return false; } foreach (CardSlot slot in Singleton.Instance.GetSlots(true)) { if ((Object)(object)slot.Card == (Object)null || !CardTraitHelper.HasTrait(slot.Card, "OLD_SA_Jackal")) { continue; } return true; } return false; } } [BepInPlugin("OLD_SA.CardTraits", "OLD SA CardTraits", "1.0.0")] public class Plugin : BaseUnityPlugin { private void Awake() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown Harmony val = new Harmony("OLD_SA.CardTraits"); val.PatchAll(); _ = CardTraitDialogueRunner.Instance; ((BaseUnityPlugin)this).Logger.LogInfo((object)"OLD SA CardTraits Loaded"); } } [HarmonyPatch(typeof(Evolve), "OnUpkeep")] public static class WeakHatchlingPatch { private static readonly FieldRef NumTurnsInPlay = AccessTools.FieldRefAccess("numTurnsInPlay"); private static readonly MethodInfo PreSuccessfulTriggerSequenceMethod = AccessTools.Method(typeof(AbilityBehaviour), "PreSuccessfulTriggerSequence", (Type[])null, (Type[])null); [HarmonyPrefix] public static bool Prefix(Evolve __instance, bool playerUpkeep, ref IEnumerator __result) { if ((Object)(object)__instance == (Object)null) { return true; } PlayableCard card = GetCard(__instance); if ((Object)(object)card == (Object)null || !CardTraitHelper.HasTrait(card, "OLD_SA_WeakHatchling")) { return true; } __result = WeakHatchlingOnUpkeep(__instance, card, playerUpkeep); return false; } private static PlayableCard GetCard(Evolve evolve) { if ((Object)(object)evolve == (Object)null) { return null; } PropertyInfo propertyInfo = AccessTools.Property(typeof(AbilityBehaviour), "Card"); if (propertyInfo == null) { return null; } object? value = propertyInfo.GetValue(evolve, null); return (PlayableCard)((value is PlayableCard) ? value : null); } private static IEnumerator WeakHatchlingOnUpkeep(Evolve evolve, PlayableCard card, bool playerUpkeep) { if (!((Object)(object)evolve == (Object)null) && !((Object)(object)card == (Object)null) && !card.Dead && card.OpponentCard != playerUpkeep) { int turnsToEvolve = ((!((Object)(object)((Card)card).Info != (Object)null) || ((Card)card).Info.evolveParams == null) ? 1 : ((Card)card).Info.evolveParams.turnsToEvolve); int numTurnsInPlay = NumTurnsInPlay.Invoke(evolve); numTurnsInPlay++; NumTurnsInPlay.Invoke(evolve) = numTurnsInPlay; int remaining = Mathf.Max(1, turnsToEvolve - numTurnsInPlay); ((Card)card).RenderInfo.OverrideAbilityIcon((Ability)5, ResourceBank.Get("Art/Cards/AbilityIcons/ability_evolve_" + remaining)); ((Card)card).RenderCard(); if (numTurnsInPlay >= turnsToEvolve) { yield return InvokePreSuccessfulTriggerSequence(evolve); CardModificationInfo growth = new CardModificationInfo { attackAdjustment = 1, healthAdjustment = 1, singletonId = "OLD_SA_WeakHatchling_Growth" }; card.AddTemporaryMod(growth); RemoveEvolve(card); ((Card)card).RenderCard(); card.OnStatsChanged(); card.UpdateFaceUpOnBoardEffects(); yield return CardTraitDialogueManager.TryTrigger(card); } } } private static IEnumerator InvokePreSuccessfulTriggerSequence(Evolve evolve) { if ((Object)(object)evolve == (Object)null || PreSuccessfulTriggerSequenceMethod == null) { yield break; } object result = PreSuccessfulTriggerSequenceMethod.Invoke(evolve, null); if (result is IEnumerator sequence) { while (sequence.MoveNext()) { yield return sequence.Current; } } } private static void RemoveEvolve(PlayableCard card) { if ((Object)(object)card == (Object)null) { return; } if ((Object)(object)card.TriggerHandler != (Object)null) { card.TriggerHandler.RemoveAbility((Ability)5); } while (true) { CardModificationInfo val = null; foreach (CardModificationInfo temporaryMod in card.TemporaryMods) { if (temporaryMod == null || temporaryMod.abilities == null || !temporaryMod.abilities.Contains((Ability)5)) { continue; } val = temporaryMod; break; } if (val == null) { break; } card.RemoveTemporaryMod(val, true); } } }