예제 #1
0
    async def _dice_roll_embed_common(self, gctx, roll_request, title_fmt: str,
                                      **fmt_kwargs):
        """
        Common method to display a character embed based on some check/save result.

        Note: {name} will be formatted with the character's name in title_fmt.
        """
        # check for loaded character
        character = await gctx.get_character()
        if character is None:
            await self.dice_roll_roll(
                gctx,
                roll_request,
                comment_getter=lambda rr:
                f"{roll_request.action}: {rr.roll_type.value.title()}")
            return

        # only listen to the first roll
        the_roll = roll_request.rolls[0]

        # send embed
        embed = embeds.EmbedWithCharacter(character, name=False)
        embed.title = title_fmt.format(name=character.get_title_name(),
                                       **fmt_kwargs)
        embed.description = str(the_roll.to_d20())
        embed.set_footer(text=f"Rolled in {gctx.campaign.campaign_name}",
                         icon_url=constants.DDB_LOGO_ICON)
        await gctx.channel.send(embed=embed)
예제 #2
0
    async def save(self, ctx, skill, *args):
        if skill == 'death':
            ds_cmd = self.bot.get_command('game deathsave')
            if ds_cmd is None:
                return await ctx.send("Error: GameTrack cog not loaded.")
            return await ctx.invoke(ds_cmd, *args)

        char: Character = await Character.from_ctx(ctx)

        args = await self.new_arg_stuff(args, ctx, char)

        hide = args.last('h', type_=bool)

        embed = embeds.EmbedWithCharacter(char, name=False, image=not hide)

        checkutils.update_csetting_args(char, args)
        caster = await targetutils.maybe_combat_caster(ctx, char)

        result = checkutils.run_save(skill, caster, args, embed)

        # send
        await ctx.send(embed=embed)
        await try_delete(ctx.message)
        if gamelog := self.bot.get_cog('GameLog'):
            await gamelog.send_save(ctx, char, result.skill_name, result.rolls)
예제 #3
0
    async def playertoken(self, ctx, *args):
        """
        Generates and sends a token for use on VTTs.
        __Valid Arguments__
        -border <gold|plain|none> - Chooses the token border.
        """

        char: Character = await Character.from_ctx(ctx)
        if not char.image:
            return await ctx.send("This character has no image.")

        token_args = argparse(args)
        ddb_user = await self.bot.ddb.get_ddb_user(ctx, ctx.author.id)
        is_subscriber = ddb_user and ddb_user.is_subscriber

        try:
            processed = await img.generate_token(char.image, is_subscriber, token_args)
        except Exception as e:
            return await ctx.send(f"Error generating token: {e}")

        file = discord.File(processed, filename="image.png")
        embed = embeds.EmbedWithCharacter(char, image=False)
        embed.set_image(url="attachment://image.png")
        await ctx.send(file=file, embed=embed)
        processed.close()
예제 #4
0
파일: sheetManager.py 프로젝트: avrae/avrae
    async def action(self, ctx, atk_name=None, *, args: str = ''):
        if atk_name is None:
            return await self.action_list(ctx)

        char: Character = await Character.from_ctx(ctx)
        args = await self.new_arg_stuff(args, ctx, char)
        hide = args.last('h', type_=bool)
        embed = embeds.EmbedWithCharacter(char, name=False, image=not hide)

        caster, targets, combat = await targetutils.maybe_combat(
            ctx, char, args)
        # we select from caster attacks b/c a combat effect could add some
        attack_or_action = await actionutils.select_action(
            ctx, atk_name, attacks=caster.attacks, actions=char.actions)

        if isinstance(attack_or_action, Attack):
            result = await actionutils.run_attack(ctx, embed, args, caster,
                                                  attack_or_action, targets,
                                                  combat)
        else:
            result = await actionutils.run_action(ctx, embed, args, caster,
                                                  attack_or_action, targets,
                                                  combat)

        await ctx.send(embed=embed)
        await try_delete(ctx.message)
        if (gamelog := self.bot.get_cog('GameLog')) and result is not None:
            await gamelog.send_automation(ctx, char, attack_or_action.name,
                                          result)
예제 #5
0
파일: charsettings.py 프로젝트: avrae/avrae
 async def get_content(self):
     outbound, inbound = await self.can_do_character_sync()
     embed = embeds.EmbedWithCharacter(
         self.character,
         title=f"Character Settings ({self.character.name}) / Sync Settings"
     )
     if outbound:
         embed.add_field(
             name="Outbound Sync",
             value=f"**{self.settings.sync_outbound}**\n"
             f"*If this is enabled, updates to your character's HP, spell slots, custom counters, and more "
             f"will be sent to your sheet provider live.*",
             inline=False)
     if inbound:
         embed.add_field(
             name="Inbound Sync",
             value=f"**{self.settings.sync_inbound}**\n"
             f"*If this is enabled, if you change your character's HP, spell slots, custom counters, or more "
             f"on your sheet provider, they will be updated here as well.*",
             inline=False)
     if not (outbound or inbound):
         embed.description = (
             "Character sync is not supported by your sheet provider (and I have no idea how you got to this menu). "
             "Press the Back button to go back, and come tell us how you got here on the [Development Discord]"
             "(https://support.avrae.io).")
     return {"embed": embed}
