Skip to content
Merged
10 changes: 6 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
- Hooked into a much more sensible method to load the cards into the card pool.
135 changes: 135 additions & 0 deletions InscryptionAPI/Card/DefaultBoneGainOnDeath.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
using DiskCardGame;
using HarmonyLib;
using System.Collections;
using System.Reflection;
using System.Reflection.Emit;

namespace InscryptionAPI.Card;

/// <summary>
/// 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.
/// </summary>
[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<CodeInstruction> InsertBoneMethodHook(IEnumerable<CodeInstruction> instructions)
Comment thread
Chaosyr marked this conversation as resolved.
Resolved
{
// === We want to turn this

// if (!HasAbility(Ability.QuadrupleBones) && slotBeforeDeath.IsPlayerSlot) {
// yield return Singleton<ResourcesManager>.Instance.AddBones(1, slotBeforeDeath);
// }

// === Into this

// if (ShouldDoDefaultBoneGain(base.Card, slotBeforeDeath) {
// yield return DoDefaultBoneGain(base.Card, slotBeforeDeath, 1);
// }

// ===
List<CodeInstruction> 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) && <slotBeforeDeath>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/'<Die>d__98'::'<slotBeforeDeath>5__2'
* callvirt instance bool DiskCardGame.CardSlot::get_IsPlayerSlot()
* brfalse.s IL_022f
*
* =
*
* // if (ShouldDoDefaultBoneGain(playableCard, <slotBeforeDeath>5__2)
* ldloc.1
* ldarg.0
* ldfld class DiskCardGame.CardSlot DiskCardGame.PlayableCard/'<Die>d__98'::'<slotBeforeDeath>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<ResourcesManager>.Instance.AddBones(1, <slotBeforeDeath>5__2);
* ldarg.0
* call !0 class Singleton`1<class DiskCardGame.ResourcesManager>::get_Instance()
* ldc.i4.1
* ldarg.0
* ldfld class DiskCardGame.CardSlot DiskCardGame.PlayableCard/'<Die>d__98'::'<slotBeforeDeath>5__2'
* callvirt instance class [netstandard]System.Collections.IEnumerator DiskCardGame.ResourcesManager::AddBones(int32, class DiskCardGame.CardSlot)
* stfld object DiskCardGame.PlayableCard/'<Die>d__98'::'<>2__current'
*
* =
*
* // <>2__current = DoDefaultBoneGain(playableCard, slotBeforeDeath, 1);
* ldarg.0
* ldloc.1
* ldarg.0
* ldfld class DiskCardGame.CardSlot DiskCardGame.PlayableCard/'<Die>d__98'::'<slotBeforeDeath>5__2'
* ldc.i4.1
* callvirt DoDefaultBoneGain
* stfld object DiskCardGame.PlayableCard/'<Die>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;
}

/// <summary>
/// Simple boolean check that by default replicates the vanilla check for whether a card should award a Bone on death.
/// </summary>
/// <param name="card">The PlayableCard that has died.</param>
/// <param name="slotBeforeDeath">The CardSlot of card.</param>
/// <returns>True if card should give a Bone (calls DoDefaultBoneGain).</returns>
public static bool ShouldDoDefaultBoneGain(PlayableCard card, CardSlot slotBeforeDeath) {
Comment thread
Chaosyr marked this conversation as resolved.
Resolved
return slotBeforeDeath.IsPlayerSlot && !card.HasAbility(Ability.QuadrupleBones);
}
/// <summary>
/// A simple call to ResourcesManager.AddBones, intended to give modders some more flexibility over the default death behaviour of player cards giving 1 Bone.
/// </summary>
/// <param name="card">The PlayableCard that has died.</param>
/// <param name="slotBeforeDeath">The CardSlot of card.</param>
/// <param name="amount">How many Bones to drop whenever a player's card dies (default is 1).</param>
public static IEnumerator DoDefaultBoneGain(PlayableCard card, CardSlot slotBeforeDeath, int amount) {
yield return Singleton<ResourcesManager>.Instance.AddBones(amount, slotBeforeDeath);
}
}
2 changes: 1 addition & 1 deletion InscryptionAPI/InscryptionAPI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
<None Remove="Assets\BaseGameDecals\decal_stitches.png" />
<EmbeddedResource Include="Assets\BaseGameDecals\decal_stitches.png" />
</ItemGroup>

<ItemGroup>
<!-- Talking Card Prefab -->
<None Remove="$(ProjectDir)\TalkingCards\talkingcardgenericprefab" />
Expand Down
22 changes: 11 additions & 11 deletions InscryptionCommunityPatch/InscryptionCommunityPatch.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,24 @@
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>

<ItemGroup>
<EmbeddedResource Include="$(ProjectDir)\Assets\*.png" />
<EmbeddedResource Include="$(ProjectDir)\Assets\*.png" />
</ItemGroup>

<ItemGroup>
<ProjectReference Private="false" Include="..\InscryptionAPI\InscryptionAPI.csproj" />
<ProjectReference Private="false" Include="..\InscryptionAPI\InscryptionAPI.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="BepInEx.Analyzers" Version="1.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="BepInEx.Core" Version="5.4.21" />
<PackageReference Include="HarmonyX" Version="2.10.2" />
<PackageReference Include="Inscryption.GameLibs" Version="1.9.0-r.0" />
<PackageReference Include="UnityEngine.Modules" Version="2019.4.24" />
<PackageReference Include="BepInEx.Analyzers" Version="1.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="BepInEx.Core" Version="5.4.21" />
<PackageReference Include="HarmonyX" Version="2.10.2" />
<PackageReference Include="Inscryption.GameLibs" Version="1.9.0-r.0" />
<PackageReference Include="UnityEngine.Modules" Version="2019.4.24" />
</ItemGroup>

<!--Net Framework References-->
Expand Down
Loading