Skip to content

Building a Race Mod

Trev edited this page Nov 23, 2023 · 20 revisions

Race Mods

One of the easiest ways to get into modding is create a custom race. Whether you have a cool homebrew concept, or there's an official race that Larian didn't add, there's a lot of room to play around in. This guide will walk you through the process of building a custom race.

Make sure to complete the steps outlined in the Getting Started page.

Table of Contents

  1. Getting Started
  2. Race Entry
  3. Root Templates
  4. Character Visuals
  5. Character Creation
  6. Localization
  7. Racial Feats
  8. Racial Progressions
  9. Tags
  10. Custom Icons

Race Entry

First thing's first, we need to work on our Races.lsx entry. Typically, this is something you'll want the Community Library to handle, as it's reusable and potentially useful for other modders. Community Library already has an existing Races.lsx file, located in CommunityLibrary/Public/CommunityLibrary/Races/Races.lsx, so that's where we'll start.

  1. Open up Community Library in VSCode(or your IDE of choice).
  2. Open up CommunityLibrary/Public/CommunityLibrary/Races/Races.lsx

Here, we can see there are already a few entries in here. We can use these as examples, but it's worth taking a look at some of the default files, especially if there are any races similar to what you want to include. If you haven't already, unpack the Shared and Gustav .pak files:

  1. Open Modder's Multitool
  2. Select "Unpack .pak Files
  3. Tick "Gustav.pak" and "Shared.pak."
  4. Select "Confirm," and wait for the process to complete.

Take a look at the Races.lsx files unpacked form Gustav and/or Shared.pak, and identify a similar race. This will be important later.

Now, let's look at our copy again. What's important to us are the nodes with an ID of "Race". Here's the entry for Shadar-Kai.

<?xml version="1.0" encoding="UTF-8"?>
<save>
  <version major="4" minor="0" revision="0" build="49"/>
  <region id="Races">
    <node id="root">
      <children>
        <node id="Race">
          <attribute id="Description" type="TranslatedString" handle="h9a4e44c9g72e0g43b6ga72bgd83fcb6b40d1" version="1"/>
          <attribute id="DisplayName" type="TranslatedString" handle="h6362370ag39d0g407fgaff0g3d98f3bb161e" version="1"/>
          <attribute id="DisplayTypeUUID" type="guid" value="899d275e-9893-490a-9cd5-be856794929f"/>
          <attribute id="Name" type="FixedString" value="CL_ShadarKai"/>
          <attribute id="ParentGuid" type="guid" value="6c038dcb-7eb5-431d-84f8-cecfaf1c0c5a"/>
          <attribute id="ProgressionTableUUID" type="guid" value="01735427-a4df-4ce4-8fb6-4b1b8c6c3771"/>
          <attribute id="RaceSoundSwitch" type="FixedString" value="Elf"/>
          <attribute id="UUID" type="guid" value="27844147-3e0f-4b2e-8377-8d015b8384df"/>
          <children>
            ...
          <children>
        </node>
      </children>
    </node>
  </region>
</save>

It's a lot to look at, I know. For now, ignore the <children> section. We have a few attributes:

  • Description: The value here refers to a localization string that defines the race's description.
  • DisplayName: The value here refers to a localization string that defines the race's name, as seen ingame.
  • DisplayTypeUUID: This will often be identical to the later ParentGuid attribute. Just keep these the same.
  • Name: This is the internal name of the Race - never seen ingame, but useful when referencing.
  • ParentGuid: The Race this race inherits from. Base races will often inherit from the Humanoid race, while subraces may inherit from other races.
  • ProgressionTableUUID: This references a specific progression table, defining spells, passives, and other key pieces. More on this later.
  • RaceSoundSwitch: References the sound types made when switching characters.
  • UUID: This is the Identifier of this race. It's how you'll reference the race when needed.

