Some more advanced characteristics, different player classes, and a CLI interface.

This commit is contained in:
Mark Wolfman
2018-03-25 23:59:48 -05:00
parent 7862d14c9d
commit d680cdc296
9 changed files with 183 additions and 56 deletions
+88 -10
View File
@@ -16,15 +16,15 @@ class Character():
name = ""
class_name = ""
player_name = ""
background = ""
level = 1
alignment = 'true neutral'
alignment = "Neutral"
race = "Human"
xp = 0
armor_class = 10
speed = 30 # In feet
# Hit points
hp_max = 10
hit_dice_num = 1
hit_dice_faces = 8
hit_dice_faces = 2
# Base stats (ability scores
strength = Stat()
dexterity = Stat()
@@ -32,6 +32,12 @@ class Character():
intelligence = Stat()
wisdom = Stat()
charisma = Stat()
# Inventory
cp = 0
sp = 0
ep = 0
gp = 0
pp = 0
def __init__(self, **attrs):
"""Takes a bunch of attrs and passes them to ``set_attrs``"""
@@ -48,10 +54,82 @@ class Character():
"""What type and how many dice to use for re-gaining hit points.
To change, set hit_dice_num and hit_dice_faces."""
return f"{self.hit_dice_num}d{self.hit_dice_faces}"
return f"{self.level}d{self.hit_dice_faces}"
@hit_dice.setter
def hit_dice(self, val):
dice = read_dice_str(val)
self.hit_dice_faces = dice.faces
self.hit_dice_num = dice.num
@property
def proficiency_bonus(self):
if self.level < 5:
prof = 2
elif 5 <= self.level < 9:
prof = 3
elif 9 <= self.level < 13:
prof = 4
elif 13 <= self.level < 17:
prof = 5
elif 17 <= self.level:
prof = 6
return prof
@property
def armor_class(self):
"""Armor class, without items."""
return 10 + self.dexterity.modifier
class Barbarian(Character):
class_name = 'Barbarian'
hit_dice_faces = 12
class Bard(Character):
class_name = 'Bard'
hit_dice_faces = 8
class Cleric(Character):
class_name = 'Cleric'
hit_dice_faces = 8
class Druid(Character):
class_name = 'Druid'
hit_dice_faces = 8
class Fighter(Character):
class_name = 'Fighter'
hit_dice_faces = 10
class Monk(Character):
class_name = 'Monk'
hit_dice_faces = 8
class Paladin(Character):
class_name = 'Paladin'
hit_dice_faces = 10
class Ranger(Character):
class_name = 'Ranger'
hit_dice_faces = 10
class Rogue(Character):
class_name = 'Rogue'
hit_dice_faces = 8
class Sorceror(Character):
class_name = 'Sorceror'
hit_dice_faces = 6
class Warlock(Character):
class_name = 'Warlock'
hit_dice_faces = 8
class Wizard(Character):
class_name = 'Wizard'
hit_dice_faces = 6
+30 -10
View File
@@ -1,12 +1,14 @@
#!/usr/bin/env python
import argparse
import importlib.util
import os
import subprocess
from fdfgen import forge_fdf
from dungeonsheets.character import Character
from dungeonsheets import character
from dungeonsheets.stats import mod_str
"""Program to take character definitions and build a PDF of the
character sheet."""
@@ -53,22 +55,31 @@ def create_fdf(character, fdfname):
('Alignment', character.alignment),
('XP', character.xp),
# Attributes
('ProfBonus', mod_str(character.proficiency_bonus)),
('STRmod', str(character.strength.value)),
('STR', character.strength.modifier_string),
('STR', mod_str(character.strength.modifier)),
('DEXmod ', character.dexterity.value),
('DEX', character.dexterity.modifier_string),
('DEX', mod_str(character.dexterity.modifier)),
('CONmod', character.constitution.value),
('CON', character.constitution.modifier_string),
('CON', mod_str(character.constitution.modifier)),
('INTmod', character.intelligence.value),
('INT', character.intelligence.modifier_string),
('INT', mod_str(character.intelligence.modifier)),
('WISmod', character.wisdom.value),
('WIS', character.wisdom.modifier_string),
('WIS', mod_str(character.wisdom.modifier)),
('CHamod', character.charisma.value),
('CHA', character.charisma.modifier_string),
('CHA', mod_str(character.charisma.modifier)),
('AC', character.armor_class),
('Initiative', mod_str(character.dexterity.modifier)),
('Speed', character.speed),
# Hit points
('HDTotal', character.hit_dice),
('HPMax', character.hp_max),
# Inventory
('CP', character.cp),
('SP', character.sp),
('EP', character.ep),
('GP', character.gp),
('PP', character.pp),
]
fdf = forge_fdf("", fields, [], [], [])
fdf_file = open(fdfname, "wb")
@@ -87,7 +98,9 @@ def make_sheet(character_file, flatten=False):
"""
# Create a character from the character definition
char_props = load_character_file(character_file)
char = Character(**char_props)
class_name = char_props.pop('character_class').lower().capitalize()
CharClass = getattr(character, class_name)
char = CharClass(**char_props)
# Set the fields in the FDF
fdfname = os.path.splitext(character_file)[0] + '.fdf'
create_fdf(character=char, fdfname=fdfname)
@@ -106,7 +119,14 @@ def make_sheet(character_file, flatten=False):
def main():
make_sheet('examples/rogue.py')
# Prepare an argument parser
parser = argparse.ArgumentParser(
description='Prepare Dungeons and Dragons character sheets as PDFs')
parser.add_argument('filename', type=str, help="Python file with character definition")
parser.add_argument('--flatten', '-F', action="store_true", help="Remove the PDF fields once processed.")
args = parser.parse_args()
# Process the requested file
make_sheet(character_file=args.filename, flatten=args.flatten)
if __name__ == '__main__':
+11 -14
View File
@@ -1,5 +1,16 @@
import math
def mod_str(modifier):
"""Converts a modifier to a string, eg 2 -> '+2'."""
if modifier > 0:
mod_str = '+' + str(modifier)
else:
mod_str = str(modifier)
return mod_str
class Stat():
value = 10
@@ -10,19 +21,5 @@ class Stat():
def modifier(self):
return math.floor((self.value - 10) / 2)
@property
def modifier_string(self):
"""Similar to ``modifier`` but as a string.
This also adds a '+' if necessary.
"""
mod = self.modifier
if mod > 0:
mod_str = '+' + str(mod)
else:
mod_str = str(mod)
return mod_str
def __set__(self, obj, val):
self.value = val
Binary file not shown.
+9 -3
View File
@@ -1,14 +1,13 @@
name = 'Mr. Stabby'
player_class = 'rogue'
character_class = 'fighter'
player_name = 'Mark'
background = "Criminal"
race = "Lightfoot halfling"
level = 3
alignment = "Neutral"
class_name = "Rogue"
xp = 1984
hp_max = 19
hit_dice = '3d10'
speed = 25
# Ability Scores
strength = 10
@@ -17,3 +16,10 @@ constitution = 12
intelligence = 13
wisdom = 9
charisma = 16
# Inventory
cp = 950
sp = 75
ep = 50
gp = 120
pp = 0
+5
View File
@@ -9,4 +9,9 @@ setup(name='dungeonsheets',
author_email='canismarko@gmail.com',
url='',
packages=['dungeonsheets'],
entry_points={
'console_scripts': [
'makesheets = dungeonsheets.make_sheets:main'
]
},
)
+24 -5
View File
@@ -13,15 +13,34 @@ class TestCharacter(TestCase):
def test_hit_dice(self):
# Test the getter
char = Character()
char.level = 2
char.hit_dice_faces = 10
char.hit_dice_num = 2
self.assertEqual(char.hit_dice, '2d10')
# Test the setter
char.hit_dice = '3d12'
self.assertEqual(char.hit_dice_faces, 12)
self.assertEqual(char.hit_dice_num, 3)
def test_set_attrs(self):
char = Character()
char.set_attrs(name='Inara')
self.assertEqual(char.name, 'Inara')
def test_proficiency_bonus(self):
char = Character()
char.level = 1
self.assertEqual(char.proficiency_bonus, 2)
char.level = 4
self.assertEqual(char.proficiency_bonus, 2)
char.level = 5
self.assertEqual(char.proficiency_bonus, 3)
char.level = 8
self.assertEqual(char.proficiency_bonus, 3)
char.level = 9
self.assertEqual(char.proficiency_bonus, 4)
char.level = 12
self.assertEqual(char.proficiency_bonus, 4)
char.level = 13
self.assertEqual(char.proficiency_bonus, 5)
char.level = 16
self.assertEqual(char.proficiency_bonus, 5)
char.level = 17
self.assertEqual(char.proficiency_bonus, 6)
char.level = 20
self.assertEqual(char.proficiency_bonus, 6)
+7 -1
View File
@@ -3,14 +3,20 @@ import os
from dungeonsheets import make_sheets, character
EG_DIR = os.path.abspath(os.path.join(os.path.split(__file__)[0], '../examples/'))
CHARFILE = os.path.join(EG_DIR, 'rogue.py')
class CharacterFileTestCase(unittest.TestCase):
def test_load_character_file(self):
charfile = 'examples/rogue.py'
charfile = CHARFILE
result = make_sheets.load_character_file(charfile)
self.assertEqual(result['strength'], 10)
class FDFTestCase(unittest.TestCase):
def tearDown(self):
if os.path.exists('temp.fdf'):
os.remove('temp.fdf')
def test_create_fdf(self):
fdfname = 'temp.fdf'
char = character.Character()
+5 -9
View File
@@ -4,6 +4,11 @@ from dungeonsheets import stats
class TestStats(TestCase):
def test_mod_str(self):
self.assertEqual(stats.mod_str(-3), '-3')
self.assertEqual(stats.mod_str(0), '0')
self.assertEqual(stats.mod_str(2), '+2')
def test_modifier(self):
ranges = [
((1,), -5),
@@ -31,15 +36,6 @@ class TestStats(TestCase):
msg = f"Stat {value} doesn't produce modifier {target} ({stat.modifier})"
self.assertEqual(stat.modifier, target, msg)
def test_modfifier_string(self):
stat = stats.Stat()
stat.value = 5
self.assertEqual(stat.modifier_string, '-3')
stat.value = 10
self.assertEqual(stat.modifier_string, '0')
stat.value = 15
self.assertEqual(stat.modifier_string, '+2')
def test_setter(self):
"""Verify that this class works as a data descriptor."""
# Set up a dummy class