<--design2 ^--xmld20--^ testing, continued-->

xmld20 - an XML Schema for d20 gaming systems - testing

Now that we've got a start to the data format, we should try it out, by writing some XSLT and see how workable this format is.


Working with XSLT is quite interesting, and can be a bit challenging. XML processing can tend to have a recursive tendency. When in the middle of processing one bit of data, we jump off to the side and take care of something within it, and within that, etc. until we "pop" back out and finish what we were doing three or four levels before that.

While I've done a fair bit of XSLT processing, I don't know that my methodologies are proper. Nevertheless, we'll do things the way that I know for now, and who knows, I might come back and redo things if I find a "right" way in the future.

Part of the problem with my reordering of bits of data is that it affects the way that XSLT can process our data. Typically, when processing XML with XSLT, you convert one bit of data to another, and the general order either stays the same, or you might swap or move things a bit. With the Levels element that we created, holding monster levels, challenge rating and effective character level (ECL), we've put a dent in the otherwise recursive nature of XML processing. Not a problem, but things might not be as elegant as they could otherwise be.

To create the mapping from one form to another, we need to look at them side-by-side, to figure out which ideas map directly across, and which need to be handled individually. This uses the <xsl:template match> pattern. For items that are different (such as multiple items from one, or a single item merged from others), we can use <xsl:template name> pattern.

Starting at the top

Both xmld20 and d20-xml have the <Monster> tag, and one maps to the other (that is, my Monster will output one of their Monsters. This means that using template matching will work here.
<xsl:template match="Monster">
  <Name><xsl:value-of select="Name"/></Name>
  <xsl:apply-templates select="StatBlock"/>
  <xsl:apply-templates select="Description"/>
Here we've said that on a <Monster> element, we'll re-output the <Monster> tags, and within we'll put in the <Name> tag, followed by processing the next two blocks, <StatBlock> and <Description>. We can use the template method again because our <StatBlock> and <Description> also match to single items in the destination format.

Moving on to the <StatBlock>:

<xsl:template match="StatBlock">
    <xsl:call-template name="SizeAndType"/>
    <xsl:call-template name="HitDice"/>
    <xsl:call-template name="Initiative"/>
    <xsl:apply-templates select="Speeds"/>
    <xsl:apply-templates select="ArmorClass"/>
    <xsl:call-template name="BaseAttack"/>
    <xsl:apply-templates select="Attacks"/>
    <xsl:apply-templates select="FullAttacks"/>
    <xsl:call-template name="SpaceReach"/>
    <xsl:apply-templates select="SpecialAttacks"/>
    <xsl:apply-templates select="SpecialQualities"/>
    <xsl:call-template name="Saves"/>
    <xsl:apply-templates select="Abilities"/>
    <xsl:apply-templates select="Skills"/>
    <xsl:apply-templates select="Feats"/>
    <xsl:apply-templates select="Organizations"/>
    <xsl:call-template name="CR"/>
    <xsl:apply-templates select="Treasure"/>
    <xsl:apply-templates select="Alignment"/>
    <xsl:apply-templates select="Advancements"/>
    <xsl:call-template name="ECL"/>
As we can see, with a combination of <call-template> and <apply-templates>, we can produce each target entry one-by-one, based on whether there's a simple mapping from our format (which lets us use <em>apply-template</em>) or not (<em>call-template</em>). The result of all of these template calls are encased in a <StatBlock>, and that's about it. The real interesting bits are in the later templates.
<xsl:template name="SizeAndType">
  <xsl:element name="SizeAndType">
    <xsl:variable name="size"><xsl:call-template name="Size"/></xsl:variable>
    <xsl:variable name="type"><xsl:call-template name="Type"/></xsl:variable>
    <xsl:variable name="subtypes"><xsl:call-template name="Subtypes"/></xsl:variable>
    <xsl:attribute name="Size"><xsl:value-of select="$size"/></xsl:attribute>
    <xsl:attribute name="Type"><xsl:value-of select="$type"/></xsl:attribute>
    <xsl:attribute name="Subtypes"><xsl:value-of select="$subtypes"/></xsl:attribute>
    <xsl:value-of select="$size"/><xsl:text> </xsl:text><xsl:value-of select="$type"/><xsl:if test="$subtypes"> (<xsl:value-of select="$subtypes"/>)</xsl:if>
For most of these templates, we're going to simply wrap the output in the appropriate tags (like the <StatBlock> tag above), but instead of trying to cobble together the correct looking tag when an element has attributes, we can take advantage of the <xsl:element> tag and the <xsl:attribute> tag. We could use the <xsl:element> tag for all output elements, but I find that it clutters the stylesheet. It goes against consistency, but I'm sticking to it. For now.

So, we define the SizeAndType element programmatically, and before we get to the attributes, we define some variables. Why? Because it's a bit easier to read (and quicker to type) when you use the same value more than once in the template; because the target XML has these values in attributes and as element text, we'll define variables to access the duplicated value easier. We'll do this later on when values are not only output, but used in calculations.

The rest of the element is pretty straightforward -- the only logic involved is the <xsl:if>, where it decides whether any subtypes exist, and if so, to output them with the parentheses.

<xsl:template name="HitDice">
    <xsl:variable name="con"><xsl:call-template name="conbonus"/></xsl:variable>
    <xsl:variable name="dice" select="ancestor-or-self::StatBlock/Levels/Level[@class='monster']"/>
    <xsl:variable name="type"><xsl:call-template name="Type"/></xsl:variable>
    <xsl:variable name="die"><xsl:if test="$type='Aberration'">8</xsl:if></xsl:variable>
    <xsl:variable name="hpbonus" select="$con * Levels/Level[@class='monster']"/>
    <HitDice><xsl:value-of select="$dice"/><xsl:text>d</xsl:text><xsl:value-of select="$die"/>+<xsl:value-of select="$hpbonus"/> (<xsl:value-of select="floor(($dice * ($die + 1)) div 2) + $hpbonus"/>hp)</HitDice>
We're using variables here because we use these values for both output and for further computation. Note that some of the variables are simple to define (such as the "dice" variable), where they can use the select attribute to define their value. Others are more complex, either requiring further template calls (which can't be put into a select attribute) or have conditional code (like the "die" variable).

