|<--Rethink, continued||^--xmld20--^||Rethink, part 4-->|
The avoral, a celestial with a progression in the Savage Species book, is a good example. At 8th level (the progression is 14 in total), the avoral gains immunity to electricity. This is not something as simple as adding
<Add type="Immunity" name="electricity"/>to its 8th-level entry, because earlier, back at 1st level, the avoral gained resistance to electricity 5. We certainly wouldn't want the end character to end up with both resistance and immunity to the same thing, so we need to know how to remove an ability completely.
We could, perhaps hard-code the fact that any time a creature gains immunity, it automatically loses its resistance, but this is a specific case, and there will certainly be others that aren't so cut-and-dry, or that shouldn't necessarily be hard-coded. We could also add an extra attribute which states an ability that's removed, but this can get complicated:
<Add type="Immunity" name="electricity" remove="Resist"/>Sure, we can assume that the Resist to remove is the one with the same name, but what if it isn't? What if we have one feat that replaces another? One ability that replaces another? What if taking a level in some class gives a feat but removes a skill?
In the end, I think we need a separate entry in the level description:
<Remove type="Resist" name="electricity"/> <Add type="Immunity" name="electricity"/>The fact that these are written as separate items isn't a problem; the act of levelling up is pretty atomic, and thus these two things are going to happen at the same time, as far as any software is concerned.
Let's assume that we have a character, such as the avoral, who after her monster progression, ends up with acid resistance 10. Perhaps we find a prestige class called an Acid Master, that the avoral wishes to take. One of the things that the Acid Master gains at 1st level is acid resistance 5. How do we handle this?
We can't have
<Add type="Resist" name="acid">5</Add>because that would give us two Resist entries for acid, and in the future, we wouldn't know which one to increase. We can't have
<Increase type="Resist" name="acid">5<because that would give the character acid resistance 15, which isn't right -- it should just be the better of the two.
One solution is to differentiate the two entries AND to have any code that cares about them to know how to deal with that. Having two entries means that we need to know which one to increase later (if, say, this Acid Master gains more resistance at a later level), and which one to eventually remove when the Acid Master gains immunity. Thus we might have
<Add type="Resist" subtype="avoral" name="acid">10</Add>and
<Add type="Resist" subtype="acid master" name="acid">5</Add>in the two different class progressions. Alternately, we might instead have
<UpTo type="Resist" name="acid">10</UpTo>and
<UpTo type="Resist" name="acid">5</UpTo>Where this UpTo element says "set this value to this number, unless it's already higher, in which case leave it." The benefit of this approach is that if any class provides Immunity later on, the Remove element will peg it properly... but what if a class later wants to add acid resistance to a character that already has immunity? Do we need something like
<Add type="Resist" name="acid">5 <Unless type="Immune" name="acid"/> </Add>It's at this point that we start seeing the issues that the d20xml people touched on, where you might need to provide a full language in your data. Can this be avoided?
If a rogue already has uncanny dodge from a different class (a rogue with at least two levels of barbarian, for example), she auto- matically gains improved uncanny dodge instead.
Even better is the text in Improved Uncanny Dodge:
If a character already has uncanny dodge from a second class, the character automatically gains improved uncanny dodge instead, and the levels from the classes that grant uncanny dodge stack to determine the minimum rogue level requires to flank the character.This leads to two different issues. Let's tackle the upgrade one first.
At the rogue's 4th level, or the shadowdancer's 2nd level, we would like to simply say
<Add type="SpecialAbility" name="uncanny dodge"/>but of course this doesn't represent the first rule above that states it should be improved uncanny dodge if uncanny dodge already exists. We could try something complex, like
<If type="SpecialAbility" name="uncanny dodge"> <Remove type="SpecialAbility" name="uncanny dodge"/> <Add type="SpecialAbility" name="improved uncanny dodge"/> <Else> <Add type="SpecialAbility" name="uncanny dodge"/> </Else> </If>But that gets us into even more code in data. And it's quite ugly. Powerful, but ugly. Instead, we could treat the progression of uncanny dodge in a numerical way, where every class simply "adds one" to the current level of uncanny dodge:
<Increase type="SpecialAbility" name="uncanny dodge">1</Increase>In this case, "1" would mean that they have uncanny dodge, and "2" would mean that they have improved uncanny dodge. The one problem is that if you took levels in barbarian, rogue and shadowdancer, you could end up with a "3", which doesn't mean anything -- but the code could be smart enough to know that anything "off the chart" just means the maximum.
And a chart is exactly what we want. Instead of having to hard code the translation of "1"->"uncanny dodge" and "2+"->"improved uncanny dodge", we'd rather have a table that kept track of this. While uncanny dodge is a known ability that we could statically code, there could be a new ability that gets added to a few different classes in the future, which wouldn't exist in our code. Instead, if we provide a lookup table, these tables can be added AS DATA instead of AS CODE. The code just needs to know to go look in the tables for a given ability to see if there's a lookup to be done, instead of the number meaning exactly that -- a number.
<Lookup name="uncanny dodge"> <Entry value="1">uncanny dodge</Entry> <Entry value="2">improved uncanny dodge</Entry> </Lookup>The names Lookup and Entry were just made up on the spot, and might be better as something else. But this idea could be used for all abilities, to find the true value that should be used. Something like "sneak attack", which is also an ability, would not have an associated Lookup table, and thus the code would use its numerical value as only that.
<Remove type="size"/> <Add type="size">Larg</Add>for the 10th level of a belker, we should realize that the table isn't really saying "a belker becomes large at 10th level", but "a belker gets one size category bigger at 10th level". While the difference may not seem important for a monster progresssion, I'm sure there are prestige classes out there that increase size (I'm pretty sure one or more of the druid-based prestige classes might do this). In general, then, we just want the Increase the size instead of set it to a specific value:
<Increase type="size">1</Increase>with an accompanying table:
<Lookup name="size"> <Entry value="-4">fine</Entry> <Entry value="-3">diminuitive</Entry> <Entry value="-2">tiny</Entry> <Entry value="-1">small</Entry> <Entry value="0">medium</Entry> <Entry value="1">large</Entry> <Entry value="2">huge</Entry> <Entry value="3">gargantuan</Entry> <Entry value="4">colossal</Entry> </Lookup>(Note I used 0 as medium, as a "base" size for creatures in the game -- it aids in future calculations based on weapon size, handedness, etc.)
Using this method for size also helps for in-game utilities, such as software that recalculates characters based on temporary effects, such as a Enlarge or Reduce spell cast upon them. Their effect would simply be and Increase or Decrease element, and the size (and thus corresponding AC, BAB, speed, etc.) would all be recalculated automatically.
The code can be told that when asked for a value (of any kind), to find the "best" one. This would usually mean the highest, which is easy enough to check for (especially if we use numerical values and table lookups for non-numeric ranking.) But what about DR?
Damage Resistance is always tricky to figure out, because it's usually a two-part value. There is the value representing the damage resisted, and the feature that can get around this resistance.
The best example is the barbarian, who gets DR through level progressions. If we take a pit fiend, which has DR 15/good and silver (and that and throws a whole other wrench into things), and add ten levels of barbarian, we get a creature that has both DR 15/good and silver and DR 2/—. These don't stack, and both values are important (unlike the case with resist, where the maximum value is the only one that matters).
This re-enforces the fact that we need to have a "label" for different versions of entries, so we can have multiple entries, as well as to be able to differentiate between them.
In fact, this isn't much of a problem. Instead of the code having to be told to just magically add class levels, yet only at certain levels, we can supply this also in the level up. So, as well as seeing
<Increase type="SpecialAbility" name="uncanny dodge">1</Increase>you might add
<Increase type="Level" name="uncanny dodge">2</Increase>on the second level of barbarian, but
<Increase type="Level" name="uncanny dodge">4</Increase>on the fourth level of rogue. Even if the character only gets to the first "rank" of uncanny dodge (so not improved), having this level around doesn't hurt, because no calculations are looking for it yet.
Whether the Level element is necessary as something different from an SpecialAbility, I'm not sure. That is, we could have
<Increase type="SpecialAbility" name="uncanny dodge level">4</Increase>instead of introducing a new element type. For now, I think I'll stick with the idea of Level for now, as an auxiliary value to an ability. I might change my mind later, however, if such values turn out to be no different (and awkward if treated so) than SpecialAbility elements.
I suggested the idea of adding conditionals and logic into the data, but I cringe at that -- this would probably lead me down a path of developing a miniature scripting language embedded in the data, and while that sounds nice and interesting, it is most certainly outside the scope of this project.
That being said, I've decided to rescind my idea of having Immunity removing Resist, as repeated here:
<Remove type="Resist" name="electricity"/> <Add type="Immunity" name="electricity"/>After discussing this problem with friends, I've decided that, in fact, a creature could be considered as having both of these abilities after all, and that it's up to the software to decide what this means. That is, if a character sheet should only show the immunity and not the resistance, then this character sheet code must know about immunities, and therefore can know about the resistances, and not to show those if immunity is present. Code that might be used to simulate combat will also have to understand (hopefully!) that immunity is better than resistance, and it should be checking for that first, before caring if resistance exists (and thus shouldn't care if it also exists.
Taking this leap of faith in the software thus means that we can stick to data that might keep track of multiple types of resistances (from different class sources), as mentioned before, and we can assume/expect/require that the software knows how to find the best of multiple entries, such as
<Resist subtype="avoral" name="acid">10<Resist> <Resist subtype="acid master" name="acid">5<Resist>and that eventually, after a few more levels of our acid master prestige class, might have
<Resist subtype="avoral" name="acid">10<Resist> <Resist subtype="acid master" name="acid">20<Resist> <Immunity name="acid"/>and all software should know how to handle this. If it knows Resist, it should know Immunity, and thus should know how to handle multiple or combined entries.
And that is all I shall say on that -- until it bites me in the ass.
|<--Rethink, continued||^--xmld20--^||Rethink, part 4-->|