Difference between revisions of "FACT Token"
Tom Parker (talk | contribs) m |
m (Files are actually called *_datacontrols.lst) |
||
(7 intermediate revisions by one other user not shown) | |||
Line 59: | Line 59: | ||
y is the factual value | y is the factual value | ||
− | An identifier (x above) must have been defined in a FACTDEF line in a DATACONTROL LST file. | + | An identifier (x above) must have been defined in a FACTDEF line in a DATACONTROL LST file (*_datacontrols.lst). |
FACT by default overwrites. | FACT by default overwrites. | ||
Line 72: | Line 72: | ||
.CLEAR will be supported as a "y" value, and it will clear just the fact for the single identifier. If .CLEAR appears as a y value, it MUST be alone, it cannot be chained with a valid FACT. | .CLEAR will be supported as a "y" value, and it will clear just the fact for the single identifier. If .CLEAR appears as a y value, it MUST be alone, it cannot be chained with a valid FACT. | ||
+ | |||
+ | It is important to note that FACT cannot be used in a Campaign (PCC) file. This is because the Campaign files must be loaded to know what the data control files are, and then the data control files define what FACTs are legal. This is a race condition we do not intend to work around. FACT is simply not legal in Campaign files. | ||
==PCC (Campaign) LST token (DATACONTROL)== | ==PCC (Campaign) LST token (DATACONTROL)== | ||
Line 93: | Line 95: | ||
This token MUST appear as the first token on the line in the file. | This token MUST appear as the first token on the line in the file. | ||
− | A Second FACTDEF with matching values here but different | + | A Second FACTDEF with matching values here but different DATAFORMAT: will produce an error, since you have two conflicting items with the same identifier. |
− | Duplicate FACTDEF with matching x, y and | + | Duplicate FACTDEF with matching x, y and DATAFORMAT: are legal (thus allowing it to be present in multiple sources), but other tokens on the line are CUMULATIVE (dupe entries even in different files ACT LIKE A MOD, see DATAFORMAT for the exception) |
− | + | GLOBAL is a legal x value. Other legal values for x (those followed by * are currently enabled in the demo) | |
ABILITY*+ | ABILITY*+ | ||
ALIGNMENT* | ALIGNMENT* | ||
Line 118: | Line 120: | ||
+: If you are going to use Abilities, you need to understand how they are "wrapped" - they are NOT like other objects and cannot be accessed directly, see [[FreeMarker Facet Output]] | +: If you are going to use Abilities, you need to understand how they are "wrapped" - they are NOT like other objects and cannot be accessed directly, see [[FreeMarker Facet Output]] | ||
− | WARNING: Any time a "y" value is used with a specific | + | WARNING: Any time a "y" value is used with a specific DATAFORMAT, it is required that ALL FACTDEF entries with that "y" value (regardless of the "x" value) have the same DATAFORMAT. This is due to some internal mechanics of PCGen which shares infrastructure. We will evaluate loosening up this limitation, but since it may add complexity to PREFACT, it may not be worth it... |
− | === | + | ===DATAFORMAT=== |
− | + | DATAFORMAT:x | |
− | This token is REQUIRED. A FACTDEF with no | + | This token is REQUIRED. A FACTDEF with no DATAFORMAT will produce an LST load error. |
Note: The "x" Items will be hardcoded. In addition to "STRING", it is expected that the same items that are valid as the [http://pcgen.org/autobuilds/pcgen-docs/listfilepages/globalfilestagpages/globalfilesother.html#QUALIFY "x" value of QUALIFY] (the existing Global token) would also be legal here. | Note: The "x" Items will be hardcoded. In addition to "STRING", it is expected that the same items that are valid as the [http://pcgen.org/autobuilds/pcgen-docs/listfilepages/globalfilestagpages/globalfilesother.html#QUALIFY "x" value of QUALIFY] (the existing Global token) would also be legal here. | ||
− | Note: This is an exception to a duplicate FACTDEF: acting as a MOD. Specifically, A | + | Note: This is an exception to a duplicate FACTDEF: acting as a MOD. Specifically, A DATAFORMAT: with a type that does not match a previous value will produce an error, since you have two conflicting FACTs with the same identifier. |
Legal Values for x: | Legal Values for x: | ||
Line 154: | Line 156: | ||
For example: | For example: | ||
− | FACTDEF:DEITY|Symbol <> | + | FACTDEF:DEITY|Symbol <> DATAFORMAT:STRING <> VISIBLE:EXPORT |
EXPORT or YES would enable the output in Freemarker: | EXPORT or YES would enable the output in Freemarker: | ||
Line 177: | Line 179: | ||
For example: | For example: | ||
− | FACTDEF:DEITY|Align <> | + | FACTDEF:DEITY|Align <> DATAFORMAT:ALIGNMENT <> VISIBLE:YES <> SELECTABLE:YES |
SELECTABLE:YES enables use in a CHOOSE: | SELECTABLE:YES enables use in a CHOOSE: | ||
Line 200: | Line 202: | ||
For example: | For example: | ||
− | FACTDEF:DEITY|Align <> | + | FACTDEF:DEITY|Align <> DATAFORMAT:ALIGNMENT <> VISIBLE:YES <> REQUIRED:YES |
This would then trigger an LST load error if a Deity did not have the "ALIGN" Fact assigned. | This would then trigger an LST load error if a Deity did not have the "ALIGN" Fact assigned. | ||
Line 207: | Line 209: | ||
As caution to the data team: Use of this forces it across ALL loaded data, NOT JUST DATA FROM THE LOCAL PCC FILE. That likely means this is used sparingly, and only in base sets, otherwise the additional sets would have to .MOD a lot of stuff... | As caution to the data team: Use of this forces it across ALL loaded data, NOT JUST DATA FROM THE LOCAL PCC FILE. That likely means this is used sparingly, and only in base sets, otherwise the additional sets would have to .MOD a lot of stuff... | ||
+ | |||
+ | WARNING: A FACTDEF where the "x" value of the FACTDEF token is "GLOBAL" MUST NOT be Required. Using REQUIRED:YES on a global FACTDEF will cause a load error. | ||
===DISPLAYNAME=== | ===DISPLAYNAME=== | ||
Line 247: | Line 251: | ||
"x" matches the same strings used in the output file, which are shown on the [[FreeMarker Facet Output]] page. In some cases, an additional piece of information is required, for example: | "x" matches the same strings used in the output file, which are shown on the [[FreeMarker Facet Output]] page. In some cases, an additional piece of information is required, for example: | ||
PREFACT:1,EQUIPMENT.EQUIPPED,COLOR=Red | PREFACT:1,EQUIPMENT.EQUIPPED,COLOR=Red | ||
+ | |||
+ | =Understanding Processing Order= | ||
+ | |||
+ | It is possible, albeit a somewhat rare case, for an output name (interpolation in FreeMarker terms) to appear in multiple places in the hierarchy. Consider the following example: | ||
+ | * Create a GLOBAL FACT called "Hands" | ||
+ | * Support the output of "Hands" on Race (HANDS is currently an integer value) | ||
+ | |||
+ | In this case, the exact same priority rules that occur for loading occur for output. The "more local" output value will "win" (So Race [using ${race.hands}] would output an integer and every other object type [such as ${deity.hands}] would output the FACT. | ||
=Code Team Awareness= | =Code Team Awareness= |
Latest revision as of 08:17, 3 July 2023
Purpose
This proposal covers a means for the data to declare named constants for rules objects (e.g deity) that have values provided by the data and that are fixed at data load time. The proposal covers a number of tokens and is the first in a set of proposals to disconnect the code from the specific underlying game mode.
The core of the proposal is a "FACT" token that allows the data team to specify specific items (with normal LST file support like .CLEAR making it usable in .COPY and .MOD lines)
Status
This is a draft proposal for architecture/code review. Syntax is not final and is subject to revision and limitations due to existing syntax which may not be apparent in this proposal.
Scope
This proposal is intended to cover factual information about a single object applied to a PC.
"About the _object_" is key.
This means it is not intended to cover the number of LEGS a PC has (that is a fact ABOUT the _PC_ that the objects can alter, we are ONLY covering facts about the _object itself_). It is intended to cover things like the Symbol of a Deity (a "fact" about that Deity that doesn't alter the PC)
In order to maintain the semantic definition of "fact", items here will be unmodifiable at runtime. If the intent is to modify an object, that is semantically a variable, and is outside the scope of this proposal.
Note for clarity: This modification statement is "unmodifiable at runtime", which means the lock occurs when LST load completes. They CAN be overwritten in a .MOD.
The distinction between "fact" and "variable" is significant. This is *intended* to cover a *limited* set of information, and support features *based upon* those limitations. Data is exchanging limitations for power, as seen below.
There will be future capabilities that are more flexible (and can be changed at runtime) but which will support a different (more limited) subset of features. That is out of scope of this proposal, however.
Background
Currently when new items are added to objects, it ends up requiring code intervention.
This has a number of unfortunate side effects:
- This gets stuck in a priority queue along with a lot of other work for the code team
- It binds the code tightly to the game mode (who says a PC has legs?)
- It often creates yet another PRExxx token to handle that item (PREDEITYALIGN, etc)
- It sometimes comes back as a request for a Primitive for use in CHOOSE
In order to get away from this tight binding, we need a more generic way of dealing with information in the data.
In addition to the work already done in the token/loader system and in the core, the final puzzle piece that enables this capability was the insertion of Freemarker as the output/template system.
Specifically, as we get to 6.5, we will be enabling a different system for output based on Freemarker rather than on the existing output tokens. (see https://groups.yahoo.com/neo/groups/pcgen_developers/conversations/messages/4165 )
This will enable more data control over what is output without relying on core knowledge.
Proposal
Base Token (FACT)
FACT:x|y x is an identifier y is the factual value
An identifier (x above) must have been defined in a FACTDEF line in a DATACONTROL LST file (*_datacontrols.lst).
FACT by default overwrites.
It will be slowly, but consciously applied until it is eventually a global capability.
It is NOT like ASPECT, it does NOT support PRExxx (if the item is changing, then semantically it is NOT a fact - see "Background" above)
e.g. a Deity might have:
FACT:SYMBOL|Star
.CLEAR will be supported as a "y" value, and it will clear just the fact for the single identifier. If .CLEAR appears as a y value, it MUST be alone, it cannot be chained with a valid FACT.
It is important to note that FACT cannot be used in a Campaign (PCC) file. This is because the Campaign files must be loaded to know what the data control files are, and then the data control files define what FACTs are legal. This is a race condition we do not intend to work around. FACT is simply not legal in Campaign files.
PCC (Campaign) LST token (DATACONTROL)
In order to make useful work of a FACT, we require a few things. One of these is a set of controls for how the FACT is used. This requires a Campaign LST file token in order to store those controls:
DATACONTROL:x
x is a file (as per other files in the Campaign LST file)
Data Control LST Contents
First Token (FACTDEF)
The Data Control LST file controls how facts (and in the future other things) are handled by the code. Specifically, we can have a line define legal facts:
FACTDEF:x|y <> ...tokens... x is a file type (e.g. DEITY for Deity LST files) y is the identifier (then usable as x in the FACT LST token)
This token MUST appear as the first token on the line in the file.
A Second FACTDEF with matching values here but different DATAFORMAT: will produce an error, since you have two conflicting items with the same identifier.
Duplicate FACTDEF with matching x, y and DATAFORMAT: are legal (thus allowing it to be present in multiple sources), but other tokens on the line are CUMULATIVE (dupe entries even in different files ACT LIKE A MOD, see DATAFORMAT for the exception)
GLOBAL is a legal x value. Other legal values for x (those followed by * are currently enabled in the demo) ABILITY*+ ALIGNMENT* CHECK* CLASS* COMPANIONMOD* DEITY* DOMAIN* EQUIPMENT* KIT* LANGUAGE* RACE* SIZEADJUSTMENT* SKILL* SPELL STAT* TEMPLATE* WEAPONPROF*
+: If you are going to use Abilities, you need to understand how they are "wrapped" - they are NOT like other objects and cannot be accessed directly, see FreeMarker Facet Output
WARNING: Any time a "y" value is used with a specific DATAFORMAT, it is required that ALL FACTDEF entries with that "y" value (regardless of the "x" value) have the same DATAFORMAT. This is due to some internal mechanics of PCGen which shares infrastructure. We will evaluate loosening up this limitation, but since it may add complexity to PREFACT, it may not be worth it...
DATAFORMAT
DATAFORMAT:x
This token is REQUIRED. A FACTDEF with no DATAFORMAT will produce an LST load error.
Note: The "x" Items will be hardcoded. In addition to "STRING", it is expected that the same items that are valid as the "x" value of QUALIFY (the existing Global token) would also be legal here.
Note: This is an exception to a duplicate FACTDEF: acting as a MOD. Specifically, A DATAFORMAT: with a type that does not match a previous value will produce an error, since you have two conflicting FACTs with the same identifier.
Legal Values for x: ALIGNMENT BOOLEAN CLASS GRIDPOINT NUMBER STAT STRING
A reminder that certain items (Alignment, Stat) are referred to by their abbreviation not their name/key
VISIBLE
For any given FACTDEF, we can control how that FACT is used. This allows us to make it visible to the end user, or display in output.
VISIBLE:x
x is:
YES NO (default is NO) DISPLAY EXPORT
For example:
FACTDEF:DEITY|Symbol <> DATAFORMAT:STRING <> VISIBLE:EXPORT
EXPORT or YES would enable the output in Freemarker:
${deity.symbol}
(which would produce an error if VISIBLE:NO or VISIBLE:DISPLAY)
EXPORT or YES or will trigger display in the UI, specifically in the items about the object, in Gui2InfoDisplay (code will know the meaning of this)
In case of .MOD (see FACTDEF for multiple definitions acting as a .MOD) this overwrites
SELECTABLE
For any given FACTDEF, we can control how that FACT is used. This allows us to make it usable in CHOOSE or other locations where a Primitive is legal:
SELECTABLE:
x is:
YES NO (default is NO)
For example:
FACTDEF:DEITY|Align <> DATAFORMAT:ALIGNMENT <> VISIBLE:YES <> SELECTABLE:YES
SELECTABLE:YES enables use in a CHOOSE:
CHOOSE:DEITY|ALIGN=LG
Given the above FACTDEF, this would allow a choice of any Deity with the 'ALIGN' FACT set to 'LG' (this emulates the ALIGN= item we have in CHOOSE:DEITY today)
(It would produce an error if the FACTDEF for ALIGN was SELECTABLE:NO)
In case of .MOD (see FACTDEF for multiple definitions acting as a .MOD) this overwrites
REQUIRED
For any given FACTDEF, we can make that FACT required for a certain type of object.
REQUIRED:x
x is:
YES NO (default is NO)
For example:
FACTDEF:DEITY|Align <> DATAFORMAT:ALIGNMENT <> VISIBLE:YES <> REQUIRED:YES
This would then trigger an LST load error if a Deity did not have the "ALIGN" Fact assigned.
In case of .MOD (see FACTDEF for multiple definitions acting as a .MOD) this overwrites
As caution to the data team: Use of this forces it across ALL loaded data, NOT JUST DATA FROM THE LOCAL PCC FILE. That likely means this is used sparingly, and only in base sets, otherwise the additional sets would have to .MOD a lot of stuff...
WARNING: A FACTDEF where the "x" value of the FACTDEF token is "GLOBAL" MUST NOT be Required. Using REQUIRED:YES on a global FACTDEF will cause a load error.
DISPLAYNAME
For any given FACTDEF, we can control the name displayed for that type of item in the UI
DISPLAYNAME:x
x is a String (used to describe this fact in the UI)
Intent is that this is used to describe the object and is the string that will be displayed in the UI
In case of .MOD (see FACTDEF for multiple definitions acting as a .MOD) this overwrites
EXPLANATION
For any given FACTDEF, we can explain the purpose it serves in the data
EXPLANATION:x
x is a String
Intent is this is used to describe what the object is for purposes of the LST editor or readers of the data
In case of .MOD (see FACTDEF for multiple definitions acting as a .MOD) this overwrites
Global PREFACT
Of course, being able to define a FACT also demands something like:
PREFACT:w,x,y=z w is a number x is the type (e.g. DEITY) y is the identifier (e.g. SYMBOL) z is the required value
For example:
PREFACT:1,DEITY,SYMBOL=Star
"x" matches the same strings used in the output file, which are shown on the FreeMarker Facet Output page. In some cases, an additional piece of information is required, for example:
PREFACT:1,EQUIPMENT.EQUIPPED,COLOR=Red
Understanding Processing Order
It is possible, albeit a somewhat rare case, for an output name (interpolation in FreeMarker terms) to appear in multiple places in the hierarchy. Consider the following example:
- Create a GLOBAL FACT called "Hands"
- Support the output of "Hands" on Race (HANDS is currently an integer value)
In this case, the exact same priority rules that occur for loading occur for output. The "more local" output value will "win" (So Race [using ${race.hands}] would output an integer and every other object type [such as ${deity.hands}] would output the FACT.
Code Team Awareness
Generic behavior
This is designed to provide generic behavior to get us "out of the business" of dealing with minor token requests. There are actually dozens of "facts" about Equipment that have been requested for MSRD and this proposal would replace MANY of the existing NEWTAG proposals, and do it with less code (and without risk of copy/paste error)
Caching
One of the weaknesses we have in our existing Primitive system is multiple construction and caching. Let's take something like this in the data:
CHOOSE:DEITY|ALIGN=LG
If this appears twice, then the unfortunate part is that we create two Deity ALIGN Primitives. This is wasteful, albeit minor.
The challenge is that we also want to ensure that we don't have to redo work. So if we had that CHOOSE twice, we would want to cache that information. With multiple primitives that is not possible. With a centralized PrimitiveFactory (or whatever) it becomes practical to build the Primitive once, and then that Primitive can itself cache results.
However, a cache is only practical if the underlying information cannot change. (If it can change, it's both not a fact, and not cacheable)
This provides a generic method of dealing with facts that ensures those facts *do not change*, thus any infrastructure we build can be cacheable (and we can automatically build that infrastructure)
Challenges
Capitalization
Frankly this is a bear. Currently we import LST files in case-insensitive form (technically all caps), but Freemarker output is case sensitive. So the conversion of Symbol (or however it appears in the FACTDEF) to .symbol in the output is a convention we have to agree upon:
- Freemarker is always in lower case for a FACT:
- We enforce an identifier to appear in all caps (not strictly required, so perhaps this is a data standard)
Localization
Currently Gui2InfoFactory uses internationalized strings to identify each item (e.g. Symbol). In order to "unhardcode" Gui2InfoFactory, we need to put that into the FACTDEF line as well, but that raises the "localization in data" debate.