mirror of
https://github.com/Threnklyn/dungeon-sheets.git
synced 2026-06-07 21:23:31 +02:00
All examples now build, plus added examples to travis build.
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
# Generated output PDFs (and intermediate files)
|
# Generated output PDFs (and intermediate files)
|
||||||
examples/*.pdf
|
examples/*.pdf
|
||||||
examples/*.aux
|
examples/*.aux
|
||||||
|
examples/*.tex
|
||||||
|
|
||||||
# Emacs temp files
|
# Emacs temp files
|
||||||
*~
|
*~
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ python:
|
|||||||
- "3.6"
|
- "3.6"
|
||||||
- "3.7"
|
- "3.7"
|
||||||
- "3.8"
|
- "3.8"
|
||||||
|
before_install:
|
||||||
|
- sudo add-apt-repository -y ppa:malteworld/ppa
|
||||||
|
- sudo apt -q update
|
||||||
|
- sudo apt-get -y install pdftk
|
||||||
# command to install dependencies
|
# command to install dependencies
|
||||||
install:
|
install:
|
||||||
- pip install -r requirements.txt
|
- pip install -r requirements.txt
|
||||||
|
|||||||
@@ -24,10 +24,11 @@ character sheet."""
|
|||||||
|
|
||||||
bold_re = re.compile(r'\*\*([^*]+)\*\*')
|
bold_re = re.compile(r'\*\*([^*]+)\*\*')
|
||||||
it_re = re.compile(r'\*([^*]+)\*')
|
it_re = re.compile(r'\*([^*]+)\*')
|
||||||
tt_re = re.compile(r'``([^`]+)``')
|
verb_re = re.compile(r'``([^`]+)``')
|
||||||
# A dice string, with optinal backticks: ``1d6 + 3``
|
# A dice string, with optinal backticks: ``1d6 + 3``
|
||||||
dice_re = re.compile(r'`*(\d+d\d+(?:\s*\+\s*\d+)?)`*')
|
dice_re = re.compile(r'`*(\d+d\d+(?:\s*\+\s*\d+)?)`*')
|
||||||
|
|
||||||
|
|
||||||
def rst_to_latex(rst):
|
def rst_to_latex(rst):
|
||||||
"""Basic markup of RST to LaTeX code."""
|
"""Basic markup of RST to LaTeX code."""
|
||||||
if rst is None:
|
if rst is None:
|
||||||
@@ -38,10 +39,11 @@ def rst_to_latex(rst):
|
|||||||
tex = tex.replace(c, '\\' + c)
|
tex = tex.replace(c, '\\' + c)
|
||||||
tex = bold_re.sub(r'\\textbf{\1}', tex)
|
tex = bold_re.sub(r'\\textbf{\1}', tex)
|
||||||
tex = it_re.sub(r'\\textit{\1}', tex)
|
tex = it_re.sub(r'\\textit{\1}', tex)
|
||||||
|
tex = verb_re.sub(r'\\begin{verbatim}{\1}\\end{verbatim}', tex)
|
||||||
tex = dice_re.sub(r'\\texttt{\1}', tex)
|
tex = dice_re.sub(r'\\texttt{\1}', tex)
|
||||||
tex = tt_re.sub(r'\\texttt{\1}', tex)
|
|
||||||
for c in ['#', '$', '%', '&', '~', '_', '^']:
|
for c in ['#', '$', '%', '&', '~', '_', '^']:
|
||||||
tex = tex.replace(c, '\\' + c)
|
tex = tex.replace(c, '\\' + c)
|
||||||
|
|
||||||
return tex
|
return tex
|
||||||
|
|
||||||
|
|
||||||
@@ -70,22 +72,22 @@ def text_box(string):
|
|||||||
return new_string
|
return new_string
|
||||||
|
|
||||||
|
|
||||||
def create_druid_shapes_pdf(character, basename):
|
def create_druid_shapes_pdf(character, basename, keep_temp_files=False):
|
||||||
template = jinja_env.get_template('druid_shapes_template.tex')
|
template = jinja_env.get_template('druid_shapes_template.tex')
|
||||||
return create_latex_pdf(character, basename, template)
|
return create_latex_pdf(character, basename, template, keep_temp_files=keep_temp_files)
|
||||||
|
|
||||||
|
|
||||||
def create_spellbook_pdf(character, basename):
|
def create_spellbook_pdf(character, basename, keep_temp_files=False):
|
||||||
template = jinja_env.get_template('spellbook_template.tex')
|
template = jinja_env.get_template('spellbook_template.tex')
|
||||||
return create_latex_pdf(character, basename, template)
|
return create_latex_pdf(character, basename, template, keep_temp_files=keep_temp_files)
|
||||||
|
|
||||||
|
|
||||||
def create_features_pdf(character, basename):
|
def create_features_pdf(character, basename, keep_temp_files=False):
|
||||||
template = jinja_env.get_template('features_template.tex')
|
template = jinja_env.get_template('features_template.tex')
|
||||||
return create_latex_pdf(character, basename, template)
|
return create_latex_pdf(character, basename, template, keep_temp_files=keep_temp_files)
|
||||||
|
|
||||||
|
|
||||||
def create_latex_pdf(character, basename, template):
|
def create_latex_pdf(character, basename, template, keep_temp_files=False):
|
||||||
tex = template.render(character=character)
|
tex = template.render(character=character)
|
||||||
# Create tex document
|
# Create tex document
|
||||||
tex_file = f'{basename}.tex'
|
tex_file = f'{basename}.tex'
|
||||||
@@ -109,19 +111,22 @@ def create_latex_pdf(character, basename, template):
|
|||||||
# Compile the PDF
|
# Compile the PDF
|
||||||
pdf_file = f'{basename}.pdf'
|
pdf_file = f'{basename}.pdf'
|
||||||
output_dir = os.path.abspath(os.path.dirname(pdf_file))
|
output_dir = os.path.abspath(os.path.dirname(pdf_file))
|
||||||
|
tex_command_line = ['pdflatex', '--output-directory', output_dir,
|
||||||
|
'-halt-on-error', '-interaction=nonstopmode',
|
||||||
|
tex_file]
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(['pdflatex', '--output-directory',
|
result = subprocess.run(tex_command_line,
|
||||||
output_dir, tex_file, '-halt-on-error'],
|
|
||||||
stdout=subprocess.DEVNULL, timeout=30)
|
stdout=subprocess.DEVNULL, timeout=30)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
# Remove temporary files
|
# Remove temporary files
|
||||||
remove_temp_files(basename)
|
remove_temp_files(basename)
|
||||||
raise exceptions.LatexNotFoundError()
|
raise exceptions.LatexNotFoundError()
|
||||||
else:
|
else:
|
||||||
if result.returncode == 0:
|
if result.returncode == 0 and not keep_temp_files:
|
||||||
remove_temp_files(basename)
|
remove_temp_files(basename)
|
||||||
else:
|
if result.returncode != 0:
|
||||||
raise exceptions.LatexError(f'Processing of {basename}.tex failed.')
|
raise exceptions.LatexError(f'Processing of {basename}.tex failed.'
|
||||||
|
f' See {basename}.log for details.')
|
||||||
|
|
||||||
|
|
||||||
def create_spells_pdf(character, basename, flatten=False):
|
def create_spells_pdf(character, basename, flatten=False):
|
||||||
@@ -431,7 +436,7 @@ def _make_pdf_pdftk(fields, src_pdf, basename, flatten=False):
|
|||||||
os.remove(fdfname)
|
os.remove(fdfname)
|
||||||
|
|
||||||
|
|
||||||
def make_sheet(character_file, character=None, flatten=False):
|
def make_sheet(character_file, character=None, flatten=False, debug=False):
|
||||||
"""Prepare a PDF character sheet from the given character file.
|
"""Prepare a PDF character sheet from the given character file.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
@@ -463,7 +468,7 @@ def make_sheet(character_file, character=None, flatten=False):
|
|||||||
feat_base = '{:s}_feats'.format(
|
feat_base = '{:s}_feats'.format(
|
||||||
os.path.splitext(character_file)[0])
|
os.path.splitext(character_file)[0])
|
||||||
try:
|
try:
|
||||||
create_features_pdf(character=character, basename=feat_base)
|
create_features_pdf(character=character, basename=feat_base, keep_temp_files=debug)
|
||||||
except exceptions.LatexNotFoundError as e:
|
except exceptions.LatexNotFoundError as e:
|
||||||
log.warning('``pdflatex`` not available. Skipping features book '
|
log.warning('``pdflatex`` not available. Skipping features book '
|
||||||
f'for {character.name}')
|
f'for {character.name}')
|
||||||
@@ -473,7 +478,7 @@ def make_sheet(character_file, character=None, flatten=False):
|
|||||||
# Create spell book
|
# Create spell book
|
||||||
spellbook_base = os.path.splitext(character_file)[0] + '_spellbook'
|
spellbook_base = os.path.splitext(character_file)[0] + '_spellbook'
|
||||||
try:
|
try:
|
||||||
create_spellbook_pdf(character=character, basename=spellbook_base)
|
create_spellbook_pdf(character=character, basename=spellbook_base, keep_temp_files=debug)
|
||||||
except exceptions.LatexNotFoundError as e:
|
except exceptions.LatexNotFoundError as e:
|
||||||
log.warning('``pdflatex`` not available. Skipping spellbook '
|
log.warning('``pdflatex`` not available. Skipping spellbook '
|
||||||
f'for {character.name}')
|
f'for {character.name}')
|
||||||
@@ -484,7 +489,7 @@ def make_sheet(character_file, character=None, flatten=False):
|
|||||||
if len(wild_shapes) > 0:
|
if len(wild_shapes) > 0:
|
||||||
shapes_base = os.path.splitext(character_file)[0] + '_wild_shapes'
|
shapes_base = os.path.splitext(character_file)[0] + '_wild_shapes'
|
||||||
try:
|
try:
|
||||||
create_druid_shapes_pdf(character=character, basename=shapes_base)
|
create_druid_shapes_pdf(character=character, basename=shapes_base, keep_temp_files=debug)
|
||||||
except exceptions.LatexNotFoundError as e:
|
except exceptions.LatexNotFoundError as e:
|
||||||
log.warning('``pdflatex`` not available. Skipping wild shapes list '
|
log.warning('``pdflatex`` not available. Skipping wild shapes list '
|
||||||
f'for {character.name}')
|
f'for {character.name}')
|
||||||
@@ -546,7 +551,7 @@ def main():
|
|||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
print(f"Processing {os.path.splitext(filename)[0]}...", end='')
|
print(f"Processing {os.path.splitext(filename)[0]}...", end='')
|
||||||
try:
|
try:
|
||||||
make_sheet(character_file=filename, flatten=(not args.editable))
|
make_sheet(character_file=filename, flatten=(not args.editable), debug=args.debug)
|
||||||
except exceptions.CharacterFileFormatError as e:
|
except exceptions.CharacterFileFormatError as e:
|
||||||
# Only raise the failed exception if this file is explicitly given
|
# Only raise the failed exception if this file is explicitly given
|
||||||
print('invalid')
|
print('invalid')
|
||||||
|
|||||||
@@ -87,39 +87,39 @@ class ScorchingRay(Spell):
|
|||||||
|
|
||||||
|
|
||||||
class Scrying(Spell):
|
class Scrying(Spell):
|
||||||
"""You can see and hear a particular creature you choose that is on the same plane
|
"""You can see and hear a particular creature you choose that is on
|
||||||
of existence as you. The target must make a W isdom saving throw, which is
|
the same plane of existence as you. The target must make a W isdom
|
||||||
modified by how well you know the target and the sort of physical connection you
|
saving throw, which is modified by how well you know the target
|
||||||
have to it. If a target knows you're casting this spell, it can fail the saving
|
and the sort of physical connection you have to it. If a target
|
||||||
throw voluntarily if it wants to be observed.
|
knows you're casting this spell, it can fail the saving throw
|
||||||
|
voluntarily if it wants to be observed.
|
||||||
|
|
||||||
Knowledge Save
|
Knowledge - Save Modifier
|
||||||
Modifier
|
=========================
|
||||||
Secondhand (you have heard of the target) +5
|
Secondhand (you have heard of the target) - +5
|
||||||
Firsthand (you have met
|
Firsthand (you have met the target) - +0
|
||||||
the target) +0
|
Familiar (you know the target well) - -5
|
||||||
Familiar (you know the target well) -5
|
|
||||||
|
|
||||||
Connection
|
Connection - Save Modifier
|
||||||
Save Modifier
|
==========================
|
||||||
Likeness or picture -2
|
Likeness or picture - -2
|
||||||
Posession or
|
Possession or garment - -4
|
||||||
garment -4
|
Body part, lock of hair, bit of nail, or the like - -10
|
||||||
Body part, lock of hair, bit of nail, or the like -10
|
|
||||||
|
|
||||||
On
|
On a successful save, the target isn't affected, and you can't use
|
||||||
a successful save, the target isn't affected, and you can't use this spell
|
this spell against it again for 24 hours.
|
||||||
against it again for 24 hours.
|
|
||||||
|
|
||||||
On a failed save, the spell creates an invisible
|
On a failed save, the spell creates an invisible sensor within 10
|
||||||
sensor within 10 feet of the target. You can see and hear through the sensor as
|
feet of the target. You can see and hear through the sensor as if
|
||||||
if you w ere there. The sensor moves with the target, remaining within 10 feet
|
you w ere there. The sensor moves with the target, remaining
|
||||||
of it for the duration. A creature that can see invisible objects sees the
|
within 10 feet of it for the duration. A creature that can see
|
||||||
sensor as a luminous orb about the size of your fist.
|
invisible objects sees the sensor as a luminous orb about the size
|
||||||
|
of your fist.
|
||||||
|
|
||||||
|
Instead of targeting a creature, you can choose a location you
|
||||||
|
have seen before as the target of this spell. When you do, the
|
||||||
|
sensor appears at that location and doesn't move.
|
||||||
|
|
||||||
Instead of targeting a
|
|
||||||
creature, you can choose a location you have seen before as the target of this
|
|
||||||
spell. When you do, the sensor appears at that location and doesn't move.
|
|
||||||
"""
|
"""
|
||||||
name = "Scrying"
|
name = "Scrying"
|
||||||
level = 5
|
level = 5
|
||||||
@@ -1508,7 +1508,7 @@ class Symbol(Spell):
|
|||||||
Each target must make a Wisdom saving throw and becomes frightened
|
Each target must make a Wisdom saving throw and becomes frightened
|
||||||
for 1 minute on a failed save. While frightened, the target drops whatever it is
|
for 1 minute on a failed save. While frightened, the target drops whatever it is
|
||||||
holding and must move at least 20 feet away from the glyph on each of ist
|
holding and must move at least 20 feet away from the glyph on each of ist
|
||||||
turns, if able.
|
turns, if able.
|
||||||
|
|
||||||
Hopelessness
|
Hopelessness
|
||||||
Each target must make a Charisma saving throw. On
|
Each target must make a Charisma saving throw. On
|
||||||
|
|||||||
@@ -47,3 +47,7 @@ class MarkdownTestCase(unittest.TestCase):
|
|||||||
def test_no_text(self):
|
def test_no_text(self):
|
||||||
text = make_sheets.rst_to_latex(None)
|
text = make_sheets.rst_to_latex(None)
|
||||||
self.assertEqual(text, '')
|
self.assertEqual(text, '')
|
||||||
|
|
||||||
|
def test_verbatim(self):
|
||||||
|
text = make_sheets.rst_to_latex('``hello, world``')
|
||||||
|
self.assertIn(r'\begin{verbatim}', text)
|
||||||
|
|||||||
Reference in New Issue
Block a user