mirror of
https://github.com/Threnklyn/dungeon-sheets.git
synced 2026-05-19 04:33:26 +02:00
Added armor and shields to calculate armor class.
This commit is contained in:
@@ -0,0 +1,159 @@
|
|||||||
|
class Shield():
|
||||||
|
"""A shield that can be worn on one hand."""
|
||||||
|
name = "Shield"
|
||||||
|
cost = "10 gp"
|
||||||
|
base_armor_class = 2
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class NoShield(Shield):
|
||||||
|
"""If a character is carrying no shield."""
|
||||||
|
name = "No shield"
|
||||||
|
cost = "0"
|
||||||
|
base_armor_class = 0
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class Armor():
|
||||||
|
"""A piece of armor that can be worn.
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
name : str
|
||||||
|
Human-readable name for this armor.
|
||||||
|
cost : str
|
||||||
|
Cost and currency for this armor.
|
||||||
|
base_armor_class : int
|
||||||
|
Armor class granted before modifiers.
|
||||||
|
dexterity_mod_max : int
|
||||||
|
How much dexterity can the user contribute. ``0`` for no
|
||||||
|
dexterity modifier, ``None`` for unlimited dexterity modifier.
|
||||||
|
strength_required : int
|
||||||
|
Minimum strength needed to use this armor properly.
|
||||||
|
stealth_disadvantage : bool
|
||||||
|
If true, the armor causes disadvantage on stealth rolls.
|
||||||
|
weight : int
|
||||||
|
In lbs.
|
||||||
|
|
||||||
|
"""
|
||||||
|
name = "Unknown Armor"
|
||||||
|
cost = "0 gp"
|
||||||
|
base_armor_class = 10
|
||||||
|
dexterity_mod_max = None
|
||||||
|
strength_required = None
|
||||||
|
stealth_disadvantage = False
|
||||||
|
weight = 0 # In lbs
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class NoArmor(Armor):
|
||||||
|
name = "No armor"
|
||||||
|
|
||||||
|
|
||||||
|
class LightPaddedArmor(Armor):
|
||||||
|
name = "Light padded armor"
|
||||||
|
cost = "5 gp"
|
||||||
|
base_armor_class = 11
|
||||||
|
weight = 8
|
||||||
|
stealth_disadvantage = True
|
||||||
|
|
||||||
|
|
||||||
|
class LightLeatherArmor(Armor):
|
||||||
|
name = "Light leather armor"
|
||||||
|
cost = "10 gp"
|
||||||
|
base_armor_class = 11
|
||||||
|
weight = 10
|
||||||
|
|
||||||
|
|
||||||
|
class LightStuddedArmor(Armor):
|
||||||
|
name = "Light studded armor"
|
||||||
|
cost = "45 gp"
|
||||||
|
base_armor_class = 12
|
||||||
|
weight = 13
|
||||||
|
|
||||||
|
|
||||||
|
class MediumHideArmor(Armor):
|
||||||
|
name = "Medium hide armor"
|
||||||
|
cost = "10 gp"
|
||||||
|
base_armor_class = 12
|
||||||
|
dexterity_mod_max = 2
|
||||||
|
weight = 12
|
||||||
|
|
||||||
|
|
||||||
|
class MediumChainShirtArmor(Armor):
|
||||||
|
name = "Medium chain shirt armor"
|
||||||
|
cost = "50 gp"
|
||||||
|
base_armor_class = 13
|
||||||
|
dexterity_mod_max = 2
|
||||||
|
weight = 20
|
||||||
|
|
||||||
|
|
||||||
|
class MediumScaleMailArmor(Armor):
|
||||||
|
name = "Medium scale mail armor"
|
||||||
|
cost = "50 gp"
|
||||||
|
base_armor_class = 14
|
||||||
|
dexterity_mod_max = 2
|
||||||
|
stealth_disadvantage = True
|
||||||
|
weight = 45
|
||||||
|
|
||||||
|
|
||||||
|
class MediumBrassplateArmor(Armor):
|
||||||
|
name = "Medium brassplate armor"
|
||||||
|
cost = "400 gp"
|
||||||
|
base_armor_class = 14
|
||||||
|
dexterity_mod_max = 2
|
||||||
|
weight = 20
|
||||||
|
|
||||||
|
|
||||||
|
class MediumHalfPlateArmor(Armor):
|
||||||
|
name = "Medium half plate armor"
|
||||||
|
cost = "750 gp"
|
||||||
|
base_armor_class = 15
|
||||||
|
dexterity_mod_max = 2
|
||||||
|
stealth_disadvantage = True
|
||||||
|
weight = 40
|
||||||
|
|
||||||
|
|
||||||
|
class HeavyRingMailArmor(Armor):
|
||||||
|
name = "Heavy ring mail armor"
|
||||||
|
cost = "30 gp"
|
||||||
|
base_armor_class = 14
|
||||||
|
dexterity_mod_max = 0
|
||||||
|
stealth_disadvantage = True
|
||||||
|
weight = 40
|
||||||
|
|
||||||
|
|
||||||
|
class HeavyChainMailArmor(Armor):
|
||||||
|
name = "Heavy chain mail armor"
|
||||||
|
cost = "75 gp"
|
||||||
|
base_armor_class = 16
|
||||||
|
dexterity_mod_max = 0
|
||||||
|
strength_required = 13
|
||||||
|
stealth_disadvantage = True
|
||||||
|
weight = 55
|
||||||
|
|
||||||
|
|
||||||
|
class HeavySplintArmor(Armor):
|
||||||
|
name = "Heavy splint armor"
|
||||||
|
cost = "200 gp"
|
||||||
|
base_armor_class = 17
|
||||||
|
dexterity_mod_max = 0
|
||||||
|
strength_required = 15
|
||||||
|
stealth_disadvantage = True
|
||||||
|
weight = 60
|
||||||
|
|
||||||
|
|
||||||
|
class HeavyPlateArmor(Armor):
|
||||||
|
name = "Heavy splint armor"
|
||||||
|
cost = "1,500 gp"
|
||||||
|
base_armor_class = 18
|
||||||
|
dexterity_mod_max = 0
|
||||||
|
strength_required = 15
|
||||||
|
stealth_disadvantage = True
|
||||||
|
weight = 65
|
||||||
@@ -5,8 +5,9 @@ import warnings
|
|||||||
|
|
||||||
from .stats import Ability, Skill, findattr
|
from .stats import Ability, Skill, findattr
|
||||||
from .dice import read_dice_str
|
from .dice import read_dice_str
|
||||||
from . import weapons, race, spells
|
from . import weapons, race, spells, armor
|
||||||
from .weapons import Weapon
|
from .weapons import Weapon
|
||||||
|
from .armor import Armor, NoArmor, Shield, NoShield
|
||||||
|
|
||||||
dice_re = re.compile('(\d+)d(\d+)')
|
dice_re = re.compile('(\d+)d(\d+)')
|
||||||
|
|
||||||
@@ -73,6 +74,8 @@ class Character():
|
|||||||
pp = 0
|
pp = 0
|
||||||
equipment = ""
|
equipment = ""
|
||||||
weapons = [] # Replaced in __init__ constructor
|
weapons = [] # Replaced in __init__ constructor
|
||||||
|
armor = None
|
||||||
|
shield = None
|
||||||
_proficiencies_text = tuple()
|
_proficiencies_text = tuple()
|
||||||
# Magic
|
# Magic
|
||||||
spellcasting_ability = None
|
spellcasting_ability = None
|
||||||
@@ -105,6 +108,10 @@ class Character():
|
|||||||
elif attr == 'race':
|
elif attr == 'race':
|
||||||
MyRace = findattr(race, val)
|
MyRace = findattr(race, val)
|
||||||
self.race = MyRace()
|
self.race = MyRace()
|
||||||
|
elif attr == 'armor':
|
||||||
|
self.wear_armor(val)
|
||||||
|
elif attr == 'shield':
|
||||||
|
self.wield_shield(val)
|
||||||
elif (attr == 'spells') or (attr == 'spells_prepared'):
|
elif (attr == 'spells') or (attr == 'spells_prepared'):
|
||||||
# Create a list of actual spell objects
|
# Create a list of actual spell objects
|
||||||
_spells = []
|
_spells = []
|
||||||
@@ -185,6 +192,40 @@ class Character():
|
|||||||
final_text += '.'
|
final_text += '.'
|
||||||
return final_text
|
return final_text
|
||||||
|
|
||||||
|
def wear_armor(self, new_armor):
|
||||||
|
"""Accepts a string or Armor class and replaces the current armor.
|
||||||
|
|
||||||
|
If a string is given, then a subclass of
|
||||||
|
:py:class:`~dungeonsheets.armor.Armor` is retrived from the
|
||||||
|
``armor.py`` file. Otherwise, an subclass of
|
||||||
|
:py:class:`~dungeonsheets.armor.Armor` can be provided
|
||||||
|
directly.
|
||||||
|
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
NewArmor = findattr(armor, new_armor)
|
||||||
|
except AttributeError:
|
||||||
|
# Not a string, so just treat it as Armor
|
||||||
|
NewArmor = new_armor
|
||||||
|
self.armor = NewArmor()
|
||||||
|
|
||||||
|
def wield_shield(self, shield):
|
||||||
|
"""Accepts a string or Shield class and replaces the current armor.
|
||||||
|
|
||||||
|
If a string is given, then a subclass of
|
||||||
|
:py:class:`~dungeonsheets.armor.Shield` is retrived from the
|
||||||
|
``armor.py`` file. Otherwise, an subclass of
|
||||||
|
:py:class:`~dungeonsheets.armor.Shield` can be provided
|
||||||
|
directly.
|
||||||
|
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
NewShield = findattr(armor, shield)
|
||||||
|
except AttributeError:
|
||||||
|
# Not a string, so just treat it as Armor
|
||||||
|
NewShield = shield
|
||||||
|
self.shield = NewShield()
|
||||||
|
|
||||||
def wield_weapon(self, weapon):
|
def wield_weapon(self, weapon):
|
||||||
"""Accepts a string and adds it to the list of wielded weapons.
|
"""Accepts a string and adds it to the list of wielded weapons.
|
||||||
|
|
||||||
@@ -236,8 +277,18 @@ class Character():
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def armor_class(self):
|
def armor_class(self):
|
||||||
"""Armor class, without items."""
|
"""Armor class, including contributions from worn armor and shield."""
|
||||||
return 10 + self.dexterity.modifier
|
# Retrieve current armor (or a generic armor substitute)
|
||||||
|
armor = self.armor if self.armor is not None else NoArmor()
|
||||||
|
shield = self.shield if self.shield is not None else NoShield()
|
||||||
|
# Calculate and apply modifiers
|
||||||
|
if armor.dexterity_mod_max is None:
|
||||||
|
modifier = self.dexterity.modifier
|
||||||
|
else:
|
||||||
|
modifier = min(self.dexterity.modifier, armor.dexterity_mod_max)
|
||||||
|
# Calculate final armor class
|
||||||
|
ac = armor.base_armor_class + shield.base_armor_class + modifier
|
||||||
|
return ac
|
||||||
|
|
||||||
|
|
||||||
class Barbarian(Character):
|
class Barbarian(Character):
|
||||||
|
|||||||
@@ -181,7 +181,6 @@ def create_character_pdf(character, basename, flatten=False):
|
|||||||
('Ideals', text_box(character.ideals)),
|
('Ideals', text_box(character.ideals)),
|
||||||
('Bonds', text_box(character.bonds)),
|
('Bonds', text_box(character.bonds)),
|
||||||
('Flaws', text_box(character.flaws)),
|
('Flaws', text_box(character.flaws)),
|
||||||
('AttacksSpellcasting', text_box(character.attacks_and_spellcasting)),
|
|
||||||
('Features and Traits', text_box(character.features_and_traits)),
|
('Features and Traits', text_box(character.features_and_traits)),
|
||||||
# Inventory
|
# Inventory
|
||||||
('CP', character.cp),
|
('CP', character.cp),
|
||||||
@@ -234,6 +233,11 @@ def create_character_pdf(character, basename, flatten=False):
|
|||||||
fields.append((name_field, weapon.name))
|
fields.append((name_field, weapon.name))
|
||||||
fields.append((atk_field, mod_str(weapon.attack_bonus)))
|
fields.append((atk_field, mod_str(weapon.attack_bonus)))
|
||||||
fields.append((dmg_field, f'{weapon.damage} {weapon.damage_type}'))
|
fields.append((dmg_field, f'{weapon.damage} {weapon.damage_type}'))
|
||||||
|
# Other attack information
|
||||||
|
attack_str = f'Armor: {character.armor}'
|
||||||
|
attack_str += f'Shield: {character.shield}\n\n'
|
||||||
|
attack_str += character.attacks_and_spellcasting
|
||||||
|
fields.append(('AttacksSpellcasting', text_box(attack_str)))
|
||||||
# Other proficiencies and languages
|
# Other proficiencies and languages
|
||||||
prof_text = "Proficiencies:\n" + text_box(character.proficiencies_text)
|
prof_text = "Proficiencies:\n" + text_box(character.proficiencies_text)
|
||||||
prof_text += "\n\nLanguages:\n" + text_box(character.languages)
|
prof_text += "\n\nLanguages:\n" + text_box(character.languages)
|
||||||
|
|||||||
+29
-21
@@ -1,4 +1,5 @@
|
|||||||
import math
|
import math
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
|
||||||
def findattr(obj, name):
|
def findattr(obj, name):
|
||||||
@@ -28,39 +29,46 @@ def mod_str(modifier):
|
|||||||
return mod_str
|
return mod_str
|
||||||
|
|
||||||
|
|
||||||
|
AbilityScore = namedtuple('AbilityScore', ('value', 'modifier', 'saving_throw'))
|
||||||
|
|
||||||
|
|
||||||
class Ability():
|
class Ability():
|
||||||
value = 10
|
|
||||||
ability_name = None
|
ability_name = None
|
||||||
character = None
|
|
||||||
|
|
||||||
def __init__(self, value=10):
|
def __init__(self, default_value=10):
|
||||||
self.value = value
|
self.default_value = default_value
|
||||||
|
|
||||||
def __set_name__(self, character, name):
|
def __set_name__(self, character, name):
|
||||||
self.ability_name = name
|
self.ability_name = name
|
||||||
|
|
||||||
|
def _check_dict(self, obj):
|
||||||
|
if not hasattr(obj, '_ability_scores'):
|
||||||
|
# No ability score dictionary exists
|
||||||
|
obj._ability_scores = {
|
||||||
|
self.ability_name: self.default_value
|
||||||
|
}
|
||||||
|
elif self.ability_name not in obj._ability_scores.keys():
|
||||||
|
# ability score dictionary exists but doesn't have this ability
|
||||||
|
obj._ability_scores[self.ability_name] = self.default_value
|
||||||
|
|
||||||
def __get__(self, character, Character):
|
def __get__(self, character, Character):
|
||||||
self.character = character
|
self._check_dict(character)
|
||||||
return self
|
score = character._ability_scores[self.ability_name]
|
||||||
|
modifier = math.floor((score - 10) / 2)
|
||||||
def __set__(self, obj, val):
|
|
||||||
self.value = val
|
|
||||||
|
|
||||||
@property
|
|
||||||
def modifier(self):
|
|
||||||
return math.floor((self.value - 10) / 2)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def saving_throw(self):
|
|
||||||
modifier = self.modifier
|
|
||||||
# Check for proficiency
|
# Check for proficiency
|
||||||
|
saving_throw = modifier
|
||||||
if self.ability_name is not None:
|
if self.ability_name is not None:
|
||||||
is_proficient = (self.ability_name in self.character.saving_throw_proficiencies)
|
is_proficient = (self.ability_name in character.saving_throw_proficiencies)
|
||||||
if is_proficient:
|
if is_proficient:
|
||||||
modifier += self.character.proficiency_bonus
|
saving_throw += character.proficiency_bonus
|
||||||
# Return the value
|
# Create the named tuple
|
||||||
return modifier
|
value = AbilityScore(modifier=modifier, value=score, saving_throw=saving_throw)
|
||||||
|
return value
|
||||||
|
|
||||||
|
def __set__(self, character, val):
|
||||||
|
self._check_dict(character)
|
||||||
|
character._ability_scores[self.ability_name] = val
|
||||||
|
self.value = val
|
||||||
|
|
||||||
|
|
||||||
class Skill():
|
class Skill():
|
||||||
|
|||||||
Binary file not shown.
@@ -33,6 +33,8 @@ ep = 50
|
|||||||
gp = 120
|
gp = 120
|
||||||
pp = 0
|
pp = 0
|
||||||
weapons = ('shortsword', 'shortbow')
|
weapons = ('shortsword', 'shortbow')
|
||||||
|
armor = 'light leather armor'
|
||||||
|
shield = 'shield'
|
||||||
equipment = (
|
equipment = (
|
||||||
"""Shortsword, shortbow, 20 arrows, leather armor, thieves’ tools,
|
"""Shortsword, shortbow, 20 arrows, leather armor, thieves’ tools,
|
||||||
backpack, bell, 5 candles, crowbar, hammer, 10 pitons, 50 feet of
|
backpack, bell, 5 candles, crowbar, hammer, 10 pitons, 50 feet of
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -1,14 +1,14 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import os
|
import os
|
||||||
# from distutils.core import setup
|
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
|
||||||
def read(fname):
|
def read(fname):
|
||||||
return open(os.path.join(os.path.dirname(__file__), fname)).read()
|
return open(os.path.join(os.path.dirname(__file__), fname)).read()
|
||||||
|
|
||||||
|
|
||||||
setup(name='dungeonsheets',
|
setup(name='dungeonsheets',
|
||||||
version='0.1.0',
|
version='0.2.1',
|
||||||
description='Dungeons and Dragons 5e Character Tools',
|
description='Dungeons and Dragons 5e Character Tools',
|
||||||
long_description=read('README.rst'),
|
long_description=read('README.rst'),
|
||||||
long_description_content_type='text/x-rst',
|
long_description_content_type='text/x-rst',
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from unittest import TestCase
|
|||||||
from dungeonsheets import race
|
from dungeonsheets import race
|
||||||
from dungeonsheets.character import Character, Wizard
|
from dungeonsheets.character import Character, Wizard
|
||||||
from dungeonsheets.weapons import Weapon, Shortsword
|
from dungeonsheets.weapons import Weapon, Shortsword
|
||||||
|
from dungeonsheets.armor import Armor, LightLeatherArmor, Shield
|
||||||
|
|
||||||
|
|
||||||
class TestCharacter(TestCase):
|
class TestCharacter(TestCase):
|
||||||
@@ -28,6 +29,10 @@ class TestCharacter(TestCase):
|
|||||||
char.set_attrs(weapons=['shortsword'])
|
char.set_attrs(weapons=['shortsword'])
|
||||||
self.assertEqual(len(char.weapons), 1)
|
self.assertEqual(len(char.weapons), 1)
|
||||||
self.assertTrue(isinstance(char.weapons[0], Shortsword))
|
self.assertTrue(isinstance(char.weapons[0], Shortsword))
|
||||||
|
# Check that armor and shield gets set_attrs
|
||||||
|
char.set_attrs(armor='light leather armor', shield='shield')
|
||||||
|
self.assertFalse(isinstance(char.armor, str))
|
||||||
|
self.assertFalse(isinstance(char.shield, str))
|
||||||
# Check that race gets set to an object
|
# Check that race gets set to an object
|
||||||
char.set_attrs(race='high elf')
|
char.set_attrs(race='high elf')
|
||||||
self.assertIsInstance(char.race, race.HighElf)
|
self.assertIsInstance(char.race, race.HighElf)
|
||||||
@@ -125,3 +130,26 @@ class TestCharacter(TestCase):
|
|||||||
self.assertEqual(char.spell_slots(spell_level=0), 3)
|
self.assertEqual(char.spell_slots(spell_level=0), 3)
|
||||||
self.assertEqual(char.spell_slots(spell_level=1), 3)
|
self.assertEqual(char.spell_slots(spell_level=1), 3)
|
||||||
self.assertEqual(char.spell_slots(spell_level=2), 0)
|
self.assertEqual(char.spell_slots(spell_level=2), 0)
|
||||||
|
|
||||||
|
def test_equip_armor(self):
|
||||||
|
char = Character(dexterity=16)
|
||||||
|
char.wear_armor('light leather armor')
|
||||||
|
self.assertTrue(isinstance(char.armor, Armor))
|
||||||
|
# Now make sure the armor class is correct
|
||||||
|
self.assertEqual(char.armor_class, 14)
|
||||||
|
# Try passing an Armor object directly
|
||||||
|
char.wear_armor(LightLeatherArmor)
|
||||||
|
self.assertEqual(char.armor_class, 14)
|
||||||
|
# Test equipped armor with max dexterity mod_str
|
||||||
|
char.armor.dexterity_mod_max = 1
|
||||||
|
self.assertEqual(char.armor_class, 12)
|
||||||
|
|
||||||
|
def test_wield_shield(self):
|
||||||
|
char = Character(dexterity=16)
|
||||||
|
char.wield_shield('shield')
|
||||||
|
self.assertTrue(isinstance(char.shield, Shield), msg=char.shield)
|
||||||
|
# Now make sure the armor class is correct
|
||||||
|
self.assertEqual(char.armor_class, 15)
|
||||||
|
# Try passing an Armor object directly
|
||||||
|
char.wield_shield(Shield)
|
||||||
|
self.assertEqual(char.armor_class, 15)
|
||||||
|
|||||||
+11
-8
@@ -10,10 +10,8 @@ class TestStats(TestCase):
|
|||||||
self.assertEqual(stats.mod_str(2), '+2')
|
self.assertEqual(stats.mod_str(2), '+2')
|
||||||
|
|
||||||
def test_saving_throw(self):
|
def test_saving_throw(self):
|
||||||
stat = stats.Ability(14)
|
# Try it with an ST proficiency
|
||||||
self.assertEqual(stat.saving_throw, 2)
|
class MyClass(character.Character):
|
||||||
# Now try it with an ST proficiency
|
|
||||||
class MyClass():
|
|
||||||
saving_throw_proficiencies = ['strength']
|
saving_throw_proficiencies = ['strength']
|
||||||
proficiency_bonus = 2
|
proficiency_bonus = 2
|
||||||
strength = stats.Ability(14)
|
strength = stats.Ability(14)
|
||||||
@@ -21,6 +19,11 @@ class TestStats(TestCase):
|
|||||||
self.assertEqual(my_class.strength.saving_throw, 4)
|
self.assertEqual(my_class.strength.saving_throw, 4)
|
||||||
|
|
||||||
def test_modifier(self):
|
def test_modifier(self):
|
||||||
|
class MyCharacter(character.Character):
|
||||||
|
saving_throw_proficiencies = ['strength']
|
||||||
|
proficiency_bonus = 2
|
||||||
|
strength = stats.Ability(14)
|
||||||
|
my_char = MyCharacter()
|
||||||
ranges = [
|
ranges = [
|
||||||
((1,), -5),
|
((1,), -5),
|
||||||
((2, 3), -4),
|
((2, 3), -4),
|
||||||
@@ -40,17 +43,17 @@ class TestStats(TestCase):
|
|||||||
((30,), 10),
|
((30,), 10),
|
||||||
]
|
]
|
||||||
# Test the values for each modifier range
|
# Test the values for each modifier range
|
||||||
stat = stats.Ability()
|
|
||||||
for range_, target in ranges:
|
for range_, target in ranges:
|
||||||
for value in range_:
|
for value in range_:
|
||||||
stat.value = value
|
my_char.strength = value
|
||||||
|
stat = my_char.strength
|
||||||
msg = f"Stat {value} doesn't produce modifier {target} ({stat.modifier})"
|
msg = f"Stat {value} doesn't produce modifier {target} ({stat.modifier})"
|
||||||
self.assertEqual(stat.modifier, target, msg)
|
self.assertEqual(stat.modifier, target, msg)
|
||||||
|
|
||||||
def test_setter(self):
|
def test_setter(self):
|
||||||
"""Verify that this class works as a data descriptor."""
|
"""Verify that this class works as a data descriptor."""
|
||||||
# Set up a dummy class
|
# Set up a dummy class
|
||||||
class MyCharacter():
|
class MyCharacter(character.Character):
|
||||||
stat = stats.Ability()
|
stat = stats.Ability()
|
||||||
char = MyCharacter()
|
char = MyCharacter()
|
||||||
# Check that the stat works as expected once set
|
# Check that the stat works as expected once set
|
||||||
@@ -60,7 +63,7 @@ class TestStats(TestCase):
|
|||||||
|
|
||||||
def test_skill(self):
|
def test_skill(self):
|
||||||
"""Test for a skill, that depends on another ability."""
|
"""Test for a skill, that depends on another ability."""
|
||||||
class MyClass():
|
class MyClass(character.Character):
|
||||||
dexterity = stats.Ability(14)
|
dexterity = stats.Ability(14)
|
||||||
acrobatics = stats.Skill(ability='dexterity')
|
acrobatics = stats.Skill(ability='dexterity')
|
||||||
skill_proficiencies = []
|
skill_proficiencies = []
|
||||||
|
|||||||
Reference in New Issue
Block a user