`Choice` structure reform
See original GitHub issueSummary
There are a number of issues with the Choice structure that keep cropping up: nested Choices, arrays of mixed types, no way to easily describe or deserialize the structure… This issue aggregates those problems and proposes a reformed Choice structure that aims to solve them.
The details of the existing problems and proposed solutions are explained below. I’ve also described the final proposed structure in TypeScript syntax for reference (this is not intended to be official documentation), and provided an example of the structure in the form of the Fighter’s starting_equipment_options
array. Both can be found in this gist: https://gist.github.com/fergcb/a02760c460def3904358cbdfea9312c6
With the community’s approval I’ll be more than happy to take up the task of implementing the proposal, as well as producing the user-facing documentation.
Details
Consistency
The biggest issue being addressed is consistency. A lot of data consumers don’t like arrays with items of mixed types (#274), and there are a variety of structures that can appear in different places without any straightforward way of identifying them.
The proposal uses an inheritance pattern to give all items in an array a common type, for easy deserialization. For example, in the from
array of a Choice, there could be nested choices, arrays of items that must each be resolved separately, or a “terminal”, single item. Currently these are given in their raw forms; the from
array could contain Choice objects, JSON arrays, or single objects.
To solve this, every item in the array will now be an object with an option_type
attribute of either choice
, multiple
or single
, which indicates the structure of the underlying data. The same pattern is used to identify whether the Choice is between an array, as described above, or a ResourceList. See the JSON snippet in the gist for examples.
Descriptions
Another issue with the current Choice structure is that it can be hard to explain to a human user what they’re actually choosing between (#355). While it is easy to list the options available, and the type
attribute of a choice provides some information, a consumer has no way of indicating where the choices are coming from, or what they are. For example type
may indicate that a "proficiency"
is being chosen, so an application could easily render something like “Choose one proficiency:”, but the underlying choice in the SRD is actually “one type of artisan’s tools or one musical instrument”
To solve this, the proposal adds an optional desc
string for Choices, so that consumers don’t have to write any funky algorithms to tell a human user what they’re choosing from. The string should include the text from the SRD that describes what needs to be chosen, e.g. “a wooden shield or any simple weapon”, “one type of artisan’s tools or one musical instrument”, “a martial weapon and a shield or two martial weapons”, etc.
Amendments
- The
type
attributes of Option and OptionSet objects have been renamed tooption_type
andoption_set_type
respectively, to more easily distinguish the difference between the structures from the programmer’s perspective. - The
array
value of theoption_type
attribute has been changed tomultiple
, to help indicate that all of the items in the JSON array come with that option. Theresource_list
value of theoption_set_type
has been changed toresource_list_url
to better indicate the structure of the data: it is a string holding a URL that resolves to a ResourceList, not an actual ResourceList in its own right.- The
resource_list
OptionSet now holds the ResourceList’s URL underresource_list_url
, rather than justurl
. - The EquipmentCategoryOptionSet type has been added to support equipment categories properly (which have a structure distinct from ResourceLists). EquipmentCategories are now linked with an APIReference.
- The
single
Option type has been split intoreference
andaction
. Thereference
type is identical to the previoussingle
type, corresponding to a counted APIReference. Theaction
type is intended for use within “Multiattack” actions, referring to actions, by name, within the same Monster document. An example based on the “Captain” has been added to the Gist. - The
array
option_set_type
has been renamed tooptions_array
, to make it clear what the array contains.
Final Spec
This section describes the state of the proposal after the discussed amendments, to aid with the implementation:
Choice
The Choice structure describes a decision that must be made by a player during character creation or gameplay. It has four attributes:
desc
(string, optional): A description of the choice to be made in human-friendly and (where possible) SRD text.choose
(number): The number of options that must be chosen. The same option can be chosen more than once.type
(string): A string indicating the type of object that will be chosen.from
(object): An object containing the options to choose from. This may take one of a number of forms as described below.
OptionSet
The OptionSet structure provides the options to be chosen from, or sufficient data to fetch and interpret the options. All OptionSets have an option_set_type
attribute that indicates the structure of the object that contains the options. The possible values are options_array
, equipment_category
and reference_list
. Other attributes on the OptionSet depend on the value of this attribute.
options_array
options
(array): An array of Option objects. Each item in the array represents an option that can be chosen.
equipment_category
equipment_category
(APIReference): A reference to an EquipmentCategory. Each item in the EquipmentCategory’sequipment
array represents one option that can be chosen.
resource_list
resource_list_url
(string): A reference (by URL) to a collection in the database. The URL may include query parameters. Each item in the resulting ResourceList’sresults
array represents one option that can be chosen.
Option
When the options are given in an options_array
, each item in the array inherits from the Option structure. All Options have an option_type
attribute that indicates the structure of the option. The possible values are reference
, action
, multiple
and choice
. The value of this attribute indicates how the option should be handled, and each type has different attributes.
reference
- A terminal option. Contains a reference to a Document that can be added to the list of options chosen.item
(APIReference): A reference to the chosen item.
action
- A terminal option. Contains information describing an action, for use within Multiattack actions.action_name
(string): The name of the action, according to itsname
attribute.count
(number): The number of times this action can be repeated if this option is chosen.type
(string ="melee" | "ranged"
, optional): For attack actions that can be either melee or ranged (e.g. for thrown weapons).
multiple
- When this option is chosen, all of its child options are chosen, and must be resolved the same way as a normal option.items
(array): An array of Option objects. All of them must be taken if the option is chosen.
choice
- A nested choice. If this option is chosen, the Choice structure contained within must be resolved like a normal Choice structure, and the results are the chosen options.choice
(Choice): The Choice to resolve.
Issue Analytics
- State:
- Created 2 years ago
- Reactions:2
- Comments:106 (55 by maintainers)
Top GitHub Comments
Very late note, but I’ve been asked to include this finding here.
This structure will have some issues handling the choices for Ideals, Flaws, Bonds and Personality Traits stored within Backgrounds. Two types of single option must be added in order for these to be used as Options. An “ideal” option will be needed in addition to the “reference” and “action” OptionTypes. Additionally a “string” OptionType will be needed to use Personality Traits, Bonds and Flaws since all of these are just strings.
Fairly self explanatory, just other option types. This would leave us with singular option types of
action
,reference
,ideal
, andstring
For any unaware, Ideals are not just strings. Here’s an example of an Ideal in the Acolyte background.
I’ve updated the Gist and added a summary of the final state of the proposal to the end of the original comment, as a sort of implementation guide for whoever picks this up.