Beispiel #1
0
    async def spell(self, ctx, *, name: str):
        """Looks up a spell."""
        choices = await get_spell_choices(ctx, filter_by_license=False)
        spell = await self._lookup_search3(ctx, {'spell': choices}, name)

        embed = EmbedWithAuthor(ctx)
        embed.url = spell.url
        color = embed.colour

        embed.title = spell.name
        school_level = f"{spell.get_level()} {spell.get_school().lower()}" if spell.level > 0 \
            else f"{spell.get_school().lower()} cantrip"
        embed.description = f"*{school_level}. " \
                            f"({', '.join(itertools.chain(spell.classes, spell.subclasses))})*"
        if spell.ritual:
            time = f"{spell.time} (ritual)"
        else:
            time = spell.time

        meta = f"**Casting Time**: {time}\n" \
               f"**Range**: {spell.range}\n" \
               f"**Components**: {spell.components}\n" \
               f"**Duration**: {spell.duration}"
        embed.add_field(name="Meta", value=meta)

        text = spell.description
        higher_levels = spell.higherlevels

        if len(text) > 1020:
            pieces = [text[:1020]] + [
                text[i:i + 2040] for i in range(1020, len(text), 2040)
            ]
        else:
            pieces = [text]

        embed.add_field(name="Description", value=pieces[0], inline=False)

        embed_queue = [embed]
        if len(pieces) > 1:
            for piece in pieces[1:]:
                temp_embed = discord.Embed()
                temp_embed.colour = color
                temp_embed.description = piece
                embed_queue.append(temp_embed)

        if higher_levels:
            add_fields_from_long_text(embed_queue[-1], "At Higher Levels",
                                      higher_levels)

        embed_queue[-1].set_footer(text=f"Spell | {spell.source_str()}")
        if spell.homebrew:
            add_homebrew_footer(embed_queue[-1])

        if spell.image:
            embed_queue[0].set_thumbnail(url=spell.image)

        await Stats.increase_stat(ctx, "spells_looked_up_life")
        destination = await self._get_destination(ctx)
        for embed in embed_queue:
            await destination.send(embed=embed)
Beispiel #2
0
    async def classfeat(self, ctx, *, name: str):
        """Looks up a class feature."""
        guild_settings = await self.get_settings(ctx.guild)
        pm = guild_settings.get("pm_result", False)
        srd = guild_settings.get("srd", False)

        destination = ctx.author if pm else ctx.channel

        result, metadata = await search_and_select(ctx, c.cfeats, name, lambda e: e['name'], srd=srd,
                                                   return_metadata=True)

        metadata['srd'] = srd
        await self.add_training_data("classfeat", name, result['name'], metadata=metadata)

        if not result['srd'] and srd:
            return await self.send_srd_error(ctx, result)

        embed = EmbedWithAuthor(ctx)
        embed.title = result['name']
        desc = result['text']
        desc = [desc[i:i + 1024] for i in range(0, len(desc), 1024)]
        embed.description = ''.join(desc[:2])
        for piece in desc[2:]:
            embed.add_field(name="** **", value=piece)

        await destination.send(embed=embed)
Beispiel #3
0
    async def _view(self, ctx, name):
        collectable = await helpers.get_collectable_named(
            ctx, name, self.personal_cls, self.workshop_cls,
            self.workshop_sub_meth, self.is_alias, self.obj_name,
            self.obj_name_pl, self.name)
        if collectable is None:
            return await ctx.send(f"No {self.obj_name} named {name} found.")
        elif isinstance(collectable, self.personal_cls):  # personal
            await send_long_code_text(
                ctx,
                outside_codeblock=f'**{name}**:',
                inside_codeblock=
                f"{ctx.prefix}{self.obj_copy_command} {collectable.name} {collectable.code}",
                codeblock_language='py')
            return
        else:  # collection
            embed = EmbedWithAuthor(ctx)
            the_collection = await collectable.load_collection(ctx)
            owner = await user_from_id(ctx, the_collection.owner)
            embed.title = f"{ctx.prefix}{name}" if self.is_alias else name
            embed.description = f"From {the_collection.name} by {owner}.\n" \
                                f"[View on Workshop]({the_collection.url})"
            embeds.add_fields_from_long_text(
                embed, "Help", collectable.docs or "No documentation.")

            if isinstance(collectable, workshop.WorkshopAlias):
                await collectable.load_subcommands(ctx)
                if collectable.subcommands:
                    subcommands = "\n".join(f"**{sc.name}** - {sc.short_docs}"
                                            for sc in collectable.subcommands)
                    embed.add_field(name="Subcommands",
                                    value=subcommands,
                                    inline=False)

            return await ctx.send(embed=embed)
