diff --git a/royalnet/packs/rpg/__init__.py b/royalnet/packs/rpg/__init__.py index 4d839b28..8cb183d1 100644 --- a/royalnet/packs/rpg/__init__.py +++ b/royalnet/packs/rpg/__init__.py @@ -4,4 +4,3 @@ from .commands import available_commands from .tables import available_tables __all__ = ["commands", "tables", "available_commands", "available_tables"] - diff --git a/royalnet/packs/rpg/commands/__init__.py b/royalnet/packs/rpg/commands/__init__.py index 569bae5e..db8cc1ec 100644 --- a/royalnet/packs/rpg/commands/__init__.py +++ b/royalnet/packs/rpg/commands/__init__.py @@ -6,8 +6,6 @@ from .dndinfo import DndinfoCommand from .dndnew import DndnewCommand from .dndedit import DndeditCommand from .dndroll import DndrollCommand -from .dndrolladv import DndrolladvCommand -from .dndrolldis import DndrolldisCommand # Enter the commands of your Pack here! available_commands = [ @@ -18,8 +16,6 @@ available_commands = [ DndnewCommand, DndeditCommand, DndrollCommand, - DndrolladvCommand, - DndrolldisCommand, ] # Don't change this, it should automatically generate __all__ diff --git a/royalnet/packs/rpg/commands/dndedit.py b/royalnet/packs/rpg/commands/dndedit.py index 12881cc9..918e431d 100644 --- a/royalnet/packs/rpg/commands/dndedit.py +++ b/royalnet/packs/rpg/commands/dndedit.py @@ -1,60 +1,35 @@ import re from royalnet.commands import * +from .dndnew import DndnewCommand from ..tables import DndCharacter, DndActiveCharacter -class DndeditCommand(Command): +class DndeditCommand(DndnewCommand): name: str = "dndedit" description: str = "Edit the active DnD character." aliases = ["de", "dnde", "edit", "dedit"] - syntax = "{name}\n" \ - "LV {level}\n" \ - "\n" \ - "STR {strength}\n" \ - "DEX {dexterity}\n" \ - "CON {constitution}\n" \ - "INT {intelligence}\n" \ - "WIS {wisdom}\n" \ - "CHA {charisma}\n" \ - "\n" \ - "MAXHP {max_hp}\n" \ - "AC {armor_class}" - tables = {DndCharacter, DndActiveCharacter} async def run(self, args: CommandArgs, data: CommandData) -> None: - name, level, strength, dexterity, constitution, intelligence, wisdom, charisma, max_hp, armor_class = \ - args.match(r"([\w ]+\w)\s*" - r"LV\s+(\d+)\s+" - r"STR\s+(\d+)\s+" - r"DEX\s+(\d+)\s+" - r"CON\s+(\d+)\s+" - r"INT\s+(\d+)\s+" - r"WIS\s+(\d+)\s+" - r"CHA\s+(\d+)\s+" - r"MAXHP\s+(\d+)\s+" - r"AC\s+(\d+)", re.IGNORECASE) - try: - int(name) - except ValueError: - pass - else: - raise CommandError("Character names cannot be composed of only a number.") + character_sheet = args.joined() + + if character_sheet == "": + await data.reply(self._syntax()) + return + author = await data.get_author(error_if_none=True) - char = author.dnd_active_character.character - char.name = name - char.level = level - char.strength = strength - char.dexterity = dexterity - char.constitution = constitution - char.intelligence = intelligence - char.wisdom = wisdom - char.charisma = charisma - char.max_hp = max_hp - char.armor_class = armor_class - data.session.add(char) + if author.dnd_active_character is None: + raise CommandError("You don't have an active character.") + + char: DndCharacter = author.dnd_active_character.character + + arguments = self._parse(character_sheet) + for key in arguments: + char.__setattr__(key, arguments[key]) + await data.session_commit() + await data.reply(f"✅ Edit successful!") diff --git a/royalnet/packs/rpg/commands/dndnew.py b/royalnet/packs/rpg/commands/dndnew.py index 4c8af8f9..18a1b540 100644 --- a/royalnet/packs/rpg/commands/dndnew.py +++ b/royalnet/packs/rpg/commands/dndnew.py @@ -1,6 +1,8 @@ import re +# noinspection PyUnresolvedReferences from royalnet.commands import * from ..tables import DndCharacter +from ..utils import DndProficiencyType class DndnewCommand(Command): @@ -10,51 +12,60 @@ class DndnewCommand(Command): aliases = ["dn", "dndn", "new", "dnew"] - syntax = "{name}\n" \ - "LV {level}\n" \ - "\n" \ - "STR {strength}\n" \ - "DEX {dexterity}\n" \ - "CON {constitution}\n" \ - "INT {intelligence}\n" \ - "WIS {wisdom}\n" \ - "CHA {charisma}\n" \ - "\n" \ - "MAXHP {max_hp}\n" \ - "AC {armor_class}" + syntax = "{name}\n{character_sheet}" tables = {DndCharacter} + def _search_value(self, name: str, string: str): + return re.search(r"\s*" + name + r"\s*([0-9]+)\s*", string, re.IGNORECASE) + + def _parse(self, character_sheet: str) -> dict: + columns = list(self.alchemy.DndCharacter.__table__.columns) + column_names = [column.name for column in columns if (not column.primary_key and + not column.foreign_keys and + column.name != "name")] + arguments = {} + for column_name in column_names: + match = self._search_value(column_name, character_sheet) + if match: + if column_name.endswith("_proficiency"): + arguments[column_name] = DndProficiencyType(float(match.group(1))) + else: + arguments[column_name] = match.group(1) + return arguments + + def _syntax(self) -> str: + columns = list(self.alchemy.DndCharacter.__table__.columns) + column_names = [column.name for column in columns if (not column.primary_key and + not column.foreign_keys and + column.name != "name")] + message = "ℹ️ Character Sheet syntax:\n[p]\nName\n" + for column_name in column_names: + message += f"{column_name} _\n" + message += "[/p]" + return message + async def run(self, args: CommandArgs, data: CommandData) -> None: - name, level, strength, dexterity, constitution, intelligence, wisdom, charisma, max_hp, armor_class = \ - args.match(r"([\w ]+\w)\s*" - r"LV\s+(\d+)\s+" - r"STR\s+(\d+)\s+" - r"DEX\s+(\d+)\s+" - r"COS\s+(\d+)\s+" - r"INT\s+(\d+)\s+" - r"WIS\s+(\d+)\s+" - r"CHA\s+(\d+)\s+" - r"MAXHP\s+(\d+)\s+" - r"AC\s+(\d+)", re.IGNORECASE) + character_sheet = args.joined() + + if character_sheet == "": + await data.reply(self._syntax()) + return + + creator = await data.get_author() + + name, rest = character_sheet.split("\n", 1) + + character = self.alchemy.DndCharacter(name=name, creator=creator, **self._parse(rest)) + data.session.add(character) + try: - int(name) - except ValueError: - pass - else: - raise CommandError("Character names cannot be composed of only a number.") - author = await data.get_author(error_if_none=True) - char = self.alchemy.DndCharacter(name=name, - level=level, - strength=strength, - dexterity=dexterity, - constitution=constitution, - intelligence=intelligence, - wisdom=wisdom, - charisma=charisma, - max_hp=max_hp, - armor_class=armor_class, - creator=author) - data.session.add(char) - await data.session_commit() - await data.reply(f"✅ Character [b]{char.name}[/b] ([c]{char.character_id}[/c]) was created!") + await data.session_commit() + except Exception as err: + # THIS IS INTENDED + if err.__class__.__name__ == "IntegrityError": + param_name = re.search(r'in column "(\S+)"', err.args[0]).group(1) + raise CommandError(f"Mandatory parameter '{param_name}' is missing.") + raise + + await data.reply(f"✅ Character [b]{character.name}[/b] created!") diff --git a/royalnet/packs/rpg/commands/dndroll.py b/royalnet/packs/rpg/commands/dndroll.py index 348b34a4..09fdc63a 100644 --- a/royalnet/packs/rpg/commands/dndroll.py +++ b/royalnet/packs/rpg/commands/dndroll.py @@ -1,94 +1,146 @@ -import typing +import re import random from royalnet.commands import * -from royalnet.utils import plusformat from ..tables import DndCharacter, DndActiveCharacter +from royalnet.utils import plusformat class DndrollCommand(Command): name: str = "dndroll" - description: str = "Roll as the active DnD character." + description: str = "Roll dice as the active DnD character." - aliases = ["dr", "dndr", "droll"] - - syntax = "{stat} [proficiency] [modifier]" + aliases = ["dr", "dndr", "roll", "droll"] tables = {DndCharacter, DndActiveCharacter} - @staticmethod - def _roll(): - return random.randrange(1, 21) + _skill_names = { + "str": "strength", + "for": "strength", + "dex": "dexterity", + "des": "dexterity", + "con": "constitution", + "cos": "constitution", + "inte": "intelligence", + "wis": "wisdom", + "sag": "wisdom", + "cha": "charisma", + "car": "charisma", - _roll_string = "1d20" + "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 - stat: str = args[0] - second: typing.Optional[str] = args.optional(1) - third: typing.Optional[str] = args.optional(2) + + first = args[0] + second = args.optional(1) + third = args.optional(2) + + advantage = False + disadvantage = False + extra_modifier = 0 if third: - extra_mod: int = int(third) - else: - extra_mod: int = 0 - - if second: - if second.startswith("e") or second.startswith("x"): - proficiency_mul: float = 2.0 - proficiency_name: str = " with Expertise" - elif second.startswith("f") or second.startswith("n") or second.startswith("c"): - proficiency_mul: float = 1.0 - proficiency_name: str = " with Proficiency" - elif second.startswith("h") or second == "/" or second.startswith("m"): - proficiency_mul: float = 0.5 - proficiency_name: str = " with Half Proficiency" - elif second.startswith("h") or second == "/" or second.startswith("m"): - proficiency_mul: float = 0.0 - proficiency_name: str = " [i]without Proficiency[/i]" + try: + extra_modifier = int(third) + 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 CommandError(f"Unknown proficiency type '{second}'") - proficiency_mod: int = int(char.proficiency_bonus * proficiency_mul) + raise InvalidInputError("Invalid advantage string (second parameter).") + + elif second: + try: + extra_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: - proficiency_name: str = "" - proficiency_mod: int = 0 + raise CommandError("Invalid skill name (first parameter).") - if stat.startswith("st") or stat.startswith("fo"): - stat_mod: int = char.strength_mod - stat_name: str = "[i]STR[/i]" - elif stat.startswith("de"): - stat_mod: int = char.dexterity_mod - stat_name: str = "[i]DEX[/i]" - elif stat.startswith("co"): - stat_mod: int = char.constitution_mod - stat_name: str = "[i]CON[/i]" - elif stat.startswith("in"): - stat_mod: int = char.intelligence_mod - stat_name: str = "[i]INT[/i]" - elif stat.startswith("wi") or stat.startswith("sa"): - stat_mod: int = char.wisdom_mod - stat_name: str = "[i]WIS[/i]" - elif stat.startswith("ch") or stat.startswith("ca"): - stat_mod: int = char.charisma_mod - stat_name: str = "[i]CHA[/i]" + skill_modifier = char.__getattribute__(skill_name) + modifier = skill_modifier + extra_modifier + 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: - raise CommandError(f"Unknown stat '{stat}'") - - total_mod = stat_mod + proficiency_mod + extra_mod - - roll = self._roll() - - result = roll + total_mod - - await data.reply(f"🎲 Rolling {stat_name}{proficiency_name}{plusformat(extra_mod, empty_if_zero=True)}:\n" - f"{self._roll_string}" - f"{plusformat(stat_mod, empty_if_zero=True)}" - f"{plusformat(proficiency_mod, empty_if_zero=True)}" - f"{plusformat(extra_mod, empty_if_zero=True)}" - f" = " - f"{roll}{plusformat(total_mod, empty_if_zero=True)}" - f" = " - f"[b]{result}[/b]") + 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/commands/dndrolladv.py b/royalnet/packs/rpg/commands/dndrolladv.py deleted file mode 100644 index 9d918597..00000000 --- a/royalnet/packs/rpg/commands/dndrolladv.py +++ /dev/null @@ -1,26 +0,0 @@ -import typing -import random -from royalnet.commands import * -from royalnet.utils import plusformat -from .dndroll import DndrollCommand -from ..tables import DndCharacter, DndActiveCharacter - - -class DndrolladvCommand(DndrollCommand): - name: str = "dndrolladv" - - description: str = "Roll with advantage as the active DnD character." - - aliases = ["dra", "dndra", "drolladv"] - - syntax = "{stat} [proficiency] [modifier]" - - tables = {DndCharacter, DndActiveCharacter} - - @staticmethod - def _roll(): - first = random.randrange(1, 21) - second = random.randrange(1, 21) - return max(first, second) - - _roll_string = "2d20h1" diff --git a/royalnet/packs/rpg/commands/dndrolldis.py b/royalnet/packs/rpg/commands/dndrolldis.py deleted file mode 100644 index e195c34d..00000000 --- a/royalnet/packs/rpg/commands/dndrolldis.py +++ /dev/null @@ -1,26 +0,0 @@ -import typing -import random -from royalnet.commands import * -from royalnet.utils import plusformat -from .dndroll import DndrollCommand -from ..tables import DndCharacter, DndActiveCharacter - - -class DndrolldisCommand(DndrollCommand): - name: str = "dndrolldis" - - description: str = "Roll with disadvantage as the active DnD character." - - aliases = ["drd", "dndrd", "drolldis"] - - syntax = "{stat} [proficiency] [modifier]" - - tables = {DndCharacter, DndActiveCharacter} - - @staticmethod - def _roll(): - first = random.randrange(1, 21) - second = random.randrange(1, 21) - return min(first, second) - - _roll_string = "2d20l1" diff --git a/royalnet/packs/rpg/tables/dndcharacters.py b/royalnet/packs/rpg/tables/dndcharacters.py index e3c4cb35..7699d1ef 100644 --- a/royalnet/packs/rpg/tables/dndcharacters.py +++ b/royalnet/packs/rpg/tables/dndcharacters.py @@ -1,6 +1,7 @@ from sqlalchemy import * from sqlalchemy.orm import * from sqlalchemy.ext.declarative import * +from ..utils import DndProficiencyType class DndCharacter: @@ -20,71 +21,267 @@ class DndCharacter: @declared_attr def name(self): - return Column(String) + return Column(String, nullable=False) @declared_attr + def strength_score(self): + return Column(Integer, nullable=False) + + @property def strength(self): - return Column(Integer) - - @property - def strength_mod(self): - return (self.strength - 10) // 2 + return (self.strength_score - 10) // 2 @declared_attr + def dexterity_score(self): + return Column(Integer, nullable=False) + + @property def dexterity(self): - return Column(Integer) - - @property - def dexterity_mod(self): - return (self.dexterity - 10) // 2 + return (self.dexterity_score - 10) // 2 @declared_attr + def constitution_score(self): + return Column(Integer, nullable=False) + + @property def constitution(self): - return Column(Integer) - - @property - def constitution_mod(self): - return (self.constitution - 10) // 2 + return (self.constitution_score - 10) // 2 @declared_attr + def intelligence_score(self): + return Column(Integer, nullable=False) + + @property def intelligence(self): - return Column(Integer) - - @property - def intelligence_mod(self): - return (self.intelligence - 10) // 2 + return (self.intelligence_score - 10) // 2 @declared_attr + def wisdom_score(self): + return Column(Integer, nullable=False) + + @property def wisdom(self): - return Column(Integer) - - @property - def wisdom_mod(self): - return (self.wisdom - 10) // 2 + return (self.wisdom_score - 10) // 2 @declared_attr - def charisma(self): - return Column(Integer) + def charisma_score(self): + return Column(Integer, nullable=False) @property - def charisma_mod(self): - return (self.charisma - 10) // 2 + def charisma(self): + return (self.charisma_score - 10) // 2 @declared_attr def level(self): - return Column(Integer) + return Column(Integer, nullable=False) @property def proficiency_bonus(self): return ((self.level - 1) // 4) + 2 + @declared_attr + def current_hp(self): + return Column(Integer, nullable=False) + @declared_attr def max_hp(self): - return Column(Integer) + return Column(Integer, nullable=False) @declared_attr def armor_class(self): - return Column(Integer) + return Column(Integer, nullable=False) + + @declared_attr + def strength_save_proficiency(self): + return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + + @declared_attr + def dexterity_save_proficiency(self): + return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + + @declared_attr + def constitution_save_proficiency(self): + return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + + @declared_attr + def intelligence_save_proficiency(self): + return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + + @declared_attr + def wisdom_save_proficiency(self): + return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + + @declared_attr + def charisma_save_proficiency(self): + return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + + @declared_attr + def acrobatics_proficiency(self): + return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + + @declared_attr + def animal_handling_proficiency(self): + return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + + @declared_attr + def arcana_proficiency(self): + return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + + @declared_attr + def athletics_proficiency(self): + return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + + @declared_attr + def deception_proficiency(self): + return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + + @declared_attr + def history_proficiency(self): + return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + + @declared_attr + def insight_proficiency(self): + return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + + @declared_attr + def intimidation_proficiency(self): + return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + + @declared_attr + def investigation_proficiency(self): + return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + + @declared_attr + def medicine_proficiency(self): + return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + + @declared_attr + def nature_proficiency(self): + return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + + @declared_attr + def perception_proficiency(self): + return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + + @declared_attr + def performance_proficiency(self): + return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + + @declared_attr + def persuasion_proficiency(self): + return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + + @declared_attr + def religion_proficiency(self): + return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + + @declared_attr + def sleight_of_hand_proficiency(self): + return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + + @declared_attr + def stealth_proficiency(self): + return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + + @declared_attr + def survival_proficiency(self): + return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE) + + @property + def strength_save(self): + return self.strength + self.proficiency_bonus * self.strength_save_proficiency.value + + @property + def dexterity_save(self): + return self.dexterity + self.proficiency_bonus * self.dexterity_save_proficiency.value + + @property + def constitution_save(self): + return self.constitution + self.proficiency_bonus * self.constitution_save_proficiency.value + + @property + def intelligence_save(self): + return self.intelligence + self.proficiency_bonus * self.intelligence_save_proficiency.value + + @property + def wisdom_save(self): + return self.wisdom + self.proficiency_bonus * self.wisdom_save_proficiency.value + + @property + def charisma_save(self): + return self.charisma + self.proficiency_bonus * self.charisma_save_proficiency.value + + @property + def acrobatics(self): + return self.dexterity + self.proficiency_bonus * self.acrobatics_proficiency.value + + @property + def animal_handling(self): + return self.wisdom + self.proficiency_bonus * self.animal_handling_proficiency.value + + @property + def arcana(self): + return self.intelligence + self.proficiency_bonus * self.arcana_proficiency.value + + @property + def athletics(self): + return self.strength + self.proficiency_bonus * self.athletics_proficiency.value + + @property + def deception(self): + return self.charisma + self.proficiency_bonus * self.deception_proficiency.value + + @property + def history(self): + return self.intelligence + self.proficiency_bonus * self.history_proficiency.value + + @property + def insight(self): + return self.wisdom + self.proficiency_bonus * self.insight_proficiency.value + + @property + def intimidation(self): + return self.charisma + self.proficiency_bonus * self.intimidation_proficiency.value + + @property + def investigation(self): + return self.intelligence + self.proficiency_bonus * self.investigation_proficiency.value + + @property + def medicine(self): + return self.wisdom + self.proficiency_bonus * self.medicine_proficiency.value + + @property + def nature(self): + return self.intelligence + self.proficiency_bonus * self.nature_proficiency.value + + @property + def perception(self): + return self.wisdom + self.proficiency_bonus * self.perception_proficiency.value + + @property + def performance(self): + return self.charisma + self.proficiency_bonus * self.performance_proficiency.value + + @property + def persuasion(self): + return self.charisma + self.proficiency_bonus * self.persuasion_proficiency.value + + @property + def religion(self): + return self.intelligence + self.proficiency_bonus * self.religion_proficiency.value + + @property + def sleight_of_hand(self): + return self.dexterity + self.proficiency_bonus * self.sleight_of_hand_proficiency.value + + @property + def stealth(self): + return self.dexterity + self.proficiency_bonus * self.stealth_proficiency.value + + @property + def survival(self): + return self.wisdom + self.proficiency_bonus * self.survival_proficiency.value def __repr__(self): return f"<{self.__class__.__qualname__} {self.name}>" @@ -92,14 +289,13 @@ class DndCharacter: def __str__(self): return f"{self.name}" - def character_sheet(self): - return f"{self.name}\n" \ - f"LV {self.level}\n\n" \ - f"STR {self.strength}\n" \ - f"DEX {self.dexterity}\n" \ - f"CON {self.constitution}\n" \ - f"INT {self.intelligence}\n" \ - f"WIS {self.wisdom}\n" \ - f"CHA {self.charisma}\n\n" \ - f"MAXHP {self.max_hp}\n" \ - f"AC {self.armor_class}\n" \ + def character_sheet(self) -> str: + columns = list(self.__class__.__table__.columns) + column_names = [column.name for column in columns if (not column.primary_key and + not column.foreign_keys and + column.name != "name")] + message = f"[b]{self.name}[/b]\n" + for column_name in column_names: + value = self.__getattribute__(column_name) + message += f"{column_name} {value}\n" + return message diff --git a/royalnet/packs/rpg/utils/__init__.py b/royalnet/packs/rpg/utils/__init__.py new file mode 100644 index 00000000..2d229e83 --- /dev/null +++ b/royalnet/packs/rpg/utils/__init__.py @@ -0,0 +1,3 @@ +from .dndproficiencytype import DndProficiencyType + +__all__ = ["DndProficiencyType"] diff --git a/royalnet/packs/rpg/utils/dndproficiencytype.py b/royalnet/packs/rpg/utils/dndproficiencytype.py new file mode 100644 index 00000000..d7ba925b --- /dev/null +++ b/royalnet/packs/rpg/utils/dndproficiencytype.py @@ -0,0 +1,11 @@ +import enum + + +class DndProficiencyType(enum.Enum): + NONE = 0 + HALF_PROFICIENCY = 0.5 + FULL_PROFICIENCY = 1 + EXPERTISE = 2 + + def __str__(self): + return str(self.value) \ No newline at end of file