The "con" variable calls a template called "conbonus"; at the bottom of the stylesheet, I've written some macros that are like function calls that return an often-used value. The "dice" variable is straightforward (for now). The "type" variable also calls a template, not because it's difficult to compute, but because it's a value that affects a lot of things (as we'll see), and it's quite a wordy value to retrieve.

Let's look at that for a moment. In the "dice" variable, we take the long way to figure out what level of monster the aboleth is. Instead of using an XPath prefix such as ../Levels/Level, we take the "safe" approach by stepping back from whereever we are in this monster's processing to find the path. Why? Because this template is a name template, which means that it can be called from anywhere, and thus doesn't have any "context" -- this means that if we call it while processing a StatBlock (as we have right now), or a sub-element further down. This changes the path back to the information we need, and thus we can't hard-code the path to that data -- we need to take advantage of the axes within XSLT.

It may seem too wordy, but it ensures that the template call doesn't fail later on. Granted, the HitDice template is not a very good example, as we're not likely to want the HitDice element output anywhere but here, but for the name templates that are used throughout, like the "conbonus" one, this becomes very important.

Continuing through our variables, the "die" one has a conditional statement hard-coded to the Aberration type. That's kind of hokey, because there are many other types, but as a proof-of-concept, we'll just put in what we need for the aboleth -- the "die" variable would eventually become a quite complex calculation to support all Types. We won't even consider class levels for now! As we go through this first-pass of the stylesheet, we will be taking a lot of shortcuts to be specific to the aboleth, but of course will have to return to make the sheet more generalized for all creatures.

