├── .gitattributes ├── .gitignore ├── LevelableAivu.sln ├── LevelableAivu ├── AivuSizeFix.cs ├── Attributes.cs ├── BlueprintCore │ ├── LICENSE │ ├── README.md │ └── changes.md ├── Config │ ├── Blueprints.cs │ ├── Blueprints.json │ ├── ICollapsableGroup.cs │ ├── IDisableableGroup.cs │ ├── IUpdateableSettings.cs │ ├── ModSettings.cs │ ├── SettingGroup.cs │ ├── Settings.cs │ └── Settings.json ├── CreateHavocDragonClass.cs ├── DescriptionTools.cs ├── ExtensionMethods.cs ├── FinishingTouches.cs ├── FixItemAccess.cs ├── GUIDs.cs ├── Helpers.cs ├── Info.json ├── LevelableAivu.csproj ├── Main.cs ├── ModifyEquipmentRestrictionsHasAnyClassFromList.cs ├── PatchTryLevelUpPet.cs ├── Properties │ └── AssemblyInfo.cs ├── Resources.cs ├── RespecPatch.cs ├── UMMSettingsUI.cs ├── VariableBaseSizeGetter.cs └── packages.config └── Readme.md /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd 364 | 365 | lib/ 366 | /lib 367 | /lib 368 | -------------------------------------------------------------------------------- /LevelableAivu.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31624.102 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LevelableAivu", "LevelableAivu\LevelableAivu.csproj", "{88C5A20A-AA6C-4856-987C-CAC9AA044C44}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{45D3F936-F8D4-43A9-8281-00194A939F51}" 9 | ProjectSection(SolutionItems) = preProject 10 | .gitignore = .gitignore 11 | Readme.md = Readme.md 12 | EndProjectSection 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {88C5A20A-AA6C-4856-987C-CAC9AA044C44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {88C5A20A-AA6C-4856-987C-CAC9AA044C44}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {88C5A20A-AA6C-4856-987C-CAC9AA044C44}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {88C5A20A-AA6C-4856-987C-CAC9AA044C44}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {E3A94FDD-EC2F-4BE5-B9DF-EFFB93A1E33A} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /LevelableAivu/AivuSizeFix.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Kingmaker.Designers.Mechanics.Buffs; 3 | using Kingmaker.EntitySystem; 4 | using Kingmaker.Enums; 5 | using Kingmaker.UnitLogic.Buffs; 6 | using Kingmaker.UnitLogic.Parts; 7 | using Kingmaker.Utility; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Linq; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | 14 | namespace LevelableAivu 15 | { 16 | [HarmonyPatch(typeof(UnitPartSizeModifier), "UpdateSize")] 17 | 18 | static class AivuSizeFix 19 | { 20 | [HarmonyPriority(Priority.Normal)] 21 | static bool Prefix(UnitPartSizeModifier __instance) 22 | { 23 | 24 | var variableSize = __instance.Owner.Facts.m_Facts.SelectMany(x => x.Components).Select(x => x.SourceBlueprintComponent).OfType(); 25 | if (variableSize.Count() == 0) 26 | { 27 | 28 | return true; 29 | } 30 | else 31 | { 32 | 33 | Size ogSize = __instance.Owner.OriginalSize + variableSize.Select(x => x.Shift).Sum(); 34 | Size finalSize = ogSize; 35 | EntityFact entityFact = __instance.m_SizeChangeFacts.LastItem(); 36 | if (entityFact != null) 37 | { 38 | foreach (EntityFactComponent entityFactComponent in entityFact.Components) 39 | { 40 | Polymorph polymorph = entityFactComponent.SourceBlueprintComponent as Polymorph; 41 | ChangeUnitSize changeUnitSize = entityFactComponent.SourceBlueprintComponent as ChangeUnitSize; 42 | if (polymorph != null) 43 | { 44 | Size? polySize = polymorph.GetUnitSize(entityFactComponent); 45 | if (polySize != null) 46 | finalSize = polySize.Value; 47 | 48 | } 49 | else if (changeUnitSize != null) 50 | { 51 | if (changeUnitSize.IsTypeDelta) 52 | { 53 | finalSize = ogSize + changeUnitSize.SizeDelta; 54 | //Main.Log($"Size Adjust: Size set to :{finalSize}"); 55 | } 56 | else if (changeUnitSize.IsTypeValue) 57 | { 58 | finalSize = changeUnitSize.Size; 59 | //Main.Log($"Size Set: Size set to :{finalSize}"); 60 | } 61 | } 62 | } 63 | } 64 | else 65 | { 66 | __instance.Owner.Remove(); 67 | } 68 | Main.Log($"Final Size Setto :{finalSize}"); 69 | __instance.Owner.State.Size = finalSize; 70 | return false; 71 | } 72 | 73 | 74 | } 75 | } 76 | 77 | static class AfterTheFactAivuSizeFix 78 | { 79 | 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /LevelableAivu/Attributes.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using System; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace LevelableAivu.Utilities 7 | { 8 | class PostPatchInitializeAttribute : Attribute 9 | { 10 | } 11 | 12 | static class PostPatchInitializer 13 | { 14 | public static void Initialize() 15 | { 16 | var methods = Assembly.GetExecutingAssembly() 17 | .GetTypes() 18 | .Where(x => x.IsClass) 19 | .SelectMany(x => AccessTools.GetDeclaredMethods(x)) 20 | .Where(x => x.GetCustomAttributes(typeof(PostPatchInitializeAttribute), false).FirstOrDefault() != null); 21 | 22 | foreach (var method in methods) 23 | { 24 | Main.LogDebug($"Executing Post Patch: {method.Name}"); 25 | method.Invoke(null, null); // invoke the method 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LevelableAivu/BlueprintCore/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 TylerGoeringer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LevelableAivu/BlueprintCore/README.md: -------------------------------------------------------------------------------- 1 | # WW-Blueprint-Core 2 | 3 | [![NuGet](https://img.shields.io/nuget/v/WW-Blueprint-Core?style=flat-square)](https://www.nuget.org/packages/WW-Blueprint-Core) 4 | 5 | **What is BlueprintCore**: A library to simplify modifying Pathfinder: Wrath of the Righteous. 6 | 7 | At a glance: 8 | 9 | * Builder-style API for creating and modifying Blueprints, Actions, and Conditions 10 | ```C# 11 | BuffConfigurator.New(MyBuffName, MyBuffGuid) 12 | .AddContextStatBonus(StatType.Strength, ContextValues.Constant(2)) 13 | .Configure(); 14 | ``` 15 | * Constructor methods for Blueprint, Action, Condition, and BlueprintComponent types 16 | * Lists example blueprints 17 | * Usage comments from modders and game assembly 18 | * Guards against null fields 19 | * API enforces required fields and implicit requirements when possible 20 | * Implemented with help from modders like you, see [How to Contribute](https://wittlewolfie.github.io/WW-Blueprint-Core/articles/contributing.html). 21 | * Blueprint API limiting BlueprintComponents to usable types 22 | * Runtime validation of blueprints 23 | * Using game validation and custom logic to validate implicit constraints 24 | * Utility classes 25 | * Create common types 26 | * Localization, blueprint management, logging, and more 27 | 28 | If you're interested in contributing, see [How to Contribute](https://wittlewolfie.github.io/WW-Blueprint-Core/articles/contributing.html). 29 | 30 | For usage see [Getting Started](https://wittlewolfie.github.io/WW-Blueprint-Core/articles/intro.html). 31 | 32 | ## Mods Using BPCore 33 | 34 | Not exhaustive but check out these mods: 35 | 36 | * [Character Options+](https://github.com/WittleWolfie/CharacterOptionsPlus) 37 | * [Tome of the Firebird](https://github.com/pheonix99/TomeOfTheFirebird) 38 | * [Added Feats](https://github.com/Telyl/AddedFeats) 39 | * [Medium Class](https://github.com/Telyl/MediumClass) 40 | * [Martial Excellence](https://github.com/Envibel/MartialExcellence) 41 | 42 | ## Features 43 | 44 | ### Blueprint Configurators 45 | 46 | Each Blueprint type has a corresponding configurator, e.g. `BuffConfigurator`, with methods for modifying its fields and components. 47 | 48 | When you're done configuring, call `Configure()` to commit your changes and run validation. Validation errors are logged as a warning. 49 | 50 | Configurators use method chaining to reduce boilerplate: 51 | 52 | ```C# 53 | FeatureConfigurator.New(FeatName, FeatGuid) 54 | .AddBuffSkillBonus(StatType.SkillKnowledgeArcana, 2) 55 | .AddBuffSkillBonus(StatType.SkillUseMagicDevice, 2) 56 | .Configure(); 57 | ``` 58 | 59 | Configurator methods can set or modify fields and add all supported BlueprintComponent types. Using auto-complete you can quickly search available component types. 60 | 61 | The Configure *should* guarantee components work with a given blueprint. This is determined using the game's `AllowedOn` attribute which declares supported blueprint types for each component. This is not always correct so please report any problems with the API: [GitHub Issues](https://github.com/WittleWolfie/WW-Blueprint-Core/issues). 62 | 63 | Every effort is made to minimize boilerplate and enforce proper usage of fields and components. Blueprint fields that should not be modified are hidden when reported by a contributor or on [GitHub Issues](https://github.com/WittleWolfie/WW-Blueprint-Core/issues). 64 | 65 | Component methods are regularly updated to ignore unused fields and require fields necessary for the component to function. Field types that should not be null are automatically populated with a default to prevent exceptions. 66 | 67 | For example, the `FeatureConfigurator` exposes a method `AddPrerequisiteCharacterLevel`: 68 | 69 | ```C# 70 | // Summary: 71 | // Adds Kingmaker.Blueprints.Classes.Prerequisites.PrerequisiteClassLevel 72 | // 73 | // Parameters: 74 | // characterClass: 75 | // Blueprint of type BlueprintCharacterClass. You can pass in the blueprint using: 76 | // • A blueprint instance – 77 | // • A blueprint reference – 78 | // • A blueprint id as a string, Guid, or BlueprintGuid – 79 | // • A blueprint name registered with BlueprintTool – 80 | // See Blueprint for more details. 81 | // 82 | // merge: 83 | // If mergeBehavior is ComponentMerge.Merge and the component already exists, this 84 | // expression is called to merge the components. 85 | // 86 | // mergeBehavior: 87 | // Handling if the component already exists since the component is unique. Defaults 88 | // to ComponentMerge.Fail. 89 | // 90 | // Remarks: 91 | // • Used by 92 | // • AdvancedWeaponTraining1 –3aa4cbdd4af5ba54888b0dc7f07f80c4 93 | // • OracleRevelationSoulSiphon –226c053a75fd7c34cab1b493f5847787 94 | // • WreckingBlowsFeature –5bccc86dd1f187a4a99f092dc054c755 95 | public TBuilder AddPrerequisiteClassLevel( 96 | Blueprint characterClass, 97 | int level, 98 | bool? checkInProgression = null, 99 | Prerequisite.GroupType? group = null, 100 | bool? hideInUI = null, 101 | Action? merge = null, 102 | ComponentMerge mergeBehavior = ComponentMerge.Fail, bool? not = null) 103 | ``` 104 | 105 | `characterClass` and `level` are required while the rest of the parameters are optional. The remarks include three 106 | blueprints which use the component for reference. 107 | 108 | ### ActionList and ConditionsChecker Builders 109 | 110 | `ActionsBuilder` is a builder API for `ActionList` and `ConditionsBuilder` is a builder API for `ConditionsChecker`. 111 | 112 | BlueprintCore APIs accept builders directly or you can call `Build()` to construct them and run validation. Validation errors are logged as a warning. 113 | 114 | Builder methods create Action and Condition types and are defined across extension classes to improve auto-complete searching. Extension classes are logically grouped so most blueprints require only one extension. 115 | 116 | For example, `ActionsBuilderKingdomEx` contains builder methods related to the Kingdom and Crusade system and can be referenced by including the namespace `BlueprintCore.Actions.Builder.KingdomEx`: 117 | 118 | ```C# 119 | using BlueprintCore.Actions.Builder.KingdomEx; 120 | 121 | ActionsBuilder.New() 122 | .ChangeTacticalMorale(ContextValues.Constant(5)) 123 | .Build(); 124 | ``` 125 | 126 | Library methods, such as configurators, accept builders directly and call `Build()` internally to minimize boilerplate: 127 | 128 | ```C# 129 | BuffConfigurator.New(BuffName, BuffGuid) 130 | .AddRestTrigger(ActionsBuilder.New().AddRandomTrashItem(TrashLootType.Scrolls, 100)) 131 | .Configure(); 132 | ``` 133 | 134 | ### Utils 135 | 136 | Util classes provide type builders, constant references, tools, and more. 137 | 138 | #### Tools 139 | 140 | Tool classes include methods for common operations. These vary from operations like `CommonTool#Append<>()` for concatening arrays to `BlueprintTool.GetRef()` which creates a blueprint reference directly, without fetching the blueprint. 141 | 142 | #### Text 143 | 144 | `LocalizationTool` uses a JSON file to define in-game text with support for localization and encyclopedia tagging: 145 | 146 | ```json 147 | { 148 | "Key": "MagicalAptitude.Name", 149 | // Don't process this since it is just a name. Without this it might create strange artifacts by trying to create 150 | // links to encycolpedia pages. 151 | "ProcessTemplates": false, 152 | "enGB": "Magical Aptitude", 153 | "deDE": "Magische Begabung" 154 | }, 155 | { 156 | "Key": "MagicalAptitude.Description", 157 | "enGB": "You get a +2 bonus on all Spellcraft and Use Magic Device skill checks. If you have 10 or more ranks in one of these skills, the bonus increases to +4 for that skill." 158 | } 159 | ``` 160 | 161 | In BPCore APIs you can reference the strings using the key: 162 | 163 | ```C# 164 | FeatureConfigurator.New(FeatName, FeatGuid) 165 | .SetDisplayName("MagicalAptitude.Name") 166 | .SetDescription("MagicalAptitude.Description") 167 | .Configure(); 168 | ``` 169 | 170 | #### Logging 171 | 172 | `LogWrapper` exposes the game's logger for mod usage. This logs output to the game logs which can be viewed using [Remote Console](https://github.com/OwlcatOpenSource/RemoteConsole/releases). 173 | 174 | It enables verbose logging for debugging and prefixes logs to filter log output to your mod or BlueprintCore logs. 175 | 176 | For example, this code 177 | ```C# 178 | LogWrapper logger = LogWrapper.Get("MyMod"); 179 | logger.Info("Logger initialized."); 180 | ``` 181 | logs: 182 | ``` 183 | BlueprintCore.MyMod: Logger initialized. 184 | ``` 185 | 186 | #### Type Builders 187 | 188 | Classes for constructing simple types like `ContextValues` for creating `ContextValue` types or `ContextRankConfigs` for creating `ContextRankConfig` components. 189 | 190 | ```C# 191 | FeatureConfigurator.New(FeatureName, FeatureGuid) 192 | .AddContextRankConfig(ContextRankConfigs.BaseAttack().WithBonusValueProgression(2)) 193 | .Build(); 194 | ``` 195 | 196 | Utility classes provide functionality to simplify modifying the game and ensure correct use of game types. 197 | 198 | ## Usage 199 | 200 | BlueprintCore is available as a [NuGet package](https://www.nuget.org/packages/WW-Blueprint-Core/). For more details see [Getting Started](https://wittlewolfie.github.io/WW-Blueprint-Core/articles/intro.html). 201 | 202 | # Acknowledgements 203 | 204 | * Thank you to the Owlcat modders who came before me, documenting their process and sharing their code. 205 | * Thank you to the modders on Discord who helped me learn modding so I could create this library. 206 | 207 | # Interested in modding? 208 | 209 | * Check out the [OwlcatModdingWiki](https://github.com/WittleWolfie/OwlcatModdingWiki/wiki). 210 | * Join us on [Discord](https://discord.com/invite/wotr). 211 | -------------------------------------------------------------------------------- /LevelableAivu/BlueprintCore/changes.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v2.7.8 Release 4 | 5 | * Added `AddToLevelEntry` to ProgressionConfigurator to simplify adding new features to an existing progression 6 | 7 | ## v2.7.7 Release 8 | 9 | * FeatureSelectionConfigurator is at feature parity w/ FeatureConfigurator for populating feature groups 10 | 11 | ## v2.7.6 Release 12 | 13 | * New custom FeatureSelectionConfigurator for easily setting feature groups 14 | * FeatureConfigurator is more aggressive about setting `IsClassFeature` to `true` 15 | 16 | ### Breaking Changes 17 | 18 | * Custom FeatureSelectionConfigurator requires updating imports 19 | 20 | ## v2.7.5 Release 21 | 22 | * AbilityConfigurator has an override for `SetLocalizedSavingThrow` for common saving throw strings 23 | 24 | ## v2.7.4 Release 25 | 26 | * Prevents mod conflict which can prevent modded spells from being learnable during level up 27 | 28 | ### Breaking changes 29 | 30 | * SpellListConfigurator namespace has changed (it is now a custom configurator), so imports require updating 31 | 32 | ## v2.7.3 Release 33 | 34 | * Removes `m_DescriptionModifiersCache` from feature configuration 35 | * Fixes a bug which breaks some feature descriptions when you modify them with configurators 36 | 37 | ### Breaking changes 38 | 39 | * You can no longer modify `m_DescriptionModifiersCache` with configurators. This field is populated automatically at runtime, if you want to change it add components inheriting from `DescriptionModifier` 40 | 41 | ## v2.7.2 Release 42 | 43 | * Update with latest patch data 44 | 45 | ## v2.7.1 Release 46 | 47 | * Delayed configuration now sets sane defaults immediately 48 | * This fixes a conflict w/ WorldCrawl 49 | 50 | ## v2.7.0 Release 51 | 52 | * Update with latest patch data 53 | * New `AssetExtensions` class for Unity extension methods (stolen shamelessly from Bubbles) 54 | * Includes new class `SnapToTransformWithRotation` and accompanying extensions, `AnchorTo()` and `AnchorToUnit()` for anchoring Unity objects to follow other objects 55 | * Added `SetLocalizedDuration` utility to `AbilityConfigurator` for easy reference of standard duration strings 56 | * Adding a spell to a mod spell list will no longer cause an NPE if the mod is not present 57 | * An error is still logged in BPCore using the Verbose channel 58 | * Fixed a bug causing validation to always fail for some components 59 | 60 | ## v2.6.2 Release 61 | 62 | * Fixes NPE with DealDamagePreRolled action 63 | 64 | ## v2.6.1 Release 65 | 66 | * Updated to latest game patch (2.0.4k) 67 | * New `AddAll` methods for `ActionsBuilder` and `ConditionsBuilder` support copying existing `ActionLists` and `ConditionsCheckers` 68 | * Implicit operators support passing in `ActionList` for `ActionsBuilder` and `ConditionsChecker` for `ConditionsBuilder` 69 | 70 | ## v2.6.0 Release 71 | 72 | * New `AbilityConfigurator` util methods for creating spells 73 | * Use `AbilityConfigurator.NewSpell()` instead of `AbilityConfigurator.New()` and read its docs 74 | * Added support for dynamically replacing game assets 75 | * See `AssetTool.RegisterDynamicPrefabLink()` for details 76 | * Use this when you want a modified version of an existing asset 77 | * New `ContextDice` and `DamageTypes` utils for creating `ContextDiceValue` and `DamageTypeDescription`, respectively 78 | * Added links to some mods made using BPCore for examples 79 | 80 | ### Breaking Changes 81 | 82 | * If you are using ILStrip there is a new patch, add the following entry point: 83 | * `BlueprintCore.Utils.Assets.AssetTool/AssetBundle_Patch` 84 | 85 | ## v2.5.1 Release 86 | 87 | * Update for 2.0.2c game version 88 | 89 | ## v2.5.0 Release 90 | 91 | * New Type Utils 92 | * `CueSelections` - For creating `CueSelection` 93 | * `CharacterSelections` - For creating `CharacterSelection` 94 | * `DialogSpeakers` - For creating `DialogSpeaker` 95 | * New Empty Constants for working with Answers, Cues, and Dialogs 96 | * Add option to skip selection processing in `FeatureConfigurator`: `SkipAddToSelections` 97 | * Teamwork feats are automatically added to Lich Skeletal Companion's feat selection 98 | 99 | ## v2.4.1 Release 100 | 101 | * Fix bug causing `CopyFrom()` to ignore all components when using a predicate 102 | * A single delayed configurator failure no longer prevents other delayed configurators from being processed 103 | * Prevent NPE when calling `SetIcon(null)` 104 | * You shouldn't do this but sometimes it's convenient for methods to call `SetIcon()` on arbitrary input 105 | 106 | ## v2.4.0 Release 107 | 108 | * Added blueprint copying methods: `CopyFrom()` 109 | * These are shallow copies that copy all fields 110 | * Components are only copied if specified, either by type or using a predicate 111 | 112 | ## v2.3.3 Release 113 | 114 | * Fixes a bug preventin archetypes from working properly in ContextRankConfigs 115 | 116 | ## v2.3.2 Release 117 | 118 | * Prevents features from being added to Feature Selection lists they're already on 119 | 120 | ## v2.3.1 Release 121 | 122 | * Updated for EE 123 | 124 | ## v2.3.0 Release 125 | 126 | * Added more support for `FeatureGroup.TeamworkFeat` so it can be configured for sharing features. Note that you need to use the method `AddAsTeamworkFeat()` since configuring for some class features requires GUIDs used to generate additional blueprints. 127 | * Battle Scion's Battle Prowess 128 | * Monster Tactician Tactics 129 | * Tactical Leader Feat Sharing 130 | * Hunter Tactics 131 | * Sacred Huntsmaster Tactics 132 | * Cavalier Tactician 133 | * Vanguard Tactician 134 | * Pack Rager's Raging Tactician 135 | * Updated for the latest game version 136 | * Removed obsolete components flagged in the 2.2 release 137 | 138 | ### Breaking Changes 139 | 140 | * All components flagged obsolete have been removed 141 | 142 | ## v2.2.5 Release 143 | 144 | * Added support for multiple strings files and embedded resources 145 | * Features with `FeatureGroup.TeamworkFeat` are automatically added to Tactical Leader's Share Feat Buff 146 | 147 | ## v2.2.4 Release 148 | 149 | * Updated for latest game version 150 | * Added blueprint references to populate content mods, including auto config for BlueprintFeatureSelection 151 | * Microscopic Content Expansion 152 | * TTT-Base, TTT-Reworks 153 | * WOTR_PATH_OF_RAGE 154 | * Expanded Content 155 | * Kineticist Expanded Elements 156 | * Magic Time! 157 | * Added support for delayed configuration 158 | * Recommended for auto config of BlueprintFeatureSelection to support mods 159 | * Hand tuned more components 160 | * Added `BlueprintTool.TryGet()` to check for presence of a blueprint 161 | 162 | ### Breaking Changes 163 | 164 | * `AddReplaceStatForPrerequisites` has been modified 165 | 166 | ## v2.2.3 Release 167 | 168 | * Updated for latest game version 169 | * Fixed bug with FeatureConfigurator which could incorrectly add features to selection groups 170 | * Added `SetClass()` and upated constructor for ArchetypeConfigurator to automatically add the archetype to the class 171 | 172 | ## v2.2.2 Release 173 | 174 | * Updated for Patch 1.4 175 | * Hand tuned Recommendation components 176 | 177 | ## v2.2.0 Release 178 | 179 | * New Quick Start setup using a project template, no more editing .csproj files! 180 | * Expanded tutorials to cover more advanced modding 181 | * Activatable Abilities, Transpilers, Assets, and more 182 | * Added several replacement components from TTT-Core 183 | * Existing methods are flagged as obsolete and will be removed in the next major release, so consider migrating 184 | * Added `Asset<>` and `AssetLink<>` types to allow referencing assets by Asset Id 185 | * Added `ToString()` override to `Blueprint<>` 186 | * This makes it easier to cast between types 187 | * Added `AddToFeatureSelection()` and `AddToRangerStyles()` methods to `FeatureConfigurator` 188 | * Convenience methods for adding to `BlueprintFeatureSelection` if the `FeatureGroup` logic doesn't handle it 189 | * ContextValues 190 | * Added support for `ContextValueType.AbilityParameter` 191 | * Updated `CustomProperty` to accept `Blueprint<>` instead of `string` 192 | * Added new `HasActionsAvailable` Condition 193 | * Fixed and filled out unit test project 194 | * Future contributions should include unit tests to the extent possible 195 | * Fixed type specific overrides to apply to Lists and Arrays 196 | * Fixed LogWrapper to respect `EnableInternalVerboseLogs` 197 | 198 | ### Breaking Changes 199 | 200 | * If you are using ILStrip there are new patches, add the following entry points: 201 | * `BlueprintCore.UnitParts.Replacements.UnitPartBuffSuppressFixed/Buff_OnAttach_Suppression_Patch` 202 | * `BlueprintCore.Utils.Assets.AssetTool/BlueprintsCaches_Patch` 203 | * `BlueprintCore.Utils.Assets.AssetTool/BundlesLoadService_Patch` 204 | * Lists and arrays of the following types have been replaced by their BPCore version, which may require updates: 205 | * `LocalizedString` => `LocalString` 206 | * `AnimationClipWrapperLink` => `AssetLink` 207 | * `AnimationClipWrapper` => `Asset` 208 | * `EquipmentEntityLink` => `AssetLink` 209 | * `EquipmentEntity` => `Asset` 210 | * `FamiliarLink` => `AssetLink` 211 | * `Familiar` => `Asset` 212 | * `PrefabLink` => `AssetLink` 213 | * `GameObject` => `Asset` 214 | * `ProjectileLink` => `AssetLink` 215 | * `Projectile` => `Asset` 216 | * `SpriteLink` => `AssetLink` 217 | * `Sprite` => `Asset` 218 | * `TextAssetLink` => `AssetLink` 219 | * `TextAsset` => `Asset` 220 | * `Texture2DLink` => `AssetLink` 221 | * `Texture2D` => `Asset` 222 | * `UnitViewLink` => `AssetLink` 223 | * `UnitEntityView` => `Asset` 224 | * `VideoLink` => `AssetLink` 225 | * `VideoClip` => `Asset` 226 | 227 | ## v2.1.2 Release 228 | 229 | * Bugfixes: 230 | * Restored pre 2.1 API for Randomize and SelectByValue 231 | 232 | ### Breaking Changes 233 | 234 | * If you updated code references you'll have to revert. This was an accidental / change introduced when params became a standard API. 235 | 236 | ## v2.1.1 Release 237 | 238 | * Bugfixes: 239 | * ArchetypeConfigurator and ProgressionConfigurator `RemoveFromX` functions now function properly 240 | * All `RemoveFromX` functions accepting a predicate now function as expected 241 | * Previously the configurators were confused and were keeping everything matching the predicate, not removing 242 | * UIGroupBuilder and LevelEntryBuilder now work properly 243 | * Rumor has it they were using static fields so they collided with one another 244 | 245 | ### Breaking Changes 246 | 247 | * If you were using any `RemoveFromX(Func)` functions the logic is now reversed. This matches the API design but not the function. 248 | 249 | ## v2.1.0 Release 250 | 251 | * Added new localization system for translation support and simplified text references. See [Text, Logging, and Utils](usage/utils.md) for more details. 252 | * Added static references to in-game blueprints. See [Referencing Blueprints](usage/blueprints.md#referencing-blueprints) for more details. 253 | * Configurators now automatically set safe default values for types which cannot be null 254 | * Methods with a single enumerable or flag parameter use `params` syntax 255 | * Removed configurators for QA related blueprints 256 | * Add more constructors for ContextDurationValue 257 | * Removed Modify field methods for primitive and enum types 258 | * If you have a use case let me know but this API was not intended to exist 259 | * Specific Type Changes 260 | * AbilityConfigurator 261 | * Removed AnimationStyle which is deprecated and unused 262 | * FeatureConfigurator 263 | * Automatically adds features to the appropriate `BlueprintFeatureSelection`. See [FeatureConfigurator.New()](xref:BlueprintCore.Blueprints.CustomConfigurators.Classes.FeatureConfigurator.New(System.String,System.String,Kingmaker.Blueprints.Classes.FeatureGroup[])). 264 | * Automatically sets `IsClassFeature` to true for features with `FeatureGroup.Feat` 265 | * ProgressionConfigurator 266 | * Added many convenience method overrides for ease of working with ClassWithLevel, LevelEntry, and ArchetypeWithLevel 267 | * Removed support for AlternateProgressionType which can only be Div2 268 | * Automatically sets `ForAllOtherClasses` to `false` when setting or adding to `m_AlternateProgressionClasses` 269 | * Removed support for Remove functions that don't make sense 270 | * BuffEnchantAnyWeapon is now available in BaseUnitFactConfigurator and all inherited types 271 | * AddStatBonusIfHasFact replaced by AddStatbonusIfHasFactFixed 272 | * ContextDurationValue has more convenience constructor methods available 273 | * BlueprintUnitProperty logs a warning if `BaseValue` is 0 when using multiplication 274 | 275 | ### Breaking Changes 276 | 277 | * ProgressionConfigurator changed namespace 278 | * It is now hand tuned so it lives in CustomConfigurators 279 | * Some `RemoveFrom` field methods were removed since they are not useful 280 | * Some ContextAction methods were updated with a stricter API 281 | * `ArmorEnchantPool` and `ShieldArmorEnchantPool` uses `BlueprintArmorEnchantReference` instead of `BlueprintItemEnchantmentReference` 282 | * `WeaponEnchantPool` and `ShieldWeaponEnchantPool` uses `BlueprintWeaponEnchantmentReference` instead of `BlueprintItemEnchantmentReference` 283 | * If you were passing in a BlueprintItemEnchantmentReference directly you'll need to update your calls 284 | * `Buffs`, `ItemEnchantments`, and `Features` were removed 285 | * Replaced by Refs classes. See [Referencing Blueprints](usage/blueprints.md#referencing-blueprints). 286 | * QA Blueprint configurators were removed 287 | * I don't expect anyone to use these, but if you have a use case let me know. 288 | * Configurators no longer exposes methods for cache fields 289 | * These are set at runtime by the blueprint, they should not be set manually. 290 | * Methods with a single enumerable parameter use `params` syntax 291 | * If they currently require a list they must be updated. You can call `ToArray()` on the list. 292 | * AddStatBonusIfHasFact is no longer supported 293 | * Use AddStatBonusIfHasFactFixed 294 | * Methods for BlueprintProgression.AlternateProgressionType are removed 295 | * It is always Div2, there is no other value 296 | * Methods for BlueprintAbility.animationStyle are removed 297 | * It is unused and deprecated 298 | 299 | ## v2.0.4 Release 300 | 301 | * Fixed an NPE resulting from not specifying optional List or Array parameters 302 | 303 | ### Breaking Changes 304 | 305 | * Fixed a bug incorrectly identifying unique and non-unique components 306 | * This removes merge handling parameters from non-unique components; you'll need to remove any you have specified 307 | 308 | ## v2.0.3 Release 309 | 310 | ### Breaking Changes 311 | 312 | * The type `Blueprint` has been shortened to `Blueprint` 313 | * You can use a Regex find/replace to fix. In VS: 314 | * Find: `Blueprint<\w*, ` 315 | * Replace With: `Blueprint<` 316 | 317 | ## v2.0.2 Release 318 | 319 | * Update to 1.3.4e game patch 320 | * Handles GUIDs with uppercase letters 321 | * Type specific handling updates 322 | * New util for creating `UnitConditionExceptions` 323 | * Blueprint field setters use `params` for enumerable types 324 | * Fixed bug with Encyclopedia tagging causing exceptions 325 | * Fixed default merge behavior which was incorrectly set to ComponentMerge.Merge instead of ComponentMerge.Fail 326 | 327 | ### Breaking Changes 328 | 329 | * Namespaces are changes to organize type specific util classes 330 | * `ContextRankConfigs` 331 | * `ContextDuration` 332 | * `ContextValues` 333 | * Blueprint field setters use `params` for enumerable types 334 | * This breaks for `List<>` fields 335 | * Removed support for `AddStatBonusScaled` 336 | * This is a legacy type and can be replaced with `AddContextStatBonus` 337 | 338 | ## v2.0.1 Release 339 | 340 | * Reverts `Configurator.Build()` to return the blueprint directly 341 | 342 | ## v2.0.0 Release 343 | 344 | * This is a significant release which is all but guaranteed to require changes to your code when updating.* 345 | 346 | ### Features 347 | 348 | * Flexible Blueprint references 349 | * BlueprintCore APIs accept blueprints by Guid, Blueprint instance, Blueprint reference, or Name for newly created 350 | blueprints 351 | * Improved Validation 352 | * Validator is now up to date with the latest changes in the game validation code 353 | * Removed unused game types from Builder and Configurator APIs 354 | * Improved documentation of games types 355 | * Examples: Every ActionsBuilder, ConditionsBuilder, and Configurator component method lists up to 3 game blueprints 356 | which use the implemented type 357 | * Developer Comments: Taken from attributes in the game code which Owlcat uses to create tooltips and help text in 358 | their level editor 359 | * Comments are integrated with the doc comments shown in your IDE 360 | * In VS you can navigate to the definition of BlueprintCore methods to see the full formatted comments for readability 361 | 362 | ### Breaking Changes 363 | 364 | * Configurator namespaces may have changed to align w/ game code structure 365 | * Method and parameter names changed for ActionsBuilder, ConditionsBuilder, and Configurators 366 | * Validator is no longer static 367 | * Builder and Configurator methods no longer exist for unused game types 368 | 369 | ### More Details 370 | 371 | Two concepts drove the creation of BlueprintCore 2.0: 372 | 373 | 1. Make it easier to improve and contribute to BlueprintCore, especially with regards to handling of game types 374 | 2. Adopt a philosophy of keeping the library as close to the game code as possible 375 | 376 | With regards to #1 I will update the contributor docs after release with guidance and examples of how to contribute. In 377 | short, you can provide remarks and instructions on handling game types by editing JSON configuration files. Alternatively, 378 | just share your knowledge in issues on GitHub and I'll do my best to update the library. 379 | 380 | For #2 there was a major shift in how the library generates ActionsBuilder, ConditionsBuilder, and Configurator classes. 381 | Previously methods were automatically generated _or_ written by hand entirely. When creating methods by hand I was aggressive 382 | about renaming things to clarify the actual game function. 383 | 384 | However, this makes it very hard to do structural improvements since all hand written code requires manual updates. 385 | Additionally a lot of the hand written code was nearly identical to generated code and only existed to rename things. 386 | With this release almost nothing is written by hand. Instead JSON config files allow selective editing of the code 387 | generation behavior. 388 | 389 | The end result is both functional and philosophical: method and parameter names map almost 1:1 with the associated type 390 | or field. This makes it easier to take existing knowledge of game code and use it to write BlueprintCore code, as well 391 | as taking BlueprintCore code and find the game code it affects. 392 | 393 | ### What's Next? 394 | 395 | With the major refactoring done I'll resume work on functional library improvements. Please file requests and suggestions 396 | on GitHub, I do read them and try my best to support them. For 2.1 I'm looking into: 397 | 398 | * Expanded Tutorial 399 | * Examples of doing more extensive changes 400 | * Improved setup instructions and examples of using different BlueprintCore classes and utilities 401 | * Localization improvements 402 | * Improve API with regards to whether a given string is parsed for tokens 403 | * Add localization support 404 | * Reduce boilerplate needed to create and reference LocalizedStrings 405 | * Smarter Configurators for common blueprint types, e.g. CharacterClass, Feature, Ability 406 | * Create blueprints using existing blueprints as a template, e.g. WizardClass template applies things like 1/2 BAB 407 | and Saving Throw progressions 408 | * Combination and creation methods that enforce contracts 409 | * Automatically add blueprints to appropriate selection groups 410 | * Static Blueprint References for common types 411 | * Expose an auto-complete list for things like Classes, Archetypes, Feats, and Spells 412 | * Project Setup Tool (Tentative) 413 | * An automated tool to create a new mod project with BlueprintCore setup 414 | * TTT-Core Support (Tentative) 415 | * I'd like to provide support for integration w/ TTT-Core without requiring it -------------------------------------------------------------------------------- /LevelableAivu/Config/Blueprints.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Kingmaker.Blueprints; 3 | using Kingmaker.Blueprints.JsonSystem; 4 | using Kingmaker.Utility; 5 | using Newtonsoft.Json; 6 | using System; 7 | using System.Collections.Generic; 8 | 9 | namespace LevelableAivu.Config 10 | { 11 | public class Blueprints : IUpdatableSettings 12 | { 13 | [JsonProperty] 14 | private bool OverrideIds = false; 15 | [JsonProperty] 16 | private readonly SortedDictionary NewBlueprints = new SortedDictionary(); 17 | [JsonProperty] 18 | private readonly SortedDictionary AutoGenerated = new SortedDictionary(); 19 | [JsonProperty] 20 | private readonly SortedDictionary UnusedGUIDs = new SortedDictionary(); 21 | private readonly SortedDictionary UsedGUIDs = new SortedDictionary(); 22 | 23 | public void OverrideSettings(IUpdatableSettings userSettings) 24 | { 25 | var loadedSettings = userSettings as Blueprints; 26 | if (loadedSettings == null) { return; } 27 | if (loadedSettings.OverrideIds) 28 | { 29 | OverrideIds = loadedSettings.OverrideIds; 30 | loadedSettings.NewBlueprints.ForEach(entry => { 31 | if (NewBlueprints.ContainsKey(entry.Key)) 32 | { 33 | NewBlueprints[entry.Key] = entry.Value; 34 | } 35 | }); 36 | } 37 | loadedSettings.AutoGenerated.ForEach(entry => { 38 | AutoGenerated[entry.Key] = entry.Value; 39 | }); 40 | } 41 | public BlueprintGuid GetGUID(string name) 42 | { 43 | 44 | Guid Id; 45 | if (!NewBlueprints.TryGetValue(name, out Id)) 46 | { 47 | #if DEBUG 48 | if (!AutoGenerated.TryGetValue(name, out Id)) 49 | { 50 | Id = Guid.NewGuid(); 51 | AutoGenerated.Add(name, Id); 52 | Main.LogDebug($"Generated new GUID: {name} - {Id}"); 53 | } 54 | else 55 | { 56 | Main.LogDebug($"WARNING: GUID: {name} - {Id} is autogenerated"); 57 | } 58 | #endif 59 | } 60 | if (Id == null) { Main.Log($"ERROR: GUID for {name} not found"); } 61 | UsedGUIDs[name] = Id; 62 | return new BlueprintGuid(Id); 63 | } 64 | 65 | public void Init() 66 | { 67 | } 68 | 69 | [HarmonyPatch(typeof(BlueprintsCache), "Init")] 70 | static class AutoGUID_Log_Patch 71 | { 72 | 73 | [HarmonyPriority(Priority.Last)] 74 | static void Postfix() 75 | { 76 | GenerateUnused(); 77 | ModSettings.SaveSettings("Blueprints.json", ModSettings.Blueprints); 78 | } 79 | static void GenerateUnused() 80 | { 81 | ModSettings.Blueprints.AutoGenerated.ForEach(entry => { 82 | if (!ModSettings.Blueprints.UsedGUIDs.ContainsKey(entry.Key)) 83 | { 84 | ModSettings.Blueprints.UnusedGUIDs[entry.Key] = entry.Value; 85 | } 86 | }); 87 | ModSettings.Blueprints.NewBlueprints.ForEach(entry => { 88 | if (!ModSettings.Blueprints.UsedGUIDs.ContainsKey(entry.Key)) 89 | { 90 | ModSettings.Blueprints.UnusedGUIDs[entry.Key] = entry.Value; 91 | } 92 | }); 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /LevelableAivu/Config/Blueprints.json: -------------------------------------------------------------------------------- 1 | { 2 | "OverrideIds": false, 3 | "NewBlueprints": { 4 | "AivuDragonfearFeature": "6d9dbeb2-8289-4673-81d4-bd3afe4f9c46", 5 | "AivuDragonfearToggle": "8d74bff4-4fd1-40a7-9a4a-643d216bfbf1", 6 | "AivuDRTier1": "5eb86bc7-396d-45eb-aa01-8319d7437c55", 7 | "AivuDRTier2": "1e5dde25-36a7-4e89-9855-3e19b48760c0", 8 | "AivuDRTier3": "9bc9f5d4-25dd-4bed-a51d-d870f64ccba1", 9 | "AivuMediumToLarge": "e28979e0-7154-4183-b9b1-fa99810db2ef", 10 | "AivuSmallToMedium": "c320e470-6005-46b0-bd07-4f98a9d4b891", 11 | "AivuUsesMythicXP": "40c16f14-dbed-4cb2-9506-08c7ba5de612", 12 | "HavocDragonClass": "c0ae6486-00bb-416b-81e0-b3e705f264c6", 13 | "HavocDragonClass20To40": "72e605ac-012e-43cf-9c75-3c69235c2d99", 14 | "HavocDragonProgress": "b3fba1ee-c6e9-4d11-8b5c-f94d1290df9c", 15 | "HavocDragonProgress2": "3fcbce70-bc95-444f-b514-9be661e003a7", 16 | "HavocDragonSpellList": "5972da2e-e772-4377-b85f-c91363c4b6c8", 17 | "AivuHeroismBuff": "d21b3d90-feb2-450d-87f1-62d4f57decd8" 18 | }, 19 | "AutoGenerated": {}, 20 | "UnusedGUIDs": {} 21 | } -------------------------------------------------------------------------------- /LevelableAivu/Config/ICollapsableGroup.cs: -------------------------------------------------------------------------------- 1 | namespace LevelableAivu.Config 2 | { 3 | public interface ICollapseableGroup 4 | { 5 | ref bool IsExpanded(); 6 | void SetExpanded(bool value); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /LevelableAivu/Config/IDisableableGroup.cs: -------------------------------------------------------------------------------- 1 | namespace LevelableAivu.Config 2 | { 3 | public interface IDisableableGroup : ICollapseableGroup 4 | { 5 | bool GroupIsDisabled(); 6 | void SetGroupDisabled(bool value); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /LevelableAivu/Config/IUpdateableSettings.cs: -------------------------------------------------------------------------------- 1 | namespace LevelableAivu.Config 2 | { 3 | public interface IUpdatableSettings 4 | { 5 | void OverrideSettings(IUpdatableSettings userSettings); 6 | void Init(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /LevelableAivu/Config/ModSettings.cs: -------------------------------------------------------------------------------- 1 |  2 | using Newtonsoft.Json; 3 | using System.IO; 4 | using System.Reflection; 5 | using static UnityModManagerNet.UnityModManager; 6 | 7 | namespace LevelableAivu.Config 8 | { 9 | class ModSettings 10 | { 11 | public static ModEntry ModEntry; 12 | public static Settings Settings; 13 | public static Blueprints Blueprints; 14 | 15 | private static string userConfigFolder => ModEntry.Path + "UserSettings"; 16 | private static string localizationFolder => ModEntry.Path + "Localization"; 17 | private static JsonSerializerSettings cachedSettings; 18 | private static JsonSerializerSettings SerializerSettings 19 | { 20 | get 21 | { 22 | if (cachedSettings == null) 23 | { 24 | cachedSettings = new JsonSerializerSettings 25 | { 26 | CheckAdditionalContent = false, 27 | ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor, 28 | DefaultValueHandling = DefaultValueHandling.Include, 29 | FloatParseHandling = FloatParseHandling.Double, 30 | Formatting = Formatting.Indented, 31 | MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead, 32 | MissingMemberHandling = MissingMemberHandling.Ignore, 33 | NullValueHandling = NullValueHandling.Ignore, 34 | ObjectCreationHandling = ObjectCreationHandling.Replace, 35 | StringEscapeHandling = StringEscapeHandling.Default, 36 | }; 37 | } 38 | return cachedSettings; 39 | } 40 | } 41 | 42 | public static void LoadAllSettings() 43 | { 44 | LoadSettings("Settings.json", ref Settings); 45 | LoadSettings("Blueprints.json", ref Blueprints); 46 | 47 | } 48 | 49 | 50 | private static void LoadSettings(string fileName, ref T setting) where T : IUpdatableSettings 51 | { 52 | JsonSerializer serializer = JsonSerializer.Create(SerializerSettings); 53 | var assembly = Assembly.GetExecutingAssembly(); 54 | var resourcePath = $"LevelableAivu.Config.{fileName}"; 55 | var userPath = $"{userConfigFolder}{Path.DirectorySeparatorChar}{fileName}"; 56 | 57 | Directory.CreateDirectory(userConfigFolder); 58 | using (Stream stream = assembly.GetManifestResourceStream(resourcePath)) 59 | using (StreamReader streamReader = new StreamReader(stream)) 60 | using (JsonReader jsonReader = new JsonTextReader(streamReader)) 61 | { 62 | setting = serializer.Deserialize(jsonReader); 63 | setting.Init(); 64 | } 65 | if (File.Exists(userPath)) 66 | { 67 | using (StreamReader streamReader = File.OpenText(userPath)) 68 | using (JsonReader jsonReader = new JsonTextReader(streamReader)) 69 | { 70 | try 71 | { 72 | T userSettings = serializer.Deserialize(jsonReader); 73 | setting.OverrideSettings(userSettings); 74 | } 75 | catch 76 | { 77 | Main.Error("Failed to load user settings. Settings will be rebuilt."); 78 | try { File.Copy(userPath, userConfigFolder + $"{Path.DirectorySeparatorChar}BROKEN_{fileName}", true); } catch { Main.Error("Failed to archive broken settings."); } 79 | } 80 | } 81 | } 82 | SaveSettings(fileName, setting); 83 | } 84 | 85 | public static void SaveSettings(string fileName, IUpdatableSettings setting) 86 | { 87 | Directory.CreateDirectory(userConfigFolder); 88 | var userPath = $"{userConfigFolder}{Path.DirectorySeparatorChar}{fileName}"; 89 | 90 | JsonSerializer serializer = JsonSerializer.Create(SerializerSettings); 91 | using (StreamWriter streamWriter = new StreamWriter(userPath)) 92 | using (JsonWriter jsonWriter = new JsonTextWriter(streamWriter)) 93 | { 94 | serializer.Serialize(jsonWriter, setting); 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /LevelableAivu/Config/SettingGroup.cs: -------------------------------------------------------------------------------- 1 | using Kingmaker.Utility; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace LevelableAivu.Config 6 | { 7 | public class SettingGroup : IDisableableGroup 8 | { 9 | public bool DisableAll = false; 10 | public virtual bool GroupIsDisabled() => DisableAll; 11 | public virtual void SetGroupDisabled(bool value) => DisableAll = value; 12 | public SortedDictionary Settings = new SortedDictionary(); 13 | public virtual bool this[string key] => IsEnabled(key); 14 | public bool IsExpanded = true; 15 | 16 | public void LoadSettingGroup(SettingGroup group, bool frozen) 17 | { 18 | DisableAll = group.DisableAll; 19 | if (frozen) 20 | { 21 | this.Settings.Keys.ToList().ForEach(key => { 22 | Settings[key].Enabled = false; 23 | }); 24 | } 25 | this.DisableAll = group.DisableAll; 26 | group.Settings.ForEach(entry => { 27 | if (Settings.ContainsKey(entry.Key)) 28 | { 29 | Settings[entry.Key].Enabled = entry.Value.Enabled; 30 | } 31 | }); 32 | } 33 | public virtual bool IsEnabled(string key) 34 | { 35 | if (!Settings.TryGetValue(key, out SettingData result)) 36 | { 37 | Main.LogDebug($"COULD NOT FIND SETTING KEY: {key}"); 38 | } 39 | return result.Enabled && !GroupIsDisabled(); 40 | } 41 | public virtual bool IsDisabled(string key) 42 | { 43 | return !IsEnabled(key); 44 | } 45 | 46 | public virtual void ChangeSetting(string key, bool value) 47 | { 48 | if (GroupIsDisabled()) 49 | { 50 | return; 51 | } 52 | Settings[key].Enabled = value; 53 | } 54 | 55 | ref bool ICollapseableGroup.IsExpanded() 56 | { 57 | return ref IsExpanded; 58 | } 59 | 60 | public void SetExpanded(bool value) 61 | { 62 | IsExpanded = value; 63 | } 64 | 65 | public class SettingData 66 | { 67 | public bool Enabled; 68 | public bool Homebrew; 69 | public string Description; 70 | 71 | public static implicit operator SettingData(bool enabled) 72 | { 73 | return new SettingData 74 | { 75 | Enabled = enabled, 76 | Description = string.Empty 77 | }; 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /LevelableAivu/Config/Settings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace LevelableAivu.Config 8 | { 9 | public class Settings : IUpdatableSettings 10 | { 11 | public bool NewSettingsOffByDefault = false; 12 | public SettingGroup settings = new SettingGroup(); 13 | 14 | public void OverrideSettings(IUpdatableSettings userSettings) 15 | { 16 | var loaded = userSettings as Settings; 17 | NewSettingsOffByDefault = loaded.NewSettingsOffByDefault; 18 | 19 | settings.LoadSettingGroup(loaded.settings, NewSettingsOffByDefault); 20 | } 21 | 22 | public void Init() 23 | { 24 | 25 | } 26 | 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LevelableAivu/Config/Settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "NewSettingsOffByDefault": false, 3 | "Settings": { 4 | "DisableAll": false, 5 | "Settings": { 6 | "AddBardSpellsToList": { 7 | "Enabled": true, 8 | "Description": "Give Aivu Bard Spell Access Per Tabletop Havoc Dragons" 9 | 10 | } 11 | 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /LevelableAivu/CreateHavocDragonClass.cs: -------------------------------------------------------------------------------- 1 | using BlueprintCore.Blueprints.Configurators; 2 | using BlueprintCore.Blueprints.Configurators.Classes; 3 | using BlueprintCore.Blueprints.CustomConfigurators; 4 | using BlueprintCore.Blueprints.CustomConfigurators.Classes; 5 | using BlueprintCore.Blueprints.CustomConfigurators.UnitLogic.Buffs; 6 | using BlueprintCore.Utils; 7 | using HarmonyLib; 8 | using Kingmaker; 9 | using Kingmaker.Blueprints; 10 | using Kingmaker.Blueprints.Classes; 11 | using Kingmaker.Blueprints.Classes.Prerequisites; 12 | using Kingmaker.Blueprints.Classes.Selection; 13 | using Kingmaker.Blueprints.Classes.Spells; 14 | using Kingmaker.Blueprints.Facts; 15 | using Kingmaker.Blueprints.JsonSystem; 16 | using Kingmaker.Blueprints.Root; 17 | using Kingmaker.Designers.EventConditionActionSystem.Actions; 18 | using Kingmaker.Designers.EventConditionActionSystem.Conditions; 19 | using Kingmaker.Designers.EventConditionActionSystem.Evaluators; 20 | using Kingmaker.Designers.EventConditionActionSystem.Events; 21 | using Kingmaker.Designers.Mechanics.Buffs; 22 | using Kingmaker.Designers.Mechanics.Facts; 23 | using Kingmaker.ElementsSystem; 24 | using Kingmaker.EntitySystem.Stats; 25 | using Kingmaker.UnitLogic.Abilities; 26 | using Kingmaker.UnitLogic.Abilities.Blueprints; 27 | using Kingmaker.UnitLogic.Abilities.Components; 28 | using Kingmaker.UnitLogic.Abilities.Components.AreaEffects; 29 | using Kingmaker.UnitLogic.ActivatableAbilities; 30 | using Kingmaker.UnitLogic.Buffs.Blueprints; 31 | using Kingmaker.UnitLogic.FactLogic; 32 | using Kingmaker.UnitLogic.Mechanics.Actions; 33 | using Kingmaker.UnitLogic.Mechanics.Components; 34 | using LevelableAivu.Config; 35 | using System; 36 | using System.Collections.Generic; 37 | using System.Linq; 38 | using System.Text; 39 | using System.Threading.Tasks; 40 | 41 | namespace LevelableAivu.Create 42 | { 43 | 44 | 45 | class CreateHavocDragonClass 46 | { 47 | 48 | 49 | [HarmonyPatch(typeof(BlueprintsCache), "Init")] 50 | [HarmonyPriority(Priority.First)] 51 | static class BlueprintsCache_Init_Patch 52 | { 53 | 54 | static BlueprintFeature AivuUsesMythixXPNew; 55 | static BlueprintCharacterClass HavocDragon; 56 | static BlueprintSpellbook HavocDragonSpellbookLoaded; 57 | 58 | static BlueprintSpellList HavocDragonSpellListNew; 59 | static bool Initialized; 60 | 61 | [HarmonyPriority(Priority.First)] 62 | static void Postfix() 63 | { 64 | 65 | if (Initialized) 66 | return; 67 | Initialized = true; 68 | 69 | BuildHavocDragonClasses(); 70 | RemoveUnneededElements(); 71 | AddFlagsToAivu(); 72 | FixHeroismAuraSFX(); 73 | 74 | } 75 | 76 | private static void FixHeroismAuraSFX() 77 | { 78 | 79 | var auraSource = BlueprintTool.Get("17831f3fa25cf52458a34b0acc034b40"); 80 | var aoe = BlueprintTool.Get("ce6652b6fb8d1504181a9f3e2aa520e3"); 81 | var baseHeroism = BlueprintTool.Get("87ab2fed7feaaff47b62a3320a57ad8d"); 82 | 83 | 84 | 85 | auraSource.FxOnStart = auraSource.FxOnRemove; 86 | 87 | 88 | 89 | var knockoffConfig = BuffConfigurator.New("AivuHeroismBuff", GUIDs.AivuHeroismBuffGUID); 90 | knockoffConfig.SetDisplayName(baseHeroism.m_DisplayName); 91 | knockoffConfig.SetDescription(baseHeroism.m_Description); 92 | knockoffConfig.SetFlags(baseHeroism.m_Flags); 93 | foreach(var c in baseHeroism.Components) 94 | { 95 | knockoffConfig.AddComponent(c); 96 | } 97 | knockoffConfig.SetDescriptionShort(baseHeroism.m_DescriptionShort); 98 | knockoffConfig.SetIcon(baseHeroism.m_Icon); 99 | var knockoff = knockoffConfig.Configure(); 100 | 101 | var applier = aoe.Components.OfType().FirstOrDefault(); 102 | 103 | if (applier != null) 104 | { 105 | 106 | applier.m_Buff = knockoff.ToReference(); 107 | } 108 | } 109 | 110 | private static void AddFlagsToAivu() 111 | { 112 | UnitConfigurator.For("32a037e97c3d5c54b85da8f639616c57").AddFacts(new List>() { AivuUsesMythixXPNew}).RemoveComponents(x=> x is LockEquipmentSlot y && y.m_SlotType == LockEquipmentSlot.SlotType.Armor).Configure(); 113 | 114 | 115 | 116 | 117 | } 118 | 119 | static void RemoveUnneededElements() 120 | { 121 | if (ModSettings.Settings.settings.GroupIsDisabled()) 122 | return; 123 | 124 | BlueprintProgression AzataProgressionLoaded = BlueprintTool.Get("9db53de4bf21b564ca1a90ff5bd16586"); 125 | BlueprintFeature T2PassToAivuFeatureLoaded = BlueprintTool.Get("4d9785fa28ab443289497ccb05e49fe2"); 126 | BlueprintFeature T3PassToAivuFeatureLoaded = BlueprintTool.Get("1bfc72ee31e349ab91991d14e1db471e"); 127 | BlueprintFeature T4PassToAivuFeatureLoaded = BlueprintTool.Get("e0cd072417ac444a99e83eae51eea8df"); 128 | AzataProgressionLoaded.LevelEntries.FirstOrDefault(x => x.Level == 3).m_Features.Remove(T2PassToAivuFeatureLoaded.ToReference()); 129 | 130 | AzataProgressionLoaded.LevelEntries.FirstOrDefault(x => x.Level == 5).m_Features.Remove(T3PassToAivuFeatureLoaded.ToReference()); 131 | 132 | AzataProgressionLoaded.LevelEntries.FirstOrDefault(x => x.Level == 7).m_Features.Remove(T4PassToAivuFeatureLoaded.ToReference()); 133 | } 134 | 135 | static void BuildHavocDragonClasses() 136 | { 137 | 138 | BlueprintUnit AivuUnitLoaded = BlueprintTool.Get("32a037e97c3d5c54b85da8f639616c57"); 139 | AivuUsesMythixXPNew = Helpers.CreateBlueprint("AivuUsesMythicXP", x => 140 | { 141 | 142 | //x.SetName("Aivu Mythic Powers"); 143 | //x.SetDescription("Aivu is amped up by Azata Mythic Power"); 144 | x.Ranks = 1; 145 | x.ReapplyOnLevelUp = true; 146 | x.AddComponent(Helpers.Create(x => 147 | { 148 | x.m_Feature = AddMechanicsFeature.MechanicsFeatureType.LegendaryHero; 149 | })); 150 | 151 | }); 152 | FeatureConfigurator.For(AivuUsesMythixXPNew).SetDisplayName(LocalizationTool.CreateString("AivuUsesMythicXP.Name", "Aivu Mythic Powers")).SetDescription(LocalizationTool.CreateString("AivuUsesMythicXP.Desc", "Aivu is amped up by Azata Mythic Power")).Configure(); 153 | Main.LogPatch("Added",AivuUsesMythixXPNew); 154 | 155 | 156 | BlueprintAbility HavocBreathLoaded = BlueprintTool.Get("42a9104e5cff51f46996d7d1ad65c0a6"); 157 | BlueprintFeature SmartBreathWeapon = BlueprintTool.Get("491c677a0a602c34fbd9530ff53d6d4a"); 158 | BlueprintBuff DragonFearIconSourceLoaded = BlueprintTool.Get("c0e8f767f87ac354495865ce3dc3ee46"); 159 | 160 | 161 | BlueprintBuff DragonFearBuffLoaded = BlueprintTool.Get("3d01fc3ae83ca834ba645013315aaec0"); 162 | 163 | 164 | BlueprintActivatableAbility AivuDragonfearAbility = Helpers.CreateBlueprint("AivuDragonfearToggle", x => 165 | { 166 | 167 | x.m_Buff = DragonFearBuffLoaded.ToReference(); 168 | x.SetName("Havoc Dragonfear"); 169 | x.SetDescription("Aivu becomes very scary. Opponents within range may become frightened or shaken. Opponents with less than Aivu's HD within 30 feet will be shaken if they fail a will save - chaff with less then 4 HD are friegtened."); 170 | x.m_Icon = DragonFearIconSourceLoaded.m_Icon; 171 | x.IsOnByDefault = true; 172 | x.DoNotTurnOffOnRest = true; 173 | x.DeactivateImmediately = true; 174 | 175 | }); 176 | Main.LogPatch("Added", AivuDragonfearAbility); 177 | BlueprintFeature AivuSizeUpToMedium = BlueprintTool.Get("50853b0623b844ac86129db459907797"); 178 | AivuSizeUpToMedium.SetName("Aivu Size Up"); 179 | AivuSizeUpToMedium.SetDescription("Aivu Is Now Medium Size"); 180 | AivuSizeUpToMedium.HideInCharacterSheetAndLevelUp = false; 181 | AivuSizeUpToMedium.HideInUI = false; 182 | Main.LogPatch("Added", AivuSizeUpToMedium); 183 | ChangeUnitSize mediumMechanic = AivuSizeUpToMedium.Components.OfType().FirstOrDefault(); 184 | if (mediumMechanic != null) 185 | { 186 | 187 | 188 | mediumMechanic.SizeDelta = 0; 189 | AivuSizeUpToMedium.AddComponent(new VariableBaseSize() 190 | { 191 | Shift = 1 192 | }); 193 | 194 | Main.LogPatch("Patched Size", AivuSizeUpToMedium); 195 | 196 | } 197 | else 198 | { 199 | Main.Log("Medium Mechanic not found"); 200 | } 201 | 202 | 203 | BlueprintFeature AivuSizeUpToMediumDummy = Helpers.CreateBlueprint("AivuSmallToMedium", x => 204 | { 205 | x.SetName("Aivu Size Up Dummy"); 206 | x.SetDescription("If you see this, respec"); 207 | 208 | }); 209 | BlueprintFeature AivuSizeUpToLargeDummy = Helpers.CreateBlueprint("AivuMediumToLarge", x => 210 | { 211 | x.SetName("Aivu Size Up Dummy"); 212 | x.SetDescription("If you see this, respec"); 213 | 214 | }); 215 | 216 | 217 | 218 | BlueprintFeature AivuDragonfear = Helpers.CreateBlueprint("AivuDragonfearFeature", x => 219 | { 220 | x.SetName("Frightful Presence"); 221 | x.SetDescription("Whenever an opponent with fewer hit dice than Aivu comes within a 30 feet range of her, they must make a Will saving throw. If it fails, the opponent becomes shaken (or, if they have less than 5 hit dice, panicked) for 5d6 rounds. A successful saving throw makes the creature immune to Aivu's Frightful Presence for 24 hours. This is a mind-affecting fear effect."); 222 | x.AddComponent(Helpers.Create(c => 223 | { 224 | c.m_Facts = new BlueprintUnitFactReference[] { 225 | Resources.GetBlueprint("a2e0cbebe3bb4a90a22b75d3c22d952c").ToReference(), 226 | }; 227 | })); 228 | 229 | 230 | 231 | 232 | }); 233 | Main.LogPatch("Added", AivuDragonfear); 234 | HavocDragonSpellbookLoaded = BlueprintTool.Get("778f544f8ed404649a261dce9d514655"); 235 | 236 | HavocDragonSpellbookLoaded.Name = LocalizationTool.CreateString(HavocDragonSpellbookLoaded.name + ".Name", "Havoc Dragon", false);//This fixes a base game UI bug, it stays 237 | HavocDragonSpellListNew = Helpers.CreateBlueprint("HavocDragonSpellList", x => 238 | { 239 | x.FilterBySchool = true; 240 | }); 241 | HavocDragonSpellbookLoaded.m_SpellList = HavocDragonSpellListNew.ToReference(); 242 | 243 | BlueprintProgression HavocDragonProgressionAdded = Helpers.CreateBlueprint("HavocDragonProgress", x => 244 | { 245 | x.IsClassFeature = true; 246 | x.SetName(""); 247 | x.SetDescription(""); 248 | }); 249 | Main.LogPatch("Added", HavocDragonProgressionAdded); 250 | BlueprintProgression HavocDragonT2ProgressionAdded = Helpers.CreateBlueprint("HavocDragonProgress2", x => 251 | { 252 | x.IsClassFeature = true; 253 | x.SetName(""); 254 | x.SetDescription(""); 255 | }); 256 | Main.LogPatch("Added", HavocDragonT2ProgressionAdded); 257 | BlueprintStatProgression saveHigh = BlueprintTool.Get("ff4662bde9e75f145853417313842751"); 258 | BlueprintStatProgression bab = BlueprintTool.Get("b3057560ffff3514299e8b93e7648a9d"); 259 | HavocDragon = Helpers.CreateBlueprint("HavocDragonClass", bp => 260 | { 261 | bp.HitDie = Kingmaker.RuleSystem.DiceType.D12; 262 | bp.m_FortitudeSave = saveHigh.ToReference(); 263 | bp.m_ReflexSave = saveHigh.ToReference(); 264 | bp.m_WillSave = saveHigh.ToReference(); 265 | bp.m_BaseAttackBonus = bab.ToReference(); 266 | bp.name = "HavocDragonClass"; 267 | bp.m_Progression = HavocDragonProgressionAdded.ToReference(); 268 | bp.m_SignatureAbilities = new BlueprintFeatureReference[] 269 | { 270 | HavocBreathLoaded.ToReference() 271 | }; 272 | 273 | 274 | 275 | bp.HideIfRestricted = true; 276 | bp.LocalizedName = LocalizationTool.CreateString(bp.name + ".Name", "Havoc Dragon", false); 277 | bp.LocalizedDescription = LocalizationTool.CreateString(bp.name + "Desc.Name", "A dragon is a reptilelike creature, usually winged, with magical or unusual abilities", false); 278 | bp.LocalizedDescriptionShort = LocalizationTool.CreateString(bp.name + "Desc.Name", "A dragon is a reptilelike creature, usually winged, with magical or unusual abilities", false); 279 | 280 | bp.SkillPoints = 6; 281 | bp.m_SignatureAbilities = new BlueprintFeatureReference[] { }; 282 | bp.ClassSkills = new Kingmaker.EntitySystem.Stats.StatType[] 283 | { 284 | StatType.SkillAthletics, StatType.SkillMobility, StatType.SkillPerception, StatType.SkillPersuasion, StatType.SkillLoreReligion, StatType.SkillKnowledgeArcana, StatType.SkillStealth, StatType.SkillLoreNature 285 | }; 286 | bp.m_Spellbook = HavocDragonSpellbookLoaded.ToReference(); 287 | 288 | bp.AddComponent(Helpers.Create(x => 289 | { 290 | 291 | x.m_Feature = AivuUsesMythixXPNew.ToReference(); 292 | })); 293 | 294 | 295 | 296 | }); 297 | Main.LogPatch("Added", HavocDragon); 298 | BlueprintCharacterClass MythicHavocDragon = Helpers.CreateBlueprint("HavocDragonClass20To40", bp => 299 | { 300 | bp.HitDie = HavocDragon.HitDie; 301 | bp.m_FortitudeSave = HavocDragon.m_FortitudeSave; 302 | bp.m_WillSave = HavocDragon.m_WillSave; 303 | bp.m_ReflexSave = HavocDragon.m_ReflexSave; 304 | bp.m_BaseAttackBonus = HavocDragon.m_BaseAttackBonus; 305 | bp.name = "HavocDragon20To40"; 306 | bp.m_Progression = HavocDragonT2ProgressionAdded.ToReference(); 307 | bp.HideIfRestricted = true; 308 | bp.LocalizedName = LocalizationTool.CreateString(bp.name + ".Name", "Mythic Havoc Dragon", false); 309 | bp.LocalizedDescription = HavocDragon.LocalizedDescription; 310 | bp.LocalizedDescriptionShort = HavocDragon.LocalizedDescriptionShort; 311 | 312 | bp.SkillPoints = 6; 313 | bp.m_SignatureAbilities = new BlueprintFeatureReference[] { }; 314 | bp.ClassSkills = new Kingmaker.EntitySystem.Stats.StatType[] 315 | { 316 | StatType.SkillAthletics, StatType.SkillMobility, StatType.SkillPerception, StatType.SkillPersuasion, StatType.SkillLoreReligion, StatType.SkillKnowledgeArcana, StatType.SkillStealth, StatType.SkillLoreNature, StatType.SkillKnowledgeWorld, StatType.SkillUseMagicDevice, StatType.SkillStealth 317 | }; 318 | bp.m_Spellbook = HavocDragonSpellbookLoaded.ToReference(); 319 | bp.AddComponent(Helpers.Create(x => 320 | { 321 | 322 | x.m_Feature = AivuUsesMythixXPNew.ToReference(); 323 | })); 324 | 325 | bp.AddComponent(Helpers.Create(x => 326 | { 327 | x.m_CharacterClass = HavocDragon.ToReference(); 328 | x.Level = 20; 329 | })); 330 | 331 | }); 332 | Main.LogPatch("Added", MythicHavocDragon); 333 | HavocDragon.AddComponent(x => 334 | { 335 | x.Not = true; 336 | x.m_CharacterClass = MythicHavocDragon.ToReference(); 337 | x.Level = 1; 338 | 339 | }); 340 | HavocDragon.AddComponent(x => 341 | { 342 | x.Not = true; 343 | x.m_CharacterClass = HavocDragon.ToReference(); 344 | x.Level = 20; 345 | 346 | }); 347 | void AddToClasslLevelEntry(BlueprintProgression progression, int level, BlueprintFeature element) 348 | { 349 | 350 | if (progression.LevelEntries == null) 351 | { 352 | 353 | progression.LevelEntries = new LevelEntry[] { }; 354 | } 355 | 356 | if (progression.LevelEntries.FirstOrDefault(x => x.Level == level) == null) 357 | { 358 | 359 | LevelEntry[] arr = progression.LevelEntries; 360 | 361 | LevelEntry add = new LevelEntry 362 | { 363 | m_Features = new List() { element.ToReference() }, 364 | Level = level 365 | 366 | }; 367 | LevelEntry[] arr2 = arr.Append(add).ToArray(); 368 | 369 | 370 | progression.LevelEntries = arr2; 371 | 372 | 373 | } 374 | else 375 | { 376 | 377 | 378 | progression.LevelEntries.First(x => x.Level == level).m_Features.Add(element.ToReference()); 379 | } 380 | Main.LogPatch($"Progression {progression.NameSafe()} patched: ", element); 381 | 382 | } 383 | 384 | 385 | BlueprintFeature DragonType = Resources.GetBlueprint("455ac88e22f55804ab87c2467deff1d6"); 386 | #region Tier One Aivu Upgrades 387 | //Feature Block 1 - T2 upgrade. MR5, HD range 16 - 20 388 | BlueprintFeature AzataDragonDR1 = Helpers.CreateBlueprint("AivuDRTier1", x => 389 | { 390 | x.IsClassFeature = true; 391 | x.SetName("Havoc Dragon DR, Lesser"); 392 | x.SetDescription("DR 5/Lawful"); 393 | AddDamageResistancePhysical dr = Helpers.Create(x => 394 | { 395 | x.Alignment = Kingmaker.Enums.Damage.DamageAlignment.Lawful; 396 | x.BypassedByAlignment = true; 397 | x.Value = new Kingmaker.UnitLogic.Mechanics.ContextValue 398 | { 399 | Value = 5 400 | }; 401 | }); 402 | 403 | x.AddComponent(dr); 404 | 405 | 406 | 407 | }); 408 | 409 | Main.LogPatch("Added", AzataDragonDR1); 410 | #endregion 411 | //End t1 block 412 | #region Teir 2 Aivu Upgrades - MR 7 413 | BlueprintFeature AzataDragonDR2 = Helpers.CreateBlueprint("AivuDRTier2", x => 414 | { 415 | x.IsClassFeature = true; 416 | x.SetName("Havoc Dragon DR"); 417 | x.SetDescription("DR 15/Lawful"); 418 | AddDamageResistancePhysical dr = Helpers.Create(x => 419 | { 420 | x.Alignment = Kingmaker.Enums.Damage.DamageAlignment.Lawful; 421 | x.BypassedByAlignment = true; 422 | x.Value = new Kingmaker.UnitLogic.Mechanics.ContextValue 423 | { 424 | Value = 15 425 | }; 426 | }); 427 | 428 | x.AddComponent(dr); 429 | 430 | 431 | 432 | }); 433 | Main.LogPatch("Added", AzataDragonDR2); 434 | BlueprintFeature AivuSizeUpToLarge = Resources.GetBlueprint("600c4d652b6e4684a7a4b77946903c30"); 435 | AivuSizeUpToLarge.SetName("Aivu Size Up"); 436 | AivuSizeUpToLarge.SetDescription("Aivu Is Now Large Size"); 437 | AivuSizeUpToLarge.HideInCharacterSheetAndLevelUp = false; 438 | AivuSizeUpToLarge.HideInUI = false; 439 | ChangeUnitSize largeMechanic = AivuSizeUpToLarge.Components.OfType().FirstOrDefault(); 440 | 441 | 442 | 443 | 444 | if (largeMechanic != null) 445 | { 446 | 447 | largeMechanic.SizeDelta = 0; 448 | AivuSizeUpToLarge.AddComponent(new VariableBaseSize() 449 | { 450 | Shift = 1, 451 | 452 | }); 453 | 454 | Main.LogPatch("Patched Size", AivuSizeUpToLarge); 455 | 456 | } 457 | else 458 | { 459 | Main.Log("LargeMechanic not found"); 460 | } 461 | BlueprintFeature HeroicAura = Resources.GetBlueprint("bb0be011191b77f418d2225399109f0c"); 462 | 463 | Main.LogPatch("Added", AzataDragonDR2); 464 | 465 | 466 | 467 | #endregion 468 | 469 | BlueprintFeature AzataDragonDR3 = Helpers.CreateBlueprint("AivuDRTier3", x => 470 | { 471 | x.IsClassFeature = true; 472 | x.SetName("Havoc Dragon DR, Greater"); 473 | x.SetDescription("DR 20/Lawful"); 474 | AddDamageResistancePhysical dr = Helpers.Create(x => 475 | { 476 | x.Alignment = Kingmaker.Enums.Damage.DamageAlignment.Lawful; 477 | x.BypassedByAlignment = true; 478 | x.Value = new Kingmaker.UnitLogic.Mechanics.ContextValue 479 | { 480 | Value = 20 481 | }; 482 | }); 483 | 484 | x.AddComponent(dr); 485 | 486 | 487 | }); 488 | 489 | 490 | 491 | Main.LogPatch("Added", AzataDragonDR3); 492 | 493 | //int mediumLev = 2; 494 | //int largeLev = 3; 495 | int mediumLev = 16; 496 | int largeLev = 26; 497 | 498 | 499 | 500 | AddToClasslLevelEntry(HavocDragonProgressionAdded, 1, DragonType); 501 | 502 | AddToClasslLevelEntry(HavocDragonProgressionAdded, mediumLev, AivuSizeUpToMedium); 503 | if (largeLev < 21) 504 | { 505 | AddToClasslLevelEntry(HavocDragonProgressionAdded, largeLev, AivuSizeUpToLarge); 506 | } 507 | else 508 | { 509 | AddToClasslLevelEntry(HavocDragonT2ProgressionAdded, largeLev - 20, AivuSizeUpToLarge); 510 | } 511 | 512 | 513 | //AddToClasslLevelEntry(HavocDragonProgressionAdded, 16, AivuSizeUpToMedium); 514 | AddToClasslLevelEntry(HavocDragonProgressionAdded, 16, SmartBreathWeapon); 515 | 516 | AddToClasslLevelEntry(HavocDragonProgressionAdded, 17, AivuDragonfear); 517 | AddToClasslLevelEntry(HavocDragonProgressionAdded, 18, AzataDragonDR1); 518 | //AddToClasslLevelEntry(HavocDragonT2ProgressionAdded, 6, AivuSizeUpToLarge); 519 | //AddToClasslLevelEntry(HavocDragonProgressionAdded, 6, HeroicAura); 520 | AddToClasslLevelEntry(HavocDragonT2ProgressionAdded, 7, HeroicAura); 521 | AddToClasslLevelEntry(HavocDragonProgressionAdded, 27, HeroicAura); 522 | AddToClasslLevelEntry(HavocDragonT2ProgressionAdded, 8, AzataDragonDR2); 523 | AddToClasslLevelEntry(HavocDragonProgressionAdded, 28, AzataDragonDR2); 524 | 525 | 526 | AddToClasslLevelEntry(HavocDragonT2ProgressionAdded, 10, AzataDragonDR3); 527 | AddToClasslLevelEntry(HavocDragonProgressionAdded, 30, AzataDragonDR3); 528 | 529 | HavocDragonProgressionAdded.m_Classes = new BlueprintProgression.ClassWithLevel[] { new BlueprintProgression.ClassWithLevel { m_Class = HavocDragon.ToReference() } }; 530 | 531 | HavocDragonT2ProgressionAdded.m_Classes = new BlueprintProgression.ClassWithLevel[] { new BlueprintProgression.ClassWithLevel { m_Class = MythicHavocDragon.ToReference() } }; 532 | 533 | 534 | HavocBreathLoaded.Components.OfType().FirstOrDefault().m_Class = HavocBreathLoaded.Components.OfType().FirstOrDefault().m_Class.Append(HavocDragon.ToReference()).ToArray(); 535 | HavocBreathLoaded.Components.OfType().FirstOrDefault().m_Class = HavocBreathLoaded.Components.OfType().FirstOrDefault().m_Class.Append(MythicHavocDragon.ToReference()).ToArray(); 536 | 537 | BlueprintRoot root = Resources.GetBlueprint("2d77316c72b9ed44f888ceefc2a131f6"); 538 | if (ModSettings.Settings.settings.GroupIsDisabled()) 539 | return; 540 | root.Progression.m_PetClasses = root.Progression.m_PetClasses.AddToArray(HavocDragon.ToReference()); 541 | root.Progression.m_PetClasses = root.Progression.m_PetClasses.AddToArray(MythicHavocDragon.ToReference()); 542 | 543 | } 544 | 545 | 546 | } 547 | } 548 | } 549 | -------------------------------------------------------------------------------- /LevelableAivu/DescriptionTools.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Text.RegularExpressions; 4 | 5 | namespace LevelableAivu 6 | { 7 | static class DescriptionTools 8 | { 9 | public static string StripEncyclopediaTags(this string str) 10 | { 11 | return Regex.Replace(str, "{.*?}", string.Empty); 12 | } 13 | 14 | private static readonly EncyclopediaEntry[] EncyclopediaEntries = new EncyclopediaEntry[] { 15 | new EncyclopediaEntry { 16 | Entry = "Strength", 17 | Patterns = { "Strength" } 18 | }, 19 | new EncyclopediaEntry { 20 | Entry = "Dexterity", 21 | Patterns = { "Dexterity" } 22 | }, 23 | new EncyclopediaEntry { 24 | Entry = "Constitution", 25 | Patterns = { "Constitution" } 26 | }, 27 | new EncyclopediaEntry { 28 | Entry = "Intelligence", 29 | Patterns = { "Intelligence" } 30 | }, 31 | new EncyclopediaEntry { 32 | Entry = "Wisdom", 33 | Patterns = { "Wisdom" } 34 | }, 35 | new EncyclopediaEntry { 36 | Entry = "Charisma", 37 | Patterns = { "Charisma" } 38 | }, 39 | new EncyclopediaEntry { 40 | Entry = "Ability_Scores", 41 | Patterns = { "Ability Scores?" } 42 | }, 43 | new EncyclopediaEntry { 44 | Entry = "Athletics", 45 | Patterns = { "Athletics" } 46 | }, 47 | new EncyclopediaEntry { 48 | Entry = "Persuasion", 49 | Patterns = { "Persuasion" } 50 | }, 51 | new EncyclopediaEntry { 52 | Entry = "Knowledge_World", 53 | Patterns = { @"Knowledge \(?World\)?" } 54 | }, 55 | new EncyclopediaEntry { 56 | Entry = "Knowledge_Arcana", 57 | Patterns = { @"Knowledge \(?Arcana\)?" } 58 | }, 59 | new EncyclopediaEntry { 60 | Entry = "Lore_Nature", 61 | Patterns = { @"Lore \(?Nature\)?" } 62 | }, 63 | new EncyclopediaEntry { 64 | Entry = "Lore_Religion", 65 | Patterns = { @"Lore \(?Religion\)?" } 66 | }, 67 | new EncyclopediaEntry { 68 | Entry = "Mobility", 69 | Patterns = { "Mobility" } 70 | }, 71 | new EncyclopediaEntry { 72 | Entry = "Perception", 73 | Patterns = { "Perception" } 74 | }, 75 | new EncyclopediaEntry { 76 | Entry = "Stealth", 77 | Patterns = { "Stealth" } 78 | }, 79 | new EncyclopediaEntry { 80 | Entry = "Trickery", 81 | Patterns = { "Trickery" } 82 | }, 83 | new EncyclopediaEntry { 84 | Entry = "Use_Magic_Device", 85 | Patterns = { 86 | "Use Magic Device", 87 | "UMD" 88 | } 89 | }, 90 | new EncyclopediaEntry { 91 | Entry = "Race", 92 | Patterns = { "Race" } 93 | }, 94 | new EncyclopediaEntry { 95 | Entry = "Alignment", 96 | Patterns = { "Alignment" } 97 | }, 98 | new EncyclopediaEntry { 99 | Entry = "Caster_Level", 100 | Patterns = { 101 | "Caster Level", 102 | "CL" 103 | } 104 | }, 105 | new EncyclopediaEntry { 106 | Entry = "DC", 107 | Patterns = { "DC" } 108 | }, 109 | new EncyclopediaEntry { 110 | Entry = "Saving_Throw", 111 | Patterns = { "Saving Throw" } 112 | }, 113 | new EncyclopediaEntry { 114 | Entry = "Spell_Resistance", 115 | Patterns = { "Spell Resistance" } 116 | }, 117 | new EncyclopediaEntry { 118 | Entry = "Spell_Fail_Chance", 119 | Patterns = { "Arcane Spell Failure" } 120 | }, 121 | new EncyclopediaEntry { 122 | Entry = "Concentration_Checks", 123 | Patterns = { "Concentration Checks?" } 124 | }, 125 | new EncyclopediaEntry { 126 | Entry = "Concealment", 127 | Patterns = { "Concealment" } 128 | }, 129 | new EncyclopediaEntry { 130 | Entry = "Bonus", 131 | Patterns = {"Bonus(es)?"} 132 | }, 133 | new EncyclopediaEntry { 134 | Entry = "Speed", 135 | Patterns = { "Speed" } 136 | }, 137 | new EncyclopediaEntry { 138 | Entry = "Flat_Footed_AC", 139 | Patterns = { 140 | "Flat Footed AC", 141 | "Flat Footed Armor Class" 142 | } 143 | }, 144 | new EncyclopediaEntry { 145 | Entry = "Flat_Footed", 146 | Patterns = { 147 | "Flat Footed", 148 | "Flat-Footed" 149 | } 150 | }, 151 | new EncyclopediaEntry { 152 | Entry = "Armor_Class", 153 | Patterns = { 154 | "Armor Class", 155 | "AC" 156 | } 157 | }, 158 | new EncyclopediaEntry { 159 | Entry = "Armor_Check_Penalty", 160 | Patterns = { "Armor Check Penalty" } 161 | }, 162 | new EncyclopediaEntry { 163 | Entry = "Damage_Reduction", 164 | Patterns = { "DR" } 165 | }, 166 | new EncyclopediaEntry { 167 | Entry = "Free_Action", 168 | Patterns = { "Free Action" } 169 | }, 170 | new EncyclopediaEntry { 171 | Entry = "Swift_Action", 172 | Patterns = { "Swift Action" } 173 | }, 174 | new EncyclopediaEntry { 175 | Entry = "Standard_Actions", 176 | Patterns = { "Standard Action" } 177 | }, 178 | new EncyclopediaEntry { 179 | Entry = "Full_Round_Action", 180 | Patterns = { "Full Round Action" } 181 | }, 182 | new EncyclopediaEntry { 183 | Entry = "Skills", 184 | Patterns = { "Skills? Checks?" } 185 | }, 186 | new EncyclopediaEntry { 187 | Entry = "Combat_Maneuvers", 188 | Patterns = { "Combat Maneuvers?" } 189 | }, 190 | new EncyclopediaEntry { 191 | Entry = "CMB", 192 | Patterns = { 193 | "Combat Maneuver Bonus", 194 | "CMB" 195 | } 196 | }, 197 | new EncyclopediaEntry { 198 | Entry = "CMD", 199 | Patterns = { 200 | "Combat Maneuver Defense", 201 | "CMD" 202 | } 203 | }, 204 | new EncyclopediaEntry { 205 | Entry = "BAB", 206 | Patterns = { 207 | "Base Attack Bonus", 208 | "BAB" 209 | } 210 | }, 211 | new EncyclopediaEntry { 212 | Entry = "Incorporeal_Touch_Attack", 213 | Patterns = { "Incorporeal Touch Attacks?" } 214 | }, 215 | new EncyclopediaEntry { 216 | Entry = "TouchAttack", 217 | Patterns = { "Touch Attacks?" } 218 | }, 219 | new EncyclopediaEntry { 220 | Entry = "NaturalAttack", 221 | Patterns = { 222 | "Natural Attacks?", 223 | "Natural Weapons?" 224 | } 225 | }, 226 | new EncyclopediaEntry { 227 | Entry = "Attack_Of_Opportunity", 228 | Patterns = { 229 | "Attacks? Of Opportunity", 230 | "AoO" 231 | } 232 | }, 233 | new EncyclopediaEntry { 234 | Entry = "Penalty", 235 | Patterns = { "Penalty" } 236 | }, 237 | new EncyclopediaEntry { 238 | Entry = "Check", 239 | Patterns = { "Checks?" } 240 | }, 241 | new EncyclopediaEntry { 242 | Entry = "Spells", 243 | Patterns = { "Spells?" } 244 | }, 245 | new EncyclopediaEntry { 246 | Entry = "Attack", 247 | Patterns = { "Attacks?" } 248 | }, 249 | new EncyclopediaEntry { 250 | Entry = "Feat", 251 | Patterns = { "Feats?" } 252 | }, 253 | new EncyclopediaEntry { 254 | Entry = "Charge", 255 | Patterns = { "Charge" } 256 | }, 257 | new EncyclopediaEntry { 258 | Entry = "Critical", 259 | Patterns = { "Critical Hit" } 260 | }, 261 | new EncyclopediaEntry { 262 | Entry = "Fast_Healing", 263 | Patterns = { "Fast Healing" } 264 | }, 265 | new EncyclopediaEntry { 266 | Entry = "Temporary_HP", 267 | Patterns = { "Temporary HP" } 268 | }, 269 | new EncyclopediaEntry { 270 | Entry = "Flanking", 271 | Patterns = { 272 | "Flanking", 273 | "Flanked" 274 | } 275 | }, 276 | new EncyclopediaEntry { 277 | Entry = "Magic_School", 278 | Patterns = { "School of Magic" } 279 | }, 280 | new EncyclopediaEntry { 281 | Entry = "Damage_Type", 282 | Patterns = { 283 | "Bludgeoning", 284 | "Piercing", 285 | "Slashing" 286 | } 287 | } 288 | }; 289 | 290 | public static string TagEncyclopediaEntries(string description) 291 | { 292 | var result = description; 293 | result = result.StripHTML(); 294 | foreach (var entry in EncyclopediaEntries) 295 | { 296 | foreach (var pattern in entry.Patterns) 297 | { 298 | result = result.ApplyTags(pattern, entry); 299 | } 300 | } 301 | return result; 302 | } 303 | 304 | private class EncyclopediaEntry 305 | { 306 | public string Entry = ""; 307 | public List Patterns = new List(); 308 | 309 | public string Tag(string keyword) 310 | { 311 | return $"{{g|Encyclopedia:{Entry}}}{keyword}{{/g}}"; 312 | } 313 | } 314 | 315 | private static string ApplyTags(this string str, string from, EncyclopediaEntry entry) 316 | { 317 | var pattern = from.EnforceSolo().ExcludeTagged(); 318 | var matches = Regex.Matches(str, pattern, RegexOptions.IgnoreCase) 319 | .OfType() 320 | .Select(m => m.Value) 321 | .Distinct(); 322 | foreach (string match in matches) 323 | { 324 | str = Regex.Replace(str, Regex.Escape(match).EnforceSolo().ExcludeTagged(), entry.Tag(match), RegexOptions.IgnoreCase); 325 | } 326 | return str; 327 | } 328 | private static string StripHTML(this string str) 329 | { 330 | return Regex.Replace(str, "<.*?>", string.Empty); 331 | } 332 | private static string ExcludeTagged(this string str) 333 | { 334 | return $"{@"(?]+)"}{str}{@"(?![^\s\.,""'<)]+)"}"; 339 | } 340 | } 341 | } 342 | -------------------------------------------------------------------------------- /LevelableAivu/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Kingmaker.Blueprints; 3 | using Kingmaker.Blueprints.Classes; 4 | using Kingmaker.Blueprints.Classes.Prerequisites; 5 | using Kingmaker.Blueprints.Classes.Selection; 6 | using Kingmaker.Blueprints.Classes.Spells; 7 | using Kingmaker.Blueprints.Facts; 8 | using Kingmaker.ElementsSystem; 9 | using Kingmaker.Localization; 10 | using Kingmaker.UnitLogic; 11 | using Kingmaker.UnitLogic.Abilities.Blueprints; 12 | using Kingmaker.UnitLogic.Abilities.Components; 13 | using Kingmaker.UnitLogic.FactLogic; 14 | using Kingmaker.UnitLogic.Mechanics.Components; 15 | using Kingmaker.Utility; 16 | using System; 17 | using System.Collections.Generic; 18 | using System.Linq; 19 | using LevelableAivu.Utilities; 20 | using UnityEngine; 21 | using static Kingmaker.Blueprints.Classes.Prerequisites.Prerequisite; 22 | using BlueprintCore.Utils; 23 | 24 | namespace LevelableAivu{ 25 | static class ExtentionMethods 26 | { 27 | 28 | 29 | 30 | 31 | 32 | 33 | public static T[] AppendToArray(this T[] array, T value) 34 | { 35 | var len = array.Length; 36 | var result = new T[len + 1]; 37 | Array.Copy(array, result, len); 38 | result[len] = value; 39 | return result; 40 | } 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | public static T[] RemoveFromArray(this T[] array, T value) 49 | { 50 | var list = array.ToList(); 51 | return list.Remove(value) ? list.ToArray() : array; 52 | } 53 | 54 | 55 | public static void AddPrerequisite(this BlueprintFeature obj, T prerequisite) where T : Prerequisite 56 | { 57 | obj.AddComponent(prerequisite); 58 | switch (prerequisite) 59 | { 60 | case PrerequisiteFeature p: 61 | var feature = p.Feature; 62 | if (feature.IsPrerequisiteFor == null) { feature.IsPrerequisiteFor = new List(); } 63 | if (!feature.IsPrerequisiteFor.Contains(obj.ToReference())) 64 | { 65 | feature.IsPrerequisiteFor.Add(obj.ToReference()); 66 | } 67 | break; 68 | case PrerequisiteFeaturesFromList p: 69 | var features = p.Features; 70 | features.ForEach(f => { 71 | if (f.IsPrerequisiteFor == null) { f.IsPrerequisiteFor = new List(); } 72 | if (!f.IsPrerequisiteFor.Contains(obj.ToReference())) 73 | { 74 | f.IsPrerequisiteFor.Add(obj.ToReference()); 75 | } 76 | }); 77 | break; 78 | default: 79 | break; 80 | } 81 | } 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | public static void AddComponent(this BlueprintScriptableObject obj, BlueprintComponent component) 94 | { 95 | obj.SetComponents(obj.ComponentsArray.AppendToArray(component)); 96 | } 97 | 98 | public static void AddComponent(this BlueprintScriptableObject obj, Action init = null) where T : BlueprintComponent, new() 99 | { 100 | obj.SetComponents(obj.ComponentsArray.AppendToArray(Helpers.Create(init))); 101 | } 102 | 103 | 104 | 105 | public static void RemoveComponents(this BlueprintScriptableObject obj, Predicate predicate) where T : BlueprintComponent 106 | { 107 | var compnents_to_remove = obj.GetComponents().ToArray(); 108 | foreach (var c in compnents_to_remove) 109 | { 110 | if (predicate(c)) 111 | { 112 | obj.SetComponents(obj.ComponentsArray.RemoveFromArray(c)); 113 | } 114 | } 115 | } 116 | 117 | 118 | 119 | 120 | public static void SetComponents(this BlueprintScriptableObject obj, params BlueprintComponent[] components) 121 | { 122 | // Fix names of components. Generally this doesn't matter, but if they have serialization state, 123 | // then their name needs to be unique. 124 | var names = new HashSet(); 125 | foreach (var c in components) 126 | { 127 | if (string.IsNullOrEmpty(c.name)) 128 | { 129 | c.name = $"${c.GetType().Name}"; 130 | } 131 | if (!names.Add(c.name)) 132 | { 133 | String name; 134 | for (int i = 0; !names.Add(name = $"{c.name}${i}"); i++) ; 135 | c.name = name; 136 | } 137 | } 138 | obj.ComponentsArray = components; 139 | obj.OnEnable(); // To make sure components are fully initialized 140 | } 141 | 142 | 143 | 144 | 145 | 146 | 147 | public static void SetName(this BlueprintUnitFact feature, String name) 148 | { 149 | feature.m_DisplayName = LocalizationTool.CreateString(feature.name + ".Name", name,false); 150 | } 151 | 152 | 153 | 154 | 155 | public static void SetDescription(this BlueprintUnitFact feature, String description) 156 | { 157 | var taggedDescription = DescriptionTools.TagEncyclopediaEntries(description); 158 | feature.m_Description = LocalizationTool.CreateString(feature.name + ".Description", taggedDescription, false); 159 | } 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /LevelableAivu/FinishingTouches.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Kingmaker.Assets.UnitLogic.Mechanics.Properties; 3 | using Kingmaker.Blueprints; 4 | using Kingmaker.Blueprints.Classes; 5 | using Kingmaker.Blueprints.Classes.Prerequisites; 6 | using Kingmaker.Blueprints.Classes.Spells; 7 | using Kingmaker.Blueprints.JsonSystem; 8 | using Kingmaker.Blueprints.Root; 9 | using Kingmaker.EntitySystem.Entities; 10 | using Kingmaker.UI.MVVM._VM.CharGen; 11 | using Kingmaker.UnitLogic.Abilities.Blueprints; 12 | using Kingmaker.UnitLogic.Class.LevelUp; 13 | using Kingmaker.UnitLogic.Mechanics.Properties; 14 | using Kingmaker.Utility; 15 | using LevelableAivu.Config; 16 | using System; 17 | using System.Collections.Generic; 18 | using System.Linq; 19 | using System.Text; 20 | using System.Threading.Tasks; 21 | 22 | namespace LevelableAivu 23 | { 24 | class FinishingTouches 25 | { 26 | [HarmonyPatch(typeof(BlueprintsCache), "Init")] 27 | [HarmonyPriority(Priority.Last)] 28 | static class BlueprintsCache_Init_Patch 29 | { 30 | static bool Initialized; 31 | [HarmonyPriority(Priority.VeryLow)] 32 | static void Postfix() 33 | { 34 | if (ModSettings.Settings.settings.GroupIsDisabled()) 35 | return; 36 | else 37 | { 38 | AlterSpellBook(); 39 | AlterCompanionClasses(); 40 | AlterBarding(); 41 | FixDestructiveDispel(); 42 | } 43 | 44 | 45 | 46 | 47 | } 48 | 49 | private static void FixDestructiveDispel() 50 | { 51 | BlueprintCharacterClass HavocDragonAdded = Resources.GetModBlueprint("HavocDragonClass"); 52 | BlueprintCharacterClass HavocDragon2Added = Resources.GetModBlueprint("HavocDragonClass20To40"); 53 | var dispelProp = Resources.GetBlueprint("13e4f1dd08954723b173335a54b48746"); 54 | var attributeProp = dispelProp.Components.OfType().FirstOrDefault(); 55 | if (attributeProp != null) 56 | { 57 | attributeProp.m_Classes = attributeProp.m_Classes.AddItem(HavocDragonAdded.ToReference()).ToArray(); 58 | } 59 | var levelProp = dispelProp.Components.OfType().FirstOrDefault(); 60 | if (levelProp != null) 61 | { 62 | levelProp.m_Class = levelProp.m_Class.AddItem(HavocDragonAdded.ToReference()).ToArray(); 63 | levelProp.m_Class = levelProp.m_Class.AddItem(HavocDragon2Added.ToReference()).ToArray(); 64 | } 65 | 66 | } 67 | 68 | private static void AlterCompanionClasses() 69 | { 70 | BlueprintFeature AivuUsesMythicXPNow = Resources.GetModBlueprint("AivuUsesMythicXP"); 71 | BlueprintRoot root = Resources.GetBlueprint("2d77316c72b9ed44f888ceefc2a131f6"); 72 | 73 | foreach (BlueprintCharacterClass c in root.Progression.m_PetClasses) 74 | { 75 | if (!c.name.Contains("Havoc")) 76 | { 77 | c.AddComponent(Helpers.Create(x => 78 | { 79 | x.Group = Prerequisite.GroupType.All; 80 | x.m_Feature = AivuUsesMythicXPNow.ToReference(); 81 | })); 82 | } 83 | 84 | } 85 | 86 | 87 | } 88 | 89 | static void AlterSpellBook() 90 | { 91 | 92 | 93 | BlueprintSpellList clericList = Resources.GetBlueprint("8443ce803d2d31347897a3d85cc32f53"); 94 | BlueprintSpellList bardList = Resources.GetBlueprint("25a5013493bdcf74bb2424532214d0c8"); 95 | BlueprintSpellList HavocDragonList = Resources.GetModBlueprint("HavocDragonSpellList"); 96 | for (int i = 0; i < 10; i++) 97 | { 98 | 99 | List clericSpells = clericList.GetSpells(i); 100 | List bardSpells = bardList.GetSpells(i); 101 | SpellLevelList havocDragonSpells = new SpellLevelList(i); 102 | foreach (BlueprintAbility s in clericSpells) 103 | { 104 | 105 | if (!havocDragonSpells.m_Spells.Contains(s.ToReference()) && !HavocDragonList.Contains(s)) 106 | { 107 | havocDragonSpells.m_Spells.Add(s.ToReference()); 108 | s.AddComponent(Helpers.Create(x => 109 | { 110 | x.m_SpellList = HavocDragonList.ToReference(); 111 | x.SpellLevel = i; 112 | 113 | })); 114 | 115 | } 116 | } 117 | if (ModSettings.Settings.settings.IsEnabled("AddBardSpellsToList")) 118 | { 119 | foreach (BlueprintAbility s in bardSpells) 120 | { 121 | 122 | if (!havocDragonSpells.m_Spells.Contains(s.ToReference()) && !HavocDragonList.Contains(s)) 123 | { 124 | havocDragonSpells.m_Spells.Add(s.ToReference()); 125 | s.AddComponent(Helpers.Create(x => 126 | { 127 | x.m_SpellList = HavocDragonList.ToReference(); 128 | x.SpellLevel = i; 129 | 130 | })); 131 | 132 | } 133 | 134 | 135 | } 136 | } 137 | HavocDragonList.SpellsByLevel = HavocDragonList.SpellsByLevel.AppendToArray(havocDragonSpells); 138 | } 139 | } 140 | 141 | static void AlterBarding() 142 | { 143 | 144 | 145 | BlueprintCharacterClass HavocDragonAdded = Resources.GetModBlueprint("HavocDragonClass"); 146 | 147 | BlueprintFeature LightBardingProfLoaded = Resources.GetBlueprint("c62ba548b1a34b94b9802925b35737c2"); 148 | 149 | BlueprintFeature MediumBardingProfLoaded = Resources.GetBlueprint("7213b7bd026d4414da2308df23715d8f"); 150 | 151 | BlueprintFeature HeavyBardingProfLoaded = Resources.GetBlueprint("aed0b33e17a3b3d44a718852e87305bd"); 152 | 153 | 154 | 155 | 156 | LightBardingProfLoaded.AddPrerequisite(Helpers.Create(x => 157 | { 158 | x.m_CharacterClass = HavocDragonAdded.ToReference(); 159 | x.Group = Prerequisite.GroupType.Any; 160 | 161 | })); 162 | 163 | 164 | MediumBardingProfLoaded.AddPrerequisite(Helpers.Create(x => 165 | { 166 | x.m_CharacterClass = HavocDragonAdded.ToReference(); 167 | x.Group = Prerequisite.GroupType.Any; 168 | 169 | })); 170 | HeavyBardingProfLoaded.AddPrerequisite(Helpers.Create(x => 171 | { 172 | x.m_CharacterClass = HavocDragonAdded.ToReference(); 173 | x.Group = Prerequisite.GroupType.Any; 174 | 175 | })); 176 | 177 | 178 | } 179 | } 180 | 181 | [HarmonyPatch(typeof(CharGenVM), "NeedNamePhase")] 182 | static class NeedName_Patch 183 | { 184 | 185 | static void Postfix(CharGenVM __instance, ref bool __result) 186 | { 187 | try 188 | { 189 | 190 | if (__instance.CharacterName.ToString().Equals("Aivu")) 191 | { 192 | __result = false; 193 | return; 194 | } 195 | 196 | } 197 | catch (Exception e) { Main.Error(e.ToString()); } 198 | } 199 | } 200 | 201 | [HarmonyPatch(typeof(LevelUpState), MethodType.Constructor)] 202 | [HarmonyPatch(new Type[] { typeof(UnitEntityData), typeof(LevelUpState.CharBuildMode), typeof(bool) })] 203 | [HarmonyPriority(9999)] 204 | static class LevelUpState_ctor_Patch 205 | { 206 | 207 | private static void Postfix(LevelUpState __instance, UnitEntityData unit, LevelUpState.CharBuildMode mode) 208 | { 209 | if (ModSettings.Settings.settings.GroupIsDisabled()) 210 | return; 211 | 212 | BlueprintFeature AivuUsesMythicXPNow = Resources.GetModBlueprint("AivuUsesMythicXP"); 213 | if (unit.Descriptor.Progression.Features.HasFact(AivuUsesMythicXPNow)) 214 | { 215 | Traverse.Create(__instance).Property("CanSelectName", null).SetValue(false); 216 | __instance.CanSelectName = false; 217 | } 218 | 219 | } 220 | } 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /LevelableAivu/FixItemAccess.cs: -------------------------------------------------------------------------------- 1 | using BlueprintCore.Blueprints.Configurators.Items; 2 | using BlueprintCore.Blueprints.Configurators.Items.Equipment; 3 | using BlueprintCore.Utils; 4 | using HarmonyLib; 5 | using Kingmaker.Blueprints; 6 | using Kingmaker.Blueprints.Items.Components; 7 | using Kingmaker.Blueprints.Items.Equipment; 8 | using Kingmaker.Blueprints.JsonSystem; 9 | using LevelableAivu.Config; 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Linq; 13 | using System.Text; 14 | using System.Threading.Tasks; 15 | using UnityModManagerNet; 16 | 17 | namespace LevelableAivu 18 | { 19 | class FixItemAccess 20 | { 21 | [HarmonyPatch(typeof(BlueprintsCache), "Init")] 22 | 23 | static class BlueprintsCache_Init_Patch 24 | { 25 | [HarmonyPriority(Priority.Last)] 26 | static void Postfix() 27 | { 28 | Main.Log($"Patching Glove Slot Items"); 29 | string[] pads = new string[] { "6497e0f07a3b49d8b50a33f27f86d8d2", 30 | "2ef8b0290fafaf647941af8c25a2d7be", 31 | "b598f24088bc49b9af70dd13969e1eee", 32 | "bfd3b4393040f854598dbdfff0feac79", 33 | "6fbf1d1001da4d04f907794c50262ae9", 34 | "efe72fa3d5164cdc93135c9e3cd91a22", 35 | "c8ad87f3fc49ccd43a5560acb271e968", 36 | "d657c55461344f748d76a8a03e6bdbd9" }; 37 | 38 | var artiGloves = BlueprintTool.Get("3230a07e17594084f88e770480277de2"); 39 | var artiGlovesRes = artiGloves.Components.OfType().FirstOrDefault(); 40 | var havocClassRef = BlueprintTool.GetRef("c0ae648600bb416b81e0b3e705f264c6"); 41 | var animalcompanionclass = BlueprintTool.GetRef("26b10d4340839004f960f9816f6109fe"); 42 | if (artiGlovesRes != null) 43 | { 44 | artiGlovesRes.m_Classes = artiGlovesRes.m_Classes.AppendToArray(havocClassRef); 45 | } 46 | Main.LogPatch($"Blocked Aivu Access:", artiGloves); 47 | 48 | 49 | var refList = new List() { havocClassRef, animalcompanionclass }; 50 | if (UnityModManager.FindMod("ExpandedContent") != null) 51 | { 52 | try 53 | { 54 | var drake = BlueprintTool.GetRef("557496bca2644c2d93c4a88b2b546430"); 55 | if (drake != null) 56 | { 57 | refList.Add(drake); 58 | Main.Log("Added Expanded Content Drake to pet extension list"); 59 | } 60 | } 61 | catch 62 | { 63 | 64 | } 65 | } 66 | 67 | foreach(string s in pads) 68 | { 69 | FixPads(BlueprintTool.Get(s)); 70 | } 71 | 72 | void FixPads(BlueprintItemEquipmentGloves gloves) 73 | { 74 | var anyType = gloves.Components.OfType().FirstOrDefault(); 75 | if (anyType!= null) 76 | { 77 | anyType.m_Classes = anyType.m_Classes.AppendToArray(havocClassRef); 78 | Main.LogPatch("Fixed pet access by patching existing multi-class restriction", gloves); 79 | } 80 | else 81 | { 82 | ItemEquipmentGlovesConfigurator.For(gloves).RemoveComponents(x => x is EquipmentRestrictionClass).AddComponent(x => 83 | { 84 | 85 | 86 | x.m_Classes = refList.ToArray(); 87 | }).Configure(); 88 | Main.LogPatch("Fixed pet access by swapping in new multiclass restriction", gloves); 89 | 90 | } 91 | 92 | } 93 | 94 | } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /LevelableAivu/GUIDs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace LevelableAivu 8 | { 9 | internal static class GUIDs 10 | { 11 | internal const string AivuHeroismBuffGUID = "d21b3d90-feb2-450d-87f1-62d4f57decd8"; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /LevelableAivu/Helpers.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using Kingmaker.Blueprints; 3 | using Kingmaker.Blueprints.Classes; 4 | using Kingmaker.ElementsSystem; 5 | using Kingmaker.EntitySystem.Stats; 6 | using Kingmaker.Enums; 7 | using Kingmaker.Localization; 8 | using Kingmaker.Localization.Shared; 9 | using Kingmaker.UnitLogic.Abilities; 10 | using Kingmaker.UnitLogic.Mechanics; 11 | using Kingmaker.UnitLogic.Mechanics.Components; 12 | using Kingmaker.UnitLogic.Mechanics.Properties; 13 | using Kingmaker.Utility; 14 | using LevelableAivu.Config; 15 | 16 | using ModKit; 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Text; 21 | using System.Threading.Tasks; 22 | 23 | namespace LevelableAivu 24 | { 25 | public static class Helpers 26 | { 27 | 28 | 29 | 30 | public static T Create(Action init = null) where T : new() 31 | { 32 | var result = new T(); 33 | init?.Invoke(result); 34 | return result; 35 | } 36 | 37 | public static T CreateBlueprint([NotNull] string name, Action init = null) where T : SimpleBlueprint, new() 38 | { 39 | var result = new T 40 | { 41 | name = name, 42 | AssetGuid = ModSettings.Blueprints.GetGUID(name) 43 | }; 44 | Resources.AddBlueprint(result); 45 | init?.Invoke(result); 46 | return result; 47 | } 48 | 49 | 50 | 51 | public static ContextRankConfig CreateContextRankConfig(ContextRankBaseValueType baseValueType = ContextRankBaseValueType.CasterLevel, 52 | ContextRankProgression progression = ContextRankProgression.AsIs, 53 | AbilityRankType type = AbilityRankType.Default, 54 | int? min = null, int? max = null, int startLevel = 0, int stepLevel = 0, 55 | bool exceptClasses = false, StatType stat = StatType.Unknown, 56 | BlueprintUnitProperty customProperty = null, 57 | BlueprintCharacterClass[] classes = null, BlueprintArchetype[] archetypes = null, BlueprintArchetype archetype = null, 58 | BlueprintFeature feature = null, BlueprintFeature[] featureList = null, 59 | (int, int)[] customProgression = null) 60 | { 61 | var config = new ContextRankConfig() 62 | { 63 | m_Type = type, 64 | m_BaseValueType = baseValueType, 65 | m_Progression = progression, 66 | m_UseMin = min.HasValue, 67 | m_Min = min.GetValueOrDefault(), 68 | m_UseMax = max.HasValue, 69 | m_Max = max.GetValueOrDefault(), 70 | m_StartLevel = startLevel, 71 | m_StepLevel = stepLevel, 72 | m_Feature = feature.ToReference(), 73 | m_ExceptClasses = exceptClasses, 74 | m_CustomProperty = customProperty.ToReference(), 75 | m_Stat = stat, 76 | m_Class = classes == null ? Array.Empty() : classes.Select(c => c.ToReference()).ToArray(), 77 | Archetype = archetype.ToReference(), 78 | m_AdditionalArchetypes = archetypes == null ? Array.Empty() : archetypes.Select(c => c.ToReference()).ToArray(), 79 | m_FeatureList = featureList == null ? Array.Empty() : featureList.Select(c => c.ToReference()).ToArray() 80 | }; 81 | #if false 82 | var config = Helpers.Create(bp => { 83 | bp.m_Type = type; 84 | bp.m_BaseValueType = baseValueType; 85 | bp.m_Progression = progression; 86 | bp.m_UseMin = min.HasValue; 87 | bp.m_Min = min.GetValueOrDefault(); 88 | bp.m_UseMax = max.HasValue; 89 | bp.m_Max = max.GetValueOrDefault(); 90 | bp.m_StartLevel = startLevel; 91 | bp.m_StepLevel = stepLevel; 92 | bp.m_Feature = feature.ToReference(); 93 | bp.m_ExceptClasses = exceptClasses; 94 | bp.m_CustomProperty = customProperty.ToReference(); 95 | bp.m_Stat = stat; 96 | bp.m_Class = classes == null ? Array.Empty() : classes.Select(c => c.ToReference()).ToArray(); 97 | bp.Archetype = archetype.ToReference(); 98 | bp.m_AdditionalArchetypes = archetypes == null ? Array.Empty() : archetypes.Select(c => c.ToReference()).ToArray(); 99 | bp.m_FeatureList = featureList == null ? Array.Empty() : featureList.Select(c => c.ToReference()).ToArray(); 100 | }); 101 | #endif 102 | return config; 103 | } 104 | 105 | public static LevelEntry LevelEntry(int level, BlueprintFeatureBase feature) 106 | { 107 | return new LevelEntry 108 | { 109 | Level = level, 110 | Features = { 111 | feature 112 | } 113 | }; 114 | } 115 | 116 | public static LevelEntry CreateLevelEntry(int level, params BlueprintFeatureBase[] features) 117 | { 118 | LevelEntry levelEntry = new LevelEntry(); 119 | levelEntry.Level = level; 120 | features.ForEach(f => levelEntry.Features.Add(f)); 121 | return levelEntry; 122 | } 123 | 124 | public static ContextRankConfig CreateContextRankConfig(Action init) 125 | { 126 | var config = CreateContextRankConfig(); 127 | init?.Invoke(config); 128 | return config; 129 | } 130 | 131 | public static FastRef CreateFieldSetter(string name) 132 | { 133 | return new FastRef(HarmonyLib.AccessTools.FieldRefAccess(HarmonyLib.AccessTools.Field(typeof(T), name))); 134 | //return new FastSetter(HarmonyLib.FastAccess.CreateSetterHandler(HarmonyLib.AccessTools.Field(typeof(T), name))); 135 | } 136 | public static FastRef CreateFieldGetter(string name) 137 | { 138 | return new FastRef(HarmonyLib.AccessTools.FieldRefAccess(HarmonyLib.AccessTools.Field(typeof(T), name))); 139 | //return new FastGetter(HarmonyLib.FastAccess.CreateGetterHandler(HarmonyLib.AccessTools.Field(typeof(T), name))); 140 | } 141 | 142 | public static ContextValue CreateContextValueRank(AbilityRankType value = AbilityRankType.Default) => value.CreateContextValue(); 143 | public static ContextValue CreateContextValue(this AbilityRankType value) 144 | { 145 | return new ContextValue() { ValueType = ContextValueType.Rank, ValueRank = value }; 146 | } 147 | public static ContextValue CreateContextValue(this AbilitySharedValue value) 148 | { 149 | return new ContextValue() { ValueType = ContextValueType.Shared, ValueShared = value }; 150 | } 151 | public static ActionList CreateActionList(params GameAction[] actions) 152 | { 153 | if (actions == null || actions.Length == 1 && actions[0] == null) actions = Array.Empty(); 154 | return new ActionList() { Actions = actions }; 155 | } 156 | } 157 | public delegate ref S FastRef(T source = default); 158 | public delegate void FastSetter(T source, S value); 159 | public delegate S FastGetter(T source); 160 | public delegate object FastInvoke(object target, params object[] paramters); 161 | } 162 | -------------------------------------------------------------------------------- /LevelableAivu/Info.json: -------------------------------------------------------------------------------- 1 | { 2 | "Id": "LevelableAivu", 3 | "DisplayName": "Levelable Aivu", 4 | "Author": "pheonix99", 5 | "Version": "1.2.7", 6 | "ManagerVersion": "0.21.3", 7 | "Requirements": [], 8 | "AssemblyName": "LevelableAivu.dll", 9 | "EntryMethod": "LevelableAivu.Main.Load", 10 | "HomePage": "https://github.com/pheonix99/LevelableAivu/releases" 11 | } -------------------------------------------------------------------------------- /LevelableAivu/LevelableAivu.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Debug 8 | AnyCPU 9 | {88C5A20A-AA6C-4856-987C-CAC9AA044C44} 10 | Library 11 | Properties 12 | LevelableAivu 13 | LevelableAivu 14 | v4.7.2 15 | 9 16 | 512 17 | true 18 | 19 | 20 | 21 | 22 | true 23 | full 24 | false 25 | bin\Debug\ 26 | DEBUG;TRACE 27 | prompt 28 | 4 29 | true 30 | 31 | 32 | pdbonly 33 | true 34 | bin\Release\ 35 | TRACE 36 | prompt 37 | 4 38 | true 39 | 40 | 41 | 42 | 43 | 44 | 1.0.2 45 | runtime; build; native; contentfiles; analyzers; buildtransitive 46 | all 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | $(WrathPath)\Wrath_Data\Managed\0Harmony.dll 80 | False 81 | 82 | 83 | $(WrathPath)\Wrath_Data\Managed\Assembly-CSharp-firstpass.dll 84 | False 85 | 86 | 87 | ..\lib\Assembly-CSharp.dll 88 | 89 | 90 | ..\packages\AssemblyPublicizer.1.0.2\lib\net472\AssemblyPublicizer.dll 91 | 92 | 93 | 94 | ..\packages\ModKit.1.0.8\lib\net472\ModKit.dll 95 | 96 | 97 | ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll 98 | 99 | 100 | $(WrathPath)\Wrath_Data\Managed\Owlcat.Runtime.Core.dll 101 | 102 | 103 | $(WrathPath)\Wrath_Data\Managed\Owlcat.Runtime.Validation.dll 104 | 105 | 106 | 107 | 108 | 109 | ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll 110 | True 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | ..\lib\UniRx.dll 122 | 123 | 124 | $(WrathPath)\Wrath_Data\Managed\UnityEngine.CoreModule.dll 125 | 126 | 127 | $(WrathPath)\Wrath_Data\Managed\UnityEngine.IMGUIModule.dll 128 | 129 | 130 | $(WrathPath)\Wrath_Data\Managed\UnityModManager\UnityModManager.dll 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | PreserveNewest 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /LevelableAivu/Main.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using JetBrains.Annotations; 3 | using Kingmaker; 4 | using Kingmaker.Blueprints.JsonSystem; 5 | using LevelableAivu.Config; 6 | using LevelableAivu.Utilities; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Reflection; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | using UnityModManagerNet; 14 | 15 | 16 | namespace LevelableAivu 17 | { 18 | static class Main 19 | { 20 | public static bool Enabled; 21 | 22 | static bool Load(UnityModManager.ModEntry modEntry) 23 | { 24 | var harmony = new Harmony(modEntry.Info.Id); 25 | ModSettings.ModEntry = modEntry; 26 | ModSettings.LoadAllSettings(); 27 | ModSettings.ModEntry.OnSaveGUI = OnSaveGUI; 28 | ModSettings.ModEntry.OnGUI = UMMSettingsUI.OnGUI; 29 | 30 | harmony.PatchAll(); 31 | PostPatchInitializer.Initialize(); 32 | return true; 33 | } 34 | public static void Error(Exception e, string message) 35 | { 36 | Log(message); 37 | Log(e.ToString()); 38 | //PFLog.Mods.Error(message); 39 | } 40 | public static void Error(string message) 41 | { 42 | Log(message); 43 | //PFLog.Mods.Error(message); 44 | } 45 | 46 | static void OnSaveGUI(UnityModManager.ModEntry modEntry) 47 | { 48 | 49 | } 50 | public static void Log(string msg) 51 | { 52 | ModSettings.ModEntry.Logger.Log(msg); 53 | } 54 | 55 | static bool OnToggle(UnityModManager.ModEntry modEntry, bool value) 56 | { 57 | Enabled = value; 58 | return true; 59 | } 60 | 61 | public static void LogDebug(string msg) 62 | { 63 | ModSettings.ModEntry.Logger.Log(msg); 64 | } 65 | public static void LogPatch(string action, [NotNull] IScriptableObjectWithAssetId bp) 66 | { 67 | Log($"{action}: {bp.AssetGuid} - {bp.name}"); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /LevelableAivu/ModifyEquipmentRestrictionsHasAnyClassFromList.cs: -------------------------------------------------------------------------------- 1 | using BlueprintCore.Utils; 2 | using HarmonyLib; 3 | using Kingmaker.Blueprints; 4 | using Kingmaker.Blueprints.Classes; 5 | using Kingmaker.Blueprints.Items.Armors; 6 | using Kingmaker.Blueprints.Items.Components; 7 | using Kingmaker.Blueprints.Items.Equipment; 8 | using Kingmaker.UnitLogic; 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | using System.Text; 13 | using System.Threading.Tasks; 14 | 15 | namespace LevelableAivu 16 | { 17 | [HarmonyPatch(typeof(EquipmentRestrictionClass), "CanBeEquippedBy", new Type[] { typeof(UnitDescriptor) })] 18 | static class ModifyEquipmentRestrictions 19 | { 20 | static void Postfix(ref bool __result, EquipmentRestrictionClass __instance, UnitDescriptor unit) 21 | { 22 | BlueprintCharacterClass companion = BlueprintTool.Get("01a754e7c1b7c5946ba895a5ff0faffc"); 23 | BlueprintFeature AivuUsesMythicXPNow = Resources.GetModBlueprint("AivuUsesMythicXP"); 24 | if (__instance.OwnerBlueprint is BlueprintItemEquipment or BlueprintItemArmor) 25 | { 26 | if (unit.HasFact(AivuUsesMythicXPNow))//This is Aivuz 27 | { 28 | 29 | if (__instance.Class.ToReference().Equals(companion.ToReference())) 30 | { 31 | __result = !__instance.Not; 32 | } 33 | } 34 | } 35 | 36 | } 37 | } 38 | 39 | 40 | 41 | 42 | [HarmonyPatch(typeof(EquipmentRestrictionHasAnyClassFromList), "CanBeEquippedBy", new Type[] { typeof(UnitDescriptor) })] 43 | static class ModifyEquipmentRestrictionsHasAnyClassFromList 44 | { 45 | 46 | 47 | static void Postfix(ref bool __result, EquipmentRestrictionHasAnyClassFromList __instance, UnitDescriptor unit) 48 | { 49 | var dragon = BlueprintTool.Get("01a754e7c1b7c5946ba895a5ff0faffc").ToReference (); 50 | 51 | BlueprintFeature AivuUsesMythicXPNow = Resources.GetModBlueprint("AivuUsesMythicXP"); 52 | if (__instance.OwnerBlueprint is BlueprintItemEquipment or BlueprintItemArmor) 53 | { 54 | if (unit.HasFact(AivuUsesMythicXPNow))//This is Aivu 55 | { 56 | 57 | if (__instance.Classes.Any(x=>x.ToReference().Equals(dragon))) 58 | { 59 | __result = !__instance.Not; 60 | } 61 | } 62 | } 63 | 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /LevelableAivu/PatchTryLevelUpPet.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Kingmaker.Blueprints; 3 | using Kingmaker.Blueprints.Classes; 4 | using Kingmaker.Blueprints.JsonSystem; 5 | using Kingmaker.Blueprints.Root; 6 | using Kingmaker.EntitySystem; 7 | using Kingmaker.EntitySystem.Entities; 8 | using Kingmaker.Enums; 9 | using Kingmaker.UI.ServiceWindow.CharacterScreen; 10 | using Kingmaker.UnitLogic; 11 | using Kingmaker.UnitLogic.FactLogic; 12 | using Kingmaker.UnitLogic.Parts; 13 | using System; 14 | using System.Collections.Generic; 15 | using System.Linq; 16 | using System.Text; 17 | using System.Threading.Tasks; 18 | 19 | namespace LevelableAivu 20 | { 21 | 22 | [HarmonyPatch(typeof(AddPet), "TryLevelUpPet")] 23 | 24 | static class PatchTryLevelUpPet 25 | { 26 | [HarmonyPriority(Priority.Normal)] 27 | static bool Prefix(AddPet __instance) 28 | { 29 | 30 | 31 | Main.Log("Levelable Aivu AddPet override started"); 32 | if (__instance.Type != PetType.AzataHavocDragon) 33 | { 34 | 35 | 36 | 37 | return true; 38 | } 39 | else 40 | { 41 | 42 | UnitEntityData value = __instance.Data.SpawnedPetRef.Value; 43 | if (value == null) 44 | { 45 | return false; 46 | } 47 | BlueprintComponentAndRuntime blueprintComponentAndRuntime = (from f in value.Facts.List 48 | select f.GetComponentWithRuntime()).FirstOrDefault((BlueprintComponentAndRuntime c) => c.Component != null); 49 | if (blueprintComponentAndRuntime.Component == null) 50 | { 51 | return false; 52 | } 53 | int characterLevel = value.Descriptor.Progression.CharacterLevel; 54 | int petLevel = __instance.GetPetLevel(); 55 | int num = petLevel - characterLevel; 56 | 57 | if (num > 0) 58 | { 59 | UnitPartCompanion unitPartCompanion = __instance.Owner.Get(); 60 | if (unitPartCompanion != null && unitPartCompanion.State != CompanionState.None && unitPartCompanion.State != CompanionState.ExCompanion && !__instance.Data.AutoLevelup && __instance.Type == PetType.AzataHavocDragon) 61 | { 62 | 63 | 64 | int bonus = BlueprintRoot.Instance.Progression.LegendXPTable.GetBonus(petLevel); 65 | value.Progression.AdvanceExperienceTo(bonus, false, true); 66 | 67 | } 68 | else 69 | { 70 | return true; 71 | } 72 | } 73 | if (__instance.GetPetLevel() >= __instance.UpgradeLevel && __instance.UpgradeFeature != null) 74 | { 75 | value.Progression.Features.AddFeature(__instance.UpgradeFeature, null); 76 | } 77 | __instance.Data.AutoLevelup = false; 78 | return false; 79 | } 80 | 81 | } 82 | 83 | } 84 | 85 | 86 | 87 | } 88 | -------------------------------------------------------------------------------- /LevelableAivu/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("LevelableAivu")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("LevelableAivu")] 13 | [assembly: AssemblyCopyright("Copyright © 2021")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("88c5a20a-aa6c-4856-987c-cac9aa044c44")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /LevelableAivu/Resources.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using Kingmaker.Blueprints; 3 | using System; 4 | using System.Collections.Generic; 5 | using LevelableAivu.Config; 6 | 7 | namespace LevelableAivu 8 | { 9 | static class Resources 10 | { 11 | public static readonly Dictionary ModBlueprints = new Dictionary(); 12 | 13 | 14 | public static T GetModBlueprint(string name) where T : SimpleBlueprint 15 | { 16 | var assetId = ModSettings.Blueprints.GetGUID(name); 17 | ModBlueprints.TryGetValue(assetId, out var value); 18 | return value as T; 19 | } 20 | public static T GetBlueprint(string id) where T : SimpleBlueprint 21 | { 22 | var assetId = new BlueprintGuid(System.Guid.Parse(id)); 23 | return GetBlueprint(assetId); 24 | } 25 | public static T GetBlueprint(BlueprintGuid id) where T : SimpleBlueprint 26 | { 27 | SimpleBlueprint asset = ResourcesLibrary.TryGetBlueprint(id); 28 | T value = asset as T; 29 | if (value == null) { Main.Log($"COULD NOT LOAD: {id} - {typeof(T)}"); } 30 | return value; 31 | } 32 | public static void AddBlueprint([NotNull] SimpleBlueprint blueprint) 33 | { 34 | AddBlueprint(blueprint, blueprint.AssetGuid); 35 | } 36 | 37 | public static void AddBlueprint([NotNull] SimpleBlueprint blueprint, BlueprintGuid assetId) 38 | { 39 | var loadedBlueprint = ResourcesLibrary.TryGetBlueprint(assetId); 40 | if (loadedBlueprint == null) 41 | { 42 | ModBlueprints[assetId] = blueprint; 43 | ResourcesLibrary.BlueprintsCache.AddCachedBlueprint(assetId, blueprint); 44 | blueprint.OnEnable(); 45 | Main.LogPatch("Added", blueprint); 46 | } 47 | else 48 | { 49 | Main.Log($"Failed to Add: {blueprint.name}"); 50 | Main.Log($"Asset ID: {assetId} already in use by: {loadedBlueprint.name}"); 51 | } 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /LevelableAivu/RespecPatch.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Kingmaker.Blueprints.Classes; 3 | using Kingmaker.EntitySystem.Entities; 4 | using Kingmaker.UnitLogic.Class.LevelUp; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace LevelableAivu 12 | { 13 | 14 | 15 | [HarmonyPatch(typeof(LevelUpController), MethodType.Constructor)] 16 | [HarmonyPatch(new Type[] { typeof(UnitEntityData), typeof(bool), typeof(LevelUpState.CharBuildMode), typeof(bool) })] 17 | [HarmonyPriority(9999)] 18 | internal static class LevelUpController_ctor_Patch 19 | { 20 | private static void Postfix(LevelUpController __instance, UnitEntityData unit, LevelUpState.CharBuildMode mode) 21 | { 22 | try 23 | { 24 | BlueprintFeature Aivu = Resources.GetModBlueprint("AivuUsesMythicXP"); 25 | if (unit.Descriptor.Progression.Features.HasFact(Aivu)) 26 | { 27 | __instance.SelectName("Aivu"); 28 | 29 | 30 | } 31 | 32 | 33 | } 34 | catch (Exception e) { Main.Log(e.ToString()); } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LevelableAivu/UMMSettingsUI.cs: -------------------------------------------------------------------------------- 1 | using Kingmaker.Utility; 2 | using ModKit; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text.RegularExpressions; 7 | using LevelableAivu.Config; 8 | using UnityEngine; 9 | using UnityModManagerNet; 10 | 11 | namespace LevelableAivu 12 | { 13 | public static class UMMSettingsUI 14 | { 15 | private static int selectedTab; 16 | public static void OnGUI(UnityModManager.ModEntry modEntry) 17 | { 18 | UI.AutoWidth(); 19 | UI.TabBar(ref selectedTab, 20 | () => UI.Label("SETTINGS WILL NOT BE UPDATED UNTIL YOU RESTART YOUR GAME.".yellow().bold()), 21 | new NamedAction("Settings", () => SettingsTabs.Settings()) 22 | 23 | ); 24 | } 25 | } 26 | 27 | static class SettingsTabs 28 | { 29 | public static void Settings() 30 | { 31 | var TabLevel = SetttingUI.TabLevel.Zero; 32 | var Settings = ModSettings.Settings; 33 | UI.Div(0, 15); 34 | using (UI.VerticalScope()) 35 | { 36 | SetttingUI.SettingGroup("Settings", TabLevel, Settings.settings); 37 | } 38 | } 39 | 40 | 41 | 42 | } 43 | 44 | static class SetttingUI 45 | { 46 | public enum TabLevel : int 47 | { 48 | Zero, 49 | One, 50 | Two, 51 | Three, 52 | Four, 53 | Five 54 | } 55 | public static void Increase(ref this TabLevel level) 56 | { 57 | level += 1; 58 | } 59 | public static void Decrease(ref this TabLevel level) 60 | { 61 | if ((int)level > 0) 62 | { 63 | level -= 1; 64 | } 65 | } 66 | public static int Spacing(this TabLevel level) 67 | { 68 | return (int)level * 50; 69 | } 70 | public static void Indent(this TabLevel level) 71 | { 72 | UI.Space(level.Spacing()); 73 | } 74 | 75 | 76 | 77 | 78 | 79 | public static void SettingGroup(string name, TabLevel level, SettingGroup group) 80 | { 81 | if (group.Settings.Empty()) { return; } 82 | RootGroup(name, level, group); 83 | if (group.IsExpanded) 84 | { 85 | level.Increase(); 86 | if (group.Settings.Any()) { TabbedItem(level, () => UI.Div(Color.grey, 500)); } 87 | group.Settings.ForEach(entry => { 88 | TabbedItem(level, 89 | () => Toggle(String.Join(" ", entry.Key.SplitOnCapitals()), group.IsEnabled(entry.Key), (enabled) => group.ChangeSetting(entry.Key, enabled), UI.Width(500 - level.Spacing())), 90 | () => Label(entry.Value.Description.green())); 91 | TabbedItem(level, () => UI.Div(Color.grey, 500)); 92 | }); 93 | level.Decrease(); 94 | } 95 | } 96 | 97 | public static void RootGroup(string name, TabLevel level, ICollapseableGroup rootGroup) 98 | { 99 | using (UI.HorizontalScope()) 100 | { 101 | level.Indent(); 102 | 103 | UI.DisclosureToggle(name.bold(), ref rootGroup.IsExpanded(), 140); 104 | } 105 | } 106 | 107 | public static void TabbedItem(TabLevel level, params Action[] actions) 108 | { 109 | using (UI.HorizontalScope()) 110 | { 111 | level.Indent(); 112 | actions.ForEach(action => action.Invoke()); 113 | } 114 | } 115 | 116 | public static bool Toggle(string title, bool value, Action action, params GUILayoutOption[] options) 117 | { 118 | options = options.AddDefaults(); 119 | var changed = ModKit.Private.UI.CheckBox(title, value, UI.toggleStyle, options); 120 | if (changed) 121 | { 122 | Main.Log($"Changed: {!value}"); 123 | action.Invoke(!value); 124 | } 125 | return changed; 126 | } 127 | 128 | public static void Label(string title) 129 | { 130 | GUILayout.Label(title, GUILayout.ExpandWidth(false)); 131 | } 132 | 133 | public static IEnumerable SplitOnCapitals(this string text) 134 | { 135 | Regex regex = new Regex(@"\p{Lu}\p{Ll}*"); 136 | foreach (Match match in regex.Matches(text)) 137 | { 138 | yield return match.Value; 139 | } 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /LevelableAivu/VariableBaseSizeGetter.cs: -------------------------------------------------------------------------------- 1 | using Kingmaker.Blueprints; 2 | using Kingmaker.Designers.Mechanics.Buffs; 3 | using Kingmaker.EntitySystem.Entities; 4 | using Kingmaker.UnitLogic; 5 | using Kingmaker.UnitLogic.Mechanics.Properties; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace LevelableAivu 13 | { 14 | 15 | class VariableBaseSize : UnitFactComponentDelegate 16 | { 17 | public override void OnActivate() 18 | { 19 | 20 | } 21 | 22 | public int Shift; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /LevelableAivu/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | A mod to make Aivu's levelups controllable. 2 | 3 | # Changelog: 4 | 5 | 6 | ## 1.2.7 7 | 8 | Official 2.2 release 9 | 10 | 11 | ## 1.2.6 12 | 13 | Offical 2.1 release. 14 | Fix for heroism aura buff persisting overlong. 15 | 16 | ## 1.2.5 17 | 2.1 Beta release 18 | 19 | ## 1.2.3 20 | Fixed for levelups past 20 being broken. 21 | Fix for inability to wear animal companion gloves. 22 | Made ExpandedContent drake companions able to wear them while I was at it. 23 | 24 | 25 | What it does: 26 | You can now pick what feats and spells Aivu takes on levelup. 27 | Aivu can now equip animal companion specific items if other requirements are met. 28 | Fixes the SFX spam on the heroism aura. 29 | 30 | 31 | Optional Components: 32 | Aivu's spell list includes the bard list per TT havoc dragons. 33 | 34 | Installation and Compatability considerations: 35 | You will need to respec to make use of this mod if you are already an Azata. 36 | An existing Aivu will not benefit from the mod's features and may encounter issues. 37 | Any mod that alters any portion of Aivu's existing progression will be non-functional and may break things - I deep-sixed all of that in the process of making this. 38 | 39 | New items and spells should work just fine. 40 | 41 | 42 | Balance considerations: 43 | 44 | Aivu has been nerfed pretty heavily, both casting and breath. This should make that tolerable. 45 | 46 | Known Display issues: 47 | The spell access line in the progression shows level 7/8/9 access starting at level 15. Works fine in practice, ignore. 48 | There may be spurrious heroism buff icons in on the character selection box at the bottom - some kind of side effect of the heroism fx fix. 49 | 50 | Permissions: 51 | Feel free to borrow the code and integrate into your own mod, just give me a shoutout. 52 | --------------------------------------------------------------------------------