Patron-only: stage 6 prototype (Patreon)
Content
There are two files to look at this time: a Wizard and a Barbarian.
I know, I know, I said there wouldn't be another prototype release until stage 7, but things change. Specifically:
1. I rejigged the order of the tasks in stages 6 and 7
2. It's been a really long time since my last preview
3. I've got some neat stuff to show you.
And what more excuse do I need?
A lot has changed under the surface that isn't immediately evident, so to make it look like I've achieved more, I also played around with colours and styles. There's a background image (optional!), accent colours (see the little d20 icon), more subtle colours, different class icons, and a lot of fine-tuning the style. I hope you agree that it's looking pretty good.
I'm not doing an April Fool release this year, since I'd rather spend that time doing the real thing. Instead there'll be a public preview, to give non-patrons a taste of what they can expect. It'll probably look a lot like this preview, just with a few tweaks and fixes. Subsequent previews will be patron-only again.
Where I am, and what's changed
Stage 4 made the process of building a character sheet data-driven: feed in a JSON representation of what you want the character sheet to be, and the program creates an HTML document from it. The test source was one massive JSON file, 3194 lines long.
Stage 5 introduced an inversion-of-control mechanism that I call the zone composer. The idea is simple: in one source file I say "All the ancestry feats go here"; in another, I say "here's an ancestry feat". The zone composer stitches those together. The mechanism is used for skill, feats, class features, pages, any place where there's a list of things. At this point I was working on much smaller data, which is why there was nothing to preview for stage 5.
Stage 6 applies the zone composer to a real source. However, I changed my mind on stages 6 and 7: after writing the massive JSON source file for stage 4, I decided to that I needed to accelerate the task of switching to a YAML source, so I brought that up to stage 6. YAML has some advantages over JSON - it's easier to read, better suited to source control, and allows comments. The conversion was actually quite easy though, since YAML is just an alternative syntax for writing JSON. I was able to use an automated converter, and clean up the results by hand.
Next, I split the source into "units". Units are the things you select when choosing a character to build. Example of units include:
- Classes
- Ancestries
- Backgrounds
- Class specialisations: Sorcerer bloodlines, Barbarian totems etc
- Archetypes
- Feats
- Optional things like the "permission to print" page, or the "build my character" page
- The base of Pathfinder 2e
- The distinctive visual style
Note that I am NOT planning to include code for every single feat in the game. Many simply don't need it, while a few benefit from inclusion. For example, any feat that adds an animal companion or familiar will need to be included, so that I can include the relevant page.
What took so long?
When I wrote the zone composer in stage 5, I tested it against a series of simple test cases intended to demonstrate some aspect or other of the code, but those tests were missing a particularly crucial use case that turned out to be more complicated than I thought it would be.
The specific case is most easily seen in the Ancestry Feats list. Here you have:
- a zebra list (every other item is shaded)
- each item has a level marker, since they go in specific slots
- items are filled in by the zone composer if somebody picks a feat during character creation
- a placeholder blank item for when there isn't a feat picked
Add those requirements together, and you have a surprisingly complex behaviour for something that looks simple on the page. The precise interactions between those rules took me a lot longer to work out than I'd planned. The code for the zone composer grew more and more complicated as it tried to handle all these interacting cases, and that made it increasingly hard to work out what was going wrong.
The solution to my conundrum had two parts. First, I needed to refactor the monolithic zone composer code into a recursive function plus a set of elements that can each transform themselves. This is slightly more object-oriented than before, which feels nice.
Second, I needed to separate out the meaning of the parts in the YAML, so that instead of a single "list" element that handles zones and level labels and so on, I have a nested set of elements that explicitly do each thing: in the above case, a "list", an "each", a "slots" and a "zone". It makes the YAML slightly bigger, but much cleaner and easier to understand. This is basically the unix principle: a collection of small tools that each do one thing and do it well, not overloaded with features.
What's next
With the rejigged tasks, I've now basically done about 80% of what was supposed to be stages 6 and 7. Of course, it would be premature to assume stage 7 is now tiny.
As I wrote in my last post, my eyes are on stage 8, which will be when I make a packageable library out of this. That'll be a big one. In preparation for that, stage 7 will mostly consist of a reorganisation of what I've got into much tidier boxes, and a careful sanding away of rough edges. I'll be beefing up the units, moving stylesheets and assets into them alongside YAML. I'll also be separating them into two piles, core units and Pathfinder 2e units, in preparation for the (long distant) day I use this system for other games.
It's fairly common for any big enough software system to have a dark, spooky basement: some core part of it that's ugly, incomprehensible, labelled "Here be dragons", but that you can't get rid of because it's essential to other things. Messy code emerges as a natural result of exploring the solution space, trying things out, sticking with what works. Part of the reason for my 12-step plan with so many refactoring steps is to avoid that, by taking the time at each stage to keep the result as tidy as possible. I don't want to let this project accumulate cruft before it even gets released; after all, I'm going to be living with it for a long time.
Open source
This isn't just my project. I want to make it possible for other people to contribute, to add their own feats, their own classes, their own house rules, even entire games; both for their own purposes, and for the benefit of everybody.
For this to work, the format for writing units needs to be clear and straightforward enough. My old character sheets had quite a high barrier to entry - people needed to know Illustrator, Scala, Git, and a bunch of other arcane technologies, which added up to a fairly intimidating stack. The code of the new version is all JavaScript, but more importantly, the units are written in YAML, so people don't need to be programmers or visual designers to use it. I should also write a clear guide to help people contribute, and possibly some online tools to help them (which would also help me).
Things will change in the next 6 months, of course, but if you're interested in writing your own additions, take a look at the complete unit source code on GitHub to get a taste for how it'll work.