The PCGen LST format: Part 2 – Races

Category: Games

Suppose we wanted to define a new race. How would we go about defining something like the Halfling from the d20 SRD?

Halflings

  • +2 Dexterity, -2 Strength.
  • Small: As a Small creature, a halfling gains a +1 size bonus to Armor Class, a +1 size bonus on attack rolls, and a +4 size bonus on Hide checks, but she uses smaller weapons than humans use, and her lifting and carrying limits are three-quarters of those of a Medium character.
  • Halfling base land speed is 20 feet.
  • +2 racial bonus on Climb, Jump, Listen, and Move Silently checks.
  • +1 racial bonus on all saving throws.
  • +2 morale bonus on saving throws against fear: This bonus stacks with the halfling’s +1 bonus on saving throws in general.
  • … (list of traits continues)

The PCGen Documentation > List Files > LST File Classes, Lessons 3 through 6, Fundamentals of Race Building, walks through the construction of example races. These lessons are enough to learn the basics.

My concern with the LST File Classes is that they were written many years ago, and don’t always show the “modern” way of doing things. e.g. the use of the SAB tag where the ASPECT tag would be preferred for new code. I would prefer to learn the language by dissecting the Pathfinder and DnD 3.5 data files, which are the most up-to-date available in the PCGen project.

↞※↠

In this article, I will dissect the Pathfinder data files cr_races.lst and cr_abilities_race.lst files to determine how the Dwarf race is defined, and trace back the code responsible for implementing the Dwarf ~ Defensive Training ability (one of a half-dozen abilities making up the Dwarf.)

In dissecting these files, I have discovered that the Pathfinder and DnD 3.5e data files are made quite complicated by the need to support sub-races (i.e. Hill Dwarf, Aquatic Dwarf, Desert Dwarf…).

Home-brew races don’t need this complexity, and are therefore much easier to implement. I will write a separate article walking through the coding of a home-brew race. Those who are only interested in simple home-brew code may await that article.

For the time being, let us press on with the dissection of the PCGen data files.


Files where races are defined

In PCGen, the races for the Pathfinder system are defined in the following files, all found in /data/pathfinder/paizo/roleplaying_game/core_rulebook:

  • cr_races.lst - basic statistical information for each race, such as:
    • SIZE: - small, medium, large, huge
    • TYPE: - Humanoid, Undead
    • MOVE: - Walk,30, Swim,60
  • cr_abilities_race.lst - a collection of ABILITY records which define each race’s traits and abilities, such as:
    • stat bonuses (+2 CON, -2 CHA)
    • skill bonuses (+2 to Open Lock)
    • saving throw bonuses (+1 Luck bonus to all saves)
  • cr_abilitycategories.lst - a small file - defines the categories of abilities ,such as Dwarf Racial Trait, Fighter Bonus Feat, etc. which are used when coding cr_abilities_race.lst. The most obvious use of this file is to group abilities for the player to choose amongst, i.e. the category Fighter Bonus Feat which contains all the feats that Fighter-class characters are allowed to take at 1st, 2nd, 4th, etc. levels.

Note the file-names all start with cr standing for the [Pathfinder] core rulebook.

The 3.5ed files are similarly named rsrd for the Revised System Reference Document - look in data/35e/wizards_of_the_coast/rsrd/basics/.

The filenames [x]_races.lst etc. are standardised according to the Data LST standards.

Basic race information: cr_races.lst

The definition of the Dwarf race in cr_races.lst is fairly straightforward.

Dwarf
    SORTKEY:a_base_pc
    STARTFEATS:1
    SIZE:M
    MOVE:Walk,20
    FACE:5
    REACH:5
    LANGBONUS:Giant,Gnome,Goblin,Orc,Terran,Undercommon
    BONUS:STAT|CON|2
    BONUS:STAT|WIS|2
    BONUS:STAT|CHA|-2
    ABILITY:Internal|AUTOMATIC|Race Traits ~ Dwarf
    RACETYPE:Humanoid
    RACESUBTYPE:Dwarf
    TYPE:Humanoid.Base.PC
    SOURCEPAGE:p.21
    FACT:IsPC|True