예제 #6
0
파일: charsettings.py 프로젝트: avrae/avrae
    async def get_content(self):
        embed = embeds.EmbedWithCharacter(
            self.character,
            title=f"Character Settings for {self.character.name}")
        embed.add_field(
            name="Cosmetic Settings",
            value=f"**Embed Color**: {color_setting_desc(self.settings.color)}\n"
            f"**Show Character Image**: {self.settings.embed_image}",
            inline=False)
        embed.add_field(
            name="Gameplay Settings",
            value=f"**Crit Range**: {crit_range_desc(self.settings.crit_on)}\n"
            f"**Extra Crit Dice**: {self.settings.extra_crit_dice}\n"
            f"**Reroll**: {self.settings.reroll}\n"
            f"**Ignore Crits**: {self.settings.ignore_crit}\n"
            f"**Reliable Talent**: {self.settings.talent}\n"
            f"**Reset All Spell Slots on Short Rest**: {self.settings.srslots}",
            inline=False)

        outbound, inbound = await self.can_do_character_sync()
        if inbound or outbound:
            sync_desc_lines = []
            if outbound:
                sync_desc_lines.append(
                    f"**Outbound Sync**: {self.settings.sync_outbound}")
            if inbound:
                sync_desc_lines.append(
                    f"**Inbound Sync**: {self.settings.sync_inbound}")
            embed.add_field(name="Character Sync Settings",
                            value='\n'.join(sync_desc_lines),
                            inline=False)
        return {"embed": embed}
예제 #7
0
    async def _active_character_embed(ctx):
        """Creates an embed to be displayed when the active character is checked"""
        active_character: Character = await ctx.get_character()
        embed = embeds.EmbedWithCharacter(active_character)

        desc = (f"Your current active character is {active_character.name}. "
                f"All of your checks, saves and actions will use this character's stats.")
        if (link := active_character.get_sheet_url()) is not None:
            desc = f"{desc}\n[Go to Character Sheet]({link})"
예제 #8
0
파일: sheetManager.py 프로젝트: avrae/avrae
async def send_ddb_ctas(ctx, character):
    """Sends relevant CTAs after a DDB character is imported. Only show a CTA 1/24h to not spam people."""
    ddb_user = await ctx.bot.ddb.get_ddb_user(ctx, ctx.author.id)
    if ddb_user is not None:
        ld_dict = ddb_user.to_ld_dict()
    else:
        ld_dict = {"key": str(ctx.author.id), "anonymous": True}
    gamelog_flag = await ctx.bot.ldclient.variation('cog.gamelog.cta.enabled',
                                                    ld_dict, False)

    # get server settings for whether to pull up campaign settings
    if ctx.guild is not None:
        guild_settings = await ctx.get_server_settings()
        show_campaign_cta = guild_settings.show_campaign_cta
    else:
        show_campaign_cta = False

    # has the user seen this cta within the last 7d?
    if await ctx.bot.rdb.get(f"cog.sheetmanager.cta.seen.{ctx.author.id}"):
        return

    embed = embeds.EmbedWithCharacter(character)
    embed.title = "Heads up!"
    embed.description = "There's a couple of things you can do to make your experience even better!"
    embed.set_footer(text="You won't see this message again this week.")

    # link ddb user
    if ddb_user is None:
        embed.add_field(
            name="Connect Your D&D Beyond Account",
            value=
            "Visit your [Account Settings](https://www.dndbeyond.com/account) page in D&D Beyond to link your "
            "D&D Beyond and Discord accounts. This lets you use all your D&D Beyond content in Avrae for free!",
            inline=False)
    # game log
    if character.ddb_campaign_id and gamelog_flag and show_campaign_cta:
        try:
            await CampaignLink.from_id(ctx.bot.mdb, character.ddb_campaign_id)
        except NoCampaignLink:
            embed.add_field(
                name="Link Your D&D Beyond Campaign",
                value=
                f"Sync rolls between a Discord channel and your D&D Beyond character sheet by linking your "
                f"campaign! Use `{ctx.prefix}campaign https://www.dndbeyond.com/campaigns/"
                f"{character.ddb_campaign_id}` in the Discord channel you want to link it to.\n"
                f"This message can be disabled in `{ctx.prefix}server_settings`.",
                inline=False)

    if not embed.fields:
        return
    await ctx.send(embed=embed)
    await ctx.bot.rdb.setex(f"cog.sheetmanager.cta.seen.{ctx.author.id}",
                            str(time.time()), 60 * 60 * 24 * 7)
예제 #9
0
    async def send_sync_result(self, gctx: GameLogEventContext,
                               char: Character, hp_result: SyncHPResult,
                               death_save_result: SyncDeathSavesResult):
        embed = embeds.EmbedWithCharacter(char)

        # --- hp ---
        if hp_result.changed:
            embed.add_field(name="Hit Points", value=hp_result.message)

        # --- death saves ---
        if death_save_result.changed:
            embed.add_field(name="Death Saves", value=str(char.death_saves))

        embed.set_footer(text=f"Updated in {gctx.campaign.campaign_name}",
                         icon_url=constants.DDB_LOGO_ICON)
        await gctx.send(embed=embed)
