<--Status ^--xmld20--^ Coding-->

xmld20 - an XML Schema for d20 gaming systems - status, continued

monster stat block generation

Here we go again! So what's missing from our current output, to generate a monster stat block?

old style

Let's start with the old style of stat block, from Monster Manual v3.5. We have the name, the size, the type and subtype. We have hitdice, and the number of them, and we also have either the hitpoints that were rolled, or an absence of them (I didn't mention that -- did you notice it?) which will be a hint to the code that it should figure out the average.

Initiative has always been a calculation. Speed is specified. Armor class is mostly calculated, with a few extras, that we supply. Base attack is supplied, and grapple is calculatable.

Attacks and Full attacks are still something we'll have to address. In the older monster entries, we had nested information, where attacks and full attacks referred to one or more uses of a weapon, which was most likely subclasses from a default weapon, and had extra data like newer Damage or Extra damage or whatever. With this new system, we've been keeping things very "flat", and it sure makes processing a lot easier in a general way. In the aboleth, you may have seen

	<Add type="Weapon" subtype="aboleth" name="tentacle" super="tentacle"/>
	<Add type="Attack" weapon="tentacle"/>
	<Add type="FullAttack" weapon="tentacle">4</Add>
This works fine to tell us that the aboleth has a tentacle attack; that it's descended from the default tentacle weapon listed in some weapon table; that it gets one attack as a standard action; and that it gets four attacks as a full-round action.

What's missing here is that the tentacle does 1d6+8 damage; we've wondered but never figured out whether the 1d6 is tied directly to the size of the aboleth (tentacles by default have 1d3 damage, and two steps up from Medium->Large->Huge would explain 1d3->1d4->1d6), and we know that the +8 is from strength. There's also the "plus slime" ability on the aboleth's tentacle.

The "plus slime" is easy enough:

	<Add type="Extra" name="tentacle">slime<Add>
Remember, the "plus" is added in by the code -- we only ever said
	<Extra>slime</Extra>
in the previous version. And the damage, in case it's not a calculatable value? The same way:
	<Add type="Damage" name="tentacle">1d6</Add>
Or should this be Set? We'll ignore the fact that that value isn't readily parsable, as we have before. With this method, then, we can have our code, for each weapon that it's describing, search for Damage and Extra elements (and Critical etc.) that have a name attribute that matches the weapon's name attribute. This will have to override any previous Damage entry that comes from the original, which we can do with a little twisting of our current object-manipulation routines.

So, arbitrary values attached to the weapons is fine, but what about the full attack? The aboleth isn't the best example for this, but the pit fiend sure is. If you recall, his Attack was one claw, but his Full Attack was 2 claws, 2 wings, a bite and a tail slap. And the astral deva was, for just the regular Attack, the mace or a slam, and the Full Attack was the mace (multiple swings) or a slam. These cases worked fine when we had embedded data, in the old version, but can we flatten this out?

We need to be able to say a few things: we need to be able to talk of an Attack or a Full Attack (and there can be multiple of each); to talk about the weapon(s) used in them (and there can be multiple), and the say how many swings (or perhaps figure that out on context?)

What if we did something like this for the pit fiend?

	<Add type="Attack" name="attack1" weapon="claw">1</Add>
	<Add type="FullAttack" name="fullattack1" weapon="claw">2</Add>
	<Add type="FullAttack" name="fullattack1" weapon="wing">2</Add>
	<Add type="FullAttack" name="fullattack1" weapon="bite">1</Add>
	<Add type="FullAttack" name="fullattack1" weapon="tail">1</Add>
Note that I've added the explicit 1 to the regular attack; we could have the code assume it, I suppose... I'm not sure it matters, and maybe we'll support both. The names of the Attack and FullAttack are arbitrary, and the "1" on the end isn't necessary for the pit fiend, but would be for the astral deva:
	<Add type="Attack" name="attack1" weapon="mace">1</Add>
	<Add type="Attack" name="attack2" weapon="slam">1</Add>
	<Add type="FullAttack" name="fullattack1" weapon="mace">3</Add>
	<Add type="FullAttack" name="fullattack2" weapon="slam">1</Add>