Beispiel #4
0
    async def _view(self, ctx, name):
        collectable = await helpers.get_collectable_named(
            ctx, name, self.personal_cls, self.workshop_cls,
            self.workshop_sub_meth, self.is_alias, self.obj_name,
            self.obj_name_pl, self.name)
        if collectable is None:
            return await ctx.send(f"No {self.obj_name} named {name} found.")
        elif isinstance(collectable, self.personal_cls):  # personal
            out = f'**{name}**: ```py\n{ctx.prefix}{self.obj_copy_command} {collectable.name} {collectable.code}\n```'
            out = out if len(
                out
            ) <= 2000 else f'**{collectable.name}**:\nCommand output too long to display.'
            return await ctx.send(out)
        else:  # collection
            embed = EmbedWithAuthor(ctx)
            the_collection = await collectable.load_collection(ctx)
            owner = await user_from_id(ctx, the_collection.owner)
            embed.title = f"{ctx.prefix}{name}" if self.is_alias else name
            embed.description = f"From {the_collection.name} by {owner}.\n" \
                                f"[View on Workshop]({the_collection.url})"
            embed.add_field(name="Help",
                            value=collectable.docs or "No documentation.",
                            inline=False)

            if isinstance(collectable, workshop.WorkshopAlias):
                await collectable.load_subcommands(ctx)
                if collectable.subcommands:
                    subcommands = "\n".join(f"**{sc.name}** - {sc.short_docs}"
                                            for sc in collectable.subcommands)
                    embed.add_field(name="Subcommands",
                                    value=subcommands,
                                    inline=False)

            return await ctx.send(embed=embed)
Beispiel #5
0
    async def item_lookup(self, ctx, *, name):
        """Looks up an item."""
        choices = await get_item_choices(ctx, filter_by_license=False)
        item = await self._lookup_search3(ctx, {'magic-item': choices},
                                          name,
                                          query_type='item')

        embed = EmbedWithAuthor(ctx)

        embed.title = item.name
        embed.url = item.url
        embed.description = item.meta

        if item.attunement:
            if item.attunement is True:  # can be truthy, but not true
                embed.add_field(name="Attunement",
                                value=f"Requires Attunement")
            else:
                embed.add_field(name="Attunement",
                                value=f"Requires Attunement {item.attunement}",
                                inline=False)

        text = trim_str(item.desc, 5500)
        add_fields_from_long_text(embed, "Description", text)

        if item.image:
            embed.set_thumbnail(url=item.image)

        handle_source_footer(embed, item, "Item")

        await Stats.increase_stat(ctx, "items_looked_up_life")
        await (await self._get_destination(ctx)).send(embed=embed)
Beispiel #6
0
    async def subscribe(self, ctx, url):
        coll_match = re.match(WORKSHOP_ADDRESS_RE, url)
        if coll_match is None:
            return await ctx.send("This is not an Alias Workshop link.")

        if self.before_edit_check:
            await self.before_edit_check(ctx)

        collection_id = coll_match.group(1)
        the_collection = await workshop.WorkshopCollection.from_id(
            ctx, collection_id)
        # private and duplicate logic handled here, also loads aliases/snippets
        if self.is_server:
            await the_collection.set_server_active(ctx)
        else:
            await the_collection.subscribe(ctx)

        embed = EmbedWithAuthor(ctx)
        embed.title = f"Subscribed to {the_collection.name}"
        embed.url = the_collection.url
        embed.description = the_collection.description
        if the_collection.aliases:
            embed.add_field(
                name="Server Aliases" if self.is_server else "Aliases",
                value=", ".join(sorted(a.name
                                       for a in the_collection.aliases)))
        if the_collection.snippets:
            embed.add_field(
                name="Server Snippets" if self.is_server else "Snippets",
                value=", ".join(sorted(a.name
                                       for a in the_collection.snippets)))
        await ctx.send(embed=embed)
