Biblyon the Great

This zine is dedicated to articles about the fantasy role-playing game Gods & Monsters, and other random musings.

Gods & Monsters Fantasy Role-Playing

Beyond here lie dragons

Daredevils NPC generator

Jerry Stratton, June 16, 2021

Daredevils cover

I ran a fun game of Daredevils at North Texas at the beginning of the month. I set it in 1976 in the Kolchak: The Night Stalker television series universe. I can’t take credit for how great the game was—it was the great characterization by the players of their Kolchak television series characters that made it a high point of the con for me.

But the preparation of those characters was all on me, and I pregenerated a lot of characters for them to choose from. The pregens were various sources Kolchak had used over the series. Everyone from Lila Morton (the bereaved widow from Chopper) to Charles Rolling Thunder (the aged shaman from Bad Medicine), as well as each of Carl’s colleagues at Independent News Service.

This meant a lot of character sheets and it also meant adjusting skills a lot as I attempted to make each character unique and useful to the adventure. I continued adjusting each character as I slowly went through viewing the season again—rewatching Kolchak is never a chore—and added not just stats but quotes and background.

Doing all this rewriting by hand is a recipe for disaster. So I wrote a script (Zip file, 16.8 KB) to:

  • calculate each character’s calculated stats;
  • handle old age for the older characters;
  • verify each character’s attribute and development totals;
  • keep the format of the character sheets standard.

By using a script to calculate the calculated statistics, I ensure that no character has a mistake.1 And when I wanted to add a new calculated stat, I just had to add it to the script and re-run the script on the characters to give everyone that new calculation.

In a game like Daredevils, that’s useful, because while the rulebook lists a few official calculated statistics in the character creation section, there are also unofficial calculations scattered throughout the book. These are calculations that are technically not character stats but for all practical purposes are character stats. It’s nice to give them to the players to see.

When I decided to add the Healing Rate calculation—not technically a stat—I added its calculation to the script, and it added that line to all of the character files.

When I decided to add the optional “Luck” rule, I added Luck to the script, and it added Luck to all of the character files.

Old age is also an interesting rule in Daredevils. Newly-created Daredevils characters get more development opportunities the older they are. To offset this, really old characters have penalties applied to physical stats and, later, to mental stats. When any stat drops to zero, the character dies.2 The script handles that, checking the character’s age and then reducing the appropriate attributes.3 There are a lot of older characters in the Kolchak series compared to modern series.

All of the characters I used, even Kolchak, conformed to the Daredevils character creation rules. To make it easier to use a script to generate character sheets, I also assumed that all rolls were averages. Daredevils uses an interesting combination of die rolls and point allocation for character creation. But I didn’t want characters getting new die rolls each time I ran the program. I wanted their stats to be stable so that I could make changes to them without the reason for those changes being obviated by the dice the next time I ran the script.

For each character, I kept a text file of my notes on the character, including who the actor was, what age the character was4, their attributes and skills, and so on.

Each of my character files starts out with the basic attributes and talents, as well as some background information about the character:

  • # Charles Rolling Thunder
  • Player: Victor Jory
  • Age: 74
  • Nationality: American
  • Career: Shaman
  • Episode: Bad Medicine
  • Height: 6’2”
  • ## Attributes
  • Wit: 13
  • Will: 13
  • Strength: 13
  • Deftness: 12
  • Speed: 12
  • Health: 12
  • ## Talents
  • Charismatic: 3
  • Combative: 0
  • Communicative: 2
  • Esthetic: 0
  • Mechanical: -2
  • Natural: 1
  • Scientific: -2

The script ensures that important information, such as the age, nationality, and “player” name, is in the file, so I don’t forget to include it on the character sheet. It also ensures that the initial attribute total adds up to 75 and that the initial talent total ranges from -2 to 3.5

Next, I kept a “development” section for each character:

  • ## Development
  • ### Attributes
  • Wit: 10
  • Will: 10
  • Deftness: 2
  • Strength: 2
  • Speed: 2
  • Health: 2
  • ### Skills
  • Hunting: 1
  • Stealth: 1
  • Survival: 3
  • Throwing: 1
  • Tracking: 2
  • North Amerindian History: 6
  • Ojibwe Language: 5
  • Occult Studies: 7
  • North Amerindian Culture: 7
  • Bow, Archaic: 1
  • Knife: 1
  • ### Talents
  • Communicative: 5
  • Natural: 9
  • Esthetic: 5
  • Combative: 2
  • Charismatic: 2
  • Scientific: 4

Here, the number of adds to attributes and to skills must equal the character’s age. The number of adds to talents must equal 27: the average of 20+2d6. While it’s not quite Daredevils rules, I assume that each add to attributes is d3, that is, 2; strictly by the rules, players don’t assign adds to individual attributes, they decide on a number of adds to all attributes, roll d3 for each add, and then allocate the resulting points. But assuming that each add applies to a particular Attribute makes adjustments easier while statting up each character, because it means a 1-to-1 correspondence between the Attribute bonus and the character’s age.

