I've made previous mention to the d20xml group, and their discussions on code versus data, and how to represent classes and such, as we're doing here. The discussion eventually brought up one comment, that if a system can handle the Dragon Disciple prestige class, then it can handle anything. I just tried coding the Dragon Disciple, and guess what: yep, the current xmld20 system couldn't handle it.
There are a couple of issues that make this prestige class a problem. Some might say that this prestige class is an odd one out, and that the expectations of a system to support it are beyond what is required for a "regular" d20 system. I think this is the easy way out, though, because while many of the classes and races are easily codeable with the current system, I don't know for sure that all of the other existing ones, or future ones, will be as easy. The Dragon Disciple may not be a one-off.
So, what are the problems with the Dragon Disciple? The natural armor increases are easily handled (but see below). The Ability boosts are also handleable by the current system. The claws and bite attacks might have been something tricky to some, but the way that we handled the weapons made this easy enough. The breath weapon is just another special ability. The blindsense we've seen before.
The wings are tricky. Instead of being a fixed speed of flight, or an increase in flight, it's described as being equal to the current ground speed. We don't have any notation that handles this directly, though we could do something like this:
<If type="Speed" name="walk" content="20"> <Add type="Speed" name="fly" value="average">20</Add> </If> <If type="Speed" name="walk" content="30"> <Add type="Speed" name="fly" value="average">30</Add> </If> <If type="Speed" name="walk" content="40"> <Add type="Speed" name="fly" value="average">40</Add> </If> <If type="Speed" name="walk" content="50"> <Add type="Speed" name="fly" value="average">50</Add> </If> <If type="Speed" name="walk" content="60"> <Add type="Speed" name="fly" value="average">60</Add> </If>And so on. This would work, assuming we can guess/know every possible value. Yuck, but doable. The real problem is in the final level of the class, the dragon apotheosis. At this point, the character takes on the half-dragon template. What does this mean? It means changing the character's Type - no problem. Add some more strength and charisma, no problem. The wing speed as mentioned above becomes double the walk speed, so now we'd need another big set of Ifs like above. Still doable! Some immunities are gained, some low-light and darkvision. According to the template, we also need to change the racial HD "one level up". That means d4 goes to d6, d6 to d8, etc. This could be done with a bunch of Ifs like before, except: what do we "If"?
Things that you see in template include hitdice changes, ability changes, skill points changes, size changes... these are all things that are added or increased at one or more levels in a monster progression. How do we decide where a template's increases and additions get added to a given monster class? And what about when we add a template "late", such as the Dragon Disciple prestige class does, or a non-inherited template?
And what about the phrasing of "fly speed is twice walk speed" or "increase HD by one size"? These all require a bunch of coding that we don't really support, and in the case of the HD increase, this means a way to search for the "racial" HD is needed; but in our case, this is a class HD like any other that we might have -- that is, if an ogre bard/dragon disciple hits the final level, we need to modify, retroactively, the ogre hitdice, not the bard and dragon disciple dice -- as well as skill points... I think. The Dragon Disciple class says that the disciple "takes on the template", but then describes some things that happens because of that, but not all that are listed in the template itself, such as the HD change or the skill point change. Does that mean this doesn't need to happen for the Dragon Disciple, but only "proper" half-dragons?
Assuming that the Dragon Disciple really means that you fully inherit the template, then it all comes down to templates in general. And the two biggest problems we've seen, so far, were revealed by the half-dragon template, namely the HD increase and the fly speed calculation. The fly increase means that we have to support calculation, but it's not clear yet how much we need to support. Will addition (and maybe subtraction) be enough? Do we need multiplication? Perhaps we should look at a few more templates to find out... right now, I'm thinking of trying something like this
<Set type="Speed" name="fly"> <Speed name="walk"/> <Speed name="walk"/> </Set>where the element contents of a Set/Add/Increase element imply a lookup and an addition. The problem is that the description on the half-dragon, for instance, says that the fly speed is twice walk speed, but doesn't say whether or not if that's set at the time of template acquisition, ignoring any increases in walk speed later in this creature's advancement, or if it always keeps pace. One could argue that some speed increases are supernatural, such as the monk's speed, and I think it makes sense that fly speed wouldn't keep pace in that case, but what about speed increases from size changes? To support that, the fly speed would be best served recorded as a calculation all the time. Hrm.
And back to the HD problem. Here's a glimpse at that ogre bard/dragon disciple:
<HD subtype="ogre">4</HD> <HD subtype="bard">5</HD> <HD subtype="dragon disciple">9</HD>This character is a 20th-level character (ECL +2 on the ogre class). For that 21st level, the player/DM wants to take that Dragon Disciple to that final level, which brings on that half-dragon template. Once we take on that tenth Disciple level, the 4 ogre HD need to change from d8 to d10. Nothing above makes any of those three HD entries stand out as the racial class, so right off we have no way of knowing which one to increase. Secondly, nowhere in our result do we store the fact that the ogre HD is d8 -- we look it up later. This means that there's not something to change in the character sheet -- we have to somehow note that the HD should be upgraded whenever it's accessed -- either for display purposes or for hp calculation. Either that, or we need to start storing the HD value itself in the character sheet, instead of just the count.
I'm not sure what to do. The first part, finding the class HD that correspond to the racial HD, is easy enough. The monster classes should really have a tag saying that they're racial classes, so character creation software can prevent someone from taking levels in more than one, which is strictly not allowed -- an ogre can't just decide to take a few levels of beholder because it wants a few extra eyes.
So, given that we're going to add something, probably an attribute to the class entry, we still have the problem of increasing the HD. One solution is to add an attribute to the HD entry:
<HD subtype="ogre" increase="1">4</HD>The code that would replace the original entry with this one needs to do a few things: find the original one, using the racial tag we talked about, then add the increase="1" attribute. The second part would be easy enough -- we can introduce a AddAttribute or maybe an IncreaseAttribute element. For that, though, we need a way to define which element gets changed. Like this?
<IncreaseAttribute type="HD" subtype="ogre" attribute="increase">1</IncreaseAttribute>This works well in general, but the problem is that we need to be non-specific -- we need to somehow say:
<HD subtype="ogre" racial="yes">4</HD>Then, if we write
<IncreaseAttribute type="HD" racial="yes" attribute="increase">1</IncreaseAttribute>We can have the code that processes IncreaseAttribute to do a search for any element that matches all of the attributes listed, except attribute, and perform the increase.
Well that wasn't so bad. We had similar code for element matching for use with the Increase and Decrease elements, which has all now been abstracted. I also added support for SetAttribute and DecreaseAttribute, just in case. I still need to add a bit of code to take advantage of the increase attribute, but that shouldn't be too hard.
So what about the Speed issue? We still haven't decided if the fly speed should be set to match the walk speed once, or follow the walk speed as it increases. The first requires just a lookup when setting the fly speed, but the second requires a constant reference any time the fly speed is accessed. This is my current thought:
<Set type="Speed" name="fly"><Add type="Speed" name="walk"/></Set>Right now, the code that processes Set, Increase and the like all just take their content from the text() of the element. If we add some code to check for embedded elements -- something we've been trying to avoid in general -- then the code can decide to take all referenced elements, using the same matching code we just wrote for the IncreaseAttribute implementation, and add them together.
Wow, was that ever a pain. Of course, I wrote my findMatch() function to find all matches -- that would make sense. But this really threw a wrench into this latest addition, which took a couple of days (days over the holiday, so not really "full" days of effort) to track down. For now, I've modified the code to return just the "best" match, but I might go back later and change it back, and modify the calling code to determine which match, if multiple are found, to use.
So. Now I have to ask myself what the true intention of the Dragon Disciple class is -- to truly take on the half-dragon template, or to take on the features that they explicitly list in the prestige class description, which isn't quite the complete template. I think I'm going to go with what the book says, and just add the changes spelled out, and not clone the changes in the half-dragon template.
The energy immunity turned out to be easier than I expected to implement, because of the forethought mentioned above:
<SetAttribute type="Immune" attribute="name"><Add type="Breath" subtype="dragon disciple"/></SetAttribute>Here we've combined the nested value retrieval with the attribute modification support -- this is necessary because the energy type for an Immune is in the name attribute instead of the text of the element.
It's one over nine megabyte spreadsheet that does character creation. They have multiple sources supported in it. I must say, this project has taken Excel to a whole other level of ability.
It's not pretty; the colors are garish, and it behaves as you'd expect something powered by a spreadsheet to behave. It's clunky and slow. It's missing a lot of error checking. It requires Excel knowledge to add content.
But it does do something. I gave it a quick spin, to see what its take on the Dragon Disciple was. It sets wing speed to walk speed properly (I snuck a level of barbarian in there to see that it wasn't hard-coded to 30' flying speed). They also take the stance that the creature type does change, even though the prestige class description doesn't mention that. That is, it looks like they might apply the full half-dragon template instead of just adding in what the prestige class description spells out. It even noted that my character was now an ex-barbarian, because he or she strayed from the path when taking the Disciple levels.
Hero Forge is an interesting diversion, but it's certainly just a kludge of calculations. Being all in Excel, the code IS the data, IS the logic, which tells you how they got around the problems that d20xml and xmld20 have faced. As mentioned in the d20xml group, though, taking a full code approach is really a problem, although the use of an existing language that directly manipulates the data (and is the data) definitely makes things a little easier. The biggest weakness is that it's only expandable within the spreadsheet; you cannot add extra races, classes, spells or items by just copying in a new data file, as we support with xmld20.
One last comment on Hero Forge, regarding the most recent post:
RE: [Hero_Forge] High STR characters... Ok Hero Forge now supports Str scores up to 70. Is that broken enough for everyone? ;-) Robert "Dark Star" Daneri Hero Forge Development Team Even if you choose not to decide, you still have made a choice. -----Original Message----- From: Hero_Forge@yahoogroups.com [mailto:Hero_Forge@yahoogroups.com] On Behalf Of Rob Cottrell Sent: Saturday, December 23, 2006 5:26 PM To: Hero_Forge@yahoogroups.com Subject: Re: [Hero_Forge] High STR characters... I have three thoughts on this subject... 1) You're not allowed at my gaming table. :-) 2) With Epic levels included, it's reasonable to be able to break the STR 50 barrier, even without the ever-so-broken War Hulk class. 3) Instead of just expanding the table to X rows (but what about my uber-barian with a STR of X+1 !!??), maybe we should make it a bit more dynamic. The formula for calculating capacities for high STR scores is listed on PHB pg. 162, and should be pretty easy to incorporate. That way, HeroForge can support characters that are even more broken than this one. :-)As the original poster mentioned, why not change the capacity to a formula instead of a table. A table!?! My god. I suppose, though, that Excel made the generation of that table a little easier!
At 2nd level, a paladin gains a bonus equal to her Charisma bonus (if any) on all saving throws.The first problem is this is a constant ability, or rather, a constant calculation -- anything that changes the Charisma bonus will change this saving throw bonus. If we add this special ability at level 2, but then increase the paladin's charisma at level 4 and 8, then we will have a new Charisma bonus, but because we've already handled the divine grace addition, the bonus it gives isn't going to know that the Charisma bonus has changed. We also don't have any way of telling the code to come back and recalculate it. We could try to add some sort of marker that the system would look for after every addition, but we would still have to keep track of the rule that originally created the entry.
This system has been designed to basically add each entry and forget about it, so keeping a "memory" around that we might need to recalculate isn't really part of the system. However, what we can do is add the entry to a special set of values that will get looked at once everything else gets added. This way, we know that all additions to Charisma have been done, and that any calculations to be done with it will be up-to-date. This would also include any buffs and equipment, so the system could be used for a "live" character tracking system. What I've done is added a Final element that marks an ability to be done afterwards, like this:
<Final> <Add type="SpecialAbility" subtype="paladin" name="divine grace"/> </Final>The second problem is that the save bonus is not as trivial as fetching a value or two, as we can support already, like we did with the fly speed of the dragon disciple. The formula for the charisma bonus is ((Charisma idiv 2) - 5), where idiv rounds down the division. This has finally driven us to supporting not just value look-up, but actual math. I had really, really wanted to avoid this. Yes, I could add specialized support for attribute bonus lookup, and might still, but for now, I think the best plan is to support that formula -- that is, support division and subtraction of values, both values from the character and constants. Boo.
Here is the new entry for the paladin ability:
<Final> <Add type="SpecialAbility" subtype="paladin" name="divine grace"/> <Add type="SavingThrow" subtype="paladin" name="fortitude"> <Minus value="5"> <Divide value="2"> <Get type="Ability" name="charisma"/> </Divide> </Minus> </Add> <Add type="SavingThrow" subtype="paladin" name="reflex"> <Minus value="5"> <Divide value="2"> <Get type="Ability" name="charisma"/> </Divide> </Minus> </Add> <Add type="SavingThrow" subtype="paladin" name="will"> <Minus value="5"> <Divide value="2"> <Get type="Ability" name="charisma"/> </Divide> </Minus> </Add> </Final>We see that the whole thing is added at the end (though technically we could add the SpecialAbility portion at any time); then we add three SavingThrow entries, each which aren't simple entries, but formulas. It's messy, but we can see that each of them fetch the current value of Charisma (the latest values, since we're doing this Finally), then Divide by 2, and finally Minus 5 -- I used Minus instead of Subtract because I couldn't use Add, since it's used elsewhere; I went with Plus and Minus instead. I also added a Multiply in case we ever need it, which I'm sure we will.
Okay, I kinda changed my mind; I added some default Final elements that calculate the bonuses for each of the ability scores, just in case they're needed. In the case of the paladin, it's used three times for the divine favor, and once for the turn undead:
<Final> <Add type="SavingThrow" subtype="paladin" name="fortitude"> <Get type="Variable" name="charismabonus"/> </Add> <Add type="SavingThrow" subtype="paladin" name="reflex"> <Get type="Variable" name="charismabonus"/> </Add> <Add type="SavingThrow" subtype="paladin" name="will"> <Get type="Variable" name="charismabonus"/> </Add> </Final> <Final> <Set type="Level" name="turn undead" unit="/day"> <Plus value="3"> <Get type="Variable" name="charismabonus"/> </Plus> </Set> </Final>Note that I've added yet another element, Variable, as a holder for this calculation. It seemed appropriate, but it could be anything, really. Just to be clear, this is what the Final block is initialized with, even if we may never used it:
<Final> <Set type="Variable" name="strengthbonus"><Minus value="5"><Divide value="2"><Get type="Ability" name="strength"/></Divide></Minus></Set> <Set type="Variable" name="dexteritybonus"><Minus value="5"><Divide value="2"><Get type="Ability" name="dexterity"/></Divide></Minus></Set> <Set type="Variable" name="constitutionbonus"><Minus value="5"><Divide value="2"><Get type="Ability" name="constitution"/></Divide></Minus></Set> <Set type="Variable" name="intelligence"><Minus value="5"><Divide value="2"><Get type="Ability" name="intelligence"/></Divide></Minus></Set> <Set type="Variable" name="wisdombonus"><Minus value="5"><Divide value="2"><Get type="Ability" name="wisdom"/></Divide></Minus></Set> <Set type="Variable" name="charismabonus"><Minus value="5"><Divide value="2"><Get type="Ability" name="charisma"/></Divide></Minus></Set> </Final>With this new functionality, I've gone back and modified a few monster entries; the hound archon's spell resistance is really 10+character level; the astral deva is the same. The solar, however, seems to have 10+HD, so in the end it worked out well that we added this general functionality.
Here's hoping that this is the extent of the additions needed!