Adding items to the PC

From PCGen Wiki
Revision as of 04:36, 27 February 2018 by Tom Parker (talk | contribs) (Created page with "{| align="right" | __TOC__ |} In the process of adding items to a Player Character, an Object (like a Language) can be in a number of possible states. First, it can be...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

In the process of adding items to a Player Character, an Object (like a Language) can be in a number of possible states.

First, it can be "Available". This is possible with things like the list of Bonus Languages, Class Skill Lists, and Class Spell Lists. Things can also be prohibited with items like Prerequisites and Requirements.

Granting

When an object is attached enough to the Player Character in order to change that PC, we refer to that as "granting" the object to the PC. Items like stats (PCStat objects) are universally granted; Skills are granted if they have at least one rank; other objects like Race are granted directly.

Spell Life Cycle

Spells are not granted, they have a different life cycle, which involves known and memorized.

Ability Life Cycle

We start with an "Ability". An "ability" is a named entry in an Ability file.

If the Ability has a chooser within it, the product of executing the chooser on the Ability is an "AbilitySelection", which contains the result of the choice. If it doesn't, there is still one default "AbilitySelection", with a null choice. There are both UI and LST data file methods of getting to an AbilitySelection - these are described below.

Once a Category and Nature are added, we successfully have a CategorizedNaturedAbilitySelection or CNAS. That can be applied to a PC.

An Example on Terminology

Let's assume there are 2 TYPE=DragonForm abilities:

BasicDragonForm <> TYPE:DragonForm <> MULT:NO
AdvancedDragonForm <> TYPE:DragonForm <> MULT:YES <> CHOOSE:PCSTAT|ALL

There are 2 abilities here:

BasicDragonForm
AdvancedDragonForm

There are 7 AbilitySelections here:

BasicDragonForm()
AdvancedDragonForm(STR)
AdvancedDragonForm(INT)
AdvancedDragonForm(WIS)
AdvancedDragonForm(CON)
AdvancedDragonForm(DEX)
AdvancedDragonForm(CHA)

Note I'm writing even MULT:NO stuff with a trailing set of () to try to distinguish the AbilitySelection from the Ability. The user never sees that thought process (it's internal to the code)

There are many combinations of CNAS that are possible; they are not all articulated here. (There are, at a minimum, 21 possible combinations. 3 natures for each of the 7 AbilitySelection items listed above assuming there are no child Category objects for the Ability... 7 more for each child category where these items could be selected [child categories only use NORMAL nature].)

Granting an Ability to a PC

Abilities are not directly granted due to their structure. In order for an Ability (e.g. Feat) to be applied to a PC, it requires:

  1. A Category (either the parent Category or a child Category - in this case to indicate which "pool" is charged for taking the Ability)
  2. A Nature (Virtual, Automatic, Normal)
  3. The Ability itself
  4. Any Selection on the Ability. The Selection is the result of a CHOOSE token being present on the Ability. If no CHOOSE is present, the the selection is null, but still required to be present in order for an Ability to be applied to the PC.

As much as this might make the data team's head hurt, at the end of the day a character can never have an Ability applied to the character. It's always a CNAS. It's just a matter of how you get the full CNAS.

Usually it starts with an AbilitySelection. When combined with a specific Category and Nature, the AbilitySelection can be granted to a PC. (a CNAS can be granted)

From the UI

Even when you select BasicDragonForm from an Ability Pool or the Feat screen or whatever, the system internally evaluates it's MULT:NO and expands it to BasicDragonForm() to get the AbilitySelection. When you select AdvancedDragonForm from a pool, the system evaluates that it's MULT:YES, determines there are multiple possible answers, and makes you pick before it has the AdvancedDragonForm(target stat).

Either way, you have generated an AbilitySelection. The screen/pool you are in defines the Category, and the nature is inherently NORMAL for items selected in the UI - so the system can generate the full CNAS to be applied to the PC. It's just that most of the work is transparent to the data team.

In LST Data

CHOOSE:ABILITY returns an Ability:

BasicDragonForm
AdvancedDragonForm

So at any point, I can select an Ability with that... but the only thing I can GRANT to a character (what ABILITY: does) is an AbilitySelection.

Form of the Dragon II

CHOOSE:ABILITYSELECTION|Special Ability|TYPE=DragonForm
ABILITY:Special Ability|AUTOMATIC|%CHOICE

This needs to be CHOOSE:ABILITYSELECTION and not CHOOSE:ABILITY because CHOOSE:ABILITY selects an ABILITY and ABILITY: applies a CNAS (so the %LIST must provide an ABILITYSELECTION).

An Example showing the subtlety

To understand the distinction, consider a Feat like Martial Weapon Proficiency.

The Ability is Martial Weapon Proficiency. This appeared in the LST file as an Ability. Applying the first to a character is not meaningful (and reintroduces bugs we had with Martial Weapon Proficiency in the past that we fixed by introducing CHOOSE:FEATSELECTION).

