<--Status ^--xmld20--^ Templates and Prestige Classes-->

xmld20 - an XML Schema for d20 gaming systems - coding

A few issues came up soon into the third version of the xmld20 system. First, I completely ignored the fact that Ability was used for both the six primary ability scores and the individual abilities in a group SpecialAbility. I've renamed the latter as SAbility. This might change in the future. I have the contents of the Bio block from the character sheet file come through untouched, to provide Class and Race.

Much of the code was taken from the second version of the system, with changes to the layout in the character data as appropriate. There was a lot of lower-casing, since I decided to go that route. The older nested model of weapons, spells and such was easy to merge with the flat model in the character.

The lists of Special Attacks and Special Qualities worked out better; in previous versions, I was concatenating the lists of attacks and qualities that were culled from the creature's type, subtypes and explicitly-listed ones in the entry. This concatenation wouldn't display quite like it did in Monster Manual -- namely, it wasn't sorted. I had ignored it for the time being, as the information was still there, but now I am returning the results as sequences, which can them be sorted.

Going with lower-case for skill names and feat names means that they don't display correctly, so I'll need a wordcap() function that will capitalize the first letter of each word, except for of and other words of that nature. For now, only the first letter is being capitalized, so we get "Sleight of hand" and "Combat casting". Very minor, but will need to be fixed at some point.

That's all fixed, and everything's looking great. The aboleth is outputting better than it used to, and the aboleth mage is almost exact. The conditional skills are a bit sticky; we currently see

	Survival (+5 underground, +5 other planes, +5 follow tracks)
instead of
	Survival (+5 following tracks, on other planes, and underground)
This is a consolidation issue (the +5s being merged into one descriptive block) and a sorting issue. There's another case, that erroneously doesn't appear in the Monster Manual at all:
	Use Magic Device +2 (+4 scrolls, +4 scrolls)
which would properly be
	Use Magic Device +2 (+6 scrolls)
I'm going to conveniently ignore these for now, because I'm otherwise quite proud of the aboleth mage's output. The DC on its SpecialAbilities all got correctly re-calculated, and the spell list output is better than I had expected. Support for PreparedSpells was added, as was support for the metamagic feats and their effect on the slot used. I finally added in support for the ability bonuses to spells per day as well as the PerDay and SpellsKnown tables for spellcasters. This means that
Typical Wizard Spells Prepared (4/6/5/4/4/3; save DC 15 + spell level):
0--daze, detect magic (2), resistance; 1st--alarm, charm person, 
color spray, mage armor, magic missile (2); 2nd--blur, bull's strength,
 darkness, fox's cunning, see invisibility; 3rd--dispel magic,
 displacement, fly, lightning bolt; 4th--greater invisibility,
 phantasmal killer, scrying, stoneskin; 5th--hold monster,
 empowered lightning bolt, wall of force
Was all generated from this information:
   <Ability name="intelligence">20</Ability>
   <Spell subtype="wizard">10</Spell>
   <PreparedSpell subtype="wizard" name="daze"/>
   <PreparedSpell subtype="wizard" name="detect magic">2</PreparedSpell>
   <PreparedSpell subtype="wizard" name="resistance"/>
   <PreparedSpell subtype="wizard" name="alarm"/>
   <PreparedSpell subtype="wizard" name="charm person"/>
   <PreparedSpell subtype="wizard" name="color spray"/>
   <PreparedSpell subtype="wizard" name="mage armor"/>
   <PreparedSpell subtype="wizard" name="magic missile">2</PreparedSpell>
   <PreparedSpell subtype="wizard" name="blur"/>
   <PreparedSpell subtype="wizard" name="bull's strength"/>
   <PreparedSpell subtype="wizard" name="darkness"/>
   <PreparedSpell subtype="wizard" name="fox's cunning"/>
   <PreparedSpell subtype="wizard" name="see invisibility"/>
   <PreparedSpell subtype="wizard" name="dispel magic"/>
   <PreparedSpell subtype="wizard" name="displacement"/>
   <PreparedSpell subtype="wizard" name="fly"/>
   <PreparedSpell subtype="wizard" name="lightning bolt"/>
   <PreparedSpell subtype="wizard" name="greater invisibility"/>
   <PreparedSpell subtype="wizard" name="phantasmal killer"/>
   <PreparedSpell subtype="wizard" name="scrying"/>
   <PreparedSpell subtype="wizard" name="stoneskin"/>
   <PreparedSpell subtype="wizard" name="hold monster"/>
   <PreparedSpell subtype="wizard" name="lightning bolt" metamagic="empower"/>
   <PreparedSpell subtype="wizard" name="wall of force"/>
Since I'm on a roll, I'm going to go code a few more monster classes for the old stat block. Then I'll look at the new stat block.

Or maybe not so soon. Coding up the achaierai, the allip and the astral deva (which went pretty smoothly), I did find a couple of things that I want to address before I continue adding more entries, or going for the new stat block:

That went pretty easy, but by making these changes, I found another issue which I might put aside for now.

Right now, the angel subtype has the following entries:

<Type name="angel">
	<Add type="SaveBonus" subtype="angel" name="fortitude" condition="against poison">4</Add>
	<Set type="Language" name="celestial"/>
	<Set type="Language" name="infernal"/>
	<Set type="Language" name="draconic"/>
	<Set type="SpecialQuality" name="low-light vision"/>
	<Add type="SpecialQuality" subtype="angel" name="immunity to acid, cold, and petrification"/>
	<Add type="SpecialQuality" subtype="angel" name="protective aura"/>
	<Add type="SpecialQuality" subtype="angel" name="resistance to electricity 10 and fire 10"/>
	<Set type="SpecialQuality" name="tongues"/>
	<Set type="Immune" name="acid"/>
	<Set type="Immune" name="cold"/>
	<Set type="Immune" name="petrification"/>
	<Add type="Resist" subtype="angel" name="electricity">10</Add>
	<Add type="Resist" subtype="angel" name="fire">10</Add>
