Show / Hide Table of Contents

Text, Logging, Unity Assets, and Utils

While the bulk of the functionality of BPCore is implemented in Blueprint Configurators and Builders, there are many utility classes that provide additional functionality.

Text

To display text in game you need to create a LocalizedString with a key used to look up the text for the appropriate locale / language.

Using BPCore you can define text in JSON file containing a MultiLocaleString array, such as this example from the Feat Tutorial:

[
  {
    "Key": "MagicalAptitude.Name",
    "ProcessTemplates": false,
    "enGB": "Magical Aptitude",
    "deDE": "Magische Begabung"
  },

  {
    "Key": "MagicalAptitude.Description",
    "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."
  }
]

By default BPCore uses the file LocalizedStrings.json in the same folder as your mod assembly; the same location as the Info.json file for a UMM mod. You can override this by calling LocalizationTool.LoadLocalizationPack() and providing your own file.

Localized Values

In your JSON file you can provide a value for each locale supported in-game: enGB, ruRU, deDE, frFR, zhCN, and esES. You must specify enGB as it is used for any locale without a value.

Referencing MagicalAptitude.Name when the locale is enGB results in "Magical Aptitude" and "Magische Begabung" when the locale is set to deDE.

Template Processing

By default ProcessTemplates is true. When set to true:

  • EncyclopediaTool.TagEncyclopediaEntries() is called (English only)
    • Generates links to the in-game encyclopedia for relevant entries such as Strength or DC
  • The game's TextTemplateEngine is used to process the string
    • Converts placeholder strings into output, e.g. {name} is replaced by the character's name
Tip

Set ProcessTemplates to false for most feature names to ensure there are no UI bugs. Features like encyclopedia links don't work propertly for feature names.

Below is a list of template placeholders as of writing this doc, but the source of truth is the game's code in TextTemplateEngine.

  • {mf}, MaleFemaleTemplate()
  • {race}, RaceTemplate()
  • {name}, NameTemplate()
  • {kingdomname}, KingdomNameTemplate()
  • {date}, DateTemplate()
  • {time}, TimeTempate()
  • {custom_companion_cost}, CustomCompanionCostTemplate()
  • {respec_cost}, RespecCostTemplate()
  • {leader_cost}, LeaderCostTemplate()
  • {flag}, FlagTemplate()
  • {n}, NarratorStartTemplate()
  • {/n}, NarratorEndTemplate()
  • {g}, TooltipStartTemplate(TooltipType.Glosary)
  • {/g}, TooltipEndTemplate(TooltipType.Glosary)
  • {d}, TooltipStartTemplate(TooltipType.Decisions)
  • {/d}, TooltipEndTemplate(TooltipType.Decisions)
  • {m}, TooltipStartTemplate(TooltipType.Mechanics)
  • {/m}, TooltipEndTemplate(TooltipType.Mechanics)
  • {mf_Regent}, LeaderMaleFemaleTemplate(LeaderType.Counselor)
  • {mf_Counsilor}, LeaderMaleFemaleTemplate(LeaderType.Strategist)
  • {mf_GrandDiplomat}, LeaderMaleFemaleTemplate(LeaderType.Diplomat)
  • {mf_Magister}, LeaderMaleFemaleTemplate(LeaderType.General)
  • {mf_General}, LeaderMaleFemaleTemplate(LeaderType.General)
  • {target}, LogTemplateTarget()
  • {formula}, LogTemplateFormula()
  • {source}, LogTemplateSource()
  • {text}, LogTemplateText()
  • {text_with_tags}, LogTemplateTextWithTags()
  • {description}, LogTemplateDescription()
  • {count}, LogTemplateCount()
  • {count_form}, LogTemplateCountForm()
  • {roll}, LogTemplateRoll()
  • {d20}, LogTemplateD20()
  • {d100}, LogTemplateD100()
  • {mod}, LogTemplateModifier()
  • {dc}, LogTemplateDC()
  • {chance_dc}, LogTemplateChanceDC()
  • {roll_chance}, LogTemplateRollChance()
  • {slotname}, UITemplateEquipedItem()
  • {wielder}, UITemplateItemWielder()
  • {rations}, UITemplateRations()
  • {recipe}, UITemplateSimpleText()
  • {attack_number}, LogTemplateAttackNumber()
  • {attacks_count}, LogTemplateAttacksCount()
  • {round}, LogTemplateRound()
  • {claimed_villages_count}, SettlementsCountTemplate(SettlementState.LevelType.Village)
  • {claimed_towns_count}, SettlementsCountTemplate(SettlementState.LevelType.Town)
  • {claimed_cities_count}, SettlementsCountTemplate(SettlementState.LevelType.City)
  • {portraits_path}, UITemplatePartraitsPath()
  • {area_name}, UITemplateAreaName()
  • {bind}, KeyBindingTemplate()
  • {console_bind}, ConsoleBindingTemplate()
  • {empty}, EmptyTemplate()
  • {br}, LineBreakTemplate()
  • {pc_console}, PcConsoleTemplate()
  • {t}, TutorialDataTemplate()
  • {ui}, UITemplate()

