Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

This is a mod for Pathfinder Wrath of the Righteous. It adds additional spellbook merging options.

## Compatibility

This branch updates the mod for the current 2.7.x game data and Unity Mod Manager 0.32.x. The mythic spellbook merge patches now discover all known base-game spellbooks, so Aeon, Azata, Demon and Trickster can use the same merge behavior as Angel and Lich. If a custom merge cannot be applied safely, the game’s native merge implementation is left in place.

The release also includes Simplified Chinese strings in `Localization/zhCN.json`. The mod keeps its English fallback when another locale is selected.

## Patch 1.3

Owlcat introduced several changes to the Spellbook Merging system in patch 1.3, that just broke the mod.
Expand Down Expand Up @@ -164,4 +170,4 @@ File an issue report if that's not the case.
I would like to give my sincerest thanks to:

1. Owlcat for this amazing game.
2. My fellow modders from discord for inspiration and answering my questions. Particularly Narria Cabarius, Vek17, Bubbles and ArcaneTrixter.
2. My fellow modders from discord for inspiration and answering my questions. Particularly Narria Cabarius, Vek17, Bubbles and ArcaneTrixter.
8 changes: 4 additions & 4 deletions SpellbookMerge/Extensions/ExtensionMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ namespace SpellbookMerge.Extensions
public static class ExtensionMethods
{
public static void SetName(this BlueprintUnitFact feature, String name) {
feature.m_DisplayName = Resources.CreateString(feature.name + ".Name", name);
Resources.SetMember(feature, "m_DisplayName", Resources.CreateString(feature.name + ".Name", name));
}

public static void SetDescription(this BlueprintUnitFact feature, LocalizedString description) {
feature.m_Description = description;
Resources.SetMember(feature, "m_Description", description);
}

public static void SetDescription(this BlueprintUnitFact feature, String description) {
var taggedDescription = DescriptionTools.TagEncyclopediaEntries(description);
feature.m_Description = Resources.CreateString(feature.name + ".Description", taggedDescription);
Resources.SetMember(feature, "m_Description", Resources.CreateString(feature.name + ".Description", taggedDescription));
}
}
}
}
59 changes: 30 additions & 29 deletions SpellbookMerge/Features/IncorporateSpellbook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public static void AddAeonIncorporateSpellbookFeature()
bp.SetName("Mythic Spellbook");
bp.SetDescription("At 3rd rank, Aeon receives the ability to cast mythic {g|Encyclopedia:Spell}spells{/g}. They can either choose to take it as part of an existing spellbook, or as a standalone spellbook.");

bp.m_AllowedSpellbooks = new[]
Resources.SetAllowedSpellbooks(bp, new[]
{
Resources.SpellbookBlueprints.InquisitorSpellbook.ToReference<BlueprintSpellbookReference>(),
Resources.SpellbookBlueprints.WarPriestSpellbook.ToReference<BlueprintSpellbookReference>(),
Expand All @@ -32,12 +32,13 @@ public static void AddAeonIncorporateSpellbookFeature()
Resources.SpellbookBlueprints.ClericSpellbook.ToReference<BlueprintSpellbookReference>(),
Resources.SpellbookBlueprints.WitchSpellbook.ToReference<BlueprintSpellbookReference>(),

};
bp.m_MythicSpellList = Resources.SpellListBlueprints.AeonSpellList.ToReference<BlueprintSpellListReference>();
// TODO This is just there for compatibility with Owlcat new changes in 1.3. We are not using it yet, since we reverted to the old merging behavior
bp.m_SpellKnownForSpontaneous = Resources.SpellTableBlueprints.MythicSpontaneousSpellsKnownTable
.ToReference<BlueprintSpellsTableReference>();
bp.IsClassFeature = true;
});
// Use the current game's native mythic-spellbook merge path. It handles the
// selected replacement spellbook, caster-level growth, and mythic spell choices.
Resources.ConfigureMythicSpellbook(
bp,
Resources.SpellListBlueprints.AeonSpellList.ToReference<BlueprintSpellListReference>(),
Resources.SpellTableBlueprints.MythicSpontaneousSpellsKnownTable.ToReference<BlueprintSpellsTableReference>());
});
}

Expand All @@ -48,7 +49,7 @@ public static void AddAzataIncorporateSpellbookFeature()
bp.SetName("Mythic Spellbook");
bp.SetDescription("At 3rd rank, Azata receives the ability to cast mythic {g|Encyclopedia:Spell}spells{/g}. They can either choose to take it as part of an existing spellbook, or as a standalone spellbook.");

