I had time to think about this some more on my bus ride home. And even take notes, though that’s a bit of a challenge (I hate writing in a moving vehicle).
It appears I was only mostly right in what I had planned. I was having difficulty reconciling ‘data type’ and ‘data object’ in some cases. For instance, ‘rage power’ is a class feature, cool… but it can also be a data type. There are literally hundreds of rage powers. I know. I counted.
Turns out a solution is pretty simple. Time for some data modeling.
Good data projects really should have a data dictionary. I’ve worked (been stuck with) too many that don’t, so I’m starting one now.
Some of the names are subject to change, but I describe the purpose of the various types below.
This is the core element of the entire system. Almost everything I care about is a data object: feats, spells, skills, monsters, everything. A data object has:
- attributes, meta-information about the object that describe how the object is to be treated. Most data objects have default values for the attributes.
- one of the common attributes is ‘parent’, indicating the data object this is an example of (“Rage Power” has an parent of “Class Feature”). This will often be set more or less implicitly by a new object being subordinate to a data object or data type. “Rage Power” is defined inside “Class Feature”; I can set it explicitly but don’t need to.
- another attribute indicates that the data object can be used as a data type. For instance, “Class Feature” might be a data object (that probably never gets printed, oh well) marked “data-type=’class-feature'”. “Rage Power” is a class feature that might be marked “data-type=’rage-power'”. When I want, I can use a ‘rage power’ data type marker and all data objects under it are marked as being rage powers.
- another attribute indicates ‘index type’, how to mark objects of this type when they get indexed. “Rage Power” is a class feature, and is indexed as “Rage Power (class feature)”, while “Animal Fury” gets indexed as “Animal Fury (rage power)”.
- stat block (often empty): spells and monsters are examples here, but even feats can be said to have a stat block (type, prerequisites, type, and minimum level). May or may not be rendered as an actual stat block.
- content: block text, tables, lists, etc., and may have sections and subobjects.
Data type is actually much smaller and simpler than I’d anticipated. It’s basically a marker to show that the objects ‘inside it’ are of a particular type. Importantly, though:
- the data type markers are never printed, they exist only in data.
- there is a hierarchy to the data type Word styles, but only so they can be nested within other things (a bloodline has bloodline powers; structurally the data file might have
- Bloodline [data type]
- Draconic [‘bloodline’ data object]
- statblock (bloodline skill, list of bloodline feats, list of bloodline spells, etc.)
- Bloodline Powers [section heading]
- Bloodline Power [data type]
- Claws [‘bloodline power’ data object]
- Dragon Resistances [‘bloodline power’ data object]
- Breath Weapon [‘bloodline power’ data object]
- statblock (save DC)
- Wings [‘bloodline power’ data object]
- Power of Wyrms [‘bloodline power’ data object]
- while there is a hierarchy to the Word styles used, it does not reflect on the data hierarchy. “Rage Power” and “Bloodline Power” are both data types, even though “Bloodline Power” is typically used inside a “Bloodline” (as shown above). There is really no fixed hierarchy to the objects, though there is to the data types. If it turns out convenient to have a “Magic Item” inside a “Monster” (not common, but not unheard of) you can do it.
- Word style hierarchy probably goes
- data type 1 >
- data object 1 >
- data section 1 >
- data type 2 >
- data object 2 >
- data section 2 >
- data type 3 >
- data object 3 >
- data section 3
- If I somehow really need more levels (class -> class feature -> class subfeature -> class subfeature power: sorcerer -> bloodline -> draconic bloodline -> claws) I’m probably making it difficult for myself. The ‘bloodline class feature’ can stay here (it identifies that the class has that feature), but the bloodline definitions can themselves be higher-level objects.
- It occurs to me that this even lets me have objects that are children of the same kind of object. For instance, ‘monster group’ and “magic item group” can now be nested. “Dragon” > “Chromatic Dragon” can now work, as can “Rod” and “Metamagic Rod” or “Wondrous Item” > “Figurine of Wondrous Power”.
- Word style hierarchy probably goes
I have a practice of indexing the game content pretty aggressively. I can include indexing rules in the data objects so that when they are referenced as types the objects can be indexed the way I want. This definitely includes the type markers I have in the index, but can also include hierarchy displayed in the index.
I expect that I will be able to create indexes something like:
- Curse, see Curse (affliction)
- Disease, see Disease (affliction)
- Poison, see Poison (affliction)
- Bloodline (class feature)
- Claws (bloodline power)
- Domain (class feature)
- Draconic (bloodline)
- Earth (domain)
- Electricity Resistance (domain power)
- Elemental (bloodline)
- Fey (bloodline)
- Lightning Arc (domain power)
- Undead (bloodline)
Parts of this will be pretty easy, a few might be a bit tricky… but I’ve solved harder problems.
The semi-arbitrary/semi-abstract nature of the data structure also makes it so I can give each object a unique identifier, as long as the object is defined in basically the same way twice.
Data objects have two identifiers. The first is the group-id and consists of the type and name (modified). The “Rage Power” class feature has group-id=”class-feature.rage-power”. Some (parent) data types can be marked to be appended to the end of their children. For instance, while “Rage Power” has group-id=”class-feature.rage-power”, when described in the barbarian class (“Class” data type says “append to children’s IDs”) that object has id=”class-feature.rage-power.barbarian”.
The dual IDs let me group the objects by group-id (they are the same type and same name, and therefore equivalent for search purposes) while keeping the individual instances separate. This is important as there is more overlap, because I can then refine the data: I can have a single master definition of “Uncanny Dodge” that explains what it does, and the class-specific entry is reduced to a note saying when that class gains access to it.
Didn’t plan to write this tonight, already had a “Y Day” post (and we were supposed to go judo but I’m having vehicle problems… again). I had some notes scribbled on the bus and I thought I’d try to get them into more readable form.
Strangely enough, I didn’t actually look at them. It seems the act of writing them was sufficient to cement them in my mind for later. Which is good, because I can’t read the damn things.
I’m kind of working with this as well. I’ve been thinking about a really generic approach. The way I view things in my head is that a character is essentially a collection of Features. So, that begs the question, “What is a Feature?” The answer to that is really, honestly, everything. Attributes are Feature, Race is Feature, their hair color, even the character’s name is a Feature…. right down to the stuff they carry. It’s all a feature of a character.
How to represent that in Data is the question that needs answering. This is the approach I am working on taking.
A Feature is (simply-two things) :
Feature -> MetaTag(Which describes the type of data represented) & Data(image, text, sound, numeric)
Where things get tricky is that features can seem to be composite. For example, spell casting could be considered a feature in itself but, it expands into a bunch of specific abilities (the individual spells). Another example would be Attribute scores. In and of itself, an Attribute score is just a number but, it ties the character into a specific modifier, a specific carry weight, and some prerequisites. And, this is where I started to get some clues about how to organize all this data.
An Attribute score isn’t just a Feature, it’s also a Feature Group. A Feature Group is a collection of related Features. In other words, an Attribute score can be broken down into several closely related Features -> Point Cost, Attribute, Modifier, Carry Weight. All OF THESE are Features. Where this becomes important is in how they are actually implemented in a database.
Of course, everything about an Attribute score could be calculated as needed and never needs to be stored. Which leads us to another clue. The relationship between interacting Features is usually some kind of procedure.
When using Feature A perform Procedure 1 modified by Feature B and Feature D to affect Feature X on ??? on what? Another clue. A Feature Group isn’t the top of the hierarchy. Above that there is a Feature Super Group. How does that look?
Feature -> Meta data & Data
FeatureGroup -> Collection of related Features
FeatureSuperGroup -> Collection of related FeatureGroups
A Super Group is the top of the chain. They can be nested. So, a character’s strength is a feature group that consists of the actual score, the modifier, the cost to get the score, and the carry weight. All of the scores together are a Stat Block which is a Super Group. The Character itself is a Super Group containing a bunch of nested Super Groups. All connected by Procedures.
At least that’s the idea… i’m still working on implementing it.
I think in a relational setup it kind of has to be that way, yes. I’m working with XML, which gives me a lot of freedom that way (and costs me in others, but I think the gains are worth it).
The degree of metadata needed to implement a generic/abstract working system, as opposed to a data storage system such as I’m working on, gets awfully hairy, fast.