Again, I don't know that the "1"s are necessary on the attacks, or can be assumed as "1" if the number if missing; also, the "3" for the full mace attack is calculatable, and probably ought to be.

The nice thing about this method is that we know that Attacks or FullAttacks with the same name are separated by an and, and those with different names are separated by ors. Our algorithm is simply

foreach Attack with a unique Attack/@name
	foreach Attack with that @name
		display weapon info
		display "and" if this isn't the last one
	display "or" if this isn't the last one
and the same thing for FullAttack (where the only difference would be that the weapon info would represent a full attack.) The only question is how do we code that algorithm? It turns out that the easiest way (that I know of) in XSLT is to use the Muenchian Method, as detailed here. The basic gist is that you need to know all of the different Attacks and FullAttacks (the "unique" part of the algorithm), and then need to be able to iterate over each, and then over each item in that group. So, our code can do it backwards, first by grouping all like Attacks and FullAttacks:
	<xsl:key name="attacks" match="Attack" use="@name"/>
	<xsl:key name="fullattacks" match="FullAttack" use="@name"/>
and then we need a way to invert this lookup, to find all of the keys that were used. To do that, we go through all of the Attacks (or FullAttacks) and pull out each one that is the first match in its @name:
	<xsl:for-each select="Attack[count(.|key('attacks',@name,$character)[1])=1]">
It looks messy, but it's basically saying "for each Attack, look its @name up in the key list (which returns all Attacks that match that @name), then take the first entry (the [1]) and merge it with this Attack; if we only end up with one node, then we're talking about the same one, and that means this node *is* the first one."

Getting the first match for each @name gives us our roundabout way to iterate over the @names, so now we can iterate all of the items that match that name:

	<xsl:for-each select="key('attacks',@name,$character)">
We would stick ands and ors in where needed, of course. XSLT 2.0 also lets us do this in one line, with
	"for $x in (Attack[count(.|key('attacks',@name,$character)[1])=1]) return 
		for $y in (key('attacks',$x/@name,$character)) return ..."
with the string-join() function, adding the ands and ors is a bit easier than XSLT 1.0 as well.

So for now, it looks like attacks are handled. What else? We've forgotten to add Space/Reach. That'll be a racial thing, with possible changes from the class. Special Attacks are a version of Special Abilities... we should probably have the description of "aboleth slime" in a long list of Special Abilities (as we mentioned we would with sneak attack, uncanny dodge, etc.) and one of the features in that table would be that it's a SpecialAttack, and should be displayed at this time. The same goes for Special Qualities. Also, I think a SpecialAbility can be both. Maybe?

Base saves are supplied, and the rest is calculated; abilities are supplied. Skill ranks and bonuses are supplied, and the rest can be calculated. Feats are given.

Now we get to more descriptive stuff. These are entries like Environment, Organization, Challenge Rating, etc., which make sense when making a monster stat block, but not in a character sheet (a character is unique, and not subject to "rules" about their organization or environment). CR might make sense for an NPC, but not a PC. Treasure for an individual isn't some default standard this and double that; it's a specific list of what they have!

These are all values that should only be used when generating a monster stat block, and not a PC or NPC. It is still information that needs to be added somewhere, but where? In the character? I'm not sure that that makes sense, since you should be able to generate a partially-progressed monster and still have all of this text; you should also be able to have a templated monster which would have this "flavor" text, or even, like the aboleth mage, a class-added monster. Do we really want the character maker to have to add this stuff to the character sheet?

Maybe you should. After all, the typical information of the aboleth is *not* included (or rather, repeated) in the aboleth mage's entry. Perhaps this information is added to a generic character in the same way that a specific character gets its inventory or history? And is this information at all pertinent to someone making a character out of this monster class -- should it be something that's skipped by the character creation software at all (that is, is it its responsibility to do so)?

