<--Template ^--xmld20--^ Refactor, continued-->

xmld20 - an XML Schema for d20 gaming systems - refactor

So it's time to give the existing code a bit of a revamp, getting rid of any hard-coded logic that we can, thereby also adding support for general concepts that we may not have needed yet, but will certainly need in the future.

The first thing I want to fix is the hard-coding of the save-affecting Feats in the statblock-generating code. What was I thinking?

Weapon Finesse

This feat has really brought home a problem that the hard-coding happily and naively got around. In the current version, the combat to-hit calculations explicitly check for whether or not you have Weapon Finesse:
<xsl:variable name="attribbonus">
	<xsl:choose>
	<xsl:when test="$source/Feat[@name='weapon finesse'] or $source/Subtype='incorporeal' or $type='ranged' or $type='thrown'">
		<xsl:value-of select="xmld20:getBonus($source,'dex')"/>
	</xsl:when>
	<xsl:otherwise>
		<xsl:value-of select="xmld20:getBonus($source,'str')"/>
	</xsl:otherwise>
	</xsl:choose>
</xsl:variable>
The most obvious limitations with this code is that it doesn't check whether the weapon actually qualifies for use with Weapon Finesse, nor does it bother to check whether the character would want to use Dexterity over Strength -- they might have taken ability damage to their Dexterity and have Bull's Strength cast upon them!

But even those errors don't get around the fact that the core code has explicitly gone looking for the presence of this feat. What happens when the next sourcebook or splatbook comes along and has a feat that gives a similar effect? Maybe some spell effect or some enchantment on an item gives a Weapon Finesse-like effect?

A few weeks ago I stumbled upon another character generation program, Metacreator. It's a very customizable interface -- too much, I feel. It's powered in the back by a database, and in the middle with a scripting language with the feel of Javascript. This system is customizable enough to be able to support systems other than D20, and allows you to code your own.

I've toyed with the idea of a scripting language in the xmld20 system for a while now, but was always able to convince myself that it wasn't necessary. In the case of Weapon Finesse, I started thinking about tags that could be within the Weapon Finesse feat that would be searchable by the attack calculation code to find the Feats that might affect that calculation. Weapons and spell-effects, too, could have the same tagging, so the code would also find them, as appropriate.

The problems I can see with this approach are twofold. One, that I have to come up with a list of every calculation and display item that might be affected by the presence or absence of an item and come up with a tag for it; and two, that sometimes just a "tag" isn't enough.

For instance, with the Weapon Finesse, sure, I need to be able to say "hey, this affects to-hit calculations!"

	<Feat name="weapon finesse"><ToHit/></Feat>
but that doesn't say how...
	<Feat name="weapon finesse"><ToHit ability="dex"/></Feat>
but that doesn't say when...
	<Feat name="weapon finesse"><ToHit ability="dex" conditiontype="weight" conditionvalue="light"/></Feat>
but that doesn't have the exceptions... (now indented for readability)
	<Feat name="weapon finesse">
		<ToHit ability="dex" conditiontype="weight" conditionvalue="light">
			<Exception type="extra" weaponname="rapier"/>
			<Exception type="extra" weaponname="whip"/>
			<Exception type="extra" weaponname="spiked chain"/>
		</ToHit>
	</Feat>
but that doesn't check if using Dexterity would even be beneficial...
	<Feat name="weapon finesse">
		<ToHit ability="dex" maximumwith="str" conditiontype="weight" conditionvalue="light">
			<Exception type="extra" weaponname="rapier"/>
			<Exception type="extra" weaponname="whip"/>
			<Exception type="extra" weaponname="spiked chain"/>
		</ToHit>
	</Feat>
but that doesn't cover the fact that use of a shield then applies the armor check penalty to the to-hit...
	<Feat name="weapon finesse">
		<ToHit ability="dex" maximumwith="str" conditiontype="weight" conditionvalue="light">
			<Exception type="extra" weaponname="rapier"/>
			<Exception type="extra" weaponname="whip"/>
			<Exception type="extra" weaponname="spiked chain"/>
			<Exception type="penalty" inuse="shield" value="armorcheck"/>

		</ToHit>
	</Feat>
