import inspect
from DnD_5e import armor, armory
[docs]
class Feature:
"""
This class represents features (class features, race features, background features etc.)
that change a Combatant's stats or how a Combatant can act
"""
[docs]
@classmethod
def get_ol_methods(cls):
"""
:return: the methods that this class overloads
:rtype: list of name, value tuples (name is a str for the name of the function, value is the method object)
"""
methods = inspect.getmembers(cls, lambda x: inspect.ismethod(x) and x.__name__ != 'get_ol_methods')
return methods
[docs]
class UnarmoredDefenseBarbarian(Feature):
[docs]
@classmethod
def get_unarmored_ac(cls, src):
"""
:param src: the Combatant this Feature applies to
:return: unarmored ac (10 + dex + con)
"""
try:
return 10 + src.get_dexterity() + src.get_constitution()
except AttributeError:
raise ValueError("Tried to use Barbarian Unarmored Defense on something that is not a Combatant")
[docs]
class FastMovementBarbarian(Feature):
[docs]
@classmethod
def get_speed(cls, src):
"""
:param src: the Combatant this Feature applies to
:return: speed + 10 (when src isn't wearing heavy armor)
"""
# pylint: disable=protected-access
if inspect.isclass(src.get_armor()) and issubclass(src.get_armor(), armor.HeavyArmor):
return src._speed # TODO: use accessor method instead of instance variable, but avoid infinite recursion
return src._speed + 10
[docs]
class ArcheryFightingStyle(Feature):
[docs]
@classmethod
def get_weapon_attack_mod(cls, src, weapon): # pylint: disable=unused-argument
"""
Get modifiers for weapon attacks.
**Update method**: returns the difference from what src would get normally
(i.e., return 2 for ranged_attack_mod to say ranged_attack_mod is 2 greater than normal)
:param src: the Combatant this Feature applies to
:param weapon: the weapon to look at
:type weapon: :py:class:`Weapon`
:return: attack modifier
:rtype: int
"""
if isinstance(weapon, armory.RangedWeapon):
return 2
return 0
[docs]
class DefenseFightingStyle(Feature):
[docs]
@classmethod
def get_ac(cls, src):
"""
Get ac for the combatant.
**Update method**: returns the difference from what src would get normally (i.e., return 1 to say ac is 1 greater than normal)
:param src: the Combatant this Feature applies to
:return: ac
:rtype: positive integer
"""
if src.get_armor() is not None:
return 1
return 0
[docs]
class UnarmoredDefenseMonk(Feature):
[docs]
@classmethod
def get_unarmored_ac(cls, src):
"""
:param src: the Combatant this Feature applies to
:return: unarmored ac (10 + dex + con)
"""
try:
return 10 + src.get_dexterity() + src.get_wisdom()
except AttributeError:
raise ValueError("Tried to use Monk Unarmored Defense on something that is not a Combatant")
[docs]
class DraconicResilience(Feature):
[docs]
@classmethod
def get_unarmored_ac(cls, src):
"""
Note: this doesn't apply the hit point maximum effects.
:param src: the Combatant this Feature applies to
:return: unarmored ac (10 + dex + con)
"""
try:
return 13 + src.get_dexterity()
except AttributeError:
raise ValueError("Tried to use Draconic Resilience on something that is not a Combatant")