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)
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)
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)
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)
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)
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}