Merge pull request #67 from rkubosz/artificer

Added the Artificer character class
This commit is contained in:
Mark Wolf
2020-05-13 16:55:26 -05:00
committed by GitHub
11 changed files with 1204 additions and 4 deletions
+34 -2
View File
@@ -6,11 +6,13 @@ import os
import re
import subprocess
import warnings
import math
import jinja2
from dungeonsheets import (armor, background, classes, exceptions, features,
magic_items, monsters, race, spells, weapons)
infusions, magic_items, monsters, race, spells,
weapons)
from dungeonsheets.armor import Armor, NoArmor, NoShield, Shield
from dungeonsheets.dice import read_dice_str
from dungeonsheets.stats import (Ability, ArmorClass, Initiative, Skill, Speed,
@@ -27,7 +29,7 @@ __version__ = read('../VERSION').strip()
dice_re = re.compile('(\d+)d(\d+)')
__all__ = ('Barbarian', 'Bard', 'Cleric', 'Druid', 'Fighter', 'Monk',
__all__ = ('Artificer', 'Barbarian', 'Bard', 'Cleric', 'Druid', 'Fighter', 'Monk',
'Paladin', 'Ranger', 'Rogue', 'Sorcerer', 'Warlock', 'Wizard', )
multiclass_spellslots_by_level = {
@@ -128,6 +130,7 @@ class Character():
spellcasting_ability = None
_spells = list()
_spells_prepared = list()
infusions = list()
# Features IN MAJOR DEVELOPMENT
custom_features = list()
feature_choices = list()
@@ -171,6 +174,7 @@ class Character():
self._proficiencies_text = list()
self._spells = list()
self._spells_prepared = list()
self.infusions = list()
self.custom_features = list()
self.feature_choices = list()
@@ -417,6 +421,8 @@ class Character():
eff_level += c.level // 2
elif type(c) in [classes.Fighter, classes.Rogue]:
eff_level += c.level // 3
elif type(c) is classes.Artificer:
eff_level += math.ceil(c.level / 2)
if eff_level == 0:
return 0
else:
@@ -516,6 +522,18 @@ class Character():
else:
# Instantiate them all for the spells list
self._spells_prepared = tuple(S() for S in _spells)
elif attr == 'infusions':
if hasattr(self, 'Artificer'):
_infusions = []
for infusion_name in val:
try:
_infusions.append(findattr(infusions, infusion_name))
except AttributeError:
msg = (f'Infusion "{infusion_name}" not defined. '
f'Please add it to ``infusions.py``')
warnings.warn(msg)
_infusions.sort(key=lambda infusion: infusion.name)
self.infusions = tuple(i() for i in _infusions)
else:
if not hasattr(self, attr):
warnings.warn(f"Setting unknown character attribute {attr}",
@@ -710,6 +728,13 @@ class Character():
if hasattr(self, 'Druid'):
self.Druid.wild_shapes = new_shapes
@property
def infusions_text(self):
if hasattr(self, 'Artificer'):
return tuple([i.name for i in self.infusions])
else:
return ()
@classmethod
def load(cls, character_file):
# Create a character from the character definition
@@ -788,6 +813,13 @@ def read_character_file(filename):
# Add backwards compatability for tests
class Artificer(Character):
def __init__(self, level=1, **attrs):
attrs['classes'] = ['Artificer']
attrs['levels'] = [level]
super().__init__(**attrs)
class Barbarian(Character):
def __init__(self, level=1, **attrs):
attrs['classes'] = ['Barbarian']
+3 -2
View File
@@ -1,7 +1,8 @@
__all__ = ('CharClass', 'Barbarian', 'Bard', 'Cleric', 'Druid', 'Fighter',
__all__ = ('CharClass', 'Artificer', 'Barbarian', 'Bard', 'Cleric', 'Druid', 'Fighter',
'Monk', 'Paladin', 'Ranger', 'Rogue', 'Sorceror', 'Warlock',
'Wizard', 'RevisedRanger', 'available_classes')
from dungeonsheets.classes.artificer import Artificer
from dungeonsheets.classes.barbarian import Barbarian
from dungeonsheets.classes.bard import Bard
from dungeonsheets.classes.classes import CharClass
@@ -16,5 +17,5 @@ from dungeonsheets.classes.sorceror import Sorceror
from dungeonsheets.classes.warlock import Warlock
from dungeonsheets.classes.wizard import Wizard
available_classes = [Barbarian, Bard, Cleric, Druid, Fighter, Monk, Paladin,
available_classes = [Artificer, Barbarian, Bard, Cleric, Druid, Fighter, Monk, Paladin,
Ranger, Rogue, Sorceror, Warlock, Wizard, RevisedRanger]
+127
View File
@@ -0,0 +1,127 @@
from collections import defaultdict
from dungeonsheets import features, weapons
from dungeonsheets.classes.classes import CharClass, SubClass
# Eberron Rising from the Last War
class Alchemist(SubClass):
"""An Alchemist is an expert at combining reagents to produce mystical
effects. Alchemists use their creations to give life and to leech it away.
Alchemy is the oldest of artificer traditions, and its versatility has
long been valued during times of war and peace.
"""
name = "Alchemist"
features_by_level = defaultdict(list)
features_by_level[3] = [features.AlchemistToolProficiency,
features.AlchemistSpells,
features.ExperimentalElixir]
features_by_level[5] = [features.AlchemicalSavant]
features_by_level[9] = [features.RestorativeReagents]
features_by_level[15] = [features.ChemicalMastery]
class Artillerist(SubClass):
"""An Artillerist specializes in using magic to hurl energy, projectiles,
and explosions on a battlefield. This destructive power was valued by all
the armies of the Last War. Now that the war is over, some members of this
specialization have sought to build a more peaceful world by using their
powers to fight the resurgence of strife in Khorvaire. The gnome artificer
Vi, an unlikely yet key member of House Cannith's warforged project, has
been especially vocal about making things right: "It's about time we fixed
things instead of blowing them all to hell."
"""
name = "Artillerist"
features_by_level = defaultdict(list)
features_by_level[3] = [features.ArtilleristSpells,
features.ArtilleristToolProficiency,
features.EldritchCannon]
features_by_level[5] = [features.ArcaneFirearm]
features_by_level[9] = [features.ExplosiveCannon]
features_by_level[15] = [features.FortifiedPosition]
class BattleSmith(SubClass):
"""Armies require protection, and someone has to put things back together
if defenses fail. A combination of protector and medic, a Battle Smith is
an expert at defending others and repairing both material and personnel.
To aid in their work, Battle Smiths are usually accompanied by a steel
defender, a protective compan­ion of their own creation. Many soldiers tell
stories of nearly dying before being saved by a Battle Smith and a steel
defender.
Battle Smiths played a key role in House Cannith's work on battle
constructs and the original warforged, and after the Last War, these
artificers led efforts to aid those who were injured in the war's horrific
battles.
"""
name = "Battle Smith"
features_by_level = defaultdict(list)
features_by_level[3] = [features.BattleSmithSpells,
features.BattleSmithToolProficiency,
features.BattleReady,
features.SteelDefender]
features_by_level[5] = [features.ExtraAttackBattleSmith]
features_by_level[9] = [features.ArcaneJolt]
features_by_level[15] = [features.ImprovedDefender]
class Artificer(CharClass):
name = "Artificer"
hit_dice_faces = 8
subclass_select_level = 3
subclasses_available = (Alchemist, Artillerist, BattleSmith)
saving_throw_proficiencies = ('intelligence', 'constitution')
primary_abilities = ('intelligence',)
_proficiencies_text = (
'Light armor', 'Medium armor', 'Shields', 'Simple weapons',
"Thieve's tools", "Tinker's tools",
"One type of artisan's tools of your choice")
_multiclass_proficiencies_text = (
"Light armor", "Medium armor", "Shields",
"Thieve's tools", "Tinker's tools")
weapon_proficiencies = (weapons.SimpleWeapon,)
infusions = []
class_skill_choices = (
'Arcana', 'History', 'Investigation',
'Medicine', 'Nature', 'Perception', 'Sleight of Hand')
features_by_level = defaultdict(list)
features_by_level[1] = [features.MagicalTinkering,
features.FirearmProficiency,
features.ArtificerSpellcasting,
features.ArtificerRitualCasting]
features_by_level[2] = [features.InfuseItem]
features_by_level[3] = [features.TheRightToolForTheJob]
features_by_level[6] = [features.ToolExpertise]
features_by_level[7] = [features.FlashOfGenius]
features_by_level[10] = [features.MagicItemAdept]
features_by_level[11] = [features.SpellStoringItem]
features_by_level[14] = [features.MagicItemSavant]
features_by_level[18] = [features.MagicItemMaster]
features_by_level[20] = [features.SoulOfArtifice]
spellcasting_ability = 'intelligence'
spell_slots_by_level = {
1: (2, 2, 0, 0, 0, 0, 0, 0, 0, 0),
2: (2, 2, 0, 0, 0, 0, 0, 0, 0, 0),
3: (2, 3, 0, 0, 0, 0, 0, 0, 0, 0),
4: (2, 3, 0, 0, 0, 0, 0, 0, 0, 0),
5: (2, 4, 2, 0, 0, 0, 0, 0, 0, 0),
6: (2, 4, 2, 0, 0, 0, 0, 0, 0, 0),
7: (2, 4, 3, 0, 0, 0, 0, 0, 0, 0),
8: (2, 4, 3, 0, 0, 0, 0, 0, 0, 0),
9: (2, 4, 3, 2, 0, 0, 0, 0, 0, 0),
10: (3, 4, 3, 2, 0, 0, 0, 0, 0, 0),
11: (3, 4, 3, 3, 0, 0, 0, 0, 0, 0),
12: (3, 4, 3, 3, 0, 0, 0, 0, 0, 0),
13: (3, 4, 3, 3, 1, 0, 0, 0, 0, 0),
14: (4, 4, 3, 3, 1, 0, 0, 0, 0, 0),
15: (4, 4, 3, 3, 2, 0, 0, 0, 0, 0),
16: (4, 4, 3, 3, 2, 0, 0, 0, 0, 0),
17: (4, 4, 3, 3, 3, 1, 0, 0, 0, 0),
18: (4, 4, 3, 3, 3, 1, 0, 0, 0, 0),
19: (4, 4, 3, 3, 3, 2, 0, 0, 0, 0),
20: (4, 4, 3, 3, 3, 2, 0, 0, 0, 0)
}
+1
View File
@@ -1,3 +1,4 @@
from dungeonsheets.features.artificer import *
from dungeonsheets.features.backgrounds import *
from dungeonsheets.features.barbarian import *
from dungeonsheets.features.bard import *
+675
View File
@@ -0,0 +1,675 @@
from dungeonsheets import spells
from dungeonsheets.features.features import Feature
class _SpecialistSpells(Feature):
"""Starting at 3rd level, you always have certain spells pre­pared after
you reach particular levels in this class, as shown in the Specialization
Spells table. These spells count as artificer spells for you, but they
don't count against the number of artificer spells you prepare.
"""
_name = "Select One"
source = "Artificer"
_spells = {
3: [],
5: [],
9: [],
13: [],
17: []
}
spells_known = []
spells_prepared = []
@property
def name(self):
return "{:s} Spells".format(self._name)
def __init__(self, owner=None):
if owner is not None:
level = owner.Artificer.level
for lvl, spl in self._spells.items():
if level >= lvl:
self.spells_known.extend(spl)
self.spells_prepared.extend(spl)
super().__init__(owner=owner)
# Alchemist
class ArtificerSpellcasting(Feature):
"""You have studied the workings of magic and how to channel it through
objects. As a result, you have gained the ability to cast spells. To
observers, you don't appear to be casting spells in a conventional way; you
look as if you're producing wonders using mundane items or out­landish
inventions.
**Tools Required** You produce your artificer spell effects through your
tools. You must have a spellcasting focus-specifically thieves' tools or
some kind of artisan's tool-in hand when you cast any spell with this
Spellcasting feature. You must be proficient with the tool to use it in
this way. See chapter 5, "Equipment," in the Player's Handbook for
descriptions of these tools.
After you gain the Infuse Item feature at 2nd level, you can also use any
item bearing one of your infusions as a spellcasting focus.
"""
name = "Spellcasting"
source = "Artificer"
class ArtificerRitualCasting(Feature):
"""You can cast an artificer spell as a ritual if that spell has the ritual
tag and you have the spell prepared.
"""
name = "Ritual Casting"
source = "Artificer"
class FirearmProficiency(Feature):
"""The secrets of creating and operating gunpowder weapons have been
discovered in various corners of the D&D multiverse. If your Dungeon Master
uses the rules on firearms in chapter 9 of the Dungeon Master's Guide and
your artificer has been exposed to the operation of such weapons, your
artificer is proficient with them.
"""
name = "Optional Rule: Firearm Proficiency"
source = "Artificer"
class MagicalTinkering(Feature):
"""At 1st level, you learn how to invest a spark of magic into mundane
objects. To use this ability, you must have tinker's tools or other
artisan's tools in hand. You then touch a Tiny nonmagical object as an
action and give it one of the following magical properties of your choice:
• The object sheds bright light in a 5-foot radius and dim light for an
additional 5 fe et.
• Whenever tapped by a creature, the object emits a recorded message that
can be heard up to 10 feet away. You utter the message when you bestow
this property on the object, and the recording can be no more than 6
seconds long.
• The object continuously emits your choice of an odor or a nonverbal sound
(wind, waves, chirping, or the like). The chosen phenomenon is
perceivable up to 10 feet away.
• A static visual effect appears on one of the object's surfaces. This
effect can be a picture, up to 25 words of text, lines and shapes, or a
mixture of these elements, as you like. The chosen property lasts
indefinitely. As an action, you can touch the object and end the property
early. You can bestow magic on multiple objects, touching one object each
time you use this feature, though a single object can only bear one
property at a time. The maximum number of objects you can affect with
this feature at one time is equal to your Intelligence modifier (minimum
of one object). If you try to exceed your maximum, the oldest property
immediately ends, and then the new property applies.
"""
name = "Magical Tinkering"
source = "Artificer"
class InfuseItem(Feature):
"""At 2nd level, you gain the ability to imbue mundane items with certain
magical infusions. The magic items you create with this feature are
effectively prototypes of permanent items.
**Infusions known**
When you gain this feature, pick four artificer infusions
to learn, choosing from the "Artificer Infusions" section at the end of the
class's description. You learn additional infusions of your choice when you
reach certain levels in this class, as shown in the Infusions Known column
of the Artificer table. Whenever you gain a level in this class, you can
re­place one of the artificer infusions you learned with a new one.
**Infusing an item**
Whenever you finish a long rest, you can touch a non­magical object and
imbue it with one of your artificer in­fusions, turning it into a magic
item. An infusion works on only certain kinds of objects, as specified in
the infusion's description. If the item requires attunement, you can
attune yourself to it the instant you infuse the item. If you decide to
attune to the item later, you must do so using the normal process for
attunement (see "Attunement" in chapter 7 of the Dungeon Master's Guide).
Your infusion remains in an item indefinitely, but when you die, the
infusion vanishes after a number of days have passed equal to your
Intelligence modifier (minimum of 1 day). The infusion also vanishes if you
give up your knowledge of the infusion for another one. You can infuse more
than one nonmagical object at the end of a long rest; the maximum number of
objects appears in the Infused Items column of the Artificer table. You
must touch each of the objects, and each of your infusions can be in only
one object at a time. Moreover, no object can bear more than one of your
infusions at a time. If you try to exceed your maximum number of
in­fusions, the oldest infusion immediately ends, and then the new
infusion applies.
"""
_name = "Infuse Item"
source = "Artificer"
_infusions = {
# level: (infusions known, infused items)
2: (4, 2),
3: (4, 2),
4: (4, 2),
5: (4, 2),
5: (4, 2),
6: (6, 3),
7: (6, 3),
8: (6, 3),
9: (6, 3),
10: (8, 4),
11: (8, 4),
12: (8, 4),
13: (8, 4),
14: (10, 5),
15: (10, 5),
16: (10, 5),
17: (10, 5),
18: (12, 6),
19: (12, 6),
20: (12, 6),
}
@property
def name(self):
known_infusions = self._infusions[self.owner.Artificer.level][0]
infused_items = self._infusions[self.owner.Artificer.level][1]
name_ext = " ({:d} Infusions Known, {:d} Infused Items)"
return self._name + name_ext.format(known_infusions, infused_items)
class ArtificerSpecialist(Feature):
"""At 3rd level, you choose the type of specialist you are: Alchemist,
Artillerist, or Battle Smith, each of which is detailed at the end of the
class's description. Your choice grants you features at 5th level and again
at 9th and 15th level.
"""
name = "Artificer Specialist"
source = "Artificer"
class TheRightToolForTheJob(Feature):
"""At 3rd level, you learn how to produce exactly the tool you need: with
tinker's tools in hand, you can magically create one set of artisan's tools
in an unoccupied space within 5 feet of you. This creation requires 1 hour
of uninterrupted work, which can coincide with a short or long rest.
Though the product of magic, the tools are nonmagical, and they vanish when
you use this feature again.
"""
name = "The Right Tool For The Job"
source = "Artificer"
class ToolExpertise(Feature):
"""Starting at 6th level, your proficiency bonus is doubled for any ability
check you make that uses your proficiency with a tool.
"""
name = "Tool Expertise"
source = "Artificer"
class FlashOfGenius(Feature):
"""Starting at 7th level, you gain the ability to come up with solutions
under pressure. When you or another creature you can see within 30 feet of
you makes an ability check or a saving throw, you can use your reaction to
add your Intelligence modifier to the roll.
You can use this feature a number of times equal to your Intelligence
modifier (minimum of once). You regain all expended uses when you finish a
long rest.
"""
name = "Flash Of Genius"
source = "Arificer"
class MagicItemAdept(Feature):
"""When you reach 10th level, you achieve a profound un­derstanding of how
to use and make magic items:
• You can attune to up to four magic items at once.
• If you craft a magic item with a rarity of common or uncommon, it takes
you a quarter of the normal time, and it costs you half as much of the
usual gold.
"""
name = "Magic Item Adept"
source = "Artificer"
class SpellStoringItem(Feature):
"""At 11th level, you learn how to store a spell in an object. Whenever you
finish a long rest, you can touch one simple or martial weapon or one item
that you can use as a spellcasting focus, and you store a spell in it,
choosing a 1st• or 2nd-level spell from the artificer spell list that
requires 1 action to cast (you needn't have it prepared).
While holding the object, a creature can take an action to produce the
spell's effect from it, using your spellcast­ing ability modifier. If the
spell requires concentration, the creature must concentrate. The spell
stays in the ob­ject until it's been used a number of times equal to twice
your Intelligence modifier (minimum of twice) or until you use this fe
ature again to store a spell in an object.
"""
name = "Spell-Storing Item"
source = "Artificer"
class MagicItemSavant(Feature):
"""At 14th level, your skill with magic items deepens more:
• You can attune to up to five magic items at once.
• You ignore all class, race, spell, and level require­ments on attuning to
or using a magic item.
"""
name = "Magic Item Savant"
source = "Artificer"
class MagicItemMaster(Feature):
"""Starting at 18th level, you can attune to up to six magic items at
once.
"""
name = "Magic Item Master"
source = "Artificer"
class SoulOfArtifice(Feature):
"""At 20th level, you develop a mystical connection to your magic items,
which you can draw on for protection:
• You gain a +1 bonus to all saving throws per magic item you are currently
attuned to.
• If you're reduced to 0 hit points but not killed out­right, you can use
your reaction to end one of your artificer infusions, causing you to drop
to 1 hit point instead of 0.
"""
name = "Soul of Artifice"
source = "Artificer"
# Alchemist
class AlchemistToolProficiency(Feature):
"""When you adopt this specialization at 3rd level, you gain proficiency
with alchemist's supplies. If you already have this proficiency, you gain
proficiency with one other type of artisan's tools of your choice.
"""
name = "Tool Proficiency"
source = "Artificer (Alchemist)"
class AlchemistSpells(_SpecialistSpells):
"""Starting at 3rd level, you always have certain spells pre­pared after
you reach particular levels in this class, as shown in the Alchemist Spells
table. These spells count as artificer spells for you, but they don't count
against the number of artificer spells you prepare.
"""
_name = "Alchemist"
_spells = {
3: [spells.HealingWord, spells.RayOfSickness],
5: [spells.FlamingSphere, spells.MelfsAcidArrow],
9: [spells.GaseousForm, spells.MassHealingWord],
13: [spells.Blight, spells.DeathWard],
17: [spells.Cloudkill, spells.RaiseDead]
}
class ExperimentalElixir(Feature):
"""Beginning at 3rd level, whenever you finish a long rest, you can
magically produce an *experimental elixir* in an empty flask you touch.
Roll on the Experimental Elixir table for the elixir's effect, which is
triggered when someone drinks the elixir. As an action, a creature can
drink the elixir or administer it to an incapacitated creature.
Creating an *experimental elixir* requires you to have alchemist supplies
on your person, and any elixir you create with this feature lasts until it
is drunk or until the end of your next long rest.
When you reach certain levels in this class, you can make more elixirs at
the end of a long rest: two at 6th level and three at 15th level. Roll for
each elixir's effect separately. Each elixir requires its own flask.
You can create additional *experimental elixirs* by expending a spell slot
of 1st level or higher for each one. When you do so, you use your action to
create the elixir in an empty flask you touch, and you choose the elixir's
effect from the Experimental Elixir table.
**Experimental Elixir**
roll d6
**1 -- Healing.** The drinker regains a number of hit points equal to 2d4 +
your Intelligence modifier.
**2 -- Swiftness.** The drinker's walking speed increases by 10 feet for 1
hour.
**3 -- Resilience.** The drinker gains a +1 bonus to AC for 10 minutes.
**4 -- Boldness.** The drinker can roll a d4 and add the num­ber rolled to
every attack roll and saving throw they make for the next minute.
**5 -- Flight.** The drinker gains a flying speed of 10 feet for 10
minutes.
**6 -- Transformation.** The drinker's body is transformed as if by the
alter self spell. The drinker determines the transformation caused by the
spell, the effects of which last for 10 minutes.
"""
name = "Experimental Elixir"
source = "Artificer (Alchemist)"
class AlchemicalSavant(Feature):
"""At 5th level, you develop masterful command of magical chemicals,
enhancing the healing and damage you create through them. Whenever you
cast a spell using your alchemist's supplies as the spellcasting focus, you
gain a bonus to one roll of the spell. That roll must restore hit points or
be a damage roll that deals acid, fire, necrotic, or poison damage, and the
bonus equals your Intelli­gence modifier (minimum of +1).
"""
name = "Alchemical Savant"
source = "Artificer (Alchemist)"
class RestorativeReagents(Feature):
"""Starting at 9th level, you can incorporate
restorative reagents into some of your works:
• Whenever a creature drinks an experimental elixir you created, the
creature gains temporary hit points equal to 2d6 + your Intelligence
modifier (minimum of 1 temporary hit point).
• You can cast lesser restoration without expending a spell slot and
without preparing the spell, provided you use alchemist's supplies as the
spellcasting focus. You can do so a number of times equal to your
Intelligence modifier (minimum of once), and you regain all expended uses
when you finish a long rest.
"""
name = "Restorative Reagents"
source = "Artificer (Alchemist)"
class ChemicalMastery(Feature):
"""By 15th level, you have been exposed to so many chemicals that they
pose little risk to you, and you can use them to quickly end certain
ailments:
• You gain resistance to acid damage and poison damage, and you are immune
to the poisoned condition.
• You can cast greater restoration and heal without expending a spell
slot, without preparing the spell, and without material components,
provided you use alchemist's supplies as the spellcasting focus. Once
you cast either spell with this feature, you can't cast that spell with
it again until you finish a long rest.
"""
name = "Chemical Mastery"
source = "Artificer (Alchemist)"
# Artillerist
class ArtilleristSpells(_SpecialistSpells):
"""Starting at 3rd level, you always have certain spells prepared after
you reach particular levels in this class, as shown in the Artillerist
Spells table. These spells count as artificer spells for you, but they
don't count against the number of artificer spells you prepare.
"""
_name = "Artillerist"
_spells = {
3: [spells.Shield, spells.Thunderwave],
5: [spells.ScorchingRay, spells.Shatter],
9: [spells.Fireball, spells.WindWall],
13: [spells.IceStorm, spells.WallOfFire],
17: [spells.ConeOfCold, spells.WallOfForce]
}
class ArtilleristToolProficiency(Feature):
"""When you adopt this specialization at 3rd level, you gain proficiency
with woodcarver's tools. If you already have this proficiency, you gain
proficiency with one other type of artisan's tools of your choice.
"""
name = "Tool Proficiency"
source = "Artificer (Artillerist)"
class EldritchCannon(Feature):
"""At 3rd level, you learn how to create a magical cannon. Using
woodcarver's tools or smith's tools, you can take an action to magically
create a Small or Tiny eldritch cannon in an unoccupied space on a
horizontal surface within 5 feet of you. A Small eldritch cannon occupies
its space, and a Tiny one can be held in one hand.
Once you create a cannon, you can't do so again until you finish a long
rest or until you expend a spell slot of 1st level or higher. You can have
only one cannon at a time and can't create one while your cannon is
present.
The cannon is a magical object. Regardless of size, the cannon has an AC of
18 and a number of hit points equal to five times your artificer level. It
is immune to poison damage, psychic damage, and all conditions. If it is
forced to make an ability check or a saving throw, treat all its ability
scores as 10 (+O). If the *mending* spell is cast on it, it regains 2d6 hit
points. It disappears if it is reduced to 0 hit points or after 1 hour. You
can dismiss it early as an action.
When you create the cannon, you determine its appearance and whether it
has legs. You also decide which type it is, choosing from the options on
the Eldritch Cannons table. On each of your turns, you can take a bonus
action to cause the cannon to activate if you are within 60 feet of it. As
part of the same bonus action, you can direct the cannon to walk or climb
up to 15 feet to an unoccupied space, provided it has legs.
**Eldritch Cannons**
*Flamethrower*: The cannon exhales fire in an adjacent 15-foot cone that
you designate. Each creature in that area must make a Dexterity saving
throw against your spell save DC, taking 2d8 fire damage on a failed save
or half as much damage on a successful one. The fire ignites any flammable
objects in the area that aren't being worn or carried.
*Force Ballista*: Make a ranged spell attack, originating from the cannon,
at one creature or object within 120 feet of it. On a hit, the target takes
2d8 force damage, and if the target is a creature, it is pushed up to 5
feet away from the cannon.
*Protector*: The cannon emits a burst of positive energy
that grants itself and each creature of your choice within 10 feet of it a
number of temporary hit points equal to 1d8 + your Intelligence modifier
(minimum of +1)
"""
name = "Eldritch Cannon"
source = "Artificer (Artillerist)"
class ArcaneFirearm(Feature):
"""At 5th level, you know how to turn a wand, staff, or rod into an arcane
firearm, a conduit for your destructive spells. When you finish a long
rest, you can use wood­carver's tools to carve special sigils into a wand,
staff, or rod and thereby turn it into your arcane firearm. The sigils
disappear from the object if you later carve them on a different item. The
sigils otherwise last indefinitely.
You can use your arcane firearm as a spellcasting focus for your artificer
spells. When you cast an artificer spell through the firearm, roll a d8,
and you gain a bonus to one of the spell's damage rolls equal to the
number rolled.
"""
name = "Arcane Firearm"
source = "Artificer (Artillerist)"
class ExplosiveCannon(Feature):
"""Starting at 9th level, every eldritch cannon you create is more
destructive:
• The cannon's damage rolls all increase by 1d8.
• As an action, you can command the cannon to detonate if you are within 60
feet of it. Doing so destroys the cannon and forces each creature within
20 feet of it to make a Dexterity saving throw against your spell save
DC, taking 3d8 force damage on a failed save or half as much damage on a
successful one.
"""
name = "Explosive Cannon"
source = "Artificer (Artillerist)"
class FortifiedPosition(Feature):
"""Starting at 15th level, you're a master at forming well-defended
emplacements using Eldritch Cannon:
• You and your allies have half cover while within 10 feet of a cannon you
create with Eldritch Cannon, as a result of a shimmering field of magical
protection that the cannon emits.
• You can now have two cannons at the same time. You can create two with
the same action (but not the same spell slot), and you can activate both
of them with the same bonus action. You determine whether the cannons are
identical to each other or different. You can't create a third cannon
while you have two.
"""
name = "Fortified Position"
source = "Artificer (Artillerist)"
# Battle Smith
class BattleSmithSpells(_SpecialistSpells):
"""Starting at 3rd level, you always have certain spells prepared after you
reach particular levels in this class, as shown in the Battle Smith Spells
table. These spells count as artificer spells for you, but they don't count
against the number of artificer spells you prepare.
"""
_name = "Battle Smith"
_spells = {
3: [spells.Heroism, spells.Shield],
5: [spells.BrandingSmite, spells.WardingBond],
9: [spells.AuraOfVitality, spells.ConjureBarrage],
13: [spells.AuraOfPurity, spells.FireShield],
17: [spells.BanishingSmite, spells.MassCureWounds]
}
class BattleSmithToolProficiency(Feature):
"""When you adopt this specialization at 3rd level, you gain proficiency
with smith's tools. If you already have this proficiency, you gain
proficiency with one other type of artisan's tools of your choice.
"""
name = "Tool Proficiency"
source = "Artificer (Battle Smith)"
class BattleReady(Feature):
"""When you reach 3rd level, your combat training and your experiments with
magic have paid off in two ways:
• You gain proficiency with martial weapons.
• When you attack with a magic weapon, you can use your Intelligence
modifier, instead of Strength or Dexterity modifier, for the attack and
damage rolls.
"""
name = "Battle Ready"
source = "Artificer (Battle Smith)"
class SteelDefender(Feature):
"""By 3rd level, your tinkering has borne you a faithful companion, a steel
defender. It is friendly to you and your companions, and it obeys your
commands. See this creature's game statistics in the steel defender stat
block. You determine the creature's appearance and whether it has two legs
or fo ur; your choice has no effect on its game statistics.
In combat, the steel defender shares your initiative count, but it takes
its turn immediately after yours. It can move and use its reaction on its
own, but the only action it takes on its turn is the Dodge action, unless
you take a bonus action on your turn to command it to take one of the
actions in its stat block or the Dash, Disen­gage, Help, Hide, or Search
action.
If the *mending* spell is cast on it, it regains 2d6 hit points. If it has
died within the last hour, you can use your smith's tools as an action to
revive it, provided you are within 5 feet of it and you expend a spell slot
of 1st level or higher. The steel defender returns to life after 1 minute
with all its hit points restored.
At the end of a long rest, you can create a new steel defender if you have
your smith's tools with you. If you already have a steel defender from this
feature, the first one immediately perishes.
"""
name = "Steel Defender"
source = "Artificer (Battle Smith)"
class ExtraAttackBattleSmith(Feature):
"""Starting at 5th level, you can attack twice, rather than once, whenever
you take the Attack action on your turn.
"""
name = "Extra Attack"
source = "Artificer (Battle Smith)"
class ArcaneJolt(Feature):
"""At 9th level, you learn new ways to channel arcane en­ergy to harm or
heal. When either you hit a target with a magic weapon attack or your steel
defender hits a target, you can channel magical energy through the strike
to create one of the following effects:
• The target takes an extra 2d6 force damage.
• Choose one creature or object you can see within 30 feet of the target.
Healing energy flows into the chosen recipient, restoring 2d6 hit points
to it. You can use this energy a number of times equal to your
Intelligence modifier (minimum of once), but you can do so no more than
once on a turn. You regain all expended uses when you finish a long rest
"""
name = "Arcane Jolt"
source = "Artificer (Battle Smith)"
class ImprovedDefender(Feature):
"""At 15th level, your Arcane jolt and steel defender be­come more
powerful:
• The extra damage and the healing of your Arcane jolt both increase to
4d6.
• Your steel defender gains a +2 bonus to Armor Class.
• Whenever your steel defender uses its Deflect Attack, the attacker takes
force damage equal to 1d4 +your Intelligence modifier.
"""
name = "Improved Defender"
source = "Artificer (Battle Smith)"
@@ -85,6 +85,9 @@ spells_prepared = {{ char.spells_prepared }}
# Wild shapes for Druid
wild_shapes = {{ char.all_wild_shapes }} # Ex: ('ape', 'wolf', 'ankylosaurus')
# Infusions for Artificer
infusions = {{ char.infusions_text }} # Ex: ('repeating shot', 'replicate magic item')
# Backstory
# Describe your backstory here
personality_traits = """{{ char.personality_traits}}"""
+3
View File
@@ -92,6 +92,9 @@ spells = spells_prepared + __spells_unprepared
# Wild shapes for Druid
wild_shapes = () # Ex: ('ape', 'wolf', 'ankylosaurus')
# Infusions for Artificer
infusions = {{ char.infusions_text }} # Ex: ('repeating shot', 'replicate magic item')
# Backstory
# Describe your backstory here
personality_traits = """{{ char.personality_traits }}"""
@@ -0,0 +1,41 @@
\documentclass[10pt,twocolumn,lettersize]{article}
\newlength{\zerosep}
[% if use_dnd_decorations %]
\usepackage[layout=true]{dnd}
\setlength{\zerosep}{0em}
[% else %]
\usepackage[margin=1.5cm]{geometry}
\setlength{\zerosep}{-1em}
[% endif %]
\title{Infusion Descriptions}
\author{[[ character.name ]] (Artificer [[ character.Artificer.level ]] level)}
\date{}
\begin{document}
\maketitle
[% for inf in character.infusions %]
\section*{[[ inf.name ]]}
[% if inf.prerequisite %]%
\noindent
\textit{Prerequisite: [[ inf.prerequisite ]]}%
[% endif %]%
[% if inf.item %]%
\noindent
\textit{Item: [[ inf.item ]]}%
[% endif %]%
[[ inf.__doc__|rst_to_latex ]]
[% endfor %]
\end{document}
+184
View File
@@ -0,0 +1,184 @@
class Infusion():
name = "Unknown infusion"
item = "Item to be infused"
prerequisite = ""
classes = ('Artificer',)
def __str__(self):
indicator = ('$', self.special_material)
if indicator:
return self.name + f' ({"".join(indicator)})'
else:
return self.name
def __repr__(self):
return "\"{:s}\"".format(self.name)
def __eq__(self, other):
return (self.name == other.name)
def __hash__(self):
return 0
@property
def special_material(self):
return ('worth at least' in self.item.lower())
class BootsOfTheWindingPath(Infusion):
"""While wearing these boots, a creature can teleport up to 15 feet as a
bonus action to an unoccupied space the creature can see. The creature
must have occupied that space at some point during the current turn.
"""
name = "Boots of the Winding Path"
item = "A pair of boots (requires attunement"
prerequisite = "6th-level artificer"
class EnhancedArcaneFocus(Infusion):
"""While holding this item, a creature gains a + 1 bonus to spell attack
rolls. In addition, the creature ignores half cover when making a spell
attack.
The bonus increases to +2 when you reach 10th level in this class.
"""
name = "Enhanced Arcane Focus"
item = "A rod, staff, or wand (requires attunement)"
class EnhancedDefense(Infusion):
"""A creature gains a + 1 bonus to Armor Class while wearing (armor) or
wielding (shield) the infused item.
The bonus increases to +2 when you reach 10th level in this class.
"""
name = "Enhanced Defense"
item = "A suit of armor or shield"
class EnhancedWeapon(Infusion):
"""This magic weapon grants a +1 bonus to attack and damage rolls made with
it.
The bonus increases to +2 when you reach 10th level in this class.
"""
name = "Enhanced Weapon"
item = "A simple or martial weapon"
class HomunculusServant(Infusion):
"""You learn intricate methods for magically creating a special homunculus
that serves you. The item you infuse serves as the creature's heart, around
which the creature's body instantly forms.
You determine the homunculus's appearance. Some artificers prefer
mechanical-looking birds, whereas some like winged vials or miniature,
animate cauldrons.
The homunculus is friendly to you and your companions, and it obeys your
commands. See this creature's game statistics in the Homunculus Servant
stat block.
In combat, the homunculus shares your initiative count, but it takes its
turn immediately after yours. It can move and use its reaction on its own,
but the only action it takes on its turn is the Dodgeaction, unless you
take a bonus action on your turn to command it to take the action in its
stat block or the Dash, Disengage, Help, Hide, or Search action.
The homunculus regains 2d6 hit points if the *mending* spell is cast on it.
If it dies, it vanishes, leaving its heart in its space.
"""
name = "Homunculus Servant"
item = "A gem worth at least 100gp or a dragonshard"
prerequisite = "6th-level artificer"
class RadiantWeapon(Infusion):
"""This magic weapon grants a + 1 bonus to attack and damage rolls made
with it. While holding it, the wielder can take a bonus action to cause it
to shed bright light in a 30-foot radius and dim light for an additional 30
feet. The wielder can extinguish the light as a bonus action.
The weapon has 4 charges. As a reaction immediately after being hit by an
attack, the wielder can expend 1 charge and cause the attacker to be
blinded until the end of the attacker's next turn, unless the attacker
succeeds on a Constitution saving throw against your spell save DC. The
weapon regains ld4 expended charges daily at dawn.
"""
name = "Radiant Weapon"
item = "A simple or martial weapon (requires attunement)"
prerequisite = "6th-level artificer"
class RepeatingShot(Infusion):
"""This magic weapon grants a + 1 bonus to attack and damage rolls made
with it when it's used to make a ranged attack, and it ignores the loading
property if it has it.
If you load no ammunition in the weapon, it produces its own, automatically
creating one piece of magic am­munition when you make a ranged attack with
it. The ammunition created by the weapon vanishes the instant after it hits
or misses a target.
"""
name = "Repeating Shot"
item = """A simple or martial weapon with the ammunition property (requires
attunement)"""
class ReplicateMagicItem(Infusion):
"""Using this infusion, you replicate a particular magic item. You can
learn this infusion multiple times; each time you do so, choose a magic
item that you can make with it, picking from the Replicable Items tables
below. A table's title tells you the level you must be in the class to
choose an item from the table.
In the tables, an item's entry tells you whether the item requires
attunement. See the item's description in the *Dungeon Master's Guide* for
more information about it, including the type of object required for its
making. If you have *Xanathar's Guide to Everything*, you can choose from
among the common magic items in that book when you pick a magic item you
can replicate with this infusion.
"""
name = "Replicate Magic Item"
class RepulsionShield(Infusion):
"""A creature gains a + 1 bonus to Armor Class while wield­ing this shield.
The shield has 4 charges. While holding it, the wielder can use a reaction
immediately after being hit by a me­lee attack to expend 1 of the shield's
charges and push the attacker up to 15 feet away. The shield regains ld4
expended charges daily at dawn.
"""
name = "Repulsion Shield"
item = "A shield (requires attunement)"
class ResistantArmor(Infusion):
"""While wearing this armor, a creature has resistance to one of the
following damage types, which you choose when you infuse the item: acid,
cold, fire, force, light­ning, necrotic, poison, psychic, radiant, or
thunder.
"""
name = "Resistant Armor"
item = "A suit of armor (requires attunement)"
class ReturningWeapon(Infusion):
"""This magic weapon grants a + 1 bonus to attack and damage rolls made
with it, and it returns to the wielder's hand immediately after it is used
to make a ranged attack.
"""
name = "Returning Weapon"
item = "A simple or martial weapon with the thrown property"
+20
View File
@@ -79,6 +79,13 @@ def create_druid_shapes_pdf(character, basename, keep_temp_files=False, use_dnd_
use_dnd_decorations=use_dnd_decorations)
def create_infusions_pdf(character, basename, keep_temp_files=False, use_dnd_decorations=False):
template = jinja_env.get_template('infusions_template.tex')
return create_latex_pdf(character, basename, template,
keep_temp_files=keep_temp_files,
use_dnd_decorations=use_dnd_decorations)
def create_spellbook_pdf(character, basename, keep_temp_files=False, use_dnd_decorations=False):
template = jinja_env.get_template('spellbook_template.tex')
return create_latex_pdf(character, basename, template,
@@ -496,6 +503,19 @@ def make_sheet(character_file, character=None, flatten=False, fancy_decorations=
f'for {character.name}')
else:
sheets.append(spellbook_base + '.pdf')
#Create a list of Artificer infusions
infusions = getattr(character, 'infusions', [])
if len(infusions) > 0:
infusions_base = os.path.splitext(character_file)[0] + '_infusions'
try:
create_infusions_pdf(character=character,
basename=infusions_base, keep_temp_files=debug,
use_dnd_decorations=fancy_decorations)
except exceptions.LatexNotFoundError as e:
log.warning('``pdflatex`` not available. Skipping infusions list '
f'for {character.name}')
else:
sheets.append(infusions_base + '.pdf')
# Create a list of Druid wild_shapes
wild_shapes = getattr(character, 'wild_shapes', [])
if len(wild_shapes) > 0:
+113
View File
@@ -0,0 +1,113 @@
"""This file describes the heroic adventurer Cemzack.
It's used primarily for saving characters from create-character,
where there will be many missing sections.
Modify this file as you level up and then re-generate the character
sheet by running ``makesheets`` from the command line.
"""
dungeonsheets_version = "0.10.1"
name = "Cemzack"
player_name = ""
# Be sure to list Primary class first
classes = ['Artificer'] # ex: ['Wizard'] or ['Rogue', 'Fighter']
levels = [20] # ex: [10] or [3, 2]
subclasses = ["Artillerist"] # ex: ['Necromacy'] or ['Thief', None]
background = "Sailor"
race = "Rock Gnome"
alignment = "Neutral good"
xp = 0
hp_max = 149
inspiration = 0 # integer inspiration value
# Ability Scores
strength = 13
dexterity = 16
constitution = 18
intelligence = 20
wisdom = 12
charisma = 10
# Select what skills you're proficient with
# ex: skill_proficiencies = ('athletics', 'acrobatics', 'arcana')
skill_proficiencies = ('investigation', 'sleight of hand', 'athletics', 'perception')
# Any skills you have "expertise" (Bard/Rogue) in
skill_expertise = ()
# Named features / feats that aren't part of your classes, race, or background.
# Also include Eldritch Invocations and features you make multiple selection of
# (like Maneuvers for Fighter, Metamagic for Sorcerors, Trick Shots for
# Gunslinger, etc.)
# Example:
# features = ('Tavern Brawler',) # take the optional Feat from PHB
features = ('sharpshooter')
# If selecting among multiple feature options: ex Fighting Style
# Example (Fighting Style):
# feature_choices = ('Archery',)
feature_choices = ()
# Weapons/other proficiencies not given by class/race/background
weapon_proficiencies = () # ex: ('shortsword', 'quarterstaff')
_proficiencies_text = () # ex: ("thieves' tools",)
# Proficiencies and languages
languages = """Common, Gnomish"""
# Inventory
# TODO: Get yourself some money
cp = 0
sp = 0
ep = 0
gp = 0
pp = 0
# TODO: Put your equipped weapons and armor here
weapons = [] # Example: ('shortsword', 'longsword')
magic_items = () # Example: ('ring of protection',)
armor = "Breastplate" # Eg "leather armor"
shield = "None" # Eg "shield"
equipment = """TODO: list the equipment and magic items your character carries"""
attacks_and_spellcasting = """TODO: Describe how your character usually attacks
or uses spells."""
# List of known spells
# Example: spells_prepared = ('magic missile', 'mage armor')
spells_prepared = () # Todo: Learn some spells
# Which spells have not been prepared
__spells_unprepared = ()
# all spells known
spells = spells_prepared + __spells_unprepared
# Wild shapes for Druid
wild_shapes = () # Ex: ('ape', 'wolf', 'ankylosaurus')
# Infusions for Artificer
infusions = ('boots of the winding path', 'enhanced arcane focus',
'enhanced defense', 'enhanced weapon', 'repeating shot',
'homunculus servant', 'radiant weapon', 'replicate magic item',
'repulsion shield', 'resistant armor', 'returning weapon')
# Ex: ('repeating shot', 'replicate magic item')
# Backstory
# Describe your backstory here
personality_traits = """TODO: Describe how your character behaves, interacts with others"""
ideals = """TODO: Describe what values your character believes in."""
bonds = """TODO: Describe your character's commitments or ongoing quests."""
flaws = """TODO: Describe your character's interesting flaws."""
features_and_traits = """TODO: Describe other features and abilities your
character has."""