and oops, natural weapons are always light for this purpose...
	<Feat name="weapon finesse">
		<ToHit ability="dex" maximumwith="str" conditiontype="weight" conditionvalue="light">
			<Exception type="extra" weaponname="rapier"/>
			<Exception type="extra" weaponname="whip"/>
			<Exception type="extra" weaponname="spiked chain"/>
			<Exception type="extra" weaponproficiency="natural"/>
			<Exception type="penalty" inuse="shield" value="armorcheck"/>

		</ToHit>
	</Feat>
Looking at the above, we've just made up all sorts of elements and attributes, and now our to-hit calculation has to be able to handle all of these values. And really, this was just a quick whip-up -- we'd probably have the elements better defined, so they were more generic in describing how they affect the calculation (or when, or if).

In case this doesn't look so bad to you, keep in mind that the core code now has to know that it needs to check for "when" (and "when not") a given feat (or item, or class ability, or spell effect) does "what", in which cases, in which exceptions, after which checks, but not when this, but only when that... we need a whole language of tags to define the when and where. Did you catch that? A whole language.

If you don't agree, then consider my made-up feat:


Convoluted Warrior allows the character to take a +1 bonus to-hit when
using a scimitar, a masterwork battleaxe, or any improvised weapon.
This bonus only applies if the character's Dexterity is no higher than
16+character level, or if they have 4 levels of wizard, but only then
without school specialization.  This bonus increases by +1 when the
character reaches level 5, and at every level divisible by 7 after
that.  Note that if the improvised weapon starts with H, then the
bonus is doubled, but only if the character's armor check penalty is
less than 3.  This feat may be taken multiple times, but its effects
don't stack.

Complete nonsense, but just imagine having to have the to-hit calculation code be able to handle that (or each of the arbitrary rules individually across a dozen feats). It was because of this possibility that I have decided to implement a scripting system.

Is scripting really necessary? I believe it is. Looking at my made-up feat, we would need code in our to-hit calculation code that looks for tags specific about the type of weapon; that say to look at attribute values; for tags that tell you to look at the class levels; that say to look at specifics about your class (the specialization); that look at total character level, at the spelling of the weapon, etc., etc. and each of these cases had different effects to the calculations... this is going to be so much code for just the to-hit calculation, and we haven't touched the possibilities of what we could need. And, of course, it only takes one innovative designer to come up with a case that isn't in the hard-coded logic, and all of a sudden, some feat or spell effect can't be supported.

But. If we have the logic for the calculation as part of the feat, spell, or item, then the designer just needs to code it into the add-on module that is bought/downloaded, and the xmld20 engine will still work. The only thing that the base code needs to know is how to go search for code that needs to be processed for to-hit calculation, or AC calculation, or save calculation. If every feat has a pre-defined tag, such as <ToHitCalc> or <ACCalc>, then the core code searches for them, executes them, and each of those tags changes the result as they see fit. The only time we would need to add a new tag is if the d20 ruleset itself changes -- an example would be the use of action points in Eberron, or using an alternate rule such as spell points instead of spells-per-day.

This does bring up the question of whether even the core code should be written in this scripting language, instead of the core code; this would mean that you could change the rules at any time by dropping in new code -- provide maybe just a new to-hit function, or maybe implementing a system completely separate from d20. I don't know if I'm ready to go this far. Instead of having a scripting language that can do this, it's probably better to just have each of the core functions as separate files in the library, so each of those could be replaced by home-brew rules if desired. That's a project feature that can be added at a later stage without and planning right now. No, for now, what we need is something that can do calculation and logic on a body of XML data; we have our resulting character sheet which holds the levels, the choices made during the levelling up, and equipment that might be available, and all of these values need to be available for our feats and special abilities to determine if they have any effect, and how.

In general, I'm going to develop a small library that implements a simple scripting language, that is designed to work on a an XML data set and modify it. That will be done in a separate project, Xscr. As I write this library, work on xmld20 will be paused, but not to worry - I've been working on this in my head for a while, and it's really not going to take too long. I'll be back.
<--Template ^--xmld20--^ Refactor, continued-->

©2002-2017 Wayne Pearson