Beispiel #7
0
    async def background(self, ctx, *, name: str):
        """Looks up a background."""
        guild_settings = await self.get_settings(ctx.message.server)
        pm = guild_settings.get("pm_result", False)
        srd = guild_settings.get("srd", False)

        result = await search_and_select(ctx,
                                         c.backgrounds,
                                         name,
                                         lambda e: e['name'],
                                         srd=srd)

        if not result['srd'] and srd:
            return await self.send_srd_error(ctx, result)

        embed = EmbedWithAuthor(ctx)
        embed.title = result['name']
        embed.description = f"*Source: {result.get('source', 'Unknown')}*"

        ignored_fields = [
            'suggested characteristics', 'personality trait', 'ideal', 'bond',
            'flaw', 'specialty', 'harrowing event'
        ]
        for trait in result['trait']:
            if trait['name'].lower() in ignored_fields: continue
            text = '\n'.join(t for t in trait['text'] if t)
            text = textwrap.shorten(text, width=1020, placeholder="...")
            embed.add_field(name=trait['name'], value=text)

        # do stuff here
        if pm:
            await self.bot.send_message(ctx.message.author, embed=embed)
        else:
            await self.bot.say(embed=embed)
Beispiel #8
0
    async def race(self, ctx, *, name: str):
        """Looks up a race."""
        guild_settings = await self.get_settings(ctx.guild)
        pm = guild_settings.get("pm_result", False)
        srd = guild_settings.get("srd", False)

        destination = ctx.author if pm else ctx.channel

        result, metadata = await search_and_select(ctx, c.fancyraces, name, lambda e: e.name,
                                                   srd=srd and (lambda e: e.srd), return_metadata=True)

        metadata['srd'] = srd
        await self.add_training_data("race", name, result.name, metadata=metadata)

        if not result.srd and srd:
            return await self.send_srd_error(ctx, result)

        embed = EmbedWithAuthor(ctx)
        embed.title = result.name
        embed.description = f"Source: {result.source}"
        embed.add_field(name="Speed", value=result.get_speed_str())
        embed.add_field(name="Size", value=result.size)
        if result.ability:
            embed.add_field(name="Ability Bonuses", value=result.get_asi_str())
        for t in result.get_traits():
            f_text = t['text']
            f_text = [f_text[i:i + 1024] for i in range(0, len(f_text), 1024)]
            embed.add_field(name=t['name'], value=f_text[0])
            for piece in f_text[1:]:
                embed.add_field(name="** **", value=piece)

        await destination.send(embed=embed)
Beispiel #9
0
    async def subclass(self, ctx, name: str):
        """Looks up a subclass."""
        guild_settings = await self.get_settings(ctx.message.server)
        pm = guild_settings.get("pm_result", False)
        srd = guild_settings.get("srd", False)

        destination = ctx.message.author if pm else ctx.message.channel

        result = await search_and_select(ctx,
                                         c.subclasses,
                                         name,
                                         lambda e: e['name'],
                                         srd=srd)

        if not result.get('srd') and srd:
            return await self.send_srd_error(ctx, result)

        embed = EmbedWithAuthor(ctx)
        embed.title = result['name']

        for level_features in result['subclassFeatures']:
            for feature in level_features:
                for entry in feature['entries']:
                    if not isinstance(entry, dict): continue
                    if not entry.get('type') == 'entries': continue
                    text = parse_data_entry(entry['entries'])
                    embed.add_field(
                        name=entry['name'],
                        value=(text[:1019] +
                               "...") if len(text) > 1023 else text)

        embed.set_footer(
            text="Use !classfeat to look up a feature if it is cut off.")

        await self.bot.send_message(destination, embed=embed)
Beispiel #10
0
    async def list(self, ctx):
        embed = EmbedWithAuthor(ctx)

        has_at_least_1 = False

        user_objs = await self.personal_cls.get_ctx_map(ctx)
        user_obj_names = list(user_objs.keys())
        if user_obj_names:
            has_at_least_1 = True
            embeds.add_fields_from_long_text(
                embed, f"Your {self.obj_name_pl.title()}",
                ', '.join(sorted(user_obj_names)))

        async for subscription_doc in self.workshop_sub_meth(ctx):
            try:
                the_collection = await workshop.WorkshopCollection.from_id(
                    ctx, subscription_doc['object_id'])
            except workshop.CollectionNotFound:
                continue
            if bindings := subscription_doc[self.binding_key]:
                has_at_least_1 = True
                embed.add_field(name=the_collection.name,
                                value=', '.join(
                                    sorted(ab['name'] for ab in bindings)),
                                inline=False)
    async def rule(self, ctx, *, name: str):
        """Looks up a rule."""
        guild_settings = await self.get_settings(ctx.guild)
        pm = guild_settings.get("pm_result", False)

        destination = ctx.author if pm else ctx.channel

        result, metadata = await search_and_select(ctx,
                                                   c.rules,
                                                   name,
                                                   lambda e: e['name'],
                                                   return_metadata=True)

        await self.add_training_data("rule",
                                     name,
                                     result['name'],
                                     metadata=metadata)

        embed = EmbedWithAuthor(ctx)
        embed.title = result['name']
        desc = result['desc']
        desc = [desc[i:i + 1024] for i in range(0, len(desc), 1024)]
        embed.description = ''.join(desc[:2])
        for piece in desc[2:]:
            embed.add_field(name="** **", value=piece)

        await destination.send(embed=embed)
