def embed(cls, state: EvosViewState): fields = [ EmbedField( ("{} evolution" if len(state.alt_versions) == 1 else "{} evolutions").format(len(state.alt_versions)), Box(*EvosView._evo_lines(state.alt_versions, state.monster, state.query_settings))) ] if state.gem_versions: fields.append( EmbedField( ("{} evolve gem" if len(state.gem_versions) == 1 else "{} evolve gems").format(len(state.gem_versions)), Box(*EvosView._evo_lines(state.gem_versions, state.monster)))) return EmbedView(EmbedMain( color=state.query_settings.embedcolor, title=MonsterHeader.menu_title( state.monster, is_tsubaki=state.alt_monsters[0].monster.monster_id == cls.TSUBAKI, is_jp_buffed=state.is_jp_buffed).to_markdown(), url=MonsterLink.header_link(state.monster, state.query_settings)), embed_thumbnail=EmbedThumbnail( MonsterImage.icon(state.monster.monster_id)), embed_footer=embed_footer_with_state(state), embed_fields=fields)
async def giveup(self, ctx): """Give up on today's PADle""" if await self.do_quit_early(ctx): return confirmation = await get_user_confirmation( ctx, "Are you sure you would like to give up?", timeout=20) dbcog = await self.get_dbcog() if not confirmation: if confirmation is None: await send_cancellation_message(ctx, "Confirmation timeout.") return monster = dbcog.get_monster(int(await self.config.padle_today())) await self.config.user(ctx.author).done.set(True) m_embed = EmbedView( EmbedMain(title=MonsterHeader.menu_title(monster).to_markdown(), description="PADle #{}".format(await self.config.num_days()), url=MonsterLink.ilmina(monster), color=discord.Color.red()), embed_thumbnail=EmbedThumbnail( MonsterImage.icon(monster.monster_id))).to_embed() await ctx.send(embed=m_embed) await ctx.send(f"Use `{ctx.prefix}padle score` to share your score!") async with self.config.user(ctx.author).score() as scores: scores.append("\N{CROSS MARK}") async with self.config.all_scores() as all_scores: all_scores.append("X") cur_day = await self.config.num_days() todays_guesses = await self.config.user(ctx.author).todays_guesses() async with self.config.user(ctx.author).all_guesses() as all_guesses: all_guesses[str(cur_day)] = todays_guesses
def embed(m: "MonsterModel", query_settings: QuerySettings): return EmbedView( EmbedMain(color=query_settings.embedcolor, title=MonsterHeader.menu_title(m).to_markdown(), description=LinksView.linksbox(m), url=MonsterLink.header_link(m, query_settings)), embed_thumbnail=EmbedThumbnail(MonsterImage.icon(m.monster_id)))
def embed(cls, state: MaterialsViewState): # m: "MonsterModel", mats, usedin, gemid, gemusedin, skillups, skillup_evo_count, link return EmbedView( EmbedMain(color=state.query_settings.embedcolor, title=MonsterHeader.menu_title( state.monster, is_tsubaki=state.alt_monsters[0].monster.monster_id == cls.TSUBAKI, is_jp_buffed=state.is_jp_buffed).to_markdown(), url=MonsterLink.header_link(state.monster, state.query_settings)), embed_thumbnail=EmbedThumbnail( MonsterImage.icon(state.monster.monster_id)), embed_footer=embed_footer_with_state(state), embed_fields=[ f for f in [ mat_use_field(state.mats, "Evo materials", query_settings=state.query_settings) if state.mats or not (state.monster.is_stackable or state.gem_override) else None, mat_use_field(state.usedin, "Material for", 10, state. query_settings) if state.usedin else None, mat_use_field( state.gemusedin, "Evo gem ({}) is mat for". format(state.gemid), 10 if state.gem_override else 5, state.query_settings) if state.gemusedin else None, skillup_field(state.skillups, state.skillup_evo_count, state.link, state.query_settings) if not (state.monster.is_stackable or state.gem_override ) else None ] if f is not None ] + [cls.evos_embed_field(state)])
async def settomorrowpadle(self, ctx, id: int): """Sets tomorrow's PADle to a specified monster ID""" dbcog = await self.get_dbcog() monsters_list = await self.config.monsters_list() if id == 0: confirmation = await get_user_confirmation( ctx, "Would you like to make tomorrow's PADle random?") if confirmation is None: return await send_cancellation_message(ctx, "Confirmation timeout") if not confirmation: return await ctx.send("No changes were made.") await self.config.tmrw_padle.set(0) return await send_confirmation_message( ctx, "Tomorrow's PADle will be random.") if id not in monsters_list: # for the most part, avoid abuse and ensure validness return await ctx.send( "This monster is not a valid PADle. If you would like to set it as tomorrow's" " PADle, please add it to the list of PADles first.") m = dbcog.get_monster(id) title = MonsterHeader.menu_title(m, use_emoji=True).to_markdown() confirmation = await get_user_confirmation( ctx, "Are you sure you would like to change tomorrow's PADle to " f"{title}?") if confirmation is None: return await send_cancellation_message(ctx, "Confirmation timeout") if not confirmation: return await ctx.send("No change to tomorrow's PADle was made.") await self.config.tmrw_padle.set(id) prefix = ctx.prefix await send_confirmation_message( ctx, f"Tomorrow's PADle will be {title}! " f"Use `{prefix}padle settomorrowpadle 0` to undo this.")
async def contains(self, ctx, *, query): """Checks if a monster is in the list of possible PADles""" monsters_list = await self.config.monsters_list() dbcog = await self.get_dbcog() monster = await dbcog.find_monster(query, ctx.author.id) if monster is None: return title = MonsterHeader.menu_title(monster, use_emoji=True).to_markdown() if monster.monster_id in monsters_list: return await send_confirmation_message( ctx, f"{title} is a possible PADle.") await send_cancellation_message(ctx, f"{title} is not a possible PADle.")
async def report_at_time(self, ctx, pulls, offset): pulls_split = pulls.split(',') # TODO: support sending pulls throughout the day, and just have a single max # of pulls if len(pulls_split) > max(await self.config.valid_lengths()): return await ctx.send( f"Please supply all of your pulls with commas in between them. You can use names or" f" numbers.\n\t" f"Valid input: `{ctx.prefix}adpem report 618, 3719, 3600, 3013, 618`\n\t" f"Valid input: `{ctx.prefix}adpem report Enoch, Facet, D Globe, B Super King, Facet`" ) dbcog: Any = ctx.bot.get_cog("DBCog") if dbcog is None: return await ctx.send( "Required cogs not loaded. Please alert a bot owner.") await dbcog.wait_until_ready() if not await self.assert_ready(ctx, self.midnight() - offset): return monsters = [ await dbcog.find_monster('inadpem ' + pull.strip()) for pull in pulls_split ] if not all(monsters): unknown = '\n\t'.join(s for s, m in zip(pulls_split, monsters) if m is None) return await ctx.send( f"Not all monsters were valid. The following could not be processed:\n\t{unknown}" ) check = '\n\t'.join( MonsterHeader.menu_title(m, use_emoji=True).to_markdown() for m in monsters) confirmation = await get_user_confirmation( ctx, f"Are these monsters correct?\n\t{check}", timeout=30, force_delete=False, show_feedback=True) if not confirmation: if confirmation is None: await ctx.send( ctx.author.mention + " Submission timed out. Please resubmit and verify.") return await self.add_pull(ctx, [m.monster_id for m in monsters], time.time() - offset)
def embed(cls, state: OtherInfoViewState): m: "MonsterModel" = state.monster return EmbedView( EmbedMain(color=state.query_settings.embedcolor, title=MonsterHeader.menu_title( state.monster, is_tsubaki=state.alt_monsters[0].monster.monster_id == cls.TSUBAKI, is_jp_buffed=state.is_jp_buffed).to_markdown(), url=MonsterLink.header_link(m, state.query_settings)), embed_footer=embed_footer_with_state(state), embed_fields=[ EmbedField( "Stats at +297:", Box( # need to put these on the same line to get around discord's insane # whitespace margins around code blocks Text(statsbox(m, plus=297) + 'Stats at +0:'), Text(statsbox(m, plus=0)), LabeledText("JP Name", m.name_ja), LinksView.linksbox(m), LabeledText("JP Added", str(m.reg_date)) if m.reg_date else None, LabeledText("Series", m.series.name_en), Box(LabeledText("Sell MP", '{:,}'.format(m.sell_mp)), LabeledText("Buy MP", '{:,}'.format(m.buy_mp)) if m.buy_mp else None, delimiter=' '), Box( LabeledText("Sell Gold", '{:,}'.format(m.sell_gold))), Box(LabeledText( "XP to Max", '{:.1f}'.format(m.exp / 1000000).rstrip('0').rstrip('.') + 'M' if m.exp >= 1000000 else '{:,}'.format(m.exp)), LabeledText("Max Level", str(m.level)), delimiter=' '), Box(LabeledText("Weighted Stats", str(m.stats()[3])), Text('LB {} (+{}%)'.format( m.stats(lv=110)[3], m.limit_mult)) if m.limit_mult > 0 else None, delimiter=' | '), LabeledText("Fodder EXP", '{:,}'.format(m.fodder_exp)), Box(LabeledText("Rarity", str(m.rarity)), LabeledText("Cost", str(m.cost)), delimiter=' '))), cls.evos_embed_field(state) ])
def embed(state, props: IdTracebackViewProps): return EmbedView( EmbedMain(color=state.query_settings.embedcolor, title=MonsterHeader.menu_title(props.monster, use_emoji=True), description=Box( LabeledText('Total score', str(round(props.score, 2))))), embed_fields=[ EmbedField('Matched Name Tokens', Box(props.name_tokens)), EmbedField('Matched Modifier Tokens', Box(props.modifier_tokens)), EmbedField('Equally-scoring matches', Box(props.lower_priority_monsters)), ], embed_footer=embed_footer_with_state(state), )
async def list(self, ctx): """Lists all possible PADles""" dbcog = await self.get_dbcog() monsters_list = await self.config.monsters_list() monsters_list.sort() result = [] for id in monsters_list: m = dbcog.get_monster(int(id)) if m is None or not m.on_na or m.name_en is None: # this should never happen but just in case result.append( f"**Error**: {id} is a valid PADle but not a monster.") continue result.append( MonsterHeader.menu_title(m, use_emoji=True).to_markdown()) for page in pagify("\n".join(result)): await ctx.send(page) await ctx.send(f"There are **{len(result)}** valid PADles!")
def embed(cls, state: PantheonViewState): fields = [EmbedField( 'Pantheon: {}'.format(state.series_name), Box(*_pantheon_lines(state.pantheon_list, state.base_monster, state.query_settings)) ), cls.evos_embed_field(state)] return EmbedView( EmbedMain( color=state.query_settings.embedcolor, title=MonsterHeader.menu_title(state.monster, is_tsubaki=state.alt_monsters[0].monster.monster_id == cls.TSUBAKI, is_jp_buffed=state.is_jp_buffed).to_markdown(), url=MonsterLink.header_link(state.monster, state.query_settings)), embed_footer=embed_footer_with_state(state), embed_fields=fields, embed_thumbnail=EmbedThumbnail(MonsterImage.icon(state.monster.monster_id)), )
def embed(state: GlobalStatsViewState): description = GlobalStatsView.get_description(state) if state.num_days == state.current_day: return EmbedView( EmbedMain( title=BoldText(f"PADle #{state.current_day}"), color=state.query_settings.embedcolor, ), embed_fields=[EmbedField("Stats", Box(description))], embed_footer=embed_footer_with_state(state, text=GlobalStatsView.get_pages_footer(state))) else: return EmbedView( EmbedMain( title=BoldText(f"PADle #{state.current_day}"), description=Box(BoldText(MonsterHeader.menu_title(state.monster).to_markdown())), color=state.query_settings.embedcolor, ), embed_fields=[EmbedField("Stats", Box(description))], embed_footer=embed_footer_with_state(state, text=GlobalStatsView.get_pages_footer(state)), embed_thumbnail=EmbedThumbnail(MonsterImage.icon(state.monster.monster_id)))
def embed(cls, state: IdViewState): m = state.monster fields = [ EmbedField( '/'.join(['{}'.format(t.name) for t in m.types]), Box(IdView.all_awakenings_row(m, state.transform_base), cls.killers_row(m, state.transform_base))), EmbedField( 'Inheritable' if m.is_inheritable else 'Not inheritable', IdView.misc_info(m, state.true_evo_type_raw, state.acquire_raw, state.base_rarity), inline=True), EmbedField(IdView.stats_header(m, state.previous_evolutions, state.query_settings).to_markdown(), IdView.stats(m, state.previous_evolutions, state.query_settings), inline=True), EmbedField( cls.active_skill_header( m, state.previous_transforms).to_markdown(), Text( cls.active_skill_text(m.active_skill, state.awoken_skill_map))), EmbedField( cls.leader_skill_header(m, state.query_settings.lsmultiplier, state.transform_base).to_markdown(), Text(m.leader_skill.desc if m.leader_skill else 'None')), cls.evos_embed_field(state) ] return EmbedView( EmbedMain(color=state.query_settings.embedcolor, title=MonsterHeader.menu_title( m, is_tsubaki=state.alt_monsters[0].monster.monster_id == cls.TSUBAKI, is_jp_buffed=state.is_jp_buffed).to_markdown(), url=MonsterLink.header_link(m, state.query_settings)), embed_thumbnail=EmbedThumbnail(MonsterImage.icon(m.monster_id)), embed_footer=embed_footer_with_state(state), embed_fields=fields)
async def remove_at_time(self, ctx, midnight): dbcog: Any = ctx.bot.get_cog("DBCog") if dbcog is None: return await ctx.send( "Required cogs not loaded. Please alert a bot owner.") await dbcog.wait_until_ready() pulls = [[uid, v, ts] for uid, v, ts in await self.config.pulls() if midnight <= ts < midnight + (24 * 60 * 60) and uid == ctx.author.id] pulls = {char_to_emoji(str(c)): vs for c, vs in enumerate(pulls, 1)} if not pulls: return await ctx.send( "There are no logged pulls during the given time period.") pulltext = [] for c, (uid, pull, ts) in pulls.items(): if isinstance(pull, str): pulltext.append(c + ' ' + pull) else: pulltext.append(c + '\n\t' + '\n\t'.join( MonsterHeader.menu_title(dbcog.get_monster(mid), use_emoji=True).to_markdown() for mid in pull)) pulltext = '\n\n'.join(pulltext) chosen = await get_user_reaction( ctx, f"Which set of pulls would you like to remove?\n\n{pulltext}", *pulls.keys(), NO_EMOJI, force_delete=False, show_feedback=True) if chosen is None or chosen == NO_EMOJI: return async with self.config.pulls() as c_pulls: c_pulls.remove(pulls[chosen]) await ctx.send("Your pulls have been removed.")
def embed(state: ClosableEmbedViewState, props: AwakeningHelpViewProps): monster = props.monster show_help = state.query_settings.showhelp.value token_map = props.token_map fields = [ EmbedField('Normal Awakenings', get_normal_awakenings(monster, show_help, token_map)), EmbedField('Super Awakenings', get_super_awakenings(monster, show_help, token_map)) ] return EmbedView( EmbedMain(color=state.query_settings.embedcolor, description='This monster has no awakenings.' if not monster.awakenings else ''), embed_author=EmbedAuthor( MonsterHeader.menu_title(monster).to_markdown(), MonsterLink.header_link(monster, query_settings=state.query_settings), MonsterImage.icon(monster.monster_id)), embed_footer=embed_footer_with_state(state), embed_fields=fields)
def embed(cls, state: PicViewState): url = MonsterImage.picture(state.monster.monster_id) animated = state.monster.has_animation fields = [EmbedField( 'Extra Links', Box( Box( Text('Animation:'), LinkedText('(MP4)', MonsterImage.video(state.monster.monster_no_jp)), Text('|'), LinkedText('(GIF)', MonsterImage.gif(state.monster.monster_no_jp)), delimiter=' ' ) if animated else None, Box( Text('Orb Skin:'), LinkedText('Regular', MonsterImage.orb_skin(state.monster.orb_skin_id)), Text('|'), LinkedText('Color Blind', MonsterImage.orb_skin_colorblind(state.monster.orb_skin_id)), delimiter=' ' ) if state.monster.orb_skin_id else None, ) ), cls.evos_embed_field(state) ] return EmbedView( EmbedMain( color=state.query_settings.embedcolor, title=MonsterHeader.menu_title(state.monster, is_tsubaki=state.alt_monsters[0].monster.monster_id == cls.TSUBAKI, is_jp_buffed=state.is_jp_buffed).to_markdown(), url=MonsterLink.header_link(state.monster, state.query_settings) ), embed_footer=embed_footer_with_state(state), embed_fields=fields, embed_body_image=EmbedBodyImage(url), )
def embed(state, props: ExperienceCurveViewProps): regular = get_normal_exp_difference(props.monster, props.low, props.high, props.offset) lb = get_lb_exp_difference(props.monster, props.low, props.high, props.offset) slb = get_slb_exp_difference(props.monster, props.low, props.high, props.offset) is_light = props.monster.full_damage_attr.value == 3 return EmbedView( EmbedMain( color=state.query_settings.embedcolor, title=MonsterHeader.menu_title(props.monster, use_emoji=True), url=MonsterLink.header_link( props.monster, query_settings=state.query_settings), description=Text(f'lv{props.low} -> lv{props.high} (' + (trunc_humanize(props.monster.exp_curve) if props.monster.exp_curve else "no") + f' curve)'), ), embed_thumbnail=EmbedThumbnail( MonsterImage.icon(props.monster.monster_id)), embed_fields=[ EmbedField(title='Exact', body=Box( LabeledText("Reg", humanize_number(regular)), LabeledText("LB", humanize_number(lb)), LabeledText("SLB", humanize_number(slb)), LabeledText("Net", humanize_number(regular + lb + slb))), inline=True), EmbedField(title='Approx', body=Box( LabeledText("Reg", trunc_humanize(regular)), LabeledText("LB", trunc_humanize(lb)), LabeledText("SLB", trunc_humanize(slb)), LabeledText("Net", trunc_humanize(regular + lb + slb))), inline=True), EmbedField( title='Resources', body=Box( LabeledText( "Globes " + emoji_cache.get_emoji( f'orb_{props.monster.full_damage_attr.name.lower()}' ), str( get_total_needed( props.monster, props.low, props.high, props.offset, ceil(1.5 * GLOBE_EXP[ props.monster.full_damage_attr.value]), is_light))), LabeledText( "TA2", str( get_total_needed(props.monster, props.low, props.high, props.offset, TA2_EXP, is_light))), LabeledText( "TA3", str( get_total_needed(props.monster, props.low, props.high, props.offset, TA3_EXP, is_light))), ), inline=True) ], embed_footer=embed_footer_with_state(state))
def embed(m: "MonsterModel", query_settings: QuerySettings): return EmbedView(EmbedMain(color=query_settings.embedcolor), embed_author=EmbedAuthor( MonsterHeader.menu_title(m).to_markdown(), MonsterLink.header_link(m, query_settings), MonsterImage.icon(m.monster_id)))
async def guess(self, ctx, *, guess): """Guess a card for the daily PADle""" if await self.do_quit_early(ctx): return dbcog = await self.get_dbcog() guess_monster = await dbcog.find_monster(guess, ctx.author.id) if guess_monster is None: close_menu = ClosableEmbedMenu.menu() props = PADleMonsterConfirmationViewProps( "Monster not found, please try again.") query_settings = await QuerySettings.extract_raw( ctx.author, self.bot, guess) state = ClosableEmbedViewState( ctx.author.id, ClosableEmbedMenu.MENU_TYPE, guess, query_settings, PADleMonsterConfirmationView.VIEW_TYPE, props) await close_menu.create(ctx, state) return m_embed = EmbedView(EmbedMain( title=MonsterHeader.menu_title(guess_monster).to_markdown(), description="Did you mean this monster?"), embed_thumbnail=EmbedThumbnail( MonsterImage.icon( guess_monster.monster_id))).to_embed() confirmation = await get_user_reaction(ctx, m_embed, YES_EMOJI, NO_EMOJI, timeout=20) if confirmation is None: return await send_cancellation_message(ctx, "Confirmation timeout.") if confirmation == NO_EMOJI: return await send_cancellation_message(ctx, "Please guess again.") if not await self.config.user(ctx.author).start(): return await send_cancellation_message( ctx, "You have not started a game yet! The PADle may have just expired." ) if await self.config.user(ctx.author).done(): return await send_cancellation_message( ctx, "You have already finished today's PADle!") async with self.config.user( ctx.author).todays_guesses() as todays_guesses: todays_guesses.append(guess_monster.monster_id) monster = dbcog.get_monster(int(await self.config.padle_today())) cur_day = await self.config.num_days() todays_guesses = await self.config.user(ctx.author).todays_guesses() async with self.config.user(ctx.author).all_guesses() as all_guesses: all_guesses[str(cur_day)] = todays_guesses channel = self.bot.get_channel(await self.config.user(ctx.author ).channel_id()) if channel is not None: with suppress(discord.HTTPException): del_msg = await channel.fetch_message(await self.config.user( ctx.author).edit_id()) await del_msg.delete() guess_monster_diff = MonsterDiff(monster, guess_monster) points = guess_monster_diff.get_diff_score() padle_menu = PADleScrollMenu.menu() # query_settings = await QuerySettings.extract_raw(ctx.author, self.bot, guess) cur_page = ceil(len(todays_guesses) / 5) - 1 page_guesses = await PADleScrollViewState.do_queries( dbcog, todays_guesses[((cur_page) * 5):((cur_page + 1) * 5)]) state = PADleScrollViewState(ctx.author.id, PADleScrollMenu.MENU_TYPE, guess, monster=monster, cur_day_page_guesses=page_guesses, current_day=cur_day, current_page=cur_page, num_pages=cur_page + 1) message = await padle_menu.create(ctx, state) await self.config.user(ctx.author).edit_id.set(message.id) await self.config.user(ctx.author).channel_id.set(ctx.channel.id) score = "\N{LARGE YELLOW SQUARE}" if guess_monster.monster_id == monster.monster_id: await ctx.send( "You got the PADle in {} guesses! Use `{}padle score` to share your score." .format( len(await self.config.user(ctx.author).todays_guesses()), ctx.prefix)) await self.config.user(ctx.author).done.set(True) async with self.config.all_scores() as all_scores: all_scores.append( str( len(await self.config.user(ctx.author).todays_guesses()))) score = "\N{LARGE GREEN SQUARE}" else: if points < 9: score = "\N{LARGE ORANGE SQUARE}" if points < 5: score = "\N{LARGE RED SQUARE}" async with self.config.user(ctx.author).score() as scores: scores.append(score)
def embed(cls, state: ButtonInfoViewState): is_coop = state.display_options.players == ButtonInfoOptions.coop is_desktop = state.display_options.device == ButtonInfoOptions.desktop max_110 = state.display_options.max_level == ButtonInfoOptions.limit_break monster = state.monster info = state.info fields = [ EmbedField( # this block does not change if the lv110/lv120 toggle is clicked ButtonInfoView.get_max_level_text(monster, True), Box( Text('Without Latents'), # avoid whitespace after code block Box(get_stat_block(info.coop if is_coop else info.solo), Text('With Latents (Atk+)'), delimiter=''), get_stat_block( info.coop_latent if is_coop else info.solo_latent)), inline=True), EmbedField( 'Lv. 120', Box( Text('Without Latents'), # avoid whitespace after code block Box(get_stat_block( info.coop_slb if is_coop else info.solo_slb), Text('With Latents (Atk++)'), delimiter=''), get_stat_block(info.coop_slb_latent if is_coop else info. solo_slb_latent)), inline=True) if monster.limit_mult != 0 else None, EmbedField( ButtonInfoView.get_common_buttons_title_text(monster, max_110), Box( Text( '*Inherits are assumed to be the max possible level (up to 110) and +297.*' ), # janky, but python gives DeprecationWarnings when using \* in a regular string Text(r'*\* = on-color stat bonus applied*'), Text('Card Button Damage'), # done this way to not have the whitespace after code block Box(BlockText(info.get_card_btn_str(is_coop, max_110)), Text('Team Button Contribution'), delimiter=''), BlockText(info.get_team_btn_str(is_coop, max_110)))) if is_desktop else None, EmbedField( ButtonInfoView.get_common_buttons_title_text(monster, max_110), Box( Text( '*Inherits are assumed to be the max possible level (up to 110) and +297.*' ), # janky, but python gives DeprecationWarnings when using \* in a regular string Text(r'*\* = on-color stat bonus applied*'))) if not is_desktop else None, EmbedField('Card Button Damage', BlockText( get_mobile_btn_str( info.get_card_btn_str(is_coop, max_110))), inline=True) if not is_desktop else None, EmbedField('Team Button Contribution', BlockText( get_mobile_btn_str( info.get_team_btn_str(is_coop, max_110))), inline=True) if not is_desktop else None, cls.evos_embed_field(state) ] return EmbedView(EmbedMain( color=state.query_settings.embedcolor, description='(Co-op mode)' if is_coop else '(Singleplayer mode)'), embed_author=EmbedAuthor( MonsterHeader.menu_title(monster).to_markdown(), MonsterLink.header_link(monster, state.query_settings), MonsterImage.icon(monster.monster_id)), embed_footer=embed_footer_with_state(state), embed_fields=fields)