From 0df7584bb7e6fffee13973e0ba4d7f5c7abfae7f Mon Sep 17 00:00:00 2001 From: Starion Date: Wed, 5 Jan 2022 23:11:37 +0100 Subject: [PATCH] Blood Arcanist, Pact Wizard and a metamagic extender. --- Assets/Icons/m_intensified.png | Bin 0 -> 9159 bytes Assets/Icons/m_intensified_spell.png | Bin 0 -> 5058 bytes Content/ArcaneDiscoveries/MetamagicInsight.cs | 7 +- Content/Archetypes/BloodArcanist.cs | 229 ++++++++++ Content/Archetypes/PactWizard.cs | 290 ++++++++++++ Content/Feats/AcadamaeGraduate.cs | 2 + Content/Feats/MetamagicIntensfied.cs | 85 ++++ Content/MainPatcher.cs | 4 +- DefaultSettings.json | 21 + Extensions/Base.cs | 53 +++ Extensions/BlueprintAbilityResource.cs | 88 ++++ Extensions/BlueprintUnitFact.cs | 142 ++++++ Extensions/MetamagicExtender.cs | 238 ++++++++++ Guids.json | 41 ++ Info.json | 4 +- MagicTime.csproj | 14 + Main.cs | 4 + Properties/AssemblyInfo.cs | 4 +- Resources.cs | 26 +- TTT/Bloodlines.cs | 421 ++++++++++++++++++ Utilities/BlueprintDatabase.json | 36 ++ Utilities/DB.cs | 5 + Utilities/Extensions.cs | 308 ++++++++++++- Utilities/Helpers.cs | 133 +++++- 24 files changed, 2132 insertions(+), 23 deletions(-) create mode 100644 Assets/Icons/m_intensified.png create mode 100644 Assets/Icons/m_intensified_spell.png create mode 100644 Content/Archetypes/BloodArcanist.cs create mode 100644 Content/Archetypes/PactWizard.cs create mode 100644 Content/Feats/MetamagicIntensfied.cs create mode 100644 Extensions/Base.cs create mode 100644 Extensions/BlueprintAbilityResource.cs create mode 100644 Extensions/BlueprintUnitFact.cs create mode 100644 Extensions/MetamagicExtender.cs create mode 100644 TTT/Bloodlines.cs diff --git a/Assets/Icons/m_intensified.png b/Assets/Icons/m_intensified.png new file mode 100644 index 0000000000000000000000000000000000000000..8c6c2e9471ca4d9178b3821d0252b73a36d58849 GIT binary patch literal 9159 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 R)c61Z002ovPDHLkV1oE_X#xNM literal 0 HcmV?d00001 diff --git a/Assets/Icons/m_intensified_spell.png b/Assets/Icons/m_intensified_spell.png new file mode 100644 index 0000000000000000000000000000000000000000..fcf1544fac5a96eaec000996e514a6022eba46cc GIT binary patch literal 5058 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 bloodlines; + private static BlueprintFeatureSelection bloodline_selection; + + public static void Create() + { + blood_arcanist_archetype = Helpers.CreateBlueprint("BloodArcanistArchetype", bp => + { + bp.LocalizedName = Helpers.CreateString("BloodArcanistArchetype.Name", "Blood Arcanist"); + bp.LocalizedDescription = Helpers.CreateString("BloodArcanistArchetype.Description", "Though most arcanists possess only a " + + "rudimentary innate arcane gift, the blood arcanist has the full power of a bloodline to draw upon."); + }); + + var ttt_bloodlines_fix = false; + if (UnityModManager.FindMod("TabletopTweaks") != null) + { + var dd_spellbook = DB.GetSelection("Dragon Disciple Spellbook Selection"); + var ttt_dd_sage_sorc_ref = TTTBloodlinesFix.Bloodlines.GetBlueprint("dd026e4d-fd5a-4f18-a79a-24a1bbaf8546") + .ToReference(); + ttt_bloodlines_fix = dd_spellbook.m_AllFeatures.Contains(ttt_dd_sage_sorc_ref); + } + + if (!ttt_bloodlines_fix) + { + TTTBloodlinesFix.Bloodlines.PatchBloodlineRestrictions(); + } + + CreateBloodlines(); + + bloodline_selection = Helpers.CreateFeatureSelection("BloodArcanistBloodlineSelection", "Blood Arcanist Bloodline", "A blood arcanist " + + "selects one bloodline from those available through the sorcerer bloodline class feature. The blood arcanist gains the bloodline " + + "arcana and bloodline powers of that bloodline, treating her arcanist level as her sorcerer level. The blood arcanist does not gain " + + "the class skill, bonus feats, or bonus spells from her bloodline.", null, true, FeatureGroup.BloodLine); + bloodline_selection.m_AllFeatures = bloodlines.ToArray(); + bloodline_selection.Group2 = FeatureGroup.None; + bloodline_selection.NoSelectionIfFeature(); + + blood_arcanist_archetype.RemoveFeatures = new LevelEntry[] { + Helpers.CreateLevelEntry(1, DB.GetSelection("Arcanist Exploits").ToReference()), + Helpers.CreateLevelEntry(3, DB.GetSelection("Arcanist Exploits").ToReference()), + Helpers.CreateLevelEntry(9, DB.GetSelection("Arcanist Exploits").ToReference()), + Helpers.CreateLevelEntry(15, DB.GetSelection("Arcanist Exploits").ToReference()), + Helpers.CreateLevelEntry(20, DB.GetFeature("Arcanist Capstone").ToReference()) + }; + + blood_arcanist_archetype.AddFeatures = new LevelEntry[] { + Helpers.CreateLevelEntry(1, bloodline_selection) + }; + + DB.GetClass("Arcanist Class").AddArchetype(blood_arcanist_archetype); + + FixBloodlineAscendance(); + } + + private static void CreateBloodlines() + { + bloodlines = new List(); + + var abyssal = Helpers.Clone(DB.GetProgression("Sorc Blood Abyssal"), "BloodArcanistAbyssalProgression"); + MakeClassAndArchetypeRef(abyssal); + RemoveSpells(abyssal); + bloodlines.Add(abyssal.ToReference()); + + var arcane = Helpers.Clone(DB.GetProgression("Sorc Blood Arcane"), "BloodArcanistArcaneProgression"); + MakeClassAndArchetypeRef(arcane); + RemoveSpells(arcane); + bloodlines.Add(arcane.ToReference()); + + var celestial = Helpers.Clone(DB.GetProgression("Sorc Blood Celestial"), "BloodArcanistCelestialProgression"); + MakeClassAndArchetypeRef(celestial); + RemoveSpells(celestial); + bloodlines.Add(celestial.ToReference()); + + var dblack = Helpers.Clone(DB.GetProgression("Sorc Blood Draconic Black"), "BloodArcanistDraconicBlackProgression"); + MakeClassAndArchetypeRef(dblack); + RemoveSpells(dblack); + bloodlines.Add(dblack.ToReference()); + + var dblue = Helpers.Clone(DB.GetProgression("Sorc Blood Draconic Blue"), "BloodArcanistDraconicBlueProgression"); + MakeClassAndArchetypeRef(dblue); + RemoveSpells(dblue); + bloodlines.Add(dblue.ToReference()); + + var dbrass = Helpers.Clone(DB.GetProgression("Sorc Blood Draconic Brass"), "BloodArcanistDraconicBrassProgression"); + MakeClassAndArchetypeRef(dbrass); + RemoveSpells(dbrass); + bloodlines.Add(dbrass.ToReference()); + + var dbronze = Helpers.Clone(DB.GetProgression("Sorc Blood Draconic Bronze"), "BloodArcanistDraconicBronzeProgression"); + MakeClassAndArchetypeRef(dbronze); + RemoveSpells(dbronze); + bloodlines.Add(dbronze.ToReference()); + + var dcopper = Helpers.Clone(DB.GetProgression("Sorc Blood Draconic Copper"), "BloodArcanistDraconicCopperProgression"); + MakeClassAndArchetypeRef(dcopper); + RemoveSpells(dcopper); + bloodlines.Add(dcopper.ToReference()); + + var dgold = Helpers.Clone(DB.GetProgression("Sorc Blood Draconic Gold"), "BloodArcanistDraconicGoldProgression"); + MakeClassAndArchetypeRef(dgold); + RemoveSpells(dgold); + bloodlines.Add(dgold.ToReference()); + + var dgreen = Helpers.Clone(DB.GetProgression("Sorc Blood Draconic Green"), "BloodArcanistDraconicGreenProgression"); + MakeClassAndArchetypeRef(dgreen); + RemoveSpells(dgreen); + bloodlines.Add(dgreen.ToReference()); + + var dred = Helpers.Clone(DB.GetProgression("Sorc Blood Draconic Red"), "BloodArcanistDraconicRedProgression"); + MakeClassAndArchetypeRef(dred); + RemoveSpells(dred); + bloodlines.Add(dred.ToReference()); + + var dsilver = Helpers.Clone(DB.GetProgression("Sorc Blood Draconic Silver"), "BloodArcanistDraconicSilverProgression"); + MakeClassAndArchetypeRef(dsilver); + RemoveSpells(dsilver); + bloodlines.Add(dsilver.ToReference()); + + var dwhite = Helpers.Clone(DB.GetProgression("Sorc Blood Draconic White"), "BloodArcanistDraconicWhiteProgression"); + MakeClassAndArchetypeRef(dwhite); + RemoveSpells(dwhite); + bloodlines.Add(dwhite.ToReference()); + + var eleair = Helpers.Clone(DB.GetProgression("Sorc Blood Elemental Air"), "BloodArcanistElementalAirProgression"); + MakeClassAndArchetypeRef(eleair); + RemoveSpells(eleair); + bloodlines.Add(eleair.ToReference()); + + var eleearth = Helpers.Clone(DB.GetProgression("Sorc Blood Elemental Earth"), "BloodArcanistElementalEarthProgression"); + MakeClassAndArchetypeRef(eleearth); + RemoveSpells(eleearth); + bloodlines.Add(eleearth.ToReference()); + + var elefire = Helpers.Clone(DB.GetProgression("Sorc Blood Elemental Fire"), "BloodArcanistElementalFireProgression"); + MakeClassAndArchetypeRef(elefire); + RemoveSpells(elefire); + bloodlines.Add(elefire.ToReference()); + + var elewater = Helpers.Clone(DB.GetProgression("Sorc Blood Elemental Water"), "BloodArcanistElementalWaterProgression"); + MakeClassAndArchetypeRef(elewater); + RemoveSpells(elewater); + bloodlines.Add(elewater.ToReference()); + + var fey = Helpers.Clone(DB.GetProgression("Sorc Blood Fey"), "BloodArcanistFeyProgression"); + MakeClassAndArchetypeRef(fey); + RemoveSpells(fey); + bloodlines.Add(fey.ToReference()); + + var infernal = Helpers.Clone(DB.GetProgression("Sorc Blood Infernal"), "BloodArcanistInfernalProgression"); + MakeClassAndArchetypeRef(infernal); + RemoveSpells(infernal); + bloodlines.Add(infernal.ToReference()); + + var serpent = Helpers.Clone(DB.GetProgression("Sorc Blood Serpentine"), "BloodArcanistSerpentineProgression"); + MakeClassAndArchetypeRef(serpent); + RemoveSpells(serpent); + bloodlines.Add(serpent.ToReference()); + + var undead = Helpers.Clone(DB.GetProgression("Sorc Blood Undead"), "BloodArcanistUndeadProgression"); + MakeClassAndArchetypeRef(undead); + RemoveSpells(undead); + bloodlines.Add(undead.ToReference()); + } + + private static void FixBloodlineAscendance() + { + var ba = DB.GetSelection("Bloodline Ascendance"); + var ttt_fix = ba.GetComponent(); + if (ttt_fix != null) + { + var list = ttt_fix.m_Features.ToList(); + list.Add(bloodline_selection.ToReference()); + ttt_fix.m_Features = list.ToArray(); + } + else + { + ba.CreateFeatureRestriction(bloodline_selection, Prerequisite.GroupType.Any); + } + bloodline_selection.IsPrerequisiteFor = new List(); + bloodline_selection.IsPrerequisiteFor.Add(ba.ToReference()); + } + + private static void MakeClassAndArchetypeRef(BlueprintProgression bloodline) + { + bloodline.m_Classes = new BlueprintProgression.ClassWithLevel[] +{ + new BlueprintProgression.ClassWithLevel() + { + m_Class = DB.GetClass("Arcanist Class").ToReference(), + AdditionalLevel = 0 + } +}; + bloodline.m_Archetypes = new BlueprintProgression.ArchetypeWithLevel[] + { + new BlueprintProgression.ArchetypeWithLevel() + { + m_Archetype = blood_arcanist_archetype.ToReference(), + AdditionalLevel = 0 + } + }; + } + + private static void RemoveSpells(BlueprintProgression bloodline) + { + var new_entries = new List(); + foreach (var entry in bloodline.LevelEntries) + { + new_entries.Add(Helpers.CreateLevelEntryByList(entry.Level, entry.m_Features.Where(feature => + !feature.Get().HasLogic() && !feature.Get().HasLogic() && + !feature.Get().AssetGuid.ToString().Equals("19ab16dba857d1a4ba617074f203f975")).ToList())); + } + bloodline.LevelEntries = new_entries.ToArray(); + } + } +} \ No newline at end of file diff --git a/Content/Archetypes/PactWizard.cs b/Content/Archetypes/PactWizard.cs new file mode 100644 index 0000000..9529b46 --- /dev/null +++ b/Content/Archetypes/PactWizard.cs @@ -0,0 +1,290 @@ +using HarmonyLib; +using Kingmaker.Blueprints; +using Kingmaker.Blueprints.Classes; +using Kingmaker.Blueprints.Classes.Selection; +using Kingmaker.Blueprints.JsonSystem; +using Kingmaker.EntitySystem.Stats; +using Kingmaker.Enums; +using Kingmaker.PubSubSystem; +using Kingmaker.RuleSystem; +using Kingmaker.RuleSystem.Rules; +using Kingmaker.RuleSystem.Rules.Abilities; +using Kingmaker.UnitLogic; +using Kingmaker.UnitLogic.Abilities; +using Kingmaker.UnitLogic.Abilities.Blueprints; +using Kingmaker.UnitLogic.ActivatableAbilities; +using Kingmaker.UnitLogic.Buffs.Blueprints; +using Kingmaker.UnitLogic.Buffs.Components; +using Kingmaker.UnitLogic.FactLogic; +using Kingmaker.UnitLogic.Mechanics; +using MagicTime.Utilities; +using Starion.BPExtender.AbilityResource; +using Starion.BPExtender.UnitFact; +using System.Collections.Generic; +using System.Linq; + +namespace MagicTime.Archetypes +{ + internal static class PactWizard + { + private static BlueprintArchetype pact_wizard_archetype; + public static BlueprintFeatureSelection patron_selection; + public static BlueprintFeature spontaneous_conversion; + private static BlueprintFeatureSelection curse_selection; + public static BlueprintAbilityResource pact_wizard_reroll_resource; + public static BlueprintBuff double_rolls_buff; + private static BlueprintFeature double_rolls_feature; + private static BlueprintFeature lv15feature; + public static BlueprintFeature auto_success; + + public static void Create() + { + pact_wizard_archetype = Helpers.CreateBlueprint("PactWizardArchetype", bp => + { + bp.LocalizedName = Helpers.CreateString("PactWizardArchetype.Name", "Pact Wizard"); + bp.LocalizedDescription = Helpers.CreateString("PactWizardArchetype.Description", "While the art of wizardry is usually a scholar’s " + + "pursuit, there are those who seek mastery of arcane power without tedious study and monotonous research. Motivated by foolish " + + "ambition, such individuals turn to the greatest enigmas of the cosmos in the hopes of attaining greater power. Though few " + + "successfully attract the attention of these forces, those who do receive phenomenal arcane power for their efforts, but become " + + "the dutiful playthings and servants of the forces with which they consort."); + }); + + CreatePatronFeature(); + CreateSpontaneousConversion(); + CreateCurseFeature(); + CreateDoubleRolls(); + CreateBonusToRolls(); + CreateAutoSuccess(); + + pact_wizard_archetype.RemoveFeatures = new LevelEntry[] { + Helpers.CreateLevelEntry(1, DB.GetFeature("Scribe Scrolls").ToReference()), + Helpers.CreateLevelEntry(5, DB.GetSelection("Wizard Feat Selection").ToReference()), + Helpers.CreateLevelEntry(10, DB.GetSelection("Wizard Feat Selection").ToReference()), + Helpers.CreateLevelEntry(15, DB.GetSelection("Wizard Feat Selection").ToReference()), + Helpers.CreateLevelEntry(20, DB.GetSelection("Wizard Feat Selection").ToReference()) + }; + + pact_wizard_archetype.AddFeatures = new LevelEntry[] { + Helpers.CreateLevelEntry(1, patron_selection, spontaneous_conversion), + Helpers.CreateLevelEntry(5, curse_selection), + Helpers.CreateLevelEntry(10, double_rolls_feature), + Helpers.CreateLevelEntry(15, lv15feature), + Helpers.CreateLevelEntry(20, auto_success) + }; + + var newUIGroups = new UIGroup[] + { + Helpers.CreateUIGroup(curse_selection, double_rolls_feature, lv15feature, auto_success) + }; + DB.GetClass("Wizard Class").Progression.UIGroups = DB.GetClass("Wizard Class").Progression.UIGroups.Concat(newUIGroups).ToArray(); + + DB.GetClass("Wizard Class").AddArchetype(pact_wizard_archetype); + } + + public static void CreatePatronFeature() + { + var patron_list = DB.GetSelection("Witch Patron Selection").m_AllFeatures; + patron_selection = Helpers.CreateFeatureSelection("PactWizardPatronSelection", "Patron", "At 1st level, a pact wizard must select a " + + "patron. This functions like the witch class ability of the same name, except the pact wizard automatically adds his patron’s " + + "spells to his spellbook instead of to his familiar.", null, false, FeatureGroup.WitchPatron); + patron_selection.m_AllFeatures = patron_list; + + // Patch the patrons to progress with the wizard class. + foreach (var reference in patron_list) + { + var patron = (BlueprintProgression)reference.Get(); + patron.m_Classes = patron.m_Classes.Concat(new[] { Helpers.CreateClassBasedProgression(DB.GetClass("Wizard Class"), 0) }).ToArray(); + patron.m_Archetypes = patron.m_Archetypes.Concat(new[] { Helpers.CreateArchetypeBasedProgression(pact_wizard_archetype, 0) }).ToArray(); + + var container = new SpellListContainer(); + + foreach (var entry in patron.LevelEntries) + { + var feature = (BlueprintFeature)entry.Features.First(); + var component = entry.Features.Select(f => f.GetComponent()).First(); + feature.CreateAddSpellKnown(DB.GetClass("Wizard Class"), pact_wizard_archetype, component.Spell, component.SpellLevel); + if (!container.spell_list.ContainsKey(component.Spell)) + { + container.spell_list.Add(component.Spell, entry.Level); + } + } + + patron.CreateComponents(container); + } + } + + public static void CreateSpontaneousConversion() + { + spontaneous_conversion = Helpers.CreateFeature("PactWizardConversion", "Spontaneous Spell Conversion", "A pact wizard can expend any " + + "prepared spell that isn’t a spell prepared using the additional spell slot the wizard receives from his arcane school in order to " + + "spontaneously cast one of his patron’s spells of the same level or lower."); + } + + public static void CreateCurseFeature() + { + var oracle_selection = DB.GetSelection("Curse Selection"); + curse_selection = Helpers.CreateFeatureSelection("PactWizardCurseSelection", "Great Power, Greater Expense", "As a pact wizard grows in " + + "power, his choice of patron begins to affect his physical body.\nAt 5th level, the pact wizard chooses one oracle curse, using half " + + "his character level as his effective oracle level when determining the effects of this curse.\nIf an oracle curse would add spells " + + "to the oracle’s list of spells known, the pact wizard instead add those spells to the wizard’s spell list as well as to his " + + "spellbook.", oracle_selection.Icon, false, FeatureGroup.OracleCurse); + curse_selection.m_AllFeatures = oracle_selection.m_AllFeatures; + + // Patch the curses giving spells so they add them to the spellbook. + foreach (var reference in oracle_selection.m_AllFeatures) + { + var curse = (BlueprintProgression)reference.Get(); + + var container = new SpellListContainer(); + + foreach (var entry in curse.LevelEntries) + { + var feature = (BlueprintFeature)entry.Features.First(); + var component = entry.Features.Select(f => f.GetComponent()).First(); + if (component != null) + { + feature.CreateAddSpellKnown(DB.GetClass("Wizard Class"), pact_wizard_archetype, component.Spell, component.SpellLevel); + container.spell_list.Add(component.Spell, entry.Level); + } + } + + curse.CreateComponents(container); + } + } + + public static void CreateDoubleRolls() + { + pact_wizard_reroll_resource = Helpers.CreateBlueprint("PactWizardDoubleRollsResource", bp => + { + bp.CreateExtraData(); + bp.SetIncreasedWithStat(StatType.Intelligence, 3, true); + }); + + double_rolls_buff = Helpers.CreateBuff("PactWizardDoubleRollsBuff", "Great Power, Greater Expense", "", null, null, + BlueprintBuff.Flags.HiddenInUi); + double_rolls_buff.CreateGenericComponent(); + + var double_rolls_ab = Helpers.CreateActivatableAbility("PactWizardDoubleRollsAb", "Great Power, Greater Expense", "At 10th level, the " + + "pact wizard can invoke his patron’s power to roll twice and take the better result when attempting any caster level check, " + + "concentration check, initiative check, or saving throw. He can activate this ability as a free action before attempting the " + + "checks. He can use this ability a number of times per day equal to 3 + half his Intelligence modifier.", null, double_rolls_buff); + double_rolls_ab.m_Icon = DB.GetAbility("Foresight Ability").Icon; + double_rolls_ab.CreateAbilityResourceLogic(pact_wizard_reroll_resource, ActivatableAbilityResourceLogic.ResourceSpendType.Never); + + double_rolls_feature = Helpers.CreateFeature("PactWizardDoubleRollsFeature", double_rolls_ab.Name, double_rolls_ab.Description, null, + DB.GetAbility("Foresight Ability").Icon); + double_rolls_feature.HideInCharacterSheetAndLevelUp = true; + double_rolls_feature.CreateAddAbilityResource(pact_wizard_reroll_resource, 0); + double_rolls_feature.CreateAddFacts(double_rolls_ab); + } + + public static void CreateBonusToRolls() + { + var lv15buff = Helpers.CreateBuff("PactWizardLv15Buff", "Great Power, Greater Expense", "", null, null, BlueprintBuff.Flags.HiddenInUi); + lv15buff.CreateAddStatBonusContext(StatType.Initiative, ModifierDescriptor.Insight, AbilityRankType.StatBonus); + lv15buff.CreateAddStatBonusContext(StatType.SaveFortitude, ModifierDescriptor.Insight, AbilityRankType.StatBonus); + lv15buff.CreateAddStatBonusContext(StatType.SaveReflex, ModifierDescriptor.Insight, AbilityRankType.StatBonus); + lv15buff.CreateAddStatBonusContext(StatType.SaveWill, ModifierDescriptor.Insight, AbilityRankType.StatBonus); + lv15buff.CreateConcentrationBonus(0, false, ContextValueType.Rank, AbilityRankType.StatBonus); + lv15buff.CreateSpellPenBonus(0, false, ContextValueType.Rank, AbilityRankType.StatBonus); + lv15buff.CreateDispelBonus(ContextValueType.Rank, AbilityRankType.StatBonus); + lv15buff.CreateContextRankConfigStat(AbilityRankType.StatBonus, StatType.Intelligence); + + lv15feature = Helpers.CreateFeature("PactWizardLv15Feature", "Great Power, Greater Expense", "At 15th level, when the pact " + + "wizard invokes his patron’s power to roll twice on a check, he adds his Intelligence bonus to the result as an insight bonus. " + + "Furthermore, when he applies metamagic feats to any spells he learned via his patron or curse, he treats that spell’s final " + + "effective level as 1 lower (to a minimum level equal to the spell’s original level).", null, double_rolls_feature.Icon); + lv15feature.CreateBuffExtraEffects(double_rolls_buff, lv15buff); + lv15feature.CreateGenericComponent(c => + { + c.group = FeatureGroup.WitchPatron; + c.group2 = FeatureGroup.OracleCurse; + c.value = 1; + }); + } + + public static void CreateAutoSuccess() + { + var auto_success_buff = Helpers.CreateBuff("PactWizardAutoSuccessBuff", "Great Power, Greater Expense", "", null, null, + BlueprintBuff.Flags.HiddenInUi); + auto_success_buff.CreateExtraData(); + auto_success_buff.GetExtraData().dispel_success_on_20 = true; + auto_success_buff.GetExtraData().spell_pen_success_on_20 = true; + + auto_success = Helpers.CreateFeature("PactWizardAutoSuccess", "Great Power, Greater Expense", "At 20th level, whenever the pact " + + "wizard invokes his patron’s power to roll twice on a check and his result is a natural 20, he automatically succeeds, " + + "regardless of whether or not a check of that type would normally allow an automatic success.", null, double_rolls_feature.Icon); + auto_success.CreateBuffExtraEffects(double_rolls_buff, auto_success_buff); + } + } + + [HarmonyPatch(typeof(Spellbook), "GetSpontaneousConversionSpells", typeof(AbilityData))] + internal class Spellbook_GetSpontaneousConversionSpells_PactWizardPatch + { + [HarmonyPostfix] + private static void PactWizard_GetSpontaneousConversionSpells(AbilityData spell, ref IEnumerable __result, Spellbook __instance) + { + if (spell == null) { return; } + var spellLevel = __instance.GetSpellLevel(spell); + if (spellLevel <= 0) { return; } + if (__instance.Blueprint == DB.GetSpellbook("Wizard Spellbook") && __instance.Owner.HasFact(PactWizard.spontaneous_conversion)) + { + if (spell.SpellSlot.Type != SpellSlotType.Common) { return; } + var patron = (BlueprintProgression)__instance.Owner.Progression.Selections[PactWizard.patron_selection].GetSelections(1).First(); + var list = new List(); + foreach (var entry in patron.LevelEntries) + { + if (entry.Level > __instance.Owner.Progression.GetClassLevel(DB.GetClass("Wizard Class"))) + { + continue; + } + var components = entry.Features.Select(f => f.GetComponent()); + foreach (var comp in components) + { + if (comp.SpellLevel <= spellLevel) + { + list.Add(comp.m_Spell.Get()); + } + } + } + __result = list.ToArray(); + } + } + } +} + +namespace MagicTime.Archetypes.Mechanics +{ + [AllowedOn(typeof(BlueprintBuff), false)] + [TypeId("36908850-ef36-4936-8d20-b67a9e6b4095")] + public class PactWizardRollD20 : UnitBuffComponentDelegate, IInitiatorRulebookHandler, IRulebookHandler, + ISubscriber, IInitiatorRulebookSubscriber + { + private bool CanApply(RuleRollD20 evt) + { + var prev = Rulebook.CurrentContext.PreviousEvent; + if ((prev is RuleInitiativeRoll || prev is RuleSpellResistanceCheck || prev is RuleDispelMagic || prev is RuleSavingThrow || + prev is RuleCheckConcentration) && evt.Initiator == Owner) + { + return true; + } + return false; + } + + public void OnEventAboutToTrigger(RuleRollD20 evt) + { + if (!CanApply(evt)) { return; } + evt.AddReroll(1, true, Buff); + } + + public void OnEventDidTrigger(RuleRollD20 evt) + { + if (!CanApply(evt)) { return; } + Owner.Resources.Spend(PactWizard.pact_wizard_reroll_resource, 1); + if (Owner.Resources.GetResourceAmount(PactWizard.pact_wizard_reroll_resource) == 0) + { + Owner.RemoveFact(Buff); + } + } + } +} \ No newline at end of file diff --git a/Content/Feats/AcadamaeGraduate.cs b/Content/Feats/AcadamaeGraduate.cs index d08132f..7b08d0c 100644 --- a/Content/Feats/AcadamaeGraduate.cs +++ b/Content/Feats/AcadamaeGraduate.cs @@ -25,6 +25,8 @@ public static void Create() "fatigued for 1 minute.", "f_graduate", null, false); graduate_feature.CreateFeatureTags(Kingmaker.Blueprints.Classes.Selection.FeatureTag.Magic); graduate_feature.CreateFeatureRestrictionInv(DB.GetFeature("Opposition School Conjuration")); + graduate_feature.RestrictByStat(StatType.Intelligence, 3); + graduate_feature.CreateRecommendationRequiresSpellbook(); graduate_feature.CreateGenericComponent(); Helpers.AddNewWizardFeat(graduate_feature); } diff --git a/Content/Feats/MetamagicIntensfied.cs b/Content/Feats/MetamagicIntensfied.cs new file mode 100644 index 0000000..43a99c9 --- /dev/null +++ b/Content/Feats/MetamagicIntensfied.cs @@ -0,0 +1,85 @@ +using Kingmaker.Blueprints; +using Kingmaker.Blueprints.Classes; +using Kingmaker.Blueprints.Classes.Selection; +using Kingmaker.UnitLogic.Abilities; +using Kingmaker.UnitLogic.Abilities.Components; +using Kingmaker.UnitLogic.Buffs.Blueprints; +using Kingmaker.UnitLogic.Commands.Base; +using Kingmaker.UnitLogic.Mechanics; +using Kingmaker.UnitLogic.Mechanics.Actions; +using MagicTime.Utilities; +using Starion.BPExtender.UnitFact; +using System.Collections.Generic; +using System.Linq; + +namespace MagicTime.Feats +{ + internal static class MetamagicIntensfied + { + public static void Create() + { + var intensified_spell_feature = Helpers.CreateFeature("MetamagicItensified", "Metamagic (Intensified Spell)", "Your spells can go beyond " + + "several normal limitations.\nBenefit: An intensified spell increases the maximum number of damage dice by 5 levels. You must " + + "actually have sufficient caster levels to surpass the maximum in order to benefit from this feat. No other variables of the " + + "spell are affected, and spells that inflict damage that is not modified by caster level are not affected by this feat.\n" + + "Level increase: +1 (An intensified spell uses up a spell slot one level higher than the spell's actual level.)", + "m_intensified_spell"); + intensified_spell_feature.Groups = new FeatureGroup[] { FeatureGroup.Feat, FeatureGroup.WizardFeat }; + intensified_spell_feature.CreateFeatureTags(FeatureTag.Magic & FeatureTag.Metamagic); + intensified_spell_feature.RestrictByStat(Kingmaker.EntitySystem.Stats.StatType.Intelligence, 3); + intensified_spell_feature.CreateRecommendationRequiresSpellbook(); + intensified_spell_feature.AddNewMetamagic(Starion.MetamagicExtender.ExtraMetamagic.Intensified); + Helpers.AddNewWizardFeat(intensified_spell_feature); + + var mythic_variant = Helpers.CreateFeature("FavoriteMetamagicIntensified", "Favorite Metamagic — Intensified", "You can now use " + + "your mythic powers to fuel your metamagic abilities.\nBenefit: Select one kind of metamagic.The level cost for its use " + + "decreases by one(to a minimum of 0)."); + mythic_variant.CreateFeatureRestriction(intensified_spell_feature); + var list = DB.GetSelection("Favorite Metamagic").m_AllFeatures.ToList(); + list.Add(mythic_variant.ToReference()); + DB.GetSelection("Favorite Metamagic").m_AllFeatures = list.ToArray(); + + intensified_spell_feature.IsPrerequisiteFor = new List() + { + mythic_variant.ToReference() + }; + + Starion.BPExtender.Mechanics.FavoriteMetamagicIntensified = mythic_variant; + + // If Metamagic Insight exists in the game then add this feat to the list of variants. + if (ArcaneDiscoveries.MetamagicInsight.Initialized) + { + var itensify_buff = Helpers.CreateBuff( + "MIIntensifyBuff", + "Metamagic Insight — Intensified Spell", + "Your next spell will be automatically Intensified.", + null, null, BlueprintBuff.Flags.RemoveOnRest); + itensify_buff.m_Icon = intensified_spell_feature.Icon; + itensify_buff.CreateAbilityUseTrigger(false, null, true, new ContextActionRemoveSelf()); + itensify_buff.CreateAutoMetamagic((Metamagic)Starion.MetamagicExtender.ExtraMetamagic.Intensified, 10); + + var intensify_ability = Helpers.CreateAbility( + "MIIntensifyAbility", + "Metamagic Insight — Intensified Spell", + "Your next spell will be automatically Intensified.", + null, + "1 round", "", + UnitCommand.CommandType.Free); + intensify_ability.m_Icon = intensified_spell_feature.Icon; + intensify_ability.SetSupernaturalSelf(); + intensify_ability.SetShowOnlyIfFact(intensified_spell_feature); + intensify_ability.DisableIfFact(itensify_buff); + intensify_ability.CreateAbilityResourceLogic(ArcaneDiscoveries.MetamagicInsight.metamagic_resource, 1); + intensify_ability.CreateAbilityEffectRunAction( + Helpers.CreateContextActionBuff(itensify_buff, false, true, Helpers.CreateContextDurationValue(1, DurationRate.Rounds))); + + var comp = ArcaneDiscoveries.MetamagicInsight.metamagic_ability.GetComponent(); + var variant_list = comp.m_Variants.ToList(); + variant_list.Add(intensify_ability.ToReference()); + comp.m_Variants = variant_list.ToArray(); + + intensify_ability.m_Parent = ArcaneDiscoveries.MetamagicInsight.metamagic_ability.ToReference(); + } + } + } +} \ No newline at end of file diff --git a/Content/MainPatcher.cs b/Content/MainPatcher.cs index a05a4a7..a8d4539 100644 --- a/Content/MainPatcher.cs +++ b/Content/MainPatcher.cs @@ -1,6 +1,5 @@ using HarmonyLib; using Kingmaker.Blueprints.JsonSystem; -using System.Linq; using Kingmaker.Blueprints.Classes; using Kingmaker.Blueprints; @@ -37,6 +36,7 @@ static void Postfix() { if (FeatON("shrewd_tactician")) { Feats.ShrewdTactician.Create(); } if (FeatON("acadamae")) { Feats.AcadamaeGraduate.Create(); } + if (FeatON("meta_intensified")) { Feats.MetamagicIntensfied.Create(); } } if (Main.SettingsContainer.groups["magus_arcana"].enabled) @@ -54,6 +54,8 @@ static void Postfix() { if (ArchetypeON("flagellant") && !no_hb) { Archetypes.Flagellant.Create(); } if (ArchetypeON("warsighted")) { Archetypes.Warsighted.Create(); } + if (ArchetypeON("blood_arcanist")) { Archetypes.BloodArcanist.Create(); } + if (ArchetypeON("pact_wizard")) { Archetypes.PactWizard.Create(); } } Resources.Cleanup(); diff --git a/DefaultSettings.json b/DefaultSettings.json index e56794a..c4234ef 100644 --- a/DefaultSettings.json +++ b/DefaultSettings.json @@ -25,6 +25,20 @@ "enabled": true, "description": "An oracle who trades revelations for combat feats.", "homebrew": false + }, + { + "id": "blood_arcanist", + "name": "Blood Arcanist", + "enabled": true, + "description": "An arcanist with fewer exploits but a full bloodline.", + "homebrew": false + }, + { + "id": "pact_wizard", + "name": "Pact Wizard", + "enabled": true, + "description": "Wizard who trades bonus feats for a witch patron and eventually an oracle curse.", + "homebrew": false } ] }, @@ -104,6 +118,13 @@ "enabled": true, "description": "Summoning spells only take a standard action to cast but have a chance to cause fatigue for a while.", "homebrew": false + }, + { + "id": "meta_intensified", + "name": "Intensified Spell", + "enabled": true, + "description": "Metamagic that raises maximum number of damage dices by 5.", + "homebrew": false } ] }, diff --git a/Extensions/Base.cs b/Extensions/Base.cs new file mode 100644 index 0000000..4855ddb --- /dev/null +++ b/Extensions/Base.cs @@ -0,0 +1,53 @@ +using Kingmaker.Blueprints.Classes; +using Kingmaker.EntitySystem; +using System.Collections.Generic; + +namespace Starion.BPExtender +{ + public static class Mechanics + { + public static BlueprintFeature FavoriteMetamagicIntensified; + } + + public class BlueprintExtraDataContainer + { + public Dictionary ResourceContainer; + public Dictionary UnitFactContainer; + + public BlueprintExtraDataContainer() + { + ResourceContainer = new Dictionary(); + UnitFactContainer = new Dictionary(); + } + } + + public static class EXData + { + public static BlueprintExtraDataContainer Data; + + public static void Initialize() + { + Data = new BlueprintExtraDataContainer(); + } + + public static List FetchUnitFactContainers(EntityFactsManager manager) + { + var result = new List(); + UnitFact.Container container; + foreach (var fact in manager.m_Facts) + { + container = null; + if (Data.UnitFactContainer.ContainsKey(fact.Blueprint.AssetGuid.m_Guid.ToString())) + { + container = Data.UnitFactContainer[fact.Blueprint.AssetGuid.m_Guid.ToString()]; + } + + if (container != null) + { + result.Add(container); + } + } + return result; + } + } +} \ No newline at end of file diff --git a/Extensions/BlueprintAbilityResource.cs b/Extensions/BlueprintAbilityResource.cs new file mode 100644 index 0000000..a00ad6c --- /dev/null +++ b/Extensions/BlueprintAbilityResource.cs @@ -0,0 +1,88 @@ +using HarmonyLib; +using Kingmaker.Blueprints; +using Kingmaker.EntitySystem.Stats; +using Kingmaker.PubSubSystem; +using Kingmaker.UnitLogic; +using System; + +namespace Starion.BPExtender.AbilityResource +{ + public class Container + { + public bool half_step = false; + public bool delayed_spending = false; + } + + internal static class Extension + { + internal static Container GetExtraData(this BlueprintAbilityResource __instance) + { + if (EXData.Data.ResourceContainer.ContainsKey(__instance.AssetGuid.m_Guid.ToString())) + { + return EXData.Data.ResourceContainer[__instance.AssetGuid.m_Guid.ToString()]; + } + return null; + } + + internal static void CreateExtraData(this BlueprintAbilityResource __instance) + { + if (__instance.GetExtraData() == null) + { + EXData.Data.ResourceContainer.Add(__instance.AssetGuid.m_Guid.ToString(), new Container()); + } + } + + /// + /// The maximum amount of available resource will be based on ability score modifiers. + /// + /// The ability score. + /// The minimum amount. + /// Flag to determine whether only half the ability score modifier is considered. + internal static void SetIncreasedWithStat(this BlueprintAbilityResource __instance, StatType stat_type, int base_value, bool half_step) + { + __instance.m_UseMax = false; + __instance.m_Min = base_value; + __instance.m_MaxAmount = new BlueprintAbilityResource.Amount + { + BaseValue = base_value, + IncreasedByStat = true, + ResourceBonusStat = stat_type + }; + if (half_step && __instance.GetExtraData() != null ) { __instance.GetExtraData().half_step = true; } + } + + /// + /// Recalculates the max amount of resources based on a stat modifier, but only with half the modifier bonus. + /// For instance, a Pact Wizard can roll twice his D20s a number of times per day equals to 3 + half his Intelligence modifier. + /// Automatically assumes the calculation is stat-based since class-based ones already have this functionality. + /// + internal static int RecalculateWithHalfModifier(this BlueprintAbilityResource __instance, UnitDescriptor unit) + { + var num = __instance.m_MaxAmount.BaseValue; + var modifiableValueAttributeStat = unit.Stats.GetStat(__instance.m_MaxAmount.ResourceBonusStat) as ModifiableValueAttributeStat; + if (modifiableValueAttributeStat != null) + { + num += modifiableValueAttributeStat.Bonus / 2; + } + var bonus = 0; + EventBus.RaiseEvent(unit.Unit, delegate (IResourceAmountBonusHandler h) + { + h.CalculateMaxResourceAmount(__instance, ref bonus); + }); + return Math.Max(__instance.m_Min, __instance.ApplyMinMax(num) + bonus); + } + } + + [HarmonyPatch(typeof(BlueprintAbilityResource), "GetMaxAmount")] + internal class EXData_BlueprintAbilityResource + { + [HarmonyPostfix] + private static void EXData_GetMaxAmount(BlueprintAbilityResource __instance, ref int __result, UnitDescriptor unit) + { + if (__instance.GetExtraData() != null && __instance.GetExtraData().half_step) + { + __result = __instance.RecalculateWithHalfModifier(unit); + } + } + } +} \ No newline at end of file diff --git a/Extensions/BlueprintUnitFact.cs b/Extensions/BlueprintUnitFact.cs new file mode 100644 index 0000000..102ee07 --- /dev/null +++ b/Extensions/BlueprintUnitFact.cs @@ -0,0 +1,142 @@ +using Kingmaker.Blueprints; +using Kingmaker.Blueprints.Classes; +using Kingmaker.Blueprints.Facts; +using Kingmaker.Blueprints.JsonSystem; +using Kingmaker.EntitySystem; +using Kingmaker.PubSubSystem; +using Kingmaker.RuleSystem.Rules; +using Kingmaker.UnitLogic; +using Kingmaker.UnitLogic.Abilities; +using Kingmaker.UnitLogic.Abilities.Blueprints; +using Kingmaker.UnitLogic.FactLogic; +using HarmonyLib; +using System.Collections.Generic; +using System.Linq; + +namespace Starion.BPExtender.UnitFact +{ + public class Container + { + public bool dispel_success_on_20; + public bool spell_pen_success_on_20; + } + + internal static class Extension + { + internal static Container GetExtraData(this BlueprintUnitFact __instance) + { + if (EXData.Data.UnitFactContainer.ContainsKey(__instance.AssetGuid.m_Guid.ToString())) + { + return EXData.Data.UnitFactContainer[__instance.AssetGuid.m_Guid.ToString()]; + } + return null; + } + + internal static void CreateExtraData(this BlueprintUnitFact __instance) + { + if (__instance.GetExtraData() == null) + { + EXData.Data.UnitFactContainer.Add(__instance.AssetGuid.m_Guid.ToString(), new Container()); + } + } + + internal static void CreateComponents(this BlueprintUnitFact __instance, params BlueprintComponent[] components) + { + __instance.ComponentsArray = __instance.ComponentsArray.Concat(components).ToArray(); + var names = new HashSet(); + foreach (var c in __instance.ComponentsArray) + { + if (string.IsNullOrEmpty(c.name)) + { + c.name = $"${c.GetType().Name}"; + } + if (!names.Add(c.name)) + { + string name; + for (int i = 0; !names.Add(name = $"{c.name}${i}"); i++) ; + c.name = name; + } + } + __instance.OnEnable(); + } + + /// + /// Add a metamagic feat that doesn't exist in the base game. + /// + /// A different enum from the regular metamagic one. + internal static void AddNewMetamagic(this BlueprintUnitFact __instance, MetamagicExtender.ExtraMetamagic metamagic) + { + var comp = new AddMetamagicFeat(); + comp.Metamagic = (Metamagic)metamagic; + __instance.CreateComponents(comp); + } + } + + [AllowedOn(typeof(BlueprintProgression), false)] + [TypeId("78e379fb-a496-419f-ab83-8dd22dfae0a3")] + public class SpellListContainer : BlueprintComponent + { + public Dictionary spell_list = new Dictionary(); + } + + [AllowedOn(typeof(BlueprintUnitFact), false)] + [TypeId("fb656052-fe08-4ad4-a8f9-b13854adf460")] + public class ReduceMetamagicCostForSpellFromFeatureGroups : UnitFactComponentDelegate, IInitiatorRulebookHandler, + IRulebookHandler, ISubscriber, IInitiatorRulebookSubscriber + { + public int value; + public FeatureGroup group = FeatureGroup.None; + public FeatureGroup group2 = FeatureGroup.None; + + public void OnEventAboutToTrigger(RuleApplyMetamagic evt) + { + if (evt.Spellbook == null || evt.Initiator != Owner) { return; } + foreach (var fact in Owner.Progression.Features) + { + if (!fact.Blueprint.Groups.Contains(group) && !fact.Blueprint.Groups.Contains(group2)) + { + continue; + } + var progression = (BlueprintProgression)fact.Blueprint; + var level = Owner.Progression.GetProgression(progression).Level; + var container = fact.GetComponent(); + if (container != null && container.spell_list.ContainsKey(evt.Spell) && container.spell_list[evt.Spell] <= level && + evt.AppliedMetamagics.Count > 0) + { + evt.ReduceCost(value); + return; + } + } + } + + public void OnEventDidTrigger(RuleApplyMetamagic evt) + { + } + } + + [HarmonyPatch(typeof(RuleDispelMagic))] + internal class UnitFactExtender_RuleDispelMagic + { + [HarmonyPatch("IsSuccessRoll", typeof(int))] + [HarmonyPostfix] + private static void UFE_IsSuccessRoll(RuleDispelMagic __instance, ref bool __result, int d20) + { + if (d20 != 20) { return; } + var d20_auto_success = EXData.FetchUnitFactContainers(__instance.Initiator.Facts).Select(f => f.dispel_success_on_20 == true).Count() > 0; + if (d20_auto_success) { __result = true; } + } + } + + [HarmonyPatch(typeof(RuleSpellResistanceCheck))] + internal class UnitFactExtender_RuleSpellResistanceCheck + { + [HarmonyPatch("get_IsSpellResisted")] + [HarmonyPostfix] + private static void UFE_get_IsSpellResisted(RuleSpellResistanceCheck __instance, ref bool __result) + { + if (__instance.Roll.Result != 20) { return; } + var d20_auto_success = EXData.FetchUnitFactContainers(__instance.Initiator.Facts).Select(f => f.spell_pen_success_on_20 == true).Count() > 0; + if (d20_auto_success) { __result = true; } + } + } +} \ No newline at end of file diff --git a/Extensions/MetamagicExtender.cs b/Extensions/MetamagicExtender.cs new file mode 100644 index 0000000..94df91c --- /dev/null +++ b/Extensions/MetamagicExtender.cs @@ -0,0 +1,238 @@ +using HarmonyLib; +using Kingmaker.RuleSystem.Rules; +using Kingmaker.UI.Common; +using Kingmaker.UI.MVVM._VM.ServiceWindows.Spellbook.Metamagic; +using Kingmaker.UnitLogic; +using Kingmaker.UnitLogic.Abilities; +using Kingmaker.UnitLogic.Abilities.Blueprints; +using Kingmaker.UnitLogic.FactLogic; +using Kingmaker.UnitLogic.Mechanics; +using Kingmaker.UnitLogic.Mechanics.Components; +using MagicTime.Utilities; +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Starion +{ + public static class MetamagicExtender + { + [Flags] + public enum ExtraMetamagic + { + Intensified = 2048 + } + + public static Dictionary MetamagicDefaultCost = new Dictionary() + { + { ExtraMetamagic.Intensified, 1 } + }; + + public static bool IsMetamagicAvailableForThisSpell(ExtraMetamagic metamagic, BlueprintAbility spell) + { + switch (metamagic) + { + case ExtraMetamagic.Intensified: + return spell.AvailableMetamagic.HasMetamagic(Metamagic.Bolstered); + + default: + return false; + } + } + + public static int CalculateIntensifiedSpellDices(ContextRankConfig crc, MechanicsContext context) + { + var value = crc.ApplyProgression(crc.GetBaseValue(context)); + return Math.Min(value, crc.m_Max + 5); + } + } + + [HarmonyPatch(typeof(RuleApplyMetamagic), "OnTrigger")] + internal class MetamagicExtender_RuleApplyMetamagic + { + [HarmonyPostfix] + private static void ME_OnTrigger(RuleApplyMetamagic __instance) + { + var lv_adjustment = 0; + if (__instance.AppliedMetamagics.Contains((Metamagic)MetamagicExtender.ExtraMetamagic.Intensified) && + __instance.Initiator.HasFact(BPExtender.Mechanics.FavoriteMetamagicIntensified)) + { + lv_adjustment++; + } + __instance.Result.SpellLevelCost -= lv_adjustment; + var corr = 0; + if (__instance.AppliedMetamagics.Contains(Metamagic.CompletelyNormal)) + { + corr = 1; + } + if (__instance.BaseLevel + __instance.Result.SpellLevelCost + corr < 0) + { + __instance.Result.SpellLevelCost = -__instance.BaseLevel; + } + } + } + + [HarmonyPatch(typeof(RuleCollectMetamagic), "AddMetamagic")] + internal class MetamagicExtender_RuleCollectMetamagic + { + [HarmonyPostfix] + private static void ME_AddMetamagic(RuleCollectMetamagic __instance, Feature metamagicFeature) + { + if (MagicTime.Main.static_constructor_uiutilitytexts_safe == false) + { + var old_method = AccessTools.Method(typeof(UIUtilityTexts), "GetMetamagicList"); + var new_method = AccessTools.Method(typeof(MetamagicExtender_UIUtilityTexts), "ME_GetMetamagicList"); + var harmony = new Harmony(MagicTime.Main.Mod.Info.Id); + harmony.Patch(old_method, postfix: new HarmonyMethod(new_method)); + MagicTime.Main.static_constructor_uiutilitytexts_safe = true; + } + + if (!__instance.KnownMetamagics.Contains(metamagicFeature)) { return; } + if (__instance.m_SpellLevel < 0) + { + return; + } + if (__instance.m_SpellLevel >= 10) + { + return; + } + AddMetamagicFeat component = metamagicFeature.GetComponent(); + Metamagic metamagic = component.Metamagic; + if (__instance.m_SpellLevel + component.Metamagic.DefaultCost() > 10) + { + return; + } + if (__instance.Spell != null && !__instance.SpellMetamagics.Contains(metamagicFeature) && + MetamagicExtender.IsMetamagicAvailableForThisSpell((MetamagicExtender.ExtraMetamagic)metamagic, __instance.Spell)) + { + __instance.SpellMetamagics.Add(metamagicFeature); + } + } + } + + [HarmonyPatch(typeof(ContextRankConfig), "GetValue", typeof(MechanicsContext))] + internal class MetamagicExtender_ContextRankConfig + { + [HarmonyPrefix] + private static bool ME_GetValue_Prefix(ContextRankConfig __instance, MechanicsContext context, out int __state) + { + __state = 0; + if (__instance.m_BaseValueType != ContextRankBaseValueType.CasterLevel) { return true; } + if (context.MaybeCaster == null) { return true; } + if (context.HasMetamagic((Metamagic)MetamagicExtender.ExtraMetamagic.Intensified)) + { + __state = MetamagicExtender.CalculateIntensifiedSpellDices(__instance, context); + } + return true; + } + + [HarmonyPostfix] + private static void ME_GetValue_Postfix(ref int __result, int __state) + { + if (__state > 0 && __state > __result) + { + __result = __state; + } + } + } + + [HarmonyPatch(typeof(MetamagicHelper))] + internal class MetamagicExtender_MetamagicHelper + { + [HarmonyPatch("DefaultCost")] + [HarmonyPostfix] + private static void ME_DefaultCost(ref int __result, Metamagic metamagic) + { + switch ((MetamagicExtender.ExtraMetamagic)metamagic) + { + case MetamagicExtender.ExtraMetamagic.Intensified: + __result = 1; + break; + } + } + + [HarmonyPatch("SpellIcon")] + [HarmonyPostfix] + private static void ME_SpellIcon(ref Sprite __result, Metamagic metamagic) + { + switch ((MetamagicExtender.ExtraMetamagic)metamagic) + { + case MetamagicExtender.ExtraMetamagic.Intensified: + __result = AssetLoader.LoadInternal("Icons", "m_intensified"); + break; + } + } + } + + [HarmonyPatch(typeof(SpellbookMetamagicSelectorVM))] + internal class MetamagicExtender_SpellbookMetamagicSelectorVM + { + [HarmonyPatch("GetCost")] + [HarmonyPostfix] + private static void ME_GetCost(SpellbookMetamagicSelectorVM __instance, ref int __result, Metamagic metamagic) + { + if ((MetamagicExtender.ExtraMetamagic)metamagic == MetamagicExtender.ExtraMetamagic.Intensified) + { + if (__instance.m_Unit.Value.HasFact(BPExtender.Mechanics.FavoriteMetamagicIntensified)) + { + __result--; + } + } + } + + [HarmonyPatch("AddMetamagic")] + [HarmonyPostfix] + private static void ME_AddMetamagic(SpellbookMetamagicSelectorVM __instance) + { + var corr = 0; + if (__instance.m_MetamagicBuilder.Value.AppliedMetamagics.Contains(Metamagic.CompletelyNormal)) + { + corr = 1; + } + if (__instance.CurrentTemporarySpell.Value.SpellLevel < __instance.m_MetamagicBuilder.Value.BaseSpellLevel - corr) + { + __instance.CurrentTemporarySpell.Value.SpellLevel = __instance.m_MetamagicBuilder.Value.BaseSpellLevel - corr; + __instance.m_MetamagicBuilder.Value.ResultSpellLevel = __instance.m_MetamagicBuilder.Value.BaseSpellLevel - corr; + } + } + + [HarmonyPatch("RemoveMetamagic")] + [HarmonyPostfix] + private static void ME_RemoveMetamagic(SpellbookMetamagicSelectorVM __instance) + { + var corr = 0; + if (__instance.m_MetamagicBuilder.Value.AppliedMetamagics.Contains(Metamagic.CompletelyNormal)) + { + corr = 1; + } + if (__instance.CurrentTemporarySpell.Value.SpellLevel < __instance.m_MetamagicBuilder.Value.BaseSpellLevel - corr) + { + __instance.CurrentTemporarySpell.Value.SpellLevel = __instance.m_MetamagicBuilder.Value.BaseSpellLevel - corr; + __instance.m_MetamagicBuilder.Value.ResultSpellLevel = __instance.m_MetamagicBuilder.Value.BaseSpellLevel - corr; + } + } + } + + internal static class MetamagicExtender_UIUtilityTexts + { + private static void ME_GetMetamagicList(ref string __result, Metamagic mask) + { + var to_append = ""; + + if ((mask & (Metamagic)MetamagicExtender.ExtraMetamagic.Intensified) != 0) + { + to_append += "Intensified, "; + } + if (to_append.Length > 2) + { + to_append = to_append.Substring(0, to_append.Length - 2); + } + if (__result.Length > 0) + { + __result += ", "; + } + + __result += to_append; + } + } +} \ No newline at end of file diff --git a/Guids.json b/Guids.json index a179134..be9a051 100644 --- a/Guids.json +++ b/Guids.json @@ -5,9 +5,34 @@ "ADForestBlessing": "4adb6146-19ee-4417-8be2-de101c8453c0", "ADIdealize": "38bbfb5b-953f-4cc1-9608-b12d3461daca", "ADStaffLikeWand": "c809ab5c-3fa8-401a-bc39-e54cd14d2c45", + "BloodArcanistArchetype": "63820c2f-22bb-44ec-b1ed-333649390119", + "BloodArcanistBloodlineSelection": "a09e5315-774e-418f-939e-78a01d0dfbc6", + "BloodArcanistAbyssalProgression": "a520e305-a01a-4aae-9591-5d466f518896", + "BloodArcanistArcaneProgression": "a98cd2e3-83b3-4c4f-ad8e-1df7afacf9af", + "BloodArcanistCelestialProgression": "a779282d-b1b9-44c0-84c0-4974aecd93e1", + "BloodArcanistDraconicBlackProgression": "8713ed41-d679-4024-9325-a0c331bfed0f", + "BloodArcanistDraconicBlueProgression": "cb766f1f-60d4-4037-a65e-19ec5aa64f4e", + "BloodArcanistDraconicBrassProgression": "1a09054e-1b79-4f7f-b56f-708db92e874d", + "BloodArcanistDraconicBronzeProgression": "78484989-4dd6-44e4-be9b-4940f1fff2e0", + "BloodArcanistDraconicCopperProgression": "bfba88c2-6736-4161-859b-eb9c1455dc9a", + "BloodArcanistDraconicGoldProgression": "fd4b6668-b6b9-471a-9f5a-3601b624839b", + "BloodArcanistDraconicGreenProgression": "4074b1cf-e5d0-4f80-bce2-9418796dfcee", + "BloodArcanistDraconicRedProgression": "96a08bfe-d6cb-410b-a816-19f89b9db3f2", + "BloodArcanistDraconicSilverProgression": "e60ab108-ff6f-42e0-a6fc-72cf73442ee6", + "BloodArcanistDraconicWhiteProgression": "ffd64b5c-534a-448e-8680-f2cff41c39f2", + "BloodArcanistElementalAirProgression": "fbbcbc6f-bd6c-4cba-8274-b48c6a873a20", + "BloodArcanistElementalEarthProgression": "6b462cd2-2a2c-4c3b-9627-93e6739b6182", + "BloodArcanistElementalFireProgression": "f3ad6791-eae3-4968-aa2f-6a7558e81ef5", + "BloodArcanistElementalWaterProgression": "4fc67e6a-5abe-4619-82c5-c8f6140eaf6a", + "BloodArcanistFeyProgression": "b1d6515b-516b-42b1-8a76-8696f68ac6b6", + "BloodArcanistInfernalProgression": "03bff8f0-6d01-4d60-baaa-d3e912aa7b38", + "BloodArcanistSerpentineProgression": "fae67d0a-3eeb-499d-9f93-519b87316176", + "BloodArcanistUndeadProgression": "f2b35267-b8c1-4dde-8f22-17f623731d20", + "BloodlineRequisiteFeature": "e2cfd3ce-df7c-4008-8b25-aa82d6db3c77", "CircleOfOrderAbility": "8fa06161-e943-4887-bc0a-36c64e3a6a5b", "CircleOfOrderBuff": "a262e6f4-870c-4e4f-bac1-671cd55492be", "CircleOfOrderFeature": "ed7aa6a6-c263-4777-b293-809a5e899d13", + "FavoriteMetamagicIntensified": "e1488702-a6da-4474-b5ca-98a79811f34c", "FlagellantArchetype": "0a770ce0-6e6f-458c-ab7d-715120e75a68", "FLDeadenedFlesh": "7cc386a6-ed37-4658-af16-a0749fd5f1e7", "FLFocused": "b295a5f6-c338-460d-941f-7f5d2fa31a32", @@ -26,6 +51,7 @@ "FLProficiencies": "cbb185fd-d659-42fe-a0ad-a82ec3c91dde", "KnowledgeIsPower": "f807015c-1d1f-4301-aedf-a28bef97455e", "MetamagicInsight": "921a984f-3730-40e7-ac0c-826f6e4b1cdf", + "MetamagicItensified": "987d7a28-3b61-4def-b882-e6dd80a41731", "MIBaseAbility": "8b9a0d8d-2027-451a-b0dd-43adcb49e97d", "MIBolsterAbility": "dad27df1-97e6-4c87-b201-fefe5ed43d86", "MIBolsterBuff": "c008767f-a8ad-4b1e-abaf-fcf72d46428d", @@ -34,6 +60,8 @@ "MIExtendAbility": "93e80475-d9b7-488b-b11e-33fb6db10a24", "MIExtendBuff": "1a875ca7-d192-407b-b651-5ecdb52e194a", "MIFeature": "1c920009-39ab-4db4-b70d-02f03a17127a", + "MIIntensifyAbility": "b0e0a490-fe5b-4a52-9805-1baa7bfea7a5", + "MIIntensifyBuff": "c8bdbcdb-5673-4f83-8d2b-b0a25377de0b", "MIMaximizeAbility": "9a7880a5-03a8-4695-8ee9-b449aec9c3a8", "MIMaximizeBuff": "f4acbb10-0750-47b2-b230-5064697bcd35", "MIPersistentAbility": "bf0ba5cf-0ec8-4b71-8da3-763196230ccf", @@ -55,7 +83,20 @@ "ORNecromancy": "bfa47162-1aee-43f2-a7a8-606430cf6489", "ORSchoolSelection": "d7e71cae-e607-490f-9198-45e6b4a59882", "ORTransmutation": "1ef0565f-6a72-4da0-9440-932ad0e44b60", + "PactWizardArchetype": "93844331-6ad2-4784-a5e0-49a7296c227b", + "PactWizardAutoSuccess": "27400567-43c6-42a9-ba71-44644e52486c", + "PactWizardAutoSuccessBuff": "89e0e5d0-cc9d-4589-8c94-b54fc2562c4b", + "PactWizardDoubleRollsBuff": "fe099b42-8fce-48e7-9005-ba0238633680", + "PactWizardDoubleRollsAb": "29c13b3f-88ca-43bc-803f-8b1a0aa2ac34", + "PactWizardDoubleRollsFeature": "49fb1f24-c5e5-4c69-a76c-86206d126a9f", + "PactWizardDoubleRollsResource": "23ba25e1-6e9d-4e1d-aec4-96b7e968caab", + "PactWizardConversion": "e6d56aad-8630-43a8-979b-604b2be71c08", + "PactWizardCurseSelection": "d1b23aa9-e4e8-4d3a-bb10-0ac0ba4276f1", + "PactWizardLv15Buff": "b5e05185-3700-4b40-afcd-f1e5750adec2", + "PactWizardLv15Feature": "cc2a38f8-5fd1-47ef-b2d6-1134d9801aad", + "PactWizardPatronSelection": "db921b58-7558-495b-8e8f-4d7077308f3c", "ShrewdTactician": "d6c2db67-a840-4b0b-add5-94a03214c9ac", + "SpellMeteorSwarm": "9a78adb7-1c0b-43ad-af3a-6b3f5c092d19", "WarsightedArchetype": "a86eea74-e45c-40ff-b281-48800117d66c", "WarsightedBonusFeat": "e8680fc5-fa2e-48a0-800c-06d4b1fdd668" } \ No newline at end of file diff --git a/Info.json b/Info.json index 40f8d49..a0966aa 100644 --- a/Info.json +++ b/Info.json @@ -3,11 +3,11 @@ "DisplayName": "Magic Time!", "Author": "Starion (aka kreaddy)", "Version": "1.3.3", - "ManagerVersion": "0.23.0.0", + "ManagerVersion": "0.23.0", "GameVersion": "1.1.4f", "Requirements": [], "AssemblyName": "MagicTime.dll", - "LoadAfter": ["Worldcrawl"], + "LoadAfter": ["Worldcrawl", "TabletopTweaks"], "HomePage": "https://github.com/Stari0n/MagicTime/releases", "Repository": "https://raw.githubusercontent.com/Stari0n/MagicTime/master/Repository.json", "EntryMethod": "MagicTime.Main.Load" diff --git a/MagicTime.csproj b/MagicTime.csproj index 25313f2..156c95c 100644 --- a/MagicTime.csproj +++ b/MagicTime.csproj @@ -111,6 +111,8 @@ + + @@ -118,8 +120,10 @@ + + @@ -128,9 +132,13 @@ + + + + @@ -166,6 +174,12 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + diff --git a/Main.cs b/Main.cs index e7fbfb2..f3b8224 100644 --- a/Main.cs +++ b/Main.cs @@ -18,6 +18,9 @@ internal static class Main public static Settings SettingsContainer = new Settings(); + // Annoying workarounds for static constructors messing up harmony. + public static bool static_constructor_uiutilitytexts_safe = false; + public static void Log(string msg) { Mod.Logger.Log(msg); @@ -36,6 +39,7 @@ private static bool Load(UnityModManager.ModEntry modEntry) modEntry.OnGUI = OnGUI; modEntry.OnSaveGUI = OnSaveGUI; var harmony = new Harmony(modEntry.Info.Id); + Starion.BPExtender.EXData.Initialize(); Resources.Initializer(); harmony.PatchAll(Assembly.GetExecutingAssembly()); return true; diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index e833cc9..57382d2 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("1.3.3")] +[assembly: AssemblyFileVersion("1.3.3")] diff --git a/Resources.cs b/Resources.cs index 2b54cd5..34d9acd 100644 --- a/Resources.cs +++ b/Resources.cs @@ -1,15 +1,13 @@ -using JetBrains.Annotations; +using Kingmaker.Blueprints; +using Newtonsoft.Json.Linq; using System; -using System.IO; using System.Collections.Generic; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; +using System.IO; using UnityModManagerNet; -using Kingmaker.Blueprints; namespace MagicTime { - static class Resources + internal static class Resources { private static readonly Dictionary new_bps = new Dictionary(); private static JObject data; @@ -21,22 +19,36 @@ public static void Initializer() file.Close(); file.Dispose(); } + public static BlueprintGuid AddAsset(string asset_name, SimpleBlueprint bp) { var result = new BlueprintGuid(Guid.Parse((string)data.SelectToken(asset_name))); if (ResourcesLibrary.BlueprintsCache.m_LoadedBlueprints.ContainsKey(result)) { - Main.Log("Duplicate Guid: " + result.ToString() + "-" + asset_name + ". Autogenerating..."); + Main.Log("Duplicate Guid: " + result.ToString() + "-" + asset_name); + Main.Log("Original Guid: " + ResourcesLibrary.BlueprintsCache.m_LoadedBlueprints[result].Blueprint.name); result = new BlueprintGuid(Guid.NewGuid()); + Main.Log("Autogenerated Guid: " + result.ToString()); } new_bps[result] = bp; ResourcesLibrary.BlueprintsCache.AddCachedBlueprint(result, bp); bp.OnEnable(); return result; } + public static void Cleanup() { data = null; } + + public static void UpdateDatabase(string guid, string name) + { + var text = File.ReadAllText(UnityModManager.modsPath + @"/MagicTime/Guids.json"); + text = text.Replace('}', ','); + text = text.Insert(text.Length, "\"" + name + "\": \"" + guid + "\"\n}"); + File.WriteAllText(UnityModManager.modsPath + @"/MagicTime/Guids.json", text); + Cleanup(); + Initializer(); + } } } \ No newline at end of file diff --git a/TTT/Bloodlines.cs b/TTT/Bloodlines.cs new file mode 100644 index 0000000..73a7d59 --- /dev/null +++ b/TTT/Bloodlines.cs @@ -0,0 +1,421 @@ +using HarmonyLib; +using Kingmaker.Blueprints; +using Kingmaker.Blueprints.Classes; +using Kingmaker.Blueprints.Classes.Prerequisites; +using Kingmaker.Blueprints.Classes.Selection; +using Kingmaker.Blueprints.Classes.Spells; +using Kingmaker.Designers.Mechanics.Facts; +using Kingmaker.UnitLogic.FactLogic; +using MagicTime.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace TTTBloodlinesFix +{ + internal static class Extensions + { + public static T[] RemoveFromArray(this T[] array, T value) + { + var list = array.ToList(); + return list.Remove(value) ? list.ToArray() : array; + } + + public static void RemoveComponents(this BlueprintScriptableObject obj) where T : BlueprintComponent + { + var compnents_to_remove = obj.GetComponents().ToArray(); + foreach (var c in compnents_to_remove) + { + obj.SetComponents(obj.ComponentsArray.RemoveFromArray(c)); + } + } + + public static void SetComponents(this BlueprintScriptableObject obj, params BlueprintComponent[] components) + { + // Fix names of components. Generally this doesn't matter, but if they have serialization state, + // then their name needs to be unique. + var names = new HashSet(); + foreach (var c in components) + { + if (string.IsNullOrEmpty(c.name)) + { + c.name = $"${c.GetType().Name}"; + } + if (!names.Add(c.name)) + { + String name; + for (int i = 0; !names.Add(name = $"{c.name}${i}"); i++) ; + c.name = name; + } + } + obj.ComponentsArray = components; + obj.OnEnable(); // To make sure components are fully initialized + } + + public static void SetComponents(this BlueprintScriptableObject obj, IEnumerable components) + { + SetComponents(obj, components.ToArray()); + } + } + + internal class Bloodlines + { + public static T GetBlueprint(string id) where T : SimpleBlueprint + { + var assetId = BlueprintGuid.Parse(id); + return GetBlueprint(assetId); + } + + public static T GetBlueprint(BlueprintGuid id) where T : SimpleBlueprint + { + SimpleBlueprint asset = ResourcesLibrary.TryGetBlueprint(id); + T value = asset as T; + return value; + } + + public static void PatchBloodlineRestrictions() + { + // Bloodline Requisite + var BloodlineRequisiteFeature = Helpers.CreateBlueprint("BloodlineRequisiteFeature", bp => { + bp.IsClassFeature = true; + bp.HideInUI = true; + bp.Ranks = 1; + bp.HideInCharacterSheetAndLevelUp = true; + bp.m_DisplayName = Helpers.CreateString("BloodlineRequisiteFeature.Name", "Bloodline"); + bp.m_Description = Helpers.CreateString("BloodlineRequisiteFeature.Description", "Bloodline Requisite Feature"); + }); + // Requisite Features + var AbyssalBloodlineRequisiteFeature = GetBlueprint("b09b58c7f8efff244a33269489abeac6"); + var ArcaneBloodlineRequisiteFeature = GetBlueprint("60d8632e96739a74dbac23dd078d205d"); + var CelestialBloodlineRequisiteFeature = GetBlueprint("6906eafd622304841bb45c2601aec7a7"); + var DraconicBlackBloodlineRequisiteFeature = GetBlueprint("d89fb8ce9152ffa4dacd69390f3d7721"); + var DraconicBlueBloodlineRequisiteFeature = GetBlueprint("64e1f27147b642448842ab8dcbaca03f"); + var DraconicBrassBloodlineRequisiteFeature = GetBlueprint("12bb1056a5f3f9f4b9facdb78b8d8914"); + var DraconicBronzeBloodlineRequisiteFeature = GetBlueprint("1d34d95ad4961e343b02db14690eb6d8"); + var DraconicCopperBloodlineRequisiteFeature = GetBlueprint("eef664d1e4318f64cb2304d1628d29ae"); + var DraconicGoldBloodlineRequisiteFeature = GetBlueprint("bef8d08ee3c20b246b404ce3ef948291"); + var DraconicGreenBloodlineRequisiteFeature = GetBlueprint("49115e2147cd32841baa34c305171daa"); + var DraconicRedBloodlineRequisiteFeature = GetBlueprint("9c5ed34089fedf54ba8d0f43565bcc91"); + var DraconicSilverBloodlineRequisiteFeature = GetBlueprint("01e7aab638d6a0b43bc4e9d5b49e68d9"); + var DraconicWhiteBloodlineRequisiteFeature = GetBlueprint("3867419bf47841b428333808dfdf4ae0"); + var ElementalAirBloodlineRequisiteFeature = GetBlueprint("65bc595a8a27acb48a8758c4d4caa338"); + var ElementalEarthBloodlineRequisiteFeature = GetBlueprint("96e15b7b08b65d44b90a3f0c3c7ed72b"); + var ElementalFireBloodlineRequisiteFeature = GetBlueprint("363737ca96b4a084b852688fd7430fa5"); + var ElementalWaterBloodlineRequisiteFeature = GetBlueprint("cfd48c078a14ee247aa7eabe263ad132"); + var FeyBloodlineRequisiteFeature = GetBlueprint("2c82cecdcafb5c741bf4901e689a4844"); + var InfernalBloodlineRequisiteFeature = GetBlueprint("e71d00a5aa1ed8f4591d021056a6dbe7"); + var SerpentineBloodlineRequisiteFeature = GetBlueprint("233d0e5bdd25a7f44a56c09c8b5041e7"); + var UndeadBloodlineRequisiteFeature = GetBlueprint("8a59e4af9f32950418260034c8b477fa"); + // DragonHeirScion Bloodlines + var DragonheirScionFeatureBlack = GetBlueprint("caee86c49e21d5845959ba5124f290cf"); + var DragonheirScionFeatureBlue = GetBlueprint("b09e72bd3325bf1419287fdbb989fc30"); + var DragonheirScionFeatureBrass = GetBlueprint("a6b1361e50599dd409905b4adaa05ee0"); + var DragonheirScionFeatureBronze = GetBlueprint("18f4e1b699f1e1f409c6bd44ce38aa0e"); + var DragonheirScionFeatureCopper = GetBlueprint("f6e0593ebfd33624f9b5d284b67927f7"); + var DragonheirScionFeatureGold = GetBlueprint("e8da00d4ee18fd246b624b776e1c0b64"); + var DragonheirScionFeatureGreen = GetBlueprint("190b2087470dcab49962af6d285ac6e2"); + var DragonheirScionFeatureRed = GetBlueprint("1a4be101e6761064e9bf4aeaf4863fff"); + var DragonheirScionFeatureSilver = GetBlueprint("be0d94ea05a931345981ac9bfd57840b"); + var DragonheirScionFeatureWhite = GetBlueprint("0db22c3cf99a03d409fb4fd9bd1ec53b"); + // Bloodrager Bloodlines + var BloodragerAbyssalBloodline = GetBlueprint("55d5bbf4b5ae1744ab26c71be98067f9"); + var BloodragerArcaneBloodline = GetBlueprint("aeff0a749e20ffe4b9e2846eae29c386"); + var BloodragerCelestialBloodline = GetBlueprint("05a141717bbce594a8a763c227f4ee2f"); + var BloodragerDragonBlackBloodline = GetBlueprint("3d030a2fed2b5cf45919fc1e40629a9e"); + var BloodragerDragonBlueBloodline = GetBlueprint("17bbb6790ca500d4190b978cab5c4dfc"); + var BloodragerDragonBrassBloodline = GetBlueprint("56e22cb1dde3f5a4297d45744ca19043"); + var BloodragerDragonBronzeBloodline = GetBlueprint("0cf41c61c8ac463478e5ba733fd26b40"); + var BloodragerDragonCopperBloodline = GetBlueprint("53f189dce67466c4f9e60610e5d1c4ba"); + var BloodragerDragonGoldBloodline = GetBlueprint("ffcb3d0a1a45d8048a691eda9f0219b9"); + var BloodragerDragonGreenBloodline = GetBlueprint("69b27eb6bd71ac747a2fac8399c27c3a"); + var BloodragerDragonRedBloodline = GetBlueprint("34209f220733fc444a039df1b1076b0b"); + var BloodragerDragonSilverBloodline = GetBlueprint("df182ceef2330d74b9bd7bfdb23d144b"); + var BloodragerDragonWhiteBloodline = GetBlueprint("c97cf6524b89d474989378d841c7cf5c"); + var BloodragerElementalAcidBloodline = GetBlueprint("dafe58c4f0785e94e93c0f07901f1343"); + var BloodragerElementalColdBloodline = GetBlueprint("5286db4f19f31eb44af99fb881c99517"); + var BloodragerElementalElectricityBloodline = GetBlueprint("777bc77d3dc652c488a20d1a7b0b95e5"); + var BloodragerElementalFireBloodline = GetBlueprint("12f7b4c5d603f3744b2b1def28c0a4fa"); + var BloodragerFeyBloodline = GetBlueprint("a6e8fae8a6d6e374a9af2893840be4ac"); + var BloodragerInfernalBloodline = GetBlueprint("9aef64b53406f114cb43f898a3aec01e"); + var BloodragerSerpentineBloodline = GetBlueprint("f5b06b67f04949f4c8d88fd3bbc0771e"); + var BloodragerUndeadBloodline = GetBlueprint("9f4ea90e9b9c27c48b541dbef184b3b7"); + // Sorceror Bloodlines + var BloodlineAbyssalProgression = GetBlueprint("d3a4cb7be97a6694290f0dcfbd147113"); + var BloodlineArcaneProgression = GetBlueprint("4d491cf9631f7e9429444f4aed629791"); + var BloodlineCelestialProgression = GetBlueprint("aa79c65fa0e11464d9d100b038c50796"); + var BloodlineDraconicBlackProgression = GetBlueprint("7bd143ead2d6c3a409aad6ee22effe34"); + var BloodlineDraconicBlueProgression = GetBlueprint("8a7f100c02d0b254d8f5f3affc8ef386"); + var BloodlineDraconicBrassProgression = GetBlueprint("5f9ecbee67db8364985e9d0500eb25f1"); + var BloodlineDraconicBronzeProgression = GetBlueprint("7e0f57d8d00464441974e303b84238ac"); + var BloodlineDraconicCopperProgression = GetBlueprint("b522759a265897b4f8f7a1a180a692e4"); + var BloodlineDraconicGoldProgression = GetBlueprint("6c67ef823db8d7d45bb0ef82f959743d"); + var BloodlineDraconicGreenProgression = GetBlueprint("7181be57d1cc3bc40bc4b552e4e4ce24"); + var BloodlineDraconicRedProgression = GetBlueprint("8c6e5b3cf12f71e43949f52c41ae70a8"); + var BloodlineDraconicSilverProgression = GetBlueprint("c7d2f393e6574874bb3fc728a69cc73a"); + var BloodlineDraconicWhiteProgression = GetBlueprint("b0f79497a0d1f4f4b8293e82c8f8fa0c"); + var BloodlineElementalAirProgression = GetBlueprint("cd788df497c6f10439c7025e87864ee4"); + var BloodlineElementalEarthProgression = GetBlueprint("32393034410fb2f4d9c8beaa5c8c8ab7"); + var BloodlineElementalFireProgression = GetBlueprint("17cc794d47408bc4986c55265475c06f"); + var BloodlineElementalWaterProgression = GetBlueprint("7c692e90592257a4e901d12ae6ec1e41"); + var BloodlineFeyProgression = GetBlueprint("e8445256abbdc45488c2d90373f7dae8"); + var BloodlineInfernalProgression = GetBlueprint("e76a774cacfb092498177e6ca706064d"); + var BloodlineSerpentineProgression = GetBlueprint("739c1e842bf77994baf963f4ad964379"); + var BloodlineUndeadProgression = GetBlueprint("a1a8bf61cadaa4143b2d4966f2d1142e"); + //Seeker Bloodlines + var SeekerBloodlineAbyssalProgression = GetBlueprint("17b752be1e0f4a34e8914df52eebeb75"); + var SeekerBloodlineArcaneProgression = GetBlueprint("562c5e4031d268244a39e01cc4b834bb"); + var SeekerBloodlineCelestialProgression = GetBlueprint("17ac9d771a944194a92ac15b5ff861c9"); + var SeekerBloodlineDraconicBlackProgression = GetBlueprint("cf448fafcd8452d4b830bcc9ca074189"); + var SeekerBloodlineDraconicBlueProgression = GetBlueprint("82f76646a7f96ed4cafa18480adc0b8c"); + var SeekerBloodlineDraconicBrassProgression = GetBlueprint("c355b8777a3bda7429d863367bda3851"); + var SeekerBloodlineDraconicBronzeProgression = GetBlueprint("468ecbdd58fbd6045a0a1888308031fe"); + var SeekerBloodlineDraconicCopperProgression = GetBlueprint("2ad98f60b29ae604da0297037054080c"); + var SeekerBloodlineDraconicGoldProgression = GetBlueprint("63c9d62a56e6921409a58de1ab9a9f9b"); + var SeekerBloodlineDraconicGreenProgression = GetBlueprint("6de526eeee72852448c5595f7a44a39d"); + var SeekerBloodlineDraconicRedProgression = GetBlueprint("d69a7785de1959c4497e4ff1e9490509"); + var SeekerBloodlineDraconicSilverProgression = GetBlueprint("efabab987569bf54abf23848c250e4d5"); + var SeekerBloodlineDraconicWhiteProgression = GetBlueprint("7572e8c020a6b8a46bde3ab3ad8c6f70"); + var SeekerBloodlineElementalAirProgression = GetBlueprint("940d34be432a1e543b0c0cbecd4ffc1d"); + var SeekerBloodlineElementalEarthProgression = GetBlueprint("1c1cdb13caa111d49bd82a7e1f320803"); + var SeekerBloodlineElementalFireProgression = GetBlueprint("b19f95964e4a18f4cb3e4e3101593f22"); + var SeekerBloodlineElementalWaterProgression = GetBlueprint("179a53407d1141142a91baace7e43325"); + var SeekerBloodlineFeyProgression = GetBlueprint("8e6dcc9095dacd042a644dd8c04ffac0"); + var SeekerBloodlineInfernalProgression = GetBlueprint("71f9b9d63f3683b4eb57e0025771932e"); + var SeekerBloodlineSerpentineProgression = GetBlueprint("59904bf6cc50a52489ebc648fb35f36f"); + var SeekerBloodlineUndeadProgression = GetBlueprint("5bc63fdb68b539f4fa500cfb2d0fe0f6"); + // Mutated Bloodlines + var EmpyrealBloodlineProgression = GetBlueprint("8a95d80a3162d274896d50c2f18bb6b1"); + var SageBloodlineProgression = GetBlueprint("7d990675841a7354c957689a6707c6c2"); + var SylvanBloodlineProgression = GetBlueprint("a46d4bd93601427409d034a997673ece"); + // Bloodline Selections + var BloodOfDragonsSelection = GetBlueprint("da48f9d7f697ae44ca891bfc50727988"); + var DragonheirDragonSelection = GetBlueprint("729411185291d704696e58316420fe38"); + var BloodragerBloodlineSelection = GetBlueprint("62b33ac8ceb18dd47ad4c8f06849bc01"); + var SorcererBloodlineSelection = GetBlueprint("24bef8d1bee12274686f6da6ccbc8914"); + var SeekerBloodlineSelection = GetBlueprint("7bda7cdb0ccda664c9eb8978cf512dbc"); + var NineTailedHeirBloodlineSelection = GetBlueprint("7c813fb495d74246918a690ba86f9c86"); + var EldritchScionBloodlineSelection = GetBlueprint("94c29f69cdc34594a6a4677441ed7375"); + + // Fix Mutated Bloodlines + AddRequisiteFeature(EmpyrealBloodlineProgression, BloodlineRequisiteFeature, CelestialBloodlineRequisiteFeature); + AddRequisiteFeature(SageBloodlineProgression, BloodlineRequisiteFeature, ArcaneBloodlineRequisiteFeature); + AddRequisiteFeature(SylvanBloodlineProgression, BloodlineRequisiteFeature, FeyBloodlineRequisiteFeature); + // Fix Sorcerer Bloodlines + FixBloodlineProgressionPrerequisites(BloodlineAbyssalProgression, AbyssalBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodlineArcaneProgression, ArcaneBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodlineCelestialProgression, CelestialBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodlineDraconicBlackProgression, DraconicBlackBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodlineDraconicBlueProgression, DraconicBlueBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodlineDraconicBrassProgression, DraconicBrassBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodlineDraconicBronzeProgression, DraconicBronzeBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodlineDraconicCopperProgression, DraconicCopperBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodlineDraconicGoldProgression, DraconicGoldBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodlineDraconicGreenProgression, DraconicGreenBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodlineDraconicRedProgression, DraconicRedBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodlineDraconicSilverProgression, DraconicSilverBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodlineDraconicWhiteProgression, DraconicWhiteBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodlineElementalAirProgression, ElementalAirBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodlineElementalEarthProgression, ElementalEarthBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodlineElementalFireProgression, ElementalFireBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodlineElementalWaterProgression, ElementalWaterBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodlineFeyProgression, FeyBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodlineInfernalProgression, InfernalBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodlineSerpentineProgression, SerpentineBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodlineUndeadProgression, UndeadBloodlineRequisiteFeature); + // Fix Seeker Bloodlines + FixBloodlineProgressionPrerequisites(SeekerBloodlineAbyssalProgression, AbyssalBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(SeekerBloodlineArcaneProgression, ArcaneBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(SeekerBloodlineCelestialProgression, CelestialBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(SeekerBloodlineDraconicBlackProgression, DraconicBlackBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(SeekerBloodlineDraconicBlueProgression, DraconicBlueBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(SeekerBloodlineDraconicBrassProgression, DraconicBrassBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(SeekerBloodlineDraconicBronzeProgression, DraconicBronzeBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(SeekerBloodlineDraconicCopperProgression, DraconicCopperBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(SeekerBloodlineDraconicGoldProgression, DraconicGoldBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(SeekerBloodlineDraconicGreenProgression, DraconicGreenBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(SeekerBloodlineDraconicRedProgression, DraconicRedBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(SeekerBloodlineDraconicSilverProgression, DraconicSilverBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(SeekerBloodlineDraconicWhiteProgression, DraconicWhiteBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(SeekerBloodlineElementalAirProgression, ElementalAirBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(SeekerBloodlineElementalEarthProgression, ElementalEarthBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(SeekerBloodlineElementalFireProgression, ElementalFireBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(SeekerBloodlineElementalWaterProgression, ElementalWaterBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(SeekerBloodlineFeyProgression, FeyBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(SeekerBloodlineInfernalProgression, InfernalBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(SeekerBloodlineSerpentineProgression, SerpentineBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(SeekerBloodlineUndeadProgression, UndeadBloodlineRequisiteFeature); + // Fix Bloodrager Bloodlines + FixBloodlineProgressionPrerequisites(BloodragerAbyssalBloodline, AbyssalBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodragerArcaneBloodline, ArcaneBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodragerCelestialBloodline, CelestialBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodragerDragonBlackBloodline, DraconicBlackBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodragerDragonBlueBloodline, DraconicBlueBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodragerDragonBrassBloodline, DraconicBrassBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodragerDragonBronzeBloodline, DraconicBronzeBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodragerDragonCopperBloodline, DraconicCopperBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodragerDragonGoldBloodline, DraconicGoldBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodragerDragonGreenBloodline, DraconicGreenBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodragerDragonRedBloodline, DraconicRedBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodragerDragonSilverBloodline, DraconicSilverBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodragerDragonWhiteBloodline, DraconicWhiteBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodragerElementalElectricityBloodline, ElementalAirBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodragerElementalAcidBloodline, ElementalEarthBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodragerElementalFireBloodline, ElementalFireBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodragerElementalColdBloodline, ElementalWaterBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodragerFeyBloodline, FeyBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodragerInfernalBloodline, InfernalBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodragerSerpentineBloodline, SerpentineBloodlineRequisiteFeature); + FixBloodlineProgressionPrerequisites(BloodragerUndeadBloodline, UndeadBloodlineRequisiteFeature); + // Fix Dragonheir Scion Bloodlines + FixBloodlineFeaturePrerequisites(DragonheirScionFeatureBlack, DraconicBlackBloodlineRequisiteFeature); + FixBloodlineFeaturePrerequisites(DragonheirScionFeatureBlue, DraconicBlueBloodlineRequisiteFeature); + FixBloodlineFeaturePrerequisites(DragonheirScionFeatureBrass, DraconicBrassBloodlineRequisiteFeature); + FixBloodlineFeaturePrerequisites(DragonheirScionFeatureBronze, DraconicBronzeBloodlineRequisiteFeature); + FixBloodlineFeaturePrerequisites(DragonheirScionFeatureCopper, DraconicCopperBloodlineRequisiteFeature); + FixBloodlineFeaturePrerequisites(DragonheirScionFeatureGold, DraconicGoldBloodlineRequisiteFeature); + FixBloodlineFeaturePrerequisites(DragonheirScionFeatureGreen, DraconicGreenBloodlineRequisiteFeature); + FixBloodlineFeaturePrerequisites(DragonheirScionFeatureRed, DraconicRedBloodlineRequisiteFeature); + FixBloodlineFeaturePrerequisites(DragonheirScionFeatureSilver, DraconicSilverBloodlineRequisiteFeature); + FixBloodlineFeaturePrerequisites(DragonheirScionFeatureWhite, DraconicWhiteBloodlineRequisiteFeature); + // Patch Bloodline Class Prerequisites + FixSorcererArchetypes(); + FixDragonheirScion(); + + void FixSorcererArchetypes() + { + var EmpyrealSorcererArchetype = GetBlueprint("aa00d945f7cf6c34c909a29a25f2df38"); + var SageSorcererArchetype = GetBlueprint("00b990c8be2117e45ae6514ee4ef561c"); + var SylvanSorcererArchetype = GetBlueprint("711d5024ecc75f346b9cda609c3a1f83"); + var SeekerSorcererArchetype = GetBlueprint("7229db6fc0b07af4180e783eed43c4d9"); + + FixMutatedPrerequisites(EmpyrealSorcererArchetype, CelestialBloodlineRequisiteFeature); + FixMutatedPrerequisites(SageSorcererArchetype, ArcaneBloodlineRequisiteFeature); + FixMutatedPrerequisites(SylvanSorcererArchetype, FeyBloodlineRequisiteFeature); + SeekerSorcererArchetype.CreateComponents(Helpers.Create(c => + { + c.Group = Prerequisite.GroupType.All; + c.m_Feature = BloodOfDragonsSelection.ToReference(); + })); + + void FixMutatedPrerequisites(BlueprintArchetype archetype, BlueprintFeature requisite) + { + var noBloodline = Helpers.Create(c => + { + c.Group = Prerequisite.GroupType.Any; + c.m_Feature = BloodlineRequisiteFeature.ToReference(); + }); + var requisiteFeature = Helpers.Create(c => + { + c.Group = Prerequisite.GroupType.Any; + c.m_Feature = requisite.ToReference(); + }); + archetype.CreateComponents(noBloodline, requisiteFeature); + }; + } + void FixDragonheirScion() + { + var DragonheirScionArchetype = GetBlueprint("8dff97413c63c1147be8a5ca229abefc"); + var DragonDiscipleClass = GetBlueprint("72051275b1dbb2d42ba9118237794f7c"); + var DragonheirAcidProgression = GetBlueprint("f09074860cc87fd4ebf6bf69ddd20d10"); + var DragonheirProgressionCold = GetBlueprint("ff7eb5969525b5b41b2c68328bc9bb7c"); + var DragonheirProgressionElectricity = GetBlueprint("07f6ba0f63d8d414f92b5d0a559455e1"); + var DragonheirProgressionFire = GetBlueprint("8e30b4dab152d4549bf9c0dbf901aadf"); + + AddDragonheirScionPrerequisites(DragonheirScionArchetype); + + EnableDragonDisicpleAdvancement(DragonheirScionFeatureBlack); + EnableDragonDisicpleAdvancement(DragonheirScionFeatureBlue); + EnableDragonDisicpleAdvancement(DragonheirScionFeatureBrass); + EnableDragonDisicpleAdvancement(DragonheirScionFeatureBronze); + EnableDragonDisicpleAdvancement(DragonheirScionFeatureCopper); + EnableDragonDisicpleAdvancement(DragonheirScionFeatureGold); + EnableDragonDisicpleAdvancement(DragonheirScionFeatureGreen); + EnableDragonDisicpleAdvancement(DragonheirScionFeatureRed); + EnableDragonDisicpleAdvancement(DragonheirScionFeatureSilver); + EnableDragonDisicpleAdvancement(DragonheirScionFeatureWhite); + + EnableDragonDisicpleProgression(DragonheirAcidProgression); + EnableDragonDisicpleProgression(DragonheirProgressionCold); + EnableDragonDisicpleProgression(DragonheirProgressionElectricity); + EnableDragonDisicpleProgression(DragonheirProgressionFire); + + void AddDragonheirScionPrerequisites(BlueprintArchetype archetype) + { + var noBloodline = Helpers.Create(c => + { + c.Group = Prerequisite.GroupType.Any; + c.m_Feature = BloodlineRequisiteFeature.ToReference(); + }); + var draconicBloodline = Helpers.Create(c => + { + c.Group = Prerequisite.GroupType.Any; + c.m_Features = DragonDiscipleClass.GetComponent().m_Features; + }); + archetype.CreateComponents(noBloodline, draconicBloodline); + } + void EnableDragonDisicpleAdvancement(BlueprintFeature feature) + { + feature.GetComponent().m_AdditionalClasses = new BlueprintCharacterClassReference[] { + DragonDiscipleClass.ToReference() + }; + } + void EnableDragonDisicpleProgression(BlueprintProgression feature) + { + feature.m_Classes = feature.m_Classes.AddItem(new BlueprintProgression.ClassWithLevel + { + m_Class = DragonDiscipleClass.ToReference() + }).ToArray(); + } + } + void AddRequisiteFeature(BlueprintProgression bloodline, params BlueprintFeature[] requisites) + { + var levelOne = bloodline.LevelEntries.Where(entry => entry.Level == 1).First(); + foreach (var requisite in requisites) + { + if (!levelOne.m_Features.Contains(requisite.ToReference())) + { + levelOne.m_Features.Add(requisite.ToReference()); + } + } + } + void FixBloodlineProgressionPrerequisites(BlueprintProgression bloodline, BlueprintFeature requisite) + { + var noBloodline = Helpers.Create(c => + { + c.Group = Prerequisite.GroupType.Any; + c.m_Feature = BloodlineRequisiteFeature.ToReference(); + }); + var requisiteFeature = Helpers.Create(c => + { + c.Group = Prerequisite.GroupType.Any; + c.m_Feature = requisite.ToReference(); + }); + AddRequisiteFeature(bloodline, BloodlineRequisiteFeature, requisite); + bloodline.RemoveComponents(); + bloodline.CreateComponents(noBloodline, requisiteFeature); + } + void FixBloodlineFeaturePrerequisites(BlueprintFeature bloodline, BlueprintFeature requisite) + { + var noBloodline = Helpers.Create(c => + { + c.Group = Prerequisite.GroupType.Any; + c.m_Feature = BloodlineRequisiteFeature.ToReference(); + }); + var requisiteFeature = Helpers.Create(c => + { + c.Group = Prerequisite.GroupType.Any; + c.m_Feature = requisite.ToReference(); + }); + var addFacts = Helpers.Create(c => + { + c.m_Facts = new BlueprintUnitFactReference[] { + BloodlineRequisiteFeature.ToReference(), + requisite.ToReference() + }; + }); + bloodline.RemoveComponents(); + bloodline.CreateComponents(requisiteFeature, noBloodline, addFacts); + } + } + } +} \ No newline at end of file diff --git a/Utilities/BlueprintDatabase.json b/Utilities/BlueprintDatabase.json index 2b721fc..ea41b10 100644 --- a/Utilities/BlueprintDatabase.json +++ b/Utilities/BlueprintDatabase.json @@ -1,21 +1,32 @@ { "Alchemist Spells": "f60d0cd93edc65c42ad31e34a905fb2f", "Alertness": "1c04fe9a13a22bc499ffac03e6f79153", + "Arcanist Capstone": "261270d064148224fb982590b7a65414", + "Arcanist Class": "52dbfd8505e22f84fad8d702611f60b7", + "Arcanist Exploits": "b8bf3d5023f2d8c428fdf6438cecaea7", "Assassin Poison": "46660d0da7797124aa221818778edc9d", "Barbarian DR": "cffb5cddefab30140ac133699d52a8f8", + "Bloodline Ascendance": "ce85aee1726900641ab53ede61ac5c19", + "Bloodline Selection Blood": "62b33ac8ceb18dd47ad4c8f06849bc01", + "Bloodline Selection Dragonheir": "729411185291d704696e58316420fe38", + "Bloodline Selection Sorc": "24bef8d1bee12274686f6da6ccbc8914", "Cleric Class": "67819271767a9dd4fbfd4ae700befea0", "Cleric Proficiencies": "8c971173613282844888dc20d572cfc9", "Cleric Spellbook": "4673d19a0cf2fab4f885cc4d1353da33", "Combat Reflexes": "0f8939ae6f220984e8fb568abbdfba95", "Combat Trick": "c5158a6622d0b694a99efb1d0025d2c1", "Corrupted Blood Buff": "1419d2e2eee432849b0a596e82b9e0a2", + "Curse Selection": "b0a5118b4fb793241bc7042464b23fab", "Diehard": "86669ce8759f9d7478565db69b8c19ad", + "Dragon Disciple Spellbook Selection": "8c1ba14c0b6dcdb439c56341385ee474", "Druid Spells": "f60d0cd93edc65c42ad31e34a905fb2f", "Eldritch Scion Arcana Selection": "d4b54d9db4932454ab2899f931c2042c", "Extra Mythic Ability Selection": "8a6a511c55e67d04db328cc49aaad2b8", "Fatigued Buff": "e6f2fc5d73d88064583cb828801212f4", + "Favorite Metamagic": "503fb196aa222b24cb6cfdc9a284e838", "Feat Selection": "247a4068296e8be42890143f451b4b45", "Fighter Feat Selection": "41c8486641f7d6d4283ca9dae4147a9f", + "Foresight Ability": "1f01a098d737ec6419aedc4e7ad61fdd", "Hexcrafter Arcana Selection": "ad6b9cecb5286d841a66e23cea3ef7bf", "Human Skilled": "3adf9274a210b164cb68f472dc1e4544", "Magus Arcana Selection": "e9dc4dfc73eaaf94aae27e0ed6cc9ada", @@ -53,15 +64,40 @@ "Prof Shield": "cb8686e7357a68c42bdd9d4e65334633", "Prof Staff": "aed4f88b52ae0fb468895f90da854ad4", "Revelation Selection": "60008a10ad7ad6543b1f63016741a5d2", + "Scribe Scrolls": "a8a385bf53ee3454593ce9054375a2ec", "Shield of Law": "73e7728808865094b8892613ddfaf7f5", "Sin Mage": "55a8ce15e30d71547a44c69bb2e8a84f", "Sin Magic Lust": "e1ebc61a71c55054991863a5f6f6d2c2", "Sin Magic Pride": "aa271e69902044b47a8e62c4e58a9dcb", + "Sorcerer Class": "b3a505fb61437dc4097f43c3f8f9a4cf", + "Sorc Blood Abyssal": "d3a4cb7be97a6694290f0dcfbd147113", + "Sorc Blood Arcane": "4d491cf9631f7e9429444f4aed629791", + "Sorc Blood Celestial": "aa79c65fa0e11464d9d100b038c50796", + "Sorc Blood Draconic Black": "7bd143ead2d6c3a409aad6ee22effe34", + "Sorc Blood Draconic Blue": "8a7f100c02d0b254d8f5f3affc8ef386", + "Sorc Blood Draconic Brass": "5f9ecbee67db8364985e9d0500eb25f1", + "Sorc Blood Draconic Bronze": "7e0f57d8d00464441974e303b84238ac", + "Sorc Blood Draconic Copper": "b522759a265897b4f8f7a1a180a692e4", + "Sorc Blood Draconic Gold": "6c67ef823db8d7d45bb0ef82f959743d", + "Sorc Blood Draconic Green": "7181be57d1cc3bc40bc4b552e4e4ce24", + "Sorc Blood Draconic Red": "8c6e5b3cf12f71e43949f52c41ae70a8", + "Sorc Blood Draconic Silver": "c7d2f393e6574874bb3fc728a69cc73a", + "Sorc Blood Draconic White": "b0f79497a0d1f4f4b8293e82c8f8fa0c", + "Sorc Blood Elemental Air": "cd788df497c6f10439c7025e87864ee4", + "Sorc Blood Elemental Earth": "32393034410fb2f4d9c8beaa5c8c8ab7", + "Sorc Blood Elemental Fire": "17cc794d47408bc4986c55265475c06f", + "Sorc Blood Elemental Water": "7c692e90592257a4e901d12ae6ec1e41", + "Sorc Blood Fey": "e8445256abbdc45488c2d90373f7dae8", + "Sorc Blood Infernal": "e76a774cacfb092498177e6ca706064d", + "Sorc Blood Serpentine": "739c1e842bf77994baf963f4ad964379", + "Sorc Blood Undead": "a1a8bf61cadaa4143b2d4966f2d1142e", "Subtype Demodand": "0d112671041420340b5ce7e9ab7b4320", "Subtype Demon": "dc960a234d365cb4f905bdc5937e623a", "Targeted Bomb Admixture": "24afb2c948c731440a3aaf5411904c89", "Warrior Priest": "b9bee4e4e15573546b76a8d942ce914b", + "Witch Patron Selection": "381cf4c890815d049a4420c6f31d063f", "Wizard Class": "ba34257984f4c41408ce1dc2004e342e", "Wizard Feat Selection": "8c3102c2ff3b69444b139a98521a4899", + "Wizard Spellbook": "5a38c9ac8607890409fcb8f6342da6f4", "Wizard Spells": "ba0401fdeb4062f40a7aa95b6f07fe89" } \ No newline at end of file diff --git a/Utilities/DB.cs b/Utilities/DB.cs index a43f842..7daa52e 100644 --- a/Utilities/DB.cs +++ b/Utilities/DB.cs @@ -46,6 +46,11 @@ public static BlueprintFeature GetFeature(string id) return GetBP(id); } + public static BlueprintProgression GetProgression(string id) + { + return GetBP(id); + } + public static BlueprintFeatureSelection GetSelection(string id) { return GetBP(id); diff --git a/Utilities/Extensions.cs b/Utilities/Extensions.cs index 825f2a1..b0138b7 100644 --- a/Utilities/Extensions.cs +++ b/Utilities/Extensions.cs @@ -7,6 +7,7 @@ using Kingmaker.Blueprints.Items.Armors; using Kingmaker.Designers.Mechanics.Buffs; using Kingmaker.Designers.Mechanics.Facts; +using Kingmaker.Designers.Mechanics.Recommendations; using Kingmaker.ElementsSystem; using Kingmaker.EntitySystem.Stats; using Kingmaker.Enums; @@ -18,6 +19,7 @@ using Kingmaker.UnitLogic.Abilities.Components; using Kingmaker.UnitLogic.Abilities.Components.Base; using Kingmaker.UnitLogic.Abilities.Components.CasterCheckers; +using Kingmaker.UnitLogic.ActivatableAbilities; using Kingmaker.UnitLogic.Alignments; using Kingmaker.UnitLogic.Buffs.Blueprints; using Kingmaker.UnitLogic.FactLogic; @@ -26,6 +28,7 @@ using Kingmaker.UnitLogic.Mechanics.Components; using Kingmaker.Utility; using Kingmaker.Visual.Animation.Kingmaker.Actions; +using System; using System.Collections.Generic; using System.Linq; @@ -33,6 +36,14 @@ namespace MagicTime.Utilities { internal static class Extensions { + public static void AddFeatures(this BlueprintFeatureSelection selection, params BlueprintFeatureReference[] features) + { + selection.m_AllFeatures.Union(features); + selection.m_Features.Union(features); + selection.m_AllFeatures = selection.m_AllFeatures.OrderBy(feature => feature.Get().Name).ToArray(); + selection.m_Features = selection.m_Features.OrderBy(feature => feature.Get().Name).ToArray(); + } + internal static void AddArchetype(this BlueprintCharacterClass bp, BlueprintArchetype archetype) { var result = bp.m_Archetypes.ToList(); @@ -76,6 +87,30 @@ internal static void CreateAbilityResourceLogic(this BlueprintAbility bp, Bluepr bp.CreateComponents(result); } + internal static void CreateAbilityResourceLogic(this BlueprintActivatableAbility bp, BlueprintAbilityResource resource, + ActivatableAbilityResourceLogic.ResourceSpendType spend_type) + { + var result = new ActivatableAbilityResourceLogic(); + result.m_RequiredResource = resource.ToReference(); + result.SpendType = spend_type; + bp.CreateComponents(result); + } + + internal static void CreateAbilityTargetAround(this BlueprintAbility bp, TargetType target_type, float radius, float speed) + { + var result = new AbilityTargetsAround(); + result.m_TargetType = target_type; + result.m_Radius = new Feet(); + result.m_Radius.m_Value = radius; + result.m_SpreadSpeed = new Feet(); + result.m_SpreadSpeed.m_Value = speed; + result.m_Condition = new ConditionsChecker(); + result.m_Condition.Conditions = new Condition[0]; + result.m_Condition.Operation = Operation.And; + result.m_IncludeDead = false; + bp.CreateComponents(result); + } + internal static void CreateAbilityUseTrigger(this BlueprintScriptableObject bp, bool from_spellbook, BlueprintSpellbook spellbook, bool after_cast, params ContextAction[] actions) { @@ -146,6 +181,17 @@ internal static void CreateAddFacts(this BlueprintFeature bp, params BlueprintUn bp.CreateComponents(result); } + internal static void CreateAddSpellKnown(this BlueprintFeature bp, BlueprintCharacterClass cclass, BlueprintArchetype arch, + BlueprintAbility spell, int level) + { + var result = new AddKnownSpell(); + result.m_CharacterClass = cclass.ToReference(); + result.m_Archetype = arch.ToReference(); + result.m_Spell = spell.ToReference(); + result.SpellLevel = level; + bp.CreateComponents(result); + } + internal static void CreateAddStatBonus(this BlueprintScriptableObject bp, int value, ModifierDescriptor descriptor, params StatType[] stats) { foreach (var stat in stats) @@ -181,6 +227,7 @@ internal static void CreateAddStatToCMB(this BlueprintScriptableObject bp, StatT result.Descriptor = ModifierDescriptor.UntypedStackable; bp.CreateComponents(result); } + internal static void CreateAlignmentRestriction(this BlueprintScriptableObject bp, AlignmentMaskType mask) { var result = new PrerequisiteAlignment(); @@ -219,6 +266,14 @@ internal static void CreateBuffExtraEffects(this BlueprintScriptableObject bp, B bp.CreateComponents(result); } + internal static void CreateBuffSummons(this BlueprintScriptableObject bp, BlueprintBuff buff) + { + var result = new OnSpawnBuff(); + result.IsInfinity = true; + result.m_buff = buff.ToReference(); + bp.CreateComponents(result); + } + internal static void CreateClassLevelRestriction(this BlueprintScriptableObject bp, BlueprintCharacterClass cclass, int level) { var result = new PrerequisiteClassLevel(); @@ -261,13 +316,24 @@ internal static void CreateConcentrationBonus(this BlueprintScriptableObject bp, internal static void CreateContextRankConfig(this BlueprintScriptableObject bp, ContextRankBaseValueType value_type, ContextRankProgression progression, int step_level, AbilityRankType type = AbilityRankType.Default, - BlueprintCharacterClass c_class = null) + BlueprintCharacterClass c_class = null, bool custom_progression = false, int base_value = 0, int progression_value = 0) { var result = new ContextRankConfig(); result.m_BaseValueType = value_type; result.m_Progression = progression; result.m_Type = type; result.m_StepLevel = step_level; + if (custom_progression) + { + result.m_CustomProgression = new ContextRankConfig.CustomProgressionItem[] + { + new ContextRankConfig.CustomProgressionItem() + { + BaseValue = base_value, + ProgressionValue = progression_value + } + }; + } if (c_class != null) { result.m_Class = new BlueprintCharacterClassReference[] { c_class.ToReference() }; @@ -276,10 +342,12 @@ internal static void CreateContextRankConfig(this BlueprintScriptableObject bp, } internal static void CreateContextRankConfigClassLevel(this BlueprintScriptableObject bp, AbilityRankType type, - BlueprintCharacterClass c_class, ContextRankProgression progression = ContextRankProgression.AsIs) + BlueprintCharacterClass c_class, ContextRankProgression progression = ContextRankProgression.AsIs, int base_value = 0, + int progression_value = 0) { CreateContextRankConfig(bp, - ContextRankBaseValueType.ClassLevel, progression, 1, type, c_class); + ContextRankBaseValueType.ClassLevel, progression, 1, type, c_class, progression == ContextRankProgression.Custom, base_value, + progression_value); } internal static void CreateContextRankConfigMythicRank(this BlueprintScriptableObject bp, ContextRankProgression progression, @@ -288,6 +356,48 @@ internal static void CreateContextRankConfigMythicRank(this BlueprintScriptableO CreateContextRankConfig(bp, ContextRankBaseValueType.MythicLevel, progression, 1, type); } + internal static void CreateContextRankConfigStat(this BlueprintScriptableObject bp, AbilityRankType rank_type, StatType stat) + { + var result = new ContextRankConfig(); + result.m_BaseValueType = ContextRankBaseValueType.StatBonus; + result.m_Stat = stat; + result.m_Type = rank_type; + bp.CreateComponents(result); + } + + internal static void CreateDerivativeStat(this BlueprintFeature bp, StatType stat, StatType derivative) + { + var comp1 = new DerivativeStatBonus(); + comp1.BaseStat = stat; + comp1.DerivativeStat = derivative; + var comp2 = new RecalculateOnStatChange(); + comp2.Stat = stat; + bp.CreateComponents(comp1, comp2); + } + + internal static void CreateDisableAttack(this BlueprintBuff bp) + { + var result = new DisableAttackType(); + result.m_AttackType = Kingmaker.RuleSystem.AttackTypeFlag.Melee | Kingmaker.RuleSystem.AttackTypeFlag.Ranged; + bp.CreateComponents(result); + } + + internal static void CreateDispelBonus(this BlueprintScriptableObject bp, ContextValueType value_type, AbilityRankType value_rank) + { + var result = new DispelCasterLevelCheckBonus(); + result.Value = new ContextValue(); + result.Value.ValueType = value_type; + result.Value.ValueRank = value_rank; + bp.CreateComponents(result); + } + + internal static void CreateEnergyImmunity(this BlueprintScriptableObject bp, DamageEnergyType elem) + { + var result = new AddEnergyImmunity(); + result.Type = elem; + bp.CreateComponents(result); + } + internal static void CreateFastHealing(this BlueprintBuff bp, int amount) { var result = new HealOverTime(); @@ -296,20 +406,22 @@ internal static void CreateFastHealing(this BlueprintBuff bp, int amount) } internal static void CreateFeatureRestriction(this BlueprintScriptableObject bp, BlueprintFeature feature, - Prerequisite.GroupType group = Prerequisite.GroupType.All) + Prerequisite.GroupType group = Prerequisite.GroupType.All, bool hidden = false) { var result = new PrerequisiteFeature(); result.m_Feature = feature.ToReference(); result.Group = group; + result.HideInUI = hidden; bp.CreateComponents(result); } internal static void CreateFeatureRestrictionInv(this BlueprintScriptableObject bp, BlueprintFeature feature, - Prerequisite.GroupType group = Prerequisite.GroupType.Any) + Prerequisite.GroupType group = Prerequisite.GroupType.Any, bool hidden = false) { var result = new PrerequisiteNoFeature(); result.m_Feature = feature.ToReference(); result.Group = group; + result.HideInUI = hidden; bp.CreateComponents(result); } @@ -317,11 +429,21 @@ internal static void CreateFeatureTags(this BlueprintFeature bp, FeatureTag tags { var result = new FeatureTagsComponent(); result.FeatureTags = tags; + bp.CreateComponents(result); } - internal static void CreateGenericComponent(this BlueprintScriptableObject bp) where T : BlueprintComponent, new() + internal static void CreateGenericComponent(this BlueprintScriptableObject bp, Action init = null) where T : BlueprintComponent, new() { var result = new T(); + init?.Invoke(result); + bp.CreateComponents(result); + } + + internal static void CreateIncreaseResource(this BlueprintFeature bp, BlueprintAbilityResource res, int amount) + { + var result = new IncreaseResourceAmount(); + result.m_Resource = res.ToReference(); + result.Value = amount; bp.CreateComponents(result); } @@ -343,6 +465,33 @@ internal static void CreateIncreaseSpellbookDC(this BlueprintScriptableObject bp bp.CreateComponents(result); } + internal static void CreateLearnSpells(this BlueprintFeature bp, BlueprintCharacterClass cclass, params BlueprintAbility[] spells) + { + var result = new LearnSpells(); + result.m_CharacterClass = cclass.ToReference(); + var list = new List(); + foreach (var spell in spells) + { + list.Add(spell.ToReference()); + } + result.m_Spells = list.ToArray(); + bp.CreateComponents(result); + } + + internal static void CreateModifyD20(this BlueprintScriptableObject bp, RuleType rule, int rolls, bool take_best, ModifierDescriptor desc, + ContextValueType value_type, int value) + { + var result = new ModifyD20(); + result.Rule = rule; + result.RollsAmount = rolls; + result.TakeBest = take_best; + result.BonusDescriptor = desc; + result.Bonus = new ContextValue(); + result.Bonus.ValueType = value_type; + result.Bonus.Value = value; + bp.CreateComponents(result); + } + internal static void CreateOutgoingDamageBonus(this BlueprintScriptableObject bp, float factor, DamageEnergyType dtype = 0, BlueprintUnitFact checked_fact = null, int reason = 0) { @@ -360,12 +509,29 @@ internal static void CreateOutgoingDamageBonus(this BlueprintScriptableObject bp bp.CreateComponents(result); } + internal static void CreateProjectile(this BlueprintAbility bp, bool attack_roll, AbilityProjectileType type, string key) + { + var result = new AbilityDeliverProjectile(); + result.NeedAttackRoll = attack_roll; + result.Type = type; + var projectile = DB.GetBP(key); + result.m_Projectiles = new BlueprintProjectileReference[] { projectile.ToReference() }; + bp.CreateComponents(result); + } + + internal static void CreateRecommendationRequiresSpellbook(this BlueprintFeature bp) + { + var result = new RecommendationRequiresSpellbook(); + bp.CreateComponents(result); + } + internal static void CreateRemoveFact(this BlueprintFeature bp, BlueprintFact fact) { var result = new RemoveFeatureOnApply(); result.m_Feature = fact.ToReference(); bp.CreateComponents(result); } + internal static void CreateResistEnergyFlat(this BlueprintScriptableObject bp, DamageEnergyType elem, int value) { var result = new ResistEnergy(); @@ -385,6 +551,21 @@ internal static void CreateRestTrigger(this BlueprintFeature bp, params ContextA bp.CreateComponents(result); } + internal static void CreateSavingThrowTrigger(this BlueprintBuff bp, params ContextAction[] actions) + { + var result = new AddInitiatorSavingThrowTrigger(); + result.Action = Helpers.CreateActionList(actions); + bp.CreateComponents(result); + } + + internal static void CreateSizeChange(this BlueprintBuff bp, Size size) + { + var result = new ChangeUnitSize(); + result.m_Type = ChangeUnitSize.ChangeType.Value; + result.Size = size; + bp.CreateComponents(result); + } + internal static void CreateSpawnFx(this BlueprintAbility bp, string asset_id, AbilitySpawnFxAnchor anchor = AbilitySpawnFxAnchor.Caster, AbilitySpawnFxTime time = AbilitySpawnFxTime.OnApplyEffect) { @@ -396,15 +577,67 @@ internal static void CreateSpawnFx(this BlueprintAbility bp, string asset_id, Ab bp.CreateComponents(result); } - internal static void CreateSpellPenBonus(this BlueprintScriptableObject bp, int bonus, bool check_fact = false) + internal static void CreateSpellComponent(this BlueprintAbility bp, SpellSchool school) + { + var result = new SpellComponent(); + result.School = school; + bp.CreateComponents(result); + } + + internal static void CreateSpellDescriptor(this BlueprintAbility bp, SpellDescriptor descriptor) + { + var result = new SpellDescriptorComponent(); + result.Descriptor = descriptor; + bp.CreateComponents(result); + } + + internal static void CreateSpellListComponent(this BlueprintAbility bp, int level, BlueprintSpellList spell_list) + { + var result = new SpellListComponent(); + result.SpellLevel = level; + result.m_SpellList = spell_list.ToReference(); + bp.CreateComponents(result); + } + + internal static void CreateSpellPenBonus(this BlueprintScriptableObject bp, int bonus, bool check_fact, ContextValueType type, + AbilityRankType rank) { var result = new SpellPenetrationBonus(); - result.Value = bonus; + result.Value = new ContextValue(); + result.Value.ValueType = type; + result.Value.ValueRank = rank; + result.Value.Value = bonus; result.CheckFact = check_fact; result.Descriptor = ModifierDescriptor.UntypedStackable; bp.CreateComponents(result); } + internal static void CreateSpellSlotOverride(this BlueprintScriptableObject bp, BlueprintAbilityResource res, int mult) + { + var result = new AbilityResourceOverride(); + result.m_AbilityResource = res.ToReference(); + result.m_SaveSpellSlot = true; + result.m_AdditionalCost = new ContextValue(); + result.m_AdditionalCost.ValueType = ContextValueType.Simple; + result.m_AdditionalCost.ValueShared = AbilitySharedValue.Damage; + result.m_AdditionalCost.Value = 0; + result.m_AdditionalCost.ValueRank = AbilityRankType.Default; + result.m_LevelMultiplier = new ContextValue(); + result.m_LevelMultiplier.ValueType = ContextValueType.Simple; + result.m_LevelMultiplier.ValueShared = AbilitySharedValue.Damage; + result.m_LevelMultiplier.Value = mult; + result.m_LevelMultiplier.ValueRank = AbilityRankType.Default; + bp.CreateComponents(result); + } + + internal static void CreateStatRestriction(this BlueprintScriptableObject bp, StatType stat, int value) + { + var result = new PrerequisiteStatValue(); + result.Stat = stat; + result.Value = value; + bp.CreateComponents(result); + } + internal static void CreateTransferShieldACToTouch(this BlueprintFeature bp, int max) { var result = new TransferDescriptorBonusToTouchAC(); @@ -427,6 +660,13 @@ internal static void CreateVariants(this BlueprintAbility bp, params BlueprintAb bp.CreateComponents(result); } + internal static void CreateWeaponProficiencies(this BlueprintFeature bp, params WeaponCategory[] weapons) + { + var result = new AddProficiencies(); + result.WeaponProficiencies = weapons; + bp.CreateComponents(result); + } + internal static void DeleteComponents(this BlueprintScriptableObject bp, params BlueprintComponent[] components) { bp.ComponentsArray = bp.ComponentsArray.Except(components).ToArray(); @@ -472,6 +712,32 @@ internal static void DisableIfNotFact(this BlueprintAbility bp, params Blueprint bp.CreateComponents(result); } + internal static void NoSelectionIfFeature(this BlueprintFeatureSelection bp) + { + var result = new NoSelectionIfAlreadyHasFeature(); + result.AnyFeatureFromSelection = true; + bp.CreateComponents(result); + } + + internal static void RestrictByStat(this BlueprintFeature bp, StatType stat, int value) + { + var result = new PrerequisiteStatValue(); + result.Stat = stat; + result.Value = value; + bp.CreateComponents(result); + } + internal static void RestrictSelectionToNewFeatures(this BlueprintFeatureSelection bp) + { + bp.Mode = SelectionMode.OnlyNew; + } + + internal static void RestrictToMC(this BlueprintCharacterClass bp) + { + var result = new PrerequisiteMainCharacter(); + result.HideInUI = true; + bp.CreateComponents(result); + } + internal static void SetParentOf(this BlueprintAbility bp, params BlueprintAbility[] children) { foreach (var child in children) @@ -487,6 +753,32 @@ internal static void SetShowOnlyIfFact(this BlueprintAbility bp, BlueprintUnitFa bp.CreateComponents(result); } + internal static void SetSpellAttack(this BlueprintAbility bp, AbilityRange range) + { + bp.Type = AbilityType.Spell; + bp.SpellResistance = true; + bp.NotOffensive = false; + bp.Animation = UnitAnimationActionCastSpell.CastAnimationStyle.Omni; + bp.Range = range; + bp.CanTargetEnemies = true; + bp.CanTargetFriends = false; + bp.CanTargetPoint = false; + bp.CanTargetSelf = false; + } + + internal static void SetSpellSupport(this BlueprintAbility bp, AbilityRange range) + { + bp.Type = AbilityType.Spell; + bp.SpellResistance = false; + bp.NotOffensive = true; + bp.Animation = UnitAnimationActionCastSpell.CastAnimationStyle.Touch; + bp.Range = range; + bp.CanTargetEnemies = false; + bp.CanTargetFriends = true; + bp.CanTargetPoint = false; + bp.CanTargetSelf = true; + } + internal static void SetSupernaturalHarmful(this BlueprintAbility bp, AbilityRange range) { bp.Type = AbilityType.Supernatural; diff --git a/Utilities/Helpers.cs b/Utilities/Helpers.cs index 7417670..52943d7 100644 --- a/Utilities/Helpers.cs +++ b/Utilities/Helpers.cs @@ -4,6 +4,7 @@ using Kingmaker.Blueprints.Facts; using Kingmaker.Designers.EventConditionActionSystem.Actions; using Kingmaker.ElementsSystem; +using Kingmaker.EntitySystem.Stats; using Kingmaker.Enums; using Kingmaker.Enums.Damage; using Kingmaker.Localization; @@ -20,12 +21,19 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using UnityEngine; namespace MagicTime.Utilities { internal static class Helpers { + public static T Create(Action init = null) where T : new() + { + var result = new T(); + init?.Invoke(result); + return result; + } public static T CreateBlueprint(string asset_name, Action init = null) where T : BlueprintScriptableObject, new() { var result = new T(); @@ -35,6 +43,37 @@ internal static class Helpers return result; } + private static readonly MethodInfo CloneMethod = typeof(object).GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance); + + public static T Clone(T original_bp, string new_name = null) where T : SimpleBlueprint, new() + { + var clone = CloneMethod.Invoke(original_bp, null) as T; + /* + var fields = new List(); + foreach (var field in original_bp.GetType().GetFields()) + { + fields.Add(field); + } + var fields_array = fields.ToArray(); + var clone = new T(); + if (new_name == null) + { + new_name = "Cloned" + original_bp.name; + Resources.UpdateDatabase(Guid.NewGuid().ToString(), "Cloned" + original_bp.name); + } + clone.name = new_name; + for (int i = 0; i < fields_array.Length; i++) + { + var field = clone.GetType().GetFields().ToArray()[i]; + var new_field = fields_array[i]; + field.SetValue(clone, new_field.GetValue(original_bp)); + + } + */ + clone.AssetGuid = Resources.AddAsset(new_name, clone); + return clone; + } + public static BlueprintBuff CreateBuff(string asset_name, string name, string description, string icon = null, PrefabLink fx_on_start = null, BlueprintBuff.Flags buff_flags = 0) { @@ -84,6 +123,23 @@ public static BlueprintAbilityResource CreateAbilityResourceFixed(string asset_n return result; } + public static BlueprintAbilityResource CreateAbilityResourceVariable(string asset_name, int base_value, bool with_level, + int lv_increase, params BlueprintCharacterClassReference[] classes) + { + var result = CreateBlueprint(asset_name, bp => + { + bp.m_UseMax = false; + bp.m_MaxAmount = new BlueprintAbilityResource.Amount + { + BaseValue = base_value, + IncreasedByLevel = with_level, + LevelIncrease = lv_increase, + m_Class = classes + }; + }); + return result; + } + public static BlueprintActivatableAbility CreateActivatableAbility(string asset_name, string name, string description, string icon, BlueprintBuff buff, bool def_on = false, bool end_after_combat = false) { @@ -100,6 +156,22 @@ public static BlueprintActivatableAbility CreateActivatableAbility(string asset_ return result; } + public static BlueprintProgression.ClassWithLevel CreateClassBasedProgression(BlueprintCharacterClass cclass, int level) + { + var result = new BlueprintProgression.ClassWithLevel(); + result.m_Class = cclass.ToReference(); + result.AdditionalLevel = level; + return result; + } + + public static BlueprintProgression.ArchetypeWithLevel CreateArchetypeBasedProgression(BlueprintArchetype arch, int level) + { + var result = new BlueprintProgression.ArchetypeWithLevel(); + result.m_Archetype = arch.ToReference(); + result.AdditionalLevel = level; + return result; + } + public static BlueprintFeature CreateFeature(string asset_name, string name, string description, string icon_name = null, Sprite icon = null, bool class_feature = true) { @@ -107,7 +179,7 @@ public static BlueprintFeature CreateFeature(string asset_name, string name, str { bp.m_DisplayName = CreateString($"{asset_name}.Name", name); bp.m_Description = CreateString($"{asset_name}.Description", DescriptionTools.TagEncyclopediaEntries(description)); - if (icon_name != null) { AssetLoader.LoadInternal("Icons", icon_name); } + if (icon_name != null) { bp.m_Icon = AssetLoader.LoadInternal("Icons", icon_name); } if (icon != null) { bp.m_Icon = icon; } bp.IsClassFeature = class_feature; bp.Ranks = 1; @@ -277,6 +349,27 @@ public static ContextActionApplyBuff CreateContextActionBuff(BlueprintBuff buff, return result; } + public static ContextActionDealDamage CreateContextActionDamageNoDice(DamageType damage_type, DamageEnergyType energy) + { + var result = new ContextActionDealDamage(); + result.Half = false; + result.IgnoreCritical = true; + result.ReadPreRolledFromSharedValue = false; + result.PreRolledSharedValue = Kingmaker.UnitLogic.Abilities.AbilitySharedValue.Damage; + result.DamageType = new DamageTypeDescription(); + result.DamageType.Type = damage_type; + result.DamageType.Energy = energy; + result.Value = new ContextDiceValue(); + result.Value.DiceType = Kingmaker.RuleSystem.DiceType.Zero; + result.Value.DiceCountValue = new ContextValue(); + result.Value.DiceCountValue.ValueType = ContextValueType.Simple; + result.Value.BonusValue = new ContextValue(); + result.Value.BonusValue.ValueType = ContextValueType.Rank; + result.Value.BonusValue.ValueShared = Kingmaker.UnitLogic.Abilities.AbilitySharedValue.Damage; + result.Value.BonusValue.ValueRank = AbilityRankType.Default; + return result; + } + public static ContextActionRemoveBuff CreateContextActionRemoveBuff(BlueprintBuff buff) { var result = new ContextActionRemoveBuff(); @@ -344,6 +437,44 @@ public static LevelEntry CreateLevelEntry(int level, params BlueprintFeature[] f return result; } + public static LevelEntry CreateLevelEntryByList(int level, List features) + { + var result = new LevelEntry(); + result.Level = level; + result.m_Features = features; + return result; + } + + public static void RegisterClass(BlueprintCharacterClass ClassToRegister) + { + var progression = ResourcesLibrary.GetRoot().Progression; + List list = ((IEnumerable)progression.m_CharacterClasses).ToList(); + list.Add(ClassToRegister.ToReference()); + list.Sort((Comparison)((x, y) => { + BlueprintCharacterClass blueprint1 = ResourcesLibrary.TryGetBlueprint(x.guid); + BlueprintCharacterClass blueprint2 = ResourcesLibrary.TryGetBlueprint(y.guid); + return blueprint1 == null || blueprint2 == null ? 1 : (blueprint1.PrestigeClass == blueprint2.PrestigeClass ? blueprint1.NameSafe().CompareTo(blueprint2.NameSafe()) : (blueprint1.PrestigeClass ? 1 : -1)); + })); + progression.m_CharacterClasses = list.ToArray(); + if (!ClassToRegister.IsArcaneCaster && !ClassToRegister.IsDivineCaster) + return; + BlueprintProgression.ClassWithLevel classWithLevel = ClassToClassWithLevel(ClassToRegister); + BlueprintProgression blueprint = ResourcesLibrary.TryGetBlueprint("fe9220cdc16e5f444a84d85d5fa8e3d5"); + var newdata = new BlueprintProgression.ClassWithLevel[] { classWithLevel }; + blueprint.m_Classes = blueprint.m_Classes.Concat(newdata).ToArray(); + } + + public static BlueprintProgression.ClassWithLevel ClassToClassWithLevel( + BlueprintCharacterClass orig, + int addLevel = 0) + { + return new BlueprintProgression.ClassWithLevel() + { + m_Class = orig.ToReference(), + AdditionalLevel = addLevel + }; + } + // All localized strings created in this mod, mapped to their localized key. Populated by CreateString. private static Dictionary textToLocalizedString = new Dictionary();