Monster stat blocks now include XP alongside challenge rating.

This commit is contained in:
Mark Wolfman
2021-08-08 22:01:02 -05:00
parent 7b5aac9247
commit 8fab8911cd
6 changed files with 60 additions and 4 deletions
+2
View File
@@ -4,6 +4,7 @@ from jinja2 import Environment, PackageLoader
from dungeonsheets.stats import mod_str, ability_mod_str, stat_abbreviation
from dungeonsheets.monsters import challenge_rating_to_xp
# A dice string, with optional backticks: ``1d6 + 3``
@@ -33,4 +34,5 @@ def jinja_environment():
jinja_env.filters["mod_str"] = mod_str
jinja_env.filters["ability_mod_str"] = ability_mod_str
jinja_env.filters["stat_abbreviation"] = stat_abbreviation
jinja_env.filters["challenge_rating_to_xp"] = challenge_rating_to_xp
return jinja_env
+1 -1
View File
@@ -62,7 +62,7 @@
[% if monster.damage_vulnerabilities != "" %]<dt>Damage Vulnerabilities</dt><dd>[[ monster.damage_vulnerabilities ]]</dd>[% endif %]
[% if monster.condition_immunities != "" %]<dt>Condition Immunuties</dt><dd>[[ monster.condition_immunities ]]</dd>[% endif %]
[% if monster.saving_throws != "" %]<dt>Saving Throws</dt><dd>[[ monster.saving_throws ]]</dd>[% endif %]
<dt>Challenge<dd>[[ monster.challenge_rating ]]&nbsp;</dd>
<dt>Challenge<dd>[[ monster.challenge_rating ]] ([[ monster.challenge_rating | challenge_rating_to_xp ]] XP)</dd>
</dl>
[% if monster.spells | length > 0 %]
+2 -2
View File
@@ -32,7 +32,7 @@
condition-immunities = {[[ monster.condition_immunities ]]},
senses = {[[ monster.senses ]]},
languages = {[% if monster.languages %][[ monster.languages ]][% else %] --- [% endif %]},
challenge = {[[ monster.challenge_rating ]]},
challenge = {[[ monster.challenge_rating ]] ([[ monster.challenge_rating | challenge_rating_to_xp ]] XP)},
]
%\DndMonsterSection{Actions}
[[ monster.__doc__ | rst_to_latex(top_heading_level=2) ]]
@@ -88,7 +88,7 @@
[% if monster.damage_vulnerabilities != "" %]\item [Damage Vulnerabilities:] [[ monster.damage_vulnerabilities ]][% endif %]
[% if monster.condition_immunities != "" %]\item [Condition Immunuties:] [[ monster.condition_immunities ]][% endif %]
[% if monster.saving_throws != "" %]\item [Saving Throws:] [[ monster.saving_throws ]][% endif %]
\item [Challenge:] [[ monster.challenge_rating ]]
\item [Challenge:] [[ monster.challenge_rating ]] ([[ monster.challenge_rating | challenge_rating_to_xp ]] XP)
\end{description}
\vspace{0.2cm}
+47
View File
@@ -10,6 +10,53 @@ from dungeonsheets.content import Creature
from dungeonsheets.spells import Spell
xp_by_challenge_rating = {
0: 10,
(1 / 8): 25,
(1 / 4): 50,
(1 / 2): 100,
1: 200,
2: 450,
3: 700,
4: 1100,
5: 1800,
6: 2300,
7: 2900,
8: 3900,
9: 5000,
10: 5900,
11: 7200,
12: 8400,
13: 10000,
14: 11500,
15: 13000,
16: 15000,
17: 18000,
18: 20000,
19: 22000,
20: 25000,
21: 33000,
22: 41000,
23: 50000,
24: 62000,
25: 75000,
26: 90000,
27: 105000,
28: 120000,
29: 130000,
30: 155000,
}
def challenge_rating_to_xp(cr):
"""Determine the XP awarded for slaying a monster with the given
challenge rating *cr*.
"""
xp = xp_by_challenge_rating[cr]
return "{:,}".format(xp)
class SpellFactory(ABCMeta):
"""Meta class to resolve spell strings into the ``spells.Spell``.
+3 -1
View File
@@ -135,7 +135,7 @@ class VashtaNerada(monsters.Monster):
damage_immunities = "Bludgeoning"
damage_resistances = "Lightning"
damage_vulnerabilities = "Wood-based"
challenge_rating = 93
challenge_rating = 30
spells = ["wish"]
@@ -159,6 +159,7 @@ class HtmlCreatorTestCase(unittest.TestCase):
self.assertIn(r"Damage Vulnerabilities", html)
self.assertIn(r"Senses", html)
self.assertIn(r"Challenge", html)
self.assertIn(r"(155,000 XP)", html)
self.assertIn(r"Languages", html)
self.assertIn(r"Skills", html)
self.assertIn(r"petrified", html)
@@ -252,6 +253,7 @@ class TexCreatorTestCase(unittest.TestCase):
self.assertIn(r"Damage Vulnerabilities:", tex)
self.assertIn(r"Senses:", tex)
self.assertIn(r"Challenge:", tex)
self.assertIn(r"(155,000 XP)", tex)
self.assertIn(r"Languages:", tex)
self.assertIn(r"Skills:", tex)
# Check fancy extended properties
+5
View File
@@ -362,3 +362,8 @@ class MonsterSpellcastingTests(TestCase):
msg="Monster spell is not a class")
self.assertTrue(issubclass(MyMonster.spells[0], spells.Spell))
class ChallengeRatingTests(TestCase):
def test_challenge_rating_to_xp(self):
self.assertEqual(monsters.challenge_rating_to_xp(1 / 8), "25")
self.assertEqual(monsters.challenge_rating_to_xp(30), "155,000")