Skip to content

Commit

Permalink
Merge pull request #315 from HumabHatterZed/rulebook-redirects
Browse files Browse the repository at this point in the history
Add text redirect support
  • Loading branch information
IngoHHacks authored Sep 30, 2024
2 parents 5fbbe30 + 9185158 commit a1a7b67
Show file tree
Hide file tree
Showing 28 changed files with 2,245 additions and 642 deletions.
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,31 @@
<details>
<summary>View Changelog</summary>

# 2.22.0
- Added FullBoon objects for each vanilla Boon
- Added 'AllFullBoons' list to BoonManager
- Added support for boons and items appearing in multiple acts' rulebooks
- Added RuleBookRedirectManager and support for rulebook text redirects/page links
- Added additional methods to RuleBookManager: ItemShouldBeAdded, BoonShouldBeAdded, SlotModShouldBeAdded, GetUnformattedPageId
- Added GetFullBoon and GetFullConsumableItemData extension methods
- Added extension methods for adding text redirects to abilities, stat icons, items, boons, slot modifications, and rulebook pages
- Added ModificationType.SetSharedRulebook - used for slot modifications that should share their rulebook entry with other slot modifications
- Added support for multiple rulebook sprites for slot modifications (SetRulebookP03Sprite, SetRulebookGrimoraSprite, SetRulebookMagnificusSprite)
- Added RuleBookController.Instance.OpenToCustomPage
- Added CustomDiskTalkingCard abstract class
- Added TalkingCardManager.NewDisk and TalkingCardManager.CreateDisk
- Fixed RuleBook construction patches having lower patch priority than intended
- Fixed slot modification interactable being enabled when no rulebook entry exists
- Fixed slot modification rulebook pages not working in Act 3
- Fixed rulebook sprites being smaller than normal after flipping to a slot modification rulebook page
- Fixed DiskTalkingCards created through the API not correctly working under certain conditions
- Moved ConsumableItemManager patches to a separate ConsumableItemPatches class
- Modified implementation of rulebook fill page logic to let modders patch the API logic
- Patch 'RuleBookManagerPatches.FillPage' to do this
- Tweaked how custom rulebook pages are added and detected
- Wiki: Tweaked page for adding custom rulebook sections
- Wiki: Added section on adding text redirects

# 2.21.1
- Fixed RuleBookManager not syncing when playing with no custom rulebook sections

Expand Down
2 changes: 1 addition & 1 deletion InscryptionAPI/Ascension/AscensionScreenManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace InscryptionAPI.Ascension;
[HarmonyPatch]
public static class AscensionScreenManager
{
internal static List<Type> registeredScreens = new List<Type>();
internal static List<Type> registeredScreens = new();

internal static Dictionary<AscensionMenuScreens.Screen, AscensionRunSetupScreenBase> screens;

Expand Down
123 changes: 81 additions & 42 deletions InscryptionAPI/Boons/BoonManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using HarmonyLib;
using InscryptionAPI.Guid;
using InscryptionAPI.Helpers;
using InscryptionAPI.RuleBook;
using System.Collections;
using System.Collections.ObjectModel;
using UnityEngine;
Expand Down Expand Up @@ -38,15 +39,35 @@ public class FullBoon
/// Indicates if the player can have multiple instances of this boon in their deck.
/// </summary>
public bool stacks;

public List<AbilityMetaCategory> metaCategories = new();

/// <summary>
/// Tracks all rulebook redirects that this ability's description will have. Explanation of the variables is as follows:
/// Key (string): the text that will be recoloured to indicate that it's clickable.
/// Tuple.Item1 (PageRangeType): the type of page the redirect will go to. Use PageRangeType.Unique if you want to redirect to a custom rulebook page using its pageId.
/// Tuple.Item2 (Color): the colour the Key text will be recoloured to.
/// Tuple.Item3 (string): the id that the API will match against to find the redirect page. Eg, for ability redirects this will be the Ability id as a string.
/// </summary>
public Dictionary<string, RuleBookManager.RedirectInfo> RulebookDescriptionRedirects = new();
}