This is mainly basic information like the creature’s size (M, medium), the race’s type (Humanoid) and subtype (Dwarf), and so on.

Before moving on, note two features here:

  1. The SORTKEY of a_base_pc is alphabetically first in the list of all race names, so it appears at the top of the list in the PCGen GUI. This means that you don’t have to wade through the hundreds of monster races (ape, bear, cheetah, deep one, earth elemental…) to get to the PC races (human, dwarf, elf, etc.)
  2. STARTFEATS:1 grants the race one bonus feat at first level. All PC races get a feat at 1st, but monster races don’t, so we have to be explicit about whether we want a given race to get a free feat at 1st, or not.

The real meat of the race definition is called out by the tag ABILITY:Internal|AUTOMATIC|Race Traits ~ Dwarf. This calls into the record called Race Traits ~ Dwarf in cr_abilities_race.lst.

Race ability definitions: cr_abilities_race.lst

This is an ability .lst file. Each record (line) in this file defines an ability.

If we search for Race Traits ~ Dwarf we find the following, apparently empty, ability record:

Race Traits ~ Dwarf
    CATEGORY:Internal

The record has a CATEGORY tag, but nothing else.

What’s going on here?

A bit more searching shows more lines matching Race Traits ~ Dwarf:

CATEGORY=Internal|Race Traits ~ Dwarf.MOD
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Defensive Training|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfDefensiveTraining
CATEGORY=Internal|Race Traits ~ Dwarf.MOD
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Greed|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfGreed
CATEGORY=Internal|Race Traits ~ Dwarf.MOD
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Hatred|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfHatred
CATEGORY=Internal|Race Traits ~ Dwarf.MOD
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Hardy|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfHardy
CATEGORY=Internal|Race Traits ~ Dwarf.MOD
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Stability|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfStability
CATEGORY=Internal|Race Traits ~ Dwarf.MOD
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Stonecunning|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfStonecunning
CATEGORY=Internal|Race Traits ~ Dwarf.MOD
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Steady|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfSteady
CATEGORY=Internal|Race Traits ~ Dwarf.MOD
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Weapon Familiarity|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfWeaponFamiliarity
CATEGORY=Internal|Race Traits ~ Dwarf.MOD
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Darkvision|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfRacialVision
CATEGORY=Internal|Race Traits ~ Dwarf.MOD
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Language|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfLanguage

These lines CATEGORY=Internal|Race Traits ~ Dwarf.MOD are modifying the “empty” record Race Traits ~ Dwarf. This is a trick for breaking up a long record into multiple lines, which is explained in my earlier post Exploring the PCGen file format – Part 1: Really long lines.

After the .MOD lines have modified the existing Race Traits ~ Dwarf record, the end result is:

Race Traits ~ Dwarf
    CATEGORY:Internal
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Defensive Training|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfDefensiveTraining
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Greed|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfGreed
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Hatred|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfHatred
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Hardy|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfHardy
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Stability|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfStability
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Stonecunning|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfStonecunning
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Steady|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfSteady
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Weapon Familiarity|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfWeaponFamiliarity
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Darkvision|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfRacialVision
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Language|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfLanguage

The Race Traits ~ Dwarf ability record now contains ten ABILITY tags within it. That is, the Race Traits ~ Dwarf ability is coded to grant ten more sub-abilities. These include Dwarf ~ Defensive Training, Dwarf ~ Greed, Dwarf ~ Hatred, and so on.

Actual ability definitions

We have seen that the Race Traits ~ Dwarf ability is just an in-direct handle to a number of sub-abilities, like:

ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Defensive Training|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfDefensiveTraining

The actual mechanical effects of the Dwarf ~ Defensive Training ability are defined in a separate record, further down in the file, as:

