コード例 #1
0
    async def item_lookup(self, ctx, *, name):
        """Looks up an item."""
        try:
            pack = await Pack.from_ctx(ctx)
            custom_items = pack.get_search_formatted_items()
            pack_id = pack.id
        except NoActiveBrew:
            custom_items = []
            pack_id = None
        choices = list(itertools.chain(compendium.items, custom_items))
        if ctx.guild:
            async for servpack in Pack.server_active(ctx):
                if servpack.id != pack_id:
                    choices.extend(servpack.get_search_formatted_items())

        # #881 - display nSRD names
        choices.extend(compendium.nitem_names)
        result, metadata = await search_and_select(ctx, choices, name, lambda e: e['name'],
                                                   selectkey=self.nsrd_selectkey, return_metadata=True)
        metadata['homebrew'] = result.get('source') == 'homebrew'
        await self.add_training_data("item", name, result['name'], metadata=metadata, srd=result['srd'])
        if not (metadata['homebrew'] or result['srd']):
            return await self._non_srd(ctx, result, "item")

        embed = EmbedWithAuthor(ctx)
        item = result

        name = item['name']
        proptext = ""

        if not item.get('source') == 'homebrew':
            damage = ''
            extras = ''
            properties = []

            if 'type' in item:
                type_ = ', '.join(
                    i for i in ([ITEM_TYPES.get(t, 'n/a') for t in item['type'].split(',')] +
                                ["Wondrous Item" if item.get('wondrous') else ''])
                    if i)
                for iType in item['type'].split(','):
                    if iType in ('M', 'R', 'GUN'):
                        damage = f"{item.get('dmg1', 'n/a')} {DMGTYPES.get(item.get('dmgType'), 'n/a')}" \
                            if 'dmg1' in item and 'dmgType' in item else ''
                        type_ += f', {item.get("weaponCategory")}'
                    if iType == 'S': damage = f"AC +{item.get('ac', 'n/a')}"
                    if iType == 'LA': damage = f"AC {item.get('ac', 'n/a')} + DEX"
                    if iType == 'MA': damage = f"AC {item.get('ac', 'n/a')} + DEX (Max 2)"
                    if iType == 'HA': damage = f"AC {item.get('ac', 'n/a')}"
                    if iType == 'SHP':  # ships
                        for p in ("CREW", "PASS", "CARGO", "DMGT", "SHPREP"):
                            a = PROPS.get(p, 'n/a')
                            proptext += f"**{a.title()}**: {compendium.itemprops[p]}\n"
                        extras = f"Speed: {item.get('speed')}\nCarrying Capacity: {item.get('carryingcapacity')}\n" \
                                 f"Crew {item.get('crew')}, AC {item.get('vehAc')}, HP {item.get('vehHp')}"
                        if 'vehDmgThresh' in item:
                            extras += f", Damage Threshold {item['vehDmgThresh']}"
                    if iType == 'siege weapon':
                        extras = f"Size: {SIZES.get(item.get('size'), 'Unknown')}\n" \
                                 f"AC {item.get('ac')}, HP {item.get('hp')}\n" \
                                 f"Immunities: {item.get('immune')}"
            else:
                type_ = ', '.join(
                    i for i in ("Wondrous Item" if item.get('wondrous') else '', item.get('technology')) if i)
            rarity = str(item.get('rarity')).replace('None', '')
            if 'tier' in item:
                if rarity:
                    rarity += f', {item["tier"]}'
                else:
                    rarity = item['tier']
            type_and_rarity = type_ + (f", {rarity}" if rarity else '')
            value = (item.get('value', 'n/a') + (', ' if 'weight' in item else '')) if 'value' in item else ''
            weight = (item.get('weight', 'n/a') + (' lb.' if item.get('weight') == '1' else ' lbs.')) \
                if 'weight' in item else ''
            weight_and_value = value + weight
            for prop in item.get('property', []):
                if not prop: continue
                a = b = prop
                a = PROPS.get(a, 'n/a')
                if b in compendium.itemprops:
                    proptext += f"**{a.title()}**: {compendium.itemprops[b]}\n"
                if b == 'V': a += " (" + item.get('dmg2', 'n/a') + ")"
                if b in ('T', 'A'): a += " (" + item.get('range', 'n/a') + "ft.)"
                if b == 'RLD': a += " (" + item.get('reload', 'n/a') + " shots)"
                properties.append(a)
            properties = ', '.join(properties)
            damage_and_properties = f"{damage} - {properties}" if properties else damage
            damage_and_properties = (' --- ' + damage_and_properties) if weight_and_value and damage_and_properties else \
                damage_and_properties

            meta = f"*{type_and_rarity}*\n{weight_and_value}{damage_and_properties}\n{extras}"
            text = item['desc']

            if 'reqAttune' in item:
                if item['reqAttune'] 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['reqAttune']}", inline=False)

            embed.set_footer(text=f"Item | {item.get('source', 'Unknown')} {item.get('page', 'Unknown')}")
        else:
            meta = item['meta']
            text = item['desc']
            if 'image' in item:
                embed.set_thumbnail(url=item['image'])
            add_homebrew_footer(embed)

        embed.title = name
        embed.description = meta  # no need to render, has been prerendered

        if proptext:
            text = f"{text}\n{proptext}"
        if len(text) > 5500:
            text = text[:5500] + "..."

        add_fields_from_long_text(embed, "Description", text)

        await Stats.increase_stat(ctx, "items_looked_up_life")
        await (await self._get_destination(ctx)).send(embed=embed)
コード例 #2
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)

        # character setup
        character = None
        if isinstance(caster, PlayerCombatant):
            character = caster.character
        elif isinstance(caster, Character):
            character = caster

        # base stat stuff
        mod_arg = args.last("mod", type_=int)
        stat_override = ''
        if mod_arg is not None:
            mod = mod_arg
            if character:
                prof_bonus = character.stats.prof_bonus
            else:
                prof_bonus = 0
            dc_override = 8 + mod + prof_bonus
            ab_override = mod + prof_bonus
            spell_override = mod
        elif character and 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 = character.stats.get_mod(base)
            dc_override = 8 + mod + character.stats.prof_bonus
            ab_override = mod + character.stats.prof_bonus
            spell_override = mod
            stat_override = f" with {verbose_stat(base)}"

        if spell_override is None and (caster.spellbook.sab is None or caster.spellbook.dc is None):
            raise SpellException("This caster does not have the ability to cast spells.")

        # 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, 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:
            text = self.description
            if len(text) > 1020:
                text = f"{text[:1020]}..."
            embed.add_field(name="Description", value=text, inline=False)
            if l != self.level and self.higherlevels:
                embed.add_field(name="At Higher Levels", value=self.higherlevels, 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 self.image:
            embed.set_thumbnail(url=self.image)

        if self.source == 'homebrew':
            add_homebrew_footer(embed)

        return {"embed": embed}