Beispiel #12
0
    async def subclass(self, ctx, name: str):
        """Looks up a subclass."""
        guild_settings = await self.get_settings(ctx.guild)
        pm = guild_settings.get("pm_result", False)
        srd = guild_settings.get("srd", False)

        destination = ctx.author if pm else ctx.channel

        result, metadata = await search_and_select(ctx, c.subclasses, name, lambda e: e['name'], srd=srd,
                                                   return_metadata=True)

        metadata['srd'] = srd
        await self.add_training_data("subclass", name, result['name'], metadata=metadata)

        if not result.get('srd') and srd:
            return await self.send_srd_error(ctx, result)

        embed = EmbedWithAuthor(ctx)
        embed.title = result['name']
        embed.description = f"*Source: {result['source']}*"

        for level_features in result['subclassFeatures']:
            for feature in level_features:
                for entry in feature['entries']:
                    if not isinstance(entry, dict): continue
                    if not entry.get('type') == 'entries': continue
                    text = parse_data_entry(entry['entries'])
                    embed.add_field(name=entry['name'], value=(text[:1019] + "...") if len(text) > 1023 else text)

        embed.set_footer(text=f"Use {ctx.prefix}classfeat to look up a feature if it is cut off.")

        await destination.send(embed=embed)
Beispiel #13
0
    async def background(self, ctx, *, name: str):
        """Looks up a background."""
        guild_settings = await self.get_settings(ctx.guild)
        pm = guild_settings.get("pm_result", False)
        srd = guild_settings.get("srd", False)

        result, metadata = await search_and_select(ctx, c.backgrounds, name, lambda e: e.name,
                                                   srd=srd and (lambda e: e.srd), return_metadata=True)

        metadata['srd'] = srd
        await self.add_training_data("background", name, result.name, metadata=metadata)

        if not result.srd and srd:
            return await self.send_srd_error(ctx, result)

        embed = EmbedWithAuthor(ctx)
        embed.title = result.name
        embed.set_footer(text=f"Background | {result.source} {result.page}")

        ignored_fields = ['suggested characteristics', 'personality trait', 'ideal', 'bond', 'flaw', 'specialty',
                          'harrowing event']
        for trait in result.traits:
            if trait['name'].lower() in ignored_fields: continue
            text = trait['text']
            text = textwrap.shorten(text, width=1020, placeholder="...")
            embed.add_field(name=trait['name'], value=text)

        # do stuff here
        if pm:
            await ctx.author.send(embed=embed)
        else:
            await ctx.send(embed=embed)
Beispiel #14
0
    async def race(self, ctx, *, name: str):
        """Looks up a race."""
        try:
            guild_id = ctx.message.server.id
            pm = self.settings.get(guild_id, {}).get("pm_result", False)
            srd = self.settings.get(guild_id, {}).get("srd", False)
        except:
            pm = False
            srd = False
        destination = ctx.message.author if pm else ctx.message.channel

        result = await search_and_select(ctx,
                                         c.fancyraces,
                                         name,
                                         lambda e: e.name,
                                         srd=srd and (lambda e: e.srd))

        if not result.srd and srd:
            return await self.send_srd_error(ctx, result)

        embed = EmbedWithAuthor(ctx)
        embed.title = result.name
        embed.description = f"Source: {result.source}"
        embed.add_field(name="Speed", value=result.get_speed_str())
        embed.add_field(name="Size", value=result.size)
        if result.ability:
            embed.add_field(name="Ability Bonuses", value=result.get_asi_str())
        for t in result.get_traits():
            f_text = t['text']
            f_text = [f_text[i:i + 1024] for i in range(0, len(f_text), 1024)]
            embed.add_field(name=t['name'], value=f_text[0])
            for piece in f_text[1:]:
                embed.add_field(name="** **", value=piece)

        await self.bot.send_message(destination, embed=embed)