Skills take one add to get the skill at the base score, and then one add for each increase beyond the base score; each add increases a skill by 7 (the average of 2d6). For Charles Rolling Thunder, for example, he has Survival and then three adds to Survival, for a total of 56. Survival is made up of the Health and Wit attributes, and the Natural talent. Charles’s initial Health and Wit are 12 and 13; his Natural talent is 10. That makes a base of 35, plus 3 times 7 (21) equals 56.

As you can see, there is a lot of room to make mistakes if I were to do the calculations for all the skills of nineteen characters by hand!

Besides doing the calculations, the script makes sure that skill scores do not exceed 100, attributes do not exceed 40, and talents do not exceed 20.

Finally, I also provided some background items and quotes for each character:

  • ## Background
  • Career: Charles Rolling Thunder is a shaman of the Ojibwe tribe. He is also a World War II veteran.
  • Harrow Spirits: The player can make a Will AST against the Will AST of an undead creature or evil spirit to send that creature away. If the PC wins, the creature will not come closer than the combined Effect Numbers, in meters. If the creature wins, the character takes subdual damage equal to the creature’s Will Effect Die. If the player wins and rolls less than or equal their Group Number, the target will also take subdual damage equal to the character’s Will Effect Die. Two characters with this power can work together to combine their Effect Numbers (if both succeed) and their damage done (if both succeed at the higher level).
  • ## Quotes
  • “It is important, Mr. Kolchak, that we talk.”
  • “If you are telling the truth, you should be armed.”
  • “It is both, and neither.”
  • “Evil is all he really is.”
  • “No one knows that.”

The script collects the background items but otherwise mostly doesn’t use them. The exception is for characters with Heightened Attribute Use. The attribute that is heightened gets better Attribute Saving Throws and Critical Saving Throws, and the script recognizes that and redoes the calculations.

Heightened Attribute Use: Deftness. Attribute Saving Throw and Critical Saving Throw values are both increased. The values on the front page reflect the increase.

The script takes my character notes, does its calculations and validations, and either tells me I did something wrong—and where I failed—or generates a simple character sheet. The files generated by the script could be used directly as character sheets; all you’d have to do is open them in a text editor and print them, or open them in a word processor and perhaps convert them to two columns, perhaps putting the background and quotes on the back page.

Like the source files, the output uses a mutated Markdown format.

What I did, however, was take the generated files and use a second script to import them into Scribus, ensuring a standard character sheet for each character. Stay tuned for part 2 if you’re interested in that.

There used to be a huge overlap between computer users and roleplayers. This is the kind of utility that would be used to automate repetitive tasks. This particular script is a little too complex to use as a detailed tutorial, but some parts of it might be instructive.

Here’s the main loop:

[toggle code]

  • #go through each file and create characters
  • for characterFile in args.character:
    • characterHandle = open(characterFile)
    • data = characterHandle.readlines()
    • characterHandle.close()
    • name, aspects, level = readNextSection(data)
    • if level != 1:
      • tools.die('Character name not found in ', characterFile)
    • character = Daredevil(name, aspects)
    • development = None
    • while (True):
      • section, values, level = readNextSection(data)
      • if level == 1:
        • tools.die('Multiple characters per file not implemented:', section)
      • if section == None:
        • break
      • if section.lower() == 'attributes':
        • character.initialAttributes(values)
      • elif section.lower() == 'talents':
        • character.initialTalents(values)
      • elif section.lower() == 'development':
        • development = values
      • elif section.lower() == 'background':
        • character.background = values
      • elif section.lower() == 'quotes':
        • character.quotes = values
      • else:
        • tools.die('Unknown section found', section)
    • if development:
      • character.initialDevelopment(development)
    • else:
      • tools.die(character, 'requires development')
    • character.showAspects()
    • character.showAttributes()
    • character.showTalents()
    • character.showCalculations()
    • character.showSkills()
    • character.showBackground()
    • character.showQuotes()

The script loops through every file given and instantiates6 a Daredevils character based on what it finds in the top-level section—or dies with an error if it doesn’t find a top-level section. It then loops through each subsection, collecting the initial attributes and talents, the development allocations, the background, and the quotes.

Once it’s looped through all of the sections, it calculates the initial development allocations and shows each section of the character sheet.

Then it starts over again on the next character file. Here’s how I’d create the character sheet for Dr. Agnes Temple:

  • daredevils Agnes\ Temple.txt > ../data/Agnes\ Temple.txt

While working through the character and creating the source file, I might just look at the results directly in the Terminal, using the aligntabs script from 42 Astounding Scripts.

  • daredevils Agnes\ Temple.txt | aligntabs