The rest of this template is straightforward, printing out numbers and a little calculation to fill in the average hitpoints, which we completely omitted from our data format.

Initiative is an element we completely omitted from our description:

<xsl:template name="Initiative">
    <xsl:variable name="dexbonus"><xsl:call-template name="dexbonus"/></xsl:variable>
    <Initiative>+<xsl:value-of select="$dexbonus"/></Initiative>
This may seem a bit wordy, as we could just call the "dexbonus" template instead of assigning it to a variable first, but I'm actually planning ahead for other modifiers to initiative (the Improved Initiative feat, for instance), where we'll need to do a bit of math.
<xsl:template match="Speeds">
    <Speed><xsl:value-of select="Speed[@type='walk']/@rate"/> ft. (<xsl:value-of select="floor(Speed[@type='walk']/@rate div 5)"/> squares)<xsl:if test="Speed[@type='swim']">, swim <xsl:value-of select="Speed[@type='swim']/@rate"/> ft.</xsl:if>
Speed is pretty easy, as it's just a bunch of transcription. We are currently hardcoding the units in, but once we have a schema, we can support default values, and we can omit this.
<xsl:template match="ArmorClass">
    <xsl:variable name="armorac" select="0"/>
    <xsl:variable name="defac" select="0"/>
    <xsl:variable name="size"><xsl:call-template name="Size"/></xsl:variable>
    <xsl:variable name="sizeac"><xsl:if test="$size='Huge'">-2</xsl:if></xsl:variable>
    <xsl:variable name="dexac"><xsl:call-template name="dexbonus"/></xsl:variable>
    <xsl:variable name="natac" select="@value"/>
    <ArmorClass><xsl:value-of select="10+$armorac+$sizeac+$dexac+$natac"/> (<xsl:value-of select="$sizeac"/> size, +<xsl:value-of select="$dexac"/> Dex, +<xsl:value-of select="$natac"/> natural), touch <xsl:value-of select="10+$sizeac+$dexac+$defac"/>, flat-footed <xsl:value-of select="10+$armorac+$sizeac+$natac"/></ArmorClass>
This ArmorClass template is pretty simple for now -- but when we start adding in armor, magic items and the like, it'll get much more involved. This is required, though, if we want to have a versatile system.
<xsl:template name="BaseAttack">
  <xsl:variable name="base"><xsl:call-template name="BAB"/></xsl:variable>
  <xsl:variable name="size"><xsl:call-template name="Size"/></xsl:variable>
  <xsl:variable name="grapsize"><xsl:if test="$size='Huge'">8</xsl:if></xsl:variable>
  <xsl:variable name="grapstr"><xsl:call-template name="strbonus"/></xsl:variable>
  <xsl:variable name="grapple" select="$base + $grapsize + $grapstr"/>
  <BaseAttackAndGrapple>+<xsl:value-of select="$base"/>/+<xsl:value-of select="$grapple"/></BaseAttackAndGrapple>
Base Attack was another entry that we completely removed from our dataset, so it being created from scratch from the data. The hard part of the base attack bonus is hidden above in a simple call to the "BAB" template. Perhaps we could have bundled the grapple calculations in a template call as well, but... I didn't.
<xsl:template match="Attacks">
  <xsl:for-each select="Attack">
  <xsl:if test="position()!=1"> or </xsl:if>
  <xsl:value-of select="@name"/> +<xsl:call-template name="MeleeAttack"/> melee (<xsl:call-template name="AttackDamage"/>)</xsl:for-each></Attack>
