Beispiel #1
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 #2
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 #3
0
def set_maybe_long_desc(embed, desc):
    """
    Sets a description that might be longer than 2048 characters but is less than 5000 characters.
    :param embed: The embed to add the description (and potentially fields) to.
    :param str desc: The description to add. Will overwrite existing description.
    """
    desc = chunk_text(trim_str(desc, 5000))
    embed.description = ''.join(desc[:2]).strip()
    for piece in desc[2:]:
        embed.add_field(name="** **", value=piece.strip(), inline=False)
Beispiel #4
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 #5
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 #6
0
    async def cast(self, ctx, caster, targets, args, combat=None):
        """
        Casts this spell.

        :param ctx: The context of the casting.
        :param caster: The caster of this spell.
        :type caster: :class:`~cogs5e.models.sheet.statblock.StatBlock`
        :param targets: A list of targets
        :type targets: list of :class:`~cogs5e.models.sheet.statblock.StatBlock`
        :param args: Args
        :type args: :class:`~utils.argparser.ParsedArguments`
        :param combat: The combat the spell was cast in, if applicable.
        :return: {embed: Embed}
        """

        # generic args
        l = args.last('l', self.level, int)
        i = args.last('i', type_=bool)
        title = args.last('title')

        # meta checks
        if not self.level <= l <= 9:
            raise SpellException("Invalid spell level.")

        # caster spell-specific overrides
        dc_override = None
        ab_override = None
        spell_override = None
        spellbook_spell = caster.spellbook.get_spell(self)
        if spellbook_spell is not None:
            dc_override = spellbook_spell.dc
            ab_override = spellbook_spell.sab
            spell_override = spellbook_spell.mod

        if not i:
            # if I'm a warlock, and I didn't have any slots of this level anyway (#655)
            # automatically scale up to the next level s.t. our slots are not 0
            if l > 0 \
                    and l == self.level \
                    and not caster.spellbook.get_max_slots(l) \
                    and not caster.spellbook.can_cast(self, l):
                l = next((sl for sl in range(l, 6)
                          if caster.spellbook.get_max_slots(sl)),
                         l)  # only scale up to l5
                args['l'] = l

            # can I cast this spell?
            if not caster.spellbook.can_cast(self, l):
                embed = EmbedWithAuthor(ctx)
                embed.title = "Cannot cast spell!"
                if not caster.spellbook.get_slots(l):
                    # out of spell slots
                    err = f"You don't have enough level {l} slots left! Use `-l <level>` to cast at a different level, " \
                          f"`{ctx.prefix}g lr` to take a long rest, or `-i` to ignore spell slots!"
                elif self.name not in caster.spellbook:
                    # don't know spell
                    err = f"You don't know this spell! Use `{ctx.prefix}sb add {self.name}` to add it to your spellbook, " \
                          f"or pass `-i` to ignore restrictions."
                else:
                    # ?
                    err = "Not enough spell slots remaining, or spell not in known spell list!\n" \
                          f"Use `{ctx.prefix}game longrest` to restore all spell slots if this is a character, " \
                          f"or pass `-i` to ignore restrictions."
                embed.description = err
                if l > 0:
                    embed.add_field(name="Spell Slots",
                                    value=caster.spellbook.remaining_casts_of(
                                        self, l))
                return {"embed": embed}

            # use resource
            caster.spellbook.cast(self, l)

        # base stat stuff
        mod_arg = args.last("mod", type_=int)
        stat_override = ''
        if mod_arg is not None:
            mod = mod_arg
            prof_bonus = caster.stats.prof_bonus
            dc_override = 8 + mod + prof_bonus
            ab_override = mod + prof_bonus
            spell_override = mod
        elif any(args.last(s, type_=bool) for s in STAT_ABBREVIATIONS):
            base = next(s for s in STAT_ABBREVIATIONS
                        if args.last(s, type_=bool))
            mod = caster.stats.get_mod(base)
            dc_override = 8 + mod + caster.stats.prof_bonus
            ab_override = mod + caster.stats.prof_bonus
            spell_override = mod
            stat_override = f" with {verbose_stat(base)}"

        # begin setup
        embed = discord.Embed()
        if title:
            embed.title = title.replace('[sname]', self.name)
        else:
            embed.title = f"{caster.get_title_name()} casts {self.name}{stat_override}!"
        if targets is None:
            targets = [None]

        # concentration
        noconc = args.last("noconc", type_=bool)
        conc_conflict = None
        conc_effect = None
        if all((self.concentration, isinstance(caster, init.Combatant), combat,
                not noconc)):
            duration = args.last('dur', self.get_combat_duration(), int)
            conc_effect = initiative.Effect.new(combat, caster, self.name,
                                                duration, "", True)
            effect_result = caster.add_effect(conc_effect)
            conc_conflict = effect_result['conc_conflict']

        if self.automation and self.automation.effects:
            title = f"{caster.name} cast {self.name}!"
            await self.automation.run(ctx,
                                      embed,
                                      caster,
                                      targets,
                                      args,
                                      combat,
                                      self,
                                      conc_effect=conc_effect,
                                      ab_override=ab_override,
                                      dc_override=dc_override,
                                      spell_override=spell_override,
                                      title=title)
        else:
            phrase = args.join('phrase', '\n')
            if phrase:
                embed.description = f"*{phrase}*"

            text = trim_str(self.description, 1024)
            embed.add_field(name="Description", value=text, inline=False)
            if l != self.level and self.higherlevels:
                embed.add_field(name="At Higher Levels",
                                value=trim_str(self.higherlevels, 1024),
                                inline=False)
            embed.set_footer(text="No spell automation found.")

        if l > 0 and not i:
            embed.add_field(name="Spell Slots",
                            value=caster.spellbook.remaining_casts_of(self, l))

        if conc_conflict:
            conflicts = ', '.join(e.name for e in conc_conflict)
            embed.add_field(name="Concentration",
                            value=f"Dropped {conflicts} due to concentration.")

        if 'thumb' in args:
            embed.set_thumbnail(url=args.last('thumb'))
        elif self.image:
            embed.set_thumbnail(url=self.image)

        add_fields_from_args(embed, args.get('f'))
        gamedata.lookuputils.handle_source_footer(embed,
                                                  self,
                                                  add_source_str=False)

        return {"embed": embed}