Referencing Text

In BPCore APIs that set text values accept a LocalString parameter. To specify a text value from this file use the key:

FeatureConfigurator.New(FeatName, FeatGuid)
    .SetDisplayName("MagicalAptitude.Name")
    .SetDescription("MagicalAptitude.Description")
    .Configure();

You can also reference text already in the game by the key.

If you have a LocalizedString you can use it directly:

LocalizedString magicalAptitudeName = LocalizationTool.CreateString(FeatNameKey, FeatName, tagEncyclopediaEntries: false);
LocalizedString magicalAptitudeDescription = LocalizationTool.CreateString(FeatDescriptionKey, FeatDescription);

FeatureConfigurator.New(FeatName, FeatGuid)
    .SetDisplayName(magicalAptitudeName)
    .SetDescription(magicalAptitudeDescription")
    .Configure();

Using LocalizationTool.CreateString() is not recommended; it only works for a single locale and fails if the user changes locale mid-game.

Passing in a LocalizedString directly is useful if you are using TabletopTweaks-Core localization or referencing text copied from an existing blueprint.

Logging

LogWrapper wraps the game's LogChannel class to provide control over verbose log output.

It is used internally for logging within BlueprintCore and is available for use within your modification, but not required.

private static readonly LogWrapper ModLogger = LogWrapper.Get("MyMod");
private static readonly LogWrapper FeatLogger = LogWrapper.Get("Feats");

ModLogger.Info("Mod initialized.");
FeatLogger.Info.("Feat initialized.");

The output to the log from the above example is:

BlueprintCore.MyMod: Mod initialized.
BlueprintCore.Feats: Mod initialized.

Log output is available locally in %APPDATA%\..\LocalLow\Owlcat Games\Pathfinder Wrath Of The Righteous\GameLogFull.txt or in Remote Console.

Log output uses the Mods channel currently.

Unity Assets

BPCore supports importing assets from a Unity AssetBundle included in your mod. Using Unity (version 2020.3.33f1), create an AssetBundle called <assemblyname>_assets and place it in the same diretory as your assembly. For a walkthrough of generating an AssetBundle see the Skald's Vigor Tutorial.

When a BPCore API needs an asset it requests an Asset or AssetLink. You can provide these directly or by Asset ID.

Unity usually defines the Asset ID using the file path relative to the Unity project directory, e.g. MyUnityProject/assets/icons/myicon.png is referenced using assets/icons/myicon.png. If you're ever unsure, look in the assets.manifest file in the same directory as the assets bundle or the bottom of the inspector tab in Unity.

BuffConfigurator.New(BuffName, BuffGuid)
  .SetDisplayName(BuffDisplayName)
  .SetDescription(BuffDescription)
  .SetIcon("assets/icons/myicon.png")
  .Configure();

This works regardless of whether an asset or an asset link, e.g. SpriteLink, is required.

If you want to directly load the resource call ResourcesLibrary.TryGetResource<Sprite>():

var myIconSprite = ResourcesLibraryTryGetResource<Sprite>("assets/icons/myicon.png");

Modifying Base Game Assets

As of v2.6.0 BPCore allows you to register a fake PrefabLink as a placeholder for a modified base game asset. Use this when you want to create a new asset by modifying an asset that already exists in game. See [AssetTool.RegisteryDynamicPrefabLink()](xref:BlueprintCore.Utils.Assets.AssetTool.RegisterDynamicPrefabLink(string, BlueprintCore.Utils.Assets.AssetLink{Kingmaker.ResourcesLinks.PrefabLink}, System.Action{UnityEngine.GameObject})) for more details.

When your fake PrefabLink is loaded:

  1. The base game asset specified is loaded instead
  2. A new asset is created by copying the base game asset
  3. init is called, passing in the new asset

In your init function you can modify the GameObject to create the resulting asset. Here's a trimmed down example taken from Ice Slick in Character Options+:

var sourceFx = "fd21d914e9f6f5e4faa77365549ad0a7"; // A 20-ft cold puddle
var newFx = "c1ef4fc5-e5ea-43b7-a9d4-cbb4be41516a"; // New GUID used for the fake PrefabLink

AssetTool.RegisterDynamicPrefabLink(newFx, sourceFx, ModifyFx);
AbilityAreaEffectConfigurator.New(AreaEffectName, AreaEffectGuid)
  .SetAffectEnemies()
  .SetAggroEnemies()
  .SetSize(10.Feet())
  .SetShape(AreaEffectShape.Cylinder)
  .SetFx(newFx)
  .Configure();

private static void ModifyFx(GameObject puddle)
{
  UnityEngine.Object.DestroyImmediate(puddle.transform.Find("Transform/ProjectorCollision_big").gameObject); // Remove unwanted particle effects
  puddle.transform.localScale = new(0.55f, 1.0f, 0.55f); // Scale from 20ft to 10ft
}

This takes the 20-ft "cold puddle" asset from the base game, removes the ice crystal effects, and scales it down to better represent a 10-ft area effect.

Here is what it looks like if the original game asset is used, unmodified:

Unmodified game asset

And here is what it looks like with my modifications:

Modified game asset

Tools

Tool classes provide simple utility functions, usually related to a specific type. See each class for more details, but some notable uses:

  • BlueprintTool
    • Use this to create, fetch, and provide a name to guid mapping for blueprints
  • ElementTool
    • Use this to create or initialize types inheriting from Element
  • PrereqTool
    • Use this to create types inheriting from Prerequisite

Type Constructors

Utility classes are provided to simplify creating game objects.

  • ContextDuration
var contextDuration = ContextDuration.Fixed(2);
  • ContextValues
var contextValue = ContextValues.Rank();
  • ContextRankConfigs
var contextRankConfig = ContextRankConfigs.BaseAttack().WithDivStepProgression(2);
  • UnitConditionException
var unitConditionException = UnitConditionException.TargetHasFeatures(FeatureGuid1, FeatureGuid2);

Validator

Validator is used by the library to validate method inputs, actions, conditions, blueprint components, and blueprints.

You can also use it separately for any game objects created outside of the library:

private static readonly LogWrapper ModLogger = LogWrapper.Get("MyMod");

var validator = new Validator("MyValidator", "BlueprintBuff");
validator.Check(myBuff);
validator.Check(myBuffActions);
if (validator.HasErrors())
{
  ModLogger.Warning(validator.GetErrorString());
}

Once you create a Validator, you can call Check() for any objects related to it and they will all be bundled into the same error validation string.

In This Article
Back to top Generated by DocFX