To get a better view of the end result, I decided to write a template that would display the class information in the standard class progression table, so I didn't necessarily have to make a character to compare to a monster entry. That is, for monster classes, I have been making monster characters that take all of their levels in that class, and the end result (if the "character" makes the right choices) should be the same as the monster manual entry.
For standard classes, though, there's no real way to know that the class progression has been entered properly, since there aren't (necessarily) example characters out there for every class at 20th level (and most certainly aren't any examples in monster stat block form).
This went surprisingly well. The build system also automatically goes out to find all of the class descriptions available and generates them all (much as it does with all of the defined characters). There are a few bits here and there that belong in a class description, but the "meat" of the class is now placed into a nice table, like this:
|Level||Base Attack Bonus||Fort Save||Ref Save||Will Save||Special||0||1st||2nd||3rd||4th||5th||6th|
|1st||+0||+0||+2||+2||Bardic music, bardic knowledge, countersong, fascinate, inspire courage||2||-||-||-||-||-||-|
|12th||+9/+4||+4||+8||+8||Song of freedom||3||3||3||3||2||-||-|
I apologize for the awkward colors -- they look better with a different background, and I just simply inserted the bard's table as-is. The "Class Features" aren't yet filled in, nor are the descriptions for each of the special abilities at the bottom, but this won't be too much work to add. The issue is whether we want wording like
At 8th level, the bard gains the Inspire greatness...Or if we want a generic description of inspire greatness (which might already be in the specialability tables). That is, do we really want two sets of text: one that is worded as you'd expect in a class table, and one as you'd expect in a monster entry?
That was a nice distraction, but there are still things that I need to attempt, to see how well they work. On the Hero Forge list, they've mentioned the gestalt classes, where you take two classes at level up and choose the best features from both. Supporting this will be interesting. I still haven't tried coding a template.
|Level||Base Attack Bonus||Fort Save||Ref Save||Will Save||Special||Spells per Day|
|1st||+0||+0||+2||+0||+1 level of cleric or wizard|
|2nd||+1||+0||+3||+0||Light blindness, light sensitivity||-|
|3rd||+2||+1||+3||+1||+2 Dex||+1 level of cleric or wizard|
There doesn't seem to be anything different between this and, say, a drow monster progression! All of the paragons in the Unearthed Arcana have three levels, and they're all for races that are "simple" and don't have any class progressions to speak of.
This brings up a discussion that nvd and I were having a few days ago, regarding the Savage Species book and the idea of monster class progressions in general. The Savage Species states that a creature (say, an ogre), must first complete its progression through its monster classes (the ogre class) before it can take any other class levels (such as barbarian). We went back and forth on the topic, each playing the devil's advocate on why or why not such a limitation should be in place.
I feel that the racial paragons are a perfect argument against it. Take the human. Until now, you would start with your human race, and then just happily take classes such as fighter, cleric, etc. But now there's a human paragon progression. If you want, you can now multiclass your regular classes with these paragon levels, freely. The justification is that you can go off and learn a trade (fighter, cleric) or just focus on being more human. Given that, it makes sense, then, that a young ogre, instead of becoming more "ogrey", decides to train as a barbarian, or a ranger. And, if this ogre chooses, he or she could focus on just becoming more ogrey for a while, thus gaining a level or two in the ogre progression. Let's look at the ogre class table:
|Level||Hit Dice||Base Attack Bonus||Fort Save||Ref Save||Will Save||Skill Points||CR||Special|
|1st||1d8||+0||+2||+0||+0||2 + Int mod||1||+3 natural armor|
|2nd||2d8||+1||+3||+0||+0||2 + Int mod||1||+2 Str, +2 Con|
|3rd||3d8||+2||+3||+1||+1||2 + Int mod||2||+4 natural armor|
|4th||3d8||+2||+3||+1||+1||--||2||+2 Str, +2 Con|
|5th||4d8||+3||+4||+1||+1||2 + Int mod||3||Large size, +2 Str, -2 Dex|
|6th||4d8||+3||+4||+1||+1||--||3||+2 Str, +5 natural armor|
So, our young adventurer ogre might start off with those listed racial traits (those are what a racial ogre gets, much like a racial halfling gets a bonus to dexterity and penalty to strength, etc.). The old SS rules state that this young ogre would have to take all six levels of the ogre class before he could do anything else; these six levels will get him to the same level as the ogre that appears in the Monster Manual. But instead, we might decide to forgo ogre tradition, and become a barbarian. After a few levels of that, adventuring around the land, he might feel a bit homesick, miss his folks, and go back to the ... well, where ever ogres live, and stick around for a bit, learning the ways of ogreness. After a few levels of that, enough to get some strength, consitution and a touch of natural armor, he goes back on the road and levels up some more barbarian levels, perhaps never to return to his ogre roots. In this way, he might never hit the Large size, gained at the 5th level of ogre, and instead makes his way through life raging against any who dare mock the short ogre.
With this approach to the paragons and monster classes, then the monsters we see in the Monster Manual are basically paragon versions of their race; that is, they're the ones that stayed at home and became "ogrey" or "astral devay" or whatever. The exception, of course, being the Monster Manual entries for halflings, elves, etc., which were written before the paragon races were introduced.
The issue comes down to the idea of XP versus the idea of age. No other creature has an age progression table. Why do dragons? If we were to make up a progression table for the black wyrmling dragon, which with 4HD and LA+3 would have seven levels, and then compare that to a table for the very young black dragon (7HD+LA+3=10 levels), would the first seven levels of the very young progression match the seven levels of the wyrmling?
The Draconomicon clears up a big problem that we faced: dragons progress by age, not by XP. The dragon classes are special in that the player cannot choose to take a level of dragon -- they have to wait until their age requires them to. Until then, they take class levels in paladin or sorcerer or whatever, but once they hit the next age, they must "spend" their next level-up on advancing as a dragon. Very interesting!
This doesn't answer the question of whether the dragon progression is one table per color or one per age category. I think, from looking at the progression tables for age, that it's safe to say that one table per color is definitely possible; the progressions you see in the age table are very regular across age categories.
For instance, for every age category, the black dragon gets +3HD, +2Str, +2 Con/Int/Cha half the time, +3 BAB, +2d4 breath weapon, etc... we do have to remember, however, that even though thie age category spans 3HD, it might span more levels, because there might be a level adjustment in there. The biggest problem I see is deciding when there should be another level adjustment.
Let's investigate this further. From the Monster Manual, the black dragon goes from ECL7 to ECL10 in one age category. That's three levels, from age 0 to 6, so two years apiece. No problem. From very young to young, the black dragon goes from ECL10 to ECL13, another three levels, in ten years, so every three years, with a little rounding at the top. From young to juvenile, though, they gain another 3HD, but also +1LA, for four levels, over 10 years. This is every 2.5 years, or perhaps every 2, then 3, then 2, then 3? The Draconomicon uses 2/3/3/2, perhaps to keep them in the "middle" of their progression for the longest time of this age category.
But what about from juvenile to young adult? The Monster Manual doesn't have the level adjustment for young adult... do we think we're so lucky as to be able to figure it out, based on the rules in Savage Species? Or do we just kinda fudge it, waving our hands and saying "oh, every X levels, or HD, we increase the LA as well"? Not an attractive idea.
I think we're going to have to look at all of the dragons as a whole, and see if the LA increases at specific times, based on the color: the white dragon goes from LA+2 to LA+6 before getting to an ECL of 20; the silver only goes from LA+4 to LA+5 by level 20. The white goes from LA+2 to LA+3 while still in wyrmling stage, where the silver gets to the very end of very young before it gets its first increase. The white is going from Tiny to Medium in that span, where the silver from Small to Medium. There are other metrics that differ, and we'll have to see if there's some logic to it all.
Unfortunately, it doesn't look like we're going to get away with a pattern or formula hidden in the dragon progressions. A quick look at the ability gains of the black dragon show that there isn't any consistency. This means that we can't automate the generation of the dragons' entries. It looks like the 55-60 levels of a black dragon on their way to Great Wyrm is going to have to be done by-hand, or at least partially so.
RPGXplorer is a commercial product, out of the UK, but you can get a 30-day trial. It comes with the classes of the SRD in there, and I'm sure the equipment and such as well. I didn't look into whether or not you could purchase other sourcebooks, or if you'd be expected to enter them yourself.
It's a workable interface, I suppose (I'm not one to talk, really, since I have no user interface at all). What I call SpecialAbilities, they call "Features". But what about the XML?
Ouch. The first file I found, AbilityScores.xml, starts like this:
<RPGXplorerConfig> <AbilityScoreDataItem> <AbilityScore>1</AbilityScore> <Modifier>-5</Modifier> <BonusLevel1Spells>0</BonusLevel1Spells> <BonusLevel2Spells>0</BonusLevel2Spells> <BonusLevel3Spells>0</BonusLevel3Spells> <BonusLevel4Spells>0</BonusLevel4Spells> <BonusLevel5Spells>0</BonusLevel5Spells> <BonusLevel6Spells>0</BonusLevel6Spells> <BonusLevel7Spells>0</BonusLevel7Spells> <BonusLevel8Spells>0</BonusLevel8Spells> <BonusLevel9Spells>0</BonusLevel9Spells> </AbilityScoreDataItem> <AbilityScoreDataItem> <AbilityScore>2</AbilityScore> <Modifier>-4</Modifier> <BonusLevel1Spells>0</BonusLevel1Spells> <BonusLevel2Spells>0</BonusLevel2Spells> <BonusLevel3Spells>0</BonusLevel3Spells> <BonusLevel4Spells>0</BonusLevel4Spells> <BonusLevel5Spells>0</BonusLevel5Spells> <BonusLevel6Spells>0</BonusLevel6Spells> <BonusLevel7Spells>0</BonusLevel7Spells> <BonusLevel8Spells>0</BonusLevel8Spells> <BonusLevel9Spells>0</BonusLevel9Spells> </AbilityScoreDataItem>etc. That's right, every single value has their modifier spelled out, and the number of bonus spells written out. It's a 1000-line XML file that could have been represented in three formulas (it does go on to have support for spell points as well). And of course, the kicker is that it only goes up to 50. As we saw back in the Hero Forge discussion, 50 wasn't enough for some people, and the Hero Forge group upped their max to 70 or so. But why have a limit at all??
The spells, well... just take a look:
<RPGXplorerObject> <Name>Sorceror 2</Name> <Type>Spell Level</Type> <ObjectGUID>07cfb07f-4e4d-46b8-9ae6-30ed84aa980e</ObjectGUID> <ParentGUID>c16853aa-6890-40df-a430-6251904a288f</ParentGUID> <HTML>Index.htm</HTML> <HTMLGUID>00000000-0000-0000-0000-000000000000</HTMLGUID> <Class FK="1f1433f6-d8f8-43b2-bb9a-7a01c9a2fe6b" reference="Name">Sorceror</Class> <Level>2</Level> <ImageFilename /> <URL /> </RPGXplorerObject> <RPGXplorerObject> <Name>Wizard 2</Name> <Type>Spell Level</Type> <ObjectGUID>3b95b02b-2f37-4052-a92a-4da880d3ee85</ObjectGUID> <ParentGUID>c16853aa-6890-40df-a430-6251904a288f</ParentGUID> <HTML>Index.htm</HTML> <HTMLGUID>00000000-0000-0000-0000-000000000000</HTMLGUID> <Class FK="98dbe2a9-745b-42df-8bbf-e0ed65976f53" reference="Name">Wizard</Class> <Level>2</Level> <ImageFilename /> <URL /> </RPGXplorerObject> <RPGXplorerObject> <Name>Acid Arrow</Name> <Type>Spell Definition</Type> <ObjectGUID>c16853aa-6890-40df-a430-6251904a288f</ObjectGUID> <ParentGUID>ddb52cd6-1f28-4c7b-8355-96952461b585</ParentGUID> <HTML>Spells\Acid arrow.htm</HTML> <HTMLGUID>00000000-0000-0000-0000-000000000000</HTMLGUID> <Description /> <School FK="b9f2e510-1d91-45b9-8a52-e3a88870217e" reference="Name">Conjuration</School> <AllowPotion /> <VerbalComponent>Yes</VerbalComponent> <SomaticComponent>Yes</SomaticComponent> <MaterialComponent>Yes</MaterialComponent> <Focus>Yes</Focus> <DivineFocus>No</DivineFocus> <Arcane>Yes</Arcane> <Divine /> <ImageFilename /> <URL /> </RPGXplorerObject>This is easiest to read from the bottom up, I guess. This is the acid arrow spell. It has a GUID, which is fine... everything's entitled to its own identifier. It has interesting tags like AllowPotion and Arcane, though I don't see why those aren't just figured out. But then look at the previous two records (which, for some reason, are all RPGXplorerObjects, like the spells). They're... "level info records" I guess you could call them. They are then tied to to acid arrow spells by the ParentGUID tag. In a sense, then, the acid arrow object has two children, which are these two "level info records". And we can also see that the acid arrow itself has a ParentGUID... but to what? It turns out there's a "Spell Definitions Folder" at the very end of the Spells.xml file.
I have to hope that the XML structure that I'm seeing is the result of some XML library, or some object library that exports to a general XML structure. I say this because XML is inherently a hierarchical data structure, and thus does not need to be supplemented by the use of ParentGUID tags.
There is one defense I can see, however, for this method. If a new class comes along that also has acid arrow as a class spell, then the RPGXplorer method allows a new "level info record" to be added from a new XML file, and it can attach itself to the old spell record. As it stands, xmld20 does have this as a problem, since at the moment, our spell record has this for acid arrow:
<Spell> <Source>PHB</Source> <Name>acid arrow</Name> <School>conjuration</School> <Subschool>creation</Subschool> <Descriptor>acid</Descriptor> <Level class="sor" level="2"/> <Level class="wiz" level="2"/> <Components> <Component>V</Component> <Component>S</Component> <Component>M</Component> <Component>F</Component> </Components> <CastingTime unit="standard" count="1"/> <Range>long</Range> <Target>One arrow of acid</Target> <Duration>1 round + 1 round per three levels</Duration> <SavingThrow>None</SavingThrow> <SR>No</SR> <Long> A magical arrow of acid springs from your hand and speeds to its target. You must succeed on a ranged touch attack to hit your target. The arrow deals 2d4 points of acid damage with no splash damage. For every three caster levels (to a maximum of 18th), the acid, unless somehow neutralized, lasts for another round, dealing another 2d4 points of damage in that round. Material Component: Powdered rhubarb leaf and an adder's stomach. Focus: A dart. </Long> <Short>Ranged touch attack; 2d4 damage for ((CASTERLEVEL/3)+1) rounds.</Short> </Spell>(Note that this was autogenerated from the d20xml version of the spell XML, so still needs a little improvement). I'll admit, though, that as it stands, if a new class has this spell in its spell list, the only way it would be properly supported, today, is to go back and edit this record. This isn't a desired solution, because the idea for this system is that you could just copy in a brand-new file, without modifying the old data, and have access to the new information.
Recall that we do support the idea of super for certain items, but this doesn't quite help us here; if we defined a new spell, say improved acid arrow, then it might very well define acid arrow as the super, and we could then add new classes, range, or whatever. But what if we needed to modify this old spell without touching the file?
The current getObject function, which is called by getSpell, first looks in the character sheet (to see if a unique item has been defined), and then goes to the source material (the XML datasets). It seems we need to not only search for an object called Spell with a name acid arrow, but also add support for an add-on object. So perhaps something like
<AddOn type="Spell" name="acid arrow"> <Level class="acd" level="1"/> </AddOn>would allow for the addition of the Acid Master prestige class (if such a think existed). The getObject function would just need to know to also search for AddOn objects, and apply them in the same way that it applies data when it encounters the super attribute. That is, it would either insert or overwrite, as needed.
This need for expansion after-the-fact does let the RPGXplorer method off the hook a little, I suppose... but I'm not going to forget that table-instead-of-function nonsense.
I have nothing nice to say about this product, however. RPGXplorer, apart from being commercial, is fine except for the underlying data representation. Hero Forge is probably the most useable package out there for free, and E-Tools is pretty slick, but fully commercial. But this PCGen is not good. Right off the bat, as a first-time user, I try running it to make a sample character. Try as I might, I could not do this -- it kept insisting I choose some source material to work with, which I have done! After a bit of that, I went looking at the data format.
Yuck. It's all a linear text-based data format. It's not of a format that I recognize, but that's not saying anything. Here's a sample (shrunk down a bit for readability):
Acid Arrow CLASSES:Sorcerer,Wizard=2 SCHOOL:Conjuration SUBSCHOOL:Creation DESCRIPTOR:Acid TYPE:Arcane COMPS:V, S, M, F CASTTIME:1 standard action RANGE:Long TARGETAREA:One arrow of acid DURATION:((CASTERLEVEL/3)+1) rounds SAVEINFO:None SPELLRES:No DESC:Ranged touch attack; 2d4 damage for ((min(CASTERLEVEL,18)/3)) rounds. SOURCEPAGE:SpellsA-B.rtf ITEM:PotionThis is all on one line, tab-delimited. The first thing I will note is that the DESC: tag sure looks an aweful lot like the Short element in my own spell list, which I parsed out from the d20xml version. There was obviously some sharing going on there! I have no doubt, though, that PCGen uses the CASTERLEVEL word as a marker for calculations (much as I do in my own text descriptions), where the d20xml project never got far enough to do so.
At first glance, I suppose the PCGen format may not look so bad... I might be accused of being an XML snob, and pooh-poohing anything that isn't structured. Yes, it's easy to pull certain things out of this data using regular expressions, looking for strings like "SCHOOL:" or "COMPS:". And once we have the components, say, it's not a lot of work to then parse of the commas to get a list of them each. But let's look at different entries:
Animate Dead CLASSES:Adept,Cleric=3|Sorcerer,Wizard=4 DOMAINS:Death=3 SCHOOL:Necromancy DESCRIPTOR:Evil TYPE:Arcane.Divine COMPS:V, S, M CASTTIME:1 standard action RANGE:Touch TARGETAREA:One or more corpses touched DURATION:Instantaneous SAVEINFO:None SPELLRES:No DESC:Creates undead skeletons and zombies. SOURCEPAGE:SpellsA-B.rtf ITEM:PotionNow the CLASSES: tag is becoming some work: first you need to find the CLASSES: tag itself; then you have to find the | , if one exists, then for each item separated by | , you need to find the = sign, to find the value; and then the stuff on the left needs to be parsed further on the comma to find each class name. Why not have multiple CLASSES: entries instead? It's not because they don't support multiple instances of a tag:
CLASS:Arcane Archer PREATT:6 PREFEAT:2,Point Blank Shot,Precise Shot PREFEAT:1,Weapon Focus (Longbow),Weapon Focus (Shortbow) PRERACE:Elf%,Half-Elf PRESPELLTYPE:Arcane,1,1Two different PREFEAT: tags there; or
CLASS:Arcane Trickster PREALIGN:NG,TN,NE,CG,CN,CE PRESKILL:3,Decipher Script,Disable Device,Escape Artist=7 PRESKILL:1,Knowledge (Arcana)=4 PRESPELL:1,Mage Hand PRESPELLTYPE:Arcane,1,3 PREVARGTEQ:SneakAttack,2two PRESKILL: tags here. Granted, once the routines are written, this isn't as bad as it could be. But for the person entering in data, they must wonder: when can I use two tags, and when only one? Why is it not
PRESKILL:4,Decipher Script,Disable Device,Escape Artist=7|Knowledge (Arcana)=4And how about the Dragon Disciple? I won't paste it here, but it does have it there; it treats most abilities as Feats, like Dragon Wings and extra spells slots and such. It doesn't change the creature type, that I can tell. It supports the various colors of dragons as subclasses of the dragon disciple, which isn't so bad I suppose. I do like that they seem to support openly-defined variables, though:
1 BONUS:VAR|BreathWeaponDC|10+CL+CON BONUS:COMBAT|AC|1|TYPE=NaturalArmor.REPLACE DEFINE:BreathWeaponDC|0 ADD:FEAT(Bonus Spell Slot 1)The BONUS:VAR looks like something that's arbitrarily supported, though I suppose the "DC" on the end of the name might mean more to the system than just a name. And it does imply that there is some math going on there, which is nice to see. I still can't such much nice about this format, however.
|Level||Base Attack Bonus||Fort Save||Ref Save||Will Save||Special||0||1st||2nd||3rd||4th||5th||6th||7th||8th||9th|
|1st||+0||+2||+2||+2||Turn or rebuke undead, trapfinding, sneak attack 1d6||3||1+1||-||-||-||-||-||-||-||-|
|3rd||+2||+4||+4||+4||Sneak attack 2d6, trap sense +1||4||2+1||1+1||-||-||-||-||-||-||-|
|5th||+3||+5||+5||+5||Sneak attack 3d6||5||3+1||2+1||1+1||-||-||-||-||-||-|
|6th||+4||+6||+6||+6||Trap sense +2||5||3+1||3+1||2+1||-||-||-||-||-||-|
|7th||+5||+6||+6||+6||Sneak attack 4d6||6||4+1||3+1||2+1||1+1||-||-||-||-||-|
|8th||+6/+1||+7||+7||+7||Improved uncanny dodge||6||4+1||3+1||3+1||2+1||-||-||-||-||-|
|9th||+6/+1||+8||+8||+8||Sneak attack 5d6, trap sense +3||6||4+1||4+1||3+1||2+1||1+1||-||-||-||-|
|11th||+8/+3||+9||+9||+9||Sneak attack 6d6||6||5+1||4+1||4+1||3+1||2+1||1+1||-||-||-|
|12th||+9/+4||+10||+10||+10||Trap sense +4||6||5+1||4+1||4+1||3+1||3+1||2+1||-||-||-|
|13th||+9/+4||+10||+10||+10||Sneak attack 7d6||6||5+1||5+1||4+1||4+1||3+1||2+1||1+1||-||-|
|15th||+11/+6/+1||+12||+12||+12||Sneak attack 8d6, trap sense +5||6||5+1||5+1||5+1||4+1||4+1||3+1||2+1||1+1||-|
|17th||+12/+7/+2||+13||+13||+13||Sneak attack 9d6||6||5+1||5+1||5+1||5+1||4+1||4+1||3+1||2+1||1+1|
|18th||+13/+8/+3||+14||+14||+14||Trap sense +6||6||5+1||5+1||5+1||5+1||4+1||4+1||3+1||3+1||2+1|
|19th||+14/+9/+4||+14||+14||+14||Sneak attack 10d6||6||5+1||5+1||5+1||5+1||5+1||4+1||4+1||3+1||3+1|
And for fun, I tried combining a base class with a monster class:
|Level||Base Attack Bonus||Fort Save||Ref Save||Will Save||Special|
|1st||+0||+2||+2||+0||Trapfinding, sneak attack 1d6, +3 natural armor|
|2nd||+1||+3||+3||+0||Evasion, +2 Str, +2 Con|
|3rd||+2||+4||+4||+1||Sneak attack 2d6, trap sense +1, +4 natural armor|
|4th||+3||+4||+5||+1||Uncanny dodge, +2 Str, +2 Con|
|5th||+4||+5||+5||+1||Sneak attack 3d6, Large size, +2 Str, -2 Dex|
|6th||+5||+6||+6||+2||Trap sense +2, +2 Str, +5 natural armor|
This is a "rogue ogre", if you couldn't decipher the class name. Note that this progression stops at 6 levels, because the ogre monster class does. After that, you'd have to gestalt with something else.
This is not the best way to develop such a large system, to be sure, and I'm quite aware of that. In my defense, it's a lot easier to get something working *now*, when you have limited time to work on a project, than it is to start in a "top-down" manner and code all of the rules and formulas right off the bat.
The fact that the current version of xmld20 is the third one is because of the shortsightedness of developing in this manner, and each subsequent version has tried to solve the problems that the older systems couldn't deal with. I believe, for the time being, that the current data format is flexible enough to handle what it needs to -- knock on wood. But, the processing code, that is, the XSL that takes the XML and spits out character sheets, stat blocks and class progression tables, has suffered.
Specifically, it's all about hard-coding. The code for figuring out how to generate an attack display is a real mess. If you've never tried coding it, you may not appreciate what goes into such a calculation. Given a creature, with a weapon, it sounds like it shouldn't be too hard to figure, right?
What's the weapon's size? Specifically, relative to the creature? Is it simple, martial or exotic? Light, one-handed or two-handed? Melee, thrown or ranged? Do they have proficiency? Do they have Weapon Focus? Weapon Finesse? Weapon Specialization? Are they wielding it in two hands, and does it matter? Does it have an enchantment on it? Is it a natural weapon? And if so, what's the size on that? Are they dual-wielding? Do they have Two-Weapon Fighting? Are they triple-wielding? Do they have Multi-Weapon Fighting?
Scared yet? Even if you take into account all of the issues I've listed above, what if they've got some feat that they added in from another sourcebook? And herein lies the problem with my current method: I've hard-coded the checks for feats and proficiencies. I may support Weapon Focus right now, but what about Superior Weapon Focus or Epic Weapon Focus? Nope, but even if I did, what if yet another feat adds another bonus?
The problem isn't limited to attack calculations, either. Right now, I'm embarassed to say, the saves also suffer from a similar problem. The code to check for the Iron Will feat is hard-coded -- and not only that, it's in the stat block code, not the base library! And what of Epic Iron Will (if it exists), or some other feat that gives save bonuses?