So what do we do with this? Let's start by copy/pasting it as a new Race node, and making our changes.

  1. Using Modder's Multitool, tick "Handle", and generate a new UUID.
  2. Copy the UUID(click on it's display), and paste it into Description's handle value.
  3. Do the same for DisplayName.
  4. Write an internal name, prefixed with CL_ to help ensure there aren't any collisions with other mods
  5. Set the RaceSoundSwitch based on another similar race.
  6. Untick "Handle" in Modder's Multitool, and generate a new UUID, pasting it into the UUID attribute's handle field.

Subraces

  1. Find the race you want your race to be a subrace of
  2. Copy its UUID handle
  3. Paste it into your race's ParentGuid value.

Children Elements

Obviously, our race needs to have visuals, possibly even tags. You can opt not to have the <children> tags, but if you want them to have features similar to another race, here's what to do:

  1. Find a similar race in the unpacked Races.lsx file.
  2. Copy their <children> section over to your race.
  3. Remove any <node id="Tags"> sections UNLESS you want them to have the reference race's tags.

Progression Table

We can work with the Progression Table once we get to Progressions, leave it as is for now.

Root Templates

Root Templates define a lot about our races, and just about everything else. By default, they're in the .lsf format, and will need to be converted into .lsx before we can edit them. If you haven't already, let's do this to the unpacked RootTemplates files in Shared:

  1. In LSLib, navigate to the LSX/LSB/LSF/LSJ Tools tab.
  2. Under "Batch Convert," set the Input Directory to match Shared's RootTemplates directory (ex. H:\BG3\ExportTool-v1.18.5\Multitool\UnpackedData\Shared\Public\Shared\RootTemplates
  3. Do the same for "Output Directory"
  4. Make sure Input format is set to LSF and Output format is set to LSX.
  5. Hit "Convert" and wait for the process to complete.

Now you can search, open, and edit the base Root Templates.

Parsing the Root Templates

You may notice that the official Root Templates name looks awfully similar to a UUID, but the ones in Community Library aren't. The naming convention doesn't matter much here, so Community Library's policy is to keep RootTemplate names readable, to save everyone time.

When it comes to Root Templates, you'll typically have 6 per race: Base Female, Base Male, Female Player, Female Player Strong, Male Player, and Male Player Strong. You can create your Base root templates in Community Library, but the remaining 4 should go into your implementation mod. This will help keep things clean.

Base Root Templates

Your base templates will look something like this:

<?xml version="1.0" encoding="utf-8"?>
<save>
  <version major="4" minor="0" revision="0" build="51" />
  <region id="Templates">
    <node id="Templates">
      <children>
        <node id="GameObjects">
          <attribute id="MapKey" type="FixedString" value="27410506-9276-48e7-b4b4-38d861aa1be7" />
          <attribute id="Name" type="LSString" value="BASE_Elves_Female_ShadarKai" />
          <attribute id="LevelName" type="FixedString" value="" />
          <attribute id="Type" type="FixedString" value="character" />
          <attribute id="ParentTemplateId" type="FixedString" value="4c3d8aa0-2e31-4dce-a792-84fc07fa63d8" />
          <attribute id="VisualTemplate" type="FixedString" value="" />
          <attribute id="Icon" type="FixedString" value="24bcc44f-4168-1468-222a-b1bb6370e000-_(Icon_Elf_Female)" />
          <attribute id="CharacterVisualResourceID" type="FixedString" value="f89e3922-f92e-4279-809e-4a95336a75a9" />
          <attribute id="Race" type="guid" value="27844147-3e0f-4b2e-8377-8d015b8384df" />
          <children>
            <node id="GameMaster" />
            <node id="LocomotionParams" />
          </children>
        </node>
      </children>
    </node>
  </region>
</save>

Once again, a lot to look at, so I'll summarize:

  • MapKey: The Identifier for your Template. This corresponds with the filenames for the default root templates. This is what you'll use to reference a template elsewhere.
  • Name: The internal name of the Template. This is what Community Library names the Template files.
  • LevelName: Typically blank, but possibly needs to exist.
  • Type: For races, this will always be "character".
  • ParentTemplateID: This will be the ID of the Template our Template inherits from.
  • VisualTemplate: For races, this will typically be blank.
  • Icon: A string that consists of a Race's ID and icon text.
  • CharacterVisualResourceID: A reference to the Visuals files for our race.
  • Race: This will be the UUID field of your Race entry in Races.lsx.

Create two new Base Root Templates for your Race in Community Library's RootTemplates folder, filling in what you can. You'll want to skip CharacterVisualResourceID for now. We'll get back to that one soon enough.

Implementation Root Templates

Next, we'll take a look at the implemented versions. These, you'll be setting up in your Implementation mod. Let's look at my implemented ShadarKai player first:

<?xml version="1.0" encoding="utf-8"?>
<save>
  <version major="4" minor="0" revision="0" build="51" />
  <region id="Templates">
    <node id="Templates">
      <children>
        <node id="GameObjects">
          <attribute id="MapKey" type="FixedString" value="692bd578-f4fe-43b1-be34-dfcfef091fe8" />
          <attribute id="Name" type="LSString" value="Elves_Female_ShadarKai_Player" />
          <attribute id="LevelName" type="FixedString" value="" />
          <attribute id="Type" type="FixedString" value="character" />
          <attribute id="ParentTemplateId" type="FixedString" value="27410506-9276-48e7-b4b4-38d861aa1be7" />
          <attribute id="Flag" type="int32" value="0" />
          <attribute id="Icon" type="FixedString" value="d67bd924-3c1f-c33a-5298-feca5bbdc284-_(Icon_Elf_Female)" />
          <attribute id="Stats" type="FixedString" value="HeroElfFemale" />
          <attribute id="SpellSet" type="FixedString" value="CommonPlayerActions" />
          <attribute id="CharacterVisualResourceID" type="FixedString" value="4358bd61-aade-4eab-b6b5-eb66c48aced0" />
          <attribute id="PortraitVisualResourceID" type="FixedString" value="e1254a4e-3d52-76c2-7b19-52e93941b6dd" />
          <children>
            <node id="SpeakerGroupList">
              <children>
                <node id="SpeakerGroup">
                  <attribute id="Object" type="guid" value="02cdc15e-1dc3-5eb4-3cf7-5894f9b49f0b" />
                </node>
                <node id="SpeakerGroup">
                  <attribute id="Object" type="guid" value="e0d1ff71-04a8-4340-64ae-849646d883eb" />
                </node>
              </children>
            </node>
            <node id="GameMaster" />
            <node id="LocomotionParams" />
          </children>
        </node>
      </children>
    </node>
  </region>
</save>

Looks a bit familiar, but it has a few additional fields:

  • Flag: Typically 0.
  • Stats: References a Data field. We'll get to these later.
  • SpellSet: typically CommonPlayerActions for playable races.
  • PortraitVisualResourceID: Corresponds to a visual for the character's portrait.

It also has a few new SpeakerGroup fields as children. These will typically be the same across all playable races, and map to the feminine and masculine voices.

The Parent template references out Base template for the corresponding sex, and the name is more specific, but otherwise, not too much new.

Finally, let's look at the Strong version, for buff bodies:

<?xml version="1.0" encoding="utf-8"?>
<save>
  <version major="4" minor="0" revision="0" build="51" />
  <region id="Templates">
    <node id="Templates">
      <children>
        <node id="GameObjects">
          <attribute id="MapKey" type="FixedString" value="8bd25683-8607-48b4-a7f8-e9f1fecba74a" />
          <attribute id="Name" type="LSString" value="Elves_Female_ShadarKai_Player_Strong" />
          <attribute id="LevelName" type="FixedString" value="" />
          <attribute id="Type" type="FixedString" value="character" />
          <attribute id="ParentTemplateId" type="FixedString" value="692bd578-f4fe-43b1-be34-dfcfef091fe8" />
          <attribute id="Icon" type="FixedString" value="eb370ddf-f7a4-4349-116e-f489556bab1d-_(Icon_Human_Female_Strong)" />
          <attribute id="EquipmentRace" type="guid" value="47c0315c-7dc6-4862-9bb3-f38b0fa1548b" />
          <attribute id="CharacterVisualResourceID" type="FixedString" value="cd4bceed-4cbb-4e9a-8531-c08bbb857ccf" />
          <attribute id="GeneratePortrait" type="LSString" value="Icon_Human_Female_Strong" />
          <children>
            <node id="Tags">
              <children>
                <node id="Tag">
                  <attribute id="Object" type="guid" value="d3116e58-c55a-4853-00a7-e9be20969773" />
                </node>
              </children>
            </node>
            <node id="GameMaster" />
            <node id="LocomotionParams" />
          </children>
        </node>
      </children>
    </node>
  </region>
</save>

Three important new things:

  • EquipmentRace: This defines the equipment to switch to now that we're using buff bodies. Without it, there'll be a ton of clipping.
  • GeneratePortrait: This maps to an icon, and for Strong character types, will often reference Humans rather than other races.
  • Tags: This sets the Swole* tag on your character when selecting the strong bodies.

*Note: It's not actually called Swole.

Why use Community Library for Your Root Templates?

By putting your Base root templates into Community Library, other modders can utilize it for situations where they want to add an NPC using your added race. Rather than duplicating the effort of having their own unique Base template, they can build off of yours, and you can build off of theirs. It's also one less file you have to worry about maintaining directly.

Compiling back to .lsf

With this information, play around to your hearts content. We'll dive into Visuals next, but first, once you're done editing your Root Templates, you'll want to convert them to .lsf:

  1. In LSLib, navigate to the LSX/LSB/LSF/LSJ Tools tab.
  2. Under "Batch Convert," set the Input Directory to match CommunityLibrary's RootTemplates directory.
  3. Do the same for "Output Directory."
  4. Make sure Input format is set to LSX and Output format is set to LSF.
  5. Hit "Convert" and wait for the process to complete.
  6. Do the same for your Implementation Mod's RootTemplates directory.

Character Visuals

This will look similar to Root Templates. These are also .lsf files that you'll want to convert to .lsx, and they can be found in the Public/ModName/Content/[PAK]_CharacterVisuals/._merged.lsf file, all bundled together. This goes for Shared and Gustav, as well, if you want to get a sense of things from the official files. There will be 6 total "Resource" nodes you'll want: 2 Within Community Library, and 4 within your Implementation Mod.

Base Visuals

Once you've converted and opened _merged.lsf, you'll see something like this:

<?xml version="1.0" encoding="utf-8"?>
<save>
  <version major="4" minor="0" revision="0" build="51" />
  <region id="CharacterVisualBank">
    <node id="CharacterVisualBank">
      <children>
        <node id="Resource">
          <attribute id="BaseVisual" type="FixedString" value="4deb74a3-fe9a-723f-89b5-799a97335cc4" />
          <attribute id="BodySetVisual" type="FixedString" value="017534b5-2b56-4160-9d8c-7712d3a413ea" />
          <attribute id="ID" type="FixedString" value="b8e49e88-a0bd-480d-9e8b-8a24a4444376" />
          <attribute id="Localized" type="bool" value="False" />
          <attribute id="Name" type="LSString" value="BASE_Elves_Male_ShadarKai" />
          <attribute id="ShowEquipmentVisuals" type="bool" value="False" />
          <attribute id="_OriginalFileVersion_" type="int64" value="144115205255725056" />
          <children>
            <node id="MaterialOverrides">
              <attribute id="MaterialResource" type="FixedString" value="" />
              <children>
                <node id="ColorPreset">
                  <attribute id="ForcePresetValues" type="bool" value="True" />
                  <attribute id="GroupName" type="FixedString" value="" />
                  <attribute id="MaterialPresetResource" type="FixedString" value="" />
                </node>
                <node id="MaterialPresets">
                  <children>
                    <node id="Object">
                      <attribute id="ForcePresetValues" type="bool" value="True" />
                      <attribute id="GroupName" type="FixedString" value="02Skin Properties" />
                      <attribute id="MapKey" type="FixedString" value="02Skin Properties" />
                      <attribute id="MaterialPresetResource" type="FixedString" value="ace8e94a-4ec0-45b3-6ff0-46a6a4a008ed" />
                    </node>
                  </children>
                </node>
              </children>
            </node>
            <node id="Slots">
              <attribute id="Slot" type="FixedString" value="Head" />
              <attribute id="VisualResource" type="FixedString" value="c1c97ff6-04b6-37c0-b855-681b18447fc4" />
            </node>
            <node id="Slots">
              <attribute id="Slot" type="FixedString" value="Body" />
              <attribute id="VisualResource" type="FixedString" value="123ff6f0-f424-61f1-1881-2cefaaf5f8ec" />
            </node>
            <node id="Slots">
              <attribute id="Slot" type="FixedString" value="Body" />
              <attribute id="VisualResource" type="FixedString" value="7a0fbf45-3619-b7f9-f405-2fd48bfd08cb" />
            </node>
            <node id="Slots">
              <attribute id="Slot" type="FixedString" value="Footwear" />
              <attribute id="VisualResource" type="FixedString" value="2627cae6-0565-7a31-466c-2ef8c283d9e3" />
            </node>
            <node id="Slots">
              <attribute id="Slot" type="FixedString" value="Hair" />
              <attribute id="VisualResource" type="FixedString" value="b801b19c-a373-0c0b-0a8c-30758b460178" />
            </node>
          </children>
        </node>
      </children>
    </node>
  </region>
</save>

It may have more or different children fields. In the case of the above, this is what a base visual will look like. Let's take a look at what we have:

  • BaseVisual: The ID of whichever visual this inherits from.
  • BodySetVisual: The ID of the BodySetVisual this inherits from.
  • ID: As with everything else, this is how the resource node is referenced elsewhere.
  • Localized: Whether or not any values are localized. This will typically be false here.
  • Name: Just like with the Root Template, this is the internal name for the Resource node
  • ShowEquipmentVisuals: Covers whether or not Equipment Visuals will be shown. For base visuals, this is typically false.
  • _OriginalFileVersion: More for posterity than anything else, this defines the version of the file. Safest to keep it the same.
  • Material Overrides
    • Color Presets: Optional reference to a Material Preset Resource governing colors
    • Material Presets: Optional reference to a Material Preset. For Base classes, this will typically just cover Skin Properties
  • Slots: There will be several of these, typically defining Visual resources for specific parts of the body

With that information, and the above example, you should be ready to tackle your own Base Visuals! Once you've played around with it, let's take a look at what the more detailed, implemented versions of visuals look like:

Implementation Visuals

Just as with Root Templates, the base Race Visual Resources have been defined in Community Library. For your custom race to become useable, you'll need Resources for the Player to use. These will be set up in your implementation mod. These have a tendency to be huge, so I won't copy/paste in a full example, but these are the attributes you can expect. Many of these will be very familiar:

  • BaseVisual: This will typically reference the ID of your above Base visual
  • BodySetVisual
  • ID
  • Name
  • ShowEquipmentVisuals
  • _OriginalFileVersion
  • MaterialOverrides
    • MaterialPresets
    • ScalarParameters
    • Vector3Parameters
  • Slots

We saw most of these in the Base Visual resource earlier, but the ScalarParameters and Vector3Parameters are new. These would look something like this:

<node id="ScalarParameters">
    <attribute id="Color" type="bool" value="False" />
    <attribute id="Custom" type="bool" value="True" />
    <attribute id="Enabled" type="bool" value="False" />
    <attribute id="Parameter" type="FixedString" value="doBodyHide" />
    <attribute id="Value" type="float" value="1" />
</node>
<node id="Vector3Parameters">
    <attribute id="Color" type="bool" value="False" />
    <attribute id="Custom" type="bool" value="True" />
    <attribute id="Enabled" type="bool" value="False" />
    <attribute id="Parameter" type="FixedString" value="AddedColor" />
    <attribute id="Value" type="fvec3" value="0.02570003 0 0" />
</node>

There are any number of these per resource, and they're identified by their Parameter fields. A non-exhaustive list is as follows:

Scalar Parameters:

DetailNormalStrength, Anisotropy, Reflectance, Opacity, Roughness, PixelDepthOffsetRoot, PixelDepthOffset, DepthTransitionMidPoint,
DepthTransitionSoftness, ColorTransitionMidPoint, ColorTransitionSoftness, IDContrast, ColorDepthContrast, UseOcclusion, Scalp_Scatter,
Scalp_ColorTransitionMidPoint, Scalp_ColorTransitionSoftness, Scalp_DepthColorExponent, Scalp_DepthColorIntensity, Scalp_IDContrast,
Scalp_ColorDepthContrast, Scalp_Roughness, Scalp_RoughnessContrast, doDrawMakeup, doBodyHide

Vector3Parameters:

AddedColor, Color_01, Color_02, Color_03

Wrapping Up

Once we've got our Visuals defined, we'll want to go back to our Root Templates, and set our CharacterVisualResourceID fields to the MapKey of the relevant visual. Then, we'll want to convert our _merged.lsx files back into _merged.lsf. Finally, we're ready to move on to getting the Race selectable.

Character Creation

A Light Recap

To recap what we've done so far:

  1. We've created our mod structure for the implementation mod, and prepared a local branch on the Community Library
  2. We created a Race entry inside the Community Library's Races.lsx file
  3. We've defined Root Templates within Community Library for the base racial template, and within our Implementation mod for the Player racial templates.
  4. We've also defined Visual Resources within Community Library for the base racial visuals, and within our Implementation mod for the Player racial visuals.

We're about halfway through the process now. The next, and arguably most important step, is to ensure our Race is playable. For this step, we'll only be working with our Implementation Mod:

Creating Presets

  1. In Public/ModName, create a subfolder titled CharacterCreationPresets.
  2. Under CharacterCreationPresets, create a file titles CharacterCreationPresets.lsx.

This file covers the main presets for choosing a character of a specific race. Examples can be found in Shared on how these look, but typically for one race, you'll have 4 presets - one each for Feminine, Masculine, Feminine Strong, and Masculine Strong. It'll look something like this, only with four CharacterCreationPreset nodes:

<?xml version="1.0" encoding="UTF-8"?>
<save>
  <version major="4" minor="0" revision="0" build="47"/>
  <region id="CharacterCreationPresets">
    <node id="root">
      <children>
        <node id="CharacterCreationPreset">
          <attribute id="BodyShape" type="uint8" value="0"/>
          <attribute id="BodyType" type="uint8" value="1"/>
          <attribute id="CloseUpA" type="LSString" value="ELF_F_Camera_Closeup_A"/>
          <attribute id="CloseUpB" type="LSString" value="ELF_F_Camera_Closeup_B"/>
          <attribute id="Overview" type="LSString" value="ELF_F_Camera_Overview_A"/>
          <attribute id="RaceUUID" type="guid" value="6c038dcb-7eb5-431d-84f8-cecfaf1c0c5a"/>
          <attribute id="RootTemplate" type="guid" value="692bd578-f4fe-43b1-be34-dfcfef091fe8"/>
          <attribute id="SubRaceUUID" type="guid" value="27844147-3e0f-4b2e-8377-8d015b8384df"/>
          <attribute id="UUID" type="guid" value="014a6ff2-a134-46a0-b587-2186727f1dc1"/>
          <attribute id="VOLinesTableUUID" type="guid" value="14df8f45-90af-4bd0-8024-42624da9976e"/>
        </node>
      </children>
    </node>
  </region>
</save>

Overall, it's fairly simple. To break down each attribute, we have:

  • BodyShape: 0 = Regular, 1 = Strong
  • Body Type: 0 = Masculine, 1 = Feminine
  • CloseUpA: The Camera viewpoint for closeups (A) - typically you'll want something that matches an existing race with a similar stature to your race
  • CloseUpB: Similar to the above, but a different Closeup value
  • Overview: The Camera viewpoint for the zoomed out view in CC. Overall, similar to the above
  • RaceUUID: This will be the UUID of the Race you created at the beginning of the guide
  • RootTemplate: The Relevant Implementation Root Template for the Body Type and Shape
  • SubRaceUUID: The relevant UUID for a Sub-Race, if you made subraces.
  • UUID: The identifier for this preset. Generate a new UUID for it.
  • VOLinesTableUUID: References the Voiceover Lines Table. Typically for a playable character, this will be 14df8f45-90af-4bd0-8024-42624da9976e

Easy, right? Well, we're not done yet, but let's pack up the Implementation Mod and our modified Community Library, and see how it all looks in-game so far. 1.

Localization

You may have noticed that, while your race shows up, there's a lot of "Not Found." in the name and description. We created a new handle for display names and descriptions, but we never actually mapped those handles to anything. We'll need to do that next. This will take place entirely within Community Library.

An Overview

In your Community Library branch, open Localization/English/CL_Races.xml It'll look something like this:

<contentList date="8/21/2023 5:00">
  <content contentuid="h9a4e44c9g72e0g43b6ga72bgd83fcb6b40d1" version="1">Text</content>
  <content contentuid="h7365c7bega8e7g4746g9f64g50548db16e70" version="1">Text</content>
  <content contentuid="h7901179eg9a5ag4a33gb561g10acfe80a85c" version="1">Text</content>
  ...
</contentList>

contentui is the handle that the game reads to identify which line goes where. The text within each Content element is what will display on the screen. If nothing is defined for a given handle, you'll see "Not Found" ingame.

Writing Localization Strings

If you recall, way back in the beginning, when we were setting up our Races.lsx entries, we generated a handle for DisplayName and Description. We're going to use those to make sure our text is readable:

  1. In Races.lsx, copy your Race's DisplayName field's handle
  2. Add a new content element, pasting in your DisplayName field's handle into contentuid.
  3. In between the opening and closing tags for your content element, input the name of your race.
  4. Repeat the above steps for the Description field, inputting the description for your race.

Compiling the Localization

You're almost ready to test your Race again! Just one thing first: We need to convert our CL_Races.xml into a .loca file.

  1. Open LSLib and navigate to the Localization Tab
  2. Under Input file path, copy and paste the location and filename of your CL_Races.xml file
  3. Under Output file path, do the same, but replace .xml with .loca`
  4. Hit Convert

Now you just need to package your local version of community library, and test again. You should see the text you'd expect to see!

Racial Feats

We've got our race ready, but something feels off. There's nothing special about the race, nothing that makes it unique or interesting. This is where Feats and Progressions come in! First, we'll start with Feats. You can do this within Community Library, or within your Implementation mod. There are a few examples of what these look like within Community Library, all within Public/ModName/Stats/Generated/Data/:

Passives

In your Community Library branch, open CL_Passives.txt, and take a look at CL_Passive_RavenQueenBlessing`.

new entry "CL_Passive_RavenQueenBlessing"
type "PassiveData"
data "DisplayName" "h5bcb3c31gc511g4bc3gbf7bgde64d29ab296"
data "Description" "h6dc6d54fgbbdeg4b6cgadc8g3922b34328e3"
data "Conditions" "SpellId('CL_Target_RavenQueenBlessing')"
data "StatsFunctors" "ApplyStatus(CL_STATUS_RAVEN_QUEEN_BLESSING, 100, 1)"
data "StatsFunctorContext" "OnSpellCast"

This is a passive, with a DisplayName and Description handle. It has a Conditions field targetting a specific Spell ID. and StatsFunctors, which is a function applied based on the above defined conditions, as well as the StatsFunctorContext beneath. In this example, when the spell CL_Target_RavenQueenBlessing is cast, the caster receives the status CL_STATUS_RAVEN_QUEEN_BLESSING, with 100% accuracy, for 1 turn.

Passives can do a variety of things, and they don't always require something to happen first. You'll also notice near the top, this entry:

new entry "CL_Passive_Petpal"
type "PassiveData"
data "DisplayName" "he617ad50g9705g4074gb9a5ge11b1fb04aec"
data "Description" "haa2a6bafg712dg477egb241gbcdcd360b364"
data "Boosts" "ApplyStatus(PETPAL,100,-1)"

Boosts defines a bonus applied to the character with the passive. In this case, it permanently applies the status of PETPAL, allowing a permanent Talk to Animals effect. A boost can confer statuses, resistances, vulnerabilities, immunities, and more.

Spells/Actions

Open CL_Spells_Target.txt. Look for the entry CL_Target_RavenQueenBlessing.

new entry "CL_Target_RavenQueenBlessing"
type "SpellData"
data "SpellType" "Target"
data "SpellProperties" "GROUND:TeleportSource();"
data "TargetRadius" "9"
data "AreaRadius" "1"
data "Height" "0"
data "Acceleration" "0"
data "TooltipUseCosts" "BonusActionPoint:1;RQBlessing:1"
data "TargetConditions" "CanStand('') and not Character() and not Self()"
data "TeleportSelf" "Yes"
data "TeleportSurface" "No"
data "Icon" "Action_Ranger_RangersCompanion_Raven"
data "DisplayName" "he699f74ag1af1g48d3gbbf9g19d38c41bab6"
data "Description" "h234b5017g929bg4403g9031gdc09713fbb1a"
data "UseCosts" "BonusActionPoint:1;RQBlessing:1"
data "PreviewCursor" "Cast"
data "CastTextEvent" "Cast"
data "PrepareEffect" "7121a488-7c9a-4ba1-a585-f79aaa77e97c"
data "SpellAnimation" "dd86aa43-8189-4d9f-9a5c-454b5fe4a197,,;,,;39daf365-ec06-49a8-81f3-9032640699d7,,;5c400e93-0266-499c-a2e1-75d53358460f,,;cc5b0caf-3ed1-4711-a50d-11dc3f1fdc6a,,;,,;1715b877-4512-472e-9bd0-fd568a112e90,,;,,;,,"

We have quite a bit more to look at here, so first I'll explain what the spell does, then I'll break it down. This spell allows you to target a specific location within 9m, and teleport to it. It costs a Bonus Action, as well as a resource, RQBlessing. It can't be cast on self or on a Character, but it teleport's the player.

  • SpellType: References the type of spell we're casting. This can be Teleport, Shout, Target, or others. More detail can be found in Shared, SharedDev, Gustav, and GustavDev.
  • SpellProperties: Defines the specific properties of the spell.
  • TargetRadius: Defines the total radius of the spell around the caster.
  • AreaRadius: Defines the total radius of the location the spell is cast.
  • Height: Defines the possible height of the spell.
  • Acceleration: Defines the speed of the player going through the spell.
  • UseCosts: The Cost of the Spell
  • TooltipUseCosts: Defines the Cost of the Spell as shown in its tooltip
  • TargetConditions: The conditions to be able to cast on the target.

The rest aren't particularly important to know 100% of what they do, but you'll want to be sure that your spell/action has a SpellAnimation field, otherwise the spell may become impossible to cast. I recommend finding a spell with a similar animation for casting and preparing, and then using its SpellAnimation field.

I would recommend looking through the spells and passives in the Shared, SharedDev, Gustav, and GustavDev folders to get a sense of what it possible.

Localization

Regardless of what you're adding, or where you're adding it, you'll need to add some localization lines. You can either create a localization .xml file similar to the one you found in Community Library, if making it in your Implementation Mod, or you can add it to a relevant or new localization .xml in your Community Library branch. You'll need one for each Description field and DisplayName field you use.

If your spell/action includes a DescriptionParams field, you'll want to end your Description field in the Spell definition with ;#, replacing # with the amount of Description Parameters defined. Then, in your Localization String in your Localization .xml file, to reference those parameters, you'll input [#], with # being replaced with the number that represents your desired Description Parameter.

Finally, you'll save and compile the localization files, as you learned above.

Racial Progressions

So, we've got these cool feats, but how do we add them to our race? This is where Spell Lists and Progressions come in. Like the above section, you can keep these within your Implementation mod, or you can set them up in Community Library. We'll take a look at some examples within Community Library.

Spell Lists

Let's check out our Spell Lists - Open up Public/ModName/Lists/SpellLists.lsx. You'll see something like this:

<?xml version="1.0" encoding="UTF-8"?>
<save>
  <version major="4" minor="0" revision="0" build="49"/>
  <region id="SpellLists">
    <node id="root">
      <children>
        <node id="SpellList">
          <attribute id="Comment" type="LSString" value="Raven Queen's Blessing Ability"/>
          <attribute id="Spells" type="LSString" value="CL_Target_RavenQueenBlessing"/>
          <attribute id="UUID" type="guid" value="cdea5ca8-e43f-4830-a265-76fec11ab79a"/>
        </node>
      </children>
    </node>
  </region>
</save>

As you can see, these SpellList nodes are pretty small. You have a Comment attribute, which is just a descriptive string on what the spells are for. You have a Spells attribute, which is a semi-colon-separated list of Spell IDs (Important to note that you don't add Passives here). Finally, you have a UUID attribute, which is yet another UUID to generate.

Progressions

So now you have this SpellList, or maybe you didn't add any spells, but want to know what to do with your passives. Like the above section, you can define these in either your Implementation Mod or Community Library. For an example of what this might look like, open any of the .lsx files under the Progressions folder. You'll see something like this:

<?xml version="1.0" encoding="UTF-8"?>
<save>
    <version major="4" minor="0" revision="0" build="49"/>
    <region id="Progressions">
        <node id="root">
            <children>
                <node id="Progression">
                  <attribute id="Boosts" type="LSString" value="Ability(Constitution,1);ActionResource(RQBlessing,2,0)"/>
                  <attribute id="Level" type="uint8" value="1"/>
                  <attribute id="Name" type="LSString" value="CL_ShadarKai"/>
                  <attribute id="PassivesAdded" type="LSString" value="CL_Passive_NecroticResistance;CL_KeenSenses"/>
                  <attribute id="ProgressionType" type="uint8" value="2"/>
                  <attribute id="Selectors" type="LSString" value="AddSpells(cdea5ca8-e43f-4830-a265-76fec11ab79a,,,,AlwaysPrepared)"/>
                  <attribute id="TableUUID" type="guid" value="01735427-a4df-4ce4-8fb6-4b1b8c6c3771"/>
                  <attribute id="UUID" type="guid" value="2f7edf7e-0a6b-4018-9715-1cb8aa238e4a"/>
                </node>
                <node id="Progression">
                  <attribute id="Level" type="uint8" value="3"/>
                  <attribute id="Name" type="LSString" value="CL_ShadarKai"/>
                  <attribute id="PassivesAdded" type="LSString" value="CL_Passive_RavenQueenBlessing"/>
                  <attribute id="ProgressionType" type="uint8" value="2"/>
                  <attribute id="Selectors" type="LSString" value=""/>
                  <attribute id="TableUUID" type="guid" value="01735427-a4df-4ce4-8fb6-4b1b8c6c3771"/>
                  <attribute id="UUID" type="guid" value="f2d55a86-433b-4f79-b381-7510cafef385"/>
                </node>
            </children>
        </node>
    </region>
</save>

It's fairly simple, but let's break this down. In each node, we have:

  • Boosts: A semicolon-separated list of values, typically Ability bonuses or action resources granted to the character.
  • Level: The level at which the progression takes effect
  • Name: An internal identifier
  • PassivesAdded: A semicolon-separated list of passive IDs
  • ProgressionType
  • Selectors: A semicolon-separated list of functions, granting particular Options. If using spells, you'll see AddSpells, which takes in the UUID of a SpellList node. There are other options as well, such as SetSkills. To see more, check through Shared, SharedDev, Gustav, or GustavDev.
  • TableUUID: The UUID that represents a set of Progressions
  • UUID: The UUID representing the specific progression

The above two nodes do the following:

  1. At level 1, the character gains +1 to their Consititution score, as well as 2 RQBlessing Action Resources. They also receive the Necrotic Resistance and Keen Senses passives, along with a spell that's always prepared, defined in a specific SpellList.
  2. At level 3, the character gains the Raven Queen's Blessing passive.

Applying the Progression

All that's left to make this character playable is to go back to the beginning. In Races.lsx, where we defined the race, we need to go take a look at our race's ProgressionTableUUID. Here, we'll want to copy/paste our TableUUID from our Progressions nodes. Once that's done, we can repack the mod, then jump in with a fully-featured custom race!

Tags

Everything currently looks great in Character Creation, but if you started a game with the character and looked at your Character Sheet, you might have noticed a couple issues:

  1. Your Race doesn't show up at all
  2. There are no tags outside of the Baldurian tag.

This could be an issue. After all, Tags are where the majority of the game's content becomes available. Dialog paths and interactions rely heavily on what tags the player has available to them. These are typically saved as .lsf files, so you'll need to make sure you compile yours with LSLib once you're done setting them up. But first, let's set up a tag:

  1. Navigate to CommunityLibrary/Public/CommunityLibrary/.
  2. Create a Tags folder.
  3. Generate a UUID and copy it.
  4. Within the Tags folder, create an .lsx file, and paste your UUID as the filename.

Now, let's open our Tag up, and paste in some Boilerplate:

<?xml version="1.0" encoding="utf-8"?>
<save>
  <version major="4" minor="0" revision="0" build="58" />
  <region id="Tags">

  </region>
</save>

Anatomy of a Tag

Great, now that we have that, we're ready to take a look at the contents of a tag file:

    <node id="Tags">
      <attribute id="Description" type="LSString" value="|Shadar-Kai|" />
      <attribute id="DisplayDescription" type="TranslatedString" handle="h38c61b04g98b8g4cc4g8cfcga1b205b26d13" version="1" />
      <attribute id="DisplayName" type="TranslatedString" handle="h976d56ddgba92g413agbd32gd85dd2551d8f" version="2" />
      <attribute id="Icon" type="FixedString" value="" />
      <attribute id="Name" type="FixedString" value="CL_ShadarKai" />
      <attribute id="UUID" type="guid" value="18e053a8-3887-4ffa-1593-128e7285a4e4" />
      <children>
        <node id="Categories">
          <children>
            <node id="Category">
              <attribute id="Name" type="LSString" value="Code" />
            </node>
            <node id="Category">
              <attribute id="Name" type="LSString" value="Dialog" />
            </node>
            <node id="Category">
              <attribute id="Name" type="LSString" value="Race" />
            </node>
            <node id="Category">
              <attribute id="Name" type="LSString" value="PlayerRace" />
            </node>
            <node id="Category">
              <attribute id="Name" type="LSString" value="CharacterSheet" />
            </node>
          </children>
        </node>
      </children>
    </node>

Here, we can see that a tag is made up of small amount of attributes, and has multiple categories it can be a part of. This tag in particular is setting up the tag containing the race's name and description.

  • Description: This will be a string, sometimes formatted with a pipe symbol. It should be representative of your Tag's purpose.
  • DisplayDescription: This is a localization handle corresponding to the desired description of the race. Typically this will be shorter than the description shown in Character Creation, but you can reuse the handle used for your race if you'd like.
  • DisplayName: Like the above, this is a localization handle corresponding to the name of your race.
  • Icon: For a race, this will typically be blank.
  • Name: This is the internal string reference to your Tag.
  • UUID: This is an ID for your tag. However, this is not how you'll reference the tag in other files - you'll instead want to use its Filename, which is a different UUID.

In addition to the above fields, there's also a significant amount of Categories listed in this example, under the Children element. These all define where the tag can be expected to be used.

Once you've created your tag, compile it into a .lsf file using LSLib.

Using the Tag

Now that we have our tag, let's go into our Races.lsx file. Under your Race node's children element, paste the following, changing the value to the filename of your tag:

            <node id="Tags">
              <attribute id="Object" type="guid" value="18e053a8-3887-4ffa-9315-8e188572e4a4"/>
            </node>

While you're here, copy your Race's UUID. You'll need it for the next step.

Ensuring the Tags reach the Root Template correctly

Now we're going to take another look at your Root Templates. Whichever Root Templates you've added that have a Race field, you'll want to open and perform these actions on:

  1. Paste the value you came up with into the value field of your Race Attribute.
  2. Paste the following into your Template's children element, replacing the guid value with your tag's filename:
            <node id="Tags">
              <children>
                <node id="Tag">
                  <attribute id="Object" type="guid" value="18e053a8-3887-4ffa-9315-8e188572e4a4" />
                </node>
              </children>
            </node>

Again, be sure to do this for EVERY Root Template you've defined for your Race. Once you've done this, you're pretty much finished with the mod, unless...

Custom Icons


WIP Notes:

As of September 2nd 2023, The ImprovedUI Mod has updated and simplified the process of adding Custom icons. This part of the guide will be updated eventually.


Now that you've got everything set up, there's one thing that might be bothering you.

Default Race Icon

It exists, that's for sure. It might even be fine as is. But if you want something unique, something that draws the player's attention and really says "This is my custom race, right here," this section is for you.

I'm here to tell you that it's quite possible, with a bit of legwork. If you don't have an icon in mind yet, that's fine. Come back to this step when you've got something you want. This guide won't walk you through the artistic creation of the icon, although you'll want to make sure of a few things:

  1. Make sure your desired icon is 196x196.\
  2. Use the naming scheme Race_Subrace.DDS (for example, Elf_ShadarKai.DDS)
  3. Save your copy in .DDS format (all caps).

Forking BG3 ImprovedUI and Importing Icons

Now you're ready to plug it in. To do this, you'll need to create a fork of BG3 ImprovedUI.

  1. Fork the Repository
  2. Clone your forked repository to your PC and make a new branch: `git checkout -b "MyRaceName-icons" 3.open it up in VSCode.
  3. Navigate to or create the directory ImprovedUI/Public/Game/GUI/Assets/CC/icons_races
  4. Drop your race icons into the icons_races folder.

Editing the XAML

It's time to edit the .xaml files.

  1. Navigate to ImprovedUI/Public/Game/GUI/Assets/Library folder
  2. Open the file DataTemplates.xaml 3 Look for the DataTrigger elements referencing Racial icons. It will look something like this:
<DataTrigger Binding="{Binding Guid}" Value="a459ba68-a9ec-4c8e-b127-602615f5b4c0">
    <Setter Property="OpacityMask">
        <Setter.Value>
            <ImageBrush ImageSource="pack://application:,,,/GustavNoesisGUI;component/Assets/CC/icons_races/Elf_WoodElf.png"/>
        </Setter.Value>
    </Setter>
</DataTrigger>
  1. If making a subrace, find the DataTrigger that is nearest to your subraces main race, otherwise go to the last of the Race-related DataTriggers.
  2. Copy and Paste the DataTrigger and its child elements beneath the one you found.
  3. Edit the component/Assets/CC/icons_races/filename.png to match your race icon's filename.
  4. Save the file

Packaging and Testing

So we're ready to make a Pull Request and make this official, right? Not quite. First, let's test the icon - make sure it looks like how you want it.

  1. Open LSLib's ConverterApp
  2. Navigate to PAK/LSV Tools
  3. Under "Create Package," set the Priority field to 21
  4. Also make sure the version is V18 (Baldur's Gate 3 Release)
  5. Enter your Source Path and expected Package Path for your local version of ImprovedUI and hit "Create Package."
  6. Import your new ImprovedUI.pak into your load order, save it, and then load the game.
  7. See if it looks good, and make any adjustments if you think it needs changing.

Making the Pull Request

Now it's time to submit this to ImprovedUI. Don't worry, most of this will just involve waiting for it to be accepted.

  1. Using Gitbash, add the changed and added files to your commit: git add ImprovedUI/Public/Game/GUI
  2. use git commit to make your commit.
  3. use git push --set-upstream origin BRANCHNAME`, subbing BRANCHNAME for the branch name we set up earlier in this section.
  4. Open your forked repo on github. There'll be a little notification that looks something like this: Github notification showing "This branch is 1 commit ahead of TheRealDjmr:main." as well as two actions: Contribute and Sync Fork
  5. Select "Contribute" and proceed through the steps to make a Pull Request.

Including the icons locally

We're technically done, but this last step will be good to have, just in case. At the start of the section, we dropped our icons into ImprovedUI's icons_races folder. We're going to want to do that for our mod, as well.

Now, because Community Library doesn't cover Character Creation bits and pieces, there's no need to submit a Pull Request for the icons - you'll want to keep them with your implemented mod.

  1. In your ModName/Public, folder, create the following folder structure: Game\GUI\Assets\CC\icons_races
  2. Drop your racial icons into icons_races.

Now you're good to go. I recommend doing one last test, and when you're sure it works, submit it to Nexus!

Clone this wiki locally