From 897107143ad7bc14c941195a2e97162172c2f257 Mon Sep 17 00:00:00 2001 From: Nathaniel Landau Date: Sat, 15 Jun 2024 17:17:26 -0400 Subject: [PATCH] fix(gameplay): improve roll display (#154) --- src/valentina/cogs/developer.py | 28 ++++++++- src/valentina/utils/helpers.py | 44 ++++++++++++++ src/valentina/views/character_sheet.py | 59 ++++++------------ src/valentina/views/roll_display.py | 83 ++++++++++++++++---------- 4 files changed, 139 insertions(+), 75 deletions(-) diff --git a/src/valentina/cogs/developer.py b/src/valentina/cogs/developer.py index 63913f8c..cd66d037 100644 --- a/src/valentina/cogs/developer.py +++ b/src/valentina/cogs/developer.py @@ -15,7 +15,13 @@ from loguru import logger from valentina.characters import RNGCharGen -from valentina.constants import PREF_MAX_EMBED_CHARACTERS, CharClass, EmbedColor, LogLevel +from valentina.constants import ( + PREF_MAX_EMBED_CHARACTERS, + CharClass, + EmbedColor, + InventoryItemType, + LogLevel, +) from valentina.models import ( AWSService, Campaign, @@ -23,8 +29,10 @@ CampaignBookChapter, ChangelogPoster, Character, + CharacterSheetSection, GlobalProperty, Guild, + InventoryItem, RollProbability, User, ) @@ -188,8 +196,24 @@ async def create_dummy_data(self, ctx: ValentinaContext) -> None: # noqa: C901 ) created_characters.append(character) - # Associated characters with campaigns + # Add inventory & custom sections to characters and associate characters with campaigns for character in created_characters: + for _ in range(3): + i = InventoryItem( + name=Faker().sentence(nb_words=2).rstrip("."), + description=Faker().sentence(), + character=str(character.id), + type=random.choice([x.name for x in InventoryItemType]), + ) + item = await i.insert() + character.inventory.append(item) + + section = CharacterSheetSection( + title=Faker().sentence(nb_words=3).rstrip("."), + content=Faker().paragraph(nb_sentences=3), + ) + character.sheet_sections.append(section) + campaign = random.choice(created_campaigns) character.campaign = str(campaign.id) await character.save() diff --git a/src/valentina/utils/helpers.py b/src/valentina/utils/helpers.py index f7a3a512..f09e06ef 100644 --- a/src/valentina/utils/helpers.py +++ b/src/valentina/utils/helpers.py @@ -14,6 +14,50 @@ _rng = default_rng() +def convert_int_to_emoji(num: int, markdown: bool = False) -> str: + """Convert an integer to an emoji or a string. + + Args: + num (int): The integer to convert. + markdown (bool, optional): Whether to wrap numbers larger than emojis in markdown code. Defaults to False. + + Returns: + str: The emoji corresponding to the integer. + + Examples: + >>> convert_int_to_emoji(1) + ':one:' + + >>> convert_int_to_emoji(10) + ':keycap_ten:' + + >>> convert_int_to_emoji(11) + '11' + + >>> convert_int_to_emoji(11, markdown=True) + '`11`' + """ + if -1 <= num <= 10: # noqa: PLR2004 + return ( + str(num) + .replace("10", ":keycap_ten:") + .replace("0", ":zero:") + .replace("1", ":one:") + .replace("2", ":two:") + .replace("3", ":three:") + .replace("4", ":four:") + .replace("5", ":five:") + .replace("6", ":six:") + .replace("7", ":seven:") + .replace("8", ":eight:") + .replace("9", ":nine:") + ) + if markdown: + return f"`{num}`" + + return str(num) + + def random_num(ceiling: int = 100) -> int: """Get a random number between 1 and ceiling.""" return _rng.integers(1, ceiling + 1) diff --git a/src/valentina/views/character_sheet.py b/src/valentina/views/character_sheet.py index 72ebcc99..d7cd79ff 100644 --- a/src/valentina/views/character_sheet.py +++ b/src/valentina/views/character_sheet.py @@ -15,7 +15,7 @@ InventoryItemType, TraitCategory, ) -from valentina.models import AWSService, Character, CharacterTrait, InventoryItem, Statistics +from valentina.models import AWSService, Character, CharacterTrait, Statistics from valentina.models.bot import ValentinaContext @@ -142,6 +142,7 @@ async def __embed2( ) -> discord.Embed: """Builds the second embed of a character sheet. This embed contains the character's bio and custom sections.""" custom_sections = character.sheet_sections + items = character.inventory if title is None: title = f"{character.full_name} - Page 2" @@ -157,6 +158,23 @@ async def __embed2( if character.bio: embed.add_field(name="**BIOGRAPHY**", value=character.bio, inline=False) + if items: + embed.add_field(name="\u200b", value="**INVENTORY**", inline=False) + for member in InventoryItemType: + sub_items = [i for i in items if i.type == member.name] # type: ignore [attr-defined] + content = "" + for i in sub_items: + line_begin = "- " + name = f"**{i.name}**" # type: ignore [attr-defined] + desc = f": {i.description}" if i.description else "" # type: ignore [attr-defined] + line_end = "\n" + content += f"{line_begin}{name}{desc}{line_end}" + + if sub_items: + embed.add_field(name=f"__**{member.value}**__", value=content, inline=False) + else: + embed.add_field(name="**EMPTY INVENTORY**", value="No items in inventory", inline=False) + if len(custom_sections) > 0: embed.add_field(name="\u200b", value="**CUSTOM SECTIONS**", inline=False) for section in custom_sections: @@ -175,44 +193,6 @@ async def __embed2( return embed -async def __embed3( - character: Character, - owned_by_user: discord.User | None = None, - title: str | None = None, - show_footer: bool = True, -) -> discord.Embed | None: - """Builds the third embed of a character sheet. This embed contains the character's inventory.""" - items = await InventoryItem.find(InventoryItem.character == str(character.id)).to_list() - if title is None: - title = f"{character.full_name} - INVENTORY - Page 3" - - embed = discord.Embed(title=title, description="", color=EmbedColor.INFO.value) - - if show_footer: - modified = arrow.get(character.date_modified).humanize() - footer = f"Owned by: {owned_by_user.display_name} • " if owned_by_user else "" - footer += f"Last updated: {modified}" - embed.set_footer(text=footer) - - if items: - for member in InventoryItemType: - sub_items = [i for i in items if i.type == member.name] - content = "" - for i in sub_items: - line_begin = "- " - name = f"**{i.name}**" - desc = f": {i.description}" if i.description else "" - line_end = "\n" - content += f"{line_begin}{name}{desc}{line_end}" - - if sub_items: - embed.add_field(name=f"__**{member.value}**__", value=content, inline=False) - else: - embed.add_field(name="**EMPTY INVENTORY**", value="No items in inventory", inline=False) - - return embed - - def __image_embed( character: Character, image_key: str, @@ -253,7 +233,6 @@ async def show_sheet( [ __embed1(character, owned_by_user, show_footer=show_footer), await __embed2(ctx, character, owned_by_user, show_footer=show_footer), - await __embed3(character, owned_by_user, show_footer=show_footer), ] ) diff --git a/src/valentina/views/roll_display.py b/src/valentina/views/roll_display.py index 7f91ba71..5e8f1ae2 100644 --- a/src/valentina/views/roll_display.py +++ b/src/valentina/views/roll_display.py @@ -6,6 +6,7 @@ from valentina.constants import Emoji from valentina.models import CharacterTrait, DiceRoll from valentina.models.bot import ValentinaContext +from valentina.utils.helpers import convert_int_to_emoji p = inflect.engine() @@ -41,9 +42,14 @@ def _add_comment_field(self, embed: discord.Embed) -> discord.Embed: async def get_embed(self) -> discord.Embed: """The graphical representation of the roll.""" - roll_string = " ".join(f"{die}" for die in self.roll.roll) + roll_string = " ".join( + f"{convert_int_to_emoji(die, markdown=True)}" for die in self.roll.roll + ) + if self.desperation_pool > 0: - desperation_roll_string = " ".join(f"{die}" for die in self.roll.desperation_roll) + desperation_roll_string = " ".join( + f"{convert_int_to_emoji(die, markdown=True)}" for die in self.roll.desperation_roll + ) description = f"""\ ### {self.ctx.author.mention} rolled `{self.desperation_pool + self.roll.pool}{self.roll.dice_type.name.lower()}` @@ -51,49 +57,60 @@ async def get_embed(self) -> discord.Embed: {self.roll.embed_description} """ - description += f"""\ -### Rolled Dice: -```scala -Roll : {roll_string} -{"Desperation : " + desperation_roll_string if self.desperation_pool > 0 else ""} -``` -""" + embed = discord.Embed( + title=None, + description=description, + color=self.roll.embed_color, + ) + + # Rolled dice + value = f"{roll_string}" + if self.desperation_pool > 0: + value += f" + {desperation_roll_string}" + + embed.add_field(name="Rolled Dice", value=f"{value}", inline=False) if self.desperation_pool > 0 and self.roll.desperation_botches > 0: - description += f"""\ -### {Emoji.FACEPALM.value} `{self.roll.desperation_botches}` Desperation {p.plural_noun('botch', self.roll.desperation_botches)} + embed.add_field(name="\u200b", value="\u200b", inline=False) # spacer + value = f"""\ > You must pick either: > - {Emoji.DESPAIR.value} **Despair** (Fail your roll) > - {Emoji.OVERREACH.value} **Overreach** (Succeed but raise the danger level by 1) """ - - description += f"""\ -### Roll Details: -```scala -Difficulty : {self.roll.difficulty} -Pool : {self.roll.pool}{self.roll.dice_type.name.lower()} -""" + embed.add_field( + name=f"**{Emoji.FACEPALM.value} {self.roll.desperation_botches} Desperation {p.plural_noun('botch', self.roll.desperation_botches)}**", + value=f"{value}", + inline=False, + ) + + embed.add_field(name="Difficulty", value=f"`{self.roll.difficulty}`", inline=True) + embed.add_field( + name="Dice Pool", + value=f"`{self.roll.pool}{self.roll.dice_type.name.lower()}`", + inline=True, + ) if self.desperation_pool > 0: - description += f"""\ -Desperation Pool : {self.desperation_pool}{self.roll.dice_type.name.lower()} -Total Dice Rolled: {self.desperation_pool + self.roll.pool}{self.roll.dice_type.name.lower()} -""" + embed.add_field( + name="Desperation Pool", + value=f"`{self.desperation_pool}{self.roll.dice_type.name.lower()}`", + inline=True, + ) if self.trait_one: - description += f"{self.trait_one.name:<17}: {self.trait_one.value} {p.plural_noun('die', self.trait_one.value)}\n" + embed.add_field(name="\u200b", value="**TRAITS**", inline=False) + embed.add_field( + name=f"{self.trait_one.name}", + value=f"`{self.trait_one.value} {p.plural_noun('die', self.trait_one.value)}`", + inline=True, + ) if self.trait_two: - description += f"{self.trait_two.name:<17}: {self.trait_two.value} {p.plural_noun('die', self.trait_two.value)}\n" - - description += "```" - - embed = discord.Embed( - title=None, - description=description, - color=self.roll.embed_color, - ) + embed.add_field( + name=f"{self.trait_two.name}", + value=f"`{self.trait_two.value} {p.plural_noun('die', self.trait_two.value)}`", + inline=True, + ) - # Thumbnail embed.set_thumbnail(url=await self.roll.thumbnail_url()) return self._add_comment_field(embed)