Difference between revisions of "SELECTION - A CHOOSE Replacement"

From PCGen Wiki
Jump to: navigation, search
(Created page with "{| align="right" | __TOC__ |} =Introduction= First draft proposal for SELECTION (replaces CHOOSE/supports multiple CHOOSE) Please note that the token names here are for...")
 
m
Line 5: Line 5:
 
=Introduction=
 
=Introduction=
  
First draft proposal for SELECTION (replaces CHOOSE/supports multiple CHOOSE)
+
First draft proposal for SELECTION (replaces CHOOSE/supports multiple CHOOSE).
  
 
Please note that the token names here are for example purposes.  Some discussion has taken place but they are not  
 
Please note that the token names here are for example purposes.  Some discussion has taken place but they are not  
 
 
finalized.  They are here to show the concepts and how the structures of this new system would work in LST files.
 
finalized.  They are here to show the concepts and how the structures of this new system would work in LST files.
  
Line 28: Line 27:
  
 
CHOOSE currently supports only one selection.  In many cases this produces a challenging format that is forced into the  
 
CHOOSE currently supports only one selection.  In many cases this produces a challenging format that is forced into the  
 
 
code.  If multiple selections were allowed, this would not be necessary, as one could be CLASS and the other LEVEL for  
 
code.  If multiple selections were allowed, this would not be necessary, as one could be CLASS and the other LEVEL for  
 
+
example.
example
 
  
 
===Automatic Expansion===
 
===Automatic Expansion===
  
 
CHOOSE currently automatically expands items.  If you CHOOSE:FEAT, you get things like Weapon Proficiency(x) and Weapon  
 
CHOOSE currently automatically expands items.  If you CHOOSE:FEAT, you get things like Weapon Proficiency(x) and Weapon  
 
 
Proficiency (y).  This also then creates an interesting visual challenge if there are multiple tiers.  If a Feat allows  
 
Proficiency (y).  This also then creates an interesting visual challenge if there are multiple tiers.  If a Feat allows  
 
 
a selection from another MULT:YES feat, then you get Something(a(b)) and Something(a(c)).  This can rapidly proliferate  
 
a selection from another MULT:YES feat, then you get Something(a(b)) and Something(a(c)).  This can rapidly proliferate  
 
 
into a huge list to scroll through.  It would be visually cleaner to select "a" and then select "b or c" within the  
 
into a huge list to scroll through.  It would be visually cleaner to select "a" and then select "b or c" within the  
 
 
context of "a" being selected under "something".  This requires a new UI to handle this hierarchical behavior.
 
context of "a" being selected under "something".  This requires a new UI to handle this hierarchical behavior.
  
 
(This item is not currently addressed further here, as I would like to talk to some of our UI folks before I propose  
 
(This item is not currently addressed further here, as I would like to talk to some of our UI folks before I propose  
 
 
too much here)
 
too much here)
  
Line 64: Line 56:
  
 
This has already been encountered in the past and discussed on the -dev list.  The BONUSes also suffer from the same  
 
This has already been encountered in the past and discussed on the -dev list.  The BONUSes also suffer from the same  
 
 
issue and there have been a few debates over proper behavior.
 
issue and there have been a few debates over proper behavior.
  
 
The solution to this problem is eliminate all ambiguity.  We should to distinguish between the ability itself (applied  
 
The solution to this problem is eliminate all ambiguity.  We should to distinguish between the ability itself (applied  
 
 
only once ever) and what I will call the "instances" of the ability (applied once per time taken by the user).  Note  
 
only once ever) and what I will call the "instances" of the ability (applied once per time taken by the user).  Note  
 
 
that I do NOT say "per selection" or "per choice" because if an item has 3 choices, we still have struggles.  There are  
 
that I do NOT say "per selection" or "per choice" because if an item has 3 choices, we still have struggles.  There are  
 
 
ways to apply once per choice as well, but we need ALL of the capabilities, so we have to have a method to do once per  
 
ways to apply once per choice as well, but we need ALL of the capabilities, so we have to have a method to do once per  
 
 
time taken by the user).
 
time taken by the user).
  
 
From this point onward, I will have an INSTANCE: token that then refers to instances rather than the parent object.   
 
