Added saving throws and skills.

This commit is contained in:
Mark Wolfman
2018-03-26 23:50:26 -05:00
parent d680cdc296
commit bbfc8ff295
7 changed files with 175 additions and 19 deletions
+30 -8
View File
@@ -2,7 +2,7 @@
import re
from .stats import Stat
from .stats import Ability, Skill
from .dice import read_dice_str
dice_re = re.compile('(\d+)d(\d+)')
@@ -25,13 +25,33 @@ class Character():
# Hit points
hp_max = 10
hit_dice_faces = 2
# Base stats (ability scores
strength = Stat()
dexterity = Stat()
constitution = Stat()
intelligence = Stat()
wisdom = Stat()
charisma = Stat()
# Base stats (ability scores)
strength = Ability()
dexterity = Ability()
constitution = Ability()
intelligence = Ability()
wisdom = Ability()
charisma = Ability()
saving_throw_proficiencies = []
# Skills
acrobatics = Skill(ability='dexterity')
animal_handling = Skill(ability='wisdom')
arcana = Skill(ability='intelligence')
athletics = Skill(ability='strength')
deception = Skill(ability='charisma')
history = Skill(ability='intelligence')
insight = Skill(ability='wisdom')
intimidation = Skill(ability='charisma')
investigation = Skill(ability='intelligence')
medicine = Skill(ability='wisdom')
nature = Skill(ability='intelligence')
perception = Skill(ability='wisdom')
performance = Skill(ability='charisma')
persuasian = Skill(ability='charisma')
religion = Skill(ability='intelligence')
sleight_of_hand = Skill(ability='dexterity')
stealth = Skill(ability='dexterity')
survival = Skill(ability='wisdom')
# Inventory
cp = 0
sp = 0
@@ -79,6 +99,7 @@ class Character():
class Barbarian(Character):
class_name = 'Barbarian'
hit_dice_faces = 12
saving_throw_proficiencies = ['strength', 'constitution']
class Bard(Character):
@@ -119,6 +140,7 @@ class Ranger(Character):
class Rogue(Character):
class_name = 'Rogue'
hit_dice_faces = 8
saving_throw_proficiencies = ['dexterity', 'intelligence']
class Sorceror(Character):
+62 -1
View File
@@ -54,7 +54,7 @@ def create_fdf(character, fdfname):
('Race ', character.race),
('Alignment', character.alignment),
('XP', character.xp),
# Attributes
# Abilities
('ProfBonus', mod_str(character.proficiency_bonus)),
('STRmod', str(character.strength.value)),
('STR', mod_str(character.strength.modifier)),
@@ -71,6 +71,33 @@ def create_fdf(character, fdfname):
('AC', character.armor_class),
('Initiative', mod_str(character.dexterity.modifier)),
('Speed', character.speed),
('Passive', 10 + character.perception),
# Saving throws (proficiencies handled later)
('ST Strength', mod_str(character.strength.saving_throw)),
('ST Dexterity', mod_str(character.dexterity.saving_throw)),
('ST Constitution', mod_str(character.constitution.saving_throw)),
('ST Intelligence', mod_str(character.intelligence.saving_throw)),
('ST Wisdom', mod_str(character.wisdom.saving_throw)),
('ST Charisma', mod_str(character.charisma.saving_throw)),
# Skills (proficiencies handled below)
('Acrobatics', mod_str(character.acrobatics)),
('Animal', mod_str(character.animal_handling)),
('Arcana', mod_str(character.arcana)),
('Athletics', mod_str(character.athletics)),
('Deception ', mod_str(character.deception)),
('History ', mod_str(character.history)),
('Insight', mod_str(character.insight)),
('Intimidation', mod_str(character.intimidation)),
('Investigation ', mod_str(character.investigation)),
('Medicine', mod_str(character.medicine)),
('Nature', mod_str(character.nature)),
('Perception ', mod_str(character.perception)),
('Performance', mod_str(character.performance)),
('Persuasion', mod_str(character.persuasian)),
('Religion', mod_str(character.religion)),
('SleightofHand', mod_str(character.sleight_of_hand)),
('Stealth ', mod_str(character.stealth)),
('Survival', mod_str(character.survival)),
# Hit points
('HDTotal', character.hit_dice),
('HPMax', character.hp_max),
@@ -81,6 +108,40 @@ def create_fdf(character, fdfname):
('GP', character.gp),
('PP', character.pp),
]
# Check boxes for proficiencies
ST_boxes = {
'strength': 'Check Box 11',
'dexterity': 'Check Box 18',
'constitution': 'Check Box 19',
'intelligence': 'Check Box 20',
'wisdom': 'Check Box 21',
'charisma': 'Check Box 22',
}
for ability in character.saving_throw_proficiencies:
fields.append((ST_boxes[ability], 'Yes'))
skill_boxes = {
'acrobatics': 'Check Box 23',
'animal_handling': 'Check Box 24',
'arcana': 'Check Box 25',
'athletics': 'Check Box 26',
'deception': 'Check Box 27',
'history': 'Check Box 28',
'insight': 'Check Box 29',
'intimidation': 'Check Box 30',
'investigation': 'Check Box 31',
'medicine': 'Check Box 32',
'nature': 'Check Box 33',
'perception': 'Check Box 34',
'performance': 'Check Box 35',
'persuasian': 'Check Box 36',
'religion': 'Check Box 37',
'sleight_of_hand': 'Check Box 38',
'stealth': 'Check Box 39',
'survival': 'Check Box 40',
}
for skill in character.skill_proficiencies:
fields.append((skill_boxes[skill], 'Yes'))
# Create the actual FDF file
fdf = forge_fdf("", fields, [], [], [])
fdf_file = open(fdfname, "wb")
fdf_file.write(fdf)
+43 -3
View File
@@ -11,15 +11,55 @@ def mod_str(modifier):
class Stat():
class Ability():
value = 10
ability_name = None
character = None
def __init__(self, value=10):
self.value = value
def __set_name__(self, character, name):
self.ability_name = name
def __get__(self, character, Character):
self.character = character
return self
def __set__(self, obj, val):
self.value = val
@property
def modifier(self):
return math.floor((self.value - 10) / 2)
def __set__(self, obj, val):
self.value = val
@property
def saving_throw(self):
modifier = self.modifier
# Check for proficiency
if self.ability_name is not None:
is_proficient = (self.ability_name in self.character.saving_throw_proficiencies)
if is_proficient:
modifier += self.character.proficiency_bonus
# Return the value
return modifier
class Skill():
"""An ability-based skill, such as athletics."""
def __init__(self, ability):
self.ability_name = ability
def __set_name__(self, character, name):
self.skill_name = name
self.character = character
def __get__(self, character, owner):
ability = getattr(character, self.ability_name)
modifier = ability.modifier
# Check for proficiency
is_proficient = self.skill_name in character.skill_proficiencies
if is_proficient:
modifier += character.proficiency_bonus
return modifier
Binary file not shown.
+12 -4
View File
@@ -1,5 +1,5 @@
name = 'Mr. Stabby'
character_class = 'fighter'
character_class = 'rogue'
player_name = 'Mark'
background = "Criminal"
race = "Lightfoot halfling"
@@ -10,12 +10,20 @@ hp_max = 19
speed = 25
# Ability Scores
strength = 10
dexterity = 15
strength = 8
dexterity = 16
constitution = 12
intelligence = 13
wisdom = 9
wisdom = 10
charisma = 16
skill_proficiencies = [
'acrobatics',
'deception',
'investigation',
'performance',
'sleight_of_hand',
'stealth'
]
# Inventory
cp = 950
+1
View File
@@ -4,6 +4,7 @@ from unittest import TestCase
from dungeonsheets.character import Character
class TestCharacter(TestCase):
"""Tests for the generic character base class."""
+27 -3
View File
@@ -1,6 +1,6 @@
from unittest import TestCase
from dungeonsheets import stats
from dungeonsheets import stats, character
class TestStats(TestCase):
@@ -9,6 +9,17 @@ class TestStats(TestCase):
self.assertEqual(stats.mod_str(0), '0')
self.assertEqual(stats.mod_str(2), '+2')
def test_saving_throw(self):
stat = stats.Ability(14)
self.assertEqual(stat.saving_throw, 2)
# Now try it with an ST proficiency
class MyClass():
saving_throw_proficiencies = ['strength']
proficiency_bonus = 2
strength = stats.Ability(14)
my_class = MyClass()
self.assertEqual(my_class.strength.saving_throw, 4)
def test_modifier(self):
ranges = [
((1,), -5),
@@ -29,7 +40,7 @@ class TestStats(TestCase):
((30,), 10),
]
# Test the values for each modifier range
stat = stats.Stat()
stat = stats.Ability()
for range_, target in ranges:
for value in range_:
stat.value = value
@@ -40,9 +51,22 @@ class TestStats(TestCase):
"""Verify that this class works as a data descriptor."""
# Set up a dummy class
class MyCharacter():
stat = stats.Stat()
stat = stats.Ability()
char = MyCharacter()
# Check that the stat works as expected once set
char.stat = 15
self.assertEqual(char.stat.value, 15)
self.assertEqual(char.stat.modifier, 2)
def test_skill(self):
"""Test for a skill, that depends on another ability."""
class MyClass():
dexterity = stats.Ability(14)
acrobatics = stats.Skill(ability='dexterity')
skill_proficiencies = []
proficiency_bonus = 2
my_class = MyClass()
self.assertEqual(my_class.acrobatics, 2)
# Check for a proficiency
my_class.skill_proficiencies = ['acrobatics']
self.assertEqual(my_class.acrobatics, 4)