<--Part II ^--xmld20--^ Re-think-->

xmld20 - an XML Schema for d20 gaming systems - Part II, continued

Part II, continued

Over three weeks later, and everything's looking great. A lot of code was pruned down, with the complicated recursive templates removed and replaced with functions. The base d20 library got a huge overhaul, but still needs some functions to be re-written with more efficient XSLT 2.0 versions. Additionally, the XSLT that produces the statblocks for both the old and new formats has been ported, but isn't fully finished.


Namespaces, to me, are generally a good idea, but I find that XML really goes overboard with them. With the implementation of XSLT and XSchema elements in namespaces, to the required use of them for user-written functions, they really start to make the XML a bit wordier than it ought to be.

That being said, I've found the right equilibrium for their use within the xmld20 project, for now at least. I use the namespace xmld20 for my XSLT functions, so we now have xmld20:skillAbility() and xmld20:thisMeleeAttack(). Most of these functions take (or will soon take) a $source as their first parameter, so no context is required (because unlike templates, functions don't have context). This is fine, because it means I can share the functions between similar XML data, such as Monster and Character records.

The data itself is currently not in a namespace. I tried this when I implemented Xschemas for the datafiles, but wasn't able to reconcile their use between schema verification and XSLT processing.


Considering the name of this project is xmld20 - an XML Schema for d20 gaming systems, I've really been negligent with actually implementing a schema. No more, as Saxon, the XSLT 2.0 processor I'm using, also well-supports XSchema.

I have put together two schemas so far, one for the Monster records, the other for the Tables data. Once I finish the port of the current work to XSLT 2.0, and get the same results with that as I did before I started the port, I'll post the schemas here. Of course, these schemas are likely to change, to allow us to accurately record everything there is to say. I will say, though, that considering it had been over three years since I had touched XSchema, it was surprising at how quickly it came back. It is now part of the build process to verify that the data is well-formed before we start to process it, making the build process take a little longer, but removing a lot of problems that didn't occur as errors.

There are a lot more checks that can be done in the schema that aren't right now, such as validity of datatypes; presently, things like damage, size, damage type and ability names are just general strings, where they can be described with their restricted forms (xdy for dice rolls, Tiny/Small/Huge/etc. for sizes, bludgeoning/slashing/piercing for damage type and Strength/Intelligence/etc. for abilities) in Xschema to further ensure the data is well-formed.

As a small digression, this is where some data purists will argue that I should have

instead of
because the schema can more easily force a known element than a known string. I have a couple issues with that: in my form, it can be printed with XSLT by just referring to KeyAbility, instead of name(KeyAbility/*); and telling the schema that KeyAbility is of type xmld20:abilityname and defining the custom type is more readable than saying that KeyAbility is a complexType and having to define that it's a xsd:choice of one of the abilities.

other improvements

One of the best changes made was to create a xmld20:getObject() function, which will retrieve a generic type of element from either the $source document or the $tables set. This lets me find a "longsword" or a "tentacle" or a "+1 dagger of stabbiness" by name, where it first checks if this is an object specific to the monster or character, and then by checking if it's in the general tables (such as the Player's Handbook's equipment list, or the Dungeon Master's Guide's magical item list. The xmld20:getObject() function also supports a super attribute which tells where a given item inherits from. For instance, an aboleth's tentacle is a bit more powerful than a regular one, since it has the slime attack, so the tentacle in the generic tables is written like this:
<Weapon name="tentacle" hand="light" range="melee" proficiency="natural">
and the aboleth's weapon is listed like this:
<Weapon name="tentacle" super="tentacle">
The super="tentacle" tells the xmld20:getObject() that it should first check for a general "tentacle" object in the tables, then override with (or add) values from the aboleth's entry (the one from the source). The final object returned is:
<Weapon name="tentacle" hand="light" range="melee" proficiency="natural">
Of course, it could be that the damage increase is based on the aboleth's size, and doesn't have to be explicitly overridden in its version of a tentacle, but I still have to investigate that.

One example that throws a wrench in the Damage element is that of the Cyclonic Ravager, from Monster Manual IV. It lists as

 Melee: smith of seven winds +28 touch (4d6+7 or 1d6+7, see text)
Unless I start formulating multiple damage entries, and other notations, this is probably best served, for now, as a static string that gets tacked on, and isn't calculated at all. I'm not sure where the +7 would come from anyway, so this is probably for the best.

While working further, I realized that I'm still handling the Knowledge, Craft, Profession and Perform skills as individual skills, such as

	<Skill name="Knowledge (religion)"/>
	<Skill name="Knowledge (the planes)"/>
when I should be handling them more like feats that have other information, such as
	<Feat name="Quicken Spell-Like Ability" value="fireball"/>
The skills are a little trickier, because the individual ones have different effects to other things, such as synergy bonuses, as seen here:
	<Skill name="Knowledge (local)"><KeyAbility>Int</KeyAbility><SynergyTo name="Gather Information">2</SynergyTo></Skill>
	<Skill name="Knowledge (nature)"><KeyAbility>Int</KeyAbility><ConditionalSynergyTo name="Survival" condition="aboveground environs">2</ConditionalSynergyTo></Skill>
This should probably be changed to something like:
	<Skill name="Knowledge" subname="local"><KeyAbility>Int</KeyAbility><SynergyTo name="Gather Information">2</SynergyTo></Skill>
	<Skill name="Knowledge" subname="nature"><KeyAbility>Int</KeyAbility><ConditionalSynergyTo name="Survival" condition="aboveground environs">2</ConditionalSynergyTo></Skill>
This makes the skill searching a little messier, but still doable, and I'm not sure about the subname name, but it should be considered for uniformity's sake.

I'm going to go back to typing in a few more entries, for a larger testbase, and will then likely look into calculating weapon damage where possible. I've recoded all of the spell and spell-like handling, which seems to be working well, including the DCs when needed.
<--Part II ^--xmld20--^ Re-think-->

©2002-2017 Wayne Pearson