The Attack template is the first one that requires us to consider that we have a list of items that we need to separate with a word or punctuation. If the aboleth had more than one type of attack, it would need to code above to stick "or" between each of the possibilities. Note we make heavy use of templates to make the Attack section look simple, but they will be more involved when we look at them.
<xsl:template match="FullAttacks">
  <xsl:for-each select="FullAttack">
    <xsl:if test="position()>1">; or </xsl:if>
    <xsl:for-each select="Attack">
      <xsl:if test="position()>1"> and </xsl:if>
      <xsl:variable name="attack" select="@attack"/>
      <xsl:value-of select="@count"/><xsl:text> </xsl:text><xsl:value-of select="$attack"/>s +<xsl:call-template name="MeleeAttack"/> melee (<xsl:for-each select="ancestor-or-self::StatBlock/Attack[@name=$attack]"><xsl:call-template name="AttackDamage"/></xsl:for-each>)</xsl:for-each>
Like the Attack template above, the FullAttack template requires even more consideration. The aboleth isn't the best example of this, but if you have a creature that has natural attacks and wields weapons, there are many combinations which mix the various attacks into a FullAttack, and these are all dealt with here.
<xsl:template name="SpaceReach">
  <SpaceAndReach><xsl:value-of select="ancestor-or-self::StatBlock/Size/@space"/> ft./<xsl:value-of select="ancestor-or-self::StatBlock/Size/@reach"/> ft.</SpaceAndReach>
The SpaceReach template is pretty straightforward; it was implemented as a name template because we didn't have a separate element for the two values (having added them into the Size element).

The SpecialAttacks are pretty straightforward as well, only needing the logic to add commas into the resulting list.

<xsl:template match="SpecialAttacks">
  <xsl:for-each select="SpecialAttack">
  <xsl:if test="position()!=1">, </xsl:if>
  <xsl:value-of select="."/>
The SpecialQualities section is a bit more involved. Instead of just a list of abilities that are noted for a monster, these qualities are typically determined based on the creature's Type and SubTypee. We need to have logic that determined what, if any, SpecialQualities come from Type, and from SubType, and then list them all with any other additional Qualities that are added on.
<xsl:template match="SpecialQualities">
  <xsl:variable name="type"><xsl:call-template name="Type"/></xsl:variable>
  <xsl:variable name="implicit1">
  <xsl:if test="ancestor-or-self::StatBlock/Type/Subtype = 'Aquatic'">Aquatic subtype</xsl:if>
  <xsl:variable name="implicit2">
  <xsl:if test="$type='Aberration'">darkvision 60 ft.</xsl:if>
  <xsl:value-of select="$implicit1"/>
  <xsl:if test="$implicit1 and $implicit2">, </xsl:if>
  <xsl:value-of select="$implicit2"/>
  <xsl:for-each select="SpecialQuality">
  <xsl:if test="$implicit1 or $implicit2 or position()!=1">, </xsl:if>
  <xsl:value-of select="@quality"/>
It's quite obvious from the above that I cut corners to just make the aboleth work; the Type only works for Aberrations and the SubType only works for Aquatic. This will have to change, of course, but for now the supplied logic will do, and is pretty clear. A bit of logic, again, is added to determine whether commas are need to separate different set of figured out values.
<xsl:template name="Saves">
  <xsl:variable name="type"><xsl:call-template name="Type"/></xsl:variable>
  <xsl:variable name="fortbase"><xsl:if test="$type='Aberration'"><xsl:call-template name="PoorSave"/></xsl:if></xsl:variable>
  <xsl:variable name="refbase"><xsl:if test="$type='Aberration'"><xsl:call-template name="PoorSave"/></xsl:if></xsl:variable>
  <xsl:variable name="willbase"><xsl:if test="$type='Aberration'"><xsl:call-template name="GoodSave"/></xsl:if></xsl:variable>
  <xsl:variable name="fortbonus"><xsl:call-template name="conbonus"/></xsl:variable>
  <xsl:variable name="refbonus"><xsl:call-template name="dexbonus"/></xsl:variable>
  <xsl:variable name="willbonus"><xsl:call-template name="wisbonus"/></xsl:variable>
  <xsl:variable name="fortextra"><xsl:choose><xsl:when test="ancestor-or-self::StatBlock/Feats/Feat[@name='Great Fortitude']">2</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable>
  <xsl:variable name="refextra"><xsl:choose><xsl:when test="ancestor-or-self::StatBlock/Feats/Feat[@name='Lightning Reflexes']">2</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable>
  <xsl:variable name="willextra"><xsl:choose><xsl:when test="ancestor-or-self::StatBlock/Feats/Feat[@name='Iron Will']">2</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable>
  <xsl:variable name="fort"><xsl:value-of select="$fortbase + $fortbonus + $fortextra"/></xsl:variable>
  <xsl:variable name="ref"><xsl:value-of select="$refbase + $refbonus + $refextra"/></xsl:variable>
  <xsl:variable name="will"><xsl:value-of select="$willbase + $willbonus + $willextra"/></xsl:variable>

  <xsl:element name="Saves">
    <xsl:attribute name="Fort"><xsl:value-of select="$fort"/></xsl:attribute>
    <xsl:attribute name="Ref"><xsl:value-of select="$ref"/></xsl:attribute>
    <xsl:attribute name="Will"><xsl:value-of select="$will"/></xsl:attribute>Fort +<xsl:value-of select="$fort"/>, Ref +<xsl:value-of select="$ref"/>, Will +<xsl:value-of select="$will"/>
