`Choice` structure reformSee original GitHub issue
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.
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
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.
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.
typeattributes of Option and OptionSet objects have been renamed to
option_set_typerespectively, to more easily distinguish the difference between the structures from the programmer’s perspective.
arrayvalue of the
option_typeattribute has been changed to
multiple, to help indicate that all of the items in the JSON array come with that option.
resource_listvalue of the
option_set_typehas been changed to
resource_list_urlto 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.
resource_listOptionSet now holds the ResourceList’s URL under
resource_list_url, rather than just
- 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.
singleOption type has been split into
referencetype is identical to the previous
singletype, corresponding to a counted APIReference. The
actiontype 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.
option_set_typehas been renamed to
options_array, to make it clear what the array contains.
This section describes the state of the proposal after the discussed amendments, to aid with the implementation:
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.
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
reference_list. Other attributes on the OptionSet depend on the value of this attribute.
options(array): An array of Option objects. Each item in the array represents an option that can be chosen.
equipment_category(APIReference): A reference to an EquipmentCategory. Each item in the EquipmentCategory’s
equipmentarray represents one option that can be chosen.
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’s
resultsarray represents one option that can be chosen.
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
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 its
count(number): The number of times this action can be repeated if this option is chosen.
"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.
- Created 2 years ago
- 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
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.