I'm going to argue, for now, that it's all character-based information, and not class-based, or race-based. We'll see if that gets me into trouble. I'm going to use specific element names, such as Environment and Organization, instead of a generic ExtraStuff tag, because I feel that they're consistently appearing values, and that they should be retrievable by name. Additionally, these values have deeper information that is better handled by our current type/subtype/name/subname system than if we used one of them up with a generic tag. Here's what I've added to the aboleth:

	<Add type="Environment">Underground</Add>
	<Add type="Organization" name="solitary"/>
	<Add type="Organization" name="brood">2-4</Add>
	<Add type="Organization" name="slaver brood">1d3+1</Add>
	<Add type="Organization" name="slaver brood" subname="skum">7-12</Add>
	<Set type="ChallengeRating">7</Set>
	<Set type="Treasure" name="standard">2</Set>
	<Set type="Alignment" name="lawful" subname="evil">usually</Set>
	<Add type="Advancement" name="huge">9-16</Add>
	<Add type="Advancement" name="gargantuan">17-24</Add>
	<Add type="LevelAdjustment">-</Add>
The Alignment is the only thing we might expect to see on a regular character, but since I happily omitted it before, it has appeared here in its regular place in a stat block.

The rest of the monster stat block consists of flavor text, monster description, a Combat block (with some possible text, as well as a full description of the Special Attacks), and some mention of racial skill bonuses. The only part of that that is at all generated is the list of Special Attacks, from the SpecialAbility list, and the text itself should be pulled from some general table describing these abilities, the same one that says that the ability is Ex or Sp or Su.

The one ability that doesn't quite fit in this mould is the Psionics that the aboleth has. Much like spell-like abilities that we'll see on other creatures, these are going to be specific Add entries that need to be grouped together under the Psionics ability. In the older formats, they also had a Group container, and we nested all sorts of stuff.

Here, we're still trying to avoid that (at least, I am -- I don't know about you), so let's look back at how the Psionics SpecialAbility was defined in the aboleth class.

	<Set type="SpecialAbility" name="psionics"/>
A few things that are missing are the effective caster level, the pertinent ability score (for DC calculation) and the class that they are cast as (if appropriate). We still have subtype and subname available in our usual attributes, and these are useful if we need to worry about duplicates; that is, if we might get psionics from somewhere else, is there any reason why they should be grouped together?

That's a good question. With psionics and spell-like abilities, which will both have a bunch of "attached" items to them (spelling out which ones they have), we'll be adding them in a similar manner as we did the Extra and Damage to the weapons, or the different constituents of the Attack and FullAttack entries.

And how do we represent each psionic or spell-like ability? Do we use their own tag

	<Add type="Psionic" name="hypnotic pattern"/>
	<Add type="SpellLike" name="blur"/>
or a generic tag for both?
	<Add type="Ability" subtype="Psionic" name="hypnotic pattern"/>
	<Add type="Ability" subtype="SpellLike" name="blur"/>
The drawback with both of these methods is that there's nothing that ties the initial entry to the individual entries. We'd have to hard-code the fact that if we have a SpecialAbility//psionics, then we should go look for Psionic or Ability/Psionic entries.

The weapon Extras and the FullAttack entries all used the name attribute to group themselves together, either as a referring tag or as a common tag. The problem with using this here is that the Abilities themselves have names, and it doesn't really make sense to call that the subname.

I think I might have to resort to using a new attribute, like group. Thus, we'll have

	<Set type="SpecialAbility" name="psionics" group="psionics"/>
	<Set type="Ability" subtype="Psionic" name="hypnotic pattern" group="psionics"/>
and any SpecialAbility (or perhaps any element whatsoever) will go in search of other entries in the same group to display at the same time. Note that there has to be some sort of "head" of the group; in this case, the SpecialAbility is the head, and the Abilities are the underlings that get displayed beneath.

Note that even though they're listed as psionics under the aboleth, they're really spell-like abilities (just done psionically by the aboleth), and thus should be labelled correctly as

	<Set type="SpecialAbility" name="psionics" group="psionics"/>
	<Set type="Ability" subtype="SpellLike" name="hypnotic pattern" group="psionics"/>