bp.m_AllowedSpellbooks = new[]
Resources.SetAllowedSpellbooks(bp, new[]
{
Resources.SpellbookBlueprints.MagusSpellbook.ToReference<BlueprintSpellbookReference>(),
Resources.SpellbookBlueprints.BardSpellbook.ToReference<BlueprintSpellbookReference>(),
Expand All @@ -68,12 +69,12 @@ public static void AddAzataIncorporateSpellbookFeature()
Resources.SpellbookBlueprints.ShamanSpellbook.ToReference<BlueprintSpellbookReference>(),
Resources.SpellbookBlueprints.ClericSpellbook.ToReference<BlueprintSpellbookReference>(),
Resources.SpellbookBlueprints.WitchSpellbook.ToReference<BlueprintSpellbookReference>(),
};
bp.m_MythicSpellList = Resources.SpellListBlueprints.AzataSpellList.ToReference<BlueprintSpellListReference>();
// TODO This is just there for compatibility with Owlcat new changes in 1.3. We are not using it yet, since we reverted to the old merging behavior
bp.m_SpellKnownForSpontaneous = Resources.SpellTableBlueprints.MythicSpontaneousSpellsKnownTable
.ToReference<BlueprintSpellsTableReference>();
bp.IsClassFeature = true;
});
// Use the current game's native mythic-spellbook merge path.
Resources.ConfigureMythicSpellbook(
bp,
Resources.SpellListBlueprints.AzataSpellList.ToReference<BlueprintSpellListReference>(),
Resources.SpellTableBlueprints.MythicSpontaneousSpellsKnownTable.ToReference<BlueprintSpellsTableReference>());
});
}

Expand All @@ -84,7 +85,7 @@ public static void AddDemonIncorporateSpellbookFeature()
bp.SetName("Mythic Spellbook");
bp.SetDescription("At 3rd rank, Demon receives the ability to cast mythic {g|Encyclopedia:Spell}spells{/g}. They can either choose to take it as part of an existing spellbook, or as a standalone spellbook.");

bp.m_AllowedSpellbooks = new[]
Resources.SetAllowedSpellbooks(bp, new[]
{
Resources.SpellbookBlueprints.MagusSpellbook.ToReference<BlueprintSpellbookReference>(),
Resources.SpellbookBlueprints.SwordSaintSpellbook.ToReference<BlueprintSpellbookReference>(),
Expand All @@ -103,12 +104,12 @@ public static void AddDemonIncorporateSpellbookFeature()
Resources.SpellbookBlueprints.WizardSpellbook.ToReference<BlueprintSpellbookReference>(),
Resources.SpellbookBlueprints.DruidSpellbook.ToReference<BlueprintSpellbookReference>(),
Resources.SpellbookBlueprints.WitchSpellbook.ToReference<BlueprintSpellbookReference>(),
};
bp.m_MythicSpellList = Resources.SpellListBlueprints.DemonSpellList.ToReference<BlueprintSpellListReference>();
// TODO This is just there for compatibility with Owlcat new changes in 1.3. We are not using it yet, since we reverted to the old merging behavior
bp.m_SpellKnownForSpontaneous = Resources.SpellTableBlueprints.MythicSpontaneousSpellsKnownTable
.ToReference<BlueprintSpellsTableReference>();
bp.IsClassFeature = true;
});
// Use the current game's native mythic-spellbook merge path.
Resources.ConfigureMythicSpellbook(
bp,
Resources.SpellListBlueprints.DemonSpellList.ToReference<BlueprintSpellListReference>(),
Resources.SpellTableBlueprints.MythicSpontaneousSpellsKnownTable.ToReference<BlueprintSpellsTableReference>());
});
}

Expand All @@ -119,7 +120,7 @@ public static void AddTricksterIncorporateSpellbookFeature()
bp.SetName("Mythic Spellbook");
bp.SetDescription("At 3rd rank, Trickster receives the ability to cast mythic {g|Encyclopedia:Spell}spells{/g}. They can either choose to take it as part of an existing spellbook, or as a standalone spellbook.");

