diff --git a/royalnet/packs/rpg/commands/dndroll.py b/royalnet/packs/rpg/commands/dndroll.py new file mode 100644 index 00000000..c534b1b4 --- /dev/null +++ b/royalnet/packs/rpg/commands/dndroll.py @@ -0,0 +1,145 @@ +import re +import random +from royalnet.commands import * +from ..tables import DndCharacter, DndActiveCharacter +from royalnet.utils import plusformat + + +class DndrollCommand(Command): + name: str = "dndroll" + + description: str = "Roll dice as the active DnD character." + + aliases = ["dr", "dndr", "roll", "droll"] + + tables = {DndCharacter, DndActiveCharacter} + + _skill_names = { + "str": "strength", + "for": "strength", + "dex": "dexterity", + "des": "dexterity", + "con": "constitution", + "cos": "constitution", + "inte": "intelligence", + "wis": "wisdom", + "sag": "wisdom", + "cha": "charisma", + "car": "charisma", + + "ststr": "strength_save", + "stfor": "strength_save", + "stdex": "dexterity_save", + "stdes": "dexterity_save", + "stcon": "constitution_save", + "stcos": "constitution_save", + "stint": "intelligence_save", + "stwis": "wisdom_save", + "stsag": "wisdom_save", + "stcha": "charisma_save", + "stcar": "charisma_save", + + "tsstr": "strength_save", + "tsfor": "strength_save", + "tsdex": "dexterity_save", + "tsdes": "dexterity_save", + "tscon": "constitution_save", + "tscos": "constitution_save", + "tsint": "intelligence_save", + "tswis": "wisdom_save", + "tssag": "wisdom_save", + "tscha": "charisma_save", + "tscar": "charisma_save", + + "acr": "acrobatics", + "add": "animal_handling", + "ani": "animal_handling", + "arc": "arcana", + "ath": "athletics", + "dec": "deception", + "ing": "deception", + "his": "history", + "sto": "history", + "ins": "insight", + "intu": "insight", + "inti": "intimidation", + "inv": "investigation", + "med": "medicine", + "nat": "nature", + "perc": "perception", + "perf": "performance", + "pers": "persuasion", + "rel": "religion", + "sle": "sleight_of_hand", + "soh": "sleight_of_hand", + "rap": "sleight_of_hand", + "ste": "stealth", + "nas": "stealth", + "sur": "survival", + "sop": "sopravvivenza", + } + + async def run(self, args: CommandArgs, data: CommandData) -> None: + author = await data.get_author(error_if_none=True) + if author.dnd_active_character is None: + raise CommandError("You don't have an active character.") + char: DndCharacter = author.dnd_active_character.character + + first = args[0] + second = args.optional(1) + third = args.optional(2) + + advantage = False + disadvantage = False + modifier = 0 + + if third: + try: + modifier = int(second) + except ValueError: + raise InvalidInputError("Invalid modifier value (third parameter).") + if second.startswith("a") or second.startswith("v"): + advantage = True + elif second.startswith("d") or second.startswith("d"): + disadvantage = True + else: + raise InvalidInputError("Invalid advantage string (second parameter).") + + elif second: + try: + modifier = int(second) + except ValueError: + if second.startswith("a") or second.startswith("v"): + advantage = True + elif second.startswith("d") or second.startswith("d"): + disadvantage = True + else: + raise InvalidInputError("Invalid modifier value or advantage string (second parameter).") + + skill_short_name = first + for root in self._skill_names: + if skill_short_name.startswith(root): + skill_name = self._skill_names[root] + break + else: + raise CommandError("Invalid skill name (first parameter).") + + modifier = char.__getattribute__(skill_name) + modifier_str = plusformat(modifier, empty_if_zero=True) + + if advantage: + roll_a = random.randrange(1, 21) + roll_b = random.randrange(1, 21) + roll = max([roll_a, roll_b]) + total = roll + modifier + await data.reply(f"🎲 2d20h1{modifier_str} = ({roll_a}|{roll_b}){modifier_str} = [b]{total}[/b]") + elif disadvantage: + roll_a = random.randrange(1, 21) + roll_b = random.randrange(1, 21) + roll = min([roll_a, roll_b]) + total = roll + modifier + await data.reply(f"🎲 2d20l1{modifier_str} = ({roll_a}|{roll_b}){modifier_str} = [b]{total}[/b]") + else: + roll = random.randrange(1, 21) + total = roll + modifier + await data.reply(f"🎲 1d20{modifier_str} = {roll}{modifier_str} = [b]{total}[/b]") diff --git a/royalnet/packs/rpg/tables/dndcharacters.py b/royalnet/packs/rpg/tables/dndcharacters.py index 6bd346c6..8de0562f 100644 --- a/royalnet/packs/rpg/tables/dndcharacters.py +++ b/royalnet/packs/rpg/tables/dndcharacters.py @@ -92,27 +92,27 @@ class DndCharacter: return Column(Integer, nullable=False) @declared_attr - def strength_proficiency(self): + def strength_save_proficiency(self): return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) @declared_attr - def dexterity_proficiency(self): + def dexterity_save_proficiency(self): return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) @declared_attr - def constitution_proficiency(self): + def constitution_save_proficiency(self): return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) @declared_attr - def intelligence_proficiency(self): + def intelligence_save_proficiency(self): return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) @declared_attr - def wisdom_proficiency(self): + def wisdom_save_proficiency(self): return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) @declared_attr - def charisma_proficiency(self): + def charisma_save_proficiency(self): return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) @declared_attr @@ -187,29 +187,33 @@ class DndCharacter: def survival_proficiency(self): return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + @declared_attr + def sanity_proficiency(self): + return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + @property def strength_save(self): - return self.strength + self.proficiency_bonus * self.strength_proficiency.value + return self.strength + self.proficiency_bonus * self.strength_save_proficiency.value @property def dexterity_save(self): - return self.dexterity + self.proficiency_bonus * self.dexterity_proficiency.value + return self.dexterity + self.proficiency_bonus * self.dexterity_save_proficiency.value @property def constitution_save(self): - return self.constitution + self.proficiency_bonus * self.constitution_proficiency.value + return self.constitution + self.proficiency_bonus * self.constitution_save_proficiency.value @property def intelligence_save(self): - return self.intelligence + self.proficiency_bonus * self.intelligence_proficiency.value + return self.intelligence + self.proficiency_bonus * self.intelligence_save_proficiency.value @property def wisdom_save(self): - return self.wisdom + self.proficiency_bonus * self.wisdom_proficiency.value + return self.wisdom + self.proficiency_bonus * self.wisdom_save_proficiency.value @property def charisma_save(self): - return self.charisma + self.proficiency_bonus * self.charisma_proficiency.value + return self.charisma + self.proficiency_bonus * self.charisma_save_proficiency.value @property def acrobatics(self):