Skip to content

Commit a1a7b67

Browse files
authored
Merge pull request #315 from HumabHatterZed/rulebook-redirects
Add text redirect support
2 parents 5fbbe30 + 9185158 commit a1a7b67

28 files changed

Lines changed: 2245 additions & 642 deletions

CHANGELOG.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,31 @@
11
<details>
22
<summary>View Changelog</summary>
33

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

InscryptionAPI/Ascension/AscensionScreenManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace InscryptionAPI.Ascension;
88
[HarmonyPatch]
99
public static class AscensionScreenManager
1010
{
11-
internal static List<Type> registeredScreens = new List<Type>();
11+
internal static List<Type> registeredScreens = new();
1212

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

InscryptionAPI/Boons/BoonManager.cs

Lines changed: 81 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using HarmonyLib;
33
using InscryptionAPI.Guid;
44
using InscryptionAPI.Helpers;
5+
using InscryptionAPI.RuleBook;
56
using System.Collections;
67
using System.Collections.ObjectModel;
78
using UnityEngine;
@@ -38,15 +39,35 @@ public class FullBoon
3839
/// Indicates if the player can have multiple instances of this boon in their deck.
3940
/// </summary>
4041
public bool stacks;
42+
43+
public List<AbilityMetaCategory> metaCategories = new();
44+
45+
/// <summary>
46+
/// Tracks all rulebook redirects that this ability's description will have. Explanation of the variables is as follows:
47+
/// Key (string): the text that will be recoloured to indicate that it's clickable.
48+
/// 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.
49+
/// Tuple.Item2 (Color): the colour the Key text will be recoloured to.
50+
/// 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.
51+
/// </summary>
52+
public Dictionary<string, RuleBookManager.RedirectInfo> RulebookDescriptionRedirects = new();
4153
}
4254

4355
/// <summary>
4456
/// All boons that come as part of the vanilla game.
4557
/// </summary>
4658
public static readonly ReadOnlyCollection<BoonData> BaseGameBoons = new(Resources.LoadAll<BoonData>("Data/Boons"));
47-
59+
public static readonly List<FullBoon> FullBaseGameBoons = BaseGameBoons.Select(x => new FullBoon()
60+
{
61+
appearInRulebook = true,
62+
boon = x,
63+
stacks = x.type == BoonData.Type.MinorStartingBones,
64+
boonHandlerType = null, // vanilla Boons are hard-coded into the game, so no custom behaviour Type
65+
metaCategories = new() { AbilityMetaCategory.Part1Rulebook } // vanilla boons appear in Act 1, obviously
66+
}).ToList();
4867
internal static readonly ObservableCollection<FullBoon> NewBoons = new();
4968

69+
public static List<FullBoon> AllFullBoons { get; private set; } = new(FullBaseGameBoons);
70+
5071
/// <summary>
5172
/// All boons, including vanilla and mod-added boons.
5273
/// </summary>
@@ -142,10 +163,28 @@ public static BoonData.Type New<T>(string guid, string name, string rulebookDesc
142163
return New<T>(guid, name, rulebookDescription, pathToIcon, pathToCardArt, stackable, appearInLeshyTrials, appearInRulebook);
143164
}
144165

166+
public static BoonData.Type AddRuleBookCategories(this BoonData.Type boonType, params AbilityMetaCategory[] categories)
167+
{
168+
FullBoon boon = boonType.GetFullBoon();
169+
if (boon != null)
170+
{
171+
foreach (AbilityMetaCategory category in categories)
172+
{
173+
if (!boon.metaCategories.Contains(category))
174+
boon.metaCategories.Add(category);
175+
}
176+
}
177+
return boonType;
178+
}
179+
public static FullBoon GetFullBoon(this BoonData.Type boonType)
180+
{
181+
return AllFullBoons.Find(x => x.boon.type == boonType);
182+
}
183+
145184
internal static void SyncBoonList()
146185
{
147-
var boons = BaseGameBoons.Concat(NewBoons.Select((x) => x.boon)).ToList();
148-
AllBoonsCopy = boons;
186+
AllBoonsCopy = BaseGameBoons.Concat(NewBoons.Select(x => x.boon)).ToList();
187+
AllFullBoons = FullBaseGameBoons.Concat(NewBoons).ToList();
149188
}
150189

