diff --git a/CHANGELOG.md b/CHANGELOG.md index a68919c9..238714d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,11 +9,13 @@ - This comes with 5 new Dictionaries `BaseGameCustomAppearances` (Internal), `BaseGameCustomAppearancesPublic`, `ModdedCustomAppearancesDictionary` (Internal), `ModdedCustomAppearancesDictionaryPublic`, `CombinedCustomAppearancesDictionaryPublic`. - This system is stored under the `TextureHelper` class. * Improved In-Code Documentation via XML Comments. +* Added a way to easily patch and modify the default bone gain when a player's card dies. +* Added the following class: `DefaultBoneGainOnDeath` and the following methods within that class: `DefaultBoneGainOnDeath.ShouldDoDefaultBoneGain` and `DefaultBoneGainOnDeeath.DoDefaultBoneGain` # 2.24.1 -* Added Extensions for setting the IconTexture of a Starter Deck. -* Added 2 Alternate Constructors for making a StarterDeck with a Display Name different from its Internal Name. -* Added Getters and Setters for Starter Deck Unlock Level. +- Added Extensions for setting the IconTexture of a Starter Deck. +- Added 2 Alternate Constructors for making a StarterDeck with a Display Name different from its Internal Name. +- Added Getters and Setters for Starter Deck Unlock Level. # 2.24.0 - Fixed Part2CardCostRender.UpdatePlayableCardCost not being invoked @@ -792,4 +794,4 @@ - Custom cards are added via the **CustomCard** constructor rather than through the **AddCard** method. # v1.1 -- Hooked into a much more sensible method to load the cards into the card pool. \ No newline at end of file +- Hooked into a much more sensible method to load the cards into the card pool. diff --git a/InscryptionAPI/Card/DefaultBoneGainOnDeath.cs b/InscryptionAPI/Card/DefaultBoneGainOnDeath.cs new file mode 100644 index 00000000..fc40bb0b --- /dev/null +++ b/InscryptionAPI/Card/DefaultBoneGainOnDeath.cs @@ -0,0 +1,135 @@ +using DiskCardGame; +using HarmonyLib; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; + +namespace InscryptionAPI.Card; + +/// +/// Class for handling exposing the bone-on-death behaviour present in the vanilla PlayableCard.Die method. +/// Allows for modifying the default behaviour through Harmony patches. +/// +[HarmonyPatch] +public class DefaultBoneGainOnDeath { + // this method does not need XML documentation since it's purely internal to make the other two methods work. + // I also don't want to explain how transpilers work. + [HarmonyTranspiler] + [HarmonyPatch(typeof(PlayableCard), nameof(PlayableCard.Die), MethodType.Enumerator)] + private static IEnumerable InsertBoneMethodHook(IEnumerable instructions) + { + // === We want to turn this + + // if (!HasAbility(Ability.QuadrupleBones) && slotBeforeDeath.IsPlayerSlot) { + // yield return Singleton.Instance.AddBones(1, slotBeforeDeath); + // } + + // === Into this + + // if (ShouldDoDefaultBoneGain(base.Card, slotBeforeDeath) { + // yield return DoDefaultBoneGain(base.Card, slotBeforeDeath, 1); + // } + + // === + List codes = new(instructions); + + for (int i = 0; i < codes.Count; i++) { + // there are two instances where this is true, but we only care about the first instance + // the second instance will be ignored + if (codes[i].opcode == OpCodes.Ldc_I4_S && codes[i].operand.ToString() == "12") { + /* + * // if (!playableCard.HasAbility(Ability.QuadrupleBones) && 5__2.IsPlayerSlot) + * ldloc.1 + * ldc.i4.s 12 + * call instance bool DiskCardGame.PlayableCard::HasAbility(valuetype DiskCardGame.Ability) + * brtrue.s IL_022f + * + * ldarg.0 + * ldfld class DiskCardGame.CardSlot DiskCardGame.PlayableCard/'d__98'::'5__2' + * callvirt instance bool DiskCardGame.CardSlot::get_IsPlayerSlot() + * brfalse.s IL_022f + * + * = + * + * // if (ShouldDoDefaultBoneGain(playableCard, 5__2) + * ldloc.1 + * ldarg.0 + * ldfld class DiskCardGame.CardSlot DiskCardGame.PlayableCard/'d__98'::'5__2' + * callvirt ShouldDo... + * brfalse.s IL_022f + */ + MethodInfo validationMethod = AccessTools.Method( + typeof(DefaultBoneGainOnDeath), nameof(ShouldDoDefaultBoneGain), + new Type[] { typeof(PlayableCard), typeof(CardSlot) }); + + codes.RemoveAt(i); + codes.RemoveAt(i); + codes.RemoveAt(i); + i += 2; + codes[i] = new CodeInstruction(OpCodes.Callvirt, validationMethod); + + /* + * // <>2__current = Singleton.Instance.AddBones(1, 5__2); + * ldarg.0 + * call !0 class Singleton`1::get_Instance() + * ldc.i4.1 + * ldarg.0 + * ldfld class DiskCardGame.CardSlot DiskCardGame.PlayableCard/'d__98'::'5__2' + * callvirt instance class [netstandard]System.Collections.IEnumerator DiskCardGame.ResourcesManager::AddBones(int32, class DiskCardGame.CardSlot) + * stfld object DiskCardGame.PlayableCard/'d__98'::'<>2__current' + * + * = + * + * // <>2__current = DoDefaultBoneGain(playableCard, slotBeforeDeath, 1); + * ldarg.0 + * ldloc.1 + * ldarg.0 + * ldfld class DiskCardGame.CardSlot DiskCardGame.PlayableCard/'d__98'::'5__2' + * ldc.i4.1 + * callvirt DoDefaultBoneGain + * stfld object DiskCardGame.PlayableCard/'d__98'::'<>2__current' + */ + + MethodInfo executionMethod = AccessTools.Method( + typeof(DefaultBoneGainOnDeath), nameof(DoDefaultBoneGain), + new Type[] { typeof(PlayableCard), typeof(CardSlot), typeof(int) }); + + i += 3; + codes[i] = new CodeInstruction(OpCodes.Ldloc_1); + i++; + + // since codes are taken from the top of the stack, we need to move the ldc.i4.1 to be AFTER we call slotBeforeDeath + codes.RemoveAt(i); + + i += 2; + + codes.Insert(i, new CodeInstruction(OpCodes.Ldc_I4_1)); + i++; + + codes[i] = new CodeInstruction(OpCodes.Callvirt, executionMethod); + break; + } + } + + return codes; + } + + /// + /// Simple boolean check that by default replicates the vanilla check for whether a card should award a Bone on death. + /// + /// The PlayableCard that has died. + /// The CardSlot of card. + /// True if card should give a Bone (calls DoDefaultBoneGain). + public static bool ShouldDoDefaultBoneGain(PlayableCard card, CardSlot slotBeforeDeath) { + return slotBeforeDeath.IsPlayerSlot && !card.HasAbility(Ability.QuadrupleBones); + } + /// + /// A simple call to ResourcesManager.AddBones, intended to give modders some more flexibility over the default death behaviour of player cards giving 1 Bone. + /// + /// The PlayableCard that has died. + /// The CardSlot of card. + /// How many Bones to drop whenever a player's card dies (default is 1). + public static IEnumerator DoDefaultBoneGain(PlayableCard card, CardSlot slotBeforeDeath, int amount) { + yield return Singleton.Instance.AddBones(amount, slotBeforeDeath); + } +} diff --git a/InscryptionAPI/InscryptionAPI.csproj b/InscryptionAPI/InscryptionAPI.csproj index 84e11198..268c4a87 100644 --- a/InscryptionAPI/InscryptionAPI.csproj +++ b/InscryptionAPI/InscryptionAPI.csproj @@ -58,7 +58,7 @@ - + diff --git a/InscryptionCommunityPatch/InscryptionCommunityPatch.csproj b/InscryptionCommunityPatch/InscryptionCommunityPatch.csproj index 64344fb5..ba313073 100644 --- a/InscryptionCommunityPatch/InscryptionCommunityPatch.csproj +++ b/InscryptionCommunityPatch/InscryptionCommunityPatch.csproj @@ -21,24 +21,24 @@ false true - + - + - + - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + +