Source code for DnD_5e.combatant.character

from DnD_5e.combatant import Combatant
from DnD_5e.utility_methods_dnd import validate_dice, TYPE_DICE_TUPLE


[docs] class Character(Combatant): """ This is for characters (PC, NPC, whatever) """ def __init__(self, **kwargs): """ Validate the input and set the instance variables :param kwargs: keyword arguments. Uses all arguments in superclass constructor, plus these: :param level: the character level :type level: an integer between 1 and 20 (inclusive) :param death_saves: a dictionary to track how number of death save fails and successes. Defaults to {0: 0, 1: 0} :raise: ValueError if input is invalid """ super().__init__(**kwargs) self._level = kwargs.get("level") if not self._level: self.get_logger().error("No level provided or level is 0", stack_info=True) raise ValueError("No level provided or level is 0") if not isinstance(self._level, int) or self._level < 1 or self._level > 20: self.get_logger().error("Level must be an integer between 1 and 20", stack_info=True) raise ValueError("Level must be an integer between 1 and 20") self._hit_dice = validate_dice(kwargs.get("hit_dice")) self._death_saves = self.validate_death_saves(kwargs.get('death_saves', {0: 0, 1: 0}))
[docs] def validate_death_saves(self, death_saves): try: # TODO: better check to make sure death saves are valid death_saves[0] # pylint:disable=pointless-statement death_saves[1] # pylint:disable=pointless-statement except KeyError: self.get_logger().error( "Death saves must be a dictionary mapping 0 to number of failures and 1 to number of successes", stack_info=True) raise ValueError( "Death saves must be a dictionary mapping 0 to number of failures and 1 to number of successes") return death_saves
def __eq__(self, other) -> bool: """ Compare *self* and *other* to determine if they are equal based on what is checked in the superclass method as well as :py:attr:`level` :param other: the Character to compare :type other: Character :return: True if *self* equals *other*, False otherwise """ return super().__eq__(other) and self.get_level() == other.get_level() \ and self.get_hit_dice() == other.get_hit_dice()
[docs] def get_level(self) -> int: """ :return: level :rtype: int """ return self._level
[docs] def get_hit_dice(self) -> TYPE_DICE_TUPLE: """ :return: hit dice :rtype: TYPE_DICE_TUPLE """ return self._hit_dice
[docs] def get_death_saves(self): """ :return: death saves """ return self._death_saves
[docs] def reset_death_saves(self): """ Reset death saves to 0 failures and 0 successes. :return: None """ self._death_saves[0] = 0 self._death_saves[1] = 0
[docs] def take_damage(self, damage: int, damage_type: str = None, is_critical: bool = False) -> int: if self.has_condition("unstable"): self.fail_death_save() if is_critical: self.fail_death_save() return super().take_damage(damage, damage_type, is_critical)
[docs] def should_die_from_damage(self, damage_taken: int, damage_type: str = None): return damage_taken >= self.get_max_hp() * 2
[docs] def become_unconscious(self): super().become_unconscious() if self.get_current_hp() == 0: self.remove_condition("stable") self.add_condition("unstable")
[docs] def become_conscious(self): """ Become conscious (removing unconcsious, unstable, and stable conditions) :return: None """ super().become_conscious() self.remove_condition("unstable") self.remove_condition("stable") self.reset_death_saves()
[docs] def fail_death_save(self): """ Record that *self* failed a death save, die if this is the third failed death save :return: None """ self.get_logger().info("%s failed a death save.", self.get_name()) self._death_saves[0] += 1 if self.get_death_saves()[0] > 2: self.get_logger().info("%s failed 3 death saves and will now die.", self.get_name()) self.die()
[docs] def succeed_death_save(self): """ Record that *self* succeeded a death save, stabilize if this is the third successful death save :return: None """ self.get_logger().info("%s succeeded a death save.", self.get_name()) self._death_saves[1] += 1 if self.get_death_saves()[1] > 2: self.get_logger().info("%s succeeded 3 death saves and is now stable.", self.get_name()) self.remove_condition("unstable") self.add_condition("stable")
[docs] def take_turn_unconscious(self): """ If unstable, make a death saving throw. If stable, do nothing. :return: None (no damage was dealt) """ if self.take_saving_throw("death", 10): self.succeed_death_save() else: self.fail_death_save() return super().take_turn_unconscious()
[docs] def reset(self): super().reset() self.reset_death_saves()