Beispiel #15
0
    async def classfeat(self, ctx, *, name: str):
        """Looks up a class feature."""
        try:
            guild_id = ctx.message.server.id
            pm = self.settings.get(guild_id, {}).get("pm_result", False)
            srd = self.settings.get(guild_id, {}).get("srd", False)
        except:
            pm = False
            srd = False
        destination = ctx.message.author if pm else ctx.message.channel

        result = await search_and_select(ctx,
                                         c.cfeats,
                                         name,
                                         lambda e: e['name'],
                                         srd=srd)

        if not result['srd'] and srd:
            return await self.send_srd_error(ctx, result)

        embed = EmbedWithAuthor(ctx)
        embed.title = result['name']
        desc = result['text']
        desc = [desc[i:i + 1024] for i in range(0, len(desc), 1024)]
        embed.description = ''.join(desc[:2])
        for piece in desc[2:]:
            embed.add_field(name="** **", value=piece)

        await self.bot.send_message(destination, embed=embed)
Beispiel #16
0
    async def _class(self, ctx, name: str, level: int = None):
        """Looks up a class, or all features of a certain level."""
        if level is not None and not 0 < level < 21:
            return await ctx.send("Invalid level.")

        result: gamedata.Class = await self._lookup_search3(
            ctx, {'class': compendium.classes}, name)

        embed = EmbedWithAuthor(ctx)
        embed.url = result.url
        if level is None:
            embed.title = result.name
            embed.add_field(name="Hit Points", value=result.hit_points)

            levels = []
            for level in range(1, 21):
                level = result.levels[level - 1]
                levels.append(', '.join([feature.name for feature in level]))

            embed.add_field(name="Starting Proficiencies",
                            value=result.proficiencies,
                            inline=False)
            embed.add_field(name="Starting Equipment",
                            value=result.equipment,
                            inline=False)

            level_features_str = ""
            for i, l in enumerate(levels):
                level_features_str += f"`{i + 1}` {l}\n"
            embed.description = level_features_str

            handle_source_footer(
                embed,
                result,
                f"Use {ctx.prefix}classfeat to look up a feature.",
                add_source_str=False)
        else:
            embed.title = f"{result.name}, Level {level}"

            level_features = result.levels[level - 1]

            for resource, value in zip(result.table.headers,
                                       result.table.levels[level - 1]):
                if value != '0':
                    embed.add_field(name=resource, value=value)

            for f in level_features:
                embed.add_field(name=f.name,
                                value=trim_str(f.text, 1024),
                                inline=False)

            handle_source_footer(
                embed,
                result,
                f"Use {ctx.prefix}classfeat to look up a feature if it is cut off.",
                add_source_str=False)

        await (await self._get_destination(ctx)).send(embed=embed)
Beispiel #17
0
    async def effect(self, ctx, name: str, effect_name: str, *args):
        """Attaches a status effect to a combatant.
        [args] is a set of args that affects a combatant in combat.
        __**Valid Arguments**__
        -dur [duration] - sets the duration of the effect, in rounds
        conc - makes effect require conc
        end - makes effect tick on end of turn
        -t [target] - specifies more combatants to add this effect to
        __Attacks__
        -b [bonus] (see !a)
        -d [damage bonus] (see !a)
        -attack "[hit]|[damage]|[description]" - Adds an attack to the combatant
        __Resists__
        -resist [resist] - gives the combatant resistance
        -immune [immune] - gives the combatant immunity
        -vuln [vulnability] - gives the combatant vulnerability
        -neutral [neutral] - removes immune/resist/vuln
        __General__
        -ac [ac] - modifies ac temporarily; adds if starts with +/- or sets otherwise
        -sb [save bonus] - Adds a bonus to saving throws"""
        combat = await Combat.from_ctx(ctx)
        args = argparse(args)

        targets = []
        first_target = await combat.select_combatant(name)
        if first_target is None:
            await ctx.send("Combatant not found.")
            return
        targets.append(first_target)

        for i, t in enumerate(args.get('t')):
            target = await combat.select_combatant(t, f"Select target #{i + 1}.", select_group=True)
            if isinstance(target, CombatantGroup):
                targets.extend(target.get_combatants())
            else:
                targets.append(target)

        duration = args.last('dur', -1, int)
        conc = args.last('conc', False, bool)
        end = args.last('end', False, bool)

        embed = EmbedWithAuthor(ctx)
        for combatant in targets:
            if effect_name.lower() in (e.name.lower() for e in combatant.get_effects()):
                out = "Effect already exists."
            else:
                effectObj = Effect.new(combat, combatant, duration=duration, name=effect_name, effect_args=args,
                                       concentration=conc, tick_on_end=end)
                result = combatant.add_effect(effectObj)
                out = "Added effect {} to {}.".format(effect_name, combatant.name)
                if result['conc_conflict']:
                    conflicts = [e.name for e in result['conc_conflict']]
                    out += f"\nRemoved {', '.join(conflicts)} due to concentration conflict!"
            embed.add_field(name=combatant.name, value=out)
        await ctx.send(embed=embed, delete_after=10 * len(targets))
        await combat.final()