From this point onward, I will have an INSTANCE: token that then refers to instances rather than the parent object.   
 
 
This is much like the current "PART:x|..." token that can appear on equipment to handle equipment heads.  Just like  
 
This is much like the current "PART:x|..." token that can appear on equipment to handle equipment heads.  Just like  
 
 
PART, it will have an argument (as to where it is applied) followed by another full token.
 
PART, it will have an argument (as to where it is applied) followed by another full token.
  
Line 88: Line 73:
  
 
Currently the system defaults to STACK:NO.  Should this be the default behavior when a lot more control exists for the  
 
Currently the system defaults to STACK:NO.  Should this be the default behavior when a lot more control exists for the  
 
 
data to define what is selectable?
 
data to define what is selectable?
  
Line 94: Line 78:
  
 
Part of the reason we have multiple different selection methods is that the code has certain race conditions (two  
 
Part of the reason we have multiple different selection methods is that the code has certain race conditions (two  
 
 
actions that need to happen in a certain order).   
 
actions that need to happen in a certain order).   
  
 
For Race and Template, they get added to the PC first, THEN the selection is made.  This means they have to be  
 
For Race and Template, they get added to the PC first, THEN the selection is made.  This means they have to be  
 
 
defending against undefined choices.  
 
defending against undefined choices.  
  
 
For Abilities, they only get added to the PC when an AbilitySelection object (an ability with its selection) is added  
 
For Abilities, they only get added to the PC when an AbilitySelection object (an ability with its selection) is added  
 
 
to the PlayerCharacter.  This means the selection occurs BEFORE the object is added.
 
to the PlayerCharacter.  This means the selection occurs BEFORE the object is added.
  
Line 113: Line 94:
  
 
Also, since backwards compatibility will not be supported (CHOOSE will not be available in the new formula system at  
 
Also, since backwards compatibility will not be supported (CHOOSE will not be available in the new formula system at  
 
 
least under the current plan), we will need a new token name.  From this point onward, it will be called SELECTION.
 
least under the current plan), we will need a new token name.  From this point onward, it will be called SELECTION.
  
Line 122: Line 102:
  
 
As mentioned above, the Selections will be placed into instances.  These instances are separate objects with a separate  
 
As mentioned above, the Selections will be placed into instances.  These instances are separate objects with a separate  
 
 
SCOPE.  For Skills (For example), the scope will be SKILL.INSTANCE
 
SCOPE.  For Skills (For example), the scope will be SKILL.INSTANCE
  

Revision as of 00:24, 4 February 2018

Introduction

First draft proposal for SELECTION (replaces CHOOSE/supports multiple CHOOSE).

Please note that the token names here are for example purposes. Some discussion has taken place but they are not finalized. They are here to show the concepts and how the structures of this new system would work in LST files.

Code Concepts

Current Issues

Multiple Methods

There are currently 4 different methods for how CHOOSE operates in the code

  • Abilities
  • Race and Template
  • Language
  • Temporary Bonuses

The desire is to reduce this to one methodology (or two at worst if temporary bonuses need to be separate)

Single Selection

CHOOSE currently supports only one selection. In many cases this produces a challenging format that is forced into the code. If multiple selections were allowed, this would not be necessary, as one could be CLASS and the other LEVEL for example.

Automatic Expansion

CHOOSE currently automatically expands items. If you CHOOSE:FEAT, you get things like Weapon Proficiency(x) and Weapon Proficiency (y). This also then creates an interesting visual challenge if there are multiple tiers. If a Feat allows a selection from another MULT:YES feat, then you get Something(a(b)) and Something(a(c)). This can rapidly proliferate into a huge list to scroll through. It would be visually cleaner to select "a" and then select "b or c" within the context of "a" being selected under "something". This requires a new UI to handle this hierarchical behavior.

(This item is not currently addressed further here, as I would like to talk to some of our UI folks before I propose too much here)

CHOOSE:NOCHOICE

Simply shouldn't have to exist.

Stacking Ambiguities

This is true more for MULT:YES than CHOOSE in particular, but imagine something like this:

ABILITY:FEAT|VIRTUAL|Dodge

Now put that on a MULT:YES Feat and select it twice.

Do you get 2 copies of Dodge or 1 copy?

This has already been encountered in the past and discussed on the -dev list. The BONUSes also suffer from the same issue and there have been a few debates over proper behavior.