Defensive Training
    KEY:Dwarf ~ Defensive Training
    CATEGORY:Special Ability
    TYPE:RacialTraits.SpecialQuality.Extraordinary.DwarfRacialTrait.DwarfRacialDefault
    PREMULT:1,[PREABILITY:1,CATEGORY=Special Ability,Dwarf ~ Defensive Training],[!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfDefensiveTraining]
    DEFINE:RacialDefensiveTrainingBonus|0
    DESC:Dwarves get a +%1 dodge bonus to AC against humanoid creatures of the giant subtype.|RacialDefensiveTrainingBonus
    BONUS:VAR|RacialDefensiveTrainingBonus|4
    SOURCEPAGE:p.21
    ASPECT:CombatBonus|+%1 dodge bonus to AC against humanoid creatures of the giant subtype.|RacialDefensiveTrainingBonus

From this, we conclude that the cr_abilities_race.lst file works like this:

  1. In cr_abilities_race.lst, a blank definition is written for the Race Traits ~ Dwarf ability.

  2. The details of the Dwarf racial abilities are defined in individual records, one per sub-ability, like Dwarf ~ Defensive Training, Dwarf ~ Greed, Dwarf ~ Hatred, and so on.

  3. The CATEGORY=Internal|Race Traits ~ Dwarf.MOD... syntax is used to add pointers from the Race Traits ~ Dwarf ability to each of the sub-abilities, Dwarf ~ Defensive Training, Dwarf ~ Greed, and so on.

  4. The cr_races.lst defines the Dwarf race as having just the one ability, Race Traits ~ Dwarf. Since this points to all the sub-abilities, granting Race Traits ~ Dwarf automatically grants all the sub-abilities, without needing to refer to each one individually in races.lst.

!PREABILITY?

One thing remains un-explained.

If it was only required to use the Race Traits ~ Dwarf ability as a handle onto the ten sub-abilities, then this would suffice:

Race Traits ~ Dwarf
    CATEGORY:Internal
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Defensive Training
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Greed
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Hatred
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Hardy
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Stability
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Stonecunning
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Steady
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Weapon Familiarity
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Darkvision
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Language

However the actual code looks like this:

Race Traits ~ Dwarf
    CATEGORY:Internal
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Defensive Training|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfDefensiveTraining
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Greed|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfGreed
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Hatred|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfHatred
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Hardy|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfHardy
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Stability|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfStability
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Stonecunning|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfStonecunning
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Steady|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfSteady
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Weapon Familiarity|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfWeaponFamiliarity
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Darkvision|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfRacialVision
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Language|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfLanguage

Recall that PREABILITY is used to implement conditional logic. If the PREABILITY condition evaluates ‘True’, the tag is applied. If the PREABILITY condition evaluates ‘False’, the tag is ignored.

Adding ! to the front inverts the logic. !PREABILITY will cause the tag to be applied if the condition evaluates ‘False’ and vice-versa.

Having recalled how PREABILITY and !PREABILITY operate, we ask: Why would we ever want to remove any of the Dwarf racial abilities? Surely all dwarves have Dwarf ~ Defensive Training? (+4 Dodge AC vs. Giant).

The answer is: No, only the default kind of Dwarf (the ‘Hill Dwarf’) has +4 Dodge AC vs. Giants. There are multiple subtypes of dwarf, and these subtypes have different bonuses.

Searching through the entire Pathfinder data file directory for DwarfDefensiveTraining reveals:

  • The Ouat class (a type of Monk from the Inner Sea Combat book) is made up of dwarves who have rejected the traditional Dwarf training vs. Giants, so they don’t have +4 AC vs anything. (isc_abilities_class.lst.)
  • The Saltbeard from the Advanced Races Guide is a sub-race of dwarves who live on the high seas. There are no giants in the ocean, so a Saltbeard loses the +4 AC vs. giants, but gains +2 AC vs. any creature of the Water or Aquatic subtypes. (arg_abilities_race.lst.)