Beispiel #18
0
 async def tutorial_list(self, ctx):
     """Lists the available tutorials."""
     embed = EmbedWithAuthor(ctx)
     embed.title = "Available Tutorials"
     embed.description = f"Use `{ctx.prefix}tutorial <name>` to select a tutorial from the ones available below!\n" \
                         f"First time here? Try `{ctx.prefix}tutorial quickstart`!"
     for tutorial in self.tutorials.values():
         embed.add_field(name=tutorial.name,
                         value=tutorial.description,
                         inline=False)
     await ctx.send(embed=embed)
Beispiel #19
0
    async def _show_reference_options(ctx, destination):
        embed = EmbedWithAuthor(ctx)
        embed.title = "Rules"
        categories = ', '.join(a['type'] for a in compendium.rule_references)
        embed.description = f"Use `{ctx.prefix}{ctx.invoked_with} <category>` to look at all actions of " \
                            f"a certain type.\nCategories: {categories}"

        for actiontype in compendium.rule_references:
            embed.add_field(name=actiontype['fullName'], value=', '.join(a['name'] for a in actiontype['items']),
                            inline=False)

        await destination.send(embed=embed)
Beispiel #20
0
    async def feat(self, ctx, *, name: str):
        """Looks up a feat."""
        result: gamedata.Feat = await self._lookup_search3(ctx, {'feat': compendium.feats}, name)

        embed = EmbedWithAuthor(ctx)
        embed.title = result.name
        embed.url = result.url
        if result.prerequisite:
            embed.add_field(name="Prerequisite", value=result.prerequisite, inline=False)
        add_fields_from_long_text(embed, "Description", result.desc)
        handle_source_footer(embed, result, "Feat")
        await (await self._get_destination(ctx)).send(embed=embed)
Beispiel #21
0
    async def background(self, ctx, *, name: str):
        """Looks up a background."""
        result: gamedata.Background = await self._lookup_search3(ctx, {'background': compendium.backgrounds}, name)

        embed = EmbedWithAuthor(ctx)
        embed.url = result.url
        embed.title = result.name
        handle_source_footer(embed, result, "Background")

        for trait in result.traits:
            text = trim_str(trait.text, 1024)
            embed.add_field(name=trait.name, value=text, inline=False)

        await (await self._get_destination(ctx)).send(embed=embed)
Beispiel #22
0
    async def race(self, ctx, *, name: str):
        """Looks up a race."""
        result: gamedata.Race = await self._lookup_search3(ctx,
                                                           {'race': compendium.races, 'subrace': compendium.subraces},
                                                           name, 'race')

        embed = EmbedWithAuthor(ctx)
        embed.title = result.name
        embed.url = result.url
        embed.add_field(name="Speed", value=result.speed)
        embed.add_field(name="Size", value=result.size)
        for t in result.traits:
            add_fields_from_long_text(embed, t.name, t.text)
        handle_source_footer(embed, result, "Race")
        await (await self._get_destination(ctx)).send(embed=embed)
Beispiel #23
0
    async def spell(self, ctx, *, name: str):
        """Looks up a spell."""
        spell, metadata = await select_spell_full(ctx, name, return_metadata=True,
                                                  extra_choices=compendium.nspell_names,
                                                  selectkey=self.nsrd_selectkey_obj)
        metadata['homebrew'] = spell.source == 'homebrew'
        await self.add_training_data("spell", name, spell.name, metadata=metadata, srd=spell.srd)
        if not (metadata['homebrew'] or spell.srd):
            return await self._non_srd(ctx, spell, "spell")

        embed = EmbedWithAuthor(ctx)
        color = embed.colour

        embed.title = spell.name
        embed.description = f"*{spell.get_level()} {spell.get_school().lower()}. " \
                            f"({', '.join(itertools.chain(spell.classes, spell.subclasses))})*"
        if spell.ritual:
            time = f"{spell.time} (ritual)"
        else:
            time = spell.time
        embed.add_field(name="Casting Time", value=time)
        embed.add_field(name="Range", value=spell.range)
        embed.add_field(name="Components", value=spell.components)
        embed.add_field(name="Duration", value=spell.duration)

        text = spell.description
        higher_levels = spell.higherlevels

        if len(text) > 1020:
            pieces = [text[:1020]] + [text[i:i + 2040] for i in range(1020, len(text), 2040)]
        else:
            pieces = [text]

        embed.add_field(name="Description", value=pieces[0], inline=False)

        embed_queue = [embed]
        if len(pieces) > 1:
            for piece in pieces[1:]:
                temp_embed = discord.Embed()
                temp_embed.colour = color
                temp_embed.description = piece
                embed_queue.append(temp_embed)

        if higher_levels:
            add_fields_from_long_text(embed_queue[-1], "At Higher Levels", higher_levels)

        if spell.source == 'homebrew':
            embed_queue[-1].set_footer(text="Homebrew content.", icon_url=HOMEBREW_ICON)
        else:
            embed_queue[-1].set_footer(text=f"Spell | {spell.source} {spell.page}")

        if spell.image:
            embed_queue[0].set_thumbnail(url=spell.image)

        destination = await self._get_destination(ctx)
        for embed in embed_queue:
            await destination.send(embed=embed)
