Features added and tested for all classes/subclasses in bens campaign

This commit is contained in:
Ben Cook
2018-12-26 18:58:25 -05:00
parent a83c49146c
commit b73e9848ef
34 changed files with 3612 additions and 313 deletions
+31 -12
View File
@@ -47,11 +47,13 @@ class CharClass():
self.features_by_level[i] = fs
for k, v in params.items():
setattr(self, k, v)
self.spells_known = [S() for S in cls.spells_known]
self.spells_prepared = [S() for S in cls.spells_prepared]
# Apply subclass
self.subclass = self.select_subclass(subclass)
if isinstance(self.subclass, SubClass):
self.apply_subclass()
self.apply_subclass(feature_choices=feature_choices)
def select_subclass(self, subclass_str):
"""
@@ -67,23 +69,31 @@ class CharClass():
return sc(owner=self.owner)
return None
def apply_subclass(self):
if self.subclass is None:
def apply_subclass(self, feature_choices=[]):
if not isinstance(self.subclass, SubClass):
return
subcls = self.subclass
for i in range(1, 21):
self.features_by_level[i] += ([f(owner=self.owner) for f in
self.subclass.features_by_level[i]])
for attr in ('weapon_proficiencies', '_proficiencies_text',
'spells_known', 'spells_prepared'):
new_list = getattr(self, attr, ()) + getattr(self.subclass, attr, ())
fs = []
for f in subcls.features_by_level[i]:
if issubclass(f, FeatureSelector):
fs.append(f(owner=self.owner,
feature_choices=feature_choices))
elif issubclass(f, Feature):
fs.append(f(owner=self.owner))
self.features_by_level[i].extend(fs)
for attr in ('weapon_proficiencies', '_proficiencies_text'):
new_list = tuple(getattr(self, attr, ())) + tuple(getattr(self.subclass, attr, ()))
setattr(self, attr, new_list)
# All subclass proficiencies transfer, regardless of if this is primary class
self.multiclass_weapon_proficiencies += (self.subclass.weapon_proficiencies)
self._multiclass_proficiencies_text += (self._proficiencies_text)
self.multiclass_weapon_proficiencies += tuple(subcls.weapon_proficiencies)
self._multiclass_proficiencies_text += tuple(subcls._proficiencies_text)
self.spellcasting_ability = (self.spellcasting_ability or
self.subclass.spellcasting_ability)
subcls.spellcasting_ability)
self.spell_slots_by_level = (self.spell_slots_by_level or
self.subclass.spell_slots_by_level)
subcls.spell_slots_by_level)
self.spells_known.extend([S() for S in subcls.spells_known])
self.spells_prepared.extend([S() for S in subcls.spells_prepared])
@property
def features(self):
@@ -104,6 +114,15 @@ class CharClass():
else:
return self.spell_slots_by_level[self.level][spell_level]
def __str__(self):
s = 'Level {:d} {:s}'.format(self.level, self.name)
if isinstance(self.subclass, SubClass):
s += ' ({:s})'.format(str(self.subclass))
return s
def __repr__(self):
return '\"{:s}\"'.format(str(self))
class SubClass():
"""
+56 -11
View File
@@ -1,9 +1,31 @@
from .. import (weapons, features)
from .. import (weapons, features, spells)
from .classes import CharClass, SubClass
from collections import defaultdict
class KnowledgeDomain(SubClass):
class ClericDomain(SubClass):
name = "Generic Cleric Domain"
_domain_spells = {1: [], 3: [], 5: [], 7: [], 9: []}
@property
def level(self):
return self.owner.Cleric.level
@property
def spells_known(self):
spells = []
for lvl, sps in self._domain_spells.items():
if self.level >= lvl:
spells.extend(sps)
return spells
# All Domain spells are both known and prepared
@property
def spells_prepared(self):
return self.spells_known
class KnowledgeDomain(ClericDomain):
"""The gods of knowledge—including Oghma, Boccob, Gilean, Aureon, and
Thoth—value learning and understanding above all. Some teach that knowledge
is to be gathered and shared in libraries and universities, or promote the
@@ -18,10 +40,11 @@ class KnowledgeDomain(SubClass):
"""
name = "Knowledge Domain"
_domain_spells = {1: [], 3: [], 5: [], 7: [], 9: []}
features_by_level = defaultdict(list)
class LifeDomain(SubClass):
class LifeDomain(ClericDomain):
"""The Life domain focuses on the vibrant positive energy—one of the
fundamental forces of the universe— that sustains all life. The gods of
life promote vitality and health through healing the sick and wounded,
@@ -34,10 +57,11 @@ class LifeDomain(SubClass):
"""
name = "Life Domain"
_domain_spells = {1: [], 3: [], 5: [], 7: [], 9: []}
features_by_level = defaultdict(list)
class LightDomain(SubClass):
class LightDomain(ClericDomain):
"""Gods of light—including Helm, Lathander, Pholtus, Branchala, the Silver
Flame, Belenus, Apollo, and Re-Horakhty—promote the ideals of rebirth and
renewal, truth, vigilance, and beauty, often using the symbol of the
@@ -51,10 +75,11 @@ class LightDomain(SubClass):
"""
name = "Light Domain"
_domain_spells = {1: [], 3: [], 5: [], 7: [], 9: []}
features_by_level = defaultdict(list)
class NatureDomain(SubClass):
class NatureDomain(ClericDomain):
"""Gods of nature are as varied as the natural world itself, from inscrutable
gods of the deep forests (such as Silvanus, Obad-Hai, Chislev, Balinor, and
Pan) to friendly deities associated with particular springs and groves
@@ -68,10 +93,11 @@ class NatureDomain(SubClass):
"""
name = "Nature Domain"
_domain_spells = {1: [], 3: [], 5: [], 7: [], 9: []}
features_by_level = defaultdict(list)
class TempestDomain(SubClass):
class TempestDomain(ClericDomain):
"""Gods whose portfolios include the Tempest domain - including Talos,
Umberlee, Kord, Zeboim, the Devourer, Zeus, and Thor — govern storms, sea,
and sky. They include gods of lightning and thunder, gods of earthquakes,
@@ -86,10 +112,22 @@ class TempestDomain(SubClass):
"""
name = "Tempest Domain"
_domain_spells = {1: [spells.FogCloud, spells.Thunderwave],
3: [spells.GustOfWind, spells.Shatter],
5: [spells.CallLightning, spells.SleetStorm],
7: [spells.ControlWater, spells.IceStorm],
9: [spells.DestructiveWave, spells.InsectPlague]}
weapon_proficiencies = (weapons.MartialWeapon,)
_proficiencies_text = ('martial weapons', 'heavy armor')
features_by_level = defaultdict(list)
features_by_level[1] = (features.WrathOfTheStorm,)
features_by_level[2] = (features.DestructiveWrath,)
features_by_level[6] = (features.ThunderboltStrike,)
features_by_level[8] = (features.DivineStrikeTempest,)
features_by_level[17] = (features.Stormborn,)
class TrickeryDomain(SubClass):
class TrickeryDomain(ClericDomain):
"""Gods of trickery—such as Tymora, Beshaba, Olidammara, the Traveler, Garl
Glittergold, and Loki—are mischief-makers and instigators who stand as a
constant challenge to the accepted order among both gods and
@@ -101,10 +139,11 @@ class TrickeryDomain(SubClass):
"""
name = "Trickery Domain"
_domain_spells = {1: [], 3: [], 5: [], 7: [], 9: []}
features_by_level = defaultdict(list)
class WarDomain(SubClass):
class WarDomain(ClericDomain):
"""War has many manifestations. It can make heroes of ordinary people. It can
be desperate and horrific, with acts of cruelty and cowardice eclipsing
instances of excellence and courage. In either case, the gods of war watch
@@ -120,11 +159,12 @@ class WarDomain(SubClass):
"""
name = "War Domain"
_domain_spells = {1: [], 3: [], 5: [], 7: [], 9: []}
features_by_level = defaultdict(list)
# SCAG
class ArcanaDomain(SubClass):
class ArcanaDomain(ClericDomain):
"""Magic is an energy that suffuses the multiverse and that fuels both
destruction and creation. Gods of the Arcana domain know the secrets and
potential of magic intimately. For some of these gods, magical knowledge
@@ -141,11 +181,12 @@ class ArcanaDomain(SubClass):
"""
name = "Arcana Domain"
_domain_spells = {1: [], 3: [], 5: [], 7: [], 9: []}
features_by_level = defaultdict(list)
# XGTE
class ForgeDomain(SubClass):
class ForgeDomain(ClericDomain):
"""The gods of the forge are patrons of artisans who work with metal, from a
humble blacksmith who keeps a village in horseshoes and plow blades to the
mighty elf artisan whose diamond-tipped arrows of mithral have felled demon
@@ -163,7 +204,7 @@ class ForgeDomain(SubClass):
features_by_level = defaultdict(list)
class GraveDomain(SubClass):
class GraveDomain(ClericDomain):
"""Gods of the grave watch over the line between life and death. To these
deities, death and the afterlife are a foundational part of the
multiverse. To desecrate the peace of the dead is an abomination. Deities
@@ -177,6 +218,7 @@ class GraveDomain(SubClass):
"""
name = "Grave Domain"
_domain_spells = {1: [], 3: [], 5: [], 7: [], 9: []}
features_by_level = defaultdict(list)
@@ -194,6 +236,9 @@ class Cleric(CharClass):
class_skill_choices = ('History', 'Insight', 'Medicine',
'Persuasion', 'Religion')
features_by_level = defaultdict(list)
features_by_level[2] = (features.ChannelDivinity, features.TurnUndead,)
features_by_level[5] = (features.DestroyUndead, )
features_by_level[10] = (features.DivineIntervention,)
subclasses_available = (KnowledgeDomain, LifeDomain, LightDomain,
NatureDomain, TempestDomain, TrickeryDomain,
WarDomain, ArcanaDomain, ForgeDomain,
+5
View File
@@ -0,0 +1,5 @@
from .. import (weapons, features, spells)
from collections import defaultdict
# Custom Classes
+26 -2
View File
@@ -13,6 +13,11 @@ class Champion(SubClass):
"""
name = "Champion"
features_by_level = defaultdict(list)
features_by_level[3] = [features.ImprovedCritical]
features_by_level[7] = [features.RemarkableAthelete]
features_by_level[10] = [features.AdditionalFightingStyle]
features_by_level[15] = [features.SuperiorCritical]
features_by_level[18] = [features.Survivor]
class BattleMaster(SubClass):
@@ -26,6 +31,9 @@ class BattleMaster(SubClass):
"""
name = "Battle Master"
features_by_level = defaultdict(list)
features_by_level[3] = [features.CombatSuperiority, features.StudentOfWar]
features_by_level[7] = [features.KnowYourEnemy]
features_by_level[15] = [features.Relentless]
class EldritchKnight(SubClass):
@@ -41,6 +49,12 @@ class EldritchKnight(SubClass):
"""
name = "Eldritch Knight"
features_by_level = defaultdict(list)
features_by_level[3] = [features.EldritchKnightSpellcasting,
features.WeaponBond]
features_by_level[7] = [features.WarMagic]
features_by_level[10] = [features.EldritchStrike]
features_by_level[15] = [features.ArcaneCharge]
features_by_level[18] = [features.ImprovedWarMagic]
spellcasting_ability = 'intelligence'
multiclass_spellslots_by_level = {
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
@@ -157,9 +171,15 @@ class Gunslinger(SubClass):
"""
name = "Gunslinger"
features_by_level = defaultdict(list)
weapon_proficiencies = (weapons.firearms)
_proficiencies_text = ('firearms')
_proficiencies_text = ('firearms', "tinker's tools")
features_by_level = defaultdict(list)
features_by_level[3] = [features.Gunsmith, features.AdeptMarksman]
features_by_level[7] = [features.QuickDraw]
features_by_level[10] = [features.RapidRepair]
features_by_level[15] = [features.LightningReload]
features_by_level[18] = [features.ViciousIntent,
features.HemorrhagingCritical]
class Fighter(CharClass):
@@ -179,6 +199,10 @@ class Fighter(CharClass):
'Athletics', 'History', 'Insight', 'Intimidation',
'Perception', 'Survival')
features_by_level = defaultdict(list)
features_by_level[1] = [features.FighterFightingStyle, features.SecondWind]
features_by_level[2] = [features.ActionSurge]
features_by_level[5] = [features.ExtraAttackFighter]
features_by_level[9] = [features.Indomitable]
subclasses_available = (Champion, BattleMaster, EldritchKnight,
PurpleDragonKnight, ArcaneArcher, Cavalier,
Samurai, Gunslinger)
+121 -9
View File
@@ -1,9 +1,31 @@
from .. import (weapons, features)
from .. import (weapons, features, spells)
from .classes import CharClass, SubClass
from collections import defaultdict
class OathOfDevotion(SubClass):
class PaladinOath(SubClass):
name = "Generic Paladin Oath"
_oath_spells = {3: [], 5: [], 9: [], 13: [], 17: []}
@property
def level(self):
return self.owner.Paladin.level
@property
def spells_known(self):
spells = []
for lvl, sps in self._oath_spells.items():
if self.level >= lvl:
spells.extend(sps)
return spells
# All Oath spells are both known and prepared
@property
def spells_prepared(self):
return self.spells_known
class OathOfDevotion(PaladinOath):
"""The Oath of Devotion binds a paladin to the loftiest ideals of justice,
virtue, and order. Sometim es called cavaliers, white knights, or holy
warriors, these paladins meet the ideal of the knight in shining armor,
@@ -35,10 +57,20 @@ class OathOfDevotion(SubClass):
"""
name = "Oath of Devotion"
_oath_spells = {3: [spells.ProtectionFromEvilAndGood,
spells.Sanctuary],
5: [spells.LesserRestoration, spells.ZoneOfTruth],
9: [spells.BeaconOfHope, spells.DispelMagic],
13: [spells.FreedomOfMovement, spells.GuardianOfFaith],
17: [spells.Commune, spells.FlameStrike]}
features_by_level = defaultdict(list)
features_by_level[3] = [features.SacredWeapon, features.TurnTheUnholy]
features_by_level[7] = [features.AuraOfDevotion]
features_by_level[15] = [features.PurityOfSpirit]
features_by_level[20] = [features.HolyNimbus]
class OathOfAncients(SubClass):
class OathOfAncients(PaladinOath):
"""The Oath of the Ancients is as old as the race of elves and the rituals of
the druids. Sometimes called fey knights, green knights, or horned knights,
paladins who swear this oath cast their lot with the side of the light in
@@ -69,10 +101,15 @@ class OathOfAncients(SubClass):
"""
name = "Oath of The Ancients"
_oath_spells = {3: [spells.EnsnaringStrike, spells.SpeakWithAnimals],
5: [spells.Moonbeam, spells.MistyStep],
9: [spells.PlantGrowth, spells.ProtectionFromEnergy],
13: [spells.IceStorm, spells.Stoneskin],
17: [spells.CommuneWithNature, spells.TreeStride]}
features_by_level = defaultdict(list)
class OathOfVengance(SubClass):
class OathOfVengance(PaladinOath):
"""The Oath of Vengeance is a solemn commitment to punish those who have
committed a grievous sin. When evil forces slaughter helpless villagers,
when an entire people turns against the will of the gods, when a thieves
@@ -103,10 +140,15 @@ class OathOfVengance(SubClass):
"""
name = "Oath of Vengance"
_oath_spells = {3: [spells.Bane, spells.HuntersMark],
5: [spells.HoldPerson, spells.MistyStep],
9: [spells.Haste, spells.ProtectionFromEnergy],
13: [spells.Banishment, spells.DimensionDoor],
17: [spells.HoldMonster, spells.Scrying]}
features_by_level = defaultdict(list)
class OathOfCrown(SubClass):
class OathOfCrown(PaladinOath):
"""The Oath of the Crown is sworn to the ideals of civilization, be it the
spirit of a nation, fealty to a sovereign, or service to a deity of law and
rulership. The paladins who swear this oath dedicate themselves to serving
@@ -137,10 +179,15 @@ class OathOfCrown(SubClass):
"""
name = "Oath of The Crown"
_oath_spells = {3: [spells.Command, spells.CompelledDuel],
5: [spells.WardingBond, spells.ZoneOfTruth],
9: [spells.AuraOfVitality, spells.SpiritGuardians],
13: [spells.Banishment, spells.GuardianOfFaith],
17: [spells.CircleOfPower, spells.Geas]}
features_by_level = defaultdict(list)
class OathOfConquest(SubClass):
class OathOfConquest(PaladinOath):
"""The Oath of Conquest calls to paladins who seek glory in battle and the
subjugation of their enemies. It isnt enough for these paladins to
establish order. They must crush the forces of chaos. Sometimes called
@@ -173,10 +220,15 @@ class OathOfConquest(SubClass):
"""
name = "Oath of Conquest"
_oath_spells = {3: [spells.ArmorOfAgathys, spells.Command],
5: [spells.HoldPerson, spells.SpiritualWeapon],
9: [spells.BestowCurse, spells.Fear],
13: [spells.DominateBeast, spells.Stoneskin],
17: [spells.Cloudkill, spells.DominatePerson]}
features_by_level = defaultdict(list)
class OathOfRedemption(SubClass):
class OathOfRedemption(PaladinOath):
"""The Oath of Redemption sets a paladin on a difficult path, one that requires
a holy warrior to use violence only as a last resort. Paladins who dedicate
themselves to this oath believe that any person can be redeemed and that
@@ -216,7 +268,56 @@ class OathOfRedemption(SubClass):
"""
name = "Oath of Redemption"
_oath_spells = {3: [spells.Sanctuary, spells.Sleep],
5: [spells.CalmEmotions, spells.HoldPerson],
9: [spells.Counterspell, spells.HypnoticPattern],
13: [spells.OtilukesResilientSphere, spells.Stoneskin],
17: [spells.HoldMonster, spells.WallOfForce]}
features_by_level = defaultdict(list)
features_by_level[3] = [features.EmissaryOfPeace,
features.RebukeTheViolent]
features_by_level[7] = [features.AuraOfTheGuardian]
features_by_level[15] = [features.ProtectiveSpirit]
features_by_level[20] = [features.EmissaryOfRedemption]
# Custom
class OathOfZor(PaladinOath):
"""The Oath of Zor
**Tenets of Zor**:
--Courage. Never fear to act, though caution is wise.
--Honesty. Dont lie or cheat. Let your word be your promise.
--Innocence. All people begin life in an innocent state, and it is their
environment or the influence of dark forces that drives them to evil. By
setting the proper example, and working to heal the wounds of a deeply
flawed world, you can set anyone on a righteous path.
--Restitution. If my foes wreak ruin on the world, it is because I failed
to stop them. I must help those harmed by their misdeeds.
--Patience. Change takes time. Those who have walked the path of the wicked
must be given reminders to keep them honest and true. Once you have planted
the seed of righteousness in a creature, you must work day after day to
allow that seed to survive and flourish.
"""
name = "Oath of Zor"
_oath_spells = {3: [spells.Sanctuary, spells.Sleep],
5: [spells.CalmEmotions, spells.HoldPerson],
9: [spells.Counterspell, spells.HypnoticPattern],
13: [spells.OtilukesResilientSphere, spells.Stoneskin],
17: [spells.HoldMonster, spells.WallOfForce]}
features_by_level = defaultdict(list)
features_by_level[3] = [features.EmissaryOfPeace,
features.RebukeTheViolent]
features_by_level[7] = [features.AuraOfTheGuardian]
features_by_level[15] = [features.ProtectiveSpirit]
features_by_level[20] = [features.EmissaryOfRedemption]
class Paladin(CharClass):
@@ -234,8 +335,19 @@ class Paladin(CharClass):
class_skill_choices = ("Athletics", 'Insight', 'Intimidation',
'Medicine', 'Persuasion', 'Religion')
features_by_level = defaultdict(list)
features_by_level[1] = [features.DivineSense, features.LayOnHands]
features_by_level[2] = [features.PaladinFightingStyle,
features.DivineSmite]
features_by_level[3] = [features.DivineHealth,
features.ChannelDivinityPaladin]
features_by_level[5] = [features.ExtraAttackPaladin]
features_by_level[6] = [features.AuraOfProtection]
features_by_level[10] = [features.AuraOfCourage]
features_by_level[11] = [features.ImprovedDivineSmite]
features_by_level[14] = [features.CleansingTouch]
subclasses_available = (OathOfDevotion, OathOfAncients, OathOfVengance,
OathOfCrown, OathOfConquest, OathOfRedemption)
OathOfCrown, OathOfConquest, OathOfRedemption,
OathOfZor)
spellcasting_ability = 'charisma'
spell_slots_by_level = {
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
+15 -6
View File
@@ -115,12 +115,6 @@ class Ranger(CharClass):
20: (0, 4, 3, 3, 3, 2, 0, 0, 0, 0),
}
def __init__(self, level, subclass=None, **params):
super().__init__(level, subclass=subclass, **params)
fighting_style = features.select_ranger_fighting_style(
feature_choices=params.get('feature_choices', []))
self.features_by_level[2].append(fighting_style)
# Revised Ranger
class BeastConclave(SubClass):
@@ -132,6 +126,11 @@ class BeastConclave(SubClass):
"""
name = "Beast Conclave"
features_by_level = defaultdict(list)
features_by_level[3] = [features.AnimalCompanion, features.CompanionsBond]
features_by_level[5] = [features.CoordinatedAttack]
features_by_level[7] = [features.BeastsDefense]
features_by_level[11] = [features.StormOfClawsAndFangs]
features_by_level[15] = [features.SuperiorBeastsDefense]
class HunterConclave(SubClass):
@@ -160,5 +159,15 @@ class DeepStalkerConclave(SubClass):
class RevisedRanger(Ranger):
name = 'Revised Ranger'
features_by_level = defaultdict(list)
features_by_level[1] = [features.FavoredEnemyRevised,
features.NaturalExplorerRevised]
features_by_level[2] = [features.RangerFightingStyle]
features_by_level[3] = [features.PrimevalAwarenessRevised]
features_by_level[6] = [features.GreaterFavoredEnemy]
features_by_level[8] = [features.FleetOfFoot]
features_by_level[10] = [features.HideInPlainSight]
features_by_level[14] = [features.Vanish]
features_by_level[18] = [features.FeralSenses]
features_by_level[20] = [features.FoeSlayer]
subclasses_available = (BeastConclave, HunterConclave, DeepStalkerConclave)
+17
View File
@@ -134,6 +134,23 @@ class Rogue(CharClass):
'Insight', 'Intimidation', 'Investigation',
'Perception', 'Performance', 'Persuasion',
'Sleight of Hand', 'Stealth')
num_skill_choices = 4
features_by_level = defaultdict(list)
features_by_level[1] = [features.Expertise, features.SneakAttack]
features_by_level[2] = [features.CunningAction]
features_by_level[5] = [features.UncannyDodge]
features_by_level[7] = [features.Evasion]
features_by_level[11] = [features.ReliableTalent]
features_by_level[14] = [features.BlindSense]
features_by_level[15] = [features.SlipperyMind]
features_by_level[18] = [features.Elusive]
features_by_level[20] = [features.StrokeOfLuck]
subclasses_available = (Thief, Assassin, ArcaneTrickster,
Inquisitive, Mastermind, Scout, Swashbuckler)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# slippery mind feature
if self.owner.Rogue.level >= 15:
self.saving_throw_proficiencies = ('dexterity', 'intelligence',
'wisdom')
+7
View File
@@ -30,6 +30,10 @@ class WildMagic(SubClass):
"""
name = "Wild Magic"
features_by_level = defaultdict(list)
features_by_level[1] = [features.WildMagicSurge, features.TidesOfChaos]
features_by_level[6] = [features.BendLuck]
features_by_level[14] = [features.ControlledChaos]
features_by_level[18] = [features.SpellBombardment]
# XGTE
@@ -107,6 +111,9 @@ class Sorceror(CharClass):
class_skill_choices = ('Arcana', 'Deception', 'Insight',
'Intimidation', 'Persuasion', 'Religion')
features_by_level = defaultdict(list)
features_by_level[2] = [features.FontOfMagic]
features_by_level[3] = [features.Metamagic]
features_by_level[20] = [features.SorcerousRestoration]
subclasses_available = (DraconicBloodline, WildMagic, DivineSoul,
ShadowMagic, StormSorcery)
spellcasting_ability = 'charisma'
+14
View File
@@ -31,6 +31,10 @@ class Fiend(SubClass):
"""
name = "The Fiend Patron"
features_by_level = defaultdict(list)
features_by_level[1] = [features.DarkOnesBlessing]
features_by_level[6] = [features.DarkOnesOwnLuck]
features_by_level[10] = [features.FiendishResilience]
features_by_level[14] = [features.HurlThroughHell]
class GreatOldOne(SubClass):
@@ -109,7 +113,13 @@ class Hexblade(SubClass):
"""
name = "Hexblade Patron"
weapon_proficiencies = (weapons.MartialWeapon,)
_proficiencies_text = ['martial weapons', 'medium armor', 'shields']
features_by_level = defaultdict(list)
features_by_level[1] = [features.HexbladesCurse, features.HexWarrior]
features_by_level[6] = [features.AccursedSpecter]
features_by_level[10] = [features.ArmorOfHexes]
features_by_level[14] = [features.MasterOfHexes]
class Warlock(CharClass):
@@ -126,6 +136,10 @@ class Warlock(CharClass):
multiclass_weapon_proficiencies = weapon_proficiencies
_multiclass_proficiencies_text = ('light armor', 'simple weapons')
features_by_level = defaultdict(list)
features_by_level[2] = [features.EldritchInvocation]
features_by_level[3] = [features.PactBoon]
features_by_level[11] = [features.MysticArcanum]
features_by_level[20] = [features.EldritchMaster]
subclasses_available = (Archfey, Fiend, GreatOldOne, Undying, Celestial,
Hexblade)
spellcasting_ability = 'charisma'