/// <summary>
/// All boons that come as part of the vanilla game.
/// </summary>
public static readonly ReadOnlyCollection<BoonData> BaseGameBoons = new(Resources.LoadAll<BoonData>("Data/Boons"));

public static readonly List<FullBoon> FullBaseGameBoons = BaseGameBoons.Select(x => new FullBoon()
{
appearInRulebook = true,
boon = x,
stacks = x.type == BoonData.Type.MinorStartingBones,
boonHandlerType = null, // vanilla Boons are hard-coded into the game, so no custom behaviour Type
metaCategories = new() { AbilityMetaCategory.Part1Rulebook } // vanilla boons appear in Act 1, obviously
}).ToList();
internal static readonly ObservableCollection<FullBoon> NewBoons = new();

public static List<FullBoon> AllFullBoons { get; private set; } = new(FullBaseGameBoons);

/// <summary>
/// All boons, including vanilla and mod-added boons.
/// </summary>
Expand Down Expand Up @@ -142,10 +163,28 @@ public static BoonData.Type New<T>(string guid, string name, string rulebookDesc
return New<T>(guid, name, rulebookDescription, pathToIcon, pathToCardArt, stackable, appearInLeshyTrials, appearInRulebook);
}

public static BoonData.Type AddRuleBookCategories(this BoonData.Type boonType, params AbilityMetaCategory[] categories)
{
FullBoon boon = boonType.GetFullBoon();
if (boon != null)
{
foreach (AbilityMetaCategory category in categories)
{
if (!boon.metaCategories.Contains(category))
boon.metaCategories.Add(category);
}
}
return boonType;
}
public static FullBoon GetFullBoon(this BoonData.Type boonType)
{
return AllFullBoons.Find(x => x.boon.type == boonType);
}

internal static void SyncBoonList()
{
var boons = BaseGameBoons.Concat(NewBoons.Select((x) => x.boon)).ToList();
AllBoonsCopy = boons;
AllBoonsCopy = BaseGameBoons.Concat(NewBoons.Select(x => x.boon)).ToList();
AllFullBoons = FullBaseGameBoons.Concat(NewBoons).ToList();
}

