mirror of
https://github.com/Threnklyn/dungeon-sheets.git
synced 2026-06-07 13:15:53 +02:00
Added weapons, weapon proficiencies, and equipment.
This commit is contained in:
@@ -0,0 +1 @@
|
||||
from . import weapons, character
|
||||
|
||||
+128
-14
@@ -2,8 +2,9 @@
|
||||
|
||||
import re
|
||||
|
||||
from .stats import Ability, Skill
|
||||
from .stats import Ability, Skill, findattr
|
||||
from .dice import read_dice_str
|
||||
from . import weapons
|
||||
|
||||
dice_re = re.compile('(\d+)d(\d+)')
|
||||
|
||||
@@ -33,6 +34,10 @@ class Character():
|
||||
wisdom = Ability()
|
||||
charisma = Ability()
|
||||
saving_throw_proficiencies = []
|
||||
skill_proficiencies = tuple()
|
||||
weapon_proficienies = tuple()
|
||||
proficiencies_extra = tuple()
|
||||
languages = ""
|
||||
# Skills
|
||||
acrobatics = Skill(ability='dexterity')
|
||||
animal_handling = Skill(ability='wisdom')
|
||||
@@ -63,16 +68,76 @@ class Character():
|
||||
ep = 0
|
||||
gp = 0
|
||||
pp = 0
|
||||
equipment = ""
|
||||
weapons = [] # Replaced in __init__ constructor
|
||||
_proficiencies_text = tuple()
|
||||
|
||||
def __init__(self, **attrs):
|
||||
"""Takes a bunch of attrs and passes them to ``set_attrs``"""
|
||||
self.weapons = []
|
||||
self.set_attrs(**attrs)
|
||||
|
||||
def set_attrs(self, **attrs):
|
||||
"""Bulk setting of attributes. Useful for loading a character from a
|
||||
dictionary."""
|
||||
for attr, val in attrs.items():
|
||||
setattr(self, attr, val)
|
||||
if attr == 'weapons':
|
||||
# Treat weapons specially
|
||||
for weap in val:
|
||||
self.wield_weapon(weap)
|
||||
else:
|
||||
if not hasattr(self, attr):
|
||||
warnings.warn(f"Setting unknown character attribute {attr}",
|
||||
RuntimeWarning)
|
||||
# Lookup general attributes
|
||||
setattr(self, attr, val)
|
||||
|
||||
@property
|
||||
def proficiencies_text(self):
|
||||
final_text = ""
|
||||
all_proficiencies = (self._proficiencies_text + self.proficiencies_extra)
|
||||
# Create a single string out of all the proficiencies
|
||||
for txt in all_proficiencies:
|
||||
if not final_text:
|
||||
# Capitalize the first entry
|
||||
txt = txt.capitalize()
|
||||
else:
|
||||
# Put a comma first
|
||||
txt = ", " + txt
|
||||
# Add this item to the list text
|
||||
final_text += txt
|
||||
# Add a period at the end
|
||||
final_text += '.'
|
||||
return final_text
|
||||
|
||||
def wield_weapon(self, weapon):
|
||||
"""Accepts a string and adds it to the list of wielded weapons.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
weapon : str
|
||||
Case-insensitive string with a name of the weapon.
|
||||
|
||||
"""
|
||||
# Retrieve the weapon class from the weapons module
|
||||
try:
|
||||
NewWeapon = findattr(weapons, weapon)
|
||||
except AttributeError:
|
||||
raise AttributeError(f'Weapon {weapon} is not defined')
|
||||
weapon_ = NewWeapon()
|
||||
# Set weapon attributes based on character
|
||||
if weapon_.is_finesse:
|
||||
ability_mod = max(self.strength.modifier, self.dexterity.modifier)
|
||||
else:
|
||||
ability_mod = getattr(self, weapon_.ability).modifier
|
||||
weapon_.attack_bonus += ability_mod
|
||||
weapon_.bonus_damage += ability_mod
|
||||
# Check for prifiency
|
||||
is_proficient = (weapon_.__class__ in self.weapon_proficienies)
|
||||
if is_proficient:
|
||||
weapon_.attack_bonus += self.proficiency_bonus
|
||||
# Save it to the array
|
||||
self.weapons.append(weapon_)
|
||||
|
||||
@property
|
||||
def hit_dice(self):
|
||||
@@ -104,70 +169,119 @@ class Character():
|
||||
class Barbarian(Character):
|
||||
class_name = 'Barbarian'
|
||||
hit_dice_faces = 12
|
||||
saving_throw_proficiencies = ['strength', 'constitution']
|
||||
saving_throw_proficiencies = ('strength', 'constitution')
|
||||
_proficiencies_text = ('light armor', 'medium armor', 'shields',
|
||||
'simple weapons', 'martial weapons')
|
||||
weapon_proficienies = (weapons.simple_weapons + weapons.martial_weapons)
|
||||
|
||||
|
||||
class Bard(Character):
|
||||
class_name = 'Bard'
|
||||
hit_dice_faces = 8
|
||||
saving_throw_proficiencies = ['dexterity', 'charisma']
|
||||
saving_throw_proficiencies = ('dexterity', 'charisma')
|
||||
_proficiencies_text = (
|
||||
'Light armor', 'simple weapons', 'hand crossbows', 'longswords',
|
||||
'rapiers', 'shortswords', 'three musical instruments of your choice')
|
||||
weapon_proficienies = ((weapons.HandCrossbow, weapons.Longsword,
|
||||
weapons.Rapier, weapons.Shortsword) +
|
||||
weapons.simple_weapons)
|
||||
|
||||
|
||||
class Cleric(Character):
|
||||
class_name = 'Cleric'
|
||||
hit_dice_faces = 8
|
||||
saving_throw_proficiencies = ['wisdom', 'charisma']
|
||||
saving_throw_proficiencies = ('wisdom', 'charisma')
|
||||
_proficiencies_text = ('light armor', 'medium armor', 'shields',
|
||||
'all simple weapons')
|
||||
weapon_proficienies = weapons.simple_weapons
|
||||
|
||||
|
||||
class Druid(Character):
|
||||
class_name = 'Druid'
|
||||
hit_dice_faces = 8
|
||||
saving_throw_proficiencies = ['intelligence', 'wisdom']
|
||||
saving_throw_proficiencies = ('intelligence', 'wisdom')
|
||||
_proficiencies_text = (
|
||||
'Light armor', 'medium armor',
|
||||
'shields (druids will not wear armor or use shields made of metal)',
|
||||
'clubs', 'daggers', 'darts', 'javelins', 'maces', 'quarterstaffs',
|
||||
'scimitars', 'sickles', 'slings', 'spears')
|
||||
weapon_proficienies = (weapons.Club, weapons.Dagger, weapons.Dart,
|
||||
weapons.Javelin, weapons.Mace, weapons.Quarterstaff,
|
||||
weapons.Scimitar, weapons.Sickle, weapons.Sling, weapons.Spear)
|
||||
|
||||
|
||||
class Fighter(Character):
|
||||
class_name = 'Fighter'
|
||||
hit_dice_faces = 10
|
||||
saving_throw_proficiencies = ['strength', 'constitution']
|
||||
saving_throw_proficiencies = ('strength', 'constitution')
|
||||
_proficiencies_text = ('All armar', 'shields', 'simple weapons', 'martial weapons')
|
||||
weapon_proficienies = weapons.simple_weapons + weapons.martial_weapons
|
||||
|
||||
|
||||
class Monk(Character):
|
||||
class_name = 'Monk'
|
||||
hit_dice_faces = 8
|
||||
saving_throw_proficiencies = ['strength', 'dexterity']
|
||||
saving_throw_proficiencies = ('strength', 'dexterity')
|
||||
_proficiencies_text = (
|
||||
'simple weapons', 'shortswords',
|
||||
"one type of artisan's tools or one musical instrument")
|
||||
weapon_proficienies = (weapons.Shortsword,) + weapons.simple_weapons
|
||||
|
||||
|
||||
class Paladin(Character):
|
||||
class_name = 'Paladin'
|
||||
hit_dice_faces = 10
|
||||
saving_throw_proficiencies = ['wisdom', 'charisma']
|
||||
saving_throw_proficiencies = ('wisdom', 'charisma')
|
||||
_proficiencies_text = ('All armor', 'shields', 'simple weapons',
|
||||
'martial weapons')
|
||||
weapon_proficienies = weapons.simple_weapons + weapons.martial_weapons
|
||||
|
||||
|
||||
class Ranger(Character):
|
||||
class_name = 'Ranger'
|
||||
hit_dice_faces = 10
|
||||
saving_throw_proficiencies = ['strength', 'dexterity']
|
||||
saving_throw_proficiencies = ('strength', 'dexterity')
|
||||
_proficiencies_text = ("light armor", "medium armor", "shields",
|
||||
"simple weapons", "martial weapons")
|
||||
weapon_proficienies = weapons.simple_weapons + weapons.martial_weapons
|
||||
|
||||
|
||||
class Rogue(Character):
|
||||
class_name = 'Rogue'
|
||||
hit_dice_faces = 8
|
||||
saving_throw_proficiencies = ['dexterity', 'intelligence']
|
||||
saving_throw_proficiencies = ('dexterity', 'intelligence')
|
||||
_proficiencies_text = (
|
||||
'light armor', 'simple weapons', 'hand crossbows', 'longswords',
|
||||
'rapiers', 'shortswords', "thieves' tools")
|
||||
weapon_proficienies = (weapons.HandCrossbow, weapons.Longsword,
|
||||
weapons.Rapier, weapons.Shortsword) + weapons.simple_weapons
|
||||
|
||||
|
||||
class Sorceror(Character):
|
||||
class_name = 'Sorceror'
|
||||
hit_dice_faces = 6
|
||||
saving_throw_proficiencies = ['constitution', 'charisma']
|
||||
saving_throw_proficiencies = ('constitution', 'charisma')
|
||||
_proficiencies_text = ('daggers', 'darts', 'slings',
|
||||
'quarterstaffs', 'light crossbows')
|
||||
weapon_proficienies = (weapons.Dagger, weapons.Dart,
|
||||
weapons.Sling, weapons.Quarterstaff,
|
||||
weapons.LightCrossbow)
|
||||
|
||||
|
||||
class Warlock(Character):
|
||||
class_name = 'Warlock'
|
||||
hit_dice_faces = 8
|
||||
saving_throw_proficiencies = ['wisdom', 'charisma']
|
||||
saving_throw_proficiencies = ('wisdom', 'charisma')
|
||||
_proficiencies_text = ("light Armor", "simple weapons")
|
||||
weapon_proficienies = weapons.simple_weapons
|
||||
|
||||
|
||||
class Wizard(Character):
|
||||
class_name = 'Wizard'
|
||||
hit_dice_faces = 6
|
||||
saving_throw_proficiencies = ['intelligence', 'wisdom']
|
||||
saving_throw_proficiencies = ('intelligence', 'wisdom')
|
||||
_proficiencies_text = ('daggers', 'darts', 'slings',
|
||||
'quarterstaffs', 'light crossbows')
|
||||
weapon_proficienies = (weapons.Dagger, weapons.Dart,
|
||||
weapons.Sling, weapons.Quarterstaff,
|
||||
weapons.LightCrossbow)
|
||||
|
||||
@@ -4,6 +4,7 @@ import argparse
|
||||
import importlib.util
|
||||
import os
|
||||
import subprocess
|
||||
import warnings
|
||||
|
||||
from fdfgen import forge_fdf
|
||||
|
||||
@@ -121,6 +122,7 @@ def create_fdf(character, fdfname):
|
||||
('EP', character.ep),
|
||||
('GP', character.gp),
|
||||
('PP', character.pp),
|
||||
('Equipment', text_box(character.equipment)),
|
||||
]
|
||||
# Check boxes for proficiencies
|
||||
ST_boxes = {
|
||||
@@ -153,8 +155,22 @@ def create_fdf(character, fdfname):
|
||||
'stealth': 'Check Box 39',
|
||||
'survival': 'Check Box 40',
|
||||
}
|
||||
# Add skill proficienies
|
||||
for skill in character.skill_proficiencies:
|
||||
fields.append((skill_boxes[skill], 'Yes'))
|
||||
# Add weapons
|
||||
weapon_fields = [('Wpn Name', 'Wpn1 AtkBonus', 'Wpn1 Damage'),
|
||||
('Wpn Name 2', 'Wpn2 AtkBonus ', 'Wpn2 Damage '),
|
||||
('Wpn Name 3', 'Wpn3 AtkBonus ', 'Wpn3 Damage '),]
|
||||
for _fields, weapon in zip(weapon_fields, character.weapons):
|
||||
name_field, atk_field, dmg_field = _fields
|
||||
fields.append((name_field, weapon.name))
|
||||
fields.append((atk_field, mod_str(weapon.attack_bonus)))
|
||||
fields.append((dmg_field, f'{weapon.damage} {weapon.damage_type}'))
|
||||
# Other proficiencies and languages
|
||||
prof_text = "Proficiencies:\n" + text_box(character.proficiencies_text)
|
||||
prof_text += "\n\nLanguages:\n" + text_box(character.languages)
|
||||
fields.append(('ProficienciesLang', prof_text))
|
||||
# Create the actual FDF file
|
||||
fdf = forge_fdf("", fields, [], [], [])
|
||||
fdf_file = open(fdfname, "wb")
|
||||
@@ -207,8 +223,13 @@ def main():
|
||||
filenames = [args.filename]
|
||||
for filename in filenames:
|
||||
print(f"Processing {os.path.splitext(filename)[0]}...", end='')
|
||||
make_sheet(character_file=filename, flatten=args.flatten)
|
||||
print("done")
|
||||
try:
|
||||
make_sheet(character_file=filename, flatten=args.flatten)
|
||||
except Exception as e:
|
||||
print('failed')
|
||||
raise
|
||||
else:
|
||||
print("done")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -1,6 +1,24 @@
|
||||
import math
|
||||
|
||||
|
||||
def findattr(obj, name):
|
||||
"""Similar to builtin getattr(obj, name) but more forgiving to
|
||||
whitespace and capitalization.
|
||||
|
||||
"""
|
||||
# Come up with several options
|
||||
py_name = name.replace('-', '_').replace(' ', '_')
|
||||
camel_case = "".join([s.capitalize() for s in py_name.split('_')])
|
||||
if hasattr(obj, py_name):
|
||||
# Direct lookup
|
||||
attr = getattr(obj, py_name)
|
||||
elif hasattr(obj, camel_case):
|
||||
# CamelCase lookup
|
||||
attr = getattr(obj, camel_case)
|
||||
else:
|
||||
raise AttributeError(f'{obj} has no attribute {name}')
|
||||
return attr
|
||||
|
||||
def mod_str(modifier):
|
||||
"""Converts a modifier to a string, eg 2 -> '+2'."""
|
||||
if modifier > 0:
|
||||
|
||||
@@ -0,0 +1,410 @@
|
||||
from .stats import mod_str
|
||||
|
||||
class Weapon():
|
||||
name = ""
|
||||
cost = "0 gp"
|
||||
base_damage = "1d4"
|
||||
bonus_damage = 0
|
||||
damage_type = "piercing"
|
||||
attack_bonus = 0
|
||||
weight = 1 # In lbs
|
||||
properties = "Light"
|
||||
ability = 'strength'
|
||||
is_finesse = False
|
||||
|
||||
@property
|
||||
def damage(self):
|
||||
dam_str = str(self.base_damage)
|
||||
if self.bonus_damage != 0:
|
||||
dam_str += ' ' + mod_str(self.bonus_damage)
|
||||
return dam_str
|
||||
|
||||
|
||||
class Club(Weapon):
|
||||
name = "Club"
|
||||
cost = "1 sp"
|
||||
base_damage = "1d4"
|
||||
damage_type = "bludgeoning"
|
||||
weight = 2
|
||||
properties = "Light"
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Dagger(Weapon):
|
||||
name = "Dagger"
|
||||
cost = "2 gp"
|
||||
base_damage = "1d4"
|
||||
damage_type = "piercing"
|
||||
weight = 1
|
||||
properties = "Finesse, light, thrown (range 20/60)"
|
||||
is_finesse = True
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Greatclub(Weapon):
|
||||
name = "Greatclub"
|
||||
cost = "2 sp"
|
||||
base_damage = "1d8"
|
||||
damage_type = "bludgeoning"
|
||||
weight = 10
|
||||
properties = "Two-handed"
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Handaxe(Weapon):
|
||||
name = "Handaxe"
|
||||
cost = "5 gp"
|
||||
base_damage = "1d6"
|
||||
damage_type = "slashing"
|
||||
weight = 2
|
||||
properties = "Light, thrown (range 20/60)"
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Javelin(Weapon):
|
||||
name = "Javelin"
|
||||
cost = "5 sp"
|
||||
base_damage = "1d6"
|
||||
damage_type = "piercing"
|
||||
weight = 2
|
||||
properties = "Thrown (range 30/120)"
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class LightHammer(Weapon):
|
||||
name = "Light hammer"
|
||||
cost = "2 gp"
|
||||
base_damage = "1d4"
|
||||
damage_type = "bludgeoning"
|
||||
weight = 2
|
||||
properties = "Light, thrown (range 20/60)"
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Mace(Weapon):
|
||||
name = "Mace"
|
||||
cost = "5 gp"
|
||||
base_damage = "1d6"
|
||||
damage_type = "bludgeoning"
|
||||
weight = 4
|
||||
properties = ""
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Quarterstaff(Weapon):
|
||||
name = "Quarterstaff"
|
||||
cost = "2 sp"
|
||||
base_damage = "1d6"
|
||||
damage_type = "bludgeoning"
|
||||
weight = 4
|
||||
properties = "Versatile (1d8)"
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Sickle(Weapon):
|
||||
name = "Sickle"
|
||||
cost = "1 gp"
|
||||
base_damage = "1d4"
|
||||
damage_type = "slashing"
|
||||
weight = 2
|
||||
properties = "Light"
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Spear(Weapon):
|
||||
name = "Spear"
|
||||
cost = "1 gp"
|
||||
base_damage = "1d6"
|
||||
damage_type = "piercing"
|
||||
weight = 3
|
||||
properties = "Thrown (range 20/60), versatile (1d8)"
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class LightCrossbow(Weapon):
|
||||
name = "Light crossbow"
|
||||
cost = "25 gp"
|
||||
base_damage = "1d8"
|
||||
damage_type = "piercing"
|
||||
weight = 5
|
||||
properties = "Ammunition (range 80/320, loading, two-handed"
|
||||
ability = 'dexterity'
|
||||
|
||||
|
||||
class Dart(Weapon):
|
||||
name = "Dart"
|
||||
cost = "5 cp"
|
||||
base_damage = "1d4"
|
||||
damage_type = "piercing"
|
||||
weight = 0.25
|
||||
properties = "Finesse, thrown (range 20/60)"
|
||||
is_finesse = True
|
||||
ability = 'dexterity'
|
||||
|
||||
|
||||
class Shortbow(Weapon):
|
||||
name = "Shortbow"
|
||||
cost = "25 gp"
|
||||
base_damage = "1d6"
|
||||
damage_type = "piercing"
|
||||
weight = 2
|
||||
properties = "Ammunition (range 80/320), two-handed"
|
||||
ability = 'dexterity'
|
||||
|
||||
|
||||
class Sling(Weapon):
|
||||
name = "Sling"
|
||||
cost = "1 sp"
|
||||
base_damage = "1d4"
|
||||
damage_type = "bludgeoning"
|
||||
weight = 0
|
||||
properties = "Ammunition (range 30/120)"
|
||||
ability = 'dexterity'
|
||||
|
||||
|
||||
class Battleaxe(Weapon):
|
||||
name = "Battleaxe"
|
||||
cost = "10 gp"
|
||||
base_damage = "1d8"
|
||||
damage_type = "slashing"
|
||||
weight = 4
|
||||
properties = "Versatile (1d10)"
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Flail(Weapon):
|
||||
name = "Flail"
|
||||
cost = "10gp"
|
||||
base_damage = "1d8"
|
||||
damage_type = "bludgeoning"
|
||||
weight = 2
|
||||
properties = ""
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Glaive(Weapon):
|
||||
name = "Glaive"
|
||||
cost = "20 gp"
|
||||
base_damage = "1d10"
|
||||
damage_type = "slashing"
|
||||
weight = 6
|
||||
properties = "Heavy, reach, two-handed"
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Greataxe(Weapon):
|
||||
name = "Greataxe"
|
||||
cost = "30 gp"
|
||||
base_damage = "1d12"
|
||||
damage_type = "slashing"
|
||||
weight = 7
|
||||
properties = "Heavy, two-handed"
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Greatsword(Weapon):
|
||||
name = "Greatsword"
|
||||
cost = "50 gp"
|
||||
base_damage = "2d6"
|
||||
damage_type = "slashing"
|
||||
weight = 6
|
||||
properties = "Heavy, two-handed"
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Halberd(Weapon):
|
||||
name = "Halberd"
|
||||
cost = "20 gp"
|
||||
base_damage = "1d10"
|
||||
damage_type = "slashing"
|
||||
weight = 6
|
||||
properties = "Heavy, reach, two-handed"
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Lance(Weapon):
|
||||
name = "Lance"
|
||||
cost = "10gp"
|
||||
base_damage = "1d12"
|
||||
damage_type = "piercing"
|
||||
weight = 6
|
||||
properties = "Reach, special"
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Longsword(Weapon):
|
||||
name = "Longsword"
|
||||
cost = "15 gp"
|
||||
base_damage = "1d8"
|
||||
damage_type = "slashing"
|
||||
weight = 3
|
||||
properties = "Versatile (1d10)"
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Maul(Weapon):
|
||||
name = "Maul"
|
||||
cost = "10 gp"
|
||||
base_damage = "2d6"
|
||||
damage_type = "bludgeoning"
|
||||
weight = 10
|
||||
properties = "Heavy, two-handed"
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Morningstar(Weapon):
|
||||
name = "Morningstar"
|
||||
cost = "15 gp"
|
||||
base_damage = "1d8"
|
||||
damage_type = "piercing"
|
||||
weight = 4
|
||||
properties = ""
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Pike(Weapon):
|
||||
name = "Pike"
|
||||
cost = "5 gp"
|
||||
base_damage = "1d10"
|
||||
damage_type = "piercing"
|
||||
weight = 18
|
||||
properties = "Heavy, reach, two-handed"
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Rapier(Weapon):
|
||||
name = "Rapier"
|
||||
cost = "25 gp"
|
||||
base_damage = "1d8"
|
||||
damage_type = "piercing"
|
||||
weight = 2
|
||||
properties = "Finesse"
|
||||
is_finesse = True
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Scimitar(Weapon):
|
||||
name = "Scimitar"
|
||||
cost = "25 gp"
|
||||
base_damage = "1d6"
|
||||
damage_type = "slashing"
|
||||
weight = 3
|
||||
properties = "Finesse, light"
|
||||
is_finesse = True
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Shortsword(Weapon):
|
||||
name = "Shortsword"
|
||||
cost = "10 gp"
|
||||
base_damage = "1d6"
|
||||
damage_type = "piercing"
|
||||
weight = 0
|
||||
properties = "Finesse, light"
|
||||
is_finesse = True
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Trident(Weapon):
|
||||
name = "Trident"
|
||||
cost = "5 gp"
|
||||
base_damage = "1d6"
|
||||
damage_type = "piercing"
|
||||
weight = 4
|
||||
properties = "Thrown (range 20/60), versatile (1d8)"
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class WarPick(Weapon):
|
||||
name = "War pick"
|
||||
cost = "5 gp"
|
||||
base_damage = "1d8"
|
||||
damage_type = "piercing"
|
||||
weight = 2
|
||||
properties = ""
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Warhammer(Weapon):
|
||||
name = "Warhammer"
|
||||
cost = "15 gp"
|
||||
base_damage = "1d8"
|
||||
damage_type = "bludgeoning"
|
||||
weight = 2
|
||||
properties = "Versatile (1d10)"
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Whip(Weapon):
|
||||
name = "Whip"
|
||||
cost = "2 gp"
|
||||
base_damage = "1d4"
|
||||
damage_type = "slashing"
|
||||
weight = 3
|
||||
properties = "Finesse, reach"
|
||||
is_finesse = True
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Blowgun(Weapon):
|
||||
name = "Blowgun"
|
||||
cost = "10 gp"
|
||||
base_damage = "1"
|
||||
damage_type = "piercing"
|
||||
weight = 1
|
||||
properties = "Ammunition (range 25/100), loading"
|
||||
ability = 'dexterity'
|
||||
|
||||
|
||||
class HandCrossbow(Weapon):
|
||||
name = "Crossbow, hand"
|
||||
cost = "75 gp"
|
||||
base_damage = "1d6"
|
||||
damage_type = "piercing"
|
||||
weight = 3
|
||||
properties = "Ammunition (range 30/120), light, loading"
|
||||
ability = 'dexterity'
|
||||
|
||||
|
||||
class HeavyCrossbow(Weapon):
|
||||
name = "Crossbow, heavy"
|
||||
cost = "50 gp"
|
||||
base_damage = "1d10"
|
||||
damage_type = "piercing"
|
||||
weight = 18
|
||||
properties = "Ammunition (range 100/400), heaving, loading, two-handed"
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Longbow(Weapon):
|
||||
name = "Longbow"
|
||||
cost = "50 gp"
|
||||
base_damage = "1d8"
|
||||
damage_type = "piercing"
|
||||
weight = 2
|
||||
properties = "Ammunition (range 150/600), heavy, two-handed"
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
class Net(Weapon):
|
||||
name = "Net"
|
||||
cost = "1 gp"
|
||||
base_damage = "-"
|
||||
damage_type = ""
|
||||
weight = 3
|
||||
properties = "Special, thrown (range 5/15)"
|
||||
ability = 'strength'
|
||||
|
||||
|
||||
# Some lists of weapons for easy proficiency resolution
|
||||
simple_melee_weapons = (Club, Dagger, Greatclub, Handaxe, Javelin,
|
||||
LightHammer, Mace, Quarterstaff, Sickle, Spear)
|
||||
simple_ranged_weapons = (LightCrossbow, Dart, Shortbow, Sling)
|
||||
simple_weapons = simple_melee_weapons + simple_ranged_weapons
|
||||
|
||||
martial_melee_weapons = (Battleaxe, Flail, Glaive, Greataxe,
|
||||
Greatsword, Halberd, Lance, Longsword, Maul, Morningstar, Pike,
|
||||
Rapier, Scimitar, Shortsword, Trident, WarPick, Warhammer, Whip)
|
||||
martial_ranged_weapons = (Blowgun, HandCrossbow, HeavyCrossbow,
|
||||
Longbow, Net)
|
||||
martial_weapons = martial_melee_weapons + martial_ranged_weapons
|
||||
Binary file not shown.
+11
-2
@@ -16,14 +16,16 @@ constitution = 12
|
||||
intelligence = 13
|
||||
wisdom = 10
|
||||
charisma = 16
|
||||
skill_proficiencies = [
|
||||
skill_proficiencies = (
|
||||
'acrobatics',
|
||||
'deception',
|
||||
'investigation',
|
||||
'performance',
|
||||
'sleight_of_hand',
|
||||
'stealth'
|
||||
]
|
||||
)
|
||||
proficiencies_extra = ('playing cards', "carpenter's tools")
|
||||
languages = 'Common, halfling'
|
||||
|
||||
# Inventory
|
||||
cp = 950
|
||||
@@ -31,6 +33,13 @@ sp = 75
|
||||
ep = 50
|
||||
gp = 120
|
||||
pp = 0
|
||||
weapons = ('shortsword', 'shortbow')
|
||||
equipment = (
|
||||
"""Shortsword, shortbow, 20 arrows, leather armor, thieves’ tools,
|
||||
backpack, bell, 5 candles, crowbar, hammer, 10 pitons, 50 feet of
|
||||
hempen rope, hooded lantern, 2 flasks of oil, 5 days rations,
|
||||
tinderbox, waterskin, crowbar, set of dark common clothes
|
||||
including a hood, pouch.""")
|
||||
|
||||
# Backstory
|
||||
personality_traits = """I never have a plan, but I’m great at making things up as I go
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from distutils.core import setup
|
||||
# from setuptools import setup
|
||||
|
||||
setup(name='dungeonsheets',
|
||||
version='0.1dev',
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
from unittest import TestCase
|
||||
|
||||
from dungeonsheets.character import Character
|
||||
from dungeonsheets.weapons import Weapon, Shortsword
|
||||
|
||||
|
||||
class TestCharacter(TestCase):
|
||||
@@ -22,6 +23,37 @@ class TestCharacter(TestCase):
|
||||
char = Character()
|
||||
char.set_attrs(name='Inara')
|
||||
self.assertEqual(char.name, 'Inara')
|
||||
# Check that the weapons get loaded as objects not string
|
||||
char.set_attrs(weapons=['shortsword'])
|
||||
self.assertEqual(len(char.weapons), 1)
|
||||
self.assertTrue(isinstance(char.weapons[0], Shortsword))
|
||||
|
||||
def test_wield_weapon(self):
|
||||
char = Character()
|
||||
char.strength = 14
|
||||
char.weapon_proficienies = [Shortsword]
|
||||
# Add a weapon
|
||||
char.wield_weapon('shortsword')
|
||||
self.assertEqual(len(char.weapons), 1)
|
||||
sword = char.weapons[0]
|
||||
self.assertTrue(isinstance(sword, Weapon))
|
||||
self.assertTrue(isinstance(sword, Shortsword))
|
||||
self.assertEqual(sword.attack_bonus, 4) # str + prof
|
||||
self.assertEqual(sword.bonus_damage, 2) # str
|
||||
# Check if dexterity is used if it's higher (Finesse weapon)
|
||||
char.weapons = []
|
||||
char.dexterity = 16
|
||||
char.wield_weapon('shortsword')
|
||||
sword = char.weapons[0]
|
||||
self.assertEqual(sword.attack_bonus, 5) # dex + prof
|
||||
|
||||
def test_proficiencies_text(self):
|
||||
char = Character()
|
||||
char._proficiencies_text = ('hello', 'world')
|
||||
self.assertEqual(char.proficiencies_text, 'Hello, world.')
|
||||
# Check for extra proficiencies
|
||||
char.proficiencies_extra = ("it's", "me")
|
||||
self.assertEqual(char.proficiencies_text, "Hello, world, it's, me.")
|
||||
|
||||
def test_proficiency_bonus(self):
|
||||
char = Character()
|
||||
|
||||
@@ -10,7 +10,7 @@ class CharacterFileTestCase(unittest.TestCase):
|
||||
def test_load_character_file(self):
|
||||
charfile = CHARFILE
|
||||
result = make_sheets.load_character_file(charfile)
|
||||
self.assertEqual(result['strength'], 10)
|
||||
self.assertEqual(result['strength'], 8)
|
||||
|
||||
class FDFTestCase(unittest.TestCase):
|
||||
def tearDown(self):
|
||||
@@ -22,4 +22,3 @@ class FDFTestCase(unittest.TestCase):
|
||||
char = character.Character()
|
||||
make_sheets.create_fdf(char, fdfname=fdfname)
|
||||
self.assertTrue(os.path.exists(fdfname))
|
||||
|
||||
|
||||
@@ -70,3 +70,20 @@ class TestStats(TestCase):
|
||||
# Check for a proficiency
|
||||
my_class.skill_proficiencies = ['acrobatics']
|
||||
self.assertEqual(my_class.acrobatics, 4)
|
||||
|
||||
def test_findattr(self):
|
||||
"""Check if the function can find attributes."""
|
||||
class TestClass():
|
||||
my_attr = 47
|
||||
YourAttr = 53
|
||||
test_class = TestClass()
|
||||
# Direct access
|
||||
self.assertEqual(stats.findattr(test_class, 'my_attr'),
|
||||
test_class.my_attr)
|
||||
self.assertEqual(stats.findattr(test_class, 'YourAttr'),
|
||||
test_class.YourAttr)
|
||||
# Swapping spaces for capitalization
|
||||
self.assertEqual(stats.findattr(test_class, 'my attr'),
|
||||
test_class.my_attr)
|
||||
self.assertEqual(stats.findattr(test_class, 'your attr'),
|
||||
test_class.YourAttr)
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import unittest
|
||||
|
||||
from dungeonsheets.weapons import Weapon
|
||||
|
||||
class WeaponTestCase(unittest.TestCase):
|
||||
def test_weapon_damage(self):
|
||||
weapon = Weapon()
|
||||
weapon.base_damage = '1d6'
|
||||
self.assertEqual(weapon.damage, '1d6')
|
||||
# Now add some bonus damage
|
||||
weapon.bonus_damage = 2
|
||||
self.assertEqual(weapon.damage, '1d6 +2')
|
||||
Reference in New Issue
Block a user