I keep wavering between Psionic/psionic and SpellLike/Spell-Like/spell-like; so far, only types have been capitalized, so I'll give that a try for now. Is it necessary to subtype at all, now that we have the group? Maybe, maybe not. It's good documentation for readers; it allows for easy searching of spell-like abilities; and it allows for the possession of both a psionic and spell-like version of the same ability. Note that the group attribute isn't used as a "identifier" for uniqueness, which means that if we eventually multiclass with something else that gives the same spell-like ability, we're going to overwrite by using Set, so we should perhaps switch to Add:
	<Add type="SpecialAbility" name="psionics" group="psionics"/>
	<Add type="Ability" subtype="spell-like" name="hypnotic pattern" group="psionics"/>
We still haven't figured out where to put class, caster level and relevant ability! These are all features of the SpecialAbility, not the Abilities themselves. We've added arbitrary attributes before, and we'll probably do it again, like this:
	<Add type="SpecialAbility" name="psionics" group="psionics" level="16" class="wizard" ability="charisma"/>
	<Add type="Ability" subtype="spell-like" name="hypnotic pattern" group="psionics"/>
(I'm guessing that these psionic abilities are considered wizard versions, until I check whether divine versions exist.)

Oh, wait: we've forgotten one more thing, which if I had been paying attention to the older implementations, I would have seen: we still need to keep track of how many times a day these abilities can be used. That's a feature of the Ability itself, but do we want to make it an attribute of them? We did so in the older versions, because when displaying the abilities, we group them according to their useability. Using the XSLT key() function, we can still group them together after the fact, as we saw with our Muenchian Method before, and this does prevent us from having to nest entries or provide subgrouping.

	<Add type="SpecialAbility" name="psionics" group="psionics" level="16" class="wizard" ability="charisma"/>
	<Add type="Ability" subtype="spell-like" name="hypnotic pattern" group="psionics" frequency="at will"/>
We'll round out the last few bits of information by using
	<Add type="Flavor">The cool, refreshing water suddenly...</Add>
	<Add type="Description">The aboleth is a revolting fishlike...</Add>
	<Add type="Combat">An aboleth attacks by flailing with its long, slimy...</Add>
That should give us enough information, for the aboleth at least, to generate a stat block. At least, for the old format...

Since we already ran the aboleth through a new stat-block generator with our previous implementation, let's take a peek at the results to see if our present setup would allow its creation as well.

Aboleth CR 7 
Usually LE Huge aberration (aquatic)
Init +1; Senses darkvision 60 ft.; Listen +16, Spot +16
Languages Aboleth, Undercommon, Aquan

--------------------------------------------------------------------------------
AC 16, touch 9, flat-footed 15
(-2 size, +1 Dex, +7 natural)
hp 76 (8 HD)
Fort +7, Ref +3, Will +11

--------------------------------------------------------------------------------
Speed 10 ft. (2 squares), swim 60 ft.
Melee 4 tentacles +12 each (1d6+8 plus slime)
Space 15 ft.; Reach 10 ft.
Base Atk +6; Grp +22
Special Attacks enslave, psionics, slime

--------------------------------------------------------------------------------
Abilities Str 26, Dex 12, Con 20, Int 15, Wis 17, Cha 17
SQ mucus cloud
Feats Alertness, Combat Casting, Iron Will
Skills Concentration +16, Knowledge (any one) +13, Listen +16, Spot +16, Swim +8
Advancement 9-16 HD (Huge); 17-24 HD (Gargantuan)
So: name, CR, alignment, size and type(s), all supplied. Initiative is computed; the Senses, as we did in the previous version, will be flagged as Sense-related abilities, skills, feats, etc. more properly in the general tables where these abilities will exist, instead of in the monster entry as before.

Languages have been forgotten for now; it the previous aboleth, they were tagged in the descriptive text, but we should add those in as part of the race or class, as appropriate. AC is all calculated, as are hitpoints and the saves. Speed is supplied, and the combat we've dealt with before. Space and Reach I've still forgotten, so I'll go add those right now. Base Attack and Grapple are still calculated. Special Attacks are just specially-tagged versions of SpecialAbilities, like the Senses were, like Resists and Immunities, SQ, etc.

Abilities are of course provided; SQ was mentioned above. Feats and Skills are like before, as is Advancement. Following that is the same set of blocks detailing all of the SpecialAbilities, and the few new entries that the new stat block contains, which we'll gloss over for now and deal with later.

This means that we might just have a workable set of data to ONCE AGAIN generate an aboleth's monster stat blocks. To summarize, this is what I've now got (note that I still haven't bothered separating all of the aboleth class gains into different levels):

<Race name="aboleth">
	<Set type="Speed" name="walk">10</Set>
	<Set type="Speed" name="swim">60</Set>
	<Set type="Size">2</Set>
	<Set type="Type">aberration</Set>
	<Add type="Subtype">aquatic</Add>
	<Add type="Language">aboleth</Add>
	<Add type="Language">undercommon</Add>
	<Add type="Language">aquan</Add>
	<Set type="Space">15</Set>
	<Set type="Reach">10</Set>
</Race>

<Class name="aboleth">
  <Levels>
    <Level number="1">
	<Increase type="HD" subtype="aboleth">8</Increase>
	<Increase type="SavingThrow" name="fortitude">2</Increase>
	<Increase type="SavingThrow" name="reflex">2</Increase>
	<Increase type="SavingThrow" name="will">6</Increase>
	<Increase type="AC" name="natural">7</Increase>
	<Add type="Weapon" subtype="aboleth" name="tentacle" super="tentacle"/>
	<Add type="Attack" weapon="tentacle"/>
	<Add type="FullAttack" weapon="tentacle">4</Add>
	<Set type="SpecialAbility" name="enslave"/>
	<Set type="SpecialAbility" name="psionics" group="psionics" level="16" class="wizard" ability="charisma"/>
	<Set type="Ability" subtype="spell-like" name="hypnotic pattern" group="psionics" frequency="at will"/>
	<Set type="Ability" subtype="spell-like" name="illusory wall" group="psionics" frequency="at will"/>
	<Set type="Ability" subtype="spell-like" name="mirage arcana" group="psionics" frequency="at will"/>
	<Set type="Ability" subtype="spell-like" name="persistent image" group="psionics" frequency="at will"/>
	<Set type="Ability" subtype="spell-like" name="programmed image" group="psionics" frequency="at will"/>
	<Set type="Ability" subtype="spell-like" name="project image" group="psionics" frequency="at will"/>
	<Set type="Ability" subtype="spell-like" name="veil" group="psionics" frequency="at will"/>
	<Set type="SpecialAbility" name="slime"/>
	<Set type="SpecialAbility" name="mucus cloud"/>
	<Increase type="Ability" name="strength">16</Increase>
	<Increase type="Ability" name="dexterity">2</Increase>
	<Increase type="Ability" name="constitution">10</Increase>
	<Increase type="Ability" name="intelligence">4</Increase>
	<Increase type="Ability" name="wisdom">6</Increase>
	<Increase type="Ability" name="charisma">6</Increase>
	<Choice type="Skill">44</Choice>
    </Level>
  </Levels>
</Class>

<Character>
	<Bio>
		<Name>aboleth</Name>
		<Race>aboleth</Race>
	</Bio>
	<Levels>
	<Level number="0">
		<Set type="Ability" name="strength">10</Set>
		<Set type="Ability" name="dexterity">10</Set>
		<Set type="Ability" name="constitution">10</Set>
		<Set type="Ability" name="intelligence">11</Set>
		<Set type="Ability" name="wisdom">11</Set>
		<Set type="Ability" name="charisma">11</Set>

		<Add type="Environment">Underground</Add>
		<Add type="Organization" name="solitary"/>
		<Add type="Organization" name="brood">2-4</Add>
		<Add type="Organization" name="slaver brood">1d3+1</Add>
		<Add type="Organization" name="slaver brood" subname="skum">7-12</Add>
		<Set type="ChallengeRating">7</Set>
		<Set type="Treasure" name="standard">2</Set>
		<Set type="Alignment" name="lawful" subname="evil">usually</Set>
		<Add type="Advancement" name="huge">9-16</Add>
		<Add type="Advancement" name="gargantuan">17-24</Add>
		<Add type="LevelAdjustment">-</Add>

		<Add type="Description">...</Add>
		<Add type="Combat">...</Add>
	</Level>
	<Level number="1" class="aboleth">
		<Increase type="Skill" name="concentration">11</Increase>
		<Increase type="Skill" name="knowledge" subname="any one">11</Increase>
		<Increase type="Skill" name="listen">11</Increase>
		<Increase type="Skill" name="spot">11</Increase>
		<Add type="Feat" name="alertness"/>
		<Add type="Feat" name="combat casting"/>
		<Add type="Feat" name="iron will"/>
	</Level>
	</Levels>
</Character>
for a final character of
<Character xmlns:xmld20="http://crwth.org/2006/xmld20">
   <Environment>Underground</Environment>
   <Organization name="solitary"/>
   <Organization name="brood">2-4</Organization>
   <Organization name="slaver brood">1d3+1</Organization>
   <Organization name="slaver brood" subname="skum">7-12</Organization>
   <ChallengeRating>7</ChallengeRating>
   <Treasure name="standard">2</Treasure>
   <Alignment name="lawful" subname="evil">usually</Alignment>
   <Advancement name="huge">9-16</Advancement>
   <Advancement name="gargantuan">17-24</Advancement>
   <LevelAdjustment>-</LevelAdjustment>
   <Description>...</Description>
   <Combat>...</Description>
   <Speed name="walk">10</Speed>
   <Speed name="swim">60</Speed>
   <Size>2</Size>
   <Type>aberration</Type>
   <Subtype>aquatic</Subtype>
   <Language>aboleth</Language>
   <Language>undercommon</Language>
   <Language>aquan</Language>
   <Space>15</Space>
   <Reach>10</Reach>
   <Skill name="concentration">11</Skill>
   <Skill name="knowledge" subname="any one">11</Skill>
   <Skill name="listen">11</Skill>
   <Skill name="spot">11</Skill>
   <Feat name="alertness"/>
   <Feat name="combat casting"/>
   <Feat name="iron will"/>
   <HD subtype="aboleth">8</HD>
   <SavingThrow name="fortitude">2</SavingThrow>
   <SavingThrow name="reflex">2</SavingThrow>
   <SavingThrow name="will">6</SavingThrow>
   <AC name="natural">7</AC>
   <Weapon subtype="aboleth" name="tentacle" super="tentacle"/>
   <Attack weapon="tentacle"/>
   <FullAttack weapon="tentacle">4</FullAttack>
   <SpecialAbility name="enslave"/>
   <SpecialAbility name="psionics" group="psionics" level="16" class="wizard" ability="charisma"/>
   <Ability subtype="spell-like" name="hypnotic pattern" group="psionics" frequency="at will"/>
   <Ability subtype="spell-like" name="illusory wall" group="psionics" frequency="at will"/>
   <Ability subtype="spell-like" name="mirage arcana" group="psionics" frequency="at will"/>
   <Ability subtype="spell-like" name="persistent image" group="psionics" frequency="at will"/>
   <Ability subtype="spell-like" name="programmed image" group="psionics" frequency="at will"/>
   <Ability subtype="spell-like" name="project image" group="psionics" frequency="at will"/>
   <Ability subtype="spell-like" name="veil" group="psionics" frequency="at will"/>
   <SpecialAbility name="slime"/>
   <SpecialAbility name="mucus cloud"/>
   <Ability name="strength">26</Ability>
   <Ability name="dexterity">12</Ability>
   <Ability name="constitution">20</Ability>
   <Ability name="intelligence">15</Ability>
   <Ability name="wisdom">17</Ability>
   <Ability name="charisma">17</Ability>
   <Level subtype="class" name="aboleth">1</Level>
</Character>

Off to code I go!
<--Status ^--xmld20--^ Coding-->
©2002-2017 Wayne Pearson