Finished the basic functionality of the character creator.

This commit is contained in:
Mark Wolfman
2018-04-23 14:06:15 -05:00
parent 31f5a32b17
commit 94d502462a
9 changed files with 245 additions and 97 deletions
+5 -1
View File
@@ -35,7 +35,11 @@ The PDF's can then be generated using the ``makesheets`` command.
.. code:: bash
$ cd examples
$ makesheets
$ makesheets wizard.py
dungeon-sheets contains definitions for standard weapons and spells,
so attack bonuses and damage can be calculated automatically.
If you'd like a **step-by-step walkthrough** for creating a new
character, just run ``create-character`` from a command line and a
helpful menu system will take care of the basics for you.
+95
View File
@@ -0,0 +1,95 @@
class Background():
name = "Generic background"
skill_proficiencies = ()
languages = ()
class Acolyte(Background):
name = "Acolyte"
skill_proficiencies = ('insight', 'religion')
languages = ("[choose one]", "[choose one]")
class Charlatan(Background):
name = "Charlatan"
skill_proficiencies = ('deception', 'sleight of hand')
class Criminal(Background):
name = "Criminal"
skill_proficiencies = ('deception', 'stealth')
class Spy(Criminal):
name = "Spy"
class Entertainer(Background):
name = "Entertainer"
skill_proficiencies = ('acrobatic', 'performance')
class Gladiator(Entertainer):
name = "Gladiator"
class FolkHero(Background):
name = "Folk Hero"
skill_proficiencies = ('animal handling', 'survival')
class GuildArtisan(Background):
name = "Guild Artisan"
skill_proficiencies = ('insight', 'persuasion')
languages = ("[choose one]", "[choose one]")
class GuildMerchant(GuildArtisan):
name = "Guild Merchant"
class Hermit(Background):
name = "Hermit"
skill_proficiencies = ("medicine", "religion")
languages = ("[choose one]")
class Noble(Background):
name = "Noble"
skill_proficiencies = ("history", 'persuasion')
languages = ("[choose one]", )
class Knight(Noble):
name = "Knight"
class Outlander(Background):
name = "Outlander"
skill_proficiencies = ('athletics', 'survival')
languages = ("[choose one]", )
class Sage(Background):
name = "Sage"
skill_proficiencies = ('arcana', 'history')
languages = ("[choose one]", '[choose one]')
class Sailor(Background):
name = "Sailor"
skill_proficiencies = ('athletics', 'perception')
class Pirate(Sailor):
name = "Pirate"
class Soldier(Background):
name = "Soldier"
skill_proficiencies = ('athletics', 'intimidation')
class Urchin(Background):
name = "Urchin"
skill_proficiencies = ('sleight of hand', 'stealth')
+2
View File
@@ -210,6 +210,7 @@ class Character():
directly.
"""
if new_armor not in ('', None):
try:
NewArmor = findattr(armor, new_armor)
except AttributeError:
@@ -227,6 +228,7 @@ class Character():
directly.
"""
if shield not in ('', None):
try:
NewShield = findattr(armor, shield)
except AttributeError:
+35 -59
View File
@@ -1,7 +1,14 @@
"""This file describes the heroic adventurer {{ char.name }}.
Modify this file as you level up and then re-generate the character
sheet by running ``makesheets`` from the command line.
"""
name = '{{ char.name }}'
character_class = '{{ char.class_name }}'
player_name = '{{ char.player_name }}'
background = "{{ char.background }}"
background = "{{ char.background.name }}"
race = "{{ char.race.name }}"
level = {{ char.level }}
alignment = "{{ char.alignment }}"
@@ -15,80 +22,49 @@ constitution = {{ char.constitution.value }}
intelligence = {{ char.intelligence.value }}
wisdom = {{ char.wisdom.value }}
charisma = {{ char.charisma.value }}
skill_proficiencies = (
# Put your skill proficiencies here, including the {{ char.race }} race.
)
skill_proficiencies = {{ char.skill_proficiencies }}
# Proficiencies and languages
languages = "Common, Elvish, Draconic, Dwarvish, Goblin."
languages = "{{ char.languages }}"
# Inventory
# TODO: Get yourself some money
cp = 0
sp = 0
ep = 0
gp = 0
pp = 0
weapons = ('shortsword', 'longsword')
equipment = (
"""Gallon of ale, red cloak, shortsword, longsword, jar of salt, vodka
(500mL), potion of vitality, wand of magic missiles (7/7),
component pouch, spellbook, backpack, bottle of ink, ink pen, 10
sheets of parchment, small knife, tome of historical lore, holy
symbol, prayer book, set of common clothes, pouch.""")
# TODO: Put your equipped weapons and armor here
weapons = () # Example: ('shortsword', 'longsword')
armor = "" # Eg "light leather armor"
shield = "" # Eg "shield"
equipment = "TODO: Describe your equipment from your {{ char.class_name }} class and {{ char.background.name }} background."
attacks_and_spellcasting = "TODO: Describe specifics for how your {{ char.class_name }} attacks."
# List of known spells
spells = ('blindness deafness', 'burning hands', 'detect magic',
'false life', 'mage armor', 'mage hand', 'magic missile',
'prestidigitation', 'ray of frost', 'ray of sickness', 'shield',
'shocking grasp', 'sleep', 'some other spell')
# Example: spells = ('magic missile', 'mage armor')
spells = () # Todo: Learn some spells
# Which spells have been prepared (not including cantrips)
spells_prepared = ('blindness deafness', 'false life', 'mage armor',
'ray of sickness', 'shield', 'sleep',)
spells_prepared = ()
# Backstory
personality_traits = """I use polysyllabic words that convey the impression of
erudition. Also, Ive spent so long in the temple that I have little
experience dealing with people on a casual basis."""
# TODO: Describe your backstory here
personality_traits = """I am a leaf on the wind,
watch how I...
"""
ideals = """Knowledge. The path to power and self-improvement is through
knowledge."""
ideals = """
"""
bonds = """The tome I carry with me is the record of my lifes work so far,
and no vault is secure enough to keep it safe."""
bonds = """
"""
flaws = """Ill do just about anything to uncover historical secrets that
would add to my research."""
flaws = """
"""
features_and_traits = (
"""Spellcasting Ability: Intelligence is your spellcasting ability for
your spells. The saving throw DC to resist a spell you cast is
13. Your attack bonus when you make an attack with a spell is
+5. See the rulebook for rules on casting your spells.
Arcane Recovery: You can regain some of your magical energy by
studying your spellbook. Once per day during a short rest, you can
choose to recover expended spell slots with a combined level equal
to or less than half your wizard level (rounded up).
Darkvision: You see in dim light within a 60-foot radius of you as
if it were bright light, and in darkness in that radius as if it
were dim light. You cant discern color in darkness, only shades
of gray.
Fey Ancestry: You have advantage on saving throws against being
charmed, and magic cant put you to sleep.
Trance: Elves dont need to sleep. They meditate deeply, remaining
semiconscious, for 4 hours a day and gain the same benefit a human
does from 8 hours of sleep.
Shelter of the Faithful: As a servant of Oghma, you command the
respect of those who share your faith, and you can perform the
rites of Oghma. You and your companions can expect to receive free
healing and care at a temple, shrine, or other established
presence of Oghmas faith. Those who share your religion will
support you (and only you) at a modest lifestyle. You also have
ties to the temple of Oghma in Neverwinter, where you have a
residence. When you are in Neverwinter, you can call upon the
priests there for assistance that wont endanger them.""")
features_and_traits = """
"""
+70 -13
View File
@@ -3,17 +3,18 @@
"""Launch a system to interactively create a character."""
import logging
logging.basicConfig(filename='character_creater.log', level=logging.DEBUG)
# logging.basicConfig(filename='character_creater.log', level=logging.DEBUG)
log = logging.getLogger(__name__)
import math
import os
from random import randint
import subprocess
import npyscreen
import jinja2
from dungeonsheets import character, race, dice
from dungeonsheets import character, race, dice, background
char_classes = {
'Barbarian': character.Barbarian,
@@ -49,6 +50,18 @@ races = {
}
backgrounds = (background.Acolyte, background.Charlatan,
background.Criminal, background.Spy,
background.Entertainer, background.Gladiator,
background.FolkHero, background.GuildArtisan,
background.GuildMerchant, background.Hermit,
background.Noble, background.Knight,
background.Outlander, background.Sage,
background.Sailor, background.Pirate,
background.Soldier, background.Urchin)
backgrounds = {bg.name: bg for bg in backgrounds}
class App(npyscreen.NPSAppManaged):
# STARTING_FORM = 'SKILLS'
character = None
@@ -68,6 +81,10 @@ class App(npyscreen.NPSAppManaged):
filename = self.getForm("SAVE").filename.value
with open(filename, mode='w') as f:
f.write(text)
# Create the PDF character sheet
if self.getForm('SAVE').make_pdf.value:
log.debug("Creating PDF")
subprocess.call(['makesheets', filename])
@property
def character_class(self, *args, **kwargs):
@@ -114,18 +131,34 @@ class App(npyscreen.NPSAppManaged):
class SkillForm(npyscreen.ActionForm):
def while_editing(self):
self.skill_proficiencies.set_values(self.parentApp.character.class_skill_choices)
# Update the static skills for race and background
bg_skills = self.parentApp.character.background.skill_proficiencies
self.bg_skills.value = str(bg_skills)[1:-1].replace("'", "")
race_skills = self.parentApp.character.race.skill_proficiencies
self.race_skills.value = str(race_skills)[1:-1].replace("'", "")
# Now set the available discretionary choices
choices = self.parentApp.character.class_skill_choices
static_skills = bg_skills + race_skills
choices = (c for c in choices if c.lower() not in static_skills)
self.skill_proficiencies.set_values(tuple(choices))
self.update_remaining()
def update_remaining(self, widget=None):
remaining = self.parentApp.character.num_skill_choices - len(self.skill_proficiencies.value)
num_choices = self.parentApp.character.num_skill_choices
num_selected = len(self.skill_proficiencies.value)
remaining = num_choices - num_selected
log.debug(f'Remaining: {remaining}')
self.remaining.value = str(remaining)
self.display()
def create(self):
self.bg_skills = self.add(
npyscreen.TitleText, name="Background:",
value="", editable=False)
self.race_skills = self.add(
npyscreen.TitleText, name="Racial:",
value="", editable=False)
self.remaining = self.add(
npyscreen.TitleText, name="Remaining:",
value=0, editable=False)
@@ -135,11 +168,22 @@ class SkillForm(npyscreen.ActionForm):
value_changed_callback=self.update_remaining)
def on_ok(self):
new_skills = self.skill_proficiencies.get_selected_objects()
if new_skills is not None:
new_skills = tuple(s.lower() for s in new_skills)
else:
new_skills = ()
bg_skills = tuple(self.parentApp.character.background.skill_proficiencies)
race_skills = tuple(self.parentApp.character.race.skill_proficiencies)
all_skills = new_skills + bg_skills + race_skills
self.parentApp.character.skill_proficiencies = all_skills
log.debug(f"Skill proficiencies: {all_skills}")
self.parentApp.setNextForm('SAVE')
def on_cancel(self):
self.parentApp.setNextForm('BACKGROUND')
class AbilityScoreForm(npyscreen.ActionForm):
def roll_dice(self):
"""Get six ability scores that can then be assigned to abilities."""
@@ -226,18 +270,22 @@ class CharacterClassForm(npyscreen.ActionForm):
class BackgroundForm(npyscreen.ActionForm):
backgrounds = ('Acolyte', 'Charlatan', 'Criminal', 'Entertainer',
'Folk hero', 'Guild Artison', 'Hermit', 'Noble', 'Knight',
'Outlander', 'Pirate', 'Sage', 'Sailor', 'Soldier', 'Urchin',)
def create(self):
self.background = self.add(npyscreen.TitleMultiLine,
name="Background:", values=self.backgrounds)
self.background = self.add(
npyscreen.TitleMultiLine,
name="Background:", values=tuple(backgrounds.keys()))
def on_ok(self):
if self.background.value is not None:
background = self.backgrounds[self.background.value]
self.parentApp.character.background = background
log.debug("Selected character background: %s", background)
selected_bg = self.background.values[self.background.value]
Background = backgrounds[selected_bg]
self.parentApp.character.background = Background()
# Update the languages based on background and race
race_languages = self.parentApp.character.race.languages
languages = Background.languages + race_languages
self.parentApp.character.languages = ', '.join(languages)
log.debug("Selected character background: %s", Background.name)
self.parentApp.setNextForm('SKILLS')
def on_cancel(self):
@@ -276,6 +324,7 @@ class AlignmentForm(npyscreen.ActionForm):
if self.alignment.value is not None:
selected_alignment = self.alignment.values[self.alignment.value]
log.debug('Selected character alignment %s', selected_alignment)
self.parentApp.character.alignment = selected_alignment
self.parentApp.setNextForm('ABILITIES')
def on_cancel(self):
@@ -312,6 +361,9 @@ class SaveForm(npyscreen.ActionForm):
self.filename = self.add(
npyscreen.TitleText, name='Filename:')
self.make_pdf = self.add(npyscreen.Checkbox, name="Create PDF:", value=True)
self.instructions = self.add(
npyscreen.FixedText, editbale=False,
value="After saving, edit this file to finish your personality, etc.")
def on_ok(self):
self.parentApp.setNextForm(None)
@@ -320,6 +372,7 @@ class SaveForm(npyscreen.ActionForm):
self.parentApp.setNextForm('SKILLS')
def main():
my_app = App()
try:
@@ -328,3 +381,7 @@ except KeyboardInterrupt:
log.error("Aborted by user request")
else:
my_app.save_character()
if __name__ == '__main__':
main()
+1 -1
View File
@@ -222,7 +222,7 @@ def create_character_pdf(character, basename, flatten=False):
}
# Add skill proficienies
for skill in character.skill_proficiencies:
fields.append((skill_boxes[skill], 'Yes'))
fields.append((skill_boxes[skill.replace(' ', '_')], 'Yes'))
# Add weapons
weapon_fields = [('Wpn Name', 'Wpn1 AtkBonus', 'Wpn1 Damage'),
('Wpn Name 2', 'Wpn2 AtkBonus ', 'Wpn2 Damage '),
+13
View File
@@ -11,8 +11,10 @@ class Race():
name = "Unknown"
size = "medium"
speed = 30
languages = ('Common', )
proficiencies_text = tuple()
weapon_proficiences = tuple()
skill_proficiencies = ()
strength_bonus = 0
dexterity_bonus = 0
constitution_bonus = 0
@@ -33,6 +35,7 @@ class Dwarf(Race):
name = "Dwarf"
size = "medium"
speed = 25
languages = ("Common", "Dwarvish")
constitution_bonus = 2
proficiencies_text = ('battleaxes', 'handaxes', 'throwing hammers', 'warhammers')
weapon_proficiences = (weapons.Battleaxe, weapons.Handaxe,
@@ -56,6 +59,8 @@ class Elf(Race):
size = "medium"
speed = 30
dexterity_bonus = 2
skill_proficiencies = ('perception',)
languages = ('Common', 'Elvish')
class HighElf(Elf):
@@ -64,6 +69,7 @@ class HighElf(Elf):
weapons.Shortbow, weapons.Longbow)
proficiencies_text = ('longswords', 'shortswords', 'shortbows', 'longbows')
intelligence_bonus = 1
languages = ('Common', 'Elvish', '[choose one]')
class WoodElf(Elf):
@@ -87,6 +93,7 @@ class Halfling(Race):
size = "small"
speed = 25
dexterity_bonus = 2
languages = ('Common', 'Halfling')
class LightfootHalfling(Halfling):
@@ -110,6 +117,7 @@ class Human(Race):
intelligence_bonus = 1
wisdom_bonus = 1
charisma_bonus = 1
languages = ("Common", '[choose one]')
# Dragonborn
@@ -119,6 +127,7 @@ class Dragonborn(Race):
speed = 30
strength_bonus = 2
charisma_bonus = 1
languages = ("Common", "Draconic")
# Gnomes
@@ -127,6 +136,7 @@ class Gnome(Race):
size = "small"
speed = 25
intelligence_bonus = 2
languages = ("Common", "Gnomish")
class ForestGnome(Gnome):
@@ -145,6 +155,7 @@ class HalfElf(Race):
size = "medium"
speed = 30
charisma_bonus = 2
languages = ("Common", "Elvish", "[choose one]")
# Half-Orcs
@@ -154,6 +165,7 @@ class HalfOrc(Race):
speed = 30
strength_bonus = 2
constitution_bonus = 1
languages = ("Common", "Orc")
# Tielflings
@@ -163,3 +175,4 @@ class Tiefling(Race):
speed = 30
intelligence_bonus = 1
charisma_bonus = 2
languages = ("Common", "Infernal")
+1 -1
View File
@@ -1,4 +1,4 @@
certifi>=2018.1.18
fdfgen>=0.16
npyscreen
jinja
jinja2
+4 -3
View File
@@ -8,7 +8,7 @@ def read(fname):
setup(name='dungeonsheets',
version='0.2.2',
version='0.3.0',
description='Dungeons and Dragons 5e Character Tools',
long_description=read('README.rst'),
long_description_content_type='text/x-rst',
@@ -23,11 +23,12 @@ setup(name='dungeonsheets',
'dungeonsheets': ['blank-character-sheet-default.pdf', 'blank-spell-sheet-default.pdf']
},
install_requires=[
'fdfgen', 'npyscreen', 'jinja',
'fdfgen', 'npyscreen', 'jinja2',
],
entry_points={
'console_scripts': [
'makesheets = dungeonsheets.make_sheets:main'
'makesheets = dungeonsheets.make_sheets:main',
'create-character = dungeonsheets.create_character:main',
]
},
python_requires='>=3.6',