Source code for DnD_5e.character_classes.cleric

from DnD_5e.combatant.character import Character
from DnD_5e.combatant.spellcaster import SpellCaster
from DnD_5e.utility_methods_dnd import ability_to_mod, proficiency_bonus_per_level


[docs] class Cleric(SpellCaster, Character): """ Cleric character class """ def __init__(self, **kwargs): """ Validate the input and set the instance variables :param kwargs: keyword arguments. Some of the keyword arguments are overridden by this class. These are the keyword arguments that the user can/must still define: :param name: what *self* is called. A unique name is recommended but not required :type name: str :param vulnerabilities: all the damage types that *self* is vulnerable to :type vulnerabilities: set, list, or tuple of strings (will be converted to set of strings) :param resistances: all the damage types that *self* is resistant to :type resistances: set, list, or tuple of strings (will be converted to set of strings) :param immunities: all the damage types that *self* is immune to :type immunities: set, list, or tuple of strings (will be converted to set of strings) :param ac: *self's* armor class :type ac: positive integer :param temp_hp: temporary hit points :type temp_hp: non-negative integer :param conditions: all conditions currently affecting *self* :type conditions: list of strings :param strength: strength score. Will be converted to modifier and stored as such. :type strength: integer between 1 and 30 (inclusive) :param strength_mod: dexterity modifier :type strength_mod: int :param dexterity: dexterity score. Will be converted to modifier and stored as such. :type dexterity: integer between 1 and 30 (inclusive) :param dexterity_mod: dexterity modifier :type dexterity_mod: int :param constitution: constitution score. Will be converted to modifier and stored as such. :type constitution: integer between 1 and 30 (inclusive) :param constitution_mod: constitution modifier :type constitution_mod: int :param intelligence: intelligence score. Will be converted to modifier and stored as such. :type intelligence: integer between 1 and 30 (inclusive) :param intelligence_mod: intelligence modifier :type intelligence_mod: int :param wisdom: wisdom score. Will be converted to modifier and stored as such. :type wisdom: integer between 1 and 30 (inclusive) :param wisdom_mod: wisdom modifier :type wisdom_mod: int :param charisma: charisma score. Will be converted to modifier and stored as such. :type charisma: integer between 1 and 30 (inclusive) :param charisma_mod: charisma modifier :type charisma_mod: int :param death_saves: NOT IMPLEMENTED YET :param attacks: NOT IMPLEMENTED YET :param weapons: Weapons (see weapons module) that *self* has available to use :type weapons: list of Weapons :param size: size :type size: one of these strings: "tiny", "small", "medium", "large", "huge", "gargantuan" :param items: NOT IMPLEMENTED YET :param level: character level :type level: integer between 1 and 20 (inclusive) :raise: ValueError if input is invalid """ level = kwargs.get("level") if not level: raise ValueError("No level provided or level is 0") if not isinstance(level, int) or level < 1 or level > 20: raise ValueError("Level must be an integer between 1 and 20") hit_dice = (1 * level, 8) constitution = kwargs.get('constitution') if not constitution: constitution_mod = kwargs.get("constitution_mod") if constitution_mod is not None: if isinstance(constitution_mod, int): self._constitution = constitution_mod else: raise ValueError("Constitution mod must be an integer") else: raise ValueError("Must provide constitution score or modifier") else: # pragma: no cover constitution_mod = ability_to_mod(constitution) max_hp = kwargs.get("max_hp") if max_hp and (not isinstance(max_hp, int) or max_hp <= 0): raise ValueError("Must provide positive integer max hp") max_hp = 8 + constitution_mod if level > 1: max_hp += (5 + constitution_mod) * (level - 1) proficiencies = kwargs.get('proficiencies') if isinstance(proficiencies, (tuple, list, set)): proficiencies = set(proficiencies) elif proficiencies is None: proficiencies = set() else: raise ValueError("Proficiencies must be provided as a set, list, or tuple") proficiencies.add("simple weapons") proficiencies.add("wisdom") proficiencies.add("charisma") proficiency_mod = proficiency_bonus_per_level(level) if level == 1: spell_slots = {1: 2} elif level == 2: spell_slots = {1: 3} elif level == 3: spell_slots = {1: 4, 2: 2} elif level == 4: spell_slots = {1: 4, 2: 3} elif level == 5: spell_slots = {1: 4, 2: 3, 3: 2} elif level == 6: spell_slots = {1: 4, 2: 3, 3: 3} elif level == 7: spell_slots = {1: 4, 2: 3, 3: 3, 4: 2} elif level == 8: spell_slots = {1: 4, 2: 3, 3: 3, 4: 2} elif level == 9: spell_slots = {1: 4, 2: 3, 3: 3, 4: 3, 5: 1} elif level == 10: spell_slots = {1: 4, 2: 3, 3: 3, 4: 3, 5: 2} elif level < 13: spell_slots = {1: 4, 2: 3, 3: 3, 4: 3, 5: 2, 6: 1} elif level < 15: spell_slots = {1: 4, 2: 3, 3: 3, 4: 3, 5: 2, 6: 1, 7: 1} elif level < 17: spell_slots = {1: 4, 2: 3, 3: 3, 4: 3, 5: 2, 6: 1, 7: 1, 8: 1} elif level == 17: spell_slots = {1: 4, 2: 3, 3: 3, 4: 3, 5: 2, 6: 1, 7: 1, 8: 1, 9: 1} elif level == 18: spell_slots = {1: 4, 2: 3, 3: 3, 4: 3, 5: 3, 6: 1, 7: 1, 8: 1, 9: 1} elif level == 19: spell_slots = {1: 4, 2: 3, 3: 3, 4: 3, 5: 3, 6: 2, 7: 1, 8: 1, 9: 1} else: spell_slots = {1: 4, 2: 3, 3: 3, 4: 3, 5: 3, 6: 2, 7: 2, 8: 1, 9: 1} kwargs.update({"hit_dice": hit_dice, "max_hp": max_hp, "proficiencies": proficiencies, "proficiency_mod": proficiency_mod, "spell_ability": "wisdom", "spell_slots": spell_slots}) super().__init__(**kwargs) if self.get_level() > 1: self.add_feature("channel divinity") if self.get_level() < 6: self._channel_divinity_slots = 1 elif self.get_level() < 18: self._channel_divinity_slots = 2 else: self._channel_divinity_slots = 3 if self.get_level() > 4: self.add_feature("destroy undead") if self.get_level() < 8: self._destroy_undead_cr = 1/2 elif self.get_level() < 11: self._destroy_undead_cr = 1 elif self.get_level() < 14: self._destroy_undead_cr = 2 elif self.get_level() < 17: self._destroy_undead_cr = 3 else: self._destroy_undead_cr = 4 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 destroy undead cr :param other: the Cleric to be compared :type other: Cleric :return: True if *self* equals *other*, False otherwise :rtype: bool """ result = super().__eq__(other) if self.has_feature("destroy undead"): result = result and self.get_destroy_undead_cr() == other.get_destroy_undead_cr() return result
[docs] def current_eq(self, other) -> bool: """ Check to see if *self* is identical to *other* by looking at everything in *equals* as well as the following attributes: channel divinity slots :param other: the Cleric to be compared :type other: Cleric :return: True if *self* is identical to *other*, False otherwise :rtype: bool """ result = super().current_eq(other) if self.has_feature("channel divinity"): result = result and self.get_channel_divinity_slots() == other.get_channel_divinity_slots() return result
[docs] def get_channel_divinity_slots(self) -> int: """ :return: channel divinity slots :rtype: non-negative integer """ try: return self._channel_divinity_slots except AttributeError: return 0
[docs] def channel_divinity(self, use_type: str = "turn undead"): """ Channel divinity (uses 1 Channel Divinity slot) :param use_type: the kind of use (e.g., "turn undead") :type use_type: str :return: None """ if not self.has_feature("channel divinity"): self.get_logger().error("You don't yet know how to channel divinity", stack_info=True) raise ValueError("You don't yet know how to channel divinity") if not self._channel_divinity_slots: self.get_logger().error("You have no more charges of channel divinity left", stack_info=True) raise ValueError("You have no more charges of channel divinity left") self._channel_divinity_slots -= 1 self.get_logger().info("%s channels divinity for %s", self.get_name(), use_type)
[docs] def get_destroy_undead_cr(self): """ :return: Max challenge rating of Creatures that will be destroyed with Destroy Undead :rtype: number """ try: return self._destroy_undead_cr except AttributeError: self.get_logger().error("You cannot yet destroy undead", stack_info=True) raise ValueError("You cannot yet destroy undead")