Beispiel #24
0
    async def feat(self, ctx, *, name: str):
        """Looks up a feat."""
        choices = compendium.feats + compendium.nfeat_names
        result = await self._lookup_search(ctx, choices, name, lambda e: e['name'], search_type='feat')
        if not result:
            return

        embed = EmbedWithAuthor(ctx)
        embed.title = result['name']
        if result['prerequisite']:
            embed.add_field(name="Prerequisite", value=result['prerequisite'], inline=False)
        if result['ability']:
            embed.add_field(name="Ability Improvement",
                            value=f"Increase your {result['ability']} score by 1, up to a maximum of 20.", inline=False)

        add_fields_from_long_text(embed, "Description", result['desc'])
        embed.set_footer(text=f"Feat | {result['source']} {result['page']}")
        await (await self._get_destination(ctx)).send(embed=embed)
Beispiel #25
0
    async def feat(self, ctx, *, name: str):
        """Looks up a feat."""
        try:
            guild_id = ctx.message.server.id
            pm = self.settings.get(guild_id, {}).get("pm_result", False)
            srd = self.settings.get(guild_id, {}).get("srd", False)
        except:
            pm = False
            srd = False
        destination = ctx.message.author if pm else ctx.message.channel

        result = await search_and_select(ctx, c.feats, name, lambda e: e['name'])

        if not result['name'] == 'Grappler' and srd:  # the only SRD feat.
            return await self.send_srd_error(ctx, result)

        text = parse_data_entry(result['entries'])
        prereq = "None"

        if 'prerequisite' in result:
            for entry in result['prerequisite']:
                if 'race' in entry:
                    prereq = ' or '.join(
                        f"{r['name']}" + (f" ({r['subrace']})" if 'subrace' in r else '') for r in entry['race'])
                if 'ability' in entry:
                    abilities = []
                    for ab in entry['ability']:
                        abilities.extend(f"{ABILITY_MAP.get(a)} {s}" for a, s in ab.items())
                    prereq = ' or '.join(abilities)
                if 'spellcasting' in entry:
                    prereq = "The ability to cast at least one spell"
                if 'proficiency' in entry:
                    prereq = f"Proficiency with {entry['proficiency'][0]['armor']} armor"

        ability = None
        if 'ability' in result:
            if 'choose' in result['ability']:
                ability = ' or '.join(ABILITY_MAP.get(a) for a in result['ability']['choose'][0]['from'])
            else:
                ability = ' or '.join(ABILITY_MAP.get(a) for a in result['ability'].keys())

        embed = EmbedWithAuthor(ctx)
        embed.title = result['name']
        embed.add_field(name="Prerequisite", value=prereq)
        embed.add_field(name="Source", value=result['source'])
        if ability:
            embed.add_field(name="Ability Improvement",
                            value=f"Increase your {ability} score by 1, up to a maximum of 20.")
        _name = 'Description'
        for piece in [text[i:i + 1024] for i in range(0, len(text), 1024)]:
            embed.add_field(name=_name, value=piece)
            _name = '** **'
        await self.bot.send_message(destination, embed=embed)
Beispiel #26
0
    async def subclass(self, ctx, *, name: str):
        """Looks up a subclass."""
        result: gamedata.Subclass = await self._lookup_search3(ctx, {'class': compendium.subclasses}, name,
                                                               query_type='subclass')

        embed = EmbedWithAuthor(ctx)
        embed.url = result.url
        embed.title = result.name
        embed.description = f"*Source: {result.source_str()}*"

        for level in result.levels:
            for feature in level:
                text = trim_str(feature.text, 1024)
                embed.add_field(name=feature.name, value=text, inline=False)

        handle_source_footer(embed, result, f"Use {ctx.prefix}classfeat to look up a feature if it is cut off.",
                             add_source_str=False)

        await (await self._get_destination(ctx)).send(embed=embed)
