diff --git a/ToyBox/classes/Infrastructure/CasterHelpers.cs b/ToyBox/classes/Infrastructure/CasterHelpers.cs index ca1509b49..c1d0e5611 100644 --- a/ToyBox/classes/Infrastructure/CasterHelpers.cs +++ b/ToyBox/classes/Infrastructure/CasterHelpers.cs @@ -10,6 +10,8 @@ using System; using System.Collections.Generic; using System.Linq; +using Kingmaker.Utility; +using ToyBox.Multiclass; #if Wrath using Kingmaker.Blueprints.Classes.Selection; using Kingmaker.Blueprints.Classes.Spells; @@ -174,7 +176,6 @@ public static void HandleAddAllSpellsOnPartyEditor(UnitDescriptor unit) { selectedSpellbook.RemoveSpellsOfLevel(level); } #if Wrath -#if true // TODO: the else case of this #if has a patch that fixes the level for spontaneous spell casters learning scrolls but causes the gestalt feature to stop working by causing spells to not show up for spell casting classes like oracle and sorc when you try to select/gestalt them after choosing say a scaled fist monk public static int GetActualSpellsLearnedForClass(UnitDescriptor unit, Spellbook spellbook, int level) { Mod.Trace($"GetActualSpellsLearnedForClass - unit: {unit?.CharacterName} spellbook: {spellbook?.Blueprint.DisplayName} level:{level}"); // Get all +spells known facts for this spellbook's class so we can ignore them when getting spell counts @@ -201,67 +202,30 @@ public static int GetActualSpellsLearned(Spellbook spellbook, int level, List - x.BlueprintComponents.Where(y => y is AddKnownSpell)).Select(z => z as AddKnownSpell) - .Where(x => x.CharacterClass == spellbook.Blueprint.CharacterClass && (x.Archetype == null || unit.Progression.IsArchetype(x.Archetype))).Select(y => y.Spell) - .ToList(); - Spellbook spellbookOfNormalUnit = null; - if (unit.TryGetPartyMemberForLevelUpVersion(out var ch)) { // get the real units spellbook, the levelup version does not contain flags like CopiedFromScroll - if (ch?.Spellbooks?.Count() > 0) - spellbookOfNormalUnit = ch.Spellbooks.First(s => s.Blueprint == spellbook.Blueprint); - } - return GetActualSpellsLearned(spellbook, level, spellsToIgnore, spellbookOfNormalUnit); - } - /// - /// Calculates the number of spells selected via levelup, excluding spells from items, learned from scrolls and similar. - /// If the spellbook comes from a UnitDescriptor thats part of a levelup, you need to specify spellbookOfNormalUnit as the base units spellbook. - /// (Because levelup logic does not copy any AbilityData flags.) (see GetActualSpellsLearnedForClass as example.) - /// - /// - /// - /// - /// - /// - public static int GetActualSpellsLearned(Spellbook spellbook, int level, List spellsToIgnore, Spellbook spellbookOfNormalUnit = null) { - Mod.Trace($"GetActualSpellsLearned - spellbook: {spellbook?.Blueprint.DisplayName} level:{level}"); + public static int CountExternallyAddedSpells(UnitDescriptor unit, Spellbook spellbook, int level) { - Func normalSpellbookCondition = x => true; - if (spellbookOfNormalUnit != null) { - var normalSpellsOfLevel = spellbookOfNormalUnit.SureKnownSpells(level); - normalSpellbookCondition = x => { - var sp = normalSpellsOfLevel.First(a => a.Blueprint == x.Blueprint); - if (sp == null) - return true; - return !sp.IsTemporary - && !sp.CopiedFromScroll - && !sp.IsFromMythicSpellList - && sp.SourceItem == null - && sp.SourceItemEquipmentBlueprint == null - && sp.SourceItemUsableBlueprint == null - && !sp.IsMysticTheurgeCombinedSpell; - }; - } - var known = spellbook.SureKnownSpells(level) - .Where(x => !x.IsTemporary - && !x.CopiedFromScroll - && !x.IsFromMythicSpellList - && x.SourceItem == null - && x.SourceItemEquipmentBlueprint == null - && x.SourceItemUsableBlueprint == null - && !x.IsMysticTheurgeCombinedSpell - && !spellsToIgnore.Contains(x.Blueprint) - && normalSpellbookCondition(x)) - .Distinct() - .ToList(); + if (!unit.TryGetPartyMemberForLevelUpVersion(out var ch)) return 0; + if (ch?.Spellbooks?.Count() <= 0) return 0; + if (!ch.Spellbooks.TryFind(s => s.Blueprint == spellbook.Blueprint, out var unitSpellbook)) return 0; + if (unitSpellbook?.SureKnownSpells(level) == null) return 0; - return known.Count; + var IsExternal = (AbilityData spell) => { + return spell.IsTemporary + || spell.CopiedFromScroll + || spell.IsFromMythicSpellList + || spell.SourceItem != null + || spell.SourceItemEquipmentBlueprint != null + || spell.SourceItemUsableBlueprint != null + || spell.IsMysticTheurgeCombinedSpell; + }; + + //If copiedCount has records here, we've already taken them into account in GetActualSpellsLearned, and would be double counting here. + var copiedCount = spellbook.SureKnownSpells(level).Where(IsExternal).Count(); + if (copiedCount > 0) return 0; + + return unitSpellbook.SureKnownSpells(level).Where(IsExternal).Count(); } -#endif public static IEnumerable MergableClasses(this UnitEntityData unit) { var spellbookCandidates = unit.Spellbooks diff --git a/ToyBox/classes/MonkeyPatchin/Multiclass/LevelUP+Multiclass.cs b/ToyBox/classes/MonkeyPatchin/Multiclass/LevelUP+Multiclass.cs index 7fe9a7222..a695d92a5 100644 --- a/ToyBox/classes/MonkeyPatchin/Multiclass/LevelUP+Multiclass.cs +++ b/ToyBox/classes/MonkeyPatchin/Multiclass/LevelUP+Multiclass.cs @@ -200,6 +200,10 @@ public static bool Apply(LevelUpState state, UnitDescriptor unit) { var spellsKnown = spellbook1.Blueprint.SpellsKnown; var expectedCount = spellsKnown.GetCount(casterLevelAfter, index); var actual = CasterHelpers.GetActualSpellsLearnedForClass(unit, spellbook1, index); + if(spellbook1.Blueprint.Spontaneous) { + var toReduce = CasterHelpers.CountExternallyAddedSpells(unit, spellbook1, index); + actual -= toReduce < 0 ? 0 : toReduce; + } int learnabl = spellbook1.GetSpellsLearnableOfLevel(index).Count(); int spelladd = Math.Max(0, Math.Min(expectedCount - actual, learnabl)); #if DEBUG