This time around I would like to deliver on my earlier promise to get into the details of building FindFamiliar. The site is still a long ways from ready, but I’ve shifted my focus this week more towards the actual project because I really want to get a prototype version published so I can finally have something to show for all of this work. I was hoping to make some solid gains on both this week, but I’ve lost a few days to personal issues and it has put me behind schedule. I’m hoping to make up some ground this weekend, but in the meantime, the publishing must continue! So, let’s get into it.
Overall Focus
My focus on FindFamiliar has been in two major areas: 1. Getting all of the data I need represented in database models and creating fixtures to populate the database with, and 2. Figuring out how exactly I want to implement the NPC generator and getting the code written for it.
The first of these is a solid mixture of the mundane and interesting and comes from the fact that there is a fair amount of static data that needs to represented for FindFamiliar. In essence, I need all of the details of the rulebooks available for the character generator, and there’s no good way to get it there other than manual entry for a lot of it. Granted, there are opportunities for scripting, and I’m trying to leverage these as much as I can, but the bulk of the data isn’t like that.
A good example of a scripting opportunity I took advantage of recently would be the progression of a characters base attack bonus and their saving throw bonuses. What these represent is an abstraction of a character’s martial prowess as well as their resilience to various ill-effects opponents might try to hit them with. All classes have a mixture of strength in four categories: Base Attack Bonus (BAB), as well as Fortitude, Reflex and Will saving throws. While each class has a mix and match of aptitude in these four things, each of the stats has a few progressions they follow. For BAB there is a slow, medium and fast progression, and for the saving throws there is a slow and fast progression. This means that I can write a simple script that is a lookup table of a classes progression for these four things, and then know what the bonus should be for each character class at each level, which can then be used to generate a database table containing the bonuses in these things. Nice and easy, and easily extensible. As an example, a Barbarian has a fast BAB, fast Fortitude, slow Reflex and Will. Knowing that I can crank out an entry for each of the 20 levels a character could take in Barbarian and have a nice simple lookup for each level. I’ll know that a 20th level Barbarian has a +20 BAB, +12 Fortitude, +6 Reflex and +6 Will.
A good example of the kind of thing I have to enter manually would be Feats. There are hundreds of the damned things, and they do everything from something as simple as adding static bonuses to a particular skill check to changing the way rules work. For example, metamagic feats allow characters to alter the effects of spells at the expense of making use of more of the caster’s daily spell-casting resources. Worse yet, some will add bonuses for specific circumstances, such as using a particular weapon, which would need to be accounted for on the character sheet when outfitting the NPC. It’s a PITA. But the worst thing about it is that there is no logic to it. While feats chain together, there’s no programmatic way to know what one feat will do based on it’s predecessor, at least, not in a way that you can write a script for. Short of writing a script that goes and replicates all of the details of each feat; an obviously bad idea. So instead, I get to manually fill out some of these fixtures by hand. The nice thing is that the Django Admin functionality makes it easy to fire up a test server and use that to populate the various fields for all the feats (and other models I need to create data for). But it’s a manual data entry task that is an hours long undertaking. Michael has mentioned trying to outsource it, and I’m tempted, but I also figure I’m still going to have to go through and proof-read it all, and I just don’t have a lot of confidence I’ll get decent work out of some random stranger. He could be right though, and maybe we should just cough up a little money.
At any rate, all of this is taking up a lot of time. I find it very satisfying to write a script to generate all the entries I need (I’ve got some pretty cool ones that created fixtures for weapon and armor enchantments), but am not so enthusiastic about the data entry stuff. Here’s hoping it will all be worth it some day.
On to the second part of where I’ve been putting my attention: designing the actual NPC generation. This has been an interesting puzzle to solve. It is my hope that I can write a generator that is perfectly generic, and doesn’t require a lot of if/else clauses to deal with class specific carve outs. At first glance, there is a lot of stuff that seems to be class specific. They often get bonus feats that allow them to select from specific choices, they get special abilities that other classes don’t get and which often change the way the rules function and many of them get spells, spells which have availability limited by the character class. Additionally, there are a lot of choices that all characters get to make, but which are informed by the classes they’ve taken levels in. A martial character might want to increase their strength, while a caster might go for intelligence or charisma. At first, this makes it seem like there is a ton of class specific logic, and my initial idea was to create an abstract base class that would define the API for a character generator. Then I would write a character generator for each of the classes that would inherit all of the methods of the base class and implement them with class specific logic. This seemed like a decent idea, but was going to require me to write at least 10 classes all with very similar logic, but different decisions.
I would prefer to avoid violating DRY principles here, and so instead, I’m trying to find a way to make all of my logic generic. I think I can get it to work for a large portion of the generation tool. It does involve having if/else logic to check for all of the classes, but I’ve tried to put those decisions into helper functions to obscure it some in the primary code base. The trouble still though is the class powers that accrue as a character gains levels. These are a pain, because they sometimes change bonuses, require decisions, and inform other decisions (like feat selection). At the moment I’m planning on writing a class power utility for each of the classes. Not my favorite, but there doesn’t yet seem to be a way around it. It will have the advantage of grouping all of the common decisions together in one area and making it much easier to edit / maintain in the future. It still doesn’t feel right though. I want it to be perfectly generic if possible.
That said, I feel the seed of an idea forming. I’m hoping I can write logic that will parse all of this stuff and database models that will essentially inform the decisions the logic makes based on the character class in question. I don’t have anything substantive yet, but my goal would be to be able to just add some new entries to the database to add new character classes or sourcebooks rather than have to go and edit the code later on. It should also make maintainability a lot better. Lastly, it should speed development way up if I can pull this off. I can feel like I’m getting close, but the details haven’t quite come to me yet. I’ll be sure to share once I get there.
Using Pycharm With Django
The IDE I’m using for development on this project is Pycharm, published by JetBrains. They publish a community version of the application and it’s a really nice IDE. I’d highly recommend it for anyone looking for a nice Python IDE.
One sticking point I have though, is that the community edition doesn’t really support Django. This is pretty easily gotten around by using their configuration editor to set it up though. In my case, I’m using the Django test framework, so I had to set up a configuration that would invoke ‘manage.py’ (a really useful utility script supplied by Django) and run my test suite. It’s pretty easy. Basically just make sure you’re pointing to the right working directory, you’ve told Pycharm to run manage.py and you pass the correct parameters to the script. In this case, test with the directory of the app I’m developing as the working directory. You can of course do this through the shell, but I like Pycharm’s debugging tools, and using this method lets me continue using them.
Concerns about Python & Django
One concern I have for this project is that, as Michael pointed out, this is a fairly processing heavy application compared to many web apps. We’re not just fetching news articles and displaying them for the user, as was the original use case for Django, but are doing a fair bit of decision making each time a person creates a character. I’ll have to get it all set up and under load before I can make any conclusions, but I’m worried that the app might get bogged down if a lot of people are all creating characters.
Still, that would be a good problem to have, and we can look at making changes to it if needs be. For now, I’m going to forge ahead. Hopefully soon I’ll have my database all set up and my perfectly generic logic written.