Added magic items

This commit is contained in:
Ben Cook
2018-12-26 19:57:22 -05:00
parent 5275cd0566
commit 7f560feedb
5 changed files with 166 additions and 12 deletions
+18 -7
View File
@@ -12,7 +12,7 @@ import subprocess
from .stats import Ability, Skill, findattr, ArmorClass, Speed
from .dice import read_dice_str
from . import (weapons, race, background, spells, armor, monsters,
exceptions, classes, features)
exceptions, classes, features, magic_items)
from .weapons import Weapon
from .armor import Armor, NoArmor, Shield, NoShield
@@ -345,7 +345,7 @@ class Character():
fts |= set(self.race.features_by_level[lvl])
if self.background is not None:
fts |= set(getattr(self.background, 'features', ()))
return tuple(fts)
return sorted(tuple(fts), key=(lambda x: x.name))
@property
def custom_features_text(self):
@@ -375,7 +375,6 @@ class Character():
return (len(self.spellcasting_classes) > 0)
def spell_slots(self, spell_level):
# TODO: Update this for Multiclassing
if len(self.spellcasting_classes) == 1:
return self.spellcasting_classes[0].spell_slots(spell_level)
else:
@@ -407,7 +406,7 @@ class Character():
spells |= set(c.spells_known) | set(c.spells_prepared)
if self.race is not None:
spells |= set(self.race.spells_known) | set(self.race.spells_prepared)
return tuple(spells)
return sorted(tuple(spells), key=(lambda x: (x.level, x.name)))
@property
def spells_prepared(self):
@@ -418,7 +417,7 @@ class Character():
spells |= set(c.spells_prepared)
if self.race is not None:
spells |= set(self.race.spells_prepared)
return tuple(spells)
return sorted(tuple(spells), key=(lambda x: (x.level, x.name)))
def set_attrs(self, **attrs):
"""Bulk setting of attributes. Useful for loading a character from a
@@ -436,7 +435,12 @@ class Character():
if isinstance(val, str):
val = [val]
for mitem in val:
self.magic_items.append(mitem(owner=self))
try:
self.magic_items.append(findattr(magic_items, mitem)(owner=self))
except (AttributeError):
msg = (f'Magic Item "{mitem}" not defined. '
f'Please add it to ``magic_items.py``')
warnings.warn(msg)
elif attr == 'weapon_proficiencies':
self.other_weapon_proficiencies = ()
wps = set([findattr(weapons, w) for w in val])
@@ -511,7 +515,6 @@ class Character():
if self.has_feature(features.NaturalExplorerRevised):
ini += '(A)'
return ini
def is_proficient(self, weapon: Weapon):
"""Is the character proficient with this item?
@@ -568,6 +571,14 @@ class Character():
s += '\n\n=================\n\n'
return s
@property
def magic_items_text(self):
s = ', '.join([f.name + ("**" if f.needs_implementation else "")
for f in sorted(self.magic_items, key=(lambda x: x.name))])
if s:
s += ', '
return s
def wear_armor(self, new_armor):
"""Accepts a string or Armor class and replaces the current armor.
+9 -3
View File
@@ -13,17 +13,21 @@
\maketitle
\section*{Subclasses}
[% for sc in character.subclasses if sc not in ['', None, 'None', 'none']%]
\section*{Subclass: [[ sc.name ]]}
\subsection*{Subclass: [[ sc.name ]]}
[[ sc.__doc__|rst_to_latex ]]
[% endfor %]
\section*{Features}
[% for feat in character.features %]
\section*{[[ feat.name ]]}
\subsection*{[[ feat.name ]]}
\noindent
\textbf{Source:} [[ feat.source ]] \\
@@ -36,9 +40,11 @@
[% endfor %]
\section*{Magic Items}
[% for mitem in character.magic_items %]
\section*{[[ mitem.name ]]}
\subsection*{[[ mitem.name ]]}
\noindent
\textbf{Requires Attunement:} [[ mitem.requires_attunement ]] \\
+129
View File
@@ -9,6 +9,15 @@ class MagicItem():
needs_implementation = False
rarity = ''
def __init__(self, owner=None):
self.owner = owner
def __str__(self):
return self.name
def __repr__(self):
return '\"{:s}\"'.format(str(self))
class RingOfProtection(MagicItem):
"""
@@ -19,3 +28,123 @@ class RingOfProtection(MagicItem):
ac_bonus = 1
requires_attunement = True
rarity = 'Rare'
class DecanterOfEndlessWater(MagicItem):
"""This stoppered flask sloshes when shaken, as if it contains water. The
decanter weighs 2 pounds.
You can use an action to remove the stopper and speak one of three command
words, whereupon an amount of fresh water or salt water (your choice) pours
out of the flask. The water stops pouring out at the start of your next
turn. Choose from the following options:
--"Stream" produces 1 gallon of water.
--"Fountain" produces 5 gallons of water.
--"Geyser" produces 30 gallons of water that gushes forth in a geyser 30
feet long and 1 foot wide. As a bonus action while holding the decanter,
you can aim the geyser at a creature you can see within 30 feet of you. The
target must succeed on a DC 13 Strength saving throw or take 1d4
bludgeoning damage and fall prone. Instead of a creature, you can target an
object that isn't being worn or carried and that weighs no more than 200
pounds. The object is either knocked over or pushed up to 15 feet away from
you.
"""
name = "Decanter of Endless Water"
rarity = 'Uncommon'
class ToothOfAnimalFriendship(MagicItem):
"""While holding this wolf's tooth, you can expend it's one charge to cast
Animal Friendship (DC 13) or Speak With Animals.
The charge resets at the next Dawn.
"""
name = "Tooth of Animal Friendship"
rarity = 'Uncommon'
class CloakOfBillowing(MagicItem):
"""While wearing this cloak, you can use a bonus action to make it billow
dramatically.
"""
name = "Cloak of Billowing"
rarity = "Common"
class CapeOfTheMountebank(MagicItem):
"""This cape smells faintly of brimstone. While wearing it, you can use it to
cast the Dimension Door spell as an action. This property of the cape can't
be used again until the next dawn.
When you disappear, you leave behind a cloud of smoke, and you appear in a
similar cloud of smoke at your destination. The smoke lightly obscures the
space you left and the space you appear in, and it dissipates at the end of
your next turn. A light or stronger wind disperses the smoke.
"""
name = "Cape of the Mountebank"
rarity = "Rare"
class EyesOfCharming(MagicItem):
"""These Crystal lenses fit over the eyes. They have 3 Charges. While wearing
them, you can expend 1 charge as an action to cast the Charm Person spell
(save DC 13) on a humanoid within 30 feet of you, provided that you and the
target can see each other. The lenses regain all expended Charges daily at
dawn.
"""
name = "Eyes of Charming"
rarity = "Uncommon"
requires_attunement = True
class CharlattansDie(MagicItem):
"""Whenever you roll this six—sided die, you can control which number it
rolls.
"""
name = "Charlattan's Die"
rarity = "Common"
class PipeOfSmokeMonsters(MagicItem):
"""While smoking this pipe, you can use an action to ex- hale a puff of smoke
that takes the form of a single crea— ture, such as a dragon, a flumph, or
a froghemoth. The form must be small enough to fit in a 1-foot cube and
loses its shape after a few seconds, becoming an ordi- nary puff of smoke.
"""
name = 'Pipe of Smoke Monsters'
rarity = "Common"
class CoinsOfCommunication(MagicItem):
"""This set of multiple coins are virtually indistinguishable from regular Gold
Pieces, but are connected by magic. Once per day, a holder of any of any
coin can whisper a single word into it, after which all coins will
immediately vibrate and the word will replace a word in the traditional
Kings Message imprinted on the coin. This ability cannot be used again by
the holder of any of the coins until the following dawn.
"""
name = "Coins of Communication"
rarity = "Uncommon"
class FlameTongue(MagicItem):
"""You can use a Bonus Action to speak this magic sword's Command Word, causing
flames to erupt from the blade. These flames shed bright light in a 40-foot
radius and dim light for an additional 40 feet. While the sword is ablaze,
it deals an extra 2d6 fire damage to any target it hits. The flames last
until you use a Bonus Action to speak the Command Word again or until you
drop or sheathe the sword
"""
name = "Flame Tongue"
rarity = "Rare"
+1 -1
View File
@@ -260,7 +260,7 @@ def create_character_pdf(character, basename, flatten=False):
'EP': character.ep,
'GP': character.gp,
'PP': character.pp,
'Equipment': text_box(character.equipment),
'Equipment': text_box(character.magic_items_text + character.equipment),
}
# Check boxes for proficiencies
ST_boxes = {
+9 -1
View File
@@ -509,7 +509,15 @@ class Musket(Firearm):
weight = 10
properties = "Ammunition (range 120/480), Two-Handed, Reload 1, Misfire 2"
# Magic Items
class FlameTongue(Greatsword):
name = "Flame Tongue +1"
magic_bonus = 1
base_damage = "4d6"
damage_type = 'f'
# Some lists of weapons for easy proficiency resolution
simple_melee_weapons = (Club, Dagger, Greatclub, Handaxe, Javelin,
LightHammer, Mace, Quarterstaff, Sickle, Spear)