</Type>
The issue I'm having here is the fact that the immunities and resistances are static bits of text, instead of being "computed". While this works fine in this case, what happens when a class or subclass adds resistances and immunities? They will likely be repeated in the <strong>Special Qualities</strong> section of the stat block, which isn't very good. The Immune and Resist entries are there for the newer stat block, which has a specific line for that, so why not figure out this information from those? That way, any duplication of abilities (say, if another class gives immunity to cold) will be overwritten (because we used Set), and any conflict (say, if another class gives resistance, but not as good, or better) can be calculated to show the best value to use.

Before I tackle that, I'm going to code up a few more monsters, just to see if anything else comes up. A little extra work had to be done to add the planetar, solar and pitfiend, but nothing too problematic. Supporting the merged Immune and Resist entries turned out to be pretty easy; the "angel" and "baatezu" subtypes now no longer list their resists and immunities as a generic SpecialQuality string as above, but instead get that generated from the Immune and Resist Entries. The allip threw a wrench into it by having a dozen explicit immunities (as an undead) but none of which get listed in the Special Qualities part of the stat block. I added a list="no" attribute that tells the code not to add it to the general description list.

I've put it off long enough -- time to start work on the new stat block generator! And that went quite well. The biggest issue that came up is that the old and new stat blocks treat special abilities, special attacks, special qualities, attack options, etc. all differently. Even resists, immunities and damage reduction are treated differently.

This revealed two problems. The first is that I'm currently adding a SpecialAbility or SpecialQuality as well as a Sense or Language for things like darkvision or telepathy. This could be better handled as we did with the Resists and Immunities.

A bigger problem is my use of SpecialAbility and SpecialQuality in different places. I was originally going to have all such items listed as generic abilities, referred to in the class tables, but there are some, such as damage reduction, that don't have a descriptive paragraph in the body of the monster entry, and I used this method to differentiate them.

This, in hindsight, was a bad way to do it. I have both SpecialQualities in the class list as well as the SpecialAbilities in the special ability list which have a SpecialQuality tag. This means searching for both every time I might want them. Additionally, some text, regeneration being one of the best examples, is very specific to the target, and would be stretching our text-replacement methods for other text blocks. There has got to be a better way to note that a SpecialAbility does or doesn't get displayed in various areas, and we're going to find it.

The biggest issue is that the old statblock lumped most special abilities as either a special attack or a special quality, where the new statblock put them as senses, languages, attack options, resists and armor class modifiers, leaving only a few not falling in those categories to be listed as special qualities. Should these abilities be only listed as a Sense, or a Language, and let the code go and find out if there's a matching SpecialAbility? And should absolutely every special ability be listed in an external list, none explicitly described in the class? Or can we adjust the SpecialAbilities so they're more like Spells, Equipment, Weapons and the rest, so they work with the super idea where it can inherit items if needed?

That's probably the best way to go. The Weapons and Equipment still maintain the nested elements as they did before, and since we need ways to tag such special ideas like "Sense" or "SpecialQuality", nested works. For the regeneration issue and the specialized text, allowing inheritance and supercedence. It does seem a little awkward to add a super attribute if all we want to do is use an ability and don't plan on overriding anything.

That worked very well. I had to make a small modification to my rules for adding Extras to items, because the pitfiend had

	<SpecialAbility name="poison"/>
	<Immune name="poison"/>
and the inheritance code was putting the immunity as part of the special ability. To get around this, attached elements now use an onto attribute, so the pitfiend's weapons went from
	<Add type="Weapon" subtype="pit fiend" name="bite" super="bite"/>
	<Add type="Damage" name="bite">4d6</Add>
	<Add type="Extra" name="bite">poison</Add>
	<Add type="Extra" name="bite">disease</Add>
	<Add type="Weapon" subtype="pit fiend" name="claw" super="claw"/>
	<Add type="Damage" name="claw">2d8</Add>
	<Add type="Weapon" subtype="pit fiend" name="wing" super="wing"/>
	<Add type="Damage" name="wing">2d6</Add>
	<Add type="Weapon" subtype="pit fiend" name="tail slap" super="tail slap"/>
	<Add type="Damage" name="tail slap">2d8</Add>
to
	<Add type="Weapon" subtype="pit fiend" name="bite" super="bite"/>
	<Add type="Damage" onto="bite">4d6</Add>
	<Add type="Extra" onto="bite">poison</Add>
	<Add type="Extra" onto="bite">disease</Add>
	<Add type="Weapon" subtype="pit fiend" name="claw" super="claw"/>
	<Add type="Damage" onto="claw">2d8</Add>
	<Add type="Weapon" subtype="pit fiend" name="wing" super="wing"/>
	<Add type="Damage" onto="wing">2d6</Add>
	<Add type="Weapon" subtype="pit fiend" name="tail slap" super="tail slap"/>
	<Add type="Damage" onto="tail slap">2d8</Add>
At this point, things are working well for the current entries. What's next? I haven't been playing too much with multi-classing yet, apart from the aboleth mage and the few multiclass characters. I've got no code to worry about the best Resist if two classes have added it. I've never tried combining two classes that build up evasion to improved evasion. And I've yet to try coding up a template or a prestige class.
<--Status ^--xmld20--^ Templates and Prestige Classes-->
©2002-2017 Wayne Pearson