This indicates that all the !PREABILITY logic is there to support racial sub-types.

 

The logic looks like:

!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfDefensiveTraining

which is read as NOT having an ability of TYPE = DwarfDefensiveTraining.

If the default Dwarf is used, then there will be no pre-existing abilities of the type DwarfDefensiveTraining, so the logic will evaluate to ‘True’. The ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Defensive Training tag will be applied, granting the Dwarf ~ Defensive Training ability.

If a racial sub-type defines an ability of the type DwarfDefensiveTraining (in the ability category Special Ability), then the !PREABILITY logic will evaluate to ‘False’. The ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Defensive Training will be ignored.

Conclusion

Racial abilities are implemented by adding references from the race definition in _races.lst to one (or more) abilities in _abilities_race.lst.

A smart way of doing this is to make one reference from _races.lst to a ‘master’ ability in _abilities_race.lst, like Race Traits ~ Dwarf, and then add further references from the ‘master’ ability to multiple sub-abilities.

Advanced !PREABILITY logic for sub-races

The Pathfinder and DnD 3.5 data files contain some advanced logic in the racial ability definitions:

Race Traits ~ Dwarf
    CATEGORY:Internal
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Defensive Training|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfDefensiveTraining
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Greed|!PREABILITY:1,CATEGORY=Special Ability,TYPE.DwarfGreed
    ...

The logic is in there to support sub-races, i.e. Dwarf → Saltbeard, which requires the ‘default’ racial abilities to be disabled in favour of the sub-race’s abilities.

The happy news is that most home-brewers won’t be interested in sub-races, and so won’t need to implement anything with this level of complexity.

The simpler code:

Race Traits ~ Dwarf
    CATEGORY:Internal
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Defensive Training
    ABILITY:Dwarf Racial Trait|AUTOMATIC|Dwarf ~ Greed
    ...

will be quite sufficient for most home-brew.


Previously: Exploring the PCGen file format – Part 1: Really long lines


From Andrew Maitland (2015-07-06) we have the below comment, and another more detailed forum post.

Only two “sub races”? I wouldn’t have coded up that crazy advanced logic if the issue was simply two subraces (which tend to be ignored in pathfinder).

That code is invoked quite a bit, thanks to the Advanced Player’s Guide giving new rules to swap Racial Traits – that is when I coded up those up (took three distinctive iterations before I finally got the correct method – first pass involved kits, it wasn’t pretty… numerous bugs for having elf traits as a halfling). Each race trait beyond the originals has a COST:0 plus an exclusion “PC cannot possess TYPE=x, or in your example “DwarfDefensiveTraining” – which the original trait does not include to avoid circular logic, the exclusion is to avoid taking two traits that affect the same racial trait. For 3.5e the Unearthed Arcana book introduced OGL subrace options, which is why the logic exists in the RSRD. Otherwise the RSRD code support would have only been to support homebrew options beyond our official releases.

You are correct about empty records. Much easier to debug issues over multiple lines. You should have seen the race files before I made that change… complicated doesn’t begin to describe it. If you want a trip down memory lane, I can get you a copy of them.

For the SORTKEY, that is overkill to be honest. TYPE:PC places it at the top of the race list already. But most people wouldn’t know that TYPE:PC actually does that, so shrug I included it. Class sorting has the same logic – “Base” = top, “Prestige” next and then everything else in alpha order after.

One correction I see – STARTFEATS is absolutely required IF the race/class ever acquires feats. Non-Intelligent beings, such as Oozes, mindless undead and mindless constructs, never gain skills or feats, hence they will lack that entry.

As to ye olde documentation. Yes, those are over ten years old easily. The problem with keeping those updated is lack of volunteers. Do you want new books, or updated documentation? Most people ask for books.

I would say you’d make a great documentation monkey. We have openings

Cheers, Andrew Maitland