Here’s an example of how the script adds a new skill to a character:

[toggle code]

  • def addSkill(self, skill, multiplier=1):
  • parts = tools.getSkillParts(skill)
  • initialValue = 0
  • for part in parts:
    • if part in attributeNames:
      • initialValue += self.traits['attributes'][part]
    • elif part in talentNames:
      • initialValue += self.traits['talents'][part]
    • else:
      • tools.die(part, 'is neither an attribute nor a talent')
  • self.skills[skill] = initialValue*multiplier

At the top of the file is a list of skills, and the attributes and talents that contribute to a character’s initial score in that skill:

[toggle code]

  • skillNames = {
    • #practical skills
    • 'acrobat': ['deftness', 'speed', 'natural'],
    • 'climbing': ['strength', 'deftness', 'natural'],
    • 'jumping': ['deftness', 'strength', 'natural'],
  • }

The addSkill method takes the skill name, gets a list of the parts that make up the initial score, and then loops through those parts, adding their value to the initial value of the skill. It then adds that skill to the character’s skill list, possibly multiplying it by two if it’s the character’s native language.

Another simple method prints out the character’s background. This typically would go on the back of the character sheet.

[toggle code]

  • def showBackground(self):
  • if self.background:
    • tools.title('background')
    • for item in self.background:
      • print(item.title() + ':', self.background[item])
    • for skill in self.skills.keys():
      • if skill in skillNotes:
        • print(skill.title() + ':', skillNotes[skill])

This prints out a background section if the character has any background items; it then prints out any notes that are needed for particular skills, such as the linguistics skill. Linguistics allows the character to speak languages they don’t specifically know, as long as they know another language in that language family.

Each of the sections of the character sheet has a method like that; there’s a showSkills, a showQuotes, a showTalents, and so on.

Next, I’ll show how I automated pulling them into a Scribus document.

In response to Daredevils Detailed Action Time and Action Options Cube: The Fantasy Games Unlimited game Daredevils, from 1982, has a very interesting combat turn system. Plus, an Action-Option cube you can assemble yourself!

July 7, 2021: Automated Scribus Daredevils NPC character sheets
Scribus Daredevils character sheet

Scribus is great for creating RPG character sheets.

In part 1, the Daredevils NPC generator, I showed how to create simple character data in a form that makes it useful for a simple character sheet. It’s nice, however, to provide players a nice cardstock pregen with a familiar layout. I deliberately made that simple character sheet output from the daredevils script provide the data in a form that makes it easy to import into other software.

I chose to import it into Scribus, an open-source desktop publishing application that creates great PDF files and can be automated using the Python programming language. Scribus runs on macOS, Linux, and Windows.

Scribus has a Script menu; you can choose to “Execute” any Python script on your computer. I keep mine in ~/bin/Scribus, which is to say, in a folder called Scribus in a folder called bin in my macOS user account. You can put them anywhere. An obvious location would a Scribus folder in your Documents folder.

For the Kolchak game, I used a script I called daredevils.py to import the character sheets into Scribus, creating a new layer for each character. The bulk of the work is done in a class called Sheet. Here’s the start of that class:

[toggle code]

  • class Sheet:
    • def __init__(self, characterName):
      • self.skillsRect = self.getRect('skills')
      • self.backgroundItems = []
      • self.quotes = []
      • self.characterName = characterName
      • self.openSection('aspects')
      • # create the layer for this character sheet
      • scribus.gotoPage(1)
      • if characterName in scribus.getLayers():
        • scribus.setActiveLayer(characterName)
      • else:
        • scribus.createLayer(characterName)
      • self.createBox('Character', characterName)
  1. If I have a mistake in the script, of course, all of the characters have that mistake, but that makes it far more likely that I’ll catch the mistake.

  2. Weird trivia: the actress playing Emily Cowles is the oldest of the pregenerated characters. When I created Emily, it was difficult adjusting her stats to keep her alive without having made her a superwoman when she was younger. I later learned that Ruth McDevitt died in 1976, a few months before I’d set the adventure.

  3. The script also increases some attributes: at the beginning of the aging process, mental attributes get bonuses. It’s only as the character enters their seventies and beyond that mental attributes start dropping (on average).

  4. I generally assumed that the character was the same age as the actor unless this appeared to conflict with the episode. I did the same for a character’s height, though it was difficult to find reliable heights for some of the more obscure actors.

  5. That range for Talents isn’t part of the rules, it’s just what I wanted to keep them to. You can obviously adjust the range, or throw it out completely. It would make sense to limit the range at least to from -14 through +21, as those are hard limits in the rules. Seven Talents, the minimum initial talent is -2 and the maximum is +3.

  6. ”Instantiates” is a fancy word for “creating from a class”, but it’s not an RPG character class, it’s an object-oriented programming class. That is, it’s a thing that contains methods (functions) and properties (variables).