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