Wow. This template make look a bit daunting, but easily two-thirds of it is variables, finding the different values that make up each save takes a bit of work, and can make you wonder why we don't just save these values in our data instead of requiring all of this in the stylesheet.

I insist that it's worth it, though, because without this breakdown, we would have either headaches or large restrictions when writing software to understand how to advance a monster, to add templates, or to add class levels.

<xsl:template match="Abilities">
  <xsl:element name="Abilities">
    <xsl:attribute name="Str"><xsl:value-of select="@Str"/></xsl:attribute>
    <xsl:attribute name="Dex"><xsl:value-of select="@Dex"/></xsl:attribute>
    <xsl:attribute name="Con"><xsl:value-of select="@Con"/></xsl:attribute>
    <xsl:attribute name="Int"><xsl:value-of select="@Int"/></xsl:attribute>
    <xsl:attribute name="Wis"><xsl:value-of select="@Wis"/></xsl:attribute>
    <xsl:attribute name="Cha"><xsl:value-of select="@Cha"/></xsl:attribute>Str <xsl:value-of select="@Str"/>, Dex <xsl:value-of select="@Dex"/>, Con <xsl:value-of select="@Con"/>, Int <xsl:value-of select="@Int"/>, Wis <xsl:value-of select="@Wis"/>, Cha <xsl:value-of select="@Cha"/>
The Attributes template looks busier than you might expect, only because we have each of the six values both as attributes and within the element text, so their all referenced twice.
<xsl:template match="Skills">
  <xsl:for-each select="Skill">
  <xsl:if test="position()!=1">, </xsl:if>
  <xsl:value-of select="@name"/> +<xsl:call-template name="SkillPoints"/>
  <xsl:if test="ancestor-or-self::Monster/Description/Combat/RacialSkill">, </xsl:if>
  <xsl:for-each select="ancestor-or-self::Monster/Description/Combat/RacialSkill">
    <xsl:if test="position()!=1">, </xsl:if>
    <xsl:value-of select="@skill"/> +<xsl:value-of select="@bonus"/>
The Skill template does three things: it iterates through each of the skills listed; it calculates the skill total (or, rather, it calls a template to do that work); and it figures out if any RacialSkills exist, and adds them to the list as well.
<xsl:template match="Feats">
  <xsl:for-each select="Feat">
     <xsl:if test="position()!=1">, </xsl:if>
     <xsl:value-of select="@name"/>
The Feats are easier than the Skills, since there's no numerical portion, so it's a simple comma-separated list.
<xsl:template match="Organizations">
  <xsl:for-each select="Organization">
    <xsl:if test="position()!=1">, </xsl:if>
    <xsl:if test="position()=last()">or </xsl:if>
    <xsl:value-of select="@name"/><xsl:if test="@count!='1'"> (<xsl:value-of select="@count"/><xsl:if test="Extras"> plus <xsl:value-of select="Extras/@count"/><xsl:text> </xsl:text><xsl:value-of select="Extras/@name"/></xsl:if>)</xsl:if>