An AbilitySelection is Martial Weapon Proficiency (Longsword). This was the result of the CHOOSE:ABILITYSELECTION and is the piece of information transferred from the CHOOSE to the ABILITY token Applying the second might be possible, but would not know what pool to deduct the application from or where it should be displayed in the UI (or in what color)

A CNAS is FEAT::NORMAL::Martial Weapon Proficiency (Longsword). This was the result of combining the AbilitySelection with the other information provided in the ABILITY: token. The last one (which is effectively generated from all the information provided to the ABILITY token) is grantable (because that's what the ABILITY token does).

A Countercase to using CHOOSE:ABILITY

From an older conversation on pcgen_experimental:

The concern: If none of the abilities-of-interest have choosers within them (so they are like your BasicDragonForm example - in fact, they are BlackDragonForm, BlueDragonForm etc.), it's counterintuitive that I can't grant the ability from the CHOOSE:ABILITY result, as it seems a small leap to the non-coder from the ability BlackDragonForm to its abilityselection BlackDragonForm()!

Let's walk through the scenarios:

CHOOSE:ABILITY|Special Ability|TYPE=DragonForm

Let's assume you start with all of the TYPE=DragonForm items being MULT:NO, so you can infer the AbilitySelection from the Ability. One could assert:

ABILITY|Special Ability|AUTOMATIC|%LIST should work.

Now someone comes along and adds a MULT:YES item. What do we do?

  1. Error on data load
  2. Don't let the CHOOSE:ABILITY present it
  3. Let the user select it, but it never gets applied to the PC.

In case #1, the code needs to recognize that a catcher (%LIST) is asking for an AbilitySelection, and a thrower (CHOOSE) is producing an Ability. Then it needs to check for auto conversion. At each load, all items of that TYPE (or listed individually) need to be checked to ensure they are MULT:NO. So we've spent a bunch of time iterating across the data.

Also, we can produce some nasty cases here. Imagine that a data monkey puts in "hollow" Abilities with only a DESC (which will happily take either Ability or AbilitySelection) to produce output in order to test their data. It has MULT:YES. The system will work with CHOOSE:ABILITY. They then go back and put in an ABILITY token to "do it for real" and the data breaks. Well, "obviously" that's a code problem (when in fact, they have changed what they are targeting).

So this scenario is (a) costly to enforce (b) fragile [meaning it produces non-intuitive errors and small extensions (creating a new Ability) will cause errors to be reported on unrelated objects]

Case #2 suffers from similar issues except that the checks are performed at runtime. If I ask for TYPE=DragonForm in CHOOSE:ABILITY, I expect it to be TYPE=DragonForm not "TYPE=DragonForm if and only if it's MULT:NO". The latter to me is what I would refer to as "magic" and something I generally try to avoid.

Case #3 also seems to suffer some nasty side effects. "I selected BasicDragonForm and it applies, but AdvancedDragonForm won't apply (and by the way, as part of your "code bug" it didn't provide me the ability to select my stat). I really don't want these "support calls" to the code team to fix things that aren't broken.

So we are strict. When a CHOOSE produces an Ability, you can do something with an Ability, when a CHOOSE produces an AbilitySelection, you can do something with the AbilitySelection. We are getting to the point where we actually produce errors about that today if you try to target the wrong type. (Go try CHOOSE:STAT|ALL and FAVOREDCLASS:%LIST in a Template and see what happens... 6.0.0 will silently consume that data. 6.1.x-dev, not so much :) )

Another example using CHOOSE

Reviewing:

ABILITY:FEAT|AUTOMATIC|%LIST wants to "catch" an AbilitySelection.

Using the model where parens indiate an AbilitySelection, this is attempting to catch something of the form:

???(?!?)

Note the ?!? may be null in the case of MULT:NO, but there may be 2 unknowns.

This case:

ABILITY:FEAT|AUTOMATIC|Weapon Proficiency (%LIST)

This wants to "catch"... well, we don't necessarily know without looking. What we do know know is the Ability, it's called Weapon Proficiency. So our AbilitySelection is effectively "Weapon Proficiency (?!?)"

What we need to fill in is a single unknown, the Selection.

CHOOSE:WEAPONPROFICIENCY chooses a Weapon Proficiency. So we are "throwing" a Weapon Proficiency... but are we catching the right thing?

What the code has to do is go peel back at the Feat Weapon Proficiency.

Weapon Proficiency <> MULT:YES <> CHOOSE:???|...

??? had *better* be WEAPONPROFICIENCY, so that the "catcher" is catching the appropriate type. If not, an error should be thrown at data load.

If the CHOOSE *does* match up, then effectively the ABILITY token in this case catches the Selection, plugs that into the Weapon Proficiency to produce Weapon Proficiency(known selection) [aka an AbilitySelection] and can grant that to the PC.

So another way of looking at CHOOSE:ABILITY is that is answers one unknown: The Ability. The CHOOSE:ABILITYSELECTION is actually a hybrid CHOOSE that (somewhere under the covers) answers two questions at once, effectively giving you the Ability and the Selection in one choice.