dnd-item-generator/dnd_item/sources/README.md
2023-12-31 13:06:23 -08:00

7.5 KiB

Data Sources

This directory contains a series of data source files for item generators. The format is used by the random_sets package to create DataSource objects that make it easy to generate and combine randomized sets with tunable frequency distributions.

  • magic_damage_types.yaml: types of magical damage that can be applied to weapons
  • properties_base.yaml: The base properties of mundane items ("light", "thrown," etc)
  • rarity.yaml: The levels of rarity in D&D, with frequency distributions by challenge rating
  • weapons.yaml: The basic weapon types (maul, shortsword, halbred, etc)

Besides properties_base.yaml, there are several files named properties_[rarity].yaml; these sources define what properties can be applied to items of each rarity. For example, in properties_rare.yaml you will find a definition for +2 weapons, but +3 weapons are only in properties_very_rare.yaml.

Data Source File Schema

Let's look at a simple example, from rarity.yaml:

metadata:
    headers:
      - rarity
      - sort_order
    frequencies:
      default:
        common: 1.0
        uncommon: 1.0
        rare: 1.0
        very rare: 1.0
        legendary: 1.0
common:
  - 0
uncommon:
  - 1
rare:
  - 2
very rare:
  - 3
legendary:
  - 4

The first block of the DataSource, metadata, contains information about the data. It may have two members, headers and frequencies. Everything after the metadata block is the data set.

Headers

Headers are applied sequentially to the members of the data when the data set is interpreted as a table, a list of dictionaries, and so on. A DataSource instance populated with this example would yield the following data structure:

[
    {'rarity': 'common', 'sort_order': 0}},
    {'rarity': 'uncommon', 'sort_order': 1}},
    {'rarity': 'rare', 'sort_order': 2}},
    {'rarity': 'very rare', 'sort_order': 3}},
    {'rarity': 'legendary', 'sort_order': 4}},
]

Note that each member of the data set creates its own entry in the list.

This mapping is recursive, such that a member in the data set may contain an arbitrarily-deep structure of arrays and collections; they will be flattened into a serial list and headers applied to each, although this level of complexity is not generally needed for Item properties in this package.

Frequencies

The frequencies collection defines one or more mappings of frequency distrubitions, from 0.0 to 1.0, for each member in the data set. The default data set in our example applies an equal weighting to all members in the set, meaning that by default all rarities defined here are equally likely to be chosen when a random item is generated.

The full rarity.yaml defines several distrubtions organized by challenge rating, making it impossible to generate legendary items for low-level parties; at higher levels, common items become paradoxically rare.

Data Set

Everything after the metadata block is the data set; each collection represents a single entry in the set of possible selections. When DataSource.random() is called, the return value will be one of these collections, mediated by the specified frequencey distribution.

Template Strings

Members of the data set can include python-style template strings. These strings are processed when an item is generated, and can refer to other properties on the item, the base item properties, or even the member's own properies. Here's an example, from properties_rare.yaml:

enchanted:
  - '{enchantment.nouns}'
  - '{enchantment.adjectives}'
  - 'Attacks made with this magical weapon do an extra {this.damage} {this.damage_type} damage.'
  - '{enchantment.damage_type}'
  - 1d6
  - 0
  - weapon

The enchanted property adds magical damage to a weapon. Enchantments are not defined in this file, though; they are defined in magic_damage_types.yaml and look like this:

fire:
  - 'flames, fire'
  - 'flaming, burning'

When an item is assigned the enchanted property, an enchantment property is added automatically. The template strings in the data are then resolved using the values from that property. So the nouns and adjectives of the enchanted property may resolve to "flames, fire" and '"flames, burning", respectively.

Remember that the keyword attributes in the template strings are defined by the headers of the data source; this all works because magic_damage_types.yaml defines includes the "nouns" and "adjectives" headeres.

Base Properties

A template string without a dotted attribute will refer to an item's basic properties. These basic properties are defined by each ItemGenerator class; for Weapons, basic properties are defined in weapons.yaml and include name, category, type, weight, damage_type, damage, range, reload, value, and properties. Here's an excerpt:

metadata:
  headers:
  - name
  - category
  - type
  - weight
  - damage_type
  - damage
  - range
  - reload
  - value
  - properties
Battleaxe:
  - martial
  - Martial
  - '4'
  - Slashing
  - 1d8
  - 5
  - ''
  - '1000'
  - versatile

Thus, {damage_type} will resolve to "Slashing" in any template string on any property of a Battleaxe.

The "this" Keyword

The enchanted member also uses the keyword this. When this property is applied to an item, this will refer to members of the enchanted collection. so {this.damage} becomes 1d6.

this can also reference other template strings! In our example, {this.damage_type} will resolve first to {enchantment.damage_type}, which will in turn resolve to fire (because magic_damage_types.yaml defines the "damage_type" header).

Putting it all together, a full substitution of values will yield a data set member that looks like this:

enchanted:
  - 'flames, fire'
  - 'flaming, burning'
  - 'Attacks made with this magical weapon do an extra 1d6 fire damage.'
  - 'fire'
  - 1d6
  - 0
  - weapon

Overrides

Data set members can override the values of base properties, by including a member with a header beginning with override_. Here's an example from properties_uncommon.yaml:

metadata:
  headers:
    - name
    - nouns
    - adjectives
    - description
    - damage_type
    - damage
    - to_hit
    - override_damage_type
    - override_damage
    - type
'elemental damage':
  - '{enchantment.nouns}'
  - '{enchantment.adjectives}'
  - 'This magical {name} deals {this.damage_type} damage.'
  - '{enchantment.damage_type}'
  - 0
  - 0
  - '{enchantment.damage_type}'
  - null
  - weapon

Here, the elemental damage member overrides the base damage_type property with that of the selected enchantment (changing a dagger's slashing damage to fire, for example). The override_damage is ignored because its value is null.

Overrides are processed before other template substitutions, ensuring that overrides are honored everywhere, so if another property on an item with the elemental damage property makes a reference to the base {damage_type} value, it will resolve to {enchantment.damage_type}.

Comma-Separated Values

When templates are processed, references to members defined as comma-separated lists are converted to lists, and a random value is selected from it. For example, the nouns on the fire enchantment are:

  - 'flames, fire'

The template string {enchantment.nouns} could resolve to either "flames" or "fire".

Reserved Keywords

The following keywords are reserved:

  • this: Refers to the current data set member
  • enchantment: Refers to a random member of the magic_damage_types.yaml data set