The Organizations section looks more complicated than in might need to be; how often do we really need access to the details of the different organizational groups, outside of pen-and-paper D&D? Who knows, but the data is there, can be separated, and really doesn't hurt to have to put back together.
<xsl:template name="CR">
  <ChallengeRating><xsl:value-of select="ancestor-or-self::StatBlock/Levels/@cr"/></ChallengeRating>
The Challenge Rating is about as simple as it gets, requiring no calculation.
<xsl:template match="Treasure">
  <xsl:if test="@multiplier=2">Double standard</xsl:if>
Even though this is specific to the aboleth (it only has the concept of double standard treasure), even when it's filled out, it's a very simple template.
<xsl:template match="Alignment">
  <xsl:if test="@flexibility"><xsl:value-of select="@flexibility"/><xsl:text> </xsl:text></xsl:if>
  <xsl:value-of select="@LawChaos"/><xsl:text> </xsl:text><xsl:value-of select="@GoodEvil"/>
We broke this one down a bit, to allow software to search for alignment-axis related effects. Building the end string is pretty obvious.

Looking at the Advancement now, we should have probably broken the range up into two values, instead of just a string, since this is a key entry for improving monsters.

<xsl:template match="Advancements">
    <xsl:for-each select="Advancement"><xsl:if test="position()!=1">; </xsl:if><xsl:value-of select="@range"/> HD (<xsl:value-of select="@size"/>)</xsl:for-each>
Oops. Flipping through the Savage Species a few minutes ago, I realized I made the same mistake I often do -- confusing ECL with level adjustment. ECL is a creature's hitdice, it's class levels and its level adjustment all added together. I suppose I should have listened to the original data source for my naming!
<xsl:template name="ECL">
  <xsl:variable name="ecl" select="ancestor-or-self::StatBlock/Levels/@ecl"/>
    <xsl:if test="$ecl = 0">-</xsl:if>
    <xsl:if test="$ecl != 0"><xsl:value-of select="$ecl"/></xsl:if>

That ends the StatBlock set of templates. The remainder is pretty simple; it's the Description block, which contains the General text (description of the monster) and the Combat section, which has the further expansion of its SpecialAbilities.
lt;xsl:template match="Description">
  <xsl:apply-templates select="General"/>
  <xsl:apply-templates select="Combat"/>
The General section is very simple -- it just dumps out the text. This is the first place we've lost information, because I didn't retain the markup in the original file.
<xsl:template match="General">
  <xsl:value-of select="."/>
The Combat section is a bit involved. It has a bit of Text, it has a list of SpecialAbilities, and it has the RacialSkills at the end.
<xsl:template match="Combat">
  <xsl:value-of select="Text"/>
  <xsl:for-each select="SpecialAbility">
  <xsl:element name="SpecialAbility">
  <xsl:attribute name="Name"><xsl:value-of select="@Name"/></xsl:attribute>
  <xsl:attribute name="Type"><xsl:value-of select="@Type"/></xsl:attribute>
  <xsl:value-of select="@Name"/> (<xsl:value-of select="@Type"/>): <xsl:value-of select="Text"/>

  <xsl:if test="RacialSkill">
  Skills: <xsl:for-each select="RacialSkill">
    <xsl:value-of select="."/>
There's nothing above that's new, if you break it down a bit.

Below are all the <xsl:template name> entries that I've been calling "macros". They're multiply-called templates that do most of the calculations required to expand the data into a useful monster entry. Note that, because these template are being used as "functions" that return values, the whitespace can be important, so the layout of the entries isn't as readable as it was before.

<!-- MACROS -->

<xsl:template name="Size"><xsl:value-of select="ancestor-or-self::StatBlock/Size/@category"/></xsl:template>