The solution to this problem is eliminate all ambiguity. We should to distinguish between the ability itself (applied only once ever) and what I will call the "instances" of the ability (applied once per time taken by the user). Note that I do NOT say "per selection" or "per choice" because if an item has 3 choices, we still have struggles. There are ways to apply once per choice as well, but we need ALL of the capabilities, so we have to have a method to do once per time taken by the user).

From this point onward, I will have an INSTANCE: token that then refers to instances rather than the parent object. This is much like the current "PART:x|..." token that can appear on equipment to handle equipment heads. Just like PART, it will have an argument (as to where it is applied) followed by another full token.

Current Quirks

Stacking

Currently the system defaults to STACK:NO. Should this be the default behavior when a lot more control exists for the data to define what is selectable?

Race Conditions

Part of the reason we have multiple different selection methods is that the code has certain race conditions (two actions that need to happen in a certain order).

For Race and Template, they get added to the PC first, THEN the selection is made. This means they have to be defending against undefined choices.

For Abilities, they only get added to the PC when an AbilitySelection object (an ability with its selection) is added to the PlayerCharacter. This means the selection occurs BEFORE the object is added.


Other Considerations

Conversion

No conversion will be automated, as the semantics here are completely different than the old system.

Also, since backwards compatibility will not be supported (CHOOSE will not be available in the new formula system at least under the current plan), we will need a new token name. From this point onward, it will be called SELECTION.


Design

Instances

As mentioned above, the Selections will be placed into instances. These instances are separate objects with a separate SCOPE. For Skills (For example), the scope will be SKILL.INSTANCE

Aligning Concepts to the new Formula System

In general, the new formula system uses the concept of a Scope to determine where a variable is processed.

A naive design would be to keep two lists on the main object. That way, the first set of choices are [0] in the lists,

the next set [1]. While I don't have a specific rule associated to this, I can quickly come up with some plausible use

cases where that would break. For example: Imagine a feat that allows you to select a class and make 2 skills from the

classskilllist of that class get +1 bonus rank.

Therefore, we delegate containing the result of the selection down into an instance object. We then make that a

subscope of the main scope for that object type. Consider the CHOOSE on skills for Speak Language. We then get

something like:

  INSTANCE:ALL|SELECTION:SpokenLanguage|LANGUAGE|getAll("LANGUAGE")

Now, every time a new instance of that Skill is selected (which happens to be from getting a new rank of that skill), a

new Instance will be added to the PC and a new selection will need to be made.

It could then be added to a list of languages via:

  INSTANCE:ALL|MODIFY:LanguageList|ADD|getSelection("SpokenLanguage")

(This assumes LanguageList is a global variable of Format "ARRAY[LANGUAGE]")

To be VERY CLEAR: getSelection("SpokenLanguage") MUST BE USED IN AN INSTANCE. If you attempt to use it up at the

parent ability/skill/whatever, it will either (a) fail or (b) be empty. (It should fail, and we will endeavor to do

that, but there will need to be enhancements to have functions available only in a limited scope... that's new effort)

Resolving the Race Condition

The race condition is particularly problematic for the new formula system. Consider a naive design like this:

  MODIFY:MyList|ADD|Foo
  SELECTION:MySelection|STUFF|myList

This immediately sets up a problem based on how the formula system works. Because it only processes items that are

actually "granted" to the PC, the MODIFY of MyList would not happen unless the object itself was already granted. This

traps us (a bit) in making sure the object is added to the PC BEFORE a selection is made, so that if it chooses to do

any list modifications, they can be done BEFORE the selection.

To resolve this race condition, we put the MODIFY on the parent object, which is granted BEFORE any selection. We then

put the SELECTION in the INSTANCE, which is granted AFTER any selection. Note this also means that if (for some

reason) the data team to have the MODIFY occur AFTER the selection, they have that power. This gives the data team

full feature, supporting add both BEFORE and AFTER selection:

  MODIFY:MyList|ADD|AddBeforeSelection
  INSTANCE:1|MODIFY:MyList|Add|AddAfterSelection

As a nice side effect, this actually gives us full power to avoid ANY popup windows in a new UI should that be a

feature we want to implement. Because all instance applications can be deferred, they could be "TODOs" on the PC

rather than stopping for user input.

Replicating NUMCHOICES

To replicate the behavior of NUMCHOICES, we then need to control the number of instances that we are allowed to apply.

This is a new token, MAXINSTANCES.

Replicating SELECT

To replace the behavior of SELECT, we need to control the number of actual selections that exist in an instance for

that SELECTION. Since this is associated to the SELECTION, we need to keep it local to that token, so it is appended

as an optional control on the SELECTION:

  SELECTION:MySelection|LANGUAGE|getAll("LANGUAGE")|SELECT=2


Happy Side Effects of INSTANCE

Please note: ALL objects that are instance-able ALWAYS will produce an instance. This is true REGARDLESS of whether

there is a SELECTION on the object. Many of these Instances will be empty shell objects, but they can also have some

interesting uses.

"At Rank 5 this skill adds 2 legs to the PC"

  INSTANCE:5|MODIFY:Legs|ADD|2

Note that this means there is no equivalent PRERANK: needed on a MODIFY. It is directly applied to the "5" instance as

a MODIFY, and when the Rank hits 5, that INSTANCE will be granted and the MODIFY will be applied. (If you are thinking

this could be useful for how pathfinder handles Skill Focus... yes, possibly... stay tuned :) )