static BoonManager()
Expand All @@ -168,32 +207,32 @@ static BoonManager()
private static IEnumerator ActivatePreCombatBoons(IEnumerator result, BoonsHandler __instance)
{
BoonBehaviour.DestroyAllInstances();
if (__instance.BoonsEnabled && RunState.Run != null && RunState.Run.playerDeck != null && RunState.Run.playerDeck.Boons != null && NewBoons != null)

if (__instance.BoonsEnabled && RunState.Run?.playerDeck != null && RunState.Run.playerDeck.Boons != null && NewBoons != null)
{
foreach (BoonData boon in RunState.Run.playerDeck.Boons)
{
if (boon != null)
{
FullBoon nb = NewBoons.ToList().Find((x) => x.boon.type == boon.type);
if (boon == null)
continue;

if (nb == null)
continue;
FullBoon nb = AllFullBoons.Find(x => x.boon.type == boon.type);
if (nb == null)
continue;

int instances = BoonBehaviour.CountInstancesOfType(nb.boon.type);
if (nb != null && nb.boonHandlerType != null && nb.boonHandlerType.IsSubclassOf(typeof(BoonBehaviour)) && (nb.stacks || instances < 1))
int instances = BoonBehaviour.CountInstancesOfType(nb.boon.type);
if (nb != null && nb.boonHandlerType != null && nb.boonHandlerType.IsSubclassOf(typeof(BoonBehaviour)) && (nb.stacks || instances < 1))
{
GameObject boonhandler = new(nb.boon.name + " Boon Handler");
BoonBehaviour behav = boonhandler.AddComponent(nb.boonHandlerType) as BoonBehaviour;
if (behav != null)
{
GameObject boonhandler = new(nb.boon.name + " Boon Handler");
BoonBehaviour behav = boonhandler.AddComponent(nb.boonHandlerType) as BoonBehaviour;
if (behav != null)
GlobalTriggerHandler.Instance?.RegisterNonCardReceiver(behav);
behav.boon = nb;
behav.instanceNumber = instances + 1;
BoonBehaviour.Instances.Add(behav);
if (behav.RespondsToPreBoonActivation())
{
GlobalTriggerHandler.Instance?.RegisterNonCardReceiver(behav);
behav.boon = nb;
behav.instanceNumber = instances + 1;
BoonBehaviour.Instances.Add(behav);
if (behav.RespondsToPreBoonActivation())
{
yield return behav.OnPreBoonActivation();
}
yield return behav.OnPreBoonActivation();
}
}
}
Expand Down Expand Up @@ -242,7 +281,7 @@ private static void AddBoon(BoonData.Type boonType)
{
if (TurnManager.Instance != null && !TurnManager.Instance.GameEnded && !TurnManager.Instance.GameEnding && !TurnManager.Instance.IsSetupPhase && TurnManager.Instance.Opponent != null)
{
FullBoon nb = NewBoons.ToList().Find((x) => x.boon.type == boonType);
FullBoon nb = NewBoons.ToList().Find(x => x.boon.type == boonType);
if (nb != null && nb.boonHandlerType != null && (nb.stacks || BoonBehaviour.CountInstancesOfType(nb.boon.type) < 1))
{
int instances = BoonBehaviour.CountInstancesOfType(nb.boon.type);
Expand Down Expand Up @@ -287,33 +326,33 @@ private static void get_Boons(DeckInfo __instance)
[HarmonyPostfix]
private static void LoadBoons(DeckInfo __instance)
{
__instance.boons.RemoveAll((x) => x == null);
__instance.boons.RemoveAll(x => x == null);
}

[HarmonyPatch(typeof(RuleBookInfo), nameof(RuleBookInfo.ConstructPageData))]
[HarmonyPostfix, HarmonyPriority(100)]
[HarmonyPostfix]
private static void ConstructPageData(ref List<RuleBookPageInfo> __result, RuleBookInfo __instance, AbilityMetaCategory metaCategory)
{
if (NewBoons.Count > 0 && metaCategory == AbilityMetaCategory.Part1Rulebook)
if (NewBoons.Count == 0)
return;

foreach (PageRangeInfo pageRangeInfo in __instance.pageRanges)
{
foreach (PageRangeInfo pageRangeInfo in __instance.pageRanges)
if (pageRangeInfo.type == PageRangeType.Boons)
{
if (pageRangeInfo.type == PageRangeType.Boons)
int insertPosition = __result.FindLastIndex(rbi => rbi.pagePrefab == pageRangeInfo.rangePrefab) + 1;
int curPageNum = (int)BoonData.Type.NUM_TYPES;
List<FullBoon> abilitiesToAdd = NewBoons.Where(x => RuleBookManager.BoonShouldBeAdded(x, metaCategory)).ToList();
//InscryptionAPIPlugin.Logger.LogInfo($"Adding {abilitiesToAdd.Count} out of {NewAbilities.Count} abilities to rulebook");
foreach (FullBoon fboo in abilitiesToAdd)
{
int insertPosition = __result.FindLastIndex(rbi => rbi.pagePrefab == pageRangeInfo.rangePrefab) + 1;
int curPageNum = (int)Ability.NUM_ABILITIES;
List<FullBoon> abilitiesToAdd = NewBoons.Where(x => x?.boon != null && BoonsUtil.GetData(x.boon.type)?.icon != null).ToList();
//InscryptionAPIPlugin.Logger.LogInfo($"Adding {abilitiesToAdd.Count} out of {NewAbilities.Count} abilities to rulebook");
foreach (FullBoon fboo in abilitiesToAdd)
{
RuleBookPageInfo info = new();
info.pagePrefab = pageRangeInfo.rangePrefab;
info.headerText = string.Format(Localization.Translate("APPENDIX XII, SUBSECTION I - MOD BOONS {0}"), curPageNum);
__instance.FillBoonPage(info, pageRangeInfo, (int)fboo.boon.type);
__result.Insert(insertPosition, info);
curPageNum += 1;
insertPosition += 1;
}
RuleBookPageInfo info = new();
info.pagePrefab = pageRangeInfo.rangePrefab;
info.headerText = string.Format(Localization.Translate("APPENDIX XII, SUBSECTION I - MOD BOONS {0}"), curPageNum);
__instance.FillBoonPage(info, pageRangeInfo, (int)fboo.boon.type);
__result.Insert(insertPosition, info);
curPageNum += 1;
insertPosition += 1;
}
}
}
Expand Down
18 changes: 16 additions & 2 deletions InscryptionAPI/Card/AbilityManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using InscryptionAPI.Guid;
using InscryptionAPI.Helpers;
using InscryptionAPI.Helpers.Extensions;
using InscryptionAPI.RuleBook;
using System.Collections;
using System.Collections.ObjectModel;
using System.Reflection;
Expand Down Expand Up @@ -65,6 +66,15 @@ public class FullAbility

public string BaseRulebookDescription { get; internal set; }

/// <summary>
/// Tracks all rulebook redirects that this ability's description will have. Explanation of the variables is as follows:
/// Key (string): the text that will be recoloured to indicate that it's clickable.
/// Tuple.Item1 (PageRangeType): the type of page the redirect will go to. Use PageRangeType.Unique if you want to redirect to a custom rulebook page using its pageId.
/// Tuple.Item2 (Color): the colour the Key text will be recoloured to.
/// Tuple.Item3 (string): the id that the API will match against to find the redirect page. Eg, for ability redirects this will be the Ability id as a string.
/// </summary>
public Dictionary<string, RuleBookManager.RedirectInfo> RulebookDescriptionRedirects = new();

internal static ConditionalWeakTable<AbilityInfo, FullAbility> ReverseMapper = new();

/// <summary>
Expand Down Expand Up @@ -131,7 +141,11 @@ public FullAbility Clone()

AbilityExtensionProperties.Add(clonedInfo, AbilityExtensionProperties.GetOrCreateValue(Info));

return new FullAbility(this.ModGUID, this.Id, clonedInfo, this.AbilityBehavior, this.Texture) { CustomFlippedTexture = this.CustomFlippedTexture };
return new FullAbility(this.ModGUID, this.Id, clonedInfo, this.AbilityBehavior, this.Texture)
{
CustomFlippedTexture = this.CustomFlippedTexture,
RulebookDescriptionRedirects = new(this.RulebookDescriptionRedirects)
};
}
}

Expand Down Expand Up @@ -538,7 +552,7 @@ internal static string ParseAndUpdateDescription(string description, ExtendedAct
}

[HarmonyPatch(typeof(RuleBookInfo), "ConstructPageData", new Type[] { typeof(AbilityMetaCategory) })]
[HarmonyPostfix, HarmonyPriority(100)]
[HarmonyPostfix]
private static void FixRulebook(AbilityMetaCategory metaCategory, RuleBookInfo __instance, ref List<RuleBookPageInfo> __result)
{
//InscryptionAPIPlugin.Logger.LogInfo($"In rulebook patch: I see {NewAbilities.Count}");
Expand Down
14 changes: 12 additions & 2 deletions InscryptionAPI/Card/SpecialStatIconManager.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using DiskCardGame;
using HarmonyLib;
using InscryptionAPI.Guid;
using InscryptionAPI.RuleBook;
using System.Collections.ObjectModel;
using UnityEngine;

Expand All @@ -27,6 +28,15 @@ public class FullStatIcon
public readonly StatIconInfo Info;
public readonly Type VariableStatBehavior;

/// <summary>
/// Tracks all rulebook redirects that this ability's description will have. Explanation of the variables is as follows:
/// Key (string): the text that will be recoloured to indicate that it's clickable.
/// Tuple.Item1 (PageRangeType): the type of page the redirect will go to. Use PageRangeType.Unique if you want to redirect to a custom rulebook page using its pageId.
/// Tuple.Item2 (Color): the colour the Key text will be recoloured to.
/// Tuple.Item3 (string): the id that the API will match against to find the redirect page. Eg, for ability redirects this will be the Ability id as a string.
/// </summary>
public Dictionary<string, RuleBookManager.RedirectInfo> RulebookDescriptionRedirects = new();

public FullStatIcon(SpecialStatIcon id, SpecialTriggeredAbility abilityId, StatIconInfo info, Type variableStatBehavior)
{
Id = id;
Expand Down Expand Up @@ -116,8 +126,8 @@ private static void AbilityLoadPrefix()
}

[HarmonyPatch(typeof(RuleBookInfo), "ConstructPageData", new Type[] { typeof(AbilityMetaCategory) })]
[HarmonyPostfix, HarmonyPriority(100)]
private static void FixRulebook(AbilityMetaCategory metaCategory, RuleBookInfo __instance, ref List<RuleBookPageInfo> __result)
[HarmonyPostfix]
private static void AddNewStatIconsToRuleBook(AbilityMetaCategory metaCategory, RuleBookInfo __instance, ref List<RuleBookPageInfo> __result)
{
if (NewStatIcons.Count > 0)
{
Expand Down
2 changes: 1 addition & 1 deletion InscryptionAPI/InscryptionAPI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<DebugType>full</DebugType>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<Version>2.21.1</Version>
<Version>2.22.0</Version>
</PropertyGroup>

<PropertyGroup>
Expand Down
2 changes: 1 addition & 1 deletion InscryptionAPI/InscryptionAPIPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class InscryptionAPIPlugin : BaseUnityPlugin
{
public const string ModGUID = "cyantist.inscryption.api";
public const string ModName = "InscryptionAPI";
public const string ModVer = "2.21.1";
public const string ModVer = "2.22.0";

public static string Directory = "";

Expand Down
30 changes: 29 additions & 1 deletion InscryptionAPI/Items/ConsumableItemDataExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using DiskCardGame;
using DiskCardGame;
using System.Runtime.CompilerServices;
using UnityEngine;

Expand Down Expand Up @@ -34,7 +34,35 @@ public static ConsumableItemData SetRulebookCategory(this ConsumableItemData dat
data.rulebookCategory = rulebookCategory;
return data;
}
public static ConsumableItemData AddExtraRulebookCategories(this ConsumableItemData data, params AbilityMetaCategory[] rulebookCategories)
{
ConsumableItemManager.FullConsumableItemData fullItem = data.GetFullConsumableItemData();
if (fullItem != null)
{
foreach (AbilityMetaCategory abilityMetaCategory in rulebookCategories)
{
if (!fullItem.rulebookMetaCategories.Contains(abilityMetaCategory))
fullItem.rulebookMetaCategories.Add(abilityMetaCategory);
}
}
return data;
}

/// <summary>
/// Retrieves the FullConsumableItemData associated with the given ConsumableItemData.
/// </summary>
/// <param name="data">The ConsumableItemData we want to find the FullConsumableItemData of.</param>
/// <param name="fallBackToName">If the initial retrieval returns null and this is true, search again for the FullConsumableItemData using the data's mod prefix and rulebookName.</param>
/// <returns>The FullConsumableItemData associated with the given COnsumableItemData, or null if it does not exist.</returns>
public static ConsumableItemManager.FullConsumableItemData GetFullConsumableItemData(this ConsumableItemData data, bool fallBackToName = true)
{
ConsumableItemManager.FullConsumableItemData fullItem = ConsumableItemManager.allFullItemDatas.Find(x => x.itemData == data);

if (fullItem == null && fallBackToName)
fullItem = ConsumableItemManager.allFullItemDatas.Find(x => x.itemData.GetModPrefix() == data.GetModPrefix() && x.itemData.rulebookName == data.rulebookName);

return fullItem;
}
/// <returns>The same ConsumableItemData so a chain can continue.</returns>
public static ConsumableItemData SetRulebookName(this ConsumableItemData data, string rulebookName)
{
Expand Down
Loading

0 comments on commit a1a7b67

Please sign in to comment.