<xsl:template name="Type"><xsl:value-of select="ancestor-or-self::StatBlock/Type/@base"/></xsl:template>

<xsl:template name="Subtypes">
<xsl:for-each select="ancestor-or-self::StatBlock/Type/Subtype">
<xsl:if test="position()!=1">, </xsl:if>
<xsl:value-of select="."/>

<xsl:template name="BAB"><xsl:if test="ancestor-or-self::StatBlock/Type/@base='Aberration'"><xsl:value-of select="floor((ancestor-or-self::StatBlock/Levels/Level * 3) div 4)"/></xsl:if></xsl:template>

<xsl:template name="strbonus"><xsl:value-of select="floor((ancestor-or-self::StatBlock/Abilities/@Str - 10) div 2)"/></xsl:template>

<xsl:template name="intbonus"><xsl:value-of select="floor((ancestor-or-self::StatBlock/Abilities/@Int - 10) div 2)"/></xsl:template>

<xsl:template name="dexbonus"><xsl:value-of select="floor((ancestor-or-self::StatBlock/Abilities/@Dex - 10) div 2)"/></xsl:template>

<xsl:template name="conbonus"><xsl:value-of select="floor((ancestor-or-self::StatBlock/Abilities/@Con - 10) div 2)"/></xsl:template>

<xsl:template name="wisbonus"><xsl:value-of select="floor((ancestor-or-self::StatBlock/Abilities/@Wis - 10) div 2)"/></xsl:template>

<xsl:template name="chabonus"><xsl:value-of select="floor((ancestor-or-self::StatBlock/Abilities/@Cha - 10) div 2)"/></xsl:template>

<xsl:template name="MeleeAttack">
<xsl:variable name="bab"><xsl:call-template name="BAB"/></xsl:variable>
<xsl:variable name="strbonus"><xsl:call-template name="strbonus"/></xsl:variable>
<xsl:variable name="sizeattack"><xsl:if test="ancestor-or-self::StatBlock/Size/@category='Huge'">-2</xsl:if></xsl:variable>
<xsl:value-of select="$bab + $strbonus + $sizeattack"/>

<xsl:template name="AttackDamage">
<xsl:call-template name="NaturalDamage"/>
<xsl:if test="@extra"> plus <xsl:value-of select="@extra"/></xsl:if>

<xsl:template name="NaturalDamage"><xsl:if test="ancestor-or-self::StatBlock/Size/@category='Huge'">1d6</xsl:if>+<xsl:call-template name="strbonus"/></xsl:template>

<xsl:template name="PoorSave"><xsl:value-of select="floor(ancestor-or-self::StatBlock/Levels/Level div 3)"/></xsl:template>

<xsl:template name="GoodSave"><xsl:value-of select="floor(ancestor-or-self::StatBlock/Levels/Level div 2) + 2"/></xsl:template>

<xsl:template name="SkillPoints">
  <xsl:variable name="ability">
  <xsl:if test="@name='Concentration' or @name='Listen' or @name='Spot'"><xsl:call-template name="wisbonus"/></xsl:if>
  <xsl:if test="contains(@name,'Knowledge')"><xsl:call-template name="intbonus"/></xsl:if>
  <xsl:variable name="extra">
    <xsl:when test="ancestor-or-self::StatBlock/Feats/Feat[@name='Alertness'] and @name='Listen'">2</xsl:when>
    <xsl:when test="ancestor-or-self::StatBlock/Feats/Feat[@name='Alertness'] and @name='Spot'">2</xsl:when>
  <xsl:value-of select="$ability + @ranks + $extra"/>
This last one is probably the most "hacked" template so far, being very specific to the aboleth. The SkillPoints "function" will eventually do a large amount of work, having to check numerous Feats, Racial Abilities and the like. Synergy bonuses will also have to be considered.
<--design2 ^--xmld20--^ testing, continued-->
©2002-2018 Wayne Pearson