Using a Selection as a Target

In some cases, we end up in situations like "This will increase the range of one type of weapon by 5'". Today there is

BONUS:WEAPONPROF=%LIST|RANGE|5

The %LIST here is actually in the target, which is a bit of a problem! The second argument to MODIFYOTHER is

definitely NOT a formula, so we need to address that somehow. In the future, there may be elegant ways of doing this. For right now, we end up with a not-so-great method, which looks like:

MODIFYOTHER:EQUIPMENT.PART|ALL|Range|ADD|if(getFact(parent(),"WEAPONPROF")==getSelection("MySelection"),5,0)

Data Information

New Tokens

INSTANCE

INSTANCE is only supported on object types that are INSTANCE-able. This will require code support for all existing

Formats.

Note: It is the intent to allow DYNAMIC to be instance-able if the data team so specifies. More on this later.

Format:

  INSTANCE:x|y
  • x is the instances to which the token given in y should be applied. Currently this supports either an integer value

or "ALL"

  • y is a full token to be applied to that instance.

Please note INSTANCE objects are NOT full CDOMObjects. They cannot support arbitrary tokens. Only a limited set

(defined here) will be supported.

SELECTION

SELECTION will ONLY be available on INSTANCE objects. This means it needs to appear as a subtoken to INSTANCE (which

itself must only appear on instance-able objects)

Format:

  SELECTION:w|x|y|z|z
  • w is the name of the selection. This is relevant when the data is using the value of the selection in the data
  • x is the FORMAT of the selection. See the Setting up the new formula system for more information on formats.
  • y is the array of available objects for the selection.
  • z is an optional set of controls for the selection.

Selection Control: SELECT

This replaces the SELECT: token in the current CHOOSE System. It is a z value for SELECTION:

  SELECT=x
  • x is a formula for the number of selections to be made.

Selection Control: ORDER

In the scenario where two SELECTION items would interact (such as choosing a level of spell for a given class, where

the class would need to be selected first), there is an ORDER control:

  ORDER=x
  • x is an integer constant. The SELECTION with the lowest ORDER will be processed first (just like PRIORITY on

MODIFY). If two SELECTION items have the same ORDER, the system will not guarantee which is processed first. If it

matters, specify it. The default value is zero.

getAll(x) Function

getAll will be a function in the new formula system.

Format of this function:

  getAll("x")
  • x is the name of a FORMAT from the new formula system.
    • Note: This format MUST be an limited format, in that you can't do getAll("NUMBER") as that is an unbounded set.

Note also this function will take some cleanup - we eventually need to figure out how to do the things that the

existing items in the CHOOSE system do... this is a patch for now to get the discussion started

getSelection(x) Function

getSelection will be a function in the new formula system.

Format of this function:

  getSelection("x")
  • x is the name of the selection from the SELECTION token.

NOTE: The FORMAT that getSelection will return will depend on the SELECTION token. It also depends on whether there is

a SELECT=x control on the SELECTION

MAXINSTANCES LST Token

This token will be usable on any format which is instance-able.

Format:

  MAXINSTANCES:x
  • x is a formula in the new formula system to identify the maximum number of instances allowed for that item.