예제 #10
0
파일: charsettings.py 프로젝트: avrae/avrae
 async def get_content(self):
     embed = embeds.EmbedWithCharacter(
         self.character,
         title=
         f"Character Settings ({self.character.name}) / Gameplay Settings")
     embed.add_field(
         name="Crit Range",
         value=f"**{crit_range_desc(self.settings.crit_on)}**\n"
         f"*If an attack roll's natural roll (the value on the d20 before modifiers) lands in this range, "
         f"the attack will be counted as a crit.*",
         inline=False)
     embed.add_field(
         name="Extra Crit Dice",
         value=f"**{self.settings.extra_crit_dice}**\n"
         f"*How many additional dice to add to a weapon's damage dice on a crit (in addition to doubling the "
         f"dice).*",
         inline=False)
     embed.add_field(
         name="Reroll",
         value=f"**{self.settings.reroll}**\n"
         f"*If an attack, save, or ability check's natural roll lands on this number, the die will be "
         f"rerolled up to once.*",
         inline=False)
     embed.add_field(
         name="Ignore Crits",
         value=f"**{self.settings.ignore_crit}**\n"
         f"*If this is enabled, any attack against your character will not have its damage dice doubled on a "
         f"critical hit.*",
         inline=False)
     embed.add_field(
         name="Reliable Talent",
         value=f"**{self.settings.talent}**\n"
         f"*If this is enabled, any d20 roll on an ability check that lets you add your proficiency bonus "
         f"will be treated as a 10 if it rolls 9 or lower.*",
         inline=False)
     sr_slot_note = ""
     if self.character.spellbook.max_pact_slots is not None:
         sr_slot_note = " Note that your pact slots will reset on a short rest even if this setting is disabled."
     embed.add_field(
         name="Reset All Spell Slots on Short Rest",
         value=f"**{self.settings.srslots}**\n"
         f"*If this is enabled, all of your spell slots (including non-pact slots) will reset on a short "
         f"rest.{sr_slot_note}*",
         inline=False)
     return {"embed": embed}
예제 #11
0
    async def action_list(self, ctx, *args):
        """
        Lists the active character's actions.
        __Valid Arguments__
        -v - Verbose: Displays each action's character sheet description rather than the effect summary.
        attack - Only displays the available attacks.
        action - Only displays the available actions.
        bonus - Only displays the available bonus actions.
        reaction - Only displays the available reactions.
        other - Only displays the available actions that have another activation time.
        """
        char: Character = await Character.from_ctx(ctx)
        caster = await targetutils.maybe_combat_caster(ctx, char)
        embed = embeds.EmbedWithCharacter(char, name=False)
        embed.title = f"{char.name}'s Actions"

        await actionutils.send_action_list(
            ctx, caster=caster, attacks=caster.attacks, actions=char.actions, embed=embed, args=args)
예제 #12
0
파일: charsettings.py 프로젝트: avrae/avrae
 async def get_content(self):
     embed = embeds.EmbedWithCharacter(
         self.character,
         title=
         f"Character Settings ({self.character.name}) / Cosmetic Settings")
     embed.add_field(
         name="Embed Color",
         value=f"**{color_setting_desc(self.settings.color)}**\n"
         f"*This color will appear on the left side of your character's check, save, actions, and some "
         f"other embeds (like this one!).*",
         inline=False)
     embed.add_field(
         name="Show Character Image",
         value=f"**{self.settings.embed_image}**\n"
         f"*If this is disabled, your character's portrait will not appear on the right side of their "
         f"checks, saves, actions, and some other embeds.*",
         inline=False)
     return {"embed": embed}
예제 #13
0
    async def desc(self, ctx):
        """Prints or edits a description of your currently active character."""
        char: Character = await Character.from_ctx(ctx)

        desc = char.description
        if not desc:
            desc = 'No description available.'

        if len(desc) > 2048:
            desc = desc[:2044] + '...'
        elif len(desc) < 2:
            desc = 'No description available.'

        embed = embeds.EmbedWithCharacter(char, name=False)
        embed.title = char.name
        embed.description = desc

        await ctx.send(embed=embed)
        await try_delete(ctx.message)
예제 #14
0
    async def check(self, ctx, check, *args):
        char: Character = await Character.from_ctx(ctx)
        skill_key = await search_and_select(ctx, SKILL_NAMES, check, lambda s: s)
        args = await self.new_arg_stuff(args, ctx, char)

        hide = args.last('h', type_=bool)

        embed = embeds.EmbedWithCharacter(char, name=False, image=not hide)
        skill = char.skills[skill_key]

        checkutils.update_csetting_args(char, args, skill)
        caster = await targetutils.maybe_combat_caster(ctx, char)

        result = checkutils.run_check(skill_key, caster, args, embed)

        await ctx.send(embed=embed)
        await try_delete(ctx.message)
        if gamelog := self.bot.get_cog('GameLog'):
            await gamelog.send_check(ctx, char, result.skill_name, result.rolls)