bp.m_AllowedSpellbooks = new[]
Resources.SetAllowedSpellbooks(bp, new[]
{
Resources.SpellbookBlueprints.MagusSpellbook.ToReference<BlueprintSpellbookReference>(),
Resources.SpellbookBlueprints.SwordSaintSpellbook.ToReference<BlueprintSpellbookReference>(),
Expand All @@ -138,14 +139,14 @@ public static void AddTricksterIncorporateSpellbookFeature()
Resources.SpellbookBlueprints.ClericSpellbook.ToReference<BlueprintSpellbookReference>(),
Resources.SpellbookBlueprints.WizardSpellbook.ToReference<BlueprintSpellbookReference>(),
Resources.SpellbookBlueprints.DruidSpellbook.ToReference<BlueprintSpellbookReference>(),
};
bp.m_MythicSpellList = Resources.SpellListBlueprints.TricksterSpellList.ToReference<BlueprintSpellListReference>();
// TODO This is just there for compatibility with Owlcat new changes in 1.3. We are not using it yet, since we reverted to the old merging behavior
bp.m_SpellKnownForSpontaneous = Resources.SpellTableBlueprints.MythicSpontaneousSpellsKnownTable
.ToReference<BlueprintSpellsTableReference>();
bp.IsClassFeature = true;
});
// Use the current game's native mythic-spellbook merge path.
Resources.ConfigureMythicSpellbook(
bp,
Resources.SpellListBlueprints.TricksterSpellList.ToReference<BlueprintSpellListReference>(),
Resources.SpellTableBlueprints.MythicSpontaneousSpellsKnownTable.ToReference<BlueprintSpellsTableReference>());
});
}

}
}
}
6 changes: 3 additions & 3 deletions SpellbookMerge/Info.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
"Id": "SpellbookMerge",
"DisplayName": "Spellbook Merge",
"Author": "vikigenius",
"Version": "1.7.1",
"ManagerVersion": "0.21.3",
"Version": "1.8.0",
"ManagerVersion": "0.32.2",
"Requirements": [],
"AssemblyName": "SpellbookMerge.dll",
"EntryMethod": "SpellbookMerge.Main.Load"
}
}
36 changes: 36 additions & 0 deletions SpellbookMerge/Localization/zhCN.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"strings": [
{
"Key": "AeonIncorporateSpellbook.Name",
"Value": "神话法术书"
},
{
"Key": "AeonIncorporateSpellbook.Description",
"Value": "在神话阶层3时,御衡者获得施放神话法术的能力。你可以选择将其并入现有法术书,或将其作为独立法术书。"
},
{
"Key": "AzataIncorporateSpellbook.Name",
"Value": "神话法术书"
},
{
"Key": "AzataIncorporateSpellbook.Description",
"Value": "在神话阶层3时,灵使获得施放神话法术的能力。你可以选择将其并入现有法术书,或将其作为独立法术书。"
},
{
"Key": "DemonIncorporateSpellbook.Name",
"Value": "神话法术书"
},
{
"Key": "DemonIncorporateSpellbook.Description",
"Value": "在神话阶层3时,恶魔获得施放神话法术的能力。你可以选择将其并入现有法术书,或将其作为独立法术书。"
},
{
"Key": "TricksterIncorporateSpellbook.Name",
"Value": "神话法术书"
},
{
"Key": "TricksterIncorporateSpellbook.Description",
"Value": "在神话阶层3时,诡计大师获得施放神话法术的能力。你可以选择将其并入现有法术书,或将其作为独立法术书。"
}
]
}
4 changes: 2 additions & 2 deletions SpellbookMerge/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static class Main
private static bool Load(UnityModManager.ModEntry modEntry)
{
ModEntry = modEntry;
UserConfigDir = ModEntry.Path + "UserSettings";
UserConfigDir = Path.Combine(ModEntry.Path, "UserSettings");
Directory.CreateDirectory(UserConfigDir);
ModSettings.OverrideFrom(UserConfigDir);
ModEntry.OnToggle = OnToggle;
Expand Down Expand Up @@ -63,4 +63,4 @@ public static void LogError(string message) {
PFLog.Mods.Error(message);
}
}
}
}
69 changes: 69 additions & 0 deletions SpellbookMerge/Patches/DynamicSpellbookChoices.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.Linq;
using HarmonyLib;
using Kingmaker.Blueprints;
using Kingmaker.Blueprints.Classes;
using Kingmaker.Blueprints.Classes.Spells;
using Kingmaker.UnitLogic;

namespace SpellbookMerge.Patches
{
/// <summary>
/// Adds the spellbooks actually owned by the unit to mythic merge selections.
///
/// The game filters BlueprintFeatureSelectMythicSpellbook.Items by exact blueprint
/// identity. Archetypes and other mods commonly replace a class spellbook with a new
/// blueprint, so a hard-coded list containing the base Wizard spellbook cannot produce
/// a choice for a character using a replacement Wizard spellbook.
/// </summary>
public static class DynamicSpellbookChoices
{
[HarmonyPatch(
typeof(BlueprintFeatureSelectMythicSpellbook),
nameof(BlueprintFeatureSelectMythicSpellbook.ExtractSelectionItems))]
private static class ExtractSelectionItemsPatch
{
// ReSharper disable once UnusedMember.Local
private static void Prefix(
BlueprintFeatureSelectMythicSpellbook __instance,
UnitDescriptor previewUnit)
{
if (!Main.Enabled || previewUnit == null) return;

var allowedSpellbooks = new List<BlueprintSpellbookReference>(
Resources.GetAllowedSpellbooks(__instance)
?? Array.Empty<BlueprintSpellbookReference>());
var addedSpellbooks = new List<BlueprintSpellbook>();

foreach (var spellbook in previewUnit.Spellbooks)
{
var blueprint = spellbook?.Blueprint;
if (blueprint == null || blueprint.IsMythic || blueprint.CharacterClass == null)
{
continue;
}

var reference = blueprint.ToReference<BlueprintSpellbookReference>();
if (allowedSpellbooks.Any(existing =>
existing != null && existing.Guid == reference.Guid))
{
continue;
}

allowedSpellbooks.Add(reference);
addedSpellbooks.Add(blueprint);
}

if (addedSpellbooks.Count == 0) return;

// This also invalidates the game's cached selection items, allowing the
// replacement blueprint to become a "Mythic Spellbook (Class)" option.
Resources.SetAllowedSpellbooks(__instance, allowedSpellbooks.ToArray());
Main.Log(
"Added runtime mythic merge choices: " +
string.Join(", ", addedSpellbooks.Select(bp => $"{bp.name} [{bp.AssetGuid}]")));
}
}
}
}
88 changes: 0 additions & 88 deletions SpellbookMerge/Patches/LegacyMerge.cs

This file was deleted.

Loading