151190
static BoonManager()
@@ -168,32 +207,32 @@ static BoonManager()
168207
private static IEnumerator ActivatePreCombatBoons(IEnumerator result, BoonsHandler __instance)
169208
{
170209
BoonBehaviour.DestroyAllInstances();
171-
if (__instance.BoonsEnabled && RunState.Run != null && RunState.Run.playerDeck != null && RunState.Run.playerDeck.Boons != null && NewBoons != null)
210+
211+
if (__instance.BoonsEnabled && RunState.Run?.playerDeck != null && RunState.Run.playerDeck.Boons != null && NewBoons != null)
172212
{
173213
foreach (BoonData boon in RunState.Run.playerDeck.Boons)
174214
{
175-
if (boon != null)
176-
{
177-
FullBoon nb = NewBoons.ToList().Find((x) => x.boon.type == boon.type);
215+
if (boon == null)
216+
continue;
178217

179-
if (nb == null)
180-
continue;
218+
FullBoon nb = AllFullBoons.Find(x => x.boon.type == boon.type);
219+
if (nb == null)
220+
continue;
181221

182-
int instances = BoonBehaviour.CountInstancesOfType(nb.boon.type);
183-
if (nb != null && nb.boonHandlerType != null && nb.boonHandlerType.IsSubclassOf(typeof(BoonBehaviour)) && (nb.stacks || instances < 1))
222+
int instances = BoonBehaviour.CountInstancesOfType(nb.boon.type);
223+
if (nb != null && nb.boonHandlerType != null && nb.boonHandlerType.IsSubclassOf(typeof(BoonBehaviour)) && (nb.stacks || instances < 1))
224+
{
225+
GameObject boonhandler = new(nb.boon.name + " Boon Handler");
226+
BoonBehaviour behav = boonhandler.AddComponent(nb.boonHandlerType) as BoonBehaviour;
227+
if (behav != null)
184228
{
185-
GameObject boonhandler = new(nb.boon.name + " Boon Handler");
186-
BoonBehaviour behav = boonhandler.AddComponent(nb.boonHandlerType) as BoonBehaviour;
187-
if (behav != null)
229+
GlobalTriggerHandler.Instance?.RegisterNonCardReceiver(behav);
230+
behav.boon = nb;
231+
behav.instanceNumber = instances + 1;
232+
BoonBehaviour.Instances.Add(behav);
233+
if (behav.RespondsToPreBoonActivation())
188234
{
189-
GlobalTriggerHandler.Instance?.RegisterNonCardReceiver(behav);
190-
behav.boon = nb;
191-
behav.instanceNumber = instances + 1;
192-
BoonBehaviour.Instances.Add(behav);
193-
if (behav.RespondsToPreBoonActivation())
194-
{
195-
yield return behav.OnPreBoonActivation();
196-
}
235+
yield return behav.OnPreBoonActivation();
197236
}
198237
}
199238
}
@@ -242,7 +281,7 @@ private static void AddBoon(BoonData.Type boonType)
242281
{
243282
if (TurnManager.Instance != null && !TurnManager.Instance.GameEnded && !TurnManager.Instance.GameEnding && !TurnManager.Instance.IsSetupPhase && TurnManager.Instance.Opponent != null)
244283
{
245-
FullBoon nb = NewBoons.ToList().Find((x) => x.boon.type == boonType);
284+
FullBoon nb = NewBoons.ToList().Find(x => x.boon.type == boonType);
246285
if (nb != null && nb.boonHandlerType != null && (nb.stacks || BoonBehaviour.CountInstancesOfType(nb.boon.type) < 1))
247286
{
248287
int instances = BoonBehaviour.CountInstancesOfType(nb.boon.type);
@@ -287,33 +326,33 @@ private static void get_Boons(DeckInfo __instance)
287326
[HarmonyPostfix]
288327
private static void LoadBoons(DeckInfo __instance)
289328
{
290-
__instance.boons.RemoveAll((x) => x == null);
329+
__instance.boons.RemoveAll(x => x == null);
291330
}
292331

293332
[HarmonyPatch(typeof(RuleBookInfo), nameof(RuleBookInfo.ConstructPageData))]
294-
[HarmonyPostfix, HarmonyPriority(100)]
333+
[HarmonyPostfix]
295334
private static void ConstructPageData(ref List<RuleBookPageInfo> __result, RuleBookInfo __instance, AbilityMetaCategory metaCategory)
296335
{
297-
if (NewBoons.Count > 0 && metaCategory == AbilityMetaCategory.Part1Rulebook)
336+
if (NewBoons.Count == 0)
337+
return;
338+
339+
foreach (PageRangeInfo pageRangeInfo in __instance.pageRanges)
298340
{
299-
foreach (PageRangeInfo pageRangeInfo in __instance.pageRanges)
341+
if (pageRangeInfo.type == PageRangeType.Boons)
300342
{
301-
if (pageRangeInfo.type == PageRangeType.Boons)
343+
int insertPosition = __result.FindLastIndex(rbi => rbi.pagePrefab == pageRangeInfo.rangePrefab) + 1;
344+
int curPageNum = (int)BoonData.Type.NUM_TYPES;
345+
List<FullBoon> abilitiesToAdd = NewBoons.Where(x => RuleBookManager.BoonShouldBeAdded(x, metaCategory)).ToList();
346+
//InscryptionAPIPlugin.Logger.LogInfo($"Adding {abilitiesToAdd.Count} out of {NewAbilities.Count} abilities to rulebook");
347+
foreach (FullBoon fboo in abilitiesToAdd)
302348
{
303-
int insertPosition = __result.FindLastIndex(rbi => rbi.pagePrefab == pageRangeInfo.rangePrefab) + 1;
304-
int curPageNum = (int)Ability.NUM_ABILITIES;
305-
List<FullBoon> abilitiesToAdd = NewBoons.Where(x => x?.boon != null && BoonsUtil.GetData(x.boon.type)?.icon != null).ToList();
306-
//InscryptionAPIPlugin.Logger.LogInfo($"Adding {abilitiesToAdd.Count} out of {NewAbilities.Count} abilities to rulebook");
307-
foreach (FullBoon fboo in abilitiesToAdd)
308-
{
309-
RuleBookPageInfo info = new();
310-
info.pagePrefab = pageRangeInfo.rangePrefab;
311-
info.headerText = string.Format(Localization.Translate("APPENDIX XII, SUBSECTION I - MOD BOONS {0}"), curPageNum);
312-
__instance.FillBoonPage(info, pageRangeInfo, (int)fboo.boon.type);
313-
__result.Insert(insertPosition, info);
314-
curPageNum += 1;
315-
insertPosition += 1;
316-
}
349+
RuleBookPageInfo info = new();
350+
info.pagePrefab = pageRangeInfo.rangePrefab;
351+
info.headerText = string.Format(Localization.Translate("APPENDIX XII, SUBSECTION I - MOD BOONS {0}"), curPageNum);
352+
__instance.FillBoonPage(info, pageRangeInfo, (int)fboo.boon.type);
353+
__result.Insert(insertPosition, info);
354+
curPageNum += 1;
355+
insertPosition += 1;
317356
}
318357
}
319358
}

InscryptionAPI/Card/AbilityManager.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using InscryptionAPI.Guid;
44
using InscryptionAPI.Helpers;
55
using InscryptionAPI.Helpers.Extensions;
6+
using InscryptionAPI.RuleBook;
67
using System.Collections;
78
using System.Collections.ObjectModel;
89
using System.Reflection;
@@ -65,6 +66,15 @@ public class FullAbility
6566

6667
public string BaseRulebookDescription { get; internal set; }
6768

69+
/// <summary>
70+
/// Tracks all rulebook redirects that this ability's description will have. Explanation of the variables is as follows:
71+
/// Key (string): the text that will be recoloured to indicate that it's clickable.
72+
/// 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.
73+
/// Tuple.Item2 (Color): the colour the Key text will be recoloured to.
74+
/// 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.
75+
/// </summary>
76+
public Dictionary<string, RuleBookManager.RedirectInfo> RulebookDescriptionRedirects = new();
77+
6878
internal static ConditionalWeakTable<AbilityInfo, FullAbility> ReverseMapper = new();
6979

7080
/// <summary>
@@ -131,7 +141,11 @@ public FullAbility Clone()
131141

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

134-
return new FullAbility(this.ModGUID, this.Id, clonedInfo, this.AbilityBehavior, this.Texture) { CustomFlippedTexture = this.CustomFlippedTexture };
144+
return new FullAbility(this.ModGUID, this.Id, clonedInfo, this.AbilityBehavior, this.Texture)
145+
{
146+
CustomFlippedTexture = this.CustomFlippedTexture,
147+
RulebookDescriptionRedirects = new(this.RulebookDescriptionRedirects)
148+
};
135149
}
136150
}
137151

@@ -538,7 +552,7 @@ internal static string ParseAndUpdateDescription(string description, ExtendedAct
538552
}
539553

540554
[HarmonyPatch(typeof(RuleBookInfo), "ConstructPageData", new Type[] { typeof(AbilityMetaCategory) })]
541-
[HarmonyPostfix, HarmonyPriority(100)]
555+
[HarmonyPostfix]
542556
private static void FixRulebook(AbilityMetaCategory metaCategory, RuleBookInfo __instance, ref List<RuleBookPageInfo> __result)
543557
{
544558
//InscryptionAPIPlugin.Logger.LogInfo($"In rulebook patch: I see {NewAbilities.Count}");

InscryptionAPI/Card/SpecialStatIconManager.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using DiskCardGame;
22
using HarmonyLib;
33
using InscryptionAPI.Guid;
4+
using InscryptionAPI.RuleBook;
45
using System.Collections.ObjectModel;
56
using UnityEngine;
67

@@ -27,6 +28,15 @@ public class FullStatIcon
2728
public readonly StatIconInfo Info;
2829
public readonly Type VariableStatBehavior;
2930

31+
/// <summary>
32+
/// Tracks all rulebook redirects that this ability's description will have. Explanation of the variables is as follows:
33+
/// Key (string): the text that will be recoloured to indicate that it's clickable.
34+
/// 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.
35+
/// Tuple.Item2 (Color): the colour the Key text will be recoloured to.
36+
/// 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.
37+
/// </summary>
38+
public Dictionary<string, RuleBookManager.RedirectInfo> RulebookDescriptionRedirects = new();
39+
3040
public FullStatIcon(SpecialStatIcon id, SpecialTriggeredAbility abilityId, StatIconInfo info, Type variableStatBehavior)
3141
{
3242
Id = id;
@@ -116,8 +126,8 @@ private static void AbilityLoadPrefix()
116126
}
117127

118128
[HarmonyPatch(typeof(RuleBookInfo), "ConstructPageData", new Type[] { typeof(AbilityMetaCategory) })]
119-
[HarmonyPostfix, HarmonyPriority(100)]
120-
private static void FixRulebook(AbilityMetaCategory metaCategory, RuleBookInfo __instance, ref List<RuleBookPageInfo> __result)
129+
[HarmonyPostfix]
130+
private static void AddNewStatIconsToRuleBook(AbilityMetaCategory metaCategory, RuleBookInfo __instance, ref List<RuleBookPageInfo> __result)
121131
{
122132
if (NewStatIcons.Count > 0)
123133
{

InscryptionAPI/InscryptionAPI.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<DebugType>full</DebugType>
1111
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
1212
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
13-
<Version>2.21.1</Version>
13+
<Version>2.22.0</Version>
1414
</PropertyGroup>
1515

1616
<PropertyGroup>

InscryptionAPI/InscryptionAPIPlugin.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public class InscryptionAPIPlugin : BaseUnityPlugin
3030
{
3131
public const string ModGUID = "cyantist.inscryption.api";
3232
public const string ModName = "InscryptionAPI";
33-
public const string ModVer = "2.21.1";
33+
public const string ModVer = "2.22.0";
3434

3535
public static string Directory = "";
3636

InscryptionAPI/Items/ConsumableItemDataExtensions.cs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using DiskCardGame;
1+
using DiskCardGame;
22
using System.Runtime.CompilerServices;
33
using UnityEngine;
44

@@ -34,7 +34,35 @@ public static ConsumableItemData SetRulebookCategory(this ConsumableItemData dat
3434
data.rulebookCategory = rulebookCategory;
3535
return data;
3636
}
37+
public static ConsumableItemData AddExtraRulebookCategories(this ConsumableItemData data, params AbilityMetaCategory[] rulebookCategories)
38+
{
39+
ConsumableItemManager.FullConsumableItemData fullItem = data.GetFullConsumableItemData();
40+
if (fullItem != null)
41+
{
42+
foreach (AbilityMetaCategory abilityMetaCategory in rulebookCategories)
43+
{
44+
if (!fullItem.rulebookMetaCategories.Contains(abilityMetaCategory))
45+
fullItem.rulebookMetaCategories.Add(abilityMetaCategory);
46+
}
47+
}
48+
return data;
49+
}
50+
51+
/// <summary>
52+
/// Retrieves the FullConsumableItemData associated with the given ConsumableItemData.
53+
/// </summary>
54+
/// <param name="data">The ConsumableItemData we want to find the FullConsumableItemData of.</param>
55+
/// <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>
56+
/// <returns>The FullConsumableItemData associated with the given COnsumableItemData, or null if it does not exist.</returns>
57+
public static ConsumableItemManager.FullConsumableItemData GetFullConsumableItemData(this ConsumableItemData data, bool fallBackToName = true)
58+
{
59+
ConsumableItemManager.FullConsumableItemData fullItem = ConsumableItemManager.allFullItemDatas.Find(x => x.itemData == data);
3760

61+
if (fullItem == null && fallBackToName)
62+
fullItem = ConsumableItemManager.allFullItemDatas.Find(x => x.itemData.GetModPrefix() == data.GetModPrefix() && x.itemData.rulebookName == data.rulebookName);
63+
64+
return fullItem;
65+
}
3866
/// <returns>The same ConsumableItemData so a chain can continue.</returns>
3967
public static ConsumableItemData SetRulebookName(this ConsumableItemData data, string rulebookName)
4068
{

0 commit comments

Comments
 (0)