Beispiel #27
0
    async def rule(self, ctx, *, name: str):
        """Looks up a rule."""
        guild_settings = await self.get_settings(ctx.message.server)
        pm = guild_settings.get("pm_result", False)

        destination = ctx.message.author if pm else ctx.message.channel

        result = await search_and_select(ctx, c.rules, name,
                                         lambda e: e['name'])

        embed = EmbedWithAuthor(ctx)
        embed.title = result['name']
        desc = result['desc']
        desc = [desc[i:i + 1024] for i in range(0, len(desc), 1024)]
        embed.description = ''.join(desc[:2])
        for piece in desc[2:]:
            embed.add_field(name="** **", value=piece)

        await self.bot.send_message(destination, embed=embed)
Beispiel #28
0
    async def spell(self, ctx, *, name: str):
        """Looks up a spell."""
        choices = await get_spell_choices(ctx, filter_by_license=False)
        spell = await self._lookup_search3(ctx, {'spell': choices}, name)

        embed = EmbedWithAuthor(ctx)
        embed.url = spell.url
        color = embed.colour

        embed.title = spell.name
        school_level = f"{spell.get_level()} {spell.get_school().lower()}" if spell.level > 0 \
            else f"{spell.get_school().lower()} cantrip"
        embed.description = f"*{school_level}. " \
                            f"({', '.join(itertools.chain(spell.classes, spell.subclasses))})*"
        if spell.ritual:
            time = f"{spell.time} (ritual)"
        else:
            time = spell.time

        meta = f"**Casting Time**: {time}\n" \
               f"**Range**: {spell.range}\n" \
               f"**Components**: {spell.components}\n" \
               f"**Duration**: {spell.duration}"
        embed.add_field(name="Meta", value=meta)

        higher_levels = spell.higherlevels
        pieces = chunk_text(spell.description)

        embed.add_field(name="Description", value=pieces[0], inline=False)

        embed_queue = [embed]
        if len(pieces) > 1:
            for i, piece in enumerate(pieces[1::2]):
                temp_embed = discord.Embed()
                temp_embed.colour = color
                if (next_idx := (i + 1) * 2) < len(
                        pieces
                ):  # this is chunked into 1024 pieces, and descs can handle 2
                    temp_embed.description = piece + pieces[next_idx]
                else:
                    temp_embed.description = piece
                embed_queue.append(temp_embed)
Beispiel #29
0
    async def background(self, ctx, *, name: str):
        """Looks up a background."""
        choices = compendium.backgrounds + compendium.nbackground_names
        result = await self._lookup_search(ctx, choices, name, lambda e: e.name, search_type='background', is_obj=True)
        if not result:
            return

        embed = EmbedWithAuthor(ctx)
        embed.title = result.name
        embed.set_footer(text=f"Background | {result.source} {result.page}")

        ignored_fields = ['suggested characteristics', 'personality trait', 'ideal', 'bond', 'flaw', 'specialty',
                          'harrowing event']
        for trait in result.traits:
            if trait['name'].lower() in ignored_fields: continue
            text = trait['text']
            text = textwrap.shorten(text, width=1020, placeholder="...")
            embed.add_field(name=trait['name'], value=text, inline=False)

        await (await self._get_destination(ctx)).send(embed=embed)
    async def patron_roscoe(self, ctx):
        embed = EmbedWithAuthor(ctx)
        embed.title = "Roscoe's Feast"
        embed.description = "*6th level conjuration. (Cleric, Druid)*"
        embed.add_field(name="Casting Time", value="10 minutes")
        embed.add_field(name="Range", value="30 feet")
        embed.add_field(name="Components", value="V, S, M (A gem encrusted bowl worth 1000 gold pieces)")
        embed.add_field(name="Duration", value="Instantaneous")
        embed.add_field(
            name="Description",
            value="You call forth the Avatar of Roscoe who brings with him a magnificent feast of chicken and waffles.\n"
                  "The feast takes 1 hour to consume and disappears at the end of that time, and the beneficial effects "
                  "don't set in until this hour is over. Up to twelve creatures can partake of the feast.\n"
                  "A creature that partakes of the feast gains several benefits. "
                  "The creature is cured of all diseases and poison, becomes immune to poison and being frightened, and "
                  "makes all Wisdom saving throws with advantage. Its hit point maximum also increases by 2d10, and it "
                  "gains the same number of hit points. These benefits last for 24 hours.")

        embed.set_footer(text=f"Spell | Thanks Roscoe!")
        await ctx.send(embed=embed)